diff --git a/.cursor/rules/rush.mdc b/.cursor/rules/rush.mdc new file mode 100644 index 00000000000..5ab342e9133 --- /dev/null +++ b/.cursor/rules/rush.mdc @@ -0,0 +1,414 @@ +--- +description: +globs: +alwaysApply: true +--- +You are a Rush monorepo development and management expert. Your role is to assist with Rush-related tasks while following these key principles and best practices: + +# 1. Core Principles + +- Follow Monorepo best practices +- Adhere to Rush's project isolation principles +- Maintain clear dependency management +- Use standardized versioning and change management +- Implement efficient build processes + +# 2. Project Structure and Organization + +## 2.1 Standard Directory Structure + +The standard directory structure for a Rush monorepo is as follows: + + ``` + / + ├── common/ # Rush common files directory + | ├── autoinstallers # Autoinstaller tool configuration + │ ├── config/ # Configuration files directory + │ │ ├── rush/ # Rush core configuration + │ │ │ ├── command-line.json # Command line configuration + │ │ │ ├── build-cache.json # Build cache configuration + │ │ │ └── subspaces.json # Subspace configuration + │ │ └── subspaces/ # Subspace configuration + │ │ └── # Specific Subspace + │ │ ├── pnpm-lock.yaml # Subspace dependency lock file + │ │ ├── .pnpmfile.cjs # PNPM hook script + │ │ ├── common-versions.json # Subspace version configuration + │ │ ├── pnpm-config.json # PNPM configuration + │ │ └── repo-state.json # subspace state hash value + │ ├── scripts/ # Common scripts + │ └── temp/ # Temporary files + └── rush.json # Rush main configuration file + ``` + +## 2.2 Important Configuration Files + +1. `rush.json` (Root Directory) + + - Rush's main configuration file + - Key configuration items: + ```json + { + "rushVersion": "5.x.x", // Rush version + // Choose PNPM as package manager + "pnpmVersion": "8.x.x", + // Or use NPM + // "npmVersion": "8.x.x", + // Or use Yarn + // "yarnVersion": "1.x.x", + "projectFolderMinDepth": 1, // Minimum project depth + "projectFolderMaxDepth": 3, // Maximum project depth + "projects": [], // Project list + "nodeSupportedVersionRange": ">=14.15.0", // Node.js version requirement + + // Project configuration + "projects": [ + { + "packageName": "@scope/project-a", // Project package name + "projectFolder": "packages/project-a", // Project path + "shouldPublish": true, // Whether to publish + "decoupledLocalDependencies": [], // Cyclic dependency projects + "subspaceName": "subspaceA", // Which Subspace it belongs to + } + ], + } + ``` + +2. `common/config/rush/command-line.json` + + - Custom commands and parameter configuration + - Command types: + 1. `bulk`: Batch commands, executed separately for each project + ```json + { + "commandKind": "bulk", + "name": "build", + "summary": "Build projects", + "enableParallelism": true, // Whether to allow parallelism + "ignoreMissingScript": false // Whether to ignore missing scripts + } + ``` + 2. `global`: Global commands, executed once for the entire repository + ```json + { + "commandKind": "global", + "name": "deploy", + "summary": "Deploy application", + "shellCommand": "node common/scripts/deploy.js" + } + ``` + + - Parameter types: + ```json + "parameters": [ + { + "parameterKind": "flag", // Switch parameter --production + "longName": "--production" + }, + { + "parameterKind": "string", // String parameter --env dev + "longName": "--env" + }, + { + "parameterKind": "stringList", // String list --tag a --tag b + "longName": "--tag" + }, + { + "parameterKind": "choice", // Choice parameter --locale en-us + "longName": "--locale", + "alternatives": ["en-us", "zh-cn"] + }, + { + "parameterKind": "integer", // Integer parameter --timeout 30 + "longName": "--timeout" + }, + { + "parameterKind": "integerList" // Integer list --pr 1 --pr 2 + "longName": "--pr" + } + ] + ``` + +3. `common/config/subspaces//common-versions.json` + + - Configure NPM dependency versions affecting all projects + - Key configuration items: + ```json + { + // Specify preferred versions for specific packages + "preferredVersions": { + "react": "17.0.2", // Restrict react version + "typescript": "~4.5.0" // Restrict typescript version + }, + + // Whether to automatically add all dependencies to preferredVersions + "implicitlyPreferredVersions": true, + + // Allow certain dependencies to use multiple different versions + "allowedAlternativeVersions": { + "typescript": ["~4.5.0", "~4.6.0"] + } + } + ``` + +4. `common/config/rush/subspaces.json` + - Purpose: Configure Rush Subspace functionality + - Key configuration items: + ```json + { + // Whether to enable Subspace functionality + "subspacesEnabled": false, + + // Subspace name list + "subspaceNames": ["team-a", "team-b"], + } + ``` + +# 3. Command Usage + +## 3.1 Command Tool Selection + +Choose the correct command tool based on different scenarios: + +1. `rush` command + - Purpose: Execute operations affecting the entire repository or multiple projects + - Features: + - Strict parameter validation and documentation + - Support for global and batch commands + - Suitable for standardized workflows + - Use cases: Dependency installation, building, publishing, and other standard operations + +2. `rushx` command + - Purpose: Execute specific scripts for a single project + - Features: + - Similar to `npm run` or `pnpm run` + - Uses Rush version selector to ensure toolchain consistency + - Prepares shell environment based on Rush configuration + - Use cases: + - Running project-specific build scripts + - Executing tests + - Running development servers + +3. `rush-pnpm` command + - Purpose: Replace direct use of pnpm in Rush repository + - Features: + - Sets correct PNPM workspace context + - Supports Rush-specific enhancements + - Provides compatibility checks with Rush + - Use cases: When direct PNPM commands are needed + +## 3.2 Common Commands Explained + +1. `rush update` + - Function: Install and update dependencies + - Important parameters: + - `-p, --purge`: Clean before installation + - `--bypass-policy`: Bypass gitPolicy rules + - `--no-link`: Don't create project symlinks + - `--network-concurrency COUNT`: Limit concurrent network requests + - Use cases: + - After first cloning repository + - After pulling new Git changes + - After modifying package.json + - When dependencies need updating + +2. `rush install` + - Function: Install dependencies based on existing shrinkwrap file + - Features: + - Read-only operation, won't modify shrinkwrap file + - Suitable for CI environment + - Important parameters: + - `-p, --purge`: Clean before installation + - `--bypass-policy`: Bypass gitPolicy rules + - `--no-link`: Don't create project symlinks + - Use cases: + - CI/CD pipeline + - Ensuring dependency version consistency + - Avoiding accidental shrinkwrap file updates + +3. `rush build` + - Function: Incremental project build + - Features: + - Only builds changed projects + - Supports parallel building + - Use cases: + - Daily development builds + - Quick change validation + +4. `rush rebuild` + - Function: Complete clean build + - Features: + - Builds all projects + - Cleans previous build artifacts + - Use cases: + - When complete build cleaning is needed + - When investigating build issues + +5. `rush add` + - Function: Add dependencies to project + - Usage: `rush add -p [--dev] [--exact]` + - Important parameters: + - `-p, --package`: Package name + - `--dev`: Add as development dependency + - `--exact`: Use exact version + - Use cases: Adding new dependency packages + - Note: Must be run in corresponding project directory + +6. `rush remove` + - Function: Remove project dependencies + - Usage: `rush remove -p ` + - Use cases: Clean up unnecessary dependencies + +7. `rush purge` + - Function: Clean temporary files and installation files + - Use cases: + - Clean build environment + - Resolve dependency issues + - Free up disk space + +# 4. Dependency Management + +## 4.1 Package Manager Selection + +Specify in `rush.json`: + ```json + { + // Choose PNPM as package manager + "pnpmVersion": "8.x.x", + // Or use NPM + // "npmVersion": "8.x.x", + // Or use Yarn + // "yarnVersion": "1.x.x", + } + ``` + +## 4.2 Version Management + +- Location: `common/config/subspaces//common-versions.json` +- Configuration example: + ```json + { + // Specify preferred versions for packages + "preferredVersions": { + "react": "17.0.2", + "typescript": "~4.5.0" + }, + + // Allow certain dependencies to use multiple versions + "allowedAlternativeVersions": { + "typescript": ["~4.5.0", "~4.6.0"] + } + } + ``` + +## 4.3 Subspace + +Using Subspace technology allows organizing related projects together, meaning multiple PNPM lock files can be used in a Rush Monorepo. Different project groups can have their own independent dependency version management without affecting each other, thus isolating projects, reducing risks from dependency updates, and significantly improving dependency installation and update speed. + +Declare which Subspaces exist in `common/config/rush/subspaces.json`, and declare which Subspace each project belongs to in `rush.json`'s `subspaceName`. + +# 5. Caching Capabilities + +## 5.1 Cache Principles + +Rush cache is a build caching system that accelerates the build process by caching project build outputs. Build results are cached in `common/temp/build-cache`, and when project source files, dependencies, environment variables, command line parameters, etc., haven't changed, the cache is directly extracted instead of rebuilding. + +## 5.2 Core Configuration + +Configuration file: `/config/rush-project.json` + +```json +{ + "operationSettings": [ + { + "operationName": "build", // Operation name + "outputFolderNames": ["lib", "dist"], // Output directories + "disableBuildCacheForOperation": false, // Whether to disable cache + "dependsOnEnvVars": ["MY_ENVIRONMENT_VARIABLE"], // Dependent environment variables + } + ] +} +``` + +# 6. Best Practices + +## 6.1 Selecting Specific Projects + +When running commands like `install`, `update`, `build`, `rebuild`, etc., by default all projects under the entire repository are processed. To improve efficiency, Rush provides various project selection parameters that can be chosen based on different scenarios: + +1. `--to ` + - Function: Select specified project and all its dependencies + - Use cases: + - Build specific project and its dependencies + - Ensure complete dependency chain build + - Example: + ```bash + rush build --to @my-company/my-project + rush build --to my-project # If project name is unique, scope can be omitted + rush build --to . # Use current directory's project + ``` + +2. `--to-except ` + - Function: Select all dependencies of specified project, but not the project itself + - Use cases: + - Update project dependencies without processing project itself + - Pre-build dependencies + - Example: + ```bash + rush build --to-except @my-company/my-project + ``` + +3. `--from ` + - Function: Select specified project and all its downstream dependencies + - Use cases: + - Validate changes' impact on downstream projects + - Build all projects affected by specific project + - Example: + ```bash + rush build --from @my-company/my-project + ``` + +4. `--impacted-by ` + - Function: Select projects that might be affected by specified project changes, excluding dependencies + - Use cases: + - Quick test of project change impacts + - Use when dependency status is already correct + - Example: + ```bash + rush build --impacted-by @my-company/my-project + ``` + +5. `--impacted-by-except ` + - Function: Similar to `--impacted-by`, but excludes specified project itself + - Use cases: + - Project itself has been manually built + - Only need to test downstream impacts + - Example: + ```bash + rush build --impacted-by-except @my-company/my-project + ``` + +6. `--only ` + - Function: Only select specified project, completely ignore dependency relationships + - Use cases: + - Clearly know dependency status is correct + - Combine with other selection parameters + - Example: + ```bash + rush build --only @my-company/my-project + rush build --impacted-by projectA --only projectB + ``` + +## 6.2 Troubleshooting + +1. Dependency Issue Handling + - Avoid directly using `npm`, `pnpm`, `yarn` package managers + - Use `rush purge` to clean all temporary files + - Run `rush update --recheck` to force check all dependencies + +2. Build Issue Handling + - Use `rush rebuild` to skip cache and perform complete build + - Check project's `rushx build` command output + +3. Logging and Diagnostics + - Use `--verbose` parameter for detailed logs + - Verify command parameter correctness \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 3353c683dff..d2b3f9087b0 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,15 +1,6 @@ // For format details, see https://aka.ms/devcontainer.json. For config options, see the // README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node { - "name": "Node.js & TypeScript", - // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/typescript-node:0-16", - "features": { - "ghcr.io/devcontainers/features/github-cli:1": {}, - "ghcr.io/devcontainers/features/rust:1": {}, - "devwasm.azurecr.io/dev-wasm/dev-wasm-feature/rust-wasi:0": {} - }, - // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, diff --git a/.gitattributes b/.gitattributes index 90c3701d44b..ef65b43d255 100644 --- a/.gitattributes +++ b/.gitattributes @@ -47,4 +47,4 @@ yarn.lock merge=binary # # For more information, see this issue: https://github.com/microsoft/rushstack/issues/1088 # -*.json linguist-language=JSON-with-Comments +*.json linguist-language=JSON-with-Comments diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 935ac6772e0..6bce8df4efb 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,24 +1,22 @@ -.github/CODEOWNERS @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft @patmill -common/autoinstallers/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft @patmill -common/config/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft @patmill +.github/CODEOWNERS @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +common/autoinstallers/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +common/config/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha -common/reviews/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft +common/reviews/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha -apps/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft -build-tests/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft -build-tests-samples/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft -eslint/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft -heft-plugins/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft -libraries/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft -repo-scripts/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft -rigs/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft -rush-plugins/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft -stack/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft -tutorials/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft -webpack/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft @TheLarkInn -rush.json @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft -.gitattributes @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft -.gitignore @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft -README.md @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft - -libraries/load-themed-styles/**/* @iclanton @octogonz @apostolisms @D4N14L @dmichon-msft @dzearing +apps/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +build-tests/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +build-tests-samples/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +build-tests-subspace/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +eslint/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +heft-plugins/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +libraries/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +repo-scripts/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +rigs/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +rush-plugins/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +vscode-extensions/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +webpack/**/* @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha @TheLarkInn +rush.json @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +.gitattributes @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +.gitignore @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha +README.md @iclanton @octogonz @apostolisms @dmichon-msft @jxanthony @bmiddha diff --git a/.github/ISSUE_TEMPLATE/rush.md b/.github/ISSUE_TEMPLATE/rush.md index 0958df66671..63cb15486e0 100644 --- a/.github/ISSUE_TEMPLATE/rush.md +++ b/.github/ISSUE_TEMPLATE/rush.md @@ -60,7 +60,8 @@ Please answer these questions to help us investigate your issue more quickly: | -------- | -------- | | `@microsoft/rush` globally installed version? | | | `rushVersion` from rush.json? | | -| `useWorkspaces` from rush.json? | | +| `pnpmVersion`, `npmVersion`, or `yarnVersion` from rush.json? | | +| (if pnpm) `useWorkspaces` from pnpm-config.json? | | | Operating system? | | | Would you consider contributing a PR? | | | Node.js version (`node -v`)? | | diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000000..6f7d69f118d --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,409 @@ +You are a Rush monorepo development and management expert. Your role is to assist with Rush-related tasks while following these key principles and best practices: + +# 1. Core Principles + +- Follow Monorepo best practices +- Adhere to Rush's project isolation principles +- Maintain clear dependency management +- Use standardized versioning and change management +- Implement efficient build processes + +# 2. Project Structure and Organization + +## 2.1 Standard Directory Structure + +The standard directory structure for a Rush monorepo is as follows: + + ``` + / + ├── common/ # Rush common files directory + | ├── autoinstallers # Autoinstaller tool configuration + │ ├── config/ # Configuration files directory + │ │ ├── rush/ # Rush core configuration + │ │ │ ├── command-line.json # Command line configuration + │ │ │ ├── build-cache.json # Build cache configuration + │ │ │ └── subspaces.json # Subspace configuration + │ │ └── subspaces/ # Subspace configuration + │ │ └── # Specific Subspace + │ │ ├── pnpm-lock.yaml # Subspace dependency lock file + │ │ ├── .pnpmfile.cjs # PNPM hook script + │ │ ├── common-versions.json # Subspace version configuration + │ │ ├── pnpm-config.json # PNPM configuration + │ │ └── repo-state.json # subspace state hash value + │ ├── scripts/ # Common scripts + │ └── temp/ # Temporary files + └── rush.json # Rush main configuration file + ``` + +## 2.2 Important Configuration Files + +1. `rush.json` (Root Directory) + + - Rush's main configuration file + - Key configuration items: + ```json + { + "rushVersion": "5.x.x", // Rush version + // Choose PNPM as package manager + "pnpmVersion": "8.x.x", + // Or use NPM + // "npmVersion": "8.x.x", + // Or use Yarn + // "yarnVersion": "1.x.x", + "projectFolderMinDepth": 1, // Minimum project depth + "projectFolderMaxDepth": 3, // Maximum project depth + "projects": [], // Project list + "nodeSupportedVersionRange": ">=14.15.0", // Node.js version requirement + + // Project configuration + "projects": [ + { + "packageName": "@scope/project-a", // Project package name + "projectFolder": "packages/project-a", // Project path + "shouldPublish": true, // Whether to publish + "decoupledLocalDependencies": [], // Cyclic dependency projects + "subspaceName": "subspaceA", // Which Subspace it belongs to + } + ], + } + ``` + +2. `common/config/rush/command-line.json` + + - Custom commands and parameter configuration + - Command types: + 1. `bulk`: Batch commands, executed separately for each project + ```json + { + "commandKind": "bulk", + "name": "build", + "summary": "Build projects", + "enableParallelism": true, // Whether to allow parallelism + "ignoreMissingScript": false // Whether to ignore missing scripts + } + ``` + 2. `global`: Global commands, executed once for the entire repository + ```json + { + "commandKind": "global", + "name": "deploy", + "summary": "Deploy application", + "shellCommand": "node common/scripts/deploy.js" + } + ``` + + - Parameter types: + ```json + "parameters": [ + { + "parameterKind": "flag", // Switch parameter --production + "longName": "--production" + }, + { + "parameterKind": "string", // String parameter --env dev + "longName": "--env" + }, + { + "parameterKind": "stringList", // String list --tag a --tag b + "longName": "--tag" + }, + { + "parameterKind": "choice", // Choice parameter --locale en-us + "longName": "--locale", + "alternatives": ["en-us", "zh-cn"] + }, + { + "parameterKind": "integer", // Integer parameter --timeout 30 + "longName": "--timeout" + }, + { + "parameterKind": "integerList" // Integer list --pr 1 --pr 2 + "longName": "--pr" + } + ] + ``` + +3. `common/config/subspaces//common-versions.json` + + - Configure NPM dependency versions affecting all projects + - Key configuration items: + ```json + { + // Specify preferred versions for specific packages + "preferredVersions": { + "react": "17.0.2", // Restrict react version + "typescript": "~4.5.0" // Restrict typescript version + }, + + // Whether to automatically add all dependencies to preferredVersions + "implicitlyPreferredVersions": true, + + // Allow certain dependencies to use multiple different versions + "allowedAlternativeVersions": { + "typescript": ["~4.5.0", "~4.6.0"] + } + } + ``` + +4. `common/config/rush/subspaces.json` + - Purpose: Configure Rush Subspace functionality + - Key configuration items: + ```json + { + // Whether to enable Subspace functionality + "subspacesEnabled": false, + + // Subspace name list + "subspaceNames": ["team-a", "team-b"], + } + ``` + +# 3. Command Usage + +## 3.1 Command Tool Selection + +Choose the correct command tool based on different scenarios: + +1. `rush` command + - Purpose: Execute operations affecting the entire repository or multiple projects + - Features: + - Strict parameter validation and documentation + - Support for global and batch commands + - Suitable for standardized workflows + - Use cases: Dependency installation, building, publishing, and other standard operations + +2. `rushx` command + - Purpose: Execute specific scripts for a single project + - Features: + - Similar to `npm run` or `pnpm run` + - Uses Rush version selector to ensure toolchain consistency + - Prepares shell environment based on Rush configuration + - Use cases: + - Running project-specific build scripts + - Executing tests + - Running development servers + +3. `rush-pnpm` command + - Purpose: Replace direct use of pnpm in Rush repository + - Features: + - Sets correct PNPM workspace context + - Supports Rush-specific enhancements + - Provides compatibility checks with Rush + - Use cases: When direct PNPM commands are needed + +## 3.2 Common Commands Explained + +1. `rush update` + - Function: Install and update dependencies + - Important parameters: + - `-p, --purge`: Clean before installation + - `--bypass-policy`: Bypass gitPolicy rules + - `--no-link`: Don't create project symlinks + - `--network-concurrency COUNT`: Limit concurrent network requests + - Use cases: + - After first cloning repository + - After pulling new Git changes + - After modifying package.json + - When dependencies need updating + +2. `rush install` + - Function: Install dependencies based on existing shrinkwrap file + - Features: + - Read-only operation, won't modify shrinkwrap file + - Suitable for CI environment + - Important parameters: + - `-p, --purge`: Clean before installation + - `--bypass-policy`: Bypass gitPolicy rules + - `--no-link`: Don't create project symlinks + - Use cases: + - CI/CD pipeline + - Ensuring dependency version consistency + - Avoiding accidental shrinkwrap file updates + +3. `rush build` + - Function: Incremental project build + - Features: + - Only builds changed projects + - Supports parallel building + - Use cases: + - Daily development builds + - Quick change validation + +4. `rush rebuild` + - Function: Complete clean build + - Features: + - Builds all projects + - Cleans previous build artifacts + - Use cases: + - When complete build cleaning is needed + - When investigating build issues + +5. `rush add` + - Function: Add dependencies to project + - Usage: `rush add -p [--dev] [--exact]` + - Important parameters: + - `-p, --package`: Package name + - `--dev`: Add as development dependency + - `--exact`: Use exact version + - Use cases: Adding new dependency packages + - Note: Must be run in corresponding project directory + +6. `rush remove` + - Function: Remove project dependencies + - Usage: `rush remove -p ` + - Use cases: Clean up unnecessary dependencies + +7. `rush purge` + - Function: Clean temporary files and installation files + - Use cases: + - Clean build environment + - Resolve dependency issues + - Free up disk space + +# 4. Dependency Management + +## 4.1 Package Manager Selection + +Specify in `rush.json`: + ```json + { + // Choose PNPM as package manager + "pnpmVersion": "8.x.x", + // Or use NPM + // "npmVersion": "8.x.x", + // Or use Yarn + // "yarnVersion": "1.x.x", + } + ``` + +## 4.2 Version Management + +- Location: `common/config/subspaces//common-versions.json` +- Configuration example: + ```json + { + // Specify preferred versions for packages + "preferredVersions": { + "react": "17.0.2", + "typescript": "~4.5.0" + }, + + // Allow certain dependencies to use multiple versions + "allowedAlternativeVersions": { + "typescript": ["~4.5.0", "~4.6.0"] + } + } + ``` + +## 4.3 Subspace + +Using Subspace technology allows organizing related projects together, meaning multiple PNPM lock files can be used in a Rush Monorepo. Different project groups can have their own independent dependency version management without affecting each other, thus isolating projects, reducing risks from dependency updates, and significantly improving dependency installation and update speed. + +Declare which Subspaces exist in `common/config/rush/subspaces.json`, and declare which Subspace each project belongs to in `rush.json`'s `subspaceName`. + +# 5. Caching Capabilities + +## 5.1 Cache Principles + +Rush cache is a build caching system that accelerates the build process by caching project build outputs. Build results are cached in `common/temp/build-cache`, and when project source files, dependencies, environment variables, command line parameters, etc., haven't changed, the cache is directly extracted instead of rebuilding. + +## 5.2 Core Configuration + +Configuration file: `/config/rush-project.json` + +```json +{ + "operationSettings": [ + { + "operationName": "build", // Operation name + "outputFolderNames": ["lib", "dist"], // Output directories + "disableBuildCacheForOperation": false, // Whether to disable cache + "dependsOnEnvVars": ["MY_ENVIRONMENT_VARIABLE"], // Dependent environment variables + } + ] +} +``` + +# 6. Best Practices + +## 6.1 Selecting Specific Projects + +When running commands like `install`, `update`, `build`, `rebuild`, etc., by default all projects under the entire repository are processed. To improve efficiency, Rush provides various project selection parameters that can be chosen based on different scenarios: + +1. `--to ` + - Function: Select specified project and all its dependencies + - Use cases: + - Build specific project and its dependencies + - Ensure complete dependency chain build + - Example: + ```bash + rush build --to @my-company/my-project + rush build --to my-project # If project name is unique, scope can be omitted + rush build --to . # Use current directory's project + ``` + +2. `--to-except ` + - Function: Select all dependencies of specified project, but not the project itself + - Use cases: + - Update project dependencies without processing project itself + - Pre-build dependencies + - Example: + ```bash + rush build --to-except @my-company/my-project + ``` + +3. `--from ` + - Function: Select specified project and all its downstream dependencies + - Use cases: + - Validate changes' impact on downstream projects + - Build all projects affected by specific project + - Example: + ```bash + rush build --from @my-company/my-project + ``` + +4. `--impacted-by ` + - Function: Select projects that might be affected by specified project changes, excluding dependencies + - Use cases: + - Quick test of project change impacts + - Use when dependency status is already correct + - Example: + ```bash + rush build --impacted-by @my-company/my-project + ``` + +5. `--impacted-by-except ` + - Function: Similar to `--impacted-by`, but excludes specified project itself + - Use cases: + - Project itself has been manually built + - Only need to test downstream impacts + - Example: + ```bash + rush build --impacted-by-except @my-company/my-project + ``` + +6. `--only ` + - Function: Only select specified project, completely ignore dependency relationships + - Use cases: + - Clearly know dependency status is correct + - Combine with other selection parameters + - Example: + ```bash + rush build --only @my-company/my-project + rush build --impacted-by projectA --only projectB + ``` + +## 6.2 Troubleshooting + +1. Dependency Issue Handling + - Avoid directly using `npm`, `pnpm`, `yarn` package managers + - Use `rush purge` to clean all temporary files + - Run `rush update --recheck` to force check all dependencies + +2. Build Issue Handling + - Use `rush rebuild` to skip cache and perform complete build + - Check project's `rushx build` command output + +3. Logging and Diagnostics + - Use `--verbose` parameter for detailed logs + - Verify command parameter correctness diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e4f101080d..e0932cb45c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,37 +8,116 @@ on: workflow_dispatch: jobs: build: - name: Node.js v${{ matrix.NodeVersion }} - runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - NodeVersion: [14, 16] + include: + # When Node 18 is removed, remove the special cases in + # - build-tests-samples/heft-storybook-v9-react-tutorial/build.js + # - build-tests-samples/heft-storybook-v9-react-tutorial-app/build.js + # - The "globalOverrides" entry for "@vscode/vsce>cheerio" in common/config/rush/pnpm-config.json + # - libraries/module-minifier/src/cryptoPolyfill.ts + - NodeVersion: 18.20.x + NodeVersionDisplayName: 18 + OS: ubuntu-latest + - NodeVersion: 20.18.x + NodeVersionDisplayName: 20 + OS: ubuntu-latest + - NodeVersion: 22.19.x + NodeVersionDisplayName: 22 + OS: ubuntu-latest + - NodeVersion: 24.11.x + NodeVersionDisplayName: 24 + OS: ubuntu-latest + - NodeVersion: 24.11.x + NodeVersionDisplayName: 24 + OS: windows-latest + name: Node.js v${{ matrix.NodeVersionDisplayName }} (${{ matrix.OS }}) + runs-on: ${{ matrix.OS }} + env: + INSTALL_RUN_RUSH_LOCKFILE_PATH: ${{ github.workspace }}/repo-a/common/config/validation/rush-package-lock.json steps: - - uses: actions/checkout@v3 + - name: Create ~/.rush-user/settings.json + shell: pwsh + # Create a .rush-user/settings.json file that looks like this: + # + # { "buildCacheFolder": "//rush-cache" } + # + # This configures the local cache to be shared between all Rush repos. This allows us to run a build in + # one clone of the repo (repo-a), and restore from the cache in another clone of the repo (repo-b) to test + # the build cache. + run: | + mkdir -p $HOME/.rush-user + @{ buildCacheFolder = Join-Path ${{ github.workspace }} rush-cache } | ConvertTo-Json > $HOME/.rush-user/settings.json + + - uses: actions/checkout@v6 with: fetch-depth: 2 + path: repo-a + - name: Git config user run: | git config --local user.name "Rushbot" git config --local user.email "rushbot@users.noreply.github.com" - - uses: actions/setup-node@v3 + working-directory: repo-a + + - uses: actions/setup-node@v6 with: node-version: ${{ matrix.NodeVersion }} + - name: Verify Change Logs run: node common/scripts/install-run-rush.js change --verify + working-directory: repo-a + - name: Rush Install run: node common/scripts/install-run-rush.js install + working-directory: repo-a + + # - if: runner.os == 'Linux' + # name: Start xvfb + # run: /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + # working-directory: repo-a + - name: Rush retest (install-run-rush) run: node common/scripts/install-run-rush.js retest --verbose --production - env: - # Prevent time-based browserslist update warning - # See https://github.com/microsoft/rushstack/issues/2981 - BROWSERSLIST_IGNORE_OLD_DATA: 1 - - name: Rush test (rush-lib) - run: node apps/rush/lib/start-dev.js test --verbose --production --timeline - env: - # Prevent time-based browserslist update warning - # See https://github.com/microsoft/rushstack/issues/2981 - BROWSERSLIST_IGNORE_OLD_DATA: 1 + working-directory: repo-a + + - name: Run package manager integration tests + run: npm run test + working-directory: repo-a/build-tests/rush-package-manager-integration-test + - name: Ensure repo README is up-to-date - run: node repo-scripts/repo-toolbox/lib/start.js readme --verify + run: node repo-scripts/repo-toolbox/lib-commonjs/start.js readme --verify + working-directory: repo-a + + - name: Collect JSON schemas + run: node repo-scripts/repo-toolbox/lib-commonjs/start.js collect-project-files --subfolder temp/json-schemas --output-path ${GITHUB_WORKSPACE}/artifacts/json-schemas + working-directory: repo-a + + - name: Collect API review files + run: node repo-scripts/repo-toolbox/lib-commonjs/start.js collect-project-files --subfolder temp/api --output-path ${GITHUB_WORKSPACE}/artifacts/api + working-directory: repo-a + + - name: Clone another copy of the repo to test the build cache + uses: actions/checkout@v6 + with: + fetch-depth: 1 + path: repo-b + + - name: Git config user + run: | + git config --local user.name "Rushbot" + git config --local user.email "rushbot@users.noreply.github.com" + working-directory: repo-b + + - name: Rush update (rush-lib) + run: node ${{ github.workspace }}/repo-a/apps/rush/lib-commonjs/start-dev.js update + working-directory: repo-b + + - name: Rush test (rush-lib) + run: node ${{ github.workspace }}/repo-a/apps/rush/lib-commonjs/start-dev.js test --verbose --production --timeline + working-directory: repo-b + + - name: Rush test (rush-lib) again to verify build cache hits + run: node ${{ github.workspace }}/repo-a/apps/rush/lib-commonjs/start-dev.js test --verbose --production --timeline + working-directory: repo-b diff --git a/.github/workflows/file-doc-tickets.yml b/.github/workflows/file-doc-tickets.yml index a22ca91016d..b2968d451f7 100644 --- a/.github/workflows/file-doc-tickets.yml +++ b/.github/workflows/file-doc-tickets.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Use nodejs - uses: actions/setup-node@v3 + uses: actions/setup-node@v6 with: node-version: 16 - name: Parse PR body @@ -76,7 +76,7 @@ jobs: fi - name: File ticket if: ${{ env.FILE_TICKET == '1' }} - uses: peter-evans/create-issue-from-file@af31b99c72f9e91877aea8a2d96fd613beafac84 # @v4 (locked) + uses: peter-evans/create-issue-from-file@fca9117c27cdc29c6c4db3b86c48e4115a786710 # v6.0.0 with: repository: microsoft/rushstack-websites token: '${{ secrets.RUSHSTACK_WEBSITES_PR_TOKEN }}' diff --git a/.gitignore b/.gitignore index b17e572cd5a..6f360c85f74 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,10 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data *.pid @@ -10,35 +14,39 @@ yarn-error.log* *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover -lib-cov +lib-cov/ # Coverage directory used by tools like istanbul -coverage +coverage/ # nyc test coverage -.nyc_output +.nyc_output/ # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt +.grunt/ # Bower dependency directory (https://bower.io/) -bower_components +bower_components/ # node-waf configuration -.lock-wscript +.lock-wscript/ # Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release +build/Release/ # Dependency directories node_modules/ +**/.storybook/node_modules jspm_packages/ +# TypeScript cache +*.tsbuildinfo + # Optional npm cache directory -.npm +.npm/ # Optional eslint cache -.eslintcache +.eslintcache/ # Optional REPL history .node_repl_history @@ -51,9 +59,32 @@ jspm_packages/ # dotenv environment variables file .env +.env.development.local +.env.test.local +.env.production.local +.env.local # next.js build output -.next +.next/ + +# Docusaurus cache and generated files +.docusaurus/ + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# yarn v2 +.yarn/cache/ +.yarn/unplugged/ +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* # OS X temporary files .DS_Store @@ -64,26 +95,45 @@ jspm_packages/ *.iml # Visual Studio Code -.vscode +.vscode/ +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/debug-certificate-manager.json +!.vscode/mcp.json # Rush temporary files common/deploy/ common/temp/ common/autoinstallers/*/.npmrc **/.rush/temp/ +*.lock + +# Common toolchain intermediate files +build/ +temp/ +lib/ +lib-amd/ +lib-dts/ +lib-es6/ +lib-esm/ +lib-esnext/ +lib-commonjs/ +lib-shim/ +dist/ +dist-storybook/ +*.tsbuildinfo # Heft temporary files -.cache -.heft +.cache/ +.heft/ -# Common toolchain intermediate files -temp -lib -lib-amd -lib-es6 -lib-esnext -lib-commonjs -lib-shim -dist -*.scss.ts -*.sass.ts +# VS Code test runner files +.vscode-test/ + +# Playwright test outputs +playwright-report/ +test-results/ + +# Claude Code local configuration +.claude/*.local.json +**/tmpclaude-*-cwd diff --git a/.prettierignore b/.prettierignore index f577e87f844..45e09e79d75 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,6 +7,10 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data *.pid @@ -14,35 +18,38 @@ yarn-error.log* *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover -lib-cov +lib-cov/ # Coverage directory used by tools like istanbul -coverage +coverage/ # nyc test coverage -.nyc_output +.nyc_output/ # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt +.grunt/ # Bower dependency directory (https://bower.io/) -bower_components +bower_components/ # node-waf configuration -.lock-wscript +.lock-wscript/ # Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release +build/Release/ # Dependency directories node_modules/ jspm_packages/ +# TypeScript cache +*.tsbuildinfo + # Optional npm cache directory -.npm +.npm/ # Optional eslint cache -.eslintcache +.eslintcache/ # Optional REPL history .node_repl_history @@ -55,48 +62,85 @@ jspm_packages/ # dotenv environment variables file .env +.env.development.local +.env.test.local +.env.production.local +.env.local # next.js build output -.next +.next/ + +# Docusaurus cache and generated files +.docusaurus/ + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# yarn v2 +.yarn/cache/ +.yarn/unplugged/ +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* # OS X temporary files .DS_Store +# IntelliJ IDEA project files; if you want to commit IntelliJ settings, this recipe may be helpful: +# https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +.idea/ +*.iml + +# Visual Studio Code +.vscode/ +!.vscode/tasks.json +!.vscode/launch.json + # Rush temporary files common/deploy/ common/temp/ +common/autoinstallers/*/.npmrc **/.rush/temp/ +*.lock # Common toolchain intermediate files -temp -lib -lib-amd -lib-es6 -dist -*.scss.ts -*.sass.ts - -# Visual Studio Code -.vscode - -# Remove eventually -package-deps.json +temp/ +lib/ +lib-amd/ +lib-es6/ +lib-esm/ +lib-esnext/ +lib-commonjs/ +lib-shim/ +dist/ +dist-storybook/ +*.tsbuildinfo + +# Heft temporary files +.cache/ +.heft/ #------------------------------------------------------------------------------------------------------------------- # Prettier-specific overrides #------------------------------------------------------------------------------------------------------------------- # Machine-egnerated files -common/reviews -common/changes -common/scripts +common/reviews/ +common/changes/ +common/scripts/ common/config/rush/browser-approved-packages.json common/config/rush/nonbrowser-approved-packages.json CHANGELOG.* pnpm-lock.yaml build-tests/*/etc -dist-dev -dist-prod +dist-dev/ +dist-prod/ # Prettier doesn't understand the /*[LINE "HYPOTHETICAL"]*/ macros in these files: libraries/rush-lib/assets/rush-init/ @@ -104,5 +148,11 @@ libraries/rush-lib/assets/rush-init/ # These are intentionally invalid files libraries/heft-config-file/src/test/errorCases/invalidJson/config.json +# common scripts in sandbox repo +build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/scripts/ + # We'll consider enabling this later; Prettier reformats code blocks, which affects end-user content *.md + +# Don't format these YAML files - they were generated by pnpm and are used in unit tests +libraries/rush-lib/src/logic/test/shrinkwrapFile/*.yaml diff --git a/.trae/project_rules.md b/.trae/project_rules.md new file mode 100644 index 00000000000..6f7d69f118d --- /dev/null +++ b/.trae/project_rules.md @@ -0,0 +1,409 @@ +You are a Rush monorepo development and management expert. Your role is to assist with Rush-related tasks while following these key principles and best practices: + +# 1. Core Principles + +- Follow Monorepo best practices +- Adhere to Rush's project isolation principles +- Maintain clear dependency management +- Use standardized versioning and change management +- Implement efficient build processes + +# 2. Project Structure and Organization + +## 2.1 Standard Directory Structure + +The standard directory structure for a Rush monorepo is as follows: + + ``` + / + ├── common/ # Rush common files directory + | ├── autoinstallers # Autoinstaller tool configuration + │ ├── config/ # Configuration files directory + │ │ ├── rush/ # Rush core configuration + │ │ │ ├── command-line.json # Command line configuration + │ │ │ ├── build-cache.json # Build cache configuration + │ │ │ └── subspaces.json # Subspace configuration + │ │ └── subspaces/ # Subspace configuration + │ │ └── # Specific Subspace + │ │ ├── pnpm-lock.yaml # Subspace dependency lock file + │ │ ├── .pnpmfile.cjs # PNPM hook script + │ │ ├── common-versions.json # Subspace version configuration + │ │ ├── pnpm-config.json # PNPM configuration + │ │ └── repo-state.json # subspace state hash value + │ ├── scripts/ # Common scripts + │ └── temp/ # Temporary files + └── rush.json # Rush main configuration file + ``` + +## 2.2 Important Configuration Files + +1. `rush.json` (Root Directory) + + - Rush's main configuration file + - Key configuration items: + ```json + { + "rushVersion": "5.x.x", // Rush version + // Choose PNPM as package manager + "pnpmVersion": "8.x.x", + // Or use NPM + // "npmVersion": "8.x.x", + // Or use Yarn + // "yarnVersion": "1.x.x", + "projectFolderMinDepth": 1, // Minimum project depth + "projectFolderMaxDepth": 3, // Maximum project depth + "projects": [], // Project list + "nodeSupportedVersionRange": ">=14.15.0", // Node.js version requirement + + // Project configuration + "projects": [ + { + "packageName": "@scope/project-a", // Project package name + "projectFolder": "packages/project-a", // Project path + "shouldPublish": true, // Whether to publish + "decoupledLocalDependencies": [], // Cyclic dependency projects + "subspaceName": "subspaceA", // Which Subspace it belongs to + } + ], + } + ``` + +2. `common/config/rush/command-line.json` + + - Custom commands and parameter configuration + - Command types: + 1. `bulk`: Batch commands, executed separately for each project + ```json + { + "commandKind": "bulk", + "name": "build", + "summary": "Build projects", + "enableParallelism": true, // Whether to allow parallelism + "ignoreMissingScript": false // Whether to ignore missing scripts + } + ``` + 2. `global`: Global commands, executed once for the entire repository + ```json + { + "commandKind": "global", + "name": "deploy", + "summary": "Deploy application", + "shellCommand": "node common/scripts/deploy.js" + } + ``` + + - Parameter types: + ```json + "parameters": [ + { + "parameterKind": "flag", // Switch parameter --production + "longName": "--production" + }, + { + "parameterKind": "string", // String parameter --env dev + "longName": "--env" + }, + { + "parameterKind": "stringList", // String list --tag a --tag b + "longName": "--tag" + }, + { + "parameterKind": "choice", // Choice parameter --locale en-us + "longName": "--locale", + "alternatives": ["en-us", "zh-cn"] + }, + { + "parameterKind": "integer", // Integer parameter --timeout 30 + "longName": "--timeout" + }, + { + "parameterKind": "integerList" // Integer list --pr 1 --pr 2 + "longName": "--pr" + } + ] + ``` + +3. `common/config/subspaces//common-versions.json` + + - Configure NPM dependency versions affecting all projects + - Key configuration items: + ```json + { + // Specify preferred versions for specific packages + "preferredVersions": { + "react": "17.0.2", // Restrict react version + "typescript": "~4.5.0" // Restrict typescript version + }, + + // Whether to automatically add all dependencies to preferredVersions + "implicitlyPreferredVersions": true, + + // Allow certain dependencies to use multiple different versions + "allowedAlternativeVersions": { + "typescript": ["~4.5.0", "~4.6.0"] + } + } + ``` + +4. `common/config/rush/subspaces.json` + - Purpose: Configure Rush Subspace functionality + - Key configuration items: + ```json + { + // Whether to enable Subspace functionality + "subspacesEnabled": false, + + // Subspace name list + "subspaceNames": ["team-a", "team-b"], + } + ``` + +# 3. Command Usage + +## 3.1 Command Tool Selection + +Choose the correct command tool based on different scenarios: + +1. `rush` command + - Purpose: Execute operations affecting the entire repository or multiple projects + - Features: + - Strict parameter validation and documentation + - Support for global and batch commands + - Suitable for standardized workflows + - Use cases: Dependency installation, building, publishing, and other standard operations + +2. `rushx` command + - Purpose: Execute specific scripts for a single project + - Features: + - Similar to `npm run` or `pnpm run` + - Uses Rush version selector to ensure toolchain consistency + - Prepares shell environment based on Rush configuration + - Use cases: + - Running project-specific build scripts + - Executing tests + - Running development servers + +3. `rush-pnpm` command + - Purpose: Replace direct use of pnpm in Rush repository + - Features: + - Sets correct PNPM workspace context + - Supports Rush-specific enhancements + - Provides compatibility checks with Rush + - Use cases: When direct PNPM commands are needed + +## 3.2 Common Commands Explained + +1. `rush update` + - Function: Install and update dependencies + - Important parameters: + - `-p, --purge`: Clean before installation + - `--bypass-policy`: Bypass gitPolicy rules + - `--no-link`: Don't create project symlinks + - `--network-concurrency COUNT`: Limit concurrent network requests + - Use cases: + - After first cloning repository + - After pulling new Git changes + - After modifying package.json + - When dependencies need updating + +2. `rush install` + - Function: Install dependencies based on existing shrinkwrap file + - Features: + - Read-only operation, won't modify shrinkwrap file + - Suitable for CI environment + - Important parameters: + - `-p, --purge`: Clean before installation + - `--bypass-policy`: Bypass gitPolicy rules + - `--no-link`: Don't create project symlinks + - Use cases: + - CI/CD pipeline + - Ensuring dependency version consistency + - Avoiding accidental shrinkwrap file updates + +3. `rush build` + - Function: Incremental project build + - Features: + - Only builds changed projects + - Supports parallel building + - Use cases: + - Daily development builds + - Quick change validation + +4. `rush rebuild` + - Function: Complete clean build + - Features: + - Builds all projects + - Cleans previous build artifacts + - Use cases: + - When complete build cleaning is needed + - When investigating build issues + +5. `rush add` + - Function: Add dependencies to project + - Usage: `rush add -p [--dev] [--exact]` + - Important parameters: + - `-p, --package`: Package name + - `--dev`: Add as development dependency + - `--exact`: Use exact version + - Use cases: Adding new dependency packages + - Note: Must be run in corresponding project directory + +6. `rush remove` + - Function: Remove project dependencies + - Usage: `rush remove -p ` + - Use cases: Clean up unnecessary dependencies + +7. `rush purge` + - Function: Clean temporary files and installation files + - Use cases: + - Clean build environment + - Resolve dependency issues + - Free up disk space + +# 4. Dependency Management + +## 4.1 Package Manager Selection + +Specify in `rush.json`: + ```json + { + // Choose PNPM as package manager + "pnpmVersion": "8.x.x", + // Or use NPM + // "npmVersion": "8.x.x", + // Or use Yarn + // "yarnVersion": "1.x.x", + } + ``` + +## 4.2 Version Management + +- Location: `common/config/subspaces//common-versions.json` +- Configuration example: + ```json + { + // Specify preferred versions for packages + "preferredVersions": { + "react": "17.0.2", + "typescript": "~4.5.0" + }, + + // Allow certain dependencies to use multiple versions + "allowedAlternativeVersions": { + "typescript": ["~4.5.0", "~4.6.0"] + } + } + ``` + +## 4.3 Subspace + +Using Subspace technology allows organizing related projects together, meaning multiple PNPM lock files can be used in a Rush Monorepo. Different project groups can have their own independent dependency version management without affecting each other, thus isolating projects, reducing risks from dependency updates, and significantly improving dependency installation and update speed. + +Declare which Subspaces exist in `common/config/rush/subspaces.json`, and declare which Subspace each project belongs to in `rush.json`'s `subspaceName`. + +# 5. Caching Capabilities + +## 5.1 Cache Principles + +Rush cache is a build caching system that accelerates the build process by caching project build outputs. Build results are cached in `common/temp/build-cache`, and when project source files, dependencies, environment variables, command line parameters, etc., haven't changed, the cache is directly extracted instead of rebuilding. + +## 5.2 Core Configuration + +Configuration file: `/config/rush-project.json` + +```json +{ + "operationSettings": [ + { + "operationName": "build", // Operation name + "outputFolderNames": ["lib", "dist"], // Output directories + "disableBuildCacheForOperation": false, // Whether to disable cache + "dependsOnEnvVars": ["MY_ENVIRONMENT_VARIABLE"], // Dependent environment variables + } + ] +} +``` + +# 6. Best Practices + +## 6.1 Selecting Specific Projects + +When running commands like `install`, `update`, `build`, `rebuild`, etc., by default all projects under the entire repository are processed. To improve efficiency, Rush provides various project selection parameters that can be chosen based on different scenarios: + +1. `--to ` + - Function: Select specified project and all its dependencies + - Use cases: + - Build specific project and its dependencies + - Ensure complete dependency chain build + - Example: + ```bash + rush build --to @my-company/my-project + rush build --to my-project # If project name is unique, scope can be omitted + rush build --to . # Use current directory's project + ``` + +2. `--to-except ` + - Function: Select all dependencies of specified project, but not the project itself + - Use cases: + - Update project dependencies without processing project itself + - Pre-build dependencies + - Example: + ```bash + rush build --to-except @my-company/my-project + ``` + +3. `--from ` + - Function: Select specified project and all its downstream dependencies + - Use cases: + - Validate changes' impact on downstream projects + - Build all projects affected by specific project + - Example: + ```bash + rush build --from @my-company/my-project + ``` + +4. `--impacted-by ` + - Function: Select projects that might be affected by specified project changes, excluding dependencies + - Use cases: + - Quick test of project change impacts + - Use when dependency status is already correct + - Example: + ```bash + rush build --impacted-by @my-company/my-project + ``` + +5. `--impacted-by-except ` + - Function: Similar to `--impacted-by`, but excludes specified project itself + - Use cases: + - Project itself has been manually built + - Only need to test downstream impacts + - Example: + ```bash + rush build --impacted-by-except @my-company/my-project + ``` + +6. `--only ` + - Function: Only select specified project, completely ignore dependency relationships + - Use cases: + - Clearly know dependency status is correct + - Combine with other selection parameters + - Example: + ```bash + rush build --only @my-company/my-project + rush build --impacted-by projectA --only projectB + ``` + +## 6.2 Troubleshooting + +1. Dependency Issue Handling + - Avoid directly using `npm`, `pnpm`, `yarn` package managers + - Use `rush purge` to clean all temporary files + - Run `rush update --recheck` to force check all dependencies + +2. Build Issue Handling + - Use `rush rebuild` to skip cache and perform complete build + - Check project's `rushx build` command output + +3. Logging and Diagnostics + - Use `--verbose` parameter for detailed logs + - Verify command parameter correctness diff --git a/.vscode/debug-certificate-manager.json b/.vscode/debug-certificate-manager.json new file mode 100644 index 00000000000..c574800aa91 --- /dev/null +++ b/.vscode/debug-certificate-manager.json @@ -0,0 +1,3 @@ +{ + "storePath": "common/temp/debug-certificates" +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index d52370624c1..265fd0e412d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,7 @@ "name": "Rush Debug", "type": "node", "request": "launch", - "program": "${workspaceRoot}/apps/rush/lib/start-dev.js", + "program": "${workspaceRoot}/apps/rush/lib-commonjs/start-dev.js", "stopOnEntry": true, "args": [ "start" @@ -16,10 +16,17 @@ "--nolazy", "--inspect-brk" ], + "skipFiles": ["/**"], + // Don't scan the file system on startup + "outFiles": [], + // Evaluate source maps for all workspace-local files + "resolveSourceMapLocations": ["${workspaceFolder}/**", "!**/node_modules/**"], "env": { "NODE_ENV": "development" }, - "sourceMaps": true + "sourceMaps": true, + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" }, { "type": "node", @@ -29,9 +36,11 @@ "runtimeArgs": [ "--nolazy", "--inspect-brk", - "${workspaceFolder}/apps/heft/lib/start.js", + "${workspaceFolder}/apps/heft/lib-commonjs/start.js", "--debug", - "test-watch" + "test", + "--test-path-pattern", + "${fileBasenameNoExtension}" ], "skipFiles": ["/**"], "outFiles": [], @@ -47,7 +56,7 @@ "runtimeArgs": [ "--nolazy", "--inspect-brk", - "${workspaceFolder}/apps/heft/lib/start.js", + "${workspaceFolder}/apps/heft/lib-commonjs/start.js", "--debug", "build" ], @@ -57,11 +66,70 @@ "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" }, + { + "type": "node", + "request": "launch", + "name": "Debug Clean Build in Selected Project (Heft)", + "cwd": "${fileDirname}", + "runtimeArgs": [ + "--nolazy", + "--inspect-brk", + "${workspaceFolder}/apps/heft/lib-commonjs/start.js", + "--debug", + "build", + "--clean" + ], + "skipFiles": ["/**"], + "outFiles": [], + "sourceMaps": true, + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" + }, { "name": "Attach", "type": "node", "request": "attach", - "port": 5858 + "port": 9229, + "outFiles": [], + }, + { + "name": "Launch Rush Extension", + "type": "extensionHost", + "request": "launch", + "cwd": "${workspaceFolder}/vscode-extensions/rush-vscode-extension", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}/vscode-extensions/rush-vscode-extension/dist/vsix/unpacked" + ], + "outFiles": [ + "${workspaceFolder}/vscode-extensions/rush-vscode-extension/**" + ] + // "preLaunchTask": "npm: build:watch - vscode-extensions/rush-vscode-extension" + }, + { + "name": "Launch Debug Certificate Manager VS Code Extension", + "type": "extensionHost", + "request": "launch", + "cwd": "${workspaceFolder}/vscode-extensions/debug-certificate-manager-vscode-extension/dist/vsix/unpacked", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}/vscode-extensions/debug-certificate-manager-vscode-extension/dist/vsix/unpacked" + ], + "sourceMaps": true, + "outFiles": [ + "${workspaceFolder}/vscode-extensions/debug-certificate-manager-vscode-extension/**" + ] + }, + { + "name": "Launch Playwright Local Browser Server VS Code Extension", + "type": "extensionHost", + "request": "launch", + "cwd": "${workspaceFolder}/vscode-extensions/playwright-local-browser-server-vscode-extension/dist/vsix/unpacked", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}/vscode-extensions/playwright-local-browser-server-vscode-extension/dist/vsix/unpacked" + ], + "sourceMaps": true, + "outFiles": [ + "${workspaceFolder}/vscode-extensions/playwright-local-browser-server-vscode-extension/**/*.js" + ] } ] } diff --git a/.vscode/mcp.json b/.vscode/mcp.json new file mode 100644 index 00000000000..c956558d21b --- /dev/null +++ b/.vscode/mcp.json @@ -0,0 +1,12 @@ +{ + "servers": { + "playwright": { + "type": "stdio", + "command": "node", + "args": [ + "${workspaceFolder}/apps/playwright-browser-tunnel/lib/PlaywrightMcpBrowserTunnelClientCommandLine.js" + ] + } + }, + "inputs": [] +} \ No newline at end of file diff --git a/.vscode/redis-cobuild.code-workspace b/.vscode/redis-cobuild.code-workspace new file mode 100644 index 00000000000..c51d9ed6da0 --- /dev/null +++ b/.vscode/redis-cobuild.code-workspace @@ -0,0 +1,20 @@ +{ + "folders": [ + { + "name": "rush-redis-cobuild-plugin-integration-test", + "path": "../build-tests/rush-redis-cobuild-plugin-integration-test" + }, + { + "name": "rush-redis-cobuild-plugin", + "path": "../rush-plugins/rush-redis-cobuild-plugin" + }, + { + "name": "rush-lib", + "path": "../libraries/rush-lib" + }, + { + "name": ".vscode", + "path": "../.vscode" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 5367d5843db..0fc07ff37ea 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,5 +21,35 @@ "files.associations": { "**/package.json": "json", "**/*.json": "jsonc" - } + }, + "json.schemas": [ + { + "fileMatch": ["/rush.json"], + "url": "./libraries/rush-lib/src/schemas/rush.schema.json" + }, + { + "fileMatch": ["**/rush-plugin.json"], + "url": "./libraries/rush-lib/src/schemas/rush-plugin-manifest.schema.json" + }, + { + "fileMatch": ["**/config/heft.json"], + "url": "./apps/heft/src/schemas/heft.schema.json" + }, + { + "fileMatch": ["**/config/rig.json"], + "url": "./libraries/rig-package/src/schemas/rig.schema.json" + }, + { + "fileMatch": ["**/config/rush-project.json"], + "url": "./libraries/rush-lib/src/schemas/rush-project.schema.json" + }, + { + "fileMatch": ["**/config/typescript.json"], + "url": "./heft-plugins/heft-typescript-plugin/src/schemas/typescript.schema.json" + }, + { + "fileMatch": ["**/heft-plugin.json"], + "url": "./apps/heft/src/schemas/heft-plugin.schema.json" + } + ] } diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000000..340d63d1fa3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +This is a monorepo, with each published package containing its own license. The +license for each package can be found in the package's folder. + +The projects in this monorepo are licensed under the MIT license. + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/README.md b/README.md index ae8e7af499e..e40ff5073d8 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,17 @@ -[![Zulip chat room](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://rushstack.zulipchat.com/)   [![Build Status](https://github.com/microsoft/rushstack/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/microsoft/rushstack/actions/workflows/ci.yml?query=branch%3Amain)   Open in Visual Studio Code +[![Zulip chat room](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://rushstack.zulipchat.com/)   [![Build Status](https://github.com/microsoft/rushstack/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/microsoft/rushstack/actions/workflows/ci.yml?query=branch%3Amain) -The home for various projects maintained by the Rush Stack community, whose mission is to develop reusable tooling + +The home for projects maintained by the Rush Stack community. Our mission is to develop reusable tooling for large scale TypeScript monorepos. - [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=69618902&machine=standardLinux32gb&devcontainer_path=.devcontainer%2Fdevcontainer.json&location=WestUs2) +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=69618902&machine=standardLinux32gb&devcontainer_path=.devcontainer%2Fdevcontainer.json&location=WestUs2) + +
+ Open in VS Code web view +
## Documentation Links @@ -18,9 +23,11 @@ for large scale TypeScript monorepos. - [API reference](https://api.rushstack.io/) - browse API documentation for NPM packages - [Zulip chat room](https://rushstack.zulipchat.com/) - chat with the Rush Stack developers - [Rush](https://rushjs.io/) - a build orchestrator for large scale TypeScript monorepos +- [Heft](https://heft.rushstack.io/) - our recommended tool that integrates with Rush - [API Extractor](https://api-extractor.com/) - create .d.ts rollups and track your TypeScript API signatures - [API Documenter](https://api-extractor.com/pages/setup/generating_docs/) - use TSDoc comments to publish an API documentation website - +- [Lockfile Explorer](https://lfx.rushstack.io/) - investigate and solve version conflicts for PNPM lockfiles +- [TSDoc](https://tsdoc.org/) - the standard for doc comments in TypeScript code ## Related Repos @@ -30,8 +37,7 @@ These GitHub repositories provide supplementary resources for Rush Stack: illustrate various project setups, including how to use Heft with other popular JavaScript frameworks - [rush-example](https://github.com/microsoft/rush-example) - a minimal Rush repo that demonstrates the fundamentals of Rush without relying on any other Rush Stack tooling -- [rushstack-legacy](https://github.com/microsoft/rushstack-legacy) - older projects that are still maintained - but no longer actively developed +- [rushstack-websites](https://github.com/microsoft/rushstack-websites) - Docusaurus monorepo for our websites @@ -44,11 +50,16 @@ These GitHub repositories provide supplementary resources for Rush Stack: | ------ | ------- | --------- | ------- | | [/apps/api-documenter](./apps/api-documenter/) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fapi-documenter.svg)](https://badge.fury.io/js/%40microsoft%2Fapi-documenter) | [changelog](./apps/api-documenter/CHANGELOG.md) | [@microsoft/api-documenter](https://www.npmjs.com/package/@microsoft/api-documenter) | | [/apps/api-extractor](./apps/api-extractor/) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fapi-extractor.svg)](https://badge.fury.io/js/%40microsoft%2Fapi-extractor) | [changelog](./apps/api-extractor/CHANGELOG.md) | [@microsoft/api-extractor](https://www.npmjs.com/package/@microsoft/api-extractor) | +| [/apps/cpu-profile-summarizer](./apps/cpu-profile-summarizer/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fcpu-profile-summarizer.svg)](https://badge.fury.io/js/%40rushstack%2Fcpu-profile-summarizer) | [changelog](./apps/cpu-profile-summarizer/CHANGELOG.md) | [@rushstack/cpu-profile-summarizer](https://www.npmjs.com/package/@rushstack/cpu-profile-summarizer) | | [/apps/heft](./apps/heft/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft.svg)](https://badge.fury.io/js/%40rushstack%2Fheft) | [changelog](./apps/heft/CHANGELOG.md) | [@rushstack/heft](https://www.npmjs.com/package/@rushstack/heft) | | [/apps/lockfile-explorer](./apps/lockfile-explorer/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Flockfile-explorer.svg)](https://badge.fury.io/js/%40rushstack%2Flockfile-explorer) | [changelog](./apps/lockfile-explorer/CHANGELOG.md) | [@rushstack/lockfile-explorer](https://www.npmjs.com/package/@rushstack/lockfile-explorer) | +| [/apps/playwright-browser-tunnel](./apps/playwright-browser-tunnel/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fplaywright-browser-tunnel.svg)](https://badge.fury.io/js/%40rushstack%2Fplaywright-browser-tunnel) | [changelog](./apps/playwright-browser-tunnel/CHANGELOG.md) | [@rushstack/playwright-browser-tunnel](https://www.npmjs.com/package/@rushstack/playwright-browser-tunnel) | | [/apps/rundown](./apps/rundown/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frundown.svg)](https://badge.fury.io/js/%40rushstack%2Frundown) | [changelog](./apps/rundown/CHANGELOG.md) | [@rushstack/rundown](https://www.npmjs.com/package/@rushstack/rundown) | | [/apps/rush](./apps/rush/) | [![npm version](https://badge.fury.io/js/%40microsoft%2Frush.svg)](https://badge.fury.io/js/%40microsoft%2Frush) | [changelog](./apps/rush/CHANGELOG.md) | [@microsoft/rush](https://www.npmjs.com/package/@microsoft/rush) | +| [/apps/rush-mcp-server](./apps/rush-mcp-server/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fmcp-server.svg)](https://badge.fury.io/js/%40rushstack%2Fmcp-server) | [changelog](./apps/rush-mcp-server/CHANGELOG.md) | [@rushstack/mcp-server](https://www.npmjs.com/package/@rushstack/mcp-server) | | [/apps/trace-import](./apps/trace-import/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Ftrace-import.svg)](https://badge.fury.io/js/%40rushstack%2Ftrace-import) | [changelog](./apps/trace-import/CHANGELOG.md) | [@rushstack/trace-import](https://www.npmjs.com/package/@rushstack/trace-import) | +| [/apps/zipsync](./apps/zipsync/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fzipsync.svg)](https://badge.fury.io/js/%40rushstack%2Fzipsync) | [changelog](./apps/zipsync/CHANGELOG.md) | [@rushstack/zipsync](https://www.npmjs.com/package/@rushstack/zipsync) | +| [/eslint/eslint-bulk](./eslint/eslint-bulk/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Feslint-bulk.svg)](https://badge.fury.io/js/%40rushstack%2Feslint-bulk) | [changelog](./eslint/eslint-bulk/CHANGELOG.md) | [@rushstack/eslint-bulk](https://www.npmjs.com/package/@rushstack/eslint-bulk) | | [/eslint/eslint-config](./eslint/eslint-config/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Feslint-config.svg)](https://badge.fury.io/js/%40rushstack%2Feslint-config) | [changelog](./eslint/eslint-config/CHANGELOG.md) | [@rushstack/eslint-config](https://www.npmjs.com/package/@rushstack/eslint-config) | | [/eslint/eslint-patch](./eslint/eslint-patch/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Feslint-patch.svg)](https://badge.fury.io/js/%40rushstack%2Feslint-patch) | [changelog](./eslint/eslint-patch/CHANGELOG.md) | [@rushstack/eslint-patch](https://www.npmjs.com/package/@rushstack/eslint-patch) | | [/eslint/eslint-plugin](./eslint/eslint-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Feslint-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Feslint-plugin) | [changelog](./eslint/eslint-plugin/CHANGELOG.md) | [@rushstack/eslint-plugin](https://www.npmjs.com/package/@rushstack/eslint-plugin) | @@ -56,25 +67,40 @@ These GitHub repositories provide supplementary resources for Rush Stack: | [/eslint/eslint-plugin-security](./eslint/eslint-plugin-security/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Feslint-plugin-security.svg)](https://badge.fury.io/js/%40rushstack%2Feslint-plugin-security) | [changelog](./eslint/eslint-plugin-security/CHANGELOG.md) | [@rushstack/eslint-plugin-security](https://www.npmjs.com/package/@rushstack/eslint-plugin-security) | | [/heft-plugins/heft-api-extractor-plugin](./heft-plugins/heft-api-extractor-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-api-extractor-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-api-extractor-plugin) | [changelog](./heft-plugins/heft-api-extractor-plugin/CHANGELOG.md) | [@rushstack/heft-api-extractor-plugin](https://www.npmjs.com/package/@rushstack/heft-api-extractor-plugin) | | [/heft-plugins/heft-dev-cert-plugin](./heft-plugins/heft-dev-cert-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-dev-cert-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-dev-cert-plugin) | [changelog](./heft-plugins/heft-dev-cert-plugin/CHANGELOG.md) | [@rushstack/heft-dev-cert-plugin](https://www.npmjs.com/package/@rushstack/heft-dev-cert-plugin) | +| [/heft-plugins/heft-isolated-typescript-transpile-plugin](./heft-plugins/heft-isolated-typescript-transpile-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-isolated-typescript-transpile-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-isolated-typescript-transpile-plugin) | [changelog](./heft-plugins/heft-isolated-typescript-transpile-plugin/CHANGELOG.md) | [@rushstack/heft-isolated-typescript-transpile-plugin](https://www.npmjs.com/package/@rushstack/heft-isolated-typescript-transpile-plugin) | | [/heft-plugins/heft-jest-plugin](./heft-plugins/heft-jest-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-jest-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-jest-plugin) | [changelog](./heft-plugins/heft-jest-plugin/CHANGELOG.md) | [@rushstack/heft-jest-plugin](https://www.npmjs.com/package/@rushstack/heft-jest-plugin) | +| [/heft-plugins/heft-json-schema-typings-plugin](./heft-plugins/heft-json-schema-typings-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-json-schema-typings-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-json-schema-typings-plugin) | [changelog](./heft-plugins/heft-json-schema-typings-plugin/CHANGELOG.md) | [@rushstack/heft-json-schema-typings-plugin](https://www.npmjs.com/package/@rushstack/heft-json-schema-typings-plugin) | | [/heft-plugins/heft-lint-plugin](./heft-plugins/heft-lint-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-lint-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-lint-plugin) | [changelog](./heft-plugins/heft-lint-plugin/CHANGELOG.md) | [@rushstack/heft-lint-plugin](https://www.npmjs.com/package/@rushstack/heft-lint-plugin) | +| [/heft-plugins/heft-localization-typings-plugin](./heft-plugins/heft-localization-typings-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-localization-typings-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-localization-typings-plugin) | [changelog](./heft-plugins/heft-localization-typings-plugin/CHANGELOG.md) | [@rushstack/heft-localization-typings-plugin](https://www.npmjs.com/package/@rushstack/heft-localization-typings-plugin) | +| [/heft-plugins/heft-rspack-plugin](./heft-plugins/heft-rspack-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-rspack-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-rspack-plugin) | [changelog](./heft-plugins/heft-rspack-plugin/CHANGELOG.md) | [@rushstack/heft-rspack-plugin](https://www.npmjs.com/package/@rushstack/heft-rspack-plugin) | +| [/heft-plugins/heft-sass-load-themed-styles-plugin](./heft-plugins/heft-sass-load-themed-styles-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-sass-load-themed-styles-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-sass-load-themed-styles-plugin) | [changelog](./heft-plugins/heft-sass-load-themed-styles-plugin/CHANGELOG.md) | [@rushstack/heft-sass-load-themed-styles-plugin](https://www.npmjs.com/package/@rushstack/heft-sass-load-themed-styles-plugin) | | [/heft-plugins/heft-sass-plugin](./heft-plugins/heft-sass-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-sass-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-sass-plugin) | [changelog](./heft-plugins/heft-sass-plugin/CHANGELOG.md) | [@rushstack/heft-sass-plugin](https://www.npmjs.com/package/@rushstack/heft-sass-plugin) | | [/heft-plugins/heft-serverless-stack-plugin](./heft-plugins/heft-serverless-stack-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-serverless-stack-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-serverless-stack-plugin) | [changelog](./heft-plugins/heft-serverless-stack-plugin/CHANGELOG.md) | [@rushstack/heft-serverless-stack-plugin](https://www.npmjs.com/package/@rushstack/heft-serverless-stack-plugin) | +| [/heft-plugins/heft-static-asset-typings-plugin](./heft-plugins/heft-static-asset-typings-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-static-asset-typings-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-static-asset-typings-plugin) | [changelog](./heft-plugins/heft-static-asset-typings-plugin/CHANGELOG.md) | [@rushstack/heft-static-asset-typings-plugin](https://www.npmjs.com/package/@rushstack/heft-static-asset-typings-plugin) | | [/heft-plugins/heft-storybook-plugin](./heft-plugins/heft-storybook-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-storybook-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-storybook-plugin) | [changelog](./heft-plugins/heft-storybook-plugin/CHANGELOG.md) | [@rushstack/heft-storybook-plugin](https://www.npmjs.com/package/@rushstack/heft-storybook-plugin) | | [/heft-plugins/heft-typescript-plugin](./heft-plugins/heft-typescript-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-typescript-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-typescript-plugin) | [changelog](./heft-plugins/heft-typescript-plugin/CHANGELOG.md) | [@rushstack/heft-typescript-plugin](https://www.npmjs.com/package/@rushstack/heft-typescript-plugin) | +| [/heft-plugins/heft-vscode-extension-plugin](./heft-plugins/heft-vscode-extension-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-vscode-extension-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-vscode-extension-plugin) | [changelog](./heft-plugins/heft-vscode-extension-plugin/CHANGELOG.md) | [@rushstack/heft-vscode-extension-plugin](https://www.npmjs.com/package/@rushstack/heft-vscode-extension-plugin) | | [/heft-plugins/heft-webpack4-plugin](./heft-plugins/heft-webpack4-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-webpack4-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-webpack4-plugin) | [changelog](./heft-plugins/heft-webpack4-plugin/CHANGELOG.md) | [@rushstack/heft-webpack4-plugin](https://www.npmjs.com/package/@rushstack/heft-webpack4-plugin) | | [/heft-plugins/heft-webpack5-plugin](./heft-plugins/heft-webpack5-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-webpack5-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-webpack5-plugin) | [changelog](./heft-plugins/heft-webpack5-plugin/CHANGELOG.md) | [@rushstack/heft-webpack5-plugin](https://www.npmjs.com/package/@rushstack/heft-webpack5-plugin) | | [/libraries/api-extractor-model](./libraries/api-extractor-model/) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fapi-extractor-model.svg)](https://badge.fury.io/js/%40microsoft%2Fapi-extractor-model) | [changelog](./libraries/api-extractor-model/CHANGELOG.md) | [@microsoft/api-extractor-model](https://www.npmjs.com/package/@microsoft/api-extractor-model) | +| [/libraries/credential-cache](./libraries/credential-cache/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fcredential-cache.svg)](https://badge.fury.io/js/%40rushstack%2Fcredential-cache) | [changelog](./libraries/credential-cache/CHANGELOG.md) | [@rushstack/credential-cache](https://www.npmjs.com/package/@rushstack/credential-cache) | | [/libraries/debug-certificate-manager](./libraries/debug-certificate-manager/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fdebug-certificate-manager.svg)](https://badge.fury.io/js/%40rushstack%2Fdebug-certificate-manager) | [changelog](./libraries/debug-certificate-manager/CHANGELOG.md) | [@rushstack/debug-certificate-manager](https://www.npmjs.com/package/@rushstack/debug-certificate-manager) | | [/libraries/heft-config-file](./libraries/heft-config-file/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-config-file.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-config-file) | [changelog](./libraries/heft-config-file/CHANGELOG.md) | [@rushstack/heft-config-file](https://www.npmjs.com/package/@rushstack/heft-config-file) | | [/libraries/load-themed-styles](./libraries/load-themed-styles/) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fload-themed-styles.svg)](https://badge.fury.io/js/%40microsoft%2Fload-themed-styles) | [changelog](./libraries/load-themed-styles/CHANGELOG.md) | [@microsoft/load-themed-styles](https://www.npmjs.com/package/@microsoft/load-themed-styles) | | [/libraries/localization-utilities](./libraries/localization-utilities/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Flocalization-utilities.svg)](https://badge.fury.io/js/%40rushstack%2Flocalization-utilities) | [changelog](./libraries/localization-utilities/CHANGELOG.md) | [@rushstack/localization-utilities](https://www.npmjs.com/package/@rushstack/localization-utilities) | +| [/libraries/lookup-by-path](./libraries/lookup-by-path/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Flookup-by-path.svg)](https://badge.fury.io/js/%40rushstack%2Flookup-by-path) | [changelog](./libraries/lookup-by-path/CHANGELOG.md) | [@rushstack/lookup-by-path](https://www.npmjs.com/package/@rushstack/lookup-by-path) | | [/libraries/module-minifier](./libraries/module-minifier/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fmodule-minifier.svg)](https://badge.fury.io/js/%40rushstack%2Fmodule-minifier) | [changelog](./libraries/module-minifier/CHANGELOG.md) | [@rushstack/module-minifier](https://www.npmjs.com/package/@rushstack/module-minifier) | | [/libraries/node-core-library](./libraries/node-core-library/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fnode-core-library.svg)](https://badge.fury.io/js/%40rushstack%2Fnode-core-library) | [changelog](./libraries/node-core-library/CHANGELOG.md) | [@rushstack/node-core-library](https://www.npmjs.com/package/@rushstack/node-core-library) | +| [/libraries/npm-check-fork](./libraries/npm-check-fork/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fnpm-check-fork.svg)](https://badge.fury.io/js/%40rushstack%2Fnpm-check-fork) | [changelog](./libraries/npm-check-fork/CHANGELOG.md) | [@rushstack/npm-check-fork](https://www.npmjs.com/package/@rushstack/npm-check-fork) | +| [/libraries/operation-graph](./libraries/operation-graph/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Foperation-graph.svg)](https://badge.fury.io/js/%40rushstack%2Foperation-graph) | [changelog](./libraries/operation-graph/CHANGELOG.md) | [@rushstack/operation-graph](https://www.npmjs.com/package/@rushstack/operation-graph) | | [/libraries/package-deps-hash](./libraries/package-deps-hash/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fpackage-deps-hash.svg)](https://badge.fury.io/js/%40rushstack%2Fpackage-deps-hash) | [changelog](./libraries/package-deps-hash/CHANGELOG.md) | [@rushstack/package-deps-hash](https://www.npmjs.com/package/@rushstack/package-deps-hash) | | [/libraries/package-extractor](./libraries/package-extractor/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fpackage-extractor.svg)](https://badge.fury.io/js/%40rushstack%2Fpackage-extractor) | [changelog](./libraries/package-extractor/CHANGELOG.md) | [@rushstack/package-extractor](https://www.npmjs.com/package/@rushstack/package-extractor) | +| [/libraries/problem-matcher](./libraries/problem-matcher/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fproblem-matcher.svg)](https://badge.fury.io/js/%40rushstack%2Fproblem-matcher) | [changelog](./libraries/problem-matcher/CHANGELOG.md) | [@rushstack/problem-matcher](https://www.npmjs.com/package/@rushstack/problem-matcher) | | [/libraries/rig-package](./libraries/rig-package/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frig-package.svg)](https://badge.fury.io/js/%40rushstack%2Frig-package) | [changelog](./libraries/rig-package/CHANGELOG.md) | [@rushstack/rig-package](https://www.npmjs.com/package/@rushstack/rig-package) | | [/libraries/rush-lib](./libraries/rush-lib/) | [![npm version](https://badge.fury.io/js/%40microsoft%2Frush-lib.svg)](https://badge.fury.io/js/%40microsoft%2Frush-lib) | | [@microsoft/rush-lib](https://www.npmjs.com/package/@microsoft/rush-lib) | +| [/libraries/rush-pnpm-kit-v10](./libraries/rush-pnpm-kit-v10/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-pnpm-kit-v10.svg)](https://badge.fury.io/js/%40rushstack%2Frush-pnpm-kit-v10) | [changelog](./libraries/rush-pnpm-kit-v10/CHANGELOG.md) | [@rushstack/rush-pnpm-kit-v10](https://www.npmjs.com/package/@rushstack/rush-pnpm-kit-v10) | +| [/libraries/rush-pnpm-kit-v8](./libraries/rush-pnpm-kit-v8/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-pnpm-kit-v8.svg)](https://badge.fury.io/js/%40rushstack%2Frush-pnpm-kit-v8) | [changelog](./libraries/rush-pnpm-kit-v8/CHANGELOG.md) | [@rushstack/rush-pnpm-kit-v8](https://www.npmjs.com/package/@rushstack/rush-pnpm-kit-v8) | +| [/libraries/rush-pnpm-kit-v9](./libraries/rush-pnpm-kit-v9/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-pnpm-kit-v9.svg)](https://badge.fury.io/js/%40rushstack%2Frush-pnpm-kit-v9) | [changelog](./libraries/rush-pnpm-kit-v9/CHANGELOG.md) | [@rushstack/rush-pnpm-kit-v9](https://www.npmjs.com/package/@rushstack/rush-pnpm-kit-v9) | | [/libraries/rush-sdk](./libraries/rush-sdk/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-sdk.svg)](https://badge.fury.io/js/%40rushstack%2Frush-sdk) | | [@rushstack/rush-sdk](https://www.npmjs.com/package/@rushstack/rush-sdk) | | [/libraries/stream-collator](./libraries/stream-collator/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fstream-collator.svg)](https://badge.fury.io/js/%40rushstack%2Fstream-collator) | [changelog](./libraries/stream-collator/CHANGELOG.md) | [@rushstack/stream-collator](https://www.npmjs.com/package/@rushstack/stream-collator) | | [/libraries/terminal](./libraries/terminal/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fterminal.svg)](https://badge.fury.io/js/%40rushstack%2Fterminal) | [changelog](./libraries/terminal/CHANGELOG.md) | [@rushstack/terminal](https://www.npmjs.com/package/@rushstack/terminal) | @@ -83,10 +109,17 @@ These GitHub repositories provide supplementary resources for Rush Stack: | [/libraries/typings-generator](./libraries/typings-generator/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Ftypings-generator.svg)](https://badge.fury.io/js/%40rushstack%2Ftypings-generator) | [changelog](./libraries/typings-generator/CHANGELOG.md) | [@rushstack/typings-generator](https://www.npmjs.com/package/@rushstack/typings-generator) | | [/libraries/worker-pool](./libraries/worker-pool/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fworker-pool.svg)](https://badge.fury.io/js/%40rushstack%2Fworker-pool) | [changelog](./libraries/worker-pool/CHANGELOG.md) | [@rushstack/worker-pool](https://www.npmjs.com/package/@rushstack/worker-pool) | | [/rigs/heft-node-rig](./rigs/heft-node-rig/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-node-rig.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-node-rig) | [changelog](./rigs/heft-node-rig/CHANGELOG.md) | [@rushstack/heft-node-rig](https://www.npmjs.com/package/@rushstack/heft-node-rig) | +| [/rigs/heft-vscode-extension-rig](./rigs/heft-vscode-extension-rig/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-vscode-extension-rig.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-vscode-extension-rig) | [changelog](./rigs/heft-vscode-extension-rig/CHANGELOG.md) | [@rushstack/heft-vscode-extension-rig](https://www.npmjs.com/package/@rushstack/heft-vscode-extension-rig) | | [/rigs/heft-web-rig](./rigs/heft-web-rig/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-web-rig.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-web-rig) | [changelog](./rigs/heft-web-rig/CHANGELOG.md) | [@rushstack/heft-web-rig](https://www.npmjs.com/package/@rushstack/heft-web-rig) | | [/rush-plugins/rush-amazon-s3-build-cache-plugin](./rush-plugins/rush-amazon-s3-build-cache-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-amazon-s3-build-cache-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-amazon-s3-build-cache-plugin) | | [@rushstack/rush-amazon-s3-build-cache-plugin](https://www.npmjs.com/package/@rushstack/rush-amazon-s3-build-cache-plugin) | | [/rush-plugins/rush-azure-storage-build-cache-plugin](./rush-plugins/rush-azure-storage-build-cache-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-azure-storage-build-cache-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-azure-storage-build-cache-plugin) | | [@rushstack/rush-azure-storage-build-cache-plugin](https://www.npmjs.com/package/@rushstack/rush-azure-storage-build-cache-plugin) | +| [/rush-plugins/rush-bridge-cache-plugin](./rush-plugins/rush-bridge-cache-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-bridge-cache-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-bridge-cache-plugin) | | [@rushstack/rush-bridge-cache-plugin](https://www.npmjs.com/package/@rushstack/rush-bridge-cache-plugin) | +| [/rush-plugins/rush-buildxl-graph-plugin](./rush-plugins/rush-buildxl-graph-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-buildxl-graph-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-buildxl-graph-plugin) | | [@rushstack/rush-buildxl-graph-plugin](https://www.npmjs.com/package/@rushstack/rush-buildxl-graph-plugin) | | [/rush-plugins/rush-http-build-cache-plugin](./rush-plugins/rush-http-build-cache-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-http-build-cache-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-http-build-cache-plugin) | | [@rushstack/rush-http-build-cache-plugin](https://www.npmjs.com/package/@rushstack/rush-http-build-cache-plugin) | +| [/rush-plugins/rush-mcp-docs-plugin](./rush-plugins/rush-mcp-docs-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-mcp-docs-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-mcp-docs-plugin) | [changelog](./rush-plugins/rush-mcp-docs-plugin/CHANGELOG.md) | [@rushstack/rush-mcp-docs-plugin](https://www.npmjs.com/package/@rushstack/rush-mcp-docs-plugin) | +| [/rush-plugins/rush-published-versions-json-plugin](./rush-plugins/rush-published-versions-json-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-published-versions-json-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-published-versions-json-plugin) | [changelog](./rush-plugins/rush-published-versions-json-plugin/CHANGELOG.md) | [@rushstack/rush-published-versions-json-plugin](https://www.npmjs.com/package/@rushstack/rush-published-versions-json-plugin) | +| [/rush-plugins/rush-redis-cobuild-plugin](./rush-plugins/rush-redis-cobuild-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-redis-cobuild-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-redis-cobuild-plugin) | | [@rushstack/rush-redis-cobuild-plugin](https://www.npmjs.com/package/@rushstack/rush-redis-cobuild-plugin) | +| [/rush-plugins/rush-resolver-cache-plugin](./rush-plugins/rush-resolver-cache-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-resolver-cache-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-resolver-cache-plugin) | | [@rushstack/rush-resolver-cache-plugin](https://www.npmjs.com/package/@rushstack/rush-resolver-cache-plugin) | | [/rush-plugins/rush-serve-plugin](./rush-plugins/rush-serve-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Frush-serve-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Frush-serve-plugin) | | [@rushstack/rush-serve-plugin](https://www.npmjs.com/package/@rushstack/rush-serve-plugin) | | [/webpack/hashed-folder-copy-plugin](./webpack/hashed-folder-copy-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fhashed-folder-copy-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fhashed-folder-copy-plugin) | [changelog](./webpack/hashed-folder-copy-plugin/CHANGELOG.md) | [@rushstack/hashed-folder-copy-plugin](https://www.npmjs.com/package/@rushstack/hashed-folder-copy-plugin) | | [/webpack/loader-load-themed-styles](./webpack/loader-load-themed-styles/) | [![npm version](https://badge.fury.io/js/%40microsoft%2Floader-load-themed-styles.svg)](https://badge.fury.io/js/%40microsoft%2Floader-load-themed-styles) | [changelog](./webpack/loader-load-themed-styles/CHANGELOG.md) | [@microsoft/loader-load-themed-styles](https://www.npmjs.com/package/@microsoft/loader-load-themed-styles) | @@ -95,6 +128,7 @@ These GitHub repositories provide supplementary resources for Rush Stack: | [/webpack/set-webpack-public-path-plugin](./webpack/set-webpack-public-path-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fset-webpack-public-path-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fset-webpack-public-path-plugin) | [changelog](./webpack/set-webpack-public-path-plugin/CHANGELOG.md) | [@rushstack/set-webpack-public-path-plugin](https://www.npmjs.com/package/@rushstack/set-webpack-public-path-plugin) | | [/webpack/webpack-embedded-dependencies-plugin](./webpack/webpack-embedded-dependencies-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fwebpack-embedded-dependencies-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fwebpack-embedded-dependencies-plugin) | [changelog](./webpack/webpack-embedded-dependencies-plugin/CHANGELOG.md) | [@rushstack/webpack-embedded-dependencies-plugin](https://www.npmjs.com/package/@rushstack/webpack-embedded-dependencies-plugin) | | [/webpack/webpack-plugin-utilities](./webpack/webpack-plugin-utilities/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fwebpack-plugin-utilities.svg)](https://badge.fury.io/js/%40rushstack%2Fwebpack-plugin-utilities) | [changelog](./webpack/webpack-plugin-utilities/CHANGELOG.md) | [@rushstack/webpack-plugin-utilities](https://www.npmjs.com/package/@rushstack/webpack-plugin-utilities) | +| [/webpack/webpack-workspace-resolve-plugin](./webpack/webpack-workspace-resolve-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fwebpack-workspace-resolve-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fwebpack-workspace-resolve-plugin) | [changelog](./webpack/webpack-workspace-resolve-plugin/CHANGELOG.md) | [@rushstack/webpack-workspace-resolve-plugin](https://www.npmjs.com/package/@rushstack/webpack-workspace-resolve-plugin) | | [/webpack/webpack4-localization-plugin](./webpack/webpack4-localization-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fwebpack4-localization-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fwebpack4-localization-plugin) | [changelog](./webpack/webpack4-localization-plugin/CHANGELOG.md) | [@rushstack/webpack4-localization-plugin](https://www.npmjs.com/package/@rushstack/webpack4-localization-plugin) | | [/webpack/webpack4-module-minifier-plugin](./webpack/webpack4-module-minifier-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fwebpack4-module-minifier-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fwebpack4-module-minifier-plugin) | [changelog](./webpack/webpack4-module-minifier-plugin/CHANGELOG.md) | [@rushstack/webpack4-module-minifier-plugin](https://www.npmjs.com/package/@rushstack/webpack4-module-minifier-plugin) | | [/webpack/webpack5-load-themed-styles-loader](./webpack/webpack5-load-themed-styles-loader/) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fwebpack5-load-themed-styles-loader.svg)](https://badge.fury.io/js/%40microsoft%2Fwebpack5-load-themed-styles-loader) | [changelog](./webpack/webpack5-load-themed-styles-loader/CHANGELOG.md) | [@microsoft/webpack5-load-themed-styles-loader](https://www.npmjs.com/package/@microsoft/webpack5-load-themed-styles-loader) | @@ -109,42 +143,67 @@ These GitHub repositories provide supplementary resources for Rush Stack: | Folder | Description | | ------ | -----------| | [/apps/lockfile-explorer-web](./apps/lockfile-explorer-web/) | Rush Lockfile Explorer: helper project for building the React web application component | +| [/apps/rush-serve-dashboard](./apps/rush-serve-dashboard/) | Web dashboard for the Rush serve WebSocket protocol | | [/build-tests-samples/heft-node-basic-tutorial](./build-tests-samples/heft-node-basic-tutorial/) | (Copy of sample project) Building this project is a regression test for Heft | | [/build-tests-samples/heft-node-jest-tutorial](./build-tests-samples/heft-node-jest-tutorial/) | (Copy of sample project) Building this project is a regression test for Heft | | [/build-tests-samples/heft-node-rig-tutorial](./build-tests-samples/heft-node-rig-tutorial/) | (Copy of sample project) Building this project is a regression test for Heft | | [/build-tests-samples/heft-serverless-stack-tutorial](./build-tests-samples/heft-serverless-stack-tutorial/) | (Copy of sample project) Building this project is a regression test for Heft | -| [/build-tests-samples/heft-storybook-react-tutorial](./build-tests-samples/heft-storybook-react-tutorial/) | (Copy of sample project) Building this project is a regression test for Heft | -| [/build-tests-samples/heft-storybook-react-tutorial-storykit](./build-tests-samples/heft-storybook-react-tutorial-storykit/) | Storybook build dependencies for heft-storybook-react-tutorial | +| [/build-tests-samples/heft-storybook-v6-react-tutorial](./build-tests-samples/heft-storybook-v6-react-tutorial/) | (Copy of sample project) Building this project is a regression test for Heft | +| [/build-tests-samples/heft-storybook-v6-react-tutorial-app](./build-tests-samples/heft-storybook-v6-react-tutorial-app/) | Building this project is a regression test for heft-storybook-plugin | +| [/build-tests-samples/heft-storybook-v6-react-tutorial-storykit](./build-tests-samples/heft-storybook-v6-react-tutorial-storykit/) | Storybook build dependencies for heft-storybook-v6-react-tutorial | +| [/build-tests-samples/heft-storybook-v9-react-tutorial](./build-tests-samples/heft-storybook-v9-react-tutorial/) | (Copy of sample project) Building this project is a regression test for Heft | +| [/build-tests-samples/heft-storybook-v9-react-tutorial-app](./build-tests-samples/heft-storybook-v9-react-tutorial-app/) | Building this project is a regression test for heft-storybook-plugin | +| [/build-tests-samples/heft-storybook-v9-react-tutorial-storykit](./build-tests-samples/heft-storybook-v9-react-tutorial-storykit/) | Storybook build dependencies for heft-storybook-v9-react-tutorial | | [/build-tests-samples/heft-web-rig-app-tutorial](./build-tests-samples/heft-web-rig-app-tutorial/) | (Copy of sample project) Building this project is a regression test for Heft | | [/build-tests-samples/heft-web-rig-library-tutorial](./build-tests-samples/heft-web-rig-library-tutorial/) | (Copy of sample project) Building this project is a regression test for Heft | | [/build-tests-samples/heft-webpack-basic-tutorial](./build-tests-samples/heft-webpack-basic-tutorial/) | (Copy of sample project) Building this project is a regression test for Heft | | [/build-tests-samples/packlets-tutorial](./build-tests-samples/packlets-tutorial/) | (Copy of sample project) Building this project is a regression test for @rushstack/eslint-plugin-packlets | +| [/build-tests-subspace/rush-lib-test](./build-tests-subspace/rush-lib-test/) | A minimal example project that imports APIs from @rushstack/rush-lib | +| [/build-tests-subspace/rush-sdk-test](./build-tests-subspace/rush-sdk-test/) | A minimal example project that imports APIs from @rushstack/rush-sdk | +| [/build-tests-subspace/typescript-newest-test](./build-tests-subspace/typescript-newest-test/) | Building this project tests Heft with the newest supported TypeScript compiler version | +| [/build-tests-subspace/typescript-v4-test](./build-tests-subspace/typescript-v4-test/) | Building this project tests Heft with TypeScript v4 | | [/build-tests/api-documenter-scenarios](./build-tests/api-documenter-scenarios/) | Building this project is a regression test for api-documenter | | [/build-tests/api-documenter-test](./build-tests/api-documenter-test/) | Building this project is a regression test for api-documenter | +| [/build-tests/api-extractor-d-cts-test](./build-tests/api-extractor-d-cts-test/) | Building this project is a regression test for api-extractor | +| [/build-tests/api-extractor-d-mts-test](./build-tests/api-extractor-d-mts-test/) | Building this project is a regression test for api-extractor | | [/build-tests/api-extractor-lib1-test](./build-tests/api-extractor-lib1-test/) | Building this project is a regression test for api-extractor | | [/build-tests/api-extractor-lib2-test](./build-tests/api-extractor-lib2-test/) | Building this project is a regression test for api-extractor | | [/build-tests/api-extractor-lib3-test](./build-tests/api-extractor-lib3-test/) | Building this project is a regression test for api-extractor | +| [/build-tests/api-extractor-lib4-test](./build-tests/api-extractor-lib4-test/) | Building this project is a regression test for api-extractor | +| [/build-tests/api-extractor-lib5-test](./build-tests/api-extractor-lib5-test/) | Building this project is a regression test for api-extractor | | [/build-tests/api-extractor-scenarios](./build-tests/api-extractor-scenarios/) | Building this project is a regression test for api-extractor | | [/build-tests/api-extractor-test-01](./build-tests/api-extractor-test-01/) | Building this project is a regression test for api-extractor | | [/build-tests/api-extractor-test-02](./build-tests/api-extractor-test-02/) | Building this project is a regression test for api-extractor | | [/build-tests/api-extractor-test-03](./build-tests/api-extractor-test-03/) | Building this project is a regression test for api-extractor | | [/build-tests/api-extractor-test-04](./build-tests/api-extractor-test-04/) | Building this project is a regression test for api-extractor | +| [/build-tests/api-extractor-test-05](./build-tests/api-extractor-test-05/) | Building this project is a regression test for api-extractor | +| [/build-tests/eslint-7-11-test](./build-tests/eslint-7-11-test/) | This project contains a build test to validate ESLint 7.11.0 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin) | +| [/build-tests/eslint-7-7-test](./build-tests/eslint-7-7-test/) | This project contains a build test to validate ESLint 7.7.0 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin) | | [/build-tests/eslint-7-test](./build-tests/eslint-7-test/) | This project contains a build test to validate ESLint 7 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin) | -| [/build-tests/hashed-folder-copy-plugin-webpack4-test](./build-tests/hashed-folder-copy-plugin-webpack4-test/) | Building this project exercises @rushstack/hashed-folder-copy-plugin with Webpack 4. | +| [/build-tests/eslint-8-test](./build-tests/eslint-8-test/) | This project contains a build test to validate ESLint 8 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin) | +| [/build-tests/eslint-9-test](./build-tests/eslint-9-test/) | This project contains a build test to validate ESLint 9 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin) | +| [/build-tests/eslint-bulk-suppressions-test](./build-tests/eslint-bulk-suppressions-test/) | Sample code to test eslint bulk suppressions | +| [/build-tests/eslint-bulk-suppressions-test-flat](./build-tests/eslint-bulk-suppressions-test-flat/) | Sample code to test eslint bulk suppressions with flat configs | +| [/build-tests/eslint-bulk-suppressions-test-legacy](./build-tests/eslint-bulk-suppressions-test-legacy/) | Sample code to test eslint bulk suppressions for versions of eslint < 8.57.0 | +| [/build-tests/esm-node-import-test](./build-tests/esm-node-import-test/) | This project validates that importing a rushstack package from a 'type: module' Node.js project works correctly with the package.json 'exports' field. See https://github.com/microsoft/rushstack/issues/5644 | | [/build-tests/hashed-folder-copy-plugin-webpack5-test](./build-tests/hashed-folder-copy-plugin-webpack5-test/) | Building this project exercises @rushstack/hashed-folder-copy-plugin with Webpack 5. NOTE - THIS TEST IS CURRENTLY EXPECTED TO BE BROKEN | | [/build-tests/heft-copy-files-test](./build-tests/heft-copy-files-test/) | Building this project tests copying files with Heft | +| [/build-tests/heft-example-lifecycle-plugin](./build-tests/heft-example-lifecycle-plugin/) | This is an example heft plugin for testing the lifecycle hooks | | [/build-tests/heft-example-plugin-01](./build-tests/heft-example-plugin-01/) | This is an example heft plugin that exposes hooks for other plugins | | [/build-tests/heft-example-plugin-02](./build-tests/heft-example-plugin-02/) | This is an example heft plugin that taps the hooks exposed from heft-example-plugin-01 | | [/build-tests/heft-fastify-test](./build-tests/heft-fastify-test/) | This project tests Heft support for the Fastify framework for Node.js services | | [/build-tests/heft-jest-preset-test](./build-tests/heft-jest-preset-test/) | This project illustrates configuring a Jest preset in a minimal Heft project | | [/build-tests/heft-jest-reporters-test](./build-tests/heft-jest-reporters-test/) | This project illustrates configuring Jest reporters in a minimal Heft project | +| [/build-tests/heft-json-schema-typings-plugin-test](./build-tests/heft-json-schema-typings-plugin-test/) | This project illustrates configuring Jest reporters in a minimal Heft project | | [/build-tests/heft-minimal-rig-test](./build-tests/heft-minimal-rig-test/) | This is a minimal rig package that is imported by the 'heft-minimal-rig-usage-test' project | | [/build-tests/heft-minimal-rig-usage-test](./build-tests/heft-minimal-rig-usage-test/) | A test project for Heft that resolves its compiler from the 'heft-minimal-rig-test' package | | [/build-tests/heft-node-everything-esm-module-test](./build-tests/heft-node-everything-esm-module-test/) | Building this project tests every task and config file for Heft when targeting the Node.js runtime when configured to use ESM module support | | [/build-tests/heft-node-everything-test](./build-tests/heft-node-everything-test/) | Building this project tests every task and config file for Heft when targeting the Node.js runtime | | [/build-tests/heft-parameter-plugin](./build-tests/heft-parameter-plugin/) | This project contains a Heft plugin that adds a custom parameter to built-in actions | | [/build-tests/heft-parameter-plugin-test](./build-tests/heft-parameter-plugin-test/) | This project exercises a built-in Heft action with a custom parameter | +| [/build-tests/heft-rspack-everything-test](./build-tests/heft-rspack-everything-test/) | Building this project tests every task and config file for Heft when targeting the web browser runtime using Rspack | | [/build-tests/heft-sass-test](./build-tests/heft-sass-test/) | This project illustrates a minimal tutorial Heft project targeting the web browser runtime | +| [/build-tests/heft-swc-test](./build-tests/heft-swc-test/) | Building this project tests building with SWC | | [/build-tests/heft-typescript-composite-test](./build-tests/heft-typescript-composite-test/) | Building this project tests behavior of Heft when the tsconfig.json file uses project references. | | [/build-tests/heft-typescript-v2-test](./build-tests/heft-typescript-v2-test/) | Building this project tests building with TypeScript v2 | | [/build-tests/heft-typescript-v3-test](./build-tests/heft-typescript-v3-test/) | Building this project tests building with TypeScript v3 | @@ -152,21 +211,38 @@ These GitHub repositories provide supplementary resources for Rush Stack: | [/build-tests/heft-web-rig-library-test](./build-tests/heft-web-rig-library-test/) | A test project for Heft that exercises the '@rushstack/heft-web-rig' package | | [/build-tests/heft-webpack4-everything-test](./build-tests/heft-webpack4-everything-test/) | Building this project tests every task and config file for Heft when targeting the web browser runtime using Webpack 4 | | [/build-tests/heft-webpack5-everything-test](./build-tests/heft-webpack5-everything-test/) | Building this project tests every task and config file for Heft when targeting the web browser runtime using Webpack 5 | -| [/build-tests/install-test-workspace](./build-tests/install-test-workspace/) | | | [/build-tests/localization-plugin-test-01](./build-tests/localization-plugin-test-01/) | Building this project exercises @microsoft/localization-plugin. This tests that the plugin works correctly without any localized resources. | | [/build-tests/localization-plugin-test-02](./build-tests/localization-plugin-test-02/) | Building this project exercises @microsoft/localization-plugin. This tests that the loader works correctly with the exportAsDefault option unset. | | [/build-tests/localization-plugin-test-03](./build-tests/localization-plugin-test-03/) | Building this project exercises @microsoft/localization-plugin. This tests that the plugin works correctly with the exportAsDefault option set to true. | +| [/build-tests/package-extractor-test-01](./build-tests/package-extractor-test-01/) | This project is used by tests in the @rushstack/package-extractor package. | +| [/build-tests/package-extractor-test-02](./build-tests/package-extractor-test-02/) | This project is used by tests in the @rushstack/package-extractor package. | +| [/build-tests/package-extractor-test-03](./build-tests/package-extractor-test-03/) | This project is used by tests in the @rushstack/package-extractor package. | +| [/build-tests/package-extractor-test-04](./build-tests/package-extractor-test-04/) | This project is used by tests in the @rushstack/package-extractor package. | +| [/build-tests/package-extractor-test-05](./build-tests/package-extractor-test-05/) | This project is used by tests in the @rushstack/package-extractor package. | +| [/build-tests/run-scenarios-helpers](./build-tests/run-scenarios-helpers/) | Helpers for the *-scenarios test projects. | | [/build-tests/rush-amazon-s3-build-cache-plugin-integration-test](./build-tests/rush-amazon-s3-build-cache-plugin-integration-test/) | Tests connecting to an amazon S3 endpoint | | [/build-tests/rush-lib-declaration-paths-test](./build-tests/rush-lib-declaration-paths-test/) | This project ensures all of the paths in rush-lib/lib/... have imports that resolve correctly. If this project builds, all `lib/**/*.d.ts` files in the `@microsoft/rush-lib` package are valid. | +| [/build-tests/rush-mcp-example-plugin](./build-tests/rush-mcp-example-plugin/) | Example showing how to create a plugin for @rushstack/mcp-server | +| [/build-tests/rush-package-manager-integration-test](./build-tests/rush-package-manager-integration-test/) | Integration tests for non-pnpm package managers in Rush. | | [/build-tests/rush-project-change-analyzer-test](./build-tests/rush-project-change-analyzer-test/) | This is an example project that uses rush-lib's ProjectChangeAnalyzer to | -| [/build-tests/set-webpack-public-path-plugin-webpack4-test](./build-tests/set-webpack-public-path-plugin-webpack4-test/) | Building this project tests the set-webpack-public-path-plugin using Webpack 4 | -| [/build-tests/ts-command-line-test](./build-tests/ts-command-line-test/) | Building this project is a regression test for ts-command-line | +| [/build-tests/rush-redis-cobuild-plugin-integration-test](./build-tests/rush-redis-cobuild-plugin-integration-test/) | Tests connecting to an redis server | +| [/build-tests/set-webpack-public-path-plugin-test](./build-tests/set-webpack-public-path-plugin-test/) | Building this project tests the set-webpack-public-path-plugin | +| [/build-tests/webpack-local-version-test](./build-tests/webpack-local-version-test/) | Building this project tests the rig loading for the local version of webpack | +| [/eslint/local-eslint-config](./eslint/local-eslint-config/) | An ESLint configuration consumed projects inside the rushstack repo. | | [/libraries/rush-themed-ui](./libraries/rush-themed-ui/) | Rush Component Library: a set of themed components for rush projects | | [/libraries/rushell](./libraries/rushell/) | Execute shell commands using a consistent syntax on every platform | | [/repo-scripts/doc-plugin-rush-stack](./repo-scripts/doc-plugin-rush-stack/) | API Documenter plugin used with the rushstack.io website | | [/repo-scripts/generate-api-docs](./repo-scripts/generate-api-docs/) | Used to generate API docs for the rushstack.io website | | [/repo-scripts/repo-toolbox](./repo-scripts/repo-toolbox/) | Used to execute various operations specific to this repo | +| [/rigs/decoupled-local-node-rig](./rigs/decoupled-local-node-rig/) | A rig package for Node.js projects that build using Heft inside the RushStack repository, but are dependencies of @rushstack/heft-node-rig or local-node-rig. | +| [/rigs/local-node-rig](./rigs/local-node-rig/) | A rig package for Node.js projects that build using Heft inside the RushStack repository. | +| [/rigs/local-web-rig](./rigs/local-web-rig/) | A rig package for Web projects that build using Heft inside the RushStack repository. | | [/rush-plugins/rush-litewatch-plugin](./rush-plugins/rush-litewatch-plugin/) | An experimental alternative approach for multi-project watch mode | +| [/vscode-extensions/debug-certificate-manager-vscode-extension](./vscode-extensions/debug-certificate-manager-vscode-extension/) | VS Code extension to manage debug TLS certificates and sync them to the VS Code workspace. Works with VS Code remote development (Codespaces, SSH, Dev Containers, WSL, VS Code Tunnels). | +| [/vscode-extensions/playwright-local-browser-server-vscode-extension](./vscode-extensions/playwright-local-browser-server-vscode-extension/) | VS Code extension to enable Playwright testing in remote VS Code environments (such as Codespaces, Dev Containers, VS Code Tunnels) while launching and driving the actual browser process on your local machine. | +| [/vscode-extensions/rush-vscode-command-webview](./vscode-extensions/rush-vscode-command-webview/) | Part of the Rush Stack VSCode extension, provides a UI for invoking Rush commands | +| [/vscode-extensions/rush-vscode-extension](./vscode-extensions/rush-vscode-extension/) | Enhanced experience for monorepos that use the Rush Stack toolchain | +| [/vscode-extensions/vscode-shared](./vscode-extensions/vscode-shared/) | | | [/webpack/webpack-deep-imports-plugin](./webpack/webpack-deep-imports-plugin/) | This plugin creates a bundle and commonJS files in a 'lib' folder mirroring modules in another 'lib' folder. | diff --git a/apps/api-documenter/.eslintrc.js b/apps/api-documenter/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/apps/api-documenter/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/apps/api-documenter/.npmignore b/apps/api-documenter/.npmignore index 302dbc5b019..f7a40e10213 100644 --- a/apps/api-documenter/.npmignore +++ b/apps/api-documenter/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,16 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/apps/api-documenter/CHANGELOG.json b/apps/api-documenter/CHANGELOG.json index 2461c4588a5..501c63f8441 100644 --- a/apps/api-documenter/CHANGELOG.json +++ b/apps/api-documenter/CHANGELOG.json @@ -1,6 +1,3025 @@ { "name": "@microsoft/api-documenter", "entries": [ + { + "version": "7.30.10", + "tag": "@microsoft/api-documenter_v7.30.10", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "7.30.9", + "tag": "@microsoft/api-documenter_v7.30.9", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "patch": [ + { + "comment": "Replace `@override` with the `override` keyword." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.10`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "7.30.8", + "tag": "@microsoft/api-documenter_v7.30.8", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "7.30.7", + "tag": "@microsoft/api-documenter_v7.30.7", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "7.30.6", + "tag": "@microsoft/api-documenter_v7.30.6", + "date": "Mon, 08 Jun 2026 15:15:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "7.30.5", + "tag": "@microsoft/api-documenter_v7.30.5", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "7.30.4", + "tag": "@microsoft/api-documenter_v7.30.4", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "7.30.3", + "tag": "@microsoft/api-documenter_v7.30.3", + "date": "Sat, 18 Apr 2026 03:47:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "7.30.2", + "tag": "@microsoft/api-documenter_v7.30.2", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "7.30.1", + "tag": "@microsoft/api-documenter_v7.30.1", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "7.30.0", + "tag": "@microsoft/api-documenter_v7.30.0", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for @defaultValue in Markdown and Yaml documenters" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "7.29.11", + "tag": "@microsoft/api-documenter_v7.29.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "7.29.10", + "tag": "@microsoft/api-documenter_v7.29.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "7.29.9", + "tag": "@microsoft/api-documenter_v7.29.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "7.29.8", + "tag": "@microsoft/api-documenter_v7.29.8", + "date": "Tue, 31 Mar 2026 15:14:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "7.29.7", + "tag": "@microsoft/api-documenter_v7.29.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "7.29.6", + "tag": "@microsoft/api-documenter_v7.29.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "7.29.5", + "tag": "@microsoft/api-documenter_v7.29.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "7.29.4", + "tag": "@microsoft/api-documenter_v7.29.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "7.29.3", + "tag": "@microsoft/api-documenter_v7.29.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "7.29.2", + "tag": "@microsoft/api-documenter_v7.29.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "7.29.1", + "tag": "@microsoft/api-documenter_v7.29.1", + "date": "Fri, 20 Feb 2026 00:15:03 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "7.29.0", + "tag": "@microsoft/api-documenter_v7.29.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "7.28.9", + "tag": "@microsoft/api-documenter_v7.28.9", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "7.28.8", + "tag": "@microsoft/api-documenter_v7.28.8", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "7.28.7", + "tag": "@microsoft/api-documenter_v7.28.7", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "7.28.6", + "tag": "@microsoft/api-documenter_v7.28.6", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "7.28.5", + "tag": "@microsoft/api-documenter_v7.28.5", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "7.28.4", + "tag": "@microsoft/api-documenter_v7.28.4", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "7.28.3", + "tag": "@microsoft/api-documenter_v7.28.3", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "7.28.2", + "tag": "@microsoft/api-documenter_v7.28.2", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.32.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + } + ] + } + }, + { + "version": "7.28.1", + "tag": "@microsoft/api-documenter_v7.28.1", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.32.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + } + ] + } + }, + { + "version": "7.28.0", + "tag": "@microsoft/api-documenter_v7.28.0", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `@microsoft/tsdoc` dependency to `~0.16.0`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.32.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + } + ] + } + }, + { + "version": "7.27.4", + "tag": "@microsoft/api-documenter_v7.27.4", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + } + ] + } + }, + { + "version": "7.27.3", + "tag": "@microsoft/api-documenter_v7.27.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.31.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + } + ] + } + }, + { + "version": "7.27.2", + "tag": "@microsoft/api-documenter_v7.27.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.31.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + } + ] + } + }, + { + "version": "7.27.1", + "tag": "@microsoft/api-documenter_v7.27.1", + "date": "Wed, 08 Oct 2025 00:13:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.31.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + } + ] + } + }, + { + "version": "7.27.0", + "tag": "@microsoft/api-documenter_v7.27.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.31.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + } + ] + } + }, + { + "version": "7.26.36", + "tag": "@microsoft/api-documenter_v7.26.36", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + } + ] + } + }, + { + "version": "7.26.35", + "tag": "@microsoft/api-documenter_v7.26.35", + "date": "Tue, 30 Sep 2025 20:33:50 GMT", + "comments": { + "patch": [ + { + "comment": "Upgraded `js-yaml` dependency" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + } + ] + } + }, + { + "version": "7.26.34", + "tag": "@microsoft/api-documenter_v7.26.34", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + } + ] + } + }, + { + "version": "7.26.33", + "tag": "@microsoft/api-documenter_v7.26.33", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + } + ] + } + }, + { + "version": "7.26.32", + "tag": "@microsoft/api-documenter_v7.26.32", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + } + ] + } + }, + { + "version": "7.26.31", + "tag": "@microsoft/api-documenter_v7.26.31", + "date": "Fri, 01 Aug 2025 00:12:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + } + ] + } + }, + { + "version": "7.26.30", + "tag": "@microsoft/api-documenter_v7.26.30", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + } + ] + } + }, + { + "version": "7.26.29", + "tag": "@microsoft/api-documenter_v7.26.29", + "date": "Tue, 24 Jun 2025 00:11:43 GMT", + "comments": { + "patch": [ + { + "comment": "Ensure a new line is inserted after rendering a table" + } + ] + } + }, + { + "version": "7.26.28", + "tag": "@microsoft/api-documenter_v7.26.28", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + } + ] + } + }, + { + "version": "7.26.27", + "tag": "@microsoft/api-documenter_v7.26.27", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + } + ] + } + }, + { + "version": "7.26.26", + "tag": "@microsoft/api-documenter_v7.26.26", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + } + ] + } + }, + { + "version": "7.26.25", + "tag": "@microsoft/api-documenter_v7.26.25", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + } + ] + } + }, + { + "version": "7.26.24", + "tag": "@microsoft/api-documenter_v7.26.24", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + } + ] + } + }, + { + "version": "7.26.23", + "tag": "@microsoft/api-documenter_v7.26.23", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + } + ] + } + }, + { + "version": "7.26.22", + "tag": "@microsoft/api-documenter_v7.26.22", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + } + ] + } + }, + { + "version": "7.26.21", + "tag": "@microsoft/api-documenter_v7.26.21", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + } + ] + } + }, + { + "version": "7.26.20", + "tag": "@microsoft/api-documenter_v7.26.20", + "date": "Wed, 09 Apr 2025 00:11:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + } + ] + } + }, + { + "version": "7.26.19", + "tag": "@microsoft/api-documenter_v7.26.19", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + } + ] + } + }, + { + "version": "7.26.18", + "tag": "@microsoft/api-documenter_v7.26.18", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + } + ] + } + }, + { + "version": "7.26.17", + "tag": "@microsoft/api-documenter_v7.26.17", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + } + ] + } + }, + { + "version": "7.26.16", + "tag": "@microsoft/api-documenter_v7.26.16", + "date": "Wed, 12 Mar 2025 00:11:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + } + ] + } + }, + { + "version": "7.26.15", + "tag": "@microsoft/api-documenter_v7.26.15", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + } + ] + } + }, + { + "version": "7.26.14", + "tag": "@microsoft/api-documenter_v7.26.14", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + } + ] + } + }, + { + "version": "7.26.13", + "tag": "@microsoft/api-documenter_v7.26.13", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + } + ] + } + }, + { + "version": "7.26.12", + "tag": "@microsoft/api-documenter_v7.26.12", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + } + ] + } + }, + { + "version": "7.26.11", + "tag": "@microsoft/api-documenter_v7.26.11", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + } + ] + } + }, + { + "version": "7.26.10", + "tag": "@microsoft/api-documenter_v7.26.10", + "date": "Sat, 22 Feb 2025 01:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + } + ] + } + }, + { + "version": "7.26.9", + "tag": "@microsoft/api-documenter_v7.26.9", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + } + ] + } + }, + { + "version": "7.26.8", + "tag": "@microsoft/api-documenter_v7.26.8", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + } + ] + } + }, + { + "version": "7.26.7", + "tag": "@microsoft/api-documenter_v7.26.7", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + } + ] + } + }, + { + "version": "7.26.6", + "tag": "@microsoft/api-documenter_v7.26.6", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + } + ] + } + }, + { + "version": "7.26.5", + "tag": "@microsoft/api-documenter_v7.26.5", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + } + ] + } + }, + { + "version": "7.26.4", + "tag": "@microsoft/api-documenter_v7.26.4", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + } + ] + } + }, + { + "version": "7.26.3", + "tag": "@microsoft/api-documenter_v7.26.3", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + } + ] + } + }, + { + "version": "7.26.2", + "tag": "@microsoft/api-documenter_v7.26.2", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + } + ] + } + }, + { + "version": "7.26.1", + "tag": "@microsoft/api-documenter_v7.26.1", + "date": "Tue, 03 Dec 2024 16:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + } + ] + } + }, + { + "version": "7.26.0", + "tag": "@microsoft/api-documenter_v7.26.0", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "minor": [ + { + "comment": "Update TSDoc dependencies." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + } + ] + } + }, + { + "version": "7.25.22", + "tag": "@microsoft/api-documenter_v7.25.22", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + } + ] + } + }, + { + "version": "7.25.21", + "tag": "@microsoft/api-documenter_v7.25.21", + "date": "Thu, 24 Oct 2024 00:15:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + } + ] + } + }, + { + "version": "7.25.20", + "tag": "@microsoft/api-documenter_v7.25.20", + "date": "Mon, 21 Oct 2024 18:50:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + } + ] + } + }, + { + "version": "7.25.19", + "tag": "@microsoft/api-documenter_v7.25.19", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + } + ] + } + }, + { + "version": "7.25.18", + "tag": "@microsoft/api-documenter_v7.25.18", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + } + ] + } + }, + { + "version": "7.25.17", + "tag": "@microsoft/api-documenter_v7.25.17", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + } + ] + } + }, + { + "version": "7.25.16", + "tag": "@microsoft/api-documenter_v7.25.16", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + } + ] + } + }, + { + "version": "7.25.15", + "tag": "@microsoft/api-documenter_v7.25.15", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + } + ] + } + }, + { + "version": "7.25.14", + "tag": "@microsoft/api-documenter_v7.25.14", + "date": "Fri, 13 Sep 2024 00:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + } + ] + } + }, + { + "version": "7.25.13", + "tag": "@microsoft/api-documenter_v7.25.13", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + } + ] + } + }, + { + "version": "7.25.12", + "tag": "@microsoft/api-documenter_v7.25.12", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + } + ] + } + }, + { + "version": "7.25.11", + "tag": "@microsoft/api-documenter_v7.25.11", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + } + ] + } + }, + { + "version": "7.25.10", + "tag": "@microsoft/api-documenter_v7.25.10", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + } + ] + } + }, + { + "version": "7.25.9", + "tag": "@microsoft/api-documenter_v7.25.9", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + } + ] + } + }, + { + "version": "7.25.8", + "tag": "@microsoft/api-documenter_v7.25.8", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + } + ] + } + }, + { + "version": "7.25.7", + "tag": "@microsoft/api-documenter_v7.25.7", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + } + ] + } + }, + { + "version": "7.25.6", + "tag": "@microsoft/api-documenter_v7.25.6", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + } + ] + } + }, + { + "version": "7.25.5", + "tag": "@microsoft/api-documenter_v7.25.5", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + } + ] + } + }, + { + "version": "7.25.4", + "tag": "@microsoft/api-documenter_v7.25.4", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + } + ] + } + }, + { + "version": "7.25.3", + "tag": "@microsoft/api-documenter_v7.25.3", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + } + ] + } + }, + { + "version": "7.25.2", + "tag": "@microsoft/api-documenter_v7.25.2", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "patch": [ + { + "comment": "Include missing `type` modifiers on type-only exports." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + } + ] + } + }, + { + "version": "7.25.1", + "tag": "@microsoft/api-documenter_v7.25.1", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + } + ] + } + }, + { + "version": "7.25.0", + "tag": "@microsoft/api-documenter_v7.25.0", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "minor": [ + { + "comment": "Bump TSDoc dependencies." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + } + ] + } + }, + { + "version": "7.24.13", + "tag": "@microsoft/api-documenter_v7.24.13", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.21`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + } + ] + } + }, + { + "version": "7.24.12", + "tag": "@microsoft/api-documenter_v7.24.12", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.20`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + } + ] + } + }, + { + "version": "7.24.11", + "tag": "@microsoft/api-documenter_v7.24.11", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.19`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + } + ] + } + }, + { + "version": "7.24.10", + "tag": "@microsoft/api-documenter_v7.24.10", + "date": "Fri, 24 May 2024 00:15:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + } + ] + } + }, + { + "version": "7.24.9", + "tag": "@microsoft/api-documenter_v7.24.9", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.18`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + } + ] + } + }, + { + "version": "7.24.8", + "tag": "@microsoft/api-documenter_v7.24.8", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + } + ] + } + }, + { + "version": "7.24.7", + "tag": "@microsoft/api-documenter_v7.24.7", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + } + ] + } + }, + { + "version": "7.24.6", + "tag": "@microsoft/api-documenter_v7.24.6", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.17`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + } + ] + } + }, + { + "version": "7.24.5", + "tag": "@microsoft/api-documenter_v7.24.5", + "date": "Fri, 10 May 2024 05:33:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.16`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + } + ] + } + }, + { + "version": "7.24.4", + "tag": "@microsoft/api-documenter_v7.24.4", + "date": "Wed, 08 May 2024 22:23:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + } + ] + } + }, + { + "version": "7.24.3", + "tag": "@microsoft/api-documenter_v7.24.3", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.15`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + } + ] + } + }, + { + "version": "7.24.2", + "tag": "@microsoft/api-documenter_v7.24.2", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.14`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + } + ] + } + }, + { + "version": "7.24.1", + "tag": "@microsoft/api-documenter_v7.24.1", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + } + ] + } + }, + { + "version": "7.24.0", + "tag": "@microsoft/api-documenter_v7.24.0", + "date": "Sat, 16 Mar 2024 00:11:37 GMT", + "comments": { + "minor": [ + { + "comment": "Emit HTML tags for tables instead of Markdown code." + } + ] + } + }, + { + "version": "7.23.38", + "tag": "@microsoft/api-documenter_v7.23.38", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + } + ] + } + }, + { + "version": "7.23.37", + "tag": "@microsoft/api-documenter_v7.23.37", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + } + ] + } + }, + { + "version": "7.23.36", + "tag": "@microsoft/api-documenter_v7.23.36", + "date": "Sun, 03 Mar 2024 20:58:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + } + ] + } + }, + { + "version": "7.23.35", + "tag": "@microsoft/api-documenter_v7.23.35", + "date": "Sat, 02 Mar 2024 02:22:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + } + ] + } + }, + { + "version": "7.23.34", + "tag": "@microsoft/api-documenter_v7.23.34", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.18.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + } + ] + } + }, + { + "version": "7.23.33", + "tag": "@microsoft/api-documenter_v7.23.33", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + } + ] + } + }, + { + "version": "7.23.32", + "tag": "@microsoft/api-documenter_v7.23.32", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + } + ] + } + }, + { + "version": "7.23.31", + "tag": "@microsoft/api-documenter_v7.23.31", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + } + ] + } + }, + { + "version": "7.23.30", + "tag": "@microsoft/api-documenter_v7.23.30", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + } + ] + } + }, + { + "version": "7.23.29", + "tag": "@microsoft/api-documenter_v7.23.29", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "patch": [ + { + "comment": "Replace the dependency on the `colors` package with `Colorize` from `@rushstack/terminal`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.13`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + } + ] + } + }, + { + "version": "7.23.28", + "tag": "@microsoft/api-documenter_v7.23.28", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + } + ] + } + }, + { + "version": "7.23.27", + "tag": "@microsoft/api-documenter_v7.23.27", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + } + ] + } + }, + { + "version": "7.23.26", + "tag": "@microsoft/api-documenter_v7.23.26", + "date": "Tue, 20 Feb 2024 16:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + } + ] + } + }, + { + "version": "7.23.25", + "tag": "@microsoft/api-documenter_v7.23.25", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.11`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + } + ] + } + }, + { + "version": "7.23.24", + "tag": "@microsoft/api-documenter_v7.23.24", + "date": "Sat, 17 Feb 2024 06:24:34 GMT", + "comments": { + "patch": [ + { + "comment": "Fix broken link to API documentation" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.10`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + } + ] + } + }, + { + "version": "7.23.23", + "tag": "@microsoft/api-documenter_v7.23.23", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + } + ] + } + }, + { + "version": "7.23.22", + "tag": "@microsoft/api-documenter_v7.23.22", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + } + ] + } + }, + { + "version": "7.23.21", + "tag": "@microsoft/api-documenter_v7.23.21", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + } + ] + } + }, + { + "version": "7.23.20", + "tag": "@microsoft/api-documenter_v7.23.20", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + } + ] + } + }, + { + "version": "7.23.19", + "tag": "@microsoft/api-documenter_v7.23.19", + "date": "Tue, 23 Jan 2024 20:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + } + ] + } + }, + { + "version": "7.23.18", + "tag": "@microsoft/api-documenter_v7.23.18", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + } + ] + } + }, + { + "version": "7.23.17", + "tag": "@microsoft/api-documenter_v7.23.17", + "date": "Tue, 16 Jan 2024 18:30:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + } + ] + } + }, + { + "version": "7.23.16", + "tag": "@microsoft/api-documenter_v7.23.16", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + } + ] + } + }, + { + "version": "7.23.15", + "tag": "@microsoft/api-documenter_v7.23.15", + "date": "Wed, 20 Dec 2023 01:09:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + } + ] + } + }, + { + "version": "7.23.14", + "tag": "@microsoft/api-documenter_v7.23.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + } + ] + } + }, + { + "version": "7.23.13", + "tag": "@microsoft/api-documenter_v7.23.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + } + ] + } + }, + { + "version": "7.23.12", + "tag": "@microsoft/api-documenter_v7.23.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + } + ] + } + }, + { + "version": "7.23.11", + "tag": "@microsoft/api-documenter_v7.23.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + } + ] + } + }, + { + "version": "7.23.10", + "tag": "@microsoft/api-documenter_v7.23.10", + "date": "Mon, 30 Oct 2023 23:36:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + } + ] + } + }, + { + "version": "7.23.9", + "tag": "@microsoft/api-documenter_v7.23.9", + "date": "Sun, 01 Oct 2023 02:56:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + } + ] + } + }, + { + "version": "7.23.8", + "tag": "@microsoft/api-documenter_v7.23.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "patch": [ + { + "comment": "Add notes for @alpha items when encountered. Mimics the existing behavior for @beta items." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + } + ] + } + }, + { + "version": "7.23.7", + "tag": "@microsoft/api-documenter_v7.23.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + } + ] + } + }, + { + "version": "7.23.6", + "tag": "@microsoft/api-documenter_v7.23.6", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + } + ] + } + }, + { + "version": "7.23.5", + "tag": "@microsoft/api-documenter_v7.23.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + } + ] + } + }, + { + "version": "7.23.4", + "tag": "@microsoft/api-documenter_v7.23.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.16.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + } + ] + } + }, + { + "version": "7.23.3", + "tag": "@microsoft/api-documenter_v7.23.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + } + ] + } + }, + { + "version": "7.23.2", + "tag": "@microsoft/api-documenter_v7.23.2", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + } + ] + } + }, + { + "version": "7.23.1", + "tag": "@microsoft/api-documenter_v7.23.1", + "date": "Tue, 19 Sep 2023 15:21:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.24`" + } + ] + } + }, + { + "version": "7.23.0", + "tag": "@microsoft/api-documenter_v7.23.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.23`" + } + ] + } + }, + { + "version": "7.22.33", + "tag": "@microsoft/api-documenter_v7.22.33", + "date": "Tue, 08 Aug 2023 07:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.27.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.22`" + } + ] + } + }, + { + "version": "7.22.32", + "tag": "@microsoft/api-documenter_v7.22.32", + "date": "Mon, 31 Jul 2023 15:19:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.21`" + } + ] + } + }, + { + "version": "7.22.31", + "tag": "@microsoft/api-documenter_v7.22.31", + "date": "Sat, 29 Jul 2023 00:22:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.20`" + } + ] + } + }, + { + "version": "7.22.30", + "tag": "@microsoft/api-documenter_v7.22.30", + "date": "Thu, 20 Jul 2023 20:47:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.19`" + } + ] + } + }, + { + "version": "7.22.29", + "tag": "@microsoft/api-documenter_v7.22.29", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.27.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.18`" + } + ] + } + }, + { + "version": "7.22.28", + "tag": "@microsoft/api-documenter_v7.22.28", + "date": "Fri, 14 Jul 2023 15:20:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.17`" + } + ] + } + }, + { + "version": "7.22.27", + "tag": "@microsoft/api-documenter_v7.22.27", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.16`" + } + ] + } + }, + { + "version": "7.22.26", + "tag": "@microsoft/api-documenter_v7.22.26", + "date": "Wed, 12 Jul 2023 15:20:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.15`" + } + ] + } + }, + { + "version": "7.22.25", + "tag": "@microsoft/api-documenter_v7.22.25", + "date": "Wed, 12 Jul 2023 00:23:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.14`" + } + ] + } + }, + { + "version": "7.22.24", + "tag": "@microsoft/api-documenter_v7.22.24", + "date": "Fri, 07 Jul 2023 00:19:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.13`" + } + ] + } + }, + { + "version": "7.22.23", + "tag": "@microsoft/api-documenter_v7.22.23", + "date": "Thu, 06 Jul 2023 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.27.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.12`" + } + ] + } + }, + { + "version": "7.22.22", + "tag": "@microsoft/api-documenter_v7.22.22", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.11`" + } + ] + } + }, + { + "version": "7.22.21", + "tag": "@microsoft/api-documenter_v7.22.21", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.10`" + } + ] + } + }, + { + "version": "7.22.20", + "tag": "@microsoft/api-documenter_v7.22.20", + "date": "Thu, 15 Jun 2023 00:21:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.27.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.9`" + } + ] + } + }, + { + "version": "7.22.19", + "tag": "@microsoft/api-documenter_v7.22.19", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.8`" + } + ] + } + }, + { + "version": "7.22.18", + "tag": "@microsoft/api-documenter_v7.22.18", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.7`" + } + ] + } + }, + { + "version": "7.22.17", + "tag": "@microsoft/api-documenter_v7.22.17", + "date": "Tue, 13 Jun 2023 01:49:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.6`" + } + ] + } + }, + { + "version": "7.22.16", + "tag": "@microsoft/api-documenter_v7.22.16", + "date": "Fri, 09 Jun 2023 18:05:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.5`" + } + ] + } + }, + { + "version": "7.22.15", + "tag": "@microsoft/api-documenter_v7.22.15", + "date": "Fri, 09 Jun 2023 15:23:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.4`" + } + ] + } + }, + { + "version": "7.22.14", + "tag": "@microsoft/api-documenter_v7.22.14", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.3`" + } + ] + } + }, + { + "version": "7.22.13", + "tag": "@microsoft/api-documenter_v7.22.13", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.2`" + } + ] + } + }, + { + "version": "7.22.12", + "tag": "@microsoft/api-documenter_v7.22.12", + "date": "Thu, 08 Jun 2023 00:20:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.1`" + } + ] + } + }, + { + "version": "7.22.11", + "tag": "@microsoft/api-documenter_v7.22.11", + "date": "Wed, 07 Jun 2023 22:45:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.27.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.0`" + } + ] + } + }, + { + "version": "7.22.10", + "tag": "@microsoft/api-documenter_v7.22.10", + "date": "Tue, 06 Jun 2023 02:52:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.1.0`" + } + ] + } + }, + { + "version": "7.22.9", + "tag": "@microsoft/api-documenter_v7.22.9", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.0.1`" + } + ] + } + }, { "version": "7.22.8", "tag": "@microsoft/api-documenter_v7.22.8", diff --git a/apps/api-documenter/CHANGELOG.md b/apps/api-documenter/CHANGELOG.md index 22c01078c3a..5956e3d393d 100644 --- a/apps/api-documenter/CHANGELOG.md +++ b/apps/api-documenter/CHANGELOG.md @@ -1,6 +1,924 @@ # Change Log - @microsoft/api-documenter -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 7.30.10 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 7.30.9 +Fri, 17 Jul 2026 00:15:59 GMT + +### Patches + +- Replace `@override` with the `override` keyword. + +## 7.30.8 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 7.30.7 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 7.30.6 +Mon, 08 Jun 2026 15:15:49 GMT + +_Version update only_ + +## 7.30.5 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 7.30.4 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 7.30.3 +Sat, 18 Apr 2026 03:47:09 GMT + +_Version update only_ + +## 7.30.2 +Sat, 18 Apr 2026 00:15:16 GMT + +_Version update only_ + +## 7.30.1 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 7.30.0 +Fri, 10 Apr 2026 22:46:34 GMT + +### Minor changes + +- Add support for @defaultValue in Markdown and Yaml documenters + +## 7.29.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 7.29.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 7.29.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 7.29.8 +Tue, 31 Mar 2026 15:14:14 GMT + +_Version update only_ + +## 7.29.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 7.29.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 7.29.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 7.29.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 7.29.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 7.29.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 7.29.1 +Fri, 20 Feb 2026 00:15:03 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 7.29.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 7.28.9 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 7.28.8 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 7.28.7 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 7.28.6 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 7.28.5 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 7.28.4 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 7.28.3 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 7.28.2 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 7.28.1 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 7.28.0 +Wed, 12 Nov 2025 01:12:56 GMT + +### Minor changes + +- Bump the `@microsoft/tsdoc` dependency to `~0.16.0`. + +## 7.27.4 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 7.27.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 7.27.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 7.27.1 +Wed, 08 Oct 2025 00:13:28 GMT + +_Version update only_ + +## 7.27.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 7.26.36 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 7.26.35 +Tue, 30 Sep 2025 20:33:50 GMT + +### Patches + +- Upgraded `js-yaml` dependency + +## 7.26.34 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 7.26.33 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 7.26.32 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 7.26.31 +Fri, 01 Aug 2025 00:12:48 GMT + +_Version update only_ + +## 7.26.30 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 7.26.29 +Tue, 24 Jun 2025 00:11:43 GMT + +### Patches + +- Ensure a new line is inserted after rendering a table + +## 7.26.28 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 7.26.27 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 7.26.26 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 7.26.25 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 7.26.24 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 7.26.23 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 7.26.22 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 7.26.21 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 7.26.20 +Wed, 09 Apr 2025 00:11:02 GMT + +_Version update only_ + +## 7.26.19 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 7.26.18 +Tue, 25 Mar 2025 15:11:15 GMT + +_Version update only_ + +## 7.26.17 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 7.26.16 +Wed, 12 Mar 2025 00:11:31 GMT + +_Version update only_ + +## 7.26.15 +Tue, 11 Mar 2025 02:12:33 GMT + +_Version update only_ + +## 7.26.14 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 7.26.13 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 7.26.12 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 7.26.11 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 7.26.10 +Sat, 22 Feb 2025 01:11:11 GMT + +_Version update only_ + +## 7.26.9 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 7.26.8 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 7.26.7 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 7.26.6 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 7.26.5 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 7.26.4 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 7.26.3 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 7.26.2 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 7.26.1 +Tue, 03 Dec 2024 16:11:07 GMT + +_Version update only_ + +## 7.26.0 +Sat, 23 Nov 2024 01:18:55 GMT + +### Minor changes + +- Update TSDoc dependencies. + +## 7.25.22 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 7.25.21 +Thu, 24 Oct 2024 00:15:47 GMT + +_Version update only_ + +## 7.25.20 +Mon, 21 Oct 2024 18:50:09 GMT + +_Version update only_ + +## 7.25.19 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 7.25.18 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 7.25.17 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 7.25.16 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 7.25.15 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 7.25.14 +Fri, 13 Sep 2024 00:11:42 GMT + +_Version update only_ + +## 7.25.13 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 7.25.12 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 7.25.11 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 7.25.10 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 7.25.9 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 7.25.8 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 7.25.7 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 7.25.6 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 7.25.5 +Tue, 16 Jul 2024 00:36:21 GMT + +_Version update only_ + +## 7.25.4 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 7.25.3 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 7.25.2 +Thu, 30 May 2024 00:13:05 GMT + +### Patches + +- Include missing `type` modifiers on type-only exports. + +## 7.25.1 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 7.25.0 +Wed, 29 May 2024 00:10:52 GMT + +### Minor changes + +- Bump TSDoc dependencies. + +## 7.24.13 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 7.24.12 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 7.24.11 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 7.24.10 +Fri, 24 May 2024 00:15:08 GMT + +_Version update only_ + +## 7.24.9 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 7.24.8 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 7.24.7 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 7.24.6 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 7.24.5 +Fri, 10 May 2024 05:33:33 GMT + +_Version update only_ + +## 7.24.4 +Wed, 08 May 2024 22:23:50 GMT + +_Version update only_ + +## 7.24.3 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 7.24.2 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 7.24.1 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 7.24.0 +Sat, 16 Mar 2024 00:11:37 GMT + +### Minor changes + +- Emit HTML tags for tables instead of Markdown code. + +## 7.23.38 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 7.23.37 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 7.23.36 +Sun, 03 Mar 2024 20:58:12 GMT + +_Version update only_ + +## 7.23.35 +Sat, 02 Mar 2024 02:22:23 GMT + +_Version update only_ + +## 7.23.34 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 7.23.33 +Thu, 29 Feb 2024 07:11:45 GMT + +_Version update only_ + +## 7.23.32 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 7.23.31 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 7.23.30 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 7.23.29 +Wed, 21 Feb 2024 21:45:28 GMT + +### Patches + +- Replace the dependency on the `colors` package with `Colorize` from `@rushstack/terminal`. + +## 7.23.28 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 7.23.27 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 7.23.26 +Tue, 20 Feb 2024 16:10:52 GMT + +_Version update only_ + +## 7.23.25 +Mon, 19 Feb 2024 21:54:27 GMT + +_Version update only_ + +## 7.23.24 +Sat, 17 Feb 2024 06:24:34 GMT + +### Patches + +- Fix broken link to API documentation + +## 7.23.23 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 7.23.22 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 7.23.21 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 7.23.20 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 7.23.19 +Tue, 23 Jan 2024 20:12:57 GMT + +_Version update only_ + +## 7.23.18 +Tue, 23 Jan 2024 16:15:05 GMT + +_Version update only_ + +## 7.23.17 +Tue, 16 Jan 2024 18:30:11 GMT + +_Version update only_ + +## 7.23.16 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 7.23.15 +Wed, 20 Dec 2023 01:09:45 GMT + +_Version update only_ + +## 7.23.14 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 7.23.13 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 7.23.12 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 7.23.11 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 7.23.10 +Mon, 30 Oct 2023 23:36:37 GMT + +_Version update only_ + +## 7.23.9 +Sun, 01 Oct 2023 02:56:29 GMT + +_Version update only_ + +## 7.23.8 +Sat, 30 Sep 2023 00:20:51 GMT + +### Patches + +- Add notes for @alpha items when encountered. Mimics the existing behavior for @beta items. + +## 7.23.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 7.23.6 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 7.23.5 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 7.23.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 7.23.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 7.23.2 +Fri, 22 Sep 2023 00:05:50 GMT + +_Version update only_ + +## 7.23.1 +Tue, 19 Sep 2023 15:21:51 GMT + +_Version update only_ + +## 7.23.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 7.22.33 +Tue, 08 Aug 2023 07:10:39 GMT + +_Version update only_ + +## 7.22.32 +Mon, 31 Jul 2023 15:19:05 GMT + +_Version update only_ + +## 7.22.31 +Sat, 29 Jul 2023 00:22:50 GMT + +_Version update only_ + +## 7.22.30 +Thu, 20 Jul 2023 20:47:28 GMT + +_Version update only_ + +## 7.22.29 +Wed, 19 Jul 2023 00:20:31 GMT + +_Version update only_ + +## 7.22.28 +Fri, 14 Jul 2023 15:20:45 GMT + +_Version update only_ + +## 7.22.27 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 7.22.26 +Wed, 12 Jul 2023 15:20:39 GMT + +_Version update only_ + +## 7.22.25 +Wed, 12 Jul 2023 00:23:29 GMT + +_Version update only_ + +## 7.22.24 +Fri, 07 Jul 2023 00:19:32 GMT + +_Version update only_ + +## 7.22.23 +Thu, 06 Jul 2023 00:16:19 GMT + +_Version update only_ + +## 7.22.22 +Tue, 04 Jul 2023 00:18:47 GMT + +_Version update only_ + +## 7.22.21 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 7.22.20 +Thu, 15 Jun 2023 00:21:01 GMT + +_Version update only_ + +## 7.22.19 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 7.22.18 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 7.22.17 +Tue, 13 Jun 2023 01:49:01 GMT + +_Version update only_ + +## 7.22.16 +Fri, 09 Jun 2023 18:05:34 GMT + +_Version update only_ + +## 7.22.15 +Fri, 09 Jun 2023 15:23:15 GMT + +_Version update only_ + +## 7.22.14 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 7.22.13 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 7.22.12 +Thu, 08 Jun 2023 00:20:02 GMT + +_Version update only_ + +## 7.22.11 +Wed, 07 Jun 2023 22:45:16 GMT + +_Version update only_ + +## 7.22.10 +Tue, 06 Jun 2023 02:52:51 GMT + +_Version update only_ + +## 7.22.9 +Mon, 05 Jun 2023 21:45:21 GMT + +_Version update only_ ## 7.22.8 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/apps/api-documenter/README.md b/apps/api-documenter/README.md index a3109a3cb00..ad3ecfd0e25 100644 --- a/apps/api-documenter/README.md +++ b/apps/api-documenter/README.md @@ -14,6 +14,6 @@ documentation. - [CHANGELOG.md]( https://github.com/microsoft/rushstack/blob/main/apps/api-documenter/CHANGELOG.md) - Find out what's new in the latest version -- [API Reference](https://rushstack.io/pages/api/api-documenter/) +- [API Reference](https://api.rushstack.io/pages/api-documenter/) API Documenter is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/apps/api-documenter/bin/api-documenter b/apps/api-documenter/bin/api-documenter index 783bb806fce..eef2fc27066 100755 --- a/apps/api-documenter/bin/api-documenter +++ b/apps/api-documenter/bin/api-documenter @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('../lib/start.js') +require('../lib-commonjs/start.js'); diff --git a/apps/api-documenter/config/api-extractor.json b/apps/api-documenter/config/api-extractor.json index aa9d8f810fd..d34381e01f3 100644 --- a/apps/api-documenter/config/api-extractor.json +++ b/apps/api-documenter/config/api-extractor.json @@ -1,17 +1,6 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - - "mainEntryPointFilePath": "/lib/index.d.ts", - - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, - - "docModel": { - "enabled": true, - "apiJsonFilePath": "../../../common/temp/api/.api.json" - }, + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json", "dtsRollup": { "enabled": true, diff --git a/apps/api-documenter/config/heft.json b/apps/api-documenter/config/heft.json new file mode 100644 index 00000000000..b3046cad172 --- /dev/null +++ b/apps/api-documenter/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/api-extractor/v7"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/apps/api-documenter/config/jest.config.json b/apps/api-documenter/config/jest.config.json index 4bb17bde3ee..d1749681d90 100644 --- a/apps/api-documenter/config/jest.config.json +++ b/apps/api-documenter/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "local-node-rig/profiles/default/config/jest.config.json" } diff --git a/apps/api-documenter/config/rig.json b/apps/api-documenter/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/apps/api-documenter/config/rig.json +++ b/apps/api-documenter/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/apps/api-documenter/eslint.config.js b/apps/api-documenter/eslint.config.js new file mode 100644 index 00000000000..ceb5a1bee40 --- /dev/null +++ b/apps/api-documenter/eslint.config.js @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + }, + rules: { + 'no-console': 'off' + } + } +]; diff --git a/apps/api-documenter/package.json b/apps/api-documenter/package.json index a4bbca74bf3..0fd02bc75a5 100644 --- a/apps/api-documenter/package.json +++ b/apps/api-documenter/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/api-documenter", - "version": "7.22.8", + "version": "7.30.10", "description": "Read JSON files from api-extractor, generate documentation pages", "repository": { "type": "git", @@ -17,23 +17,50 @@ "bin": { "api-documenter": "./bin/api-documenter" }, - "main": "lib/index.js", - "typings": "dist/rollup.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/rollup.d.ts", + "exports": { + ".": { + "types": "./dist/rollup.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "dependencies": { "@microsoft/api-extractor-model": "workspace:*", - "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc": "~0.16.0", "@rushstack/node-core-library": "workspace:*", + "@rushstack/terminal": "workspace:*", "@rushstack/ts-command-line": "workspace:*", - "colors": "~1.2.1", - "js-yaml": "~3.13.1", + "js-yaml": "~4.1.0", "resolve": "~1.22.1" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", - "@types/js-yaml": "3.12.1", - "@types/node": "14.18.36", - "@types/resolve": "1.20.2" - } + "@types/js-yaml": "4.0.9", + "@types/resolve": "1.20.2", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "sideEffects": [ + "lib-commonjs/start.js", + "lib-esm/start.js" + ] } diff --git a/apps/api-documenter/src/cli/ApiDocumenterCommandLine.ts b/apps/api-documenter/src/cli/ApiDocumenterCommandLine.ts index b201d6456b6..cc6c6774111 100644 --- a/apps/api-documenter/src/cli/ApiDocumenterCommandLine.ts +++ b/apps/api-documenter/src/cli/ApiDocumenterCommandLine.ts @@ -2,6 +2,7 @@ // See LICENSE in the project root for license information. import { CommandLineParser } from '@rushstack/ts-command-line'; + import { MarkdownAction } from './MarkdownAction'; import { YamlAction } from './YamlAction'; import { GenerateAction } from './GenerateAction'; diff --git a/apps/api-documenter/src/cli/BaseAction.ts b/apps/api-documenter/src/cli/BaseAction.ts index d70a1ec16f3..fbd3f10a14f 100644 --- a/apps/api-documenter/src/cli/BaseAction.ts +++ b/apps/api-documenter/src/cli/BaseAction.ts @@ -1,23 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as tsdoc from '@microsoft/tsdoc'; -import colors from 'colors/safe'; +import * as path from 'node:path'; +import type * as tsdoc from '@microsoft/tsdoc'; import { CommandLineAction, - CommandLineStringParameter, + type CommandLineStringParameter, type ICommandLineActionOptions } from '@rushstack/ts-command-line'; import { FileSystem } from '@rushstack/node-core-library'; import { ApiModel, - ApiItem, + type ApiItem, ApiItemContainerMixin, ApiDocumentedItem, - IResolveDeclarationReferenceResult + type IResolveDeclarationReferenceResult } from '@microsoft/api-extractor-model'; +import { Colorize } from '@rushstack/terminal'; export interface IBuildApiModelResult { apiModel: ApiModel; @@ -32,7 +32,6 @@ export abstract class BaseAction extends CommandLineAction { protected constructor(options: ICommandLineActionOptions) { super(options); - // override this._inputFolderParameter = this.defineStringParameter({ parameterLongName: '--input-folder', parameterShortName: '-i', @@ -94,7 +93,7 @@ export abstract class BaseAction extends CommandLineAction { if (result.errorMessage) { console.log( - colors.yellow( + Colorize.yellow( `Warning: Unresolved @inheritDoc tag for ${apiItem.displayName}: ` + result.errorMessage ) ); diff --git a/apps/api-documenter/src/cli/GenerateAction.ts b/apps/api-documenter/src/cli/GenerateAction.ts index 4ecb815333c..155b22320d1 100644 --- a/apps/api-documenter/src/cli/GenerateAction.ts +++ b/apps/api-documenter/src/cli/GenerateAction.ts @@ -1,14 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; -import { ApiDocumenterCommandLine } from './ApiDocumenterCommandLine'; +import { FileSystem } from '@rushstack/node-core-library'; + +import type { ApiDocumenterCommandLine } from './ApiDocumenterCommandLine'; import { BaseAction } from './BaseAction'; import { DocumenterConfig } from '../documenters/DocumenterConfig'; import { ExperimentalYamlDocumenter } from '../documenters/ExperimentalYamlDocumenter'; - -import { FileSystem } from '@rushstack/node-core-library'; import { MarkdownDocumenter } from '../documenters/MarkdownDocumenter'; export class GenerateAction extends BaseAction { @@ -22,8 +22,7 @@ export class GenerateAction extends BaseAction { }); } - protected async onExecute(): Promise { - // override + protected override async onExecuteAsync(): Promise { // Look for the config file under the current folder let configFilePath: string = path.join(process.cwd(), DocumenterConfig.FILENAME); diff --git a/apps/api-documenter/src/cli/MarkdownAction.ts b/apps/api-documenter/src/cli/MarkdownAction.ts index 01d6e30a220..6dba1d3ac6d 100644 --- a/apps/api-documenter/src/cli/MarkdownAction.ts +++ b/apps/api-documenter/src/cli/MarkdownAction.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ApiDocumenterCommandLine } from './ApiDocumenterCommandLine'; +import type { ApiDocumenterCommandLine } from './ApiDocumenterCommandLine'; import { BaseAction } from './BaseAction'; import { MarkdownDocumenter } from '../documenters/MarkdownDocumenter'; @@ -16,8 +16,7 @@ export class MarkdownAction extends BaseAction { }); } - protected async onExecute(): Promise { - // override + protected override async onExecuteAsync(): Promise { const { apiModel, outputFolder } = this.buildApiModel(); const markdownDocumenter: MarkdownDocumenter = new MarkdownDocumenter({ diff --git a/apps/api-documenter/src/cli/YamlAction.ts b/apps/api-documenter/src/cli/YamlAction.ts index b980610711d..7ed4161fb2d 100644 --- a/apps/api-documenter/src/cli/YamlAction.ts +++ b/apps/api-documenter/src/cli/YamlAction.ts @@ -1,18 +1,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { CommandLineFlagParameter, CommandLineChoiceParameter } from '@rushstack/ts-command-line'; +import type { + CommandLineFlagParameter, + IRequiredCommandLineChoiceParameter +} from '@rushstack/ts-command-line'; -import { ApiDocumenterCommandLine } from './ApiDocumenterCommandLine'; +import type { ApiDocumenterCommandLine } from './ApiDocumenterCommandLine'; import { BaseAction } from './BaseAction'; - -import { YamlDocumenter } from '../documenters/YamlDocumenter'; +import { YamlDocumenter, type YamlFormat } from '../documenters/YamlDocumenter'; import { OfficeYamlDocumenter } from '../documenters/OfficeYamlDocumenter'; export class YamlAction extends BaseAction { private readonly _officeParameter: CommandLineFlagParameter; private readonly _newDocfxNamespacesParameter: CommandLineFlagParameter; - private readonly _yamlFormatParameter: CommandLineChoiceParameter; + private readonly _yamlFormatParameter: IRequiredCommandLineChoiceParameter; public constructor(parser: ApiDocumenterCommandLine) { super({ @@ -36,7 +38,7 @@ export class YamlAction extends BaseAction { ` adds them to the table of contents. This will also affect file layout as namespaced items will be nested` + ` under a directory for the namespace instead of just within the package.` }); - this._yamlFormatParameter = this.defineChoiceParameter({ + this._yamlFormatParameter = this.defineChoiceParameter({ parameterLongName: '--yaml-format', alternatives: ['udp', 'sdp'], defaultValue: 'sdp', @@ -47,8 +49,7 @@ export class YamlAction extends BaseAction { }); } - protected async onExecute(): Promise { - // override + protected override async onExecuteAsync(): Promise { const { apiModel, inputFolder, outputFolder } = this.buildApiModel(); const yamlDocumenter: YamlDocumenter = this._officeParameter.value diff --git a/apps/api-documenter/src/documenters/DocumenterConfig.ts b/apps/api-documenter/src/documenters/DocumenterConfig.ts index 18d13550c6e..3a0e32c3736 100644 --- a/apps/api-documenter/src/documenters/DocumenterConfig.ts +++ b/apps/api-documenter/src/documenters/DocumenterConfig.ts @@ -1,9 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import { JsonSchema, JsonFile, NewlineKind } from '@rushstack/node-core-library'; -import { IConfigFile } from './IConfigFile'; + +import type { IConfigFile } from './IConfigFile'; +import apiDocumenterSchema from '../schemas/api-documenter.schema.json'; /** * Helper for loading the api-documenter.json file format. Later when the schema is more mature, @@ -23,9 +26,7 @@ export class DocumenterConfig { /** * The JSON Schema for API Documenter config file (api-documenter.schema.json). */ - public static readonly jsonSchema: JsonSchema = JsonSchema.fromFile( - path.join(__dirname, '..', 'schemas', 'api-documenter.schema.json') - ); + public static readonly jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(apiDocumenterSchema); /** * The config file name "api-documenter.json". diff --git a/apps/api-documenter/src/documenters/ExperimentalYamlDocumenter.ts b/apps/api-documenter/src/documenters/ExperimentalYamlDocumenter.ts index af20c75f7c4..69c0efbdc2f 100644 --- a/apps/api-documenter/src/documenters/ExperimentalYamlDocumenter.ts +++ b/apps/api-documenter/src/documenters/ExperimentalYamlDocumenter.ts @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { DocComment, DocInlineTag } from '@microsoft/tsdoc'; -import { ApiModel, ApiItem, ApiItemKind, ApiDocumentedItem } from '@microsoft/api-extractor-model'; +import { type DocComment, DocInlineTag } from '@microsoft/tsdoc'; +import { type ApiModel, type ApiItem, ApiItemKind, ApiDocumentedItem } from '@microsoft/api-extractor-model'; -import { IConfigTableOfContents } from './IConfigFile'; -import { IYamlTocItem, IYamlTocFile } from '../yaml/IYamlTocFile'; +import type { IConfigTableOfContents } from './IConfigFile'; +import type { IYamlTocItem, IYamlTocFile } from '../yaml/IYamlTocFile'; import { YamlDocumenter } from './YamlDocumenter'; -import { DocumenterConfig } from './DocumenterConfig'; +import type { DocumenterConfig } from './DocumenterConfig'; /** * EXPERIMENTAL - This documenter is a prototype of a new config file driven mode of operation for @@ -27,8 +27,7 @@ export class ExperimentalYamlDocumenter extends YamlDocumenter { this._generateTocPointersMap(this._config.tocConfig); } - /** @override */ - protected buildYamlTocFile(apiItems: ReadonlyArray): IYamlTocFile { + protected override buildYamlTocFile(apiItems: ReadonlyArray): IYamlTocFile { this._buildTocItems2(apiItems); return this._config.tocConfig; } diff --git a/apps/api-documenter/src/documenters/IConfigFile.ts b/apps/api-documenter/src/documenters/IConfigFile.ts index 3c60580fd4a..5f617a33276 100644 --- a/apps/api-documenter/src/documenters/IConfigFile.ts +++ b/apps/api-documenter/src/documenters/IConfigFile.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IYamlTocFile } from '../yaml/IYamlTocFile'; +import type { IYamlTocFile } from '../yaml/IYamlTocFile'; /** * Typescript interface describing the config schema for toc.yml file format. diff --git a/apps/api-documenter/src/documenters/MarkdownDocumenter.ts b/apps/api-documenter/src/documenters/MarkdownDocumenter.ts index e2f3afdf7d9..203509cf816 100644 --- a/apps/api-documenter/src/documenters/MarkdownDocumenter.ts +++ b/apps/api-documenter/src/documenters/MarkdownDocumenter.ts @@ -1,28 +1,29 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import { PackageName, FileSystem, NewlineKind } from '@rushstack/node-core-library'; import { DocSection, DocPlainText, DocLinkTag, - TSDocConfiguration, + type TSDocConfiguration, StringBuilder, DocNodeKind, DocParagraph, DocCodeSpan, DocFencedCode, StandardTags, - DocBlock, - DocComment, - DocNodeContainer + type DocBlock, + type DocComment, + type DocNodeContainer } from '@microsoft/tsdoc'; import { - ApiModel, - ApiItem, - ApiEnum, - ApiPackage, + type ApiModel, + type ApiItem, + type ApiEnum, + type ApiPackage, ApiItemKind, ApiReleaseTagMixin, ApiDocumentedItem, @@ -31,21 +32,21 @@ import { ApiStaticMixin, ApiPropertyItem, ApiInterface, - Excerpt, + type Excerpt, ApiAbstractMixin, ApiParameterListMixin, ApiReturnTypeMixin, ApiDeclaredItem, - ApiNamespace, + type ApiNamespace, ExcerptTokenKind, - IResolveDeclarationReferenceResult, + type IResolveDeclarationReferenceResult, ApiTypeAlias, - ExcerptToken, + type ExcerptToken, ApiOptionalMixin, ApiInitializerMixin, ApiProtectedMixin, ApiReadonlyMixin, - IFindApiItemsResult + type IFindApiItemsResult } from '@microsoft/api-extractor-model'; import { CustomDocNodes } from '../nodes/CustomDocNodeKind'; @@ -59,10 +60,10 @@ import { Utilities } from '../utils/Utilities'; import { CustomMarkdownEmitter } from '../markdown/CustomMarkdownEmitter'; import { PluginLoader } from '../plugin/PluginLoader'; import { - IMarkdownDocumenterFeatureOnBeforeWritePageArgs, + type IMarkdownDocumenterFeatureOnBeforeWritePageArgs, MarkdownDocumenterFeatureContext } from '../plugin/MarkdownDocumenterFeature'; -import { DocumenterConfig } from './DocumenterConfig'; +import type { DocumenterConfig } from './DocumenterConfig'; import { MarkdownDocumenterAccessor } from '../plugin/MarkdownDocumenterAccessor'; export interface IMarkdownDocumenterOptions { @@ -173,7 +174,9 @@ export class MarkdownDocumenter { } if (ApiReleaseTagMixin.isBaseClassOf(apiItem)) { - if (apiItem.releaseTag === ReleaseTag.Beta) { + if (apiItem.releaseTag === ReleaseTag.Alpha) { + this._writeAlphaWarning(output); + } else if (apiItem.releaseTag === ReleaseTag.Beta) { this._writeBetaWarning(output); } } @@ -270,6 +273,7 @@ export class MarkdownDocumenter { case ApiItemKind.Function: this._writeParameterTables(output, apiItem as ApiParameterListMixin); this._writeThrowsSection(output, apiItem); + this._writeDefaultValueSection(output, apiItem); break; case ApiItemKind.Namespace: this._writePackageOrNamespaceTables(output, apiItem as ApiNamespace); @@ -282,10 +286,13 @@ export class MarkdownDocumenter { break; case ApiItemKind.Property: case ApiItemKind.PropertySignature: + this._writeDefaultValueSection(output, apiItem); break; case ApiItemKind.TypeAlias: + this._writeDefaultValueSection(output, apiItem); break; case ApiItemKind.Variable: + this._writeDefaultValueSection(output, apiItem); break; default: throw new Error('Unsupported API item kind: ' + apiItem.kind); @@ -466,6 +473,30 @@ export class MarkdownDocumenter { } } + private _writeDefaultValueSection(output: DocSection, apiItem: ApiItem): void { + const configuration: TSDocConfiguration = this._tsdocConfiguration; + + if (apiItem instanceof ApiDocumentedItem) { + const tsdocComment: DocComment | undefined = apiItem.tsdocComment; + + if (tsdocComment) { + // Write the @defaultValue blocks + const defaultValueBlocks: DocBlock[] = tsdocComment.customBlocks.filter( + (x) => x.blockTag.tagNameWithUpperCase === StandardTags.defaultValue.tagNameWithUpperCase + ); + + if (defaultValueBlocks.length > 0) { + const heading: string = 'Default Value'; + output.appendNode(new DocHeading({ configuration, title: heading })); + + for (const defaultValueBlock of defaultValueBlocks) { + this._appendSection(output, defaultValueBlock.content); + } + } + } + } + } + /** * GENERATE PAGE: MODEL */ @@ -1000,10 +1031,13 @@ export class MarkdownDocumenter { const section: DocSection = new DocSection({ configuration }); if (ApiReleaseTagMixin.isBaseClassOf(apiItem)) { - if (apiItem.releaseTag === ReleaseTag.Beta) { + if (apiItem.releaseTag === ReleaseTag.Alpha || apiItem.releaseTag === ReleaseTag.Beta) { section.appendNodesInParagraph([ new DocEmphasisSpan({ configuration, bold: true, italic: true }, [ - new DocPlainText({ configuration, text: '(BETA)' }) + new DocPlainText({ + configuration, + text: `(${apiItem.releaseTag === ReleaseTag.Alpha ? 'ALPHA' : 'BETA'})` + }) ]), new DocPlainText({ configuration, text: ' ' }) ]); @@ -1152,10 +1186,22 @@ export class MarkdownDocumenter { } } + private _writeAlphaWarning(output: DocSection): void { + const configuration: TSDocConfiguration = this._tsdocConfiguration; + const betaWarning: string = + 'This API is provided as an alpha preview for developers and may change' + + ' based on feedback that we receive. Do not use this API in a production environment.'; + output.appendNode( + new DocNoteBox({ configuration }, [ + new DocParagraph({ configuration }, [new DocPlainText({ configuration, text: betaWarning })]) + ]) + ); + } + private _writeBetaWarning(output: DocSection): void { const configuration: TSDocConfiguration = this._tsdocConfiguration; const betaWarning: string = - 'This API is provided as a preview for developers and may change' + + 'This API is provided as a beta preview for developers and may change' + ' based on feedback that we receive. Do not use this API in a production environment.'; output.appendNode( new DocNoteBox({ configuration }, [ diff --git a/apps/api-documenter/src/documenters/OfficeYamlDocumenter.ts b/apps/api-documenter/src/documenters/OfficeYamlDocumenter.ts index 19b9d58c843..df82c0caa2b 100644 --- a/apps/api-documenter/src/documenters/OfficeYamlDocumenter.ts +++ b/apps/api-documenter/src/documenters/OfficeYamlDocumenter.ts @@ -1,15 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors'; -import * as path from 'path'; +import * as path from 'node:path'; + import yaml = require('js-yaml'); -import { ApiModel } from '@microsoft/api-extractor-model'; +import type { ApiModel } from '@microsoft/api-extractor-model'; import { FileSystem } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; -import { IYamlTocItem } from '../yaml/IYamlTocFile'; -import { IYamlItem } from '../yaml/IYamlApiFile'; +import type { IYamlTocItem } from '../yaml/IYamlTocFile'; +import type { IYamlItem } from '../yaml/IYamlApiFile'; import { YamlDocumenter } from './YamlDocumenter'; interface ISnippetsFile { @@ -48,24 +49,21 @@ export class OfficeYamlDocumenter extends YamlDocumenter { console.log('Loading snippets from ' + snippetsFilePath); const snippetsContent: string = FileSystem.readFile(snippetsFilePath); - this._snippets = yaml.load(snippetsContent, { filename: snippetsFilePath }); - this._snippetsAll = yaml.load(snippetsContent, { filename: snippetsFilePath }); + this._snippets = yaml.load(snippetsContent, { filename: snippetsFilePath }) as ISnippetsFile; + this._snippetsAll = yaml.load(snippetsContent, { filename: snippetsFilePath }) as ISnippetsFile; } - /** @override */ - public generateFiles(outputFolder: string): void { + public override generateFiles(outputFolder: string): void { super.generateFiles(outputFolder); // After we generate everything, check for any unused snippets console.log(); for (const apiName of Object.keys(this._snippets)) { - console.error(colors.yellow('Warning: Unused snippet ' + apiName)); + console.error(Colorize.yellow('Warning: Unused snippet ' + apiName)); } } - /** @override */ - protected onGetTocRoot(): IYamlTocItem { - // override + protected override onGetTocRoot(): IYamlTocItem { return { name: 'API reference', href: 'overview.md', @@ -73,8 +71,7 @@ export class OfficeYamlDocumenter extends YamlDocumenter { }; } - /** @override */ - protected onCustomizeYamlItem(yamlItem: IYamlItem): void { + protected override onCustomizeYamlItem(yamlItem: IYamlItem): void { const nameWithoutPackage: string = yamlItem.uid.replace(/^[^.]+\!/, ''); if (yamlItem.summary) { yamlItem.summary = this._fixupApiSet(yamlItem.summary, yamlItem.uid); diff --git a/apps/api-documenter/src/documenters/YamlDocumenter.ts b/apps/api-documenter/src/documenters/YamlDocumenter.ts index 9f04aa959e0..73420328756 100644 --- a/apps/api-documenter/src/documenters/YamlDocumenter.ts +++ b/apps/api-documenter/src/documenters/YamlDocumenter.ts @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; import yaml = require('js-yaml'); + import { JsonFile, JsonSchema, @@ -12,39 +13,46 @@ import { NewlineKind, InternalError } from '@rushstack/node-core-library'; -import { StringBuilder, DocSection, DocComment, DocBlock, StandardTags } from '@microsoft/tsdoc'; import { - ApiModel, - ApiItem, + StringBuilder, + type DocSection, + type DocComment, + type DocBlock, + StandardTags +} from '@microsoft/tsdoc'; +import { + type ApiModel, + type ApiItem, ApiItemKind, ApiDocumentedItem, ApiReleaseTagMixin, ReleaseTag, - ApiPropertyItem, + type ApiPropertyItem, ApiItemContainerMixin, - ApiPackage, - ApiEnumMember, + type ApiPackage, + type ApiEnumMember, ApiClass, ApiInterface, - ApiMethod, - ApiMethodSignature, - ApiConstructor, - ApiFunction, + type ApiMethod, + type ApiMethodSignature, + type ApiConstructor, + type ApiFunction, ApiReturnTypeMixin, ApiTypeParameterListMixin, - Excerpt, - ExcerptToken, + type Excerpt, + type ExcerptToken, ExcerptTokenKind, - HeritageType, - ApiVariable, - ApiTypeAlias + type HeritageType, + type ApiVariable, + type ApiTypeAlias } from '@microsoft/api-extractor-model'; import { - DeclarationReference, + type DeclarationReference, Navigation, Meaning } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; -import { + +import type { IYamlApiFile, IYamlItem, IYamlSyntax, @@ -53,14 +61,13 @@ import { IYamlReferenceSpec, IYamlInheritanceTree } from '../yaml/IYamlApiFile'; -import { IYamlTocFile, IYamlTocItem } from '../yaml/IYamlTocFile'; +import type { IYamlTocFile, IYamlTocItem } from '../yaml/IYamlTocFile'; import { Utilities } from '../utils/Utilities'; import { CustomMarkdownEmitter } from '../markdown/CustomMarkdownEmitter'; import { convertUDPYamlToSDP } from '../utils/ToSdpConvertHelper'; +import typescriptSchema from '../yaml/typescript.schema.json'; -const yamlApiSchema: JsonSchema = JsonSchema.fromFile( - path.join(__dirname, '..', 'yaml', 'typescript.schema.json') -); +const yamlApiSchema: JsonSchema = JsonSchema.fromLoadedObject(typescriptSchema); interface IYamlReferences { references: IYamlReference[]; @@ -68,7 +75,7 @@ interface IYamlReferences { uidTypeReferenceCounters: Map; } -const enum FlattenMode { +enum FlattenMode { /** Include entries for nested namespaces and non-namespace children. */ NestedNamespacesAndChildren, /** Include entries for nested namespaces only. */ @@ -84,6 +91,8 @@ interface INameOptions { includeNamespace?: boolean; } +export type YamlFormat = 'udp' | 'sdp'; + /** * Writes documentation in the Universal Reference YAML file format, as defined by typescript.schema.json. */ @@ -96,7 +105,11 @@ export class YamlDocumenter { private _apiItemsByCanonicalReference: Map; private _yamlReferences: IYamlReferences | undefined; - public constructor(apiModel: ApiModel, newDocfxNamespaces: boolean = false, yamlFormat: string = 'sdp') { + public constructor( + apiModel: ApiModel, + newDocfxNamespaces: boolean = false, + yamlFormat: YamlFormat = 'sdp' + ) { this._apiModel = apiModel; this.newDocfxNamespaces = newDocfxNamespaces; this._yamlFormat = yamlFormat; @@ -414,6 +427,18 @@ export class YamlDocumenter { yamlItem.example = [...(yamlItem.example || []), example]; } } + + // Write the @defaultValue block + const defaultValueBlocks: DocBlock[] = tsdocComment.customBlocks.filter( + (x) => x.blockTag.tagNameWithUpperCase === StandardTags.defaultValue.tagNameWithUpperCase + ); + + for (const defaultValueBlock of defaultValueBlocks) { + const defaultValueContent: string = this._renderMarkdown(defaultValueBlock.content, apiItem); + if (defaultValueContent) { + yamlItem.defaultValue = defaultValueContent.trim(); + } + } } if (tsdocComment.deprecatedBlock) { @@ -425,7 +450,7 @@ export class YamlDocumenter { } if (ApiReleaseTagMixin.isBaseClassOf(apiItem)) { - if (apiItem.releaseTag === ReleaseTag.Beta) { + if (apiItem.releaseTag === ReleaseTag.Alpha || apiItem.releaseTag === ReleaseTag.Beta) { yamlItem.isPreview = true; } } @@ -745,7 +770,7 @@ export class YamlDocumenter { ): void { JsonFile.validateNoUndefinedMembers(dataObject); - let stringified: string = yaml.safeDump(dataObject, { + let stringified: string = yaml.dump(dataObject, { lineWidth: 120 }); @@ -932,12 +957,12 @@ export class YamlDocumenter { spec.fullName = apiItem ? apiItem.getScopedNameWithinPackage() : token.canonicalReference - ? token.canonicalReference - .withSource(undefined) - .withMeaning(undefined) - .withOverloadIndex(undefined) - .toString() - : token.text; + ? token.canonicalReference + .withSource(undefined) + .withMeaning(undefined) + .withOverloadIndex(undefined) + .toString() + : token.text; specs.push(spec); } else { specs.push({ diff --git a/apps/api-documenter/src/index.ts b/apps/api-documenter/src/index.ts index 90eb399d131..fe1f9a6a206 100644 --- a/apps/api-documenter/src/index.ts +++ b/apps/api-documenter/src/index.ts @@ -9,12 +9,12 @@ * @packageDocumentation */ -export { IFeatureDefinition, IApiDocumenterPluginManifest } from './plugin/IApiDocumenterPluginManifest'; +export type { IFeatureDefinition, IApiDocumenterPluginManifest } from './plugin/IApiDocumenterPluginManifest'; export { MarkdownDocumenterAccessor } from './plugin/MarkdownDocumenterAccessor'; export { MarkdownDocumenterFeatureContext, - IMarkdownDocumenterFeatureOnBeforeWritePageArgs, - IMarkdownDocumenterFeatureOnFinishedArgs, + type IMarkdownDocumenterFeatureOnBeforeWritePageArgs, + type IMarkdownDocumenterFeatureOnFinishedArgs, MarkdownDocumenterFeature } from './plugin/MarkdownDocumenterFeature'; export { PluginFeature, PluginFeatureContext, PluginFeatureInitialization } from './plugin/PluginFeature'; diff --git a/apps/api-documenter/src/markdown/CustomMarkdownEmitter.ts b/apps/api-documenter/src/markdown/CustomMarkdownEmitter.ts index edec714d34a..fa6637733e9 100644 --- a/apps/api-documenter/src/markdown/CustomMarkdownEmitter.ts +++ b/apps/api-documenter/src/markdown/CustomMarkdownEmitter.ts @@ -1,19 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors'; - -import { DocNode, DocLinkTag, StringBuilder } from '@microsoft/tsdoc'; -import { ApiModel, IResolveDeclarationReferenceResult, ApiItem } from '@microsoft/api-extractor-model'; +import type { DocNode, DocLinkTag, StringBuilder } from '@microsoft/tsdoc'; +import type { ApiModel, IResolveDeclarationReferenceResult, ApiItem } from '@microsoft/api-extractor-model'; +import { Colorize } from '@rushstack/terminal'; import { CustomDocNodeKind } from '../nodes/CustomDocNodeKind'; -import { DocHeading } from '../nodes/DocHeading'; -import { DocNoteBox } from '../nodes/DocNoteBox'; -import { DocTable } from '../nodes/DocTable'; -import { DocTableCell } from '../nodes/DocTableCell'; -import { DocEmphasisSpan } from '../nodes/DocEmphasisSpan'; -import { MarkdownEmitter, IMarkdownEmitterContext, IMarkdownEmitterOptions } from './MarkdownEmitter'; -import { IndentedWriter } from '../utils/IndentedWriter'; +import type { DocHeading } from '../nodes/DocHeading'; +import type { DocNoteBox } from '../nodes/DocNoteBox'; +import type { DocTable } from '../nodes/DocTable'; +import type { DocTableCell } from '../nodes/DocTableCell'; +import type { DocEmphasisSpan } from '../nodes/DocEmphasisSpan'; +import { + MarkdownEmitter, + type IMarkdownEmitterContext, + type IMarkdownEmitterOptions +} from './MarkdownEmitter'; +import type { IndentedWriter } from '../utils/IndentedWriter'; export interface ICustomMarkdownEmitterOptions extends IMarkdownEmitterOptions { contextApiItem: ApiItem | undefined; @@ -30,7 +33,7 @@ export class CustomMarkdownEmitter extends MarkdownEmitter { this._apiModel = apiModel; } - public emit( + public override emit( stringBuilder: StringBuilder, docNode: DocNode, options: ICustomMarkdownEmitterOptions @@ -38,8 +41,11 @@ export class CustomMarkdownEmitter extends MarkdownEmitter { return super.emit(stringBuilder, docNode, options); } - /** @override */ - protected writeNode(docNode: DocNode, context: IMarkdownEmitterContext, docNodeSiblings: boolean): void { + protected override writeNode( + docNode: DocNode, + context: IMarkdownEmitterContext, + docNodeSiblings: boolean + ): void { const writer: IndentedWriter = context.writer; switch (docNode.kind) { @@ -86,8 +92,6 @@ export class CustomMarkdownEmitter extends MarkdownEmitter { // whereas VS Code's renderer is totally fine with it. writer.ensureSkippedLine(); - context.insideTable = true; - // Markdown table rows can have inconsistent cell counts. Size the table based on the longest row. let columnCount: number = 0; if (docTable.header) { @@ -99,39 +103,43 @@ export class CustomMarkdownEmitter extends MarkdownEmitter { } } - // write the table header (which is required by Markdown) - writer.write('| '); - for (let i: number = 0; i < columnCount; ++i) { - writer.write(' '); - if (docTable.header) { + writer.write(''); + if (docTable.header) { + writer.write(''); + for (let i: number = 0; i < columnCount; ++i) { + writer.write(''); } - writer.write(' |'); - } - writer.writeLine(); - - // write the divider - writer.write('| '); - for (let i: number = 0; i < columnCount; ++i) { - writer.write(' --- |'); + writer.write(''); } writer.writeLine(); + writer.write(''); for (const row of docTable.rows) { - writer.write('| '); + writer.write(''); for (const cell of row.cells) { - writer.write(' '); + writer.write(''); } + writer.write(''); writer.writeLine(); } - writer.writeLine(); - - context.insideTable = false; + writer.write(''); + writer.write('
'); + writer.ensureNewLine(); + writer.writeLine(); const cell: DocTableCell | undefined = docTable.header.cells[i]; if (cell) { this.writeNode(cell.content, context, false); } + writer.ensureNewLine(); + writer.writeLine(); + writer.write('
'); + writer.ensureNewLine(); + writer.writeLine(); this.writeNode(cell.content, context, false); - writer.write(' |'); + writer.ensureNewLine(); + writer.writeLine(); + writer.write('
'); + writer.ensureSkippedLine(); break; } @@ -151,8 +159,7 @@ export class CustomMarkdownEmitter extends MarkdownEmitter { } } - /** @override */ - protected writeLinkTagWithCodeDestination( + protected override writeLinkTagWithCodeDestination( docLinkTag: DocLinkTag, context: IMarkdownEmitterContext ): void { @@ -179,12 +186,12 @@ export class CustomMarkdownEmitter extends MarkdownEmitter { context.writer.write(encodedLinkText); context.writer.write(`](${filename!})`); } else { - console.log(colors.yellow('WARNING: Unable to determine link text')); + console.log(Colorize.yellow('WARNING: Unable to determine link text')); } } } else if (result.errorMessage) { console.log( - colors.yellow( + Colorize.yellow( `WARNING: Unable to resolve reference "${docLinkTag.codeDestination!.emitAsTsdoc()}": ` + result.errorMessage ) diff --git a/apps/api-documenter/src/markdown/MarkdownEmitter.ts b/apps/api-documenter/src/markdown/MarkdownEmitter.ts index 30946b9cb9a..663eee17f1e 100644 --- a/apps/api-documenter/src/markdown/MarkdownEmitter.ts +++ b/apps/api-documenter/src/markdown/MarkdownEmitter.ts @@ -2,21 +2,21 @@ // See LICENSE in the project root for license information. import { - DocNode, + type DocNode, DocNodeKind, - StringBuilder, - DocPlainText, - DocHtmlStartTag, - DocHtmlEndTag, - DocCodeSpan, - DocLinkTag, - DocParagraph, - DocFencedCode, - DocSection, + type StringBuilder, + type DocPlainText, + type DocHtmlStartTag, + type DocHtmlEndTag, + type DocCodeSpan, + type DocLinkTag, + type DocParagraph, + type DocFencedCode, + type DocSection, DocNodeTransforms, - DocEscapedText, - DocErrorText, - DocBlockTag + type DocEscapedText, + type DocErrorText, + type DocBlockTag } from '@microsoft/tsdoc'; import { InternalError } from '@rushstack/node-core-library'; @@ -26,7 +26,6 @@ export interface IMarkdownEmitterOptions {} export interface IMarkdownEmitterContext { writer: IndentedWriter; - insideTable: boolean; boldRequested: boolean; italicRequested: boolean; @@ -47,7 +46,6 @@ export class MarkdownEmitter { const context: IMarkdownEmitterContext = { writer, - insideTable: false, boldRequested: false, italicRequested: false, @@ -106,23 +104,9 @@ export class MarkdownEmitter { } case DocNodeKind.CodeSpan: { const docCodeSpan: DocCodeSpan = docNode as DocCodeSpan; - if (context.insideTable) { - writer.write(''); - } else { - writer.write('`'); - } - if (context.insideTable) { - const code: string = this.getTableEscapedText(docCodeSpan.code); - const parts: string[] = code.split(/\r?\n/g); - writer.write(parts.join('
')); - } else { - writer.write(docCodeSpan.code); - } - if (context.insideTable) { - writer.write(''); - } else { - writer.write('`'); - } + writer.write('`'); + writer.write(docCodeSpan.code); + writer.write('`'); break; } case DocNodeKind.LinkTag: { @@ -139,24 +123,10 @@ export class MarkdownEmitter { case DocNodeKind.Paragraph: { const docParagraph: DocParagraph = docNode as DocParagraph; const trimmedParagraph: DocParagraph = DocNodeTransforms.trimSpacesInParagraph(docParagraph); - if (context.insideTable) { - if (docNodeSiblings) { - // This tentative write is necessary to avoid writing empty paragraph tags (i.e. `

`). At the - // time this code runs, we do not know whether the `writeNodes` call below will actually write - // anything. Thus, we want to only write a `

` tag (as well as eventually a corresponding - // `

` tag) if something ends up being written within the tags. - writer.writeTentative('

', '

', () => { - this.writeNodes(trimmedParagraph.nodes, context); - }); - } else { - // Special case: If we are the only element inside this table cell, then we can omit the

container. - this.writeNodes(trimmedParagraph.nodes, context); - } - } else { - this.writeNodes(trimmedParagraph.nodes, context); - writer.ensureNewLine(); - writer.writeLine(); - } + + this.writeNodes(trimmedParagraph.nodes, context); + writer.ensureNewLine(); + writer.writeLine(); break; } case DocNodeKind.FencedCode: { diff --git a/apps/api-documenter/src/markdown/test/CustomMarkdownEmitter.test.ts b/apps/api-documenter/src/markdown/test/CustomMarkdownEmitter.test.ts index f892d53a36c..5e545c8d39b 100644 --- a/apps/api-documenter/src/markdown/test/CustomMarkdownEmitter.test.ts +++ b/apps/api-documenter/src/markdown/test/CustomMarkdownEmitter.test.ts @@ -3,7 +3,7 @@ import { DocSection, - TSDocConfiguration, + type TSDocConfiguration, DocPlainText, StringBuilder, DocParagraph, @@ -21,7 +21,7 @@ import { DocTable } from '../../nodes/DocTable'; import { DocTableRow } from '../../nodes/DocTableRow'; import { DocTableCell } from '../../nodes/DocTableCell'; import { CustomMarkdownEmitter } from '../CustomMarkdownEmitter'; -import { ApiModel, ApiItem } from '@microsoft/api-extractor-model'; +import { ApiModel, type ApiItem } from '@microsoft/api-extractor-model'; test('render Markdown from TSDoc', () => { const configuration: TSDocConfiguration = CustomDocNodes.configuration; @@ -171,6 +171,13 @@ test('render Markdown from TSDoc', () => { ) ]); + output.appendNodes([ + new DocHeading({ configuration, title: 'After a table' }), + new DocParagraph({ configuration }, [ + new DocPlainText({ configuration, text: 'just checking lines after a table' }) + ]) + ]); + const stringBuilder: StringBuilder = new StringBuilder(); const apiModel: ApiModel = new ApiModel(); const markdownEmitter: CustomMarkdownEmitter = new CustomMarkdownEmitter(apiModel); diff --git a/apps/api-documenter/src/markdown/test/__snapshots__/CustomMarkdownEmitter.test.ts.snap b/apps/api-documenter/src/markdown/test/__snapshots__/CustomMarkdownEmitter.test.ts.snap index 76263562553..611349e525c 100644 --- a/apps/api-documenter/src/markdown/test/__snapshots__/CustomMarkdownEmitter.test.ts.snap +++ b/apps/api-documenter/src/markdown/test/__snapshots__/CustomMarkdownEmitter.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`render Markdown from TSDoc 1`] = ` " @@ -50,9 +50,35 @@ HTML escape: &quot; ## Table -| Header 1 | Header 2 | -| --- | --- | -| Cell 1 |

Cell 2

**bold text**

| + + +
+ +Header 1 + + + + +Header 2 + + +
+ +Cell 1 + + + + +Cell 2 + +**bold text** + + +
+ +## After a table + +just checking lines after a table " `; diff --git a/apps/api-documenter/src/nodes/CustomDocNodeKind.ts b/apps/api-documenter/src/nodes/CustomDocNodeKind.ts index bb2f6731584..a022ac9dc28 100644 --- a/apps/api-documenter/src/nodes/CustomDocNodeKind.ts +++ b/apps/api-documenter/src/nodes/CustomDocNodeKind.ts @@ -1,4 +1,8 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import { TSDocConfiguration, DocNodeKind } from '@microsoft/tsdoc'; + import { DocEmphasisSpan } from './DocEmphasisSpan'; import { DocHeading } from './DocHeading'; import { DocNoteBox } from './DocNoteBox'; @@ -6,13 +10,10 @@ import { DocTable } from './DocTable'; import { DocTableCell } from './DocTableCell'; import { DocTableRow } from './DocTableRow'; -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - /** * Identifies custom subclasses of {@link DocNode}. */ -export const enum CustomDocNodeKind { +export enum CustomDocNodeKind { EmphasisSpan = 'EmphasisSpan', Heading = 'Heading', NoteBox = 'NoteBox', @@ -21,11 +22,11 @@ export const enum CustomDocNodeKind { TableRow = 'TableRow' } -export class CustomDocNodes { - private static _configuration: TSDocConfiguration | undefined; +let _configuration: TSDocConfiguration | undefined; +export class CustomDocNodes { public static get configuration(): TSDocConfiguration { - if (CustomDocNodes._configuration === undefined) { + if (_configuration === undefined) { const configuration: TSDocConfiguration = new TSDocConfiguration(); configuration.docNodeManager.registerDocNodes('@micrososft/api-documenter', [ @@ -52,8 +53,8 @@ export class CustomDocNodes { CustomDocNodeKind.EmphasisSpan ]); - CustomDocNodes._configuration = configuration; + _configuration = configuration; } - return CustomDocNodes._configuration; + return _configuration; } } diff --git a/apps/api-documenter/src/nodes/DocEmphasisSpan.ts b/apps/api-documenter/src/nodes/DocEmphasisSpan.ts index 0f1f543b877..1f1a5304d5b 100644 --- a/apps/api-documenter/src/nodes/DocEmphasisSpan.ts +++ b/apps/api-documenter/src/nodes/DocEmphasisSpan.ts @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { DocNode, DocNodeContainer, IDocNodeContainerParameters } from '@microsoft/tsdoc'; +import { type DocNode, DocNodeContainer, type IDocNodeContainerParameters } from '@microsoft/tsdoc'; + import { CustomDocNodeKind } from './CustomDocNodeKind'; /** @@ -26,8 +27,7 @@ export class DocEmphasisSpan extends DocNodeContainer { this.italic = !!parameters.italic; } - /** @override */ - public get kind(): string { + public override get kind(): string { return CustomDocNodeKind.EmphasisSpan; } } diff --git a/apps/api-documenter/src/nodes/DocHeading.ts b/apps/api-documenter/src/nodes/DocHeading.ts index d5d48227667..f29eeb46be5 100644 --- a/apps/api-documenter/src/nodes/DocHeading.ts +++ b/apps/api-documenter/src/nodes/DocHeading.ts @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IDocNodeParameters, DocNode } from '@microsoft/tsdoc'; +import { type IDocNodeParameters, DocNode } from '@microsoft/tsdoc'; + import { CustomDocNodeKind } from './CustomDocNodeKind'; /** @@ -33,8 +34,7 @@ export class DocHeading extends DocNode { } } - /** @override */ - public get kind(): string { + public override get kind(): string { return CustomDocNodeKind.Heading; } } diff --git a/apps/api-documenter/src/nodes/DocNoteBox.ts b/apps/api-documenter/src/nodes/DocNoteBox.ts index 85372fb5a29..bc6d09ab39a 100644 --- a/apps/api-documenter/src/nodes/DocNoteBox.ts +++ b/apps/api-documenter/src/nodes/DocNoteBox.ts @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IDocNodeParameters, DocNode, DocSection } from '@microsoft/tsdoc'; +import { type IDocNodeParameters, DocNode, DocSection } from '@microsoft/tsdoc'; + import { CustomDocNodeKind } from './CustomDocNodeKind'; /** @@ -20,13 +21,11 @@ export class DocNoteBox extends DocNode { this.content = new DocSection({ configuration: this.configuration }, sectionChildNodes); } - /** @override */ - public get kind(): string { + public override get kind(): string { return CustomDocNodeKind.NoteBox; } - /** @override */ - protected onGetChildNodes(): ReadonlyArray { + protected override onGetChildNodes(): ReadonlyArray { return [this.content]; } } diff --git a/apps/api-documenter/src/nodes/DocTable.ts b/apps/api-documenter/src/nodes/DocTable.ts index 095e39e3b1f..43da35b609b 100644 --- a/apps/api-documenter/src/nodes/DocTable.ts +++ b/apps/api-documenter/src/nodes/DocTable.ts @@ -1,10 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IDocNodeParameters, DocNode } from '@microsoft/tsdoc'; +import { type IDocNodeParameters, DocNode } from '@microsoft/tsdoc'; + import { CustomDocNodeKind } from './CustomDocNodeKind'; import { DocTableRow } from './DocTableRow'; -import { DocTableCell } from './DocTableCell'; +import type { DocTableCell } from './DocTableCell'; /** * Constructor parameters for {@link DocTable}. @@ -53,8 +54,7 @@ export class DocTable extends DocNode { } } - /** @override */ - public get kind(): string { + public override get kind(): string { return CustomDocNodeKind.Table; } @@ -72,8 +72,7 @@ export class DocTable extends DocNode { return row; } - /** @override */ - protected onGetChildNodes(): ReadonlyArray { + protected override onGetChildNodes(): ReadonlyArray { return [this.header, ...this._rows]; } } diff --git a/apps/api-documenter/src/nodes/DocTableCell.ts b/apps/api-documenter/src/nodes/DocTableCell.ts index 8a56c13a836..ce07c0ef084 100644 --- a/apps/api-documenter/src/nodes/DocTableCell.ts +++ b/apps/api-documenter/src/nodes/DocTableCell.ts @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IDocNodeParameters, DocNode, DocSection } from '@microsoft/tsdoc'; +import { type IDocNodeParameters, DocNode, DocSection } from '@microsoft/tsdoc'; + import { CustomDocNodeKind } from './CustomDocNodeKind'; /** @@ -21,8 +22,7 @@ export class DocTableCell extends DocNode { this.content = new DocSection({ configuration: this.configuration }, sectionChildNodes); } - /** @override */ - public get kind(): string { + public override get kind(): string { return CustomDocNodeKind.TableCell; } } diff --git a/apps/api-documenter/src/nodes/DocTableRow.ts b/apps/api-documenter/src/nodes/DocTableRow.ts index d0d949a7c9b..bf215b7c6d1 100644 --- a/apps/api-documenter/src/nodes/DocTableRow.ts +++ b/apps/api-documenter/src/nodes/DocTableRow.ts @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IDocNodeParameters, DocNode, DocPlainText } from '@microsoft/tsdoc'; +import { type IDocNodeParameters, DocNode, DocPlainText } from '@microsoft/tsdoc'; + import { CustomDocNodeKind } from './CustomDocNodeKind'; import { DocTableCell } from './DocTableCell'; @@ -27,8 +28,7 @@ export class DocTableRow extends DocNode { } } - /** @override */ - public get kind(): string { + public override get kind(): string { return CustomDocNodeKind.TableRow; } @@ -57,8 +57,7 @@ export class DocTableRow extends DocNode { return cell; } - /** @override */ - protected onGetChildNodes(): ReadonlyArray { + protected override onGetChildNodes(): ReadonlyArray { return this._cells; } } diff --git a/apps/api-documenter/src/plugin/IApiDocumenterPluginManifest.ts b/apps/api-documenter/src/plugin/IApiDocumenterPluginManifest.ts index 1e047bff133..c01e7c195e9 100644 --- a/apps/api-documenter/src/plugin/IApiDocumenterPluginManifest.ts +++ b/apps/api-documenter/src/plugin/IApiDocumenterPluginManifest.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { MarkdownDocumenterFeature } from './MarkdownDocumenterFeature'; -import { PluginFeatureInitialization } from './PluginFeature'; +import type { MarkdownDocumenterFeature } from './MarkdownDocumenterFeature'; +import type { PluginFeatureInitialization } from './PluginFeature'; /** * Defines a "feature" that is provided by an API Documenter plugin. A feature is a user-defined module diff --git a/apps/api-documenter/src/plugin/MarkdownDocumenterAccessor.ts b/apps/api-documenter/src/plugin/MarkdownDocumenterAccessor.ts index 41a57a8e6df..8f6c176e094 100644 --- a/apps/api-documenter/src/plugin/MarkdownDocumenterAccessor.ts +++ b/apps/api-documenter/src/plugin/MarkdownDocumenterAccessor.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ApiItem } from '@microsoft/api-extractor-model'; +import type { ApiItem } from '@microsoft/api-extractor-model'; /** @internal */ export interface IMarkdownDocumenterAccessorImplementation { diff --git a/apps/api-documenter/src/plugin/MarkdownDocumenterFeature.ts b/apps/api-documenter/src/plugin/MarkdownDocumenterFeature.ts index 8a15c0f1435..efa45f7d726 100644 --- a/apps/api-documenter/src/plugin/MarkdownDocumenterFeature.ts +++ b/apps/api-documenter/src/plugin/MarkdownDocumenterFeature.ts @@ -1,10 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ApiItem, ApiModel } from '@microsoft/api-extractor-model'; +import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model'; import { TypeUuid } from '@rushstack/node-core-library'; + import { PluginFeature } from './PluginFeature'; -import { MarkdownDocumenterAccessor } from './MarkdownDocumenterAccessor'; +import type { MarkdownDocumenterAccessor } from './MarkdownDocumenterAccessor'; /** * Context object for {@link MarkdownDocumenterFeature}. @@ -74,7 +75,7 @@ const uuidMarkdownDocumenterFeature: string = '34196154-9eb3-4de0-a8c8-7e9539dfe */ export class MarkdownDocumenterFeature extends PluginFeature { /** {@inheritdoc PluginFeature.context} */ - public context!: MarkdownDocumenterFeatureContext; + public override context!: MarkdownDocumenterFeatureContext; /** * This event occurs before each markdown file is written. It provides an opportunity to customize the @@ -93,7 +94,7 @@ export class MarkdownDocumenterFeature extends PluginFeature { // (implemented by child class) } - public static [Symbol.hasInstance](instance: object): boolean { + public static override [Symbol.hasInstance](instance: object): boolean { return TypeUuid.isInstanceOf(instance, uuidMarkdownDocumenterFeature); } } diff --git a/apps/api-documenter/src/plugin/PluginLoader.ts b/apps/api-documenter/src/plugin/PluginLoader.ts index dbf6caba709..6d926f6ea66 100644 --- a/apps/api-documenter/src/plugin/PluginLoader.ts +++ b/apps/api-documenter/src/plugin/PluginLoader.ts @@ -1,13 +1,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import * as resolve from 'resolve'; -import { IApiDocumenterPluginManifest, IFeatureDefinition } from './IApiDocumenterPluginManifest'; -import { MarkdownDocumenterFeature, MarkdownDocumenterFeatureContext } from './MarkdownDocumenterFeature'; +import type { IApiDocumenterPluginManifest, IFeatureDefinition } from './IApiDocumenterPluginManifest'; +import { + MarkdownDocumenterFeature, + type MarkdownDocumenterFeatureContext +} from './MarkdownDocumenterFeature'; import { PluginFeatureInitialization } from './PluginFeature'; -import { DocumenterConfig } from '../documenters/DocumenterConfig'; +import type { DocumenterConfig } from '../documenters/DocumenterConfig'; interface ILoadedPlugin { packageName: string; diff --git a/apps/api-documenter/src/start.ts b/apps/api-documenter/src/start.ts index 592e84d2ed6..e6266aa99fa 100644 --- a/apps/api-documenter/src/start.ts +++ b/apps/api-documenter/src/start.ts @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as os from 'os'; -import colors from 'colors'; +import * as os from 'node:os'; import { PackageJsonLookup } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; import { ApiDocumenterCommandLine } from './cli/ApiDocumenterCommandLine'; @@ -12,9 +12,11 @@ const myPackageVersion: string = PackageJsonLookup.loadOwnPackageJson(__dirname) console.log( os.EOL + - colors.bold(`api-documenter ${myPackageVersion} ` + colors.cyan(' - https://api-extractor.com/') + os.EOL) + Colorize.bold( + `api-documenter ${myPackageVersion} ` + Colorize.cyan(' - https://api-extractor.com/') + os.EOL + ) ); const parser: ApiDocumenterCommandLine = new ApiDocumenterCommandLine(); -parser.execute().catch(console.error); // CommandLineParser.execute() should never reject the promise +parser.executeAsync().catch(console.error); // CommandLineParser.executeAsync() should never reject the promise diff --git a/apps/api-documenter/src/utils/IndentedWriter.ts b/apps/api-documenter/src/utils/IndentedWriter.ts index cd5d1a31020..d751ac1682e 100644 --- a/apps/api-documenter/src/utils/IndentedWriter.ts +++ b/apps/api-documenter/src/utils/IndentedWriter.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { StringBuilder, IStringBuilder } from '@rushstack/node-core-library'; +import { StringBuilder, type IStringBuilder } from '@rushstack/node-core-library'; /** * A utility for writing indented text. diff --git a/apps/api-documenter/src/utils/ToSdpConvertHelper.ts b/apps/api-documenter/src/utils/ToSdpConvertHelper.ts index 392ad7b2e1f..c81d01f6a18 100644 --- a/apps/api-documenter/src/utils/ToSdpConvertHelper.ts +++ b/apps/api-documenter/src/utils/ToSdpConvertHelper.ts @@ -1,11 +1,20 @@ -import { +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; + +import yaml = require('js-yaml'); + +import { FileSystem, Encoding, NewlineKind } from '@rushstack/node-core-library'; + +import type { IYamlItem, IYamlApiFile, IYamlSyntax, IYamlReferenceSpec, IYamlReference } from '../yaml/IYamlApiFile'; -import { +import type { PackageYamlModel, EnumYamlModel, TypeAliasYamlModel, @@ -14,9 +23,6 @@ import { FunctionYamlModel, CommonYamlModel } from '../yaml/ISDPYamlFile'; -import path from 'path'; -import { FileSystem, Encoding, NewlineKind } from '@rushstack/node-core-library'; -import yaml = require('js-yaml'); export function convertUDPYamlToSDP(folderPath: string): void { convert(folderPath, folderPath); @@ -46,10 +52,10 @@ function convert(inputPath: string, outputPath: string): void { console.log(`convert file ${fpath} from udp to sdp`); - const file: IYamlApiFile = yaml.safeLoad(yamlContent) as IYamlApiFile; + const file: IYamlApiFile = yaml.load(yamlContent) as IYamlApiFile; const result: { model: CommonYamlModel; type: string } | undefined = convertToSDP(file); if (result && result.model) { - const stringified: string = `### YamlMime:TS${result.type}\n${yaml.safeDump(result.model, { + const stringified: string = `### YamlMime:TS${result.type}\n${yaml.dump(result.model, { lineWidth: 120 })}`; FileSystem.writeFile(`${outputPath}/${name}`, stringified, { @@ -298,6 +304,10 @@ function convertCommonYamlModel( result.example = []; } + if (element.defaultValue) { + result.defaultValue = element.defaultValue; + } + result.isPreview = element.isPreview; if (!result.isPreview) { result.isPreview = false; diff --git a/apps/api-documenter/src/utils/Utilities.ts b/apps/api-documenter/src/utils/Utilities.ts index 0416dc7fa86..56be69c2e03 100644 --- a/apps/api-documenter/src/utils/Utilities.ts +++ b/apps/api-documenter/src/utils/Utilities.ts @@ -1,10 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ApiParameterListMixin, ApiItem } from '@microsoft/api-extractor-model'; +import { ApiParameterListMixin, type ApiItem } from '@microsoft/api-extractor-model'; + +const _badFilenameCharsRegExp: RegExp = /[^a-z0-9_\-\.]/gi; export class Utilities { - private static readonly _badFilenameCharsRegExp: RegExp = /[^a-z0-9_\-\.]/gi; /** * Generates a concise signature for a function. Example: "getArea(width, height)" */ @@ -21,6 +22,6 @@ export class Utilities { public static getSafeFilenameForName(name: string): string { // TODO: This can introduce naming collisions. // We will fix that as part of https://github.com/microsoft/rushstack/issues/1308 - return name.replace(Utilities._badFilenameCharsRegExp, '_').toLowerCase(); + return name.replace(_badFilenameCharsRegExp, '_').toLowerCase(); } } diff --git a/apps/api-documenter/src/utils/test/__snapshots__/IndentedWriter.test.ts.snap b/apps/api-documenter/src/utils/test/__snapshots__/IndentedWriter.test.ts.snap index 62778d301c8..115fb3887a6 100644 --- a/apps/api-documenter/src/utils/test/__snapshots__/IndentedWriter.test.ts.snap +++ b/apps/api-documenter/src/utils/test/__snapshots__/IndentedWriter.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`01 Demo from docs 1`] = ` "begin diff --git a/apps/api-documenter/src/yaml/ISDPYamlFile.ts b/apps/api-documenter/src/yaml/ISDPYamlFile.ts index 250e9c94949..574434154d4 100644 --- a/apps/api-documenter/src/yaml/ISDPYamlFile.ts +++ b/apps/api-documenter/src/yaml/ISDPYamlFile.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + interface IBaseYamlModel { uid: string; name: string; @@ -13,6 +16,7 @@ export type CommonYamlModel = IBaseYamlModel & { remarks?: string; example?: string[]; customDeprecatedMessage?: string; + defaultValue?: string; }; export type PackageYamlModel = CommonYamlModel & { diff --git a/apps/api-documenter/src/yaml/IYamlApiFile.ts b/apps/api-documenter/src/yaml/IYamlApiFile.ts index 58048a50d41..59f1b2ab1ad 100644 --- a/apps/api-documenter/src/yaml/IYamlApiFile.ts +++ b/apps/api-documenter/src/yaml/IYamlApiFile.ts @@ -327,6 +327,12 @@ export interface IYamlItem { * NOTE: This is an extension and corresponds to `ItemViewModel.Metadata` in DocFX. */ package?: string; + + /** + * The default value for the item. + * NOTE: This is an extension and corresponds to `ItemViewModel.Metadata` in DocFX. + */ + defaultValue?: string; } /** diff --git a/apps/api-documenter/src/yaml/typescript.schema.json b/apps/api-documenter/src/yaml/typescript.schema.json index cc1ad42ccf0..d5baf66d72a 100644 --- a/apps/api-documenter/src/yaml/typescript.schema.json +++ b/apps/api-documenter/src/yaml/typescript.schema.json @@ -262,6 +262,9 @@ }, "package": { "type": "string" + }, + "defaultValue": { + "type": "string" } }, "patternProperties": { diff --git a/apps/api-documenter/tsconfig.json b/apps/api-documenter/tsconfig.json index a114c3448ed..dac21d04081 100644 --- a/apps/api-documenter/tsconfig.json +++ b/apps/api-documenter/tsconfig.json @@ -1,3 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json" + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/apps/api-extractor/.eslintrc.js b/apps/api-extractor/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/apps/api-extractor/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/apps/api-extractor/.npmignore b/apps/api-extractor/.npmignore index 7a29489cbcc..64ed79eb4bc 100644 --- a/apps/api-extractor/.npmignore +++ b/apps/api-extractor/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,18 +21,17 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- - -# (Add your project-specific overrides here) +# README.md +# LICENSE +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- !/extends/*.json diff --git a/apps/api-extractor/.vscode/launch.json b/apps/api-extractor/.vscode/launch.json index b9c74c3b15e..4a3f7bb4fa8 100644 --- a/apps/api-extractor/.vscode/launch.json +++ b/apps/api-extractor/.vscode/launch.json @@ -79,7 +79,7 @@ "run", "--local", "--config", - "./temp/configs/api-extractor-spanSorting.json" + "./temp/configs/api-extractor-destructuredParameters.json" ], "sourceMaps": true } diff --git a/apps/api-extractor/CHANGELOG.json b/apps/api-extractor/CHANGELOG.json index 094c0b03c1e..e6f8e8dc0b8 100644 --- a/apps/api-extractor/CHANGELOG.json +++ b/apps/api-extractor/CHANGELOG.json @@ -1,6 +1,2192 @@ { "name": "@microsoft/api-extractor", "entries": [ + { + "version": "7.58.12", + "tag": "@microsoft/api-extractor_v7.58.12", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "patch": [ + { + "comment": "Improve the performance of the internal excerpt token condensing algorithm from O(n^2) to O(n) by eliminating repeated array splicing and per-merge bookkeeping." + } + ] + } + }, + { + "version": "7.58.11", + "tag": "@microsoft/api-extractor_v7.58.11", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.10`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.12`" + } + ] + } + }, + { + "version": "7.58.10", + "tag": "@microsoft/api-extractor_v7.58.10", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.11`" + } + ] + } + }, + { + "version": "7.58.9", + "tag": "@microsoft/api-extractor_v7.58.9", + "date": "Sat, 13 Jun 2026 00:16:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.10`" + } + ] + } + }, + { + "version": "7.58.8", + "tag": "@microsoft/api-extractor_v7.58.8", + "date": "Mon, 08 Jun 2026 15:15:49 GMT", + "comments": { + "patch": [ + { + "comment": "Add support for new d.ts extension format when using TS moduleResolution 'bundler' or 'nodenext'." + } + ] + } + }, + { + "version": "7.58.7", + "tag": "@microsoft/api-extractor_v7.58.7", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.9`" + } + ] + } + }, + { + "version": "7.58.6", + "tag": "@microsoft/api-extractor_v7.58.6", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where empty lines were included in DTS rollups in place of API items that were trimmed." + } + ] + } + }, + { + "version": "7.58.5", + "tag": "@microsoft/api-extractor_v7.58.5", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.8`" + } + ] + } + }, + { + "version": "7.58.4", + "tag": "@microsoft/api-extractor_v7.58.4", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "patch": [ + { + "comment": "Bump semver." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.7`" + } + ] + } + }, + { + "version": "7.58.3", + "tag": "@microsoft/api-extractor_v7.58.3", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "patch": [ + { + "comment": "Remove dependecy on `lodash`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.6`" + } + ] + } + }, + { + "version": "7.58.2", + "tag": "@microsoft/api-extractor_v7.58.2", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.5`" + } + ] + } + }, + { + "version": "7.58.1", + "tag": "@microsoft/api-extractor_v7.58.1", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "patch": [ + { + "comment": "Bump lodash 4.18.1 to address CVEs GHSA-r5fr-rjxr-66jc, GHSA-f23m-r3pf-42rh" + } + ] + } + }, + { + "version": "7.58.0", + "tag": "@microsoft/api-extractor_v7.58.0", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "minor": [ + { + "comment": "Upgrade the bundled compiler engine to TypeScript 5.9.3" + } + ] + } + }, + { + "version": "7.57.8", + "tag": "@microsoft/api-extractor_v7.57.8", + "date": "Tue, 31 Mar 2026 15:14:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.4`" + } + ] + } + }, + { + "version": "7.57.7", + "tag": "@microsoft/api-extractor_v7.57.7", + "date": "Mon, 09 Mar 2026 15:14:07 GMT", + "comments": { + "patch": [ + { + "comment": "Bump `minimatch` version from `10.2.1` to `10.2.3` to address CVE-2026-27903." + } + ] + } + }, + { + "version": "7.57.6", + "tag": "@microsoft/api-extractor_v7.57.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "patch": [ + { + "comment": "Bump `@microsoft/tsdoc-config` to `~0.18.1` to mitigate CVE-2025-69873." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.4`" + } + ] + } + }, + { + "version": "7.57.5", + "tag": "@microsoft/api-extractor_v7.57.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.7.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.3`" + } + ] + } + }, + { + "version": "7.57.4", + "tag": "@microsoft/api-extractor_v7.57.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.2`" + } + ] + } + }, + { + "version": "7.57.3", + "tag": "@microsoft/api-extractor_v7.57.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "patch": [ + { + "comment": "Add missing \"./extends/*.json\" to the package.json \"exports\" field so that \"@microsoft/api-extractor/extends/tsdoc-base.json\" is importable." + } + ] + } + }, + { + "version": "7.57.2", + "tag": "@microsoft/api-extractor_v7.57.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "patch": [ + { + "comment": "Bump minimatch from 10.1.2 to 10.2.1" + } + ] + } + }, + { + "version": "7.57.1", + "tag": "@microsoft/api-extractor_v7.57.1", + "date": "Fri, 20 Feb 2026 00:15:03 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.7.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.1`" + } + ] + } + }, + { + "version": "7.57.0", + "tag": "@microsoft/api-extractor_v7.57.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.33.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.0`" + } + ] + } + }, + { + "version": "7.56.3", + "tag": "@microsoft/api-extractor_v7.56.3", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "patch": [ + { + "comment": "Upgrade `lodash` dependency from `~4.17.15` to `~4.17.23`." + } + ] + } + }, + { + "version": "7.56.2", + "tag": "@microsoft/api-extractor_v7.56.2", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "patch": [ + { + "comment": "Update minimatch dependency from 10.0.3 to 10.1.2" + } + ] + } + }, + { + "version": "7.56.1", + "tag": "@microsoft/api-extractor_v7.56.1", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.2.0`" + } + ] + } + }, + { + "version": "7.56.0", + "tag": "@microsoft/api-extractor_v7.56.0", + "date": "Fri, 30 Jan 2026 01:16:12 GMT", + "comments": { + "minor": [ + { + "comment": "Fix an issue where destructured parameters produced an incorrect parameter name" + } + ] + } + }, + { + "version": "7.55.5", + "tag": "@microsoft/api-extractor_v7.55.5", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "patch": [ + { + "comment": "Fix missing 'export' keyword for namespace re-exports that produced invalid TypeScript output" + } + ] + } + }, + { + "version": "7.55.4", + "tag": "@microsoft/api-extractor_v7.55.4", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.7`" + } + ] + } + }, + { + "version": "7.55.3", + "tag": "@microsoft/api-extractor_v7.55.3", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.6`" + } + ] + } + }, + { + "version": "7.55.2", + "tag": "@microsoft/api-extractor_v7.55.2", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.32.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.5`" + } + ] + } + }, + { + "version": "7.55.1", + "tag": "@microsoft/api-extractor_v7.55.1", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.32.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.4`" + } + ] + } + }, + { + "version": "7.55.0", + "tag": "@microsoft/api-extractor_v7.55.0", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `@microsoft/tsdoc` dependency to `~0.16.0`." + }, + { + "comment": "Bump the `@microsoft/tsdoc-config` dependency to `~0.18.0`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.32.0`" + } + ] + } + }, + { + "version": "7.54.0", + "tag": "@microsoft/api-extractor_v7.54.0", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "minor": [ + { + "comment": "Add a new setting `IExtractorInvokeOptions.printApiReportDiff` that makes build logs easier to diagnose by printing a diff of any changes to API report files (*.api.md)." + }, + { + "comment": "Add a `--print-api-report-diff` CLI flag that causes a diff of any changes to API report files (*.api.md) to be printed." + } + ] + } + }, + { + "version": "7.53.3", + "tag": "@microsoft/api-extractor_v7.53.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.31.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.3`" + } + ] + } + }, + { + "version": "7.53.2", + "tag": "@microsoft/api-extractor_v7.53.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.31.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.2`" + } + ] + } + }, + { + "version": "7.53.1", + "tag": "@microsoft/api-extractor_v7.53.1", + "date": "Wed, 08 Oct 2025 00:13:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.31.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.1`" + } + ] + } + }, + { + "version": "7.53.0", + "tag": "@microsoft/api-extractor_v7.53.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.31.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.0`" + } + ] + } + }, + { + "version": "7.52.15", + "tag": "@microsoft/api-extractor_v7.52.15", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.5`" + } + ] + } + }, + { + "version": "7.52.14", + "tag": "@microsoft/api-extractor_v7.52.14", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.4`" + } + ] + } + }, + { + "version": "7.52.13", + "tag": "@microsoft/api-extractor_v7.52.13", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "patch": [ + { + "comment": "Fixes a bug in ExtractorAnalyzer._isExternalModulePath where type import declarations were not resolved." + } + ] + } + }, + { + "version": "7.52.12", + "tag": "@microsoft/api-extractor_v7.52.12", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.3`" + } + ] + } + }, + { + "version": "7.52.11", + "tag": "@microsoft/api-extractor_v7.52.11", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "patch": [ + { + "comment": "Fix self-package import resolution by removing outDir/declarationDir from compiler options" + } + ] + } + }, + { + "version": "7.52.10", + "tag": "@microsoft/api-extractor_v7.52.10", + "date": "Fri, 01 Aug 2025 00:12:48 GMT", + "comments": { + "patch": [ + { + "comment": "Upgrades the minimatch dependency from ~3.0.3 to 10.0.3 across the entire Rush monorepo to address a Regular Expression Denial of Service (ReDoS) vulnerability in the underlying brace-expansion dependency." + } + ] + } + }, + { + "version": "7.52.9", + "tag": "@microsoft/api-extractor_v7.52.9", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.2`" + } + ] + } + }, + { + "version": "7.52.8", + "tag": "@microsoft/api-extractor_v7.52.8", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "patch": [ + { + "comment": "Fixes API extractor error handling when changed APIs are encountered and the \"--local\" flag is not specified" + } + ] + } + }, + { + "version": "7.52.7", + "tag": "@microsoft/api-extractor_v7.52.7", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where default exports were sometimes trimmed incorrectly in .api.md files when using `reportVariants` (GitHub #4775)" + } + ] + } + }, + { + "version": "7.52.6", + "tag": "@microsoft/api-extractor_v7.52.6", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.1`" + } + ] + } + }, + { + "version": "7.52.5", + "tag": "@microsoft/api-extractor_v7.52.5", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.0`" + } + ] + } + }, + { + "version": "7.52.4", + "tag": "@microsoft/api-extractor_v7.52.4", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "patch": [ + { + "comment": "Update documentation for `extends`" + } + ] + } + }, + { + "version": "7.52.3", + "tag": "@microsoft/api-extractor_v7.52.3", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "patch": [ + { + "comment": "Add support for customizing which TSDoc tags appear in API reports" + } + ] + } + }, + { + "version": "7.52.2", + "tag": "@microsoft/api-extractor_v7.52.2", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.7`" + } + ] + } + }, + { + "version": "7.52.1", + "tag": "@microsoft/api-extractor_v7.52.1", + "date": "Tue, 11 Mar 2025 02:12:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.6`" + } + ] + } + }, + { + "version": "7.52.0", + "tag": "@microsoft/api-extractor_v7.52.0", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "minor": [ + { + "comment": "Upgrade the bundled compiler engine to TypeScript 5.8.2" + } + ] + } + }, + { + "version": "7.51.1", + "tag": "@microsoft/api-extractor_v7.51.1", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "patch": [ + { + "comment": "Include triple-slash references marked with `preserve=\"true\"` from files that only contain re-exports. There was a behavior change in TypeScript 5.5, where only triple-slash references that are explicitly marked with `preserve=\"true\"` are emitted into declaration files. This change adds support for placing these references in files that only contain re-exports, like the API entrypoint file." + } + ] + } + }, + { + "version": "7.51.0", + "tag": "@microsoft/api-extractor_v7.51.0", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "minor": [ + { + "comment": "Add a `docModel.releaseTagsToTrim` property to `api-extractor.json` to specify which release tags should be trimmed when the doc model is produced." + } + ] + } + }, + { + "version": "7.50.1", + "tag": "@microsoft/api-extractor_v7.50.1", + "date": "Sat, 22 Feb 2025 01:11:11 GMT", + "comments": { + "patch": [ + { + "comment": "Upgrade the bundled compiler engine to TypeScript 5.7.3" + } + ] + } + }, + { + "version": "7.50.0", + "tag": "@microsoft/api-extractor_v7.50.0", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "minor": [ + { + "comment": "Update merge behavior for derived configurations to allow overriding array properties" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.5`" + } + ] + } + }, + { + "version": "7.49.2", + "tag": "@microsoft/api-extractor_v7.49.2", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.4`" + } + ] + } + }, + { + "version": "7.49.1", + "tag": "@microsoft/api-extractor_v7.49.1", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.3`" + } + ] + } + }, + { + "version": "7.49.0", + "tag": "@microsoft/api-extractor_v7.49.0", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "minor": [ + { + "comment": "Upgrade the bundled compiler engine to TypeScript 5.7.2" + } + ] + } + }, + { + "version": "7.48.1", + "tag": "@microsoft/api-extractor_v7.48.1", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.2`" + } + ] + } + }, + { + "version": "7.48.0", + "tag": "@microsoft/api-extractor_v7.48.0", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "minor": [ + { + "comment": "Update TSDoc dependencies." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.30.0`" + } + ] + } + }, + { + "version": "7.47.12", + "tag": "@microsoft/api-extractor_v7.47.12", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.1`" + } + ] + } + }, + { + "version": "7.47.11", + "tag": "@microsoft/api-extractor_v7.47.11", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.0`" + } + ] + } + }, + { + "version": "7.47.10", + "tag": "@microsoft/api-extractor_v7.47.10", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "patch": [ + { + "comment": "Fix a compatibility issue with usage of `getModeForUsageLocation` in TypeScript 5.6" + } + ] + } + }, + { + "version": "7.47.9", + "tag": "@microsoft/api-extractor_v7.47.9", + "date": "Fri, 13 Sep 2024 00:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.8`" + } + ] + } + }, + { + "version": "7.47.8", + "tag": "@microsoft/api-extractor_v7.47.8", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.7`" + } + ] + } + }, + { + "version": "7.47.7", + "tag": "@microsoft/api-extractor_v7.47.7", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.6`" + } + ] + } + }, + { + "version": "7.47.6", + "tag": "@microsoft/api-extractor_v7.47.6", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.5`" + } + ] + } + }, + { + "version": "7.47.5", + "tag": "@microsoft/api-extractor_v7.47.5", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.4`" + } + ] + } + }, + { + "version": "7.47.4", + "tag": "@microsoft/api-extractor_v7.47.4", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.5.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.3`" + } + ] + } + }, + { + "version": "7.47.3", + "tag": "@microsoft/api-extractor_v7.47.3", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an edge case when discarding the file extension from the \"reportFileName\" setting and improve its documentation" + } + ] + } + }, + { + "version": "7.47.2", + "tag": "@microsoft/api-extractor_v7.47.2", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.2`" + } + ] + } + }, + { + "version": "7.47.1", + "tag": "@microsoft/api-extractor_v7.47.1", + "date": "Tue, 16 Jul 2024 00:36:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.1`" + } + ] + } + }, + { + "version": "7.47.0", + "tag": "@microsoft/api-extractor_v7.47.0", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for re-exporting modules using syntax such as `export * as ns from './file'` (GitHub #2780)" + } + ] + } + }, + { + "version": "7.46.2", + "tag": "@microsoft/api-extractor_v7.46.2", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "patch": [ + { + "comment": "Include missing `type` modifiers on type-only exports." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.0`" + } + ] + } + }, + { + "version": "7.46.1", + "tag": "@microsoft/api-extractor_v7.46.1", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.5`" + } + ] + } + }, + { + "version": "7.46.0", + "tag": "@microsoft/api-extractor_v7.46.0", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "minor": [ + { + "comment": "Bump TSDoc dependencies." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.29.0`" + } + ] + } + }, + { + "version": "7.45.1", + "tag": "@microsoft/api-extractor_v7.45.1", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.21`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.4`" + } + ] + } + }, + { + "version": "7.45.0", + "tag": "@microsoft/api-extractor_v7.45.0", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "minor": [ + { + "comment": "Improve support for resolving the `tsdoc-metadata.json` to include the folder referenced by a `types` field in an `\"exports\"` field and an `\"typesVersions\"` field in addition to `\"types\"`, `\"typings\"`, and `\"tsdocMetadata\"` fields." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.20`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.3`" + } + ] + } + }, + { + "version": "7.44.1", + "tag": "@microsoft/api-extractor_v7.44.1", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.19`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.2`" + } + ] + } + }, + { + "version": "7.44.0", + "tag": "@microsoft/api-extractor_v7.44.0", + "date": "Fri, 24 May 2024 00:15:08 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for \"variants\" of API reports which include or exclude items by release tag" + } + ] + } + }, + { + "version": "7.43.8", + "tag": "@microsoft/api-extractor_v7.43.8", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.18`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.1`" + } + ] + } + }, + { + "version": "7.43.7", + "tag": "@microsoft/api-extractor_v7.43.7", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.0`" + } + ] + } + }, + { + "version": "7.43.6", + "tag": "@microsoft/api-extractor_v7.43.6", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.20.1`" + } + ] + } + }, + { + "version": "7.43.5", + "tag": "@microsoft/api-extractor_v7.43.5", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.17`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.20.0`" + } + ] + } + }, + { + "version": "7.43.4", + "tag": "@microsoft/api-extractor_v7.43.4", + "date": "Fri, 10 May 2024 05:33:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.16`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.5`" + } + ] + } + }, + { + "version": "7.43.3", + "tag": "@microsoft/api-extractor_v7.43.3", + "date": "Wed, 08 May 2024 22:23:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.4`" + } + ] + } + }, + { + "version": "7.43.2", + "tag": "@microsoft/api-extractor_v7.43.2", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.15`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.3`" + } + ] + } + }, + { + "version": "7.43.1", + "tag": "@microsoft/api-extractor_v7.43.1", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.14`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.2`" + } + ] + } + }, + { + "version": "7.43.0", + "tag": "@microsoft/api-extractor_v7.43.0", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "minor": [ + { + "comment": "Upgrade the bundled compiler engine to TypeScript 5.4.2" + } + ] + } + }, + { + "version": "7.42.3", + "tag": "@microsoft/api-extractor_v7.42.3", + "date": "Sun, 03 Mar 2024 20:58:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.1`" + } + ] + } + }, + { + "version": "7.42.2", + "tag": "@microsoft/api-extractor_v7.42.2", + "date": "Sat, 02 Mar 2024 02:22:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.0`" + } + ] + } + }, + { + "version": "7.42.1", + "tag": "@microsoft/api-extractor_v7.42.1", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.18.1`" + } + ] + } + }, + { + "version": "7.42.0", + "tag": "@microsoft/api-extractor_v7.42.0", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "patch": [ + { + "comment": "Don't mark items documented with {@inheritDoc} references to package-external items as \"undocumented\"" + } + ], + "minor": [ + { + "comment": "Add glob support in `bundledPackages`" + } + ] + } + }, + { + "version": "7.41.1", + "tag": "@microsoft/api-extractor_v7.41.1", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.18.0`" + } + ] + } + }, + { + "version": "7.41.0", + "tag": "@microsoft/api-extractor_v7.41.0", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "minor": [ + { + "comment": "Replace const enums with conventional enums to allow for compatibility with JavaScript consumers." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.4`" + } + ] + } + }, + { + "version": "7.40.6", + "tag": "@microsoft/api-extractor_v7.40.6", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "patch": [ + { + "comment": "Replace the dependency on the `colors` package with `Colorize` from `@rushstack/terminal`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.13`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.3`" + } + ] + } + }, + { + "version": "7.40.5", + "tag": "@microsoft/api-extractor_v7.40.5", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where imports were trimmed from external packages based when generating .d.ts rollups" + } + ] + } + }, + { + "version": "7.40.4", + "tag": "@microsoft/api-extractor_v7.40.4", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + } + ] + } + }, + { + "version": "7.40.3", + "tag": "@microsoft/api-extractor_v7.40.3", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.11`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + } + ] + } + }, + { + "version": "7.40.2", + "tag": "@microsoft/api-extractor_v7.40.2", + "date": "Sat, 17 Feb 2024 06:24:34 GMT", + "comments": { + "patch": [ + { + "comment": "Fix broken link to API documentation" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.10`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.5.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.2`" + } + ] + } + }, + { + "version": "7.40.1", + "tag": "@microsoft/api-extractor_v7.40.1", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + } + ] + } + }, + { + "version": "7.40.0", + "tag": "@microsoft/api-extractor_v7.40.0", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "minor": [ + { + "comment": "Classify arrow functions as `function` kind in the doc model export." + } + ] + } + }, + { + "version": "7.39.5", + "tag": "@microsoft/api-extractor_v7.39.5", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + } + ] + } + }, + { + "version": "7.39.4", + "tag": "@microsoft/api-extractor_v7.39.4", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + } + ] + } + }, + { + "version": "7.39.3", + "tag": "@microsoft/api-extractor_v7.39.3", + "date": "Tue, 23 Jan 2024 20:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + } + ] + } + }, + { + "version": "7.39.2", + "tag": "@microsoft/api-extractor_v7.39.2", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + } + ] + } + }, + { + "version": "7.39.1", + "tag": "@microsoft/api-extractor_v7.39.1", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + } + ] + } + }, + { + "version": "7.39.0", + "tag": "@microsoft/api-extractor_v7.39.0", + "date": "Wed, 20 Dec 2023 01:09:45 GMT", + "comments": { + "minor": [ + { + "comment": "Update API Extractor to support TypeScript 5.3.3" + } + ] + } + }, + { + "version": "7.38.5", + "tag": "@microsoft/api-extractor_v7.38.5", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + } + ] + } + }, + { + "version": "7.38.4", + "tag": "@microsoft/api-extractor_v7.38.4", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "patch": [ + { + "comment": "Don't export trimmed namespace members during rollup (#2791)" + } + ] + } + }, + { + "version": "7.38.3", + "tag": "@microsoft/api-extractor_v7.38.3", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where \"ae-undocumented\" was incorrectly reported for private members" + } + ] + } + }, + { + "version": "7.38.2", + "tag": "@microsoft/api-extractor_v7.38.2", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.1`" + } + ] + } + }, + { + "version": "7.38.1", + "tag": "@microsoft/api-extractor_v7.38.1", + "date": "Mon, 30 Oct 2023 23:36:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.0`" + } + ] + } + }, + { + "version": "7.38.0", + "tag": "@microsoft/api-extractor_v7.38.0", + "date": "Sun, 01 Oct 2023 02:56:29 GMT", + "comments": { + "minor": [ + { + "comment": "Add a new message \"ae-undocumented\" to support logging of undocumented API items" + } + ] + } + }, + { + "version": "7.37.3", + "tag": "@microsoft/api-extractor_v7.37.3", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "patch": [ + { + "comment": "Don't strip out @alpha items when generating API reports." + } + ] + } + }, + { + "version": "7.37.2", + "tag": "@microsoft/api-extractor_v7.37.2", + "date": "Thu, 28 Sep 2023 20:53:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + } + ] + } + }, + { + "version": "7.37.1", + "tag": "@microsoft/api-extractor_v7.37.1", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.16.1`" + } + ] + } + }, + { + "version": "7.37.0", + "tag": "@microsoft/api-extractor_v7.37.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.28.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + } + ] + } + }, + { + "version": "7.36.4", + "tag": "@microsoft/api-extractor_v7.36.4", + "date": "Tue, 08 Aug 2023 07:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.27.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + } + ] + } + }, + { + "version": "7.36.3", + "tag": "@microsoft/api-extractor_v7.36.3", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "patch": [ + { + "comment": "Updated semver dependency" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.27.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + } + ] + } + }, + { + "version": "7.36.2", + "tag": "@microsoft/api-extractor_v7.36.2", + "date": "Wed, 12 Jul 2023 15:20:39 GMT", + "comments": { + "patch": [ + { + "comment": "Add api-extractor support for .d.mts and .d.cts files" + } + ] + } + }, + { + "version": "7.36.1", + "tag": "@microsoft/api-extractor_v7.36.1", + "date": "Thu, 06 Jul 2023 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.27.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + } + ] + } + }, + { + "version": "7.36.0", + "tag": "@microsoft/api-extractor_v7.36.0", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "minor": [ + { + "comment": "Use the `IRigConfig` interface in the `IExtractorConfigLoadForFolderOptions` object insteacd of the `RigConfig` class." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.4.0`" + } + ] + } + }, + { + "version": "7.35.4", + "tag": "@microsoft/api-extractor_v7.35.4", + "date": "Thu, 15 Jun 2023 00:21:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.27.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.3.21`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + } + ] + } + }, + { + "version": "7.35.3", + "tag": "@microsoft/api-extractor_v7.35.3", + "date": "Tue, 13 Jun 2023 01:49:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.15.0`" + } + ] + } + }, + { + "version": "7.35.2", + "tag": "@microsoft/api-extractor_v7.35.2", + "date": "Wed, 07 Jun 2023 22:45:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor-model\" to `7.27.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.3.20`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + } + ] + } + }, { "version": "7.35.1", "tag": "@microsoft/api-extractor_v7.35.1", diff --git a/apps/api-extractor/CHANGELOG.md b/apps/api-extractor/CHANGELOG.md index 8b7a8fa69fa..8317a197c3d 100644 --- a/apps/api-extractor/CHANGELOG.md +++ b/apps/api-extractor/CHANGELOG.md @@ -1,6 +1,772 @@ # Change Log - @microsoft/api-extractor -This log was last generated on Mon, 29 May 2023 15:21:15 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 7.58.12 +Tue, 21 Jul 2026 02:53:22 GMT + +### Patches + +- Improve the performance of the internal excerpt token condensing algorithm from O(n^2) to O(n) by eliminating repeated array splicing and per-merge bookkeeping. + +## 7.58.11 +Fri, 17 Jul 2026 00:15:59 GMT + +_Version update only_ + +## 7.58.10 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 7.58.9 +Sat, 13 Jun 2026 00:16:18 GMT + +_Version update only_ + +## 7.58.8 +Mon, 08 Jun 2026 15:15:49 GMT + +### Patches + +- Add support for new d.ts extension format when using TS moduleResolution 'bundler' or 'nodenext'. + +## 7.58.7 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 7.58.6 +Mon, 20 Apr 2026 15:15:24 GMT + +### Patches + +- Fix an issue where empty lines were included in DTS rollups in place of API items that were trimmed. + +## 7.58.5 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 7.58.4 +Sat, 18 Apr 2026 00:15:16 GMT + +### Patches + +- Bump semver. + +## 7.58.3 +Fri, 17 Apr 2026 15:14:57 GMT + +### Patches + +- Remove dependecy on `lodash`. + +## 7.58.2 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 7.58.1 +Sat, 04 Apr 2026 00:14:00 GMT + +### Patches + +- Bump lodash 4.18.1 to address CVEs GHSA-r5fr-rjxr-66jc, GHSA-f23m-r3pf-42rh + +## 7.58.0 +Wed, 01 Apr 2026 15:13:38 GMT + +### Minor changes + +- Upgrade the bundled compiler engine to TypeScript 5.9.3 + +## 7.57.8 +Tue, 31 Mar 2026 15:14:14 GMT + +_Version update only_ + +## 7.57.7 +Mon, 09 Mar 2026 15:14:07 GMT + +### Patches + +- Bump `minimatch` version from `10.2.1` to `10.2.3` to address CVE-2026-27903. + +## 7.57.6 +Wed, 25 Feb 2026 21:39:42 GMT + +### Patches + +- Bump `@microsoft/tsdoc-config` to `~0.18.1` to mitigate CVE-2025-69873. + +## 7.57.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 7.57.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 7.57.3 +Mon, 23 Feb 2026 00:42:21 GMT + +### Patches + +- Add missing "./extends/*.json" to the package.json "exports" field so that "@microsoft/api-extractor/extends/tsdoc-base.json" is importable. + +## 7.57.2 +Fri, 20 Feb 2026 16:14:49 GMT + +### Patches + +- Bump minimatch from 10.1.2 to 10.2.1 + +## 7.57.1 +Fri, 20 Feb 2026 00:15:03 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 7.57.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 7.56.3 +Sat, 07 Feb 2026 01:13:26 GMT + +### Patches + +- Upgrade `lodash` dependency from `~4.17.15` to `~4.17.23`. + +## 7.56.2 +Wed, 04 Feb 2026 20:42:47 GMT + +### Patches + +- Update minimatch dependency from 10.0.3 to 10.1.2 + +## 7.56.1 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 7.56.0 +Fri, 30 Jan 2026 01:16:12 GMT + +### Minor changes + +- Fix an issue where destructured parameters produced an incorrect parameter name + +## 7.55.5 +Thu, 08 Jan 2026 01:12:30 GMT + +### Patches + +- Fix missing 'export' keyword for namespace re-exports that produced invalid TypeScript output + +## 7.55.4 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 7.55.3 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 7.55.2 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 7.55.1 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 7.55.0 +Wed, 12 Nov 2025 01:12:56 GMT + +### Minor changes + +- Bump the `@microsoft/tsdoc` dependency to `~0.16.0`. +- Bump the `@microsoft/tsdoc-config` dependency to `~0.18.0`. + +## 7.54.0 +Tue, 04 Nov 2025 08:15:14 GMT + +### Minor changes + +- Add a new setting `IExtractorInvokeOptions.printApiReportDiff` that makes build logs easier to diagnose by printing a diff of any changes to API report files (*.api.md). +- Add a `--print-api-report-diff` CLI flag that causes a diff of any changes to API report files (*.api.md) to be printed. + +## 7.53.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 7.53.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 7.53.1 +Wed, 08 Oct 2025 00:13:28 GMT + +_Version update only_ + +## 7.53.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 7.52.15 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 7.52.14 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 7.52.13 +Fri, 12 Sep 2025 15:13:07 GMT + +### Patches + +- Fixes a bug in ExtractorAnalyzer._isExternalModulePath where type import declarations were not resolved. + +## 7.52.12 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 7.52.11 +Tue, 19 Aug 2025 20:45:02 GMT + +### Patches + +- Fix self-package import resolution by removing outDir/declarationDir from compiler options + +## 7.52.10 +Fri, 01 Aug 2025 00:12:48 GMT + +### Patches + +- Upgrades the minimatch dependency from ~3.0.3 to 10.0.3 across the entire Rush monorepo to address a Regular Expression Denial of Service (ReDoS) vulnerability in the underlying brace-expansion dependency. + +## 7.52.9 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 7.52.8 +Tue, 13 May 2025 02:09:20 GMT + +### Patches + +- Fixes API extractor error handling when changed APIs are encountered and the "--local" flag is not specified + +## 7.52.7 +Thu, 01 May 2025 15:11:33 GMT + +### Patches + +- Fix an issue where default exports were sometimes trimmed incorrectly in .api.md files when using `reportVariants` (GitHub #4775) + +## 7.52.6 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 7.52.5 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 7.52.4 +Thu, 17 Apr 2025 00:11:21 GMT + +### Patches + +- Update documentation for `extends` + +## 7.52.3 +Fri, 04 Apr 2025 18:34:35 GMT + +### Patches + +- Add support for customizing which TSDoc tags appear in API reports + +## 7.52.2 +Tue, 25 Mar 2025 15:11:15 GMT + +_Version update only_ + +## 7.52.1 +Tue, 11 Mar 2025 02:12:34 GMT + +_Version update only_ + +## 7.52.0 +Tue, 11 Mar 2025 00:11:25 GMT + +### Minor changes + +- Upgrade the bundled compiler engine to TypeScript 5.8.2 + +## 7.51.1 +Sat, 01 Mar 2025 05:00:09 GMT + +### Patches + +- Include triple-slash references marked with `preserve="true"` from files that only contain re-exports. There was a behavior change in TypeScript 5.5, where only triple-slash references that are explicitly marked with `preserve="true"` are emitted into declaration files. This change adds support for placing these references in files that only contain re-exports, like the API entrypoint file. + +## 7.51.0 +Thu, 27 Feb 2025 01:10:39 GMT + +### Minor changes + +- Add a `docModel.releaseTagsToTrim` property to `api-extractor.json` to specify which release tags should be trimmed when the doc model is produced. + +## 7.50.1 +Sat, 22 Feb 2025 01:11:11 GMT + +### Patches + +- Upgrade the bundled compiler engine to TypeScript 5.7.3 + +## 7.50.0 +Wed, 12 Feb 2025 01:10:52 GMT + +### Minor changes + +- Update merge behavior for derived configurations to allow overriding array properties + +## 7.49.2 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 7.49.1 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 7.49.0 +Tue, 07 Jan 2025 22:17:32 GMT + +### Minor changes + +- Upgrade the bundled compiler engine to TypeScript 5.7.2 + +## 7.48.1 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 7.48.0 +Sat, 23 Nov 2024 01:18:55 GMT + +### Minor changes + +- Update TSDoc dependencies. + +## 7.47.12 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 7.47.11 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 7.47.10 +Tue, 15 Oct 2024 00:12:31 GMT + +### Patches + +- Fix a compatibility issue with usage of `getModeForUsageLocation` in TypeScript 5.6 + +## 7.47.9 +Fri, 13 Sep 2024 00:11:42 GMT + +_Version update only_ + +## 7.47.8 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 7.47.7 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 7.47.6 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 7.47.5 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 7.47.4 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 7.47.3 +Wed, 24 Jul 2024 00:12:14 GMT + +### Patches + +- Fix an edge case when discarding the file extension from the "reportFileName" setting and improve its documentation + +## 7.47.2 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 7.47.1 +Tue, 16 Jul 2024 00:36:22 GMT + +_Version update only_ + +## 7.47.0 +Mon, 03 Jun 2024 23:43:15 GMT + +### Minor changes + +- Add support for re-exporting modules using syntax such as `export * as ns from './file'` (GitHub #2780) + +## 7.46.2 +Thu, 30 May 2024 00:13:05 GMT + +### Patches + +- Include missing `type` modifiers on type-only exports. + +## 7.46.1 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 7.46.0 +Wed, 29 May 2024 00:10:52 GMT + +### Minor changes + +- Bump TSDoc dependencies. + +## 7.45.1 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 7.45.0 +Tue, 28 May 2024 00:09:47 GMT + +### Minor changes + +- Improve support for resolving the `tsdoc-metadata.json` to include the folder referenced by a `types` field in an `"exports"` field and an `"typesVersions"` field in addition to `"types"`, `"typings"`, and `"tsdocMetadata"` fields. + +## 7.44.1 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 7.44.0 +Fri, 24 May 2024 00:15:08 GMT + +### Minor changes + +- Add support for "variants" of API reports which include or exclude items by release tag + +## 7.43.8 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 7.43.7 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 7.43.6 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 7.43.5 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 7.43.4 +Fri, 10 May 2024 05:33:33 GMT + +_Version update only_ + +## 7.43.3 +Wed, 08 May 2024 22:23:50 GMT + +_Version update only_ + +## 7.43.2 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 7.43.1 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 7.43.0 +Tue, 19 Mar 2024 15:10:18 GMT + +### Minor changes + +- Upgrade the bundled compiler engine to TypeScript 5.4.2 + +## 7.42.3 +Sun, 03 Mar 2024 20:58:12 GMT + +_Version update only_ + +## 7.42.2 +Sat, 02 Mar 2024 02:22:23 GMT + +_Version update only_ + +## 7.42.1 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 7.42.0 +Thu, 29 Feb 2024 07:11:45 GMT + +### Minor changes + +- Add glob support in `bundledPackages` + +### Patches + +- Don't mark items documented with {@inheritDoc} references to package-external items as "undocumented" + +## 7.41.1 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 7.41.0 +Sat, 24 Feb 2024 23:02:51 GMT + +### Minor changes + +- Replace const enums with conventional enums to allow for compatibility with JavaScript consumers. + +## 7.40.6 +Wed, 21 Feb 2024 21:45:28 GMT + +### Patches + +- Replace the dependency on the `colors` package with `Colorize` from `@rushstack/terminal`. + +## 7.40.5 +Wed, 21 Feb 2024 08:55:47 GMT + +### Patches + +- Fix an issue where imports were trimmed from external packages based when generating .d.ts rollups + +## 7.40.4 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 7.40.3 +Mon, 19 Feb 2024 21:54:27 GMT + +_Version update only_ + +## 7.40.2 +Sat, 17 Feb 2024 06:24:34 GMT + +### Patches + +- Fix broken link to API documentation + +## 7.40.1 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 7.40.0 +Wed, 07 Feb 2024 01:11:18 GMT + +### Minor changes + +- Classify arrow functions as `function` kind in the doc model export. + +## 7.39.5 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 7.39.4 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 7.39.3 +Tue, 23 Jan 2024 20:12:57 GMT + +_Version update only_ + +## 7.39.2 +Tue, 23 Jan 2024 16:15:05 GMT + +_Version update only_ + +## 7.39.1 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 7.39.0 +Wed, 20 Dec 2023 01:09:45 GMT + +### Minor changes + +- Update API Extractor to support TypeScript 5.3.3 + +## 7.38.5 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 7.38.4 +Tue, 05 Dec 2023 01:10:16 GMT + +### Patches + +- Don't export trimmed namespace members during rollup (#2791) + +## 7.38.3 +Fri, 10 Nov 2023 18:02:04 GMT + +### Patches + +- Fix an issue where "ae-undocumented" was incorrectly reported for private members + +## 7.38.2 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 7.38.1 +Mon, 30 Oct 2023 23:36:38 GMT + +_Version update only_ + +## 7.38.0 +Sun, 01 Oct 2023 02:56:29 GMT + +### Minor changes + +- Add a new message "ae-undocumented" to support logging of undocumented API items + +## 7.37.3 +Sat, 30 Sep 2023 00:20:51 GMT + +### Patches + +- Don't strip out @alpha items when generating API reports. + +## 7.37.2 +Thu, 28 Sep 2023 20:53:16 GMT + +_Version update only_ + +## 7.37.1 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 7.37.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 7.36.4 +Tue, 08 Aug 2023 07:10:39 GMT + +_Version update only_ + +## 7.36.3 +Wed, 19 Jul 2023 00:20:31 GMT + +### Patches + +- Updated semver dependency + +## 7.36.2 +Wed, 12 Jul 2023 15:20:39 GMT + +### Patches + +- Add api-extractor support for .d.mts and .d.cts files + +## 7.36.1 +Thu, 06 Jul 2023 00:16:19 GMT + +_Version update only_ + +## 7.36.0 +Mon, 19 Jun 2023 22:40:21 GMT + +### Minor changes + +- Use the `IRigConfig` interface in the `IExtractorConfigLoadForFolderOptions` object insteacd of the `RigConfig` class. + +## 7.35.4 +Thu, 15 Jun 2023 00:21:01 GMT + +_Version update only_ + +## 7.35.3 +Tue, 13 Jun 2023 01:49:01 GMT + +_Version update only_ + +## 7.35.2 +Wed, 07 Jun 2023 22:45:16 GMT + +_Version update only_ ## 7.35.1 Mon, 29 May 2023 15:21:15 GMT diff --git a/apps/api-extractor/README.md b/apps/api-extractor/README.md index 1f3ebfa7c14..69c1846fc1d 100644 --- a/apps/api-extractor/README.md +++ b/apps/api-extractor/README.md @@ -46,6 +46,6 @@ For more details and support resources, please visit: https://api-extractor.com/ - [CHANGELOG.md]( https://github.com/microsoft/rushstack/blob/main/apps/api-extractor/CHANGELOG.md) - Find out what's new in the latest version -- [API Reference](https://rushstack.io/pages/api/api-extractor/) +- [API Reference](https://api.rushstack.io/pages/api-extractor/) API Extractor is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/apps/api-extractor/bin/api-extractor b/apps/api-extractor/bin/api-extractor index 783bb806fce..eef2fc27066 100755 --- a/apps/api-extractor/bin/api-extractor +++ b/apps/api-extractor/bin/api-extractor @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('../lib/start.js') +require('../lib-commonjs/start.js'); diff --git a/apps/api-extractor/build-tests.cmd b/apps/api-extractor/build-tests.cmd index f2ae7a2cc93..cc33d4a4554 100644 --- a/apps/api-extractor/build-tests.cmd +++ b/apps/api-extractor/build-tests.cmd @@ -1,3 +1,3 @@ @ECHO OFF @SETLOCAL -rush test -t api-extractor-lib1-test -t api-extractor-lib2-test -t api-extractor-lib3-test -t api-extractor-scenarios -t api-extractor-test-01 -t api-extractor-test-02 -t api-extractor-test-03 -t api-extractor-test-04 -t api-documenter-test +rush test -t tag:api-extractor-tests diff --git a/apps/api-extractor/config/api-extractor.json b/apps/api-extractor/config/api-extractor.json index aa9d8f810fd..bf6aa143c60 100644 --- a/apps/api-extractor/config/api-extractor.json +++ b/apps/api-extractor/config/api-extractor.json @@ -1,17 +1,6 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - - "mainEntryPointFilePath": "/lib/index.d.ts", - - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, - - "docModel": { - "enabled": true, - "apiJsonFilePath": "../../../common/temp/api/.api.json" - }, + "extends": "decoupled-local-node-rig/profiles/default/config/api-extractor-base.json", "dtsRollup": { "enabled": true, diff --git a/apps/api-extractor/config/heft.json b/apps/api-extractor/config/heft.json new file mode 100644 index 00000000000..f3b430106df --- /dev/null +++ b/apps/api-extractor/config/heft.json @@ -0,0 +1,53 @@ +/** + * Defines configuration used by core Heft. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + /** + * Optionally specifies another JSON config file that this file extends from. This provides a way for standard + * settings to be shared across multiple projects. + * + * To delete an inherited setting, set it to `null` in this file. + */ + "extends": "decoupled-local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "perform-copy": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src", + "destinationFolders": ["lib-commonjs"], + "includeGlobs": ["**/test/test-data/**/*"] + } + ] + } + } + }, + + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/api-extractor/v7"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/apps/api-extractor/config/jest.config.json b/apps/api-extractor/config/jest.config.json index 4bb17bde3ee..7c0f9ccc9d6 100644 --- a/apps/api-extractor/config/jest.config.json +++ b/apps/api-extractor/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "decoupled-local-node-rig/profiles/default/config/jest.config.json" } diff --git a/apps/api-extractor/config/rig.json b/apps/api-extractor/config/rig.json index 6ac88a96368..cc98dea43dd 100644 --- a/apps/api-extractor/config/rig.json +++ b/apps/api-extractor/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "decoupled-local-node-rig" } diff --git a/apps/api-extractor/eslint.config.js b/apps/api-extractor/eslint.config.js new file mode 100644 index 00000000000..2b99226b7c4 --- /dev/null +++ b/apps/api-extractor/eslint.config.js @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + }, + rules: { + 'no-console': 'off' + } + } +]; diff --git a/apps/api-extractor/package.json b/apps/api-extractor/package.json index 00f6ceb9815..8e14a294030 100644 --- a/apps/api-extractor/package.json +++ b/apps/api-extractor/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/api-extractor", - "version": "7.35.1", + "version": "7.58.12", "description": "Analyze the exported API for a TypeScript library and generate reviews, documentation, and .d.ts rollups", "keywords": [ "typescript", @@ -25,39 +25,67 @@ "directory": "apps/api-extractor" }, "homepage": "https://api-extractor.com", - "main": "lib/index.js", - "typings": "dist/rollup.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/rollup.d.ts", + "exports": { + ".": { + "types": "./dist/rollup.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./extends/*.json": "./extends/*.json", + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "bin": { "api-extractor": "./bin/api-extractor" }, "license": "MIT", "scripts": { "build": "heft build --clean", - "_phase:build": "heft build --clean", - "_phase:test": "heft test --no-build" + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" }, "dependencies": { "@microsoft/api-extractor-model": "workspace:*", - "@microsoft/tsdoc": "0.14.2", - "@microsoft/tsdoc-config": "~0.16.1", + "@microsoft/tsdoc-config": "~0.18.1", + "@microsoft/tsdoc": "~0.16.0", "@rushstack/node-core-library": "workspace:*", "@rushstack/rig-package": "workspace:*", + "@rushstack/terminal": "workspace:*", "@rushstack/ts-command-line": "workspace:*", - "colors": "~1.2.1", - "lodash": "~4.17.15", + "diff": "~8.0.2", + "minimatch": "10.2.3", "resolve": "~1.22.1", - "semver": "~7.3.0", + "semver": "~7.7.4", "source-map": "~0.6.1", - "typescript": "~5.0.4" + "typescript": "5.9.3" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft": "0.50.6", - "@rushstack/heft-node-rig": "1.13.0", - "@types/heft-jest": "1.0.1", - "@types/lodash": "4.14.116", - "@types/node": "14.18.36", + "@rushstack/heft": "1.2.22", "@types/resolve": "1.20.2", - "@types/semver": "7.3.5" - } + "@types/semver": "7.7.1", + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*" + }, + "sideEffects": [ + "lib-commonjs/start.js", + "lib-esm/start.js" + ] } diff --git a/apps/api-extractor/src/aedoc/PackageDocComment.ts b/apps/api-extractor/src/aedoc/PackageDocComment.ts index ea0ea4d9def..3cc10e34526 100644 --- a/apps/api-extractor/src/aedoc/PackageDocComment.ts +++ b/apps/api-extractor/src/aedoc/PackageDocComment.ts @@ -2,7 +2,8 @@ // See LICENSE in the project root for license information. import * as ts from 'typescript'; -import { Collector } from '../collector/Collector'; + +import type { Collector } from '../collector/Collector'; import { ExtractorMessageId } from '../api/ExtractorMessageId'; export class PackageDocComment { diff --git a/apps/api-extractor/src/analyzer/AstDeclaration.ts b/apps/api-extractor/src/analyzer/AstDeclaration.ts index 02540312168..78cc4ed7337 100644 --- a/apps/api-extractor/src/analyzer/AstDeclaration.ts +++ b/apps/api-extractor/src/analyzer/AstDeclaration.ts @@ -2,10 +2,12 @@ // See LICENSE in the project root for license information. import * as ts from 'typescript'; -import { AstSymbol } from './AstSymbol'; -import { Span } from './Span'; + import { InternalError } from '@rushstack/node-core-library'; -import { AstEntity } from './AstEntity'; + +import type { AstSymbol } from './AstSymbol'; +import { Span } from './Span'; +import type { AstEntity } from './AstEntity'; /** * Constructor options for AstDeclaration diff --git a/apps/api-extractor/src/analyzer/AstImport.ts b/apps/api-extractor/src/analyzer/AstImport.ts index 3a39a8ff7e6..38d4b1928a9 100644 --- a/apps/api-extractor/src/analyzer/AstImport.ts +++ b/apps/api-extractor/src/analyzer/AstImport.ts @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { AstSymbol } from './AstSymbol'; import { InternalError } from '@rushstack/node-core-library'; + +import type { AstSymbol } from './AstSymbol'; import { AstSyntheticEntity } from './AstEntity'; /** @@ -154,8 +155,8 @@ export class AstImport extends AstSyntheticEntity { const subKey: string = !options.exportName ? '*' // Equivalent to StarImport : options.exportName.includes('.') // Equivalent to a named export - ? options.exportName.split('.')[0] - : options.exportName; + ? options.exportName.split('.')[0] + : options.exportName; return `${options.modulePath}:${subKey}`; } default: diff --git a/apps/api-extractor/src/analyzer/AstModule.ts b/apps/api-extractor/src/analyzer/AstModule.ts index 264d06d5f19..1ae3c2e91a3 100644 --- a/apps/api-extractor/src/analyzer/AstModule.ts +++ b/apps/api-extractor/src/analyzer/AstModule.ts @@ -1,17 +1,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as ts from 'typescript'; +import type * as ts from 'typescript'; -import { AstSymbol } from './AstSymbol'; -import { AstEntity } from './AstEntity'; +import type { AstSymbol } from './AstSymbol'; +import type { AstEntity } from './AstEntity'; /** * Represents information collected by {@link AstSymbolTable.fetchAstModuleExportInfo} */ -export class AstModuleExportInfo { - public readonly exportedLocalEntities: Map = new Map(); - public readonly starExportedExternalModules: Set = new Set(); +export interface IAstModuleExportInfo { + readonly visitedAstModules: Set; + readonly exportedLocalEntities: Map; + readonly starExportedExternalModules: Set; } /** @@ -64,7 +65,7 @@ export class AstModule { /** * Additional state calculated by `AstSymbolTable.fetchWorkingPackageModule()`. */ - public astModuleExportInfo: AstModuleExportInfo | undefined; + public astModuleExportInfo: IAstModuleExportInfo | undefined; public constructor(options: IAstModuleOptions) { this.sourceFile = options.sourceFile; diff --git a/apps/api-extractor/src/analyzer/AstNamespaceExport.ts b/apps/api-extractor/src/analyzer/AstNamespaceExport.ts new file mode 100644 index 00000000000..f339bf6dbbb --- /dev/null +++ b/apps/api-extractor/src/analyzer/AstNamespaceExport.ts @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { AstNamespaceImport, type IAstNamespaceImportOptions } from './AstNamespaceImport'; + +export interface IAstNamespaceExportOptions extends IAstNamespaceImportOptions {} + +/** + * `AstNamespaceExport` represents a namespace that is created implicitly and exported by a statement + * such as `export * as example from "./file";` + * + * @remarks + * + * A typical input looks like this: + * ```ts + * // Suppose that example.ts exports two functions f1() and f2(). + * export * as example from "./file"; + * ``` + * + * API Extractor's .d.ts rollup will transform it into an explicit namespace, like this: + * ```ts + * declare f1(): void; + * declare f2(): void; + * + * export declare namespace example { + * export { + * f1, + * f2 + * } + * } + * ``` + * + * The current implementation does not attempt to relocate f1()/f2() to be inside the `namespace` + * because other type signatures may reference them directly (without using the namespace qualifier). + * The AstNamespaceExports behaves the same as AstNamespaceImport, it just also has the inline export for the craeted namespace. + */ + +export class AstNamespaceExport extends AstNamespaceImport { + public constructor(options: IAstNamespaceExportOptions) { + super(options); + } +} diff --git a/apps/api-extractor/src/analyzer/AstNamespaceImport.ts b/apps/api-extractor/src/analyzer/AstNamespaceImport.ts index acb4bd4c578..09304b9efcb 100644 --- a/apps/api-extractor/src/analyzer/AstNamespaceImport.ts +++ b/apps/api-extractor/src/analyzer/AstNamespaceImport.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as ts from 'typescript'; +import type * as ts from 'typescript'; -import { AstModule, AstModuleExportInfo } from './AstModule'; +import type { AstModule, IAstModuleExportInfo } from './AstModule'; import { AstSyntheticEntity } from './AstEntity'; -import { Collector } from '../collector/Collector'; +import type { Collector } from '../collector/Collector'; export interface IAstNamespaceImportOptions { readonly astModule: AstModule; @@ -87,8 +87,8 @@ export class AstNamespaceImport extends AstSyntheticEntity { return this.namespaceName; } - public fetchAstModuleExportInfo(collector: Collector): AstModuleExportInfo { - const astModuleExportInfo: AstModuleExportInfo = collector.astSymbolTable.fetchAstModuleExportInfo( + public fetchAstModuleExportInfo(collector: Collector): IAstModuleExportInfo { + const astModuleExportInfo: IAstModuleExportInfo = collector.astSymbolTable.fetchAstModuleExportInfo( this.astModule ); return astModuleExportInfo; diff --git a/apps/api-extractor/src/analyzer/AstReferenceResolver.ts b/apps/api-extractor/src/analyzer/AstReferenceResolver.ts index 6d91f6c194f..e51de2f1934 100644 --- a/apps/api-extractor/src/analyzer/AstReferenceResolver.ts +++ b/apps/api-extractor/src/analyzer/AstReferenceResolver.ts @@ -2,15 +2,16 @@ // See LICENSE in the project root for license information. import * as ts from 'typescript'; + import * as tsdoc from '@microsoft/tsdoc'; -import { AstSymbolTable } from './AstSymbolTable'; -import { AstEntity } from './AstEntity'; -import { AstDeclaration } from './AstDeclaration'; -import { WorkingPackage } from '../collector/WorkingPackage'; -import { AstModule } from './AstModule'; -import { Collector } from '../collector/Collector'; -import { DeclarationMetadata } from '../collector/DeclarationMetadata'; +import type { AstSymbolTable } from './AstSymbolTable'; +import type { AstEntity } from './AstEntity'; +import type { AstDeclaration } from './AstDeclaration'; +import type { WorkingPackage } from '../collector/WorkingPackage'; +import type { AstModule } from './AstModule'; +import type { Collector } from '../collector/Collector'; +import type { DeclarationMetadata } from '../collector/DeclarationMetadata'; import { AstSymbol } from './AstSymbol'; /** @@ -245,7 +246,7 @@ export class AstReferenceResolver { memberSelector: tsdoc.DocMemberSelector, astSymbolName: string ): AstDeclaration | ResolverFailure { - const selectorOverloadIndex: number = parseInt(memberSelector.selector); + const selectorOverloadIndex: number = parseInt(memberSelector.selector, 10); const matches: AstDeclaration[] = []; for (const astDeclaration of astDeclarations) { diff --git a/apps/api-extractor/src/analyzer/AstSymbol.ts b/apps/api-extractor/src/analyzer/AstSymbol.ts index fcb3269cf36..fb34e28efc2 100644 --- a/apps/api-extractor/src/analyzer/AstSymbol.ts +++ b/apps/api-extractor/src/analyzer/AstSymbol.ts @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as ts from 'typescript'; -import { AstDeclaration } from './AstDeclaration'; +import type * as ts from 'typescript'; + import { InternalError } from '@rushstack/node-core-library'; + +import type { AstDeclaration } from './AstDeclaration'; import { AstEntity } from './AstEntity'; /** @@ -115,12 +117,20 @@ export class AstSymbol extends AstEntity { public constructor(options: IAstSymbolOptions) { super(); - this.followedSymbol = options.followedSymbol; - this.localName = options.localName; - this.isExternal = options.isExternal; - this.nominalAnalysis = options.nominalAnalysis; - this.parentAstSymbol = options.parentAstSymbol; - this.rootAstSymbol = options.rootAstSymbol || this; + const { + followedSymbol, + localName, + isExternal, + nominalAnalysis, + parentAstSymbol, + rootAstSymbol = this + } = options; + this.followedSymbol = followedSymbol; + this.localName = localName; + this.isExternal = isExternal; + this.nominalAnalysis = nominalAnalysis; + this.parentAstSymbol = parentAstSymbol; + this.rootAstSymbol = rootAstSymbol; this._astDeclarations = []; } diff --git a/apps/api-extractor/src/analyzer/AstSymbolTable.ts b/apps/api-extractor/src/analyzer/AstSymbolTable.ts index cee070753dc..1bfa982b578 100644 --- a/apps/api-extractor/src/analyzer/AstSymbolTable.ts +++ b/apps/api-extractor/src/analyzer/AstSymbolTable.ts @@ -4,18 +4,19 @@ /* eslint-disable no-bitwise */ // for ts.SymbolFlags import * as ts from 'typescript'; -import { PackageJsonLookup, InternalError } from '@rushstack/node-core-library'; + +import { type PackageJsonLookup, InternalError } from '@rushstack/node-core-library'; import { AstDeclaration } from './AstDeclaration'; import { TypeScriptHelpers } from './TypeScriptHelpers'; import { AstSymbol } from './AstSymbol'; -import { AstModule, AstModuleExportInfo } from './AstModule'; +import type { AstModule, IAstModuleExportInfo } from './AstModule'; import { PackageMetadataManager } from './PackageMetadataManager'; import { ExportAnalyzer } from './ExportAnalyzer'; -import { AstEntity } from './AstEntity'; +import type { AstEntity } from './AstEntity'; import { AstNamespaceImport } from './AstNamespaceImport'; -import { MessageRouter } from '../collector/MessageRouter'; -import { TypeScriptInternals, IGlobalVariableAnalyzer } from './TypeScriptInternals'; +import type { MessageRouter } from '../collector/MessageRouter'; +import { TypeScriptInternals, type IGlobalVariableAnalyzer } from './TypeScriptInternals'; import { SyntaxHelpers } from './SyntaxHelpers'; import { SourceFileLocationFormatter } from './SourceFileLocationFormatter'; @@ -124,7 +125,7 @@ export class AstSymbolTable { /** * This crawls the specified entry point and collects the full set of exported AstSymbols. */ - public fetchAstModuleExportInfo(astModule: AstModule): AstModuleExportInfo { + public fetchAstModuleExportInfo(astModule: AstModule): IAstModuleExportInfo { return this._exportAnalyzer.fetchAstModuleExportInfo(astModule); } @@ -615,9 +616,6 @@ export class AstSymbolTable { // - but P1 and P2 may be different (e.g. merged namespaces containing merged interfaces) // Is there a parent AstSymbol? First we check to see if there is a parent declaration: - const arbitraryDeclaration: ts.Node | undefined = - TypeScriptHelpers.tryGetADeclaration(followedSymbol); - if (arbitraryDeclaration) { const arbitraryParentDeclaration: ts.Node | undefined = this._tryFindFirstAstDeclarationParent(arbitraryDeclaration); diff --git a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts index 3610f828c07..6870d3a1954 100644 --- a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts +++ b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts @@ -2,18 +2,20 @@ // See LICENSE in the project root for license information. import * as ts from 'typescript'; + import { InternalError } from '@rushstack/node-core-library'; import { TypeScriptHelpers } from './TypeScriptHelpers'; import { AstSymbol } from './AstSymbol'; -import { AstImport, IAstImportOptions, AstImportKind } from './AstImport'; -import { AstModule, AstModuleExportInfo } from './AstModule'; +import { AstImport, type IAstImportOptions, AstImportKind } from './AstImport'; +import { AstModule, type IAstModuleExportInfo } from './AstModule'; import { TypeScriptInternals } from './TypeScriptInternals'; import { SourceFileLocationFormatter } from './SourceFileLocationFormatter'; -import { IFetchAstSymbolOptions } from './AstSymbolTable'; -import { AstEntity } from './AstEntity'; +import type { IFetchAstSymbolOptions } from './AstSymbolTable'; +import type { AstEntity } from './AstEntity'; import { AstNamespaceImport } from './AstNamespaceImport'; import { SyntaxHelpers } from './SyntaxHelpers'; +import { AstNamespaceExport } from './AstNamespaceExport'; /** * Exposes the minimal APIs from AstSymbolTable that are needed by ExportAnalyzer. @@ -236,15 +238,19 @@ export class ExportAnalyzer { /** * Implementation of {@link AstSymbolTable.fetchAstModuleExportInfo}. */ - public fetchAstModuleExportInfo(entryPointAstModule: AstModule): AstModuleExportInfo { + public fetchAstModuleExportInfo(entryPointAstModule: AstModule): IAstModuleExportInfo { if (entryPointAstModule.isExternal) { throw new Error('fetchAstModuleExportInfo() is not supported for external modules'); } if (entryPointAstModule.astModuleExportInfo === undefined) { - const astModuleExportInfo: AstModuleExportInfo = new AstModuleExportInfo(); + const astModuleExportInfo: IAstModuleExportInfo = { + visitedAstModules: new Set(), + exportedLocalEntities: new Map(), + starExportedExternalModules: new Set() + }; - this._collectAllExportsRecursive(astModuleExportInfo, entryPointAstModule, new Set()); + this._collectAllExportsRecursive(astModuleExportInfo, entryPointAstModule); entryPointAstModule.astModuleExportInfo = astModuleExportInfo; } @@ -259,15 +265,23 @@ export class ExportAnalyzer { importOrExportDeclaration: ts.ImportDeclaration | ts.ExportDeclaration | ts.ImportTypeNode, moduleSpecifier: string ): boolean { - const specifier: ts.TypeNode | ts.Expression | undefined = ts.isImportTypeNode(importOrExportDeclaration) + let specifier: ts.TypeNode | ts.Expression | undefined = ts.isImportTypeNode(importOrExportDeclaration) ? importOrExportDeclaration.argument : importOrExportDeclaration.moduleSpecifier; + if (specifier && ts.isLiteralTypeNode(specifier)) { + specifier = specifier.literal; + } const mode: ts.ModuleKind.CommonJS | ts.ModuleKind.ESNext | undefined = specifier && ts.isStringLiteralLike(specifier) - ? TypeScriptInternals.getModeForUsageLocation(importOrExportDeclaration.getSourceFile(), specifier) + ? TypeScriptInternals.getModeForUsageLocation( + importOrExportDeclaration.getSourceFile(), + specifier, + this._program.getCompilerOptions() + ) : undefined; const resolvedModule: ts.ResolvedModuleFull | undefined = TypeScriptInternals.getResolvedModule( + this._program, importOrExportDeclaration.getSourceFile(), moduleSpecifier, mode @@ -308,18 +322,15 @@ export class ExportAnalyzer { return this._importableAmbientSourceFiles.has(sourceFile); } - private _collectAllExportsRecursive( - astModuleExportInfo: AstModuleExportInfo, - astModule: AstModule, - visitedAstModules: Set - ): void { + private _collectAllExportsRecursive(astModuleExportInfo: IAstModuleExportInfo, astModule: AstModule): void { + const { visitedAstModules, starExportedExternalModules, exportedLocalEntities } = astModuleExportInfo; if (visitedAstModules.has(astModule)) { return; } visitedAstModules.add(astModule); if (astModule.isExternal) { - astModuleExportInfo.starExportedExternalModules.add(astModule); + starExportedExternalModules.add(astModule); } else { // Fetch each of the explicit exports for this module if (astModule.moduleSymbol.exports) { @@ -331,7 +342,7 @@ export class ExportAnalyzer { default: // Don't collect the "export default" symbol unless this is the entry point module if (exportName !== ts.InternalSymbolName.Default || visitedAstModules.size === 1) { - if (!astModuleExportInfo.exportedLocalEntities.has(exportSymbol.name)) { + if (!exportedLocalEntities.has(exportSymbol.name)) { const astEntity: AstEntity = this._getExportOfAstModule(exportSymbol.name, astModule); if (astEntity instanceof AstSymbol && !astEntity.isExternal) { @@ -342,7 +353,7 @@ export class ExportAnalyzer { this._astSymbolTable.analyze(astEntity); } - astModuleExportInfo.exportedLocalEntities.set(exportSymbol.name, astEntity); + exportedLocalEntities.set(exportSymbol.name, astEntity); } } break; @@ -351,7 +362,7 @@ export class ExportAnalyzer { } for (const starExportedModule of astModule.starExportedModules) { - this._collectAllExportsRecursive(astModuleExportInfo, starExportedModule, visitedAstModules); + this._collectAllExportsRecursive(astModuleExportInfo, starExportedModule); } } } @@ -561,11 +572,9 @@ export class ExportAnalyzer { // SemicolonToken: pre=[;] // Issue tracking this feature: https://github.com/microsoft/rushstack/issues/2780 - throw new Error( - `The "export * as ___" syntax is not supported yet; as a workaround,` + - ` use "import * as ___" with a separate "export { ___ }" declaration\n` + - SourceFileLocationFormatter.formatDeclaration(declaration) - ); + + const astModule: AstModule = this._fetchSpecifierAstModule(exportDeclaration, declarationSymbol); + return this._getAstNamespaceExport(astModule, declarationSymbol, declaration); } else { throw new InternalError( `Unimplemented export declaration kind: ${declaration.getText()}\n` + @@ -593,6 +602,25 @@ export class ExportAnalyzer { return undefined; } + private _getAstNamespaceExport( + astModule: AstModule, + declarationSymbol: ts.Symbol, + declaration: ts.Declaration + ): AstNamespaceExport { + const imoprtNamespace: AstNamespaceImport = this._getAstNamespaceImport( + astModule, + declarationSymbol, + declaration + ); + + return new AstNamespaceExport({ + namespaceName: imoprtNamespace.localName, + astModule: astModule, + declaration, + symbol: declarationSymbol + }); + } + private _tryMatchImportDeclaration( declaration: ts.Declaration, declarationSymbol: ts.Symbol @@ -620,18 +648,7 @@ export class ExportAnalyzer { if (externalModulePath === undefined) { const astModule: AstModule = this._fetchSpecifierAstModule(importDeclaration, declarationSymbol); - let namespaceImport: AstNamespaceImport | undefined = - this._astNamespaceImportByModule.get(astModule); - if (namespaceImport === undefined) { - namespaceImport = new AstNamespaceImport({ - namespaceName: declarationSymbol.name, - astModule: astModule, - declaration: declaration, - symbol: declarationSymbol - }); - this._astNamespaceImportByModule.set(astModule, namespaceImport); - } - return namespaceImport; + return this._getAstNamespaceImport(astModule, declarationSymbol, declaration); } // Here importSymbol=undefined because {@inheritDoc} and such are not going to work correctly for @@ -640,7 +657,7 @@ export class ExportAnalyzer { importKind: AstImportKind.StarImport, exportName: declarationSymbol.name, modulePath: externalModulePath, - isTypeOnly: ExportAnalyzer._getIsTypeOnly(importDeclaration) + isTypeOnly: _getIsTypeOnly(importDeclaration) }); } @@ -673,7 +690,7 @@ export class ExportAnalyzer { importKind: AstImportKind.NamedImport, modulePath: externalModulePath, exportName: exportName, - isTypeOnly: ExportAnalyzer._getIsTypeOnly(importDeclaration) + isTypeOnly: _getIsTypeOnly(importDeclaration) }); } @@ -707,7 +724,7 @@ export class ExportAnalyzer { importKind: AstImportKind.DefaultImport, modulePath: externalModulePath, exportName, - isTypeOnly: ExportAnalyzer._getIsTypeOnly(importDeclaration) + isTypeOnly: _getIsTypeOnly(importDeclaration) }); } @@ -758,11 +775,23 @@ export class ExportAnalyzer { return undefined; } - private static _getIsTypeOnly(importDeclaration: ts.ImportDeclaration): boolean { - if (importDeclaration.importClause) { - return !!importDeclaration.importClause.isTypeOnly; + private _getAstNamespaceImport( + astModule: AstModule, + declarationSymbol: ts.Symbol, + declaration: ts.Declaration + ): AstNamespaceImport { + let namespaceImport: AstNamespaceImport | undefined = this._astNamespaceImportByModule.get(astModule); + if (namespaceImport === undefined) { + namespaceImport = new AstNamespaceImport({ + namespaceName: declarationSymbol.name, + astModule: astModule, + declaration: declaration, + symbol: declarationSymbol + }); + this._astNamespaceImportByModule.set(astModule, namespaceImport); } - return false; + + return namespaceImport; } private _getExportOfSpecifierAstModule( @@ -878,10 +907,12 @@ export class ExportAnalyzer { ts.isStringLiteralLike(importOrExportDeclaration.moduleSpecifier) ? TypeScriptInternals.getModeForUsageLocation( importOrExportDeclaration.getSourceFile(), - importOrExportDeclaration.moduleSpecifier + importOrExportDeclaration.moduleSpecifier, + this._program.getCompilerOptions() ) : undefined; const resolvedModule: ts.ResolvedModuleFull | undefined = TypeScriptInternals.getResolvedModule( + this._program, importOrExportDeclaration.getSourceFile(), moduleSpecifier, mode @@ -974,3 +1005,10 @@ export class ExportAnalyzer { return moduleSpecifier; } } + +function _getIsTypeOnly(importDeclaration: ts.ImportDeclaration): boolean { + if (importDeclaration.importClause) { + return !!importDeclaration.importClause.isTypeOnly; + } + return false; +} diff --git a/apps/api-extractor/src/analyzer/PackageMetadataManager.ts b/apps/api-extractor/src/analyzer/PackageMetadataManager.ts index 3a3e031296d..a007664d3c4 100644 --- a/apps/api-extractor/src/analyzer/PackageMetadataManager.ts +++ b/apps/api-extractor/src/analyzer/PackageMetadataManager.ts @@ -1,18 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import path from 'node:path'; + +import semver from 'semver'; import { - PackageJsonLookup, + type PackageJsonLookup, FileSystem, JsonFile, - NewlineKind, - INodePackageJson, - JsonObject + type NewlineKind, + type INodePackageJson, + type JsonObject, + type IPackageJsonExports } from '@rushstack/node-core-library'; + import { Extractor } from '../api/Extractor'; -import { MessageRouter } from '../collector/MessageRouter'; +import type { MessageRouter } from '../collector/MessageRouter'; import { ConsoleMessageId } from '../api/ConsoleMessageId'; /** @@ -42,6 +46,161 @@ export class PackageMetadata { } } +const TSDOC_METADATA_FILENAME: 'tsdoc-metadata.json' = 'tsdoc-metadata.json'; + +/** + * 1. If package.json a `"tsdocMetadata": "./path1/path2/tsdoc-metadata.json"` field + * then that takes precedence. This convention will be rarely needed, since the other rules below generally + * produce a good result. + */ +function _tryResolveTsdocMetadataFromTsdocMetadataField({ + tsdocMetadata +}: INodePackageJson): string | undefined { + return tsdocMetadata; +} + +/** + * 2. If package.json contains a `"exports": { ".": { "types": "./path1/path2/index.d.ts" } }` field, + * then we look for the file under "./path1/path2/tsdoc-metadata.json" + * + * This always looks for a "." and then a "*" entry in the exports field, and then evaluates for + * a "types" field in that entry. + */ + +function _tryResolveTsdocMetadataFromExportsField({ exports }: INodePackageJson): string | undefined { + switch (typeof exports) { + case 'string': { + return `${path.dirname(exports)}/${TSDOC_METADATA_FILENAME}`; + } + + case 'object': { + if (Array.isArray(exports)) { + const [firstExport] = exports; + // Take the first entry in the array + if (firstExport) { + return `${path.dirname(exports[0])}/${TSDOC_METADATA_FILENAME}`; + } + } else { + const rootExport: IPackageJsonExports | string | null | undefined = exports['.'] ?? exports['*']; + switch (typeof rootExport) { + case 'string': { + return `${path.dirname(rootExport)}/${TSDOC_METADATA_FILENAME}`; + } + + case 'object': { + let typesExport: IPackageJsonExports | string | undefined = rootExport?.types; + while (typesExport) { + switch (typeof typesExport) { + case 'string': { + return `${path.dirname(typesExport)}/${TSDOC_METADATA_FILENAME}`; + } + + case 'object': { + typesExport = typesExport?.types; + break; + } + } + } + } + } + } + break; + } + } +} + +/** + * 3. If package.json contains a `typesVersions` field, look for the version + * matching the highest minimum version that either includes a "." or "*" entry. + */ +function _tryResolveTsdocMetadataFromTypesVersionsField({ + typesVersions +}: INodePackageJson): string | undefined { + if (typesVersions) { + let highestMinimumMatchingSemver: semver.SemVer | undefined; + let latestMatchingPath: string | undefined; + for (const [version, paths] of Object.entries(typesVersions)) { + let range: semver.Range; + try { + range = new semver.Range(version); + } catch { + continue; + } + + const minimumMatchingSemver: semver.SemVer | null = semver.minVersion(range); + if ( + minimumMatchingSemver && + (!highestMinimumMatchingSemver || semver.gt(minimumMatchingSemver, highestMinimumMatchingSemver)) + ) { + const pathEntry: string[] | undefined = paths['.'] ?? paths['*']; + const firstPath: string | undefined = pathEntry?.[0]; + if (firstPath) { + highestMinimumMatchingSemver = minimumMatchingSemver; + latestMatchingPath = firstPath; + } + } + } + + if (latestMatchingPath) { + return `${path.dirname(latestMatchingPath)}/${TSDOC_METADATA_FILENAME}`; + } + } +} + +/** + * 4. If package.json contains a `"types": "./path1/path2/index.d.ts"` or a `"typings": "./path1/path2/index.d.ts"` + * field, then we look for the file under "./path1/path2/tsdoc-metadata.json". + * + * @remarks + * `types` takes precedence over `typings`. + */ +function _tryResolveTsdocMetadataFromTypesOrTypingsFields({ + typings, + types +}: INodePackageJson): string | undefined { + const typesField: string | undefined = types ?? typings; + if (typesField) { + return `${path.dirname(typesField)}/${TSDOC_METADATA_FILENAME}`; + } +} + +/** + * 5. If package.json contains a `"main": "./path1/path2/index.js"` field, then we look for the file under + * "./path1/path2/tsdoc-metadata.json". + */ +function _tryResolveTsdocMetadataFromMainField({ main }: INodePackageJson): string | undefined { + if (main) { + return `${path.dirname(main)}/${TSDOC_METADATA_FILENAME}`; + } +} + +/** + * This feature is still being standardized: https://github.com/microsoft/tsdoc/issues/7 + * In the future we will use the @microsoft/tsdoc library to read this file. + */ +function _resolveTsdocMetadataPathFromPackageJson( + packageFolder: string, + packageJson: INodePackageJson +): string { + const tsdocMetadataRelativePath: string = + _tryResolveTsdocMetadataFromTsdocMetadataField(packageJson) ?? + _tryResolveTsdocMetadataFromExportsField(packageJson) ?? + _tryResolveTsdocMetadataFromTypesVersionsField(packageJson) ?? + _tryResolveTsdocMetadataFromTypesOrTypingsFields(packageJson) ?? + _tryResolveTsdocMetadataFromMainField(packageJson) ?? + // As a final fallback, place the file in the root of the package. + TSDOC_METADATA_FILENAME; + + // Always resolve relative to the package folder. + const tsdocMetadataPath: string = path.resolve( + packageFolder, + // This non-null assertion is safe because the last entry in TSDOC_METADATA_RESOLUTION_FUNCTIONS + // returns a non-undefined value. + tsdocMetadataRelativePath! + ); + return tsdocMetadataPath; +} + /** * This class maintains a cache of analyzed information obtained from package.json * files. It is built on top of the PackageJsonLookup class. @@ -56,7 +215,7 @@ export class PackageMetadata { * Use ts.program.isSourceFileFromExternalLibrary() to test source files before passing the to PackageMetadataManager. */ export class PackageMetadataManager { - public static tsdocMetadataFilename: string = 'tsdoc-metadata.json'; + public static tsdocMetadataFilename: string = TSDOC_METADATA_FILENAME; private readonly _packageJsonLookup: PackageJsonLookup; private readonly _messageRouter: MessageRouter; @@ -70,40 +229,6 @@ export class PackageMetadataManager { this._messageRouter = messageRouter; } - // This feature is still being standardized: https://github.com/microsoft/tsdoc/issues/7 - // In the future we will use the @microsoft/tsdoc library to read this file. - private static _resolveTsdocMetadataPathFromPackageJson( - packageFolder: string, - packageJson: INodePackageJson - ): string { - const tsdocMetadataFilename: string = PackageMetadataManager.tsdocMetadataFilename; - - let tsdocMetadataRelativePath: string; - - if (packageJson.tsdocMetadata) { - // 1. If package.json contains a field such as "tsdocMetadata": "./path1/path2/tsdoc-metadata.json", - // then that takes precedence. This convention will be rarely needed, since the other rules below generally - // produce a good result. - tsdocMetadataRelativePath = packageJson.tsdocMetadata; - } else if (packageJson.typings) { - // 2. If package.json contains a field such as "typings": "./path1/path2/index.d.ts", then we look - // for the file under "./path1/path2/tsdoc-metadata.json" - tsdocMetadataRelativePath = path.join(path.dirname(packageJson.typings), tsdocMetadataFilename); - } else if (packageJson.main) { - // 3. If package.json contains a field such as "main": "./path1/path2/index.js", then we look for - // the file under "./path1/path2/tsdoc-metadata.json" - tsdocMetadataRelativePath = path.join(path.dirname(packageJson.main), tsdocMetadataFilename); - } else { - // 4. If none of the above rules apply, then by default we look for the file under "./tsdoc-metadata.json" - // since the default entry point is "./index.js" - tsdocMetadataRelativePath = tsdocMetadataFilename; - } - - // Always resolve relative to the package folder. - const tsdocMetadataPath: string = path.resolve(packageFolder, tsdocMetadataRelativePath); - return tsdocMetadataPath; - } - /** * @param tsdocMetadataPath - An explicit path that can be configured in api-extractor.json. * If this parameter is not an empty string, it overrides the normal path calculation. @@ -117,7 +242,8 @@ export class PackageMetadataManager { if (tsdocMetadataPath) { return path.resolve(packageFolder, tsdocMetadataPath); } - return PackageMetadataManager._resolveTsdocMetadataPathFromPackageJson(packageFolder, packageJson); + + return _resolveTsdocMetadataPathFromPackageJson(packageFolder, packageJson); } /** @@ -166,7 +292,7 @@ export class PackageMetadataManager { let aedocSupported: boolean = false; - const tsdocMetadataPath: string = PackageMetadataManager._resolveTsdocMetadataPathFromPackageJson( + const tsdocMetadataPath: string = _resolveTsdocMetadataPathFromPackageJson( packageJsonFolder, packageJson ); diff --git a/apps/api-extractor/src/analyzer/SourceFileLocationFormatter.ts b/apps/api-extractor/src/analyzer/SourceFileLocationFormatter.ts index 3521334e915..939f9aa5ba2 100644 --- a/apps/api-extractor/src/analyzer/SourceFileLocationFormatter.ts +++ b/apps/api-extractor/src/analyzer/SourceFileLocationFormatter.ts @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as ts from 'typescript'; -import * as path from 'path'; +import * as path from 'node:path'; + +import type * as ts from 'typescript'; + import { Path, Text } from '@rushstack/node-core-library'; export interface ISourceFileLocationFormatOptions { diff --git a/apps/api-extractor/src/analyzer/Span.ts b/apps/api-extractor/src/analyzer/Span.ts index 06afaf7b1a3..e4d1f2970ab 100644 --- a/apps/api-extractor/src/analyzer/Span.ts +++ b/apps/api-extractor/src/analyzer/Span.ts @@ -2,7 +2,8 @@ // See LICENSE in the project root for license information. import * as ts from 'typescript'; -import { InternalError, Sort } from '@rushstack/node-core-library'; + +import { InternalError, Sort, Text } from '@rushstack/node-core-library'; import { IndentedWriter } from '../generators/IndentedWriter'; @@ -637,12 +638,7 @@ export class Span { } private _getTrimmed(text: string): string { - const trimmed: string = text.replace(/\r?\n/g, '\\n'); - - if (trimmed.length > 100) { - return trimmed.substr(0, 97) + '...'; - } - return trimmed; + return Text.truncateWithEllipsis(Text.convertToLf(text), 100); } private _getSubstring(startIndex: number, endIndex: number): string { diff --git a/apps/api-extractor/src/analyzer/TypeScriptHelpers.ts b/apps/api-extractor/src/analyzer/TypeScriptHelpers.ts index d63e3ffe8d1..ccff983de02 100644 --- a/apps/api-extractor/src/analyzer/TypeScriptHelpers.ts +++ b/apps/api-extractor/src/analyzer/TypeScriptHelpers.ts @@ -4,19 +4,21 @@ /* eslint-disable no-bitwise */ import * as ts from 'typescript'; + +import { InternalError } from '@rushstack/node-core-library'; + import { SourceFileLocationFormatter } from './SourceFileLocationFormatter'; import { TypeScriptInternals } from './TypeScriptInternals'; -import { InternalError } from '@rushstack/node-core-library'; -export class TypeScriptHelpers { - // Matches TypeScript's encoded names for well-known ECMAScript symbols like - // "__@iterator" or "__@toStringTag". - private static readonly _wellKnownSymbolNameRegExp: RegExp = /^__@(\w+)$/; +// Matches TypeScript's encoded names for well-known ECMAScript symbols like +// "__@iterator" or "__@toStringTag". +const _wellKnownSymbolNameRegExp: RegExp = /^__@(\w+)$/; - // Matches TypeScript's encoded names for late-bound symbols derived from `unique symbol` declarations - // which have the form of "__@@", i.e. "__@someSymbol@12345". - private static readonly _uniqueSymbolNameRegExp: RegExp = /^__@.*@\d+$/; +// Matches TypeScript's encoded names for late-bound symbols derived from `unique symbol` declarations +// which have the form of "__@@", i.e. "__@someSymbol@12345". +const _uniqueSymbolNameRegExp: RegExp = /^__@.*@\d+$/; +export class TypeScriptHelpers { /** * This traverses any symbol aliases to find the original place where an item was defined. * For example, suppose a class is defined as "export default class MyClass { }" @@ -266,7 +268,7 @@ export class TypeScriptHelpers { * If the string does not start with `__@` then `undefined` is returned. */ public static tryDecodeWellKnownSymbolName(name: ts.__String): string | undefined { - const match: RegExpExecArray | null = TypeScriptHelpers._wellKnownSymbolNameRegExp.exec(name as string); + const match: RegExpExecArray | null = _wellKnownSymbolNameRegExp.exec(name as string); if (match) { const identifier: string = match[1]; return `[Symbol.${identifier}]`; @@ -278,7 +280,7 @@ export class TypeScriptHelpers { * Returns whether the provided name was generated for a TypeScript `unique symbol`. */ public static isUniqueSymbolName(name: ts.__String): boolean { - return TypeScriptHelpers._uniqueSymbolNameRegExp.test(name as string); + return _uniqueSymbolNameRegExp.test(name as string); } /** diff --git a/apps/api-extractor/src/analyzer/TypeScriptInternals.ts b/apps/api-extractor/src/analyzer/TypeScriptInternals.ts index e8b74f8844a..88059f26e80 100644 --- a/apps/api-extractor/src/analyzer/TypeScriptInternals.ts +++ b/apps/api-extractor/src/analyzer/TypeScriptInternals.ts @@ -4,6 +4,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import * as ts from 'typescript'; + import { InternalError } from '@rushstack/node-core-library'; /** @@ -16,8 +17,8 @@ export interface IGlobalVariableAnalyzer { export class TypeScriptInternals { public static getImmediateAliasedSymbol(symbol: ts.Symbol, typeChecker: ts.TypeChecker): ts.Symbol { // Compiler internal: - // https://github.com/microsoft/TypeScript/blob/v3.2.2/src/compiler/checker.ts - return (typeChecker as any).getImmediateAliasedSymbol(symbol); // eslint-disable-line @typescript-eslint/no-explicit-any + // https://github.com/microsoft/TypeScript/blob/v5.9.3/src/compiler/checker.ts + return (typeChecker as any).getImmediateAliasedSymbol(symbol); } /** @@ -60,7 +61,7 @@ export class TypeScriptInternals { */ public static getJSDocCommentRanges(node: ts.Node, text: string): ts.CommentRange[] | undefined { // Compiler internal: - // https://github.com/microsoft/TypeScript/blob/v2.4.2/src/compiler/utilities.ts#L616 + // https://github.com/microsoft/TypeScript/blob/v5.9.3/src/compiler/utilities.ts#L2710 return (ts as any).getJSDocCommentRanges.apply(this, arguments); } @@ -72,7 +73,7 @@ export class TypeScriptInternals { node: ts.Identifier | ts.StringLiteralLike | ts.NumericLiteral ): string { // Compiler internal: - // https://github.com/microsoft/TypeScript/blob/v3.2.2/src/compiler/utilities.ts#L2721 + // https://github.com/microsoft/TypeScript/blob/v5.9.3/src/compiler/utilities.ts#L5368 return (ts as any).getTextOfIdentifierOrLiteral(node); } @@ -82,27 +83,33 @@ export class TypeScriptInternals { * The compiler populates this cache as part of analyzing the source file. */ public static getResolvedModule( + program: ts.Program, sourceFile: ts.SourceFile, moduleNameText: string, mode: ts.ModuleKind.CommonJS | ts.ModuleKind.ESNext | undefined ): ts.ResolvedModuleFull | undefined { // Compiler internal: - // https://github.com/microsoft/TypeScript/blob/v4.7.2/src/compiler/utilities.ts#L161 - - return (ts as any).getResolvedModule(sourceFile, moduleNameText, mode); + // https://github.com/microsoft/TypeScript/blob/v5.9.3/src/compiler/types.ts#L5064 + const result: ts.ResolvedModuleWithFailedLookupLocations | undefined = (program as any).getResolvedModule( + sourceFile, + moduleNameText, + mode + ); + return result?.resolvedModule; } /** * Gets the mode required for module resolution required with the addition of Node16/nodenext */ public static getModeForUsageLocation( - file: { impliedNodeFormat?: ts.SourceFile['impliedNodeFormat'] }, - usage: ts.StringLiteralLike | undefined + file: ts.SourceFile, + usage: ts.StringLiteralLike, + compilerOptions: ts.CompilerOptions ): ts.ModuleKind.CommonJS | ts.ModuleKind.ESNext | undefined { // Compiler internal: - // https://github.com/microsoft/TypeScript/blob/v4.7.2/src/compiler/program.ts#L568 + // https://github.com/microsoft/TypeScript/blob/v5.9.3/src/compiler/program.ts#L932 - return (ts as any).getModeForUsageLocation?.(file, usage); + return ts.getModeForUsageLocation?.(file, usage, compilerOptions); } /** diff --git a/apps/api-extractor/src/analyzer/test/PackageMetadataManager.test.ts b/apps/api-extractor/src/analyzer/test/PackageMetadataManager.test.ts index d6cf4cf4c82..7f5acef9c2f 100644 --- a/apps/api-extractor/src/analyzer/test/PackageMetadataManager.test.ts +++ b/apps/api-extractor/src/analyzer/test/PackageMetadataManager.test.ts @@ -1,34 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { PackageMetadataManager } from '../PackageMetadataManager'; -import { FileSystem, PackageJsonLookup, INodePackageJson, NewlineKind } from '@rushstack/node-core-library'; - -const packageJsonLookup: PackageJsonLookup = new PackageJsonLookup(); - -function resolveInTestPackage(testPackageName: string, ...args: string[]): string { - return path.resolve(__dirname, 'test-data/tsdoc-metadata-path-inference', testPackageName, ...args); -} +jest.mock('node:path', () => { + const actualPath: typeof import('path') = jest.requireActual('node:path'); + return { + ...actualPath, + resolve: actualPath.posix.resolve + }; +}); -function getPackageMetadata(testPackageName: string): { - packageFolder: string; - packageJson: INodePackageJson; -} { - const packageFolder: string = resolveInTestPackage(testPackageName); - const packageJson: INodePackageJson | undefined = packageJsonLookup.tryLoadPackageJsonFor(packageFolder); - if (!packageJson) { - throw new Error('There should be a package.json file in the test package'); - } - return { packageFolder, packageJson }; -} +import { PackageMetadataManager } from '../PackageMetadataManager'; +import { FileSystem, type INodePackageJson, NewlineKind } from '@rushstack/node-core-library'; // eslint-disable-next-line @typescript-eslint/no-explicit-any function firstArgument(mockFn: jest.Mock): any { return mockFn.mock.calls[0][0]; } -/* eslint-disable @typescript-eslint/typedef */ +const PACKAGE_FOLDER: '/pkg' = '/pkg'; describe(PackageMetadataManager.name, () => { describe(PackageMetadataManager.writeTsdocMetadataFile.name, () => { @@ -37,9 +26,11 @@ describe(PackageMetadataManager.name, () => { beforeAll(() => { FileSystem.writeFile = mockWriteFile; }); + afterEach(() => { mockWriteFile.mockClear(); }); + afterAll(() => { FileSystem.writeFile = originalWriteFile; }); @@ -51,77 +42,403 @@ describe(PackageMetadataManager.name, () => { }); describe(PackageMetadataManager.resolveTsdocMetadataPath.name, () => { - describe('when an empty tsdocMetadataPath is provided', () => { - const tsdocMetadataPath: string = ''; + describe.each([ + { + tsdocMetadataPath: '', + label: 'when an empty tsdocMetadataPath is provided' + }, + { + tsdocMetadataPath: 'path/to/custom-tsdoc-metadata.json', + label: 'when a non-empty tsdocMetadataPath is provided', + itValue: + 'outputs the tsdoc metadata file at the provided path in the folder where package.json is located', + overrideExpected: `${PACKAGE_FOLDER}/path/to/custom-tsdoc-metadata.json` + } + ])('$label', ({ tsdocMetadataPath, itValue, overrideExpected }) => { + function testForPackageJson( + packageJson: INodePackageJson, + options: + | { expectsPackageRoot: true } + | { + expectedPathInsidePackage: string; + } + ): void { + const { expectsPackageRoot, expectedPathInsidePackage } = options as { + expectsPackageRoot: true; + } & { + expectedPathInsidePackage: string; + }; + const resolvedTsdocMetadataPath: string = PackageMetadataManager.resolveTsdocMetadataPath( + PACKAGE_FOLDER, + packageJson, + tsdocMetadataPath + ); + if (overrideExpected) { + expect(resolvedTsdocMetadataPath).toBe(overrideExpected); + } else if (expectsPackageRoot) { + expect(resolvedTsdocMetadataPath).toBe(`${PACKAGE_FOLDER}/tsdoc-metadata.json`); + } else { + expect(resolvedTsdocMetadataPath).toBe( + `${PACKAGE_FOLDER}/${expectedPathInsidePackage}/tsdoc-metadata.json` + ); + } + } + describe('given a package.json where the field "tsdocMetadata" is defined', () => { - it('outputs the tsdoc metadata path as given by "tsdocMetadata" relative to the folder of package.json', () => { - const { packageFolder, packageJson } = getPackageMetadata('package-inferred-from-tsdoc-metadata'); - expect( - PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) - ).toBe(path.resolve(packageFolder, packageJson.tsdocMetadata as string)); - }); + it( + itValue ?? + 'outputs the tsdoc metadata path as given by "tsdocMetadata" relative to the folder of package.json', + () => { + testForPackageJson( + { + name: 'package-inferred-from-tsdoc-metadata', + version: '1.0.0', + main: 'path/to/main.js', + typings: 'path/to/typings/typings.d.ts', + tsdocMetadata: 'path/to/tsdoc-metadata/tsdoc-metadata.json' + }, + { + expectedPathInsidePackage: 'path/to/tsdoc-metadata' + } + ); + } + ); }); - describe('given a package.json where the field "typings" is defined and "tsdocMetadata" is not defined', () => { - it('outputs the tsdoc metadata file "tsdoc-metadata.json" in the same folder as the path of "typings"', () => { - const { packageFolder, packageJson } = getPackageMetadata('package-inferred-from-typings'); - expect( - PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) - ).toBe(path.resolve(packageFolder, path.dirname(packageJson.typings!), 'tsdoc-metadata.json')); + + describe('given a package.json where the field "exports" is defined', () => { + describe('with a string value', () => { + testForPackageJson( + { + name: 'package-inferred-from-exports', + version: '1.0.0', + exports: 'path/to/exports/exports.js' + }, + { expectedPathInsidePackage: 'path/to/exports' } + ); }); - }); - describe('given a package.json where the field "main" is defined but not "typings" nor "tsdocMetadata"', () => { - it('outputs the tsdoc metadata file "tsdoc-metadata.json" in the same folder as the path of "main"', () => { - const { packageFolder, packageJson } = getPackageMetadata('package-inferred-from-main'); - expect( - PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) - ).toBe(path.resolve(packageFolder, path.dirname(packageJson.main!), 'tsdoc-metadata.json')); + + describe('with an array value', () => { + testForPackageJson( + { + name: 'package-inferred-from-exports', + version: '1.0.0', + exports: ['path/to/exports/exports.js', 'path/to/exports2/exports.js'] + }, + { expectedPathInsidePackage: 'path/to/exports' } + ); }); - }); - describe('given a package.json where the fields "main", "typings" and "tsdocMetadata" are not defined', () => { - it('outputs the tsdoc metadata file "tsdoc-metadata.json" in the folder where package.json is located', () => { - const { packageFolder, packageJson } = getPackageMetadata('package-default'); - expect( - PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) - ).toBe(path.resolve(packageFolder, 'tsdoc-metadata.json')); + + describe.each(['.', '*'])('with an exports field that contains a "%s" key', (exportsKey) => { + describe('with a string value', () => { + it( + itValue ?? + `outputs the tsdoc metadata file "tsdoc-metadata.json" in the same folder as the path of "${exportsKey}"`, + () => { + testForPackageJson( + { + name: 'package-inferred-from-exports', + version: '1.0.0', + exports: { + [exportsKey]: 'path/to/exports/exports.js' + } + }, + { expectedPathInsidePackage: 'path/to/exports' } + ); + } + ); + }); + + describe('with an object value that does not include a "types" key', () => { + it(itValue ?? 'outputs the tsdoc metadata file "tsdoc-metadata.json" in the package root', () => { + testForPackageJson( + { + name: 'package-inferred-from-exports', + version: '1.0.0', + exports: { + [exportsKey]: { + import: 'path/to/exports/exports.js' + } + } + }, + { expectsPackageRoot: true } + ); + }); + }); + + describe('with an object value that does include a "types" key', () => { + it( + itValue ?? + `outputs the tsdoc metadata file "tsdoc-metadata.json" in the same folder as the path of "${exportsKey}"`, + () => { + testForPackageJson( + { + name: 'package-inferred-from-exports', + version: '1.0.0', + exports: { + [exportsKey]: { + types: 'path/to/types-exports/exports.d.ts' + } + } + }, + { expectedPathInsidePackage: 'path/to/types-exports' } + ); + } + ); + }); + + describe('that nests into an object that doesn\'t contain a "types" key', () => { + it(itValue ?? 'outputs the tsdoc metadata file "tsdoc-metadata.json" package root', () => { + testForPackageJson( + { + name: 'package-inferred-from-exports', + version: '1.0.0', + exports: { + [exportsKey]: { + types: { + import: 'path/to/types-exports/exports.js' + } + } + } + }, + { expectsPackageRoot: true } + ); + }); + }); + + describe('that nests into an object that contains a "types" key', () => { + it(itValue ?? 'outputs the tsdoc metadata file "tsdoc-metadata.json" package root', () => { + testForPackageJson( + { + name: 'package-inferred-from-exports', + version: '1.0.0', + exports: { + [exportsKey]: { + types: { + types: 'path/to/types-exports/exports.d.ts' + } + } + } + }, + { expectedPathInsidePackage: 'path/to/types-exports' } + ); + }); + }); }); }); - }); - describe('when a non-empty tsdocMetadataPath is provided', () => { - const tsdocMetadataPath: string = 'path/to/custom-tsdoc-metadata.json'; - describe('given a package.json where the field "tsdocMetadata" is defined', () => { - it('outputs the tsdoc metadata file at the provided path in the folder where package.json is located', () => { - const { packageFolder, packageJson } = getPackageMetadata('package-inferred-from-tsdocMetadata'); - expect( - PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) - ).toBe(path.resolve(packageFolder, tsdocMetadataPath)); + + describe('given a package.json where the field "typesVersions" is defined', () => { + describe('with an exports field that contains a "%s" key', () => { + describe('with no selectors', () => { + it(itValue ?? 'outputs the tsdoc metadata file "tsdoc-metadata.json" in the package root', () => { + testForPackageJson( + { + name: 'package-inferred-from-typesVersions', + version: '1.0.0', + typesVersions: {} + }, + { expectsPackageRoot: true } + ); + }); + }); + + describe.each(['.', '*'])('with a %s selector', (pathSelector) => { + it( + itValue ?? 'outputs the tsdoc metadata file "tsdoc-metadata.json" in the path selected', + () => { + testForPackageJson( + { + name: 'package-inferred-from-typesVersions', + version: '1.0.0', + typesVersions: { + '>=3.0': { + [pathSelector]: ['path/to/types-exports/exports.d.ts'] + } + } + }, + { expectedPathInsidePackage: 'path/to/types-exports' } + ); + } + ); + }); + + describe('with multiple TypeScript versions', () => { + describe.each(['.', '*'])('with a %s selector', (pathSelector) => { + it( + itValue ?? + 'outputs the tsdoc metadata file "tsdoc-metadata.json" in the path selected for the latest TypeScript version', + () => { + testForPackageJson( + { + name: 'package-inferred-from-typesVersions', + version: '1.0.0', + typesVersions: { + '>=3.6': { + [pathSelector]: ['path/to/types-exports-3.6/exports.d.ts'] + }, + '>=3.0': { + [pathSelector]: ['path/to/types-exports-3.0/exports.d.ts'] + }, + '~4.0': { + [pathSelector]: ['path/to/types-exports-4.0/exports.d.ts'] + } + } + }, + { expectedPathInsidePackage: 'path/to/types-exports-4.0' } + ); + } + ); + }); + }); }); }); - describe('given a package.json where the field "typings" is defined and "tsdocMetadata" is not defined', () => { - it('outputs the tsdoc metadata file at the provided path in the folder where package.json is located', () => { - const { packageFolder, packageJson } = getPackageMetadata('package-inferred-from-typings'); - expect( - PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) - ).toBe(path.resolve(packageFolder, tsdocMetadataPath)); - }); + + describe('given a package.json where the field "types" is defined', () => { + it( + itValue ?? + 'outputs the tsdoc metadata file "tsdoc-metadata.json" in the same folder as the path of "types"', + () => { + testForPackageJson( + { + name: 'package-inferred-from-types', + version: '1.0.0', + main: 'path/to/main.js', + types: 'path/to/types/types.d.ts' + }, + { expectedPathInsidePackage: 'path/to/types' } + ); + } + ); }); - describe('given a package.json where the field "main" is defined but not "typings" nor "tsdocMetadata"', () => { - it('outputs the tsdoc metadata file at the provided path in the folder where package.json is located', () => { - const { packageFolder, packageJson } = getPackageMetadata('package-inferred-from-main'); - expect( - PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) - ).toBe(path.resolve(packageFolder, tsdocMetadataPath)); - }); + + describe('given a package.json where the field "types" and "typings" are defined', () => { + it( + itValue ?? + 'outputs the tsdoc metadata file "tsdoc-metadata.json" in the same folder as the path of "types"', + () => { + testForPackageJson( + { + name: 'package-inferred-from-types', + version: '1.0.0', + main: 'path/to/main.js', + types: 'path/to/types/types.d.ts', + typings: 'path/to/typings/typings.d.ts' + }, + { expectedPathInsidePackage: 'path/to/types' } + ); + } + ); }); - describe('given a package.json where the fields "main", "typings" and "tsdocMetadata" are not defined', () => { - it('outputs the tsdoc metadata file at the provided path in the folder where package.json is located', () => { - const { packageFolder, packageJson } = getPackageMetadata('package-default'); - expect( - PackageMetadataManager.resolveTsdocMetadataPath(packageFolder, packageJson, tsdocMetadataPath) - ).toBe(path.resolve(packageFolder, tsdocMetadataPath)); - }); + + describe('given a package.json where the field "typings" is defined and "types" is not defined', () => { + it( + itValue ?? + 'outputs the tsdoc metadata file "tsdoc-metadata.json" in the same folder as the path of "typings"', + () => { + testForPackageJson( + { + name: 'package-inferred-from-typings', + version: '1.0.0', + main: 'path/to/main.js', + typings: 'path/to/typings/typings.d.ts' + }, + { expectedPathInsidePackage: 'path/to/typings' } + ); + } + ); }); + + describe('given a package.json where the field "main" is defined', () => { + it( + itValue ?? + 'outputs the tsdoc metadata file "tsdoc-metadata.json" in the same folder as the path of "main"', + () => { + testForPackageJson( + { + name: 'package-inferred-from-main', + version: '1.0.0', + main: 'path/to/main/main.js' + }, + { expectedPathInsidePackage: 'path/to/main' } + ); + } + ); + }); + + describe( + 'given a package.json where the fields "exports", "typesVersions", "types", "main", "typings" ' + + 'and "tsdocMetadata" are not defined', + () => { + it( + itValue ?? + 'outputs the tsdoc metadata file "tsdoc-metadata.json" in the folder where package.json is located', + () => { + testForPackageJson( + { + name: 'package-default', + version: '1.0.0' + }, + { expectsPackageRoot: true } + ); + } + ); + } + ); + }); + + it('correctly resolves the tsdoc-metadata with the right precedence', () => { + const packageJson: INodePackageJson = { + name: 'package-inferred-tsdoc-metadata', + tsdocMetadata: 'path/to/tsdoc-metadata/tsdoc-metadata.json', + exports: { + '.': 'path/to/exports-dot/exports.js', + '*': 'path/to/exports-star/*.js' + }, + typesVersions: { + '>=3.0': { + '.': ['path/to/typesVersions-dot/exports.d.ts'], + '*': ['path/to/typesVersions-star/*.d.ts'] + } + }, + types: 'path/to/types/types.d.ts', + typings: 'path/to/typings/typings.d.ts', + main: 'path/to/main/main.js' + }; + + expect(PackageMetadataManager.resolveTsdocMetadataPath(PACKAGE_FOLDER, packageJson)).toBe( + `${PACKAGE_FOLDER}/path/to/tsdoc-metadata/tsdoc-metadata.json` + ); + delete packageJson.tsdocMetadata; + expect(PackageMetadataManager.resolveTsdocMetadataPath(PACKAGE_FOLDER, packageJson)).toBe( + `${PACKAGE_FOLDER}/path/to/exports-dot/tsdoc-metadata.json` + ); + delete (packageJson.exports as { '.': unknown })!['.']; + expect(PackageMetadataManager.resolveTsdocMetadataPath(PACKAGE_FOLDER, packageJson)).toBe( + `${PACKAGE_FOLDER}/path/to/exports-star/tsdoc-metadata.json` + ); + delete packageJson.exports; + expect(PackageMetadataManager.resolveTsdocMetadataPath(PACKAGE_FOLDER, packageJson)).toBe( + `${PACKAGE_FOLDER}/path/to/typesVersions-dot/tsdoc-metadata.json` + ); + delete packageJson.typesVersions!['>=3.0']!['.']; + expect(PackageMetadataManager.resolveTsdocMetadataPath(PACKAGE_FOLDER, packageJson)).toBe( + `${PACKAGE_FOLDER}/path/to/typesVersions-star/tsdoc-metadata.json` + ); + delete packageJson.typesVersions; + expect(PackageMetadataManager.resolveTsdocMetadataPath(PACKAGE_FOLDER, packageJson)).toBe( + `${PACKAGE_FOLDER}/path/to/types/tsdoc-metadata.json` + ); + delete packageJson.types; + expect(PackageMetadataManager.resolveTsdocMetadataPath(PACKAGE_FOLDER, packageJson)).toBe( + `${PACKAGE_FOLDER}/path/to/typings/tsdoc-metadata.json` + ); + delete packageJson.typings; + expect(PackageMetadataManager.resolveTsdocMetadataPath(PACKAGE_FOLDER, packageJson)).toBe( + `${PACKAGE_FOLDER}/path/to/main/tsdoc-metadata.json` + ); + delete packageJson.main; + expect(PackageMetadataManager.resolveTsdocMetadataPath(PACKAGE_FOLDER, packageJson)).toBe( + `${PACKAGE_FOLDER}/tsdoc-metadata.json` + ); }); }); }); - -/* eslint-enable @typescript-eslint/typedef */ diff --git a/apps/api-extractor/src/analyzer/test/test-data/tsdoc-metadata-path-inference/package-default/package.json b/apps/api-extractor/src/analyzer/test/test-data/tsdoc-metadata-path-inference/package-default/package.json deleted file mode 100644 index 1a58930cee9..00000000000 --- a/apps/api-extractor/src/analyzer/test/test-data/tsdoc-metadata-path-inference/package-default/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "package-default", - "version": "1.0.0" -} diff --git a/apps/api-extractor/src/analyzer/test/test-data/tsdoc-metadata-path-inference/package-inferred-from-main/package.json b/apps/api-extractor/src/analyzer/test/test-data/tsdoc-metadata-path-inference/package-inferred-from-main/package.json deleted file mode 100644 index 30a7f604a96..00000000000 --- a/apps/api-extractor/src/analyzer/test/test-data/tsdoc-metadata-path-inference/package-inferred-from-main/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "package-inferred-from-main", - "version": "1.0.0", - "main": "path/to/main.js" -} diff --git a/apps/api-extractor/src/analyzer/test/test-data/tsdoc-metadata-path-inference/package-inferred-from-tsdoc-metadata/package.json b/apps/api-extractor/src/analyzer/test/test-data/tsdoc-metadata-path-inference/package-inferred-from-tsdoc-metadata/package.json deleted file mode 100644 index fbb048f47ef..00000000000 --- a/apps/api-extractor/src/analyzer/test/test-data/tsdoc-metadata-path-inference/package-inferred-from-tsdoc-metadata/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "package-inferred-from-tsdoc-metadata", - "version": "1.0.0", - "main": "path/to/main.js", - "typings": "path/to/typings.d.ts", - "tsdocMetadata": "path/to/tsdoc-metadata.json" -} diff --git a/apps/api-extractor/src/analyzer/test/test-data/tsdoc-metadata-path-inference/package-inferred-from-typings/package.json b/apps/api-extractor/src/analyzer/test/test-data/tsdoc-metadata-path-inference/package-inferred-from-typings/package.json deleted file mode 100644 index bb73979d054..00000000000 --- a/apps/api-extractor/src/analyzer/test/test-data/tsdoc-metadata-path-inference/package-inferred-from-typings/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "package-inferred-from-typings", - "version": "1.0.0", - "main": "path/to/main.js", - "typings": "path/to/typings.d.ts" -} diff --git a/apps/api-extractor/src/api/CompilerState.ts b/apps/api-extractor/src/api/CompilerState.ts index fa2d78bef02..798718a25b8 100644 --- a/apps/api-extractor/src/api/CompilerState.ts +++ b/apps/api-extractor/src/api/CompilerState.ts @@ -1,14 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import * as ts from 'typescript'; -import colors = require('colors'); import { JsonFile } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; import { ExtractorConfig } from './ExtractorConfig'; -import { IExtractorInvokeOptions } from './Extractor'; +import type { IExtractorInvokeOptions } from './Extractor'; /** * Options for {@link CompilerState.create} @@ -60,22 +61,31 @@ export class CompilerState { if (!commandLine.options.skipLibCheck && extractorConfig.skipLibCheck) { commandLine.options.skipLibCheck = true; console.log( - colors.cyan( + Colorize.cyan( 'API Extractor was invoked with skipLibCheck. This is not recommended and may cause ' + 'incorrect type analysis.' ) ); } + // Delete outDir and declarationDir to prevent TypeScript from redirecting self-package + // imports to source files. When these options are set, TypeScript's module resolution + // tries to map output .d.ts files back to their source .ts files to avoid analyzing + // build outputs during compilation. However, API Extractor specifically wants to analyze + // the .d.ts build artifacts, not the source files. Since API Extractor doesn't emit any + // files, these options are unnecessary and interfere with correct module resolution. + delete commandLine.options.outDir; + delete commandLine.options.declarationDir; + const inputFilePaths: string[] = commandLine.fileNames.concat(extractorConfig.mainEntryPointFilePath); if (options && options.additionalEntryPoints) { inputFilePaths.push(...options.additionalEntryPoints); } // Append the entry points and remove any non-declaration files from the list - const analysisFilePaths: string[] = CompilerState._generateFilePathsForAnalysis(inputFilePaths); + const analysisFilePaths: string[] = _generateFilePathsForAnalysis(inputFilePaths); - const compilerHost: ts.CompilerHost = CompilerState._createCompilerHost(commandLine, options); + const compilerHost: ts.CompilerHost = _createCompilerHost(commandLine, options); const program: ts.Program = ts.createProgram(analysisFilePaths, commandLine.options, compilerHost); @@ -88,114 +98,114 @@ export class CompilerState { program }); } +} - /** - * Given a list of absolute file paths, return a list containing only the declaration - * files. Duplicates are also eliminated. - * - * @remarks - * The tsconfig.json settings specify the compiler's input (a set of *.ts source files, - * plus some *.d.ts declaration files used for legacy typings). However API Extractor - * analyzes the compiler's output (a set of *.d.ts entry point files, plus any legacy - * typings). This requires API Extractor to generate a special file list when it invokes - * the compiler. - * - * Duplicates are removed so that entry points can be appended without worrying whether they - * may already appear in the tsconfig.json file list. - */ - private static _generateFilePathsForAnalysis(inputFilePaths: string[]): string[] { - const analysisFilePaths: string[] = []; +/** + * Given a list of absolute file paths, return a list containing only the declaration + * files. Duplicates are also eliminated. + * + * @remarks + * The tsconfig.json settings specify the compiler's input (a set of *.ts source files, + * plus some *.d.ts declaration files used for legacy typings). However API Extractor + * analyzes the compiler's output (a set of *.d.ts entry point files, plus any legacy + * typings). This requires API Extractor to generate a special file list when it invokes + * the compiler. + * + * Duplicates are removed so that entry points can be appended without worrying whether they + * may already appear in the tsconfig.json file list. + */ +function _generateFilePathsForAnalysis(inputFilePaths: string[]): string[] { + const analysisFilePaths: string[] = []; - const seenFiles: Set = new Set(); + const seenFiles: Set = new Set(); - for (const inputFilePath of inputFilePaths) { - const inputFileToUpper: string = inputFilePath.toUpperCase(); - if (!seenFiles.has(inputFileToUpper)) { - seenFiles.add(inputFileToUpper); + for (const inputFilePath of inputFilePaths) { + const inputFileToUpper: string = inputFilePath.toUpperCase(); + if (!seenFiles.has(inputFileToUpper)) { + seenFiles.add(inputFileToUpper); - if (!path.isAbsolute(inputFilePath)) { - throw new Error('Input file is not an absolute path: ' + inputFilePath); - } + if (!path.isAbsolute(inputFilePath)) { + throw new Error('Input file is not an absolute path: ' + inputFilePath); + } - if (ExtractorConfig.hasDtsFileExtension(inputFilePath)) { - analysisFilePaths.push(inputFilePath); - } + if (ExtractorConfig.hasDtsFileExtension(inputFilePath)) { + analysisFilePaths.push(inputFilePath); } } - - return analysisFilePaths; } - private static _createCompilerHost( - commandLine: ts.ParsedCommandLine, - options: IExtractorInvokeOptions | undefined - ): ts.CompilerHost { - // Create a default CompilerHost that we will override - const compilerHost: ts.CompilerHost = ts.createCompilerHost(commandLine.options); - - // Save a copy of the original members. Note that "compilerHost" cannot be the copy, because - // createCompilerHost() captures that instance in a closure that is used by the members. - const defaultCompilerHost: ts.CompilerHost = { ...compilerHost }; - - if (options && options.typescriptCompilerFolder) { - // Prevent a closure parameter - const typescriptCompilerLibFolder: string = path.join(options.typescriptCompilerFolder, 'lib'); - compilerHost.getDefaultLibLocation = () => typescriptCompilerLibFolder; - } + return analysisFilePaths; +} - // Used by compilerHost.fileExists() - // .d.ts file path --> whether the file exists - const dtsExistsCache: Map = new Map(); - - // Used by compilerHost.fileExists() - // Example: "c:/folder/file.part.ts" - const fileExtensionRegExp: RegExp = /^(.+)(\.[a-z0-9_]+)$/i; - - compilerHost.fileExists = (fileName: string): boolean => { - // In certain deprecated setups, the compiler may write its output files (.js and .d.ts) - // in the same folder as the corresponding input file (.ts or .tsx). When following imports, - // API Extractor wants to analyze the .d.ts file; however recent versions of the compiler engine - // will instead choose the .ts file. To work around this, we hook fileExists() to hide the - // existence of those files. - - // Is "fileName" a .d.ts file? The double extension ".d.ts" needs to be matched specially. - if (!ExtractorConfig.hasDtsFileExtension(fileName)) { - // It's not a .d.ts file. Is the file extension a potential source file? - const match: RegExpExecArray | null = fileExtensionRegExp.exec(fileName); - if (match) { - // Example: "c:/folder/file.part" - const pathWithoutExtension: string = match[1]; - // Example: ".ts" - const fileExtension: string = match[2]; - - switch (fileExtension.toLocaleLowerCase()) { - case '.ts': - case '.tsx': - case '.js': - case '.jsx': - // Yes, this is a possible source file. Is there a corresponding .d.ts file in the same folder? - const dtsFileName: string = `${pathWithoutExtension}.d.ts`; - - let dtsFileExists: boolean | undefined = dtsExistsCache.get(dtsFileName); - if (dtsFileExists === undefined) { - dtsFileExists = defaultCompilerHost.fileExists!(dtsFileName); - dtsExistsCache.set(dtsFileName, dtsFileExists); - } - - if (dtsFileExists) { - // fileName is a potential source file and a corresponding .d.ts file exists. - // Thus, API Extractor should ignore this file (so the .d.ts file will get analyzed instead). - return false; - } - break; - } +function _createCompilerHost( + commandLine: ts.ParsedCommandLine, + options: IExtractorInvokeOptions | undefined +): ts.CompilerHost { + // Create a default CompilerHost that we will override + const compilerHost: ts.CompilerHost = ts.createCompilerHost(commandLine.options); + + // Save a copy of the original members. Note that "compilerHost" cannot be the copy, because + // createCompilerHost() captures that instance in a closure that is used by the members. + const defaultCompilerHost: ts.CompilerHost = { ...compilerHost }; + + if (options && options.typescriptCompilerFolder) { + // Prevent a closure parameter + const typescriptCompilerLibFolder: string = path.join(options.typescriptCompilerFolder, 'lib'); + compilerHost.getDefaultLibLocation = () => typescriptCompilerLibFolder; + } + + // Used by compilerHost.fileExists() + // .d.ts file path --> whether the file exists + const dtsExistsCache: Map = new Map(); + + // Used by compilerHost.fileExists() + // Example: "c:/folder/file.part.ts" + const fileExtensionRegExp: RegExp = /^(.+)(\.[a-z0-9_]+)$/i; + + compilerHost.fileExists = (fileName: string): boolean => { + // In certain deprecated setups, the compiler may write its output files (.js and .d.ts) + // in the same folder as the corresponding input file (.ts or .tsx). When following imports, + // API Extractor wants to analyze the .d.ts file; however recent versions of the compiler engine + // will instead choose the .ts file. To work around this, we hook fileExists() to hide the + // existence of those files. + + // Is "fileName" a .d.ts file? The double extension ".d.ts" needs to be matched specially. + if (!ExtractorConfig.hasDtsFileExtension(fileName)) { + // It's not a .d.ts file. Is the file extension a potential source file? + const match: RegExpExecArray | null = fileExtensionRegExp.exec(fileName); + if (match) { + // Example: "c:/folder/file.part" + const pathWithoutExtension: string = match[1]; + // Example: ".ts" + const fileExtension: string = match[2]; + + switch (fileExtension.toLocaleLowerCase()) { + case '.ts': + case '.tsx': + case '.js': + case '.jsx': + // Yes, this is a possible source file. Is there a corresponding .d.ts file in the same folder? + const dtsFileName: string = `${pathWithoutExtension}.d.ts`; + + let dtsFileExists: boolean | undefined = dtsExistsCache.get(dtsFileName); + if (dtsFileExists === undefined) { + dtsFileExists = defaultCompilerHost.fileExists!(dtsFileName); + dtsExistsCache.set(dtsFileName, dtsFileExists); + } + + if (dtsFileExists) { + // fileName is a potential source file and a corresponding .d.ts file exists. + // Thus, API Extractor should ignore this file (so the .d.ts file will get analyzed instead). + return false; + } + break; } } + } - // Fall through to the default implementation - return defaultCompilerHost.fileExists!(fileName); - }; + // Fall through to the default implementation + return defaultCompilerHost.fileExists!(fileName); + }; - return compilerHost; - } + return compilerHost; } diff --git a/apps/api-extractor/src/api/ConsoleMessageId.ts b/apps/api-extractor/src/api/ConsoleMessageId.ts index 8c02dabc0b9..5d1345a2093 100644 --- a/apps/api-extractor/src/api/ConsoleMessageId.ts +++ b/apps/api-extractor/src/api/ConsoleMessageId.ts @@ -11,7 +11,7 @@ * * @public */ -export const enum ConsoleMessageId { +export enum ConsoleMessageId { /** * "Analysis will use the bundled TypeScript version ___" */ @@ -43,6 +43,11 @@ export const enum ConsoleMessageId { */ WritingDtsRollup = 'console-writing-dts-rollup', + /** + * "Generating ___ API report: ___" + */ + WritingApiReport = 'console-writing-api-report', + /** * "You have changed the public API signature for this project. * Please copy the file ___ to ___, or perform a local build (which does this automatically). @@ -56,6 +61,12 @@ export const enum ConsoleMessageId { */ ApiReportNotCopied = 'console-api-report-not-copied', + /** + * Changes to the API report: + * ___ + */ + ApiReportDiff = 'console-api-report-diff', + /** * "You have changed the public API signature for this project. Updating ___" */ diff --git a/apps/api-extractor/src/api/Extractor.ts b/apps/api-extractor/src/api/Extractor.ts index 6059fd9756f..c22d226551f 100644 --- a/apps/api-extractor/src/api/Extractor.ts +++ b/apps/api-extractor/src/api/Extractor.ts @@ -1,33 +1,35 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import * as semver from 'semver'; import * as ts from 'typescript'; import * as resolve from 'resolve'; + +import type { ApiPackage } from '@microsoft/api-extractor-model'; +import { TSDocConfigFile } from '@microsoft/tsdoc-config'; import { FileSystem, NewlineKind, PackageJsonLookup, - IPackageJson, - INodePackageJson, + type IPackageJson, + type INodePackageJson, Path } from '@rushstack/node-core-library'; -import { ExtractorConfig } from './ExtractorConfig'; +import { ExtractorConfig, type IExtractorConfigApiReport } from './ExtractorConfig'; import { Collector } from '../collector/Collector'; import { DtsRollupGenerator, DtsRollupKind } from '../generators/DtsRollupGenerator'; import { ApiModelGenerator } from '../generators/ApiModelGenerator'; -import { ApiPackage } from '@microsoft/api-extractor-model'; import { ApiReportGenerator } from '../generators/ApiReportGenerator'; import { PackageMetadataManager } from '../analyzer/PackageMetadataManager'; import { ValidationEnhancer } from '../enhancers/ValidationEnhancer'; import { DocCommentEnhancer } from '../enhancers/DocCommentEnhancer'; import { CompilerState } from './CompilerState'; -import { ExtractorMessage } from './ExtractorMessage'; +import type { ExtractorMessage } from './ExtractorMessage'; import { MessageRouter } from '../collector/MessageRouter'; import { ConsoleMessageId } from './ConsoleMessageId'; -import { TSDocConfigFile } from '@microsoft/tsdoc-config'; import { SourceMapper } from '../collector/SourceMapper'; /** @@ -89,6 +91,15 @@ export interface IExtractorInvokeOptions { * the STDERR/STDOUT console. */ messageCallback?: (message: ExtractorMessage) => void; + + /** + * If true, then any differences between the actual and expected API reports will be + * printed on the console. + * + * @remarks + * The diff is not printed if the expected API report file has not been created yet. + */ + printApiReportDiff?: boolean; } /** @@ -142,12 +153,14 @@ export class ExtractorResult { /** @internal */ public constructor(properties: ExtractorResult) { - this.compilerState = properties.compilerState; - this.extractorConfig = properties.extractorConfig; - this.succeeded = properties.succeeded; - this.apiReportChanged = properties.apiReportChanged; - this.errorCount = properties.errorCount; - this.warningCount = properties.warningCount; + const { compilerState, extractorConfig, succeeded, apiReportChanged, errorCount, warningCount } = + properties; + this.compilerState = compilerState; + this.extractorConfig = extractorConfig; + this.succeeded = succeeded; + this.apiReportChanged = apiReportChanged; + this.errorCount = errorCount; + this.warningCount = warningCount; } } @@ -160,18 +173,14 @@ export class Extractor { * Returns the version number of the API Extractor NPM package. */ public static get version(): string { - return Extractor._getPackageJson().version; + return _getPackageJson().version; } /** * Returns the package name of the API Extractor NPM package. */ public static get packageName(): string { - return Extractor._getPackageJson().name; - } - - private static _getPackageJson(): IPackageJson { - return PackageJsonLookup.loadOwnPackageJson(__dirname); + return _getPackageJson().name; } /** @@ -190,41 +199,57 @@ export class Extractor { * Invoke API Extractor using an already prepared `ExtractorConfig` object. */ public static invoke(extractorConfig: ExtractorConfig, options?: IExtractorInvokeOptions): ExtractorResult { - if (!options) { - options = {}; - } - - const localBuild: boolean = options.localBuild || false; - - let compilerState: CompilerState | undefined; - if (options.compilerState) { - compilerState = options.compilerState; - } else { - compilerState = CompilerState.create(extractorConfig, options); - } + const { + packageFolder, + messages, + tsdocConfiguration, + tsdocConfigFile: { filePath: tsdocConfigFilePath, fileNotFound: tsdocConfigFileNotFound }, + apiJsonFilePath, + newlineKind, + reportTempFolder, + reportFolder, + apiReportEnabled, + reportConfigs, + testMode, + rollupEnabled, + publicTrimmedFilePath, + alphaTrimmedFilePath, + betaTrimmedFilePath, + untrimmedFilePath, + tsdocMetadataEnabled, + tsdocMetadataFilePath + } = extractorConfig; + const { + localBuild = false, + compilerState = CompilerState.create(extractorConfig, options), + messageCallback, + showVerboseMessages = false, + showDiagnostics = false, + printApiReportDiff = false + } = options ?? {}; const sourceMapper: SourceMapper = new SourceMapper(); const messageRouter: MessageRouter = new MessageRouter({ - workingPackageFolder: extractorConfig.packageFolder, - messageCallback: options.messageCallback, - messagesConfig: extractorConfig.messages || {}, - showVerboseMessages: !!options.showVerboseMessages, - showDiagnostics: !!options.showDiagnostics, - tsdocConfiguration: extractorConfig.tsdocConfiguration, + workingPackageFolder: packageFolder, + messageCallback, + messagesConfig: messages || {}, + showVerboseMessages, + showDiagnostics, + tsdocConfiguration, sourceMapper }); - if (extractorConfig.tsdocConfigFile.filePath && !extractorConfig.tsdocConfigFile.fileNotFound) { - if (!Path.isEqual(extractorConfig.tsdocConfigFile.filePath, ExtractorConfig._tsdocBaseFilePath)) { + if (tsdocConfigFilePath && !tsdocConfigFileNotFound) { + if (!Path.isEqual(tsdocConfigFilePath, ExtractorConfig._tsdocBaseFilePath)) { messageRouter.logVerbose( ConsoleMessageId.UsingCustomTSDocConfig, - 'Using custom TSDoc config from ' + extractorConfig.tsdocConfigFile.filePath + `Using custom TSDoc config from ${tsdocConfigFilePath}` ); } } - this._checkCompilerCompatibility(extractorConfig, messageRouter); + _checkCompilerCompatibility(extractorConfig, messageRouter); if (messageRouter.showDiagnostics) { messageRouter.logDiagnostic(''); @@ -241,9 +266,7 @@ export class Extractor { messageRouter.logDiagnosticHeader('TSDoc configuration'); // Convert the TSDocConfiguration into a tsdoc.json representation - const combinedConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromParser( - extractorConfig.tsdocConfiguration - ); + const combinedConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromParser(tsdocConfiguration); const serializedTSDocConfig: object = MessageRouter.buildJsonDumpObject( combinedConfigFile.saveToObject() ); @@ -254,7 +277,7 @@ export class Extractor { const collector: Collector = new Collector({ program: compilerState.program as ts.Program, messageRouter, - extractorConfig: extractorConfig, + extractorConfig, sourceMapper }); @@ -263,158 +286,55 @@ export class Extractor { DocCommentEnhancer.analyze(collector); ValidationEnhancer.analyze(collector); - const modelBuilder: ApiModelGenerator = new ApiModelGenerator(collector); + const modelBuilder: ApiModelGenerator = new ApiModelGenerator(collector, extractorConfig); const apiPackage: ApiPackage = modelBuilder.buildApiPackage(); if (messageRouter.showDiagnostics) { messageRouter.logDiagnostic(''); // skip a line after any diagnostic messages } - if (extractorConfig.docModelEnabled) { - messageRouter.logVerbose( - ConsoleMessageId.WritingDocModelFile, - 'Writing: ' + extractorConfig.apiJsonFilePath - ); - apiPackage.saveToJsonFile(extractorConfig.apiJsonFilePath, { + if (modelBuilder.docModelEnabled) { + messageRouter.logVerbose(ConsoleMessageId.WritingDocModelFile, `Writing: ${apiJsonFilePath}`); + apiPackage.saveToJsonFile(apiJsonFilePath, { toolPackage: Extractor.packageName, toolVersion: Extractor.version, - newlineConversion: extractorConfig.newlineKind, + newlineConversion: newlineKind, ensureFolderExists: true, - testMode: extractorConfig.testMode + testMode }); } - let apiReportChanged: boolean = false; - - if (extractorConfig.apiReportEnabled) { - const actualApiReportPath: string = extractorConfig.reportTempFilePath; - const actualApiReportShortPath: string = extractorConfig._getShortFilePath( - extractorConfig.reportTempFilePath - ); - - const expectedApiReportPath: string = extractorConfig.reportFilePath; - const expectedApiReportShortPath: string = extractorConfig._getShortFilePath( - extractorConfig.reportFilePath + function writeApiReport(reportConfig: IExtractorConfigApiReport): boolean { + return _writeApiReport( + collector, + extractorConfig, + messageRouter, + reportTempFolder, + reportFolder, + reportConfig, + localBuild, + printApiReportDiff ); + } - const actualApiReportContent: string = ApiReportGenerator.generateReviewFileContent(collector); - - // Write the actual file - FileSystem.writeFile(actualApiReportPath, actualApiReportContent, { - ensureFolderExists: true, - convertLineEndings: extractorConfig.newlineKind - }); - - // Compare it against the expected file - if (FileSystem.exists(expectedApiReportPath)) { - const expectedApiReportContent: string = FileSystem.readFile(expectedApiReportPath); - - if ( - !ApiReportGenerator.areEquivalentApiFileContents(actualApiReportContent, expectedApiReportContent) - ) { - apiReportChanged = true; - - if (!localBuild) { - // For a production build, issue a warning that will break the CI build. - messageRouter.logWarning( - ConsoleMessageId.ApiReportNotCopied, - 'You have changed the public API signature for this project.' + - ` Please copy the file "${actualApiReportShortPath}" to "${expectedApiReportShortPath}",` + - ` or perform a local build (which does this automatically).` + - ` See the Git repo documentation for more info.` - ); - } else { - // For a local build, just copy the file automatically. - messageRouter.logWarning( - ConsoleMessageId.ApiReportCopied, - 'You have changed the public API signature for this project.' + - ` Updating ${expectedApiReportShortPath}` - ); - - FileSystem.writeFile(expectedApiReportPath, actualApiReportContent, { - ensureFolderExists: true, - convertLineEndings: extractorConfig.newlineKind - }); - } - } else { - messageRouter.logVerbose( - ConsoleMessageId.ApiReportUnchanged, - `The API report is up to date: ${actualApiReportShortPath}` - ); - } - } else { - // The target file does not exist, so we are setting up the API review file for the first time. - // - // NOTE: People sometimes make a mistake where they move a project and forget to update the "reportFolder" - // setting, which causes a new file to silently get written to the wrong place. This can be confusing. - // Thus we treat the initial creation of the file specially. - apiReportChanged = true; - - if (!localBuild) { - // For a production build, issue a warning that will break the CI build. - messageRouter.logWarning( - ConsoleMessageId.ApiReportNotCopied, - 'The API report file is missing.' + - ` Please copy the file "${actualApiReportShortPath}" to "${expectedApiReportShortPath}",` + - ` or perform a local build (which does this automatically).` + - ` See the Git repo documentation for more info.` - ); - } else { - const expectedApiReportFolder: string = path.dirname(expectedApiReportPath); - if (!FileSystem.exists(expectedApiReportFolder)) { - messageRouter.logError( - ConsoleMessageId.ApiReportFolderMissing, - 'Unable to create the API report file. Please make sure the target folder exists:\n' + - expectedApiReportFolder - ); - } else { - FileSystem.writeFile(expectedApiReportPath, actualApiReportContent, { - convertLineEndings: extractorConfig.newlineKind - }); - messageRouter.logWarning( - ConsoleMessageId.ApiReportCreated, - 'The API report file was missing, so a new file was created. Please add this file to Git:\n' + - expectedApiReportPath - ); - } - } + let anyReportChanged: boolean = false; + if (apiReportEnabled) { + for (const reportConfig of reportConfigs) { + anyReportChanged = writeApiReport(reportConfig) || anyReportChanged; } } - if (extractorConfig.rollupEnabled) { - Extractor._generateRollupDtsFile( - collector, - extractorConfig.publicTrimmedFilePath, - DtsRollupKind.PublicRelease, - extractorConfig.newlineKind - ); - Extractor._generateRollupDtsFile( - collector, - extractorConfig.alphaTrimmedFilePath, - DtsRollupKind.AlphaRelease, - extractorConfig.newlineKind - ); - Extractor._generateRollupDtsFile( - collector, - extractorConfig.betaTrimmedFilePath, - DtsRollupKind.BetaRelease, - extractorConfig.newlineKind - ); - Extractor._generateRollupDtsFile( - collector, - extractorConfig.untrimmedFilePath, - DtsRollupKind.InternalRelease, - extractorConfig.newlineKind - ); + if (rollupEnabled) { + _generateRollupDtsFile(collector, publicTrimmedFilePath, DtsRollupKind.PublicRelease, newlineKind); + _generateRollupDtsFile(collector, alphaTrimmedFilePath, DtsRollupKind.AlphaRelease, newlineKind); + _generateRollupDtsFile(collector, betaTrimmedFilePath, DtsRollupKind.BetaRelease, newlineKind); + _generateRollupDtsFile(collector, untrimmedFilePath, DtsRollupKind.InternalRelease, newlineKind); } - if (extractorConfig.tsdocMetadataEnabled) { + if (tsdocMetadataEnabled) { // Write the tsdoc-metadata.json file for this project - PackageMetadataManager.writeTsdocMetadataFile( - extractorConfig.tsdocMetadataFilePath, - extractorConfig.newlineKind - ); + PackageMetadataManager.writeTsdocMetadataFile(tsdocMetadataFilePath, newlineKind); } // Show all the messages that we collected during analysis @@ -434,62 +354,206 @@ export class Extractor { compilerState, extractorConfig, succeeded, - apiReportChanged, + apiReportChanged: anyReportChanged, errorCount: messageRouter.errorCount, warningCount: messageRouter.warningCount }); } +} - private static _checkCompilerCompatibility( - extractorConfig: ExtractorConfig, - messageRouter: MessageRouter - ): void { - messageRouter.logInfo( - ConsoleMessageId.Preamble, - `Analysis will use the bundled TypeScript version ${ts.version}` - ); +function _getPackageJson(): IPackageJson { + return PackageJsonLookup.loadOwnPackageJson(__dirname); +} - try { - const typescriptPath: string = resolve.sync('typescript', { - basedir: extractorConfig.projectFolder, - preserveSymlinks: false - }); - const packageJsonLookup: PackageJsonLookup = new PackageJsonLookup(); - const packageJson: INodePackageJson | undefined = - packageJsonLookup.tryLoadNodePackageJsonFor(typescriptPath); - if (packageJson && packageJson.version && semver.valid(packageJson.version)) { - // Consider a newer MINOR release to be incompatible - const ourMajor: number = semver.major(ts.version); - const ourMinor: number = semver.minor(ts.version); - - const theirMajor: number = semver.major(packageJson.version); - const theirMinor: number = semver.minor(packageJson.version); - - if (theirMajor > ourMajor || (theirMajor === ourMajor && theirMinor > ourMinor)) { - messageRouter.logInfo( - ConsoleMessageId.CompilerVersionNotice, - `*** The target project appears to use TypeScript ${packageJson.version} which is newer than the` + - ` bundled compiler engine; consider upgrading API Extractor.` - ); - } +/** + * Generates the API report at the specified release level, writes it to the specified file path, and compares + * the output to the existing report (if one exists). + * + * @param reportTempDirectoryPath - The path to the directory under which the temp report file will be written prior + * to comparison with an existing report. + * @param reportDirectoryPath - The path to the directory under which the existing report file is located, and to + * which the new report will be written post-comparison. + * @param reportConfig - API report configuration, including its file name and {@link ApiReportVariant}. + * @param printApiReportDiff - {@link IExtractorInvokeOptions.printApiReportDiff} + * + * @returns Whether or not the newly generated report differs from the existing report (if one exists). + */ +function _writeApiReport( + collector: Collector, + extractorConfig: ExtractorConfig, + messageRouter: MessageRouter, + reportTempDirectoryPath: string, + reportDirectoryPath: string, + reportConfig: IExtractorConfigApiReport, + localBuild: boolean, + printApiReportDiff: boolean +): boolean { + let apiReportChanged: boolean = false; + + const actualApiReportPath: string = path.resolve(reportTempDirectoryPath, reportConfig.fileName); + const actualApiReportShortPath: string = extractorConfig._getShortFilePath(actualApiReportPath); + + const expectedApiReportPath: string = path.resolve(reportDirectoryPath, reportConfig.fileName); + const expectedApiReportShortPath: string = extractorConfig._getShortFilePath(expectedApiReportPath); + + collector.messageRouter.logVerbose( + ConsoleMessageId.WritingApiReport, + `Generating ${reportConfig.variant} API report: ${expectedApiReportPath}` + ); + + const actualApiReportContent: string = ApiReportGenerator.generateReviewFileContent( + collector, + reportConfig.variant + ); + + // Write the actual file + FileSystem.writeFile(actualApiReportPath, actualApiReportContent, { + ensureFolderExists: true, + convertLineEndings: extractorConfig.newlineKind + }); + + // Compare it against the expected file + if (FileSystem.exists(expectedApiReportPath)) { + const expectedApiReportContent: string = FileSystem.readFile(expectedApiReportPath, { + convertLineEndings: NewlineKind.Lf + }); + + if (!ApiReportGenerator.areEquivalentApiFileContents(actualApiReportContent, expectedApiReportContent)) { + apiReportChanged = true; + + if (!localBuild) { + // For a production build, issue a warning that will break the CI build. + messageRouter.logWarning( + ConsoleMessageId.ApiReportNotCopied, + 'You have changed the API signature for this project.' + + ` Please copy the file "${actualApiReportShortPath}" to "${expectedApiReportShortPath}",` + + ` or perform a local build (which does this automatically).` + + ` See the Git repo documentation for more info.` + ); + } else { + // For a local build, just copy the file automatically. + messageRouter.logWarning( + ConsoleMessageId.ApiReportCopied, + `You have changed the API signature for this project. Updating ${expectedApiReportShortPath}` + ); + + FileSystem.writeFile(expectedApiReportPath, actualApiReportContent, { + ensureFolderExists: true, + convertLineEndings: extractorConfig.newlineKind + }); + } + + if (messageRouter.showVerboseMessages || printApiReportDiff) { + const Diff: typeof import('diff') = require('diff'); + const patch: import('diff').StructuredPatch = Diff.structuredPatch( + expectedApiReportShortPath, + actualApiReportShortPath, + expectedApiReportContent, + actualApiReportContent + ); + const logFunction: + | (typeof MessageRouter.prototype)['logWarning'] + | (typeof MessageRouter.prototype)['logVerbose'] = printApiReportDiff + ? messageRouter.logWarning.bind(messageRouter) + : messageRouter.logVerbose.bind(messageRouter); + + logFunction( + ConsoleMessageId.ApiReportDiff, + 'Changes to the API report:\n\n' + Diff.formatPatch(patch) + ); + } + } else { + messageRouter.logVerbose( + ConsoleMessageId.ApiReportUnchanged, + `The API report is up to date: ${actualApiReportShortPath}` + ); + } + } else { + // The target file does not exist, so we are setting up the API review file for the first time. + // + // NOTE: People sometimes make a mistake where they move a project and forget to update the "reportFolder" + // setting, which causes a new file to silently get written to the wrong place. This can be confusing. + // Thus we treat the initial creation of the file specially. + apiReportChanged = true; + + if (!localBuild) { + // For a production build, issue a warning that will break the CI build. + messageRouter.logWarning( + ConsoleMessageId.ApiReportNotCopied, + 'The API report file is missing.' + + ` Please copy the file "${actualApiReportShortPath}" to "${expectedApiReportShortPath}",` + + ` or perform a local build (which does this automatically).` + + ` See the Git repo documentation for more info.` + ); + } else { + const expectedApiReportFolder: string = path.dirname(expectedApiReportPath); + if (!FileSystem.exists(expectedApiReportFolder)) { + messageRouter.logError( + ConsoleMessageId.ApiReportFolderMissing, + 'Unable to create the API report file. Please make sure the target folder exists:\n' + + expectedApiReportFolder + ); + } else { + FileSystem.writeFile(expectedApiReportPath, actualApiReportContent, { + convertLineEndings: extractorConfig.newlineKind + }); + messageRouter.logWarning( + ConsoleMessageId.ApiReportCreated, + 'The API report file was missing, so a new file was created. Please add this file to Git:\n' + + expectedApiReportPath + ); } - } catch (e) { - // The compiler detection heuristic is not expected to work in many configurations } } + return apiReportChanged; +} - private static _generateRollupDtsFile( - collector: Collector, - outputPath: string, - dtsKind: DtsRollupKind, - newlineKind: NewlineKind - ): void { - if (outputPath !== '') { - collector.messageRouter.logVerbose( - ConsoleMessageId.WritingDtsRollup, - `Writing package typings: ${outputPath}` - ); - DtsRollupGenerator.writeTypingsFile(collector, outputPath, dtsKind, newlineKind); +function _checkCompilerCompatibility(extractorConfig: ExtractorConfig, messageRouter: MessageRouter): void { + messageRouter.logInfo( + ConsoleMessageId.Preamble, + `Analysis will use the bundled TypeScript version ${ts.version}` + ); + + try { + const typescriptPath: string = resolve.sync('typescript', { + basedir: extractorConfig.projectFolder, + preserveSymlinks: false + }); + const packageJsonLookup: PackageJsonLookup = new PackageJsonLookup(); + const packageJson: INodePackageJson | undefined = + packageJsonLookup.tryLoadNodePackageJsonFor(typescriptPath); + if (packageJson && packageJson.version && semver.valid(packageJson.version)) { + // Consider a newer MINOR release to be incompatible + const ourMajor: number = semver.major(ts.version); + const ourMinor: number = semver.minor(ts.version); + + const theirMajor: number = semver.major(packageJson.version); + const theirMinor: number = semver.minor(packageJson.version); + + if (theirMajor > ourMajor || (theirMajor === ourMajor && theirMinor > ourMinor)) { + messageRouter.logInfo( + ConsoleMessageId.CompilerVersionNotice, + `*** The target project appears to use TypeScript ${packageJson.version} which is newer than the` + + ` bundled compiler engine; consider upgrading API Extractor.` + ); + } } + } catch (e) { + // The compiler detection heuristic is not expected to work in many configurations + } +} + +function _generateRollupDtsFile( + collector: Collector, + outputPath: string, + dtsKind: DtsRollupKind, + newlineKind: NewlineKind +): void { + if (outputPath !== '') { + collector.messageRouter.logVerbose( + ConsoleMessageId.WritingDtsRollup, + `Writing package typings: ${outputPath}` + ); + DtsRollupGenerator.writeTypingsFile(collector, outputPath, dtsKind, newlineKind); } } diff --git a/apps/api-extractor/src/api/ExtractorConfig.ts b/apps/api-extractor/src/api/ExtractorConfig.ts index 95d5542df99..f3e33f46825 100644 --- a/apps/api-extractor/src/api/ExtractorConfig.ts +++ b/apps/api-extractor/src/api/ExtractorConfig.ts @@ -1,29 +1,38 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import * as resolve from 'resolve'; -import lodash = require('lodash'); + +import { EnumMemberOrder, ReleaseTag } from '@microsoft/api-extractor-model'; +import { TSDocConfiguration, TSDocTagDefinition } from '@microsoft/tsdoc'; +import { TSDocConfigFile } from '@microsoft/tsdoc-config'; +import { type IRigConfig, RigConfig } from '@rushstack/rig-package'; import { JsonFile, JsonSchema, FileSystem, + Objects, PackageJsonLookup, - INodePackageJson, + type INodePackageJson, PackageName, Text, InternalError, Path, NewlineKind } from '@rushstack/node-core-library'; -import { RigConfig } from '@rushstack/rig-package'; -import { IConfigFile, IExtractorMessagesConfig } from './IConfigFile'; +import type { + ApiReportVariant, + IConfigApiReport, + IConfigFile, + IExtractorMessagesConfig +} from './IConfigFile'; import { PackageMetadataManager } from '../analyzer/PackageMetadataManager'; import { MessageRouter } from '../collector/MessageRouter'; -import { EnumMemberOrder } from '@microsoft/api-extractor-model'; -import { TSDocConfiguration } from '@microsoft/tsdoc'; -import { TSDocConfigFile } from '@microsoft/tsdoc-config'; +import type { IApiModelGenerationOptions } from '../generators/ApiModelGenerator'; +import apiExtractorSchema from '../schemas/api-extractor.schema.json'; /** * Tokens used during variable expansion of path fields from api-extractor.json. @@ -71,7 +80,7 @@ export interface IExtractorConfigLoadForFolderOptions { /** * An already constructed `RigConfig` object. If omitted, then a new `RigConfig` object will be constructed. */ - rigConfig?: RigConfig; + rigConfig?: IRigConfig; } /** @@ -146,6 +155,44 @@ export interface IExtractorConfigPrepareOptions { ignoreMissingEntryPoint?: boolean; } +/** + * Configuration for a single API report, including its {@link IExtractorConfigApiReport.variant}. + * + * @public + */ +export interface IExtractorConfigApiReport { + /** + * Report variant. + * Determines which API items will be included in the report output, based on their tagged release levels. + */ + variant: ApiReportVariant; + + /** + * Name of the output report file. + * @remarks Relative to the configured report directory path. + */ + fileName: string; +} + +/** Default {@link IConfigApiReport.reportVariants} */ +const defaultApiReportVariants: readonly ApiReportVariant[] = ['complete']; + +/** + * Default {@link IConfigApiReport.tagsToReport}. + * + * @remarks + * Note that this list is externally documented, and directly affects report output. + * Also note that the order of tags in this list is significant, as it determines the order of tags in the report. + * Any changes to this list should be considered breaking. + */ +const defaultTagsToReport: Readonly> = { + '@sealed': true, + '@virtual': true, + '@override': true, + '@eventProperty': true, + '@deprecated': true +}; + interface IExtractorConfigParameters { projectFolder: string; packageJson: INodePackageJson | undefined; @@ -156,10 +203,12 @@ interface IExtractorConfigParameters { overrideTsconfig: {} | undefined; skipLibCheck: boolean; apiReportEnabled: boolean; - reportFilePath: string; - reportTempFilePath: string; + reportConfigs: readonly IExtractorConfigApiReport[]; + reportFolder: string; + reportTempFolder: string; apiReportIncludeForgottenExports: boolean; - docModelEnabled: boolean; + tagsToReport: Readonly>; + docModelGenerationOptions: IApiModelGenerationOptions | undefined; apiJsonFilePath: string; docModelIncludeForgottenExports: boolean; projectFolderUrl: string | undefined; @@ -179,17 +228,26 @@ interface IExtractorConfigParameters { enumMemberOrder: EnumMemberOrder; } +const _defaultConfig: Partial = JsonFile.load( + path.join(__dirname, '../schemas/api-extractor-defaults.json') +); + +/** + * Match all three flavors for type declaration files (.d.ts, .d.mts, .d.cts) + * including the new TS 5 bundle resolutions (.d.\{extension\}.ts, .d.\{extension\}.mts, .d.\{extension\}.cts) + **/ +const _declarationFileExtensionRegExp: RegExp = /\.d(\.[^./\\]+)?\.(c|m)?ts$/i; + /** * The `ExtractorConfig` class loads, validates, interprets, and represents the api-extractor.json config file. + * @sealed * @public */ export class ExtractorConfig { /** * The JSON Schema for API Extractor config file (api-extractor.schema.json). */ - public static readonly jsonSchema: JsonSchema = JsonSchema.fromFile( - path.join(__dirname, '../schemas/api-extractor.schema.json') - ); + public static readonly jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(apiExtractorSchema); /** * The config file name "api-extractor.json". @@ -206,12 +264,6 @@ export class ExtractorConfig { '../../extends/tsdoc-base.json' ); - private static readonly _defaultConfig: Partial = JsonFile.load( - path.join(__dirname, '../schemas/api-extractor-defaults.json') - ); - - private static readonly _declarationFileExtensionRegExp: RegExp = /\.d\.ts$/i; - /** {@inheritDoc IConfigFile.projectFolder} */ public readonly projectFolder: string; @@ -245,15 +297,46 @@ export class ExtractorConfig { /** {@inheritDoc IConfigApiReport.enabled} */ public readonly apiReportEnabled: boolean; - /** The `reportFolder` path combined with the `reportFileName`. */ - public readonly reportFilePath: string; - /** The `reportTempFolder` path combined with the `reportFileName`. */ - public readonly reportTempFilePath: string; + /** + * List of configurations for report files to be generated. + * @remarks Derived from {@link IConfigApiReport.reportFileName} and {@link IConfigApiReport.reportVariants}. + */ + public readonly reportConfigs: readonly IExtractorConfigApiReport[]; + /** {@inheritDoc IConfigApiReport.reportFolder} */ + public readonly reportFolder: string; + /** {@inheritDoc IConfigApiReport.reportTempFolder} */ + public readonly reportTempFolder: string; + /** {@inheritDoc IConfigApiReport.tagsToReport} */ + public readonly tagsToReport: Readonly>; + + /** + * Gets the file path for the "complete" (default) report configuration, if one was specified. + * Otherwise, returns an empty string. + * @deprecated Use {@link ExtractorConfig.reportConfigs} to access all report configurations. + */ + public get reportFilePath(): string { + const completeConfig: IExtractorConfigApiReport | undefined = this._getCompleteReportConfig(); + return completeConfig === undefined ? '' : path.join(this.reportFolder, completeConfig.fileName); + } + + /** + * Gets the temp file path for the "complete" (default) report configuration, if one was specified. + * Otherwise, returns an empty string. + * @deprecated Use {@link ExtractorConfig.reportConfigs} to access all report configurations. + */ + public get reportTempFilePath(): string { + const completeConfig: IExtractorConfigApiReport | undefined = this._getCompleteReportConfig(); + return completeConfig === undefined ? '' : path.join(this.reportTempFolder, completeConfig.fileName); + } + /** {@inheritDoc IConfigApiReport.includeForgottenExports} */ public readonly apiReportIncludeForgottenExports: boolean; - /** {@inheritDoc IConfigDocModel.enabled} */ - public readonly docModelEnabled: boolean; + /** + * If specified, the doc model is enabled and the specified options will be used. + * @beta + */ + public readonly docModelGenerationOptions: IApiModelGenerationOptions | undefined; /** {@inheritDoc IConfigDocModel.apiJsonFilePath} */ public readonly apiJsonFilePath: string; /** {@inheritDoc IConfigDocModel.includeForgottenExports} */ @@ -304,37 +387,72 @@ export class ExtractorConfig { /** {@inheritDoc IConfigFile.enumMemberOrder} */ public readonly enumMemberOrder: EnumMemberOrder; - private constructor(parameters: IExtractorConfigParameters) { - this.projectFolder = parameters.projectFolder; - this.packageJson = parameters.packageJson; - this.packageFolder = parameters.packageFolder; - this.mainEntryPointFilePath = parameters.mainEntryPointFilePath; - this.bundledPackages = parameters.bundledPackages; - this.tsconfigFilePath = parameters.tsconfigFilePath; - this.overrideTsconfig = parameters.overrideTsconfig; - this.skipLibCheck = parameters.skipLibCheck; - this.apiReportEnabled = parameters.apiReportEnabled; - this.reportFilePath = parameters.reportFilePath; - this.reportTempFilePath = parameters.reportTempFilePath; - this.apiReportIncludeForgottenExports = parameters.apiReportIncludeForgottenExports; - this.docModelEnabled = parameters.docModelEnabled; - this.apiJsonFilePath = parameters.apiJsonFilePath; - this.docModelIncludeForgottenExports = parameters.docModelIncludeForgottenExports; - this.projectFolderUrl = parameters.projectFolderUrl; - this.rollupEnabled = parameters.rollupEnabled; - this.untrimmedFilePath = parameters.untrimmedFilePath; - this.alphaTrimmedFilePath = parameters.alphaTrimmedFilePath; - this.betaTrimmedFilePath = parameters.betaTrimmedFilePath; - this.publicTrimmedFilePath = parameters.publicTrimmedFilePath; - this.omitTrimmingComments = parameters.omitTrimmingComments; - this.tsdocMetadataEnabled = parameters.tsdocMetadataEnabled; - this.tsdocMetadataFilePath = parameters.tsdocMetadataFilePath; - this.tsdocConfigFile = parameters.tsdocConfigFile; - this.tsdocConfiguration = parameters.tsdocConfiguration; - this.newlineKind = parameters.newlineKind; - this.messages = parameters.messages; - this.testMode = parameters.testMode; - this.enumMemberOrder = parameters.enumMemberOrder; + private constructor({ + projectFolder, + packageJson, + packageFolder, + mainEntryPointFilePath, + bundledPackages, + tsconfigFilePath, + overrideTsconfig, + skipLibCheck, + apiReportEnabled, + apiReportIncludeForgottenExports, + reportConfigs, + reportFolder, + reportTempFolder, + tagsToReport, + docModelGenerationOptions, + apiJsonFilePath, + docModelIncludeForgottenExports, + projectFolderUrl, + rollupEnabled, + untrimmedFilePath, + alphaTrimmedFilePath, + betaTrimmedFilePath, + publicTrimmedFilePath, + omitTrimmingComments, + tsdocMetadataEnabled, + tsdocMetadataFilePath, + tsdocConfigFile, + tsdocConfiguration, + newlineKind, + messages, + testMode, + enumMemberOrder + }: IExtractorConfigParameters) { + this.projectFolder = projectFolder; + this.packageJson = packageJson; + this.packageFolder = packageFolder; + this.mainEntryPointFilePath = mainEntryPointFilePath; + this.bundledPackages = bundledPackages; + this.tsconfigFilePath = tsconfigFilePath; + this.overrideTsconfig = overrideTsconfig; + this.skipLibCheck = skipLibCheck; + this.apiReportEnabled = apiReportEnabled; + this.apiReportIncludeForgottenExports = apiReportIncludeForgottenExports; + this.reportConfigs = reportConfigs; + this.reportFolder = reportFolder; + this.reportTempFolder = reportTempFolder; + this.tagsToReport = tagsToReport; + this.docModelGenerationOptions = docModelGenerationOptions; + this.apiJsonFilePath = apiJsonFilePath; + this.docModelIncludeForgottenExports = docModelIncludeForgottenExports; + this.projectFolderUrl = projectFolderUrl; + this.rollupEnabled = rollupEnabled; + this.untrimmedFilePath = untrimmedFilePath; + this.alphaTrimmedFilePath = alphaTrimmedFilePath; + this.betaTrimmedFilePath = betaTrimmedFilePath; + this.publicTrimmedFilePath = publicTrimmedFilePath; + this.omitTrimmingComments = omitTrimmingComments; + this.tsdocMetadataEnabled = tsdocMetadataEnabled; + this.tsdocMetadataFilePath = tsdocMetadataFilePath; + this.tsdocConfigFile = tsdocConfigFile; + this.tsdocConfiguration = tsdocConfiguration; + this.newlineKind = newlineKind; + this.messages = messages; + this.testMode = testMode; + this.enumMemberOrder = enumMemberOrder; } /** @@ -426,7 +544,7 @@ export class ExtractorConfig { // If We didn't find it in /api-extractor.json or /config/api-extractor.json // then check for a rig package if (packageFolder) { - let rigConfig: RigConfig; + let rigConfig: IRigConfig; if (options.rigConfig) { // The caller provided an already solved RigConfig. Double-check that it is for the right project. if (!Path.isEqual(options.rigConfig.projectFolderPath, packageFolder)) { @@ -514,6 +632,17 @@ export class ExtractorConfig { let currentConfigFilePath: string = path.resolve(jsonFilePath); let configObject: Partial = {}; + // Arrays are overwritten rather than merged, which is the intuitive behavior for config files. + // For example, given a base config containing an array property with value ["foo", "bar"] and a + // derived config that specifies ["baz"] for that property, the result is ["baz"] (not ["baz", "bar"]). + const mergeCustomizer: Objects.MergeWithCustomizer = (objValue, srcValue) => { + if (Array.isArray(srcValue)) { + return srcValue; + } + // Fall back to default merge behavior. + return undefined; + }; + try { do { // Check if this file was already processed. @@ -554,11 +683,11 @@ export class ExtractorConfig { } // This step has to be performed in advance, since the currentConfigFolderPath information will be lost - // after lodash.merge() is performed. - ExtractorConfig._resolveConfigFileRelativePaths(baseConfig, currentConfigFolderPath); + // after the merge is performed. + _resolveConfigFileRelativePaths(baseConfig, currentConfigFolderPath); // Merge extractorConfig into baseConfig, mutating baseConfig - lodash.merge(baseConfig, configObject); + Objects.mergeWith(baseConfig, configObject, mergeCustomizer); configObject = baseConfig; currentConfigFilePath = extendsField; @@ -568,7 +697,11 @@ export class ExtractorConfig { } // Lastly, apply the defaults - configObject = lodash.merge(lodash.cloneDeep(ExtractorConfig._defaultConfig), configObject); + configObject = Objects.mergeWith( + structuredClone(_defaultConfig), + configObject, + mergeCustomizer + ) as Partial; ExtractorConfig.jsonSchema.validateObject(configObject, jsonFilePath); @@ -576,121 +709,6 @@ export class ExtractorConfig { return configObject as IConfigFile; } - private static _resolveConfigFileRelativePaths( - configFile: IConfigFile, - currentConfigFolderPath: string - ): void { - if (configFile.projectFolder) { - configFile.projectFolder = ExtractorConfig._resolveConfigFileRelativePath( - 'projectFolder', - configFile.projectFolder, - currentConfigFolderPath - ); - } - - if (configFile.mainEntryPointFilePath) { - configFile.mainEntryPointFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'mainEntryPointFilePath', - configFile.mainEntryPointFilePath, - currentConfigFolderPath - ); - } - - if (configFile.compiler) { - if (configFile.compiler.tsconfigFilePath) { - configFile.compiler.tsconfigFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'tsconfigFilePath', - configFile.compiler.tsconfigFilePath, - currentConfigFolderPath - ); - } - } - - if (configFile.apiReport) { - if (configFile.apiReport.reportFolder) { - configFile.apiReport.reportFolder = ExtractorConfig._resolveConfigFileRelativePath( - 'reportFolder', - configFile.apiReport.reportFolder, - currentConfigFolderPath - ); - } - if (configFile.apiReport.reportTempFolder) { - configFile.apiReport.reportTempFolder = ExtractorConfig._resolveConfigFileRelativePath( - 'reportTempFolder', - configFile.apiReport.reportTempFolder, - currentConfigFolderPath - ); - } - } - - if (configFile.docModel) { - if (configFile.docModel.apiJsonFilePath) { - configFile.docModel.apiJsonFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'apiJsonFilePath', - configFile.docModel.apiJsonFilePath, - currentConfigFolderPath - ); - } - } - - if (configFile.dtsRollup) { - if (configFile.dtsRollup.untrimmedFilePath) { - configFile.dtsRollup.untrimmedFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'untrimmedFilePath', - configFile.dtsRollup.untrimmedFilePath, - currentConfigFolderPath - ); - } - if (configFile.dtsRollup.alphaTrimmedFilePath) { - configFile.dtsRollup.alphaTrimmedFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'alphaTrimmedFilePath', - configFile.dtsRollup.alphaTrimmedFilePath, - currentConfigFolderPath - ); - } - if (configFile.dtsRollup.betaTrimmedFilePath) { - configFile.dtsRollup.betaTrimmedFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'betaTrimmedFilePath', - configFile.dtsRollup.betaTrimmedFilePath, - currentConfigFolderPath - ); - } - if (configFile.dtsRollup.publicTrimmedFilePath) { - configFile.dtsRollup.publicTrimmedFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'publicTrimmedFilePath', - configFile.dtsRollup.publicTrimmedFilePath, - currentConfigFolderPath - ); - } - } - - if (configFile.tsdocMetadata) { - if (configFile.tsdocMetadata.tsdocMetadataFilePath) { - configFile.tsdocMetadata.tsdocMetadataFilePath = ExtractorConfig._resolveConfigFileRelativePath( - 'tsdocMetadataFilePath', - configFile.tsdocMetadata.tsdocMetadataFilePath, - currentConfigFolderPath - ); - } - } - } - - private static _resolveConfigFileRelativePath( - fieldName: string, - fieldValue: string, - currentConfigFolderPath: string - ): string { - if (!path.isAbsolute(fieldValue)) { - if (fieldValue.indexOf('') !== 0) { - // If the path is not absolute and does not start with "", then resolve it relative - // to the folder of the config file that it appears in - return path.join(currentConfigFolderPath, fieldValue); - } - } - - return fieldValue; - } - /** * Prepares an `ExtractorConfig` object using a configuration that is provided as a runtime object, * rather than reading it from disk. This allows configurations to be constructed programmatically, @@ -795,7 +813,7 @@ export class ExtractorConfig { } } } else { - ExtractorConfig._rejectAnyTokensInPath(configObject.projectFolder, 'projectFolder'); + _rejectAnyTokensInPath(configObject.projectFolder, 'projectFolder'); if (!FileSystem.exists(configObject.projectFolder)) { throw new Error('The specified "projectFolder" path does not exist: ' + configObject.projectFolder); @@ -819,7 +837,7 @@ export class ExtractorConfig { // A merged configuration should have this throw new Error('The "mainEntryPointFilePath" setting is missing'); } - const mainEntryPointFilePath: string = ExtractorConfig._resolvePathWithTokens( + const mainEntryPointFilePath: string = _resolvePathWithTokens( 'mainEntryPointFilePath', configObject.mainEntryPointFilePath, tokenContext @@ -836,13 +854,11 @@ export class ExtractorConfig { } const bundledPackages: string[] = configObject.bundledPackages || []; - for (const bundledPackage of bundledPackages) { - if (!PackageName.isValidName(bundledPackage)) { - throw new Error(`The "bundledPackages" list contains an invalid package name: "${bundledPackage}"`); - } - } - const tsconfigFilePath: string = ExtractorConfig._resolvePathWithTokens( + // Note: we cannot fully validate package name patterns, as the strings may contain wildcards. + // We won't know if the entries are valid until we can compare them against the package.json "dependencies" contents. + + const tsconfigFilePath: string = _resolvePathWithTokens( 'tsconfigFilePath', configObject.compiler.tsconfigFilePath, tokenContext @@ -857,57 +873,136 @@ export class ExtractorConfig { } } - let apiReportEnabled: boolean = false; - let reportFilePath: string = ''; - let reportTempFilePath: string = ''; - let apiReportIncludeForgottenExports: boolean = false; - if (configObject.apiReport) { - apiReportEnabled = !!configObject.apiReport.enabled; + if (configObject.apiReport?.tagsToReport) { + _validateTagsToReport(configObject.apiReport.tagsToReport); + } - const reportFilename: string = ExtractorConfig._expandStringWithTokens( - 'reportFileName', - configObject.apiReport.reportFileName || '', - tokenContext - ); + const apiReportEnabled: boolean = configObject.apiReport?.enabled ?? false; + const apiReportIncludeForgottenExports: boolean = + configObject.apiReport?.includeForgottenExports ?? false; + let reportFolder: string = tokenContext.projectFolder; + let reportTempFolder: string = tokenContext.projectFolder; + const reportConfigs: IExtractorConfigApiReport[] = []; + let tagsToReport: Record<`@${string}`, boolean> = {}; + if (apiReportEnabled) { + // Undefined case checked above where we assign `apiReportEnabled` + const apiReportConfig: IConfigApiReport = configObject.apiReport!; + + const reportFileNameSuffix: string = '.api.md'; + let reportFileNameBase: string; + if (apiReportConfig.reportFileName) { + if ( + apiReportConfig.reportFileName.indexOf('/') >= 0 || + apiReportConfig.reportFileName.indexOf('\\') >= 0 + ) { + throw new Error( + `The "reportFileName" setting contains invalid characters: "${apiReportConfig.reportFileName}"` + ); + } - if (!reportFilename) { - // A merged configuration should have this - throw new Error('The "reportFilename" setting is missing'); + if (!apiReportConfig.reportFileName.endsWith(reportFileNameSuffix)) { + // `.api.md` extension was not specified. Use provided file name base as is. + reportFileNameBase = apiReportConfig.reportFileName; + } else { + // The system previously asked users to specify their filenames in a form containing the `.api.md` extension. + // This guidance has changed, but to maintain backwards compatibility, we will temporarily support input + // that ends with the `.api.md` extension specially, by stripping it out. + // This should be removed in version 8, possibly replaced with an explicit error to help users + // migrate their configs. + reportFileNameBase = apiReportConfig.reportFileName.slice(0, -reportFileNameSuffix.length); + } + } else { + // Default value + reportFileNameBase = ''; } - if (reportFilename.indexOf('/') >= 0 || reportFilename.indexOf('\\') >= 0) { - // A merged configuration should have this - throw new Error(`The "reportFilename" setting contains invalid characters: "${reportFilename}"`); + + const reportVariantKinds: readonly ApiReportVariant[] = + apiReportConfig.reportVariants ?? defaultApiReportVariants; + + for (const reportVariantKind of reportVariantKinds) { + // Omit the variant kind from the "complete" report file name for simplicity and for backwards compatibility. + const fileNameWithTokens: string = `${reportFileNameBase}${ + reportVariantKind === 'complete' ? '' : `.${reportVariantKind}` + }${reportFileNameSuffix}`; + const normalizedFileName: string = _expandStringWithTokens( + 'reportFileName', + fileNameWithTokens, + tokenContext + ); + + reportConfigs.push({ + fileName: normalizedFileName, + variant: reportVariantKind + }); } - const reportFolder: string = ExtractorConfig._resolvePathWithTokens( - 'reportFolder', - configObject.apiReport.reportFolder, - tokenContext - ); - const reportTempFolder: string = ExtractorConfig._resolvePathWithTokens( - 'reportTempFolder', - configObject.apiReport.reportTempFolder, - tokenContext - ); + if (apiReportConfig.reportFolder) { + reportFolder = _resolvePathWithTokens('reportFolder', apiReportConfig.reportFolder, tokenContext); + } + + if (apiReportConfig.reportTempFolder) { + reportTempFolder = _resolvePathWithTokens( + 'reportTempFolder', + apiReportConfig.reportTempFolder, + tokenContext + ); + } - reportFilePath = path.join(reportFolder, reportFilename); - reportTempFilePath = path.join(reportTempFolder, reportFilename); - apiReportIncludeForgottenExports = !!configObject.apiReport.includeForgottenExports; + tagsToReport = { + ...defaultTagsToReport, + ...apiReportConfig.tagsToReport + }; } - let docModelEnabled: boolean = false; + let docModelGenerationOptions: IApiModelGenerationOptions | undefined = undefined; let apiJsonFilePath: string = ''; let docModelIncludeForgottenExports: boolean = false; let projectFolderUrl: string | undefined; - if (configObject.docModel) { - docModelEnabled = !!configObject.docModel.enabled; - apiJsonFilePath = ExtractorConfig._resolvePathWithTokens( + if (configObject.docModel?.enabled) { + apiJsonFilePath = _resolvePathWithTokens( 'apiJsonFilePath', configObject.docModel.apiJsonFilePath, tokenContext ); docModelIncludeForgottenExports = !!configObject.docModel.includeForgottenExports; projectFolderUrl = configObject.docModel.projectFolderUrl; + + const releaseTagsToTrim: Set = new Set(); + const releaseTagsToTrimOption: string[] = configObject.docModel.releaseTagsToTrim || ['@internal']; + for (const releaseTagToTrim of releaseTagsToTrimOption) { + let releaseTag: ReleaseTag; + switch (releaseTagToTrim) { + case '@internal': { + releaseTag = ReleaseTag.Internal; + break; + } + + case '@alpha': { + releaseTag = ReleaseTag.Alpha; + break; + } + + case '@beta': { + releaseTag = ReleaseTag.Beta; + break; + } + + case '@public': { + releaseTag = ReleaseTag.Public; + break; + } + + default: { + throw new Error(`The release tag "${releaseTagToTrim}" is not supported`); + } + } + + releaseTagsToTrim.add(releaseTag); + } + + docModelGenerationOptions = { + releaseTagsToTrim + }; } let tsdocMetadataEnabled: boolean = false; @@ -936,7 +1031,7 @@ export class ExtractorConfig { packageJson ); } else { - tsdocMetadataFilePath = ExtractorConfig._resolvePathWithTokens( + tsdocMetadataFilePath = _resolvePathWithTokens( 'tsdocMetadataFilePath', configObject.tsdocMetadata.tsdocMetadataFilePath, tokenContext @@ -961,22 +1056,22 @@ export class ExtractorConfig { if (configObject.dtsRollup) { rollupEnabled = !!configObject.dtsRollup.enabled; - untrimmedFilePath = ExtractorConfig._resolvePathWithTokens( + untrimmedFilePath = _resolvePathWithTokens( 'untrimmedFilePath', configObject.dtsRollup.untrimmedFilePath, tokenContext ); - alphaTrimmedFilePath = ExtractorConfig._resolvePathWithTokens( + alphaTrimmedFilePath = _resolvePathWithTokens( 'alphaTrimmedFilePath', configObject.dtsRollup.alphaTrimmedFilePath, tokenContext ); - betaTrimmedFilePath = ExtractorConfig._resolvePathWithTokens( + betaTrimmedFilePath = _resolvePathWithTokens( 'betaTrimmedFilePath', configObject.dtsRollup.betaTrimmedFilePath, tokenContext ); - publicTrimmedFilePath = ExtractorConfig._resolvePathWithTokens( + publicTrimmedFilePath = _resolvePathWithTokens( 'publicTrimmedFilePath', configObject.dtsRollup.publicTrimmedFilePath, tokenContext @@ -1009,10 +1104,12 @@ export class ExtractorConfig { overrideTsconfig: configObject.compiler.overrideTsconfig, skipLibCheck: !!configObject.compiler.skipLibCheck, apiReportEnabled, - reportFilePath, - reportTempFilePath, + reportConfigs, + reportFolder, + reportTempFolder, apiReportIncludeForgottenExports, - docModelEnabled, + tagsToReport, + docModelGenerationOptions, apiJsonFilePath, docModelIncludeForgottenExports, projectFolderUrl, @@ -1067,72 +1164,235 @@ export class ExtractorConfig { return new ExtractorConfig({ ...extractorConfigParameters, tsdocConfigFile, tsdocConfiguration }); } - private static _resolvePathWithTokens( - fieldName: string, - value: string | undefined, - tokenContext: IExtractorConfigTokenContext - ): string { - value = ExtractorConfig._expandStringWithTokens(fieldName, value, tokenContext); - if (value !== '') { - value = path.resolve(tokenContext.projectFolder, value); + /** + * Gets the report configuration for the "complete" (default) report configuration, if one was specified. + */ + private _getCompleteReportConfig(): IExtractorConfigApiReport | undefined { + return this.reportConfigs.find((x) => x.variant === 'complete'); + } + + /** + * Returns true if the specified file path has the ".d.ts" file extension. + */ + public static hasDtsFileExtension(filePath: string): boolean { + return _declarationFileExtensionRegExp.test(filePath); + } +} + +function _resolveConfigFileRelativePaths(configFile: IConfigFile, currentConfigFolderPath: string): void { + if (configFile.projectFolder) { + configFile.projectFolder = _resolveConfigFileRelativePath( + 'projectFolder', + configFile.projectFolder, + currentConfigFolderPath + ); + } + + if (configFile.mainEntryPointFilePath) { + configFile.mainEntryPointFilePath = _resolveConfigFileRelativePath( + 'mainEntryPointFilePath', + configFile.mainEntryPointFilePath, + currentConfigFolderPath + ); + } + + if (configFile.compiler) { + if (configFile.compiler.tsconfigFilePath) { + configFile.compiler.tsconfigFilePath = _resolveConfigFileRelativePath( + 'tsconfigFilePath', + configFile.compiler.tsconfigFilePath, + currentConfigFolderPath + ); } - return value; } - private static _expandStringWithTokens( - fieldName: string, - value: string | undefined, - tokenContext: IExtractorConfigTokenContext - ): string { - value = value ? value.trim() : ''; - if (value !== '') { - value = Text.replaceAll(value, '', tokenContext.unscopedPackageName); - value = Text.replaceAll(value, '', tokenContext.packageName); - - const projectFolderToken: string = ''; - if (value.indexOf(projectFolderToken) === 0) { - // Replace "" at the start of a string - value = path.join(tokenContext.projectFolder, value.substr(projectFolderToken.length)); - } + if (configFile.apiReport) { + if (configFile.apiReport.reportFolder) { + configFile.apiReport.reportFolder = _resolveConfigFileRelativePath( + 'reportFolder', + configFile.apiReport.reportFolder, + currentConfigFolderPath + ); + } + if (configFile.apiReport.reportTempFolder) { + configFile.apiReport.reportTempFolder = _resolveConfigFileRelativePath( + 'reportTempFolder', + configFile.apiReport.reportTempFolder, + currentConfigFolderPath + ); + } + } - if (value.indexOf(projectFolderToken) >= 0) { - // If after all replacements, "" appears somewhere in the string, report an error - throw new Error( - `The "${fieldName}" value incorrectly uses the "" token.` + - ` It must appear at the start of the string.` - ); - } + if (configFile.docModel) { + if (configFile.docModel.apiJsonFilePath) { + configFile.docModel.apiJsonFilePath = _resolveConfigFileRelativePath( + 'apiJsonFilePath', + configFile.docModel.apiJsonFilePath, + currentConfigFolderPath + ); + } + } - if (value.indexOf('') >= 0) { - throw new Error(`The "${fieldName}" value incorrectly uses the "" token`); - } - ExtractorConfig._rejectAnyTokensInPath(value, fieldName); + if (configFile.dtsRollup) { + if (configFile.dtsRollup.untrimmedFilePath) { + configFile.dtsRollup.untrimmedFilePath = _resolveConfigFileRelativePath( + 'untrimmedFilePath', + configFile.dtsRollup.untrimmedFilePath, + currentConfigFolderPath + ); + } + if (configFile.dtsRollup.alphaTrimmedFilePath) { + configFile.dtsRollup.alphaTrimmedFilePath = _resolveConfigFileRelativePath( + 'alphaTrimmedFilePath', + configFile.dtsRollup.alphaTrimmedFilePath, + currentConfigFolderPath + ); + } + if (configFile.dtsRollup.betaTrimmedFilePath) { + configFile.dtsRollup.betaTrimmedFilePath = _resolveConfigFileRelativePath( + 'betaTrimmedFilePath', + configFile.dtsRollup.betaTrimmedFilePath, + currentConfigFolderPath + ); + } + if (configFile.dtsRollup.publicTrimmedFilePath) { + configFile.dtsRollup.publicTrimmedFilePath = _resolveConfigFileRelativePath( + 'publicTrimmedFilePath', + configFile.dtsRollup.publicTrimmedFilePath, + currentConfigFolderPath + ); } - return value; } - /** - * Returns true if the specified file path has the ".d.ts" file extension. - */ - public static hasDtsFileExtension(filePath: string): boolean { - return ExtractorConfig._declarationFileExtensionRegExp.test(filePath); + if (configFile.tsdocMetadata) { + if (configFile.tsdocMetadata.tsdocMetadataFilePath) { + configFile.tsdocMetadata.tsdocMetadataFilePath = _resolveConfigFileRelativePath( + 'tsdocMetadataFilePath', + configFile.tsdocMetadata.tsdocMetadataFilePath, + currentConfigFolderPath + ); + } } +} - /** - * Given a path string that may have originally contained expandable tokens such as `"` - * this reports an error if any token-looking substrings remain after expansion (e.g. `c:\blah\\blah`). - */ - private static _rejectAnyTokensInPath(value: string, fieldName: string): void { - if (value.indexOf('<') < 0 && value.indexOf('>') < 0) { - return; +function _resolveConfigFileRelativePath( + fieldName: string, + fieldValue: string, + currentConfigFolderPath: string +): string { + if (!path.isAbsolute(fieldValue)) { + if (fieldValue.indexOf('') !== 0) { + // If the path is not absolute and does not start with "", then resolve it relative + // to the folder of the config file that it appears in + return path.join(currentConfigFolderPath, fieldValue); } + } + + return fieldValue; +} - // Can we determine the name of a token? - const tokenRegExp: RegExp = /(\<[^<]*?\>)/; - const match: RegExpExecArray | null = tokenRegExp.exec(value); - if (match) { - throw new Error(`The "${fieldName}" value contains an unrecognized token "${match[1]}"`); +function _resolvePathWithTokens( + fieldName: string, + value: string | undefined, + tokenContext: IExtractorConfigTokenContext +): string { + value = _expandStringWithTokens(fieldName, value, tokenContext); + if (value !== '') { + value = path.resolve(tokenContext.projectFolder, value); + } + return value; +} + +function _expandStringWithTokens( + fieldName: string, + value: string | undefined, + tokenContext: IExtractorConfigTokenContext +): string { + value = value ? value.trim() : ''; + if (value !== '') { + value = Text.replaceAll(value, '', tokenContext.unscopedPackageName); + value = Text.replaceAll(value, '', tokenContext.packageName); + + const projectFolderToken: string = ''; + if (value.indexOf(projectFolderToken) === 0) { + // Replace "" at the start of a string + value = path.join(tokenContext.projectFolder, value.substr(projectFolderToken.length)); } - throw new Error(`The "${fieldName}" value contains extra token characters ("<" or ">"): ${value}`); + + if (value.indexOf(projectFolderToken) >= 0) { + // If after all replacements, "" appears somewhere in the string, report an error + throw new Error( + `The "${fieldName}" value incorrectly uses the "" token.` + + ` It must appear at the start of the string.` + ); + } + + if (value.indexOf('') >= 0) { + throw new Error(`The "${fieldName}" value incorrectly uses the "" token`); + } + _rejectAnyTokensInPath(value, fieldName); + } + return value; +} + +/** + * Given a path string that may have originally contained expandable tokens such as `"` + * this reports an error if any token-looking substrings remain after expansion (e.g. `c:\blah\\blah`). + */ +function _rejectAnyTokensInPath(value: string, fieldName: string): void { + if (value.indexOf('<') < 0 && value.indexOf('>') < 0) { + return; + } + + // Can we determine the name of a token? + const tokenRegExp: RegExp = /(\<[^<]*?\>)/; + const match: RegExpExecArray | null = tokenRegExp.exec(value); + if (match) { + throw new Error(`The "${fieldName}" value contains an unrecognized token "${match[1]}"`); + } + throw new Error(`The "${fieldName}" value contains extra token characters ("<" or ">"): ${value}`); +} + +const releaseTags: Set = new Set(['@public', '@alpha', '@beta', '@internal']); + +/** + * Validate {@link ExtractorConfig.tagsToReport}. + */ +function _validateTagsToReport( + tagsToReport: Record +): asserts tagsToReport is Record<`@${string}`, boolean> { + const includedReleaseTags: string[] = []; + const invalidTags: [string, string][] = []; // tag name, error + for (const tag of Object.keys(tagsToReport)) { + if (releaseTags.has(tag)) { + // If a release tags is specified, regardless of whether it is enabled, we will throw an error. + // Release tags must not be specified. + includedReleaseTags.push(tag); + } + + // If the tag is invalid, generate an error string from the inner error message. + try { + TSDocTagDefinition.validateTSDocTagName(tag); + } catch (error) { + invalidTags.push([tag, (error as Error).message]); + } + } + + const errorMessages: string[] = []; + for (const includedReleaseTag of includedReleaseTags) { + errorMessages.push( + `${includedReleaseTag}: Release tags are always included in API reports and must not be specified` + ); + } + for (const [invalidTag, innerError] of invalidTags) { + errorMessages.push(`${invalidTag}: ${innerError}`); + } + + if (errorMessages.length > 0) { + const errorMessage: string = [ + `"tagsToReport" contained one or more invalid tags:`, + ...errorMessages + ].join('\n\t- '); + throw new Error(errorMessage); } } diff --git a/apps/api-extractor/src/api/ExtractorLogLevel.ts b/apps/api-extractor/src/api/ExtractorLogLevel.ts index 40a687aa3a0..9670889bf78 100644 --- a/apps/api-extractor/src/api/ExtractorLogLevel.ts +++ b/apps/api-extractor/src/api/ExtractorLogLevel.ts @@ -9,7 +9,7 @@ * * @public */ -export const enum ExtractorLogLevel { +export enum ExtractorLogLevel { /** * The message will be displayed as an error. * diff --git a/apps/api-extractor/src/api/ExtractorMessage.ts b/apps/api-extractor/src/api/ExtractorMessage.ts index 4e64d31cdba..60e9e95e274 100644 --- a/apps/api-extractor/src/api/ExtractorMessage.ts +++ b/apps/api-extractor/src/api/ExtractorMessage.ts @@ -1,10 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as tsdoc from '@microsoft/tsdoc'; -import { ExtractorMessageId } from './ExtractorMessageId'; +import type * as tsdoc from '@microsoft/tsdoc'; + +import type { ExtractorMessageId } from './ExtractorMessageId'; import { ExtractorLogLevel } from './ExtractorLogLevel'; -import { ConsoleMessageId } from './ConsoleMessageId'; +import type { ConsoleMessageId } from './ConsoleMessageId'; import { SourceFileLocationFormatter } from '../analyzer/SourceFileLocationFormatter'; /** @@ -28,7 +29,7 @@ export interface IExtractorMessageProperties { * Specifies a category of messages for use with {@link ExtractorMessage}. * @public */ -export const enum ExtractorMessageCategory { +export enum ExtractorMessageCategory { /** * Messages originating from the TypeScript compiler. * @@ -131,16 +132,26 @@ export class ExtractorMessage { /** @internal */ public constructor(options: IExtractorMessageOptions) { - this.category = options.category; - this.messageId = options.messageId; - this.text = options.text; - this.sourceFilePath = options.sourceFilePath; - this.sourceFileLine = options.sourceFileLine; - this.sourceFileColumn = options.sourceFileColumn; - this.properties = options.properties || {}; + const { + category, + messageId, + text, + sourceFilePath, + sourceFileLine, + sourceFileColumn, + properties = {}, + logLevel = ExtractorLogLevel.None + } = options; + this.category = category; + this.messageId = messageId; + this.text = text; + this.sourceFilePath = sourceFilePath; + this.sourceFileLine = sourceFileLine; + this.sourceFileColumn = sourceFileColumn; + this.properties = properties; this._handled = false; - this._logLevel = options.logLevel || ExtractorLogLevel.None; + this._logLevel = logLevel; } /** diff --git a/apps/api-extractor/src/api/ExtractorMessageId.ts b/apps/api-extractor/src/api/ExtractorMessageId.ts index 9af37df1ff9..9e423d9a420 100644 --- a/apps/api-extractor/src/api/ExtractorMessageId.ts +++ b/apps/api-extractor/src/api/ExtractorMessageId.ts @@ -11,12 +11,31 @@ * * @public */ -export const enum ExtractorMessageId { +export enum ExtractorMessageId { /** * "The doc comment should not contain more than one release tag." */ ExtraReleaseTag = 'ae-extra-release-tag', + /** + * "Missing documentation for ___." + * @remarks + * The `ae-undocumented` message is only generated if the API report feature is enabled. + * + * Because the API report file already annotates undocumented items with `// (undocumented)`, + * the `ae-undocumented` message is not logged by default. To see it, add a setting such as: + * ```json + * "messages": { + * "extractorMessageReporting": { + * "ae-undocumented": { + * "logLevel": "warning" + * } + * } + * } + * ``` + */ + Undocumented = 'ae-undocumented', + /** * "This symbol has another declaration with a different release tag." */ @@ -106,6 +125,7 @@ export const enum ExtractorMessageId { export const allExtractorMessageIds: Set = new Set([ 'ae-extra-release-tag', + 'ae-undocumented', 'ae-different-release-tags', 'ae-incompatible-release-tags', 'ae-missing-release-tag', diff --git a/apps/api-extractor/src/api/IConfigFile.ts b/apps/api-extractor/src/api/IConfigFile.ts index 328f76f1445..c6c9d6e72c1 100644 --- a/apps/api-extractor/src/api/IConfigFile.ts +++ b/apps/api-extractor/src/api/IConfigFile.ts @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { EnumMemberOrder } from '@microsoft/api-extractor-model'; -import { ExtractorLogLevel } from './ExtractorLogLevel'; +import type { EnumMemberOrder } from '@microsoft/api-extractor-model'; + +import type { ExtractorLogLevel } from './ExtractorLogLevel'; /** * Determines how the TypeScript compiler engine will be invoked by API Extractor. @@ -48,6 +49,13 @@ export interface IConfigCompiler { skipLibCheck?: boolean; } +/** + * The allowed variations of API reports. + * + * @public + */ +export type ApiReportVariant = 'public' | 'beta' | 'alpha' | 'complete'; + /** * Configures how the API report files (*.api.md) will be generated. * @@ -63,14 +71,39 @@ export interface IConfigApiReport { enabled: boolean; /** - * The filename for the API report files. It will be combined with `reportFolder` or `reportTempFolder` to produce - * a full output filename. + * The base filename for the API report files, to be combined with {@link IConfigApiReport.reportFolder} or + * {@link IConfigApiReport.reportTempFolder} to produce the full file path. * * @remarks - * The file extension should be ".api.md", and the string should not contain a path separator such as `\` or `/`. + * The `reportFileName` should not include any path separators such as `\` or `/`. The `reportFileName` should + * not include a file extension, since API Extractor will automatically append an appropriate file extension such + * as `.api.md`. If the {@link IConfigApiReport.reportVariants} setting is used, then the file extension includes + * the variant name, for example `my-report.public.api.md` or `my-report.beta.api.md`. The `complete` variant always + * uses the simple extension `my-report.api.md`. + * + * Previous versions of API Extractor required `reportFileName` to include the `.api.md` extension explicitly; + * for backwards compatibility, that is still accepted but will be discarded before applying the above rules. + * + * @defaultValue `` */ reportFileName?: string; + /** + * The set of report variants to generate. + * + * @remarks + * To support different approval requirements for different API levels, multiple "variants" of the API report can + * be generated. The `reportVariants` setting specifies a list of variants to be generated. If omitted, + * by default only the `complete` variant will be generated, which includes all `@internal`, `@alpha`, `@beta`, + * and `@public` items. Other possible variants are `alpha` (`@alpha` + `@beta` + `@public`), + * `beta` (`@beta` + `@public`), and `public` (`@public only`). + * + * The resulting API report file names will be derived from the {@link IConfigApiReport.reportFileName}. + * + * @defaultValue `[ "complete" ]` + */ + reportVariants?: ApiReportVariant[]; + /** * Specifies the folder where the API report file is written. The file name portion is determined by * the `reportFileName` setting. @@ -107,8 +140,49 @@ export interface IConfigApiReport { * @defaultValue `false` */ includeForgottenExports?: boolean; + + /** + * Specifies a list of {@link https://tsdoc.org/ | TSDoc} tags that should be reported in the API report file for + * items whose documentation contains them. + * + * @remarks + * Tag names must begin with `@`. + * + * This list may include standard TSDoc tags as well as custom ones. + * For more information on defining custom TSDoc tags, see + * {@link https://api-extractor.com/pages/configs/tsdoc_json/#defining-your-own-tsdoc-tags | here}. + * + * Note that an item's release tag will always reported; this behavior cannot be overridden. + * + * @defaultValue `@sealed`, `@virtual`, `@override`, `@eventProperty`, and `@deprecated` + * + * @example Omitting default tags + * To omit the `@sealed` and `@virtual` tags from API reports, you would specify `tagsToReport` as follows: + * ```json + * "tagsToReport": { + * "@sealed": false, + * "@virtual": false + * } + * ``` + * + * @example Including additional tags + * To include additional tags to the set included in API reports, you could specify `tagsToReport` like this: + * ```json + * "tagsToReport": { + * "@customTag": true + * } + * ``` + * This will result in `@customTag` being included in addition to the default tags. + */ + tagsToReport?: Readonly>; } +/** + * The allowed release tags that can be used to mark API items. + * @public + */ +export type ReleaseTagForTrim = '@internal' | '@alpha' | '@beta' | '@public'; + /** * Configures how the doc model file (*.api.json) will be generated. * @@ -156,6 +230,13 @@ export interface IConfigDocModel { * Can be omitted if you don't need source code links in your API documentation reference. */ projectFolderUrl?: string; + + /** + * Specifies a list of release tags that will be trimmed from the doc model. + * + * @defaultValue `["@internal"]` + */ + releaseTagsToTrim?: ReleaseTagForTrim[]; } /** @@ -369,7 +450,7 @@ export interface IConfigFile { * * @remarks * - * The file extension must be ".d.ts" and not ".ts". + * The file extension must be a declaration file (e.g. ".d.ts", ".d.mts", ".d.\{extension\}.ts"), not ".ts". * The path is resolved relative to the "projectFolder" location. */ mainEntryPointFilePath: string; @@ -378,8 +459,17 @@ export interface IConfigFile { * A list of NPM package names whose exports should be treated as part of this package. * * @remarks + * Also supports glob patterns. + * Note: glob patterns will **only** be resolved against dependencies listed in the project's package.json file. + * + * * This is both a safety and a performance precaution. + * + * Exact package names will be applied against any dependency encountered while walking the type graph, regardless of + * dependencies listed in the package.json. + * + * @example * - * For example, suppose that Webpack is used to generate a distributed bundle for the project `library1`, + * Suppose that Webpack is used to generate a distributed bundle for the project `library1`, * and another NPM package `library2` is embedded in this bundle. Some types from `library2` may become part * of the exported API for `library1`, but by default API Extractor would generate a .d.ts rollup that explicitly * imports `library2`. To avoid this, we can specify: diff --git a/apps/api-extractor/src/api/test/Extractor-custom-tags.test.ts b/apps/api-extractor/src/api/test/Extractor-custom-tags.test.ts index 720f60a6e21..357e7c1dc00 100644 --- a/apps/api-extractor/src/api/test/Extractor-custom-tags.test.ts +++ b/apps/api-extractor/src/api/test/Extractor-custom-tags.test.ts @@ -2,7 +2,7 @@ // See LICENSE in the project root for license information. import { StandardTags } from '@microsoft/tsdoc'; -import * as path from 'path'; +import * as path from 'node:path'; import { ExtractorConfig } from '../ExtractorConfig'; @@ -10,7 +10,7 @@ const testDataFolder: string = path.join(__dirname, 'test-data'); describe('Extractor-custom-tags', () => { describe('should use a TSDocConfiguration', () => { - it.only("with custom TSDoc tags defined in the package's tsdoc.json", () => { + it("with custom TSDoc tags defined in the package's tsdoc.json", () => { const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare( path.join(testDataFolder, 'custom-tsdoc-tags/api-extractor.json') ); @@ -20,7 +20,7 @@ describe('Extractor-custom-tags', () => { expect(tsdocConfiguration.tryGetTagDefinition('@inline')).not.toBe(undefined); expect(tsdocConfiguration.tryGetTagDefinition('@modifier')).not.toBe(undefined); }); - it.only("with custom TSDoc tags enabled per the package's tsdoc.json", () => { + it("with custom TSDoc tags enabled per the package's tsdoc.json", () => { const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare( path.join(testDataFolder, 'custom-tsdoc-tags/api-extractor.json') ); @@ -33,7 +33,7 @@ describe('Extractor-custom-tags', () => { expect(tsdocConfiguration.isTagSupported(inline)).toBe(true); expect(tsdocConfiguration.isTagSupported(modifier)).toBe(false); }); - it.only("with standard tags and API Extractor custom tags defined and supported when the package's tsdoc.json extends API Extractor's tsdoc.json", () => { + it("with standard tags and API Extractor custom tags defined and supported when the package's tsdoc.json extends API Extractor's tsdoc.json", () => { const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare( path.join(testDataFolder, 'custom-tsdoc-tags/api-extractor.json') ); diff --git a/apps/api-extractor/src/api/test/ExtractorConfig-lookup.test.ts b/apps/api-extractor/src/api/test/ExtractorConfig-lookup.test.ts index 9fb0a6e6dde..e93e1d8b4ed 100644 --- a/apps/api-extractor/src/api/test/ExtractorConfig-lookup.test.ts +++ b/apps/api-extractor/src/api/test/ExtractorConfig-lookup.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; import { Path } from '@rushstack/node-core-library'; import { ExtractorConfig } from '../ExtractorConfig'; @@ -16,19 +16,19 @@ function expectEqualPaths(path1: string, path2: string): void { // Tests for expanding the "" token for the "projectFolder" setting in api-extractor.json describe(`${ExtractorConfig.name}.${ExtractorConfig.loadFileAndPrepare.name}`, () => { - it.only('config-lookup1: looks up ./api-extractor.json', () => { + it('config-lookup1: looks up ./api-extractor.json', () => { const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare( path.join(testDataFolder, 'config-lookup1/api-extractor.json') ); expectEqualPaths(extractorConfig.projectFolder, path.join(testDataFolder, 'config-lookup1')); }); - it.only('config-lookup2: looks up ./config/api-extractor.json', () => { + it('config-lookup2: looks up ./config/api-extractor.json', () => { const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare( path.join(testDataFolder, 'config-lookup2/config/api-extractor.json') ); expectEqualPaths(extractorConfig.projectFolder, path.join(testDataFolder, 'config-lookup2')); }); - it.only('config-lookup3a: looks up ./src/test/config/api-extractor.json', () => { + it('config-lookup3a: looks up ./src/test/config/api-extractor.json', () => { const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare( path.join(testDataFolder, 'config-lookup3/src/test/config/api-extractor.json') ); diff --git a/apps/api-extractor/src/api/test/ExtractorConfig-merge.test.ts b/apps/api-extractor/src/api/test/ExtractorConfig-merge.test.ts new file mode 100644 index 00000000000..d422c2fd54f --- /dev/null +++ b/apps/api-extractor/src/api/test/ExtractorConfig-merge.test.ts @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { ExtractorConfig } from '../ExtractorConfig'; + +const testDataFolder: string = path.join(__dirname, 'test-data'); + +// Tests verifying the merge behavior of ExtractorConfig.loadFile +describe(`${ExtractorConfig.name}.${ExtractorConfig.loadFile.name}`, () => { + it('array properties completely override array properties in the base config', () => { + const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare( + path.join(testDataFolder, 'override-array-properties', 'api-extractor.json') + ); + // Base config specifies: ["alpha", "beta", "public"] + // Derived config specifies: ["complete"] + // By default, lodash's merge() function would generate ["complete", "beta", "public"], + // but we instead want the derived config's array property to completely override that of the base. + expect(extractorConfig.reportConfigs).toEqual([ + { + variant: 'complete', + fileName: 'override-array-properties.api.md' + } + ]); + }); +}); diff --git a/apps/api-extractor/src/api/test/ExtractorConfig-tagsToReport.test.ts b/apps/api-extractor/src/api/test/ExtractorConfig-tagsToReport.test.ts new file mode 100644 index 00000000000..0ca8ae974a9 --- /dev/null +++ b/apps/api-extractor/src/api/test/ExtractorConfig-tagsToReport.test.ts @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { ExtractorConfig } from '../ExtractorConfig'; + +const testDataFolder: string = path.join(__dirname, 'test-data'); + +describe('ExtractorConfig-tagsToReport', () => { + it('tagsToReport merge correctly', () => { + const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare( + path.join(testDataFolder, 'tags-to-report/api-extractor.json') + ); + const { tagsToReport } = extractorConfig; + expect(tagsToReport).toEqual({ + '@deprecated': true, + '@eventProperty': true, + '@myCustomTag': true, + '@myCustomTag2': false, + '@override': false, + '@sealed': true, + '@virtual': true + }); + }); + it('Invalid tagsToReport values', () => { + const expectedErrorMessage = `"tagsToReport" contained one or more invalid tags: +\t- @public: Release tags are always included in API reports and must not be specified +\t- @-invalid-tag-2: A TSDoc tag name must start with a letter and contain only letters and numbers`; + expect(() => + ExtractorConfig.loadFileAndPrepare( + path.join(testDataFolder, 'invalid-tags-to-report/api-extractor.json') + ) + ).toThrow(expectedErrorMessage); + }); +}); diff --git a/apps/api-extractor/src/api/test/ExtractorConfig.test.ts b/apps/api-extractor/src/api/test/ExtractorConfig.test.ts new file mode 100644 index 00000000000..1ab62e2c1c3 --- /dev/null +++ b/apps/api-extractor/src/api/test/ExtractorConfig.test.ts @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { ExtractorConfig } from '../ExtractorConfig'; + +describe('ExtractorConfig', () => { + describe('hasDtsFileExtension', () => { + it.each([ + ['test.ts', false], + ['test.cts', false], + ['test.mts', false], + ['test.d.ts', true], + ['test.d.mts', true], + ['test.d.cts', true], + ['test.css', false], + ['test.css.ts', false], + ['test.css.d.ts', true], + ['test.d.css.ts', true], + ['test.json', false], + ['test.json.ts', false], + ['test.json.d.ts', true], + ['test.d.json.ts', true], + ['video.d.mp4.ts', true], + ['font.d.woff2.ts', true], + ['model.d.3mf.ts', true] + ])('file "%s" has dts file extension equals "%s"', (file, expected) => { + const result = ExtractorConfig.hasDtsFileExtension(file); + expect(result).toEqual(expected); + }); + }); +}); diff --git a/apps/api-extractor/src/api/test/test-data/config-lookup1/tsconfig.json b/apps/api-extractor/src/api/test/test-data/config-lookup1/tsconfig.json index 845c0343e3c..a141c3e954b 100644 --- a/apps/api-extractor/src/api/test/test-data/config-lookup1/tsconfig.json +++ b/apps/api-extractor/src/api/test/test-data/config-lookup1/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -14,12 +14,11 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest", "node"], + "types": ["jest", "node"], "module": "commonjs", "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/apps/api-extractor/src/api/test/test-data/config-lookup2/tsconfig.json b/apps/api-extractor/src/api/test/test-data/config-lookup2/tsconfig.json index 845c0343e3c..a141c3e954b 100644 --- a/apps/api-extractor/src/api/test/test-data/config-lookup2/tsconfig.json +++ b/apps/api-extractor/src/api/test/test-data/config-lookup2/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -14,12 +14,11 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest", "node"], + "types": ["jest", "node"], "module": "commonjs", "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/apps/api-extractor/src/api/test/test-data/config-lookup3/src/test/tsconfig.json b/apps/api-extractor/src/api/test/test-data/config-lookup3/src/test/tsconfig.json index 845c0343e3c..a141c3e954b 100644 --- a/apps/api-extractor/src/api/test/test-data/config-lookup3/src/test/tsconfig.json +++ b/apps/api-extractor/src/api/test/test-data/config-lookup3/src/test/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -14,12 +14,11 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest", "node"], + "types": ["jest", "node"], "module": "commonjs", "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/apps/api-extractor/src/api/test/test-data/config-lookup3/tsconfig.json b/apps/api-extractor/src/api/test/test-data/config-lookup3/tsconfig.json index 845c0343e3c..a141c3e954b 100644 --- a/apps/api-extractor/src/api/test/test-data/config-lookup3/tsconfig.json +++ b/apps/api-extractor/src/api/test/test-data/config-lookup3/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -14,12 +14,11 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest", "node"], + "types": ["jest", "node"], "module": "commonjs", "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/apps/api-extractor/src/api/test/test-data/custom-tsdoc-tags/tsconfig.json b/apps/api-extractor/src/api/test/test-data/custom-tsdoc-tags/tsconfig.json index 845c0343e3c..a141c3e954b 100644 --- a/apps/api-extractor/src/api/test/test-data/custom-tsdoc-tags/tsconfig.json +++ b/apps/api-extractor/src/api/test/test-data/custom-tsdoc-tags/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -14,12 +14,11 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest", "node"], + "types": ["jest", "node"], "module": "commonjs", "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/apps/api-extractor/src/api/test/test-data/invalid-tags-to-report/README.md b/apps/api-extractor/src/api/test/test-data/invalid-tags-to-report/README.md new file mode 100644 index 00000000000..48328444ea7 --- /dev/null +++ b/apps/api-extractor/src/api/test/test-data/invalid-tags-to-report/README.md @@ -0,0 +1 @@ +Test case to ensure that merging of `apiReport.tagsToReport` is correct. diff --git a/apps/api-extractor/src/api/test/test-data/invalid-tags-to-report/api-extractor.json b/apps/api-extractor/src/api/test/test-data/invalid-tags-to-report/api-extractor.json new file mode 100644 index 00000000000..17c021c3e64 --- /dev/null +++ b/apps/api-extractor/src/api/test/test-data/invalid-tags-to-report/api-extractor.json @@ -0,0 +1,23 @@ +{ + "$schema": "../../../../schemas/api-extractor.schema.json", + + "mainEntryPointFilePath": "index.d.ts", + + "apiReport": { + "enabled": true, + "tagsToReport": { + "@validTag1": true, // Valid custom tag + "@-invalid-tag-2": true, // Invalid tag - invalid characters + "@public": false, // Release tags must not be specified + "@override": false // Valid (override base tag) + } + }, + + "docModel": { + "enabled": true + }, + + "dtsRollup": { + "enabled": true + } +} diff --git a/apps/api-extractor/src/api/test/test-data/invalid-tags-to-report/index.d.ts b/apps/api-extractor/src/api/test/test-data/invalid-tags-to-report/index.d.ts new file mode 100644 index 00000000000..4bd8276f349 --- /dev/null +++ b/apps/api-extractor/src/api/test/test-data/invalid-tags-to-report/index.d.ts @@ -0,0 +1 @@ +// empty file diff --git a/apps/api-extractor/src/api/test/test-data/invalid-tags-to-report/package.json b/apps/api-extractor/src/api/test/test-data/invalid-tags-to-report/package.json new file mode 100644 index 00000000000..dc906e7d69b --- /dev/null +++ b/apps/api-extractor/src/api/test/test-data/invalid-tags-to-report/package.json @@ -0,0 +1,4 @@ +{ + "name": "tags-to-report", + "version": "1.0.0" +} diff --git a/apps/api-extractor/src/api/test/test-data/override-array-properties/README.md b/apps/api-extractor/src/api/test/test-data/override-array-properties/README.md new file mode 100644 index 00000000000..22f9b309e36 --- /dev/null +++ b/apps/api-extractor/src/api/test/test-data/override-array-properties/README.md @@ -0,0 +1,2 @@ +Reproduction of #4786. +Merging of configs should *not* merge arrays - the overriding config file's array properties should completely overwrite the base config's array properties. diff --git a/apps/api-extractor/src/api/test/test-data/override-array-properties/api-extractor-base.json b/apps/api-extractor/src/api/test/test-data/override-array-properties/api-extractor-base.json new file mode 100644 index 00000000000..f41027c72e4 --- /dev/null +++ b/apps/api-extractor/src/api/test/test-data/override-array-properties/api-extractor-base.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + "mainEntryPointFilePath": "index.d.ts", + + "apiReport": { + "enabled": true, + "reportVariants": ["alpha", "beta", "public"] + }, + + "docModel": { + "enabled": true + }, + + "dtsRollup": { + "enabled": true + } +} diff --git a/apps/api-extractor/src/api/test/test-data/override-array-properties/api-extractor.json b/apps/api-extractor/src/api/test/test-data/override-array-properties/api-extractor.json new file mode 100644 index 00000000000..fd4839e09fd --- /dev/null +++ b/apps/api-extractor/src/api/test/test-data/override-array-properties/api-extractor.json @@ -0,0 +1,7 @@ +{ + "extends": "./api-extractor-base.json", + + "apiReport": { + "reportVariants": ["complete"] + } +} diff --git a/apps/api-extractor/src/api/test/test-data/override-array-properties/index.d.ts b/apps/api-extractor/src/api/test/test-data/override-array-properties/index.d.ts new file mode 100644 index 00000000000..4bd8276f349 --- /dev/null +++ b/apps/api-extractor/src/api/test/test-data/override-array-properties/index.d.ts @@ -0,0 +1 @@ +// empty file diff --git a/apps/api-extractor/src/api/test/test-data/override-array-properties/package.json b/apps/api-extractor/src/api/test/test-data/override-array-properties/package.json new file mode 100644 index 00000000000..29ba54f2ce1 --- /dev/null +++ b/apps/api-extractor/src/api/test/test-data/override-array-properties/package.json @@ -0,0 +1,4 @@ +{ + "name": "override-array-properties", + "version": "1.0.0" +} diff --git a/apps/api-extractor/src/api/test/test-data/tags-to-report/README.md b/apps/api-extractor/src/api/test/test-data/tags-to-report/README.md new file mode 100644 index 00000000000..48328444ea7 --- /dev/null +++ b/apps/api-extractor/src/api/test/test-data/tags-to-report/README.md @@ -0,0 +1 @@ +Test case to ensure that merging of `apiReport.tagsToReport` is correct. diff --git a/apps/api-extractor/src/api/test/test-data/tags-to-report/api-extractor-base.json b/apps/api-extractor/src/api/test/test-data/tags-to-report/api-extractor-base.json new file mode 100644 index 00000000000..6ab11ff3957 --- /dev/null +++ b/apps/api-extractor/src/api/test/test-data/tags-to-report/api-extractor-base.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + "mainEntryPointFilePath": "index.d.ts", + + "apiReport": { + "enabled": true + }, + + "docModel": { + "enabled": true + }, + + "dtsRollup": { + "enabled": true + } +} diff --git a/apps/api-extractor/src/api/test/test-data/tags-to-report/api-extractor.json b/apps/api-extractor/src/api/test/test-data/tags-to-report/api-extractor.json new file mode 100644 index 00000000000..10df8061cce --- /dev/null +++ b/apps/api-extractor/src/api/test/test-data/tags-to-report/api-extractor.json @@ -0,0 +1,11 @@ +{ + "extends": "./api-extractor-base.json", + + "apiReport": { + "tagsToReport": { + "@myCustomTag": true, // Enable reporting of custom tag + "@override": false, // Disable default reporting of `@override` tag + "@myCustomTag2": false // Disable reporting of custom tag (not included by base config) + } + } +} diff --git a/apps/api-extractor/src/api/test/test-data/tags-to-report/index.d.ts b/apps/api-extractor/src/api/test/test-data/tags-to-report/index.d.ts new file mode 100644 index 00000000000..4bd8276f349 --- /dev/null +++ b/apps/api-extractor/src/api/test/test-data/tags-to-report/index.d.ts @@ -0,0 +1 @@ +// empty file diff --git a/apps/api-extractor/src/api/test/test-data/tags-to-report/package.json b/apps/api-extractor/src/api/test/test-data/tags-to-report/package.json new file mode 100644 index 00000000000..dc906e7d69b --- /dev/null +++ b/apps/api-extractor/src/api/test/test-data/tags-to-report/package.json @@ -0,0 +1,4 @@ +{ + "name": "tags-to-report", + "version": "1.0.0" +} diff --git a/apps/api-extractor/src/cli/ApiExtractorCommandLine.ts b/apps/api-extractor/src/cli/ApiExtractorCommandLine.ts index 7480f460a3e..2c434e32cec 100644 --- a/apps/api-extractor/src/cli/ApiExtractorCommandLine.ts +++ b/apps/api-extractor/src/cli/ApiExtractorCommandLine.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors'; -import * as os from 'os'; +import * as os from 'node:os'; -import { CommandLineParser, CommandLineFlagParameter } from '@rushstack/ts-command-line'; -import { InternalError } from '@rushstack/node-core-library'; +import { CommandLineParser, type CommandLineFlagParameter } from '@rushstack/ts-command-line'; +import { AlreadyReportedError, InternalError } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; import { RunAction } from './RunAction'; import { InitAction } from './InitAction'; @@ -32,21 +32,24 @@ export class ApiExtractorCommandLine extends CommandLineParser { }); } - protected onExecute(): Promise { - // override + protected override async onExecuteAsync(): Promise { if (this._debugParameter.value) { InternalError.breakInDebugger = true; } - return super.onExecute().catch((error) => { - if (this._debugParameter.value) { - console.error(os.EOL + error.stack); - } else { - console.error(os.EOL + colors.red('ERROR: ' + error.message.trim())); + process.exitCode = 1; + try { + await super.onExecuteAsync(); + process.exitCode = 0; + } catch (error) { + if (!(error instanceof AlreadyReportedError)) { + if (this._debugParameter.value) { + console.error(os.EOL + error.stack); + } else { + console.error(os.EOL + Colorize.red('ERROR: ' + error.message.trim())); + } } - - process.exitCode = 1; - }); + } } private _populateActions(): void { diff --git a/apps/api-extractor/src/cli/InitAction.ts b/apps/api-extractor/src/cli/InitAction.ts index f97561fa9ab..834e2a7660f 100644 --- a/apps/api-extractor/src/cli/InitAction.ts +++ b/apps/api-extractor/src/cli/InitAction.ts @@ -1,12 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors'; -import * as path from 'path'; +import * as path from 'node:path'; + import { FileSystem } from '@rushstack/node-core-library'; import { CommandLineAction } from '@rushstack/ts-command-line'; +import { Colorize } from '@rushstack/terminal'; -import { ApiExtractorCommandLine } from './ApiExtractorCommandLine'; +import type { ApiExtractorCommandLine } from './ApiExtractorCommandLine'; import { ExtractorConfig } from '../api/ExtractorConfig'; export class InitAction extends CommandLineAction { @@ -21,18 +22,17 @@ export class InitAction extends CommandLineAction { }); } - protected async onExecute(): Promise { - // override + protected override async onExecuteAsync(): Promise { const inputFilePath: string = path.resolve(__dirname, '../schemas/api-extractor-template.json'); const outputFilePath: string = path.resolve(ExtractorConfig.FILENAME); if (FileSystem.exists(outputFilePath)) { - console.log(colors.red('The output file already exists:')); + console.log(Colorize.red('The output file already exists:')); console.log('\n ' + outputFilePath + '\n'); throw new Error('Unable to write output file'); } - console.log(colors.green('Writing file: ') + outputFilePath); + console.log(Colorize.green('Writing file: ') + outputFilePath); FileSystem.copyFile({ sourcePath: inputFilePath, destinationPath: outputFilePath diff --git a/apps/api-extractor/src/cli/RunAction.ts b/apps/api-extractor/src/cli/RunAction.ts index e9c4c878441..d0e60bb9326 100644 --- a/apps/api-extractor/src/cli/RunAction.ts +++ b/apps/api-extractor/src/cli/RunAction.ts @@ -1,28 +1,34 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors'; -import * as os from 'os'; -import * as path from 'path'; -import { PackageJsonLookup, FileSystem, IPackageJson, Path } from '@rushstack/node-core-library'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import { + PackageJsonLookup, + FileSystem, + type IPackageJson, + Path, + AlreadyReportedError +} from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; import { CommandLineAction, - CommandLineStringParameter, - CommandLineFlagParameter + type CommandLineStringParameter, + type CommandLineFlagParameter } from '@rushstack/ts-command-line'; -import { Extractor, ExtractorResult } from '../api/Extractor'; - -import { ApiExtractorCommandLine } from './ApiExtractorCommandLine'; -import { ExtractorConfig, IExtractorConfigPrepareOptions } from '../api/ExtractorConfig'; +import { Extractor, type ExtractorResult } from '../api/Extractor'; +import type { ApiExtractorCommandLine } from './ApiExtractorCommandLine'; +import { ExtractorConfig, type IExtractorConfigPrepareOptions } from '../api/ExtractorConfig'; export class RunAction extends CommandLineAction { private readonly _configFileParameter: CommandLineStringParameter; - private readonly _localParameter: CommandLineFlagParameter; - private readonly _verboseParameter: CommandLineFlagParameter; + private readonly _localFlag: CommandLineFlagParameter; + private readonly _verboseFlag: CommandLineFlagParameter; private readonly _diagnosticsParameter: CommandLineFlagParameter; - private readonly _typescriptCompilerFolder: CommandLineStringParameter; + private readonly _typescriptCompilerFolderParameter: CommandLineStringParameter; + private readonly _printApiReportDiffFlag: CommandLineFlagParameter; public constructor(parser: ApiExtractorCommandLine) { super({ @@ -38,7 +44,7 @@ export class RunAction extends CommandLineAction { description: `Use the specified ${ExtractorConfig.FILENAME} file path, rather than guessing its location` }); - this._localParameter = this.defineFlagParameter({ + this._localFlag = this.defineFlagParameter({ parameterLongName: '--local', parameterShortName: '-l', description: @@ -48,7 +54,7 @@ export class RunAction extends CommandLineAction { ' report file is automatically copied in a local build.' }); - this._verboseParameter = this.defineFlagParameter({ + this._verboseFlag = this.defineFlagParameter({ parameterLongName: '--verbose', parameterShortName: '-v', description: 'Show additional informational messages in the output.' @@ -61,7 +67,7 @@ export class RunAction extends CommandLineAction { ' This flag also enables the "--verbose" flag.' }); - this._typescriptCompilerFolder = this.defineStringParameter({ + this._typescriptCompilerFolderParameter = this.defineStringParameter({ parameterLongName: '--typescript-compiler-folder', argumentName: 'PATH', description: @@ -71,14 +77,21 @@ export class RunAction extends CommandLineAction { ' "--typescriptCompilerFolder" option to specify the folder path where you installed the TypeScript package,' + " and API Extractor's compiler will use those system typings instead." }); + + this._printApiReportDiffFlag = this.defineFlagParameter({ + parameterLongName: '--print-api-report-diff', + description: + 'If provided, then any differences between the actual and expected API reports will be ' + + 'printed on the console. Note that the diff is not printed if the expected API report file has not been ' + + 'created yet.' + }); } - protected async onExecute(): Promise { - // override + protected override async onExecuteAsync(): Promise { const lookup: PackageJsonLookup = new PackageJsonLookup(); let configFilename: string; - let typescriptCompilerFolder: string | undefined = this._typescriptCompilerFolder.value; + let typescriptCompilerFolder: string | undefined = this._typescriptCompilerFolderParameter.value; if (typescriptCompilerFolder) { typescriptCompilerFolder = path.normalize(typescriptCompilerFolder); @@ -89,17 +102,17 @@ export class RunAction extends CommandLineAction { : undefined; if (!typescriptCompilerPackageJson) { throw new Error( - `The path specified in the ${this._typescriptCompilerFolder.longName} parameter is not a package.` + `The path specified in the ${this._typescriptCompilerFolderParameter.longName} parameter is not a package.` ); } else if (typescriptCompilerPackageJson.name !== 'typescript') { throw new Error( - `The path specified in the ${this._typescriptCompilerFolder.longName} parameter is not a TypeScript` + + `The path specified in the ${this._typescriptCompilerFolderParameter.longName} parameter is not a TypeScript` + ' compiler package.' ); } } else { throw new Error( - `The path specified in the ${this._typescriptCompilerFolder.longName} parameter does not exist.` + `The path specified in the ${this._typescriptCompilerFolderParameter.longName} parameter does not exist.` ); } } @@ -132,22 +145,22 @@ export class RunAction extends CommandLineAction { } const extractorResult: ExtractorResult = Extractor.invoke(extractorConfig, { - localBuild: this._localParameter.value, - showVerboseMessages: this._verboseParameter.value, + localBuild: this._localFlag.value, + showVerboseMessages: this._verboseFlag.value, showDiagnostics: this._diagnosticsParameter.value, - typescriptCompilerFolder: typescriptCompilerFolder + typescriptCompilerFolder: typescriptCompilerFolder, + printApiReportDiff: this._printApiReportDiffFlag.value }); if (extractorResult.succeeded) { console.log(os.EOL + 'API Extractor completed successfully'); } else { - process.exitCode = 1; - if (extractorResult.errorCount > 0) { - console.log(os.EOL + colors.red('API Extractor completed with errors')); + console.log(os.EOL + Colorize.red('API Extractor completed with errors')); } else { - console.log(os.EOL + colors.yellow('API Extractor completed with warnings')); + console.log(os.EOL + Colorize.yellow('API Extractor completed with warnings')); } + throw new AlreadyReportedError(); } } } diff --git a/apps/api-extractor/src/collector/ApiItemMetadata.ts b/apps/api-extractor/src/collector/ApiItemMetadata.ts index 738e58619dc..a25b5da858a 100644 --- a/apps/api-extractor/src/collector/ApiItemMetadata.ts +++ b/apps/api-extractor/src/collector/ApiItemMetadata.ts @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as tsdoc from '@microsoft/tsdoc'; -import { ReleaseTag } from '@microsoft/api-extractor-model'; +import type * as tsdoc from '@microsoft/tsdoc'; +import type { ReleaseTag } from '@microsoft/api-extractor-model'; + import { VisitorState } from './VisitorState'; /** @@ -75,19 +76,41 @@ export class ApiItemMetadata { */ public tsdocComment: tsdoc.DocComment | undefined; - // Assigned by DocCommentEnhancer - public needsDocumentation: boolean = true; + /** + * Tracks whether or not the associated API item is known to be missing sufficient documentation. + * + * @remarks + * + * An "undocumented" item is one whose TSDoc comment which either does not contain a summary comment block, or + * has an `@inheritDoc` tag that resolves to another "undocumented" API member. + * + * If there is any ambiguity (e.g. if an `@inheritDoc` comment points to an external API member, whose documentation, + * we can't parse), "undocumented" will be `false`. + * + * @remarks Assigned by {@link DocCommentEnhancer}. + */ + public undocumented: boolean = true; public docCommentEnhancerVisitorState: VisitorState = VisitorState.Unvisited; public constructor(options: IApiItemMetadataOptions) { - this.declaredReleaseTag = options.declaredReleaseTag; - this.effectiveReleaseTag = options.effectiveReleaseTag; - this.releaseTagSameAsParent = options.releaseTagSameAsParent; - this.isEventProperty = options.isEventProperty; - this.isOverride = options.isOverride; - this.isSealed = options.isSealed; - this.isVirtual = options.isVirtual; - this.isPreapproved = options.isPreapproved; + const { + declaredReleaseTag, + effectiveReleaseTag, + releaseTagSameAsParent, + isEventProperty, + isOverride, + isSealed, + isVirtual, + isPreapproved + } = options; + this.declaredReleaseTag = declaredReleaseTag; + this.effectiveReleaseTag = effectiveReleaseTag; + this.releaseTagSameAsParent = releaseTagSameAsParent; + this.isEventProperty = isEventProperty; + this.isOverride = isOverride; + this.isSealed = isSealed; + this.isVirtual = isVirtual; + this.isPreapproved = isPreapproved; } } diff --git a/apps/api-extractor/src/collector/Collector.ts b/apps/api-extractor/src/collector/Collector.ts index 4e6e1efa70a..2e6a374f11d 100644 --- a/apps/api-extractor/src/collector/Collector.ts +++ b/apps/api-extractor/src/collector/Collector.ts @@ -2,31 +2,38 @@ // See LICENSE in the project root for license information. import * as ts from 'typescript'; +import { minimatch } from 'minimatch'; + import * as tsdoc from '@microsoft/tsdoc'; -import { PackageJsonLookup, Sort, InternalError } from '@rushstack/node-core-library'; import { ReleaseTag } from '@microsoft/api-extractor-model'; +import { + PackageJsonLookup, + Sort, + InternalError, + type INodePackageJson, + PackageName +} from '@rushstack/node-core-library'; import { ExtractorMessageId } from '../api/ExtractorMessageId'; - import { CollectorEntity } from './CollectorEntity'; import { AstSymbolTable } from '../analyzer/AstSymbolTable'; -import { AstEntity } from '../analyzer/AstEntity'; -import { AstModule, AstModuleExportInfo } from '../analyzer/AstModule'; +import type { AstEntity } from '../analyzer/AstEntity'; +import type { AstModule, IAstModuleExportInfo } from '../analyzer/AstModule'; import { AstSymbol } from '../analyzer/AstSymbol'; -import { AstDeclaration } from '../analyzer/AstDeclaration'; +import type { AstDeclaration } from '../analyzer/AstDeclaration'; import { TypeScriptHelpers } from '../analyzer/TypeScriptHelpers'; import { WorkingPackage } from './WorkingPackage'; import { PackageDocComment } from '../aedoc/PackageDocComment'; -import { DeclarationMetadata, InternalDeclarationMetadata } from './DeclarationMetadata'; -import { ApiItemMetadata, IApiItemMetadataOptions } from './ApiItemMetadata'; +import { type DeclarationMetadata, InternalDeclarationMetadata } from './DeclarationMetadata'; +import { ApiItemMetadata, type IApiItemMetadataOptions } from './ApiItemMetadata'; import { SymbolMetadata } from './SymbolMetadata'; -import { TypeScriptInternals, IGlobalVariableAnalyzer } from '../analyzer/TypeScriptInternals'; -import { MessageRouter } from './MessageRouter'; +import { TypeScriptInternals, type IGlobalVariableAnalyzer } from '../analyzer/TypeScriptInternals'; +import type { MessageRouter } from './MessageRouter'; import { AstReferenceResolver } from '../analyzer/AstReferenceResolver'; import { ExtractorConfig } from '../api/ExtractorConfig'; import { AstNamespaceImport } from '../analyzer/AstNamespaceImport'; import { AstImport } from '../analyzer/AstImport'; -import { SourceMapper } from './SourceMapper'; +import type { SourceMapper } from './SourceMapper'; /** * Options for Collector constructor. @@ -100,11 +107,12 @@ export class Collector { public constructor(options: ICollectorOptions) { this.packageJsonLookup = new PackageJsonLookup(); - this._program = options.program; - this.extractorConfig = options.extractorConfig; - this.sourceMapper = options.sourceMapper; + const { program, extractorConfig, sourceMapper, messageRouter } = options; + this._program = program; + this.extractorConfig = extractorConfig; + this.sourceMapper = sourceMapper; - const entryPointSourceFile: ts.SourceFile | undefined = options.program.getSourceFile( + const entryPointSourceFile: ts.SourceFile | undefined = program.getSourceFile( this.extractorConfig.mainEntryPointFilePath ); @@ -124,15 +132,19 @@ export class Collector { entryPointSourceFile }); - this.messageRouter = options.messageRouter; + this.messageRouter = messageRouter; - this.program = options.program; - this.typeChecker = options.program.getTypeChecker(); + this.program = program; + this.typeChecker = program.getTypeChecker(); this.globalVariableAnalyzer = TypeScriptInternals.getGlobalVariableAnalyzer(this.program); this._tsdocParser = new tsdoc.TSDocParser(this.extractorConfig.tsdocConfiguration); - this.bundledPackageNames = new Set(this.extractorConfig.bundledPackages); + // Resolve package name patterns and store concrete set of bundled package dependency names + this.bundledPackageNames = _resolveBundledPackagePatterns( + this.extractorConfig.bundledPackages, + this.extractorConfig.packageJson + ); this.astSymbolTable = new AstSymbolTable( this.program, @@ -146,7 +158,7 @@ export class Collector { this._cachedOverloadIndexesByDeclaration = new Map(); } - /** + /**a * Returns a list of names (e.g. "example-library") that should appear in a reference like this: * * ``` @@ -253,12 +265,12 @@ export class Collector { this.workingPackage.tsdocComment = this.workingPackage.tsdocParserContext!.docComment; } - const astModuleExportInfo: AstModuleExportInfo = + const { exportedLocalEntities, starExportedExternalModules, visitedAstModules }: IAstModuleExportInfo = this.astSymbolTable.fetchAstModuleExportInfo(astEntryPoint); // Create a CollectorEntity for each top-level export. const processedAstEntities: AstEntity[] = []; - for (const [exportName, astEntity] of astModuleExportInfo.exportedLocalEntities) { + for (const [exportName, astEntity] of exportedLocalEntities) { this._createCollectorEntity(astEntity, exportName); processedAstEntities.push(astEntity); } @@ -273,9 +285,33 @@ export class Collector { } } + // Ensure references are collected from any intermediate files that + // only include exports + const nonExternalSourceFiles: Set = new Set(); + for (const { sourceFile, isExternal } of visitedAstModules) { + if (!nonExternalSourceFiles.has(sourceFile) && !isExternal) { + nonExternalSourceFiles.add(sourceFile); + } + } + + // Here, we're collecting reference directives from all non-external source files + // that were encountered while looking for exports, but only those references that + // were explicitly written by the developer and marked with the `preserve="true"` + // attribute. In TS >= 5.5, only references that are explicitly authored and marked + // with `preserve="true"` are included in the output. See https://github.com/microsoft/TypeScript/pull/57681 + // + // The `_collectReferenceDirectives` function pulls in all references in files that + // contain definitions, but does not examine files that only reexport from other + // files. Here, we're looking through files that were missed by `_collectReferenceDirectives`, + // but only collecting references that were explicitly marked with `preserve="true"`. + // It is intuitive for developers to include references that they explicitly want part of + // their public API in a file like the entrypoint, which is likely to only contain reexports, + // and this picks those up. + this._collectReferenceDirectivesFromSourceFiles(nonExternalSourceFiles, true); + this._makeUniqueNames(); - for (const starExportedExternalModule of astModuleExportInfo.starExportedExternalModules) { + for (const starExportedExternalModule of starExportedExternalModules) { if (starExportedExternalModule.externalModulePath !== undefined) { this._starExportedExternalModulePaths.push(starExportedExternalModule.externalModulePath); } @@ -479,7 +515,7 @@ export class Collector { } if (astEntity instanceof AstNamespaceImport) { - const astModuleExportInfo: AstModuleExportInfo = astEntity.fetchAstModuleExportInfo(this); + const astModuleExportInfo: IAstModuleExportInfo = astEntity.fetchAstModuleExportInfo(this); const parentEntity: CollectorEntity | undefined = this._entitiesByAstEntity.get(astEntity); if (!parentEntity) { // This should never happen, as we've already created entities for all AstNamespaceImports. @@ -932,44 +968,131 @@ export class Collector { } private _collectReferenceDirectives(astEntity: AstEntity): void { + // Here, we're collecting reference directives from source files that contain extracted + // definitions (i.e. - files that contain `export class ...`, `export interface ...`, ...). + // These references may or may not include the `preserve="true" attribute. In TS < 5.5, + // references that end up in .D.TS files may or may not be explicity written by the developer. + // In TS >= 5.5, only references that are explicitly authored and are marked with + // `preserve="true"` are included in the output. See https://github.com/microsoft/TypeScript/pull/57681 + // + // The calls to `_collectReferenceDirectivesFromSourceFiles` in this function are + // preserving existing behavior, which is to include all reference directives + // regardless of whether they are explicitly authored or not, but only in files that + // contain definitions. + if (astEntity instanceof AstSymbol) { const sourceFiles: ts.SourceFile[] = astEntity.astDeclarations.map((astDeclaration) => astDeclaration.declaration.getSourceFile() ); - return this._collectReferenceDirectivesFromSourceFiles(sourceFiles); + return this._collectReferenceDirectivesFromSourceFiles(sourceFiles, false); } if (astEntity instanceof AstNamespaceImport) { const sourceFiles: ts.SourceFile[] = [astEntity.astModule.sourceFile]; - return this._collectReferenceDirectivesFromSourceFiles(sourceFiles); + return this._collectReferenceDirectivesFromSourceFiles(sourceFiles, false); } } - private _collectReferenceDirectivesFromSourceFiles(sourceFiles: ts.SourceFile[]): void { + private _collectReferenceDirectivesFromSourceFiles( + sourceFiles: Iterable, + onlyIncludeExplicitlyPreserved: boolean + ): void { const seenFilenames: Set = new Set(); for (const sourceFile of sourceFiles) { - if (sourceFile && sourceFile.fileName) { - if (!seenFilenames.has(sourceFile.fileName)) { - seenFilenames.add(sourceFile.fileName); - - for (const typeReferenceDirective of sourceFile.typeReferenceDirectives) { - const name: string = sourceFile.text.substring( - typeReferenceDirective.pos, - typeReferenceDirective.end + if (sourceFile?.fileName) { + const { + fileName, + typeReferenceDirectives, + libReferenceDirectives, + text: sourceFileText + } = sourceFile; + if (!seenFilenames.has(fileName)) { + seenFilenames.add(fileName); + + for (const typeReferenceDirective of typeReferenceDirectives) { + const name: string | undefined = this._getReferenceDirectiveFromSourceFile( + sourceFileText, + typeReferenceDirective, + onlyIncludeExplicitlyPreserved ); - this._dtsTypeReferenceDirectives.add(name); + if (name) { + this._dtsTypeReferenceDirectives.add(name); + } } - for (const libReferenceDirective of sourceFile.libReferenceDirectives) { - const name: string = sourceFile.text.substring( - libReferenceDirective.pos, - libReferenceDirective.end + for (const libReferenceDirective of libReferenceDirectives) { + const reference: string | undefined = this._getReferenceDirectiveFromSourceFile( + sourceFileText, + libReferenceDirective, + onlyIncludeExplicitlyPreserved ); - this._dtsLibReferenceDirectives.add(name); + if (reference) { + this._dtsLibReferenceDirectives.add(reference); + } } } } } } + + private _getReferenceDirectiveFromSourceFile( + sourceFileText: string, + { pos, end, preserve }: ts.FileReference, + onlyIncludeExplicitlyPreserved: boolean + ): string | undefined { + const reference: string = sourceFileText.substring(pos, end); + if (preserve || !onlyIncludeExplicitlyPreserved) { + return reference; + } + } +} + +/** + * Resolve provided `bundledPackages` names and glob patterns to a list of explicit package names. + * + * @remarks + * Explicit package names will be included in the output unconditionally. However, wildcard patterns will + * only be matched against the various dependencies listed in the provided package.json (if there was one). + * Patterns will be matched against `dependencies`, `devDependencies`, `optionalDependencies`, and `peerDependencies`. + * + * @param bundledPackages - The list of package names and/or glob patterns to resolve. + * @param packageJson - The package.json of the package being processed (if there is one). + * @returns The set of resolved package names to be bundled during analysis. + */ +function _resolveBundledPackagePatterns( + bundledPackages: string[], + packageJson: INodePackageJson | undefined +): ReadonlySet { + if (bundledPackages.length === 0) { + // If no `bundledPackages` were specified, then there is nothing to resolve. + // Return an empty set. + return new Set(); + } + + // Accumulate all declared dependencies. + // Any wildcard patterns in `bundledPackages` will be resolved against these. + const dependencyNames: Set = new Set(); + Object.keys(packageJson?.dependencies ?? {}).forEach((dep) => dependencyNames.add(dep)); + Object.keys(packageJson?.devDependencies ?? {}).forEach((dep) => dependencyNames.add(dep)); + Object.keys(packageJson?.peerDependencies ?? {}).forEach((dep) => dependencyNames.add(dep)); + Object.keys(packageJson?.optionalDependencies ?? {}).forEach((dep) => dependencyNames.add(dep)); + + // The set of resolved package names to be populated and returned + const resolvedPackageNames: Set = new Set(); + + for (const packageNameOrPattern of bundledPackages) { + // If the string is an exact package name, use it regardless of package.json contents + if (PackageName.isValidName(packageNameOrPattern)) { + resolvedPackageNames.add(packageNameOrPattern); + } else { + // If the entry isn't an exact package name, assume glob pattern and search for matches + for (const dependencyName of dependencyNames) { + if (minimatch(dependencyName, packageNameOrPattern)) { + resolvedPackageNames.add(dependencyName); + } + } + } + } + return resolvedPackageNames; } diff --git a/apps/api-extractor/src/collector/CollectorEntity.ts b/apps/api-extractor/src/collector/CollectorEntity.ts index db52e1f4ad9..36bb07353d9 100644 --- a/apps/api-extractor/src/collector/CollectorEntity.ts +++ b/apps/api-extractor/src/collector/CollectorEntity.ts @@ -3,10 +3,12 @@ import * as ts from 'typescript'; +import { Sort } from '@rushstack/node-core-library'; + import { AstSymbol } from '../analyzer/AstSymbol'; import { Collector } from './Collector'; -import { Sort } from '@rushstack/node-core-library'; -import { AstEntity } from '../analyzer/AstEntity'; +import type { AstEntity } from '../analyzer/AstEntity'; +import { AstNamespaceExport } from '../analyzer/AstNamespaceExport'; /** * This is a data structure used by the Collector to track an AstEntity that may be emitted in the *.d.ts file. @@ -82,6 +84,11 @@ export class CollectorEntity { * such as "export class X { }" instead of "export { X }". */ public get shouldInlineExport(): boolean { + // We export the namespace directly + if (this.astEntity instanceof AstNamespaceExport) { + return true; + } + // We don't inline an AstImport if (this.astEntity instanceof AstSymbol) { // We don't inline a symbol with more than one exported name diff --git a/apps/api-extractor/src/collector/DeclarationMetadata.ts b/apps/api-extractor/src/collector/DeclarationMetadata.ts index f4510795a8b..f3d44de3a17 100644 --- a/apps/api-extractor/src/collector/DeclarationMetadata.ts +++ b/apps/api-extractor/src/collector/DeclarationMetadata.ts @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as tsdoc from '@microsoft/tsdoc'; -import { AstDeclaration } from '../analyzer/AstDeclaration'; +import type * as tsdoc from '@microsoft/tsdoc'; + +import type { AstDeclaration } from '../analyzer/AstDeclaration'; /** * Stores the Collector's additional analysis for a specific `AstDeclaration` signature. This object is assigned to diff --git a/apps/api-extractor/src/collector/MessageRouter.ts b/apps/api-extractor/src/collector/MessageRouter.ts index 1bb5dd8d3d4..fe13466fe31 100644 --- a/apps/api-extractor/src/collector/MessageRouter.ts +++ b/apps/api-extractor/src/collector/MessageRouter.ts @@ -1,22 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors'; import * as ts from 'typescript'; -import * as tsdoc from '@microsoft/tsdoc'; + +import type * as tsdoc from '@microsoft/tsdoc'; import { Sort, InternalError } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; import { AstDeclaration } from '../analyzer/AstDeclaration'; -import { AstSymbol } from '../analyzer/AstSymbol'; +import type { AstSymbol } from '../analyzer/AstSymbol'; import { ExtractorMessage, ExtractorMessageCategory, - IExtractorMessageOptions, - IExtractorMessageProperties + type IExtractorMessageOptions, + type IExtractorMessageProperties } from '../api/ExtractorMessage'; -import { ExtractorMessageId, allExtractorMessageIds } from '../api/ExtractorMessageId'; -import { IExtractorMessagesConfig, IConfigMessageReportingRule } from '../api/IConfigFile'; -import { ISourceLocation, SourceMapper } from './SourceMapper'; +import { type ExtractorMessageId, allExtractorMessageIds } from '../api/ExtractorMessageId'; +import type { IExtractorMessagesConfig, IConfigMessageReportingRule } from '../api/IConfigFile'; +import type { ISourceLocation, SourceMapper } from './SourceMapper'; import { ExtractorLogLevel } from '../api/ExtractorLogLevel'; import { ConsoleMessageId } from '../api/ConsoleMessageId'; @@ -85,19 +86,28 @@ export class MessageRouter { public readonly showDiagnostics: boolean; public constructor(options: IMessageRouterOptions) { - this._workingPackageFolder = options.workingPackageFolder; - this._messageCallback = options.messageCallback; + const { + workingPackageFolder, + messageCallback, + sourceMapper, + tsdocConfiguration, + showVerboseMessages, + showDiagnostics, + messagesConfig + } = options; + this._workingPackageFolder = workingPackageFolder; + this._messageCallback = messageCallback; this._messages = []; this._associatedMessagesForAstDeclaration = new Map(); - this._sourceMapper = options.sourceMapper; - this._tsdocConfiguration = options.tsdocConfiguration; + this._sourceMapper = sourceMapper; + this._tsdocConfiguration = tsdocConfiguration; // showDiagnostics implies showVerboseMessages - this.showVerboseMessages = options.showVerboseMessages || options.showDiagnostics; - this.showDiagnostics = options.showDiagnostics; + this.showVerboseMessages = showVerboseMessages || showDiagnostics; + this.showDiagnostics = showDiagnostics; - this._applyMessagesConfig(options.messagesConfig); + this._applyMessagesConfig(messagesConfig); } /** @@ -106,7 +116,7 @@ export class MessageRouter { private _applyMessagesConfig(messagesConfig: IExtractorMessagesConfig): void { if (messagesConfig.compilerMessageReporting) { for (const messageId of Object.getOwnPropertyNames(messagesConfig.compilerMessageReporting)) { - const reportingRule: IReportingRule = MessageRouter._getNormalizedRule( + const reportingRule: IReportingRule = _getNormalizedRule( messagesConfig.compilerMessageReporting[messageId] ); @@ -125,7 +135,7 @@ export class MessageRouter { if (messagesConfig.extractorMessageReporting) { for (const messageId of Object.getOwnPropertyNames(messagesConfig.extractorMessageReporting)) { - const reportingRule: IReportingRule = MessageRouter._getNormalizedRule( + const reportingRule: IReportingRule = _getNormalizedRule( messagesConfig.extractorMessageReporting[messageId] ); @@ -149,7 +159,7 @@ export class MessageRouter { if (messagesConfig.tsdocMessageReporting) { for (const messageId of Object.getOwnPropertyNames(messagesConfig.tsdocMessageReporting)) { - const reportingRule: IReportingRule = MessageRouter._getNormalizedRule( + const reportingRule: IReportingRule = _getNormalizedRule( messagesConfig.tsdocMessageReporting[messageId] ); @@ -172,13 +182,6 @@ export class MessageRouter { } } - private static _getNormalizedRule(rule: IConfigMessageReportingRule): IReportingRule { - return { - logLevel: rule.logLevel || 'none', - addToApiReportFile: rule.addToApiReportFile || false - }; - } - public get messages(): ReadonlyArray { return this._messages; } @@ -293,55 +296,7 @@ export class MessageRouter { const keyNamesToOmit: Set = new Set(options.keyNamesToOmit); - return MessageRouter._buildJsonDumpObject(input, keyNamesToOmit); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private static _buildJsonDumpObject(input: any, keyNamesToOmit: Set): any | undefined { - if (input === null || input === undefined) { - return null; // JSON uses null instead of undefined - } - - switch (typeof input) { - case 'boolean': - case 'number': - case 'string': - return input; - case 'object': - if (Array.isArray(input)) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const outputArray: any[] = []; - for (const element of input) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const serializedElement: any = MessageRouter._buildJsonDumpObject(element, keyNamesToOmit); - if (serializedElement !== undefined) { - outputArray.push(serializedElement); - } - } - return outputArray; - } - - const outputObject: object = {}; - for (const key of Object.getOwnPropertyNames(input)) { - if (keyNamesToOmit.has(key)) { - continue; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const value: any = input[key]; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const serializedValue: any = MessageRouter._buildJsonDumpObject(value, keyNamesToOmit); - - if (serializedValue !== undefined) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (outputObject as any)[key] = serializedValue; - } - } - return outputObject; - } - - return undefined; + return _buildJsonDumpObject(input, keyNamesToOmit); } /** @@ -421,7 +376,7 @@ export class MessageRouter { /** * This returns all remaining messages that were flagged with `addToApiReportFile`, but which were not - * retreieved using `fetchAssociatedMessagesForReviewFile()`. + * retrieved using `fetchAssociatedMessagesForReviewFile()`. */ public fetchUnassociatedMessagesForReviewFile(): ExtractorMessage[] { const messagesForApiReportFile: ExtractorMessage[] = []; @@ -597,17 +552,17 @@ export class MessageRouter { switch (message.logLevel) { case ExtractorLogLevel.Error: - console.error(colors.red('Error: ' + messageText)); + console.error(Colorize.red('Error: ' + messageText)); break; case ExtractorLogLevel.Warning: - console.warn(colors.yellow('Warning: ' + messageText)); + console.warn(Colorize.yellow('Warning: ' + messageText)); break; case ExtractorLogLevel.Info: console.log(messageText); break; case ExtractorLogLevel.Verbose: if (this.showVerboseMessages) { - console.log(colors.cyan(messageText)); + console.log(Colorize.cyan(messageText)); } break; default: @@ -656,3 +611,58 @@ export class MessageRouter { }); } } + +function _getNormalizedRule(rule: IConfigMessageReportingRule): IReportingRule { + return { + logLevel: rule.logLevel || 'none', + addToApiReportFile: rule.addToApiReportFile || false + }; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function _buildJsonDumpObject(input: any, keyNamesToOmit: Set): any | undefined { + if (input === null || input === undefined) { + return null; // JSON uses null instead of undefined + } + + switch (typeof input) { + case 'boolean': + case 'number': + case 'string': + return input; + case 'object': + if (Array.isArray(input)) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const outputArray: any[] = []; + for (const element of input) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const serializedElement: any = _buildJsonDumpObject(element, keyNamesToOmit); + if (serializedElement !== undefined) { + outputArray.push(serializedElement); + } + } + return outputArray; + } + + const outputObject: object = {}; + for (const key of Object.getOwnPropertyNames(input)) { + if (keyNamesToOmit.has(key)) { + continue; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const value: any = input[key]; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const serializedValue: any = _buildJsonDumpObject(value, keyNamesToOmit); + + if (serializedValue !== undefined) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (outputObject as any)[key] = serializedValue; + } + } + return outputObject; + } + + return undefined; +} diff --git a/apps/api-extractor/src/collector/SourceMapper.ts b/apps/api-extractor/src/collector/SourceMapper.ts index e566c774b50..d0859a4f395 100644 --- a/apps/api-extractor/src/collector/SourceMapper.ts +++ b/apps/api-extractor/src/collector/SourceMapper.ts @@ -1,17 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { SourceMapConsumer, RawSourceMap, MappingItem, Position } from 'source-map'; +import * as path from 'node:path'; + +import { SourceMapConsumer, type RawSourceMap, type MappingItem, type Position } from 'source-map'; +import type ts from 'typescript'; + import { FileSystem, InternalError, JsonFile, NewlineKind } from '@rushstack/node-core-library'; -import ts from 'typescript'; interface ISourceMap { sourceMapConsumer: SourceMapConsumer; // SourceMapConsumer.originalPositionFor() is useless because the mapping contains numerous gaps, // and the API provides no way to find the nearest match. So instead we extract all the mapping items - // and search them using SourceMapper._findNearestMappingItem(). + // and search them using _findNearestMappingItem(). mappingItems: MappingItem[]; } @@ -103,13 +105,10 @@ export class SourceMapper { const sourceMap: ISourceMap | null = this._getSourceMap(sourceFilePath); if (!sourceMap) return; - const nearestMappingItem: MappingItem | undefined = SourceMapper._findNearestMappingItem( - sourceMap.mappingItems, - { - line: sourceFileLine, - column: sourceFileColumn - } - ); + const nearestMappingItem: MappingItem | undefined = _findNearestMappingItem(sourceMap.mappingItems, { + line: sourceFileLine, + column: sourceFileColumn + }); if (!nearestMappingItem) return; @@ -220,46 +219,43 @@ export class SourceMapper { return sourceMap; } +} - // The `mappingItems` array is sorted by generatedLine/generatedColumn (GENERATED_ORDER). - // The _findNearestMappingItem() lookup is a simple binary search that returns the previous item - // if there is no exact match. - private static _findNearestMappingItem( - mappingItems: MappingItem[], - position: Position - ): MappingItem | undefined { - if (mappingItems.length === 0) { - return undefined; - } +// The `mappingItems` array is sorted by generatedLine/generatedColumn (GENERATED_ORDER). +// The _findNearestMappingItem() lookup is a simple binary search that returns the previous item +// if there is no exact match. +function _findNearestMappingItem(mappingItems: MappingItem[], position: Position): MappingItem | undefined { + if (mappingItems.length === 0) { + return undefined; + } - let startIndex: number = 0; - let endIndex: number = mappingItems.length - 1; + let startIndex: number = 0; + let endIndex: number = mappingItems.length - 1; - while (startIndex <= endIndex) { - const middleIndex: number = startIndex + Math.floor((endIndex - startIndex) / 2); + while (startIndex <= endIndex) { + const middleIndex: number = startIndex + Math.floor((endIndex - startIndex) / 2); - const diff: number = SourceMapper._compareMappingItem(mappingItems[middleIndex], position); + const diff: number = _compareMappingItem(mappingItems[middleIndex], position); - if (diff < 0) { - startIndex = middleIndex + 1; - } else if (diff > 0) { - endIndex = middleIndex - 1; - } else { - // Exact match - return mappingItems[middleIndex]; - } + if (diff < 0) { + startIndex = middleIndex + 1; + } else if (diff > 0) { + endIndex = middleIndex - 1; + } else { + // Exact match + return mappingItems[middleIndex]; } - - // If we didn't find an exact match, then endIndex < startIndex. - // Take endIndex because it's the smaller value. - return mappingItems[endIndex]; } - private static _compareMappingItem(mappingItem: MappingItem, position: Position): number { - const diff: number = mappingItem.generatedLine - position.line; - if (diff !== 0) { - return diff; - } - return mappingItem.generatedColumn - position.column; + // If we didn't find an exact match, then endIndex < startIndex. + // Take endIndex because it's the smaller value. + return mappingItems[endIndex]; +} + +function _compareMappingItem(mappingItem: MappingItem, position: Position): number { + const diff: number = mappingItem.generatedLine - position.line; + if (diff !== 0) { + return diff; } + return mappingItem.generatedColumn - position.column; } diff --git a/apps/api-extractor/src/collector/SymbolMetadata.ts b/apps/api-extractor/src/collector/SymbolMetadata.ts index d28d731cc4d..8a467219360 100644 --- a/apps/api-extractor/src/collector/SymbolMetadata.ts +++ b/apps/api-extractor/src/collector/SymbolMetadata.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ReleaseTag } from '@microsoft/api-extractor-model'; +import type { ReleaseTag } from '@microsoft/api-extractor-model'; /** * Constructor parameters for `SymbolMetadata`. diff --git a/apps/api-extractor/src/collector/WorkingPackage.ts b/apps/api-extractor/src/collector/WorkingPackage.ts index 7cd76fad9c8..d85e88e4c11 100644 --- a/apps/api-extractor/src/collector/WorkingPackage.ts +++ b/apps/api-extractor/src/collector/WorkingPackage.ts @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as ts from 'typescript'; -import * as tsdoc from '@microsoft/tsdoc'; +import type * as ts from 'typescript'; -import { INodePackageJson } from '@rushstack/node-core-library'; +import type * as tsdoc from '@microsoft/tsdoc'; +import type { INodePackageJson } from '@rushstack/node-core-library'; /** * Constructor options for WorkingPackage diff --git a/apps/api-extractor/src/enhancers/DocCommentEnhancer.ts b/apps/api-extractor/src/enhancers/DocCommentEnhancer.ts index ba96c9db803..17b228afe35 100644 --- a/apps/api-extractor/src/enhancers/DocCommentEnhancer.ts +++ b/apps/api-extractor/src/enhancers/DocCommentEnhancer.ts @@ -2,13 +2,14 @@ // See LICENSE in the project root for license information. import * as ts from 'typescript'; + import * as tsdoc from '@microsoft/tsdoc'; +import { ReleaseTag } from '@microsoft/api-extractor-model'; -import { Collector } from '../collector/Collector'; +import type { Collector } from '../collector/Collector'; import { AstSymbol } from '../analyzer/AstSymbol'; -import { AstDeclaration } from '../analyzer/AstDeclaration'; -import { ApiItemMetadata } from '../collector/ApiItemMetadata'; -import { ReleaseTag } from '@microsoft/api-extractor-model'; +import type { AstDeclaration } from '../analyzer/AstDeclaration'; +import type { ApiItemMetadata } from '../collector/ApiItemMetadata'; import { ExtractorMessageId } from '../api/ExtractorMessageId'; import { VisitorState } from '../collector/VisitorState'; import { ResolverFailure } from '../analyzer/AstReferenceResolver'; @@ -73,7 +74,7 @@ export class DocCommentEnhancer { // Constructors always do pretty much the same thing, so it's annoying to require people to write // descriptions for them. Instead, if the constructor lacks a TSDoc summary, then API Extractor // will auto-generate one. - metadata.needsDocumentation = false; + metadata.undocumented = false; // The class that contains this constructor const classDeclaration: AstDeclaration = astDeclaration.parent!; @@ -131,16 +132,43 @@ export class DocCommentEnhancer { ); } return; - } - - if (metadata.tsdocComment) { - // Require the summary to contain at least 10 non-spacing characters - metadata.needsDocumentation = !tsdoc.PlainTextEmitter.hasAnyTextContent( - metadata.tsdocComment.summarySection, - 10 - ); } else { - metadata.needsDocumentation = true; + // For non-constructor items, we will determine whether or not the item is documented as follows: + // 1. If it contains a summary section with at least 10 characters, then it is considered "documented". + // 2. If it contains an @inheritDoc tag, then it *may* be considered "documented", depending on whether or not + // the tag resolves to a "documented" API member. + // - Note: for external members, we cannot currently determine this, so we will consider the "documented" + // status to be unknown. + if (metadata.tsdocComment) { + if (tsdoc.PlainTextEmitter.hasAnyTextContent(metadata.tsdocComment.summarySection, 10)) { + // If the API item has a summary comment block (with at least 10 characters), mark it as "documented". + metadata.undocumented = false; + } else if (metadata.tsdocComment.inheritDocTag) { + if ( + this._refersToDeclarationInWorkingPackage( + metadata.tsdocComment.inheritDocTag.declarationReference + ) + ) { + // If the API item has an `@inheritDoc` comment that points to an API item in the working package, + // then the documentation contents should have already been copied from the target via `_applyInheritDoc`. + // The continued existence of the tag indicates that the declaration reference was invalid, and not + // documentation contents could be copied. + // An analyzer issue will have already been logged for this. + // We will treat such an API as "undocumented". + metadata.undocumented = true; + } else { + // If the API item has an `@inheritDoc` comment that points to an external API item, we cannot currently + // determine whether or not the target is "documented", so we cannot say definitively that this is "undocumented". + metadata.undocumented = false; + } + } else { + // If the API item has neither a summary comment block, nor an `@inheritDoc` comment, mark it as "undocumented". + metadata.undocumented = true; + } + } else { + // If there is no tsdoc comment at all, mark "undocumented". + metadata.undocumented = true; + } } } @@ -157,10 +185,7 @@ export class DocCommentEnhancer { // Is it referring to the working package? If not, we don't do any link validation, because // AstReferenceResolver doesn't support it yet (but ModelReferenceResolver does of course). // Tracked by: https://github.com/microsoft/rushstack/issues/1195 - if ( - node.codeDestination.packageName === undefined || - node.codeDestination.packageName === this._collector.workingPackage.name - ) { + if (this._refersToDeclarationInWorkingPackage(node.codeDestination)) { const referencedAstDeclaration: AstDeclaration | ResolverFailure = this._collector.astReferenceResolver.resolve(node.codeDestination); @@ -196,14 +221,8 @@ export class DocCommentEnhancer { return; } - // Is it referring to the working package? - if ( - !( - inheritDocTag.declarationReference.packageName === undefined || - inheritDocTag.declarationReference.packageName === this._collector.workingPackage.name - ) - ) { - // It's referencing an external package, so skip this inheritDoc tag, since AstReferenceResolver doesn't + if (!this._refersToDeclarationInWorkingPackage(inheritDocTag.declarationReference)) { + // The `@inheritDoc` tag is referencing an external package. Skip it, since AstReferenceResolver doesn't // support it yet. As a workaround, this tag will get handled later by api-documenter. // Tracked by: https://github.com/microsoft/rushstack/issues/1195 return; @@ -249,4 +268,16 @@ export class DocCommentEnhancer { targetDocComment.inheritDocTag = undefined; } + + /** + * Determines whether or not the provided declaration reference points to an item in the working package. + */ + private _refersToDeclarationInWorkingPackage( + declarationReference: tsdoc.DocDeclarationReference | undefined + ): boolean { + return ( + declarationReference?.packageName === undefined || + declarationReference.packageName === this._collector.workingPackage.name + ); + } } diff --git a/apps/api-extractor/src/enhancers/ValidationEnhancer.ts b/apps/api-extractor/src/enhancers/ValidationEnhancer.ts index 8795a4ec1ed..31d4862748f 100644 --- a/apps/api-extractor/src/enhancers/ValidationEnhancer.ts +++ b/apps/api-extractor/src/enhancers/ValidationEnhancer.ts @@ -1,20 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import * as ts from 'typescript'; -import { Collector } from '../collector/Collector'; +import { ReleaseTag } from '@microsoft/api-extractor-model'; + +import type { Collector } from '../collector/Collector'; import { AstSymbol } from '../analyzer/AstSymbol'; -import { AstDeclaration } from '../analyzer/AstDeclaration'; -import { ApiItemMetadata } from '../collector/ApiItemMetadata'; -import { SymbolMetadata } from '../collector/SymbolMetadata'; -import { CollectorEntity } from '../collector/CollectorEntity'; +import type { AstDeclaration } from '../analyzer/AstDeclaration'; +import type { ApiItemMetadata } from '../collector/ApiItemMetadata'; +import type { SymbolMetadata } from '../collector/SymbolMetadata'; +import type { CollectorEntity } from '../collector/CollectorEntity'; import { ExtractorMessageId } from '../api/ExtractorMessageId'; -import { ReleaseTag } from '@microsoft/api-extractor-model'; import { AstNamespaceImport } from '../analyzer/AstNamespaceImport'; -import { AstModuleExportInfo } from '../analyzer/AstModule'; -import { AstEntity } from '../analyzer/AstEntity'; +import type { IAstModuleExportInfo } from '../analyzer/AstModule'; +import type { AstEntity } from '../analyzer/AstEntity'; export class ValidationEnhancer { public static analyze(collector: Collector): void { @@ -37,17 +39,17 @@ export class ValidationEnhancer { const astSymbol: AstSymbol = entity.astEntity; astSymbol.forEachDeclarationRecursive((astDeclaration: AstDeclaration) => { - ValidationEnhancer._checkReferences(collector, astDeclaration, alreadyWarnedEntities); + _checkReferences(collector, astDeclaration, alreadyWarnedEntities); }); const symbolMetadata: SymbolMetadata = collector.fetchSymbolMetadata(astSymbol); - ValidationEnhancer._checkForInternalUnderscore(collector, entity, astSymbol, symbolMetadata); - ValidationEnhancer._checkForInconsistentReleaseTags(collector, astSymbol, symbolMetadata); + _checkForInternalUnderscore(collector, entity, astSymbol, symbolMetadata); + _checkForInconsistentReleaseTags(collector, astSymbol, symbolMetadata); } else if (entity.astEntity instanceof AstNamespaceImport) { // A namespace created using "import * as ___ from ___" const astNamespaceImport: AstNamespaceImport = entity.astEntity; - const astModuleExportInfo: AstModuleExportInfo = + const astModuleExportInfo: IAstModuleExportInfo = astNamespaceImport.fetchAstModuleExportInfo(collector); for (const namespaceMemberAstEntity of astModuleExportInfo.exportedLocalEntities.values()) { @@ -55,248 +57,245 @@ export class ValidationEnhancer { const astSymbol: AstSymbol = namespaceMemberAstEntity; astSymbol.forEachDeclarationRecursive((astDeclaration: AstDeclaration) => { - ValidationEnhancer._checkReferences(collector, astDeclaration, alreadyWarnedEntities); + _checkReferences(collector, astDeclaration, alreadyWarnedEntities); }); const symbolMetadata: SymbolMetadata = collector.fetchSymbolMetadata(astSymbol); - // (Don't apply ValidationEnhancer._checkForInternalUnderscore() for AstNamespaceImport members) + // (Don't apply _checkForInternalUnderscore() for AstNamespaceImport members) - ValidationEnhancer._checkForInconsistentReleaseTags(collector, astSymbol, symbolMetadata); + _checkForInconsistentReleaseTags(collector, astSymbol, symbolMetadata); } } } } } +} - private static _checkForInternalUnderscore( - collector: Collector, - collectorEntity: CollectorEntity, - astSymbol: AstSymbol, - symbolMetadata: SymbolMetadata - ): void { - let needsUnderscore: boolean = false; - - if (symbolMetadata.maxEffectiveReleaseTag === ReleaseTag.Internal) { - if (!astSymbol.parentAstSymbol) { - // If it's marked as @internal and has no parent, then it needs an underscore. - // We use maxEffectiveReleaseTag because a merged declaration would NOT need an underscore in a case like this: - // - // /** @public */ - // export enum X { } - // - // /** @internal */ - // export namespace X { } - // - // (The above normally reports an error "ae-different-release-tags", but that may be suppressed.) +function _checkForInternalUnderscore( + collector: Collector, + collectorEntity: CollectorEntity, + astSymbol: AstSymbol, + symbolMetadata: SymbolMetadata +): void { + let needsUnderscore: boolean = false; + + if (symbolMetadata.maxEffectiveReleaseTag === ReleaseTag.Internal) { + if (!astSymbol.parentAstSymbol) { + // If it's marked as @internal and has no parent, then it needs an underscore. + // We use maxEffectiveReleaseTag because a merged declaration would NOT need an underscore in a case like this: + // + // /** @public */ + // export enum X { } + // + // /** @internal */ + // export namespace X { } + // + // (The above normally reports an error "ae-different-release-tags", but that may be suppressed.) + needsUnderscore = true; + } else { + // If it's marked as @internal and the parent isn't obviously already @internal, then it needs an underscore. + // + // For example, we WOULD need an underscore for a merged declaration like this: + // + // /** @internal */ + // export namespace X { + // export interface _Y { } + // } + // + // /** @public */ + // export class X { + // /** @internal */ + // public static _Y(): void { } // <==== different from parent + // } + const parentSymbolMetadata: SymbolMetadata = collector.fetchSymbolMetadata(astSymbol); + if (parentSymbolMetadata.maxEffectiveReleaseTag > ReleaseTag.Internal) { needsUnderscore = true; - } else { - // If it's marked as @internal and the parent isn't obviously already @internal, then it needs an underscore. - // - // For example, we WOULD need an underscore for a merged declaration like this: - // - // /** @internal */ - // export namespace X { - // export interface _Y { } - // } - // - // /** @public */ - // export class X { - // /** @internal */ - // public static _Y(): void { } // <==== different from parent - // } - const parentSymbolMetadata: SymbolMetadata = collector.fetchSymbolMetadata(astSymbol); - if (parentSymbolMetadata.maxEffectiveReleaseTag > ReleaseTag.Internal) { - needsUnderscore = true; - } } } + } - if (needsUnderscore) { - for (const exportName of collectorEntity.exportNames) { - if (exportName[0] !== '_') { - collector.messageRouter.addAnalyzerIssue( - ExtractorMessageId.InternalMissingUnderscore, - `The name "${exportName}" should be prefixed with an underscore` + - ` because the declaration is marked as @internal`, - astSymbol, - { exportName } - ); - } + if (needsUnderscore) { + for (const exportName of collectorEntity.exportNames) { + if (exportName[0] !== '_') { + collector.messageRouter.addAnalyzerIssue( + ExtractorMessageId.InternalMissingUnderscore, + `The name "${exportName}" should be prefixed with an underscore` + + ` because the declaration is marked as @internal`, + astSymbol, + { exportName } + ); } } } +} - private static _checkForInconsistentReleaseTags( - collector: Collector, - astSymbol: AstSymbol, - symbolMetadata: SymbolMetadata - ): void { - if (astSymbol.isExternal) { - // For now, don't report errors for external code. If the developer cares about it, they should run - // API Extractor separately on the external project - return; - } - - // Normally we will expect all release tags to be the same. Arbitrarily we choose the maxEffectiveReleaseTag - // as the thing they should all match. - const expectedEffectiveReleaseTag: ReleaseTag = symbolMetadata.maxEffectiveReleaseTag; +function _checkForInconsistentReleaseTags( + collector: Collector, + astSymbol: AstSymbol, + symbolMetadata: SymbolMetadata +): void { + if (astSymbol.isExternal) { + // For now, don't report errors for external code. If the developer cares about it, they should run + // API Extractor separately on the external project + return; + } - // This is set to true if we find a declaration whose release tag is different from expectedEffectiveReleaseTag - let mixedReleaseTags: boolean = false; + // Normally we will expect all release tags to be the same. Arbitrarily we choose the maxEffectiveReleaseTag + // as the thing they should all match. + const expectedEffectiveReleaseTag: ReleaseTag = symbolMetadata.maxEffectiveReleaseTag; - // This is set to false if we find a declaration that is not a function/method overload - let onlyFunctionOverloads: boolean = true; + // This is set to true if we find a declaration whose release tag is different from expectedEffectiveReleaseTag + let mixedReleaseTags: boolean = false; - // This is set to true if we find a declaration that is @internal - let anyInternalReleaseTags: boolean = false; + // This is set to false if we find a declaration that is not a function/method overload + let onlyFunctionOverloads: boolean = true; - for (const astDeclaration of astSymbol.astDeclarations) { - const apiItemMetadata: ApiItemMetadata = collector.fetchApiItemMetadata(astDeclaration); - const effectiveReleaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; + // This is set to true if we find a declaration that is @internal + let anyInternalReleaseTags: boolean = false; - switch (astDeclaration.declaration.kind) { - case ts.SyntaxKind.FunctionDeclaration: - case ts.SyntaxKind.MethodDeclaration: - break; - default: - onlyFunctionOverloads = false; - } + for (const astDeclaration of astSymbol.astDeclarations) { + const apiItemMetadata: ApiItemMetadata = collector.fetchApiItemMetadata(astDeclaration); + const effectiveReleaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; + + switch (astDeclaration.declaration.kind) { + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.MethodDeclaration: + break; + default: + onlyFunctionOverloads = false; + } - if (effectiveReleaseTag !== expectedEffectiveReleaseTag) { - mixedReleaseTags = true; - } + if (effectiveReleaseTag !== expectedEffectiveReleaseTag) { + mixedReleaseTags = true; + } - if (effectiveReleaseTag === ReleaseTag.Internal) { - anyInternalReleaseTags = true; - } + if (effectiveReleaseTag === ReleaseTag.Internal) { + anyInternalReleaseTags = true; } + } - if (mixedReleaseTags) { - if (!onlyFunctionOverloads) { - collector.messageRouter.addAnalyzerIssue( - ExtractorMessageId.DifferentReleaseTags, - 'This symbol has another declaration with a different release tag', - astSymbol - ); - } + if (mixedReleaseTags) { + if (!onlyFunctionOverloads) { + collector.messageRouter.addAnalyzerIssue( + ExtractorMessageId.DifferentReleaseTags, + 'This symbol has another declaration with a different release tag', + astSymbol + ); + } - if (anyInternalReleaseTags) { - collector.messageRouter.addAnalyzerIssue( - ExtractorMessageId.InternalMixedReleaseTag, - `Mixed release tags are not allowed for "${astSymbol.localName}" because one of its declarations` + - ` is marked as @internal`, - astSymbol - ); - } + if (anyInternalReleaseTags) { + collector.messageRouter.addAnalyzerIssue( + ExtractorMessageId.InternalMixedReleaseTag, + `Mixed release tags are not allowed for "${astSymbol.localName}" because one of its declarations` + + ` is marked as @internal`, + astSymbol + ); } } +} - private static _checkReferences( - collector: Collector, - astDeclaration: AstDeclaration, - alreadyWarnedEntities: Set - ): void { - const apiItemMetadata: ApiItemMetadata = collector.fetchApiItemMetadata(astDeclaration); - const declarationReleaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; - - for (const referencedEntity of astDeclaration.referencedAstEntities) { - let collectorEntity: CollectorEntity | undefined; - let referencedReleaseTag: ReleaseTag; - let localName: string; - - if (referencedEntity instanceof AstSymbol) { - // If this is e.g. a member of a namespace, then we need to be checking the top-level scope to see - // whether it's exported. - // - // TODO: Technically we should also check each of the nested scopes along the way. - const rootSymbol: AstSymbol = referencedEntity.rootAstSymbol; - - if (rootSymbol.isExternal) { - continue; - } +function _checkReferences( + collector: Collector, + astDeclaration: AstDeclaration, + alreadyWarnedEntities: Set +): void { + const apiItemMetadata: ApiItemMetadata = collector.fetchApiItemMetadata(astDeclaration); + const declarationReleaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; + + for (const referencedEntity of astDeclaration.referencedAstEntities) { + let collectorEntity: CollectorEntity | undefined; + let referencedReleaseTag: ReleaseTag; + let localName: string; + + if (referencedEntity instanceof AstSymbol) { + // If this is e.g. a member of a namespace, then we need to be checking the top-level scope to see + // whether it's exported. + // + // TODO: Technically we should also check each of the nested scopes along the way. + const rootSymbol: AstSymbol = referencedEntity.rootAstSymbol; + + if (rootSymbol.isExternal) { + continue; + } - collectorEntity = collector.tryGetCollectorEntity(rootSymbol); - localName = collectorEntity?.nameForEmit || rootSymbol.localName; + collectorEntity = collector.tryGetCollectorEntity(rootSymbol); + localName = collectorEntity?.nameForEmit || rootSymbol.localName; - const referencedMetadata: SymbolMetadata = collector.fetchSymbolMetadata(referencedEntity); - referencedReleaseTag = referencedMetadata.maxEffectiveReleaseTag; - } else if (referencedEntity instanceof AstNamespaceImport) { - collectorEntity = collector.tryGetCollectorEntity(referencedEntity); + const referencedMetadata: SymbolMetadata = collector.fetchSymbolMetadata(referencedEntity); + referencedReleaseTag = referencedMetadata.maxEffectiveReleaseTag; + } else if (referencedEntity instanceof AstNamespaceImport) { + collectorEntity = collector.tryGetCollectorEntity(referencedEntity); - // TODO: Currently the "import * as ___ from ___" syntax does not yet support doc comments - referencedReleaseTag = ReleaseTag.Public; + // TODO: Currently the "import * as ___ from ___" syntax does not yet support doc comments + referencedReleaseTag = ReleaseTag.Public; - localName = collectorEntity?.nameForEmit || referencedEntity.localName; - } else { - continue; - } + localName = collectorEntity?.nameForEmit || referencedEntity.localName; + } else { + continue; + } - if (collectorEntity && collectorEntity.consumable) { - if (ReleaseTag.compare(declarationReleaseTag, referencedReleaseTag) > 0) { + if (collectorEntity && collectorEntity.consumable) { + if (ReleaseTag.compare(declarationReleaseTag, referencedReleaseTag) > 0) { + collector.messageRouter.addAnalyzerIssue( + ExtractorMessageId.IncompatibleReleaseTags, + `The symbol "${astDeclaration.astSymbol.localName}"` + + ` is marked as ${ReleaseTag.getTagName(declarationReleaseTag)},` + + ` but its signature references "${localName}"` + + ` which is marked as ${ReleaseTag.getTagName(referencedReleaseTag)}`, + astDeclaration + ); + } + } else { + const entryPointFilename: string = path.basename( + collector.workingPackage.entryPointSourceFile.fileName + ); + + if (!alreadyWarnedEntities.has(referencedEntity)) { + alreadyWarnedEntities.add(referencedEntity); + + if (referencedEntity instanceof AstSymbol && _isEcmaScriptSymbol(referencedEntity)) { + // The main usage scenario for ECMAScript symbols is to attach private data to a JavaScript object, + // so as a special case, we do NOT report them as forgotten exports. + } else { collector.messageRouter.addAnalyzerIssue( - ExtractorMessageId.IncompatibleReleaseTags, - `The symbol "${astDeclaration.astSymbol.localName}"` + - ` is marked as ${ReleaseTag.getTagName(declarationReleaseTag)},` + - ` but its signature references "${localName}"` + - ` which is marked as ${ReleaseTag.getTagName(referencedReleaseTag)}`, + ExtractorMessageId.ForgottenExport, + `The symbol "${localName}" needs to be exported by the entry point ${entryPointFilename}`, astDeclaration ); } - } else { - const entryPointFilename: string = path.basename( - collector.workingPackage.entryPointSourceFile.fileName - ); - - if (!alreadyWarnedEntities.has(referencedEntity)) { - alreadyWarnedEntities.add(referencedEntity); - - if ( - referencedEntity instanceof AstSymbol && - ValidationEnhancer._isEcmaScriptSymbol(referencedEntity) - ) { - // The main usage scenario for ECMAScript symbols is to attach private data to a JavaScript object, - // so as a special case, we do NOT report them as forgotten exports. - } else { - collector.messageRouter.addAnalyzerIssue( - ExtractorMessageId.ForgottenExport, - `The symbol "${localName}" needs to be exported by the entry point ${entryPointFilename}`, - astDeclaration - ); - } - } } } } +} - // Detect an AstSymbol that refers to an ECMAScript symbol declaration such as: - // - // const mySymbol: unique symbol = Symbol('mySymbol'); - private static _isEcmaScriptSymbol(astSymbol: AstSymbol): boolean { - if (astSymbol.astDeclarations.length !== 1) { - return false; - } +// Detect an AstSymbol that refers to an ECMAScript symbol declaration such as: +// +// const mySymbol: unique symbol = Symbol('mySymbol'); +function _isEcmaScriptSymbol(astSymbol: AstSymbol): boolean { + if (astSymbol.astDeclarations.length !== 1) { + return false; + } - // We are matching a form like this: - // - // - VariableDeclaration: - // - Identifier: pre=[mySymbol] - // - ColonToken: pre=[:] sep=[ ] - // - TypeOperator: - // - UniqueKeyword: pre=[unique] sep=[ ] - // - SymbolKeyword: pre=[symbol] - const astDeclaration: AstDeclaration = astSymbol.astDeclarations[0]; - if (ts.isVariableDeclaration(astDeclaration.declaration)) { - const variableTypeNode: ts.TypeNode | undefined = astDeclaration.declaration.type; - if (variableTypeNode) { - for (const token of variableTypeNode.getChildren()) { - if (token.kind === ts.SyntaxKind.SymbolKeyword) { - return true; - } + // We are matching a form like this: + // + // - VariableDeclaration: + // - Identifier: pre=[mySymbol] + // - ColonToken: pre=[:] sep=[ ] + // - TypeOperator: + // - UniqueKeyword: pre=[unique] sep=[ ] + // - SymbolKeyword: pre=[symbol] + const astDeclaration: AstDeclaration = astSymbol.astDeclarations[0]; + if (ts.isVariableDeclaration(astDeclaration.declaration)) { + const variableTypeNode: ts.TypeNode | undefined = astDeclaration.declaration.type; + if (variableTypeNode) { + for (const token of variableTypeNode.getChildren()) { + if (token.kind === ts.SyntaxKind.SymbolKeyword) { + return true; } } } - - return false; } + + return false; } diff --git a/apps/api-extractor/src/generators/ApiModelGenerator.ts b/apps/api-extractor/src/generators/ApiModelGenerator.ts index 64c4fc56ceb..e03cfab218e 100644 --- a/apps/api-extractor/src/generators/ApiModelGenerator.ts +++ b/apps/api-extractor/src/generators/ApiModelGenerator.ts @@ -3,9 +3,11 @@ /* eslint-disable no-bitwise */ -import * as path from 'path'; +import * as path from 'node:path'; + import * as ts from 'typescript'; -import * as tsdoc from '@microsoft/tsdoc'; + +import type * as tsdoc from '@microsoft/tsdoc'; import { ApiModel, ApiClass, @@ -15,15 +17,15 @@ import { ApiNamespace, ApiInterface, ApiPropertySignature, - ApiItemContainerMixin, + type ApiItemContainerMixin, ReleaseTag, ApiProperty, ApiMethodSignature, - IApiParameterOptions, + type IApiParameterOptions, ApiEnum, ApiEnumMember, - IExcerptTokenRange, - IExcerptToken, + type IExcerptTokenRange, + type IExcerptToken, ApiConstructor, ApiConstructSignature, ApiFunction, @@ -31,23 +33,25 @@ import { ApiVariable, ApiTypeAlias, ApiCallSignature, - IApiTypeParameterOptions, + type IApiTypeParameterOptions, EnumMemberOrder } from '@microsoft/api-extractor-model'; import { Path } from '@rushstack/node-core-library'; -import { Collector } from '../collector/Collector'; -import { ISourceLocation } from '../collector/SourceMapper'; -import { AstDeclaration } from '../analyzer/AstDeclaration'; -import { ExcerptBuilder, IExcerptBuilderNodeToCapture } from './ExcerptBuilder'; +import type { Collector } from '../collector/Collector'; +import type { ISourceLocation } from '../collector/SourceMapper'; +import type { AstDeclaration } from '../analyzer/AstDeclaration'; +import { ExcerptBuilder, type IExcerptBuilderNodeTransform } from './ExcerptBuilder'; import { AstSymbol } from '../analyzer/AstSymbol'; import { DeclarationReferenceGenerator } from './DeclarationReferenceGenerator'; -import { ApiItemMetadata } from '../collector/ApiItemMetadata'; -import { DeclarationMetadata } from '../collector/DeclarationMetadata'; +import type { ApiItemMetadata } from '../collector/ApiItemMetadata'; +import type { DeclarationMetadata } from '../collector/DeclarationMetadata'; import { AstNamespaceImport } from '../analyzer/AstNamespaceImport'; -import { AstEntity } from '../analyzer/AstEntity'; -import { AstModule } from '../analyzer/AstModule'; +import type { AstEntity } from '../analyzer/AstEntity'; +import type { AstModule } from '../analyzer/AstModule'; import { TypeScriptInternals } from '../analyzer/TypeScriptInternals'; +import type { ExtractorConfig } from '../api/ExtractorConfig'; +import { DtsEmitHelpers } from './DtsEmitHelpers'; interface IProcessAstEntityContext { name: string; @@ -55,15 +59,37 @@ interface IProcessAstEntityContext { parentApiItem: ApiItemContainerMixin; } +/** + * @beta + */ +export interface IApiModelGenerationOptions { + /** + * The release tags to trim. + */ + releaseTagsToTrim: Set; +} + export class ApiModelGenerator { private readonly _collector: Collector; private readonly _apiModel: ApiModel; private readonly _referenceGenerator: DeclarationReferenceGenerator; + private readonly _releaseTagsToTrim: Set | undefined; + + public readonly docModelEnabled: boolean; - public constructor(collector: Collector) { + public constructor(collector: Collector, extractorConfig: ExtractorConfig) { this._collector = collector; this._apiModel = new ApiModel(); this._referenceGenerator = new DeclarationReferenceGenerator(collector); + + const apiModelGenerationOptions: IApiModelGenerationOptions | undefined = + extractorConfig.docModelGenerationOptions; + if (apiModelGenerationOptions) { + this._releaseTagsToTrim = apiModelGenerationOptions.releaseTagsToTrim; + this.docModelEnabled = true; + } else { + this.docModelEnabled = false; + } } public get apiModel(): ApiModel { @@ -176,8 +202,8 @@ export class ApiModelGenerator { const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; - if (releaseTag === ReleaseTag.Internal || releaseTag === ReleaseTag.Alpha) { - return; // trim out items marked as "@internal" or "@alpha" + if (this._releaseTagsToTrim?.has(releaseTag)) { + return; } switch (astDeclaration.declaration.kind) { @@ -250,7 +276,14 @@ export class ApiModelGenerator { break; case ts.SyntaxKind.VariableDeclaration: - this._processApiVariable(astDeclaration, context); + // check for arrow functions in variable declaration + const functionDeclaration: ts.FunctionDeclaration | undefined = + this._tryFindFunctionDeclaration(astDeclaration); + if (functionDeclaration) { + this._processApiFunction(astDeclaration, context, functionDeclaration); + } else { + this._processApiVariable(astDeclaration, context); + } break; default: @@ -258,6 +291,13 @@ export class ApiModelGenerator { } } + private _tryFindFunctionDeclaration(astDeclaration: AstDeclaration): ts.FunctionDeclaration | undefined { + const children: readonly ts.Node[] = astDeclaration.declaration.getChildren( + astDeclaration.declaration.getSourceFile() + ); + return children.find(ts.isFunctionTypeNode) as ts.FunctionDeclaration | undefined; + } + private _processChildDeclarations(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void { for (const childDeclaration of astDeclaration.children) { this._processDeclaration(childDeclaration, { @@ -280,22 +320,24 @@ export class ApiModelGenerator { const callSignature: ts.CallSignatureDeclaration = astDeclaration.declaration as ts.CallSignatureDeclaration; - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; + const nodeTransforms: IExcerptBuilderNodeTransform[] = []; const returnTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: callSignature.type, tokenRange: returnTypeTokenRange }); + if (callSignature.type) { + nodeTransforms.push({ node: callSignature.type, captureTokenRange: returnTypeTokenRange }); + } const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( - nodesToCapture, + nodeTransforms, callSignature.typeParameters ); const parameters: IApiParameterOptions[] = this._captureParameters( - nodesToCapture, + nodeTransforms, callSignature.parameters ); - const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); + const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodeTransforms); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; @@ -329,14 +371,14 @@ export class ApiModelGenerator { const constructorDeclaration: ts.ConstructorDeclaration = astDeclaration.declaration as ts.ConstructorDeclaration; - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; + const nodeTransforms: IExcerptBuilderNodeTransform[] = []; const parameters: IApiParameterOptions[] = this._captureParameters( - nodesToCapture, + nodeTransforms, constructorDeclaration.parameters ); - const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); + const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodeTransforms); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; @@ -366,10 +408,10 @@ export class ApiModelGenerator { if (apiClass === undefined) { const classDeclaration: ts.ClassDeclaration = astDeclaration.declaration as ts.ClassDeclaration; - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; + const nodeTransforms: IExcerptBuilderNodeTransform[] = []; const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( - nodesToCapture, + nodeTransforms, classDeclaration.typeParameters ); @@ -380,18 +422,18 @@ export class ApiModelGenerator { if (heritageClause.token === ts.SyntaxKind.ExtendsKeyword) { extendsTokenRange = ExcerptBuilder.createEmptyTokenRange(); if (heritageClause.types.length > 0) { - nodesToCapture.push({ node: heritageClause.types[0], tokenRange: extendsTokenRange }); + nodeTransforms.push({ node: heritageClause.types[0], captureTokenRange: extendsTokenRange }); } } else if (heritageClause.token === ts.SyntaxKind.ImplementsKeyword) { for (const heritageType of heritageClause.types) { const implementsTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); implementsTokenRanges.push(implementsTokenRange); - nodesToCapture.push({ node: heritageType, tokenRange: implementsTokenRange }); + nodeTransforms.push({ node: heritageType, captureTokenRange: implementsTokenRange }); } } } - const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); + const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodeTransforms); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; @@ -437,22 +479,24 @@ export class ApiModelGenerator { const constructSignature: ts.ConstructSignatureDeclaration = astDeclaration.declaration as ts.ConstructSignatureDeclaration; - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; + const nodeTransforms: IExcerptBuilderNodeTransform[] = []; const returnTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: constructSignature.type, tokenRange: returnTypeTokenRange }); + if (constructSignature.type) { + nodeTransforms.push({ node: constructSignature.type, captureTokenRange: returnTypeTokenRange }); + } const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( - nodesToCapture, + nodeTransforms, constructSignature.typeParameters ); const parameters: IApiParameterOptions[] = this._captureParameters( - nodesToCapture, + nodeTransforms, constructSignature.parameters ); - const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); + const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodeTransforms); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; @@ -517,15 +561,15 @@ export class ApiModelGenerator { if (apiEnumMember === undefined) { const enumMember: ts.EnumMember = astDeclaration.declaration as ts.EnumMember; - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; + const nodeTransforms: IExcerptBuilderNodeTransform[] = []; let initializerTokenRange: IExcerptTokenRange | undefined = undefined; if (enumMember.initializer) { initializerTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: enumMember.initializer, tokenRange: initializerTokenRange }); + nodeTransforms.push({ node: enumMember.initializer, captureTokenRange: initializerTokenRange }); } - const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); + const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodeTransforms); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; @@ -544,7 +588,11 @@ export class ApiModelGenerator { } } - private _processApiFunction(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void { + private _processApiFunction( + astDeclaration: AstDeclaration, + context: IProcessAstEntityContext, + altFunctionDeclaration?: ts.FunctionDeclaration + ): void { const { name, isExported, parentApiItem } = context; const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration); @@ -554,24 +602,26 @@ export class ApiModelGenerator { if (apiFunction === undefined) { const functionDeclaration: ts.FunctionDeclaration = - astDeclaration.declaration as ts.FunctionDeclaration; + altFunctionDeclaration ?? (astDeclaration.declaration as ts.FunctionDeclaration); - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; + const nodeTransforms: IExcerptBuilderNodeTransform[] = []; const returnTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: functionDeclaration.type, tokenRange: returnTypeTokenRange }); + if (functionDeclaration.type) { + nodeTransforms.push({ node: functionDeclaration.type, captureTokenRange: returnTypeTokenRange }); + } const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( - nodesToCapture, + nodeTransforms, functionDeclaration.typeParameters ); const parameters: IApiParameterOptions[] = this._captureParameters( - nodesToCapture, + nodeTransforms, functionDeclaration.parameters ); - const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); + const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodeTransforms); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; @@ -607,17 +657,17 @@ export class ApiModelGenerator { const indexSignature: ts.IndexSignatureDeclaration = astDeclaration.declaration as ts.IndexSignatureDeclaration; - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; + const nodeTransforms: IExcerptBuilderNodeTransform[] = []; const returnTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: indexSignature.type, tokenRange: returnTypeTokenRange }); + nodeTransforms.push({ node: indexSignature.type, captureTokenRange: returnTypeTokenRange }); const parameters: IApiParameterOptions[] = this._captureParameters( - nodesToCapture, + nodeTransforms, indexSignature.parameters ); - const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); + const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodeTransforms); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; @@ -651,10 +701,10 @@ export class ApiModelGenerator { const interfaceDeclaration: ts.InterfaceDeclaration = astDeclaration.declaration as ts.InterfaceDeclaration; - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; + const nodeTransforms: IExcerptBuilderNodeTransform[] = []; const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( - nodesToCapture, + nodeTransforms, interfaceDeclaration.typeParameters ); @@ -665,12 +715,12 @@ export class ApiModelGenerator { for (const heritageType of heritageClause.types) { const extendsTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); extendsTokenRanges.push(extendsTokenRange); - nodesToCapture.push({ node: heritageType, tokenRange: extendsTokenRange }); + nodeTransforms.push({ node: heritageType, captureTokenRange: extendsTokenRange }); } } } - const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); + const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodeTransforms); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; @@ -707,22 +757,24 @@ export class ApiModelGenerator { if (apiMethod === undefined) { const methodDeclaration: ts.MethodDeclaration = astDeclaration.declaration as ts.MethodDeclaration; - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; + const nodeTransforms: IExcerptBuilderNodeTransform[] = []; const returnTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: methodDeclaration.type, tokenRange: returnTypeTokenRange }); + if (methodDeclaration.type) { + nodeTransforms.push({ node: methodDeclaration.type, captureTokenRange: returnTypeTokenRange }); + } const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( - nodesToCapture, + nodeTransforms, methodDeclaration.typeParameters ); const parameters: IApiParameterOptions[] = this._captureParameters( - nodesToCapture, + nodeTransforms, methodDeclaration.parameters ); - const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); + const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodeTransforms); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; @@ -770,22 +822,24 @@ export class ApiModelGenerator { if (apiMethodSignature === undefined) { const methodSignature: ts.MethodSignature = astDeclaration.declaration as ts.MethodSignature; - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; + const nodeTransforms: IExcerptBuilderNodeTransform[] = []; const returnTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: methodSignature.type, tokenRange: returnTypeTokenRange }); + if (methodSignature.type) { + nodeTransforms.push({ node: methodSignature.type, captureTokenRange: returnTypeTokenRange }); + } const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( - nodesToCapture, + nodeTransforms, methodSignature.typeParameters ); const parameters: IApiParameterOptions[] = this._captureParameters( - nodesToCapture, + nodeTransforms, methodSignature.parameters ); - const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); + const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodeTransforms); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; @@ -851,7 +905,7 @@ export class ApiModelGenerator { if (apiProperty === undefined) { const declaration: ts.Declaration = astDeclaration.declaration; - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; + const nodeTransforms: IExcerptBuilderNodeTransform[] = []; const propertyTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); let propertyTypeNode: ts.TypeNode | undefined; @@ -865,15 +919,17 @@ export class ApiModelGenerator { propertyTypeNode = declaration.parameters[0].type; } - nodesToCapture.push({ node: propertyTypeNode, tokenRange: propertyTypeTokenRange }); + if (propertyTypeNode) { + nodeTransforms.push({ node: propertyTypeNode, captureTokenRange: propertyTypeTokenRange }); + } let initializerTokenRange: IExcerptTokenRange | undefined = undefined; if (ts.isPropertyDeclaration(declaration) && declaration.initializer) { initializerTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: declaration.initializer, tokenRange: initializerTokenRange }); + nodeTransforms.push({ node: declaration.initializer, captureTokenRange: initializerTokenRange }); } - const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); + const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodeTransforms); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; @@ -919,12 +975,14 @@ export class ApiModelGenerator { if (apiPropertySignature === undefined) { const propertySignature: ts.PropertySignature = astDeclaration.declaration as ts.PropertySignature; - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; + const nodeTransforms: IExcerptBuilderNodeTransform[] = []; const propertyTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: propertySignature.type, tokenRange: propertyTypeTokenRange }); + if (propertySignature.type) { + nodeTransforms.push({ node: propertySignature.type, captureTokenRange: propertyTypeTokenRange }); + } - const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); + const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodeTransforms); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; @@ -964,17 +1022,17 @@ export class ApiModelGenerator { const typeAliasDeclaration: ts.TypeAliasDeclaration = astDeclaration.declaration as ts.TypeAliasDeclaration; - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; + const nodeTransforms: IExcerptBuilderNodeTransform[] = []; const typeParameters: IApiTypeParameterOptions[] = this._captureTypeParameters( - nodesToCapture, + nodeTransforms, typeAliasDeclaration.typeParameters ); const typeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: typeAliasDeclaration.type, tokenRange: typeTokenRange }); + nodeTransforms.push({ node: typeAliasDeclaration.type, captureTokenRange: typeTokenRange }); - const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); + const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodeTransforms); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; @@ -1006,18 +1064,23 @@ export class ApiModelGenerator { const variableDeclaration: ts.VariableDeclaration = astDeclaration.declaration as ts.VariableDeclaration; - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; + const nodeTransforms: IExcerptBuilderNodeTransform[] = []; const variableTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: variableDeclaration.type, tokenRange: variableTypeTokenRange }); + if (variableDeclaration.type) { + nodeTransforms.push({ node: variableDeclaration.type, captureTokenRange: variableTypeTokenRange }); + } let initializerTokenRange: IExcerptTokenRange | undefined = undefined; if (variableDeclaration.initializer) { initializerTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: variableDeclaration.initializer, tokenRange: initializerTokenRange }); + nodeTransforms.push({ + node: variableDeclaration.initializer, + captureTokenRange: initializerTokenRange + }); } - const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); + const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodeTransforms); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; @@ -1041,16 +1104,16 @@ export class ApiModelGenerator { } /** - * @param nodesToCapture - A list of child nodes whose token ranges we want to capture + * @param nodeTransforms - A list of child nodes whose token ranges we want to capture */ private _buildExcerptTokens( astDeclaration: AstDeclaration, - nodesToCapture: IExcerptBuilderNodeToCapture[] + nodeTransforms: IExcerptBuilderNodeTransform[] ): IExcerptToken[] { const excerptTokens: IExcerptToken[] = []; // Build the main declaration - ExcerptBuilder.addDeclaration(excerptTokens, astDeclaration, nodesToCapture, this._referenceGenerator); + ExcerptBuilder.addDeclaration(excerptTokens, astDeclaration, nodeTransforms, this._referenceGenerator); const declarationMetadata: DeclarationMetadata = this._collector.fetchDeclarationMetadata(astDeclaration); @@ -1060,7 +1123,7 @@ export class ApiModelGenerator { ExcerptBuilder.addDeclaration( excerptTokens, ancillaryDeclaration, - nodesToCapture, + nodeTransforms, this._referenceGenerator ); } @@ -1069,17 +1132,21 @@ export class ApiModelGenerator { } private _captureTypeParameters( - nodesToCapture: IExcerptBuilderNodeToCapture[], + nodeTransforms: IExcerptBuilderNodeTransform[], typeParameterNodes: ts.NodeArray | undefined ): IApiTypeParameterOptions[] { const typeParameters: IApiTypeParameterOptions[] = []; if (typeParameterNodes) { for (const typeParameter of typeParameterNodes) { const constraintTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: typeParameter.constraint, tokenRange: constraintTokenRange }); + if (typeParameter.constraint) { + nodeTransforms.push({ node: typeParameter.constraint, captureTokenRange: constraintTokenRange }); + } const defaultTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: typeParameter.default, tokenRange: defaultTypeTokenRange }); + if (typeParameter.default) { + nodeTransforms.push({ node: typeParameter.default, captureTokenRange: defaultTypeTokenRange }); + } typeParameters.push({ typeParameterName: typeParameter.name.getText().trim(), @@ -1092,19 +1159,31 @@ export class ApiModelGenerator { } private _captureParameters( - nodesToCapture: IExcerptBuilderNodeToCapture[], + nodeTransforms: IExcerptBuilderNodeTransform[], parameterNodes: ts.NodeArray ): IApiParameterOptions[] { const parameters: IApiParameterOptions[] = []; - for (const parameter of parameterNodes) { - const parameterTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: parameter.type, tokenRange: parameterTypeTokenRange }); - parameters.push({ - parameterName: parameter.name.getText().trim(), - parameterTypeTokenRange, - isOptional: this._collector.typeChecker.isOptionalParameter(parameter) - }); - } + + DtsEmitHelpers.forEachParameterToNormalize( + parameterNodes, + (parameter: ts.ParameterDeclaration, syntheticName: string | undefined): void => { + const parameterTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); + if (parameter.type) { + nodeTransforms.push({ node: parameter.type, captureTokenRange: parameterTypeTokenRange }); + } + parameters.push({ + parameterName: syntheticName ?? parameter.name.getText().trim(), + parameterTypeTokenRange, + isOptional: this._collector.typeChecker.isOptionalParameter(parameter) + }); + + if (syntheticName !== undefined) { + // Replace the subexpression like "{ y, z }" with the synthesized parameter name + nodeTransforms.push({ node: parameter.name, replacementText: syntheticName }); + } + } + ); + return parameters; } diff --git a/apps/api-extractor/src/generators/ApiReportGenerator.ts b/apps/api-extractor/src/generators/ApiReportGenerator.ts index 6c99bdee3db..41ab7b71d86 100644 --- a/apps/api-extractor/src/generators/ApiReportGenerator.ts +++ b/apps/api-extractor/src/generators/ApiReportGenerator.ts @@ -2,28 +2,39 @@ // See LICENSE in the project root for license information. import * as ts from 'typescript'; -import { Text, InternalError } from '@rushstack/node-core-library'; + import { ReleaseTag } from '@microsoft/api-extractor-model'; +import { Text, InternalError } from '@rushstack/node-core-library'; import { Collector } from '../collector/Collector'; import { TypeScriptHelpers } from '../analyzer/TypeScriptHelpers'; import { Span } from '../analyzer/Span'; -import { CollectorEntity } from '../collector/CollectorEntity'; +import type { CollectorEntity } from '../collector/CollectorEntity'; import { AstDeclaration } from '../analyzer/AstDeclaration'; -import { ApiItemMetadata } from '../collector/ApiItemMetadata'; +import type { ApiItemMetadata } from '../collector/ApiItemMetadata'; import { AstImport } from '../analyzer/AstImport'; import { AstSymbol } from '../analyzer/AstSymbol'; -import { ExtractorMessage } from '../api/ExtractorMessage'; +import type { ExtractorMessage } from '../api/ExtractorMessage'; import { IndentedWriter } from './IndentedWriter'; import { DtsEmitHelpers } from './DtsEmitHelpers'; import { AstNamespaceImport } from '../analyzer/AstNamespaceImport'; -import { AstEntity } from '../analyzer/AstEntity'; -import { AstModuleExportInfo } from '../analyzer/AstModule'; +import type { AstEntity } from '../analyzer/AstEntity'; +import type { IAstModuleExportInfo } from '../analyzer/AstModule'; import { SourceFileLocationFormatter } from '../analyzer/SourceFileLocationFormatter'; +import { SyntaxHelpers } from '../analyzer/SyntaxHelpers'; +import { ExtractorMessageId } from '../api/ExtractorMessageId'; +import type { ApiReportVariant } from '../api/IConfigFile'; +import type { SymbolMetadata } from '../collector/SymbolMetadata'; + +interface IContext { + collector: Collector; + reportVariant: ApiReportVariant; + alreadyProcessedSignatures: Set; +} -export class ApiReportGenerator { - private static _trimSpacesRegExp: RegExp = / +$/gm; +const _trimSpacesRegExp: RegExp = / +$/gm; +export class ApiReportGenerator { /** * Compares the contents of two API files that were created using ApiFileGenerator, * and returns true if they are equivalent. Note that these files are not normally edited @@ -41,13 +52,26 @@ export class ApiReportGenerator { return normalizedActual === normalizedExpected; } - public static generateReviewFileContent(collector: Collector): string { + /** + * Generates and returns the API report contents as a string. + * + * @param reportVariant - The release level with which the report is associated. + * Can also be viewed as the minimal release level of items that should be included in the report. + */ + public static generateReviewFileContent(collector: Collector, reportVariant: ApiReportVariant): string { const writer: IndentedWriter = new IndentedWriter(); writer.trimLeadingSpaces = true; + function capitalizeFirstLetter(input: string): string { + return input === '' ? '' : `${input[0].toLocaleUpperCase()}${input.slice(1)}`; + } + + // For backwards compatibility, don't emit "complete" in report text for untrimmed reports. + const releaseLevelPrefix: string = + reportVariant === 'complete' ? '' : `${capitalizeFirstLetter(reportVariant)} `; writer.writeLine( [ - `## API Report File for "${collector.workingPackage.name}"`, + `## ${releaseLevelPrefix}API Report File for "${collector.workingPackage.name}"`, ``, `> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).`, `` @@ -75,9 +99,22 @@ export class ApiReportGenerator { } writer.ensureSkippedLine(); + const context: IContext = { + collector, + reportVariant, + alreadyProcessedSignatures: new Set() + }; + // Emit the regular declarations for (const entity of collector.entities) { const astEntity: AstEntity = entity.astEntity; + const symbolMetadata: SymbolMetadata | undefined = collector.tryFetchMetadataForAstEntity(astEntity); + const maxEffectiveReleaseTag: ReleaseTag = symbolMetadata?.maxEffectiveReleaseTag ?? ReleaseTag.None; + + if (!_shouldIncludeReleaseTag(maxEffectiveReleaseTag, reportVariant)) { + continue; + } + if (entity.consumable || collector.extractorConfig.apiReportIncludeForgottenExports) { // First, collect the list of export names for this symbol. When reporting messages with // ExtractorMessage.properties.exportName, this will enable us to emit the warning comments alongside @@ -118,25 +155,27 @@ export class ApiReportGenerator { messagesToReport.push(message); } - writer.ensureSkippedLine(); - writer.write(ApiReportGenerator._getAedocSynopsis(collector, astDeclaration, messagesToReport)); + if (_shouldIncludeDeclaration(collector, astDeclaration, reportVariant)) { + writer.ensureSkippedLine(); + writer.write(_getAedocSynopsis(collector, astDeclaration, messagesToReport)); - const span: Span = new Span(astDeclaration.declaration); + const span: Span = new Span(astDeclaration.declaration); - const apiItemMetadata: ApiItemMetadata = collector.fetchApiItemMetadata(astDeclaration); - if (apiItemMetadata.isPreapproved) { - ApiReportGenerator._modifySpanForPreapproved(span); - } else { - ApiReportGenerator._modifySpan(collector, span, entity, astDeclaration, false); - } + const apiItemMetadata: ApiItemMetadata = collector.fetchApiItemMetadata(astDeclaration); + if (apiItemMetadata.isPreapproved) { + _modifySpanForPreapproved(span); + } else { + _modifySpan(span, entity, astDeclaration, false, context); + } - span.writeModifiedText(writer); - writer.ensureNewLine(); + span.writeModifiedText(writer); + writer.ensureNewLine(); + } } } if (astEntity instanceof AstNamespaceImport) { - const astModuleExportInfo: AstModuleExportInfo = astEntity.fetchAstModuleExportInfo(collector); + const astModuleExportInfo: IAstModuleExportInfo = astEntity.fetchAstModuleExportInfo(collector); if (entity.nameForEmit === undefined) { // This should never happen @@ -186,7 +225,10 @@ export class ApiReportGenerator { if (collectorEntity.nameForEmit === exportedName) { exportClauses.push(collectorEntity.nameForEmit); } else { - exportClauses.push(`${collectorEntity.nameForEmit} as ${exportedName}`); + const safeExportedName: string = SyntaxHelpers.isSafeUnquotedMemberIdentifier(exportedName) + ? exportedName + : JSON.stringify(exportedName); + exportClauses.push(`${collectorEntity.nameForEmit} as ${safeExportedName}`); } } writer.writeLine(exportClauses.join(',\n')); @@ -203,10 +245,7 @@ export class ApiReportGenerator { if (exportToEmit.associatedMessages.length > 0) { writer.ensureSkippedLine(); for (const message of exportToEmit.associatedMessages) { - ApiReportGenerator._writeLineAsComments( - writer, - 'Warning: ' + message.formatMessageWithoutLocation() - ); + _writeLineAsComments(writer, 'Warning: ' + message.formatMessageWithoutLocation()); } } @@ -223,10 +262,10 @@ export class ApiReportGenerator { collector.messageRouter.fetchUnassociatedMessagesForReviewFile(); if (unassociatedMessages.length > 0) { writer.ensureSkippedLine(); - ApiReportGenerator._writeLineAsComments(writer, 'Warnings were encountered during analysis:'); - ApiReportGenerator._writeLineAsComments(writer, ''); + _writeLineAsComments(writer, 'Warnings were encountered during analysis:'); + _writeLineAsComments(writer, ''); for (const unassociatedMessage of unassociatedMessages) { - ApiReportGenerator._writeLineAsComments( + _writeLineAsComments( writer, unassociatedMessage.formatMessageWithLocation(collector.workingPackage.packageFolder) ); @@ -235,7 +274,7 @@ export class ApiReportGenerator { if (collector.workingPackage.tsdocComment === undefined) { writer.ensureSkippedLine(); - ApiReportGenerator._writeLineAsComments(writer, '(No @packageDocumentation comment for this package)'); + _writeLineAsComments(writer, '(No @packageDocumentation comment for this package)'); } // Write the closing delimiter for the Markdown code fence @@ -243,164 +282,182 @@ export class ApiReportGenerator { writer.writeLine('```'); // Remove any trailing spaces - return writer.toString().replace(ApiReportGenerator._trimSpacesRegExp, ''); + return writer.toString().replace(_trimSpacesRegExp, ''); } +} - /** - * Before writing out a declaration, _modifySpan() applies various fixups to make it nice. - */ - private static _modifySpan( - collector: Collector, - span: Span, - entity: CollectorEntity, - astDeclaration: AstDeclaration, - insideTypeLiteral: boolean - ): void { - // Should we process this declaration at all? - // eslint-disable-next-line no-bitwise - if ((astDeclaration.modifierFlags & ts.ModifierFlags.Private) !== 0) { - span.modification.skipAll(); - return; - } +/** + * Before writing out a declaration, _modifySpan() applies various fixups to make it nice. + */ +function _modifySpan( + span: Span, + entity: CollectorEntity, + astDeclaration: AstDeclaration, + insideTypeLiteral: boolean, + context: IContext +): void { + const { collector, reportVariant } = context; + + // Should we process this declaration at all? + if (!_shouldIncludeDeclaration(collector, astDeclaration, reportVariant)) { + span.modification.skipAll(); + return; + } - const previousSpan: Span | undefined = span.previousSibling; + const previousSpan: Span | undefined = span.previousSibling; - let recurseChildren: boolean = true; - let sortChildren: boolean = false; + let recurseChildren: boolean = true; + let sortChildren: boolean = false; - switch (span.kind) { - case ts.SyntaxKind.JSDocComment: - span.modification.skipAll(); - // For now, we don't transform JSDoc comment nodes at all - recurseChildren = false; - break; + switch (span.kind) { + case ts.SyntaxKind.JSDocComment: + span.modification.skipAll(); + // For now, we don't transform JSDoc comment nodes at all + recurseChildren = false; + break; - case ts.SyntaxKind.ExportKeyword: - case ts.SyntaxKind.DefaultKeyword: - case ts.SyntaxKind.DeclareKeyword: - // Delete any explicit "export" or "declare" keywords -- we will re-add them below - span.modification.skipAll(); + case ts.SyntaxKind.ExportKeyword: + if (DtsEmitHelpers.isExportKeywordInNamespaceExportDeclaration(span.node)) { + // This is an export declaration inside a namespace - preserve the export keyword break; + } + // Otherwise, delete the export keyword -- we will re-add it below + span.modification.skipAll(); + break; - case ts.SyntaxKind.InterfaceKeyword: - case ts.SyntaxKind.ClassKeyword: - case ts.SyntaxKind.EnumKeyword: - case ts.SyntaxKind.NamespaceKeyword: - case ts.SyntaxKind.ModuleKeyword: - case ts.SyntaxKind.TypeKeyword: - case ts.SyntaxKind.FunctionKeyword: - // Replace the stuff we possibly deleted above - let replacedModifiers: string = ''; + case ts.SyntaxKind.DefaultKeyword: + case ts.SyntaxKind.DeclareKeyword: + // Delete any explicit "export" or "declare" keywords -- we will re-add them below + span.modification.skipAll(); + break; + + case ts.SyntaxKind.InterfaceKeyword: + case ts.SyntaxKind.ClassKeyword: + case ts.SyntaxKind.EnumKeyword: + case ts.SyntaxKind.NamespaceKeyword: + case ts.SyntaxKind.ModuleKeyword: + case ts.SyntaxKind.TypeKeyword: + case ts.SyntaxKind.FunctionKeyword: + // Replace the stuff we possibly deleted above + let replacedModifiers: string = ''; + + if (entity.shouldInlineExport) { + replacedModifiers = 'export ' + replacedModifiers; + } - if (entity.shouldInlineExport) { - replacedModifiers = 'export ' + replacedModifiers; + if (previousSpan && previousSpan.kind === ts.SyntaxKind.SyntaxList) { + // If there is a previous span of type SyntaxList, then apply it before any other modifiers + // (e.g. "abstract") that appear there. + previousSpan.modification.prefix = replacedModifiers + previousSpan.modification.prefix; + } else { + // Otherwise just stick it in front of this span + span.modification.prefix = replacedModifiers + span.modification.prefix; + } + break; + + case ts.SyntaxKind.SyntaxList: + if (span.parent) { + if (AstDeclaration.isSupportedSyntaxKind(span.parent.kind)) { + // If the immediate parent is an API declaration, and the immediate children are API declarations, + // then sort the children alphabetically + sortChildren = true; + } else if (span.parent.kind === ts.SyntaxKind.ModuleBlock) { + // Namespaces are special because their chain goes ModuleDeclaration -> ModuleBlock -> SyntaxList + sortChildren = true; } - - if (previousSpan && previousSpan.kind === ts.SyntaxKind.SyntaxList) { - // If there is a previous span of type SyntaxList, then apply it before any other modifiers - // (e.g. "abstract") that appear there. - previousSpan.modification.prefix = replacedModifiers + previousSpan.modification.prefix; - } else { - // Otherwise just stick it in front of this span - span.modification.prefix = replacedModifiers + span.modification.prefix; + } + break; + + case ts.SyntaxKind.VariableDeclaration: + if (!span.parent) { + // The VariableDeclaration node is part of a VariableDeclarationList, however + // the Entry.followedSymbol points to the VariableDeclaration part because + // multiple definitions might share the same VariableDeclarationList. + // + // Since we are emitting a separate declaration for each one, we need to look upwards + // in the ts.Node tree and write a copy of the enclosing VariableDeclarationList + // content (e.g. "var" from "var x=1, y=2"). + const list: ts.VariableDeclarationList | undefined = TypeScriptHelpers.matchAncestor(span.node, [ + ts.SyntaxKind.VariableDeclarationList, + ts.SyntaxKind.VariableDeclaration + ]); + if (!list) { + // This should not happen unless the compiler API changes somehow + throw new InternalError('Unsupported variable declaration'); } - break; + const listPrefix: string = list + .getSourceFile() + .text.substring(list.getStart(), list.declarations[0].getStart()); + span.modification.prefix = listPrefix + span.modification.prefix; + span.modification.suffix = ';'; - case ts.SyntaxKind.SyntaxList: - if (span.parent) { - if (AstDeclaration.isSupportedSyntaxKind(span.parent.kind)) { - // If the immediate parent is an API declaration, and the immediate children are API declarations, - // then sort the children alphabetically - sortChildren = true; - } else if (span.parent.kind === ts.SyntaxKind.ModuleBlock) { - // Namespaces are special because their chain goes ModuleDeclaration -> ModuleBlock -> SyntaxList - sortChildren = true; - } + if (entity.shouldInlineExport) { + span.modification.prefix = 'export ' + span.modification.prefix; } - break; - - case ts.SyntaxKind.VariableDeclaration: - if (!span.parent) { - // The VariableDeclaration node is part of a VariableDeclarationList, however - // the Entry.followedSymbol points to the VariableDeclaration part because - // multiple definitions might share the same VariableDeclarationList. - // - // Since we are emitting a separate declaration for each one, we need to look upwards - // in the ts.Node tree and write a copy of the enclosing VariableDeclarationList - // content (e.g. "var" from "var x=1, y=2"). - const list: ts.VariableDeclarationList | undefined = TypeScriptHelpers.matchAncestor(span.node, [ - ts.SyntaxKind.VariableDeclarationList, - ts.SyntaxKind.VariableDeclaration - ]); - if (!list) { - // This should not happen unless the compiler API changes somehow - throw new InternalError('Unsupported variable declaration'); - } - const listPrefix: string = list - .getSourceFile() - .text.substring(list.getStart(), list.declarations[0].getStart()); - span.modification.prefix = listPrefix + span.modification.prefix; - span.modification.suffix = ';'; - - if (entity.shouldInlineExport) { - span.modification.prefix = 'export ' + span.modification.prefix; + } + break; + + case ts.SyntaxKind.Parameter: + { + // (signature) -> SyntaxList -> Parameter + const signatureParent: Span | undefined = span.parent; + if (signatureParent) { + if (!context.alreadyProcessedSignatures.has(signatureParent)) { + context.alreadyProcessedSignatures.add(signatureParent); + DtsEmitHelpers.normalizeParameterNames(signatureParent); } } - break; + } + break; - case ts.SyntaxKind.Identifier: - const referencedEntity: CollectorEntity | undefined = collector.tryGetEntityForNode( - span.node as ts.Identifier - ); + case ts.SyntaxKind.Identifier: + const referencedEntity: CollectorEntity | undefined = collector.tryGetEntityForNode( + span.node as ts.Identifier + ); - if (referencedEntity) { - if (!referencedEntity.nameForEmit) { - // This should never happen - throw new InternalError('referencedEntry.nameForEmit is undefined'); - } - - span.modification.prefix = referencedEntity.nameForEmit; - // For debugging: - // span.modification.prefix += '/*R=FIX*/'; - } else { - // For debugging: - // span.modification.prefix += '/*R=KEEP*/'; + if (referencedEntity) { + if (!referencedEntity.nameForEmit) { + // This should never happen + throw new InternalError('referencedEntry.nameForEmit is undefined'); } - break; + span.modification.prefix = referencedEntity.nameForEmit; + // For debugging: + // span.modification.prefix += '/*R=FIX*/'; + } else { + // For debugging: + // span.modification.prefix += '/*R=KEEP*/'; + } - case ts.SyntaxKind.TypeLiteral: - insideTypeLiteral = true; - break; + break; - case ts.SyntaxKind.ImportType: - DtsEmitHelpers.modifyImportTypeSpan( - collector, - span, - astDeclaration, - (childSpan, childAstDeclaration) => { - ApiReportGenerator._modifySpan( - collector, - childSpan, - entity, - childAstDeclaration, - insideTypeLiteral - ); - } - ); - break; - } + case ts.SyntaxKind.TypeLiteral: + insideTypeLiteral = true; + break; + + case ts.SyntaxKind.ImportType: + DtsEmitHelpers.modifyImportTypeSpan( + collector, + span, + astDeclaration, + (childSpan, childAstDeclaration) => { + _modifySpan(childSpan, entity, childAstDeclaration, insideTypeLiteral, context); + } + ); + break; + } - if (recurseChildren) { - for (const child of span.children) { - let childAstDeclaration: AstDeclaration = astDeclaration; + if (recurseChildren) { + for (const child of span.children) { + let childAstDeclaration: AstDeclaration = astDeclaration; - if (AstDeclaration.isSupportedSyntaxKind(child.kind)) { - childAstDeclaration = collector.astSymbolTable.getChildAstDeclarationByNode( - child.node, - astDeclaration - ); + if (AstDeclaration.isSupportedSyntaxKind(child.kind)) { + childAstDeclaration = collector.astSymbolTable.getChildAstDeclarationByNode( + child.node, + astDeclaration + ); + if (_shouldIncludeDeclaration(collector, childAstDeclaration, reportVariant)) { if (sortChildren) { span.modification.sortChildren = true; child.modification.sortKey = Collector.getSortKeyIgnoringUnderscore( @@ -411,139 +468,212 @@ export class ApiReportGenerator { if (!insideTypeLiteral) { const messagesToReport: ExtractorMessage[] = collector.messageRouter.fetchAssociatedMessagesForReviewFile(childAstDeclaration); - const aedocSynopsis: string = ApiReportGenerator._getAedocSynopsis( - collector, - childAstDeclaration, - messagesToReport - ); + + // NOTE: This generates ae-undocumented messages as a side effect + const aedocSynopsis: string = _getAedocSynopsis(collector, childAstDeclaration, messagesToReport); child.modification.prefix = aedocSynopsis + child.modification.prefix; } } - - ApiReportGenerator._modifySpan(collector, child, entity, childAstDeclaration, insideTypeLiteral); } + + _modifySpan(child, entity, childAstDeclaration, insideTypeLiteral, context); } } +} - /** - * For declarations marked as `@preapproved`, this is used instead of _modifySpan(). - */ - private static _modifySpanForPreapproved(span: Span): void { - // Match something like this: - // - // ClassDeclaration: - // SyntaxList: - // ExportKeyword: pre=[export] sep=[ ] - // DeclareKeyword: pre=[declare] sep=[ ] - // ClassKeyword: pre=[class] sep=[ ] - // Identifier: pre=[_PreapprovedClass] sep=[ ] - // FirstPunctuation: pre=[{] sep=[\n\n ] - // SyntaxList: - // ... - // CloseBraceToken: pre=[}] - // - // or this: - // ModuleDeclaration: - // SyntaxList: - // ExportKeyword: pre=[export] sep=[ ] - // DeclareKeyword: pre=[declare] sep=[ ] - // NamespaceKeyword: pre=[namespace] sep=[ ] - // Identifier: pre=[_PreapprovedNamespace] sep=[ ] - // ModuleBlock: - // FirstPunctuation: pre=[{] sep=[\n\n ] - // SyntaxList: - // ... - // CloseBraceToken: pre=[}] - // - // And reduce it to something like this: - // - // // @internal (undocumented) - // class _PreapprovedClass { /* (preapproved) */ } - // - - let skipRest: boolean = false; - for (const child of span.children) { - if (skipRest || child.kind === ts.SyntaxKind.SyntaxList || child.kind === ts.SyntaxKind.JSDocComment) { - child.modification.skipAll(); - } - if (child.kind === ts.SyntaxKind.Identifier) { - skipRest = true; - child.modification.omitSeparatorAfter = true; - child.modification.suffix = ' { /* (preapproved) */ }'; - } - } +function _shouldIncludeDeclaration( + collector: Collector, + astDeclaration: AstDeclaration, + reportVariant: ApiReportVariant +): boolean { + // Private declarations are not included in the API report + // eslint-disable-next-line no-bitwise + if ((astDeclaration.modifierFlags & ts.ModifierFlags.Private) !== 0) { + return false; } - /** - * Writes a synopsis of the AEDoc comments, which indicates the release tag, - * whether the item has been documented, and any warnings that were detected - * by the analysis. - */ - private static _getAedocSynopsis( - collector: Collector, - astDeclaration: AstDeclaration, - messagesToReport: ExtractorMessage[] - ): string { - const writer: IndentedWriter = new IndentedWriter(); + const apiItemMetadata: ApiItemMetadata = collector.fetchApiItemMetadata(astDeclaration); - for (const message of messagesToReport) { - ApiReportGenerator._writeLineAsComments(writer, 'Warning: ' + message.formatMessageWithoutLocation()); - } + return _shouldIncludeReleaseTag(apiItemMetadata.effectiveReleaseTag, reportVariant); +} - if (!collector.isAncillaryDeclaration(astDeclaration)) { - const footerParts: string[] = []; - const apiItemMetadata: ApiItemMetadata = collector.fetchApiItemMetadata(astDeclaration); - if (!apiItemMetadata.releaseTagSameAsParent) { - if (apiItemMetadata.effectiveReleaseTag !== ReleaseTag.None) { - footerParts.push(ReleaseTag.getTagName(apiItemMetadata.effectiveReleaseTag)); - } - } +function _shouldIncludeReleaseTag(releaseTag: ReleaseTag, reportVariant: ApiReportVariant): boolean { + switch (reportVariant) { + case 'complete': + return true; + case 'alpha': + return ( + releaseTag === ReleaseTag.Alpha || + releaseTag === ReleaseTag.Beta || + releaseTag === ReleaseTag.Public || + // NOTE: No specified release tag is implicitly treated as `@public`. + releaseTag === ReleaseTag.None + ); + case 'beta': + return ( + releaseTag === ReleaseTag.Beta || + releaseTag === ReleaseTag.Public || + // NOTE: No specified release tag is implicitly treated as `@public`. + releaseTag === ReleaseTag.None + ); + case 'public': + return ( + releaseTag === ReleaseTag.Public || + // NOTE: No specified release tag is implicitly treated as `@public`. + releaseTag === ReleaseTag.None + ); + default: + throw new Error(`Unrecognized release level: ${reportVariant}`); + } +} - if (apiItemMetadata.isSealed) { - footerParts.push('@sealed'); - } +/** + * For declarations marked as `@preapproved`, this is used instead of _modifySpan(). + */ +function _modifySpanForPreapproved(span: Span): void { + // Match something like this: + // + // ClassDeclaration: + // SyntaxList: + // ExportKeyword: pre=[export] sep=[ ] + // DeclareKeyword: pre=[declare] sep=[ ] + // ClassKeyword: pre=[class] sep=[ ] + // Identifier: pre=[_PreapprovedClass] sep=[ ] + // FirstPunctuation: pre=[{] sep=[\n\n ] + // SyntaxList: + // ... + // CloseBraceToken: pre=[}] + // + // or this: + // ModuleDeclaration: + // SyntaxList: + // ExportKeyword: pre=[export] sep=[ ] + // DeclareKeyword: pre=[declare] sep=[ ] + // NamespaceKeyword: pre=[namespace] sep=[ ] + // Identifier: pre=[_PreapprovedNamespace] sep=[ ] + // ModuleBlock: + // FirstPunctuation: pre=[{] sep=[\n\n ] + // SyntaxList: + // ... + // CloseBraceToken: pre=[}] + // + // And reduce it to something like this: + // + // // @internal (undocumented) + // class _PreapprovedClass { /* (preapproved) */ } + // + + let skipRest: boolean = false; + for (const child of span.children) { + if (skipRest || child.kind === ts.SyntaxKind.SyntaxList || child.kind === ts.SyntaxKind.JSDocComment) { + child.modification.skipAll(); + } + if (child.kind === ts.SyntaxKind.Identifier) { + skipRest = true; + child.modification.omitSeparatorAfter = true; + child.modification.suffix = ' { /* (preapproved) */ }'; + } + } +} - if (apiItemMetadata.isVirtual) { - footerParts.push('@virtual'); - } +/** + * Writes a synopsis of the AEDoc comments, which indicates the release tag, + * whether the item has been documented, and any warnings that were detected + * by the analysis. + */ +function _getAedocSynopsis( + collector: Collector, + astDeclaration: AstDeclaration, + messagesToReport: ExtractorMessage[] +): string { + const writer: IndentedWriter = new IndentedWriter(); + + for (const message of messagesToReport) { + _writeLineAsComments(writer, 'Warning: ' + message.formatMessageWithoutLocation()); + } - if (apiItemMetadata.isOverride) { - footerParts.push('@override'); - } + if (!collector.isAncillaryDeclaration(astDeclaration)) { + const footerParts: string[] = []; + const apiItemMetadata: ApiItemMetadata = collector.fetchApiItemMetadata(astDeclaration); - if (apiItemMetadata.isEventProperty) { - footerParts.push('@eventProperty'); + // 1. Release tag (if present) + if (!apiItemMetadata.releaseTagSameAsParent) { + if (apiItemMetadata.effectiveReleaseTag !== ReleaseTag.None) { + footerParts.push(ReleaseTag.getTagName(apiItemMetadata.effectiveReleaseTag)); } + } - if (apiItemMetadata.tsdocComment) { - if (apiItemMetadata.tsdocComment.deprecatedBlock) { - footerParts.push('@deprecated'); + // 2. Enumerate configured tags, reporting standard system tags first and then other configured tags. + // Note that the ordering we handle the standard tags is important for backwards compatibility. + // Also note that we had special mechanisms for checking whether or not an item is documented with these tags, + // so they are checked specially. + const { + '@sealed': reportSealedTag, + '@virtual': reportVirtualTag, + '@override': reportOverrideTag, + '@eventProperty': reportEventPropertyTag, + '@deprecated': reportDeprecatedTag, + ...otherTagsToReport + } = collector.extractorConfig.tagsToReport; + + // 2.a Check for standard tags and report those that are both configured and present in the metadata. + if (reportSealedTag && apiItemMetadata.isSealed) { + footerParts.push('@sealed'); + } + if (reportVirtualTag && apiItemMetadata.isVirtual) { + footerParts.push('@virtual'); + } + if (reportOverrideTag && apiItemMetadata.isOverride) { + footerParts.push('@override'); + } + if (reportEventPropertyTag && apiItemMetadata.isEventProperty) { + footerParts.push('@eventProperty'); + } + if (reportDeprecatedTag && apiItemMetadata.tsdocComment?.deprecatedBlock) { + footerParts.push('@deprecated'); + } + + // 2.b Check for other configured tags and report those that are present in the tsdoc metadata. + for (const [tag, reportTag] of Object.entries(otherTagsToReport)) { + if (reportTag) { + // If the tag was not handled specially, check if it is present in the metadata. + if (apiItemMetadata.tsdocComment?.customBlocks.some((block) => block.blockTag.tagName === tag)) { + footerParts.push(tag); + } else if (apiItemMetadata.tsdocComment?.modifierTagSet.hasTagName(tag)) { + footerParts.push(tag); } } + } - if (apiItemMetadata.needsDocumentation) { - footerParts.push('(undocumented)'); - } + // 3. If the item is undocumented, append notice at the end of the list + if (apiItemMetadata.undocumented) { + footerParts.push('(undocumented)'); - if (footerParts.length > 0) { - if (messagesToReport.length > 0) { - ApiReportGenerator._writeLineAsComments(writer, ''); // skip a line after the warnings - } + collector.messageRouter.addAnalyzerIssue( + ExtractorMessageId.Undocumented, + `Missing documentation for "${astDeclaration.astSymbol.localName}".`, + astDeclaration + ); + } - ApiReportGenerator._writeLineAsComments(writer, footerParts.join(' ')); + if (footerParts.length > 0) { + if (messagesToReport.length > 0) { + _writeLineAsComments(writer, ''); // skip a line after the warnings } - } - return writer.toString(); + _writeLineAsComments(writer, footerParts.join(' ')); + } } - private static _writeLineAsComments(writer: IndentedWriter, line: string): void { - const lines: string[] = Text.convertToLf(line).split('\n'); - for (const realLine of lines) { - writer.write('// '); - writer.write(realLine); - writer.writeLine(); - } + return writer.toString(); +} + +function _writeLineAsComments(writer: IndentedWriter, line: string): void { + const lines: string[] = Text.convertToLf(line).split('\n'); + for (const realLine of lines) { + writer.write('// '); + writer.write(realLine); + writer.writeLine(); } } diff --git a/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts b/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts index 6a41534fec7..bb81031afe4 100644 --- a/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts +++ b/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts @@ -3,6 +3,7 @@ /* eslint-disable no-bitwise */ import * as ts from 'typescript'; + import { DeclarationReference, ModuleSource, @@ -10,11 +11,12 @@ import { Navigation, Meaning } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; -import { INodePackageJson, InternalError } from '@rushstack/node-core-library'; +import { type INodePackageJson, InternalError } from '@rushstack/node-core-library'; + import { TypeScriptHelpers } from '../analyzer/TypeScriptHelpers'; import { TypeScriptInternals } from '../analyzer/TypeScriptInternals'; -import { Collector } from '../collector/Collector'; -import { CollectorEntity } from '../collector/CollectorEntity'; +import type { Collector } from '../collector/Collector'; +import type { CollectorEntity } from '../collector/CollectorEntity'; import { AstNamespaceImport } from '../analyzer/AstNamespaceImport'; export class DeclarationReferenceGenerator { @@ -32,7 +34,7 @@ export class DeclarationReferenceGenerator { public getDeclarationReferenceForIdentifier(node: ts.Identifier): DeclarationReference | undefined { const symbol: ts.Symbol | undefined = this._collector.typeChecker.getSymbolAtLocation(node); if (symbol !== undefined) { - const isExpression: boolean = DeclarationReferenceGenerator._isInExpressionContext(node); + const isExpression: boolean = _isInExpressionContext(node); return ( this.getDeclarationReferenceForSymbol( symbol, @@ -57,38 +59,6 @@ export class DeclarationReferenceGenerator { return this._symbolToDeclarationReference(symbol, meaning, /*includeModuleSymbols*/ false); } - private static _isInExpressionContext(node: ts.Node): boolean { - switch (node.parent.kind) { - case ts.SyntaxKind.TypeQuery: - case ts.SyntaxKind.ComputedPropertyName: - return true; - case ts.SyntaxKind.QualifiedName: - return DeclarationReferenceGenerator._isInExpressionContext(node.parent); - default: - return false; - } - } - - private static _isExternalModuleSymbol(symbol: ts.Symbol): boolean { - return ( - !!(symbol.flags & ts.SymbolFlags.ValueModule) && - symbol.valueDeclaration !== undefined && - ts.isSourceFile(symbol.valueDeclaration) - ); - } - - private static _isSameSymbol(left: ts.Symbol | undefined, right: ts.Symbol): boolean { - return ( - left === right || - !!( - left && - left.valueDeclaration && - right.valueDeclaration && - left.valueDeclaration === right.valueDeclaration - ) - ); - } - private _getNavigationToSymbol(symbol: ts.Symbol): Navigation { const declaration: ts.Declaration | undefined = TypeScriptHelpers.tryGetADeclaration(symbol); const sourceFile: ts.SourceFile | undefined = declaration?.getSourceFile(); @@ -100,11 +70,7 @@ export class DeclarationReferenceGenerator { const isFromExternalLibrary: boolean = !!sourceFile && this._collector.program.isSourceFileFromExternalLibrary(sourceFile); if (isGlobal || isFromExternalLibrary) { - if ( - parent && - parent.members && - DeclarationReferenceGenerator._isSameSymbol(parent.members.get(symbol.escapedName), symbol) - ) { + if (parent && parent.members && _isSameSymbol(parent.members.get(symbol.escapedName), symbol)) { return Navigation.Members; } @@ -123,11 +89,8 @@ export class DeclarationReferenceGenerator { // If its parent symbol is not a source file, then use either Exports or Members. If the parent symbol // is a source file, but it wasn't exported from the package entry point (in the check above), then the // symbol is a local, so fall through below. - if (parent && !DeclarationReferenceGenerator._isExternalModuleSymbol(parent)) { - if ( - parent.members && - DeclarationReferenceGenerator._isSameSymbol(parent.members.get(symbol.escapedName), symbol) - ) { + if (parent && !_isExternalModuleSymbol(parent)) { + if (parent.members && _isSameSymbol(parent.members.get(symbol.escapedName), symbol)) { return Navigation.Members; } @@ -141,55 +104,6 @@ export class DeclarationReferenceGenerator { return Navigation.Locals; } - private static _getMeaningOfSymbol(symbol: ts.Symbol, meaning: ts.SymbolFlags): Meaning | undefined { - if (symbol.flags & meaning & ts.SymbolFlags.Class) { - return Meaning.Class; - } - if (symbol.flags & meaning & ts.SymbolFlags.Enum) { - return Meaning.Enum; - } - if (symbol.flags & meaning & ts.SymbolFlags.Interface) { - return Meaning.Interface; - } - if (symbol.flags & meaning & ts.SymbolFlags.TypeAlias) { - return Meaning.TypeAlias; - } - if (symbol.flags & meaning & ts.SymbolFlags.Function) { - return Meaning.Function; - } - if (symbol.flags & meaning & ts.SymbolFlags.Variable) { - return Meaning.Variable; - } - if (symbol.flags & meaning & ts.SymbolFlags.Module) { - return Meaning.Namespace; - } - if (symbol.flags & meaning & ts.SymbolFlags.ClassMember) { - return Meaning.Member; - } - if (symbol.flags & meaning & ts.SymbolFlags.Constructor) { - return Meaning.Constructor; - } - if (symbol.flags & meaning & ts.SymbolFlags.EnumMember) { - return Meaning.Member; - } - if (symbol.flags & meaning & ts.SymbolFlags.Signature) { - if (symbol.escapedName === ts.InternalSymbolName.Call) { - return Meaning.CallSignature; - } - if (symbol.escapedName === ts.InternalSymbolName.New) { - return Meaning.ConstructSignature; - } - if (symbol.escapedName === ts.InternalSymbolName.Index) { - return Meaning.IndexSignature; - } - } - if (symbol.flags & meaning & ts.SymbolFlags.TypeParameter) { - // This should have already been handled in `getDeclarationReferenceOfSymbol`. - throw new InternalError('Not supported.'); - } - return undefined; - } - private _symbolToDeclarationReference( symbol: ts.Symbol, meaning: ts.SymbolFlags, @@ -212,7 +126,7 @@ export class DeclarationReferenceGenerator { } } - if (DeclarationReferenceGenerator._isExternalModuleSymbol(followedSymbol)) { + if (_isExternalModuleSymbol(followedSymbol)) { if (!includeModuleSymbols) { return undefined; } @@ -268,7 +182,7 @@ export class DeclarationReferenceGenerator { return parentRef .addNavigationStep(navigation, localName) - .withMeaning(DeclarationReferenceGenerator._getMeaningOfSymbol(followedSymbol, meaning)); + .withMeaning(_getMeaningOfSymbol(followedSymbol, meaning)); } private _getParentReference(symbol: ts.Symbol): DeclarationReference | undefined { @@ -378,3 +292,84 @@ export class DeclarationReferenceGenerator { return GlobalSource.instance; } } + +function _isInExpressionContext(node: ts.Node): boolean { + switch (node.parent.kind) { + case ts.SyntaxKind.TypeQuery: + case ts.SyntaxKind.ComputedPropertyName: + return true; + case ts.SyntaxKind.QualifiedName: + return _isInExpressionContext(node.parent); + default: + return false; + } +} + +function _isExternalModuleSymbol(symbol: ts.Symbol): boolean { + return ( + !!(symbol.flags & ts.SymbolFlags.ValueModule) && + symbol.valueDeclaration !== undefined && + ts.isSourceFile(symbol.valueDeclaration) + ); +} + +function _isSameSymbol(left: ts.Symbol | undefined, right: ts.Symbol): boolean { + return ( + left === right || + !!( + left && + left.valueDeclaration && + right.valueDeclaration && + left.valueDeclaration === right.valueDeclaration + ) + ); +} + +function _getMeaningOfSymbol(symbol: ts.Symbol, meaning: ts.SymbolFlags): Meaning | undefined { + if (symbol.flags & meaning & ts.SymbolFlags.Class) { + return Meaning.Class; + } + if (symbol.flags & meaning & ts.SymbolFlags.Enum) { + return Meaning.Enum; + } + if (symbol.flags & meaning & ts.SymbolFlags.Interface) { + return Meaning.Interface; + } + if (symbol.flags & meaning & ts.SymbolFlags.TypeAlias) { + return Meaning.TypeAlias; + } + if (symbol.flags & meaning & ts.SymbolFlags.Function) { + return Meaning.Function; + } + if (symbol.flags & meaning & ts.SymbolFlags.Variable) { + return Meaning.Variable; + } + if (symbol.flags & meaning & ts.SymbolFlags.Module) { + return Meaning.Namespace; + } + if (symbol.flags & meaning & ts.SymbolFlags.ClassMember) { + return Meaning.Member; + } + if (symbol.flags & meaning & ts.SymbolFlags.Constructor) { + return Meaning.Constructor; + } + if (symbol.flags & meaning & ts.SymbolFlags.EnumMember) { + return Meaning.Member; + } + if (symbol.flags & meaning & ts.SymbolFlags.Signature) { + if (symbol.escapedName === ts.InternalSymbolName.Call) { + return Meaning.CallSignature; + } + if (symbol.escapedName === ts.InternalSymbolName.New) { + return Meaning.ConstructSignature; + } + if (symbol.escapedName === ts.InternalSymbolName.Index) { + return Meaning.IndexSignature; + } + } + if (symbol.flags & meaning & ts.SymbolFlags.TypeParameter) { + // This should have already been handled in `getDeclarationReferenceOfSymbol`. + throw new InternalError('Not supported.'); + } + return undefined; +} diff --git a/apps/api-extractor/src/generators/DtsEmitHelpers.ts b/apps/api-extractor/src/generators/DtsEmitHelpers.ts index 8095297b80d..7cde437bee8 100644 --- a/apps/api-extractor/src/generators/DtsEmitHelpers.ts +++ b/apps/api-extractor/src/generators/DtsEmitHelpers.ts @@ -4,13 +4,15 @@ import * as ts from 'typescript'; import { InternalError } from '@rushstack/node-core-library'; -import { CollectorEntity } from '../collector/CollectorEntity'; + +import type { CollectorEntity } from '../collector/CollectorEntity'; import { AstImport, AstImportKind } from '../analyzer/AstImport'; import { AstDeclaration } from '../analyzer/AstDeclaration'; -import { Collector } from '../collector/Collector'; -import { Span } from '../analyzer/Span'; -import { IndentedWriter } from './IndentedWriter'; +import type { Collector } from '../collector/Collector'; +import type { Span } from '../analyzer/Span'; +import type { IndentedWriter } from './IndentedWriter'; import { SourceFileLocationFormatter } from '../analyzer/SourceFileLocationFormatter'; +import { TypeScriptHelpers } from '../analyzer/TypeScriptHelpers'; /** * Some common code shared between DtsRollupGenerator and ApiReportGenerator. @@ -170,4 +172,124 @@ export class DtsEmitHelpers { } } } + + /** + * Checks if an export keyword is part of an ExportDeclaration inside a namespace + * (e.g., "export { Foo, Bar };" inside "declare namespace SDK { ... }"). + * In that case, the export keyword must be preserved, otherwise the output is invalid TypeScript. + */ + public static isExportKeywordInNamespaceExportDeclaration(node: ts.Node): boolean { + if (node.parent && ts.isExportDeclaration(node.parent)) { + const moduleBlock: ts.ModuleBlock | undefined = TypeScriptHelpers.findFirstParent( + node, + ts.SyntaxKind.ModuleBlock + ); + if (moduleBlock) { + return true; + } + } + return false; + } + + /** + * Given an array that includes some parameter nodes, this returns an array of the same length; + * elements that are not undefined correspond to a parameter that should be renamed. + */ + public static forEachParameterToNormalize( + nodes: ArrayLike, + action: (parameter: ts.ParameterDeclaration, syntheticName: string | undefined) => void + ): void { + let actionIndex: number = 0; + + // Optimistically assume that no parameters need to be normalized + for (actionIndex = 0; actionIndex < nodes.length; ++actionIndex) { + const parameter: ts.Node = nodes[actionIndex]; + if (!ts.isParameter(parameter)) { + continue; + } + action(parameter, undefined); + if (ts.isObjectBindingPattern(parameter.name) || ts.isArrayBindingPattern(parameter.name)) { + // Our optimistic assumption was not true; we'll need to stop and calculate alreadyUsedNames + break; + } + } + + if (actionIndex === nodes.length) { + // Our optimistic assumption was true + return; + } + + // First, calculate alreadyUsedNames + const alreadyUsedNames: string[] = []; + + for (let index: number = 0; index < nodes.length; ++index) { + const parameter: ts.Node = nodes[index]; + if (!ts.isParameter(parameter)) { + continue; + } + + if (!(ts.isObjectBindingPattern(parameter.name) || ts.isArrayBindingPattern(parameter.name))) { + alreadyUsedNames.push(parameter.name.text.trim()); + } + } + + // Now continue with the rest of the actions + for (; actionIndex < nodes.length; ++actionIndex) { + const parameter: ts.Node = nodes[actionIndex]; + if (!ts.isParameter(parameter)) { + continue; + } + + if (ts.isObjectBindingPattern(parameter.name) || ts.isArrayBindingPattern(parameter.name)) { + // Examples: + // + // function f({ y, z }: { y: string, z: string }) + // ---> function f(input: { y: string, z: string }) + // + // function f(x: number, [a, b]: [number, number]) + // ---> function f(x: number, input: [number, number]) + // + // Example of a naming collision: + // + // function f({ a }: { a: string }, { b }: { b: string }, input2: string) + // ---> function f(input: { a: string }, input3: { b: string }, input2: string) + const baseName: string = 'input'; + let counter: number = 2; + + let syntheticName: string = baseName; + while (alreadyUsedNames.includes(syntheticName)) { + syntheticName = `${baseName}${counter++}`; + } + alreadyUsedNames.push(syntheticName); + + action(parameter, syntheticName); + } else { + action(parameter, undefined); + } + } + } + + public static normalizeParameterNames(signatureSpan: Span): void { + const syntheticNamesByNode: Map = new Map(); + + DtsEmitHelpers.forEachParameterToNormalize( + signatureSpan.node.getChildren(), + (parameter: ts.ParameterDeclaration, syntheticName: string | undefined): void => { + if (syntheticName !== undefined) { + syntheticNamesByNode.set(parameter.name, syntheticName); + } + } + ); + + if (syntheticNamesByNode.size > 0) { + signatureSpan.forEach((childSpan: Span): void => { + const syntheticName: string | undefined = syntheticNamesByNode.get(childSpan.node); + if (syntheticName !== undefined) { + childSpan.modification.prefix = syntheticName; + childSpan.modification.suffix = ''; + childSpan.modification.omitChildren = true; + } + }); + } + } } diff --git a/apps/api-extractor/src/generators/DtsRollupGenerator.ts b/apps/api-extractor/src/generators/DtsRollupGenerator.ts index b60623524d1..519e3b7efd5 100644 --- a/apps/api-extractor/src/generators/DtsRollupGenerator.ts +++ b/apps/api-extractor/src/generators/DtsRollupGenerator.ts @@ -1,28 +1,28 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -/* eslint-disable no-bitwise */ - import * as ts from 'typescript'; -import { FileSystem, NewlineKind, InternalError } from '@rushstack/node-core-library'; + import { ReleaseTag } from '@microsoft/api-extractor-model'; +import { FileSystem, type NewlineKind, InternalError } from '@rushstack/node-core-library'; -import { Collector } from '../collector/Collector'; +import type { Collector } from '../collector/Collector'; import { TypeScriptHelpers } from '../analyzer/TypeScriptHelpers'; -import { IndentDocCommentScope, Span, SpanModification } from '../analyzer/Span'; +import { IndentDocCommentScope, Span, type SpanModification } from '../analyzer/Span'; import { AstImport } from '../analyzer/AstImport'; -import { CollectorEntity } from '../collector/CollectorEntity'; +import type { CollectorEntity } from '../collector/CollectorEntity'; import { AstDeclaration } from '../analyzer/AstDeclaration'; -import { ApiItemMetadata } from '../collector/ApiItemMetadata'; +import type { ApiItemMetadata } from '../collector/ApiItemMetadata'; import { AstSymbol } from '../analyzer/AstSymbol'; -import { SymbolMetadata } from '../collector/SymbolMetadata'; +import type { SymbolMetadata } from '../collector/SymbolMetadata'; import { IndentedWriter } from './IndentedWriter'; import { DtsEmitHelpers } from './DtsEmitHelpers'; -import { DeclarationMetadata } from '../collector/DeclarationMetadata'; +import type { DeclarationMetadata } from '../collector/DeclarationMetadata'; import { AstNamespaceImport } from '../analyzer/AstNamespaceImport'; -import { AstModuleExportInfo } from '../analyzer/AstModule'; +import type { IAstModuleExportInfo } from '../analyzer/AstModule'; import { SourceFileLocationFormatter } from '../analyzer/SourceFileLocationFormatter'; -import { AstEntity } from '../analyzer/AstEntity'; +import { SyntaxHelpers } from '../analyzer/SyntaxHelpers'; +import type { AstEntity } from '../analyzer/AstEntity'; /** * Used with DtsRollupGenerator.writeTypingsFile() @@ -71,406 +71,427 @@ export class DtsRollupGenerator { const writer: IndentedWriter = new IndentedWriter(); writer.trimLeadingSpaces = true; - DtsRollupGenerator._generateTypingsFileContent(collector, writer, dtsKind); + _generateTypingsFileContent(collector, writer, dtsKind); FileSystem.writeFile(dtsFilename, writer.toString(), { convertLineEndings: newlineKind, ensureFolderExists: true }); } +} - private static _generateTypingsFileContent( - collector: Collector, - writer: IndentedWriter, - dtsKind: DtsRollupKind - ): void { - // Emit the @packageDocumentation comment at the top of the file - if (collector.workingPackage.tsdocParserContext) { - writer.trimLeadingSpaces = false; - writer.writeLine(collector.workingPackage.tsdocParserContext.sourceRange.toString()); - writer.trimLeadingSpaces = true; - writer.ensureSkippedLine(); - } +function _generateTypingsFileContent( + collector: Collector, + writer: IndentedWriter, + dtsKind: DtsRollupKind +): void { + // Emit the @packageDocumentation comment at the top of the file + if (collector.workingPackage.tsdocParserContext) { + writer.trimLeadingSpaces = false; + writer.writeLine(collector.workingPackage.tsdocParserContext.sourceRange.toString()); + writer.trimLeadingSpaces = true; + writer.ensureSkippedLine(); + } - // Emit the triple slash directives - for (const typeDirectiveReference of collector.dtsTypeReferenceDirectives) { - // https://github.com/microsoft/TypeScript/blob/611ebc7aadd7a44a4c0447698bfda9222a78cb66/src/compiler/declarationEmitter.ts#L162 - writer.writeLine(`/// `); + // Emit the triple slash directives + for (const typeDirectiveReference of collector.dtsTypeReferenceDirectives) { + // https://github.com/microsoft/TypeScript/blob/611ebc7aadd7a44a4c0447698bfda9222a78cb66/src/compiler/declarationEmitter.ts#L162 + writer.writeLine(`/// `); + } + for (const libDirectiveReference of collector.dtsLibReferenceDirectives) { + writer.writeLine(`/// `); + } + writer.ensureSkippedLine(); + + // Emit the imports + for (const entity of collector.entities) { + if (entity.astEntity instanceof AstImport) { + // Note: it isn't valid to trim imports based on their release tags. + // E.g. class Foo (`@public`) extends interface Bar (`@beta`) from some external library. + // API-Extractor cannot trim `import { Bar } from "external-library"` when generating its public rollup, + // or the export of `Foo` would include a broken reference to `Bar`. + const astImport: AstImport = entity.astEntity; + DtsEmitHelpers.emitImport(writer, entity, astImport); } - for (const libDirectiveReference of collector.dtsLibReferenceDirectives) { - writer.writeLine(`/// `); + } + writer.ensureSkippedLine(); + + // Emit the regular declarations + for (const entity of collector.entities) { + const astEntity: AstEntity = entity.astEntity; + const symbolMetadata: SymbolMetadata | undefined = collector.tryFetchMetadataForAstEntity(astEntity); + const maxEffectiveReleaseTag: ReleaseTag = symbolMetadata + ? symbolMetadata.maxEffectiveReleaseTag + : ReleaseTag.None; + + if (!_shouldIncludeReleaseTag(maxEffectiveReleaseTag, dtsKind)) { + if (!collector.extractorConfig.omitTrimmingComments) { + writer.ensureSkippedLine(); + writer.writeLine(`/* Excluded from this release type: ${entity.nameForEmit} */`); + } + continue; } - writer.ensureSkippedLine(); - - // Emit the imports - for (const entity of collector.entities) { - if (entity.astEntity instanceof AstImport) { - const astImport: AstImport = entity.astEntity; - // For example, if the imported API comes from an external package that supports AEDoc, - // and it was marked as `@internal`, then don't emit it. - const symbolMetadata: SymbolMetadata | undefined = collector.tryFetchMetadataForAstEntity(astImport); - const maxEffectiveReleaseTag: ReleaseTag = symbolMetadata - ? symbolMetadata.maxEffectiveReleaseTag - : ReleaseTag.None; + if (astEntity instanceof AstSymbol) { + // Emit all the declarations for this entry + for (const astDeclaration of astEntity.astDeclarations || []) { + const apiItemMetadata: ApiItemMetadata = collector.fetchApiItemMetadata(astDeclaration); - if (this._shouldIncludeReleaseTag(maxEffectiveReleaseTag, dtsKind)) { - DtsEmitHelpers.emitImport(writer, entity, astImport); + if (!_shouldIncludeReleaseTag(apiItemMetadata.effectiveReleaseTag, dtsKind)) { + if (!collector.extractorConfig.omitTrimmingComments) { + writer.ensureSkippedLine(); + writer.writeLine(`/* Excluded declaration from this release type: ${entity.nameForEmit} */`); + } + continue; + } else { + const span: Span = new Span(astDeclaration.declaration); + _modifySpan(collector, span, entity, astDeclaration, dtsKind); + writer.ensureSkippedLine(); + span.writeModifiedText(writer); + writer.ensureNewLine(); } } } - writer.ensureSkippedLine(); - // Emit the regular declarations - for (const entity of collector.entities) { - const astEntity: AstEntity = entity.astEntity; - const symbolMetadata: SymbolMetadata | undefined = collector.tryFetchMetadataForAstEntity(astEntity); - const maxEffectiveReleaseTag: ReleaseTag = symbolMetadata - ? symbolMetadata.maxEffectiveReleaseTag - : ReleaseTag.None; + if (astEntity instanceof AstNamespaceImport) { + const astModuleExportInfo: IAstModuleExportInfo = astEntity.fetchAstModuleExportInfo(collector); - if (!this._shouldIncludeReleaseTag(maxEffectiveReleaseTag, dtsKind)) { - if (!collector.extractorConfig.omitTrimmingComments) { - writer.ensureSkippedLine(); - writer.writeLine(`/* Excluded from this release type: ${entity.nameForEmit} */`); - } - continue; + if (entity.nameForEmit === undefined) { + // This should never happen + throw new InternalError('referencedEntry.nameForEmit is undefined'); } - if (astEntity instanceof AstSymbol) { - // Emit all the declarations for this entry - for (const astDeclaration of astEntity.astDeclarations || []) { - const apiItemMetadata: ApiItemMetadata = collector.fetchApiItemMetadata(astDeclaration); + if (astModuleExportInfo.starExportedExternalModules.size > 0) { + // We could support this, but we would need to find a way to safely represent it. + throw new Error( + `The ${entity.nameForEmit} namespace import includes a start export, which is not supported:\n` + + SourceFileLocationFormatter.formatDeclaration(astEntity.declaration) + ); + } - if (!this._shouldIncludeReleaseTag(apiItemMetadata.effectiveReleaseTag, dtsKind)) { - if (!collector.extractorConfig.omitTrimmingComments) { - writer.ensureSkippedLine(); - writer.writeLine(`/* Excluded declaration from this release type: ${entity.nameForEmit} */`); - } - continue; - } else { - const span: Span = new Span(astDeclaration.declaration); - DtsRollupGenerator._modifySpan(collector, span, entity, astDeclaration, dtsKind); - writer.ensureSkippedLine(); - span.writeModifiedText(writer); - writer.ensureNewLine(); - } - } + // Emit a synthetic declaration for the namespace. It will look like this: + // + // declare namespace example { + // export { + // f1, + // f2 + // } + // } + // + // Note that we do not try to relocate f1()/f2() to be inside the namespace because other type + // signatures may reference them directly (without using the namespace qualifier). + + writer.ensureSkippedLine(); + if (entity.shouldInlineExport) { + writer.write('export '); } + writer.writeLine(`declare namespace ${entity.nameForEmit} {`); - if (astEntity instanceof AstNamespaceImport) { - const astModuleExportInfo: AstModuleExportInfo = astEntity.fetchAstModuleExportInfo(collector); + // all local exports of local imported module are just references to top-level declarations + writer.increaseIndent(); + writer.writeLine('export {'); + writer.increaseIndent(); - if (entity.nameForEmit === undefined) { + const exportClauses: string[] = []; + for (const [exportedName, exportedEntity] of astModuleExportInfo.exportedLocalEntities) { + const collectorEntity: CollectorEntity | undefined = collector.tryGetCollectorEntity(exportedEntity); + if (collectorEntity === undefined) { // This should never happen - throw new InternalError('referencedEntry.nameForEmit is undefined'); - } - - if (astModuleExportInfo.starExportedExternalModules.size > 0) { - // We could support this, but we would need to find a way to safely represent it. - throw new Error( - `The ${entity.nameForEmit} namespace import includes a start export, which is not supported:\n` + - SourceFileLocationFormatter.formatDeclaration(astEntity.declaration) + // top-level exports of local imported module should be added as collector entities before + throw new InternalError( + `Cannot find collector entity for ${entity.nameForEmit}.${exportedEntity.localName}` ); } - // Emit a synthetic declaration for the namespace. It will look like this: - // - // declare namespace example { - // export { - // f1, - // f2 - // } - // } - // - // Note that we do not try to relocate f1()/f2() to be inside the namespace because other type - // signatures may reference them directly (without using the namespace qualifier). - - writer.ensureSkippedLine(); - if (entity.shouldInlineExport) { - writer.write('export '); - } - writer.writeLine(`declare namespace ${entity.nameForEmit} {`); - - // all local exports of local imported module are just references to top-level declarations - writer.increaseIndent(); - writer.writeLine('export {'); - writer.increaseIndent(); - - const exportClauses: string[] = []; - for (const [exportedName, exportedEntity] of astModuleExportInfo.exportedLocalEntities) { - const collectorEntity: CollectorEntity | undefined = - collector.tryGetCollectorEntity(exportedEntity); - if (collectorEntity === undefined) { - // This should never happen - // top-level exports of local imported module should be added as collector entities before - throw new InternalError( - `Cannot find collector entity for ${entity.nameForEmit}.${exportedEntity.localName}` - ); - } - - if (collectorEntity.nameForEmit === exportedName) { - exportClauses.push(collectorEntity.nameForEmit); - } else { - exportClauses.push(`${collectorEntity.nameForEmit} as ${exportedName}`); - } + // If the entity's declaration won't be included, then neither should the namespace export it + // This fixes the issue encountered here: https://github.com/microsoft/rushstack/issues/2791 + const exportedSymbolMetadata: SymbolMetadata | undefined = + collector.tryFetchMetadataForAstEntity(exportedEntity); + const exportedMaxEffectiveReleaseTag: ReleaseTag = exportedSymbolMetadata + ? exportedSymbolMetadata.maxEffectiveReleaseTag + : ReleaseTag.None; + if (!_shouldIncludeReleaseTag(exportedMaxEffectiveReleaseTag, dtsKind)) { + continue; } - writer.writeLine(exportClauses.join(',\n')); - writer.decreaseIndent(); - writer.writeLine('}'); // end of "export { ... }" - writer.decreaseIndent(); - writer.writeLine('}'); // end of "declare namespace { ... }" - } - - if (!entity.shouldInlineExport) { - for (const exportName of entity.exportNames) { - DtsEmitHelpers.emitNamedExport(writer, exportName, entity); + if (collectorEntity.nameForEmit === exportedName) { + exportClauses.push(collectorEntity.nameForEmit); + } else { + const safeExportedName: string = SyntaxHelpers.isSafeUnquotedMemberIdentifier(exportedName) + ? exportedName + : JSON.stringify(exportedName); + exportClauses.push(`${collectorEntity.nameForEmit} as ${safeExportedName}`); } } + writer.writeLine(exportClauses.join(',\n')); - writer.ensureSkippedLine(); + writer.decreaseIndent(); + writer.writeLine('}'); // end of "export { ... }" + writer.decreaseIndent(); + writer.writeLine('}'); // end of "declare namespace { ... }" } - DtsEmitHelpers.emitStarExports(writer, collector); + if (!entity.shouldInlineExport) { + for (const exportName of entity.exportNames) { + DtsEmitHelpers.emitNamedExport(writer, exportName, entity); + } + } - // Emit "export { }" which is a special directive that prevents consumers from importing declarations - // that don't have an explicit "export" modifier. writer.ensureSkippedLine(); - writer.writeLine('export { }'); } - /** - * Before writing out a declaration, _modifySpan() applies various fixups to make it nice. - */ - private static _modifySpan( - collector: Collector, - span: Span, - entity: CollectorEntity, - astDeclaration: AstDeclaration, - dtsKind: DtsRollupKind - ): void { - const previousSpan: Span | undefined = span.previousSibling; - - let recurseChildren: boolean = true; - switch (span.kind) { - case ts.SyntaxKind.JSDocComment: - // If the @packageDocumentation comment seems to be attached to one of the regular API items, - // omit it. It gets explictly emitted at the top of the file. - if (span.node.getText().match(/(?:\s|\*)@packageDocumentation(?:\s|\*)/gi)) { - span.modification.skipAll(); - } + DtsEmitHelpers.emitStarExports(writer, collector); - // For now, we don't transform JSDoc comment nodes at all - recurseChildren = false; - break; + // Emit "export { }" which is a special directive that prevents consumers from importing declarations + // that don't have an explicit "export" modifier. + writer.ensureSkippedLine(); + writer.writeLine('export { }'); +} - case ts.SyntaxKind.ExportKeyword: - case ts.SyntaxKind.DefaultKeyword: - case ts.SyntaxKind.DeclareKeyword: - // Delete any explicit "export" or "declare" keywords -- we will re-add them below +/** + * Before writing out a declaration, _modifySpan() applies various fixups to make it nice. + */ +function _modifySpan( + collector: Collector, + span: Span, + entity: CollectorEntity, + astDeclaration: AstDeclaration, + dtsKind: DtsRollupKind +): void { + const previousSpan: Span | undefined = span.previousSibling; + + let recurseChildren: boolean = true; + switch (span.kind) { + case ts.SyntaxKind.JSDocComment: + // If the @packageDocumentation comment seems to be attached to one of the regular API items, + // omit it. It gets explictly emitted at the top of the file. + if (span.node.getText().match(/(?:\s|\*)@packageDocumentation(?:\s|\*)/gi)) { span.modification.skipAll(); + } + + // For now, we don't transform JSDoc comment nodes at all + recurseChildren = false; + break; + + case ts.SyntaxKind.ExportKeyword: + if (DtsEmitHelpers.isExportKeywordInNamespaceExportDeclaration(span.node)) { + // This is an export declaration inside a namespace - preserve the export keyword break; + } + // Otherwise, delete the export keyword -- we will re-add it below + span.modification.skipAll(); + break; + + case ts.SyntaxKind.DefaultKeyword: + case ts.SyntaxKind.DeclareKeyword: + // Delete any explicit "export" or "declare" keywords -- we will re-add them below + span.modification.skipAll(); + break; + + case ts.SyntaxKind.InterfaceKeyword: + case ts.SyntaxKind.ClassKeyword: + case ts.SyntaxKind.EnumKeyword: + case ts.SyntaxKind.NamespaceKeyword: + case ts.SyntaxKind.ModuleKeyword: + case ts.SyntaxKind.TypeKeyword: + case ts.SyntaxKind.FunctionKeyword: + // Replace the stuff we possibly deleted above + let replacedModifiers: string = ''; + + // Add a declare statement for root declarations (but not for nested declarations) + if (!astDeclaration.parent) { + replacedModifiers += 'declare '; + } - case ts.SyntaxKind.InterfaceKeyword: - case ts.SyntaxKind.ClassKeyword: - case ts.SyntaxKind.EnumKeyword: - case ts.SyntaxKind.NamespaceKeyword: - case ts.SyntaxKind.ModuleKeyword: - case ts.SyntaxKind.TypeKeyword: - case ts.SyntaxKind.FunctionKeyword: - // Replace the stuff we possibly deleted above - let replacedModifiers: string = ''; - - // Add a declare statement for root declarations (but not for nested declarations) - if (!astDeclaration.parent) { - replacedModifiers += 'declare '; - } + if (entity.shouldInlineExport) { + replacedModifiers = 'export ' + replacedModifiers; + } - if (entity.shouldInlineExport) { - replacedModifiers = 'export ' + replacedModifiers; + if (previousSpan && previousSpan.kind === ts.SyntaxKind.SyntaxList) { + // If there is a previous span of type SyntaxList, then apply it before any other modifiers + // (e.g. "abstract") that appear there. + previousSpan.modification.prefix = replacedModifiers + previousSpan.modification.prefix; + } else { + // Otherwise just stick it in front of this span + span.modification.prefix = replacedModifiers + span.modification.prefix; + } + break; + + case ts.SyntaxKind.VariableDeclaration: + // Is this a top-level variable declaration? + // (The logic below does not apply to variable declarations that are part of an explicit "namespace" block, + // since the compiler prefers not to emit "declare" or "export" keywords for those declarations.) + if (!span.parent) { + // The VariableDeclaration node is part of a VariableDeclarationList, however + // the Entry.followedSymbol points to the VariableDeclaration part because + // multiple definitions might share the same VariableDeclarationList. + // + // Since we are emitting a separate declaration for each one, we need to look upwards + // in the ts.Node tree and write a copy of the enclosing VariableDeclarationList + // content (e.g. "var" from "var x=1, y=2"). + const list: ts.VariableDeclarationList | undefined = TypeScriptHelpers.matchAncestor(span.node, [ + ts.SyntaxKind.VariableDeclarationList, + ts.SyntaxKind.VariableDeclaration + ]); + if (!list) { + // This should not happen unless the compiler API changes somehow + throw new InternalError('Unsupported variable declaration'); } + const listPrefix: string = list + .getSourceFile() + .text.substring(list.getStart(), list.declarations[0].getStart()); + span.modification.prefix = 'declare ' + listPrefix + span.modification.prefix; + span.modification.suffix = ';'; - if (previousSpan && previousSpan.kind === ts.SyntaxKind.SyntaxList) { - // If there is a previous span of type SyntaxList, then apply it before any other modifiers - // (e.g. "abstract") that appear there. - previousSpan.modification.prefix = replacedModifiers + previousSpan.modification.prefix; - } else { - // Otherwise just stick it in front of this span - span.modification.prefix = replacedModifiers + span.modification.prefix; + if (entity.shouldInlineExport) { + span.modification.prefix = 'export ' + span.modification.prefix; } - break; - case ts.SyntaxKind.VariableDeclaration: - // Is this a top-level variable declaration? - // (The logic below does not apply to variable declarations that are part of an explicit "namespace" block, - // since the compiler prefers not to emit "declare" or "export" keywords for those declarations.) - if (!span.parent) { - // The VariableDeclaration node is part of a VariableDeclarationList, however - // the Entry.followedSymbol points to the VariableDeclaration part because - // multiple definitions might share the same VariableDeclarationList. - // - // Since we are emitting a separate declaration for each one, we need to look upwards - // in the ts.Node tree and write a copy of the enclosing VariableDeclarationList - // content (e.g. "var" from "var x=1, y=2"). - const list: ts.VariableDeclarationList | undefined = TypeScriptHelpers.matchAncestor(span.node, [ - ts.SyntaxKind.VariableDeclarationList, - ts.SyntaxKind.VariableDeclaration - ]); - if (!list) { - // This should not happen unless the compiler API changes somehow - throw new InternalError('Unsupported variable declaration'); - } - const listPrefix: string = list - .getSourceFile() - .text.substring(list.getStart(), list.declarations[0].getStart()); - span.modification.prefix = 'declare ' + listPrefix + span.modification.prefix; - span.modification.suffix = ';'; - - if (entity.shouldInlineExport) { - span.modification.prefix = 'export ' + span.modification.prefix; - } - - const declarationMetadata: DeclarationMetadata = collector.fetchDeclarationMetadata(astDeclaration); - if (declarationMetadata.tsdocParserContext) { - // Typically the comment for a variable declaration is attached to the outer variable statement - // (which may possibly contain multiple variable declarations), so it's not part of the Span. - // Instead we need to manually inject it. - let originalComment: string = declarationMetadata.tsdocParserContext.sourceRange.toString(); - if (!/\r?\n\s*$/.test(originalComment)) { - originalComment += '\n'; - } - span.modification.indentDocComment = IndentDocCommentScope.PrefixOnly; - span.modification.prefix = originalComment + span.modification.prefix; + const declarationMetadata: DeclarationMetadata = collector.fetchDeclarationMetadata(astDeclaration); + if (declarationMetadata.tsdocParserContext) { + // Typically the comment for a variable declaration is attached to the outer variable statement + // (which may possibly contain multiple variable declarations), so it's not part of the Span. + // Instead we need to manually inject it. + let originalComment: string = declarationMetadata.tsdocParserContext.sourceRange.toString(); + if (!/\r?\n\s*$/.test(originalComment)) { + originalComment += '\n'; } + span.modification.indentDocComment = IndentDocCommentScope.PrefixOnly; + span.modification.prefix = originalComment + span.modification.prefix; } - break; - - case ts.SyntaxKind.Identifier: - { - const referencedEntity: CollectorEntity | undefined = collector.tryGetEntityForNode( - span.node as ts.Identifier - ); + } + break; - if (referencedEntity) { - if (!referencedEntity.nameForEmit) { - // This should never happen - throw new InternalError('referencedEntry.nameForEmit is undefined'); - } + case ts.SyntaxKind.Identifier: + { + const referencedEntity: CollectorEntity | undefined = collector.tryGetEntityForNode( + span.node as ts.Identifier + ); - span.modification.prefix = referencedEntity.nameForEmit; - // For debugging: - // span.modification.prefix += '/*R=FIX*/'; - } else { - // For debugging: - // span.modification.prefix += '/*R=KEEP*/'; + if (referencedEntity) { + if (!referencedEntity.nameForEmit) { + // This should never happen + throw new InternalError('referencedEntry.nameForEmit is undefined'); } + + span.modification.prefix = referencedEntity.nameForEmit; + // For debugging: + // span.modification.prefix += '/*R=FIX*/'; + } else { + // For debugging: + // span.modification.prefix += '/*R=KEEP*/'; } - break; + } + break; + + case ts.SyntaxKind.ImportType: + DtsEmitHelpers.modifyImportTypeSpan( + collector, + span, + astDeclaration, + (childSpan, childAstDeclaration) => { + _modifySpan(collector, childSpan, entity, childAstDeclaration, dtsKind); + } + ); + break; + } - case ts.SyntaxKind.ImportType: - DtsEmitHelpers.modifyImportTypeSpan( - collector, - span, - astDeclaration, - (childSpan, childAstDeclaration) => { - DtsRollupGenerator._modifySpan(collector, childSpan, entity, childAstDeclaration, dtsKind); - } + if (recurseChildren) { + for (const child of span.children) { + let childAstDeclaration: AstDeclaration = astDeclaration; + + // Should we trim this node? + let trimmed: boolean = false; + if (AstDeclaration.isSupportedSyntaxKind(child.kind)) { + childAstDeclaration = collector.astSymbolTable.getChildAstDeclarationByNode( + child.node, + astDeclaration ); - break; - } + const releaseTag: ReleaseTag = + collector.fetchApiItemMetadata(childAstDeclaration).effectiveReleaseTag; - if (recurseChildren) { - for (const child of span.children) { - let childAstDeclaration: AstDeclaration = astDeclaration; + if (!_shouldIncludeReleaseTag(releaseTag, dtsKind)) { + let nodeToTrim: Span = child; - // Should we trim this node? - let trimmed: boolean = false; - if (AstDeclaration.isSupportedSyntaxKind(child.kind)) { - childAstDeclaration = collector.astSymbolTable.getChildAstDeclarationByNode( - child.node, - astDeclaration - ); - const releaseTag: ReleaseTag = - collector.fetchApiItemMetadata(childAstDeclaration).effectiveReleaseTag; - - if (!this._shouldIncludeReleaseTag(releaseTag, dtsKind)) { - let nodeToTrim: Span = child; - - // If we are trimming a variable statement, then we need to trim the outer VariableDeclarationList - // as well. - if (child.kind === ts.SyntaxKind.VariableDeclaration) { - const variableStatement: Span | undefined = child.findFirstParent( - ts.SyntaxKind.VariableStatement - ); - if (variableStatement !== undefined) { - nodeToTrim = variableStatement; - } + // If we are trimming a variable statement, then we need to trim the outer VariableDeclarationList + // as well. + if (child.kind === ts.SyntaxKind.VariableDeclaration) { + const variableStatement: Span | undefined = child.findFirstParent( + ts.SyntaxKind.VariableStatement + ); + if (variableStatement !== undefined) { + nodeToTrim = variableStatement; } + } - const modification: SpanModification = nodeToTrim.modification; + const modification: SpanModification = nodeToTrim.modification; - // Yes, trim it and stop here - const name: string = childAstDeclaration.astSymbol.localName; - modification.omitChildren = true; + // Yes, trim it and stop here + const name: string = childAstDeclaration.astSymbol.localName; + modification.omitChildren = true; - if (!collector.extractorConfig.omitTrimmingComments) { - modification.prefix = `/* Excluded from this release type: ${name} */`; - } else { - modification.prefix = ''; - } - modification.suffix = ''; + if (!collector.extractorConfig.omitTrimmingComments) { + modification.prefix = `/* Excluded from this release type: ${name} */`; + } else { + modification.prefix = ''; + } + modification.suffix = ''; - if (nodeToTrim.children.length > 0) { - // If there are grandchildren, then keep the last grandchild's separator, - // since it often has useful whitespace - modification.suffix = nodeToTrim.children[nodeToTrim.children.length - 1].separator; - } + if (nodeToTrim.children.length > 0) { + // If there are grandchildren, then keep the last grandchild's separator, + // since it often has useful whitespace + modification.suffix = nodeToTrim.children[nodeToTrim.children.length - 1].separator; + } - if (nodeToTrim.nextSibling) { - // If the thing we are trimming is followed by a comma, then trim the comma also. - // An example would be an enum member. - if (nodeToTrim.nextSibling.kind === ts.SyntaxKind.CommaToken) { - // Keep its separator since it often has useful whitespace - modification.suffix += nodeToTrim.nextSibling.separator; - nodeToTrim.nextSibling.modification.skipAll(); - } + if (nodeToTrim.nextSibling) { + // If the thing we are trimming is followed by a comma, then trim the comma also. + // An example would be an enum member. + if (nodeToTrim.nextSibling.kind === ts.SyntaxKind.CommaToken) { + // Keep its separator since it often has useful whitespace + modification.suffix += nodeToTrim.nextSibling.separator; + nodeToTrim.nextSibling.modification.skipAll(); } + } - trimmed = true; + if (modification.suffix.trim().length === 0 && modification.prefix.trim().length === 0) { + // In case of blank prefix and suffix, remove indentation to avoid blank lines in place of removed members + modification.suffix = ''; + modification.prefix = ''; } - } - if (!trimmed) { - DtsRollupGenerator._modifySpan(collector, child, entity, childAstDeclaration, dtsKind); + trimmed = true; } } + + if (!trimmed) { + _modifySpan(collector, child, entity, childAstDeclaration, dtsKind); + } } } +} - private static _shouldIncludeReleaseTag(releaseTag: ReleaseTag, dtsKind: DtsRollupKind): boolean { - switch (dtsKind) { - case DtsRollupKind.InternalRelease: - return true; - case DtsRollupKind.AlphaRelease: - return ( - releaseTag === ReleaseTag.Alpha || - releaseTag === ReleaseTag.Beta || - releaseTag === ReleaseTag.Public || - // NOTE: If the release tag is "None", then we don't have enough information to trim it - releaseTag === ReleaseTag.None - ); - case DtsRollupKind.BetaRelease: - return ( - releaseTag === ReleaseTag.Beta || - releaseTag === ReleaseTag.Public || - // NOTE: If the release tag is "None", then we don't have enough information to trim it - releaseTag === ReleaseTag.None - ); - case DtsRollupKind.PublicRelease: - return releaseTag === ReleaseTag.Public || releaseTag === ReleaseTag.None; - default: - throw new Error(`${DtsRollupKind[dtsKind]} is not implemented`); - } +function _shouldIncludeReleaseTag(releaseTag: ReleaseTag, dtsKind: DtsRollupKind): boolean { + switch (dtsKind) { + case DtsRollupKind.InternalRelease: + return true; + case DtsRollupKind.AlphaRelease: + return ( + releaseTag === ReleaseTag.Alpha || + releaseTag === ReleaseTag.Beta || + releaseTag === ReleaseTag.Public || + // NOTE: If the release tag is "None", then we don't have enough information to trim it + releaseTag === ReleaseTag.None + ); + case DtsRollupKind.BetaRelease: + return ( + releaseTag === ReleaseTag.Beta || + releaseTag === ReleaseTag.Public || + // NOTE: If the release tag is "None", then we don't have enough information to trim it + releaseTag === ReleaseTag.None + ); + case DtsRollupKind.PublicRelease: + return releaseTag === ReleaseTag.Public || releaseTag === ReleaseTag.None; + default: + throw new Error(`${DtsRollupKind[dtsKind]} is not implemented`); } } diff --git a/apps/api-extractor/src/generators/ExcerptBuilder.ts b/apps/api-extractor/src/generators/ExcerptBuilder.ts index 40e4a9c83e3..f318f7cd95c 100644 --- a/apps/api-extractor/src/generators/ExcerptBuilder.ts +++ b/apps/api-extractor/src/generators/ExcerptBuilder.ts @@ -2,26 +2,38 @@ // See LICENSE in the project root for license information. import * as ts from 'typescript'; -import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; -import { ExcerptTokenKind, IExcerptToken, IExcerptTokenRange } from '@microsoft/api-extractor-model'; + +import type { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; +import { + ExcerptTokenKind, + type IExcerptToken, + type IExcerptTokenRange +} from '@microsoft/api-extractor-model'; import { Span } from '../analyzer/Span'; -import { DeclarationReferenceGenerator } from './DeclarationReferenceGenerator'; -import { AstDeclaration } from '../analyzer/AstDeclaration'; +import type { DeclarationReferenceGenerator } from './DeclarationReferenceGenerator'; +import type { AstDeclaration } from '../analyzer/AstDeclaration'; +import { condenseTokens } from './condenseTokens'; /** * Used to provide ExcerptBuilder with a list of nodes whose token range we want to capture. */ -export interface IExcerptBuilderNodeToCapture { +export interface IExcerptBuilderNodeTransform { /** - * The node to capture + * The node to process */ - node: ts.Node | undefined; + node: ts.Node; + /** - * The token range whose startIndex/endIndex will be overwritten with the indexes for the - * tokens corresponding to IExcerptBuilderNodeToCapture.node + * A token range whose startIndex/endIndex will be overwritten with the indexes for the + * tokens corresponding to IExcerptBuilderNodeTransform.node */ - tokenRange: IExcerptTokenRange; + captureTokenRange?: IExcerptTokenRange; + + /** + * Text that will replace the text of the given node during emit. + */ + replacementText?: string; } /** @@ -46,7 +58,7 @@ interface IBuildSpanState { */ stopBeforeChildKind: ts.SyntaxKind | undefined; - tokenRangesByNode: Map; + transformsByNode: Map; /** * Tracks whether the last appended token was a separator. If so, and we're in the middle of @@ -75,12 +87,12 @@ export class ExcerptBuilder { /** * Appends the signature for the specified `AstDeclaration` to the `excerptTokens` list. * @param excerptTokens - The target token list to append to - * @param nodesToCapture - A list of child nodes whose token ranges we want to capture + * @param nodeTransforms - A list of child nodes whose token ranges we want to capture */ public static addDeclaration( excerptTokens: IExcerptToken[], astDeclaration: AstDeclaration, - nodesToCapture: IExcerptBuilderNodeToCapture[], + nodeTransforms: IExcerptBuilderNodeTransform[], referenceGenerator: DeclarationReferenceGenerator ): void { let stopBeforeChildKind: ts.SyntaxKind | undefined = undefined; @@ -100,238 +112,169 @@ export class ExcerptBuilder { const span: Span = new Span(astDeclaration.declaration); - const tokenRangesByNode: Map = new Map(); - for (const excerpt of nodesToCapture || []) { - if (excerpt.node) { - tokenRangesByNode.set(excerpt.node, excerpt.tokenRange); + const transformsByNode: Map = new Map(); + const captureTokenRanges: IExcerptTokenRange[] = []; + for (const nodeTransform of nodeTransforms || []) { + transformsByNode.set(nodeTransform.node, nodeTransform); + if (nodeTransform.captureTokenRange) { + captureTokenRanges.push(nodeTransform.captureTokenRange); } } - ExcerptBuilder._buildSpan(excerptTokens, span, { + _buildSpan(excerptTokens, span, { referenceGenerator: referenceGenerator, startingNode: span.node, stopBeforeChildKind, - tokenRangesByNode, + transformsByNode: transformsByNode, lastAppendedTokenIsSeparator: false }); - ExcerptBuilder._condenseTokens(excerptTokens, [...tokenRangesByNode.values()]); + + condenseTokens(excerptTokens, captureTokenRanges); } public static createEmptyTokenRange(): IExcerptTokenRange { return { startIndex: 0, endIndex: 0 }; } +} - private static _buildSpan(excerptTokens: IExcerptToken[], span: Span, state: IBuildSpanState): boolean { - if (span.kind === ts.SyntaxKind.JSDocComment) { - // Discard any comments - return true; - } - - // Can this node start a excerpt? - const capturedTokenRange: IExcerptTokenRange | undefined = state.tokenRangesByNode.get(span.node); - let excerptStartIndex: number = 0; +/** @returns false if we encountered a token that causes iteration to stop. */ +function _buildSpan(excerptTokens: IExcerptToken[], span: Span, state: IBuildSpanState): boolean { + if (span.kind === ts.SyntaxKind.JSDocComment) { + // Discard any comments + return true; + } - if (capturedTokenRange) { - // We will assign capturedTokenRange.startIndex to be the index of the next token to be appended - excerptStartIndex = excerptTokens.length; - } + // Can this node start a excerpt? + const transform: IExcerptBuilderNodeTransform | undefined = state.transformsByNode.get(span.node); - if (span.prefix) { - let canonicalReference: DeclarationReference | undefined = undefined; + let captureTokenRange: IExcerptTokenRange | undefined = undefined; - if (span.kind === ts.SyntaxKind.Identifier) { - const name: ts.Identifier = span.node as ts.Identifier; - if (!ExcerptBuilder._isDeclarationName(name)) { - canonicalReference = state.referenceGenerator.getDeclarationReferenceForIdentifier(name); - } - } + if (transform) { + captureTokenRange = transform.captureTokenRange; + if (transform.replacementText !== undefined) { + excerptTokens.push({ + kind: ExcerptTokenKind.Content, + text: transform.replacementText + }); + state.lastAppendedTokenIsSeparator = false; - if (canonicalReference) { - ExcerptBuilder._appendToken( - excerptTokens, - ExcerptTokenKind.Reference, - span.prefix, - canonicalReference - ); - } else { - ExcerptBuilder._appendToken(excerptTokens, ExcerptTokenKind.Content, span.prefix); + if (captureTokenRange) { + captureTokenRange.startIndex = excerptTokens.length; + captureTokenRange.endIndex = captureTokenRange.startIndex + 1; } - state.lastAppendedTokenIsSeparator = false; + return true; } + } - for (const child of span.children) { - if (span.node === state.startingNode) { - if (state.stopBeforeChildKind && child.kind === state.stopBeforeChildKind) { - // We reached a child whose kind is stopBeforeChildKind, so stop traversing - return false; - } - } + let excerptStartIndex: number = 0; - if (!this._buildSpan(excerptTokens, child, state)) { - return false; + if (captureTokenRange) { + // We will assign capturedTokenRange.startIndex to be the index of the next token to be appended + excerptStartIndex = excerptTokens.length; + } + + if (span.prefix) { + let canonicalReference: DeclarationReference | undefined = undefined; + + if (span.kind === ts.SyntaxKind.Identifier) { + const name: ts.Identifier = span.node as ts.Identifier; + if (!_isDeclarationName(name)) { + canonicalReference = state.referenceGenerator.getDeclarationReferenceForIdentifier(name); } } - if (span.suffix) { - ExcerptBuilder._appendToken(excerptTokens, ExcerptTokenKind.Content, span.suffix); - state.lastAppendedTokenIsSeparator = false; + if (canonicalReference) { + _appendToken(excerptTokens, ExcerptTokenKind.Reference, span.prefix, canonicalReference); + } else { + _appendToken(excerptTokens, ExcerptTokenKind.Content, span.prefix); } - if (span.separator) { - ExcerptBuilder._appendToken(excerptTokens, ExcerptTokenKind.Content, span.separator); - state.lastAppendedTokenIsSeparator = true; - } - - // Are we building a excerpt? If so, set its range - if (capturedTokenRange) { - capturedTokenRange.startIndex = excerptStartIndex; + state.lastAppendedTokenIsSeparator = false; + } - // We will assign capturedTokenRange.startIndex to be the index after the last token - // that was appended so far. However, if the last appended token was a separator, omit - // it from the range. - let excerptEndIndex: number = excerptTokens.length; - if (state.lastAppendedTokenIsSeparator) { - excerptEndIndex--; + for (const child of span.children) { + if (span.node === state.startingNode) { + if (state.stopBeforeChildKind && child.kind === state.stopBeforeChildKind) { + // We reached a child whose kind is stopBeforeChildKind, so stop traversing + return false; } + } - capturedTokenRange.endIndex = excerptEndIndex; + if (!_buildSpan(excerptTokens, child, state)) { + return false; } + } - return true; + if (span.suffix) { + _appendToken(excerptTokens, ExcerptTokenKind.Content, span.suffix); + state.lastAppendedTokenIsSeparator = false; + } + if (span.separator) { + _appendToken(excerptTokens, ExcerptTokenKind.Content, span.separator); + state.lastAppendedTokenIsSeparator = true; } - private static _appendToken( - excerptTokens: IExcerptToken[], - excerptTokenKind: ExcerptTokenKind, - text: string, - canonicalReference?: DeclarationReference - ): void { - if (text.length === 0) { - return; - } + // Are we building a excerpt? If so, set its range + if (captureTokenRange) { + captureTokenRange.startIndex = excerptStartIndex; - const excerptToken: IExcerptToken = { kind: excerptTokenKind, text: text }; - if (canonicalReference !== undefined) { - excerptToken.canonicalReference = canonicalReference.toString(); + // We will assign capturedTokenRange.startIndex to be the index after the last token + // that was appended so far. However, if the last appended token was a separator, omit + // it from the range. + let excerptEndIndex: number = excerptTokens.length; + if (state.lastAppendedTokenIsSeparator) { + excerptEndIndex--; } - excerptTokens.push(excerptToken); + + captureTokenRange.endIndex = excerptEndIndex; } - /** - * Condenses the provided excerpt tokens by merging tokens where possible. Updates the provided token ranges to - * remain accurate after token merging. - * - * @remarks - * For example, suppose we have excerpt tokens ["A", "B", "C"] and a token range [0, 2]. If the excerpt tokens - * are condensed to ["AB", "C"], then the token range would be updated to [0, 1]. Note that merges are only - * performed if they are compatible with the provided token ranges. In the example above, if our token range was - * originally [0, 1], we would not be able to merge tokens "A" and "B". - */ - private static _condenseTokens(excerptTokens: IExcerptToken[], tokenRanges: IExcerptTokenRange[]): void { - // This set is used to quickly lookup a start or end index. - const startOrEndIndices: Set = new Set(); - for (const tokenRange of tokenRanges) { - startOrEndIndices.add(tokenRange.startIndex); - startOrEndIndices.add(tokenRange.endIndex); - } + return true; +} - for (let currentIndex: number = 1; currentIndex < excerptTokens.length; ++currentIndex) { - while (currentIndex < excerptTokens.length) { - const prevPrevToken: IExcerptToken = excerptTokens[currentIndex - 2]; // May be undefined - const prevToken: IExcerptToken = excerptTokens[currentIndex - 1]; - const currentToken: IExcerptToken = excerptTokens[currentIndex]; - - // The number of excerpt tokens that are merged in this iteration. We need this to determine - // how to update the start and end indices of our token ranges. - let mergeCount: number; - - // There are two types of merges that can occur. We only perform these merges if they are - // compatible with all of our token ranges. - if ( - prevPrevToken && - prevPrevToken.kind === ExcerptTokenKind.Reference && - prevToken.kind === ExcerptTokenKind.Content && - prevToken.text.trim() === '.' && - currentToken.kind === ExcerptTokenKind.Reference && - !startOrEndIndices.has(currentIndex) && - !startOrEndIndices.has(currentIndex - 1) - ) { - // If the current token is a reference token, the previous token is a ".", and the previous- - // previous token is a reference token, then merge all three tokens into a reference token. - // - // For example: Given ["MyNamespace" (R), ".", "MyClass" (R)], tokens "." and "MyClass" might - // be merged into "MyNamespace". The condensed token would be ["MyNamespace.MyClass" (R)]. - prevPrevToken.text += prevToken.text + currentToken.text; - prevPrevToken.canonicalReference = currentToken.canonicalReference; - mergeCount = 2; - currentIndex--; - } else if ( - // If the current and previous tokens are both content tokens, then merge the tokens into a - // single content token. For example: Given ["export ", "declare class"], these tokens - // might be merged into "export declare class". - prevToken.kind === ExcerptTokenKind.Content && - prevToken.kind === currentToken.kind && - !startOrEndIndices.has(currentIndex) - ) { - prevToken.text += currentToken.text; - mergeCount = 1; - } else { - // Otherwise, no merging can occur here. Continue to the next index. - break; - } - - // Remove the now redundant excerpt token(s), as they were merged into a previous token. - excerptTokens.splice(currentIndex, mergeCount); - - // Update the start and end indices for all token ranges based upon how many excerpt - // tokens were merged and in what positions. - for (const tokenRange of tokenRanges) { - if (tokenRange.startIndex > currentIndex) { - tokenRange.startIndex -= mergeCount; - } - - if (tokenRange.endIndex > currentIndex) { - tokenRange.endIndex -= mergeCount; - } - } - - // Clear and repopulate our set with the updated indices. - startOrEndIndices.clear(); - for (const tokenRange of tokenRanges) { - startOrEndIndices.add(tokenRange.startIndex); - startOrEndIndices.add(tokenRange.endIndex); - } - } - } +function _appendToken( + excerptTokens: IExcerptToken[], + excerptTokenKind: ExcerptTokenKind, + text: string, + canonicalReference?: DeclarationReference +): void { + if (text.length === 0) { + return; } - private static _isDeclarationName(name: ts.Identifier): boolean { - return ExcerptBuilder._isDeclaration(name.parent) && name.parent.name === name; + const excerptToken: IExcerptToken = { kind: excerptTokenKind, text: text }; + if (canonicalReference !== undefined) { + excerptToken.canonicalReference = canonicalReference.toString(); } + excerptTokens.push(excerptToken); +} - private static _isDeclaration(node: ts.Node): node is ts.NamedDeclaration { - switch (node.kind) { - case ts.SyntaxKind.FunctionDeclaration: - case ts.SyntaxKind.FunctionExpression: - case ts.SyntaxKind.VariableDeclaration: - case ts.SyntaxKind.Parameter: - case ts.SyntaxKind.EnumDeclaration: - case ts.SyntaxKind.ClassDeclaration: - case ts.SyntaxKind.ClassExpression: - case ts.SyntaxKind.ModuleDeclaration: - case ts.SyntaxKind.MethodDeclaration: - case ts.SyntaxKind.MethodSignature: - case ts.SyntaxKind.PropertyDeclaration: - case ts.SyntaxKind.PropertySignature: - case ts.SyntaxKind.GetAccessor: - case ts.SyntaxKind.SetAccessor: - case ts.SyntaxKind.InterfaceDeclaration: - case ts.SyntaxKind.TypeAliasDeclaration: - case ts.SyntaxKind.TypeParameter: - case ts.SyntaxKind.EnumMember: - case ts.SyntaxKind.BindingElement: - return true; - default: - return false; - } +function _isDeclarationName(name: ts.Identifier): boolean { + return _isDeclaration(name.parent) && name.parent.name === name; +} + +function _isDeclaration(node: ts.Node): node is ts.NamedDeclaration { + switch (node.kind) { + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.FunctionExpression: + case ts.SyntaxKind.VariableDeclaration: + case ts.SyntaxKind.Parameter: + case ts.SyntaxKind.EnumDeclaration: + case ts.SyntaxKind.ClassDeclaration: + case ts.SyntaxKind.ClassExpression: + case ts.SyntaxKind.ModuleDeclaration: + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.MethodSignature: + case ts.SyntaxKind.PropertyDeclaration: + case ts.SyntaxKind.PropertySignature: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: + case ts.SyntaxKind.InterfaceDeclaration: + case ts.SyntaxKind.TypeAliasDeclaration: + case ts.SyntaxKind.TypeParameter: + case ts.SyntaxKind.EnumMember: + case ts.SyntaxKind.BindingElement: + return true; + default: + return false; } } diff --git a/apps/api-extractor/src/generators/IndentedWriter.ts b/apps/api-extractor/src/generators/IndentedWriter.ts index d2e60bff6bc..84c2a91e35b 100644 --- a/apps/api-extractor/src/generators/IndentedWriter.ts +++ b/apps/api-extractor/src/generators/IndentedWriter.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { StringBuilder, IStringBuilder } from '@rushstack/node-core-library'; +import { StringBuilder, type IStringBuilder } from '@rushstack/node-core-library'; /** * A utility for writing indented text. diff --git a/apps/api-extractor/src/generators/condenseTokens.ts b/apps/api-extractor/src/generators/condenseTokens.ts new file mode 100644 index 00000000000..ab4ae563169 --- /dev/null +++ b/apps/api-extractor/src/generators/condenseTokens.ts @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { + ExcerptTokenKind, + type IExcerptToken, + type IExcerptTokenRange +} from '@microsoft/api-extractor-model'; + +/** + * Condenses the provided excerpt tokens by merging tokens where possible. Updates the provided token ranges to + * remain accurate after token merging. + * + * @remarks + * For example, suppose we have excerpt tokens ["A", "B", "C"] and a token range [0, 2]. If the excerpt tokens + * are condensed to ["AB", "C"], then the token range would be updated to [0, 1]. Note that merges are only + * performed if they are compatible with the provided token ranges. In the example above, if our token range was + * originally [0, 1], we would not be able to merge tokens "A" and "B". + */ +export function condenseTokens(excerptTokens: IExcerptToken[], tokenRanges: IExcerptTokenRange[]): void { + const originalTokenCount: number = excerptTokens.length; + + // A token that sits at the start or end index of any token range must be preserved (never merged + // away), so that every range can be accurately remapped after condensing. These indices refer to + // the original positions in `excerptTokens`, and since preserved tokens are never removed, this + // set never needs to be rebuilt. + const preservedIndices: Set = new Set(); + for (const tokenRange of tokenRanges) { + preservedIndices.add(tokenRange.startIndex); + preservedIndices.add(tokenRange.endIndex); + } + + // Build the condensed token list in a single forward pass, treating it as a stack so that a + // token merged into its predecessor can itself be merged into a further predecessor (e.g. a + // chain of reference tokens such as "A" "." "B" "." "C"). + // + // `newIndexByOriginalIndex` maps each kept token's original index to its index in the condensed + // list, which is then used to remap the token ranges. Every range boundary refers to a preserved + // token (or, for an exclusive `endIndex`, the token count), and preserved tokens are never merged + // away, so a direct lookup always resolves. It is sized `originalTokenCount + 1` to hold the + // mapping for an `endIndex` equal to the token count. + const condensedTokens: IExcerptToken[] = []; + const newIndexByOriginalIndex: Int32Array = new Int32Array(originalTokenCount + 1); + + // Whether the token currently on top of the stack is preserved. Only consulted when that token is + // the "." of a reference merge; because a "." only ever becomes the top via a push (content tokens + // are always separated by references, so a "." is never uncovered by a pop), this scalar is always + // up to date at the point it is read, avoiding a parallel array of original indices. + let prevTokenIsPreserved: boolean = false; + + for (let currentIndex: number = 0; currentIndex < originalTokenCount; ++currentIndex) { + const currentToken: IExcerptToken = excerptTokens[currentIndex]; + const currentIsPreserved: boolean = preservedIndices.has(currentIndex); + const condensedCount: number = condensedTokens.length; + + // A preserved token must never be merged away, so merges are only attempted when the current + // token is not preserved. There are two types of merges that can occur, and both consume the + // current token. Reads of the top two stack entries are guarded so they never index out of + // bounds. + let merged: boolean = false; + if (!currentIsPreserved && condensedCount >= 1) { + const prevToken: IExcerptToken = condensedTokens[condensedCount - 1]; + + if ( + condensedCount >= 2 && + currentToken.kind === ExcerptTokenKind.Reference && + prevToken.kind === ExcerptTokenKind.Content && + prevToken.text.trim() === '.' && + !prevTokenIsPreserved && + condensedTokens[condensedCount - 2].kind === ExcerptTokenKind.Reference + ) { + // If the current token is a reference token, the previous token is a ".", and the previous- + // previous token is a reference token, then merge all three tokens into a reference token. + // + // For example: Given ["MyNamespace" (R), ".", "MyClass" (R)], tokens "." and "MyClass" might + // be merged into "MyNamespace". The condensed token would be ["MyNamespace.MyClass" (R)]. + const prevPrevToken: IExcerptToken = condensedTokens[condensedCount - 2]; + prevPrevToken.text += prevToken.text + currentToken.text; + prevPrevToken.canonicalReference = currentToken.canonicalReference; + + // The "." token (already kept) and the current token are both merged into prevPrevToken. + condensedTokens.pop(); + merged = true; + } else if ( + // If the current and previous tokens are both content tokens, then merge the tokens into a + // single content token. For example: Given ["export ", "declare class"], these tokens + // might be merged into "export declare class". + prevToken.kind === ExcerptTokenKind.Content && + currentToken.kind === ExcerptTokenKind.Content + ) { + prevToken.text += currentToken.text; + merged = true; + } + } + + if (!merged) { + // No merging occurred, so keep the current token, record its new index, and update the + // preservation flag to reflect the new top of the stack. + newIndexByOriginalIndex[currentIndex] = condensedTokens.length; + condensedTokens.push(currentToken); + prevTokenIsPreserved = currentIsPreserved; + } + } + + // Remap the token ranges directly. Each boundary is a preserved token's original index (or the + // token count, for an exclusive `endIndex`), which maps straight to its position in the condensed + // list. `endIndex` is clamped because it may equal the token count. + newIndexByOriginalIndex[originalTokenCount] = condensedTokens.length; + for (const tokenRange of tokenRanges) { + tokenRange.startIndex = newIndexByOriginalIndex[Math.min(tokenRange.startIndex, originalTokenCount)]; + tokenRange.endIndex = newIndexByOriginalIndex[Math.min(tokenRange.endIndex, originalTokenCount)]; + } + + // Replace the excerpt tokens in place with the condensed list. + excerptTokens.length = 0; + for (const token of condensedTokens) { + excerptTokens.push(token); + } +} diff --git a/apps/api-extractor/src/generators/test/__snapshots__/IndentedWriter.test.ts.snap b/apps/api-extractor/src/generators/test/__snapshots__/IndentedWriter.test.ts.snap index 6803fe9f220..1665c736ea8 100644 --- a/apps/api-extractor/src/generators/test/__snapshots__/IndentedWriter.test.ts.snap +++ b/apps/api-extractor/src/generators/test/__snapshots__/IndentedWriter.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`01 Demo from docs 1`] = ` "begin diff --git a/apps/api-extractor/src/generators/test/condenseTokens.test.ts b/apps/api-extractor/src/generators/test/condenseTokens.test.ts new file mode 100644 index 00000000000..8322a18fe54 --- /dev/null +++ b/apps/api-extractor/src/generators/test/condenseTokens.test.ts @@ -0,0 +1,250 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { + ExcerptTokenKind, + type IExcerptToken, + type IExcerptTokenRange +} from '@microsoft/api-extractor-model'; + +import { condenseTokens } from '../condenseTokens'; + +function content(text: string): IExcerptToken { + return { kind: ExcerptTokenKind.Content, text }; +} + +function reference(text: string, canonicalReference?: string): IExcerptToken { + return { kind: ExcerptTokenKind.Reference, text, canonicalReference }; +} + +/** + * A deliberately naive reference implementation of the token-condensing algorithm, used as an oracle + * in the randomized test below. It mirrors the original (pre-optimization) behavior: repeatedly + * splicing merged tokens out of the array and re-deriving the set of protected indices after each + * merge. It is intentionally O(n^2) and simple so that it is easy to verify by inspection. + */ +function condenseTokensReference(excerptTokens: IExcerptToken[], tokenRanges: IExcerptTokenRange[]): void { + const startOrEndIndices: Set = new Set(); + for (const tokenRange of tokenRanges) { + startOrEndIndices.add(tokenRange.startIndex); + startOrEndIndices.add(tokenRange.endIndex); + } + + for (let currentIndex: number = 1; currentIndex < excerptTokens.length; ++currentIndex) { + while (currentIndex < excerptTokens.length) { + const prevPrevToken: IExcerptToken = excerptTokens[currentIndex - 2]; + const prevToken: IExcerptToken = excerptTokens[currentIndex - 1]; + const currentToken: IExcerptToken = excerptTokens[currentIndex]; + + let mergeCount: number; + if ( + prevPrevToken && + prevPrevToken.kind === ExcerptTokenKind.Reference && + prevToken.kind === ExcerptTokenKind.Content && + prevToken.text.trim() === '.' && + currentToken.kind === ExcerptTokenKind.Reference && + !startOrEndIndices.has(currentIndex) && + !startOrEndIndices.has(currentIndex - 1) + ) { + prevPrevToken.text += prevToken.text + currentToken.text; + prevPrevToken.canonicalReference = currentToken.canonicalReference; + mergeCount = 2; + currentIndex--; + } else if ( + prevToken.kind === ExcerptTokenKind.Content && + prevToken.kind === currentToken.kind && + !startOrEndIndices.has(currentIndex) + ) { + prevToken.text += currentToken.text; + mergeCount = 1; + } else { + break; + } + + excerptTokens.splice(currentIndex, mergeCount); + for (const tokenRange of tokenRanges) { + if (tokenRange.startIndex > currentIndex) { + tokenRange.startIndex -= mergeCount; + } + if (tokenRange.endIndex > currentIndex) { + tokenRange.endIndex -= mergeCount; + } + } + + startOrEndIndices.clear(); + for (const tokenRange of tokenRanges) { + startOrEndIndices.add(tokenRange.startIndex); + startOrEndIndices.add(tokenRange.endIndex); + } + } + } +} + +describe(condenseTokens.name, () => { + it('merges adjacent content tokens', () => { + const tokens: IExcerptToken[] = [content('export '), content('declare '), content('class')]; + const ranges: IExcerptTokenRange[] = []; + + condenseTokens(tokens, ranges); + + expect(tokens).toEqual([content('export declare class')]); + }); + + it('does not merge content into an adjacent reference token', () => { + const tokens: IExcerptToken[] = [reference('Foo', 'foo'), content(' bar')]; + const ranges: IExcerptTokenRange[] = []; + + condenseTokens(tokens, ranges); + + expect(tokens).toEqual([reference('Foo', 'foo'), content(' bar')]); + }); + + it('merges a "Reference . Reference" sequence into a single reference token', () => { + const tokens: IExcerptToken[] = [ + reference('MyNamespace', 'ns'), + content('.'), + reference('MyClass', 'cls') + ]; + const ranges: IExcerptTokenRange[] = []; + + condenseTokens(tokens, ranges); + + // The canonical reference of the last reference token wins. + expect(tokens).toEqual([reference('MyNamespace.MyClass', 'cls')]); + }); + + it('merges a chain of reference tokens', () => { + const tokens: IExcerptToken[] = [ + reference('A', 'a'), + content('.'), + reference('B', 'b'), + content('.'), + reference('C', 'c') + ]; + const ranges: IExcerptTokenRange[] = []; + + condenseTokens(tokens, ranges); + + expect(tokens).toEqual([reference('A.B.C', 'c')]); + }); + + it('remaps a token range after merging preceding tokens', () => { + // ["A", "B", "C"] with range [0, 2) should become ["AB", "C"] with range [0, 1). + const tokens: IExcerptToken[] = [content('A'), content('B'), content('C')]; + const ranges: IExcerptTokenRange[] = [{ startIndex: 0, endIndex: 2 }]; + + condenseTokens(tokens, ranges); + + expect(tokens).toEqual([content('AB'), content('C')]); + expect(ranges).toEqual([{ startIndex: 0, endIndex: 1 }]); + }); + + it('does not merge across a range boundary', () => { + // With range [0, 1), token "B" is protected as the exclusive end, so "A" and "B" cannot merge. + const tokens: IExcerptToken[] = [content('A'), content('B'), content('C')]; + const ranges: IExcerptTokenRange[] = [{ startIndex: 0, endIndex: 1 }]; + + condenseTokens(tokens, ranges); + + expect(tokens).toEqual([content('A'), content('BC')]); + expect(ranges).toEqual([{ startIndex: 0, endIndex: 1 }]); + }); + + it('does not merge a reference chain when the "." token is a range boundary', () => { + const tokens: IExcerptToken[] = [reference('A', 'a'), content('.'), reference('B', 'b')]; + // The "." token (index 1) is the start of a range, so the reference merge must not occur. + const ranges: IExcerptTokenRange[] = [{ startIndex: 1, endIndex: 3 }]; + + condenseTokens(tokens, ranges); + + expect(tokens).toEqual([reference('A', 'a'), content('.'), reference('B', 'b')]); + expect(ranges).toEqual([{ startIndex: 1, endIndex: 3 }]); + }); + + it('handles a "." token that is itself a merged whitespace + dot content token', () => { + // The leading whitespace content token and the "." content token merge into " .", which then + // participates in the reference merge. The surviving "." token originates from the whitespace + // token's index, which is what protects the range remapping. + const tokens: IExcerptToken[] = [reference('A', 'a'), content(' '), content('.'), reference('B', 'b')]; + const ranges: IExcerptTokenRange[] = []; + + condenseTokens(tokens, ranges); + + expect(tokens).toEqual([reference('A .B', 'b')]); + }); + + it('remaps an exclusive endIndex equal to the token count', () => { + const tokens: IExcerptToken[] = [content('a'), content('b'), content('c')]; + const ranges: IExcerptTokenRange[] = [{ startIndex: 0, endIndex: 3 }]; + + condenseTokens(tokens, ranges); + + expect(tokens).toEqual([content('abc')]); + expect(ranges).toEqual([{ startIndex: 0, endIndex: 1 }]); + }); + + it('handles empty input', () => { + const tokens: IExcerptToken[] = []; + const ranges: IExcerptTokenRange[] = [{ startIndex: 0, endIndex: 0 }]; + + condenseTokens(tokens, ranges); + + expect(tokens).toEqual([]); + expect(ranges).toEqual([{ startIndex: 0, endIndex: 0 }]); + }); + + it('matches the reference implementation across many randomized inputs', () => { + let seed: number = 0x1234abcd; + // A small deterministic PRNG so the test is reproducible. + const nextRandom = (): number => { + seed ^= seed << 13; + seed ^= seed >>> 17; + seed ^= seed << 5; + return ((seed >>> 0) % 100000) / 100000; + }; + + const makeToken = (index: number): IExcerptToken => { + const roll: number = nextRandom(); + if (roll < 0.4) { + return reference('Ref' + index, 'cr' + index); + } + if (roll < 0.6) { + return content('.'); + } + if (roll < 0.78) { + return content(' '); + } + return content('txt' + index); + }; + + for (let iteration: number = 0; iteration < 5000; ++iteration) { + const tokenCount: number = Math.floor(nextRandom() * 10); + const baseTokens: IExcerptToken[] = []; + for (let i: number = 0; i < tokenCount; ++i) { + baseTokens.push(makeToken(i)); + } + + const rangeCount: number = Math.floor(nextRandom() * 3); + const baseRanges: IExcerptTokenRange[] = []; + for (let r: number = 0; r < rangeCount; ++r) { + const startIndex: number = Math.floor(nextRandom() * (tokenCount + 1)); + const endIndex: number = Math.min( + tokenCount, + startIndex + Math.floor(nextRandom() * (tokenCount + 1)) + ); + baseRanges.push({ startIndex, endIndex }); + } + + const actualTokens: IExcerptToken[] = baseTokens.map((token) => ({ ...token })); + const actualRanges: IExcerptTokenRange[] = baseRanges.map((range) => ({ ...range })); + condenseTokens(actualTokens, actualRanges); + + const expectedTokens: IExcerptToken[] = baseTokens.map((token) => ({ ...token })); + const expectedRanges: IExcerptTokenRange[] = baseRanges.map((range) => ({ ...range })); + condenseTokensReference(expectedTokens, expectedRanges); + + expect(actualTokens).toEqual(expectedTokens); + expect(actualRanges).toEqual(expectedRanges); + } + }); +}); diff --git a/apps/api-extractor/src/index.ts b/apps/api-extractor/src/index.ts index cd65453daaa..657d112b6d9 100644 --- a/apps/api-extractor/src/index.ts +++ b/apps/api-extractor/src/index.ts @@ -11,27 +11,31 @@ export { ConsoleMessageId } from './api/ConsoleMessageId'; -export { CompilerState, ICompilerStateCreateOptions } from './api/CompilerState'; +export { CompilerState, type ICompilerStateCreateOptions } from './api/CompilerState'; -export { Extractor, IExtractorInvokeOptions, ExtractorResult } from './api/Extractor'; +export { Extractor, type IExtractorInvokeOptions, ExtractorResult } from './api/Extractor'; export { - IExtractorConfigPrepareOptions, - IExtractorConfigLoadForFolderOptions, + type IExtractorConfigApiReport, + type IExtractorConfigPrepareOptions, + type IExtractorConfigLoadForFolderOptions, ExtractorConfig } from './api/ExtractorConfig'; +export type { IApiModelGenerationOptions } from './generators/ApiModelGenerator'; + export { ExtractorLogLevel } from './api/ExtractorLogLevel'; export { ExtractorMessage, - IExtractorMessageProperties, + type IExtractorMessageProperties, ExtractorMessageCategory } from './api/ExtractorMessage'; export { ExtractorMessageId } from './api/ExtractorMessageId'; -export { +export type { + ApiReportVariant, IConfigCompiler, IConfigApiReport, IConfigDocModel, @@ -40,5 +44,6 @@ export { IConfigMessageReportingRule, IConfigMessageReportingTable, IExtractorMessagesConfig, - IConfigFile + IConfigFile, + ReleaseTagForTrim } from './api/IConfigFile'; diff --git a/apps/api-extractor/src/schemas/api-extractor-defaults.json b/apps/api-extractor/src/schemas/api-extractor-defaults.json index d3a949e4c3f..bb6b6157aaf 100644 --- a/apps/api-extractor/src/schemas/api-extractor-defaults.json +++ b/apps/api-extractor/src/schemas/api-extractor-defaults.json @@ -30,7 +30,6 @@ "dtsRollup": { // ("enabled" is required) - "untrimmedFilePath": "/dist/.d.ts", "alphaTrimmedFilePath": "", "betaTrimmedFilePath": "", @@ -69,6 +68,9 @@ "logLevel": "warning", "addToApiReportFile": true }, + "ae-undocumented": { + "logLevel": "none" + }, "ae-unresolved-inheritdoc-reference": { "logLevel": "warning", "addToApiReportFile": true diff --git a/apps/api-extractor/src/schemas/api-extractor-template.json b/apps/api-extractor/src/schemas/api-extractor-template.json index c5b47c880aa..0f01118c2ec 100644 --- a/apps/api-extractor/src/schemas/api-extractor-template.json +++ b/apps/api-extractor/src/schemas/api-extractor-template.json @@ -38,7 +38,7 @@ * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor * analyzes the symbols exported by this module. * - * The file extension must be ".d.ts" and not ".ts". + * The file extension must be a declaration file (e.g. ".d.ts", ".d.mts", ".d.{extension}.ts"), not ".ts". * * The path is resolved relative to the folder of the config file that contains the setting; to change this, * prepend a folder token such as "". @@ -53,12 +53,19 @@ * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly - * imports library2. To avoid this, we can specify: + * imports library2. To avoid this, we might specify: * * "bundledPackages": [ "library2" ], * * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been * local files for library1. + * + * The "bundledPackages" elements may specify glob patterns using minimatch syntax. To ensure deterministic + * output, globs are expanded by matching explicitly declared top-level dependencies only. For example, + * the pattern below will NOT match "@my-company/example" unless it appears in a field such as "dependencies" + * or "devDependencies" of the project's package.json file: + * + * "bundledPackages": [ "@my-company/*" ], */ "bundledPackages": [], @@ -71,14 +78,6 @@ */ // "newlineKind": "crlf", - /** - * Set to true when invoking API Extractor's test harness. When `testMode` is true, the `toolVersion` field in the - * .api.json file is assigned an empty string to prevent spurious diffs in output files tracked for tests. - * - * DEFAULT VALUE: "false" - */ - // "testMode": false, - /** * Specifies how API Extractor sorts members of an enum when generating the .api.json file. By default, the output * files will be sorted alphabetically, which is "by-name". To keep the ordering in the source code, specify @@ -88,6 +87,14 @@ */ // "enumMemberOrder": "by-name", + /** + * Set to true when invoking API Extractor's test harness. When `testMode` is true, the `toolVersion` field in the + * .api.json file is assigned an empty string to prevent spurious diffs in output files tracked for tests. + * + * DEFAULT VALUE: "false" + */ + // "testMode": false, + /** * Determines how the TypeScript compiler engine will be invoked by API Extractor. */ @@ -138,15 +145,31 @@ "enabled": true /** - * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce - * a full file path. + * The base filename for the API report files, to be combined with "reportFolder" or "reportTempFolder" + * to produce the full file path. The "reportFileName" should not include any path separators such as + * "\" or "/". The "reportFileName" should not include a file extension, since API Extractor will automatically + * append an appropriate file extension such as ".api.md". If the "reportVariants" setting is used, then the + * file extension includes the variant name, for example "my-report.public.api.md" or "my-report.beta.api.md". + * The "complete" variant always uses the simple extension "my-report.api.md". * - * The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/". + * Previous versions of API Extractor required "reportFileName" to include the ".api.md" extension explicitly; + * for backwards compatibility, that is still accepted but will be discarded before applying the above rules. * * SUPPORTED TOKENS: , - * DEFAULT VALUE: ".api.md" + * DEFAULT VALUE: "" */ - // "reportFileName": ".api.md", + // "reportFileName": "", + + /** + * To support different approval requirements for different API levels, multiple "variants" of the API report can + * be generated. The "reportVariants" setting specifies a list of variants to be generated. If omitted, + * by default only the "complete" variant will be generated, which includes all @internal, @alpha, @beta, + * and @public items. Other possible variants are "alpha" (@alpha + @beta + @public), "beta" (@beta + @public), + * and "public" (@public only). + * + * DEFAULT VALUE: [ "complete" ] + */ + // "reportVariants": ["public", "beta"], /** * Specifies the folder where the API report file is written. The file name portion is determined by @@ -159,9 +182,9 @@ * prepend a folder token such as "". * * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/temp/" + * DEFAULT VALUE: "/etc/" */ - // "reportFolder": "/temp/", + // "reportFolder": "/etc/", /** * Specifies the folder where the temporary report file is written. The file name portion is determined by @@ -226,7 +249,7 @@ * item's file path is "api/ExtractorConfig.ts", the full URL file path would be * "https://github.com/microsoft/rushstack/tree/main/apps/api-extractor/api/ExtractorConfig.js". * - * Can be omitted if you don't need source code links in your API documentation reference. + * This setting can be omitted if you don't need source code links in your API documentation reference. * * SUPPORTED TOKENS: none * DEFAULT VALUE: "" @@ -261,6 +284,8 @@ * Specifies the output path for a .d.ts rollup file to be generated with trimming for an "alpha" release. * This file will include only declarations that are marked as "@public", "@beta", or "@alpha". * + * If the path is an empty string, then this file will not be written. + * * The path is resolved relative to the folder of the config file that contains the setting; to change this, * prepend a folder token such as "". * @@ -273,6 +298,8 @@ * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. * This file will include only declarations that are marked as "@public" or "@beta". * + * If the path is an empty string, then this file will not be written. + * * The path is resolved relative to the folder of the config file that contains the setting; to change this, * prepend a folder token such as "". * diff --git a/apps/api-extractor/src/schemas/api-extractor.schema.json b/apps/api-extractor/src/schemas/api-extractor.schema.json index 2d64af8b313..20773498c59 100644 --- a/apps/api-extractor/src/schemas/api-extractor.schema.json +++ b/apps/api-extractor/src/schemas/api-extractor.schema.json @@ -24,13 +24,20 @@ }, "bundledPackages": { - "description": "A list of NPM package names whose exports should be treated as part of this package.", + "description": "A list of NPM package names whose exports should be treated as part of this package. Also supports glob patterns.", "type": "array", "items": { "type": "string" } }, + "newlineKind": { + "description": "Specifies what type of newlines API Extractor should use when writing output files. By default, the output files will be written with Windows-style newlines. To use POSIX-style newlines, specify \"lf\" instead. To use the OS's default newline kind, specify \"os\".", + "type": "string", + "enum": ["crlf", "lf", "os"], + "default": "crlf" + }, + "enumMemberOrder": { "description": "Specifies how API Extractor sorts the members of an enum when generating the .api.json doc model. \n 'by-name': sort the items according to the enum member name \n 'preserve': keep the original order that items appear in the source code", "type": "string", @@ -38,6 +45,11 @@ "default": "by-name" }, + "testMode": { + "description": "Set to true invoking API Extractor's test harness. When \"testMode\" is true, the \"toolVersion\" field in the .api.json file is assigned an empty string to prevent spurious diffs in output files tracked for tests.", + "type": "boolean" + }, + "compiler": { "description": "Determines how the TypeScript compiler engine will be invoked by API Extractor.", "type": "object", @@ -68,8 +80,17 @@ }, "reportFileName": { - "description": "The filename for the API report files. It will be combined with \"reportFolder\" or \"reportTempFolder\" to produce a full file path. The file extension should be \".api.md\", and the string should not contain a path separator such as \"\\\" or \"/\".", - "type": "string" + "description": "The base filename for the API report files, to be combined with \"reportFolder\" or \"reportTempFolder\" to produce the full file path. The \"reportFileName\" should not include any path separators such as \"\\\" or \"/\". The \"reportFileName\" should not include a file extension, since API Extractor will automatically append an appropriate file extension such as \".api.md\". If the \"reportVariants\" setting is used, then the file extension includes the variant name, for example \"my-report.public.api.md\" or \"my-report.beta.api.md\". The \"complete\" variant always uses the simple extension \"my-report.api.md\".\n\nPrevious versions of API Extractor required \"reportFileName\" to include the \".api.md\" extension explicitly; for backwards compatibility, that is still accepted but will be discarded before applying the above rules.", + "type": ["string"] + }, + + "reportVariants": { + "description": "To support different approval requirements for different API levels, multiple \"variants\" of the API report can be generated. The \"reportVariants\" setting specifies a list of variants to be generated. If omitted, by default only the \"complete\" variant will be generated, which includes all @internal, @alpha, @beta, and @public items. Other possible variants are \"alpha\" (@alpha + @beta + @public), \"beta\" (@beta + @public), and \"public\" (@public only).", + "type": "array", + "items": { + "type": "string", + "enum": ["public", "beta", "alpha", "complete"] + } }, "reportFolder": { @@ -85,6 +106,17 @@ "includeForgottenExports": { "description": "Whether \"forgotten exports\" should be included in the API report file. Forgotten exports are declarations flagged with `ae-forgotten-export` warnings. See https://api-extractor.com/pages/messages/ae-forgotten-export/ to learn more.", "type": "boolean" + }, + + "tagsToReport": { + "description": "Specifies a list of TSDoc tags that should be reported in the API report file for items whose documentation contains them. This can be used to include standard TSDoc tags or custom ones. Specified tag names must begin with \"@\". By default, the following tags are reported: [@sealed, @virtual, @override, @eventProperty, @deprecated]. Tags will appear in the order they are specified in this list. Note that an item's release tag will always reported; this behavior cannot be overridden.", + "type": "object", + "patternProperties": { + "^@[^\\s]*$": { + "type": "boolean" + } + }, + "additionalProperties": false } }, "required": ["enabled"], @@ -110,6 +142,14 @@ "projectFolderUrl": { "description": "The base URL where the project's source code can be viewed on a website such as GitHub or Azure DevOps. This URL path corresponds to the `` path on disk. This URL is concatenated with the file paths serialized to the doc model to produce URL file paths to individual API items. For example, if the `projectFolderUrl` is \"https://github.com/microsoft/rushstack/tree/main/apps/api-extractor\" and an API item's file path is \"api/ExtractorConfig.ts\", the full URL file path would be \"https://github.com/microsoft/rushstack/tree/main/apps/api-extractor/api/ExtractorConfig.js\". Can be omitted if you don't need source code links in your API documentation reference.", "type": "string" + }, + "releaseTagsToTrim": { + "description": "Specifies a list of release tags that will be trimmed from the doc model. The default value is `[\"@internal\"]`.", + "type": "array", + "items": { + "enum": ["@internal", "@alpha", "@beta", "@public"] + }, + "uniqueItems": true } }, "required": ["enabled"], @@ -165,13 +205,6 @@ "additionalProperties": false }, - "newlineKind": { - "description": "Specifies what type of newlines API Extractor should use when writing output files. By default, the output files will be written with Windows-style newlines. To use POSIX-style newlines, specify \"lf\" instead. To use the OS's default newline kind, specify \"os\".", - "type": "string", - "enum": ["crlf", "lf", "os"], - "default": "crlf" - }, - "messages": { "description": "Configures how API Extractor reports error and warning messages produced during analysis.", "type": "object", @@ -190,11 +223,6 @@ } }, "additionalProperties": false - }, - - "testMode": { - "description": "Set to true invoking API Extractor's test harness. When \"testMode\" is true, the \"toolVersion\" field in the .api.json file is assigned an empty string to prevent spurious diffs in output files tracked for tests.", - "type": "boolean" } }, "required": ["mainEntryPointFilePath"], diff --git a/apps/api-extractor/src/start.ts b/apps/api-extractor/src/start.ts index f2ae9e9c45e..2274fea6b22 100644 --- a/apps/api-extractor/src/start.ts +++ b/apps/api-extractor/src/start.ts @@ -1,20 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as os from 'os'; -import colors from 'colors'; +import * as os from 'node:os'; + +import { Colorize } from '@rushstack/terminal'; import { ApiExtractorCommandLine } from './cli/ApiExtractorCommandLine'; import { Extractor } from './api/Extractor'; console.log( os.EOL + - colors.bold(`api-extractor ${Extractor.version} ` + colors.cyan(' - https://api-extractor.com/') + os.EOL) + Colorize.bold( + `api-extractor ${Extractor.version} ` + Colorize.cyan(' - https://api-extractor.com/') + os.EOL + ) ); const parser: ApiExtractorCommandLine = new ApiExtractorCommandLine(); -parser.execute().catch((error) => { - console.error(colors.red(`An unexpected error occurred: ${error}`)); +parser.executeAsync().catch((error) => { + console.error(Colorize.red(`An unexpected error occurred: ${error}`)); process.exit(1); }); diff --git a/apps/api-extractor/tsconfig.json b/apps/api-extractor/tsconfig.json index fbc2f5c0a6c..1a33d17b873 100644 --- a/apps/api-extractor/tsconfig.json +++ b/apps/api-extractor/tsconfig.json @@ -1,7 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["heft-jest", "node"] - } + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/apps/cpu-profile-summarizer/.npmignore b/apps/cpu-profile-summarizer/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/apps/cpu-profile-summarizer/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/apps/cpu-profile-summarizer/CHANGELOG.json b/apps/cpu-profile-summarizer/CHANGELOG.json new file mode 100644 index 00000000000..5d06484004e --- /dev/null +++ b/apps/cpu-profile-summarizer/CHANGELOG.json @@ -0,0 +1,1110 @@ +{ + "name": "@rushstack/cpu-profile-summarizer", + "entries": [ + { + "version": "0.2.22", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.22", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "0.2.21", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.21", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.12`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "0.2.20", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.20", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.11`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "0.2.19", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.19", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.10`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "0.2.18", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.18", + "date": "Mon, 08 Jun 2026 15:15:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "0.2.17", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.9`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "0.2.16", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.16", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "0.2.15", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.15", + "date": "Sat, 18 Apr 2026 03:47:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.8`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.14", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.12", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/cpu-profile-summarizer_v0.2.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "0.1.43", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.43", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "0.1.42", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.42", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "0.1.41", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.41", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "0.1.40", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.40", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "0.1.39", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.39", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "0.1.38", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.38", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "0.1.37", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.37", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "0.1.36", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.36", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + } + ] + } + }, + { + "version": "0.1.35", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.35", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + } + ] + } + }, + { + "version": "0.1.34", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.34", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + } + ] + } + }, + { + "version": "0.1.33", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.33", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + } + ] + } + }, + { + "version": "0.1.32", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.32", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + } + ] + } + }, + { + "version": "0.1.31", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.31", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + } + ] + } + }, + { + "version": "0.1.30", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.30", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + } + ] + } + }, + { + "version": "0.1.29", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.29", + "date": "Fri, 03 Oct 2025 20:10:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + } + ] + } + }, + { + "version": "0.1.28", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.28", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.5`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + } + ] + } + }, + { + "version": "0.1.27", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.27", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.4`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + } + ] + } + }, + { + "version": "0.1.26", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.26", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + } + ] + } + }, + { + "version": "0.1.25", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.25", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.3`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + } + ] + } + }, + { + "version": "0.1.24", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.24", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + } + ] + } + }, + { + "version": "0.1.23", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.23", + "date": "Fri, 01 Aug 2025 00:12:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + } + ] + } + }, + { + "version": "0.1.22", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.22", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + } + ] + } + }, + { + "version": "0.1.21", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.21", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + } + ] + } + }, + { + "version": "0.1.20", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.20", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + } + ] + } + }, + { + "version": "0.1.19", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.19", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + } + ] + } + }, + { + "version": "0.1.18", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.18", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + } + ] + } + }, + { + "version": "0.1.17", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.17", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + } + ] + } + }, + { + "version": "0.1.16", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.16", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + } + ] + } + }, + { + "version": "0.1.15", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.15", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + } + ] + } + }, + { + "version": "0.1.14", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.14", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + } + ] + } + }, + { + "version": "0.1.13", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.13", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + } + ] + } + }, + { + "version": "0.1.12", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.12", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + } + ] + } + }, + { + "version": "0.1.11", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.11", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.7`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + } + ] + } + }, + { + "version": "0.1.10", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.10", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + } + ] + } + }, + { + "version": "0.1.9", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.9", + "date": "Wed, 12 Mar 2025 00:11:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + } + ] + } + }, + { + "version": "0.1.8", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.8", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.6`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + } + ] + } + }, + { + "version": "0.1.7", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.7", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + } + ] + } + }, + { + "version": "0.1.6", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.6", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + } + ] + } + }, + { + "version": "0.1.5", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.5", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + } + ] + } + }, + { + "version": "0.1.4", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.4", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + } + ] + } + }, + { + "version": "0.1.3", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.3", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.2", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.1", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.5`" + }, + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/cpu-profile-summarizer_v0.1.0", + "date": "Sat, 08 Feb 2025 01:10:27 GMT", + "comments": { + "minor": [ + { + "comment": "Add new command line tool for summarizing data across a collection of .cpuprofile files." + } + ] + } + } + ] +} diff --git a/apps/cpu-profile-summarizer/CHANGELOG.md b/apps/cpu-profile-summarizer/CHANGELOG.md new file mode 100644 index 00000000000..d1e66d46acf --- /dev/null +++ b/apps/cpu-profile-summarizer/CHANGELOG.md @@ -0,0 +1,345 @@ +# Change Log - @rushstack/cpu-profile-summarizer + +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 0.2.22 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 0.2.21 +Fri, 17 Jul 2026 00:15:59 GMT + +_Version update only_ + +## 0.2.20 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.2.19 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.2.18 +Mon, 08 Jun 2026 15:15:49 GMT + +_Version update only_ + +## 0.2.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.2.16 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 0.2.15 +Sat, 18 Apr 2026 03:47:09 GMT + +_Version update only_ + +## 0.2.14 +Sat, 18 Apr 2026 00:15:16 GMT + +_Version update only_ + +## 0.2.13 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.2.12 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 0.2.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.2.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 0.2.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 0.2.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.2.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 0.2.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.2.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 0.2.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.2.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 0.2.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 0.2.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.2.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.1.43 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 0.1.42 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 0.1.41 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 0.1.40 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 0.1.39 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 0.1.38 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 0.1.37 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 0.1.36 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 0.1.35 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 0.1.34 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 0.1.33 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 0.1.32 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 0.1.31 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 0.1.30 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 0.1.29 +Fri, 03 Oct 2025 20:10:00 GMT + +_Version update only_ + +## 0.1.28 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 0.1.27 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.1.26 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.1.25 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.1.24 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.1.23 +Fri, 01 Aug 2025 00:12:48 GMT + +_Version update only_ + +## 0.1.22 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.1.21 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.1.20 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.1.19 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.1.18 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.1.17 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.1.16 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.1.15 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.1.14 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 0.1.13 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 0.1.12 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.1.11 +Tue, 25 Mar 2025 15:11:15 GMT + +_Version update only_ + +## 0.1.10 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.1.9 +Wed, 12 Mar 2025 00:11:31 GMT + +_Version update only_ + +## 0.1.8 +Tue, 11 Mar 2025 02:12:33 GMT + +_Version update only_ + +## 0.1.7 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.1.6 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.1.5 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.1.4 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 0.1.3 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.1.2 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.1.1 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.1.0 +Sat, 08 Feb 2025 01:10:27 GMT + +### Minor changes + +- Add new command line tool for summarizing data across a collection of .cpuprofile files. + diff --git a/apps/cpu-profile-summarizer/LICENSE b/apps/cpu-profile-summarizer/LICENSE new file mode 100644 index 00000000000..f354f2bbba9 --- /dev/null +++ b/apps/cpu-profile-summarizer/LICENSE @@ -0,0 +1,24 @@ +@rushstack/cpu-profile-summarizer + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. \ No newline at end of file diff --git a/apps/cpu-profile-summarizer/README.md b/apps/cpu-profile-summarizer/README.md new file mode 100644 index 00000000000..b7c59218377 --- /dev/null +++ b/apps/cpu-profile-summarizer/README.md @@ -0,0 +1,26 @@ +# @rushstack/cpu-profile-summarizer + +> 🚨 _EARLY PREVIEW RELEASE_ 🚨 +> +> Not all features are implemented yet. To provide suggestions, please +> [create a GitHub issue](https://github.com/microsoft/rushstack/issues/new/choose). +> If you have questions, see the [Rush Stack Help page](https://rushstack.io/pages/help/support/) +> for support resources. + +The `cpu-profile-summarizer` command line tool helps you: + +- Collate self/total CPU usage statistics for an entire monorepo worth of V8 .cpuprofile files + +## Usage + +It's recommended to install this package globally: + +``` +# Install the NPM package +npm install -g @rushstack/cpu-profile-summarizer + +# Process a folder of cpuprofile files into a summary tsv file +cpu-profile-summarizer --input FOLDER --output FILE.tsv +``` + +The output file is in the tab-separated values (tsv) format. \ No newline at end of file diff --git a/apps/cpu-profile-summarizer/bin/cpu-profile-aggregator b/apps/cpu-profile-summarizer/bin/cpu-profile-aggregator new file mode 100644 index 00000000000..eef2fc27066 --- /dev/null +++ b/apps/cpu-profile-summarizer/bin/cpu-profile-aggregator @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('../lib-commonjs/start.js'); diff --git a/apps/cpu-profile-summarizer/config/jest.config.json b/apps/cpu-profile-summarizer/config/jest.config.json new file mode 100644 index 00000000000..d1749681d90 --- /dev/null +++ b/apps/cpu-profile-summarizer/config/jest.config.json @@ -0,0 +1,3 @@ +{ + "extends": "local-node-rig/profiles/default/config/jest.config.json" +} diff --git a/apps/cpu-profile-summarizer/config/rig.json b/apps/cpu-profile-summarizer/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/apps/cpu-profile-summarizer/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/apps/cpu-profile-summarizer/eslint.config.js b/apps/cpu-profile-summarizer/eslint.config.js new file mode 100644 index 00000000000..ceb5a1bee40 --- /dev/null +++ b/apps/cpu-profile-summarizer/eslint.config.js @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + }, + rules: { + 'no-console': 'off' + } + } +]; diff --git a/apps/cpu-profile-summarizer/package.json b/apps/cpu-profile-summarizer/package.json new file mode 100644 index 00000000000..b853f73ffb2 --- /dev/null +++ b/apps/cpu-profile-summarizer/package.json @@ -0,0 +1,49 @@ +{ + "name": "@rushstack/cpu-profile-summarizer", + "version": "0.2.22", + "description": "CLI tool for running analytics on multiple V8 .cpuprofile files", + "repository": { + "type": "git", + "url": "https://github.com/microsoft/rushstack.git", + "directory": "apps/cpu-profile-summarizer" + }, + "bin": { + "cpu-profile-summarizer": "./bin/cpu-profile-summarizer" + }, + "license": "MIT", + "scripts": { + "start": "node lib/start", + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "dependencies": { + "@rushstack/ts-command-line": "workspace:*", + "@rushstack/worker-pool": "workspace:*" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "sideEffects": [ + "lib-commonjs/start.js", + "lib-esm/start.js" + ] +} diff --git a/apps/cpu-profile-summarizer/src/protocol.ts b/apps/cpu-profile-summarizer/src/protocol.ts new file mode 100644 index 00000000000..eddd992e4ae --- /dev/null +++ b/apps/cpu-profile-summarizer/src/protocol.ts @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IProfileSummary } from './types'; + +/** + * A message sent to a worker to process a file (or shutdown). + */ +export type IMessageToWorker = string | false; + +/** + * A message sent from a worker to the main thread on success. + */ +export interface IWorkerSuccessMessage { + type: 'success'; + /** + * The file requested to be processed. + */ + file: string; + /** + * The summary of the profile data. + */ + data: IProfileSummary; +} + +/** + * A message sent from a worker to the main thread on error. + */ +export interface IWorkerErrorMessage { + type: 'error'; + /** + * The file requested to be processed. + */ + file: string; + /** + * The error stack trace or message. + */ + data: string; +} + +/** + * A message sent from a worker to the main thread. + */ +export type IMessageFromWorker = IWorkerSuccessMessage | IWorkerErrorMessage; diff --git a/apps/cpu-profile-summarizer/src/start.ts b/apps/cpu-profile-summarizer/src/start.ts new file mode 100644 index 00000000000..8360ef05e9f --- /dev/null +++ b/apps/cpu-profile-summarizer/src/start.ts @@ -0,0 +1,191 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { once } from 'node:events'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import type { Worker } from 'node:worker_threads'; + +import { + type CommandLineStringListParameter, + type IRequiredCommandLineStringParameter, + CommandLineParser +} from '@rushstack/ts-command-line'; +import { WorkerPool } from '@rushstack/worker-pool'; + +import type { IMessageFromWorker } from './protocol'; +import type { INodeSummary, IProfileSummary } from './types'; + +/** + * Merges summarized information from multiple profiles into a single collection. + * @param accumulator - The collection to merge the nodes into + * @param values - The nodes to merge + */ +function mergeProfileSummaries( + accumulator: Map, + values: Iterable<[string, INodeSummary]> +): void { + for (const [nodeId, node] of values) { + const existing: INodeSummary | undefined = accumulator.get(nodeId); + if (!existing) { + accumulator.set(nodeId, node); + } else { + existing.selfTime += node.selfTime; + existing.totalTime += node.totalTime; + } + } +} + +/** + * Scans a directory and its subdirectories for CPU profiles. + * @param baseDir - The directory to recursively search for CPU profiles + * @returns All .cpuprofile files found in the directory and its subdirectories + */ +function findProfiles(baseDir: string): string[] { + baseDir = path.resolve(baseDir); + + const files: string[] = []; + const directories: string[] = [baseDir]; + + for (const dir of directories) { + const entries: fs.Dirent[] = fs.readdirSync(dir, { withFileTypes: true }); + for (const entry of entries) { + if (entry.isFile() && entry.name.endsWith('.cpuprofile')) { + files.push(`${dir}/${entry.name}`); + } else if (entry.isDirectory()) { + directories.push(`${dir}/${entry.name}`); + } + } + } + + return files; +} + +/** + * Processes a set of CPU profiles and aggregates the results. + * Uses a worker pool. + * @param profiles - The set of .cpuprofile files to process + * @returns A summary of the profiles + */ +async function processProfilesAsync(profiles: Set): Promise { + const maxWorkers: number = Math.min(profiles.size, os.availableParallelism()); + console.log(`Processing ${profiles.size} profiles using ${maxWorkers} workers...`); + const workerPool: WorkerPool = new WorkerPool({ + id: 'cpu-profile-summarizer', + maxWorkers, + workerScriptPath: path.resolve(__dirname, 'worker.js') + }); + + const summary: IProfileSummary = new Map(); + + let processed: number = 0; + await Promise.all( + Array.from(profiles, async (profile: string) => { + const worker: Worker = await workerPool.checkoutWorkerAsync(true); + const responsePromise: Promise = once(worker, 'message'); + worker.postMessage(profile); + const { 0: messageFromWorker } = await responsePromise; + if (messageFromWorker.type === 'error') { + console.error(`Error processing ${profile}: ${messageFromWorker.data}`); + } else { + ++processed; + console.log(`Processed ${profile} (${processed}/${profiles.size})`); + mergeProfileSummaries(summary, messageFromWorker.data); + } + workerPool.checkinWorker(worker); + }) + ); + + await workerPool.finishAsync(); + + return summary; +} + +function writeSummaryToTsv(tsvPath: string, summary: IProfileSummary): void { + const dir: string = path.dirname(tsvPath); + fs.mkdirSync(dir, { recursive: true }); + + let tsv: string = `Self Time (seconds)\tTotal Time (seconds)\tFunction Name\tURL\tLine\tColumn`; + for (const { selfTime, totalTime, functionName, url, lineNumber, columnNumber } of summary.values()) { + const selfSeconds: string = (selfTime / 1e6).toFixed(3); + const totalSeconds: string = (totalTime / 1e6).toFixed(3); + + tsv += `\n${selfSeconds}\t${totalSeconds}\t${functionName}\t${url}\t${lineNumber}\t${columnNumber}`; + } + + fs.writeFileSync(tsvPath, tsv, 'utf8'); + console.log(`Wrote summary to ${tsvPath}`); +} + +class CpuProfileSummarizerCommandLineParser extends CommandLineParser { + private readonly _inputParameter: CommandLineStringListParameter; + private readonly _outputParameter: IRequiredCommandLineStringParameter; + + public constructor() { + super({ + toolFilename: 'cpu-profile-summarizer', + toolDescription: + 'This tool summarizes the contents of multiple V8 .cpuprofile reports. ' + + 'For example, those generated by running `node --cpu-prof`.' + }); + + this._inputParameter = this.defineStringListParameter({ + parameterLongName: '--input', + parameterShortName: '-i', + description: 'The directory containing .cpuprofile files to summarize', + argumentName: 'DIR', + required: true + }); + + this._outputParameter = this.defineStringParameter({ + parameterLongName: '--output', + parameterShortName: '-o', + description: 'The output file to write the summary to', + argumentName: 'TSV_FILE', + required: true + }); + } + + protected override async onExecuteAsync(): Promise { + const input: readonly string[] = this._inputParameter.values; + const output: string = this._outputParameter.value; + + if (input.length === 0) { + throw new Error('No input directories provided'); + } + + const allProfiles: Set = new Set(); + for (const dir of input) { + const resolvedDir: string = path.resolve(dir); + console.log(`Collating CPU profiles from ${resolvedDir}...`); + const profiles: string[] = findProfiles(resolvedDir); + console.log(`Found ${profiles.length} profiles`); + for (const profile of profiles) { + allProfiles.add(profile); + } + } + + if (allProfiles.size === 0) { + throw new Error(`No profiles found`); + } + + const summary: IProfileSummary = await processProfilesAsync(allProfiles); + + writeSummaryToTsv(output, summary); + } +} + +process.exitCode = 1; +const parser: CpuProfileSummarizerCommandLineParser = new CpuProfileSummarizerCommandLineParser(); + +parser + .executeAsync() + .then((success: boolean) => { + if (success) { + process.exitCode = 0; + } + }) + .catch((error: Error) => { + console.error(error); + }); diff --git a/apps/cpu-profile-summarizer/src/types.ts b/apps/cpu-profile-summarizer/src/types.ts new file mode 100644 index 00000000000..34e54cbc498 --- /dev/null +++ b/apps/cpu-profile-summarizer/src/types.ts @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * Content of a V8 CPU Profile file + */ +export interface ICpuProfile { + nodes: INode[]; + /** + * Start time in microseconds. Offset is arbitrary. + */ + startTime: number; + /** + * End time in microseconds. Only relevant compared to `startTime`. + */ + endTime: number; + /** + * The identifier of the active node at each sample. + */ + samples: number[]; + /** + * The time deltas between samples, in microseconds. + */ + timeDeltas: number[]; +} + +/** + * A single stack frame in a CPU profile. + */ +export interface INode { + /** + * Identifier of the node. + * Referenced in the `children` field of other nodes and in the `samples` field of the profile. + */ + id: number; + /** + * The call frame of the function that was executing when the profile was taken. + */ + callFrame: ICallFrame; + /** + * The number of samples where this node was on top of the stack. + */ + hitCount: number; + /** + * The child nodes. + */ + children?: number[]; + /** + * Optional information about time spent on particular lines. + */ + positionTicks?: IPositionTick[]; +} + +/** + * The call frame of a profiler tick + */ +export interface ICallFrame { + /** + * The name of the function being executed, if any + */ + functionName: string; + /** + * An identifier for the script containing the function. + * Mostly relevant for new Function() and similar. + */ + scriptId: string; + /** + * The URL of the script being executed. + */ + url: string; + /** + * The line number in the script where the function is defined. + */ + lineNumber: number; + /** + * The column number in the line where the function is defined. + */ + columnNumber: number; +} + +/** + * Summarized information about a node in the CPU profile. + * Caller/callee information is discarded for brevity. + */ +export interface INodeSummary { + /** + * The name of the function being executed, if any + */ + functionName: string; + /** + * The URL of the script being executed + */ + url: string; + /** + * The line number in the script where the function is defined. + */ + lineNumber: number; + /** + * The column number in the line where the function is defined. + */ + columnNumber: number; + + /** + * Time spent while this function was the top of the stack, in microseconds. + */ + selfTime: number; + /** + * Time spent while this function was on the stack, in microseconds. + */ + totalTime: number; +} + +/** + * A collection of summarized information about nodes in a CPU profile. + * The keys contain the function name, url, line number, and column number of the node. + */ +export type IProfileSummary = Map; + +/** + * Information about a sample that is tied to a specific line within a function + */ +export interface IPositionTick { + /** + * The line number where the tick was recorded, within the script containing the executing function. + */ + line: number; + /** + * The number of samples where this line was active. + */ + ticks: number; +} diff --git a/apps/cpu-profile-summarizer/src/worker.ts b/apps/cpu-profile-summarizer/src/worker.ts new file mode 100644 index 00000000000..65b84db5a66 --- /dev/null +++ b/apps/cpu-profile-summarizer/src/worker.ts @@ -0,0 +1,173 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import fs from 'node:fs'; +import worker_threads from 'node:worker_threads'; + +import type { ICallFrame, ICpuProfile, INodeSummary, IProfileSummary } from './types'; +import type { IMessageToWorker } from './protocol'; + +interface ILocalTimeInfo { + self: number; + contributors: Set | undefined; +} + +/** + * Computes an identifier to use for summarizing call frames. + * @param callFrame - The call frame to compute the ID for + * @returns A portable string identifying the call frame + */ +function computeCallFrameId(callFrame: ICallFrame): string { + const { url, lineNumber, columnNumber, functionName } = callFrame; + return `${url}\0${lineNumber}\0${columnNumber}\0${functionName}`; +} + +/** + * Adds the contents of a .cpuprofile file to a summary. + * @param filePath - The path to the .cpuprofile file to read + * @param accumulator - The summary to add the profile to + */ +function addFileToSummary(filePath: string, accumulator: IProfileSummary): void { + const profile: ICpuProfile = JSON.parse(fs.readFileSync(filePath, 'utf8')); + addProfileToSummary(profile, accumulator); +} + +/** + * Adds a CPU profile to a summary. + * @param profile - The profile to add + * @param accumulator - The summary to add the profile to + * @returns + */ +function addProfileToSummary(profile: ICpuProfile, accumulator: IProfileSummary): IProfileSummary { + const { nodes, samples, timeDeltas, startTime, endTime }: ICpuProfile = profile; + + const localTimes: ILocalTimeInfo[] = []; + const nodeIdToIndex: Map = new Map(); + + function getIndexFromNodeId(id: number): number { + let index: number | undefined = nodeIdToIndex.get(id); + if (index === undefined) { + index = nodeIdToIndex.size; + nodeIdToIndex.set(id, index); + } + return index; + } + + for (let i: number = 0; i < nodes.length; i++) { + localTimes.push({ + self: 0, + contributors: undefined + }); + + const { id } = nodes[i]; + // Ensure that the mapping entry has been created. + getIndexFromNodeId(id); + } + + const duration: number = endTime - startTime; + let lastNodeTime: number = duration - timeDeltas[0]; + for (let i: number = 0; i < timeDeltas.length - 1; i++) { + const sampleDuration: number = timeDeltas[i + 1]; + const localTime: ILocalTimeInfo = localTimes[getIndexFromNodeId(samples[i])]; + localTime.self += sampleDuration; + lastNodeTime -= sampleDuration; + } + + localTimes[getIndexFromNodeId(samples[samples.length - 1])].self += lastNodeTime; + + // Have to pick the maximum totalTime for a given frame, + const nodesByFrame: Map> = new Map(); + + for (let i: number = 0; i < nodes.length; i++) { + const { callFrame } = nodes[i]; + const frameId: string = computeCallFrameId(callFrame); + + const nodesForFrame: Set | undefined = nodesByFrame.get(frameId); + if (nodesForFrame) { + nodesForFrame.add(i); + } else { + nodesByFrame.set(frameId, new Set([i])); + } + } + + for (const [frameId, contributors] of nodesByFrame) { + // To compute the total time spent in a node, we need to sum the self time of all contributors. + // We can't simply add up total times because a frame can recurse. + let selfTime: number = 0; + let totalTime: number = 0; + + let selfIndex: number | undefined; + for (const contributor of contributors) { + if (selfIndex === undefined) { + // The first contributor to a frame will always be itself. + selfIndex = contributor; + } + + const localTime: ILocalTimeInfo = localTimes[contributor]; + selfTime += localTime.self; + } + + const queue: Set = new Set(contributors); + for (const nodeIndex of queue) { + totalTime += localTimes[nodeIndex].self; + const { children } = nodes[nodeIndex]; + if (children) { + for (const childId of children) { + const childIndex: number = getIndexFromNodeId(childId); + queue.add(childIndex); + } + } + } + + const frame: INodeSummary | undefined = accumulator.get(frameId); + if (!frame) { + if (selfIndex === undefined) { + throw new Error('selfIndex should not be undefined'); + } + + const { + callFrame: { functionName, url, lineNumber, columnNumber } + } = nodes[selfIndex]; + + accumulator.set(frameId, { + functionName, + url, + lineNumber, + columnNumber, + + selfTime, + totalTime + }); + } else { + frame.selfTime += selfTime; + frame.totalTime += totalTime; + } + } + + return accumulator; +} + +const { parentPort } = worker_threads; +if (parentPort) { + const messageHandler = (message: IMessageToWorker): void => { + if (message === false) { + // Shutdown signal. + parentPort.removeListener('message', messageHandler); + parentPort.close(); + return; + } + + try { + const summary: IProfileSummary = new Map(); + addFileToSummary(message, summary); + parentPort.postMessage({ file: message, data: summary }); + } catch (error) { + parentPort.postMessage({ + file: message, + data: error.stack || error.message + }); + } + }; + + parentPort.on('message', messageHandler); +} diff --git a/apps/cpu-profile-summarizer/tsconfig.json b/apps/cpu-profile-summarizer/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/apps/cpu-profile-summarizer/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/apps/heft/.eslintrc.js b/apps/heft/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/apps/heft/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/apps/heft/.npmignore b/apps/heft/.npmignore index 26245a35adf..1d4b6d39ec5 100644 --- a/apps/heft/.npmignore +++ b/apps/heft/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,18 +21,18 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE +# README.md +# LICENSE -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- -# (Add your project-specific overrides here) -!/includes/** !UPGRADING.md diff --git a/apps/heft/CHANGELOG.json b/apps/heft/CHANGELOG.json index 70a61578e79..61a7eefc572 100644 --- a/apps/heft/CHANGELOG.json +++ b/apps/heft/CHANGELOG.json @@ -1,6 +1,3189 @@ { "name": "@rushstack/heft", "entries": [ + { + "version": "1.2.22", + "tag": "@rushstack/heft_v1.2.22", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.12`" + } + ] + } + }, + { + "version": "1.2.21", + "tag": "@rushstack/heft_v1.2.21", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "patch": [ + { + "comment": "Replace `@override` with the `override` keyword." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.6.11`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.12`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.11`" + } + ] + } + }, + { + "version": "1.2.20", + "tag": "@rushstack/heft_v1.2.20", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.11`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.6.10`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.11`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.10`" + } + ] + } + }, + { + "version": "1.2.19", + "tag": "@rushstack/heft_v1.2.19", + "date": "Sat, 13 Jun 2026 00:16:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.10`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.9`" + } + ] + } + }, + { + "version": "1.2.18", + "tag": "@rushstack/heft_v1.2.18", + "date": "Mon, 08 Jun 2026 15:15:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.8`" + } + ] + } + }, + { + "version": "1.2.17", + "tag": "@rushstack/heft_v1.2.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.10`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.6.9`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.9`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.7`" + } + ] + } + }, + { + "version": "1.2.16", + "tag": "@rushstack/heft_v1.2.16", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.6`" + } + ] + } + }, + { + "version": "1.2.15", + "tag": "@rushstack/heft_v1.2.15", + "date": "Sat, 18 Apr 2026 03:47:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.9`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.6.8`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.8`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.5`" + } + ] + } + }, + { + "version": "1.2.14", + "tag": "@rushstack/heft_v1.2.14", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.6.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.7`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.4`" + } + ] + } + }, + { + "version": "1.2.13", + "tag": "@rushstack/heft_v1.2.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.6.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.6`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.3`" + } + ] + } + }, + { + "version": "1.2.12", + "tag": "@rushstack/heft_v1.2.12", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.6`" + } + ] + } + }, + { + "version": "1.2.11", + "tag": "@rushstack/heft_v1.2.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.6.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.5`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.2`" + } + ] + } + }, + { + "version": "1.2.10", + "tag": "@rushstack/heft_v1.2.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.1`" + } + ] + } + }, + { + "version": "1.2.9", + "tag": "@rushstack/heft_v1.2.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.0`" + } + ] + } + }, + { + "version": "1.2.8", + "tag": "@rushstack/heft_v1.2.8", + "date": "Tue, 31 Mar 2026 15:14:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.6.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.8`" + } + ] + } + }, + { + "version": "1.2.7", + "tag": "@rushstack/heft_v1.2.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.7`" + } + ] + } + }, + { + "version": "1.2.6", + "tag": "@rushstack/heft_v1.2.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.6`" + } + ] + } + }, + { + "version": "1.2.5", + "tag": "@rushstack/heft_v1.2.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.6.3`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.7.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.5`" + } + ] + } + }, + { + "version": "1.2.4", + "tag": "@rushstack/heft_v1.2.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.6.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.4`" + } + ] + } + }, + { + "version": "1.2.3", + "tag": "@rushstack/heft_v1.2.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.3`" + } + ] + } + }, + { + "version": "1.2.2", + "tag": "@rushstack/heft_v1.2.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.2`" + } + ] + } + }, + { + "version": "1.2.1", + "tag": "@rushstack/heft_v1.2.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.6.1`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.7.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.1`" + } + ] + } + }, + { + "version": "1.2.0", + "tag": "@rushstack/heft_v1.2.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.0`" + } + ] + } + }, + { + "version": "1.1.14", + "tag": "@rushstack/heft_v1.1.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.3`" + } + ] + } + }, + { + "version": "1.1.13", + "tag": "@rushstack/heft_v1.1.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.2`" + } + ] + } + }, + { + "version": "1.1.12", + "tag": "@rushstack/heft_v1.1.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.1`" + } + ] + } + }, + { + "version": "1.1.11", + "tag": "@rushstack/heft_v1.1.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.0`" + } + ] + } + }, + { + "version": "1.1.10", + "tag": "@rushstack/heft_v1.1.10", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.5`" + } + ] + } + }, + { + "version": "1.1.9", + "tag": "@rushstack/heft_v1.1.9", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.7`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.5.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.7`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.4`" + } + ] + } + }, + { + "version": "1.1.8", + "tag": "@rushstack/heft_v1.1.8", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.6`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.5.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.6`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.3`" + } + ] + } + }, + { + "version": "1.1.7", + "tag": "@rushstack/heft_v1.1.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.5.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.5`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.2`" + } + ] + } + }, + { + "version": "1.1.6", + "tag": "@rushstack/heft_v1.1.6", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.5.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.1`" + } + ] + } + }, + { + "version": "1.1.5", + "tag": "@rushstack/heft_v1.1.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.0`" + } + ] + } + }, + { + "version": "1.1.4", + "tag": "@rushstack/heft_v1.1.4", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.54.0`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft_v1.1.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.5.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.3`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft_v1.1.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.5.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.2`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft_v1.1.1", + "date": "Wed, 08 Oct 2025 00:13:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft_v1.1.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.5`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.15`" + } + ] + } + }, + { + "version": "0.75.0", + "tag": "@rushstack/heft_v0.75.0", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "minor": [ + { + "comment": "Enhance logging in watch mode by allowing plugins to report detailed reasons for requesting rerun, e.g. specific changed files." + }, + { + "comment": "(BREAKING CHANGE) Make the `taskStart`/`taskFinish`/`phaseStart`/`phaseFinish` hooks synchronous to signify that they are not intended to be used for expensive work." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.14`" + } + ] + } + }, + { + "version": "0.74.5", + "tag": "@rushstack/heft_v0.74.5", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.13`" + } + ] + } + }, + { + "version": "0.74.4", + "tag": "@rushstack/heft_v0.74.4", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.4`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.12`" + } + ] + } + }, + { + "version": "0.74.3", + "tag": "@rushstack/heft_v0.74.3", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.11`" + } + ] + } + }, + { + "version": "0.74.2", + "tag": "@rushstack/heft_v0.74.2", + "date": "Fri, 01 Aug 2025 00:12:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.10`" + } + ] + } + }, + { + "version": "0.74.1", + "tag": "@rushstack/heft_v0.74.1", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.9`" + } + ] + } + }, + { + "version": "0.74.0", + "tag": "@rushstack/heft_v0.74.0", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "minor": [ + { + "comment": "Added support for task and phase lifecycle events, `taskStart`, `taskFinish`, `phaseStart`, `phaseFinish`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.3.0`" + } + ] + } + }, + { + "version": "0.73.6", + "tag": "@rushstack/heft_v0.73.6", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.8`" + } + ] + } + }, + { + "version": "0.73.5", + "tag": "@rushstack/heft_v0.73.5", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.7`" + } + ] + } + }, + { + "version": "0.73.4", + "tag": "@rushstack/heft_v0.73.4", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.41`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.6`" + } + ] + } + }, + { + "version": "0.73.3", + "tag": "@rushstack/heft_v0.73.3", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.1`" + } + ] + } + }, + { + "version": "0.73.2", + "tag": "@rushstack/heft_v0.73.2", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.5`" + } + ] + } + }, + { + "version": "0.73.1", + "tag": "@rushstack/heft_v0.73.1", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "patch": [ + { + "comment": "Update documentation for `extends`" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.4`" + } + ] + } + }, + { + "version": "0.73.0", + "tag": "@rushstack/heft_v0.73.0", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "minor": [ + { + "comment": "Add `globAsync` to task run options." + } + ] + } + }, + { + "version": "0.72.0", + "tag": "@rushstack/heft_v0.72.0", + "date": "Wed, 09 Apr 2025 00:11:02 GMT", + "comments": { + "minor": [ + { + "comment": "Add a method `tryLoadProjectConfigurationFileAsync(options, terminal)` to `HeftConfiguration`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.17.0`" + } + ] + } + }, + { + "version": "0.71.2", + "tag": "@rushstack/heft_v0.71.2", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.3`" + } + ] + } + }, + { + "version": "0.71.1", + "tag": "@rushstack/heft_v0.71.1", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.40`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.7`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.2`" + } + ] + } + }, + { + "version": "0.71.0", + "tag": "@rushstack/heft_v0.71.0", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "minor": [ + { + "comment": "Add a `numberOfCores` property to `HeftConfiguration`." + } + ] + } + }, + { + "version": "0.70.1", + "tag": "@rushstack/heft_v0.70.1", + "date": "Wed, 12 Mar 2025 00:11:31 GMT", + "comments": { + "patch": [ + { + "comment": "Revert `useNodeJSResolver: true` to deal with plugins that have an `exports` field that doesn't contain `./package.json`." + } + ] + } + }, + { + "version": "0.70.0", + "tag": "@rushstack/heft_v0.70.0", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "minor": [ + { + "comment": "Use `useNodeJSResolver: true` in `Import.resolvePackage` calls." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.39`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.6`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.1`" + } + ] + } + }, + { + "version": "0.69.3", + "tag": "@rushstack/heft_v0.69.3", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.0`" + } + ] + } + }, + { + "version": "0.69.2", + "tag": "@rushstack/heft_v0.69.2", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.51.1`" + } + ] + } + }, + { + "version": "0.69.1", + "tag": "@rushstack/heft_v0.69.1", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.51.0`" + } + ] + } + }, + { + "version": "0.69.0", + "tag": "@rushstack/heft_v0.69.0", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "minor": [ + { + "comment": "Expose `watchFs` on the incremental run options for tasks to give more flexibility when having Heft perform file watching than only invoking globs directly." + } + ] + } + }, + { + "version": "0.68.18", + "tag": "@rushstack/heft_v0.68.18", + "date": "Sat, 22 Feb 2025 01:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.50.1`" + } + ] + } + }, + { + "version": "0.68.17", + "tag": "@rushstack/heft_v0.68.17", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.6`" + } + ] + } + }, + { + "version": "0.68.16", + "tag": "@rushstack/heft_v0.68.16", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.5`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.38`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.5`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.50.0`" + } + ] + } + }, + { + "version": "0.68.15", + "tag": "@rushstack/heft_v0.68.15", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "patch": [ + { + "comment": "Prefer `os.availableParallelism()` to `os.cpus().length`." + } + ] + } + }, + { + "version": "0.68.14", + "tag": "@rushstack/heft_v0.68.14", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.37`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.49.2`" + } + ] + } + }, + { + "version": "0.68.13", + "tag": "@rushstack/heft_v0.68.13", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.36`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.49.1`" + } + ] + } + }, + { + "version": "0.68.12", + "tag": "@rushstack/heft_v0.68.12", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.49.0`" + } + ] + } + }, + { + "version": "0.68.11", + "tag": "@rushstack/heft_v0.68.11", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.35`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.48.1`" + } + ] + } + }, + { + "version": "0.68.10", + "tag": "@rushstack/heft_v0.68.10", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.1`" + } + ] + } + }, + { + "version": "0.68.9", + "tag": "@rushstack/heft_v0.68.9", + "date": "Tue, 03 Dec 2024 16:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.0`" + } + ] + } + }, + { + "version": "0.68.8", + "tag": "@rushstack/heft_v0.68.8", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.48.0`" + } + ] + } + }, + { + "version": "0.68.7", + "tag": "@rushstack/heft_v0.68.7", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.34`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.12`" + } + ] + } + }, + { + "version": "0.68.6", + "tag": "@rushstack/heft_v0.68.6", + "date": "Thu, 24 Oct 2024 00:15:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.8`" + } + ] + } + }, + { + "version": "0.68.5", + "tag": "@rushstack/heft_v0.68.5", + "date": "Mon, 21 Oct 2024 18:50:09 GMT", + "comments": { + "patch": [ + { + "comment": "Remove usage of true-case-path in favor of manually adjusting the drive letter casing to avoid confusing file system tracing tools with unnecessary directory enumerations." + } + ] + } + }, + { + "version": "0.68.4", + "tag": "@rushstack/heft_v0.68.4", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.11`" + } + ] + } + }, + { + "version": "0.68.3", + "tag": "@rushstack/heft_v0.68.3", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.10`" + } + ] + } + }, + { + "version": "0.68.2", + "tag": "@rushstack/heft_v0.68.2", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "patch": [ + { + "comment": "Ensure `configHash` for file copy incremental cache file is portable." + } + ] + } + }, + { + "version": "0.68.1", + "tag": "@rushstack/heft_v0.68.1", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "patch": [ + { + "comment": "Include all previous `inputFileVersions` in incremental copy files cache file during watch mode. Fix incorrect serialization of cache file for file copy." + } + ] + } + }, + { + "version": "0.68.0", + "tag": "@rushstack/heft_v0.68.0", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "minor": [ + { + "comment": "Update file copy logic to use an incremental cache file in the temp directory for the current task to avoid unnecessary file writes." + } + ] + } + }, + { + "version": "0.67.2", + "tag": "@rushstack/heft_v0.67.2", + "date": "Fri, 13 Sep 2024 00:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.33`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.8`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.9`" + } + ] + } + }, + { + "version": "0.67.1", + "tag": "@rushstack/heft_v0.67.1", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.32`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.7`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.8`" + } + ] + } + }, + { + "version": "0.67.0", + "tag": "@rushstack/heft_v0.67.0", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "minor": [ + { + "comment": "Add a `slashNormalizedBuildFolderPath` property to `HeftConfiguration`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.31`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.6`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.7`" + } + ] + } + }, + { + "version": "0.66.26", + "tag": "@rushstack/heft_v0.66.26", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.30`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.5`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.6`" + } + ] + } + }, + { + "version": "0.66.25", + "tag": "@rushstack/heft_v0.66.25", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.5`" + } + ] + } + }, + { + "version": "0.66.24", + "tag": "@rushstack/heft_v0.66.24", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.29`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.5.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.4`" + } + ] + } + }, + { + "version": "0.66.23", + "tag": "@rushstack/heft_v0.66.23", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.3`" + } + ] + } + }, + { + "version": "0.66.22", + "tag": "@rushstack/heft_v0.66.22", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.28`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.2`" + } + ] + } + }, + { + "version": "0.66.21", + "tag": "@rushstack/heft_v0.66.21", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.27`" + } + ] + } + }, + { + "version": "0.66.20", + "tag": "@rushstack/heft_v0.66.20", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "patch": [ + { + "comment": "Update schemas/templates/heft.json to reflect new settings" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.26`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.1`" + } + ] + } + }, + { + "version": "0.66.19", + "tag": "@rushstack/heft_v0.66.19", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.0`" + } + ] + } + }, + { + "version": "0.66.18", + "tag": "@rushstack/heft_v0.66.18", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.0`" + } + ] + } + }, + { + "version": "0.66.17", + "tag": "@rushstack/heft_v0.66.17", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.25`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.25`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.46.2`" + } + ] + } + }, + { + "version": "0.66.16", + "tag": "@rushstack/heft_v0.66.16", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.24`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.24`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.5`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.46.1`" + } + ] + } + }, + { + "version": "0.66.15", + "tag": "@rushstack/heft_v0.66.15", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.46.0`" + } + ] + } + }, + { + "version": "0.66.14", + "tag": "@rushstack/heft_v0.66.14", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.23`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.23`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.45.1`" + } + ] + } + }, + { + "version": "0.66.13", + "tag": "@rushstack/heft_v0.66.13", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.22`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.45.0`" + } + ] + } + }, + { + "version": "0.66.12", + "tag": "@rushstack/heft_v0.66.12", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.21`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.44.1`" + } + ] + } + }, + { + "version": "0.66.11", + "tag": "@rushstack/heft_v0.66.11", + "date": "Fri, 24 May 2024 00:15:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.44.0`" + } + ] + } + }, + { + "version": "0.66.10", + "tag": "@rushstack/heft_v0.66.10", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "patch": [ + { + "comment": "Update schema definitions to conform to strict schema-type validation." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.20`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.8`" + } + ] + } + }, + { + "version": "0.66.9", + "tag": "@rushstack/heft_v0.66.9", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.7`" + } + ] + } + }, + { + "version": "0.66.8", + "tag": "@rushstack/heft_v0.66.8", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.19`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.20.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.6`" + } + ] + } + }, + { + "version": "0.66.7", + "tag": "@rushstack/heft_v0.66.7", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.18`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.20.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.5`" + } + ] + } + }, + { + "version": "0.66.6", + "tag": "@rushstack/heft_v0.66.6", + "date": "Fri, 10 May 2024 05:33:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.17`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.5`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.4`" + } + ] + } + }, + { + "version": "0.66.5", + "tag": "@rushstack/heft_v0.66.5", + "date": "Wed, 08 May 2024 22:23:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.3`" + } + ] + } + }, + { + "version": "0.66.4", + "tag": "@rushstack/heft_v0.66.4", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.16`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.2`" + } + ] + } + }, + { + "version": "0.66.3", + "tag": "@rushstack/heft_v0.66.3", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.15`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.1`" + } + ] + } + }, + { + "version": "0.66.2", + "tag": "@rushstack/heft_v0.66.2", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.0`" + } + ] + } + }, + { + "version": "0.66.1", + "tag": "@rushstack/heft_v0.66.1", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "patch": [ + { + "comment": "Fix internal error when run 'heft clean'" + } + ] + } + }, + { + "version": "0.66.0", + "tag": "@rushstack/heft_v0.66.0", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "minor": [ + { + "comment": "Add new metrics value `bootDurationMs` to track the boot overhead of Heft before the action starts executing the subtasks. Update the start time used to compute `taskTotalExecutionMs` to be the beginning of operation graph execution. Fix the value of `taskTotalExecutionMs` field to be in milliseconds instead of seconds. Add new metrics value `totalUptimeMs` to track how long watch mode sessions are kept alive." + } + ] + } + }, + { + "version": "0.65.10", + "tag": "@rushstack/heft_v0.65.10", + "date": "Sun, 03 Mar 2024 20:58:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.3`" + } + ] + } + }, + { + "version": "0.65.9", + "tag": "@rushstack/heft_v0.65.9", + "date": "Sat, 02 Mar 2024 02:22:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.2`" + } + ] + } + }, + { + "version": "0.65.8", + "tag": "@rushstack/heft_v0.65.8", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.18.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.1`" + } + ] + } + }, + { + "version": "0.65.7", + "tag": "@rushstack/heft_v0.65.7", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.0`" + } + ] + } + }, + { + "version": "0.65.6", + "tag": "@rushstack/heft_v0.65.6", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.18.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.41.1`" + } + ] + } + }, + { + "version": "0.65.5", + "tag": "@rushstack/heft_v0.65.5", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.14`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.41.0`" + } + ] + } + }, + { + "version": "0.65.4", + "tag": "@rushstack/heft_v0.65.4", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.13`" + } + ] + } + }, + { + "version": "0.65.3", + "tag": "@rushstack/heft_v0.65.3", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.13`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.6`" + } + ] + } + }, + { + "version": "0.65.2", + "tag": "@rushstack/heft_v0.65.2", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.5`" + } + ] + } + }, + { + "version": "0.65.1", + "tag": "@rushstack/heft_v0.65.1", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "patch": [ + { + "comment": "Fix a recent regression causing `Error: Cannot find module 'colors/safe'` (GitHub #4525)" + }, + { + "comment": "Remove a no longer needed dependency on the `chokidar` package" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.4`" + } + ] + } + }, + { + "version": "0.65.0", + "tag": "@rushstack/heft_v0.65.0", + "date": "Tue, 20 Feb 2024 16:10:52 GMT", + "comments": { + "minor": [ + { + "comment": "Add a built-in `set-environment-variables-plugin` task plugin to set environment variables." + } + ] + } + }, + { + "version": "0.64.8", + "tag": "@rushstack/heft_v0.64.8", + "date": "Mon, 19 Feb 2024 21:54:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.11`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.3`" + } + ] + } + }, + { + "version": "0.64.7", + "tag": "@rushstack/heft_v0.64.7", + "date": "Sat, 17 Feb 2024 06:24:34 GMT", + "comments": { + "patch": [ + { + "comment": "Fix broken link to API documentation" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.10`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.5.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.2`" + } + ] + } + }, + { + "version": "0.64.6", + "tag": "@rushstack/heft_v0.64.6", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.8`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.1`" + } + ] + } + }, + { + "version": "0.64.5", + "tag": "@rushstack/heft_v0.64.5", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.0`" + } + ] + } + }, + { + "version": "0.64.4", + "tag": "@rushstack/heft_v0.64.4", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.7`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.5`" + } + ] + } + }, + { + "version": "0.64.3", + "tag": "@rushstack/heft_v0.64.3", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.6`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.4`" + } + ] + } + }, + { + "version": "0.64.2", + "tag": "@rushstack/heft_v0.64.2", + "date": "Tue, 23 Jan 2024 20:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.5`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.3`" + } + ] + } + }, + { + "version": "0.64.1", + "tag": "@rushstack/heft_v0.64.1", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.2`" + } + ] + } + }, + { + "version": "0.64.0", + "tag": "@rushstack/heft_v0.64.0", + "date": "Tue, 16 Jan 2024 18:30:10 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for TypeScript 5.3" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.3`" + } + ] + } + }, + { + "version": "0.63.6", + "tag": "@rushstack/heft_v0.63.6", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.1`" + } + ] + } + }, + { + "version": "0.63.5", + "tag": "@rushstack/heft_v0.63.5", + "date": "Wed, 20 Dec 2023 01:09:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.0`" + } + ] + } + }, + { + "version": "0.63.4", + "tag": "@rushstack/heft_v0.63.4", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.5`" + } + ] + } + }, + { + "version": "0.63.3", + "tag": "@rushstack/heft_v0.63.3", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.4`" + } + ] + } + }, + { + "version": "0.63.2", + "tag": "@rushstack/heft_v0.63.2", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.3`" + } + ] + } + }, + { + "version": "0.63.1", + "tag": "@rushstack/heft_v0.63.1", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.2`" + } + ] + } + }, + { + "version": "0.63.0", + "tag": "@rushstack/heft_v0.63.0", + "date": "Mon, 30 Oct 2023 23:36:37 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue with parsing of the \"--debug\" and \"--unmanaged\" flags for Heft" + } + ], + "minor": [ + { + "comment": "[BREAKING CHANGE] Remove \"heft run\" short-parameters for \"--to\" (\"-t\"), \"--to-except\" (\"-T\"), and \"--only\" (\"-o\")." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.1`" + } + ] + } + }, + { + "version": "0.62.3", + "tag": "@rushstack/heft_v0.62.3", + "date": "Sun, 01 Oct 2023 02:56:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.0`" + } + ] + } + }, + { + "version": "0.62.2", + "tag": "@rushstack/heft_v0.62.2", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.3`" + } + ] + } + }, + { + "version": "0.62.1", + "tag": "@rushstack/heft_v0.62.1", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.2.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.2`" + } + ] + } + }, + { + "version": "0.62.0", + "tag": "@rushstack/heft_v0.62.0", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "minor": [ + { + "comment": "(BREAKING API CHANGE) Remove the deprecated `cancellationToken` property of `IHeftTaskRunHookOptions`. Use `abortSignal` on that object instead." + } + ] + } + }, + { + "version": "0.61.3", + "tag": "@rushstack/heft_v0.61.3", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where `heft clean` would crash with `ERR_ILLEGAL_CONSTRUCTOR`." + } + ] + } + }, + { + "version": "0.61.2", + "tag": "@rushstack/heft_v0.61.2", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.16.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.1`" + } + ] + } + }, + { + "version": "0.61.1", + "tag": "@rushstack/heft_v0.61.1", + "date": "Mon, 25 Sep 2023 23:38:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.1.1`" + } + ] + } + }, + { + "version": "0.61.0", + "tag": "@rushstack/heft_v0.61.0", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "minor": [ + { + "comment": "(BREAKING CHANGE): Rename task temp folder from \".\" to \"/\" to simplify caching phase outputs." + } + ] + } + }, + { + "version": "0.60.0", + "tag": "@rushstack/heft_v0.60.0", + "date": "Tue, 19 Sep 2023 15:21:51 GMT", + "comments": { + "minor": [ + { + "comment": "Allow Heft to communicate via IPC with a host process when running in watch mode. The host controls scheduling of incremental re-runs." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/operation-graph\" to `0.1.0`" + } + ] + } + }, + { + "version": "0.59.0", + "tag": "@rushstack/heft_v0.59.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "patch": [ + { + "comment": "Migrate plugin name collision detection to the InternalHeftSession instance to allow multiple Heft sessions in the same process." + } + ], + "none": [ + { + "comment": "Avoid mutating config files after reading." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.16.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + } + ] + } + }, + { + "version": "0.58.2", + "tag": "@rushstack/heft_v0.58.2", + "date": "Tue, 08 Aug 2023 07:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.15.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + } + ] + } + }, + { + "version": "0.58.1", + "tag": "@rushstack/heft_v0.58.1", + "date": "Sat, 29 Jul 2023 00:22:50 GMT", + "comments": { + "patch": [ + { + "comment": "Fix the `toolFinish` lifecycle hook so that it is invoked after the `recordMetrics` hook, rather than before. Ensure that the `toolFinish` lifecycle hook is invoked if the user performs a graceful shutdown of Heft (e.g. via Ctrl+C)." + } + ] + } + }, + { + "version": "0.58.0", + "tag": "@rushstack/heft_v0.58.0", + "date": "Thu, 20 Jul 2023 20:47:28 GMT", + "comments": { + "minor": [ + { + "comment": "BREAKING CHANGE: Update the heft.json \"cleanFiles\" property and the delete-files-plugin to delete the contents of folders specified by \"sourcePath\" instead of deleting the folders themselves. To delete the folders, use the \"includeGlobs\" property to specify the folder to delete." + } + ] + } + }, + { + "version": "0.57.1", + "tag": "@rushstack/heft_v0.57.1", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.3`" + } + ] + } + }, + { + "version": "0.57.0", + "tag": "@rushstack/heft_v0.57.0", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "minor": [ + { + "comment": "Support `--clean` in watch mode. Cleaning in watch mode is now performed only during the first-pass of lifecycle or phase operations. Once the clean has been completed, `--clean` will be ignored until the command is restarted" + } + ] + } + }, + { + "version": "0.56.3", + "tag": "@rushstack/heft_v0.56.3", + "date": "Wed, 12 Jul 2023 15:20:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.2`" + } + ] + } + }, + { + "version": "0.56.2", + "tag": "@rushstack/heft_v0.56.2", + "date": "Fri, 07 Jul 2023 00:19:32 GMT", + "comments": { + "patch": [ + { + "comment": "Revise README.md and UPGRADING.md documentation" + } + ] + } + }, + { + "version": "0.56.1", + "tag": "@rushstack/heft_v0.56.1", + "date": "Thu, 06 Jul 2023 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.1`" + } + ] + } + }, + { + "version": "0.56.0", + "tag": "@rushstack/heft_v0.56.0", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "minor": [ + { + "comment": "Use the `IRigConfig` interface in the `HeftConfiguration` object insteacd of the `RigConfig` class." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.4.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.0`" + } + ] + } + }, + { + "version": "0.55.2", + "tag": "@rushstack/heft_v0.55.2", + "date": "Thu, 15 Jun 2023 00:21:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.12.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.3.21`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.15.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.35.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + } + ] + } + }, + { + "version": "0.55.1", + "tag": "@rushstack/heft_v0.55.1", + "date": "Wed, 14 Jun 2023 00:19:41 GMT", + "comments": { + "patch": [ + { + "comment": "Add MockScopedLogger to help plugin authors with unit testing." + } + ] + } + }, + { + "version": "0.55.0", + "tag": "@rushstack/heft_v0.55.0", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "minor": [ + { + "comment": "Remove the deprecated `cacheFolderPath` property from the session object." + } + ] + } + }, + { + "version": "0.54.0", + "tag": "@rushstack/heft_v0.54.0", + "date": "Tue, 13 Jun 2023 01:49:01 GMT", + "comments": { + "minor": [ + { + "comment": "Add plugin support for parameter short-names." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.15.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.35.3`" + } + ] + } + }, + { + "version": "0.53.1", + "tag": "@rushstack/heft_v0.53.1", + "date": "Fri, 09 Jun 2023 18:05:34 GMT", + "comments": { + "patch": [ + { + "comment": "Revise CHANGELOG.md to more clearly identify the breaking changes" + } + ] + } + }, + { + "version": "0.53.0", + "tag": "@rushstack/heft_v0.53.0", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "patch": [ + { + "comment": "Update UPGRADING.md with new JSON schema URLs" + } + ], + "minor": [ + { + "comment": "(BREAKING CHANGE) Remove \"taskEvents\" heft.json configuration option, and replace it with directly referencing the included plugins. Please read https://github.com/microsoft/rushstack/blob/main/apps/heft/UPGRADING.md" + } + ] + } + }, + { + "version": "0.52.2", + "tag": "@rushstack/heft_v0.52.2", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "patch": [ + { + "comment": "Provide a useful error message when encountering legacy Heft configurations" + } + ] + } + }, + { + "version": "0.52.1", + "tag": "@rushstack/heft_v0.52.1", + "date": "Thu, 08 Jun 2023 00:20:02 GMT", + "comments": { + "patch": [ + { + "comment": "Remove the concept of the cache folder, since it mostly just causes bugs." + } + ] + } + }, + { + "version": "0.52.0", + "tag": "@rushstack/heft_v0.52.0", + "date": "Wed, 07 Jun 2023 22:45:16 GMT", + "comments": { + "minor": [ + { + "comment": "Add a new API IHeftTaskSession.parsedCommandLine for accessing the invoked command name" + }, + { + "comment": "(BREAKING CHANGE) The built-in task NodeServicePlugin now supports the \"--serve\" mode with semantics similar to heft-webpack5-plugin. Please read https://github.com/microsoft/rushstack/blob/main/apps/heft/UPGRADING.md" + } + ], + "patch": [ + { + "comment": "Add action aliases support. Action aliases can be used to create custom \"heft \" commands which call existing Heft commands with optional default arguments." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.12.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.3.20`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.14.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.35.2`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + } + ] + } + }, { "version": "0.51.0", "tag": "@rushstack/heft_v0.51.0", @@ -8,7 +3191,7 @@ "comments": { "minor": [ { - "comment": "Overhaul to support splitting single-project builds into more phases than \"build\" and \"test\", to align with Rush phased commands. See UPGRADING.md for details." + "comment": "(BREAKING CHANGE) Overhaul to support splitting single-project builds into more phases than \"build\" and \"test\", to align with Rush phased commands. Please read https://github.com/microsoft/rushstack/blob/main/apps/heft/UPGRADING.md" } ] } diff --git a/apps/heft/CHANGELOG.md b/apps/heft/CHANGELOG.md index eb77511aced..8b46cf7bd88 100644 --- a/apps/heft/CHANGELOG.md +++ b/apps/heft/CHANGELOG.md @@ -1,13 +1,967 @@ # Change Log - @rushstack/heft -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 1.2.22 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 1.2.21 +Fri, 17 Jul 2026 00:15:59 GMT + +### Patches + +- Replace `@override` with the `override` keyword. + +## 1.2.20 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.2.19 +Sat, 13 Jun 2026 00:16:18 GMT + +_Version update only_ + +## 1.2.18 +Mon, 08 Jun 2026 15:15:49 GMT + +_Version update only_ + +## 1.2.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.2.16 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.2.15 +Sat, 18 Apr 2026 03:47:09 GMT + +_Version update only_ + +## 1.2.14 +Sat, 18 Apr 2026 00:15:16 GMT + +_Version update only_ + +## 1.2.13 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.2.12 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.2.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.2.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.2.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.2.8 +Tue, 31 Mar 2026 15:14:14 GMT + +_Version update only_ + +## 1.2.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.2.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.2.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 1.2.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.2.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.2.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.2.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.2.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.1.14 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.1.13 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.1.12 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.1.11 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.1.10 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.1.9 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 1.1.8 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 1.1.7 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.1.6 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.1.5 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.1.4 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 1.1.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.1.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.1.1 +Wed, 08 Oct 2025 00:13:28 GMT + +_Version update only_ + +## 1.1.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.75.0 +Tue, 30 Sep 2025 20:33:51 GMT + +### Minor changes + +- Enhance logging in watch mode by allowing plugins to report detailed reasons for requesting rerun, e.g. specific changed files. +- (BREAKING CHANGE) Make the `taskStart`/`taskFinish`/`phaseStart`/`phaseFinish` hooks synchronous to signify that they are not intended to be used for expensive work. + +## 0.74.5 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.74.4 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.74.3 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.74.2 +Fri, 01 Aug 2025 00:12:48 GMT + +_Version update only_ + +## 0.74.1 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.74.0 +Sat, 21 Jun 2025 00:13:15 GMT + +### Minor changes + +- Added support for task and phase lifecycle events, `taskStart`, `taskFinish`, `phaseStart`, `phaseFinish`. + +## 0.73.6 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.73.5 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.73.4 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.73.3 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.73.2 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.73.1 +Thu, 17 Apr 2025 00:11:21 GMT + +### Patches + +- Update documentation for `extends` + +## 0.73.0 +Tue, 15 Apr 2025 15:11:57 GMT + +### Minor changes + +- Add `globAsync` to task run options. + +## 0.72.0 +Wed, 09 Apr 2025 00:11:02 GMT + +### Minor changes + +- Add a method `tryLoadProjectConfigurationFileAsync(options, terminal)` to `HeftConfiguration`. + +## 0.71.2 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.71.1 +Tue, 25 Mar 2025 15:11:15 GMT + +_Version update only_ + +## 0.71.0 +Wed, 12 Mar 2025 22:41:36 GMT + +### Minor changes + +- Add a `numberOfCores` property to `HeftConfiguration`. + +## 0.70.1 +Wed, 12 Mar 2025 00:11:31 GMT + +### Patches + +- Revert `useNodeJSResolver: true` to deal with plugins that have an `exports` field that doesn't contain `./package.json`. + +## 0.70.0 +Tue, 11 Mar 2025 02:12:33 GMT + +### Minor changes + +- Use `useNodeJSResolver: true` in `Import.resolvePackage` calls. + +## 0.69.3 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.69.2 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.69.1 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.69.0 +Wed, 26 Feb 2025 16:11:11 GMT + +### Minor changes + +- Expose `watchFs` on the incremental run options for tasks to give more flexibility when having Heft perform file watching than only invoking globs directly. + +## 0.68.18 +Sat, 22 Feb 2025 01:11:11 GMT + +_Version update only_ + +## 0.68.17 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.68.16 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.68.15 +Thu, 30 Jan 2025 16:10:36 GMT + +### Patches + +- Prefer `os.availableParallelism()` to `os.cpus().length`. + +## 0.68.14 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.68.13 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.68.12 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.68.11 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.68.10 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.68.9 +Tue, 03 Dec 2024 16:11:07 GMT + +_Version update only_ + +## 0.68.8 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.68.7 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.68.6 +Thu, 24 Oct 2024 00:15:47 GMT + +_Version update only_ + +## 0.68.5 +Mon, 21 Oct 2024 18:50:09 GMT + +### Patches + +- Remove usage of true-case-path in favor of manually adjusting the drive letter casing to avoid confusing file system tracing tools with unnecessary directory enumerations. + +## 0.68.4 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.68.3 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.68.2 +Wed, 02 Oct 2024 00:11:19 GMT + +### Patches + +- Ensure `configHash` for file copy incremental cache file is portable. + +## 0.68.1 +Tue, 01 Oct 2024 00:11:28 GMT + +### Patches + +- Include all previous `inputFileVersions` in incremental copy files cache file during watch mode. Fix incorrect serialization of cache file for file copy. + +## 0.68.0 +Mon, 30 Sep 2024 15:12:19 GMT + +### Minor changes + +- Update file copy logic to use an incremental cache file in the temp directory for the current task to avoid unnecessary file writes. + +## 0.67.2 +Fri, 13 Sep 2024 00:11:42 GMT + +_Version update only_ + +## 0.67.1 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.67.0 +Wed, 21 Aug 2024 05:43:04 GMT + +### Minor changes + +- Add a `slashNormalizedBuildFolderPath` property to `HeftConfiguration`. + +## 0.66.26 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.66.25 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.66.24 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.66.23 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 0.66.22 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 0.66.21 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.66.20 +Tue, 16 Jul 2024 00:36:21 GMT + +### Patches + +- Update schemas/templates/heft.json to reflect new settings + +## 0.66.19 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.66.18 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.66.17 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.66.16 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 0.66.15 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.66.14 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.66.13 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.66.12 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.66.11 +Fri, 24 May 2024 00:15:08 GMT + +_Version update only_ + +## 0.66.10 +Thu, 23 May 2024 02:26:56 GMT + +### Patches + +- Update schema definitions to conform to strict schema-type validation. + +## 0.66.9 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.66.8 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.66.7 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.66.6 +Fri, 10 May 2024 05:33:33 GMT + +_Version update only_ + +## 0.66.5 +Wed, 08 May 2024 22:23:50 GMT + +_Version update only_ + +## 0.66.4 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 0.66.3 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.66.2 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 0.66.1 +Fri, 15 Mar 2024 00:12:40 GMT + +### Patches + +- Fix internal error when run 'heft clean' + +## 0.66.0 +Tue, 05 Mar 2024 01:19:24 GMT + +### Minor changes + +- Add new metrics value `bootDurationMs` to track the boot overhead of Heft before the action starts executing the subtasks. Update the start time used to compute `taskTotalExecutionMs` to be the beginning of operation graph execution. Fix the value of `taskTotalExecutionMs` field to be in milliseconds instead of seconds. Add new metrics value `totalUptimeMs` to track how long watch mode sessions are kept alive. + +## 0.65.10 +Sun, 03 Mar 2024 20:58:12 GMT + +_Version update only_ + +## 0.65.9 +Sat, 02 Mar 2024 02:22:23 GMT + +_Version update only_ + +## 0.65.8 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 0.65.7 +Thu, 29 Feb 2024 07:11:45 GMT + +_Version update only_ + +## 0.65.6 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.65.5 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.65.4 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.65.3 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.65.2 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.65.1 +Tue, 20 Feb 2024 21:45:10 GMT + +### Patches + +- Fix a recent regression causing `Error: Cannot find module 'colors/safe'` (GitHub #4525) +- Remove a no longer needed dependency on the `chokidar` package + +## 0.65.0 +Tue, 20 Feb 2024 16:10:52 GMT + +### Minor changes + +- Add a built-in `set-environment-variables-plugin` task plugin to set environment variables. + +## 0.64.8 +Mon, 19 Feb 2024 21:54:26 GMT + +_Version update only_ + +## 0.64.7 +Sat, 17 Feb 2024 06:24:34 GMT + +### Patches + +- Fix broken link to API documentation + +## 0.64.6 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 0.64.5 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.64.4 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.64.3 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.64.2 +Tue, 23 Jan 2024 20:12:57 GMT + +_Version update only_ + +## 0.64.1 +Tue, 23 Jan 2024 16:15:05 GMT + +_Version update only_ + +## 0.64.0 +Tue, 16 Jan 2024 18:30:10 GMT + +### Minor changes + +- Add support for TypeScript 5.3 + +## 0.63.6 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.63.5 +Wed, 20 Dec 2023 01:09:45 GMT + +_Version update only_ + +## 0.63.4 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.63.3 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.63.2 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 0.63.1 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 0.63.0 +Mon, 30 Oct 2023 23:36:37 GMT + +### Minor changes + +- [BREAKING CHANGE] Remove "heft run" short-parameters for "--to" ("-t"), "--to-except" ("-T"), and "--only" ("-o"). + +### Patches + +- Fix an issue with parsing of the "--debug" and "--unmanaged" flags for Heft + +## 0.62.3 +Sun, 01 Oct 2023 02:56:29 GMT + +_Version update only_ + +## 0.62.2 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 0.62.1 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.62.0 +Wed, 27 Sep 2023 00:21:38 GMT + +### Minor changes + +- (BREAKING API CHANGE) Remove the deprecated `cancellationToken` property of `IHeftTaskRunHookOptions`. Use `abortSignal` on that object instead. + +## 0.61.3 +Tue, 26 Sep 2023 21:02:30 GMT + +### Patches + +- Fix an issue where `heft clean` would crash with `ERR_ILLEGAL_CONSTRUCTOR`. + +## 0.61.2 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.61.1 +Mon, 25 Sep 2023 23:38:27 GMT + +_Version update only_ + +## 0.61.0 +Fri, 22 Sep 2023 00:05:50 GMT + +### Minor changes + +- (BREAKING CHANGE): Rename task temp folder from "." to "/" to simplify caching phase outputs. + +## 0.60.0 +Tue, 19 Sep 2023 15:21:51 GMT + +### Minor changes + +- Allow Heft to communicate via IPC with a host process when running in watch mode. The host controls scheduling of incremental re-runs. + +## 0.59.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +### Patches + +- Migrate plugin name collision detection to the InternalHeftSession instance to allow multiple Heft sessions in the same process. + +## 0.58.2 +Tue, 08 Aug 2023 07:10:39 GMT + +_Version update only_ + +## 0.58.1 +Sat, 29 Jul 2023 00:22:50 GMT + +### Patches + +- Fix the `toolFinish` lifecycle hook so that it is invoked after the `recordMetrics` hook, rather than before. Ensure that the `toolFinish` lifecycle hook is invoked if the user performs a graceful shutdown of Heft (e.g. via Ctrl+C). + +## 0.58.0 +Thu, 20 Jul 2023 20:47:28 GMT + +### Minor changes + +- BREAKING CHANGE: Update the heft.json "cleanFiles" property and the delete-files-plugin to delete the contents of folders specified by "sourcePath" instead of deleting the folders themselves. To delete the folders, use the "includeGlobs" property to specify the folder to delete. + +## 0.57.1 +Wed, 19 Jul 2023 00:20:31 GMT + +_Version update only_ + +## 0.57.0 +Thu, 13 Jul 2023 00:22:37 GMT + +### Minor changes + +- Support `--clean` in watch mode. Cleaning in watch mode is now performed only during the first-pass of lifecycle or phase operations. Once the clean has been completed, `--clean` will be ignored until the command is restarted + +## 0.56.3 +Wed, 12 Jul 2023 15:20:39 GMT + +_Version update only_ + +## 0.56.2 +Fri, 07 Jul 2023 00:19:32 GMT + +### Patches + +- Revise README.md and UPGRADING.md documentation + +## 0.56.1 +Thu, 06 Jul 2023 00:16:19 GMT + +_Version update only_ + +## 0.56.0 +Mon, 19 Jun 2023 22:40:21 GMT + +### Minor changes + +- Use the `IRigConfig` interface in the `HeftConfiguration` object insteacd of the `RigConfig` class. + +## 0.55.2 +Thu, 15 Jun 2023 00:21:01 GMT + +_Version update only_ + +## 0.55.1 +Wed, 14 Jun 2023 00:19:41 GMT + +### Patches + +- Add MockScopedLogger to help plugin authors with unit testing. + +## 0.55.0 +Tue, 13 Jun 2023 15:17:20 GMT + +### Minor changes + +- Remove the deprecated `cacheFolderPath` property from the session object. + +## 0.54.0 +Tue, 13 Jun 2023 01:49:01 GMT + +### Minor changes + +- Add plugin support for parameter short-names. + +## 0.53.1 +Fri, 09 Jun 2023 18:05:34 GMT + +### Patches + +- Revise CHANGELOG.md to more clearly identify the breaking changes + +## 0.53.0 +Fri, 09 Jun 2023 00:19:49 GMT + +### Minor changes + +- (BREAKING CHANGE) Remove "taskEvents" heft.json configuration option, and replace it with directly referencing the included plugins. Please read https://github.com/microsoft/rushstack/blob/main/apps/heft/UPGRADING.md + +### Patches + +- Update UPGRADING.md with new JSON schema URLs + +## 0.52.2 +Thu, 08 Jun 2023 15:21:17 GMT + +### Patches + +- Provide a useful error message when encountering legacy Heft configurations + +## 0.52.1 +Thu, 08 Jun 2023 00:20:02 GMT + +### Patches + +- Remove the concept of the cache folder, since it mostly just causes bugs. + +## 0.52.0 +Wed, 07 Jun 2023 22:45:16 GMT + +### Minor changes + +- Add a new API IHeftTaskSession.parsedCommandLine for accessing the invoked command name +- (BREAKING CHANGE) The built-in task NodeServicePlugin now supports the "--serve" mode with semantics similar to heft-webpack5-plugin. Please read https://github.com/microsoft/rushstack/blob/main/apps/heft/UPGRADING.md + +### Patches + +- Add action aliases support. Action aliases can be used to create custom "heft " commands which call existing Heft commands with optional default arguments. ## 0.51.0 Fri, 02 Jun 2023 02:01:12 GMT ### Minor changes -- Overhaul to support splitting single-project builds into more phases than "build" and "test", to align with Rush phased commands. See UPGRADING.md for details. +- (BREAKING CHANGE) Overhaul to support splitting single-project builds into more phases than "build" and "test", to align with Rush phased commands. Please read https://github.com/microsoft/rushstack/blob/main/apps/heft/UPGRADING.md ## 0.50.7 Mon, 29 May 2023 15:21:15 GMT diff --git a/apps/heft/README.md b/apps/heft/README.md index a652807e8c3..fd224141575 100644 --- a/apps/heft/README.md +++ b/apps/heft/README.md @@ -8,21 +8,21 @@

- - - + + + Heft is a config-driven toolchain that invokes other popular tools such as TypeScript, ESLint, Jest, Webpack, and API Extractor. You can use it to build web applications, Node.js services, command-line tools, libraries, and more. Heft builds all your JavaScript projects the same way: A way that works. -Heft is typically launched by the `"build"` action from a **package.json** file. It's designed for use in -a monorepo with potentially hundreds of projects, where the [Rush](https://rushjs.io/) orchestrator invokes -a `"build"` action separately in each project folder. In this situation, everything must execute as fast as possible. +Heft is typically launched by **package.json** commands such as `"npm run build"` or `"npm run test"`. It's designed +for use in a monorepo with potentially hundreds of projects, where the [Rush](https://rushjs.io/) orchestrator invokes +these commands separately in each project folder. In this situation, everything must execute as fast as possible. Special purpose scripts become a headache to maintain, so it's better to replace them with a reusable engine that's driven by config files. In a large repo, you'll want to minimize duplication of these config files across projects. Ultimately, you'll want to define a small set of stereotypical project types -(["rigs"](https://rushstack.io/pages/heft/rig_packages/)) that you will maintain, then discourage projects from +(["rigs"](https://rushstack.io/pages/heft/rig_packages/)) to officially support, then discourage projects from overriding the rig configuration. Being consistent ensures that any person can easily contribute to any project. Heft is a ready-made implementation of all these concepts. @@ -30,38 +30,39 @@ You don't need a monorepo to use Heft, however. It also works well for small sta similar systems, Heft has some unique design goals: - **Scalable**: Heft interfaces with the [Rush Stack](https://rushstack.io/) family of tools, which are tailored - for large monorepos with many people and projects. Heft doesn't require Rush, though. + for large monorepos with many people and projects. Heft doesn't require Rush, though. -- **Optimized**: Heft tracks fine-grained performance metrics at each step. Although Heft is still in its - early stages, the TypeScript plugin already implements sophisticated optimizations such as: filesystem caching, - incremental compilation, symlinking of cache files to reduce copy times, hosting the compiler in a separate - worker process, and a unified compiler pass for Jest and Webpack. +- **Optimized**: Heft tracks fine-grained performance metrics at each step. The TypeScript plugin implements + sophisticated optimizations such as: filesystem caching, incremental compilation, simultaneous multi-target emit, + and a unified compiler pass for Jest/Webpack/ESLint. JSON config files and plugin manifests enable fast + querying of metadata without evaluating potentially inefficient script code. - **Complete**: Rush Stack aspires to establish a fully worked out solution for building typical TypeScript projects. Unopinionated task abstractions often work against this goal: It is expensive to optimize and support - (and document!) every possible cocktail of tech choices. The best optimizations and integrations - make lots of assumptions about how tasks will interact. Heft is opinionated. Our aim is to agree on a recommended - toolkit that works well for a broad range of scenarios, then work together on the deep investments that will - make that a great experience. + (and document!) every possible cocktail of tech choices. The best optimizations and integrations + make deep assumptions about how tasks will interact. Although the Heft engine itself is very flexible, + our philosophy is to agree on a standard approach that covers a broad range of scenarios, then invest in + making the best possible experience for that approach. - **Extensible**: Most projects require at least a few specialized tasks such as preprocessors, postprocessors, - or loaders. Heft is composed of plugins using the [tapable](https://www.npmjs.com/package/tapable) - hook system (familiar from Webpack). It's easy to write your own plugins. Compared to loose architectures - such as Grunt or Gulp, Heft ships a predefined arrangement of "stages" that custom tasks hook into. Having - a standardized starting point makes it easier to get technical support for customized rigs. + or loaders. Heft is organized around plugins using the [tapable](https://www.npmjs.com/package/tapable) + hook system (familiar from Webpack). Strongly typed APIs make it easy to write your own plugins. Compared to + loose architectures such as Grunt or Gulp, Heft's plugin-system is organized around explicit easy-to-read + config files. Customizations generally will extend a standard rig rather than starting from scratch. - **Familiar**: Like Rush, Heft is a regular Node.js application -- developers don't need to install native - prerequisites such as Python, MSYS2, or the .NET Framework. Heft's source code is easy to understand and debug - because it's 100% TypeScript, the same programming language as your web projects. Developing for native targets + prerequisites such as Python, MSYS2, or the .NET Framework. Heft's source code is easy to understand and debug + because it's 100% TypeScript, the same programming language as your web projects. Developing for native targets is still possible, of course. -- **Professional**: The Rush Stack projects are developed by and for engineers who ship major commercial services. - Each feature is designed, discussed in the open, and thoughtfully code reviewed. Despite being a free community - collaboration, this software is developed with the mindset that we'll be depending on it for many years to come. +- **Professional**: The Rush Stack projects are developed by and for engineers who ship large scale commercial + apps. Each feature is designed, discussed in the open, and thoughtfully code reviewed. Breaking changes + require us to migrate thousands of our own projects, so upgrades are relatively painless compared to typical + Node.js tooling. - - - + + + Heft has not yet reached its 1.0 milestone, however the following tasks are already available: @@ -84,6 +85,6 @@ the Rush Stack website. - [UPGRADING.md]( https://github.com/microsoft/rushstack/blob/main/apps/heft/UPGRADING.md) - Instructions for migrating existing projects to use a newer version of Heft -- [API Reference](https://rushstack.io/pages/api/heft/) +- [API Reference](https://api.rushstack.io/pages/heft/) Heft is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/apps/heft/UPGRADING.md b/apps/heft/UPGRADING.md index 0e3048b7e89..fa6a9bc7af3 100644 --- a/apps/heft/UPGRADING.md +++ b/apps/heft/UPGRADING.md @@ -1,347 +1,59 @@ # Upgrade notes for @rushstack/heft -### Heft 0.51.0 - -Multi-phase Heft is a complete re-write of the `@rushstack/heft` project with the intention of being more closely compatible with multi-phase Rush builds. In addition, this update brings greater customizability and improved parallel process handling to Heft. - -Some key areas that were improved with the updated version of Heft include: -- Developer-defined order of execution for Heft plugins and Heft events -- Partial execution of Heft actions via scoping parameters like `--to` or `--only` -- A simplified plugin API for developers making Heft plugins -- Explicit definition of Heft plugins via "heft-plugin.json" -- Native support for defining multiple plugins within a single plugin package -- Improved handling of plugin parameters -- Native support for incremental watch-mode in Heft actions -- Reduced overhead and performance improvements -- Much more! - -#### Heft Tasks -Heft tasks are the smallest unit of work specified in "heft.json". Tasks can either implement _a single plugin_, or _a single Heft event_. Heft tasks may take dependencies on other tasks within the same phase, and all task dependencies must complete execution before dependent tasks can execute. - -Heft events are essentially built-in plugins that can be used to provide the implementation of a Heft task. Available Heft events include: -- `copyFiles` -- `deleteFiles` -- `runScript` -- `nodeService` - -#### Heft Phases -Heft phases are a collection of tasks that will run when executing a phase. Phases act as a logical collection of tasks that would reasonably (but not necessarily) map to a Rush phase. Heft phases may take dependencies on other phases, and when executing multiple phases, all selected phases must complete execution before dependent phases can execute. - -#### Heft Actions -Using similar expansion logic to Rush, execution of a selection of Heft phases can be done through the use of the `heft run` action. This action executes a set of selected phases in order of phase dependency. If the selected phases are not dependencies, they will be executed in parallel. Selection parameters include: -- `--only` - Execute the specified phase -- `--to` - Execute the specified phase and all its dependencies - -Additionally, task- and phase-specific parameters may be provided to the `heft run` action by appending `-- ` to the command. For example, `heft run --only build -- --clean` will run only the `build` phase and will run a clean before executing the phase. - -In addition, Heft will generate actions for each phase specified in the "heft.json" configuration. These actions are executed by running `heft ` and run Heft to the specified phase, including all phase dependencies. As such, these inferred Heft actions are equivalent to running `heft run --to `, and are intended as a CLI shorthand. - -#### Watch Mode -Watch mode is now a first-class feature in Heft. Watch mode actions are created for all Heft actions. For example, to run "build" and "test" phases in watch mode, either of the commands `heft test-watch` or `heft run-watch --to test`. When running in watch mode, Heft prefers the `runIncremental` hook to the `run` hook (see [Heft Task Plugins](#heft-task-plugins)). +### Heft 0.53.0 +The `taskEvent` configuration option in heft.json has been removed, and use of any `taskEvent`-based functionality is now accomplished by referencing the plugins directly within the `@rushstack/heft` package. -#### Heft Plugins -##### Heft Lifecycle Plugins -Heft lifecycle plugins provide the implementation for certain lifecycle-related hooks. These plugins will be used across all Heft phases, and as such should be rarely used outside of a few specific cases (such as for metrics reporting). Heft lifecycle plugins provide an `apply` method, and here plugins can hook into the following Tapable hooks: -- `toolStart` - Used to provide plugin-related functionality at the start of Heft execution -- `toolFinish` - Used to provide plugin-related functionality at the end of Heft execution, after all tasks are finished -- `recordMetrics` - Used to provide metrics information about the Heft run to the plugin after all tasks are finished +Plugin name mappings for previously-existing task events are: +- `copyFiles` -> `copy-files-plugin` +- `deleteFiles` -> `delete-files-plugin` +- `runScript` -> `run-script-plugin` +- `nodeService` -> `node-service-plugin` -##### Heft Task Plugins -Heft task plugins provide the implementation for Heft tasks. Heft plugins provide an `apply` method, and here plugins can hook into the following Tapable hooks: -- `registerFileOperations` - Invoked exactly once before the first time a plugin runs. Allows a plugin to register copy or delete operations using the same options as the `copyFiles` and `deleteFiles` Heft events (this hook is how those events are implemented). -- `run` - Used to provide plugin-related task functionality -- `runIncremental` - Used to provide plugin-related task functionality when in watch mode. If no `runIncremental` implementation is provided for a task, Heft will fall back to using the `run` hook as usual. The options structure includes two functions used to support watch operations: - - `requestRun()` - This function asks the Heft runtime to schedule a new run of the plugin's owning task, potentially cancelling the current build. - - `watchGlobAsync(patterns, options)` - This function is provided for convenience for the common case of monitoring a glob for changes. It returns a `Map` that enumerates the list of files (or folders) selected by the glob and whether or not they have changed since the previous invocation. It will automatically invoke the `requestRun()` callback if it detects changes to files or directory listings that might impact the output of the glob. - -##### Heft Cross-Plugin Interaction -Heft plugins can use the `requestAccessToPluginByName` API to access the requested plugin accessors. Accessors are objects provided by plugins for external use and are the ideal place to share plugin-specific information or hooks used to provide additional plugin functionality. - -Access requests are fulfilled at the beginning of phase execution, prior to `clean` hook execution. If the requested plugin does not provide an accessor, an error will be thrown noting the plugin with the missing accessor. However, if the plugin requested is not present at all, the access request will silently fail. This is done to allow for non-required integrations with external plugins. For this reason, it is important to implement cross-plugin interaction in such a way as to expect this case and to handle it gracefully, or to throw a helpful error. - -Plugins available for access are restricted based on scope. For lifecycle plugins, you may request access to any other lifecycle plugin added to the Heft configuration. For task plugins, you may request access to any other task plugin residing within the same phase in the Heft configuration. - -#### heft.json -The "heft.json" file is where phases and tasks are defined. Since contains the relationships between the phases and tasks, it defines the order of operations for the execution of a Heft action. - -##### Lifecycle Plugin Specification -Lifecycle plugins are specified in the top-level `heftPlugins` array. Plugins can be referenced by providing a package name and a plugin name. Optionally, if a package contains only a single plugin, a plugin can be referenced by providing only the package name and Heft will resolve to the only exported plugin. Lifecycle plugins can also be provided options to modify the default behavior. -```json +Example diff of a heft.json file that uses the `copyFiles` task event: +```diff { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", - "extends": "base-project/config/heft.json", - - "heftPlugins": [ - { - "packageName": "@rushstack/heft-metrics-reporter", - "options": { - "disableMetrics": true - } - }, - { - "packageName": "@rushstack/heft-initialization-plugin", - "pluginName": "my-lifecycle-plugin" - } - ] -} -``` - -##### Phase, Task, and Task Plugin Specification -All phases are defined within the top-level `phasesByName` property. Each phase may specify `phaseDependencies` to define the order of phase execution when running a selection of Heft phases. Phases may also provide the `cleanFiles` option, which accepts an array of deletion operations to perform when running with the `--clean` flag. - -Within the phase specification, `tasksByName` defines all tasks that run while executing a phase. Each task may specify `taskDependencies` to define the order of task execution. All tasks defined in `taskDependencies` must exist within the same phase. For CLI-availability reasons, phase names, task names, plugin names, and parameter scopes, must be `kebab-cased`. - -The following is an example "heft.json" file defining both a "build" and a "test" phase: -```json -{ - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", - "extends": "base-project/config/heft.json", - - // "heftPlugins" can be used alongside "phasesByName" - "heftPlugins": [ - { - "packageName": "@rushstack/heft-metrics-reporter" - } - ], - - // "phasesByName" defines all phases, and each phase defines tasks to be run "phasesByName": { "build": { - "phaseDescription": "Transpile and run a linter against build output", - "cleanFiles": [ - { - "sourcePath": "temp-build-output" - } - ], - // "tasksByName" defines all tasks within a phase - "tasksByName": { - "typescript": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft-typescript-plugin" - } - }, - "lint": { - "taskDependencies": [ "typescript" ], - "taskPlugin": { - "pluginPackage": "@rushstack/heft-lint-plugin", - "pluginName": "eslint" - } - }, - "copy-assets": { - "taskEvent": { - "eventKind": "copyFiles", + "tasksbyName": { + "perform-copy": { +- "taskEvent": { +- "eventKind": "copyFiles", ++ "taskPlugin": { ++ "pluginPackage": "@rushstack/heft", ++ "pluginName": "copy-files-plugin", "options": { - "copyOperations": [ - { - "sourceFolder": "src/assets", - "destinationFolders": [ "dist/assets" ] - } - ] + ... } } } } - }, - - "test": { - "phaseDependencies": [ "build" ], - "phaseDescription": "Run Jest tests, if provided.", - "tasksByName": { - "jest": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft-jest-plugin" - } - } - } } } } ``` -##### Property Inheritance in "heft.json" -Previously, common properties between a "heft.json" file its extended base file would merge arrays and overwrite objects. Now, both arrays and objects will merge, allowing for simplified use of the "heft.json" file when customizing extended base configurations. +### Heft 0.52.0 -Additionally, we now provide merge behavior overrides to allow modifying extended configurations more dynamically. This is done by using inline markup properties that define inheritance behavior. For example, assume that we are extending a file with a previously defined "property1" value that is a keyed object, and a "property2" value that is an array object: -```json -{ - "$schema": "...", - "$extends": "...", - - "$property1.inheritanceType": "override | merge", - "property1": { - "$subProperty1.inheritanceType": "override | merge", - "subProperty1": { ... }, - "$subProperty2.inheritanceType": "override | append", - "subProperty2": [ ... ] - }, - - "$property2.inheritanceType": "override | append", - "property2": [ ... ] -} -``` -Once an object is set to a `inheritanceType` of override, all sub-property `inheritanceType` values will be ignored, since the top-most object already overrides all sub-properties. -One thing to note is that different mergeBehavior verbs are used for the merging of keyed objects and arrays. This is to make it explicit that arrays will be appended as-is, and no additional processing (eg. deduping if the array is intended to be a set) is done during merge. If such behavior is required, it can be done on the implementation side. Deduping arrays within the @rushstack/heft-config-file package doesn't quite make sense, since deduping arrays of non-primitive objects is not easily defined. - -##### Example "heft.json" Comparison -###### "heft.json" in `@rushstack/heft@0.49.0-rc.1` -```json -{ - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", - - "phasesByName": { - "build": { - "cleanFiles": [ - { "sourcePath": "dist" }, - { "sourcePath": "lib" } - ], - "tasksByName": { - "typescript": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft-typescript-plugin" - } - }, - "lint": { - "taskDependencies": ["typescript"], - "taskPlugin": { - "pluginPackage": "@rushstack/heft-lint-plugin" - } - }, - "api-extractor": { - "taskDependencies": ["typescript"], - "taskPlugin": { - "pluginPackage": "@rushstack/heft-api-extractor-plugin" - } - } - } - }, - - "test": { - "phaseDependencies": ["build"], - "tasksByName": { - "jest": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft-jest-plugin" - } - } - } - } - } -} -``` -###### "heft.json" in `@rushstack/heft@0.48.8` -```json -{ - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", +The `nodeService` built-in plugin now supports the `--serve` parameter, to be consistent with the `@rushstack/heft-webpack5-plugin` dev server. - "eventActions": [ - { - "actionKind": "deleteGlobs", - "heftEvent": "clean", - "actionId": "defaultClean", - "globsToDelete": ["dist", "lib", "lib-commonjs", "temp"] - } - ], +Old behavior: +- `nodeService` was always enabled, but would have no effect unless Heft was in watch mode (`heft start`) +- If `config/node-service.json` was omitted, the plugin would silently be disabled - "heftPlugins": [ - { "plugin": "@rushstack/heft-jest-plugin" } - ] -} -``` -*NOTE: This "heft.json" file is simple due to the implicitly included plugins, which must now be added by developers or consumed via a rig.* +New behavior: +- `nodeService` is always loaded by `@rushstack/heft-node-rig` but for a custom `heft.json` you need to load it manually +- `nodeService` has no effect unless you specify `--serve`, for example: `heft build-watch --serve` +- If `--serve` is specified and `config/node-service.json` is omitted, then Heft fails with a hard error -#### heft-plugin.json -The new heft-plugin.json file is a new, required manifest file specified at the root of all Heft plugin packages. This file is used for multiple purposes, including the definition of all contained lifecycle or task plugins, the definition of all plugin-specific CLI parameters, and providing an optional schema file to validate plugin options that can be passed via "heft.json". +### Heft 0.51.0 -The following is an example "heft-plugin.json" file defining a lifecycle plugin and a task plugin: -```json -{ - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-plugin.schema.json", +⭐ This release included significant breaking changes. ⭐ - "lifecyclePlugins": [ - { - "pluginName": "my-lifecycle-plugin", - "entryPoint": "./lib/MyLifecyclePlugin.js", - "optionsSchema": "./lib/schemas/mylifecycleplugin.schema.json", - "parameterScope": "my-lifecycle", - "parameters": [ - { - "parameterKind": "string", - "longName": "--my-string", - "description": "…", - "argumentName": "ARG_NAME", - "required": false - } - ] - } - ], +For details, please see our two blog posts: - "taskPlugins": [ - { - "pluginName": "my-task-plugin", - "entryPoint": "./lib/MyTaskPlugin.js", - "optionsSchema": "./lib/schemas/mytaskplugin.schema.json", - "parameterScope": "my-task", - "parameters": [ - { - "parameterKind": "string", - "longName": "--my-other-string", - "description": "…", - "argumentName": "ARG_NAME", - "required": false - } - ] - } - ] -} -``` +- [What's New in Heft 0.51](https://rushstack.io/blog/2023/06/15/heft-whats-new/) -##### Defining Plugin CLI Parameters -Defining CLI parameters is now only possible via "heft-plugin.json", and defined parameters can be consumed in plugins via the `HeftTaskSession.parameters` API. Additionally, all plugin parameters for the selected Heft phases are now discoverable on the CLI when using the `--help` argument (ex. `heft test --help` or `heft run --to test -- --help`). - -These parameters can be automatically "de-duped" on the CLI using an optionally-provided `parameterScope`. By default, parameters defined in "heft-plugin.json" will be available on the CLI using `--` and `--:`. When multiple plugins provide the same parameter, only the latter parameter will be available on the CLI in order to "de-dupe" conflicting parameters. For example, if PluginA with parameter scope "PluginA" defines `--parameter`, and PluginB with parameter scope "PluginB" also defines `--parameter`, the parameters will _only_ be available as `--PluginA:parameter` and `--PluginB:parameter`. - -#### Updating "heft.json" -In updating to the new version of Heft, "heft.json" files will need to be updated to define the flow of your Heft run. This is a big change in behavior since legacy Heft defined a strict set of hooks, any of which could be tied into by any plugin. When converting to the new "heft.json" format, special care should be paid to the order-of-operations. - -An important note on upgrading to the new version of Heft is that legacy Heft included a few plugins by default which have since been externalized. Due to this change, these default plugins need to be manually included in your Heft project. These plugins include: -- `@rushstack/heft-typescript-plugin` -- `@rushstack/heft-lint-plugin` -- `@rushstack/heft-api-extractor-plugin` - -To simplify upgrading to the new version of Heft, usage of rigs is encouraged since rigs help centralize changes to Heft configurations in one location. The above plugins are included in the Rushstack-provided `@rushstack/heft-node-rig` and `@rushstack/heft-web-rig` packages. - -#### Updating Heft Plugins -In updating to the new version of Heft, plugins will also need to be updated for compatibility. Some of the more notable API changes include: -- "heft.json" format completely changed. See above for more information on "heft.json" -- "heft-plugin.json" manifest file must accompany any plugin package. If no "heft-plugin.json" file is found, Heft will throw an error. See above for more information on "heft-plugin.json" -- Plugin classes must have parameterless constructors, and must be the default export of the file pointed to by the `entryPoint` property in "heft-plugin.json" -- Schema files for options provided in "heft.json" can now be specified using the `optionsSchema` property in "heft-plugin.json" and they will be validated by Heft -- Parameters are now defined in "heft-plugin.json" and are consumed in the plugin via the `IHeftTaskSession.parameters` or `IHeftLifecycleSession.parameters` property. *NOTE: Other than the default Heft-included parameters, only parameters defined by the calling plugin are accessible* -- Plugins can no longer define their own actions. If a plugin deserves its own action, a dedicated phase should be added to the consumers "heft.json" -- The `runScript` Heft event has been modified to only accept a `runAsync` method, and the properties have been updated to reflect what is available to normal Heft task plugins -- Path-related variables have been renamed to clarify they are paths (ex. `HeftConfiguration.buildFolder` is now `HeftConfiguration.buildFolderPath`) -- The `runIncremental` hook can now be utilized to add ensure that watch mode rebuilds occur in proper dependency order -- The `clean` hook was removed in favor of the `cleanFiles` option in "heft.json" in order to make it obvious what files are being cleaned and when -- The `folderNameForTests` and `extensionForTests` properties have been removed and should instead be addressed via the `testMatch` property in `jest.config.json` - -#### Testing on Your Own Project -The new version of Heft and all related plugins are available in the following packages: -- `@rushstack/heft@0.51.0` -- `@rushstack/heft-typescript-plugin@0.1.0` -- `@rushstack/heft-lint-plugin@0.1.0` -- `@rushstack/heft-api-extractor-plugin@0.1.0` -- `@rushstack/heft-jest-plugin@0.6.0` -- `@rushstack/heft-sass-plugin@0.11.0` -- `@rushstack/heft-storybook-plugin@0.3.0` -- `@rushstack/heft-webpack4-plugin@0.6.0` -- `@rushstack/heft-webpack5-plugin@0.7.0` -- `@rushstack/heft-dev-cert-plugin@0.3.0` - -Additionally, Rushstack-provided rigs have been updated to be compatible with the new version of Heft: -- `@rushstack/heft-node-rig@1.14.0` -- `@rushstack/heft-web-rig@0.16.0` - -If you have any issues with the prerelease packages or the new changes to Heft, please [file an issue](https://github.com/microsoft/rushstack/issues/new?assignees=&labels=&template=heft.md&title=%5Bheft%2Frc%2f0%5D+). +- [Heft 0.51 Migration Guide](https://rushstack.io/blog/2023/06/16/heft-migration-guide/) ### Heft 0.35.0 diff --git a/apps/heft/bin/heft b/apps/heft/bin/heft index ef67d5db050..eb51ae89e3e 100755 --- a/apps/heft/bin/heft +++ b/apps/heft/bin/heft @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('../lib/startWithVersionSelector.js'); +require('../lib-commonjs/startWithVersionSelector.js'); diff --git a/apps/heft/config/api-extractor.json b/apps/heft/config/api-extractor.json index 34fb7776c9d..005e818a08b 100644 --- a/apps/heft/config/api-extractor.json +++ b/apps/heft/config/api-extractor.json @@ -1,15 +1,7 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/api-extractor-base.json", - "mainEntryPointFilePath": "/lib/index.d.ts", - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, - "docModel": { - "enabled": true, - "apiJsonFilePath": "../../../common/temp/api/.api.json" - }, "dtsRollup": { "enabled": true, "betaTrimmedFilePath": "/dist/.d.ts" diff --git a/apps/heft/config/heft.json b/apps/heft/config/heft.json new file mode 100644 index 00000000000..76eac0e3d31 --- /dev/null +++ b/apps/heft/config/heft.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + }, + + "copy-legacy-compatibility-start-js": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/legacy-compatibility", + "destinationFolders": ["lib"], + "includeGlobs": ["*"] + } + ] + } + } + } + } + } + } +} diff --git a/apps/heft/config/jest.config.json b/apps/heft/config/jest.config.json index 4bb17bde3ee..7c0f9ccc9d6 100644 --- a/apps/heft/config/jest.config.json +++ b/apps/heft/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "decoupled-local-node-rig/profiles/default/config/jest.config.json" } diff --git a/apps/heft/config/rig.json b/apps/heft/config/rig.json index 6ac88a96368..cc98dea43dd 100644 --- a/apps/heft/config/rig.json +++ b/apps/heft/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "decoupled-local-node-rig" } diff --git a/apps/heft/eslint.config.js b/apps/heft/eslint.config.js new file mode 100644 index 00000000000..e54effd122a --- /dev/null +++ b/apps/heft/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/apps/heft/heft-plugin.json b/apps/heft/heft-plugin.json new file mode 100644 index 00000000000..417896dc263 --- /dev/null +++ b/apps/heft/heft-plugin.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", + + "lifecyclePlugins": [], + + "taskPlugins": [ + { + "pluginName": "copy-files-plugin", + "entryPoint": "./lib-commonjs/plugins/CopyFilesPlugin", + "optionsSchema": "./lib-commonjs/schemas/copy-files-options.schema.json" + }, + { + "pluginName": "delete-files-plugin", + "entryPoint": "./lib-commonjs/plugins/DeleteFilesPlugin", + "optionsSchema": "./lib-commonjs/schemas/delete-files-options.schema.json" + }, + { + "pluginName": "node-service-plugin", + "entryPoint": "./lib-commonjs/plugins/NodeServicePlugin", + "parameterScope": "node-service", + "parameters": [ + { + "longName": "--serve", + "parameterKind": "flag", + "description": "Start a local web server for testing purposes. This parameter is only available when running in watch mode." + } + ] + }, + { + "pluginName": "run-script-plugin", + "entryPoint": "./lib-commonjs/plugins/RunScriptPlugin", + "optionsSchema": "./lib-commonjs/schemas/run-script-options.schema.json" + }, + { + "entryPoint": "./lib-commonjs/plugins/SetEnvironmentVariablesPlugin", + "pluginName": "set-environment-variables-plugin", + "optionsSchema": "./lib-commonjs/schemas/set-environment-variables-plugin.schema.json" + } + ] +} diff --git a/apps/heft/package.json b/apps/heft/package.json index 1563419e831..698f854968e 100644 --- a/apps/heft/package.json +++ b/apps/heft/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/heft", - "version": "0.51.0", + "version": "1.2.22", "description": "Build all your JavaScript projects the same way: A way that works.", "keywords": [ "toolchain", @@ -21,44 +21,68 @@ "node": ">=10.13.0" }, "homepage": "https://rushstack.io/pages/heft/overview/", - "main": "lib/index.js", - "types": "dist/heft.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/heft.d.ts", + "exports": { + ".": { + "types": "./dist/heft.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "bin": { "heft": "./bin/heft" }, "license": "MIT", "scripts": { "build": "heft build --clean", - "start": "heft test --clean --watch", - "_phase:build": "heft build --clean", - "_phase:test": "heft test --no-build" + "start": "heft build-watch --clean", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" }, "dependencies": { "@rushstack/heft-config-file": "workspace:*", "@rushstack/node-core-library": "workspace:*", + "@rushstack/operation-graph": "workspace:*", "@rushstack/rig-package": "workspace:*", + "@rushstack/terminal": "workspace:*", "@rushstack/ts-command-line": "workspace:*", "@types/tapable": "1.0.6", - "argparse": "~1.0.9", - "chokidar": "~3.4.0", - "fast-glob": "~3.2.4", + "fast-glob": "~3.3.1", "git-repo-info": "~2.1.0", "ignore": "~5.1.6", "tapable": "1.1.3", - "true-case-path": "~2.2.1", "watchpack": "2.4.0" }, "devDependencies": { "@microsoft/api-extractor": "workspace:*", - "@nodelib/fs.scandir": "2.1.5", - "@nodelib/fs.stat": "2.0.5", - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft": "0.50.6", - "@rushstack/heft-node-rig": "1.13.0", - "@types/argparse": "1.0.38", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", + "@rushstack/heft": "1.2.22", "@types/watchpack": "2.4.0", - "typescript": "~5.0.4" - } + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0" + }, + "sideEffects": [ + "lib-commonjs/start.js", + "lib-commonjs/startWithVersionSelector.js", + "lib-esm/start.js", + "lib-esm/startWithVersionSelector.js" + ] } diff --git a/apps/heft/src/cli/HeftActionRunner.ts b/apps/heft/src/cli/HeftActionRunner.ts index 167ee869c1e..b9d5c7f7f26 100644 --- a/apps/heft/src/cli/HeftActionRunner.ts +++ b/apps/heft/src/cli/HeftActionRunner.ts @@ -1,18 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { performance } from 'perf_hooks'; -import { createInterface, type Interface as ReadlineInterface } from 'readline'; -import os from 'os'; +import { performance } from 'node:perf_hooks'; +import { createInterface, type Interface as ReadlineInterface } from 'node:readline'; +import os from 'node:os'; +import { AlreadyReportedError, InternalError, type IPackageJson } from '@rushstack/node-core-library'; +import { Colorize, ConsoleTerminalProvider, type ITerminal } from '@rushstack/terminal'; import { - AlreadyReportedError, - Colors, - ConsoleTerminalProvider, - InternalError, - type ITerminal, - type IPackageJson -} from '@rushstack/node-core-library'; + type IOperationExecutionOptions, + type IWatchLoopState, + Operation, + OperationExecutionManager, + OperationGroupRecord, + type OperationRequestRunCallback, + OperationStatus, + WatchLoop +} from '@rushstack/operation-graph'; import type { CommandLineFlagParameter, CommandLineParameterProvider, @@ -24,26 +28,42 @@ import type { HeftConfiguration } from '../configuration/HeftConfiguration'; import type { LoggingManager } from '../pluginFramework/logging/LoggingManager'; import type { MetricsCollector } from '../metrics/MetricsCollector'; import { HeftParameterManager } from '../pluginFramework/HeftParameterManager'; -import { - OperationExecutionManager, - type IOperationExecutionOptions -} from '../operations/OperationExecutionManager'; -import { Operation } from '../operations/Operation'; import { TaskOperationRunner } from '../operations/runners/TaskOperationRunner'; import { PhaseOperationRunner } from '../operations/runners/PhaseOperationRunner'; -import { LifecycleOperationRunner } from '../operations/runners/LifecycleOperationRunner'; -import type { HeftPhase } from '../pluginFramework/HeftPhase'; -import type { IHeftAction, IHeftActionOptions } from '../cli/actions/IHeftAction'; -import type { HeftTask } from '../pluginFramework/HeftTask'; -import type { LifecycleOperationRunnerType } from '../operations/runners/LifecycleOperationRunner'; -import { CancellationToken, CancellationTokenSource } from '../pluginFramework/CancellationToken'; +import type { IHeftPhase, HeftPhase } from '../pluginFramework/HeftPhase'; +import type { IHeftAction, IHeftActionOptions } from './actions/IHeftAction'; +import type { + IHeftLifecycleCleanHookOptions, + IHeftLifecycleSession, + IHeftLifecycleToolFinishHookOptions, + IHeftLifecycleToolStartHookOptions +} from '../pluginFramework/HeftLifecycleSession'; +import type { HeftLifecycle } from '../pluginFramework/HeftLifecycle'; +import type { IHeftTask, HeftTask } from '../pluginFramework/HeftTask'; +import { deleteFilesAsync, type IDeleteOperation } from '../plugins/DeleteFilesPlugin'; import { Constants } from '../utilities/Constants'; -import { OperationStatus } from '../operations/OperationStatus'; export interface IHeftActionRunnerOptions extends IHeftActionOptions { action: IHeftAction; } +/** + * Metadata for an operation that represents a task. + * @public + */ +export interface IHeftTaskOperationMetadata { + task: IHeftTask; + phase: IHeftPhase; +} + +/** + * Metadata for an operation that represents a phase. + * @public + */ +export interface IHeftPhaseOperationMetadata { + phase: IHeftPhase; +} + export function initializeHeft( heftConfiguration: HeftConfiguration, terminal: ITerminal, @@ -69,21 +89,53 @@ export function initializeHeft( terminal.writeVerboseLine(''); } +let _cliAbortSignal: AbortSignal | undefined; +export function ensureCliAbortSignal(terminal: ITerminal): AbortSignal { + if (!_cliAbortSignal) { + // Set up the ability to terminate the build via Ctrl+C and have it exit gracefully if pressed once, + // less gracefully if pressed a second time. + const cliAbortController: AbortController = new AbortController(); + _cliAbortSignal = cliAbortController.signal; + const cli: ReadlineInterface = createInterface(process.stdin, undefined, undefined, true); + let forceTerminate: boolean = false; + cli.on('SIGINT', () => { + cli.close(); + + if (forceTerminate) { + terminal.writeErrorLine(`Forcibly terminating.`); + process.exit(1); + } else { + terminal.writeLine( + Colorize.yellow(Colorize.bold(`Canceling... Press Ctrl+C again to forcibly terminate.`)) + ); + } + + forceTerminate = true; + cliAbortController.abort(); + }); + } + + return _cliAbortSignal; +} + export async function runWithLoggingAsync( fn: () => Promise, action: IHeftAction, loggingManager: LoggingManager, terminal: ITerminal, metricsCollector: MetricsCollector, - cancellationToken: CancellationToken -): Promise { + abortSignal: AbortSignal, + throwOnFailure?: boolean +): Promise { const startTime: number = performance.now(); loggingManager.resetScopedLoggerErrorsAndWarnings(); + let result: OperationStatus = OperationStatus.Failure; + // Execute the action operations let encounteredError: boolean = false; try { - const result: OperationStatus = await fn(); + result = await fn(); if (result === OperationStatus.Failure) { encounteredError = true; } @@ -94,8 +146,8 @@ export async function runWithLoggingAsync( const warningStrings: string[] = loggingManager.getWarningStrings(); const errorStrings: string[] = loggingManager.getErrorStrings(); - const wasCancelled: boolean = cancellationToken.isCancelled; - const encounteredWarnings: boolean = warningStrings.length > 0 || wasCancelled; + const wasAborted: boolean = abortSignal.aborted; + const encounteredWarnings: boolean = warningStrings.length > 0 || wasAborted; encounteredError = encounteredError || errorStrings.length > 0; await metricsCollector.recordAsync( @@ -106,13 +158,13 @@ export async function runWithLoggingAsync( action.getParameterStringMap() ); - const finishedLoggingWord: string = encounteredError ? 'Failed' : wasCancelled ? 'Cancelled' : 'Finished'; + const finishedLoggingWord: string = encounteredError ? 'Failed' : wasAborted ? 'Aborted' : 'Finished'; const duration: number = performance.now() - startTime; const durationSeconds: number = Math.round(duration) / 1000; const finishedLoggingLine: string = `-------------------- ${finishedLoggingWord} (${durationSeconds}s) --------------------`; terminal.writeLine( - Colors.bold( - (encounteredError ? Colors.red : encounteredWarnings ? Colors.yellow : Colors.green)( + Colorize.bold( + (encounteredError ? Colorize.red : encounteredWarnings ? Colorize.yellow : Colorize.green)( finishedLoggingLine ) ) @@ -137,9 +189,11 @@ export async function runWithLoggingAsync( } } - if (encounteredError) { + if (encounteredError && throwOnFailure) { throw new AlreadyReportedError(); } + + return result; } export class HeftActionRunner { @@ -153,14 +207,16 @@ export class HeftActionRunner { private readonly _parallelism: number; public constructor(options: IHeftActionRunnerOptions) { - this._action = options.action; - this._internalHeftSession = options.internalHeftSession; - this._heftConfiguration = options.heftConfiguration; - this._loggingManager = options.loggingManager; - this._terminal = options.terminal; - this._metricsCollector = options.metricsCollector; + const { action, internalHeftSession, heftConfiguration, loggingManager, terminal, metricsCollector } = + options; + this._action = action; + this._internalHeftSession = internalHeftSession; + this._heftConfiguration = heftConfiguration; + this._loggingManager = loggingManager; + this._terminal = terminal; + this._metricsCollector = metricsCollector; - const numberOfCores: number = os.cpus().length; + const numberOfCores: number = heftConfiguration.numberOfCores; // If an explicit parallelism number wasn't provided, then choose a sensible // default. @@ -174,8 +230,6 @@ export class HeftActionRunner { // to the number of CPU cores this._parallelism = numberOfCores; } - - this._metricsCollector.setStartTime(); } protected get parameterManager(): HeftParameterManager { @@ -209,21 +263,17 @@ export class HeftActionRunner { description: 'Use the specified locale for this run, if applicable.' }); - let cleanFlag: CommandLineFlagParameter | undefined; - let cleanCacheFlag: CommandLineFlagParameter | undefined; - if (!this._action.watch) { - // Only enable the clean flags in non-watch mode - cleanFlag = parameterProvider.defineFlagParameter({ - parameterLongName: Constants.cleanParameterLongName, - description: 'If specified, clean the outputs before running each phase.' - }); - cleanCacheFlag = parameterProvider.defineFlagParameter({ - parameterLongName: Constants.cleanCacheParameterLongName, - description: - 'If specified, clean the cache before running each phase. To use this flag, the ' + - `${JSON.stringify(Constants.cleanParameterLongName)} flag must also be provided.` - }); + let cleanFlagDescription: string = + 'If specified, clean the outputs at the beginning of the lifecycle and before running each phase.'; + if (this._action.watch) { + cleanFlagDescription = + `${cleanFlagDescription} Cleaning will only be performed once for the lifecycle and each phase, ` + + `and further incremental runs will not be cleaned for the duration of execution.`; } + const cleanFlag: CommandLineFlagParameter = parameterProvider.defineFlagParameter({ + parameterLongName: Constants.cleanParameterLongName, + description: cleanFlagDescription + }); const parameterManager: HeftParameterManager = new HeftParameterManager({ getIsDebug: () => this._internalHeftSession.debug, @@ -231,8 +281,7 @@ export class HeftActionRunner { getIsProduction: () => productionFlag.value, getIsWatch: () => this._action.watch, getLocales: () => localesParameter.values, - getIsClean: () => !!cleanFlag?.value, - getIsCleanCache: () => !!cleanCacheFlag?.value + getIsClean: () => !!cleanFlag?.value }); // Add all the lifecycle parameters for the action @@ -261,130 +310,111 @@ export class HeftActionRunner { initializeHeft(this._heftConfiguration, terminal, this.parameterManager.defaultParameters.verbose); - const operations: ReadonlySet = this._generateOperations(); + const operations: ReadonlySet> = + this._generateOperations(); - // Set up the ability to terminate the build via Ctrl+C and have it exit gracefully if pressed once, - // less gracefully if pressed a second time. - const cliCancellationTokenSource: CancellationTokenSource = new CancellationTokenSource(); - const cliCancellationToken: CancellationToken = cliCancellationTokenSource.token; - const cli: ReadlineInterface = createInterface(process.stdin, undefined, undefined, true); - let forceTerminate: boolean = false; - cli.on('SIGINT', () => { - cli.close(); + const executionManager: OperationExecutionManager< + IHeftTaskOperationMetadata, + IHeftPhaseOperationMetadata + > = new OperationExecutionManager(operations); - if (forceTerminate) { - terminal.writeErrorLine(`Forcibly terminating.`); - process.exit(1); - } else { - terminal.writeLine( - Colors.yellow(Colors.bold(`Canceling build... Press Ctrl+C again to forcibly terminate.`)) - ); - } + const cliAbortSignal: AbortSignal = ensureCliAbortSignal(this._terminal); - forceTerminate = true; - cliCancellationTokenSource.cancel(); - }); + try { + await _startLifecycleAsync(this._internalHeftSession); - const executionManager: OperationExecutionManager = new OperationExecutionManager(operations); + if (this._action.watch) { + const watchLoop: WatchLoop = this._createWatchLoop(executionManager); - if (this._action.watch) { - await this._executeWatchAsync(executionManager, cliCancellationToken); - } else { - await this._executeOnceAsync(executionManager, cliCancellationToken); + if (process.send) { + await watchLoop.runIPCAsync(); + } else { + await watchLoop.runUntilAbortedAsync(cliAbortSignal, () => { + terminal.writeLine(Colorize.bold('Waiting for changes. Press CTRL + C to exit...')); + terminal.writeLine(''); + }); + } + } else { + await this._executeOnceAsync(executionManager, cliAbortSignal); + } + } finally { + // Invoke this here both to ensure it always runs and that it does so after recordMetrics + // This is treated as a finalizer for any assets created in lifecycle plugins. + // It is the responsibility of the lifecycle plugin to ensure that finish gracefully handles + // aborted runs. + await _finishLifecycleAsync(this._internalHeftSession); } } - private async _executeWatchAsync( - executionManager: OperationExecutionManager, - cliCancellationToken: CancellationToken - ): Promise { - let runRequested: boolean = true; - let isRunning: boolean = true; - let cancellationTokenSource: CancellationTokenSource = new CancellationTokenSource(); - + private _createWatchLoop(executionManager: OperationExecutionManager): WatchLoop { const { _terminal: terminal } = this; - - let resolveRequestRun!: (requestor?: string) => void; - function createRequestRunPromise(): Promise { - return new Promise( - (resolve: (requestor?: string) => void, reject: (err: Error) => void) => { - resolveRequestRun = resolve; - } - ).then((requestor: string | undefined) => { - terminal.writeLine(Colors.bold(`New run requested by ${requestor || 'unknown task'}`)); - runRequested = true; - if (isRunning) { - terminal.writeLine(Colors.bold(`Cancelling incremental build...`)); - // If there's a source file change, we need to cancel the incremental build and wait for the - // execution to finish before we begin execution again. - cancellationTokenSource.cancel(); - } - }); - } - let requestRunPromise: Promise = createRequestRunPromise(); - - function cancelExecution(): void { - cancellationTokenSource.cancel(); - } - - function requestRun(requestor?: string): void { - // The wrapper here allows operation runners to hang onto a single instance, despite the underlying - // promise changing. - resolveRequestRun(requestor); - } - - // eslint-disable-next-line no-constant-condition - while (!cliCancellationToken.isCancelled) { - if (cancellationTokenSource.isCancelled) { - cancellationTokenSource = new CancellationTokenSource(); - cliCancellationToken.onCancelledPromise.finally(cancelExecution); - } - - // Create the cancellation token which is passed to the incremental build. - const cancellationToken: CancellationToken = cancellationTokenSource.token; - - // Write an empty line to the terminal for separation between iterations. We've already iterated - // at this point, so log out that we're about to start a new run. - terminal.writeLine(''); - terminal.writeLine(Colors.bold('Starting incremental build...')); - - // Start the incremental build and wait for a source file to change - runRequested = false; - isRunning = true; - - try { - await this._executeOnceAsync(executionManager, cancellationToken, requestRun); - } catch (err) { - if (!(err instanceof AlreadyReportedError)) { - throw err; - } - } finally { - isRunning = false; - } - - if (!runRequested) { - terminal.writeLine(Colors.bold('Waiting for changes. Press CTRL + C to exit...')); + const watchLoop: WatchLoop = new WatchLoop({ + onBeforeExecute: () => { + // Write an empty line to the terminal for separation between iterations. We've already iterated + // at this point, so log out that we're about to start a new run. terminal.writeLine(''); - await Promise.race([requestRunPromise, cliCancellationToken.onCancelledPromise]); + terminal.writeLine(Colorize.bold('Starting incremental build...')); + }, + executeAsync: (state: IWatchLoopState): Promise => { + return this._executeOnceAsync(executionManager, state.abortSignal, state.requestRun); + }, + onRequestRun: (requestor?: string) => { + terminal.writeLine(Colorize.bold(`New run requested by ${requestor || 'unknown task'}`)); + }, + onAbort: () => { + terminal.writeLine(Colorize.bold(`Cancelling incremental build...`)); } - - requestRunPromise = createRequestRunPromise(); - } + }); + return watchLoop; } private async _executeOnceAsync( - executionManager: OperationExecutionManager, - cancellationToken: CancellationToken, - requestRun?: (requestor?: string) => void - ): Promise { + executionManager: OperationExecutionManager, + abortSignal: AbortSignal, + requestRun?: OperationRequestRunCallback + ): Promise { + const { taskStart, taskFinish, phaseStart, phaseFinish } = this._internalHeftSession.lifecycle.hooks; + // Record this as the start of task execution. + this._metricsCollector.setStartTime(); // Execute the action operations - await runWithLoggingAsync( + return await runWithLoggingAsync( () => { - const operationExecutionManagerOptions: IOperationExecutionOptions = { + const operationExecutionManagerOptions: IOperationExecutionOptions< + IHeftTaskOperationMetadata, + IHeftPhaseOperationMetadata + > = { terminal: this._terminal, parallelism: this._parallelism, - cancellationToken, - requestRun + abortSignal, + requestRun, + beforeExecuteOperation( + operation: Operation + ): void { + if (taskStart.isUsed()) { + taskStart.call({ operation }); + } + }, + afterExecuteOperation( + operation: Operation + ): void { + if (taskFinish.isUsed()) { + taskFinish.call({ operation }); + } + }, + beforeExecuteOperationGroup( + operationGroup: OperationGroupRecord + ): void { + if (operationGroup.metadata.phase && phaseStart.isUsed()) { + phaseStart.call({ operation: operationGroup }); + } + }, + afterExecuteOperationGroup( + operationGroup: OperationGroupRecord + ): void { + if (operationGroup.metadata.phase && phaseFinish.isUsed()) { + phaseFinish.call({ operation: operationGroup }); + } + } }; return executionManager.executeAsync(operationExecutionManagerOptions); @@ -393,35 +423,20 @@ export class HeftActionRunner { this._loggingManager, this._terminal, this._metricsCollector, - cancellationToken + abortSignal, + !requestRun ); } - private _generateOperations(): Set { + private _generateOperations(): Set> { const { selectedPhases } = this._action; - const { - defaultParameters: { clean, cleanCache } - } = this.parameterManager; - - if (cleanCache && !clean) { - throw new Error( - `The ${JSON.stringify(Constants.cleanCacheParameterLongName)} option can only be used in ` + - `conjunction with ${JSON.stringify(Constants.cleanParameterLongName)}.` - ); - } - const operations: Map = new Map(); + const operations: Map< + string, + Operation + > = new Map(); + const operationGroups: Map> = new Map(); const internalHeftSession: InternalHeftSession = this._internalHeftSession; - const startLifecycleOperation: Operation = _getOrCreateLifecycleOperation( - internalHeftSession, - 'start', - operations - ); - const finishLifecycleOperation: Operation = _getOrCreateLifecycleOperation( - internalHeftSession, - 'finish', - operations - ); let hasWarnedAboutSkippedPhases: boolean = false; for (const phase of selectedPhases) { @@ -432,7 +447,7 @@ export class HeftActionRunner { // Only write once, and write with yellow to make it stand out without writing a warning to stderr hasWarnedAboutSkippedPhases = true; this._terminal.writeLine( - Colors.bold( + Colorize.bold( 'The provided list of phases does not contain all phase dependencies. You may need to run the ' + 'excluded phases manually.' ) @@ -443,27 +458,28 @@ export class HeftActionRunner { } // Create operation for the phase start node - const phaseOperation: Operation = _getOrCreatePhaseOperation(internalHeftSession, phase, operations); - // Set the 'start' lifecycle operation as a dependency of all phases to ensure the 'start' lifecycle - // operation runs first - phaseOperation.addDependency(startLifecycleOperation); - // Set the phase operation as a dependency of the 'end' lifecycle operation to ensure the phase - // operation runs first - finishLifecycleOperation.addDependency(phaseOperation); + const phaseOperation: Operation = _getOrCreatePhaseOperation( + internalHeftSession, + phase, + operations, + operationGroups + ); // Create operations for each task for (const task of phase.tasks) { - const taskOperation: Operation = _getOrCreateTaskOperation(internalHeftSession, task, operations); + const taskOperation: Operation = _getOrCreateTaskOperation( + internalHeftSession, + task, + operations, + operationGroups + ); // Set the phase operation as a dependency of the task operation to ensure the phase operation runs first taskOperation.addDependency(phaseOperation); - // Set the task operation as a dependency of the 'stop' lifecycle operation to ensure the task operation - // runs first - finishLifecycleOperation.addDependency(taskOperation); // Set all dependency tasks as dependencies of the task operation for (const dependencyTask of task.dependencyTasks) { taskOperation.addDependency( - _getOrCreateTaskOperation(internalHeftSession, dependencyTask, operations) + _getOrCreateTaskOperation(internalHeftSession, dependencyTask, operations, operationGroups) ); } @@ -475,7 +491,8 @@ export class HeftActionRunner { const consumingPhaseOperation: Operation = _getOrCreatePhaseOperation( internalHeftSession, consumingPhase, - operations + operations, + operationGroups ); consumingPhaseOperation.addDependency(taskOperation); // This is purely to simplify the reported graph for phase circularities @@ -489,36 +506,28 @@ export class HeftActionRunner { } } -function _getOrCreateLifecycleOperation( - internalHeftSession: InternalHeftSession, - type: LifecycleOperationRunnerType, - operations: Map -): Operation { - const key: string = `lifecycle.${type}`; - - let operation: Operation | undefined = operations.get(key); - if (!operation) { - operation = new Operation({ - groupName: 'lifecycle', - runner: new LifecycleOperationRunner({ type, internalHeftSession }) - }); - operations.set(key, operation); - } - return operation; -} - function _getOrCreatePhaseOperation( + this: void, internalHeftSession: InternalHeftSession, phase: HeftPhase, - operations: Map + operations: Map, + operationGroups: Map> ): Operation { const key: string = phase.phaseName; let operation: Operation | undefined = operations.get(key); if (!operation) { + let group: OperationGroupRecord | undefined = operationGroups.get( + phase.phaseName + ); + if (!group) { + group = new OperationGroupRecord(phase.phaseName, { phase }); + operationGroups.set(phase.phaseName, group); + } // Only create the operation. Dependencies are hooked up separately operation = new Operation({ - groupName: phase.phaseName, + group, + name: phase.phaseName, runner: new PhaseOperationRunner({ phase, internalHeftSession }) }); operations.set(key, operation); @@ -527,22 +536,110 @@ function _getOrCreatePhaseOperation( } function _getOrCreateTaskOperation( + this: void, internalHeftSession: InternalHeftSession, task: HeftTask, - operations: Map + operations: Map, + operationGroups: Map> ): Operation { const key: string = `${task.parentPhase.phaseName}.${task.taskName}`; - let operation: Operation | undefined = operations.get(key); + let operation: Operation | undefined = operations.get( + key + ) as Operation; if (!operation) { + const group: OperationGroupRecord | undefined = operationGroups.get( + task.parentPhase.phaseName + ); + if (!group) { + throw new InternalError( + `Task ${task.taskName} in phase ${task.parentPhase.phaseName} has no group. This should not happen.` + ); + } operation = new Operation({ - groupName: task.parentPhase.phaseName, + group, runner: new TaskOperationRunner({ internalHeftSession, task - }) + }), + name: task.taskName, + metadata: { task, phase: task.parentPhase } }); operations.set(key, operation); } return operation; } + +async function _startLifecycleAsync(this: void, internalHeftSession: InternalHeftSession): Promise { + const { clean } = internalHeftSession.parameterManager.defaultParameters; + + // Load and apply the lifecycle plugins + const lifecycle: HeftLifecycle = internalHeftSession.lifecycle; + const { lifecycleLogger } = lifecycle; + await lifecycle.applyPluginsAsync(lifecycleLogger.terminal); + + if (lifecycleLogger.hasErrors) { + throw new AlreadyReportedError(); + } + + if (clean) { + const startTime: number = performance.now(); + lifecycleLogger.terminal.writeVerboseLine('Starting clean'); + + // Grab the additional clean operations from the phase + const deleteOperations: IDeleteOperation[] = []; + + // Delete all temp folders for tasks by default + for (const pluginDefinition of lifecycle.pluginDefinitions) { + const lifecycleSession: IHeftLifecycleSession = + await lifecycle.getSessionForPluginDefinitionAsync(pluginDefinition); + deleteOperations.push({ sourcePath: lifecycleSession.tempFolderPath }); + } + + // Create the options and provide a utility method to obtain paths to delete + const cleanHookOptions: IHeftLifecycleCleanHookOptions = { + addDeleteOperations: (...deleteOperationsToAdd: IDeleteOperation[]) => + deleteOperations.push(...deleteOperationsToAdd) + }; + + // Run the plugin clean hook + if (lifecycle.hooks.clean.isUsed()) { + try { + await lifecycle.hooks.clean.promise(cleanHookOptions); + } catch (e) { + // Log out using the clean logger, and return an error status + if (!(e instanceof AlreadyReportedError)) { + lifecycleLogger.emitError(e as Error); + } + throw new AlreadyReportedError(); + } + } + + // Delete the files if any were specified + if (deleteOperations.length) { + const rootFolderPath: string = internalHeftSession.heftConfiguration.buildFolderPath; + await deleteFilesAsync(rootFolderPath, deleteOperations, lifecycleLogger.terminal); + } + + lifecycleLogger.terminal.writeVerboseLine(`Finished clean (${performance.now() - startTime}ms)`); + + if (lifecycleLogger.hasErrors) { + throw new AlreadyReportedError(); + } + } + + // Run the start hook + if (lifecycle.hooks.toolStart.isUsed()) { + const lifecycleToolStartHookOptions: IHeftLifecycleToolStartHookOptions = {}; + await lifecycle.hooks.toolStart.promise(lifecycleToolStartHookOptions); + + if (lifecycleLogger.hasErrors) { + throw new AlreadyReportedError(); + } + } +} + +async function _finishLifecycleAsync(internalHeftSession: InternalHeftSession): Promise { + const lifecycleToolFinishHookOptions: IHeftLifecycleToolFinishHookOptions = {}; + await internalHeftSession.lifecycle.hooks.toolFinish.promise(lifecycleToolFinishHookOptions); +} diff --git a/apps/heft/src/cli/HeftCommandLineParser.ts b/apps/heft/src/cli/HeftCommandLineParser.ts index 18ec4b3f8da..67389a7d053 100644 --- a/apps/heft/src/cli/HeftCommandLineParser.ts +++ b/apps/heft/src/cli/HeftCommandLineParser.ts @@ -1,25 +1,28 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ArgumentParser } from 'argparse'; -import { CommandLineParser, type CommandLineFlagParameter } from '@rushstack/ts-command-line'; +import os from 'node:os'; + import { - Terminal, - InternalError, - ConsoleTerminalProvider, - AlreadyReportedError, - type ITerminal -} from '@rushstack/node-core-library'; + CommandLineParser, + type AliasCommandLineAction, + type CommandLineFlagParameter, + type CommandLineAction +} from '@rushstack/ts-command-line'; +import { InternalError, AlreadyReportedError } from '@rushstack/node-core-library'; +import { Terminal, ConsoleTerminalProvider, type ITerminal } from '@rushstack/terminal'; import { MetricsCollector } from '../metrics/MetricsCollector'; import { HeftConfiguration } from '../configuration/HeftConfiguration'; import { InternalHeftSession } from '../pluginFramework/InternalHeftSession'; import { LoggingManager } from '../pluginFramework/logging/LoggingManager'; -import { Constants } from '../utilities/Constants'; import { CleanAction } from './actions/CleanAction'; import { PhaseAction } from './actions/PhaseAction'; import { RunAction } from './actions/RunAction'; import type { IHeftActionOptions } from './actions/IHeftAction'; +import { AliasAction } from './actions/AliasAction'; +import { getToolParameterNamesFromArgs } from '../utilities/CliUtilities'; +import { Constants } from '../utilities/Constants'; /** * This interfaces specifies values for parameters that must be parsed before the CLI @@ -30,6 +33,8 @@ interface IPreInitializationArgumentValues { unmanaged?: boolean; } +const HEFT_TOOL_FILENAME: 'heft' = 'heft'; + export class HeftCommandLineParser extends CommandLineParser { public readonly globalTerminal: ITerminal; @@ -40,10 +45,11 @@ export class HeftCommandLineParser extends CommandLineParser { private readonly _loggingManager: LoggingManager; private readonly _metricsCollector: MetricsCollector; private readonly _heftConfiguration: HeftConfiguration; + private _internalHeftSession: InternalHeftSession | undefined; public constructor() { super({ - toolFilename: 'heft', + toolFilename: HEFT_TOOL_FILENAME, toolDescription: 'Heft is a pluggable build system designed for web projects.' }); @@ -83,15 +89,17 @@ export class HeftCommandLineParser extends CommandLineParser { InternalError.breakInDebugger = true; } + const numberOfCores: number = os.availableParallelism?.() ?? os.cpus().length; this._heftConfiguration = HeftConfiguration.initialize({ cwd: process.cwd(), - terminalProvider: this._terminalProvider + terminalProvider: this._terminalProvider, + numberOfCores }); this._metricsCollector = new MetricsCollector(); } - public async execute(args?: string[]): Promise { + public async executeAsync(args?: string[]): Promise { // Defensively set the exit code to 1 so if the tool crashes for whatever reason, // we'll have a nonzero exit code. process.exitCode = 1; @@ -105,6 +113,7 @@ export class HeftCommandLineParser extends CommandLineParser { loggingManager: this._loggingManager, metricsCollector: this._metricsCollector }); + this._internalHeftSession = internalHeftSession; const actionOptions: IHeftActionOptions = { internalHeftSession: internalHeftSession, @@ -127,18 +136,70 @@ export class HeftCommandLineParser extends CommandLineParser { this.addAction(new PhaseAction({ ...actionOptions, phase, watch: true })); } - return await super.execute(args); + // Add the action aliases last, since we need the targets to be defined before we can add the aliases + const aliasActions: AliasCommandLineAction[] = []; + for (const [ + aliasName, + { actionName, defaultParameters } + ] of internalHeftSession.actionReferencesByAlias) { + const existingAction: CommandLineAction | undefined = this.tryGetAction(aliasName); + if (existingAction) { + throw new Error( + `The alias "${aliasName}" specified in heft.json cannot be used because an action ` + + 'with that name already exists.' + ); + } + const targetAction: CommandLineAction | undefined = this.tryGetAction(actionName); + if (!targetAction) { + throw new Error( + `The action "${actionName}" referred to by alias "${aliasName}" in heft.json could not be found.` + ); + } + aliasActions.push( + new AliasAction({ + terminal: this.globalTerminal, + toolFilename: HEFT_TOOL_FILENAME, + aliasName, + targetAction, + defaultParameters + }) + ); + } + // Add the alias actions. Do this in a second pass to disallow aliases that refer to other aliases. + for (const aliasAction of aliasActions) { + this.addAction(aliasAction); + } + + return await super.executeAsync(args); } catch (e) { - await this._reportErrorAndSetExitCode(e as Error); + await this._reportErrorAndSetExitCodeAsync(e as Error); return false; } } - protected async onExecute(): Promise { + protected override async onExecuteAsync(): Promise { try { - await super.onExecute(); + const selectedAction: CommandLineAction | undefined = this.selectedAction; + + let commandName: string = ''; + let unaliasedCommandName: string = ''; + + if (selectedAction) { + commandName = selectedAction.actionName; + if (selectedAction instanceof AliasAction) { + unaliasedCommandName = selectedAction.targetAction.actionName; + } else { + unaliasedCommandName = selectedAction.actionName; + } + } + + this._internalHeftSession!.parsedCommandLine = { + commandName, + unaliasedCommandName + }; + await super.onExecuteAsync(); } catch (e) { - await this._reportErrorAndSetExitCode(e as Error); + await this._reportErrorAndSetExitCodeAsync(e as Error); } // If we make it here, things are fine and reset the exit code back to 0 @@ -168,19 +229,17 @@ export class HeftCommandLineParser extends CommandLineParser { // try to evaluate any parameters. This is to ensure that the // `--debug` flag is defined correctly before we do this not-so-rigorous // parameter parsing. - throw new InternalError('onDefineParameters() has not yet been called.'); + throw new InternalError('parameters have not yet been defined.'); } - // This is a rough parsing of the --debug parameter - const parser: ArgumentParser = new ArgumentParser({ addHelp: false }); - parser.addArgument(this._debugFlag.longName, { dest: 'debug', action: 'storeTrue' }); - parser.addArgument(this._unmanagedFlag.longName, { dest: 'unmanaged', action: 'storeTrue' }); - - const [result]: IPreInitializationArgumentValues[] = parser.parseKnownArgs(args); - return result; + const toolParameters: Set = getToolParameterNamesFromArgs(args); + return { + debug: toolParameters.has(this._debugFlag.longName), + unmanaged: toolParameters.has(this._unmanagedFlag.longName) + }; } - private async _reportErrorAndSetExitCode(error: Error): Promise { + private async _reportErrorAndSetExitCodeAsync(error: Error): Promise { if (!(error instanceof AlreadyReportedError)) { this.globalTerminal.writeErrorLine(error.toString()); } @@ -190,8 +249,9 @@ export class HeftCommandLineParser extends CommandLineParser { this.globalTerminal.writeErrorLine(error.stack!); } - if (!process.exitCode || process.exitCode > 0) { - process.exit(process.exitCode); + const exitCode: string | number | undefined = process.exitCode; + if (!exitCode || typeof exitCode !== 'number' || exitCode > 0) { + process.exit(exitCode); } else { process.exit(1); } diff --git a/apps/heft/src/cli/actions/AliasAction.ts b/apps/heft/src/cli/actions/AliasAction.ts new file mode 100644 index 00000000000..71fd1e52af3 --- /dev/null +++ b/apps/heft/src/cli/actions/AliasAction.ts @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { ITerminal } from '@rushstack/terminal'; +import { + AliasCommandLineAction, + type IAliasCommandLineActionOptions, + type CommandLineAction +} from '@rushstack/ts-command-line'; + +export interface IAliasActionOptions extends IAliasCommandLineActionOptions { + terminal: ITerminal; +} + +export class AliasAction extends AliasCommandLineAction { + private readonly _toolFilename: string; + private readonly _terminal: ITerminal; + + public constructor(options: IAliasActionOptions) { + super(options); + this._toolFilename = options.toolFilename; + this._terminal = options.terminal; + } + + protected override async onExecuteAsync(): Promise { + const toolFilename: string = this._toolFilename; + const actionName: string = this.actionName; + const targetAction: CommandLineAction = this.targetAction; + const defaultParameters: ReadonlyArray = this.defaultParameters; + const defaultParametersString: string = defaultParameters.join(' '); + + this._terminal.writeLine( + `The "${toolFilename} ${actionName}" alias was expanded to "${toolFilename} ${targetAction.actionName}` + + `${defaultParametersString ? ` ${defaultParametersString}` : ''}".` + ); + + await super.onExecuteAsync(); + } +} diff --git a/apps/heft/src/cli/actions/CleanAction.ts b/apps/heft/src/cli/actions/CleanAction.ts index 39e4f0f3506..11d1c8e4c2b 100644 --- a/apps/heft/src/cli/actions/CleanAction.ts +++ b/apps/heft/src/cli/actions/CleanAction.ts @@ -6,7 +6,8 @@ import { type CommandLineFlagParameter, type CommandLineStringListParameter } from '@rushstack/ts-command-line'; -import type { ITerminal } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; +import { OperationStatus } from '@rushstack/operation-graph'; import type { IHeftAction, IHeftActionOptions } from './IHeftAction'; import type { HeftPhase } from '../../pluginFramework/HeftPhase'; @@ -17,9 +18,7 @@ import type { HeftTaskSession } from '../../pluginFramework/HeftTaskSession'; import { Constants } from '../../utilities/Constants'; import { definePhaseScopingParameters, expandPhases } from './RunAction'; import { deleteFilesAsync, type IDeleteOperation } from '../../plugins/DeleteFilesPlugin'; -import { initializeHeft, runWithLoggingAsync } from '../HeftActionRunner'; -import { CancellationToken } from '../../pluginFramework/CancellationToken'; -import { OperationStatus } from '../../operations/OperationStatus'; +import { ensureCliAbortSignal, initializeHeft, runWithLoggingAsync } from '../HeftActionRunner'; export class CleanAction extends CommandLineAction implements IHeftAction { public readonly watch: boolean = false; @@ -30,7 +29,6 @@ export class CleanAction extends CommandLineAction implements IHeftAction { private readonly _toParameter: CommandLineStringListParameter; private readonly _toExceptParameter: CommandLineStringListParameter; private readonly _onlyParameter: CommandLineStringListParameter; - private readonly _cleanCacheFlag: CommandLineFlagParameter; private _selectedPhases: ReadonlySet | undefined; public constructor(options: IHeftActionOptions) { @@ -54,12 +52,6 @@ export class CleanAction extends CommandLineAction implements IHeftAction { parameterShortName: Constants.verboseParameterShortName, description: 'If specified, log information useful for debugging.' }); - this._cleanCacheFlag = this.defineFlagParameter({ - parameterLongName: Constants.cleanCacheParameterLongName, - description: - 'If specified, clean the cache directories in addition to the temp directories and provided ' + - 'clean operations.' - }); } public get selectedPhases(): ReadonlySet { @@ -84,10 +76,12 @@ export class CleanAction extends CommandLineAction implements IHeftAction { return this._selectedPhases; } - protected async onExecute(): Promise { + protected override async onExecuteAsync(): Promise { const { heftConfiguration } = this._internalHeftSession; - const cancellationToken: CancellationToken = new CancellationToken(); + const abortSignal: AbortSignal = ensureCliAbortSignal(this._terminal); + // Record this as the start of task execution. + this._metricsCollector.setStartTime(); initializeHeft(heftConfiguration, this._terminal, this._verboseFlag.value); await runWithLoggingAsync( this._cleanFilesAsync.bind(this), @@ -95,7 +89,7 @@ export class CleanAction extends CommandLineAction implements IHeftAction { this._internalHeftSession.loggingManager, this._terminal, this._metricsCollector, - cancellationToken + abortSignal ); } @@ -107,16 +101,16 @@ export class CleanAction extends CommandLineAction implements IHeftAction { for (const task of phase.tasks) { const taskSession: HeftTaskSession = phaseSession.getSessionForTask(task); deleteOperations.push({ sourcePath: taskSession.tempFolderPath }); - if (this._cleanCacheFlag.value) { - deleteOperations.push({ sourcePath: taskSession.cacheFolderPath }); - } } // Add the manually specified clean operations deleteOperations.push(...phase.cleanFiles); } // Delete the files - await deleteFilesAsync(deleteOperations, this._terminal); + if (deleteOperations.length) { + const rootFolderPath: string = this._internalHeftSession.heftConfiguration.buildFolderPath; + await deleteFilesAsync(rootFolderPath, deleteOperations, this._terminal); + } return deleteOperations.length === 0 ? OperationStatus.NoOp : OperationStatus.Success; } diff --git a/apps/heft/src/cli/actions/IHeftAction.ts b/apps/heft/src/cli/actions/IHeftAction.ts index 5925bfb503e..ad4a91468da 100644 --- a/apps/heft/src/cli/actions/IHeftAction.ts +++ b/apps/heft/src/cli/actions/IHeftAction.ts @@ -2,7 +2,7 @@ // See LICENSE in the project root for license information. import type { CommandLineAction } from '@rushstack/ts-command-line'; -import type { ITerminal } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; import type { HeftConfiguration } from '../../configuration/HeftConfiguration'; import type { MetricsCollector } from '../../metrics/MetricsCollector'; diff --git a/apps/heft/src/cli/actions/PhaseAction.ts b/apps/heft/src/cli/actions/PhaseAction.ts index c2bcc6542a2..f1f4643af3a 100644 --- a/apps/heft/src/cli/actions/PhaseAction.ts +++ b/apps/heft/src/cli/actions/PhaseAction.ts @@ -20,19 +20,21 @@ export class PhaseAction extends CommandLineAction implements IHeftAction { private _selectedPhases: Set | undefined; public constructor(options: IPhaseActionOptions) { + const { phase, watch = false } = options; + const { phaseName, phaseDescription } = phase; super({ - actionName: `${options.phase.phaseName}${options.watch ? '-watch' : ''}`, + actionName: `${phaseName}${watch ? '-watch' : ''}`, documentation: - `Runs to the ${options.phase.phaseName} phase, including all transitive dependencies` + - (options.watch ? ', in watch mode.' : '.') + - (options.phase.phaseDescription ? ` ${options.phase.phaseDescription}` : ''), + `Runs to the ${phaseName} phase, including all transitive dependencies` + + (watch ? ', in watch mode.' : '.') + + (phaseDescription ? ` ${phaseDescription}` : ''), summary: - `Runs to the ${options.phase.phaseName} phase, including all transitive dependencies` + - (options.watch ? ', in watch mode.' : '.') + `Runs to the ${phaseName} phase, including all transitive dependencies` + + (watch ? ', in watch mode.' : '.') }); - this.watch = options.watch ?? false; - this._phase = options.phase; + this.watch = watch; + this._phase = phase; this._actionRunner = new HeftActionRunner({ action: this, ...options }); this._actionRunner.defineParameters(); } @@ -47,7 +49,7 @@ export class PhaseAction extends CommandLineAction implements IHeftAction { return this._selectedPhases; } - protected async onExecute(): Promise { + protected override async onExecuteAsync(): Promise { await this._actionRunner.executeAsync(); } } diff --git a/apps/heft/src/cli/actions/RunAction.ts b/apps/heft/src/cli/actions/RunAction.ts index 638f76b3890..6454ea5b901 100644 --- a/apps/heft/src/cli/actions/RunAction.ts +++ b/apps/heft/src/cli/actions/RunAction.ts @@ -6,7 +6,8 @@ import { type CommandLineParameterProvider, type CommandLineStringListParameter } from '@rushstack/ts-command-line'; -import { AlreadyReportedError, type ITerminal } from '@rushstack/node-core-library'; +import { AlreadyReportedError } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; import { Selection } from '../../utilities/Selection'; import { HeftActionRunner } from '../HeftActionRunner'; @@ -78,21 +79,18 @@ export function definePhaseScopingParameters(action: IHeftAction): IScopingParam return { toParameter: action.defineStringListParameter({ parameterLongName: Constants.toParameterLongName, - parameterShortName: Constants.toParameterShortName, description: `The phase to ${action.actionName} to, including all transitive dependencies.`, argumentName: 'PHASE', parameterGroup: ScopedCommandLineAction.ScopingParameterGroup }), toExceptParameter: action.defineStringListParameter({ parameterLongName: Constants.toExceptParameterLongName, - parameterShortName: Constants.toExceptParameterShortName, description: `The phase to ${action.actionName} to (but not include), including all transitive dependencies.`, argumentName: 'PHASE', parameterGroup: ScopedCommandLineAction.ScopingParameterGroup }), onlyParameter: action.defineStringListParameter({ parameterLongName: Constants.onlyParameterLongName, - parameterShortName: Constants.onlyParameterShortName, description: `The phase to ${action.actionName}.`, argumentName: 'PHASE', parameterGroup: ScopedCommandLineAction.ScopingParameterGroup @@ -147,7 +145,7 @@ export class RunAction extends ScopedCommandLineAction implements IHeftAction { this._actionRunner.defineParameters(scopedParameterProvider); } - protected async onExecute(): Promise { + protected override async onExecuteAsync(): Promise { await this._actionRunner.executeAsync(); } } diff --git a/apps/heft/src/configuration/HeftConfiguration.ts b/apps/heft/src/configuration/HeftConfiguration.ts index 9d247603027..e6079a8a3f6 100644 --- a/apps/heft/src/configuration/HeftConfiguration.ts +++ b/apps/heft/src/configuration/HeftConfiguration.ts @@ -1,17 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + +import { type IPackageJson, PackageJsonLookup, InternalError, Path } from '@rushstack/node-core-library'; +import { Terminal, type ITerminalProvider, type ITerminal } from '@rushstack/terminal'; import { - Terminal, - type ITerminalProvider, - type IPackageJson, - PackageJsonLookup, - InternalError, - type ITerminal -} from '@rushstack/node-core-library'; -import { trueCasePathSync } from 'true-case-path'; -import { RigConfig } from '@rushstack/rig-package'; + type IProjectConfigurationFileSpecification, + ProjectConfigurationFile +} from '@rushstack/heft-config-file'; +import { type IRigConfig, RigConfig } from '@rushstack/rig-package'; import { Constants } from '../utilities/Constants'; import { RigPackageResolver, type IRigPackageResolver } from './RigPackageResolver'; @@ -29,26 +27,48 @@ export interface IHeftConfigurationInitializationOptions { * Terminal instance to facilitate logging. */ terminalProvider: ITerminalProvider; + + /** + * The number of CPU cores available to the process. This is used to determine how many tasks can be run in parallel. + */ + numberOfCores: number; +} + +interface IHeftConfigurationOptions extends IHeftConfigurationInitializationOptions { + buildFolderPath: string; +} + +interface IProjectConfigurationFileEntry { + options: IProjectConfigurationFileSpecification; + loader: ProjectConfigurationFile; } /** * @public */ export class HeftConfiguration { - private _buildFolderPath!: string; + private _slashNormalizedBuildFolderPath: string | undefined; private _projectConfigFolderPath: string | undefined; - private _cacheFolderPath: string | undefined; private _tempFolderPath: string | undefined; - private _rigConfig: RigConfig | undefined; - private _globalTerminal!: Terminal; - private _terminalProvider!: ITerminalProvider; - private _rigPackageResolver!: RigPackageResolver; + private _rigConfig: IRigConfig | undefined; + private _rigPackageResolver: RigPackageResolver | undefined; + + private readonly _knownConfigurationFiles: Map> = new Map(); /** * Project build folder path. This is the folder containing the project's package.json file. */ - public get buildFolderPath(): string { - return this._buildFolderPath; + public readonly buildFolderPath: string; + + /** + * {@link HeftConfiguration.buildFolderPath} with all path separators converted to forward slashes. + */ + public get slashNormalizedBuildFolderPath(): string { + if (!this._slashNormalizedBuildFolderPath) { + this._slashNormalizedBuildFolderPath = Path.convertToSlashes(this.buildFolderPath); + } + + return this._slashNormalizedBuildFolderPath; } /** @@ -62,22 +82,6 @@ export class HeftConfiguration { return this._projectConfigFolderPath; } - /** - * The project's cache folder. - * - * @remarks This folder exists at \/.cache. In general, this folder is used to store - * cached output from tasks under task-specific subfolders, and is not intended to be directly - * written to. Instead, plugins should write to the directory provided by - * HeftTaskSession.taskCacheFolderPath - */ - public get cacheFolderPath(): string { - if (!this._cacheFolderPath) { - this._cacheFolderPath = path.join(this.buildFolderPath, Constants.cacheFolderName); - } - - return this._cacheFolderPath; - } - /** * The project's temporary folder. * @@ -87,7 +91,7 @@ export class HeftConfiguration { */ public get tempFolderPath(): string { if (!this._tempFolderPath) { - this._tempFolderPath = path.join(this._buildFolderPath, Constants.tempFolderName); + this._tempFolderPath = path.join(this.buildFolderPath, Constants.tempFolderName); } return this._tempFolderPath; @@ -96,7 +100,7 @@ export class HeftConfiguration { /** * The rig.json configuration for this project, if present. */ - public get rigConfig(): RigConfig { + public get rigConfig(): IRigConfig { if (!this._rigConfig) { throw new InternalError( 'The rigConfig cannot be accessed until HeftConfiguration.checkForRigAsync() has been called' @@ -116,22 +120,19 @@ export class HeftConfiguration { rigConfig: this.rigConfig }); } + return this._rigPackageResolver; } /** * Terminal instance to facilitate logging. */ - public get globalTerminal(): ITerminal { - return this._globalTerminal; - } + public readonly globalTerminal: ITerminal; /** * Terminal provider for the provided terminal. */ - public get terminalProvider(): ITerminalProvider { - return this._terminalProvider; - } + public readonly terminalProvider: ITerminalProvider; /** * The Heft tool's package.json @@ -147,7 +148,18 @@ export class HeftConfiguration { return PackageJsonLookup.instance.tryLoadPackageJsonFor(this.buildFolderPath)!; } - private constructor() {} + /** + * The number of CPU cores available to the process. This can be used to determine how many tasks can be run + * in parallel. + */ + public readonly numberOfCores: number; + + private constructor({ terminalProvider, buildFolderPath, numberOfCores }: IHeftConfigurationOptions) { + this.buildFolderPath = buildFolderPath; + this.terminalProvider = terminalProvider; + this.numberOfCores = numberOfCores; + this.globalTerminal = new Terminal(terminalProvider); + } /** * Performs the search for rig.json and initializes the `HeftConfiguration.rigConfig` object. @@ -156,34 +168,84 @@ export class HeftConfiguration { public async _checkForRigAsync(): Promise { if (!this._rigConfig) { this._rigConfig = await RigConfig.loadForProjectFolderAsync({ - projectFolderPath: this._buildFolderPath + projectFolderPath: this.buildFolderPath }); } } + /** + * Attempts to load a riggable project configuration file using blocking, synchronous I/O. + * @param options - The options for the configuration file loader from `@rushstack/heft-config-file`. If invoking this function multiple times for the same file, reuse the same object. + * @param terminal - The terminal to log messages during configuration file loading. + * @returns The configuration file, or undefined if it could not be loaded. + */ + public tryLoadProjectConfigurationFile( + options: IProjectConfigurationFileSpecification, + terminal: ITerminal + ): TConfigFile | undefined { + const loader: ProjectConfigurationFile = this._getConfigFileLoader(options); + return loader.tryLoadConfigurationFileForProject(terminal, this.buildFolderPath, this._rigConfig); + } + + /** + * Attempts to load a riggable project configuration file using asynchronous I/O. + * @param options - The options for the configuration file loader from `@rushstack/heft-config-file`. If invoking this function multiple times for the same file, reuse the same object. + * @param terminal - The terminal to log messages during configuration file loading. + * @returns A promise that resolves to the configuration file, or undefined if it could not be loaded. + */ + public async tryLoadProjectConfigurationFileAsync( + options: IProjectConfigurationFileSpecification, + terminal: ITerminal + ): Promise { + const loader: ProjectConfigurationFile = this._getConfigFileLoader(options); + return loader.tryLoadConfigurationFileForProjectAsync(terminal, this.buildFolderPath, this._rigConfig); + } + /** * @internal */ public static initialize(options: IHeftConfigurationInitializationOptions): HeftConfiguration { - const configuration: HeftConfiguration = new HeftConfiguration(); - const packageJsonPath: string | undefined = PackageJsonLookup.instance.tryGetPackageJsonFilePathFor( options.cwd ); + let buildFolderPath: string; if (packageJsonPath) { - let buildFolderPath: string = path.dirname(packageJsonPath); - // The CWD path's casing may be incorrect on a case-insensitive filesystem. Some tools, like Jest - // expect the casing of the project path to be correct and produce unexpected behavior when the casing - // isn't correct. - // This ensures the casing of the project folder is correct. - buildFolderPath = trueCasePathSync(buildFolderPath); - configuration._buildFolderPath = buildFolderPath; + buildFolderPath = path.dirname(packageJsonPath); + // On Windows it is possible for the drive letter in the CWD to be lowercase, but the normalized naming is uppercase + // Force it to always be uppercase for consistency. + buildFolderPath = + process.platform === 'win32' + ? buildFolderPath.charAt(0).toUpperCase() + buildFolderPath.slice(1) + : buildFolderPath; } else { throw new Error('No package.json file found. Are you in a project folder?'); } - configuration._terminalProvider = options.terminalProvider; - configuration._globalTerminal = new Terminal(options.terminalProvider); + const configuration: HeftConfiguration = new HeftConfiguration({ + ...options, + buildFolderPath + }); return configuration; } + + private _getConfigFileLoader( + options: IProjectConfigurationFileSpecification + ): ProjectConfigurationFile { + let entry: IProjectConfigurationFileEntry | undefined = this._knownConfigurationFiles.get( + options.projectRelativeFilePath + ) as IProjectConfigurationFileEntry | undefined; + + if (!entry) { + entry = { + options: Object.freeze(options), + loader: new ProjectConfigurationFile(options) + }; + } else if (options !== entry.options) { + throw new Error( + `The project configuration file for ${options.projectRelativeFilePath} has already been loaded with different options. Please ensure that options object used to load the configuration file is the same referenced object in all calls.` + ); + } + + return entry.loader; + } } diff --git a/apps/heft/src/configuration/HeftPluginConfiguration.ts b/apps/heft/src/configuration/HeftPluginConfiguration.ts index edb45399c33..1842c68d778 100644 --- a/apps/heft/src/configuration/HeftPluginConfiguration.ts +++ b/apps/heft/src/configuration/HeftPluginConfiguration.ts @@ -1,17 +1,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; import { JsonFile, JsonSchema } from '@rushstack/node-core-library'; import { HeftLifecyclePluginDefinition, - HeftPluginDefinitionBase, + type HeftPluginDefinitionBase, HeftTaskPluginDefinition, type IHeftLifecyclePluginDefinitionJson, type IHeftTaskPluginDefinitionJson } from './HeftPluginDefinition'; import type { IHeftConfigurationJsonPluginSpecifier } from '../utilities/CoreConfigFiles'; +import heftPluginSchema from '../schemas/heft-plugin.schema.json'; export interface IHeftPluginConfigurationJson { lifecyclePlugins?: IHeftLifecyclePluginDefinitionJson[]; @@ -20,15 +20,13 @@ export interface IHeftPluginConfigurationJson { const HEFT_PLUGIN_CONFIGURATION_FILENAME: 'heft-plugin.json' = 'heft-plugin.json'; +const _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(heftPluginSchema); +const _pluginConfigurationPromises: Map> = new Map(); + /** * Loads and validates the heft-plugin.json file. */ export class HeftPluginConfiguration { - private static _jsonSchema: JsonSchema = JsonSchema.fromFile( - path.join(__dirname, '..', 'schemas', 'heft-plugin.schema.json') - ); - private static _pluginConfigurationPromises: Map> = new Map(); - private readonly _heftPluginConfigurationJson: IHeftPluginConfigurationJson; private _lifecyclePluginDefinitions: Set | undefined; private _lifecyclePluginDefinitionsMap: Map | undefined; @@ -65,56 +63,21 @@ export class HeftPluginConfiguration { ): Promise { const resolvedHeftPluginConfigurationJsonFilename: string = `${packageRoot}/${HEFT_PLUGIN_CONFIGURATION_FILENAME}`; let heftPluginConfigurationPromise: Promise | undefined = - HeftPluginConfiguration._pluginConfigurationPromises.get(packageRoot); + _pluginConfigurationPromises.get(packageRoot); if (!heftPluginConfigurationPromise) { heftPluginConfigurationPromise = (async () => { const heftPluginConfigurationJson: IHeftPluginConfigurationJson = await JsonFile.loadAndValidateAsync( resolvedHeftPluginConfigurationJsonFilename, - HeftPluginConfiguration._jsonSchema + _jsonSchema ); return new HeftPluginConfiguration(heftPluginConfigurationJson, packageRoot, packageName); })(); - HeftPluginConfiguration._pluginConfigurationPromises.set(packageRoot, heftPluginConfigurationPromise); + _pluginConfigurationPromises.set(packageRoot, heftPluginConfigurationPromise); } return await heftPluginConfigurationPromise; } - public get lifecyclePluginDefinitions(): ReadonlySet { - if (!this._lifecyclePluginDefinitions) { - this._lifecyclePluginDefinitions = new Set(); - for (const lifecyclePluginDefinitionJson of this._heftPluginConfigurationJson.lifecyclePlugins || []) { - this._lifecyclePluginDefinitions.add( - HeftLifecyclePluginDefinition.loadFromObject({ - heftPluginDefinitionJson: lifecyclePluginDefinitionJson, - packageRoot: this.packageRoot, - packageName: this.packageName - }) - ); - } - } - return this._lifecyclePluginDefinitions; - } - - /** - * Task plugin definitions sourced from the heft-plugin.json file. - */ - public get taskPluginDefinitions(): ReadonlySet { - if (!this._taskPluginDefinitions) { - this._taskPluginDefinitions = new Set(); - for (const taskPluginDefinitionJson of this._heftPluginConfigurationJson.taskPlugins || []) { - this._taskPluginDefinitions.add( - HeftTaskPluginDefinition.loadFromObject({ - heftPluginDefinitionJson: taskPluginDefinitionJson, - packageRoot: this.packageRoot, - packageName: this.packageName - }) - ); - } - } - return this._taskPluginDefinitions; - } - /** * Returns a loaded plugin definition for the provided specifier. Specifiers are normally obtained from the * heft.json file. @@ -123,10 +86,10 @@ export class HeftPluginConfiguration { pluginSpecifier: IHeftConfigurationJsonPluginSpecifier ): HeftPluginDefinitionBase { if (!pluginSpecifier.pluginName) { - const pluginDefinitions: HeftPluginDefinitionBase[] = [ - ...this.lifecyclePluginDefinitions, - ...this.taskPluginDefinitions - ]; + const pluginDefinitions: HeftPluginDefinitionBase[] = ([] as HeftPluginDefinitionBase[]).concat( + Array.from(this._getLifecyclePluginDefinitions()), + Array.from(this._getTaskPluginDefinitions()) + ); // Make an attempt at resolving the plugin without the name by looking for the first plugin if (pluginDefinitions.length > 1) { throw new Error( @@ -150,6 +113,24 @@ export class HeftPluginConfiguration { } } + /** + * Returns if the provided plugin definition is a lifecycle plugin definition. + */ + public isLifecyclePluginDefinition( + pluginDefinition: HeftPluginDefinitionBase + ): pluginDefinition is HeftLifecyclePluginDefinition { + return this._getLifecyclePluginDefinitions().has(pluginDefinition); + } + + /** + * Returns if the provided plugin definition is a task plugin definition. + */ + public isTaskPluginDefinition( + pluginDefinition: HeftPluginDefinitionBase + ): pluginDefinition is HeftTaskPluginDefinition { + return this._getTaskPluginDefinitions().has(pluginDefinition); + } + /** * Returns a loaded lifecycle plugin definition for the provided plugin name. If one can't be found, * returns undefined. @@ -159,7 +140,10 @@ export class HeftPluginConfiguration { ): HeftLifecyclePluginDefinition | undefined { if (!this._lifecyclePluginDefinitionsMap) { this._lifecyclePluginDefinitionsMap = new Map( - [...this.lifecyclePluginDefinitions].map((d: HeftLifecyclePluginDefinition) => [d.pluginName, d]) + Array.from(this._getLifecyclePluginDefinitions()).map((d: HeftLifecyclePluginDefinition) => [ + d.pluginName, + d + ]) ); } return this._lifecyclePluginDefinitionsMap.get(lifecyclePluginName); @@ -172,12 +156,47 @@ export class HeftPluginConfiguration { public tryGetTaskPluginDefinitionByName(taskPluginName: string): HeftTaskPluginDefinition | undefined { if (!this._taskPluginDefinitionsMap) { this._taskPluginDefinitionsMap = new Map( - [...this.taskPluginDefinitions].map((d: HeftTaskPluginDefinition) => [d.pluginName, d]) + Array.from(this._getTaskPluginDefinitions()).map((d: HeftTaskPluginDefinition) => [d.pluginName, d]) ); } return this._taskPluginDefinitionsMap.get(taskPluginName); } + private _getLifecyclePluginDefinitions(): ReadonlySet { + if (!this._lifecyclePluginDefinitions) { + this._lifecyclePluginDefinitions = new Set(); + for (const lifecyclePluginDefinitionJson of this._heftPluginConfigurationJson.lifecyclePlugins || []) { + this._lifecyclePluginDefinitions.add( + HeftLifecyclePluginDefinition.loadFromObject({ + heftPluginDefinitionJson: lifecyclePluginDefinitionJson, + packageRoot: this.packageRoot, + packageName: this.packageName + }) + ); + } + } + return this._lifecyclePluginDefinitions; + } + + /** + * Task plugin definitions sourced from the heft-plugin.json file. + */ + private _getTaskPluginDefinitions(): ReadonlySet { + if (!this._taskPluginDefinitions) { + this._taskPluginDefinitions = new Set(); + for (const taskPluginDefinitionJson of this._heftPluginConfigurationJson.taskPlugins || []) { + this._taskPluginDefinitions.add( + HeftTaskPluginDefinition.loadFromObject({ + heftPluginDefinitionJson: taskPluginDefinitionJson, + packageRoot: this.packageRoot, + packageName: this.packageName + }) + ); + } + } + return this._taskPluginDefinitions; + } + private _validate(heftPluginConfigurationJson: IHeftPluginConfigurationJson, packageName: string): void { if ( !heftPluginConfigurationJson.lifecyclePlugins?.length && diff --git a/apps/heft/src/configuration/HeftPluginDefinition.ts b/apps/heft/src/configuration/HeftPluginDefinition.ts index b6c9e5f4886..9c4a0114054 100644 --- a/apps/heft/src/configuration/HeftPluginDefinition.ts +++ b/apps/heft/src/configuration/HeftPluginDefinition.ts @@ -1,4 +1,8 @@ -import * as path from 'path'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + import { InternalError, JsonSchema } from '@rushstack/node-core-library'; import type { IHeftPlugin } from '../pluginFramework/IHeftPlugin'; @@ -19,6 +23,10 @@ export interface IBaseParameterJson { * The name of the parameter (e.g. \"--verbose\"). This is a required field. */ longName: string; + /** + * An optional short form of the parameter (e.g. \"-v\" instead of \"--verbose\"). + */ + shortName?: string; /** * A detailed description of the parameter, which appears when requesting help for the command (e.g. \"rush --help my-command\"). */ @@ -185,8 +193,6 @@ export interface IHeftPluginDefinitionOptions { } export abstract class HeftPluginDefinitionBase { - private static _loadedPluginPathsByName: Map = new Map(); - private _heftPluginDefinitionJson: IHeftPluginDefinitionJson; private _pluginPackageName: string; private _resolvedEntryPoint: string; @@ -210,20 +216,6 @@ export abstract class HeftPluginDefinitionBase { seenParameters.add(parameter.longName); } - // Ensure that plugin names are unique. Main reason for this restriction is to ensure that command-line - // parameter conflicts can be handled/undocumented synonms can be provided in all scenarios - const existingPluginPath: string | undefined = HeftPluginDefinitionBase._loadedPluginPathsByName.get( - this.pluginName - ); - if (existingPluginPath && existingPluginPath !== this._resolvedEntryPoint) { - throw new Error( - `A plugin named ${JSON.stringify(this.pluginName)} has already been loaded from ` + - `"${existingPluginPath}". Plugins must have unique names.` - ); - } else if (!existingPluginPath) { - HeftPluginDefinitionBase._loadedPluginPathsByName.set(this.pluginName, this._resolvedEntryPoint); - } - // Unfortunately loading the schema is a synchronous process. if (options.heftPluginDefinitionJson.optionsSchema) { const resolvedSchemaPath: string = path.resolve( @@ -345,9 +337,10 @@ export class HeftLifecyclePluginDefinition extends HeftPluginDefinitionBase { /** * {@inheritDoc HeftPluginDefinitionBase.loadPluginAsync} - * @override */ - public loadPluginAsync(logger: IScopedLogger): Promise> { + public override loadPluginAsync( + logger: IScopedLogger + ): Promise> { return super.loadPluginAsync(logger); } } @@ -362,9 +355,10 @@ export class HeftTaskPluginDefinition extends HeftPluginDefinitionBase { /** * {@inheritDoc HeftPluginDefinitionBase.loadPluginAsync} - * @override */ - public loadPluginAsync(logger: IScopedLogger): Promise> { + public override loadPluginAsync( + logger: IScopedLogger + ): Promise> { return super.loadPluginAsync(logger); } } diff --git a/apps/heft/src/configuration/RigPackageResolver.ts b/apps/heft/src/configuration/RigPackageResolver.ts index 0f08c95e6b7..03a70db6dc4 100644 --- a/apps/heft/src/configuration/RigPackageResolver.ts +++ b/apps/heft/src/configuration/RigPackageResolver.ts @@ -1,15 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import { PackageJsonLookup, Import, - type ITerminal, type INodePackageJson, type IPackageJson } from '@rushstack/node-core-library'; -import type { RigConfig } from '@rushstack/rig-package'; +import type { ITerminal } from '@rushstack/terminal'; +import type { IRigConfig } from '@rushstack/rig-package'; /** * Rig resolves requested tools from the project's Heft rig. @@ -29,7 +30,7 @@ export interface IRigPackageResolver { export interface IRigPackageResolverOptions { buildFolder: string; projectPackageJson: IPackageJson; - rigConfig: RigConfig; + rigConfig: IRigConfig; } /** @@ -38,7 +39,7 @@ export interface IRigPackageResolverOptions { export class RigPackageResolver implements IRigPackageResolver { private readonly _buildFolder: string; private readonly _projectPackageJson: IPackageJson; - private readonly _rigConfig: RigConfig; + private readonly _rigConfig: IRigConfig; private readonly _packageJsonLookup: PackageJsonLookup = new PackageJsonLookup(); private readonly _resolverCache: Map> = new Map(); @@ -100,7 +101,7 @@ export class RigPackageResolver implements IRigPackageResolver { } // See if the project rig has a regular dependency on the package - const rigConfiguration: RigConfig = this._rigConfig; + const rigConfiguration: IRigConfig = this._rigConfig; if (rigConfiguration.rigFound) { const rigFolder: string = rigConfiguration.getResolvedProfileFolder(); const rigPackageJsonPath: string | undefined = diff --git a/apps/heft/src/configuration/types.ts b/apps/heft/src/configuration/types.ts new file mode 100644 index 00000000000..46d9e68f411 --- /dev/null +++ b/apps/heft/src/configuration/types.ts @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export type { + CustomValidationFunction, + ICustomJsonPathMetadata, + ICustomPropertyInheritance, + IJsonPathMetadata, + IJsonPathMetadataResolverOptions, + IJsonPathsMetadata, + INonCustomJsonPathMetadata, + IOriginalValueOptions, + IProjectConfigurationFileSpecification, + IPropertiesInheritance, + IPropertyInheritance, + IPropertyInheritanceDefaults, + InheritanceType, + PathResolutionMethod, + PropertyInheritanceCustomFunction +} from '@rushstack/heft-config-file'; diff --git a/apps/heft/src/index.ts b/apps/heft/src/index.ts index 09be18b4754..0c6006dfc8c 100644 --- a/apps/heft/src/index.ts +++ b/apps/heft/src/index.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +/// + /** * Heft is a config-driven toolchain that invokes other popular tools such * as TypeScript, ESLint, Jest, Webpack, and API Extractor. You can use it to build @@ -9,6 +11,9 @@ * @packageDocumentation */ +import type * as ConfigurationFile from './configuration/types'; +export type { ConfigurationFile }; + export { HeftConfiguration, type IHeftConfigurationInitializationOptions as _IHeftConfigurationInitializationOptions @@ -18,13 +23,6 @@ export type { IRigPackageResolver } from './configuration/RigPackageResolver'; export type { IHeftPlugin, IHeftTaskPlugin, IHeftLifecyclePlugin } from './pluginFramework/IHeftPlugin'; -export { - CancellationTokenSource, - CancellationToken, - type ICancellationTokenSourceOptions, - type ICancellationTokenOptions as _ICancellationTokenOptions -} from './pluginFramework/CancellationToken'; - export type { IHeftParameters, IHeftDefaultParameters } from './pluginFramework/HeftParameterManager'; export type { @@ -32,10 +30,15 @@ export type { IHeftLifecycleHooks, IHeftLifecycleCleanHookOptions, IHeftLifecycleToolStartHookOptions, - IHeftLifecycleToolFinishHookOptions + IHeftLifecycleToolFinishHookOptions, + IHeftTaskStartHookOptions, + IHeftTaskFinishHookOptions, + IHeftPhaseStartHookOptions, + IHeftPhaseFinishHookOptions } from './pluginFramework/HeftLifecycleSession'; export type { + IHeftParsedCommandLine, IHeftTaskSession, IHeftTaskHooks, IHeftTaskFileOperations, @@ -51,7 +54,14 @@ export type { IRunScript, IRunScriptOptions } from './plugins/RunScriptPlugin'; export type { IFileSelectionSpecifier, IGlobOptions, GlobFn, WatchGlobFn } from './plugins/FileGlobSpecifier'; -export type { IWatchedFileState } from './utilities/WatchFileSystemAdapter'; +export type { + IWatchedFileState, + IWatchFileSystem, + ReaddirDirentCallback, + ReaddirStringCallback, + StatCallback, + IReaddirOptions +} from './utilities/WatchFileSystemAdapter'; export { type IHeftRecordMetricsHookOptions, @@ -73,3 +83,9 @@ export type { CommandLineStringListParameter, CommandLineStringParameter } from '@rushstack/ts-command-line'; + +export type { IHeftTaskOperationMetadata } from './cli/HeftActionRunner'; +export type { IHeftPhaseOperationMetadata } from './cli/HeftActionRunner'; + +export type { IHeftTask } from './pluginFramework/HeftTask'; +export type { IHeftPhase } from './pluginFramework/HeftPhase'; diff --git a/apps/heft/src/legacy-compatibility/start.js b/apps/heft/src/legacy-compatibility/start.js new file mode 100644 index 00000000000..5a4723e7d2e --- /dev/null +++ b/apps/heft/src/legacy-compatibility/start.js @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// This file is specifically included for compatibility with older global installations of Heft. +require('../lib-commonjs/start.js'); diff --git a/apps/heft/src/metrics/MetricsCollector.ts b/apps/heft/src/metrics/MetricsCollector.ts index 08ca1ec2aa5..b7f6352387d 100644 --- a/apps/heft/src/metrics/MetricsCollector.ts +++ b/apps/heft/src/metrics/MetricsCollector.ts @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as os from 'os'; +import * as os from 'node:os'; +import { performance } from 'node:perf_hooks'; + import { AsyncParallelHook } from 'tapable'; -import { performance } from 'perf_hooks'; + import { InternalError } from '@rushstack/node-core-library'; /** @@ -21,10 +23,24 @@ export interface IMetricsData { encounteredError?: boolean; /** - * The amount of time the command took to execute, in milliseconds. + * The total execution duration of all user-defined tasks from `heft.json`, in milliseconds. + * This metric is for measuring the cumulative time spent on the underlying build steps for a project. + * If running in watch mode, this will be the duration of the most recent incremental build. */ taskTotalExecutionMs: number; + /** + * The total duration before Heft started executing user-defined tasks, in milliseconds. + * This metric is for tracking the contribution of Heft itself to total build duration. + */ + bootDurationMs: number; + + /** + * How long the process has been alive, in milliseconds. + * This metric is for watch mode, to analyze how long developers leave individual Heft sessions running. + */ + totalUptimeMs: number; + /** * The name of the operating system provided by NodeJS. */ @@ -87,12 +103,17 @@ export class MetricsCollector { public readonly recordMetricsHook: AsyncParallelHook = new AsyncParallelHook(['recordMetricsHookOptions']); + private _bootDurationMs: number | undefined; private _startTimeMs: number | undefined; /** * Start metrics log timer. */ public setStartTime(): void { + if (this._bootDurationMs === undefined) { + // Only set this once. This is for tracking boot overhead. + this._bootDurationMs = process.uptime() * 1000; + } this._startTimeMs = performance.now(); } @@ -108,7 +129,8 @@ export class MetricsCollector { performanceData?: Partial, parameters?: Record ): Promise { - if (this._startTimeMs === undefined) { + const { _bootDurationMs, _startTimeMs } = this; + if (_bootDurationMs === undefined || _startTimeMs === undefined) { throw new InternalError('MetricsCollector has not been initialized with setStartTime() yet'); } @@ -117,18 +139,26 @@ export class MetricsCollector { } const filledPerformanceData: IPerformanceData = { - taskTotalExecutionMs: (performance.now() - this._startTimeMs) / 1000, + taskTotalExecutionMs: performance.now() - _startTimeMs, ...(performanceData || {}) }; + const { taskTotalExecutionMs } = filledPerformanceData; + + const cpus: os.CpuInfo[] = os.cpus(); + const metricData: IMetricsData = { command: command, encounteredError: filledPerformanceData.encounteredError, - taskTotalExecutionMs: filledPerformanceData.taskTotalExecutionMs, + bootDurationMs: _bootDurationMs, + taskTotalExecutionMs: taskTotalExecutionMs, + totalUptimeMs: process.uptime() * 1000, machineOs: process.platform, machineArch: process.arch, - machineCores: os.cpus().length, - machineProcessor: os.cpus()[0].model, + machineCores: cpus.length, + // The Node.js model is sometimes padded, for example: + // "AMD Ryzen 7 3700X 8-Core Processor + machineProcessor: cpus[0].model.trim(), machineTotalMemoryMB: os.totalmem(), commandParameters: parameters || {} }; diff --git a/apps/heft/src/operations/IOperationRunner.ts b/apps/heft/src/operations/IOperationRunner.ts deleted file mode 100644 index b71cde79db8..00000000000 --- a/apps/heft/src/operations/IOperationRunner.ts +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import type { OperationStatus } from './OperationStatus'; -import type { OperationError } from './OperationError'; - -import type { CancellationToken } from '../pluginFramework/CancellationToken'; -import type { Stopwatch } from '../utilities/Stopwatch'; - -/** - * Information passed to the executing `IOperationRunner` - * - * @beta - */ -export interface IOperationRunnerContext { - /** - * A cancellation token for the overarching execution. Runners should do their best to gracefully abort - * as soon as possible if the cancellation token is canceled. - */ - cancellationToken: CancellationToken; - - /** - * If this is the first time this operation has been executed. - */ - isFirstRun: boolean; - - /** - * A callback to the overarching orchestrator to request that the operation be invoked again. - * Used in watch mode to signal that inputs have changed. - */ - requestRun?: () => void; -} - -/** - * - */ -export interface IOperationState { - status: OperationStatus; - error: OperationError | undefined; - stopwatch: Stopwatch; -} - -export interface IOperationStates { - readonly state: Readonly | undefined; - readonly lastState: Readonly | undefined; -} - -/** - * The `Operation` class is a node in the dependency graph of work that needs to be scheduled by the - * `OperationExecutionManager`. Each `Operation` has a `runner` member of type `IOperationRunner`, whose - * implementation manages the actual process for running a single operation. - * - * @beta - */ -export interface IOperationRunner { - /** - * Name of the operation, for logging. - */ - readonly name: string; - - /** - * Indicates that this runner is architectural and should not be reported on. - */ - silent: boolean; - - /** - * Method to be executed for the operation. - */ - executeAsync(context: IOperationRunnerContext): Promise; -} diff --git a/apps/heft/src/operations/Operation.ts b/apps/heft/src/operations/Operation.ts deleted file mode 100644 index e30a678d665..00000000000 --- a/apps/heft/src/operations/Operation.ts +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { InternalError, ITerminal } from '@rushstack/node-core-library'; - -import { Stopwatch } from '../utilities/Stopwatch'; -import type { - IOperationRunner, - IOperationRunnerContext, - IOperationState, - IOperationStates -} from './IOperationRunner'; -import { OperationError } from './OperationError'; -import { OperationStatus } from './OperationStatus'; - -/** - * Options for constructing a new Operation. - * @alpha - */ -export interface IOperationOptions { - /** - * The group that this operation belongs to. Will be used for logging and duration tracking. - */ - groupName?: string | undefined; - - /** - * When the scheduler is ready to process this `Operation`, the `runner` implements the actual work of - * running the operation. - */ - runner?: IOperationRunner | undefined; - - /** - * The weight used by the scheduler to determine order of execution. - */ - weight?: number | undefined; -} - -/** - * Information provided to `executeAsync` by the `OperationExecutionManager`. - */ -export interface IExecuteOperationContext extends Omit { - /** - * Function to invoke before execution of an operation, for logging. - */ - beforeExecute(operation: Operation, state: IOperationState): void; - /** - * Function to invoke after execution of an operation, for logging. - */ - afterExecute(operation: Operation, state: IOperationState): void; - /** - * Function used to schedule the concurrency-limited execution of an operation. - */ - queueWork(workFn: () => Promise, priority: number): Promise; - - /** - * A callback to the overarching orchestrator to request that the operation be invoked again. - * Used in watch mode to signal that inputs have changed. - */ - requestRun?: (requestor?: string) => void; - - /** - * Terminal to write output to. - */ - terminal: ITerminal; -} - -/** - * The `Operation` class is a node in the dependency graph of work that needs to be scheduled by the - * `OperationExecutionManager`. Each `Operation` has a `runner` member of type `IOperationRunner`, whose - * implementation manages the actual process of running a single operation. - * - * The graph of `Operation` instances will be cloned into a separate execution graph after processing. - * - * @alpha - */ -export class Operation implements IOperationStates { - /** - * A set of all dependencies which must be executed before this operation is complete. - */ - public readonly dependencies: Set = new Set(); - /** - * A set of all operations that wait for this operation. - */ - public readonly consumers: Set = new Set(); - /** - * If specified, the name of a grouping to which this Operation belongs, for logging start and end times. - */ - public readonly groupName: string | undefined; - - /** - * When the scheduler is ready to process this `Operation`, the `runner` implements the actual work of - * running the operation. - */ - public runner: IOperationRunner | undefined = undefined; - - /** - * This number represents how far away this Operation is from the furthest "root" operation (i.e. - * an operation with no consumers). This helps us to calculate the critical path (i.e. the - * longest chain of projects which must be executed in order, thereby limiting execution speed - * of the entire operation tree. - * - * This number is calculated via a memoized depth-first search, and when choosing the next - * operation to execute, the operation with the highest criticalPathLength is chosen. - * - * Example: - * (0) A - * \ - * (1) B C (0) (applications) - * \ /|\ - * \ / | \ - * (2) D | X (1) (utilities) - * | / \ - * |/ \ - * (2) Y Z (2) (other utilities) - * - * All roots (A & C) have a criticalPathLength of 0. - * B has a score of 1, since A depends on it. - * D has a score of 2, since we look at the longest chain (e.g D->B->A is longer than D->C) - * X has a score of 1, since the only package which depends on it is A - * Z has a score of 2, since only X depends on it, and X has a score of 1 - * Y has a score of 2, since the chain Y->X->C is longer than Y->C - * - * The algorithm is implemented in AsyncOperationQueue.ts as calculateCriticalPathLength() - */ - public criticalPathLength: number | undefined = undefined; - - /** - * The weight for this operation. This scalar is the contribution of this operation to the - * `criticalPathLength` calculation above. Modify to indicate the following: - * - `weight` === 1: indicates that this operation has an average duration - * - `weight` > 1: indicates that this operation takes longer than average and so the scheduler - * should try to favor starting it over other, shorter operations. An example might be an operation that - * bundles an entire application and runs whole-program optimization. - * - `weight` < 1: indicates that this operation takes less time than average and so the scheduler - * should favor other, longer operations over it. An example might be an operation to unpack a cached - * output, or an operation using NullOperationRunner, which might use a value of 0. - */ - public weight: number; - - /** - * The state of this operation the previous time a manager was invoked. - */ - public lastState: IOperationState | undefined = undefined; - - /** - * The current state of this operation - */ - public state: IOperationState | undefined = undefined; - - /** - * A cached execution promise for the current OperationExecutionManager invocation of this operation. - */ - private _promise: Promise | undefined = undefined; - - /** - * If true, then a run of this operation is currently wanted. - * This is used to track state from the `requestRun` callback passed to the runner. - */ - private _runPending: boolean = true; - - public constructor(options?: IOperationOptions) { - this.groupName = options?.groupName; - this.runner = options?.runner; - this.weight = options?.weight || 1; - } - - /** - * The name of this operation, for logging. - */ - public get name(): string | undefined { - return this.runner?.name; - } - - public addDependency(dependency: Operation): void { - this.dependencies.add(dependency); - dependency.consumers.add(this); - } - - public deleteDependency(dependency: Operation): void { - this.dependencies.delete(dependency); - dependency.consumers.delete(this); - } - - public reset(): void { - // Reset operation state - this.lastState = this.state; - - this.state = { - status: OperationStatus.Ready, - error: undefined, - stopwatch: new Stopwatch() - }; - - this._promise = undefined; - this._runPending = true; - } - - /** - * @internal - */ - public async _executeAsync(context: IExecuteOperationContext): Promise { - const { state } = this; - if (!state) { - throw new Error(`Operation state has not been initialized.`); - } - - if (!this._promise) { - this._promise = this._executeInnerAsync(context, state); - } - - return this._promise; - } - - private async _executeInnerAsync( - context: IExecuteOperationContext, - rawState: IOperationState - ): Promise { - const state: IOperationState = rawState; - const { runner } = this; - - const dependencyResults: PromiseSettledResult[] = await Promise.allSettled( - Array.from(this.dependencies, (dependency: Operation) => dependency._executeAsync(context)) - ); - - const { cancellationToken, requestRun, queueWork } = context; - - if (cancellationToken.isCancelled) { - state.status = OperationStatus.Cancelled; - return state.status; - } - - for (const result of dependencyResults) { - if ( - result.status === 'rejected' || - result.value === OperationStatus.Blocked || - result.value === OperationStatus.Failure - ) { - state.status = OperationStatus.Blocked; - return state.status; - } - } - - const innerContext: IOperationRunnerContext = { - cancellationToken, - isFirstRun: !this.lastState, - requestRun: requestRun - ? () => { - switch (this.state?.status) { - case OperationStatus.Ready: - case OperationStatus.Executing: - // If current status has not yet resolved to a fixed value, - // re-executing this operation does not require a full rerun - // of the operation graph. Simply mark that a run is requested. - - // This variable is on the Operation instead of the - // containing closure to deal with scenarios in which - // the runner hangs on to an old copy of the callback. - this._runPending = true; - return; - - case OperationStatus.Blocked: - case OperationStatus.Cancelled: - case OperationStatus.Failure: - case OperationStatus.NoOp: - case OperationStatus.Success: - // The requestRun callback is assumed to remain constant - // throughout the lifetime of the process, so it is safe - // to capture here. - return requestRun(this.name); - default: - throw new InternalError(`Unexpected status: ${this.state?.status}`); - } - } - : undefined - }; - - await queueWork(async () => { - // Redundant variable to satisfy require-atomic-updates - const innerState: IOperationState = state; - - if (cancellationToken.isCancelled) { - innerState.status = OperationStatus.Cancelled; - return innerState.status; - } - - context.beforeExecute(this, innerState); - - innerState.status = OperationStatus.Executing; - innerState.stopwatch.start(); - - while (this._runPending) { - this._runPending = false; - try { - innerState.status = runner ? await runner.executeAsync(innerContext) : OperationStatus.NoOp; - } catch (error) { - innerState.status = OperationStatus.Failure; - innerState.error = error as OperationError; - } - - // Since runner.executeAsync is async, a change could have occurred that requires re-execution - // This operation is still active, so can re-execute immediately, rather than forcing a whole - // new execution pass. - - // As currently written, this does mean that if a job is scheduled with higher priority while - // this operation is still executing, it will still wait for this retry. This may not be desired - // and if it becomes a problem, the retry loop will need to be moved outside of the `queueWork` call. - // This introduces complexity regarding tracking of timing and start/end logging, however. - - if (this._runPending) { - if (cancellationToken.isCancelled) { - innerState.status = OperationStatus.Cancelled; - break; - } else { - context.terminal.writeLine(`Immediate rerun requested. Executing.`); - } - } - } - - state.stopwatch.stop(); - context.afterExecute(this, state); - }, this.criticalPathLength ?? 0); - - return state.status; - } -} diff --git a/apps/heft/src/operations/OperationExecutionManager.ts b/apps/heft/src/operations/OperationExecutionManager.ts deleted file mode 100644 index 5d6b28bb3c7..00000000000 --- a/apps/heft/src/operations/OperationExecutionManager.ts +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import type { ITerminal } from '@rushstack/node-core-library'; - -import { OperationStatus } from './OperationStatus'; -import type { Operation, IExecuteOperationContext } from './Operation'; -import { OperationGroupRecord } from './OperationGroupRecord'; -import { CancellationToken } from '../pluginFramework/CancellationToken'; -import { calculateCriticalPathLengths } from './calculateCriticalPath'; -import type { IOperationState } from './IOperationRunner'; - -export interface IOperationExecutionOptions { - cancellationToken: CancellationToken; - parallelism: number; - terminal: ITerminal; - - requestRun?: (requestor?: string) => void; -} - -/** - * A class which manages the execution of a set of tasks with interdependencies. - * Initially, and at the end of each task execution, all unblocked tasks - * are added to a ready queue which is then executed. This is done continually until all - * tasks are complete, or prematurely fails if any of the tasks fail. - */ -export class OperationExecutionManager { - /** - * The set of operations that will be executed - */ - private readonly _operations: Operation[]; - /** - * Group records are metadata-only entities used for tracking the start and end of a set of related tasks. - * This is the only extent to which the operation graph is aware of Heft phases. - */ - private readonly _groupRecordByName: Map; - /** - * The total number of non-silent operations in the graph. - * Silent operations are generally used to simplify the construction of the graph. - */ - private readonly _trackedOperationCount: number; - - public constructor(operations: ReadonlySet) { - const groupRecordByName: Map = new Map(); - this._groupRecordByName = groupRecordByName; - - let trackedOperationCount: number = 0; - for (const operation of operations) { - const { groupName } = operation; - let group: OperationGroupRecord | undefined = undefined; - if (groupName && !(group = groupRecordByName.get(groupName))) { - group = new OperationGroupRecord(groupName); - groupRecordByName.set(groupName, group); - } - - group?.addOperation(operation); - - if (!operation.runner?.silent) { - // Only count non-silent operations - trackedOperationCount++; - } - } - - this._trackedOperationCount = trackedOperationCount; - - this._operations = calculateCriticalPathLengths(operations); - - for (const consumer of operations) { - for (const dependency of consumer.dependencies) { - if (!operations.has(dependency)) { - throw new Error( - `Operation ${JSON.stringify(consumer.name)} declares a dependency on operation ` + - `${JSON.stringify(dependency.name)} that is not in the set of operations to execute.` - ); - } - } - } - } - - /** - * Executes all operations which have been registered, returning a promise which is resolved when all the - * operations are completed successfully, or rejects when any operation fails. - */ - public async executeAsync(executionOptions: IOperationExecutionOptions): Promise { - let hasReportedFailures: boolean = false; - - const { cancellationToken, parallelism, terminal, requestRun } = executionOptions; - - const startedGroups: Set = new Set(); - const finishedGroups: Set = new Set(); - - const maxParallelism: number = Math.min(this._operations.length, parallelism); - const groupRecords: Map = this._groupRecordByName; - for (const groupRecord of groupRecords.values()) { - groupRecord.reset(); - } - - for (const operation of this._operations) { - operation.reset(); - } - - terminal.writeVerboseLine(`Executing a maximum of ${maxParallelism} simultaneous tasks...`); - - const executionContext: IExecuteOperationContext = { - terminal, - cancellationToken, - - requestRun, - - queueWork: (workFn: () => Promise, priority: number): Promise => { - // TODO: Update to throttle parallelism - // Can just be a standard priority queue from async - return workFn(); - }, - - beforeExecute: (operation: Operation): void => { - // Initialize group if uninitialized and log the group name - const { groupName } = operation; - const groupRecord: OperationGroupRecord | undefined = groupName - ? groupRecords.get(groupName) - : undefined; - if (groupRecord && !startedGroups.has(groupRecord)) { - startedGroups.add(groupRecord); - groupRecord.startTimer(); - terminal.writeLine(` ---- ${groupRecord.name} started ---- `); - } - }, - - afterExecute: (operation: Operation, state: IOperationState): void => { - const { groupName } = operation; - const groupRecord: OperationGroupRecord | undefined = groupName - ? groupRecords.get(groupName) - : undefined; - if (groupRecord) { - groupRecord.setOperationAsComplete(operation, state); - } - - if (state.status === OperationStatus.Failure) { - // This operation failed. Mark it as such and all reachable dependents as blocked. - // Failed operations get reported, even if silent. - // Generally speaking, silent operations shouldn't be able to fail, so this is a safety measure. - const message: string | undefined = state.error?.message; - if (message) { - terminal.writeErrorLine(message); - } - hasReportedFailures = true; - } - - // Log out the group name and duration if it is the last operation in the group - if (groupRecord?.finished && !finishedGroups.has(groupRecord)) { - finishedGroups.add(groupRecord); - const finishedLoggingWord: string = groupRecord.hasFailures - ? 'encountered an error' - : groupRecord.hasCancellations - ? 'cancelled' - : 'finished'; - terminal.writeLine( - ` ---- ${groupRecord.name} ${finishedLoggingWord} (${groupRecord.duration.toFixed(3)}s) ---- ` - ); - } - } - }; - - await Promise.all(this._operations.map((record: Operation) => record._executeAsync(executionContext))); - - const finalStatus: OperationStatus = - this._trackedOperationCount === 0 - ? OperationStatus.NoOp - : cancellationToken.isCancelled - ? OperationStatus.Cancelled - : hasReportedFailures - ? OperationStatus.Failure - : OperationStatus.Success; - - return finalStatus; - } -} diff --git a/apps/heft/src/operations/runners/LifecycleOperationRunner.ts b/apps/heft/src/operations/runners/LifecycleOperationRunner.ts deleted file mode 100644 index dddd694e913..00000000000 --- a/apps/heft/src/operations/runners/LifecycleOperationRunner.ts +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { performance } from 'perf_hooks'; -import { AlreadyReportedError, InternalError } from '@rushstack/node-core-library'; - -import { deleteFilesAsync } from '../../plugins/DeleteFilesPlugin'; -import { OperationStatus } from '../OperationStatus'; -import type { IOperationRunner, IOperationRunnerContext } from '../IOperationRunner'; -import type { InternalHeftSession } from '../../pluginFramework/InternalHeftSession'; -import type { ScopedLogger } from '../../pluginFramework/logging/ScopedLogger'; -import type { HeftLifecycle } from '../../pluginFramework/HeftLifecycle'; -import type { IDeleteOperation } from '../../plugins/DeleteFilesPlugin'; -import type { - IHeftLifecycleCleanHookOptions, - IHeftLifecycleToolStartHookOptions, - IHeftLifecycleToolFinishHookOptions, - IHeftLifecycleSession -} from '../../pluginFramework/HeftLifecycleSession'; - -export type LifecycleOperationRunnerType = 'start' | 'finish'; - -export interface ILifecycleOperationRunnerOptions { - internalHeftSession: InternalHeftSession; - type: LifecycleOperationRunnerType; -} - -export class LifecycleOperationRunner implements IOperationRunner { - private readonly _options: ILifecycleOperationRunnerOptions; - - public readonly silent: boolean = true; - - public get name(): string { - return `Lifecycle ${JSON.stringify(this._options.type)}`; - } - - public constructor(options: ILifecycleOperationRunnerOptions) { - this._options = options; - } - - public async executeAsync(context: IOperationRunnerContext): Promise { - const { internalHeftSession, type } = this._options; - const { clean, cleanCache, watch } = internalHeftSession.parameterManager.defaultParameters; - - if (watch) { - // Avoid running the lifecycle operation when in watch mode - return OperationStatus.NoOp; - } - - const lifecycle: HeftLifecycle = internalHeftSession.lifecycle; - const lifecycleLogger: ScopedLogger = internalHeftSession.loggingManager.requestScopedLogger( - `lifecycle:${this._options.type}` - ); - - switch (type) { - case 'start': { - // We can only apply the plugins once, so only do it during the start operation - lifecycleLogger.terminal.writeVerboseLine('Applying lifecycle plugins'); - await lifecycle.applyPluginsAsync(); - - // Run the clean hook - if (clean) { - const startTime: number = performance.now(); - const cleanLogger: ScopedLogger = - internalHeftSession.loggingManager.requestScopedLogger(`lifecycle:clean`); - cleanLogger.terminal.writeVerboseLine('Starting clean'); - - // Grab the additional clean operations from the phase - const deleteOperations: IDeleteOperation[] = []; - - // Delete all temp folders for tasks by default - for (const pluginDefinition of lifecycle.pluginDefinitions) { - const lifecycleSession: IHeftLifecycleSession = - await lifecycle.getSessionForPluginDefinitionAsync(pluginDefinition); - deleteOperations.push({ sourcePath: lifecycleSession.tempFolderPath }); - - // Also delete the cache folder if requested - if (cleanCache) { - deleteOperations.push({ sourcePath: lifecycleSession.cacheFolderPath }); - } - } - - // Create the options and provide a utility method to obtain paths to delete - const cleanHookOptions: IHeftLifecycleCleanHookOptions = { - addDeleteOperations: (...deleteOperationsToAdd: IDeleteOperation[]) => - deleteOperations.push(...deleteOperationsToAdd) - }; - - // Run the plugin clean hook - if (lifecycle.hooks.clean.isUsed()) { - try { - await lifecycle.hooks.clean.promise(cleanHookOptions); - } catch (e: unknown) { - // Log out using the clean logger, and return an error status - if (!(e instanceof AlreadyReportedError)) { - cleanLogger.emitError(e as Error); - } - return OperationStatus.Failure; - } - } - - // Delete the files if any were specified - if (deleteOperations.length) { - await deleteFilesAsync(deleteOperations, cleanLogger.terminal); - } - - cleanLogger.terminal.writeVerboseLine(`Finished clean (${performance.now() - startTime}ms)`); - } - - // Run the start hook - if (lifecycle.hooks.toolStart.isUsed()) { - const lifecycleToolStartHookOptions: IHeftLifecycleToolStartHookOptions = {}; - await lifecycle.hooks.toolStart.promise(lifecycleToolStartHookOptions); - } - break; - } - case 'finish': { - if (lifecycle.hooks.toolFinish.isUsed()) { - const lifeycleToolFinishHookOptions: IHeftLifecycleToolFinishHookOptions = {}; - await lifecycle.hooks.toolFinish.promise(lifeycleToolFinishHookOptions); - } - break; - } - default: { - // Should never happen, but just in case - throw new InternalError(`Unrecognized lifecycle type: ${this._options.type}`); - } - } - - // Return success and allow for the TaskOperationRunner to execute tasks - return OperationStatus.Success; - } -} diff --git a/apps/heft/src/operations/runners/PhaseOperationRunner.ts b/apps/heft/src/operations/runners/PhaseOperationRunner.ts index c1f13c16011..f6f1ba3ed0d 100644 --- a/apps/heft/src/operations/runners/PhaseOperationRunner.ts +++ b/apps/heft/src/operations/runners/PhaseOperationRunner.ts @@ -1,14 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { performance } from 'perf_hooks'; +import { + type IOperationRunner, + type IOperationRunnerContext, + OperationStatus +} from '@rushstack/operation-graph'; -import { OperationStatus } from '../OperationStatus'; import { deleteFilesAsync, type IDeleteOperation } from '../../plugins/DeleteFilesPlugin'; -import type { IOperationRunner, IOperationRunnerContext } from '../IOperationRunner'; import type { HeftPhase } from '../../pluginFramework/HeftPhase'; import type { HeftPhaseSession } from '../../pluginFramework/HeftPhaseSession'; -import type { HeftTaskSession } from '../../pluginFramework/HeftTaskSession'; import type { InternalHeftSession } from '../../pluginFramework/InternalHeftSession'; export interface IPhaseOperationRunnerOptions { @@ -20,6 +21,7 @@ export class PhaseOperationRunner implements IOperationRunner { public readonly silent: boolean = true; private readonly _options: IPhaseOperationRunnerOptions; + private _isClean: boolean = false; public get name(): string { return `Phase ${JSON.stringify(this._options.phase.phaseName)}`; @@ -31,46 +33,45 @@ export class PhaseOperationRunner implements IOperationRunner { public async executeAsync(context: IOperationRunnerContext): Promise { const { internalHeftSession, phase } = this._options; - const { clean, cleanCache, watch } = internalHeftSession.parameterManager.defaultParameters; + const { clean } = internalHeftSession.parameterManager.defaultParameters; // Load and apply the plugins for this phase only const phaseSession: HeftPhaseSession = internalHeftSession.getSessionForPhase(phase); const { phaseLogger, cleanLogger } = phaseSession; - phaseLogger.terminal.writeVerboseLine('Applying task plugins'); - await phaseSession.applyPluginsAsync(); + await phaseSession.applyPluginsAsync(phaseLogger.terminal); - if (watch) { - // Avoid running the phase operation when in watch mode + if (this._isClean || !clean) { return OperationStatus.NoOp; } // Run the clean hook - if (clean) { - const startTime: number = performance.now(); - - // Grab the additional clean operations from the phase - cleanLogger.terminal.writeVerboseLine('Starting clean'); - const deleteOperations: IDeleteOperation[] = Array.from(phase.cleanFiles); - - // Delete all temp folders for tasks by default - for (const task of phase.tasks) { - const taskSession: HeftTaskSession = phaseSession.getSessionForTask(task); - deleteOperations.push({ sourcePath: taskSession.tempFolderPath }); - - // Also delete the cache folder if requested - if (cleanCache) { - deleteOperations.push({ sourcePath: taskSession.cacheFolderPath }); - } - } - - // Delete the files if any were specified - if (deleteOperations.length) { - await deleteFilesAsync(deleteOperations, cleanLogger.terminal); - } - - cleanLogger.terminal.writeVerboseLine(`Finished clean (${performance.now() - startTime}ms)`); + const startTime: number = performance.now(); + + // Grab the additional clean operations from the phase + cleanLogger.terminal.writeVerboseLine('Starting clean'); + const deleteOperations: IDeleteOperation[] = Array.from(phase.cleanFiles); + + // Delete all temp folders for tasks by default + const tempFolderGlobs: string[] = [ + /* heft@>0.60.0 */ phase.phaseName, + /* heft@<=0.60.0 */ `${phase.phaseName}.*` + ]; + deleteOperations.push({ + sourcePath: internalHeftSession.heftConfiguration.tempFolderPath, + includeGlobs: tempFolderGlobs + }); + + // Delete the files if any were specified + if (deleteOperations.length) { + const rootFolderPath: string = internalHeftSession.heftConfiguration.buildFolderPath; + await deleteFilesAsync(rootFolderPath, deleteOperations, cleanLogger.terminal); } + // Ensure we only run the clean operation once + this._isClean = true; + + cleanLogger.terminal.writeVerboseLine(`Finished clean (${performance.now() - startTime}ms)`); + // Return success and allow for the TaskOperationRunner to execute tasks return OperationStatus.Success; } diff --git a/apps/heft/src/operations/runners/TaskOperationRunner.ts b/apps/heft/src/operations/runners/TaskOperationRunner.ts index bd9e787db81..5cdd397d9b4 100644 --- a/apps/heft/src/operations/runners/TaskOperationRunner.ts +++ b/apps/heft/src/operations/runners/TaskOperationRunner.ts @@ -1,15 +1,25 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { performance } from 'perf_hooks'; +import { createHash, type Hash } from 'node:crypto'; +import { glob } from 'fast-glob'; + +import { + type IOperationRunner, + type IOperationRunnerContext, + OperationStatus +} from '@rushstack/operation-graph'; import { AlreadyReportedError, InternalError } from '@rushstack/node-core-library'; -import { OperationStatus } from '../OperationStatus'; -import { HeftTask } from '../../pluginFramework/HeftTask'; -import { copyFilesAsync } from '../../plugins/CopyFilesPlugin'; +import type { HeftTask } from '../../pluginFramework/HeftTask'; +import { + copyFilesAsync, + type ICopyOperation, + asAbsoluteCopyOperation, + asRelativeCopyOperation +} from '../../plugins/CopyFilesPlugin'; import { deleteFilesAsync } from '../../plugins/DeleteFilesPlugin'; -import type { IOperationRunner, IOperationRunnerContext } from '../IOperationRunner'; import type { HeftTaskSession, IHeftTaskFileOperations, @@ -18,12 +28,12 @@ import type { } from '../../pluginFramework/HeftTaskSession'; import type { HeftPhaseSession } from '../../pluginFramework/HeftPhaseSession'; import type { InternalHeftSession } from '../../pluginFramework/InternalHeftSession'; +import { watchGlobAsync, type IGlobOptions } from '../../plugins/FileGlobSpecifier'; import { - type IGlobOptions, - normalizeFileSelectionSpecifier, - watchGlobAsync -} from '../../plugins/FileGlobSpecifier'; -import { type IWatchedFileState, WatchFileSystemAdapter } from '../../utilities/WatchFileSystemAdapter'; + type IWatchedFileState, + type IWatchFileSystem, + WatchFileSystemAdapter +} from '../../utilities/WatchFileSystemAdapter'; export interface ITaskOperationRunnerOptions { internalHeftSession: InternalHeftSession; @@ -53,6 +63,7 @@ export class TaskOperationRunner implements IOperationRunner { private readonly _options: ITaskOperationRunnerOptions; private _fileOperations: IHeftTaskFileOperations | undefined = undefined; + private _copyConfigHash: string | undefined; private _watchFileSystemAdapter: WatchFileSystemAdapter | undefined = undefined; public readonly silent: boolean = false; @@ -78,20 +89,21 @@ export class TaskOperationRunner implements IOperationRunner { context: IOperationRunnerContext, taskSession: HeftTaskSession ): Promise { - const { cancellationToken, requestRun } = context; + const { abortSignal, requestRun } = context; const { hooks, logger } = taskSession; // Need to clear any errors or warnings from the previous invocation, particularly // if this is an immediate rerun logger.resetErrorsAndWarnings(); + const rootFolderPath: string = this._options.internalHeftSession.heftConfiguration.buildFolderPath; const isWatchMode: boolean = taskSession.parameters.watch && !!requestRun; const { terminal } = logger; // Exit the task early if cancellation is requested - if (cancellationToken.isCancelled) { - return OperationStatus.Cancelled; + if (abortSignal.aborted) { + return OperationStatus.Aborted; } if (!this._fileOperations && hooks.registerFileOperations.isUsed()) { @@ -100,12 +112,31 @@ export class TaskOperationRunner implements IOperationRunner { deleteOperations: new Set() }); - for (const copyOperation of fileOperations.copyOperations) { - // Consolidate fileExtensions, includeGlobs, excludeGlobs - normalizeFileSelectionSpecifier(copyOperation); + let copyConfigHash: string | undefined; + const { copyOperations } = fileOperations; + if (copyOperations.size > 0) { + // Do this here so that we only need to do it once for each Heft invocation + const hasher: Hash | undefined = createHash('sha256'); + const absolutePathCopyOperations: Set = new Set(); + for (const copyOperation of fileOperations.copyOperations) { + // The paths in the `fileOperations` object may be either absolute or relative + // For execution we need absolute paths. + const absoluteOperation: ICopyOperation = asAbsoluteCopyOperation(rootFolderPath, copyOperation); + absolutePathCopyOperations.add(absoluteOperation); + + // For portability of the hash we need relative paths. + const portableCopyOperation: ICopyOperation = asRelativeCopyOperation( + rootFolderPath, + absoluteOperation + ); + hasher.update(JSON.stringify(portableCopyOperation)); + } + fileOperations.copyOperations = absolutePathCopyOperations; + copyConfigHash = hasher.digest('base64'); } this._fileOperations = fileOperations; + this._copyConfigHash = copyConfigHash; } const shouldRunIncremental: boolean = isWatchMode && hooks.runIncremental.isUsed(); @@ -129,7 +160,8 @@ export class TaskOperationRunner implements IOperationRunner { async (): Promise => { // Create the options and provide a utility method to obtain paths to copy const runHookOptions: IHeftTaskRunHookOptions = { - cancellationToken + abortSignal, + globAsync: glob }; // Run the plugin run hook @@ -146,6 +178,9 @@ export class TaskOperationRunner implements IOperationRunner { fs: getWatchFileSystemAdapter() }); }, + get watchFs(): IWatchFileSystem { + return getWatchFileSystemAdapter(); + }, requestRun: requestRun! }; await hooks.runIncremental.promise(runIncrementalHookOptions); @@ -160,15 +195,15 @@ export class TaskOperationRunner implements IOperationRunner { return OperationStatus.Failure; } - if (cancellationToken.isCancelled) { - return OperationStatus.Cancelled; + if (abortSignal.aborted) { + return OperationStatus.Aborted; } return OperationStatus.Success; }, () => `Starting ${shouldRunIncremental ? 'incremental ' : ''}task execution`, () => { - const finishedWord: string = cancellationToken.isCancelled ? 'Cancelled' : 'Finished'; + const finishedWord: string = abortSignal.aborted ? 'Aborted' : 'Finished'; return `${finishedWord} ${shouldRunIncremental ? 'incremental ' : ''}task execution`; }, terminal.writeVerboseLine.bind(terminal) @@ -178,16 +213,21 @@ export class TaskOperationRunner implements IOperationRunner { if (this._fileOperations) { const { copyOperations, deleteOperations } = this._fileOperations; + const copyConfigHash: string | undefined = this._copyConfigHash; await Promise.all([ - copyOperations.size > 0 + copyConfigHash ? copyFilesAsync( copyOperations, logger.terminal, + `${taskSession.tempFolderPath}/file-copy.json`, + copyConfigHash, isWatchMode ? getWatchFileSystemAdapter() : undefined ) : Promise.resolve(), - deleteOperations.size > 0 ? deleteFilesAsync(deleteOperations, logger.terminal) : Promise.resolve() + deleteOperations.size > 0 + ? deleteFilesAsync(rootFolderPath, deleteOperations, logger.terminal) + : Promise.resolve() ]); } @@ -200,8 +240,8 @@ export class TaskOperationRunner implements IOperationRunner { // Even if the entire process has completed, we should mark the operation as cancelled if // cancellation has been requested. - if (cancellationToken.isCancelled) { - return OperationStatus.Cancelled; + if (abortSignal.aborted) { + return OperationStatus.Aborted; } if (logger.hasErrors) { diff --git a/apps/heft/src/pluginFramework/CancellationToken.ts b/apps/heft/src/pluginFramework/CancellationToken.ts deleted file mode 100644 index 8e86151312a..00000000000 --- a/apps/heft/src/pluginFramework/CancellationToken.ts +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { InternalError } from '@rushstack/node-core-library'; - -/** - * Options for the cancellation token. - * - * @internal - */ -export interface ICancellationTokenOptions { - /** - * A cancellation token source to use for the token. - * - * @internal - */ - cancellationTokenSource?: CancellationTokenSource; - - /** - * A static cancellation state. Mutually exclusive with `cancellationTokenSource`. - * If true, CancellationToken.isCancelled will always return true. Otherwise, - * CancellationToken.isCancelled will always return false. - * - * @internal - */ - isCancelled?: boolean; -} - -/** - * Options for the cancellation token source. - * - * @beta - */ -export interface ICancellationTokenSourceOptions { - /** - * Amount of time in milliseconds to wait before cancelling the token. - */ - delayMs?: number; -} - -/** - * A cancellation token. Can be used to signal that an ongoing process has either been cancelled - * or timed out. - * - * @remarks This class will eventually be removed once the `AbortSignal` API is available in - * the lowest supported LTS version of Node.js. See here for more information: - * https://nodejs.org/docs/latest-v16.x/api/globals.html#class-abortsignal - * - * @beta - */ -export class CancellationToken { - private readonly _isCancelled: boolean | undefined; - private readonly _cancellationTokenSource: CancellationTokenSource | undefined; - - /** @internal */ - public constructor(options: ICancellationTokenOptions = {}) { - if (options.cancellationTokenSource && options.isCancelled !== undefined) { - throw new InternalError( - 'CancellationTokenOptions.cancellationTokenSource and CancellationTokenOptions.isCancelled ' + - 'are mutually exclusive. Specify only one.' - ); - } - - this._cancellationTokenSource = options.cancellationTokenSource; - this._isCancelled = options.isCancelled; - } - - /** - * {@inheritdoc CancellationTokenSource.isCancelled} - */ - public get isCancelled(): boolean { - // Returns the cancellation state if it's explicitly set, otherwise returns the cancellation - // state from the source. If that too is not provided, the token is not cancellable. - return this._isCancelled ?? this._cancellationTokenSource?.isCancelled ?? false; - } - - /** - * Obtain a promise that resolves when the token is cancelled. - */ - public get onCancelledPromise(): Promise { - if (this._isCancelled !== undefined) { - // If the token is explicitly set to cancelled, return a resolved promise. - // If the token is explicitly set to not cancelled, return a promise that never resolves. - return this._isCancelled ? Promise.resolve() : new Promise(() => {}); - } else if (this._cancellationTokenSource) { - // Return the promise sourced from the cancellation token source - return this._cancellationTokenSource._onCancelledPromise; - } else { - // Neither provided, token can never be cancelled. Return a promise that never resovles. - return new Promise(() => {}); - } - } -} - -/** - * A cancellation token source. Produces cancellation tokens that can be used to signal that - * an ongoing process has either been cancelled or timed out. - * - * @remarks This class will eventually be removed once the `AbortController` API is available - * in the lowest supported LTS version of Node.js. See here for more information: - * https://nodejs.org/docs/latest-v16.x/api/globals.html#class-abortcontroller - * - * @beta - */ -export class CancellationTokenSource { - private readonly _cancellationToken: CancellationToken; - private readonly _cancellationPromise: Promise; - private _resolveCancellationPromise!: () => void; - private _isCancelled: boolean = false; - - public constructor(options: ICancellationTokenSourceOptions = {}) { - const { delayMs } = options; - this._cancellationToken = new CancellationToken({ cancellationTokenSource: this }); - this._cancellationPromise = new Promise((resolve) => { - this._resolveCancellationPromise = resolve; - }); - if (delayMs !== undefined) { - setTimeout(() => this.cancel(), delayMs); - } - } - - /** - * Whether or not the token has been cancelled. - */ - public get isCancelled(): boolean { - return this._isCancelled; - } - - /** - * Obtain the cancellation token produced by this source. - */ - public get token(): CancellationToken { - return this._cancellationToken; - } - - /** @internal */ - public get _onCancelledPromise(): Promise { - return this._cancellationPromise; - } - - /** - * Cancel the token provided by the source. - */ - public cancel(): void { - this._isCancelled = true; - this._resolveCancellationPromise(); - } -} diff --git a/apps/heft/src/pluginFramework/HeftLifecycle.ts b/apps/heft/src/pluginFramework/HeftLifecycle.ts index 1bdffc4f083..b762e23f238 100644 --- a/apps/heft/src/pluginFramework/HeftLifecycle.ts +++ b/apps/heft/src/pluginFramework/HeftLifecycle.ts @@ -1,14 +1,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { AsyncParallelHook } from 'tapable'; +import { AsyncParallelHook, SyncHook } from 'tapable'; + import { InternalError } from '@rushstack/node-core-library'; import { HeftPluginConfiguration } from '../configuration/HeftPluginConfiguration'; import { HeftPluginHost } from './HeftPluginHost'; import type { InternalHeftSession } from './InternalHeftSession'; import type { IHeftConfigurationJsonPluginSpecifier } from '../utilities/CoreConfigFiles'; -import type { HeftLifecyclePluginDefinition } from '../configuration/HeftPluginDefinition'; +import type { + HeftLifecyclePluginDefinition, + HeftPluginDefinitionBase +} from '../configuration/HeftPluginDefinition'; import type { IHeftLifecyclePlugin, IHeftPlugin } from './IHeftPlugin'; import { HeftLifecycleSession, @@ -16,8 +20,13 @@ import { type IHeftLifecycleHooks, type IHeftLifecycleToolStartHookOptions, type IHeftLifecycleToolFinishHookOptions, - type IHeftLifecycleSession + type IHeftLifecycleSession, + type IHeftTaskStartHookOptions, + type IHeftTaskFinishHookOptions, + type IHeftPhaseStartHookOptions, + type IHeftPhaseFinishHookOptions } from './HeftLifecycleSession'; +import type { ScopedLogger } from './logging/ScopedLogger'; export interface IHeftLifecycleContext { lifecycleSession?: HeftLifecycleSession; @@ -34,6 +43,7 @@ export class HeftLifecycle extends HeftPluginHost { HeftLifecyclePluginDefinition, IHeftLifecyclePlugin > = new Map(); + private _lifecycleLogger: ScopedLogger | undefined; private _isInitialized: boolean = false; @@ -62,7 +72,11 @@ export class HeftLifecycle extends HeftPluginHost { clean: new AsyncParallelHook(), toolStart: new AsyncParallelHook(), toolFinish: new AsyncParallelHook(), - recordMetrics: internalHeftSession.metricsCollector.recordMetricsHook + recordMetrics: internalHeftSession.metricsCollector.recordMetricsHook, + taskStart: new SyncHook(['task']), + taskFinish: new SyncHook(['task']), + phaseStart: new SyncHook(['phase']), + phaseFinish: new SyncHook(['phase']) }; } @@ -144,11 +158,12 @@ export class HeftLifecycle extends HeftPluginHost { let pluginConfigurationIndex: number = 0; for (const pluginSpecifier of this._lifecyclePluginSpecifiers) { const pluginConfiguration: HeftPluginConfiguration = pluginConfigurations[pluginConfigurationIndex++]; - const pluginDefinition: HeftLifecyclePluginDefinition = + const pluginDefinition: HeftPluginDefinitionBase = pluginConfiguration.getPluginDefinitionBySpecifier(pluginSpecifier); // Ensure the plugin is a lifecycle plugin - if (!pluginConfiguration.lifecyclePluginDefinitions.has(pluginDefinition)) { + const isLifecyclePlugin: boolean = pluginConfiguration.isLifecyclePluginDefinition(pluginDefinition); + if (!isLifecyclePlugin) { throw new Error( `Plugin ${JSON.stringify(pluginDefinition.pluginName)} from package ` + `${JSON.stringify(pluginSpecifier.pluginPackage)} is not a lifecycle plugin.` @@ -174,6 +189,15 @@ export class HeftLifecycle extends HeftPluginHost { } } + public get lifecycleLogger(): ScopedLogger { + let logger: ScopedLogger | undefined = this._lifecycleLogger; + if (!logger) { + logger = this._internalHeftSession.loggingManager.requestScopedLogger(`lifecycle`); + this._lifecycleLogger = logger; + } + return logger; + } + public async getSessionForPluginDefinitionAsync( pluginDefinition: HeftLifecyclePluginDefinition ): Promise { diff --git a/apps/heft/src/pluginFramework/HeftLifecycleSession.ts b/apps/heft/src/pluginFramework/HeftLifecycleSession.ts index 0c481b556d4..92d30a13fac 100644 --- a/apps/heft/src/pluginFramework/HeftLifecycleSession.ts +++ b/apps/heft/src/pluginFramework/HeftLifecycleSession.ts @@ -1,8 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import type { AsyncParallelHook } from 'tapable'; +import * as path from 'node:path'; + +import type { AsyncParallelHook, SyncHook } from 'tapable'; + +import type { Operation, OperationGroupRecord } from '@rushstack/operation-graph'; import type { IHeftRecordMetricsHookOptions, MetricsCollector } from '../metrics/MetricsCollector'; import type { ScopedLogger, IScopedLogger } from './logging/ScopedLogger'; @@ -11,6 +14,7 @@ import type { IHeftParameters } from './HeftParameterManager'; import type { IDeleteOperation } from '../plugins/DeleteFilesPlugin'; import type { HeftPluginDefinitionBase } from '../configuration/HeftPluginDefinition'; import type { HeftPluginHost } from './HeftPluginHost'; +import type { IHeftPhaseOperationMetadata, IHeftTaskOperationMetadata } from '../cli/HeftActionRunner'; /** * The lifecycle session is responsible for providing session-specific information to Heft lifecycle @@ -36,15 +40,6 @@ export interface IHeftLifecycleSession { */ readonly parameters: IHeftParameters; - /** - * The cache folder for the lifecycle plugin. This folder is unique for each lifecycle plugin, - * and will not be cleaned when Heft is run with `--clean`. However, it will be cleaned when - * Heft is run with `--clean` and `--clean-cache`. - * - * @public - */ - readonly cacheFolderPath: string; - /** * The temp folder for the lifecycle plugin. This folder is unique for each lifecycle plugin, * and will be cleaned when Heft is run with `--clean`. @@ -55,7 +50,7 @@ export interface IHeftLifecycleSession { /** * The scoped logger for the lifecycle plugin. Messages logged with this logger will be prefixed - * with the plugin name, in the format "[lifecycle:]". It is highly recommended that + * with the plugin name, in the format `[lifecycle:]`. It is highly recommended that * writing to the console be performed via the logger, as it will ensure that logging messages * are labeled with the source of the message. * @@ -76,6 +71,34 @@ export interface IHeftLifecycleSession { ): void; } +/** + * @public + */ +export interface IHeftTaskStartHookOptions { + operation: Operation; +} + +/** + * @public + */ +export interface IHeftTaskFinishHookOptions { + operation: Operation; +} + +/** + * @public + */ +export interface IHeftPhaseStartHookOptions { + operation: OperationGroupRecord; +} + +/** + * @public + */ +export interface IHeftPhaseFinishHookOptions { + operation: OperationGroupRecord; +} + /** * Hooks that are available to the lifecycle plugin. * @@ -102,15 +125,56 @@ export interface IHeftLifecycleHooks { /** * The `toolFinish` hook is called at the end of Heft execution. It is called after all phases have - * completed execution. To use it, call + * completed execution. Plugins that tap this hook are resposible for handling the scenario in which + * `toolStart` threw an error, since this hook is used to clean up any resources allocated earlier + * in the lifecycle and therefore runs even in error conditions. To use it, call * `toolFinish.tapPromise(, )`. * * @public */ toolFinish: AsyncParallelHook; - // TODO: Wire up and document this hook. + /** + * The `recordMetrics` hook is called at the end of every Heft execution pass. It is called after all + * phases have completed execution (or been canceled). In a watch run, it will be called several times + * in between `toolStart` and (if the session is gracefully interrupted via Ctrl+C), `toolFinish`. + * In a non-watch run, it will be invoked exactly once between `toolStart` and `toolFinish`. + * To use it, call `recordMetrics.tapPromise(, )`. + * @public + */ recordMetrics: AsyncParallelHook; + + /** + * The `taskStart` hook is called at the beginning of a task. It is called before the task has begun + * to execute. To use it, call `taskStart.tap(, )`. + * + * @public + */ + taskStart: SyncHook; + + /** + * The `taskFinish` hook is called at the end of a task. It is called after the task has completed + * execution. To use it, call `taskFinish.tap(, )`. + * + * @public + */ + taskFinish: SyncHook; + + /** + * The `phaseStart` hook is called at the beginning of a phase. It is called before the phase has + * begun to execute. To use it, call `phaseStart.tap(, )`. + * + * @public + */ + phaseStart: SyncHook; + + /** + * The `phaseFinish` hook is called at the end of a phase. It is called after the phase has completed + * execution. To use it, call `phaseFinish.tap(, )`. + * + * @public + */ + phaseFinish: SyncHook; } /** @@ -155,7 +219,6 @@ export class HeftLifecycleSession implements IHeftLifecycleSession { public readonly hooks: IHeftLifecycleHooks; public readonly parameters: IHeftParameters; - public readonly cacheFolderPath: string; public readonly tempFolderPath: string; public readonly logger: IScopedLogger; public readonly debug: boolean; @@ -167,23 +230,22 @@ export class HeftLifecycleSession implements IHeftLifecycleSession { public constructor(options: IHeftLifecycleSessionOptions) { this._options = options; - this.logger = options.logger; - this.metricsCollector = options.metricsCollector; - this.hooks = options.lifecycleHooks; - this.parameters = options.lifecycleParameters; - this.debug = options.debug; + const { logger, metricsCollector, lifecycleHooks, lifecycleParameters, debug, pluginDefinition, heftConfiguration, pluginHost } = + options; + this.logger = logger; + this.metricsCollector = metricsCollector; + this.hooks = lifecycleHooks; + this.parameters = lifecycleParameters; + this.debug = debug; // Guranteed to be unique since phases are forbidden from using the name 'lifecycle' // and lifecycle plugin names are enforced to be unique. - const uniquePluginFolderName: string = `lifecycle.${options.pluginDefinition.pluginName}`; - - // /.cache/. - this.cacheFolderPath = path.join(options.heftConfiguration.cacheFolderPath, uniquePluginFolderName); + const uniquePluginFolderName: string = `lifecycle.${pluginDefinition.pluginName}`; // /temp/. - this.tempFolderPath = path.join(options.heftConfiguration.tempFolderPath, uniquePluginFolderName); + this.tempFolderPath = path.join(heftConfiguration.tempFolderPath, uniquePluginFolderName); - this._pluginHost = options.pluginHost; + this._pluginHost = pluginHost; } public requestAccessToPluginByName( diff --git a/apps/heft/src/pluginFramework/HeftParameterManager.ts b/apps/heft/src/pluginFramework/HeftParameterManager.ts index 82934d7dcfb..edf79c00fd4 100644 --- a/apps/heft/src/pluginFramework/HeftParameterManager.ts +++ b/apps/heft/src/pluginFramework/HeftParameterManager.ts @@ -3,16 +3,16 @@ import { InternalError } from '@rushstack/node-core-library'; import { - CommandLineParameter, - CommandLineParameterProvider, + type CommandLineParameter, + type CommandLineParameterProvider, CommandLineParameterKind, - CommandLineChoiceParameter, - CommandLineChoiceListParameter, - CommandLineFlagParameter, - CommandLineIntegerParameter, - CommandLineIntegerListParameter, - CommandLineStringParameter, - CommandLineStringListParameter + type CommandLineChoiceParameter, + type CommandLineChoiceListParameter, + type CommandLineFlagParameter, + type CommandLineIntegerParameter, + type CommandLineIntegerListParameter, + type CommandLineStringParameter, + type CommandLineStringListParameter } from '@rushstack/ts-command-line'; import type { @@ -34,13 +34,6 @@ export interface IHeftDefaultParameters { */ readonly clean: boolean; - /** - * Whether or not the `--clean-cache` flag was passed to Heft. - * - * @public - */ - readonly cleanCache: boolean; - /** * Whether or not the `--debug` flag was passed to Heft. * @@ -133,7 +126,6 @@ export interface IHeftParameters extends IHeftDefaultParameters { export interface IHeftParameterManagerOptions { getIsClean: () => boolean; - getIsCleanCache: () => boolean; getIsDebug: () => boolean; getIsVerbose: () => boolean; getIsProduction: () => boolean; @@ -143,7 +135,7 @@ export interface IHeftParameterManagerOptions { export class HeftParameterManager { private readonly _options: IHeftParameterManagerOptions; - // plugin defintiion => parameter accessors and defaults + // plugin definition => parameter accessors and defaults private readonly _heftParametersByDefinition: Map = new Map(); // plugin definition => Map< parameter long name => applied parameter > private readonly _parametersByDefinition: Map> = @@ -162,7 +154,6 @@ export class HeftParameterManager { if (!this._defaultParameters) { this._defaultParameters = { clean: this._options.getIsClean(), - cleanCache: this._options.getIsCleanCache(), debug: this._options.getIsDebug(), verbose: this._options.getIsVerbose(), production: this._options.getIsProduction(), @@ -249,105 +240,125 @@ export class HeftParameterManager { /** * Add the parameters specified by a plugin definition to the command line parameter provider. * Duplicate parameters are allowed, as long as they have different parameter scopes. In this - * case, the parameter will only be referencable by the CLI argument + * case, the parameter will only be referenceable by the CLI argument * "--:". If there is no duplicate parameter, it will also be - * referencable by the CLI argument "--". + * referenceable by the CLI argument "--". */ private _addParametersToProvider( pluginDefinition: HeftPluginDefinitionBase, commandLineParameterProvider: CommandLineParameterProvider ): void { + const { + pluginName, + pluginPackageName, + pluginParameterScope: parameterScope, + pluginParameters + } = pluginDefinition; const existingDefinitionWithScope: HeftPluginDefinitionBase | undefined = - this._pluginDefinitionsByScope.get(pluginDefinition.pluginParameterScope); + this._pluginDefinitionsByScope.get(parameterScope); if (existingDefinitionWithScope && existingDefinitionWithScope !== pluginDefinition) { + const { pluginName: existingScopePluginName, pluginPackageName: existingScopePluginPackageName } = + existingDefinitionWithScope; throw new Error( - `Plugin ${JSON.stringify(pluginDefinition.pluginName)} in package ` + - `${JSON.stringify(pluginDefinition.pluginPackageName)} specifies the same parameter scope ` + - `${JSON.stringify(pluginDefinition.pluginParameterScope)} as plugin ` + - `${JSON.stringify(existingDefinitionWithScope.pluginName)} from package ` + - `${JSON.stringify(existingDefinitionWithScope.pluginPackageName)}.` + `Plugin ${JSON.stringify(pluginName)} in package ` + + `${JSON.stringify(pluginPackageName)} specifies the same parameter scope ` + + `${JSON.stringify(parameterScope)} as plugin ` + + `${JSON.stringify(existingScopePluginName)} from package ` + + `${JSON.stringify(existingScopePluginPackageName)}.` ); } else { - this._pluginDefinitionsByScope.set(pluginDefinition.pluginParameterScope, pluginDefinition); + this._pluginDefinitionsByScope.set(parameterScope, pluginDefinition); } const definedPluginParametersByName: Map = this._parametersByDefinition.get(pluginDefinition)!; - for (const parameter of pluginDefinition.pluginParameters) { - // Short names are excluded since it would be difficult and confusing to de-dupe/handle shortname - // conflicts as well as longname conflicts + for (const parameter of pluginParameters) { let definedParameter: CommandLineParameter; + const { description, required, longName: parameterLongName, shortName: parameterShortName } = parameter; switch (parameter.parameterKind) { case 'choiceList': { + const { alternatives } = parameter; definedParameter = commandLineParameterProvider.defineChoiceListParameter({ - description: parameter.description, - required: parameter.required, - alternatives: parameter.alternatives.map((p: IChoiceParameterAlternativeJson) => p.name), - parameterLongName: parameter.longName, - parameterScope: pluginDefinition.pluginParameterScope + description, + required, + alternatives: alternatives.map((p: IChoiceParameterAlternativeJson) => p.name), + parameterLongName, + parameterShortName, + parameterScope }); break; } case 'choice': { + const { alternatives, defaultValue } = parameter; definedParameter = commandLineParameterProvider.defineChoiceParameter({ - description: parameter.description, - required: parameter.required, - alternatives: parameter.alternatives.map((p: IChoiceParameterAlternativeJson) => p.name), - defaultValue: parameter.defaultValue, - parameterLongName: parameter.longName, - parameterScope: pluginDefinition.pluginParameterScope + description, + required, + alternatives: alternatives.map((p: IChoiceParameterAlternativeJson) => p.name), + defaultValue, + parameterLongName, + parameterShortName, + parameterScope }); break; } case 'flag': { definedParameter = commandLineParameterProvider.defineFlagParameter({ - description: parameter.description, - required: parameter.required, - parameterLongName: parameter.longName, - parameterScope: pluginDefinition.pluginParameterScope + description, + required, + parameterLongName, + parameterShortName, + parameterScope }); break; } case 'integerList': { + const { argumentName } = parameter; definedParameter = commandLineParameterProvider.defineIntegerListParameter({ - description: parameter.description, - required: parameter.required, - argumentName: parameter.argumentName, - parameterLongName: parameter.longName, - parameterScope: pluginDefinition.pluginParameterScope + description, + required, + argumentName, + parameterLongName, + parameterShortName, + parameterScope }); break; } case 'integer': { + const { argumentName, defaultValue } = parameter; definedParameter = commandLineParameterProvider.defineIntegerParameter({ - description: parameter.description, - required: parameter.required, - argumentName: parameter.argumentName, - defaultValue: parameter.defaultValue, - parameterLongName: parameter.longName, - parameterScope: pluginDefinition.pluginParameterScope + description, + required, + argumentName, + defaultValue, + parameterLongName, + parameterShortName, + parameterScope }); break; } case 'stringList': { + const { argumentName } = parameter; definedParameter = commandLineParameterProvider.defineStringListParameter({ - description: parameter.description, - required: parameter.required, - argumentName: parameter.argumentName, - parameterLongName: parameter.longName, - parameterScope: pluginDefinition.pluginParameterScope + description, + required, + argumentName, + parameterLongName, + parameterShortName, + parameterScope }); break; } case 'string': { + const { argumentName, defaultValue } = parameter; definedParameter = commandLineParameterProvider.defineStringParameter({ - description: parameter.description, - required: parameter.required, - argumentName: parameter.argumentName, - defaultValue: parameter.defaultValue, - parameterLongName: parameter.longName, - parameterScope: pluginDefinition.pluginParameterScope + description, + required, + argumentName, + defaultValue, + parameterLongName, + parameterShortName, + parameterScope }); break; } diff --git a/apps/heft/src/pluginFramework/HeftPhase.ts b/apps/heft/src/pluginFramework/HeftPhase.ts index 1a6de503ef9..e7ac8e65ca6 100644 --- a/apps/heft/src/pluginFramework/HeftPhase.ts +++ b/apps/heft/src/pluginFramework/HeftPhase.ts @@ -1,14 +1,30 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { HeftTask } from './HeftTask'; +import { HeftTask, type IHeftTask } from './HeftTask'; import type { InternalHeftSession } from './InternalHeftSession'; import type { IHeftConfigurationJsonPhaseSpecifier } from '../utilities/CoreConfigFiles'; import type { IDeleteOperation } from '../plugins/DeleteFilesPlugin'; const RESERVED_PHASE_NAMES: Set = new Set(['lifecycle']); -export class HeftPhase { +/** + * @public + */ +export interface IHeftPhase { + readonly phaseName: string; + readonly phaseDescription: string | undefined; + cleanFiles: ReadonlySet; + consumingPhases: ReadonlySet; + dependencyPhases: ReadonlySet; + tasks: ReadonlySet; + tasksByName: ReadonlyMap; +} + +/** + * @internal + */ +export class HeftPhase implements IHeftPhase { private _internalHeftSession: InternalHeftSession; private _phaseName: string; private _phaseSpecifier: IHeftConfigurationJsonPhaseSpecifier; diff --git a/apps/heft/src/pluginFramework/HeftPluginHost.ts b/apps/heft/src/pluginFramework/HeftPluginHost.ts index b13a48c86d9..9f3dad8edcc 100644 --- a/apps/heft/src/pluginFramework/HeftPluginHost.ts +++ b/apps/heft/src/pluginFramework/HeftPluginHost.ts @@ -2,7 +2,9 @@ // See LICENSE in the project root for license information. import { SyncHook } from 'tapable'; + import { InternalError } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; import type { HeftPluginDefinitionBase } from '../configuration/HeftPluginDefinition'; import type { IHeftPlugin } from './IHeftPlugin'; @@ -12,11 +14,12 @@ export abstract class HeftPluginHost { private readonly _pluginAccessRequestHooks: Map> = new Map(); private _pluginsApplied: boolean = false; - public async applyPluginsAsync(): Promise { + public async applyPluginsAsync(terminal: ITerminal): Promise { if (this._pluginsApplied) { // No need to apply them a second time. return; } + terminal.writeVerboseLine('Applying plugins'); await this.applyPluginsInternalAsync(); this._pluginsApplied = true; } diff --git a/apps/heft/src/pluginFramework/HeftTask.ts b/apps/heft/src/pluginFramework/HeftTask.ts index 3ee476411e4..d5f098bd1d8 100644 --- a/apps/heft/src/pluginFramework/HeftTask.ts +++ b/apps/heft/src/pluginFramework/HeftTask.ts @@ -4,87 +4,34 @@ import { InternalError } from '@rushstack/node-core-library'; import { HeftPluginConfiguration } from '../configuration/HeftPluginConfiguration'; -import { +import type { HeftTaskPluginDefinition, - type HeftPluginDefinitionBase + HeftPluginDefinitionBase } from '../configuration/HeftPluginDefinition'; -import type { HeftPhase } from './HeftPhase'; +import type { HeftPhase, IHeftPhase } from './HeftPhase'; import type { IHeftConfigurationJsonTaskSpecifier, IHeftConfigurationJsonPluginSpecifier } from '../utilities/CoreConfigFiles'; -import type { IHeftTaskPlugin } from '../pluginFramework/IHeftPlugin'; -import type { IScopedLogger } from '../pluginFramework/logging/ScopedLogger'; +import type { IHeftTaskPlugin } from './IHeftPlugin'; +import type { IScopedLogger } from './logging/ScopedLogger'; const RESERVED_TASK_NAMES: Set = new Set(['clean']); -let _copyFilesPluginDefinition: HeftTaskPluginDefinition | undefined; -function _getCopyFilesPluginDefinition(): HeftTaskPluginDefinition { - if (!_copyFilesPluginDefinition) { - _copyFilesPluginDefinition = HeftTaskPluginDefinition.loadFromObject({ - heftPluginDefinitionJson: { - pluginName: 'copy-files-plugin', - entryPoint: './lib/plugins/CopyFilesPlugin', - optionsSchema: './lib/schemas/copy-files-options.schema.json' - }, - packageRoot: `${__dirname}/../..`, - packageName: '@rushstack/heft' - }); - } - return _copyFilesPluginDefinition; -} - -let _deleteFilesPluginDefinition: HeftTaskPluginDefinition | undefined; -function _getDeleteFilesPluginDefinition(): HeftTaskPluginDefinition { - if (!_deleteFilesPluginDefinition) { - _deleteFilesPluginDefinition = HeftTaskPluginDefinition.loadFromObject({ - heftPluginDefinitionJson: { - pluginName: 'delete-files-plugin', - entryPoint: './lib/plugins/DeleteFilesPlugin', - optionsSchema: './lib/schemas/delete-files-options.schema.json' - }, - packageRoot: `${__dirname}/../..`, - packageName: '@rushstack/heft' - }); - } - return _deleteFilesPluginDefinition; -} - -let _runScriptPluginDefinition: HeftTaskPluginDefinition | undefined; -function _getRunScriptPluginDefinition(): HeftTaskPluginDefinition { - if (!_runScriptPluginDefinition) { - _runScriptPluginDefinition = HeftTaskPluginDefinition.loadFromObject({ - heftPluginDefinitionJson: { - pluginName: 'run-script-plugin', - entryPoint: './lib/plugins/RunScriptPlugin', - optionsSchema: './lib/schemas/run-script-options.schema.json' - }, - packageRoot: `${__dirname}/../..`, - packageName: '@rushstack/heft' - }); - } - return _runScriptPluginDefinition; -} - -let _nodeServicePluginDefinition: HeftTaskPluginDefinition | undefined; -function _getNodeServicePluginDefinition(): HeftTaskPluginDefinition { - if (!_nodeServicePluginDefinition) { - _nodeServicePluginDefinition = HeftTaskPluginDefinition.loadFromObject({ - heftPluginDefinitionJson: { - pluginName: 'node-service-plugin', - entryPoint: './lib/plugins/NodeServicePlugin' - }, - packageRoot: `${__dirname}/../..`, - packageName: '@rushstack/heft' - }); - } - return _nodeServicePluginDefinition; +/** + * @public + */ +export interface IHeftTask { + readonly parentPhase: IHeftPhase; + readonly taskName: string; + readonly consumingTasks: ReadonlySet; + readonly dependencyTasks: ReadonlySet; } /** * @internal */ -export class HeftTask { +export class HeftTask implements IHeftTask { private _parentPhase: HeftPhase; private _taskName: string; private _taskSpecifier: IHeftConfigurationJsonTaskSpecifier; @@ -132,7 +79,7 @@ export class HeftTask { } public get pluginOptions(): object | undefined { - return this._taskSpecifier.taskEvent?.options || this._taskSpecifier.taskPlugin?.options; + return this._taskSpecifier.taskPlugin.options; } public get dependencyTasks(): Set { @@ -170,7 +117,7 @@ export class HeftTask { public async ensureInitializedAsync(): Promise { if (!this._taskPluginDefinition) { - this._taskPluginDefinition = await this._loadTaskPluginDefintionAsync(); + this._taskPluginDefinition = await this._loadTaskPluginDefinitionAsync(); this.pluginDefinition.validateOptions(this.pluginOptions); } } @@ -183,58 +130,25 @@ export class HeftTask { return this._taskPlugin; } - private async _loadTaskPluginDefintionAsync(): Promise { - if (this._taskSpecifier.taskEvent && this._taskSpecifier.taskPlugin) { - // This is validated in the schema so this shouldn't happen, but throw just in case. + private async _loadTaskPluginDefinitionAsync(): Promise { + // taskPlugin.pluginPackage should already be resolved to the package root. + // See CoreConfigFiles.heftConfigFileLoader + const pluginSpecifier: IHeftConfigurationJsonPluginSpecifier = this._taskSpecifier.taskPlugin; + const pluginConfiguration: HeftPluginConfiguration = await HeftPluginConfiguration.loadFromPackageAsync( + pluginSpecifier.pluginPackageRoot, + pluginSpecifier.pluginPackage + ); + const pluginDefinition: HeftPluginDefinitionBase = + pluginConfiguration.getPluginDefinitionBySpecifier(pluginSpecifier); + + const isTaskPluginDefinition: boolean = pluginConfiguration.isTaskPluginDefinition(pluginDefinition); + if (!isTaskPluginDefinition) { throw new Error( - `Task ${JSON.stringify(this._taskName)} has both a taskEvent and a taskPlugin. ` + - `Only one of these can be specified.` - ); - } - - if (this._taskSpecifier.taskEvent) { - switch (this._taskSpecifier.taskEvent.eventKind) { - case 'copyFiles': { - return _getCopyFilesPluginDefinition(); - } - case 'deleteFiles': { - return _getDeleteFilesPluginDefinition(); - } - case 'runScript': { - return _getRunScriptPluginDefinition(); - } - case 'nodeService': { - return _getNodeServicePluginDefinition(); - } - default: { - throw new InternalError( - `Unknown task event kind ${JSON.stringify(this._taskSpecifier.taskEvent.eventKind)}` - ); - } - } - } else if (this._taskSpecifier.taskPlugin) { - // taskPlugin.pluginPackage should already be resolved to the package root. - // See CoreConfigFiles.heftConfigFileLoader - const pluginSpecifier: IHeftConfigurationJsonPluginSpecifier = this._taskSpecifier.taskPlugin; - const pluginConfiguration: HeftPluginConfiguration = await HeftPluginConfiguration.loadFromPackageAsync( - pluginSpecifier.pluginPackageRoot, - pluginSpecifier.pluginPackage - ); - const pluginDefinition: HeftPluginDefinitionBase = - pluginConfiguration.getPluginDefinitionBySpecifier(pluginSpecifier); - if (!pluginConfiguration.taskPluginDefinitions.has(pluginDefinition)) { - throw new Error( - `Plugin ${JSON.stringify(pluginSpecifier.pluginName)} specified by task ` + - `${JSON.stringify(this._taskName)} is not a task plugin.` - ); - } - return pluginDefinition as HeftTaskPluginDefinition; - } else { - // This is validated in the schema so this shouldn't happen, but throw just in case - throw new InternalError( - `Task ${JSON.stringify(this._taskName)} has no specified task event or task plugin.` + `Plugin ${JSON.stringify(pluginSpecifier.pluginName)} specified by task ` + + `${JSON.stringify(this._taskName)} is not a task plugin.` ); } + return pluginDefinition; } private _validate(): void { @@ -243,8 +157,8 @@ export class HeftTask { `Task name ${JSON.stringify(this.taskName)} is reserved and cannot be used as a task name.` ); } - if (!this._taskSpecifier.taskEvent && !this._taskSpecifier.taskPlugin) { - throw new Error(`Task ${JSON.stringify(this.taskName)} has no specified task event or task plugin.`); + if (!this._taskSpecifier.taskPlugin) { + throw new Error(`Task ${JSON.stringify(this.taskName)} has no specified task plugin.`); } } } diff --git a/apps/heft/src/pluginFramework/HeftTaskSession.ts b/apps/heft/src/pluginFramework/HeftTaskSession.ts index db7d2d59c9d..9898b11a492 100644 --- a/apps/heft/src/pluginFramework/HeftTaskSession.ts +++ b/apps/heft/src/pluginFramework/HeftTaskSession.ts @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; import { AsyncParallelHook, AsyncSeriesWaterfallHook } from 'tapable'; +import { InternalError } from '@rushstack/node-core-library'; + import type { MetricsCollector } from '../metrics/MetricsCollector'; import type { IScopedLogger } from './logging/ScopedLogger'; import type { HeftTask } from './HeftTask'; @@ -12,8 +13,51 @@ import type { HeftParameterManager, IHeftParameters } from './HeftParameterManag import type { IDeleteOperation } from '../plugins/DeleteFilesPlugin'; import type { ICopyOperation } from '../plugins/CopyFilesPlugin'; import type { HeftPluginHost } from './HeftPluginHost'; -import type { CancellationToken } from './CancellationToken'; -import { WatchGlobFn } from '../plugins/FileGlobSpecifier'; +import type { GlobFn, WatchGlobFn } from '../plugins/FileGlobSpecifier'; +import type { IWatchFileSystem } from '../utilities/WatchFileSystemAdapter'; + +/** + * The type of {@link IHeftTaskSession.parsedCommandLine}, which exposes details about the + * command line that was used to invoke Heft. + * @public + */ +export interface IHeftParsedCommandLine { + /** + * Returns the subcommand passed on the Heft command line, before any aliases have been expanded. + * This can be useful when printing error messages that need to refer to the invoked command line. + * + * @remarks + * For example, if the invoked command was `heft test --verbose`, then `commandName` + * would be `test`. + * + * Suppose the invoked command was `heft start` which is an alias for `heft build-watch --serve`. + * In this case, the `commandName` would be `start`. To get the expanded name `build-watch`, + * use {@link IHeftParsedCommandLine.unaliasedCommandName} instead. + * + * When invoking phases directly using `heft run`, the `commandName` is `run`. + * + * @see {@link IHeftParsedCommandLine.unaliasedCommandName} + */ + readonly commandName: string; + + /** + * Returns the subcommand passed on the Heft command line, after any aliases have been expanded. + * This can be useful when printing error messages that need to refer to the invoked command line. + * + * @remarks + * For example, if the invoked command was `heft test --verbose`, then `unaliasedCommandName` + * would be `test`. + * + * Suppose the invoked command was `heft start` which is an alias for `heft build-watch --serve`. + * In this case, the `unaliasedCommandName` would be `build-watch`. To get the alias name + * `start`, use @see {@link IHeftParsedCommandLine.commandName} instead. + * + * When invoking phases directly using `heft run`, the `unaliasedCommandName` is `run`. + * + * @see {@link IHeftParsedCommandLine.commandName} + */ + readonly unaliasedCommandName: string; +} /** * The task session is responsible for providing session-specific information to Heft task plugins. @@ -47,13 +91,10 @@ export interface IHeftTaskSession { readonly parameters: IHeftParameters; /** - * The cache folder for the task. This folder is unique for each task, and will not be - * cleaned when Heft is run with `--clean`. However, it will be cleaned when Heft is run - * with `--clean` and `--clean-cache`. - * - * @public + * Exposes details about the command line that was used to invoke Heft. + * This value is initially `undefined` and later filled in after the command line has been parsed. */ - readonly cacheFolderPath: string; + readonly parsedCommandLine: IHeftParsedCommandLine; /** * The temp folder for the task. This folder is unique for each task, and will be cleaned @@ -65,7 +106,7 @@ export interface IHeftTaskSession { /** * The scoped logger for the task. Messages logged with this logger will be prefixed with - * the phase and task name, in the format "[:]". It is highly recommended + * the phase and task name, in the format `[:]`. It is highly recommended * that writing to the console be performed via the logger, as it will ensure that logging messages * are labeled with the source of the message. * @@ -123,13 +164,17 @@ export interface IHeftTaskHooks { */ export interface IHeftTaskRunHookOptions { /** - * A cancellation token that is used to signal that the build is cancelled. This - * can be used to stop operations early and allow for a new build to - * be started. + * An abort signal that is used to abort the build. This can be used to stop operations early and allow + * for a new build to be started. * * @beta */ - readonly cancellationToken: CancellationToken; + readonly abortSignal: AbortSignal; + + /** + * Reads the specified globs and returns the result. + */ + readonly globAsync: GlobFn; } /** @@ -151,6 +196,12 @@ export interface IHeftTaskRunIncrementalHookOptions extends IHeftTaskRunHookOpti * If a change to the monitored files is detected, the task will be scheduled for re-execution. */ readonly watchGlobAsync: WatchGlobFn; + + /** + * Access to the file system view that powers `watchGlobAsync`. + * This is useful for plugins that do their own file system operations but still want to leverage Heft for watching. + */ + readonly watchFs: IWatchFileSystem; } /** @@ -184,12 +235,12 @@ export interface IHeftTaskSessionOptions extends IHeftPhaseSessionOptions { export class HeftTaskSession implements IHeftTaskSession { public readonly taskName: string; public readonly hooks: IHeftTaskHooks; - public readonly cacheFolderPath: string; public readonly tempFolderPath: string; public readonly logger: IScopedLogger; private readonly _options: IHeftTaskSessionOptions; private _parameters: IHeftParameters | undefined; + private _parsedCommandLine: IHeftParsedCommandLine; /** * @internal @@ -206,10 +257,14 @@ export class HeftTaskSession implements IHeftTaskSession { return this._parameters; } + public get parsedCommandLine(): IHeftParsedCommandLine { + return this._parsedCommandLine; + } + public constructor(options: IHeftTaskSessionOptions) { const { internalHeftSession: { - heftConfiguration: { cacheFolderPath: cacheFolder, tempFolderPath: tempFolder }, + heftConfiguration: { tempFolderPath: tempFolder }, loggingManager, metricsCollector }, @@ -217,6 +272,12 @@ export class HeftTaskSession implements IHeftTaskSession { task } = options; + if (!options.internalHeftSession.parsedCommandLine) { + // This should not happen + throw new InternalError('Attempt to construct HeftTaskSession before command line has been parsed'); + } + this._parsedCommandLine = options.internalHeftSession.parsedCommandLine; + this.logger = loggingManager.requestScopedLogger(`${phase.phaseName}:${task.taskName}`); this.metricsCollector = metricsCollector; this.taskName = task.taskName; @@ -226,18 +287,16 @@ export class HeftTaskSession implements IHeftTaskSession { registerFileOperations: new AsyncSeriesWaterfallHook(['fileOperations']) }; - // Guranteed to be unique since phases are uniquely named, tasks are uniquely named within - // phases, and neither can have '.' in their names. We will also use the phase name and + // Guaranteed to be unique since phases are uniquely named, tasks are uniquely named within + // phases, and neither can have '/' in their names. We will also use the phase name and // task name as the folder name (instead of the plugin name) since we want to enable re-use // of plugins in multiple phases and tasks while maintaining unique temp/cache folders for // each task. - const uniqueTaskFolderName: string = `${phase.phaseName}.${task.taskName}`; - - // /.cache/. - this.cacheFolderPath = path.join(cacheFolder, uniqueTaskFolderName); + // Having a parent folder for the phase simplifies interaction with the Rush build cache. + const uniqueTaskFolderName: string = `${phase.phaseName}/${task.taskName}`; - // /temp/. - this.tempFolderPath = path.join(tempFolder, uniqueTaskFolderName); + // /temp// + this.tempFolderPath = `${tempFolder}/${uniqueTaskFolderName}`; this._options = options; } diff --git a/apps/heft/src/pluginFramework/IncrementalBuildInfo.ts b/apps/heft/src/pluginFramework/IncrementalBuildInfo.ts new file mode 100644 index 00000000000..3c30d07cec5 --- /dev/null +++ b/apps/heft/src/pluginFramework/IncrementalBuildInfo.ts @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { FileSystem, Path } from '@rushstack/node-core-library'; + +/** + * Information about an incremental build. This information is used to determine which files need to be rebuilt. + * @beta + */ +export interface IIncrementalBuildInfo { + /** + * A string that represents the configuration inputs for the build. + * If the configuration changes, the old build info object should be discarded. + */ + configHash: string; + + /** + * A map of absolute input file paths to their version strings. + * The version string should change if the file changes. + */ + inputFileVersions: Map; + + /** + * A map of absolute output file paths to the input files they were computed from. + */ + fileDependencies?: Map; +} + +/** + * Serialized version of {@link IIncrementalBuildInfo}. + * @beta + */ +export interface ISerializedIncrementalBuildInfo { + /** + * A string that represents the configuration inputs for the build. + * If the configuration changes, the old build info object should be discarded. + */ + configHash: string; + + /** + * A map of input files to their version strings. + * File paths are specified relative to the folder containing the build info file. + */ + inputFileVersions: Record; + + /** + * Map of output file names to the corresponding index in `Object.entries(inputFileVersions)`. + * File paths are specified relative to the folder containing the build info file. + */ + fileDependencies?: Record; +} + +/** + * Converts an absolute path to a path relative to a base path. + */ +export const makePathRelative: (absolutePath: string, basePath: string) => string = + process.platform === 'win32' + ? (absolutePath: string, basePath: string) => { + // On Windows, need to normalize slashes + return Path.convertToSlashes(path.win32.relative(basePath, absolutePath)); + } + : (absolutePath: string, basePath: string) => { + // On POSIX, can preserve existing slashes + return path.posix.relative(basePath, absolutePath); + }; + +/** + * Serializes a build info object to a portable format that can be written to disk. + * @param state - The build info to serialize + * @param makePathPortable - A function that converts an absolute path to a portable path. This is a separate argument to support cross-platform tests. + * @returns The serialized build info + * @beta + */ +export function serializeBuildInfo( + state: IIncrementalBuildInfo, + makePathPortable: (absolutePath: string) => string +): ISerializedIncrementalBuildInfo { + const fileIndices: Map = new Map(); + const inputFileVersions: Record = {}; + + for (const [absolutePath, version] of state.inputFileVersions) { + const relativePath: string = makePathPortable(absolutePath); + fileIndices.set(absolutePath, fileIndices.size); + inputFileVersions[relativePath] = version; + } + + const { fileDependencies: newFileDependencies } = state; + let fileDependencies: Record | undefined; + if (newFileDependencies) { + fileDependencies = {}; + for (const [absolutePath, dependencies] of newFileDependencies) { + const relativePath: string = makePathPortable(absolutePath); + const indices: number[] = []; + for (const dependency of dependencies) { + const index: number | undefined = fileIndices.get(dependency); + if (index === undefined) { + throw new Error(`Dependency not found: ${dependency}`); + } + indices.push(index); + } + + fileDependencies[relativePath] = indices; + } + } + + const serializedBuildInfo: ISerializedIncrementalBuildInfo = { + configHash: state.configHash, + inputFileVersions, + fileDependencies + }; + + return serializedBuildInfo; +} + +/** + * Deserializes a build info object from its portable format. + * @param serializedBuildInfo - The build info to deserialize + * @param makePathAbsolute - A function that converts a portable path to an absolute path. This is a separate argument to support cross-platform tests. + * @returns The deserialized build info + */ +export function deserializeBuildInfo( + serializedBuildInfo: ISerializedIncrementalBuildInfo, + makePathAbsolute: (relativePath: string) => string +): IIncrementalBuildInfo { + const inputFileVersions: Map = new Map(); + const absolutePathByIndex: string[] = []; + for (const [relativePath, version] of Object.entries(serializedBuildInfo.inputFileVersions)) { + const absolutePath: string = makePathAbsolute(relativePath); + absolutePathByIndex.push(absolutePath); + inputFileVersions.set(absolutePath, version); + } + + let fileDependencies: Map | undefined; + const { fileDependencies: serializedFileDependencies } = serializedBuildInfo; + if (serializedFileDependencies) { + fileDependencies = new Map(); + for (const [relativeOutputFile, indices] of Object.entries(serializedFileDependencies)) { + const absoluteOutputFile: string = makePathAbsolute(relativeOutputFile); + const dependencies: string[] = []; + for (const index of Array.isArray(indices) ? indices : [indices]) { + const dependencyAbsolutePath: string | undefined = absolutePathByIndex[index]; + if (dependencyAbsolutePath === undefined) { + throw new Error(`Dependency index not found: ${index}`); + } + dependencies.push(dependencyAbsolutePath); + } + fileDependencies.set(absoluteOutputFile, dependencies); + } + } + + const buildInfo: IIncrementalBuildInfo = { + configHash: serializedBuildInfo.configHash, + inputFileVersions, + fileDependencies + }; + + return buildInfo; +} + +/** + * Writes a build info object to disk. + * @param state - The build info to write + * @param filePath - The file path to write the build info to + * @beta + */ +export async function writeBuildInfoAsync(state: IIncrementalBuildInfo, filePath: string): Promise { + const basePath: string = path.dirname(filePath); + + const serializedBuildInfo: ISerializedIncrementalBuildInfo = serializeBuildInfo( + state, + (absolutePath: string) => { + return makePathRelative(absolutePath, basePath); + } + ); + + // This file is meant only for machine reading, so don't pretty-print it. + const stringified: string = JSON.stringify(serializedBuildInfo); + + await FileSystem.writeFileAsync(filePath, stringified, { ensureFolderExists: true }); +} + +/** + * Reads a build info object from disk. + * @param filePath - The file path to read the build info from + * @returns The build info object, or undefined if the file does not exist or cannot be parsed + * @beta + */ +export async function tryReadBuildInfoAsync(filePath: string): Promise { + let serializedBuildInfo: ISerializedIncrementalBuildInfo | undefined; + try { + const fileContents: string = await FileSystem.readFileAsync(filePath); + serializedBuildInfo = JSON.parse(fileContents) as ISerializedIncrementalBuildInfo; + } catch (error) { + if (FileSystem.isNotExistError(error)) { + return; + } + throw error; + } + + const basePath: string = path.dirname(filePath); + + const buildInfo: IIncrementalBuildInfo = deserializeBuildInfo( + serializedBuildInfo, + (relativePath: string) => { + return path.resolve(basePath, relativePath); + } + ); + + return buildInfo; +} diff --git a/apps/heft/src/pluginFramework/InternalHeftSession.ts b/apps/heft/src/pluginFramework/InternalHeftSession.ts index b84f63f9cdd..93905e3e94f 100644 --- a/apps/heft/src/pluginFramework/InternalHeftSession.ts +++ b/apps/heft/src/pluginFramework/InternalHeftSession.ts @@ -7,26 +7,28 @@ import { Constants } from '../utilities/Constants'; import { HeftLifecycle } from './HeftLifecycle'; import { HeftPhaseSession } from './HeftPhaseSession'; import { HeftPhase } from './HeftPhase'; -import { CoreConfigFiles, type IHeftConfigurationJson } from '../utilities/CoreConfigFiles'; +import { + CoreConfigFiles, + type IHeftConfigurationJson, + type IHeftConfigurationJsonActionReference +} from '../utilities/CoreConfigFiles'; import type { MetricsCollector } from '../metrics/MetricsCollector'; import type { LoggingManager } from './logging/LoggingManager'; import type { HeftConfiguration } from '../configuration/HeftConfiguration'; +import type { HeftPluginDefinitionBase } from '../configuration/HeftPluginDefinition'; import type { HeftTask } from './HeftTask'; import type { HeftParameterManager } from './HeftParameterManager'; +import type { IHeftParsedCommandLine } from './HeftTaskSession'; export interface IInternalHeftSessionOptions { heftConfiguration: HeftConfiguration; loggingManager: LoggingManager; metricsCollector: MetricsCollector; - debug: boolean; -} -export interface IHeftSessionWatchOptions { - ignoredSourceFileGlobs: readonly string[]; - forbiddenSourceFileGlobs: readonly string[]; + debug: boolean; } -function* getAllTasks(phases: Iterable): Iterable { +function* getAllTasks(phases: Iterable): IterableIterator { for (const phase of phases) { yield* phase.tasks; } @@ -35,11 +37,11 @@ function* getAllTasks(phases: Iterable): Iterable { export class InternalHeftSession { private readonly _phaseSessionsByPhase: Map = new Map(); private readonly _heftConfigurationJson: IHeftConfigurationJson; + private _actionReferencesByAlias: ReadonlyMap | undefined; private _lifecycle: HeftLifecycle | undefined; private _phases: Set | undefined; private _phasesByName: Map | undefined; private _parameterManager: HeftParameterManager | undefined; - private _watchOptions: IHeftSessionWatchOptions | undefined; public readonly heftConfiguration: HeftConfiguration; @@ -47,6 +49,8 @@ export class InternalHeftSession { public readonly metricsCollector: MetricsCollector; + public parsedCommandLine: IHeftParsedCommandLine | undefined; + public readonly debug: boolean; private constructor(heftConfigurationJson: IHeftConfigurationJson, options: IInternalHeftSessionOptions) { @@ -83,6 +87,33 @@ export class InternalHeftSession { { concurrency: Constants.maxParallelism } ); + function* getAllPluginDefinitions(): IterableIterator { + yield* internalHeftSession.lifecycle.pluginDefinitions; + for (const task of getAllTasks(internalHeftSession.phases)) { + yield task.pluginDefinition; + } + } + + const loadedPluginPathsByName: Map> = new Map(); + for (const { pluginName, entryPoint } of getAllPluginDefinitions()) { + let existingPluginPaths: Set | undefined = loadedPluginPathsByName.get(pluginName); + if (!existingPluginPaths) { + existingPluginPaths = new Set(); + loadedPluginPathsByName.set(pluginName, existingPluginPaths); + } + + existingPluginPaths.add(entryPoint); + } + + for (const [pluginName, pluginPaths] of loadedPluginPathsByName) { + if (pluginPaths.size > 1) { + throw new Error( + `Multiple plugins named ${JSON.stringify(pluginName)} were loaded from different paths: ` + + `${Array.from(pluginPaths, (x) => JSON.stringify(x)).join(', ')}. Plugins must have unique names.` + ); + } + } + return internalHeftSession; } @@ -97,6 +128,15 @@ export class InternalHeftSession { this._parameterManager = value; } + public get actionReferencesByAlias(): ReadonlyMap { + if (!this._actionReferencesByAlias) { + this._actionReferencesByAlias = new Map( + Object.entries(this._heftConfigurationJson.aliasesByName || {}) + ); + } + return this._actionReferencesByAlias; + } + public get lifecycle(): HeftLifecycle { if (!this._lifecycle) { this._lifecycle = new HeftLifecycle(this, this._heftConfigurationJson.heftPlugins || []); diff --git a/apps/heft/src/pluginFramework/StaticFileSystemAdapter.ts b/apps/heft/src/pluginFramework/StaticFileSystemAdapter.ts index d4146d0104b..89129078af2 100644 --- a/apps/heft/src/pluginFramework/StaticFileSystemAdapter.ts +++ b/apps/heft/src/pluginFramework/StaticFileSystemAdapter.ts @@ -1,8 +1,11 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import type { ReaddirAsynchronousMethod, ReaddirSynchronousMethod } from '@nodelib/fs.scandir'; -import type { StatAsynchronousMethod, StatSynchronousMethod } from '@nodelib/fs.stat'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type * as fs from 'node:fs'; +import * as path from 'node:path'; + import type { FileSystemAdapter } from 'fast-glob'; + import { Path } from '@rushstack/node-core-library'; interface IVirtualFileSystemEntry { @@ -35,7 +38,7 @@ export class StaticFileSystemAdapter implements FileSystemAdapter { private _directoryMap: Map = new Map(); /** { @inheritdoc fs.lstat } */ - public lstat: StatAsynchronousMethod = ((filePath: string, callback: StatCallback) => { + public lstat: FileSystemAdapter['lstat'] = ((filePath: string, callback: StatCallback) => { process.nextTick(() => { let result: fs.Stats; try { @@ -44,13 +47,13 @@ export class StaticFileSystemAdapter implements FileSystemAdapter { callback(e, {} as fs.Stats); return; } - // eslint-disable-next-line @rushstack/no-new-null + callback(null, result); }); - }) as StatAsynchronousMethod; + }) as FileSystemAdapter['lstat']; /** { @inheritdoc fs.lstatSync } */ - public lstatSync: StatSynchronousMethod = ((filePath: string) => { + public lstatSync: FileSystemAdapter['lstatSync'] = ((filePath: string) => { filePath = this._normalizePath(filePath); const entry: IVirtualFileSystemEntry | undefined = this._directoryMap.get(filePath); if (!entry) { @@ -71,20 +74,20 @@ export class StaticFileSystemAdapter implements FileSystemAdapter { isFIFO: () => false, isSocket: () => false }; - }) as StatSynchronousMethod; + }) as FileSystemAdapter['lstatSync']; /** { @inheritdoc fs.stat } */ - public stat: StatAsynchronousMethod = ((filePath: string, callback: StatCallback) => { + public stat: FileSystemAdapter['stat'] = ((filePath: string, callback: StatCallback) => { this.lstat(filePath, callback); - }) as StatAsynchronousMethod; + }) as FileSystemAdapter['stat']; /** { @inheritdoc fs.statSync } */ - public statSync: StatSynchronousMethod = ((filePath: string) => { + public statSync: FileSystemAdapter['statSync'] = ((filePath: string) => { return this.lstatSync(filePath); - }) as StatSynchronousMethod; + }) as FileSystemAdapter['statSync']; /** { @inheritdoc fs.readdir } */ - public readdir: ReaddirAsynchronousMethod = (( + public readdir: FileSystemAdapter['readdir'] = (( filePath: string, optionsOrCallback: IReaddirOptions | ReaddirStringCallback, callback?: ReaddirDirentCallback | ReaddirStringCallback @@ -102,7 +105,7 @@ export class StaticFileSystemAdapter implements FileSystemAdapter { let result: fs.Dirent[] | string[]; try { if (options?.withFileTypes) { - result = this.readdirSync(filePath, options); + result = this.readdirSync(filePath, options) as fs.Dirent[]; } else { result = this.readdirSync(filePath); } @@ -114,17 +117,15 @@ export class StaticFileSystemAdapter implements FileSystemAdapter { // When "withFileTypes" is false or undefined, the callback is expected to return a string array. // Otherwise, we return a fs.Dirent array. if (options?.withFileTypes) { - // eslint-disable-next-line @rushstack/no-new-null (callback as ReaddirDirentCallback)(null, result as fs.Dirent[]); } else { - // eslint-disable-next-line @rushstack/no-new-null (callback as ReaddirStringCallback)(null, result as string[]); } }); - }) as ReaddirAsynchronousMethod; + }) as FileSystemAdapter['readdir']; /** { @inheritdoc fs.readdirSync } */ - public readdirSync: ReaddirSynchronousMethod = ((filePath: string, options?: IReaddirOptions) => { + public readdirSync: FileSystemAdapter['readdirSync'] = ((filePath: string, options?: IReaddirOptions) => { filePath = this._normalizePath(filePath); const virtualDirectory: IVirtualFileSystemEntry | undefined = this._directoryMap.get(filePath); if (!virtualDirectory) { @@ -167,7 +168,7 @@ export class StaticFileSystemAdapter implements FileSystemAdapter { } else { return result.map((entry: IVirtualFileSystemEntry) => entry.name); } - }) as ReaddirSynchronousMethod; + }) as FileSystemAdapter['readdirSync']; /** * Create a new StaticFileSystemAdapter instance with the provided file paths. diff --git a/apps/heft/src/pluginFramework/logging/LoggingManager.ts b/apps/heft/src/pluginFramework/logging/LoggingManager.ts index 06f970c300b..36c243a8084 100644 --- a/apps/heft/src/pluginFramework/logging/LoggingManager.ts +++ b/apps/heft/src/pluginFramework/logging/LoggingManager.ts @@ -1,14 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ScopedLogger } from './ScopedLogger'; import { FileError, - FileLocationStyle, - ITerminalProvider, - IFileErrorFormattingOptions + type FileLocationStyle, + type IFileErrorFormattingOptions } from '@rushstack/node-core-library'; +import type { ITerminalProvider } from '@rushstack/terminal'; +import { ScopedLogger } from './ScopedLogger'; export interface ILoggingManagerOptions { terminalProvider: ITerminalProvider; } @@ -17,12 +17,17 @@ export class LoggingManager { private _options: ILoggingManagerOptions; private _scopedLoggers: Map = new Map(); private _shouldPrintStacks: boolean = false; + private _hasAnyWarnings: boolean = false; private _hasAnyErrors: boolean = false; public get errorsHaveBeenEmitted(): boolean { return this._hasAnyErrors; } + public get warningsHaveBeenEmitted(): boolean { + return this._hasAnyWarnings; + } + public constructor(options: ILoggingManagerOptions) { this._options = options; } @@ -33,6 +38,7 @@ export class LoggingManager { public resetScopedLoggerErrorsAndWarnings(): void { this._hasAnyErrors = false; + this._hasAnyWarnings = false; for (const scopedLogger of this._scopedLoggers.values()) { scopedLogger.resetErrorsAndWarnings(); } @@ -47,7 +53,8 @@ export class LoggingManager { loggerName, terminalProvider: this._options.terminalProvider, getShouldPrintStacks: () => this._shouldPrintStacks, - errorHasBeenEmittedCallback: () => (this._hasAnyErrors = true) + errorHasBeenEmittedCallback: () => (this._hasAnyErrors = true), + warningHasBeenEmittedCallback: () => (this._hasAnyWarnings = true) }); this._scopedLoggers.set(loggerName, scopedLogger); return scopedLogger; diff --git a/apps/heft/src/pluginFramework/logging/MockScopedLogger.ts b/apps/heft/src/pluginFramework/logging/MockScopedLogger.ts new file mode 100644 index 00000000000..1ffe5ec6c3d --- /dev/null +++ b/apps/heft/src/pluginFramework/logging/MockScopedLogger.ts @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { ITerminal } from '@rushstack/terminal'; + +import type { IScopedLogger } from './ScopedLogger'; + +/** + * Implementation of IScopedLogger for use by unit tests. + * + * @internal + */ +export class MockScopedLogger implements IScopedLogger { + public errors: Error[] = []; + public warnings: Error[] = []; + + public loggerName: string = 'mockLogger'; + + public terminal: ITerminal; + + public constructor(terminal: ITerminal) { + this.terminal = terminal; + } + public get hasErrors(): boolean { + return this.errors.length > 0; + } + + public emitError(error: Error): void { + this.errors.push(error); + } + public emitWarning(warning: Error): void { + this.warnings.push(warning); + } + + public resetErrorsAndWarnings(): void { + this.errors.length = 0; + this.warnings.length = 0; + } +} diff --git a/apps/heft/src/pluginFramework/logging/ScopedLogger.ts b/apps/heft/src/pluginFramework/logging/ScopedLogger.ts index a67e4e51c55..357289ecc5d 100644 --- a/apps/heft/src/pluginFramework/logging/ScopedLogger.ts +++ b/apps/heft/src/pluginFramework/logging/ScopedLogger.ts @@ -6,7 +6,7 @@ import { Terminal, type ITerminalProvider, type ITerminal -} from '@rushstack/node-core-library'; +} from '@rushstack/terminal'; import { LoggingManager } from './LoggingManager'; @@ -53,6 +53,7 @@ export interface IScopedLoggerOptions { terminalProvider: ITerminalProvider; getShouldPrintStacks: () => boolean; errorHasBeenEmittedCallback: () => void; + warningHasBeenEmittedCallback: () => void; } export class ScopedLogger implements IScopedLogger { @@ -104,6 +105,7 @@ export class ScopedLogger implements IScopedLogger { * {@inheritdoc IScopedLogger.emitError} */ public emitError(error: Error): void { + this._options.errorHasBeenEmittedCallback(); this._errors.push(error); this.terminal.writeErrorLine(`Error: ${LoggingManager.getErrorMessage(error)}`); if (this._shouldPrintStacks && error.stack) { @@ -115,6 +117,7 @@ export class ScopedLogger implements IScopedLogger { * {@inheritdoc IScopedLogger.emitWarning} */ public emitWarning(warning: Error): void { + this._options.warningHasBeenEmittedCallback(); this._warnings.push(warning); this.terminal.writeWarningLine(`Warning: ${LoggingManager.getErrorMessage(warning)}`); if (this._shouldPrintStacks && warning.stack) { diff --git a/apps/heft/src/pluginFramework/tests/IncrementalBuildInfo.test.ts b/apps/heft/src/pluginFramework/tests/IncrementalBuildInfo.test.ts new file mode 100644 index 00000000000..0a5d3be7f65 --- /dev/null +++ b/apps/heft/src/pluginFramework/tests/IncrementalBuildInfo.test.ts @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; + +import { Path } from '@rushstack/node-core-library'; + +import { + serializeBuildInfo, + deserializeBuildInfo, + type IIncrementalBuildInfo, + type ISerializedIncrementalBuildInfo +} from '../IncrementalBuildInfo'; + +const posixBuildInfo: IIncrementalBuildInfo = { + configHash: 'foobar', + inputFileVersions: new Map([ + ['/a/b/c/file1', '1'], + ['/a/b/c/file2', '2'] + ]), + fileDependencies: new Map([ + ['/a/b/c/output1', ['/a/b/c/file1']], + ['/a/b/c/output2', ['/a/b/c/file1', '/a/b/c/file2']] + ]) +}; + +const win32BuildInfo: IIncrementalBuildInfo = { + configHash: 'foobar', + inputFileVersions: new Map([ + ['A:\\b\\c\\file1', '1'], + ['A:\\b\\c\\file2', '2'] + ]), + fileDependencies: new Map([ + ['A:\\b\\c\\output1', ['A:\\b\\c\\file1']], + ['A:\\b\\c\\output2', ['A:\\b\\c\\file1', 'A:\\b\\c\\file2']] + ]) +}; + +const posixBasePath: string = '/a/b/temp'; +const win32BasePath: string = 'A:\\b\\temp'; + +function posixToPortable(absolutePath: string): string { + return path.posix.relative(posixBasePath, absolutePath); +} +function portableToPosix(portablePath: string): string { + return path.posix.resolve(posixBasePath, portablePath); +} + +function win32ToPortable(absolutePath: string): string { + return Path.convertToSlashes(path.win32.relative(win32BasePath, absolutePath)); +} +function portableToWin32(portablePath: string): string { + return path.win32.resolve(win32BasePath, portablePath); +} + +describe(serializeBuildInfo.name, () => { + it('Round trips correctly (POSIX)', () => { + const serialized: ISerializedIncrementalBuildInfo = serializeBuildInfo(posixBuildInfo, posixToPortable); + + const deserialized: IIncrementalBuildInfo = deserializeBuildInfo(serialized, portableToPosix); + + expect(deserialized).toEqual(posixBuildInfo); + }); + + it('Round trips correctly (Win32)', () => { + const serialized: ISerializedIncrementalBuildInfo = serializeBuildInfo(win32BuildInfo, win32ToPortable); + + const deserialized: IIncrementalBuildInfo = deserializeBuildInfo(serialized, portableToWin32); + + expect(deserialized).toEqual(win32BuildInfo); + }); + + it('Converts (POSIX to Win32)', () => { + const serialized: ISerializedIncrementalBuildInfo = serializeBuildInfo(posixBuildInfo, posixToPortable); + + const deserialized: IIncrementalBuildInfo = deserializeBuildInfo(serialized, portableToWin32); + + expect(deserialized).toEqual(win32BuildInfo); + }); + + it('Converts (Win32 to POSIX)', () => { + const serialized: ISerializedIncrementalBuildInfo = serializeBuildInfo(win32BuildInfo, win32ToPortable); + + const deserialized: IIncrementalBuildInfo = deserializeBuildInfo(serialized, portableToPosix); + + expect(deserialized).toEqual(posixBuildInfo); + }); + + it('Has expected serialized format', () => { + const serializedPosix: ISerializedIncrementalBuildInfo = serializeBuildInfo( + posixBuildInfo, + posixToPortable + ); + const serializedWin32: ISerializedIncrementalBuildInfo = serializeBuildInfo( + win32BuildInfo, + win32ToPortable + ); + + expect(serializedPosix).toMatchSnapshot('posix'); + expect(serializedWin32).toMatchSnapshot('win32'); + + expect(serializedPosix).toEqual(serializedWin32); + }); +}); diff --git a/apps/heft/src/pluginFramework/tests/__snapshots__/IncrementalBuildInfo.test.ts.snap b/apps/heft/src/pluginFramework/tests/__snapshots__/IncrementalBuildInfo.test.ts.snap new file mode 100644 index 00000000000..1803097905b --- /dev/null +++ b/apps/heft/src/pluginFramework/tests/__snapshots__/IncrementalBuildInfo.test.ts.snap @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`serializeBuildInfo Has expected serialized format: posix 1`] = ` +Object { + "configHash": "foobar", + "fileDependencies": Object { + "../c/output1": Array [ + 0, + ], + "../c/output2": Array [ + 0, + 1, + ], + }, + "inputFileVersions": Object { + "../c/file1": "1", + "../c/file2": "2", + }, +} +`; + +exports[`serializeBuildInfo Has expected serialized format: win32 1`] = ` +Object { + "configHash": "foobar", + "fileDependencies": Object { + "../c/output1": Array [ + 0, + ], + "../c/output2": Array [ + 0, + 1, + ], + }, + "inputFileVersions": Object { + "../c/file1": "1", + "../c/file2": "2", + }, +} +`; diff --git a/apps/heft/src/plugins/CopyFilesPlugin.ts b/apps/heft/src/plugins/CopyFilesPlugin.ts index 7efa77ac58b..2c9d47347a1 100644 --- a/apps/heft/src/plugins/CopyFilesPlugin.ts +++ b/apps/heft/src/plugins/CopyFilesPlugin.ts @@ -1,15 +1,29 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { AlreadyExistsBehavior, FileSystem, Async, ITerminal } from '@rushstack/node-core-library'; +import { createHash } from 'node:crypto'; +import type * as fs from 'node:fs'; +import * as path from 'node:path'; + +import { AlreadyExistsBehavior, FileSystem, Async } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; import { Constants } from '../utilities/Constants'; -import { getFilePathsAsync, type IFileSelectionSpecifier } from './FileGlobSpecifier'; +import { + asAbsoluteFileSelectionSpecifier, + getFileSelectionSpecifierPathsAsync, + type IFileSelectionSpecifier +} from './FileGlobSpecifier'; import type { HeftConfiguration } from '../configuration/HeftConfiguration'; import type { IHeftTaskPlugin } from '../pluginFramework/IHeftPlugin'; import type { IHeftTaskSession, IHeftTaskFileOperations } from '../pluginFramework/HeftTaskSession'; -import { WatchFileSystemAdapter } from '../utilities/WatchFileSystemAdapter'; +import type { WatchFileSystemAdapter } from '../utilities/WatchFileSystemAdapter'; +import { + type IIncrementalBuildInfo, + makePathRelative, + tryReadBuildInfoAsync, + writeBuildInfoAsync +} from '../pluginFramework/IncrementalBuildInfo'; /** * Used to specify a selection of files to copy from a specific source folder to one @@ -66,9 +80,38 @@ interface ICopyDescriptor { hardlink: boolean; } +export function asAbsoluteCopyOperation( + rootFolderPath: string, + copyOperation: ICopyOperation +): ICopyOperation { + const absoluteCopyOperation: ICopyOperation = asAbsoluteFileSelectionSpecifier( + rootFolderPath, + copyOperation + ); + absoluteCopyOperation.destinationFolders = copyOperation.destinationFolders.map((folder) => + path.resolve(rootFolderPath, folder) + ); + return absoluteCopyOperation; +} + +export function asRelativeCopyOperation( + rootFolderPath: string, + copyOperation: ICopyOperation +): ICopyOperation { + return { + ...copyOperation, + destinationFolders: copyOperation.destinationFolders.map((folder) => + makePathRelative(folder, rootFolderPath) + ), + sourcePath: copyOperation.sourcePath && makePathRelative(copyOperation.sourcePath, rootFolderPath) + }; +} + export async function copyFilesAsync( copyOperations: Iterable, terminal: ITerminal, + buildInfoPath: string, + configHash: string, watchFileSystemAdapter?: WatchFileSystemAdapter ): Promise { const copyDescriptorByDestination: Map = await _getCopyDescriptorsAsync( @@ -76,12 +119,12 @@ export async function copyFilesAsync( watchFileSystemAdapter ); - await _copyFilesInnerAsync(copyDescriptorByDestination, terminal); + await _copyFilesInnerAsync(copyDescriptorByDestination, configHash, buildInfoPath, terminal); } async function _getCopyDescriptorsAsync( copyConfigurations: Iterable, - fs: WatchFileSystemAdapter | undefined + fileSystemAdapter: WatchFileSystemAdapter | undefined ): Promise> { // Create a map to deduplicate and prevent double-writes // resolvedDestinationFilePath -> descriptor @@ -91,19 +134,23 @@ async function _getCopyDescriptorsAsync( copyConfigurations, async (copyConfiguration: ICopyOperation) => { // "sourcePath" is required to be a folder. To copy a single file, put the parent folder in "sourcePath" - // and the filename in "includeGlobs" - const sourceFolder: string | undefined = copyConfiguration.sourcePath; - const sourceFilePaths: Set | undefined = await getFilePathsAsync(copyConfiguration, fs); + // and the filename in "includeGlobs". + const sourceFolder: string = copyConfiguration.sourcePath!; + const sourceFiles: Map = await getFileSelectionSpecifierPathsAsync({ + fileGlobSpecifier: copyConfiguration, + fileSystemAdapter + }); // Dedupe and throw if a double-write is detected for (const destinationFolderPath of copyConfiguration.destinationFolders) { - for (const sourceFilePath of sourceFilePaths!) { + // We only need to care about the keys of the map since we know all the keys are paths to files + for (const sourceFilePath of sourceFiles.keys()) { // Only include the relative path from the sourceFolder if flatten is false const resolvedDestinationPath: string = path.resolve( destinationFolderPath, copyConfiguration.flatten ? path.basename(sourceFilePath) - : path.relative(sourceFolder!, sourceFilePath) + : path.relative(sourceFolder, sourceFilePath) ); // Throw if a duplicate copy target with a different source or options is specified @@ -141,16 +188,71 @@ async function _getCopyDescriptorsAsync( async function _copyFilesInnerAsync( copyDescriptors: Map, + configHash: string, + buildInfoPath: string, terminal: ITerminal ): Promise { if (copyDescriptors.size === 0) { return; } - let copiedFolderOrFileCount: number = 0; + let oldBuildInfo: IIncrementalBuildInfo | undefined = await tryReadBuildInfoAsync(buildInfoPath); + if (oldBuildInfo && oldBuildInfo.configHash !== configHash) { + terminal.writeVerboseLine(`File copy configuration changed, discarding incremental state.`); + oldBuildInfo = undefined; + } + + // Since in watch mode only changed files will get passed in, need to ensure that all files from + // the previous build are still tracked. + const inputFileVersions: Map = new Map(oldBuildInfo?.inputFileVersions); + + const buildInfo: IIncrementalBuildInfo = { + configHash, + inputFileVersions + }; + + const allInputFiles: Set = new Set(); + for (const copyDescriptor of copyDescriptors.values()) { + allInputFiles.add(copyDescriptor.sourcePath); + } + + await Async.forEachAsync( + allInputFiles, + async (inputFilePath: string) => { + const fileContent: Buffer = await FileSystem.readFileToBufferAsync(inputFilePath); + const fileHash: string = createHash('sha256').update(fileContent).digest('base64'); + inputFileVersions.set(inputFilePath, fileHash); + }, + { + concurrency: Constants.maxParallelism + } + ); + + const copyDescriptorsWithWork: ICopyDescriptor[] = []; + for (const copyDescriptor of copyDescriptors.values()) { + const { sourcePath } = copyDescriptor; + + const sourceFileHash: string | undefined = inputFileVersions.get(sourcePath); + if (!sourceFileHash) { + throw new Error(`Missing hash for input file: ${sourcePath}`); + } + + if (oldBuildInfo?.inputFileVersions.get(sourcePath) === sourceFileHash) { + continue; + } + + copyDescriptorsWithWork.push(copyDescriptor); + } + + if (copyDescriptorsWithWork.length === 0) { + terminal.writeLine('All requested file copy operations are up to date. Nothing to do.'); + return; + } + + let copiedFileCount: number = 0; let linkedFileCount: number = 0; await Async.forEachAsync( - copyDescriptors.values(), + copyDescriptorsWithWork, async (copyDescriptor: ICopyDescriptor) => { if (copyDescriptor.hardlink) { linkedFileCount++; @@ -163,7 +265,7 @@ async function _copyFilesInnerAsync( `Linked "${copyDescriptor.sourcePath}" to "${copyDescriptor.destinationPath}".` ); } else { - copiedFolderOrFileCount++; + copiedFileCount++; await FileSystem.copyFilesAsync({ sourcePath: copyDescriptor.sourcePath, destinationPath: copyDescriptor.destinationPath, @@ -177,30 +279,12 @@ async function _copyFilesInnerAsync( { concurrency: Constants.maxParallelism } ); - const folderOrFilesPlural: string = copiedFolderOrFileCount === 1 ? '' : 's'; terminal.writeLine( - `Copied ${copiedFolderOrFileCount} folder${folderOrFilesPlural} or file${folderOrFilesPlural} and ` + + `Copied ${copiedFileCount} file${copiedFileCount === 1 ? '' : 's'} and ` + `linked ${linkedFileCount} file${linkedFileCount === 1 ? '' : 's'}` ); -} - -function* _resolveCopyOperationPaths( - heftConfiguration: HeftConfiguration, - copyOperations: Iterable -): IterableIterator { - const { buildFolderPath } = heftConfiguration; - function resolvePath(inputPath: string | undefined): string { - return inputPath ? path.resolve(buildFolderPath, inputPath) : buildFolderPath; - } - - for (const copyOperation of copyOperations) { - yield { - ...copyOperation, - sourcePath: resolvePath(copyOperation.sourcePath), - destinationFolders: copyOperation.destinationFolders.map(resolvePath) - }; - } + await writeBuildInfoAsync(buildInfo, buildInfoPath); } const PLUGIN_NAME: 'copy-files-plugin' = 'copy-files-plugin'; @@ -214,7 +298,7 @@ export default class CopyFilesPlugin implements IHeftTaskPlugin { - for (const operation of _resolveCopyOperationPaths(heftConfiguration, pluginOptions.copyOperations)) { + for (const operation of pluginOptions.copyOperations) { operations.copyOperations.add(operation); } return operations; diff --git a/apps/heft/src/plugins/DeleteFilesPlugin.ts b/apps/heft/src/plugins/DeleteFilesPlugin.ts index 711f2f7d44d..73976dad8f4 100644 --- a/apps/heft/src/plugins/DeleteFilesPlugin.ts +++ b/apps/heft/src/plugins/DeleteFilesPlugin.ts @@ -1,13 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { FileSystem, Async, ITerminal } from '@rushstack/node-core-library'; +import type * as fs from 'node:fs'; + +import { FileSystem, Async } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; import { Constants } from '../utilities/Constants'; import { - getFilePathsAsync, - normalizeFileSelectionSpecifier, + getFileSelectionSpecifierPathsAsync, + asAbsoluteFileSelectionSpecifier, type IFileSelectionSpecifier } from './FileGlobSpecifier'; import type { HeftConfiguration } from '../configuration/HeftConfiguration'; @@ -25,46 +27,73 @@ interface IDeleteFilesPluginOptions { deleteOperations: IDeleteOperation[]; } -async function _getPathsToDeleteAsync(deleteOperations: Iterable): Promise> { - const pathsToDelete: Set = new Set(); +interface IGetPathsToDeleteResult { + filesToDelete: Set; + foldersToDelete: Set; +} + +async function _getPathsToDeleteAsync( + rootFolderPath: string, + deleteOperations: Iterable +): Promise { + const result: IGetPathsToDeleteResult = { + filesToDelete: new Set(), + foldersToDelete: new Set() + }; + await Async.forEachAsync( deleteOperations, async (deleteOperation: IDeleteOperation) => { - if ( - !deleteOperation.fileExtensions?.length && - !deleteOperation.includeGlobs?.length && - !deleteOperation.excludeGlobs?.length - ) { - // If no globs or file extensions are provided add the path to the set of paths to delete - pathsToDelete.add(deleteOperation.sourcePath); - } else { - normalizeFileSelectionSpecifier(deleteOperation); - // Glob the files under the source path and add them to the set of files to delete - const sourceFilePaths: Set = await getFilePathsAsync(deleteOperation); - for (const sourceFilePath of sourceFilePaths) { - pathsToDelete.add(sourceFilePath); + const absoluteSpecifier: IDeleteOperation = asAbsoluteFileSelectionSpecifier( + rootFolderPath, + deleteOperation + ); + + // Glob the files under the source path and add them to the set of files to delete + const sourcePaths: Map = await getFileSelectionSpecifierPathsAsync({ + fileGlobSpecifier: absoluteSpecifier, + includeFolders: true + }); + for (const [sourcePath, dirent] of sourcePaths) { + // If the sourcePath is a folder, add it to the foldersToDelete set. Otherwise, add it to + // the filesToDelete set. Symlinks and junctions are treated as files, and thus will fall + // into the filesToDelete set. + if (dirent.isDirectory()) { + result.foldersToDelete.add(sourcePath); + } else { + result.filesToDelete.add(sourcePath); } } }, { concurrency: Constants.maxParallelism } ); - return pathsToDelete; + return result; } export async function deleteFilesAsync( + rootFolderPath: string, deleteOperations: Iterable, terminal: ITerminal ): Promise { - const pathsToDelete: Set = await _getPathsToDeleteAsync(deleteOperations); + const pathsToDelete: IGetPathsToDeleteResult = await _getPathsToDeleteAsync( + rootFolderPath, + deleteOperations + ); await _deleteFilesInnerAsync(pathsToDelete, terminal); } -async function _deleteFilesInnerAsync(pathsToDelete: Set, terminal: ITerminal): Promise { +async function _deleteFilesInnerAsync( + pathsToDelete: IGetPathsToDeleteResult, + terminal: ITerminal +): Promise { let deletedFiles: number = 0; let deletedFolders: number = 0; + + const { filesToDelete, foldersToDelete } = pathsToDelete; + await Async.forEachAsync( - pathsToDelete, + filesToDelete, async (pathToDelete: string) => { try { await FileSystem.deleteFileAsync(pathToDelete, { throwIfNotExists: true }); @@ -73,16 +102,38 @@ async function _deleteFilesInnerAsync(pathsToDelete: Set, terminal: ITer } catch (error) { // If it doesn't exist, we can ignore the error. if (!FileSystem.isNotExistError(error)) { - // When we encounter an error relating to deleting a directory as if it was a file, - // attempt to delete the folder. Windows throws the unlink not permitted error, while - // linux throws the EISDIR error. - if (FileSystem.isUnlinkNotPermittedError(error) || FileSystem.isDirectoryError(error)) { - await FileSystem.deleteFolderAsync(pathToDelete); - terminal.writeVerboseLine(`Deleted folder "${pathToDelete}".`); - deletedFolders++; - } else { - throw error; - } + throw error; + } + } + }, + { concurrency: Constants.maxParallelism } + ); + + // Reverse the list of matching folders. Assuming that the list of folders came from + // the globber, the folders will be specified in tree-walk order, so by reversing the + // list we delete the deepest folders first and avoid not-exist errors for subfolders + // of an already-deleted parent folder. + const reversedFoldersToDelete: string[] = Array.from(foldersToDelete).reverse(); + + // Clear out any folders that were encountered during the file deletion process. This + // will recursively delete the folder and it's contents. There are two scenarios that + // this handles: + // - Deletions of empty folder structures (ex. when the delete glob is '**/*') + // - Deletions of folders that still contain files (ex. when the delete glob is 'lib') + // In the latter scenario, the count of deleted files will not be tracked. However, + // this is a fair trade-off for the performance benefit of not having to glob the + // folder structure again. + await Async.forEachAsync( + reversedFoldersToDelete, + async (folderToDelete: string) => { + try { + await FileSystem.deleteFolderAsync(folderToDelete); + terminal.writeVerboseLine(`Deleted folder "${folderToDelete}".`); + deletedFolders++; + } catch (error) { + // If it doesn't exist, we can ignore the error. + if (!FileSystem.isNotExistError(error)) { + throw error; } } }, @@ -97,20 +148,6 @@ async function _deleteFilesInnerAsync(pathsToDelete: Set, terminal: ITer } } -function* _resolveDeleteOperationPaths( - heftConfiguration: HeftConfiguration, - deleteOperations: Iterable -): IterableIterator { - const { buildFolderPath } = heftConfiguration; - for (const deleteOperation of deleteOperations) { - const { sourcePath } = deleteOperation; - yield { - ...deleteOperation, - sourcePath: sourcePath ? path.resolve(buildFolderPath, sourcePath) : buildFolderPath - }; - } -} - const PLUGIN_NAME: 'delete-files-plugin' = 'delete-files-plugin'; export default class DeleteFilesPlugin implements IHeftTaskPlugin { @@ -122,10 +159,7 @@ export default class DeleteFilesPlugin implements IHeftTaskPlugin { - for (const deleteOperation of _resolveDeleteOperationPaths( - heftConfiguration, - pluginOptions.deleteOperations - )) { + for (const deleteOperation of pluginOptions.deleteOperations) { fileOperations.deleteOperations.add(deleteOperation); } return fileOperations; diff --git a/apps/heft/src/plugins/FileGlobSpecifier.ts b/apps/heft/src/plugins/FileGlobSpecifier.ts index a4a6fe3658e..96d3223dd5a 100644 --- a/apps/heft/src/plugins/FileGlobSpecifier.ts +++ b/apps/heft/src/plugins/FileGlobSpecifier.ts @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import glob, { FileSystemAdapter } from 'fast-glob'; +import type * as fs from 'node:fs'; +import * as path from 'node:path'; + +import glob, { type FileSystemAdapter, type Entry } from 'fast-glob'; import { Async } from '@rushstack/node-core-library'; @@ -19,7 +21,7 @@ export interface IFileSelectionSpecifier { * fileExtensions, excludeGlobs, or includeGlobs are specified, the sourcePath is assumed * to be a folder. If it is not a folder, an error will be thrown. */ - sourcePath: string; + sourcePath?: string; /** * File extensions that should be included from the source folder. Only supported when the sourcePath @@ -71,6 +73,12 @@ export interface IGlobOptions { dot?: boolean; } +export interface IGetFileSelectionSpecifierPathsOptions { + fileGlobSpecifier: IFileSelectionSpecifier; + includeFolders?: boolean; + fileSystemAdapter?: FileSystemAdapter; +} + /** * Glob a set of files and return a list of paths that match the provided patterns. * @@ -129,40 +137,59 @@ export async function watchGlobAsync( return results; } -export async function getFilePathsAsync( - fileGlobSpecifier: IFileSelectionSpecifier, - fs?: FileSystemAdapter -): Promise> { - const rawFiles: string[] = await glob(fileGlobSpecifier.includeGlobs!, { - fs, +export async function getFileSelectionSpecifierPathsAsync( + options: IGetFileSelectionSpecifierPathsOptions +): Promise> { + const { fileGlobSpecifier, includeFolders, fileSystemAdapter } = options; + const rawEntries: Entry[] = await glob(fileGlobSpecifier.includeGlobs!, { + fs: fileSystemAdapter, cwd: fileGlobSpecifier.sourcePath, ignore: fileGlobSpecifier.excludeGlobs, + onlyFiles: !includeFolders, dot: true, - absolute: true + absolute: true, + objectMode: true }); - if (fs && isWatchFileSystemAdapter(fs)) { - const changedFiles: Set = new Set(); + let results: Map; + if (fileSystemAdapter && isWatchFileSystemAdapter(fileSystemAdapter)) { + results = new Map(); await Async.forEachAsync( - rawFiles, - async (file: string) => { - const state: IWatchedFileState = await fs.getStateAndTrackAsync(path.normalize(file)); + rawEntries, + async (entry: Entry) => { + const { path: filePath, dirent } = entry; + if (entry.dirent.isDirectory()) { + return; + } + const state: IWatchedFileState = await fileSystemAdapter.getStateAndTrackAsync( + path.normalize(filePath) + ); if (state.changed) { - changedFiles.add(file); + results.set(filePath, dirent as fs.Dirent); } }, { concurrency: 20 } ); - return changedFiles; + } else { + results = new Map(rawEntries.map((entry) => [entry.path, entry.dirent as fs.Dirent])); } - return new Set(rawFiles); + return results; } -export function normalizeFileSelectionSpecifier(fileGlobSpecifier: IFileSelectionSpecifier): void { - fileGlobSpecifier.includeGlobs = getIncludedGlobPatterns(fileGlobSpecifier); +export function asAbsoluteFileSelectionSpecifier( + rootPath: string, + fileGlobSpecifier: TSpecifier +): TSpecifier { + const { sourcePath } = fileGlobSpecifier; + return { + ...fileGlobSpecifier, + sourcePath: sourcePath ? path.resolve(rootPath, sourcePath) : rootPath, + includeGlobs: getIncludedGlobPatterns(fileGlobSpecifier), + fileExtensions: undefined + }; } function getIncludedGlobPatterns(fileGlobSpecifier: IFileSelectionSpecifier): string[] { diff --git a/apps/heft/src/plugins/NodeServicePlugin.ts b/apps/heft/src/plugins/NodeServicePlugin.ts index f0d3223b93f..40b2753f55f 100644 --- a/apps/heft/src/plugins/NodeServicePlugin.ts +++ b/apps/heft/src/plugins/NodeServicePlugin.ts @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as child_process from 'child_process'; -import * as process from 'process'; +import * as child_process from 'node:child_process'; +import * as process from 'node:process'; + import { InternalError, SubprocessTerminator } from '@rushstack/node-core-library'; import type { IHeftTaskPlugin } from '../pluginFramework/IHeftPlugin'; @@ -15,6 +16,9 @@ import type { IScopedLogger } from '../pluginFramework/logging/ScopedLogger'; import { CoreConfigFiles } from '../utilities/CoreConfigFiles'; const PLUGIN_NAME: 'node-service-plugin' = 'node-service-plugin'; +const SERVE_PARAMETER_LONG_NAME: '--serve' = '--serve'; + +const _isWindows: boolean = process.platform === 'win32'; export interface INodeServicePluginCompleteConfiguration { commandName: string; @@ -54,8 +58,6 @@ enum State { } export default class NodeServicePlugin implements IHeftTaskPlugin { - private static readonly _isWindows: boolean = process.platform === 'win32'; - private _activeChildProcess: child_process.ChildProcess | undefined; private _childProcessExitPromise: Promise | undefined; private _childProcessExitPromiseResolveFn: (() => void) | undefined; @@ -92,9 +94,24 @@ export default class NodeServicePlugin implements IHeftTaskPlugin { // Set this immediately to make it available to the internal methods that use it this._logger = taskSession.logger; - taskSession.hooks.run.tapPromise(PLUGIN_NAME, async () => { - taskSession.logger.terminal.writeWarningLine('Node services can only be run in watch mode.'); - }); + const isServeMode: boolean = taskSession.parameters.getFlagParameter(SERVE_PARAMETER_LONG_NAME).value; + + if (isServeMode && !taskSession.parameters.watch) { + throw new Error( + `The ${JSON.stringify( + SERVE_PARAMETER_LONG_NAME + )} parameter is only available when running in watch mode.` + + ` Try replacing "${taskSession.parsedCommandLine?.unaliasedCommandName}" with` + + ` "${taskSession.parsedCommandLine?.unaliasedCommandName}-watch" in your Heft command line.` + ); + } + + if (!isServeMode) { + taskSession.logger.terminal.writeVerboseLine( + `Not launching the service because the "${SERVE_PARAMETER_LONG_NAME}" parameter was not specified` + ); + return; + } taskSession.hooks.runIncremental.tapPromise( PLUGIN_NAME, @@ -104,17 +121,16 @@ export default class NodeServicePlugin implements IHeftTaskPlugin { ); } - private async _loadStageConfiguration( + private async _loadStageConfigurationAsync( taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration ): Promise { if (!this._rawConfiguration) { - this._rawConfiguration = - await CoreConfigFiles.nodeServiceConfigurationFile.tryLoadConfigurationFileForProjectAsync( - taskSession.logger.terminal, - heftConfiguration.buildFolderPath, - heftConfiguration.rigConfig - ); + this._rawConfiguration = await CoreConfigFiles.tryLoadNodeServiceConfigurationFileAsync( + taskSession.logger.terminal, + heftConfiguration.buildFolderPath, + heftConfiguration.rigConfig + ); // defaults this._configuration = { @@ -148,21 +164,21 @@ export default class NodeServicePlugin implements IHeftTaskPlugin { if (this._shellCommand === undefined) { if (this._configuration.ignoreMissingScript) { taskSession.logger.terminal.writeLine( - `The plugin is disabled because the project's package.json` + + `The node service cannot be started because the project's package.json` + ` does not have a "${this._configuration.commandName}" script` ); } else { throw new Error( - `The node-service task cannot start because the project's package.json ` + + `The node service cannot be started because the project's package.json ` + `does not have a "${this._configuration.commandName}" script` ); } this._pluginEnabled = false; } } else { - taskSession.logger.terminal.writeVerboseLine( - 'The plugin is disabled because its config file was not found: ' + - CoreConfigFiles.nodeServiceConfigurationFile.projectRelativeFilePath + throw new Error( + 'The node service cannot be started because the task config file was not found: ' + + CoreConfigFiles.nodeServiceConfigurationProjectRelativeFilePath ); } } @@ -172,7 +188,7 @@ export default class NodeServicePlugin implements IHeftTaskPlugin { taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration ): Promise { - await this._loadStageConfiguration(taskSession, heftConfiguration); + await this._loadStageConfigurationAsync(taskSession, heftConfiguration); if (!this._pluginEnabled) { return; } @@ -192,7 +208,7 @@ export default class NodeServicePlugin implements IHeftTaskPlugin { return; } - if (NodeServicePlugin._isWindows) { + if (_isWindows) { // On Windows, SIGTERM can kill Cmd.exe and leave its children running in the background this._transitionToKilling(); } else { @@ -206,7 +222,12 @@ export default class NodeServicePlugin implements IHeftTaskPlugin { // Passing a negative PID terminates the entire group instead of just the one process. // This works because we set detached=true for child_process.spawn() - process.kill(-this._activeChildProcess.pid, 'SIGTERM'); + + const pid: number | undefined = this._activeChildProcess.pid; + if (pid !== undefined) { + // If pid was undefined, the process failed to spawn + process.kill(-pid, 'SIGTERM'); + } this._clearTimeout(); this._timeout = setTimeout(() => { @@ -274,7 +295,10 @@ export default class NodeServicePlugin implements IHeftTaskPlugin { }); SubprocessTerminator.killProcessTreeOnExit(childProcess, SubprocessTerminator.RECOMMENDED_OPTIONS); - const childPid: number = childProcess.pid; + const childPid: number | undefined = childProcess.pid; + if (childPid === undefined) { + throw new InternalError(`Failed to spawn child process`); + } this._logger.terminal.writeVerboseLine(`Started service process #${childPid}`); // Create a promise that resolves when the child process exits @@ -290,7 +314,7 @@ export default class NodeServicePlugin implements IHeftTaskPlugin { this._logger.terminal.writeError(data.toString()); }); - childProcess.on('close', (code: number, signal: string): void => { + childProcess.on('close', (exitCode: number | null, signal: NodeJS.Signals | null): void => { try { // The 'close' event is emitted after a process has ended and the stdio streams of a child process // have been closed. This is distinct from the 'exit' event, since multiple processes might share the @@ -300,7 +324,7 @@ export default class NodeServicePlugin implements IHeftTaskPlugin { if (this._state === State.Running) { this._logger.terminal.writeWarningLine( `The service process #${childPid} terminated unexpectedly` + - this._formatCodeOrSignal(code, signal) + this._formatCodeOrSignal(exitCode, signal) ); this._transitionToStopped(); return; @@ -309,7 +333,7 @@ export default class NodeServicePlugin implements IHeftTaskPlugin { if (this._state === State.Stopping || this._state === State.Killing) { this._logger.terminal.writeVerboseLine( `The service process #${childPid} terminated successfully` + - this._formatCodeOrSignal(code, signal) + this._formatCodeOrSignal(exitCode, signal) ); this._transitionToStopped(); return; @@ -321,6 +345,8 @@ export default class NodeServicePlugin implements IHeftTaskPlugin { childProcess.on('exit', (code: number | null, signal: string | null) => { try { + // Under normal conditions we don't reject the promise here, because 'data' events can continue + // to fire as data is flushed, before finally concluding with the 'close' event. this._logger.terminal.writeVerboseLine( `The service process fired its "exit" event` + this._formatCodeOrSignal(code, signal) ); diff --git a/apps/heft/src/plugins/RunScriptPlugin.ts b/apps/heft/src/plugins/RunScriptPlugin.ts index 11430e11eb1..6c74d9dfd56 100644 --- a/apps/heft/src/plugins/RunScriptPlugin.ts +++ b/apps/heft/src/plugins/RunScriptPlugin.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import * as path from 'node:path'; + import type { HeftConfiguration } from '../configuration/HeftConfiguration'; import type { IHeftTaskPlugin } from '../pluginFramework/IHeftPlugin'; import type { IHeftTaskSession, IHeftTaskRunHookOptions } from '../pluginFramework/HeftTaskSession'; @@ -53,10 +55,10 @@ export default class RunScriptPlugin implements IHeftTaskPlugin { - // The scriptPath property should be fully resolved since it is included in the resolution logic used by - // HeftConfiguration - const resolvedModulePath: string = pluginOptions.scriptPath; - + const resolvedModulePath: string = path.resolve( + heftConfiguration.buildFolderPath, + pluginOptions.scriptPath + ); const runScript: IRunScript = await import(resolvedModulePath); if (!runScript.runAsync) { throw new Error( diff --git a/apps/heft/src/plugins/SetEnvironmentVariablesPlugin.ts b/apps/heft/src/plugins/SetEnvironmentVariablesPlugin.ts new file mode 100644 index 00000000000..fb83903b83b --- /dev/null +++ b/apps/heft/src/plugins/SetEnvironmentVariablesPlugin.ts @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { HeftConfiguration } from '../configuration/HeftConfiguration'; +import type { IHeftTaskSession } from '../pluginFramework/HeftTaskSession'; +import type { IHeftTaskPlugin } from '../pluginFramework/IHeftPlugin'; + +export const PLUGIN_NAME: string = 'set-environment-variables-plugin'; + +export interface ISetEnvironmentVariablesPluginOptions { + environmentVariablesToSet: Record; +} + +export default class SetEnvironmentVariablesPlugin + implements IHeftTaskPlugin +{ + public apply( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + { environmentVariablesToSet }: ISetEnvironmentVariablesPluginOptions + ): void { + taskSession.hooks.run.tap( + { + name: PLUGIN_NAME, + stage: Number.MIN_SAFE_INTEGER + }, + () => { + for (const [key, value] of Object.entries(environmentVariablesToSet)) { + taskSession.logger.terminal.writeLine(`Setting environment variable ${key}=${value}`); + process.env[key] = value; + } + } + ); + } +} diff --git a/apps/heft/src/schemas/copy-files-options.schema.json b/apps/heft/src/schemas/copy-files-options.schema.json index c52a19a8e77..f42d46af61c 100644 --- a/apps/heft/src/schemas/copy-files-options.schema.json +++ b/apps/heft/src/schemas/copy-files-options.schema.json @@ -15,20 +15,13 @@ "items": { "type": "object", "additionalProperties": false, + "required": ["destinationFolders"], "anyOf": [ - { - "required": ["sourcePath"] - }, - { - "required": ["includeGlobs"] - }, - { - "required": ["fileExtensions"] - } + { "required": ["sourcePath"] }, + { "required": ["fileExtensions"] }, + { "required": ["includeGlobs"] }, + { "required": ["excludeGlobs"] } ], - - "required": ["destinationFolders"], - "properties": { "sourcePath": { "title": "Source Path", diff --git a/apps/heft/src/schemas/delete-files-options.schema.json b/apps/heft/src/schemas/delete-files-options.schema.json index d248396ea8c..0e6c0a9ccae 100644 --- a/apps/heft/src/schemas/delete-files-options.schema.json +++ b/apps/heft/src/schemas/delete-files-options.schema.json @@ -16,17 +16,11 @@ "type": "object", "additionalProperties": false, "anyOf": [ - { - "required": ["sourcePath"] - }, - { - "required": ["includeGlobs"] - }, - { - "required": ["fileExtensions"] - } + { "required": ["sourcePath"] }, + { "required": ["fileExtensions"] }, + { "required": ["includeGlobs"] }, + { "required": ["excludeGlobs"] } ], - "properties": { "sourcePath": { "title": "Source Path", diff --git a/apps/heft/src/schemas/heft-legacy.schema.json b/apps/heft/src/schemas/heft-legacy.schema.json new file mode 100644 index 00000000000..45d26aa00f0 --- /dev/null +++ b/apps/heft/src/schemas/heft-legacy.schema.json @@ -0,0 +1,211 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Legacy Heft Configuration", + "description": "Defines configuration used by the legacy version of Heft.", + "type": "object", + + "definitions": { + "anything": { + "type": ["array", "boolean", "integer", "number", "object", "string"], + "items": { "$ref": "#/definitions/anything" } + } + }, + + "additionalProperties": false, + + "properties": { + "$schema": { + "description": "Part of the JSON Schema standard, this optional keyword declares the URL of the schema that the file conforms to. Editors may download the schema and use it to perform syntax highlighting.", + "type": "string" + }, + + "extends": { + "description": "Optionally specifies another JSON config file that this file extends from. This provides a way for standard settings to be shared across multiple projects. To delete an inherited setting, set it to `null` in this file.", + "type": "string" + }, + + "eventActions": { + "type": "array", + "description": "An array of actions (such as deleting files or folders) that should occur during a Heft run.", + + "items": { + "type": "object", + "required": ["actionKind", "heftEvent", "actionId"], + "allOf": [ + { + "properties": { + "actionKind": { + "type": "string", + "description": "The kind of built-in operation that should be performed.", + "enum": ["deleteGlobs", "copyFiles", "runScript"] + }, + + "heftEvent": { + "type": "string", + "description": "The Heft stage when this action should be performed. Note that heft.json event actions are scheduled after any plugin tasks have processed the event. For example, a \"compile\" event action will be performed after the TypeScript compiler has been invoked.", + "enum": ["clean", "pre-compile", "compile", "bundle", "post-build", "test"] + }, + + "actionId": { + "type": "string", + "description": "A user-defined tag whose purpose is to allow configs to replace/delete handlers that were added by other configs." + } + } + }, + { + "oneOf": [ + { + "required": ["globsToDelete"], + "properties": { + "actionKind": { + "type": "string", + "enum": ["deleteGlobs"] + }, + + "heftEvent": { + "type": "string", + "enum": ["clean", "pre-compile", "compile", "bundle", "post-build"] + }, + + "globsToDelete": { + "type": "array", + "description": "Glob patterns to be deleted. The paths are resolved relative to the project folder.", + "items": { + "type": "string", + "pattern": "[^\\\\]" + } + } + } + }, + { + "required": ["copyOperations"], + "properties": { + "actionKind": { + "type": "string", + "enum": ["copyFiles"] + }, + + "heftEvent": { + "type": "string", + "enum": ["pre-compile", "compile", "bundle", "post-build"] + }, + + "copyOperations": { + "type": "array", + "description": "An array of copy operations to run perform during the specified Heft event.", + "items": { + "type": "object", + "required": ["sourceFolder", "destinationFolders"], + "properties": { + "sourceFolder": { + "type": "string", + "description": "The base folder that files will be copied from, relative to the project root. Settings such as \"includeGlobs\" and \"excludeGlobs\" will be resolved relative to this folder. NOTE: Assigning \"sourceFolder\" does not by itself select any files to be copied.", + "pattern": "[^\\\\]" + }, + + "destinationFolders": { + "type": "array", + "description": "One or more folders that files will be copied into, relative to the project root. If you specify more than one destination folder, Heft will read the input files only once, using streams to efficiently write multiple outputs.", + "items": { + "type": "string", + "pattern": "[^\\\\]" + } + }, + + "fileExtensions": { + "type": "array", + "description": "If specified, this option recursively scans all folders under \"sourceFolder\" and includes any files that match the specified extensions. (If \"fileExtensions\" and \"includeGlobs\" are both specified, their selections are added together.)", + "items": { + "type": "string", + "pattern": "^\\.[A-z0-9-_.]*[A-z0-9-_]+$" + } + }, + + "excludeGlobs": { + "type": "array", + "description": "A list of glob patterns that exclude files/folders from being copied. The paths are resolved relative to \"sourceFolder\". These exclusions eliminate items that were selected by the \"includeGlobs\" or \"fileExtensions\" setting.", + "items": { + "type": "string", + "pattern": "[^\\\\]" + } + }, + + "includeGlobs": { + "type": "array", + "description": "A list of glob patterns that select files to be copied. The paths are resolved relative to \"sourceFolder\".", + "items": { + "type": "string", + "pattern": "[^\\\\]" + } + }, + + "flatten": { + "type": "boolean", + "description": "Normally, when files are selected under a child folder, a corresponding folder will be created in the destination folder. Specify flatten=true to discard the source path and copy all matching files to the same folder. If two files have the same name an error will be reported. The default value is false." + }, + + "hardlink": { + "type": "boolean", + "description": "If true, filesystem hard links will be created instead of copying the file. Depending on the operating system, this may be faster. (But note that it may cause unexpected behavior if a tool modifies the link.) The default value is false." + } + } + } + } + } + }, + { + "required": ["scriptPath"], + "properties": { + "actionKind": { + "type": "string", + "enum": ["runScript"] + }, + + "heftEvent": { + "type": "string", + "enum": ["pre-compile", "compile", "bundle", "post-build", "test"] + }, + + "scriptPath": { + "type": "string", + "description": "Path to the script that will be run, relative to the project root.", + "pattern": "[^\\\\]" + }, + + "scriptOptions": { + "type": "object", + "description": "Optional parameters that will be passed to the script at runtime.", + "patternProperties": { + "^.*$": { "$ref": "#/definitions/anything" } + } + } + } + } + ] + } + ] + } + }, + + "heftPlugins": { + "type": "array", + "description": "Defines heft plugins that are used by a project.", + + "items": { + "type": "object", + "required": ["plugin"], + "properties": { + "plugin": { + "description": "Path to the plugin package, relative to the project root.", + "type": "string", + "pattern": "[^\\\\]" + }, + + "options": { + "type": "object" + } + } + } + } + } +} diff --git a/apps/heft/src/schemas/heft-plugin.schema.json b/apps/heft/src/schemas/heft-plugin.schema.json index 8ddf6b2d024..37dd45012d7 100644 --- a/apps/heft/src/schemas/heft-plugin.schema.json +++ b/apps/heft/src/schemas/heft-plugin.schema.json @@ -27,6 +27,12 @@ "type": "string", "pattern": "^-(-[a-z0-9]+)+$" }, + "shortName": { + "title": "Short Name", + "description": "A optional short form of the parameter (e.g. \"-v\" instead of \"--verbose\")", + "type": "string", + "pattern": "^-[a-zA-Z]$" + }, "description": { "title": "Parameter Description", "description": "A detailed description of the parameter, which appears when requesting help for the command (e.g. \"heft phaseName --help my-command\").", @@ -90,6 +96,7 @@ "properties": { "parameterKind": { "$ref": "#/definitions/anything" }, "longName": { "$ref": "#/definitions/anything" }, + "shortName": { "$ref": "#/definitions/anything" }, "description": { "$ref": "#/definitions/anything" }, "required": { "$ref": "#/definitions/anything" }, @@ -145,6 +152,7 @@ "properties": { "parameterKind": { "$ref": "#/definitions/anything" }, "longName": { "$ref": "#/definitions/anything" }, + "shortName": { "$ref": "#/definitions/anything" }, "description": { "$ref": "#/definitions/anything" }, "required": { "$ref": "#/definitions/anything" }, @@ -175,6 +183,7 @@ "properties": { "parameterKind": { "$ref": "#/definitions/anything" }, "longName": { "$ref": "#/definitions/anything" }, + "shortName": { "$ref": "#/definitions/anything" }, "description": { "$ref": "#/definitions/anything" }, "required": { "$ref": "#/definitions/anything" } } @@ -214,6 +223,7 @@ "properties": { "parameterKind": { "$ref": "#/definitions/anything" }, "longName": { "$ref": "#/definitions/anything" }, + "shortName": { "$ref": "#/definitions/anything" }, "description": { "$ref": "#/definitions/anything" }, "required": { "$ref": "#/definitions/anything" }, @@ -251,6 +261,7 @@ "properties": { "parameterKind": { "$ref": "#/definitions/anything" }, "longName": { "$ref": "#/definitions/anything" }, + "shortName": { "$ref": "#/definitions/anything" }, "description": { "$ref": "#/definitions/anything" }, "required": { "$ref": "#/definitions/anything" }, @@ -292,6 +303,7 @@ "properties": { "parameterKind": { "$ref": "#/definitions/anything" }, "longName": { "$ref": "#/definitions/anything" }, + "shortName": { "$ref": "#/definitions/anything" }, "description": { "$ref": "#/definitions/anything" }, "required": { "$ref": "#/definitions/anything" }, @@ -329,6 +341,7 @@ "properties": { "parameterKind": { "$ref": "#/definitions/anything" }, "longName": { "$ref": "#/definitions/anything" }, + "shortName": { "$ref": "#/definitions/anything" }, "description": { "$ref": "#/definitions/anything" }, "required": { "$ref": "#/definitions/anything" }, diff --git a/apps/heft/src/schemas/heft.schema.json b/apps/heft/src/schemas/heft.schema.json index 3b0211d3275..cba91630724 100644 --- a/apps/heft/src/schemas/heft.schema.json +++ b/apps/heft/src/schemas/heft.schema.json @@ -54,6 +54,12 @@ "delete-operation": { "type": "object", "additionalProperties": false, + "anyOf": [ + { "required": ["sourcePath"] }, + { "required": ["fileExtensions"] }, + { "required": ["includeGlobs"] }, + { "required": ["excludeGlobs"] } + ], "properties": { "sourcePath": { "title": "Source Path", @@ -98,7 +104,7 @@ }, "extends": { - "description": "Optionally specifies another JSON config file that this file extends from. This provides a way for standard settings to be shared across multiple projects.", + "description": "Optionally specifies another JSON config file that this file extends from. This provides a way for standard settings to be shared across multiple projects. To delete an inherited setting, set it to `null` in this file.", "type": "string" }, @@ -108,6 +114,34 @@ "items": { "$ref": "#/definitions/heft-plugin" } }, + "aliasesByName": { + "type": "object", + "description": "Defines aliases for existing Heft actions, and allows them to be invoked by name with default parameters.", + "additionalProperties": false, + "patternProperties": { + "^[a-z][a-z0-9]*([-][a-z0-9]+)*$": { + "description": "Defines a Heft action alias.", + "type": "object", + "additionalProperties": false, + "required": ["actionName"], + "properties": { + "actionName": { + "description": "The name of the Heft action to invoke.", + "type": "string", + "pattern": "^[a-z][a-z0-9]*([-][a-z0-9]+)*$" + }, + "defaultParameters": { + "description": "Parameters to pass to the Heft action by default. These parameters will be appended after the specified action and before any user-specified parameters.", + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "phasesByName": { "type": "object", "description": "Heft phases that can be run during an execution of Heft.", @@ -147,29 +181,9 @@ "type": "object", "description": "Defines a Heft task.", "additionalProperties": false, - "oneOf": [ - { - "required": ["taskPlugin"], - "properties": { - "taskPlugin": { "$ref": "#/definitions/heft-plugin" } - } - }, - { - "required": ["taskEvent"], - "properties": { - "taskEvent": { "$ref": "#/definitions/heft-event" } - } - } - ], + "required": ["taskPlugin"], "properties": { - "taskPlugin": { - "description": "A plugin that can be used to extend Heft functionality.", - "type": "object" - }, - "taskEvent": { - "description": "An event that can be used to extend Heft functionality.", - "type": "object" - }, + "taskPlugin": { "$ref": "#/definitions/heft-plugin" }, "taskDependencies": { "type": "array", diff --git a/apps/heft/src/schemas/node-service.schema.json b/apps/heft/src/schemas/node-service.schema.json index ee9700a5308..67af1bc48a9 100644 --- a/apps/heft/src/schemas/node-service.schema.json +++ b/apps/heft/src/schemas/node-service.schema.json @@ -13,7 +13,7 @@ }, "extends": { - "description": "Optionally specifies another JSON config file that this file extends from. This provides a way for standard settings to be shared across multiple projects.", + "description": "Optionally specifies another JSON config file that this file extends from. This provides a way for standard settings to be shared across multiple projects. To delete an inherited setting, set it to `null` in this file.", "type": "string" }, diff --git a/apps/heft/src/schemas/run-script-options.schema.json b/apps/heft/src/schemas/run-script-options.schema.json index be41cfc37b7..1a42badddbb 100644 --- a/apps/heft/src/schemas/run-script-options.schema.json +++ b/apps/heft/src/schemas/run-script-options.schema.json @@ -19,10 +19,7 @@ "title": "Script Path", "type": "string", "description": "Path to the script that will be run, relative to the project root.", - "items": { - "type": "string", - "pattern": "[^\\\\]" - } + "pattern": "[^\\\\]" }, "scriptOptions": { diff --git a/apps/heft/src/schemas/set-environment-variables-plugin.schema.json b/apps/heft/src/schemas/set-environment-variables-plugin.schema.json new file mode 100644 index 00000000000..7ffa21d2967 --- /dev/null +++ b/apps/heft/src/schemas/set-environment-variables-plugin.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "CopyFiles Heft Task Event Options", + "description": "Defines configuration used by the \"copyFiles\" Heft task event.", + "type": "object", + + "additionalProperties": false, + "required": ["environmentVariablesToSet"], + + "properties": { + "environmentVariablesToSet": { + "type": "object", + "additionalProperties": { + "type": "string", + "pattern": ".+" + } + } + } +} diff --git a/apps/heft/src/schemas/templates/heft.json b/apps/heft/src/schemas/templates/heft.json index 38670ddac5b..870a1a538a3 100644 --- a/apps/heft/src/schemas/templates/heft.json +++ b/apps/heft/src/schemas/templates/heft.json @@ -2,145 +2,173 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", /** * Optionally specifies another JSON config file that this file extends from. This provides a way for standard * settings to be shared across multiple projects. + * + * To delete an inherited setting, set it to `null` in this file. */ // "extends": "base-project/config/heft.json", - "eventActions": [ - // { - // /** - // * (Required) The kind of built-in operation that should be performed. - // * The "deleteGlobs" action deletes files or folders that match the specified glob patterns. - // */ - // "actionKind": "deleteGlobs", - // - // /** - // * (Required) The Heft stage when this action should be performed. Note that heft.json event actions - // * are scheduled after any plugin tasks have processed the event. For example, a "compile" event action - // * will be performed after the TypeScript compiler has been invoked. - // * - // * Options: "clean", "pre-compile", "compile", "bundle", "post-build" - // */ - // "heftEvent": "clean", - // + /** + * Defines aliases for existing Heft actions, and allows them to be invoked by + * name with default parameters. The JSON keys is are user-defined names. + * + * For example, the "heft start" alias is conventionally defined to invoke + * "heft build-watch --serve" using a definition like this: + * + * "aliasesByName": { "start": { "actionName": "build-watch", "defaultParameters": [ "--serve" ] } } + */ + "aliasesByName": { + // /** + // * The command-line action name of the Heft alias that is being defined. + // * This JSON key is a user-defined value. + // */ + // "example-alias-name": { // /** - // * (Required) A user-defined tag whose purpose is to allow configs to replace/delete handlers that - // * were added by other configs. + // * The name of the existing Heft command-line action to be invoked by this alias. // */ - // "actionId": "my-example-action", + // "actionName": "example-action", // // /** - // * (Required) Glob patterns to be deleted. The paths are resolved relative to the project folder. - // * Documentation for supported glob syntaxes: https://www.npmjs.com/package/fast-glob + // * A list of command-line parameters to pass to the Heft action by default. + // * These parameters will be appended after the specified action and before + // * any user-specified parameters. // */ - // "globsToDelete": [ - // "dist", - // "lib", - // "lib-esnext", - // "temp" - // ] - // }, - // + // "defaultParameters": [ "--do-some-thing" ] + // } + }, + + /** + * List of Heft lifecycle plugins to be loaded for this project. + */ + "heftPlugins": [ // { // /** - // * (Required) The kind of built-in operation that should be performed. - // * The "copyFiles" action copies files that match the specified patterns. - // */ - // "actionKind": "copyFiles", - // - // /** - // * (Required) The Heft stage when this action should be performed. Note that heft.json event actions - // * are scheduled after any plugin tasks have processed the event. For example, a "compile" event action - // * will be performed after the TypeScript compiler has been invoked. - // * - // * Options: "pre-compile", "compile", "bundle", "post-build" + // * (REQUIRED) The NPM package name for the plugin. // */ - // "heftEvent": "pre-compile", + // "pluginPackage": "@mycorp/heft-example-plugin", // // /** - // * (Required) A user-defined tag whose purpose is to allow configs to replace/delete handlers that - // * were added by other configs. + // * The name of the plugin to load from the NPM package's heft-plugin.json manifest. + // * If not specified, and if the plugin package provides a single plugin, then that + // * plugin will be loaded. // */ - // "actionId": "my-example-action", + // // "pluginName": "example-plugin", // // /** - // * (Required) An array of copy operations to run perform during the specified Heft event. + // * Options to pass to the plugin. This is a custom object whose structure + // * is defined by the plugin. // */ - // "copyOperations": [ - // { - // /** - // * (Required) The base folder that files will be copied from, relative to the project root. - // * Settings such as "includeGlobs" and "excludeGlobs" will be resolved relative - // * to this folder. - // * NOTE: Assigning "sourceFolder" does not by itself select any files to be copied. - // */ - // "sourceFolder": "src", - // - // /** - // * (Required) One or more folders that files will be copied into, relative to the project root. - // * If you specify more than one destination folder, Heft will read the input files only once, using - // * streams to efficiently write multiple outputs. - // */ - // "destinationFolders": ["dist/assets"], - // - // /** - // * If specified, this option recursively scans all folders under "sourceFolder" and includes any files - // * that match the specified extensions. (If "fileExtensions" and "includeGlobs" are both - // * specified, their selections are added together.) - // */ - // "fileExtensions": [".jpg", ".png"], - // - // /** - // * A list of glob patterns that select files to be copied. The paths are resolved relative - // * to "sourceFolder". - // * Documentation for supported glob syntaxes: https://www.npmjs.com/package/fast-glob - // */ - // "includeGlobs": ["assets/*.md"], - // - // /** - // * A list of glob patterns that exclude files/folders from being copied. The paths are resolved relative - // * to "sourceFolder". These exclusions eliminate items that were selected by the "includeGlobs" - // * or "fileExtensions" setting. - // */ - // "excludeGlobs": [], - // - // /** - // * Normally, when files are selected under a child folder, a corresponding folder will be created in - // * the destination folder. Specify flatten=true to discard the source path and copy all matching files - // * to the same folder. If two files have the same name an error will be reported. - // * The default value is false. - // */ - // "flatten": false, - // - // /** - // * If true, filesystem hard links will be created instead of copying the file. Depending on the - // * operating system, this may be faster. (But note that it may cause unexpected behavior if a tool - // * modifies the link.) The default value is false. - // */ - // "hardlink": false - // } - // ] + // // "options": { "example-key": "example-value" } // } ], /** - * The list of Heft plugins to be loaded. + * Heft phases that can be run during an execution of Heft. + * The JSON keys is are user-defined names. */ - "heftPlugins": [ - // { - // /** - // * The path to the plugin package. - // */ - // "plugin": "path/to/my-plugin", - // - // /** - // * An optional object that provides additional settings that may be defined by the plugin. - // */ - // // "options": { } - // } - ] + "phasesByName": { + /** + * The name of the phase, which is used by other fields such as "phaseDependencies". + * This JSON key is a user-defined value. + */ + "example-phase": { + /** + * A description to be shown in the command-line help. + */ + "phaseDescription": "An example phase", + + /** + * A list of delete operations to perform when cleaning at the beginning of phase execution. + * Their structure is similar the options used by the delete-files-plugin. + */ + "cleanFiles": [ + // { + // /** + // * Absolute path to the source file or folder, relative to the project root. + // * If "fileExtensions", "excludeGlobs", or "includeGlobs" are specified, then "sourcePath" + // * is assumed to be a folder; if it is not a folder, an error will be thrown. + // * Settings such as "includeGlobs" and "excludeGlobs" will be resolved relative to this path. + // * If no globs or file extensions are specified, the entire folder will be copied. + // * If this parameter is not provided, it defaults to the project root. + // */ + // // "sourcePath": "lib", + // + // /** + // * If specified, this option recursively scans all folders under "sourcePath" and includes + // * any files that match the specified extensions. If "fileExtensions" and "includeGlobs" + // * are both specified, their selections are added together. + // */ + // // "fileExtensions": [ ".png" ], + // + // /** + // * A list of glob patterns that select files to be copied. The paths are resolved relative + // * to "sourcePath", which must be a folder path. If "fileExtensions" and "includeGlobs" + // * are both specified, their selections are added together. + // * + // * For glob syntax, refer to: https://www.npmjs.com/package/fast-glob + // */ + // // "excludeGlobs": [], + // + // + // /** + // * A list of glob patterns that exclude files or folders from being copied. The paths are resolved + // * relative to "sourcePath", which must be a folder path. These exclusions eliminate items that + // * were selected by the "includeGlobs" or "fileExtensions" setting. + // * + // * For glob syntax, refer to: https://www.npmjs.com/package/fast-glob + // */ + // // "includeGlobs": [ "**/temp" ] + // } + ], + + /** + * A list of phase names that must be run before this phase can start. + */ + "phaseDependencies": [], + + /** + * Heft tasks that are run during an execution of the Heft phase. + * The JSON keys is are user-defined names. + */ + "tasksByName": { + /** + * The name of the task, which is used by other fields such as "taskDependencies". + * This JSON key is a user-defined value. + */ + "example-task": { + /** + * A list of task names that must be run before this task can start. + */ + "taskDependencies": [], + + /** + * (REQUIRED) The Heft plugin to be loaded, which will perform the operation for this task. + */ + "taskPlugin": { + /** + * (REQUIRED) The NPM package name for the plugin. + */ + "pluginPackage": "@mycorp/heft-example-plugin" + + /** + * The name of the plugin to load from the NPM package's heft-plugin.json manifest. + * If not specified, and if the plugin package provides a single plugin, then that + * plugin will be loaded. + */ + // "pluginName": "example-plugin", + + /** + * Options to pass to the plugin. This is a custom object whose structure + * is defined by the plugin. + */ + // "options": { "example-key": "example-value" } + } + } + } + } + } } diff --git a/apps/heft/src/start.ts b/apps/heft/src/start.ts index d4cce67642f..0fa5a60c557 100644 --- a/apps/heft/src/start.ts +++ b/apps/heft/src/start.ts @@ -3,12 +3,12 @@ import { HeftCommandLineParser } from './cli/HeftCommandLineParser'; -// Launching via lib/start.js bypasses the version selector. Use that for debugging Heft. +// Launching via lib-commonjs/start.js bypasses the version selector. Use that for debugging Heft. const parser: HeftCommandLineParser = new HeftCommandLineParser(); parser - .execute() + .executeAsync() .then(() => { // This should be removed when the issue with aria not tearing down process.exit(process.exitCode === undefined ? 0 : process.exitCode); diff --git a/apps/heft/src/startWithVersionSelector.ts b/apps/heft/src/startWithVersionSelector.ts index eea96101400..e9cc3b950f6 100644 --- a/apps/heft/src/startWithVersionSelector.ts +++ b/apps/heft/src/startWithVersionSelector.ts @@ -1,14 +1,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +/* eslint-disable no-console */ + // NOTE: Since startWithVersionSelector.ts is loaded in the same process as start.ts, any dependencies that // we import here may become side-by-side versions. We want to minimize any dependencies. -import * as path from 'path'; -import * as fs from 'fs'; +import * as path from 'node:path'; +import * as fs from 'node:fs'; + import type { IPackageJson } from '@rushstack/node-core-library'; -import { Constants } from './utilities/Constants'; -const HEFT_PACKAGE_NAME: string = '@rushstack/heft'; +import { getToolParameterNamesFromArgs } from './utilities/CliUtilities'; +import { Constants } from './utilities/Constants'; // Excerpted from PackageJsonLookup.tryGetPackageFolderFor() function tryGetPackageFolderFor(resolvedFileOrFolderPath: string): string | undefined { @@ -47,14 +50,15 @@ function tryGetPackageFolderFor(resolvedFileOrFolderPath: string): string | unde * Use "heft --unmanaged" to bypass this feature. */ function tryStartLocalHeft(): boolean { - if (process.argv.indexOf(Constants.unmanagedParameterLongName) >= 0) { + const toolParameters: Set = getToolParameterNamesFromArgs(); + if (toolParameters.has(Constants.unmanagedParameterLongName)) { console.log( `Bypassing the Heft version selector because ${JSON.stringify(Constants.unmanagedParameterLongName)} ` + 'was specified.' ); console.log(); return false; - } else if (process.argv.indexOf(Constants.debugParameterLongName) >= 0) { + } else if (toolParameters.has(Constants.debugParameterLongName)) { // The unmanaged flag could be undiscoverable if it's not in their locally installed version console.log( 'Searching for a locally installed version of Heft. Use the ' + @@ -78,8 +82,8 @@ function tryStartLocalHeft(): boolean { // Does package.json have a dependency on Heft? if ( - !(packageJson.dependencies && packageJson.dependencies[HEFT_PACKAGE_NAME]) && - !(packageJson.devDependencies && packageJson.devDependencies[HEFT_PACKAGE_NAME]) + !(packageJson.dependencies && packageJson.dependencies[Constants.heftPackageName]) && + !(packageJson.devDependencies && packageJson.devDependencies[Constants.heftPackageName]) ) { // No explicit dependency on Heft return false; @@ -87,11 +91,21 @@ function tryStartLocalHeft(): boolean { // To avoid a loading the "resolve" NPM package, let's assume that the Heft dependency must be // installed as "/node_modules/@rushstack/heft". - const heftFolder: string = path.join(projectFolder, 'node_modules', HEFT_PACKAGE_NAME); - - heftEntryPoint = path.join(heftFolder, 'lib', 'start.js'); - if (!fs.existsSync(heftEntryPoint)) { - throw new Error('Unable to find Heft entry point: ' + heftEntryPoint); + const heftFolder: string = path.join(projectFolder, 'node_modules', Constants.heftPackageName); + + // Try the new output layout first, then fall back to the legacy layout + const commonJsHeftEntryPoint: string = path.join(heftFolder, 'lib-commonjs', 'start.js'); + if (!fs.existsSync(commonJsHeftEntryPoint)) { + const legacyHeftEntryPoint: string = path.join(heftFolder, 'lib', 'start.js'); + if (!fs.existsSync(legacyHeftEntryPoint)) { + throw new Error( + `Unable to find Heft entry point: ${commonJsHeftEntryPoint} or ${legacyHeftEntryPoint}` + ); + } else { + heftEntryPoint = legacyHeftEntryPoint; + } + } else { + heftEntryPoint = commonJsHeftEntryPoint; } } catch (error) { throw new Error('Error probing for local Heft version: ' + (error as Error).message); diff --git a/apps/heft/src/utilities/CliUtilities.ts b/apps/heft/src/utilities/CliUtilities.ts new file mode 100644 index 00000000000..d322dc45e1f --- /dev/null +++ b/apps/heft/src/utilities/CliUtilities.ts @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * Parse the arguments to the tool being executed and return the tool argument names. + * + * @param argv - The arguments to parse. Defaults to `process.argv`. + */ +export function getToolParameterNamesFromArgs(argv: string[] = process.argv): Set { + const toolParameters: Set = new Set(); + // Skip the first two arguments, which are the path to the Node executable and the path to the Heft + // entrypoint. The remaining arguments are the tool arguments. Grab them until we reach a non-"-"-prefixed + // argument. We can do this simple parsing because the Heft tool only has simple optional flags. + for (let i: number = 2; i < argv.length; ++i) { + const arg: string = argv[i]; + if (!arg.startsWith('-')) { + break; + } + toolParameters.add(arg); + } + return toolParameters; +} diff --git a/apps/heft/src/utilities/Constants.ts b/apps/heft/src/utilities/Constants.ts index dd2c6ad1eee..60e929519e6 100644 --- a/apps/heft/src/utilities/Constants.ts +++ b/apps/heft/src/utilities/Constants.ts @@ -14,26 +14,18 @@ export class Constants { public static cleanParameterLongName: string = '--clean'; - public static cleanCacheParameterLongName: string = '--clean-cache'; - public static debugParameterLongName: string = '--debug'; public static localesParameterLongName: string = '--locales'; public static onlyParameterLongName: string = '--only'; - public static onlyParameterShortName: string = '-o'; - public static productionParameterLongName: string = '--production'; public static toParameterLongName: string = '--to'; - public static toParameterShortName: string = '-t'; - public static toExceptParameterLongName: string = '--to-except'; - public static toExceptParameterShortName: string = '-T'; - public static unmanagedParameterLongName: string = '--unmanaged'; public static verboseParameterLongName: string = '--verbose'; @@ -41,4 +33,6 @@ export class Constants { public static verboseParameterShortName: string = '-v'; public static maxParallelism: number = 100; + + public static heftPackageName: string = '@rushstack/heft'; } diff --git a/apps/heft/src/utilities/CoreConfigFiles.ts b/apps/heft/src/utilities/CoreConfigFiles.ts index 55aafe0b9a2..ff221359a7d 100644 --- a/apps/heft/src/utilities/CoreConfigFiles.ts +++ b/apps/heft/src/utilities/CoreConfigFiles.ts @@ -1,25 +1,29 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import { - ConfigurationFile, - IJsonPathMetadataResolverOptions, + ProjectConfigurationFile, InheritanceType, - PathResolutionMethod + PathResolutionMethod, + type IJsonPathMetadataResolverOptions } from '@rushstack/heft-config-file'; -import { Import, ITerminal } from '@rushstack/node-core-library'; -import type { RigConfig } from '@rushstack/rig-package'; +import { Import, PackageJsonLookup, InternalError } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; +import type { IRigConfig } from '@rushstack/rig-package'; import type { IDeleteOperation } from '../plugins/DeleteFilesPlugin'; import type { INodeServicePluginConfiguration } from '../plugins/NodeServicePlugin'; import { Constants } from './Constants'; -export type HeftEventKind = 'copyFiles' | 'deleteFiles' | 'runScript' | 'nodeService'; +export interface IHeftConfigurationJsonActionReference { + actionName: string; + defaultParameters?: string[]; +} -export interface IHeftConfigurationJsonEventSpecifier { - eventKind: HeftEventKind; - options?: object; +export interface IHeftConfigurationJsonAliases { + [aliasName: string]: IHeftConfigurationJsonActionReference; } export interface IHeftConfigurationJsonPluginSpecifier { @@ -31,8 +35,7 @@ export interface IHeftConfigurationJsonPluginSpecifier { export interface IHeftConfigurationJsonTaskSpecifier { taskDependencies?: string[]; - taskEvent?: IHeftConfigurationJsonEventSpecifier; - taskPlugin?: IHeftConfigurationJsonPluginSpecifier; + taskPlugin: IHeftConfigurationJsonPluginSpecifier; } export interface IHeftConfigurationJsonTasks { @@ -52,14 +55,17 @@ export interface IHeftConfigurationJsonPhases { export interface IHeftConfigurationJson { heftPlugins?: IHeftConfigurationJsonPluginSpecifier[]; + aliasesByName?: IHeftConfigurationJsonAliases; phasesByName?: IHeftConfigurationJsonPhases; } +let _heftConfigFileLoader: ProjectConfigurationFile | undefined; +let _nodeServiceConfigurationLoader: ProjectConfigurationFile | undefined; + export class CoreConfigFiles { - private static _heftConfigFileLoader: ConfigurationFile | undefined; - private static _nodeServiceConfigurationLoader: - | ConfigurationFile - | undefined; + public static heftConfigurationProjectRelativeFilePath: string = `${Constants.projectConfigFolderName}/${Constants.heftConfigurationFilename}`; + + public static nodeServiceConfigurationProjectRelativeFilePath: string = `${Constants.projectConfigFolderName}/${Constants.nodeServiceConfigurationFilename}`; /** * Returns the loader for the `config/heft.json` config file. @@ -67,24 +73,46 @@ export class CoreConfigFiles { public static async loadHeftConfigurationFileForProjectAsync( terminal: ITerminal, projectPath: string, - rigConfig?: RigConfig | undefined + rigConfig?: IRigConfig | undefined ): Promise { - if (!CoreConfigFiles._heftConfigFileLoader) { + if (!_heftConfigFileLoader) { + let heftPluginPackageFolder: string | undefined; + const pluginPackageResolver: ( options: IJsonPathMetadataResolverOptions ) => string = (options: IJsonPathMetadataResolverOptions) => { const { propertyValue, configurationFilePath } = options; - const configurationFileDirectory: string = path.dirname(configurationFilePath); - return Import.resolvePackage({ - packageName: propertyValue, - baseFolderPath: configurationFileDirectory - }); + if (propertyValue === Constants.heftPackageName) { + // If the value is "@rushstack/heft", then resolve to the Heft package that is + // installed in the project folder. This avoids issues with mismatched versions + // between the project and the globally installed Heft. Use the PackageJsonLookup + // class to find the package folder to avoid hardcoding the path for compatibility + // with bundling. + if (!heftPluginPackageFolder) { + heftPluginPackageFolder = PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname); + } + + if (!heftPluginPackageFolder) { + // This should never happen + throw new InternalError('Unable to find the @rushstack/heft package folder'); + } + + return heftPluginPackageFolder; + } else { + const configurationFileDirectory: string = path.dirname(configurationFilePath); + return Import.resolvePackage({ + packageName: propertyValue, + baseFolderPath: configurationFileDirectory, + allowSelfReference: true + }); + } }; - const schemaPath: string = path.join(__dirname, '..', 'schemas', 'heft.schema.json'); - CoreConfigFiles._heftConfigFileLoader = new ConfigurationFile({ - projectRelativeFilePath: `${Constants.projectConfigFolderName}/${Constants.heftConfigurationFilename}`, - jsonSchemaPath: schemaPath, + const schemaObject: object = await import('../schemas/heft.schema.json'); + // eslint-disable-next-line require-atomic-updates + _heftConfigFileLoader = new ProjectConfigurationFile({ + projectRelativeFilePath: CoreConfigFiles.heftConfigurationProjectRelativeFilePath, + jsonSchemaObject: schemaObject, propertyInheritanceDefaults: { array: { inheritanceType: InheritanceType.append }, object: { inheritanceType: InheritanceType.merge } @@ -101,58 +129,121 @@ export class CoreConfigFiles { '$.phasesByName.*.tasksByName.*.taskPlugin.pluginPackage': { pathResolutionMethod: PathResolutionMethod.custom, customResolver: pluginPackageResolver - }, - // Special handling for "runScript" task events to resolve the script path - '$.phasesByName.*.tasksByName[?(@.taskEvent && @.taskEvent.eventKind == "runScript")].taskEvent.options.scriptPath': - { - pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToProjectRoot - } + } } }); } - const configurationFile: IHeftConfigurationJson = - await CoreConfigFiles._heftConfigFileLoader.loadConfigurationFileForProjectAsync( + const heftConfigFileLoader: ProjectConfigurationFile = _heftConfigFileLoader; + + let configurationFile: IHeftConfigurationJson; + try { + configurationFile = await heftConfigFileLoader.loadConfigurationFileForProjectAsync( terminal, projectPath, rigConfig ); + } catch (e: unknown) { + if ( + !(e instanceof Error) || + !e.message.startsWith('Resolved configuration object does not match schema') + ) { + throw e; + } - // The pluginPackage field was resolved to the root of the package, but we also want to have - // the original plugin package name in the config file. Gather all the plugin specifiers so we can - // add the original data ourselves. - const pluginSpecifiers: IHeftConfigurationJsonPluginSpecifier[] = [ - ...(configurationFile.heftPlugins || []) - ]; - for (const { tasksByName } of Object.values(configurationFile.phasesByName || {})) { - for (const { taskPlugin } of Object.values(tasksByName || {})) { - if (taskPlugin) { - pluginSpecifiers.push(taskPlugin); - } + try { + // If the config file doesn't match the schema, then we should check to see if it does + // match the legacy schema. We don't need to worry about the resulting object, we just + // want to see if it parses. We will use the ConfigurationFile class to load it to ensure + // that we follow the "extends" chain for the entire config file. + const legacySchemaObject: object = await import('../schemas/heft-legacy.schema.json'); + const legacyConfigFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath: CoreConfigFiles.heftConfigurationProjectRelativeFilePath, + jsonSchemaObject: legacySchemaObject + }); + await legacyConfigFileLoader.loadConfigurationFileForProjectAsync(terminal, projectPath, rigConfig); + } catch (e2) { + // It doesn't match the legacy schema either. Throw the original error. + throw e; } + // Matches the legacy schema, so throw a more helpful error. + throw new Error( + "This project's Heft configuration appears to be using an outdated schema.\n\n" + + 'Heft 0.51.0 introduced a major breaking change for Heft configuration files. ' + + 'Your project appears to be using the older file format. You will need to ' + + 'migrate your project to the new format. Follow these instructions: ' + + 'https://rushstack.io/link/heft-0.51' + ); } - for (const pluginSpecifier of pluginSpecifiers) { - const pluginPackageName: string = CoreConfigFiles._heftConfigFileLoader.getPropertyOriginalValue({ - parentObject: pluginSpecifier, + // The pluginPackage field was resolved to the root of the package, but we also want to have + // the original plugin package name in the config file. + function getUpdatedPluginSpecifier( + rawSpecifier: IHeftConfigurationJsonPluginSpecifier + ): IHeftConfigurationJsonPluginSpecifier { + const pluginPackageName: string = heftConfigFileLoader.getPropertyOriginalValue({ + parentObject: rawSpecifier, propertyName: 'pluginPackage' })!; - pluginSpecifier.pluginPackageRoot = pluginSpecifier.pluginPackage; - pluginSpecifier.pluginPackage = pluginPackageName; + const newSpecifier: IHeftConfigurationJsonPluginSpecifier = { + ...rawSpecifier, + pluginPackageRoot: rawSpecifier.pluginPackage, + pluginPackage: pluginPackageName + }; + return newSpecifier; } - return configurationFile; + const phasesByName: IHeftConfigurationJsonPhases = {}; + + const normalizedConfigurationFile: IHeftConfigurationJson = { + ...configurationFile, + heftPlugins: configurationFile.heftPlugins?.map(getUpdatedPluginSpecifier) ?? [], + phasesByName + }; + + for (const [phaseName, phase] of Object.entries(configurationFile.phasesByName || {})) { + const tasksByName: IHeftConfigurationJsonTasks = {}; + phasesByName[phaseName] = { + ...phase, + tasksByName + }; + + for (const [taskName, task] of Object.entries(phase.tasksByName || {})) { + if (task.taskPlugin) { + tasksByName[taskName] = { + ...task, + taskPlugin: getUpdatedPluginSpecifier(task.taskPlugin) + }; + } else { + tasksByName[taskName] = task; + } + } + } + + return normalizedConfigurationFile; } - public static get nodeServiceConfigurationFile(): ConfigurationFile { - if (!CoreConfigFiles._nodeServiceConfigurationLoader) { - const schemaPath: string = path.resolve(__dirname, '..', 'schemas', 'node-service.schema.json'); - CoreConfigFiles._nodeServiceConfigurationLoader = - new ConfigurationFile({ - projectRelativeFilePath: `${Constants.projectConfigFolderName}/${Constants.nodeServiceConfigurationFilename}`, - jsonSchemaPath: schemaPath - }); + public static async tryLoadNodeServiceConfigurationFileAsync( + terminal: ITerminal, + projectPath: string, + rigConfig?: IRigConfig | undefined + ): Promise { + if (!_nodeServiceConfigurationLoader) { + const schemaObject: object = await import('../schemas/node-service.schema.json'); + // eslint-disable-next-line require-atomic-updates + _nodeServiceConfigurationLoader = new ProjectConfigurationFile({ + projectRelativeFilePath: CoreConfigFiles.nodeServiceConfigurationProjectRelativeFilePath, + jsonSchemaObject: schemaObject + }); } - return CoreConfigFiles._nodeServiceConfigurationLoader; + + const configurationFile: INodeServicePluginConfiguration | undefined = + await _nodeServiceConfigurationLoader.tryLoadConfigurationFileForProjectAsync( + terminal, + projectPath, + rigConfig + ); + return configurationFile; } } diff --git a/apps/heft/src/utilities/GitUtilities.ts b/apps/heft/src/utilities/GitUtilities.ts index 6e8898f459c..a0f918b1242 100644 --- a/apps/heft/src/utilities/GitUtilities.ts +++ b/apps/heft/src/utilities/GitUtilities.ts @@ -1,13 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { ChildProcess, SpawnSyncReturns } from 'child_process'; -import { default as getGitRepoInfo, GitRepoInfo as IGitRepoInfo } from 'git-repo-info'; -import { Executable, FileSystem, InternalError, Path } from '@rushstack/node-core-library'; +import * as path from 'node:path'; +import type { ChildProcess, SpawnSyncReturns } from 'node:child_process'; + +import { default as getGitRepoInfo, type GitRepoInfo as IGitRepoInfo } from 'git-repo-info'; import { default as ignore, type Ignore as IIgnoreMatcher } from 'ignore'; -// Matches lines starting with "#" and whitepace lines +import { Executable, FileSystem, InternalError, Path, Text } from '@rushstack/node-core-library'; + +// Matches lines starting with "#" and whitespace lines const GITIGNORE_IGNORABLE_LINE_REGEX: RegExp = /^(?:(?:#.*)|(?:\s+))$/; const UNINITIALIZED: 'UNINITIALIZED' = 'UNINITIALIZED'; @@ -188,9 +190,8 @@ export class GitUtilities { let currentPath: string = normalizedWorkingDirectory; while (currentPath.length >= gitRepoRootPath.length) { const gitIgnoreFilePath: string = `${currentPath}/.gitignore`; - const gitIgnorePatterns: string[] | undefined = await this._tryReadGitIgnoreFileAsync( - gitIgnoreFilePath - ); + const gitIgnorePatterns: string[] | undefined = + await this._tryReadGitIgnoreFileAsync(gitIgnoreFilePath); if (gitIgnorePatterns) { rawIgnorePatternsByGitignoreFolder.set(currentPath, gitIgnorePatterns); } @@ -201,9 +202,8 @@ export class GitUtilities { const gitignoreRelativeFilePaths: string[] = await this._findUnignoredFilesAsync('*.gitignore'); for (const gitignoreRelativeFilePath of gitignoreRelativeFilePaths) { const gitignoreFilePath: string = `${normalizedWorkingDirectory}/${gitignoreRelativeFilePath}`; - const gitIgnorePatterns: string[] | undefined = await this._tryReadGitIgnoreFileAsync( - gitignoreFilePath - ); + const gitIgnorePatterns: string[] | undefined = + await this._tryReadGitIgnoreFileAsync(gitignoreFilePath); if (gitIgnorePatterns) { const parentPath: string = gitignoreFilePath.slice(0, gitignoreFilePath.lastIndexOf('/')); rawIgnorePatternsByGitignoreFolder.set(parentPath, gitIgnorePatterns); @@ -284,7 +284,7 @@ export class GitUtilities { const foundIgnorePatterns: string[] = []; if (gitIgnoreContent) { - const gitIgnorePatterns: string[] = gitIgnoreContent.split(/\r?\n/g); + const gitIgnorePatterns: string[] = Text.splitByNewLines(gitIgnoreContent); for (const gitIgnorePattern of gitIgnorePatterns) { // Ignore whitespace-only lines and comments if (gitIgnorePattern.length === 0 || GITIGNORE_IGNORABLE_LINE_REGEX.test(gitIgnorePattern)) { @@ -347,11 +347,13 @@ export class GitUtilities { childProcess.stderr!.on('data', (chunk: Buffer) => { errorMessage += chunk.toString(); }); - childProcess.on('close', (exitCode: number) => { - if (exitCode !== 0) { + childProcess.on('close', (exitCode: number | null, signal: NodeJS.Signals | null) => { + if (exitCode) { reject( new Error(`git exited with error code ${exitCode}${errorMessage ? `: ${errorMessage}` : ''}`) ); + } else if (signal) { + reject(new Error(`git terminated by signal ${signal}`)); } let remainder: string = ''; for (let chunk of stdoutBuffer) { diff --git a/apps/heft/src/utilities/WatchFileSystemAdapter.ts b/apps/heft/src/utilities/WatchFileSystemAdapter.ts index d5d548f75de..97c1ae5bdd3 100644 --- a/apps/heft/src/utilities/WatchFileSystemAdapter.ts +++ b/apps/heft/src/utilities/WatchFileSystemAdapter.ts @@ -1,19 +1,38 @@ -import * as fs from 'fs'; -import * as path from 'path'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as fs from 'node:fs'; +import * as path from 'node:path'; -import type { ReaddirAsynchronousMethod, ReaddirSynchronousMethod } from '@nodelib/fs.scandir'; -import type { StatAsynchronousMethod, StatSynchronousMethod } from '@nodelib/fs.stat'; -import type { FileSystemAdapter } from 'fast-glob'; import Watchpack from 'watchpack'; -interface IReaddirOptions { +/** + * Options for `fs.readdir` + * @public + */ +export interface IReaddirOptions { + /** + * If true, readdir will return `fs.Dirent` objects instead of strings. + */ withFileTypes: true; } /* eslint-disable @rushstack/no-new-null */ -type StatCallback = (error: NodeJS.ErrnoException | null, stats: fs.Stats) => void; -type ReaddirStringCallback = (error: NodeJS.ErrnoException | null, files: string[]) => void; -type ReaddirDirentCallback = (error: NodeJS.ErrnoException | null, files: fs.Dirent[]) => void; +/** + * Callback for `fs.stat` and `fs.lstat` + * @public + */ +export type StatCallback = (error: NodeJS.ErrnoException | null, stats: fs.Stats) => void; +/** + * Callback for `fs.readdir` when `withFileTypes` is not specified or false + * @public + */ +export type ReaddirStringCallback = (error: NodeJS.ErrnoException | null, files: string[]) => void; +/** + * Callback for `fs.readdir` when `withFileTypes` is true + * @public + */ +export type ReaddirDirentCallback = (error: NodeJS.ErrnoException | null, files: fs.Dirent[]) => void; /* eslint-enable @rushstack/no-new-null */ /** @@ -27,13 +46,73 @@ export interface IWatchedFileState { changed: boolean; } +/** + * Interface contract for heft plugins to use the `WatchFileSystemAdapter` + * @public + */ +export interface IWatchFileSystem { + /** + * Synchronous readdir. Watches the directory for changes. + * + * @see fs.readdirSync + */ + readdirSync(filePath: string): string[]; + readdirSync(filePath: string, options: IReaddirOptions): fs.Dirent[]; + + /** + * Asynchronous readdir. Watches the directory for changes. + * + * @see fs.readdir + */ + readdir(filePath: string, callback: ReaddirStringCallback): void; + readdir(filePath: string, options: IReaddirOptions, callback: ReaddirDirentCallback): void; + + /** + * Asynchronous lstat. Watches the file for changes, or if it does not exist, watches to see if it is created. + * @see fs.lstat + */ + lstat(filePath: string, callback: StatCallback): void; + + /** + * Synchronous lstat. Watches the file for changes, or if it does not exist, watches to see if it is created. + * @see fs.lstatSync + */ + lstatSync(filePath: string): fs.Stats; + + /** + * Asynchronous stat. Watches the file for changes, or if it does not exist, watches to see if it is created. + * @see fs.stat + */ + stat(filePath: string, callback: StatCallback): void; + + /** + * Synchronous stat. Watches the file for changes, or if it does not exist, watches to see if it is created. + * @see fs.statSync + */ + statSync(filePath: string): fs.Stats; + + /** + * Tells the adapter to track the specified file (or folder) as used. + * Returns an object containing data about the state of said file (or folder). + * Uses promise-based API. + */ + getStateAndTrackAsync(filePath: string): Promise; + + /** + * Tells the adapter to track the specified file (or folder) as used. + * Returns an object containing data about the state of said file (or folder). + * Uses synchronous API. + */ + getStateAndTrack(filePath: string): IWatchedFileState; +} + /** * Interface contract for `WatchFileSystemAdapter` for cross-version compatibility */ -export interface IWatchFileSystemAdapter extends FileSystemAdapter { +export interface IWatchFileSystemAdapter extends IWatchFileSystem { /** * Prepares for incoming glob requests. Any file changed after this method is called - * will trigger teh watch callback in the next invocation. + * will trigger the watch callback in the next invocation. * File mtimes will be recorded at this time for any files that do not receive explicit * stat() or lstat() calls. */ @@ -43,11 +122,6 @@ export interface IWatchFileSystemAdapter extends FileSystemAdapter { * Clears the tracked file lists. */ watch(onChange: () => void): void; - /** - * Tells the adapter to track the specified file (or folder) as used. - * Returns an object containing data about the state of said file (or folder). - */ - getStateAndTrackAsync(filePath: string): Promise; } interface ITimeEntry { @@ -71,7 +145,10 @@ export class WatchFileSystemAdapter implements IWatchFileSystemAdapter { private _times: Map | undefined; /** { @inheritdoc fs.readdirSync } */ - public readdirSync: ReaddirSynchronousMethod = ((filePath: string, options?: IReaddirOptions) => { + public readdirSync: IWatchFileSystemAdapter['readdirSync'] = (( + filePath: string, + options?: IReaddirOptions + ) => { filePath = path.normalize(filePath); try { @@ -88,10 +165,10 @@ export class WatchFileSystemAdapter implements IWatchFileSystemAdapter { this._missing.set(filePath, Date.now()); throw err; } - }) as ReaddirSynchronousMethod; + }) as IWatchFileSystemAdapter['readdirSync']; /** { @inheritdoc fs.readdir } */ - public readdir: ReaddirAsynchronousMethod = ( + public readdir: IWatchFileSystemAdapter['readdir'] = ( filePath: string, optionsOrCallback: IReaddirOptions | ReaddirStringCallback, callback?: ReaddirDirentCallback | ReaddirStringCallback @@ -127,7 +204,7 @@ export class WatchFileSystemAdapter implements IWatchFileSystemAdapter { }; /** { @inheritdoc fs.lstat } */ - public lstat: StatAsynchronousMethod = (filePath: string, callback: StatCallback): void => { + public lstat: IWatchFileSystemAdapter['lstat'] = (filePath: string, callback: StatCallback): void => { filePath = path.normalize(filePath); fs.lstat(filePath, (err: NodeJS.ErrnoException | null, stats: fs.Stats) => { if (err) { @@ -140,7 +217,7 @@ export class WatchFileSystemAdapter implements IWatchFileSystemAdapter { }; /** { @inheritdoc fs.lstatSync } */ - public lstatSync: StatSynchronousMethod = (filePath: string): fs.Stats => { + public lstatSync: IWatchFileSystemAdapter['lstatSync'] = (filePath: string): fs.Stats => { filePath = path.normalize(filePath); try { const stats: fs.Stats = fs.lstatSync(filePath); @@ -153,7 +230,7 @@ export class WatchFileSystemAdapter implements IWatchFileSystemAdapter { }; /** { @inheritdoc fs.stat } */ - public stat: StatAsynchronousMethod = (filePath: string, callback: StatCallback): void => { + public stat: IWatchFileSystemAdapter['stat'] = (filePath: string, callback: StatCallback): void => { filePath = path.normalize(filePath); fs.stat(filePath, (err: NodeJS.ErrnoException | null, stats: fs.Stats) => { if (err) { @@ -166,7 +243,7 @@ export class WatchFileSystemAdapter implements IWatchFileSystemAdapter { }; /** { @inheritdoc fs.statSync } */ - public statSync: StatSynchronousMethod = (filePath: string) => { + public statSync: IWatchFileSystemAdapter['statSync'] = (filePath: string) => { filePath = path.normalize(filePath); try { const stats: fs.Stats = fs.statSync(filePath); @@ -254,4 +331,38 @@ export class WatchFileSystemAdapter implements IWatchFileSystemAdapter { changed: newTime !== oldTime }; } + + /** + * @inheritdoc + */ + public getStateAndTrack(filePath: string): IWatchedFileState { + const normalizedSourcePath: string = path.normalize(filePath); + const oldTime: number | undefined = this._lastFiles?.get(normalizedSourcePath); + let newTimeEntry: ITimeEntry | undefined = this._times?.get(normalizedSourcePath); + + if (!newTimeEntry) { + // Need to record a timestamp, otherwise first rerun will select everything + const stats: fs.Stats | undefined = fs.lstatSync(normalizedSourcePath, { throwIfNoEntry: false }); + if (stats) { + const rounded: number = stats.mtime.getTime() || stats.ctime.getTime() || Date.now(); + newTimeEntry = { + timestamp: rounded, + safeTime: rounded + }; + } else { + this._missing.set(normalizedSourcePath, Date.now()); + } + } + + const newTime: number | undefined = + (newTimeEntry && (newTimeEntry.timestamp ?? newTimeEntry.safeTime)) || this._lastQueryTime; + + if (newTime) { + this._files.set(normalizedSourcePath, newTime); + } + + return { + changed: newTime !== oldTime + }; + } } diff --git a/apps/heft/src/utilities/test/GitUtilities.test.ts b/apps/heft/src/utilities/test/GitUtilities.test.ts index 82d9eec7d35..9b51b6d1f1d 100644 --- a/apps/heft/src/utilities/test/GitUtilities.test.ts +++ b/apps/heft/src/utilities/test/GitUtilities.test.ts @@ -1,9 +1,15 @@ -import * as path from 'path'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; import { GitUtilities, type GitignoreFilterFn } from '../GitUtilities'; +import { PackageJsonLookup } from '@rushstack/node-core-library'; describe('GitUtilities', () => { describe('checkIgnoreAsync', () => { - const testFoldersBasePath: string = path.join(__dirname, 'checkIgnoreTests'); + const projectRoot: string = PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname)!; + + const testFoldersBasePath: string = `${projectRoot}/src/utilities/test/checkIgnoreTests`; it('returns all files are ignored', async () => { const testFolderPath: string = path.join(testFoldersBasePath, 'allIgnored'); @@ -37,13 +43,12 @@ describe('GitUtilities', () => { it('returns ignored files specified in the repo gitignore', async () => { // /apps/heft - const testFolderPath: string = path.resolve(__dirname, '..', '..', '..'); - const git = new GitUtilities(testFolderPath); + const git = new GitUtilities(projectRoot); const isUnignoredAsync: GitignoreFilterFn = (await git.tryCreateGitignoreFilterAsync())!; - expect(await isUnignoredAsync(path.join(testFolderPath, 'lib', 'a.txt'))).toEqual(false); - expect(await isUnignoredAsync(path.join(testFolderPath, 'temp', 'a.txt'))).toEqual(false); - expect(await isUnignoredAsync(path.join(testFolderPath, 'dist', 'a.txt'))).toEqual(false); - expect(await isUnignoredAsync(path.join(testFolderPath, 'src', 'a.txt'))).toEqual(true); + expect(await isUnignoredAsync(path.join(projectRoot, 'lib', 'a.txt'))).toEqual(false); + expect(await isUnignoredAsync(path.join(projectRoot, 'temp', 'a.txt'))).toEqual(false); + expect(await isUnignoredAsync(path.join(projectRoot, 'dist', 'a.txt'))).toEqual(false); + expect(await isUnignoredAsync(path.join(projectRoot, 'src', 'a.txt'))).toEqual(true); const ignoredFolderPath: string = path.join(testFoldersBasePath, 'allIgnored'); expect(await isUnignoredAsync(path.join(ignoredFolderPath, 'a.txt'))).toEqual(false); diff --git a/apps/heft/tsconfig.json b/apps/heft/tsconfig.json index da04e9fff9d..1a33d17b873 100644 --- a/apps/heft/tsconfig.json +++ b/apps/heft/tsconfig.json @@ -1,8 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["heft-jest", "node"], - "lib": ["ES2020"] - } + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/apps/lockfile-explorer-web/.eslintrc.js b/apps/lockfile-explorer-web/.eslintrc.js deleted file mode 100644 index 288eaa16364..00000000000 --- a/apps/lockfile-explorer-web/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/web-app', '@rushstack/eslint-config/mixins/react'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/apps/lockfile-explorer-web/assets/index.html b/apps/lockfile-explorer-web/assets/index.html index d31e23960e4..1c5d6f471a0 100644 --- a/apps/lockfile-explorer-web/assets/index.html +++ b/apps/lockfile-explorer-web/assets/index.html @@ -1,4 +1,4 @@ - + diff --git a/apps/lockfile-explorer-web/config/heft.json b/apps/lockfile-explorer-web/config/heft.json index 7204e0560b5..e031c98a5c5 100644 --- a/apps/lockfile-explorer-web/config/heft.json +++ b/apps/lockfile-explorer-web/config/heft.json @@ -2,21 +2,24 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", /** * Optionally specifies another JSON config file that this file extends from. This provides a way for standard * settings to be shared across multiple projects. + * + * To delete an inherited setting, set it to `null` in this file. */ - "extends": "@rushstack/heft-web-rig/profiles/app/config/heft.json", + "extends": "local-web-rig/profiles/app/config/heft.json", "phasesByName": { "build": { "tasksByName": { "copy-stub": { "taskDependencies": ["typescript"], - "taskEvent": { - "eventKind": "copyFiles", + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", "options": { "copyOperations": [ { diff --git a/apps/lockfile-explorer-web/config/jest.config.json b/apps/lockfile-explorer-web/config/jest.config.json index 7f2f5dc42b6..dd440826f6c 100644 --- a/apps/lockfile-explorer-web/config/jest.config.json +++ b/apps/lockfile-explorer-web/config/jest.config.json @@ -1,5 +1,5 @@ { - "extends": "@rushstack/heft-web-rig/profiles/app/config/jest.config.json", + "extends": "local-web-rig/profiles/app/config/jest.config.json", // Load the initappcontext.js stub when running tests "setupFiles": ["../lib-commonjs/stub/initappcontext.js"] diff --git a/apps/lockfile-explorer-web/config/rig.json b/apps/lockfile-explorer-web/config/rig.json index 687fc2911bc..26f617ab3fc 100644 --- a/apps/lockfile-explorer-web/config/rig.json +++ b/apps/lockfile-explorer-web/config/rig.json @@ -1,6 +1,6 @@ { "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-web-rig", + "rigPackageName": "local-web-rig", "rigProfile": "app" } diff --git a/apps/lockfile-explorer-web/eslint.config.js b/apps/lockfile-explorer-web/eslint.config.js new file mode 100644 index 00000000000..9765c392aa3 --- /dev/null +++ b/apps/lockfile-explorer-web/eslint.config.js @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-web-rig/profiles/app/includes/eslint/flat/profile/web-app'); +const reactMixin = require('local-web-rig/profiles/app/includes/eslint/flat/mixins/react'); +const packletsMixin = require('local-web-rig/profiles/app/includes/eslint/flat/mixins/packlets'); + +module.exports = [ + ...webAppProfile, + ...reactMixin, + packletsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/apps/lockfile-explorer-web/package.json b/apps/lockfile-explorer-web/package.json index 1f541497761..bb22f5bd43f 100644 --- a/apps/lockfile-explorer-web/package.json +++ b/apps/lockfile-explorer-web/package.json @@ -6,28 +6,28 @@ "license": "MIT", "scripts": { "build": "heft test --clean", - "start": "heft build-watch", + "start": "heft start", "test": "heft test", "_phase:build": "heft run --only build -- --clean", "_phase:test": "heft run --only test -- --clean" }, "dependencies": { - "@fluentui/react": "^8.96.1", - "react": "~16.13.1", - "react-dom": "~16.13.1", - "@lifaon/path": "~2.1.0", - "@reduxjs/toolkit": "~1.8.6", - "react-redux": "~8.0.4", - "redux": "~4.2.0", - "@rushstack/rush-themed-ui": "workspace:*" + "@reduxjs/toolkit": "~2.11.2", + "@rushstack/rush-themed-ui": "workspace:*", + "prism-react-renderer": "~2.4.1", + "react-dom": "~19.2.3", + "react-redux": "~9.2.0", + "react": "~19.2.3", + "redux": "~5.0.1", + "tslib": "~2.8.1" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft-web-rig": "workspace:*", "@rushstack/heft": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/react-dom": "16.9.14", - "@types/react": "16.14.23", - "@types/webpack-env": "1.18.0" - } + "@types/react": "19.2.7", + "@types/react-dom": "19.2.3", + "eslint": "~9.37.0", + "local-web-rig": "workspace:*", + "typescript": "5.8.2" + }, + "sideEffects": false } diff --git a/apps/lockfile-explorer-web/src/App.tsx b/apps/lockfile-explorer-web/src/App.tsx index e0d110a2c62..243b65c52db 100644 --- a/apps/lockfile-explorer-web/src/App.tsx +++ b/apps/lockfile-explorer-web/src/App.tsx @@ -2,6 +2,7 @@ // See LICENSE in the project root for license information. import React, { useEffect } from 'react'; + import styles from './App.scss'; import { readLockfileAsync } from './parsing/readLockfile'; import { LockfileViewer } from './containers/LockfileViewer'; @@ -17,7 +18,7 @@ import { ConnectionModal } from './components/ConnectionModal'; /** * This React component renders the application page. */ -export const App = (): JSX.Element => { +export const App = (): React.ReactElement => { const dispatch = useAppDispatch(); useEffect(() => { @@ -26,9 +27,10 @@ export const App = (): JSX.Element => { dispatch(loadEntries(lockfile)); } loadLockfileAsync().catch((e) => { + // eslint-disable-next-line no-console console.log(`Failed to read lockfile: ${e}`); }); - }, []); + }, [dispatch]); return ( <> diff --git a/apps/lockfile-explorer-web/src/AppContext.ts b/apps/lockfile-explorer-web/src/AppContext.ts deleted file mode 100644 index 61417a22fbe..00000000000 --- a/apps/lockfile-explorer-web/src/AppContext.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Describes the `window.appContext` object that the Node.js service uses - * to communicate runtime configuration to the web app. - * - * @remarks - * The `dist/index.html` page loads a script `initappcontext.js` to initialize - * this object before the web app starts. - * - * When the app is hosted by Webpack dev server, this is implemented by - * `lockfile-explorer-web/src/stub/initappcontext.ts`. - * - * When the app is hosted by the CLI front end, the `initappcontext.js` content - * is generated by an Express route. - */ -export interface IAppContext { - /** - * The service URL, without the trailing slash. - * - * @example - * Example: `http://localhost:8091` - */ - serviceUrl: string; - - /** - * The `package.json` version for the app. - */ - appVersion: string; - - /** - * Whether the CLI was invoked with the `--debug` parameter. - */ - debugMode: boolean; -} - -declare global { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Window { - appContext: IAppContext; - } -} diff --git a/apps/lockfile-explorer-web/src/components/ConnectionModal/index.tsx b/apps/lockfile-explorer-web/src/components/ConnectionModal/index.tsx index f946b60558e..afc205df90d 100644 --- a/apps/lockfile-explorer-web/src/components/ConnectionModal/index.tsx +++ b/apps/lockfile-explorer-web/src/components/ConnectionModal/index.tsx @@ -2,13 +2,15 @@ // See LICENSE in the project root for license information. import React, { useCallback, useEffect, useState } from 'react'; + import { Button, Text } from '@rushstack/rush-themed-ui'; + import styles from './styles.scss'; import appStyles from '../../App.scss'; -import { checkAliveAsync } from '../../parsing/getPackageFiles'; -import { ReactNull } from '../../types/ReactNull'; +import { checkAliveAsync } from '../../helpers/lfxApiClient'; +import type { ReactNull } from '../../types/ReactNull'; -export const ConnectionModal = (): JSX.Element | ReactNull => { +export const ConnectionModal = (): React.ReactElement | ReactNull => { const [isAlive, setIsAlive] = useState(true); const [checking, setChecking] = useState(false); const [manualChecked, setManualChecked] = useState(false); @@ -31,6 +33,7 @@ export const ConnectionModal = (): JSX.Element | ReactNull => { setManualChecked(true); keepAliveAsync().catch((e) => { // Keep alive cannot fail + // eslint-disable-next-line no-console console.error(`Unexpected exception: ${e}`); }); }, []); diff --git a/apps/lockfile-explorer-web/src/containers/BookmarksSidebar/index.tsx b/apps/lockfile-explorer-web/src/containers/BookmarksSidebar/index.tsx index eebd3c54643..79e7bd3a3fc 100644 --- a/apps/lockfile-explorer-web/src/containers/BookmarksSidebar/index.tsx +++ b/apps/lockfile-explorer-web/src/containers/BookmarksSidebar/index.tsx @@ -2,28 +2,30 @@ // See LICENSE in the project root for license information. import React, { useCallback } from 'react'; + +import { Button, ScrollArea, Text } from '@rushstack/rush-themed-ui'; + import appStyles from '../../App.scss'; import styles from './styles.scss'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; -import { LockfileEntry } from '../../parsing/LockfileEntry'; +import type { LfxGraphEntry } from '../../packlets/lfx-shared'; import { clearStackAndPush, removeBookmark } from '../../store/slices/entrySlice'; -import { Button, ScrollArea, Text } from '@rushstack/rush-themed-ui'; -export const BookmarksSidebar = (): JSX.Element => { +export const BookmarksSidebar = (): React.ReactElement => { const bookmarks = useAppSelector((state) => state.entry.bookmarkedEntries); const dispatch = useAppDispatch(); const clear = useCallback( - (entry: LockfileEntry) => () => { + (entry: LfxGraphEntry) => () => { dispatch(clearStackAndPush(entry)); }, - [] + [dispatch] ); const deleteEntry = useCallback( - (entry: LockfileEntry) => () => { + (entry: LfxGraphEntry) => () => { dispatch(removeBookmark(entry)); }, - [] + [dispatch] ); return ( diff --git a/apps/lockfile-explorer-web/src/containers/LockfileEntryDetailsView/index.tsx b/apps/lockfile-explorer-web/src/containers/LockfileEntryDetailsView/index.tsx index 8eb049e2043..66bc2f2fc03 100644 --- a/apps/lockfile-explorer-web/src/containers/LockfileEntryDetailsView/index.tsx +++ b/apps/lockfile-explorer-web/src/containers/LockfileEntryDetailsView/index.tsx @@ -2,80 +2,110 @@ // See LICENSE in the project root for license information. import React, { useCallback, useEffect, useState } from 'react'; + import { ScrollArea, Text } from '@rushstack/rush-themed-ui'; + import styles from './styles.scss'; import appStyles from '../../App.scss'; -import { IDependencyType, LockfileDependency } from '../../parsing/LockfileDependency'; +import { LfxDependencyKind, type LfxGraphDependency, type LfxGraphEntry } from '../../packlets/lfx-shared'; +import { readPackageJsonAsync } from '../../helpers/lfxApiClient'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; import { pushToStack, selectCurrentEntry } from '../../store/slices/entrySlice'; import { ReactNull } from '../../types/ReactNull'; -import { LockfileEntry } from '../../parsing/LockfileEntry'; import { logDiagnosticInfo } from '../../helpers/logDiagnosticInfo'; import { displaySpecChanges } from '../../helpers/displaySpecChanges'; +import type { IPackageJson } from '../../types/IPackageJson'; enum DependencyType { Determinant, TransitiveReferrer } +enum DependencyKey { + Regular = 'dependencies', + Dev = 'devDependencies', + Peer = 'peerDependencies' +} + interface IInfluencerType { - entry: LockfileEntry; + entry: LfxGraphEntry; type: DependencyType; } -export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => { +export const LockfileEntryDetailsView = (): React.ReactElement | ReactNull => { const selectedEntry = useAppSelector(selectCurrentEntry); const specChanges = useAppSelector((state) => state.workspace.specChanges); const dispatch = useAppDispatch(); - const [inspectDependency, setInspectDependency] = useState(null); + const [inspectDependency, setInspectDependency] = useState(null); const [influencers, setInfluencers] = useState([]); + const [directRefsPackageJSON, setDirectRefsPackageJSON] = useState>( + new Map() + ); useEffect(() => { + async function loadPackageJson(referrers: LfxGraphEntry[]): Promise { + const referrersJsonMap = new Map(); + await Promise.all( + referrers.map(async (ref) => { + const packageJson = await readPackageJsonAsync(ref.packageJsonFolderPath); + referrersJsonMap.set(ref.rawEntryId, packageJson); + return packageJson; + }) + ); + + setDirectRefsPackageJSON(referrersJsonMap); + } + + loadPackageJson(selectedEntry?.referrers || []).catch((e) => { + // eslint-disable-next-line no-console + console.error(`Failed to load referrers package.json: ${e}`); + }); if (selectedEntry) { setInspectDependency(null); } }, [selectedEntry]); const selectResolvedEntry = useCallback( - (dependencyToTrace) => () => { + (dependencyToTrace: LfxGraphDependency) => () => { if (inspectDependency && inspectDependency.entryId === dependencyToTrace.entryId) { if (dependencyToTrace.resolvedEntry) { dispatch(pushToStack(dependencyToTrace.resolvedEntry)); } else { - logDiagnosticInfo('No resolved entry for dependency:', dependencyToTrace); + logDiagnosticInfo('No resolved entry for dependency:', dependencyToTrace.entryId); } } else if (selectedEntry) { + // eslint-disable-next-line no-console console.log('dependency to trace: ', dependencyToTrace); setInspectDependency(dependencyToTrace); // Check if we need to calculate influencers. // If the current dependencyToTrace is a peer dependency then we do - if (dependencyToTrace.dependencyType !== IDependencyType.PEER_DEPENDENCY) { + if (dependencyToTrace.dependencyKind !== LfxDependencyKind.Peer) { return; } // calculate influencers const stack = [selectedEntry]; - const determinants = new Set(); - const transitiveReferrers = new Set(); - const visitedNodes = new Set(); + const determinants = new Set(); + const transitiveReferrers = new Set(); + const visitedNodes = new Set(); visitedNodes.add(selectedEntry); while (stack.length) { const currEntry = stack.pop(); if (currEntry) { - for (const referrer of currEntry.referrers) { + for (const referrer1 of currEntry.referrers) { let hasDependency = false; - for (const dependency of referrer.dependencies) { + for (const dependency of referrer1.dependencies) { if (dependency.name === dependencyToTrace.name) { - determinants.add(referrer); + determinants.add(referrer1); hasDependency = true; break; } } if (!hasDependency) { - if (referrer.transitivePeerDependencies.has(dependencyToTrace.name)) { - transitiveReferrers.add(referrer); + if (referrer1.transitivePeerDependencies.has(dependencyToTrace.name)) { + transitiveReferrers.add(referrer1); } else { // Since this referrer does not declare "dependency", it is a // transitive peer dependency, and we call the referrer a "transitive referrer". @@ -83,50 +113,54 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => { // YAML file. If not, either something is wrong with our algorithm, or else // something has changed about how PNPM manages its "transitivePeerDependencies" // field. + // eslint-disable-next-line no-console console.error( 'Error analyzing influencers: A referrer appears to be missing its "transitivePeerDependencies" field in the YAML file: ', dependencyToTrace, - referrer, + referrer1, currEntry ); } - for (const referrer of currEntry.referrers) { - if (!visitedNodes.has(referrer)) { - stack.push(referrer); - visitedNodes.add(referrer); + + for (const referrer2 of currEntry.referrers) { + if (!visitedNodes.has(referrer2)) { + stack.push(referrer2); + visitedNodes.add(referrer2); } } } } } } - const influencers: IInfluencerType[] = []; + const newInfluencers: IInfluencerType[] = []; for (const determinant of determinants.values()) { - influencers.push({ + newInfluencers.push({ entry: determinant, type: DependencyType.Determinant }); } for (const referrer of transitiveReferrers.values()) { - influencers.push({ + newInfluencers.push({ entry: referrer, type: DependencyType.TransitiveReferrer }); } - setInfluencers(influencers); + setInfluencers(newInfluencers); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [selectedEntry, inspectDependency] ); const selectResolvedReferencer = useCallback( - (referrer) => () => { + (referrer: LfxGraphEntry) => () => { dispatch(pushToStack(referrer)); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [selectedEntry] ); - const renderDependencyMetadata = (): JSX.Element | ReactNull => { + const renderDependencyMetadata = (): React.ReactElement | ReactNull => { if (!inspectDependency) { return ReactNull; } @@ -138,7 +172,7 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => { Selected Dependency:{' '} - {inspectDependency.name}: {inspectDependency.version} + {inspectDependency.name}: {inspectDependency.versionPath}
@@ -146,11 +180,11 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => { package.json spec:{' '} - {inspectDependency.dependencyType === IDependencyType.PEER_DEPENDENCY + {inspectDependency.dependencyKind === LfxDependencyKind.Peer ? `"${inspectDependency.peerDependencyMeta.version}" ${ inspectDependency.peerDependencyMeta.optional ? 'Optional' : 'Required' } Peer` - : inspectDependency.version} + : inspectDependency.versionPath}
@@ -168,11 +202,9 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => { ); }; - const renderPeerDependencies = (): JSX.Element | ReactNull => { + const renderPeerDependencies = (): React.ReactElement | ReactNull => { if (!selectedEntry) return ReactNull; - const peerDeps = selectedEntry.dependencies.filter( - (d) => d.dependencyType === IDependencyType.PEER_DEPENDENCY - ); + const peerDeps = selectedEntry.dependencies.filter((d) => d.dependencyKind === LfxDependencyKind.Peer); if (!peerDeps.length) { return (
@@ -180,7 +212,7 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => {
); } - if (!inspectDependency || inspectDependency.dependencyType !== IDependencyType.PEER_DEPENDENCY) { + if (!inspectDependency || inspectDependency.dependencyKind !== LfxDependencyKind.Peer) { return (
Select a peer dependency to view its influencers @@ -231,6 +263,24 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => { ); }; + const getDependencyInfo = ( + rawEntryId: string, + entryPackageName: string + ): { type: DependencyKey; version: string } | undefined => { + const packageJson = directRefsPackageJSON.get(rawEntryId); + if (!packageJson) return undefined; + + const dependencyTypes = [DependencyKey.Regular, DependencyKey.Dev, DependencyKey.Peer]; + + for (const type of dependencyTypes) { + const version = packageJson[type]?.[entryPackageName]; + if (version) { + return { type, version }; + } + } + return undefined; + }; + if (!selectedEntry) { return (
@@ -250,7 +300,7 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => {
- {selectedEntry.referrers?.map((referrer: LockfileEntry) => ( + {selectedEntry.referrers?.map((referrer: LfxGraphEntry) => (
{
Entry ID: {referrer.rawEntryId} + + {'Dependency version: '} + {getDependencyInfo(referrer.rawEntryId, selectedEntry.entryPackageName)?.version} +
))} @@ -273,7 +327,7 @@ export const LockfileEntryDetailsView = (): JSX.Element | ReactNull => {
- {selectedEntry.dependencies?.map((dependency: LockfileDependency) => ( + {selectedEntry.dependencies?.map((dependency: LfxGraphDependency) => (
{ > Name: {dependency.name}{' '} - {dependency.dependencyType === IDependencyType.PEER_DEPENDENCY + {dependency.dependencyKind === LfxDependencyKind.Peer ? `${ dependency.peerDependencyMeta.optional ? '(Optional)' : '(Non-optional)' } Peer Dependency` : ''}
- Version: {dependency.version} + Version: {dependency.versionPath} Entry ID: {dependency.entryId}
diff --git a/apps/lockfile-explorer-web/src/containers/LockfileViewer/index.tsx b/apps/lockfile-explorer-web/src/containers/LockfileViewer/index.tsx index d4a1083786f..6467ae37f12 100644 --- a/apps/lockfile-explorer-web/src/containers/LockfileViewer/index.tsx +++ b/apps/lockfile-explorer-web/src/containers/LockfileViewer/index.tsx @@ -2,8 +2,11 @@ // See LICENSE in the project root for license information. import React, { useCallback, useEffect, useRef, useState } from 'react'; + +import { Tabs, Checkbox, ScrollArea, Input, Text } from '@rushstack/rush-themed-ui'; + import styles from './styles.scss'; -import { LockfileEntry, LockfileEntryFilter } from '../../parsing/LockfileEntry'; +import { type LfxGraphEntry, LfxGraphEntryKind } from '../../packlets/lfx-shared'; import { ReactNull } from '../../types/ReactNull'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; import { @@ -13,34 +16,33 @@ import { setFilter as selectFilter } from '../../store/slices/entrySlice'; import { getFilterFromLocalStorage, saveFilterToLocalStorage } from '../../helpers/localStorage'; -import { Tabs, Checkbox, ScrollArea, Input, Text } from '@rushstack/rush-themed-ui'; interface ILockfileEntryGroup { entryName: string; - versions: LockfileEntry[]; + versions: LfxGraphEntry[]; } -const LockfileEntryLi = ({ group }: { group: ILockfileEntryGroup }): JSX.Element => { +const LockfileEntryLi = ({ group }: { group: ILockfileEntryGroup }): React.ReactElement => { const selectedEntry = useAppSelector(selectCurrentEntry); const activeFilters = useAppSelector((state) => state.entry.filters); const dispatch = useAppDispatch(); - const fieldRef = useRef() as React.MutableRefObject; + const fieldRef = useRef(null); const clear = useCallback( - (entry: LockfileEntry) => () => { + (entry: LfxGraphEntry) => () => { dispatch(pushToStack(entry)); }, - [] + [dispatch] ); useEffect(() => { if (selectedEntry && selectedEntry.entryPackageName === group.entryName) { - fieldRef.current.scrollIntoView({ + fieldRef.current?.scrollIntoView({ behavior: 'smooth' }); } }, [selectedEntry, group]); - if (activeFilters[LockfileEntryFilter.Project]) { + if (activeFilters[LfxGraphEntryKind.Project]) { return (
{group.versions.map((entry) => ( @@ -80,7 +82,7 @@ const LockfileEntryLi = ({ group }: { group: ILockfileEntryGroup }): JSX.Element ); }; -const multipleVersions = (entries: LockfileEntry[]): boolean => { +const multipleVersions = (entries: LfxGraphEntry[]): boolean => { const set = new Set(); for (const entry of entries) { if (set.has(entry.entryPackageVersion)) return true; @@ -89,15 +91,15 @@ const multipleVersions = (entries: LockfileEntry[]): boolean => { return false; }; -export const LockfileViewer = (): JSX.Element | ReactNull => { +export const LockfileViewer = (): React.ReactElement | ReactNull => { const dispatch = useAppDispatch(); const [projectFilter, setProjectFilter] = useState(''); const [packageFilter, setPackageFilter] = useState(''); const entries = useAppSelector(selectFilteredEntries); const activeFilters = useAppSelector((state) => state.entry.filters); const updateFilter = useCallback( - (type: LockfileEntryFilter) => (e: React.ChangeEvent) => { - if (type === LockfileEntryFilter.Project) { + (type: LfxGraphEntryKind) => (e: React.ChangeEvent) => { + if (type === LfxGraphEntryKind.Project) { setProjectFilter(e.target.value); } else { setPackageFilter(e.target.value); @@ -108,42 +110,40 @@ export const LockfileViewer = (): JSX.Element | ReactNull => { ); useEffect(() => { - setProjectFilter(getFilterFromLocalStorage(LockfileEntryFilter.Project)); - setPackageFilter(getFilterFromLocalStorage(LockfileEntryFilter.Package)); + setProjectFilter(getFilterFromLocalStorage(LfxGraphEntryKind.Project)); + setPackageFilter(getFilterFromLocalStorage(LfxGraphEntryKind.Package)); }, []); - if (!entries) return ReactNull; - const getEntriesToShow = (): ILockfileEntryGroup[] => { - let filteredEntries: LockfileEntry[] = entries; - if (projectFilter && activeFilters[LockfileEntryFilter.Project]) { + let filteredEntries: LfxGraphEntry[] = entries; + if (projectFilter && activeFilters[LfxGraphEntryKind.Project]) { filteredEntries = entries.filter((entry) => entry.entryPackageName.indexOf(projectFilter) !== -1); - } else if (packageFilter && activeFilters[LockfileEntryFilter.Package]) { + } else if (packageFilter && activeFilters[LfxGraphEntryKind.Package]) { filteredEntries = entries.filter((entry) => entry.entryPackageName.indexOf(packageFilter) !== -1); } - const reducedEntries = filteredEntries.reduce((groups: { [key in string]: LockfileEntry[] }, item) => { + const reducedEntries = filteredEntries.reduce((groups: { [key: string]: LfxGraphEntry[] }, item) => { const group = groups[item.entryPackageName] || []; group.push(item); groups[item.entryPackageName] = group; return groups; }, {}); let groupedEntries: ILockfileEntryGroup[] = []; - for (const [packageName, entries] of Object.entries(reducedEntries)) { + for (const [packageName, versions] of Object.entries(reducedEntries)) { groupedEntries.push({ entryName: packageName, - versions: entries + versions }); } - if (activeFilters[LockfileEntryFilter.SideBySide]) { + if (activeFilters[LfxGraphEntryKind.SideBySide]) { groupedEntries = groupedEntries.filter((entry) => entry.versions.length > 1); } - if (activeFilters[LockfileEntryFilter.Doppelganger]) { + if (activeFilters[LfxGraphEntryKind.Doppelganger]) { groupedEntries = groupedEntries.filter((entry) => multipleVersions(entry.versions)); } - if (activeFilters[LockfileEntryFilter.Project]) { + if (activeFilters[LfxGraphEntryKind.Project]) { groupedEntries = groupedEntries.sort((a, b) => a.entryName > b.entryName ? 1 : b.entryName > a.entryName ? -1 : 0 ); @@ -153,79 +153,82 @@ export const LockfileViewer = (): JSX.Element | ReactNull => { }; const changeFilter = useCallback( - (filter: LockfileEntryFilter, enabled: boolean) => (): void => { + (filter: LfxGraphEntryKind, enabled: boolean) => (): void => { dispatch(selectFilter({ filter, state: enabled })); }, - [] + [dispatch] ); const togglePackageView = useCallback( (selected: string) => { if (selected === 'Projects') { - dispatch(selectFilter({ filter: LockfileEntryFilter.Project, state: true })); - dispatch(selectFilter({ filter: LockfileEntryFilter.Package, state: false })); + dispatch(selectFilter({ filter: LfxGraphEntryKind.Project, state: true })); + dispatch(selectFilter({ filter: LfxGraphEntryKind.Package, state: false })); } else { - dispatch(selectFilter({ filter: LockfileEntryFilter.Package, state: true })); - dispatch(selectFilter({ filter: LockfileEntryFilter.Project, state: false })); + dispatch(selectFilter({ filter: LfxGraphEntryKind.Package, state: true })); + dispatch(selectFilter({ filter: LfxGraphEntryKind.Project, state: false })); } }, - [activeFilters] + // eslint-disable-next-line react-hooks/exhaustive-deps + [dispatch, activeFilters] ); - return ( -
-
- - - - {getEntriesToShow().map((lockfileEntryGroup) => ( - - ))} - - {activeFilters[LockfileEntryFilter.Package] ? ( -
- - Filters - - - -
- ) : null} + if (!entries) { + return ReactNull; + } else { + return ( +
+
+ + + + {getEntriesToShow().map((lockfileEntryGroup) => ( + + ))} + + {activeFilters[LfxGraphEntryKind.Package] ? ( +
+ + Filters + + + +
+ ) : null} +
-
- ); + ); + } }; diff --git a/apps/lockfile-explorer-web/src/containers/LogoPanel/index.tsx b/apps/lockfile-explorer-web/src/containers/LogoPanel/index.tsx index 0d13a4e4abc..2c9ab618525 100644 --- a/apps/lockfile-explorer-web/src/containers/LogoPanel/index.tsx +++ b/apps/lockfile-explorer-web/src/containers/LogoPanel/index.tsx @@ -2,9 +2,10 @@ // See LICENSE in the project root for license information. import React from 'react'; + import styles from './styles.scss'; -export const LogoPanel = (): JSX.Element => { +export const LogoPanel = (): React.ReactElement => { // TODO: Add a mechanism to keep this in sync with the @rushstack/lockfile-explorer // package version. const appPackageVersion: string = window.appContext.appVersion; @@ -13,16 +14,28 @@ export const LogoPanel = (): JSX.Element => {
- +
- +
{appPackageVersion}
diff --git a/apps/lockfile-explorer-web/src/containers/PackageJsonViewer/CodeBox.tsx b/apps/lockfile-explorer-web/src/containers/PackageJsonViewer/CodeBox.tsx new file mode 100644 index 00000000000..be941756ca6 --- /dev/null +++ b/apps/lockfile-explorer-web/src/containers/PackageJsonViewer/CodeBox.tsx @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import React from 'react'; +import { Highlight, themes } from 'prism-react-renderer'; + +// Generate this list by doing console.log(Object.keys(Prism.languages)) +// BUT THEN DELETE the APIs that are bizarrely mixed into this namespace: +// "extend", "insertBefore", "DFS" +export type PrismLanguage = + | 'plain' + | 'plaintext' + | 'text' + | 'txt' + | 'markup' + | 'html' + | 'mathml' + | 'svg' + | 'xml' + | 'ssml' + | 'atom' + | 'rss' + | 'regex' + | 'clike' + | 'javascript' + | 'js' + | 'actionscript' + | 'coffeescript' + | 'coffee' + | 'javadoclike' + | 'css' + | 'yaml' + | 'yml' + | 'markdown' + | 'md' + | 'graphql' + | 'sql' + | 'typescript' + | 'ts' + | 'jsdoc' + | 'flow' + | 'n4js' + | 'n4jsd' + | 'jsx' + | 'tsx' + | 'swift' + | 'kotlin' + | 'kt' + | 'kts' + | 'c' + | 'objectivec' + | 'objc' + | 'reason' + | 'rust' + | 'go' + | 'cpp' + | 'python' + | 'py' + | 'json' + | 'webmanifest'; + +export const CodeBox = (props: { code: string; language: PrismLanguage }): React.ReactElement => { + return ( + + {({ className, style, tokens, getLineProps, getTokenProps }) => ( +
+          {tokens.map((line, i) => (
+            
+ {line.map((token, key) => ( + + ))} +
+ ))} +
+ )} +
+ ); +}; diff --git a/apps/lockfile-explorer-web/src/containers/PackageJsonViewer/index.tsx b/apps/lockfile-explorer-web/src/containers/PackageJsonViewer/index.tsx index 94087655684..906c58fd0af 100644 --- a/apps/lockfile-explorer-web/src/containers/PackageJsonViewer/index.tsx +++ b/apps/lockfile-explorer-web/src/containers/PackageJsonViewer/index.tsx @@ -2,25 +2,28 @@ // See LICENSE in the project root for license information. import React, { useCallback, useEffect, useState } from 'react'; -import { readPnpmfileAsync, readPackageSpecAsync, readPackageJsonAsync } from '../../parsing/getPackageFiles'; -import styles from './styles.scss'; + +import { ScrollArea, Tabs, Text } from '@rushstack/rush-themed-ui'; + +import { readPnpmfileAsync, readPackageSpecAsync, readPackageJsonAsync } from '../../helpers/lfxApiClient'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; import { selectCurrentEntry } from '../../store/slices/entrySlice'; -import { IPackageJson } from '../../types/IPackageJson'; +import type { IPackageJson } from '../../types/IPackageJson'; import { compareSpec } from '../../parsing/compareSpec'; import { loadSpecChanges } from '../../store/slices/workspaceSlice'; import { displaySpecChanges } from '../../helpers/displaySpecChanges'; import { isEntryModified } from '../../helpers/isEntryModified'; -import { ScrollArea, Tabs, Text } from '@rushstack/rush-themed-ui'; -import { LockfileEntryFilter } from '../../parsing/LockfileEntry'; +import { LfxGraphEntryKind } from '../../packlets/lfx-shared'; +import { CodeBox } from './CodeBox'; +import styles from './styles.scss'; -const PackageView: { [key in string]: string } = { +const PackageView: { [key: string]: string } = { PACKAGE_JSON: 'PACKAGE_JSON', PACKAGE_SPEC: 'PACKAGE_SPEC', PARSED_PACKAGE_JSON: 'PARSED_PACKAGE_JSON' }; -export const PackageJsonViewer = (): JSX.Element => { +export const PackageJsonViewer = (): React.ReactElement => { const dispatch = useAppDispatch(); const [packageJSON, setPackageJSON] = useState(undefined); const [parsedPackageJSON, setParsedPackageJSON] = useState(undefined); @@ -37,19 +40,20 @@ export const PackageJsonViewer = (): JSX.Element => { useEffect(() => { async function loadPnpmFileAsync(): Promise { - const pnpmfile = await readPnpmfileAsync(); - setPnpmfile(pnpmfile); + const repoPnpmfile = await readPnpmfileAsync(); + setPnpmfile(repoPnpmfile); } loadPnpmFileAsync().catch((e) => { + // eslint-disable-next-line no-console console.error(`Failed to load project's pnpm file: ${e}`); }); }, []); useEffect(() => { async function loadPackageDetailsAsync(packageName: string): Promise { - const packageJSONFile = await readPackageJsonAsync(packageName); + const packageJSONFile: IPackageJson | undefined = await readPackageJsonAsync(packageName); setPackageJSON(packageJSONFile); - const parsedJSON = await readPackageSpecAsync(packageName); + const parsedJSON: IPackageJson | undefined = await readPackageSpecAsync(packageName); setParsedPackageJSON(parsedJSON); if (packageJSONFile && parsedJSON) { @@ -60,17 +64,19 @@ export const PackageJsonViewer = (): JSX.Element => { if (selectedEntry) { if (selectedEntry.entryPackageName) { loadPackageDetailsAsync(selectedEntry.packageJsonFolderPath).catch((e) => { + // eslint-disable-next-line no-console console.error(`Failed to load project information: ${e}`); }); } else { // This is used to develop the lockfile explorer application in case there is a mistake in our logic + // eslint-disable-next-line no-console console.log('The selected entry has no entry name: ', selectedEntry.entryPackageName); } } - }, [selectedEntry]); + }, [dispatch, selectedEntry]); const renderDep = - (name: boolean): ((dependencyDetails: [string, string]) => JSX.Element) => + (name: boolean): ((dependencyDetails: [string, string]) => React.ReactElement) => (dependencyDetails) => { const [dep, version] = dependencyDetails; if (specChanges.has(dep)) { @@ -149,7 +155,7 @@ export const PackageJsonViewer = (): JSX.Element => { } }; - const renderFile = (): JSX.Element | null => { + const renderFile = (): React.ReactElement | null => { switch (selection) { case PackageView.PACKAGE_JSON: if (!packageJSON) @@ -158,7 +164,7 @@ export const PackageJsonViewer = (): JSX.Element => { Please select a Project or Package to view it's package.json ); - return
{JSON.stringify(packageJSON, null, 2)}
; + return ; case PackageView.PACKAGE_SPEC: if (!pnpmfile) { return ( @@ -168,7 +174,7 @@ export const PackageJsonViewer = (): JSX.Element => { ); } - return
{pnpmfile}
; + return ; case PackageView.PARSED_PACKAGE_JSON: if (!parsedPackageJSON) return ( @@ -183,7 +189,7 @@ export const PackageJsonViewer = (): JSX.Element => { Package Name: - {selectedEntry?.kind === LockfileEntryFilter.Project + {selectedEntry?.kind === LfxGraphEntryKind.Project ? parsedPackageJSON.name : selectedEntry?.displayText} diff --git a/apps/lockfile-explorer-web/src/containers/SelectedEntryPreview/index.tsx b/apps/lockfile-explorer-web/src/containers/SelectedEntryPreview/index.tsx index 14c5ca393f9..310b7e3cf7d 100644 --- a/apps/lockfile-explorer-web/src/containers/SelectedEntryPreview/index.tsx +++ b/apps/lockfile-explorer-web/src/containers/SelectedEntryPreview/index.tsx @@ -2,6 +2,9 @@ // See LICENSE in the project root for license information. import React, { useCallback } from 'react'; + +import { Button, ScrollArea, Text } from '@rushstack/rush-themed-ui'; + import styles from './styles.scss'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; import { @@ -11,9 +14,8 @@ import { removeBookmark, selectCurrentEntry } from '../../store/slices/entrySlice'; -import { Button, ScrollArea, Text } from '@rushstack/rush-themed-ui'; -export const SelectedEntryPreview = (): JSX.Element => { +export const SelectedEntryPreview = (): React.ReactElement => { const selectedEntry = useAppSelector(selectCurrentEntry); const isBookmarked = useAppSelector((state) => selectedEntry ? state.entry.bookmarkedEntries.includes(selectedEntry) : false @@ -21,23 +23,23 @@ export const SelectedEntryPreview = (): JSX.Element => { const entryStack = useAppSelector((state) => state.entry.selectedEntryStack); const entryForwardStack = useAppSelector((state) => state.entry.selectedEntryForwardStack); - const useDispatch = useAppDispatch(); + const dispatch = useAppDispatch(); const bookmark = useCallback(() => { - if (selectedEntry) useDispatch(addBookmark(selectedEntry)); - }, [selectedEntry]); + if (selectedEntry) dispatch(addBookmark(selectedEntry)); + }, [dispatch, selectedEntry]); const deleteEntry = useCallback(() => { - if (selectedEntry) useDispatch(removeBookmark(selectedEntry)); - }, [selectedEntry]); + if (selectedEntry) dispatch(removeBookmark(selectedEntry)); + }, [dispatch, selectedEntry]); const pop = useCallback(() => { - useDispatch(popStack()); - }, []); + dispatch(popStack()); + }, [dispatch]); const forward = useCallback(() => { - useDispatch(forwardStack()); - }, []); + dispatch(forwardStack()); + }, [dispatch]); - const renderButtonRow = (): JSX.Element => { + const renderButtonRow = (): React.ReactElement => { return (
+ + + + + + +
+
+
+ + + + +
+
+
+ + +
+
+ +
+
+ Dependency Graph + +
+
+ + + + + + + + +
+
+ +
+
+
+
+ + + +
+
+
+
+ +
+ +
+
+
Terminal Output
+
+ + + + + +
+
+
+
+
+ + diff --git a/apps/rush-serve-dashboard/config/heft.json b/apps/rush-serve-dashboard/config/heft.json new file mode 100644 index 00000000000..2086b83cf1d --- /dev/null +++ b/apps/rush-serve-dashboard/config/heft.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-web-rig/profiles/app/config/heft.json" +} diff --git a/apps/rush-serve-dashboard/config/rig.json b/apps/rush-serve-dashboard/config/rig.json new file mode 100644 index 00000000000..26f617ab3fc --- /dev/null +++ b/apps/rush-serve-dashboard/config/rig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-web-rig", + "rigProfile": "app" +} diff --git a/apps/rush-serve-dashboard/eslint.config.js b/apps/rush-serve-dashboard/eslint.config.js new file mode 100644 index 00000000000..72fe2a29d25 --- /dev/null +++ b/apps/rush-serve-dashboard/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-web-rig/profiles/app/includes/eslint/flat/profile/web-app'); + +module.exports = [ + ...webAppProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/apps/rush-serve-dashboard/package.json b/apps/rush-serve-dashboard/package.json new file mode 100644 index 00000000000..df19cbc9db8 --- /dev/null +++ b/apps/rush-serve-dashboard/package.json @@ -0,0 +1,24 @@ +{ + "name": "@rushstack/rush-serve-dashboard", + "version": "0.0.0", + "description": "Web dashboard for the Rush serve WebSocket protocol", + "private": true, + "license": "MIT", + "scripts": { + "build": "heft test --clean", + "start": "heft start", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "dependencies": { + "tslib": "~2.8.1" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "eslint": "~9.37.0", + "local-web-rig": "workspace:*" + }, + "sideEffects": [ + "**/*.css" + ] +} diff --git a/apps/rush-serve-dashboard/src/dashboard.ts b/apps/rush-serve-dashboard/src/dashboard.ts new file mode 100644 index 00000000000..4ef26f4a83e --- /dev/null +++ b/apps/rush-serve-dashboard/src/dashboard.ts @@ -0,0 +1,623 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import globalStyles from './styles/global.module.css'; +import graphStyles from './styles/graphView.module.css'; +import selectionStyles from './styles/selectionBar.module.css'; +import tableStyles from './styles/tableView.module.css'; +import terminalStyles from './styles/terminalPane.module.css'; +import topBarStyles from './styles/topBar.module.css'; +import { + applyExecutionStates as applyExecutionStatesMutation, + patchOperationsFromPayload, + setOperationsFromPayload, + setQueuedStates, + toLastExecutionResultsMap +} from './modules/dashboardMutations'; +import { + computeWsUrl as computeWebSocketUrl, + overallStatusText, + setConnected as setTopBarConnected, + showConnectingStatus, + updateDerivedUrlDisplay as updateTopBarDerivedUrlDisplay, + updateManagerState as updateTopBarManagerState, + updateStatusPill as updateTopBarStatusPill, + type ITopBarRefs +} from './modules/topBar'; +import { createTerminalPaneController, type ITerminalPaneController } from './modules/terminalPane'; +import { createDashboardWebSocketController } from './modules/dashboardWebSocket'; +import { computeFilterSetsCore } from './modules/graphFiltering'; +import { createGraphViewController, graphState } from './modules/graphView'; +import { createGraphSelectionController } from './modules/graphSelection'; +import { createSelectionBarController } from './modules/selectionBar'; +import { createTableViewController } from './modules/tableView'; +import { createPhaseLegendController } from './modules/phaseLegend'; +import { wireLeftBarActions } from './modules/leftBar'; +import { wireMainBarActions } from './modules/mainBar'; +import { wireViewBar } from './modules/viewBar'; +import { loadDashboardUrlState, type DashboardFilter, type DashboardView } from './modules/urlState'; +import { + buildRunPolicyText, + buildTooltip, + computeDisplayStatus as computeDisplayStatusCore, + enabledGlyph, + getStatusColors, + statusEmoji +} from './modules/statusHelpers'; + +interface IOperationLogFileURLs { + text?: string; + error?: string; + jsonl?: string; +} + +interface IOperationInfo { + name: string; + dependencies?: string[]; + phaseName?: string; + packageName?: string; + noop?: boolean; + enabled?: string; + status?: string; + runInThisIteration?: boolean; + isActive?: boolean; + logFileURLs?: IOperationLogFileURLs; +} + +interface IOperationExecutionState { + name: string; + status?: string; + runInThisIteration?: boolean; + isActive?: boolean; + logFileURLs?: IOperationLogFileURLs; +} + +interface IDashboardGraphState { + status?: string; + debugMode?: boolean; + verbose?: boolean; + pauseNextIteration?: boolean; + parallelism?: number | string; + hasScheduledIteration?: boolean; +} + +interface IDashboardSessionInfo { + actionName: string; + repositoryIdentifier: string; +} + +interface IDashboardMessage { + event: string; + operations?: IOperationInfo[]; + currentExecutionStates?: IOperationExecutionState[]; + executionStates?: IOperationExecutionState[]; + queuedStates?: IOperationExecutionState[]; + graphState?: IDashboardGraphState; + resultByOperation?: IOperationExecutionState[]; + status?: string; + sessionInfo?: IDashboardSessionInfo; + kind?: string; + text?: string; +} + +interface IGraphViewControllerLike { + markGraphDirty(): void; + ensureGraph(): void; + updateGraph(): void; +} + +function addClasses(id: string, ...classNames: string[]): void { + document.getElementById(id)?.classList.add(...classNames); +} + +function applyStaticStyles(): void { + document.body.classList.add(globalStyles.dashboard); + addClasses('top-bar', topBarStyles.topBar); + addClasses('overall-status', topBarStyles.overallStatus); + addClasses('status-emoji', topBarStyles.statusEmoji); + addClasses( + 'status-pill', + globalStyles.statusPill, + topBarStyles.statusPill, + globalStyles.statusDisconnected + ); + addClasses('app-title', globalStyles.appTitle); + addClasses('actions', globalStyles.actions, topBarStyles.actions); + addClasses('graph-state', globalStyles.graphState, topBarStyles.graphState); + addClasses('view-controls', globalStyles.viewControls, topBarStyles.viewControls); + addClasses('parallelism-label', globalStyles.flexRow, globalStyles.parallelismLabel); + addClasses('parallelism-input', globalStyles.parallelismInput); + addClasses('search-label', globalStyles.searchLabel); + addClasses('name-search', globalStyles.nameSearch); + addClasses('top-bar-spacer', globalStyles.flexSpacer); + addClasses( + 'connection-form', + globalStyles.flexRow, + globalStyles.connectionForm, + topBarStyles.connectionForm + ); + addClasses('connect-btn', topBarStyles.connectButton); + + const iconButtonIds: string[] = [ + 'play-pause-btn', + 'execute-btn', + 'abort-execution-btn', + 'debug-btn', + 'verbose-btn', + 'select-visible-btn', + 'connect-btn', + 'toggle-terminal-btn', + 'term-clear-btn', + 'term-autoscroll-btn' + ]; + iconButtonIds.forEach((id) => addClasses(id, globalStyles.iconBtn)); + addClasses('abort-execution-btn', globalStyles.stop); + ['debug-btn', 'verbose-btn', 'connect-btn', 'toggle-terminal-btn', 'term-autoscroll-btn'].forEach((id) => + addClasses(id, globalStyles.toggle) + ); + + addClasses('selection-bar', selectionStyles.selectionBar); + addClasses('selection-heading', selectionStyles.selectionHeading); + addClasses('selection-actions', selectionStyles.selectionActions); + const actionButtonIds: string[] = [ + 'clear-selection-btn', + 'invalidate-btn', + 'close-runners-btn', + 'set-enabled-default-btn', + 'set-enabled-ignore-deps-btn', + 'set-enabled-disabled-btn', + 'selection-mode-btn', + 'expand-deps-btn', + 'expand-consumers-btn' + ]; + actionButtonIds.forEach((id) => addClasses(id, globalStyles.action)); + addClasses('clear-selection-btn', selectionStyles.selectionCountButton); + addClasses('selection-mode-btn', globalStyles.toggle); + + addClasses('content-wrap', globalStyles.contentWrap); + addClasses('main', globalStyles.main); + addClasses('left', globalStyles.pane, globalStyles.leftPane); + addClasses('right', globalStyles.pane); + addClasses('operations-table-container', tableStyles.operationsTableContainer); + addClasses('operations-table', tableStyles.operationsTable); + addClasses('table-stats', tableStyles.tableStats); + addClasses('graph-container', graphStyles.graphContainer); + addClasses('phase-pane', graphStyles.phasePane); + addClasses('phase-groups', graphStyles.phaseGroups); + addClasses('graph-wrapper', graphStyles.graphWrapper); + addClasses('graph', graphStyles.graph); + addClasses('graph-legend', graphStyles.graphLegend); + + addClasses('resizer', terminalStyles.resizer); + addClasses('terminal', terminalStyles.terminalContainer); + addClasses('terminal-header', terminalStyles.terminalHeader); + addClasses('terminal-title', terminalStyles.terminalTitle); + addClasses('terminal-controls', terminalStyles.terminalControls); + addClasses('terminal-body', terminalStyles.terminalBody); + addClasses('term-autoscroll', terminalStyles.termAutoscroll); + addClasses('toggle-terminal-btn', terminalStyles.toggleTerminalButton); + addClasses('term-autoscroll-icon', terminalStyles.vertical); +} + +applyStaticStyles(); + +const statusPill: HTMLElement = document.getElementById('status-pill') as HTMLElement; +const statusEmojiEl: HTMLElement = document.getElementById('status-emoji') as HTMLElement; +const connectBtn: HTMLButtonElement | undefined = + (document.getElementById('connect-btn') as HTMLButtonElement | null) ?? undefined; +const appTitleEl: HTMLElement | undefined = document.getElementById('app-title') ?? undefined; +const tableEl: HTMLTableElement = document.getElementById('operations-table') as HTMLTableElement; +const tableHead: HTMLTableSectionElement | undefined = tableEl.querySelector('thead') ?? undefined; +const tableBody: HTMLTableSectionElement | undefined = tableEl.querySelector('tbody') ?? undefined; +const tableStats: HTMLElement | undefined = document.getElementById('table-stats') ?? undefined; +const managerStateEl: HTMLElement | undefined = document.getElementById('graph-state') ?? undefined; +const edgesSvg: SVGSVGElement | undefined = document.querySelector('svg#edges') ?? undefined; +if (!edgesSvg) { + throw new Error('The graph edges SVG element was not found.'); +} +const graphEl: HTMLElement = document.getElementById('graph') as HTMLElement; +const legendEl: HTMLElement | undefined = document.getElementById('graph-legend') ?? undefined; +const phaseGroupsEl: HTMLElement | undefined = document.getElementById('phase-groups') ?? undefined; +const playPauseBtn: HTMLButtonElement | undefined = + (document.getElementById('play-pause-btn') as HTMLButtonElement | null) ?? undefined; +const parallelismInput: HTMLInputElement | undefined = + (document.getElementById('parallelism-input') as HTMLInputElement | null) ?? undefined; +const debugBtn: HTMLButtonElement | undefined = + (document.getElementById('debug-btn') as HTMLButtonElement | null) ?? undefined; +const verboseBtn: HTMLButtonElement | undefined = + (document.getElementById('verbose-btn') as HTMLButtonElement | null) ?? undefined; +const terminalEl: HTMLElement | undefined = document.getElementById('terminal') ?? undefined; +const terminalBody: HTMLElement | undefined = document.getElementById('terminal-body') ?? undefined; +const termClearBtn: HTMLButtonElement | undefined = + (document.getElementById('term-clear-btn') as HTMLButtonElement | null) ?? undefined; +const termAutoScroll: HTMLInputElement | undefined = + (document.getElementById('term-autoscroll') as HTMLInputElement | null) ?? undefined; +const termAutoscrollBtn: HTMLButtonElement | undefined = + (document.getElementById('term-autoscroll-btn') as HTMLButtonElement | null) ?? undefined; +const toggleTerminalBtn: HTMLButtonElement | undefined = + (document.getElementById('toggle-terminal-btn') as HTMLButtonElement | null) ?? undefined; +const resizerEl: HTMLElement | undefined = document.getElementById('resizer') ?? undefined; + +const terminalPane: ITerminalPaneController = createTerminalPaneController({ + terminalEl, + terminalBody, + termClearBtn, + termAutoScrollCheckbox: termAutoScroll, + termAutoscrollBtn, + toggleTerminalBtn, + resizerEl +}); + +const topBarRefs: ITopBarRefs = { + connectBtn, + statusPill, + statusEmojiEl, + debugBtn, + verboseBtn, + playPauseBtn, + parallelismInput, + managerStateEl +}; + +const disabledControlIds: string[] = [ + 'invalidate-btn', + 'close-runners-btn', + 'set-enabled-default-btn', + 'set-enabled-ignore-deps-btn', + 'set-enabled-disabled-btn', + 'expand-deps-btn', + 'expand-consumers-btn', + 'execute-btn', + 'abort-execution-btn', + 'clear-selection-btn', + 'debug-btn', + 'verbose-btn', + 'parallelism-input', + 'play-pause-btn' +]; + +const operations: Map = new Map(); +const executionStates: Map = new Map(); +const queuedStates: Map = new Map(); +let lastExecutionResults: Map = new Map(); +let selection: Set = new Set(); +let graphSettings: IDashboardGraphState | undefined; +let currentView: DashboardView = 'table'; +let currentFilter: DashboardFilter = 'all'; +let searchQuery: string = ''; +let filteredOutNames: Set = new Set(); +let searchFilteredOutNames: Set = new Set(); + +function computeDisplayStatus(op: IOperationInfo): string { + return computeDisplayStatusCore(op, executionStates, lastExecutionResults); +} + +function computeVisibleOperations(): IOperationInfo[] { + const result: ReturnType = computeFilterSetsCore({ + operations, + executionStates, + currentFilter, + searchQuery, + computeDisplayStatus + }); + filteredOutNames = result.filteredOutNames; + searchFilteredOutNames = result.searchFilteredOutNames; + return result.visibleOperations; +} + +const tableViewController: ReturnType = createTableViewController({ + tableHead: tableHead || undefined, + tableBody: tableBody || undefined, + tableStats: tableStats || undefined, + getOperations: () => operations, + getFilteredOperations: () => computeVisibleOperations(), + getSelection: () => selection, + setSelection: (nextSelection: Set) => { + selection = nextSelection; + }, + onSelectionMutated: () => { + updateSelectionUI(); + render(); + }, + computeDisplayStatus, + enabledGlyph, + buildRunPolicyText, + buildTooltip, + statusEmoji, + overallStatusText +}); + +const phaseLegendController: ReturnType = createPhaseLegendController({ + phaseGroupsEl: phaseGroupsEl || undefined, + legendEl: legendEl || undefined, + getOperations: () => operations, + getGraphVisibleNames: () => + graphState.nodePositions.size ? new Set(graphState.nodePositions.keys()) : undefined, + computeDisplayStatus, + statusEmoji, + overallStatusText, + getStatusColors +}); + +function selectionChanged(): void { + updateSelectionUI(); + render(); +} + +const graphSelectionController: ReturnType = + createGraphSelectionController({ + graphEl, + getCurrentView: () => currentView, + getSelection: () => selection, + setSelection: (nextSelection: Set) => { + selection = nextSelection; + }, + getOperations: () => operations, + getGraphNodePositions: () => graphState.nodePositions, + graphNodeWidth: 28, + graphNodeHeight: 28, + onSelectionChanged: selectionChanged, + onLiveSelectionChanged: updateGraph + }); + +function singleSelect(name: string): void { + graphSelectionController.singleSelect(name); +} + +function toggleSelect(name: string): void { + graphSelectionController.toggleSelect(name); +} + +const graphViewController: IGraphViewControllerLike = createGraphViewController({ + graphEl, + edgesSvg, + getOperations: () => operations, + getExecutionStates: () => executionStates, + getQueuedStates: () => queuedStates, + getSelection: () => selection, + getFilteredOutNames: () => filteredOutNames, + getSearchFilteredOutNames: () => searchFilteredOutNames, + getLastExecutionResults: () => lastExecutionResults, + getComputeDisplayStatus: computeDisplayStatus, + getStatusEmoji: statusEmoji, + getOverallStatusText: overallStatusText, + renderPhaseLegend: () => phaseLegendController.renderAll(), + singleSelect, + toggleSelect +}); + +graphSelectionController.wireGraphMarqueeSelection(); + +function markGraphDirty(): void { + graphViewController.markGraphDirty(); + if (currentView === 'graph') { + ensureGraph(); + } +} + +function ensureGraph(): void { + graphViewController.ensureGraph(); +} + +function updateGraph(): void { + graphViewController.updateGraph(); +} + +function renderTable(): void { + tableViewController.renderTable(); +} + +function render(): void { + if (currentView === 'table') { + renderTable(); + } else { + ensureGraph(); + } + updateSelectionUI(); +} + +function setConnected(connected: boolean): void { + setTopBarConnected(topBarRefs, connected, updateSelectionUI, disabledControlIds); +} + +function updateDerivedUrlDisplay(): void { + updateTopBarDerivedUrlDisplay(connectBtn); +} + +function updateManagerState(): void { + if (!graphSettings) return; + updateTopBarManagerState(topBarRefs, graphSettings); +} + +function log(message: string): void { + const time: string = new Date().toLocaleTimeString(); + window.console.log('[' + time + '] ' + message); +} + +const socketController: ReturnType = + createDashboardWebSocketController({ + getUrl: () => computeWebSocketUrl(window.location), + onConnecting: () => { + showConnectingStatus(statusPill, statusEmojiEl, statusEmoji); + }, + onConnectedStateChange: (connected: boolean) => { + setConnected(connected); + }, + onOpen: () => { + updateStatusPill(); + }, + onClose: () => { + updateStatusPill(); + }, + onError: (event: Event) => { + log('WebSocket error: ' + event.type); + }, + onParsedMessage: (message: unknown) => { + handleMessage(message as IDashboardMessage); + }, + onParseError: (error: unknown) => { + log('Bad JSON: ' + String(error)); + }, + onLog: log + }); + +function updateStatusPill(): void { + updateTopBarStatusPill(topBarRefs, socketController.getSocket(), graphSettings, statusEmoji); +} + +const selectionBarController: ReturnType = createSelectionBarController({ + getSelection: () => selection, + getCurrentView: () => currentView, + isConnected: () => socketController.isConnected() +}); + +function connect(): void { + socketController.connect(); +} + +function disconnect(): void { + socketController.disconnect(); +} + +function sendCommand(cmd: unknown): void { + socketController.sendCommand(cmd); +} + +function handleMessage(msg: IDashboardMessage): void { + switch (msg.event) { + case 'sync': { + setOperationsFromPayload(operations, msg.operations || []); + executionStates.clear(); + applyExecutionStatesMutation(operations, executionStates, msg.currentExecutionStates || []); + setQueuedStates(queuedStates, msg.queuedStates || []); + graphSettings = msg.graphState; + lastExecutionResults = toLastExecutionResultsMap(msg.resultByOperation || []); + if (appTitleEl && msg.sessionInfo) { + const title: string = `${msg.sessionInfo.actionName} — ${msg.sessionInfo.repositoryIdentifier}`; + appTitleEl.textContent = title; + document.title = title; + } + markGraphDirty(); + break; + } + case 'sync-operations': { + patchOperationsFromPayload(operations, msg.operations || []); + markGraphDirty(); + break; + } + case 'sync-graph-state': { + graphSettings = msg.graphState; + break; + } + case 'iteration-scheduled': { + setQueuedStates(queuedStates, msg.queuedStates || []); + break; + } + case 'before-execute': + case 'status-change': { + applyExecutionStatesMutation(operations, executionStates, msg.executionStates || []); + break; + } + case 'after-execute': { + applyExecutionStatesMutation(operations, executionStates, msg.executionStates || []); + lastExecutionResults = toLastExecutionResultsMap(msg.resultByOperation || []); + if (graphSettings && msg.status) { + graphSettings.status = msg.status; + } + break; + } + case 'terminal-chunk': { + terminalPane.appendChunk(msg.kind, msg.text); + break; + } + } + + updateManagerState(); + updateStatusPill(); + render(); +} + +function updateSelectionUI(): void { + selectionBarController.updateSelectionUI(); +} + +function expandSelectionDependencies(): void { + graphSelectionController.expandSelectionDependencies(); +} + +function expandSelectionConsumers(): void { + graphSelectionController.expandSelectionConsumers(); +} + +function wireActions(): void { + wireMainBarActions({ + connect, + disconnect, + isConnected: () => socketController.isConnected(), + sendCommand, + getGraphSettings: () => graphSettings, + debugBtn, + verboseBtn, + parallelismInput, + playPauseBtn, + getOperationNames: () => Array.from(operations.keys()), + setSelection: (nextSelection: Set) => { + selection = nextSelection; + }, + clearSelection: () => { + selection.clear(); + }, + hasSelection: () => selection.size > 0, + render + }); + + wireLeftBarActions({ + sendCommand, + getSelection: () => selection, + clearSelectionAndRender: () => { + if (!selection.size) return; + selection.clear(); + render(); + }, + expandSelectionDependencies, + expandSelectionConsumers + }); + + wireViewBar({ + getView: () => currentView, + setView: (nextView: DashboardView) => { + currentView = nextView; + }, + getFilter: () => currentFilter, + setFilter: (nextFilter: DashboardFilter) => { + currentFilter = nextFilter; + }, + setSearchQuery: (nextSearchQuery: string) => { + searchQuery = nextSearchQuery; + }, + markGraphDirty, + render + }); +} +function init(): void { + const urlState: ReturnType = loadDashboardUrlState(window.location.search); + currentView = urlState.view; + currentFilter = urlState.filter; + + wireActions(); + updateDerivedUrlDisplay(); + updateSelectionUI(); + updateManagerState(); + updateStatusPill(); + connect(); + ( + window as Window & { + __rushServeDemo?: { operations: Map; selection: Set }; + } + ).__rushServeDemo = { + operations, + selection + }; +} + +init(); diff --git a/apps/rush-serve-dashboard/src/modules/ansiSgrParser.ts b/apps/rush-serve-dashboard/src/modules/ansiSgrParser.ts new file mode 100644 index 00000000000..68ac598e6cd --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/ansiSgrParser.ts @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export interface IAnsiSegment { + text: string; + style: string; +} + +interface IAnsiState { + bold: boolean; + underline: boolean; + inverse: boolean; + fg?: string; + bg?: string; +} + +export class AnsiSgrParser { + private readonly _state: IAnsiState = { + bold: false, + underline: false, + inverse: false, + fg: undefined, + bg: undefined + }; + + public process(input: string): IAnsiSegment[] { + let lastIndex: number = 0; + let searchIndex: number = 0; + const segments: IAnsiSegment[] = []; + + const pushSegmentIfText = (text: string): void => { + if (!text) return; + const style: string = this._ansiStateToStyle(this._state); + segments.push({ text, style }); + }; + + while (searchIndex < input.length) { + const escapeIndex: number = input.indexOf('\u001b[', searchIndex); + if (escapeIndex < 0) break; + + const suffixMatch: RegExpExecArray | undefined = + /^[0-9;]*m/.exec(input.slice(escapeIndex + 2)) ?? undefined; + if (!suffixMatch) { + searchIndex = escapeIndex + 2; + continue; + } + + if (escapeIndex > lastIndex) { + pushSegmentIfText(input.slice(lastIndex, escapeIndex)); + } + + const sequenceEnd: number = escapeIndex + 2 + suffixMatch[0].length; + const seq: string = input.slice(escapeIndex, sequenceEnd); + try { + this._applySgr(this._parseSgrParams(seq)); + } catch { + // Ignore malformed control sequences. + } + + lastIndex = sequenceEnd; + searchIndex = sequenceEnd; + } + + if (lastIndex < input.length) { + pushSegmentIfText(input.slice(lastIndex)); + } + + return segments; + } + + private _parseSgrParams(seq: string): number[] { + let s: string = seq; + if (s.startsWith('\u001b[')) { + s = s.slice(2); + } + if (s.endsWith('m')) { + s = s.slice(0, -1); + } + if (!s) return [0]; + return s.split(';').map((p) => Number(p || 0)); + } + + private _applySgr(params: number[]): void { + if (!params || !params.length) params = [0]; + + for (const p of params) { + if (p === 0) { + this._state.bold = false; + this._state.underline = false; + this._state.inverse = false; + this._state.fg = undefined; + this._state.bg = undefined; + } else if (p === 1) { + this._state.bold = true; + } else if (p === 4) { + this._state.underline = true; + } else if (p === 7) { + this._state.inverse = true; + } else if (p === 22) { + this._state.bold = false; + } else if (p === 24) { + this._state.underline = false; + } else if (p >= 30 && p <= 37) { + this._state.fg = this._sgrColorToCss(p - 30, false); + } else if (p === 39) { + this._state.fg = undefined; + } else if (p >= 40 && p <= 47) { + this._state.bg = this._sgrColorToCss(p - 40, false); + } else if (p === 49) { + this._state.bg = undefined; + } else if (p >= 90 && p <= 97) { + this._state.fg = this._sgrColorToCss(p - 90, true); + } else if (p >= 100 && p <= 107) { + this._state.bg = this._sgrColorToCss(p - 100, true); + } + } + } + + private _sgrColorToCss(idx: number, bright: boolean): string | undefined { + const base: string[] = ['#000000', '#a00', '#0a0', '#aa0', '#00a', '#a0a', '#0aa', '#ddd']; + const brightMap: string[] = [ + '#555', + '#ff5555', + '#55ff55', + '#ffff55', + '#5555ff', + '#ff55ff', + '#55ffff', + '#fff' + ]; + return bright ? brightMap[idx] || base[idx] : base[idx]; + } + + private _ansiStateToStyle(state: IAnsiState): string { + const styles: string[] = []; + if (state.fg) styles.push('color: ' + state.fg); + if (state.bg) styles.push('background-color: ' + state.bg); + if (state.bold) styles.push('font-weight: 700'); + if (state.underline) styles.push('text-decoration: underline'); + if (state.inverse) styles.push('filter: invert(100%)'); + return styles.join('; '); + } +} diff --git a/apps/rush-serve-dashboard/src/modules/dashboardMutations.ts b/apps/rush-serve-dashboard/src/modules/dashboardMutations.ts new file mode 100644 index 00000000000..c3c1f4bf251 --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/dashboardMutations.ts @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +interface IOperationLogFileURLs { + text?: string; + error?: string; + jsonl?: string; +} + +interface IOperationData { + name: string; + isActive?: boolean; + status?: string; + runInThisIteration?: boolean; + logFileURLs?: IOperationLogFileURLs; +} + +interface IStateEntry { + name: string; + isActive?: boolean; + status?: string; + runInThisIteration?: boolean; + logFileURLs?: IOperationLogFileURLs; +} + +export function applyExecutionStates( + operations: Map, + executionStates: Map, + stateArray: IStateEntry[] +): void { + if (!stateArray) return; + + stateArray.forEach((stateEntry) => { + executionStates.set(stateEntry.name, stateEntry); + + const op: IOperationData | undefined = operations.get(stateEntry.name); + if (!op) return; + + op.isActive = stateEntry.isActive; + op.status = stateEntry.status || op.status; + op.runInThisIteration = stateEntry.runInThisIteration; + op.logFileURLs = stateEntry.logFileURLs; + }); +} + +export function setQueuedStates(queuedStates: Map, stateArray: IStateEntry[]): void { + queuedStates.clear(); + if (!stateArray) return; + + stateArray.forEach((stateEntry) => { + queuedStates.set(stateEntry.name, stateEntry); + }); +} + +export function setOperationsFromPayload( + operations: Map, + operationArray: IOperationData[] +): void { + operations.clear(); + operationArray.forEach((op) => operations.set(op.name, op)); +} + +export function patchOperationsFromPayload( + operations: Map, + operationArray: IOperationData[] +): void { + operationArray.forEach((op) => operations.set(op.name, op)); +} + +export function toLastExecutionResultsMap( + lastExecutionResultsArray: IStateEntry[] | undefined +): Map { + const result: Map = new Map(); + if (!lastExecutionResultsArray) return result; + + lastExecutionResultsArray.forEach((entry) => { + result.set(entry.name, entry); + }); + + return result; +} diff --git a/apps/rush-serve-dashboard/src/modules/dashboardWebSocket.ts b/apps/rush-serve-dashboard/src/modules/dashboardWebSocket.ts new file mode 100644 index 00000000000..83c6e9cb8dd --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/dashboardWebSocket.ts @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export interface IDashboardWebSocketControllerOptions { + getUrl: () => string; + onConnecting: (url: string) => void; + onConnectedStateChange: (connected: boolean) => void; + onOpen: () => void; + onClose: () => void; + onError: (event: Event) => void; + onParsedMessage: (message: unknown) => void; + onParseError: (error: unknown) => void; + onLog: (message: string) => void; +} + +export interface IDashboardWebSocketController { + connect: () => void; + disconnect: () => void; + sendCommand: (command: unknown) => void; + isConnected: () => boolean; + getSocket: () => WebSocket | undefined; +} + +export function createDashboardWebSocketController( + options: IDashboardWebSocketControllerOptions +): IDashboardWebSocketController { + let ws: WebSocket | undefined; + let reconnectTimer: ReturnType | undefined; + let manualDisconnect: boolean = false; + + const isConnected = (): boolean => !!ws && ws.readyState === WebSocket.OPEN; + const getSocket = (): WebSocket | undefined => ws; + + const scheduleReconnect = (): void => { + if (reconnectTimer) return; + reconnectTimer = setTimeout(() => { + reconnectTimer = undefined; + if (!manualDisconnect) connect(); + }, 4000); + }; + + function connect(): void { + if (ws && ws.readyState === WebSocket.OPEN) return; + + if (reconnectTimer) { + clearTimeout(reconnectTimer); + reconnectTimer = undefined; + } + + manualDisconnect = false; + const url: string = options.getUrl(); + if (!url) return; + + options.onConnecting(url); + options.onLog('Attempting connection to ' + url); + + try { + ws = new WebSocket(url); + } catch (error: unknown) { + options.onLog('WebSocket creation failed: ' + String(error)); + scheduleReconnect(); + return; + } + + options.onConnectedStateChange(false); + + ws.addEventListener('open', () => { + options.onLog('Connected'); + options.onConnectedStateChange(true); + options.onOpen(); + }); + + ws.addEventListener('close', () => { + options.onLog('Disconnected'); + options.onConnectedStateChange(false); + ws = undefined; + options.onClose(); + if (!manualDisconnect) scheduleReconnect(); + }); + + ws.addEventListener('error', (event: Event) => { + options.onError(event); + }); + + ws.addEventListener('message', (ev: MessageEvent) => { + try { + options.onParsedMessage(JSON.parse(ev.data)); + } catch (error: unknown) { + options.onParseError(error); + } + }); + } + + const disconnect = (): void => { + manualDisconnect = true; + if (reconnectTimer) { + clearTimeout(reconnectTimer); + reconnectTimer = undefined; + } + if (ws) { + try { + ws.close(); + } catch { + // ignore close failures + } + ws = undefined; + } + }; + + const sendCommand = (command: unknown): void => { + if (ws && ws.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify(command)); + } + }; + + return { + connect, + disconnect, + sendCommand, + isConnected, + getSocket + }; +} diff --git a/apps/rush-serve-dashboard/src/modules/graphFiltering.ts b/apps/rush-serve-dashboard/src/modules/graphFiltering.ts new file mode 100644 index 00000000000..66ba48fa8b8 --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/graphFiltering.ts @@ -0,0 +1,199 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +interface IOperationLogFileURLs { + text?: string; + error?: string; + jsonl?: string; +} + +interface IOperationData { + name: string; + dependencies?: string[]; + noop?: boolean; + runInThisIteration?: boolean; + status?: string; + isActive?: boolean; + logFileURLs?: IOperationLogFileURLs; +} + +interface IExecutionState { + name: string; + status?: string; + runInThisIteration?: boolean; + isActive?: boolean; + logFileURLs?: IOperationLogFileURLs; +} + +export interface IComputeFilterSetsOptions { + operations: Map; + executionStates: Map; + currentFilter: 'all' | 'failed-warn'; + searchQuery: string; + computeDisplayStatus: (op: IOperationData) => string; +} + +export interface IComputeFilterSetsResult { + visibleOperations: IOperationData[]; + filteredOutNames: Set; + searchFilteredOutNames: Set; +} + +export function computeFilterSetsCore(options: IComputeFilterSetsOptions): IComputeFilterSetsResult { + const { operations, executionStates, currentFilter, searchQuery, computeDisplayStatus } = options; + + const filteredOutNames: Set = new Set(); + const searchFilteredOutNames: Set = new Set(); + const visibleOperations: IOperationData[] = []; + const query: string = searchQuery.trim().toLowerCase(); + + for (const op of operations.values()) { + const state: IExecutionState | undefined = executionStates.get(op.name) || undefined; + + // Merge dynamic fields so rendering logic can treat operation rows uniformly. + op.runInThisIteration = state?.runInThisIteration; + op.status = state?.status || op.status; + op.isActive = state?.isActive; + op.logFileURLs = state?.logFileURLs; + + const effectiveStatus: string = computeDisplayStatus(op); + if (currentFilter === 'failed-warn') { + const includeInFailedWarn: boolean = + effectiveStatus === 'Failure' || effectiveStatus === 'SuccessWithWarning'; + if (!includeInFailedWarn) { + filteredOutNames.add(op.name); + continue; + } + } + + if (query && !op.name.toLowerCase().includes(query)) { + searchFilteredOutNames.add(op.name); + continue; + } + + visibleOperations.push(op); + } + + return { + visibleOperations, + filteredOutNames, + searchFilteredOutNames + }; +} + +export function pruneGraphOperations(baseOperations: IOperationData[]): IOperationData[] { + if (!baseOperations.length) return baseOperations; + + const byName: Map = new Map(); + baseOperations.forEach((op) => byName.set(op.name, op)); + + const dependents: Map> = new Map(); + baseOperations.forEach((op) => { + (op.dependencies || []).forEach((dependencyName: string) => { + if (!byName.has(dependencyName)) return; + let setForDependency: Set | undefined = dependents.get(dependencyName); + if (!setForDependency) { + setForDependency = new Set(); + dependents.set(dependencyName, setForDependency); + } + setForDependency.add(op.name); + }); + }); + + const active: Set = new Set(baseOperations.map((op) => op.name)); + const noopSet: Set = new Set(baseOperations.filter((op) => op.noop).map((op) => op.name)); + + const incomingCount: Map = new Map(); + const outgoingCount: Map = new Map(); + baseOperations.forEach((op) => { + incomingCount.set(op.name, (dependents.get(op.name) || new Set()).size); + const outgoing: number = (op.dependencies || []).filter((dependencyName: string) => + byName.has(dependencyName) + ).length; + outgoingCount.set(op.name, outgoing); + }); + + const queue: string[] = []; + active.forEach((nodeName) => { + if (!noopSet.has(nodeName)) return; + const incoming: number = incomingCount.get(nodeName) || 0; + const outgoing: number = outgoingCount.get(nodeName) || 0; + if (incoming === 0 || incoming === 1 || outgoing === 1) queue.push(nodeName); + }); + + while (queue.length) { + const nodeName: string | undefined = queue.pop(); + if (!nodeName || !active.has(nodeName)) continue; + + active.delete(nodeName); + const op: IOperationData | undefined = byName.get(nodeName); + if (!op) continue; + + for (const dependencyName of op.dependencies || []) { + if (!active.has(dependencyName)) continue; + const previous: number = incomingCount.get(dependencyName) || 0; + incomingCount.set(dependencyName, Math.max(0, previous - 1)); + const incoming: number = incomingCount.get(dependencyName) || 0; + const outgoing: number = outgoingCount.get(dependencyName) || 0; + if (noopSet.has(dependencyName) && (incoming === 0 || incoming === 1 || outgoing === 1)) { + queue.push(dependencyName); + } + } + + const dependentNodes: Set = dependents.get(nodeName) || new Set(); + for (const dependentName of dependentNodes) { + if (!active.has(dependentName)) continue; + const previousOutgoing: number = outgoingCount.get(dependentName) || 0; + outgoingCount.set(dependentName, Math.max(0, previousOutgoing - 1)); + const incoming: number = incomingCount.get(dependentName) || 0; + const outgoing: number = outgoingCount.get(dependentName) || 0; + if (noopSet.has(dependentName) && (incoming === 0 || incoming === 1 || outgoing === 1)) { + queue.push(dependentName); + } + } + } + + const resolvedMemo: Map> = new Map(); + function resolveDeps(nodeName: string, seen: Set): Set { + const cached: Set | undefined = resolvedMemo.get(nodeName); + if (cached) return cached; + + if (seen.has(nodeName)) return new Set(); + seen.add(nodeName); + + const op: IOperationData | undefined = byName.get(nodeName); + const resolved: Set = new Set(); + if (!op) return resolved; + + for (const dependencyName of op.dependencies || []) { + if (!byName.has(dependencyName)) continue; + if (active.has(dependencyName)) { + resolved.add(dependencyName); + } else { + const subResolved: Set = resolveDeps(dependencyName, seen); + for (const item of subResolved) resolved.add(item); + } + } + + seen.delete(nodeName); + resolvedMemo.set(nodeName, resolved); + return resolved; + } + + return baseOperations + .filter((op) => active.has(op.name)) + .map((op) => { + const dependencies: Set = new Set(); + for (const dependencyName of op.dependencies || []) { + if (!byName.has(dependencyName)) continue; + if (active.has(dependencyName)) { + dependencies.add(dependencyName); + } else { + const subResolved: Set = resolveDeps(dependencyName, new Set()); + for (const item of subResolved) dependencies.add(item); + } + } + + return Object.assign({}, op, { dependencies: Array.from(dependencies) }); + }); +} diff --git a/apps/rush-serve-dashboard/src/modules/graphSelection.ts b/apps/rush-serve-dashboard/src/modules/graphSelection.ts new file mode 100644 index 00000000000..6531c42e5e0 --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/graphSelection.ts @@ -0,0 +1,246 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import graphStyles from '../styles/graphView.module.css'; + +interface IOperationInfo { + name: string; + dependencies?: string[]; +} + +interface IPoint { + x: number; + y: number; +} + +export interface IGraphSelectionControllerOptions { + graphEl: HTMLElement; + getCurrentView: () => string; + getSelection: () => Set; + setSelection: (nextSelection: Set) => void; + getOperations: () => Map; + getGraphNodePositions: () => Map; + graphNodeWidth: number; + graphNodeHeight: number; + onSelectionChanged: () => void; + onLiveSelectionChanged: () => void; +} + +export interface IGraphSelectionController { + singleSelect: (name: string) => void; + toggleSelect: (name: string) => void; + expandSelectionDependencies: () => void; + expandSelectionConsumers: () => void; + wireGraphMarqueeSelection: () => void; +} + +export function createGraphSelectionController( + options: IGraphSelectionControllerOptions +): IGraphSelectionController { + let graphMarqueeEl: HTMLDivElement | null = null; + let dragSelecting: boolean = false; + let dragStart: IPoint | null = null; + let dragLast: IPoint | null = null; + let preDragSelection: Set | null = null; + let dragModifierMode: 'replace' | 'add' | 'subtract' = 'replace'; + + const _selectionChanged = (): void => { + options.onSelectionChanged(); + }; + + const _graphPointFromEvent = (e: MouseEvent): IPoint => { + const rect: DOMRect = options.graphEl.getBoundingClientRect(); + return { + x: e.clientX - rect.left + options.graphEl.scrollLeft, + y: e.clientY - rect.top + options.graphEl.scrollTop + }; + }; + + const _updateDragModifierMode = (e: MouseEvent | KeyboardEvent): void => { + if (e.altKey) dragModifierMode = 'subtract'; + else if (e.metaKey || e.ctrlKey || e.shiftKey) dragModifierMode = 'add'; + else dragModifierMode = 'replace'; + }; + + const _updateMarquee = (): void => { + if (!dragSelecting || !graphMarqueeEl || !dragStart || !dragLast) return; + + const x1: number = Math.min(dragStart.x, dragLast.x); + const y1: number = Math.min(dragStart.y, dragLast.y); + const x2: number = Math.max(dragStart.x, dragLast.x); + const y2: number = Math.max(dragStart.y, dragLast.y); + + graphMarqueeEl.style.left = x1 + 'px'; + graphMarqueeEl.style.top = y1 + 'px'; + graphMarqueeEl.style.width = x2 - x1 + 'px'; + graphMarqueeEl.style.height = y2 - y1 + 'px'; + + const newlySelected: Set = new Set(); + for (const [name, pos] of options.getGraphNodePositions().entries()) { + const nx1: number = pos.x; + const ny1: number = pos.y; + const nx2: number = pos.x + options.graphNodeWidth; + const ny2: number = pos.y + options.graphNodeHeight; + if (nx2 < x1 || nx1 > x2 || ny2 < y1 || ny1 > y2) continue; + newlySelected.add(name); + } + + let nextSelection: Set; + if (dragModifierMode === 'add') { + nextSelection = new Set(preDragSelection || []); + newlySelected.forEach((name) => nextSelection.add(name)); + } else if (dragModifierMode === 'subtract') { + nextSelection = new Set(preDragSelection || []); + newlySelected.forEach((name) => nextSelection.delete(name)); + } else { + nextSelection = newlySelected; + } + + options.setSelection(nextSelection); + options.onLiveSelectionChanged(); + }; + + const _beginDragSelection = (e: MouseEvent): void => { + if (options.getCurrentView() !== 'graph' || e.button !== 0) return; + const target: Element | null = e.target as Element | null; + if (target && target.closest && target.closest(`.${graphStyles.opNode}`)) return; + + dragSelecting = true; + dragStart = _graphPointFromEvent(e); + dragLast = dragStart; + preDragSelection = new Set(options.getSelection()); + graphMarqueeEl = document.createElement('div'); + graphMarqueeEl.className = graphStyles.graphMarquee; + options.graphEl.appendChild(graphMarqueeEl); + _updateMarquee(); + e.preventDefault(); + }; + + const _wireGraphMarqueeSelection = (): void => { + options.graphEl.addEventListener('mousedown', (e: MouseEvent) => { + _updateDragModifierMode(e); + _beginDragSelection(e); + }); + + options.graphEl.addEventListener('mousemove', (e: MouseEvent) => { + if (!dragSelecting) return; + dragLast = _graphPointFromEvent(e); + _updateDragModifierMode(e); + _updateMarquee(); + e.preventDefault(); + }); + + window.addEventListener('mouseup', () => { + if (!dragSelecting) return; + dragSelecting = false; + if (graphMarqueeEl) { + graphMarqueeEl.remove(); + graphMarqueeEl = null; + } + dragStart = null; + dragLast = null; + preDragSelection = null; + }); + + window.addEventListener('keydown', (e: KeyboardEvent) => { + if (dragSelecting) { + _updateDragModifierMode(e); + _updateMarquee(); + } + }); + + window.addEventListener('keyup', (e: KeyboardEvent) => { + if (dragSelecting) { + _updateDragModifierMode(e); + _updateMarquee(); + } + }); + }; + + const _singleSelect = (name: string): void => { + options.setSelection(new Set([name])); + _selectionChanged(); + }; + + const _toggleSelect = (name: string): void => { + const nextSelection: Set = new Set(options.getSelection()); + if (nextSelection.has(name)) nextSelection.delete(name); + else nextSelection.add(name); + options.setSelection(nextSelection); + _selectionChanged(); + }; + + const _expandSelectionDependencies = (): void => { + const currentSelection: Set = options.getSelection(); + if (!currentSelection.size) return; + + const queue: string[] = [...currentSelection]; + const seen: Set = new Set(currentSelection); + const operations: Map = options.getOperations(); + + while (queue.length) { + const name: string | undefined = queue.shift(); + if (!name) continue; + const op: IOperationInfo | undefined = operations.get(name); + if (!op) continue; + for (const dep of op.dependencies || []) { + if (!seen.has(dep) && operations.has(dep)) { + seen.add(dep); + queue.push(dep); + } + } + } + + if (seen.size !== currentSelection.size) { + options.setSelection(seen); + _selectionChanged(); + } + }; + + const _expandSelectionConsumers = (): void => { + const currentSelection: Set = options.getSelection(); + if (!currentSelection.size) return; + + const operations: Map = options.getOperations(); + const dependents: Map> = new Map>(); + for (const op of operations.values()) { + for (const dep of op.dependencies || []) { + if (!operations.has(dep)) continue; + let setForDep: Set | undefined = dependents.get(dep); + if (!setForDep) { + setForDep = new Set(); + dependents.set(dep, setForDep); + } + setForDep.add(op.name); + } + } + + const queue: string[] = [...currentSelection]; + const seen: Set = new Set(currentSelection); + while (queue.length) { + const name: string | undefined = queue.shift(); + if (!name) continue; + const consumers: Set | undefined = dependents.get(name); + if (!consumers) continue; + for (const consumer of consumers) { + if (!seen.has(consumer)) { + seen.add(consumer); + queue.push(consumer); + } + } + } + + if (seen.size !== currentSelection.size) { + options.setSelection(seen); + _selectionChanged(); + } + }; + + return { + singleSelect: _singleSelect, + toggleSelect: _toggleSelect, + expandSelectionDependencies: _expandSelectionDependencies, + expandSelectionConsumers: _expandSelectionConsumers, + wireGraphMarqueeSelection: _wireGraphMarqueeSelection + }; +} diff --git a/apps/rush-serve-dashboard/src/modules/graphView.ts b/apps/rush-serve-dashboard/src/modules/graphView.ts new file mode 100644 index 00000000000..3a1a2f89061 --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/graphView.ts @@ -0,0 +1,579 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import graphStyles from '../styles/graphView.module.css'; +import { pruneGraphOperations } from './graphFiltering'; + +interface IOperationLogFileURLs { + text?: string; + error?: string; + jsonl?: string; +} + +interface IOperationInfo { + name: string; + dependencies?: string[]; + phaseName?: string; + packageName?: string; + noop?: boolean; + enabled?: string; + status?: string; + runInThisIteration?: boolean; + isActive?: boolean; + logFileURLs?: IOperationLogFileURLs; +} + +interface IOperationExecutionState { + name: string; + status?: string; + runInThisIteration?: boolean; + isActive?: boolean; + logFileURLs?: IOperationLogFileURLs; +} + +export interface IPoint { + x: number; + y: number; +} + +export interface IGraphEdgeRecord { + path: SVGPathElement; + from: string; + to: string; +} + +export interface IGraphState { + nodePositions: Map; + nodeStatus: Map; + nodeElements: Map; + edgeElements: IGraphEdgeRecord[]; +} + +export const GRAPH_NODE_WIDTH: number = 28; +export const GRAPH_NODE_HEIGHT: number = 28; +export const GRAPH_COL_WIDTH: number = 46; +export const GRAPH_NODE_GAP: number = 10; +export const GRAPH_LEVEL_GAP: number = 70; +export const GRAPH_BASE_X: number = 16; +export const GRAPH_BASE_Y: number = 16; + +export const graphState: IGraphState = { + nodePositions: new Map(), + nodeStatus: new Map(), + nodeElements: new Map(), + edgeElements: [] +}; + +export interface IGraphViewControllerOptions { + graphEl: HTMLElement; + edgesSvg: SVGSVGElement; + getOperations: () => Map; + getExecutionStates: () => Map; + getQueuedStates: () => Map; + getSelection: () => Set; + getFilteredOutNames: () => Set; + getSearchFilteredOutNames: () => Set; + getLastExecutionResults: () => Map; + getComputeDisplayStatus: (op: IOperationInfo) => string; + getStatusEmoji: (status: string) => string; + getOverallStatusText: (status: string | undefined) => string; + renderPhaseLegend: () => void; + singleSelect: (name: string) => void; + toggleSelect: (name: string) => void; +} + +export interface IGraphViewController { + markGraphDirty(): void; + ensureGraph(): void; + updateGraph(): void; +} + +export function createGraphViewController(options: IGraphViewControllerOptions): IGraphViewController { + let graphNeedsFullRender: boolean = true; + function getStatusColors(): Record { + const cs: CSSStyleDeclaration = getComputedStyle(document.documentElement); + return { + Ready: cs.getPropertyValue('--status-ready').trim(), + Waiting: cs.getPropertyValue('--status-waiting').trim(), + Queued: cs.getPropertyValue('--status-queued').trim(), + Executing: cs.getPropertyValue('--status-executing')?.trim() || cs.getPropertyValue('--warn').trim(), + Success: cs.getPropertyValue('--status-success')?.trim() || cs.getPropertyValue('--success').trim(), + SuccessWithWarning: cs.getPropertyValue('--status-success-warning').trim(), + Skipped: cs.getPropertyValue('--status-skipped').trim(), + FromCache: cs.getPropertyValue('--status-from-cache').trim(), + Failure: cs.getPropertyValue('--status-failure')?.trim() || cs.getPropertyValue('--danger').trim(), + Blocked: cs.getPropertyValue('--status-blocked').trim(), + NoOp: cs.getPropertyValue('--status-noop').trim(), + Aborted: cs.getPropertyValue('--status-aborted').trim() + }; + } + + let statusColors: Record = getStatusColors(); + + const mo: MutationObserver = new MutationObserver(() => { + statusColors = getStatusColors(); + }); + mo.observe(document.documentElement, { attributes: true, attributeFilter: ['style'] }); + + const updateStatusColors = (): void => { + statusColors = getStatusColors(); + }; + + function computeLevels(filteredOps: IOperationInfo[]): Map { + const indegree: Map = new Map(); + const deps: Map = new Map(); + filteredOps.forEach((op: IOperationInfo) => { + deps.set(op.name, op.dependencies || []); + indegree.set(op.name, (op.dependencies || []).length); + }); + + const queue: string[] = []; + indegree.forEach((v: number, k: string) => { + if (v === 0) queue.push(k); + }); + + const level: Map = new Map(); + queue.forEach((k: string) => level.set(k, 0)); + + while (queue.length) { + const cur: string | undefined = queue.shift(); + if (!cur) continue; + const curLevel: number = level.get(cur) || 0; + filteredOps.forEach((op: IOperationInfo) => { + if ((op.dependencies || []).includes(cur)) { + indegree.set(op.name, (indegree.get(op.name) || 0) - 1); + if (!level.has(op.name) || (level.get(op.name) || 0) < curLevel + 1) { + level.set(op.name, curLevel + 1); + } + if ((indegree.get(op.name) || 0) === 0) queue.push(op.name); + } + }); + } + + return level; + } + + function computeGraphOperations(): IOperationInfo[] { + const filteredOutNames: Set = options.getFilteredOutNames(); + const searchFilteredOutNames: Set = options.getSearchFilteredOutNames(); + const visibleOperations: IOperationInfo[] = []; + + for (const op of options.getOperations().values()) { + if (filteredOutNames.has(op.name)) continue; + if (searchFilteredOutNames.has(op.name)) continue; + visibleOperations.push(op); + } + + return pruneGraphOperations(visibleOperations); + } + + function dimColor(hex: string, amount: number = 0.55): string { + if (!hex || !/^#?[0-9a-fA-F]{6}$/.test(hex)) return hex || '#4b5563'; + if (hex[0] === '#') hex = hex.slice(1); + const r: number = parseInt(hex.slice(0, 2), 16); + const g: number = parseInt(hex.slice(2, 4), 16); + const b: number = parseInt(hex.slice(4, 6), 16); + const br: number = 30; + const bg: number = 41; + const bb: number = 59; + const nr: number = Math.round(r * (1 - amount) + br * amount); + const ng: number = Math.round(g * (1 - amount) + bg * amount); + const nb: number = Math.round(b * (1 - amount) + bb * amount); + return ( + '#' + + nr.toString(16).padStart(2, '0') + + ng.toString(16).padStart(2, '0') + + nb.toString(16).padStart(2, '0') + ); + } + + function buildGraph(): void { + graphState.nodePositions.clear(); + graphState.nodeStatus.clear(); + graphState.nodeElements.forEach((el) => el.remove()); + graphState.nodeElements.clear(); + graphState.edgeElements.forEach((e: IGraphEdgeRecord) => e.path.remove()); + graphState.edgeElements.length = 0; + + options.edgesSvg.innerHTML = + '' + + Object.entries(statusColors) + .map( + ([status, color]: [string, string]) => + `` + ) + .join('') + + ''; + + const filteredOpsArr: IOperationInfo[] = computeGraphOperations(); + const level: Map = computeLevels(filteredOpsArr); + const groups: Record = {}; + level.forEach((value: number, name: string) => (groups[value] ||= []).push(name)); + const sortedLevels: number[] = Object.keys(groups) + .map(Number) + .sort((a, b) => a - b); + const maxLevel: number = sortedLevels.length ? Math.max(...sortedLevels) : 0; + + const dependentsMap: Map> = new Map>(); + filteredOpsArr.forEach((op: IOperationInfo) => { + (op.dependencies || []).forEach((dependencyName: string) => { + if (!options.getOperations().has(dependencyName)) return; + let setForDependency: Set | undefined = dependentsMap.get(dependencyName); + if (!setForDependency) { + setForDependency = new Set(); + dependentsMap.set(dependencyName, setForDependency); + } + setForDependency.add(op.name); + }); + }); + + const byNameFiltered: Map = new Map( + filteredOpsArr.map((op) => [op.name, op]) + ); + const memoCpl: Map = new Map(); + function criticalPathLen(name: string): number { + const cached: number | undefined = memoCpl.get(name); + if (cached !== undefined) return cached; + const op: IOperationInfo | undefined = byNameFiltered.get(name); + if (!op) { + memoCpl.set(name, 0); + return 0; + } + const deps: string[] = Array.from(dependentsMap.get(name) || new Set()); + if (!deps.length) { + memoCpl.set(name, 0); + return 0; + } + let best: number = 0; + for (const dependencyName of deps) { + best = Math.max(best, 1 + criticalPathLen(dependencyName)); + } + memoCpl.set(name, best); + return best; + } + + sortedLevels.forEach((levelValue: number) => { + const nodes: string[] = groups[levelValue] || []; + nodes.sort((a: string, b: string) => { + const cplA: number = criticalPathLen(a); + const cplB: number = criticalPathLen(b); + if (cplA !== cplB) return cplB - cplA; + const consA: number = (dependentsMap.get(a) || new Set()).size; + const consB: number = (dependentsMap.get(b) || new Set()).size; + if (consA !== consB) return consB - consA; + return a.localeCompare(b); + }); + + const levelIndexFromTop: number = maxLevel - levelValue; + nodes.forEach((name: string, index: number) => { + const op: IOperationInfo | undefined = options.getOperations().get(name); + if (!op) return; + const x: number = GRAPH_BASE_X + index * (GRAPH_COL_WIDTH + GRAPH_NODE_GAP); + const y: number = GRAPH_BASE_Y + levelIndexFromTop * GRAPH_LEVEL_GAP; + const button: HTMLButtonElement = document.createElement('button'); + button.type = 'button'; + button.setAttribute('aria-label', name); + button.className = graphStyles.opNode; + button.dataset.name = name; + button.style.transform = `translate(${x}px, ${y}px)`; + const emojiSpan: HTMLSpanElement = document.createElement('span'); + emojiSpan.className = graphStyles.emoji; + emojiSpan.textContent = options.getStatusEmoji(options.getComputeDisplayStatus(op)); + button.appendChild(emojiSpan); + const enabledSup: HTMLSpanElement = document.createElement('span'); + enabledSup.className = graphStyles.enabledIndicator; + enabledSup.textContent = ''; + button.appendChild(enabledSup); + button.addEventListener('click', (e) => { + e.stopPropagation(); + if (e.metaKey || e.ctrlKey) options.toggleSelect(name); + else options.singleSelect(name); + }); + options.graphEl.appendChild(button); + graphState.nodePositions.set(name, { x, y }); + graphState.nodeElements.set(name, button); + }); + }); + + const byName: Map = new Map( + filteredOpsArr.map((op) => [op.name, op]) + ); + const memoReach: Map> = new Map>(); + function getReachable(name: string): Set { + const cached: Set | undefined = memoReach.get(name); + if (cached) return cached; + const op: IOperationInfo | undefined = byName.get(name); + const visited: Set = new Set(); + if (op) { + const stack: string[] = [...(op.dependencies || [])]; + while (stack.length) { + const dependencyName: string | undefined = stack.pop(); + if (!dependencyName) continue; + if (visited.has(dependencyName)) continue; + visited.add(dependencyName); + const dependencyOp: IOperationInfo | undefined = byName.get(dependencyName); + if (dependencyOp) stack.push(...(dependencyOp.dependencies || [])); + } + } + memoReach.set(name, visited); + return visited; + } + + const edgeRecords: IGraphEdgeRecord[] = []; + for (const op of filteredOpsArr) { + const deps: string[] = op.dependencies || []; + for (const depName of deps) { + if (!byName.has(depName)) continue; + let redundant: boolean = false; + for (const intermediate of deps) { + if (intermediate === depName) continue; + if (!byName.has(intermediate)) continue; + if (getReachable(intermediate).has(depName)) { + redundant = true; + break; + } + } + if (redundant) continue; + const fromPos: IPoint | undefined = graphState.nodePositions.get(op.name); + const toPos: IPoint | undefined = graphState.nodePositions.get(depName); + if (!fromPos || !toPos) continue; + const path: SVGPathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + edgeRecords.push({ path, from: op.name, to: depName }); + options.edgesSvg.appendChild(path); + } + } + graphState.edgeElements = edgeRecords; + updateGraph(); + + if (graphState.nodePositions.size) { + const maxX: number = + Math.max(...Array.from(graphState.nodePositions.values()).map((p) => p.x)) + GRAPH_NODE_WIDTH + 40; + const maxY: number = + Math.max(...Array.from(graphState.nodePositions.values()).map((p) => p.y)) + + GRAPH_LEVEL_GAP + + GRAPH_NODE_HEIGHT; + options.edgesSvg.setAttribute('width', String(maxX)); + options.edgesSvg.setAttribute('height', String(maxY)); + } + } + + function updateGraph(): void { + updateStatusColors(); + for (const [name, div] of graphState.nodeElements.entries()) { + const op: IOperationInfo | undefined = options.getOperations().get(name); + if (!op) continue; + const displayStatus: string = options.getComputeDisplayStatus(op); + const prevStatus: string | undefined = graphState.nodeStatus.get(name); + const state: IOperationExecutionState | undefined = options.getExecutionStates().get(name); + const runInThisIteration: boolean | undefined = state + ? state.runInThisIteration + : op.runInThisIteration; + const notRunning: boolean = runInThisIteration === false || !!op.noop; + const queuedState: IOperationExecutionState | undefined = options.getQueuedStates().get(name); + const isQueuedNext: boolean = !!(queuedState && queuedState.runInThisIteration === true); + const isFilteredOut: boolean = options.getFilteredOutNames().has(name); + const isSearchFiltered: boolean = options.getSearchFilteredOutNames().has(name); + const emojiSpan: Element | undefined = div.getElementsByClassName(graphStyles.emoji)[0]; + if (emojiSpan && (prevStatus !== displayStatus || !emojiSpan.textContent)) { + emojiSpan.textContent = options.getStatusEmoji(displayStatus); + } + const enabledSpan: HTMLSpanElement | undefined = div.getElementsByClassName( + graphStyles.enabledIndicator + )[0] as HTMLSpanElement | undefined; + if (enabledSpan) { + let indicator: string = ''; + if (op.noop) { + indicator = '⚪'; + enabledSpan.title = 'No-op operation'; + } else { + switch (op.enabled) { + case 'never': + indicator = '🔴'; + enabledSpan.title = 'Disabled'; + break; + case 'ignore-dependency-changes': + indicator = '🟡'; + enabledSpan.title = 'Ignores dependency changes'; + break; + case 'affected': + default: + indicator = '🟢'; + enabledSpan.title = 'Enabled'; + break; + } + } + if (enabledSpan.textContent !== indicator) enabledSpan.textContent = indicator; + } + let baseColor: string = statusColors[displayStatus] || '#4b5563'; + if (isSearchFiltered) baseColor = dimColor(baseColor, 0.72); + else if (isFilteredOut) baseColor = dimColor(baseColor, 0.6); + else if (notRunning) baseColor = dimColor(baseColor, 0.35); + div.style.borderColor = baseColor; + const isSelected: boolean = options.getSelection().has(name); + div.setAttribute('aria-pressed', String(isSelected)); + if (isSelected) div.classList.add(graphStyles.selected); + else div.classList.remove(graphStyles.selected); + let activeSpan: HTMLSpanElement | undefined = div.getElementsByClassName( + graphStyles.activeIndicator + )[0] as HTMLSpanElement | undefined; + if (op.isActive) { + if (!activeSpan) { + activeSpan = document.createElement('span'); + activeSpan.className = graphStyles.activeIndicator; + activeSpan.textContent = '⚡'; + div.appendChild(activeSpan); + } + activeSpan.title = 'Active (in-memory state)'; + } else if (activeSpan) { + activeSpan.remove(); + } + let pendingSpan: HTMLSpanElement | undefined = div.getElementsByClassName( + graphStyles.pendingIndicator + )[0] as HTMLSpanElement | undefined; + if (isQueuedNext) { + if (!pendingSpan) { + pendingSpan = document.createElement('span'); + pendingSpan.className = graphStyles.pendingIndicator; + pendingSpan.textContent = '🕒'; + div.appendChild(pendingSpan); + } + pendingSpan.title = 'Pending changes (iteration queued)'; + } else if (pendingSpan) { + pendingSpan.remove(); + } + div.classList.remove( + graphStyles.notRunning, + graphStyles.filteredOut, + graphStyles.filteredOutSearch, + graphStyles.dashed, + graphStyles.dotted + ); + if (isSearchFiltered) { + div.classList.add(graphStyles.filteredOutSearch); + } else if (isFilteredOut) { + div.classList.add(graphStyles.filteredOut); + } else if (notRunning) { + div.classList.add(graphStyles.notRunning); + } + div.title = `${op.name}\nLast Result: ${(options.getLastExecutionResults().get(name) || {}).status || displayStatus}\n${options.getOverallStatusText(displayStatus)}${op.isActive ? '\nHas in-memory state' : ''}`; + graphState.nodeStatus.set(name, displayStatus); + } + + for (const rec of graphState.edgeElements) { + const fromPos: IPoint | undefined = graphState.nodePositions.get(rec.from); + const toPos: IPoint | undefined = graphState.nodePositions.get(rec.to); + if (!fromPos || !toPos) continue; + const startX: number = fromPos.x + GRAPH_NODE_WIDTH / 2; + const startY: number = fromPos.y + GRAPH_NODE_HEIGHT; + const endX: number = toPos.x + GRAPH_NODE_WIDTH / 2; + const endY: number = toPos.y; + const rowsApart: number = Math.max(1, Math.round((toPos.y - fromPos.y) / GRAPH_LEVEL_GAP)); + const colStep: number = GRAPH_COL_WIDTH + GRAPH_NODE_GAP; + function quadratic(sx: number, sy: number, ex: number, ey: number): string { + const mx: number = (sx + ex) / 2; + const my: number = (sy + ey) / 2; + const baseOffset: number = (ey - sy) / 4; + return `Q ${sx} ${sy + baseOffset} ${mx} ${my} ${ex} ${my + baseOffset} ${ex} ${ey}`; + } + let d: string = ''; + if (startX === endX && rowsApart === 1) { + d = `M ${startX} ${startY} L ${endX} ${endY}`; + } else if (rowsApart === 1) { + d = `M ${startX} ${startY} ` + quadratic(startX, startY, endX, endY); + } else { + const dir: number = Math.sign(endX - startX) || 1; + const halfColShift: number = colStep / 2; + const candidateX: number = startX + (endX - startX) * 0.5; + const deltaCols: number = Math.max(1, Math.round(Math.abs(endX - startX) / colStep)); + let intermediateX: number = candidateX; + let tooClose: boolean = false; + for (let k: number = 0; k <= deltaCols; k++) { + const center: number = startX + dir * k * colStep; + if (Math.abs(candidateX - center) < GRAPH_NODE_WIDTH / 2 + 2) { + tooClose = true; + break; + } + } + if (tooClose) intermediateX = candidateX + dir * halfColShift; + const firstTargetY: number = fromPos.y + GRAPH_LEVEL_GAP; + const bottomOfRowAboveDest: number = toPos.y - GRAPH_LEVEL_GAP + GRAPH_NODE_HEIGHT; + const midY1: number = firstTargetY; + const midY2: number = bottomOfRowAboveDest; + d = `M ${startX} ${startY} ` + quadratic(startX, startY, intermediateX, midY1); + d += ` L ${intermediateX} ${midY2}`; + d += ' ' + quadratic(intermediateX, midY2, endX, endY); + } + rec.path.setAttribute('d', d); + const depStatus: string = graphState.nodeStatus.get(rec.to) || 'Ready'; + rec.path.setAttribute('stroke', statusColors[depStatus] || '#4b5563'); + rec.path.setAttribute('class', graphStyles.edge); + rec.path.setAttribute('marker-end', `url(#arrowhead-${depStatus})`); + const fromOp: IOperationInfo | undefined = options.getOperations().get(rec.from); + if (options.getSelection().has(rec.to) && options.getSelection().has(rec.from)) + rec.path.classList.add(graphStyles.highlight); + else rec.path.classList.remove(graphStyles.highlight); + rec.path.classList.remove( + graphStyles.dashed, + graphStyles.dotted, + graphStyles.filteredOut, + graphStyles.notRunning + ); + rec.path.classList.remove(graphStyles.filteredOutSearch); + const fromState: IOperationExecutionState | undefined = fromOp + ? options.getExecutionStates().get(rec.from) + : undefined; + const fromRunInThisIteration: boolean | undefined = fromState + ? fromState.runInThisIteration + : fromOp?.runInThisIteration; + const fromNotRunning: boolean = !!(fromOp && (fromRunInThisIteration === false || fromOp.noop)); + const edgeStatusFiltered: boolean = + options.getFilteredOutNames().has(rec.from) || options.getFilteredOutNames().has(rec.to); + const edgeSearchFiltered: boolean = + options.getSearchFilteredOutNames().has(rec.from) || options.getSearchFilteredOutNames().has(rec.to); + if (edgeSearchFiltered || edgeStatusFiltered || fromNotRunning) { + let strokeColor: string = statusColors[depStatus] || '#4b5563'; + if (edgeSearchFiltered) { + strokeColor = dimColor(strokeColor, 0.78); + rec.path.style.opacity = '0.22'; + } else if (edgeStatusFiltered) { + strokeColor = dimColor(strokeColor, 0.65); + rec.path.style.opacity = '0.3'; + } else if (fromNotRunning) { + strokeColor = dimColor(strokeColor, 0.4); + rec.path.style.opacity = '0.42'; + } + rec.path.setAttribute('stroke', strokeColor); + rec.path.setAttribute('marker-end', `url(#arrowhead-${depStatus})`); + } else { + rec.path.style.opacity = ''; + const semImportant: boolean = depStatus === 'Executing' || depStatus === 'Failure'; + if (!semImportant && !rec.path.classList.contains(graphStyles.highlight)) { + rec.path.classList.add(graphStyles.dim); + } else { + rec.path.classList.remove(graphStyles.dim); + } + } + } + + options.renderPhaseLegend(); + } + + function markGraphDirty(): void { + graphNeedsFullRender = true; + } + + function ensureGraph(): void { + if (graphNeedsFullRender) { + buildGraph(); + graphNeedsFullRender = false; + } else { + updateGraph(); + } + } + + return { + markGraphDirty, + ensureGraph, + updateGraph + }; +} diff --git a/apps/rush-serve-dashboard/src/modules/leftBar.ts b/apps/rush-serve-dashboard/src/modules/leftBar.ts new file mode 100644 index 00000000000..1d7f1ae6098 --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/leftBar.ts @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export interface ILeftBarActionWiringOptions { + sendCommand: (cmd: { + command: string; + operationNames?: string[]; + targetState?: string; + mode?: string; + }) => void; + getSelection: () => Set; + clearSelectionAndRender: () => void; + expandSelectionDependencies: () => void; + expandSelectionConsumers: () => void; +} + +export function wireLeftBarActions(options: ILeftBarActionWiringOptions): void { + const { + sendCommand, + getSelection, + clearSelectionAndRender, + expandSelectionDependencies, + expandSelectionConsumers + } = options; + + const invalidateBtn: HTMLElement | null = document.getElementById('invalidate-btn'); + const closeRunnersBtn: HTMLElement | null = document.getElementById('close-runners-btn'); + const clearSelectionBtn: HTMLElement | null = document.getElementById('clear-selection-btn'); + const expandDepsBtn: HTMLElement | null = document.getElementById('expand-deps-btn'); + const expandConsumersBtn: HTMLElement | null = document.getElementById('expand-consumers-btn'); + const setEnabledDefaultBtn: HTMLElement | null = document.getElementById('set-enabled-default-btn'); + const setEnabledIgnoreDepsBtn: HTMLElement | null = document.getElementById('set-enabled-ignore-deps-btn'); + const setEnabledDisabledBtn: HTMLElement | null = document.getElementById('set-enabled-disabled-btn'); + const selectionModeBtn: HTMLElement | null = document.getElementById('selection-mode-btn'); + + if (invalidateBtn) { + invalidateBtn.addEventListener('click', () => { + sendCommand({ command: 'invalidate', operationNames: Array.from(getSelection()) }); + }); + } + + if (closeRunnersBtn) { + closeRunnersBtn.addEventListener('click', () => { + sendCommand({ command: 'close-runners', operationNames: Array.from(getSelection()) }); + }); + } + + if (expandDepsBtn) { + expandDepsBtn.addEventListener('click', expandSelectionDependencies); + } + + if (expandConsumersBtn) { + expandConsumersBtn.addEventListener('click', expandSelectionConsumers); + } + + let selectionEnableMode: 'safe' | 'unsafe' = 'safe'; + if (selectionModeBtn) { + selectionModeBtn.addEventListener('click', () => { + selectionEnableMode = selectionEnableMode === 'safe' ? 'unsafe' : 'safe'; + selectionModeBtn.dataset.mode = selectionEnableMode; + selectionModeBtn.textContent = `Mode: ${selectionEnableMode[0].toUpperCase()}${selectionEnableMode.slice(1)}`; + selectionModeBtn.title = + selectionEnableMode === 'safe' + ? 'Currently Safe mode (dependency aware). Click to switch to Unsafe.' + : 'Currently Unsafe mode (direct mutation). Click to switch to Safe.'; + }); + } + + const sendEnableState = (targetState: 'affected' | 'ignore-dependency-changes' | 'never'): void => { + const selection: Set = getSelection(); + if (!selection.size) return; + + sendCommand({ + command: 'set-enabled-states', + operationNames: Array.from(selection), + targetState, + mode: selectionEnableMode + }); + }; + + if (setEnabledDefaultBtn) { + setEnabledDefaultBtn.addEventListener('click', () => sendEnableState('affected')); + } + + if (setEnabledIgnoreDepsBtn) { + setEnabledIgnoreDepsBtn.addEventListener('click', () => sendEnableState('ignore-dependency-changes')); + } + + if (setEnabledDisabledBtn) { + setEnabledDisabledBtn.addEventListener('click', () => sendEnableState('never')); + } + + if (clearSelectionBtn) { + clearSelectionBtn.addEventListener('click', clearSelectionAndRender); + } +} diff --git a/apps/rush-serve-dashboard/src/modules/mainBar.ts b/apps/rush-serve-dashboard/src/modules/mainBar.ts new file mode 100644 index 00000000000..d4a8b216a09 --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/mainBar.ts @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export interface IMainBarActionWiringOptions { + connect: () => void; + disconnect: () => void; + isConnected: () => boolean; + sendCommand: (cmd: { command: string; value?: boolean; parallelism?: number }) => void; + getGraphSettings: () => + | { debugMode?: boolean; verbose?: boolean; pauseNextIteration?: boolean } + | undefined; + debugBtn: HTMLElement | undefined; + verboseBtn: HTMLElement | undefined; + parallelismInput: HTMLInputElement | undefined; + playPauseBtn: HTMLElement | undefined; + getOperationNames: () => string[]; + setSelection: (next: Set) => void; + clearSelection: () => void; + hasSelection: () => boolean; + render: () => void; +} + +function isTextEditingTarget(target: EventTarget | undefined): boolean { + return ( + target instanceof HTMLInputElement || + target instanceof HTMLTextAreaElement || + (target instanceof HTMLElement && target.isContentEditable) + ); +} + +export function wireMainBarActions(options: IMainBarActionWiringOptions): void { + const { + connect, + disconnect, + isConnected, + sendCommand, + getGraphSettings, + debugBtn, + verboseBtn, + parallelismInput, + playPauseBtn, + getOperationNames, + setSelection, + clearSelection, + hasSelection, + render + } = options; + + const connectBtn: HTMLElement | undefined = document.getElementById('connect-btn') ?? undefined; + if (connectBtn) { + connectBtn.addEventListener('click', () => { + if (isConnected()) { + disconnect(); + } else { + connect(); + } + }); + } + + const executeBtn: HTMLElement | undefined = document.getElementById('execute-btn') ?? undefined; + if (executeBtn) { + executeBtn.addEventListener('click', () => sendCommand({ command: 'execute' })); + } + + const abortBtn: HTMLElement | undefined = document.getElementById('abort-execution-btn') ?? undefined; + if (abortBtn) { + abortBtn.addEventListener('click', () => sendCommand({ command: 'abort-execution' })); + } + + if (debugBtn) { + debugBtn.addEventListener('click', () => { + const newVal: boolean = !getGraphSettings()?.debugMode; + debugBtn.title = newVal ? 'Turn off debug logging' : 'Turn on debug logging'; + sendCommand({ command: 'set-debug', value: newVal }); + }); + } + + if (verboseBtn) { + verboseBtn.addEventListener('click', () => { + const newVal: boolean = !getGraphSettings()?.verbose; + verboseBtn.title = newVal ? 'Turn off verbose logging' : 'Turn on verbose logging'; + sendCommand({ command: 'set-verbose', value: newVal }); + }); + } + + if (parallelismInput) { + parallelismInput.addEventListener('change', () => { + const value: number = Number(parallelismInput.value) || 1; + sendCommand({ command: 'set-parallelism', parallelism: value }); + }); + } + + if (playPauseBtn) { + playPauseBtn.addEventListener('click', () => { + const graphSettings: + | { debugMode?: boolean; verbose?: boolean; pauseNextIteration?: boolean } + | undefined = getGraphSettings(); + if (!graphSettings) return; + const next: boolean = !!graphSettings.pauseNextIteration; + sendCommand({ command: 'set-pause-next-iteration', value: !next }); + }); + } + + window.addEventListener('keydown', (e: KeyboardEvent) => { + if (e.key === 'a' && (e.metaKey || e.ctrlKey) && !isTextEditingTarget(e.target ?? undefined)) { + e.preventDefault(); + setSelection(new Set(getOperationNames())); + render(); + } + + if (e.key === 'Escape' && hasSelection()) { + clearSelection(); + render(); + } + }); +} diff --git a/apps/rush-serve-dashboard/src/modules/phaseLegend.ts b/apps/rush-serve-dashboard/src/modules/phaseLegend.ts new file mode 100644 index 00000000000..4ff6f654055 --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/phaseLegend.ts @@ -0,0 +1,448 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import graphStyles from '../styles/graphView.module.css'; +import { getStatusClassName } from './statusHelpers'; + +interface IPhaseLegendOperation { + name: string; + phaseName?: string; + logFileURLs?: { + text?: string; + error?: string; + jsonl?: string; + }; +} + +interface IPhaseProblemOperation { + op: IPhaseLegendOperation; + displayStatus: string; +} + +interface ILegendElement extends HTMLElement { + _initialized?: boolean; +} + +export interface IPhaseLegendControllerOptions { + phaseGroupsEl: Element | undefined; + legendEl: HTMLElement | undefined; + getOperations: () => Map; + getGraphVisibleNames: () => Set | undefined; + computeDisplayStatus: (op: IPhaseLegendOperation) => string; + statusEmoji: (status: string) => string; + overallStatusText: (status: string | undefined) => string; + getStatusColors: () => Record; +} + +export interface IPhaseLegendController { + renderPhasePane: () => void; + renderLegend: () => void; + renderAll: () => void; +} + +const phaseStatusPriority: string[] = [ + 'Failure', + 'SuccessWithWarning', + 'Blocked', + 'Aborted', + 'Executing', + 'Queued', + 'Ready', + 'Waiting', + 'Success', + 'Skipped', + 'FromCache', + 'NoOp' +]; + +const phaseStatusPriorityIndex: Map = new Map( + phaseStatusPriority.map((status, index) => [status, index]) +); + +const legendOrder: string[] = [...phaseStatusPriority]; + +export function createPhaseLegendController(options: IPhaseLegendControllerOptions): IPhaseLegendController { + const computePhaseSummaries = (): Array<{ + phase: string; + status: string; + problemOps: IPhaseProblemOperation[]; + }> => { + const byPhase: Map }> = new Map(); + const graphMembership: Set | undefined = options.getGraphVisibleNames(); + + for (const op of options.getOperations().values()) { + const phase: string = op.phaseName || '(none)'; + const displayStatus: string = options.computeDisplayStatus(op); + + // Keep currently executing operations visible in phase summaries even when not in graph membership. + if (graphMembership && displayStatus !== 'Executing' && !graphMembership.has(op.name)) { + continue; + } + + let rec: { ops: IPhaseProblemOperation[]; statusSet: Set } | undefined = byPhase.get(phase); + if (!rec) { + rec = { ops: [], statusSet: new Set() }; + byPhase.set(phase, rec); + } + + rec.ops.push({ op, displayStatus }); + rec.statusSet.add(displayStatus); + } + + const summaries: Array<{ + phase: string; + status: string; + problemOps: IPhaseProblemOperation[]; + }> = []; + + for (const [phase, rec] of byPhase.entries()) { + let chosen: string | undefined; + let bestIdx: number = Infinity; + + for (const status of rec.statusSet) { + const index: number | undefined = phaseStatusPriorityIndex.get(status); + if (index !== undefined && index < bestIdx) { + bestIdx = index; + chosen = status; + } + } + + if (!chosen) chosen = 'Ready'; + + const problemOps: IPhaseProblemOperation[] = rec.ops.filter( + ({ displayStatus }) => + displayStatus === 'Failure' || + displayStatus === 'SuccessWithWarning' || + displayStatus === 'Executing' + ); + + summaries.push({ phase, status: chosen, problemOps }); + } + + summaries.sort((a, b) => a.phase.localeCompare(b.phase)); + return summaries; + }; + + const renderPhasePane = (): void => { + if (!options.phaseGroupsEl) return; + + const summaries: Array<{ phase: string; status: string; problemOps: IPhaseProblemOperation[] }> = + computePhaseSummaries(); + (options.phaseGroupsEl as HTMLElement).innerHTML = ''; + + if (!summaries.length) { + const empty: HTMLDivElement = document.createElement('div'); + empty.className = graphStyles.phasePaneEmpty; + empty.textContent = 'No phases'; + options.phaseGroupsEl.appendChild(empty); + return; + } + + for (const summary of summaries) { + const group: HTMLDivElement = document.createElement('div'); + group.className = graphStyles.phaseGroup; + + const header: HTMLDivElement = document.createElement('div'); + header.className = graphStyles.phaseHeader; + + const emoji: HTMLSpanElement = document.createElement('span'); + emoji.className = graphStyles.phaseStatusEmoji; + emoji.textContent = options.statusEmoji(summary.status); + + const nameSpan: HTMLSpanElement = document.createElement('span'); + nameSpan.className = graphStyles.phaseName; + nameSpan.textContent = summary.phase.replace(/^_phase:/, ''); + + header.appendChild(emoji); + header.appendChild(nameSpan); + group.appendChild(header); + + if (summary.problemOps.length) { + const list: HTMLUListElement = document.createElement('ul'); + list.className = graphStyles.phaseProblems; + + const sortedProblems: IPhaseProblemOperation[] = [...summary.problemOps].sort((a, b) => { + const ai: number = phaseStatusPriorityIndex.get(a.displayStatus) ?? 999; + const bi: number = phaseStatusPriorityIndex.get(b.displayStatus) ?? 999; + if (ai !== bi) return ai - bi; + const an: string = a.op.name.toLowerCase(); + const bn: string = b.op.name.toLowerCase(); + if (an < bn) return -1; + if (an > bn) return 1; + return 0; + }); + + for (const { op, displayStatus } of sortedProblems) { + const item: HTMLLIElement = document.createElement('li'); + + const status: HTMLSpanElement = document.createElement('span'); + status.className = graphStyles.phaseProblemEmoji; + status.textContent = options.statusEmoji(displayStatus); + item.appendChild(status); + + const logUrl: string | undefined = + (op.logFileURLs && (op.logFileURLs.text || op.logFileURLs.error || op.logFileURLs.jsonl)) || + undefined; + if (logUrl) { + const link: HTMLAnchorElement = document.createElement('a'); + link.href = logUrl; + link.target = '_blank'; + link.rel = 'noopener noreferrer'; + link.textContent = op.name; + item.appendChild(link); + } else { + const span: HTMLSpanElement = document.createElement('span'); + span.textContent = op.name; + item.appendChild(span); + } + + list.appendChild(item); + } + + group.appendChild(list); + } + + options.phaseGroupsEl.appendChild(group); + } + }; + + const renderLegend = (): void => { + const legendEl: ILegendElement | undefined = options.legendEl as ILegendElement | undefined; + if (!legendEl) return; + + if (!legendEl._initialized) { + const header: HTMLHeadingElement = document.createElement('h4'); + header.textContent = 'Legend'; + + const toggleBtn: HTMLButtonElement = document.createElement('button'); + toggleBtn.type = 'button'; + toggleBtn.id = 'legend-collapse-btn'; + toggleBtn.setAttribute('aria-label', 'Collapse legend'); + toggleBtn.style.background = 'transparent'; + toggleBtn.style.border = 'none'; + toggleBtn.style.color = 'var(--text)'; + toggleBtn.style.cursor = 'pointer'; + toggleBtn.style.fontSize = '12px'; + toggleBtn.style.padding = '2px 4px'; + toggleBtn.style.marginLeft = 'auto'; + toggleBtn.style.display = 'flex'; + toggleBtn.style.alignItems = 'center'; + toggleBtn.style.lineHeight = '1'; + toggleBtn.textContent = '−'; + + header.appendChild(toggleBtn); + legendEl.appendChild(header); + legendEl._initialized = true; + + const collapsed: boolean = window.localStorage.getItem('rushServeLegendCollapsed') === '1'; + if (collapsed) legendEl.classList.add(graphStyles.collapsed); + toggleBtn.textContent = collapsed ? '+' : '−'; + toggleBtn.setAttribute('aria-expanded', collapsed ? 'false' : 'true'); + + toggleBtn.addEventListener('click', () => { + const isCollapsed: boolean = legendEl.classList.toggle(graphStyles.collapsed); + window.localStorage.setItem('rushServeLegendCollapsed', isCollapsed ? '1' : '0'); + toggleBtn.textContent = isCollapsed ? '+' : '−'; + toggleBtn.setAttribute('aria-label', isCollapsed ? 'Expand legend' : 'Collapse legend'); + toggleBtn.setAttribute('aria-expanded', isCollapsed ? 'false' : 'true'); + renderLegend(); + }); + } + + while (legendEl.children.length > 1) { + const lastChild: ChildNode | null = legendEl.lastChild; + if (!lastChild) break; + legendEl.removeChild(lastChild); + } + + if (legendEl.classList.contains(graphStyles.collapsed)) { + const stub: HTMLDivElement = document.createElement('div'); + stub.style.fontSize = '0.5rem'; + stub.style.opacity = '0.7'; + stub.textContent = 'Collapsed'; + legendEl.appendChild(stub); + return; + } + + const statusColors: Record = options.getStatusColors(); + + const columnsWrap: HTMLDivElement = document.createElement('div'); + columnsWrap.className = graphStyles.legendColumns; + + const colPrimary: HTMLDivElement = document.createElement('div'); + colPrimary.className = graphStyles.legendCol; + + const primaryHead: HTMLDivElement = document.createElement('div'); + primaryHead.className = graphStyles.legendHeading; + primaryHead.textContent = 'Statuses'; + colPrimary.appendChild(primaryHead); + + for (const status of legendOrder) { + const row: HTMLDivElement = document.createElement('div'); + row.className = graphStyles.legendRow; + + const sample: HTMLSpanElement = document.createElement('span'); + sample.className = graphStyles.legendEmoji; + sample.textContent = options.statusEmoji(status); + sample.style.borderColor = statusColors[status] || '#4b5563'; + + const labelWrap: HTMLDivElement = document.createElement('div'); + labelWrap.className = graphStyles.legendLabelWrap; + const titleSpan: HTMLSpanElement = document.createElement('span'); + titleSpan.textContent = options.overallStatusText(status); + labelWrap.appendChild(titleSpan); + + row.appendChild(sample); + row.appendChild(labelWrap); + colPrimary.appendChild(row); + } + + const unknownRow: HTMLDivElement = document.createElement('div'); + unknownRow.className = graphStyles.legendRow; + + const unknownSample: HTMLSpanElement = document.createElement('span'); + unknownSample.className = `${graphStyles.legendEmoji} ${getStatusClassName('Unknown')}`; + unknownSample.textContent = '❓'; + unknownSample.style.borderColor = '#4b5563'; + + const unknownLabelWrap: HTMLDivElement = document.createElement('div'); + unknownLabelWrap.className = graphStyles.legendLabelWrap; + + const unknownTitle: HTMLSpanElement = document.createElement('span'); + unknownTitle.textContent = 'UNKNOWN'; + const unknownDetail: HTMLElement = document.createElement('small'); + unknownDetail.textContent = 'Never executed'; + + unknownLabelWrap.appendChild(unknownTitle); + unknownLabelWrap.appendChild(unknownDetail); + + unknownRow.appendChild(unknownSample); + unknownRow.appendChild(unknownLabelWrap); + colPrimary.appendChild(unknownRow); + + const colSecondary: HTMLDivElement = document.createElement('div'); + colSecondary.className = graphStyles.legendCol; + + const secondaryHead: HTMLDivElement = document.createElement('div'); + secondaryHead.className = graphStyles.legendHeading; + secondaryHead.textContent = 'State Modifiers'; + colSecondary.appendChild(secondaryHead); + + const addModifier = (sampleFactory: () => HTMLElement, label: string, detail?: string): void => { + const row: HTMLDivElement = document.createElement('div'); + row.className = graphStyles.legendRow; + const sample: HTMLElement = sampleFactory(); + + const labelWrap: HTMLDivElement = document.createElement('div'); + labelWrap.className = graphStyles.legendLabelWrap; + const titleSpan: HTMLSpanElement = document.createElement('span'); + titleSpan.textContent = label; + labelWrap.appendChild(titleSpan); + + if (detail) { + const small: HTMLElement = document.createElement('small'); + small.textContent = detail; + labelWrap.appendChild(small); + } + + row.appendChild(sample); + row.appendChild(labelWrap); + colSecondary.appendChild(row); + }; + + const makeNodeBox = (borderStyle?: string, boxShadow?: string, borderColor?: string): HTMLElement => { + const sample: HTMLSpanElement = document.createElement('span'); + sample.className = graphStyles.legendEmoji; + if (borderStyle) sample.style.borderStyle = borderStyle; + if (borderColor) sample.style.borderColor = borderColor; + if (boxShadow) sample.style.boxShadow = boxShadow; + sample.textContent = ' '; + return sample; + }; + + const makeDashed = (): HTMLElement => makeNodeBox('dashed'); + const makeDotted = (): HTMLElement => makeNodeBox('dotted'); + + const makeActive = (): HTMLElement => { + const wrap: HTMLDivElement = document.createElement('div'); + wrap.className = graphStyles.legendEnabledSample; + + const base: HTMLSpanElement = document.createElement('span'); + base.style.opacity = '0.15'; + base.style.fontSize = '11px'; + base.textContent = '⬜'; + wrap.appendChild(base); + + const rocket: HTMLSpanElement = document.createElement('span'); + rocket.style.position = 'absolute'; + rocket.style.bottom = '0'; + rocket.style.left = '0'; + rocket.style.transform = 'translate(-50%, 50%)'; + rocket.style.fontSize = '12px'; + rocket.textContent = '⚡'; + wrap.appendChild(rocket); + + return wrap; + }; + + const makePending = (): HTMLElement => { + const wrap: HTMLDivElement = document.createElement('div'); + wrap.className = graphStyles.legendEnabledSample; + + const base: HTMLSpanElement = document.createElement('span'); + base.style.opacity = '0.15'; + base.style.fontSize = '11px'; + base.textContent = '⬜'; + wrap.appendChild(base); + + const clock: HTMLSpanElement = document.createElement('span'); + clock.style.position = 'absolute'; + clock.style.top = '0'; + clock.style.left = '0'; + clock.style.transform = 'translate(-50%, -50%)'; + clock.style.fontSize = '12px'; + clock.textContent = '🕒'; + wrap.appendChild(clock); + + return wrap; + }; + + const makeEnabledSample = (emoji: string): HTMLElement => { + const wrap: HTMLDivElement = document.createElement('div'); + wrap.className = graphStyles.legendEnabledSample; + const sub: HTMLSpanElement = document.createElement('span'); + sub.className = graphStyles.sub; + sub.textContent = emoji; + wrap.appendChild(sub); + return wrap; + }; + + addModifier(makePending, 'Pending changes', 'Iteration queued'); + addModifier(makeActive, 'Active', 'In-memory state'); + addModifier(makeDashed, 'Not in this iteration', 'Excluded this iteration'); + addModifier(makeDotted, 'Filtered out', 'Hidden by view/search'); + + const enabledHead: HTMLDivElement = document.createElement('div'); + enabledHead.className = graphStyles.legendSubheading; + enabledHead.textContent = 'Enabled States'; + colSecondary.appendChild(enabledHead); + + addModifier(() => makeEnabledSample('🟢'), 'Enabled', 'Runs normally'); + addModifier(() => makeEnabledSample('🟡'), 'Ignore dependency changes', 'Skips if no local changes'); + addModifier(() => makeEnabledSample('🔴'), 'Disabled', 'Never runs'); + addModifier(() => makeEnabledSample('⚪'), 'No-op', 'Operation does no work'); + + columnsWrap.appendChild(colPrimary); + columnsWrap.appendChild(colSecondary); + legendEl.appendChild(columnsWrap); + }; + + return { + renderPhasePane, + renderLegend, + renderAll: () => { + renderPhasePane(); + renderLegend(); + } + }; +} diff --git a/apps/rush-serve-dashboard/src/modules/selectionBar.ts b/apps/rush-serve-dashboard/src/modules/selectionBar.ts new file mode 100644 index 00000000000..3e6cd6f7cfb --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/selectionBar.ts @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export interface ISelectionBarControllerOptions { + getSelection: () => Set; + getCurrentView: () => string; + isConnected: () => boolean; +} + +export interface ISelectionBarController { + updateSelectionUI: () => void; +} + +const selectionButtonsIds: string[] = [ + 'invalidate-btn', + 'close-runners-btn', + 'set-enabled-default-btn', + 'set-enabled-ignore-deps-btn', + 'set-enabled-disabled-btn', + 'expand-deps-btn', + 'expand-consumers-btn' +]; + +export function createSelectionBarController( + options: ISelectionBarControllerOptions +): ISelectionBarController { + const updateSelectionUI = (): void => { + const bar: HTMLElement | null = document.getElementById('selection-bar'); + if (!bar) return; + + bar.style.display = 'flex'; + const headingSpan: HTMLElement | null = document.getElementById('view-heading-text'); + if (headingSpan) { + headingSpan.textContent = + options.getCurrentView() === 'graph' ? 'Dependency Graph' : 'Operations Table'; + } + + const hasSelection: boolean = options.getSelection().size > 0; + const connected: boolean = options.isConnected(); + selectionButtonsIds.forEach((id) => { + const el: HTMLButtonElement | HTMLInputElement | null = document.getElementById(id) as + | HTMLButtonElement + | HTMLInputElement + | null; + if (el) el.disabled = !(hasSelection && connected); + }); + + const clearBtn: HTMLButtonElement | null = document.getElementById( + 'clear-selection-btn' + ) as HTMLButtonElement | null; + if (clearBtn) { + clearBtn.disabled = !(hasSelection && connected); + clearBtn.title = 'Clear selection'; + clearBtn.setAttribute('aria-label', 'Clear selection'); + } + + const countSpan: HTMLElement | null = document.getElementById('selection-count'); + if (countSpan) { + const count: number = options.getSelection().size; + countSpan.textContent = count + (count === 1 ? ' selected' : ' selected'); + } + }; + + return { + updateSelectionUI + }; +} diff --git a/apps/rush-serve-dashboard/src/modules/statusHelpers.ts b/apps/rush-serve-dashboard/src/modules/statusHelpers.ts new file mode 100644 index 00000000000..59135be4d1b --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/statusHelpers.ts @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import globalStyles from '../styles/global.module.css'; + +interface IOperationExecutionStateLike { + name: string; + status?: string; + runInThisIteration?: boolean; +} + +interface IOperationInfoLike { + name: string; + status?: string; + runInThisIteration?: boolean; + isActive?: boolean; + noop?: boolean; + enabled?: string; +} + +const statusEmojiMap: Record = { + Ready: '⏸️', + Waiting: '🕘', + Queued: '📝', + Executing: '⚙️', + Success: '✅', + SuccessWithWarning: '⚠️', + Skipped: '💤', + FromCache: '🟩', + Failure: '❌', + Blocked: '🚫', + NoOp: '💤', + Aborted: '🛑', + Disconnected: '⏸️', + Unknown: '❓' +}; + +const statusClassNames: Record = { + Ready: globalStyles.statusReady, + Waiting: globalStyles.statusWaiting, + Queued: globalStyles.statusQueued, + Executing: globalStyles.statusExecuting, + Success: globalStyles.statusSuccess, + SuccessWithWarning: globalStyles.statusSuccessWithWarning, + Skipped: globalStyles.statusSkipped, + FromCache: globalStyles.statusFromCache, + Failure: globalStyles.statusFailure, + Blocked: globalStyles.statusBlocked, + NoOp: globalStyles.statusNoOp, + Aborted: globalStyles.statusAborted, + Canceled: globalStyles.statusCanceled, + Unspecified: globalStyles.statusUnspecified, + Unknown: globalStyles.statusUnknown, + Disconnected: globalStyles.statusDisconnected +}; + +export function getStatusClassName(status: string | undefined): string { + return (status && statusClassNames[status]) || globalStyles.statusUnknown; +} + +export function statusEmoji(status: string): string { + return statusEmojiMap[status] || '•'; +} + +export function computeDisplayStatus( + op: IOperationInfoLike, + executionStates: Map, + lastExecutionResults: Map +): string { + const state: IOperationExecutionStateLike | undefined = executionStates.get(op.name); + let displayStatus: string = state?.status || op.status || ''; + const runInThisIteration: boolean | undefined = state ? state.runInThisIteration : op.runInThisIteration; + + if (runInThisIteration === false) { + const prev: IOperationExecutionStateLike | undefined = lastExecutionResults.get(op.name); + displayStatus = prev?.status || 'Skipped'; + } + + if (!displayStatus) { + const last: IOperationExecutionStateLike | undefined = lastExecutionResults.get(op.name); + displayStatus = last?.status || (op.noop ? 'NoOp' : 'Ready'); + } + + return displayStatus; +} + +export function enabledGlyph(op: IOperationInfoLike): string { + if (op.noop) return '⚪'; + + switch (op.enabled) { + case 'never': + return '🔴'; + case 'ignore-dependency-changes': + return '🟡'; + default: + return '🟢'; + } +} + +export function buildRunPolicyText(op: IOperationInfoLike): string { + if (op.noop) return 'Operation does no work'; + + switch (op.enabled) { + case 'never': + return 'Never run'; + case 'ignore-dependency-changes': + return 'Ignores dependency changes'; + default: + return 'Run if affected'; + } +} + +export function buildTooltip(op: IOperationInfoLike, lastResultStatus: string): string { + const activeLine: string = op.isActive ? '\nHas in-memory state' : ''; + return `${op.name}\nLast Result: ${lastResultStatus}\n${buildRunPolicyText(op)}${activeLine}`; +} + +export function getStatusColors(): Record { + const cs: CSSStyleDeclaration = getComputedStyle(document.documentElement); + return { + Ready: cs.getPropertyValue('--status-ready').trim(), + Waiting: cs.getPropertyValue('--status-waiting').trim(), + Queued: cs.getPropertyValue('--status-queued').trim(), + Executing: cs.getPropertyValue('--status-executing').trim() || cs.getPropertyValue('--warn').trim(), + Success: cs.getPropertyValue('--status-success').trim() || cs.getPropertyValue('--success').trim(), + SuccessWithWarning: cs.getPropertyValue('--status-success-warning').trim(), + Skipped: cs.getPropertyValue('--status-skipped').trim(), + FromCache: cs.getPropertyValue('--status-from-cache').trim(), + Failure: cs.getPropertyValue('--status-failure').trim() || cs.getPropertyValue('--danger').trim(), + Blocked: cs.getPropertyValue('--status-blocked').trim(), + NoOp: cs.getPropertyValue('--status-noop').trim(), + Aborted: cs.getPropertyValue('--status-aborted').trim() + }; +} diff --git a/apps/rush-serve-dashboard/src/modules/tableView.ts b/apps/rush-serve-dashboard/src/modules/tableView.ts new file mode 100644 index 00000000000..0100c0b6d2c --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/tableView.ts @@ -0,0 +1,274 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import tableStyles from '../styles/tableView.module.css'; +import globalStyles from '../styles/global.module.css'; +import { getStatusClassName } from './statusHelpers'; + +interface ITableAnchorCoordinate { + row: number; + phase: number; +} + +interface ITablePackageRecord { + packageName: string; + byPhase: Map; +} + +interface ITableOperation { + name: string; + packageName?: string; + phaseName?: string; + isActive?: boolean; + enabled?: string; +} + +export interface ITableViewControllerOptions { + tableHead: HTMLElement | undefined; + tableBody: HTMLElement | undefined; + tableStats: HTMLElement | undefined; + getOperations: () => Map; + getFilteredOperations: () => ITableOperation[]; + getSelection: () => Set; + setSelection: (nextSelection: Set) => void; + onSelectionMutated: () => void; + computeDisplayStatus: (op: ITableOperation) => string; + enabledGlyph: (op: ITableOperation) => string; + buildRunPolicyText: (op: ITableOperation) => string; + buildTooltip: (op: ITableOperation, lastResultStatus: string) => string; + statusEmoji: (status: string) => string; + overallStatusText: (status: string | undefined) => string; +} + +export interface ITableViewController { + renderTable: () => void; +} + +export function createTableViewController(options: ITableViewControllerOptions): ITableViewController { + let tableOpOrder: string[] = []; + let lastTableAnchorName: string | undefined; + let lastTableAnchorCoord: ITableAnchorCoordinate | undefined; + let lastTablePhases: string[] = []; + let lastTablePackages: ITablePackageRecord[] = []; + + const buildPivotData = (): { phases: string[]; packages: ITablePackageRecord[] } => { + const allPhases: Set = new Set(); + for (const op of options.getOperations().values()) { + const phaseName: string = op.phaseName || '(none)'; + allPhases.add(phaseName); + } + const phases: string[] = Array.from(allPhases).sort(); + + const filteredOps: ITableOperation[] = options.getFilteredOperations(); + const packageMap: Map = new Map(); + for (const op of filteredOps) { + const packageName: string = op.packageName || '(unknown package)'; + const phaseName: string = op.phaseName || '(none)'; + let rec: ITablePackageRecord | undefined = packageMap.get(packageName); + if (!rec) { + rec = { packageName, byPhase: new Map() }; + packageMap.set(packageName, rec); + } + rec.byPhase.set(phaseName, op); + } + + const packages: ITablePackageRecord[] = Array.from(packageMap.values()).sort((a, b) => + a.packageName.localeCompare(b.packageName) + ); + + return { phases, packages }; + }; + + const commitSelection = (nextSelection: Set): void => { + options.setSelection(nextSelection); + options.onSelectionMutated(); + }; + + const handleMultiSelectGroup = (e: MouseEvent, names: string[]): void => { + const isMeta: boolean = e.metaKey || e.ctrlKey; + const isShift: boolean = e.shiftKey; + let nextSelection: Set = new Set(options.getSelection()); + + if (isShift && lastTableAnchorName && tableOpOrder.includes(lastTableAnchorName)) { + if (!isMeta) nextSelection = new Set(nextSelection); + names.forEach((name) => nextSelection.add(name)); + } else if (isMeta) { + let anyNew: boolean = false; + names.forEach((name) => { + if (!nextSelection.delete(name)) { + nextSelection.add(name); + anyNew = true; + } + }); + if (anyNew && names.length) lastTableAnchorName = names[0]; + } else { + nextSelection = new Set(names); + if (names.length) lastTableAnchorName = names[0]; + } + + commitSelection(nextSelection); + }; + + const handlePivotCellClick = ( + e: MouseEvent, + opName: string, + rowIndex: number, + phaseIndex: number + ): void => { + if (!opName) return; + + const isMeta: boolean = e.metaKey || e.ctrlKey; + const isShift: boolean = e.shiftKey; + + if (isShift && lastTableAnchorCoord) { + const { row: anchorRow, phase: anchorPhase } = lastTableAnchorCoord; + const rowStart: number = Math.min(anchorRow, rowIndex); + const rowEnd: number = Math.max(anchorRow, rowIndex); + const phaseStart: number = Math.min(anchorPhase, phaseIndex); + const phaseEnd: number = Math.max(anchorPhase, phaseIndex); + const rectNames: Set = new Set(); + + for (let row: number = rowStart; row <= rowEnd; row++) { + const packageRecord: ITablePackageRecord | undefined = lastTablePackages[row]; + if (!packageRecord) continue; + + for (let phase: number = phaseStart; phase <= phaseEnd; phase++) { + const phaseName: string | undefined = lastTablePhases[phase]; + if (!phaseName) continue; + const cellOp: ITableOperation | undefined = packageRecord.byPhase.get(phaseName); + if (cellOp) rectNames.add(cellOp.name); + } + } + + if (isMeta) { + const nextSelection: Set = new Set(options.getSelection()); + rectNames.forEach((name) => nextSelection.add(name)); + commitSelection(nextSelection); + } else { + commitSelection(rectNames); + } + + return; + } + + const nextSelection: Set = new Set(options.getSelection()); + if (isMeta) { + if (nextSelection.has(opName)) nextSelection.delete(opName); + else nextSelection.add(opName); + } else { + nextSelection.clear(); + nextSelection.add(opName); + } + + lastTableAnchorName = opName; + lastTableAnchorCoord = { row: rowIndex, phase: phaseIndex }; + commitSelection(nextSelection); + }; + + const renderTable = (): void => { + const tableHead: HTMLElement | undefined = options.tableHead; + const tableBody: HTMLElement | undefined = options.tableBody; + const tableStats: HTMLElement | undefined = options.tableStats; + if (!tableHead || !tableBody || !tableStats) return; + + const { phases, packages } = buildPivotData(); + tableOpOrder = []; + lastTablePhases = phases; + lastTablePackages = packages; + + tableHead.innerHTML = ''; + const headerRow: HTMLTableRowElement = document.createElement('tr'); + const packageHeader: HTMLTableCellElement = document.createElement('th'); + packageHeader.textContent = 'Package'; + headerRow.appendChild(packageHeader); + + for (const phase of phases) { + const th: HTMLTableCellElement = document.createElement('th'); + const displayPhase: string = phase.replace(/^_phase:/, ''); + th.textContent = displayPhase; + if (displayPhase !== phase) th.title = phase; + th.style.cursor = 'pointer'; + th.addEventListener('click', (e: Event) => { + const phaseNames: string[] = []; + for (const op of options.getOperations().values()) { + if ((op.phaseName || '(none)') === phase) phaseNames.push(op.name); + } + handleMultiSelectGroup(e as MouseEvent, phaseNames); + }); + headerRow.appendChild(th); + } + tableHead.appendChild(headerRow); + + tableBody.innerHTML = ''; + let opCount: number = 0; + const selection: Set = options.getSelection(); + + packages.forEach((pkg, rowIndex) => { + const tr: HTMLTableRowElement = document.createElement('tr'); + + const namesInRow: string[] = Array.from(pkg.byPhase.values()).map((op) => op.name); + const allSelected: boolean = !!namesInRow.length && namesInRow.every((name) => selection.has(name)); + if (allSelected) tr.classList.add(tableStyles.selected); + + const pkgTd: HTMLTableCellElement = document.createElement('td'); + pkgTd.className = tableStyles.pkgCell; + pkgTd.textContent = pkg.packageName; + pkgTd.style.fontWeight = '600'; + pkgTd.style.cursor = 'pointer'; + pkgTd.addEventListener('click', (e: Event) => { + handleMultiSelectGroup(e as MouseEvent, namesInRow); + e.stopPropagation(); + }); + tr.appendChild(pkgTd); + + phases.forEach((phase, phaseIndex) => { + const td: HTMLTableCellElement = document.createElement('td'); + td.className = tableStyles.pivotCell; + td.style.whiteSpace = 'nowrap'; + + const op: ITableOperation | undefined = pkg.byPhase.get(phase); + if (op) { + opCount++; + const displayStatus: string = options.computeDisplayStatus(op); + const glyph: string = options.enabledGlyph(op); + const active: string = op.isActive + ? `` + : ''; + td.innerHTML = ` + ${options.statusEmoji(displayStatus)} + ${escapeHtml(options.overallStatusText(displayStatus))} + ${glyph} + ${active} + `; + td.title = options.buildTooltip(op, displayStatus); + if (selection.has(op.name)) td.classList.add(tableStyles.selected); + td.style.cursor = 'pointer'; + tableOpOrder.push(op.name); + td.addEventListener('click', (e: Event) => { + handlePivotCellClick(e as MouseEvent, op.name, rowIndex, phaseIndex); + e.stopPropagation(); + }); + } else { + td.innerHTML = ''; + } + + tr.appendChild(td); + }); + + tableBody.appendChild(tr); + }); + + tableStats.textContent = opCount + ' operations'; + }; + + return { + renderTable + }; +} + +function escapeHtml(s: string): string { + return String(s).replace( + /[&<>"']/g, + (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c] as string + ); +} diff --git a/apps/rush-serve-dashboard/src/modules/terminalPane.ts b/apps/rush-serve-dashboard/src/modules/terminalPane.ts new file mode 100644 index 00000000000..11282c72e8f --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/terminalPane.ts @@ -0,0 +1,210 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { AnsiSgrParser } from './ansiSgrParser'; +import globalStyles from '../styles/global.module.css'; +import terminalStyles from '../styles/terminalPane.module.css'; + +interface ITerminalElementWithState extends HTMLElement { + _savedWidth?: number; +} + +export interface ITerminalPaneRefs { + terminalEl: HTMLElement | undefined; + terminalBody: HTMLElement | undefined; + termClearBtn: HTMLElement | undefined; + termAutoScrollCheckbox: HTMLInputElement | undefined; + termAutoscrollBtn: HTMLElement | undefined; + toggleTerminalBtn: HTMLElement | undefined; + resizerEl: HTMLElement | undefined; +} + +export interface ITerminalPaneController { + appendChunk(kind: string | undefined, text: string | undefined): void; +} + +export function createTerminalPaneController(refs: ITerminalPaneRefs): ITerminalPaneController { + const ansiParser: AnsiSgrParser = new AnsiSgrParser(); + _wireLayoutInit(refs.terminalEl); + _wireClearButton(refs.terminalBody, refs.termClearBtn); + _wireResizer(refs.terminalEl, refs.resizerEl); + _wireToggle(refs); + + return { + appendChunk(kind: string | undefined, text: string | undefined): void { + _appendChunk(refs, ansiParser, kind, text); + } + }; +} + +function _appendChunk( + refs: ITerminalPaneRefs, + ansiParser: AnsiSgrParser, + kind: string | undefined, + text: string | undefined +): void { + const { terminalEl, terminalBody, termAutoScrollCheckbox } = refs; + if (!terminalBody) return; + + const raw: string = String(text || ''); + const segments: Array<{ text: string; style?: string }> = ansiParser.process(raw); + + if (segments && segments.length) { + for (const seg of segments) { + const span: HTMLSpanElement = document.createElement('span'); + span.className = `${terminalStyles.termChunk} ${ + kind === 'stderr' ? terminalStyles.stderr : terminalStyles.stdout + }`; + if (seg.style) span.setAttribute('style', seg.style); + span.textContent = seg.text; + terminalBody.appendChild(span); + } + } + + if (!termAutoScrollCheckbox || termAutoScrollCheckbox.checked) { + terminalBody.scrollTop = terminalBody.scrollHeight; + } + + if (terminalEl && !terminalEl.classList.contains(terminalStyles.hidden)) { + terminalEl.classList.add(terminalStyles.termFlash); + setTimeout(() => terminalEl.classList.remove(terminalStyles.termFlash), 350); + } +} + +function _wireLayoutInit(terminalEl: HTMLElement | undefined): void { + try { + if (terminalEl) { + terminalEl.style.top = ''; + } + } catch { + // no-op + } +} + +function _wireClearButton( + terminalBody: HTMLElement | undefined, + termClearBtn: HTMLElement | undefined +): void { + if (!terminalBody || !termClearBtn) return; + + termClearBtn.addEventListener('click', () => { + terminalBody.innerHTML = ''; + }); +} + +function _wireResizer(terminalEl: HTMLElement | undefined, resizerEl: HTMLElement | undefined): void { + if (!terminalEl || !resizerEl) return; + + const terminalWithState: ITerminalElementWithState = terminalEl as ITerminalElementWithState; + let dragging: boolean = false; + let startX: number = 0; + let startWidth: number = 0; + const minW: number = 120; + const maxW: number = Math.max(240, window.innerWidth - 200); + + resizerEl.addEventListener('pointerdown', (e: PointerEvent) => { + dragging = true; + startX = e.clientX; + startWidth = terminalWithState.getBoundingClientRect().width; + resizerEl.setPointerCapture(e.pointerId); + document.body.style.userSelect = 'none'; + }); + + window.addEventListener('pointermove', (e: PointerEvent) => { + if (!dragging) return; + const dx: number = startX - e.clientX; + let newW: number = startWidth + dx; + newW = Math.max(minW, Math.min(maxW, newW)); + + terminalWithState._savedWidth = newW; + if (!terminalWithState.classList.contains(terminalStyles.hidden)) { + terminalWithState.style.width = newW + 'px'; + terminalWithState.style.flex = '0 0 ' + newW + 'px'; + } + }); + + window.addEventListener('pointerup', (e: PointerEvent) => { + if (!dragging) return; + dragging = false; + try { + resizerEl.releasePointerCapture(e.pointerId); + } catch { + // ignore mismatched pointer state + } + document.body.style.userSelect = ''; + }); + + resizerEl.tabIndex = 0; + resizerEl.addEventListener('keydown', (e: KeyboardEvent) => { + const step: number = 16; + const rect: DOMRect = terminalWithState.getBoundingClientRect(); + let w: number = rect.width; + if (e.key === 'ArrowLeft') w = Math.max(minW, w - step); + else if (e.key === 'ArrowRight') w = Math.min(maxW, w + step); + + terminalWithState._savedWidth = w; + if (!terminalWithState.classList.contains(terminalStyles.hidden)) { + terminalWithState.style.width = w + 'px'; + terminalWithState.style.flex = '0 0 ' + w + 'px'; + } + }); +} + +function _wireToggle(refs: ITerminalPaneRefs): void { + const { toggleTerminalBtn, terminalEl, resizerEl, termAutoscrollBtn, termAutoScrollCheckbox } = refs; + if (!toggleTerminalBtn || !terminalEl) return; + + const terminalWithState: ITerminalElementWithState = terminalEl as ITerminalElementWithState; + + toggleTerminalBtn.addEventListener('click', () => { + const currentlyHidden: boolean = terminalWithState.classList.contains(terminalStyles.hidden); + + if (currentlyHidden) { + terminalWithState.classList.remove(terminalStyles.hidden); + if (resizerEl) resizerEl.classList.remove(terminalStyles.hidden); + + if (terminalWithState._savedWidth) { + terminalWithState.style.width = terminalWithState._savedWidth + 'px'; + terminalWithState.style.flex = '0 0 ' + terminalWithState._savedWidth + 'px'; + } else { + terminalWithState.style.width = ''; + terminalWithState.style.flex = ''; + } + + if (resizerEl) resizerEl.tabIndex = 0; + toggleTerminalBtn.setAttribute('aria-pressed', 'true'); + toggleTerminalBtn.classList.add(globalStyles.active); + } else { + try { + terminalWithState._savedWidth = terminalWithState.getBoundingClientRect().width; + } catch { + // no-op + } + + terminalWithState.classList.add(terminalStyles.hidden); + if (resizerEl) { + resizerEl.classList.add(terminalStyles.hidden); + resizerEl.tabIndex = -1; + } + + terminalWithState.style.width = ''; + terminalWithState.style.flex = ''; + toggleTerminalBtn.setAttribute('aria-pressed', 'false'); + toggleTerminalBtn.classList.remove(globalStyles.active); + } + }); + + const isVisible: boolean = !terminalWithState.classList.contains(terminalStyles.hidden); + toggleTerminalBtn.setAttribute('aria-pressed', isVisible ? 'true' : 'false'); + if (isVisible) toggleTerminalBtn.classList.add(globalStyles.active); + else toggleTerminalBtn.classList.remove(globalStyles.active); + + if (termAutoscrollBtn && termAutoScrollCheckbox) { + termAutoscrollBtn.setAttribute('aria-pressed', termAutoScrollCheckbox.checked ? 'true' : 'false'); + termAutoscrollBtn.addEventListener('click', () => { + const newVal: boolean = !termAutoScrollCheckbox.checked; + termAutoScrollCheckbox.checked = newVal; + termAutoscrollBtn.setAttribute('aria-pressed', newVal ? 'true' : 'false'); + }); + } +} diff --git a/apps/rush-serve-dashboard/src/modules/topBar.ts b/apps/rush-serve-dashboard/src/modules/topBar.ts new file mode 100644 index 00000000000..92611a5311e --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/topBar.ts @@ -0,0 +1,193 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import globalStyles from '../styles/global.module.css'; +import topBarStyles from '../styles/topBar.module.css'; +import { getStatusClassName } from './statusHelpers'; + +export interface ITopBarRefs { + connectBtn: HTMLElement | undefined; + statusPill: HTMLElement | undefined; + statusEmojiEl: HTMLElement | undefined; + debugBtn: HTMLElement | undefined; + verboseBtn: HTMLElement | undefined; + playPauseBtn: HTMLElement | undefined; + parallelismInput: HTMLInputElement | undefined; + managerStateEl: HTMLElement | undefined; +} + +export interface ITopBarGraphState { + status?: string; + debugMode?: boolean; + verbose?: boolean; + pauseNextIteration?: boolean; + parallelism?: number | string; + hasScheduledIteration?: boolean; +} + +export function overallStatusText(status: string | undefined): string { + if (!status) return ''; + + switch (status) { + case 'SuccessWithWarning': + return 'WARNING'; + case 'FromCache': + return 'CACHED'; + case 'NoOp': + return 'NO-OP'; + case 'Disconnected': + return 'DISCONNECTED'; + case 'Connecting': + return 'CONNECTING'; + case 'Connected': + return 'CONNECTED'; + case 'Unknown': + return 'UNKNOWN'; + default: + return String(status).toUpperCase(); + } +} + +export function computeWsUrl(loc: Location): string { + if (!loc || !loc.host) { + return 'ws://localhost:9001/'; + } + + const proto: string = loc.protocol === 'https:' ? 'wss:' : 'ws:'; + return proto + '//' + loc.host + '/ws'; +} + +export function updateDerivedUrlDisplay(connectBtn: HTMLElement | undefined): void { + if (!connectBtn) return; + + const url: string = computeWsUrl(window.location); + connectBtn.title = 'Connect to WebSocket at ' + url; + connectBtn.setAttribute('aria-label', 'Connect to WebSocket at ' + url); +} + +export function showConnectingStatus( + statusPill: HTMLElement | undefined, + statusEmojiEl: HTMLElement | undefined, + statusEmoji: (status: string) => string +): void { + if (!statusPill || !statusEmojiEl) return; + + statusPill.className = `${globalStyles.statusPill} ${topBarStyles.statusPill} ${getStatusClassName('Unspecified')}`; + statusEmojiEl.textContent = statusEmoji('Waiting'); + statusPill.textContent = overallStatusText('Connecting'); +} + +export function updateStatusPill( + refs: ITopBarRefs, + ws: WebSocket | undefined, + graphSettings: ITopBarGraphState | undefined, + statusEmoji: (status: string) => string +): void { + if (!refs.statusPill || !refs.statusEmojiEl) return; + + let pillStatus: string = 'Disconnected'; + if (ws && ws.readyState === WebSocket.OPEN) { + pillStatus = graphSettings?.status || 'Unspecified'; + } + + refs.statusPill.className = `${globalStyles.statusPill} ${topBarStyles.statusPill} ${getStatusClassName(pillStatus)}`; + refs.statusEmojiEl.textContent = statusEmoji(pillStatus); + refs.statusPill.textContent = overallStatusText(pillStatus); +} + +export function setConnected( + refs: ITopBarRefs, + connected: boolean, + updateSelectionUI: () => void, + disabledControlIds: string[] +): void { + const connectBtnEl: HTMLElement | undefined = refs.connectBtn; + const iconSpan: HTMLElement | undefined = + (connectBtnEl?.querySelector('span.codicon') as HTMLElement | null) ?? undefined; + + if (connectBtnEl) { + if (connected) { + if (iconSpan) iconSpan.className = 'codicon codicon-debug-disconnect'; + connectBtnEl.setAttribute('data-state', 'connected'); + connectBtnEl.title = 'Disconnect WebSocket'; + connectBtnEl.setAttribute('aria-label', 'Disconnect WebSocket'); + } else { + if (iconSpan) iconSpan.className = 'codicon codicon-plug'; + connectBtnEl.setAttribute('data-state', 'disconnected'); + connectBtnEl.title = 'Connect to WebSocket'; + connectBtnEl.setAttribute('aria-label', 'Connect to WebSocket'); + updateDerivedUrlDisplay(connectBtnEl); + } + } + + disabledControlIds.forEach((id) => { + const el: HTMLButtonElement | HTMLInputElement | undefined = + (document.getElementById(id) as HTMLButtonElement | HTMLInputElement | null) ?? undefined; + if (el) el.disabled = !connected; + }); + + updateSelectionUI(); +} + +export function updateManagerState(refs: ITopBarRefs, graphSettings: ITopBarGraphState): void { + if (!graphSettings) return; + + const { debugBtn, verboseBtn, playPauseBtn, parallelismInput, managerStateEl } = refs; + + if (debugBtn) { + if (graphSettings.debugMode) debugBtn.classList.add(globalStyles.active); + else debugBtn.classList.remove(globalStyles.active); + debugBtn.setAttribute('aria-pressed', graphSettings.debugMode ? 'true' : 'false'); + debugBtn.title = graphSettings.debugMode ? 'Turn off debug logging' : 'Turn on debug logging'; + } + + if (verboseBtn) { + if (graphSettings.verbose) verboseBtn.classList.add(globalStyles.active); + else verboseBtn.classList.remove(globalStyles.active); + verboseBtn.setAttribute('aria-pressed', graphSettings.verbose ? 'true' : 'false'); + verboseBtn.title = graphSettings.verbose ? 'Turn off verbose logging' : 'Turn on verbose logging'; + } + + const ppIcon: HTMLElement | undefined = + (playPauseBtn?.querySelector('.codicon') as HTMLElement | null) ?? undefined; + if (playPauseBtn) { + if (!graphSettings.pauseNextIteration) { + playPauseBtn.classList.add(globalStyles.playing); + playPauseBtn.setAttribute('aria-label', 'Switch to manual (pause)'); + playPauseBtn.title = 'Pause automatic iterations'; + if (ppIcon) { + ppIcon.classList.remove('codicon-debug-start', 'codicon-debug-continue'); + ppIcon.classList.add('codicon-debug-pause'); + } + } else { + playPauseBtn.classList.remove(globalStyles.playing); + playPauseBtn.setAttribute('aria-label', 'Switch to automatic (play)'); + playPauseBtn.title = 'Resume automatic iterations'; + if (ppIcon) { + ppIcon.classList.remove('codicon-debug-pause'); + ppIcon.classList.add('codicon-debug-start'); + } + } + } + + if (parallelismInput) { + parallelismInput.value = String(graphSettings.parallelism ?? ''); + } + + if (managerStateEl) { + managerStateEl.innerHTML = ''; + } + + const executeBtn: HTMLElement | undefined = document.getElementById('execute-btn') ?? undefined; + if (executeBtn) { + if (graphSettings.hasScheduledIteration) { + executeBtn.classList.add(globalStyles.queued); + executeBtn.title = 'Run once (changes detected)'; + executeBtn.setAttribute('aria-label', 'Run once (changes detected)'); + } else { + executeBtn.classList.remove(globalStyles.queued); + executeBtn.title = 'Run once'; + executeBtn.setAttribute('aria-label', 'Run once'); + } + } +} diff --git a/apps/rush-serve-dashboard/src/modules/urlState.ts b/apps/rush-serve-dashboard/src/modules/urlState.ts new file mode 100644 index 00000000000..94748819a56 --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/urlState.ts @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export type DashboardView = 'table' | 'graph'; +export type DashboardFilter = 'all' | 'failed-warn'; + +export interface IDashboardUrlState { + view: DashboardView; + filter: DashboardFilter; +} + +export function loadDashboardUrlState(search: string): IDashboardUrlState { + const state: IDashboardUrlState = { + view: 'table', + filter: 'all' + }; + + try { + const params: URLSearchParams = new URLSearchParams(search); + const viewParam: string | null = params.get('view'); + const filterParam: string | null = params.get('filter'); + + if (viewParam === 'graph' || viewParam === 'table') { + state.view = viewParam; + } + + if (filterParam === 'failed-warn' || filterParam === 'all') { + state.filter = filterParam; + } + } catch { + // ignore invalid URL state + } + + return state; +} + +export function syncDashboardUrlState(view: DashboardView, filter: DashboardFilter): void { + try { + const params: URLSearchParams = new URLSearchParams(window.location.search); + params.set('view', view); + params.set('filter', filter); + const newUrl: string = window.location.pathname + '?' + params.toString() + window.location.hash; + window.history.replaceState(null, '', newUrl); + } catch { + // ignore browser APIs unavailable + } +} diff --git a/apps/rush-serve-dashboard/src/modules/viewBar.ts b/apps/rush-serve-dashboard/src/modules/viewBar.ts new file mode 100644 index 00000000000..c574bb70a26 --- /dev/null +++ b/apps/rush-serve-dashboard/src/modules/viewBar.ts @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { syncDashboardUrlState, type DashboardFilter, type DashboardView } from './urlState'; + +export interface IViewBarWiringOptions { + getView: () => DashboardView; + setView: (next: DashboardView) => void; + getFilter: () => DashboardFilter; + setFilter: (next: DashboardFilter) => void; + setSearchQuery: (next: string) => void; + markGraphDirty: () => void; + render: () => void; +} + +export function wireViewBar(options: IViewBarWiringOptions): void { + const { getView, setView, getFilter, setFilter, setSearchQuery, markGraphDirty, render } = options; + + document.querySelectorAll('input[name="view"]').forEach((radio: Element) => { + radio.addEventListener('change', () => { + const input: HTMLInputElement = radio as HTMLInputElement; + if (!input.checked) return; + + setView(input.value as DashboardView); + _applyViewVisibility(getView()); + syncDashboardUrlState(getView(), getFilter()); + render(); + }); + }); + + const filterSelect: HTMLSelectElement | null = document.getElementById( + 'filter-select' + ) as HTMLSelectElement | null; + if (filterSelect) { + filterSelect.addEventListener('change', (e: Event) => { + const next: DashboardFilter = (e.target as HTMLSelectElement).value as DashboardFilter; + setFilter(next); + markGraphDirty(); + syncDashboardUrlState(getView(), getFilter()); + render(); + }); + } + + const nameSearchInput: HTMLInputElement | null = document.getElementById( + 'name-search' + ) as HTMLInputElement | null; + if (nameSearchInput) { + nameSearchInput.addEventListener('input', () => { + setSearchQuery(nameSearchInput.value); + markGraphDirty(); + render(); + }); + } + + const initialView: DashboardView = getView(); + const viewRadio: HTMLInputElement | null = document.querySelector( + `input[name="view"][value="${initialView}"]` + ) as HTMLInputElement | null; + if (viewRadio) { + viewRadio.checked = true; + } + + if (filterSelect) { + filterSelect.value = getFilter(); + } + + _applyViewVisibility(initialView); + syncDashboardUrlState(getView(), getFilter()); +} + +function _applyViewVisibility(view: DashboardView): void { + const leftPane: HTMLElement | null = document.getElementById('left'); + const rightPane: HTMLElement | null = document.getElementById('right'); + + if (leftPane) { + leftPane.style.display = view === 'table' ? '' : 'none'; + } + + if (rightPane) { + rightPane.style.display = view === 'graph' ? '' : 'none'; + } +} diff --git a/apps/rush-serve-dashboard/src/styles/global.module.css b/apps/rush-serve-dashboard/src/styles/global.module.css new file mode 100644 index 00000000000..c8d248f46b6 --- /dev/null +++ b/apps/rush-serve-dashboard/src/styles/global.module.css @@ -0,0 +1,425 @@ +/* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. */ +/* See LICENSE in the project root for license information. */ + +:global(:root) { + --bg: #0f1115; + --panel: #1b1f27; + --panel-alt: #232a34; + --border: #2a3240; + --text: #d4d7dd; + --muted: #8692a2; + --accent: #3b82f6; + --accent-hover: #2563eb; + --danger: #ef4444; + --warn: #f59e0b; + --success: #10b981; + --rgb-accent: 59, 130, 246; + --rgb-white: 255, 255, 255; + --status-ready: #334155; + --status-waiting: #475569; + --status-queued: #475569; + --status-executing: var(--warn); + --status-success: var(--success); + --status-success-warning: #eab308; + --status-skipped: #475569; + --status-from-cache: #0ea5e9; + --status-failure: var(--danger); + --status-blocked: #475569; + --status-noop: #6366f1; + --status-aborted: #64748b; + --status-disconnected: #334155; + --scroll-track: #1b1f27; + --scroll-thumb: #334155; + --scroll-thumb-hover: #3f5166; + --radius: 6px; + font-family: + system-ui, + Segoe UI, + Roboto, + Helvetica, + Arial, + sans-serif; +} +:global(html), +:global(body) { + height: 100%; + margin: 0; + background: var(--bg); + color: var(--text); +} +.dashboard { + display: flex; + flex-direction: column; +} +.dashboard h1 { + font-size: 1.2rem; + margin: 0 0 0.5rem; +} +.dashboard a { + color: var(--accent); +} +.dashboard code { + background: var(--panel-alt); + padding: 2px 4px; + border-radius: 4px; +} +.flexSpacer { + flex: 1 1 auto; +} +.appTitle { + font-size: 0.8rem; + font-weight: 600; + letter-spacing: 0.5px; + min-width: 180px; +} +.actions, +.graphState { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + align-items: center; +} +.parallelismLabel { + font-size: 0.65rem; + align-items: baseline; + gap: 4px; +} +.searchLabel { + display: flex; + align-items: center; + gap: 4px; +} +.parallelismInput, +.nameSearch { + background: var(--panel-alt); + border: 1px solid var(--border); + color: var(--text); + border-radius: var(--radius); + padding: 2px 4px; + font-size: 0.7rem; +} +.parallelismInput { + width: 60px; +} +.nameSearch { + min-width: 140px; +} +.connectionForm { + gap: 0.4rem; +} +.contentWrap { + display: flex; + flex: 1; + min-height: 0; + overflow: hidden; +} +.main { + flex: 1; + display: flex; + min-height: 0; +} +.viewControls { + display: flex; + gap: 0.75rem; + align-items: center; + font-size: 0.7rem; +} +.viewControls label { + display: flex; + gap: 0.25rem; + align-items: center; + cursor: pointer; +} +.dashboard select { + background: var(--panel-alt); + color: var(--text); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 2px 4px; + font-size: 0.7rem; +} +.dashboard select:disabled { + opacity: 0.6; +} +.connectionForm input { + background: var(--panel-alt); + border: 1px solid var(--border); + color: var(--text); + padding: 0.35rem 0.5rem; + border-radius: var(--radius); +} +.dashboard button:not(:disabled) { + cursor: pointer; +} +.dashboard button:disabled { + opacity: 0.5; + cursor: not-allowed; +} +.action { + background: var(--panel-alt); + color: var(--text); + border: 1px solid var(--border); + padding: 0.35rem 0.6rem; + border-radius: var(--radius); + font-size: 0.8rem; +} +.action:hover:not(:disabled) { + background: #2d3744; +} +.primary, +.iconBtn.primary { + background: var(--accent); + border-color: var(--accent); + color: #fff; +} +.primary:hover:not(:disabled), +.iconBtn.primary:hover:not(:disabled) { + background: var(--accent-hover); +} +.iconBtn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 30px; + height: 30px; + padding: 0; + font-size: 16px; + line-height: 1; + background: transparent; + border: none; + color: var(--success); + position: relative; +} +.iconBtn:hover:not(:disabled), +.iconBtn.stop:hover:not(:disabled) { + filter: brightness(1.15); +} +.iconBtn.stop { + color: var(--danger); +} +.iconBtn.playing { + color: var(--accent); +} +.iconBtn.playing:hover:not(:disabled) { + color: var(--accent-hover); + filter: none; +} +.iconBtn:disabled { + opacity: 0.45; +} +.iconBtn:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + border-radius: 4px; +} +.iconBtn.queued::after { + content: ''; + position: absolute; + width: 18px; + height: 18px; + border: 2px dashed var(--warn); + border-radius: 50%; + animation: queuedPulse 1.2s linear infinite; + pointer-events: none; +} +.iconBtn.toggle { + color: var(--muted); +} +.iconBtn.toggle:hover:not(.active):not(:disabled) { + color: var(--text); +} +.iconBtn.toggle.active, +.iconBtn.active { + color: var(--accent); +} +.iconBtn.toggle.active:hover:not(:disabled) { + color: var(--accent-hover); +} +@keyframes queuedPulse { + 0%, + 100% { + opacity: 0.2; + } + 50% { + opacity: 0.9; + } +} +.dashboard :global(.codicon) { + font-size: 18px; + line-height: 1; +} +.iconBtn :global(.codicon) { + pointer-events: none; +} +.iconBtn.stop :global(.codicon) { + font-size: 17px; +} +.danger { + background: var(--danger); + border-color: var(--danger); + color: #fff; +} +.danger:hover:not(:disabled) { + filter: brightness(1.1); +} +.pane { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; +} +.leftPane { + border-right: 1px solid var(--border); +} +.section { + padding: 0.75rem 0.75rem 0.25rem; + background: var(--panel); + border-bottom: 1px solid var(--border); +} +.section h2 { + margin: 0 0 0.5rem; + font-size: 0.95rem; + letter-spacing: 0.5px; + font-weight: 600; +} +.statusPill { + padding: 2px 6px; + border-radius: 8px; + font-weight: 600; + font-size: 0.65rem; + text-transform: uppercase; + letter-spacing: 0.5px; + display: inline-block; + width: 110px; + text-align: center; + box-sizing: border-box; +} +.statusReady, +.statusUnspecified, +.statusUnknown { + background: var(--status-ready); +} +.statusWaiting { + background: var(--status-waiting); +} +.statusQueued { + background: var(--status-queued); +} +.statusExecuting { + background: var(--warn); + color: #000; +} +.statusSuccess { + background: var(--success); + color: #000; +} +.statusSuccessWithWarning { + background: var(--status-success-warning); + color: #000; +} +.statusFromCache { + background: var(--status-from-cache); + color: #000; +} +.statusFailure { + background: var(--danger); +} +.statusBlocked, +.statusSkipped { + background: var(--status-skipped); +} +.statusNoOp { + background: var(--status-noop); +} +.statusAborted, +.statusCanceled { + background: var(--status-aborted); +} +.statusUnknown { + opacity: 0.85; +} +.statusDisconnected { + background: var(--status-disconnected); +} +.mono { + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace; +} +.badge { + background: #334155; + padding: 2px 4px; + font-size: 0.55rem; + border-radius: 4px; + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 600; +} +.badge.silent { + background: #4b5563; +} +.badge.active { + background: var(--accent); +} +.badge.disabled { + background: #64748b; +} +.badge.localOnly { + background: var(--warn); + color: #000; +} +.badge.notRunning { + background: #1e293b; + border: 1px dashed #475569; +} +.bottomBar { + background: var(--panel); + border-top: 1px solid var(--border); + padding: 0.3rem 0.6rem; + display: flex; + gap: 1rem; + font-size: 0.6rem; + align-items: center; +} +.pill { + background: var(--panel-alt); + padding: 2px 6px; + border-radius: 12px; +} +.flexRow { + display: flex; + gap: 0.4rem; + align-items: center; +} +.dashboard input[type='number'] { + width: 70px; +} +@media (max-width: 1200px) { + .main { + flex-direction: column; + } + .leftPane { + border-right: none; + border-bottom: 1px solid var(--border); + } +} +.dashboard * { + scrollbar-color: var(--scroll-thumb) var(--scroll-track); + scrollbar-width: thin; +} +.dashboard *::-webkit-scrollbar { + width: 10px; + height: 10px; +} +.dashboard *::-webkit-scrollbar-track { + background: var(--scroll-track); +} +.dashboard *::-webkit-scrollbar-thumb { + background: var(--scroll-thumb); + border: 2px solid var(--scroll-track); + border-radius: 8px; +} +.dashboard *::-webkit-scrollbar-thumb:hover { + background: var(--scroll-thumb-hover); +} +.dashboard *::-webkit-scrollbar-corner { + background: var(--scroll-track); +} diff --git a/apps/rush-serve-dashboard/src/styles/graphView.module.css b/apps/rush-serve-dashboard/src/styles/graphView.module.css new file mode 100644 index 00000000000..0aaa61d566f --- /dev/null +++ b/apps/rush-serve-dashboard/src/styles/graphView.module.css @@ -0,0 +1,451 @@ +/* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. */ +/* See LICENSE in the project root for license information. */ + +.graphContainer { + position: relative; + flex: 1; + min-height: 0; + display: flex; + flex-direction: row; + align-items: stretch; + gap: 0; +} + +.graph { + width: 100%; + height: 100%; + position: relative; + overflow: auto; + background: radial-gradient(circle at 25% 20%, #1e2530, #101318); +} + +.phasePane { + width: 220px; + background: var(--panel); + border-right: 1px solid var(--border); + overflow: hidden; + font-size: 0.65rem; + padding: 0.4rem 0.5rem 0.6rem; + box-sizing: border-box; + position: relative; +} + +.phasePane h3 { + margin: 0 0 0.4rem; + font-size: 0.68rem; + letter-spacing: 0.5px; + text-transform: uppercase; + opacity: 0.8; +} + +.phaseGroup { + padding: 0.4rem 0.45rem 0.45rem; + background: var(--panel-alt); + border: 1px solid var(--border); + border-radius: 4px; + display: flex; + flex-direction: column; + min-height: 0; +} + +.phaseGroups { + display: flex; + flex-direction: column; + gap: 0.6rem; + height: calc(100% - 1.2rem); + overflow: hidden; +} + +.phaseGroups > .phaseGroup { + flex: 1 1 0; +} + +.phaseHeader { + display: flex; + align-items: center; + gap: 0.35rem; + font-weight: 600; + margin-bottom: 0.3rem; + font-size: 0.66rem; + padding: 2px 4px 3px; + background: #27303c; + border: 1px solid var(--border); + border-radius: 4px; +} + +.phaseStatusEmoji { + font-size: 0.8rem; + line-height: 1; +} + +.phaseName { + flex: 1; +} + +.phaseProblems { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 2px; + overflow-y: auto; + flex: 1 1 auto; + min-height: 0; +} + +.phaseProblems li { + display: flex; + align-items: center; + gap: 0.35rem; +} + +.phaseProblems a { + color: var(--accent); + text-decoration: none; +} + +.phaseProblems a:hover { + text-decoration: underline; +} + +.phaseProblemEmoji { + font-size: 0.75rem; +} + +.phasePaneEmpty { + opacity: 0.6; + font-style: italic; + font-size: 0.6rem; +} + +.graphWrapper { + flex: 1; + min-height: 0; + position: relative; +} + +.graphLegend { + position: absolute; + right: 28px; + bottom: 28px; + background: rgba(15, 17, 21, 0.9); + border: 1px solid var(--border); + border-radius: 6px; + padding: 4px 6px 6px; + font-size: 0.54rem; + line-height: 1.1; + max-width: 420px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); + pointer-events: auto; +} + +.graphLegend .legendColumns { + display: flex; + gap: 10px; + align-items: flex-start; +} + +.graphLegend .legendCol:first-child { + flex: 0 0 130px; +} + +.graphLegend .legendCol:last-child { + flex: 1 1 auto; +} + +.graphLegend.collapsed { + width: auto; + max-width: none; + padding: 4px 6px; +} + +.graphLegend.collapsed .legendColumns { + display: none; +} + +.graphLegend .legendCol { + flex: 1 1 0; + min-width: 0; + display: flex; + flex-direction: column; + gap: 2px; +} + +.graphLegend .legendHeading { + font-weight: 600; + letter-spacing: 0.5px; + text-transform: uppercase; + opacity: 0.75; + font-size: 0.58rem; + margin: 0 0 4px; +} + +.graphLegend .legendRow { + display: flex; + align-items: center; + gap: 5px; + min-width: 0; +} + +.graphLegend .legendLabelWrap { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} + +.graphLegend .legendRow small { + font-size: 0.5rem; + opacity: 0.7; +} + +.graphLegend .legendSubheading { + margin: 4px 0 2px; + font-size: 0.52rem; + text-transform: uppercase; + letter-spacing: 0.5px; + opacity: 0.65; + font-weight: 600; +} + +.graphLegend h4 { + margin: 0 0 4px; + font-size: 0.6rem; + letter-spacing: 0.6px; + text-transform: uppercase; + opacity: 0.8; + display: flex; + align-items: center; + gap: 4px; +} + +.legendEmoji { + font-size: 0.7rem; + width: 18px; + height: 18px; + display: flex; + align-items: center; + justify-content: center; + border: 2px solid var(--border); + border-radius: 6px; + background: #1f2530; + box-sizing: border-box; +} + +.legendEnabledSample { + position: relative; + width: 18px; + height: 18px; + border: 2px solid var(--border); + border-radius: 6px; + background: #1f2530; + display: flex; + align-items: center; + justify-content: center; + font-size: 13px; + box-sizing: border-box; +} + +.legendEnabledSample .sub { + position: absolute; + bottom: 0; + right: 0; + transform: translate(50%, 50%); + font-size: 9px; + line-height: 1; + filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.55)); + pointer-events: none; +} + +.legendLabel { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.graph svg { + position: absolute; + top: 0; + left: 0; + overflow: visible; + pointer-events: none; +} + +.graphMarquee { + position: absolute; + border: 1px solid var(--accent); + background: rgba(var(--rgb-accent), 0.15); + pointer-events: none; + z-index: 5; + box-shadow: 0 0 0 1px rgba(var(--rgb-accent), 0.4) inset; +} + +.opNode { + position: absolute; + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + padding: 0; + font-size: 14px; + font-family: inherit; + line-height: 1; + border: 2px solid var(--border); + border-radius: 6px; + background: #1f2530; + box-sizing: border-box; + user-select: none; + cursor: pointer; + transition: + border-color 0.15s ease, + transform 0.05s ease; +} + +.opNode .enabledIndicator { + position: absolute; + bottom: 0; + right: 0; + transform: translate(50%, 50%); + font-size: 11px; + line-height: 1; + pointer-events: none; + filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.55)); + transition: opacity 0.15s ease; +} + +.opNode.selected { + box-shadow: 0 0 0 2px var(--accent); +} + +.opNode:hover { + transform: scale(1.08); +} + +.opNode .activeIndicator { + position: absolute; + bottom: 0; + left: 0; + transform: translate(-50%, 50%); + font-size: 12px; + line-height: 1; + pointer-events: none; + filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.55)); +} + +.opNode .pendingIndicator { + position: absolute; + top: 0; + left: 0; + transform: translate(-50%, -50%); + font-size: 12px; + line-height: 1; + pointer-events: none; + filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.55)); +} + +.opNode.filteredOut .activeIndicator, +.opNode.filteredOutSearch .activeIndicator, +.opNode.notRunning .activeIndicator, +.opNode.filteredOut.notRunning .activeIndicator { + opacity: 1 !important; +} + +.opNode .name { + font-weight: 600; +} + +.opNode .pkg { + color: var(--muted); + font-size: 0.6rem; +} + +.opNode .badges { + display: flex; + gap: 4px; + flex-wrap: wrap; +} + +.opNode .enabledIndicator { + opacity: 1; +} + +.opNode.notRunning .enabledIndicator { + opacity: 0.5; +} + +.opNode.filteredOut .enabledIndicator { + opacity: 0.25; +} + +.opNode.filteredOutSearch .enabledIndicator { + opacity: 0.15; +} + +.opNode.filteredOut.notRunning .enabledIndicator { + opacity: 0.25; +} + +.opNode .emoji { + transition: opacity 0.15s ease; +} + +.opNode.filteredOut .emoji { + opacity: 0.25; +} + +.opNode.filteredOutSearch .emoji { + opacity: 0.15; +} + +.opNode.notRunning .emoji { + opacity: 0.5; +} + +.opNode.filteredOut.notRunning .emoji { + opacity: 0.25; +} + +.opNode.dashed { + border-style: dashed; +} + +.opNode.dotted { + border-style: dotted; +} + +.edge { + stroke-width: 1.2; + fill: none; +} + +.edge.dashed { + stroke-dasharray: 5 4; +} + +.edge.dotted { + stroke-dasharray: 2 4; +} + +.edge.filteredOut { + opacity: 0.35; +} + +.edge.filteredOutSearch { + opacity: 0.2; +} + +.edge.highlight { + stroke: var(--accent); + stroke-width: 2; +} + +.edge.dim { + opacity: 0.28; + filter: brightness(0.8); +} + +.edge.notRunning { + opacity: 0.55; +} diff --git a/apps/rush-serve-dashboard/src/styles/selectionBar.module.css b/apps/rush-serve-dashboard/src/styles/selectionBar.module.css new file mode 100644 index 00000000000..8d2250f3445 --- /dev/null +++ b/apps/rush-serve-dashboard/src/styles/selectionBar.module.css @@ -0,0 +1,45 @@ +/* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. */ +/* See LICENSE in the project root for license information. */ + +.selectionHeading { + font-weight: 600; + letter-spacing: 0.5px; + font-size: 0.7rem; + display: flex; + align-items: center; + gap: 0.5rem; +} +.selectionActions { + display: flex; + gap: 0.4rem; + flex-wrap: wrap; + align-items: center; +} +.selectionBar { + background: var(--panel-alt); + border-bottom: 1px solid var(--border); + padding: 0.35rem 0.6rem; + font-size: 0.65rem; + display: flex; + gap: 0.6rem; + align-items: center; + flex-wrap: wrap; +} +.selectionActions button { + font-size: 0.65rem; +} +.selectionBar button { + line-height: 1.1; + height: 30px; + display: inline-flex; + align-items: center; + gap: 4px; + font-size: 0.65rem; +} +.selectionCountButton[disabled] { + opacity: 0.6; + cursor: default; +} +.selectionBar[aria-hidden='true'] { + display: none; +} diff --git a/apps/rush-serve-dashboard/src/styles/tableView.module.css b/apps/rush-serve-dashboard/src/styles/tableView.module.css new file mode 100644 index 00000000000..3eff12a282a --- /dev/null +++ b/apps/rush-serve-dashboard/src/styles/tableView.module.css @@ -0,0 +1,108 @@ +/* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. */ +/* See LICENSE in the project root for license information. */ + +.operationsTableContainer { + flex: 1; + min-height: 0; + overflow: auto; +} + +.operationsTable { + width: 100%; + border-collapse: collapse; + font-size: 0.75rem; +} + +.operationsTable thead { + position: sticky; + top: 0; + background: var(--panel-alt); + z-index: 2; +} + +.operationsTable th, +.operationsTable td { + padding: 0.35rem 0.5rem; + border-bottom: 1px solid var(--border); + text-align: left; + vertical-align: top; +} + +.operationsTable tbody tr { + cursor: pointer; +} + +.operationsTable tbody tr.selected { + background: rgba(var(--rgb-accent), 0.15); +} + +.operationsTable tbody tr:hover { + background: rgba(var(--rgb-white), 0.05); +} + +.operationsTable td.pivotCell.selected { + background: rgba(var(--rgb-accent), 0.22); +} + +.operationsTable tr.selected > td.pkgCell { + background: rgba(var(--rgb-accent), 0.18); +} + +/* Prevent brief native text highlight flash during selection interactions */ +.operationsTable td.pivotCell, +.operationsTable td.pkgCell { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.operationsTable th.sortable { + user-select: none; +} + +.operationsTable th.sortable span.sortIndicator { + opacity: 0.5; + margin-left: 4px; +} + +.statusRow { + display: flex; + gap: 4px; + align-items: center; + flex-wrap: wrap; +} + +.statusRow .badges { + display: flex; + gap: 4px; + flex-wrap: wrap; + margin-left: 4px; +} + +.operationsTable td.statusCell { + white-space: nowrap; +} + +.operationsTable td.statusCell .statusPill { + margin-left: 4px; +} + +.pivotEnabled { + margin-left: 4px; + font-size: 0.75rem; +} + +.pivotActive { + margin-left: 4px; + font-size: 0.75rem; + position: relative; + top: 1px; +} + +.tableStats { + font-size: 0.6rem; + color: var(--muted); + padding: 0.25rem 0.75rem 0.5rem; + background: var(--panel); + border-top: 1px solid var(--border); +} diff --git a/apps/rush-serve-dashboard/src/styles/terminalPane.module.css b/apps/rush-serve-dashboard/src/styles/terminalPane.module.css new file mode 100644 index 00000000000..24908205c54 --- /dev/null +++ b/apps/rush-serve-dashboard/src/styles/terminalPane.module.css @@ -0,0 +1,92 @@ +/* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. */ +/* See LICENSE in the project root for license information. */ + +.resizer { + width: 6px; + cursor: col-resize; + background: linear-gradient(90deg, transparent, rgba(var(--rgb-accent), 0.06), transparent); + transition: background 0.12s ease; +} +.resizer:hover { + background: linear-gradient(90deg, transparent, rgba(var(--rgb-accent), 0.12), transparent); +} +.termAutoscroll { + display: none; +} +.terminalContainer { + background: #000; + border-left: 1px solid var(--border); + color: var(--text); + font-size: 0.75rem; + display: flex; + flex-direction: column; + align-self: stretch; + flex: 0 0 360px; + width: 360px; + min-width: 36px; + transition: + width 0.18s ease, + box-shadow 0.18s ease; +} +.terminalHeader { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 6px 8px; + border-bottom: 1px solid var(--border); +} +.terminalTitle { + font-weight: 600; + font-size: 0.8rem; +} +.terminalControls { + margin-left: auto; + display: flex; + gap: 6px; + align-items: center; +} +.terminalContainer.hidden, +.resizer.hidden { + display: none !important; +} +.terminalBody { + flex: 1 1 auto; + padding: 8px; + overflow: auto; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace; + white-space: pre; + font-size: 12px; + line-height: 1.25; +} +.termChunk.stdout { + color: var(--text); +} +.termChunk.stderr { + color: var(--danger); +} +.terminalControls button { + width: auto; + height: auto; + padding: 4px 8px; +} +.terminalContainer.termFlash .terminalHeader { + animation: termFlash 300ms ease-in-out; +} +.toggleTerminalButton[aria-pressed='true'], +.toggleTerminalButton[aria-pressed='true'] :global(.codicon) { + color: var(--accent) !important; + fill: var(--accent) !important; +} +.vertical { + display: inline-block; + transform: rotate(90deg); +} +@keyframes termFlash { + 0%, + 100% { + box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); + } + 30% { + box-shadow: 0 0 8px 2px rgba(59, 130, 246, 0.18); + } +} diff --git a/apps/rush-serve-dashboard/src/styles/topBar.module.css b/apps/rush-serve-dashboard/src/styles/topBar.module.css new file mode 100644 index 00000000000..e60eb9bd759 --- /dev/null +++ b/apps/rush-serve-dashboard/src/styles/topBar.module.css @@ -0,0 +1,95 @@ +/* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. */ +/* See LICENSE in the project root for license information. */ + +.topBar { + display: flex; + flex-wrap: wrap; + gap: 0.5rem 0.75rem; + align-items: baseline; + padding: 0.5rem 0.75rem; + background: var(--panel); + border-bottom: 1px solid var(--border); + max-height: 40vh; + overflow-y: auto; +} +.topBar > * { + display: flex; + align-items: baseline; +} +.topBar button { + width: auto; + height: auto; + padding: 0 4px; + font-size: 0.8rem; + line-height: 1.05; + align-items: baseline; +} +.topBar button :global(.codicon), +.topBar button span[aria-hidden='true'] { + position: relative; + top: 2px; + line-height: 1; +} +.actions, +.graphState, +.viewControls { + align-items: baseline; +} +.overallStatus { + display: flex; + align-items: center; +} +.statusEmoji { + position: relative; + top: 1px; + font-size: 1rem; + line-height: 1; + display: inline-flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; +} +.statusPill { + padding: 2px 10px; + border-radius: 999px; + font-size: 0.7rem; + font-weight: 600; + line-height: 1.05; +} +.connectionForm .connectButton[data-state='connected'] { + color: var(--danger); +} +.connectionForm .connectButton[data-state='disconnected'] { + color: var(--accent); +} +.topBar input[type='number']::-webkit-inner-spin-button, +.topBar input[type='number']::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} +.topBar input[type='number'] { + background-image: + linear-gradient(135deg, var(--muted) 50%, transparent 50%), + linear-gradient(315deg, var(--muted) 50%, transparent 50%), + linear-gradient(45deg, var(--muted) 50%, transparent 50%), + linear-gradient(225deg, var(--muted) 50%, transparent 50%); + background-size: + 6px 6px, + 6px 6px, + 6px 6px, + 6px 6px; + background-position: + calc(100% - 10px) 6px, + calc(100% - 6px) 6px, + calc(100% - 10px) calc(100% - 6px), + calc(100% - 6px) calc(100% - 6px); + background-repeat: no-repeat; + padding-right: 20px; + appearance: textfield; + -moz-appearance: textfield; +} +.topBar input[type='number']:focus { + outline: 1px solid var(--accent); + outline-offset: 0; +} diff --git a/apps/rush-serve-dashboard/src/test/actionWiring.test.ts b/apps/rush-serve-dashboard/src/test/actionWiring.test.ts new file mode 100644 index 00000000000..7493402b511 --- /dev/null +++ b/apps/rush-serve-dashboard/src/test/actionWiring.test.ts @@ -0,0 +1,201 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { wireLeftBarActions } from '../modules/leftBar'; +import { wireMainBarActions } from '../modules/mainBar'; +import { createSelectionBarController } from '../modules/selectionBar'; +import globalStyles from '../styles/global.module.css'; +import { + computeWsUrl, + overallStatusText, + setConnected, + showConnectingStatus, + updateManagerState, + updateStatusPill, + type ITopBarRefs +} from '../modules/topBar'; + +function click(id: string): void { + document.getElementById(id)?.dispatchEvent(new MouseEvent('click', { bubbles: true })); +} + +describe('action wiring', () => { + beforeEach(() => { + document.body.innerHTML = ''; + window.history.replaceState(null, '', '/dashboard'); + }); + + it('wires selection commands and safe/unsafe enabled-state mode', () => { + document.body.innerHTML = ` + + + + + `; + const sendCommand = jest.fn(); + const clearSelectionAndRender = jest.fn(); + const expandSelectionDependencies = jest.fn(); + const expandSelectionConsumers = jest.fn(); + wireLeftBarActions({ + sendCommand, + getSelection: () => new Set(['build']), + clearSelectionAndRender, + expandSelectionDependencies, + expandSelectionConsumers + }); + + click('invalidate-btn'); + click('selection-mode-btn'); + click('set-enabled-disabled-btn'); + click('expand-deps-btn'); + click('expand-consumers-btn'); + click('clear-selection-btn'); + + expect(sendCommand).toHaveBeenNthCalledWith(1, { command: 'invalidate', operationNames: ['build'] }); + expect(sendCommand).toHaveBeenNthCalledWith(2, { + command: 'set-enabled-states', + operationNames: ['build'], + targetState: 'never', + mode: 'unsafe' + }); + expect(expandSelectionDependencies).toHaveBeenCalledTimes(1); + expect(expandSelectionConsumers).toHaveBeenCalledTimes(1); + expect(clearSelectionAndRender).toHaveBeenCalledTimes(1); + }); + + it('wires manager commands and keyboard selection', () => { + document.body.innerHTML = + ''; + const debugBtn: HTMLButtonElement = document.createElement('button'); + const verboseBtn: HTMLButtonElement = document.createElement('button'); + const parallelismInput: HTMLInputElement = document.createElement('input'); + const playPauseBtn: HTMLButtonElement = document.createElement('button'); + const sendCommand = jest.fn(); + const connect = jest.fn(); + const setSelection = jest.fn(); + const clearSelection = jest.fn(); + const render = jest.fn(); + parallelismInput.value = '4'; + + wireMainBarActions({ + connect, + disconnect: jest.fn(), + isConnected: () => false, + sendCommand, + getGraphSettings: () => ({ debugMode: false, verbose: true, pauseNextIteration: false }), + debugBtn, + verboseBtn, + parallelismInput, + playPauseBtn, + getOperationNames: () => ['build', 'test'], + setSelection, + clearSelection, + hasSelection: () => true, + render + }); + + click('connect-btn'); + click('execute-btn'); + debugBtn.click(); + verboseBtn.click(); + parallelismInput.dispatchEvent(new Event('change')); + playPauseBtn.click(); + const textField: HTMLInputElement = document.getElementById('name-search') as HTMLInputElement; + const textFieldSelectAllEvent: KeyboardEvent = new KeyboardEvent('keydown', { + key: 'a', + ctrlKey: true, + bubbles: true, + cancelable: true + }); + textField.dispatchEvent(textFieldSelectAllEvent); + expect(textFieldSelectAllEvent.defaultPrevented).toBe(false); + expect(setSelection).not.toHaveBeenCalled(); + expect(render).not.toHaveBeenCalled(); + + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'a', ctrlKey: true })); + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })); + + expect(connect).toHaveBeenCalledTimes(1); + expect(sendCommand.mock.calls.map((call: unknown[]) => call[0])).toEqual([ + { command: 'execute' }, + { command: 'set-debug', value: true }, + { command: 'set-verbose', value: false }, + { command: 'set-parallelism', parallelism: 4 }, + { command: 'set-pause-next-iteration', value: true } + ]); + expect(setSelection).toHaveBeenCalledWith(new Set(['build', 'test'])); + expect(clearSelection).toHaveBeenCalledTimes(1); + expect(render).toHaveBeenCalledTimes(2); + }); +}); + +describe('top and selection bars', () => { + beforeEach(() => { + document.body.innerHTML = ` + + + + + +
+ `; + }); + + function getRefs(): ITopBarRefs { + return { + connectBtn: document.getElementById('connect-btn') ?? undefined, + statusPill: document.getElementById('status-pill') ?? undefined, + statusEmojiEl: document.getElementById('status-emoji') ?? undefined, + debugBtn: document.getElementById('debug-btn') ?? undefined, + verboseBtn: document.getElementById('verbose-btn') ?? undefined, + playPauseBtn: document.getElementById('play-pause-btn') ?? undefined, + parallelismInput: (document.getElementById('parallelism') as HTMLInputElement) ?? undefined, + managerStateEl: document.getElementById('manager-state') ?? undefined + }; + } + + it('formats URLs and updates connection and status state', () => { + const refs: ITopBarRefs = getRefs(); + expect(computeWsUrl({ host: 'example.test', protocol: 'https:' } as Location)).toBe( + 'wss://example.test/ws' + ); + expect(overallStatusText('SuccessWithWarning')).toBe('WARNING'); + + showConnectingStatus(refs.statusPill, refs.statusEmojiEl, () => 'waiting'); + expect(refs.statusPill?.textContent).toBe('CONNECTING'); + const socket: WebSocket = { readyState: WebSocket.OPEN } as WebSocket; + updateStatusPill(refs, socket, { status: 'Success' }, () => 'success'); + expect(refs.statusPill?.classList.contains(globalStyles.statusSuccess)).toBe(true); + + const updateSelectionUI = jest.fn(); + setConnected(refs, true, updateSelectionUI, ['execute-btn']); + expect(refs.connectBtn?.dataset.state).toBe('connected'); + expect((document.getElementById('execute-btn') as HTMLButtonElement).disabled).toBe(false); + expect(updateSelectionUI).toHaveBeenCalledTimes(1); + }); + + it('updates manager and selection controls', () => { + const refs: ITopBarRefs = getRefs(); + updateManagerState(refs, { + debugMode: true, + verbose: false, + pauseNextIteration: true, + parallelism: 8, + hasScheduledIteration: true + }); + expect(refs.debugBtn?.getAttribute('aria-pressed')).toBe('true'); + expect(refs.playPauseBtn?.title).toBe('Resume automatic iterations'); + expect(refs.parallelismInput?.value).toBe('8'); + expect(document.getElementById('execute-btn')?.classList.contains(globalStyles.queued)).toBe(true); + + const controller = createSelectionBarController({ + getSelection: () => new Set(['build']), + getCurrentView: () => 'graph', + isConnected: () => true + }); + controller.updateSelectionUI(); + expect(document.getElementById('view-heading-text')?.textContent).toBe('Dependency Graph'); + expect(document.getElementById('selection-count')?.textContent).toBe('1 selected'); + expect((document.getElementById('invalidate-btn') as HTMLButtonElement).disabled).toBe(false); + }); +}); diff --git a/apps/rush-serve-dashboard/src/test/dashboard.test.ts b/apps/rush-serve-dashboard/src/test/dashboard.test.ts new file mode 100644 index 00000000000..7721c618b33 --- /dev/null +++ b/apps/rush-serve-dashboard/src/test/dashboard.test.ts @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const sockets: MockWebSocket[] = []; + +class MockWebSocket extends EventTarget { + public static readonly OPEN: number = 1; + public readyState: number = 0; + + public constructor() { + super(); + sockets.push(this); + } + + public send(): void {} + public close(): void {} +} + +describe('dashboard entrypoint', () => { + const originalWebSocket: typeof WebSocket = globalThis.WebSocket; + + beforeEach(() => { + jest.resetModules(); + sockets.length = 0; + globalThis.WebSocket = MockWebSocket as unknown as typeof WebSocket; + window.history.replaceState(null, '', '/dashboard'); + document.body.innerHTML = ` + +
+ + +
+
+
+ +
+
+
+ + `; + }); + + afterEach(() => { + globalThis.WebSocket = originalWebSocket; + }); + + it('connects and renders a synchronization message', async () => { + const consoleSpy = jest.spyOn(window.console, 'log').mockImplementation(() => {}); + await import('../dashboard'); + const socket: MockWebSocket = sockets[0]; + socket.readyState = MockWebSocket.OPEN; + socket.dispatchEvent(new Event('open')); + socket.dispatchEvent( + new MessageEvent('message', { + data: JSON.stringify({ + event: 'sync', + operations: [{ name: 'package-a (build)', packageName: 'package-a', phaseName: '_phase:build' }], + currentExecutionStates: [{ name: 'package-a (build)', status: 'Success' }], + graphState: { status: 'Success', parallelism: 2 }, + sessionInfo: { actionName: 'start', repositoryIdentifier: 'rushstack' } + }) + }) + ); + + expect(document.title).toBe('start — rushstack'); + expect(document.getElementById('table-stats')?.textContent).toBe('1 operations'); + expect(document.querySelector('#operations-table tbody')?.textContent).toContain('package-a'); + expect(document.getElementById('status-pill')?.textContent).toBe('SUCCESS'); + consoleSpy.mockRestore(); + }); +}); diff --git a/apps/rush-serve-dashboard/src/test/dashboardState.test.ts b/apps/rush-serve-dashboard/src/test/dashboardState.test.ts new file mode 100644 index 00000000000..fbf296c0072 --- /dev/null +++ b/apps/rush-serve-dashboard/src/test/dashboardState.test.ts @@ -0,0 +1,182 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { AnsiSgrParser } from '../modules/ansiSgrParser'; +import { + applyExecutionStates, + patchOperationsFromPayload, + setOperationsFromPayload, + setQueuedStates, + toLastExecutionResultsMap +} from '../modules/dashboardMutations'; +import { computeFilterSetsCore, pruneGraphOperations } from '../modules/graphFiltering'; +import { + buildRunPolicyText, + buildTooltip, + computeDisplayStatus, + enabledGlyph, + getStatusColors, + statusEmoji +} from '../modules/statusHelpers'; +import { loadDashboardUrlState, syncDashboardUrlState } from '../modules/urlState'; + +describe(AnsiSgrParser.name, () => { + it('applies styles, preserves state between chunks, and resets it', () => { + const parser: AnsiSgrParser = new AnsiSgrParser(); + + expect(parser.process('plain\u001b[1;31mbold red')).toEqual([ + { text: 'plain', style: '' }, + { text: 'bold red', style: 'color: #a00; font-weight: 700' } + ]); + expect(parser.process(' continued\u001b[0m reset')).toEqual([ + { text: ' continued', style: 'color: #a00; font-weight: 700' }, + { text: ' reset', style: '' } + ]); + }); + + it('supports bright, background, underline, and inverse styles', () => { + const parser: AnsiSgrParser = new AnsiSgrParser(); + + expect(parser.process('\u001b[4;7;96;41mstyled')).toEqual([ + { + text: 'styled', + style: 'color: #55ffff; background-color: #a00; text-decoration: underline; filter: invert(100%)' + } + ]); + }); + + it('leaves malformed escape sequences as text', () => { + expect(new AnsiSgrParser().process('before\u001b[not-sgr')).toEqual([ + { text: 'before\u001b[not-sgr', style: '' } + ]); + }); +}); + +describe('dashboard mutations', () => { + it('replaces, patches, and updates operation state', () => { + const operations: Map = new Map([ + ['old', { name: 'old' }] + ]); + const executionStates: Map = new Map(); + + setOperationsFromPayload(operations, [{ name: 'build', status: 'Ready' }]); + patchOperationsFromPayload(operations, [{ name: 'test', status: 'Waiting' }]); + applyExecutionStates(operations, executionStates, [ + { name: 'build', status: 'Success', isActive: true }, + { name: 'unknown', status: 'Failure' } + ]); + + expect(Array.from(operations.keys())).toEqual(['build', 'test']); + expect(operations.get('build')).toMatchObject({ status: 'Success', isActive: true }); + expect(executionStates.has('unknown')).toBe(true); + }); + + it('replaces queued states and indexes prior results', () => { + const queuedStates: Map = new Map([['old', { name: 'old' }]]); + setQueuedStates(queuedStates, [{ name: 'next', status: 'Queued' }]); + + expect(Array.from(queuedStates.keys())).toEqual(['next']); + expect(toLastExecutionResultsMap([{ name: 'build', status: 'Failure' }]).get('build')?.status).toBe( + 'Failure' + ); + expect(toLastExecutionResultsMap(undefined).size).toBe(0); + }); +}); + +describe('graph filtering', () => { + it('merges execution state before filtering by status and search text', () => { + const operations: Map = new Map([ + ['package-a (build)', { name: 'package-a (build)', status: 'Ready' }], + ['package-b (test)', { name: 'package-b (test)', status: 'Success' }] + ]); + + const result = computeFilterSetsCore({ + operations, + executionStates: new Map([ + ['package-a (build)', { name: 'package-a (build)', status: 'Failure', isActive: true }] + ]), + currentFilter: 'failed-warn', + searchQuery: 'PACKAGE-A', + computeDisplayStatus: (operation) => operation.status || 'Ready' + }); + + expect(result.visibleOperations.map((operation) => operation.name)).toEqual(['package-a (build)']); + expect(result.filteredOutNames).toEqual(new Set(['package-b (test)'])); + expect(operations.get('package-a (build)')).toMatchObject({ status: 'Failure', isActive: true }); + }); + + it('removes no-op nodes and reconnects their dependents', () => { + const result = pruneGraphOperations([ + { name: 'compile', dependencies: [] }, + { name: 'noop', dependencies: ['compile'], noop: true }, + { name: 'test', dependencies: ['noop'] } + ]); + + expect(result).toEqual([ + { name: 'compile', dependencies: [] }, + { name: 'test', dependencies: ['compile'] } + ]); + }); +}); + +describe('status helpers', () => { + it('uses current state, prior results, and defaults in priority order', () => { + expect( + computeDisplayStatus( + { name: 'build', status: 'Ready' }, + new Map([['build', { name: 'build', status: 'Executing' }]]), + new Map() + ) + ).toBe('Executing'); + expect( + computeDisplayStatus( + { name: 'build', runInThisIteration: false }, + new Map(), + new Map([['build', { name: 'build', status: 'Success' }]]) + ) + ).toBe('Success'); + expect(computeDisplayStatus({ name: 'noop', noop: true }, new Map(), new Map())).toBe('NoOp'); + }); + + it('formats status, policy, and tooltip text', () => { + expect(statusEmoji('Failure')).toBe('❌'); + expect(statusEmoji('custom')).toBe('•'); + expect(enabledGlyph({ name: 'build', enabled: 'never' })).toBe('🔴'); + expect(buildRunPolicyText({ name: 'build', enabled: 'ignore-dependency-changes' })).toBe( + 'Ignores dependency changes' + ); + expect(buildTooltip({ name: 'build', isActive: true }, 'Success')).toContain('Has in-memory state'); + }); + + it('reads status colors with fallback variables', () => { + document.documentElement.style.setProperty('--status-ready', '#111111'); + document.documentElement.style.setProperty('--warn', '#222222'); + document.documentElement.style.setProperty('--danger', '#333333'); + + expect(getStatusColors()).toMatchObject({ + Ready: '#111111', + Executing: '#222222', + Failure: '#333333' + }); + }); +}); + +describe('URL state', () => { + it('loads supported values and ignores unsupported values', () => { + expect(loadDashboardUrlState('?view=graph&filter=failed-warn')).toEqual({ + view: 'graph', + filter: 'failed-warn' + }); + expect(loadDashboardUrlState('?view=cards&filter=success')).toEqual({ view: 'table', filter: 'all' }); + }); + + it('updates dashboard parameters while preserving other URL state', () => { + window.history.replaceState(null, '', '/dashboard?custom=value#output'); + + syncDashboardUrlState('graph', 'failed-warn'); + + expect(window.location.pathname + window.location.search + window.location.hash).toBe( + '/dashboard?custom=value&view=graph&filter=failed-warn#output' + ); + }); +}); diff --git a/apps/rush-serve-dashboard/src/test/dashboardWebSocket.test.ts b/apps/rush-serve-dashboard/src/test/dashboardWebSocket.test.ts new file mode 100644 index 00000000000..1d5739e67fb --- /dev/null +++ b/apps/rush-serve-dashboard/src/test/dashboardWebSocket.test.ts @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { createDashboardWebSocketController } from '../modules/dashboardWebSocket'; + +const mockSockets: MockWebSocket[] = []; + +class MockWebSocket extends EventTarget { + public static readonly OPEN: number = 1; + public readonly sent: string[] = []; + public readonly url: string; + public readyState: number = 0; + public closed: boolean = false; + + public constructor(url: string) { + super(); + this.url = url; + mockSockets.push(this); + } + + public send(data: string): void { + this.sent.push(data); + } + + public close(): void { + this.closed = true; + } +} + +describe('dashboard WebSocket controller', () => { + const originalWebSocket: typeof WebSocket = globalThis.WebSocket; + + beforeEach(() => { + mockSockets.length = 0; + jest.useFakeTimers(); + globalThis.WebSocket = MockWebSocket as unknown as typeof WebSocket; + }); + + afterEach(() => { + jest.useRealTimers(); + globalThis.WebSocket = originalWebSocket; + }); + + it('connects, parses messages, sends commands, and reports lifecycle events', () => { + const onConnectedStateChange = jest.fn(); + const onOpen = jest.fn(); + const onParsedMessage = jest.fn(); + const onParseError = jest.fn(); + const controller = createDashboardWebSocketController({ + getUrl: () => 'ws://localhost/ws', + onConnecting: jest.fn(), + onConnectedStateChange, + onOpen, + onClose: jest.fn(), + onError: jest.fn(), + onParsedMessage, + onParseError, + onLog: jest.fn() + }); + + controller.connect(); + const socket: MockWebSocket = mockSockets[0]; + socket.readyState = MockWebSocket.OPEN; + socket.dispatchEvent(new Event('open')); + socket.dispatchEvent(new MessageEvent('message', { data: '{"event":"sync"}' })); + socket.dispatchEvent(new MessageEvent('message', { data: 'invalid' })); + controller.sendCommand({ command: 'execute' }); + + expect(controller.isConnected()).toBe(true); + expect(onConnectedStateChange).toHaveBeenLastCalledWith(true); + expect(onOpen).toHaveBeenCalledTimes(1); + expect(onParsedMessage).toHaveBeenCalledWith({ event: 'sync' }); + expect(onParseError).toHaveBeenCalledTimes(1); + expect(socket.sent).toEqual(['{"command":"execute"}']); + }); + + it('reconnects after an unexpected close but not after a manual disconnect', () => { + const controller = createDashboardWebSocketController({ + getUrl: () => 'ws://localhost/ws', + onConnecting: jest.fn(), + onConnectedStateChange: jest.fn(), + onOpen: jest.fn(), + onClose: jest.fn(), + onError: jest.fn(), + onParsedMessage: jest.fn(), + onParseError: jest.fn(), + onLog: jest.fn() + }); + + controller.connect(); + mockSockets[0].dispatchEvent(new Event('close')); + jest.advanceTimersByTime(4000); + expect(mockSockets).toHaveLength(2); + + controller.disconnect(); + expect(mockSockets[1].closed).toBe(true); + jest.advanceTimersByTime(4000); + expect(mockSockets).toHaveLength(2); + }); +}); diff --git a/apps/rush-serve-dashboard/src/test/graphSelection.test.ts b/apps/rush-serve-dashboard/src/test/graphSelection.test.ts new file mode 100644 index 00000000000..9cae50407dc --- /dev/null +++ b/apps/rush-serve-dashboard/src/test/graphSelection.test.ts @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { createGraphSelectionController } from '../modules/graphSelection'; +import graphStyles from '../styles/graphView.module.css'; + +describe('graph selection controller', () => { + it('selects nodes and expands dependencies and consumers transitively', () => { + const graphEl: HTMLDivElement = document.createElement('div'); + let selection: Set = new Set(); + const onSelectionChanged = jest.fn(); + const controller = createGraphSelectionController({ + graphEl, + getCurrentView: () => 'graph', + getSelection: () => selection, + setSelection: (next) => (selection = next), + getOperations: () => + new Map([ + ['compile', { name: 'compile' }], + ['build', { name: 'build', dependencies: ['compile'] }], + ['test', { name: 'test', dependencies: ['build'] }] + ]), + getGraphNodePositions: () => new Map(), + graphNodeWidth: 100, + graphNodeHeight: 40, + onSelectionChanged, + onLiveSelectionChanged: jest.fn() + }); + + controller.singleSelect('test'); + controller.expandSelectionDependencies(); + expect(selection).toEqual(new Set(['test', 'build', 'compile'])); + controller.singleSelect('compile'); + controller.expandSelectionConsumers(); + expect(selection).toEqual(new Set(['compile', 'build', 'test'])); + controller.toggleSelect('build'); + expect(selection).toEqual(new Set(['compile', 'test'])); + expect(onSelectionChanged).toHaveBeenCalledTimes(5); + }); + + it('replaces selection using a marquee over graph nodes', () => { + const graphEl: HTMLDivElement = document.createElement('div'); + document.body.appendChild(graphEl); + graphEl.getBoundingClientRect = () => ({ left: 0, top: 0 }) as DOMRect; + let selection: Set = new Set(['outside']); + const onLiveSelectionChanged = jest.fn(); + const controller = createGraphSelectionController({ + graphEl, + getCurrentView: () => 'graph', + getSelection: () => selection, + setSelection: (next) => (selection = next), + getOperations: () => new Map(), + getGraphNodePositions: () => + new Map([ + ['inside', { x: 10, y: 10 }], + ['outside', { x: 200, y: 200 }] + ]), + graphNodeWidth: 50, + graphNodeHeight: 30, + onSelectionChanged: jest.fn(), + onLiveSelectionChanged + }); + controller.wireGraphMarqueeSelection(); + + graphEl.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, button: 0, clientX: 0, clientY: 0 })); + graphEl.dispatchEvent(new MouseEvent('mousemove', { bubbles: true, clientX: 100, clientY: 100 })); + window.dispatchEvent(new MouseEvent('mouseup')); + + expect(selection).toEqual(new Set(['inside'])); + expect(onLiveSelectionChanged).toHaveBeenCalled(); + expect(graphEl.querySelector(`.${graphStyles.graphMarquee}`)).toBeNull(); + }); +}); diff --git a/apps/rush-serve-dashboard/src/test/graphView.test.ts b/apps/rush-serve-dashboard/src/test/graphView.test.ts new file mode 100644 index 00000000000..a14815965a1 --- /dev/null +++ b/apps/rush-serve-dashboard/src/test/graphView.test.ts @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { createGraphViewController, graphState } from '../modules/graphView'; +import graphStyles from '../styles/graphView.module.css'; + +describe('graph view controller', () => { + it('renders nodes, removes transitive edges, updates indicators, and wires selection', () => { + document.documentElement.style.setProperty('--status-success', '#00aa00'); + document.body.innerHTML = '
'; + const graphEl: HTMLElement = document.getElementById('graph') as HTMLElement; + const edgesSvg: SVGSVGElement | undefined = + document.querySelector('svg#edges') ?? undefined; + if (!edgesSvg) { + throw new Error('The graph edges SVG test fixture was not found.'); + } + const operations = new Map([ + ['compile', { name: 'compile', status: 'Success', enabled: 'affected' }], + ['build', { name: 'build', dependencies: ['compile'], status: 'Success', isActive: true }], + ['test', { name: 'test', dependencies: ['compile', 'build'], status: 'Success', enabled: 'never' }] + ]); + const renderPhaseLegend = jest.fn(); + const singleSelect = jest.fn(); + const toggleSelect = jest.fn(); + const controller = createGraphViewController({ + graphEl, + edgesSvg, + getOperations: () => operations, + getExecutionStates: () => new Map(), + getQueuedStates: () => new Map([['test', { name: 'test', runInThisIteration: true }]]), + getSelection: () => new Set(['compile', 'build']), + getFilteredOutNames: () => new Set(), + getSearchFilteredOutNames: () => new Set(), + getLastExecutionResults: () => new Map(), + getComputeDisplayStatus: () => 'Success', + getStatusEmoji: () => 'ok', + getOverallStatusText: (status) => status || '', + renderPhaseLegend, + singleSelect, + toggleSelect + }); + + controller.ensureGraph(); + + expect(graphState.nodeElements.size).toBe(3); + expect(graphState.edgeElements.map(({ from, to }) => `${from}->${to}`).sort()).toEqual([ + 'build->compile', + 'test->build' + ]); + expect( + graphState.nodeElements.get('build')?.querySelector(`.${graphStyles.activeIndicator}`) + ).not.toBeNull(); + expect( + graphState.nodeElements.get('test')?.querySelector(`.${graphStyles.pendingIndicator}`) + ).not.toBeNull(); + expect( + graphState.nodeElements.get('test')?.querySelector(`.${graphStyles.enabledIndicator}`)?.textContent + ).toBe('🔴'); + expect(renderPhaseLegend).toHaveBeenCalled(); + + const compileNode: HTMLButtonElement | undefined = graphState.nodeElements.get('compile'); + expect(compileNode).toMatchObject({ type: 'button', tabIndex: 0 }); + expect(compileNode?.getAttribute('aria-label')).toBe('compile'); + expect(compileNode?.getAttribute('aria-pressed')).toBe('true'); + compileNode?.focus(); + expect(document.activeElement).toBe(compileNode); + + compileNode?.dispatchEvent(new MouseEvent('click', { bubbles: true })); + graphState.nodeElements + .get('build') + ?.dispatchEvent(new MouseEvent('click', { bubbles: true, ctrlKey: true })); + expect(singleSelect).toHaveBeenCalledWith('compile'); + expect(toggleSelect).toHaveBeenCalledWith('build'); + }); +}); diff --git a/apps/rush-serve-dashboard/src/test/outputPanels.test.ts b/apps/rush-serve-dashboard/src/test/outputPanels.test.ts new file mode 100644 index 00000000000..2192a3d2178 --- /dev/null +++ b/apps/rush-serve-dashboard/src/test/outputPanels.test.ts @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { createPhaseLegendController } from '../modules/phaseLegend'; +import { createTerminalPaneController } from '../modules/terminalPane'; +import graphStyles from '../styles/graphView.module.css'; +import terminalStyles from '../styles/terminalPane.module.css'; + +describe('phase and legend controller', () => { + beforeEach(() => { + window.localStorage.clear(); + document.body.innerHTML = '
'; + }); + + it('summarizes visible phases by priority and links operation logs', () => { + const operations = new Map([ + ['build-a', { name: 'build-a', phaseName: '_phase:build', logFileURLs: { text: '/logs/build-a.log' } }], + ['build-b', { name: 'build-b', phaseName: '_phase:build' }], + ['hidden', { name: 'hidden', phaseName: '_phase:test' }], + ['executing', { name: 'executing', phaseName: '_phase:test' }] + ]); + const statuses: Record = { + 'build-a': 'Failure', + 'build-b': 'Success', + hidden: 'Success', + executing: 'Executing' + }; + const controller = createPhaseLegendController({ + phaseGroupsEl: document.getElementById('phases') ?? undefined, + legendEl: document.getElementById('legend') ?? undefined, + getOperations: () => operations, + getGraphVisibleNames: () => new Set(['build-a', 'build-b']), + computeDisplayStatus: (operation) => statuses[operation.name], + statusEmoji: (status) => status, + overallStatusText: (status) => status || '', + getStatusColors: () => ({ Failure: '#ff0000' }) + }); + + controller.renderAll(); + + const phases: HTMLElement = document.getElementById('phases') as HTMLElement; + expect(phases.textContent).toContain('build'); + expect(phases.textContent).toContain('Failure'); + expect(phases.textContent).toContain('executing'); + expect(phases.textContent).not.toContain('hidden'); + const logLink: HTMLAnchorElement = phases.querySelector('a') as HTMLAnchorElement; + expect(logLink.getAttribute('href')).toBe('/logs/build-a.log'); + expect(logLink.rel).toBe('noopener noreferrer'); + + const collapseButton: HTMLButtonElement = document.getElementById( + 'legend-collapse-btn' + ) as HTMLButtonElement; + collapseButton.click(); + expect(document.getElementById('legend')?.classList.contains(graphStyles.collapsed)).toBe(true); + expect(window.localStorage.getItem('rushServeLegendCollapsed')).toBe('1'); + }); +}); + +describe('terminal pane controller', () => { + beforeEach(() => { + jest.useFakeTimers(); + document.body.innerHTML = ` + + +
`; + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('renders ANSI chunks and wires clear, auto-scroll, and visibility controls', () => { + const terminalEl: HTMLElement = document.getElementById('terminal') as HTMLElement; + const terminalBody: HTMLElement = document.getElementById('body') as HTMLElement; + terminalEl.getBoundingClientRect = () => ({ width: 320 }) as DOMRect; + const controller = createTerminalPaneController({ + terminalEl, + terminalBody, + termClearBtn: document.getElementById('clear') ?? undefined, + termAutoScrollCheckbox: document.getElementById('autoscroll-checkbox') as HTMLInputElement, + termAutoscrollBtn: document.getElementById('autoscroll') ?? undefined, + toggleTerminalBtn: document.getElementById('toggle') ?? undefined, + resizerEl: document.getElementById('resizer') ?? undefined + }); + + controller.appendChunk('stderr', '\u001b[31mfailed'); + const chunk: HTMLElement = terminalBody.querySelector(`.${terminalStyles.termChunk}`) as HTMLElement; + expect(chunk.textContent).toBe('failed'); + expect(chunk.classList.contains(terminalStyles.stderr)).toBe(true); + expect(chunk.style.color).toBe('rgb(170, 0, 0)'); + expect(terminalEl.classList.contains(terminalStyles.termFlash)).toBe(true); + jest.advanceTimersByTime(350); + expect(terminalEl.classList.contains(terminalStyles.termFlash)).toBe(false); + + document.getElementById('autoscroll')?.click(); + expect((document.getElementById('autoscroll-checkbox') as HTMLInputElement).checked).toBe(false); + document.getElementById('toggle')?.click(); + expect(terminalEl.classList.contains(terminalStyles.hidden)).toBe(true); + document.getElementById('clear')?.click(); + expect(terminalBody.textContent).toBe(''); + }); +}); diff --git a/apps/rush-serve-dashboard/src/test/tableView.test.ts b/apps/rush-serve-dashboard/src/test/tableView.test.ts new file mode 100644 index 00000000000..afae8a9ecde --- /dev/null +++ b/apps/rush-serve-dashboard/src/test/tableView.test.ts @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { createTableViewController } from '../modules/tableView'; +import tableStyles from '../styles/tableView.module.css'; + +describe('table view controller', () => { + it('renders a package/phase pivot safely and supports cell and group selection', () => { + document.body.innerHTML = '
'; + const operations = new Map([ + ['a-build', { name: 'a-build', packageName: '', phaseName: '_phase:build', isActive: true }], + ['a-test', { name: 'a-test', packageName: '', phaseName: '_phase:test' }], + ['b-build', { name: 'b-build', packageName: 'package-b', phaseName: '_phase:build' }] + ]); + let selection: Set = new Set(); + const onSelectionMutated = jest.fn(); + const controller = createTableViewController({ + tableHead: document.querySelector('thead') ?? undefined, + tableBody: document.querySelector('tbody') ?? undefined, + tableStats: document.getElementById('stats') ?? undefined, + getOperations: () => operations, + getFilteredOperations: () => Array.from(operations.values()), + getSelection: () => selection, + setSelection: (next) => (selection = next), + onSelectionMutated, + computeDisplayStatus: () => 'Success', + enabledGlyph: () => 'enabled', + buildRunPolicyText: () => 'Run if affected', + buildTooltip: (operation) => operation.name, + statusEmoji: () => 'ok', + overallStatusText: (status) => status || '' + }); + + controller.renderTable(); + + expect(document.querySelector('thead')?.textContent).toContain('build'); + expect(document.querySelector('tbody')?.textContent).toContain(''); + expect(document.querySelector('tbody')?.innerHTML).not.toContain(''); + expect(document.getElementById('stats')?.textContent).toBe('3 operations'); + + const firstOperationCell = document.querySelector( + `td.${tableStyles.pivotCell}[title="a-build"]` + ) as HTMLElement; + firstOperationCell.click(); + expect(selection).toEqual(new Set(['a-build'])); + + const firstPackageCell = document.querySelector(`.${tableStyles.pkgCell}`) as HTMLElement; + firstPackageCell.click(); + expect(selection).toEqual(new Set(['a-build', 'a-test'])); + expect(onSelectionMutated).toHaveBeenCalledTimes(2); + }); +}); diff --git a/apps/rush-serve-dashboard/src/test/viewBar.test.ts b/apps/rush-serve-dashboard/src/test/viewBar.test.ts new file mode 100644 index 00000000000..3283f60dd51 --- /dev/null +++ b/apps/rush-serve-dashboard/src/test/viewBar.test.ts @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { wireViewBar } from '../modules/viewBar'; +import type { DashboardFilter, DashboardView } from '../modules/urlState'; + +describe(wireViewBar.name, () => { + beforeEach(() => { + document.body.innerHTML = ` + + + + +
+ `; + window.history.replaceState(null, '', '/dashboard'); + }); + + it('initializes controls, panel visibility, and URL state', () => { + wireViewBar({ + getView: () => 'graph', + setView: jest.fn(), + getFilter: () => 'failed-warn', + setFilter: jest.fn(), + setSearchQuery: jest.fn(), + markGraphDirty: jest.fn(), + render: jest.fn() + }); + + expect(document.querySelector('input[value="graph"]')?.checked).toBe(true); + expect(document.querySelector('#filter-select')?.value).toBe('failed-warn'); + expect(document.getElementById('left')?.style.display).toBe('none'); + expect(document.getElementById('right')?.style.display).toBe(''); + expect(window.location.search).toBe('?view=graph&filter=failed-warn'); + }); + + it('updates state and renders when view, filter, and search controls change', () => { + let view: DashboardView = 'table'; + let filter: DashboardFilter = 'all'; + const setSearchQuery = jest.fn(); + const markGraphDirty = jest.fn(); + const render = jest.fn(); + wireViewBar({ + getView: () => view, + setView: (next: DashboardView) => (view = next), + getFilter: () => filter, + setFilter: (next: DashboardFilter) => (filter = next), + setSearchQuery, + markGraphDirty, + render + }); + + const graphRadio: HTMLInputElement = document.querySelector( + 'input[value="graph"]' + ) as HTMLInputElement; + graphRadio.checked = true; + graphRadio.dispatchEvent(new Event('change')); + + const filterSelect: HTMLSelectElement = document.getElementById('filter-select') as HTMLSelectElement; + filterSelect.value = 'failed-warn'; + filterSelect.dispatchEvent(new Event('change')); + + const searchInput: HTMLInputElement = document.getElementById('name-search') as HTMLInputElement; + searchInput.value = 'build'; + searchInput.dispatchEvent(new Event('input')); + + expect(view).toBe('graph'); + expect(filter).toBe('failed-warn'); + expect(setSearchQuery).toHaveBeenCalledWith('build'); + expect(markGraphDirty).toHaveBeenCalledTimes(2); + expect(render).toHaveBeenCalledTimes(3); + expect(document.getElementById('left')?.style.display).toBe('none'); + expect(document.getElementById('right')?.style.display).toBe(''); + expect(window.location.search).toBe('?view=graph&filter=failed-warn'); + }); +}); diff --git a/apps/rush-serve-dashboard/tsconfig.json b/apps/rush-serve-dashboard/tsconfig.json new file mode 100644 index 00000000000..9688e20399b --- /dev/null +++ b/apps/rush-serve-dashboard/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-web-rig/profiles/app/tsconfig-base.json" +} diff --git a/apps/rush/.eslintrc.js b/apps/rush/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/apps/rush/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/apps/rush/.npmignore b/apps/rush/.npmignore index b84c64f869e..0bc76278fc1 100644 --- a/apps/rush/.npmignore +++ b/apps/rush/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,19 +21,19 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -/lib/start-dev.* -/lib/start-dev-docs.* +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- -# (Add your project-specific overrides here) \ No newline at end of file +/lib-*/start-dev.* +/lib-*/start-dev-docs.* diff --git a/apps/rush/CHANGELOG.json b/apps/rush/CHANGELOG.json index 388424dfe6f..350eadec0c9 100644 --- a/apps/rush/CHANGELOG.json +++ b/apps/rush/CHANGELOG.json @@ -1,6 +1,3071 @@ { "name": "@microsoft/rush", "entries": [ + { + "version": "5.178.1", + "tag": "@microsoft/rush_v5.178.1", + "date": "Wed, 05 Aug 2026 19:31:07 GMT", + "comments": { + "none": [ + { + "comment": "Add `useDirectFileTransfersForBuildCache` to the `experiments.json` `rush init` template." + }, + { + "comment": "Add catalog support to `rush-pnpm update`." + } + ], + "patch": [ + { + "comment": "Fix an issue where a phased command exited with a nonzero exit code when its overall execution status was successful but not `SUCCESS`. This covers an iteration that scheduled no operations because a plugin consumed the work itself (which broke `rush --drop-graph` in `@rushstack/rush-buildxl-graph-plugin`), as well as an iteration that a plugin short-circuited with a `SKIPPED` or `FROM CACHE` status." + } + ] + } + }, + { + "version": "5.178.0", + "tag": "@microsoft/rush_v5.178.0", + "date": "Mon, 20 Jul 2026 17:49:39 GMT", + "comments": { + "none": [ + { + "comment": "Fix `minimumReleaseAge` and `minimumReleaseAgeExclude` in `pnpm-config.json` being silently ignored because they were written to package.json instead of `pnpm-workspace.yaml`" + }, + { + "comment": "Add optional file-based transfer APIs (`tryDownloadCacheEntryToFileAsync`, `tryUploadCacheEntryFromFileAsync`) to `ICloudBuildCacheProvider`, allowing cache plugins to transfer cache entries directly to and from files on disk without buffering entire contents in memory. Implement in `@rushstack/rush-http-build-cache-plugin`, `@rushstack/rush-amazon-s3-build-cache-plugin`, and `@rushstack/rush-azure-storage-build-cache-plugin`. Gated behind the `useDirectFileTransfersForBuildCache` experiment." + }, + { + "comment": "Avoid redundant downloads when multiple local Rush processes race to restore the same build cache entry (when useDirectFileTransfersForBuildCache is enabled)." + }, + { + "comment": "Replace `@override` with the `override` keyword." + }, + { + "comment": "(PLUGIN BREAKING CHANGE) Overhaul watch-mode commands such that the graph is only created once at the start of command invocation, along with a stateful manager object. Plugins may now access the manager object and use it to orchestrate and tap into the build process." + } + ], + "patch": [ + { + "comment": "Pass change file paths as discrete Git arguments when adding commit details during publishing." + }, + { + "comment": "Fix an issue where \"rush-pnpm patch-commit\" rewrote the pre-existing \"globalPatchedDependencies\" entries in pnpm-config.json using absolute paths when running with pnpm >= 9." + }, + { + "comment": "Fixed an issue where the pnpm `ignoredOptionalDependencies`, `trustPolicy`, `trustPolicyExclude`, and `trustPolicyIgnoreAfterMinutes` settings were written to the `pnpm` field of the generated `package.json` (which pnpm 11 ignores) instead of `pnpm-workspace.yaml`, causing them to be silently ignored under pnpm 11." + }, + { + "comment": "Fix pnpm 11 silently ignoring the `globalOverrides`, `globalPackageExtensions`, `globalPeerDependencyRules`, `globalAllowedDeprecatedVersions`, and `globalPatchedDependencies` settings from pnpm-config.json. Because pnpm 11 no longer reads the `pnpm` field of package.json, Rush now writes these settings to the generated `common/temp/pnpm-workspace.yaml` for pnpm 11+ (matching the existing `allowBuilds` relocation), and `rush-pnpm patch-commit`/`patch-remove` now read `patchedDependencies` back from `pnpm-workspace.yaml` for pnpm 11+. Behavior for pnpm 10 and earlier is unchanged." + } + ] + } + }, + { + "version": "5.177.2", + "tag": "@microsoft/rush_v5.177.2", + "date": "Wed, 08 Jul 2026 21:27:15 GMT", + "comments": { + "patch": [ + { + "comment": "Upgrade the `pnpm-sync-lib` dependency to 0.3.4." + } + ] + } + }, + { + "version": "5.177.1", + "tag": "@microsoft/rush_v5.177.1", + "date": "Sat, 20 Jun 2026 21:37:30 GMT", + "comments": { + "none": [ + { + "comment": "Bump `ws` in `rush-serve-plugin` to mitigate CVE-2026-48779." + } + ] + } + }, + { + "version": "5.177.0", + "tag": "@microsoft/rush_v5.177.0", + "date": "Sat, 20 Jun 2026 00:16:15 GMT", + "comments": { + "none": [ + { + "comment": "Fix build cache failures when running inside a git linked worktree via a pre-commit hook, caused by GIT_DIR being set to the per-worktree metadata directory" + } + ], + "patch": [ + { + "comment": "Set Redis `connectTimeout` and `socketTimeout` so half-dead TCP connections (NAT/firewall) surface in seconds instead of stalling in-flight commands for many minutes while the kernel waits to give up." + } + ] + } + }, + { + "version": "5.176.0", + "tag": "@microsoft/rush_v5.176.0", + "date": "Tue, 09 Jun 2026 02:02:32 GMT", + "comments": { + "none": [ + { + "comment": "Bump the `ws` dependency to `~8.20.0`." + } + ], + "minor": [ + { + "comment": "Add support for pnpm 11's `allowBuilds` field in `pnpm-workspace.yaml`. Rush now correctly handles the pnpm 11 security model where build scripts must be explicitly approved. The new `globalAllowBuilds` field in `pnpm-config.json` replaces the deprecated `globalOnlyBuiltDependencies` and `globalNeverBuiltDependencies` fields for pnpm 11+. The `rush-pnpm approve-builds` command is also updated to work correctly with pnpm 11." + }, + { + "comment": "Include seconds in the generated change file name so that running `rush change` more than once in the same minute no longer silently overwrites the previously generated change file." + }, + { + "comment": "Default `rush-pnpm outdated` and `rush-pnpm why` to recursive workspace queries." + } + ], + "patch": [ + { + "comment": "Fix a regression where Rush change-detection treated any `pnpm-config.json` containing comments as unparseable, causing every project to be flagged as impacted." + }, + { + "comment": "Route the \"Lockfile was created or deleted\" warning to stderr so that machine-readable output (e.g. `rush list --json`) remains parseable when the lockfile was added or removed in the diff range." + }, + { + "comment": "Fix `rush update` not syncing `pnpm-lock.yaml` when a workspace dependency moves from `dependencies` to `devDependencies`." + } + ] + } + }, + { + "version": "5.175.1", + "tag": "@microsoft/rush_v5.175.1", + "date": "Mon, 20 Apr 2026 23:31:34 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where `rush list --detailed` did not print horizontal table separators." + } + ] + } + }, + { + "version": "5.175.0", + "tag": "@microsoft/rush_v5.175.0", + "date": "Sat, 18 Apr 2026 03:47:29 GMT", + "comments": { + "patch": [ + { + "comment": "Bump the Azure cache plugin dependencies to use `@azure/identity` `~4.13.1` and `@azure/storage-blob` `~12.31.0`." + }, + { + "comment": "Remove unused dependencies: replace `glob-escape` with `fast-glob`'s `escapePath`, replace `figures.pointer` with a named const, replace `builtin-modules` with `node:module.isBuiltin()`." + }, + { + "comment": "Replace `cli-table` dependency with `TerminalTable` from `@rushstack/terminal`." + } + ], + "none": [ + { + "comment": "Bump semver." + }, + { + "comment": "Replace deprecated `inquirer` packages with modern per-prompt `@inquirer/*` family of packages." + } + ] + } + }, + { + "version": "5.174.0", + "tag": "@microsoft/rush_v5.174.0", + "date": "Thu, 16 Apr 2026 05:25:41 GMT", + "comments": { + "none": [ + { + "comment": "rush-resolver-cache-plugin: add pnpm 10 / lockfile v9 compatibility" + }, + { + "comment": "Deprecate `minimumReleaseAge` in `common/config/rush/pnpm-config.json`; use `minimumReleaseAgeMinutes` instead" + }, + { + "comment": "Add support for pnpm global catalog detection to `rush change`. Now, when a dependencyis changed in the pnpm global catalog, changelogs will be required for affected published packages." + }, + { + "comment": "Fix a bug where the injected dependency state hash updated on devDependency changes that don't impact the lockfile." + }, + { + "comment": "Add \"strictChangefileValidation\" experiment and \"--verify-all\" flag for \"rush change\". When the experiment is enabled, \"rush change --verify\" and \"rush change --verify-all\" will report errors if change files reference nonexistent projects or target non-main projects in a lockstepped version policy." + } + ], + "minor": [ + { + "comment": "Add support for pnpm `trustPolicy`, `trustPolicyExclude`, and `trustPolicyIgnoreAfterMinutes` settings in `pnpm-config.json`." + } + ] + } + }, + { + "version": "5.173.0", + "tag": "@microsoft/rush_v5.173.0", + "date": "Fri, 10 Apr 2026 22:46:54 GMT", + "comments": { + "none": [ + { + "comment": "Move stale autoinstaller `node_modules` folders into Rush's recycler before asynchronously deleting them, instead of synchronously deleting them in place." + }, + { + "comment": "Filter npm-incompatible properties from .npmrc when installing rush-lib via npm, to eliminate spurious \"Unknown env config\" and \"Unknown project config\" warnings." + }, + { + "comment": "When cobuilds are remote executing, check them after locally executable tasks." + } + ], + "minor": [ + { + "comment": "Add async variants of disk-touching APIs in `PackageJsonEditor` (`loadAsync`, `saveIfModifiedAsync`), `CommonVersionsConfiguration` (`loadFromFileAsync`, `saveAsync`), and `VersionPolicy` (`setDependenciesBeforePublishAsync`, `setDependenciesBeforeCommitAsync`); deprecate corresponding sync methods." + } + ] + } + }, + { + "version": "5.172.1", + "tag": "@microsoft/rush_v5.172.1", + "date": "Wed, 25 Mar 2026 01:01:07 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where \"rush deploy\" could fail with EEXIST due to a \"npm-packlist\" regression (GitHub #5720)" + } + ] + } + }, + { + "version": "5.172.0", + "tag": "@microsoft/rush_v5.172.0", + "date": "Tue, 24 Mar 2026 21:52:13 GMT", + "comments": { + "none": [ + { + "comment": "Add `getCustomParametersByLongName()` and `setHandled()` to `IGlobalCommand`, enabling Rush plugins to handle global command execution and access parsed command-line parameter values." + }, + { + "comment": "Add a new \"globalPlugin\" command kind for command-line.json that allows Rush plugins to define global commands without a shellCommand. This command kind can only be used in plugin-provided command-line.json files." + } + ] + } + }, + { + "version": "5.171.0", + "tag": "@microsoft/rush_v5.171.0", + "date": "Sat, 21 Mar 2026 03:10:24 GMT", + "comments": { + "minor": [ + { + "comment": "Add RUSH_QUIET_MODE environment variable that, when set to `1` or `true`, is equivalent to passing `--quiet` for `rush`, `rushx`, and `install-run-rush.ts`" + } + ], + "patch": [ + { + "comment": "Update OperationExecutionRecord to only set `this.logFilePaths` if the value to be assigned is not `undefined`" + }, + { + "comment": "Fix ProblemCollector wiring in OperationExecutionRecord: add preventAutoclose, strip ANSI colors before matching, and always include problemCollector in the terminal pipeline" + }, + { + "comment": "Fix weighted concurrency budget being capped by operation count" + } + ], + "none": [ + { + "comment": "Fix an issue where the assets used by `rush init` weren't shipped." + } + ] + } + }, + { + "version": "5.170.1", + "tag": "@microsoft/rush_v5.170.1", + "date": "Thu, 12 Mar 2026 22:33:34 GMT", + "comments": { + "none": [ + { + "comment": "Fix a recent regression that sometimes produced ENOENT errors when installing autoinstallers" + } + ] + } + }, + { + "version": "5.170.0", + "tag": "@microsoft/rush_v5.170.0", + "date": "Thu, 12 Mar 2026 09:15:52 GMT", + "comments": { + "none": [ + { + "comment": "Add custom endpoint support via a `storageEndpoint` configuration option for the Azure Storage build cache plugin." + }, + { + "comment": "Fix autoinstaller plugin loader behavior." + }, + { + "comment": "Support percentage weight in operationSettings in rush-project.json file." + } + ] + } + }, + { + "version": "5.169.3", + "tag": "@microsoft/rush_v5.169.3", + "date": "Mon, 23 Feb 2026 00:42:39 GMT", + "comments": { + "none": [ + { + "comment": "Fix .npmrc syncing to common/temp incorrectly caching results, which caused pnpm-specific properties like hoist-pattern to be stripped when the same .npmrc was processed with different options." + } + ] + } + }, + { + "version": "5.169.2", + "tag": "@microsoft/rush_v5.169.2", + "date": "Fri, 20 Feb 2026 00:15:23 GMT", + "comments": { + "none": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ] + } + }, + { + "version": "5.169.1", + "tag": "@microsoft/rush_v5.169.1", + "date": "Thu, 19 Feb 2026 01:30:24 GMT", + "comments": { + "none": [ + { + "comment": "Add missing README for rush-azure-storage-build-cache-plugin and rush-buildxl-graph-plugin. Filter files from publish for rush-bridge-cache-plugin." + }, + { + "comment": "Fix an issue where files were missing from the published version of `@rushstack/package-extractor`." + } + ] + } + }, + { + "version": "5.169.0", + "tag": "@microsoft/rush_v5.169.0", + "date": "Thu, 19 Feb 2026 00:05:11 GMT", + "comments": { + "none": [ + { + "comment": "Sort the `additionalFilesForOperation` property in operation settings entries in projects' `config/rush-project.json` files before computing operation hashes to produce a stable hash for caching." + }, + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs` and DTS is now under `lib-dts`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + }, + { + "comment": "Add a new \"omitAppleDoubleFilesFromBuildCache\" experiment. When enabled, the Rush build cache will omit macOS AppleDouble metadata files (._*) from cache archives when a companion file exists in the same directory. This prevents platform-specific metadata files from polluting the shared build cache. The exclusion only applies when running on macOS." + }, + { + "comment": "Add a new `dependsOnNodeVersion` setting for operation entries in rush-project.json. When enabled, the Node.js version is included in the build cache hash, ensuring that cached outputs are invalidated when the Node.js version changes. Accepts `true` (alias for `\"patch\"`), `\"major\"`, `\"minor\"`, or `\"patch\"` to control the granularity of version matching." + } + ] + } + }, + { + "version": "5.168.0", + "tag": "@microsoft/rush_v5.168.0", + "date": "Thu, 12 Feb 2026 23:01:10 GMT", + "comments": { + "none": [ + { + "comment": "Add named exports to support named imports to `@rushstack/rush-sdk`." + }, + { + "comment": "Fix `rush change --verify` to ignore version-only changes in package.json files and changes to CHANGELOG.md and CHANGELOG.json files, preventing false positives after `rush version --bump` updates package versions and changelogs." + } + ] + } + }, + { + "version": "5.167.0", + "tag": "@microsoft/rush_v5.167.0", + "date": "Thu, 05 Feb 2026 00:24:16 GMT", + "comments": { + "none": [ + { + "comment": "Add support for `rush-pnpm approve-builds` command to persist `globalOnlyBuiltDependencies` in pnpm-config.json" + }, + { + "comment": "Filter npm-incompatible properties from .npmrc when npm is used with a configuration intended for pnpm or yarn, to eliminate spurious warnings during package manager installation." + }, + { + "comment": "Fix a longstanding issue where a package.json script could hang on Windows if it accessed STDIN under certain circumstances" + }, + { + "comment": "Upgrade tar dependency from 6.2.1 to 7.5.6 to fix security vulnerability GHSA-8qq5-rm4j-mr97" + } + ] + } + }, + { + "version": "5.166.0", + "tag": "@microsoft/rush_v5.166.0", + "date": "Mon, 12 Jan 2026 23:39:06 GMT", + "comments": { + "none": [ + { + "comment": "Add support for Node 24 and bump the `rush init` template to default to Node 24." + }, + { + "comment": "Remove use of the deprecated `shell: true` option in process spawn operations." + } + ] + } + }, + { + "version": "5.165.0", + "tag": "@microsoft/rush_v5.165.0", + "date": "Mon, 29 Dec 2025 22:43:14 GMT", + "comments": { + "none": [ + { + "comment": "Forward the `parameterNamesToIgnore` `/config/rush-project.json` property to child processes via a `RUSHSTACK_CLI_IGNORED_PARAMETER_NAMES` environment variable" + }, + { + "comment": "Fix an issue where `rush update` will error complaining that the shrinkwrap file hasn't been updated to support workspaces in a subspace with no projects." + }, + { + "comment": "Fix an issue where packages listed in the `pnpmLockfilePolicies.disallowInsecureSha1.exemptPackageVersions` `common/config/rush/pnpm-config.json` config file are not exempted in PNPM 9." + }, + { + "comment": "Add support for the `globalOnlyBuiltDependencies` PNPM 10.x option to specify an allowlist of packages permitted to run build scripts to `common/config/rush/pnpm-config.json`." + }, + { + "comment": "Upgrade `pnpm-sync-lib` to v0.3.3 for pnpm v10 compatibility" + }, + { + "comment": "Change the Git hook file shebangs to use `/usr/bin/env bash` instead of `/bin/bash` for greater platform compatability." + } + ] + } + }, + { + "version": "5.164.0", + "tag": "@microsoft/rush_v5.164.0", + "date": "Tue, 16 Dec 2025 21:49:00 GMT", + "comments": { + "minor": [ + { + "comment": "Hash full shrinkwrap entry to detect sub-dependency resolution changes" + } + ], + "none": [ + { + "comment": "Fix an issue where ProjectChangeAnalyzer checked the pnpm-lock.yaml file in the default subspace only, when it should consider all subspaces." + }, + { + "comment": "Log a warning if Git-tracked symbolic links are encountered during repo state analysis." + }, + { + "comment": "Add support for defining pnpm catalog config." + } + ] + } + }, + { + "version": "5.163.0", + "tag": "@microsoft/rush_v5.163.0", + "date": "Tue, 25 Nov 2025 17:04:05 GMT", + "comments": { + "minor": [ + { + "comment": "Added the ability to select projects via path, e.g. `rush build --to path:./my-project` or `rush build --only path:/some/absolute/path`" + }, + { + "comment": "Add project-level parameter ignoring to prevent unnecessary cache invalidation. Projects can now use \"parameterNamesToIgnore\" in \"rush-project.json\" to exclude custom command-line parameters that don't affect their operations." + } + ], + "none": [ + { + "comment": "Extract CredentialCache API out into \"@rushstack/credential-cache\". Reference directly in plugins to avoid pulling in all of \"@rushstack/rush-sdk\" unless necessary." + }, + { + "comment": "Add subspaceName to the output of the `rush list` command" + } + ] + } + }, + { + "version": "5.162.0", + "tag": "@microsoft/rush_v5.162.0", + "date": "Sat, 18 Oct 2025 00:06:36 GMT", + "comments": { + "none": [ + { + "comment": "Fork npm-check to address npm audit CVE" + } + ] + } + }, + { + "version": "5.161.0", + "tag": "@microsoft/rush_v5.161.0", + "date": "Fri, 17 Oct 2025 23:22:50 GMT", + "comments": { + "none": [ + { + "comment": "Add an `allowOversubscription` option to the command definitions in `common/config/rush/command-line.json` to prevent running tasks from exceeding concurrency." + }, + { + "comment": "Add support for PNPM's minimumReleaseAge setting to help mitigate supply chain attacks" + }, + { + "comment": "Enable prerelease version matching in bridge-package command" + }, + { + "comment": "Fix an issue where `rush add --make-consistent ...` may drop the `implicitlyPreferredVersions` and `ensureConsistentVersions` properties from `common/config/rush/common-versions.json`." + }, + { + "comment": "Treat intermittent ignored redis errors as warnings and allow build to continue." + } + ] + } + }, + { + "version": "5.160.1", + "tag": "@microsoft/rush_v5.160.1", + "date": "Fri, 03 Oct 2025 22:25:25 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue with validation of the `pnpm-lock.yaml` `packageExtensionsChecksum` field in pnpm v10." + } + ] + } + }, + { + "version": "5.160.0", + "tag": "@microsoft/rush_v5.160.0", + "date": "Fri, 03 Oct 2025 20:10:21 GMT", + "comments": { + "none": [ + { + "comment": "Bump the default Node and `pnpm` versions in the `rush init` template." + }, + { + "comment": "Fix an issue with validation of the `pnpm-lock.yaml` `packageExtensionsChecksum` field in pnpm v10." + }, + { + "comment": "Fix an issue where the `$schema` property is dropped from `common/config/rush/pnpm-config.json` when running `rush-pnpm patch-commit ...`" + } + ], + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ] + } + }, + { + "version": "5.159.0", + "tag": "@microsoft/rush_v5.159.0", + "date": "Fri, 03 Oct 2025 00:50:08 GMT", + "comments": { + "none": [ + { + "comment": "Fix to allow Bridge Cache plugin be installed but not used when build cache disabled; add cache key to terminal logs" + }, + { + "comment": "Add `IOperationExecutionResult.problemCollector` API which matches and collects VS Code style problem matchers" + }, + { + "comment": "Replace uuid package dependency with Node.js built-in crypto.randomUUID" + }, + { + "comment": "[rush-resolver-cache] Ensure that the correct version of rush-lib is loaded when the global version doesn't match the repository version." + }, + { + "comment": "Upgraded `js-yaml` dependency" + }, + { + "comment": "Enhance logging for IPC mode by allowing IPC runners to report detailed reasons for rerun, e.g. specific changed files." + }, + { + "comment": "Support aborting execution in phased commands. The CLI allows aborting via the \"a\" key in watch mode, and it is available to plugin authors for more advanced scenarios." + }, + { + "comment": "[rush-serve-plugin] Support aborting execution via Web Socket. Include information about the dependencies of operations in messages to the client.." + }, + { + "comment": "Add a logging message after the 'Trying to find \"tar\" binary' message when the binary is found." + }, + { + "comment": "Upgrade inquirer to 8.2.7 in rush-lib" + }, + { + "comment": "Bump \"express\" to 4.21.1 to address reported vulnerabilities in 4.20.0." + } + ], + "patch": [ + { + "comment": "[rush-azure-storage-build-cache-plugin] Trim access token output in AdoCodespacesAuthCredential" + } + ] + } + }, + { + "version": "5.158.1", + "tag": "@microsoft/rush_v5.158.1", + "date": "Fri, 29 Aug 2025 00:08:18 GMT", + "comments": { + "none": [ + { + "comment": "Deduplicate parsing of dependency specifiers." + }, + { + "comment": "Optimize detection of local projects when collecting implicit preferred versions." + }, + { + "comment": "Dedupe shrinkwrap parsing by content hash." + }, + { + "comment": "[resolver-cache] Use shrinkwrap hash to skip resolver cache regeneration." + } + ] + } + }, + { + "version": "5.158.0", + "tag": "@microsoft/rush_v5.158.0", + "date": "Tue, 26 Aug 2025 23:27:47 GMT", + "comments": { + "none": [ + { + "comment": "Adds an optional safety check flag to the Bridge Cache plugin write action." + }, + { + "comment": "Fix a bug in \"@rushstack/rush-bridge-cache-plugin\" where the cache replay did not block the normal execution process and instead was a floating promise." + }, + { + "comment": "[resolver-cache-plugin] Optimize search for nested package.json files with persistent cache file keyed by integrity hash." + }, + { + "comment": "[rush-serve-plugin] Allow the Rush process to exit if the server is the only active handle." + }, + { + "comment": "Fix poor performance scaling during `rush install` when identifying projects in the lockfile that no longer exist." + }, + { + "comment": "[resolver-cache-plugin] Improve performance of scan for nested package.json files in external packages." + }, + { + "comment": "Optimize `setPreferredVersions` in install setup." + }, + { + "comment": "Ensure that `rush version` and `rush publish` preserve all fields in `version-policies-json`." + } + ] + } + }, + { + "version": "5.157.0", + "tag": "@microsoft/rush_v5.157.0", + "date": "Fri, 25 Jul 2025 01:24:42 GMT", + "comments": { + "none": [ + { + "comment": "Improve performance for publishing on filtered clones." + } + ] + } + }, + { + "version": "5.156.0", + "tag": "@microsoft/rush_v5.156.0", + "date": "Wed, 23 Jul 2025 20:56:15 GMT", + "comments": { + "none": [ + { + "comment": "Include \"parallelism\" in phased operation execution context. Update \"rush-bridge-cache-plugin\" to support both cache read and cache write, selectable via command line choice parameter. Fixes an issue that the options schema for \"rush-bridge-cache-plugin\" was invalid." + }, + { + "comment": "Add support for `RUSH_BUILD_CACHE_OVERRIDE_JSON` environment variable that takes a JSON string with the same format as the `common/config/build-cache.json` file and a `RUSH_BUILD_CACHE_OVERRIDE_JSON_FILE_PATH` environment variable that takes a file path that can be used to override the build cache configuration that is normally provided by that file." + }, + { + "comment": "Add support for setting environment variables via `/.env` and `~/.rush-user/.env` files." + }, + { + "comment": "[azure-storage-build-cache] Update build-cache.json schema to allow the full range of `loginFlow` options supported by the underlying authentication provider. Add `loginFlowFailover` option to customize fallback sequencing." + }, + { + "comment": "Add performance measures around various operations, include performance entries in telemetry payload." + }, + { + "comment": "Do not run afterExecuteOperation if the operation has not actually completed." + } + ] + } + }, + { + "version": "5.155.1", + "tag": "@microsoft/rush_v5.155.1", + "date": "Fri, 27 Jun 2025 19:57:04 GMT", + "comments": { + "none": [ + { + "comment": "Fix pnpm-sync caused .modules.yaml ENOENT during install" + } + ] + } + }, + { + "version": "5.155.0", + "tag": "@microsoft/rush_v5.155.0", + "date": "Fri, 13 Jun 2025 16:10:38 GMT", + "comments": { + "none": [ + { + "comment": "Add support for PNPM v9 to the pnpm-sync feature." + } + ] + } + }, + { + "version": "5.154.0", + "tag": "@microsoft/rush_v5.154.0", + "date": "Tue, 10 Jun 2025 18:45:59 GMT", + "comments": { + "none": [ + { + "comment": "Introduce a `@rushstack/rush-bridge-cache-plugin` package that adds a `--set-cache-only` flag to phased commands, which sets the cache entry without performing the operation." + }, + { + "comment": "Update the `CredentialCache` options object to add support for custom cache file paths. This is useful if `CredentialCache` is used outside of Rush." + }, + { + "comment": "PNPMv10 support: SHA256 hashing for dependencies paths lookup" + }, + { + "comment": "Add Linux/MacOS support for new 'virtual-store-dir-max-length'" + } + ] + } + }, + { + "version": "5.153.2", + "tag": "@microsoft/rush_v5.153.2", + "date": "Tue, 13 May 2025 20:33:12 GMT", + "comments": { + "none": [ + { + "comment": "Fix path parsing issue when running rush bridge-package" + }, + { + "comment": "Operations that were cobuilt now have the cobuild time correctly reflected across all agents." + }, + { + "comment": "Add `hasUncommittedChanges` to `IInputSnapshot` for use by plugins." + } + ] + } + }, + { + "version": "5.153.1", + "tag": "@microsoft/rush_v5.153.1", + "date": "Fri, 25 Apr 2025 01:12:48 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue with implicit phase expansion when `--include-phase-deps` is not specified." + }, + { + "comment": "Upgrade `rushstack/heft-config-file` to fix an incompatibility with Node 16" + } + ] + } + }, + { + "version": "5.153.0", + "tag": "@microsoft/rush_v5.153.0", + "date": "Thu, 17 Apr 2025 21:59:15 GMT", + "comments": { + "none": [ + { + "comment": "Update documentation for `extends`" + }, + { + "comment": "Bind \"q\" to gracefully exit the watcher." + }, + { + "comment": "Clarify registry authentication settings in \"rush init\" template for .npmrc" + }, + { + "comment": "Support the `--changed-projects-only` flag in watch mode and allow it to be toggled between iterations." + }, + { + "comment": "Fix telemetry for \"--changed-projects-only\" when toggled in watch mode." + }, + { + "comment": "(rush-serve-plugin) Support websocket message to enable/disable operations." + } + ] + } + }, + { + "version": "5.152.0", + "tag": "@microsoft/rush_v5.152.0", + "date": "Tue, 08 Apr 2025 18:41:27 GMT", + "comments": { + "none": [ + { + "comment": "Add `ChainedCredential` to `AzureAuthenticationBase` to handle auth failover." + }, + { + "comment": "Add support for developer tools credentials to the Azure build cache." + }, + { + "comment": "Add a new CLI flag `--debug-build-cache-ids` to help with root-causing unexpected cache misses." + }, + { + "comment": "Sort all operations lexicographically by name for reporting purposes." + }, + { + "comment": "(EXPERIMENTAL) Add new commands `rush link-package` and `rush bridge-package`" + } + ] + } + }, + { + "version": "5.151.0", + "tag": "@microsoft/rush_v5.151.0", + "date": "Tue, 25 Mar 2025 16:58:46 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where `--include-phase-deps` and watch mode sometimes included operations that were not required" + }, + { + "comment": "Fix an issue where build/rebuild can not be defined in a rush plugin command line configuration" + }, + { + "comment": "Use `useNodeJSResolver: true` in `Import.resolvePackage` calls." + }, + { + "comment": "Add missing `./package.json` export; revert `useNodeJSResolver: true`." + }, + { + "comment": "(plugin-api) Guaranteed `operation.associatedPhase` and `operation.associatedProject` are not undefined." + } + ] + } + }, + { + "version": "5.150.0", + "tag": "@microsoft/rush_v5.150.0", + "date": "Thu, 27 Feb 2025 17:41:59 GMT", + "comments": { + "none": [ + { + "comment": "Add an `--include-phase-deps` switch that expands an unsafe project selection to include its phase dependencies" + } + ] + } + }, + { + "version": "5.149.1", + "tag": "@microsoft/rush_v5.149.1", + "date": "Wed, 19 Feb 2025 18:54:06 GMT", + "comments": { + "none": [ + { + "comment": "Remove the unused `RushConstants.rushAlertsStateFilename` property." + }, + { + "comment": "Bump `jsonpath-plus` to `~10.3.0`." + } + ] + } + }, + { + "version": "5.149.0", + "tag": "@microsoft/rush_v5.149.0", + "date": "Wed, 12 Feb 2025 04:07:30 GMT", + "comments": { + "none": [ + { + "comment": "Prefer `os.availableParallelism()` to `os.cpus().length`." + }, + { + "comment": "Add a new command line parameter `--node-diagnostic-dir=DIR` to phased commands that, when specified, tells all child build processes to write NodeJS diagnostics into `${DIR}/${packageName}/${phaseIdentifier}`. This is useful if `--cpu-prof` or `--heap-prof` are enabled, to avoid polluting workspace folders." + }, + { + "comment": "Add a new phased command hook `createEnvironmentForOperation` that can be used to customize the environment variables passed to individual operation subprocesses. This may be used to, for example, customize `NODE_OPTIONS` to pass `--diagnostic-dir` or other such parameters." + }, + { + "comment": "Allow --timeline option for all phased commands" + }, + { + "comment": "Fix support for \"ensureConsistentVersions\" in common-versions.json when subspaces features is not enabled." + }, + { + "comment": "Fix an issue where the port parameter in `@rushstack/rush-serve-plugin` was allowed to be a string parameter." + } + ] + } + }, + { + "version": "5.148.0", + "tag": "@microsoft/rush_v5.148.0", + "date": "Fri, 10 Jan 2025 02:36:20 GMT", + "comments": { + "none": [ + { + "comment": "Add a configuration option to avoid manually configuring decoupledLocalDependencies across subspaces." + }, + { + "comment": "Improve some `rush-sdk` APIs to support future work on GitHub issue #3994" + }, + { + "comment": "Fix an issue where MaxListenersExceeded would get thrown when using the HTTP build cache plugin" + } + ] + } + }, + { + "version": "5.147.2", + "tag": "@microsoft/rush_v5.147.2", + "date": "Mon, 06 Jan 2025 21:48:43 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue with evaluation of `shouldEnsureConsistentVersions` when the value is not constant across subspaces or variants." + }, + { + "comment": "Fix an issue where the lockfile object has a nullish value causing yaml.dump to report an error." + } + ] + } + }, + { + "version": "5.147.1", + "tag": "@microsoft/rush_v5.147.1", + "date": "Thu, 26 Dec 2024 23:35:27 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue with the `enableSubpathScan` experiment where the set of returned hashes would result in incorrect build cache identifiers when using `--only`." + }, + { + "comment": "When a no-op operation is not in scope, reflect its result as no-op instead of skipped, so that downstream operations can still write to the build cache." + }, + { + "comment": "Allow injected dependencies without enabling subspaces." + } + ] + } + }, + { + "version": "5.147.0", + "tag": "@microsoft/rush_v5.147.0", + "date": "Thu, 12 Dec 2024 01:37:25 GMT", + "comments": { + "none": [ + { + "comment": "Add a new experiment flag `enableSubpathScan` that, when invoking phased script commands with project selection parameters, such as `--to` or `--from`, only hashes files that are needed to compute the cache ids for the selected projects." + } + ] + } + }, + { + "version": "5.146.0", + "tag": "@microsoft/rush_v5.146.0", + "date": "Tue, 10 Dec 2024 21:23:18 GMT", + "comments": { + "none": [ + { + "comment": "Support fallback syntax in `.npmrc` files if the package manager is PNPM. See https://pnpm.io/npmrc" + }, + { + "comment": "Add an `.isPnpm` property to `RushConfiguration` that is set to true if the package manager for the Rush repo is PNPM." + }, + { + "comment": "Support pnpm lockfile v9, which is used by default starting in pnpm v9." + } + ] + } + }, + { + "version": "5.145.0", + "tag": "@microsoft/rush_v5.145.0", + "date": "Tue, 10 Dec 2024 05:14:11 GMT", + "comments": { + "none": [ + { + "comment": "Upgrade `@azure/identity` and `@azure/storage-blob`." + }, + { + "comment": "Add support for Node 22." + }, + { + "comment": "Remove the dependency on node-fetch." + } + ] + } + }, + { + "version": "5.144.1", + "tag": "@microsoft/rush_v5.144.1", + "date": "Mon, 09 Dec 2024 20:32:01 GMT", + "comments": { + "none": [ + { + "comment": "Bump `jsonpath-plus` to `~10.2.0`." + } + ] + } + }, + { + "version": "5.144.0", + "tag": "@microsoft/rush_v5.144.0", + "date": "Wed, 04 Dec 2024 19:32:23 GMT", + "comments": { + "none": [ + { + "comment": "Remove the `node-fetch` dependency from `@rushstack/rush-http-build-cache-plugin`." + } + ] + } + }, + { + "version": "5.143.0", + "tag": "@microsoft/rush_v5.143.0", + "date": "Wed, 04 Dec 2024 03:07:08 GMT", + "comments": { + "none": [ + { + "comment": "Remove the `node-fetch` dependency from @rushstack/rush-amazon-s3-build-cache-plugin." + }, + { + "comment": "(BREAKING API CHANGE) Remove the exported `WebClient` API from @rushstack/rush-amazon-s3-build-cache-plugin." + } + ] + } + }, + { + "version": "5.142.0", + "tag": "@microsoft/rush_v5.142.0", + "date": "Tue, 03 Dec 2024 23:42:22 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where the ability to skip `rush install` may be incorrectly calculated when using the variants feature." + }, + { + "comment": "Add support for an `\"extends\"` property in the `common/config/rush/pnpm-config.json` and `common/config/subspace/*/pnpm-config.json` files." + }, + { + "comment": "Add warning when the `globalIgnoredOptionalDependencies` property is specified in `common/config/rush/pnpm-config.json` and the repo is configured to use pnpm <9.0.0." + } + ] + } + }, + { + "version": "5.141.4", + "tag": "@microsoft/rush_v5.141.4", + "date": "Mon, 02 Dec 2024 20:40:41 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where Rush sometimes incorrectly reported \"fatal: could not open 'packages/xxx/.rush/temp/shrinkwrap-deps.json' for reading: No such file or directory\" when using subspaces" + } + ] + } + }, + { + "version": "5.141.3", + "tag": "@microsoft/rush_v5.141.3", + "date": "Wed, 27 Nov 2024 07:16:50 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where Rush sometimes incorrectly reported \"The overrides settings doesn't match the current shrinkwrap\" when using subspaces" + }, + { + "comment": "Fix an issue where Rush sometimes incorrectly reported \"The package extension hash doesn't match the current shrinkwrap.\" when using subspaces" + } + ] + } + }, + { + "version": "5.141.2", + "tag": "@microsoft/rush_v5.141.2", + "date": "Wed, 27 Nov 2024 03:27:26 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where filtered installs neglected to install dependencies from other subspaces" + } + ] + } + }, + { + "version": "5.141.1", + "tag": "@microsoft/rush_v5.141.1", + "date": "Wed, 20 Nov 2024 00:24:34 GMT", + "comments": { + "none": [ + { + "comment": "Update schema for build-cache.json to include recent updates to the @rushstack/rush-azure-storage-build-cache-plugin." + } + ] + } + }, + { + "version": "5.141.0", + "tag": "@microsoft/rush_v5.141.0", + "date": "Tue, 19 Nov 2024 06:38:33 GMT", + "comments": { + "none": [ + { + "comment": "Adds two new properties to the configuration for `rush-azure-storage-build-cache-plugin`: `loginFlow` selects the flow to use for interactive authentication to Entra ID, and `readRequiresAuthentication` specifies that a SAS token is required for read and therefore expired authentication is always fatal." + }, + { + "comment": "Adds a new `wasExecutedOnThisMachine` property to operation telemetry events, to simplify reporting about cobuilt operations." + }, + { + "comment": "Fix an issue where empty error logs were created for operations that did not write to standard error." + }, + { + "comment": "Fix an issue where incremental building (with LegacySkipPlugin) would not work when no-op operations were present in the process" + }, + { + "comment": "Fix lack of \"local-only\" option for cacheProvider in build-cache.schema.json" + }, + { + "comment": "Fix an issue where if an Operation wrote all logs to stdout, then exited with a non-zero exit code, only the non-zero exit code would show up in the summary." + } + ] + } + }, + { + "version": "5.140.1", + "tag": "@microsoft/rush_v5.140.1", + "date": "Wed, 30 Oct 2024 21:50:51 GMT", + "comments": { + "none": [ + { + "comment": "Update the `jsonpath-plus` indirect dependency to mitigate CVE-2024-21534." + } + ] + } + }, + { + "version": "5.140.0", + "tag": "@microsoft/rush_v5.140.0", + "date": "Tue, 22 Oct 2024 23:59:54 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue when using `rush deploy` where the `node_modules/.bin` folder symlinks were not created for deployed packages when using the \"default\" link creation mode" + }, + { + "comment": "Add support for the `globalIgnoredOptionalDependencies` field in the `common/config/rush/pnpm-config.json` file to allow specifying optional dependencies that should be ignored by PNPM" + } + ] + } + }, + { + "version": "5.139.0", + "tag": "@microsoft/rush_v5.139.0", + "date": "Thu, 17 Oct 2024 20:37:39 GMT", + "comments": { + "none": [ + { + "comment": "Allow rush plugins to extend build cache entries by writing additional files to the metadata folder. Expose the metadata folder path to plugins." + }, + { + "comment": "[CACHE BREAK] Alter the computation of build cache IDs to depend on the graph of operations in the build and therefore account for multiple phases, rather than only the declared dependencies. Ensure that `dependsOnEnvVars` and command line parameters that affect upstream phases impact the cache IDs of downstream operations." + }, + { + "comment": "(BREAKING CHANGE) Replace use of `ProjectChangeAnalyzer` in phased command hooks with a new `InputsSnapshot` data structure that is completely synchronous and does not perform any disk operations. Perform all disk operations and state computation prior to executing the build graph." + }, + { + "comment": "Add a new property `enabled` to `Operation` that when set to false, will cause the execution engine to immediately return `OperationStatus.Skipped` instead of invoking the runner. Use this property to disable operations that are not intended to be executed in the current pass, e.g. those that did not contain changes in the most recent watch iteration, or those excluded by `--only`." + }, + { + "comment": "Add an optional property `cacheHashSalt` to `build-cache.json` to allow repository maintainers to globally force a hash change in build cache entries." + } + ] + } + }, + { + "version": "5.138.0", + "tag": "@microsoft/rush_v5.138.0", + "date": "Thu, 03 Oct 2024 22:31:07 GMT", + "comments": { + "none": [ + { + "comment": "Changes the behavior of phased commands in watch mode to, when running a phase `_phase:` in all iterations after the first, prefer a script entry named `_phase::incremental` if such a script exists. The build cache will expect the outputs from the corresponding `_phase:` script (with otherwise the same inputs) to be equivalent when looking for a cache hit." + } + ] + } + }, + { + "version": "5.137.0", + "tag": "@microsoft/rush_v5.137.0", + "date": "Thu, 03 Oct 2024 19:46:40 GMT", + "comments": { + "patch": [ + { + "comment": "Expose `getChangesByProject` to allow classes that extend ProjectChangeAnalyzer to override file change analysis" + } + ] + } + }, + { + "version": "5.136.1", + "tag": "@microsoft/rush_v5.136.1", + "date": "Thu, 26 Sep 2024 22:59:11 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where the `--variant` parameter was missing from a phased command when the command's `alwaysInstall` property was set to `true`." + } + ] + } + }, + { + "version": "5.136.0", + "tag": "@microsoft/rush_v5.136.0", + "date": "Thu, 26 Sep 2024 21:48:00 GMT", + "comments": { + "none": [ + { + "comment": "Bring back the Variants feature that was removed in https://github.com/microsoft/rushstack/pull/4538." + }, + { + "comment": "Bump express dependency to 4.20.0" + } + ] + } + }, + { + "version": "5.135.0", + "tag": "@microsoft/rush_v5.135.0", + "date": "Fri, 20 Sep 2024 20:23:40 GMT", + "comments": { + "none": [ + { + "comment": "Fix a bug that caused rush-resolver-cache-plugin to crash on Windows." + }, + { + "comment": "Make individual Rush log files available via the rush-serve-plugin server at the relative URL specified by \"logServePath\" option. Annotate operations sent over the WebSocket with the URLs of their log files." + }, + { + "comment": "Adds a new experiment 'allowCobuildWithoutCache' for cobuilds to allow uncacheable operations to benefit from cobuild orchestration without using the build cache." + }, + { + "comment": "Deprecate the `sharding.shardOperationSettings` property in the project `config/rush-project.json` in favor of an `operationSettings` entry for an operation with a suffix of `:shard`." + } + ] + } + }, + { + "version": "5.134.0", + "tag": "@microsoft/rush_v5.134.0", + "date": "Fri, 13 Sep 2024 01:02:46 GMT", + "comments": { + "none": [ + { + "comment": "Always update shrinkwrap when `globalPackageExtensions` in `common/config/rush/pnpm-config.json` has been changed." + }, + { + "comment": "Pass the initialized credentials cache to `AzureAuthenticationBase._getCredentialFromTokenAsync` in `@rushstack/rush-azure-storage-build-cache-plugin`." + }, + { + "comment": "Support the `rush-pnpm patch-remove` command." + } + ] + } + }, + { + "version": "5.133.4", + "tag": "@microsoft/rush_v5.133.4", + "date": "Sat, 07 Sep 2024 00:18:08 GMT", + "comments": { + "none": [ + { + "comment": "Mark `AzureAuthenticationBase._credentialCacheId` as protected in `@rushstack/rush-azure-storage-build-cache-plugin`." + } + ] + } + }, + { + "version": "5.133.3", + "tag": "@microsoft/rush_v5.133.3", + "date": "Thu, 29 Aug 2024 22:49:36 GMT", + "comments": { + "none": [ + { + "comment": "Fix Windows compatibility for `@rushstack/rush-resolver-cache-plugin`." + } + ] + } + }, + { + "version": "5.133.2", + "tag": "@microsoft/rush_v5.133.2", + "date": "Wed, 28 Aug 2024 20:46:32 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where running `rush install --resolution-only` followed by `rush install` would not actually install modules." + } + ] + } + }, + { + "version": "5.133.1", + "tag": "@microsoft/rush_v5.133.1", + "date": "Wed, 28 Aug 2024 18:19:55 GMT", + "comments": { + "none": [ + { + "comment": "In rush-resolver-cache-plugin, include the base path in the resolver cache file." + }, + { + "comment": "Support `bundledDependencies` in rush-resolver-cache-plugin." + } + ] + } + }, + { + "version": "5.133.0", + "tag": "@microsoft/rush_v5.133.0", + "date": "Fri, 23 Aug 2024 00:40:08 GMT", + "comments": { + "none": [ + { + "comment": "Always update shrinkwrap when globalOverrides has been changed" + }, + { + "comment": "Add `afterInstall` plugin hook, which runs after any install finishes." + }, + { + "comment": "Add rush.json option \"suppressRushIsPublicVersionCheck\" to allow suppressing hardcoded calls to the npmjs.org registry." + } + ] + } + }, + { + "version": "5.132.0", + "tag": "@microsoft/rush_v5.132.0", + "date": "Wed, 21 Aug 2024 16:25:07 GMT", + "comments": { + "none": [ + { + "comment": "Add a new `rush install-autoinstaller` command that ensures that the specified autoinstaller is installed." + }, + { + "comment": "Emit an error if a `workspace:` specifier is used in a dependency that is listed in `decoupledLocalDependencies`." + }, + { + "comment": "Add support for `--resolution-only` to `rush install` to enforce strict peer dependency resolution." + } + ] + } + }, + { + "version": "5.131.5", + "tag": "@microsoft/rush_v5.131.5", + "date": "Mon, 19 Aug 2024 20:03:03 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where PreferredVersions are ignored when a project contains an overlapping dependency entry (https://github.com/microsoft/rushstack/issues/3205)" + } + ] + } + }, + { + "version": "5.131.4", + "tag": "@microsoft/rush_v5.131.4", + "date": "Sun, 11 Aug 2024 05:02:05 GMT", + "comments": { + "none": [ + { + "comment": "Revert a breaking change in Rush 5.131.3 where pnpm patches were moved from `common/pnpm-patches` to `common/config/rush/pnpm-patches`." + } + ] + } + }, + { + "version": "5.131.3", + "tag": "@microsoft/rush_v5.131.3", + "date": "Sat, 10 Aug 2024 02:27:14 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where `rush-pnpm patch-commit` would not correctly resolve patch files when the subspaces feature is enabled." + } + ] + } + }, + { + "version": "5.131.2", + "tag": "@microsoft/rush_v5.131.2", + "date": "Thu, 08 Aug 2024 23:38:18 GMT", + "comments": { + "none": [ + { + "comment": "Include a missing dependency in `@rushstack/rush-sdk`." + } + ] + } + }, + { + "version": "5.131.1", + "tag": "@microsoft/rush_v5.131.1", + "date": "Thu, 08 Aug 2024 22:08:41 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where rush-sdk can't be bundled by a consuming package." + }, + { + "comment": "Extract LookupByPath to @rushstack/lookup-by-path and load it from there." + } + ] + } + }, + { + "version": "5.131.0", + "tag": "@microsoft/rush_v5.131.0", + "date": "Fri, 02 Aug 2024 17:26:59 GMT", + "comments": { + "none": [ + { + "comment": "Improve Rush alerts with a new \"rush alert\" command and snooze feature" + } + ] + } + }, + { + "version": "5.130.3", + "tag": "@microsoft/rush_v5.130.3", + "date": "Wed, 31 Jul 2024 23:30:13 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where Rush does not detect an outdated lockfile if the `dependenciesMeta` `package.json` field is edited." + }, + { + "comment": "Include CHANGELOG.md in published releases again" + }, + { + "comment": "Fix a bug that caused the build cache to close its terminal writer before execution on error." + } + ] + } + }, + { + "version": "5.130.2", + "tag": "@microsoft/rush_v5.130.2", + "date": "Fri, 19 Jul 2024 03:41:44 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where `rush-pnpm patch-commit` did not work correctly when subspaces are enabled." + } + ] + } + }, + { + "version": "5.130.1", + "tag": "@microsoft/rush_v5.130.1", + "date": "Wed, 17 Jul 2024 07:37:13 GMT", + "comments": { + "none": [ + { + "comment": "Fix a recent regression for `rush init`" + } + ] + } + }, + { + "version": "5.130.0", + "tag": "@microsoft/rush_v5.130.0", + "date": "Wed, 17 Jul 2024 06:55:27 GMT", + "comments": { + "none": [ + { + "comment": "(EXPERIMENTAL) Initial implementation of Rush alerts feature" + }, + { + "comment": "Adjusts how cobuilt operations are added and requeued to the operation graph. Removes the 'RemoteExecuting' status." + } + ] + } + }, + { + "version": "5.129.7", + "tag": "@microsoft/rush_v5.129.7", + "date": "Tue, 16 Jul 2024 04:16:56 GMT", + "comments": { + "none": [ + { + "comment": "Upgrade pnpm-sync-lib to fix an edge case when handling node_modules folder" + }, + { + "comment": "Don't interrupt the installation process if the user hasn't enabled the inject dependencies feature." + }, + { + "comment": "Improve `@rushtack/rush-sdk` and make it reuse `@microsoft/rush-lib` from rush global folder" + }, + { + "comment": "Remove the trailing slash in the `.DS_Store/` line in the `.gitignore` file generated by `rush init`. `.DS_Store` is a file, not a folder." + }, + { + "comment": "Support deep references to internal Apis" + }, + { + "comment": "Fix an issue where `rush add` would ignore the `ensureConsistentVersions` option if that option was set in `rush.json` instead of in `common/config/rush/common-versions.json`." + }, + { + "comment": "Fix an issue where running `rush add` in a project can generate a `package.json` file that uses JSON5 syntax. Package managers expect strict JSON." + }, + { + "comment": "fix spelling of \"committing\" in rush.json init template and schema" + } + ] + } + }, + { + "version": "5.129.6", + "tag": "@microsoft/rush_v5.129.6", + "date": "Thu, 27 Jun 2024 00:44:32 GMT", + "comments": { + "none": [ + { + "comment": "Fix an edge case for workspace peer dependencies when calculating packageJsonInjectedDependenciesHash to improve its accuracy " + }, + { + "comment": "Update a URL in the `.pnpmfile.cjs` generated by `rush init`." + } + ] + } + }, + { + "version": "5.129.5", + "tag": "@microsoft/rush_v5.129.5", + "date": "Tue, 25 Jun 2024 20:13:29 GMT", + "comments": { + "none": [ + { + "comment": "Don't include package.json version field when calculating packageJsonInjectedDependenciesHash" + } + ] + } + }, + { + "version": "5.129.4", + "tag": "@microsoft/rush_v5.129.4", + "date": "Mon, 24 Jun 2024 23:49:10 GMT", + "comments": { + "none": [ + { + "comment": "Normalize the file permissions (644) for Rush plugin files that are committed to Git" + } + ] + } + }, + { + "version": "5.129.3", + "tag": "@microsoft/rush_v5.129.3", + "date": "Fri, 21 Jun 2024 00:15:54 GMT", + "comments": { + "none": [ + { + "comment": "Fixed an issue where DependencyAnalyzer caches the same analysis for all subspaces" + } + ] + } + }, + { + "version": "5.129.2", + "tag": "@microsoft/rush_v5.129.2", + "date": "Wed, 19 Jun 2024 23:59:09 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where the `rush pnpm ...` command always terminates with an exit code of 1." + } + ] + } + }, + { + "version": "5.129.1", + "tag": "@microsoft/rush_v5.129.1", + "date": "Wed, 19 Jun 2024 04:20:03 GMT", + "comments": { + "none": [ + { + "comment": "Add logic to remove outdated .pnpm-sync.json files during rush install or update" + } + ] + } + }, + { + "version": "5.129.0", + "tag": "@microsoft/rush_v5.129.0", + "date": "Wed, 19 Jun 2024 03:31:48 GMT", + "comments": { + "none": [ + { + "comment": "Add a new `init-subspace` command to initialize a new subspace." + }, + { + "comment": "Move the `ensureConsistentVersions` setting from `rush.json` to `common/config/rush/common-versions.json`, or to `common/config/rush//common-versions.json` if subspaces are enabled." + } + ] + } + }, + { + "version": "5.128.5", + "tag": "@microsoft/rush_v5.128.5", + "date": "Tue, 18 Jun 2024 04:02:54 GMT", + "comments": { + "none": [ + { + "comment": "Fix a key collision for cobuild clustering for operations that share the same phase name." + } + ] + } + }, + { + "version": "5.128.4", + "tag": "@microsoft/rush_v5.128.4", + "date": "Mon, 17 Jun 2024 23:22:49 GMT", + "comments": { + "none": [ + { + "comment": "Bump the `@azure/identity` package to `~4.2.1` to mitigate GHSA-m5vv-6r4h-3vj9." + } + ] + } + }, + { + "version": "5.128.3", + "tag": "@microsoft/rush_v5.128.3", + "date": "Mon, 17 Jun 2024 20:46:21 GMT", + "comments": { + "none": [ + { + "comment": "Fixed an issue where the --make-consistent flag would affect projects outside the current subspace." + } + ] + } + }, + { + "version": "5.128.2", + "tag": "@microsoft/rush_v5.128.2", + "date": "Mon, 17 Jun 2024 17:08:00 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where rush-pnpm patch is not working for the subspace scenario" + }, + { + "comment": "Fix an issue where rush update can not detect package.json changes in other subspaces for the injected installation case" + } + ] + } + }, + { + "version": "5.128.1", + "tag": "@microsoft/rush_v5.128.1", + "date": "Wed, 12 Jun 2024 20:07:44 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where running `rush install` in a subspace with only a `--from` selector is treated as selecting all projects." + }, + { + "comment": "Fix an issue where not published packages are not correctly identified as not published when querying a package feed under certain versions of NPM." + }, + { + "comment": "Fix an issue where selection syntax (like `--to` or `--from`) misses project dependencies declared using workspace alias syntax (i.e. - `workspace:alias@1.2.3`)." + }, + { + "comment": "Fix an issue where an error is thrown if a Git email address isn't configured and email validation isn't configured in `rush.json` via `allowedEmailRegExps`." + }, + { + "comment": "Display the name of the subspace when an error is emitted because a dependency hash uses the SHA1 algorithm and the \"disallowInsecureSha1\" option is enabled." + } + ] + } + }, + { + "version": "5.128.0", + "tag": "@microsoft/rush_v5.128.0", + "date": "Fri, 07 Jun 2024 22:59:12 GMT", + "comments": { + "none": [ + { + "comment": "Graduate the `phasedCommands` experiment to a standard feature." + }, + { + "comment": "Improve `rush init` template for `.gitignore`" + }, + { + "comment": "Remove an unnecessary condition in the logic for skipping operations when build cache is disabled." + } + ] + } + }, + { + "version": "5.127.1", + "tag": "@microsoft/rush_v5.127.1", + "date": "Thu, 06 Jun 2024 03:05:21 GMT", + "comments": { + "none": [ + { + "comment": "Remove the second instance of the project name from the project operation filenames in `/rush-logs`. This restores the log filenames to their format before Rush 5.125.0." + } + ] + } + }, + { + "version": "5.127.0", + "tag": "@microsoft/rush_v5.127.0", + "date": "Tue, 04 Jun 2024 00:44:18 GMT", + "comments": { + "none": [ + { + "comment": "Fixes build cache no-op and sharded operation clustering." + }, + { + "comment": "Updated common-veresions.json schema with ensureConsistentVersions property" + } + ] + } + }, + { + "version": "5.126.0", + "tag": "@microsoft/rush_v5.126.0", + "date": "Mon, 03 Jun 2024 02:49:05 GMT", + "comments": { + "none": [ + { + "comment": "Fixes a string schema validation warning message when running `rush deploy`." + }, + { + "comment": "Update the functionality that runs external lifecycle processes to be async." + }, + { + "comment": "Move logs into the project `rush-logs` folder regardless of whether or not the `\"phasedCommands\"` experiment is enabled." + }, + { + "comment": "Update the `nodeSupportedVersionRange` in the `rush init` template to the LTS and current Node versions." + }, + { + "comment": "Update the `pnpmVersion` in the `rush init` template to the latest version of pnpm 8." + }, + { + "comment": "Update the `.gitignore` in the `rush init` template to include some common toolchain output files and folders." + }, + { + "comment": "Include missing `type` modifiers on type-only exports." + } + ] + } + }, + { + "version": "5.125.1", + "tag": "@microsoft/rush_v5.125.1", + "date": "Wed, 29 May 2024 05:39:54 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where if `missingScriptBehavior` is set to `\"error\"` and a script is present and empty, an error would be thrown." + } + ] + } + }, + { + "version": "5.125.0", + "tag": "@microsoft/rush_v5.125.0", + "date": "Sat, 25 May 2024 05:12:20 GMT", + "comments": { + "none": [ + { + "comment": "Fixes a bug where no-op operations were treated as having build cache disabled." + }, + { + "comment": "Adds support for sharding operations during task execution." + }, + { + "comment": "Fix an issue where warnings and errors were not shown in the build summary for all cobuild agents." + }, + { + "comment": "Add a `rush check --subspace` parameter to specify which subspace to analyze" + }, + { + "comment": "Rename the subspace level lockfile from `.pnpmfile-subspace.cjs` to `.pnpmfile.cjs`. This is a breaking change for the experimental feature." + } + ] + } + }, + { + "version": "5.124.7", + "tag": "@microsoft/rush_v5.124.7", + "date": "Thu, 23 May 2024 02:27:13 GMT", + "comments": { + "none": [ + { + "comment": "Improve the `usePnpmSyncForInjectedDependencies` experiment to also include any dependency whose lockfile entry has the `file:` protocol, unless it is a tarball reference" + }, + { + "comment": "Fix an issue where the build cache analysis was incorrect in rare situations due to a race condition (GitHub #4711)" + } + ] + } + }, + { + "version": "5.124.6", + "tag": "@microsoft/rush_v5.124.6", + "date": "Thu, 16 May 2024 01:12:22 GMT", + "comments": { + "none": [ + { + "comment": "Fix an edge case for pnpm-sync when the .pnpm folder is absent but still a valid installation." + } + ] + } + }, + { + "version": "5.124.5", + "tag": "@microsoft/rush_v5.124.5", + "date": "Wed, 15 May 2024 23:43:15 GMT", + "comments": { + "none": [ + { + "comment": "Fix count of completed operations when silent operations are blocked. Add explicit message for child processes terminated by signals. Ensure that errors show up in summarized view." + }, + { + "comment": "Ensure that errors thrown in afterExecuteOperation show up in the summary at the end of the build." + } + ] + } + }, + { + "version": "5.124.4", + "tag": "@microsoft/rush_v5.124.4", + "date": "Wed, 15 May 2024 03:05:57 GMT", + "comments": { + "none": [ + { + "comment": "Improve the detection of PNPM lockfile versions." + }, + { + "comment": "Fix an issue where the `--subspace` CLI parameter would install for all subspaces in a monorepo when passed to the install or update action" + } + ] + } + }, + { + "version": "5.124.3", + "tag": "@microsoft/rush_v5.124.3", + "date": "Wed, 15 May 2024 01:18:25 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where `rush install` and `rush update` will fail with an `ENAMETOOLONG` error on Windows in repos with a large number of projects." + }, + { + "comment": "Fix an issue where installing multiple subspaces consecutively can cause unexpected cross-contamination between pnpmfiles." + } + ], + "patch": [ + { + "comment": "Ensure async telemetry tasks are flushed by error reporter" + } + ] + } + }, + { + "version": "5.124.2", + "tag": "@microsoft/rush_v5.124.2", + "date": "Fri, 10 May 2024 06:35:26 GMT", + "comments": { + "none": [ + { + "comment": "Fix a recent regression where `rush deploy` did not correctly apply the `additionalProjectsToInclude` setting (GitHub #4683)" + } + ] + } + }, + { + "version": "5.124.1", + "tag": "@microsoft/rush_v5.124.1", + "date": "Fri, 10 May 2024 05:33:51 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where the `disallowInsecureSha1` policy failed to parse certain lockfile entries" + }, + { + "comment": "Fix some minor issues with the \"rush init\" template files" + }, + { + "comment": "Report an error if subspacesFeatureEnabled=true without useWorkspaces=true" + }, + { + "comment": "Fix an issue where operation weights were not respected." + } + ] + } + }, + { + "version": "5.124.0", + "tag": "@microsoft/rush_v5.124.0", + "date": "Wed, 08 May 2024 22:24:08 GMT", + "comments": { + "none": [ + { + "comment": "Add a new setting `alwaysInjectDependenciesFromOtherSubspaces` in pnpm-config.json" + }, + { + "comment": "Fix a issue where rush install/update can not detect pnpm-sync.json is out of date" + }, + { + "comment": "Improve the error message when the pnpm-sync version is outdated" + }, + { + "comment": "Fixes a bug where cobuilds would cause a GC error when waiting for long periods of time." + }, + { + "comment": "Fix an issue where tab competions did not suggest parameter values." + } + ] + } + }, + { + "version": "5.123.1", + "tag": "@microsoft/rush_v5.123.1", + "date": "Tue, 07 May 2024 22:38:00 GMT", + "comments": { + "none": [ + { + "comment": "Fix a recent regression where \"rush install\" would sometimes incorrectly determine whether to skip the install" + } + ] + } + }, + { + "version": "5.123.0", + "tag": "@microsoft/rush_v5.123.0", + "date": "Tue, 07 May 2024 18:32:36 GMT", + "comments": { + "none": [ + { + "comment": "Provide the file path if there is an error parsing a `package.json` file." + }, + { + "comment": "Timeline view will now only show terminal build statuses as cobuilt, all other statuses will reflect their original icons." + }, + { + "comment": "Add a `\"weight\"` property to the `\"operation\"` object in the project `config/rush-project.json` file that defines an integer weight for how much of the allowed parallelism the operation uses." + }, + { + "comment": "Optimize skipping of unnecessary installs when using filters such as \"rush install --to x\"" + } + ] + } + }, + { + "version": "5.122.1", + "tag": "@microsoft/rush_v5.122.1", + "date": "Tue, 30 Apr 2024 23:36:50 GMT", + "comments": { + "none": [ + { + "comment": "Make `disallowInsecureSha1` policy a subspace-level configuration." + }, + { + "comment": "Fix an issue where `rush update` sometimes did not detect changes to pnpm-config.json" + } + ] + } + }, + { + "version": "5.122.0", + "tag": "@microsoft/rush_v5.122.0", + "date": "Thu, 25 Apr 2024 07:33:18 GMT", + "comments": { + "none": [ + { + "comment": "Support rush-pnpm for subspace feature" + }, + { + "comment": "Skip determining merge base if given git hash" + }, + { + "comment": "(BREAKING CHANGE) Improve the `disallowInsecureSha1` policy to support exemptions for certain package versions. This is a breaking change for the `disallowInsecureSha1` field in pnpm-config.json since Rush 5.119.0." + } + ] + } + }, + { + "version": "5.121.0", + "tag": "@microsoft/rush_v5.121.0", + "date": "Mon, 22 Apr 2024 19:11:26 GMT", + "comments": { + "none": [ + { + "comment": "Add support for auth via microsoft/ado-codespaces-auth vscode extension in `@rushstack/rush-azure-storage-build-cache-plugin`" + } + ] + } + }, + { + "version": "5.120.6", + "tag": "@microsoft/rush_v5.120.6", + "date": "Thu, 18 Apr 2024 23:20:02 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where \"rush deploy\" did not correctly deploy build outputs combining multiple Rush subspaces" + } + ] + } + }, + { + "version": "5.120.5", + "tag": "@microsoft/rush_v5.120.5", + "date": "Wed, 17 Apr 2024 21:58:17 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where rush add affects all packages in a subspace" + } + ] + } + }, + { + "version": "5.120.4", + "tag": "@microsoft/rush_v5.120.4", + "date": "Tue, 16 Apr 2024 20:04:25 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where `rush deploy` sometimes used an incorrect temp folder when the experimental subspaces feature is enabled" + } + ] + } + }, + { + "version": "5.120.3", + "tag": "@microsoft/rush_v5.120.3", + "date": "Tue, 16 Apr 2024 02:59:48 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where `pnpm-sync copy` was skipped when a build is restored from build cache." + }, + { + "comment": "Upgrade `tar` dependency to 6.2.1" + } + ] + } + }, + { + "version": "5.120.2", + "tag": "@microsoft/rush_v5.120.2", + "date": "Mon, 15 Apr 2024 00:25:04 GMT", + "comments": { + "none": [ + { + "comment": "Fixes an issue where rush install fails in monorepos with subspaces enabled" + } + ] + } + }, + { + "version": "5.120.1", + "tag": "@microsoft/rush_v5.120.1", + "date": "Sat, 13 Apr 2024 18:31:00 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where install-run-rush.js sometimes incorrectly invoked .cmd files on Windows OS due to a recent Node.js behavior change." + }, + { + "comment": "Fix an issue with the skip install logic when the experimental subspaces feature is enabled" + } + ] + } + }, + { + "version": "5.120.0", + "tag": "@microsoft/rush_v5.120.0", + "date": "Wed, 10 Apr 2024 21:59:57 GMT", + "comments": { + "none": [ + { + "comment": "Bump express." + }, + { + "comment": "Add support for `optionalDependencies` in transitive injected install in the Subspaces feature." + }, + { + "comment": "Update dependency: pnpm-sync-lib@0.2.2" + }, + { + "comment": "Remove a restriction where the repo root would not be found if the CWD is >10 directory levels deep." + }, + { + "comment": "Improve the error message that is printed in a repo using PNPM workspaces when a non-`workspace:` version is used for a project inside the repo." + }, + { + "comment": "Include a missing space in a logging message printed when running `rush add`." + }, + { + "comment": "Clarify the copyright notice emitted in common/scripts/*.js" + }, + { + "comment": "Fix an issue with loading of implicitly preferred versions when the experimental subspaces feature is enabled" + } + ] + } + }, + { + "version": "5.119.0", + "tag": "@microsoft/rush_v5.119.0", + "date": "Sat, 30 Mar 2024 04:32:31 GMT", + "comments": { + "none": [ + { + "comment": "Add a policy to forbid sha1 hashes in pnpm-lock.yaml." + }, + { + "comment": "(BREAKING API CHANGE) Refactor phased action execution to analyze the repo after the initial operations are created. This removes the `projectChangeAnalyzer` property from the context parameter passed to the `createOperations` hook." + } + ] + } + }, + { + "version": "5.118.7", + "tag": "@microsoft/rush_v5.118.7", + "date": "Thu, 28 Mar 2024 19:55:27 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where in the previous release, built-in plugins were not included." + } + ] + } + }, + { + "version": "5.118.6", + "tag": "@microsoft/rush_v5.118.6", + "date": "Wed, 27 Mar 2024 05:31:17 GMT", + "comments": { + "none": [ + { + "comment": "Symlinks are now generated for workspace projects in the temp folder when subspaces and splitWorkspaceCompatibility is enabled." + } + ] + } + }, + { + "version": "5.118.5", + "tag": "@microsoft/rush_v5.118.5", + "date": "Tue, 26 Mar 2024 19:58:40 GMT", + "comments": { + "none": [ + { + "comment": "Use pnpm-sync-lib logging APIs to customize the log message for pnpm-sync operations" + } + ] + } + }, + { + "version": "5.118.4", + "tag": "@microsoft/rush_v5.118.4", + "date": "Tue, 26 Mar 2024 02:39:06 GMT", + "comments": { + "none": [ + { + "comment": "Added warnings if there are .npmrc or .pnpmfile.cjs files in project folders after migrating to subspaces" + } + ] + } + }, + { + "version": "5.118.3", + "tag": "@microsoft/rush_v5.118.3", + "date": "Sat, 23 Mar 2024 01:41:10 GMT", + "comments": { + "none": [ + { + "comment": "Fix an edge case for computing the PNPM store path when the experimental subspaces feature is enabled" + } + ] + } + }, + { + "version": "5.118.2", + "tag": "@microsoft/rush_v5.118.2", + "date": "Fri, 22 Mar 2024 17:30:47 GMT", + "comments": { + "none": [ + { + "comment": "Fix bugs related to path operation in Windows OS for subspace feature" + } + ] + } + }, + { + "version": "5.118.1", + "tag": "@microsoft/rush_v5.118.1", + "date": "Thu, 21 Mar 2024 16:39:32 GMT", + "comments": { + "none": [ + { + "comment": "Support PNPM injected installation in Rush subspace feature" + } + ] + } + }, + { + "version": "5.118.0", + "tag": "@microsoft/rush_v5.118.0", + "date": "Wed, 20 Mar 2024 20:45:18 GMT", + "comments": { + "none": [ + { + "comment": "(BREAKING API CHANGE) Rename `AzureAuthenticationBase._getCredentialFromDeviceCodeAsync` to `AzureAuthenticationBase._getCredentialFromTokenAsync` in `@rushstack/rush-azure-storage-build-cache-plugin`. Adding support for InteractiveBrowserCredential." + } + ] + } + }, + { + "version": "5.117.10", + "tag": "@microsoft/rush_v5.117.10", + "date": "Wed, 20 Mar 2024 04:57:57 GMT", + "comments": { + "none": [ + { + "comment": "Improve the \"splitWorkspaceCompatibility\" setting to simulate hoisted dependencies when the experimental Rush subspaces feature is enabled" + } + ] + } + }, + { + "version": "5.117.9", + "tag": "@microsoft/rush_v5.117.9", + "date": "Tue, 12 Mar 2024 19:15:07 GMT", + "comments": { + "none": [ + { + "comment": "Add functionality to disable filtered installs for specific subspaces" + } + ] + } + }, + { + "version": "5.117.8", + "tag": "@microsoft/rush_v5.117.8", + "date": "Sat, 09 Mar 2024 01:11:16 GMT", + "comments": { + "none": [ + { + "comment": "Fixes a bug where the syncNpmrc function incorrectly uses the folder instead of the path" + } + ] + } + }, + { + "version": "5.117.7", + "tag": "@microsoft/rush_v5.117.7", + "date": "Fri, 08 Mar 2024 23:45:24 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where, when the experimental subspace feature is enabled, the subspace's \".npmrc\" file did not take precedence over \".npmrc-global\"." + } + ] + } + }, + { + "version": "5.117.6", + "tag": "@microsoft/rush_v5.117.6", + "date": "Thu, 07 Mar 2024 19:35:20 GMT", + "comments": { + "none": [ + { + "comment": "Fixes an issue where cobuilds would write success with warnings as successful cache entries." + } + ] + } + }, + { + "version": "5.117.5", + "tag": "@microsoft/rush_v5.117.5", + "date": "Wed, 06 Mar 2024 23:03:27 GMT", + "comments": { + "none": [ + { + "comment": "Add filtered installs for subspaces" + } + ] + } + }, + { + "version": "5.117.4", + "tag": "@microsoft/rush_v5.117.4", + "date": "Tue, 05 Mar 2024 21:15:26 GMT", + "comments": { + "none": [ + { + "comment": "Add support for subspace level scoped pnpm-config.json e.g. `common/config/subspaces/default/pnpm-config.json`" + } + ] + } + }, + { + "version": "5.117.3", + "tag": "@microsoft/rush_v5.117.3", + "date": "Tue, 05 Mar 2024 01:19:42 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where if a patch is removed from `common/pnpm-patches` after `rush install` had already been run with that patch present, pnpm would try to continue applying the patch." + }, + { + "comment": "Intercept the output printed by `rush-pnpm patch` to update the next step's instructions to run `rush-pnpm patch-commit ...` instead of `pnpm patch-commit ...`." + } + ] + } + }, + { + "version": "5.117.2", + "tag": "@microsoft/rush_v5.117.2", + "date": "Fri, 01 Mar 2024 23:12:43 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue with the experimental subspaces feature, where version checks incorrectly scanned irrelevant subspaces." + } + ] + } + }, + { + "version": "5.117.1", + "tag": "@microsoft/rush_v5.117.1", + "date": "Thu, 29 Feb 2024 07:34:31 GMT", + "comments": { + "none": [ + { + "comment": "Update \"rush init\" template to document the new build-cache.json constants" + }, + { + "comment": "Remove trailing slashes from `node_modules` and `jspm_packages` paths in the `.gitignore` file generated by `rush init`." + }, + { + "comment": "Introduce a `RushCommandLine` API that exposes an object representing the skeleton of the Rush command-line." + }, + { + "comment": "Fix an issue where, when the experimental subspaces feature was enabled, the lockfile validation would check irrelevant subspaces" + } + ] + } + }, + { + "version": "5.117.0", + "tag": "@microsoft/rush_v5.117.0", + "date": "Mon, 26 Feb 2024 21:39:36 GMT", + "comments": { + "none": [ + { + "comment": "Include the ability to add `[os]` and `[arch]` tokens to cache entry name patterns." + }, + { + "comment": "(BREAKING CHANGE) Remove the 'installation variants' feature and its related APIs, which have been superceded by the Subspaces feature." + }, + { + "comment": "Extract the \"rush.json\" filename to a constant as `RushConstants.rushJsonFilename`." + } + ] + } + }, + { + "version": "5.116.0", + "tag": "@microsoft/rush_v5.116.0", + "date": "Mon, 26 Feb 2024 20:04:02 GMT", + "comments": { + "none": [ + { + "comment": "Upgrade the `pnpm-sync-lib` dependency version." + }, + { + "comment": "Handle `workspace:~` and `workspace:^` wildcard specifiers when publishing. They remain as-is in package.json but get converted to `~${current}` and `^${current}` in changelogs." + }, + { + "comment": "Validate that the \"projectFolder\" and \"publishFolder\" fields in the \"projects\" list in \"rush.json\" are normalized POSIX relative paths that do not end in trailing \"/\" or contain \"\\\\\"." + } + ] + } + }, + { + "version": "5.115.0", + "tag": "@microsoft/rush_v5.115.0", + "date": "Thu, 22 Feb 2024 01:36:27 GMT", + "comments": { + "none": [ + { + "comment": "Add a \"runWithTerminalAsync\" resource lifetime helper to `IOperationRunnerContext` to manage the creation and cleanup of logging for operation execution." + }, + { + "comment": "Adds a new experiment `useIPCScriptsInWatchMode`. When this flag is enabled and Rush is running in watch mode, it will check for npm scripts named `_phase::ipc`, and if found, use them instead of the normal invocation of `_phase:`. When doing so, it will provide an IPC channel to the child process and expect the child to outlive the current build pass." + } + ] + } + }, + { + "version": "5.114.3", + "tag": "@microsoft/rush_v5.114.3", + "date": "Thu, 22 Feb 2024 00:10:32 GMT", + "comments": { + "none": [ + { + "comment": "Replace deprecated function, and fix a path bug in Windows env" + } + ] + } + }, + { + "version": "5.114.2", + "tag": "@microsoft/rush_v5.114.2", + "date": "Wed, 21 Feb 2024 21:45:46 GMT", + "comments": { + "none": [ + { + "comment": "Replace the dependency on the `colors` package with `Colorize` from `@rushstack/terminal`." + } + ] + } + }, + { + "version": "5.114.1", + "tag": "@microsoft/rush_v5.114.1", + "date": "Wed, 21 Feb 2024 08:56:05 GMT", + "comments": { + "none": [ + { + "comment": "Improve `rush scan` to analyze APIs such as `Import.lazy()` and `await import()`" + }, + { + "comment": "Fix a recent regression where `@rushstack/rush-sdk` did not declare its dependency on `@rushstack/terminal`" + } + ] + } + }, + { + "version": "5.114.0", + "tag": "@microsoft/rush_v5.114.0", + "date": "Mon, 19 Feb 2024 21:54:44 GMT", + "comments": { + "none": [ + { + "comment": "(EXPERIMENTAL) Add `enablePnpmSyncForInjectedDependenciesMeta` to experiments.json; it is part of an upcoming feature for managing PNPM \"injected\" dependencies: https://www.npmjs.com/package/pnpm-sync" + }, + { + "comment": "Include a `pnpmPatchesCommonFolderName` constant for the folder name \"pnpm-patches\" that gets placed under \"common\"." + }, + { + "comment": "Add a feature to generate a `project-impact-graph.yaml` file in the repo root. This feature is gated under the new `generateProjectImpactGraphDuringRushUpdate` experiment." + }, + { + "comment": "Fix a formatting issue with the LICENSE." + }, + { + "comment": "Fix an issue with filtered installs when the experimental subspaces feature is enabled" + } + ] + } + }, + { + "version": "5.113.4", + "tag": "@microsoft/rush_v5.113.4", + "date": "Wed, 31 Jan 2024 22:49:17 GMT", + "comments": { + "none": [ + { + "comment": "Introduce an explicit warning message during `rush install` or `rush update` about `dependenciesMeta` not being up-to-date." + } + ] + } + }, + { + "version": "5.113.3", + "tag": "@microsoft/rush_v5.113.3", + "date": "Wed, 31 Jan 2024 22:25:55 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where `rush update` would sometimes not correctly sync the `pnpm-lock.yaml` file back to `common/config/rush/` after a project's `package.json` has been updated." + } + ] + } + }, + { + "version": "5.113.2", + "tag": "@microsoft/rush_v5.113.2", + "date": "Wed, 31 Jan 2024 18:45:33 GMT", + "comments": { + "none": [ + { + "comment": "Fix some minor issues when the experimental subspaces feature is enabled" + } + ] + } + }, + { + "version": "5.113.1", + "tag": "@microsoft/rush_v5.113.1", + "date": "Wed, 31 Jan 2024 07:07:50 GMT", + "comments": { + "none": [ + { + "comment": "(EXPERIMENTAL) Enable filtered installs of subspaces and add a \"preventSelectingAllSubspaces\" setting" + } + ] + } + }, + { + "version": "5.113.0", + "tag": "@microsoft/rush_v5.113.0", + "date": "Tue, 30 Jan 2024 22:58:52 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where Rush does not detect changes to the `dependenciesMeta` field in project's `package.json` files, so may incorrectly skip updating/installation." + }, + { + "comment": "Add ability to enable IPC channels in `Utilities#executeLifeCycleCommand`." + }, + { + "comment": "Update `rush init` template to document the \"buildSkipWithAllowWarningsInSuccessfulBuild\" experiment" + }, + { + "comment": "(BREAKING CHANGE) Begin removal of APIs for the deprecated \"installation variants\" feature, since subspaces are a more robust solution for that problem" + }, + { + "comment": "(EXPERIMENTAL) Implement installation for the not-yet-released \"subspaces\" feature (GitHub #4230)" + } + ] + } + }, + { + "version": "5.112.2", + "tag": "@microsoft/rush_v5.112.2", + "date": "Tue, 12 Dec 2023 00:20:51 GMT", + "comments": { + "none": [ + { + "comment": "Bring back the erroneously removed `preminor` bump type for lockstepped packages." + }, + { + "comment": "Fix an issue where the contents of a folder set in the `\"folderToCopy\"` field of the `deploy.json` config file would be copied into a subfolder instead of into the root of the deploy folder." + }, + { + "comment": "(EXPERIMENTAL) Implemented config file loader for the not-yet-released \"subspaces\" feature (GitHub #4230)" + } + ] + } + }, + { + "version": "5.112.1", + "tag": "@microsoft/rush_v5.112.1", + "date": "Wed, 29 Nov 2023 08:59:31 GMT", + "comments": { + "none": [ + { + "comment": "Allow the device code credential options to be extended Azure authentication subclasses, used in advanced authentication scenarios." + } + ] + } + }, + { + "version": "5.112.0", + "tag": "@microsoft/rush_v5.112.0", + "date": "Mon, 27 Nov 2023 23:36:11 GMT", + "comments": { + "none": [ + { + "comment": "Update the `@azure/identity` and `@azure/storage-blob` dependencies of `@rushstack/rush-azure-storage-build-cache-plugin` to eliminate an `EBADENGINE` error when installing Rush on Node 20." + } + ] + } + }, + { + "version": "5.111.0", + "tag": "@microsoft/rush_v5.111.0", + "date": "Sat, 18 Nov 2023 00:06:20 GMT", + "comments": { + "none": [ + { + "comment": "Add experiment `buildSkipWithAllowWarningsInSuccessfulBuild` to allow skipping builds that succeeded with warnings in the previous run." + } + ] + } + }, + { + "version": "5.110.2", + "tag": "@microsoft/rush_v5.110.2", + "date": "Thu, 16 Nov 2023 01:36:10 GMT", + "comments": {} + }, + { + "version": "5.110.1", + "tag": "@microsoft/rush_v5.110.1", + "date": "Wed, 01 Nov 2023 23:29:47 GMT", + "comments": { + "none": [ + { + "comment": "Fix line endings in published package." + } + ] + } + }, + { + "version": "5.110.0", + "tag": "@microsoft/rush_v5.110.0", + "date": "Mon, 30 Oct 2023 23:37:07 GMT", + "comments": { + "none": [ + { + "comment": "Include the filename of the shrinkwrap file in logging messages for all package managers, not just Yarn." + }, + { + "comment": "performance improvements by running asynchronous code concurrently using Promise.all" + } + ] + } + }, + { + "version": "5.109.2", + "tag": "@microsoft/rush_v5.109.2", + "date": "Fri, 20 Oct 2023 01:54:21 GMT", + "comments": { + "none": [ + { + "comment": "Allow the output preservation incremental strategy if the build cache is configured but disabled. When running in verbose mode, log the incremental strategy that is being used." + }, + { + "comment": "Log the cache key in `--verbose` mode when the cache is successfully read from or written to." + }, + { + "comment": "Fix an issue where console colors were sometimes not enabled correctly during `rush install`" + }, + { + "comment": "Fix an issue where running `rush update-cloud-credentials --interactive` sometimes used the wrong working directory when invoked in a repo configured to use the `http` build cache provider (GitHub #4396)" + } + ] + } + }, + { + "version": "5.109.1", + "tag": "@microsoft/rush_v5.109.1", + "date": "Sat, 07 Oct 2023 01:20:56 GMT", + "comments": { + "none": [ + { + "comment": "Fix incorrect capitalization in the \"rush init\" template" + } + ] + } + }, + { + "version": "5.109.0", + "tag": "@microsoft/rush_v5.109.0", + "date": "Sat, 07 Oct 2023 00:25:27 GMT", + "comments": { + "none": [ + { + "comment": "(IMPORTANT) Add a new setting `autoInstallPeers` in pnpm-config.json; be aware that Rush changes PNPM's default if you are using PNPM 8 or newer" + }, + { + "comment": "(IMPORTANT) After upgrading, if `rush install` fails with `ERR_PNPM_LOCKFILE_CONFIG_MISMATCH`, please run `rush update --recheck`" + }, + { + "comment": "Improve visual formatting of custom tips" + }, + { + "comment": "Add start `preRushx` and `postRushx` event hooks for monitoring the `rushx` command" + }, + { + "comment": "Update the oldest usable Node.js version to 14.18.0, since 14.17.0 fails to load" + } + ] + } + }, + { + "version": "5.108.0", + "tag": "@microsoft/rush_v5.108.0", + "date": "Mon, 02 Oct 2023 20:23:27 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where `rush purge` fails on Linux and Mac if the `common/temp/rush-recycler` folder does not exist." + }, + { + "comment": "Add \"--offline\" parameter for \"rush install\" and \"rush update\"" + }, + { + "comment": "Ignore pause/resume watcher actions when the process is not TTY mode" + } + ] + } + }, + { + "version": "5.107.4", + "tag": "@microsoft/rush_v5.107.4", + "date": "Tue, 26 Sep 2023 21:02:52 GMT", + "comments": { + "none": [ + { + "comment": "Update type-only imports to include the type modifier." + }, + { + "comment": "Make the project watcher status and keyboard commands message more visible." + } + ] + } + }, + { + "version": "5.107.3", + "tag": "@microsoft/rush_v5.107.3", + "date": "Fri, 22 Sep 2023 09:01:38 GMT", + "comments": { + "none": [ + { + "comment": "Fix filtered installs in pnpm@8." + } + ] + } + }, + { + "version": "5.107.2", + "tag": "@microsoft/rush_v5.107.2", + "date": "Fri, 22 Sep 2023 00:06:12 GMT", + "comments": { + "none": [ + { + "comment": "Fix a bug in which an operation failing incorrectly does not block its consumers." + }, + { + "comment": "Add `resolutionMode` to `rush init` template for pnpm-config.json" + } + ] + } + }, + { + "version": "5.107.1", + "tag": "@microsoft/rush_v5.107.1", + "date": "Tue, 19 Sep 2023 21:13:23 GMT", + "comments": { + "none": [ + { + "comment": "Fix pnpm's install status printing when pnpm custom tips are defined." + } + ] + } + }, + { + "version": "5.107.0", + "tag": "@microsoft/rush_v5.107.0", + "date": "Tue, 19 Sep 2023 00:36:50 GMT", + "comments": { + "none": [ + { + "comment": "Update @types/node from 14 to 18" + }, + { + "comment": "Remove previously removed fields from the `custom-tips.json` schema." + }, + { + "comment": "(BREAKING API CHANGE) Refactor the `CustomTipsConfiguration` by removing the `configuration` property and adding a `providedCustomTipsByTipId` map property." + }, + { + "comment": "Fix an issue where pnpm would would not rewrite the current status line on a TTY console, and instead would print a series of separate status lines during installation. Note that this is only fixed when there are no custom PNPM tips provided." + }, + { + "comment": "Add \"Waiting\" operation status for operations that have one or more dependencies still pending. Ensure that the `onOperationStatusChanged` hook fires for every status change." + }, + { + "comment": "Add support for optional build status notifications over a web socket connection to `@rushstack/rush-serve-plugin`." + }, + { + "comment": "Add pause/resume option to project watcher" + } + ] + } + }, + { + "version": "5.106.0", + "tag": "@microsoft/rush_v5.106.0", + "date": "Thu, 14 Sep 2023 09:20:11 GMT", + "comments": { + "none": [ + { + "comment": "(IMPORTANT) Add a new setting `resolutionMode` in pnpm-config.json; be aware that Rush now overrides the default behavior if you are using PNPM 8.0.0 through 8.6.12 (GitHub #4283)" + }, + { + "comment": "Support adding custom tips for pnpm-printed logs" + }, + { + "comment": "(BREAKING CHANGE) Remove the \"defaultMessagePrefix\" config in custom-tips.json" + }, + { + "comment": "Rename the `PnpmStoreOptions` type to `PnpmStoreLocation`." + } + ] + } + }, + { + "version": "5.105.0", + "tag": "@microsoft/rush_v5.105.0", + "date": "Fri, 08 Sep 2023 04:09:06 GMT", + "comments": { + "none": [ + { + "comment": "Disable build cache writes in watch rebuilds." + }, + { + "comment": "Fix the instance of \"ICreateOperationsContext\" passed to the \"beforeExecuteOperations\" hook in watch mode rebuilds to match the instance passed to the \"createOperations\" hook." + }, + { + "comment": "Fix an issue where the error message printed when two phases have overlapping output folders did not mention both phases." + }, + { + "comment": "Update the phase output folders validation to only check for overlapping folders for phases that actually execute an operation in a given project." + }, + { + "comment": "Add the \"disableBuildCache\" option to the schema for phased commands (it is already present for bulk commands). Update the behavior of the \"disableBuildCache\" flag to also disable the legacy skip detection, in the event that the build cache is not configured." + } + ] + } + }, + { + "version": "5.104.1", + "tag": "@microsoft/rush_v5.104.1", + "date": "Tue, 05 Sep 2023 18:53:03 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where `rush init` generated a `cobuild.json` file that reported errors (GitHub #4307)" + } + ] + } + }, + { + "version": "5.104.0", + "tag": "@microsoft/rush_v5.104.0", + "date": "Fri, 01 Sep 2023 04:54:16 GMT", + "comments": { + "none": [ + { + "comment": "(EXPERIMENTAL) Initial release of the cobuild feature, a cheap way to distribute jobs Rush builds across multiple VMs. (GitHub #3485)" + } + ] + } + }, + { + "version": "5.103.0", + "tag": "@microsoft/rush_v5.103.0", + "date": "Thu, 31 Aug 2023 23:28:28 GMT", + "comments": { + "none": [ + { + "comment": "Add dependencySettings field to Rush deploy.json configurations. This will allow developers to customize how third party dependencies are processed when running `rush deploy`" + }, + { + "comment": "Fix an issue where `rush update-autoinstaller` sometimes did not fully upgrade the lockfile" + }, + { + "comment": "Fix an issue where \"undefined\" was sometimes printed instead of a blank line" + } + ] + } + }, + { + "version": "5.102.0", + "tag": "@microsoft/rush_v5.102.0", + "date": "Tue, 15 Aug 2023 20:09:40 GMT", + "comments": { + "none": [ + { + "comment": "Add a new config file \"custom-tips.json\" for customizing Rush messages (GitHub #4207)" + }, + { + "comment": "Improve \"rush scan\" to recognize module patterns such as \"import get from 'lodash.get'\"" + }, + { + "comment": "Update Node.js version checks to support the new LTS release" + }, + { + "comment": "Update \"rush init\" template to use PNPM 7.33.5" + }, + { + "comment": "Update the \"rush init\" template's .gitignore to avoid spurious diffs for files such as \"autoinstaller.lock\"" + }, + { + "comment": "Fix an issue where a pnpm-lock file would fail to parse if a project used a package alias in a repo using pnpm 8." + }, + { + "comment": "Fix HTTP/1 backwards compatibility in rush-serve-plugin." + }, + { + "comment": "Add experiment \"usePnpmLockfileOnlyThenFrozenLockfileForRushUpdate\" that, when running `rush update`, performs first a `--lockfile-only` update to the lockfile, then a `--frozen-lockfile` installation. This mitigates issues that may arise when using the `afterAllResolved` hook in `.pnpmfile.cjs`." + } + ] + } + }, + { + "version": "5.101.1", + "tag": "@microsoft/rush_v5.101.1", + "date": "Fri, 11 Aug 2023 17:57:55 GMT", + "comments": { + "none": [ + { + "comment": "Fix a regression from 5.101.0 where publishing features did not detect changes properly when running on Windows OS (GitHub #4277)" + }, + { + "comment": "Add support in rush-serve-plugin for HTTP/2, gzip compression, and CORS preflight requests." + } + ] + } + }, + { + "version": "5.101.0", + "tag": "@microsoft/rush_v5.101.0", + "date": "Tue, 08 Aug 2023 07:11:02 GMT", + "comments": { + "none": [ + { + "comment": "Enable the \"http\" option for build-cache providers" + }, + { + "comment": "Switch from glob to fast-glob." + }, + { + "comment": "Reduce false positive detections of the pnpm shrinkwrap file being out of date in the presence of the `globalOverrides` setting in `pnpm-config.json`, or when a dependency is listed in both `dependencies` and `devDependencies` in the same package." + }, + { + "comment": "@rushstack/rush-sdk now exposes a secondary API for manually loading the Rush engine and monitoring installation progress" + }, + { + "comment": "Add support for npm aliases in `PnpmShrinkwrapFile._getPackageId`." + }, + { + "comment": "Improve version resolution logic in common/scripts/install-run.js (see https://github.com/microsoft/rushstack/issues/4256)" + }, + { + "comment": "Add `patternsToInclude` and `patternsToExclude` support to Rush deploy.json configurations. This will allow developers to include or exclude provided glob patterns within a local project when running `rush deploy`." + } + ] + } + }, + { + "version": "5.100.2", + "tag": "@microsoft/rush_v5.100.2", + "date": "Mon, 24 Jul 2023 18:54:49 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where the git pre-push hook would allow push to go through if the script exited with error." + } + ], + "none": [ + { + "comment": "Updated semver dependency" + } + ] + } + }, + { + "version": "5.100.1", + "tag": "@microsoft/rush_v5.100.1", + "date": "Wed, 14 Jun 2023 19:42:12 GMT", + "comments": { + "none": [ + { + "comment": "Fix an issue where Rush would attempt to open a project's log file for writing twice." + }, + { + "comment": "Fix an issue where arguments weren't passed to git hook scripts." + } + ] + } + }, + { + "version": "5.100.0", + "tag": "@microsoft/rush_v5.100.0", + "date": "Tue, 13 Jun 2023 01:49:21 GMT", + "comments": { + "none": [ + { + "comment": "(BREAKING API CHANGE) Remove unused members of the `BumpType` API. See https://github.com/microsoft/rushstack/issues/1335 for details." + }, + { + "comment": "Add `--peer` flag to `rush add` command to add peerDependencies" + }, + { + "comment": "Add support for PNPM 8." + }, + { + "comment": "Remove the dependency on `lodash`." + }, + { + "comment": "Add functionality for the Amazon S3 Build Cache Plugin to read credentials from common AWS_* environment variables." + }, + { + "comment": "Write cache logs to their own file(s)." + }, + { + "comment": "Fix an issue where cache logging data was always written to stdout." + }, + { + "comment": "Generate scripts in the Git hooks folder referring to the actual hook implementations in-place in the Rush `common/git-hooks/` folder instead of copying the scripts to the Git hooks folder." + }, + { + "comment": "Bump webpack to v5.82.1" + } + ] + } + }, + { + "version": "5.99.0", + "tag": "@microsoft/rush_v5.99.0", + "date": "Fri, 02 Jun 2023 22:08:28 GMT", + "comments": { + "none": [ + { + "comment": "Use a separate temrinal for logging cache subsystem" + }, + { + "comment": "Expose beforeLog hook" + }, + { + "comment": "Convert to multi-phase Heft" + }, + { + "comment": "Use `JSON.parse` instead of `jju` to parse `package.json` files for faster performance." + } + ] + } + }, { "version": "5.98.0", "tag": "@microsoft/rush_v5.98.0", @@ -155,7 +3220,7 @@ "comments": { "none": [ { - "comment": "Add code path to @rushstack/rush-sdk for inheriting @microsoft/rush-lib location from a parent process via the RUSH_LIB_PATH environment variable." + "comment": "Add code path to @rushstack/rush-sdk for inheriting @microsoft/rush-lib location from a parent process via the _RUSH_LIB_PATH environment variable." } ] } diff --git a/apps/rush/CHANGELOG.md b/apps/rush/CHANGELOG.md index 3b0d65f2964..78a3e35f0a5 100644 --- a/apps/rush/CHANGELOG.md +++ b/apps/rush/CHANGELOG.md @@ -1,6 +1,1614 @@ # Change Log - @microsoft/rush -This log was last generated on Sun, 21 May 2023 00:18:35 GMT and should not be manually modified. +This log was last generated on Wed, 05 Aug 2026 19:31:07 GMT and should not be manually modified. + +## 5.178.1 +Wed, 05 Aug 2026 19:31:07 GMT + +### Patches + +- Fix an issue where a phased command exited with a nonzero exit code when its overall execution status was successful but not `SUCCESS`. This covers an iteration that scheduled no operations because a plugin consumed the work itself (which broke `rush --drop-graph` in `@rushstack/rush-buildxl-graph-plugin`), as well as an iteration that a plugin short-circuited with a `SKIPPED` or `FROM CACHE` status. + +### Updates + +- Add `useDirectFileTransfersForBuildCache` to the `experiments.json` `rush init` template. +- Add catalog support to `rush-pnpm update`. + +## 5.178.0 +Mon, 20 Jul 2026 17:49:39 GMT + +### Patches + +- Pass change file paths as discrete Git arguments when adding commit details during publishing. +- Fix an issue where "rush-pnpm patch-commit" rewrote the pre-existing "globalPatchedDependencies" entries in pnpm-config.json using absolute paths when running with pnpm >= 9. +- Fixed an issue where the pnpm `ignoredOptionalDependencies`, `trustPolicy`, `trustPolicyExclude`, and `trustPolicyIgnoreAfterMinutes` settings were written to the `pnpm` field of the generated `package.json` (which pnpm 11 ignores) instead of `pnpm-workspace.yaml`, causing them to be silently ignored under pnpm 11. +- Fix pnpm 11 silently ignoring the `globalOverrides`, `globalPackageExtensions`, `globalPeerDependencyRules`, `globalAllowedDeprecatedVersions`, and `globalPatchedDependencies` settings from pnpm-config.json. Because pnpm 11 no longer reads the `pnpm` field of package.json, Rush now writes these settings to the generated `common/temp/pnpm-workspace.yaml` for pnpm 11+ (matching the existing `allowBuilds` relocation), and `rush-pnpm patch-commit`/`patch-remove` now read `patchedDependencies` back from `pnpm-workspace.yaml` for pnpm 11+. Behavior for pnpm 10 and earlier is unchanged. + +### Updates + +- Fix `minimumReleaseAge` and `minimumReleaseAgeExclude` in `pnpm-config.json` being silently ignored because they were written to package.json instead of `pnpm-workspace.yaml` +- Add optional file-based transfer APIs (`tryDownloadCacheEntryToFileAsync`, `tryUploadCacheEntryFromFileAsync`) to `ICloudBuildCacheProvider`, allowing cache plugins to transfer cache entries directly to and from files on disk without buffering entire contents in memory. Implement in `@rushstack/rush-http-build-cache-plugin`, `@rushstack/rush-amazon-s3-build-cache-plugin`, and `@rushstack/rush-azure-storage-build-cache-plugin`. Gated behind the `useDirectFileTransfersForBuildCache` experiment. +- Avoid redundant downloads when multiple local Rush processes race to restore the same build cache entry (when useDirectFileTransfersForBuildCache is enabled). +- Replace `@override` with the `override` keyword. +- (PLUGIN BREAKING CHANGE) Overhaul watch-mode commands such that the graph is only created once at the start of command invocation, along with a stateful manager object. Plugins may now access the manager object and use it to orchestrate and tap into the build process. + +## 5.177.2 +Wed, 08 Jul 2026 21:27:15 GMT + +### Patches + +- Upgrade the `pnpm-sync-lib` dependency to 0.3.4. + +## 5.177.1 +Sat, 20 Jun 2026 21:37:30 GMT + +### Updates + +- Bump `ws` in `rush-serve-plugin` to mitigate CVE-2026-48779. + +## 5.177.0 +Sat, 20 Jun 2026 00:16:15 GMT + +### Patches + +- Set Redis `connectTimeout` and `socketTimeout` so half-dead TCP connections (NAT/firewall) surface in seconds instead of stalling in-flight commands for many minutes while the kernel waits to give up. + +### Updates + +- Fix build cache failures when running inside a git linked worktree via a pre-commit hook, caused by GIT_DIR being set to the per-worktree metadata directory + +## 5.176.0 +Tue, 09 Jun 2026 02:02:32 GMT + +### Minor changes + +- Add support for pnpm 11's `allowBuilds` field in `pnpm-workspace.yaml`. Rush now correctly handles the pnpm 11 security model where build scripts must be explicitly approved. The new `globalAllowBuilds` field in `pnpm-config.json` replaces the deprecated `globalOnlyBuiltDependencies` and `globalNeverBuiltDependencies` fields for pnpm 11+. The `rush-pnpm approve-builds` command is also updated to work correctly with pnpm 11. +- Include seconds in the generated change file name so that running `rush change` more than once in the same minute no longer silently overwrites the previously generated change file. +- Default `rush-pnpm outdated` and `rush-pnpm why` to recursive workspace queries. + +### Patches + +- Fix a regression where Rush change-detection treated any `pnpm-config.json` containing comments as unparseable, causing every project to be flagged as impacted. +- Route the "Lockfile was created or deleted" warning to stderr so that machine-readable output (e.g. `rush list --json`) remains parseable when the lockfile was added or removed in the diff range. +- Fix `rush update` not syncing `pnpm-lock.yaml` when a workspace dependency moves from `dependencies` to `devDependencies`. + +### Updates + +- Bump the `ws` dependency to `~8.20.0`. + +## 5.175.1 +Mon, 20 Apr 2026 23:31:34 GMT + +### Updates + +- Fix an issue where `rush list --detailed` did not print horizontal table separators. + +## 5.175.0 +Sat, 18 Apr 2026 03:47:29 GMT + +### Patches + +- Bump the Azure cache plugin dependencies to use `@azure/identity` `~4.13.1` and `@azure/storage-blob` `~12.31.0`. +- Remove unused dependencies: replace `glob-escape` with `fast-glob`'s `escapePath`, replace `figures.pointer` with a named const, replace `builtin-modules` with `node:module.isBuiltin()`. +- Replace `cli-table` dependency with `TerminalTable` from `@rushstack/terminal`. + +### Updates + +- Bump semver. +- Replace deprecated `inquirer` packages with modern per-prompt `@inquirer/*` family of packages. + +## 5.174.0 +Thu, 16 Apr 2026 05:25:41 GMT + +### Minor changes + +- Add support for pnpm `trustPolicy`, `trustPolicyExclude`, and `trustPolicyIgnoreAfterMinutes` settings in `pnpm-config.json`. + +### Updates + +- rush-resolver-cache-plugin: add pnpm 10 / lockfile v9 compatibility +- Deprecate `minimumReleaseAge` in `common/config/rush/pnpm-config.json`; use `minimumReleaseAgeMinutes` instead +- Add support for pnpm global catalog detection to `rush change`. Now, when a dependencyis changed in the pnpm global catalog, changelogs will be required for affected published packages. +- Fix a bug where the injected dependency state hash updated on devDependency changes that don't impact the lockfile. +- Add "strictChangefileValidation" experiment and "--verify-all" flag for "rush change". When the experiment is enabled, "rush change --verify" and "rush change --verify-all" will report errors if change files reference nonexistent projects or target non-main projects in a lockstepped version policy. + +## 5.173.0 +Fri, 10 Apr 2026 22:46:54 GMT + +### Minor changes + +- Add async variants of disk-touching APIs in `PackageJsonEditor` (`loadAsync`, `saveIfModifiedAsync`), `CommonVersionsConfiguration` (`loadFromFileAsync`, `saveAsync`), and `VersionPolicy` (`setDependenciesBeforePublishAsync`, `setDependenciesBeforeCommitAsync`); deprecate corresponding sync methods. + +### Updates + +- Move stale autoinstaller `node_modules` folders into Rush's recycler before asynchronously deleting them, instead of synchronously deleting them in place. +- Filter npm-incompatible properties from .npmrc when installing rush-lib via npm, to eliminate spurious "Unknown env config" and "Unknown project config" warnings. +- When cobuilds are remote executing, check them after locally executable tasks. + +## 5.172.1 +Wed, 25 Mar 2026 01:01:07 GMT + +### Updates + +- Fix an issue where "rush deploy" could fail with EEXIST due to a "npm-packlist" regression (GitHub #5720) + +## 5.172.0 +Tue, 24 Mar 2026 21:52:13 GMT + +### Updates + +- Add `getCustomParametersByLongName()` and `setHandled()` to `IGlobalCommand`, enabling Rush plugins to handle global command execution and access parsed command-line parameter values. +- Add a new "globalPlugin" command kind for command-line.json that allows Rush plugins to define global commands without a shellCommand. This command kind can only be used in plugin-provided command-line.json files. + +## 5.171.0 +Sat, 21 Mar 2026 03:10:24 GMT + +### Minor changes + +- Add RUSH_QUIET_MODE environment variable that, when set to `1` or `true`, is equivalent to passing `--quiet` for `rush`, `rushx`, and `install-run-rush.ts` + +### Patches + +- Update OperationExecutionRecord to only set `this.logFilePaths` if the value to be assigned is not `undefined` +- Fix ProblemCollector wiring in OperationExecutionRecord: add preventAutoclose, strip ANSI colors before matching, and always include problemCollector in the terminal pipeline +- Fix weighted concurrency budget being capped by operation count + +### Updates + +- Fix an issue where the assets used by `rush init` weren't shipped. + +## 5.170.1 +Thu, 12 Mar 2026 22:33:34 GMT + +### Updates + +- Fix a recent regression that sometimes produced ENOENT errors when installing autoinstallers + +## 5.170.0 +Thu, 12 Mar 2026 09:15:52 GMT + +### Updates + +- Add custom endpoint support via a `storageEndpoint` configuration option for the Azure Storage build cache plugin. +- Fix autoinstaller plugin loader behavior. +- Support percentage weight in operationSettings in rush-project.json file. + +## 5.169.3 +Mon, 23 Feb 2026 00:42:39 GMT + +### Updates + +- Fix .npmrc syncing to common/temp incorrectly caching results, which caused pnpm-specific properties like hoist-pattern to be stripped when the same .npmrc was processed with different options. + +## 5.169.2 +Fri, 20 Feb 2026 00:15:23 GMT + +### Updates + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 5.169.1 +Thu, 19 Feb 2026 01:30:24 GMT + +### Updates + +- Add missing README for rush-azure-storage-build-cache-plugin and rush-buildxl-graph-plugin. Filter files from publish for rush-bridge-cache-plugin. +- Fix an issue where files were missing from the published version of `@rushstack/package-extractor`. + +## 5.169.0 +Thu, 19 Feb 2026 00:05:11 GMT + +### Updates + +- Sort the `additionalFilesForOperation` property in operation settings entries in projects' `config/rush-project.json` files before computing operation hashes to produce a stable hash for caching. +- Normalize package layout. CommonJS is now under `lib-commonjs` and DTS is now under `lib-dts`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. +- Add a new "omitAppleDoubleFilesFromBuildCache" experiment. When enabled, the Rush build cache will omit macOS AppleDouble metadata files (._*) from cache archives when a companion file exists in the same directory. This prevents platform-specific metadata files from polluting the shared build cache. The exclusion only applies when running on macOS. +- Add a new `dependsOnNodeVersion` setting for operation entries in rush-project.json. When enabled, the Node.js version is included in the build cache hash, ensuring that cached outputs are invalidated when the Node.js version changes. Accepts `true` (alias for `"patch"`), `"major"`, `"minor"`, or `"patch"` to control the granularity of version matching. + +## 5.168.0 +Thu, 12 Feb 2026 23:01:10 GMT + +### Updates + +- Add named exports to support named imports to `@rushstack/rush-sdk`. +- Fix `rush change --verify` to ignore version-only changes in package.json files and changes to CHANGELOG.md and CHANGELOG.json files, preventing false positives after `rush version --bump` updates package versions and changelogs. + +## 5.167.0 +Thu, 05 Feb 2026 00:24:16 GMT + +### Updates + +- Add support for `rush-pnpm approve-builds` command to persist `globalOnlyBuiltDependencies` in pnpm-config.json +- Filter npm-incompatible properties from .npmrc when npm is used with a configuration intended for pnpm or yarn, to eliminate spurious warnings during package manager installation. +- Fix a longstanding issue where a package.json script could hang on Windows if it accessed STDIN under certain circumstances +- Upgrade tar dependency from 6.2.1 to 7.5.6 to fix security vulnerability GHSA-8qq5-rm4j-mr97 + +## 5.166.0 +Mon, 12 Jan 2026 23:39:06 GMT + +### Updates + +- Add support for Node 24 and bump the `rush init` template to default to Node 24. +- Remove use of the deprecated `shell: true` option in process spawn operations. + +## 5.165.0 +Mon, 29 Dec 2025 22:43:14 GMT + +### Updates + +- Forward the `parameterNamesToIgnore` `/config/rush-project.json` property to child processes via a `RUSHSTACK_CLI_IGNORED_PARAMETER_NAMES` environment variable +- Fix an issue where `rush update` will error complaining that the shrinkwrap file hasn't been updated to support workspaces in a subspace with no projects. +- Fix an issue where packages listed in the `pnpmLockfilePolicies.disallowInsecureSha1.exemptPackageVersions` `common/config/rush/pnpm-config.json` config file are not exempted in PNPM 9. +- Add support for the `globalOnlyBuiltDependencies` PNPM 10.x option to specify an allowlist of packages permitted to run build scripts to `common/config/rush/pnpm-config.json`. +- Upgrade `pnpm-sync-lib` to v0.3.3 for pnpm v10 compatibility +- Change the Git hook file shebangs to use `/usr/bin/env bash` instead of `/bin/bash` for greater platform compatability. + +## 5.164.0 +Tue, 16 Dec 2025 21:49:00 GMT + +### Minor changes + +- Hash full shrinkwrap entry to detect sub-dependency resolution changes + +### Updates + +- Fix an issue where ProjectChangeAnalyzer checked the pnpm-lock.yaml file in the default subspace only, when it should consider all subspaces. +- Log a warning if Git-tracked symbolic links are encountered during repo state analysis. +- Add support for defining pnpm catalog config. + +## 5.163.0 +Tue, 25 Nov 2025 17:04:05 GMT + +### Minor changes + +- Added the ability to select projects via path, e.g. `rush build --to path:./my-project` or `rush build --only path:/some/absolute/path` +- Add project-level parameter ignoring to prevent unnecessary cache invalidation. Projects can now use "parameterNamesToIgnore" in "rush-project.json" to exclude custom command-line parameters that don't affect their operations. + +### Updates + +- Extract CredentialCache API out into "@rushstack/credential-cache". Reference directly in plugins to avoid pulling in all of "@rushstack/rush-sdk" unless necessary. +- Add subspaceName to the output of the `rush list` command + +## 5.162.0 +Sat, 18 Oct 2025 00:06:36 GMT + +### Updates + +- Fork npm-check to address npm audit CVE + +## 5.161.0 +Fri, 17 Oct 2025 23:22:50 GMT + +### Updates + +- Add an `allowOversubscription` option to the command definitions in `common/config/rush/command-line.json` to prevent running tasks from exceeding concurrency. +- Add support for PNPM's minimumReleaseAge setting to help mitigate supply chain attacks +- Enable prerelease version matching in bridge-package command +- Fix an issue where `rush add --make-consistent ...` may drop the `implicitlyPreferredVersions` and `ensureConsistentVersions` properties from `common/config/rush/common-versions.json`. +- Treat intermittent ignored redis errors as warnings and allow build to continue. + +## 5.160.1 +Fri, 03 Oct 2025 22:25:25 GMT + +### Updates + +- Fix an issue with validation of the `pnpm-lock.yaml` `packageExtensionsChecksum` field in pnpm v10. + +## 5.160.0 +Fri, 03 Oct 2025 20:10:21 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +### Updates + +- Bump the default Node and `pnpm` versions in the `rush init` template. +- Fix an issue with validation of the `pnpm-lock.yaml` `packageExtensionsChecksum` field in pnpm v10. +- Fix an issue where the `$schema` property is dropped from `common/config/rush/pnpm-config.json` when running `rush-pnpm patch-commit ...` + +## 5.159.0 +Fri, 03 Oct 2025 00:50:08 GMT + +### Patches + +- [rush-azure-storage-build-cache-plugin] Trim access token output in AdoCodespacesAuthCredential + +### Updates + +- Fix to allow Bridge Cache plugin be installed but not used when build cache disabled; add cache key to terminal logs +- Add `IOperationExecutionResult.problemCollector` API which matches and collects VS Code style problem matchers +- Replace uuid package dependency with Node.js built-in crypto.randomUUID +- [rush-resolver-cache] Ensure that the correct version of rush-lib is loaded when the global version doesn't match the repository version. +- Upgraded `js-yaml` dependency +- Enhance logging for IPC mode by allowing IPC runners to report detailed reasons for rerun, e.g. specific changed files. +- Support aborting execution in phased commands. The CLI allows aborting via the "a" key in watch mode, and it is available to plugin authors for more advanced scenarios. +- [rush-serve-plugin] Support aborting execution via Web Socket. Include information about the dependencies of operations in messages to the client.. +- Add a logging message after the 'Trying to find "tar" binary' message when the binary is found. +- Upgrade inquirer to 8.2.7 in rush-lib +- Bump "express" to 4.21.1 to address reported vulnerabilities in 4.20.0. + +## 5.158.1 +Fri, 29 Aug 2025 00:08:18 GMT + +### Updates + +- Deduplicate parsing of dependency specifiers. +- Optimize detection of local projects when collecting implicit preferred versions. +- Dedupe shrinkwrap parsing by content hash. +- [resolver-cache] Use shrinkwrap hash to skip resolver cache regeneration. + +## 5.158.0 +Tue, 26 Aug 2025 23:27:47 GMT + +### Updates + +- Adds an optional safety check flag to the Bridge Cache plugin write action. +- Fix a bug in "@rushstack/rush-bridge-cache-plugin" where the cache replay did not block the normal execution process and instead was a floating promise. +- [resolver-cache-plugin] Optimize search for nested package.json files with persistent cache file keyed by integrity hash. +- [rush-serve-plugin] Allow the Rush process to exit if the server is the only active handle. +- Fix poor performance scaling during `rush install` when identifying projects in the lockfile that no longer exist. +- [resolver-cache-plugin] Improve performance of scan for nested package.json files in external packages. +- Optimize `setPreferredVersions` in install setup. +- Ensure that `rush version` and `rush publish` preserve all fields in `version-policies-json`. + +## 5.157.0 +Fri, 25 Jul 2025 01:24:42 GMT + +### Updates + +- Improve performance for publishing on filtered clones. + +## 5.156.0 +Wed, 23 Jul 2025 20:56:15 GMT + +### Updates + +- Include "parallelism" in phased operation execution context. Update "rush-bridge-cache-plugin" to support both cache read and cache write, selectable via command line choice parameter. Fixes an issue that the options schema for "rush-bridge-cache-plugin" was invalid. +- Add support for `RUSH_BUILD_CACHE_OVERRIDE_JSON` environment variable that takes a JSON string with the same format as the `common/config/build-cache.json` file and a `RUSH_BUILD_CACHE_OVERRIDE_JSON_FILE_PATH` environment variable that takes a file path that can be used to override the build cache configuration that is normally provided by that file. +- Add support for setting environment variables via `/.env` and `~/.rush-user/.env` files. +- [azure-storage-build-cache] Update build-cache.json schema to allow the full range of `loginFlow` options supported by the underlying authentication provider. Add `loginFlowFailover` option to customize fallback sequencing. +- Add performance measures around various operations, include performance entries in telemetry payload. +- Do not run afterExecuteOperation if the operation has not actually completed. + +## 5.155.1 +Fri, 27 Jun 2025 19:57:04 GMT + +### Updates + +- Fix pnpm-sync caused .modules.yaml ENOENT during install + +## 5.155.0 +Fri, 13 Jun 2025 16:10:38 GMT + +### Updates + +- Add support for PNPM v9 to the pnpm-sync feature. + +## 5.154.0 +Tue, 10 Jun 2025 18:45:59 GMT + +### Updates + +- Introduce a `@rushstack/rush-bridge-cache-plugin` package that adds a `--set-cache-only` flag to phased commands, which sets the cache entry without performing the operation. +- Update the `CredentialCache` options object to add support for custom cache file paths. This is useful if `CredentialCache` is used outside of Rush. +- PNPMv10 support: SHA256 hashing for dependencies paths lookup +- Add Linux/MacOS support for new 'virtual-store-dir-max-length' + +## 5.153.2 +Tue, 13 May 2025 20:33:12 GMT + +### Updates + +- Fix path parsing issue when running rush bridge-package +- Operations that were cobuilt now have the cobuild time correctly reflected across all agents. +- Add `hasUncommittedChanges` to `IInputSnapshot` for use by plugins. + +## 5.153.1 +Fri, 25 Apr 2025 01:12:48 GMT + +### Updates + +- Fix an issue with implicit phase expansion when `--include-phase-deps` is not specified. +- Upgrade `rushstack/heft-config-file` to fix an incompatibility with Node 16 + +## 5.153.0 +Thu, 17 Apr 2025 21:59:15 GMT + +### Updates + +- Update documentation for `extends` +- Bind "q" to gracefully exit the watcher. +- Clarify registry authentication settings in "rush init" template for .npmrc +- Support the `--changed-projects-only` flag in watch mode and allow it to be toggled between iterations. +- Fix telemetry for "--changed-projects-only" when toggled in watch mode. +- (rush-serve-plugin) Support websocket message to enable/disable operations. + +## 5.152.0 +Tue, 08 Apr 2025 18:41:27 GMT + +### Updates + +- Add `ChainedCredential` to `AzureAuthenticationBase` to handle auth failover. +- Add support for developer tools credentials to the Azure build cache. +- Add a new CLI flag `--debug-build-cache-ids` to help with root-causing unexpected cache misses. +- Sort all operations lexicographically by name for reporting purposes. +- (EXPERIMENTAL) Add new commands `rush link-package` and `rush bridge-package` + +## 5.151.0 +Tue, 25 Mar 2025 16:58:46 GMT + +### Updates + +- Fix an issue where `--include-phase-deps` and watch mode sometimes included operations that were not required +- Fix an issue where build/rebuild can not be defined in a rush plugin command line configuration +- Use `useNodeJSResolver: true` in `Import.resolvePackage` calls. +- Add missing `./package.json` export; revert `useNodeJSResolver: true`. +- (plugin-api) Guaranteed `operation.associatedPhase` and `operation.associatedProject` are not undefined. + +## 5.150.0 +Thu, 27 Feb 2025 17:41:59 GMT + +### Updates + +- Add an `--include-phase-deps` switch that expands an unsafe project selection to include its phase dependencies + +## 5.149.1 +Wed, 19 Feb 2025 18:54:06 GMT + +### Updates + +- Remove the unused `RushConstants.rushAlertsStateFilename` property. +- Bump `jsonpath-plus` to `~10.3.0`. + +## 5.149.0 +Wed, 12 Feb 2025 04:07:30 GMT + +### Updates + +- Prefer `os.availableParallelism()` to `os.cpus().length`. +- Add a new command line parameter `--node-diagnostic-dir=DIR` to phased commands that, when specified, tells all child build processes to write NodeJS diagnostics into `${DIR}/${packageName}/${phaseIdentifier}`. This is useful if `--cpu-prof` or `--heap-prof` are enabled, to avoid polluting workspace folders. +- Add a new phased command hook `createEnvironmentForOperation` that can be used to customize the environment variables passed to individual operation subprocesses. This may be used to, for example, customize `NODE_OPTIONS` to pass `--diagnostic-dir` or other such parameters. +- Allow --timeline option for all phased commands +- Fix support for "ensureConsistentVersions" in common-versions.json when subspaces features is not enabled. +- Fix an issue where the port parameter in `@rushstack/rush-serve-plugin` was allowed to be a string parameter. + +## 5.148.0 +Fri, 10 Jan 2025 02:36:20 GMT + +### Updates + +- Add a configuration option to avoid manually configuring decoupledLocalDependencies across subspaces. +- Improve some `rush-sdk` APIs to support future work on GitHub issue #3994 +- Fix an issue where MaxListenersExceeded would get thrown when using the HTTP build cache plugin + +## 5.147.2 +Mon, 06 Jan 2025 21:48:43 GMT + +### Updates + +- Fix an issue with evaluation of `shouldEnsureConsistentVersions` when the value is not constant across subspaces or variants. +- Fix an issue where the lockfile object has a nullish value causing yaml.dump to report an error. + +## 5.147.1 +Thu, 26 Dec 2024 23:35:27 GMT + +### Updates + +- Fix an issue with the `enableSubpathScan` experiment where the set of returned hashes would result in incorrect build cache identifiers when using `--only`. +- When a no-op operation is not in scope, reflect its result as no-op instead of skipped, so that downstream operations can still write to the build cache. +- Allow injected dependencies without enabling subspaces. + +## 5.147.0 +Thu, 12 Dec 2024 01:37:25 GMT + +### Updates + +- Add a new experiment flag `enableSubpathScan` that, when invoking phased script commands with project selection parameters, such as `--to` or `--from`, only hashes files that are needed to compute the cache ids for the selected projects. + +## 5.146.0 +Tue, 10 Dec 2024 21:23:18 GMT + +### Updates + +- Support fallback syntax in `.npmrc` files if the package manager is PNPM. See https://pnpm.io/npmrc +- Add an `.isPnpm` property to `RushConfiguration` that is set to true if the package manager for the Rush repo is PNPM. +- Support pnpm lockfile v9, which is used by default starting in pnpm v9. + +## 5.145.0 +Tue, 10 Dec 2024 05:14:11 GMT + +### Updates + +- Upgrade `@azure/identity` and `@azure/storage-blob`. +- Add support for Node 22. +- Remove the dependency on node-fetch. + +## 5.144.1 +Mon, 09 Dec 2024 20:32:01 GMT + +### Updates + +- Bump `jsonpath-plus` to `~10.2.0`. + +## 5.144.0 +Wed, 04 Dec 2024 19:32:23 GMT + +### Updates + +- Remove the `node-fetch` dependency from `@rushstack/rush-http-build-cache-plugin`. + +## 5.143.0 +Wed, 04 Dec 2024 03:07:08 GMT + +### Updates + +- Remove the `node-fetch` dependency from @rushstack/rush-amazon-s3-build-cache-plugin. +- (BREAKING API CHANGE) Remove the exported `WebClient` API from @rushstack/rush-amazon-s3-build-cache-plugin. + +## 5.142.0 +Tue, 03 Dec 2024 23:42:22 GMT + +### Updates + +- Fix an issue where the ability to skip `rush install` may be incorrectly calculated when using the variants feature. +- Add support for an `"extends"` property in the `common/config/rush/pnpm-config.json` and `common/config/subspace/*/pnpm-config.json` files. +- Add warning when the `globalIgnoredOptionalDependencies` property is specified in `common/config/rush/pnpm-config.json` and the repo is configured to use pnpm <9.0.0. + +## 5.141.4 +Mon, 02 Dec 2024 20:40:41 GMT + +### Updates + +- Fix an issue where Rush sometimes incorrectly reported "fatal: could not open 'packages/xxx/.rush/temp/shrinkwrap-deps.json' for reading: No such file or directory" when using subspaces + +## 5.141.3 +Wed, 27 Nov 2024 07:16:50 GMT + +### Updates + +- Fix an issue where Rush sometimes incorrectly reported "The overrides settings doesn't match the current shrinkwrap" when using subspaces +- Fix an issue where Rush sometimes incorrectly reported "The package extension hash doesn't match the current shrinkwrap." when using subspaces + +## 5.141.2 +Wed, 27 Nov 2024 03:27:26 GMT + +### Updates + +- Fix an issue where filtered installs neglected to install dependencies from other subspaces + +## 5.141.1 +Wed, 20 Nov 2024 00:24:34 GMT + +### Updates + +- Update schema for build-cache.json to include recent updates to the @rushstack/rush-azure-storage-build-cache-plugin. + +## 5.141.0 +Tue, 19 Nov 2024 06:38:33 GMT + +### Updates + +- Adds two new properties to the configuration for `rush-azure-storage-build-cache-plugin`: `loginFlow` selects the flow to use for interactive authentication to Entra ID, and `readRequiresAuthentication` specifies that a SAS token is required for read and therefore expired authentication is always fatal. +- Adds a new `wasExecutedOnThisMachine` property to operation telemetry events, to simplify reporting about cobuilt operations. +- Fix an issue where empty error logs were created for operations that did not write to standard error. +- Fix an issue where incremental building (with LegacySkipPlugin) would not work when no-op operations were present in the process +- Fix lack of "local-only" option for cacheProvider in build-cache.schema.json +- Fix an issue where if an Operation wrote all logs to stdout, then exited with a non-zero exit code, only the non-zero exit code would show up in the summary. + +## 5.140.1 +Wed, 30 Oct 2024 21:50:51 GMT + +### Updates + +- Update the `jsonpath-plus` indirect dependency to mitigate CVE-2024-21534. + +## 5.140.0 +Tue, 22 Oct 2024 23:59:54 GMT + +### Updates + +- Fix an issue when using `rush deploy` where the `node_modules/.bin` folder symlinks were not created for deployed packages when using the "default" link creation mode +- Add support for the `globalIgnoredOptionalDependencies` field in the `common/config/rush/pnpm-config.json` file to allow specifying optional dependencies that should be ignored by PNPM + +## 5.139.0 +Thu, 17 Oct 2024 20:37:39 GMT + +### Updates + +- Allow rush plugins to extend build cache entries by writing additional files to the metadata folder. Expose the metadata folder path to plugins. +- [CACHE BREAK] Alter the computation of build cache IDs to depend on the graph of operations in the build and therefore account for multiple phases, rather than only the declared dependencies. Ensure that `dependsOnEnvVars` and command line parameters that affect upstream phases impact the cache IDs of downstream operations. +- (BREAKING CHANGE) Replace use of `ProjectChangeAnalyzer` in phased command hooks with a new `InputsSnapshot` data structure that is completely synchronous and does not perform any disk operations. Perform all disk operations and state computation prior to executing the build graph. +- Add a new property `enabled` to `Operation` that when set to false, will cause the execution engine to immediately return `OperationStatus.Skipped` instead of invoking the runner. Use this property to disable operations that are not intended to be executed in the current pass, e.g. those that did not contain changes in the most recent watch iteration, or those excluded by `--only`. +- Add an optional property `cacheHashSalt` to `build-cache.json` to allow repository maintainers to globally force a hash change in build cache entries. + +## 5.138.0 +Thu, 03 Oct 2024 22:31:07 GMT + +### Updates + +- Changes the behavior of phased commands in watch mode to, when running a phase `_phase:` in all iterations after the first, prefer a script entry named `_phase::incremental` if such a script exists. The build cache will expect the outputs from the corresponding `_phase:` script (with otherwise the same inputs) to be equivalent when looking for a cache hit. + +## 5.137.0 +Thu, 03 Oct 2024 19:46:40 GMT + +### Patches + +- Expose `getChangesByProject` to allow classes that extend ProjectChangeAnalyzer to override file change analysis + +## 5.136.1 +Thu, 26 Sep 2024 22:59:11 GMT + +### Updates + +- Fix an issue where the `--variant` parameter was missing from a phased command when the command's `alwaysInstall` property was set to `true`. + +## 5.136.0 +Thu, 26 Sep 2024 21:48:00 GMT + +### Updates + +- Bring back the Variants feature that was removed in https://github.com/microsoft/rushstack/pull/4538. +- Bump express dependency to 4.20.0 + +## 5.135.0 +Fri, 20 Sep 2024 20:23:40 GMT + +### Updates + +- Fix a bug that caused rush-resolver-cache-plugin to crash on Windows. +- Make individual Rush log files available via the rush-serve-plugin server at the relative URL specified by "logServePath" option. Annotate operations sent over the WebSocket with the URLs of their log files. +- Adds a new experiment 'allowCobuildWithoutCache' for cobuilds to allow uncacheable operations to benefit from cobuild orchestration without using the build cache. +- Deprecate the `sharding.shardOperationSettings` property in the project `config/rush-project.json` in favor of an `operationSettings` entry for an operation with a suffix of `:shard`. + +## 5.134.0 +Fri, 13 Sep 2024 01:02:46 GMT + +### Updates + +- Always update shrinkwrap when `globalPackageExtensions` in `common/config/rush/pnpm-config.json` has been changed. +- Pass the initialized credentials cache to `AzureAuthenticationBase._getCredentialFromTokenAsync` in `@rushstack/rush-azure-storage-build-cache-plugin`. +- Support the `rush-pnpm patch-remove` command. + +## 5.133.4 +Sat, 07 Sep 2024 00:18:08 GMT + +### Updates + +- Mark `AzureAuthenticationBase._credentialCacheId` as protected in `@rushstack/rush-azure-storage-build-cache-plugin`. + +## 5.133.3 +Thu, 29 Aug 2024 22:49:36 GMT + +### Updates + +- Fix Windows compatibility for `@rushstack/rush-resolver-cache-plugin`. + +## 5.133.2 +Wed, 28 Aug 2024 20:46:32 GMT + +### Updates + +- Fix an issue where running `rush install --resolution-only` followed by `rush install` would not actually install modules. + +## 5.133.1 +Wed, 28 Aug 2024 18:19:55 GMT + +### Updates + +- In rush-resolver-cache-plugin, include the base path in the resolver cache file. +- Support `bundledDependencies` in rush-resolver-cache-plugin. + +## 5.133.0 +Fri, 23 Aug 2024 00:40:08 GMT + +### Updates + +- Always update shrinkwrap when globalOverrides has been changed +- Add `afterInstall` plugin hook, which runs after any install finishes. +- Add rush.json option "suppressRushIsPublicVersionCheck" to allow suppressing hardcoded calls to the npmjs.org registry. + +## 5.132.0 +Wed, 21 Aug 2024 16:25:07 GMT + +### Updates + +- Add a new `rush install-autoinstaller` command that ensures that the specified autoinstaller is installed. +- Emit an error if a `workspace:` specifier is used in a dependency that is listed in `decoupledLocalDependencies`. +- Add support for `--resolution-only` to `rush install` to enforce strict peer dependency resolution. + +## 5.131.5 +Mon, 19 Aug 2024 20:03:03 GMT + +### Updates + +- Fix an issue where PreferredVersions are ignored when a project contains an overlapping dependency entry (https://github.com/microsoft/rushstack/issues/3205) + +## 5.131.4 +Sun, 11 Aug 2024 05:02:05 GMT + +### Updates + +- Revert a breaking change in Rush 5.131.3 where pnpm patches were moved from `common/pnpm-patches` to `common/config/rush/pnpm-patches`. + +## 5.131.3 +Sat, 10 Aug 2024 02:27:14 GMT + +### Updates + +- Fix an issue where `rush-pnpm patch-commit` would not correctly resolve patch files when the subspaces feature is enabled. + +## 5.131.2 +Thu, 08 Aug 2024 23:38:18 GMT + +### Updates + +- Include a missing dependency in `@rushstack/rush-sdk`. + +## 5.131.1 +Thu, 08 Aug 2024 22:08:41 GMT + +### Updates + +- Fix an issue where rush-sdk can't be bundled by a consuming package. +- Extract LookupByPath to @rushstack/lookup-by-path and load it from there. + +## 5.131.0 +Fri, 02 Aug 2024 17:26:59 GMT + +### Updates + +- Improve Rush alerts with a new "rush alert" command and snooze feature + +## 5.130.3 +Wed, 31 Jul 2024 23:30:13 GMT + +### Updates + +- Fix an issue where Rush does not detect an outdated lockfile if the `dependenciesMeta` `package.json` field is edited. +- Include CHANGELOG.md in published releases again +- Fix a bug that caused the build cache to close its terminal writer before execution on error. + +## 5.130.2 +Fri, 19 Jul 2024 03:41:44 GMT + +### Updates + +- Fix an issue where `rush-pnpm patch-commit` did not work correctly when subspaces are enabled. + +## 5.130.1 +Wed, 17 Jul 2024 07:37:13 GMT + +### Updates + +- Fix a recent regression for `rush init` + +## 5.130.0 +Wed, 17 Jul 2024 06:55:27 GMT + +### Updates + +- (EXPERIMENTAL) Initial implementation of Rush alerts feature +- Adjusts how cobuilt operations are added and requeued to the operation graph. Removes the 'RemoteExecuting' status. + +## 5.129.7 +Tue, 16 Jul 2024 04:16:56 GMT + +### Updates + +- Upgrade pnpm-sync-lib to fix an edge case when handling node_modules folder +- Don't interrupt the installation process if the user hasn't enabled the inject dependencies feature. +- Improve `@rushtack/rush-sdk` and make it reuse `@microsoft/rush-lib` from rush global folder +- Remove the trailing slash in the `.DS_Store/` line in the `.gitignore` file generated by `rush init`. `.DS_Store` is a file, not a folder. +- Support deep references to internal Apis +- Fix an issue where `rush add` would ignore the `ensureConsistentVersions` option if that option was set in `rush.json` instead of in `common/config/rush/common-versions.json`. +- Fix an issue where running `rush add` in a project can generate a `package.json` file that uses JSON5 syntax. Package managers expect strict JSON. +- fix spelling of "committing" in rush.json init template and schema + +## 5.129.6 +Thu, 27 Jun 2024 00:44:32 GMT + +### Updates + +- Fix an edge case for workspace peer dependencies when calculating packageJsonInjectedDependenciesHash to improve its accuracy +- Update a URL in the `.pnpmfile.cjs` generated by `rush init`. + +## 5.129.5 +Tue, 25 Jun 2024 20:13:29 GMT + +### Updates + +- Don't include package.json version field when calculating packageJsonInjectedDependenciesHash + +## 5.129.4 +Mon, 24 Jun 2024 23:49:10 GMT + +### Updates + +- Normalize the file permissions (644) for Rush plugin files that are committed to Git + +## 5.129.3 +Fri, 21 Jun 2024 00:15:54 GMT + +### Updates + +- Fixed an issue where DependencyAnalyzer caches the same analysis for all subspaces + +## 5.129.2 +Wed, 19 Jun 2024 23:59:09 GMT + +### Updates + +- Fix an issue where the `rush pnpm ...` command always terminates with an exit code of 1. + +## 5.129.1 +Wed, 19 Jun 2024 04:20:03 GMT + +### Updates + +- Add logic to remove outdated .pnpm-sync.json files during rush install or update + +## 5.129.0 +Wed, 19 Jun 2024 03:31:48 GMT + +### Updates + +- Add a new `init-subspace` command to initialize a new subspace. +- Move the `ensureConsistentVersions` setting from `rush.json` to `common/config/rush/common-versions.json`, or to `common/config/rush//common-versions.json` if subspaces are enabled. + +## 5.128.5 +Tue, 18 Jun 2024 04:02:54 GMT + +### Updates + +- Fix a key collision for cobuild clustering for operations that share the same phase name. + +## 5.128.4 +Mon, 17 Jun 2024 23:22:49 GMT + +### Updates + +- Bump the `@azure/identity` package to `~4.2.1` to mitigate GHSA-m5vv-6r4h-3vj9. + +## 5.128.3 +Mon, 17 Jun 2024 20:46:21 GMT + +### Updates + +- Fixed an issue where the --make-consistent flag would affect projects outside the current subspace. + +## 5.128.2 +Mon, 17 Jun 2024 17:08:00 GMT + +### Updates + +- Fix an issue where rush-pnpm patch is not working for the subspace scenario +- Fix an issue where rush update can not detect package.json changes in other subspaces for the injected installation case + +## 5.128.1 +Wed, 12 Jun 2024 20:07:44 GMT + +### Updates + +- Fix an issue where running `rush install` in a subspace with only a `--from` selector is treated as selecting all projects. +- Fix an issue where not published packages are not correctly identified as not published when querying a package feed under certain versions of NPM. +- Fix an issue where selection syntax (like `--to` or `--from`) misses project dependencies declared using workspace alias syntax (i.e. - `workspace:alias@1.2.3`). +- Fix an issue where an error is thrown if a Git email address isn't configured and email validation isn't configured in `rush.json` via `allowedEmailRegExps`. +- Display the name of the subspace when an error is emitted because a dependency hash uses the SHA1 algorithm and the "disallowInsecureSha1" option is enabled. + +## 5.128.0 +Fri, 07 Jun 2024 22:59:12 GMT + +### Updates + +- Graduate the `phasedCommands` experiment to a standard feature. +- Improve `rush init` template for `.gitignore` +- Remove an unnecessary condition in the logic for skipping operations when build cache is disabled. + +## 5.127.1 +Thu, 06 Jun 2024 03:05:21 GMT + +### Updates + +- Remove the second instance of the project name from the project operation filenames in `/rush-logs`. This restores the log filenames to their format before Rush 5.125.0. + +## 5.127.0 +Tue, 04 Jun 2024 00:44:18 GMT + +### Updates + +- Fixes build cache no-op and sharded operation clustering. +- Updated common-veresions.json schema with ensureConsistentVersions property + +## 5.126.0 +Mon, 03 Jun 2024 02:49:05 GMT + +### Updates + +- Fixes a string schema validation warning message when running `rush deploy`. +- Update the functionality that runs external lifecycle processes to be async. +- Move logs into the project `rush-logs` folder regardless of whether or not the `"phasedCommands"` experiment is enabled. +- Update the `nodeSupportedVersionRange` in the `rush init` template to the LTS and current Node versions. +- Update the `pnpmVersion` in the `rush init` template to the latest version of pnpm 8. +- Update the `.gitignore` in the `rush init` template to include some common toolchain output files and folders. +- Include missing `type` modifiers on type-only exports. + +## 5.125.1 +Wed, 29 May 2024 05:39:54 GMT + +### Updates + +- Fix an issue where if `missingScriptBehavior` is set to `"error"` and a script is present and empty, an error would be thrown. + +## 5.125.0 +Sat, 25 May 2024 05:12:20 GMT + +### Updates + +- Fixes a bug where no-op operations were treated as having build cache disabled. +- Adds support for sharding operations during task execution. +- Fix an issue where warnings and errors were not shown in the build summary for all cobuild agents. +- Add a `rush check --subspace` parameter to specify which subspace to analyze +- Rename the subspace level lockfile from `.pnpmfile-subspace.cjs` to `.pnpmfile.cjs`. This is a breaking change for the experimental feature. + +## 5.124.7 +Thu, 23 May 2024 02:27:13 GMT + +### Updates + +- Improve the `usePnpmSyncForInjectedDependencies` experiment to also include any dependency whose lockfile entry has the `file:` protocol, unless it is a tarball reference +- Fix an issue where the build cache analysis was incorrect in rare situations due to a race condition (GitHub #4711) + +## 5.124.6 +Thu, 16 May 2024 01:12:22 GMT + +### Updates + +- Fix an edge case for pnpm-sync when the .pnpm folder is absent but still a valid installation. + +## 5.124.5 +Wed, 15 May 2024 23:43:15 GMT + +### Updates + +- Fix count of completed operations when silent operations are blocked. Add explicit message for child processes terminated by signals. Ensure that errors show up in summarized view. +- Ensure that errors thrown in afterExecuteOperation show up in the summary at the end of the build. + +## 5.124.4 +Wed, 15 May 2024 03:05:57 GMT + +### Updates + +- Improve the detection of PNPM lockfile versions. +- Fix an issue where the `--subspace` CLI parameter would install for all subspaces in a monorepo when passed to the install or update action + +## 5.124.3 +Wed, 15 May 2024 01:18:25 GMT + +### Patches + +- Ensure async telemetry tasks are flushed by error reporter + +### Updates + +- Fix an issue where `rush install` and `rush update` will fail with an `ENAMETOOLONG` error on Windows in repos with a large number of projects. +- Fix an issue where installing multiple subspaces consecutively can cause unexpected cross-contamination between pnpmfiles. + +## 5.124.2 +Fri, 10 May 2024 06:35:26 GMT + +### Updates + +- Fix a recent regression where `rush deploy` did not correctly apply the `additionalProjectsToInclude` setting (GitHub #4683) + +## 5.124.1 +Fri, 10 May 2024 05:33:51 GMT + +### Updates + +- Fix an issue where the `disallowInsecureSha1` policy failed to parse certain lockfile entries +- Fix some minor issues with the "rush init" template files +- Report an error if subspacesFeatureEnabled=true without useWorkspaces=true +- Fix an issue where operation weights were not respected. + +## 5.124.0 +Wed, 08 May 2024 22:24:08 GMT + +### Updates + +- Add a new setting `alwaysInjectDependenciesFromOtherSubspaces` in pnpm-config.json +- Fix a issue where rush install/update can not detect pnpm-sync.json is out of date +- Improve the error message when the pnpm-sync version is outdated +- Fixes a bug where cobuilds would cause a GC error when waiting for long periods of time. +- Fix an issue where tab competions did not suggest parameter values. + +## 5.123.1 +Tue, 07 May 2024 22:38:00 GMT + +### Updates + +- Fix a recent regression where "rush install" would sometimes incorrectly determine whether to skip the install + +## 5.123.0 +Tue, 07 May 2024 18:32:36 GMT + +### Updates + +- Provide the file path if there is an error parsing a `package.json` file. +- Timeline view will now only show terminal build statuses as cobuilt, all other statuses will reflect their original icons. +- Add a `"weight"` property to the `"operation"` object in the project `config/rush-project.json` file that defines an integer weight for how much of the allowed parallelism the operation uses. +- Optimize skipping of unnecessary installs when using filters such as "rush install --to x" + +## 5.122.1 +Tue, 30 Apr 2024 23:36:50 GMT + +### Updates + +- Make `disallowInsecureSha1` policy a subspace-level configuration. +- Fix an issue where `rush update` sometimes did not detect changes to pnpm-config.json + +## 5.122.0 +Thu, 25 Apr 2024 07:33:18 GMT + +### Updates + +- Support rush-pnpm for subspace feature +- Skip determining merge base if given git hash +- (BREAKING CHANGE) Improve the `disallowInsecureSha1` policy to support exemptions for certain package versions. This is a breaking change for the `disallowInsecureSha1` field in pnpm-config.json since Rush 5.119.0. + +## 5.121.0 +Mon, 22 Apr 2024 19:11:26 GMT + +### Updates + +- Add support for auth via microsoft/ado-codespaces-auth vscode extension in `@rushstack/rush-azure-storage-build-cache-plugin` + +## 5.120.6 +Thu, 18 Apr 2024 23:20:02 GMT + +### Updates + +- Fix an issue where "rush deploy" did not correctly deploy build outputs combining multiple Rush subspaces + +## 5.120.5 +Wed, 17 Apr 2024 21:58:17 GMT + +### Updates + +- Fix an issue where rush add affects all packages in a subspace + +## 5.120.4 +Tue, 16 Apr 2024 20:04:25 GMT + +### Updates + +- Fix an issue where `rush deploy` sometimes used an incorrect temp folder when the experimental subspaces feature is enabled + +## 5.120.3 +Tue, 16 Apr 2024 02:59:48 GMT + +### Updates + +- Fix an issue where `pnpm-sync copy` was skipped when a build is restored from build cache. +- Upgrade `tar` dependency to 6.2.1 + +## 5.120.2 +Mon, 15 Apr 2024 00:25:04 GMT + +### Updates + +- Fixes an issue where rush install fails in monorepos with subspaces enabled + +## 5.120.1 +Sat, 13 Apr 2024 18:31:00 GMT + +### Updates + +- Fix an issue where install-run-rush.js sometimes incorrectly invoked .cmd files on Windows OS due to a recent Node.js behavior change. +- Fix an issue with the skip install logic when the experimental subspaces feature is enabled + +## 5.120.0 +Wed, 10 Apr 2024 21:59:57 GMT + +### Updates + +- Bump express. +- Add support for `optionalDependencies` in transitive injected install in the Subspaces feature. +- Update dependency: pnpm-sync-lib@0.2.2 +- Remove a restriction where the repo root would not be found if the CWD is >10 directory levels deep. +- Improve the error message that is printed in a repo using PNPM workspaces when a non-`workspace:` version is used for a project inside the repo. +- Include a missing space in a logging message printed when running `rush add`. +- Clarify the copyright notice emitted in common/scripts/*.js +- Fix an issue with loading of implicitly preferred versions when the experimental subspaces feature is enabled + +## 5.119.0 +Sat, 30 Mar 2024 04:32:31 GMT + +### Updates + +- Add a policy to forbid sha1 hashes in pnpm-lock.yaml. +- (BREAKING API CHANGE) Refactor phased action execution to analyze the repo after the initial operations are created. This removes the `projectChangeAnalyzer` property from the context parameter passed to the `createOperations` hook. + +## 5.118.7 +Thu, 28 Mar 2024 19:55:27 GMT + +### Updates + +- Fix an issue where in the previous release, built-in plugins were not included. + +## 5.118.6 +Wed, 27 Mar 2024 05:31:17 GMT + +### Updates + +- Symlinks are now generated for workspace projects in the temp folder when subspaces and splitWorkspaceCompatibility is enabled. + +## 5.118.5 +Tue, 26 Mar 2024 19:58:40 GMT + +### Updates + +- Use pnpm-sync-lib logging APIs to customize the log message for pnpm-sync operations + +## 5.118.4 +Tue, 26 Mar 2024 02:39:06 GMT + +### Updates + +- Added warnings if there are .npmrc or .pnpmfile.cjs files in project folders after migrating to subspaces + +## 5.118.3 +Sat, 23 Mar 2024 01:41:10 GMT + +### Updates + +- Fix an edge case for computing the PNPM store path when the experimental subspaces feature is enabled + +## 5.118.2 +Fri, 22 Mar 2024 17:30:47 GMT + +### Updates + +- Fix bugs related to path operation in Windows OS for subspace feature + +## 5.118.1 +Thu, 21 Mar 2024 16:39:32 GMT + +### Updates + +- Support PNPM injected installation in Rush subspace feature + +## 5.118.0 +Wed, 20 Mar 2024 20:45:18 GMT + +### Updates + +- (BREAKING API CHANGE) Rename `AzureAuthenticationBase._getCredentialFromDeviceCodeAsync` to `AzureAuthenticationBase._getCredentialFromTokenAsync` in `@rushstack/rush-azure-storage-build-cache-plugin`. Adding support for InteractiveBrowserCredential. + +## 5.117.10 +Wed, 20 Mar 2024 04:57:57 GMT + +### Updates + +- Improve the "splitWorkspaceCompatibility" setting to simulate hoisted dependencies when the experimental Rush subspaces feature is enabled + +## 5.117.9 +Tue, 12 Mar 2024 19:15:07 GMT + +### Updates + +- Add functionality to disable filtered installs for specific subspaces + +## 5.117.8 +Sat, 09 Mar 2024 01:11:16 GMT + +### Updates + +- Fixes a bug where the syncNpmrc function incorrectly uses the folder instead of the path + +## 5.117.7 +Fri, 08 Mar 2024 23:45:24 GMT + +### Updates + +- Fix an issue where, when the experimental subspace feature is enabled, the subspace's ".npmrc" file did not take precedence over ".npmrc-global". + +## 5.117.6 +Thu, 07 Mar 2024 19:35:20 GMT + +### Updates + +- Fixes an issue where cobuilds would write success with warnings as successful cache entries. + +## 5.117.5 +Wed, 06 Mar 2024 23:03:27 GMT + +### Updates + +- Add filtered installs for subspaces + +## 5.117.4 +Tue, 05 Mar 2024 21:15:26 GMT + +### Updates + +- Add support for subspace level scoped pnpm-config.json e.g. `common/config/subspaces/default/pnpm-config.json` + +## 5.117.3 +Tue, 05 Mar 2024 01:19:42 GMT + +### Updates + +- Fix an issue where if a patch is removed from `common/pnpm-patches` after `rush install` had already been run with that patch present, pnpm would try to continue applying the patch. +- Intercept the output printed by `rush-pnpm patch` to update the next step's instructions to run `rush-pnpm patch-commit ...` instead of `pnpm patch-commit ...`. + +## 5.117.2 +Fri, 01 Mar 2024 23:12:43 GMT + +### Updates + +- Fix an issue with the experimental subspaces feature, where version checks incorrectly scanned irrelevant subspaces. + +## 5.117.1 +Thu, 29 Feb 2024 07:34:31 GMT + +### Updates + +- Update "rush init" template to document the new build-cache.json constants +- Remove trailing slashes from `node_modules` and `jspm_packages` paths in the `.gitignore` file generated by `rush init`. +- Introduce a `RushCommandLine` API that exposes an object representing the skeleton of the Rush command-line. +- Fix an issue where, when the experimental subspaces feature was enabled, the lockfile validation would check irrelevant subspaces + +## 5.117.0 +Mon, 26 Feb 2024 21:39:36 GMT + +### Updates + +- Include the ability to add `[os]` and `[arch]` tokens to cache entry name patterns. +- (BREAKING CHANGE) Remove the 'installation variants' feature and its related APIs, which have been superceded by the Subspaces feature. +- Extract the "rush.json" filename to a constant as `RushConstants.rushJsonFilename`. + +## 5.116.0 +Mon, 26 Feb 2024 20:04:02 GMT + +### Updates + +- Upgrade the `pnpm-sync-lib` dependency version. +- Handle `workspace:~` and `workspace:^` wildcard specifiers when publishing. They remain as-is in package.json but get converted to `~${current}` and `^${current}` in changelogs. +- Validate that the "projectFolder" and "publishFolder" fields in the "projects" list in "rush.json" are normalized POSIX relative paths that do not end in trailing "/" or contain "\\". + +## 5.115.0 +Thu, 22 Feb 2024 01:36:27 GMT + +### Updates + +- Add a "runWithTerminalAsync" resource lifetime helper to `IOperationRunnerContext` to manage the creation and cleanup of logging for operation execution. +- Adds a new experiment `useIPCScriptsInWatchMode`. When this flag is enabled and Rush is running in watch mode, it will check for npm scripts named `_phase::ipc`, and if found, use them instead of the normal invocation of `_phase:`. When doing so, it will provide an IPC channel to the child process and expect the child to outlive the current build pass. + +## 5.114.3 +Thu, 22 Feb 2024 00:10:32 GMT + +### Updates + +- Replace deprecated function, and fix a path bug in Windows env + +## 5.114.2 +Wed, 21 Feb 2024 21:45:46 GMT + +### Updates + +- Replace the dependency on the `colors` package with `Colorize` from `@rushstack/terminal`. + +## 5.114.1 +Wed, 21 Feb 2024 08:56:05 GMT + +### Updates + +- Improve `rush scan` to analyze APIs such as `Import.lazy()` and `await import()` +- Fix a recent regression where `@rushstack/rush-sdk` did not declare its dependency on `@rushstack/terminal` + +## 5.114.0 +Mon, 19 Feb 2024 21:54:44 GMT + +### Updates + +- (EXPERIMENTAL) Add `enablePnpmSyncForInjectedDependenciesMeta` to experiments.json; it is part of an upcoming feature for managing PNPM "injected" dependencies: https://www.npmjs.com/package/pnpm-sync +- Include a `pnpmPatchesCommonFolderName` constant for the folder name "pnpm-patches" that gets placed under "common". +- Add a feature to generate a `project-impact-graph.yaml` file in the repo root. This feature is gated under the new `generateProjectImpactGraphDuringRushUpdate` experiment. +- Fix a formatting issue with the LICENSE. +- Fix an issue with filtered installs when the experimental subspaces feature is enabled + +## 5.113.4 +Wed, 31 Jan 2024 22:49:17 GMT + +### Updates + +- Introduce an explicit warning message during `rush install` or `rush update` about `dependenciesMeta` not being up-to-date. + +## 5.113.3 +Wed, 31 Jan 2024 22:25:55 GMT + +### Updates + +- Fix an issue where `rush update` would sometimes not correctly sync the `pnpm-lock.yaml` file back to `common/config/rush/` after a project's `package.json` has been updated. + +## 5.113.2 +Wed, 31 Jan 2024 18:45:33 GMT + +### Updates + +- Fix some minor issues when the experimental subspaces feature is enabled + +## 5.113.1 +Wed, 31 Jan 2024 07:07:50 GMT + +### Updates + +- (EXPERIMENTAL) Enable filtered installs of subspaces and add a "preventSelectingAllSubspaces" setting + +## 5.113.0 +Tue, 30 Jan 2024 22:58:52 GMT + +### Updates + +- Fix an issue where Rush does not detect changes to the `dependenciesMeta` field in project's `package.json` files, so may incorrectly skip updating/installation. +- Add ability to enable IPC channels in `Utilities#executeLifeCycleCommand`. +- Update `rush init` template to document the "buildSkipWithAllowWarningsInSuccessfulBuild" experiment +- (BREAKING CHANGE) Begin removal of APIs for the deprecated "installation variants" feature, since subspaces are a more robust solution for that problem +- (EXPERIMENTAL) Implement installation for the not-yet-released "subspaces" feature (GitHub #4230) + +## 5.112.2 +Tue, 12 Dec 2023 00:20:51 GMT + +### Updates + +- Bring back the erroneously removed `preminor` bump type for lockstepped packages. +- Fix an issue where the contents of a folder set in the `"folderToCopy"` field of the `deploy.json` config file would be copied into a subfolder instead of into the root of the deploy folder. +- (EXPERIMENTAL) Implemented config file loader for the not-yet-released "subspaces" feature (GitHub #4230) + +## 5.112.1 +Wed, 29 Nov 2023 08:59:31 GMT + +### Updates + +- Allow the device code credential options to be extended Azure authentication subclasses, used in advanced authentication scenarios. + +## 5.112.0 +Mon, 27 Nov 2023 23:36:11 GMT + +### Updates + +- Update the `@azure/identity` and `@azure/storage-blob` dependencies of `@rushstack/rush-azure-storage-build-cache-plugin` to eliminate an `EBADENGINE` error when installing Rush on Node 20. + +## 5.111.0 +Sat, 18 Nov 2023 00:06:20 GMT + +### Updates + +- Add experiment `buildSkipWithAllowWarningsInSuccessfulBuild` to allow skipping builds that succeeded with warnings in the previous run. + +## 5.110.2 +Thu, 16 Nov 2023 01:36:10 GMT + +_Version update only_ + +## 5.110.1 +Wed, 01 Nov 2023 23:29:47 GMT + +### Updates + +- Fix line endings in published package. + +## 5.110.0 +Mon, 30 Oct 2023 23:37:07 GMT + +### Updates + +- Include the filename of the shrinkwrap file in logging messages for all package managers, not just Yarn. +- performance improvements by running asynchronous code concurrently using Promise.all + +## 5.109.2 +Fri, 20 Oct 2023 01:54:21 GMT + +### Updates + +- Allow the output preservation incremental strategy if the build cache is configured but disabled. When running in verbose mode, log the incremental strategy that is being used. +- Log the cache key in `--verbose` mode when the cache is successfully read from or written to. +- Fix an issue where console colors were sometimes not enabled correctly during `rush install` +- Fix an issue where running `rush update-cloud-credentials --interactive` sometimes used the wrong working directory when invoked in a repo configured to use the `http` build cache provider (GitHub #4396) + +## 5.109.1 +Sat, 07 Oct 2023 01:20:56 GMT + +### Updates + +- Fix incorrect capitalization in the "rush init" template + +## 5.109.0 +Sat, 07 Oct 2023 00:25:27 GMT + +### Updates + +- (IMPORTANT) Add a new setting `autoInstallPeers` in pnpm-config.json; be aware that Rush changes PNPM's default if you are using PNPM 8 or newer +- (IMPORTANT) After upgrading, if `rush install` fails with `ERR_PNPM_LOCKFILE_CONFIG_MISMATCH`, please run `rush update --recheck` +- Improve visual formatting of custom tips +- Add start `preRushx` and `postRushx` event hooks for monitoring the `rushx` command +- Update the oldest usable Node.js version to 14.18.0, since 14.17.0 fails to load + +## 5.108.0 +Mon, 02 Oct 2023 20:23:27 GMT + +### Updates + +- Fix an issue where `rush purge` fails on Linux and Mac if the `common/temp/rush-recycler` folder does not exist. +- Add "--offline" parameter for "rush install" and "rush update" +- Ignore pause/resume watcher actions when the process is not TTY mode + +## 5.107.4 +Tue, 26 Sep 2023 21:02:52 GMT + +### Updates + +- Update type-only imports to include the type modifier. +- Make the project watcher status and keyboard commands message more visible. + +## 5.107.3 +Fri, 22 Sep 2023 09:01:38 GMT + +### Updates + +- Fix filtered installs in pnpm@8. + +## 5.107.2 +Fri, 22 Sep 2023 00:06:12 GMT + +### Updates + +- Fix a bug in which an operation failing incorrectly does not block its consumers. +- Add `resolutionMode` to `rush init` template for pnpm-config.json + +## 5.107.1 +Tue, 19 Sep 2023 21:13:23 GMT + +### Updates + +- Fix pnpm's install status printing when pnpm custom tips are defined. + +## 5.107.0 +Tue, 19 Sep 2023 00:36:50 GMT + +### Updates + +- Update @types/node from 14 to 18 +- Remove previously removed fields from the `custom-tips.json` schema. +- (BREAKING API CHANGE) Refactor the `CustomTipsConfiguration` by removing the `configuration` property and adding a `providedCustomTipsByTipId` map property. +- Fix an issue where pnpm would would not rewrite the current status line on a TTY console, and instead would print a series of separate status lines during installation. Note that this is only fixed when there are no custom PNPM tips provided. +- Add "Waiting" operation status for operations that have one or more dependencies still pending. Ensure that the `onOperationStatusChanged` hook fires for every status change. +- Add support for optional build status notifications over a web socket connection to `@rushstack/rush-serve-plugin`. +- Add pause/resume option to project watcher + +## 5.106.0 +Thu, 14 Sep 2023 09:20:11 GMT + +### Updates + +- (IMPORTANT) Add a new setting `resolutionMode` in pnpm-config.json; be aware that Rush now overrides the default behavior if you are using PNPM 8.0.0 through 8.6.12 (GitHub #4283) +- Support adding custom tips for pnpm-printed logs +- (BREAKING CHANGE) Remove the "defaultMessagePrefix" config in custom-tips.json +- Rename the `PnpmStoreOptions` type to `PnpmStoreLocation`. + +## 5.105.0 +Fri, 08 Sep 2023 04:09:06 GMT + +### Updates + +- Disable build cache writes in watch rebuilds. +- Fix the instance of "ICreateOperationsContext" passed to the "beforeExecuteOperations" hook in watch mode rebuilds to match the instance passed to the "createOperations" hook. +- Fix an issue where the error message printed when two phases have overlapping output folders did not mention both phases. +- Update the phase output folders validation to only check for overlapping folders for phases that actually execute an operation in a given project. +- Add the "disableBuildCache" option to the schema for phased commands (it is already present for bulk commands). Update the behavior of the "disableBuildCache" flag to also disable the legacy skip detection, in the event that the build cache is not configured. + +## 5.104.1 +Tue, 05 Sep 2023 18:53:03 GMT + +### Updates + +- Fix an issue where `rush init` generated a `cobuild.json` file that reported errors (GitHub #4307) + +## 5.104.0 +Fri, 01 Sep 2023 04:54:16 GMT + +### Updates + +- (EXPERIMENTAL) Initial release of the cobuild feature, a cheap way to distribute jobs Rush builds across multiple VMs. (GitHub #3485) + +## 5.103.0 +Thu, 31 Aug 2023 23:28:28 GMT + +### Updates + +- Add dependencySettings field to Rush deploy.json configurations. This will allow developers to customize how third party dependencies are processed when running `rush deploy` +- Fix an issue where `rush update-autoinstaller` sometimes did not fully upgrade the lockfile +- Fix an issue where "undefined" was sometimes printed instead of a blank line + +## 5.102.0 +Tue, 15 Aug 2023 20:09:40 GMT + +### Updates + +- Add a new config file "custom-tips.json" for customizing Rush messages (GitHub #4207) +- Improve "rush scan" to recognize module patterns such as "import get from 'lodash.get'" +- Update Node.js version checks to support the new LTS release +- Update "rush init" template to use PNPM 7.33.5 +- Update the "rush init" template's .gitignore to avoid spurious diffs for files such as "autoinstaller.lock" +- Fix an issue where a pnpm-lock file would fail to parse if a project used a package alias in a repo using pnpm 8. +- Fix HTTP/1 backwards compatibility in rush-serve-plugin. +- Add experiment "usePnpmLockfileOnlyThenFrozenLockfileForRushUpdate" that, when running `rush update`, performs first a `--lockfile-only` update to the lockfile, then a `--frozen-lockfile` installation. This mitigates issues that may arise when using the `afterAllResolved` hook in `.pnpmfile.cjs`. + +## 5.101.1 +Fri, 11 Aug 2023 17:57:55 GMT + +### Updates + +- Fix a regression from 5.101.0 where publishing features did not detect changes properly when running on Windows OS (GitHub #4277) +- Add support in rush-serve-plugin for HTTP/2, gzip compression, and CORS preflight requests. + +## 5.101.0 +Tue, 08 Aug 2023 07:11:02 GMT + +### Updates + +- Enable the "http" option for build-cache providers +- Switch from glob to fast-glob. +- Reduce false positive detections of the pnpm shrinkwrap file being out of date in the presence of the `globalOverrides` setting in `pnpm-config.json`, or when a dependency is listed in both `dependencies` and `devDependencies` in the same package. +- @rushstack/rush-sdk now exposes a secondary API for manually loading the Rush engine and monitoring installation progress +- Add support for npm aliases in `PnpmShrinkwrapFile._getPackageId`. +- Improve version resolution logic in common/scripts/install-run.js (see https://github.com/microsoft/rushstack/issues/4256) +- Add `patternsToInclude` and `patternsToExclude` support to Rush deploy.json configurations. This will allow developers to include or exclude provided glob patterns within a local project when running `rush deploy`. + +## 5.100.2 +Mon, 24 Jul 2023 18:54:49 GMT + +### Patches + +- Fix an issue where the git pre-push hook would allow push to go through if the script exited with error. + +### Updates + +- Updated semver dependency + +## 5.100.1 +Wed, 14 Jun 2023 19:42:12 GMT + +### Updates + +- Fix an issue where Rush would attempt to open a project's log file for writing twice. +- Fix an issue where arguments weren't passed to git hook scripts. + +## 5.100.0 +Tue, 13 Jun 2023 01:49:21 GMT + +### Updates + +- (BREAKING API CHANGE) Remove unused members of the `BumpType` API. See https://github.com/microsoft/rushstack/issues/1335 for details. +- Add `--peer` flag to `rush add` command to add peerDependencies +- Add support for PNPM 8. +- Remove the dependency on `lodash`. +- Add functionality for the Amazon S3 Build Cache Plugin to read credentials from common AWS_* environment variables. +- Write cache logs to their own file(s). +- Fix an issue where cache logging data was always written to stdout. +- Generate scripts in the Git hooks folder referring to the actual hook implementations in-place in the Rush `common/git-hooks/` folder instead of copying the scripts to the Git hooks folder. +- Bump webpack to v5.82.1 + +## 5.99.0 +Fri, 02 Jun 2023 22:08:28 GMT + +### Updates + +- Use a separate temrinal for logging cache subsystem +- Expose beforeLog hook +- Convert to multi-phase Heft +- Use `JSON.parse` instead of `jju` to parse `package.json` files for faster performance. ## 5.98.0 Sun, 21 May 2023 00:18:35 GMT @@ -83,7 +1691,7 @@ Fri, 17 Feb 2023 02:14:43 GMT ### Updates -- Add code path to @rushstack/rush-sdk for inheriting @microsoft/rush-lib location from a parent process via the RUSH_LIB_PATH environment variable. +- Add code path to @rushstack/rush-sdk for inheriting @microsoft/rush-lib location from a parent process via the _RUSH_LIB_PATH environment variable. ## 5.92.0 Sun, 12 Feb 2023 02:50:42 GMT diff --git a/apps/rush/UPGRADING.md b/apps/rush/UPGRADING.md index cb78f05e251..000e3bbb3fc 100644 --- a/apps/rush/UPGRADING.md +++ b/apps/rush/UPGRADING.md @@ -1,5 +1,47 @@ # Upgrade notes for @microsoft/rush +### Rush 5.135.0 + +This release of Rush deprecates the `rush-project.json`'s `operationSettings.sharding.shardOperationSettings` +option in favor of defining a separate operation with a `:shard` suffix. This will only affect projects that +have opted into sharding and have custom sharded operation settings. + +To migrate, +**`rush-project.json`** (OLD) +```json +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "sharding": { + "count": 4, + "shardOperationSettings": { + "weight": 4 + } + }, + } + ] +} +``` + +**`rush-project.json`** (NEW) +```json +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "sharding": { + "count": 4, + }, + }, + { + "operationName": "_phase:build:shard", // note the suffix here + "weight": 4 + } + ] +} +``` + ### Rush 5.60.0 This release of Rush includes a breaking change for the experiment build cache feature. It only affects diff --git a/apps/rush/bin/rush b/apps/rush/bin/rush index 783bb806fce..eef2fc27066 100755 --- a/apps/rush/bin/rush +++ b/apps/rush/bin/rush @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('../lib/start.js') +require('../lib-commonjs/start.js'); diff --git a/apps/rush/bin/rush-pnpm b/apps/rush/bin/rush-pnpm old mode 100644 new mode 100755 index aee68e80224..eef2fc27066 --- a/apps/rush/bin/rush-pnpm +++ b/apps/rush/bin/rush-pnpm @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('../lib/start.js'); +require('../lib-commonjs/start.js'); diff --git a/apps/rush/bin/rushx b/apps/rush/bin/rushx index aee68e80224..eef2fc27066 100755 --- a/apps/rush/bin/rushx +++ b/apps/rush/bin/rushx @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('../lib/start.js'); +require('../lib-commonjs/start.js'); diff --git a/apps/rush/config/jest.config.json b/apps/rush/config/jest.config.json index 4bb17bde3ee..d1749681d90 100644 --- a/apps/rush/config/jest.config.json +++ b/apps/rush/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "local-node-rig/profiles/default/config/jest.config.json" } diff --git a/apps/rush/config/rig.json b/apps/rush/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/apps/rush/config/rig.json +++ b/apps/rush/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/apps/rush/eslint.config.js b/apps/rush/eslint.config.js new file mode 100644 index 00000000000..ceb5a1bee40 --- /dev/null +++ b/apps/rush/eslint.config.js @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + }, + rules: { + 'no-console': 'off' + } + } +]; diff --git a/apps/rush/package.json b/apps/rush/package.json index bd9211859a1..c2cbfab5abe 100644 --- a/apps/rush/package.json +++ b/apps/rush/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/rush", - "version": "5.98.0", + "version": "5.178.1", "description": "A professional solution for consolidating all your JavaScript projects in one Git repo", "keywords": [ "install", @@ -38,18 +38,37 @@ "dependencies": { "@microsoft/rush-lib": "workspace:*", "@rushstack/node-core-library": "workspace:*", - "colors": "~1.2.1", - "semver": "~7.3.0" + "@rushstack/terminal": "workspace:*", + "semver": "~7.7.4" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*", "@rushstack/rush-amazon-s3-build-cache-plugin": "workspace:*", "@rushstack/rush-azure-storage-build-cache-plugin": "workspace:*", "@rushstack/rush-http-build-cache-plugin": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "@types/semver": "7.3.5" - } + "@rushstack/rush-serve-plugin": "workspace:*", + "@types/semver": "7.7.1" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "sideEffects": [ + "lib-commonjs/start.js", + "lib-esm/start.js" + ] } diff --git a/apps/rush/src/MinimalRushConfiguration.ts b/apps/rush/src/MinimalRushConfiguration.ts index 7078f4af812..0cc4436b964 100644 --- a/apps/rush/src/MinimalRushConfiguration.ts +++ b/apps/rush/src/MinimalRushConfiguration.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; import { JsonFile } from '@rushstack/node-core-library'; import { RushConfiguration } from '@microsoft/rush-lib'; @@ -37,17 +37,13 @@ export class MinimalRushConfiguration { showVerbose: !RushCommandLineParser.shouldRestrictConsoleOutput() }); if (rushJsonLocation) { - return MinimalRushConfiguration._loadFromConfigurationFile(rushJsonLocation); - } else { + const minimalRushConfigurationJson: IMinimalRushConfigurationJson | undefined = + _loadConfigurationJson(rushJsonLocation); + if (minimalRushConfigurationJson) { + return new MinimalRushConfiguration(minimalRushConfigurationJson, rushJsonLocation); + } return undefined; - } - } - - private static _loadFromConfigurationFile(rushJsonFilename: string): MinimalRushConfiguration | undefined { - try { - const minimalRushConfigurationJson: IMinimalRushConfigurationJson = JsonFile.load(rushJsonFilename); - return new MinimalRushConfiguration(minimalRushConfigurationJson, rushJsonFilename); - } catch (e) { + } else { return undefined; } } @@ -73,3 +69,11 @@ export class MinimalRushConfiguration { return this._commonRushConfigFolder; } } + +function _loadConfigurationJson(rushJsonFilename: string): IMinimalRushConfigurationJson | undefined { + try { + return JsonFile.load(rushJsonFilename); + } catch (e) { + return undefined; + } +} diff --git a/apps/rush/src/RushCommandSelector.ts b/apps/rush/src/RushCommandSelector.ts index 8264b060f21..d85f00c5a91 100644 --- a/apps/rush/src/RushCommandSelector.ts +++ b/apps/rush/src/RushCommandSelector.ts @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; -import * as rushLib from '@microsoft/rush-lib'; +import * as path from 'node:path'; + +import type { ILaunchOptions } from '@microsoft/rush-lib/lib/index'; +import { Colorize } from '@rushstack/terminal'; type CommandName = 'rush' | 'rush-pnpm' | 'rushx' | undefined; @@ -16,9 +17,9 @@ type CommandName = 'rush' | 'rush-pnpm' | 'rushx' | undefined; */ export class RushCommandSelector { public static failIfNotInvokedAsRush(version: string): void { - const commandName: CommandName = RushCommandSelector._getCommandName(); + const commandName: CommandName = _getCommandName(); if (commandName !== 'rush' && commandName !== undefined) { - RushCommandSelector._failWithError( + _failWithError( `This repository is using Rush version ${version} which does not support the ${commandName} command` ); } @@ -26,22 +27,21 @@ export class RushCommandSelector { public static execute( launcherVersion: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - selectedRushLib: any, - options: rushLib.ILaunchOptions + selectedRushLib: typeof import('@microsoft/rush-lib'), + options: ILaunchOptions ): void { - const Rush: typeof rushLib.Rush = selectedRushLib.Rush; + const { Rush } = selectedRushLib; if (!Rush) { // This should be impossible unless we somehow loaded an unexpected version - RushCommandSelector._failWithError(`Unable to find the "Rush" entry point in @microsoft/rush-lib`); + _failWithError(`Unable to find the "Rush" entry point in @microsoft/rush-lib`); } - const commandName: CommandName = RushCommandSelector._getCommandName(); + const commandName: CommandName = _getCommandName(); if (commandName === 'rush-pnpm') { if (!Rush.launchRushPnpm) { - RushCommandSelector._failWithError( + _failWithError( `This repository is using Rush version ${Rush.version}` + ` which does not support the "rush-pnpm" command` ); @@ -52,7 +52,7 @@ export class RushCommandSelector { }); } else if (commandName === 'rushx') { if (!Rush.launchRushX) { - RushCommandSelector._failWithError( + _failWithError( `This repository is using Rush version ${Rush.version}` + ` which does not support the "rushx" command` ); @@ -62,28 +62,28 @@ export class RushCommandSelector { Rush.launch(launcherVersion, options); } } +} - private static _failWithError(message: string): never { - console.log(colors.red(message)); - return process.exit(1); - } +function _failWithError(message: string): never { + console.log(Colorize.red(message)); + return process.exit(1); +} - private static _getCommandName(): CommandName { - if (process.argv.length >= 2) { - // Example: - // argv[0]: "C:\\Program Files\\nodejs\\node.exe" - // argv[1]: "C:\\Program Files\\nodejs\\node_modules\\@microsoft\\rush\\bin\\rushx" - const basename: string = path.basename(process.argv[1]).toUpperCase(); - if (basename === 'RUSH') { - return 'rush'; - } - if (basename === 'RUSH-PNPM') { - return 'rush-pnpm'; - } - if (basename === 'RUSHX') { - return 'rushx'; - } +function _getCommandName(): CommandName { + if (process.argv.length >= 2) { + // Example: + // argv[0]: "C:\\Program Files\\nodejs\\node.exe" + // argv[1]: "C:\\Program Files\\nodejs\\node_modules\\@microsoft\\rush\\bin\\rushx" + const basename: string = path.basename(process.argv[1]).toUpperCase(); + if (basename === 'RUSH') { + return 'rush'; + } + if (basename === 'RUSH-PNPM') { + return 'rush-pnpm'; + } + if (basename === 'RUSHX') { + return 'rushx'; } - return undefined; } + return undefined; } diff --git a/apps/rush/src/RushVersionSelector.ts b/apps/rush/src/RushVersionSelector.ts index 7f00b5a0b57..615aaa0e356 100644 --- a/apps/rush/src/RushVersionSelector.ts +++ b/apps/rush/src/RushVersionSelector.ts @@ -1,15 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import * as semver from 'semver'; -import { LockFile } from '@rushstack/node-core-library'; +import { LockFile, Import } from '@rushstack/node-core-library'; import { Utilities } from '@microsoft/rush-lib/lib/utilities/Utilities'; -import { _LastInstallFlag, _RushGlobalFolder, ILaunchOptions } from '@microsoft/rush-lib'; +import { _FlagFile, _RushGlobalFolder, type ILaunchOptions } from '@microsoft/rush-lib'; import { RushCommandSelector } from './RushCommandSelector'; -import { MinimalRushConfiguration } from './MinimalRushConfiguration'; +import type { MinimalRushConfiguration } from './MinimalRushConfiguration'; const MAX_INSTALL_ATTEMPTS: number = 3; @@ -30,11 +31,12 @@ export class RushVersionSelector { const isLegacyRushVersion: boolean = semver.lt(version, '4.0.0'); const expectedRushPath: string = path.join(this._rushGlobalFolder.nodeSpecificPath, `rush-${version}`); - const installMarker: _LastInstallFlag = new _LastInstallFlag(expectedRushPath, { + const installMarker: _FlagFile = new _FlagFile(expectedRushPath, 'last-install', { node: process.versions.node }); - if (!installMarker.isValid()) { + let installIsValid: boolean = await installMarker.isValidAsync(); + if (!installIsValid) { // Need to install Rush console.log(`Rush version ${version} is not currently installed. Installing...`); @@ -42,11 +44,12 @@ export class RushVersionSelector { console.log(`Trying to acquire lock for ${resourceName}`); - const lock: LockFile = await LockFile.acquire(expectedRushPath, resourceName); - if (installMarker.isValid()) { + const lock: LockFile = await LockFile.acquireAsync(expectedRushPath, resourceName); + installIsValid = await installMarker.isValidAsync(); + if (installIsValid) { console.log('Another process performed the installation.'); } else { - Utilities.installPackageInDirectory({ + await Utilities.installPackageInDirectoryAsync({ directory: expectedRushPath, packageName: isLegacyRushVersion ? '@microsoft/rush' : '@microsoft/rush-lib', version: version, @@ -59,13 +62,16 @@ export class RushVersionSelector { // different implementations of the same version of the same package. // This was needed for: https://github.com/microsoft/rushstack/issues/691 commonRushConfigFolder: configuration ? configuration.commonRushConfigFolder : undefined, - suppressOutput: true + suppressOutput: true, + // Filter out npm-incompatible properties (e.g. pnpm-specific settings) from .npmrc + // since this installation always uses npm regardless of the repo's package manager. + filterNpmIncompatibleProperties: true }); console.log(`Successfully installed Rush version ${version} in ${expectedRushPath}.`); // If we've made it here without exception, write the flag file - installMarker.create(); + await installMarker.createAsync(); lock.release(); } @@ -82,15 +88,15 @@ export class RushVersionSelector { RushCommandSelector.failIfNotInvokedAsRush(version); require(path.join(expectedRushPath, 'node_modules', '@microsoft', 'rush', 'lib', 'start')); } else { + // Explicitly resolve the entry point for rush-lib, rather than using a simple require, because + // newer versions of rush-lib use the package.json `exports` field, which maps + // `lib/index` to `lib-commonjs/index.js` + const rushLibEntrypoint: string = await Import.resolveModuleAsync({ + modulePath: '@microsoft/rush-lib/lib/index', + baseFolderPath: expectedRushPath + }); + const rushCliEntrypoint: typeof import('@microsoft/rush-lib') = require(rushLibEntrypoint); // For newer rush-lib, RushCommandSelector can test whether "rushx" is supported or not - const rushCliEntrypoint: {} = require(path.join( - expectedRushPath, - 'node_modules', - '@microsoft', - 'rush-lib', - 'lib', - 'index' - )); RushCommandSelector.execute(this._currentPackageVersion, rushCliEntrypoint, executeOptions); } } diff --git a/apps/rush/src/start-dev-docs.ts b/apps/rush/src/start-dev-docs.ts index de91c35a30c..4bd9539a8bb 100644 --- a/apps/rush/src/start-dev-docs.ts +++ b/apps/rush/src/start-dev-docs.ts @@ -1,6 +1,9 @@ -import { Colors, ConsoleTerminalProvider, Terminal } from '@rushstack/node-core-library'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { Colorize, ConsoleTerminalProvider, Terminal } from '@rushstack/terminal'; const terminal: Terminal = new Terminal(new ConsoleTerminalProvider()); terminal.writeLine('For instructions on debugging Rush, please see this documentation:'); -terminal.writeLine(Colors.bold('https://rushjs.io/pages/contributing/debugging/')); +terminal.writeLine(Colorize.bold('https://rushjs.io/pages/contributing/#debugging-rush')); diff --git a/apps/rush/src/start-dev.ts b/apps/rush/src/start-dev.ts index 46e79f2c0b5..bba3469421f 100644 --- a/apps/rush/src/start-dev.ts +++ b/apps/rush/src/start-dev.ts @@ -4,7 +4,7 @@ // This file is used during development to load the built-in plugins and to bypass // some other checks -import * as rushLib from '@microsoft/rush-lib/lib/index'; +import * as rushLib from '@microsoft/rush-lib'; import { PackageJsonLookup, Import } from '@rushstack/node-core-library'; import { RushCommandSelector } from './RushCommandSelector'; @@ -20,7 +20,8 @@ function includePlugin(pluginName: string, pluginPackageName?: string): void { pluginName: pluginName, pluginPackageFolder: Import.resolvePackage({ packageName: pluginPackageName, - baseFolderPath: __dirname + baseFolderPath: __dirname, + useNodeJSResolver: true }) }); } @@ -28,6 +29,7 @@ function includePlugin(pluginName: string, pluginPackageName?: string): void { includePlugin('rush-amazon-s3-build-cache-plugin'); includePlugin('rush-azure-storage-build-cache-plugin'); includePlugin('rush-http-build-cache-plugin'); +includePlugin('rush-serve-plugin'); // Including this here so that developers can reuse it without installing the plugin a second time includePlugin('rush-azure-interactive-auth-plugin', '@rushstack/rush-azure-storage-build-cache-plugin'); diff --git a/apps/rush/src/start.ts b/apps/rush/src/start.ts index fb5af3d130d..bf8d5927230 100644 --- a/apps/rush/src/start.ts +++ b/apps/rush/src/start.ts @@ -5,6 +5,7 @@ // we check to see if the Node.js version is too old. If, for whatever reason, Rush crashes with // an old Node.js version when evaluating one of the more complex imports, we'll at least // shown a meaningful error message. +// eslint-disable-next-line import/order import { NodeJsCompatibility } from '@microsoft/rush-lib/lib/logic/NodeJsCompatibility'; if (NodeJsCompatibility.reportAncientIncompatibleVersion()) { @@ -18,17 +19,14 @@ const alreadyReportedNodeTooNewError: boolean = NodeJsCompatibility.warnAboutVer alreadyReportedNodeTooNewError: false }); -import colors from 'colors/safe'; -import * as os from 'os'; +import * as os from 'node:os'; + import * as semver from 'semver'; -import { - ConsoleTerminalProvider, - Text, - PackageJsonLookup, - ITerminalProvider -} from '@rushstack/node-core-library'; +import { Text, PackageJsonLookup } from '@rushstack/node-core-library'; +import { Colorize, ConsoleTerminalProvider, type ITerminalProvider } from '@rushstack/terminal'; import { EnvironmentVariableNames } from '@microsoft/rush-lib'; +import type { ILaunchOptions } from '@microsoft/rush-lib'; import * as rushLib from '@microsoft/rush-lib'; import { RushCommandSelector } from './RushCommandSelector'; @@ -48,7 +46,7 @@ const previewVersion: string | undefined = process.env[EnvironmentVariableNames. if (previewVersion) { if (!semver.valid(previewVersion, false)) { console.error( - colors.red(`Invalid value for RUSH_PREVIEW_VERSION environment variable: "${previewVersion}"`) + Colorize.red(`Invalid value for RUSH_PREVIEW_VERSION environment variable: "${previewVersion}"`) ); process.exit(1); } @@ -74,7 +72,7 @@ if (previewVersion) { `*********************************************************************` ); - console.error(lines.map((line) => colors.black(colors.bgYellow(line))).join(os.EOL)); + console.error(lines.map((line) => Colorize.black(Colorize.yellowBackground(line))).join(os.EOL)); } else if (configuration) { rushVersionToLoad = configuration.rushVersion; } @@ -90,7 +88,7 @@ const isManaged: boolean = !!configuration; const terminalProvider: ITerminalProvider = new ConsoleTerminalProvider(); -const launchOptions: rushLib.ILaunchOptions = { isManaged, alreadyReportedNodeTooNewError, terminalProvider }; +const launchOptions: ILaunchOptions = { isManaged, alreadyReportedNodeTooNewError, terminalProvider }; // If we're inside a repo folder, and it's requesting a different version, then use the RushVersionManager to // install it @@ -99,7 +97,7 @@ if (rushVersionToLoad && rushVersionToLoad !== currentPackageVersion) { versionSelector .ensureRushVersionInstalledAsync(rushVersionToLoad, configuration, launchOptions) .catch((error: Error) => { - console.log(colors.red('Error: ' + error.message)); + console.log(Colorize.red('Error: ' + error.message)); }); } else { // Otherwise invoke the rush-lib that came with this rush package diff --git a/apps/rush/src/test/MinimalRushConfiguration.test.ts b/apps/rush/src/test/MinimalRushConfiguration.test.ts index dccbe83783f..391c9feeeb2 100644 --- a/apps/rush/src/test/MinimalRushConfiguration.test.ts +++ b/apps/rush/src/test/MinimalRushConfiguration.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; import { MinimalRushConfiguration } from '../MinimalRushConfiguration'; diff --git a/apps/rush/src/test/sandbox/legacy-repo/project/package.json b/apps/rush/src/test/sandbox/legacy-repo/project/package.json index cef656a4e3a..4809d0e8e37 100644 --- a/apps/rush/src/test/sandbox/legacy-repo/project/package.json +++ b/apps/rush/src/test/sandbox/legacy-repo/project/package.json @@ -9,7 +9,7 @@ "author": "", "license": "ISC", "dependencies": { - "lodash": "^4.17.15", + "semver": "^7.5.4", "react": "^0.14.9" } } diff --git a/apps/rush/src/test/sandbox/repo/project/package.json b/apps/rush/src/test/sandbox/repo/project/package.json index cef656a4e3a..4809d0e8e37 100644 --- a/apps/rush/src/test/sandbox/repo/project/package.json +++ b/apps/rush/src/test/sandbox/repo/project/package.json @@ -9,7 +9,7 @@ "author": "", "license": "ISC", "dependencies": { - "lodash": "^4.17.15", + "semver": "^7.5.4", "react": "^0.14.9" } } diff --git a/apps/rush/tsconfig.json b/apps/rush/tsconfig.json index 22f94ca28b5..dac21d04081 100644 --- a/apps/rush/tsconfig.json +++ b/apps/rush/tsconfig.json @@ -1,6 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - "compilerOptions": { - "types": ["heft-jest", "node"] - } + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/apps/trace-import/.eslintrc.js b/apps/trace-import/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/apps/trace-import/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/apps/trace-import/.npmignore b/apps/trace-import/.npmignore index 6f3340c50ce..f7a40e10213 100644 --- a/apps/trace-import/.npmignore +++ b/apps/trace-import/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,17 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- - -# (Add your project-specific overrides here) +# README.md +# LICENSE +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/apps/trace-import/CHANGELOG.json b/apps/trace-import/CHANGELOG.json index f04c319193d..880e3567f75 100644 --- a/apps/trace-import/CHANGELOG.json +++ b/apps/trace-import/CHANGELOG.json @@ -1,6 +1,2793 @@ { "name": "@rushstack/trace-import", "entries": [ + { + "version": "0.7.22", + "tag": "@rushstack/trace-import_v0.7.22", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "0.7.21", + "tag": "@rushstack/trace-import_v0.7.21", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "0.7.20", + "tag": "@rushstack/trace-import_v0.7.20", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "0.7.19", + "tag": "@rushstack/trace-import_v0.7.19", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "0.7.18", + "tag": "@rushstack/trace-import_v0.7.18", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "0.7.17", + "tag": "@rushstack/trace-import_v0.7.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "0.7.16", + "tag": "@rushstack/trace-import_v0.7.16", + "date": "Mon, 20 Apr 2026 15:15:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "0.7.15", + "tag": "@rushstack/trace-import_v0.7.15", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "0.7.14", + "tag": "@rushstack/trace-import_v0.7.14", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "patch": [ + { + "comment": "Bump semver." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "0.7.13", + "tag": "@rushstack/trace-import_v0.7.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "0.7.12", + "tag": "@rushstack/trace-import_v0.7.12", + "date": "Fri, 10 Apr 2026 22:46:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "0.7.11", + "tag": "@rushstack/trace-import_v0.7.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "0.7.10", + "tag": "@rushstack/trace-import_v0.7.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "0.7.9", + "tag": "@rushstack/trace-import_v0.7.9", + "date": "Wed, 01 Apr 2026 15:13:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "0.7.8", + "tag": "@rushstack/trace-import_v0.7.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "0.7.7", + "tag": "@rushstack/trace-import_v0.7.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "0.7.6", + "tag": "@rushstack/trace-import_v0.7.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "0.7.5", + "tag": "@rushstack/trace-import_v0.7.5", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "0.7.4", + "tag": "@rushstack/trace-import_v0.7.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "0.7.3", + "tag": "@rushstack/trace-import_v0.7.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "0.7.2", + "tag": "@rushstack/trace-import_v0.7.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "0.7.1", + "tag": "@rushstack/trace-import_v0.7.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "0.7.0", + "tag": "@rushstack/trace-import_v0.7.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "0.6.14", + "tag": "@rushstack/trace-import_v0.6.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "0.6.13", + "tag": "@rushstack/trace-import_v0.6.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "0.6.12", + "tag": "@rushstack/trace-import_v0.6.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "0.6.11", + "tag": "@rushstack/trace-import_v0.6.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "0.6.10", + "tag": "@rushstack/trace-import_v0.6.10", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "0.6.9", + "tag": "@rushstack/trace-import_v0.6.9", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "0.6.8", + "tag": "@rushstack/trace-import_v0.6.8", + "date": "Mon, 05 Jan 2026 16:12:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "0.6.7", + "tag": "@rushstack/trace-import_v0.6.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + } + ] + } + }, + { + "version": "0.6.6", + "tag": "@rushstack/trace-import_v0.6.6", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + } + ] + } + }, + { + "version": "0.6.5", + "tag": "@rushstack/trace-import_v0.6.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + } + ] + } + }, + { + "version": "0.6.4", + "tag": "@rushstack/trace-import_v0.6.4", + "date": "Tue, 04 Nov 2025 08:15:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + } + ] + } + }, + { + "version": "0.6.3", + "tag": "@rushstack/trace-import_v0.6.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + } + ] + } + }, + { + "version": "0.6.2", + "tag": "@rushstack/trace-import_v0.6.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + } + ] + } + }, + { + "version": "0.6.1", + "tag": "@rushstack/trace-import_v0.6.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + } + ] + } + }, + { + "version": "0.6.0", + "tag": "@rushstack/trace-import_v0.6.0", + "date": "Fri, 03 Oct 2025 20:10:00 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + } + ] + } + }, + { + "version": "0.5.20", + "tag": "@rushstack/trace-import_v0.5.20", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + } + ] + } + }, + { + "version": "0.5.19", + "tag": "@rushstack/trace-import_v0.5.19", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + } + ] + } + }, + { + "version": "0.5.18", + "tag": "@rushstack/trace-import_v0.5.18", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + } + ] + } + }, + { + "version": "0.5.17", + "tag": "@rushstack/trace-import_v0.5.17", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + } + ] + } + }, + { + "version": "0.5.16", + "tag": "@rushstack/trace-import_v0.5.16", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + } + ] + } + }, + { + "version": "0.5.15", + "tag": "@rushstack/trace-import_v0.5.15", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + } + ] + } + }, + { + "version": "0.5.14", + "tag": "@rushstack/trace-import_v0.5.14", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + } + ] + } + }, + { + "version": "0.5.13", + "tag": "@rushstack/trace-import_v0.5.13", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + } + ] + } + }, + { + "version": "0.5.12", + "tag": "@rushstack/trace-import_v0.5.12", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + } + ] + } + }, + { + "version": "0.5.11", + "tag": "@rushstack/trace-import_v0.5.11", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + } + ] + } + }, + { + "version": "0.5.10", + "tag": "@rushstack/trace-import_v0.5.10", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + } + ] + } + }, + { + "version": "0.5.9", + "tag": "@rushstack/trace-import_v0.5.9", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + } + ] + } + }, + { + "version": "0.5.8", + "tag": "@rushstack/trace-import_v0.5.8", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + } + ] + } + }, + { + "version": "0.5.7", + "tag": "@rushstack/trace-import_v0.5.7", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + } + ] + } + }, + { + "version": "0.5.6", + "tag": "@rushstack/trace-import_v0.5.6", + "date": "Tue, 15 Apr 2025 15:11:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + } + ] + } + }, + { + "version": "0.5.5", + "tag": "@rushstack/trace-import_v0.5.5", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + } + ] + } + }, + { + "version": "0.5.4", + "tag": "@rushstack/trace-import_v0.5.4", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + } + ] + } + }, + { + "version": "0.5.3", + "tag": "@rushstack/trace-import_v0.5.3", + "date": "Tue, 25 Mar 2025 15:11:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + } + ] + } + }, + { + "version": "0.5.2", + "tag": "@rushstack/trace-import_v0.5.2", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + } + ] + } + }, + { + "version": "0.5.1", + "tag": "@rushstack/trace-import_v0.5.1", + "date": "Wed, 12 Mar 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + } + ] + } + }, + { + "version": "0.5.0", + "tag": "@rushstack/trace-import_v0.5.0", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the TypeScript dependency to ~5.8.2." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + } + ] + } + }, + { + "version": "0.4.1", + "tag": "@rushstack/trace-import_v0.4.1", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + } + ] + } + }, + { + "version": "0.4.0", + "tag": "@rushstack/trace-import_v0.4.0", + "date": "Sat, 01 Mar 2025 07:23:16 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `typescript` dependency to `~5.7.3`." + } + ] + } + }, + { + "version": "0.3.88", + "tag": "@rushstack/trace-import_v0.3.88", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + } + ] + } + }, + { + "version": "0.3.87", + "tag": "@rushstack/trace-import_v0.3.87", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + } + ] + } + }, + { + "version": "0.3.86", + "tag": "@rushstack/trace-import_v0.3.86", + "date": "Wed, 26 Feb 2025 16:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + } + ] + } + }, + { + "version": "0.3.85", + "tag": "@rushstack/trace-import_v0.3.85", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + } + ] + } + }, + { + "version": "0.3.84", + "tag": "@rushstack/trace-import_v0.3.84", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + } + ] + } + }, + { + "version": "0.3.83", + "tag": "@rushstack/trace-import_v0.3.83", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + } + ] + } + }, + { + "version": "0.3.82", + "tag": "@rushstack/trace-import_v0.3.82", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + } + ] + } + }, + { + "version": "0.3.81", + "tag": "@rushstack/trace-import_v0.3.81", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + } + ] + } + }, + { + "version": "0.3.80", + "tag": "@rushstack/trace-import_v0.3.80", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + } + ] + } + }, + { + "version": "0.3.79", + "tag": "@rushstack/trace-import_v0.3.79", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + } + ] + } + }, + { + "version": "0.3.78", + "tag": "@rushstack/trace-import_v0.3.78", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + } + ] + } + }, + { + "version": "0.3.77", + "tag": "@rushstack/trace-import_v0.3.77", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + } + ] + } + }, + { + "version": "0.3.76", + "tag": "@rushstack/trace-import_v0.3.76", + "date": "Tue, 03 Dec 2024 16:11:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + } + ] + } + }, + { + "version": "0.3.75", + "tag": "@rushstack/trace-import_v0.3.75", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + } + ] + } + }, + { + "version": "0.3.74", + "tag": "@rushstack/trace-import_v0.3.74", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + } + ] + } + }, + { + "version": "0.3.73", + "tag": "@rushstack/trace-import_v0.3.73", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + } + ] + } + }, + { + "version": "0.3.72", + "tag": "@rushstack/trace-import_v0.3.72", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + } + ] + } + }, + { + "version": "0.3.71", + "tag": "@rushstack/trace-import_v0.3.71", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + } + ] + } + }, + { + "version": "0.3.70", + "tag": "@rushstack/trace-import_v0.3.70", + "date": "Tue, 15 Oct 2024 00:12:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + } + ] + } + }, + { + "version": "0.3.69", + "tag": "@rushstack/trace-import_v0.3.69", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + } + ] + } + }, + { + "version": "0.3.68", + "tag": "@rushstack/trace-import_v0.3.68", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + } + ] + } + }, + { + "version": "0.3.67", + "tag": "@rushstack/trace-import_v0.3.67", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + } + ] + } + }, + { + "version": "0.3.66", + "tag": "@rushstack/trace-import_v0.3.66", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + } + ] + } + }, + { + "version": "0.3.65", + "tag": "@rushstack/trace-import_v0.3.65", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + } + ] + } + }, + { + "version": "0.3.64", + "tag": "@rushstack/trace-import_v0.3.64", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + } + ] + } + }, + { + "version": "0.3.63", + "tag": "@rushstack/trace-import_v0.3.63", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + } + ] + } + }, + { + "version": "0.3.62", + "tag": "@rushstack/trace-import_v0.3.62", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + } + ] + } + }, + { + "version": "0.3.61", + "tag": "@rushstack/trace-import_v0.3.61", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + } + ] + } + }, + { + "version": "0.3.60", + "tag": "@rushstack/trace-import_v0.3.60", + "date": "Wed, 24 Jul 2024 00:12:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + } + ] + } + }, + { + "version": "0.3.59", + "tag": "@rushstack/trace-import_v0.3.59", + "date": "Wed, 17 Jul 2024 06:55:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + } + ] + } + }, + { + "version": "0.3.58", + "tag": "@rushstack/trace-import_v0.3.58", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + } + ] + } + }, + { + "version": "0.3.57", + "tag": "@rushstack/trace-import_v0.3.57", + "date": "Tue, 16 Jul 2024 00:36:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + } + ] + } + }, + { + "version": "0.3.56", + "tag": "@rushstack/trace-import_v0.3.56", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + } + ] + } + }, + { + "version": "0.3.55", + "tag": "@rushstack/trace-import_v0.3.55", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + } + ] + } + }, + { + "version": "0.3.54", + "tag": "@rushstack/trace-import_v0.3.54", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + } + ] + } + }, + { + "version": "0.3.53", + "tag": "@rushstack/trace-import_v0.3.53", + "date": "Wed, 29 May 2024 02:03:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + } + ] + } + }, + { + "version": "0.3.52", + "tag": "@rushstack/trace-import_v0.3.52", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + } + ] + } + }, + { + "version": "0.3.51", + "tag": "@rushstack/trace-import_v0.3.51", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + } + ] + } + }, + { + "version": "0.3.50", + "tag": "@rushstack/trace-import_v0.3.50", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + } + ] + } + }, + { + "version": "0.3.49", + "tag": "@rushstack/trace-import_v0.3.49", + "date": "Sat, 25 May 2024 04:54:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + } + ] + } + }, + { + "version": "0.3.48", + "tag": "@rushstack/trace-import_v0.3.48", + "date": "Fri, 24 May 2024 00:15:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + } + ] + } + }, + { + "version": "0.3.47", + "tag": "@rushstack/trace-import_v0.3.47", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + } + ] + } + }, + { + "version": "0.3.46", + "tag": "@rushstack/trace-import_v0.3.46", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + } + ] + } + }, + { + "version": "0.3.45", + "tag": "@rushstack/trace-import_v0.3.45", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + } + ] + } + }, + { + "version": "0.3.44", + "tag": "@rushstack/trace-import_v0.3.44", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + } + ] + } + }, + { + "version": "0.3.43", + "tag": "@rushstack/trace-import_v0.3.43", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + } + ] + } + }, + { + "version": "0.3.42", + "tag": "@rushstack/trace-import_v0.3.42", + "date": "Wed, 08 May 2024 22:23:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + } + ] + } + }, + { + "version": "0.3.41", + "tag": "@rushstack/trace-import_v0.3.41", + "date": "Mon, 06 May 2024 15:11:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + } + ] + } + }, + { + "version": "0.3.40", + "tag": "@rushstack/trace-import_v0.3.40", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + } + ] + } + }, + { + "version": "0.3.39", + "tag": "@rushstack/trace-import_v0.3.39", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + } + ] + } + }, + { + "version": "0.3.38", + "tag": "@rushstack/trace-import_v0.3.38", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + } + ] + } + }, + { + "version": "0.3.37", + "tag": "@rushstack/trace-import_v0.3.37", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + } + ] + } + }, + { + "version": "0.3.36", + "tag": "@rushstack/trace-import_v0.3.36", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + } + ] + } + }, + { + "version": "0.3.35", + "tag": "@rushstack/trace-import_v0.3.35", + "date": "Sat, 02 Mar 2024 02:22:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + } + ] + } + }, + { + "version": "0.3.34", + "tag": "@rushstack/trace-import_v0.3.34", + "date": "Fri, 01 Mar 2024 01:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.18.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + } + ] + } + }, + { + "version": "0.3.33", + "tag": "@rushstack/trace-import_v0.3.33", + "date": "Thu, 29 Feb 2024 07:11:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + } + ] + } + }, + { + "version": "0.3.32", + "tag": "@rushstack/trace-import_v0.3.32", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + } + ] + } + }, + { + "version": "0.3.31", + "tag": "@rushstack/trace-import_v0.3.31", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + } + ] + } + }, + { + "version": "0.3.30", + "tag": "@rushstack/trace-import_v0.3.30", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + } + ] + } + }, + { + "version": "0.3.29", + "tag": "@rushstack/trace-import_v0.3.29", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "patch": [ + { + "comment": "Replace the dependency on the `colors` package with `Colorize` from `@rushstack/terminal`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + } + ] + } + }, + { + "version": "0.3.28", + "tag": "@rushstack/trace-import_v0.3.28", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + } + ] + } + }, + { + "version": "0.3.27", + "tag": "@rushstack/trace-import_v0.3.27", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + } + ] + } + }, + { + "version": "0.3.26", + "tag": "@rushstack/trace-import_v0.3.26", + "date": "Tue, 20 Feb 2024 16:10:53 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + } + ] + } + }, + { + "version": "0.3.25", + "tag": "@rushstack/trace-import_v0.3.25", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + } + ] + } + }, + { + "version": "0.3.24", + "tag": "@rushstack/trace-import_v0.3.24", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + } + ] + } + }, + { + "version": "0.3.23", + "tag": "@rushstack/trace-import_v0.3.23", + "date": "Thu, 08 Feb 2024 01:09:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + } + ] + } + }, + { + "version": "0.3.22", + "tag": "@rushstack/trace-import_v0.3.22", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + } + ] + } + }, + { + "version": "0.3.21", + "tag": "@rushstack/trace-import_v0.3.21", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + } + ] + } + }, + { + "version": "0.3.20", + "tag": "@rushstack/trace-import_v0.3.20", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + } + ] + } + }, + { + "version": "0.3.19", + "tag": "@rushstack/trace-import_v0.3.19", + "date": "Tue, 23 Jan 2024 20:12:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + } + ] + } + }, + { + "version": "0.3.18", + "tag": "@rushstack/trace-import_v0.3.18", + "date": "Tue, 23 Jan 2024 16:15:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + } + ] + } + }, + { + "version": "0.3.17", + "tag": "@rushstack/trace-import_v0.3.17", + "date": "Tue, 16 Jan 2024 18:30:10 GMT", + "comments": { + "patch": [ + { + "comment": "Upgrade build dependencies" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + } + ] + } + }, + { + "version": "0.3.16", + "tag": "@rushstack/trace-import_v0.3.16", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + } + ] + } + }, + { + "version": "0.3.15", + "tag": "@rushstack/trace-import_v0.3.15", + "date": "Wed, 20 Dec 2023 01:09:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + } + ] + } + }, + { + "version": "0.3.14", + "tag": "@rushstack/trace-import_v0.3.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + } + ] + } + }, + { + "version": "0.3.13", + "tag": "@rushstack/trace-import_v0.3.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + } + ] + } + }, + { + "version": "0.3.12", + "tag": "@rushstack/trace-import_v0.3.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + } + ] + } + }, + { + "version": "0.3.11", + "tag": "@rushstack/trace-import_v0.3.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + } + ] + } + }, + { + "version": "0.3.10", + "tag": "@rushstack/trace-import_v0.3.10", + "date": "Mon, 30 Oct 2023 23:36:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + } + ] + } + }, + { + "version": "0.3.9", + "tag": "@rushstack/trace-import_v0.3.9", + "date": "Sun, 01 Oct 2023 02:56:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + } + ] + } + }, + { + "version": "0.3.8", + "tag": "@rushstack/trace-import_v0.3.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + } + ] + } + }, + { + "version": "0.3.7", + "tag": "@rushstack/trace-import_v0.3.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + } + ] + } + }, + { + "version": "0.3.6", + "tag": "@rushstack/trace-import_v0.3.6", + "date": "Wed, 27 Sep 2023 00:21:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + } + ] + } + }, + { + "version": "0.3.5", + "tag": "@rushstack/trace-import_v0.3.5", + "date": "Tue, 26 Sep 2023 21:02:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + } + ] + } + }, + { + "version": "0.3.4", + "tag": "@rushstack/trace-import_v0.3.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.16.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + } + ] + } + }, + { + "version": "0.3.3", + "tag": "@rushstack/trace-import_v0.3.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + } + ] + } + }, + { + "version": "0.3.2", + "tag": "@rushstack/trace-import_v0.3.2", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + } + ] + } + }, + { + "version": "0.3.1", + "tag": "@rushstack/trace-import_v0.3.1", + "date": "Tue, 19 Sep 2023 15:21:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.24`" + } + ] + } + }, + { + "version": "0.3.0", + "tag": "@rushstack/trace-import_v0.3.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.23`" + } + ] + } + }, + { + "version": "0.2.27", + "tag": "@rushstack/trace-import_v0.2.27", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.22`" + } + ] + } + }, + { + "version": "0.2.26", + "tag": "@rushstack/trace-import_v0.2.26", + "date": "Mon, 31 Jul 2023 15:19:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.21`" + } + ] + } + }, + { + "version": "0.2.25", + "tag": "@rushstack/trace-import_v0.2.25", + "date": "Sat, 29 Jul 2023 00:22:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.20`" + } + ] + } + }, + { + "version": "0.2.24", + "tag": "@rushstack/trace-import_v0.2.24", + "date": "Thu, 20 Jul 2023 20:47:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.19`" + } + ] + } + }, + { + "version": "0.2.23", + "tag": "@rushstack/trace-import_v0.2.23", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "patch": [ + { + "comment": "Updated semver dependency" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.18`" + } + ] + } + }, + { + "version": "0.2.22", + "tag": "@rushstack/trace-import_v0.2.22", + "date": "Fri, 14 Jul 2023 15:20:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.17`" + } + ] + } + }, + { + "version": "0.2.21", + "tag": "@rushstack/trace-import_v0.2.21", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.16`" + } + ] + } + }, + { + "version": "0.2.20", + "tag": "@rushstack/trace-import_v0.2.20", + "date": "Wed, 12 Jul 2023 15:20:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.15`" + } + ] + } + }, + { + "version": "0.2.19", + "tag": "@rushstack/trace-import_v0.2.19", + "date": "Wed, 12 Jul 2023 00:23:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.14`" + } + ] + } + }, + { + "version": "0.2.18", + "tag": "@rushstack/trace-import_v0.2.18", + "date": "Fri, 07 Jul 2023 00:19:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.13`" + } + ] + } + }, + { + "version": "0.2.17", + "tag": "@rushstack/trace-import_v0.2.17", + "date": "Thu, 06 Jul 2023 00:16:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.12`" + } + ] + } + }, + { + "version": "0.2.16", + "tag": "@rushstack/trace-import_v0.2.16", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.11`" + } + ] + } + }, + { + "version": "0.2.15", + "tag": "@rushstack/trace-import_v0.2.15", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.10`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/trace-import_v0.2.14", + "date": "Thu, 15 Jun 2023 00:21:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.9`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/trace-import_v0.2.13", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.8`" + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/trace-import_v0.2.12", + "date": "Tue, 13 Jun 2023 15:17:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.7`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/trace-import_v0.2.11", + "date": "Tue, 13 Jun 2023 01:49:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.6`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/trace-import_v0.2.10", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.5`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/trace-import_v0.2.9", + "date": "Fri, 09 Jun 2023 15:23:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.4`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/trace-import_v0.2.8", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.3`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/trace-import_v0.2.7", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.2`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/trace-import_v0.2.6", + "date": "Thu, 08 Jun 2023 00:20:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.1`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/trace-import_v0.2.5", + "date": "Wed, 07 Jun 2023 22:45:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.0`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/trace-import_v0.2.4", + "date": "Tue, 06 Jun 2023 02:52:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.1.0`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/trace-import_v0.2.3", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.0.1`" + } + ] + } + }, { "version": "0.2.2", "tag": "@rushstack/trace-import_v0.2.2", diff --git a/apps/trace-import/CHANGELOG.md b/apps/trace-import/CHANGELOG.md index 1de60f53d81..e1a539dce9e 100644 --- a/apps/trace-import/CHANGELOG.md +++ b/apps/trace-import/CHANGELOG.md @@ -1,6 +1,907 @@ # Change Log - @rushstack/trace-import -This log was last generated on Fri, 02 Jun 2023 02:01:13 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 0.7.22 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 0.7.21 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 0.7.20 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.7.19 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.7.18 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 0.7.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.7.16 +Mon, 20 Apr 2026 15:15:25 GMT + +_Version update only_ + +## 0.7.15 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 0.7.14 +Sat, 18 Apr 2026 00:15:16 GMT + +### Patches + +- Bump semver. + +## 0.7.13 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.7.12 +Fri, 10 Apr 2026 22:46:35 GMT + +_Version update only_ + +## 0.7.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.7.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 0.7.9 +Wed, 01 Apr 2026 15:13:39 GMT + +_Version update only_ + +## 0.7.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.7.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 0.7.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.7.5 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 0.7.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.7.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 0.7.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 0.7.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.7.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.6.14 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 0.6.13 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 0.6.12 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 0.6.11 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 0.6.10 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 0.6.9 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 0.6.8 +Mon, 05 Jan 2026 16:12:50 GMT + +_Version update only_ + +## 0.6.7 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 0.6.6 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 0.6.5 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 0.6.4 +Tue, 04 Nov 2025 08:15:15 GMT + +_Version update only_ + +## 0.6.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 0.6.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 0.6.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 0.6.0 +Fri, 03 Oct 2025 20:10:00 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 0.5.20 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 0.5.19 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.5.18 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.5.17 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.5.16 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.5.15 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 0.5.14 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.5.13 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.5.12 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.5.11 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.5.10 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.5.9 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.5.8 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.5.7 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.5.6 +Tue, 15 Apr 2025 15:11:58 GMT + +_Version update only_ + +## 0.5.5 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 0.5.4 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.5.3 +Tue, 25 Mar 2025 15:11:16 GMT + +_Version update only_ + +## 0.5.2 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.5.1 +Wed, 12 Mar 2025 00:11:32 GMT + +_Version update only_ + +## 0.5.0 +Tue, 11 Mar 2025 02:12:33 GMT + +### Minor changes + +- Bump the TypeScript dependency to ~5.8.2. + +## 0.4.1 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.4.0 +Sat, 01 Mar 2025 07:23:16 GMT + +### Minor changes + +- Bump the `typescript` dependency to `~5.7.3`. + +## 0.3.88 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.3.87 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.3.86 +Wed, 26 Feb 2025 16:11:12 GMT + +_Version update only_ + +## 0.3.85 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.3.84 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.3.83 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.3.82 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 0.3.81 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.3.80 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.3.79 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.3.78 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.3.77 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.3.76 +Tue, 03 Dec 2024 16:11:08 GMT + +_Version update only_ + +## 0.3.75 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.3.74 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.3.73 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.3.72 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.3.71 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.3.70 +Tue, 15 Oct 2024 00:12:32 GMT + +_Version update only_ + +## 0.3.69 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.3.68 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.3.67 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.3.66 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.3.65 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.3.64 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.3.63 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.3.62 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.3.61 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.3.60 +Wed, 24 Jul 2024 00:12:15 GMT + +_Version update only_ + +## 0.3.59 +Wed, 17 Jul 2024 06:55:10 GMT + +_Version update only_ + +## 0.3.58 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.3.57 +Tue, 16 Jul 2024 00:36:22 GMT + +_Version update only_ + +## 0.3.56 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.3.55 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.3.54 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.3.53 +Wed, 29 May 2024 02:03:51 GMT + +_Version update only_ + +## 0.3.52 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.3.51 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.3.50 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.3.49 +Sat, 25 May 2024 04:54:08 GMT + +_Version update only_ + +## 0.3.48 +Fri, 24 May 2024 00:15:09 GMT + +_Version update only_ + +## 0.3.47 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 0.3.46 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.3.45 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.3.44 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.3.43 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.3.42 +Wed, 08 May 2024 22:23:51 GMT + +_Version update only_ + +## 0.3.41 +Mon, 06 May 2024 15:11:05 GMT + +_Version update only_ + +## 0.3.40 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.3.39 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 0.3.38 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 0.3.37 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 0.3.36 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 0.3.35 +Sat, 02 Mar 2024 02:22:23 GMT + +_Version update only_ + +## 0.3.34 +Fri, 01 Mar 2024 01:10:09 GMT + +_Version update only_ + +## 0.3.33 +Thu, 29 Feb 2024 07:11:46 GMT + +_Version update only_ + +## 0.3.32 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.3.31 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.3.30 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.3.29 +Wed, 21 Feb 2024 21:45:28 GMT + +### Patches + +- Replace the dependency on the `colors` package with `Colorize` from `@rushstack/terminal`. + +## 0.3.28 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.3.27 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.3.26 +Tue, 20 Feb 2024 16:10:53 GMT + +_Version update only_ + +## 0.3.25 +Mon, 19 Feb 2024 21:54:27 GMT + +_Version update only_ + +## 0.3.24 +Sat, 17 Feb 2024 06:24:35 GMT + +_Version update only_ + +## 0.3.23 +Thu, 08 Feb 2024 01:09:22 GMT + +_Version update only_ + +## 0.3.22 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.3.21 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.3.20 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.3.19 +Tue, 23 Jan 2024 20:12:58 GMT + +_Version update only_ + +## 0.3.18 +Tue, 23 Jan 2024 16:15:06 GMT + +_Version update only_ + +## 0.3.17 +Tue, 16 Jan 2024 18:30:10 GMT + +### Patches + +- Upgrade build dependencies + +## 0.3.16 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.3.15 +Wed, 20 Dec 2023 01:09:46 GMT + +_Version update only_ + +## 0.3.14 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.3.13 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.3.12 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 0.3.11 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 0.3.10 +Mon, 30 Oct 2023 23:36:38 GMT + +_Version update only_ + +## 0.3.9 +Sun, 01 Oct 2023 02:56:30 GMT + +_Version update only_ + +## 0.3.8 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 0.3.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.3.6 +Wed, 27 Sep 2023 00:21:39 GMT + +_Version update only_ + +## 0.3.5 +Tue, 26 Sep 2023 21:02:31 GMT + +_Version update only_ + +## 0.3.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.3.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 0.3.2 +Fri, 22 Sep 2023 00:05:50 GMT + +_Version update only_ + +## 0.3.1 +Tue, 19 Sep 2023 15:21:52 GMT + +_Version update only_ + +## 0.3.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 0.2.27 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 0.2.26 +Mon, 31 Jul 2023 15:19:06 GMT + +_Version update only_ + +## 0.2.25 +Sat, 29 Jul 2023 00:22:51 GMT + +_Version update only_ + +## 0.2.24 +Thu, 20 Jul 2023 20:47:29 GMT + +_Version update only_ + +## 0.2.23 +Wed, 19 Jul 2023 00:20:31 GMT + +### Patches + +- Updated semver dependency + +## 0.2.22 +Fri, 14 Jul 2023 15:20:46 GMT + +_Version update only_ + +## 0.2.21 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 0.2.20 +Wed, 12 Jul 2023 15:20:40 GMT + +_Version update only_ + +## 0.2.19 +Wed, 12 Jul 2023 00:23:30 GMT + +_Version update only_ + +## 0.2.18 +Fri, 07 Jul 2023 00:19:33 GMT + +_Version update only_ + +## 0.2.17 +Thu, 06 Jul 2023 00:16:20 GMT + +_Version update only_ + +## 0.2.16 +Tue, 04 Jul 2023 00:18:47 GMT + +_Version update only_ + +## 0.2.15 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 0.2.14 +Thu, 15 Jun 2023 00:21:02 GMT + +_Version update only_ + +## 0.2.13 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 0.2.12 +Tue, 13 Jun 2023 15:17:21 GMT + +_Version update only_ + +## 0.2.11 +Tue, 13 Jun 2023 01:49:02 GMT + +_Version update only_ + +## 0.2.10 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 0.2.9 +Fri, 09 Jun 2023 15:23:15 GMT + +_Version update only_ + +## 0.2.8 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 0.2.7 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 0.2.6 +Thu, 08 Jun 2023 00:20:03 GMT + +_Version update only_ + +## 0.2.5 +Wed, 07 Jun 2023 22:45:17 GMT + +_Version update only_ + +## 0.2.4 +Tue, 06 Jun 2023 02:52:51 GMT + +_Version update only_ + +## 0.2.3 +Mon, 05 Jun 2023 21:45:21 GMT + +_Version update only_ ## 0.2.2 Fri, 02 Jun 2023 02:01:13 GMT diff --git a/apps/trace-import/bin/trace-import b/apps/trace-import/bin/trace-import index aee68e80224..eef2fc27066 100644 --- a/apps/trace-import/bin/trace-import +++ b/apps/trace-import/bin/trace-import @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('../lib/start.js'); +require('../lib-commonjs/start.js'); diff --git a/apps/trace-import/config/jest.config.json b/apps/trace-import/config/jest.config.json index 4bb17bde3ee..d1749681d90 100644 --- a/apps/trace-import/config/jest.config.json +++ b/apps/trace-import/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "local-node-rig/profiles/default/config/jest.config.json" } diff --git a/apps/trace-import/config/rig.json b/apps/trace-import/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/apps/trace-import/config/rig.json +++ b/apps/trace-import/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/apps/trace-import/eslint.config.js b/apps/trace-import/eslint.config.js new file mode 100644 index 00000000000..ceb5a1bee40 --- /dev/null +++ b/apps/trace-import/eslint.config.js @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + }, + rules: { + 'no-console': 'off' + } + } +]; diff --git a/apps/trace-import/package.json b/apps/trace-import/package.json index f95c70b88b4..49ccb156973 100644 --- a/apps/trace-import/package.json +++ b/apps/trace-import/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/trace-import", - "version": "0.2.2", + "version": "0.7.22", "description": "CLI tool for understanding how require() and \"import\" statements get resolved", "repository": { "type": "git", @@ -19,19 +19,37 @@ }, "dependencies": { "@rushstack/node-core-library": "workspace:*", + "@rushstack/terminal": "workspace:*", "@rushstack/ts-command-line": "workspace:*", - "colors": "~1.2.1", "resolve": "~1.22.1", - "semver": "~7.3.0", - "typescript": "~5.0.4" + "semver": "~7.7.4", + "typescript": "~5.8.2" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", "@types/resolve": "1.20.2", - "@types/semver": "7.3.5" - } + "@types/semver": "7.7.1", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "sideEffects": [ + "lib-commonjs/start.js", + "lib-esm/start.js" + ] } diff --git a/apps/trace-import/src/TraceImportCommandLineParser.ts b/apps/trace-import/src/TraceImportCommandLineParser.ts index bc4ca31445a..f34cec4a90d 100644 --- a/apps/trace-import/src/TraceImportCommandLineParser.ts +++ b/apps/trace-import/src/TraceImportCommandLineParser.ts @@ -1,22 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; import { CommandLineParser, - CommandLineFlagParameter, - CommandLineStringParameter, - CommandLineChoiceParameter + type CommandLineFlagParameter, + type CommandLineStringParameter, + type IRequiredCommandLineStringParameter, + type IRequiredCommandLineChoiceParameter } from '@rushstack/ts-command-line'; import { InternalError } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; -import { ResolutionType, traceImport } from './traceImport'; +import { type ResolutionType, traceImport } from './traceImport'; export class TraceImportCommandLineParser extends CommandLineParser { private readonly _debugParameter: CommandLineFlagParameter; - private readonly _pathParameter: CommandLineStringParameter; + private readonly _pathParameter: IRequiredCommandLineStringParameter; private readonly _baseFolderParameter: CommandLineStringParameter; - private readonly _resolutionTypeParameter: CommandLineChoiceParameter; + private readonly _resolutionTypeParameter: IRequiredCommandLineChoiceParameter; public constructor() { super({ @@ -54,7 +55,7 @@ export class TraceImportCommandLineParser extends CommandLineParser { argumentName: 'FOLDER_PATH' }); - this._resolutionTypeParameter = this.defineChoiceParameter({ + this._resolutionTypeParameter = this.defineChoiceParameter({ parameterLongName: '--resolution-type', parameterShortName: '-t', description: @@ -65,22 +66,21 @@ export class TraceImportCommandLineParser extends CommandLineParser { }); } - protected async onExecute(): Promise { - // override + protected override async onExecuteAsync(): Promise { if (this._debugParameter.value) { InternalError.breakInDebugger = true; } try { traceImport({ - importPath: this._pathParameter.value!, + importPath: this._pathParameter.value, baseFolder: this._baseFolderParameter.value, - resolutionType: (this._resolutionTypeParameter.value ?? 'cjs') as ResolutionType + resolutionType: this._resolutionTypeParameter.value }); } catch (error) { if (this._debugParameter.value) { console.error('\n' + error.stack); } else { - console.error('\n' + colors.red('ERROR: ' + error.message.trim())); + console.error('\n' + Colorize.red('ERROR: ' + error.message.trim())); } } } diff --git a/apps/trace-import/src/start.ts b/apps/trace-import/src/start.ts index 13364229d1a..a14bfe9247f 100644 --- a/apps/trace-import/src/start.ts +++ b/apps/trace-import/src/start.ts @@ -1,18 +1,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; - import { PackageJsonLookup } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; + import { TraceImportCommandLineParser } from './TraceImportCommandLineParser'; const toolVersion: string = PackageJsonLookup.loadOwnPackageJson(__dirname).version; console.log(); -console.log(colors.bold(`trace-import ${toolVersion}`) + ' - ' + colors.cyan('https://rushstack.io')); +console.log(Colorize.bold(`trace-import ${toolVersion}`) + ' - ' + Colorize.cyan('https://rushstack.io')); console.log(); const commandLine: TraceImportCommandLineParser = new TraceImportCommandLineParser(); -commandLine.execute().catch((error) => { +commandLine.executeAsync().catch((error) => { console.error(error); }); diff --git a/apps/trace-import/src/traceImport.ts b/apps/trace-import/src/traceImport.ts index b8dd7312302..28ae8ddc8a8 100644 --- a/apps/trace-import/src/traceImport.ts +++ b/apps/trace-import/src/traceImport.ts @@ -1,17 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import * as path from 'node:path'; +import * as process from 'node:process'; + +import * as Resolve from 'resolve'; + +import { Colorize } from '@rushstack/terminal'; import { FileSystem, - IPackageJson, - IParsedPackageName, + type IPackageJson, + type IParsedPackageName, JsonFile, PackageName } from '@rushstack/node-core-library'; -import colors from 'colors/safe'; -import * as path from 'path'; -import * as process from 'process'; -import * as Resolve from 'resolve'; const jsExtensions: string[] = ['.js', '.cjs', '.jsx', '.json']; const tsExtensions: string[] = ['.d.ts', '.ts', '.tsx', '.json']; @@ -36,11 +38,11 @@ interface IExecuteOptions { const packageImportPathRegExp: RegExp = /^((?:@[a-z0-9_][a-z0-9\-_\.]*\/)?[a-z0-9_][a-z0-9\-_\.]*)(\/.*)?$/i; function logInputField(title: string, value: string): void { - console.log(colors.cyan(title.padEnd(25)) + value); + console.log(Colorize.cyan(title.padEnd(25)) + value); } function logOutputField(title: string, value: string): void { - console.log(colors.green(title.padEnd(25)) + value); + console.log(Colorize.green(title.padEnd(25)) + value); } function traceTypeScriptPackage(options: { @@ -323,7 +325,7 @@ export function traceImport(options: IExecuteOptions): void { if (warnings.length) { console.log(); for (const warning of warnings) { - console.log(colors.yellow('Warning: ' + warning)); + console.log(Colorize.yellow('Warning: ' + warning)); } } } diff --git a/apps/trace-import/tsconfig.json b/apps/trace-import/tsconfig.json index fbc2f5c0a6c..dac21d04081 100644 --- a/apps/trace-import/tsconfig.json +++ b/apps/trace-import/tsconfig.json @@ -1,7 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["heft-jest", "node"] - } + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/apps/zipsync/.npmignore b/apps/zipsync/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/apps/zipsync/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/apps/zipsync/CHANGELOG.json b/apps/zipsync/CHANGELOG.json new file mode 100644 index 00000000000..58a38e7d050 --- /dev/null +++ b/apps/zipsync/CHANGELOG.json @@ -0,0 +1,763 @@ +{ + "name": "@rushstack/zipsync", + "entries": [ + { + "version": "0.3.22", + "tag": "@rushstack/zipsync_v0.3.22", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "0.3.21", + "tag": "@rushstack/zipsync_v0.3.21", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.12`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "0.3.20", + "tag": "@rushstack/zipsync_v0.3.20", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.11`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "0.3.19", + "tag": "@rushstack/zipsync_v0.3.19", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.10`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "0.3.18", + "tag": "@rushstack/zipsync_v0.3.18", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "0.3.17", + "tag": "@rushstack/zipsync_v0.3.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.9`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "0.3.16", + "tag": "@rushstack/zipsync_v0.3.16", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "0.3.15", + "tag": "@rushstack/zipsync_v0.3.15", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.8`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "0.3.14", + "tag": "@rushstack/zipsync_v0.3.14", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "0.3.13", + "tag": "@rushstack/zipsync_v0.3.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "0.3.12", + "tag": "@rushstack/zipsync_v0.3.12", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "0.3.11", + "tag": "@rushstack/zipsync_v0.3.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "0.3.10", + "tag": "@rushstack/zipsync_v0.3.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "0.3.9", + "tag": "@rushstack/zipsync_v0.3.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "0.3.8", + "tag": "@rushstack/zipsync_v0.3.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "0.3.7", + "tag": "@rushstack/zipsync_v0.3.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "0.3.6", + "tag": "@rushstack/zipsync_v0.3.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "0.3.5", + "tag": "@rushstack/zipsync_v0.3.5", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "0.3.4", + "tag": "@rushstack/zipsync_v0.3.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "0.3.3", + "tag": "@rushstack/zipsync_v0.3.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "0.3.2", + "tag": "@rushstack/zipsync_v0.3.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "0.3.1", + "tag": "@rushstack/zipsync_v0.3.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "0.3.0", + "tag": "@rushstack/zipsync_v0.3.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/zipsync_v0.2.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/zipsync_v0.2.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/zipsync_v0.2.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/zipsync_v0.2.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/zipsync_v0.2.10", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/zipsync_v0.2.9", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/zipsync_v0.2.8", + "date": "Mon, 05 Jan 2026 16:12:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/zipsync_v0.2.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/zipsync_v0.2.6", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/zipsync_v0.2.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/zipsync_v0.2.4", + "date": "Tue, 04 Nov 2025 08:15:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/zipsync_v0.2.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/zipsync_v0.2.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/zipsync_v0.2.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/zipsync_v0.2.0", + "date": "Fri, 03 Oct 2025 20:10:00 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/zipsync_v0.1.1", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.5`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/zipsync_v0.1.0", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "minor": [ + { + "comment": "Add zipsync tool to pack and unpack build cache entries." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.4`" + }, + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + } + ] + } + } + ] +} diff --git a/apps/zipsync/CHANGELOG.md b/apps/zipsync/CHANGELOG.md new file mode 100644 index 00000000000..430f79ea455 --- /dev/null +++ b/apps/zipsync/CHANGELOG.md @@ -0,0 +1,212 @@ +# Change Log - @rushstack/zipsync + +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 0.3.22 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 0.3.21 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 0.3.20 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.3.19 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.3.18 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 0.3.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.3.16 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 0.3.15 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 0.3.14 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 0.3.13 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.3.12 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 0.3.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.3.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 0.3.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 0.3.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.3.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 0.3.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.3.5 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 0.3.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.3.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 0.3.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 0.3.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.3.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.2.14 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 0.2.13 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 0.2.12 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 0.2.11 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 0.2.10 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 0.2.9 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 0.2.8 +Mon, 05 Jan 2026 16:12:50 GMT + +_Version update only_ + +## 0.2.7 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 0.2.6 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 0.2.5 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 0.2.4 +Tue, 04 Nov 2025 08:15:15 GMT + +_Version update only_ + +## 0.2.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 0.2.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 0.2.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 0.2.0 +Fri, 03 Oct 2025 20:10:00 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 0.1.1 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 0.1.0 +Tue, 30 Sep 2025 20:33:51 GMT + +### Minor changes + +- Add zipsync tool to pack and unpack build cache entries. + diff --git a/apps/zipsync/LICENSE b/apps/zipsync/LICENSE new file mode 100644 index 00000000000..e75a1fe895f --- /dev/null +++ b/apps/zipsync/LICENSE @@ -0,0 +1,24 @@ +@rushstack/zipsync + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. \ No newline at end of file diff --git a/apps/zipsync/README.md b/apps/zipsync/README.md new file mode 100644 index 00000000000..661629a6f94 --- /dev/null +++ b/apps/zipsync/README.md @@ -0,0 +1,48 @@ +# @rushstack/zipsync + +zipsync is a focused tool for packing and unpacking build cache entries using a constrained subset of the ZIP format for high performance. It optimizes the common scenario where most files already exist in the target location and are unchanged. + +## Goals & Rationale + +- **Optimize partial unpack**: Most builds reuse the majority of previously produced outputs. Skipping rewrites preserves filesystem and page cache state. +- **Only write when needed**: Fewer syscalls. +- **Integrated cleanup**: Removes the need for a separate `rm -rf` pass; extra files and empty directories are pruned automatically. +- **ZIP subset**: Compatibility with malware scanners. +- **Fast inspection**: The central directory can be enumerated without inflating the entire archive (unlike tar+gzip). + +## How It Works + +### Pack Flow + +``` +for each file F + write LocalFileHeader(F) + stream chunks: + read -> hash + crc + maybe compress -> write + finalize compressor + write DataDescriptor(F) +add metadata entry (same pattern) +write central directory records +``` + +### Unpack Flow + +``` +load archive -> parse central dir -> read metadata +scan filesystem & delete extraneous entries +for each entry (except metadata): + if unchanged (sha1 matches) => skip + else extract (decompress if needed) +``` + +## Why ZIP (vs tar + gzip) + +Pros for this scenario: + +- Central directory enables cheap listing without decompressing entire payload. +- Widely understood / tooling-friendly (system explorers, scanners, CI tooling). +- Per-file compression keeps selective unpack simple (no need to inflate all bytes). + +Trade-offs: + +- Tar+gzip can exploit cross-file redundancy for better compressed size in datasets with many similar files. diff --git a/apps/zipsync/bin/zipsync b/apps/zipsync/bin/zipsync new file mode 100755 index 00000000000..eef2fc27066 --- /dev/null +++ b/apps/zipsync/bin/zipsync @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('../lib-commonjs/start.js'); diff --git a/apps/zipsync/config/jest.config.json b/apps/zipsync/config/jest.config.json new file mode 100644 index 00000000000..f385c6fdc0f --- /dev/null +++ b/apps/zipsync/config/jest.config.json @@ -0,0 +1,4 @@ +{ + "extends": "local-node-rig/profiles/default/config/jest.config.json", + "setupFilesAfterEnv": ["/config/jestSymbolDispose.js"] +} diff --git a/apps/zipsync/config/jestSymbolDispose.js b/apps/zipsync/config/jestSymbolDispose.js new file mode 100644 index 00000000000..25328e10b8c --- /dev/null +++ b/apps/zipsync/config/jestSymbolDispose.js @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const disposeSymbol = Symbol('Symbol.dispose'); +const asyncDisposeSymbol = Symbol('Symbol.asyncDispose'); + +Symbol.asyncDispose ??= asyncDisposeSymbol; +Symbol.dispose ??= disposeSymbol; diff --git a/apps/zipsync/config/rig.json b/apps/zipsync/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/apps/zipsync/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/apps/zipsync/eslint.config.js b/apps/zipsync/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/apps/zipsync/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/apps/zipsync/package.json b/apps/zipsync/package.json new file mode 100644 index 00000000000..aff10fd111a --- /dev/null +++ b/apps/zipsync/package.json @@ -0,0 +1,60 @@ +{ + "name": "@rushstack/zipsync", + "version": "0.3.22", + "description": "CLI tool for creating and extracting ZIP archives with intelligent filesystem synchronization", + "repository": { + "type": "git", + "url": "https://github.com/microsoft/rushstack.git", + "directory": "apps/zipsync" + }, + "bin": { + "zipsync": "./bin/zipsync" + }, + "license": "MIT", + "scripts": { + "start": "node lib/start", + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "dependencies": { + "@rushstack/terminal": "workspace:*", + "@rushstack/ts-command-line": "workspace:*", + "typescript": "~5.8.2", + "@rushstack/lookup-by-path": "workspace:*" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "sideEffects": [ + "lib-commonjs/start.js", + "lib-esm/start.js" + ] +} diff --git a/apps/zipsync/src/cli/ZipSyncCommandLineParser.ts b/apps/zipsync/src/cli/ZipSyncCommandLineParser.ts new file mode 100644 index 00000000000..594ad4c9ab6 --- /dev/null +++ b/apps/zipsync/src/cli/ZipSyncCommandLineParser.ts @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { CommandLineParser } from '@rushstack/ts-command-line/lib/providers/CommandLineParser'; +import type { + CommandLineFlagParameter, + IRequiredCommandLineStringParameter, + IRequiredCommandLineChoiceParameter, + CommandLineStringListParameter +} from '@rushstack/ts-command-line/lib/index'; +import type { ConsoleTerminalProvider } from '@rushstack/terminal/lib/ConsoleTerminalProvider'; +import type { ITerminal } from '@rushstack/terminal/lib/ITerminal'; + +import type { IZipSyncMode, ZipSyncOptionCompression } from '../zipSyncUtils'; +import { pack, unpack } from '../index'; + +export class ZipSyncCommandLineParser extends CommandLineParser { + private readonly _debugParameter: CommandLineFlagParameter; + private readonly _verboseParameter: CommandLineFlagParameter; + private readonly _modeParameter: IRequiredCommandLineChoiceParameter; + private readonly _archivePathParameter: IRequiredCommandLineStringParameter; + private readonly _baseDirParameter: IRequiredCommandLineStringParameter; + private readonly _targetDirectoriesParameter: CommandLineStringListParameter; + private readonly _compressionParameter: IRequiredCommandLineChoiceParameter; + private readonly _terminal: ITerminal; + private readonly _terminalProvider: ConsoleTerminalProvider; + + public constructor(terminalProvider: ConsoleTerminalProvider, terminal: ITerminal) { + super({ + toolFilename: 'zipsync', + toolDescription: '' + }); + + this._terminal = terminal; + this._terminalProvider = terminalProvider; + + this._debugParameter = this.defineFlagParameter({ + parameterLongName: '--debug', + parameterShortName: '-d', + description: 'Show the full call stack if an error occurs while executing the tool' + }); + + this._verboseParameter = this.defineFlagParameter({ + parameterLongName: '--verbose', + parameterShortName: '-v', + description: 'Show verbose output' + }); + + this._modeParameter = this.defineChoiceParameter({ + parameterLongName: '--mode', + parameterShortName: '-m', + description: + 'The mode of operation: "pack" to create a zip archive, or "unpack" to extract files from a zip archive', + alternatives: ['pack', 'unpack'], + required: true + }); + + this._archivePathParameter = this.defineStringParameter({ + parameterLongName: '--archive-path', + parameterShortName: '-a', + description: 'Zip file path', + argumentName: 'ARCHIVE_PATH', + required: true + }); + + this._targetDirectoriesParameter = this.defineStringListParameter({ + parameterLongName: '--target-directory', + parameterShortName: '-t', + description: 'Target directories to pack or unpack', + argumentName: 'TARGET_DIRECTORIES', + required: true + }); + + this._baseDirParameter = this.defineStringParameter({ + parameterLongName: '--base-dir', + parameterShortName: '-b', + description: 'Base directory for relative paths within the archive', + argumentName: 'BASE_DIR', + required: true + }); + + this._compressionParameter = this.defineChoiceParameter({ + parameterLongName: '--compression', + parameterShortName: '-z', + description: + 'Compression strategy when packing. "deflate" and "zlib" attempts compression for every file (keeps only if smaller); "auto" first skips likely-compressed types before attempting "deflate" compression; "store" disables compression.', + alternatives: ['store', 'deflate', 'zstd', 'auto'], + required: true + }); + } + + protected override async onExecuteAsync(): Promise { + if (this._debugParameter.value) { + // eslint-disable-next-line no-debugger + debugger; + this._terminalProvider.debugEnabled = true; + this._terminalProvider.verboseEnabled = true; + } + if (this._verboseParameter.value) { + this._terminalProvider.verboseEnabled = true; + } + try { + if (this._modeParameter.value === 'pack') { + pack({ + terminal: this._terminal, + archivePath: this._archivePathParameter.value, + targetDirectories: this._targetDirectoriesParameter.values, + baseDir: this._baseDirParameter.value, + compression: this._compressionParameter.value + }); + } else if (this._modeParameter.value === 'unpack') { + unpack({ + terminal: this._terminal, + archivePath: this._archivePathParameter.value, + targetDirectories: this._targetDirectoriesParameter.values, + baseDir: this._baseDirParameter.value + }); + } + } catch (error) { + this._terminal.writeErrorLine('\n' + error.stack); + } + } +} diff --git a/apps/zipsync/src/cli/test/__snapshots__/start.test.ts.snap b/apps/zipsync/src/cli/test/__snapshots__/start.test.ts.snap new file mode 100644 index 00000000000..50597110881 --- /dev/null +++ b/apps/zipsync/src/cli/test/__snapshots__/start.test.ts.snap @@ -0,0 +1,34 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`CLI Tool Tests should display help for "zipsync --help" 1`] = ` +" +zipsync {version} - https://rushstack.io + +usage: zipsync [-h] [-d] [-v] -m {pack,unpack} -a ARCHIVE_PATH -t + TARGET_DIRECTORIES -b BASE_DIR -z {store,deflate,zstd,auto} + + +Optional arguments: + -h, --help Show this help message and exit. + -d, --debug Show the full call stack if an error occurs while + executing the tool + -v, --verbose Show verbose output + -m {pack,unpack}, --mode {pack,unpack} + The mode of operation: \\"pack\\" to create a zip archive, + or \\"unpack\\" to extract files from a zip archive + -a ARCHIVE_PATH, --archive-path ARCHIVE_PATH + Zip file path + -t TARGET_DIRECTORIES, --target-directory TARGET_DIRECTORIES + Target directories to pack or unpack + -b BASE_DIR, --base-dir BASE_DIR + Base directory for relative paths within the archive + -z {store,deflate,zstd,auto}, --compression {store,deflate,zstd,auto} + Compression strategy when packing. \\"deflate\\" and + \\"zlib\\" attempts compression for every file (keeps + only if smaller); \\"auto\\" first skips + likely-compressed types before attempting \\"deflate\\" + compression; \\"store\\" disables compression. + +For detailed help about a specific command, use: zipsync -h +" +`; diff --git a/apps/zipsync/src/cli/test/start.test.ts b/apps/zipsync/src/cli/test/start.test.ts new file mode 100644 index 00000000000..d9d9a090844 --- /dev/null +++ b/apps/zipsync/src/cli/test/start.test.ts @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; +import { execSync } from 'node:child_process'; + +describe('CLI Tool Tests', () => { + it('should display help for "zipsync --help"', () => { + const packageFolder: string = path.resolve(__dirname, '../../..'); + const startOutput = execSync('node lib-commonjs/start.js --help', { + encoding: 'utf-8', + cwd: packageFolder + }); + const normalized = startOutput.replace( + /zipsync \d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)? - https:\/\/rushstack\.io/, + 'zipsync {version} - https://rushstack.io' + ); + expect(normalized).toMatchSnapshot(); + }); +}); diff --git a/apps/zipsync/src/compress.ts b/apps/zipsync/src/compress.ts new file mode 100644 index 00000000000..1c89ef8c58e --- /dev/null +++ b/apps/zipsync/src/compress.ts @@ -0,0 +1,214 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { Transform } from 'node:stream'; +import zlib from 'node:zlib'; + +type OutputChunkHandler = (chunk: Uint8Array, lengthBytes: number) => void; + +const kError: unique symbol = (() => { + // Create an instance of Deflate so that we can get our hands on the internal error symbol + // It isn't exported. + const reference: zlib.Deflate = zlib.createDeflateRaw(); + const kErrorResult: symbol | undefined = Object.getOwnPropertySymbols(reference).find((x) => + x.toString().includes('kError') + ); + if (kErrorResult === undefined) { + throw new Error('Unable to find the internal error symbol in node:zlib'); + } + reference.close(); + return kErrorResult; + // Casting `symbol` to the exact symbol of this definition +})() as typeof kError; + +/** + * Internal members of all Zlib compressors. + * Needed to + */ +interface IZlibInternals { + /** + * The native binding to Zlib. + */ + _handle: IHandle | undefined; + /** + * The flush flag passed to each call other than the last one for this implementation. + * Varies by compressor. + */ + _defaultFlushFlag: number; + /** + * The flush flag passed to the final call for this implementation. + * Varies by compressor. + */ + _finishFlushFlag: number; + /** + * The number of bytes read from the input and written to the output. + */ + _writeState: [number, number]; + /** + * The internal error state + */ + [kError]: Error | undefined; +} + +type Compressor = Transform & IZlibInternals; + +interface IHandle { + /** + * Closes the handle and releases resources. + * Ensure that this is always invoked. + */ + close(): void; + /** + * Compresses up to `inLen` bytes from `chunk` starting at `inOff`. + * Writes up to `outLen` bytes to `output` starting at `outOff`. + * @param flushFlag - The flush flag to the compressor implementation. Defines the behavior when reaching the end of the input. + * @param chunk - The buffer containing the data to be compressed + * @param inOff - The offset in bytes to start reading from `chunk` + * @param inLen - The maximum number of bytes to read from `chunk` + * @param output - The buffer to write the compressed data to + * @param outOff - The offset in bytes to start writing to `output` at + * @param outLen - The maximum number of bytes to write to `output`. + */ + writeSync( + flushFlag: number, + chunk: Uint8Array, + inOff: number, + inLen: number, + output: Uint8Array, + outOff: number, + outLen: number + ): void; +} + +export type IIncrementalZlib = Disposable & { + update: (inputBuffer: Uint8Array) => void; +}; + +// zstd is available in Node 22+ +type IExtendedZlib = typeof zlib & { + createZstdCompress: (options?: zlib.ZlibOptions) => Transform; + createZstdDecompress: (options?: zlib.ZlibOptions) => Transform; +}; + +export type IncrementalZlibMode = 'deflate' | 'inflate' | 'zstd-compress' | 'zstd-decompress'; + +export function createIncrementalZlib( + outputBuffer: Uint8Array, + handleOutputChunk: OutputChunkHandler, + mode: IncrementalZlibMode +): IIncrementalZlib { + // The zlib constructors all allocate a buffer of size chunkSize using Buffer.allocUnsafe + // We want to ensure that that invocation doesn't allocate a buffer. + // Unfortunately the minimum value of `chunkSize` to the constructor is non-zero + + let compressor: Compressor | undefined; + + const savedAllocUnsafe: typeof Buffer.allocUnsafe = Buffer.allocUnsafe; + + try { + //@ts-expect-error + Buffer.allocUnsafe = () => outputBuffer; + switch (mode) { + case 'inflate': + compressor = zlib.createInflateRaw({ + chunkSize: outputBuffer.byteLength + }) as unknown as Transform & IZlibInternals; + break; + case 'deflate': + compressor = zlib.createDeflateRaw({ + chunkSize: outputBuffer.byteLength, + level: zlib.constants.Z_BEST_COMPRESSION + }) as unknown as Transform & IZlibInternals; + break; + case 'zstd-compress': + // available in Node 22.15+ + compressor = (zlib as IExtendedZlib).createZstdCompress({ + chunkSize: outputBuffer.byteLength + }) as unknown as Transform & IZlibInternals; + break; + case 'zstd-decompress': + // available in Node 22.15+ + compressor = (zlib as IExtendedZlib).createZstdDecompress({ + chunkSize: outputBuffer.byteLength + }) as unknown as Transform & IZlibInternals; + break; + default: + // Unsupported mode (types currently restrict to 'deflate' | 'inflate') + break; + } + } finally { + Buffer.allocUnsafe = savedAllocUnsafe; + } + + if (!compressor) { + throw new Error('Failed to create zlib instance'); + } + + const handle: IHandle = compressor._handle!; + + return { + [Symbol.dispose]: () => { + if (compressor._handle) { + compressor._handle.close(); + compressor._handle = undefined; + } + }, + update: function processInputChunk(inputBuffer: Uint8Array): void { + let error: Error | undefined; + + // Directive to the compressor on reaching the end of the current input buffer + // Default value is to expect more data + let flushFlag: number = compressor._defaultFlushFlag; + + let bytesInInputBuffer: number = inputBuffer.byteLength; + + if (bytesInInputBuffer <= 0) { + // Ensure the value is non-negative + // We will call the compressor one last time with 0 bytes of input + bytesInInputBuffer = 0; + // Tell the compressor to flush anything in its internal buffer and write any needed trailer. + flushFlag = compressor._finishFlushFlag; + } + + let availInBefore: number = bytesInInputBuffer; + let inOff: number = 0; + let availOutAfter: number = 0; + let availInAfter: number | undefined; + + const state: [number, number] = compressor._writeState; + + do { + handle.writeSync( + flushFlag, + inputBuffer, // in + inOff, // in_off + availInBefore, // in_len + outputBuffer, // out + 0, // out_off + outputBuffer.byteLength // out_len + ); + + if (error) { + throw error; + } else if (compressor[kError]) { + throw compressor[kError]; + } + + availOutAfter = state[0]; + availInAfter = state[1]; + + const inDelta: number = availInBefore - availInAfter; + + const have: number = outputBuffer.byteLength - availOutAfter; + if (have > 0) { + handleOutputChunk(outputBuffer, have); + } + + // These values get reset if we have new data, + // so we can update them even if we're done + inOff += inDelta; + availInBefore = availInAfter; + } while (availOutAfter === 0); + } + }; +} diff --git a/apps/zipsync/src/crc32.ts b/apps/zipsync/src/crc32.ts new file mode 100644 index 00000000000..18017c58852 --- /dev/null +++ b/apps/zipsync/src/crc32.ts @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as zlib from 'node:zlib'; + +let crcTable: Uint32Array | undefined; + +function initCrcTable(): Uint32Array { + if (crcTable) { + return crcTable; + } + + crcTable = new Uint32Array(256); + for (let i: number = 0; i < 256; i++) { + let crcEntry: number = i; + for (let j: number = 0; j < 8; j++) { + // eslint-disable-next-line no-bitwise + crcEntry = crcEntry & 1 ? 0xedb88320 ^ (crcEntry >>> 1) : crcEntry >>> 1; + } + crcTable[i] = crcEntry; + } + return crcTable; +} + +export function fallbackCrc32(data: Buffer, value: number = 0): number { + const table: Uint32Array = initCrcTable(); + value = (value ^ 0xffffffff) >>> 0; + + for (let i: number = 0; i < data.length; i++) { + // eslint-disable-next-line no-bitwise + value = table[(value ^ data[i]) & 0xff] ^ (value >>> 8); + } + + value = (value ^ 0xffffffff) >>> 0; + return value; +} + +export const crc32Builder: (data: Buffer, value?: number) => number = + zlib.crc32 ?? fallbackCrc32; diff --git a/apps/zipsync/src/fs.ts b/apps/zipsync/src/fs.ts new file mode 100644 index 00000000000..ced3056a6c6 --- /dev/null +++ b/apps/zipsync/src/fs.ts @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { default as fs, type OpenMode } from 'node:fs'; + +interface IInternalDisposableFileHandle extends Disposable { + fd: number; +} + +export interface IDisposableFileHandle extends IInternalDisposableFileHandle { + readonly fd: number; +} + +export const DISPOSE_SYMBOL: typeof Symbol.dispose = Symbol.dispose ?? Symbol.for('Symbol.dispose'); + +export function getDisposableFileHandle(path: string, openMode: OpenMode): IDisposableFileHandle { + const result: IInternalDisposableFileHandle = { + fd: fs.openSync(path, openMode), + [DISPOSE_SYMBOL]: () => { + if (!isNaN(result.fd)) { + fs.closeSync(result.fd); + result.fd = NaN; + } + } + }; + + return result; +} + +export function rmdirSync(dirPath: string): boolean { + try { + fs.rmdirSync(dirPath); + return true; + } catch (e) { + if ((e as NodeJS.ErrnoException).code === 'ENOENT' || (e as NodeJS.ErrnoException).code === 'ENOTDIR') { + // Not found, ignore + } else { + throw e; + } + } + return false; +} + +export function unlinkSync(filePath: string): boolean { + try { + fs.unlinkSync(filePath); + return true; + } catch (e) { + if (e && (e as NodeJS.ErrnoException).code === 'ENOENT') { + // Not found, ignore + } else { + throw e; + } + } + return false; +} diff --git a/apps/zipsync/src/hash.ts b/apps/zipsync/src/hash.ts new file mode 100644 index 00000000000..6ba6e59a587 --- /dev/null +++ b/apps/zipsync/src/hash.ts @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { readSync, fstatSync, type Stats } from 'node:fs'; +import { createHash, type Hash } from 'node:crypto'; + +const buffer: Buffer = Buffer.allocUnsafeSlow(1 << 24); + +export function computeFileHash(fd: number): string | false { + try { + const hash: Hash = createHash('sha1'); + let totalBytesRead: number = 0; + let bytesRead: number; + do { + bytesRead = readSync(fd, buffer, 0, buffer.length, -1); + if (bytesRead <= 0) { + break; + } + totalBytesRead += bytesRead; + hash.update(buffer.subarray(0, bytesRead)); + } while (bytesRead > 0); + if (totalBytesRead === 0) { + // Sometimes directories get treated as empty files + const stat: Stats = fstatSync(fd); + if (!stat.isFile()) { + return false; + } + } + + return hash.digest('hex'); + } catch (err) { + // There is a bug in node-core-library where it doesn't handle if the operation was on a file descriptor + if (err.code === 'EISDIR' || err.code === 'ENOENT' || err.code === 'ENOTDIR') { + return false; + } + throw err; + } +} + +export function calculateSHA1(data: Buffer): string { + return createHash('sha1').update(data).digest('hex'); +} diff --git a/apps/zipsync/src/index.ts b/apps/zipsync/src/index.ts new file mode 100644 index 00000000000..11913cee85c --- /dev/null +++ b/apps/zipsync/src/index.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export { pack, type IZipSyncPackResult, type IZipSyncPackOptions } from './pack'; +export { unpack, type IZipSyncUnpackResult, type IZipSyncUnpackOptions } from './unpack'; diff --git a/apps/zipsync/src/pack.ts b/apps/zipsync/src/pack.ts new file mode 100644 index 00000000000..177fc6fec6c --- /dev/null +++ b/apps/zipsync/src/pack.ts @@ -0,0 +1,425 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import * as crypto from 'node:crypto'; +import * as zlib from 'node:zlib'; + +import type { ITerminal } from '@rushstack/terminal/lib/ITerminal'; + +import { crc32Builder } from './crc32'; +import { DISPOSE_SYMBOL, getDisposableFileHandle, type IDisposableFileHandle } from './fs'; +import { type IIncrementalZlib, type IncrementalZlibMode, createIncrementalZlib } from './compress'; +import { markStart, markEnd, getDuration, emitSummary, formatDuration } from './perf'; +import { + writeLocalFileHeader, + writeDataDescriptor, + writeCentralDirectoryHeader, + writeEndOfCentralDirectory, + ZSTD_COMPRESSION, + DEFLATE_COMPRESSION, + STORE_COMPRESSION, + type ZipMetaCompressionMethod, + type IFileEntry, + dosDateTime +} from './zipUtils'; +import { calculateSHA1 } from './hash'; +import { + type ZipSyncOptionCompression, + type IMetadata, + type IDirQueueItem, + METADATA_VERSION, + METADATA_FILENAME, + defaultBufferSize +} from './zipSyncUtils'; + +/** + * File extensions for which additional DEFLATE/ZSTD compression is unlikely to help. + * Used by the 'auto' compression heuristic to avoid wasting CPU on data that is already + * compressed (images, media, existing archives, fonts, etc.). + */ +const LIKELY_COMPRESSED_EXTENSION_REGEX: RegExp = + /\.(?:zip|gz|tgz|bz2|xz|7z|rar|jpg|jpeg|png|gif|webp|avif|mp4|m4v|mov|mkv|webm|mp3|ogg|aac|flac|pdf|woff|woff2)$/; + +/** + * Basic heuristic: skip re-compressing file types that are already compressed. + */ +function isLikelyAlreadyCompressed(filename: string): boolean { + return LIKELY_COMPRESSED_EXTENSION_REGEX.test(filename.toLowerCase()); +} + +/** + * Map zip compression method code -> incremental zlib mode label + */ +const zlibPackModes: Record = { + [ZSTD_COMPRESSION]: 'zstd-compress', + [DEFLATE_COMPRESSION]: 'deflate', + [STORE_COMPRESSION]: undefined +} as const; + +/** + * Public facing CLI option -> actual zip method used for a file we decide to compress. + */ +const zipSyncCompressionOptions: Record = { + store: STORE_COMPRESSION, + deflate: DEFLATE_COMPRESSION, + zstd: ZSTD_COMPRESSION, + auto: DEFLATE_COMPRESSION +} as const; + +/** + * @public + * Options for zipsync + */ +export interface IZipSyncPackOptions { + /** + * \@rushstack/terminal compatible terminal for logging + */ + terminal: ITerminal; + /** + * Zip file path + */ + archivePath: string; + /** + * Target directories to pack (relative to baseDir) + */ + targetDirectories: ReadonlyArray; + /** + * Base directory for relative paths within the archive (defaults to common parent of targetDirectories) + */ + baseDir: string; + /** + * Compression mode. If set to 'deflate', file data will be compressed using raw DEFLATE (method 8) when this + * produces a smaller result; otherwise it will fall back to 'store' per-file. + */ + compression: ZipSyncOptionCompression; + /** + * Optional buffer that can be provided to avoid internal allocations. + */ + inputBuffer?: Buffer; + /** + * Optional buffer that can be provided to avoid internal allocations. + */ + outputBuffer?: Buffer; +} + +export interface IZipSyncPackResult { + filesPacked: number; + metadata: IMetadata; +} + +/** + * Create a zipsync archive by enumerating target directories, then streaming each file into the + * output zip using the local file header + (optional compressed data) + data descriptor pattern. + * + * Performance characteristics: + * - Single pass per file (no read-then-compress-then-write buffering). CRC32 + SHA-1 are computed + * while streaming so the metadata JSON can later be used for selective unpack. + * - Data descriptor usage (bit 3) allows writing headers before we know sizes or CRC32. + * - A single timestamp (captured once) is applied to all entries for determinism. + * - Metadata entry is added as a normal zip entry at the end (before central directory) so legacy + * tools can still list/extract it, while zipsync can quickly parse file hashes. + */ +export function pack({ + archivePath, + targetDirectories: rawTargetDirectories, + baseDir: rawBaseDir, + compression, + terminal, + inputBuffer = Buffer.allocUnsafeSlow(defaultBufferSize), + outputBuffer = Buffer.allocUnsafeSlow(defaultBufferSize) +}: IZipSyncPackOptions): IZipSyncPackResult { + const baseDir: string = path.resolve(rawBaseDir); + const targetDirectories: string[] = rawTargetDirectories.map((dir) => path.join(baseDir, dir)); + terminal.writeLine(`Packing to ${archivePath} from ${rawTargetDirectories.join(', ')}`); + + markStart('pack.total'); + terminal.writeDebugLine('Starting pack'); + // Pass 1: enumerate files with a queue to avoid deep recursion + markStart('pack.enumerate'); + + const filePaths: string[] = []; + const queue: IDirQueueItem[] = targetDirectories.map((dir) => ({ dir, depth: 0 })); + + while (queue.length) { + const { dir: currentDir, depth } = queue.shift()!; + terminal.writeDebugLine(`Enumerating directory: ${currentDir}`); + + const padding: string = depth === 0 ? '' : '-↳'.repeat(depth); + + let items: fs.Dirent[]; + try { + items = fs.readdirSync(currentDir, { withFileTypes: true }); + } catch (e) { + if ( + e && + ((e as NodeJS.ErrnoException).code === 'ENOENT' || (e as NodeJS.ErrnoException).code === 'ENOTDIR') + ) { + terminal.writeWarningLine(`Failed to read directory: ${currentDir}. Ignoring.`); + continue; + } else { + throw e; + } + } + + for (const item of items) { + const fullPath: string = path.join(currentDir, item.name); + if (item.isFile()) { + const relativePath: string = path.relative(baseDir, fullPath).replace(/\\/g, '/'); + terminal.writeVerboseLine(`${padding}${item.name}`); + filePaths.push(relativePath); + } else if (item.isDirectory()) { + terminal.writeVerboseLine(`${padding}${item.name}/`); + queue.push({ dir: fullPath, depth: depth + 1 }); + } else { + throw new Error(`Unexpected item (not file or directory): ${fullPath}. Aborting.`); + } + } + } + + terminal.writeLine(`Found ${filePaths.length} files to pack (enumerated)`); + markEnd('pack.enumerate'); + + // Pass 2: stream each file: read chunks -> hash + (maybe) compress -> write local header + data descriptor. + markStart('pack.prepareEntries'); + + terminal.writeDebugLine(`Opening archive for write: ${archivePath}`); + using zipFile: IDisposableFileHandle = getDisposableFileHandle(archivePath, 'w'); + let currentOffset: number = 0; + /** + * Write a raw chunk to the archive file descriptor, updating current offset. + */ + function writeChunkToZip(chunk: Uint8Array, lengthBytes: number = chunk.byteLength): void { + let offset: number = 0; + while (lengthBytes > 0 && offset < chunk.byteLength) { + // In practice this call always writes all data at once, but the spec says it is not an error + // for it to not do so. Possibly that situation comes up when writing to something that is not + // an ordinary file. + const written: number = fs.writeSync(zipFile.fd, chunk, offset, lengthBytes); + lengthBytes -= written; + offset += written; + } + currentOffset += offset; + } + /** Convenience wrapper for writing multiple buffers sequentially. */ + function writeChunksToZip(chunks: Uint8Array[]): void { + for (const chunk of chunks) { + writeChunkToZip(chunk); + } + } + + const dosDateTimeNow: { time: number; date: number } = dosDateTime(new Date()); + /** + * Stream a single file into the archive. + * Steps: + * 1. Decide compression (based on user choice + heuristic). + * 2. Emit local file header (sizes/CRC zeroed because we use a data descriptor). + * 3. Read file in 32 MiB chunks: update SHA-1 + CRC32; optionally feed compressor or write raw. + * 4. Flush compressor (if any) and write trailing data descriptor containing sizes + CRC. + * 5. Return populated entry metadata for later central directory + JSON metadata. + */ + function writeFileEntry(relativePath: string): IFileEntry { + const fullPath: string = path.join(baseDir, relativePath); + + /** + * Read file in large fixed-size buffer; invoke callback for each filled chunk. + */ + const readInputInChunks: (onChunk: (bytesInInputBuffer: number) => void) => void = ( + onChunk: (bytesInInputBuffer: number) => void + ): void => { + using inputDisposable: IDisposableFileHandle = getDisposableFileHandle(fullPath, 'r'); + + let bytesInInputBuffer: number = 0; + // The entire input buffer will be drained in each loop iteration + // So run until EOF + while (!isNaN(inputDisposable.fd)) { + bytesInInputBuffer = fs.readSync(inputDisposable.fd, inputBuffer, 0, inputBuffer.byteLength, -1); + + if (bytesInInputBuffer <= 0) { + // EOF, close the input fd + inputDisposable[DISPOSE_SYMBOL](); + } + + onChunk(bytesInInputBuffer); + } + }; + + let shouldCompress: boolean = false; + if (compression === 'deflate' || compression === 'zstd') { + shouldCompress = true; + } else if (compression === 'auto') { + // Heuristic: skip compression for small files or likely-already-compressed files + if (!isLikelyAlreadyCompressed(relativePath)) { + shouldCompress = true; + } else { + terminal.writeVerboseLine( + `Skip compression heuristically (already-compressed) for ${relativePath} (size unknown at this point)` + ); + } + } + + const compressionMethod: ZipMetaCompressionMethod = shouldCompress + ? zipSyncCompressionOptions[compression] + : zipSyncCompressionOptions.store; + + const entry: IFileEntry = { + filename: relativePath, + size: 0, + compressedSize: 0, + crc32: 0, + sha1Hash: '', + localHeaderOffset: currentOffset, + compressionMethod, + dosDateTime: dosDateTimeNow + }; + + writeChunksToZip(writeLocalFileHeader(entry)); + + const sha1HashBuilder: crypto.Hash = crypto.createHash('sha1'); + let crc32: number = 0; + let uncompressedSize: number = 0; + let compressedSize: number = 0; + + /** + * Compressor instance (deflate or zstd) created only if needed. + */ + using incrementalZlib: IIncrementalZlib | undefined = shouldCompress + ? createIncrementalZlib( + outputBuffer, + (chunk, lengthBytes) => { + writeChunkToZip(chunk, lengthBytes); + compressedSize += lengthBytes; + }, + zlibPackModes[compressionMethod]! + ) + : undefined; + + // Read input file in chunks, update hashes, and either compress or write raw. + readInputInChunks((bytesInInputBuffer: number) => { + const slice: Buffer = inputBuffer.subarray(0, bytesInInputBuffer); + sha1HashBuilder.update(slice); + crc32 = crc32Builder(slice, crc32); + if (incrementalZlib) { + incrementalZlib.update(slice); + } else { + writeChunkToZip(slice, bytesInInputBuffer); + } + uncompressedSize += bytesInInputBuffer; + }); + + // finalize hashes, compression + incrementalZlib?.update(Buffer.alloc(0)); + crc32 = crc32 >>> 0; + const sha1Hash: string = sha1HashBuilder.digest('hex'); + + if (!shouldCompress) { + compressedSize = uncompressedSize; + } + + entry.size = uncompressedSize; + entry.compressedSize = compressedSize; + entry.crc32 = crc32; + entry.sha1Hash = sha1Hash; + + // Trailing data descriptor now that final CRC/sizes are known. + writeChunkToZip(writeDataDescriptor(entry)); + + terminal.writeVerboseLine( + `${relativePath} (sha1=${entry.sha1Hash}, crc32=${entry.crc32.toString(16)}, size=${ + entry.size + }, compressed=${entry.compressedSize}, method=${entry.compressionMethod}, compressed ${( + 100 - + (entry.compressedSize / entry.size) * 100 + ).toFixed(1)}%)` + ); + return entry; + } + + const entries: IFileEntry[] = []; + // Emit all file entries in enumeration order. + for (const relativePath of filePaths) { + entries.push(writeFileEntry(relativePath)); + } + + markEnd('pack.prepareEntries'); + terminal.writeLine(`Prepared ${entries.length} file entries`); + + markStart('pack.metadata.build'); + const metadata: IMetadata = { version: METADATA_VERSION, files: {} }; + // Build metadata map used for selective unpack (size + SHA‑1 per file). + for (const entry of entries) { + metadata.files[entry.filename] = { size: entry.size, sha1Hash: entry.sha1Hash }; + } + + const metadataContent: string = JSON.stringify(metadata); + const metadataBuffer: Buffer = Buffer.from(metadataContent, 'utf8'); + terminal.writeDebugLine( + `Metadata size=${metadataBuffer.length} bytes, fileCount=${Object.keys(metadata.files).length}` + ); + + let metadataCompressionMethod: ZipMetaCompressionMethod = zipSyncCompressionOptions.store; + let metadataData: Buffer = metadataBuffer; + let metadataCompressedSize: number = metadataBuffer.length; + // Compress metadata (deflate) iff user allowed compression and it helps (>64 bytes & smaller result). + if (compression !== 'store' && metadataBuffer.length > 64) { + const compressed: Buffer = zlib.deflateRawSync(metadataBuffer, { level: 9 }); + if (compressed.length < metadataBuffer.length) { + metadataCompressionMethod = zipSyncCompressionOptions.deflate; + metadataData = compressed; + metadataCompressedSize = compressed.length; + terminal.writeDebugLine( + `Metadata compressed (orig=${metadataBuffer.length}, compressed=${compressed.length})` + ); + } else { + terminal.writeDebugLine('Metadata compression skipped (not smaller)'); + } + } + + const metadataEntry: IFileEntry = { + filename: METADATA_FILENAME, + size: metadataBuffer.length, + compressedSize: metadataCompressedSize, + crc32: crc32Builder(metadataBuffer), + sha1Hash: calculateSHA1(metadataBuffer), + localHeaderOffset: currentOffset, + compressionMethod: metadataCompressionMethod, + dosDateTime: dosDateTimeNow + }; + + writeChunksToZip(writeLocalFileHeader(metadataEntry)); + writeChunkToZip(metadataData, metadataCompressedSize); + writeChunkToZip(writeDataDescriptor(metadataEntry)); + + entries.push(metadataEntry); + terminal.writeVerboseLine(`Total entries including metadata: ${entries.length}`); + + markEnd('pack.metadata.build'); + + markStart('pack.write.entries'); + const outputDir: string = path.dirname(archivePath); + fs.mkdirSync(outputDir, { recursive: true }); + + markEnd('pack.write.entries'); + + markStart('pack.write.centralDirectory'); + const centralDirOffset: number = currentOffset; + // Emit central directory records. + for (const entry of entries) { + writeChunksToZip(writeCentralDirectoryHeader(entry)); + } + const centralDirSize: number = currentOffset - centralDirOffset; + markEnd('pack.write.centralDirectory'); + + // Write end of central directory + markStart('pack.write.eocd'); + writeChunkToZip(writeEndOfCentralDirectory(centralDirOffset, centralDirSize, entries.length)); + terminal.writeDebugLine('EOCD record written'); + markEnd('pack.write.eocd'); + + markEnd('pack.total'); + const total: number = getDuration('pack.total'); + emitSummary('pack', terminal); + terminal.writeLine(`Successfully packed ${entries.length} files in ${formatDuration(total)}`); + return { filesPacked: entries.length, metadata }; +} diff --git a/apps/zipsync/src/packWorker.ts b/apps/zipsync/src/packWorker.ts new file mode 100644 index 00000000000..39e82fe8177 --- /dev/null +++ b/apps/zipsync/src/packWorker.ts @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { parentPort as rawParentPort, type MessagePort } from 'node:worker_threads'; + +import { Terminal } from '@rushstack/terminal/lib/Terminal'; +import { StringBufferTerminalProvider } from '@rushstack/terminal/lib/StringBufferTerminalProvider'; + +import { type IZipSyncPackOptions, type IZipSyncPackResult, pack } from './pack'; +import { defaultBufferSize } from './zipSyncUtils'; + +export { type IZipSyncPackOptions, type IZipSyncPackResult } from './pack'; + +export interface IHashWorkerData { + basePath: string; +} + +export interface IZipSyncPackCommandMessage { + type: 'zipsync-pack'; + id: number; + options: Omit; +} + +export interface IZipSyncPackWorkerResult { + zipSyncReturn: IZipSyncPackResult; + zipSyncLogs: string; +} + +interface IZipSyncSuccessMessage { + id: number; + type: 'zipsync-pack'; + result: IZipSyncPackWorkerResult; +} + +export interface IZipSyncPackErrorMessage { + type: 'error'; + id: number; + args: { + message: string; + stack: string; + zipSyncLogs: string; + }; +} + +export type IHostToWorkerMessage = IZipSyncPackCommandMessage; +export type IWorkerToHostMessage = IZipSyncSuccessMessage | IZipSyncPackErrorMessage; + +if (!rawParentPort) { + throw new Error('This module must be run in a worker thread.'); +} +const parentPort: MessagePort = rawParentPort; + +let inputBuffer: Buffer | undefined = undefined; +let outputBuffer: Buffer | undefined = undefined; + +function handleMessage(message: IHostToWorkerMessage | false): void { + if (message === false) { + parentPort.removeAllListeners(); + parentPort.close(); + return; + } + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(); + const terminal: Terminal = new Terminal(terminalProvider); + + try { + switch (message.type) { + case 'zipsync-pack': { + const { options } = message; + if (!inputBuffer) { + inputBuffer = Buffer.allocUnsafeSlow(defaultBufferSize); + } + if (!outputBuffer) { + outputBuffer = Buffer.allocUnsafeSlow(defaultBufferSize); + } + + const successMessage: IZipSyncSuccessMessage = { + type: message.type, + id: message.id, + result: { + zipSyncReturn: pack({ ...options, terminal, inputBuffer, outputBuffer }), + zipSyncLogs: terminalProvider.getOutput() + } + }; + return parentPort.postMessage(successMessage); + } + } + } catch (err) { + const errorMessage: IZipSyncPackErrorMessage = { + type: 'error', + id: message.id, + args: { + message: (err as Error).message, + stack: (err as Error).stack || '', + zipSyncLogs: terminalProvider.getOutput() + } + }; + parentPort.postMessage(errorMessage); + } +} + +parentPort.on('message', handleMessage); diff --git a/apps/zipsync/src/packWorkerAsync.ts b/apps/zipsync/src/packWorkerAsync.ts new file mode 100644 index 00000000000..71e7b1db062 --- /dev/null +++ b/apps/zipsync/src/packWorkerAsync.ts @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { Worker } from 'node:worker_threads'; + +import type { + IWorkerToHostMessage, + IHostToWorkerMessage, + IZipSyncPackWorkerResult, + IZipSyncPackOptions +} from './packWorker'; + +export type { IZipSyncPackWorkerResult } from './packWorker'; + +export async function packWorkerAsync( + options: Omit +): Promise { + const { Worker } = await import('node:worker_threads'); + + const worker: Worker = new Worker(require.resolve('./packWorker')); + + return new Promise((resolve, reject) => { + worker.on('message', (message: IWorkerToHostMessage) => { + switch (message.type) { + case 'zipsync-pack': { + resolve(message.result); + break; + } + case 'error': { + const error: Error = new Error(message.args.message); + error.stack = message.args.stack; + reject(error); + break; + } + default: { + const exhaustiveCheck: never = message; + throw new Error(`Unexpected message type: ${JSON.stringify(exhaustiveCheck)}`); + } + } + }); + + worker.on('error', (err) => { + reject(err); + }); + + worker.on('exit', (code) => { + if (code !== 0) { + reject(new Error(`Worker stopped with exit code ${code}`)); + } + }); + + const commandMessage: IHostToWorkerMessage = { + type: 'zipsync-pack', + id: 0, + options + }; + worker.postMessage(commandMessage); + }).finally(() => { + worker.postMessage(false); + }); +} diff --git a/apps/zipsync/src/perf.ts b/apps/zipsync/src/perf.ts new file mode 100644 index 00000000000..1e4677a8eee --- /dev/null +++ b/apps/zipsync/src/perf.ts @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { PerformanceEntry } from 'node:perf_hooks'; +import { performance } from 'node:perf_hooks'; + +import type { ITerminal } from '@rushstack/terminal/lib/ITerminal'; + +export function markStart(name: string): void { + performance.mark(`zipsync:${name}:start`); +} +export function markEnd(name: string): void { + const base: string = `zipsync:${name}`; + performance.mark(`${base}:end`); + performance.measure(base, `${base}:start`, `${base}:end`); +} +export function getDuration(name: string): number { + const measures: PerformanceEntry[] = performance.getEntriesByName( + `zipsync:${name}` + ) as unknown as PerformanceEntry[]; + if (measures.length === 0) return 0; + return measures[measures.length - 1].duration; +} +export function formatDuration(ms: number): string { + return ms >= 1000 ? (ms / 1000).toFixed(2) + 's' : ms.toFixed(2) + 'ms'; +} +export function emitSummary(operation: 'pack' | 'unpack', term: ITerminal): void { + const totalName: string = `${operation}.total`; + // Ensure total is measured + markEnd(totalName); + const totalDuration: number = getDuration(totalName); + const prefix: string = `zipsync:${operation}.`; + const measures: PerformanceEntry[] = performance.getEntriesByType( + 'measure' + ) as unknown as PerformanceEntry[]; + const rows: Array<{ name: string; dur: number }> = []; + for (const m of measures) { + if (!m.name.startsWith(prefix)) continue; + if (m.name === `zipsync:${totalName}`) continue; + // Extract segment name (remove prefix) + const segment: string = m.name.substring(prefix.length); + rows.push({ name: segment, dur: m.duration }); + } + rows.sort((a, b) => b.dur - a.dur); + const lines: string[] = rows.map((r) => { + const pct: number = totalDuration ? (r.dur / totalDuration) * 100 : 0; + return ` ${r.name}: ${formatDuration(r.dur)} (${pct.toFixed(1)}%)`; + }); + lines.push(` TOTAL ${operation}.total: ${formatDuration(totalDuration)}`); + term.writeVerboseLine(`Performance summary (${operation}):\n` + lines.join('\n')); + // Cleanup marks/measures to avoid unbounded growth + performance.clearMarks(); + performance.clearMeasures(); +} diff --git a/apps/zipsync/src/start.ts b/apps/zipsync/src/start.ts new file mode 100644 index 00000000000..14e23e53406 --- /dev/null +++ b/apps/zipsync/src/start.ts @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { ConsoleTerminalProvider } from '@rushstack/terminal/lib/ConsoleTerminalProvider'; +import { Terminal } from '@rushstack/terminal/lib/Terminal'; + +import { version } from '../package.json'; +import { ZipSyncCommandLineParser } from './cli/ZipSyncCommandLineParser'; + +const toolVersion: string = version; + +const consoleTerminalProvider: ConsoleTerminalProvider = new ConsoleTerminalProvider(); +const terminal: Terminal = new Terminal(consoleTerminalProvider); + +terminal.writeLine(); +terminal.writeLine(`zipsync ${toolVersion} - https://rushstack.io`); +terminal.writeLine(); + +const commandLine: ZipSyncCommandLineParser = new ZipSyncCommandLineParser(consoleTerminalProvider, terminal); +commandLine.executeAsync().catch((error) => { + terminal.writeError(error); +}); diff --git a/apps/zipsync/src/test/benchmark.test.ts b/apps/zipsync/src/test/benchmark.test.ts new file mode 100644 index 00000000000..244b30ab203 --- /dev/null +++ b/apps/zipsync/src/test/benchmark.test.ts @@ -0,0 +1,516 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/* eslint-disable no-console */ + +import { execSync } from 'node:child_process'; +import { tmpdir, cpus, platform, release, arch, totalmem } from 'node:os'; +import * as path from 'node:path'; +import * as fs from 'node:fs'; +import { createHash, randomUUID } from 'node:crypto'; + +import { NoOpTerminalProvider, Terminal } from '@rushstack/terminal'; + +import type { ZipSyncOptionCompression } from '../zipSyncUtils'; +import { pack } from '../pack'; +import { unpack } from '../unpack'; + +const compressionOptions = ['store', 'deflate', 'zstd', 'auto'] satisfies ZipSyncOptionCompression[]; + +// create a tempdir and setup dummy files there for benchmarking +const NUM_FILES = 1000; // number of files per subdir +let tempDir: string; +const runId = randomUUID(); +async function setupDemoDataAsync(): Promise { + console.log('Setting up demo data for benchmark...'); + tempDir = path.join(tmpdir(), `zipsync-benchmark-${runId}`); + fs.mkdirSync(tempDir, { recursive: true }); + + const demoSubDir1 = path.join(tempDir, 'demo-data', 'subdir1'); + fs.mkdirSync(demoSubDir1, { recursive: true }); + const demoSubDir2 = path.join(tempDir, 'demo-data', 'subdir2'); + fs.mkdirSync(demoSubDir2, { recursive: true }); + + for (let i = 0; i < NUM_FILES; i++) { + const filePath1 = path.join(demoSubDir1, `file${i}.txt`); + fs.writeFileSync(filePath1, `This is file ${i} in subdir1\n`.repeat(1000), { encoding: 'utf-8' }); + const filePath2 = path.join(demoSubDir2, `file${i}.txt`); + fs.writeFileSync(filePath2, `This is file ${i} in subdir2\n`.repeat(1000), { encoding: 'utf-8' }); + } + + console.log(`Demo data setup complete in ${tempDir}`); +} + +async function cleanupDemoDataAsync(): Promise { + if (tempDir && fs.existsSync(tempDir)) { + fs.rmSync(tempDir, { recursive: true, force: true }); + console.log(`Cleaned up temp directory: ${tempDir}`); + } +} + +beforeAll(async () => { + await setupDemoDataAsync(); +}); + +afterAll(async () => { + await cleanupDemoDataAsync(); +}); + +// Collect timings for table output after all tests +interface IMeasurement { + name: string; + kind: string; + phase: 'pack' | 'unpack'; + ms: number; + // Only for pack phase: archive size in bytes and compression ratio (archiveSize / uncompressedSourceSize) + sizeBytes?: number; +} +const measurements: IMeasurement[] = []; +// Allow specifying iterations via env BENCH_ITERATIONS. Defaults to 0 to avoid running the benchmark unless explicitly enabled. +function detectIterations(): number { + let iter = 0; + const envParsed: number = parseInt(process.env.BENCH_ITERATIONS || '', 10); + if (!isNaN(envParsed) && envParsed > 0) { + iter = envParsed; + } + return iter; +} +const ITERATIONS: number = detectIterations(); + +function measureFn(callback: () => void): number { + const start: number = performance.now(); + callback(); + return performance.now() - start; +} + +interface IBenchContext { + archive: string; + demoDir: string; // source demo data directory + unpackDir: string; +} + +interface IBenchCommands { + // Function that performs the packing. Receives archive path and demoDir. + pack: (ctx: IBenchContext) => void; + // Function that performs the unpack. Receives archive and unpackDir. + unpack: (ctx: IBenchContext) => void; + archive: string; + unpackDir: string; + populateUnpackDir?: 'full' | 'partial'; + cleanBeforeUnpack?: boolean; +} + +function bench(kind: string, commands: IBenchCommands): void { + const demoDataPath = path.join(tempDir, 'demo-data'); + const srcDir = demoDataPath; + // Compute total uncompressed source size once per bench invocation + // We intentionally no longer compute total source size for ratio; only archive size is tracked. + function verifyUnpack(unpackDir: string): void { + // Compare file listings and hashes + function buildMap(root: string): Map { + const map = new Map(); + function walk(current: string): void { + for (const entry of fs.readdirSync(current, { withFileTypes: true })) { + const full = path.join(current, entry.name); + if (entry.isDirectory()) { + walk(full); + } else if (entry.isFile()) { + const rel = path.relative(root, full).replace(/\\/g, '/'); + const buf = fs.readFileSync(full); + const hash = createHash('sha256').update(buf).digest('hex'); + map.set(rel, { size: buf.length, hash }); + } + } + } + walk(root); + return map; + } + const srcMap = buildMap(srcDir); + const dstMap = buildMap(unpackDir); + if (srcMap.size !== dstMap.size) { + throw new Error( + `Verification failed (${kind}): file count mismatch src=${srcMap.size} dst=${dstMap.size}` + ); + } + for (const [rel, meta] of srcMap) { + const other = dstMap.get(rel); + if (!other) throw new Error(`Verification failed (${kind}): missing file ${rel}`); + if (other.size !== meta.size || other.hash !== meta.hash) { + throw new Error(`Verification failed (${kind}): content mismatch in ${rel}`); + } + } + } + for (let i = 0; i < ITERATIONS; i++) { + // Ensure previous artifacts removed + if (fs.existsSync(commands.archive)) fs.rmSync(commands.archive, { force: true }); + if (commands.populateUnpackDir === 'full') { + fs.cpSync(srcDir, commands.unpackDir, { recursive: true }); + } else if (commands.populateUnpackDir === 'partial') { + // Copy half the files + for (let j = 0; j < NUM_FILES / 2; j++) { + const file1 = path.join(srcDir, 'subdir1', `file${j}.txt`); + const file2 = path.join(srcDir, 'subdir2', `file${j}.txt`); + const dest1 = path.join(commands.unpackDir, 'subdir1', `file${j}.txt`); + const dest2 = path.join(commands.unpackDir, 'subdir2', `file${j}.txt`); + fs.mkdirSync(path.dirname(dest1), { recursive: true }); + fs.mkdirSync(path.dirname(dest2), { recursive: true }); + fs.copyFileSync(file1, dest1); + fs.copyFileSync(file2, dest2); + } + } + + let archiveSize: number | undefined; + const packMs: number = measureFn(() => { + commands.pack({ archive: commands.archive, demoDir: demoDataPath, unpackDir: commands.unpackDir }); + try { + const stat = fs.statSync(commands.archive); + archiveSize = stat.size; + } catch { + // ignore if archive not found + } + }); + measurements.push({ + name: `${kind}#${i + 1}`, + kind, + phase: 'pack', + ms: packMs, + sizeBytes: archiveSize + }); + + const unpackMs: number = measureFn(() => { + if (commands.cleanBeforeUnpack) { + fs.rmSync(commands.unpackDir, { recursive: true, force: true }); + fs.mkdirSync(commands.unpackDir, { recursive: true }); + } + commands.unpack({ archive: commands.archive, demoDir: demoDataPath, unpackDir: commands.unpackDir }); + }); + measurements.push({ name: `${kind}#${i + 1}`, kind, phase: 'unpack', ms: unpackMs }); + verifyUnpack(commands.unpackDir); + } +} + +function benchZipSyncScenario( + kind: string, + compression: ZipSyncOptionCompression, + existingFiles: 'all' | 'none' | 'partial' +): void { + if (!tempDir) throw new Error('Temp directory is not set up.'); + const terminal = new Terminal(new NoOpTerminalProvider()); + bench(kind, { + pack: ({ archive, demoDir }) => { + const { filesPacked } = pack({ + archivePath: archive, + targetDirectories: ['subdir1', 'subdir2'], + baseDir: demoDir, + compression, + terminal + }); + console.log(`Files packed: ${filesPacked}`); + }, + unpack: ({ archive, unpackDir }) => { + const { filesDeleted, filesExtracted, filesSkipped, foldersDeleted, otherEntriesDeleted } = unpack({ + archivePath: archive, + targetDirectories: ['subdir1', 'subdir2'], + baseDir: unpackDir, + terminal + }); + console.log( + `Files extracted: ${filesExtracted}, files skipped: ${filesSkipped}, files deleted: ${filesDeleted}, folders deleted: ${foldersDeleted}, other entries deleted: ${otherEntriesDeleted}` + ); + }, + archive: path.join(tempDir, `archive-zipsync-${compression}.zip`), + unpackDir: path.join(tempDir, `unpacked-zipsync-${compression}-${existingFiles}`), + populateUnpackDir: existingFiles === 'all' ? 'full' : existingFiles === 'partial' ? 'partial' : undefined, + cleanBeforeUnpack: false // cleaning is handled internally by zipsync + }); +} + +// the benchmarks are skipped by default because they require external tools (tar, zip) to be installed +describe(`archive benchmarks (iterations=${ITERATIONS})`, () => { + it('tar', () => { + if (!isTarAvailable()) { + console.log('Skipping tar test because tar is not available'); + return; + } + if (!tempDir) throw new Error('Temp directory is not set up.'); + bench('tar', { + pack: ({ archive, demoDir }) => execSync(`tar -cf "${archive}" -C "${demoDir}" .`), + unpack: ({ archive, unpackDir }) => execSync(`tar -xf "${archive}" -C "${unpackDir}"`), + archive: path.join(tempDir, 'archive.tar'), + unpackDir: path.join(tempDir, 'unpacked-tar'), + populateUnpackDir: 'full', + cleanBeforeUnpack: true + }); + }); + it('tar-gz', () => { + if (!isTarAvailable()) { + console.log('Skipping tar test because tar is not available'); + return; + } + if (!tempDir) throw new Error('Temp directory is not set up.'); + bench('tar-gz', { + pack: ({ archive, demoDir }) => execSync(`tar -czf "${archive}" -C "${demoDir}" .`), + unpack: ({ archive, unpackDir }) => execSync(`tar -xzf "${archive}" -C "${unpackDir}"`), + archive: path.join(tempDir, 'archive.tar.gz'), + unpackDir: path.join(tempDir, 'unpacked-tar-gz'), + populateUnpackDir: 'full', + cleanBeforeUnpack: true + }); + }); + it('zip-store', () => { + if (!isZipAvailable()) { + console.log('Skipping zip test because zip is not available'); + return; + } + if (!tempDir) throw new Error('Temp directory is not set up.'); + bench('zip-store', { + pack: ({ archive, demoDir }) => execSync(`zip -r -Z store "${archive}" .`, { cwd: demoDir }), + unpack: ({ archive, unpackDir }) => execSync(`unzip "${archive}" -d "${unpackDir}"`), + archive: path.join(tempDir, 'archive.zip'), + unpackDir: path.join(tempDir, 'unpacked-zip'), + populateUnpackDir: 'full', + cleanBeforeUnpack: true + }); + }); + it('zip-deflate', () => { + if (!isZipAvailable()) { + console.log('Skipping zip test because zip is not available'); + return; + } + if (!tempDir) throw new Error('Temp directory is not set up.'); + bench('zip-deflate', { + pack: ({ archive, demoDir }) => execSync(`zip -r -Z deflate -9 "${archive}" .`, { cwd: demoDir }), + unpack: ({ archive, unpackDir }) => execSync(`unzip "${archive}" -d "${unpackDir}"`), + archive: path.join(tempDir, 'archive-deflate.zip'), + unpackDir: path.join(tempDir, 'unpacked-zip-deflate'), + populateUnpackDir: 'full', + cleanBeforeUnpack: true + }); + }); + const existingFileOptions: ['all', 'none', 'partial'] = ['all', 'none', 'partial']; + compressionOptions.forEach((compression) => { + if (compression === 'zstd') { + const [major, minor] = process.versions.node.split('.').map((x) => parseInt(x, 10)); + if (major < 22 || (major === 22 && minor < 15)) { + console.warn(`Skipping zstd test on Node ${process.versions.node}`); + return; + } + } + existingFileOptions.forEach((existingFiles) => { + it(`zipsync-${compression}-${existingFiles}-existing`, () => { + benchZipSyncScenario(`zipsync-${compression}-${existingFiles}-existing`, compression, existingFiles); + }); + }); + }); +}); + +afterAll(() => { + if (!measurements.length) return; + interface IStats { + kind: string; + phase: string; + n: number; + min: number; + max: number; + mean: number; + p95: number; + std: number; + sizeMean?: number; // only for pack + } + const groups: Map = new Map(); + for (const m of measurements) { + const key: string = `${m.kind}|${m.phase}`; + let bucket = groups.get(key); + if (!bucket) { + bucket = { times: [], sizes: [] }; + groups.set(key, bucket); + } + bucket.times.push(m.ms); + if (typeof m.sizeBytes === 'number') bucket.sizes.push(m.sizeBytes); + } + const stats: IStats[] = []; + function percentile(sorted: number[], p: number): number { + if (!sorted.length) return 0; + const idx: number = Math.min(sorted.length - 1, Math.ceil((p / 100) * sorted.length) - 1); + return sorted[idx]; + } + for (const [key, bucket] of groups) { + const [kind, phase] = key.split('|'); + bucket.times.sort((a, b) => a - b); + const arr = bucket.times; + const n = arr.length; + const min = arr[0]; + const max = arr[n - 1]; + const sum = arr.reduce((a, b) => a + b, 0); + const mean = sum / n; + const variance = arr.reduce((a, b) => a + (b - mean) * (b - mean), 0) / n; + const std = Math.sqrt(variance); + const p95 = percentile(arr, 95); + const sizeMean = bucket.sizes.length + ? bucket.sizes.reduce((a, b) => a + b, 0) / bucket.sizes.length + : undefined; + stats.push({ kind, phase, n, min, max, mean, std, p95, sizeMean }); + } + // Organize into groups + const groupsDef: Array<{ title: string; baseline: string; members: string[] }> = [ + { + title: 'Compressed (baseline: tar-gz)', + baseline: 'tar-gz', + members: [ + 'tar-gz', + 'zip-deflate', + 'zipsync-zstd-all-existing', + 'zipsync-zstd-none-existing', + 'zipsync-zstd-partial-existing', + 'zipsync-deflate-all-existing', + 'zipsync-deflate-none-existing', + 'zipsync-deflate-partial-existing', + 'zipsync-auto-all-existing', + 'zipsync-auto-none-existing', + 'zipsync-auto-partial-existing' + ] + }, + { + title: 'Uncompressed (baseline: tar)', + baseline: 'tar', + members: [ + 'tar', + 'zip-store', + 'zipsync-store-all-existing', + 'zipsync-store-none-existing', + 'zipsync-store-partial-existing' + ] + } + ]; + // Build per-group markdown tables (no Group column) for each phase + function buildGroupTable( + group: { title: string; baseline: string; members: string[] }, + phase: 'pack' | 'unpack' + ): string[] { + // Human readable bytes formatter + function formatBytes(bytes: number): string { + const units = ['B', 'KB', 'MB', 'GB']; + let value = bytes; + let i = 0; + while (value >= 1024 && i < units.length - 1) { + value /= 1024; + i++; + } + const formatted = value >= 100 ? value.toFixed(0) : value >= 10 ? value.toFixed(1) : value.toFixed(2); + return `${formatted} ${units[i]}`; + } + const headers = + phase === 'pack' + ? ['Archive', 'min (ms)', 'mean (ms)', 'p95 (ms)', 'max (ms)', 'std (ms)', 'speed (x)', 'size'] + : ['Archive', 'min (ms)', 'mean (ms)', 'p95 (ms)', 'max (ms)', 'std (ms)', 'speed (x)']; + const lines: string[] = []; + lines.push('| ' + headers.join(' | ') + ' |'); + const align: string[] = headers.map((header, idx) => (idx === 0 ? '---' : '---:')); + lines.push('| ' + align.join(' | ') + ' |'); + const baselineStats: IStats | undefined = stats.find( + (s) => s.kind === group.baseline && s.phase === phase + ); + for (const member of group.members) { + const s: IStats | undefined = stats.find((st) => st.kind === member && st.phase === phase); + if (!s) continue; + const isBaseline: boolean = member === group.baseline; + const speedFactor: number = baselineStats ? baselineStats.mean / s.mean : 1; + const cols: string[] = [ + (isBaseline ? '**' : '') + s.kind + (isBaseline ? '**' : ''), + s.min.toFixed(2), + s.mean.toFixed(2), + s.p95.toFixed(2), + s.max.toFixed(2), + s.std.toFixed(2), + speedFactor.toFixed(2) + 'x' + ]; + if (phase === 'pack') { + cols.push(s.sizeMean !== undefined ? formatBytes(Math.round(s.sizeMean)) : ''); + } + lines.push('| ' + cols.join(' | ') + ' |'); + } + return lines; + } + const outputLines: string[] = []; + outputLines.push('# Benchmark Results'); + outputLines.push(''); + outputLines.push( + ` +This document contains performance measurements for packing and unpacking a synthetic dataset using tar, zip, and zipsync. + +The dataset consists of two directory trees (subdir1, subdir2) populated with ${NUM_FILES} text files each. + +zipsync scenarios +* "all-existing": unpack directory is fully populated with existing files +* "none-existing": unpack directory is empty +* "partial-existing": unpack directory contains half of the files + +zip and tar scenarios clean the unpack directory before unpacking. This time is included in the measurements because +zipsync internally handles cleaning as part of its operation. +` + ); + outputLines.push(''); + // System info + try { + const cpuList = cpus(); + const cpuModelRaw: string | undefined = cpuList[0]?.model; + const cpuModel: string = cpuModelRaw ? cpuModelRaw.replace(/\|/g, ' ').trim() : 'unknown'; + const logicalCores: number = cpuList.length || 0; + const memGB: string = (totalmem() / 1024 ** 3).toFixed(1); + outputLines.push('**System**'); + outputLines.push(''); + outputLines.push('| OS | Arch | Node | CPU | Logical Cores | Memory |'); + outputLines.push('| --- | --- | --- | --- | ---: | --- |'); + outputLines.push( + `| ${platform()} ${release()} | ${arch()} | ${ + process.version + } | ${cpuModel} | ${logicalCores} | ${memGB} GB |` + ); + outputLines.push(''); + } catch { + // ignore system info errors + } + outputLines.push(`Iterations: ${ITERATIONS}`); + outputLines.push(''); + for (const g of groupsDef) { + outputLines.push(`## ${g.title}`); + outputLines.push(''); + outputLines.push('### Unpack Phase'); + outputLines.push(''); + outputLines.push(...buildGroupTable(g, 'unpack')); + outputLines.push(''); + outputLines.push('### Pack Phase'); + outputLines.push(''); + outputLines.push(...buildGroupTable(g, 'pack')); + outputLines.push(''); + } + const resultText = outputLines.join('\n'); + console.log(resultText); + try { + const resultFile = path.join(__dirname, '..', 'temp', `benchmark-results.md`); + fs.writeFileSync(resultFile, resultText, { encoding: 'utf-8' }); + console.log(`Benchmark results written to: ${resultFile}`); + } catch (e) { + console.warn('Failed to write benchmark results file:', (e as Error).message); + } +}); +function isZipAvailable(): boolean { + try { + const checkZip = process.platform === 'win32' ? 'where zip' : 'command -v zip'; + const checkUnzip = process.platform === 'win32' ? 'where unzip' : 'command -v unzip'; + execSync(checkZip, { stdio: 'ignore' }); + execSync(checkUnzip, { stdio: 'ignore' }); + return true; + } catch { + return false; + } +} +function isTarAvailable(): boolean { + try { + const checkTar = process.platform === 'win32' ? 'where tar' : 'command -v tar'; + execSync(checkTar, { stdio: 'ignore' }); + return true; + } catch { + return false; + } +} diff --git a/apps/zipsync/src/test/crc32.test.ts b/apps/zipsync/src/test/crc32.test.ts new file mode 100644 index 00000000000..67ccbb9180c --- /dev/null +++ b/apps/zipsync/src/test/crc32.test.ts @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as zlib from 'node:zlib'; + +import { fallbackCrc32 } from '../crc32'; + +describe('crc32', () => { + it('fallbackCrc32 should match zlib.crc32', () => { + if (!zlib.crc32) { + // eslint-disable-next-line no-console + console.log('Skipping test because zlib.crc32 is not available in this Node.js version'); + return; + } + + const testData = [ + Buffer.from('hello world', 'utf-8'), + Buffer.alloc(0), // empty buffer + Buffer.from('hello crc32', 'utf-8'), + Buffer.from([-1, 2, 3, 4, 5, 255, 0, 128]) + ]; + + let fallbackCrc: number = 0; + let zlibCrc: number = 0; + + for (const data of testData) { + fallbackCrc = fallbackCrc32(data, fallbackCrc); + zlibCrc = zlib.crc32(data, zlibCrc); + } + + fallbackCrc = fallbackCrc >>> 0; + zlibCrc = zlibCrc >>> 0; + + expect(fallbackCrc).toBe(zlibCrc); + }); +}); diff --git a/apps/zipsync/src/test/index.test.ts b/apps/zipsync/src/test/index.test.ts new file mode 100644 index 00000000000..42dd8d96331 --- /dev/null +++ b/apps/zipsync/src/test/index.test.ts @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import * as fs from 'node:fs'; + +import { NoOpTerminalProvider } from '@rushstack/terminal/lib/NoOpTerminalProvider'; +import { Terminal } from '@rushstack/terminal/lib/Terminal'; + +import { pack } from '../pack'; +import { unpack } from '../unpack'; +import { getDemoDataDirectoryDisposable } from './testUtils'; +import type { ZipSyncOptionCompression } from '../zipSyncUtils'; + +describe('zipSync tests', () => { + it(`basic pack test`, () => { + const compressionOptions = ['store', 'deflate', 'zstd', 'auto'] satisfies ZipSyncOptionCompression[]; + compressionOptions.forEach((compression) => { + if (compression === 'zstd') { + const [major, minor] = process.versions.node.split('.').map((x) => parseInt(x, 10)); + if (major < 22 || (major === 22 && minor < 15)) { + // eslint-disable-next-line no-console + console.warn(`Skipping zstd test on Node ${process.versions.node}`); + return; + } + } + + using demoDataDisposable = getDemoDataDirectoryDisposable(5); + const { targetDirectories, baseDir, metadata } = demoDataDisposable; + + const terminal = new Terminal(new NoOpTerminalProvider()); + + const archivePath: string = path.join(baseDir, 'archive.zip'); + const packResult = pack({ + terminal: terminal, + compression, + baseDir, + targetDirectories, + archivePath + }); + + expect(packResult).toMatchObject({ filesPacked: 21, metadata }); + + using unpackDemoDataDisposable = getDemoDataDirectoryDisposable(2); + const { baseDir: unpackBaseDir } = unpackDemoDataDisposable; + + const unpackResult = unpack({ + terminal: terminal, + archivePath, + baseDir: unpackBaseDir, + targetDirectories + }); + + expect(unpackResult).toMatchObject({ + filesDeleted: 0, + filesExtracted: 12, + filesSkipped: 8, + foldersDeleted: 0, + metadata + }); + + // Verify files were extracted + for (const targetDirectory of targetDirectories) { + const sourceDir: string = path.join(baseDir, targetDirectory); + for (let i: number = 0; i < 5; ++i) { + const sourceFile: string = path.join(sourceDir, 'subdir', `file-${i}.txt`); + const destFile: string = path.join(unpackBaseDir, targetDirectory, 'subdir', `file-${i}.txt`); + expect(fs.readFileSync(destFile, { encoding: 'utf-8' })).toEqual( + fs.readFileSync(sourceFile, { encoding: 'utf-8' }) + ); + } + } + }); + }); +}); diff --git a/apps/zipsync/src/test/testUtils.ts b/apps/zipsync/src/test/testUtils.ts new file mode 100644 index 00000000000..6d75dbcfdfc --- /dev/null +++ b/apps/zipsync/src/test/testUtils.ts @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { tmpdir } from 'node:os'; +import * as path from 'node:path'; +import * as fs from 'node:fs'; +import * as crypto from 'node:crypto'; + +import type { IMetadata } from '../zipSyncUtils'; + +export function getTempDir(): string { + const randomId: string = crypto.randomUUID(); + const tempDir: string = path.join(tmpdir(), `zipsync-test-${randomId}`); + fs.mkdirSync(tempDir); + return tempDir; +} + +export function getDemoDataDirectoryDisposable(numFiles: number): { + targetDirectories: string[]; + baseDir: string; + metadata: IMetadata; + [Symbol.dispose](): void; +} { + const baseDir: string = getTempDir(); + + const metadata: IMetadata = { files: {}, version: '1.0' }; + + const targetDirectories: string[] = ['demo-data-1', 'demo-data-2', 'demo-data-3', 'nested/demo/dir/4'].map( + (folderName) => { + const dataDir: string = path.join(baseDir, folderName); + fs.mkdirSync(dataDir, { recursive: true }); + const subdir: string = path.join(dataDir, 'subdir'); + fs.mkdirSync(subdir); + for (let i: number = 0; i < numFiles; ++i) { + const filePath: string = path.join(subdir, `file-${i}.txt`); + const content: string = `This is file ${i} in ${folderName}/subdir\n`; + const sha1Hash: string = crypto.createHash('sha1').update(content).digest('hex'); + fs.writeFileSync(filePath, content, { encoding: 'utf-8' }); + const relativeFilePath: string = path.relative(baseDir, filePath).replace(/\\/g, '/'); + metadata.files[relativeFilePath] = { size: content.length, sha1Hash }; + } + return folderName; + } + ); + + return { + targetDirectories, + baseDir, + metadata, + [Symbol.dispose]() { + fs.rmSync(baseDir, { recursive: true, force: true }); + } + }; +} diff --git a/apps/zipsync/src/test/workerAsync.test.ts b/apps/zipsync/src/test/workerAsync.test.ts new file mode 100644 index 00000000000..07a09357259 --- /dev/null +++ b/apps/zipsync/src/test/workerAsync.test.ts @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import * as fs from 'node:fs'; + +import { unpackWorkerAsync } from '../unpackWorkerAsync'; +import { packWorkerAsync } from '../packWorkerAsync'; +import { getDemoDataDirectoryDisposable } from './testUtils'; + +describe('zipSyncWorkerAsync tests', () => { + it('basic pack test', async () => { + using demoDataDisposable = getDemoDataDirectoryDisposable(5); + const { targetDirectories, baseDir, metadata } = demoDataDisposable; + + const archivePath: string = path.join(baseDir, 'archive.zip'); + const { zipSyncReturn: packResult } = await packWorkerAsync({ + compression: 'deflate', + baseDir, + targetDirectories, + archivePath + }); + + expect(packResult).toMatchObject({ filesPacked: 21, metadata }); + + using unpackDemoDataDisposable = getDemoDataDirectoryDisposable(2); + const { baseDir: unpackBaseDir } = unpackDemoDataDisposable; + + const { zipSyncReturn: unpackResult } = await unpackWorkerAsync({ + archivePath, + baseDir: unpackBaseDir, + targetDirectories + }); + + expect(unpackResult).toMatchObject({ + filesDeleted: 0, + filesExtracted: 12, + filesSkipped: 8, + foldersDeleted: 0, + metadata + }); + + // Verify files were extracted + for (const targetDirectory of targetDirectories) { + const sourceDir: string = path.join(baseDir, targetDirectory); + for (let i: number = 0; i < 5; ++i) { + const sourceFile: string = path.join(sourceDir, 'subdir', `file-${i}.txt`); + const destFile: string = path.join(unpackBaseDir, targetDirectory, 'subdir', `file-${i}.txt`); + expect(fs.readFileSync(destFile, { encoding: 'utf-8' })).toEqual( + fs.readFileSync(sourceFile, { encoding: 'utf-8' }) + ); + } + } + }); +}); diff --git a/apps/zipsync/src/unpack.ts b/apps/zipsync/src/unpack.ts new file mode 100644 index 00000000000..f4592c34d16 --- /dev/null +++ b/apps/zipsync/src/unpack.ts @@ -0,0 +1,368 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import * as zlib from 'node:zlib'; + +import { type IReadonlyPathTrieNode, LookupByPath } from '@rushstack/lookup-by-path/lib/LookupByPath'; +import type { ITerminal } from '@rushstack/terminal'; + +import { getDisposableFileHandle, rmdirSync, unlinkSync, type IDisposableFileHandle } from './fs'; +import { type IIncrementalZlib, type IncrementalZlibMode, createIncrementalZlib } from './compress'; +import { markStart, markEnd, getDuration, emitSummary, formatDuration } from './perf'; +import { + findEndOfCentralDirectory, + parseCentralDirectoryHeader, + getFileFromZip, + ZSTD_COMPRESSION, + DEFLATE_COMPRESSION, + STORE_COMPRESSION, + type IEndOfCentralDirectory, + type ICentralDirectoryHeaderParseResult, + type ZipMetaCompressionMethod +} from './zipUtils'; +import { computeFileHash } from './hash'; +import { + defaultBufferSize, + METADATA_FILENAME, + METADATA_VERSION, + type IDirQueueItem, + type IMetadata +} from './zipSyncUtils'; + +const zlibUnpackModes: Record = { + [ZSTD_COMPRESSION]: 'zstd-decompress', + [DEFLATE_COMPRESSION]: 'inflate', + [STORE_COMPRESSION]: undefined +} as const; + +/** + * @public + * Options for zipsync + */ +export interface IZipSyncUnpackOptions { + /** + * \@rushstack/terminal compatible terminal for logging + */ + terminal: ITerminal; + /** + * Zip file path + */ + archivePath: string; + /** + * Target directories to unpack, relative to baseDir + */ + targetDirectories: ReadonlyArray; + /** + * Base directory for relative paths within the archive (defaults to common parent of targetDirectories) + */ + baseDir: string; + /** + * Optional buffer that can be provided to avoid internal allocations. + */ + outputBuffer?: Buffer; +} + +export interface IZipSyncUnpackResult { + metadata: IMetadata; + filesExtracted: number; + filesSkipped: number; + filesDeleted: number; + foldersDeleted: number; + otherEntriesDeleted: number; +} + +/** + * Unpack a zipsync archive into the provided target directories. + */ +export function unpack({ + archivePath, + targetDirectories: rawTargetDirectories, + baseDir: rawBaseDir, + terminal, + outputBuffer = Buffer.allocUnsafeSlow(defaultBufferSize) +}: IZipSyncUnpackOptions): IZipSyncUnpackResult { + const baseDir: string = path.resolve(rawBaseDir); + const targetDirectories: string[] = rawTargetDirectories.map((dir) => path.join(baseDir, dir)); + terminal.writeLine(`Unpacking to ${rawTargetDirectories.join(', ')} from ${archivePath}`); + + markStart('unpack.total'); + terminal.writeDebugLine('Starting unpackZip'); + + // Read entire archive into memory (build cache entries are expected to be relatively small/medium). + markStart('unpack.read.archive'); + const zipBuffer: Buffer = fs.readFileSync(archivePath); + terminal.writeDebugLine(`Archive size=${zipBuffer.length} bytes`); + markEnd('unpack.read.archive'); + + // Locate & parse central directory so we have random-access metadata for all entries. + markStart('unpack.parse.centralDirectory'); + const zipTree: LookupByPath = new LookupByPath(); + const endOfCentralDir: IEndOfCentralDirectory = findEndOfCentralDirectory(zipBuffer); + + const centralDirBuffer: Buffer = zipBuffer.subarray( + endOfCentralDir.centralDirOffset, + endOfCentralDir.centralDirOffset + endOfCentralDir.centralDirSize + ); + terminal.writeDebugLine( + `Central directory slice size=${centralDirBuffer.length} (expected=${endOfCentralDir.centralDirSize})` + ); + + let metadataEntry: ICentralDirectoryHeaderParseResult | undefined; + const entries: Array = []; + let offset: number = 0; + + for (let i: number = 0; i < endOfCentralDir.totalCentralDirRecords; i++) { + const result: ICentralDirectoryHeaderParseResult = parseCentralDirectoryHeader(centralDirBuffer, offset); + zipTree.setItem(result.filename, true); + + if (result.filename === METADATA_FILENAME) { + if (metadataEntry) { + throw new Error('Multiple metadata entries found in archive'); + } + metadataEntry = result; + } + + entries.push(result); + offset = result.nextOffset; + terminal.writeDebugLine( + `Parsed central entry ${result.filename} (method=${result.header.compressionMethod}, compSize=${result.header.compressedSize})` + ); + } + markEnd('unpack.parse.centralDirectory'); + + if (!metadataEntry) { + throw new Error(`Metadata entry not found in archive`); + } + + markStart('unpack.read.metadata'); + terminal.writeDebugLine('Metadata entry found, reading'); + const metadataZipBuffer: Buffer = getFileFromZip(zipBuffer, metadataEntry); + + let metadataBuffer: Buffer; + if (metadataEntry.header.compressionMethod === STORE_COMPRESSION) { + metadataBuffer = metadataZipBuffer; + } else if (metadataEntry.header.compressionMethod === DEFLATE_COMPRESSION) { + metadataBuffer = zlib.inflateRawSync(metadataZipBuffer); + if (metadataBuffer.length !== metadataEntry.header.uncompressedSize) { + throw new Error( + `Metadata size mismatch (expected ${metadataEntry.header.uncompressedSize}, got ${metadataBuffer.length})` + ); + } + } else { + throw new Error(`Unsupported compression method for metadata: ${metadataEntry.header.compressionMethod}`); + } + + const metadata: IMetadata = JSON.parse(metadataBuffer.toString('utf8')) as IMetadata; + + if (metadata.version !== METADATA_VERSION) { + throw new Error(`Unsupported metadata version: ${metadata.version}`); + } + + terminal.writeDebugLine( + `Metadata (version=${metadata.version}) parsed (fileCount=${Object.keys(metadata.files).length}, rawSize=${metadataBuffer.length})` + ); + markEnd('unpack.read.metadata'); + + terminal.writeLine(`Found ${entries.length} files in archive`); + + // Ensure root target directories exist (they may be empty initially for cache misses). + for (const targetDirectory of targetDirectories) { + fs.mkdirSync(targetDirectory, { recursive: true }); + terminal.writeDebugLine(`Ensured target directory: ${targetDirectory}`); + } + + let extractedCount: number = 0; + let skippedCount: number = 0; + let deletedFilesCount: number = 0; + let deletedOtherCount: number = 0; + let deletedFoldersCount: number = 0; + let scanCount: number = 0; + + const dirsToCleanup: string[] = []; + + // Phase: scan filesystem to delete entries not present in archive and record empty dirs for later removal. + markStart('unpack.scan.existing'); + const queue: IDirQueueItem[] = targetDirectories.map((dir) => ({ + dir, + depth: 0, + node: zipTree.getNodeAtPrefix(path.relative(baseDir, dir)) + })); + + while (queue.length) { + const { dir: currentDir, depth, node } = queue.shift()!; + terminal.writeDebugLine(`Enumerating directory: ${currentDir}`); + + const padding: string = depth === 0 ? '' : '-↳'.repeat(depth); + + let items: fs.Dirent[]; + try { + items = fs.readdirSync(currentDir, { withFileTypes: true }); + } catch (e) { + terminal.writeWarningLine(`Failed to read directory: ${currentDir}`); + continue; + } + + for (const item of items) { + scanCount++; + // check if exists in zipTree, if not delete + const relativePath: string = path + .relative(baseDir, path.join(currentDir, item.name)) + .replace(/\\/g, '/'); + + const childNode: IReadonlyPathTrieNode | undefined = node?.children?.get(item.name); + + if (item.isFile()) { + terminal.writeVerboseLine(`${padding}${item.name}`); + if (!childNode?.value) { + terminal.writeDebugLine(`Deleting file: ${relativePath}`); + if (unlinkSync(relativePath)) { + deletedFilesCount++; + } + } + } else if (item.isDirectory()) { + terminal.writeVerboseLine(`${padding}${item.name}/`); + queue.push({ dir: relativePath, depth: depth + 1, node: childNode }); + if (!childNode || childNode.value) { + dirsToCleanup.push(relativePath); + } + } else { + terminal.writeVerboseLine(`${padding}${item.name} (not file or directory, deleting)`); + if (unlinkSync(relativePath)) { + deletedOtherCount++; + } + } + } + } + + // Try to delete now-empty directories (created in previous builds but not in this archive). + for (const dir of dirsToCleanup) { + // Try to remove the directory. If it is not empty, this will throw and we can ignore the error. + if (rmdirSync(dir)) { + terminal.writeDebugLine(`Deleted empty directory: ${dir}`); + deletedFoldersCount++; + } + } + + terminal.writeDebugLine(`Existing entries tracked: ${scanCount}`); + markEnd('unpack.scan.existing'); + + markStart('unpack.extract.loop'); + + /** + * Stream-decompress (or copy) an individual file from the archive into place. + * We allocate a single large output buffer reused for all inflation operations to limit GC. + */ + function extractFileFromZip(targetPath: string, entry: ICentralDirectoryHeaderParseResult): void { + terminal.writeDebugLine(`Extracting file: ${entry.filename}`); + const fileZipBuffer: Buffer = getFileFromZip(zipBuffer, entry); + let fileData: Buffer; + using fileHandle: IDisposableFileHandle = getDisposableFileHandle(targetPath, 'w'); + if (entry.header.compressionMethod === STORE_COMPRESSION) { + fileData = fileZipBuffer; + let writeOffset: number = 0; + while (writeOffset < fileData.length && !isNaN(fileHandle.fd)) { + const written: number = fs.writeSync( + fileHandle.fd, + fileData, + writeOffset, + fileData.length - writeOffset + ); + writeOffset += written; + } + } else if ( + entry.header.compressionMethod === DEFLATE_COMPRESSION || + entry.header.compressionMethod === ZSTD_COMPRESSION + ) { + using incrementalZlib: IIncrementalZlib = createIncrementalZlib( + outputBuffer, + (chunk, lengthBytes) => { + let writeOffset: number = 0; + while (lengthBytes > 0 && writeOffset < chunk.byteLength) { + const written: number = fs.writeSync(fileHandle.fd, chunk, writeOffset, lengthBytes); + lengthBytes -= written; + writeOffset += written; + } + }, + zlibUnpackModes[entry.header.compressionMethod]! + ); + incrementalZlib.update(fileZipBuffer); + incrementalZlib.update(Buffer.alloc(0)); + } else { + throw new Error( + `Unsupported compression method: ${entry.header.compressionMethod} for ${entry.filename}` + ); + } + } + + /** + * Decide whether a file needs extraction by comparing existing file SHA‑1 vs metadata. + * If file is missing or hash differs we extract; otherwise we skip to preserve existing inode/data. + */ + function shouldExtract(targetPath: string, entry: ICentralDirectoryHeaderParseResult): boolean { + if (metadata) { + const metadataFile: { size: number; sha1Hash: string } | undefined = metadata.files[entry.filename]; + + if (metadataFile) { + try { + using existingFile: IDisposableFileHandle = getDisposableFileHandle(targetPath, 'r'); + const existingHash: string | false = computeFileHash(existingFile.fd); + if (existingHash === metadataFile.sha1Hash) { + return false; + } + } catch (e) { + if ((e as NodeJS.ErrnoException).code === 'ENOENT') { + terminal.writeDebugLine(`File does not exist, will extract: ${entry.filename}`); + } else { + throw e; + } + } + } + } + return true; + } + + const dirsCreated: Set = new Set(); + + // Iterate all entries excluding metadata; create parent dirs lazily; selective extraction. + for (const entry of entries) { + if (entry.filename === METADATA_FILENAME) { + continue; + } + + const targetPath: string = path.join(baseDir, entry.filename); + const targetDir: string = path.dirname(targetPath); + if (!dirsCreated.has(targetDir)) { + fs.mkdirSync(targetDir, { recursive: true }); + dirsCreated.add(targetDir); + } + + if (shouldExtract(targetPath, entry)) { + extractFileFromZip(targetPath, entry); + extractedCount++; + } else { + skippedCount++; + terminal.writeDebugLine(`Skip unchanged file: ${entry.filename}`); + } + } + markEnd('unpack.extract.loop'); + + markEnd('unpack.total'); + const unpackTotal: number = getDuration('unpack.total'); + terminal.writeLine( + `Extraction complete: ${extractedCount} extracted, ${skippedCount} skipped, ${deletedFilesCount} deleted, ${deletedFoldersCount} folders deleted, ${deletedOtherCount} other entries deleted in ${formatDuration( + unpackTotal + )}` + ); + emitSummary('unpack', terminal); + terminal.writeDebugLine('unpackZip finished'); + return { + metadata, + filesExtracted: extractedCount, + filesSkipped: skippedCount, + filesDeleted: deletedFilesCount, + foldersDeleted: deletedFoldersCount, + otherEntriesDeleted: deletedOtherCount + }; +} diff --git a/apps/zipsync/src/unpackWorker.ts b/apps/zipsync/src/unpackWorker.ts new file mode 100644 index 00000000000..b8ddedb5fa5 --- /dev/null +++ b/apps/zipsync/src/unpackWorker.ts @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { parentPort as rawParentPort, type MessagePort } from 'node:worker_threads'; + +import { Terminal } from '@rushstack/terminal/lib/Terminal'; +import { StringBufferTerminalProvider } from '@rushstack/terminal/lib/StringBufferTerminalProvider'; + +import { type IZipSyncUnpackOptions, type IZipSyncUnpackResult, unpack } from './unpack'; +import { defaultBufferSize } from './zipSyncUtils'; + +export { type IZipSyncUnpackOptions, type IZipSyncUnpackResult } from './unpack'; + +export interface IHashWorkerData { + basePath: string; +} + +export interface IZipSyncUnpackCommandMessage { + type: 'zipsync-unpack'; + id: number; + options: Omit; +} + +export interface IZipSyncUnpackWorkerResult { + zipSyncReturn: IZipSyncUnpackResult; + zipSyncLogs: string; +} + +interface IZipSyncUnpackSuccessMessage { + id: number; + type: 'zipsync-unpack'; + result: IZipSyncUnpackWorkerResult; +} + +export interface IZipSyncUnpackErrorMessage { + type: 'error'; + id: number; + args: { + message: string; + stack: string; + zipSyncLogs: string; + }; +} + +export type IHostToWorkerMessage = IZipSyncUnpackCommandMessage; +export type IWorkerToHostMessage = IZipSyncUnpackSuccessMessage | IZipSyncUnpackErrorMessage; + +if (!rawParentPort) { + throw new Error('This module must be run in a worker thread.'); +} +const parentPort: MessagePort = rawParentPort; + +let outputBuffer: Buffer | undefined = undefined; + +function handleMessage(message: IHostToWorkerMessage | false): void { + if (message === false) { + parentPort.removeAllListeners(); + parentPort.close(); + return; + } + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(); + const terminal: Terminal = new Terminal(terminalProvider); + + try { + switch (message.type) { + case 'zipsync-unpack': { + const { options } = message; + if (!outputBuffer) { + outputBuffer = Buffer.allocUnsafeSlow(defaultBufferSize); + } + + const successMessage: IZipSyncUnpackSuccessMessage = { + type: message.type, + id: message.id, + result: { + zipSyncReturn: unpack({ ...options, terminal, outputBuffer }), + zipSyncLogs: terminalProvider.getOutput() + } + }; + return parentPort.postMessage(successMessage); + } + } + } catch (err) { + const errorMessage: IZipSyncUnpackErrorMessage = { + type: 'error', + id: message.id, + args: { + message: (err as Error).message, + stack: (err as Error).stack || '', + zipSyncLogs: terminalProvider.getOutput() + } + }; + parentPort.postMessage(errorMessage); + } +} + +parentPort.on('message', handleMessage); diff --git a/apps/zipsync/src/unpackWorkerAsync.ts b/apps/zipsync/src/unpackWorkerAsync.ts new file mode 100644 index 00000000000..73714a016b7 --- /dev/null +++ b/apps/zipsync/src/unpackWorkerAsync.ts @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { Worker } from 'node:worker_threads'; + +import type { + IWorkerToHostMessage, + IHostToWorkerMessage, + IZipSyncUnpackWorkerResult, + IZipSyncUnpackOptions +} from './unpackWorker'; + +export type { IZipSyncUnpackWorkerResult } from './unpackWorker'; + +export async function unpackWorkerAsync( + options: Omit +): Promise { + const { Worker } = await import('node:worker_threads'); + + const worker: Worker = new Worker(require.resolve('./unpackWorker')); + + return new Promise((resolve, reject) => { + worker.on('message', (message: IWorkerToHostMessage) => { + switch (message.type) { + case 'zipsync-unpack': { + resolve(message.result); + break; + } + case 'error': { + const error: Error = new Error(message.args.message); + error.stack = message.args.stack; + reject(error); + break; + } + default: { + const exhaustiveCheck: never = message; + throw new Error(`Unexpected message type: ${JSON.stringify(exhaustiveCheck)}`); + } + } + }); + + worker.on('error', (err) => { + reject(err); + }); + + worker.on('exit', (code) => { + if (code !== 0) { + reject(new Error(`Worker stopped with exit code ${code}`)); + } + }); + + const commandMessage: IHostToWorkerMessage = { + type: 'zipsync-unpack', + id: 0, + options + }; + worker.postMessage(commandMessage); + }).finally(() => { + worker.postMessage(false); + }); +} diff --git a/apps/zipsync/src/zipSyncUtils.ts b/apps/zipsync/src/zipSyncUtils.ts new file mode 100644 index 00000000000..3e03062eb84 --- /dev/null +++ b/apps/zipsync/src/zipSyncUtils.ts @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IReadonlyPathTrieNode } from '@rushstack/lookup-by-path/lib/LookupByPath'; + +export const METADATA_FILENAME: string = '__zipsync_metadata__.json'; +export const METADATA_VERSION: string = '1.0'; + +export interface IDirQueueItem { + dir: string; + depth: number; + node?: IReadonlyPathTrieNode | undefined; +} + +export interface IMetadataFileRecord { + size: number; + sha1Hash: string; +} + +export interface IMetadata { + version: string; + files: Record; +} + +export type IZipSyncMode = 'pack' | 'unpack'; + +export type ZipSyncOptionCompression = 'store' | 'deflate' | 'zstd' | 'auto'; + +export const defaultBufferSize: number = 1 << 25; // 32 MiB diff --git a/apps/zipsync/src/zipUtils.ts b/apps/zipsync/src/zipUtils.ts new file mode 100644 index 00000000000..5dbd1d1f59c --- /dev/null +++ b/apps/zipsync/src/zipUtils.ts @@ -0,0 +1,411 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * Low-level ZIP structure helpers used by the zipsync pack/unpack pipeline. + * + * Spec reference: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT + */ + +/** + * Local file header signature PK\x03\x04 + */ +const LOCAL_FILE_HEADER_SIGNATURE: number = 0x04034b50; // PK\x03\x04 +/** + * Central directory file header signature PK\x01\x02 + */ +const CENTRAL_DIR_HEADER_SIGNATURE: number = 0x02014b50; // PK\x01\x02 +/** + * End of central directory signature PK\x05\x06 + */ +const END_OF_CENTRAL_DIR_SIGNATURE: number = 0x06054b50; // PK\x05\x06 +/** + * Data descriptor signature PK\x07\x08 + */ +const DATA_DESCRIPTOR_SIGNATURE: number = 0x08074b50; // PK\x07\x08 + +export const STORE_COMPRESSION: 0 = 0; +export const DEFLATE_COMPRESSION: 8 = 8; +export const ZSTD_COMPRESSION: 93 = 93; +export type ZipMetaCompressionMethod = + | typeof STORE_COMPRESSION + | typeof DEFLATE_COMPRESSION + | typeof ZSTD_COMPRESSION; + +export interface IFileEntry { + filename: string; + size: number; + compressedSize: number; + crc32: number; + sha1Hash: string; + localHeaderOffset: number; + compressionMethod: ZipMetaCompressionMethod; + dosDateTime: { time: number; date: number }; +} + +export interface ILocalFileHeader { + signature: number; + versionNeeded: number; + flags: number; + compressionMethod: number; + lastModTime: number; + lastModDate: number; + crc32: number; + compressedSize: number; + uncompressedSize: number; + filenameLength: number; + extraFieldLength: number; +} + +export interface ICentralDirectoryHeader { + signature: number; + versionMadeBy: number; + versionNeeded: number; + flags: number; + compressionMethod: number; + lastModTime: number; + lastModDate: number; + crc32: number; + compressedSize: number; + uncompressedSize: number; + filenameLength: number; + extraFieldLength: number; + commentLength: number; + diskNumberStart: number; + internalFileAttributes: number; + externalFileAttributes: number; + localHeaderOffset: number; +} + +export interface IEndOfCentralDirectory { + signature: number; + diskNumber: number; + centralDirStartDisk: number; + centralDirRecordsOnDisk: number; + totalCentralDirRecords: number; + centralDirSize: number; + centralDirOffset: number; + commentLength: number; +} + +function writeUInt32LE(buffer: Buffer, value: number, offset: number): void { + buffer.writeUInt32LE(value, offset); +} + +function writeUInt16LE(buffer: Buffer, value: number, offset: number): void { + buffer.writeUInt16LE(value, offset); +} + +function readUInt32LE(buffer: Buffer, offset: number): number { + return buffer.readUInt32LE(offset); +} + +function readUInt16LE(buffer: Buffer, offset: number): number { + return buffer.readUInt16LE(offset); +} + +/** + * Convert a JS Date into packed DOS time/date fields used by classic ZIP. + * Seconds are stored /2 (range 0-29 => 0-58s). Years are offset from 1980. + */ +export function dosDateTime(date: Date): { time: number; date: number } { + /* eslint-disable no-bitwise */ + const time: number = + ((date.getHours() & 0x1f) << 11) | ((date.getMinutes() & 0x3f) << 5) | ((date.getSeconds() / 2) & 0x1f); + + const dateVal: number = + (((date.getFullYear() - 1980) & 0x7f) << 9) | + (((date.getMonth() + 1) & 0xf) << 5) | + (date.getDate() & 0x1f); + /* eslint-enable no-bitwise */ + + return { time, date: dateVal }; +} + +/** + * Reusable scratch buffer for the fixed-length local file header (30 bytes). + * Using a single Buffer avoids per-file allocations; callers must copy/use synchronously. + */ +const localFileHeaderBuffer: Buffer = Buffer.allocUnsafe(30); +/** + * Write the fixed portion of a local file header for an entry (with data descriptor flag set) and + * return the header buffer plus the variable-length filename buffer. + * + * Layout (little-endian): + * signature(4) versionNeeded(2) flags(2) method(2) modTime(2) modDate(2) + * crc32(4) compSize(4) uncompSize(4) nameLen(2) extraLen(2) + * + * Because we set bit 3 of the general purpose flag, crc32/compSize/uncompSize are zero here and the + * actual values appear later in a trailing data descriptor record. This enables streaming without + * buffering entire file contents beforehand. + */ +export function writeLocalFileHeader( + entry: IFileEntry +): [fileHeaderWithoutVariableLengthData: Buffer, fileHeaderVariableLengthData: Buffer] { + const filenameBuffer: Buffer = Buffer.from(entry.filename, 'utf8'); + + const { time, date } = entry.dosDateTime; + + let offset: number = 0; + writeUInt32LE(localFileHeaderBuffer, LOCAL_FILE_HEADER_SIGNATURE, offset); + offset += 4; + writeUInt16LE(localFileHeaderBuffer, 20, offset); // version needed + offset += 2; + // General purpose bit flag: set bit 3 (0x0008) to indicate presence of data descriptor + // Per APPNOTE: when bit 3 is set, CRC-32 and sizes in local header are set to zero and + // the actual values are stored in the data descriptor that follows the file data. + writeUInt16LE(localFileHeaderBuffer, 0x0008, offset); // flags (data descriptor) + offset += 2; + writeUInt16LE(localFileHeaderBuffer, entry.compressionMethod, offset); // compression method (0=store,8=deflate) + offset += 2; + writeUInt16LE(localFileHeaderBuffer, time, offset); // last mod time + offset += 2; + writeUInt16LE(localFileHeaderBuffer, date, offset); // last mod date + offset += 2; + // With bit 3 set, these three fields MUST be zero in the local header + writeUInt32LE(localFileHeaderBuffer, 0, offset); // crc32 (placeholder, real value in data descriptor) + offset += 4; + writeUInt32LE(localFileHeaderBuffer, 0, offset); // compressed size (placeholder) + offset += 4; + writeUInt32LE(localFileHeaderBuffer, 0, offset); // uncompressed size (placeholder) + offset += 4; + writeUInt16LE(localFileHeaderBuffer, filenameBuffer.length, offset); // filename length + offset += 2; + writeUInt16LE(localFileHeaderBuffer, 0, offset); // extra field length + offset += 2; + + return [localFileHeaderBuffer, filenameBuffer]; +} + +/** + * Reusable scratch buffer for central directory entries (fixed-length 46 bytes before filename) + */ +const centralDirHeaderBuffer: Buffer = Buffer.allocUnsafe(46); +/** + * Write a central directory header referencing an already written local file entry. + * Central directory consolidates the final CRC + sizes (always present here) and provides a table + * for fast enumeration without scanning the archive sequentially. + */ +export function writeCentralDirectoryHeader(entry: IFileEntry): Buffer[] { + const filenameBuffer: Buffer = Buffer.from(entry.filename, 'utf8'); + + const now: Date = new Date(); + const { time, date } = dosDateTime(now); + + let offset: number = 0; + writeUInt32LE(centralDirHeaderBuffer, CENTRAL_DIR_HEADER_SIGNATURE, offset); + offset += 4; + writeUInt16LE(centralDirHeaderBuffer, 20, offset); // version made by + offset += 2; + writeUInt16LE(centralDirHeaderBuffer, 20, offset); // version needed + offset += 2; + // Mirror flags used in local header (bit 3 set to indicate data descriptor was used) + writeUInt16LE(centralDirHeaderBuffer, 0x0008, offset); // flags + offset += 2; + writeUInt16LE(centralDirHeaderBuffer, entry.compressionMethod, offset); // compression method + offset += 2; + writeUInt16LE(centralDirHeaderBuffer, time, offset); // last mod time + offset += 2; + writeUInt16LE(centralDirHeaderBuffer, date, offset); // last mod date + offset += 2; + writeUInt32LE(centralDirHeaderBuffer, entry.crc32, offset); // crc32 + offset += 4; + writeUInt32LE(centralDirHeaderBuffer, entry.compressedSize, offset); // compressed size + offset += 4; + writeUInt32LE(centralDirHeaderBuffer, entry.size, offset); // uncompressed size + offset += 4; + writeUInt16LE(centralDirHeaderBuffer, filenameBuffer.length, offset); // filename length + offset += 2; + writeUInt16LE(centralDirHeaderBuffer, 0, offset); // extra field length + offset += 2; + writeUInt16LE(centralDirHeaderBuffer, 0, offset); // comment length + offset += 2; + writeUInt16LE(centralDirHeaderBuffer, 0, offset); // disk number start + offset += 2; + writeUInt16LE(centralDirHeaderBuffer, 0, offset); // internal file attributes + offset += 2; + writeUInt32LE(centralDirHeaderBuffer, 0, offset); // external file attributes + offset += 4; + writeUInt32LE(centralDirHeaderBuffer, entry.localHeaderOffset, offset); // local header offset + offset += 4; + + return [centralDirHeaderBuffer, filenameBuffer]; +} + +/** + * Data descriptor: signature(4) crc32(4) compSize(4) uncompSize(4) + */ +const dataDescriptorBuffer: Buffer = Buffer.allocUnsafe(16); +/** + * Write the trailing data descriptor for an entry. Only used because we set flag bit 3 in the + * local file header allowing deferred CRC/size calculation. + */ +export function writeDataDescriptor(entry: IFileEntry): Buffer { + let offset: number = 0; + writeUInt32LE(dataDescriptorBuffer, DATA_DESCRIPTOR_SIGNATURE, offset); // signature PK\x07\x08 + offset += 4; + writeUInt32LE(dataDescriptorBuffer, entry.crc32, offset); // crc32 + offset += 4; + writeUInt32LE(dataDescriptorBuffer, entry.compressedSize, offset); // compressed size + offset += 4; + writeUInt32LE(dataDescriptorBuffer, entry.size, offset); // uncompressed size + return dataDescriptorBuffer; +} + +/** + * End of central directory (EOCD) record (22 bytes when comment length = 0) + */ +const endOfCentralDirBuffer: Buffer = Buffer.allocUnsafe(22); +/** + * Write the EOCD record referencing the accumulated central directory. We omit archive comments + * and do not support ZIP64 (sufficient for build cache archive sizes today). + */ +export function writeEndOfCentralDirectory( + centralDirOffset: number, + centralDirSize: number, + entryCount: number +): Buffer { + let offset: number = 0; + writeUInt32LE(endOfCentralDirBuffer, END_OF_CENTRAL_DIR_SIGNATURE, offset); + offset += 4; + writeUInt16LE(endOfCentralDirBuffer, 0, offset); // disk number + offset += 2; + writeUInt16LE(endOfCentralDirBuffer, 0, offset); // central dir start disk + offset += 2; + writeUInt16LE(endOfCentralDirBuffer, entryCount, offset); // central dir records on disk + offset += 2; + writeUInt16LE(endOfCentralDirBuffer, entryCount, offset); // total central dir records + offset += 2; + writeUInt32LE(endOfCentralDirBuffer, centralDirSize, offset); // central dir size + offset += 4; + writeUInt32LE(endOfCentralDirBuffer, centralDirOffset, offset); // central dir offset + offset += 4; + writeUInt16LE(endOfCentralDirBuffer, 0, offset); // comment length + + return endOfCentralDirBuffer; +} + +interface ILocalFileHeaderParseResult { + header: ILocalFileHeader; + nextOffset: number; +} + +/** + * Parse a local file header at the provided offset. Minimal validation: signature check only. + * Returns header plus the offset pointing just past the variable-length name+extra field. + */ +export function parseLocalFileHeader(buffer: Buffer, offset: number): ILocalFileHeaderParseResult { + const signature: number = readUInt32LE(buffer, offset); + if (signature !== LOCAL_FILE_HEADER_SIGNATURE) { + throw new Error( + `Unexpected local file header signature at offset ${offset.toString(16)}: ${signature.toString(16)}` + ); + } + const header: ILocalFileHeader = { + signature, + versionNeeded: readUInt16LE(buffer, offset + 4), + flags: readUInt16LE(buffer, offset + 6), + compressionMethod: readUInt16LE(buffer, offset + 8), + lastModTime: readUInt16LE(buffer, offset + 10), + lastModDate: readUInt16LE(buffer, offset + 12), + crc32: readUInt32LE(buffer, offset + 14), + compressedSize: readUInt32LE(buffer, offset + 18), + uncompressedSize: readUInt32LE(buffer, offset + 22), + filenameLength: readUInt16LE(buffer, offset + 26), + extraFieldLength: readUInt16LE(buffer, offset + 28) + }; + + return { + header, + nextOffset: offset + 30 + header.filenameLength + header.extraFieldLength + }; +} + +export interface ICentralDirectoryHeaderParseResult { + header: ICentralDirectoryHeader; + filename: string; + nextOffset: number; +} + +/** + * Parse a central directory header at the given offset (within a sliced central directory buffer). + * Returns header, filename string, and nextOffset pointing to the next structure. + */ +export function parseCentralDirectoryHeader( + buffer: Buffer, + offset: number +): ICentralDirectoryHeaderParseResult { + const signature: number = readUInt32LE(buffer, offset); + if (signature !== CENTRAL_DIR_HEADER_SIGNATURE) { + throw new Error( + `Unexpected central directory signature at offset ${offset.toString(16)}: ${signature.toString(16)}` + ); + } + const header: ICentralDirectoryHeader = { + signature, + versionMadeBy: readUInt16LE(buffer, offset + 4), + versionNeeded: readUInt16LE(buffer, offset + 6), + flags: readUInt16LE(buffer, offset + 8), + compressionMethod: readUInt16LE(buffer, offset + 10), + lastModTime: readUInt16LE(buffer, offset + 12), + lastModDate: readUInt16LE(buffer, offset + 14), + crc32: readUInt32LE(buffer, offset + 16), + compressedSize: readUInt32LE(buffer, offset + 20), + uncompressedSize: readUInt32LE(buffer, offset + 24), + filenameLength: readUInt16LE(buffer, offset + 28), + extraFieldLength: readUInt16LE(buffer, offset + 30), + commentLength: readUInt16LE(buffer, offset + 32), + diskNumberStart: readUInt16LE(buffer, offset + 34), + internalFileAttributes: readUInt16LE(buffer, offset + 36), + externalFileAttributes: readUInt32LE(buffer, offset + 38), + localHeaderOffset: readUInt32LE(buffer, offset + 42) + }; + + offset += 46; + + const filename: string = buffer.toString('utf8', offset, offset + header.filenameLength); + + return { + header, + filename, + nextOffset: offset + header.filenameLength + header.extraFieldLength + header.commentLength + }; +} + +/** + * Locate the EOCD record by reverse scanning. Since we never write a comment the EOCD will be the + * first matching signature encountered scanning backwards from the end. + */ +export function findEndOfCentralDirectory(buffer: Buffer): IEndOfCentralDirectory { + for (let i: number = buffer.length - 22; i >= 0; i--) { + if (readUInt32LE(buffer, i) === END_OF_CENTRAL_DIR_SIGNATURE) { + return { + signature: readUInt32LE(buffer, i), + diskNumber: readUInt16LE(buffer, i + 4), + centralDirStartDisk: readUInt16LE(buffer, i + 6), + centralDirRecordsOnDisk: readUInt16LE(buffer, i + 8), + totalCentralDirRecords: readUInt16LE(buffer, i + 10), + centralDirSize: readUInt32LE(buffer, i + 12), + centralDirOffset: readUInt32LE(buffer, i + 16), + commentLength: readUInt16LE(buffer, i + 20) + }; + } + } + + throw new Error('End of central directory not found'); +} + +/** + * Slice out the (possibly compressed) file data bytes for a central directory entry. + * Caller will decompress if needed based on entry.header.compressionMethod. + */ +export function getFileFromZip(zipBuffer: Buffer, entry: ICentralDirectoryHeaderParseResult): Buffer { + const { header: localFileHeader } = parseLocalFileHeader(zipBuffer, entry.header.localHeaderOffset); + const localDataOffset: number = + entry.header.localHeaderOffset + 30 + localFileHeader.filenameLength + localFileHeader.extraFieldLength; + const fileZipBuffer: Buffer = zipBuffer.subarray( + localDataOffset, + localDataOffset + entry.header.compressedSize + ); + return fileZipBuffer; +} diff --git a/apps/zipsync/tsconfig.json b/apps/zipsync/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/apps/zipsync/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/build-tests-samples/heft-node-basic-tutorial/.eslintrc.js b/build-tests-samples/heft-node-basic-tutorial/.eslintrc.js deleted file mode 100644 index 60160b354c4..00000000000 --- a/build-tests-samples/heft-node-basic-tutorial/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/node'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests-samples/heft-node-basic-tutorial/config/heft.json b/build-tests-samples/heft-node-basic-tutorial/config/heft.json index fc8cb12b842..c1211a3c86c 100644 --- a/build-tests-samples/heft-node-basic-tutorial/config/heft.json +++ b/build-tests-samples/heft-node-basic-tutorial/config/heft.json @@ -2,12 +2,13 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }], + "cleanFiles": [{ "includeGlobs": ["dist", "lib"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests-samples/heft-node-basic-tutorial/config/jest.config.json b/build-tests-samples/heft-node-basic-tutorial/config/jest.config.json index b6f305ec886..1a044a08397 100644 --- a/build-tests-samples/heft-node-basic-tutorial/config/jest.config.json +++ b/build-tests-samples/heft-node-basic-tutorial/config/jest.config.json @@ -1,3 +1,16 @@ { - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json" + "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", + + "roots": ["/lib-commonjs"], + "testMatch": ["/lib-commonjs/**/*.test.js"], + + // These additional properties exist for caching purposes in the rushstack repo + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests-samples/heft-node-basic-tutorial/config/rush-project.json b/build-tests-samples/heft-node-basic-tutorial/config/rush-project.json index 247dc17187a..21fa5b8e2ea 100644 --- a/build-tests-samples/heft-node-basic-tutorial/config/rush-project.json +++ b/build-tests-samples/heft-node-basic-tutorial/config/rush-project.json @@ -1,8 +1,15 @@ +// This file exists for caching purposes in the rushstack repo { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + "outputFolderNames": [".heft", "lib-commonjs", "dist"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests-samples/heft-node-basic-tutorial/eslint.config.js b/build-tests-samples/heft-node-basic-tutorial/eslint.config.js new file mode 100644 index 00000000000..3d80b5cc649 --- /dev/null +++ b/build-tests-samples/heft-node-basic-tutorial/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-eslint-config/flat/profile/node'); + +module.exports = [ + ...nodeProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-samples/heft-node-basic-tutorial/package.json b/build-tests-samples/heft-node-basic-tutorial/package.json index ecd031b6e5f..d2f8675a942 100644 --- a/build-tests-samples/heft-node-basic-tutorial/package.json +++ b/build-tests-samples/heft-node-basic-tutorial/package.json @@ -11,14 +11,14 @@ "_phase:test": "heft run --only test -- --clean" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "eslint": "~8.7.0", - "typescript": "~5.0.4" + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*", + "typescript": "~5.8.2" } } diff --git a/build-tests-samples/heft-node-basic-tutorial/src/index.ts b/build-tests-samples/heft-node-basic-tutorial/src/index.ts index 15a2bae17e3..659610ef84f 100644 --- a/build-tests-samples/heft-node-basic-tutorial/src/index.ts +++ b/build-tests-samples/heft-node-basic-tutorial/src/index.ts @@ -4,4 +4,4 @@ /** * @public */ -export class TestClass {} // tslint:disable-line:export-name +export class TestClass {} diff --git a/build-tests-samples/heft-node-basic-tutorial/src/test/__snapshots__/ExampleTest.test.ts.snap b/build-tests-samples/heft-node-basic-tutorial/src/test/__snapshots__/ExampleTest.test.ts.snap index 1ca0d3b526a..8fdcc5d1121 100644 --- a/build-tests-samples/heft-node-basic-tutorial/src/test/__snapshots__/ExampleTest.test.ts.snap +++ b/build-tests-samples/heft-node-basic-tutorial/src/test/__snapshots__/ExampleTest.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`Example Test Correctly handles snapshots 1`] = ` Object { diff --git a/build-tests-samples/heft-node-basic-tutorial/tsconfig.json b/build-tests-samples/heft-node-basic-tutorial/tsconfig.json index 36f4f4267dc..46d511fd04f 100644 --- a/build-tests-samples/heft-node-basic-tutorial/tsconfig.json +++ b/build-tests-samples/heft-node-basic-tutorial/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -18,12 +18,11 @@ "noEmitOnError": false, "allowUnreachableCode": false, - "types": ["heft-jest", "node"], + "types": ["jest", "node"], "module": "commonjs", "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests-samples/heft-node-jest-tutorial/.eslintrc.js b/build-tests-samples/heft-node-jest-tutorial/.eslintrc.js deleted file mode 100644 index 60160b354c4..00000000000 --- a/build-tests-samples/heft-node-jest-tutorial/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/node'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests-samples/heft-node-jest-tutorial/config/heft.json b/build-tests-samples/heft-node-jest-tutorial/config/heft.json index a3a9d3fa0f7..304ccc96c03 100644 --- a/build-tests-samples/heft-node-jest-tutorial/config/heft.json +++ b/build-tests-samples/heft-node-jest-tutorial/config/heft.json @@ -1,10 +1,11 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }], + "cleanFiles": [{ "includeGlobs": ["dist", "lib"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests-samples/heft-node-jest-tutorial/config/jest.config.json b/build-tests-samples/heft-node-jest-tutorial/config/jest.config.json index 3c7379f4c5a..888a4ea4f6d 100644 --- a/build-tests-samples/heft-node-jest-tutorial/config/jest.config.json +++ b/build-tests-samples/heft-node-jest-tutorial/config/jest.config.json @@ -1,6 +1,9 @@ { "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", - "collectCoverage": true, + + "roots": ["/lib-commonjs"], + "testMatch": ["/lib-commonjs/**/*.test.js"], + "coverageThreshold": { "global": { "branches": 50, @@ -8,5 +11,15 @@ "lines": 50, "statements": 50 } - } + }, + + // These additional properties exist for caching purposes in the rushstack repo + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests-samples/heft-node-jest-tutorial/config/rush-project.json b/build-tests-samples/heft-node-jest-tutorial/config/rush-project.json index 247dc17187a..21fa5b8e2ea 100644 --- a/build-tests-samples/heft-node-jest-tutorial/config/rush-project.json +++ b/build-tests-samples/heft-node-jest-tutorial/config/rush-project.json @@ -1,8 +1,15 @@ +// This file exists for caching purposes in the rushstack repo { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + "outputFolderNames": [".heft", "lib-commonjs", "dist"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests-samples/heft-node-jest-tutorial/eslint.config.js b/build-tests-samples/heft-node-jest-tutorial/eslint.config.js new file mode 100644 index 00000000000..3d80b5cc649 --- /dev/null +++ b/build-tests-samples/heft-node-jest-tutorial/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-eslint-config/flat/profile/node'); + +module.exports = [ + ...nodeProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-samples/heft-node-jest-tutorial/package.json b/build-tests-samples/heft-node-jest-tutorial/package.json index 9cc2f5704f5..044a914768d 100644 --- a/build-tests-samples/heft-node-jest-tutorial/package.json +++ b/build-tests-samples/heft-node-jest-tutorial/package.json @@ -10,14 +10,14 @@ "_phase:test": "heft run --only test -- --clean" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "eslint": "~8.7.0", - "typescript": "~5.0.4" + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*", + "typescript": "~5.8.2" } } diff --git a/build-tests-samples/heft-node-jest-tutorial/src/guide/01-automatic-mock.test.ts b/build-tests-samples/heft-node-jest-tutorial/src/guide/01-automatic-mock.test.ts index ae802e168c1..66cbc4c33cf 100644 --- a/build-tests-samples/heft-node-jest-tutorial/src/guide/01-automatic-mock.test.ts +++ b/build-tests-samples/heft-node-jest-tutorial/src/guide/01-automatic-mock.test.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + // This example is adapted from the Jest guide here: // https://jestjs.io/docs/en/es6-class-mocks#automatic-mock @@ -8,7 +11,7 @@ import { SoundPlayerConsumer } from './SoundPlayerConsumer'; beforeEach(() => { // Clear all instances and calls to constructor and all methods: - mocked(SoundPlayer).mockClear(); + jest.mocked(SoundPlayer).mockClear(); }); it('We can check if the consumer called the class constructor', () => { @@ -28,9 +31,9 @@ it('We can check if the consumer called a method on the class instance', () => { soundPlayerConsumer.playSomethingCool(); // mock.instances is available with automatic mocks: - const mockSoundPlayerInstance: SoundPlayer = mocked(SoundPlayer).mock.instances[0]; + const mockSoundPlayerInstance: SoundPlayer = jest.mocked(SoundPlayer).mock.instances[0]; - const mockPlaySoundFile = mocked(mockSoundPlayerInstance.playSoundFile); + const mockPlaySoundFile = jest.mocked(mockSoundPlayerInstance.playSoundFile); expect(mockPlaySoundFile.mock.calls[0][0]).toEqual(coolSoundFileName); // Equivalent to above check: diff --git a/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/SoundPlayer.ts b/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/SoundPlayer.ts index 39a0dfbde06..e1459e70e6e 100644 --- a/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/SoundPlayer.ts +++ b/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/SoundPlayer.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + // This example is adapted from the Jest guide here: // https://jestjs.io/docs/en/es6-class-mocks @@ -9,7 +12,9 @@ export class SoundPlayer { } public playSoundFile(fileName: string): void { + // eslint-disable-next-line no-console console.log('Playing sound file ' + fileName); + // eslint-disable-next-line no-console console.log('Foo=' + this._foo); } } diff --git a/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/SoundPlayerConsumer.test.ts b/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/SoundPlayerConsumer.test.ts index 940da3c37b8..93a782faf56 100644 --- a/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/SoundPlayerConsumer.test.ts +++ b/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/SoundPlayerConsumer.test.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + // This example is adapted from the Jest guide here: // https://jestjs.io/docs/en/es6-class-mocks#manual-mock @@ -9,7 +12,7 @@ import { SoundPlayerConsumer } from './SoundPlayerConsumer'; beforeEach(() => { // Clear all instances and calls to constructor and all methods: - mocked(SoundPlayer).mockClear(); + jest.mocked(SoundPlayer).mockClear(); mockPlaySoundFile.mockClear(); }); diff --git a/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/SoundPlayerConsumer.ts b/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/SoundPlayerConsumer.ts index ffb7d2edd04..c860b9f816a 100644 --- a/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/SoundPlayerConsumer.ts +++ b/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/SoundPlayerConsumer.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + // This example is adapted from the Jest guide here: // https://jestjs.io/docs/en/es6-class-mocks diff --git a/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/__mocks__/SoundPlayer.ts b/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/__mocks__/SoundPlayer.ts index 98cae5dbc4a..0541a13992a 100644 --- a/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/__mocks__/SoundPlayer.ts +++ b/build-tests-samples/heft-node-jest-tutorial/src/guide/02-manual-mock/__mocks__/SoundPlayer.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + // Import this named export into your test file: export const mockPlaySoundFile = jest.fn(); diff --git a/build-tests-samples/heft-node-jest-tutorial/src/guide/03-factory-constructor-mock.test.ts b/build-tests-samples/heft-node-jest-tutorial/src/guide/03-factory-constructor-mock.test.ts index 59fc4e0081f..1309c70e898 100644 --- a/build-tests-samples/heft-node-jest-tutorial/src/guide/03-factory-constructor-mock.test.ts +++ b/build-tests-samples/heft-node-jest-tutorial/src/guide/03-factory-constructor-mock.test.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + // This example is adapted from the Jest guide here: // https://jestjs.io/docs/en/es6-class-mocks#complete-example @@ -15,7 +18,7 @@ import { SoundPlayerConsumer } from './SoundPlayerConsumer'; import { SoundPlayer } from './SoundPlayer'; beforeEach(() => { - mocked(SoundPlayer).mockClear(); + jest.mocked(SoundPlayer).mockClear(); mockPlaySoundFile.mockClear(); }); diff --git a/build-tests-samples/heft-node-jest-tutorial/src/guide/SoundPlayer.ts b/build-tests-samples/heft-node-jest-tutorial/src/guide/SoundPlayer.ts index 39a0dfbde06..e1459e70e6e 100644 --- a/build-tests-samples/heft-node-jest-tutorial/src/guide/SoundPlayer.ts +++ b/build-tests-samples/heft-node-jest-tutorial/src/guide/SoundPlayer.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + // This example is adapted from the Jest guide here: // https://jestjs.io/docs/en/es6-class-mocks @@ -9,7 +12,9 @@ export class SoundPlayer { } public playSoundFile(fileName: string): void { + // eslint-disable-next-line no-console console.log('Playing sound file ' + fileName); + // eslint-disable-next-line no-console console.log('Foo=' + this._foo); } } diff --git a/build-tests-samples/heft-node-jest-tutorial/src/guide/SoundPlayerConsumer.ts b/build-tests-samples/heft-node-jest-tutorial/src/guide/SoundPlayerConsumer.ts index ffb7d2edd04..c860b9f816a 100644 --- a/build-tests-samples/heft-node-jest-tutorial/src/guide/SoundPlayerConsumer.ts +++ b/build-tests-samples/heft-node-jest-tutorial/src/guide/SoundPlayerConsumer.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + // This example is adapted from the Jest guide here: // https://jestjs.io/docs/en/es6-class-mocks diff --git a/build-tests-samples/heft-node-jest-tutorial/src/inlineSnapshot.test.ts b/build-tests-samples/heft-node-jest-tutorial/src/inlineSnapshot.test.ts index a844d9452d5..10b12fbf160 100644 --- a/build-tests-samples/heft-node-jest-tutorial/src/inlineSnapshot.test.ts +++ b/build-tests-samples/heft-node-jest-tutorial/src/inlineSnapshot.test.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + // This example is adapted from the Jest guide here: // https://jestjs.io/docs/en/es6-class-mocks#automatic-mock diff --git a/build-tests-samples/heft-node-jest-tutorial/tsconfig.json b/build-tests-samples/heft-node-jest-tutorial/tsconfig.json index 36f4f4267dc..46d511fd04f 100644 --- a/build-tests-samples/heft-node-jest-tutorial/tsconfig.json +++ b/build-tests-samples/heft-node-jest-tutorial/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -18,12 +18,11 @@ "noEmitOnError": false, "allowUnreachableCode": false, - "types": ["heft-jest", "node"], + "types": ["jest", "node"], "module": "commonjs", "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests-samples/heft-node-rig-tutorial/.eslintrc.js b/build-tests-samples/heft-node-rig-tutorial/.eslintrc.js deleted file mode 100644 index 60160b354c4..00000000000 --- a/build-tests-samples/heft-node-rig-tutorial/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/node'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests-samples/heft-node-rig-tutorial/config/jest.config.json b/build-tests-samples/heft-node-rig-tutorial/config/jest.config.json index 4bb17bde3ee..c1f53a85731 100644 --- a/build-tests-samples/heft-node-rig-tutorial/config/jest.config.json +++ b/build-tests-samples/heft-node-rig-tutorial/config/jest.config.json @@ -1,3 +1,13 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json", + + // These additional properties exist for caching purposes in the rushstack repo + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests-samples/heft-node-rig-tutorial/config/rush-project.json b/build-tests-samples/heft-node-rig-tutorial/config/rush-project.json new file mode 100644 index 00000000000..417e302ddcc --- /dev/null +++ b/build-tests-samples/heft-node-rig-tutorial/config/rush-project.json @@ -0,0 +1,15 @@ +// This file exists for caching purposes in the rushstack repo +{ + "extends": "@rushstack/heft-node-rig/profiles/default/config/rush-project.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": [".heft", "release"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] + } + ] +} diff --git a/build-tests-samples/heft-node-rig-tutorial/eslint.config.js b/build-tests-samples/heft-node-rig-tutorial/eslint.config.js new file mode 100644 index 00000000000..3d80b5cc649 --- /dev/null +++ b/build-tests-samples/heft-node-rig-tutorial/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-eslint-config/flat/profile/node'); + +module.exports = [ + ...nodeProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-samples/heft-node-rig-tutorial/package.json b/build-tests-samples/heft-node-rig-tutorial/package.json index 9fcf3ea48bd..84412af9589 100644 --- a/build-tests-samples/heft-node-rig-tutorial/package.json +++ b/build-tests-samples/heft-node-rig-tutorial/package.json @@ -11,10 +11,11 @@ "_phase:test": "heft run --only test -- --clean" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", + "local-eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/heft-node-rig": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36" + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "eslint": "~9.37.0" } } diff --git a/build-tests-samples/heft-node-rig-tutorial/src/index.ts b/build-tests-samples/heft-node-rig-tutorial/src/index.ts index 15a2bae17e3..659610ef84f 100644 --- a/build-tests-samples/heft-node-rig-tutorial/src/index.ts +++ b/build-tests-samples/heft-node-rig-tutorial/src/index.ts @@ -4,4 +4,4 @@ /** * @public */ -export class TestClass {} // tslint:disable-line:export-name +export class TestClass {} diff --git a/build-tests-samples/heft-node-rig-tutorial/src/test/__snapshots__/ExampleTest.test.ts.snap b/build-tests-samples/heft-node-rig-tutorial/src/test/__snapshots__/ExampleTest.test.ts.snap index 1ca0d3b526a..8fdcc5d1121 100644 --- a/build-tests-samples/heft-node-rig-tutorial/src/test/__snapshots__/ExampleTest.test.ts.snap +++ b/build-tests-samples/heft-node-rig-tutorial/src/test/__snapshots__/ExampleTest.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`Example Test Correctly handles snapshots 1`] = ` Object { diff --git a/build-tests-samples/heft-node-rig-tutorial/tsconfig.json b/build-tests-samples/heft-node-rig-tutorial/tsconfig.json index 22f94ca28b5..af91dc9d42b 100644 --- a/build-tests-samples/heft-node-rig-tutorial/tsconfig.json +++ b/build-tests-samples/heft-node-rig-tutorial/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "types": ["heft-jest", "node"] + "isolatedModules": true, + "types": ["jest", "node"] } } diff --git a/build-tests-samples/heft-serverless-stack-tutorial/.eslintrc.js b/build-tests-samples/heft-serverless-stack-tutorial/.eslintrc.js deleted file mode 100644 index 60160b354c4..00000000000 --- a/build-tests-samples/heft-serverless-stack-tutorial/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/node'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests-samples/heft-serverless-stack-tutorial/config/heft.json b/build-tests-samples/heft-serverless-stack-tutorial/config/heft.json index 400e71407f8..ab858871b9f 100644 --- a/build-tests-samples/heft-serverless-stack-tutorial/config/heft.json +++ b/build-tests-samples/heft-serverless-stack-tutorial/config/heft.json @@ -2,12 +2,13 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }, { "sourcePath": ".build" }], + "cleanFiles": [{ "includeGlobs": ["dist", "lib", ".build"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests-samples/heft-serverless-stack-tutorial/config/jest.config.json b/build-tests-samples/heft-serverless-stack-tutorial/config/jest.config.json index b6f305ec886..bfc5ce0d9b7 100644 --- a/build-tests-samples/heft-serverless-stack-tutorial/config/jest.config.json +++ b/build-tests-samples/heft-serverless-stack-tutorial/config/jest.config.json @@ -1,3 +1,26 @@ { - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json" + "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", + + "roots": ["/lib-commonjs"], + + "testMatch": ["/lib-commonjs/**/*.test.js"], + "collectCoverageFrom": [ + "lib-commonjs/**/*.js", + "!lib-commonjs/**/*.d.ts", + "!lib-commonjs/**/*.test.js", + "!lib-commonjs/**/test/**", + "!lib-commonjs/**/__tests__/**", + "!lib-commonjs/**/__fixtures__/**", + "!lib-commonjs/**/__mocks__/**" + ], + + // These additional properties exist for caching purposes in the rushstack repo + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests-samples/heft-serverless-stack-tutorial/config/rush-project.json b/build-tests-samples/heft-serverless-stack-tutorial/config/rush-project.json index 247dc17187a..21fa5b8e2ea 100644 --- a/build-tests-samples/heft-serverless-stack-tutorial/config/rush-project.json +++ b/build-tests-samples/heft-serverless-stack-tutorial/config/rush-project.json @@ -1,8 +1,15 @@ +// This file exists for caching purposes in the rushstack repo { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + "outputFolderNames": [".heft", "lib-commonjs", "dist"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests-samples/heft-serverless-stack-tutorial/eslint.config.js b/build-tests-samples/heft-serverless-stack-tutorial/eslint.config.js new file mode 100644 index 00000000000..3d80b5cc649 --- /dev/null +++ b/build-tests-samples/heft-serverless-stack-tutorial/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-eslint-config/flat/profile/node'); + +module.exports = [ + ...nodeProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-samples/heft-serverless-stack-tutorial/package.json b/build-tests-samples/heft-serverless-stack-tutorial/package.json index 7f64c7291b8..0eb38492889 100644 --- a/build-tests-samples/heft-serverless-stack-tutorial/package.json +++ b/build-tests-samples/heft-serverless-stack-tutorial/package.json @@ -13,21 +13,23 @@ "_phase:test": "heft run --only test -- --clean" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", + "@aws-sdk/client-sso-oidc": "^3.567.0", + "@aws-sdk/client-sts": "^3.567.0", + "@rushstack/heft": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-serverless-stack-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", - "@rushstack/heft": "workspace:*", "@serverless-stack/aws-lambda-ric": "^2.0.12", - "@serverless-stack/cli": "0.67.0", - "@serverless-stack/resources": "0.67.0", + "@serverless-stack/cli": "1.18.4", + "@serverless-stack/resources": "1.18.4", "@types/aws-lambda": "8.10.93", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "aws-cdk-lib": "2.7.0", + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "aws-cdk-lib": "2.189.1", "constructs": "~10.0.98", - "eslint": "~8.7.0", - "typescript": "~5.0.4" + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*", + "typescript": "~5.8.2" } } diff --git a/build-tests-samples/heft-serverless-stack-tutorial/src/lambda.ts b/build-tests-samples/heft-serverless-stack-tutorial/src/lambda.ts index e9cbe252501..fa6a2bda2b4 100644 --- a/build-tests-samples/heft-serverless-stack-tutorial/src/lambda.ts +++ b/build-tests-samples/heft-serverless-stack-tutorial/src/lambda.ts @@ -1,4 +1,7 @@ -import { APIGatewayProxyHandlerV2 } from 'aws-lambda'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { APIGatewayProxyHandlerV2 } from 'aws-lambda'; export const handler: APIGatewayProxyHandlerV2 = async (event) => { return { diff --git a/build-tests-samples/heft-serverless-stack-tutorial/src/stacks/MyStack.ts b/build-tests-samples/heft-serverless-stack-tutorial/src/stacks/MyStack.ts index a659619c241..93b89b02ba9 100644 --- a/build-tests-samples/heft-serverless-stack-tutorial/src/stacks/MyStack.ts +++ b/build-tests-samples/heft-serverless-stack-tutorial/src/stacks/MyStack.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import * as sst from '@serverless-stack/resources'; export default class MyStack extends sst.Stack { diff --git a/build-tests-samples/heft-serverless-stack-tutorial/src/stacks/index.ts b/build-tests-samples/heft-serverless-stack-tutorial/src/stacks/index.ts index cad28003a96..d8e9f7ecc71 100644 --- a/build-tests-samples/heft-serverless-stack-tutorial/src/stacks/index.ts +++ b/build-tests-samples/heft-serverless-stack-tutorial/src/stacks/index.ts @@ -1,5 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type * as sst from '@serverless-stack/resources'; + import MyStack from './MyStack'; -import * as sst from '@serverless-stack/resources'; export default function main(app: sst.App): void { // Set default runtime for all functions diff --git a/build-tests-samples/heft-serverless-stack-tutorial/src/test/MyStack.test.ts b/build-tests-samples/heft-serverless-stack-tutorial/src/test/MyStack.test.ts index 6bc9502bbe9..74785d4607a 100644 --- a/build-tests-samples/heft-serverless-stack-tutorial/src/test/MyStack.test.ts +++ b/build-tests-samples/heft-serverless-stack-tutorial/src/test/MyStack.test.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + // TODO: Jest tests should not be invoking ESBuild or making network calls! Reenable this once // we have a fix for https://github.com/serverless-stack/serverless-stack/issues/1537 diff --git a/build-tests-samples/heft-serverless-stack-tutorial/tsconfig.json b/build-tests-samples/heft-serverless-stack-tutorial/tsconfig.json index 4d8e982ab3f..3f9ca0d7d77 100644 --- a/build-tests-samples/heft-serverless-stack-tutorial/tsconfig.json +++ b/build-tests-samples/heft-serverless-stack-tutorial/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -18,12 +18,13 @@ "noEmitOnError": false, "allowUnreachableCode": false, - "types": ["heft-jest", "node"], + "skipLibCheck": true, // Some of the AWS dependencies have typings issues + + "types": ["jest", "node"], "module": "commonjs", "target": "es2017", "lib": ["es2017", "DOM"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests-samples/heft-storybook-react-tutorial-storykit/dist/index.cjs b/build-tests-samples/heft-storybook-react-tutorial-storykit/dist/index.cjs deleted file mode 100644 index 68de8a6a8f7..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial-storykit/dist/index.cjs +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -__exportStar(require("@storybook/react"), exports); diff --git a/build-tests-samples/heft-storybook-react-tutorial-storykit/dist/index.d.ts b/build-tests-samples/heft-storybook-react-tutorial-storykit/dist/index.d.ts deleted file mode 100644 index da28051ed33..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial-storykit/dist/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@storybook/react'; diff --git a/build-tests-samples/heft-storybook-react-tutorial-storykit/dist/index.js b/build-tests-samples/heft-storybook-react-tutorial-storykit/dist/index.js deleted file mode 100644 index 230b842e312..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial-storykit/dist/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from '@storybook/react'; \ No newline at end of file diff --git a/build-tests-samples/heft-storybook-react-tutorial-storykit/package.json b/build-tests-samples/heft-storybook-react-tutorial-storykit/package.json deleted file mode 100644 index 1b0124db004..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial-storykit/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "heft-storybook-react-tutorial-storykit", - "version": "0.0.0", - "private": true, - "description": "Storybook build dependencies for heft-storybook-react-tutorial", - "main": "dist/index.cjs", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "scripts": { - "build": "", - "_phase:build": "" - }, - "devDependencies": { - "@babel/core": "~7.20.0", - "@storybook/addon-actions": "~6.4.18", - "@storybook/addon-essentials": "~6.4.18", - "@storybook/addon-links": "~6.4.18", - "@storybook/cli": "~6.4.18", - "@storybook/components": "~6.4.18", - "@storybook/core-events": "~6.4.18", - "@storybook/react": "~6.4.18", - "@storybook/theming": "~6.4.18", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "@types/react-dom": "16.9.14", - "@types/react": "16.14.23", - "@types/webpack-env": "1.18.0", - "babel-loader": "~8.2.3", - "css-loader": "~5.2.7", - "jest": "~29.3.1", - "react-dom": "~16.13.1", - "react": "~16.13.1", - "style-loader": "~2.0.0", - "terser-webpack-plugin": "~3.0.8", - "typescript": "~5.0.4", - "webpack": "~4.44.2" - } -} diff --git a/build-tests-samples/heft-storybook-react-tutorial/.eslintrc.js b/build-tests-samples/heft-storybook-react-tutorial/.eslintrc.js deleted file mode 100644 index 288eaa16364..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/web-app', '@rushstack/eslint-config/mixins/react'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests-samples/heft-storybook-react-tutorial/.storybook/main.js b/build-tests-samples/heft-storybook-react-tutorial/.storybook/main.js deleted file mode 100644 index d4d78a64362..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/.storybook/main.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - stories: ['../lib/**/*.stories.js'], - addons: ['@storybook/addon-links', '@storybook/addon-essentials'] -}; diff --git a/build-tests-samples/heft-storybook-react-tutorial/.vscode/launch.json b/build-tests-samples/heft-storybook-react-tutorial/.vscode/launch.json deleted file mode 100644 index 32591233b77..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/.vscode/launch.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Debug \"heft start\"", - "program": "${workspaceFolder}/node_modules/@rushstack/heft/lib/start.js", - "cwd": "${workspaceFolder}", - "args": ["--debug", "start", "--storybook"], - "console": "integratedTerminal", - "sourceMaps": false - }, - ] -} \ No newline at end of file diff --git a/build-tests-samples/heft-storybook-react-tutorial/README.md b/build-tests-samples/heft-storybook-react-tutorial/README.md deleted file mode 100644 index c79d7763687..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# heft-webpack-basic-tutorial - -This is a copy of the -[heft-storybook-react-tutorial](https://github.com/microsoft/rushstack-samples/tree/main/heft/heft-storybook-react-tutorial) -tutorial project from the [rushstack-samples](https://github.com/microsoft/rushstack-samples) repo. - -The copy here serves as a regression test, by using `"workspace:*"` references to the local projects in this repo. -Please update the copy from time to time to keep it in sync with the official tutorial. diff --git a/build-tests-samples/heft-storybook-react-tutorial/assets/index.html b/build-tests-samples/heft-storybook-react-tutorial/assets/index.html deleted file mode 100644 index 3cfae745c19..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/assets/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - Example Application - - - -
- - diff --git a/build-tests-samples/heft-storybook-react-tutorial/config/heft.json b/build-tests-samples/heft-storybook-react-tutorial/config/heft.json deleted file mode 100644 index 5403ea91277..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/config/heft.json +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Defines configuration used by core Heft. - */ -{ - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", - - // TODO: Add comments - "phasesByName": { - "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }, { "sourcePath": "lib-commonjs" }], - "tasksByName": { - "typescript": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft-typescript-plugin" - } - }, - "lint": { - "taskDependencies": ["typescript"], - "taskPlugin": { - "pluginPackage": "@rushstack/heft-lint-plugin" - } - }, - "webpack": { - "taskDependencies": ["typescript"], - "taskPlugin": { - "pluginPackage": "@rushstack/heft-webpack4-plugin" - } - }, - "storybook": { - "taskDependencies": ["typescript"], - "taskPlugin": { - "pluginPackage": "@rushstack/heft-storybook-plugin", - "options": { - "storykitPackageName": "heft-storybook-react-tutorial-storykit", - "startupModulePath": "@storybook/react/bin/index.js", - "staticBuildModulePath": "@storybook/react/bin/build.js", - "staticBuildOutputFolder": "static-build-dir" - } - } - } - } - }, - - "test": { - "phaseDependencies": ["build"], - "tasksByName": { - "jest": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft-jest-plugin" - } - } - } - } - } -} diff --git a/build-tests-samples/heft-storybook-react-tutorial/config/jest.config.json b/build-tests-samples/heft-storybook-react-tutorial/config/jest.config.json deleted file mode 100644 index 21ac001f531..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/config/jest.config.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", - - "roots": ["/lib-commonjs"], - - "testMatch": ["/lib-commonjs/**/*.test.js"], - - "collectCoverageFrom": [ - "lib-commonjs/**/*.js", - "!lib-commonjs/**/*.d.ts", - "!lib-commonjs/**/*.test.js", - "!lib-commonjs/**/test/**", - "!lib-commonjs/**/__tests__/**", - "!lib-commonjs/**/__fixtures__/**", - "!lib-commonjs/**/__mocks__/**" - ] -} diff --git a/build-tests-samples/heft-storybook-react-tutorial/config/rush-project.json b/build-tests-samples/heft-storybook-react-tutorial/config/rush-project.json deleted file mode 100644 index 247dc17187a..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/config/rush-project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "operationSettings": [ - { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] - } - ] -} diff --git a/build-tests-samples/heft-storybook-react-tutorial/config/typescript.json b/build-tests-samples/heft-storybook-react-tutorial/config/typescript.json deleted file mode 100644 index 49f4b4370e3..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/config/typescript.json +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Configures the TypeScript plugin for Heft. This plugin also manages linting. - */ -{ - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", - - /** - * If provided, emit these module kinds in addition to the modules specified in the tsconfig. - * Note that this option only applies to the main tsconfig.json configuration. - */ - "additionalModuleKindsToEmit": [ - // { - // /** - // * (Required) Must be one of "commonjs", "amd", "umd", "system", "es2015", "esnext" - // */ - // "moduleKind": "amd", - // - // /** - // * (Required) The name of the folder where the output will be written. - // */ - // "outFolderName": "lib-amd" - // } - { - "moduleKind": "commonjs", - "outFolderName": "lib-commonjs" - } - ], - - /** - * Describes the way files should be statically coped from src to TS output folders - */ - "staticAssetsToCopy": { - /** - * File extensions that should be copied from the src folder to the destination folder(s). - */ - "fileExtensions": [".css"] - - /** - * Glob patterns that should be explicitly included. - */ - // "includeGlobs": [ - // "some/path/*.js" - // ], - - /** - * Glob patterns that should be explicitly excluded. This takes precedence over globs listed - * in "includeGlobs" and files that match the file extensions provided in "fileExtensions". - */ - // "excludeGlobs": [ - // "some/path/*.css" - // ] - } -} diff --git a/build-tests-samples/heft-storybook-react-tutorial/package.json b/build-tests-samples/heft-storybook-react-tutorial/package.json deleted file mode 100644 index 33a17c66694..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "heft-storybook-react-tutorial", - "description": "(Copy of sample project) Building this project is a regression test for Heft", - "version": "1.0.0", - "private": true, - "scripts": { - "build": "heft build --clean", - "start": "heft build-watch", - "storybook": "heft build-watch --storybook", - "build-storybook": "heft build --storybook", - "_phase:build": "heft run --only build -- --clean", - "_phase:test": "heft run --only test -- --clean" - }, - "dependencies": { - "react-dom": "~16.13.1", - "react": "~16.13.1", - "tslib": "~2.3.1" - }, - "devDependencies": { - "@babel/core": "~7.20.0", - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft-jest-plugin": "workspace:*", - "@rushstack/heft-lint-plugin": "workspace:*", - "@rushstack/heft-storybook-plugin": "workspace:*", - "@rushstack/heft-typescript-plugin": "workspace:*", - "@rushstack/heft-webpack4-plugin": "workspace:*", - "@rushstack/heft": "workspace:*", - "@storybook/react": "~6.4.18", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "@types/react-dom": "16.9.14", - "@types/react": "16.14.23", - "@types/webpack-env": "1.18.0", - "css-loader": "~5.2.7", - "eslint": "~8.7.0", - "heft-storybook-react-tutorial-storykit": "workspace:*", - "html-webpack-plugin": "~4.5.2", - "source-map-loader": "~1.1.3", - "style-loader": "~2.0.0", - "typescript": "~5.0.4", - "webpack": "~4.44.2" - } -} diff --git a/build-tests-samples/heft-storybook-react-tutorial/src/ExampleApp.tsx b/build-tests-samples/heft-storybook-react-tutorial/src/ExampleApp.tsx deleted file mode 100644 index 5e5447e2490..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/src/ExampleApp.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import * as React from 'react'; -import { ToggleSwitch, IToggleEventArgs } from './ToggleSwitch'; - -/** - * This React component renders the application page. - */ -export class ExampleApp extends React.Component { - public render(): React.ReactNode { - const appStyle: React.CSSProperties = { - backgroundColor: '#ffffff', - padding: '20px', - borderRadius: '5px', - width: '400px' - }; - - return ( -
-
-

Hello, world!

- Here is an example control: - -
-
- ); - } - - // React event handlers should be represented as fields instead of methods to ensure the "this" pointer - // is bound correctly. This form does not work with virtual/override inheritance, so use regular methods - // everywhere else. - private _onToggle = (sender: ToggleSwitch, args: IToggleEventArgs): void => { - console.log('Toggle switch changed: ' + args.sliderPosition); - }; -} diff --git a/build-tests-samples/heft-storybook-react-tutorial/src/ToggleSwitch.stories.tsx b/build-tests-samples/heft-storybook-react-tutorial/src/ToggleSwitch.stories.tsx deleted file mode 100644 index cef411d3a07..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/src/ToggleSwitch.stories.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as React from 'react'; - -import { ComponentStory, ComponentMeta } from 'heft-storybook-react-tutorial-storykit'; - -import { ToggleSwitch } from './ToggleSwitch'; - -export default { - title: 'Octogonz/ToggleSwitch', - component: ToggleSwitch, - argTypes: { - leftColor: { control: 'color' }, - rightColor: { control: 'color' } - } -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -// eslint-disable-next-line -export const Primary: any = Template.bind({}); -Primary.args = {}; - -// eslint-disable-next-line -export const Secondary: any = Template.bind({}); -Secondary.args = {}; diff --git a/build-tests-samples/heft-storybook-react-tutorial/src/ToggleSwitch.tsx b/build-tests-samples/heft-storybook-react-tutorial/src/ToggleSwitch.tsx deleted file mode 100644 index c4ac05fcde1..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/src/ToggleSwitch.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import * as React from 'react'; - -/** - * Slider positions for `ToggleSwitch`. - */ -export const enum ToggleSwitchPosition { - Left = 'left', - Right = 'right' -} - -/** - * Event arguments for `IToggleSwitchProps.onToggle`. - */ -export interface IToggleEventArgs { - sliderPosition: ToggleSwitchPosition; -} - -export interface IToggleSwitchProps { - /** - * The CSS color when the `ToggleSwitch` slider is in the left position. - * Example value: `"#800000"` - */ - leftColor: string; - - /** - * The CSS color when the `ToggleSwitch` slider is in the right position. - * Example value: `"#008000"` - */ - rightColor: string; - - /** - * An event that fires when the `ToggleSwitch` control is clicked. - */ - onToggle?: (sender: ToggleSwitch, args: IToggleEventArgs) => void; -} - -/** - * Private state for ToggleSwitch. - */ -interface IToggleSwitchState { - sliderPosition: ToggleSwitchPosition; -} - -/** - * An example component that renders a switch whose slider position can be "left" or "right". - */ -export class ToggleSwitch extends React.Component { - public constructor(props: IToggleSwitchProps) { - super(props); - this.state = { - sliderPosition: ToggleSwitchPosition.Left - }; - } - - public render(): React.ReactNode { - const frameStyle: React.CSSProperties = { - borderRadius: '10px', - backgroundColor: - this.state.sliderPosition === ToggleSwitchPosition.Left - ? this.props.leftColor - : this.props.rightColor, - width: '35px', - height: '20px', - cursor: 'pointer' - }; - const sliderStyle: React.CSSProperties = { - borderRadius: '10px', - backgroundColor: '#c0c0c0', - width: '20px', - height: '20px' - }; - - if (this.state.sliderPosition === ToggleSwitchPosition.Left) { - sliderStyle.marginLeft = '0px'; - sliderStyle.marginRight = 'auto'; - } else { - sliderStyle.marginLeft = 'auto'; - sliderStyle.marginRight = '0px'; - } - - return ( -
-
-
- ); - } - - // React event handlers should be represented as fields instead of methods to ensure the "this" pointer - // is bound correctly. This form does not work with virtual/override inheritance, so use regular methods - // everywhere else. - private _onClickSlider = (event: React.MouseEvent): void => { - if (this.state.sliderPosition === ToggleSwitchPosition.Left) { - this.setState({ sliderPosition: ToggleSwitchPosition.Right }); - } else { - this.setState({ sliderPosition: ToggleSwitchPosition.Left }); - } - - if (this.props.onToggle) { - this.props.onToggle(this, { sliderPosition: this.state.sliderPosition }); - } - }; -} diff --git a/build-tests-samples/heft-storybook-react-tutorial/src/index.tsx b/build-tests-samples/heft-storybook-react-tutorial/src/index.tsx deleted file mode 100644 index bfa20faf63e..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/src/index.tsx +++ /dev/null @@ -1,9 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import { ExampleApp } from './ExampleApp'; - -import './index.css'; - -const rootDiv: HTMLElement = document.getElementById('root') as HTMLElement; -ReactDOM.render(, rootDiv); diff --git a/build-tests-samples/heft-storybook-react-tutorial/src/test/ToggleSwitch.test.ts b/build-tests-samples/heft-storybook-react-tutorial/src/test/ToggleSwitch.test.ts deleted file mode 100644 index 53e11ba1742..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/src/test/ToggleSwitch.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ToggleSwitch } from '../ToggleSwitch'; - -describe('ToggleSwitch', () => { - it('can be tested', () => { - expect(ToggleSwitch).toBeDefined(); - }); -}); diff --git a/build-tests-samples/heft-storybook-react-tutorial/tsconfig.json b/build-tests-samples/heft-storybook-react-tutorial/tsconfig.json deleted file mode 100644 index 4d703032a26..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/tsconfig", - - "compilerOptions": { - "outDir": "lib", - "rootDir": "src", - - "forceConsistentCasingInFileNames": true, - "jsx": "react", - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strict": true, - "useUnknownInCatchVariables": false, - "esModuleInterop": true, - "noEmitOnError": false, - "allowUnreachableCode": false, - "importHelpers": true, - - "types": ["heft-jest", "webpack-env"], - - "module": "esnext", - "moduleResolution": "node", - "target": "es5", - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"] - }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] -} diff --git a/build-tests-samples/heft-storybook-react-tutorial/webpack.config.js b/build-tests-samples/heft-storybook-react-tutorial/webpack.config.js deleted file mode 100644 index c6a4c30ea7d..00000000000 --- a/build-tests-samples/heft-storybook-react-tutorial/webpack.config.js +++ /dev/null @@ -1,61 +0,0 @@ -'use strict'; - -const path = require('path'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); - -/** - * If the "--production" command-line parameter is specified when invoking Heft, then the - * "production" function parameter will be true. You can use this to enable bundling optimizations. - */ -function createWebpackConfig({ production }) { - const webpackConfig = { - // Documentation: https://webpack.js.org/configuration/mode/ - mode: production ? 'production' : 'development', - resolve: { - extensions: ['.js', '.jsx', '.json'] - }, - module: { - rules: [ - { - test: /\.css$/, - use: [require.resolve('style-loader'), require.resolve('css-loader')] - }, - { - test: /\.js$/, - enforce: 'pre', - use: ['source-map-loader'] - } - ] - }, - entry: { - app: path.join(__dirname, 'lib', 'index.js'), - - // Put these libraries in a separate vendor bundle - vendor: ['react', 'react-dom'] - }, - output: { - path: path.join(__dirname, 'dist'), - filename: '[name]_[contenthash].js' - }, - performance: { - // This specifies the bundle size limit that will trigger Webpack's warning saying: - // "The following entrypoint(s) combined asset size exceeds the recommended limit." - maxEntrypointSize: 250000, - maxAssetSize: 250000 - }, - devServer: { - port: 9000 - }, - devtool: production ? undefined : 'source-map', - plugins: [ - // See here for documentation: https://github.com/jantimon/html-webpack-plugin - new HtmlWebpackPlugin({ - template: 'assets/index.html' - }) - ] - }; - - return webpackConfig; -} - -module.exports = createWebpackConfig; diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial-app/README.md b/build-tests-samples/heft-storybook-v6-react-tutorial-app/README.md new file mode 100644 index 00000000000..816457634ac --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial-app/README.md @@ -0,0 +1,4 @@ +# heft-storybook-react-v6-tutorial-app + +This is project builds the storybook exports from the +[heft-storybook-v6-react-tutorial](https://github.com/microsoft/rushstack-samples/tree/main/heft/heft-storybook-v6-react-tutorial) and is a regression test for the heft-storybook-plugin `cwdPackageName` option. diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial-app/config/heft.json b/build-tests-samples/heft-storybook-v6-react-tutorial-app/config/heft.json new file mode 100644 index 00000000000..01ed5752bcb --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial-app/config/heft.json @@ -0,0 +1,25 @@ +/** + * Defines configuration used by core Heft. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "phasesByName": { + "build": { + "tasksByName": { + "storybook": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-storybook-plugin", + "options": { + "storykitPackageName": "heft-storybook-v6-react-tutorial-storykit", + "cliPackageName": "@storybook/react", + "cliCallingConvention": "storybook6", + "staticBuildOutputFolder": "dist", + "cwdPackageName": "heft-storybook-v6-react-tutorial" + } + } + } + } + } + } +} diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial-app/config/rush-project.json b/build-tests-samples/heft-storybook-v6-react-tutorial-app/config/rush-project.json new file mode 100644 index 00000000000..f3549d52dfd --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial-app/config/rush-project.json @@ -0,0 +1,11 @@ +// This file exists for caching purposes in the rushstack repo +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:lite-build", + "outputFolderNames": ["dist"] + } + ] +} diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial-app/package.json b/build-tests-samples/heft-storybook-v6-react-tutorial-app/package.json new file mode 100644 index 00000000000..8c2138b14fa --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial-app/package.json @@ -0,0 +1,19 @@ +{ + "name": "heft-storybook-v6-react-tutorial-app", + "description": "Building this project is a regression test for heft-storybook-plugin", + "version": "1.0.0", + "private": true, + "scripts": { + "build": "heft build --clean --storybook", + "_phase:lite-build": "heft run --only build -- --clean --storybook", + "_phase:test": "" + }, + "dependencies": { + "heft-storybook-v6-react-tutorial": "workspace: *" + }, + "devDependencies": { + "@rushstack/heft-storybook-plugin": "workspace:*", + "@rushstack/heft": "workspace:*", + "heft-storybook-v6-react-tutorial-storykit": "workspace:*" + } +} diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/config/rig.json b/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/config/rig.json new file mode 100644 index 00000000000..659f339663a --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/config/rig.json @@ -0,0 +1,8 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-web-rig", + "rigProfile": "library" +} diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/eslint.config.js b/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/eslint.config.js new file mode 100644 index 00000000000..03f094b356b --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-web-rig/profiles/library/includes/eslint/flat/profile/web-app'); +const friendlyLocalsMixin = require('local-web-rig/profiles/library/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/package.json b/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/package.json new file mode 100644 index 00000000000..83f9a52f58d --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/package.json @@ -0,0 +1,63 @@ +{ + "name": "heft-storybook-v6-react-tutorial-storykit", + "version": "0.0.0", + "private": true, + "description": "Storybook build dependencies for heft-storybook-v6-react-tutorial", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*", + "import": "./lib-esm/*", + "require": "./lib-commonjs/*" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" + }, + "dependencies": { + "@babel/core": "~7.20.0", + "@storybook/addon-actions": "~6.4.18", + "@storybook/addon-essentials": "~6.4.18", + "@storybook/addon-links": "~6.4.18", + "@storybook/cli": "~6.4.18", + "@storybook/components": "~6.4.18", + "@storybook/core-events": "~6.4.18", + "@storybook/react": "~6.4.18", + "@storybook/theming": "~6.4.18", + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "@types/react-dom": "17.0.25", + "@types/react": "17.0.74", + "@types/webpack-env": "1.18.8", + "babel-loader": "~8.2.3", + "css-loader": "~5.2.7", + "jest": "~29.3.1", + "react-dom": "~17.0.2", + "react": "~17.0.2", + "style-loader": "~2.0.0", + "terser-webpack-plugin": "~3.0.8", + "typescript": "~5.8.2", + "webpack": "~4.47.0" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "eslint": "~9.37.0", + "local-web-rig": "workspace:*" + } +} diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/src/index.ts b/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/src/index.ts new file mode 100644 index 00000000000..0943778a5c3 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/src/index.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// eslint-disable-next-line no-restricted-syntax +export * from '@storybook/react'; diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/tsconfig.json b/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/tsconfig.json new file mode 100644 index 00000000000..de5a9dd1fdd --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial-storykit/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "./node_modules/local-web-rig/profiles/library/tsconfig-base.json", + "compilerOptions": { + // The dependencies of this project have issues + "skipLibCheck": true + } +} diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/.gitignore b/build-tests-samples/heft-storybook-v6-react-tutorial/.gitignore new file mode 100644 index 00000000000..f0f1cccf7f1 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/.gitignore @@ -0,0 +1 @@ +!.vscode \ No newline at end of file diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/.storybook/main.js b/build-tests-samples/heft-storybook-v6-react-tutorial/.storybook/main.js new file mode 100644 index 00000000000..14d42461c99 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/.storybook/main.js @@ -0,0 +1,4 @@ +module.exports = { + stories: ['../lib-esm/**/*.stories.js'], + addons: ['@storybook/addon-links', '@storybook/addon-essentials'] +}; diff --git a/build-tests-samples/heft-storybook-react-tutorial/.storybook/preview.js b/build-tests-samples/heft-storybook-v6-react-tutorial/.storybook/preview.js similarity index 100% rename from build-tests-samples/heft-storybook-react-tutorial/.storybook/preview.js rename to build-tests-samples/heft-storybook-v6-react-tutorial/.storybook/preview.js diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/.vscode/launch.json b/build-tests-samples/heft-storybook-v6-react-tutorial/.vscode/launch.json new file mode 100644 index 00000000000..90018262003 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Debug \"heft start\"", + "program": "${workspaceFolder}/node_modules/@rushstack/heft/lib-commonjs/start.js", + "cwd": "${workspaceFolder}", + "args": ["--debug", "start", "--storybook"], + "console": "integratedTerminal", + "sourceMaps": false + }, + ] +} \ No newline at end of file diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/README.md b/build-tests-samples/heft-storybook-v6-react-tutorial/README.md new file mode 100644 index 00000000000..a524fc552e0 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/README.md @@ -0,0 +1,8 @@ +# heft-storybook-v6-react-tutorial + +This is a copy of the +[heft-storybook-v6-react-tutorial](https://github.com/microsoft/rushstack-samples/tree/main/heft/heft-storybook-v6-react-tutorial) +tutorial project from the [rushstack-samples](https://github.com/microsoft/rushstack-samples) repo. + +The copy here serves as a regression test, by using `"workspace:*"` references to the local projects in this repo. +Please update the copy from time to time to keep it in sync with the official tutorial. diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/assets/index.html b/build-tests-samples/heft-storybook-v6-react-tutorial/assets/index.html new file mode 100644 index 00000000000..9e89ef57d85 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/assets/index.html @@ -0,0 +1,12 @@ + + + + + + Example Application + + + +
+ + diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/config/heft.json b/build-tests-samples/heft-storybook-v6-react-tutorial/config/heft.json new file mode 100644 index 00000000000..efe61e5d669 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/config/heft.json @@ -0,0 +1,56 @@ +/** + * Defines configuration used by core Heft. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + // TODO: Add comments + "phasesByName": { + "build": { + "cleanFiles": [{ "includeGlobs": ["dist", "dist-storybook", "lib", "lib-commonjs"] }], + + "tasksByName": { + "typescript": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-typescript-plugin" + } + }, + "lint": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-lint-plugin" + } + }, + "webpack": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-webpack4-plugin" + } + }, + "storybook": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-storybook-plugin", + "options": { + "storykitPackageName": "heft-storybook-v6-react-tutorial-storykit", + "cliPackageName": "@storybook/react", + "cliCallingConvention": "storybook6", + "staticBuildOutputFolder": "dist-storybook" + } + } + } + } + }, + + "test": { + "phaseDependencies": ["build"], + "tasksByName": { + "jest": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-jest-plugin" + } + } + } + } + } +} diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/config/jest.config.json b/build-tests-samples/heft-storybook-v6-react-tutorial/config/jest.config.json new file mode 100644 index 00000000000..5e165f55d1d --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/config/jest.config.json @@ -0,0 +1,13 @@ +{ + "extends": "@rushstack/heft-jest-plugin/includes/jest-web.config.json", + + // These additional properties exist for caching purposes in the rushstack repo + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" +} diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/config/rush-project.json b/build-tests-samples/heft-storybook-v6-react-tutorial/config/rush-project.json new file mode 100644 index 00000000000..700f29565b5 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/config/rush-project.json @@ -0,0 +1,15 @@ +// This file exists for caching purposes in the rushstack repo +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": [".heft", "lib-commonjs", "lib-esm", "dist", "dist-storybook"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] + } + ] +} diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/config/typescript.json b/build-tests-samples/heft-storybook-v6-react-tutorial/config/typescript.json new file mode 100644 index 00000000000..80efdd16510 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/config/typescript.json @@ -0,0 +1,53 @@ +/** + * Configures the TypeScript plugin for Heft. This plugin also manages linting. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", + + /** + * If provided, emit these module kinds in addition to the modules specified in the tsconfig. + * Note that this option only applies to the main tsconfig.json configuration. + */ + "additionalModuleKindsToEmit": [ + // { + // /** + // * (Required) Must be one of "commonjs", "amd", "umd", "system", "es2015", "esnext" + // */ + // "moduleKind": "amd", + // + // /** + // * (Required) The name of the folder where the output will be written. + // */ + // "outFolderName": "lib-amd" + // } + { + "moduleKind": "commonjs", + "outFolderName": "lib-commonjs" + } + ], + + /** + * Describes the way files should be statically coped from src to TS output folders + */ + "staticAssetsToCopy": { + /** + * File extensions that should be copied from the src folder to the destination folder(s). + */ + "fileExtensions": [".css"] + + /** + * Glob patterns that should be explicitly included. + */ + // "includeGlobs": [ + // "some/path/*.js" + // ], + + /** + * Glob patterns that should be explicitly excluded. This takes precedence over globs listed + * in "includeGlobs" and files that match the file extensions provided in "fileExtensions". + */ + // "excludeGlobs": [ + // "some/path/*.css" + // ] + } +} diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/eslint.config.js b/build-tests-samples/heft-storybook-v6-react-tutorial/eslint.config.js new file mode 100644 index 00000000000..e5eaf3c624a --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-eslint-config/flat/profile/web-app'); +const reactMixin = require('local-eslint-config/flat/mixins/react'); + +module.exports = [ + ...webAppProfile, + ...reactMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/package.json b/build-tests-samples/heft-storybook-v6-react-tutorial/package.json new file mode 100644 index 00000000000..4bc6a70f023 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/package.json @@ -0,0 +1,44 @@ +{ + "name": "heft-storybook-v6-react-tutorial", + "description": "(Copy of sample project) Building this project is a regression test for Heft", + "version": "1.0.0", + "private": true, + "scripts": { + "build": "heft build --clean", + "start": "heft build-watch", + "storybook": "heft build-watch --serve --storybook", + "build-storybook": "heft build --storybook", + "_phase:build": "heft run --only build -- --clean --storybook", + "_phase:test": "heft run --only test -- --clean" + }, + "dependencies": { + "react-dom": "~17.0.2", + "react": "~17.0.2", + "tslib": "~2.8.1" + }, + "devDependencies": { + "@babel/core": "~7.20.0", + "@rushstack/heft": "workspace:*", + "@rushstack/heft-jest-plugin": "workspace:*", + "@rushstack/heft-lint-plugin": "workspace:*", + "@rushstack/heft-storybook-plugin": "workspace:*", + "@rushstack/heft-typescript-plugin": "workspace:*", + "@rushstack/heft-webpack4-plugin": "workspace:*", + "@rushstack/webpack4-module-minifier-plugin": "workspace:*", + "@storybook/react": "~6.4.18", + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "@types/react": "17.0.74", + "@types/react-dom": "17.0.25", + "@types/webpack-env": "1.18.8", + "css-loader": "~5.2.7", + "eslint": "~9.37.0", + "heft-storybook-v6-react-tutorial-storykit": "workspace:*", + "html-webpack-plugin": "~4.5.2", + "local-eslint-config": "workspace:*", + "source-map-loader": "~1.1.3", + "style-loader": "~2.0.0", + "typescript": "~5.8.2", + "webpack": "~4.47.0" + } +} diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/src/ExampleApp.tsx b/build-tests-samples/heft-storybook-v6-react-tutorial/src/ExampleApp.tsx new file mode 100644 index 00000000000..d81154eaefe --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/src/ExampleApp.tsx @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as React from 'react'; + +import { ToggleSwitch, type IToggleEventArgs } from './ToggleSwitch'; + +/** + * This React component renders the application page. + */ +export class ExampleApp extends React.Component { + public render(): React.ReactNode { + const appStyle: React.CSSProperties = { + backgroundColor: '#ffffff', + padding: '20px', + borderRadius: '5px', + width: '400px' + }; + + return ( +
+
+

Hello, world!

+ Here is an example control: + +
+
+ ); + } + + // React event handlers should be represented as fields instead of methods to ensure the "this" pointer + // is bound correctly. This form does not work with virtual/override inheritance, so use regular methods + // everywhere else. + private _onToggle = (sender: ToggleSwitch, args: IToggleEventArgs): void => { + // eslint-disable-next-line no-console + console.log('Toggle switch changed: ' + args.sliderPosition); + }; +} diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/src/ToggleSwitch.stories.tsx b/build-tests-samples/heft-storybook-v6-react-tutorial/src/ToggleSwitch.stories.tsx new file mode 100644 index 00000000000..364bf6e0366 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/src/ToggleSwitch.stories.tsx @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as React from 'react'; +import type { ComponentStory, ComponentMeta } from 'heft-storybook-v6-react-tutorial-storykit'; + +import { ToggleSwitch } from './ToggleSwitch'; + +export default { + title: 'Octogonz/ToggleSwitch', + component: ToggleSwitch, + argTypes: { + leftColor: { control: 'color' }, + rightColor: { control: 'color' } + } +} as ComponentMeta; + +const Template: ComponentStory = (args) => ; + +// eslint-disable-next-line +export const Primary: any = Template.bind({}); +Primary.args = {}; + +// eslint-disable-next-line +export const Secondary: any = Template.bind({}); +Secondary.args = {}; diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/src/ToggleSwitch.tsx b/build-tests-samples/heft-storybook-v6-react-tutorial/src/ToggleSwitch.tsx new file mode 100644 index 00000000000..79e43aad327 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/src/ToggleSwitch.tsx @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as React from 'react'; + +/** + * Slider positions for `ToggleSwitch`. + */ +export const enum ToggleSwitchPosition { + Left = 'left', + Right = 'right' +} + +/** + * Event arguments for `IToggleSwitchProps.onToggle`. + */ +export interface IToggleEventArgs { + sliderPosition: ToggleSwitchPosition; +} + +export interface IToggleSwitchProps { + /** + * The CSS color when the `ToggleSwitch` slider is in the left position. + * Example value: `"#800000"` + */ + leftColor: string; + + /** + * The CSS color when the `ToggleSwitch` slider is in the right position. + * Example value: `"#008000"` + */ + rightColor: string; + + /** + * An event that fires when the `ToggleSwitch` control is clicked. + */ + onToggle?: (sender: ToggleSwitch, args: IToggleEventArgs) => void; +} + +/** + * Private state for ToggleSwitch. + */ +interface IToggleSwitchState { + sliderPosition: ToggleSwitchPosition; +} + +/** + * An example component that renders a switch whose slider position can be "left" or "right". + */ +export class ToggleSwitch extends React.Component { + public constructor(props: IToggleSwitchProps) { + super(props); + this.state = { + sliderPosition: ToggleSwitchPosition.Left + }; + } + + public render(): React.ReactNode { + const frameStyle: React.CSSProperties = { + borderRadius: '10px', + backgroundColor: + this.state.sliderPosition === ToggleSwitchPosition.Left + ? this.props.leftColor + : this.props.rightColor, + width: '35px', + height: '20px', + cursor: 'pointer' + }; + const sliderStyle: React.CSSProperties = { + borderRadius: '10px', + backgroundColor: '#c0c0c0', + width: '20px', + height: '20px' + }; + + if (this.state.sliderPosition === ToggleSwitchPosition.Left) { + sliderStyle.marginLeft = '0px'; + sliderStyle.marginRight = 'auto'; + } else { + sliderStyle.marginLeft = 'auto'; + sliderStyle.marginRight = '0px'; + } + + return ( +
+
+
+ ); + } + + // React event handlers should be represented as fields instead of methods to ensure the "this" pointer + // is bound correctly. This form does not work with virtual/override inheritance, so use regular methods + // everywhere else. + private _onClickSlider = (event: React.MouseEvent): void => { + if (this.state.sliderPosition === ToggleSwitchPosition.Left) { + this.setState({ sliderPosition: ToggleSwitchPosition.Right }); + } else { + this.setState({ sliderPosition: ToggleSwitchPosition.Left }); + } + + if (this.props.onToggle) { + this.props.onToggle(this, { sliderPosition: this.state.sliderPosition }); + } + }; +} diff --git a/build-tests-samples/heft-storybook-react-tutorial/src/index.css b/build-tests-samples/heft-storybook-v6-react-tutorial/src/index.css similarity index 100% rename from build-tests-samples/heft-storybook-react-tutorial/src/index.css rename to build-tests-samples/heft-storybook-v6-react-tutorial/src/index.css diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/src/index.tsx b/build-tests-samples/heft-storybook-v6-react-tutorial/src/index.tsx new file mode 100644 index 00000000000..d2a345895ca --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/src/index.tsx @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; + +import { ExampleApp } from './ExampleApp'; + +import './index.css'; + +const rootDiv: HTMLElement = document.getElementById('root') as HTMLElement; +ReactDOM.render(, rootDiv); diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/src/test/ToggleSwitch.test.ts b/build-tests-samples/heft-storybook-v6-react-tutorial/src/test/ToggleSwitch.test.ts new file mode 100644 index 00000000000..0891628cc54 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/src/test/ToggleSwitch.test.ts @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { ToggleSwitch } from '../ToggleSwitch'; + +describe('ToggleSwitch', () => { + it('can be tested', () => { + expect(ToggleSwitch).toBeDefined(); + }); +}); diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/tsconfig.json b/build-tests-samples/heft-storybook-v6-react-tutorial/tsconfig.json new file mode 100644 index 00000000000..6bc3d29f6b0 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/tsconfig.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib-esm", + "rootDir": "src", + + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strict": true, + "useUnknownInCatchVariables": false, + "esModuleInterop": true, + "noEmitOnError": false, + "allowUnreachableCode": false, + "importHelpers": true, + + "types": ["jest", "webpack-env"], + + "module": "esnext", + "moduleResolution": "node", + "target": "es5", + "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/build-tests-samples/heft-storybook-v6-react-tutorial/webpack.config.js b/build-tests-samples/heft-storybook-v6-react-tutorial/webpack.config.js new file mode 100644 index 00000000000..f74383cf630 --- /dev/null +++ b/build-tests-samples/heft-storybook-v6-react-tutorial/webpack.config.js @@ -0,0 +1,70 @@ +'use strict'; + +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const { ModuleMinifierPlugin, WorkerPoolMinifier } = require('@rushstack/webpack4-module-minifier-plugin'); + +/** + * If the "--production" command-line parameter is specified when invoking Heft, then the + * "production" function parameter will be true. You can use this to enable bundling optimizations. + */ +function createWebpackConfig({ production }) { + const webpackConfig = { + // Documentation: https://webpack.js.org/configuration/mode/ + mode: production ? 'production' : 'development', + resolve: { + extensions: ['.js', '.json'] + }, + module: { + rules: [ + { + test: /\.css$/, + use: [require.resolve('style-loader'), require.resolve('css-loader')] + }, + { + test: /\.js$/, + enforce: 'pre', + use: ['source-map-loader'] + } + ] + }, + entry: { + app: path.join(__dirname, 'lib-commonjs', 'index.js'), + + // Put these libraries in a separate vendor bundle + vendor: ['react', 'react-dom'] + }, + output: { + path: path.join(__dirname, 'dist'), + filename: '[name]_[contenthash].js' + }, + performance: { + // This specifies the bundle size limit that will trigger Webpack's warning saying: + // "The following entrypoint(s) combined asset size exceeds the recommended limit." + maxEntrypointSize: 250000, + maxAssetSize: 250000 + }, + devServer: { + port: 9000 + }, + devtool: production ? undefined : 'source-map', + plugins: [ + // See here for documentation: https://github.com/jantimon/html-webpack-plugin + new HtmlWebpackPlugin({ + template: 'assets/index.html' + }) + ], + optimization: { + minimizer: [ + new ModuleMinifierPlugin({ + minifier: new WorkerPoolMinifier(), + useSourceMap: true + }) + ] + } + }; + + return webpackConfig; +} + +module.exports = createWebpackConfig; diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial-app/README.md b/build-tests-samples/heft-storybook-v9-react-tutorial-app/README.md new file mode 100644 index 00000000000..a64b0ba8922 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial-app/README.md @@ -0,0 +1,4 @@ +# heft-storybook-v9-react-tutorial-app + +This is project builds the storybook exports from the +[heft-storybook-v9-react-tutorial-app](https://github.com/microsoft/rushstack-samples/tree/main/heft/heft-storybook-v9-react-tutorial-app) and is a regression test for the heft-storybook-plugin `cwdPackageName` option. diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial-app/build.js b/build-tests-samples/heft-storybook-v9-react-tutorial-app/build.js new file mode 100644 index 00000000000..f0def856e8d --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial-app/build.js @@ -0,0 +1,32 @@ +// TODO: Remove this and change the _phase:build script back to "heft run --only build -- --clean --storybook" +// when we drop support for Node 18 + +const { Executable, Import } = require('@rushstack/node-core-library'); + +const heftBinPath = Import.resolveModule({ + modulePath: '@rushstack/heft/bin/heft', + baseFolderPath: __dirname +}); + +const heftArgs = [ + heftBinPath, + 'run', + '--only', + 'build', + '--', + '--clean', + ...(process.version.startsWith('v18.') + ? [] // Under Node 18, don't run storybook + : ['--storybook']) +]; + +const { signal, status } = Executable.spawnSync(process.argv0, heftArgs, { + stdio: 'inherit', + environment: process.env +}); + +if (signal) { + process.kill(process.pid, signal); +} else { + process.exit(status); +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial-app/config/heft.json b/build-tests-samples/heft-storybook-v9-react-tutorial-app/config/heft.json new file mode 100644 index 00000000000..8db2e46e058 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial-app/config/heft.json @@ -0,0 +1,25 @@ +/** + * Defines configuration used by core Heft. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "phasesByName": { + "build": { + "tasksByName": { + "storybook": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-storybook-plugin", + "options": { + "storykitPackageName": "heft-storybook-v9-react-tutorial-storykit", + "cliPackageName": "storybook", + "cliCallingConvention": "storybook9", + "staticBuildOutputFolder": "dist", + "cwdPackageName": "heft-storybook-v9-react-tutorial" + } + } + } + } + } + } +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial-app/config/rush-project.json b/build-tests-samples/heft-storybook-v9-react-tutorial-app/config/rush-project.json new file mode 100644 index 00000000000..a5b43ed724b --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial-app/config/rush-project.json @@ -0,0 +1,13 @@ +// This file exists for caching purposes in the rushstack repo +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:lite-build", + "outputFolderNames": ["dist"], + // This project builds differently between Node 18 and other versions of Node + "dependsOnNodeVersion": "major" + } + ] +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial-app/package.json b/build-tests-samples/heft-storybook-v9-react-tutorial-app/package.json new file mode 100644 index 00000000000..efedb3b1790 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial-app/package.json @@ -0,0 +1,19 @@ +{ + "name": "heft-storybook-v9-react-tutorial-app", + "description": "Building this project is a regression test for heft-storybook-plugin", + "version": "1.0.0", + "private": true, + "scripts": { + "build": "heft build --clean --storybook", + "_phase:lite-build": "node ./build" + }, + "dependencies": { + "heft-storybook-v9-react-tutorial": "workspace: *" + }, + "devDependencies": { + "@rushstack/heft-storybook-plugin": "workspace:*", + "@rushstack/heft": "workspace:*", + "heft-storybook-v9-react-tutorial-storykit": "workspace:*", + "@rushstack/node-core-library": "workspace:*" + } +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/config/rig.json b/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/config/rig.json new file mode 100644 index 00000000000..659f339663a --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/config/rig.json @@ -0,0 +1,8 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-web-rig", + "rigProfile": "library" +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/eslint.config.js b/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/eslint.config.js new file mode 100644 index 00000000000..03f094b356b --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-web-rig/profiles/library/includes/eslint/flat/profile/web-app'); +const friendlyLocalsMixin = require('local-web-rig/profiles/library/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/package.json b/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/package.json new file mode 100644 index 00000000000..fd0b2858e4c --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/package.json @@ -0,0 +1,60 @@ +{ + "name": "heft-storybook-v9-react-tutorial-storykit", + "version": "0.0.0", + "private": true, + "description": "Storybook build dependencies for heft-storybook-v9-react-tutorial", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*", + "import": "./lib-esm/*", + "require": "./lib-commonjs/*" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" + }, + "dependencies": { + "@babel/core": "~7.20.0", + "@storybook/cli": "~9.1.6", + "@storybook/react": "~9.1.6", + "@storybook/react-webpack5": "~9.1.6", + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "@types/react-dom": "19.2.3", + "@types/react": "19.2.7", + "@types/webpack-env": "1.18.8", + "babel-loader": "~8.2.3", + "css-loader": "~5.2.7", + "jest": "~29.3.1", + "react-dom": "~19.2.3", + "react": "~19.2.3", + "style-loader": "~2.0.0", + "terser-webpack-plugin": "~3.0.8", + "typescript": "~5.8.2", + "webpack": "~5.105.2", + "storybook": "~9.1.6", + "@testing-library/dom": "~7.21.4" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "eslint": "~9.37.0", + "local-web-rig": "workspace:*" + } +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/src/index.ts b/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/src/index.ts new file mode 100644 index 00000000000..0943778a5c3 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/src/index.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// eslint-disable-next-line no-restricted-syntax +export * from '@storybook/react'; diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/tsconfig.json b/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/tsconfig.json new file mode 100644 index 00000000000..de5a9dd1fdd --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial-storykit/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "./node_modules/local-web-rig/profiles/library/tsconfig-base.json", + "compilerOptions": { + // The dependencies of this project have issues + "skipLibCheck": true + } +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/.gitignore b/build-tests-samples/heft-storybook-v9-react-tutorial/.gitignore new file mode 100644 index 00000000000..f0f1cccf7f1 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/.gitignore @@ -0,0 +1 @@ +!.vscode \ No newline at end of file diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/.storybook/main.js b/build-tests-samples/heft-storybook-v9-react-tutorial/.storybook/main.js new file mode 100644 index 00000000000..2dd156ba39d --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/.storybook/main.js @@ -0,0 +1,9 @@ +const { Import } = require('@rushstack/node-core-library'); + +module.exports = { + stories: ['../lib-esm/**/*.stories.js'], + framework: Import.resolvePackage({ + packageName: '@storybook/react-webpack5', + baseFolderPath: __dirname + }) +}; diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/.storybook/preview.js b/build-tests-samples/heft-storybook-v9-react-tutorial/.storybook/preview.js new file mode 100644 index 00000000000..30f63ee2960 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/.storybook/preview.js @@ -0,0 +1,9 @@ +export const parameters = { + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/ + } + } +}; diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/.vscode/launch.json b/build-tests-samples/heft-storybook-v9-react-tutorial/.vscode/launch.json new file mode 100644 index 00000000000..90018262003 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Debug \"heft start\"", + "program": "${workspaceFolder}/node_modules/@rushstack/heft/lib-commonjs/start.js", + "cwd": "${workspaceFolder}", + "args": ["--debug", "start", "--storybook"], + "console": "integratedTerminal", + "sourceMaps": false + }, + ] +} \ No newline at end of file diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/README.md b/build-tests-samples/heft-storybook-v9-react-tutorial/README.md new file mode 100644 index 00000000000..a6247950760 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/README.md @@ -0,0 +1,8 @@ +# heft-storybook-v9-react-tutorial + +This is a copy of the +[heft-storybook-v9-react-tutorial](https://github.com/microsoft/rushstack-samples/tree/main/heft/heft-storybook-v9-react-tutorial) +tutorial project from the [rushstack-samples](https://github.com/microsoft/rushstack-samples) repo. + +The copy here serves as a regression test, by using `"workspace:*"` references to the local projects in this repo. +Please update the copy from time to time to keep it in sync with the official tutorial. diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/assets/index.html b/build-tests-samples/heft-storybook-v9-react-tutorial/assets/index.html new file mode 100644 index 00000000000..9e89ef57d85 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/assets/index.html @@ -0,0 +1,12 @@ + + + + + + Example Application + + + +
+ + diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/build.js b/build-tests-samples/heft-storybook-v9-react-tutorial/build.js new file mode 100644 index 00000000000..f0def856e8d --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/build.js @@ -0,0 +1,32 @@ +// TODO: Remove this and change the _phase:build script back to "heft run --only build -- --clean --storybook" +// when we drop support for Node 18 + +const { Executable, Import } = require('@rushstack/node-core-library'); + +const heftBinPath = Import.resolveModule({ + modulePath: '@rushstack/heft/bin/heft', + baseFolderPath: __dirname +}); + +const heftArgs = [ + heftBinPath, + 'run', + '--only', + 'build', + '--', + '--clean', + ...(process.version.startsWith('v18.') + ? [] // Under Node 18, don't run storybook + : ['--storybook']) +]; + +const { signal, status } = Executable.spawnSync(process.argv0, heftArgs, { + stdio: 'inherit', + environment: process.env +}); + +if (signal) { + process.kill(process.pid, signal); +} else { + process.exit(status); +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/config/heft.json b/build-tests-samples/heft-storybook-v9-react-tutorial/config/heft.json new file mode 100644 index 00000000000..2f616721e8c --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/config/heft.json @@ -0,0 +1,56 @@ +/** + * Defines configuration used by core Heft. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + // TODO: Add comments + "phasesByName": { + "build": { + "cleanFiles": [{ "includeGlobs": ["dist", "dist-storybook", "lib", "lib-commonjs"] }], + + "tasksByName": { + "typescript": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-typescript-plugin" + } + }, + "lint": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-lint-plugin" + } + }, + "webpack": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-webpack5-plugin" + } + }, + "storybook": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-storybook-plugin", + "options": { + "storykitPackageName": "heft-storybook-v9-react-tutorial-storykit", + "cliPackageName": "storybook", + "cliCallingConvention": "storybook9", + "staticBuildOutputFolder": "dist-storybook" + } + } + } + } + }, + + "test": { + "phaseDependencies": ["build"], + "tasksByName": { + "jest": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-jest-plugin" + } + } + } + } + } +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/config/jest.config.json b/build-tests-samples/heft-storybook-v9-react-tutorial/config/jest.config.json new file mode 100644 index 00000000000..5e165f55d1d --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/config/jest.config.json @@ -0,0 +1,13 @@ +{ + "extends": "@rushstack/heft-jest-plugin/includes/jest-web.config.json", + + // These additional properties exist for caching purposes in the rushstack repo + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/config/rush-project.json b/build-tests-samples/heft-storybook-v9-react-tutorial/config/rush-project.json new file mode 100644 index 00000000000..cdcf8f93e85 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/config/rush-project.json @@ -0,0 +1,17 @@ +// This file exists for caching purposes in the rushstack repo +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": [".heft", "lib-esm", "lib-commonjs", "dist"], + // This project builds differently between Node 18 and other versions of Node + "dependsOnNodeVersion": "major" + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] + } + ] +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/config/typescript.json b/build-tests-samples/heft-storybook-v9-react-tutorial/config/typescript.json new file mode 100644 index 00000000000..80efdd16510 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/config/typescript.json @@ -0,0 +1,53 @@ +/** + * Configures the TypeScript plugin for Heft. This plugin also manages linting. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", + + /** + * If provided, emit these module kinds in addition to the modules specified in the tsconfig. + * Note that this option only applies to the main tsconfig.json configuration. + */ + "additionalModuleKindsToEmit": [ + // { + // /** + // * (Required) Must be one of "commonjs", "amd", "umd", "system", "es2015", "esnext" + // */ + // "moduleKind": "amd", + // + // /** + // * (Required) The name of the folder where the output will be written. + // */ + // "outFolderName": "lib-amd" + // } + { + "moduleKind": "commonjs", + "outFolderName": "lib-commonjs" + } + ], + + /** + * Describes the way files should be statically coped from src to TS output folders + */ + "staticAssetsToCopy": { + /** + * File extensions that should be copied from the src folder to the destination folder(s). + */ + "fileExtensions": [".css"] + + /** + * Glob patterns that should be explicitly included. + */ + // "includeGlobs": [ + // "some/path/*.js" + // ], + + /** + * Glob patterns that should be explicitly excluded. This takes precedence over globs listed + * in "includeGlobs" and files that match the file extensions provided in "fileExtensions". + */ + // "excludeGlobs": [ + // "some/path/*.css" + // ] + } +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/eslint.config.js b/build-tests-samples/heft-storybook-v9-react-tutorial/eslint.config.js new file mode 100644 index 00000000000..e5eaf3c624a --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-eslint-config/flat/profile/web-app'); +const reactMixin = require('local-eslint-config/flat/mixins/react'); + +module.exports = [ + ...webAppProfile, + ...reactMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/package.json b/build-tests-samples/heft-storybook-v9-react-tutorial/package.json new file mode 100644 index 00000000000..3c415916e4c --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/package.json @@ -0,0 +1,49 @@ +{ + "name": "heft-storybook-v9-react-tutorial", + "description": "(Copy of sample project) Building this project is a regression test for Heft", + "version": "1.0.0", + "private": true, + "scripts": { + "build": "heft build --clean", + "start": "heft build-watch", + "storybook": "heft build-watch --serve --storybook", + "build-storybook": "heft build --storybook", + "_phase:build": "node ./build", + "_phase:test": "heft run --only test -- --clean" + }, + "dependencies": { + "react-dom": "~19.2.3", + "react": "~19.2.3", + "tslib": "~2.8.1" + }, + "devDependencies": { + "@babel/core": "~7.20.0", + "@rushstack/heft": "workspace:*", + "@rushstack/heft-jest-plugin": "workspace:*", + "@rushstack/heft-lint-plugin": "workspace:*", + "@rushstack/heft-storybook-plugin": "workspace:*", + "@rushstack/heft-typescript-plugin": "workspace:*", + "@rushstack/heft-webpack5-plugin": "workspace:*", + "@rushstack/webpack5-module-minifier-plugin": "workspace:*", + "@storybook/react-webpack5": "~9.1.6", + "@storybook/react": "~9.1.6", + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "@types/react": "19.2.7", + "@types/react-dom": "19.2.3", + "@types/webpack-env": "1.18.8", + "css-loader": "~5.2.7", + "eslint": "~9.37.0", + "heft-storybook-v9-react-tutorial-storykit": "workspace:*", + "html-webpack-plugin": "~5.5.0", + "local-eslint-config": "workspace:*", + "source-map-loader": "~1.1.3", + "style-loader": "~2.0.0", + "typescript": "~5.8.2", + "webpack": "~5.105.2", + "storybook": "~9.1.6", + "@testing-library/dom": "~7.21.4", + "@rushstack/module-minifier": "workspace:*", + "@rushstack/node-core-library": "workspace:*" + } +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/src/ExampleApp.tsx b/build-tests-samples/heft-storybook-v9-react-tutorial/src/ExampleApp.tsx new file mode 100644 index 00000000000..d81154eaefe --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/src/ExampleApp.tsx @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as React from 'react'; + +import { ToggleSwitch, type IToggleEventArgs } from './ToggleSwitch'; + +/** + * This React component renders the application page. + */ +export class ExampleApp extends React.Component { + public render(): React.ReactNode { + const appStyle: React.CSSProperties = { + backgroundColor: '#ffffff', + padding: '20px', + borderRadius: '5px', + width: '400px' + }; + + return ( +
+
+

Hello, world!

+ Here is an example control: + +
+
+ ); + } + + // React event handlers should be represented as fields instead of methods to ensure the "this" pointer + // is bound correctly. This form does not work with virtual/override inheritance, so use regular methods + // everywhere else. + private _onToggle = (sender: ToggleSwitch, args: IToggleEventArgs): void => { + // eslint-disable-next-line no-console + console.log('Toggle switch changed: ' + args.sliderPosition); + }; +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/src/ToggleSwitch.stories.tsx b/build-tests-samples/heft-storybook-v9-react-tutorial/src/ToggleSwitch.stories.tsx new file mode 100644 index 00000000000..83befb9fff2 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/src/ToggleSwitch.stories.tsx @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { Meta, StoryObj } from 'heft-storybook-v9-react-tutorial-storykit'; + +import { ToggleSwitch } from './ToggleSwitch'; + +export default { + title: 'Octogonz/ToggleSwitch', + component: ToggleSwitch, + argTypes: { + leftColor: { control: 'color' }, + rightColor: { control: 'color' } + } +} as Meta; + +export const Primary: StoryObj = { + args: { + leftColor: '#880000', + rightColor: '#008000' + } +}; + +export const Secondary: StoryObj = { + args: {} +}; diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/src/ToggleSwitch.tsx b/build-tests-samples/heft-storybook-v9-react-tutorial/src/ToggleSwitch.tsx new file mode 100644 index 00000000000..79e43aad327 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/src/ToggleSwitch.tsx @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as React from 'react'; + +/** + * Slider positions for `ToggleSwitch`. + */ +export const enum ToggleSwitchPosition { + Left = 'left', + Right = 'right' +} + +/** + * Event arguments for `IToggleSwitchProps.onToggle`. + */ +export interface IToggleEventArgs { + sliderPosition: ToggleSwitchPosition; +} + +export interface IToggleSwitchProps { + /** + * The CSS color when the `ToggleSwitch` slider is in the left position. + * Example value: `"#800000"` + */ + leftColor: string; + + /** + * The CSS color when the `ToggleSwitch` slider is in the right position. + * Example value: `"#008000"` + */ + rightColor: string; + + /** + * An event that fires when the `ToggleSwitch` control is clicked. + */ + onToggle?: (sender: ToggleSwitch, args: IToggleEventArgs) => void; +} + +/** + * Private state for ToggleSwitch. + */ +interface IToggleSwitchState { + sliderPosition: ToggleSwitchPosition; +} + +/** + * An example component that renders a switch whose slider position can be "left" or "right". + */ +export class ToggleSwitch extends React.Component { + public constructor(props: IToggleSwitchProps) { + super(props); + this.state = { + sliderPosition: ToggleSwitchPosition.Left + }; + } + + public render(): React.ReactNode { + const frameStyle: React.CSSProperties = { + borderRadius: '10px', + backgroundColor: + this.state.sliderPosition === ToggleSwitchPosition.Left + ? this.props.leftColor + : this.props.rightColor, + width: '35px', + height: '20px', + cursor: 'pointer' + }; + const sliderStyle: React.CSSProperties = { + borderRadius: '10px', + backgroundColor: '#c0c0c0', + width: '20px', + height: '20px' + }; + + if (this.state.sliderPosition === ToggleSwitchPosition.Left) { + sliderStyle.marginLeft = '0px'; + sliderStyle.marginRight = 'auto'; + } else { + sliderStyle.marginLeft = 'auto'; + sliderStyle.marginRight = '0px'; + } + + return ( +
+
+
+ ); + } + + // React event handlers should be represented as fields instead of methods to ensure the "this" pointer + // is bound correctly. This form does not work with virtual/override inheritance, so use regular methods + // everywhere else. + private _onClickSlider = (event: React.MouseEvent): void => { + if (this.state.sliderPosition === ToggleSwitchPosition.Left) { + this.setState({ sliderPosition: ToggleSwitchPosition.Right }); + } else { + this.setState({ sliderPosition: ToggleSwitchPosition.Left }); + } + + if (this.props.onToggle) { + this.props.onToggle(this, { sliderPosition: this.state.sliderPosition }); + } + }; +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/src/index.css b/build-tests-samples/heft-storybook-v9-react-tutorial/src/index.css new file mode 100644 index 00000000000..47cd6d4c1ad --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/src/index.css @@ -0,0 +1,11 @@ +/** + * This file gets copied to the "lib" folder because its extension is registered in copy-static-assets.json + * Then Webpack uses css-loader to embed it in the application bundle, and then style-loader applies to the DOM. + */ +html, +body { + margin: 0; + height: 100%; + background-color: #c0c0c0; + font-family: Tahoma, sans-serif; +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/src/index.tsx b/build-tests-samples/heft-storybook-v9-react-tutorial/src/index.tsx new file mode 100644 index 00000000000..71c8197d797 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/src/index.tsx @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as React from 'react'; +import { createRoot } from 'react-dom/client'; + +import { ExampleApp } from './ExampleApp'; + +import './index.css'; + +const rootDiv: HTMLElement = document.getElementById('root') as HTMLElement; +createRoot(rootDiv).render(); diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/src/test/ToggleSwitch.test.ts b/build-tests-samples/heft-storybook-v9-react-tutorial/src/test/ToggleSwitch.test.ts new file mode 100644 index 00000000000..0891628cc54 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/src/test/ToggleSwitch.test.ts @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { ToggleSwitch } from '../ToggleSwitch'; + +describe('ToggleSwitch', () => { + it('can be tested', () => { + expect(ToggleSwitch).toBeDefined(); + }); +}); diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/tsconfig.json b/build-tests-samples/heft-storybook-v9-react-tutorial/tsconfig.json new file mode 100644 index 00000000000..5b06a6ff332 --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/tsconfig.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib-esm", + "rootDir": "src", + + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strict": true, + "useUnknownInCatchVariables": false, + "esModuleInterop": true, + "noEmitOnError": false, + "allowUnreachableCode": false, + "importHelpers": true, + + "types": ["jest", "webpack-env"], + + "module": "esnext", + "moduleResolution": "node", + "target": "es2020", + "lib": ["es2020", "scripthost", "dom"], + "skipLibCheck": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/build-tests-samples/heft-storybook-v9-react-tutorial/webpack.config.js b/build-tests-samples/heft-storybook-v9-react-tutorial/webpack.config.js new file mode 100644 index 00000000000..5c2abec2f3b --- /dev/null +++ b/build-tests-samples/heft-storybook-v9-react-tutorial/webpack.config.js @@ -0,0 +1,77 @@ +'use strict'; + +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const { ModuleMinifierPlugin } = require('@rushstack/webpack5-module-minifier-plugin'); +const { WorkerPoolMinifier } = require('@rushstack/module-minifier'); + +/** + * If the "--production" command-line parameter is specified when invoking Heft, then the + * "production" function parameter will be true. You can use this to enable bundling optimizations. + */ +function createWebpackConfig({ production }) { + const webpackConfig = { + // Documentation: https://webpack.js.org/configuration/mode/ + mode: production ? 'production' : 'development', + resolve: { + extensions: ['.js', '.json'] + }, + module: { + rules: [ + { + test: /\.css$/, + use: [require.resolve('style-loader'), require.resolve('css-loader')] + }, + { + test: /\.js$/, + enforce: 'pre', + use: ['source-map-loader'] + } + ] + }, + entry: { + app: path.join(__dirname, 'lib-commonjs', 'index.js'), + + // Put these libraries in a separate vendor bundle + vendor: ['react', 'react-dom'] + }, + output: { + path: path.join(__dirname, 'dist'), + filename: '[name]_[contenthash].js' + }, + performance: { + // This specifies the bundle size limit that will trigger Webpack's warning saying: + // "The following entrypoint(s) combined asset size exceeds the recommended limit." + maxEntrypointSize: 250000, + maxAssetSize: 250000 + }, + devServer: { + port: 9000 + }, + devtool: production ? undefined : 'source-map', + plugins: [ + // See here for documentation: https://github.com/jantimon/html-webpack-plugin + new HtmlWebpackPlugin({ + template: 'assets/index.html' + }) + ], + optimization: { + minimizer: [ + new ModuleMinifierPlugin({ + minifier: new WorkerPoolMinifier({ + terserOptions: { + ecma: 2020, + mangle: true + }, + verbose: true + }), + useSourceMap: true + }) + ] + } + }; + + return webpackConfig; +} + +module.exports = createWebpackConfig; diff --git a/build-tests-samples/heft-web-rig-app-tutorial/.eslintrc.js b/build-tests-samples/heft-web-rig-app-tutorial/.eslintrc.js deleted file mode 100644 index 288eaa16364..00000000000 --- a/build-tests-samples/heft-web-rig-app-tutorial/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/web-app', '@rushstack/eslint-config/mixins/react'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests-samples/heft-web-rig-app-tutorial/assets/index.html b/build-tests-samples/heft-web-rig-app-tutorial/assets/index.html index 3cfae745c19..9e89ef57d85 100644 --- a/build-tests-samples/heft-web-rig-app-tutorial/assets/index.html +++ b/build-tests-samples/heft-web-rig-app-tutorial/assets/index.html @@ -1,4 +1,4 @@ - + diff --git a/build-tests-samples/heft-web-rig-app-tutorial/config/jest.config.json b/build-tests-samples/heft-web-rig-app-tutorial/config/jest.config.json index 600ba9ea39a..29266733d41 100644 --- a/build-tests-samples/heft-web-rig-app-tutorial/config/jest.config.json +++ b/build-tests-samples/heft-web-rig-app-tutorial/config/jest.config.json @@ -1,3 +1,13 @@ { - "extends": "@rushstack/heft-web-rig/profiles/library/config/jest.config.json" + "extends": "@rushstack/heft-web-rig/profiles/app/config/jest.config.json", + + // These additional properties exist for caching purposes in the rushstack repo + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests-samples/heft-web-rig-app-tutorial/config/rig.json b/build-tests-samples/heft-web-rig-app-tutorial/config/rig.json index d72946b5042..687fc2911bc 100644 --- a/build-tests-samples/heft-web-rig-app-tutorial/config/rig.json +++ b/build-tests-samples/heft-web-rig-app-tutorial/config/rig.json @@ -2,5 +2,5 @@ "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", "rigPackageName": "@rushstack/heft-web-rig", - "rigProfile": "library" + "rigProfile": "app" } diff --git a/build-tests-samples/heft-web-rig-app-tutorial/config/rush-project.json b/build-tests-samples/heft-web-rig-app-tutorial/config/rush-project.json new file mode 100644 index 00000000000..852bbf63b9e --- /dev/null +++ b/build-tests-samples/heft-web-rig-app-tutorial/config/rush-project.json @@ -0,0 +1,15 @@ +// This file exists for caching purposes in the rushstack repo +{ + "extends": "@rushstack/heft-web-rig/profiles/app/config/rush-project.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": [".heft", "release"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] + } + ] +} diff --git a/build-tests-samples/heft-web-rig-app-tutorial/eslint.config.js b/build-tests-samples/heft-web-rig-app-tutorial/eslint.config.js new file mode 100644 index 00000000000..e5eaf3c624a --- /dev/null +++ b/build-tests-samples/heft-web-rig-app-tutorial/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-eslint-config/flat/profile/web-app'); +const reactMixin = require('local-eslint-config/flat/mixins/react'); + +module.exports = [ + ...webAppProfile, + ...reactMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-samples/heft-web-rig-app-tutorial/package.json b/build-tests-samples/heft-web-rig-app-tutorial/package.json index afb5a6f1f0a..b9370f0dbe0 100644 --- a/build-tests-samples/heft-web-rig-app-tutorial/package.json +++ b/build-tests-samples/heft-web-rig-app-tutorial/package.json @@ -11,17 +11,17 @@ }, "dependencies": { "heft-web-rig-library-tutorial": "workspace:*", - "react": "~16.13.1", - "react-dom": "~16.13.1", - "tslib": "~2.3.1" + "react": "~19.2.3", + "react-dom": "~19.2.3", + "tslib": "~2.8.1" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft-web-rig": "workspace:*", "@rushstack/heft": "workspace:*", - "@types/react-dom": "16.9.14", - "@types/react": "16.14.23", - "@types/webpack-env": "1.18.0", - "typescript": "~5.0.4" + "@rushstack/heft-web-rig": "workspace:*", + "@types/react": "19.2.7", + "@types/react-dom": "19.2.3", + "@types/webpack-env": "1.18.8", + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*" } } diff --git a/build-tests-samples/heft-web-rig-app-tutorial/src/ExampleApp.tsx b/build-tests-samples/heft-web-rig-app-tutorial/src/ExampleApp.tsx index f003efacfaa..3ef0dfdafab 100644 --- a/build-tests-samples/heft-web-rig-app-tutorial/src/ExampleApp.tsx +++ b/build-tests-samples/heft-web-rig-app-tutorial/src/ExampleApp.tsx @@ -1,5 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import * as React from 'react'; -import { ToggleSwitch, IToggleEventArgs } from 'heft-web-rig-library-tutorial'; +import { ToggleSwitch, type IToggleEventArgs } from 'heft-web-rig-library-tutorial'; + +import exampleImage from './example-image.png'; /** * This React component renders the application page. @@ -21,7 +26,7 @@ export class ExampleApp extends React.Component {

Here is an example image:

- +
); } @@ -30,6 +35,7 @@ export class ExampleApp extends React.Component { // is bound correctly. This form does not work with virtual/override inheritance, so use regular methods // everywhere else. private _onToggle = (sender: ToggleSwitch, args: IToggleEventArgs): void => { + // eslint-disable-next-line no-console console.log('Toggle switch changed: ' + args.sliderPosition); }; } diff --git a/build-tests-samples/heft-web-rig-app-tutorial/src/start.tsx b/build-tests-samples/heft-web-rig-app-tutorial/src/start.tsx index b17340dfd75..bfd75db4580 100644 --- a/build-tests-samples/heft-web-rig-app-tutorial/src/start.tsx +++ b/build-tests-samples/heft-web-rig-app-tutorial/src/start.tsx @@ -1,9 +1,12 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import * as React from 'react'; -import * as ReactDOM from 'react-dom'; +import * as ReactDOM from 'react-dom/client'; + import { ExampleApp } from './ExampleApp'; import './start.css'; -const rootDiv: HTMLElement = document.getElementById('root') as HTMLElement; -ReactDOM.render(, rootDiv); +const rootDiv: HTMLElement = document.getElementById('root')!; +ReactDOM.createRoot(rootDiv).render(); diff --git a/build-tests-samples/heft-web-rig-app-tutorial/tsconfig.json b/build-tests-samples/heft-web-rig-app-tutorial/tsconfig.json index bb41d01a056..94aca74325a 100644 --- a/build-tests-samples/heft-web-rig-app-tutorial/tsconfig.json +++ b/build-tests-samples/heft-web-rig-app-tutorial/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "./node_modules/@rushstack/heft-web-rig/profiles/app/tsconfig-base.json", "compilerOptions": { - "types": ["heft-jest", "webpack-env"] + "types": ["jest", "webpack-env"] } } diff --git a/build-tests-samples/heft-web-rig-library-tutorial/.eslintrc.js b/build-tests-samples/heft-web-rig-library-tutorial/.eslintrc.js deleted file mode 100644 index 288eaa16364..00000000000 --- a/build-tests-samples/heft-web-rig-library-tutorial/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/web-app', '@rushstack/eslint-config/mixins/react'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests-samples/heft-web-rig-library-tutorial/config/api-extractor.json b/build-tests-samples/heft-web-rig-library-tutorial/config/api-extractor.json index 8fcd66198e4..d09d72d73e6 100644 --- a/build-tests-samples/heft-web-rig-library-tutorial/config/api-extractor.json +++ b/build-tests-samples/heft-web-rig-library-tutorial/config/api-extractor.json @@ -8,6 +8,8 @@ * Optionally specifies another JSON config file that this file extends from. This provides a way for * standard settings to be shared across multiple projects. * + * To delete an inherited setting, set it to `null` in this file. + * * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be * resolved using NodeJS require(). @@ -38,7 +40,7 @@ * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor * analyzes the symbols exported by this module. * - * The file extension must be ".d.ts" and not ".ts". + * The file extension must be a declaration file (e.g. ".d.ts", ".d.mts", ".d.{extension}.ts"), not ".ts". * * The path is resolved relative to the folder of the config file that contains the setting; to change this, * prepend a folder token such as "". diff --git a/build-tests-samples/heft-web-rig-library-tutorial/config/jest.config.json b/build-tests-samples/heft-web-rig-library-tutorial/config/jest.config.json index 600ba9ea39a..105047ade63 100644 --- a/build-tests-samples/heft-web-rig-library-tutorial/config/jest.config.json +++ b/build-tests-samples/heft-web-rig-library-tutorial/config/jest.config.json @@ -1,3 +1,13 @@ { - "extends": "@rushstack/heft-web-rig/profiles/library/config/jest.config.json" + "extends": "@rushstack/heft-web-rig/profiles/library/config/jest.config.json", + + // These additional properties exist for caching purposes in the rushstack repo + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests-samples/heft-web-rig-library-tutorial/config/rush-project.json b/build-tests-samples/heft-web-rig-library-tutorial/config/rush-project.json new file mode 100644 index 00000000000..93c1e05a56f --- /dev/null +++ b/build-tests-samples/heft-web-rig-library-tutorial/config/rush-project.json @@ -0,0 +1,15 @@ +// This file exists for caching purposes in the rushstack repo +{ + "extends": "@rushstack/heft-web-rig/profiles/library/config/rush-project.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": [".heft", "release"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] + } + ] +} diff --git a/build-tests-samples/heft-web-rig-library-tutorial/eslint.config.js b/build-tests-samples/heft-web-rig-library-tutorial/eslint.config.js new file mode 100644 index 00000000000..e5eaf3c624a --- /dev/null +++ b/build-tests-samples/heft-web-rig-library-tutorial/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-eslint-config/flat/profile/web-app'); +const reactMixin = require('local-eslint-config/flat/mixins/react'); + +module.exports = [ + ...webAppProfile, + ...reactMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-samples/heft-web-rig-library-tutorial/package.json b/build-tests-samples/heft-web-rig-library-tutorial/package.json index d8c7fefdb0e..593026c0aa8 100644 --- a/build-tests-samples/heft-web-rig-library-tutorial/package.json +++ b/build-tests-samples/heft-web-rig-library-tutorial/package.json @@ -13,17 +13,17 @@ "_phase:test": "heft run --only test -- --clean" }, "dependencies": { - "react": "~16.13.1", - "react-dom": "~16.13.1", - "tslib": "~2.3.1" + "react": "~19.2.3", + "react-dom": "~19.2.3", + "tslib": "~2.8.1" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft-web-rig": "workspace:*", "@rushstack/heft": "workspace:*", - "@types/react-dom": "16.9.14", - "@types/react": "16.14.23", - "@types/webpack-env": "1.18.0", - "typescript": "~5.0.4" + "@types/react-dom": "19.2.3", + "@types/react": "19.2.7", + "@types/webpack-env": "1.18.8", + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*" } } diff --git a/build-tests-samples/heft-web-rig-library-tutorial/src/ToggleSwitch.tsx b/build-tests-samples/heft-web-rig-library-tutorial/src/ToggleSwitch.tsx index dc032d10ec6..3d2488fd556 100644 --- a/build-tests-samples/heft-web-rig-library-tutorial/src/ToggleSwitch.tsx +++ b/build-tests-samples/heft-web-rig-library-tutorial/src/ToggleSwitch.tsx @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import * as React from 'react'; /** diff --git a/build-tests-samples/heft-web-rig-library-tutorial/src/index.ts b/build-tests-samples/heft-web-rig-library-tutorial/src/index.ts index abd33f3ca71..1976762f2cb 100644 --- a/build-tests-samples/heft-web-rig-library-tutorial/src/index.ts +++ b/build-tests-samples/heft-web-rig-library-tutorial/src/index.ts @@ -1 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// eslint-disable-next-line no-restricted-syntax export * from './ToggleSwitch'; diff --git a/build-tests-samples/heft-web-rig-library-tutorial/tsconfig.json b/build-tests-samples/heft-web-rig-library-tutorial/tsconfig.json index bb41d01a056..94aca74325a 100644 --- a/build-tests-samples/heft-web-rig-library-tutorial/tsconfig.json +++ b/build-tests-samples/heft-web-rig-library-tutorial/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "./node_modules/@rushstack/heft-web-rig/profiles/app/tsconfig-base.json", "compilerOptions": { - "types": ["heft-jest", "webpack-env"] + "types": ["jest", "webpack-env"] } } diff --git a/build-tests-samples/heft-webpack-basic-tutorial/.eslintrc.js b/build-tests-samples/heft-webpack-basic-tutorial/.eslintrc.js deleted file mode 100644 index 288eaa16364..00000000000 --- a/build-tests-samples/heft-webpack-basic-tutorial/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/web-app', '@rushstack/eslint-config/mixins/react'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests-samples/heft-webpack-basic-tutorial/assets/index.html b/build-tests-samples/heft-webpack-basic-tutorial/assets/index.html index 3cfae745c19..9e89ef57d85 100644 --- a/build-tests-samples/heft-webpack-basic-tutorial/assets/index.html +++ b/build-tests-samples/heft-webpack-basic-tutorial/assets/index.html @@ -1,4 +1,4 @@ - + diff --git a/build-tests-samples/heft-webpack-basic-tutorial/config/heft.json b/build-tests-samples/heft-webpack-basic-tutorial/config/heft.json index f617ec08be0..b78b27272ac 100644 --- a/build-tests-samples/heft-webpack-basic-tutorial/config/heft.json +++ b/build-tests-samples/heft-webpack-basic-tutorial/config/heft.json @@ -2,12 +2,13 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }, { "sourcePath": "lib-commonjs" }], + "cleanFiles": [{ "includeGlobs": ["dist", "lib", "lib-commonjs"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests-samples/heft-webpack-basic-tutorial/config/jest.config.json b/build-tests-samples/heft-webpack-basic-tutorial/config/jest.config.json index 52635a8eb53..bfc5ce0d9b7 100644 --- a/build-tests-samples/heft-webpack-basic-tutorial/config/jest.config.json +++ b/build-tests-samples/heft-webpack-basic-tutorial/config/jest.config.json @@ -12,5 +12,15 @@ "!lib-commonjs/**/__tests__/**", "!lib-commonjs/**/__fixtures__/**", "!lib-commonjs/**/__mocks__/**" - ] + ], + + // These additional properties exist for caching purposes in the rushstack repo + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests-samples/heft-webpack-basic-tutorial/config/rush-project.json b/build-tests-samples/heft-webpack-basic-tutorial/config/rush-project.json index 247dc17187a..f25e963df0a 100644 --- a/build-tests-samples/heft-webpack-basic-tutorial/config/rush-project.json +++ b/build-tests-samples/heft-webpack-basic-tutorial/config/rush-project.json @@ -1,8 +1,15 @@ +// This file exists for caching purposes in the rushstack repo { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + "outputFolderNames": [".heft", "lib-esm", "lib-commonjs", "dist"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests-samples/heft-webpack-basic-tutorial/config/typescript.json b/build-tests-samples/heft-webpack-basic-tutorial/config/typescript.json index 49f4b4370e3..80efdd16510 100644 --- a/build-tests-samples/heft-webpack-basic-tutorial/config/typescript.json +++ b/build-tests-samples/heft-webpack-basic-tutorial/config/typescript.json @@ -2,7 +2,7 @@ * Configures the TypeScript plugin for Heft. This plugin also manages linting. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", /** * If provided, emit these module kinds in addition to the modules specified in the tsconfig. diff --git a/build-tests-samples/heft-webpack-basic-tutorial/eslint.config.js b/build-tests-samples/heft-webpack-basic-tutorial/eslint.config.js new file mode 100644 index 00000000000..e5eaf3c624a --- /dev/null +++ b/build-tests-samples/heft-webpack-basic-tutorial/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-eslint-config/flat/profile/web-app'); +const reactMixin = require('local-eslint-config/flat/mixins/react'); + +module.exports = [ + ...webAppProfile, + ...reactMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-samples/heft-webpack-basic-tutorial/package.json b/build-tests-samples/heft-webpack-basic-tutorial/package.json index 398ca9288a3..9935de8db91 100644 --- a/build-tests-samples/heft-webpack-basic-tutorial/package.json +++ b/build-tests-samples/heft-webpack-basic-tutorial/package.json @@ -10,27 +10,27 @@ "_phase:test": "heft run --only test -- --clean" }, "dependencies": { - "react-dom": "~16.13.1", - "react": "~16.13.1", - "tslib": "~2.3.1" + "react-dom": "~19.2.3", + "react": "~19.2.3", + "tslib": "~2.8.1" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", "@rushstack/heft-webpack5-plugin": "workspace:*", "@rushstack/heft": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/react-dom": "16.9.14", - "@types/react": "16.14.23", - "@types/webpack-env": "1.18.0", + "@types/jest": "30.0.0", + "@types/react": "19.2.7", + "@types/react-dom": "19.2.3", + "@types/webpack-env": "1.18.8", "css-loader": "~6.6.0", - "eslint": "~8.7.0", + "eslint": "~9.37.0", "html-webpack-plugin": "~5.5.0", + "local-eslint-config": "workspace:*", "source-map-loader": "~3.0.1", "style-loader": "~3.3.1", - "typescript": "~5.0.4", - "webpack": "~5.80.0" + "typescript": "~5.8.2", + "webpack": "~5.105.2" } } diff --git a/build-tests-samples/heft-webpack-basic-tutorial/src/ExampleApp.tsx b/build-tests-samples/heft-webpack-basic-tutorial/src/ExampleApp.tsx index 5e5447e2490..d81154eaefe 100644 --- a/build-tests-samples/heft-webpack-basic-tutorial/src/ExampleApp.tsx +++ b/build-tests-samples/heft-webpack-basic-tutorial/src/ExampleApp.tsx @@ -1,5 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import * as React from 'react'; -import { ToggleSwitch, IToggleEventArgs } from './ToggleSwitch'; + +import { ToggleSwitch, type IToggleEventArgs } from './ToggleSwitch'; /** * This React component renders the application page. @@ -28,6 +32,7 @@ export class ExampleApp extends React.Component { // is bound correctly. This form does not work with virtual/override inheritance, so use regular methods // everywhere else. private _onToggle = (sender: ToggleSwitch, args: IToggleEventArgs): void => { + // eslint-disable-next-line no-console console.log('Toggle switch changed: ' + args.sliderPosition); }; } diff --git a/build-tests-samples/heft-webpack-basic-tutorial/src/ToggleSwitch.tsx b/build-tests-samples/heft-webpack-basic-tutorial/src/ToggleSwitch.tsx index c4ac05fcde1..79e43aad327 100644 --- a/build-tests-samples/heft-webpack-basic-tutorial/src/ToggleSwitch.tsx +++ b/build-tests-samples/heft-webpack-basic-tutorial/src/ToggleSwitch.tsx @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import * as React from 'react'; /** diff --git a/build-tests-samples/heft-webpack-basic-tutorial/src/index.tsx b/build-tests-samples/heft-webpack-basic-tutorial/src/index.tsx index bfa20faf63e..eb3afe5d1ee 100644 --- a/build-tests-samples/heft-webpack-basic-tutorial/src/index.tsx +++ b/build-tests-samples/heft-webpack-basic-tutorial/src/index.tsx @@ -1,9 +1,12 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import * as React from 'react'; -import * as ReactDOM from 'react-dom'; +import * as ReactDOM from 'react-dom/client'; + import { ExampleApp } from './ExampleApp'; import './index.css'; -const rootDiv: HTMLElement = document.getElementById('root') as HTMLElement; -ReactDOM.render(, rootDiv); +const rootDiv: HTMLElement = document.getElementById('root')!; +ReactDOM.createRoot(rootDiv).render(); diff --git a/build-tests-samples/heft-webpack-basic-tutorial/tsconfig.json b/build-tests-samples/heft-webpack-basic-tutorial/tsconfig.json index 4d703032a26..80dbef20d99 100644 --- a/build-tests-samples/heft-webpack-basic-tutorial/tsconfig.json +++ b/build-tests-samples/heft-webpack-basic-tutorial/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-esm", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -19,13 +19,20 @@ "allowUnreachableCode": false, "importHelpers": true, - "types": ["heft-jest", "webpack-env"], + "types": ["jest", "webpack-env"], "module": "esnext", "moduleResolution": "node", "target": "es5", - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"] + "lib": [ + "es5", + "scripthost", + "es2015.collection", + "es2015.promise", + "es2015.iterable", + "es2015.symbol.wellknown", + "dom" + ] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests-samples/heft-webpack-basic-tutorial/webpack.config.js b/build-tests-samples/heft-webpack-basic-tutorial/webpack.config.js index c6a4c30ea7d..8df73cadf1a 100644 --- a/build-tests-samples/heft-webpack-basic-tutorial/webpack.config.js +++ b/build-tests-samples/heft-webpack-basic-tutorial/webpack.config.js @@ -12,7 +12,7 @@ function createWebpackConfig({ production }) { // Documentation: https://webpack.js.org/configuration/mode/ mode: production ? 'production' : 'development', resolve: { - extensions: ['.js', '.jsx', '.json'] + extensions: ['.js', '.json'] }, module: { rules: [ @@ -28,7 +28,7 @@ function createWebpackConfig({ production }) { ] }, entry: { - app: path.join(__dirname, 'lib', 'index.js'), + app: path.join(__dirname, 'lib-esm', 'index.js'), // Put these libraries in a separate vendor bundle vendor: ['react', 'react-dom'] diff --git a/build-tests-samples/packlets-tutorial/config/heft.json b/build-tests-samples/packlets-tutorial/config/heft.json index a3806a1005b..694f8935bb1 100644 --- a/build-tests-samples/packlets-tutorial/config/heft.json +++ b/build-tests-samples/packlets-tutorial/config/heft.json @@ -2,12 +2,13 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }], + "cleanFiles": [{ "includeGlobs": ["dist", "lib"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests-samples/packlets-tutorial/config/rush-project.json b/build-tests-samples/packlets-tutorial/config/rush-project.json index 247dc17187a..ad4743f873c 100644 --- a/build-tests-samples/packlets-tutorial/config/rush-project.json +++ b/build-tests-samples/packlets-tutorial/config/rush-project.json @@ -1,8 +1,11 @@ +// This file exists for caching purposes in the rushstack repo { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + "outputFolderNames": [".heft", "lib-commonjs", "dist"] } ] } diff --git a/build-tests-samples/packlets-tutorial/package.json b/build-tests-samples/packlets-tutorial/package.json index abedcccc3c3..3aa2baa2241 100644 --- a/build-tests-samples/packlets-tutorial/package.json +++ b/build-tests-samples/packlets-tutorial/package.json @@ -14,8 +14,8 @@ "@rushstack/heft": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", - "@types/node": "14.18.36", - "eslint": "~8.7.0", - "typescript": "~5.0.4" + "@types/node": "20.17.19", + "eslint": "~8.57.0", + "typescript": "~5.8.2" } } diff --git a/build-tests-samples/packlets-tutorial/tsconfig.json b/build-tests-samples/packlets-tutorial/tsconfig.json index 3a994cbd1c2..8968fb092bd 100644 --- a/build-tests-samples/packlets-tutorial/tsconfig.json +++ b/build-tests-samples/packlets-tutorial/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -24,6 +24,5 @@ "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests-subspace/rush-lib-test/config/rig.json b/build-tests-subspace/rush-lib-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests-subspace/rush-lib-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests-subspace/rush-lib-test/eslint.config.js b/build-tests-subspace/rush-lib-test/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/build-tests-subspace/rush-lib-test/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-subspace/rush-lib-test/package.json b/build-tests-subspace/rush-lib-test/package.json new file mode 100644 index 00000000000..13104c9f069 --- /dev/null +++ b/build-tests-subspace/rush-lib-test/package.json @@ -0,0 +1,51 @@ +{ + "name": "rush-lib-test", + "version": "0.0.0", + "private": true, + "description": "A minimal example project that imports APIs from @rushstack/rush-lib", + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "start": "node lib/start.js", + "_phase:build": "heft run --only build -- --clean" + }, + "dependencies": { + "@microsoft/rush-lib": "workspace:*", + "@rushstack/terminal": "workspace:*" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "@types/node": "20.17.19", + "eslint": "~9.25.1", + "local-node-rig": "workspace:*" + }, + "dependenciesMeta": { + "@microsoft/rush-lib": { + "injected": true + }, + "@rushstack/terminal": { + "injected": true + }, + "@rushstack/heft": { + "injected": true + }, + "local-node-rig": { + "injected": true + } + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + } +} diff --git a/build-tests-subspace/rush-lib-test/src/start.ts b/build-tests-subspace/rush-lib-test/src/start.ts new file mode 100644 index 00000000000..a389623e6e4 --- /dev/null +++ b/build-tests-subspace/rush-lib-test/src/start.ts @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/* eslint-disable no-console */ + +console.log('rush-lib-test loading Rush configuration...'); + +// Important: Since we're calling an internal API, we need to use the unbundled .d.ts files +// instead of the normal .d.ts rollup +// eslint-disable-next-line import/order +import { RushConfiguration } from '@microsoft/rush-lib/lib/index'; + +const config: RushConfiguration = RushConfiguration.loadFromDefaultLocation(); +console.log(config.commonFolder); + +console.log('Calling an internal API...'); + +// Use a path-based import to access an internal API (do so at your own risk!) +import { VersionMismatchFinder } from '@microsoft/rush-lib/lib/logic/versionMismatch/VersionMismatchFinder'; +import { ConsoleTerminalProvider, Terminal } from '@rushstack/terminal'; + +const terminal: Terminal = new Terminal(new ConsoleTerminalProvider()); +VersionMismatchFinder.ensureConsistentVersions(config, terminal); + +console.log(new ConsoleTerminalProvider().supportsColor); diff --git a/build-tests-subspace/rush-lib-test/tsconfig.json b/build-tests-subspace/rush-lib-test/tsconfig.json new file mode 100644 index 00000000000..c053aa1cd89 --- /dev/null +++ b/build-tests-subspace/rush-lib-test/tsconfig.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib-commonjs", + "declarationDir": "lib-dts", + "rootDir": "src", + + "module": "commonjs", + "target": "es2017", + "lib": ["es2017"], + + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictNullChecks": true, + "noUnusedLocals": true, + "types": ["node"] + }, + + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/build-tests-subspace/rush-sdk-test/config/heft.json b/build-tests-subspace/rush-sdk-test/config/heft.json new file mode 100644 index 00000000000..9160e07413f --- /dev/null +++ b/build-tests-subspace/rush-sdk-test/config/heft.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "run-start": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "run-script-plugin", + "options": { + "scriptPath": "./lib-commonjs/run-start.js" + } + } + } + } + } + } +} diff --git a/build-tests-subspace/rush-sdk-test/config/rig.json b/build-tests-subspace/rush-sdk-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests-subspace/rush-sdk-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests-subspace/rush-sdk-test/eslint.config.js b/build-tests-subspace/rush-sdk-test/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/build-tests-subspace/rush-sdk-test/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-subspace/rush-sdk-test/package.json b/build-tests-subspace/rush-sdk-test/package.json new file mode 100644 index 00000000000..1b1197952cc --- /dev/null +++ b/build-tests-subspace/rush-sdk-test/package.json @@ -0,0 +1,51 @@ +{ + "name": "rush-sdk-test", + "version": "0.0.0", + "private": true, + "description": "A minimal example project that imports APIs from @rushstack/rush-sdk", + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "start": "node lib/start.js", + "_phase:build": "heft run --only build -- --clean" + }, + "dependencies": { + "@rushstack/rush-sdk": "workspace:*" + }, + "devDependencies": { + "@microsoft/rush-lib": "workspace:*", + "@rushstack/heft": "workspace:*", + "@types/node": "20.17.19", + "eslint": "~9.25.1", + "local-node-rig": "workspace:*" + }, + "dependenciesMeta": { + "@microsoft/rush-lib": { + "injected": true + }, + "@rushstack/rush-sdk": { + "injected": true + }, + "@rushstack/heft": { + "injected": true + }, + "local-node-rig": { + "injected": true + } + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + } +} diff --git a/build-tests-subspace/rush-sdk-test/src/run-start.ts b/build-tests-subspace/rush-sdk-test/src/run-start.ts new file mode 100644 index 00000000000..a5fcb483473 --- /dev/null +++ b/build-tests-subspace/rush-sdk-test/src/run-start.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export async function runAsync(): Promise { + await import('./start.js'); +} diff --git a/build-tests-subspace/rush-sdk-test/src/start.ts b/build-tests-subspace/rush-sdk-test/src/start.ts new file mode 100644 index 00000000000..92517a312f9 --- /dev/null +++ b/build-tests-subspace/rush-sdk-test/src/start.ts @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/* eslint-disable no-console */ + +console.log('rush-sdk-test loading Rush configuration...'); + +// Important: Since we're calling an internal API, we need to use the unbundled .d.ts files +// instead of the normal .d.ts rollup +// eslint-disable-next-line import/order +import { RushConfiguration } from '@rushstack/rush-sdk/lib/index'; + +const config: RushConfiguration = RushConfiguration.loadFromDefaultLocation(); +console.log(config.commonFolder); + +console.log('Calling an internal API...'); + +// Use a path-based import to access an internal API (do so at your own risk!) +import * as GitEmailPolicy from '@rushstack/rush-sdk/lib/logic/policy/GitEmailPolicy'; +console.log(GitEmailPolicy.getEmailExampleLines(config)); diff --git a/build-tests-subspace/rush-sdk-test/tsconfig.json b/build-tests-subspace/rush-sdk-test/tsconfig.json new file mode 100644 index 00000000000..7c6800ed98c --- /dev/null +++ b/build-tests-subspace/rush-sdk-test/tsconfig.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib-commonjs", + "rootDir": "src", + + "module": "commonjs", + "target": "es2017", + "lib": ["es2017"], + + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictNullChecks": true, + "noUnusedLocals": true, + "types": ["node"], + "skipLibCheck": true // There are issues with importing rush-sdk as an injected dependency + }, + + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/build-tests-subspace/typescript-newest-test/config/rig.json b/build-tests-subspace/typescript-newest-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests-subspace/typescript-newest-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests-subspace/typescript-newest-test/eslint.config.js b/build-tests-subspace/typescript-newest-test/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/build-tests-subspace/typescript-newest-test/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-subspace/typescript-newest-test/package.json b/build-tests-subspace/typescript-newest-test/package.json new file mode 100644 index 00000000000..42455e7ed18 --- /dev/null +++ b/build-tests-subspace/typescript-newest-test/package.json @@ -0,0 +1,48 @@ +{ + "name": "typescript-newest-test", + "description": "Building this project tests Heft with the newest supported TypeScript compiler version", + "version": "1.0.0", + "private": true, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "eslint": "~9.25.1", + "local-node-rig": "workspace:*", + "typescript": "~5.8.2" + }, + "dependenciesMeta": { + "@rushstack/heft": { + "injected": true + }, + "local-node-rig": { + "injected": true + } + } +} diff --git a/build-tests-subspace/typescript-newest-test/src/index.ts b/build-tests-subspace/typescript-newest-test/src/index.ts new file mode 100644 index 00000000000..659610ef84f --- /dev/null +++ b/build-tests-subspace/typescript-newest-test/src/index.ts @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * @public + */ +export class TestClass {} diff --git a/build-tests-subspace/typescript-newest-test/tsconfig.json b/build-tests-subspace/typescript-newest-test/tsconfig.json new file mode 100644 index 00000000000..8958c41ded6 --- /dev/null +++ b/build-tests-subspace/typescript-newest-test/tsconfig.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib-commonjs", + "declarationDir": "lib-dts", + "rootDir": "src", + + "module": "commonjs", + "target": "es2017", + "lib": ["es2017"], + + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictNullChecks": true, + "noUnusedLocals": true, + "types": [] + }, + + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/build-tests-subspace/typescript-v4-test/config/heft.json b/build-tests-subspace/typescript-v4-test/config/heft.json new file mode 100644 index 00000000000..0413fcc7fec --- /dev/null +++ b/build-tests-subspace/typescript-v4-test/config/heft.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "phasesByName": { + "build": { + "cleanFiles": [{ "includeGlobs": ["dist", "lib", "temp"] }], + + "tasksByName": { + "typescript": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-typescript-plugin" + } + }, + "lint": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-lint-plugin" + } + } + } + } + } +} diff --git a/build-tests-subspace/typescript-v4-test/config/rush-project.json b/build-tests-subspace/typescript-v4-test/config/rush-project.json new file mode 100644 index 00000000000..d8e232986d3 --- /dev/null +++ b/build-tests-subspace/typescript-v4-test/config/rush-project.json @@ -0,0 +1,8 @@ +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["lib-commonjs", "dist"] + } + ] +} diff --git a/build-tests-subspace/typescript-v4-test/eslint.config.js b/build-tests-subspace/typescript-v4-test/eslint.config.js new file mode 100644 index 00000000000..20dead69438 --- /dev/null +++ b/build-tests-subspace/typescript-v4-test/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('@rushstack/eslint-config/flat/profile/node'); + +module.exports = [ + ...nodeProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests-subspace/typescript-v4-test/package.json b/build-tests-subspace/typescript-v4-test/package.json new file mode 100644 index 00000000000..0da7a11deb1 --- /dev/null +++ b/build-tests-subspace/typescript-v4-test/package.json @@ -0,0 +1,35 @@ +{ + "name": "typescript-v4-test", + "description": "Building this project tests Heft with TypeScript v4", + "version": "1.0.0", + "private": true, + "main": "./lib/index.js", + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" + }, + "devDependencies": { + "@rushstack/eslint-config": "workspace:*", + "@rushstack/heft": "workspace:*", + "@rushstack/heft-lint-plugin": "workspace:*", + "@rushstack/heft-typescript-plugin": "workspace:*", + "typescript": "~4.9.5", + "tslint": "~5.20.1", + "eslint": "~9.25.1" + }, + "dependenciesMeta": { + "@rushstack/eslint-config": { + "injected": true + }, + "@rushstack/heft": { + "injected": true + }, + "@rushstack/heft-lint-plugin": { + "injected": true + }, + "@rushstack/heft-typescript-plugin": { + "injected": true + } + } +} diff --git a/build-tests-subspace/typescript-v4-test/src/index.ts b/build-tests-subspace/typescript-v4-test/src/index.ts new file mode 100644 index 00000000000..659610ef84f --- /dev/null +++ b/build-tests-subspace/typescript-v4-test/src/index.ts @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * @public + */ +export class TestClass {} diff --git a/build-tests-subspace/typescript-v4-test/tsconfig.json b/build-tests-subspace/typescript-v4-test/tsconfig.json new file mode 100644 index 00000000000..b4a2bb819a1 --- /dev/null +++ b/build-tests-subspace/typescript-v4-test/tsconfig.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib-commonjs", + "rootDir": "src", + + "module": "commonjs", + "target": "es2017", + "lib": ["es2017"], + + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictNullChecks": true, + "noUnusedLocals": true, + "types": [] + }, + + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/build-tests/install-test-workspace/workspace/typescript-newest-test/tslint.json b/build-tests-subspace/typescript-v4-test/tslint.json similarity index 100% rename from build-tests/install-test-workspace/workspace/typescript-newest-test/tslint.json rename to build-tests-subspace/typescript-v4-test/tslint.json diff --git a/build-tests/api-documenter-scenarios/.vscode/launch.json b/build-tests/api-documenter-scenarios/.vscode/launch.json new file mode 100644 index 00000000000..912e1303501 --- /dev/null +++ b/build-tests/api-documenter-scenarios/.vscode/launch.json @@ -0,0 +1,34 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "heft build (runScenarios.js)", + "program": "${workspaceFolder}/node_modules/@rushstack/heft/lib/start.js", + "cwd": "${workspaceFolder}", + "args": ["--debug", "build"], + "console": "integratedTerminal", + "sourceMaps": true + }, + { + "type": "node", + "request": "launch", + "name": "api-documenter [scenario]", + "program": "${workspaceFolder}/node_modules/@microsoft/api-documenter/lib/start.js", + "cwd": "${workspaceFolder}/temp/configs", + "args": [ + "generate", + "--input-folder", + "${workspaceFolder}/temp/etc/commentedDestructure", + "--output-folder", + "${workspaceFolder}/temp/etc/commentedDestructure/markdown" + ], + "console": "integratedTerminal", + "sourceMaps": true + } + ] +} diff --git a/build-tests/api-documenter-scenarios/build.js b/build-tests/api-documenter-scenarios/build.js deleted file mode 100644 index 04eec231385..00000000000 --- a/build-tests/api-documenter-scenarios/build.js +++ /dev/null @@ -1,27 +0,0 @@ -const fsx = require('fs-extra'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -console.log(); - -// Clean the old build outputs -fsx.emptyDirSync('dist'); -fsx.emptyDirSync('lib'); -fsx.emptyDirSync('temp'); -fsx.emptyDirSync('etc'); - -// Run the TypeScript compiler -executeCommand('node node_modules/typescript/lib/tsc'); - -// Run the scenario runner -require('./lib/runScenarios').runScenarios('./config/build-config.json'); - -console.log(); -console.log(`==> Finished build.js for ${path.basename(process.cwd())}`); diff --git a/build-tests/api-documenter-scenarios/config/build-config.json b/build-tests/api-documenter-scenarios/config/build-config.json deleted file mode 100644 index d6bb035b7cd..00000000000 --- a/build-tests/api-documenter-scenarios/config/build-config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "scenarioFolderNames": ["inheritedMembers"] -} diff --git a/build-tests/api-documenter-scenarios/config/heft.json b/build-tests/api-documenter-scenarios/config/heft.json new file mode 100644 index 00000000000..6aac1a2c858 --- /dev/null +++ b/build-tests/api-documenter-scenarios/config/heft.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "run-scenarios": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "run-script-plugin", + "options": { + "scriptPath": "./lib-commonjs/runScenarios.js" + } + } + } + } + } + } +} diff --git a/build-tests/api-documenter-scenarios/config/rig.json b/build-tests/api-documenter-scenarios/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-documenter-scenarios/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-documenter-scenarios/config/rush-project.json b/build-tests/api-documenter-scenarios/config/rush-project.json deleted file mode 100644 index 247dc17187a..00000000000 --- a/build-tests/api-documenter-scenarios/config/rush-project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "operationSettings": [ - { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] - } - ] -} diff --git a/build-tests/api-documenter-scenarios/config/typescript.json b/build-tests/api-documenter-scenarios/config/typescript.json new file mode 100644 index 00000000000..a49fb3a3931 --- /dev/null +++ b/build-tests/api-documenter-scenarios/config/typescript.json @@ -0,0 +1,7 @@ +{ + "extends": "local-node-rig/profiles/default/config/typescript.json", + + "staticAssetsToCopy": { + "fileExtensions": [".json", ".d.ts"] + } +} diff --git a/build-tests/api-documenter-scenarios/eslint.config.js b/build-tests/api-documenter-scenarios/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/build-tests/api-documenter-scenarios/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/api-documenter-scenarios.api.json b/build-tests/api-documenter-scenarios/etc/inheritedMembers/api-documenter-scenarios.api.json index 372fac3f034..58f0d98c22b 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/api-documenter-scenarios.api.json +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/api-documenter-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class1.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class1.md index 40286ceedb2..1bfaa4f7169 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class1.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class1.md @@ -14,17 +14,156 @@ export declare class Class1 extends Class2 ## Properties -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [fourthProp](./api-documenter-scenarios.class1.fourthprop.md) | | number | A fourth prop | -| [secondProp](./api-documenter-scenarios.class1.secondprop.md) | | boolean | A second prop. Overrides Class2.secondProp. | -| [someProp](./api-documenter-scenarios.namespace1.class3.someprop.md) | | number |

Some prop.

(Inherited from [Class3](./api-documenter-scenarios.namespace1.class3.md))

| -| [thirdProp](./api-documenter-scenarios.class2.thirdprop.md) | | T |

A third prop.

(Inherited from [Class2](./api-documenter-scenarios.class2.md))

| + + + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[fourthProp](./api-documenter-scenarios.class1.fourthprop.md) + + + + + + + +number + + + + +A fourth prop + + +
+ +[secondProp](./api-documenter-scenarios.class1.secondprop.md) + + + + + + + +boolean + + + + +A second prop. Overrides `Class2.secondProp`. + + +
+ +[someProp](./api-documenter-scenarios.namespace1.class3.someprop.md) + + + + + + + +number + + + + +Some prop. + +(Inherited from [Class3](./api-documenter-scenarios.namespace1.class3.md)) + + +
+ +[thirdProp](./api-documenter-scenarios.class2.thirdprop.md) + + + + + + + +T + + + + +A third prop. + +(Inherited from [Class2](./api-documenter-scenarios.class2.md)) + + +
## Methods -| Method | Modifiers | Description | -| --- | --- | --- | -| [someMethod(x)](./api-documenter-scenarios.class2.somemethod.md) | |

Some method. Overrides Class3.someMethod.

(Inherited from [Class2](./api-documenter-scenarios.class2.md))

| -| [someOverload(x)](./api-documenter-scenarios.class1.someoverload.md) | | Some overload. Overrides Class3.someOverload. | + + + +
+ +Method + + + + +Modifiers + + + + +Description + + +
+ +[someMethod(x)](./api-documenter-scenarios.class2.somemethod.md) + + + + + + + +Some method. Overrides `Class3.someMethod`. + +(Inherited from [Class2](./api-documenter-scenarios.class2.md)) + + +
+ +[someOverload(x)](./api-documenter-scenarios.class1.someoverload.md) + + + + + + + +Some overload. Overrides `Class3.someOverload`. + + +
diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class1.someoverload.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class1.someoverload.md index 43cd1dcb975..6b6cd8d0a07 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class1.someoverload.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class1.someoverload.md @@ -14,9 +14,37 @@ someOverload(x: boolean | string): void; ## Parameters -| Parameter | Type | Description | -| --- | --- | --- | -| x | boolean \| string | | + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +x + + + + +boolean \| string + + + + + +
**Returns:** diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class2.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class2.md index 64d21d6aaf7..b952855673a 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class2.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class2.md @@ -14,17 +14,151 @@ export declare class Class2 extends Namespace1.Class3 ## Properties -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [secondProp](./api-documenter-scenarios.class2.secondprop.md) | | boolean \| string | A second prop. | -| [someProp](./api-documenter-scenarios.namespace1.class3.someprop.md) | | number |

Some prop.

(Inherited from [Class3](./api-documenter-scenarios.namespace1.class3.md))

| -| [thirdProp](./api-documenter-scenarios.class2.thirdprop.md) | | T | A third prop. | + + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[secondProp](./api-documenter-scenarios.class2.secondprop.md) + + + + + + + +boolean \| string + + + + +A second prop. + + +
+ +[someProp](./api-documenter-scenarios.namespace1.class3.someprop.md) + + + + + + + +number + + + + +Some prop. + +(Inherited from [Class3](./api-documenter-scenarios.namespace1.class3.md)) + + +
+ +[thirdProp](./api-documenter-scenarios.class2.thirdprop.md) + + + + + + + +T + + + + +A third prop. + + +
## Methods -| Method | Modifiers | Description | -| --- | --- | --- | -| [someMethod(x)](./api-documenter-scenarios.class2.somemethod.md) | | Some method. Overrides Class3.someMethod. | -| [someOverload(x)](./api-documenter-scenarios.namespace1.class3.someoverload.md) | |

Some overload.

(Inherited from [Class3](./api-documenter-scenarios.namespace1.class3.md))

| -| [someOverload(x)](./api-documenter-scenarios.namespace1.class3.someoverload_1.md) | |

Some overload.

(Inherited from [Class3](./api-documenter-scenarios.namespace1.class3.md))

| + + + + +
+ +Method + + + + +Modifiers + + + + +Description + + +
+ +[someMethod(x)](./api-documenter-scenarios.class2.somemethod.md) + + + + + + + +Some method. Overrides `Class3.someMethod`. + + +
+ +[someOverload(x)](./api-documenter-scenarios.namespace1.class3.someoverload.md) + + + + + + + +Some overload. + +(Inherited from [Class3](./api-documenter-scenarios.namespace1.class3.md)) + + +
+ +[someOverload(x)](./api-documenter-scenarios.namespace1.class3.someoverload_1.md) + + + + + + + +Some overload. + +(Inherited from [Class3](./api-documenter-scenarios.namespace1.class3.md)) + + +
diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class2.somemethod.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class2.somemethod.md index 804fa09c005..ddc7fe613a0 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class2.somemethod.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.class2.somemethod.md @@ -14,9 +14,37 @@ someMethod(x: boolean): void; ## Parameters -| Parameter | Type | Description | -| --- | --- | --- | -| x | boolean | | + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +x + + + + +boolean + + + + + +
**Returns:** diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iextendsinterfaceliketypealias.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iextendsinterfaceliketypealias.md index d7b01f76490..e8f6984d281 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iextendsinterfaceliketypealias.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iextendsinterfaceliketypealias.md @@ -17,8 +17,68 @@ _(Some inherited members may not be shown because they are not represented in th ## Properties -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [secondProp](./api-documenter-scenarios.iinterface1.secondprop.md) | | boolean \| string |

A second prop.

(Inherited from [IInterface1](./api-documenter-scenarios.iinterface1.md))

| -| [someProp](./api-documenter-scenarios.iinterface1.someprop.md) | | number |

Some prop.

(Inherited from [IInterface1](./api-documenter-scenarios.iinterface1.md))

| + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[secondProp](./api-documenter-scenarios.iinterface1.secondprop.md) + + + + + + + +boolean \| string + + + + +A second prop. + +(Inherited from [IInterface1](./api-documenter-scenarios.iinterface1.md)) + + +
+ +[someProp](./api-documenter-scenarios.iinterface1.someprop.md) + + + + + + + +number + + + + +Some prop. + +(Inherited from [IInterface1](./api-documenter-scenarios.iinterface1.md)) + + +
diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iextendsmultipleinterfaces.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iextendsmultipleinterfaces.md index 100c750a17c..5192a7a9df0 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iextendsmultipleinterfaces.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iextendsmultipleinterfaces.md @@ -15,9 +15,85 @@ export interface IExtendsMultipleInterfaces extends IInterface1, IInterface2 ## Properties -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [secondProp](./api-documenter-scenarios.iextendsmultipleinterfaces.secondprop.md) | | boolean | A second prop. Overrides IInterface1.someProp. | -| [someProp](./api-documenter-scenarios.iinterface1.someprop.md) | | number |

Some prop.

(Inherited from [IInterface1](./api-documenter-scenarios.iinterface1.md))

| -| [thirdProp](./api-documenter-scenarios.iextendsmultipleinterfaces.thirdprop.md) | | string | A third prop. | + + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[secondProp](./api-documenter-scenarios.iextendsmultipleinterfaces.secondprop.md) + + + + + + + +boolean + + + + +A second prop. Overrides `IInterface1.someProp`. + + +
+ +[someProp](./api-documenter-scenarios.iinterface1.someprop.md) + + + + + + + +number + + + + +Some prop. + +(Inherited from [IInterface1](./api-documenter-scenarios.iinterface1.md)) + + +
+ +[thirdProp](./api-documenter-scenarios.iextendsmultipleinterfaces.thirdprop.md) + + + + + + + +string + + + + +A third prop. + + +
diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iinterface1.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iinterface1.md index f3fa0933e54..015a51957b5 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iinterface1.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iinterface1.md @@ -13,8 +13,64 @@ export interface IInterface1 ## Properties -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [secondProp](./api-documenter-scenarios.iinterface1.secondprop.md) | | boolean \| string | A second prop. | -| [someProp](./api-documenter-scenarios.iinterface1.someprop.md) | | number | Some prop. | + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[secondProp](./api-documenter-scenarios.iinterface1.secondprop.md) + + + + + + + +boolean \| string + + + + +A second prop. + + +
+ +[someProp](./api-documenter-scenarios.iinterface1.someprop.md) + + + + + + + +number + + + + +Some prop. + + +
diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iinterface2.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iinterface2.md index 32a50b5ce2c..f3c7c0e45da 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iinterface2.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.iinterface2.md @@ -13,7 +13,45 @@ export interface IInterface2 ## Properties -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [someProp](./api-documenter-scenarios.iinterface2.someprop.md) | | number | Some prop. | + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[someProp](./api-documenter-scenarios.iinterface2.someprop.md) + + + + + + + +number + + + + +Some prop. + + +
diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.md index 32669c2e697..30d4ba70736 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.md @@ -6,39 +6,214 @@ ## Classes -| Class | Description | -| --- | --- | -| [Class1](./api-documenter-scenarios.class1.md) | | -| [Class2](./api-documenter-scenarios.class2.md) | | -| [ExtendsAnonymousClass](./api-documenter-scenarios.extendsanonymousclass.md) | Some class that extends an anonymous class. | -| [ExtendsClassFromAnotherPackage](./api-documenter-scenarios.extendsclassfromanotherpackage.md) | Some class that extends a class from another package. This base class is not in any API doc model. | -| [ExtendsClassLikeVariable](./api-documenter-scenarios.extendsclasslikevariable.md) | Some class that extends a class-like variable. | -| [ExtendsUnexportedClass](./api-documenter-scenarios.extendsunexportedclass.md) | Some class that extends an unexported class. | + + + + + + + +
+ +Class + + + + +Description + + +
+ +[Class1](./api-documenter-scenarios.class1.md) + + + + + + +
+ +[Class2](./api-documenter-scenarios.class2.md) + + + + + + +
+ +[ExtendsAnonymousClass](./api-documenter-scenarios.extendsanonymousclass.md) + + + + +Some class that extends an anonymous class. + + +
+ +[ExtendsClassFromAnotherPackage](./api-documenter-scenarios.extendsclassfromanotherpackage.md) + + + + +Some class that extends a class from another package. This base class is not in any API doc model. + + +
+ +[ExtendsClassLikeVariable](./api-documenter-scenarios.extendsclasslikevariable.md) + + + + +Some class that extends a class-like variable. + + +
+ +[ExtendsUnexportedClass](./api-documenter-scenarios.extendsunexportedclass.md) + + + + +Some class that extends an unexported class. + + +
## Interfaces -| Interface | Description | -| --- | --- | -| [IExtendsInterfaceLikeTypeAlias](./api-documenter-scenarios.iextendsinterfaceliketypealias.md) | Some interface that extends an interface-like type alias as well as another interface. | -| [IExtendsMultipleInterfaces](./api-documenter-scenarios.iextendsmultipleinterfaces.md) | Some interface that extends multiple interfaces. | -| [IInterface1](./api-documenter-scenarios.iinterface1.md) | | -| [IInterface2](./api-documenter-scenarios.iinterface2.md) | | + + + + + +
+ +Interface + + + + +Description + + +
+ +[IExtendsInterfaceLikeTypeAlias](./api-documenter-scenarios.iextendsinterfaceliketypealias.md) + + + + +Some interface that extends an interface-like type alias as well as another interface. + + +
+ +[IExtendsMultipleInterfaces](./api-documenter-scenarios.iextendsmultipleinterfaces.md) + + + + +Some interface that extends multiple interfaces. + + +
+ +[IInterface1](./api-documenter-scenarios.iinterface1.md) + + + + + + +
+ +[IInterface2](./api-documenter-scenarios.iinterface2.md) + + + + + + +
## Namespaces -| Namespace | Description | -| --- | --- | -| [Namespace1](./api-documenter-scenarios.namespace1.md) | | + + +
+ +Namespace + + + + +Description + + +
+ +[Namespace1](./api-documenter-scenarios.namespace1.md) + + + + + + +
## Variables -| Variable | Description | -| --- | --- | -| [ClassLikeVariable](./api-documenter-scenarios.classlikevariable.md) | Some class-like variable. | + + +
+ +Variable + + + + +Description + + +
+ +[ClassLikeVariable](./api-documenter-scenarios.classlikevariable.md) + + + + +Some class-like variable. + + +
## Type Aliases -| Type Alias | Description | -| --- | --- | -| [IInterfaceLikeTypeAlias](./api-documenter-scenarios.iinterfaceliketypealias.md) | Some interface-like type alias. | + + +
+ +Type Alias + + + + +Description + + +
+ +[IInterfaceLikeTypeAlias](./api-documenter-scenarios.iinterfaceliketypealias.md) + + + + +Some interface-like type alias. + + +
diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.md index 8167ea6c94f..923ef2d33ea 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.md @@ -13,15 +13,107 @@ class Class3 ## Properties -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [someProp](./api-documenter-scenarios.namespace1.class3.someprop.md) | | number | Some prop. | + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[someProp](./api-documenter-scenarios.namespace1.class3.someprop.md) + + + + + + + +number + + + + +Some prop. + + +
## Methods -| Method | Modifiers | Description | -| --- | --- | --- | -| [someMethod(x)](./api-documenter-scenarios.namespace1.class3.somemethod.md) | | Some method. | -| [someOverload(x)](./api-documenter-scenarios.namespace1.class3.someoverload.md) | | Some overload. | -| [someOverload(x)](./api-documenter-scenarios.namespace1.class3.someoverload_1.md) | | Some overload. | + + + + +
+ +Method + + + + +Modifiers + + + + +Description + + +
+ +[someMethod(x)](./api-documenter-scenarios.namespace1.class3.somemethod.md) + + + + + + + +Some method. + + +
+ +[someOverload(x)](./api-documenter-scenarios.namespace1.class3.someoverload.md) + + + + + + + +Some overload. + + +
+ +[someOverload(x)](./api-documenter-scenarios.namespace1.class3.someoverload_1.md) + + + + + + + +Some overload. + + +
diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.somemethod.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.somemethod.md index 4e729cc2d18..fe6ce4e2c5b 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.somemethod.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.somemethod.md @@ -14,9 +14,37 @@ someMethod(x: boolean | string): void; ## Parameters -| Parameter | Type | Description | -| --- | --- | --- | -| x | boolean \| string | | + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +x + + + + +boolean \| string + + + + + +
**Returns:** diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.someoverload.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.someoverload.md index 4824c88c162..1229bb41ccd 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.someoverload.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.someoverload.md @@ -14,9 +14,37 @@ someOverload(x: boolean): void; ## Parameters -| Parameter | Type | Description | -| --- | --- | --- | -| x | boolean | | + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +x + + + + +boolean + + + + + +
**Returns:** diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.someoverload_1.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.someoverload_1.md index 71b607dc325..a6c4c229baa 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.someoverload_1.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.class3.someoverload_1.md @@ -14,9 +14,37 @@ someOverload(x: string): void; ## Parameters -| Parameter | Type | Description | -| --- | --- | --- | -| x | string | | + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +x + + + + +string + + + + + +
**Returns:** diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.md index c7956bcc7a5..bfe2141e4f6 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/api-documenter-scenarios.namespace1.md @@ -13,7 +13,26 @@ export declare namespace Namespace1 ## Classes -| Class | Description | -| --- | --- | -| [Class3](./api-documenter-scenarios.namespace1.class3.md) | | + + +
+ +Class + + + + +Description + + +
+ +[Class3](./api-documenter-scenarios.namespace1.class3.md) + + + + + + +
diff --git a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/index.md b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/index.md index f5ce6743125..3fb30d96fea 100644 --- a/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/index.md +++ b/build-tests/api-documenter-scenarios/etc/inheritedMembers/markdown/index.md @@ -6,7 +6,25 @@ ## Packages -| Package | Description | -| --- | --- | -| [api-documenter-scenarios](./api-documenter-scenarios.md) | | + + +
+ +Package + + + + +Description + + +
+ +[api-documenter-scenarios](./api-documenter-scenarios.md) + + + + + +
diff --git a/build-tests/api-documenter-scenarios/package.json b/build-tests/api-documenter-scenarios/package.json index 0de2bd91da2..413243f376b 100644 --- a/build-tests/api-documenter-scenarios/package.json +++ b/build-tests/api-documenter-scenarios/package.json @@ -3,19 +3,33 @@ "description": "Building this project is a regression test for api-documenter", "version": "1.0.0", "private": true, - "typings": "dist/internal/some-fake-file.d.ts", + "types": "./dist/internal/some-fake-file.d.ts", + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "scripts": { - "build": "node build.js", - "_phase:build": "node build.js" + "build": "heft build --clean", + "_phase:build": "heft build --clean" }, "devDependencies": { "@microsoft/api-documenter": "workspace:*", "@microsoft/api-extractor": "workspace:*", - "@microsoft/teams-js": "1.3.0-beta.4", + "@rushstack/heft": "workspace:*", "@rushstack/node-core-library": "workspace:*", - "@types/jest": "29.2.5", - "@types/node": "14.18.36", - "fs-extra": "~7.0.1", - "typescript": "~5.0.4" + "eslint": "~9.37.0", + "local-node-rig": "workspace:*", + "run-scenarios-helpers": "workspace:*" } } diff --git a/build-tests/api-documenter-scenarios/src/inheritedMembers/index.ts b/build-tests/api-documenter-scenarios/src/inheritedMembers/index.ts index 5852291681b..a5f0cb269ce 100644 --- a/build-tests/api-documenter-scenarios/src/inheritedMembers/index.ts +++ b/build-tests/api-documenter-scenarios/src/inheritedMembers/index.ts @@ -1,21 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import { Extractor } from '@microsoft/api-extractor'; /** @public */ +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace Namespace1 { /** @public */ export class Class3 { /** Some prop. */ + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility someProp: number; /** Some method. */ + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility someMethod(x: boolean | string): void {} /** Some overload. */ + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility someOverload(x: boolean): void; /** Some overload. */ + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility someOverload(x: string): void; + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility someOverload(x: boolean | string): void {} } } @@ -23,25 +32,31 @@ export namespace Namespace1 { /** @public */ export class Class2 extends Namespace1.Class3 { /** A second prop. */ + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility secondProp: boolean | string; /** A third prop. */ + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility thirdProp: T; /** Some method. Overrides `Class3.someMethod`. */ - someMethod(x: boolean): void {} + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility + override someMethod(x: boolean): void {} } /** @public */ export class Class1 extends Class2 { /** A second prop. Overrides `Class2.secondProp`. */ - secondProp: boolean; + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility + override secondProp: boolean; /** A fourth prop */ + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility fourthProp: number; /** Some overload. Overrides `Class3.someOverload`. */ - someOverload(x: boolean | string): void {} + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility + override someOverload(x: boolean | string): void {} } /** @public */ @@ -72,6 +87,7 @@ export interface IExtendsMultipleInterfaces extends IInterface1, IInterface2 { } class UnexportedClass { + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility someProp: number; } @@ -86,6 +102,7 @@ export class ExtendsUnexportedClass extends UnexportedClass {} * @public */ export class ExtendsAnonymousClass extends class { + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility someProp: number; } {} @@ -93,7 +110,9 @@ export class ExtendsAnonymousClass extends class { * Some class-like variable. * @public */ +// eslint-disable-next-line @typescript-eslint/typedef export const ClassLikeVariable = class { + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility someProp: number; }; @@ -107,6 +126,7 @@ export class ExtendsClassLikeVariable extends ClassLikeVariable {} * Some interface-like type alias. * @public */ +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type IInterfaceLikeTypeAlias = { someProp: number; }; diff --git a/build-tests/api-documenter-scenarios/src/runScenarios.ts b/build-tests/api-documenter-scenarios/src/runScenarios.ts index ed160fdd283..6dc753d8862 100644 --- a/build-tests/api-documenter-scenarios/src/runScenarios.ts +++ b/build-tests/api-documenter-scenarios/src/runScenarios.ts @@ -1,153 +1,68 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import child_process = require('child_process'); -import { AlreadyExistsBehavior, FileSystem, JsonFile } from '@rushstack/node-core-library'; -import { - Extractor, - ExtractorConfig, - CompilerState, - ExtractorResult, - ExtractorMessage, - ConsoleMessageId, - ExtractorLogLevel -} from '@microsoft/api-extractor'; - -export function runScenarios(buildConfigPath: string): void { - const buildConfig = JsonFile.load(buildConfigPath); - - // Copy any .d.ts files into the "lib/" folder - FileSystem.copyFiles({ - sourcePath: './src/', - destinationPath: './lib/', - alreadyExistsBehavior: AlreadyExistsBehavior.Overwrite, - filter: (sourcePath: string): boolean => { - if (sourcePath.endsWith('.d.ts') || !sourcePath.endsWith('.ts')) { - // console.log('COPY ' + sourcePath); - return true; +import type { ChildProcess } from 'node:child_process'; + +import { runScenariosAsync } from 'run-scenarios-helpers'; + +import type { IRunScriptOptions } from '@rushstack/heft'; +import { Executable, FileSystem, JsonFile } from '@rushstack/node-core-library'; + +export async function runAsync(runScriptOptions: IRunScriptOptions): Promise { + const { + heftConfiguration: { buildFolderPath } + } = runScriptOptions; + + const apiDocumenterJsonPath: string = `${buildFolderPath}/temp/configs/api-documenter.json`; + + await runScenariosAsync(runScriptOptions, { + libFolderPath: __dirname, + afterApiExtractorAsync: async (scenarioFolderName: string) => { + // API Documenter will always look for a config file in the same place (it cannot be configured), so this script + // manually overwrites the API documenter config for each scenario. This is in contrast to the separate config files + // created when invoking API Extractor above. + const apiDocumenterOverridesPath: string = `${buildFolderPath}/src/${scenarioFolderName}/config/api-documenter-overrides.json`; + let apiDocumenterJsonOverrides: {} | undefined; + try { + apiDocumenterJsonOverrides = await JsonFile.loadAsync(apiDocumenterOverridesPath); + } catch (e) { + if (!FileSystem.isNotExistError(e)) { + throw e; + } } - return false; - } - }); - - const entryPoints: string[] = []; - - for (const scenarioFolderName of buildConfig.scenarioFolderNames) { - const entryPoint: string = path.resolve(`./lib/${scenarioFolderName}/index.d.ts`); - entryPoints.push(entryPoint); - - const apiExtractorOverridesPath = path.resolve( - `./src/${scenarioFolderName}/config/api-extractor-overrides.json` - ); - const apiExtractorJsonOverrides = FileSystem.exists(apiExtractorOverridesPath) - ? JsonFile.load(apiExtractorOverridesPath) - : {}; - const apiExtractorJson = { - $schema: 'https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json', - - mainEntryPointFilePath: entryPoint, - - apiReport: { - enabled: true, - reportFolder: `/etc/${scenarioFolderName}` - }, - - dtsRollup: { - enabled: true, - untrimmedFilePath: `/etc/${scenarioFolderName}/rollup.d.ts` - }, - - docModel: { - enabled: true, - apiJsonFilePath: `/etc/${scenarioFolderName}/.api.json` - }, - - testMode: true, - ...apiExtractorJsonOverrides - }; - - const apiExtractorJsonPath: string = `./temp/configs/api-extractor-${scenarioFolderName}.json`; - - JsonFile.save(apiExtractorJson, apiExtractorJsonPath, { ensureFolderExists: true }); - } - - const apiDocumenterJsonPath: string = `./config/api-documenter.json`; - let compilerState: CompilerState | undefined = undefined; - let anyErrors: boolean = false; - process.exitCode = 1; - - for (const scenarioFolderName of buildConfig.scenarioFolderNames) { - console.log('Scenario: ' + scenarioFolderName); - - // Run the API Extractor programmtically - const apiExtractorJsonPath: string = `./temp/configs/api-extractor-${scenarioFolderName}.json`; - const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare(apiExtractorJsonPath); - - if (!compilerState) { - compilerState = CompilerState.create(extractorConfig, { - additionalEntryPoints: entryPoints - }); - } - - const extractorResult: ExtractorResult = Extractor.invoke(extractorConfig, { - localBuild: true, - showVerboseMessages: true, - messageCallback: (message: ExtractorMessage) => { - switch (message.messageId) { - case ConsoleMessageId.ApiReportCreated: - // This script deletes the outputs for a clean build, so don't issue a warning if the file gets created - message.logLevel = ExtractorLogLevel.None; - break; - case ConsoleMessageId.Preamble: - // Less verbose output - message.logLevel = ExtractorLogLevel.None; - break; + const apiDocumenterJson: {} = { + $schema: 'https://developer.microsoft.com/json-schemas/api-extractor/v7/api-documenter.schema.json', + outputTarget: 'markdown', + tableOfContents: {}, + ...apiDocumenterJsonOverrides + }; + + await JsonFile.saveAsync(apiDocumenterJson, apiDocumenterJsonPath, { ensureFolderExists: true }); + + // TODO: Ensure that the checked-in files are up-to-date + // Run the API Documenter command-line + const childProcess: ChildProcess = Executable.spawn( + process.argv0, + [ + `${buildFolderPath}/node_modules/@microsoft/api-documenter/lib-commonjs/start`, + 'generate', + `--input-folder`, + `${buildFolderPath}/temp/etc/${scenarioFolderName}`, + '--output-folder', + `${buildFolderPath}/temp/etc/${scenarioFolderName}/markdown` + ], + { + stdio: 'inherit', + // api-documenter will find api-documenter.json in this folder: + currentWorkingDirectory: `${buildFolderPath}/temp/configs` } - }, - compilerState - }); + ); - if (extractorResult.errorCount > 0) { - anyErrors = true; + await Executable.waitForExitAsync(childProcess, { + throwOnNonZeroExitCode: true, + throwOnSignal: true + }); } - - // API Documenter will always look for a config file in the same place (it cannot be configured), so this script - // manually overwrites the API documenter config for each scenario. This is in contrast to the separate config files - // created when invoking API Extractor above. - const apiDocumenterOverridesPath = path.resolve( - `./src/${scenarioFolderName}/config/api-documenter-overrides.json` - ); - const apiDocumenterJsonOverrides = FileSystem.exists(apiDocumenterOverridesPath) - ? JsonFile.load(apiDocumenterOverridesPath) - : {}; - const apiDocumenterJson = { - $schema: 'https://developer.microsoft.com/json-schemas/api-extractor/v7/api-documenter.schema.json', - outputTarget: 'markdown', - tableOfContents: {}, - ...apiDocumenterJsonOverrides - }; - - JsonFile.save(apiDocumenterJson, apiDocumenterJsonPath, { ensureFolderExists: true }); - - // Run the API Documenter command-line - executeCommand( - 'node node_modules/@microsoft/api-documenter/lib/start ' + - `generate --input-folder etc/${scenarioFolderName} --output-folder etc/${scenarioFolderName}/markdown` - ); - } - - // Delete the transient `api-documenter.json` file before completing, as it'll just be whatever the last scenario - // was, and shouldn't be committed. - FileSystem.deleteFile(apiDocumenterJsonPath); - - if (!anyErrors) { - process.exitCode = 0; - } -} - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); + }); } diff --git a/build-tests/api-documenter-scenarios/tsconfig.json b/build-tests/api-documenter-scenarios/tsconfig.json index 748d07a6ea4..d5641d0ba38 100644 --- a/build-tests/api-documenter-scenarios/tsconfig.json +++ b/build-tests/api-documenter-scenarios/tsconfig.json @@ -1,16 +1,6 @@ { + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "target": "es6", - "forceConsistentCasingInFileNames": true, - "module": "commonjs", - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "types": ["node", "jest"], - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "outDir": "lib" - }, - "include": ["src/**/*.ts"] + "strictPropertyInitialization": false + } } diff --git a/build-tests/api-documenter-test/build.js b/build-tests/api-documenter-test/build.js deleted file mode 100644 index ecf61a017a2..00000000000 --- a/build-tests/api-documenter-test/build.js +++ /dev/null @@ -1,37 +0,0 @@ -const fsx = require('fs-extra'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -fsx.emptyDirSync('dist'); -fsx.emptyDirSync('lib'); -fsx.emptyDirSync('temp'); - -// Run the TypeScript compiler -executeCommand('node node_modules/typescript/lib/tsc'); - -// Run the API Extractor command-line -if (process.argv.indexOf('--production') >= 0) { - executeCommand('node node_modules/@microsoft/api-extractor/lib/start run'); -} else { - executeCommand('node node_modules/@microsoft/api-extractor/lib/start run --local'); -} - -// Run the API Documenter command-line -executeCommand( - 'node node_modules/@microsoft/api-documenter/lib/start ' + - 'generate --input-folder etc --output-folder etc/yaml' -); -executeCommand( - 'node node_modules/@microsoft/api-documenter/lib/start ' + - 'markdown --input-folder etc --output-folder etc/markdown' -); - -console.log(`==> Finished build.js for ${path.basename(process.cwd())}`); diff --git a/build-tests/api-documenter-test/config/api-extractor.json b/build-tests/api-documenter-test/config/api-extractor.json index 17b82fa03cc..bab654deb35 100644 --- a/build-tests/api-documenter-test/config/api-extractor.json +++ b/build-tests/api-documenter-test/config/api-extractor.json @@ -1,12 +1,15 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - "mainEntryPointFilePath": "/lib/index.d.ts", + "mainEntryPointFilePath": "/lib-dts/index.d.ts", "newlineKind": "crlf", "apiReport": { - "enabled": true + "enabled": true, + "tagsToReport": { + "@myCustomTag": true + } }, "docModel": { diff --git a/build-tests/api-documenter-test/config/rig.json b/build-tests/api-documenter-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-documenter-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-documenter-test/config/rush-project.json b/build-tests/api-documenter-test/config/rush-project.json deleted file mode 100644 index 247dc17187a..00000000000 --- a/build-tests/api-documenter-test/config/rush-project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "operationSettings": [ - { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] - } - ] -} diff --git a/build-tests/api-documenter-test/eslint.config.js b/build-tests/api-documenter-test/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/build-tests/api-documenter-test/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/api-documenter-test/etc/api-documenter-test.api.json b/build-tests/api-documenter-test/etc/api-documenter-test.api.json index 8d070ccc44c..19aa17448e4 100644 --- a/build-tests/api-documenter-test/etc/api-documenter-test.api.json +++ b/build-tests/api-documenter-test/etc/api-documenter-test.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" @@ -576,7 +592,7 @@ { "kind": "Method", "canonicalReference": "api-documenter-test!DocClass1#exampleFunction:member(2)", - "docComment": "/**\n * This is also an overloaded function.\n *\n * @param x - the number\n */\n", + "docComment": "/**\n * This is also an overloaded function.\n *\n * @param x - the number\n *\n * @defaultValue 123\n */\n", "excerptTokens": [ { "kind": "Content", @@ -1353,22 +1369,22 @@ }, { "kind": "Namespace", - "canonicalReference": "api-documenter-test!EcmaSmbols:namespace", + "canonicalReference": "api-documenter-test!EcmaSymbols:namespace", "docComment": "/**\n * A namespace containing an ECMAScript symbol\n *\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", - "text": "export declare namespace EcmaSmbols " + "text": "export declare namespace EcmaSymbols " } ], "fileUrlPath": "src/DocClass1.ts", "releaseTag": "Public", - "name": "EcmaSmbols", + "name": "EcmaSymbols", "preserveMemberOrder": false, "members": [ { "kind": "Variable", - "canonicalReference": "api-documenter-test!EcmaSmbols.example:var", + "canonicalReference": "api-documenter-test!EcmaSymbols.example:var", "docComment": "/**\n * An ECMAScript symbol\n */\n", "excerptTokens": [ { @@ -1776,7 +1792,7 @@ }, { "kind": "PropertySignature", - "canonicalReference": "api-documenter-test!IDocInterface3#[EcmaSmbols.example]:member", + "canonicalReference": "api-documenter-test!IDocInterface3#[EcmaSymbols.example]:member", "docComment": "/**\n * ECMAScript symbol\n */\n", "excerptTokens": [ { @@ -1785,8 +1801,8 @@ }, { "kind": "Reference", - "text": "EcmaSmbols.example", - "canonicalReference": "api-documenter-test!EcmaSmbols.example" + "text": "EcmaSymbols.example", + "canonicalReference": "api-documenter-test!EcmaSymbols.example" }, { "kind": "Content", @@ -1804,7 +1820,7 @@ "isReadonly": false, "isOptional": false, "releaseTag": "Public", - "name": "[EcmaSmbols.example]", + "name": "[EcmaSymbols.example]", "propertyTypeTokenRange": { "startIndex": 3, "endIndex": 4 @@ -2103,7 +2119,7 @@ { "kind": "PropertySignature", "canonicalReference": "api-documenter-test!IDocInterface5#regularProperty:member", - "docComment": "/**\n * Property of type string that does something\n */\n", + "docComment": "/**\n * Property of type string that does something\n *\n * @defaultValue \"Hello World\"\n */\n", "excerptTokens": [ { "kind": "Content", diff --git a/build-tests/api-documenter-test/etc/api-documenter-test.api.md b/build-tests/api-documenter-test/etc/api-documenter-test.api.md index 18bab020adb..4c1077d66a6 100644 --- a/build-tests/api-documenter-test/etc/api-documenter-test.api.md +++ b/build-tests/api-documenter-test/etc/api-documenter-test.api.md @@ -87,7 +87,7 @@ export namespace DocEnumNamespaceMerge { } // @public -export namespace EcmaSmbols { +export namespace EcmaSymbols { const example: unique symbol; } @@ -124,7 +124,7 @@ export interface IDocInterface2 extends IDocInterface1 { // @public export interface IDocInterface3 { "[not.a.symbol]": string; - [EcmaSmbols.example]: string; + [EcmaSymbols.example]: string; (x: number): number; [x: string]: string; new (): IDocInterface1; @@ -133,7 +133,7 @@ export interface IDocInterface3 { // @public export interface IDocInterface4 { - Context: ({ children }: { + Context: (input: { children: string; }) => boolean; generic: Generic; @@ -184,7 +184,7 @@ export namespace OuterNamespace { let nestedVariable: boolean; } -// @public +// @public @myCustomTag export class SystemEvent { addHandler(handler: () => void): void; } diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.abstractclass.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.abstractclass.md deleted file mode 100644 index eb8ca6923d4..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.abstractclass.md +++ /dev/null @@ -1,26 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [AbstractClass](./api-documenter-test.abstractclass.md) - -## AbstractClass class - -Some abstract class with abstract members. - -**Signature:** - -```typescript -export declare abstract class AbstractClass -``` - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [property](./api-documenter-test.abstractclass.property.md) |

protected

abstract

| number | Some abstract property. | - -## Methods - -| Method | Modifiers | Description | -| --- | --- | --- | -| [method()](./api-documenter-test.abstractclass.method.md) | abstract | Some abstract method. | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.abstractclass.method.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.abstractclass.method.md deleted file mode 100644 index ed23bf4d3eb..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.abstractclass.method.md +++ /dev/null @@ -1,17 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [AbstractClass](./api-documenter-test.abstractclass.md) > [method](./api-documenter-test.abstractclass.method.md) - -## AbstractClass.method() method - -Some abstract method. - -**Signature:** - -```typescript -abstract method(): void; -``` -**Returns:** - -void - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.abstractclass.property.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.abstractclass.property.md deleted file mode 100644 index 14d9a35f906..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.abstractclass.property.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [AbstractClass](./api-documenter-test.abstractclass.md) > [property](./api-documenter-test.abstractclass.property.md) - -## AbstractClass.property property - -Some abstract property. - -**Signature:** - -```typescript -protected abstract property: number; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.constraint.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.constraint.md deleted file mode 100644 index 23e1eba6aa2..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.constraint.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [Constraint](./api-documenter-test.constraint.md) - -## Constraint interface - -Type parameter constraint used by test case below. - -**Signature:** - -```typescript -export interface Constraint -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.constvariable.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.constvariable.md deleted file mode 100644 index f1983273097..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.constvariable.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [constVariable](./api-documenter-test.constvariable.md) - -## constVariable variable - -An exported variable declaration. - -**Signature:** - -```typescript -constVariable: number -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.decoratorexample.creationdate.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.decoratorexample.creationdate.md deleted file mode 100644 index 10d7cac8d65..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.decoratorexample.creationdate.md +++ /dev/null @@ -1,23 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DecoratorExample](./api-documenter-test.decoratorexample.md) > [creationDate](./api-documenter-test.decoratorexample.creationdate.md) - -## DecoratorExample.creationDate property - -The date when the record was created. - -**Signature:** - -```typescript -creationDate: Date; -``` -**Decorators:** - -`@jsonSerialized` - -`@jsonFormat('mm/dd/yy')` - -## Remarks - -Here is a longer description of the property. - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.decoratorexample.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.decoratorexample.md deleted file mode 100644 index 1ad743d98dc..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.decoratorexample.md +++ /dev/null @@ -1,19 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DecoratorExample](./api-documenter-test.decoratorexample.md) - -## DecoratorExample class - - -**Signature:** - -```typescript -export declare class DecoratorExample -``` - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [creationDate](./api-documenter-test.decoratorexample.creationdate.md) | | Date | The date when the record was created. | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.defaulttype.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.defaulttype.md deleted file mode 100644 index 63b11b92846..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.defaulttype.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DefaultType](./api-documenter-test.defaulttype.md) - -## DefaultType interface - -Type parameter default type used by test case below. - -**Signature:** - -```typescript -export interface DefaultType -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docbaseclass._constructor_.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docbaseclass._constructor_.md deleted file mode 100644 index 2bf60957465..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docbaseclass._constructor_.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocBaseClass](./api-documenter-test.docbaseclass.md) > [(constructor)](./api-documenter-test.docbaseclass._constructor_.md) - -## DocBaseClass.(constructor) - -The simple constructor for `DocBaseClass` - -**Signature:** - -```typescript -constructor(); -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docbaseclass._constructor__1.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docbaseclass._constructor__1.md deleted file mode 100644 index 6e1fb0514dd..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docbaseclass._constructor__1.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocBaseClass](./api-documenter-test.docbaseclass.md) > [(constructor)](./api-documenter-test.docbaseclass._constructor__1.md) - -## DocBaseClass.(constructor) - -The overloaded constructor for `DocBaseClass` - -**Signature:** - -```typescript -constructor(x: number); -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| x | number | | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docbaseclass.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docbaseclass.md deleted file mode 100644 index 9956be3c3e8..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docbaseclass.md +++ /dev/null @@ -1,22 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocBaseClass](./api-documenter-test.docbaseclass.md) - -## DocBaseClass class - -Example base class - - -**Signature:** - -```typescript -export declare class DocBaseClass -``` - -## Constructors - -| Constructor | Modifiers | Description | -| --- | --- | --- | -| [(constructor)()](./api-documenter-test.docbaseclass._constructor_.md) | | The simple constructor for DocBaseClass | -| [(constructor)(x)](./api-documenter-test.docbaseclass._constructor__1.md) | | The overloaded constructor for DocBaseClass | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.deprecatedexample.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.deprecatedexample.md deleted file mode 100644 index 7bc105104ec..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.deprecatedexample.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [deprecatedExample](./api-documenter-test.docclass1.deprecatedexample.md) - -## DocClass1.deprecatedExample() method - -> Warning: This API is now obsolete. -> -> Use `otherThing()` instead. -> - -**Signature:** - -```typescript -deprecatedExample(): void; -``` -**Returns:** - -void - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.examplefunction.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.examplefunction.md deleted file mode 100644 index 25b2e9e4eee..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.examplefunction.md +++ /dev/null @@ -1,31 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [exampleFunction](./api-documenter-test.docclass1.examplefunction.md) - -## DocClass1.exampleFunction() method - -This is an overloaded function. - -**Signature:** - -```typescript -exampleFunction(a: string, b: string): string; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| a | string | the first string | -| b | string | the second string | - -**Returns:** - -string - -## Exceptions - -`Error` The first throws line - -The second throws line - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.examplefunction_1.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.examplefunction_1.md deleted file mode 100644 index 63c9b7d4075..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.examplefunction_1.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [exampleFunction](./api-documenter-test.docclass1.examplefunction_1.md) - -## DocClass1.exampleFunction() method - -This is also an overloaded function. - -**Signature:** - -```typescript -exampleFunction(x: number): number; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| x | number | the number | - -**Returns:** - -number - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.genericwithconstraintanddefault.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.genericwithconstraintanddefault.md deleted file mode 100644 index d13ef6c720f..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.genericwithconstraintanddefault.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [genericWithConstraintAndDefault](./api-documenter-test.docclass1.genericwithconstraintanddefault.md) - -## DocClass1.genericWithConstraintAndDefault() method - -This is a method with a complex type parameter. - -**Signature:** - -```typescript -genericWithConstraintAndDefault(x: T): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| x | T | some generic parameter. | - -**Returns:** - -void - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.interestingedgecases.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.interestingedgecases.md deleted file mode 100644 index d38c8881388..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.interestingedgecases.md +++ /dev/null @@ -1,19 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [interestingEdgeCases](./api-documenter-test.docclass1.interestingedgecases.md) - -## DocClass1.interestingEdgeCases() method - -Example: "{ \\"maxItemsToShow\\": 123 }" - -The regular expression used to validate the constraints is /^\[a-zA-Z0-9\\-\_\]+$/ - -**Signature:** - -```typescript -interestingEdgeCases(): void; -``` -**Returns:** - -void - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.malformedevent.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.malformedevent.md deleted file mode 100644 index d5366273cce..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.malformedevent.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [malformedEvent](./api-documenter-test.docclass1.malformedevent.md) - -## DocClass1.malformedEvent property - -This event should have been marked as readonly. - -**Signature:** - -```typescript -malformedEvent: SystemEvent; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.md deleted file mode 100644 index 5a0a095b7e4..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.md +++ /dev/null @@ -1,57 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) - -## DocClass1 class - -This is an example class. - -**Signature:** - -```typescript -export declare class DocClass1 extends DocBaseClass implements IDocInterface1, IDocInterface2 -``` -**Extends:** [DocBaseClass](./api-documenter-test.docbaseclass.md) - -**Implements:** [IDocInterface1](./api-documenter-test.idocinterface1.md), [IDocInterface2](./api-documenter-test.idocinterface2.md) - -## Remarks - -[Link to overload 1](./api-documenter-test.docclass1.examplefunction.md) - -[Link to overload 2](./api-documenter-test.docclass1.examplefunction_1.md) - - -The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the `DocClass1` class. - -## Events - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [malformedEvent](./api-documenter-test.docclass1.malformedevent.md) | | [SystemEvent](./api-documenter-test.systemevent.md) | This event should have been marked as readonly. | -| [modifiedEvent](./api-documenter-test.docclass1.modifiedevent.md) | readonly | [SystemEvent](./api-documenter-test.systemevent.md) | This event is fired whenever the object is modified. | - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [multipleModifiersProperty](./api-documenter-test.docclass1.multiplemodifiersproperty.md) |

protected

static

readonly

| boolean | Some property with multiple modifiers. | -| [protectedProperty](./api-documenter-test.docclass1.protectedproperty.md) | protected | string | Some protected property. | -| [readonlyProperty](./api-documenter-test.docclass1.readonlyproperty.md) | readonly | string | | -| [regularProperty](./api-documenter-test.docclass1.regularproperty.md) | | [SystemEvent](./api-documenter-test.systemevent.md) | This is a regular property that happens to use the SystemEvent type. | -| [writeableProperty](./api-documenter-test.docclass1.writeableproperty.md) | | string | | -| [writeonlyProperty](./api-documenter-test.docclass1.writeonlyproperty.md) | | string | API Extractor will surface an ae-missing-getter finding for this property. | - -## Methods - -| Method | Modifiers | Description | -| --- | --- | --- | -| [deprecatedExample()](./api-documenter-test.docclass1.deprecatedexample.md) | | | -| [exampleFunction(a, b)](./api-documenter-test.docclass1.examplefunction.md) | | This is an overloaded function. | -| [exampleFunction(x)](./api-documenter-test.docclass1.examplefunction_1.md) | | This is also an overloaded function. | -| [genericWithConstraintAndDefault(x)](./api-documenter-test.docclass1.genericwithconstraintanddefault.md) | | This is a method with a complex type parameter. | -| [interestingEdgeCases()](./api-documenter-test.docclass1.interestingedgecases.md) | |

Example: "{ \\"maxItemsToShow\\": 123 }"

The regular expression used to validate the constraints is /^\[a-zA-Z0-9\\-\_\]+$/

| -| [optionalParamFunction(x)](./api-documenter-test.docclass1.optionalparamfunction.md) | | This is a function with an optional parameter. | -| [sumWithExample(x, y)](./api-documenter-test.docclass1.sumwithexample.md) | static | Returns the sum of two numbers. | -| [tableExample()](./api-documenter-test.docclass1.tableexample.md) | | An example with tables: | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.modifiedevent.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.modifiedevent.md deleted file mode 100644 index 6efebd1c5ef..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.modifiedevent.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [modifiedEvent](./api-documenter-test.docclass1.modifiedevent.md) - -## DocClass1.modifiedEvent property - -This event is fired whenever the object is modified. - -**Signature:** - -```typescript -readonly modifiedEvent: SystemEvent; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.multiplemodifiersproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.multiplemodifiersproperty.md deleted file mode 100644 index 255329bedc9..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.multiplemodifiersproperty.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [multipleModifiersProperty](./api-documenter-test.docclass1.multiplemodifiersproperty.md) - -## DocClass1.multipleModifiersProperty property - -Some property with multiple modifiers. - -**Signature:** - -```typescript -protected static readonly multipleModifiersProperty: boolean; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.optionalparamfunction.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.optionalparamfunction.md deleted file mode 100644 index 77c1a7b9978..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.optionalparamfunction.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [optionalParamFunction](./api-documenter-test.docclass1.optionalparamfunction.md) - -## DocClass1.optionalParamFunction() method - -This is a function with an optional parameter. - -**Signature:** - -```typescript -optionalParamFunction(x?: number): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| x | number | _(Optional)_ the number | - -**Returns:** - -void - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.protectedproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.protectedproperty.md deleted file mode 100644 index 80faac05579..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.protectedproperty.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [protectedProperty](./api-documenter-test.docclass1.protectedproperty.md) - -## DocClass1.protectedProperty property - -Some protected property. - -**Signature:** - -```typescript -protected protectedProperty: string; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.readonlyproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.readonlyproperty.md deleted file mode 100644 index df221991d88..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.readonlyproperty.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [readonlyProperty](./api-documenter-test.docclass1.readonlyproperty.md) - -## DocClass1.readonlyProperty property - -**Signature:** - -```typescript -get readonlyProperty(): string; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.regularproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.regularproperty.md deleted file mode 100644 index b08e0233e35..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.regularproperty.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [regularProperty](./api-documenter-test.docclass1.regularproperty.md) - -## DocClass1.regularProperty property - -This is a regular property that happens to use the SystemEvent type. - -**Signature:** - -```typescript -regularProperty: SystemEvent; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.sumwithexample.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.sumwithexample.md deleted file mode 100644 index 5e8dd1a7d57..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.sumwithexample.md +++ /dev/null @@ -1,49 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [sumWithExample](./api-documenter-test.docclass1.sumwithexample.md) - -## DocClass1.sumWithExample() method - -Returns the sum of two numbers. - -**Signature:** - -```typescript -static sumWithExample(x: number, y: number): number; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| x | number | the first number to add | -| y | number | the second number to add | - -**Returns:** - -number - -the sum of the two numbers - -## Remarks - -This illustrates usage of the `@example` block tag. - -## Example 1 - -Here's a simple example: - -``` -// Prints "2": -console.log(DocClass1.sumWithExample(1,1)); -``` - -## Example 2 - -Here's an example with negative numbers: - -``` -// Prints "0": -console.log(DocClass1.sumWithExample(1,-1)); -``` - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.tableexample.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.tableexample.md deleted file mode 100644 index 30e593b2370..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.tableexample.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [tableExample](./api-documenter-test.docclass1.tableexample.md) - -## DocClass1.tableExample() method - -An example with tables: - -**Signature:** - -```typescript -tableExample(): void; -``` -**Returns:** - -void - -## Remarks - -
John Doe
- diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.writeableproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.writeableproperty.md deleted file mode 100644 index 2a98f0b99af..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.writeableproperty.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [writeableProperty](./api-documenter-test.docclass1.writeableproperty.md) - -## DocClass1.writeableProperty property - -**Signature:** - -```typescript -get writeableProperty(): string; - -set writeableProperty(value: string); -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.writeonlyproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.writeonlyproperty.md deleted file mode 100644 index a8a3e2f3c1e..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.writeonlyproperty.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [writeonlyProperty](./api-documenter-test.docclass1.writeonlyproperty.md) - -## DocClass1.writeonlyProperty property - -API Extractor will surface an `ae-missing-getter` finding for this property. - -**Signature:** - -```typescript -set writeonlyProperty(value: string); -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclassinterfacemerge.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclassinterfacemerge.md deleted file mode 100644 index 09e8b18f838..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclassinterfacemerge.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClassInterfaceMerge](./api-documenter-test.docclassinterfacemerge.md) - -## DocClassInterfaceMerge interface - -Interface that merges with class - -**Signature:** - -```typescript -export interface DocClassInterfaceMerge -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docenum.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docenum.md deleted file mode 100644 index 2cf74f71557..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docenum.md +++ /dev/null @@ -1,23 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocEnum](./api-documenter-test.docenum.md) - -## DocEnum enum - -Docs for DocEnum - - -**Signature:** - -```typescript -export declare enum DocEnum -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| One | 1 | These are some docs for One | -| Two | 2 |

These are some docs for Two.

[DocEnum.One](./api-documenter-test.docenum.md) is a direct link to another enum member.

| -| Zero | 0 | These are some docs for Zero | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docenumnamespacemerge.examplefunction.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docenumnamespacemerge.examplefunction.md deleted file mode 100644 index d3450d285d5..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docenumnamespacemerge.examplefunction.md +++ /dev/null @@ -1,17 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocEnumNamespaceMerge](./api-documenter-test.docenumnamespacemerge.md) > [exampleFunction](./api-documenter-test.docenumnamespacemerge.examplefunction.md) - -## DocEnumNamespaceMerge.exampleFunction() function - -This is a function inside of a namespace that merges with an enum. - -**Signature:** - -```typescript -function exampleFunction(): void; -``` -**Returns:** - -void - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docenumnamespacemerge.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docenumnamespacemerge.md deleted file mode 100644 index 3d6ea812daf..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docenumnamespacemerge.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocEnumNamespaceMerge](./api-documenter-test.docenumnamespacemerge.md) - -## DocEnumNamespaceMerge namespace - -Namespace that merges with enum - -**Signature:** - -```typescript -export declare namespace DocEnumNamespaceMerge -``` - -## Functions - -| Function | Description | -| --- | --- | -| [exampleFunction()](./api-documenter-test.docenumnamespacemerge.examplefunction.md) | This is a function inside of a namespace that merges with an enum. | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.ecmasmbols.example.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.ecmasmbols.example.md deleted file mode 100644 index 03bffc31e62..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.ecmasmbols.example.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [EcmaSmbols](./api-documenter-test.ecmasmbols.md) > [example](./api-documenter-test.ecmasmbols.example.md) - -## EcmaSmbols.example variable - -An ECMAScript symbol - -**Signature:** - -```typescript -example: unique symbol -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.ecmasmbols.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.ecmasmbols.md deleted file mode 100644 index 223267ecb6a..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.ecmasmbols.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [EcmaSmbols](./api-documenter-test.ecmasmbols.md) - -## EcmaSmbols namespace - -A namespace containing an ECMAScript symbol - -**Signature:** - -```typescript -export declare namespace EcmaSmbols -``` - -## Variables - -| Variable | Description | -| --- | --- | -| [example](./api-documenter-test.ecmasmbols.example.md) | An ECMAScript symbol | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.exampleduplicatetypealias.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.exampleduplicatetypealias.md deleted file mode 100644 index a8aa327934f..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.exampleduplicatetypealias.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [ExampleDuplicateTypeAlias](./api-documenter-test.exampleduplicatetypealias.md) - -## ExampleDuplicateTypeAlias type - -A type alias that has duplicate references. - -**Signature:** - -```typescript -export type ExampleDuplicateTypeAlias = SystemEvent | typeof SystemEvent; -``` -**References:** [SystemEvent](./api-documenter-test.systemevent.md) - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.examplefunction.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.examplefunction.md deleted file mode 100644 index 4c1163ab3ee..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.examplefunction.md +++ /dev/null @@ -1,27 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [exampleFunction](./api-documenter-test.examplefunction.md) - -## exampleFunction() function - -An exported function with hyperlinked parameters and return value. - -**Signature:** - -```typescript -export declare function exampleFunction(x: ExampleTypeAlias, y: number): IDocInterface1; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| x | [ExampleTypeAlias](./api-documenter-test.exampletypealias.md) | an API item that should get hyperlinked | -| y | number | a system type that should NOT get hyperlinked | - -**Returns:** - -[IDocInterface1](./api-documenter-test.idocinterface1.md) - -an interface that should get hyperlinked - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.exampletypealias.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.exampletypealias.md deleted file mode 100644 index c6b401aadaa..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.exampletypealias.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [ExampleTypeAlias](./api-documenter-test.exampletypealias.md) - -## ExampleTypeAlias type - -A type alias - -**Signature:** - -```typescript -export type ExampleTypeAlias = Promise; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.exampleuniontypealias.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.exampleuniontypealias.md deleted file mode 100644 index d305e374539..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.exampleuniontypealias.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [ExampleUnionTypeAlias](./api-documenter-test.exampleuniontypealias.md) - -## ExampleUnionTypeAlias type - -A type alias that references multiple other types. - -**Signature:** - -```typescript -export type ExampleUnionTypeAlias = IDocInterface1 | IDocInterface3; -``` -**References:** [IDocInterface1](./api-documenter-test.idocinterface1.md), [IDocInterface3](./api-documenter-test.idocinterface3.md) - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.generic.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.generic.md deleted file mode 100644 index 78cadda1bb5..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.generic.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [Generic](./api-documenter-test.generic.md) - -## Generic class - -Generic class. - -**Signature:** - -```typescript -export declare class Generic -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.generictypealias.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.generictypealias.md deleted file mode 100644 index a574c6868f9..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.generictypealias.md +++ /dev/null @@ -1,12 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [GenericTypeAlias](./api-documenter-test.generictypealias.md) - -## GenericTypeAlias type - - -**Signature:** - -```typescript -export type GenericTypeAlias = T[]; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface1.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface1.md deleted file mode 100644 index 077ad95bb0a..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface1.md +++ /dev/null @@ -1,19 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface1](./api-documenter-test.idocinterface1.md) - -## IDocInterface1 interface - - -**Signature:** - -```typescript -export interface IDocInterface1 -``` - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [regularProperty](./api-documenter-test.idocinterface1.regularproperty.md) | | [SystemEvent](./api-documenter-test.systemevent.md) | Does something | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface1.regularproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface1.regularproperty.md deleted file mode 100644 index 0f6a2940db8..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface1.regularproperty.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface1](./api-documenter-test.idocinterface1.md) > [regularProperty](./api-documenter-test.idocinterface1.regularproperty.md) - -## IDocInterface1.regularProperty property - -Does something - -**Signature:** - -```typescript -regularProperty: SystemEvent; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface2.deprecatedexample.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface2.deprecatedexample.md deleted file mode 100644 index e49ad4c922c..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface2.deprecatedexample.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface2](./api-documenter-test.idocinterface2.md) > [deprecatedExample](./api-documenter-test.idocinterface2.deprecatedexample.md) - -## IDocInterface2.deprecatedExample() method - -> Warning: This API is now obsolete. -> -> Use `otherThing()` instead. -> - -**Signature:** - -```typescript -deprecatedExample(): void; -``` -**Returns:** - -void - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface2.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface2.md deleted file mode 100644 index 56c63981e8e..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface2.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface2](./api-documenter-test.idocinterface2.md) - -## IDocInterface2 interface - - -**Signature:** - -```typescript -export interface IDocInterface2 extends IDocInterface1 -``` -**Extends:** [IDocInterface1](./api-documenter-test.idocinterface1.md) - -## Methods - -| Method | Description | -| --- | --- | -| [deprecatedExample()](./api-documenter-test.idocinterface2.deprecatedexample.md) | | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3.__not.a.symbol__.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3.__not.a.symbol__.md deleted file mode 100644 index b6911fc3191..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3.__not.a.symbol__.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface3](./api-documenter-test.idocinterface3.md) > ["\[not.a.symbol\]"](./api-documenter-test.idocinterface3.__not.a.symbol__.md) - -## IDocInterface3."\[not.a.symbol\]" property - -An identifier that does need quotes. It misleadingly looks like an ECMAScript symbol. - -**Signature:** - -```typescript -"[not.a.symbol]": string; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3._ecmasmbols.example_.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3._ecmasmbols.example_.md deleted file mode 100644 index 40924a574db..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3._ecmasmbols.example_.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface3](./api-documenter-test.idocinterface3.md) > [\[EcmaSmbols.example\]](./api-documenter-test.idocinterface3._ecmasmbols.example_.md) - -## IDocInterface3.\[EcmaSmbols.example\] property - -ECMAScript symbol - -**Signature:** - -```typescript -[EcmaSmbols.example]: string; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3._new_.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3._new_.md deleted file mode 100644 index 8e5092862e7..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3._new_.md +++ /dev/null @@ -1,17 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface3](./api-documenter-test.idocinterface3.md) > [(new)](./api-documenter-test.idocinterface3._new_.md) - -## IDocInterface3.(new) - -Construct signature - -**Signature:** - -```typescript -new (): IDocInterface1; -``` -**Returns:** - -[IDocInterface1](./api-documenter-test.idocinterface1.md) - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3.md deleted file mode 100644 index 94e789746c8..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3.md +++ /dev/null @@ -1,29 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface3](./api-documenter-test.idocinterface3.md) - -## IDocInterface3 interface - -Some less common TypeScript declaration kinds. - - -**Signature:** - -```typescript -export interface IDocInterface3 -``` - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| ["\[not.a.symbol\]"](./api-documenter-test.idocinterface3.__not.a.symbol__.md) | | string | An identifier that does need quotes. It misleadingly looks like an ECMAScript symbol. | -| [\[EcmaSmbols.example\]](./api-documenter-test.idocinterface3._ecmasmbols.example_.md) | | string | ECMAScript symbol | -| [redundantQuotes](./api-documenter-test.idocinterface3.redundantquotes.md) | | string | A quoted identifier with redundant quotes. | - -## Methods - -| Method | Description | -| --- | --- | -| [(new)()](./api-documenter-test.idocinterface3._new_.md) | Construct signature | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3.redundantquotes.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3.redundantquotes.md deleted file mode 100644 index 436d698afd1..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface3.redundantquotes.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface3](./api-documenter-test.idocinterface3.md) > [redundantQuotes](./api-documenter-test.idocinterface3.redundantquotes.md) - -## IDocInterface3.redundantQuotes property - -A quoted identifier with redundant quotes. - -**Signature:** - -```typescript -"redundantQuotes": string; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.context.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.context.md deleted file mode 100644 index 9401520b9b0..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.context.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface4](./api-documenter-test.idocinterface4.md) > [Context](./api-documenter-test.idocinterface4.context.md) - -## IDocInterface4.Context property - -Test newline rendering when code blocks are used in tables - -**Signature:** - -```typescript -Context: ({ children }: { - children: string; - }) => boolean; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.generic.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.generic.md deleted file mode 100644 index ea1eb271b11..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.generic.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface4](./api-documenter-test.idocinterface4.md) > [generic](./api-documenter-test.idocinterface4.generic.md) - -## IDocInterface4.generic property - -make sure html entities are escaped in tables. - -**Signature:** - -```typescript -generic: Generic; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.md deleted file mode 100644 index 94eaaf0b17a..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface4](./api-documenter-test.idocinterface4.md) - -## IDocInterface4 interface - -Type union in an interface. - - -**Signature:** - -```typescript -export interface IDocInterface4 -``` - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [Context](./api-documenter-test.idocinterface4.context.md) | | ({ children }: { children: string; }) => boolean | Test newline rendering when code blocks are used in tables | -| [generic](./api-documenter-test.idocinterface4.generic.md) | | [Generic](./api-documenter-test.generic.md)<number> | make sure html entities are escaped in tables. | -| [numberOrFunction](./api-documenter-test.idocinterface4.numberorfunction.md) | | number \| (() => number) | a union type with a function | -| [stringOrNumber](./api-documenter-test.idocinterface4.stringornumber.md) | | string \| number | a union type | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.numberorfunction.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.numberorfunction.md deleted file mode 100644 index 50e32926ea2..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.numberorfunction.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface4](./api-documenter-test.idocinterface4.md) > [numberOrFunction](./api-documenter-test.idocinterface4.numberorfunction.md) - -## IDocInterface4.numberOrFunction property - -a union type with a function - -**Signature:** - -```typescript -numberOrFunction: number | (() => number); -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.stringornumber.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.stringornumber.md deleted file mode 100644 index cf2a3c0d630..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface4.stringornumber.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface4](./api-documenter-test.idocinterface4.md) > [stringOrNumber](./api-documenter-test.idocinterface4.stringornumber.md) - -## IDocInterface4.stringOrNumber property - -a union type - -**Signature:** - -```typescript -stringOrNumber: string | number; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface5.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface5.md deleted file mode 100644 index 96de72c48b2..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface5.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface5](./api-documenter-test.idocinterface5.md) - -## IDocInterface5 interface - -Interface without inline tag to test custom TOC - -**Signature:** - -```typescript -export interface IDocInterface5 -``` - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [regularProperty](./api-documenter-test.idocinterface5.regularproperty.md) | | string | Property of type string that does something | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface5.regularproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface5.regularproperty.md deleted file mode 100644 index 0db8715b972..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface5.regularproperty.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface5](./api-documenter-test.idocinterface5.md) > [regularProperty](./api-documenter-test.idocinterface5.regularproperty.md) - -## IDocInterface5.regularProperty property - -Property of type string that does something - -**Signature:** - -```typescript -regularProperty: string; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.arrayproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.arrayproperty.md deleted file mode 100644 index 40dccacfec2..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.arrayproperty.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) > [arrayProperty](./api-documenter-test.idocinterface6.arrayproperty.md) - -## IDocInterface6.arrayProperty property - -**Signature:** - -```typescript -arrayProperty: IDocInterface1[]; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.genericreferencemethod.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.genericreferencemethod.md deleted file mode 100644 index f4b1091b493..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.genericreferencemethod.md +++ /dev/null @@ -1,22 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) > [genericReferenceMethod](./api-documenter-test.idocinterface6.genericreferencemethod.md) - -## IDocInterface6.genericReferenceMethod() method - -**Signature:** - -```typescript -genericReferenceMethod(x: T): T; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| x | T | | - -**Returns:** - -T - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.intersectionproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.intersectionproperty.md deleted file mode 100644 index 0487eada5e2..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.intersectionproperty.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) > [intersectionProperty](./api-documenter-test.idocinterface6.intersectionproperty.md) - -## IDocInterface6.intersectionProperty property - -**Signature:** - -```typescript -intersectionProperty: IDocInterface1 & IDocInterface2; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.md deleted file mode 100644 index 9a38896d932..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.md +++ /dev/null @@ -1,31 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) - -## IDocInterface6 interface - -Interface without inline tag to test custom TOC with injection - -**Signature:** - -```typescript -export interface IDocInterface6 -``` - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [arrayProperty](./api-documenter-test.idocinterface6.arrayproperty.md) | | [IDocInterface1](./api-documenter-test.idocinterface1.md)\[\] | | -| [intersectionProperty](./api-documenter-test.idocinterface6.intersectionproperty.md) | | [IDocInterface1](./api-documenter-test.idocinterface1.md) & [IDocInterface2](./api-documenter-test.idocinterface2.md) | | -| [regularProperty](./api-documenter-test.idocinterface6.regularproperty.md) | | number | Property of type number that does something | -| [tupleProperty](./api-documenter-test.idocinterface6.tupleproperty.md) | | \[[IDocInterface1](./api-documenter-test.idocinterface1.md), [IDocInterface2](./api-documenter-test.idocinterface2.md)\] | | -| [typeReferenceProperty](./api-documenter-test.idocinterface6.typereferenceproperty.md) | | [Generic](./api-documenter-test.generic.md)<[IDocInterface1](./api-documenter-test.idocinterface1.md)> | | -| [unionProperty](./api-documenter-test.idocinterface6.unionproperty.md) | | [IDocInterface1](./api-documenter-test.idocinterface1.md) \| [IDocInterface2](./api-documenter-test.idocinterface2.md) | | - -## Methods - -| Method | Description | -| --- | --- | -| [genericReferenceMethod(x)](./api-documenter-test.idocinterface6.genericreferencemethod.md) | | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.regularproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.regularproperty.md deleted file mode 100644 index ac13117a377..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.regularproperty.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) > [regularProperty](./api-documenter-test.idocinterface6.regularproperty.md) - -## IDocInterface6.regularProperty property - -Property of type number that does something - -**Signature:** - -```typescript -regularProperty: number; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.tupleproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.tupleproperty.md deleted file mode 100644 index 8f0627e8d6e..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.tupleproperty.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) > [tupleProperty](./api-documenter-test.idocinterface6.tupleproperty.md) - -## IDocInterface6.tupleProperty property - -**Signature:** - -```typescript -tupleProperty: [IDocInterface1, IDocInterface2]; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.typereferenceproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.typereferenceproperty.md deleted file mode 100644 index d265dc5b78b..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.typereferenceproperty.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) > [typeReferenceProperty](./api-documenter-test.idocinterface6.typereferenceproperty.md) - -## IDocInterface6.typeReferenceProperty property - -**Signature:** - -```typescript -typeReferenceProperty: Generic; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.unionproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.unionproperty.md deleted file mode 100644 index 1e364da532b..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface6.unionproperty.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) > [unionProperty](./api-documenter-test.idocinterface6.unionproperty.md) - -## IDocInterface6.unionProperty property - -**Signature:** - -```typescript -unionProperty: IDocInterface1 | IDocInterface2; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.md deleted file mode 100644 index fc66ce93d27..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.md +++ /dev/null @@ -1,28 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface7](./api-documenter-test.idocinterface7.md) - -## IDocInterface7 interface - -Interface for testing optional properties - -**Signature:** - -```typescript -export interface IDocInterface7 -``` - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [optionalField?](./api-documenter-test.idocinterface7.optionalfield.md) | | boolean | _(Optional)_ Description of optionalField | -| [optionalReadonlyField?](./api-documenter-test.idocinterface7.optionalreadonlyfield.md) | readonly | boolean | _(Optional)_ Description of optionalReadonlyField | -| [optionalUndocumentedField?](./api-documenter-test.idocinterface7.optionalundocumentedfield.md) | | boolean | _(Optional)_ | - -## Methods - -| Method | Description | -| --- | --- | -| [optionalMember()?](./api-documenter-test.idocinterface7.optionalmember.md) | _(Optional)_ Description of optionalMember | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.optionalfield.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.optionalfield.md deleted file mode 100644 index d2644fbc653..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.optionalfield.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface7](./api-documenter-test.idocinterface7.md) > [optionalField](./api-documenter-test.idocinterface7.optionalfield.md) - -## IDocInterface7.optionalField property - -Description of optionalField - -**Signature:** - -```typescript -optionalField?: boolean; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.optionalmember.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.optionalmember.md deleted file mode 100644 index 90b672df149..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.optionalmember.md +++ /dev/null @@ -1,17 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface7](./api-documenter-test.idocinterface7.md) > [optionalMember](./api-documenter-test.idocinterface7.optionalmember.md) - -## IDocInterface7.optionalMember() method - -Description of optionalMember - -**Signature:** - -```typescript -optionalMember?(): any; -``` -**Returns:** - -any - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.optionalreadonlyfield.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.optionalreadonlyfield.md deleted file mode 100644 index 5969b1df869..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.optionalreadonlyfield.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface7](./api-documenter-test.idocinterface7.md) > [optionalReadonlyField](./api-documenter-test.idocinterface7.optionalreadonlyfield.md) - -## IDocInterface7.optionalReadonlyField property - -Description of optionalReadonlyField - -**Signature:** - -```typescript -readonly optionalReadonlyField?: boolean; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.optionalundocumentedfield.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.optionalundocumentedfield.md deleted file mode 100644 index 163348c2f72..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.idocinterface7.optionalundocumentedfield.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface7](./api-documenter-test.idocinterface7.md) > [optionalUndocumentedField](./api-documenter-test.idocinterface7.optionalundocumentedfield.md) - -## IDocInterface7.optionalUndocumentedField property - -**Signature:** - -```typescript -optionalUndocumentedField?: boolean; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.md deleted file mode 100644 index ff7da3c5e33..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.md +++ /dev/null @@ -1,80 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) - -## api-documenter-test package - -api-extractor-test-05 - -This project tests various documentation generation scenarios and doc comment syntaxes. - -## Classes - -| Class | Description | -| --- | --- | -| [DecoratorExample](./api-documenter-test.decoratorexample.md) | | -| [DocBaseClass](./api-documenter-test.docbaseclass.md) |

Example base class

| -| [DocClass1](./api-documenter-test.docclass1.md) | This is an example class. | -| [DocClassInterfaceMerge](./api-documenter-test.docclassinterfacemerge.md) | Class that merges with interface | -| [Generic](./api-documenter-test.generic.md) | Generic class. | -| [SystemEvent](./api-documenter-test.systemevent.md) |

A class used to exposed events.

| - -## Abstract Classes - -| Abstract Class | Description | -| --- | --- | -| [AbstractClass](./api-documenter-test.abstractclass.md) | Some abstract class with abstract members. | - -## Enumerations - -| Enumeration | Description | -| --- | --- | -| [DocEnum](./api-documenter-test.docenum.md) |

Docs for DocEnum

| -| [DocEnumNamespaceMerge](./api-documenter-test.docenumnamespacemerge.md) | Enum that merges with namespace | - -## Functions - -| Function | Description | -| --- | --- | -| [exampleFunction(x, y)](./api-documenter-test.examplefunction.md) | An exported function with hyperlinked parameters and return value. | -| [yamlReferenceUniquenessTest()](./api-documenter-test.yamlreferenceuniquenesstest.md) | | - -## Interfaces - -| Interface | Description | -| --- | --- | -| [Constraint](./api-documenter-test.constraint.md) | Type parameter constraint used by test case below. | -| [DefaultType](./api-documenter-test.defaulttype.md) | Type parameter default type used by test case below. | -| [DocClassInterfaceMerge](./api-documenter-test.docclassinterfacemerge.md) | Interface that merges with class | -| [IDocInterface1](./api-documenter-test.idocinterface1.md) | | -| [IDocInterface2](./api-documenter-test.idocinterface2.md) | | -| [IDocInterface3](./api-documenter-test.idocinterface3.md) |

Some less common TypeScript declaration kinds.

| -| [IDocInterface4](./api-documenter-test.idocinterface4.md) |

Type union in an interface.

| -| [IDocInterface5](./api-documenter-test.idocinterface5.md) | Interface without inline tag to test custom TOC | -| [IDocInterface6](./api-documenter-test.idocinterface6.md) | Interface without inline tag to test custom TOC with injection | -| [IDocInterface7](./api-documenter-test.idocinterface7.md) | Interface for testing optional properties | - -## Namespaces - -| Namespace | Description | -| --- | --- | -| [DocEnumNamespaceMerge](./api-documenter-test.docenumnamespacemerge.md) | Namespace that merges with enum | -| [EcmaSmbols](./api-documenter-test.ecmasmbols.md) | A namespace containing an ECMAScript symbol | -| [OuterNamespace](./api-documenter-test.outernamespace.md) | A top-level namespace | - -## Variables - -| Variable | Description | -| --- | --- | -| [constVariable](./api-documenter-test.constvariable.md) | An exported variable declaration. | - -## Type Aliases - -| Type Alias | Description | -| --- | --- | -| [ExampleDuplicateTypeAlias](./api-documenter-test.exampleduplicatetypealias.md) | A type alias that has duplicate references. | -| [ExampleTypeAlias](./api-documenter-test.exampletypealias.md) | A type alias | -| [ExampleUnionTypeAlias](./api-documenter-test.exampleuniontypealias.md) | A type alias that references multiple other types. | -| [GenericTypeAlias](./api-documenter-test.generictypealias.md) | | -| [TypeAlias](./api-documenter-test.typealias.md) | | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.outernamespace.innernamespace.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.outernamespace.innernamespace.md deleted file mode 100644 index f4494372d73..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.outernamespace.innernamespace.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [OuterNamespace](./api-documenter-test.outernamespace.md) > [InnerNamespace](./api-documenter-test.outernamespace.innernamespace.md) - -## OuterNamespace.InnerNamespace namespace - -A nested namespace - -**Signature:** - -```typescript -namespace InnerNamespace -``` - -## Functions - -| Function | Description | -| --- | --- | -| [nestedFunction(x)](./api-documenter-test.outernamespace.innernamespace.nestedfunction.md) | A function inside a namespace | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.outernamespace.innernamespace.nestedfunction.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.outernamespace.innernamespace.nestedfunction.md deleted file mode 100644 index 11eec09bc35..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.outernamespace.innernamespace.nestedfunction.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [OuterNamespace](./api-documenter-test.outernamespace.md) > [InnerNamespace](./api-documenter-test.outernamespace.innernamespace.md) > [nestedFunction](./api-documenter-test.outernamespace.innernamespace.nestedfunction.md) - -## OuterNamespace.InnerNamespace.nestedFunction() function - -A function inside a namespace - -**Signature:** - -```typescript -function nestedFunction(x: number): number; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| x | number | | - -**Returns:** - -number - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.outernamespace.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.outernamespace.md deleted file mode 100644 index 70a81789486..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.outernamespace.md +++ /dev/null @@ -1,26 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [OuterNamespace](./api-documenter-test.outernamespace.md) - -## OuterNamespace namespace - -A top-level namespace - -**Signature:** - -```typescript -export declare namespace OuterNamespace -``` - -## Namespaces - -| Namespace | Description | -| --- | --- | -| [InnerNamespace](./api-documenter-test.outernamespace.innernamespace.md) | A nested namespace | - -## Variables - -| Variable | Description | -| --- | --- | -| [nestedVariable](./api-documenter-test.outernamespace.nestedvariable.md) | A variable exported from within a namespace. | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.outernamespace.nestedvariable.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.outernamespace.nestedvariable.md deleted file mode 100644 index b898150a9f5..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.outernamespace.nestedvariable.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [OuterNamespace](./api-documenter-test.outernamespace.md) > [nestedVariable](./api-documenter-test.outernamespace.nestedvariable.md) - -## OuterNamespace.nestedVariable variable - -A variable exported from within a namespace. - -**Signature:** - -```typescript -nestedVariable: boolean -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.systemevent.addhandler.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.systemevent.addhandler.md deleted file mode 100644 index ec269a54f4f..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.systemevent.addhandler.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [SystemEvent](./api-documenter-test.systemevent.md) > [addHandler](./api-documenter-test.systemevent.addhandler.md) - -## SystemEvent.addHandler() method - -Adds an handler for the event. - -**Signature:** - -```typescript -addHandler(handler: () => void): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| handler | () => void | | - -**Returns:** - -void - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.systemevent.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.systemevent.md deleted file mode 100644 index 844fef6c164..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.systemevent.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [SystemEvent](./api-documenter-test.systemevent.md) - -## SystemEvent class - -A class used to exposed events. - - -**Signature:** - -```typescript -export declare class SystemEvent -``` - -## Methods - -| Method | Modifiers | Description | -| --- | --- | --- | -| [addHandler(handler)](./api-documenter-test.systemevent.addhandler.md) | | Adds an handler for the event. | - diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.typealias.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.typealias.md deleted file mode 100644 index 560476c6753..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.typealias.md +++ /dev/null @@ -1,12 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [TypeAlias](./api-documenter-test.typealias.md) - -## TypeAlias type - - -**Signature:** - -```typescript -export type TypeAlias = number; -``` diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.yamlreferenceuniquenesstest.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.yamlreferenceuniquenesstest.md deleted file mode 100644 index 8cd0c20f399..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.yamlreferenceuniquenesstest.md +++ /dev/null @@ -1,16 +0,0 @@ - - -[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [yamlReferenceUniquenessTest](./api-documenter-test.yamlreferenceuniquenesstest.md) - -## yamlReferenceUniquenessTest() function - - -**Signature:** - -```typescript -export declare function yamlReferenceUniquenessTest(): IDocInterface1; -``` -**Returns:** - -[IDocInterface1](./api-documenter-test.idocinterface1.md) - diff --git a/build-tests/api-documenter-test/etc/markdown/index.md b/build-tests/api-documenter-test/etc/markdown/index.md deleted file mode 100644 index 1eb428e99a5..00000000000 --- a/build-tests/api-documenter-test/etc/markdown/index.md +++ /dev/null @@ -1,12 +0,0 @@ - - -[Home](./index.md) - -## API Reference - -## Packages - -| Package | Description | -| --- | --- | -| [api-documenter-test](./api-documenter-test.md) |

api-extractor-test-05

This project tests various documentation generation scenarios and doc comment syntaxes.

| - diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test.yml deleted file mode 100644 index 241ca97a87d..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test.yml +++ /dev/null @@ -1,70 +0,0 @@ -### YamlMime:TSPackage -uid: api-documenter-test! -name: api-documenter-test -type: package -summary: |- - api-extractor-test-05 - - This project tests various documentation generation scenarios and doc comment syntaxes. -classes: - - 'api-documenter-test!AbstractClass:class' - - 'api-documenter-test!DecoratorExample:class' - - 'api-documenter-test!DocBaseClass:class' - - 'api-documenter-test!DocClass1:class' - - 'api-documenter-test!DocClassInterfaceMerge:class' - - 'api-documenter-test!Generic:class' - - 'api-documenter-test!SystemEvent:class' -interfaces: - - 'api-documenter-test!Constraint:interface' - - 'api-documenter-test!DefaultType:interface' - - 'api-documenter-test!DocClassInterfaceMerge:interface' - - 'api-documenter-test!IDocInterface1:interface' - - 'api-documenter-test!IDocInterface2:interface' - - 'api-documenter-test!IDocInterface3:interface' - - 'api-documenter-test!IDocInterface4:interface' - - 'api-documenter-test!IDocInterface5:interface' - - 'api-documenter-test!IDocInterface6:interface' - - 'api-documenter-test!IDocInterface7:interface' -enums: - - 'api-documenter-test!DocEnum:enum' - - 'api-documenter-test!DocEnumNamespaceMerge:enum' -typeAliases: - - 'api-documenter-test!ExampleDuplicateTypeAlias:type' - - 'api-documenter-test!ExampleTypeAlias:type' - - 'api-documenter-test!ExampleUnionTypeAlias:type' - - 'api-documenter-test!GenericTypeAlias:type' - - 'api-documenter-test!TypeAlias:type' -functions: - - name: 'exampleFunction(x, y)' - uid: 'api-documenter-test!exampleFunction:function(1)' - package: api-documenter-test! - summary: An exported function with hyperlinked parameters and return value. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'export declare function exampleFunction(x: ExampleTypeAlias, y: number): IDocInterface1;' - parameters: - - id: x - description: an API item that should get hyperlinked - type: '' - - id: 'y' - description: a system type that should NOT get hyperlinked - type: number - return: - type: '' - description: an interface that should get hyperlinked - - name: yamlReferenceUniquenessTest() - uid: 'api-documenter-test!yamlReferenceUniquenessTest:function(1)' - package: api-documenter-test! - summary: '' - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'export declare function yamlReferenceUniquenessTest(): IDocInterface1;' - return: - type: '' - description: '' diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/abstractclass.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/abstractclass.yml deleted file mode 100644 index 7d215efa4f1..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/abstractclass.yml +++ /dev/null @@ -1,40 +0,0 @@ -### YamlMime:TSType -name: AbstractClass -uid: 'api-documenter-test!AbstractClass:class' -package: api-documenter-test! -fullName: AbstractClass -summary: Some abstract class with abstract members. -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: class -properties: - - name: property - uid: 'api-documenter-test!AbstractClass#property:member' - package: api-documenter-test! - fullName: property - summary: Some abstract property. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'protected abstract property: number;' - return: - type: number -methods: - - name: method() - uid: 'api-documenter-test!AbstractClass#method:member(1)' - package: api-documenter-test! - fullName: method() - summary: Some abstract method. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'abstract method(): void;' - return: - type: void - description: '' diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/constraint.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/constraint.yml deleted file mode 100644 index f15321decd5..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/constraint.yml +++ /dev/null @@ -1,11 +0,0 @@ -### YamlMime:TSType -name: Constraint -uid: 'api-documenter-test!Constraint:interface' -package: api-documenter-test! -fullName: Constraint -summary: Type parameter constraint used by test case below. -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: interface diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/decoratorexample.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/decoratorexample.yml deleted file mode 100644 index b0b8abcb0ef..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/decoratorexample.yml +++ /dev/null @@ -1,25 +0,0 @@ -### YamlMime:TSType -name: DecoratorExample -uid: 'api-documenter-test!DecoratorExample:class' -package: api-documenter-test! -fullName: DecoratorExample -summary: '' -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: class -properties: - - name: creationDate - uid: 'api-documenter-test!DecoratorExample#creationDate:member' - package: api-documenter-test! - fullName: creationDate - summary: The date when the record was created. - remarks: Here is a longer description of the property. - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'creationDate: Date;' - return: - type: Date diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/defaulttype.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/defaulttype.yml deleted file mode 100644 index 84a92f8792b..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/defaulttype.yml +++ /dev/null @@ -1,11 +0,0 @@ -### YamlMime:TSType -name: DefaultType -uid: 'api-documenter-test!DefaultType:interface' -package: api-documenter-test! -fullName: DefaultType -summary: Type parameter default type used by test case below. -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: interface diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docbaseclass.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docbaseclass.yml deleted file mode 100644 index d244597a8a5..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docbaseclass.yml +++ /dev/null @@ -1,38 +0,0 @@ -### YamlMime:TSType -name: DocBaseClass -uid: 'api-documenter-test!DocBaseClass:class' -package: api-documenter-test! -fullName: DocBaseClass -summary: Example base class -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: class -constructors: - - name: (constructor)() - uid: 'api-documenter-test!DocBaseClass:constructor(1)' - package: api-documenter-test! - fullName: (constructor)() - summary: The simple constructor for `DocBaseClass` - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: constructor(); - - name: (constructor)(x) - uid: 'api-documenter-test!DocBaseClass:constructor(2)' - package: api-documenter-test! - fullName: (constructor)(x) - summary: The overloaded constructor for `DocBaseClass` - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'constructor(x: number);' - parameters: - - id: x - description: '' - type: number diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docclass1.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docclass1.yml deleted file mode 100644 index c1149bb8c2e..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docclass1.yml +++ /dev/null @@ -1,287 +0,0 @@ -### YamlMime:TSType -name: DocClass1 -uid: 'api-documenter-test!DocClass1:class' -package: api-documenter-test! -fullName: DocClass1 -summary: This is an example class. -remarks: >- - [Link to overload 1](xref:api-documenter-test!DocClass1%23exampleFunction:member(1)) - - - [Link to overload 2](xref:api-documenter-test!DocClass1%23exampleFunction:member(2)) - - - - The constructor for this class is marked as internal. Third-party code should not call the constructor directly or - create subclasses that extend the `DocClass1` class. -example: [] -isPreview: false -isDeprecated: false -type: class -properties: - - name: multipleModifiersProperty - uid: 'api-documenter-test!DocClass1.multipleModifiersProperty:member' - package: api-documenter-test! - fullName: multipleModifiersProperty - summary: Some property with multiple modifiers. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'protected static readonly multipleModifiersProperty: boolean;' - return: - type: boolean - - name: protectedProperty - uid: 'api-documenter-test!DocClass1#protectedProperty:member' - package: api-documenter-test! - fullName: protectedProperty - summary: Some protected property. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'protected protectedProperty: string;' - return: - type: string - - name: readonlyProperty - uid: 'api-documenter-test!DocClass1#readonlyProperty:member' - package: api-documenter-test! - fullName: readonlyProperty - summary: '' - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'get readonlyProperty(): string;' - return: - type: string - - name: regularProperty - uid: 'api-documenter-test!DocClass1#regularProperty:member' - package: api-documenter-test! - fullName: regularProperty - summary: This is a regular property that happens to use the SystemEvent type. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'regularProperty: SystemEvent;' - return: - type: '' - - name: writeableProperty - uid: 'api-documenter-test!DocClass1#writeableProperty:member' - package: api-documenter-test! - fullName: writeableProperty - summary: '' - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: |- - get writeableProperty(): string; - - set writeableProperty(value: string); - return: - type: string - - name: writeonlyProperty - uid: 'api-documenter-test!DocClass1#writeonlyProperty:member' - package: api-documenter-test! - fullName: writeonlyProperty - summary: API Extractor will surface an `ae-missing-getter` finding for this property. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'set writeonlyProperty(value: string);' - return: - type: string -methods: - - name: deprecatedExample() - uid: 'api-documenter-test!DocClass1#deprecatedExample:member(1)' - package: api-documenter-test! - fullName: deprecatedExample() - summary: '' - remarks: '' - example: [] - isPreview: false - isDeprecated: true - customDeprecatedMessage: Use `otherThing()` instead. - syntax: - content: 'deprecatedExample(): void;' - return: - type: void - description: '' - - name: 'exampleFunction(a, b)' - uid: 'api-documenter-test!DocClass1#exampleFunction:member(1)' - package: api-documenter-test! - fullName: 'exampleFunction(a, b)' - summary: This is an overloaded function. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'exampleFunction(a: string, b: string): string;' - parameters: - - id: a - description: the first string - type: string - - id: b - description: the second string - type: string - return: - type: string - description: '' - - name: exampleFunction(x) - uid: 'api-documenter-test!DocClass1#exampleFunction:member(2)' - package: api-documenter-test! - fullName: exampleFunction(x) - summary: This is also an overloaded function. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'exampleFunction(x: number): number;' - parameters: - - id: x - description: the number - type: number - return: - type: number - description: '' - - name: genericWithConstraintAndDefault(x) - uid: 'api-documenter-test!DocClass1#genericWithConstraintAndDefault:member(1)' - package: api-documenter-test! - fullName: genericWithConstraintAndDefault(x) - summary: This is a method with a complex type parameter. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'genericWithConstraintAndDefault(x: T): void;' - parameters: - - id: x - description: some generic parameter. - type: T - return: - type: void - description: '' - - name: interestingEdgeCases() - uid: 'api-documenter-test!DocClass1#interestingEdgeCases:member(1)' - package: api-documenter-test! - fullName: interestingEdgeCases() - summary: |- - Example: "{ \\"maxItemsToShow\\": 123 }" - - The regular expression used to validate the constraints is /^\[a-zA-Z0-9\\-\_\]+$/ - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'interestingEdgeCases(): void;' - return: - type: void - description: '' - - name: optionalParamFunction(x) - uid: 'api-documenter-test!DocClass1#optionalParamFunction:member(1)' - package: api-documenter-test! - fullName: optionalParamFunction(x) - summary: This is a function with an optional parameter. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'optionalParamFunction(x?: number): void;' - parameters: - - id: x - description: the number - type: number - return: - type: void - description: '' - - name: 'sumWithExample(x, y)' - uid: 'api-documenter-test!DocClass1.sumWithExample:member(1)' - package: api-documenter-test! - fullName: 'sumWithExample(x, y)' - summary: Returns the sum of two numbers. - remarks: This illustrates usage of the `@example` block tag. - example: - - |- - Here's a simple example: - - ``` - // Prints "2": - console.log(DocClass1.sumWithExample(1,1)); - ``` - - |- - Here's an example with negative numbers: - - ``` - // Prints "0": - console.log(DocClass1.sumWithExample(1,-1)); - ``` - isPreview: false - isDeprecated: false - syntax: - content: 'static sumWithExample(x: number, y: number): number;' - parameters: - - id: x - description: the first number to add - type: number - - id: 'y' - description: the second number to add - type: number - return: - type: number - description: the sum of the two numbers - - name: tableExample() - uid: 'api-documenter-test!DocClass1#tableExample:member(1)' - package: api-documenter-test! - fullName: tableExample() - summary: 'An example with tables:' - remarks:
John Doe
- example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'tableExample(): void;' - return: - type: void - description: '' -events: - - name: malformedEvent - uid: 'api-documenter-test!DocClass1#malformedEvent:member' - package: api-documenter-test! - fullName: malformedEvent - summary: This event should have been marked as readonly. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'malformedEvent: SystemEvent;' - return: - type: '' - - name: modifiedEvent - uid: 'api-documenter-test!DocClass1#modifiedEvent:member' - package: api-documenter-test! - fullName: modifiedEvent - summary: This event is fired whenever the object is modified. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'readonly modifiedEvent: SystemEvent;' - return: - type: '' -extends: '' diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docclassinterfacemerge-class.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docclassinterfacemerge-class.yml deleted file mode 100644 index f1b69cea505..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docclassinterfacemerge-class.yml +++ /dev/null @@ -1,14 +0,0 @@ -### YamlMime:TSType -name: DocClassInterfaceMerge -uid: 'api-documenter-test!DocClassInterfaceMerge:class' -package: api-documenter-test! -fullName: DocClassInterfaceMerge -summary: Class that merges with interface -remarks: |- - [Link to class](xref:api-documenter-test!DocClassInterfaceMerge:class) - - [Link to interface](xref:api-documenter-test!DocClassInterfaceMerge:interface) -example: [] -isPreview: false -isDeprecated: false -type: class diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docclassinterfacemerge-interface.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docclassinterfacemerge-interface.yml deleted file mode 100644 index 30076403cfa..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docclassinterfacemerge-interface.yml +++ /dev/null @@ -1,11 +0,0 @@ -### YamlMime:TSType -name: DocClassInterfaceMerge -uid: 'api-documenter-test!DocClassInterfaceMerge:interface' -package: api-documenter-test! -fullName: DocClassInterfaceMerge -summary: Interface that merges with class -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: interface diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docenum.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docenum.yml deleted file mode 100644 index 5a39c3aa58b..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docenum.yml +++ /dev/null @@ -1,29 +0,0 @@ -### YamlMime:TSEnum -name: DocEnum -uid: 'api-documenter-test!DocEnum:enum' -package: api-documenter-test! -fullName: DocEnum -summary: Docs for DocEnum -remarks: '' -example: [] -isPreview: false -isDeprecated: false -fields: - - name: One - uid: 'api-documenter-test!DocEnum.One:member' - package: api-documenter-test! - summary: These are some docs for One - value: '1' - - name: Two - uid: 'api-documenter-test!DocEnum.Two:member' - package: api-documenter-test! - summary: |- - These are some docs for Two. - - [DocEnum.One](xref:api-documenter-test!DocEnum.One:member) is a direct link to another enum member. - value: '2' - - name: Zero - uid: 'api-documenter-test!DocEnum.Zero:member' - package: api-documenter-test! - summary: These are some docs for Zero - value: '0' diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docenumnamespacemerge-enum.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docenumnamespacemerge-enum.yml deleted file mode 100644 index 0f6d4f6bc18..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docenumnamespacemerge-enum.yml +++ /dev/null @@ -1,26 +0,0 @@ -### YamlMime:TSEnum -name: DocEnumNamespaceMerge -uid: 'api-documenter-test!DocEnumNamespaceMerge:enum' -package: api-documenter-test! -fullName: DocEnumNamespaceMerge -summary: Enum that merges with namespace -remarks: |- - [Link to enum](xref:api-documenter-test!DocEnumNamespaceMerge:enum) - - [Link to namespace](xref:api-documenter-test!DocEnumNamespaceMerge:namespace) - - [Link to function inside namespace](xref:api-documenter-test!DocEnumNamespaceMerge.exampleFunction:function(1)) -example: [] -isPreview: false -isDeprecated: false -fields: - - name: Left - uid: 'api-documenter-test!DocEnumNamespaceMerge.Left:member' - package: api-documenter-test! - summary: These are some docs for Left - value: '0' - - name: Right - uid: 'api-documenter-test!DocEnumNamespaceMerge.Right:member' - package: api-documenter-test! - summary: These are some docs for Right - value: '1' diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docenumnamespacemerge-namespace.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docenumnamespacemerge-namespace.yml deleted file mode 100644 index 7e3911f28db..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docenumnamespacemerge-namespace.yml +++ /dev/null @@ -1,26 +0,0 @@ -### YamlMime:UniversalReference -items: - - uid: 'api-documenter-test!DocEnumNamespaceMerge:namespace' - summary: Namespace that merges with enum - name: DocEnumNamespaceMerge - fullName: DocEnumNamespaceMerge - langs: - - typeScript - type: namespace - package: api-documenter-test! - children: - - 'api-documenter-test!DocEnumNamespaceMerge.exampleFunction:function(1)' - - uid: 'api-documenter-test!DocEnumNamespaceMerge.exampleFunction:function(1)' - summary: This is a function inside of a namespace that merges with an enum. - name: exampleFunction() - fullName: DocEnumNamespaceMerge.exampleFunction() - langs: - - typeScript - namespace: 'api-documenter-test!DocEnumNamespaceMerge:namespace' - type: function - syntax: - content: 'function exampleFunction(): void;' - return: - type: - - void - description: '' diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/ecmasmbols.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/ecmasmbols.yml deleted file mode 100644 index 1cef2ce3d4c..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/ecmasmbols.yml +++ /dev/null @@ -1,25 +0,0 @@ -### YamlMime:UniversalReference -items: - - uid: 'api-documenter-test!EcmaSmbols:namespace' - summary: A namespace containing an ECMAScript symbol - name: EcmaSmbols - fullName: EcmaSmbols - langs: - - typeScript - type: namespace - package: api-documenter-test! - children: - - 'api-documenter-test!EcmaSmbols.example:var' - - uid: 'api-documenter-test!EcmaSmbols.example:var' - summary: An ECMAScript symbol - name: example - fullName: EcmaSmbols.example - langs: - - typeScript - namespace: 'api-documenter-test!EcmaSmbols:namespace' - type: variable - syntax: - content: 'example: unique symbol' - return: - type: - - unique symbol diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/exampleduplicatetypealias.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/exampleduplicatetypealias.yml deleted file mode 100644 index 9527b848c90..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/exampleduplicatetypealias.yml +++ /dev/null @@ -1,11 +0,0 @@ -### YamlMime:TSTypeAlias -name: ExampleDuplicateTypeAlias -uid: 'api-documenter-test!ExampleDuplicateTypeAlias:type' -package: api-documenter-test! -fullName: ExampleDuplicateTypeAlias -summary: A type alias that has duplicate references. -remarks: '' -example: [] -isPreview: false -isDeprecated: false -syntax: export type ExampleDuplicateTypeAlias = SystemEvent | typeof SystemEvent; diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/exampletypealias.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/exampletypealias.yml deleted file mode 100644 index f3dcfaaf267..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/exampletypealias.yml +++ /dev/null @@ -1,11 +0,0 @@ -### YamlMime:TSTypeAlias -name: ExampleTypeAlias -uid: 'api-documenter-test!ExampleTypeAlias:type' -package: api-documenter-test! -fullName: ExampleTypeAlias -summary: A type alias -remarks: '' -example: [] -isPreview: false -isDeprecated: false -syntax: export type ExampleTypeAlias = Promise; diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/exampleuniontypealias.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/exampleuniontypealias.yml deleted file mode 100644 index 282875feb1c..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/exampleuniontypealias.yml +++ /dev/null @@ -1,11 +0,0 @@ -### YamlMime:TSTypeAlias -name: ExampleUnionTypeAlias -uid: 'api-documenter-test!ExampleUnionTypeAlias:type' -package: api-documenter-test! -fullName: ExampleUnionTypeAlias -summary: A type alias that references multiple other types. -remarks: '' -example: [] -isPreview: false -isDeprecated: false -syntax: export type ExampleUnionTypeAlias = IDocInterface1 | IDocInterface3; diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/generic.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/generic.yml deleted file mode 100644 index a367f34ad41..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/generic.yml +++ /dev/null @@ -1,11 +0,0 @@ -### YamlMime:TSType -name: Generic -uid: 'api-documenter-test!Generic:class' -package: api-documenter-test! -fullName: Generic -summary: Generic class. -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: class diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/generictypealias.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/generictypealias.yml deleted file mode 100644 index 9dfddbb4304..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/generictypealias.yml +++ /dev/null @@ -1,11 +0,0 @@ -### YamlMime:TSTypeAlias -name: GenericTypeAlias -uid: 'api-documenter-test!GenericTypeAlias:type' -package: api-documenter-test! -fullName: GenericTypeAlias -summary: '' -remarks: '' -example: [] -isPreview: false -isDeprecated: false -syntax: 'export type GenericTypeAlias = T[];' diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface1.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface1.yml deleted file mode 100644 index 68d90cf143f..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface1.yml +++ /dev/null @@ -1,25 +0,0 @@ -### YamlMime:TSType -name: IDocInterface1 -uid: 'api-documenter-test!IDocInterface1:interface' -package: api-documenter-test! -fullName: IDocInterface1 -summary: '' -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: interface -properties: - - name: regularProperty - uid: 'api-documenter-test!IDocInterface1#regularProperty:member' - package: api-documenter-test! - fullName: regularProperty - summary: Does something - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'regularProperty: SystemEvent;' - return: - type: '' diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface2.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface2.yml deleted file mode 100644 index e7abfc60d9f..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface2.yml +++ /dev/null @@ -1,28 +0,0 @@ -### YamlMime:TSType -name: IDocInterface2 -uid: 'api-documenter-test!IDocInterface2:interface' -package: api-documenter-test! -fullName: IDocInterface2 -summary: '' -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: interface -methods: - - name: deprecatedExample() - uid: 'api-documenter-test!IDocInterface2#deprecatedExample:member(1)' - package: api-documenter-test! - fullName: deprecatedExample() - summary: '' - remarks: '' - example: [] - isPreview: false - isDeprecated: true - customDeprecatedMessage: Use `otherThing()` instead. - syntax: - content: 'deprecatedExample(): void;' - return: - type: void - description: '' -extends: '' diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface3.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface3.yml deleted file mode 100644 index f7ec0be3e0b..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface3.yml +++ /dev/null @@ -1,51 +0,0 @@ -### YamlMime:TSType -name: IDocInterface3 -uid: 'api-documenter-test!IDocInterface3:interface' -package: api-documenter-test! -fullName: IDocInterface3 -summary: Some less common TypeScript declaration kinds. -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: interface -properties: - - name: '"[not.a.symbol]"' - uid: 'api-documenter-test!IDocInterface3#"[not.a.symbol]":member' - package: api-documenter-test! - fullName: '"[not.a.symbol]"' - summary: An identifier that does need quotes. It misleadingly looks like an ECMAScript symbol. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: '"[not.a.symbol]": string;' - return: - type: string - - name: '[EcmaSmbols.example]' - uid: 'api-documenter-test!IDocInterface3#[EcmaSmbols.example]:member' - package: api-documenter-test! - fullName: '[EcmaSmbols.example]' - summary: ECMAScript symbol - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: '[EcmaSmbols.example]: string;' - return: - type: string - - name: redundantQuotes - uid: 'api-documenter-test!IDocInterface3#redundantQuotes:member' - package: api-documenter-test! - fullName: redundantQuotes - summary: A quoted identifier with redundant quotes. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: '"redundantQuotes": string;' - return: - type: string diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface4.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface4.yml deleted file mode 100644 index 1cc811d84c8..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface4.yml +++ /dev/null @@ -1,70 +0,0 @@ -### YamlMime:TSType -name: IDocInterface4 -uid: 'api-documenter-test!IDocInterface4:interface' -package: api-documenter-test! -fullName: IDocInterface4 -summary: Type union in an interface. -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: interface -properties: - - name: Context - uid: 'api-documenter-test!IDocInterface4#Context:member' - package: api-documenter-test! - fullName: Context - summary: Test newline rendering when code blocks are used in tables - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: |- - Context: ({ children }: { - children: string; - }) => boolean; - return: - type: |- - ({ children }: { - children: string; - }) => boolean - - name: generic - uid: 'api-documenter-test!IDocInterface4#generic:member' - package: api-documenter-test! - fullName: generic - summary: make sure html entities are escaped in tables. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'generic: Generic;' - return: - type: '<number>' - - name: numberOrFunction - uid: 'api-documenter-test!IDocInterface4#numberOrFunction:member' - package: api-documenter-test! - fullName: numberOrFunction - summary: a union type with a function - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'numberOrFunction: number | (() => number);' - return: - type: number | (() => number) - - name: stringOrNumber - uid: 'api-documenter-test!IDocInterface4#stringOrNumber:member' - package: api-documenter-test! - fullName: stringOrNumber - summary: a union type - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'stringOrNumber: string | number;' - return: - type: string | number diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface5.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface5.yml deleted file mode 100644 index 76dacaabae5..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface5.yml +++ /dev/null @@ -1,25 +0,0 @@ -### YamlMime:TSType -name: IDocInterface5 -uid: 'api-documenter-test!IDocInterface5:interface' -package: api-documenter-test! -fullName: IDocInterface5 -summary: Interface without inline tag to test custom TOC -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: interface -properties: - - name: regularProperty - uid: 'api-documenter-test!IDocInterface5#regularProperty:member' - package: api-documenter-test! - fullName: regularProperty - summary: Property of type string that does something - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'regularProperty: string;' - return: - type: string diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface6.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface6.yml deleted file mode 100644 index 8d1e14211d7..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface6.yml +++ /dev/null @@ -1,117 +0,0 @@ -### YamlMime:TSType -name: IDocInterface6 -uid: 'api-documenter-test!IDocInterface6:interface' -package: api-documenter-test! -fullName: IDocInterface6 -summary: Interface without inline tag to test custom TOC with injection -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: interface -properties: - - name: arrayProperty - uid: 'api-documenter-test!IDocInterface6#arrayProperty:member' - package: api-documenter-test! - fullName: arrayProperty - summary: '' - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'arrayProperty: IDocInterface1[];' - return: - type: '[]' - - name: intersectionProperty - uid: 'api-documenter-test!IDocInterface6#intersectionProperty:member' - package: api-documenter-test! - fullName: intersectionProperty - summary: '' - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'intersectionProperty: IDocInterface1 & IDocInterface2;' - return: - type: >- - & - - name: regularProperty - uid: 'api-documenter-test!IDocInterface6#regularProperty:member' - package: api-documenter-test! - fullName: regularProperty - summary: Property of type number that does something - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'regularProperty: number;' - return: - type: number - - name: tupleProperty - uid: 'api-documenter-test!IDocInterface6#tupleProperty:member' - package: api-documenter-test! - fullName: tupleProperty - summary: '' - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'tupleProperty: [IDocInterface1, IDocInterface2];' - return: - type: >- - [, ] - - name: typeReferenceProperty - uid: 'api-documenter-test!IDocInterface6#typeReferenceProperty:member' - package: api-documenter-test! - fullName: typeReferenceProperty - summary: '' - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'typeReferenceProperty: Generic;' - return: - type: >- - <> - - name: unionProperty - uid: 'api-documenter-test!IDocInterface6#unionProperty:member' - package: api-documenter-test! - fullName: unionProperty - summary: '' - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'unionProperty: IDocInterface1 | IDocInterface2;' - return: - type: >- - | -methods: - - name: genericReferenceMethod(x) - uid: 'api-documenter-test!IDocInterface6#genericReferenceMethod:member(1)' - package: api-documenter-test! - fullName: genericReferenceMethod(x) - summary: '' - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'genericReferenceMethod(x: T): T;' - parameters: - - id: x - description: '' - type: T - return: - type: T - description: '' diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface7.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface7.yml deleted file mode 100644 index 1c1f68bcfcb..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/idocinterface7.yml +++ /dev/null @@ -1,66 +0,0 @@ -### YamlMime:TSType -name: IDocInterface7 -uid: 'api-documenter-test!IDocInterface7:interface' -package: api-documenter-test! -fullName: IDocInterface7 -summary: Interface for testing optional properties -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: interface -properties: - - name: optionalField - uid: 'api-documenter-test!IDocInterface7#optionalField:member' - package: api-documenter-test! - fullName: optionalField - summary: Description of optionalField - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'optionalField?: boolean;' - return: - type: boolean - - name: optionalReadonlyField - uid: 'api-documenter-test!IDocInterface7#optionalReadonlyField:member' - package: api-documenter-test! - fullName: optionalReadonlyField - summary: Description of optionalReadonlyField - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'readonly optionalReadonlyField?: boolean;' - return: - type: boolean - - name: optionalUndocumentedField - uid: 'api-documenter-test!IDocInterface7#optionalUndocumentedField:member' - package: api-documenter-test! - fullName: optionalUndocumentedField - summary: '' - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'optionalUndocumentedField?: boolean;' - return: - type: boolean -methods: - - name: optionalMember() - uid: 'api-documenter-test!IDocInterface7#optionalMember:member(1)' - package: api-documenter-test! - fullName: optionalMember() - summary: Description of optionalMember - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'optionalMember?(): any;' - return: - type: any - description: '' diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/outernamespace.innernamespace.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/outernamespace.innernamespace.yml deleted file mode 100644 index 0a9ec85f376..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/outernamespace.innernamespace.yml +++ /dev/null @@ -1,32 +0,0 @@ -### YamlMime:UniversalReference -items: - - uid: 'api-documenter-test!OuterNamespace.InnerNamespace:namespace' - summary: A nested namespace - name: OuterNamespace.InnerNamespace - fullName: OuterNamespace.InnerNamespace - langs: - - typeScript - type: namespace - package: api-documenter-test! - children: - - 'api-documenter-test!OuterNamespace.InnerNamespace.nestedFunction:function(1)' - - uid: 'api-documenter-test!OuterNamespace.InnerNamespace.nestedFunction:function(1)' - summary: A function inside a namespace - name: nestedFunction(x) - fullName: OuterNamespace.InnerNamespace.nestedFunction(x) - langs: - - typeScript - namespace: 'api-documenter-test!OuterNamespace.InnerNamespace:namespace' - type: function - syntax: - content: 'function nestedFunction(x: number): number;' - return: - type: - - number - description: '' - parameters: - - id: x - description: '' - type: - - number - optional: false diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/outernamespace.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/outernamespace.yml deleted file mode 100644 index 44145cff45b..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/outernamespace.yml +++ /dev/null @@ -1,25 +0,0 @@ -### YamlMime:UniversalReference -items: - - uid: 'api-documenter-test!OuterNamespace:namespace' - summary: A top-level namespace - name: OuterNamespace - fullName: OuterNamespace - langs: - - typeScript - type: namespace - package: api-documenter-test! - children: - - 'api-documenter-test!OuterNamespace.nestedVariable:var' - - uid: 'api-documenter-test!OuterNamespace.nestedVariable:var' - summary: A variable exported from within a namespace. - name: nestedVariable - fullName: OuterNamespace.nestedVariable - langs: - - typeScript - namespace: 'api-documenter-test!OuterNamespace:namespace' - type: variable - syntax: - content: 'nestedVariable: boolean' - return: - type: - - boolean diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/systemevent.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/systemevent.yml deleted file mode 100644 index da8e4dcce39..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/systemevent.yml +++ /dev/null @@ -1,30 +0,0 @@ -### YamlMime:TSType -name: SystemEvent -uid: 'api-documenter-test!SystemEvent:class' -package: api-documenter-test! -fullName: SystemEvent -summary: A class used to exposed events. -remarks: '' -example: [] -isPreview: false -isDeprecated: false -type: class -methods: - - name: addHandler(handler) - uid: 'api-documenter-test!SystemEvent#addHandler:member(1)' - package: api-documenter-test! - fullName: addHandler(handler) - summary: Adds an handler for the event. - remarks: '' - example: [] - isPreview: false - isDeprecated: false - syntax: - content: 'addHandler(handler: () => void): void;' - parameters: - - id: handler - description: '' - type: () => void - return: - type: void - description: '' diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/typealias.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/typealias.yml deleted file mode 100644 index 242b7ab1b18..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/typealias.yml +++ /dev/null @@ -1,11 +0,0 @@ -### YamlMime:TSTypeAlias -name: TypeAlias -uid: 'api-documenter-test!TypeAlias:type' -package: api-documenter-test! -fullName: TypeAlias -summary: '' -remarks: '' -example: [] -isPreview: false -isDeprecated: false -syntax: export type TypeAlias = number; diff --git a/build-tests/api-documenter-test/etc/yaml/toc.yml b/build-tests/api-documenter-test/etc/yaml/toc.yml deleted file mode 100644 index 22523f8276b..00000000000 --- a/build-tests/api-documenter-test/etc/yaml/toc.yml +++ /dev/null @@ -1,81 +0,0 @@ -items: - - name: Test api-documenter - href: ~/homepage/homepage.md - - name: Test Sample for AD - href: api-documenter-test - extended: true - items: - - name: Classes - items: - - name: DocBaseClass - items: - - name: DocBaseClass - uid: 'api-documenter-test!DocBaseClass:class' - - name: IDocInterface1 - uid: 'api-documenter-test!IDocInterface1:interface' - - name: IDocInterface2 - uid: 'api-documenter-test!IDocInterface2:interface' - - name: DocClass1 - items: - - name: DocClass1 - uid: 'api-documenter-test!DocClass1:class' - - name: IDocInterface3 - uid: 'api-documenter-test!IDocInterface3:interface' - - name: IDocInterface4 - uid: 'api-documenter-test!IDocInterface4:interface' - - name: Interfaces - items: - - name: Interface5 - items: - - name: IDocInterface5 - uid: 'api-documenter-test!IDocInterface5:interface' - - name: Interface6 - items: - - name: InjectedCustomInterface - uid: customUid - - name: IDocInterface6 - uid: 'api-documenter-test!IDocInterface6:interface' - - name: References - items: - - name: InjectedCustomItem - uid: customUrl - - name: AbstractClass - uid: 'api-documenter-test!AbstractClass:class' - - name: Constraint - uid: 'api-documenter-test!Constraint:interface' - - name: DecoratorExample - uid: 'api-documenter-test!DecoratorExample:class' - - name: DefaultType - uid: 'api-documenter-test!DefaultType:interface' - - name: DocClassInterfaceMerge (Class) - uid: 'api-documenter-test!DocClassInterfaceMerge:class' - - name: DocClassInterfaceMerge (Interface) - uid: 'api-documenter-test!DocClassInterfaceMerge:interface' - - name: DocEnum - uid: 'api-documenter-test!DocEnum:enum' - - name: DocEnumNamespaceMerge (Enum) - uid: 'api-documenter-test!DocEnumNamespaceMerge:enum' - - name: DocEnumNamespaceMerge (Namespace) - uid: 'api-documenter-test!DocEnumNamespaceMerge:namespace' - - name: EcmaSmbols - uid: 'api-documenter-test!EcmaSmbols:namespace' - - name: ExampleDuplicateTypeAlias - uid: 'api-documenter-test!ExampleDuplicateTypeAlias:type' - - name: ExampleTypeAlias - uid: 'api-documenter-test!ExampleTypeAlias:type' - - name: ExampleUnionTypeAlias - uid: 'api-documenter-test!ExampleUnionTypeAlias:type' - - name: Generic - uid: 'api-documenter-test!Generic:class' - - name: GenericTypeAlias - uid: 'api-documenter-test!GenericTypeAlias:type' - - name: IDocInterface7 - uid: 'api-documenter-test!IDocInterface7:interface' - - name: OuterNamespace - uid: 'api-documenter-test!OuterNamespace:namespace' - - name: OuterNamespace.InnerNamespace - uid: 'api-documenter-test!OuterNamespace.InnerNamespace:namespace' - - name: SystemEvent - uid: 'api-documenter-test!SystemEvent:class' - - name: TypeAlias - uid: 'api-documenter-test!TypeAlias:type' diff --git a/build-tests/api-documenter-test/package.json b/build-tests/api-documenter-test/package.json index 401ad54b239..59890f2be8e 100644 --- a/build-tests/api-documenter-test/package.json +++ b/build-tests/api-documenter-test/package.json @@ -3,18 +3,41 @@ "description": "Building this project is a regression test for api-documenter", "version": "1.0.0", "private": true, - "main": "lib/index.js", - "typings": "lib/index.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "scripts": { - "build": "node build.js", - "_phase:build": "node build.js" + "build": "heft build --clean", + "test": "heft test", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" }, "devDependencies": { "@microsoft/api-documenter": "workspace:*", "@microsoft/api-extractor": "workspace:*", - "@types/jest": "29.2.5", - "@types/node": "14.18.36", - "fs-extra": "~7.0.1", - "typescript": "~5.0.4" + "@rushstack/heft": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" } } diff --git a/build-tests/api-documenter-test/src/AbstractClass.ts b/build-tests/api-documenter-test/src/AbstractClass.ts index bfb5f3d4dd9..db9e37f4865 100644 --- a/build-tests/api-documenter-test/src/AbstractClass.ts +++ b/build-tests/api-documenter-test/src/AbstractClass.ts @@ -1,10 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + /** * Some abstract class with abstract members. * @public */ export abstract class AbstractClass { - /** Some abstract method. */ - public abstract method(): void; /** Some abstract property. */ protected abstract property: number; + /** Some abstract method. */ + public abstract method(): void; } diff --git a/build-tests/api-documenter-test/src/DecoratorExample.ts b/build-tests/api-documenter-test/src/DecoratorExample.ts index aa97f1db0a9..affcfcd961d 100644 --- a/build-tests/api-documenter-test/src/DecoratorExample.ts +++ b/build-tests/api-documenter-test/src/DecoratorExample.ts @@ -1,7 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-explicit-any function jsonSerialized(target: any, propertyKey: string) {} +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type function jsonFormat(value: string) { - return function (target: Object, propertyKey: string) {}; + return function (target: object, propertyKey: string) {}; } /** @public */ diff --git a/build-tests/api-documenter-test/src/DocClass1.ts b/build-tests/api-documenter-test/src/DocClass1.ts index 221cc7862c2..1f7100236aa 100644 --- a/build-tests/api-documenter-test/src/DocClass1.ts +++ b/build-tests/api-documenter-test/src/DocClass1.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + /** * A class used to exposed events. * @public @@ -57,11 +60,12 @@ export interface IDocInterface2 extends IDocInterface1 { * A namespace containing an ECMAScript symbol * @public */ -export namespace EcmaSmbols { +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace EcmaSymbols { /** * An ECMAScript symbol */ - export const example: unique symbol = Symbol('EcmaSmbols.exampleSymbol'); + export const example: unique symbol = Symbol('EcmaSymbols.exampleSymbol'); } /** @@ -90,7 +94,7 @@ export interface IDocInterface3 { /** * ECMAScript symbol */ - [EcmaSmbols.example]: string; + [EcmaSymbols.example]: string; /** * A quoted identifier with redundant quotes. @@ -109,6 +113,7 @@ export interface IDocInterface3 { * Generic class. * @public */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars export class Generic {} /** @@ -141,12 +146,14 @@ export interface IDocInterface4 { * Type parameter constraint used by test case below. * @public */ +// eslint-disable-next-line @typescript-eslint/naming-convention export interface Constraint {} /** * Type parameter default type used by test case below. * @public */ +// eslint-disable-next-line @typescript-eslint/naming-convention export interface DefaultType {} /** @@ -161,14 +168,6 @@ export interface DefaultType {} * {@docCategory DocClass1} */ export class DocClass1 extends DocBaseClass implements IDocInterface1, IDocInterface2 { - /** - * An internal class constructor. - * @internal - */ - public constructor(name: string) { - super(); - } - /** * Some protected property. */ @@ -179,6 +178,31 @@ export class DocClass1 extends DocBaseClass implements IDocInterface1, IDocInter */ protected static readonly multipleModifiersProperty: boolean; + /** + * This event is fired whenever the object is modified. + * @eventProperty + */ + public readonly modifiedEvent: SystemEvent; + + /** + * This event should have been marked as readonly. + * @eventProperty + */ + public malformedEvent: SystemEvent; + + /** + * This is a regular property that happens to use the SystemEvent type. + */ + public regularProperty: SystemEvent; + + /** + * An internal class constructor. + * @internal + */ + public constructor(name: string) { + super(); + } + /** * This is an overloaded function. * @param a - the first string @@ -189,12 +213,16 @@ export class DocClass1 extends DocBaseClass implements IDocInterface1, IDocInter * * @throws The second throws line */ + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility exampleFunction(a: string, b: string): string; /** * This is also an overloaded function. * @param x - the number + * + * @defaultValue 123 */ + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility exampleFunction(x: number): number; public exampleFunction(x: number | string, y?: string): string | number { @@ -219,25 +247,9 @@ export class DocClass1 extends DocBaseClass implements IDocInterface1, IDocInter /** * API Extractor will surface an `ae-missing-getter` finding for this property. */ + // eslint-disable-next-line accessor-pairs public set writeonlyProperty(value: string) {} - /** - * This event is fired whenever the object is modified. - * @eventProperty - */ - public readonly modifiedEvent: SystemEvent; - - /** - * This event should have been marked as readonly. - * @eventProperty - */ - public malformedEvent: SystemEvent; - - /** - * This is a regular property that happens to use the SystemEvent type. - */ - public regularProperty: SystemEvent; - /** * An example with tables: * @remarks @@ -248,6 +260,7 @@ export class DocClass1 extends DocBaseClass implements IDocInterface1, IDocInter * * */ + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility tableExample(): void {} /** @@ -255,6 +268,7 @@ export class DocClass1 extends DocBaseClass implements IDocInterface1, IDocInter * * The regular expression used to validate the constraints is /^[a-zA-Z0-9\\-_]+$/ */ + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility interestingEdgeCases(): void {} /** @@ -293,6 +307,7 @@ export class DocClass1 extends DocBaseClass implements IDocInterface1, IDocInter * This is a method with a complex type parameter. * @param x - some generic parameter. */ + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type public genericWithConstraintAndDefault(x: T) {} } @@ -303,6 +318,8 @@ export class DocClass1 extends DocBaseClass implements IDocInterface1, IDocInter export interface IDocInterface5 { /** * Property of type string that does something + * + * @defaultValue "Hello World" */ regularProperty: string; } @@ -364,4 +381,5 @@ export class DocClassInterfaceMerge {} * Interface that merges with class * @public */ +// eslint-disable-next-line @typescript-eslint/naming-convention export interface DocClassInterfaceMerge {} diff --git a/build-tests/api-documenter-test/src/DocEnums.ts b/build-tests/api-documenter-test/src/DocEnums.ts index 7dddb5ee2a7..63c8d2c2f3c 100644 --- a/build-tests/api-documenter-test/src/DocEnums.ts +++ b/build-tests/api-documenter-test/src/DocEnums.ts @@ -53,6 +53,7 @@ export enum DocEnumNamespaceMerge { * Namespace that merges with enum * @public */ +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace DocEnumNamespaceMerge { /** * This is a function inside of a namespace that merges with an enum. diff --git a/build-tests/api-documenter-test/src/index.ts b/build-tests/api-documenter-test/src/index.ts index bc08d496e2b..b4a1fefa50d 100644 --- a/build-tests/api-documenter-test/src/index.ts +++ b/build-tests/api-documenter-test/src/index.ts @@ -10,9 +10,11 @@ * @packageDocumentation */ +// eslint-disable-next-line no-restricted-syntax export * from './DocClass1'; +// eslint-disable-next-line no-restricted-syntax export * from './DocEnums'; -import { IDocInterface1, IDocInterface3, SystemEvent } from './DocClass1'; +import type { IDocInterface1, IDocInterface3, SystemEvent } from './DocClass1'; export { DecoratorExample } from './DecoratorExample'; @@ -58,10 +60,12 @@ export function exampleFunction(x: ExampleTypeAlias, y: number): IDocInterface1 * A top-level namespace * @public */ +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace OuterNamespace { /** * A nested namespace */ + // eslint-disable-next-line @typescript-eslint/no-namespace export namespace InnerNamespace { /** * A function inside a namespace @@ -74,6 +78,7 @@ export namespace OuterNamespace { /** * A variable exported from within a namespace. */ + // eslint-disable-next-line prefer-const export let nestedVariable: boolean = false; } diff --git a/build-tests/api-documenter-test/src/test/__snapshots__/snapshot.test.ts.snap b/build-tests/api-documenter-test/src/test/__snapshots__/snapshot.test.ts.snap new file mode 100644 index 00000000000..ac1c88d3bbd --- /dev/null +++ b/build-tests/api-documenter-test/src/test/__snapshots__/snapshot.test.ts.snap @@ -0,0 +1,4747 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`api-documenter YAML: itemContents 1`] = ` +Object { + "/api-documenter-test.yml": "### YamlMime:TSPackage +uid: api-documenter-test! +name: api-documenter-test +type: package +summary: |- + api-extractor-test-05 + + This project tests various documentation generation scenarios and doc comment syntaxes. +classes: + - api-documenter-test!AbstractClass:class + - api-documenter-test!DecoratorExample:class + - api-documenter-test!DocBaseClass:class + - api-documenter-test!DocClass1:class + - api-documenter-test!DocClassInterfaceMerge:class + - api-documenter-test!Generic:class + - api-documenter-test!SystemEvent:class +interfaces: + - api-documenter-test!Constraint:interface + - api-documenter-test!DefaultType:interface + - api-documenter-test!DocClassInterfaceMerge:interface + - api-documenter-test!IDocInterface1:interface + - api-documenter-test!IDocInterface2:interface + - api-documenter-test!IDocInterface3:interface + - api-documenter-test!IDocInterface4:interface + - api-documenter-test!IDocInterface5:interface + - api-documenter-test!IDocInterface6:interface + - api-documenter-test!IDocInterface7:interface +enums: + - api-documenter-test!DocEnum:enum + - api-documenter-test!DocEnumNamespaceMerge:enum +typeAliases: + - api-documenter-test!ExampleDuplicateTypeAlias:type + - api-documenter-test!ExampleTypeAlias:type + - api-documenter-test!ExampleUnionTypeAlias:type + - api-documenter-test!GenericTypeAlias:type + - api-documenter-test!TypeAlias:type +functions: + - name: exampleFunction(x, y) + uid: api-documenter-test!exampleFunction:function(1) + package: api-documenter-test! + summary: An exported function with hyperlinked parameters and return value. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'export declare function exampleFunction(x: ExampleTypeAlias, y: number): IDocInterface1;' + parameters: + - id: x + description: an API item that should get hyperlinked + type: + - id: 'y' + description: a system type that should NOT get hyperlinked + type: number + return: + type: + description: an interface that should get hyperlinked + - name: yamlReferenceUniquenessTest() + uid: api-documenter-test!yamlReferenceUniquenessTest:function(1) + package: api-documenter-test! + summary: '' + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'export declare function yamlReferenceUniquenessTest(): IDocInterface1;' + return: + type: + description: '' +", + "/api-documenter-test/abstractclass.yml": "### YamlMime:TSType +name: AbstractClass +uid: api-documenter-test!AbstractClass:class +package: api-documenter-test! +fullName: AbstractClass +summary: Some abstract class with abstract members. +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: class +properties: + - name: property + uid: api-documenter-test!AbstractClass#property:member + package: api-documenter-test! + fullName: property + summary: Some abstract property. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'protected abstract property: number;' + return: + type: number +methods: + - name: method() + uid: api-documenter-test!AbstractClass#method:member(1) + package: api-documenter-test! + fullName: method() + summary: Some abstract method. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'abstract method(): void;' + return: + type: void + description: '' +", + "/api-documenter-test/constraint.yml": "### YamlMime:TSType +name: Constraint +uid: api-documenter-test!Constraint:interface +package: api-documenter-test! +fullName: Constraint +summary: Type parameter constraint used by test case below. +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: interface +", + "/api-documenter-test/decoratorexample.yml": "### YamlMime:TSType +name: DecoratorExample +uid: api-documenter-test!DecoratorExample:class +package: api-documenter-test! +fullName: DecoratorExample +summary: '' +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: class +properties: + - name: creationDate + uid: api-documenter-test!DecoratorExample#creationDate:member + package: api-documenter-test! + fullName: creationDate + summary: The date when the record was created. + remarks: Here is a longer description of the property. + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'creationDate: Date;' + return: + type: Date +", + "/api-documenter-test/defaulttype.yml": "### YamlMime:TSType +name: DefaultType +uid: api-documenter-test!DefaultType:interface +package: api-documenter-test! +fullName: DefaultType +summary: Type parameter default type used by test case below. +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: interface +", + "/api-documenter-test/docbaseclass.yml": "### YamlMime:TSType +name: DocBaseClass +uid: api-documenter-test!DocBaseClass:class +package: api-documenter-test! +fullName: DocBaseClass +summary: Example base class +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: class +constructors: + - name: (constructor)() + uid: api-documenter-test!DocBaseClass:constructor(1) + package: api-documenter-test! + fullName: (constructor)() + summary: The simple constructor for \`DocBaseClass\` + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: constructor(); + - name: (constructor)(x) + uid: api-documenter-test!DocBaseClass:constructor(2) + package: api-documenter-test! + fullName: (constructor)(x) + summary: The overloaded constructor for \`DocBaseClass\` + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'constructor(x: number);' + parameters: + - id: x + description: '' + type: number +", + "/api-documenter-test/docclass1.yml": "### YamlMime:TSType +name: DocClass1 +uid: api-documenter-test!DocClass1:class +package: api-documenter-test! +fullName: DocClass1 +summary: This is an example class. +remarks: >- + [Link to overload 1](xref:api-documenter-test!DocClass1%23exampleFunction:member(1)) + + + [Link to overload 2](xref:api-documenter-test!DocClass1%23exampleFunction:member(2)) + + + + The constructor for this class is marked as internal. Third-party code should not call the constructor directly or + create subclasses that extend the \`DocClass1\` class. +example: [] +isPreview: false +isDeprecated: false +type: class +properties: + - name: multipleModifiersProperty + uid: api-documenter-test!DocClass1.multipleModifiersProperty:member + package: api-documenter-test! + fullName: multipleModifiersProperty + summary: Some property with multiple modifiers. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'protected static readonly multipleModifiersProperty: boolean;' + return: + type: boolean + - name: protectedProperty + uid: api-documenter-test!DocClass1#protectedProperty:member + package: api-documenter-test! + fullName: protectedProperty + summary: Some protected property. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'protected protectedProperty: string;' + return: + type: string + - name: readonlyProperty + uid: api-documenter-test!DocClass1#readonlyProperty:member + package: api-documenter-test! + fullName: readonlyProperty + summary: '' + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'get readonlyProperty(): string;' + return: + type: string + - name: regularProperty + uid: api-documenter-test!DocClass1#regularProperty:member + package: api-documenter-test! + fullName: regularProperty + summary: This is a regular property that happens to use the SystemEvent type. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'regularProperty: SystemEvent;' + return: + type: + - name: writeableProperty + uid: api-documenter-test!DocClass1#writeableProperty:member + package: api-documenter-test! + fullName: writeableProperty + summary: '' + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: |- + get writeableProperty(): string; + + set writeableProperty(value: string); + return: + type: string + - name: writeonlyProperty + uid: api-documenter-test!DocClass1#writeonlyProperty:member + package: api-documenter-test! + fullName: writeonlyProperty + summary: API Extractor will surface an \`ae-missing-getter\` finding for this property. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'set writeonlyProperty(value: string);' + return: + type: string +methods: + - name: deprecatedExample() + uid: api-documenter-test!DocClass1#deprecatedExample:member(1) + package: api-documenter-test! + fullName: deprecatedExample() + summary: '' + remarks: '' + example: [] + isPreview: false + isDeprecated: true + customDeprecatedMessage: Use \`otherThing()\` instead. + syntax: + content: 'deprecatedExample(): void;' + return: + type: void + description: '' + - name: exampleFunction(a, b) + uid: api-documenter-test!DocClass1#exampleFunction:member(1) + package: api-documenter-test! + fullName: exampleFunction(a, b) + summary: This is an overloaded function. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'exampleFunction(a: string, b: string): string;' + parameters: + - id: a + description: the first string + type: string + - id: b + description: the second string + type: string + return: + type: string + description: '' + - name: exampleFunction(x) + uid: api-documenter-test!DocClass1#exampleFunction:member(2) + package: api-documenter-test! + fullName: exampleFunction(x) + summary: This is also an overloaded function. + remarks: '' + example: [] + defaultValue: '123' + isPreview: false + isDeprecated: false + syntax: + content: 'exampleFunction(x: number): number;' + parameters: + - id: x + description: the number + type: number + return: + type: number + description: '' + - name: genericWithConstraintAndDefault(x) + uid: api-documenter-test!DocClass1#genericWithConstraintAndDefault:member(1) + package: api-documenter-test! + fullName: genericWithConstraintAndDefault(x) + summary: This is a method with a complex type parameter. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'genericWithConstraintAndDefault(x: T): void;' + parameters: + - id: x + description: some generic parameter. + type: T + return: + type: void + description: '' + - name: interestingEdgeCases() + uid: api-documenter-test!DocClass1#interestingEdgeCases:member(1) + package: api-documenter-test! + fullName: interestingEdgeCases() + summary: |- + Example: \\"{ \\\\\\\\\\"maxItemsToShow\\\\\\\\\\": 123 }\\" + + The regular expression used to validate the constraints is /^\\\\[a-zA-Z0-9\\\\\\\\-\\\\_\\\\]+$/ + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'interestingEdgeCases(): void;' + return: + type: void + description: '' + - name: optionalParamFunction(x) + uid: api-documenter-test!DocClass1#optionalParamFunction:member(1) + package: api-documenter-test! + fullName: optionalParamFunction(x) + summary: This is a function with an optional parameter. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'optionalParamFunction(x?: number): void;' + parameters: + - id: x + description: the number + type: number + return: + type: void + description: '' + - name: sumWithExample(x, y) + uid: api-documenter-test!DocClass1.sumWithExample:member(1) + package: api-documenter-test! + fullName: sumWithExample(x, y) + summary: Returns the sum of two numbers. + remarks: This illustrates usage of the \`@example\` block tag. + example: + - |- + Here's a simple example: + + \`\`\` + // Prints \\"2\\": + console.log(DocClass1.sumWithExample(1,1)); + \`\`\` + - |- + Here's an example with negative numbers: + + \`\`\` + // Prints \\"0\\": + console.log(DocClass1.sumWithExample(1,-1)); + \`\`\` + isPreview: false + isDeprecated: false + syntax: + content: 'static sumWithExample(x: number, y: number): number;' + parameters: + - id: x + description: the first number to add + type: number + - id: 'y' + description: the second number to add + type: number + return: + type: number + description: the sum of the two numbers + - name: tableExample() + uid: api-documenter-test!DocClass1#tableExample:member(1) + package: api-documenter-test! + fullName: tableExample() + summary: 'An example with tables:' + remarks:
John Doe
+ example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'tableExample(): void;' + return: + type: void + description: '' +events: + - name: malformedEvent + uid: api-documenter-test!DocClass1#malformedEvent:member + package: api-documenter-test! + fullName: malformedEvent + summary: This event should have been marked as readonly. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'malformedEvent: SystemEvent;' + return: + type: + - name: modifiedEvent + uid: api-documenter-test!DocClass1#modifiedEvent:member + package: api-documenter-test! + fullName: modifiedEvent + summary: This event is fired whenever the object is modified. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'readonly modifiedEvent: SystemEvent;' + return: + type: +extends: +", + "/api-documenter-test/docclassinterfacemerge-class.yml": "### YamlMime:TSType +name: DocClassInterfaceMerge +uid: api-documenter-test!DocClassInterfaceMerge:class +package: api-documenter-test! +fullName: DocClassInterfaceMerge +summary: Class that merges with interface +remarks: |- + [Link to class](xref:api-documenter-test!DocClassInterfaceMerge:class) + + [Link to interface](xref:api-documenter-test!DocClassInterfaceMerge:interface) +example: [] +isPreview: false +isDeprecated: false +type: class +", + "/api-documenter-test/docclassinterfacemerge-interface.yml": "### YamlMime:TSType +name: DocClassInterfaceMerge +uid: api-documenter-test!DocClassInterfaceMerge:interface +package: api-documenter-test! +fullName: DocClassInterfaceMerge +summary: Interface that merges with class +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: interface +", + "/api-documenter-test/docenum.yml": "### YamlMime:TSEnum +name: DocEnum +uid: api-documenter-test!DocEnum:enum +package: api-documenter-test! +fullName: DocEnum +summary: Docs for DocEnum +remarks: '' +example: [] +isPreview: false +isDeprecated: false +fields: + - name: One + uid: api-documenter-test!DocEnum.One:member + package: api-documenter-test! + summary: These are some docs for One + value: '1' + - name: Two + uid: api-documenter-test!DocEnum.Two:member + package: api-documenter-test! + summary: |- + These are some docs for Two. + + [DocEnum.One](xref:api-documenter-test!DocEnum.One:member) is a direct link to another enum member. + value: '2' + - name: Zero + uid: api-documenter-test!DocEnum.Zero:member + package: api-documenter-test! + summary: These are some docs for Zero + value: '0' +", + "/api-documenter-test/docenumnamespacemerge-enum.yml": "### YamlMime:TSEnum +name: DocEnumNamespaceMerge +uid: api-documenter-test!DocEnumNamespaceMerge:enum +package: api-documenter-test! +fullName: DocEnumNamespaceMerge +summary: Enum that merges with namespace +remarks: |- + [Link to enum](xref:api-documenter-test!DocEnumNamespaceMerge:enum) + + [Link to namespace](xref:api-documenter-test!DocEnumNamespaceMerge:namespace) + + [Link to function inside namespace](xref:api-documenter-test!DocEnumNamespaceMerge.exampleFunction:function(1)) +example: [] +isPreview: false +isDeprecated: false +fields: + - name: Left + uid: api-documenter-test!DocEnumNamespaceMerge.Left:member + package: api-documenter-test! + summary: These are some docs for Left + value: '0' + - name: Right + uid: api-documenter-test!DocEnumNamespaceMerge.Right:member + package: api-documenter-test! + summary: These are some docs for Right + value: '1' +", + "/api-documenter-test/docenumnamespacemerge-namespace.yml": "### YamlMime:UniversalReference +items: + - uid: api-documenter-test!DocEnumNamespaceMerge:namespace + summary: Namespace that merges with enum + name: DocEnumNamespaceMerge + fullName: DocEnumNamespaceMerge + langs: + - typeScript + type: namespace + package: api-documenter-test! + children: + - api-documenter-test!DocEnumNamespaceMerge.exampleFunction:function(1) + - uid: api-documenter-test!DocEnumNamespaceMerge.exampleFunction:function(1) + summary: This is a function inside of a namespace that merges with an enum. + name: exampleFunction() + fullName: DocEnumNamespaceMerge.exampleFunction() + langs: + - typeScript + namespace: api-documenter-test!DocEnumNamespaceMerge:namespace + type: function + syntax: + content: 'function exampleFunction(): void;' + return: + type: + - void + description: '' +", + "/api-documenter-test/ecmasymbols.yml": "### YamlMime:UniversalReference +items: + - uid: api-documenter-test!EcmaSymbols:namespace + summary: A namespace containing an ECMAScript symbol + name: EcmaSymbols + fullName: EcmaSymbols + langs: + - typeScript + type: namespace + package: api-documenter-test! + children: + - api-documenter-test!EcmaSymbols.example:var + - uid: api-documenter-test!EcmaSymbols.example:var + summary: An ECMAScript symbol + name: example + fullName: EcmaSymbols.example + langs: + - typeScript + namespace: api-documenter-test!EcmaSymbols:namespace + type: variable + syntax: + content: 'example: unique symbol' + return: + type: + - unique symbol +", + "/api-documenter-test/exampleduplicatetypealias.yml": "### YamlMime:TSTypeAlias +name: ExampleDuplicateTypeAlias +uid: api-documenter-test!ExampleDuplicateTypeAlias:type +package: api-documenter-test! +fullName: ExampleDuplicateTypeAlias +summary: A type alias that has duplicate references. +remarks: '' +example: [] +isPreview: false +isDeprecated: false +syntax: export type ExampleDuplicateTypeAlias = SystemEvent | typeof SystemEvent; +", + "/api-documenter-test/exampletypealias.yml": "### YamlMime:TSTypeAlias +name: ExampleTypeAlias +uid: api-documenter-test!ExampleTypeAlias:type +package: api-documenter-test! +fullName: ExampleTypeAlias +summary: A type alias +remarks: '' +example: [] +isPreview: false +isDeprecated: false +syntax: export type ExampleTypeAlias = Promise; +", + "/api-documenter-test/exampleuniontypealias.yml": "### YamlMime:TSTypeAlias +name: ExampleUnionTypeAlias +uid: api-documenter-test!ExampleUnionTypeAlias:type +package: api-documenter-test! +fullName: ExampleUnionTypeAlias +summary: A type alias that references multiple other types. +remarks: '' +example: [] +isPreview: false +isDeprecated: false +syntax: export type ExampleUnionTypeAlias = IDocInterface1 | IDocInterface3; +", + "/api-documenter-test/generic.yml": "### YamlMime:TSType +name: Generic +uid: api-documenter-test!Generic:class +package: api-documenter-test! +fullName: Generic +summary: Generic class. +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: class +", + "/api-documenter-test/generictypealias.yml": "### YamlMime:TSTypeAlias +name: GenericTypeAlias +uid: api-documenter-test!GenericTypeAlias:type +package: api-documenter-test! +fullName: GenericTypeAlias +summary: '' +remarks: '' +example: [] +isPreview: false +isDeprecated: false +syntax: export type GenericTypeAlias = T[]; +", + "/api-documenter-test/idocinterface1.yml": "### YamlMime:TSType +name: IDocInterface1 +uid: api-documenter-test!IDocInterface1:interface +package: api-documenter-test! +fullName: IDocInterface1 +summary: '' +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: interface +properties: + - name: regularProperty + uid: api-documenter-test!IDocInterface1#regularProperty:member + package: api-documenter-test! + fullName: regularProperty + summary: Does something + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'regularProperty: SystemEvent;' + return: + type: +", + "/api-documenter-test/idocinterface2.yml": "### YamlMime:TSType +name: IDocInterface2 +uid: api-documenter-test!IDocInterface2:interface +package: api-documenter-test! +fullName: IDocInterface2 +summary: '' +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: interface +methods: + - name: deprecatedExample() + uid: api-documenter-test!IDocInterface2#deprecatedExample:member(1) + package: api-documenter-test! + fullName: deprecatedExample() + summary: '' + remarks: '' + example: [] + isPreview: false + isDeprecated: true + customDeprecatedMessage: Use \`otherThing()\` instead. + syntax: + content: 'deprecatedExample(): void;' + return: + type: void + description: '' +extends: +", + "/api-documenter-test/idocinterface3.yml": "### YamlMime:TSType +name: IDocInterface3 +uid: api-documenter-test!IDocInterface3:interface +package: api-documenter-test! +fullName: IDocInterface3 +summary: Some less common TypeScript declaration kinds. +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: interface +properties: + - name: '\\"[not.a.symbol]\\"' + uid: api-documenter-test!IDocInterface3#\\"[not.a.symbol]\\":member + package: api-documenter-test! + fullName: '\\"[not.a.symbol]\\"' + summary: An identifier that does need quotes. It misleadingly looks like an ECMAScript symbol. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: '\\"[not.a.symbol]\\": string;' + return: + type: string + - name: '[EcmaSymbols.example]' + uid: api-documenter-test!IDocInterface3#[EcmaSymbols.example]:member + package: api-documenter-test! + fullName: '[EcmaSymbols.example]' + summary: ECMAScript symbol + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: '[EcmaSymbols.example]: string;' + return: + type: string + - name: redundantQuotes + uid: api-documenter-test!IDocInterface3#redundantQuotes:member + package: api-documenter-test! + fullName: redundantQuotes + summary: A quoted identifier with redundant quotes. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: '\\"redundantQuotes\\": string;' + return: + type: string +", + "/api-documenter-test/idocinterface4.yml": "### YamlMime:TSType +name: IDocInterface4 +uid: api-documenter-test!IDocInterface4:interface +package: api-documenter-test! +fullName: IDocInterface4 +summary: Type union in an interface. +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: interface +properties: + - name: Context + uid: api-documenter-test!IDocInterface4#Context:member + package: api-documenter-test! + fullName: Context + summary: Test newline rendering when code blocks are used in tables + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: |- + Context: ({ children }: { + children: string; + }) => boolean; + return: + type: |- + ({ children }: { + children: string; + }) => boolean + - name: generic + uid: api-documenter-test!IDocInterface4#generic:member + package: api-documenter-test! + fullName: generic + summary: make sure html entities are escaped in tables. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'generic: Generic;' + return: + type: <number> + - name: numberOrFunction + uid: api-documenter-test!IDocInterface4#numberOrFunction:member + package: api-documenter-test! + fullName: numberOrFunction + summary: a union type with a function + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'numberOrFunction: number | (() => number);' + return: + type: number | (() => number) + - name: stringOrNumber + uid: api-documenter-test!IDocInterface4#stringOrNumber:member + package: api-documenter-test! + fullName: stringOrNumber + summary: a union type + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'stringOrNumber: string | number;' + return: + type: string | number +", + "/api-documenter-test/idocinterface5.yml": "### YamlMime:TSType +name: IDocInterface5 +uid: api-documenter-test!IDocInterface5:interface +package: api-documenter-test! +fullName: IDocInterface5 +summary: Interface without inline tag to test custom TOC +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: interface +properties: + - name: regularProperty + uid: api-documenter-test!IDocInterface5#regularProperty:member + package: api-documenter-test! + fullName: regularProperty + summary: Property of type string that does something + remarks: '' + example: [] + defaultValue: '\\"Hello World\\"' + isPreview: false + isDeprecated: false + syntax: + content: 'regularProperty: string;' + return: + type: string +", + "/api-documenter-test/idocinterface6.yml": "### YamlMime:TSType +name: IDocInterface6 +uid: api-documenter-test!IDocInterface6:interface +package: api-documenter-test! +fullName: IDocInterface6 +summary: Interface without inline tag to test custom TOC with injection +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: interface +properties: + - name: arrayProperty + uid: api-documenter-test!IDocInterface6#arrayProperty:member + package: api-documenter-test! + fullName: arrayProperty + summary: '' + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'arrayProperty: IDocInterface1[];' + return: + type: [] + - name: intersectionProperty + uid: api-documenter-test!IDocInterface6#intersectionProperty:member + package: api-documenter-test! + fullName: intersectionProperty + summary: '' + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'intersectionProperty: IDocInterface1 & IDocInterface2;' + return: + type: >- + & + - name: regularProperty + uid: api-documenter-test!IDocInterface6#regularProperty:member + package: api-documenter-test! + fullName: regularProperty + summary: Property of type number that does something + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'regularProperty: number;' + return: + type: number + - name: tupleProperty + uid: api-documenter-test!IDocInterface6#tupleProperty:member + package: api-documenter-test! + fullName: tupleProperty + summary: '' + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'tupleProperty: [IDocInterface1, IDocInterface2];' + return: + type: >- + [, ] + - name: typeReferenceProperty + uid: api-documenter-test!IDocInterface6#typeReferenceProperty:member + package: api-documenter-test! + fullName: typeReferenceProperty + summary: '' + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'typeReferenceProperty: Generic;' + return: + type: >- + <> + - name: unionProperty + uid: api-documenter-test!IDocInterface6#unionProperty:member + package: api-documenter-test! + fullName: unionProperty + summary: '' + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'unionProperty: IDocInterface1 | IDocInterface2;' + return: + type: >- + | +methods: + - name: genericReferenceMethod(x) + uid: api-documenter-test!IDocInterface6#genericReferenceMethod:member(1) + package: api-documenter-test! + fullName: genericReferenceMethod(x) + summary: '' + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'genericReferenceMethod(x: T): T;' + parameters: + - id: x + description: '' + type: T + return: + type: T + description: '' +", + "/api-documenter-test/idocinterface7.yml": "### YamlMime:TSType +name: IDocInterface7 +uid: api-documenter-test!IDocInterface7:interface +package: api-documenter-test! +fullName: IDocInterface7 +summary: Interface for testing optional properties +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: interface +properties: + - name: optionalField + uid: api-documenter-test!IDocInterface7#optionalField:member + package: api-documenter-test! + fullName: optionalField + summary: Description of optionalField + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'optionalField?: boolean;' + return: + type: boolean + - name: optionalReadonlyField + uid: api-documenter-test!IDocInterface7#optionalReadonlyField:member + package: api-documenter-test! + fullName: optionalReadonlyField + summary: Description of optionalReadonlyField + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'readonly optionalReadonlyField?: boolean;' + return: + type: boolean + - name: optionalUndocumentedField + uid: api-documenter-test!IDocInterface7#optionalUndocumentedField:member + package: api-documenter-test! + fullName: optionalUndocumentedField + summary: '' + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'optionalUndocumentedField?: boolean;' + return: + type: boolean +methods: + - name: optionalMember() + uid: api-documenter-test!IDocInterface7#optionalMember:member(1) + package: api-documenter-test! + fullName: optionalMember() + summary: Description of optionalMember + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'optionalMember?(): any;' + return: + type: any + description: '' +", + "/api-documenter-test/outernamespace.innernamespace.yml": "### YamlMime:UniversalReference +items: + - uid: api-documenter-test!OuterNamespace.InnerNamespace:namespace + summary: A nested namespace + name: OuterNamespace.InnerNamespace + fullName: OuterNamespace.InnerNamespace + langs: + - typeScript + type: namespace + package: api-documenter-test! + children: + - api-documenter-test!OuterNamespace.InnerNamespace.nestedFunction:function(1) + - uid: api-documenter-test!OuterNamespace.InnerNamespace.nestedFunction:function(1) + summary: A function inside a namespace + name: nestedFunction(x) + fullName: OuterNamespace.InnerNamespace.nestedFunction(x) + langs: + - typeScript + namespace: api-documenter-test!OuterNamespace.InnerNamespace:namespace + type: function + syntax: + content: 'function nestedFunction(x: number): number;' + return: + type: + - number + description: '' + parameters: + - id: x + description: '' + type: + - number + optional: false +", + "/api-documenter-test/outernamespace.yml": "### YamlMime:UniversalReference +items: + - uid: api-documenter-test!OuterNamespace:namespace + summary: A top-level namespace + name: OuterNamespace + fullName: OuterNamespace + langs: + - typeScript + type: namespace + package: api-documenter-test! + children: + - api-documenter-test!OuterNamespace.nestedVariable:var + - uid: api-documenter-test!OuterNamespace.nestedVariable:var + summary: A variable exported from within a namespace. + name: nestedVariable + fullName: OuterNamespace.nestedVariable + langs: + - typeScript + namespace: api-documenter-test!OuterNamespace:namespace + type: variable + syntax: + content: 'nestedVariable: boolean' + return: + type: + - boolean +", + "/api-documenter-test/systemevent.yml": "### YamlMime:TSType +name: SystemEvent +uid: api-documenter-test!SystemEvent:class +package: api-documenter-test! +fullName: SystemEvent +summary: A class used to exposed events. +remarks: '' +example: [] +isPreview: false +isDeprecated: false +type: class +methods: + - name: addHandler(handler) + uid: api-documenter-test!SystemEvent#addHandler:member(1) + package: api-documenter-test! + fullName: addHandler(handler) + summary: Adds an handler for the event. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'addHandler(handler: () => void): void;' + parameters: + - id: handler + description: '' + type: () => void + return: + type: void + description: '' +", + "/api-documenter-test/typealias.yml": "### YamlMime:TSTypeAlias +name: TypeAlias +uid: api-documenter-test!TypeAlias:type +package: api-documenter-test! +fullName: TypeAlias +summary: '' +remarks: '' +example: [] +isPreview: false +isDeprecated: false +syntax: export type TypeAlias = number; +", + "/toc.yml": "items: + - name: Test api-documenter + href: ~/homepage/homepage.md + - name: Test Sample for AD + href: api-documenter-test + extended: true + items: + - name: Classes + items: + - name: DocBaseClass + items: + - name: DocBaseClass + uid: api-documenter-test!DocBaseClass:class + - name: IDocInterface1 + uid: api-documenter-test!IDocInterface1:interface + - name: IDocInterface2 + uid: api-documenter-test!IDocInterface2:interface + - name: DocClass1 + items: + - name: DocClass1 + uid: api-documenter-test!DocClass1:class + - name: IDocInterface3 + uid: api-documenter-test!IDocInterface3:interface + - name: IDocInterface4 + uid: api-documenter-test!IDocInterface4:interface + - name: Interfaces + items: + - name: Interface5 + items: + - name: IDocInterface5 + uid: api-documenter-test!IDocInterface5:interface + - name: Interface6 + items: + - name: InjectedCustomInterface + uid: customUid + - name: IDocInterface6 + uid: api-documenter-test!IDocInterface6:interface + - name: References + items: + - name: InjectedCustomItem + uid: customUrl + - name: AbstractClass + uid: api-documenter-test!AbstractClass:class + - name: Constraint + uid: api-documenter-test!Constraint:interface + - name: DecoratorExample + uid: api-documenter-test!DecoratorExample:class + - name: DefaultType + uid: api-documenter-test!DefaultType:interface + - name: DocClassInterfaceMerge (Class) + uid: api-documenter-test!DocClassInterfaceMerge:class + - name: DocClassInterfaceMerge (Interface) + uid: api-documenter-test!DocClassInterfaceMerge:interface + - name: DocEnum + uid: api-documenter-test!DocEnum:enum + - name: DocEnumNamespaceMerge (Enum) + uid: api-documenter-test!DocEnumNamespaceMerge:enum + - name: DocEnumNamespaceMerge (Namespace) + uid: api-documenter-test!DocEnumNamespaceMerge:namespace + - name: EcmaSymbols + uid: api-documenter-test!EcmaSymbols:namespace + - name: ExampleDuplicateTypeAlias + uid: api-documenter-test!ExampleDuplicateTypeAlias:type + - name: ExampleTypeAlias + uid: api-documenter-test!ExampleTypeAlias:type + - name: ExampleUnionTypeAlias + uid: api-documenter-test!ExampleUnionTypeAlias:type + - name: Generic + uid: api-documenter-test!Generic:class + - name: GenericTypeAlias + uid: api-documenter-test!GenericTypeAlias:type + - name: IDocInterface7 + uid: api-documenter-test!IDocInterface7:interface + - name: OuterNamespace + uid: api-documenter-test!OuterNamespace:namespace + - name: OuterNamespace.InnerNamespace + uid: api-documenter-test!OuterNamespace.InnerNamespace:namespace + - name: SystemEvent + uid: api-documenter-test!SystemEvent:class + - name: TypeAlias + uid: api-documenter-test!TypeAlias:type +", +} +`; + +exports[`api-documenter markdown: itemContents 1`] = ` +Object { + "/api-documenter-test.abstractclass.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [AbstractClass](./api-documenter-test.abstractclass.md) + +## AbstractClass class + +Some abstract class with abstract members. + +**Signature:** + +\`\`\`typescript +export declare abstract class AbstractClass +\`\`\` + +## Properties + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[property](./api-documenter-test.abstractclass.property.md) + + + + +\`protected\` + +\`abstract\` + + + + +number + + + + +Some abstract property. + + +
+ +## Methods + + + +
+ +Method + + + + +Modifiers + + + + +Description + + +
+ +[method()](./api-documenter-test.abstractclass.method.md) + + + + +\`abstract\` + + + + +Some abstract method. + + +
+ +", + "/api-documenter-test.abstractclass.method.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [AbstractClass](./api-documenter-test.abstractclass.md) > [method](./api-documenter-test.abstractclass.method.md) + +## AbstractClass.method() method + +Some abstract method. + +**Signature:** + +\`\`\`typescript +abstract method(): void; +\`\`\` +**Returns:** + +void + +", + "/api-documenter-test.abstractclass.property.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [AbstractClass](./api-documenter-test.abstractclass.md) > [property](./api-documenter-test.abstractclass.property.md) + +## AbstractClass.property property + +Some abstract property. + +**Signature:** + +\`\`\`typescript +protected abstract property: number; +\`\`\` +", + "/api-documenter-test.constraint.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [Constraint](./api-documenter-test.constraint.md) + +## Constraint interface + +Type parameter constraint used by test case below. + +**Signature:** + +\`\`\`typescript +export interface Constraint +\`\`\` +", + "/api-documenter-test.constvariable.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [constVariable](./api-documenter-test.constvariable.md) + +## constVariable variable + +An exported variable declaration. + +**Signature:** + +\`\`\`typescript +constVariable: number +\`\`\` +", + "/api-documenter-test.decoratorexample.creationdate.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DecoratorExample](./api-documenter-test.decoratorexample.md) > [creationDate](./api-documenter-test.decoratorexample.creationdate.md) + +## DecoratorExample.creationDate property + +The date when the record was created. + +**Signature:** + +\`\`\`typescript +creationDate: Date; +\`\`\` +**Decorators:** + +\`@jsonSerialized\` + +\`@jsonFormat('mm/dd/yy')\` + +## Remarks + +Here is a longer description of the property. + +", + "/api-documenter-test.decoratorexample.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DecoratorExample](./api-documenter-test.decoratorexample.md) + +## DecoratorExample class + + +**Signature:** + +\`\`\`typescript +export declare class DecoratorExample +\`\`\` + +## Properties + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[creationDate](./api-documenter-test.decoratorexample.creationdate.md) + + + + + + + +Date + + + + +The date when the record was created. + + +
+ +", + "/api-documenter-test.defaulttype.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DefaultType](./api-documenter-test.defaulttype.md) + +## DefaultType interface + +Type parameter default type used by test case below. + +**Signature:** + +\`\`\`typescript +export interface DefaultType +\`\`\` +", + "/api-documenter-test.docbaseclass._constructor_.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocBaseClass](./api-documenter-test.docbaseclass.md) > [(constructor)](./api-documenter-test.docbaseclass._constructor_.md) + +## DocBaseClass.(constructor) + +The simple constructor for \`DocBaseClass\` + +**Signature:** + +\`\`\`typescript +constructor(); +\`\`\` +", + "/api-documenter-test.docbaseclass._constructor__1.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocBaseClass](./api-documenter-test.docbaseclass.md) > [(constructor)](./api-documenter-test.docbaseclass._constructor__1.md) + +## DocBaseClass.(constructor) + +The overloaded constructor for \`DocBaseClass\` + +**Signature:** + +\`\`\`typescript +constructor(x: number); +\`\`\` + +## Parameters + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +x + + + + +number + + + + + +
+ +", + "/api-documenter-test.docbaseclass.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocBaseClass](./api-documenter-test.docbaseclass.md) + +## DocBaseClass class + +Example base class + + +**Signature:** + +\`\`\`typescript +export declare class DocBaseClass +\`\`\` + +## Constructors + + + + +
+ +Constructor + + + + +Modifiers + + + + +Description + + +
+ +[(constructor)()](./api-documenter-test.docbaseclass._constructor_.md) + + + + + + + +The simple constructor for \`DocBaseClass\` + + +
+ +[(constructor)(x)](./api-documenter-test.docbaseclass._constructor__1.md) + + + + + + + +The overloaded constructor for \`DocBaseClass\` + + +
+ +", + "/api-documenter-test.docclass1.deprecatedexample.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [deprecatedExample](./api-documenter-test.docclass1.deprecatedexample.md) + +## DocClass1.deprecatedExample() method + +> Warning: This API is now obsolete. +> +> Use \`otherThing()\` instead. +> + +**Signature:** + +\`\`\`typescript +deprecatedExample(): void; +\`\`\` +**Returns:** + +void + +", + "/api-documenter-test.docclass1.examplefunction.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [exampleFunction](./api-documenter-test.docclass1.examplefunction.md) + +## DocClass1.exampleFunction() method + +This is an overloaded function. + +**Signature:** + +\`\`\`typescript +exampleFunction(a: string, b: string): string; +\`\`\` + +## Parameters + + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +a + + + + +string + + + + +the first string + + +
+ +b + + + + +string + + + + +the second string + + +
+ +**Returns:** + +string + +## Exceptions + +\`Error\` The first throws line + +The second throws line + +", + "/api-documenter-test.docclass1.examplefunction_1.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [exampleFunction](./api-documenter-test.docclass1.examplefunction_1.md) + +## DocClass1.exampleFunction() method + +This is also an overloaded function. + +**Signature:** + +\`\`\`typescript +exampleFunction(x: number): number; +\`\`\` + +## Parameters + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +x + + + + +number + + + + +the number + + +
+ +**Returns:** + +number + +## Default Value + +123 + +", + "/api-documenter-test.docclass1.genericwithconstraintanddefault.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [genericWithConstraintAndDefault](./api-documenter-test.docclass1.genericwithconstraintanddefault.md) + +## DocClass1.genericWithConstraintAndDefault() method + +This is a method with a complex type parameter. + +**Signature:** + +\`\`\`typescript +genericWithConstraintAndDefault(x: T): void; +\`\`\` + +## Parameters + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +x + + + + +T + + + + +some generic parameter. + + +
+ +**Returns:** + +void + +", + "/api-documenter-test.docclass1.interestingedgecases.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [interestingEdgeCases](./api-documenter-test.docclass1.interestingedgecases.md) + +## DocClass1.interestingEdgeCases() method + +Example: \\"{ \\\\\\\\\\"maxItemsToShow\\\\\\\\\\": 123 }\\" + +The regular expression used to validate the constraints is /^\\\\[a-zA-Z0-9\\\\\\\\-\\\\_\\\\]+$/ + +**Signature:** + +\`\`\`typescript +interestingEdgeCases(): void; +\`\`\` +**Returns:** + +void + +", + "/api-documenter-test.docclass1.malformedevent.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [malformedEvent](./api-documenter-test.docclass1.malformedevent.md) + +## DocClass1.malformedEvent property + +This event should have been marked as readonly. + +**Signature:** + +\`\`\`typescript +malformedEvent: SystemEvent; +\`\`\` +", + "/api-documenter-test.docclass1.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) + +## DocClass1 class + +This is an example class. + +**Signature:** + +\`\`\`typescript +export declare class DocClass1 extends DocBaseClass implements IDocInterface1, IDocInterface2 +\`\`\` +**Extends:** [DocBaseClass](./api-documenter-test.docbaseclass.md) + +**Implements:** [IDocInterface1](./api-documenter-test.idocinterface1.md), [IDocInterface2](./api-documenter-test.idocinterface2.md) + +## Remarks + +[Link to overload 1](./api-documenter-test.docclass1.examplefunction.md) + +[Link to overload 2](./api-documenter-test.docclass1.examplefunction_1.md) + + +The constructor for this class is marked as internal. Third-party code should not call the constructor directly or create subclasses that extend the \`DocClass1\` class. + +## Events + + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[malformedEvent](./api-documenter-test.docclass1.malformedevent.md) + + + + + + + +[SystemEvent](./api-documenter-test.systemevent.md) + + + + +This event should have been marked as readonly. + + +
+ +[modifiedEvent](./api-documenter-test.docclass1.modifiedevent.md) + + + + +\`readonly\` + + + + +[SystemEvent](./api-documenter-test.systemevent.md) + + + + +This event is fired whenever the object is modified. + + +
+ +## Properties + + + + + + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[multipleModifiersProperty](./api-documenter-test.docclass1.multiplemodifiersproperty.md) + + + + +\`protected\` + +\`static\` + +\`readonly\` + + + + +boolean + + + + +Some property with multiple modifiers. + + +
+ +[protectedProperty](./api-documenter-test.docclass1.protectedproperty.md) + + + + +\`protected\` + + + + +string + + + + +Some protected property. + + +
+ +[readonlyProperty](./api-documenter-test.docclass1.readonlyproperty.md) + + + + +\`readonly\` + + + + +string + + + + + +
+ +[regularProperty](./api-documenter-test.docclass1.regularproperty.md) + + + + + + + +[SystemEvent](./api-documenter-test.systemevent.md) + + + + +This is a regular property that happens to use the SystemEvent type. + + +
+ +[writeableProperty](./api-documenter-test.docclass1.writeableproperty.md) + + + + + + + +string + + + + + +
+ +[writeonlyProperty](./api-documenter-test.docclass1.writeonlyproperty.md) + + + + + + + +string + + + + +API Extractor will surface an \`ae-missing-getter\` finding for this property. + + +
+ +## Methods + + + + + + + + + + +
+ +Method + + + + +Modifiers + + + + +Description + + +
+ +[deprecatedExample()](./api-documenter-test.docclass1.deprecatedexample.md) + + + + + + + + +
+ +[exampleFunction(a, b)](./api-documenter-test.docclass1.examplefunction.md) + + + + + + + +This is an overloaded function. + + +
+ +[exampleFunction(x)](./api-documenter-test.docclass1.examplefunction_1.md) + + + + + + + +This is also an overloaded function. + + +
+ +[genericWithConstraintAndDefault(x)](./api-documenter-test.docclass1.genericwithconstraintanddefault.md) + + + + + + + +This is a method with a complex type parameter. + + +
+ +[interestingEdgeCases()](./api-documenter-test.docclass1.interestingedgecases.md) + + + + + + + +Example: \\"{ \\\\\\\\\\"maxItemsToShow\\\\\\\\\\": 123 }\\" + +The regular expression used to validate the constraints is /^\\\\[a-zA-Z0-9\\\\\\\\-\\\\_\\\\]+$/ + + +
+ +[optionalParamFunction(x)](./api-documenter-test.docclass1.optionalparamfunction.md) + + + + + + + +This is a function with an optional parameter. + + +
+ +[sumWithExample(x, y)](./api-documenter-test.docclass1.sumwithexample.md) + + + + +\`static\` + + + + +Returns the sum of two numbers. + + +
+ +[tableExample()](./api-documenter-test.docclass1.tableexample.md) + + + + + + + +An example with tables: + + +
+ +", + "/api-documenter-test.docclass1.modifiedevent.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [modifiedEvent](./api-documenter-test.docclass1.modifiedevent.md) + +## DocClass1.modifiedEvent property + +This event is fired whenever the object is modified. + +**Signature:** + +\`\`\`typescript +readonly modifiedEvent: SystemEvent; +\`\`\` +", + "/api-documenter-test.docclass1.multiplemodifiersproperty.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [multipleModifiersProperty](./api-documenter-test.docclass1.multiplemodifiersproperty.md) + +## DocClass1.multipleModifiersProperty property + +Some property with multiple modifiers. + +**Signature:** + +\`\`\`typescript +protected static readonly multipleModifiersProperty: boolean; +\`\`\` +", + "/api-documenter-test.docclass1.optionalparamfunction.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [optionalParamFunction](./api-documenter-test.docclass1.optionalparamfunction.md) + +## DocClass1.optionalParamFunction() method + +This is a function with an optional parameter. + +**Signature:** + +\`\`\`typescript +optionalParamFunction(x?: number): void; +\`\`\` + +## Parameters + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +x + + + + +number + + + + +_(Optional)_ the number + + +
+ +**Returns:** + +void + +", + "/api-documenter-test.docclass1.protectedproperty.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [protectedProperty](./api-documenter-test.docclass1.protectedproperty.md) + +## DocClass1.protectedProperty property + +Some protected property. + +**Signature:** + +\`\`\`typescript +protected protectedProperty: string; +\`\`\` +", + "/api-documenter-test.docclass1.readonlyproperty.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [readonlyProperty](./api-documenter-test.docclass1.readonlyproperty.md) + +## DocClass1.readonlyProperty property + +**Signature:** + +\`\`\`typescript +get readonlyProperty(): string; +\`\`\` +", + "/api-documenter-test.docclass1.regularproperty.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [regularProperty](./api-documenter-test.docclass1.regularproperty.md) + +## DocClass1.regularProperty property + +This is a regular property that happens to use the SystemEvent type. + +**Signature:** + +\`\`\`typescript +regularProperty: SystemEvent; +\`\`\` +", + "/api-documenter-test.docclass1.sumwithexample.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [sumWithExample](./api-documenter-test.docclass1.sumwithexample.md) + +## DocClass1.sumWithExample() method + +Returns the sum of two numbers. + +**Signature:** + +\`\`\`typescript +static sumWithExample(x: number, y: number): number; +\`\`\` + +## Parameters + + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +x + + + + +number + + + + +the first number to add + + +
+ +y + + + + +number + + + + +the second number to add + + +
+ +**Returns:** + +number + +the sum of the two numbers + +## Remarks + +This illustrates usage of the \`@example\` block tag. + +## Example 1 + +Here's a simple example: + +\`\`\` +// Prints \\"2\\": +console.log(DocClass1.sumWithExample(1,1)); +\`\`\` + +## Example 2 + +Here's an example with negative numbers: + +\`\`\` +// Prints \\"0\\": +console.log(DocClass1.sumWithExample(1,-1)); +\`\`\` + +", + "/api-documenter-test.docclass1.tableexample.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [tableExample](./api-documenter-test.docclass1.tableexample.md) + +## DocClass1.tableExample() method + +An example with tables: + +**Signature:** + +\`\`\`typescript +tableExample(): void; +\`\`\` +**Returns:** + +void + +## Remarks + +
John Doe
+ +", + "/api-documenter-test.docclass1.writeableproperty.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [writeableProperty](./api-documenter-test.docclass1.writeableproperty.md) + +## DocClass1.writeableProperty property + +**Signature:** + +\`\`\`typescript +get writeableProperty(): string; + +set writeableProperty(value: string); +\`\`\` +", + "/api-documenter-test.docclass1.writeonlyproperty.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [writeonlyProperty](./api-documenter-test.docclass1.writeonlyproperty.md) + +## DocClass1.writeonlyProperty property + +API Extractor will surface an \`ae-missing-getter\` finding for this property. + +**Signature:** + +\`\`\`typescript +set writeonlyProperty(value: string); +\`\`\` +", + "/api-documenter-test.docclassinterfacemerge.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClassInterfaceMerge](./api-documenter-test.docclassinterfacemerge.md) + +## DocClassInterfaceMerge interface + +Interface that merges with class + +**Signature:** + +\`\`\`typescript +export interface DocClassInterfaceMerge +\`\`\` +", + "/api-documenter-test.docenum.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocEnum](./api-documenter-test.docenum.md) + +## DocEnum enum + +Docs for DocEnum + + +**Signature:** + +\`\`\`typescript +export declare enum DocEnum +\`\`\` + +## Enumeration Members + + + + + +
+ +Member + + + + +Value + + + + +Description + + +
+ +One + + + + +\`1\` + + + + +These are some docs for One + + +
+ +Two + + + + +\`2\` + + + + +These are some docs for Two. + +[DocEnum.One](./api-documenter-test.docenum.md) is a direct link to another enum member. + + +
+ +Zero + + + + +\`0\` + + + + +These are some docs for Zero + + +
+ +", + "/api-documenter-test.docenumnamespacemerge.examplefunction.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocEnumNamespaceMerge](./api-documenter-test.docenumnamespacemerge.md) > [exampleFunction](./api-documenter-test.docenumnamespacemerge.examplefunction.md) + +## DocEnumNamespaceMerge.exampleFunction() function + +This is a function inside of a namespace that merges with an enum. + +**Signature:** + +\`\`\`typescript +function exampleFunction(): void; +\`\`\` +**Returns:** + +void + +", + "/api-documenter-test.docenumnamespacemerge.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocEnumNamespaceMerge](./api-documenter-test.docenumnamespacemerge.md) + +## DocEnumNamespaceMerge namespace + +Namespace that merges with enum + +**Signature:** + +\`\`\`typescript +export declare namespace DocEnumNamespaceMerge +\`\`\` + +## Functions + + + +
+ +Function + + + + +Description + + +
+ +[exampleFunction()](./api-documenter-test.docenumnamespacemerge.examplefunction.md) + + + + +This is a function inside of a namespace that merges with an enum. + + +
+ +", + "/api-documenter-test.ecmasymbols.example.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [EcmaSymbols](./api-documenter-test.ecmasymbols.md) > [example](./api-documenter-test.ecmasymbols.example.md) + +## EcmaSymbols.example variable + +An ECMAScript symbol + +**Signature:** + +\`\`\`typescript +example: unique symbol +\`\`\` +", + "/api-documenter-test.ecmasymbols.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [EcmaSymbols](./api-documenter-test.ecmasymbols.md) + +## EcmaSymbols namespace + +A namespace containing an ECMAScript symbol + +**Signature:** + +\`\`\`typescript +export declare namespace EcmaSymbols +\`\`\` + +## Variables + + + +
+ +Variable + + + + +Description + + +
+ +[example](./api-documenter-test.ecmasymbols.example.md) + + + + +An ECMAScript symbol + + +
+ +", + "/api-documenter-test.exampleduplicatetypealias.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [ExampleDuplicateTypeAlias](./api-documenter-test.exampleduplicatetypealias.md) + +## ExampleDuplicateTypeAlias type + +A type alias that has duplicate references. + +**Signature:** + +\`\`\`typescript +export type ExampleDuplicateTypeAlias = SystemEvent | typeof SystemEvent; +\`\`\` +**References:** [SystemEvent](./api-documenter-test.systemevent.md) + +", + "/api-documenter-test.examplefunction.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [exampleFunction](./api-documenter-test.examplefunction.md) + +## exampleFunction() function + +An exported function with hyperlinked parameters and return value. + +**Signature:** + +\`\`\`typescript +export declare function exampleFunction(x: ExampleTypeAlias, y: number): IDocInterface1; +\`\`\` + +## Parameters + + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +x + + + + +[ExampleTypeAlias](./api-documenter-test.exampletypealias.md) + + + + +an API item that should get hyperlinked + + +
+ +y + + + + +number + + + + +a system type that should NOT get hyperlinked + + +
+ +**Returns:** + +[IDocInterface1](./api-documenter-test.idocinterface1.md) + +an interface that should get hyperlinked + +", + "/api-documenter-test.exampletypealias.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [ExampleTypeAlias](./api-documenter-test.exampletypealias.md) + +## ExampleTypeAlias type + +A type alias + +**Signature:** + +\`\`\`typescript +export type ExampleTypeAlias = Promise; +\`\`\` +", + "/api-documenter-test.exampleuniontypealias.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [ExampleUnionTypeAlias](./api-documenter-test.exampleuniontypealias.md) + +## ExampleUnionTypeAlias type + +A type alias that references multiple other types. + +**Signature:** + +\`\`\`typescript +export type ExampleUnionTypeAlias = IDocInterface1 | IDocInterface3; +\`\`\` +**References:** [IDocInterface1](./api-documenter-test.idocinterface1.md), [IDocInterface3](./api-documenter-test.idocinterface3.md) + +", + "/api-documenter-test.generic.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [Generic](./api-documenter-test.generic.md) + +## Generic class + +Generic class. + +**Signature:** + +\`\`\`typescript +export declare class Generic +\`\`\` +", + "/api-documenter-test.generictypealias.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [GenericTypeAlias](./api-documenter-test.generictypealias.md) + +## GenericTypeAlias type + + +**Signature:** + +\`\`\`typescript +export type GenericTypeAlias = T[]; +\`\`\` +", + "/api-documenter-test.idocinterface1.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface1](./api-documenter-test.idocinterface1.md) + +## IDocInterface1 interface + + +**Signature:** + +\`\`\`typescript +export interface IDocInterface1 +\`\`\` + +## Properties + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[regularProperty](./api-documenter-test.idocinterface1.regularproperty.md) + + + + + + + +[SystemEvent](./api-documenter-test.systemevent.md) + + + + +Does something + + +
+ +", + "/api-documenter-test.idocinterface1.regularproperty.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface1](./api-documenter-test.idocinterface1.md) > [regularProperty](./api-documenter-test.idocinterface1.regularproperty.md) + +## IDocInterface1.regularProperty property + +Does something + +**Signature:** + +\`\`\`typescript +regularProperty: SystemEvent; +\`\`\` +", + "/api-documenter-test.idocinterface2.deprecatedexample.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface2](./api-documenter-test.idocinterface2.md) > [deprecatedExample](./api-documenter-test.idocinterface2.deprecatedexample.md) + +## IDocInterface2.deprecatedExample() method + +> Warning: This API is now obsolete. +> +> Use \`otherThing()\` instead. +> + +**Signature:** + +\`\`\`typescript +deprecatedExample(): void; +\`\`\` +**Returns:** + +void + +", + "/api-documenter-test.idocinterface2.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface2](./api-documenter-test.idocinterface2.md) + +## IDocInterface2 interface + + +**Signature:** + +\`\`\`typescript +export interface IDocInterface2 extends IDocInterface1 +\`\`\` +**Extends:** [IDocInterface1](./api-documenter-test.idocinterface1.md) + +## Methods + + + +
+ +Method + + + + +Description + + +
+ +[deprecatedExample()](./api-documenter-test.idocinterface2.deprecatedexample.md) + + + + + +
+ +", + "/api-documenter-test.idocinterface3.__not.a.symbol__.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface3](./api-documenter-test.idocinterface3.md) > [\\"\\\\[not.a.symbol\\\\]\\"](./api-documenter-test.idocinterface3.__not.a.symbol__.md) + +## IDocInterface3.\\"\\\\[not.a.symbol\\\\]\\" property + +An identifier that does need quotes. It misleadingly looks like an ECMAScript symbol. + +**Signature:** + +\`\`\`typescript +\\"[not.a.symbol]\\": string; +\`\`\` +", + "/api-documenter-test.idocinterface3._ecmasymbols.example_.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface3](./api-documenter-test.idocinterface3.md) > [\\\\[EcmaSymbols.example\\\\]](./api-documenter-test.idocinterface3._ecmasymbols.example_.md) + +## IDocInterface3.\\\\[EcmaSymbols.example\\\\] property + +ECMAScript symbol + +**Signature:** + +\`\`\`typescript +[EcmaSymbols.example]: string; +\`\`\` +", + "/api-documenter-test.idocinterface3._new_.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface3](./api-documenter-test.idocinterface3.md) > [(new)](./api-documenter-test.idocinterface3._new_.md) + +## IDocInterface3.(new) + +Construct signature + +**Signature:** + +\`\`\`typescript +new (): IDocInterface1; +\`\`\` +**Returns:** + +[IDocInterface1](./api-documenter-test.idocinterface1.md) + +", + "/api-documenter-test.idocinterface3.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface3](./api-documenter-test.idocinterface3.md) + +## IDocInterface3 interface + +Some less common TypeScript declaration kinds. + + +**Signature:** + +\`\`\`typescript +export interface IDocInterface3 +\`\`\` + +## Properties + + + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[\\"\\\\[not.a.symbol\\\\]\\"](./api-documenter-test.idocinterface3.__not.a.symbol__.md) + + + + + + + +string + + + + +An identifier that does need quotes. It misleadingly looks like an ECMAScript symbol. + + +
+ +[\\\\[EcmaSymbols.example\\\\]](./api-documenter-test.idocinterface3._ecmasymbols.example_.md) + + + + + + + +string + + + + +ECMAScript symbol + + +
+ +[redundantQuotes](./api-documenter-test.idocinterface3.redundantquotes.md) + + + + + + + +string + + + + +A quoted identifier with redundant quotes. + + +
+ +## Methods + + + +
+ +Method + + + + +Description + + +
+ +[(new)()](./api-documenter-test.idocinterface3._new_.md) + + + + +Construct signature + + +
+ +", + "/api-documenter-test.idocinterface3.redundantquotes.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface3](./api-documenter-test.idocinterface3.md) > [redundantQuotes](./api-documenter-test.idocinterface3.redundantquotes.md) + +## IDocInterface3.redundantQuotes property + +A quoted identifier with redundant quotes. + +**Signature:** + +\`\`\`typescript +\\"redundantQuotes\\": string; +\`\`\` +", + "/api-documenter-test.idocinterface4.context.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface4](./api-documenter-test.idocinterface4.md) > [Context](./api-documenter-test.idocinterface4.context.md) + +## IDocInterface4.Context property + +Test newline rendering when code blocks are used in tables + +**Signature:** + +\`\`\`typescript +Context: ({ children }: { + children: string; + }) => boolean; +\`\`\` +", + "/api-documenter-test.idocinterface4.generic.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface4](./api-documenter-test.idocinterface4.md) > [generic](./api-documenter-test.idocinterface4.generic.md) + +## IDocInterface4.generic property + +make sure html entities are escaped in tables. + +**Signature:** + +\`\`\`typescript +generic: Generic; +\`\`\` +", + "/api-documenter-test.idocinterface4.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface4](./api-documenter-test.idocinterface4.md) + +## IDocInterface4 interface + +Type union in an interface. + + +**Signature:** + +\`\`\`typescript +export interface IDocInterface4 +\`\`\` + +## Properties + + + + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[Context](./api-documenter-test.idocinterface4.context.md) + + + + + + + +({ children }: { children: string; }) => boolean + + + + +Test newline rendering when code blocks are used in tables + + +
+ +[generic](./api-documenter-test.idocinterface4.generic.md) + + + + + + + +[Generic](./api-documenter-test.generic.md)<number> + + + + +make sure html entities are escaped in tables. + + +
+ +[numberOrFunction](./api-documenter-test.idocinterface4.numberorfunction.md) + + + + + + + +number \\\\| (() => number) + + + + +a union type with a function + + +
+ +[stringOrNumber](./api-documenter-test.idocinterface4.stringornumber.md) + + + + + + + +string \\\\| number + + + + +a union type + + +
+ +", + "/api-documenter-test.idocinterface4.numberorfunction.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface4](./api-documenter-test.idocinterface4.md) > [numberOrFunction](./api-documenter-test.idocinterface4.numberorfunction.md) + +## IDocInterface4.numberOrFunction property + +a union type with a function + +**Signature:** + +\`\`\`typescript +numberOrFunction: number | (() => number); +\`\`\` +", + "/api-documenter-test.idocinterface4.stringornumber.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface4](./api-documenter-test.idocinterface4.md) > [stringOrNumber](./api-documenter-test.idocinterface4.stringornumber.md) + +## IDocInterface4.stringOrNumber property + +a union type + +**Signature:** + +\`\`\`typescript +stringOrNumber: string | number; +\`\`\` +", + "/api-documenter-test.idocinterface5.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface5](./api-documenter-test.idocinterface5.md) + +## IDocInterface5 interface + +Interface without inline tag to test custom TOC + +**Signature:** + +\`\`\`typescript +export interface IDocInterface5 +\`\`\` + +## Properties + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[regularProperty](./api-documenter-test.idocinterface5.regularproperty.md) + + + + + + + +string + + + + +Property of type string that does something + + +
+ +", + "/api-documenter-test.idocinterface5.regularproperty.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface5](./api-documenter-test.idocinterface5.md) > [regularProperty](./api-documenter-test.idocinterface5.regularproperty.md) + +## IDocInterface5.regularProperty property + +Property of type string that does something + +**Signature:** + +\`\`\`typescript +regularProperty: string; +\`\`\` + +## Default Value + +\\"Hello World\\" + +", + "/api-documenter-test.idocinterface6.arrayproperty.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) > [arrayProperty](./api-documenter-test.idocinterface6.arrayproperty.md) + +## IDocInterface6.arrayProperty property + +**Signature:** + +\`\`\`typescript +arrayProperty: IDocInterface1[]; +\`\`\` +", + "/api-documenter-test.idocinterface6.genericreferencemethod.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) > [genericReferenceMethod](./api-documenter-test.idocinterface6.genericreferencemethod.md) + +## IDocInterface6.genericReferenceMethod() method + +**Signature:** + +\`\`\`typescript +genericReferenceMethod(x: T): T; +\`\`\` + +## Parameters + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +x + + + + +T + + + + + +
+ +**Returns:** + +T + +", + "/api-documenter-test.idocinterface6.intersectionproperty.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) > [intersectionProperty](./api-documenter-test.idocinterface6.intersectionproperty.md) + +## IDocInterface6.intersectionProperty property + +**Signature:** + +\`\`\`typescript +intersectionProperty: IDocInterface1 & IDocInterface2; +\`\`\` +", + "/api-documenter-test.idocinterface6.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) + +## IDocInterface6 interface + +Interface without inline tag to test custom TOC with injection + +**Signature:** + +\`\`\`typescript +export interface IDocInterface6 +\`\`\` + +## Properties + + + + + + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[arrayProperty](./api-documenter-test.idocinterface6.arrayproperty.md) + + + + + + + +[IDocInterface1](./api-documenter-test.idocinterface1.md)\\\\[\\\\] + + + + + +
+ +[intersectionProperty](./api-documenter-test.idocinterface6.intersectionproperty.md) + + + + + + + +[IDocInterface1](./api-documenter-test.idocinterface1.md) & [IDocInterface2](./api-documenter-test.idocinterface2.md) + + + + + +
+ +[regularProperty](./api-documenter-test.idocinterface6.regularproperty.md) + + + + + + + +number + + + + +Property of type number that does something + + +
+ +[tupleProperty](./api-documenter-test.idocinterface6.tupleproperty.md) + + + + + + + +\\\\[[IDocInterface1](./api-documenter-test.idocinterface1.md), [IDocInterface2](./api-documenter-test.idocinterface2.md)\\\\] + + + + + +
+ +[typeReferenceProperty](./api-documenter-test.idocinterface6.typereferenceproperty.md) + + + + + + + +[Generic](./api-documenter-test.generic.md)<[IDocInterface1](./api-documenter-test.idocinterface1.md)> + + + + + +
+ +[unionProperty](./api-documenter-test.idocinterface6.unionproperty.md) + + + + + + + +[IDocInterface1](./api-documenter-test.idocinterface1.md) \\\\| [IDocInterface2](./api-documenter-test.idocinterface2.md) + + + + + +
+ +## Methods + + + +
+ +Method + + + + +Description + + +
+ +[genericReferenceMethod(x)](./api-documenter-test.idocinterface6.genericreferencemethod.md) + + + + + +
+ +", + "/api-documenter-test.idocinterface6.regularproperty.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) > [regularProperty](./api-documenter-test.idocinterface6.regularproperty.md) + +## IDocInterface6.regularProperty property + +Property of type number that does something + +**Signature:** + +\`\`\`typescript +regularProperty: number; +\`\`\` +", + "/api-documenter-test.idocinterface6.tupleproperty.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) > [tupleProperty](./api-documenter-test.idocinterface6.tupleproperty.md) + +## IDocInterface6.tupleProperty property + +**Signature:** + +\`\`\`typescript +tupleProperty: [IDocInterface1, IDocInterface2]; +\`\`\` +", + "/api-documenter-test.idocinterface6.typereferenceproperty.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) > [typeReferenceProperty](./api-documenter-test.idocinterface6.typereferenceproperty.md) + +## IDocInterface6.typeReferenceProperty property + +**Signature:** + +\`\`\`typescript +typeReferenceProperty: Generic; +\`\`\` +", + "/api-documenter-test.idocinterface6.unionproperty.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface6](./api-documenter-test.idocinterface6.md) > [unionProperty](./api-documenter-test.idocinterface6.unionproperty.md) + +## IDocInterface6.unionProperty property + +**Signature:** + +\`\`\`typescript +unionProperty: IDocInterface1 | IDocInterface2; +\`\`\` +", + "/api-documenter-test.idocinterface7.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface7](./api-documenter-test.idocinterface7.md) + +## IDocInterface7 interface + +Interface for testing optional properties + +**Signature:** + +\`\`\`typescript +export interface IDocInterface7 +\`\`\` + +## Properties + + + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[optionalField?](./api-documenter-test.idocinterface7.optionalfield.md) + + + + + + + +boolean + + + + +_(Optional)_ Description of optionalField + + +
+ +[optionalReadonlyField?](./api-documenter-test.idocinterface7.optionalreadonlyfield.md) + + + + +\`readonly\` + + + + +boolean + + + + +_(Optional)_ Description of optionalReadonlyField + + +
+ +[optionalUndocumentedField?](./api-documenter-test.idocinterface7.optionalundocumentedfield.md) + + + + + + + +boolean + + + + +_(Optional)_ + + +
+ +## Methods + + + +
+ +Method + + + + +Description + + +
+ +[optionalMember()?](./api-documenter-test.idocinterface7.optionalmember.md) + + + + +_(Optional)_ Description of optionalMember + + +
+ +", + "/api-documenter-test.idocinterface7.optionalfield.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface7](./api-documenter-test.idocinterface7.md) > [optionalField](./api-documenter-test.idocinterface7.optionalfield.md) + +## IDocInterface7.optionalField property + +Description of optionalField + +**Signature:** + +\`\`\`typescript +optionalField?: boolean; +\`\`\` +", + "/api-documenter-test.idocinterface7.optionalmember.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface7](./api-documenter-test.idocinterface7.md) > [optionalMember](./api-documenter-test.idocinterface7.optionalmember.md) + +## IDocInterface7.optionalMember() method + +Description of optionalMember + +**Signature:** + +\`\`\`typescript +optionalMember?(): any; +\`\`\` +**Returns:** + +any + +", + "/api-documenter-test.idocinterface7.optionalreadonlyfield.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface7](./api-documenter-test.idocinterface7.md) > [optionalReadonlyField](./api-documenter-test.idocinterface7.optionalreadonlyfield.md) + +## IDocInterface7.optionalReadonlyField property + +Description of optionalReadonlyField + +**Signature:** + +\`\`\`typescript +readonly optionalReadonlyField?: boolean; +\`\`\` +", + "/api-documenter-test.idocinterface7.optionalundocumentedfield.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [IDocInterface7](./api-documenter-test.idocinterface7.md) > [optionalUndocumentedField](./api-documenter-test.idocinterface7.optionalundocumentedfield.md) + +## IDocInterface7.optionalUndocumentedField property + +**Signature:** + +\`\`\`typescript +optionalUndocumentedField?: boolean; +\`\`\` +", + "/api-documenter-test.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) + +## api-documenter-test package + +api-extractor-test-05 + +This project tests various documentation generation scenarios and doc comment syntaxes. + +## Classes + + + + + + + + +
+ +Class + + + + +Description + + +
+ +[DecoratorExample](./api-documenter-test.decoratorexample.md) + + + + + + +
+ +[DocBaseClass](./api-documenter-test.docbaseclass.md) + + + + +Example base class + + + +
+ +[DocClass1](./api-documenter-test.docclass1.md) + + + + +This is an example class. + + +
+ +[DocClassInterfaceMerge](./api-documenter-test.docclassinterfacemerge.md) + + + + +Class that merges with interface + + +
+ +[Generic](./api-documenter-test.generic.md) + + + + +Generic class. + + +
+ +[SystemEvent](./api-documenter-test.systemevent.md) + + + + +A class used to exposed events. + + + +
+ +## Abstract Classes + + + +
+ +Abstract Class + + + + +Description + + +
+ +[AbstractClass](./api-documenter-test.abstractclass.md) + + + + +Some abstract class with abstract members. + + +
+ +## Enumerations + + + + +
+ +Enumeration + + + + +Description + + +
+ +[DocEnum](./api-documenter-test.docenum.md) + + + + +Docs for DocEnum + + + +
+ +[DocEnumNamespaceMerge](./api-documenter-test.docenumnamespacemerge.md) + + + + +Enum that merges with namespace + + +
+ +## Functions + + + + +
+ +Function + + + + +Description + + +
+ +[exampleFunction(x, y)](./api-documenter-test.examplefunction.md) + + + + +An exported function with hyperlinked parameters and return value. + + +
+ +[yamlReferenceUniquenessTest()](./api-documenter-test.yamlreferenceuniquenesstest.md) + + + + + + +
+ +## Interfaces + + + + + + + + + + + + +
+ +Interface + + + + +Description + + +
+ +[Constraint](./api-documenter-test.constraint.md) + + + + +Type parameter constraint used by test case below. + + +
+ +[DefaultType](./api-documenter-test.defaulttype.md) + + + + +Type parameter default type used by test case below. + + +
+ +[DocClassInterfaceMerge](./api-documenter-test.docclassinterfacemerge.md) + + + + +Interface that merges with class + + +
+ +[IDocInterface1](./api-documenter-test.idocinterface1.md) + + + + + + +
+ +[IDocInterface2](./api-documenter-test.idocinterface2.md) + + + + + + +
+ +[IDocInterface3](./api-documenter-test.idocinterface3.md) + + + + +Some less common TypeScript declaration kinds. + + + +
+ +[IDocInterface4](./api-documenter-test.idocinterface4.md) + + + + +Type union in an interface. + + + +
+ +[IDocInterface5](./api-documenter-test.idocinterface5.md) + + + + +Interface without inline tag to test custom TOC + + +
+ +[IDocInterface6](./api-documenter-test.idocinterface6.md) + + + + +Interface without inline tag to test custom TOC with injection + + +
+ +[IDocInterface7](./api-documenter-test.idocinterface7.md) + + + + +Interface for testing optional properties + + +
+ +## Namespaces + + + + + +
+ +Namespace + + + + +Description + + +
+ +[DocEnumNamespaceMerge](./api-documenter-test.docenumnamespacemerge.md) + + + + +Namespace that merges with enum + + +
+ +[EcmaSymbols](./api-documenter-test.ecmasymbols.md) + + + + +A namespace containing an ECMAScript symbol + + +
+ +[OuterNamespace](./api-documenter-test.outernamespace.md) + + + + +A top-level namespace + + +
+ +## Variables + + + +
+ +Variable + + + + +Description + + +
+ +[constVariable](./api-documenter-test.constvariable.md) + + + + +An exported variable declaration. + + +
+ +## Type Aliases + + + + + + + +
+ +Type Alias + + + + +Description + + +
+ +[ExampleDuplicateTypeAlias](./api-documenter-test.exampleduplicatetypealias.md) + + + + +A type alias that has duplicate references. + + +
+ +[ExampleTypeAlias](./api-documenter-test.exampletypealias.md) + + + + +A type alias + + +
+ +[ExampleUnionTypeAlias](./api-documenter-test.exampleuniontypealias.md) + + + + +A type alias that references multiple other types. + + +
+ +[GenericTypeAlias](./api-documenter-test.generictypealias.md) + + + + + + +
+ +[TypeAlias](./api-documenter-test.typealias.md) + + + + + + +
+ +", + "/api-documenter-test.outernamespace.innernamespace.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [OuterNamespace](./api-documenter-test.outernamespace.md) > [InnerNamespace](./api-documenter-test.outernamespace.innernamespace.md) + +## OuterNamespace.InnerNamespace namespace + +A nested namespace + +**Signature:** + +\`\`\`typescript +namespace InnerNamespace +\`\`\` + +## Functions + + + +
+ +Function + + + + +Description + + +
+ +[nestedFunction(x)](./api-documenter-test.outernamespace.innernamespace.nestedfunction.md) + + + + +A function inside a namespace + + +
+ +", + "/api-documenter-test.outernamespace.innernamespace.nestedfunction.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [OuterNamespace](./api-documenter-test.outernamespace.md) > [InnerNamespace](./api-documenter-test.outernamespace.innernamespace.md) > [nestedFunction](./api-documenter-test.outernamespace.innernamespace.nestedfunction.md) + +## OuterNamespace.InnerNamespace.nestedFunction() function + +A function inside a namespace + +**Signature:** + +\`\`\`typescript +function nestedFunction(x: number): number; +\`\`\` + +## Parameters + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +x + + + + +number + + + + + +
+ +**Returns:** + +number + +", + "/api-documenter-test.outernamespace.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [OuterNamespace](./api-documenter-test.outernamespace.md) + +## OuterNamespace namespace + +A top-level namespace + +**Signature:** + +\`\`\`typescript +export declare namespace OuterNamespace +\`\`\` + +## Namespaces + + + +
+ +Namespace + + + + +Description + + +
+ +[InnerNamespace](./api-documenter-test.outernamespace.innernamespace.md) + + + + +A nested namespace + + +
+ +## Variables + + + +
+ +Variable + + + + +Description + + +
+ +[nestedVariable](./api-documenter-test.outernamespace.nestedvariable.md) + + + + +A variable exported from within a namespace. + + +
+ +", + "/api-documenter-test.outernamespace.nestedvariable.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [OuterNamespace](./api-documenter-test.outernamespace.md) > [nestedVariable](./api-documenter-test.outernamespace.nestedvariable.md) + +## OuterNamespace.nestedVariable variable + +A variable exported from within a namespace. + +**Signature:** + +\`\`\`typescript +nestedVariable: boolean +\`\`\` +", + "/api-documenter-test.systemevent.addhandler.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [SystemEvent](./api-documenter-test.systemevent.md) > [addHandler](./api-documenter-test.systemevent.addhandler.md) + +## SystemEvent.addHandler() method + +Adds an handler for the event. + +**Signature:** + +\`\`\`typescript +addHandler(handler: () => void): void; +\`\`\` + +## Parameters + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +handler + + + + +() => void + + + + + +
+ +**Returns:** + +void + +", + "/api-documenter-test.systemevent.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [SystemEvent](./api-documenter-test.systemevent.md) + +## SystemEvent class + +A class used to exposed events. + + +**Signature:** + +\`\`\`typescript +export declare class SystemEvent +\`\`\` + +## Methods + + + +
+ +Method + + + + +Modifiers + + + + +Description + + +
+ +[addHandler(handler)](./api-documenter-test.systemevent.addhandler.md) + + + + + + + +Adds an handler for the event. + + +
+ +", + "/api-documenter-test.typealias.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [TypeAlias](./api-documenter-test.typealias.md) + +## TypeAlias type + + +**Signature:** + +\`\`\`typescript +export type TypeAlias = number; +\`\`\` +", + "/api-documenter-test.yamlreferenceuniquenesstest.md": " + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [yamlReferenceUniquenessTest](./api-documenter-test.yamlreferenceuniquenesstest.md) + +## yamlReferenceUniquenessTest() function + + +**Signature:** + +\`\`\`typescript +export declare function yamlReferenceUniquenessTest(): IDocInterface1; +\`\`\` +**Returns:** + +[IDocInterface1](./api-documenter-test.idocinterface1.md) + +", + "/index.md": " + +[Home](./index.md) + +## API Reference + +## Packages + + + +
+ +Package + + + + +Description + + +
+ +[api-documenter-test](./api-documenter-test.md) + + + + +api-extractor-test-05 + +This project tests various documentation generation scenarios and doc comment syntaxes. + + +
+ +", +} +`; diff --git a/build-tests/api-documenter-test/src/test/snapshot.test.ts b/build-tests/api-documenter-test/src/test/snapshot.test.ts new file mode 100644 index 00000000000..a1019e7d538 --- /dev/null +++ b/build-tests/api-documenter-test/src/test/snapshot.test.ts @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { + Async, + Executable, + FileSystem, + type FolderItem, + PackageJsonLookup +} from '@rushstack/node-core-library'; +import process from 'node:process'; + +const PROJECT_FOLDER: string | undefined = PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname); +const API_DOCUMENTER_PATH: string = require.resolve('@microsoft/api-documenter/lib/start'); + +interface IFolderItem { + absolutePath: string; + relativePath: string; +} +async function* readFolderItemsAsync( + folderAbsolutePath: string, + folderRelativePat: string +): AsyncIterable { + const folderItems: FolderItem[] = await FileSystem.readFolderItemsAsync(folderAbsolutePath); + for (const folderItem of folderItems) { + const itemAbsolutePath: string = `${folderAbsolutePath}/${folderItem.name}`; + const itemRelativePath: string = `${folderRelativePat}/${folderItem.name}`; + if (folderItem.isFile()) { + yield { absolutePath: itemAbsolutePath, relativePath: itemRelativePath }; + } else { + yield* readFolderItemsAsync(itemAbsolutePath, itemRelativePath); + } + } +} + +async function runApiDocumenterAsync(verb: string, outputFolderName: string): Promise { + if (!PROJECT_FOLDER) { + throw new Error('Cannot find package.json'); + } + + const outputPath: string = `${PROJECT_FOLDER}/temp/${outputFolderName}`; + + const apiDocumenterProcess = Executable.spawn( + process.argv0, + [API_DOCUMENTER_PATH, verb, '--input-folder', 'etc', '--output-folder', outputPath], + { + currentWorkingDirectory: PROJECT_FOLDER, + stdio: 'pipe' + } + ); + + const { exitCode } = await Executable.waitForExitAsync(apiDocumenterProcess); + + expect(exitCode).toBe(0); + + const itemContents: Record = {}; + await Async.forEachAsync( + readFolderItemsAsync(outputPath, ''), + async ({ relativePath, absolutePath }) => { + itemContents[relativePath] = await FileSystem.readFileAsync(absolutePath); + }, + { concurrency: 50 } + ); + + const sortedEntries: [string, string][] = Object.entries(itemContents).sort(([a], [b]) => + a.localeCompare(b) + ); + expect(Object.fromEntries(sortedEntries)).toMatchSnapshot('itemContents'); +} + +describe('api-documenter', () => { + it('YAML', async () => { + await runApiDocumenterAsync('generate', 'yaml'); + }); + + it('markdown', async () => { + await runApiDocumenterAsync('markdown', 'markdown'); + }); +}); diff --git a/build-tests/api-documenter-test/tsconfig.json b/build-tests/api-documenter-test/tsconfig.json index 1799652cc42..d1f567b6358 100644 --- a/build-tests/api-documenter-test/tsconfig.json +++ b/build-tests/api-documenter-test/tsconfig.json @@ -1,16 +1,7 @@ { + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "target": "es6", - "forceConsistentCasingInFileNames": true, - "module": "commonjs", - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "types": ["node", "jest"], - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "outDir": "lib" - }, - "include": ["src/**/*.ts", "typings/tsd.d.ts"] + "strictPropertyInitialization": false, + "noImplicitAny": false + } } diff --git a/build-tests/api-extractor-test-03/.gitignore b/build-tests/api-extractor-d-cts-test/.gitignore similarity index 100% rename from build-tests/api-extractor-test-03/.gitignore rename to build-tests/api-extractor-d-cts-test/.gitignore diff --git a/build-tests/api-extractor-d-cts-test/config/api-extractor.json b/build-tests/api-extractor-d-cts-test/config/api-extractor.json new file mode 100644 index 00000000000..570369b31a2 --- /dev/null +++ b/build-tests/api-extractor-d-cts-test/config/api-extractor.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + "mainEntryPointFilePath": "/lib-dts/index.d.cts", + + "apiReport": { + "enabled": true + }, + + "docModel": { + "enabled": true + }, + + "dtsRollup": { + "enabled": true, + "untrimmedFilePath": "/dist/.d.cts", + "alphaTrimmedFilePath": "/dist/-alpha.d.cts", + "publicTrimmedFilePath": "/dist/-public.d.cts" + }, + + "testMode": true +} diff --git a/build-tests/api-extractor-d-cts-test/config/rig.json b/build-tests/api-extractor-d-cts-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-extractor-d-cts-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-extractor-d-cts-test/etc/api-extractor-d-cts-test.api.md b/build-tests/api-extractor-d-cts-test/etc/api-extractor-d-cts-test.api.md new file mode 100644 index 00000000000..163443bda1a --- /dev/null +++ b/build-tests/api-extractor-d-cts-test/etc/api-extractor-d-cts-test.api.md @@ -0,0 +1,22 @@ +## API Report File for "api-extractor-d-cts-test" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public (undocumented) +class DefaultClass { +} +export default DefaultClass; + +// @public (undocumented) +export class Lib5Class { + // (undocumented) + prop: number; +} + +// @alpha (undocumented) +export interface Lib5Interface { +} + +``` diff --git a/build-tests/api-extractor-d-cts-test/package.json b/build-tests/api-extractor-d-cts-test/package.json new file mode 100644 index 00000000000..16b197dd289 --- /dev/null +++ b/build-tests/api-extractor-d-cts-test/package.json @@ -0,0 +1,37 @@ +{ + "name": "api-extractor-d-cts-test", + "description": "Building this project is a regression test for api-extractor", + "version": "1.0.0", + "private": true, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/api-extractor-d-cts-test.d.cts", + "exports": { + ".": { + "types": "./dist/api-extractor-d-cts-test.d.cts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" + } +} diff --git a/build-tests/api-extractor-d-cts-test/src/index.cts b/build-tests/api-extractor-d-cts-test/src/index.cts new file mode 100644 index 00000000000..754a8ba3af5 --- /dev/null +++ b/build-tests/api-extractor-d-cts-test/src/index.cts @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * api-extractor-d-cts-test + * + * @remarks + * This library is consumed by api-extractor-scenarios. + * + * @packageDocumentation + */ + +/** @public */ +export class Lib5Class { + prop: number; +} + +/** @alpha */ +export interface Lib5Interface {} + +/** @public */ +export default class DefaultClass {} diff --git a/build-tests/api-extractor-d-cts-test/tsconfig.json b/build-tests/api-extractor-d-cts-test/tsconfig.json new file mode 100644 index 00000000000..00f29d003a2 --- /dev/null +++ b/build-tests/api-extractor-d-cts-test/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", + "compilerOptions": { + "strictPropertyInitialization": false + }, + "include": ["src/**/*.cts", "typings/tsd.d.ts"] +} diff --git a/build-tests/api-extractor-d-mts-test/.gitignore b/build-tests/api-extractor-d-mts-test/.gitignore new file mode 100644 index 00000000000..e730b77542b --- /dev/null +++ b/build-tests/api-extractor-d-mts-test/.gitignore @@ -0,0 +1,4 @@ +# This project's outputs are tracked to surface changes to API Extractor rollups during PRs +!dist +dist/* +!dist/*.d.ts diff --git a/build-tests/api-extractor-d-mts-test/config/api-extractor.json b/build-tests/api-extractor-d-mts-test/config/api-extractor.json new file mode 100644 index 00000000000..c84f19c8801 --- /dev/null +++ b/build-tests/api-extractor-d-mts-test/config/api-extractor.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + "mainEntryPointFilePath": "/lib-dts/index.d.mts", + + "apiReport": { + "enabled": true + }, + + "docModel": { + "enabled": true + }, + + "dtsRollup": { + "enabled": true, + "untrimmedFilePath": "/dist/.d.mts", + "alphaTrimmedFilePath": "/dist/-alpha.d.mts", + "publicTrimmedFilePath": "/dist/-public.d.mts" + }, + + "testMode": true +} diff --git a/build-tests/api-extractor-d-mts-test/config/rig.json b/build-tests/api-extractor-d-mts-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-extractor-d-mts-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-extractor-d-mts-test/etc/api-extractor-d-mts-test.api.md b/build-tests/api-extractor-d-mts-test/etc/api-extractor-d-mts-test.api.md new file mode 100644 index 00000000000..f1aeecb6710 --- /dev/null +++ b/build-tests/api-extractor-d-mts-test/etc/api-extractor-d-mts-test.api.md @@ -0,0 +1,22 @@ +## API Report File for "api-extractor-d-mts-test" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public (undocumented) +class DefaultClass { +} +export default DefaultClass; + +// @public (undocumented) +export class Lib4Class { + // (undocumented) + prop: number; +} + +// @alpha (undocumented) +export interface Lib4Interface { +} + +``` diff --git a/build-tests/api-extractor-d-mts-test/package.json b/build-tests/api-extractor-d-mts-test/package.json new file mode 100644 index 00000000000..9adb070d919 --- /dev/null +++ b/build-tests/api-extractor-d-mts-test/package.json @@ -0,0 +1,31 @@ +{ + "name": "api-extractor-d-mts-test", + "description": "Building this project is a regression test for api-extractor", + "version": "1.0.0", + "private": true, + "type": "module", + "types": "./dist/api-extractor-d-mts-test.d.mts", + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" + } +} diff --git a/build-tests/api-extractor-d-mts-test/src/index.mts b/build-tests/api-extractor-d-mts-test/src/index.mts new file mode 100644 index 00000000000..6b2966543e7 --- /dev/null +++ b/build-tests/api-extractor-d-mts-test/src/index.mts @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * api-extractor-d-mts-test + * + * @remarks + * This library is consumed by api-extractor-scenarios. + * + * @packageDocumentation + */ + +/** @public */ +export class Lib4Class { + prop: number; +} + +/** @alpha */ +export interface Lib4Interface {} + +/** @public */ +export default class DefaultClass {} diff --git a/build-tests/api-extractor-d-mts-test/tsconfig.json b/build-tests/api-extractor-d-mts-test/tsconfig.json new file mode 100644 index 00000000000..85306f1bec6 --- /dev/null +++ b/build-tests/api-extractor-d-mts-test/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", + "compilerOptions": { + "strictPropertyInitialization": false + }, + "include": ["src/**/*.mts", "typings/tsd.d.ts"] +} diff --git a/build-tests/api-extractor-lib1-test/build.js b/build-tests/api-extractor-lib1-test/build.js deleted file mode 100644 index 6378977fc0b..00000000000 --- a/build-tests/api-extractor-lib1-test/build.js +++ /dev/null @@ -1,27 +0,0 @@ -const fsx = require('fs-extra'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -fsx.emptyDirSync('dist'); -fsx.emptyDirSync('lib'); -fsx.emptyDirSync('temp'); - -// Run the TypeScript compiler -executeCommand('node node_modules/typescript/lib/tsc'); - -// Run the API Extractor command-line -if (process.argv.indexOf('--production') >= 0) { - executeCommand('node node_modules/@microsoft/api-extractor/lib/start run'); -} else { - executeCommand('node node_modules/@microsoft/api-extractor/lib/start run --local'); -} - -console.log(`==> Finished build.js for ${path.basename(process.cwd())}`); diff --git a/build-tests/api-extractor-lib1-test/config/api-extractor.json b/build-tests/api-extractor-lib1-test/config/api-extractor.json index 609b62dc032..6f78a23add0 100644 --- a/build-tests/api-extractor-lib1-test/config/api-extractor.json +++ b/build-tests/api-extractor-lib1-test/config/api-extractor.json @@ -1,10 +1,11 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - "mainEntryPointFilePath": "/lib/index.d.ts", + "mainEntryPointFilePath": "/lib-dts/index.d.ts", "apiReport": { - "enabled": true + "enabled": true, + "reportFileName": "" }, "docModel": { diff --git a/build-tests/api-extractor-lib1-test/config/rig.json b/build-tests/api-extractor-lib1-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-extractor-lib1-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-extractor-lib1-test/config/rush-project.json b/build-tests/api-extractor-lib1-test/config/rush-project.json index 247dc17187a..6c75ba02c45 100644 --- a/build-tests/api-extractor-lib1-test/config/rush-project.json +++ b/build-tests/api-extractor-lib1-test/config/rush-project.json @@ -1,8 +1,11 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + // dist is intentionally tracked + "outputFolderNames": ["lib-commonjs", "lib-esm", "lib-dts"] } ] } diff --git a/build-tests/api-extractor-lib1-test/package.json b/build-tests/api-extractor-lib1-test/package.json index 7c3be140959..058fda700bf 100644 --- a/build-tests/api-extractor-lib1-test/package.json +++ b/build-tests/api-extractor-lib1-test/package.json @@ -3,15 +3,36 @@ "description": "Building this project is a regression test for api-extractor", "version": "1.0.0", "private": true, - "main": "lib/index.js", - "typings": "dist/api-extractor-lib1-test.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/api-extractor-lib1-test.d.ts", + "exports": { + ".": { + "types": "./dist/api-extractor-lib1-test.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "scripts": { - "build": "node build.js", - "_phase:build": "node build.js" + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" }, "devDependencies": { - "@microsoft/api-extractor": "workspace:*", - "fs-extra": "~7.0.1", + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*", "typescript": "~2.9.2" } } diff --git a/build-tests/api-extractor-lib1-test/tsconfig.json b/build-tests/api-extractor-lib1-test/tsconfig.json index b0538f2bd78..b7e4568513a 100644 --- a/build-tests/api-extractor-lib1-test/tsconfig.json +++ b/build-tests/api-extractor-lib1-test/tsconfig.json @@ -1,3 +1,4 @@ +// This project is using TypeScript 2, so we can't extend from the rig { "compilerOptions": { "target": "es6", @@ -7,8 +8,9 @@ "sourceMap": true, "experimentalDecorators": true, "strictNullChecks": true, - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "outDir": "lib" + "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable"], + "outDir": "lib-commonjs", + "declarationDir": "lib-dts" }, "include": ["src/**/*.ts"] } diff --git a/build-tests/api-extractor-lib2-test/build.js b/build-tests/api-extractor-lib2-test/build.js deleted file mode 100644 index 6378977fc0b..00000000000 --- a/build-tests/api-extractor-lib2-test/build.js +++ /dev/null @@ -1,27 +0,0 @@ -const fsx = require('fs-extra'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -fsx.emptyDirSync('dist'); -fsx.emptyDirSync('lib'); -fsx.emptyDirSync('temp'); - -// Run the TypeScript compiler -executeCommand('node node_modules/typescript/lib/tsc'); - -// Run the API Extractor command-line -if (process.argv.indexOf('--production') >= 0) { - executeCommand('node node_modules/@microsoft/api-extractor/lib/start run'); -} else { - executeCommand('node node_modules/@microsoft/api-extractor/lib/start run --local'); -} - -console.log(`==> Finished build.js for ${path.basename(process.cwd())}`); diff --git a/build-tests/api-extractor-lib2-test/config/api-extractor.json b/build-tests/api-extractor-lib2-test/config/api-extractor.json index 609b62dc032..ccf32660858 100644 --- a/build-tests/api-extractor-lib2-test/config/api-extractor.json +++ b/build-tests/api-extractor-lib2-test/config/api-extractor.json @@ -1,10 +1,12 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - "mainEntryPointFilePath": "/lib/index.d.ts", + "mainEntryPointFilePath": "/lib-dts/index.d.ts", "apiReport": { - "enabled": true + "enabled": true, + "reportFileName": ".api.md", + "reportVariants": ["alpha", "public", "complete"] }, "docModel": { diff --git a/build-tests/api-extractor-lib2-test/config/rig.json b/build-tests/api-extractor-lib2-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-extractor-lib2-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-extractor-lib2-test/config/rush-project.json b/build-tests/api-extractor-lib2-test/config/rush-project.json index 247dc17187a..6c75ba02c45 100644 --- a/build-tests/api-extractor-lib2-test/config/rush-project.json +++ b/build-tests/api-extractor-lib2-test/config/rush-project.json @@ -1,8 +1,11 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + // dist is intentionally tracked + "outputFolderNames": ["lib-commonjs", "lib-esm", "lib-dts"] } ] } diff --git a/build-tests/api-extractor-lib2-test/dist/api-extractor-lib2-test.d.ts b/build-tests/api-extractor-lib2-test/dist/api-extractor-lib2-test.d.ts index 3f860232b9f..2816f40985f 100644 --- a/build-tests/api-extractor-lib2-test/dist/api-extractor-lib2-test.d.ts +++ b/build-tests/api-extractor-lib2-test/dist/api-extractor-lib2-test.d.ts @@ -7,7 +7,7 @@ * @packageDocumentation */ -/** @public */ +/** @beta */ declare class DefaultClass { } export default DefaultClass; diff --git a/build-tests/api-extractor-lib2-test/etc/api-extractor-lib2-test.alpha.api.md b/build-tests/api-extractor-lib2-test/etc/api-extractor-lib2-test.alpha.api.md new file mode 100644 index 00000000000..145cfdd01b5 --- /dev/null +++ b/build-tests/api-extractor-lib2-test/etc/api-extractor-lib2-test.alpha.api.md @@ -0,0 +1,22 @@ +## Alpha API Report File for "api-extractor-lib2-test" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @beta (undocumented) +class DefaultClass { +} +export default DefaultClass; + +// @public (undocumented) +export class Lib2Class { + // (undocumented) + prop: number; +} + +// @alpha (undocumented) +export interface Lib2Interface { +} + +``` diff --git a/build-tests/api-extractor-lib2-test/etc/api-extractor-lib2-test.api.md b/build-tests/api-extractor-lib2-test/etc/api-extractor-lib2-test.api.md index e89ae310395..e3df0b5f94d 100644 --- a/build-tests/api-extractor-lib2-test/etc/api-extractor-lib2-test.api.md +++ b/build-tests/api-extractor-lib2-test/etc/api-extractor-lib2-test.api.md @@ -4,7 +4,7 @@ ```ts -// @public (undocumented) +// @beta (undocumented) class DefaultClass { } export default DefaultClass; diff --git a/build-tests/api-extractor-lib2-test/etc/api-extractor-lib2-test.public.api.md b/build-tests/api-extractor-lib2-test/etc/api-extractor-lib2-test.public.api.md new file mode 100644 index 00000000000..87ca7608c88 --- /dev/null +++ b/build-tests/api-extractor-lib2-test/etc/api-extractor-lib2-test.public.api.md @@ -0,0 +1,13 @@ +## Public API Report File for "api-extractor-lib2-test" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public (undocumented) +export class Lib2Class { + // (undocumented) + prop: number; +} + +``` diff --git a/build-tests/api-extractor-lib2-test/package.json b/build-tests/api-extractor-lib2-test/package.json index f7a1d9f1a58..1f7dda6b570 100644 --- a/build-tests/api-extractor-lib2-test/package.json +++ b/build-tests/api-extractor-lib2-test/package.json @@ -3,17 +3,35 @@ "description": "Building this project is a regression test for api-extractor", "version": "1.0.0", "private": true, - "main": "lib/index.js", - "typings": "dist/api-extractor-lib2-test.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/api-extractor-lib2-test.d.ts", + "exports": { + ".": { + "types": "./dist/api-extractor-lib2-test.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "scripts": { - "build": "node build.js", - "_phase:build": "node build.js" + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" }, "devDependencies": { - "@microsoft/api-extractor": "workspace:*", - "@types/jest": "29.2.5", - "@types/node": "14.18.36", - "fs-extra": "~7.0.1", - "typescript": "~5.0.4" + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" } } diff --git a/build-tests/api-extractor-lib2-test/src/index.ts b/build-tests/api-extractor-lib2-test/src/index.ts index 6fd2ffc71cb..616e9f7f9f2 100644 --- a/build-tests/api-extractor-lib2-test/src/index.ts +++ b/build-tests/api-extractor-lib2-test/src/index.ts @@ -18,5 +18,5 @@ export class Lib2Class { /** @alpha */ export interface Lib2Interface {} -/** @public */ +/** @beta */ export default class DefaultClass {} diff --git a/build-tests/api-extractor-lib2-test/tsconfig.json b/build-tests/api-extractor-lib2-test/tsconfig.json index 1799652cc42..d5641d0ba38 100644 --- a/build-tests/api-extractor-lib2-test/tsconfig.json +++ b/build-tests/api-extractor-lib2-test/tsconfig.json @@ -1,16 +1,6 @@ { + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "target": "es6", - "forceConsistentCasingInFileNames": true, - "module": "commonjs", - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "types": ["node", "jest"], - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "outDir": "lib" - }, - "include": ["src/**/*.ts", "typings/tsd.d.ts"] + "strictPropertyInitialization": false + } } diff --git a/build-tests/api-extractor-lib3-test/build.js b/build-tests/api-extractor-lib3-test/build.js deleted file mode 100644 index 6378977fc0b..00000000000 --- a/build-tests/api-extractor-lib3-test/build.js +++ /dev/null @@ -1,27 +0,0 @@ -const fsx = require('fs-extra'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -fsx.emptyDirSync('dist'); -fsx.emptyDirSync('lib'); -fsx.emptyDirSync('temp'); - -// Run the TypeScript compiler -executeCommand('node node_modules/typescript/lib/tsc'); - -// Run the API Extractor command-line -if (process.argv.indexOf('--production') >= 0) { - executeCommand('node node_modules/@microsoft/api-extractor/lib/start run'); -} else { - executeCommand('node node_modules/@microsoft/api-extractor/lib/start run --local'); -} - -console.log(`==> Finished build.js for ${path.basename(process.cwd())}`); diff --git a/build-tests/api-extractor-lib3-test/config/api-extractor.json b/build-tests/api-extractor-lib3-test/config/api-extractor.json index 609b62dc032..4507f009769 100644 --- a/build-tests/api-extractor-lib3-test/config/api-extractor.json +++ b/build-tests/api-extractor-lib3-test/config/api-extractor.json @@ -1,10 +1,17 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - "mainEntryPointFilePath": "/lib/index.d.ts", + "mainEntryPointFilePath": "/lib-dts/index.d.ts", "apiReport": { - "enabled": true + "enabled": true, + "tagsToReport": { + // Disable reporting of `@virtual`, which is reported by default + "@virtual": false, + // Enable reporting of our custom TSDoc tags. + "@internalRemarks": true, + "@betaDocumentation": true + } }, "docModel": { diff --git a/build-tests/api-extractor-lib3-test/config/rig.json b/build-tests/api-extractor-lib3-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-extractor-lib3-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-extractor-lib3-test/config/rush-project.json b/build-tests/api-extractor-lib3-test/config/rush-project.json index 247dc17187a..6c75ba02c45 100644 --- a/build-tests/api-extractor-lib3-test/config/rush-project.json +++ b/build-tests/api-extractor-lib3-test/config/rush-project.json @@ -1,8 +1,11 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + // dist is intentionally tracked + "outputFolderNames": ["lib-commonjs", "lib-esm", "lib-dts"] } ] } diff --git a/build-tests/api-extractor-lib3-test/dist/api-extractor-lib3-test.d.ts b/build-tests/api-extractor-lib3-test/dist/api-extractor-lib3-test.d.ts index cfc4f14a9bc..d809db353f7 100644 --- a/build-tests/api-extractor-lib3-test/dist/api-extractor-lib3-test.d.ts +++ b/build-tests/api-extractor-lib3-test/dist/api-extractor-lib3-test.d.ts @@ -11,4 +11,17 @@ import { Lib1Class } from 'api-extractor-lib1-test'; export { Lib1Class } +/** + * @internalRemarks Internal remarks + * @public + */ +export declare class Lib3Class { + /** + * I am a documented property! + * @betaDocumentation My docs include a custom block tag! + * @virtual @override + */ + prop: boolean; +} + export { } diff --git a/build-tests/api-extractor-lib3-test/etc/api-extractor-lib3-test.api.md b/build-tests/api-extractor-lib3-test/etc/api-extractor-lib3-test.api.md index 08b8ba504ea..294d3c76c11 100644 --- a/build-tests/api-extractor-lib3-test/etc/api-extractor-lib3-test.api.md +++ b/build-tests/api-extractor-lib3-test/etc/api-extractor-lib3-test.api.md @@ -8,4 +8,10 @@ import { Lib1Class } from 'api-extractor-lib1-test'; export { Lib1Class } +// @public @internalRemarks (undocumented) +export class Lib3Class { + // @override @betaDocumentation + prop: boolean; +} + ``` diff --git a/build-tests/api-extractor-lib3-test/package.json b/build-tests/api-extractor-lib3-test/package.json index d3dc7149586..7bc339f0987 100644 --- a/build-tests/api-extractor-lib3-test/package.json +++ b/build-tests/api-extractor-lib3-test/package.json @@ -3,20 +3,39 @@ "description": "Building this project is a regression test for api-extractor", "version": "1.0.0", "private": true, - "main": "lib/index.js", - "typings": "dist/api-extractor-lib3-test.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/api-extractor-lib3-test.d.ts", + "exports": { + ".": { + "types": "./dist/api-extractor-lib3-test.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "scripts": { - "build": "node build.js", - "_phase:build": "node build.js" + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" }, "dependencies": { "api-extractor-lib1-test": "workspace:*" }, "devDependencies": { "@microsoft/api-extractor": "workspace:*", - "@types/jest": "29.2.5", - "@types/node": "14.18.36", - "fs-extra": "~7.0.1", - "typescript": "~5.0.4" + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" } } diff --git a/build-tests/api-extractor-lib3-test/src/index.ts b/build-tests/api-extractor-lib3-test/src/index.ts index c4b8f3ccee4..8d41743a7e8 100644 --- a/build-tests/api-extractor-lib3-test/src/index.ts +++ b/build-tests/api-extractor-lib3-test/src/index.ts @@ -11,3 +11,17 @@ */ export { Lib1Class } from 'api-extractor-lib1-test'; + +/** + * @internalRemarks Internal remarks + * @public + */ +export class Lib3Class { + /** + * I am a documented property! + * @betaDocumentation My docs include a custom block tag! + * @virtual @override + */ + // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility + prop: boolean; +} diff --git a/build-tests/api-extractor-lib3-test/tsconfig.json b/build-tests/api-extractor-lib3-test/tsconfig.json index 1799652cc42..d5641d0ba38 100644 --- a/build-tests/api-extractor-lib3-test/tsconfig.json +++ b/build-tests/api-extractor-lib3-test/tsconfig.json @@ -1,16 +1,6 @@ { + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "target": "es6", - "forceConsistentCasingInFileNames": true, - "module": "commonjs", - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "types": ["node", "jest"], - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "outDir": "lib" - }, - "include": ["src/**/*.ts", "typings/tsd.d.ts"] + "strictPropertyInitialization": false + } } diff --git a/build-tests/api-extractor-lib4-test/.gitignore b/build-tests/api-extractor-lib4-test/.gitignore new file mode 100644 index 00000000000..e730b77542b --- /dev/null +++ b/build-tests/api-extractor-lib4-test/.gitignore @@ -0,0 +1,4 @@ +# This project's outputs are tracked to surface changes to API Extractor rollups during PRs +!dist +dist/* +!dist/*.d.ts diff --git a/build-tests/api-extractor-lib4-test/config/api-extractor.json b/build-tests/api-extractor-lib4-test/config/api-extractor.json new file mode 100644 index 00000000000..6509703367b --- /dev/null +++ b/build-tests/api-extractor-lib4-test/config/api-extractor.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + "mainEntryPointFilePath": "/lib-dts/index.d.ts", + + "apiReport": { + "enabled": true + }, + + "docModel": { + "enabled": true + }, + + "dtsRollup": { + "enabled": true + }, + + "testMode": true +} diff --git a/build-tests/api-extractor-lib4-test/config/rig.json b/build-tests/api-extractor-lib4-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-extractor-lib4-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-extractor-lib4-test/config/rush-project.json b/build-tests/api-extractor-lib4-test/config/rush-project.json new file mode 100644 index 00000000000..6c75ba02c45 --- /dev/null +++ b/build-tests/api-extractor-lib4-test/config/rush-project.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + // dist is intentionally tracked + "outputFolderNames": ["lib-commonjs", "lib-esm", "lib-dts"] + } + ] +} diff --git a/build-tests/api-extractor-lib4-test/dist/api-extractor-lib4-test.d.ts b/build-tests/api-extractor-lib4-test/dist/api-extractor-lib4-test.d.ts new file mode 100644 index 00000000000..372aca18620 --- /dev/null +++ b/build-tests/api-extractor-lib4-test/dist/api-extractor-lib4-test.d.ts @@ -0,0 +1,17 @@ +/** + * api-extractor-lib4-test + * + * @remarks + * This library is consumed by api-extractor-scenarios. + * + * @packageDocumentation + */ + +/** @public */ +export declare enum Lib4Enum { + Foo = "Foo", + Bar = "Bar", + Baz = "Baz" +} + +export { } diff --git a/build-tests/api-extractor-lib4-test/etc/api-extractor-lib4-test.api.md b/build-tests/api-extractor-lib4-test/etc/api-extractor-lib4-test.api.md new file mode 100644 index 00000000000..292d6ec7cb3 --- /dev/null +++ b/build-tests/api-extractor-lib4-test/etc/api-extractor-lib4-test.api.md @@ -0,0 +1,17 @@ +## API Report File for "api-extractor-lib4-test" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public (undocumented) +export enum Lib4Enum { + // (undocumented) + Bar = "Bar", + // (undocumented) + Baz = "Baz", + // (undocumented) + Foo = "Foo" +} + +``` diff --git a/build-tests/api-extractor-lib4-test/package.json b/build-tests/api-extractor-lib4-test/package.json new file mode 100644 index 00000000000..a55fa300d98 --- /dev/null +++ b/build-tests/api-extractor-lib4-test/package.json @@ -0,0 +1,37 @@ +{ + "name": "api-extractor-lib4-test", + "description": "Building this project is a regression test for api-extractor", + "version": "1.0.0", + "private": true, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/api-extractor-lib3-test.d.ts", + "exports": { + ".": { + "types": "./dist/api-extractor-lib3-test.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" + } +} diff --git a/build-tests/api-extractor-lib4-test/src/index.ts b/build-tests/api-extractor-lib4-test/src/index.ts new file mode 100644 index 00000000000..768db739ed4 --- /dev/null +++ b/build-tests/api-extractor-lib4-test/src/index.ts @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * api-extractor-lib4-test + * + * @remarks + * This library is consumed by api-extractor-scenarios. + * + * @packageDocumentation + */ + +/** @public */ +export enum Lib4Enum { + Foo = 'Foo', + Bar = 'Bar', + Baz = 'Baz' +} diff --git a/build-tests/api-extractor-lib4-test/tsconfig.json b/build-tests/api-extractor-lib4-test/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/build-tests/api-extractor-lib4-test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/build-tests/api-extractor-lib5-test/.gitignore b/build-tests/api-extractor-lib5-test/.gitignore new file mode 100644 index 00000000000..e730b77542b --- /dev/null +++ b/build-tests/api-extractor-lib5-test/.gitignore @@ -0,0 +1,4 @@ +# This project's outputs are tracked to surface changes to API Extractor rollups during PRs +!dist +dist/* +!dist/*.d.ts diff --git a/build-tests/api-extractor-lib5-test/config/api-extractor.json b/build-tests/api-extractor-lib5-test/config/api-extractor.json new file mode 100644 index 00000000000..6509703367b --- /dev/null +++ b/build-tests/api-extractor-lib5-test/config/api-extractor.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + "mainEntryPointFilePath": "/lib-dts/index.d.ts", + + "apiReport": { + "enabled": true + }, + + "docModel": { + "enabled": true + }, + + "dtsRollup": { + "enabled": true + }, + + "testMode": true +} diff --git a/build-tests/api-extractor-lib5-test/config/rig.json b/build-tests/api-extractor-lib5-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-extractor-lib5-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-extractor-lib5-test/config/rush-project.json b/build-tests/api-extractor-lib5-test/config/rush-project.json new file mode 100644 index 00000000000..6c75ba02c45 --- /dev/null +++ b/build-tests/api-extractor-lib5-test/config/rush-project.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + // dist is intentionally tracked + "outputFolderNames": ["lib-commonjs", "lib-esm", "lib-dts"] + } + ] +} diff --git a/build-tests/api-extractor-lib5-test/dist/api-extractor-lib5-test.d.ts b/build-tests/api-extractor-lib5-test/dist/api-extractor-lib5-test.d.ts new file mode 100644 index 00000000000..b7b1090bfe0 --- /dev/null +++ b/build-tests/api-extractor-lib5-test/dist/api-extractor-lib5-test.d.ts @@ -0,0 +1,13 @@ +/** + * api-extractor-lib5-test + * + * @remarks + * This library is consumed by api-extractor-scenarios. + * + * @packageDocumentation + */ + +/** @public */ +export declare function lib5Function(): number; + +export { } diff --git a/build-tests/api-extractor-lib5-test/etc/api-extractor-lib5-test.api.md b/build-tests/api-extractor-lib5-test/etc/api-extractor-lib5-test.api.md new file mode 100644 index 00000000000..7c49cb23de0 --- /dev/null +++ b/build-tests/api-extractor-lib5-test/etc/api-extractor-lib5-test.api.md @@ -0,0 +1,10 @@ +## API Report File for "api-extractor-lib5-test" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public (undocumented) +export function lib5Function(): number; + +``` diff --git a/build-tests/api-extractor-lib5-test/package.json b/build-tests/api-extractor-lib5-test/package.json new file mode 100644 index 00000000000..bf97ee6e501 --- /dev/null +++ b/build-tests/api-extractor-lib5-test/package.json @@ -0,0 +1,37 @@ +{ + "name": "api-extractor-lib5-test", + "description": "Building this project is a regression test for api-extractor", + "version": "1.0.0", + "private": true, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/api-extractor-lib3-test.d.ts", + "exports": { + ".": { + "types": "./dist/api-extractor-lib3-test.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" + } +} diff --git a/build-tests/api-extractor-lib5-test/src/index.ts b/build-tests/api-extractor-lib5-test/src/index.ts new file mode 100644 index 00000000000..92b95d65880 --- /dev/null +++ b/build-tests/api-extractor-lib5-test/src/index.ts @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * api-extractor-lib5-test + * + * @remarks + * This library is consumed by api-extractor-scenarios. + * + * @packageDocumentation + */ + +/** @public */ +export function lib5Function(): number { + return 42; +} diff --git a/build-tests/api-extractor-lib5-test/tsconfig.json b/build-tests/api-extractor-lib5-test/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/build-tests/api-extractor-lib5-test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/build-tests/api-extractor-scenarios/build.js b/build-tests/api-extractor-scenarios/build.js deleted file mode 100644 index 04eec231385..00000000000 --- a/build-tests/api-extractor-scenarios/build.js +++ /dev/null @@ -1,27 +0,0 @@ -const fsx = require('fs-extra'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -console.log(); - -// Clean the old build outputs -fsx.emptyDirSync('dist'); -fsx.emptyDirSync('lib'); -fsx.emptyDirSync('temp'); -fsx.emptyDirSync('etc'); - -// Run the TypeScript compiler -executeCommand('node node_modules/typescript/lib/tsc'); - -// Run the scenario runner -require('./lib/runScenarios').runScenarios('./config/build-config.json'); - -console.log(); -console.log(`==> Finished build.js for ${path.basename(process.cwd())}`); diff --git a/build-tests/api-extractor-scenarios/config/build-config.json b/build-tests/api-extractor-scenarios/config/build-config.json deleted file mode 100644 index f779c412fed..00000000000 --- a/build-tests/api-extractor-scenarios/config/build-config.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "scenarioFolderNames": [ - "ambientNameConflict", - "ambientNameConflict2", - "ancillaryDeclarations", - "apiItemKinds", - "bundledPackages", - "circularImport", - "circularImport2", - "defaultExportOfEntryPoint", - "defaultExportOfEntryPoint2", - "defaultExportOfEntryPoint3", - "defaultExportOfEntryPoint4", - "docReferences", - "docReferences2", - "docReferences3", - "docReferencesAlias", - "docReferencesNamespaceAlias", - "dynamicImportType", - "dynamicImportType2", - "dynamicImportType3", - "ecmaScriptPrivateFields", - "enumSorting", - "excerptTokens", - "exportDuplicate", - "exportEquals", - "exportImportedExternal", - "exportImportedExternal2", - "exportImportedExternalDefault", - "exportImportStarAs", - "exportImportStarAs2", - "exportStar", - "exportStar2", - "exportStar3", - "functionOverload", - "importEquals", - "importType", - "includeForgottenExports", - "inconsistentReleaseTags", - "internationalCharacters", - "mergedDeclarations", - "mixinPattern", - "namedDefaultImport", - "namespaceImports", - "namespaceImports2", - "preapproved", - "projectFolderUrl", - "readonlyDeclarations", - "referenceTokens", - "spanSorting", - "typeLiterals", - "typeOf", - "typeOf2", - "typeOf3", - "typeParameters" - ] -} diff --git a/build-tests/api-extractor-scenarios/config/heft.json b/build-tests/api-extractor-scenarios/config/heft.json new file mode 100644 index 00000000000..df9febe88cb --- /dev/null +++ b/build-tests/api-extractor-scenarios/config/heft.json @@ -0,0 +1,50 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "cleanFiles": [ + { + "sourcePath": "temp/etc", + "includeGlobs": ["**/*"] + }, + { + "sourcePath": "temp/configs", + "includeGlobs": ["**/*"] + } + ], + + "tasksByName": { + "copy-dts": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src", + "destinationFolders": ["lib-dts"], + "fileExtensions": [".d.ts"] + } + ] + } + } + }, + + "run-scenarios": { + "taskDependencies": ["typescript", "copy-dts"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "run-script-plugin", + "options": { + "scriptPath": "./lib-commonjs/runScenarios.js" + } + } + } + } + } + } +} diff --git a/build-tests/api-extractor-scenarios/config/rig.json b/build-tests/api-extractor-scenarios/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-extractor-scenarios/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-extractor-scenarios/config/rush-project.json b/build-tests/api-extractor-scenarios/config/rush-project.json deleted file mode 100644 index 247dc17187a..00000000000 --- a/build-tests/api-extractor-scenarios/config/rush-project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "operationSettings": [ - { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] - } - ] -} diff --git a/build-tests/api-extractor-scenarios/etc/ambientNameConflict/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/ambientNameConflict/api-extractor-scenarios.api.json index 0697715d9d5..174025c1db3 100644 --- a/build-tests/api-extractor-scenarios/etc/ambientNameConflict/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/ambientNameConflict/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/ambientNameConflict2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/ambientNameConflict2/api-extractor-scenarios.api.json index d0e3d0c7980..d949bb58ab1 100644 --- a/build-tests/api-extractor-scenarios/etc/ambientNameConflict2/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/ambientNameConflict2/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/ancillaryDeclarations/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/ancillaryDeclarations/api-extractor-scenarios.api.json index 8eda7baa8a6..6d63ae3142c 100644 --- a/build-tests/api-extractor-scenarios/etc/ancillaryDeclarations/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/ancillaryDeclarations/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/apiItemKinds/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/apiItemKinds/api-extractor-scenarios.api.json index 64b88b41b1e..c22e4d5b525 100644 --- a/build-tests/api-extractor-scenarios/etc/apiItemKinds/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/apiItemKinds/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/apiItemKinds/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/apiItemKinds/api-extractor-scenarios.api.md index c0743f1c37e..ccc12604ddd 100644 --- a/build-tests/api-extractor-scenarios/etc/apiItemKinds/api-extractor-scenarios.api.md +++ b/build-tests/api-extractor-scenarios/etc/apiItemKinds/api-extractor-scenarios.api.md @@ -51,7 +51,7 @@ export namespace n1 { // (undocumented) export class SomeClass2 extends SomeClass1 { } - {}; + export {}; } // @public (undocumented) diff --git a/build-tests/api-extractor-scenarios/etc/apiItemKinds/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/apiItemKinds/rollup.d.ts index dbe1c3219c1..b69a119ae1c 100644 --- a/build-tests/api-extractor-scenarios/etc/apiItemKinds/rollup.d.ts +++ b/build-tests/api-extractor-scenarios/etc/apiItemKinds/rollup.d.ts @@ -36,7 +36,7 @@ export declare namespace n1 { export class SomeClass3 { } } - {}; + export {}; } /** @public */ diff --git a/build-tests/api-extractor-scenarios/etc/bundledPackages/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/bundledPackages/api-extractor-scenarios.api.json index 5146f92aab4..752716990bc 100644 --- a/build-tests/api-extractor-scenarios/etc/bundledPackages/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/bundledPackages/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" @@ -193,7 +209,25 @@ { "kind": "Reference", "text": "Lib2Class", - "canonicalReference": "api-extractor-lib2-test!Lib2Class:class" + "canonicalReference": "api-extractor-scenarios!Lib2Class:class" + }, + { + "kind": "Content", + "text": ", arg3: " + }, + { + "kind": "Reference", + "text": "Lib3Class", + "canonicalReference": "api-extractor-lib3-test!Lib3Class:class" + }, + { + "kind": "Content", + "text": ", arg4: " + }, + { + "kind": "Reference", + "text": "Lib4Enum", + "canonicalReference": "api-extractor-scenarios!Lib4Enum:enum" }, { "kind": "Content", @@ -210,8 +244,8 @@ ], "fileUrlPath": "src/bundledPackages/index.ts", "returnTypeTokenRange": { - "startIndex": 5, - "endIndex": 6 + "startIndex": 9, + "endIndex": 10 }, "releaseTag": "Public", "overloadIndex": 1, @@ -231,6 +265,22 @@ "endIndex": 4 }, "isOptional": false + }, + { + "parameterName": "arg3", + "parameterTypeTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "isOptional": false + }, + { + "parameterName": "arg4", + "parameterTypeTokenRange": { + "startIndex": 7, + "endIndex": 8 + }, + "isOptional": false } ], "name": "f" @@ -254,7 +304,7 @@ "text": " " } ], - "fileUrlPath": "../api-extractor-lib1-test/lib/index.d.ts", + "fileUrlPath": "../api-extractor-lib1-test/lib-dts/index.d.ts", "releaseTag": "Public", "isAbstract": false, "name": "Lib1Class", @@ -326,6 +376,163 @@ "endIndex": 2 }, "implementsTokenRanges": [] + }, + { + "kind": "Class", + "canonicalReference": "api-extractor-scenarios!Lib2Class:class", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare class Lib2Class " + } + ], + "fileUrlPath": "../api-extractor-lib2-test/src/index.ts", + "releaseTag": "Public", + "isAbstract": false, + "name": "Lib2Class", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Property", + "canonicalReference": "api-extractor-scenarios!Lib2Class#prop:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "prop: " + }, + { + "kind": "Content", + "text": "number" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "prop", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isStatic": false, + "isProtected": false, + "isAbstract": false + } + ], + "implementsTokenRanges": [] + }, + { + "kind": "Enum", + "canonicalReference": "api-extractor-scenarios!Lib4Enum:enum", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare enum Lib4Enum " + } + ], + "fileUrlPath": "../api-extractor-lib4-test/src/index.ts", + "releaseTag": "Public", + "name": "Lib4Enum", + "preserveMemberOrder": false, + "members": [ + { + "kind": "EnumMember", + "canonicalReference": "api-extractor-scenarios!Lib4Enum.Bar:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "Bar = " + }, + { + "kind": "Content", + "text": "\"Bar\"" + } + ], + "initializerTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "releaseTag": "Public", + "name": "Bar" + }, + { + "kind": "EnumMember", + "canonicalReference": "api-extractor-scenarios!Lib4Enum.Baz:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "Baz = " + }, + { + "kind": "Content", + "text": "\"Baz\"" + } + ], + "initializerTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "releaseTag": "Public", + "name": "Baz" + }, + { + "kind": "EnumMember", + "canonicalReference": "api-extractor-scenarios!Lib4Enum.Foo:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "Foo = " + }, + { + "kind": "Content", + "text": "\"Foo\"" + } + ], + "initializerTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "releaseTag": "Public", + "name": "Foo" + } + ] + }, + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!lib5Function:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function lib5Function(): " + }, + { + "kind": "Content", + "text": "number" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "../api-extractor-lib5-test/src/index.ts", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "lib5Function" } ] } diff --git a/build-tests/api-extractor-scenarios/etc/bundledPackages/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/bundledPackages/api-extractor-scenarios.api.md index 95bc505ad9c..4e33e833787 100644 --- a/build-tests/api-extractor-scenarios/etc/bundledPackages/api-extractor-scenarios.api.md +++ b/build-tests/api-extractor-scenarios/etc/bundledPackages/api-extractor-scenarios.api.md @@ -4,10 +4,10 @@ ```ts -import { Lib2Class } from 'api-extractor-lib2-test/lib/index'; +import { Lib3Class } from 'api-extractor-lib3-test/lib/index'; // @public (undocumented) -export function f(arg1: Lib1Class, arg2: Lib2Class): void; +export function f(arg1: Lib1Class, arg2: Lib2Class, arg3: Lib3Class, arg4: Lib4Enum): void; // Warning: (ae-forgotten-export) The symbol "Lib1ForgottenExport" needs to be exported by the entry point index.d.ts // @@ -19,7 +19,26 @@ export class Lib1Class extends Lib1ForgottenExport { writeableProperty: string; } -export { Lib2Class } +// @public (undocumented) +export class Lib2Class { + // (undocumented) + prop: number; +} + +export { Lib3Class } + +// @public (undocumented) +export enum Lib4Enum { + // (undocumented) + Bar = "Bar", + // (undocumented) + Baz = "Baz", + // (undocumented) + Foo = "Foo" +} + +// @public (undocumented) +export function lib5Function(): number; // (No @packageDocumentation comment for this package) diff --git a/build-tests/api-extractor-scenarios/etc/bundledPackages/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/bundledPackages/rollup.d.ts index d62eb9b0639..4b0107ec8dd 100644 --- a/build-tests/api-extractor-scenarios/etc/bundledPackages/rollup.d.ts +++ b/build-tests/api-extractor-scenarios/etc/bundledPackages/rollup.d.ts @@ -1,7 +1,7 @@ -import { Lib2Class } from 'api-extractor-lib2-test/lib/index'; +import { Lib3Class } from 'api-extractor-lib3-test/lib/index'; /** @public */ -export declare function f(arg1: Lib1Class, arg2: Lib2Class): void; +export declare function f(arg1: Lib1Class, arg2: Lib2Class, arg3: Lib3Class, arg4: Lib4Enum): void; /** @public */ export declare class Lib1Class extends Lib1ForgottenExport { @@ -12,6 +12,21 @@ export declare class Lib1Class extends Lib1ForgottenExport { declare class Lib1ForgottenExport { } -export { Lib2Class } +/** @public */ +export declare class Lib2Class { + prop: number; +} + +export { Lib3Class } + +/** @public */ +export declare enum Lib4Enum { + Foo = "Foo", + Bar = "Bar", + Baz = "Baz" +} + +/** @public */ +export declare function lib5Function(): number; export { } diff --git a/build-tests/api-extractor-scenarios/etc/bundlerModuleResolution/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/bundlerModuleResolution/api-extractor-scenarios.api.json new file mode 100644 index 00000000000..440de9656ff --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/bundlerModuleResolution/api-extractor-scenarios.api.json @@ -0,0 +1,222 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1011, + "oldestForwardsCompatibleVersion": 1001, + "tsdocConfig": { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "noStandardTags": true, + "tagDefinitions": [ + { + "tagName": "@alpha", + "syntaxKind": "modifier" + }, + { + "tagName": "@beta", + "syntaxKind": "modifier" + }, + { + "tagName": "@defaultValue", + "syntaxKind": "block" + }, + { + "tagName": "@decorator", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@deprecated", + "syntaxKind": "block" + }, + { + "tagName": "@eventProperty", + "syntaxKind": "modifier" + }, + { + "tagName": "@example", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@experimental", + "syntaxKind": "modifier" + }, + { + "tagName": "@inheritDoc", + "syntaxKind": "inline" + }, + { + "tagName": "@internal", + "syntaxKind": "modifier" + }, + { + "tagName": "@label", + "syntaxKind": "inline" + }, + { + "tagName": "@link", + "syntaxKind": "inline", + "allowMultiple": true + }, + { + "tagName": "@override", + "syntaxKind": "modifier" + }, + { + "tagName": "@packageDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@param", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@privateRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@public", + "syntaxKind": "modifier" + }, + { + "tagName": "@readonly", + "syntaxKind": "modifier" + }, + { + "tagName": "@remarks", + "syntaxKind": "block" + }, + { + "tagName": "@returns", + "syntaxKind": "block" + }, + { + "tagName": "@sealed", + "syntaxKind": "modifier" + }, + { + "tagName": "@see", + "syntaxKind": "block" + }, + { + "tagName": "@throws", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@typeParam", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@virtual", + "syntaxKind": "modifier" + }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, + { + "tagName": "@betaDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@internalRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@preapproved", + "syntaxKind": "modifier" + } + ], + "supportForTags": { + "@alpha": true, + "@beta": true, + "@defaultValue": true, + "@decorator": true, + "@deprecated": true, + "@eventProperty": true, + "@example": true, + "@experimental": true, + "@inheritDoc": true, + "@internal": true, + "@label": true, + "@link": true, + "@override": true, + "@packageDocumentation": true, + "@param": true, + "@privateRemarks": true, + "@public": true, + "@readonly": true, + "@remarks": true, + "@returns": true, + "@sealed": true, + "@see": true, + "@throws": true, + "@typeParam": true, + "@virtual": true, + "@betaDocumentation": true, + "@internalRemarks": true, + "@preapproved": true + }, + "reportUnsupportedHtmlElements": false + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-scenarios!", + "docComment": "", + "name": "api-extractor-scenarios", + "preserveMemberOrder": false, + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-scenarios!", + "name": "", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Variable", + "canonicalReference": "api-extractor-scenarios!reexport:var", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "reexport: " + }, + { + "kind": "Content", + "text": "import(\"./other\")." + }, + { + "kind": "Reference", + "text": "Foo", + "canonicalReference": "api-extractor-scenarios!~Foo:class" + } + ], + "fileUrlPath": "src/bundlerModuleResolution/index.ts", + "isReadonly": true, + "releaseTag": "Public", + "name": "reexport", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 3 + } + } + ] + } + ] +} diff --git a/build-tests/api-extractor-scenarios/etc/bundlerModuleResolution/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/bundlerModuleResolution/api-extractor-scenarios.api.md new file mode 100644 index 00000000000..49848c6f755 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/bundlerModuleResolution/api-extractor-scenarios.api.md @@ -0,0 +1,14 @@ +## API Report File for "api-extractor-scenarios" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// Warning: (ae-forgotten-export) The symbol "Foo" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export const reexport: Foo; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/build-tests/api-extractor-scenarios/etc/bundlerModuleResolution/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/bundlerModuleResolution/rollup.d.ts new file mode 100644 index 00000000000..e5eff73d8ab --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/bundlerModuleResolution/rollup.d.ts @@ -0,0 +1,9 @@ +declare class Foo { +} + +/** + * @public + */ +export declare const reexport: Foo; + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/circularImport/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/circularImport/api-extractor-scenarios.api.json index d4f99324946..11072d35f10 100644 --- a/build-tests/api-extractor-scenarios/etc/circularImport/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/circularImport/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/circularImport2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/circularImport2/api-extractor-scenarios.api.json index 2af5ea9bb31..4b4d33f465e 100644 --- a/build-tests/api-extractor-scenarios/etc/circularImport2/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/circularImport2/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint/api-extractor-scenarios.api.json index ddc7e3f6556..c3376538caa 100644 --- a/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint2/api-extractor-scenarios.api.json index d5b2da9ff7c..f6652971c0a 100644 --- a/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint2/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint2/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" @@ -173,27 +189,28 @@ "preserveMemberOrder": false, "members": [ { - "kind": "Variable", - "canonicalReference": "api-extractor-scenarios!defaultFunctionStatement:var", + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!defaultFunctionStatement:function(1)", "docComment": "/**\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", - "text": "defaultFunctionStatement: " + "text": "defaultFunctionStatement: () => " }, { "kind": "Content", - "text": "() => void" + "text": "void" } ], "fileUrlPath": "src/defaultExportOfEntryPoint2/index.ts", - "isReadonly": true, - "releaseTag": "Public", - "name": "defaultFunctionStatement", - "variableTypeTokenRange": { + "returnTypeTokenRange": { "startIndex": 1, "endIndex": 2 - } + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "defaultFunctionStatement" } ] } diff --git a/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint3/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint3/api-extractor-scenarios.api.json index 35ffd2ab980..7ca2d175a65 100644 --- a/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint3/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint3/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint4/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint4/api-extractor-scenarios.api.json index 5cdb5ae7e93..be718ee63d0 100644 --- a/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint4/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/defaultExportOfEntryPoint4/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/destructuredParameters/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/destructuredParameters/api-extractor-scenarios.api.json new file mode 100644 index 00000000000..e3e5d39c9f9 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/destructuredParameters/api-extractor-scenarios.api.json @@ -0,0 +1,608 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1011, + "oldestForwardsCompatibleVersion": 1001, + "tsdocConfig": { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "noStandardTags": true, + "tagDefinitions": [ + { + "tagName": "@alpha", + "syntaxKind": "modifier" + }, + { + "tagName": "@beta", + "syntaxKind": "modifier" + }, + { + "tagName": "@defaultValue", + "syntaxKind": "block" + }, + { + "tagName": "@decorator", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@deprecated", + "syntaxKind": "block" + }, + { + "tagName": "@eventProperty", + "syntaxKind": "modifier" + }, + { + "tagName": "@example", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@experimental", + "syntaxKind": "modifier" + }, + { + "tagName": "@inheritDoc", + "syntaxKind": "inline" + }, + { + "tagName": "@internal", + "syntaxKind": "modifier" + }, + { + "tagName": "@label", + "syntaxKind": "inline" + }, + { + "tagName": "@link", + "syntaxKind": "inline", + "allowMultiple": true + }, + { + "tagName": "@override", + "syntaxKind": "modifier" + }, + { + "tagName": "@packageDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@param", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@privateRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@public", + "syntaxKind": "modifier" + }, + { + "tagName": "@readonly", + "syntaxKind": "modifier" + }, + { + "tagName": "@remarks", + "syntaxKind": "block" + }, + { + "tagName": "@returns", + "syntaxKind": "block" + }, + { + "tagName": "@sealed", + "syntaxKind": "modifier" + }, + { + "tagName": "@see", + "syntaxKind": "block" + }, + { + "tagName": "@throws", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@typeParam", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@virtual", + "syntaxKind": "modifier" + }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, + { + "tagName": "@betaDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@internalRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@preapproved", + "syntaxKind": "modifier" + } + ], + "supportForTags": { + "@alpha": true, + "@beta": true, + "@defaultValue": true, + "@decorator": true, + "@deprecated": true, + "@eventProperty": true, + "@example": true, + "@experimental": true, + "@inheritDoc": true, + "@internal": true, + "@label": true, + "@link": true, + "@override": true, + "@packageDocumentation": true, + "@param": true, + "@privateRemarks": true, + "@public": true, + "@readonly": true, + "@remarks": true, + "@returns": true, + "@sealed": true, + "@see": true, + "@throws": true, + "@typeParam": true, + "@virtual": true, + "@betaDocumentation": true, + "@internalRemarks": true, + "@preapproved": true + }, + "reportUnsupportedHtmlElements": false + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-scenarios!", + "docComment": "", + "name": "api-extractor-scenarios", + "preserveMemberOrder": false, + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-scenarios!", + "name": "", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!testArray:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function testArray(input: " + }, + { + "kind": "Content", + "text": "[number, number]" + }, + { + "kind": "Content", + "text": ", last: " + }, + { + "kind": "Content", + "text": "string" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "void" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "src/destructuredParameters/index.ts", + "returnTypeTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "[x, y]", + "parameterTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + }, + "isOptional": false + }, + { + "parameterName": "input", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + }, + { + "parameterName": "last", + "parameterTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "isOptional": false + } + ], + "name": "testArray" + }, + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!testNameConflict:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function testNameConflict(input2: " + }, + { + "kind": "Content", + "text": "[number, number]" + }, + { + "kind": "Content", + "text": ", input: " + }, + { + "kind": "Content", + "text": "boolean" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "void" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "src/destructuredParameters/index.ts", + "returnTypeTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "[x, y]", + "parameterTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + }, + "isOptional": false + }, + { + "parameterName": "input2", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + }, + { + "parameterName": "input", + "parameterTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "isOptional": false + } + ], + "name": "testNameConflict" + }, + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!testNameConflict2:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function testNameConflict2(input: " + }, + { + "kind": "Content", + "text": "{\n x: number;\n}" + }, + { + "kind": "Content", + "text": ", input3: " + }, + { + "kind": "Content", + "text": "{\n y: number;\n}" + }, + { + "kind": "Content", + "text": ", input2: " + }, + { + "kind": "Content", + "text": "string" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "void" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "src/destructuredParameters/index.ts", + "returnTypeTokenRange": { + "startIndex": 7, + "endIndex": 8 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ x }", + "parameterTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + }, + "isOptional": false + }, + { + "parameterName": "input", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + }, + { + "parameterName": "input3", + "parameterTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "isOptional": false + }, + { + "parameterName": "input2", + "parameterTypeTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "isOptional": false + } + ], + "name": "testNameConflict2" + }, + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!testObject:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function testObject(first: " + }, + { + "kind": "Content", + "text": "string" + }, + { + "kind": "Content", + "text": ", input: " + }, + { + "kind": "Content", + "text": "{\n x: number;\n y: number;\n}" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "void" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "src/destructuredParameters/index.ts", + "returnTypeTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "first", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + }, + { + "parameterName": "{ x, y }", + "parameterTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + }, + "isOptional": false + }, + { + "parameterName": "input", + "parameterTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "isOptional": false + } + ], + "name": "testObject" + }, + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!testObjects:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function testObjects(input: " + }, + { + "kind": "Content", + "text": "{\n x: number;\n}" + }, + { + "kind": "Content", + "text": ", input2: " + }, + { + "kind": "Content", + "text": "{\n y: number;\n}" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "void" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "src/destructuredParameters/index.ts", + "returnTypeTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ x }", + "parameterTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + }, + "isOptional": false + }, + { + "parameterName": "input", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + }, + { + "parameterName": "input2", + "parameterTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "isOptional": false + } + ], + "name": "testObjects" + }, + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!testObjectWithComments:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function testObjectWithComments(input: " + }, + { + "kind": "Content", + "text": "{\n x: number;\n y: number;\n}" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "void" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "src/destructuredParameters/index.ts", + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "{ x, // slash P3\ny }", + "parameterTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + }, + "isOptional": false + }, + { + "parameterName": "input", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "testObjectWithComments" + } + ] + } + ] +} diff --git a/build-tests/api-extractor-scenarios/etc/destructuredParameters/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/destructuredParameters/api-extractor-scenarios.api.md new file mode 100644 index 00000000000..9cf44eb2703 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/destructuredParameters/api-extractor-scenarios.api.md @@ -0,0 +1,41 @@ +## API Report File for "api-extractor-scenarios" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public (undocumented) +export function testArray(input: [number, number], last: string): void; + +// @public (undocumented) +export function testNameConflict(input2: [number, number], input: boolean): void; + +// @public (undocumented) +export function testNameConflict2(input: { + x: number; +}, input3: { + y: number; +}, input2: string): void; + +// @public (undocumented) +export function testObject(first: string, input: { + x: number; + y: number; +}): void; + +// @public (undocumented) +export function testObjects(input: { + x: number; +}, input2: { + y: number; +}): void; + +// @public (undocumented) +export function testObjectWithComments(input: { + x: number; + y: number; +}): void; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/build-tests/api-extractor-scenarios/etc/destructuredParameters/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/destructuredParameters/rollup.d.ts new file mode 100644 index 00000000000..ff81ffe1caf --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/destructuredParameters/rollup.d.ts @@ -0,0 +1,34 @@ +/** @public */ +export declare function testArray([x, y]: [number, number], last: string): void; + +/** @public */ +export declare function testNameConflict([x, y]: [number, number], input: boolean): void; + +/** @public */ +export declare function testNameConflict2({ x }: { + x: number; +}, { y }: { + y: number; +}, input2: string): void; + +/** @public */ +export declare function testObject(first: string, { x, y }: { + x: number; + y: number; +}): void; + +/** @public */ +export declare function testObjects({ x }: { + x: number; +}, { y }: { + y: number; +}): void; + +/** @public */ +export declare function testObjectWithComments({ x, // slash P3 + y }: { + x: number; + y: number; +}): void; + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/docReferences/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/docReferences/api-extractor-scenarios.api.json index fa4fdb7faba..5f21c8220c2 100644 --- a/build-tests/api-extractor-scenarios/etc/docReferences/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/docReferences/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/docReferences/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/docReferences/api-extractor-scenarios.api.md index 37ae5294263..14031441b3b 100644 --- a/build-tests/api-extractor-scenarios/etc/docReferences/api-extractor-scenarios.api.md +++ b/build-tests/api-extractor-scenarios/etc/docReferences/api-extractor-scenarios.api.md @@ -23,7 +23,7 @@ export namespace MyNamespace { } } -// @public (undocumented) +// @public export function succeedForNow(): void; // @public diff --git a/build-tests/api-extractor-scenarios/etc/docReferences2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/docReferences2/api-extractor-scenarios.api.json index 89801701c03..1e5b18c2e4a 100644 --- a/build-tests/api-extractor-scenarios/etc/docReferences2/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/docReferences2/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/docReferences3/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/docReferences3/api-extractor-scenarios.api.json index f6bdf6840ed..132c1188dc0 100644 --- a/build-tests/api-extractor-scenarios/etc/docReferences3/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/docReferences3/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/docReferencesAlias/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/docReferencesAlias/api-extractor-scenarios.api.json index 0e10f085184..9c65d23e2d6 100644 --- a/build-tests/api-extractor-scenarios/etc/docReferencesAlias/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/docReferencesAlias/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/api-extractor-scenarios.api.json index 4bf60a5d307..aa38a67cebf 100644 --- a/build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/dynamicImportType/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/dynamicImportType/api-extractor-scenarios.api.json index 3a7783606e3..f9ae5ecf136 100644 --- a/build-tests/api-extractor-scenarios/etc/dynamicImportType/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/dynamicImportType/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/dynamicImportType2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/dynamicImportType2/api-extractor-scenarios.api.json index b7a2b150998..206970f232e 100644 --- a/build-tests/api-extractor-scenarios/etc/dynamicImportType2/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/dynamicImportType2/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/dynamicImportType3/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/dynamicImportType3/api-extractor-scenarios.api.json index 45d2df5805b..08d82bdea53 100644 --- a/build-tests/api-extractor-scenarios/etc/dynamicImportType3/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/dynamicImportType3/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/ecmaScriptPrivateFields/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/ecmaScriptPrivateFields/api-extractor-scenarios.api.json index 623949f6883..7c85d11223b 100644 --- a/build-tests/api-extractor-scenarios/etc/ecmaScriptPrivateFields/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/ecmaScriptPrivateFields/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/enumSorting/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/enumSorting/api-extractor-scenarios.api.json index e5c18346cba..021c6a99b03 100644 --- a/build-tests/api-extractor-scenarios/etc/enumSorting/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/enumSorting/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/excerptTokens/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/excerptTokens/api-extractor-scenarios.api.json index a79d24c516d..8a3cdb3b666 100644 --- a/build-tests/api-extractor-scenarios/etc/excerptTokens/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/excerptTokens/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" @@ -186,7 +202,7 @@ "text": "number" } ], - "fileUrlPath": "lib/excerptTokens/index.d.ts", + "fileUrlPath": "lib-dts/excerptTokens/index.d.ts", "isReadonly": false, "releaseTag": "Public", "name": "MY_CONSTANT", @@ -205,7 +221,7 @@ "text": "export class MyClass " } ], - "fileUrlPath": "lib/excerptTokens/index.d.ts", + "fileUrlPath": "lib-dts/excerptTokens/index.d.ts", "releaseTag": "Public", "isAbstract": false, "name": "MyClass", diff --git a/build-tests/api-extractor-scenarios/etc/exportDuplicate/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportDuplicate/api-extractor-scenarios.api.json index f4ca40c849b..af560e6437e 100644 --- a/build-tests/api-extractor-scenarios/etc/exportDuplicate/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/exportDuplicate/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/exportEquals/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportEquals/api-extractor-scenarios.api.json index 14e0e241335..2af67bcc3e2 100644 --- a/build-tests/api-extractor-scenarios/etc/exportEquals/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/exportEquals/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/exportImportStarAs/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportImportStarAs/api-extractor-scenarios.api.json index 65254a86a60..d36f5836527 100644 --- a/build-tests/api-extractor-scenarios/etc/exportImportStarAs/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/exportImportStarAs/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json index 569dc468334..3fcbf300fb2 100644 --- a/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/exportImportStarAs3/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportImportStarAs3/api-extractor-scenarios.api.json new file mode 100644 index 00000000000..e4d22195cde --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/exportImportStarAs3/api-extractor-scenarios.api.json @@ -0,0 +1,260 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1011, + "oldestForwardsCompatibleVersion": 1001, + "tsdocConfig": { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "noStandardTags": true, + "tagDefinitions": [ + { + "tagName": "@alpha", + "syntaxKind": "modifier" + }, + { + "tagName": "@beta", + "syntaxKind": "modifier" + }, + { + "tagName": "@defaultValue", + "syntaxKind": "block" + }, + { + "tagName": "@decorator", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@deprecated", + "syntaxKind": "block" + }, + { + "tagName": "@eventProperty", + "syntaxKind": "modifier" + }, + { + "tagName": "@example", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@experimental", + "syntaxKind": "modifier" + }, + { + "tagName": "@inheritDoc", + "syntaxKind": "inline" + }, + { + "tagName": "@internal", + "syntaxKind": "modifier" + }, + { + "tagName": "@label", + "syntaxKind": "inline" + }, + { + "tagName": "@link", + "syntaxKind": "inline", + "allowMultiple": true + }, + { + "tagName": "@override", + "syntaxKind": "modifier" + }, + { + "tagName": "@packageDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@param", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@privateRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@public", + "syntaxKind": "modifier" + }, + { + "tagName": "@readonly", + "syntaxKind": "modifier" + }, + { + "tagName": "@remarks", + "syntaxKind": "block" + }, + { + "tagName": "@returns", + "syntaxKind": "block" + }, + { + "tagName": "@sealed", + "syntaxKind": "modifier" + }, + { + "tagName": "@see", + "syntaxKind": "block" + }, + { + "tagName": "@throws", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@typeParam", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@virtual", + "syntaxKind": "modifier" + }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, + { + "tagName": "@betaDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@internalRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@preapproved", + "syntaxKind": "modifier" + } + ], + "supportForTags": { + "@alpha": true, + "@beta": true, + "@defaultValue": true, + "@decorator": true, + "@deprecated": true, + "@eventProperty": true, + "@example": true, + "@experimental": true, + "@inheritDoc": true, + "@internal": true, + "@label": true, + "@link": true, + "@override": true, + "@packageDocumentation": true, + "@param": true, + "@privateRemarks": true, + "@public": true, + "@readonly": true, + "@remarks": true, + "@returns": true, + "@sealed": true, + "@see": true, + "@throws": true, + "@typeParam": true, + "@virtual": true, + "@betaDocumentation": true, + "@internalRemarks": true, + "@preapproved": true + }, + "reportUnsupportedHtmlElements": false + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-scenarios!", + "docComment": "", + "name": "api-extractor-scenarios", + "preserveMemberOrder": false, + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-scenarios!", + "name": "", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Namespace", + "canonicalReference": "api-extractor-scenarios!NS:namespace", + "docComment": "", + "excerptTokens": [], + "fileUrlPath": "src/exportImportStarAs3/index.ts", + "releaseTag": "None", + "name": "NS", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Variable", + "canonicalReference": "api-extractor-scenarios!NS.NS_BETA:var", + "docComment": "/**\n * @beta\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "NS_BETA = " + }, + { + "kind": "Content", + "text": "\"BETA\"" + } + ], + "fileUrlPath": "src/exportImportStarAs3/NamespaceWithTrimming.ts", + "initializerTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isReadonly": true, + "releaseTag": "Beta", + "name": "NS_BETA", + "variableTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + } + }, + { + "kind": "Variable", + "canonicalReference": "api-extractor-scenarios!NS.NS_PUBLIC:var", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "NS_PUBLIC = " + }, + { + "kind": "Content", + "text": "\"PUBLIC\"" + } + ], + "fileUrlPath": "src/exportImportStarAs3/NamespaceWithTrimming.ts", + "initializerTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isReadonly": true, + "releaseTag": "Public", + "name": "NS_PUBLIC", + "variableTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + } + } + ] + } + ] + } + ] +} diff --git a/build-tests/api-extractor-scenarios/etc/exportImportStarAs3/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/exportImportStarAs3/api-extractor-scenarios.api.md new file mode 100644 index 00000000000..e9a2e07d1b6 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/exportImportStarAs3/api-extractor-scenarios.api.md @@ -0,0 +1,27 @@ +## API Report File for "api-extractor-scenarios" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +declare namespace NS { + export { + NS_PUBLIC, + NS_BETA, + NS_INTERNAL + } +} +export { NS } + +// @beta (undocumented) +const NS_BETA = "BETA"; + +// @internal (undocumented) +const NS_INTERNAL = "INTERNAL"; + +// @public (undocumented) +const NS_PUBLIC = "PUBLIC"; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/build-tests/api-extractor-scenarios/etc/exportImportStarAs3/rollup-public.d.ts b/build-tests/api-extractor-scenarios/etc/exportImportStarAs3/rollup-public.d.ts new file mode 100644 index 00000000000..386f2a3006d --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/exportImportStarAs3/rollup-public.d.ts @@ -0,0 +1,15 @@ +declare namespace NS { + export { + NS_PUBLIC + } +} +export { NS } + +/* Excluded from this release type: NS_BETA */ + +/* Excluded from this release type: NS_INTERNAL */ + +/** @public */ +declare const NS_PUBLIC = "PUBLIC"; + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/exportImportStarAs3/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/exportImportStarAs3/rollup.d.ts new file mode 100644 index 00000000000..c2331582e0c --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/exportImportStarAs3/rollup.d.ts @@ -0,0 +1,19 @@ +declare namespace NS { + export { + NS_PUBLIC, + NS_BETA, + NS_INTERNAL + } +} +export { NS } + +/** @beta */ +declare const NS_BETA = "BETA"; + +/** @internal */ +declare const NS_INTERNAL = "INTERNAL"; + +/** @public */ +declare const NS_PUBLIC = "PUBLIC"; + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/exportImportedExternal/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportImportedExternal/api-extractor-scenarios.api.json index 895ed82382e..f2997df1bb1 100644 --- a/build-tests/api-extractor-scenarios/etc/exportImportedExternal/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/exportImportedExternal/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/exportImportedExternal2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportImportedExternal2/api-extractor-scenarios.api.json index 895ed82382e..f2997df1bb1 100644 --- a/build-tests/api-extractor-scenarios/etc/exportImportedExternal2/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/exportImportedExternal2/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/exportImportedExternalDefault/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportImportedExternalDefault/api-extractor-scenarios.api.json index e38f2eb3fbc..771b403636c 100644 --- a/build-tests/api-extractor-scenarios/etc/exportImportedExternalDefault/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/exportImportedExternalDefault/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/exportStar/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportStar/api-extractor-scenarios.api.json index 034087c4404..e720c20a841 100644 --- a/build-tests/api-extractor-scenarios/etc/exportStar/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/exportStar/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/exportStar2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportStar2/api-extractor-scenarios.api.json index 89196c9883e..d199190991d 100644 --- a/build-tests/api-extractor-scenarios/etc/exportStar2/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/exportStar2/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/exportStar3/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportStar3/api-extractor-scenarios.api.json index 978e79dd798..324740f7367 100644 --- a/build-tests/api-extractor-scenarios/etc/exportStar3/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/exportStar3/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/exportStarAs/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportStarAs/api-extractor-scenarios.api.json new file mode 100644 index 00000000000..cd930436f06 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/exportStarAs/api-extractor-scenarios.api.json @@ -0,0 +1,508 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1011, + "oldestForwardsCompatibleVersion": 1001, + "tsdocConfig": { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "noStandardTags": true, + "tagDefinitions": [ + { + "tagName": "@alpha", + "syntaxKind": "modifier" + }, + { + "tagName": "@beta", + "syntaxKind": "modifier" + }, + { + "tagName": "@defaultValue", + "syntaxKind": "block" + }, + { + "tagName": "@decorator", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@deprecated", + "syntaxKind": "block" + }, + { + "tagName": "@eventProperty", + "syntaxKind": "modifier" + }, + { + "tagName": "@example", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@experimental", + "syntaxKind": "modifier" + }, + { + "tagName": "@inheritDoc", + "syntaxKind": "inline" + }, + { + "tagName": "@internal", + "syntaxKind": "modifier" + }, + { + "tagName": "@label", + "syntaxKind": "inline" + }, + { + "tagName": "@link", + "syntaxKind": "inline", + "allowMultiple": true + }, + { + "tagName": "@override", + "syntaxKind": "modifier" + }, + { + "tagName": "@packageDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@param", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@privateRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@public", + "syntaxKind": "modifier" + }, + { + "tagName": "@readonly", + "syntaxKind": "modifier" + }, + { + "tagName": "@remarks", + "syntaxKind": "block" + }, + { + "tagName": "@returns", + "syntaxKind": "block" + }, + { + "tagName": "@sealed", + "syntaxKind": "modifier" + }, + { + "tagName": "@see", + "syntaxKind": "block" + }, + { + "tagName": "@throws", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@typeParam", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@virtual", + "syntaxKind": "modifier" + }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, + { + "tagName": "@betaDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@internalRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@preapproved", + "syntaxKind": "modifier" + } + ], + "supportForTags": { + "@alpha": true, + "@beta": true, + "@defaultValue": true, + "@decorator": true, + "@deprecated": true, + "@eventProperty": true, + "@example": true, + "@experimental": true, + "@inheritDoc": true, + "@internal": true, + "@label": true, + "@link": true, + "@override": true, + "@packageDocumentation": true, + "@param": true, + "@privateRemarks": true, + "@public": true, + "@readonly": true, + "@remarks": true, + "@returns": true, + "@sealed": true, + "@see": true, + "@throws": true, + "@typeParam": true, + "@virtual": true, + "@betaDocumentation": true, + "@internalRemarks": true, + "@preapproved": true + }, + "reportUnsupportedHtmlElements": false + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-scenarios!", + "docComment": "", + "name": "api-extractor-scenarios", + "preserveMemberOrder": false, + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-scenarios!", + "name": "", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Namespace", + "canonicalReference": "api-extractor-scenarios!calculator:namespace", + "docComment": "", + "excerptTokens": [], + "fileUrlPath": "src/exportStarAs/index.ts", + "releaseTag": "None", + "name": "calculator", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!calculator.add:function(1)", + "docComment": "/**\n * Returns the sum of adding `b` to `a`\n *\n * @param a - first number\n *\n * @param b - second number\n *\n * @returns Sum of adding `b` to `a`\n *\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function add(a: " + }, + { + "kind": "Content", + "text": "number" + }, + { + "kind": "Content", + "text": ", b: " + }, + { + "kind": "Content", + "text": "number" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "number" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "src/exportStarAs/calculator.ts", + "returnTypeTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "a", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + }, + { + "parameterName": "b", + "parameterTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "isOptional": false + } + ], + "name": "add" + }, + { + "kind": "Variable", + "canonicalReference": "api-extractor-scenarios!calculator.calculatorVersion:var", + "docComment": "/**\n * Returns the version of the calculator.\n *\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "calculatorVersion: " + }, + { + "kind": "Content", + "text": "string" + } + ], + "fileUrlPath": "src/exportStarAs/common.ts", + "isReadonly": true, + "releaseTag": "Public", + "name": "calculatorVersion", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!calculator.subtract:function(1)", + "docComment": "/**\n * Returns the sum of subtracting `b` from `a`\n *\n * @param a - first number\n *\n * @param b - second number\n *\n * @returns Sum of subtract `b` from `a`\n *\n * @beta\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function subtract(a: " + }, + { + "kind": "Content", + "text": "number" + }, + { + "kind": "Content", + "text": ", b: " + }, + { + "kind": "Content", + "text": "number" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "number" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "src/exportStarAs/calculator.ts", + "returnTypeTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "releaseTag": "Beta", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "a", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + }, + { + "parameterName": "b", + "parameterTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "isOptional": false + } + ], + "name": "subtract" + } + ] + }, + { + "kind": "Namespace", + "canonicalReference": "api-extractor-scenarios!calculator2:namespace", + "docComment": "", + "excerptTokens": [], + "fileUrlPath": "src/exportStarAs/index.ts", + "releaseTag": "None", + "name": "calculator2", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!calculator2.add:function(1)", + "docComment": "/**\n * Returns the sum of adding `b` to `a` for large integers\n *\n * @param a - first number\n *\n * @param b - second number\n *\n * @returns Sum of adding `b` to `a`\n *\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function add(a: " + }, + { + "kind": "Content", + "text": "bigint" + }, + { + "kind": "Content", + "text": ", b: " + }, + { + "kind": "Content", + "text": "bigint" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "bigint" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "src/exportStarAs/calculator2.ts", + "returnTypeTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "a", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + }, + { + "parameterName": "b", + "parameterTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "isOptional": false + } + ], + "name": "add" + }, + { + "kind": "Variable", + "canonicalReference": "api-extractor-scenarios!calculator2.calculatorVersion:var", + "docComment": "/**\n * Returns the version of the calculator.\n *\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "calculatorVersion: " + }, + { + "kind": "Content", + "text": "string" + } + ], + "fileUrlPath": "src/exportStarAs/common.ts", + "isReadonly": true, + "releaseTag": "Public", + "name": "calculatorVersion", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!calculator2.subtract:function(1)", + "docComment": "/**\n * Returns the sum of subtracting `b` from `a` for large integers\n *\n * @param a - first number\n *\n * @param b - second number\n *\n * @returns Sum of subtract `b` from `a`\n *\n * @beta\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function subtract(a: " + }, + { + "kind": "Content", + "text": "bigint" + }, + { + "kind": "Content", + "text": ", b: " + }, + { + "kind": "Content", + "text": "bigint" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "bigint" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "src/exportStarAs/calculator2.ts", + "returnTypeTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "releaseTag": "Beta", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "a", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + }, + { + "parameterName": "b", + "parameterTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "isOptional": false + } + ], + "name": "subtract" + } + ] + } + ] + } + ] +} diff --git a/build-tests/api-extractor-scenarios/etc/exportStarAs/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/exportStarAs/api-extractor-scenarios.api.md new file mode 100644 index 00000000000..fe0c6582f31 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/exportStarAs/api-extractor-scenarios.api.md @@ -0,0 +1,40 @@ +## API Report File for "api-extractor-scenarios" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public +function add(a: number, b: number): number; + +// @public +function add_2(a: bigint, b: bigint): bigint; + +declare namespace calculator { + export { + add, + subtract, + calculatorVersion + } +} + +declare namespace calculator2 { + export { + add_2 as add, + subtract_2 as subtract, + calculatorVersion + } +} + +// @public +const calculatorVersion: string; + +// @beta +function subtract(a: number, b: number): number; + +// @beta +function subtract_2(a: bigint, b: bigint): bigint; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/build-tests/api-extractor-scenarios/etc/exportStarAs/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/exportStarAs/rollup.d.ts new file mode 100644 index 00000000000..4376f485243 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/exportStarAs/rollup.d.ts @@ -0,0 +1,59 @@ +/** + * Returns the sum of adding `b` to `a` + * @param a - first number + * @param b - second number + * @returns Sum of adding `b` to `a` + * @public + */ +declare function add(a: number, b: number): number; + +/** + * Returns the sum of adding `b` to `a` for large integers + * @param a - first number + * @param b - second number + * @returns Sum of adding `b` to `a` + * @public + */ +declare function add_2(a: bigint, b: bigint): bigint; + +export declare namespace calculator { + export { + add, + subtract, + calculatorVersion + } +} + +export declare namespace calculator2 { + export { + add_2 as add, + subtract_2 as subtract, + calculatorVersion + } +} + +/** + * Returns the version of the calculator. + * @public + */ +declare const calculatorVersion: string; + +/** + * Returns the sum of subtracting `b` from `a` + * @param a - first number + * @param b - second number + * @returns Sum of subtract `b` from `a` + * @beta + */ +declare function subtract(a: number, b: number): number; + +/** + * Returns the sum of subtracting `b` from `a` for large integers + * @param a - first number + * @param b - second number + * @returns Sum of subtract `b` from `a` + * @beta + */ +declare function subtract_2(a: bigint, b: bigint): bigint; + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/exportStarAs2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportStarAs2/api-extractor-scenarios.api.json new file mode 100644 index 00000000000..291bb916343 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/exportStarAs2/api-extractor-scenarios.api.json @@ -0,0 +1,235 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1011, + "oldestForwardsCompatibleVersion": 1001, + "tsdocConfig": { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "noStandardTags": true, + "tagDefinitions": [ + { + "tagName": "@alpha", + "syntaxKind": "modifier" + }, + { + "tagName": "@beta", + "syntaxKind": "modifier" + }, + { + "tagName": "@defaultValue", + "syntaxKind": "block" + }, + { + "tagName": "@decorator", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@deprecated", + "syntaxKind": "block" + }, + { + "tagName": "@eventProperty", + "syntaxKind": "modifier" + }, + { + "tagName": "@example", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@experimental", + "syntaxKind": "modifier" + }, + { + "tagName": "@inheritDoc", + "syntaxKind": "inline" + }, + { + "tagName": "@internal", + "syntaxKind": "modifier" + }, + { + "tagName": "@label", + "syntaxKind": "inline" + }, + { + "tagName": "@link", + "syntaxKind": "inline", + "allowMultiple": true + }, + { + "tagName": "@override", + "syntaxKind": "modifier" + }, + { + "tagName": "@packageDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@param", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@privateRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@public", + "syntaxKind": "modifier" + }, + { + "tagName": "@readonly", + "syntaxKind": "modifier" + }, + { + "tagName": "@remarks", + "syntaxKind": "block" + }, + { + "tagName": "@returns", + "syntaxKind": "block" + }, + { + "tagName": "@sealed", + "syntaxKind": "modifier" + }, + { + "tagName": "@see", + "syntaxKind": "block" + }, + { + "tagName": "@throws", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@typeParam", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@virtual", + "syntaxKind": "modifier" + }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, + { + "tagName": "@betaDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@internalRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@preapproved", + "syntaxKind": "modifier" + } + ], + "supportForTags": { + "@alpha": true, + "@beta": true, + "@defaultValue": true, + "@decorator": true, + "@deprecated": true, + "@eventProperty": true, + "@example": true, + "@experimental": true, + "@inheritDoc": true, + "@internal": true, + "@label": true, + "@link": true, + "@override": true, + "@packageDocumentation": true, + "@param": true, + "@privateRemarks": true, + "@public": true, + "@readonly": true, + "@remarks": true, + "@returns": true, + "@sealed": true, + "@see": true, + "@throws": true, + "@typeParam": true, + "@virtual": true, + "@betaDocumentation": true, + "@internalRemarks": true, + "@preapproved": true + }, + "reportUnsupportedHtmlElements": false + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-scenarios!", + "docComment": "", + "name": "api-extractor-scenarios", + "preserveMemberOrder": false, + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-scenarios!", + "name": "", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Namespace", + "canonicalReference": "api-extractor-scenarios!ns:namespace", + "docComment": "", + "excerptTokens": [], + "fileUrlPath": "src/exportStarAs2/index.ts", + "releaseTag": "None", + "name": "ns", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!ns.exportedApi:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function exportedApi(): " + }, + { + "kind": "Reference", + "text": "forgottenNs.ForgottenClass", + "canonicalReference": "api-extractor-scenarios!~ForgottenClass:class" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "src/exportStarAs2/ns.ts", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "exportedApi" + } + ] + } + ] + } + ] +} diff --git a/build-tests/api-extractor-scenarios/etc/exportStarAs2/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/exportStarAs2/api-extractor-scenarios.api.md new file mode 100644 index 00000000000..d45af72898d --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/exportStarAs2/api-extractor-scenarios.api.md @@ -0,0 +1,20 @@ +## API Report File for "api-extractor-scenarios" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// Warning: (ae-forgotten-export) The symbol "forgottenNs" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +function exportedApi(): forgottenNs.ForgottenClass; + +declare namespace ns { + export { + exportedApi + } +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/build-tests/api-extractor-scenarios/etc/exportStarAs2/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/exportStarAs2/rollup.d.ts new file mode 100644 index 00000000000..47900d36059 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/exportStarAs2/rollup.d.ts @@ -0,0 +1,24 @@ +/** + * @public + */ +declare function exportedApi(): forgottenNs.ForgottenClass; + +/** + * @public + */ +declare class ForgottenClass { +} + +declare namespace forgottenNs { + export { + ForgottenClass + } +} + +export declare namespace ns { + export { + exportedApi + } +} + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/exportStarAs3/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportStarAs3/api-extractor-scenarios.api.json new file mode 100644 index 00000000000..0c043e9368a --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/exportStarAs3/api-extractor-scenarios.api.json @@ -0,0 +1,260 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1011, + "oldestForwardsCompatibleVersion": 1001, + "tsdocConfig": { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "noStandardTags": true, + "tagDefinitions": [ + { + "tagName": "@alpha", + "syntaxKind": "modifier" + }, + { + "tagName": "@beta", + "syntaxKind": "modifier" + }, + { + "tagName": "@defaultValue", + "syntaxKind": "block" + }, + { + "tagName": "@decorator", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@deprecated", + "syntaxKind": "block" + }, + { + "tagName": "@eventProperty", + "syntaxKind": "modifier" + }, + { + "tagName": "@example", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@experimental", + "syntaxKind": "modifier" + }, + { + "tagName": "@inheritDoc", + "syntaxKind": "inline" + }, + { + "tagName": "@internal", + "syntaxKind": "modifier" + }, + { + "tagName": "@label", + "syntaxKind": "inline" + }, + { + "tagName": "@link", + "syntaxKind": "inline", + "allowMultiple": true + }, + { + "tagName": "@override", + "syntaxKind": "modifier" + }, + { + "tagName": "@packageDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@param", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@privateRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@public", + "syntaxKind": "modifier" + }, + { + "tagName": "@readonly", + "syntaxKind": "modifier" + }, + { + "tagName": "@remarks", + "syntaxKind": "block" + }, + { + "tagName": "@returns", + "syntaxKind": "block" + }, + { + "tagName": "@sealed", + "syntaxKind": "modifier" + }, + { + "tagName": "@see", + "syntaxKind": "block" + }, + { + "tagName": "@throws", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@typeParam", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@virtual", + "syntaxKind": "modifier" + }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, + { + "tagName": "@betaDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@internalRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@preapproved", + "syntaxKind": "modifier" + } + ], + "supportForTags": { + "@alpha": true, + "@beta": true, + "@defaultValue": true, + "@decorator": true, + "@deprecated": true, + "@eventProperty": true, + "@example": true, + "@experimental": true, + "@inheritDoc": true, + "@internal": true, + "@label": true, + "@link": true, + "@override": true, + "@packageDocumentation": true, + "@param": true, + "@privateRemarks": true, + "@public": true, + "@readonly": true, + "@remarks": true, + "@returns": true, + "@sealed": true, + "@see": true, + "@throws": true, + "@typeParam": true, + "@virtual": true, + "@betaDocumentation": true, + "@internalRemarks": true, + "@preapproved": true + }, + "reportUnsupportedHtmlElements": false + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-scenarios!", + "docComment": "", + "name": "api-extractor-scenarios", + "preserveMemberOrder": false, + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-scenarios!", + "name": "", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Namespace", + "canonicalReference": "api-extractor-scenarios!NS:namespace", + "docComment": "", + "excerptTokens": [], + "fileUrlPath": "src/exportStarAs3/index.ts", + "releaseTag": "None", + "name": "NS", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Variable", + "canonicalReference": "api-extractor-scenarios!NS.NS_BETA:var", + "docComment": "/**\n * @beta\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "NS_BETA = " + }, + { + "kind": "Content", + "text": "\"BETA\"" + } + ], + "fileUrlPath": "src/exportStarAs3/NamespaceWithTrimming.ts", + "initializerTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isReadonly": true, + "releaseTag": "Beta", + "name": "NS_BETA", + "variableTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + } + }, + { + "kind": "Variable", + "canonicalReference": "api-extractor-scenarios!NS.NS_PUBLIC:var", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "NS_PUBLIC = " + }, + { + "kind": "Content", + "text": "\"PUBLIC\"" + } + ], + "fileUrlPath": "src/exportStarAs3/NamespaceWithTrimming.ts", + "initializerTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isReadonly": true, + "releaseTag": "Public", + "name": "NS_PUBLIC", + "variableTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + } + } + ] + } + ] + } + ] +} diff --git a/build-tests/api-extractor-scenarios/etc/exportStarAs3/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/exportStarAs3/api-extractor-scenarios.api.md new file mode 100644 index 00000000000..1741a4e1d27 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/exportStarAs3/api-extractor-scenarios.api.md @@ -0,0 +1,26 @@ +## API Report File for "api-extractor-scenarios" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +declare namespace NS { + export { + NS_PUBLIC, + NS_BETA, + NS_INTERNAL + } +} + +// @beta (undocumented) +const NS_BETA = "BETA"; + +// @internal (undocumented) +const NS_INTERNAL = "INTERNAL"; + +// @public (undocumented) +const NS_PUBLIC = "PUBLIC"; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/build-tests/api-extractor-scenarios/etc/exportStarAs3/rollup-public.d.ts b/build-tests/api-extractor-scenarios/etc/exportStarAs3/rollup-public.d.ts new file mode 100644 index 00000000000..80ea239c15f --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/exportStarAs3/rollup-public.d.ts @@ -0,0 +1,14 @@ +export declare namespace NS { + export { + NS_PUBLIC + } +} + +/* Excluded from this release type: NS_BETA */ + +/* Excluded from this release type: NS_INTERNAL */ + +/** @public */ +declare const NS_PUBLIC = "PUBLIC"; + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/exportStarAs3/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/exportStarAs3/rollup.d.ts new file mode 100644 index 00000000000..1bdfef79947 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/exportStarAs3/rollup.d.ts @@ -0,0 +1,18 @@ +export declare namespace NS { + export { + NS_PUBLIC, + NS_BETA, + NS_INTERNAL + } +} + +/** @beta */ +declare const NS_BETA = "BETA"; + +/** @internal */ +declare const NS_INTERNAL = "INTERNAL"; + +/** @public */ +declare const NS_PUBLIC = "PUBLIC"; + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/functionOverload/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/functionOverload/api-extractor-scenarios.api.json index 77963078446..1b4335b21d6 100644 --- a/build-tests/api-extractor-scenarios/etc/functionOverload/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/functionOverload/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" @@ -233,6 +249,67 @@ ], "name": "_combine" }, + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!combine:function(1)", + "docComment": "/**\n * @alpha\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function combine(x: " + }, + { + "kind": "Content", + "text": "boolean" + }, + { + "kind": "Content", + "text": ", y: " + }, + { + "kind": "Content", + "text": "boolean" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "boolean" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "src/functionOverload/index.ts", + "returnTypeTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "releaseTag": "Alpha", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "x", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + }, + { + "parameterName": "y", + "parameterTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "isOptional": false + } + ], + "name": "combine" + }, { "kind": "Function", "canonicalReference": "api-extractor-scenarios!combine:function(2)", diff --git a/build-tests/api-extractor-scenarios/etc/importEquals/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/importEquals/api-extractor-scenarios.api.json index 99e2120fe4f..c1eb4247d74 100644 --- a/build-tests/api-extractor-scenarios/etc/importEquals/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/importEquals/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" @@ -187,8 +203,8 @@ }, { "kind": "Reference", - "text": "colors.zebra", - "canonicalReference": "colors!zebra:var" + "text": "Colorize.red", + "canonicalReference": "@rushstack/terminal!Colorize.red:member" }, { "kind": "Content", diff --git a/build-tests/api-extractor-scenarios/etc/importEquals/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/importEquals/api-extractor-scenarios.api.md index c12c59a717e..d6c801106bd 100644 --- a/build-tests/api-extractor-scenarios/etc/importEquals/api-extractor-scenarios.api.md +++ b/build-tests/api-extractor-scenarios/etc/importEquals/api-extractor-scenarios.api.md @@ -4,10 +4,10 @@ ```ts -import colors = require('colors'); +import { Colorize } from '@rushstack/terminal'; // @public (undocumented) -export function useColors(): typeof colors.zebra; +export function useColors(): typeof Colorize.red; // (No @packageDocumentation comment for this package) diff --git a/build-tests/api-extractor-scenarios/etc/importEquals/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/importEquals/rollup.d.ts index 144348ae07f..49db8d3d46d 100644 --- a/build-tests/api-extractor-scenarios/etc/importEquals/rollup.d.ts +++ b/build-tests/api-extractor-scenarios/etc/importEquals/rollup.d.ts @@ -1,6 +1,6 @@ -import colors = require('colors'); +import { Colorize } from '@rushstack/terminal'; /** @public */ -export declare function useColors(): typeof colors.zebra; +export declare function useColors(): typeof Colorize.red; export { } diff --git a/build-tests/api-extractor-scenarios/etc/importType/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/importType/api-extractor-scenarios.api.json index 9b7a0ba9812..b46345fa992 100644 --- a/build-tests/api-extractor-scenarios/etc/importType/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/importType/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json index 9a755f8d5cf..c7fccc789df 100644 --- a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.md index 43157fe55bb..483dafd6381 100644 --- a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.md +++ b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.md @@ -86,7 +86,7 @@ export namespace SomeNamespace1 { } // (undocumented) export function someFunction3(): ForgottenExport3; - {}; + export {}; } // (No @packageDocumentation comment for this package) diff --git a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/rollup.d.ts index 8010e499625..4d56859ed89 100644 --- a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/rollup.d.ts +++ b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/rollup.d.ts @@ -80,7 +80,7 @@ export declare namespace SomeNamespace1 { export class ForgottenExport3 { } export function someFunction3(): ForgottenExport3; - {}; + export {}; } export { } diff --git a/build-tests/api-extractor-scenarios/etc/inconsistentReleaseTags/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/inconsistentReleaseTags/api-extractor-scenarios.api.json index fd70ca6eb1a..53b805d5e00 100644 --- a/build-tests/api-extractor-scenarios/etc/inconsistentReleaseTags/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/inconsistentReleaseTags/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" @@ -172,6 +188,35 @@ "name": "", "preserveMemberOrder": false, "members": [ + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!alphaFunctionReturnsBeta:function(1)", + "docComment": "/**\n * It's okay for an \"alpha\" function to reference a \"beta\" symbol, because \"beta\" is more public than \"alpha\".\n *\n * @alpha\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function alphaFunctionReturnsBeta(): " + }, + { + "kind": "Reference", + "text": "IBeta", + "canonicalReference": "api-extractor-scenarios!IBeta:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "fileUrlPath": "src/inconsistentReleaseTags/index.ts", + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "releaseTag": "Alpha", + "overloadIndex": 1, + "parameters": [], + "name": "alphaFunctionReturnsBeta" + }, { "kind": "Interface", "canonicalReference": "api-extractor-scenarios!IBeta:interface", diff --git a/build-tests/api-extractor-scenarios/etc/inheritDoc/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/inheritDoc/api-extractor-scenarios.api.json new file mode 100644 index 00000000000..c69fe4339a2 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/inheritDoc/api-extractor-scenarios.api.json @@ -0,0 +1,302 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1011, + "oldestForwardsCompatibleVersion": 1001, + "tsdocConfig": { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "noStandardTags": true, + "tagDefinitions": [ + { + "tagName": "@alpha", + "syntaxKind": "modifier" + }, + { + "tagName": "@beta", + "syntaxKind": "modifier" + }, + { + "tagName": "@defaultValue", + "syntaxKind": "block" + }, + { + "tagName": "@decorator", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@deprecated", + "syntaxKind": "block" + }, + { + "tagName": "@eventProperty", + "syntaxKind": "modifier" + }, + { + "tagName": "@example", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@experimental", + "syntaxKind": "modifier" + }, + { + "tagName": "@inheritDoc", + "syntaxKind": "inline" + }, + { + "tagName": "@internal", + "syntaxKind": "modifier" + }, + { + "tagName": "@label", + "syntaxKind": "inline" + }, + { + "tagName": "@link", + "syntaxKind": "inline", + "allowMultiple": true + }, + { + "tagName": "@override", + "syntaxKind": "modifier" + }, + { + "tagName": "@packageDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@param", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@privateRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@public", + "syntaxKind": "modifier" + }, + { + "tagName": "@readonly", + "syntaxKind": "modifier" + }, + { + "tagName": "@remarks", + "syntaxKind": "block" + }, + { + "tagName": "@returns", + "syntaxKind": "block" + }, + { + "tagName": "@sealed", + "syntaxKind": "modifier" + }, + { + "tagName": "@see", + "syntaxKind": "block" + }, + { + "tagName": "@throws", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@typeParam", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@virtual", + "syntaxKind": "modifier" + }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, + { + "tagName": "@betaDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@internalRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@preapproved", + "syntaxKind": "modifier" + } + ], + "supportForTags": { + "@alpha": true, + "@beta": true, + "@defaultValue": true, + "@decorator": true, + "@deprecated": true, + "@eventProperty": true, + "@example": true, + "@experimental": true, + "@inheritDoc": true, + "@internal": true, + "@label": true, + "@link": true, + "@override": true, + "@packageDocumentation": true, + "@param": true, + "@privateRemarks": true, + "@public": true, + "@readonly": true, + "@remarks": true, + "@returns": true, + "@sealed": true, + "@see": true, + "@throws": true, + "@typeParam": true, + "@virtual": true, + "@betaDocumentation": true, + "@internalRemarks": true, + "@preapproved": true + }, + "reportUnsupportedHtmlElements": false + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-scenarios!", + "docComment": "", + "name": "api-extractor-scenarios", + "preserveMemberOrder": false, + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-scenarios!", + "name": "", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Variable", + "canonicalReference": "api-extractor-scenarios!inheritsFromExternal:var", + "docComment": "/**\n * {@inheritDoc some-external-library#foo}\n *\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "inheritsFromExternal = " + }, + { + "kind": "Content", + "text": "3" + } + ], + "fileUrlPath": "src/inheritDoc/index.ts", + "initializerTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isReadonly": true, + "releaseTag": "Public", + "name": "inheritsFromExternal", + "variableTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + } + }, + { + "kind": "Variable", + "canonicalReference": "api-extractor-scenarios!inheritsFromInternal:var", + "docComment": "/**\n * An API item with its own documentation.\n *\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "inheritsFromInternal = " + }, + { + "kind": "Content", + "text": "1" + } + ], + "fileUrlPath": "src/inheritDoc/index.ts", + "initializerTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isReadonly": true, + "releaseTag": "Public", + "name": "inheritsFromInternal", + "variableTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + } + }, + { + "kind": "Variable", + "canonicalReference": "api-extractor-scenarios!inheritsFromInvalidInternal:var", + "docComment": "/**\n * {@inheritDoc nonExistentTarget}\n *\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "inheritsFromInvalidInternal = " + }, + { + "kind": "Content", + "text": "2" + } + ], + "fileUrlPath": "src/inheritDoc/index.ts", + "initializerTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isReadonly": true, + "releaseTag": "Public", + "name": "inheritsFromInvalidInternal", + "variableTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + } + }, + { + "kind": "Variable", + "canonicalReference": "api-extractor-scenarios!withOwnDocs:var", + "docComment": "/**\n * An API item with its own documentation.\n *\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "withOwnDocs = " + }, + { + "kind": "Content", + "text": "0" + } + ], + "fileUrlPath": "src/inheritDoc/index.ts", + "initializerTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isReadonly": true, + "releaseTag": "Public", + "name": "withOwnDocs", + "variableTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + } + } + ] + } + ] +} diff --git a/build-tests/api-extractor-scenarios/etc/inheritDoc/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/inheritDoc/api-extractor-scenarios.api.md new file mode 100644 index 00000000000..1144e1838ad --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/inheritDoc/api-extractor-scenarios.api.md @@ -0,0 +1,23 @@ +## API Report File for "api-extractor-scenarios" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public +export const inheritsFromExternal = 3; + +// @public +export const inheritsFromInternal = 1; + +// Warning: (ae-unresolved-inheritdoc-reference) The @inheritDoc reference could not be resolved: The package "api-extractor-scenarios" does not have an export "nonExistentTarget" +// +// @public (undocumented) +export const inheritsFromInvalidInternal = 2; + +// @public +export const withOwnDocs = 0; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/build-tests/api-extractor-scenarios/etc/inheritDoc/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/inheritDoc/rollup.d.ts new file mode 100644 index 00000000000..4d235f3d54f --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/inheritDoc/rollup.d.ts @@ -0,0 +1,25 @@ +/** + * {@inheritDoc some-external-library#foo} + * @public + */ +export declare const inheritsFromExternal = 3; + +/** + * {@inheritDoc withOwnDocs} + * @public + */ +export declare const inheritsFromInternal = 1; + +/** + * {@inheritDoc nonExistentTarget} + * @public + */ +export declare const inheritsFromInvalidInternal = 2; + +/** + * An API item with its own documentation. + * @public + */ +export declare const withOwnDocs = 0; + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/internationalCharacters/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/internationalCharacters/api-extractor-scenarios.api.json index 5c99dc8cdb7..e52b79b4c90 100644 --- a/build-tests/api-extractor-scenarios/etc/internationalCharacters/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/internationalCharacters/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/mergedDeclarations/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/mergedDeclarations/api-extractor-scenarios.api.json index 41bf7a4b7cd..ce3f72c1a0b 100644 --- a/build-tests/api-extractor-scenarios/etc/mergedDeclarations/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/mergedDeclarations/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/mixinPattern/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/mixinPattern/api-extractor-scenarios.api.json index 133cb558f57..48db87ac527 100644 --- a/build-tests/api-extractor-scenarios/etc/mixinPattern/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/mixinPattern/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/mixinPattern/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/mixinPattern/rollup.d.ts index 2aeae4e1272..df49ee9bcab 100644 --- a/build-tests/api-extractor-scenarios/etc/mixinPattern/rollup.d.ts +++ b/build-tests/api-extractor-scenarios/etc/mixinPattern/rollup.d.ts @@ -9,7 +9,7 @@ export declare class B extends B_base { declare const B_base: { new (...args: any[]): { - mixinProp?: string | undefined; + mixinProp?: string; }; } & typeof A; diff --git a/build-tests/api-extractor-scenarios/etc/namedDefaultImport/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/namedDefaultImport/api-extractor-scenarios.api.json index e4a6ab82c27..6dc01c4631a 100644 --- a/build-tests/api-extractor-scenarios/etc/namedDefaultImport/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/namedDefaultImport/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.json index 7c915e7984a..c5bc0066f09 100644 --- a/build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/namespaceImports2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/namespaceImports2/api-extractor-scenarios.api.json index f40d2e64608..ebe57a27586 100644 --- a/build-tests/api-extractor-scenarios/etc/namespaceImports2/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/namespaceImports2/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/omitNewlines/alpha-rollup.d.ts b/build-tests/api-extractor-scenarios/etc/omitNewlines/alpha-rollup.d.ts new file mode 100644 index 00000000000..20f677445d5 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/omitNewlines/alpha-rollup.d.ts @@ -0,0 +1,19 @@ +/** + * @public + */ +export declare class Combiner { + /** + * @alpha + */ + alphaMember(x: boolean, y: boolean): boolean; + /** + * @beta + */ + betaMember(x: string, y: string): string; + /** + * @public + */ + publicMember(x: number, y: number): number; +} + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/omitNewlines/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/omitNewlines/api-extractor-scenarios.api.json new file mode 100644 index 00000000000..eaa246f2eb1 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/omitNewlines/api-extractor-scenarios.api.json @@ -0,0 +1,341 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1011, + "oldestForwardsCompatibleVersion": 1001, + "tsdocConfig": { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "noStandardTags": true, + "tagDefinitions": [ + { + "tagName": "@alpha", + "syntaxKind": "modifier" + }, + { + "tagName": "@beta", + "syntaxKind": "modifier" + }, + { + "tagName": "@defaultValue", + "syntaxKind": "block" + }, + { + "tagName": "@decorator", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@deprecated", + "syntaxKind": "block" + }, + { + "tagName": "@eventProperty", + "syntaxKind": "modifier" + }, + { + "tagName": "@example", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@experimental", + "syntaxKind": "modifier" + }, + { + "tagName": "@inheritDoc", + "syntaxKind": "inline" + }, + { + "tagName": "@internal", + "syntaxKind": "modifier" + }, + { + "tagName": "@label", + "syntaxKind": "inline" + }, + { + "tagName": "@link", + "syntaxKind": "inline", + "allowMultiple": true + }, + { + "tagName": "@override", + "syntaxKind": "modifier" + }, + { + "tagName": "@packageDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@param", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@privateRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@public", + "syntaxKind": "modifier" + }, + { + "tagName": "@readonly", + "syntaxKind": "modifier" + }, + { + "tagName": "@remarks", + "syntaxKind": "block" + }, + { + "tagName": "@returns", + "syntaxKind": "block" + }, + { + "tagName": "@sealed", + "syntaxKind": "modifier" + }, + { + "tagName": "@see", + "syntaxKind": "block" + }, + { + "tagName": "@throws", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@typeParam", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@virtual", + "syntaxKind": "modifier" + }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, + { + "tagName": "@betaDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@internalRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@preapproved", + "syntaxKind": "modifier" + } + ], + "supportForTags": { + "@alpha": true, + "@beta": true, + "@defaultValue": true, + "@decorator": true, + "@deprecated": true, + "@eventProperty": true, + "@example": true, + "@experimental": true, + "@inheritDoc": true, + "@internal": true, + "@label": true, + "@link": true, + "@override": true, + "@packageDocumentation": true, + "@param": true, + "@privateRemarks": true, + "@public": true, + "@readonly": true, + "@remarks": true, + "@returns": true, + "@sealed": true, + "@see": true, + "@throws": true, + "@typeParam": true, + "@virtual": true, + "@betaDocumentation": true, + "@internalRemarks": true, + "@preapproved": true + }, + "reportUnsupportedHtmlElements": false + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-scenarios!", + "docComment": "", + "name": "api-extractor-scenarios", + "preserveMemberOrder": false, + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-scenarios!", + "name": "", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Class", + "canonicalReference": "api-extractor-scenarios!Combiner:class", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare class Combiner " + } + ], + "fileUrlPath": "src/omitNewlines/index.ts", + "releaseTag": "Public", + "isAbstract": false, + "name": "Combiner", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Method", + "canonicalReference": "api-extractor-scenarios!Combiner#betaMember:member(1)", + "docComment": "/**\n * @beta\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "betaMember(x: " + }, + { + "kind": "Content", + "text": "string" + }, + { + "kind": "Content", + "text": ", y: " + }, + { + "kind": "Content", + "text": "string" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "string" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isStatic": false, + "returnTypeTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "releaseTag": "Beta", + "isProtected": false, + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "x", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + }, + { + "parameterName": "y", + "parameterTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "isOptional": false + } + ], + "isOptional": false, + "isAbstract": false, + "name": "betaMember" + }, + { + "kind": "Method", + "canonicalReference": "api-extractor-scenarios!Combiner#publicMember:member(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "publicMember(x: " + }, + { + "kind": "Content", + "text": "number" + }, + { + "kind": "Content", + "text": ", y: " + }, + { + "kind": "Content", + "text": "number" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "number" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isStatic": false, + "returnTypeTokenRange": { + "startIndex": 5, + "endIndex": 6 + }, + "releaseTag": "Public", + "isProtected": false, + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "x", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + }, + { + "parameterName": "y", + "parameterTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "isOptional": false + } + ], + "isOptional": false, + "isAbstract": false, + "name": "publicMember" + } + ], + "implementsTokenRanges": [] + } + ] + } + ] +} diff --git a/build-tests/api-extractor-scenarios/etc/omitNewlines/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/omitNewlines/api-extractor-scenarios.api.md new file mode 100644 index 00000000000..243e1bc494c --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/omitNewlines/api-extractor-scenarios.api.md @@ -0,0 +1,19 @@ +## API Report File for "api-extractor-scenarios" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public (undocumented) +export class Combiner { + // @alpha (undocumented) + alphaMember(x: boolean, y: boolean): boolean; + // @beta (undocumented) + betaMember(x: string, y: string): string; + // (undocumented) + publicMember(x: number, y: number): number; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/build-tests/api-extractor-scenarios/etc/omitNewlines/beta-rollup.d.ts b/build-tests/api-extractor-scenarios/etc/omitNewlines/beta-rollup.d.ts new file mode 100644 index 00000000000..c8d01554999 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/omitNewlines/beta-rollup.d.ts @@ -0,0 +1,15 @@ +/** + * @public + */ +export declare class Combiner { + /** + * @beta + */ + betaMember(x: string, y: string): string; + /** + * @public + */ + publicMember(x: number, y: number): number; +} + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/omitNewlines/public-rollup.d.ts b/build-tests/api-extractor-scenarios/etc/omitNewlines/public-rollup.d.ts new file mode 100644 index 00000000000..a089126470d --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/omitNewlines/public-rollup.d.ts @@ -0,0 +1,11 @@ +/** + * @public + */ +export declare class Combiner { + /** + * @public + */ + publicMember(x: number, y: number): number; +} + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/omitNewlines/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/omitNewlines/rollup.d.ts new file mode 100644 index 00000000000..20f677445d5 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/omitNewlines/rollup.d.ts @@ -0,0 +1,19 @@ +/** + * @public + */ +export declare class Combiner { + /** + * @alpha + */ + alphaMember(x: boolean, y: boolean): boolean; + /** + * @beta + */ + betaMember(x: string, y: string): string; + /** + * @public + */ + publicMember(x: number, y: number): number; +} + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/preapproved/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/preapproved/api-extractor-scenarios.api.json index 895ed82382e..f2997df1bb1 100644 --- a/build-tests/api-extractor-scenarios/etc/preapproved/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/preapproved/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/projectFolderUrl/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/projectFolderUrl/api-extractor-scenarios.api.json index bb32a39af82..87512f5600a 100644 --- a/build-tests/api-extractor-scenarios/etc/projectFolderUrl/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/projectFolderUrl/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/readonlyDeclarations/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/readonlyDeclarations/api-extractor-scenarios.api.json index c0ed696410b..c365d60a23a 100644 --- a/build-tests/api-extractor-scenarios/etc/readonlyDeclarations/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/readonlyDeclarations/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/referenceTokens/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/referenceTokens/api-extractor-scenarios.api.json index 3840c1933fe..5216041af41 100644 --- a/build-tests/api-extractor-scenarios/etc/referenceTokens/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/referenceTokens/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" @@ -640,7 +656,7 @@ "excerptTokens": [ { "kind": "Content", - "text": "export declare function someFunction7({ then }: " + "text": "export declare function someFunction7(input: " }, { "kind": "Reference", @@ -678,7 +694,15 @@ "overloadIndex": 1, "parameters": [ { - "parameterName": "{ then }", + "parameterName": "{ then: then2 }", + "parameterTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + }, + "isOptional": false + }, + { + "parameterName": "input", "parameterTypeTokenRange": { "startIndex": 1, "endIndex": 3 @@ -695,7 +719,7 @@ "excerptTokens": [ { "kind": "Content", - "text": "export declare function someFunction8({ prop }: " + "text": "export declare function someFunction8(input: " }, { "kind": "Reference", @@ -724,7 +748,15 @@ "overloadIndex": 1, "parameters": [ { - "parameterName": "{ prop }", + "parameterName": "{ prop: prop2 }", + "parameterTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + }, + "isOptional": false + }, + { + "parameterName": "input", "parameterTypeTokenRange": { "startIndex": 1, "endIndex": 2 @@ -741,7 +773,7 @@ "excerptTokens": [ { "kind": "Content", - "text": "export declare function someFunction9({ prop }: " + "text": "export declare function someFunction9(input: " }, { "kind": "Reference", @@ -770,7 +802,15 @@ "overloadIndex": 1, "parameters": [ { - "parameterName": "{ prop }", + "parameterName": "{ prop: prop2 }", + "parameterTypeTokenRange": { + "startIndex": 0, + "endIndex": 0 + }, + "isOptional": false + }, + { + "parameterName": "input", "parameterTypeTokenRange": { "startIndex": 1, "endIndex": 2 diff --git a/build-tests/api-extractor-scenarios/etc/referenceTokens/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/referenceTokens/api-extractor-scenarios.api.md index 0573dda8f50..16cf6cb3149 100644 --- a/build-tests/api-extractor-scenarios/etc/referenceTokens/api-extractor-scenarios.api.md +++ b/build-tests/api-extractor-scenarios/etc/referenceTokens/api-extractor-scenarios.api.md @@ -21,13 +21,13 @@ export namespace n1 { export function someFunction2(): SomeType2; // (undocumented) export type SomeType2 = number; - {}; + export {}; } // (undocumented) export function someFunction1(): SomeType1; // (undocumented) export type SomeType1 = number; - {}; + export {}; } // @public (undocumented) @@ -65,13 +65,13 @@ export function someFunction5(): SomeEnum.A; export function someFunction6(): typeof SomeClass1.staticProp; // @public -export function someFunction7({ then }: Promise): typeof Date.prototype.getDate; +export function someFunction7(input: Promise): typeof Date.prototype.getDate; // @public -export function someFunction8({ prop }: Lib2Class): void; +export function someFunction8(input: Lib2Class): void; // @public -export function someFunction9({ prop }: SomeInterface1): void; +export function someFunction9(input: SomeInterface1): void; // @public (undocumented) export interface SomeInterface1 { diff --git a/build-tests/api-extractor-scenarios/etc/referenceTokens/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/referenceTokens/rollup.d.ts index 85543b1e275..c27d5c3ee90 100644 --- a/build-tests/api-extractor-scenarios/etc/referenceTokens/rollup.d.ts +++ b/build-tests/api-extractor-scenarios/etc/referenceTokens/rollup.d.ts @@ -14,9 +14,9 @@ export declare namespace n1 { export type SomeType3 = number; export function someFunction3(): n2.n3.SomeType3; } - {}; + export {}; } - {}; + export {}; } /** @public */ @@ -67,19 +67,19 @@ export declare function someFunction6(): typeof SomeClass1.staticProp; * Global symbol reference. * @public */ -export declare function someFunction7({ then }: Promise): typeof Date.prototype.getDate; +export declare function someFunction7({ then: then2 }: Promise): typeof Date.prototype.getDate; /** * External symbol reference. * @public */ -export declare function someFunction8({ prop }: Lib2Class): void; +export declare function someFunction8({ prop: prop2 }: Lib2Class): void; /** * Interface member reference. * @public */ -export declare function someFunction9({ prop }: SomeInterface1): void; +export declare function someFunction9({ prop: prop2 }: SomeInterface1): void; /** @public */ export declare interface SomeInterface1 { diff --git a/build-tests/api-extractor-scenarios/etc/spanSorting/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/spanSorting/api-extractor-scenarios.api.json index 5315cb39238..a6a20a02af3 100644 --- a/build-tests/api-extractor-scenarios/etc/spanSorting/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/spanSorting/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" @@ -375,27 +391,45 @@ "implementsTokenRanges": [] }, { - "kind": "Variable", - "canonicalReference": "api-extractor-scenarios!exampleD:var", + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!exampleD:function(1)", "docComment": "/**\n * Outer description\n *\n * @public\n */\n", "excerptTokens": [ { "kind": "Content", - "text": "exampleD: " + "text": "exampleD: (o: " }, { "kind": "Content", - "text": "(o: {\n a: number;\n b(): string;\n}) => void" + "text": "{\n a: number;\n b(): string;\n}" + }, + { + "kind": "Content", + "text": ") => " + }, + { + "kind": "Content", + "text": "void" } ], "fileUrlPath": "src/spanSorting/index.ts", - "isReadonly": true, + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, "releaseTag": "Public", - "name": "exampleD", - "variableTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - } + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "o", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": false + } + ], + "name": "exampleD" } ] } diff --git a/build-tests/api-extractor-scenarios/etc/typeLiterals/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/typeLiterals/api-extractor-scenarios.api.json index 9e76ccb3d77..4af8707d08d 100644 --- a/build-tests/api-extractor-scenarios/etc/typeLiterals/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/typeLiterals/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/typeOf/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/typeOf/api-extractor-scenarios.api.json index 52ea964fd3c..febcf6ccf7c 100644 --- a/build-tests/api-extractor-scenarios/etc/typeOf/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/typeOf/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/typeOf2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/typeOf2/api-extractor-scenarios.api.json index bd0a1edd8c6..ba35fe47ce2 100644 --- a/build-tests/api-extractor-scenarios/etc/typeOf2/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/typeOf2/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/typeOf3/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/typeOf3/api-extractor-scenarios.api.json index 36f784c17cc..d592718a445 100644 --- a/build-tests/api-extractor-scenarios/etc/typeOf3/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/typeOf3/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/etc/typeParameters/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/typeParameters/api-extractor-scenarios.api.json index e4a3a2edda9..ed2b317d221 100644 --- a/build-tests/api-extractor-scenarios/etc/typeParameters/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/typeParameters/api-extractor-scenarios.api.json @@ -114,6 +114,22 @@ "tagName": "@virtual", "syntaxKind": "modifier" }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, { "tagName": "@betaDocumentation", "syntaxKind": "modifier" diff --git a/build-tests/api-extractor-scenarios/package.json b/build-tests/api-extractor-scenarios/package.json index c8f82b4a910..a0cd1de6461 100644 --- a/build-tests/api-extractor-scenarios/package.json +++ b/build-tests/api-extractor-scenarios/package.json @@ -3,23 +3,50 @@ "description": "Building this project is a regression test for api-extractor", "version": "1.0.0", "private": true, - "main": "lib/index.js", - "typings": "dist/internal/some-fake-file.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/internal/some-fake-file.d.ts", + "exports": { + ".": { + "types": "./dist/internal/some-fake-file.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "scripts": { - "build": "node build.js", - "_phase:build": "node build.js" + "build": "heft build --clean", + "_phase:build": "heft build --clean" + }, + "dependencies": { + "api-extractor-lib1-test": "workspace:*" }, "devDependencies": { "@microsoft/api-extractor": "workspace:*", "@microsoft/teams-js": "1.3.0-beta.4", + "@rushstack/heft": "workspace:*", "@rushstack/node-core-library": "workspace:*", - "@types/jest": "29.2.5", - "@types/node": "14.18.36", - "api-extractor-lib1-test": "workspace:*", + "@rushstack/terminal": "workspace:*", "api-extractor-lib2-test": "workspace:*", "api-extractor-lib3-test": "workspace:*", - "colors": "~1.2.1", - "fs-extra": "~7.0.1", - "typescript": "~5.0.4" + "api-extractor-lib4-test": "workspace:*", + "api-extractor-lib5-test": "workspace:*", + "local-node-rig": "workspace:*", + "run-scenarios-helpers": "workspace:*" + }, + "peerDependencies": { + "api-extractor-lib5-test": "workspace:*" } } diff --git a/build-tests/api-extractor-scenarios/src/bundledPackages/config/api-extractor-overrides.json b/build-tests/api-extractor-scenarios/src/bundledPackages/config/api-extractor-overrides.json index 0b497a09ba1..8f1c25c7572 100644 --- a/build-tests/api-extractor-scenarios/src/bundledPackages/config/api-extractor-overrides.json +++ b/build-tests/api-extractor-scenarios/src/bundledPackages/config/api-extractor-overrides.json @@ -1,3 +1,18 @@ { - "bundledPackages": ["api-extractor-lib1-test"] + "bundledPackages": [ + // Explicit package name + "api-extractor-lib1-test", + + // Simple glob pattern (resolves to a single package) + "api-extractor-lib2*", + + // Complex glob pattern (resolves to 3 packages: lib2, which is captured above; lib4; and lib5) + "*-lib{2,4,5}**", + + // Explicit package name with no dependency match + "@foo/bar", + + // Glob pattern with no dependency matches + "@baz/*" + ] } diff --git a/build-tests/api-extractor-scenarios/src/bundledPackages/index.ts b/build-tests/api-extractor-scenarios/src/bundledPackages/index.ts index b5344220bbb..5e13eba2702 100644 --- a/build-tests/api-extractor-scenarios/src/bundledPackages/index.ts +++ b/build-tests/api-extractor-scenarios/src/bundledPackages/index.ts @@ -3,8 +3,11 @@ import { Lib1Class } from 'api-extractor-lib1-test/lib/index'; import { Lib2Class } from 'api-extractor-lib2-test/lib/index'; +import { Lib3Class } from 'api-extractor-lib3-test/lib/index'; +import { Lib4Enum } from 'api-extractor-lib4-test/lib/index'; +import { lib5Function } from 'api-extractor-lib5-test/lib/index'; /** @public */ -export function f(arg1: Lib1Class, arg2: Lib2Class): void {} +export function f(arg1: Lib1Class, arg2: Lib2Class, arg3: Lib3Class, arg4: Lib4Enum): void {} -export { Lib1Class, Lib2Class }; +export { Lib1Class, Lib2Class, Lib3Class, Lib4Enum, lib5Function }; diff --git a/build-tests/api-extractor-scenarios/src/bundlerModuleResolution/config/api-extractor-overrides.json b/build-tests/api-extractor-scenarios/src/bundlerModuleResolution/config/api-extractor-overrides.json new file mode 100644 index 00000000000..01d3d35df03 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/bundlerModuleResolution/config/api-extractor-overrides.json @@ -0,0 +1,5 @@ +{ + "compiler": { + "tsconfigFilePath": "/src/bundlerModuleResolution/config/tsconfig.json" + } +} diff --git a/build-tests/api-extractor-scenarios/src/bundlerModuleResolution/config/tsconfig.json b/build-tests/api-extractor-scenarios/src/bundlerModuleResolution/config/tsconfig.json new file mode 100644 index 00000000000..337ccda7424 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/bundlerModuleResolution/config/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "module": "esnext", + "moduleResolution": "bundler" + } +} diff --git a/build-tests/api-extractor-scenarios/src/bundlerModuleResolution/index.ts b/build-tests/api-extractor-scenarios/src/bundlerModuleResolution/index.ts new file mode 100644 index 00000000000..5587838462e --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/bundlerModuleResolution/index.ts @@ -0,0 +1,6 @@ +import { foo } from './other'; + +/** + * @public + */ +export const reexport = foo; diff --git a/build-tests/api-extractor-scenarios/src/bundlerModuleResolution/other.ts b/build-tests/api-extractor-scenarios/src/bundlerModuleResolution/other.ts new file mode 100644 index 00000000000..a047a632f4d --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/bundlerModuleResolution/other.ts @@ -0,0 +1,3 @@ +export class Foo {} + +export const foo = new Foo(); diff --git a/build-tests/api-extractor-scenarios/src/destructuredParameters/index.ts b/build-tests/api-extractor-scenarios/src/destructuredParameters/index.ts new file mode 100644 index 00000000000..021cda4b5dd --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/destructuredParameters/index.ts @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** @public */ +export function testObject(first: string, { x, y }: { x: number; y: number }): void {} + +/** @public */ +export function testArray([x, y]: [number, number], last: string): void {} + +/** @public */ +export function testObjects({ x }: { x: number }, { y }: { y: number }): void {} + +/** @public */ +export function testNameConflict([x, y]: [number, number], input: boolean): void {} + +/** @public */ +export function testNameConflict2({ x }: { x: number }, { y }: { y: number }, input2: string): void {} + +/** @public */ +export function testObjectWithComments( + // slash P1 + { + // slash P2 + x, // slash P3 + // slash P4 + y // slash P5 + // slash P6 + }: // slash T1 + { + // slash T2 + x: number; // slash T3 + // slash T4 + y: number; // slash T5 + // slash T6 + } // slash T7 + // slash T8 +): void {} diff --git a/build-tests/api-extractor-scenarios/src/docReferencesAlias/index.ts b/build-tests/api-extractor-scenarios/src/docReferencesAlias/index.ts index 5eb64bd7644..889570c3306 100644 --- a/build-tests/api-extractor-scenarios/src/docReferencesAlias/index.ts +++ b/build-tests/api-extractor-scenarios/src/docReferencesAlias/index.ts @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -export { default as renamed_Options } from './Options'; +export type { default as renamed_Options } from './Options'; export { default as Item } from './Item'; diff --git a/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/sub/index.ts b/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/sub/index.ts index bbef81bf862..1c21a1ed30e 100644 --- a/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/sub/index.ts +++ b/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/sub/index.ts @@ -1,4 +1,4 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -export { default as SubOptions } from './SubOptions'; +export type { default as SubOptions } from './SubOptions'; diff --git a/build-tests/api-extractor-scenarios/src/exportImportStarAs3/NamespaceWithTrimming.ts b/build-tests/api-extractor-scenarios/src/exportImportStarAs3/NamespaceWithTrimming.ts new file mode 100644 index 00000000000..7045c1aa3eb --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/exportImportStarAs3/NamespaceWithTrimming.ts @@ -0,0 +1,8 @@ +/** @public */ +export const NS_PUBLIC = 'PUBLIC'; + +/** @beta */ +export const NS_BETA = 'BETA'; + +/** @internal */ +export const NS_INTERNAL = 'INTERNAL'; diff --git a/build-tests/api-extractor-scenarios/src/exportImportStarAs3/config/api-extractor-overrides.json b/build-tests/api-extractor-scenarios/src/exportImportStarAs3/config/api-extractor-overrides.json new file mode 100644 index 00000000000..56d14e1823b --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/exportImportStarAs3/config/api-extractor-overrides.json @@ -0,0 +1,7 @@ +{ + "dtsRollup": { + "enabled": true, + "untrimmedFilePath": "/temp/etc/exportImportStarAs3/rollup.d.ts", + "publicTrimmedFilePath": "/temp/etc/exportImportStarAs3/rollup-public.d.ts" + } +} diff --git a/build-tests/api-extractor-scenarios/src/exportImportStarAs3/index.ts b/build-tests/api-extractor-scenarios/src/exportImportStarAs3/index.ts new file mode 100644 index 00000000000..0afcd1c27ef --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/exportImportStarAs3/index.ts @@ -0,0 +1,6 @@ +/** + * Test that when exporting namespaces, we don't export members that got trimmed. + * See this issue: https://github.com/microsoft/rushstack/issues/2791 + */ +import * as NS from './NamespaceWithTrimming'; +export { NS }; diff --git a/build-tests/api-extractor-scenarios/src/exportStarAs/calculator.ts b/build-tests/api-extractor-scenarios/src/exportStarAs/calculator.ts new file mode 100644 index 00000000000..17df6809b45 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/exportStarAs/calculator.ts @@ -0,0 +1,23 @@ +/** + * Returns the sum of adding `b` to `a` + * @param a - first number + * @param b - second number + * @returns Sum of adding `b` to `a` + * @public + */ +export function add(a: number, b: number): number { + return a + b; +} + +/** + * Returns the sum of subtracting `b` from `a` + * @param a - first number + * @param b - second number + * @returns Sum of subtract `b` from `a` + * @beta + */ +export function subtract(a: number, b: number): number { + return a - b; +} + +export * from './common'; diff --git a/build-tests/api-extractor-scenarios/src/exportStarAs/calculator2.ts b/build-tests/api-extractor-scenarios/src/exportStarAs/calculator2.ts new file mode 100644 index 00000000000..38b5ef6950a --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/exportStarAs/calculator2.ts @@ -0,0 +1,23 @@ +/** + * Returns the sum of adding `b` to `a` for large integers + * @param a - first number + * @param b - second number + * @returns Sum of adding `b` to `a` + * @public + */ +export function add(a: bigint, b: bigint): bigint { + return a + b; +} + +/** + * Returns the sum of subtracting `b` from `a` for large integers + * @param a - first number + * @param b - second number + * @returns Sum of subtract `b` from `a` + * @beta + */ +export function subtract(a: bigint, b: bigint): bigint { + return a - b; +} + +export * from './common'; diff --git a/build-tests/api-extractor-scenarios/src/exportStarAs/common.ts b/build-tests/api-extractor-scenarios/src/exportStarAs/common.ts new file mode 100644 index 00000000000..67d0445901d --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/exportStarAs/common.ts @@ -0,0 +1,5 @@ +/** + * Returns the version of the calculator. + * @public + */ +export const calculatorVersion: string = '1.0.0'; diff --git a/build-tests/api-extractor-scenarios/src/exportStarAs/index.ts b/build-tests/api-extractor-scenarios/src/exportStarAs/index.ts new file mode 100644 index 00000000000..22ebe390dc0 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/exportStarAs/index.ts @@ -0,0 +1,2 @@ +export * as calculator from './calculator'; +export * as calculator2 from './calculator2'; diff --git a/build-tests/api-extractor-scenarios/src/exportStarAs2/forgottenNs.ts b/build-tests/api-extractor-scenarios/src/exportStarAs2/forgottenNs.ts new file mode 100644 index 00000000000..fc104acc837 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/exportStarAs2/forgottenNs.ts @@ -0,0 +1,4 @@ +/** + * @public + */ +export class ForgottenClass {} diff --git a/build-tests/api-extractor-scenarios/src/exportStarAs2/index.ts b/build-tests/api-extractor-scenarios/src/exportStarAs2/index.ts new file mode 100644 index 00000000000..497daa5907b --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/exportStarAs2/index.ts @@ -0,0 +1 @@ +export * as ns from './ns'; diff --git a/build-tests/api-extractor-scenarios/src/exportStarAs2/ns.ts b/build-tests/api-extractor-scenarios/src/exportStarAs2/ns.ts new file mode 100644 index 00000000000..5ea39d1dc1c --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/exportStarAs2/ns.ts @@ -0,0 +1,8 @@ +import * as forgottenNs from './forgottenNs'; + +/** + * @public + */ +export function exportedApi(): forgottenNs.ForgottenClass { + return new forgottenNs.ForgottenClass(); +} diff --git a/build-tests/api-extractor-scenarios/src/exportStarAs3/NamespaceWithTrimming.ts b/build-tests/api-extractor-scenarios/src/exportStarAs3/NamespaceWithTrimming.ts new file mode 100644 index 00000000000..7045c1aa3eb --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/exportStarAs3/NamespaceWithTrimming.ts @@ -0,0 +1,8 @@ +/** @public */ +export const NS_PUBLIC = 'PUBLIC'; + +/** @beta */ +export const NS_BETA = 'BETA'; + +/** @internal */ +export const NS_INTERNAL = 'INTERNAL'; diff --git a/build-tests/api-extractor-scenarios/src/exportStarAs3/config/api-extractor-overrides.json b/build-tests/api-extractor-scenarios/src/exportStarAs3/config/api-extractor-overrides.json new file mode 100644 index 00000000000..414a2156393 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/exportStarAs3/config/api-extractor-overrides.json @@ -0,0 +1,7 @@ +{ + "dtsRollup": { + "enabled": true, + "untrimmedFilePath": "/temp/etc/exportStarAs3/rollup.d.ts", + "publicTrimmedFilePath": "/temp/etc/exportStarAs3/rollup-public.d.ts" + } +} diff --git a/build-tests/api-extractor-scenarios/src/exportStarAs3/index.ts b/build-tests/api-extractor-scenarios/src/exportStarAs3/index.ts new file mode 100644 index 00000000000..6bd5a26a26d --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/exportStarAs3/index.ts @@ -0,0 +1,5 @@ +/** + * Test that when exporting namespaces, we don't export members that got trimmed. + * See this issue: https://github.com/microsoft/rushstack/issues/2791 + */ +export * as NS from './NamespaceWithTrimming'; diff --git a/build-tests/api-extractor-scenarios/src/functionOverload/config/api-extractor-overrides.json b/build-tests/api-extractor-scenarios/src/functionOverload/config/api-extractor-overrides.json index 10a6d0092aa..43a683131c6 100644 --- a/build-tests/api-extractor-scenarios/src/functionOverload/config/api-extractor-overrides.json +++ b/build-tests/api-extractor-scenarios/src/functionOverload/config/api-extractor-overrides.json @@ -1,9 +1,9 @@ { "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/etc/functionOverload/rollup.d.ts", - "alphaTrimmedFilePath": "/etc/functionOverload/alpha-rollup.d.ts", - "betaTrimmedFilePath": "/etc/functionOverload/beta-rollup.d.ts", - "publicTrimmedFilePath": "/etc/functionOverload/public-rollup.d.ts" + "untrimmedFilePath": "/temp/etc/functionOverload/rollup.d.ts", + "alphaTrimmedFilePath": "/temp/etc/functionOverload/alpha-rollup.d.ts", + "betaTrimmedFilePath": "/temp/etc/functionOverload/beta-rollup.d.ts", + "publicTrimmedFilePath": "/temp/etc/functionOverload/public-rollup.d.ts" } } diff --git a/build-tests/api-extractor-scenarios/src/importEquals/index.ts b/build-tests/api-extractor-scenarios/src/importEquals/index.ts index d57b9076617..6af3a22b33f 100644 --- a/build-tests/api-extractor-scenarios/src/importEquals/index.ts +++ b/build-tests/api-extractor-scenarios/src/importEquals/index.ts @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors = require('colors'); +import { Colorize } from '@rushstack/terminal'; /** @public */ -export function useColors(): typeof colors.zebra { - return colors.zebra; +export function useColors(): typeof Colorize.red { + return Colorize.red; } diff --git a/build-tests/api-extractor-scenarios/src/includeForgottenExports/config/api-extractor-overrides.json b/build-tests/api-extractor-scenarios/src/includeForgottenExports/config/api-extractor-overrides.json index 103f3c5ec14..da3ba9470ba 100644 --- a/build-tests/api-extractor-scenarios/src/includeForgottenExports/config/api-extractor-overrides.json +++ b/build-tests/api-extractor-scenarios/src/includeForgottenExports/config/api-extractor-overrides.json @@ -1,13 +1,13 @@ { "apiReport": { "enabled": true, - "reportFolder": "/etc/includeForgottenExports", + "reportFolder": "/temp/etc/includeForgottenExports", "includeForgottenExports": true }, "docModel": { "enabled": true, - "apiJsonFilePath": "/etc/includeForgottenExports/.api.json", + "apiJsonFilePath": "/temp/etc/includeForgottenExports/.api.json", "includeForgottenExports": true } } diff --git a/build-tests/api-extractor-scenarios/src/inheritDoc/index.ts b/build-tests/api-extractor-scenarios/src/inheritDoc/index.ts new file mode 100644 index 00000000000..32973b443bc --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/inheritDoc/index.ts @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * An API item with its own documentation. + * @public + */ +export const withOwnDocs = 0; + +/** + * {@inheritDoc withOwnDocs} + * @public + */ +export const inheritsFromInternal = 1; + +/** + * {@inheritDoc nonExistentTarget} + * @public + */ +export const inheritsFromInvalidInternal = 2; + +/** + * {@inheritDoc some-external-library#foo} + * @public + */ +export const inheritsFromExternal = 3; diff --git a/build-tests/api-extractor-scenarios/src/omitNewlines/config/api-extractor-overrides.json b/build-tests/api-extractor-scenarios/src/omitNewlines/config/api-extractor-overrides.json new file mode 100644 index 00000000000..fc9e30c0dea --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/omitNewlines/config/api-extractor-overrides.json @@ -0,0 +1,10 @@ +{ + "dtsRollup": { + "enabled": true, + "untrimmedFilePath": "/temp/etc/omitNewlines/rollup.d.ts", + "alphaTrimmedFilePath": "/temp/etc/omitNewlines/alpha-rollup.d.ts", + "betaTrimmedFilePath": "/temp/etc/omitNewlines/beta-rollup.d.ts", + "publicTrimmedFilePath": "/temp/etc/omitNewlines/public-rollup.d.ts", + "omitTrimmingComments": true + } +} diff --git a/build-tests/api-extractor-scenarios/src/omitNewlines/index.ts b/build-tests/api-extractor-scenarios/src/omitNewlines/index.ts new file mode 100644 index 00000000000..3630c671f60 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/omitNewlines/index.ts @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * @public + */ +export class Combiner { + /** + * @alpha + */ + public alphaMember(x: boolean, y: boolean): boolean { + return false; + } + + /** + * @beta + */ + public betaMember(x: string, y: string): string { + return ''; + } + + /** + * @public + */ + public publicMember(x: number, y: number): number { + return 42; + } +} diff --git a/build-tests/api-extractor-scenarios/src/projectFolderUrl/config/api-extractor-overrides.json b/build-tests/api-extractor-scenarios/src/projectFolderUrl/config/api-extractor-overrides.json index d2b093e8ee4..248ad89f144 100644 --- a/build-tests/api-extractor-scenarios/src/projectFolderUrl/config/api-extractor-overrides.json +++ b/build-tests/api-extractor-scenarios/src/projectFolderUrl/config/api-extractor-overrides.json @@ -1,7 +1,7 @@ { "docModel": { "enabled": true, - "apiJsonFilePath": "/etc/projectFolderUrl/.api.json", + "apiJsonFilePath": "/temp/etc/projectFolderUrl/.api.json", "projectFolderUrl": "http://github.com/path/to/some/projectFolder" } } diff --git a/build-tests/api-extractor-scenarios/src/runScenarios.ts b/build-tests/api-extractor-scenarios/src/runScenarios.ts index 54863dc9863..6282ce60443 100644 --- a/build-tests/api-extractor-scenarios/src/runScenarios.ts +++ b/build-tests/api-extractor-scenarios/src/runScenarios.ts @@ -1,65 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { AlreadyExistsBehavior, FileSystem, JsonFile } from '@rushstack/node-core-library'; -import { - Extractor, - ExtractorConfig, - CompilerState, - ExtractorResult, - ExtractorMessage, - ConsoleMessageId, - ExtractorLogLevel -} from '@microsoft/api-extractor'; - -export function runScenarios(buildConfigPath: string): void { - const buildConfig = JsonFile.load(buildConfigPath); - - // Copy any .d.ts files into the "lib/" folder - FileSystem.copyFiles({ - sourcePath: './src/', - destinationPath: './lib/', - alreadyExistsBehavior: AlreadyExistsBehavior.Overwrite, - filter: (sourcePath: string): boolean => { - if (sourcePath.endsWith('.d.ts') || !sourcePath.endsWith('.ts')) { - // console.log('COPY ' + sourcePath); - return true; - } - return false; - } - }); - - const entryPoints: string[] = []; - - for (const scenarioFolderName of buildConfig.scenarioFolderNames) { - const entryPoint: string = path.resolve(`./lib/${scenarioFolderName}/index.d.ts`); - entryPoints.push(entryPoint); - - const overridesPath = path.resolve(`./src/${scenarioFolderName}/config/api-extractor-overrides.json`); - const apiExtractorJsonOverrides = FileSystem.exists(overridesPath) ? JsonFile.load(overridesPath) : {}; - const apiExtractorJson = { - $schema: 'https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json', - - mainEntryPointFilePath: entryPoint, - - apiReport: { - enabled: true, - reportFolder: `/etc/${scenarioFolderName}` - }, - - dtsRollup: { - enabled: true, - untrimmedFilePath: `/etc/${scenarioFolderName}/rollup.d.ts` - }, - - docModel: { - enabled: true, - apiJsonFilePath: `/etc/${scenarioFolderName}/.api.json` - }, - - newlineKind: 'os', +import type { IRunScriptOptions } from '@rushstack/heft'; +import { runScenariosAsync } from 'run-scenarios-helpers'; +export async function runAsync(runScriptOptions: IRunScriptOptions): Promise { + await runScenariosAsync(runScriptOptions, { + libFolderPath: __dirname, + additionalApiExtractorConfig: { messages: { extractorMessageReporting: { // For test purposes, write these warnings into .api.md @@ -73,58 +21,7 @@ export function runScenarios(buildConfigPath: string): void { addToApiReportFile: true } } - }, - - testMode: true, - ...apiExtractorJsonOverrides - }; - - const apiExtractorJsonPath: string = `./temp/configs/api-extractor-${scenarioFolderName}.json`; - - JsonFile.save(apiExtractorJson, apiExtractorJsonPath, { ensureFolderExists: true }); - } - - let compilerState: CompilerState | undefined = undefined; - let anyErrors: boolean = false; - process.exitCode = 1; - - for (const scenarioFolderName of buildConfig.scenarioFolderNames) { - console.log('Scenario: ' + scenarioFolderName); - - // Run the API Extractor programmatically - const apiExtractorJsonPath: string = `./temp/configs/api-extractor-${scenarioFolderName}.json`; - const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare(apiExtractorJsonPath); - - if (!compilerState) { - compilerState = CompilerState.create(extractorConfig, { - additionalEntryPoints: entryPoints - }); - } - - const extractorResult: ExtractorResult = Extractor.invoke(extractorConfig, { - localBuild: true, - showVerboseMessages: true, - messageCallback: (message: ExtractorMessage) => { - switch (message.messageId) { - case ConsoleMessageId.ApiReportCreated: - // This script deletes the outputs for a clean build, so don't issue a warning if the file gets created - message.logLevel = ExtractorLogLevel.None; - break; - case ConsoleMessageId.Preamble: - // Less verbose output - message.logLevel = ExtractorLogLevel.None; - break; - } - }, - compilerState - }); - - if (extractorResult.errorCount > 0) { - anyErrors = true; + } } - } - - if (!anyErrors) { - process.exitCode = 0; - } + }); } diff --git a/build-tests/api-extractor-scenarios/tsconfig.json b/build-tests/api-extractor-scenarios/tsconfig.json index 1799652cc42..2452b093b0e 100644 --- a/build-tests/api-extractor-scenarios/tsconfig.json +++ b/build-tests/api-extractor-scenarios/tsconfig.json @@ -1,16 +1,11 @@ { + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "target": "es6", - "forceConsistentCasingInFileNames": true, - "module": "commonjs", - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "types": ["node", "jest"], - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "outDir": "lib" + "strictPropertyInitialization": false, + "noImplicitAny": false, + // Intentionally turn this off for this project to test a combination of `export` and `export type` + // with type-only exports + "isolatedModules": false }, "include": ["src/**/*.ts", "typings/tsd.d.ts"] } diff --git a/build-tests/api-extractor-test-01/build.js b/build-tests/api-extractor-test-01/build.js deleted file mode 100644 index 6378977fc0b..00000000000 --- a/build-tests/api-extractor-test-01/build.js +++ /dev/null @@ -1,27 +0,0 @@ -const fsx = require('fs-extra'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -fsx.emptyDirSync('dist'); -fsx.emptyDirSync('lib'); -fsx.emptyDirSync('temp'); - -// Run the TypeScript compiler -executeCommand('node node_modules/typescript/lib/tsc'); - -// Run the API Extractor command-line -if (process.argv.indexOf('--production') >= 0) { - executeCommand('node node_modules/@microsoft/api-extractor/lib/start run'); -} else { - executeCommand('node node_modules/@microsoft/api-extractor/lib/start run --local'); -} - -console.log(`==> Finished build.js for ${path.basename(process.cwd())}`); diff --git a/build-tests/api-extractor-test-01/config/api-extractor.json b/build-tests/api-extractor-test-01/config/api-extractor.json index e4725c5edc7..7b0adce7331 100644 --- a/build-tests/api-extractor-test-01/config/api-extractor.json +++ b/build-tests/api-extractor-test-01/config/api-extractor.json @@ -1,7 +1,7 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - "mainEntryPointFilePath": "/lib/index.d.ts", + "mainEntryPointFilePath": "/lib-dts/index.d.ts", "apiReport": { "enabled": true diff --git a/build-tests/api-extractor-test-01/config/rig.json b/build-tests/api-extractor-test-01/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-extractor-test-01/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-extractor-test-01/config/rush-project.json b/build-tests/api-extractor-test-01/config/rush-project.json index 317ec878bc3..6c75ba02c45 100644 --- a/build-tests/api-extractor-test-01/config/rush-project.json +++ b/build-tests/api-extractor-test-01/config/rush-project.json @@ -1,8 +1,11 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib"] + "operationName": "_phase:build", + // dist is intentionally tracked + "outputFolderNames": ["lib-commonjs", "lib-esm", "lib-dts"] } ] } diff --git a/build-tests/api-extractor-test-01/dist/api-extractor-test-01-alpha.d.ts b/build-tests/api-extractor-test-01/dist/api-extractor-test-01-alpha.d.ts index 09a808c796b..fc9359c3768 100644 --- a/build-tests/api-extractor-test-01/dist/api-extractor-test-01-alpha.d.ts +++ b/build-tests/api-extractor-test-01/dist/api-extractor-test-01-alpha.d.ts @@ -9,10 +9,6 @@ * @packageDocumentation */ -/// -/// -/// - import { default as Long_2 } from 'long'; import { MAX_UNSIGNED_VALUE } from 'long'; diff --git a/build-tests/api-extractor-test-01/dist/api-extractor-test-01-beta.d.ts b/build-tests/api-extractor-test-01/dist/api-extractor-test-01-beta.d.ts index eeefa612ec5..da1c9f372fa 100644 --- a/build-tests/api-extractor-test-01/dist/api-extractor-test-01-beta.d.ts +++ b/build-tests/api-extractor-test-01/dist/api-extractor-test-01-beta.d.ts @@ -9,10 +9,6 @@ * @packageDocumentation */ -/// -/// -/// - import { default as Long_2 } from 'long'; import { MAX_UNSIGNED_VALUE } from 'long'; diff --git a/build-tests/api-extractor-test-01/dist/api-extractor-test-01-public.d.ts b/build-tests/api-extractor-test-01/dist/api-extractor-test-01-public.d.ts index 7931a32b65b..5dfc19c301d 100644 --- a/build-tests/api-extractor-test-01/dist/api-extractor-test-01-public.d.ts +++ b/build-tests/api-extractor-test-01/dist/api-extractor-test-01-public.d.ts @@ -9,10 +9,6 @@ * @packageDocumentation */ -/// -/// -/// - import { default as Long_2 } from 'long'; import { MAX_UNSIGNED_VALUE } from 'long'; diff --git a/build-tests/api-extractor-test-01/dist/api-extractor-test-01.d.ts b/build-tests/api-extractor-test-01/dist/api-extractor-test-01.d.ts index 4c16330d5de..957d7427063 100644 --- a/build-tests/api-extractor-test-01/dist/api-extractor-test-01.d.ts +++ b/build-tests/api-extractor-test-01/dist/api-extractor-test-01.d.ts @@ -9,10 +9,6 @@ * @packageDocumentation */ -/// -/// -/// - import { default as Long_2 } from 'long'; import { MAX_UNSIGNED_VALUE } from 'long'; diff --git a/build-tests/api-extractor-test-01/etc/api-extractor-test-01.api.md b/build-tests/api-extractor-test-01/etc/api-extractor-test-01.api.md index 395686f0c86..40014a89d95 100644 --- a/build-tests/api-extractor-test-01/etc/api-extractor-test-01.api.md +++ b/build-tests/api-extractor-test-01/etc/api-extractor-test-01.api.md @@ -4,10 +4,6 @@ ```ts -/// -/// -/// - import { default as Long_2 } from 'long'; import { MAX_UNSIGNED_VALUE } from 'long'; diff --git a/build-tests/api-extractor-test-01/package.json b/build-tests/api-extractor-test-01/package.json index 217134d3da7..cd5cf7c1792 100644 --- a/build-tests/api-extractor-test-01/package.json +++ b/build-tests/api-extractor-test-01/package.json @@ -3,22 +3,40 @@ "description": "Building this project is a regression test for api-extractor", "version": "1.0.0", "private": true, - "main": "lib/index.js", - "typings": "dist/api-extractor-test-01.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/api-extractor-test-01.d.ts", + "exports": { + ".": { + "types": "./dist/api-extractor-test-01.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "scripts": { - "build": "node build.js", - "_phase:build": "node build.js" + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" }, "dependencies": { - "@types/jest": "29.2.5", + "@types/jest": "30.0.0", "@types/long": "4.0.0", "long": "^4.0.0" }, "devDependencies": { - "@microsoft/api-extractor": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "fs-extra": "~7.0.1", - "typescript": "~5.0.4" + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" } } diff --git a/build-tests/api-extractor-test-01/src/index.ts b/build-tests/api-extractor-test-01/src/index.ts index d4c2dcb4338..7f45cb7eb03 100644 --- a/build-tests/api-extractor-test-01/src/index.ts +++ b/build-tests/api-extractor-test-01/src/index.ts @@ -113,7 +113,7 @@ export { ForgottenExportConsumer1 } from './ForgottenExportConsumer1'; export { ForgottenExportConsumer2 } from './ForgottenExportConsumer2'; export { ForgottenExportConsumer3 } from './ForgottenExportConsumer3'; -export { default as IInterfaceAsDefaultExport } from './IInterfaceAsDefaultExport'; +export type { default as IInterfaceAsDefaultExport } from './IInterfaceAsDefaultExport'; /** * Test the alias-following logic: This class gets aliased twice before being diff --git a/build-tests/api-extractor-test-01/tsconfig.json b/build-tests/api-extractor-test-01/tsconfig.json index a48293c1057..b4f9d8b2945 100644 --- a/build-tests/api-extractor-test-01/tsconfig.json +++ b/build-tests/api-extractor-test-01/tsconfig.json @@ -1,17 +1,7 @@ { + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "target": "es6", - "forceConsistentCasingInFileNames": true, - "module": "commonjs", - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "esModuleInterop": true, - "types": ["node", "jest"], - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "outDir": "lib" + "strictPropertyInitialization": false }, "include": ["src/**/*.ts", "typings/tsd.d.ts"] } diff --git a/build-tests/api-extractor-test-02/build.js b/build-tests/api-extractor-test-02/build.js deleted file mode 100644 index 6378977fc0b..00000000000 --- a/build-tests/api-extractor-test-02/build.js +++ /dev/null @@ -1,27 +0,0 @@ -const fsx = require('fs-extra'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -fsx.emptyDirSync('dist'); -fsx.emptyDirSync('lib'); -fsx.emptyDirSync('temp'); - -// Run the TypeScript compiler -executeCommand('node node_modules/typescript/lib/tsc'); - -// Run the API Extractor command-line -if (process.argv.indexOf('--production') >= 0) { - executeCommand('node node_modules/@microsoft/api-extractor/lib/start run'); -} else { - executeCommand('node node_modules/@microsoft/api-extractor/lib/start run --local'); -} - -console.log(`==> Finished build.js for ${path.basename(process.cwd())}`); diff --git a/build-tests/api-extractor-test-02/config/api-extractor.json b/build-tests/api-extractor-test-02/config/api-extractor.json index e4725c5edc7..7b0adce7331 100644 --- a/build-tests/api-extractor-test-02/config/api-extractor.json +++ b/build-tests/api-extractor-test-02/config/api-extractor.json @@ -1,7 +1,7 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - "mainEntryPointFilePath": "/lib/index.d.ts", + "mainEntryPointFilePath": "/lib-dts/index.d.ts", "apiReport": { "enabled": true diff --git a/build-tests/api-extractor-test-02/config/rig.json b/build-tests/api-extractor-test-02/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-extractor-test-02/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-extractor-test-02/config/rush-project.json b/build-tests/api-extractor-test-02/config/rush-project.json index 317ec878bc3..6c75ba02c45 100644 --- a/build-tests/api-extractor-test-02/config/rush-project.json +++ b/build-tests/api-extractor-test-02/config/rush-project.json @@ -1,8 +1,11 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib"] + "operationName": "_phase:build", + // dist is intentionally tracked + "outputFolderNames": ["lib-commonjs", "lib-esm", "lib-dts"] } ] } diff --git a/build-tests/api-extractor-test-02/dist/api-extractor-test-02-alpha.d.ts b/build-tests/api-extractor-test-02/dist/api-extractor-test-02-alpha.d.ts index 66198cfe58d..a07f2b9459b 100644 --- a/build-tests/api-extractor-test-02/dist/api-extractor-test-02-alpha.d.ts +++ b/build-tests/api-extractor-test-02/dist/api-extractor-test-02-alpha.d.ts @@ -7,6 +7,8 @@ * @packageDocumentation */ +/// + import { ISimpleInterface } from 'api-extractor-test-01'; import { ReexportedClass as RenamedReexportedClass3 } from 'api-extractor-test-01'; import * as semver1 from 'semver'; diff --git a/build-tests/api-extractor-test-02/dist/api-extractor-test-02-beta.d.ts b/build-tests/api-extractor-test-02/dist/api-extractor-test-02-beta.d.ts index 66198cfe58d..a07f2b9459b 100644 --- a/build-tests/api-extractor-test-02/dist/api-extractor-test-02-beta.d.ts +++ b/build-tests/api-extractor-test-02/dist/api-extractor-test-02-beta.d.ts @@ -7,6 +7,8 @@ * @packageDocumentation */ +/// + import { ISimpleInterface } from 'api-extractor-test-01'; import { ReexportedClass as RenamedReexportedClass3 } from 'api-extractor-test-01'; import * as semver1 from 'semver'; diff --git a/build-tests/api-extractor-test-02/dist/api-extractor-test-02-public.d.ts b/build-tests/api-extractor-test-02/dist/api-extractor-test-02-public.d.ts index 66198cfe58d..a07f2b9459b 100644 --- a/build-tests/api-extractor-test-02/dist/api-extractor-test-02-public.d.ts +++ b/build-tests/api-extractor-test-02/dist/api-extractor-test-02-public.d.ts @@ -7,6 +7,8 @@ * @packageDocumentation */ +/// + import { ISimpleInterface } from 'api-extractor-test-01'; import { ReexportedClass as RenamedReexportedClass3 } from 'api-extractor-test-01'; import * as semver1 from 'semver'; diff --git a/build-tests/api-extractor-test-02/dist/api-extractor-test-02.d.ts b/build-tests/api-extractor-test-02/dist/api-extractor-test-02.d.ts index 66198cfe58d..a07f2b9459b 100644 --- a/build-tests/api-extractor-test-02/dist/api-extractor-test-02.d.ts +++ b/build-tests/api-extractor-test-02/dist/api-extractor-test-02.d.ts @@ -7,6 +7,8 @@ * @packageDocumentation */ +/// + import { ISimpleInterface } from 'api-extractor-test-01'; import { ReexportedClass as RenamedReexportedClass3 } from 'api-extractor-test-01'; import * as semver1 from 'semver'; diff --git a/build-tests/api-extractor-test-02/etc/api-extractor-test-02.api.md b/build-tests/api-extractor-test-02/etc/api-extractor-test-02.api.md index 73cf2593182..14b9d3ba205 100644 --- a/build-tests/api-extractor-test-02/etc/api-extractor-test-02.api.md +++ b/build-tests/api-extractor-test-02/etc/api-extractor-test-02.api.md @@ -4,6 +4,8 @@ ```ts +/// + import { ISimpleInterface } from 'api-extractor-test-01'; import { ReexportedClass as RenamedReexportedClass3 } from 'api-extractor-test-01'; import * as semver1 from 'semver'; @@ -38,5 +40,4 @@ export class SubclassWithImport extends RenamedReexportedClass3 implements ISimp test(): void; } - ``` diff --git a/build-tests/api-extractor-test-02/package.json b/build-tests/api-extractor-test-02/package.json index 049ff0375b1..3d702adf843 100644 --- a/build-tests/api-extractor-test-02/package.json +++ b/build-tests/api-extractor-test-02/package.json @@ -3,21 +3,41 @@ "description": "Building this project is a regression test for api-extractor", "version": "1.0.0", "private": true, - "main": "lib/index.js", - "typings": "dist/api-extractor-test-02.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/api-extractor-test-02.d.ts", + "exports": { + ".": { + "types": "./dist/api-extractor-test-02.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "scripts": { - "build": "node build.js", - "_phase:build": "node build.js" + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" }, "dependencies": { - "@types/semver": "7.3.5", + "@types/long": "4.0.0", + "@types/semver": "7.7.1", "api-extractor-test-01": "workspace:*", - "semver": "~7.3.0" + "semver": "~7.7.4" }, "devDependencies": { - "@microsoft/api-extractor": "workspace:*", - "@types/node": "14.18.36", - "fs-extra": "~7.0.1", - "typescript": "~5.0.4" + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" } } diff --git a/build-tests/api-extractor-test-02/src/Ambient.ts b/build-tests/api-extractor-test-02/src/Ambient.ts new file mode 100644 index 00000000000..2ec6180c9e0 --- /dev/null +++ b/build-tests/api-extractor-test-02/src/Ambient.ts @@ -0,0 +1,7 @@ +import { AmbientConsumer } from 'api-extractor-test-01'; + +// Test that the ambient types are accessible even though api-extractor-02 doesn't +// import Jest +const x = new AmbientConsumer(); +const y = x.definitelyTyped(); +const z = y.results; diff --git a/build-tests/api-extractor-test-02/src/index.ts b/build-tests/api-extractor-test-02/src/index.ts index 3ef4f5e585c..b70d3bd1b78 100644 --- a/build-tests/api-extractor-test-02/src/index.ts +++ b/build-tests/api-extractor-test-02/src/index.ts @@ -2,6 +2,7 @@ // See LICENSE in the project root for license information. /// +/// /** * api-extractor-test-02 @@ -19,10 +20,4 @@ export { importDeduping1 } from './ImportDeduping1'; export { importDeduping2 } from './ImportDeduping2'; export { ReexportedClass as RenamedReexportedClass3 } from 'api-extractor-test-01'; -import { AmbientConsumer } from 'api-extractor-test-01'; - -// Test that the ambient types are accessible even though api-extractor-02 doesn't -// import Jest -const x = new AmbientConsumer(); -const y = x.definitelyTyped(); -const z = y.results; +export * from './Ambient'; diff --git a/build-tests/api-extractor-test-02/tsconfig.json b/build-tests/api-extractor-test-02/tsconfig.json index 0a13e0ef163..a4616cb045e 100644 --- a/build-tests/api-extractor-test-02/tsconfig.json +++ b/build-tests/api-extractor-test-02/tsconfig.json @@ -1,17 +1,4 @@ { - "compilerOptions": { - "target": "es6", - "forceConsistentCasingInFileNames": true, - "module": "commonjs", - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "esModuleInterop": true, - "types": ["node"], - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "outDir": "lib" - }, + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "include": ["src/**/*.ts", "typings/tsd.d.ts"] } diff --git a/build-tests/api-extractor-test-03/build.js b/build-tests/api-extractor-test-03/build.js deleted file mode 100644 index 4019f096262..00000000000 --- a/build-tests/api-extractor-test-03/build.js +++ /dev/null @@ -1,22 +0,0 @@ -const fsx = require('fs-extra'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -fsx.emptyDirSync('dist'); -fsx.emptyDirSync('lib'); -fsx.emptyDirSync('temp'); - -// Run the TypeScript compiler -executeCommand('node node_modules/typescript/lib/tsc'); - -// (NO API EXTRACTOR FOR THIS PROJECT) - -console.log(`==> Finished build.js for ${path.basename(process.cwd())}`); diff --git a/build-tests/api-extractor-test-03/config/rig.json b/build-tests/api-extractor-test-03/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-extractor-test-03/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-extractor-test-03/config/rush-project.json b/build-tests/api-extractor-test-03/config/rush-project.json deleted file mode 100644 index 247dc17187a..00000000000 --- a/build-tests/api-extractor-test-03/config/rush-project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "operationSettings": [ - { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] - } - ] -} diff --git a/build-tests/api-extractor-test-03/package.json b/build-tests/api-extractor-test-03/package.json index aecbd224d81..5d5589b7c1a 100644 --- a/build-tests/api-extractor-test-03/package.json +++ b/build-tests/api-extractor-test-03/package.json @@ -4,14 +4,37 @@ "version": "1.0.0", "private": true, "scripts": { - "build": "node build.js", - "_phase:build": "node build.js" + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" + }, + "dependencies": { + "api-extractor-test-02": "workspace:*" }, "devDependencies": { - "@types/jest": "29.2.5", - "@types/node": "14.18.36", - "api-extractor-test-02": "workspace:*", - "fs-extra": "~7.0.1", - "typescript": "~5.0.4" + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" + }, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } } } diff --git a/build-tests/api-extractor-test-03/tsconfig.json b/build-tests/api-extractor-test-03/tsconfig.json index a48293c1057..a4616cb045e 100644 --- a/build-tests/api-extractor-test-03/tsconfig.json +++ b/build-tests/api-extractor-test-03/tsconfig.json @@ -1,17 +1,4 @@ { - "compilerOptions": { - "target": "es6", - "forceConsistentCasingInFileNames": true, - "module": "commonjs", - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "esModuleInterop": true, - "types": ["node", "jest"], - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "outDir": "lib" - }, + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "include": ["src/**/*.ts", "typings/tsd.d.ts"] } diff --git a/build-tests/api-extractor-test-04/beta-consumer/tsconfig.json b/build-tests/api-extractor-test-04/beta-consumer/tsconfig.json index 805bac5b2e8..7f2efa812b8 100644 --- a/build-tests/api-extractor-test-04/beta-consumer/tsconfig.json +++ b/build-tests/api-extractor-test-04/beta-consumer/tsconfig.json @@ -8,8 +8,8 @@ "declarationMap": true, "experimentalDecorators": true, "strictNullChecks": true, - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "outDir": "lib" + "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable"], + "outDir": "lib-commonjs" }, "include": ["src/**/*.ts", "typings/tsd.d.ts"] } diff --git a/build-tests/api-extractor-test-04/build.js b/build-tests/api-extractor-test-04/build.js deleted file mode 100644 index cf12d4f13e6..00000000000 --- a/build-tests/api-extractor-test-04/build.js +++ /dev/null @@ -1,36 +0,0 @@ -const fsx = require('../api-extractor-test-04/node_modules/fs-extra'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -console.log(`==> Invoking tsc in the "beta-consumer" folder`); - -function executeCommand(command, cwd) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit', cwd: cwd }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -fsx.emptyDirSync('dist'); -fsx.emptyDirSync('lib'); -fsx.emptyDirSync('temp'); - -// Run the TypeScript compiler -executeCommand('node node_modules/typescript/lib/tsc'); - -// Run the API Extractor command-line -if (process.argv.indexOf('--production') >= 0) { - executeCommand('node node_modules/@microsoft/api-extractor/lib/start run'); -} else { - executeCommand('node node_modules/@microsoft/api-extractor/lib/start run --local'); -} - -// Run the TypeScript compiler in the beta-consumer folder -console.log(`==> Invoking tsc in the "beta-consumer" folder`); - -fsx.emptyDirSync('beta-consumer/lib'); -const tscPath = path.resolve('node_modules/typescript/lib/tsc'); -executeCommand(`node ${tscPath}`, 'beta-consumer'); - -console.log(`==> Finished build.js for ${path.basename(process.cwd())}`); diff --git a/build-tests/api-extractor-test-04/config/api-extractor.json b/build-tests/api-extractor-test-04/config/api-extractor.json index e4725c5edc7..7b0adce7331 100644 --- a/build-tests/api-extractor-test-04/config/api-extractor.json +++ b/build-tests/api-extractor-test-04/config/api-extractor.json @@ -1,7 +1,7 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - "mainEntryPointFilePath": "/lib/index.d.ts", + "mainEntryPointFilePath": "/lib-dts/index.d.ts", "apiReport": { "enabled": true diff --git a/build-tests/api-extractor-test-04/config/rig.json b/build-tests/api-extractor-test-04/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-extractor-test-04/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-extractor-test-04/config/rush-project.json b/build-tests/api-extractor-test-04/config/rush-project.json index 247dc17187a..6c75ba02c45 100644 --- a/build-tests/api-extractor-test-04/config/rush-project.json +++ b/build-tests/api-extractor-test-04/config/rush-project.json @@ -1,8 +1,11 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + // dist is intentionally tracked + "outputFolderNames": ["lib-commonjs", "lib-esm", "lib-dts"] } ] } diff --git a/build-tests/api-extractor-test-04/dist/api-extractor-test-04-beta.d.ts b/build-tests/api-extractor-test-04/dist/api-extractor-test-04-beta.d.ts index 9b9be28f436..449c8aedea6 100644 --- a/build-tests/api-extractor-test-04/dist/api-extractor-test-04-beta.d.ts +++ b/build-tests/api-extractor-test-04/dist/api-extractor-test-04-beta.d.ts @@ -6,6 +6,8 @@ * @packageDocumentation */ +import { Lib1Interface } from 'api-extractor-lib1-test'; + /* Excluded from this release type: AlphaClass */ /** diff --git a/build-tests/api-extractor-test-04/dist/api-extractor-test-04-public.d.ts b/build-tests/api-extractor-test-04/dist/api-extractor-test-04-public.d.ts index d146365e6bd..3f31586f792 100644 --- a/build-tests/api-extractor-test-04/dist/api-extractor-test-04-public.d.ts +++ b/build-tests/api-extractor-test-04/dist/api-extractor-test-04-public.d.ts @@ -6,6 +6,8 @@ * @packageDocumentation */ +import { Lib1Interface } from 'api-extractor-lib1-test'; + /* Excluded from this release type: AlphaClass */ /* Excluded from this release type: BetaClass */ diff --git a/build-tests/api-extractor-test-04/etc/api-extractor-test-04.api.md b/build-tests/api-extractor-test-04/etc/api-extractor-test-04.api.md index bb2463f5c67..b6f80fc8894 100644 --- a/build-tests/api-extractor-test-04/etc/api-extractor-test-04.api.md +++ b/build-tests/api-extractor-test-04/etc/api-extractor-test-04.api.md @@ -110,5 +110,4 @@ export enum RegularEnum { // @beta export const variableDeclaration: string; - ``` diff --git a/build-tests/api-extractor-test-04/package.json b/build-tests/api-extractor-test-04/package.json index 680dc57dd49..130becfe2db 100644 --- a/build-tests/api-extractor-test-04/package.json +++ b/build-tests/api-extractor-test-04/package.json @@ -3,16 +3,38 @@ "description": "Building this project is a regression test for api-extractor", "version": "1.0.0", "private": true, - "main": "lib/index.js", - "typings": "dist/api-extractor-test-04.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/api-extractor-test-04.d.ts", + "exports": { + ".": { + "types": "./dist/api-extractor-test-04.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "scripts": { - "build": "node build.js", - "_phase:build": "node build.js" + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" }, "dependencies": { - "@microsoft/api-extractor": "workspace:*", - "api-extractor-lib1-test": "workspace:*", - "fs-extra": "~7.0.1", - "typescript": "~5.0.4" + "api-extractor-lib1-test": "workspace:*" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" } } diff --git a/build-tests/api-extractor-test-04/src/index.ts b/build-tests/api-extractor-test-04/src/index.ts index dbb54cf8031..29d8a6df352 100644 --- a/build-tests/api-extractor-test-04/src/index.ts +++ b/build-tests/api-extractor-test-04/src/index.ts @@ -11,13 +11,13 @@ export { AlphaClass } from './AlphaClass'; export { BetaClass } from './BetaClass'; -export { PublicClass, IPublicClassInternalParameters } from './PublicClass'; +export { PublicClass, type IPublicClassInternalParameters } from './PublicClass'; export { InternalClass } from './InternalClass'; export { EntangledNamespace } from './EntangledNamespace'; export * from './EnumExamples'; -export { BetaInterface } from './BetaInterface'; +export type { BetaInterface } from './BetaInterface'; /** * This is a module-scoped variable. @@ -33,6 +33,6 @@ import { AlphaClass } from './AlphaClass'; */ export type ExportedAlias = AlphaClass; -export { IPublicComplexInterface } from './IPublicComplexInterface'; +export type { IPublicComplexInterface } from './IPublicComplexInterface'; -export { Lib1Interface } from 'api-extractor-lib1-test'; +export type { Lib1Interface } from 'api-extractor-lib1-test'; diff --git a/build-tests/api-extractor-test-04/tsconfig.json b/build-tests/api-extractor-test-04/tsconfig.json index 7e2cba55d35..86dcc8c8f9e 100644 --- a/build-tests/api-extractor-test-04/tsconfig.json +++ b/build-tests/api-extractor-test-04/tsconfig.json @@ -1,17 +1,8 @@ { + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "target": "es6", - "forceConsistentCasingInFileNames": true, - "module": "commonjs", - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "esModuleInterop": true, - "types": [], - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "outDir": "lib" + "strictPropertyInitialization": false, + "noImplicitAny": false }, "include": ["src/**/*.ts", "typings/tsd.d.ts"] } diff --git a/build-tests/api-extractor-test-05/.gitignore b/build-tests/api-extractor-test-05/.gitignore new file mode 100644 index 00000000000..2ea2efde372 --- /dev/null +++ b/build-tests/api-extractor-test-05/.gitignore @@ -0,0 +1,5 @@ +# This project's outputs are tracked to surface changes to API Extractor rollups during PRs +!dist +dist/* +!dist/*.d.ts +!dist/*.json diff --git a/build-tests/api-extractor-test-05/config/api-extractor.json b/build-tests/api-extractor-test-05/config/api-extractor.json new file mode 100644 index 00000000000..26e66af1b08 --- /dev/null +++ b/build-tests/api-extractor-test-05/config/api-extractor.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + "mainEntryPointFilePath": "/lib-dts/index.d.ts", + + "apiReport": { + "enabled": true + }, + + "docModel": { + "enabled": true, + "apiJsonFilePath": "/dist/api-extractor-test-05.api.json", + "releaseTagsToTrim": ["@internal", "@alpha"] + }, + + "dtsRollup": { + "enabled": true, + + "alphaTrimmedFilePath": "/dist/-alpha.d.ts", + "betaTrimmedFilePath": "/dist/-beta.d.ts", + "publicTrimmedFilePath": "/dist/-public.d.ts" + }, + + "testMode": true +} diff --git a/build-tests/api-extractor-test-05/config/rig.json b/build-tests/api-extractor-test-05/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/api-extractor-test-05/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/api-extractor-test-05/config/rush-project.json b/build-tests/api-extractor-test-05/config/rush-project.json new file mode 100644 index 00000000000..6c75ba02c45 --- /dev/null +++ b/build-tests/api-extractor-test-05/config/rush-project.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + // dist is intentionally tracked + "outputFolderNames": ["lib-commonjs", "lib-esm", "lib-dts"] + } + ] +} diff --git a/build-tests/api-extractor-test-05/dist/api-extractor-test-05-alpha.d.ts b/build-tests/api-extractor-test-05/dist/api-extractor-test-05-alpha.d.ts new file mode 100644 index 00000000000..a9df2f8369e --- /dev/null +++ b/build-tests/api-extractor-test-05/dist/api-extractor-test-05-alpha.d.ts @@ -0,0 +1,26 @@ +/** + * api-extractor-test-05 + * + * Test trimming of @internal and @alpha from doc model. + * + * @packageDocumentation + */ + +/** + * @alpha + */ +export declare const alpha: string; + +/** + * @beta + */ +export declare const beta: string; + +/* Excluded from this release type: _internal */ + +/** + * @public + */ +export declare const publick: string; + +export { } diff --git a/build-tests/api-extractor-test-05/dist/api-extractor-test-05-beta.d.ts b/build-tests/api-extractor-test-05/dist/api-extractor-test-05-beta.d.ts new file mode 100644 index 00000000000..0a5457a1530 --- /dev/null +++ b/build-tests/api-extractor-test-05/dist/api-extractor-test-05-beta.d.ts @@ -0,0 +1,23 @@ +/** + * api-extractor-test-05 + * + * Test trimming of @internal and @alpha from doc model. + * + * @packageDocumentation + */ + +/* Excluded from this release type: alpha */ + +/** + * @beta + */ +export declare const beta: string; + +/* Excluded from this release type: _internal */ + +/** + * @public + */ +export declare const publick: string; + +export { } diff --git a/build-tests/api-extractor-test-05/dist/api-extractor-test-05-public.d.ts b/build-tests/api-extractor-test-05/dist/api-extractor-test-05-public.d.ts new file mode 100644 index 00000000000..b0585edaed5 --- /dev/null +++ b/build-tests/api-extractor-test-05/dist/api-extractor-test-05-public.d.ts @@ -0,0 +1,20 @@ +/** + * api-extractor-test-05 + * + * Test trimming of @internal and @alpha from doc model. + * + * @packageDocumentation + */ + +/* Excluded from this release type: alpha */ + +/* Excluded from this release type: beta */ + +/* Excluded from this release type: _internal */ + +/** + * @public + */ +export declare const publick: string; + +export { } diff --git a/build-tests/api-extractor-test-05/dist/api-extractor-test-05.api.json b/build-tests/api-extractor-test-05/dist/api-extractor-test-05.api.json new file mode 100644 index 00000000000..3184c889da4 --- /dev/null +++ b/build-tests/api-extractor-test-05/dist/api-extractor-test-05.api.json @@ -0,0 +1,240 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1011, + "oldestForwardsCompatibleVersion": 1001, + "tsdocConfig": { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "noStandardTags": true, + "tagDefinitions": [ + { + "tagName": "@alpha", + "syntaxKind": "modifier" + }, + { + "tagName": "@beta", + "syntaxKind": "modifier" + }, + { + "tagName": "@defaultValue", + "syntaxKind": "block" + }, + { + "tagName": "@decorator", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@deprecated", + "syntaxKind": "block" + }, + { + "tagName": "@eventProperty", + "syntaxKind": "modifier" + }, + { + "tagName": "@example", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@experimental", + "syntaxKind": "modifier" + }, + { + "tagName": "@inheritDoc", + "syntaxKind": "inline" + }, + { + "tagName": "@internal", + "syntaxKind": "modifier" + }, + { + "tagName": "@label", + "syntaxKind": "inline" + }, + { + "tagName": "@link", + "syntaxKind": "inline", + "allowMultiple": true + }, + { + "tagName": "@override", + "syntaxKind": "modifier" + }, + { + "tagName": "@packageDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@param", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@privateRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@public", + "syntaxKind": "modifier" + }, + { + "tagName": "@readonly", + "syntaxKind": "modifier" + }, + { + "tagName": "@remarks", + "syntaxKind": "block" + }, + { + "tagName": "@returns", + "syntaxKind": "block" + }, + { + "tagName": "@sealed", + "syntaxKind": "modifier" + }, + { + "tagName": "@see", + "syntaxKind": "block" + }, + { + "tagName": "@throws", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@typeParam", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@virtual", + "syntaxKind": "modifier" + }, + { + "tagName": "@jsx", + "syntaxKind": "block" + }, + { + "tagName": "@jsxRuntime", + "syntaxKind": "block" + }, + { + "tagName": "@jsxFrag", + "syntaxKind": "block" + }, + { + "tagName": "@jsxImportSource", + "syntaxKind": "block" + }, + { + "tagName": "@betaDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@internalRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@preapproved", + "syntaxKind": "modifier" + } + ], + "supportForTags": { + "@alpha": true, + "@beta": true, + "@defaultValue": true, + "@decorator": true, + "@deprecated": true, + "@eventProperty": true, + "@example": true, + "@experimental": true, + "@inheritDoc": true, + "@internal": true, + "@label": true, + "@link": true, + "@override": true, + "@packageDocumentation": true, + "@param": true, + "@privateRemarks": true, + "@public": true, + "@readonly": true, + "@remarks": true, + "@returns": true, + "@sealed": true, + "@see": true, + "@throws": true, + "@typeParam": true, + "@virtual": true, + "@betaDocumentation": true, + "@internalRemarks": true, + "@preapproved": true + }, + "reportUnsupportedHtmlElements": false + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-test-05!", + "docComment": "/**\n * api-extractor-test-05\n *\n * Test trimming of and from doc model.\n *\n * @internal @alpha @packageDocumentation\n */\n", + "name": "api-extractor-test-05", + "preserveMemberOrder": false, + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-test-05!", + "name": "", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Variable", + "canonicalReference": "api-extractor-test-05!beta:var", + "docComment": "/**\n * @beta\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "beta: " + }, + { + "kind": "Content", + "text": "string" + } + ], + "fileUrlPath": "src/index.ts", + "isReadonly": true, + "releaseTag": "Beta", + "name": "beta", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "Variable", + "canonicalReference": "api-extractor-test-05!publick:var", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "publick: " + }, + { + "kind": "Content", + "text": "string" + } + ], + "fileUrlPath": "src/index.ts", + "isReadonly": true, + "releaseTag": "Public", + "name": "publick", + "variableTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + } + ] + } + ] +} diff --git a/build-tests/api-extractor-test-05/dist/api-extractor-test-05.d.ts b/build-tests/api-extractor-test-05/dist/api-extractor-test-05.d.ts new file mode 100644 index 00000000000..b097af23aad --- /dev/null +++ b/build-tests/api-extractor-test-05/dist/api-extractor-test-05.d.ts @@ -0,0 +1,29 @@ +/** + * api-extractor-test-05 + * + * Test trimming of @internal and @alpha from doc model. + * + * @packageDocumentation + */ + +/** + * @alpha + */ +export declare const alpha: string; + +/** + * @beta + */ +export declare const beta: string; + +/** + * @internal + */ +export declare const _internal: string; + +/** + * @public + */ +export declare const publick: string; + +export { } diff --git a/build-tests/api-extractor-test-05/dist/tsdoc-metadata.json b/build-tests/api-extractor-test-05/dist/tsdoc-metadata.json new file mode 100644 index 00000000000..ccb284c3209 --- /dev/null +++ b/build-tests/api-extractor-test-05/dist/tsdoc-metadata.json @@ -0,0 +1,11 @@ +// This file is read by tools that parse documentation comments conforming to the TSDoc standard. +// It should be published with your NPM package. It should not be tracked by Git. +{ + "tsdocVersion": "0.12", + "toolPackages": [ + { + "packageName": "@microsoft/api-extractor", + "packageVersion": "7.58.11" + } + ] +} diff --git a/build-tests/api-extractor-test-05/etc/api-extractor-test-05.api.md b/build-tests/api-extractor-test-05/etc/api-extractor-test-05.api.md new file mode 100644 index 00000000000..a96a754474e --- /dev/null +++ b/build-tests/api-extractor-test-05/etc/api-extractor-test-05.api.md @@ -0,0 +1,19 @@ +## API Report File for "api-extractor-test-05" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @alpha (undocumented) +export const alpha: string; + +// @beta (undocumented) +export const beta: string; + +// @internal (undocumented) +export const _internal: string; + +// @public (undocumented) +export const publick: string; + +``` diff --git a/build-tests/api-extractor-test-05/package.json b/build-tests/api-extractor-test-05/package.json new file mode 100644 index 00000000000..430eb5728f1 --- /dev/null +++ b/build-tests/api-extractor-test-05/package.json @@ -0,0 +1,37 @@ +{ + "name": "api-extractor-test-05", + "description": "Building this project is a regression test for api-extractor", + "version": "1.0.0", + "private": true, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/api-extractor-test-05.d.ts", + "exports": { + ".": { + "types": "./dist/api-extractor-test-05.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" + }, + "dependencies": { + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" + } +} diff --git a/build-tests/api-extractor-test-05/src/index.ts b/build-tests/api-extractor-test-05/src/index.ts new file mode 100644 index 00000000000..3bcbd162140 --- /dev/null +++ b/build-tests/api-extractor-test-05/src/index.ts @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * api-extractor-test-05 + * + * Test trimming of @internal and @alpha from doc model. + * + * @packageDocumentation + */ + +/** + * @internal + */ +export const _internal: string = 'internal'; + +/** + * @alpha + */ +export const alpha: string = 'alpha'; + +/** + * @beta + */ +export const beta: string = 'beta'; + +/** + * @public + */ +export const publick: string = 'public'; diff --git a/build-tests/api-extractor-test-05/tsconfig.json b/build-tests/api-extractor-test-05/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/build-tests/api-extractor-test-05/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/build-tests/eslint-7-11-test/.eslintrc.js b/build-tests/eslint-7-11-test/.eslintrc.js new file mode 100644 index 00000000000..00f91351897 --- /dev/null +++ b/build-tests/eslint-7-11-test/.eslintrc.js @@ -0,0 +1,27 @@ +// This is a workaround for https://github.com/eslint/eslint/issues/3458 +require('@rushstack/eslint-config/patch/modern-module-resolution'); +// This is a workaround for https://github.com/microsoft/rushstack/issues/3021 +require('@rushstack/eslint-config/patch/custom-config-package-names'); + +module.exports = { + extends: [ + '@rushstack/eslint-config/profile/node-trusted-tool', + '@rushstack/eslint-config/mixins/friendly-locals' + ], + parserOptions: { tsconfigRootDir: __dirname }, + + overrides: [ + /** + * Override the parser from local-eslint-config. Since the config is coming + * from the workspace instead of the external NPM package, the versions of ESLint + * and TypeScript that the config consumes will be resolved from the devDependencies + * of the config instead of from the eslint-7-test package. Overriding the parser + * ensures that the these dependencies come from the eslint-7-test package. See: + * https://github.com/microsoft/rushstack/issues/3021 + */ + { + files: ['*.ts', '*.tsx'], + parser: '@typescript-eslint/parser' + } + ] +}; diff --git a/build-tests/eslint-7-11-test/README.md b/build-tests/eslint-7-11-test/README.md new file mode 100644 index 00000000000..7e236873623 --- /dev/null +++ b/build-tests/eslint-7-11-test/README.md @@ -0,0 +1,6 @@ +# eslint-7-11-test + +This project folder is one of the **build-tests** for the Rushstack [ESLint configuration](https://www.npmjs.com/package/@rushstack/eslint-config) (and by extension, the [ESLint plugin](https://www.npmjs.com/package/@rushstack/eslint-plugin)) +package. This project builds using ESLint v7.11.0 and contains a simple index file to ensure that the build runs ESLint successfully against source code. + +Please see the [ESLint Heft task documentation](https://rushstack.io/pages/heft_tasks/eslint/) for documentation and tutorials. diff --git a/build-tests/eslint-7-11-test/config/heft.json b/build-tests/eslint-7-11-test/config/heft.json new file mode 100644 index 00000000000..e7cc05b1da4 --- /dev/null +++ b/build-tests/eslint-7-11-test/config/heft.json @@ -0,0 +1,16 @@ +{ + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "lint": { + "taskPlugin": { + // Clear the SARIF emitter + "options": null + } + } + } + } + } +} diff --git a/build-tests/eslint-7-11-test/config/rig.json b/build-tests/eslint-7-11-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/eslint-7-11-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/eslint-7-11-test/package.json b/build-tests/eslint-7-11-test/package.json new file mode 100644 index 00000000000..236aaa2169d --- /dev/null +++ b/build-tests/eslint-7-11-test/package.json @@ -0,0 +1,43 @@ +{ + "name": "eslint-7-11-test", + "description": "This project contains a build test to validate ESLint 7.11.0 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin)", + "version": "1.0.0", + "private": true, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" + }, + "devDependencies": { + "@rushstack/eslint-config": "3.7.1", + "@rushstack/heft": "workspace:*", + "@types/node": "20.17.19", + "@typescript-eslint/parser": "~6.19.0", + "eslint": "7.11.0", + "local-node-rig": "workspace:*", + "typescript": "~5.8.2" + } +} diff --git a/build-tests/eslint-7-11-test/src/index.ts b/build-tests/eslint-7-11-test/src/index.ts new file mode 100644 index 00000000000..428f8caba4f --- /dev/null +++ b/build-tests/eslint-7-11-test/src/index.ts @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export class Foo { + private _bar: string = 'bar'; + public baz: string = this._bar; +} diff --git a/build-tests/eslint-7-11-test/tsconfig.json b/build-tests/eslint-7-11-test/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/build-tests/eslint-7-11-test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/build-tests/eslint-7-7-test/.eslintrc.js b/build-tests/eslint-7-7-test/.eslintrc.js new file mode 100644 index 00000000000..00f91351897 --- /dev/null +++ b/build-tests/eslint-7-7-test/.eslintrc.js @@ -0,0 +1,27 @@ +// This is a workaround for https://github.com/eslint/eslint/issues/3458 +require('@rushstack/eslint-config/patch/modern-module-resolution'); +// This is a workaround for https://github.com/microsoft/rushstack/issues/3021 +require('@rushstack/eslint-config/patch/custom-config-package-names'); + +module.exports = { + extends: [ + '@rushstack/eslint-config/profile/node-trusted-tool', + '@rushstack/eslint-config/mixins/friendly-locals' + ], + parserOptions: { tsconfigRootDir: __dirname }, + + overrides: [ + /** + * Override the parser from local-eslint-config. Since the config is coming + * from the workspace instead of the external NPM package, the versions of ESLint + * and TypeScript that the config consumes will be resolved from the devDependencies + * of the config instead of from the eslint-7-test package. Overriding the parser + * ensures that the these dependencies come from the eslint-7-test package. See: + * https://github.com/microsoft/rushstack/issues/3021 + */ + { + files: ['*.ts', '*.tsx'], + parser: '@typescript-eslint/parser' + } + ] +}; diff --git a/build-tests/eslint-7-7-test/README.md b/build-tests/eslint-7-7-test/README.md new file mode 100644 index 00000000000..6002dff592a --- /dev/null +++ b/build-tests/eslint-7-7-test/README.md @@ -0,0 +1,6 @@ +# eslint-7-7-test + +This project folder is one of the **build-tests** for the Rushstack [ESLint configuration](https://www.npmjs.com/package/@rushstack/eslint-config) (and by extension, the [ESLint plugin](https://www.npmjs.com/package/@rushstack/eslint-plugin)) +package. This project builds using ESLint v7.7.0 and contains a simple index file to ensure that the build runs ESLint successfully against source code. + +Please see the [ESLint Heft task documentation](https://rushstack.io/pages/heft_tasks/eslint/) for documentation and tutorials. diff --git a/build-tests/eslint-7-7-test/config/heft.json b/build-tests/eslint-7-7-test/config/heft.json new file mode 100644 index 00000000000..e7cc05b1da4 --- /dev/null +++ b/build-tests/eslint-7-7-test/config/heft.json @@ -0,0 +1,16 @@ +{ + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "lint": { + "taskPlugin": { + // Clear the SARIF emitter + "options": null + } + } + } + } + } +} diff --git a/build-tests/eslint-7-7-test/config/rig.json b/build-tests/eslint-7-7-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/eslint-7-7-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/eslint-7-7-test/package.json b/build-tests/eslint-7-7-test/package.json new file mode 100644 index 00000000000..33b4a44737c --- /dev/null +++ b/build-tests/eslint-7-7-test/package.json @@ -0,0 +1,43 @@ +{ + "name": "eslint-7-7-test", + "description": "This project contains a build test to validate ESLint 7.7.0 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin)", + "version": "1.0.0", + "private": true, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" + }, + "devDependencies": { + "@rushstack/eslint-config": "3.7.1", + "@rushstack/heft": "workspace:*", + "@types/node": "20.17.19", + "@typescript-eslint/parser": "~6.19.0", + "eslint": "7.7.0", + "local-node-rig": "workspace:*", + "typescript": "~5.8.2" + } +} diff --git a/build-tests/eslint-7-7-test/src/index.ts b/build-tests/eslint-7-7-test/src/index.ts new file mode 100644 index 00000000000..428f8caba4f --- /dev/null +++ b/build-tests/eslint-7-7-test/src/index.ts @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export class Foo { + private _bar: string = 'bar'; + public baz: string = this._bar; +} diff --git a/build-tests/eslint-7-7-test/tsconfig.json b/build-tests/eslint-7-7-test/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/build-tests/eslint-7-7-test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/build-tests/eslint-7-test/.eslintrc.js b/build-tests/eslint-7-test/.eslintrc.js index 9a6c31a97f4..00f91351897 100644 --- a/build-tests/eslint-7-test/.eslintrc.js +++ b/build-tests/eslint-7-test/.eslintrc.js @@ -1,5 +1,7 @@ // This is a workaround for https://github.com/eslint/eslint/issues/3458 require('@rushstack/eslint-config/patch/modern-module-resolution'); +// This is a workaround for https://github.com/microsoft/rushstack/issues/3021 +require('@rushstack/eslint-config/patch/custom-config-package-names'); module.exports = { extends: [ @@ -10,7 +12,7 @@ module.exports = { overrides: [ /** - * Override the parser from @rushstack/eslint-config. Since the config is coming + * Override the parser from local-eslint-config. Since the config is coming * from the workspace instead of the external NPM package, the versions of ESLint * and TypeScript that the config consumes will be resolved from the devDependencies * of the config instead of from the eslint-7-test package. Overriding the parser diff --git a/build-tests/eslint-7-test/config/rig.json b/build-tests/eslint-7-test/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/build-tests/eslint-7-test/config/rig.json +++ b/build-tests/eslint-7-test/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/build-tests/eslint-7-test/package.json b/build-tests/eslint-7-test/package.json index 23ef008a9c7..aebf9a26cac 100644 --- a/build-tests/eslint-7-test/package.json +++ b/build-tests/eslint-7-test/package.json @@ -3,19 +3,41 @@ "description": "This project contains a build test to validate ESLint 7 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin)", "version": "1.0.0", "private": true, - "main": "lib/index.js", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "license": "MIT", "scripts": { "build": "heft build --clean", "_phase:build": "heft run --only build -- --clean" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", + "@rushstack/eslint-config": "3.7.1", "@rushstack/heft": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", - "@types/node": "14.18.36", - "@typescript-eslint/parser": "~5.59.2", + "@types/node": "20.17.19", + "@typescript-eslint/parser": "~6.19.0", "eslint": "~7.30.0", - "typescript": "~5.0.4" + "local-node-rig": "workspace:*", + "typescript": "~5.8.2" } } diff --git a/build-tests/eslint-7-test/tsconfig.json b/build-tests/eslint-7-test/tsconfig.json index 8a46ac2445e..dac21d04081 100644 --- a/build-tests/eslint-7-test/tsconfig.json +++ b/build-tests/eslint-7-test/tsconfig.json @@ -1,24 +1,3 @@ { - "$schema": "http://json.schemastore.org/tsconfig", - - "compilerOptions": { - "outDir": "lib", - "rootDir": "src", - - "forceConsistentCasingInFileNames": true, - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "noUnusedLocals": true, - - "module": "esnext", - "moduleResolution": "node", - "target": "es5", - "lib": ["es5"] - }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/build-tests/eslint-8-test/.eslintrc.js b/build-tests/eslint-8-test/.eslintrc.js new file mode 100644 index 00000000000..177da749b07 --- /dev/null +++ b/build-tests/eslint-8-test/.eslintrc.js @@ -0,0 +1,27 @@ +// This is a workaround for https://github.com/eslint/eslint/issues/3458 +require('@rushstack/eslint-config/patch/modern-module-resolution'); +// This is a workaround for https://github.com/microsoft/rushstack/issues/3021 +require('@rushstack/eslint-config/patch/custom-config-package-names'); + +module.exports = { + extends: [ + '@rushstack/eslint-config/profile/node-trusted-tool', + '@rushstack/eslint-config/mixins/friendly-locals' + ], + parserOptions: { tsconfigRootDir: __dirname }, + + overrides: [ + /** + * Override the parser from @rushstack/eslint-config. Since the config is coming + * from the workspace instead of the external NPM package, the versions of ESLint + * and TypeScript that the config consumes will be resolved from the devDependencies + * of the config instead of from the eslint-8-test package. Overriding the parser + * ensures that the these dependencies come from the eslint-8-test package. See: + * https://github.com/microsoft/rushstack/issues/3021 + */ + { + files: ['*.ts', '*.tsx'], + parser: '@typescript-eslint/parser' + } + ] +}; diff --git a/build-tests/eslint-8-test/README.md b/build-tests/eslint-8-test/README.md new file mode 100644 index 00000000000..f4d85f1fdb3 --- /dev/null +++ b/build-tests/eslint-8-test/README.md @@ -0,0 +1,6 @@ +# eslint-7-test + +This project folder is one of the **build-tests** for the Rushstack [ESLint configuration](https://www.npmjs.com/package/@rushstack/eslint-config) (and by extension, the [ESLint plugin](https://www.npmjs.com/package/@rushstack/eslint-plugin)) +package. This project builds using ESLint v7 and contains a simple index file to ensure that the build runs ESLint successfully against source code. + +Please see the [ESLint Heft task documentation](https://rushstack.io/pages/heft_tasks/eslint/) for documentation and tutorials. diff --git a/build-tests/eslint-8-test/config/rig.json b/build-tests/eslint-8-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/eslint-8-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/eslint-8-test/package.json b/build-tests/eslint-8-test/package.json new file mode 100644 index 00000000000..b431562561c --- /dev/null +++ b/build-tests/eslint-8-test/package.json @@ -0,0 +1,43 @@ +{ + "name": "eslint-8-test", + "description": "This project contains a build test to validate ESLint 8 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin)", + "version": "1.0.0", + "private": true, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" + }, + "devDependencies": { + "@rushstack/eslint-config": "workspace:*", + "@rushstack/heft": "workspace:*", + "@types/node": "20.17.19", + "@typescript-eslint/parser": "~8.56.1", + "eslint": "~8.57.0", + "local-node-rig": "workspace:*", + "typescript": "~5.8.2" + } +} diff --git a/build-tests/eslint-8-test/src/index.ts b/build-tests/eslint-8-test/src/index.ts new file mode 100644 index 00000000000..428f8caba4f --- /dev/null +++ b/build-tests/eslint-8-test/src/index.ts @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export class Foo { + private _bar: string = 'bar'; + public baz: string = this._bar; +} diff --git a/build-tests/eslint-8-test/tsconfig.json b/build-tests/eslint-8-test/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/build-tests/eslint-8-test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/build-tests/eslint-9-test/.eslint-bulk-suppressions.json b/build-tests/eslint-9-test/.eslint-bulk-suppressions.json new file mode 100644 index 00000000000..961e6033858 --- /dev/null +++ b/build-tests/eslint-9-test/.eslint-bulk-suppressions.json @@ -0,0 +1,9 @@ +{ + "suppressions": [ + { + "file": "src/index.ts", + "scopeId": ".", + "rule": "@typescript-eslint/naming-convention" + } + ] +} diff --git a/build-tests/eslint-9-test/README.md b/build-tests/eslint-9-test/README.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/build-tests/eslint-9-test/config/rig.json b/build-tests/eslint-9-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/eslint-9-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/eslint-9-test/eslint.config.js b/build-tests/eslint-9-test/eslint.config.js new file mode 100644 index 00000000000..75eb0c727fc --- /dev/null +++ b/build-tests/eslint-9-test/eslint.config.js @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +require('local-node-rig/profiles/default/includes/eslint/flat/patch/eslint-bulk-suppressions'); +const typescriptEslintParser = require('@typescript-eslint/parser'); +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + /** + * Override the parser from @rushstack/eslint-config. Since the config is coming + * from the workspace instead of the external NPM package, the versions of ESLint + * and TypeScript that the config consumes will be resolved from the devDependencies + * of the config instead of from the eslint-8-test package. Overriding the parser + * ensures that the these dependencies come from the eslint-8-test package. See: + * https://github.com/microsoft/rushstack/issues/3021 + */ + parser: typescriptEslintParser, + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/eslint-9-test/package.json b/build-tests/eslint-9-test/package.json new file mode 100644 index 00000000000..085c1165292 --- /dev/null +++ b/build-tests/eslint-9-test/package.json @@ -0,0 +1,43 @@ +{ + "name": "eslint-9-test", + "description": "This project contains a build test to validate ESLint 9 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin)", + "version": "1.0.0", + "private": true, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*", + "@types/node": "20.17.19", + "@typescript-eslint/parser": "~8.56.1", + "eslint": "~9.37.0", + "typescript": "~5.8.2" + } +} diff --git a/build-tests/eslint-9-test/src/__snapshots__/sarif.test.ts.snap b/build-tests/eslint-9-test/src/__snapshots__/sarif.test.ts.snap new file mode 100644 index 00000000000..0ddfa4d6a6f --- /dev/null +++ b/build-tests/eslint-9-test/src/__snapshots__/sarif.test.ts.snap @@ -0,0 +1,111 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`Sarif Logs has the expected content 1`] = ` +Object { + "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", + "runs": Array [ + Object { + "artifacts": Array [ + Object { + "location": Object { + "uri": "src/index.ts", + }, + }, + Object { + "location": Object { + "uri": "src/sarif.test.ts", + }, + }, + ], + "results": Array [ + Object { + "level": "warning", + "locations": Array [ + Object { + "physicalLocation": Object { + "artifactLocation": Object { + "index": 0, + "uri": "src/index.ts", + }, + "region": Object { + "endColumn": 24, + "endLine": 6, + "startColumn": 3, + "startLine": 6, + }, + }, + }, + ], + "message": Object { + "text": "Expected _bar to have a type annotation.", + }, + "ruleId": "@typescript-eslint/typedef", + "ruleIndex": 0, + "suppressions": Array [ + Object { + "justification": "", + "kind": "inSource", + }, + ], + }, + Object { + "level": "warning", + "locations": Array [ + Object { + "physicalLocation": Object { + "artifactLocation": Object { + "index": 0, + "uri": "src/index.ts", + }, + "region": Object { + "endColumn": 30, + "endLine": 10, + "startColumn": 14, + "startLine": 10, + }, + }, + }, + ], + "message": Object { + "text": "Variable name \`Bad_Name\` must match one of the following formats: camelCase, UPPER_CASE, PascalCase", + }, + "ruleId": "@typescript-eslint/naming-convention", + "ruleIndex": 1, + "suppressions": Array [ + Object { + "justification": "", + "kind": "external", + }, + ], + }, + ], + "tool": Object { + "driver": Object { + "informationUri": "https://eslint.org", + "name": "ESLint", + "rules": Array [ + Object { + "helpUri": "https://typescript-eslint.io/rules/typedef", + "id": "@typescript-eslint/typedef", + "properties": Object {}, + "shortDescription": Object { + "text": "Require type annotations in certain places", + }, + }, + Object { + "helpUri": "https://typescript-eslint.io/rules/naming-convention", + "id": "@typescript-eslint/naming-convention", + "properties": Object {}, + "shortDescription": Object { + "text": "Enforce naming conventions for everything across a codebase", + }, + }, + ], + "version": "9.37.0", + }, + }, + }, + ], + "version": "2.1.0", +} +`; diff --git a/build-tests/eslint-9-test/src/index.ts b/build-tests/eslint-9-test/src/index.ts new file mode 100644 index 00000000000..549373093be --- /dev/null +++ b/build-tests/eslint-9-test/src/index.ts @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export class Foo { + // eslint-disable-next-line @typescript-eslint/typedef + private _bar = 'bar'; + public baz: string = this._bar; +} + +export const Bad_Name: string = '37'; diff --git a/build-tests/eslint-9-test/src/sarif.test.ts b/build-tests/eslint-9-test/src/sarif.test.ts new file mode 100644 index 00000000000..b24c907090d --- /dev/null +++ b/build-tests/eslint-9-test/src/sarif.test.ts @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as fs from 'node:fs'; +import * as path from 'node:path'; + +const sarifLogPath: string = path.resolve(__dirname, '../temp/build/lint/lint.sarif'); + +describe('Sarif Logs', () => { + it('has the expected content', () => { + const logContent = fs.readFileSync(sarifLogPath, 'utf-8'); + const parsedLog = JSON.parse(logContent); + expect(parsedLog).toMatchSnapshot(); + }); +}); diff --git a/build-tests/eslint-9-test/tsconfig.json b/build-tests/eslint-9-test/tsconfig.json new file mode 100644 index 00000000000..65e0cf100f1 --- /dev/null +++ b/build-tests/eslint-9-test/tsconfig.json @@ -0,0 +1,5 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/build-tests/eslint-bulk-suppressions-test-flat/build.js b/build-tests/eslint-bulk-suppressions-test-flat/build.js new file mode 100644 index 00000000000..39b6bc1ffa3 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-flat/build.js @@ -0,0 +1,107 @@ +// This project has a duplicate "eslint-bulk-suppressions-test-legacy" intended to test eslint +// against the older version of the TypeScript parser. Any modifications made to this project +// should be reflected in "eslint-bulk-suppressions-test-legacy" as well. + +const { FileSystem, Executable, Text, Import } = require('@rushstack/node-core-library'); +const path = require('path'); +const { + ESLINT_PACKAGE_NAME_ENV_VAR_NAME +} = require('@rushstack/eslint-patch/lib/eslint-bulk-suppressions/constants'); + +const eslintBulkStartPath = Import.resolveModule({ + modulePath: '@rushstack/eslint-bulk/lib-commonjs/start', + baseFolderPath: __dirname +}); + +function tryLoadSuppressions(suppressionsJsonPath) { + try { + return Text.convertToLf(FileSystem.readFile(suppressionsJsonPath)).trim(); + } catch (e) { + if (FileSystem.isNotExistError(e)) { + return ''; + } else { + throw e; + } + } +} + +const RUN_FOLDER_PATHS = ['client', 'server']; +const ESLINT_PACKAGE_NAMES = ['eslint']; + +const updateFilePaths = new Set(); + +for (const runFolderPath of RUN_FOLDER_PATHS) { + const folderPath = `${__dirname}/${runFolderPath}`; + const suppressionsJsonPath = `${folderPath}/.eslint-bulk-suppressions.json`; + + const folderItems = FileSystem.readFolderItems(folderPath); + for (const folderItem of folderItems) { + if (folderItem.isFile() && folderItem.name.match(/^\.eslint\-bulk\-suppressions\-[\d.]+\.json$/)) { + const fullPath = `${folderPath}/${folderItem.name}`; + updateFilePaths.add(fullPath); + } + } + + for (const eslintPackageName of ESLINT_PACKAGE_NAMES) { + const { version: eslintVersion } = require(`${eslintPackageName}/package.json`); + + const startLoggingMessage = `-- Running eslint-bulk-suppressions for eslint@${eslintVersion} in ${runFolderPath} --`; + console.log(startLoggingMessage); + const referenceSuppressionsJsonPath = `${folderPath}/.eslint-bulk-suppressions-${eslintVersion}.json`; + const existingSuppressions = tryLoadSuppressions(referenceSuppressionsJsonPath); + + // The eslint-bulk-suppressions patch expects to find "eslint" in the shell PATH. To ensure deterministic + // test behavior, we need to designate an explicit "node_modules/.bin" folder. + // + // Use the ".bin" folder from @rushstack/eslint-patch as a workaround for this PNPM bug: + // https://github.com/pnpm/pnpm/issues/7833 + const dependencyBinFolder = path.join( + __dirname, + 'node_modules', + '@rushstack', + 'eslint-patch', + 'node_modules', + '.bin' + ); + const shellPathWithEslint = `${dependencyBinFolder}${path.delimiter}${process.env['PATH']}`; + + const args = [eslintBulkStartPath, 'suppress', '--all', 'src']; + const executableResult = Executable.spawnSync(process.argv0, args, { + currentWorkingDirectory: folderPath, + environment: { + ...process.env, + PATH: shellPathWithEslint, + [ESLINT_PACKAGE_NAME_ENV_VAR_NAME]: eslintPackageName + } + }); + + if (executableResult.status !== 0) { + console.error( + `The eslint-bulk-suppressions command (\`node ${args.join(' ')}\` in ${folderPath}) failed.` + ); + console.error('STDOUT:'); + console.error(executableResult.stdout.toString()); + console.error('STDERR:'); + console.error(executableResult.stderr.toString()); + process.exit(1); + } + + const newSuppressions = tryLoadSuppressions(suppressionsJsonPath); + if (newSuppressions === existingSuppressions) { + updateFilePaths.delete(referenceSuppressionsJsonPath); + } else { + updateFilePaths.add(referenceSuppressionsJsonPath); + FileSystem.writeFile(referenceSuppressionsJsonPath, newSuppressions); + } + + FileSystem.deleteFile(suppressionsJsonPath); + } +} + +if (updateFilePaths.size > 0) { + for (const updateFilePath of updateFilePaths) { + console.log(`The suppressions file "${updateFilePath}" was updated and must be committed to git.`); + } + + process.exit(1); +} diff --git a/build-tests/eslint-bulk-suppressions-test-flat/client/.eslint-bulk-suppressions-9.37.0.json b/build-tests/eslint-bulk-suppressions-test-flat/client/.eslint-bulk-suppressions-9.37.0.json new file mode 100644 index 00000000000..070dbc8562a --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-flat/client/.eslint-bulk-suppressions-9.37.0.json @@ -0,0 +1,149 @@ +{ + "suppressions": [ + { + "file": "src/index.ts", + "scopeId": ".", + "rule": "prefer-const" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass", + "rule": "@typescript-eslint/no-wrapper-object-types" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "no-var" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "no-useless-concat" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.constructor", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/no-wrapper-object-types" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "prefer-const" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "dot-notation" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "no-empty" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "no-unused-expressions" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "@typescript-eslint/no-wrapper-object-types" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "no-empty-pattern" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "no-extra-boolean-cast" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "no-unused-expressions" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleObject", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".x", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".y", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".z", + "rule": "@typescript-eslint/explicit-function-return-type" + } + ] +} diff --git a/build-tests/eslint-bulk-suppressions-test-flat/client/eslint.config.js b/build-tests/eslint-bulk-suppressions-test-flat/client/eslint.config.js new file mode 100644 index 00000000000..6753205e3c4 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-flat/client/eslint.config.js @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +require('local-node-rig/profiles/default/includes/eslint/flat/patch/eslint-bulk-suppressions'); + +const typescriptEslintParser = require('@typescript-eslint/parser'); +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + { + ignores: ['.eslintrc.js'] + }, + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parser: typescriptEslintParser, + parserOptions: { + project: '../tsconfig.json', + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/eslint-bulk-suppressions-test-flat/client/src/index.ts b/build-tests/eslint-bulk-suppressions-test-flat/client/src/index.ts new file mode 100644 index 00000000000..570229800d8 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-flat/client/src/index.ts @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/* Top-level scope code samples */ +// scopeId: '.' +let exampleString: string = 5 + ''; + +const exampleObject = { + exampleString: exampleString +}; + +/* Function scope code samples */ +export function exampleFunction() { + const {}: Object = exampleObject; + + // scopeId: '.exampleFunction' + !!!exampleString as Boolean; +} + +// scope: '.ArrowFunctionExpression', +export const x = () => {}, + // scopeId: '.y' + y = () => {}, + // scopeId: '.z' + z = () => {}; + +/* Class scope code samples */ +export class ExampleClass { + // scopeId: '.ExampleClass' + exampleClassProperty: String = exampleString + '4'; + + exampleMethod() { + // scopeId: '.exampleClass.exampleMethod' + var exampleVar; + return exampleVar; + } +} + +/* Variable and anonymous constructs code samples */ +export const exampleArrowFunction = () => { + const exampleBoolean = true; + if (exampleBoolean) { + } + + exampleObject['exampleString']; +}; + +export const exampleAnonymousClass = class { + exampleClassProperty = 'x' + 'y'; + + // scopeId: '.exampleAnonymousClass.constructor' + constructor() {} + + set exampleSetGet(val: string) { + // scopeId: '.exampleAnonymousClass.exampleSetGet' + let exampleVariable: Number = 1; + this.exampleClassProperty = val + exampleVariable; + } + + get exampleSetGet() { + // scopeId: '.exampleAnonymousClass.exampleSetGet' + return this.exampleClassProperty as String as string; + } +}; diff --git a/build-tests/eslint-bulk-suppressions-test-flat/config/rig.json b/build-tests/eslint-bulk-suppressions-test-flat/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-flat/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/eslint-bulk-suppressions-test-flat/config/typescript.json b/build-tests/eslint-bulk-suppressions-test-flat/config/typescript.json new file mode 100644 index 00000000000..77ac46fab28 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-flat/config/typescript.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", + + "additionalModuleKindsToEmit": [] +} diff --git a/build-tests/eslint-bulk-suppressions-test-flat/package.json b/build-tests/eslint-bulk-suppressions-test-flat/package.json new file mode 100644 index 00000000000..a3b74d88134 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-flat/package.json @@ -0,0 +1,19 @@ +{ + "name": "eslint-bulk-suppressions-test-flat", + "description": "Sample code to test eslint bulk suppressions with flat configs", + "version": "1.0.0", + "private": true, + "scripts": { + "_phase:build": "node build.js" + }, + "devDependencies": { + "@rushstack/eslint-bulk": "workspace:*", + "@rushstack/eslint-patch": "workspace:*", + "@rushstack/heft": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "@typescript-eslint/parser": "~8.56.1", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*", + "typescript": "~5.8.2" + } +} diff --git a/build-tests/eslint-bulk-suppressions-test-flat/server/.eslint-bulk-suppressions-9.37.0.json b/build-tests/eslint-bulk-suppressions-test-flat/server/.eslint-bulk-suppressions-9.37.0.json new file mode 100644 index 00000000000..5d0e82b5147 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-flat/server/.eslint-bulk-suppressions-9.37.0.json @@ -0,0 +1,69 @@ +{ + "suppressions": [ + { + "file": "src/index.ts", + "scopeId": ".", + "rule": "@typescript-eslint/no-namespace" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2.constructor", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2.constructor.absurdObject", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleEnum", + "rule": "dot-notation" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleInterface", + "rule": "@typescript-eslint/naming-convention" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleInterface2", + "rule": "@typescript-eslint/naming-convention" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleObjectType", + "rule": "@typescript-eslint/consistent-type-definitions" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleObjectType", + "rule": "@typescript-eslint/no-wrapper-object-types" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleObject2", + "rule": "@typescript-eslint/typedef" + } + ] +} diff --git a/build-tests/eslint-bulk-suppressions-test-flat/server/eslint.config.js b/build-tests/eslint-bulk-suppressions-test-flat/server/eslint.config.js new file mode 100644 index 00000000000..6753205e3c4 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-flat/server/eslint.config.js @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +require('local-node-rig/profiles/default/includes/eslint/flat/patch/eslint-bulk-suppressions'); + +const typescriptEslintParser = require('@typescript-eslint/parser'); +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + { + ignores: ['.eslintrc.js'] + }, + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parser: typescriptEslintParser, + parserOptions: { + project: '../tsconfig.json', + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/eslint-bulk-suppressions-test-flat/server/src/index.ts b/build-tests/eslint-bulk-suppressions-test-flat/server/src/index.ts new file mode 100644 index 00000000000..34328698008 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-flat/server/src/index.ts @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// /* Object property and method code samples */ +export const exampleObject2 = { + // scopeId: '.exampleObject2.exampleObjectProperty + exampleObjectProperty: () => {}, + + exampleObjectMethod() { + // scopeId: '.exampleObject2.exampleObjectMethod' + const exampleUndefined: undefined = undefined; + return exampleUndefined; + } +}; + +/* Absurd examples */ +export class AbsurdClass { + absurdClassMethod() { + return class AbsurdClass2 { + absurdClassProperty; + constructor() { + const absurdObject = { + // scopeId: '.AbsurdClass.absurdClassMethod.AbsurdClass2.constructor.absurdObject.absurdObjectMethod' + absurdObjectMethod() {} + }; + this.absurdClassProperty = absurdObject; + } + }; + } +} + +/* Type, interface, enum code samples */ +export type ExampleObjectType = { + // scopeId: '.ExampleObjectType' + examplePropertyType: String; +}; + +// scopeId: '.ExampleInterface' +export interface ExampleInterface {} + +export enum ExampleEnum { + A = 0, + + B = 1, + + C = 'exampleStringValue'['length'], + + D = 1 +} + +/* Namespace, declare, module code samples */ +// scopeId: '.ExampleModule' +export namespace ExampleModule { + // scopeId: '.ExampleModule.ExampleInterface2' + export interface ExampleInterface2 {} +} diff --git a/build-tests/eslint-bulk-suppressions-test-flat/tsconfig.json b/build-tests/eslint-bulk-suppressions-test-flat/tsconfig.json new file mode 100644 index 00000000000..cce25e95fc4 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-flat/tsconfig.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib-esm", + "declarationDir": "lib-dts", + "rootDirs": ["client", "server"], + + "forceConsistentCasingInFileNames": true, + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictNullChecks": true, + "noUnusedLocals": true, + + "module": "esnext", + "moduleResolution": "node", + "target": "es5", + "lib": ["es5"] + }, + + "include": ["client/**/*.ts", "client/**/*.tsx", "server/**/*.ts", "server/**/*.tsx"] +} diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/build.js b/build-tests/eslint-bulk-suppressions-test-legacy/build.js new file mode 100644 index 00000000000..d7137ab7a77 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/build.js @@ -0,0 +1,107 @@ +// This project is a duplicate of "eslint-bulk-suppressions-test" intended to test eslint +// against the older version of the TypeScript parser. Any modifications made to this project +// should be reflected in "eslint-bulk-suppressions-test" as well. + +const { FileSystem, Executable, Text, Import } = require('@rushstack/node-core-library'); +const path = require('path'); +const { + ESLINT_PACKAGE_NAME_ENV_VAR_NAME +} = require('@rushstack/eslint-patch/lib/eslint-bulk-suppressions/constants'); + +const eslintBulkStartPath = Import.resolveModule({ + modulePath: '@rushstack/eslint-bulk/lib-commonjs/start', + baseFolderPath: __dirname +}); + +function tryLoadSuppressions(suppressionsJsonPath) { + try { + return Text.convertToLf(FileSystem.readFile(suppressionsJsonPath)).trim(); + } catch (e) { + if (FileSystem.isNotExistError(e)) { + return ''; + } else { + throw e; + } + } +} + +const RUN_FOLDER_PATHS = ['client', 'server']; +const ESLINT_PACKAGE_NAMES = ['eslint', 'eslint-8.23', 'eslint-oldest']; + +const updateFilePaths = new Set(); + +for (const runFolderPath of RUN_FOLDER_PATHS) { + const folderPath = `${__dirname}/${runFolderPath}`; + const suppressionsJsonPath = `${folderPath}/.eslint-bulk-suppressions.json`; + + const folderItems = FileSystem.readFolderItems(folderPath); + for (const folderItem of folderItems) { + if (folderItem.isFile() && folderItem.name.match(/^\.eslint\-bulk\-suppressions\-[\d.]+\.json$/)) { + const fullPath = `${folderPath}/${folderItem.name}`; + updateFilePaths.add(fullPath); + } + } + + for (const eslintPackageName of ESLINT_PACKAGE_NAMES) { + const { version: eslintVersion } = require(`${eslintPackageName}/package.json`); + + const startLoggingMessage = `-- Running eslint-bulk-suppressions for eslint@${eslintVersion} in ${runFolderPath} --`; + console.log(startLoggingMessage); + const referenceSuppressionsJsonPath = `${folderPath}/.eslint-bulk-suppressions-${eslintVersion}.json`; + const existingSuppressions = tryLoadSuppressions(referenceSuppressionsJsonPath); + + // The eslint-bulk-suppressions patch expects to find "eslint" in the shell PATH. To ensure deterministic + // test behavior, we need to designate an explicit "node_modules/.bin" folder. + // + // Use the ".bin" folder from @rushstack/eslint-patch as a workaround for this PNPM bug: + // https://github.com/pnpm/pnpm/issues/7833 + const dependencyBinFolder = path.join( + __dirname, + 'node_modules', + '@rushstack', + 'eslint-patch', + 'node_modules', + '.bin' + ); + const shellPathWithEslint = `${dependencyBinFolder}${path.delimiter}${process.env['PATH']}`; + + const args = [eslintBulkStartPath, 'suppress', '--all', 'src']; + const executableResult = Executable.spawnSync(process.argv0, args, { + currentWorkingDirectory: folderPath, + environment: { + ...process.env, + PATH: shellPathWithEslint, + [ESLINT_PACKAGE_NAME_ENV_VAR_NAME]: eslintPackageName + } + }); + + if (executableResult.status !== 0) { + console.error( + `The eslint-bulk-suppressions command (\`node ${args.join(' ')}\` in ${folderPath}) failed.` + ); + console.error('STDOUT:'); + console.error(executableResult.stdout.toString()); + console.error('STDERR:'); + console.error(executableResult.stderr.toString()); + process.exit(1); + } + + const newSuppressions = tryLoadSuppressions(suppressionsJsonPath); + if (newSuppressions === existingSuppressions) { + updateFilePaths.delete(referenceSuppressionsJsonPath); + } else { + updateFilePaths.add(referenceSuppressionsJsonPath); + FileSystem.writeFile(referenceSuppressionsJsonPath, newSuppressions); + } + + FileSystem.deleteFile(suppressionsJsonPath); + } +} + +if (updateFilePaths.size > 0) { + for (const updateFilePath of updateFilePaths) { + console.log(`The suppressions file "${updateFilePath}" was updated and must be committed to git.`); + } + + process.exit(1); +} diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/client/.eslint-bulk-suppressions-8.23.1.json b/build-tests/eslint-bulk-suppressions-test-legacy/client/.eslint-bulk-suppressions-8.23.1.json new file mode 100644 index 00000000000..40059c12365 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/client/.eslint-bulk-suppressions-8.23.1.json @@ -0,0 +1,139 @@ +{ + "suppressions": [ + { + "file": "src/index.ts", + "scopeId": ".", + "rule": "prefer-const" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass", + "rule": "@typescript-eslint/ban-types" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "no-var" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "no-useless-concat" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.constructor", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/ban-types" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "dot-notation" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "no-empty" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "no-unused-expressions" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "@typescript-eslint/ban-types" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "no-empty-pattern" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "no-extra-boolean-cast" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleObject", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".x", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".y", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".z", + "rule": "@typescript-eslint/explicit-function-return-type" + } + ] +} diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/client/.eslint-bulk-suppressions-8.57.1.json b/build-tests/eslint-bulk-suppressions-test-legacy/client/.eslint-bulk-suppressions-8.57.1.json new file mode 100644 index 00000000000..40059c12365 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/client/.eslint-bulk-suppressions-8.57.1.json @@ -0,0 +1,139 @@ +{ + "suppressions": [ + { + "file": "src/index.ts", + "scopeId": ".", + "rule": "prefer-const" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass", + "rule": "@typescript-eslint/ban-types" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "no-var" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "no-useless-concat" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.constructor", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/ban-types" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "dot-notation" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "no-empty" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "no-unused-expressions" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "@typescript-eslint/ban-types" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "no-empty-pattern" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "no-extra-boolean-cast" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleObject", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".x", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".y", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".z", + "rule": "@typescript-eslint/explicit-function-return-type" + } + ] +} diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/client/.eslint-bulk-suppressions-8.6.0.json b/build-tests/eslint-bulk-suppressions-test-legacy/client/.eslint-bulk-suppressions-8.6.0.json new file mode 100644 index 00000000000..40059c12365 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/client/.eslint-bulk-suppressions-8.6.0.json @@ -0,0 +1,139 @@ +{ + "suppressions": [ + { + "file": "src/index.ts", + "scopeId": ".", + "rule": "prefer-const" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass", + "rule": "@typescript-eslint/ban-types" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "no-var" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "no-useless-concat" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.constructor", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/ban-types" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "dot-notation" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "no-empty" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "no-unused-expressions" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "@typescript-eslint/ban-types" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "no-empty-pattern" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "no-extra-boolean-cast" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleObject", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".x", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".y", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".z", + "rule": "@typescript-eslint/explicit-function-return-type" + } + ] +} diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/client/.eslintrc.js b/build-tests/eslint-bulk-suppressions-test-legacy/client/.eslintrc.js new file mode 100644 index 00000000000..4cc74b0570a --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/client/.eslintrc.js @@ -0,0 +1,28 @@ +// This is a workaround for https://github.com/eslint/eslint/issues/3458 +require('@rushstack/eslint-config/patch/modern-module-resolution'); +require('@rushstack/eslint-config/patch/custom-config-package-names'); +require('@rushstack/eslint-config/patch/eslint-bulk-suppressions'); + +module.exports = { + extends: [ + '@rushstack/eslint-config/profile/node-trusted-tool', + '@rushstack/eslint-config/mixins/friendly-locals' + ], + ignorePatterns: ['.eslintrc.js'], + + overrides: [ + /** + * Override the parser from @rushstack/eslint-config. Since the config is coming + * from the workspace instead of the external NPM package, the versions of ESLint + * and TypeScript that the config consumes will be resolved from the devDependencies + * of the config instead of from the eslint-8-test package. Overriding the parser + * ensures that the these dependencies come from the eslint-8-test package. See: + * https://github.com/microsoft/rushstack/issues/3021 + */ + { + files: ['**/*.ts', '**/*.tsx'], + parser: '@typescript-eslint/parser', + parserOptions: { project: '../tsconfig.json', tsconfigRootDir: __dirname } + } + ] +}; diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/client/src/index.ts b/build-tests/eslint-bulk-suppressions-test-legacy/client/src/index.ts new file mode 100644 index 00000000000..570229800d8 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/client/src/index.ts @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/* Top-level scope code samples */ +// scopeId: '.' +let exampleString: string = 5 + ''; + +const exampleObject = { + exampleString: exampleString +}; + +/* Function scope code samples */ +export function exampleFunction() { + const {}: Object = exampleObject; + + // scopeId: '.exampleFunction' + !!!exampleString as Boolean; +} + +// scope: '.ArrowFunctionExpression', +export const x = () => {}, + // scopeId: '.y' + y = () => {}, + // scopeId: '.z' + z = () => {}; + +/* Class scope code samples */ +export class ExampleClass { + // scopeId: '.ExampleClass' + exampleClassProperty: String = exampleString + '4'; + + exampleMethod() { + // scopeId: '.exampleClass.exampleMethod' + var exampleVar; + return exampleVar; + } +} + +/* Variable and anonymous constructs code samples */ +export const exampleArrowFunction = () => { + const exampleBoolean = true; + if (exampleBoolean) { + } + + exampleObject['exampleString']; +}; + +export const exampleAnonymousClass = class { + exampleClassProperty = 'x' + 'y'; + + // scopeId: '.exampleAnonymousClass.constructor' + constructor() {} + + set exampleSetGet(val: string) { + // scopeId: '.exampleAnonymousClass.exampleSetGet' + let exampleVariable: Number = 1; + this.exampleClassProperty = val + exampleVariable; + } + + get exampleSetGet() { + // scopeId: '.exampleAnonymousClass.exampleSetGet' + return this.exampleClassProperty as String as string; + } +}; diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/config/rig.json b/build-tests/eslint-bulk-suppressions-test-legacy/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/config/typescript.json b/build-tests/eslint-bulk-suppressions-test-legacy/config/typescript.json new file mode 100644 index 00000000000..77ac46fab28 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/config/typescript.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", + + "additionalModuleKindsToEmit": [] +} diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/package.json b/build-tests/eslint-bulk-suppressions-test-legacy/package.json new file mode 100644 index 00000000000..e848729288f --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/package.json @@ -0,0 +1,22 @@ +{ + "name": "eslint-bulk-suppressions-test-legacy", + "description": "Sample code to test eslint bulk suppressions for versions of eslint < 8.57.0", + "version": "1.0.0", + "private": true, + "scripts": { + "_phase:build": "node build.js" + }, + "devDependencies": { + "@rushstack/eslint-bulk": "workspace:*", + "@rushstack/eslint-config": "3.7.1", + "@rushstack/eslint-patch": "workspace:*", + "@rushstack/heft": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "@typescript-eslint/parser": "~8.56.1", + "eslint": "~8.57.0", + "eslint-8.23": "npm:eslint@8.23.1", + "eslint-oldest": "npm:eslint@8.6.0", + "local-node-rig": "workspace:*", + "typescript": "~5.8.2" + } +} diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/server/.eslint-bulk-suppressions-8.23.1.json b/build-tests/eslint-bulk-suppressions-test-legacy/server/.eslint-bulk-suppressions-8.23.1.json new file mode 100644 index 00000000000..ab3846d907a --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/server/.eslint-bulk-suppressions-8.23.1.json @@ -0,0 +1,69 @@ +{ + "suppressions": [ + { + "file": "src/index.ts", + "scopeId": ".", + "rule": "@typescript-eslint/no-namespace" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2.constructor", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2.constructor.absurdObject", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleEnum", + "rule": "dot-notation" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleInterface", + "rule": "@typescript-eslint/naming-convention" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleInterface2", + "rule": "@typescript-eslint/naming-convention" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleObjectType", + "rule": "@typescript-eslint/ban-types" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleObjectType", + "rule": "@typescript-eslint/consistent-type-definitions" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleObject2", + "rule": "@typescript-eslint/typedef" + } + ] +} diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/server/.eslint-bulk-suppressions-8.57.1.json b/build-tests/eslint-bulk-suppressions-test-legacy/server/.eslint-bulk-suppressions-8.57.1.json new file mode 100644 index 00000000000..ab3846d907a --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/server/.eslint-bulk-suppressions-8.57.1.json @@ -0,0 +1,69 @@ +{ + "suppressions": [ + { + "file": "src/index.ts", + "scopeId": ".", + "rule": "@typescript-eslint/no-namespace" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2.constructor", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2.constructor.absurdObject", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleEnum", + "rule": "dot-notation" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleInterface", + "rule": "@typescript-eslint/naming-convention" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleInterface2", + "rule": "@typescript-eslint/naming-convention" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleObjectType", + "rule": "@typescript-eslint/ban-types" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleObjectType", + "rule": "@typescript-eslint/consistent-type-definitions" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleObject2", + "rule": "@typescript-eslint/typedef" + } + ] +} diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/server/.eslint-bulk-suppressions-8.6.0.json b/build-tests/eslint-bulk-suppressions-test-legacy/server/.eslint-bulk-suppressions-8.6.0.json new file mode 100644 index 00000000000..ab3846d907a --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/server/.eslint-bulk-suppressions-8.6.0.json @@ -0,0 +1,69 @@ +{ + "suppressions": [ + { + "file": "src/index.ts", + "scopeId": ".", + "rule": "@typescript-eslint/no-namespace" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2.constructor", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2.constructor.absurdObject", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleEnum", + "rule": "dot-notation" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleInterface", + "rule": "@typescript-eslint/naming-convention" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleInterface2", + "rule": "@typescript-eslint/naming-convention" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleObjectType", + "rule": "@typescript-eslint/ban-types" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleObjectType", + "rule": "@typescript-eslint/consistent-type-definitions" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleObject2", + "rule": "@typescript-eslint/typedef" + } + ] +} diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/server/.eslintrc.js b/build-tests/eslint-bulk-suppressions-test-legacy/server/.eslintrc.js new file mode 100644 index 00000000000..4cc74b0570a --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/server/.eslintrc.js @@ -0,0 +1,28 @@ +// This is a workaround for https://github.com/eslint/eslint/issues/3458 +require('@rushstack/eslint-config/patch/modern-module-resolution'); +require('@rushstack/eslint-config/patch/custom-config-package-names'); +require('@rushstack/eslint-config/patch/eslint-bulk-suppressions'); + +module.exports = { + extends: [ + '@rushstack/eslint-config/profile/node-trusted-tool', + '@rushstack/eslint-config/mixins/friendly-locals' + ], + ignorePatterns: ['.eslintrc.js'], + + overrides: [ + /** + * Override the parser from @rushstack/eslint-config. Since the config is coming + * from the workspace instead of the external NPM package, the versions of ESLint + * and TypeScript that the config consumes will be resolved from the devDependencies + * of the config instead of from the eslint-8-test package. Overriding the parser + * ensures that the these dependencies come from the eslint-8-test package. See: + * https://github.com/microsoft/rushstack/issues/3021 + */ + { + files: ['**/*.ts', '**/*.tsx'], + parser: '@typescript-eslint/parser', + parserOptions: { project: '../tsconfig.json', tsconfigRootDir: __dirname } + } + ] +}; diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/server/src/index.ts b/build-tests/eslint-bulk-suppressions-test-legacy/server/src/index.ts new file mode 100644 index 00000000000..34328698008 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/server/src/index.ts @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// /* Object property and method code samples */ +export const exampleObject2 = { + // scopeId: '.exampleObject2.exampleObjectProperty + exampleObjectProperty: () => {}, + + exampleObjectMethod() { + // scopeId: '.exampleObject2.exampleObjectMethod' + const exampleUndefined: undefined = undefined; + return exampleUndefined; + } +}; + +/* Absurd examples */ +export class AbsurdClass { + absurdClassMethod() { + return class AbsurdClass2 { + absurdClassProperty; + constructor() { + const absurdObject = { + // scopeId: '.AbsurdClass.absurdClassMethod.AbsurdClass2.constructor.absurdObject.absurdObjectMethod' + absurdObjectMethod() {} + }; + this.absurdClassProperty = absurdObject; + } + }; + } +} + +/* Type, interface, enum code samples */ +export type ExampleObjectType = { + // scopeId: '.ExampleObjectType' + examplePropertyType: String; +}; + +// scopeId: '.ExampleInterface' +export interface ExampleInterface {} + +export enum ExampleEnum { + A = 0, + + B = 1, + + C = 'exampleStringValue'['length'], + + D = 1 +} + +/* Namespace, declare, module code samples */ +// scopeId: '.ExampleModule' +export namespace ExampleModule { + // scopeId: '.ExampleModule.ExampleInterface2' + export interface ExampleInterface2 {} +} diff --git a/build-tests/eslint-bulk-suppressions-test-legacy/tsconfig.json b/build-tests/eslint-bulk-suppressions-test-legacy/tsconfig.json new file mode 100644 index 00000000000..118ccfd8998 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test-legacy/tsconfig.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + "compilerOptions": { + "outDir": "lib-esm", + "declarationDir": "lib-dts", + "rootDirs": ["client", "server"], + + "forceConsistentCasingInFileNames": true, + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictNullChecks": true, + "noUnusedLocals": true, + + "module": "esnext", + "moduleResolution": "node", + "target": "es5", + "lib": ["es5"] + }, + + "include": ["client/**/*.ts", "client/**/*.tsx", "server/**/*.ts", "server/**/*.tsx"] +} diff --git a/build-tests/eslint-bulk-suppressions-test/build.js b/build-tests/eslint-bulk-suppressions-test/build.js new file mode 100644 index 00000000000..39b6bc1ffa3 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test/build.js @@ -0,0 +1,107 @@ +// This project has a duplicate "eslint-bulk-suppressions-test-legacy" intended to test eslint +// against the older version of the TypeScript parser. Any modifications made to this project +// should be reflected in "eslint-bulk-suppressions-test-legacy" as well. + +const { FileSystem, Executable, Text, Import } = require('@rushstack/node-core-library'); +const path = require('path'); +const { + ESLINT_PACKAGE_NAME_ENV_VAR_NAME +} = require('@rushstack/eslint-patch/lib/eslint-bulk-suppressions/constants'); + +const eslintBulkStartPath = Import.resolveModule({ + modulePath: '@rushstack/eslint-bulk/lib-commonjs/start', + baseFolderPath: __dirname +}); + +function tryLoadSuppressions(suppressionsJsonPath) { + try { + return Text.convertToLf(FileSystem.readFile(suppressionsJsonPath)).trim(); + } catch (e) { + if (FileSystem.isNotExistError(e)) { + return ''; + } else { + throw e; + } + } +} + +const RUN_FOLDER_PATHS = ['client', 'server']; +const ESLINT_PACKAGE_NAMES = ['eslint']; + +const updateFilePaths = new Set(); + +for (const runFolderPath of RUN_FOLDER_PATHS) { + const folderPath = `${__dirname}/${runFolderPath}`; + const suppressionsJsonPath = `${folderPath}/.eslint-bulk-suppressions.json`; + + const folderItems = FileSystem.readFolderItems(folderPath); + for (const folderItem of folderItems) { + if (folderItem.isFile() && folderItem.name.match(/^\.eslint\-bulk\-suppressions\-[\d.]+\.json$/)) { + const fullPath = `${folderPath}/${folderItem.name}`; + updateFilePaths.add(fullPath); + } + } + + for (const eslintPackageName of ESLINT_PACKAGE_NAMES) { + const { version: eslintVersion } = require(`${eslintPackageName}/package.json`); + + const startLoggingMessage = `-- Running eslint-bulk-suppressions for eslint@${eslintVersion} in ${runFolderPath} --`; + console.log(startLoggingMessage); + const referenceSuppressionsJsonPath = `${folderPath}/.eslint-bulk-suppressions-${eslintVersion}.json`; + const existingSuppressions = tryLoadSuppressions(referenceSuppressionsJsonPath); + + // The eslint-bulk-suppressions patch expects to find "eslint" in the shell PATH. To ensure deterministic + // test behavior, we need to designate an explicit "node_modules/.bin" folder. + // + // Use the ".bin" folder from @rushstack/eslint-patch as a workaround for this PNPM bug: + // https://github.com/pnpm/pnpm/issues/7833 + const dependencyBinFolder = path.join( + __dirname, + 'node_modules', + '@rushstack', + 'eslint-patch', + 'node_modules', + '.bin' + ); + const shellPathWithEslint = `${dependencyBinFolder}${path.delimiter}${process.env['PATH']}`; + + const args = [eslintBulkStartPath, 'suppress', '--all', 'src']; + const executableResult = Executable.spawnSync(process.argv0, args, { + currentWorkingDirectory: folderPath, + environment: { + ...process.env, + PATH: shellPathWithEslint, + [ESLINT_PACKAGE_NAME_ENV_VAR_NAME]: eslintPackageName + } + }); + + if (executableResult.status !== 0) { + console.error( + `The eslint-bulk-suppressions command (\`node ${args.join(' ')}\` in ${folderPath}) failed.` + ); + console.error('STDOUT:'); + console.error(executableResult.stdout.toString()); + console.error('STDERR:'); + console.error(executableResult.stderr.toString()); + process.exit(1); + } + + const newSuppressions = tryLoadSuppressions(suppressionsJsonPath); + if (newSuppressions === existingSuppressions) { + updateFilePaths.delete(referenceSuppressionsJsonPath); + } else { + updateFilePaths.add(referenceSuppressionsJsonPath); + FileSystem.writeFile(referenceSuppressionsJsonPath, newSuppressions); + } + + FileSystem.deleteFile(suppressionsJsonPath); + } +} + +if (updateFilePaths.size > 0) { + for (const updateFilePath of updateFilePaths) { + console.log(`The suppressions file "${updateFilePath}" was updated and must be committed to git.`); + } + + process.exit(1); +} diff --git a/build-tests/eslint-bulk-suppressions-test/client/.eslint-bulk-suppressions-8.57.1.json b/build-tests/eslint-bulk-suppressions-test/client/.eslint-bulk-suppressions-8.57.1.json new file mode 100644 index 00000000000..8852d8bc68f --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test/client/.eslint-bulk-suppressions-8.57.1.json @@ -0,0 +1,144 @@ +{ + "suppressions": [ + { + "file": "src/index.ts", + "scopeId": ".", + "rule": "prefer-const" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass", + "rule": "@typescript-eslint/no-wrapper-object-types" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleClass.exampleMethod", + "rule": "no-var" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass", + "rule": "no-useless-concat" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.constructor", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "@typescript-eslint/no-wrapper-object-types" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleAnonymousClass.exampleSetGet", + "rule": "prefer-const" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "dot-notation" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "no-empty" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleArrowFunction", + "rule": "no-unused-expressions" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "@typescript-eslint/no-wrapper-object-types" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "no-empty-pattern" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleFunction", + "rule": "no-extra-boolean-cast" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleObject", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".x", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".y", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".z", + "rule": "@typescript-eslint/explicit-function-return-type" + } + ] +} diff --git a/build-tests/eslint-bulk-suppressions-test/client/.eslintrc.js b/build-tests/eslint-bulk-suppressions-test/client/.eslintrc.js new file mode 100644 index 00000000000..3d3ed2f7929 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test/client/.eslintrc.js @@ -0,0 +1,29 @@ +// This is a workaround for https://github.com/eslint/eslint/issues/3458 +require('@rushstack/eslint-config/patch/modern-module-resolution'); +// This is a workaround for https://github.com/microsoft/rushstack/issues/3021 +require('@rushstack/eslint-config/patch/custom-config-package-names'); +require('@rushstack/eslint-config/patch/eslint-bulk-suppressions'); + +module.exports = { + extends: [ + '@rushstack/eslint-config/profile/node-trusted-tool', + '@rushstack/eslint-config/mixins/friendly-locals' + ], + ignorePatterns: ['.eslintrc.js'], + + overrides: [ + /** + * Override the parser from @rushstack/eslint-config. Since the config is coming + * from the workspace instead of the external NPM package, the versions of ESLint + * and TypeScript that the config consumes will be resolved from the devDependencies + * of the config instead of from the eslint-8-test package. Overriding the parser + * ensures that the these dependencies come from the eslint-8-test package. See: + * https://github.com/microsoft/rushstack/issues/3021 + */ + { + files: ['**/*.ts', '**/*.tsx'], + parser: '@typescript-eslint/parser', + parserOptions: { project: '../tsconfig.json', tsconfigRootDir: __dirname } + } + ] +}; diff --git a/build-tests/eslint-bulk-suppressions-test/client/src/index.ts b/build-tests/eslint-bulk-suppressions-test/client/src/index.ts new file mode 100644 index 00000000000..570229800d8 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test/client/src/index.ts @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/* Top-level scope code samples */ +// scopeId: '.' +let exampleString: string = 5 + ''; + +const exampleObject = { + exampleString: exampleString +}; + +/* Function scope code samples */ +export function exampleFunction() { + const {}: Object = exampleObject; + + // scopeId: '.exampleFunction' + !!!exampleString as Boolean; +} + +// scope: '.ArrowFunctionExpression', +export const x = () => {}, + // scopeId: '.y' + y = () => {}, + // scopeId: '.z' + z = () => {}; + +/* Class scope code samples */ +export class ExampleClass { + // scopeId: '.ExampleClass' + exampleClassProperty: String = exampleString + '4'; + + exampleMethod() { + // scopeId: '.exampleClass.exampleMethod' + var exampleVar; + return exampleVar; + } +} + +/* Variable and anonymous constructs code samples */ +export const exampleArrowFunction = () => { + const exampleBoolean = true; + if (exampleBoolean) { + } + + exampleObject['exampleString']; +}; + +export const exampleAnonymousClass = class { + exampleClassProperty = 'x' + 'y'; + + // scopeId: '.exampleAnonymousClass.constructor' + constructor() {} + + set exampleSetGet(val: string) { + // scopeId: '.exampleAnonymousClass.exampleSetGet' + let exampleVariable: Number = 1; + this.exampleClassProperty = val + exampleVariable; + } + + get exampleSetGet() { + // scopeId: '.exampleAnonymousClass.exampleSetGet' + return this.exampleClassProperty as String as string; + } +}; diff --git a/build-tests/eslint-bulk-suppressions-test/config/rig.json b/build-tests/eslint-bulk-suppressions-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/eslint-bulk-suppressions-test/config/typescript.json b/build-tests/eslint-bulk-suppressions-test/config/typescript.json new file mode 100644 index 00000000000..77ac46fab28 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test/config/typescript.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", + + "additionalModuleKindsToEmit": [] +} diff --git a/build-tests/eslint-bulk-suppressions-test/package.json b/build-tests/eslint-bulk-suppressions-test/package.json new file mode 100644 index 00000000000..b5e8dec61db --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test/package.json @@ -0,0 +1,20 @@ +{ + "name": "eslint-bulk-suppressions-test", + "description": "Sample code to test eslint bulk suppressions", + "version": "1.0.0", + "private": true, + "scripts": { + "_phase:build": "node build.js" + }, + "devDependencies": { + "@rushstack/eslint-bulk": "workspace:*", + "@rushstack/eslint-config": "workspace:*", + "@rushstack/eslint-patch": "workspace:*", + "@rushstack/heft": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "@typescript-eslint/parser": "~8.56.1", + "eslint": "~8.57.0", + "local-node-rig": "workspace:*", + "typescript": "~5.8.2" + } +} diff --git a/build-tests/eslint-bulk-suppressions-test/server/.eslint-bulk-suppressions-8.57.1.json b/build-tests/eslint-bulk-suppressions-test/server/.eslint-bulk-suppressions-8.57.1.json new file mode 100644 index 00000000000..5d0e82b5147 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test/server/.eslint-bulk-suppressions-8.57.1.json @@ -0,0 +1,69 @@ +{ + "suppressions": [ + { + "file": "src/index.ts", + "scopeId": ".", + "rule": "@typescript-eslint/no-namespace" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod", + "rule": "@typescript-eslint/explicit-function-return-type" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2.constructor", + "rule": "@typescript-eslint/explicit-member-accessibility" + }, + { + "file": "src/index.ts", + "scopeId": ".AbsurdClass.absurdClassMethod.AbsurdClass2.constructor.absurdObject", + "rule": "@typescript-eslint/typedef" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleEnum", + "rule": "dot-notation" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleInterface", + "rule": "@typescript-eslint/naming-convention" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleInterface2", + "rule": "@typescript-eslint/naming-convention" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleObjectType", + "rule": "@typescript-eslint/consistent-type-definitions" + }, + { + "file": "src/index.ts", + "scopeId": ".ExampleObjectType", + "rule": "@typescript-eslint/no-wrapper-object-types" + }, + { + "file": "src/index.ts", + "scopeId": ".exampleObject2", + "rule": "@typescript-eslint/typedef" + } + ] +} diff --git a/build-tests/eslint-bulk-suppressions-test/server/.eslintrc.js b/build-tests/eslint-bulk-suppressions-test/server/.eslintrc.js new file mode 100644 index 00000000000..3d3ed2f7929 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test/server/.eslintrc.js @@ -0,0 +1,29 @@ +// This is a workaround for https://github.com/eslint/eslint/issues/3458 +require('@rushstack/eslint-config/patch/modern-module-resolution'); +// This is a workaround for https://github.com/microsoft/rushstack/issues/3021 +require('@rushstack/eslint-config/patch/custom-config-package-names'); +require('@rushstack/eslint-config/patch/eslint-bulk-suppressions'); + +module.exports = { + extends: [ + '@rushstack/eslint-config/profile/node-trusted-tool', + '@rushstack/eslint-config/mixins/friendly-locals' + ], + ignorePatterns: ['.eslintrc.js'], + + overrides: [ + /** + * Override the parser from @rushstack/eslint-config. Since the config is coming + * from the workspace instead of the external NPM package, the versions of ESLint + * and TypeScript that the config consumes will be resolved from the devDependencies + * of the config instead of from the eslint-8-test package. Overriding the parser + * ensures that the these dependencies come from the eslint-8-test package. See: + * https://github.com/microsoft/rushstack/issues/3021 + */ + { + files: ['**/*.ts', '**/*.tsx'], + parser: '@typescript-eslint/parser', + parserOptions: { project: '../tsconfig.json', tsconfigRootDir: __dirname } + } + ] +}; diff --git a/build-tests/eslint-bulk-suppressions-test/server/src/index.ts b/build-tests/eslint-bulk-suppressions-test/server/src/index.ts new file mode 100644 index 00000000000..34328698008 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test/server/src/index.ts @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// /* Object property and method code samples */ +export const exampleObject2 = { + // scopeId: '.exampleObject2.exampleObjectProperty + exampleObjectProperty: () => {}, + + exampleObjectMethod() { + // scopeId: '.exampleObject2.exampleObjectMethod' + const exampleUndefined: undefined = undefined; + return exampleUndefined; + } +}; + +/* Absurd examples */ +export class AbsurdClass { + absurdClassMethod() { + return class AbsurdClass2 { + absurdClassProperty; + constructor() { + const absurdObject = { + // scopeId: '.AbsurdClass.absurdClassMethod.AbsurdClass2.constructor.absurdObject.absurdObjectMethod' + absurdObjectMethod() {} + }; + this.absurdClassProperty = absurdObject; + } + }; + } +} + +/* Type, interface, enum code samples */ +export type ExampleObjectType = { + // scopeId: '.ExampleObjectType' + examplePropertyType: String; +}; + +// scopeId: '.ExampleInterface' +export interface ExampleInterface {} + +export enum ExampleEnum { + A = 0, + + B = 1, + + C = 'exampleStringValue'['length'], + + D = 1 +} + +/* Namespace, declare, module code samples */ +// scopeId: '.ExampleModule' +export namespace ExampleModule { + // scopeId: '.ExampleModule.ExampleInterface2' + export interface ExampleInterface2 {} +} diff --git a/build-tests/eslint-bulk-suppressions-test/tsconfig.json b/build-tests/eslint-bulk-suppressions-test/tsconfig.json new file mode 100644 index 00000000000..cce25e95fc4 --- /dev/null +++ b/build-tests/eslint-bulk-suppressions-test/tsconfig.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib-esm", + "declarationDir": "lib-dts", + "rootDirs": ["client", "server"], + + "forceConsistentCasingInFileNames": true, + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictNullChecks": true, + "noUnusedLocals": true, + + "module": "esnext", + "moduleResolution": "node", + "target": "es5", + "lib": ["es5"] + }, + + "include": ["client/**/*.ts", "client/**/*.tsx", "server/**/*.ts", "server/**/*.tsx"] +} diff --git a/build-tests/esm-node-import-test/config/jest.config.json b/build-tests/esm-node-import-test/config/jest.config.json new file mode 100644 index 00000000000..d1749681d90 --- /dev/null +++ b/build-tests/esm-node-import-test/config/jest.config.json @@ -0,0 +1,3 @@ +{ + "extends": "local-node-rig/profiles/default/config/jest.config.json" +} diff --git a/build-tests/esm-node-import-test/config/rig.json b/build-tests/esm-node-import-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/esm-node-import-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/esm-node-import-test/eslint.config.cjs b/build-tests/esm-node-import-test/eslint.config.cjs new file mode 100644 index 00000000000..87132f43292 --- /dev/null +++ b/build-tests/esm-node-import-test/eslint.config.cjs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); +const tsdocMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/tsdoc'); + +module.exports = [ + ...nodeProfile, + ...friendlyLocalsMixin, + ...tsdocMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/esm-node-import-test/package.json b/build-tests/esm-node-import-test/package.json new file mode 100644 index 00000000000..1420121453c --- /dev/null +++ b/build-tests/esm-node-import-test/package.json @@ -0,0 +1,20 @@ +{ + "name": "esm-node-import-test", + "description": "This project validates that importing a rushstack package from a 'type: module' Node.js project works correctly with the package.json 'exports' field. See https://github.com/microsoft/rushstack/issues/5644", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "heft test --clean", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "dependencies": { + "@rushstack/node-core-library": "workspace:*" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + } +} diff --git a/build-tests/esm-node-import-test/src/start.ts b/build-tests/esm-node-import-test/src/start.ts new file mode 100644 index 00000000000..7240f7bcc2a --- /dev/null +++ b/build-tests/esm-node-import-test/src/start.ts @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { Path } from '@rushstack/node-core-library'; + +export const EXPECTED_OUTPUT: string = + 'ESM import test PASSED: @rushstack/node-core-library resolved correctly under Node.js ESM.'; + +// If this line runs without ERR_MODULE_NOT_FOUND, the exports map is working correctly. +const result: string = Path.convertToSlashes('foo\\bar'); +if (result !== 'foo/bar') { + throw new Error('Unexpected result from Path.convertToSlashes: ' + result); +} + +// eslint-disable-next-line no-console +console.log(EXPECTED_OUTPUT); diff --git a/build-tests/esm-node-import-test/src/test/__snapshots__/start.test.ts.snap b/build-tests/esm-node-import-test/src/test/__snapshots__/start.test.ts.snap new file mode 100644 index 00000000000..c2466aea9b1 --- /dev/null +++ b/build-tests/esm-node-import-test/src/test/__snapshots__/start.test.ts.snap @@ -0,0 +1,6 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`ESM Node Import Test should resolve @rushstack/node-core-library correctly under Node.js ESM 1`] = ` +"ESM import test PASSED: @rushstack/node-core-library resolved correctly under Node.js ESM. +" +`; diff --git a/build-tests/esm-node-import-test/src/test/start.test.ts b/build-tests/esm-node-import-test/src/test/start.test.ts new file mode 100644 index 00000000000..75792fa12c6 --- /dev/null +++ b/build-tests/esm-node-import-test/src/test/start.test.ts @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { ChildProcess } from 'node:child_process'; + +import { Executable, PackageJsonLookup } from '@rushstack/node-core-library'; + +describe('ESM Node Import Test', () => { + it('should resolve @rushstack/node-core-library correctly under Node.js ESM', async () => { + const buildFolderPath: string | undefined = PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname); + if (!buildFolderPath) { + throw new Error('Unable to determine build folder path for test script.'); + } + + const result: ChildProcess = Executable.spawn(process.execPath, [`${buildFolderPath}/lib-esm/start.js`], { + currentWorkingDirectory: buildFolderPath + }); + + const { stderr, stdout, exitCode, signal } = await Executable.waitForExitAsync(result, { + encoding: 'utf8' + }); + + expect(stderr).toBe(''); + expect(stdout).toMatchSnapshot(); + expect(exitCode).toBe(0); + expect(signal).toBeNull(); + }); +}); diff --git a/build-tests/esm-node-import-test/tsconfig.json b/build-tests/esm-node-import-test/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/build-tests/esm-node-import-test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/build-tests/hashed-folder-copy-plugin-webpack4-test/assets/blue.png b/build-tests/hashed-folder-copy-plugin-webpack4-test/assets/blue.png deleted file mode 100644 index d9119489cb1..00000000000 Binary files a/build-tests/hashed-folder-copy-plugin-webpack4-test/assets/blue.png and /dev/null differ diff --git a/build-tests/hashed-folder-copy-plugin-webpack4-test/assets/green.png b/build-tests/hashed-folder-copy-plugin-webpack4-test/assets/green.png deleted file mode 100644 index 8ba4f25579b..00000000000 Binary files a/build-tests/hashed-folder-copy-plugin-webpack4-test/assets/green.png and /dev/null differ diff --git a/build-tests/hashed-folder-copy-plugin-webpack4-test/assets/red.png b/build-tests/hashed-folder-copy-plugin-webpack4-test/assets/red.png deleted file mode 100644 index b4e46a0538a..00000000000 Binary files a/build-tests/hashed-folder-copy-plugin-webpack4-test/assets/red.png and /dev/null differ diff --git a/build-tests/hashed-folder-copy-plugin-webpack4-test/assets/subfolder/yellow.png b/build-tests/hashed-folder-copy-plugin-webpack4-test/assets/subfolder/yellow.png deleted file mode 100644 index df897b6e041..00000000000 Binary files a/build-tests/hashed-folder-copy-plugin-webpack4-test/assets/subfolder/yellow.png and /dev/null differ diff --git a/build-tests/hashed-folder-copy-plugin-webpack4-test/config/heft.json b/build-tests/hashed-folder-copy-plugin-webpack4-test/config/heft.json deleted file mode 100644 index 867a0e043dd..00000000000 --- a/build-tests/hashed-folder-copy-plugin-webpack4-test/config/heft.json +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Defines configuration used by core Heft. - */ -{ - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", - - // TODO: Add comments - "phasesByName": { - "build": { - "cleanFiles": [{ "sourcePath": "dist-dev" }, { "sourcePath": "dist-prod" }, { "sourcePath": "lib" }], - "tasksByName": { - "typescript": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft-typescript-plugin" - } - }, - - "lint": { - "taskDependencies": ["typescript"], - "taskPlugin": { - "pluginPackage": "@rushstack/heft-lint-plugin" - } - }, - - "webpack4": { - "taskDependencies": ["typescript"], - "taskPlugin": { - "pluginPackage": "@rushstack/heft-webpack4-plugin" - } - } - } - } - } -} diff --git a/build-tests/hashed-folder-copy-plugin-webpack4-test/config/rush-project.json b/build-tests/hashed-folder-copy-plugin-webpack4-test/config/rush-project.json deleted file mode 100644 index c861eda9bd6..00000000000 --- a/build-tests/hashed-folder-copy-plugin-webpack4-test/config/rush-project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "operationSettings": [ - { - "operationName": "build", - "outputFolderNames": ["lib", "dist-dev", "dist-prod"] - } - ] -} diff --git a/build-tests/hashed-folder-copy-plugin-webpack4-test/package.json b/build-tests/hashed-folder-copy-plugin-webpack4-test/package.json deleted file mode 100644 index fbda790f905..00000000000 --- a/build-tests/hashed-folder-copy-plugin-webpack4-test/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "hashed-folder-copy-plugin-webpack4-test", - "description": "Building this project exercises @rushstack/hashed-folder-copy-plugin with Webpack 4.", - "version": "0.0.0", - "private": true, - "scripts": { - "build": "heft build --clean", - "serve": "heft build-watch --serve", - "_phase:build": "heft run --only build -- --clean" - }, - "devDependencies": { - "@rushstack/hashed-folder-copy-plugin": "workspace:*", - "@rushstack/heft": "workspace:*", - "@rushstack/heft-lint-plugin": "workspace:*", - "@rushstack/heft-typescript-plugin": "workspace:*", - "@rushstack/heft-webpack4-plugin": "workspace:*", - "@rushstack/webpack4-module-minifier-plugin": "workspace:*", - "@rushstack/set-webpack-public-path-plugin": "workspace:*", - "@types/webpack-env": "1.18.0", - "html-webpack-plugin": "~4.5.2", - "typescript": "~5.0.4", - "webpack-bundle-analyzer": "~4.5.0", - "webpack": "~4.44.2" - } -} diff --git a/build-tests/hashed-folder-copy-plugin-webpack4-test/src/index.ts b/build-tests/hashed-folder-copy-plugin-webpack4-test/src/index.ts deleted file mode 100644 index 20d1946ada6..00000000000 --- a/build-tests/hashed-folder-copy-plugin-webpack4-test/src/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ASSETS_BASE_URL2 } from './submodule'; - -const ASSETS_BASE_URL: string = requireFolder({ - outputFolder: 'assets_[hash]', - sources: [ - { - globsBase: '../assets', - globPatterns: ['**/*'] - } - ] -}); - -function appendImageToBody(url: string): void { - const image: HTMLImageElement = document.createElement('img'); - image.src = url; - document.body.appendChild(image); -} - -appendImageToBody(`${ASSETS_BASE_URL}/red.png`); -appendImageToBody(`${ASSETS_BASE_URL}/green.png`); -appendImageToBody(`${ASSETS_BASE_URL}/blue.png`); -appendImageToBody(`${ASSETS_BASE_URL}/subfolder/yellow.png`); - -appendImageToBody(`${ASSETS_BASE_URL2}/red.png`); -appendImageToBody(`${ASSETS_BASE_URL2}/green.png`); -appendImageToBody(`${ASSETS_BASE_URL2}/blue.png`); -appendImageToBody(`${ASSETS_BASE_URL2}/subfolder/yellow.png`); diff --git a/build-tests/hashed-folder-copy-plugin-webpack4-test/src/submodule.ts b/build-tests/hashed-folder-copy-plugin-webpack4-test/src/submodule.ts deleted file mode 100644 index d9a6e6fa8b0..00000000000 --- a/build-tests/hashed-folder-copy-plugin-webpack4-test/src/submodule.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const ASSETS_BASE_URL2: string = requireFolder({ - outputFolder: 'assets2_[hash]', - sources: [ - { - globsBase: '../assets', - globPatterns: ['**/*'] - } - ] -}); diff --git a/build-tests/hashed-folder-copy-plugin-webpack4-test/tsconfig.json b/build-tests/hashed-folder-copy-plugin-webpack4-test/tsconfig.json deleted file mode 100644 index 0c287924501..00000000000 --- a/build-tests/hashed-folder-copy-plugin-webpack4-test/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "declaration": true, - "declarationMap": true, - "experimentalDecorators": true, - "forceConsistentCasingInFileNames": true, - "inlineSources": true, - "jsx": "react", - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "module": "esnext", - "moduleResolution": "node", - "noUnusedLocals": true, - "sourceMap": true, - "strictNullChecks": true, - "target": "es5", - "types": ["webpack-env", "@rushstack/hashed-folder-copy-plugin/ambientTypes"], - - "outDir": "lib", - "rootDir": "src", - "rootDirs": ["src", "temp/loc-json-ts"] - }, - "include": ["src/**/*.ts", "src/**/*.tsx"] -} diff --git a/build-tests/hashed-folder-copy-plugin-webpack4-test/webpack.config.js b/build-tests/hashed-folder-copy-plugin-webpack4-test/webpack.config.js deleted file mode 100644 index c11507c5a9d..00000000000 --- a/build-tests/hashed-folder-copy-plugin-webpack4-test/webpack.config.js +++ /dev/null @@ -1,55 +0,0 @@ -'use strict'; - -const path = require('path'); -const webpack = require('webpack'); - -const { HashedFolderCopyPlugin } = require('@rushstack/hashed-folder-copy-plugin'); -const { ModuleMinifierPlugin, LocalMinifier } = require('@rushstack/webpack4-module-minifier-plugin'); -const { SetPublicPathPlugin } = require('@rushstack/set-webpack-public-path-plugin'); -const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); - -function generateConfiguration(mode, outputFolderName) { - return { - mode: mode, - entry: { - test: path.join(__dirname, 'lib', 'index.js') - }, - output: { - path: path.join(__dirname, outputFolderName), - filename: '[name]_[contenthash].js', - chunkFilename: '[id].[name]_[contenthash].js', - hashSalt: '2' - }, - optimization: { - minimizer: [ - new ModuleMinifierPlugin({ - minifier: new LocalMinifier() - }) - ] - }, - plugins: [ - new webpack.optimize.ModuleConcatenationPlugin(), - new HashedFolderCopyPlugin(), - new BundleAnalyzerPlugin({ - openAnalyzer: false, - analyzerMode: 'static', - reportFilename: path.resolve(__dirname, 'temp', 'stats.html'), - generateStatsFile: true, - statsFilename: path.resolve(__dirname, 'temp', 'stats.json'), - logLevel: 'error' - }), - new SetPublicPathPlugin({ - scriptName: { - useAssetName: true - } - }), - new HtmlWebpackPlugin() - ] - }; -} - -module.exports = [ - generateConfiguration('development', 'dist-dev'), - generateConfiguration('production', 'dist-prod') -]; diff --git a/build-tests/hashed-folder-copy-plugin-webpack5-test/config/heft.json b/build-tests/hashed-folder-copy-plugin-webpack5-test/config/heft.json index 7118ff12f83..09e8cc23d23 100644 --- a/build-tests/hashed-folder-copy-plugin-webpack5-test/config/heft.json +++ b/build-tests/hashed-folder-copy-plugin-webpack5-test/config/heft.json @@ -2,12 +2,13 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "dist-dev" }, { "sourcePath": "dist-prod" }, { "sourcePath": "lib" }], + "cleanFiles": [{ "includeGlobs": ["dist-dev", "dist-prod", "lib"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests/hashed-folder-copy-plugin-webpack5-test/config/rush-project.json b/build-tests/hashed-folder-copy-plugin-webpack5-test/config/rush-project.json index c861eda9bd6..0dbbaa83178 100644 --- a/build-tests/hashed-folder-copy-plugin-webpack5-test/config/rush-project.json +++ b/build-tests/hashed-folder-copy-plugin-webpack5-test/config/rush-project.json @@ -1,8 +1,10 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist-dev", "dist-prod"] + "operationName": "_phase:build", + "outputFolderNames": ["lib-esm", "dist-dev", "dist-prod"] } ] } diff --git a/build-tests/hashed-folder-copy-plugin-webpack5-test/package.json b/build-tests/hashed-folder-copy-plugin-webpack5-test/package.json index e6ea8507faa..3261d033cae 100644 --- a/build-tests/hashed-folder-copy-plugin-webpack5-test/package.json +++ b/build-tests/hashed-folder-copy-plugin-webpack5-test/package.json @@ -14,10 +14,10 @@ "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", "@rushstack/heft-webpack5-plugin": "workspace:*", - "@types/webpack-env": "1.18.0", - "html-webpack-plugin": "~4.5.2", - "typescript": "~5.0.4", + "@types/webpack-env": "1.18.8", + "html-webpack-plugin": "~5.5.0", + "typescript": "~5.8.2", "webpack-bundle-analyzer": "~4.5.0", - "webpack": "~5.80.0" + "webpack": "~5.105.2" } } diff --git a/build-tests/hashed-folder-copy-plugin-webpack5-test/src/index.ts b/build-tests/hashed-folder-copy-plugin-webpack5-test/src/index.ts index 20d1946ada6..72f4cd93c96 100644 --- a/build-tests/hashed-folder-copy-plugin-webpack5-test/src/index.ts +++ b/build-tests/hashed-folder-copy-plugin-webpack5-test/src/index.ts @@ -10,6 +10,16 @@ const ASSETS_BASE_URL: string = requireFolder({ ] }); +const HEFT_SRC_FILES_BASE_URL: string = requireFolder({ + outputFolder: 'heft_src_files_[hash]', + sources: [ + { + globsBase: '@rushstack/heft/src', + globPatterns: ['**/*'] + } + ] +}); + function appendImageToBody(url: string): void { const image: HTMLImageElement = document.createElement('img'); image.src = url; @@ -25,3 +35,5 @@ appendImageToBody(`${ASSETS_BASE_URL2}/red.png`); appendImageToBody(`${ASSETS_BASE_URL2}/green.png`); appendImageToBody(`${ASSETS_BASE_URL2}/blue.png`); appendImageToBody(`${ASSETS_BASE_URL2}/subfolder/yellow.png`); + +console.log(HEFT_SRC_FILES_BASE_URL); diff --git a/build-tests/hashed-folder-copy-plugin-webpack5-test/tsconfig.json b/build-tests/hashed-folder-copy-plugin-webpack5-test/tsconfig.json index 0c287924501..f75d74a5e8c 100644 --- a/build-tests/hashed-folder-copy-plugin-webpack5-test/tsconfig.json +++ b/build-tests/hashed-folder-copy-plugin-webpack5-test/tsconfig.json @@ -15,7 +15,7 @@ "target": "es5", "types": ["webpack-env", "@rushstack/hashed-folder-copy-plugin/ambientTypes"], - "outDir": "lib", + "outDir": "lib-esm", "rootDir": "src", "rootDirs": ["src", "temp/loc-json-ts"] }, diff --git a/build-tests/hashed-folder-copy-plugin-webpack5-test/webpack.config.js b/build-tests/hashed-folder-copy-plugin-webpack5-test/webpack.config.js index 69c5dfaff1c..2fe982efd88 100644 --- a/build-tests/hashed-folder-copy-plugin-webpack5-test/webpack.config.js +++ b/build-tests/hashed-folder-copy-plugin-webpack5-test/webpack.config.js @@ -11,7 +11,7 @@ function generateConfiguration(mode, outputFolderName) { return { mode: mode, entry: { - test: path.join(__dirname, 'lib', 'index.js') + test: path.join(__dirname, 'lib-esm', 'index.js') }, output: { path: path.join(__dirname, outputFolderName), @@ -35,7 +35,6 @@ function generateConfiguration(mode, outputFolderName) { } module.exports = [ - // // Build currently emits warnings - // generateConfiguration('development', 'dist-dev'), - // generateConfiguration('production', 'dist-prod') + generateConfiguration('development', 'dist-dev'), + generateConfiguration('production', 'dist-prod') ]; diff --git a/build-tests/heft-copy-files-test/config/heft.json b/build-tests/heft-copy-files-test/config/heft.json index a6b2fd7ac28..de160bcccda 100644 --- a/build-tests/heft-copy-files-test/config/heft.json +++ b/build-tests/heft-copy-files-test/config/heft.json @@ -2,56 +2,41 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { "cleanFiles": [ { - "sourcePath": "out-all" - }, - { - "sourcePath": "out-all-linked" - }, - { - "sourcePath": "out-all-flattened" - }, - { - "sourcePath": "out-all-except-for-images" - }, - { - "sourcePath": "out-images1" - }, - { - "sourcePath": "out-images2" - }, - { - "sourcePath": "out-images3" - }, - { - "sourcePath": "out-images4" - }, - { - "sourcePath": "out-images5" + "includeGlobs": [ + "out-all", + "out-all-linked", + "out-all-flattened", + "out-all-except-for-images", + "out-images1", + "out-images2", + "out-images3", + "out-images4", + "out-images5" + ] } ], "tasksByName": { "perform-copy": { - "taskEvent": { - "eventKind": "copyFiles", + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", "options": { "copyOperations": [ { "sourcePath": "src", - "destinationFolders": ["out-all"], - "includeGlobs": ["**/*"] + "destinationFolders": ["out-all"] }, { "sourcePath": "src", "destinationFolders": ["out-all-linked"], - "includeGlobs": ["**/*"], "hardlink": true }, { @@ -63,8 +48,7 @@ { "sourcePath": "src", "destinationFolders": ["out-all-except-for-images"], - "excludeGlobs": ["**/*.png", "**/*.jpg"], - "includeGlobs": ["**/*"] + "excludeGlobs": ["**/*.png", "**/*.jpg"] }, { "sourcePath": "src", diff --git a/build-tests/heft-copy-files-test/config/rush-project.json b/build-tests/heft-copy-files-test/config/rush-project.json index 358d65d92c6..e3df32c4de7 100644 --- a/build-tests/heft-copy-files-test/config/rush-project.json +++ b/build-tests/heft-copy-files-test/config/rush-project.json @@ -1,7 +1,9 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", + "operationName": "_phase:lite-build", "outputFolderNames": [ "out-all", "out-all-except-for-images", @@ -11,7 +13,8 @@ "out-images2", "out-images3", "out-images4", - "out-images5" + "out-images5", + "temp/build" ] } ] diff --git a/build-tests/heft-copy-files-test/package.json b/build-tests/heft-copy-files-test/package.json index 5258d276510..5ef07daffea 100644 --- a/build-tests/heft-copy-files-test/package.json +++ b/build-tests/heft-copy-files-test/package.json @@ -6,7 +6,7 @@ "license": "MIT", "scripts": { "build": "heft build --clean", - "_phase:build": "heft run --only build -- --clean" + "_phase:lite-build": "heft run --only build -- --clean" }, "devDependencies": { "@rushstack/heft": "workspace:*" diff --git a/build-tests/heft-example-lifecycle-plugin/config/heft.json b/build-tests/heft-example-lifecycle-plugin/config/heft.json new file mode 100644 index 00000000000..64d969be2eb --- /dev/null +++ b/build-tests/heft-example-lifecycle-plugin/config/heft.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + // TODO: Add comments + "phasesByName": { + "build": { + "cleanFiles": [{ "includeGlobs": ["dist", "lib"] }], + + "tasksByName": { + "typescript": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-typescript-plugin" + } + }, + "lint": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-lint-plugin" + } + } + } + } + } +} diff --git a/build-tests/heft-example-lifecycle-plugin/config/rush-project.json b/build-tests/heft-example-lifecycle-plugin/config/rush-project.json new file mode 100644 index 00000000000..a015d9f1f5a --- /dev/null +++ b/build-tests/heft-example-lifecycle-plugin/config/rush-project.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["lib-commonjs", "dist"] + } + ] +} diff --git a/build-tests/heft-example-lifecycle-plugin/eslint.config.js b/build-tests/heft-example-lifecycle-plugin/eslint.config.js new file mode 100644 index 00000000000..a05a76dc048 --- /dev/null +++ b/build-tests/heft-example-lifecycle-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-eslint-config/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-eslint-config/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-example-lifecycle-plugin/heft-plugin.json b/build-tests/heft-example-lifecycle-plugin/heft-plugin.json new file mode 100644 index 00000000000..7f81eb33b23 --- /dev/null +++ b/build-tests/heft-example-lifecycle-plugin/heft-plugin.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", + + "lifecyclePlugins": [ + { + "pluginName": "example-lifecycle-plugin", + "entryPoint": "./lib-commonjs/index" + } + ] +} diff --git a/build-tests/heft-example-lifecycle-plugin/package.json b/build-tests/heft-example-lifecycle-plugin/package.json new file mode 100644 index 00000000000..06fc79053ae --- /dev/null +++ b/build-tests/heft-example-lifecycle-plugin/package.json @@ -0,0 +1,22 @@ +{ + "name": "heft-example-lifecycle-plugin", + "description": "This is an example heft plugin for testing the lifecycle hooks", + "version": "1.0.0", + "private": true, + "main": "./lib-commonjs/index.js", + "types": "./lib-commonjs/index.d.ts", + "scripts": { + "build": "heft build --clean", + "start": "heft build-watch", + "_phase:build": "heft run --only build -- --clean" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "@rushstack/heft-lint-plugin": "workspace:*", + "@rushstack/heft-typescript-plugin": "workspace:*", + "@types/node": "20.17.19", + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*", + "typescript": "~5.8.2" + } +} diff --git a/build-tests/heft-example-lifecycle-plugin/src/index.ts b/build-tests/heft-example-lifecycle-plugin/src/index.ts new file mode 100644 index 00000000000..b1ed00244cd --- /dev/null +++ b/build-tests/heft-example-lifecycle-plugin/src/index.ts @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { + IHeftLifecyclePlugin, + IHeftLifecycleSession, + IHeftTaskFinishHookOptions, + IHeftTaskStartHookOptions, + IHeftPhaseFinishHookOptions, + IHeftPhaseStartHookOptions +} from '@rushstack/heft'; + +export const PLUGIN_NAME: 'example-lifecycle-plugin' = 'example-lifecycle-plugin'; + +export default class ExampleLifecyclePlugin implements IHeftLifecyclePlugin { + public apply(session: IHeftLifecycleSession): void { + const { logger } = session; + session.hooks.taskFinish.tap(PLUGIN_NAME, (options: IHeftTaskFinishHookOptions) => { + const { + operation: { + metadata: { task }, + state + } + } = options; + if (state) { + logger.terminal.writeLine( + `--- ${task.taskName} finished in ${state.stopwatch.duration.toFixed(2)}s ---` + ); + } + }); + + session.hooks.taskStart.tap(PLUGIN_NAME, (options: IHeftTaskStartHookOptions) => { + const { + operation: { + metadata: { task } + } + } = options; + logger.terminal.writeLine(`--- ${task.taskName} started ---`); + }); + + session.hooks.phaseStart.tap(PLUGIN_NAME, (options: IHeftPhaseStartHookOptions) => { + const { + operation: { + metadata: { phase } + } + } = options; + logger.terminal.writeLine(`--- ${phase.phaseName} started ---`); + }); + + session.hooks.phaseFinish.tap(PLUGIN_NAME, (options: IHeftPhaseFinishHookOptions) => { + const { + operation: { + metadata: { phase }, + duration + } + } = options; + logger.terminal.writeLine(`--- ${phase.phaseName} finished in ${duration.toFixed(2)}s ---`); + }); + } +} diff --git a/build-tests/heft-example-lifecycle-plugin/tsconfig.json b/build-tests/heft-example-lifecycle-plugin/tsconfig.json new file mode 100644 index 00000000000..e2f35108020 --- /dev/null +++ b/build-tests/heft-example-lifecycle-plugin/tsconfig.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib-commonjs", + "rootDir": "src", + + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictNullChecks": true, + "noUnusedLocals": true, + "types": ["node"], + + "module": "commonjs", + "target": "es2017", + "lib": ["es2017"] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/build-tests/heft-example-plugin-01/.eslintrc.js b/build-tests/heft-example-plugin-01/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/build-tests/heft-example-plugin-01/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/heft-example-plugin-01/config/heft.json b/build-tests/heft-example-plugin-01/config/heft.json index 44c384a982a..64d969be2eb 100644 --- a/build-tests/heft-example-plugin-01/config/heft.json +++ b/build-tests/heft-example-plugin-01/config/heft.json @@ -1,10 +1,11 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }], + "cleanFiles": [{ "includeGlobs": ["dist", "lib"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests/heft-example-plugin-01/config/rush-project.json b/build-tests/heft-example-plugin-01/config/rush-project.json index 247dc17187a..a015d9f1f5a 100644 --- a/build-tests/heft-example-plugin-01/config/rush-project.json +++ b/build-tests/heft-example-plugin-01/config/rush-project.json @@ -1,8 +1,10 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + "outputFolderNames": ["lib-commonjs", "dist"] } ] } diff --git a/build-tests/heft-example-plugin-01/eslint.config.js b/build-tests/heft-example-plugin-01/eslint.config.js new file mode 100644 index 00000000000..a05a76dc048 --- /dev/null +++ b/build-tests/heft-example-plugin-01/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-eslint-config/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-eslint-config/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-example-plugin-01/heft-plugin.json b/build-tests/heft-example-plugin-01/heft-plugin.json index 9c42646468d..41b9b2195a9 100644 --- a/build-tests/heft-example-plugin-01/heft-plugin.json +++ b/build-tests/heft-example-plugin-01/heft-plugin.json @@ -1,10 +1,10 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-plugin.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", "taskPlugins": [ { "pluginName": "example-plugin-01", - "entryPoint": "./lib/index" + "entryPoint": "./lib-commonjs/index" } ] } diff --git a/build-tests/heft-example-plugin-01/package.json b/build-tests/heft-example-plugin-01/package.json index 550986a466e..66e72848852 100644 --- a/build-tests/heft-example-plugin-01/package.json +++ b/build-tests/heft-example-plugin-01/package.json @@ -3,8 +3,8 @@ "description": "This is an example heft plugin that exposes hooks for other plugins", "version": "1.0.0", "private": true, - "main": "./lib/index.js", - "typings": "./lib/index.d.ts", + "main": "./lib-commonjs/index.js", + "types": "./lib-commonjs/index.d.ts", "scripts": { "build": "heft build --clean", "start": "heft build-watch", @@ -14,13 +14,13 @@ "tapable": "1.1.3" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", - "@types/node": "14.18.36", + "@types/node": "20.17.19", "@types/tapable": "1.0.6", - "eslint": "~8.7.0", - "typescript": "~5.0.4" + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*", + "typescript": "~5.8.2" } } diff --git a/build-tests/heft-example-plugin-01/src/index.ts b/build-tests/heft-example-plugin-01/src/index.ts index ab03fc5b2a1..8bc385908fa 100644 --- a/build-tests/heft-example-plugin-01/src/index.ts +++ b/build-tests/heft-example-plugin-01/src/index.ts @@ -1,4 +1,8 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import { SyncHook } from 'tapable'; + import type { IHeftTaskPlugin, IHeftTaskSession, diff --git a/build-tests/heft-example-plugin-01/tsconfig.json b/build-tests/heft-example-plugin-01/tsconfig.json index 2d179c7173f..e2f35108020 100644 --- a/build-tests/heft-example-plugin-01/tsconfig.json +++ b/build-tests/heft-example-plugin-01/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -20,6 +20,5 @@ "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-example-plugin-02/.eslintrc.js b/build-tests/heft-example-plugin-02/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/build-tests/heft-example-plugin-02/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/heft-example-plugin-02/config/heft.json b/build-tests/heft-example-plugin-02/config/heft.json index 44c384a982a..64d969be2eb 100644 --- a/build-tests/heft-example-plugin-02/config/heft.json +++ b/build-tests/heft-example-plugin-02/config/heft.json @@ -1,10 +1,11 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }], + "cleanFiles": [{ "includeGlobs": ["dist", "lib"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests/heft-example-plugin-02/config/rush-project.json b/build-tests/heft-example-plugin-02/config/rush-project.json index 247dc17187a..a015d9f1f5a 100644 --- a/build-tests/heft-example-plugin-02/config/rush-project.json +++ b/build-tests/heft-example-plugin-02/config/rush-project.json @@ -1,8 +1,10 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + "outputFolderNames": ["lib-commonjs", "dist"] } ] } diff --git a/build-tests/heft-example-plugin-02/eslint.config.js b/build-tests/heft-example-plugin-02/eslint.config.js new file mode 100644 index 00000000000..a05a76dc048 --- /dev/null +++ b/build-tests/heft-example-plugin-02/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-eslint-config/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-eslint-config/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-example-plugin-02/heft-plugin.json b/build-tests/heft-example-plugin-02/heft-plugin.json index ff0565a793a..92a56d2c5e1 100644 --- a/build-tests/heft-example-plugin-02/heft-plugin.json +++ b/build-tests/heft-example-plugin-02/heft-plugin.json @@ -1,10 +1,10 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-plugin.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", "taskPlugins": [ { "pluginName": "example-plugin-02", - "entryPoint": "./lib/index" + "entryPoint": "./lib-commonjs/index" } ] } diff --git a/build-tests/heft-example-plugin-02/package.json b/build-tests/heft-example-plugin-02/package.json index 37ce16acff8..9f8a6da74cc 100644 --- a/build-tests/heft-example-plugin-02/package.json +++ b/build-tests/heft-example-plugin-02/package.json @@ -3,8 +3,8 @@ "description": "This is an example heft plugin that taps the hooks exposed from heft-example-plugin-01", "version": "1.0.0", "private": true, - "main": "./lib/index.js", - "typings": "./lib/index.d.ts", + "main": "./lib-commonjs/index.js", + "types": "./lib-commonjs/index.d.ts", "scripts": { "build": "heft build --clean", "start": "heft build-watch", @@ -19,13 +19,13 @@ } }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", - "@types/node": "14.18.36", - "eslint": "~8.7.0", + "@types/node": "20.17.19", + "eslint": "~9.37.0", "heft-example-plugin-01": "workspace:*", - "typescript": "~5.0.4" + "local-eslint-config": "workspace:*", + "typescript": "~5.8.2" } } diff --git a/build-tests/heft-example-plugin-02/src/index.ts b/build-tests/heft-example-plugin-02/src/index.ts index 61b98622a27..d1382dd8527 100644 --- a/build-tests/heft-example-plugin-02/src/index.ts +++ b/build-tests/heft-example-plugin-02/src/index.ts @@ -1,6 +1,10 @@ -import type { IHeftTaskSession, HeftConfiguration, IHeftTaskPlugin } from '@rushstack/heft'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import type { PLUGIN_NAME as ExamplePlugin01Name, IExamplePlugin01Accessor } from 'heft-example-plugin-01'; +import type { IHeftTaskSession, HeftConfiguration, IHeftTaskPlugin } from '@rushstack/heft'; + export const PLUGIN_NAME: 'example-plugin-02' = 'example-plugin-02'; const EXAMPLE_PLUGIN_01_NAME: typeof ExamplePlugin01Name = 'example-plugin-01'; diff --git a/build-tests/heft-example-plugin-02/tsconfig.json b/build-tests/heft-example-plugin-02/tsconfig.json index 2d179c7173f..e2f35108020 100644 --- a/build-tests/heft-example-plugin-02/tsconfig.json +++ b/build-tests/heft-example-plugin-02/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -20,6 +20,5 @@ "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-fastify-test/.eslintrc.js b/build-tests/heft-fastify-test/.eslintrc.js deleted file mode 100644 index 60160b354c4..00000000000 --- a/build-tests/heft-fastify-test/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/node'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/heft-fastify-test/config/heft.json b/build-tests/heft-fastify-test/config/heft.json index ec34efe60b4..d77d65a9b32 100644 --- a/build-tests/heft-fastify-test/config/heft.json +++ b/build-tests/heft-fastify-test/config/heft.json @@ -2,12 +2,13 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }], + "cleanFiles": [{ "includeGlobs": ["dist", "lib"] }], + "tasksByName": { "typescript": { "taskPlugin": { @@ -19,16 +20,12 @@ "taskPlugin": { "pluginPackage": "@rushstack/heft-lint-plugin" } - } - } - }, - - "serve": { - "phaseDependencies": ["build"], - "tasksByName": { - "fastify": { - "taskEvent": { - "eventKind": "nodeService" + }, + "node-service": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "node-service-plugin" } } } diff --git a/build-tests/heft-fastify-test/config/node-service.json b/build-tests/heft-fastify-test/config/node-service.json index f2b04b0d2ac..62e904d49ce 100644 --- a/build-tests/heft-fastify-test/config/node-service.json +++ b/build-tests/heft-fastify-test/config/node-service.json @@ -3,11 +3,13 @@ * Heft will watch for changes and restart the service process whenever it gets rebuilt. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/node-service.schema.json" + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/node-service.schema.json" /** * Optionally specifies another JSON config file that this file extends from. This provides a way for standard * settings to be shared across multiple projects. + * + * To delete an inherited setting, set it to `null` in this file. */ // "extends": "base-project/config/serve-command.json", diff --git a/build-tests/heft-fastify-test/config/rush-project.json b/build-tests/heft-fastify-test/config/rush-project.json index 247dc17187a..d8e232986d3 100644 --- a/build-tests/heft-fastify-test/config/rush-project.json +++ b/build-tests/heft-fastify-test/config/rush-project.json @@ -1,8 +1,8 @@ { "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + "outputFolderNames": ["lib-commonjs", "dist"] } ] } diff --git a/build-tests/heft-fastify-test/eslint.config.js b/build-tests/heft-fastify-test/eslint.config.js new file mode 100644 index 00000000000..3d80b5cc649 --- /dev/null +++ b/build-tests/heft-fastify-test/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-eslint-config/flat/profile/node'); + +module.exports = [ + ...nodeProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-fastify-test/package.json b/build-tests/heft-fastify-test/package.json index b019158dc9f..4d5f783758d 100644 --- a/build-tests/heft-fastify-test/package.json +++ b/build-tests/heft-fastify-test/package.json @@ -3,23 +3,23 @@ "description": "This project tests Heft support for the Fastify framework for Node.js services", "version": "1.0.0", "private": true, - "main": "lib/index.js", + "main": "./lib/index.js", "license": "MIT", "scripts": { "build": "heft build --clean", - "start": "heft serve-watch", + "start": "heft build-watch --serve", "serve": "node lib/start.js", "_phase:build": "heft run --only build -- --clean" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "eslint": "~8.7.0", - "typescript": "~5.0.4" + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*", + "typescript": "~5.8.2" }, "dependencies": { "fastify": "~3.16.1" diff --git a/build-tests/heft-fastify-test/src/start.ts b/build-tests/heft-fastify-test/src/start.ts index 6672db451e6..99497d954e0 100644 --- a/build-tests/heft-fastify-test/src/start.ts +++ b/build-tests/heft-fastify-test/src/start.ts @@ -1,19 +1,24 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { fastify, FastifyInstance } from 'fastify'; +import { fastify, type FastifyInstance } from 'fastify'; +// eslint-disable-next-line no-console console.error('CHILD STARTING'); process.on('beforeExit', () => { + // eslint-disable-next-line no-console console.error('CHILD BEFOREEXIT'); }); process.on('exit', () => { + // eslint-disable-next-line no-console console.error('CHILD EXITED'); }); process.on('SIGINT', function () { + // eslint-disable-next-line no-console console.error('CHILD SIGINT'); }); process.on('SIGTERM', function () { + // eslint-disable-next-line no-console console.error('CHILD SIGTERM'); }); @@ -31,6 +36,7 @@ class MyApp { return { hello: 'world' }; }); + // eslint-disable-next-line no-console console.log('Listening on http://localhost:3000'); await this.server.listen(3000); } @@ -41,9 +47,12 @@ class MyApp { this.server.log.error(error); if (error.stack) { + // eslint-disable-next-line no-console console.error(error.stack); + // eslint-disable-next-line no-console console.error(); } + // eslint-disable-next-line no-console console.error('ERROR: ' + error.toString()); }); } diff --git a/build-tests/heft-fastify-test/tsconfig.json b/build-tests/heft-fastify-test/tsconfig.json index 845c0343e3c..a141c3e954b 100644 --- a/build-tests/heft-fastify-test/tsconfig.json +++ b/build-tests/heft-fastify-test/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -14,12 +14,11 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest", "node"], + "types": ["jest", "node"], "module": "commonjs", "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-jest-preset-test/.eslintrc.js b/build-tests/heft-jest-preset-test/.eslintrc.js deleted file mode 100644 index 60160b354c4..00000000000 --- a/build-tests/heft-jest-preset-test/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/node'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/heft-jest-preset-test/config/heft.json b/build-tests/heft-jest-preset-test/config/heft.json index c71f9427a45..0a9b64544ef 100644 --- a/build-tests/heft-jest-preset-test/config/heft.json +++ b/build-tests/heft-jest-preset-test/config/heft.json @@ -1,15 +1,15 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { "cleanFiles": [ - { "sourcePath": "dist" }, - { "sourcePath": "lib" }, - { "sourcePath": "lib-commonjs" }, - { "sourcePath": "temp" } + { + "includeGlobs": ["dist", "lib", "lib-commonjs", "temp"] + } ], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests/heft-jest-preset-test/config/jest.config.json b/build-tests/heft-jest-preset-test/config/jest.config.json index 19e885c9ff5..01e3d369776 100644 --- a/build-tests/heft-jest-preset-test/config/jest.config.json +++ b/build-tests/heft-jest-preset-test/config/jest.config.json @@ -37,7 +37,7 @@ // This is set to true in the preset for the "heft" package // "collectCoverage": false, - "coverageDirectory": "/temp/coverage", + "coverageDirectory": "/coverage", "collectCoverageFrom": [ "lib/**/*.cjs", @@ -55,9 +55,9 @@ // jest-string-mock-transform returns the filename, where Webpack would return a URL // When using the heft-jest-plugin, these will be replaced with the resolved module location "transform": { - "\\.(css|sass|scss)$": "../node_modules/@rushstack/heft-jest-plugin/lib/exports/jest-identity-mock-transform.js", + "\\.(css|sass|scss)$": "@rushstack/heft-jest-plugin/lib-commonjs/exports/jest-identity-mock-transform.js", - "\\.(aac|eot|gif|jpeg|jpg|m4a|mp3|mp4|oga|otf|png|svg|ttf|wav|webm|webp|woff|woff2)$": "../node_modules/@rushstack/heft-jest-plugin/lib/exports/jest-string-mock-transform.js" + "\\.(aac|eot|gif|jpeg|jpg|m4a|mp3|mp4|oga|otf|png|svg|ttf|wav|webm|webp|woff|woff2)$": "@rushstack/heft-jest-plugin/lib-commonjs/exports/jest-string-mock-transform.js" }, // The modulePathIgnorePatterns below accepts these sorts of paths: @@ -70,8 +70,5 @@ "moduleFileExtensions": ["cjs", "js", "json", "node"], // When using the heft-jest-plugin, these will be replaced with the resolved module location - "setupFiles": ["../node_modules/@rushstack/heft-jest-plugin/lib/exports/jest-global-setup.js"], - - // When using the heft-jest-plugin, these will be replaced with the resolved module location - "resolver": "../node_modules/@rushstack/heft-jest-plugin/lib/exports/jest-improved-resolver.js" + "resolver": "@rushstack/heft-jest-plugin/lib-commonjs/exports/jest-improved-resolver.js" } diff --git a/build-tests/heft-jest-preset-test/config/rush-project.json b/build-tests/heft-jest-preset-test/config/rush-project.json index 9af7b46f8af..030d8d0ff0e 100644 --- a/build-tests/heft-jest-preset-test/config/rush-project.json +++ b/build-tests/heft-jest-preset-test/config/rush-project.json @@ -1,12 +1,14 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", + "operationName": "_phase:build", "outputFolderNames": ["lib", "dist"] }, { - "operationName": "_phase:build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests/heft-jest-preset-test/config/typescript.json b/build-tests/heft-jest-preset-test/config/typescript.json index ae67b89ebf9..a1e6192d386 100644 --- a/build-tests/heft-jest-preset-test/config/typescript.json +++ b/build-tests/heft-jest-preset-test/config/typescript.json @@ -2,7 +2,7 @@ * Configures the TypeScript plugin for Heft. This plugin also manages linting. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", /** * If provided, emit these module kinds in addition to the modules specified in the tsconfig. diff --git a/build-tests/heft-jest-preset-test/config/verify-coverage.js b/build-tests/heft-jest-preset-test/config/verify-coverage.js index 5360470b42b..91c2555cd8f 100644 --- a/build-tests/heft-jest-preset-test/config/verify-coverage.js +++ b/build-tests/heft-jest-preset-test/config/verify-coverage.js @@ -1,6 +1,6 @@ const fs = require('fs'); // Verify that the coverage folder exists, since it would only exist // if the preset was used. -if (!fs.existsSync(`${__dirname}/../temp/coverage`)) { +if (!fs.existsSync(`${__dirname}/../coverage`)) { throw new Error('Coverage folder does not exist'); } diff --git a/build-tests/heft-jest-preset-test/eslint.config.js b/build-tests/heft-jest-preset-test/eslint.config.js new file mode 100644 index 00000000000..3d80b5cc649 --- /dev/null +++ b/build-tests/heft-jest-preset-test/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-eslint-config/flat/profile/node'); + +module.exports = [ + ...nodeProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-jest-preset-test/package.json b/build-tests/heft-jest-preset-test/package.json index e997ea59187..d96626daeba 100644 --- a/build-tests/heft-jest-preset-test/package.json +++ b/build-tests/heft-jest-preset-test/package.json @@ -10,14 +10,14 @@ "_phase:test": "heft run --only test -- --clean && node ./config/verify-coverage.js" }, "devDependencies": { - "@jest/types": "29.5.0", - "@rushstack/eslint-config": "workspace:*", + "@jest/types": "30.3.0", "@rushstack/heft": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", - "@types/heft-jest": "1.0.1", - "eslint": "~8.7.0", - "typescript": "~5.0.4" + "@types/jest": "30.0.0", + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*", + "typescript": "~5.8.2" } } diff --git a/build-tests/heft-jest-preset-test/tsconfig.json b/build-tests/heft-jest-preset-test/tsconfig.json index 0ad08cab8f8..77942323d5f 100644 --- a/build-tests/heft-jest-preset-test/tsconfig.json +++ b/build-tests/heft-jest-preset-test/tsconfig.json @@ -13,13 +13,12 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest"], + "types": ["jest"], "module": "esnext", "moduleResolution": "node", "target": "ES2015", "lib": ["ES2015"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-jest-reporters-test/.eslintrc.js b/build-tests/heft-jest-reporters-test/.eslintrc.js deleted file mode 100644 index 60160b354c4..00000000000 --- a/build-tests/heft-jest-reporters-test/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/node'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/heft-jest-reporters-test/config/heft.json b/build-tests/heft-jest-reporters-test/config/heft.json index 1da5919b562..304ccc96c03 100644 --- a/build-tests/heft-jest-reporters-test/config/heft.json +++ b/build-tests/heft-jest-reporters-test/config/heft.json @@ -1,10 +1,11 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }, { "sourcePath": "lib-commonjs" }], + "cleanFiles": [{ "includeGlobs": ["dist", "lib"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests/heft-jest-reporters-test/config/jest.config.json b/build-tests/heft-jest-reporters-test/config/jest.config.json index 9175f5c7e1c..294f12cc0bb 100644 --- a/build-tests/heft-jest-reporters-test/config/jest.config.json +++ b/build-tests/heft-jest-reporters-test/config/jest.config.json @@ -1,7 +1,8 @@ { "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", + "coverageDirectory": "/coverage", "reporters": ["default", "../lib/test/customJestReporter.cjs"], - "testMatch": ["/lib/**.test.cjs"], + "testMatch": ["/lib/**/*.test.cjs"], "collectCoverageFrom": [ "lib/**/*.cjs", "!lib/**/*.d.ts", diff --git a/build-tests/heft-jest-reporters-test/config/rush-project.json b/build-tests/heft-jest-reporters-test/config/rush-project.json index 247dc17187a..030d8d0ff0e 100644 --- a/build-tests/heft-jest-reporters-test/config/rush-project.json +++ b/build-tests/heft-jest-reporters-test/config/rush-project.json @@ -1,8 +1,14 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", + "operationName": "_phase:build", "outputFolderNames": ["lib", "dist"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests/heft-jest-reporters-test/config/typescript.json b/build-tests/heft-jest-reporters-test/config/typescript.json index ae67b89ebf9..a1e6192d386 100644 --- a/build-tests/heft-jest-reporters-test/config/typescript.json +++ b/build-tests/heft-jest-reporters-test/config/typescript.json @@ -2,7 +2,7 @@ * Configures the TypeScript plugin for Heft. This plugin also manages linting. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", /** * If provided, emit these module kinds in addition to the modules specified in the tsconfig. diff --git a/build-tests/heft-jest-reporters-test/eslint.config.js b/build-tests/heft-jest-reporters-test/eslint.config.js new file mode 100644 index 00000000000..3d80b5cc649 --- /dev/null +++ b/build-tests/heft-jest-reporters-test/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-eslint-config/flat/profile/node'); + +module.exports = [ + ...nodeProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-jest-reporters-test/package.json b/build-tests/heft-jest-reporters-test/package.json index 45d040f710c..11156f770fc 100644 --- a/build-tests/heft-jest-reporters-test/package.json +++ b/build-tests/heft-jest-reporters-test/package.json @@ -10,15 +10,16 @@ "_phase:test": "heft run --only test -- --clean" }, "devDependencies": { - "@jest/reporters": "~29.5.0", - "@jest/types": "29.5.0", - "@rushstack/eslint-config": "workspace:*", + "@jest/reporters": "~30.3.0", + "@jest/types": "30.3.0", "@rushstack/heft": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", - "@types/heft-jest": "1.0.1", - "eslint": "~8.7.0", - "typescript": "~5.0.4" + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*", + "typescript": "~5.8.2" } } diff --git a/build-tests/heft-jest-reporters-test/src/test/customJestReporter.ts b/build-tests/heft-jest-reporters-test/src/test/customJestReporter.ts index 8c0ec66138e..3f0166f6208 100644 --- a/build-tests/heft-jest-reporters-test/src/test/customJestReporter.ts +++ b/build-tests/heft-jest-reporters-test/src/test/customJestReporter.ts @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. + import type { Config } from '@jest/types'; import type { Reporter, @@ -14,22 +15,28 @@ module.exports = class CustomJestReporter implements Reporter { public constructor(globalConfig: Config.GlobalConfig, options: unknown) {} public onRunStart(results: AggregatedResult, options: ReporterOnStartOptions): void | Promise { + // eslint-disable-next-line no-console console.log(); + // eslint-disable-next-line no-console console.log(`################# Custom Jest reporter: Starting test run #################`); } public onTestStart(test: Test): void | Promise {} public onTestResult(test: Test, testResult: TestResult, results: AggregatedResult): void | Promise { + // eslint-disable-next-line no-console console.log('Custom Jest reporter: Reporting test result'); for (const result of testResult.testResults) { + // eslint-disable-next-line no-console console.log(`${result.title}: ${result.status}`); } } public onRunComplete(contexts: Set, results: AggregatedResult): void | Promise { + // eslint-disable-next-line no-console console.log('################# Completing test run #################'); + // eslint-disable-next-line no-console console.log(); } diff --git a/build-tests/heft-jest-reporters-test/tsconfig.json b/build-tests/heft-jest-reporters-test/tsconfig.json index ffed841a228..c14b5e13900 100644 --- a/build-tests/heft-jest-reporters-test/tsconfig.json +++ b/build-tests/heft-jest-reporters-test/tsconfig.json @@ -13,13 +13,12 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest"], + "types": ["jest"], "module": "esnext", "moduleResolution": "node", "target": "es2020", "lib": ["es2020"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-json-schema-typings-plugin-test/config/heft.json b/build-tests/heft-json-schema-typings-plugin-test/config/heft.json new file mode 100644 index 00000000000..f74df35f8af --- /dev/null +++ b/build-tests/heft-json-schema-typings-plugin-test/config/heft.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "cleanFiles": [{ "includeGlobs": ["temp/schema-dts"] }], + + "tasksByName": { + "json-schema-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-json-schema-typings-plugin", + "pluginName": "json-schema-typings-plugin", + "options": { + "srcFolder": "node_modules/@rushstack/node-core-library/src/test/test-data/test-schemas", + "generatedTsFolders": ["temp/schema-dts"] + } + } + }, + + "json-schema-typings-formatted": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-json-schema-typings-plugin", + "pluginName": "json-schema-typings-plugin", + "options": { + "srcFolder": "node_modules/@rushstack/node-core-library/src/test/test-data/test-schemas", + "generatedTsFolders": ["temp/schema-dts-formatted"], + "formatWithPrettier": true + } + } + } + } + } + } +} diff --git a/build-tests/heft-json-schema-typings-plugin-test/config/jest.config.json b/build-tests/heft-json-schema-typings-plugin-test/config/jest.config.json new file mode 100644 index 00000000000..d1749681d90 --- /dev/null +++ b/build-tests/heft-json-schema-typings-plugin-test/config/jest.config.json @@ -0,0 +1,3 @@ +{ + "extends": "local-node-rig/profiles/default/config/jest.config.json" +} diff --git a/build-tests/heft-json-schema-typings-plugin-test/config/rig.json b/build-tests/heft-json-schema-typings-plugin-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/heft-json-schema-typings-plugin-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/heft-json-schema-typings-plugin-test/eslint.config.js b/build-tests/heft-json-schema-typings-plugin-test/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/build-tests/heft-json-schema-typings-plugin-test/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-json-schema-typings-plugin-test/package.json b/build-tests/heft-json-schema-typings-plugin-test/package.json new file mode 100644 index 00000000000..5b36aa15f14 --- /dev/null +++ b/build-tests/heft-json-schema-typings-plugin-test/package.json @@ -0,0 +1,34 @@ +{ + "name": "heft-json-schema-typings-plugin-test", + "description": "This project illustrates configuring Jest reporters in a minimal Heft project", + "version": "1.0.0", + "private": true, + "scripts": { + "build": "heft build --clean", + "start": "heft build-watch", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "devDependencies": { + "@rushstack/heft-json-schema-typings-plugin": "workspace:*", + "@rushstack/heft": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + } +} diff --git a/build-tests/heft-json-schema-typings-plugin-test/src/test/JsonSchemaTypingsGenerator.test.ts b/build-tests/heft-json-schema-typings-plugin-test/src/test/JsonSchemaTypingsGenerator.test.ts new file mode 100644 index 00000000000..20a46ffc077 --- /dev/null +++ b/build-tests/heft-json-schema-typings-plugin-test/src/test/JsonSchemaTypingsGenerator.test.ts @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { FileSystem, type FolderItem, PackageJsonLookup } from '@rushstack/node-core-library'; + +async function getFolderItemsAsync( + absolutePath: string, + relativePath: string +): Promise> { + const folderQueue: [string, string][] = [[absolutePath, relativePath]]; + const results: [string, string][] = []; + for (const [folderAbsolutePath, folderRelativePath] of folderQueue) { + const folderItems: FolderItem[] = await FileSystem.readFolderItemsAsync(folderAbsolutePath); + for (const item of folderItems) { + const itemName: string = item.name; + const itemAbsolutePath: string = `${folderAbsolutePath}/${itemName}`; + const itemRelativePath: string = `${folderRelativePath}/${itemName}`; + if (item.isDirectory()) { + folderQueue.push([itemAbsolutePath, itemRelativePath]); + } else { + const itemContents: string = await FileSystem.readFileAsync(itemAbsolutePath); + results.push([itemRelativePath, itemContents]); + } + } + } + + results.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)); + return Object.fromEntries(results); +} + +describe('json-schema-typings-plugin', () => { + let rootFolder: string; + + beforeAll(() => { + const foundRootFolder: string | undefined = PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname); + if (!foundRootFolder) { + throw new Error('Could not find root folder for the test'); + } + + rootFolder = foundRootFolder; + }); + + it('should generate typings for JSON Schemas', async () => { + const folderItems: Record = await getFolderItemsAsync( + `${rootFolder}/temp/schema-dts`, + '.' + ); + expect(folderItems).toMatchSnapshot(); + }); + + it('should generate formatted typings for JSON Schemas', async () => { + const folderItems: Record = await getFolderItemsAsync( + `${rootFolder}/temp/schema-dts-formatted`, + '.' + ); + expect(folderItems).toMatchSnapshot(); + }); +}); diff --git a/build-tests/heft-json-schema-typings-plugin-test/src/test/__snapshots__/JsonSchemaTypingsGenerator.test.ts.snap b/build-tests/heft-json-schema-typings-plugin-test/src/test/__snapshots__/JsonSchemaTypingsGenerator.test.ts.snap new file mode 100644 index 00000000000..676fafea989 --- /dev/null +++ b/build-tests/heft-json-schema-typings-plugin-test/src/test/__snapshots__/JsonSchemaTypingsGenerator.test.ts.snap @@ -0,0 +1,369 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`json-schema-typings-plugin should generate formatted typings for JSON Schemas 1`] = ` +Object { + "./test-invalid-additional.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface TestInvalidAdditional { + [k: string]: unknown; +} +", + "./test-invalid-format.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface TestInvalidFormat { + [k: string]: unknown; +} +", + "./test-schema-draft-04.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface TestSchemaFile { + exampleString: string; + exampleLink?: string; + exampleArray: string[]; + /** + * Description for exampleOneOf - this is a very long description to show in an error message + */ + exampleOneOf?: Type1 | Type2; + exampleUniqueObjectArray?: { + field2?: string; + field3?: string; + }[]; +} +/** + * Description for type1 + */ +export interface Type1 { + /** + * Description for field1 + */ + field1: string; +} +/** + * Description for type2 + */ +export interface Type2 { + /** + * Description for field2 + */ + field2: string; + /** + * Description for field3 + */ + field3: string; +} +", + "./test-schema-draft-07.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface TestSchemaFile { + exampleString: string; + exampleLink?: string; + exampleArray: string[]; + /** + * Description for exampleOneOf - this is a very long description to show in an error message + */ + exampleOneOf?: Type1 | Type2; + exampleUniqueObjectArray?: { + field2?: string; + field3?: string; + }[]; +} +/** + * Description for type1 + */ +export interface Type1 { + /** + * Description for field1 + */ + field1: string; +} +/** + * Description for type2 + */ +export interface Type2 { + /** + * Description for field2 + */ + field2: string; + /** + * Description for field3 + */ + field3: string; +} +", + "./test-schema-invalid.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface HttpExampleComSchemasTestSchemaNestedChildSchemaJson { + [k: string]: unknown; +} +", + "./test-schema-nested-child.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface HttpExampleComSchemasTestSchemaNestedChildSchemaJson { + [k: string]: unknown; +} +", + "./test-schema-nested.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface TestSchemaFile { + exampleString: string; + exampleLink?: string; + exampleArray: string[]; + /** + * Description for exampleOneOf - this is a very long description to show in an error message + */ + exampleOneOf?: Type1 | Type2; + exampleUniqueObjectArray?: Type2[]; +} +/** + * Description for type1 + */ +export interface Type1 { + /** + * Description for field1 + */ + field1: string; +} +/** + * Description for type2 + */ +export interface Type2 { + /** + * Description for field2 + */ + field2: string; + /** + * Description for field3 + */ + field3: string; +} +", + "./test-schema.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface TestSchemaFile { + exampleString: string; + exampleLink?: string; + exampleArray: string[]; + /** + * Description for exampleOneOf - this is a very long description to show in an error message + */ + exampleOneOf?: Type1 | Type2; + exampleUniqueObjectArray?: { + field2?: string; + field3?: string; + }[]; +} +/** + * Description for type1 + */ +export interface Type1 { + /** + * Description for field1 + */ + field1: string; +} +/** + * Description for type2 + */ +export interface Type2 { + /** + * Description for field2 + */ + field2: string; + /** + * Description for field3 + */ + field3: string; +} +", + "./test-valid.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface TestValid { + [k: string]: unknown; +} +", +} +`; + +exports[`json-schema-typings-plugin should generate typings for JSON Schemas 1`] = ` +Object { + "./test-invalid-additional.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface TestInvalidAdditional { +[k: string]: unknown +} +", + "./test-invalid-format.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface TestInvalidFormat { +[k: string]: unknown +} +", + "./test-schema-draft-04.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface TestSchemaFile { +exampleString: string +exampleLink?: string +exampleArray: string[] +/** + * Description for exampleOneOf - this is a very long description to show in an error message + */ +exampleOneOf?: (Type1 | Type2) +exampleUniqueObjectArray?: { +field2?: string +field3?: string +}[] +} +/** + * Description for type1 + */ +export interface Type1 { +/** + * Description for field1 + */ +field1: string +} +/** + * Description for type2 + */ +export interface Type2 { +/** + * Description for field2 + */ +field2: string +/** + * Description for field3 + */ +field3: string +} +", + "./test-schema-draft-07.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface TestSchemaFile { +exampleString: string +exampleLink?: string +exampleArray: string[] +/** + * Description for exampleOneOf - this is a very long description to show in an error message + */ +exampleOneOf?: (Type1 | Type2) +exampleUniqueObjectArray?: { +field2?: string +field3?: string +}[] +} +/** + * Description for type1 + */ +export interface Type1 { +/** + * Description for field1 + */ +field1: string +} +/** + * Description for type2 + */ +export interface Type2 { +/** + * Description for field2 + */ +field2: string +/** + * Description for field3 + */ +field3: string +} +", + "./test-schema-invalid.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface HttpExampleComSchemasTestSchemaNestedChildSchemaJson { +[k: string]: unknown +} +", + "./test-schema-nested-child.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface HttpExampleComSchemasTestSchemaNestedChildSchemaJson { +[k: string]: unknown +} +", + "./test-schema-nested.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface TestSchemaFile { +exampleString: string +exampleLink?: string +exampleArray: string[] +/** + * Description for exampleOneOf - this is a very long description to show in an error message + */ +exampleOneOf?: (Type1 | Type2) +exampleUniqueObjectArray?: Type2[] +} +/** + * Description for type1 + */ +export interface Type1 { +/** + * Description for field1 + */ +field1: string +} +/** + * Description for type2 + */ +export interface Type2 { +/** + * Description for field2 + */ +field2: string +/** + * Description for field3 + */ +field3: string +} +", + "./test-schema.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface TestSchemaFile { +exampleString: string +exampleLink?: string +exampleArray: string[] +/** + * Description for exampleOneOf - this is a very long description to show in an error message + */ +exampleOneOf?: (Type1 | Type2) +exampleUniqueObjectArray?: { +field2?: string +field3?: string +}[] +} +/** + * Description for type1 + */ +export interface Type1 { +/** + * Description for field1 + */ +field1: string +} +/** + * Description for type2 + */ +export interface Type2 { +/** + * Description for field2 + */ +field2: string +/** + * Description for field3 + */ +field3: string +} +", + "./test-valid.schema.json.d.ts": "// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface TestValid { +[k: string]: unknown +} +", +} +`; diff --git a/build-tests/heft-json-schema-typings-plugin-test/tsconfig.json b/build-tests/heft-json-schema-typings-plugin-test/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/build-tests/heft-json-schema-typings-plugin-test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/build-tests/heft-minimal-rig-test/config/rush-project.json b/build-tests/heft-minimal-rig-test/config/rush-project.json deleted file mode 100644 index 247dc17187a..00000000000 --- a/build-tests/heft-minimal-rig-test/config/rush-project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "operationSettings": [ - { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] - } - ] -} diff --git a/build-tests/heft-minimal-rig-test/package.json b/build-tests/heft-minimal-rig-test/package.json index 59c07c47bea..8b4c68d9859 100644 --- a/build-tests/heft-minimal-rig-test/package.json +++ b/build-tests/heft-minimal-rig-test/package.json @@ -9,7 +9,7 @@ "_phase:build": "" }, "dependencies": { - "typescript": "~5.0.4", + "typescript": "~5.8.2", "@microsoft/api-extractor": "workspace:*", "@rushstack/heft-api-extractor-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", diff --git a/build-tests/heft-minimal-rig-test/profiles/default/config/heft.json b/build-tests/heft-minimal-rig-test/profiles/default/config/heft.json index 98d5d74694c..7ec35fead4f 100644 --- a/build-tests/heft-minimal-rig-test/profiles/default/config/heft.json +++ b/build-tests/heft-minimal-rig-test/profiles/default/config/heft.json @@ -1,10 +1,11 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }, { "sourcePath": "lib-commonjs" }], + "cleanFiles": [{ "includeGlobs": ["dist", "lib", "lib-commonjs"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests/heft-minimal-rig-test/profiles/default/config/jest.config.json b/build-tests/heft-minimal-rig-test/profiles/default/config/jest.config.json index 21ac001f531..441ad22d6d1 100644 --- a/build-tests/heft-minimal-rig-test/profiles/default/config/jest.config.json +++ b/build-tests/heft-minimal-rig-test/profiles/default/config/jest.config.json @@ -1,17 +1,3 @@ { - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", - - "roots": ["/lib-commonjs"], - - "testMatch": ["/lib-commonjs/**/*.test.js"], - - "collectCoverageFrom": [ - "lib-commonjs/**/*.js", - "!lib-commonjs/**/*.d.ts", - "!lib-commonjs/**/*.test.js", - "!lib-commonjs/**/test/**", - "!lib-commonjs/**/__tests__/**", - "!lib-commonjs/**/__fixtures__/**", - "!lib-commonjs/**/__mocks__/**" - ] + "extends": "@rushstack/heft-jest-plugin/includes/jest-web.config.json" } diff --git a/build-tests/heft-minimal-rig-test/profiles/default/config/rush-project.json b/build-tests/heft-minimal-rig-test/profiles/default/config/rush-project.json new file mode 100644 index 00000000000..0f74ea5a520 --- /dev/null +++ b/build-tests/heft-minimal-rig-test/profiles/default/config/rush-project.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["lib", "lib-commonjs", "dist"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] + } + ] +} diff --git a/build-tests/heft-minimal-rig-test/profiles/default/config/typescript.json b/build-tests/heft-minimal-rig-test/profiles/default/config/typescript.json index 0e4ecb0fd1d..5bb725c1781 100644 --- a/build-tests/heft-minimal-rig-test/profiles/default/config/typescript.json +++ b/build-tests/heft-minimal-rig-test/profiles/default/config/typescript.json @@ -2,7 +2,7 @@ * Configures the TypeScript plugin for Heft. This plugin also manages linting. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", /** * If provided, emit these module kinds in addition to the modules specified in the tsconfig. diff --git a/build-tests/heft-minimal-rig-usage-test/config/jest.config.json b/build-tests/heft-minimal-rig-usage-test/config/jest.config.json index 84dd06a10c3..ac52a5b3dc3 100644 --- a/build-tests/heft-minimal-rig-usage-test/config/jest.config.json +++ b/build-tests/heft-minimal-rig-usage-test/config/jest.config.json @@ -1,3 +1,11 @@ { - "extends": "heft-minimal-rig-test/profiles/default/config/jest.config.json" + "extends": "heft-minimal-rig-test/profiles/default/config/jest.config.json", + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests/heft-minimal-rig-usage-test/config/rush-project.json b/build-tests/heft-minimal-rig-usage-test/config/rush-project.json deleted file mode 100644 index 247dc17187a..00000000000 --- a/build-tests/heft-minimal-rig-usage-test/config/rush-project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "operationSettings": [ - { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] - } - ] -} diff --git a/build-tests/heft-minimal-rig-usage-test/package.json b/build-tests/heft-minimal-rig-usage-test/package.json index 2fd2b93d170..645bf2f314b 100644 --- a/build-tests/heft-minimal-rig-usage-test/package.json +++ b/build-tests/heft-minimal-rig-usage-test/package.json @@ -12,8 +12,8 @@ "devDependencies": { "@rushstack/heft": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", + "@types/jest": "30.0.0", + "@types/node": "20.17.19", "heft-minimal-rig-test": "workspace:*" } } diff --git a/build-tests/heft-minimal-rig-usage-test/tsconfig.json b/build-tests/heft-minimal-rig-usage-test/tsconfig.json index 219354d8484..d76275f96eb 100644 --- a/build-tests/heft-minimal-rig-usage-test/tsconfig.json +++ b/build-tests/heft-minimal-rig-usage-test/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "./node_modules/heft-minimal-rig-test/profiles/default/tsconfig-base.json", "compilerOptions": { - "types": ["heft-jest", "node"] + "types": ["jest", "node"] } } diff --git a/build-tests/heft-node-everything-esm-module-test/.eslintrc.cjs b/build-tests/heft-node-everything-esm-module-test/.eslintrc.cjs deleted file mode 100644 index a3975375e4b..00000000000 --- a/build-tests/heft-node-everything-esm-module-test/.eslintrc.cjs +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/node'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/heft-node-everything-esm-module-test/config/api-extractor-task.json b/build-tests/heft-node-everything-esm-module-test/config/api-extractor-task.json index 6df9b914aa9..860479fe991 100644 --- a/build-tests/heft-node-everything-esm-module-test/config/api-extractor-task.json +++ b/build-tests/heft-node-everything-esm-module-test/config/api-extractor-task.json @@ -5,7 +5,7 @@ * controlled by API Extractor's own "api-extractor.json" config file. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/api-extractor-task.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/api-extractor-task.schema.json", /** * If set to true, use the project's TypeScript compiler version for API Extractor's diff --git a/build-tests/heft-node-everything-esm-module-test/config/api-extractor.json b/build-tests/heft-node-everything-esm-module-test/config/api-extractor.json index b3969a325c1..890e2190b40 100644 --- a/build-tests/heft-node-everything-esm-module-test/config/api-extractor.json +++ b/build-tests/heft-node-everything-esm-module-test/config/api-extractor.json @@ -1,7 +1,7 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - "mainEntryPointFilePath": "/lib/index.d.ts", + "mainEntryPointFilePath": "/lib-dts/index.d.ts", "apiReport": { "enabled": true, "reportFolder": "/etc" diff --git a/build-tests/heft-node-everything-esm-module-test/config/heft.json b/build-tests/heft-node-everything-esm-module-test/config/heft.json index 5a87c229fd6..e2e0619b020 100644 --- a/build-tests/heft-node-everything-esm-module-test/config/heft.json +++ b/build-tests/heft-node-everything-esm-module-test/config/heft.json @@ -2,18 +2,25 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", "phasesByName": { "build": { - "cleanFiles": [ - { "sourcePath": "dist" }, - { "sourcePath": "lib" }, - { "sourcePath": "lib-esnext" }, - { "sourcePath": "lib-umd" } - ], + "cleanFiles": [{ "includeGlobs": ["dist", "lib-commonjs", "lib", "lib-esm", "lib-esnext", "lib-umd"] }], + "tasksByName": { + "text-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "source-assets-plugin", + "options": { + "configType": "file", + "configFileName": "source-assets.json" + } + } + }, "typescript": { + "taskDependencies": ["text-typings"], "taskPlugin": { "pluginPackage": "@rushstack/heft-typescript-plugin" } diff --git a/build-tests/heft-node-everything-esm-module-test/config/jest.config.json b/build-tests/heft-node-everything-esm-module-test/config/jest.config.json index b6f305ec886..c0687c6d488 100644 --- a/build-tests/heft-node-everything-esm-module-test/config/jest.config.json +++ b/build-tests/heft-node-everything-esm-module-test/config/jest.config.json @@ -1,3 +1,11 @@ { - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json" + "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests/heft-node-everything-esm-module-test/config/rush-project.json b/build-tests/heft-node-everything-esm-module-test/config/rush-project.json index a9116e7e1f5..95af43b193f 100644 --- a/build-tests/heft-node-everything-esm-module-test/config/rush-project.json +++ b/build-tests/heft-node-everything-esm-module-test/config/rush-project.json @@ -1,8 +1,14 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["dist", "lib", "lib-esnext", "lib-umd"] + "operationName": "_phase:build", + "outputFolderNames": ["dist", "lib-commonjs", "lib-esm", "lib-umd"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests/heft-node-everything-esm-module-test/config/source-assets.json b/build-tests/heft-node-everything-esm-module-test/config/source-assets.json new file mode 100644 index 00000000000..7d3efa3121a --- /dev/null +++ b/build-tests/heft-node-everything-esm-module-test/config/source-assets.json @@ -0,0 +1,6 @@ +{ + "fileExtensions": [".html"], + "cjsOutputFolders": ["lib-commonjs"], + "esmOutputFolders": ["lib-esm"], + "generatedTsFolders": ["temp/text-typings"] +} diff --git a/build-tests/heft-node-everything-esm-module-test/config/typescript.json b/build-tests/heft-node-everything-esm-module-test/config/typescript.json index 793ed73ef63..d7616572a6c 100644 --- a/build-tests/heft-node-everything-esm-module-test/config/typescript.json +++ b/build-tests/heft-node-everything-esm-module-test/config/typescript.json @@ -2,7 +2,7 @@ * Configures the TypeScript plugin for Heft. This plugin also manages linting. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", /** * If provided, emit these module kinds in addition to the modules specified in the tsconfig. @@ -11,7 +11,7 @@ "additionalModuleKindsToEmit": [ { "moduleKind": "esnext", - "outFolderName": "lib-esnext" + "outFolderName": "lib-esm" }, { "moduleKind": "umd", diff --git a/build-tests/heft-node-everything-esm-module-test/eslint.config.cjs b/build-tests/heft-node-everything-esm-module-test/eslint.config.cjs new file mode 100644 index 00000000000..3d80b5cc649 --- /dev/null +++ b/build-tests/heft-node-everything-esm-module-test/eslint.config.cjs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-eslint-config/flat/profile/node'); + +module.exports = [ + ...nodeProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-node-everything-esm-module-test/etc/heft-node-everything-esm-module-test.api.md b/build-tests/heft-node-everything-esm-module-test/etc/heft-node-everything-esm-module-test.api.md index 9d5bceee423..86361b68da7 100644 --- a/build-tests/heft-node-everything-esm-module-test/etc/heft-node-everything-esm-module-test.api.md +++ b/build-tests/heft-node-everything-esm-module-test/etc/heft-node-everything-esm-module-test.api.md @@ -4,6 +4,9 @@ ```ts +// @public +export const templateContent: string; + // @public (undocumented) export class TestClass { } diff --git a/build-tests/heft-node-everything-esm-module-test/package.json b/build-tests/heft-node-everything-esm-module-test/package.json index ca8f0338307..d5047720980 100644 --- a/build-tests/heft-node-everything-esm-module-test/package.json +++ b/build-tests/heft-node-everything-esm-module-test/package.json @@ -3,7 +3,7 @@ "description": "Building this project tests every task and config file for Heft when targeting the Node.js runtime when configured to use ESM module support", "version": "1.0.0", "private": true, - "main": "lib/index.js", + "main": "./lib/index.js", "type": "module", "license": "MIT", "scripts": { @@ -13,19 +13,19 @@ }, "devDependencies": { "@microsoft/api-extractor": "workspace:*", - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/heft-api-extractor-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", + "@rushstack/heft-static-asset-typings-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "eslint": "~8.7.0", + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "eslint": "~9.37.0", "heft-example-plugin-01": "workspace:*", "heft-example-plugin-02": "workspace:*", + "local-eslint-config": "workspace:*", "tslint": "~5.20.1", - "tslint-microsoft-contrib": "~6.2.0", - "typescript": "~5.0.4" + "typescript": "~5.8.2" } } diff --git a/build-tests/heft-node-everything-esm-module-test/src/exampleTemplate.html b/build-tests/heft-node-everything-esm-module-test/src/exampleTemplate.html new file mode 100644 index 00000000000..44ab8a9ae74 --- /dev/null +++ b/build-tests/heft-node-everything-esm-module-test/src/exampleTemplate.html @@ -0,0 +1 @@ +

This is an example template imported as a text asset.

diff --git a/build-tests/heft-node-everything-esm-module-test/src/index.ts b/build-tests/heft-node-everything-esm-module-test/src/index.ts index 15a2bae17e3..22b50eb31b1 100644 --- a/build-tests/heft-node-everything-esm-module-test/src/index.ts +++ b/build-tests/heft-node-everything-esm-module-test/src/index.ts @@ -1,7 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import exampleTemplate from './exampleTemplate.html'; + +/** + * The content of exampleTemplate.html, imported as a text asset. + * @public + */ +export const templateContent: string = exampleTemplate; + /** * @public */ -export class TestClass {} // tslint:disable-line:export-name +export class TestClass {} diff --git a/build-tests/heft-node-everything-esm-module-test/tsconfig.json b/build-tests/heft-node-everything-esm-module-test/tsconfig.json index 845c0343e3c..018269cb4ec 100644 --- a/build-tests/heft-node-everything-esm-module-test/tsconfig.json +++ b/build-tests/heft-node-everything-esm-module-test/tsconfig.json @@ -2,8 +2,9 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", - "rootDir": "src", + "outDir": "lib-commonjs", + "declarationDir": "lib-dts", + "rootDirs": ["src", "temp/text-typings"], "forceConsistentCasingInFileNames": true, "jsx": "react", @@ -14,12 +15,11 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest", "node"], + "types": ["jest", "node"], "module": "commonjs", "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-node-everything-esm-module-test/tslint.json b/build-tests/heft-node-everything-esm-module-test/tslint.json index f55613b66cc..56dfd9f2bb6 100644 --- a/build-tests/heft-node-everything-esm-module-test/tslint.json +++ b/build-tests/heft-node-everything-esm-module-test/tslint.json @@ -1,13 +1,11 @@ { "$schema": "http://json.schemastore.org/tslint", - "rulesDirectory": ["tslint-microsoft-contrib"], "rules": { "class-name": true, "comment-format": [true, "check-space"], "curly": true, "eofline": false, - "export-name": true, "forin": true, "indent": [true, "spaces", 2], "interface-name": true, @@ -36,22 +34,18 @@ ] } ], - "missing-optional-annotation": true, "no-arg": true, "no-any": true, "no-bitwise": true, "no-consecutive-blank-lines": true, "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], - "no-constant-condition": true, "no-construct": true, "no-debugger": true, "no-duplicate-switch-case": true, - "no-duplicate-parameter-names": true, "no-duplicate-variable": true, "no-empty": true, "no-eval": true, "no-floating-promises": true, - "no-function-expression": true, "no-inferrable-types": false, "no-internal-module": true, "no-null-keyword": true, @@ -59,9 +53,7 @@ "no-string-literal": true, "no-switch-case-fall-through": true, "no-trailing-whitespace": true, - "no-unnecessary-semicolons": true, "no-unused-expression": true, - "no-with-statement": true, "no-var-keyword": true, "object-literal-sort-keys": false, "one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"], @@ -96,7 +88,6 @@ } ], "use-isnan": true, - "use-named-parameter": true, "variable-name": [true, "check-format", "allow-leading-underscore", "ban-keywords"], "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"] } diff --git a/build-tests/heft-node-everything-test/.eslintrc.js b/build-tests/heft-node-everything-test/.eslintrc.js deleted file mode 100644 index 60160b354c4..00000000000 --- a/build-tests/heft-node-everything-test/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/node'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/heft-node-everything-test/config/api-extractor-task.json b/build-tests/heft-node-everything-test/config/api-extractor-task.json index 6df9b914aa9..860479fe991 100644 --- a/build-tests/heft-node-everything-test/config/api-extractor-task.json +++ b/build-tests/heft-node-everything-test/config/api-extractor-task.json @@ -5,7 +5,7 @@ * controlled by API Extractor's own "api-extractor.json" config file. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/api-extractor-task.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/api-extractor-task.schema.json", /** * If set to true, use the project's TypeScript compiler version for API Extractor's diff --git a/build-tests/heft-node-everything-test/config/api-extractor.json b/build-tests/heft-node-everything-test/config/api-extractor.json index e451cf705d0..d12edb988a5 100644 --- a/build-tests/heft-node-everything-test/config/api-extractor.json +++ b/build-tests/heft-node-everything-test/config/api-extractor.json @@ -1,7 +1,7 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - "mainEntryPointFilePath": "/lib/index.d.ts", + "mainEntryPointFilePath": "/lib-dts/index.d.ts", "apiReport": { "enabled": true, "reportFolder": "/etc" diff --git a/build-tests/heft-node-everything-test/config/heft.json b/build-tests/heft-node-everything-test/config/heft.json index b3e11933a92..ba8a4e8318b 100644 --- a/build-tests/heft-node-everything-test/config/heft.json +++ b/build-tests/heft-node-everything-test/config/heft.json @@ -2,19 +2,37 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "heftPlugins": [ + { + "pluginPackage": "heft-example-lifecycle-plugin" + } + ], // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [ - { "sourcePath": "dist" }, - { "sourcePath": "lib" }, - { "sourcePath": "lib-esnext" }, - { "sourcePath": "lib-umd" } - ], + "cleanFiles": [{ "includeGlobs": ["dist", "lib-commonjs", "lib", "lib-esm", "lib-esnext", "lib-umd"] }], + "tasksByName": { + "text-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "source-assets-plugin", + "options": { + "configType": "inline", + "config": { + "fileExtensions": [".html"], + "cjsOutputFolders": ["lib-commonjs"], + "esmOutputFolders": ["lib-esm"], + "generatedTsFolders": ["temp/text-typings"] + } + } + } + }, "typescript": { + "taskDependencies": ["text-typings"], "taskPlugin": { "pluginPackage": "@rushstack/heft-typescript-plugin" } @@ -30,6 +48,16 @@ "taskPlugin": { "pluginPackage": "@rushstack/heft-api-extractor-plugin" } + }, + "metadata-test": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "run-script-plugin", + "options": { + "scriptPath": "./lib-commonjs/test-metadata.js" + } + } } } }, diff --git a/build-tests/heft-node-everything-test/config/jest.config.json b/build-tests/heft-node-everything-test/config/jest.config.json index b6f305ec886..d22ada97183 100644 --- a/build-tests/heft-node-everything-test/config/jest.config.json +++ b/build-tests/heft-node-everything-test/config/jest.config.json @@ -1,3 +1,14 @@ { - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json" + "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", + + "roots": ["/lib-commonjs"], + "testMatch": ["/lib-commonjs/**/*.test.{cjs,js}"], + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests/heft-node-everything-test/config/rush-project.json b/build-tests/heft-node-everything-test/config/rush-project.json index a9116e7e1f5..50339f80875 100644 --- a/build-tests/heft-node-everything-test/config/rush-project.json +++ b/build-tests/heft-node-everything-test/config/rush-project.json @@ -1,8 +1,14 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["dist", "lib", "lib-esnext", "lib-umd"] + "operationName": "_phase:build", + "outputFolderNames": ["dist", "lib-commonjs", "lib-esm", "lib-umd", "lib-dts", "temp/text-typings"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests/heft-node-everything-test/config/source-assets.json b/build-tests/heft-node-everything-test/config/source-assets.json new file mode 100644 index 00000000000..e35e877e016 --- /dev/null +++ b/build-tests/heft-node-everything-test/config/source-assets.json @@ -0,0 +1,4 @@ +{ + "fileExtensions": [".html"], + "generatedTsFolder": "temp/text-typings" +} diff --git a/build-tests/heft-node-everything-test/config/typescript.json b/build-tests/heft-node-everything-test/config/typescript.json index 793ed73ef63..d7616572a6c 100644 --- a/build-tests/heft-node-everything-test/config/typescript.json +++ b/build-tests/heft-node-everything-test/config/typescript.json @@ -2,7 +2,7 @@ * Configures the TypeScript plugin for Heft. This plugin also manages linting. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", /** * If provided, emit these module kinds in addition to the modules specified in the tsconfig. @@ -11,7 +11,7 @@ "additionalModuleKindsToEmit": [ { "moduleKind": "esnext", - "outFolderName": "lib-esnext" + "outFolderName": "lib-esm" }, { "moduleKind": "umd", diff --git a/build-tests/heft-node-everything-test/eslint.config.js b/build-tests/heft-node-everything-test/eslint.config.js new file mode 100644 index 00000000000..3d80b5cc649 --- /dev/null +++ b/build-tests/heft-node-everything-test/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-eslint-config/flat/profile/node'); + +module.exports = [ + ...nodeProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-node-everything-test/etc/heft-node-everything-test.api.md b/build-tests/heft-node-everything-test/etc/heft-node-everything-test.api.md index 339f2d2315e..906e342ef52 100644 --- a/build-tests/heft-node-everything-test/etc/heft-node-everything-test.api.md +++ b/build-tests/heft-node-everything-test/etc/heft-node-everything-test.api.md @@ -4,11 +4,13 @@ ```ts +// @public +export const templateContent: string; + // @public (undocumented) export class TestClass { } - // (No @packageDocumentation comment for this package) ``` diff --git a/build-tests/heft-node-everything-test/package.json b/build-tests/heft-node-everything-test/package.json index 2ad7036df62..c3f7da1fe32 100644 --- a/build-tests/heft-node-everything-test/package.json +++ b/build-tests/heft-node-everything-test/package.json @@ -3,28 +3,31 @@ "description": "Building this project tests every task and config file for Heft when targeting the Node.js runtime", "version": "1.0.0", "private": true, - "main": "lib/index.js", + "main": "./lib/index.js", "license": "MIT", "scripts": { "build": "heft build --clean", "_phase:build": "heft run --only build -- --clean", - "_phase:test": "heft run --only test -- --clean" + "_phase:build:incremental": "heft run --only build --", + "_phase:test": "heft run --only test -- --clean", + "_phase:test:incremental": "heft run --only test --" }, "devDependencies": { "@microsoft/api-extractor": "workspace:*", - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/heft-api-extractor-plugin": "workspace:*", + "@rushstack/heft-static-asset-typings-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "eslint": "~8.7.0", + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "eslint": "~9.37.0", + "heft-example-lifecycle-plugin": "workspace:*", "heft-example-plugin-01": "workspace:*", "heft-example-plugin-02": "workspace:*", + "local-eslint-config": "workspace:*", "tslint": "~5.20.1", - "tslint-microsoft-contrib": "~6.2.0", - "typescript": "~5.0.4" + "typescript": "~5.8.2" } } diff --git a/build-tests/heft-node-everything-test/src/exampleTemplate.html b/build-tests/heft-node-everything-test/src/exampleTemplate.html new file mode 100644 index 00000000000..bba457469cc --- /dev/null +++ b/build-tests/heft-node-everything-test/src/exampleTemplate.html @@ -0,0 +1,9 @@ + + + + Example Template + + +

Hello, world!

+ + diff --git a/build-tests/heft-node-everything-test/src/index.ts b/build-tests/heft-node-everything-test/src/index.ts index 15a2bae17e3..22b50eb31b1 100644 --- a/build-tests/heft-node-everything-test/src/index.ts +++ b/build-tests/heft-node-everything-test/src/index.ts @@ -1,7 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import exampleTemplate from './exampleTemplate.html'; + +/** + * The content of exampleTemplate.html, imported as a text asset. + * @public + */ +export const templateContent: string = exampleTemplate; + /** * @public */ -export class TestClass {} // tslint:disable-line:export-name +export class TestClass {} diff --git a/build-tests/heft-node-everything-test/src/test-metadata.ts b/build-tests/heft-node-everything-test/src/test-metadata.ts new file mode 100644 index 00000000000..1fd618c599a --- /dev/null +++ b/build-tests/heft-node-everything-test/src/test-metadata.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as fs from 'node:fs/promises'; + +import type { IRunScriptOptions } from '@rushstack/heft'; + +export async function runAsync({ heftConfiguration: { buildFolderPath } }: IRunScriptOptions): Promise { + const metadataFolder: string = `${buildFolderPath}/.rush/temp/operation/_phase_build`; + + await fs.mkdir(metadataFolder, { recursive: true }); + + await fs.writeFile(`${metadataFolder}/test.txt`, new Date().toString(), 'utf-8'); +} diff --git a/build-tests/heft-node-everything-test/src/test/ExampleTest.test.ts b/build-tests/heft-node-everything-test/src/test/ExampleTest.test.ts index ccae242d321..26fa4ae4bb1 100644 --- a/build-tests/heft-node-everything-test/src/test/ExampleTest.test.ts +++ b/build-tests/heft-node-everything-test/src/test/ExampleTest.test.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import { templateContent } from '../index'; + interface IInterface { element: string; } @@ -20,4 +22,10 @@ describe('Example Test', () => { }; expect(interfaceInstance).toBeTruthy(); }); + + it('Correctly imports text assets', () => { + expect(typeof templateContent).toBe('string'); + expect(templateContent).toContain('Example Template'); + expect(templateContent).toContain('Hello, world!'); + }); }); diff --git a/build-tests/heft-node-everything-test/src/test/__snapshots__/ExampleTest.test.ts.snap b/build-tests/heft-node-everything-test/src/test/__snapshots__/ExampleTest.test.ts.snap index 1ca0d3b526a..8fdcc5d1121 100644 --- a/build-tests/heft-node-everything-test/src/test/__snapshots__/ExampleTest.test.ts.snap +++ b/build-tests/heft-node-everything-test/src/test/__snapshots__/ExampleTest.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`Example Test Correctly handles snapshots 1`] = ` Object { diff --git a/build-tests/heft-node-everything-test/tsconfig.json b/build-tests/heft-node-everything-test/tsconfig.json index 845c0343e3c..347de5f3a4e 100644 --- a/build-tests/heft-node-everything-test/tsconfig.json +++ b/build-tests/heft-node-everything-test/tsconfig.json @@ -2,8 +2,10 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", + "declarationDir": "lib-dts", "rootDir": "src", + "rootDirs": ["src", "temp/text-typings"], "forceConsistentCasingInFileNames": true, "jsx": "react", @@ -14,12 +16,11 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest", "node"], + "types": ["jest", "node"], "module": "commonjs", "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-node-everything-test/tslint.json b/build-tests/heft-node-everything-test/tslint.json index f55613b66cc..56dfd9f2bb6 100644 --- a/build-tests/heft-node-everything-test/tslint.json +++ b/build-tests/heft-node-everything-test/tslint.json @@ -1,13 +1,11 @@ { "$schema": "http://json.schemastore.org/tslint", - "rulesDirectory": ["tslint-microsoft-contrib"], "rules": { "class-name": true, "comment-format": [true, "check-space"], "curly": true, "eofline": false, - "export-name": true, "forin": true, "indent": [true, "spaces", 2], "interface-name": true, @@ -36,22 +34,18 @@ ] } ], - "missing-optional-annotation": true, "no-arg": true, "no-any": true, "no-bitwise": true, "no-consecutive-blank-lines": true, "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], - "no-constant-condition": true, "no-construct": true, "no-debugger": true, "no-duplicate-switch-case": true, - "no-duplicate-parameter-names": true, "no-duplicate-variable": true, "no-empty": true, "no-eval": true, "no-floating-promises": true, - "no-function-expression": true, "no-inferrable-types": false, "no-internal-module": true, "no-null-keyword": true, @@ -59,9 +53,7 @@ "no-string-literal": true, "no-switch-case-fall-through": true, "no-trailing-whitespace": true, - "no-unnecessary-semicolons": true, "no-unused-expression": true, - "no-with-statement": true, "no-var-keyword": true, "object-literal-sort-keys": false, "one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"], @@ -96,7 +88,6 @@ } ], "use-isnan": true, - "use-named-parameter": true, "variable-name": [true, "check-format", "allow-leading-underscore", "ban-keywords"], "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"] } diff --git a/build-tests/heft-parameter-plugin-test/config/heft.json b/build-tests/heft-parameter-plugin-test/config/heft.json index f7aea5d035a..e0863aedb18 100644 --- a/build-tests/heft-parameter-plugin-test/config/heft.json +++ b/build-tests/heft-parameter-plugin-test/config/heft.json @@ -2,12 +2,13 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "lib" }], + "cleanFiles": [{ "includeGlobs": ["lib"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests/heft-parameter-plugin-test/config/jest.config.json b/build-tests/heft-parameter-plugin-test/config/jest.config.json index b6f305ec886..8ece84202f7 100644 --- a/build-tests/heft-parameter-plugin-test/config/jest.config.json +++ b/build-tests/heft-parameter-plugin-test/config/jest.config.json @@ -1,3 +1,24 @@ { - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json" + "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", + + "roots": ["/lib-commonjs"], + + "testMatch": ["/lib-commonjs/**/*.test.js"], + "collectCoverageFrom": [ + "lib-commonjs/**/*.js", + "!lib-commonjs/**/*.d.ts", + "!lib-commonjs/**/*.test.js", + "!lib-commonjs/**/test/**", + "!lib-commonjs/**/__tests__/**", + "!lib-commonjs/**/__fixtures__/**", + "!lib-commonjs/**/__mocks__/**" + ], + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests/heft-parameter-plugin-test/config/rush-project.json b/build-tests/heft-parameter-plugin-test/config/rush-project.json index 317ec878bc3..b3729624aed 100644 --- a/build-tests/heft-parameter-plugin-test/config/rush-project.json +++ b/build-tests/heft-parameter-plugin-test/config/rush-project.json @@ -1,8 +1,14 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib"] + "operationName": "_phase:build", + "outputFolderNames": ["lib-commonjs"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests/heft-parameter-plugin-test/package.json b/build-tests/heft-parameter-plugin-test/package.json index e07306fda24..32e408f7aab 100644 --- a/build-tests/heft-parameter-plugin-test/package.json +++ b/build-tests/heft-parameter-plugin-test/package.json @@ -10,13 +10,14 @@ "_phase:test": "heft run --only test -- --clean --custom-parameter --custom-integer-parameter 5 --custom-integer-list-parameter 6 --custom-integer-list-parameter 7 --custom-string-parameter test --custom-string-list-parameter eevee --custom-string-list-parameter togepi --custom-string-list-parameter mareep --custom-choice-parameter red --custom-choice-list-parameter totodile --custom-choice-list-parameter gudetama --custom-choice-list-parameter wobbuffet" }, "devDependencies": { - "@rushstack/heft": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", + "@rushstack/heft": "workspace:*", "@rushstack/node-core-library": "workspace:*", - "@types/heft-jest": "1.0.1", + "@types/jest": "30.0.0", + "@types/node": "20.17.19", "heft-parameter-plugin": "workspace:*", - "typescript": "~5.0.4" + "typescript": "~5.8.2" } } diff --git a/build-tests/heft-parameter-plugin-test/src/test/customParameter.test.ts b/build-tests/heft-parameter-plugin-test/src/test/customParameter.test.ts index e48c506becd..791e0d7bcbc 100644 --- a/build-tests/heft-parameter-plugin-test/src/test/customParameter.test.ts +++ b/build-tests/heft-parameter-plugin-test/src/test/customParameter.test.ts @@ -4,7 +4,7 @@ import { FileSystem } from '@rushstack/node-core-library'; describe('CustomParameterOutput', () => { it('parses command line arguments and prints output.', async () => { const outputContent: string = await FileSystem.readFileAsync( - `${dirname(dirname(__dirname))}/temp/test.write-parameters/custom_output.txt` + `${dirname(dirname(__dirname))}/temp/test/write-parameters/custom_output.txt` ); expect(outputContent).toBe( 'customIntegerParameter: 5\n' + diff --git a/build-tests/heft-parameter-plugin-test/tsconfig.json b/build-tests/heft-parameter-plugin-test/tsconfig.json index 98464e7dfed..f93a8eee353 100644 --- a/build-tests/heft-parameter-plugin-test/tsconfig.json +++ b/build-tests/heft-parameter-plugin-test/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -14,12 +14,11 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest"], + "types": ["jest"], "module": "commonjs", "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-parameter-plugin/.eslintrc.js b/build-tests/heft-parameter-plugin/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/build-tests/heft-parameter-plugin/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/heft-parameter-plugin/config/heft.json b/build-tests/heft-parameter-plugin/config/heft.json index 8b3ddf50355..8e9ad151053 100644 --- a/build-tests/heft-parameter-plugin/config/heft.json +++ b/build-tests/heft-parameter-plugin/config/heft.json @@ -2,11 +2,12 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "lib" }], + "cleanFiles": [{ "includeGlobs": ["lib"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests/heft-parameter-plugin/config/rush-project.json b/build-tests/heft-parameter-plugin/config/rush-project.json index 317ec878bc3..3437c4d7730 100644 --- a/build-tests/heft-parameter-plugin/config/rush-project.json +++ b/build-tests/heft-parameter-plugin/config/rush-project.json @@ -1,8 +1,10 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib"] + "operationName": "_phase:build", + "outputFolderNames": ["lib-commonjs"] } ] } diff --git a/build-tests/heft-parameter-plugin/eslint.config.js b/build-tests/heft-parameter-plugin/eslint.config.js new file mode 100644 index 00000000000..a05a76dc048 --- /dev/null +++ b/build-tests/heft-parameter-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-eslint-config/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-eslint-config/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-parameter-plugin/heft-plugin.json b/build-tests/heft-parameter-plugin/heft-plugin.json index f8726f45880..aedf5173751 100644 --- a/build-tests/heft-parameter-plugin/heft-plugin.json +++ b/build-tests/heft-parameter-plugin/heft-plugin.json @@ -1,10 +1,10 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-plugin.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", "taskPlugins": [ { "pluginName": "heft-parameter-plugin", - "entryPoint": "./lib/index", + "entryPoint": "./lib-commonjs/index", "parameterScope": "heft-parameter-plugin", "parameters": [ { diff --git a/build-tests/heft-parameter-plugin/package.json b/build-tests/heft-parameter-plugin/package.json index 0352b0a8b76..a6f8ca86524 100644 --- a/build-tests/heft-parameter-plugin/package.json +++ b/build-tests/heft-parameter-plugin/package.json @@ -3,20 +3,20 @@ "description": "This project contains a Heft plugin that adds a custom parameter to built-in actions", "version": "1.0.0", "private": true, - "main": "lib/index.js", + "main": "./lib/index.js", "license": "MIT", "scripts": { "build": "heft build --clean", "_phase:build": "heft run --only build -- --clean" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", - "@types/node": "14.18.36", - "eslint": "~8.7.0", - "typescript": "~5.0.4" + "@types/node": "20.17.19", + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*", + "typescript": "~5.8.2" }, "dependencies": { "@rushstack/node-core-library": "workspace:*" diff --git a/build-tests/heft-parameter-plugin/tsconfig.json b/build-tests/heft-parameter-plugin/tsconfig.json index 2d179c7173f..e2f35108020 100644 --- a/build-tests/heft-parameter-plugin/tsconfig.json +++ b/build-tests/heft-parameter-plugin/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -20,6 +20,5 @@ "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-rspack-everything-test/config/heft.json b/build-tests/heft-rspack-everything-test/config/heft.json new file mode 100644 index 00000000000..4b784c91939 --- /dev/null +++ b/build-tests/heft-rspack-everything-test/config/heft.json @@ -0,0 +1,80 @@ +/** + * Defines configuration used by core Heft. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + // TODO: Add comments + "phasesByName": { + "build": { + "cleanFiles": [{ "includeGlobs": ["dist", "lib", "lib-commonjs", "temp/image-typings"] }], + + "tasksByName": { + "image-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "resource-assets-plugin", + "options": { + "configType": "inline", + "config": { + "fileExtensions": [".png"], + "generatedTsFolders": ["temp/image-typings"] + } + } + } + }, + "typescript": { + "taskDependencies": ["image-typings"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-typescript-plugin" + } + }, + "lint": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-lint-plugin" + } + }, + "rspack": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-rspack-plugin" + } + } + } + }, + + "test": { + "phaseDependencies": ["build"], + "tasksByName": { + "jest": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-jest-plugin" + } + } + } + }, + + "trust-dev-cert": { + "tasksByName": { + "trust-dev-cert": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-dev-cert-plugin", + "pluginName": "trust-dev-certificate-plugin" + } + } + } + }, + + "untrust-dev-cert": { + "tasksByName": { + "untrust-dev-cert": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-dev-cert-plugin", + "pluginName": "untrust-dev-certificate-plugin" + } + } + } + } + } +} diff --git a/build-tests/heft-rspack-everything-test/config/jest.config.json b/build-tests/heft-rspack-everything-test/config/jest.config.json new file mode 100644 index 00000000000..210c642e7a8 --- /dev/null +++ b/build-tests/heft-rspack-everything-test/config/jest.config.json @@ -0,0 +1,15 @@ +{ + "extends": "@rushstack/heft-jest-plugin/includes/jest-web.config.json", + + "roots": ["/lib-commonjs"], + "testMatch": ["/lib-commonjs/**/*.test.js"], + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8", + "resolver": "@rushstack/heft-jest-plugin/lib-commonjs/exports/jest-node-modules-symlink-resolver" +} diff --git a/build-tests/heft-rspack-everything-test/config/rush-project.json b/build-tests/heft-rspack-everything-test/config/rush-project.json new file mode 100644 index 00000000000..f1de027644c --- /dev/null +++ b/build-tests/heft-rspack-everything-test/config/rush-project.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["lib-esm", "lib-commonjs", "dist", "temp/image-typings"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] + } + ] +} diff --git a/build-tests/heft-rspack-everything-test/config/typescript.json b/build-tests/heft-rspack-everything-test/config/typescript.json new file mode 100644 index 00000000000..86a32ee3552 --- /dev/null +++ b/build-tests/heft-rspack-everything-test/config/typescript.json @@ -0,0 +1,55 @@ +/** + * Configures the TypeScript plugin for Heft. This plugin also manages linting. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", + + /** + * If provided, emit these module kinds in addition to the modules specified in the tsconfig. + * Note that this option only applies to the main tsconfig.json configuration. + */ + "additionalModuleKindsToEmit": [ + // { + // /** + // * (Required) Must be one of "commonjs", "amd", "umd", "system", "es2015", "esnext" + // */ + // "moduleKind": "amd", + // + // /** + // * (Required) The name of the folder where the output will be written. + // */ + // "outFolderName": "lib-amd" + // } + { + "moduleKind": "commonjs", + "outFolderName": "lib-commonjs" + } + ], + + /** + * Describes the way files should be statically coped from src to TS output folders + */ + "staticAssetsToCopy": { + /** + * File extensions that should be copied from the src folder to the destination folder(s). + */ + "fileExtensions": [".css", ".png"] + + /** + * Glob patterns that should be explicitly included. + */ + // "includeGlobs": [ + // "some/path/*.js" + // ], + + /** + * Glob patterns that should be explicitly excluded. This takes precedence over globs listed + * in "includeGlobs" and files that match the file extensions provided in "fileExtensions". + */ + // "excludeGlobs": [ + // "some/path/*.css" + // ] + }, + + "onlyResolveSymlinksInNodeModules": true +} diff --git a/build-tests/heft-rspack-everything-test/eslint.config.js b/build-tests/heft-rspack-everything-test/eslint.config.js new file mode 100644 index 00000000000..5a9df48909b --- /dev/null +++ b/build-tests/heft-rspack-everything-test/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-eslint-config/flat/profile/web-app'); + +module.exports = [ + ...webAppProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-rspack-everything-test/package.json b/build-tests/heft-rspack-everything-test/package.json new file mode 100644 index 00000000000..62e05f4e2ab --- /dev/null +++ b/build-tests/heft-rspack-everything-test/package.json @@ -0,0 +1,30 @@ +{ + "name": "heft-rspack-everything-test", + "description": "Building this project tests every task and config file for Heft when targeting the web browser runtime using Rspack", + "version": "1.0.0", + "private": true, + "scripts": { + "build": "heft build --clean", + "start": "heft build-watch", + "serve": "heft build-watch --serve", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "devDependencies": { + "@rushstack/heft-dev-cert-plugin": "workspace:*", + "@rushstack/heft-static-asset-typings-plugin": "workspace:*", + "@rushstack/heft-jest-plugin": "workspace:*", + "@rushstack/heft-lint-plugin": "workspace:*", + "@rushstack/heft-typescript-plugin": "workspace:*", + "@rushstack/heft-rspack-plugin": "workspace:*", + "@rushstack/heft": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "@rushstack/rush-sdk": "workspace:*", + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*", + "typescript": "~5.8.2", + "@rspack/core": "~1.6.0-beta.0" + } +} diff --git a/build-tests/heft-rspack-everything-test/rspack.config.mjs b/build-tests/heft-rspack-everything-test/rspack.config.mjs new file mode 100644 index 00000000000..4b82c88b2a2 --- /dev/null +++ b/build-tests/heft-rspack-everything-test/rspack.config.mjs @@ -0,0 +1,51 @@ +// @ts-check +/** @typedef {import('@rushstack/heft-rspack-plugin').IRspackConfiguration} IRspackConfiguration */ +'use strict'; + +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { HtmlRspackPlugin, SwcJsMinimizerRspackPlugin } from '@rspack/core'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +/** @type {IRspackConfiguration} */ +const config = { + mode: 'production', + module: { + rules: [ + { + test: /\.png$/i, + type: 'asset/resource' + }, + { + test: /\.js$/, + enforce: 'pre' + // TODO: enable after rspack drops a new version with this commit https://github.com/web-infra-dev/rspack/commit/d31f2fa07179d72eee99b21db517946d08073767 + // extractSourceMap: true + } + ] + }, + target: ['web', 'es2020'], + resolve: { + extensions: ['.js', '.json'] + }, + entry: { + 'heft-test-A': resolve(__dirname, 'lib-esm', 'indexA.js'), + 'heft-test-B': resolve(__dirname, 'lib-esm', 'indexB.js') + }, + output: { + path: resolve(__dirname, 'dist'), + filename: '[name]_[contenthash].js', + chunkFilename: '[id].[name]_[contenthash].js', + assetModuleFilename: '[name]_[contenthash][ext][query]' + }, + devtool: 'source-map', + optimization: { + minimize: true, + minimizer: [new SwcJsMinimizerRspackPlugin({})] + }, + plugins: [new HtmlRspackPlugin()] +}; + +export default config; diff --git a/build-tests/heft-rspack-everything-test/rspack.dev.config.mjs b/build-tests/heft-rspack-everything-test/rspack.dev.config.mjs new file mode 100644 index 00000000000..36981719750 --- /dev/null +++ b/build-tests/heft-rspack-everything-test/rspack.dev.config.mjs @@ -0,0 +1,45 @@ +// @ts-check +/** @typedef {import('@rushstack/heft-rspack-plugin').IRspackConfiguration} IRspackConfiguration */ +'use strict'; + +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { HtmlRspackPlugin } from '@rspack/core'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +/** @type {IRspackConfiguration} */ +const config = { + mode: 'none', + module: { + rules: [ + { + test: /\.png$/i, + type: 'asset/resource' + } + ] + }, + target: ['web', 'es2020'], + resolve: { + extensions: ['.js', '.json'] + }, + entry: { + 'heft-test-A': resolve(__dirname, 'lib-commonjs', 'indexA.js'), + 'heft-test-B': resolve(__dirname, 'lib-commonjs', 'indexB.js') + }, + output: { + path: resolve(__dirname, 'dist'), + filename: '[name]_[contenthash].js', + chunkFilename: '[id].[name]_[contenthash].js', + assetModuleFilename: '[name]_[contenthash][ext][query]' + }, + devtool: 'source-map', + optimization: { + minimize: false, + minimizer: [] + }, + plugins: [new HtmlRspackPlugin()] +}; + +export default config; diff --git a/build-tests/heft-rspack-everything-test/src/chunks/ChunkClass.ts b/build-tests/heft-rspack-everything-test/src/chunks/ChunkClass.ts new file mode 100644 index 00000000000..ddbf7d148c7 --- /dev/null +++ b/build-tests/heft-rspack-everything-test/src/chunks/ChunkClass.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export class ChunkClass { + public doStuff(): void { + // eslint-disable-next-line no-console + console.log('CHUNK'); + } + + public getImageUrl(): string { + // eslint-disable-next-line @typescript-eslint/no-require-imports + return require('./image.png'); + } +} diff --git a/build-tests/heft-rspack-everything-test/src/chunks/image.png b/build-tests/heft-rspack-everything-test/src/chunks/image.png new file mode 100644 index 00000000000..a028cfeb69f Binary files /dev/null and b/build-tests/heft-rspack-everything-test/src/chunks/image.png differ diff --git a/build-tests/heft-rspack-everything-test/src/copiedAsset.css b/build-tests/heft-rspack-everything-test/src/copiedAsset.css new file mode 100644 index 00000000000..e9747e441d3 --- /dev/null +++ b/build-tests/heft-rspack-everything-test/src/copiedAsset.css @@ -0,0 +1 @@ +/* THIS FILE SHOULD GET COPIED TO THE "lib" FOLDER BECAUSE IT IS REFERENCED IN copy-static-assets.json */ diff --git a/build-tests/heft-rspack-everything-test/src/indexA.ts b/build-tests/heft-rspack-everything-test/src/indexA.ts new file mode 100644 index 00000000000..6b4db719843 --- /dev/null +++ b/build-tests/heft-rspack-everything-test/src/indexA.ts @@ -0,0 +1,10 @@ +/* eslint-disable */ +/* tslint:disable */ +import(/* webpackChunkName: 'chunk' */ './chunks/ChunkClass') + .then(({ ChunkClass }) => { + const chunk: any = new ChunkClass(); + chunk.doStuff(); + }) + .catch((e) => { + console.log('Error: ' + e.message); + }); diff --git a/build-tests/heft-rspack-everything-test/src/indexB.ts b/build-tests/heft-rspack-everything-test/src/indexB.ts new file mode 100644 index 00000000000..2bcd3820a4b --- /dev/null +++ b/build-tests/heft-rspack-everything-test/src/indexB.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// eslint-disable-next-line no-console +console.log('dostuff'); diff --git a/build-tests/heft-rspack-everything-test/src/test/ExampleTest.test.ts b/build-tests/heft-rspack-everything-test/src/test/ExampleTest.test.ts new file mode 100644 index 00000000000..565432eacf5 --- /dev/null +++ b/build-tests/heft-rspack-everything-test/src/test/ExampleTest.test.ts @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { ChunkClass } from '../chunks/ChunkClass'; + +describe('Example Test', () => { + it('Correctly tests stuff', () => { + expect(true).toBeTruthy(); + }); + + it('Correctly handles images', () => { + const chunkClass: ChunkClass = new ChunkClass(); + expect(() => chunkClass.getImageUrl()).not.toThrow(); + expect(typeof chunkClass.getImageUrl()).toBe('string'); + }); +}); diff --git a/build-tests/heft-rspack-everything-test/src/test/Image.test.ts b/build-tests/heft-rspack-everything-test/src/test/Image.test.ts new file mode 100644 index 00000000000..c336d269d60 --- /dev/null +++ b/build-tests/heft-rspack-everything-test/src/test/Image.test.ts @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import image from '../chunks/image.png'; + +describe('Image Test', () => { + it('correctly handles urls for images', () => { + expect(image).toBe('lib-commonjs/chunks/image.png'); + }); +}); diff --git a/build-tests/heft-rspack-everything-test/src/test/SourceMapTest.test.ts b/build-tests/heft-rspack-everything-test/src/test/SourceMapTest.test.ts new file mode 100644 index 00000000000..2c500cdd547 --- /dev/null +++ b/build-tests/heft-rspack-everything-test/src/test/SourceMapTest.test.ts @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { FileSystem, PackageJsonLookup } from '@rushstack/node-core-library'; + +interface IMap { + sources?: string[]; + file?: string; + sourcesContent?: string[]; + names?: string[]; +} + +interface IMapValue { + mapFileName: string; + mapObject: IMap; +} + +interface IMapTestEntry { + name: string; + mapRegex: RegExp; + map: IMapValue | undefined; +} + +const mapTests: IMapTestEntry[] = [ + { + name: 'Test-A', + mapRegex: /^heft-test-A_[\w\d]*\.js.map$/, + map: undefined + }, + { + name: 'Test-B', + mapRegex: /^heft-test-B_[\w\d]*\.js.map$/, + map: undefined + }, + { + name: 'Chunk', + mapRegex: /^[\w\d\.]*chunk_[\w\d]*\.js.map$/, + map: undefined + } +]; + +const lookup: PackageJsonLookup = new PackageJsonLookup(); +lookup.tryGetPackageFolderFor(__dirname); +const thisProjectFolder: string | undefined = lookup.tryGetPackageFolderFor(__dirname); +if (!thisProjectFolder) { + throw new Error('Cannot find project folder'); +} +const distEntries: string[] = FileSystem.readFolderItemNames(thisProjectFolder + '/dist'); +for (const distEntry of distEntries) { + for (const test of mapTests) { + if (test.mapRegex.test(distEntry)) { + const mapText: string = FileSystem.readFile(`${thisProjectFolder}/dist/${distEntry}`); + const mapObject: IMap = JSON.parse(mapText); + test.map = { + mapFileName: distEntry, + mapObject + }; + } + } +} + +describe('Source Maps', () => { + for (const test of mapTests) { + mapValueCheck(test); + } +}); + +function mapValueCheck(entry: IMapTestEntry): void { + it(`${entry.name} has map value`, () => { + expect(entry.map).toBeTruthy(); + }); + + if (!entry.map) { + return; + } + + const map: IMapValue = entry.map; + + it(`${entry.name} has filename matching file attribute`, () => { + if (map.mapObject.file) { + expect(map.mapFileName).toMatch(`${map.mapObject.file}.map`); + } + }); + + const properties: (keyof IMap)[] = ['sources', 'file', 'sourcesContent', 'names']; + for (const property of properties) { + it(`${map.mapFileName} has ${property} property`, () => { + expect(map.mapObject[property]).toBeTruthy(); + }); + } + + it(`${entry.name} has sources and sourcesContent arrays of the same length`, () => { + if (map.mapObject.sourcesContent && map.mapObject.sources) { + let numSrcs: number = 0; + for (const source of map.mapObject.sources) { + if (source) { + numSrcs++; + } + } + + let numContents: number = 0; + for (const content of map.mapObject.sourcesContent) { + if (content) { + numContents++; + } + } + expect(numSrcs).toEqual(numContents); + } + }); + + it(`${entry.name} has a source that matches the sourceFileRegex`, () => { + if (map.mapObject.sources) { + expect(map.mapObject.sources).toMatchSnapshot(); + } + }); +} diff --git a/build-tests/heft-rspack-everything-test/src/test/__snapshots__/SourceMapTest.test.ts.snap b/build-tests/heft-rspack-everything-test/src/test/__snapshots__/SourceMapTest.test.ts.snap new file mode 100644 index 00000000000..775451f0731 --- /dev/null +++ b/build-tests/heft-rspack-everything-test/src/test/__snapshots__/SourceMapTest.test.ts.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`Source Maps Chunk has a source that matches the sourceFileRegex 1`] = ` +Array [ + "webpack://heft-rspack-everything-test/./lib-esm/chunks/ChunkClass.js", +] +`; + +exports[`Source Maps Test-A has a source that matches the sourceFileRegex 1`] = ` +Array [ + "webpack://heft-rspack-everything-test/webpack/runtime/jsonp_chunk_loading", + "webpack://heft-rspack-everything-test/webpack/runtime/define_property_getters", + "webpack://heft-rspack-everything-test/webpack/runtime/ensure_chunk", + "webpack://heft-rspack-everything-test/webpack/runtime/get javascript chunk filename", + "webpack://heft-rspack-everything-test/webpack/runtime/global", + "webpack://heft-rspack-everything-test/webpack/runtime/has_own_property", + "webpack://heft-rspack-everything-test/webpack/runtime/load_script", + "webpack://heft-rspack-everything-test/webpack/runtime/rspack_version", + "webpack://heft-rspack-everything-test/webpack/runtime/auto_public_path", + "webpack://heft-rspack-everything-test/webpack/runtime/rspack_unique_id", + "webpack://heft-rspack-everything-test/./lib-esm/indexA.js", +] +`; + +exports[`Source Maps Test-B has a source that matches the sourceFileRegex 1`] = ` +Array [ + "webpack://heft-rspack-everything-test/webpack/runtime/rspack_version", + "webpack://heft-rspack-everything-test/webpack/runtime/rspack_unique_id", + "webpack://heft-rspack-everything-test/./lib-esm/indexB.js", +] +`; diff --git a/build-tests/heft-rspack-everything-test/tsconfig.json b/build-tests/heft-rspack-everything-test/tsconfig.json new file mode 100644 index 00000000000..898cdf85cda --- /dev/null +++ b/build-tests/heft-rspack-everything-test/tsconfig.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib-esm", + "rootDirs": ["src", "temp/image-typings"], + + "forceConsistentCasingInFileNames": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "jsx": "react", + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictNullChecks": true, + "noUnusedLocals": true, + "types": ["jest", "node"], + + "module": "esnext", + "moduleResolution": "node", + "target": "es5", + "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/build-tests/heft-sass-test/.eslintrc.js b/build-tests/heft-sass-test/.eslintrc.js deleted file mode 100644 index 288eaa16364..00000000000 --- a/build-tests/heft-sass-test/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/web-app', '@rushstack/eslint-config/mixins/react'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/heft-sass-test/.gitignore b/build-tests/heft-sass-test/.gitignore new file mode 100644 index 00000000000..bea2cfa7ff8 --- /dev/null +++ b/build-tests/heft-sass-test/.gitignore @@ -0,0 +1 @@ +lib-css \ No newline at end of file diff --git a/build-tests/heft-sass-test/assets/index.html b/build-tests/heft-sass-test/assets/index.html index 3cfae745c19..9e89ef57d85 100644 --- a/build-tests/heft-sass-test/assets/index.html +++ b/build-tests/heft-sass-test/assets/index.html @@ -1,4 +1,4 @@ - + diff --git a/build-tests/heft-sass-test/config/heft.json b/build-tests/heft-sass-test/config/heft.json index 283ba3aadbb..685ffd8236e 100644 --- a/build-tests/heft-sass-test/config/heft.json +++ b/build-tests/heft-sass-test/config/heft.json @@ -2,18 +2,41 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }, { "sourcePath": "lib-commonjs" }], + "cleanFiles": [{ "includeGlobs": ["dist", "lib", "lib-commonjs", "lib-css", "lib-esm", "temp"] }], + "tasksByName": { + "set-browserslist-ignore-old-data-env-var": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "set-environment-variables-plugin", + "options": { + "environmentVariablesToSet": { + // Suppress the "Browserslist: caniuse-lite is outdated" warning. Although the warning is + // potentially useful, the check is performed in a way that is nondeterministic and can cause + // Rush pipelines to fail. Moreover, the outdated version is often irrelevant and/or nontrivial + // to upgrade. See this thread for details: https://github.com/microsoft/rushstack/issues/2981 + "BROWSERSLIST_IGNORE_OLD_DATA": "1" + } + } + } + }, "sass": { + "taskDependencies": ["set-browserslist-ignore-old-data-env-var"], "taskPlugin": { "pluginPackage": "@rushstack/heft-sass-plugin" } }, + "sass-load-styles": { + "taskDependencies": [], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-sass-load-themed-styles-plugin" + } + }, "typescript": { "taskDependencies": ["sass"], "taskPlugin": { diff --git a/build-tests/heft-sass-test/config/jest.config.json b/build-tests/heft-sass-test/config/jest.config.json index f009ce3d13e..1d798061493 100644 --- a/build-tests/heft-sass-test/config/jest.config.json +++ b/build-tests/heft-sass-test/config/jest.config.json @@ -1,19 +1,13 @@ { - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", + "extends": "@rushstack/heft-jest-plugin/includes/jest-web.config.json", - "roots": ["/lib-commonjs"], + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], - "testMatch": ["/lib-commonjs/**/*.test.js"], - - "collectCoverageFrom": [ - "lib-commonjs/**/*.js", - "!lib-commonjs/**/*.d.ts", - "!lib-commonjs/**/*.test.js", - "!lib-commonjs/**/test/**", - "!lib-commonjs/**/__tests__/**", - "!lib-commonjs/**/__fixtures__/**", - "!lib-commonjs/**/__mocks__/**" - ], + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8", "moduleFileExtensions": ["js", "css", "json", "node"] } diff --git a/build-tests/heft-sass-test/config/rush-project.json b/build-tests/heft-sass-test/config/rush-project.json index 247dc17187a..8862092ef1d 100644 --- a/build-tests/heft-sass-test/config/rush-project.json +++ b/build-tests/heft-sass-test/config/rush-project.json @@ -1,8 +1,14 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + "outputFolderNames": ["lib-esm", "lib-commonjs", "lib-css", "dist", "temp/sass-ts"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests/heft-sass-test/config/sass.json b/build-tests/heft-sass-test/config/sass.json index 4d3db247cce..1b51ab5dcb3 100644 --- a/build-tests/heft-sass-test/config/sass.json +++ b/build-tests/heft-sass-test/config/sass.json @@ -1,4 +1,16 @@ { - "cssOutputFolders": ["lib", "lib-commonjs"], - "secondaryGeneratedTsFolders": ["lib"] + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-sass-plugin.schema.json", + + "cssOutputFolders": [ + { "folder": "lib-esm", "shimModuleFormat": "esnext" }, + { "folder": "lib-commonjs", "shimModuleFormat": "commonjs" }, + "lib-css" + ], + "secondaryGeneratedTsFolders": ["lib-esm"], + "excludeFiles": ["./ignored1.scss", "ignored2.scss"], + + "fileExtensions": [".module.scss", ".module.sass", ".module.css"], + "nonModuleFileExtensions": [".global.scss", ".global.sass", ".global.css"], + + "silenceDeprecations": ["mixed-decls", "import", "global-builtin", "color-functions"] } diff --git a/build-tests/heft-sass-test/config/typescript.json b/build-tests/heft-sass-test/config/typescript.json index 75b995668a3..634bd4e7493 100644 --- a/build-tests/heft-sass-test/config/typescript.json +++ b/build-tests/heft-sass-test/config/typescript.json @@ -2,7 +2,7 @@ * Configures the TypeScript plugin for Heft. This plugin also manages linting. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", /** * If provided, emit these module kinds in addition to the modules specified in the tsconfig. diff --git a/build-tests/heft-sass-test/eslint.config.js b/build-tests/heft-sass-test/eslint.config.js new file mode 100644 index 00000000000..e5eaf3c624a --- /dev/null +++ b/build-tests/heft-sass-test/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-eslint-config/flat/profile/web-app'); +const reactMixin = require('local-eslint-config/flat/mixins/react'); + +module.exports = [ + ...webAppProfile, + ...reactMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-sass-test/package.json b/build-tests/heft-sass-test/package.json index fd8d795a7d0..02d9fe4395b 100644 --- a/build-tests/heft-sass-test/package.json +++ b/build-tests/heft-sass-test/package.json @@ -10,30 +10,31 @@ "_phase:test": "heft run --only test -- --clean" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", + "local-eslint-config": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-sass-plugin": "workspace:*", + "@rushstack/heft-sass-load-themed-styles-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", "@rushstack/heft-webpack4-plugin": "workspace:*", "@rushstack/heft": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/react-dom": "16.9.14", - "@types/react": "16.14.23", - "@types/webpack-env": "1.18.0", + "@rushstack/webpack4-module-minifier-plugin": "workspace:*", + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "@types/react-dom": "19.2.3", + "@types/react": "19.2.7", + "@types/webpack-env": "1.18.8", "autoprefixer": "~10.4.2", "css-loader": "~5.2.7", - "eslint": "~8.7.0", + "eslint": "~9.37.0", "html-webpack-plugin": "~4.5.2", "postcss-loader": "~4.1.0", - "postcss": "~8.4.6", - "react-dom": "~16.13.1", - "react": "~16.13.1", - "sass-loader": "~10.0.0", - "sass": "~1.3.0", + "postcss": "~8.5.10", + "react-dom": "~19.2.3", + "react": "~19.2.3", "style-loader": "~2.0.0", - "typescript": "~5.0.4", - "webpack": "~4.44.2" + "typescript": "~5.8.2", + "webpack": "~4.47.0" }, "dependencies": { "buttono": "~1.0.2" diff --git a/build-tests/heft-sass-test/src/ExampleApp.tsx b/build-tests/heft-sass-test/src/ExampleApp.tsx index 57f3f692066..0fca1af0443 100644 --- a/build-tests/heft-sass-test/src/ExampleApp.tsx +++ b/build-tests/heft-sass-test/src/ExampleApp.tsx @@ -3,11 +3,11 @@ import * as React from 'react'; -import styles from './styles.sass'; -import oldStyles from './stylesCSS.css'; -import altSyntaxStyles from './stylesAltSyntax.scss'; -import stylesUseSyntax from './stylesUseSyntax.sass'; -import stylesUseAltSyntax from './stylesUseAltSyntax.scss'; +import styles from './styles.module.sass'; +import oldStyles from './stylesCSS.module.css'; +import altSyntaxStyles from './stylesAltSyntax.module.scss'; +import stylesUseSyntax from './stylesUseSyntax.module.sass'; +import stylesUseAltSyntax from './stylesUseAltSyntax.module.scss'; import './stylesAltSyntax.global.scss'; /** @@ -31,6 +31,7 @@ export class ExampleApp extends React.Component {
  • 2nd
  • 3rd
  • +

    This element has a complex class name.

    ); diff --git a/build-tests/heft-sass-test/src/ignored1.scss b/build-tests/heft-sass-test/src/ignored1.scss new file mode 100644 index 00000000000..5a951e022cc --- /dev/null +++ b/build-tests/heft-sass-test/src/ignored1.scss @@ -0,0 +1,3 @@ +.ignoredStyle { + color: green; +} diff --git a/build-tests/heft-sass-test/src/ignored2.scss b/build-tests/heft-sass-test/src/ignored2.scss new file mode 100644 index 00000000000..aa93cd03eb5 --- /dev/null +++ b/build-tests/heft-sass-test/src/ignored2.scss @@ -0,0 +1,3 @@ +.otherIgnoredStyle { + color: blue; +} diff --git a/build-tests/heft-sass-test/src/index.tsx b/build-tests/heft-sass-test/src/index.tsx index a9e29e0b029..83eba2aa723 100644 --- a/build-tests/heft-sass-test/src/index.tsx +++ b/build-tests/heft-sass-test/src/index.tsx @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -// eslint-disable-next-line @typescript-eslint/no-unused-vars import * as React from 'react'; -import * as ReactDOM from 'react-dom'; +import * as ReactDOM from 'react-dom/client'; + import { ExampleApp } from './ExampleApp'; -const rootDiv: HTMLElement = document.getElementById('root') as HTMLElement; -ReactDOM.render(, rootDiv); +const rootDiv: HTMLElement = document.getElementById('root')!; +ReactDOM.createRoot(rootDiv).render(); diff --git a/build-tests/heft-sass-test/src/styles.module.sass b/build-tests/heft-sass-test/src/styles.module.sass new file mode 100644 index 00000000000..61be0178191 --- /dev/null +++ b/build-tests/heft-sass-test/src/styles.module.sass @@ -0,0 +1,28 @@ +/** + * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + */ + +// Testing Sass imports +@import 'stylesImport' + +// Testing node_modules imports +@import 'pkg:buttono/buttono' + +// Testing root styles +html, body + margin: 0 + height: 100% + background-color: #c0c0c0 + font-family: Tahoma, sans-serif + +// Testing Sass classes +.exampleApp + background-color: #ffffff + padding: 20px + border-radius: 5px + width: 400px + +.exampleButton + @include buttono-block() + @include buttono-style-modifier($background-color: mediumorchid) diff --git a/build-tests/heft-sass-test/src/styles.sass b/build-tests/heft-sass-test/src/styles.sass deleted file mode 100644 index a79830ecfef..00000000000 --- a/build-tests/heft-sass-test/src/styles.sass +++ /dev/null @@ -1,28 +0,0 @@ -/** - * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. - * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. - */ - -// Testing Sass imports -@import 'stylesImport' - -// Testing node_modules imports -@import '~buttono/buttono' - -// Testing root styles -html, body - margin: 0 - height: 100% - background-color: #c0c0c0 - font-family: Tahoma, sans-serif - -// Testing Sass classes -.exampleApp - background-color: #ffffff - padding: 20px - border-radius: 5px - width: 400px - -.exampleButton - @include buttono-block() - @include buttono-style-modifier($background-color: mediumorchid) diff --git a/build-tests/heft-sass-test/src/stylesAltSyntax.module.scss b/build-tests/heft-sass-test/src/stylesAltSyntax.module.scss new file mode 100644 index 00000000000..c97eebeb3cd --- /dev/null +++ b/build-tests/heft-sass-test/src/stylesAltSyntax.module.scss @@ -0,0 +1,15 @@ +/** + * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + */ + +// Testing SCSS syntax +$marginValue: '[theme:normalMargin, default: 20px]'; + +.label { + margin-bottom: $marginValue; +} + +.style-with-dashes { + margin-top: $marginValue; +} diff --git a/build-tests/heft-sass-test/src/stylesAltSyntax.scss b/build-tests/heft-sass-test/src/stylesAltSyntax.scss deleted file mode 100644 index 34bd511beee..00000000000 --- a/build-tests/heft-sass-test/src/stylesAltSyntax.scss +++ /dev/null @@ -1,11 +0,0 @@ -/** - * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. - * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. - */ - -// Testing SCSS syntax -$marginValue: 20px; - -.label { - margin-bottom: $marginValue; -} diff --git a/build-tests/heft-sass-test/src/stylesCSS.css b/build-tests/heft-sass-test/src/stylesCSS.module.css similarity index 100% rename from build-tests/heft-sass-test/src/stylesCSS.css rename to build-tests/heft-sass-test/src/stylesCSS.module.css diff --git a/build-tests/heft-sass-test/src/stylesUseAltSyntax.scss b/build-tests/heft-sass-test/src/stylesUseAltSyntax.module.scss similarity index 100% rename from build-tests/heft-sass-test/src/stylesUseAltSyntax.scss rename to build-tests/heft-sass-test/src/stylesUseAltSyntax.module.scss diff --git a/build-tests/heft-sass-test/src/stylesUseSyntax.sass b/build-tests/heft-sass-test/src/stylesUseSyntax.module.sass similarity index 100% rename from build-tests/heft-sass-test/src/stylesUseSyntax.sass rename to build-tests/heft-sass-test/src/stylesUseSyntax.module.sass diff --git a/build-tests/heft-sass-test/src/test/__snapshots__/lib-commonjs.test.ts.snap b/build-tests/heft-sass-test/src/test/__snapshots__/lib-commonjs.test.ts.snap new file mode 100644 index 00000000000..08e11340535 --- /dev/null +++ b/build-tests/heft-sass-test/src/test/__snapshots__/lib-commonjs.test.ts.snap @@ -0,0 +1,247 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`SASS CJS Shims ignored1.scss: files 1`] = `Array []`; + +exports[`SASS CJS Shims ignored2.scss: files 1`] = `Array []`; + +exports[`SASS CJS Shims styles.module.sass: files 1`] = ` +Array [ + "styles.module.css", + "styles.module.sass.js", +] +`; + +exports[`SASS CJS Shims styles.module.sass: styles.module.css 1`] = ` +"/** + * * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + * */ +/** + * * This file is a SASS partial and therefore has no direct output file, but gets embedded into other files. + * */ +.exampleImport { + font-style: italic; + color: darkcyan; +} + +html, body { + margin: 0; + height: 100%; + background-color: #c0c0c0; + font-family: Tahoma, sans-serif; +} + +.exampleApp { + background-color: #ffffff; + padding: 20px; + border-radius: 5px; + width: 400px; +} + +.exampleButton { + display: inline-block; + padding: 10px 20px; + border: 0 solid transparent; + font-size: 16px; + line-height: 1.5; + text-align: center; + transition-duration: 0.4s; + user-select: none; + vertical-align: middle; + transition-property: background-color, color, border-color; + background-color: mediumorchid; + border-color: mediumorchid; + border-radius: 3px; + color: #fff; +} +.exampleButton:not(:disabled):not([aria-disabled=true]) { + cursor: pointer; +} +.exampleButton:hover, .exampleButton:focus { + text-decoration: none; +} +.exampleButton:disabled, .exampleButton[aria-disabled=true] { + box-shadow: none; +} +.exampleButton:hover { + background-color: rgb(160.4485981308, 48.6878504673, 188.1121495327); + border-color: rgb(160.4485981308, 48.6878504673, 188.1121495327); + color: #fff; +} +.exampleButton:focus { + outline: 2px dotted mediumorchid; + outline-offset: 1px; +} +.exampleButton:disabled, .exampleButton[aria-disabled=true] { + background-color: mediumorchid; + border-color: mediumorchid; + color: #fff; + opacity: 0.7; +}" +`; + +exports[`SASS CJS Shims styles.module.sass: styles.module.sass.js 1`] = ` +"module.exports = require(\\"./styles.module.css\\"); +module.exports.default = module.exports;" +`; + +exports[`SASS CJS Shims stylesAltSyntax.global.scss: files 1`] = ` +Array [ + "stylesAltSyntax.global.css", + "stylesAltSyntax.global.scss.js", +] +`; + +exports[`SASS CJS Shims stylesAltSyntax.global.scss: stylesAltSyntax.global.css 1`] = ` +"/** + * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + */ +.ms-label { + margin-bottom: 20px; +}" +`; + +exports[`SASS CJS Shims stylesAltSyntax.global.scss: stylesAltSyntax.global.scss.js 1`] = `"require(\\"./stylesAltSyntax.global.css\\");"`; + +exports[`SASS CJS Shims stylesAltSyntax.module.scss: files 1`] = ` +Array [ + "stylesAltSyntax.module.css", + "stylesAltSyntax.module.scss.js", +] +`; + +exports[`SASS CJS Shims stylesAltSyntax.module.scss: stylesAltSyntax.module.css 1`] = ` +"/** + * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + */ +.label { + margin-bottom: var(--normalMargin, 20px); +} + +.style-with-dashes { + margin-top: var(--normalMargin, 20px); +}" +`; + +exports[`SASS CJS Shims stylesAltSyntax.module.scss: stylesAltSyntax.module.scss.js 1`] = ` +"module.exports = require(\\"./stylesAltSyntax.module.css\\"); +module.exports.default = module.exports;" +`; + +exports[`SASS CJS Shims stylesUseAltSyntax.module.scss: files 1`] = ` +Array [ + "stylesUseAltSyntax.module.css", + "stylesUseAltSyntax.module.scss.js", +] +`; + +exports[`SASS CJS Shims stylesUseAltSyntax.module.scss: stylesUseAltSyntax.module.css 1`] = ` +"/** + * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + */ +/** + * This file is a SASS partial and therefore has no direct output file, + * but gets embedded into other files. + */ +/** + * * * This file is used to verify that Sass imports using with configurable variable definition are successful. + * * */ +:root { + --list-margin-top: calc(1.25 * 1rem); +} + +.label { + display: block; + color: #4682ff; +} + +.exampleList { + list-style-type: circle; + margin-top: var(--list-margin-top); +} +.exampleListItem1 { + color: deepskyblue; +} +.exampleListItem2 { + background-color: dodgerblue; + color: #e16f00; +} +.exampleListItem3 { + background-color: #b7c274; + color: darkslateblue; +}" +`; + +exports[`SASS CJS Shims stylesUseAltSyntax.module.scss: stylesUseAltSyntax.module.scss.js 1`] = ` +"module.exports = require(\\"./stylesUseAltSyntax.module.css\\"); +module.exports.default = module.exports;" +`; + +exports[`SASS CJS Shims stylesUseSyntax.module.sass: files 1`] = ` +Array [ + "stylesUseSyntax.module.css", + "stylesUseSyntax.module.sass.js", +] +`; + +exports[`SASS CJS Shims stylesUseSyntax.module.sass: stylesUseSyntax.module.css 1`] = ` +"/** + * * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + * */ +/** + * This file is a SASS partial and therefore has no direct output file, + * but gets embedded into other files. + */ +/** + * * * This file is used to verify that Sass imports using with configurable variable definition are successful. + * * */ +.exampleAnchor { + display: inline-block; + padding: 10px 20px; + border: 0 solid transparent; + font-size: 16px; + line-height: 1.5; + text-align: center; + transition-duration: 0.4s; + user-select: none; + vertical-align: middle; + transition-property: background-color, color, border-color; + background-color: #7a717f; + border-color: #7a717f; + border-radius: 3px; + color: #fff; +} +.exampleAnchor:not(:disabled):not([aria-disabled=true]) { + cursor: pointer; +} +.exampleAnchor:hover, .exampleAnchor:focus { + text-decoration: none; +} +.exampleAnchor:disabled, .exampleAnchor[aria-disabled=true] { + box-shadow: none; +} +.exampleAnchor:hover { + background-color: rgb(97.6, 90.4, 101.6); + border-color: rgb(97.6, 90.4, 101.6); + color: #fff; +} +.exampleAnchor:focus { + outline: 2px dotted #7a717f; + outline-offset: 1px; +} +.exampleAnchor:disabled, .exampleAnchor[aria-disabled=true] { + background-color: #7a717f; + border-color: #7a717f; + color: #fff; + opacity: 0.7; +}" +`; + +exports[`SASS CJS Shims stylesUseSyntax.module.sass: stylesUseSyntax.module.sass.js 1`] = ` +"module.exports = require(\\"./stylesUseSyntax.module.css\\"); +module.exports.default = module.exports;" +`; diff --git a/build-tests/heft-sass-test/src/test/__snapshots__/lib-css.test.ts.snap b/build-tests/heft-sass-test/src/test/__snapshots__/lib-css.test.ts.snap new file mode 100644 index 00000000000..81e797c5b75 --- /dev/null +++ b/build-tests/heft-sass-test/src/test/__snapshots__/lib-css.test.ts.snap @@ -0,0 +1,220 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`SASS No Shims ignored1.scss: files 1`] = `Array []`; + +exports[`SASS No Shims ignored2.scss: files 1`] = `Array []`; + +exports[`SASS No Shims styles.module.sass: files 1`] = ` +Array [ + "styles.module.css", +] +`; + +exports[`SASS No Shims styles.module.sass: styles.module.css 1`] = ` +"/** + * * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + * */ +/** + * * This file is a SASS partial and therefore has no direct output file, but gets embedded into other files. + * */ +.exampleImport { + font-style: italic; + color: darkcyan; +} + +html, body { + margin: 0; + height: 100%; + background-color: #c0c0c0; + font-family: Tahoma, sans-serif; +} + +.exampleApp { + background-color: #ffffff; + padding: 20px; + border-radius: 5px; + width: 400px; +} + +.exampleButton { + display: inline-block; + padding: 10px 20px; + border: 0 solid transparent; + font-size: 16px; + line-height: 1.5; + text-align: center; + transition-duration: 0.4s; + user-select: none; + vertical-align: middle; + transition-property: background-color, color, border-color; + background-color: mediumorchid; + border-color: mediumorchid; + border-radius: 3px; + color: #fff; +} +.exampleButton:not(:disabled):not([aria-disabled=true]) { + cursor: pointer; +} +.exampleButton:hover, .exampleButton:focus { + text-decoration: none; +} +.exampleButton:disabled, .exampleButton[aria-disabled=true] { + box-shadow: none; +} +.exampleButton:hover { + background-color: rgb(160.4485981308, 48.6878504673, 188.1121495327); + border-color: rgb(160.4485981308, 48.6878504673, 188.1121495327); + color: #fff; +} +.exampleButton:focus { + outline: 2px dotted mediumorchid; + outline-offset: 1px; +} +.exampleButton:disabled, .exampleButton[aria-disabled=true] { + background-color: mediumorchid; + border-color: mediumorchid; + color: #fff; + opacity: 0.7; +}" +`; + +exports[`SASS No Shims stylesAltSyntax.global.scss: files 1`] = ` +Array [ + "stylesAltSyntax.global.css", +] +`; + +exports[`SASS No Shims stylesAltSyntax.global.scss: stylesAltSyntax.global.css 1`] = ` +"/** + * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + */ +.ms-label { + margin-bottom: 20px; +}" +`; + +exports[`SASS No Shims stylesAltSyntax.module.scss: files 1`] = ` +Array [ + "stylesAltSyntax.module.css", +] +`; + +exports[`SASS No Shims stylesAltSyntax.module.scss: stylesAltSyntax.module.css 1`] = ` +"/** + * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + */ +.label { + margin-bottom: var(--normalMargin, 20px); +} + +.style-with-dashes { + margin-top: var(--normalMargin, 20px); +}" +`; + +exports[`SASS No Shims stylesUseAltSyntax.module.scss: files 1`] = ` +Array [ + "stylesUseAltSyntax.module.css", +] +`; + +exports[`SASS No Shims stylesUseAltSyntax.module.scss: stylesUseAltSyntax.module.css 1`] = ` +"/** + * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + */ +/** + * This file is a SASS partial and therefore has no direct output file, + * but gets embedded into other files. + */ +/** + * * * This file is used to verify that Sass imports using with configurable variable definition are successful. + * * */ +:root { + --list-margin-top: calc(1.25 * 1rem); +} + +.label { + display: block; + color: #4682ff; +} + +.exampleList { + list-style-type: circle; + margin-top: var(--list-margin-top); +} +.exampleListItem1 { + color: deepskyblue; +} +.exampleListItem2 { + background-color: dodgerblue; + color: #e16f00; +} +.exampleListItem3 { + background-color: #b7c274; + color: darkslateblue; +}" +`; + +exports[`SASS No Shims stylesUseSyntax.module.sass: files 1`] = ` +Array [ + "stylesUseSyntax.module.css", +] +`; + +exports[`SASS No Shims stylesUseSyntax.module.sass: stylesUseSyntax.module.css 1`] = ` +"/** + * * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + * */ +/** + * This file is a SASS partial and therefore has no direct output file, + * but gets embedded into other files. + */ +/** + * * * This file is used to verify that Sass imports using with configurable variable definition are successful. + * * */ +.exampleAnchor { + display: inline-block; + padding: 10px 20px; + border: 0 solid transparent; + font-size: 16px; + line-height: 1.5; + text-align: center; + transition-duration: 0.4s; + user-select: none; + vertical-align: middle; + transition-property: background-color, color, border-color; + background-color: #7a717f; + border-color: #7a717f; + border-radius: 3px; + color: #fff; +} +.exampleAnchor:not(:disabled):not([aria-disabled=true]) { + cursor: pointer; +} +.exampleAnchor:hover, .exampleAnchor:focus { + text-decoration: none; +} +.exampleAnchor:disabled, .exampleAnchor[aria-disabled=true] { + box-shadow: none; +} +.exampleAnchor:hover { + background-color: rgb(97.6, 90.4, 101.6); + border-color: rgb(97.6, 90.4, 101.6); + color: #fff; +} +.exampleAnchor:focus { + outline: 2px dotted #7a717f; + outline-offset: 1px; +} +.exampleAnchor:disabled, .exampleAnchor[aria-disabled=true] { + background-color: #7a717f; + border-color: #7a717f; + color: #fff; + opacity: 0.7; +}" +`; diff --git a/build-tests/heft-sass-test/src/test/__snapshots__/lib.test.ts.snap b/build-tests/heft-sass-test/src/test/__snapshots__/lib.test.ts.snap new file mode 100644 index 00000000000..90eb24cc36f --- /dev/null +++ b/build-tests/heft-sass-test/src/test/__snapshots__/lib.test.ts.snap @@ -0,0 +1,281 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`SASS ESM Shims ignored1.scss: files 1`] = `Array []`; + +exports[`SASS ESM Shims ignored2.scss: files 1`] = `Array []`; + +exports[`SASS ESM Shims styles.module.sass: files 1`] = ` +Array [ + "styles.module.css", + "styles.module.sass.d.ts", + "styles.module.sass.js", +] +`; + +exports[`SASS ESM Shims styles.module.sass: styles.module.css 1`] = ` +"/** + * * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + * */ +/** + * * This file is a SASS partial and therefore has no direct output file, but gets embedded into other files. + * */ +.exampleImport { + font-style: italic; + color: darkcyan; +} + +html, body { + margin: 0; + height: 100%; + background-color: #c0c0c0; + font-family: Tahoma, sans-serif; +} + +.exampleApp { + background-color: #ffffff; + padding: 20px; + border-radius: 5px; + width: 400px; +} + +.exampleButton { + display: inline-block; + padding: 10px 20px; + border: 0 solid transparent; + font-size: 16px; + line-height: 1.5; + text-align: center; + transition-duration: 0.4s; + user-select: none; + vertical-align: middle; + transition-property: background-color, color, border-color; + background-color: mediumorchid; + border-color: mediumorchid; + border-radius: 3px; + color: #fff; +} +.exampleButton:not(:disabled):not([aria-disabled=true]) { + cursor: pointer; +} +.exampleButton:hover, .exampleButton:focus { + text-decoration: none; +} +.exampleButton:disabled, .exampleButton[aria-disabled=true] { + box-shadow: none; +} +.exampleButton:hover { + background-color: rgb(160.4485981308, 48.6878504673, 188.1121495327); + border-color: rgb(160.4485981308, 48.6878504673, 188.1121495327); + color: #fff; +} +.exampleButton:focus { + outline: 2px dotted mediumorchid; + outline-offset: 1px; +} +.exampleButton:disabled, .exampleButton[aria-disabled=true] { + background-color: mediumorchid; + border-color: mediumorchid; + color: #fff; + opacity: 0.7; +}" +`; + +exports[`SASS ESM Shims styles.module.sass: styles.module.sass.d.ts 1`] = ` +"declare interface IStyles { + exampleImport: string; + exampleApp: string; + exampleButton: string; +} +declare const styles: IStyles; +export default styles;" +`; + +exports[`SASS ESM Shims styles.module.sass: styles.module.sass.js 1`] = `"export { default } from \\"./styles.module.css\\";"`; + +exports[`SASS ESM Shims stylesAltSyntax.global.scss: files 1`] = ` +Array [ + "stylesAltSyntax.global.css", + "stylesAltSyntax.global.scss.d.ts", + "stylesAltSyntax.global.scss.js", +] +`; + +exports[`SASS ESM Shims stylesAltSyntax.global.scss: stylesAltSyntax.global.css 1`] = ` +"/** + * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + */ +.ms-label { + margin-bottom: 20px; +}" +`; + +exports[`SASS ESM Shims stylesAltSyntax.global.scss: stylesAltSyntax.global.scss.d.ts 1`] = `"export {};"`; + +exports[`SASS ESM Shims stylesAltSyntax.global.scss: stylesAltSyntax.global.scss.js 1`] = `"import \\"./stylesAltSyntax.global.css\\";export {};"`; + +exports[`SASS ESM Shims stylesAltSyntax.module.scss: files 1`] = ` +Array [ + "stylesAltSyntax.module.css", + "stylesAltSyntax.module.scss.d.ts", + "stylesAltSyntax.module.scss.js", +] +`; + +exports[`SASS ESM Shims stylesAltSyntax.module.scss: stylesAltSyntax.module.css 1`] = ` +"/** + * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + */ +.label { + margin-bottom: var(--normalMargin, 20px); +} + +.style-with-dashes { + margin-top: var(--normalMargin, 20px); +}" +`; + +exports[`SASS ESM Shims stylesAltSyntax.module.scss: stylesAltSyntax.module.scss.d.ts 1`] = ` +"declare interface IStyles { + label: string; + \\"style-with-dashes\\": string; +} +declare const styles: IStyles; +export default styles;" +`; + +exports[`SASS ESM Shims stylesAltSyntax.module.scss: stylesAltSyntax.module.scss.js 1`] = `"export { default } from \\"./stylesAltSyntax.module.css\\";"`; + +exports[`SASS ESM Shims stylesUseAltSyntax.module.scss: files 1`] = ` +Array [ + "stylesUseAltSyntax.module.css", + "stylesUseAltSyntax.module.scss.d.ts", + "stylesUseAltSyntax.module.scss.js", +] +`; + +exports[`SASS ESM Shims stylesUseAltSyntax.module.scss: stylesUseAltSyntax.module.css 1`] = ` +"/** + * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + */ +/** + * This file is a SASS partial and therefore has no direct output file, + * but gets embedded into other files. + */ +/** + * * * This file is used to verify that Sass imports using with configurable variable definition are successful. + * * */ +:root { + --list-margin-top: calc(1.25 * 1rem); +} + +.label { + display: block; + color: #4682ff; +} + +.exampleList { + list-style-type: circle; + margin-top: var(--list-margin-top); +} +.exampleListItem1 { + color: deepskyblue; +} +.exampleListItem2 { + background-color: dodgerblue; + color: #e16f00; +} +.exampleListItem3 { + background-color: #b7c274; + color: darkslateblue; +}" +`; + +exports[`SASS ESM Shims stylesUseAltSyntax.module.scss: stylesUseAltSyntax.module.scss.d.ts 1`] = ` +"declare interface IStyles { + label: string; + exampleList: string; + exampleListItem1: string; + exampleListItem2: string; + exampleListItem3: string; +} +declare const styles: IStyles; +export default styles;" +`; + +exports[`SASS ESM Shims stylesUseAltSyntax.module.scss: stylesUseAltSyntax.module.scss.js 1`] = `"export { default } from \\"./stylesUseAltSyntax.module.css\\";"`; + +exports[`SASS ESM Shims stylesUseSyntax.module.sass: files 1`] = ` +Array [ + "stylesUseSyntax.module.css", + "stylesUseSyntax.module.sass.d.ts", + "stylesUseSyntax.module.sass.js", +] +`; + +exports[`SASS ESM Shims stylesUseSyntax.module.sass: stylesUseSyntax.module.css 1`] = ` +"/** + * * This file gets transpiled by the heft-sass-plugin and output to the lib/ folder. + * * Then Webpack uses css-loader to embed, and finally style-loader to apply it to the DOM. + * */ +/** + * This file is a SASS partial and therefore has no direct output file, + * but gets embedded into other files. + */ +/** + * * * This file is used to verify that Sass imports using with configurable variable definition are successful. + * * */ +.exampleAnchor { + display: inline-block; + padding: 10px 20px; + border: 0 solid transparent; + font-size: 16px; + line-height: 1.5; + text-align: center; + transition-duration: 0.4s; + user-select: none; + vertical-align: middle; + transition-property: background-color, color, border-color; + background-color: #7a717f; + border-color: #7a717f; + border-radius: 3px; + color: #fff; +} +.exampleAnchor:not(:disabled):not([aria-disabled=true]) { + cursor: pointer; +} +.exampleAnchor:hover, .exampleAnchor:focus { + text-decoration: none; +} +.exampleAnchor:disabled, .exampleAnchor[aria-disabled=true] { + box-shadow: none; +} +.exampleAnchor:hover { + background-color: rgb(97.6, 90.4, 101.6); + border-color: rgb(97.6, 90.4, 101.6); + color: #fff; +} +.exampleAnchor:focus { + outline: 2px dotted #7a717f; + outline-offset: 1px; +} +.exampleAnchor:disabled, .exampleAnchor[aria-disabled=true] { + background-color: #7a717f; + border-color: #7a717f; + color: #fff; + opacity: 0.7; +}" +`; + +exports[`SASS ESM Shims stylesUseSyntax.module.sass: stylesUseSyntax.module.sass.d.ts 1`] = ` +"declare interface IStyles { + exampleAnchor: string; +} +declare const styles: IStyles; +export default styles;" +`; + +exports[`SASS ESM Shims stylesUseSyntax.module.sass: stylesUseSyntax.module.sass.js 1`] = `"export { default } from \\"./stylesUseSyntax.module.css\\";"`; diff --git a/build-tests/heft-sass-test/src/test/__snapshots__/sass-ts.test.ts.snap b/build-tests/heft-sass-test/src/test/__snapshots__/sass-ts.test.ts.snap new file mode 100644 index 00000000000..c4d89a6df85 --- /dev/null +++ b/build-tests/heft-sass-test/src/test/__snapshots__/sass-ts.test.ts.snap @@ -0,0 +1,76 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`SASS Typings ignored1.scss: files 1`] = `Array []`; + +exports[`SASS Typings ignored2.scss: files 1`] = `Array []`; + +exports[`SASS Typings styles.module.sass: files 1`] = ` +Array [ + "styles.module.sass.d.ts", +] +`; + +exports[`SASS Typings styles.module.sass: styles.module.sass.d.ts 1`] = ` +"declare interface IStyles { + exampleImport: string; + exampleApp: string; + exampleButton: string; +} +declare const styles: IStyles; +export default styles;" +`; + +exports[`SASS Typings stylesAltSyntax.global.scss: files 1`] = ` +Array [ + "stylesAltSyntax.global.scss.d.ts", +] +`; + +exports[`SASS Typings stylesAltSyntax.global.scss: stylesAltSyntax.global.scss.d.ts 1`] = `"export {};"`; + +exports[`SASS Typings stylesAltSyntax.module.scss: files 1`] = ` +Array [ + "stylesAltSyntax.module.scss.d.ts", +] +`; + +exports[`SASS Typings stylesAltSyntax.module.scss: stylesAltSyntax.module.scss.d.ts 1`] = ` +"declare interface IStyles { + label: string; + \\"style-with-dashes\\": string; +} +declare const styles: IStyles; +export default styles;" +`; + +exports[`SASS Typings stylesUseAltSyntax.module.scss: files 1`] = ` +Array [ + "stylesUseAltSyntax.module.scss.d.ts", +] +`; + +exports[`SASS Typings stylesUseAltSyntax.module.scss: stylesUseAltSyntax.module.scss.d.ts 1`] = ` +"declare interface IStyles { + label: string; + exampleList: string; + exampleListItem1: string; + exampleListItem2: string; + exampleListItem3: string; +} +declare const styles: IStyles; +export default styles;" +`; + +exports[`SASS Typings stylesUseSyntax.module.sass: files 1`] = ` +Array [ + "stylesUseSyntax.module.sass.d.ts", +] +`; + +exports[`SASS Typings stylesUseSyntax.module.sass: stylesUseSyntax.module.sass.d.ts 1`] = ` +"declare interface IStyles { + exampleAnchor: string; +} +declare const styles: IStyles; +export default styles;" +`; diff --git a/build-tests/heft-sass-test/src/test/lib-commonjs.test.ts b/build-tests/heft-sass-test/src/test/lib-commonjs.test.ts new file mode 100644 index 00000000000..93bf6191ae0 --- /dev/null +++ b/build-tests/heft-sass-test/src/test/lib-commonjs.test.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import { validateSnapshots, getScssFiles } from './validateSnapshots'; + +describe('SASS CJS Shims', () => { + const libFolder: string = path.join(__dirname, '../../lib-commonjs'); + getScssFiles().forEach((fileName: string) => { + it(fileName, () => { + validateSnapshots(libFolder, fileName); + }); + }); +}); diff --git a/build-tests/heft-sass-test/src/test/lib-css.test.ts b/build-tests/heft-sass-test/src/test/lib-css.test.ts new file mode 100644 index 00000000000..d80ff020159 --- /dev/null +++ b/build-tests/heft-sass-test/src/test/lib-css.test.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import { validateSnapshots, getScssFiles } from './validateSnapshots'; + +describe('SASS No Shims', () => { + const libFolder: string = path.join(__dirname, '../../lib-css'); + getScssFiles().forEach((fileName: string) => { + it(fileName, () => { + validateSnapshots(libFolder, fileName); + }); + }); +}); diff --git a/build-tests/heft-sass-test/src/test/lib.test.ts b/build-tests/heft-sass-test/src/test/lib.test.ts new file mode 100644 index 00000000000..55a32154653 --- /dev/null +++ b/build-tests/heft-sass-test/src/test/lib.test.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import { validateSnapshots, getScssFiles } from './validateSnapshots'; + +describe('SASS ESM Shims', () => { + const libFolder: string = path.join(__dirname, '../../lib-esm'); + getScssFiles().forEach((fileName: string) => { + it(fileName, () => { + validateSnapshots(libFolder, fileName); + }); + }); +}); diff --git a/build-tests/heft-sass-test/src/test/sass-ts.test.ts b/build-tests/heft-sass-test/src/test/sass-ts.test.ts new file mode 100644 index 00000000000..1055956639c --- /dev/null +++ b/build-tests/heft-sass-test/src/test/sass-ts.test.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import { validateSnapshots, getScssFiles } from './validateSnapshots'; + +describe('SASS Typings', () => { + const libFolder: string = path.join(__dirname, '../../temp/sass-ts'); + getScssFiles().forEach((fileName: string) => { + it(fileName, () => { + validateSnapshots(libFolder, fileName); + }); + }); +}); diff --git a/build-tests/heft-sass-test/src/test/validateSnapshots.ts b/build-tests/heft-sass-test/src/test/validateSnapshots.ts new file mode 100644 index 00000000000..f7b5542c881 --- /dev/null +++ b/build-tests/heft-sass-test/src/test/validateSnapshots.ts @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/// +import * as fs from 'node:fs'; +import * as path from 'node:path'; + +export function getScssFiles(): string[] { + const srcFolder: string = path.join(__dirname, '../../src'); + const sourceFiles: string[] = fs + .readdirSync(srcFolder, { withFileTypes: true }) + .filter((file: fs.Dirent) => { + const { name } = file; + return file.isFile() && !name.startsWith('_') && (name.endsWith('.sass') || name.endsWith('.scss')); + }) + .map((dirent) => dirent.name); + return sourceFiles; +} + +export function validateSnapshots(dir: string, fileName: string): void { + const originalExt: string = path.extname(fileName); + const basename: string = path.basename(fileName, originalExt) + '.'; + const files: fs.Dirent[] = fs.readdirSync(dir, { withFileTypes: true }); + const filteredFiles: fs.Dirent[] = files.filter((file: fs.Dirent) => { + return file.isFile() && file.name.startsWith(basename); + }); + expect(filteredFiles.map((x) => x.name)).toMatchSnapshot(`files`); + filteredFiles.forEach((file: fs.Dirent) => { + if (!file.isFile() || !file.name.startsWith(basename)) { + return; + } + const filePath: string = path.join(dir, file.name); + const fileContents: string = fs.readFileSync(filePath, 'utf8'); + const normalizedFileContents: string = fileContents.replace(/\r/gm, ''); + expect(normalizedFileContents).toMatchSnapshot(`${file.name}`); + }); +} diff --git a/build-tests/heft-sass-test/src/utilities/configurableModule.sass b/build-tests/heft-sass-test/src/utilities/_configurableModule.sass similarity index 100% rename from build-tests/heft-sass-test/src/utilities/configurableModule.sass rename to build-tests/heft-sass-test/src/utilities/_configurableModule.sass diff --git a/build-tests/heft-sass-test/src/utilities/_relativeImport.sass b/build-tests/heft-sass-test/src/utilities/_relativeImport.sass new file mode 100644 index 00000000000..627b27fa18e --- /dev/null +++ b/build-tests/heft-sass-test/src/utilities/_relativeImport.sass @@ -0,0 +1,5 @@ +/** + * This file is not used by the example component. However, it verifies that relative Sass imports are successful. + */ + +@import '../stylesImport' \ No newline at end of file diff --git a/build-tests/heft-sass-test/src/utilities/useSyntaxRelativeImport.sass b/build-tests/heft-sass-test/src/utilities/_useSyntaxRelativeImport.sass similarity index 100% rename from build-tests/heft-sass-test/src/utilities/useSyntaxRelativeImport.sass rename to build-tests/heft-sass-test/src/utilities/_useSyntaxRelativeImport.sass diff --git a/build-tests/heft-sass-test/src/utilities/relativeImport.sass b/build-tests/heft-sass-test/src/utilities/relativeImport.sass deleted file mode 100644 index 49289ad2a51..00000000000 --- a/build-tests/heft-sass-test/src/utilities/relativeImport.sass +++ /dev/null @@ -1,5 +0,0 @@ -/** - * This file is not used by the example component. However, it verifies that relative Sass imports are successful. - */ - -@import '../stylesImport' \ No newline at end of file diff --git a/build-tests/heft-sass-test/tsconfig.json b/build-tests/heft-sass-test/tsconfig.json index aab26ef92a8..c3d716c5d49 100644 --- a/build-tests/heft-sass-test/tsconfig.json +++ b/build-tests/heft-sass-test/tsconfig.json @@ -2,7 +2,7 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-esm", "rootDir": "src", "rootDirs": ["src", "temp/sass-ts"], @@ -15,13 +15,12 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest", "webpack-env"], + "types": ["jest", "webpack-env"], "module": "esnext", "moduleResolution": "node", "target": "es5", "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-sass-test/webpack.config.js b/build-tests/heft-sass-test/webpack.config.js index be235dfd775..9441cfeea98 100644 --- a/build-tests/heft-sass-test/webpack.config.js +++ b/build-tests/heft-sass-test/webpack.config.js @@ -3,6 +3,7 @@ const path = require('path'); const Autoprefixer = require('autoprefixer'); const HtmlWebpackPlugin = require('html-webpack-plugin'); +const { ModuleMinifierPlugin, WorkerPoolMinifier } = require('@rushstack/webpack4-module-minifier-plugin'); /** * If the "--production" command-line parameter is specified when invoking Heft, then the @@ -12,9 +13,6 @@ function createWebpackConfig({ production }) { const webpackConfig = { // Documentation: https://webpack.js.org/configuration/mode/ mode: production ? 'production' : 'development', - resolve: { - extensions: ['.js', '.jsx', '.json', '.css'] - }, module: { rules: [ { @@ -45,7 +43,7 @@ function createWebpackConfig({ production }) { ] }, entry: { - app: path.join(__dirname, 'lib', 'index.js'), + app: path.join(__dirname, 'lib-esm', 'index.js'), // Put these libraries in a separate vendor bundle vendor: ['react', 'react-dom'] @@ -66,7 +64,15 @@ function createWebpackConfig({ production }) { new HtmlWebpackPlugin({ template: 'assets/index.html' }) - ] + ], + optimization: { + minimizer: [ + new ModuleMinifierPlugin({ + minifier: new WorkerPoolMinifier(), + useSourceMap: true + }) + ] + } }; return webpackConfig; diff --git a/build-tests/heft-swc-test/.gitignore b/build-tests/heft-swc-test/.gitignore new file mode 100644 index 00000000000..69667d9ce25 --- /dev/null +++ b/build-tests/heft-swc-test/.gitignore @@ -0,0 +1 @@ +lib-* \ No newline at end of file diff --git a/build-tests/heft-swc-test/config/heft.json b/build-tests/heft-swc-test/config/heft.json new file mode 100644 index 00000000000..7a3e0bf9ef3 --- /dev/null +++ b/build-tests/heft-swc-test/config/heft.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + // TODO: Add comments + "phasesByName": { + "build": { + "cleanFiles": [{ "includeGlobs": ["dist", "lib-commonjs", "lib-esm", "lib-es5", "temp"] }], + + "tasksByName": { + "typescript": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-typescript-plugin" + } + }, + "lint": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-lint-plugin" + } + }, + "transpile": { + "taskDependencies": [], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-isolated-typescript-transpile-plugin", + "options": { + "emitKinds": [ + { + "outDir": "lib-commonjs", + "formatOverride": "CommonJS", + "targetOverride": "ESNext" + }, + { + "outDir": "lib-esm", + "formatOverride": "ESNext", + "targetOverride": "ESNext" + }, + { + "outDir": "lib-es5", + "formatOverride": "ESNext", + "targetOverride": "ES5" + } + ] + } + } + } + } + }, + + "test": { + "phaseDependencies": ["build"], + "tasksByName": { + "jest": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-jest-plugin" + } + } + } + } + } +} diff --git a/build-tests/heft-swc-test/config/jest.config.json b/build-tests/heft-swc-test/config/jest.config.json new file mode 100644 index 00000000000..fdfebd67c85 --- /dev/null +++ b/build-tests/heft-swc-test/config/jest.config.json @@ -0,0 +1,8 @@ +{ + "extends": "@rushstack/heft-jest-plugin/includes/jest-web.config.json", + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"] +} diff --git a/build-tests/heft-swc-test/config/rush-project.json b/build-tests/heft-swc-test/config/rush-project.json new file mode 100644 index 00000000000..de6d0e4f9a6 --- /dev/null +++ b/build-tests/heft-swc-test/config/rush-project.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["lib-commonjs", "lib-dts", "lib-esm", "lib-es5", "temp/build"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] + } + ] +} diff --git a/build-tests/heft-swc-test/config/typescript.json b/build-tests/heft-swc-test/config/typescript.json new file mode 100644 index 00000000000..07809edc481 --- /dev/null +++ b/build-tests/heft-swc-test/config/typescript.json @@ -0,0 +1,6 @@ +/** + * Configures the TypeScript plugin for Heft. This plugin also manages linting. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json" +} diff --git a/build-tests/heft-swc-test/eslint.config.js b/build-tests/heft-swc-test/eslint.config.js new file mode 100644 index 00000000000..5a9df48909b --- /dev/null +++ b/build-tests/heft-swc-test/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-eslint-config/flat/profile/web-app'); + +module.exports = [ + ...webAppProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-swc-test/package.json b/build-tests/heft-swc-test/package.json new file mode 100644 index 00000000000..807350604ae --- /dev/null +++ b/build-tests/heft-swc-test/package.json @@ -0,0 +1,26 @@ +{ + "name": "heft-swc-test", + "description": "Building this project tests building with SWC", + "version": "1.0.0", + "private": true, + "main": "./lib/index.js", + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "build-watch": "heft build-watch --clean", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "@rushstack/heft-isolated-typescript-transpile-plugin": "workspace:*", + "@rushstack/heft-jest-plugin": "workspace:*", + "@rushstack/heft-lint-plugin": "workspace:*", + "@rushstack/heft-typescript-plugin": "workspace:*", + "@types/jest": "30.0.0", + "@types/webpack-env": "1.18.8", + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*", + "typescript": "~5.8.2" + } +} diff --git a/build-tests/heft-swc-test/src/index.ts b/build-tests/heft-swc-test/src/index.ts new file mode 100644 index 00000000000..659610ef84f --- /dev/null +++ b/build-tests/heft-swc-test/src/index.ts @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * @public + */ +export class TestClass {} diff --git a/build-tests/heft-swc-test/src/test/ExampleTest.test.ts b/build-tests/heft-swc-test/src/test/ExampleTest.test.ts new file mode 100644 index 00000000000..ccae242d321 --- /dev/null +++ b/build-tests/heft-swc-test/src/test/ExampleTest.test.ts @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +interface IInterface { + element: string; +} + +describe('Example Test', () => { + it('Correctly tests stuff', () => { + expect(true).toBeTruthy(); + }); + + it('Correctly handles snapshots', () => { + expect({ a: 1, b: 2, c: 3 }).toMatchSnapshot(); + }); + + it('Correctly handles TypeScript constructs', () => { + const interfaceInstance: IInterface = { + element: 'a' + }; + expect(interfaceInstance).toBeTruthy(); + }); +}); diff --git a/build-tests/heft-swc-test/src/test/__snapshots__/ExampleTest.test.ts.snap b/build-tests/heft-swc-test/src/test/__snapshots__/ExampleTest.test.ts.snap new file mode 100644 index 00000000000..8fdcc5d1121 --- /dev/null +++ b/build-tests/heft-swc-test/src/test/__snapshots__/ExampleTest.test.ts.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`Example Test Correctly handles snapshots 1`] = ` +Object { + "a": 1, + "b": 2, + "c": 3, +} +`; diff --git a/build-tests/heft-swc-test/tsconfig.json b/build-tests/heft-swc-test/tsconfig.json new file mode 100644 index 00000000000..3ba11a66303 --- /dev/null +++ b/build-tests/heft-swc-test/tsconfig.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib-esm", + "rootDir": "src", + + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "declarationDir": "lib-dts", + "emitDeclarationOnly": true, + "inlineSources": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "strictNullChecks": true, + "noUnusedLocals": true, + "types": ["jest", "webpack-env"], + + "module": "esnext", + "target": "esnext", + "lib": ["esnext"] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/build-tests/heft-typescript-composite-test/.eslintrc.js b/build-tests/heft-typescript-composite-test/.eslintrc.js deleted file mode 100644 index 2144bff3da6..00000000000 --- a/build-tests/heft-typescript-composite-test/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/web-app'], - parserOptions: { tsconfigRootDir: __dirname, project: './tsconfig-eslint.json' } -}; diff --git a/build-tests/heft-typescript-composite-test/config/heft.json b/build-tests/heft-typescript-composite-test/config/heft.json index ff2503a5a8a..c0cc9f94228 100644 --- a/build-tests/heft-typescript-composite-test/config/heft.json +++ b/build-tests/heft-typescript-composite-test/config/heft.json @@ -2,12 +2,13 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "lib" }, { "sourcePath": "lib-commonjs" }], + "cleanFiles": [{ "includeGlobs": ["lib", "lib-commonjs"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests/heft-typescript-composite-test/config/jest.config.json b/build-tests/heft-typescript-composite-test/config/jest.config.json index 41b0a39a8af..ee9b78eb63f 100644 --- a/build-tests/heft-typescript-composite-test/config/jest.config.json +++ b/build-tests/heft-typescript-composite-test/config/jest.config.json @@ -1,6 +1,14 @@ { "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8", + "testMatch": ["/lib/**/*.test.cjs"], "collectCoverageFrom": [ diff --git a/build-tests/heft-typescript-composite-test/config/rush-project.json b/build-tests/heft-typescript-composite-test/config/rush-project.json index 317ec878bc3..93089856e44 100644 --- a/build-tests/heft-typescript-composite-test/config/rush-project.json +++ b/build-tests/heft-typescript-composite-test/config/rush-project.json @@ -1,8 +1,14 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", + "operationName": "_phase:build", "outputFolderNames": ["lib"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests/heft-typescript-composite-test/config/typescript.json b/build-tests/heft-typescript-composite-test/config/typescript.json index 74d309d9193..2979f43bfb0 100644 --- a/build-tests/heft-typescript-composite-test/config/typescript.json +++ b/build-tests/heft-typescript-composite-test/config/typescript.json @@ -2,7 +2,7 @@ * Configures the TypeScript plugin for Heft. This plugin also manages linting. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", /** * If provided, emit these module kinds in addition to the modules specified in the tsconfig. diff --git a/build-tests/heft-typescript-composite-test/eslint.config.js b/build-tests/heft-typescript-composite-test/eslint.config.js new file mode 100644 index 00000000000..0b09ea9f25b --- /dev/null +++ b/build-tests/heft-typescript-composite-test/eslint.config.js @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-eslint-config/flat/profile/web-app'); + +module.exports = [ + ...webAppProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig-eslint.json' + } + } + } +]; diff --git a/build-tests/heft-typescript-composite-test/package.json b/build-tests/heft-typescript-composite-test/package.json index 293c98a97f7..fe878efcf44 100644 --- a/build-tests/heft-typescript-composite-test/package.json +++ b/build-tests/heft-typescript-composite-test/package.json @@ -10,17 +10,15 @@ "_phase:test": "heft run --only test -- --clean" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/jest": "29.2.5", - "@types/webpack-env": "1.18.0", - "eslint": "~8.7.0", + "@types/jest": "30.0.0", + "@types/webpack-env": "1.18.8", + "eslint": "~9.37.0", + "local-eslint-config": "workspace:*", "tslint": "~5.20.1", - "tslint-microsoft-contrib": "~6.2.0", - "typescript": "~5.0.4" + "typescript": "~5.8.2" } } diff --git a/build-tests/heft-typescript-composite-test/src/chunks/ChunkClass.ts b/build-tests/heft-typescript-composite-test/src/chunks/ChunkClass.ts index 79a43a9d249..ddbf7d148c7 100644 --- a/build-tests/heft-typescript-composite-test/src/chunks/ChunkClass.ts +++ b/build-tests/heft-typescript-composite-test/src/chunks/ChunkClass.ts @@ -1,9 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + export class ChunkClass { public doStuff(): void { + // eslint-disable-next-line no-console console.log('CHUNK'); } public getImageUrl(): string { + // eslint-disable-next-line @typescript-eslint/no-require-imports return require('./image.png'); } } diff --git a/build-tests/heft-typescript-composite-test/src/indexB.ts b/build-tests/heft-typescript-composite-test/src/indexB.ts index e122ca67a73..ffbc71e0a7f 100644 --- a/build-tests/heft-typescript-composite-test/src/indexB.ts +++ b/build-tests/heft-typescript-composite-test/src/indexB.ts @@ -1,3 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// eslint-disable-next-line no-console console.log('dostuff'); export {}; diff --git a/build-tests/heft-typescript-composite-test/tsconfig-base.json b/build-tests/heft-typescript-composite-test/tsconfig-base.json index b5bf6469742..d312c6331c2 100644 --- a/build-tests/heft-typescript-composite-test/tsconfig-base.json +++ b/build-tests/heft-typescript-composite-test/tsconfig-base.json @@ -14,7 +14,7 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest", "webpack-env"], + "types": ["jest", "webpack-env"], "isolatedModules": true, "verbatimModuleSyntax": true, @@ -22,6 +22,14 @@ "module": "esnext", "moduleResolution": "node", "target": "es5", - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"] + "lib": [ + "es5", + "scripthost", + "es2015.collection", + "es2015.promise", + "es2015.iterable", + "es2015.symbol.wellknown", + "dom" + ] } } diff --git a/build-tests/heft-typescript-composite-test/tslint.json b/build-tests/heft-typescript-composite-test/tslint.json index f55613b66cc..7dc059cfb49 100644 --- a/build-tests/heft-typescript-composite-test/tslint.json +++ b/build-tests/heft-typescript-composite-test/tslint.json @@ -1,13 +1,11 @@ { "$schema": "http://json.schemastore.org/tslint", - "rulesDirectory": ["tslint-microsoft-contrib"], "rules": { "class-name": true, "comment-format": [true, "check-space"], "curly": true, "eofline": false, - "export-name": true, "forin": true, "indent": [true, "spaces", 2], "interface-name": true, @@ -36,32 +34,29 @@ ] } ], - "missing-optional-annotation": true, "no-arg": true, "no-any": true, "no-bitwise": true, "no-consecutive-blank-lines": true, "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], - "no-constant-condition": true, "no-construct": true, "no-debugger": true, "no-duplicate-switch-case": true, - "no-duplicate-parameter-names": true, "no-duplicate-variable": true, "no-empty": true, "no-eval": true, "no-floating-promises": true, - "no-function-expression": true, "no-inferrable-types": false, "no-internal-module": true, "no-null-keyword": true, - "no-shadowed-variable": true, + + // This rule throws a DeprecationError exception with TypeScript 5.3.x + // "no-shadowed-variable": true, + "no-string-literal": true, "no-switch-case-fall-through": true, "no-trailing-whitespace": true, - "no-unnecessary-semicolons": true, "no-unused-expression": true, - "no-with-statement": true, "no-var-keyword": true, "object-literal-sort-keys": false, "one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"], @@ -96,7 +91,6 @@ } ], "use-isnan": true, - "use-named-parameter": true, "variable-name": [true, "check-format", "allow-leading-underscore", "ban-keywords"], "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"] } diff --git a/build-tests/heft-typescript-v2-test/config/api-extractor.json b/build-tests/heft-typescript-v2-test/config/api-extractor.json index e451cf705d0..d12edb988a5 100644 --- a/build-tests/heft-typescript-v2-test/config/api-extractor.json +++ b/build-tests/heft-typescript-v2-test/config/api-extractor.json @@ -1,7 +1,7 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - "mainEntryPointFilePath": "/lib/index.d.ts", + "mainEntryPointFilePath": "/lib-dts/index.d.ts", "apiReport": { "enabled": true, "reportFolder": "/etc" diff --git a/build-tests/heft-typescript-v2-test/config/heft.json b/build-tests/heft-typescript-v2-test/config/heft.json index 2f8f70f38b7..7a5e5b56ad9 100644 --- a/build-tests/heft-typescript-v2-test/config/heft.json +++ b/build-tests/heft-typescript-v2-test/config/heft.json @@ -1,16 +1,11 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [ - { "sourcePath": "dist" }, - { "sourcePath": "lib" }, - { "sourcePath": "lib-esnext" }, - { "sourcePath": "lib-umd" }, - { "sourcePath": "temp" } - ], + "cleanFiles": [{ "includeGlobs": ["dist", "lib", "lib-esnext", "lib-umd", "temp"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests/heft-typescript-v2-test/config/jest.config.json b/build-tests/heft-typescript-v2-test/config/jest.config.json index b6f305ec886..c0687c6d488 100644 --- a/build-tests/heft-typescript-v2-test/config/jest.config.json +++ b/build-tests/heft-typescript-v2-test/config/jest.config.json @@ -1,3 +1,11 @@ { - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json" + "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests/heft-typescript-v2-test/config/rush-project.json b/build-tests/heft-typescript-v2-test/config/rush-project.json index a9116e7e1f5..40a0d93f857 100644 --- a/build-tests/heft-typescript-v2-test/config/rush-project.json +++ b/build-tests/heft-typescript-v2-test/config/rush-project.json @@ -1,8 +1,14 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["dist", "lib", "lib-esnext", "lib-umd"] + "operationName": "_phase:build", + "outputFolderNames": ["dist", "lib-commonjs", "lib-esm", "lib-umd", "lib-dts"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests/heft-typescript-v2-test/config/typescript.json b/build-tests/heft-typescript-v2-test/config/typescript.json index a91d06e161b..85dc7d42b7a 100644 --- a/build-tests/heft-typescript-v2-test/config/typescript.json +++ b/build-tests/heft-typescript-v2-test/config/typescript.json @@ -2,7 +2,7 @@ * Configures the TypeScript plugin for Heft. This plugin also manages linting. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", /** * If provided, emit these module kinds in addition to the modules specified in the tsconfig. @@ -11,7 +11,7 @@ "additionalModuleKindsToEmit": [ { "moduleKind": "esnext", - "outFolderName": "lib-esnext" + "outFolderName": "lib-esm" }, { "moduleKind": "umd", diff --git a/build-tests/heft-typescript-v2-test/package.json b/build-tests/heft-typescript-v2-test/package.json index a0b1b1da16a..2f8e7b572e5 100644 --- a/build-tests/heft-typescript-v2-test/package.json +++ b/build-tests/heft-typescript-v2-test/package.json @@ -3,7 +3,7 @@ "description": "Building this project tests building with TypeScript v2", "version": "1.0.0", "private": true, - "main": "lib/index.js", + "main": "./lib/index.js", "license": "MIT", "scripts": { "build": "heft build --clean", @@ -20,7 +20,6 @@ "@types/jest": "ts2.9", "@types/node": "ts2.9", "tslint": "~5.20.1", - "tslint-microsoft-contrib": "~6.2.0", "typescript": "~2.9.2" } } diff --git a/build-tests/heft-typescript-v2-test/src/index.ts b/build-tests/heft-typescript-v2-test/src/index.ts index 15a2bae17e3..659610ef84f 100644 --- a/build-tests/heft-typescript-v2-test/src/index.ts +++ b/build-tests/heft-typescript-v2-test/src/index.ts @@ -4,4 +4,4 @@ /** * @public */ -export class TestClass {} // tslint:disable-line:export-name +export class TestClass {} diff --git a/build-tests/heft-typescript-v2-test/tsconfig.json b/build-tests/heft-typescript-v2-test/tsconfig.json index a24c429ddf6..819c8cda10f 100644 --- a/build-tests/heft-typescript-v2-test/tsconfig.json +++ b/build-tests/heft-typescript-v2-test/tsconfig.json @@ -2,7 +2,8 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", + "declarationDir": "lib-dts", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -18,8 +19,10 @@ "module": "commonjs", "target": "es2017", - "lib": ["es2017"] + "lib": ["es2017"], + + // TODO: REVERT THIS AFTER WE UPGRADE heft-typescript-v2-test TO A NEWER VERSION + "skipLibCheck": true }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-typescript-v2-test/tslint.json b/build-tests/heft-typescript-v2-test/tslint.json index f55613b66cc..56dfd9f2bb6 100644 --- a/build-tests/heft-typescript-v2-test/tslint.json +++ b/build-tests/heft-typescript-v2-test/tslint.json @@ -1,13 +1,11 @@ { "$schema": "http://json.schemastore.org/tslint", - "rulesDirectory": ["tslint-microsoft-contrib"], "rules": { "class-name": true, "comment-format": [true, "check-space"], "curly": true, "eofline": false, - "export-name": true, "forin": true, "indent": [true, "spaces", 2], "interface-name": true, @@ -36,22 +34,18 @@ ] } ], - "missing-optional-annotation": true, "no-arg": true, "no-any": true, "no-bitwise": true, "no-consecutive-blank-lines": true, "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], - "no-constant-condition": true, "no-construct": true, "no-debugger": true, "no-duplicate-switch-case": true, - "no-duplicate-parameter-names": true, "no-duplicate-variable": true, "no-empty": true, "no-eval": true, "no-floating-promises": true, - "no-function-expression": true, "no-inferrable-types": false, "no-internal-module": true, "no-null-keyword": true, @@ -59,9 +53,7 @@ "no-string-literal": true, "no-switch-case-fall-through": true, "no-trailing-whitespace": true, - "no-unnecessary-semicolons": true, "no-unused-expression": true, - "no-with-statement": true, "no-var-keyword": true, "object-literal-sort-keys": false, "one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"], @@ -96,7 +88,6 @@ } ], "use-isnan": true, - "use-named-parameter": true, "variable-name": [true, "check-format", "allow-leading-underscore", "ban-keywords"], "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"] } diff --git a/build-tests/heft-typescript-v3-test/config/api-extractor.json b/build-tests/heft-typescript-v3-test/config/api-extractor.json index e451cf705d0..d12edb988a5 100644 --- a/build-tests/heft-typescript-v3-test/config/api-extractor.json +++ b/build-tests/heft-typescript-v3-test/config/api-extractor.json @@ -1,7 +1,7 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - "mainEntryPointFilePath": "/lib/index.d.ts", + "mainEntryPointFilePath": "/lib-dts/index.d.ts", "apiReport": { "enabled": true, "reportFolder": "/etc" diff --git a/build-tests/heft-typescript-v3-test/config/heft.json b/build-tests/heft-typescript-v3-test/config/heft.json index 2f8f70f38b7..07fd7b53d05 100644 --- a/build-tests/heft-typescript-v3-test/config/heft.json +++ b/build-tests/heft-typescript-v3-test/config/heft.json @@ -1,16 +1,11 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [ - { "sourcePath": "dist" }, - { "sourcePath": "lib" }, - { "sourcePath": "lib-esnext" }, - { "sourcePath": "lib-umd" }, - { "sourcePath": "temp" } - ], + "cleanFiles": [{ "includeGlobs": ["dist", "lib-commonjs", "lib", "lib-esnext", "lib-umd", "temp"] }], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests/heft-typescript-v3-test/config/jest.config.json b/build-tests/heft-typescript-v3-test/config/jest.config.json index b6f305ec886..c0687c6d488 100644 --- a/build-tests/heft-typescript-v3-test/config/jest.config.json +++ b/build-tests/heft-typescript-v3-test/config/jest.config.json @@ -1,3 +1,11 @@ { - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json" + "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests/heft-typescript-v3-test/config/rush-project.json b/build-tests/heft-typescript-v3-test/config/rush-project.json index a9116e7e1f5..40a0d93f857 100644 --- a/build-tests/heft-typescript-v3-test/config/rush-project.json +++ b/build-tests/heft-typescript-v3-test/config/rush-project.json @@ -1,8 +1,14 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["dist", "lib", "lib-esnext", "lib-umd"] + "operationName": "_phase:build", + "outputFolderNames": ["dist", "lib-commonjs", "lib-esm", "lib-umd", "lib-dts"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests/heft-typescript-v3-test/config/typescript.json b/build-tests/heft-typescript-v3-test/config/typescript.json index a91d06e161b..85dc7d42b7a 100644 --- a/build-tests/heft-typescript-v3-test/config/typescript.json +++ b/build-tests/heft-typescript-v3-test/config/typescript.json @@ -2,7 +2,7 @@ * Configures the TypeScript plugin for Heft. This plugin also manages linting. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", /** * If provided, emit these module kinds in addition to the modules specified in the tsconfig. @@ -11,7 +11,7 @@ "additionalModuleKindsToEmit": [ { "moduleKind": "esnext", - "outFolderName": "lib-esnext" + "outFolderName": "lib-esm" }, { "moduleKind": "umd", diff --git a/build-tests/heft-typescript-v3-test/package.json b/build-tests/heft-typescript-v3-test/package.json index 042c2c64fdc..8f8fb7a56e1 100644 --- a/build-tests/heft-typescript-v3-test/package.json +++ b/build-tests/heft-typescript-v3-test/package.json @@ -3,7 +3,7 @@ "description": "Building this project tests building with TypeScript v3", "version": "1.0.0", "private": true, - "main": "lib/index.js", + "main": "./lib/index.js", "license": "MIT", "scripts": { "build": "heft build --clean", @@ -20,7 +20,6 @@ "@types/jest": "ts3.9", "@types/node": "ts3.9", "tslint": "~5.20.1", - "tslint-microsoft-contrib": "~6.2.0", "typescript": "~3.9.10" } } diff --git a/build-tests/heft-typescript-v3-test/src/index.ts b/build-tests/heft-typescript-v3-test/src/index.ts index 15a2bae17e3..659610ef84f 100644 --- a/build-tests/heft-typescript-v3-test/src/index.ts +++ b/build-tests/heft-typescript-v3-test/src/index.ts @@ -4,4 +4,4 @@ /** * @public */ -export class TestClass {} // tslint:disable-line:export-name +export class TestClass {} diff --git a/build-tests/heft-typescript-v3-test/tsconfig.json b/build-tests/heft-typescript-v3-test/tsconfig.json index a24c429ddf6..a60e33c7bfa 100644 --- a/build-tests/heft-typescript-v3-test/tsconfig.json +++ b/build-tests/heft-typescript-v3-test/tsconfig.json @@ -2,7 +2,8 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", + "declarationDir": "lib-dts", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -15,11 +16,12 @@ "strictNullChecks": true, "noUnusedLocals": true, "types": ["jest", "node"], + // Skips lib check to suppress api-extractor Uint8Array generic type incompatibility with @types/node@17.0.41 + "skipLibCheck": true, "module": "commonjs", "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-typescript-v3-test/tslint.json b/build-tests/heft-typescript-v3-test/tslint.json index f55613b66cc..56dfd9f2bb6 100644 --- a/build-tests/heft-typescript-v3-test/tslint.json +++ b/build-tests/heft-typescript-v3-test/tslint.json @@ -1,13 +1,11 @@ { "$schema": "http://json.schemastore.org/tslint", - "rulesDirectory": ["tslint-microsoft-contrib"], "rules": { "class-name": true, "comment-format": [true, "check-space"], "curly": true, "eofline": false, - "export-name": true, "forin": true, "indent": [true, "spaces", 2], "interface-name": true, @@ -36,22 +34,18 @@ ] } ], - "missing-optional-annotation": true, "no-arg": true, "no-any": true, "no-bitwise": true, "no-consecutive-blank-lines": true, "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], - "no-constant-condition": true, "no-construct": true, "no-debugger": true, "no-duplicate-switch-case": true, - "no-duplicate-parameter-names": true, "no-duplicate-variable": true, "no-empty": true, "no-eval": true, "no-floating-promises": true, - "no-function-expression": true, "no-inferrable-types": false, "no-internal-module": true, "no-null-keyword": true, @@ -59,9 +53,7 @@ "no-string-literal": true, "no-switch-case-fall-through": true, "no-trailing-whitespace": true, - "no-unnecessary-semicolons": true, "no-unused-expression": true, - "no-with-statement": true, "no-var-keyword": true, "object-literal-sort-keys": false, "one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"], @@ -96,7 +88,6 @@ } ], "use-isnan": true, - "use-named-parameter": true, "variable-name": [true, "check-format", "allow-leading-underscore", "ban-keywords"], "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"] } diff --git a/build-tests/heft-typescript-v4-test/.eslintrc.js b/build-tests/heft-typescript-v4-test/.eslintrc.js index 60160b354c4..5d2d42aa7de 100644 --- a/build-tests/heft-typescript-v4-test/.eslintrc.js +++ b/build-tests/heft-typescript-v4-test/.eslintrc.js @@ -1,5 +1,7 @@ // This is a workaround for https://github.com/eslint/eslint/issues/3458 require('@rushstack/eslint-config/patch/modern-module-resolution'); +// This is a workaround for https://github.com/microsoft/rushstack/issues/3021 +require('@rushstack/eslint-config/patch/custom-config-package-names'); module.exports = { extends: ['@rushstack/eslint-config/profile/node'], diff --git a/build-tests/heft-typescript-v4-test/config/api-extractor.json b/build-tests/heft-typescript-v4-test/config/api-extractor.json index e451cf705d0..d12edb988a5 100644 --- a/build-tests/heft-typescript-v4-test/config/api-extractor.json +++ b/build-tests/heft-typescript-v4-test/config/api-extractor.json @@ -1,7 +1,7 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - "mainEntryPointFilePath": "/lib/index.d.ts", + "mainEntryPointFilePath": "/lib-dts/index.d.ts", "apiReport": { "enabled": true, "reportFolder": "/etc" diff --git a/build-tests/heft-typescript-v4-test/config/heft.json b/build-tests/heft-typescript-v4-test/config/heft.json index 2f8f70f38b7..460ff4c0c94 100644 --- a/build-tests/heft-typescript-v4-test/config/heft.json +++ b/build-tests/heft-typescript-v4-test/config/heft.json @@ -1,16 +1,13 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { "cleanFiles": [ - { "sourcePath": "dist" }, - { "sourcePath": "lib" }, - { "sourcePath": "lib-esnext" }, - { "sourcePath": "lib-umd" }, - { "sourcePath": "temp" } + { "includeGlobs": ["dist", "lib-commonjs", "lib", "lib-esm", "lib-esnext", "lib-umd", "temp"] } ], + "tasksByName": { "typescript": { "taskPlugin": { diff --git a/build-tests/heft-typescript-v4-test/config/jest.config.json b/build-tests/heft-typescript-v4-test/config/jest.config.json index b6f305ec886..c0687c6d488 100644 --- a/build-tests/heft-typescript-v4-test/config/jest.config.json +++ b/build-tests/heft-typescript-v4-test/config/jest.config.json @@ -1,3 +1,11 @@ { - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json" + "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests/heft-typescript-v4-test/config/rush-project.json b/build-tests/heft-typescript-v4-test/config/rush-project.json index a9116e7e1f5..40a0d93f857 100644 --- a/build-tests/heft-typescript-v4-test/config/rush-project.json +++ b/build-tests/heft-typescript-v4-test/config/rush-project.json @@ -1,8 +1,14 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["dist", "lib", "lib-esnext", "lib-umd"] + "operationName": "_phase:build", + "outputFolderNames": ["dist", "lib-commonjs", "lib-esm", "lib-umd", "lib-dts"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests/heft-typescript-v4-test/config/typescript.json b/build-tests/heft-typescript-v4-test/config/typescript.json index a91d06e161b..85dc7d42b7a 100644 --- a/build-tests/heft-typescript-v4-test/config/typescript.json +++ b/build-tests/heft-typescript-v4-test/config/typescript.json @@ -2,7 +2,7 @@ * Configures the TypeScript plugin for Heft. This plugin also manages linting. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", /** * If provided, emit these module kinds in addition to the modules specified in the tsconfig. @@ -11,7 +11,7 @@ "additionalModuleKindsToEmit": [ { "moduleKind": "esnext", - "outFolderName": "lib-esnext" + "outFolderName": "lib-esm" }, { "moduleKind": "umd", diff --git a/build-tests/heft-typescript-v4-test/package.json b/build-tests/heft-typescript-v4-test/package.json index 1817ce48f57..027a3cbabf7 100644 --- a/build-tests/heft-typescript-v4-test/package.json +++ b/build-tests/heft-typescript-v4-test/package.json @@ -3,7 +3,7 @@ "description": "Building this project tests building with TypeScript v4", "version": "1.0.0", "private": true, - "main": "lib/index.js", + "main": "./lib/index.js", "license": "MIT", "scripts": { "build": "heft build --clean", @@ -12,7 +12,8 @@ }, "devDependencies": { "@microsoft/api-extractor": "workspace:*", - "@rushstack/eslint-config": "workspace:*", + "@rushstack/eslint-config": "4.6.4", + "@rushstack/eslint-patch": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/heft-api-extractor-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", @@ -20,9 +21,8 @@ "@rushstack/heft-typescript-plugin": "workspace:*", "@types/jest": "ts4.9", "@types/node": "ts4.9", - "eslint": "~8.7.0", + "eslint": "~8.57.0", "tslint": "~5.20.1", - "tslint-microsoft-contrib": "~6.2.0", "typescript": "~4.9.5" } } diff --git a/build-tests/heft-typescript-v4-test/src/index.ts b/build-tests/heft-typescript-v4-test/src/index.ts index 15a2bae17e3..659610ef84f 100644 --- a/build-tests/heft-typescript-v4-test/src/index.ts +++ b/build-tests/heft-typescript-v4-test/src/index.ts @@ -4,4 +4,4 @@ /** * @public */ -export class TestClass {} // tslint:disable-line:export-name +export class TestClass {} diff --git a/build-tests/heft-typescript-v4-test/tsconfig.json b/build-tests/heft-typescript-v4-test/tsconfig.json index a24c429ddf6..9bb1bff372e 100644 --- a/build-tests/heft-typescript-v4-test/tsconfig.json +++ b/build-tests/heft-typescript-v4-test/tsconfig.json @@ -2,7 +2,8 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", + "declarationDir": "lib-dts", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -20,6 +21,5 @@ "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-typescript-v4-test/tslint.json b/build-tests/heft-typescript-v4-test/tslint.json index f55613b66cc..56dfd9f2bb6 100644 --- a/build-tests/heft-typescript-v4-test/tslint.json +++ b/build-tests/heft-typescript-v4-test/tslint.json @@ -1,13 +1,11 @@ { "$schema": "http://json.schemastore.org/tslint", - "rulesDirectory": ["tslint-microsoft-contrib"], "rules": { "class-name": true, "comment-format": [true, "check-space"], "curly": true, "eofline": false, - "export-name": true, "forin": true, "indent": [true, "spaces", 2], "interface-name": true, @@ -36,22 +34,18 @@ ] } ], - "missing-optional-annotation": true, "no-arg": true, "no-any": true, "no-bitwise": true, "no-consecutive-blank-lines": true, "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], - "no-constant-condition": true, "no-construct": true, "no-debugger": true, "no-duplicate-switch-case": true, - "no-duplicate-parameter-names": true, "no-duplicate-variable": true, "no-empty": true, "no-eval": true, "no-floating-promises": true, - "no-function-expression": true, "no-inferrable-types": false, "no-internal-module": true, "no-null-keyword": true, @@ -59,9 +53,7 @@ "no-string-literal": true, "no-switch-case-fall-through": true, "no-trailing-whitespace": true, - "no-unnecessary-semicolons": true, "no-unused-expression": true, - "no-with-statement": true, "no-var-keyword": true, "object-literal-sort-keys": false, "one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"], @@ -96,7 +88,6 @@ } ], "use-isnan": true, - "use-named-parameter": true, "variable-name": [true, "check-format", "allow-leading-underscore", "ban-keywords"], "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"] } diff --git a/build-tests/heft-web-rig-library-test/config/jest.config.json b/build-tests/heft-web-rig-library-test/config/jest.config.json index 600ba9ea39a..22bfd5895cd 100644 --- a/build-tests/heft-web-rig-library-test/config/jest.config.json +++ b/build-tests/heft-web-rig-library-test/config/jest.config.json @@ -1,3 +1,11 @@ { - "extends": "@rushstack/heft-web-rig/profiles/library/config/jest.config.json" + "extends": "@rushstack/heft-web-rig/profiles/library/config/jest.config.json", + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests/heft-web-rig-library-test/config/rush-project.json b/build-tests/heft-web-rig-library-test/config/rush-project.json new file mode 100644 index 00000000000..5cfd2f72217 --- /dev/null +++ b/build-tests/heft-web-rig-library-test/config/rush-project.json @@ -0,0 +1,14 @@ +{ + "extends": "@rushstack/heft-web-rig/profiles/library/config/rush-project.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": [".heft", "release"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] + } + ] +} diff --git a/build-tests/heft-web-rig-library-test/eslint.config.js b/build-tests/heft-web-rig-library-test/eslint.config.js new file mode 100644 index 00000000000..2c2f8d27066 --- /dev/null +++ b/build-tests/heft-web-rig-library-test/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('@rushstack/heft-web-rig/profiles/library/includes/eslint/flat/profile/web-app'); + +module.exports = [ + ...webAppProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-web-rig-library-test/package.json b/build-tests/heft-web-rig-library-test/package.json index 7b5f3a08559..037696daccd 100644 --- a/build-tests/heft-web-rig-library-test/package.json +++ b/build-tests/heft-web-rig-library-test/package.json @@ -3,7 +3,7 @@ "description": "A test project for Heft that exercises the '@rushstack/heft-web-rig' package", "version": "1.0.0", "private": true, - "main": "lib/index.js", + "main": "./lib/index.js", "license": "MIT", "scripts": { "build": "heft build --clean", @@ -12,6 +12,7 @@ }, "devDependencies": { "@rushstack/heft": "workspace:*", - "@rushstack/heft-web-rig": "workspace:*" + "@rushstack/heft-web-rig": "workspace:*", + "eslint": "~9.37.0" } } diff --git a/build-tests/heft-webpack4-everything-test/.eslintrc.js b/build-tests/heft-webpack4-everything-test/.eslintrc.js deleted file mode 100644 index 999926cceed..00000000000 --- a/build-tests/heft-webpack4-everything-test/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/web-app'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/heft-webpack4-everything-test/config/heft.json b/build-tests/heft-webpack4-everything-test/config/heft.json index 94a564def06..e90bf3d3c74 100644 --- a/build-tests/heft-webpack4-everything-test/config/heft.json +++ b/build-tests/heft-webpack4-everything-test/config/heft.json @@ -2,14 +2,26 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }, { "sourcePath": "lib-commonjs" }], + "cleanFiles": [{ "includeGlobs": ["dist", "lib", "lib-commonjs"] }], + "tasksByName": { + "image-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "resource-assets-plugin", + "options": { + "configType": "file", + "configFileName": "resource-assets.json" + } + } + }, "typescript": { + "taskDependencies": ["image-typings"], "taskPlugin": { "pluginPackage": "@rushstack/heft-typescript-plugin" } diff --git a/build-tests/heft-webpack4-everything-test/config/jest.config.json b/build-tests/heft-webpack4-everything-test/config/jest.config.json index 52635a8eb53..331b9187503 100644 --- a/build-tests/heft-webpack4-everything-test/config/jest.config.json +++ b/build-tests/heft-webpack4-everything-test/config/jest.config.json @@ -1,16 +1,11 @@ { - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", + "extends": "@rushstack/heft-jest-plugin/includes/jest-web.config.json", - "roots": ["/lib-commonjs"], + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], - "testMatch": ["/lib-commonjs/**/*.test.js"], - "collectCoverageFrom": [ - "lib-commonjs/**/*.js", - "!lib-commonjs/**/*.d.ts", - "!lib-commonjs/**/*.test.js", - "!lib-commonjs/**/test/**", - "!lib-commonjs/**/__tests__/**", - "!lib-commonjs/**/__fixtures__/**", - "!lib-commonjs/**/__mocks__/**" - ] + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8" } diff --git a/build-tests/heft-webpack4-everything-test/config/resource-assets.json b/build-tests/heft-webpack4-everything-test/config/resource-assets.json new file mode 100644 index 00000000000..09dfe4998d3 --- /dev/null +++ b/build-tests/heft-webpack4-everything-test/config/resource-assets.json @@ -0,0 +1,4 @@ +{ + "fileExtensions": [".png"], + "generatedTsFolders": ["temp/image-typings"] +} diff --git a/build-tests/heft-webpack4-everything-test/config/rush-project.json b/build-tests/heft-webpack4-everything-test/config/rush-project.json index 247dc17187a..f1de027644c 100644 --- a/build-tests/heft-webpack4-everything-test/config/rush-project.json +++ b/build-tests/heft-webpack4-everything-test/config/rush-project.json @@ -1,8 +1,14 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + "outputFolderNames": ["lib-esm", "lib-commonjs", "dist", "temp/image-typings"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests/heft-webpack4-everything-test/config/typescript.json b/build-tests/heft-webpack4-everything-test/config/typescript.json index b06158df772..c9a48461bbc 100644 --- a/build-tests/heft-webpack4-everything-test/config/typescript.json +++ b/build-tests/heft-webpack4-everything-test/config/typescript.json @@ -2,7 +2,7 @@ * Configures the TypeScript plugin for Heft. This plugin also manages linting. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", /** * If provided, emit these module kinds in addition to the modules specified in the tsconfig. diff --git a/build-tests/heft-webpack4-everything-test/eslint.config.js b/build-tests/heft-webpack4-everything-test/eslint.config.js new file mode 100644 index 00000000000..5a9df48909b --- /dev/null +++ b/build-tests/heft-webpack4-everything-test/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-eslint-config/flat/profile/web-app'); + +module.exports = [ + ...webAppProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-webpack4-everything-test/package.json b/build-tests/heft-webpack4-everything-test/package.json index 61434a3c6e8..9e17f24f09b 100644 --- a/build-tests/heft-webpack4-everything-test/package.json +++ b/build-tests/heft-webpack4-everything-test/package.json @@ -10,20 +10,25 @@ "_phase:test": "heft run --only test -- --clean" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft": "workspace:*", "@rushstack/heft-dev-cert-plugin": "workspace:*", + "@rushstack/heft-static-asset-typings-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", "@rushstack/heft-webpack4-plugin": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/webpack-env": "1.18.0", - "eslint": "~8.7.0", + "@rushstack/heft": "workspace:*", + "@rushstack/module-minifier": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "@rushstack/webpack4-module-minifier-plugin": "workspace:*", + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "@types/webpack-env": "1.18.8", + "eslint": "~9.37.0", "file-loader": "~6.0.0", + "local-eslint-config": "workspace:*", + "source-map-loader": "~1.1.3", "tslint": "~5.20.1", - "tslint-microsoft-contrib": "~6.2.0", - "typescript": "~5.0.4", - "webpack": "~4.44.2" + "typescript": "~5.8.2", + "webpack": "~4.47.0" } } diff --git a/build-tests/heft-webpack4-everything-test/src/chunks/ChunkClass.ts b/build-tests/heft-webpack4-everything-test/src/chunks/ChunkClass.ts index 79a43a9d249..50e541f593d 100644 --- a/build-tests/heft-webpack4-everything-test/src/chunks/ChunkClass.ts +++ b/build-tests/heft-webpack4-everything-test/src/chunks/ChunkClass.ts @@ -1,9 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import image from './image.png'; + export class ChunkClass { public doStuff(): void { + // eslint-disable-next-line no-console console.log('CHUNK'); } public getImageUrl(): string { - return require('./image.png'); + return image; } } diff --git a/build-tests/heft-webpack4-everything-test/src/indexB.ts b/build-tests/heft-webpack4-everything-test/src/indexB.ts index e122ca67a73..ffbc71e0a7f 100644 --- a/build-tests/heft-webpack4-everything-test/src/indexB.ts +++ b/build-tests/heft-webpack4-everything-test/src/indexB.ts @@ -1,3 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// eslint-disable-next-line no-console console.log('dostuff'); export {}; diff --git a/build-tests/heft-webpack4-everything-test/src/test/SourceMapTest.test.ts b/build-tests/heft-webpack4-everything-test/src/test/SourceMapTest.test.ts new file mode 100644 index 00000000000..d1357f910cb --- /dev/null +++ b/build-tests/heft-webpack4-everything-test/src/test/SourceMapTest.test.ts @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { FileSystem, PackageJsonLookup } from '@rushstack/node-core-library'; + +interface IMap { + sources?: string[]; + file?: string; + sourcesContent?: string[]; + names?: string[]; +} + +interface IMapValue { + mapFileName: string; + mapObject: IMap; +} + +interface IMapTestEntry { + name: string; + mapRegex: RegExp; + sourceFileRegex: RegExp; + map: IMapValue | undefined; +} + +const mapTests: IMapTestEntry[] = [ + { + name: 'Test-A', + mapRegex: /^heft-test-A_[\w\d]*\.js.map$/, + sourceFileRegex: /indexA\.ts$/, + map: undefined + }, + { + name: 'Test-B', + mapRegex: /^heft-test-B_[\w\d]*\.js.map$/, + sourceFileRegex: /indexB\.ts$/, + map: undefined + }, + { + name: 'Chunk', + mapRegex: /^[\w\d\.]*chunk_[\w\d]*\.js.map$/, + sourceFileRegex: /ChunkClass\.ts$/, + map: undefined + } +]; + +const lookup: PackageJsonLookup = new PackageJsonLookup(); +lookup.tryGetPackageFolderFor(__dirname); +const thisProjectFolder: string | undefined = lookup.tryGetPackageFolderFor(__dirname); +if (!thisProjectFolder) { + throw new Error('Cannot find project folder'); +} +const distEntries: string[] = FileSystem.readFolderItemNames(thisProjectFolder + '/dist'); +for (const distEntry of distEntries) { + for (const test of mapTests) { + if (test.mapRegex.test(distEntry)) { + const mapText: string = FileSystem.readFile(`${thisProjectFolder}/dist/${distEntry}`); + const mapObject: IMap = JSON.parse(mapText); + test.map = { + mapFileName: distEntry, + mapObject + }; + } + } +} + +describe('Source Maps', () => { + for (const test of mapTests) { + mapValueCheck(test); + } +}); + +function mapValueCheck(entry: IMapTestEntry): void { + it(`${entry.name} has map value`, () => { + expect(entry.map).toBeTruthy(); + }); + + if (!entry.map) { + return; + } + + const map: IMapValue = entry.map; + + it(`${entry.name} has filename matching file attribute`, () => { + if (map.mapObject.file) { + expect(map.mapFileName).toMatch(`${map.mapObject.file}.map`); + } + }); + + const properties: (keyof IMap)[] = ['sources', 'file', 'sourcesContent', 'names']; + for (const property of properties) { + it(`${map.mapFileName} has ${property} property`, () => { + expect(map.mapObject[property]).toBeTruthy(); + }); + } + + it(`${entry.name} has sources and sourcesContent arrays of the same length`, () => { + if (map.mapObject.sourcesContent && map.mapObject.sources) { + let numSrcs: number = 0; + for (const source of map.mapObject.sources) { + if (source) { + numSrcs++; + } + } + + let numContents: number = 0; + for (const content of map.mapObject.sourcesContent) { + if (content) { + numContents++; + } + } + expect(numSrcs).toEqual(numContents); + } + }); + + // TODO: remove skip when mapping back to .ts files is debugged + it.skip(`${entry.name} has a source that matches the sourceFileRegex`, () => { + if (map.mapObject.sources) { + expect(map.mapObject.sources).toContainEqual(expect.stringMatching(entry.sourceFileRegex)); + } + }); +} diff --git a/build-tests/heft-webpack4-everything-test/tsconfig.json b/build-tests/heft-webpack4-everything-test/tsconfig.json index eb547d1f50d..7a85932fe67 100644 --- a/build-tests/heft-webpack4-everything-test/tsconfig.json +++ b/build-tests/heft-webpack4-everything-test/tsconfig.json @@ -2,10 +2,12 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", - "rootDir": "src", + "outDir": "lib-esm", + "rootDirs": ["src", "temp/image-typings"], "forceConsistentCasingInFileNames": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, "jsx": "react", "declaration": true, "sourceMap": true, @@ -14,7 +16,7 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest", "webpack-env"], + "types": ["jest", "webpack-env"], "incremental": true, "isolatedModules": true, @@ -23,6 +25,5 @@ "target": "es5", "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-webpack4-everything-test/tslint.json b/build-tests/heft-webpack4-everything-test/tslint.json index f55613b66cc..7dc059cfb49 100644 --- a/build-tests/heft-webpack4-everything-test/tslint.json +++ b/build-tests/heft-webpack4-everything-test/tslint.json @@ -1,13 +1,11 @@ { "$schema": "http://json.schemastore.org/tslint", - "rulesDirectory": ["tslint-microsoft-contrib"], "rules": { "class-name": true, "comment-format": [true, "check-space"], "curly": true, "eofline": false, - "export-name": true, "forin": true, "indent": [true, "spaces", 2], "interface-name": true, @@ -36,32 +34,29 @@ ] } ], - "missing-optional-annotation": true, "no-arg": true, "no-any": true, "no-bitwise": true, "no-consecutive-blank-lines": true, "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], - "no-constant-condition": true, "no-construct": true, "no-debugger": true, "no-duplicate-switch-case": true, - "no-duplicate-parameter-names": true, "no-duplicate-variable": true, "no-empty": true, "no-eval": true, "no-floating-promises": true, - "no-function-expression": true, "no-inferrable-types": false, "no-internal-module": true, "no-null-keyword": true, - "no-shadowed-variable": true, + + // This rule throws a DeprecationError exception with TypeScript 5.3.x + // "no-shadowed-variable": true, + "no-string-literal": true, "no-switch-case-fall-through": true, "no-trailing-whitespace": true, - "no-unnecessary-semicolons": true, "no-unused-expression": true, - "no-with-statement": true, "no-var-keyword": true, "object-literal-sort-keys": false, "one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"], @@ -96,7 +91,6 @@ } ], "use-isnan": true, - "use-named-parameter": true, "variable-name": [true, "check-format", "allow-leading-underscore", "ban-keywords"], "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"] } diff --git a/build-tests/heft-webpack4-everything-test/webpack.config.js b/build-tests/heft-webpack4-everything-test/webpack.config.js index fe5bef928ed..f4382840815 100644 --- a/build-tests/heft-webpack4-everything-test/webpack.config.js +++ b/build-tests/heft-webpack4-everything-test/webpack.config.js @@ -1,6 +1,8 @@ 'use strict'; const path = require('path'); +const { ModuleMinifierPlugin } = require('@rushstack/webpack4-module-minifier-plugin'); +const { WorkerPoolMinifier } = require('@rushstack/module-minifier'); module.exports = { mode: 'development', @@ -13,19 +15,40 @@ module.exports = { loader: 'file-loader' } ] + }, + { + test: /\.js$/, + enforce: 'pre', + use: ['source-map-loader'] } ] }, resolve: { - extensions: ['.js', '.jsx', '.json'] + extensions: ['.js', '.json'] }, entry: { - 'heft-test-A': path.join(__dirname, 'lib', 'indexA.js'), - 'heft-test-B': path.join(__dirname, 'lib', 'indexB.js') + 'heft-test-A': path.join(__dirname, 'lib-esm', 'indexA.js'), + 'heft-test-B': path.join(__dirname, 'lib-esm', 'indexB.js') }, output: { path: path.join(__dirname, 'dist'), filename: '[name]_[contenthash].js', chunkFilename: '[id].[name]_[contenthash].js' + }, + devtool: 'source-map', + optimization: { + minimize: true, + minimizer: [ + new ModuleMinifierPlugin({ + minifier: new WorkerPoolMinifier({ + terserOptions: { + ecma: 2020, + mangle: true + }, + verbose: true + }), + sourceMap: true + }) + ] } }; diff --git a/build-tests/heft-webpack5-everything-test/.eslintrc.js b/build-tests/heft-webpack5-everything-test/.eslintrc.js deleted file mode 100644 index 999926cceed..00000000000 --- a/build-tests/heft-webpack5-everything-test/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/web-app'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/heft-webpack5-everything-test/config/heft.json b/build-tests/heft-webpack5-everything-test/config/heft.json index f2911cf5b9a..77ef3731529 100644 --- a/build-tests/heft-webpack5-everything-test/config/heft.json +++ b/build-tests/heft-webpack5-everything-test/config/heft.json @@ -2,14 +2,29 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", // TODO: Add comments "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }, { "sourcePath": "lib-commonjs" }], + "cleanFiles": [{ "includeGlobs": ["dist", "lib", "lib-commonjs", "temp/image-typings"] }], + "tasksByName": { + "image-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "resource-assets-plugin", + "options": { + "configType": "inline", + "config": { + "fileExtensions": [".png"], + "generatedTsFolders": ["temp/image-typings"] + } + } + } + }, "typescript": { + "taskDependencies": ["image-typings"], "taskPlugin": { "pluginPackage": "@rushstack/heft-typescript-plugin" } diff --git a/build-tests/heft-webpack5-everything-test/config/jest.config.json b/build-tests/heft-webpack5-everything-test/config/jest.config.json index 52635a8eb53..210c642e7a8 100644 --- a/build-tests/heft-webpack5-everything-test/config/jest.config.json +++ b/build-tests/heft-webpack5-everything-test/config/jest.config.json @@ -1,16 +1,15 @@ { - "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", + "extends": "@rushstack/heft-jest-plugin/includes/jest-web.config.json", "roots": ["/lib-commonjs"], - "testMatch": ["/lib-commonjs/**/*.test.js"], - "collectCoverageFrom": [ - "lib-commonjs/**/*.js", - "!lib-commonjs/**/*.d.ts", - "!lib-commonjs/**/*.test.js", - "!lib-commonjs/**/test/**", - "!lib-commonjs/**/__tests__/**", - "!lib-commonjs/**/__fixtures__/**", - "!lib-commonjs/**/__mocks__/**" - ] + + // Enable code coverage for Jest + "collectCoverage": true, + "coverageDirectory": "/coverage", + "coverageReporters": ["cobertura", "html"], + + // Use v8 coverage provider to avoid Babel + "coverageProvider": "v8", + "resolver": "@rushstack/heft-jest-plugin/lib-commonjs/exports/jest-node-modules-symlink-resolver" } diff --git a/build-tests/heft-webpack5-everything-test/config/rush-project.json b/build-tests/heft-webpack5-everything-test/config/rush-project.json index 247dc17187a..f1de027644c 100644 --- a/build-tests/heft-webpack5-everything-test/config/rush-project.json +++ b/build-tests/heft-webpack5-everything-test/config/rush-project.json @@ -1,8 +1,14 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + "outputFolderNames": ["lib-esm", "lib-commonjs", "dist", "temp/image-typings"] + }, + { + "operationName": "_phase:test", + "outputFolderNames": ["coverage"] } ] } diff --git a/build-tests/heft-webpack5-everything-test/config/typescript.json b/build-tests/heft-webpack5-everything-test/config/typescript.json index 435c2ca42eb..86a32ee3552 100644 --- a/build-tests/heft-webpack5-everything-test/config/typescript.json +++ b/build-tests/heft-webpack5-everything-test/config/typescript.json @@ -2,7 +2,7 @@ * Configures the TypeScript plugin for Heft. This plugin also manages linting. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", /** * If provided, emit these module kinds in addition to the modules specified in the tsconfig. @@ -49,5 +49,7 @@ // "excludeGlobs": [ // "some/path/*.css" // ] - } + }, + + "onlyResolveSymlinksInNodeModules": true } diff --git a/build-tests/heft-webpack5-everything-test/eslint.config.js b/build-tests/heft-webpack5-everything-test/eslint.config.js new file mode 100644 index 00000000000..5a9df48909b --- /dev/null +++ b/build-tests/heft-webpack5-everything-test/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-eslint-config/flat/profile/web-app'); + +module.exports = [ + ...webAppProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/heft-webpack5-everything-test/package.json b/build-tests/heft-webpack5-everything-test/package.json index dd8ca4dc0e5..61ef576df2e 100644 --- a/build-tests/heft-webpack5-everything-test/package.json +++ b/build-tests/heft-webpack5-everything-test/package.json @@ -7,25 +7,31 @@ "build": "heft build --clean", "start": "heft build-watch", "_phase:build": "heft run --only build -- --clean", - "_phase:test": "heft run --only test -- --clean" + "_phase:build:ipc": "heft run-watch --only build -- --clean", + "_phase:test": "heft run --only test -- --clean", + "_phase:test:ipc": "heft run-watch --only test -- --clean" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft": "workspace:*", "@rushstack/heft-dev-cert-plugin": "workspace:*", + "@rushstack/heft-static-asset-typings-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", "@rushstack/heft-webpack5-plugin": "workspace:*", + "@rushstack/heft": "workspace:*", "@rushstack/module-minifier": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "@rushstack/rush-sdk": "workspace:*", "@rushstack/webpack5-module-minifier-plugin": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/webpack-env": "1.18.0", - "eslint": "~8.7.0", + "@types/jest": "30.0.0", + "@types/node": "20.17.19", + "@types/webpack-env": "1.18.8", + "eslint": "~9.37.0", "html-webpack-plugin": "~5.5.0", + "local-eslint-config": "workspace:*", + "source-map-loader": "~3.0.1", "tslint": "~5.20.1", - "tslint-microsoft-contrib": "~6.2.0", - "typescript": "~5.0.4", - "webpack": "~5.80.0" + "typescript": "~5.8.2", + "webpack": "~5.105.2" } } diff --git a/build-tests/heft-webpack5-everything-test/src/chunks/ChunkClass.ts b/build-tests/heft-webpack5-everything-test/src/chunks/ChunkClass.ts index 79a43a9d249..50e541f593d 100644 --- a/build-tests/heft-webpack5-everything-test/src/chunks/ChunkClass.ts +++ b/build-tests/heft-webpack5-everything-test/src/chunks/ChunkClass.ts @@ -1,9 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import image from './image.png'; + export class ChunkClass { public doStuff(): void { + // eslint-disable-next-line no-console console.log('CHUNK'); } public getImageUrl(): string { - return require('./image.png'); + return image; } } diff --git a/build-tests/heft-webpack5-everything-test/src/indexB.ts b/build-tests/heft-webpack5-everything-test/src/indexB.ts index 16401835981..2bcd3820a4b 100644 --- a/build-tests/heft-webpack5-everything-test/src/indexB.ts +++ b/build-tests/heft-webpack5-everything-test/src/indexB.ts @@ -1 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// eslint-disable-next-line no-console console.log('dostuff'); diff --git a/build-tests/heft-webpack5-everything-test/src/test/Image.test.ts b/build-tests/heft-webpack5-everything-test/src/test/Image.test.ts new file mode 100644 index 00000000000..c336d269d60 --- /dev/null +++ b/build-tests/heft-webpack5-everything-test/src/test/Image.test.ts @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import image from '../chunks/image.png'; + +describe('Image Test', () => { + it('correctly handles urls for images', () => { + expect(image).toBe('lib-commonjs/chunks/image.png'); + }); +}); diff --git a/build-tests/heft-webpack5-everything-test/src/test/SourceMapTest.test.ts b/build-tests/heft-webpack5-everything-test/src/test/SourceMapTest.test.ts new file mode 100644 index 00000000000..52171383838 --- /dev/null +++ b/build-tests/heft-webpack5-everything-test/src/test/SourceMapTest.test.ts @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { FileSystem, PackageJsonLookup } from '@rushstack/node-core-library'; + +interface IMap { + sources?: string[]; + file?: string; + sourcesContent?: string[]; + names?: string[]; +} + +interface IMapValue { + mapFileName: string; + mapObject: IMap; +} + +interface IMapTestEntry { + name: string; + mapRegex: RegExp; + sourceFileRegex: RegExp; + map: IMapValue | undefined; +} + +const mapTests: IMapTestEntry[] = [ + { + name: 'Test-A', + mapRegex: /^heft-test-A_[\w\d]*\.js.map$/, + sourceFileRegex: /indexA\.ts$/, + map: undefined + }, + { + name: 'Test-B', + mapRegex: /^heft-test-B_[\w\d]*\.js.map$/, + sourceFileRegex: /indexB\.ts$/, + map: undefined + }, + { + name: 'Chunk', + mapRegex: /^[\w\d\.]*chunk_[\w\d]*\.js.map$/, + sourceFileRegex: /ChunkClass\.ts$/, + map: undefined + } +]; + +const lookup: PackageJsonLookup = new PackageJsonLookup(); +lookup.tryGetPackageFolderFor(__dirname); +const thisProjectFolder: string | undefined = lookup.tryGetPackageFolderFor(__dirname); +if (!thisProjectFolder) { + throw new Error('Cannot find project folder'); +} +const distEntries: string[] = FileSystem.readFolderItemNames(thisProjectFolder + '/dist'); +for (const distEntry of distEntries) { + for (const test of mapTests) { + if (test.mapRegex.test(distEntry)) { + const mapText: string = FileSystem.readFile(`${thisProjectFolder}/dist/${distEntry}`); + const mapObject: IMap = JSON.parse(mapText); + test.map = { + mapFileName: distEntry, + mapObject + }; + } + } +} + +describe('Source Maps', () => { + for (const test of mapTests) { + mapValueCheck(test); + } +}); + +function mapValueCheck(entry: IMapTestEntry): void { + it(`${entry.name} has map value`, () => { + expect(entry.map).toBeTruthy(); + }); + + if (!entry.map) { + return; + } + + const map: IMapValue = entry.map; + + it(`${entry.name} has filename matching file attribute`, () => { + if (map.mapObject.file) { + expect(map.mapFileName).toMatch(`${map.mapObject.file}.map`); + } + }); + + const properties: (keyof IMap)[] = ['sources', 'file', 'sourcesContent', 'names']; + for (const property of properties) { + it(`${map.mapFileName} has ${property} property`, () => { + expect(map.mapObject[property]).toBeTruthy(); + }); + } + + it(`${entry.name} has sources and sourcesContent arrays of the same length`, () => { + if (map.mapObject.sourcesContent && map.mapObject.sources) { + let numSrcs: number = 0; + for (const source of map.mapObject.sources) { + if (source) { + numSrcs++; + } + } + + let numContents: number = 0; + for (const content of map.mapObject.sourcesContent) { + if (content) { + numContents++; + } + } + expect(numSrcs).toEqual(numContents); + } + }); + + it(`${entry.name} has a source that matches the sourceFileRegex`, () => { + if (map.mapObject.sources) { + expect(map.mapObject.sources).toContainEqual(expect.stringMatching(entry.sourceFileRegex)); + } + }); +} diff --git a/build-tests/heft-webpack5-everything-test/tsconfig.json b/build-tests/heft-webpack5-everything-test/tsconfig.json index bd378b854d7..591ea0e0ac3 100644 --- a/build-tests/heft-webpack5-everything-test/tsconfig.json +++ b/build-tests/heft-webpack5-everything-test/tsconfig.json @@ -2,10 +2,12 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", - "rootDir": "src", + "outDir": "lib-esm", + "rootDirs": ["src", "temp/image-typings"], "forceConsistentCasingInFileNames": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, "jsx": "react", "declaration": true, "sourceMap": true, @@ -14,13 +16,12 @@ "experimentalDecorators": true, "strictNullChecks": true, "noUnusedLocals": true, - "types": ["heft-jest", "webpack-env"], + "types": ["jest", "webpack-env", "node"], "module": "esnext", "moduleResolution": "node", "target": "es5", "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/heft-webpack5-everything-test/tslint.json b/build-tests/heft-webpack5-everything-test/tslint.json index f55613b66cc..7dc059cfb49 100644 --- a/build-tests/heft-webpack5-everything-test/tslint.json +++ b/build-tests/heft-webpack5-everything-test/tslint.json @@ -1,13 +1,11 @@ { "$schema": "http://json.schemastore.org/tslint", - "rulesDirectory": ["tslint-microsoft-contrib"], "rules": { "class-name": true, "comment-format": [true, "check-space"], "curly": true, "eofline": false, - "export-name": true, "forin": true, "indent": [true, "spaces", 2], "interface-name": true, @@ -36,32 +34,29 @@ ] } ], - "missing-optional-annotation": true, "no-arg": true, "no-any": true, "no-bitwise": true, "no-consecutive-blank-lines": true, "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], - "no-constant-condition": true, "no-construct": true, "no-debugger": true, "no-duplicate-switch-case": true, - "no-duplicate-parameter-names": true, "no-duplicate-variable": true, "no-empty": true, "no-eval": true, "no-floating-promises": true, - "no-function-expression": true, "no-inferrable-types": false, "no-internal-module": true, "no-null-keyword": true, - "no-shadowed-variable": true, + + // This rule throws a DeprecationError exception with TypeScript 5.3.x + // "no-shadowed-variable": true, + "no-string-literal": true, "no-switch-case-fall-through": true, "no-trailing-whitespace": true, - "no-unnecessary-semicolons": true, "no-unused-expression": true, - "no-with-statement": true, "no-var-keyword": true, "object-literal-sort-keys": false, "one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"], @@ -96,7 +91,6 @@ } ], "use-isnan": true, - "use-named-parameter": true, "variable-name": [true, "check-format", "allow-leading-underscore", "ban-keywords"], "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"] } diff --git a/build-tests/heft-webpack5-everything-test/webpack.config.js b/build-tests/heft-webpack5-everything-test/webpack.config.js index b8f6ccee353..b1208d56ad0 100644 --- a/build-tests/heft-webpack5-everything-test/webpack.config.js +++ b/build-tests/heft-webpack5-everything-test/webpack.config.js @@ -12,16 +12,21 @@ module.exports = { { test: /\.png$/i, type: 'asset/resource' + }, + { + test: /\.js$/, + enforce: 'pre', + use: ['source-map-loader'] } ] }, - target: ['web', 'es2020'], + target: ['web', 'es5'], resolve: { - extensions: ['.js', '.jsx', '.json'] + extensions: ['.js', '.json'] }, entry: { - 'heft-test-A': path.join(__dirname, 'lib', 'indexA.js'), - 'heft-test-B': path.join(__dirname, 'lib', 'indexB.js') + 'heft-test-A': path.join(__dirname, 'lib-esm', 'indexA.js'), + 'heft-test-B': path.join(__dirname, 'lib-esm', 'indexB.js') }, output: { path: path.join(__dirname, 'dist'), @@ -36,7 +41,7 @@ module.exports = { new ModuleMinifierPlugin({ minifier: new WorkerPoolMinifier({ terserOptions: { - ecma: 2020, + ecma: 5, mangle: true }, verbose: true diff --git a/build-tests/heft-webpack5-everything-test/webpack.dev.config.js b/build-tests/heft-webpack5-everything-test/webpack.dev.config.js index 25459e25909..504bdc6e13c 100644 --- a/build-tests/heft-webpack5-everything-test/webpack.dev.config.js +++ b/build-tests/heft-webpack5-everything-test/webpack.dev.config.js @@ -15,7 +15,7 @@ module.exports = { }, target: ['web', 'es2020'], resolve: { - extensions: ['.js', '.jsx', '.json'] + extensions: ['.js', '.json'] }, entry: { 'heft-test-A': path.join(__dirname, 'lib', 'indexA.js'), diff --git a/build-tests/install-test-workspace/.vscode/launch.json b/build-tests/install-test-workspace/.vscode/launch.json deleted file mode 100644 index 157ed92c5a6..00000000000 --- a/build-tests/install-test-workspace/.vscode/launch.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "pwa-node", - "request": "launch", - "name": "build.js", - "program": "${workspaceFolder}/build.js", - "args": ["--skip-pack"] - }, - { - "type": "pwa-node", - "request": "launch", - "name": "pnpm install", - "program": "${workspaceFolder}/../../common/temp/pnpm-local/node_modules/pnpm/bin/pnpm.cjs", - "args": ["install"], - "cwd": "${workspaceFolder}/workspace" - } - ] -} \ No newline at end of file diff --git a/build-tests/install-test-workspace/build.js b/build-tests/install-test-workspace/build.js deleted file mode 100644 index a50b6fbafac..00000000000 --- a/build-tests/install-test-workspace/build.js +++ /dev/null @@ -1,202 +0,0 @@ -const path = require('path'); -const { RushConfiguration } = require('@microsoft/rush-lib'); -const { Executable, FileSystem, JsonFile } = require('@rushstack/node-core-library'); - -function collect(project) { - if (allDependencyProjects.has(project)) { - return; - } - allDependencyProjects.add(project); - - for (const dependencyProject of project.dependencyProjects) { - collect(dependencyProject); - } -} - -function checkSpawnResult(result, commandName) { - if (result.status !== 0) { - if (result.stderr) { - console.error('-----------------------'); - console.error(result.stderr); - console.error('-----------------------'); - } else { - if (result.stdout) { - console.error('-----------------------'); - console.error(result.stdout); - console.error('-----------------------'); - } - } - throw new Error(`Failed to execute command "${commandName}" command`); - } -} - -process.exitCode = 1; - -const productionMode = process.argv.indexOf('--production') >= 0; -const skipPack = process.argv.indexOf('--skip-pack') >= 0; - -const rushConfiguration = RushConfiguration.loadFromDefaultLocation(); -const currentProject = rushConfiguration.tryGetProjectForPath(__dirname); -if (!currentProject) { - throw new Error('Cannot find current project'); -} - -const allDependencyProjects = new Set(); -collect(currentProject); - -const tarballFolder = path.join(__dirname, 'temp/tarballs'); - -if (!skipPack) { - FileSystem.ensureEmptyFolder(tarballFolder); - - const tarballsJson = {}; - - for (const project of allDependencyProjects) { - if (project.versionPolicy || project.shouldPublish) { - console.log('Invoking "pnpm pack" in ' + project.publishFolder); - - const packageJsonFilename = path.join(project.projectFolder, 'package.json'); - const packageJson = FileSystem.readFile(packageJsonFilename); - - let result; - - try { - result = Executable.spawnSync(rushConfiguration.packageManagerToolFilename, ['pack'], { - currentWorkingDirectory: project.publishFolder, - stdio: ['ignore', 'pipe', 'pipe'] - }); - } finally { - // This is a workaround for an issue where "pnpm pack" modifies the project's package.json file - // before invoking "npm pack", and then does not restore it afterwards. - try { - FileSystem.writeFile(packageJsonFilename, packageJson); - } catch (error) { - console.error('Error restoring ' + packageJsonFilename); - } - } - checkSpawnResult(result, 'pnpm pack'); - const tarballFilename = result.stdout.trimRight().split().pop().trim(); - if (!tarballFilename) { - throw new Error('Failed to parse "pnpm pack" output'); - } - const tarballPath = path.join(project.publishFolder, tarballFilename); - if (!FileSystem.exists(tarballPath)) { - throw new Error('Expecting a tarball: ' + tarballPath); - } - - tarballsJson[project.packageName] = tarballFilename; - - const targetPath = path.join(tarballFolder, tarballFilename); - FileSystem.move({ - sourcePath: tarballPath, - destinationPath: targetPath, - overwrite: true - }); - } - } - - JsonFile.save(tarballsJson, path.join(tarballFolder, 'tarballs.json')); -} - -// Look for folder names like this: -// local+C++Git+rushstack+build-tests+install-test-wo_7efa61ad1cd268a0ef451c2450ca0351 -// -// This caches the tarball contents, ignoring the integrity hashes. -const dotPnpmFolderPath = path.resolve(__dirname, 'workspace/node_modules/.pnpm'); - -console.log('\nCleaning cached tarballs...'); -if (FileSystem.exists(dotPnpmFolderPath)) { - for (const filename of FileSystem.readFolderItemNames(dotPnpmFolderPath)) { - if (filename.startsWith('local+')) { - const filePath = path.join(dotPnpmFolderPath, filename); - console.log(' Deleting ' + filePath); - FileSystem.deleteFolder(filePath); - } - } -} - -const pnpmLockBeforePath = path.join(__dirname, 'workspace/common/pnpm-lock.yaml'); -const pnpmLockAfterPath = path.join(__dirname, 'workspace/pnpm-lock.yaml'); -let pnpmLockBeforeContent = ''; - -if (FileSystem.exists(pnpmLockBeforePath)) { - pnpmLockBeforeContent = FileSystem.readFile(pnpmLockBeforePath).toString().replace(/\s+/g, ' ').trim(); - FileSystem.copyFile({ - sourcePath: pnpmLockBeforePath, - destinationPath: pnpmLockAfterPath, - alreadyExistsBehavior: 'overwrite' - }); -} else { - pnpmLockBeforeContent = ''; - FileSystem.deleteFile(pnpmLockAfterPath); -} - -const pnpmInstallArgs = [ - 'install', - '--store', - rushConfiguration.pnpmOptions.pnpmStorePath, - '--strict-peer-dependencies', - '--recursive', - '--link-workspace-packages=false', - // PNPM gets confused by the rewriting performed by our .pnpmfile.cjs afterAllResolved hook - '--frozen-lockfile=false' -]; - -console.log('\nInstalling:'); -console.log(' pnpm ' + pnpmInstallArgs.join(' ')); - -checkSpawnResult( - Executable.spawnSync(rushConfiguration.packageManagerToolFilename, pnpmInstallArgs, { - currentWorkingDirectory: path.join(__dirname, 'workspace'), - stdio: 'inherit' - }), - 'pnpm install' -); - -// Now compare the before/after -const pnpmLockAfterContent = FileSystem.readFile(pnpmLockAfterPath).toString().replace(/\s+/g, ' ').trim(); - -let shrinkwrapUpdatedNotice = false; - -if (pnpmLockBeforeContent !== pnpmLockAfterContent) { - if (productionMode) { - // TODO: Re-enable when issue with lockfile diffing is resolved - // console.error('The shrinkwrap file is not up to date:'); - // console.error(' Git copy: ' + pnpmLockBeforePath); - // console.error(' Current copy: ' + pnpmLockAfterPath); - // console.error('\nPlease commit the updated copy to Git\n'); - // process.exitCode = 1; - // return; - } else { - // Automatically update the copy - FileSystem.copyFile({ - sourcePath: pnpmLockAfterPath, - destinationPath: pnpmLockBeforePath, - alreadyExistsBehavior: 'overwrite' - }); - - // Show the notice at the very end - shrinkwrapUpdatedNotice = true; - } -} - -console.log('\n\nInstallation completed successfully.'); - -console.log('\nBuilding projects...\n'); - -checkSpawnResult( - Executable.spawnSync(rushConfiguration.packageManagerToolFilename, ['run', '--recursive', 'build'], { - currentWorkingDirectory: path.join(__dirname, 'workspace'), - stdio: 'inherit' - }), - 'pnpm run' -); - -if (shrinkwrapUpdatedNotice) { - console.error('\n==> The shrinkwrap file has been updated. Please commit the changes to Git:'); - console.error(` ${pnpmLockBeforePath}`); -} - -console.log('\n\nFinished build.js'); - -process.exitCode = 0; diff --git a/build-tests/install-test-workspace/package.json b/build-tests/install-test-workspace/package.json deleted file mode 100644 index 6a994fe8341..00000000000 --- a/build-tests/install-test-workspace/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "install-test-workspace", - "description": "", - "version": "1.0.0", - "private": true, - "scripts": { - "build": "node build.js", - "_phase:build": "node build.js" - }, - "devDependencies": { - "@microsoft/rush-lib": "workspace:*", - "@rushstack/node-core-library": "workspace:*", - "@rushstack/rush-sdk": "workspace:*" - } -} diff --git a/build-tests/install-test-workspace/workspace/.gitignore b/build-tests/install-test-workspace/workspace/.gitignore deleted file mode 100644 index 54e8e7dc16e..00000000000 --- a/build-tests/install-test-workspace/workspace/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/pnpm-lock.yaml diff --git a/build-tests/install-test-workspace/workspace/.pnpmfile.cjs b/build-tests/install-test-workspace/workspace/.pnpmfile.cjs deleted file mode 100644 index 860bf0d3300..00000000000 --- a/build-tests/install-test-workspace/workspace/.pnpmfile.cjs +++ /dev/null @@ -1,91 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); - -console.log('Using pnpmfile'); - -/** - * When using the PNPM package manager, you can use pnpmfile.js to workaround - * dependencies that have mistakes in their package.json file. (This feature is - * functionally similar to Yarn's "resolutions".) - * - * For details, see the PNPM documentation: - * https://pnpm.js.org/docs/en/hooks.html - * - * IMPORTANT: SINCE THIS FILE CONTAINS EXECUTABLE CODE, MODIFYING IT IS LIKELY TO INVALIDATE - * ANY CACHED DEPENDENCY ANALYSIS. After any modification to pnpmfile.js, it's recommended to run - * "rush update --full" so that PNPM will recalculate all version selections. - */ -module.exports = { - hooks: { - readPackage, - afterAllResolved - } -}; - -const tarballsJsonFolder = path.resolve(__dirname, '../temp/tarballs'); -const tarballsJson = JSON.parse(fs.readFileSync(path.join(tarballsJsonFolder, 'tarballs.json')).toString()); - -function fixup(packageJson, dependencies) { - if (!dependencies) { - return; - } - - for (const dependencyName of Object.keys(dependencies)) { - const tarballFilename = tarballsJson[dependencyName.trim()]; - if (tarballFilename) { - // This must be an absolute path, since a relative path would get resolved relative to an unknown folder - const tarballSpecifier = 'file:' + path.join(tarballsJsonFolder, tarballFilename).split('\\').join('/'); - - // console.log(`Remapping ${packageJson.name}: ${dependencyName} --> ${tarballSpecifier}`); - dependencies[dependencyName] = tarballSpecifier; - } - } -} - -/** - * This hook is invoked during installation before a package's dependencies - * are selected. - * The `packageJson` parameter is the deserialized package.json - * contents for the package that is about to be installed. - * The `context` parameter provides a log() function. - * The return value is the updated object. - */ -function readPackage(packageJson, context) { - fixup(packageJson, packageJson.dependencies); - fixup(packageJson, packageJson.devDependencies); - fixup(packageJson, packageJson.optionalDependencies); - return packageJson; -} - -function afterAllResolved(lockfile, context) { - // Remove the absolute path from the specifiers to avoid shrinkwrap churn - for (const importerName of Object.keys(lockfile.importers || {})) { - const importer = lockfile.importers[importerName]; - const specifiers = importer.specifiers; - if (specifiers) { - for (const dependencyName of Object.keys(specifiers)) { - const tarballFilename = tarballsJson[dependencyName.trim()]; - if (tarballFilename) { - const tarballSpecifier = 'file:' + tarballFilename; - specifiers[dependencyName] = tarballSpecifier; - } - } - } - } - - // Delete the resolution.integrity hash for tarball paths to avoid shrinkwrap churn. - // PNPM seems to ignore these hashes during installation. - for (const packagePath of Object.keys(lockfile.packages || {})) { - if (packagePath.startsWith('file:')) { - const packageInfo = lockfile.packages[packagePath]; - const resolution = packageInfo.resolution; - if (resolution && resolution.integrity && resolution.tarball) { - delete resolution.integrity; - } - } - } - - return lockfile; -} diff --git a/build-tests/install-test-workspace/workspace/common/pnpm-lock.yaml b/build-tests/install-test-workspace/workspace/common/pnpm-lock.yaml deleted file mode 100644 index fed3486cb39..00000000000 --- a/build-tests/install-test-workspace/workspace/common/pnpm-lock.yaml +++ /dev/null @@ -1,4316 +0,0 @@ -lockfileVersion: 5.4 - -importers: - - .: - specifiers: {} - - rush-lib-test: - specifiers: - '@microsoft/rush-lib': file:microsoft-rush-lib-5.98.0.tgz - '@types/node': 14.18.36 - colors: ^1.4.0 - rimraf: ^4.1.2 - typescript: ~5.0.4 - dependencies: - '@microsoft/rush-lib': file:../temp/tarballs/microsoft-rush-lib-5.98.0.tgz_@types+node@14.18.36 - colors: 1.4.0 - devDependencies: - '@types/node': 14.18.36 - rimraf: 4.4.1 - typescript: 5.0.4 - - rush-sdk-test: - specifiers: - '@microsoft/rush-lib': file:microsoft-rush-lib-5.98.0.tgz - '@rushstack/rush-sdk': file:rushstack-rush-sdk-5.98.0.tgz - '@types/node': 14.18.36 - colors: ^1.4.0 - rimraf: ^4.1.2 - typescript: ~5.0.4 - dependencies: - '@rushstack/rush-sdk': file:../temp/tarballs/rushstack-rush-sdk-5.98.0.tgz_@types+node@14.18.36 - colors: 1.4.0 - devDependencies: - '@microsoft/rush-lib': file:../temp/tarballs/microsoft-rush-lib-5.98.0.tgz_@types+node@14.18.36 - '@types/node': 14.18.36 - rimraf: 4.4.1 - typescript: 5.0.4 - - typescript-newest-test: - specifiers: - '@rushstack/eslint-config': file:rushstack-eslint-config-3.3.0.tgz - '@rushstack/heft': file:rushstack-heft-0.50.7.tgz - '@rushstack/heft-lint-plugin': file:rushstack-heft-lint-plugin-0.0.0.tgz - '@rushstack/heft-typescript-plugin': file:rushstack-heft-typescript-plugin-0.0.0.tgz - eslint: ~8.7.0 - tslint: ~5.20.1 - typescript: ~5.0.4 - devDependencies: - '@rushstack/eslint-config': file:../temp/tarballs/rushstack-eslint-config-3.3.0.tgz_ucoohk2w7gukx6ccuul7rl7pnq - '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.50.7.tgz - '@rushstack/heft-lint-plugin': file:../temp/tarballs/rushstack-heft-lint-plugin-0.0.0.tgz_@rushstack+heft@0.50.7 - '@rushstack/heft-typescript-plugin': file:../temp/tarballs/rushstack-heft-typescript-plugin-0.0.0.tgz_@rushstack+heft@0.50.7 - eslint: 8.7.0 - tslint: 5.20.1_typescript@5.0.4 - typescript: 5.0.4 - - typescript-v4-test: - specifiers: - '@rushstack/eslint-config': file:rushstack-eslint-config-3.3.0.tgz - '@rushstack/heft': file:rushstack-heft-0.50.7.tgz - '@rushstack/heft-lint-plugin': file:rushstack-heft-lint-plugin-0.0.0.tgz - '@rushstack/heft-typescript-plugin': file:rushstack-heft-typescript-plugin-0.0.0.tgz - eslint: ~8.7.0 - tslint: ~5.20.1 - typescript: ~4.7.0 - devDependencies: - '@rushstack/eslint-config': file:../temp/tarballs/rushstack-eslint-config-3.3.0.tgz_valmiib6gbzc7jhcbpocdsabay - '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.50.7.tgz - '@rushstack/heft-lint-plugin': file:../temp/tarballs/rushstack-heft-lint-plugin-0.0.0.tgz_@rushstack+heft@0.50.7 - '@rushstack/heft-typescript-plugin': file:../temp/tarballs/rushstack-heft-typescript-plugin-0.0.0.tgz_@rushstack+heft@0.50.7 - eslint: 8.7.0 - tslint: 5.20.1_typescript@4.7.4 - typescript: 4.7.4 - -packages: - - /@babel/code-frame/7.12.13: - resolution: {integrity: sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==} - dependencies: - '@babel/highlight': 7.14.0 - dev: true - - /@babel/code-frame/7.18.6: - resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.18.6 - - /@babel/generator/7.21.3: - resolution: {integrity: sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.3 - '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.17 - jsesc: 2.5.2 - - /@babel/helper-environment-visitor/7.18.9: - resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} - engines: {node: '>=6.9.0'} - - /@babel/helper-function-name/7.21.0: - resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.20.7 - '@babel/types': 7.21.3 - - /@babel/helper-hoist-variables/7.18.6: - resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.3 - - /@babel/helper-split-export-declaration/7.18.6: - resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.3 - - /@babel/helper-string-parser/7.19.4: - resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} - engines: {node: '>=6.9.0'} - - /@babel/helper-validator-identifier/7.14.0: - resolution: {integrity: sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==} - dev: true - - /@babel/helper-validator-identifier/7.19.1: - resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} - engines: {node: '>=6.9.0'} - - /@babel/highlight/7.14.0: - resolution: {integrity: sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==} - dependencies: - '@babel/helper-validator-identifier': 7.14.0 - chalk: 2.4.2 - js-tokens: 4.0.0 - dev: true - - /@babel/highlight/7.18.6: - resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.19.1 - chalk: 2.4.2 - js-tokens: 4.0.0 - - /@babel/parser/7.16.4: - resolution: {integrity: sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.21.3 - - /@babel/parser/7.21.3: - resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.21.3 - - /@babel/template/7.20.7: - resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.18.6 - '@babel/parser': 7.21.3 - '@babel/types': 7.21.3 - - /@babel/traverse/7.21.3: - resolution: {integrity: sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.21.3 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.21.3 - '@babel/types': 7.21.3 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - /@babel/types/7.21.3: - resolution: {integrity: sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.19.4 - '@babel/helper-validator-identifier': 7.19.1 - to-fast-properties: 2.0.0 - - /@devexpress/error-stack-parser/2.0.6: - resolution: {integrity: sha512-fneVypElGUH6Be39mlRZeAu00pccTlf4oVuzf9xPJD1cdEqI8NyAiQua/EW7lZdrbMUbgyXcJmfKPefhYius3A==} - dependencies: - stackframe: 1.3.4 - - /@eslint-community/eslint-utils/4.4.0_eslint@8.7.0: - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.7.0 - eslint-visitor-keys: 3.3.0 - dev: true - - /@eslint-community/regexpp/4.5.1: - resolution: {integrity: sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - dev: true - - /@eslint/eslintrc/1.3.0: - resolution: {integrity: sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.3.2 - globals: 13.15.0 - ignore: 5.2.0 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@humanwhocodes/config-array/0.9.5: - resolution: {integrity: sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==} - engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@humanwhocodes/object-schema/1.2.1: - resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} - dev: true - - /@jridgewell/gen-mapping/0.3.2: - resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 - '@jridgewell/trace-mapping': 0.3.17 - - /@jridgewell/resolve-uri/3.1.0: - resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} - engines: {node: '>=6.0.0'} - - /@jridgewell/set-array/1.1.2: - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} - engines: {node: '>=6.0.0'} - - /@jridgewell/sourcemap-codec/1.4.14: - resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} - - /@jridgewell/trace-mapping/0.3.17: - resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} - dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 - - /@microsoft/tsdoc-config/0.16.1: - resolution: {integrity: sha512-2RqkwiD4uN6MLnHFljqBlZIXlt/SaUT6cuogU1w2ARw4nKuuppSmR0+s+NC+7kXBQykd9zzu0P4HtBpZT5zBpQ==} - dependencies: - '@microsoft/tsdoc': 0.14.1 - ajv: 6.12.6 - jju: 1.4.0 - resolve: 1.19.0 - dev: true - - /@microsoft/tsdoc/0.14.1: - resolution: {integrity: sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw==} - dev: true - - /@nodelib/fs.scandir/2.1.5: - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - /@nodelib/fs.stat/2.0.5: - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - /@nodelib/fs.walk/1.2.7: - resolution: {integrity: sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==} - engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.11.0 - - /@pnpm/crypto.base32-hash/1.0.1: - resolution: {integrity: sha512-pzAXNn6KxTA3kbcI3iEnYs4vtH51XEVqmK/1EiD18MaPKylhqy8UvMJK3zKG+jeP82cqQbozcTGm4yOQ8i3vNw==} - engines: {node: '>=14.6'} - dependencies: - rfc4648: 1.5.2 - - /@pnpm/error/1.4.0: - resolution: {integrity: sha512-vxkRrkneBPVmP23kyjnYwVOtipwlSl6UfL+h+Xa3TrABJTz5rYBXemlTsU5BzST8U4pD7YDkTb3SQu+MMuIDKA==} - engines: {node: '>=10.16'} - - /@pnpm/link-bins/5.3.25: - resolution: {integrity: sha512-9Xq8lLNRHFDqvYPXPgaiKkZ4rtdsm7izwM/cUsFDc5IMnG0QYIVBXQbgwhz2UvjUotbJrvfKLJaCfA3NGBnLDg==} - engines: {node: '>=10.16'} - dependencies: - '@pnpm/error': 1.4.0 - '@pnpm/package-bins': 4.1.0 - '@pnpm/read-modules-dir': 2.0.3 - '@pnpm/read-package-json': 4.0.0 - '@pnpm/read-project-manifest': 1.1.7 - '@pnpm/types': 6.4.0 - '@zkochan/cmd-shim': 5.4.1 - is-subdir: 1.2.0 - is-windows: 1.0.2 - mz: 2.7.0 - normalize-path: 3.0.0 - p-settle: 4.1.1 - ramda: 0.27.2 - - /@pnpm/package-bins/4.1.0: - resolution: {integrity: sha512-57/ioGYLBbVRR80Ux9/q2i3y8Q+uQADc3c+Yse8jr/60YLOi3jcWz13e2Jy+ANYtZI258Qc5wk2X077rp0Ly/Q==} - engines: {node: '>=10.16'} - dependencies: - '@pnpm/types': 6.4.0 - fast-glob: 3.2.11 - is-subdir: 1.2.0 - - /@pnpm/read-modules-dir/2.0.3: - resolution: {integrity: sha512-i9OgRvSlxrTS9a2oXokhDxvQzDtfqtsooJ9jaGoHkznue5aFCTSrNZFQ6M18o8hC03QWfnxaKi0BtOvNkKu2+A==} - engines: {node: '>=10.13'} - dependencies: - mz: 2.7.0 - - /@pnpm/read-package-json/4.0.0: - resolution: {integrity: sha512-1cr2tEwe4YU6SI0Hmg+wnsr6yxBt2iJtqv6wrF84On8pS9hx4A2PLw3CIgbwxaG0b+ur5wzhNogwl4qD5FLFNg==} - engines: {node: '>=10.16'} - dependencies: - '@pnpm/error': 1.4.0 - '@pnpm/types': 6.4.0 - load-json-file: 6.2.0 - normalize-package-data: 3.0.3 - - /@pnpm/read-project-manifest/1.1.7: - resolution: {integrity: sha512-tj8ExXZeDcMmMUj7D292ETe/RiEirr1X1wpT6Zy85z2MrFYoG9jfCJpps40OdZBNZBhxbuKtGPWKVSgXD0yrVw==} - engines: {node: '>=10.16'} - dependencies: - '@pnpm/error': 1.4.0 - '@pnpm/types': 6.4.0 - '@pnpm/write-project-manifest': 1.1.7 - detect-indent: 6.1.0 - fast-deep-equal: 3.1.3 - graceful-fs: 4.2.4 - is-windows: 1.0.2 - json5: 2.2.3 - parse-json: 5.2.0 - read-yaml-file: 2.1.0 - sort-keys: 4.2.0 - strip-bom: 4.0.0 - - /@pnpm/types/6.4.0: - resolution: {integrity: sha512-nco4+4sZqNHn60Y4VE/fbtlShCBqipyUO+nKRPvDHqLrecMW9pzHWMVRxk4nrMRoeowj3q0rX3GYRBa8lsHTAg==} - engines: {node: '>=10.16'} - - /@pnpm/types/8.9.0: - resolution: {integrity: sha512-3MYHYm8epnciApn6w5Fzx6sepawmsNU7l6lvIq+ER22/DPSrr83YMhU/EQWnf4lORn2YyiXFj0FJSyJzEtIGmw==} - engines: {node: '>=14.6'} - - /@pnpm/write-project-manifest/1.1.7: - resolution: {integrity: sha512-OLkDZSqkA1mkoPNPvLFXyI6fb0enCuFji6Zfditi/CLAo9kmIhQFmEUDu4krSB8i908EljG8YwL5Xjxzm5wsWA==} - engines: {node: '>=10.16'} - dependencies: - '@pnpm/types': 6.4.0 - json5: 2.2.3 - mz: 2.7.0 - write-file-atomic: 3.0.3 - write-yaml-file: 4.2.0 - - /@sindresorhus/is/0.14.0: - resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==} - engines: {node: '>=6'} - - /@szmarczak/http-timer/1.1.2: - resolution: {integrity: sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==} - engines: {node: '>=6'} - dependencies: - defer-to-connect: 1.1.3 - - /@types/argparse/1.0.38: - resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} - - /@types/json-schema/7.0.11: - resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} - dev: true - - /@types/keyv/3.1.4: - resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} - dependencies: - '@types/node': 14.18.36 - - /@types/lodash/4.14.192: - resolution: {integrity: sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==} - - /@types/minimatch/3.0.5: - resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} - - /@types/minimist/1.2.2: - resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} - - /@types/node-fetch/2.6.2: - resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} - dependencies: - '@types/node': 14.18.36 - form-data: 3.0.1 - - /@types/node/14.18.36: - resolution: {integrity: sha1-xBQFLLnUP6tn1nnV88ZBvpEfWDU=} - - /@types/normalize-package-data/2.4.1: - resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} - - /@types/parse-json/4.0.0: - resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} - - /@types/responselike/1.0.0: - resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} - dependencies: - '@types/node': 14.18.36 - - /@types/semver/7.5.0: - resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} - dev: true - - /@types/tapable/1.0.6: - resolution: {integrity: sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==} - dev: true - - /@typescript-eslint/eslint-plugin/5.59.7_7yosyjls7ieoemdl24ktrlsrzm: - resolution: {integrity: sha512-BL+jYxUFIbuYwy+4fF86k5vdT9lT0CNJ6HtwrIvGh0PhH8s0yy5rjaKH2fDCrz5ITHy07WCzVGNvAmjJh4IJFA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@eslint-community/regexpp': 4.5.1 - '@typescript-eslint/parser': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - '@typescript-eslint/scope-manager': 5.59.7 - '@typescript-eslint/type-utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - '@typescript-eslint/utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - debug: 4.3.4 - eslint: 8.7.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.0 - natural-compare-lite: 1.4.0 - semver: 7.3.8 - tsutils: 3.21.0_typescript@5.0.4 - typescript: 5.0.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/eslint-plugin/5.59.7_cmopfgbf56lh5wztbaq3kmg4gm: - resolution: {integrity: sha512-BL+jYxUFIbuYwy+4fF86k5vdT9lT0CNJ6HtwrIvGh0PhH8s0yy5rjaKH2fDCrz5ITHy07WCzVGNvAmjJh4IJFA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@eslint-community/regexpp': 4.5.1 - '@typescript-eslint/parser': 5.59.7_valmiib6gbzc7jhcbpocdsabay - '@typescript-eslint/scope-manager': 5.59.7 - '@typescript-eslint/type-utils': 5.59.7_valmiib6gbzc7jhcbpocdsabay - '@typescript-eslint/utils': 5.59.7_valmiib6gbzc7jhcbpocdsabay - debug: 4.3.4 - eslint: 8.7.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.0 - natural-compare-lite: 1.4.0 - semver: 7.3.8 - tsutils: 3.21.0_typescript@4.7.4 - typescript: 4.7.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/experimental-utils/5.59.7_ucoohk2w7gukx6ccuul7rl7pnq: - resolution: {integrity: sha512-jqM0Cjfvta/sBlY1MxdXYv853/dJUC2wmUWnKoG2srwp0njNGQ6Zu/XLWoRFiLvocQbzBbpHkPFwKgC2UqyovA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@typescript-eslint/utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - eslint: 8.7.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@typescript-eslint/experimental-utils/5.59.7_valmiib6gbzc7jhcbpocdsabay: - resolution: {integrity: sha512-jqM0Cjfvta/sBlY1MxdXYv853/dJUC2wmUWnKoG2srwp0njNGQ6Zu/XLWoRFiLvocQbzBbpHkPFwKgC2UqyovA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@typescript-eslint/utils': 5.59.7_valmiib6gbzc7jhcbpocdsabay - eslint: 8.7.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@typescript-eslint/parser/5.59.7_ucoohk2w7gukx6ccuul7rl7pnq: - resolution: {integrity: sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/scope-manager': 5.59.7 - '@typescript-eslint/types': 5.59.7 - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - debug: 4.3.4 - eslint: 8.7.0 - typescript: 5.0.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/parser/5.59.7_valmiib6gbzc7jhcbpocdsabay: - resolution: {integrity: sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/scope-manager': 5.59.7 - '@typescript-eslint/types': 5.59.7 - '@typescript-eslint/typescript-estree': 5.59.7_typescript@4.7.4 - debug: 4.3.4 - eslint: 8.7.0 - typescript: 4.7.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/scope-manager/5.59.7: - resolution: {integrity: sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.59.7 - '@typescript-eslint/visitor-keys': 5.59.7 - dev: true - - /@typescript-eslint/type-utils/5.59.7_ucoohk2w7gukx6ccuul7rl7pnq: - resolution: {integrity: sha512-ozuz/GILuYG7osdY5O5yg0QxXUAEoI4Go3Do5xeu+ERH9PorHBPSdvD3Tjp2NN2bNLh1NJQSsQu2TPu/Ly+HaQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - '@typescript-eslint/utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - debug: 4.3.4 - eslint: 8.7.0 - tsutils: 3.21.0_typescript@5.0.4 - typescript: 5.0.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/type-utils/5.59.7_valmiib6gbzc7jhcbpocdsabay: - resolution: {integrity: sha512-ozuz/GILuYG7osdY5O5yg0QxXUAEoI4Go3Do5xeu+ERH9PorHBPSdvD3Tjp2NN2bNLh1NJQSsQu2TPu/Ly+HaQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/typescript-estree': 5.59.7_typescript@4.7.4 - '@typescript-eslint/utils': 5.59.7_valmiib6gbzc7jhcbpocdsabay - debug: 4.3.4 - eslint: 8.7.0 - tsutils: 3.21.0_typescript@4.7.4 - typescript: 4.7.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/types/5.59.7: - resolution: {integrity: sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - - /@typescript-eslint/typescript-estree/5.59.7_typescript@4.7.4: - resolution: {integrity: sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 5.59.7 - '@typescript-eslint/visitor-keys': 5.59.7 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.3.8 - tsutils: 3.21.0_typescript@4.7.4 - typescript: 4.7.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/typescript-estree/5.59.7_typescript@5.0.4: - resolution: {integrity: sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 5.59.7 - '@typescript-eslint/visitor-keys': 5.59.7 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.3.8 - tsutils: 3.21.0_typescript@5.0.4 - typescript: 5.0.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/utils/5.59.7_ucoohk2w7gukx6ccuul7rl7pnq: - resolution: {integrity: sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0_eslint@8.7.0 - '@types/json-schema': 7.0.11 - '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 5.59.7 - '@typescript-eslint/types': 5.59.7 - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - eslint: 8.7.0 - eslint-scope: 5.1.1 - semver: 7.3.8 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@typescript-eslint/utils/5.59.7_valmiib6gbzc7jhcbpocdsabay: - resolution: {integrity: sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0_eslint@8.7.0 - '@types/json-schema': 7.0.11 - '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 5.59.7 - '@typescript-eslint/types': 5.59.7 - '@typescript-eslint/typescript-estree': 5.59.7_typescript@4.7.4 - eslint: 8.7.0 - eslint-scope: 5.1.1 - semver: 7.3.8 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@typescript-eslint/visitor-keys/5.59.7: - resolution: {integrity: sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.59.7 - eslint-visitor-keys: 3.3.0 - dev: true - - /@vue/compiler-core/3.2.47: - resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==} - dependencies: - '@babel/parser': 7.21.3 - '@vue/shared': 3.2.47 - estree-walker: 2.0.2 - source-map: 0.6.1 - - /@vue/compiler-dom/3.2.47: - resolution: {integrity: sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==} - dependencies: - '@vue/compiler-core': 3.2.47 - '@vue/shared': 3.2.47 - - /@vue/compiler-sfc/3.2.47: - resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==} - dependencies: - '@babel/parser': 7.21.3 - '@vue/compiler-core': 3.2.47 - '@vue/compiler-dom': 3.2.47 - '@vue/compiler-ssr': 3.2.47 - '@vue/reactivity-transform': 3.2.47 - '@vue/shared': 3.2.47 - estree-walker: 2.0.2 - magic-string: 0.25.9 - postcss: 8.4.21 - source-map: 0.6.1 - - /@vue/compiler-ssr/3.2.47: - resolution: {integrity: sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==} - dependencies: - '@vue/compiler-dom': 3.2.47 - '@vue/shared': 3.2.47 - - /@vue/reactivity-transform/3.2.47: - resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==} - dependencies: - '@babel/parser': 7.21.3 - '@vue/compiler-core': 3.2.47 - '@vue/shared': 3.2.47 - estree-walker: 2.0.2 - magic-string: 0.25.9 - - /@vue/shared/3.2.47: - resolution: {integrity: sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==} - - /@yarnpkg/lockfile/1.0.2: - resolution: {integrity: sha512-MqJ00WXw89ga0rK6GZkdmmgv3bAsxpJixyTthjcix73O44pBqotyU2BejBkLuIsaOBI6SEu77vAnSyLe5iIHkw==} - - /@zkochan/cmd-shim/5.4.1: - resolution: {integrity: sha512-odWb1qUzt0dIOEUPyWBEpFDYQPRjEMr/dbHHAfgBkVkYR9aO7Zo+I7oYWrXIxl+cKlC7+49ftPm8uJxL1MA9kw==} - engines: {node: '>=10.13'} - dependencies: - cmd-extension: 1.0.2 - graceful-fs: 4.2.11 - is-windows: 1.0.2 - - /acorn-jsx/5.3.2_acorn@8.7.1: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.7.1 - dev: true - - /acorn/8.7.1: - resolution: {integrity: sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /agent-base/6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - dependencies: - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - - /ajv/6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - dev: true - - /ansi-align/3.0.1: - resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} - dependencies: - string-width: 4.2.3 - - /ansi-escapes/4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.21.3 - - /ansi-regex/5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - /ansi-styles/3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - dependencies: - color-convert: 1.9.3 - - /ansi-styles/4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 - - /any-promise/1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - - /anymatch/3.1.2: - resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} - engines: {node: '>= 8'} - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.0 - - /argparse/1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - dependencies: - sprintf-js: 1.0.3 - - /argparse/2.0.1: - resolution: {integrity: sha1-JG9Q88p4oyQPbJl+ipvR6sSeSzg=} - - /array-differ/3.0.0: - resolution: {integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==} - engines: {node: '>=8'} - - /array-includes/3.1.5: - resolution: {integrity: sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.1 - get-intrinsic: 1.1.1 - is-string: 1.0.7 - dev: true - - /array-union/2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - /array.prototype.flatmap/1.3.0: - resolution: {integrity: sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.1 - es-shim-unscopables: 1.0.0 - dev: true - - /arrify/1.0.1: - resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} - engines: {node: '>=0.10.0'} - - /arrify/2.0.1: - resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} - engines: {node: '>=8'} - - /asap/2.0.6: - resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - - /asynckit/0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - - /balanced-match/1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - /base64-js/1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - /better-path-resolve/1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} - dependencies: - is-windows: 1.0.2 - - /binary-extensions/2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} - engines: {node: '>=8'} - - /bl/4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - - /boxen/5.1.2: - resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} - engines: {node: '>=10'} - dependencies: - ansi-align: 3.0.1 - camelcase: 6.3.0 - chalk: 4.1.1 - cli-boxes: 2.2.1 - string-width: 4.2.3 - type-fest: 0.20.2 - widest-line: 3.1.0 - wrap-ansi: 7.0.0 - - /brace-expansion/1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - /brace-expansion/2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - dependencies: - balanced-match: 1.0.2 - dev: true - - /braces/3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} - engines: {node: '>=8'} - dependencies: - fill-range: 7.0.1 - - /buffer/5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - /builtin-modules/1.1.1: - resolution: {integrity: sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=} - engines: {node: '>=0.10.0'} - dev: true - - /builtin-modules/3.1.0: - resolution: {integrity: sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==} - engines: {node: '>=6'} - - /builtins/1.0.3: - resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} - - /cacheable-request/6.1.0: - resolution: {integrity: sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==} - engines: {node: '>=8'} - dependencies: - clone-response: 1.0.3 - get-stream: 5.2.0 - http-cache-semantics: 4.1.1 - keyv: 3.1.0 - lowercase-keys: 2.0.0 - normalize-url: 4.5.1 - responselike: 1.0.2 - - /call-bind/1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} - dependencies: - function-bind: 1.1.1 - get-intrinsic: 1.1.1 - dev: true - - /callsite-record/4.1.5: - resolution: {integrity: sha512-OqeheDucGKifjQRx524URgV4z4NaKjocGhygTptDea+DLROre4ZEecA4KXDq+P7qlGCohYVNOh3qr+y5XH5Ftg==} - dependencies: - '@devexpress/error-stack-parser': 2.0.6 - '@types/lodash': 4.14.192 - callsite: 1.0.0 - chalk: 2.4.2 - highlight-es: 1.0.3 - lodash: 4.17.21 - pinkie-promise: 2.0.1 - - /callsite/1.0.0: - resolution: {integrity: sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==} - - /callsites/3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - /camelcase-keys/6.2.2: - resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} - engines: {node: '>=8'} - dependencies: - camelcase: 5.3.1 - map-obj: 4.3.0 - quick-lru: 4.0.1 - - /camelcase/5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - /camelcase/6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - - /chalk/2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - - /chalk/4.1.1: - resolution: {integrity: sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - /chardet/0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - - /chokidar/3.4.3: - resolution: {integrity: sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==} - engines: {node: '>= 8.10.0'} - dependencies: - anymatch: 3.1.2 - braces: 3.0.2 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.5.0 - optionalDependencies: - fsevents: 2.1.3 - - /chownr/2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - - /ci-info/2.0.0: - resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} - - /cli-boxes/2.2.1: - resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} - engines: {node: '>=6'} - - /cli-cursor/3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} - dependencies: - restore-cursor: 3.1.0 - - /cli-spinners/2.7.0: - resolution: {integrity: sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==} - engines: {node: '>=6'} - - /cli-table/0.3.11: - resolution: {integrity: sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==} - engines: {node: '>= 0.2.0'} - dependencies: - colors: 1.0.3 - - /cli-width/3.0.0: - resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} - engines: {node: '>= 10'} - - /cliui/7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - /clone-response/1.0.3: - resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} - dependencies: - mimic-response: 1.0.1 - - /clone/1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - - /cmd-extension/1.0.2: - resolution: {integrity: sha512-iWDjmP8kvsMdBmLTHxFaqXikO8EdFRDfim7k6vUHglY/2xJ5jLrPsnQGijdfp4U+sr/BeecG0wKm02dSIAeQ1g==} - engines: {node: '>=10'} - - /co/4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - - /color-convert/1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - dependencies: - color-name: 1.1.3 - - /color-convert/2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - dependencies: - color-name: 1.1.4 - - /color-name/1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - - /color-name/1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - /colors/1.0.3: - resolution: {integrity: sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==} - engines: {node: '>=0.1.90'} - - /colors/1.2.5: - resolution: {integrity: sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==} - engines: {node: '>=0.1.90'} - - /colors/1.4.0: - resolution: {integrity: sha1-xQSRR51MG9rtLJztMs98fcI2D3g=} - engines: {node: '>=0.1.90'} - dev: false - - /combined-stream/1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - dependencies: - delayed-stream: 1.0.0 - - /commander/2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - - /concat-map/0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} - - /configstore/5.0.1: - resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} - engines: {node: '>=8'} - dependencies: - dot-prop: 5.3.0 - graceful-fs: 4.2.11 - make-dir: 3.1.0 - unique-string: 2.0.0 - write-file-atomic: 3.0.3 - xdg-basedir: 4.0.0 - - /core-util-is/1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - - /cosmiconfig/7.1.0: - resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} - engines: {node: '>=10'} - dependencies: - '@types/parse-json': 4.0.0 - import-fresh: 3.3.0 - parse-json: 5.2.0 - path-type: 4.0.0 - yaml: 1.10.2 - - /cross-spawn/7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - /crypto-random-string/2.0.0: - resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} - engines: {node: '>=8'} - - /debug/4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - - /debuglog/1.0.1: - resolution: {integrity: sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==} - - /decamelize-keys/1.1.1: - resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} - engines: {node: '>=0.10.0'} - dependencies: - decamelize: 1.2.0 - map-obj: 1.0.1 - - /decamelize/1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - - /decompress-response/3.3.0: - resolution: {integrity: sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==} - engines: {node: '>=4'} - dependencies: - mimic-response: 1.0.1 - - /deep-extend/0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - - /deep-is/0.1.3: - resolution: {integrity: sha512-GtxAN4HvBachZzm4OnWqc45ESpUCMwkYcsjnsPs23FwJbsO+k4t0k9bQCgOmzIlpHO28+WPK/KRbRk0DDHuuDw==} - dev: true - - /defaults/1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - dependencies: - clone: 1.0.4 - - /defer-to-connect/1.1.3: - resolution: {integrity: sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==} - - /define-properties/1.1.4: - resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} - engines: {node: '>= 0.4'} - dependencies: - has-property-descriptors: 1.0.0 - object-keys: 1.1.1 - dev: true - - /delayed-stream/1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - - /depcheck/1.4.3: - resolution: {integrity: sha512-vy8xe1tlLFu7t4jFyoirMmOR7x7N601ubU9Gkifyr9z8rjBFtEdWHDBMqXyk6OkK+94NXutzddVXJuo0JlUQKQ==} - engines: {node: '>=10'} - hasBin: true - dependencies: - '@babel/parser': 7.16.4 - '@babel/traverse': 7.21.3 - '@vue/compiler-sfc': 3.2.47 - camelcase: 6.3.0 - cosmiconfig: 7.1.0 - debug: 4.3.4 - deps-regex: 0.1.4 - ignore: 5.2.0 - is-core-module: 2.11.0 - js-yaml: 3.14.1 - json5: 2.2.3 - lodash: 4.17.21 - minimatch: 3.1.2 - multimatch: 5.0.0 - please-upgrade-node: 3.2.0 - query-ast: 1.0.5 - readdirp: 3.5.0 - require-package-name: 2.0.1 - resolve: 1.22.1 - sass: 1.60.0 - scss-parser: 1.0.6 - semver: 7.3.8 - yargs: 16.2.0 - transitivePeerDependencies: - - supports-color - - /dependency-path/9.2.8: - resolution: {integrity: sha512-S0OhIK7sIyAsph8hVH/LMCTDL3jozKtlrPx3dMQrlE2nAlXTquTT+AcOufphDMTQqLkfn4acvfiem9I1IWZ4jQ==} - engines: {node: '>=14.6'} - dependencies: - '@pnpm/crypto.base32-hash': 1.0.1 - '@pnpm/types': 8.9.0 - encode-registry: 3.0.0 - semver: 7.3.8 - - /deps-regex/0.1.4: - resolution: {integrity: sha512-3tzwGYogSJi8HoG93R5x9NrdefZQOXgHgGih/7eivloOq6yC6O+yoFxZnkgP661twvfILONfoKRdF9GQOGx2RA==} - - /detect-indent/6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - - /dezalgo/1.0.4: - resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} - dependencies: - asap: 2.0.6 - wrappy: 1.0.2 - - /diff/4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dev: true - - /dir-glob/3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - dependencies: - path-type: 4.0.0 - - /doctrine/2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - dependencies: - esutils: 2.0.3 - dev: true - - /doctrine/3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dependencies: - esutils: 2.0.3 - dev: true - - /dot-prop/5.3.0: - resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} - engines: {node: '>=8'} - dependencies: - is-obj: 2.0.0 - - /duplexer3/0.1.4: - resolution: {integrity: sha512-CEj8FwwNA4cVH2uFCoHUrmojhYh1vmCdOaneKJXwkeY1i9jnlslVo9dx+hQ5Hl9GnH/Bwy/IjxAyOePyPKYnzA==} - - /emoji-regex/8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - /encode-registry/3.0.0: - resolution: {integrity: sha512-2fRYji8K6FwYuQ6EPBKR/J9mcqb7kIoNqt1vGvJr3NrvKfncRiNm00Oxo6gi/YJF8R5Sp2bNFSFdGKTG0rje1Q==} - engines: {node: '>=10'} - dependencies: - mem: 8.1.1 - - /end-of-stream/1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - dependencies: - once: 1.4.0 - - /error-ex/1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - dependencies: - is-arrayish: 0.2.1 - - /es-abstract/1.20.1: - resolution: {integrity: sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - es-to-primitive: 1.2.1 - function-bind: 1.1.1 - function.prototype.name: 1.1.5 - get-intrinsic: 1.1.1 - get-symbol-description: 1.0.0 - has: 1.0.3 - has-property-descriptors: 1.0.0 - has-symbols: 1.0.3 - internal-slot: 1.0.3 - is-callable: 1.2.4 - is-negative-zero: 2.0.2 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 - is-string: 1.0.7 - is-weakref: 1.0.2 - object-inspect: 1.12.2 - object-keys: 1.1.1 - object.assign: 4.1.2 - regexp.prototype.flags: 1.4.3 - string.prototype.trimend: 1.0.5 - string.prototype.trimstart: 1.0.5 - unbox-primitive: 1.0.2 - dev: true - - /es-shim-unscopables/1.0.0: - resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} - dependencies: - has: 1.0.3 - dev: true - - /es-to-primitive/1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} - dependencies: - is-callable: 1.2.4 - is-date-object: 1.0.4 - is-symbol: 1.0.4 - dev: true - - /escalade/3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} - engines: {node: '>=6'} - - /escape-goat/2.1.1: - resolution: {integrity: sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==} - engines: {node: '>=8'} - - /escape-string-regexp/1.0.5: - resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=} - engines: {node: '>=0.8.0'} - - /escape-string-regexp/4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - dev: true - - /eslint-plugin-promise/6.0.0_eslint@8.7.0: - resolution: {integrity: sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - dependencies: - eslint: 8.7.0 - dev: true - - /eslint-plugin-react/7.27.1_eslint@8.7.0: - resolution: {integrity: sha512-meyunDjMMYeWr/4EBLTV1op3iSG3mjT/pz5gti38UzfM4OPpNc2m0t2xvKCOMU5D6FSdd34BIMFOvQbW+i8GAA==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - array-includes: 3.1.5 - array.prototype.flatmap: 1.3.0 - doctrine: 2.1.0 - eslint: 8.7.0 - estraverse: 5.3.0 - jsx-ast-utils: 2.4.1 - minimatch: 3.1.2 - object.entries: 1.1.5 - object.fromentries: 2.0.5 - object.hasown: 1.1.1 - object.values: 1.1.5 - prop-types: 15.7.2 - resolve: 2.0.0-next.3 - semver: 6.3.0 - string.prototype.matchall: 4.0.7 - dev: true - - /eslint-plugin-tsdoc/0.2.16: - resolution: {integrity: sha512-F/RWMnyDQuGlg82vQEFHQtGyWi7++XJKdYNn0ulIbyMOFqYIjoJOUdE6olORxgwgLkpJxsCJpJbTHgxJ/ggfXw==} - dependencies: - '@microsoft/tsdoc': 0.14.1 - '@microsoft/tsdoc-config': 0.16.1 - dev: true - - /eslint-scope/5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - dev: true - - /eslint-scope/7.1.1: - resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - dev: true - - /eslint-utils/3.0.0_eslint@8.7.0: - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - dependencies: - eslint: 8.7.0 - eslint-visitor-keys: 2.1.0 - dev: true - - /eslint-visitor-keys/2.1.0: - resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} - engines: {node: '>=10'} - dev: true - - /eslint-visitor-keys/3.3.0: - resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - - /eslint/8.7.0: - resolution: {integrity: sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true - dependencies: - '@eslint/eslintrc': 1.3.0 - '@humanwhocodes/config-array': 0.9.5 - ajv: 6.12.6 - chalk: 4.1.1 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.1.1 - eslint-utils: 3.0.0_eslint@8.7.0 - eslint-visitor-keys: 3.3.0 - espree: 9.3.2 - esquery: 1.4.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - functional-red-black-tree: 1.0.1 - glob-parent: 6.0.2 - globals: 13.10.0 - ignore: 5.2.0 - import-fresh: 3.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.1 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.0.4 - natural-compare: 1.4.0 - optionator: 0.9.1 - regexpp: 3.2.0 - strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 - text-table: 0.2.0 - v8-compile-cache: 2.3.0 - transitivePeerDependencies: - - supports-color - dev: true - - /espree/9.3.2: - resolution: {integrity: sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.7.1 - acorn-jsx: 5.3.2_acorn@8.7.1 - eslint-visitor-keys: 3.3.0 - dev: true - - /esprima/4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - /esquery/1.4.0: - resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} - engines: {node: '>=0.10'} - dependencies: - estraverse: 5.3.0 - dev: true - - /esrecurse/4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - dependencies: - estraverse: 5.3.0 - dev: true - - /estraverse/4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - dev: true - - /estraverse/5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - dev: true - - /estree-walker/2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - - /esutils/2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - dev: true - - /execa/5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - /external-editor/3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} - dependencies: - chardet: 0.7.0 - iconv-lite: 0.4.24 - tmp: 0.0.33 - - /fast-deep-equal/3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - /fast-glob/3.2.11: - resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} - engines: {node: '>=8.6.0'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.7 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.4 - - /fast-json-stable-stringify/2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true - - /fast-levenshtein/2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - dev: true - - /fastq/1.11.0: - resolution: {integrity: sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==} - dependencies: - reusify: 1.0.4 - - /figures/3.0.0: - resolution: {integrity: sha512-HKri+WoWoUgr83pehn/SIgLOMZ9nAWC6dcGj26RY2R4F50u4+RTUz0RCrUlOV3nKRAICW1UGzyb+kcX2qK1S/g==} - engines: {node: '>=8'} - dependencies: - escape-string-regexp: 1.0.5 - - /file-entry-cache/6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flat-cache: 3.0.4 - dev: true - - /fill-range/7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} - engines: {node: '>=8'} - dependencies: - to-regex-range: 5.0.1 - - /find-up/4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - - /find-up/5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - /find-yarn-workspace-root2/1.2.16: - resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} - dependencies: - micromatch: 4.0.4 - pkg-dir: 4.2.0 - - /flat-cache/3.0.4: - resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flatted: 3.2.1 - rimraf: 3.0.2 - dev: true - - /flatted/3.2.1: - resolution: {integrity: sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==} - dev: true - - /form-data/3.0.1: - resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} - engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - - /fs-extra/7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - /fs-minipass/2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - - /fs.realpath/1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - /fsevents/2.1.3: - resolution: {integrity: sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - deprecated: '"Please update to latest v2.3 or v2.2"' - requiresBuild: true - optional: true - - /function-bind/1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - - /function.prototype.name/1.1.5: - resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.1 - functions-have-names: 1.2.3 - dev: true - - /functional-red-black-tree/1.0.1: - resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} - dev: true - - /functions-have-names/1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - dev: true - - /get-caller-file/2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - /get-intrinsic/1.1.1: - resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==} - dependencies: - function-bind: 1.1.1 - has: 1.0.3 - has-symbols: 1.0.3 - dev: true - - /get-stream/4.1.0: - resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} - engines: {node: '>=6'} - dependencies: - pump: 3.0.0 - - /get-stream/5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - dependencies: - pump: 3.0.0 - - /get-stream/6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - /get-symbol-description/1.0.0: - resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.1.1 - dev: true - - /git-repo-info/2.1.1: - resolution: {integrity: sha512-8aCohiDo4jwjOwma4FmYFd3i97urZulL8XL24nIPxuE+GZnfsAyy/g2Shqx6OjUiFKUXZM+Yy+KHnOmmA3FVcg==} - engines: {node: '>= 4.0'} - - /giturl/1.0.1: - resolution: {integrity: sha512-wQourBdI13n8tbjcZTDl6k+ZrCRMU6p9vfp9jknZq+zfWc8xXNztpZFM4XkPHVzHcMSUZxEMYYKZjIGkPlei6Q==} - engines: {node: '>= 0.10.0'} - - /glob-escape/0.0.2: - resolution: {integrity: sha512-L/cXYz8x7qer1HAyUQ+mbjcUsJVdpRxpAf7CwqHoNBs9vTpABlGfNN4tzkDxt+u3Z7ZncVyKlCNPtzb0R/7WbA==} - engines: {node: '>= 0.10'} - - /glob-parent/5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - dependencies: - is-glob: 4.0.3 - - /glob-parent/6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - dependencies: - is-glob: 4.0.3 - dev: true - - /glob-to-regexp/0.4.1: - resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - dev: true - - /glob/7.0.6: - resolution: {integrity: sha512-f8c0rE8JiCxpa52kWPAOa3ZaYEnzofDzCQLCn3Vdk0Z5OVLq3BsRFJI4S4ykpeVW6QMGBUkMeUpoEgWnMTnw5Q==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - /glob/7.1.7: - resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - /glob/9.3.2: - resolution: {integrity: sha1-hShSLgA4GeY9Ecl5swiW4Or1Lto=} - engines: {node: '>=16 || 14 >=14.17'} - dependencies: - fs.realpath: 1.0.0 - minimatch: 7.4.3 - minipass: 4.2.5 - path-scurry: 1.6.3 - dev: true - - /global-dirs/3.0.1: - resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} - engines: {node: '>=10'} - dependencies: - ini: 2.0.0 - - /global-modules/2.0.0: - resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} - engines: {node: '>=6'} - dependencies: - global-prefix: 3.0.0 - - /global-prefix/3.0.0: - resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} - engines: {node: '>=6'} - dependencies: - ini: 1.3.8 - kind-of: 6.0.3 - which: 1.3.1 - - /globals/11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - - /globals/13.10.0: - resolution: {integrity: sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.20.2 - dev: true - - /globals/13.15.0: - resolution: {integrity: sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.20.2 - dev: true - - /globby/11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.2.11 - ignore: 5.2.0 - merge2: 1.4.1 - slash: 3.0.0 - - /got/9.6.0: - resolution: {integrity: sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==} - engines: {node: '>=8.6'} - dependencies: - '@sindresorhus/is': 0.14.0 - '@szmarczak/http-timer': 1.1.2 - '@types/keyv': 3.1.4 - '@types/responselike': 1.0.0 - cacheable-request: 6.1.0 - decompress-response: 3.3.0 - duplexer3: 0.1.4 - get-stream: 4.1.0 - lowercase-keys: 1.0.1 - mimic-response: 1.0.1 - p-cancelable: 1.1.0 - to-readable-stream: 1.0.0 - url-parse-lax: 3.0.0 - - /graceful-fs/4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - /graceful-fs/4.2.4: - resolution: {integrity: sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==} - - /grapheme-splitter/1.0.4: - resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} - dev: true - - /hard-rejection/2.1.0: - resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} - engines: {node: '>=6'} - - /has-bigints/1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - dev: true - - /has-flag/3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - - /has-flag/4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - /has-property-descriptors/1.0.0: - resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} - dependencies: - get-intrinsic: 1.1.1 - dev: true - - /has-symbols/1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - dev: true - - /has-tostringtag/1.0.0: - resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} - engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.0.3 - dev: true - - /has-yarn/2.1.0: - resolution: {integrity: sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==} - engines: {node: '>=8'} - - /has/1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} - dependencies: - function-bind: 1.1.1 - - /highlight-es/1.0.3: - resolution: {integrity: sha512-s/SIX6yp/5S1p8aC/NRDC1fwEb+myGIfp8/TzZz0rtAv8fzsdX7vGl3Q1TrXCsczFq8DI3CBFBCySPClfBSdbg==} - dependencies: - chalk: 2.4.2 - is-es2016-keyword: 1.0.0 - js-tokens: 3.0.2 - - /hosted-git-info/2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - - /hosted-git-info/4.1.0: - resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} - engines: {node: '>=10'} - dependencies: - lru-cache: 6.0.0 - - /http-cache-semantics/4.1.1: - resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - - /https-proxy-agent/5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - - /human-signals/2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - - /iconv-lite/0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - dependencies: - safer-buffer: 2.1.2 - - /ieee754/1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - /ignore-walk/3.0.4: - resolution: {integrity: sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==} - dependencies: - minimatch: 3.1.2 - - /ignore/5.1.9: - resolution: {integrity: sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==} - engines: {node: '>= 4'} - - /ignore/5.2.0: - resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} - engines: {node: '>= 4'} - - /immediate/3.0.6: - resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - - /immutable/4.3.0: - resolution: {integrity: sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==} - - /import-fresh/3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - /import-lazy/2.1.0: - resolution: {integrity: sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==} - engines: {node: '>=4'} - - /import-lazy/4.0.0: - resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} - engines: {node: '>=8'} - - /imurmurhash/0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - /indent-string/4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - - /inflight/1.0.6: - resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=} - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - /inherits/2.0.4: - resolution: {integrity: sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=} - - /ini/1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - - /ini/2.0.0: - resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} - engines: {node: '>=10'} - - /inquirer/7.3.3: - resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==} - engines: {node: '>=8.0.0'} - dependencies: - ansi-escapes: 4.3.2 - chalk: 4.1.1 - cli-cursor: 3.1.0 - cli-width: 3.0.0 - external-editor: 3.1.0 - figures: 3.0.0 - lodash: 4.17.21 - mute-stream: 0.0.8 - run-async: 2.4.1 - rxjs: 6.6.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - through: 2.3.8 - - /internal-slot/1.0.3: - resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.1.1 - has: 1.0.3 - side-channel: 1.0.4 - dev: true - - /invariant/2.2.4: - resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - dependencies: - loose-envify: 1.4.0 - - /is-arrayish/0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - /is-bigint/1.0.2: - resolution: {integrity: sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==} - dev: true - - /is-binary-path/2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - dependencies: - binary-extensions: 2.2.0 - - /is-boolean-object/1.1.1: - resolution: {integrity: sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - dev: true - - /is-callable/1.2.4: - resolution: {integrity: sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==} - engines: {node: '>= 0.4'} - dev: true - - /is-ci/2.0.0: - resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==} - hasBin: true - dependencies: - ci-info: 2.0.0 - - /is-core-module/2.11.0: - resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} - dependencies: - has: 1.0.3 - - /is-date-object/1.0.4: - resolution: {integrity: sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==} - engines: {node: '>= 0.4'} - dev: true - - /is-es2016-keyword/1.0.0: - resolution: {integrity: sha512-JtZWPUwjdbQ1LIo9OSZ8MdkWEve198ors27vH+RzUUvZXXZkzXCxFnlUhzWYxy5IexQSRiXVw9j2q/tHMmkVYQ==} - - /is-extglob/2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - /is-fullwidth-code-point/3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - /is-glob/4.0.1: - resolution: {integrity: sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==} - engines: {node: '>=0.10.0'} - dependencies: - is-extglob: 2.1.1 - dev: true - - /is-glob/4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - dependencies: - is-extglob: 2.1.1 - - /is-installed-globally/0.4.0: - resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} - engines: {node: '>=10'} - dependencies: - global-dirs: 3.0.1 - is-path-inside: 3.0.3 - - /is-interactive/1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - - /is-negative-zero/2.0.2: - resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} - engines: {node: '>= 0.4'} - dev: true - - /is-npm/5.0.0: - resolution: {integrity: sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==} - engines: {node: '>=10'} - - /is-number-object/1.0.5: - resolution: {integrity: sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==} - engines: {node: '>= 0.4'} - dev: true - - /is-number/7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - /is-obj/2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - - /is-path-inside/3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - - /is-plain-obj/1.1.0: - resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} - engines: {node: '>=0.10.0'} - - /is-plain-obj/2.1.0: - resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} - engines: {node: '>=8'} - - /is-regex/1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - dev: true - - /is-shared-array-buffer/1.0.2: - resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} - dependencies: - call-bind: 1.0.2 - dev: true - - /is-stream/2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - /is-string/1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - dev: true - - /is-subdir/1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} - dependencies: - better-path-resolve: 1.0.0 - - /is-symbol/1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.0.3 - dev: true - - /is-typedarray/1.0.0: - resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - - /is-unicode-supported/0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - - /is-weakref/1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - dependencies: - call-bind: 1.0.2 - dev: true - - /is-windows/1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - - /is-yarn-global/0.3.0: - resolution: {integrity: sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==} - - /isarray/1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - - /isexe/2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - /jju/1.4.0: - resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} - - /js-tokens/3.0.2: - resolution: {integrity: sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==} - - /js-tokens/4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - /js-yaml/3.13.1: - resolution: {integrity: sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==} - hasBin: true - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - - /js-yaml/3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - - /js-yaml/4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - dependencies: - argparse: 2.0.1 - - /jsesc/2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true - - /json-buffer/3.0.0: - resolution: {integrity: sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==} - - /json-parse-even-better-errors/2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - - /json-schema-traverse/0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true - - /json-stable-stringify-without-jsonify/1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - dev: true - - /json5/2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - /jsonfile/4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - optionalDependencies: - graceful-fs: 4.2.11 - - /jsonpath-plus/4.0.0: - resolution: {integrity: sha512-e0Jtg4KAzDJKKwzbLaUtinCn0RZseWBVRTRGihSpvFlM3wTR7ExSp+PTdeTsDrLNJUe7L7JYJe8mblHX5SCT6A==} - engines: {node: '>=10.0'} - - /jsx-ast-utils/2.4.1: - resolution: {integrity: sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w==} - engines: {node: '>=4.0'} - dependencies: - array-includes: 3.1.5 - object.assign: 4.1.2 - dev: true - - /jszip/3.8.0: - resolution: {integrity: sha512-cnpQrXvFSLdsR9KR5/x7zdf6c3m8IhZfZzSblFEHSqBaVwD2nvJ4CuCKLyvKvwBgZm08CgfSoiTBQLm5WW9hGw==} - dependencies: - lie: 3.3.0 - pako: 1.0.11 - readable-stream: 2.3.8 - set-immediate-shim: 1.0.1 - - /keyv/3.1.0: - resolution: {integrity: sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==} - dependencies: - json-buffer: 3.0.0 - - /kind-of/6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - - /latest-version/5.1.0: - resolution: {integrity: sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==} - engines: {node: '>=8'} - dependencies: - package-json: 6.5.0 - - /levn/0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - dev: true - - /lie/3.3.0: - resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} - dependencies: - immediate: 3.0.6 - - /lines-and-columns/1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - /load-json-file/6.2.0: - resolution: {integrity: sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==} - engines: {node: '>=8'} - dependencies: - graceful-fs: 4.2.11 - parse-json: 5.2.0 - strip-bom: 4.0.0 - type-fest: 0.6.0 - - /load-yaml-file/0.2.0: - resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} - engines: {node: '>=6'} - dependencies: - graceful-fs: 4.2.11 - js-yaml: 3.14.1 - pify: 4.0.1 - strip-bom: 3.0.0 - - /locate-path/5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - dependencies: - p-locate: 4.1.0 - - /locate-path/6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - dependencies: - p-locate: 5.0.0 - - /lodash.get/4.4.2: - resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} - - /lodash.isequal/4.5.0: - resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - - /lodash.merge/4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: true - - /lodash/4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - /log-symbols/4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - dependencies: - chalk: 4.1.1 - is-unicode-supported: 0.1.0 - - /loose-envify/1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - dependencies: - js-tokens: 4.0.0 - - /lowercase-keys/1.0.1: - resolution: {integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==} - engines: {node: '>=0.10.0'} - - /lowercase-keys/2.0.0: - resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} - engines: {node: '>=8'} - - /lru-cache/6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - dependencies: - yallist: 4.0.0 - - /lru-cache/7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - dev: true - - /magic-string/0.25.9: - resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} - dependencies: - sourcemap-codec: 1.4.8 - - /make-dir/3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} - engines: {node: '>=8'} - dependencies: - semver: 6.3.0 - - /map-age-cleaner/0.1.3: - resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==} - engines: {node: '>=6'} - dependencies: - p-defer: 1.0.0 - - /map-obj/1.0.1: - resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} - engines: {node: '>=0.10.0'} - - /map-obj/4.3.0: - resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} - engines: {node: '>=8'} - - /mem/8.1.1: - resolution: {integrity: sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==} - engines: {node: '>=10'} - dependencies: - map-age-cleaner: 0.1.3 - mimic-fn: 3.1.0 - - /meow/9.0.0: - resolution: {integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==} - engines: {node: '>=10'} - dependencies: - '@types/minimist': 1.2.2 - camelcase-keys: 6.2.2 - decamelize: 1.2.0 - decamelize-keys: 1.1.1 - hard-rejection: 2.1.0 - minimist-options: 4.1.0 - normalize-package-data: 3.0.3 - read-pkg-up: 7.0.1 - redent: 3.0.0 - trim-newlines: 3.0.1 - type-fest: 0.18.1 - yargs-parser: 20.2.9 - - /merge-stream/2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - /merge2/1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - /micromatch/4.0.4: - resolution: {integrity: sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==} - engines: {node: '>=8.6'} - dependencies: - braces: 3.0.2 - picomatch: 2.3.0 - - /mime-db/1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - /mime-types/2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - dependencies: - mime-db: 1.52.0 - - /mimic-fn/2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - /mimic-fn/3.1.0: - resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==} - engines: {node: '>=8'} - - /mimic-response/1.0.1: - resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} - engines: {node: '>=4'} - - /min-indent/1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - - /minimatch/3.0.4: - resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} - dependencies: - brace-expansion: 1.1.11 - dev: true - - /minimatch/3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - dependencies: - brace-expansion: 1.1.11 - - /minimatch/7.4.3: - resolution: {integrity: sha1-ASy/EQplE0uzVK6Xc7VSVs2wRaI=} - engines: {node: '>=10'} - dependencies: - brace-expansion: 2.0.1 - dev: true - - /minimist-options/4.1.0: - resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} - engines: {node: '>= 6'} - dependencies: - arrify: 1.0.1 - is-plain-obj: 1.1.0 - kind-of: 6.0.3 - - /minimist/1.2.5: - resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==} - - /minipass/3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - dependencies: - yallist: 4.0.0 - - /minipass/4.2.5: - resolution: {integrity: sha1-ng5SVvHjUT+MNGkd1oVJ6FssjOs=} - engines: {node: '>=8'} - - /minizlib/2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - - /mkdirp/0.5.5: - resolution: {integrity: sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==} - hasBin: true - dependencies: - minimist: 1.2.5 - dev: true - - /mkdirp/1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - - /ms/2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - - /multimatch/5.0.0: - resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} - engines: {node: '>=10'} - dependencies: - '@types/minimatch': 3.0.5 - array-differ: 3.0.0 - array-union: 2.1.0 - arrify: 2.0.1 - minimatch: 3.1.2 - - /mute-stream/0.0.8: - resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} - - /mz/2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 - - /nanoid/3.3.6: - resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - /natural-compare-lite/1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - dev: true - - /natural-compare/1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: true - - /node-emoji/1.11.0: - resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} - dependencies: - lodash: 4.17.21 - - /node-fetch/2.6.7: - resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - dependencies: - whatwg-url: 5.0.0 - - /normalize-package-data/2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.1 - semver: 5.7.1 - validate-npm-package-license: 3.0.4 - - /normalize-package-data/3.0.3: - resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} - engines: {node: '>=10'} - dependencies: - hosted-git-info: 4.1.0 - is-core-module: 2.11.0 - semver: 7.3.8 - validate-npm-package-license: 3.0.4 - - /normalize-path/3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - /normalize-url/4.5.1: - resolution: {integrity: sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==} - engines: {node: '>=8'} - - /npm-bundled/1.1.2: - resolution: {integrity: sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==} - dependencies: - npm-normalize-package-bin: 1.0.1 - - /npm-check/6.0.1: - resolution: {integrity: sha512-tlEhXU3689VLUHYEZTS/BC61vfeN2xSSZwoWDT6WLuenZTpDmGmNT5mtl15erTR0/A15ldK06/NEKg9jYJ9OTQ==} - engines: {node: '>=10.9.0'} - hasBin: true - dependencies: - callsite-record: 4.1.5 - chalk: 4.1.1 - co: 4.6.0 - depcheck: 1.4.3 - execa: 5.1.1 - giturl: 1.0.1 - global-modules: 2.0.0 - globby: 11.1.0 - inquirer: 7.3.3 - is-ci: 2.0.0 - lodash: 4.17.21 - meow: 9.0.0 - minimatch: 3.1.2 - node-emoji: 1.11.0 - ora: 5.4.1 - package-json: 6.5.0 - path-exists: 4.0.0 - pkg-dir: 5.0.0 - preferred-pm: 3.0.3 - rc-config-loader: 4.1.2 - semver: 7.3.8 - semver-diff: 3.1.1 - strip-ansi: 6.0.1 - text-table: 0.2.0 - throat: 6.0.2 - update-notifier: 5.1.0 - xtend: 4.0.2 - transitivePeerDependencies: - - supports-color - - /npm-normalize-package-bin/1.0.1: - resolution: {integrity: sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==} - - /npm-package-arg/6.1.1: - resolution: {integrity: sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==} - dependencies: - hosted-git-info: 2.8.9 - osenv: 0.1.5 - semver: 5.7.1 - validate-npm-package-name: 3.0.0 - - /npm-packlist/2.1.5: - resolution: {integrity: sha512-KCfK3Vi2F+PH1klYauoQzg81GQ8/GGjQRKYY6tRnpQUPKTs/1gBZSRWtTEd7jGdSn1LZL7gpAmJT+BcS55k2XQ==} - engines: {node: '>=10'} - hasBin: true - dependencies: - glob: 7.1.7 - ignore-walk: 3.0.4 - npm-bundled: 1.1.2 - npm-normalize-package-bin: 1.0.1 - - /npm-run-path/4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - dependencies: - path-key: 3.1.1 - - /object-assign/4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - /object-inspect/1.12.2: - resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} - dev: true - - /object-keys/1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - dev: true - - /object.assign/4.1.2: - resolution: {integrity: sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - has-symbols: 1.0.3 - object-keys: 1.1.1 - dev: true - - /object.entries/1.1.5: - resolution: {integrity: sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.1 - dev: true - - /object.fromentries/2.0.5: - resolution: {integrity: sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.1 - dev: true - - /object.hasown/1.1.1: - resolution: {integrity: sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==} - dependencies: - define-properties: 1.1.4 - es-abstract: 1.20.1 - dev: true - - /object.values/1.1.5: - resolution: {integrity: sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.1 - dev: true - - /once/1.4.0: - resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} - dependencies: - wrappy: 1.0.2 - - /onetime/5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - dependencies: - mimic-fn: 2.1.0 - - /optionator/0.9.1: - resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} - engines: {node: '>= 0.8.0'} - dependencies: - deep-is: 0.1.3 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.3 - dev: true - - /ora/5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} - dependencies: - bl: 4.1.0 - chalk: 4.1.1 - cli-cursor: 3.1.0 - cli-spinners: 2.7.0 - is-interactive: 1.0.0 - is-unicode-supported: 0.1.0 - log-symbols: 4.1.0 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - - /os-homedir/1.0.2: - resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} - engines: {node: '>=0.10.0'} - - /os-tmpdir/1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - - /osenv/0.1.5: - resolution: {integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==} - dependencies: - os-homedir: 1.0.2 - os-tmpdir: 1.0.2 - - /p-cancelable/1.1.0: - resolution: {integrity: sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==} - engines: {node: '>=6'} - - /p-defer/1.0.0: - resolution: {integrity: sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==} - engines: {node: '>=4'} - - /p-limit/2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - dependencies: - p-try: 2.2.0 - - /p-limit/3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - dependencies: - yocto-queue: 0.1.0 - - /p-locate/4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - dependencies: - p-limit: 2.3.0 - - /p-locate/5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - dependencies: - p-limit: 3.1.0 - - /p-reflect/2.1.0: - resolution: {integrity: sha512-paHV8NUz8zDHu5lhr/ngGWQiW067DK/+IbJ+RfZ4k+s8y4EKyYCz8pGYWjxCg35eHztpJAt+NUgvN4L+GCbPlg==} - engines: {node: '>=8'} - - /p-settle/4.1.1: - resolution: {integrity: sha512-6THGh13mt3gypcNMm0ADqVNCcYa3BK6DWsuJWFCuEKP1rpY+OKGp7gaZwVmLspmic01+fsg/fN57MfvDzZ/PuQ==} - engines: {node: '>=10'} - dependencies: - p-limit: 2.3.0 - p-reflect: 2.1.0 - - /p-try/2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - /package-json/6.5.0: - resolution: {integrity: sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==} - engines: {node: '>=8'} - dependencies: - got: 9.6.0 - registry-auth-token: 4.2.2 - registry-url: 5.1.0 - semver: 6.3.0 - - /pako/1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - - /parent-module/1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - dependencies: - callsites: 3.1.0 - - /parse-json/5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - dependencies: - '@babel/code-frame': 7.18.6 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - - /path-exists/4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - /path-is-absolute/1.0.1: - resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} - engines: {node: '>=0.10.0'} - - /path-key/3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - /path-parse/1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - /path-scurry/1.6.3: - resolution: {integrity: sha1-Trpxg9ZO+Itjx9Mwvdw7onncbEA=} - engines: {node: '>=16 || 14 >=14.17'} - dependencies: - lru-cache: 7.18.3 - minipass: 4.2.5 - dev: true - - /path-type/4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - /picocolors/1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - - /picomatch/2.3.0: - resolution: {integrity: sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==} - engines: {node: '>=8.6'} - - /pify/4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - - /pinkie-promise/2.0.1: - resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} - engines: {node: '>=0.10.0'} - dependencies: - pinkie: 2.0.4 - - /pinkie/2.0.4: - resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} - engines: {node: '>=0.10.0'} - - /pkg-dir/4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - dependencies: - find-up: 4.1.0 - - /pkg-dir/5.0.0: - resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} - engines: {node: '>=10'} - dependencies: - find-up: 5.0.0 - - /please-upgrade-node/3.2.0: - resolution: {integrity: sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==} - dependencies: - semver-compare: 1.0.0 - - /postcss/8.4.21: - resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.6 - picocolors: 1.0.0 - source-map-js: 1.0.2 - - /preferred-pm/3.0.3: - resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==} - engines: {node: '>=10'} - dependencies: - find-up: 5.0.0 - find-yarn-workspace-root2: 1.2.16 - path-exists: 4.0.0 - which-pm: 2.0.0 - - /prelude-ls/1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - dev: true - - /prepend-http/2.0.0: - resolution: {integrity: sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==} - engines: {node: '>=4'} - - /process-nextick-args/2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - /prop-types/15.7.2: - resolution: {integrity: sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==} - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - dev: true - - /pump/3.0.0: - resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} - dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 - - /punycode/2.1.1: - resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} - engines: {node: '>=6'} - dev: true - - /pupa/2.1.1: - resolution: {integrity: sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==} - engines: {node: '>=8'} - dependencies: - escape-goat: 2.1.1 - - /query-ast/1.0.5: - resolution: {integrity: sha512-JK+1ma4YDuLjvKKcz9JZ70G+CM9qEOs/l1cZzstMMfwKUabTJ9sud5jvDGrUNuv03yKUgs82bLkHXJkDyhRmBw==} - dependencies: - invariant: 2.2.4 - lodash: 4.17.21 - - /queue-microtask/1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - /quick-lru/4.0.1: - resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} - engines: {node: '>=8'} - - /ramda/0.27.2: - resolution: {integrity: sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA==} - - /rc-config-loader/4.1.2: - resolution: {integrity: sha512-qKTnVWFl9OQYKATPzdfaZIbTxcHziQl92zYSxYC6umhOqyAsoj8H8Gq/+aFjAso68sBdjTz3A7omqeAkkF1MWg==} - dependencies: - debug: 4.3.4 - js-yaml: 4.1.0 - json5: 2.2.3 - require-from-string: 2.0.2 - transitivePeerDependencies: - - supports-color - - /rc/1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true - dependencies: - deep-extend: 0.6.0 - ini: 1.3.8 - minimist: 1.2.5 - strip-json-comments: 2.0.1 - - /react-is/16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - dev: true - - /read-package-json/2.1.2: - resolution: {integrity: sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==} - dependencies: - glob: 7.1.7 - json-parse-even-better-errors: 2.3.1 - normalize-package-data: 2.5.0 - npm-normalize-package-bin: 1.0.1 - - /read-package-tree/5.1.6: - resolution: {integrity: sha512-FCX1aT3GWyY658wzDICef4p+n0dB+ENRct8E/Qyvppj6xVpOYerBHfUu7OP5Rt1/393Tdglguf5ju5DEX4wZNg==} - deprecated: The functionality that this package provided is now in @npmcli/arborist - dependencies: - debuglog: 1.0.1 - dezalgo: 1.0.4 - once: 1.4.0 - read-package-json: 2.1.2 - readdir-scoped-modules: 1.1.0 - - /read-pkg-up/7.0.1: - resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} - engines: {node: '>=8'} - dependencies: - find-up: 4.1.0 - read-pkg: 5.2.0 - type-fest: 0.8.1 - - /read-pkg/5.2.0: - resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} - engines: {node: '>=8'} - dependencies: - '@types/normalize-package-data': 2.4.1 - normalize-package-data: 2.5.0 - parse-json: 5.2.0 - type-fest: 0.6.0 - - /read-yaml-file/2.1.0: - resolution: {integrity: sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==} - engines: {node: '>=10.13'} - dependencies: - js-yaml: 4.1.0 - strip-bom: 4.0.0 - - /readable-stream/2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - - /readable-stream/3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - - /readdir-scoped-modules/1.1.0: - resolution: {integrity: sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==} - deprecated: This functionality has been moved to @npmcli/fs - dependencies: - debuglog: 1.0.1 - dezalgo: 1.0.4 - graceful-fs: 4.2.11 - once: 1.4.0 - - /readdirp/3.5.0: - resolution: {integrity: sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==} - engines: {node: '>=8.10.0'} - dependencies: - picomatch: 2.3.0 - - /redent/3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} - dependencies: - indent-string: 4.0.0 - strip-indent: 3.0.0 - - /regexp.prototype.flags/1.4.3: - resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - functions-have-names: 1.2.3 - dev: true - - /regexpp/3.2.0: - resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} - engines: {node: '>=8'} - dev: true - - /registry-auth-token/4.2.2: - resolution: {integrity: sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==} - engines: {node: '>=6.0.0'} - dependencies: - rc: 1.2.8 - - /registry-url/5.1.0: - resolution: {integrity: sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==} - engines: {node: '>=8'} - dependencies: - rc: 1.2.8 - - /require-directory/2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - /require-from-string/2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - - /require-package-name/2.0.1: - resolution: {integrity: sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==} - - /resolve-from/4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - /resolve/1.19.0: - resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} - dependencies: - is-core-module: 2.11.0 - path-parse: 1.0.7 - dev: true - - /resolve/1.20.0: - resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==} - dependencies: - is-core-module: 2.11.0 - path-parse: 1.0.7 - dev: true - - /resolve/1.22.1: - resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} - hasBin: true - dependencies: - is-core-module: 2.11.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - /resolve/2.0.0-next.3: - resolution: {integrity: sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==} - dependencies: - is-core-module: 2.11.0 - path-parse: 1.0.7 - dev: true - - /responselike/1.0.2: - resolution: {integrity: sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==} - dependencies: - lowercase-keys: 1.0.1 - - /restore-cursor/3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - - /reusify/1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - /rfc4648/1.5.2: - resolution: {integrity: sha512-tLOizhR6YGovrEBLatX1sdcuhoSCXddw3mqNVAcKxGJ+J0hFeJ+SjeWCv5UPA/WU3YzWPPuCVYgXBKZUPGpKtg==} - - /rimraf/3.0.2: - resolution: {integrity: sha1-8aVAK6YiCtUswSgrrBrjqkn9Bho=} - hasBin: true - dependencies: - glob: 7.1.7 - dev: true - - /rimraf/4.4.1: - resolution: {integrity: sha1-vTM2T2cCHFt56T1/T6BWjHwht1U=} - engines: {node: '>=14'} - hasBin: true - dependencies: - glob: 9.3.2 - dev: true - - /run-async/2.4.1: - resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} - engines: {node: '>=0.12.0'} - - /run-parallel/1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - dependencies: - queue-microtask: 1.2.3 - - /rxjs/6.6.7: - resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} - engines: {npm: '>=2.0.0'} - dependencies: - tslib: 1.14.1 - - /safe-buffer/5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - - /safe-buffer/5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - /safer-buffer/2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - /sass/1.60.0: - resolution: {integrity: sha512-updbwW6fNb5gGm8qMXzVO7V4sWf7LMXnMly/JEyfbfERbVH46Fn6q02BX7/eHTdKpE7d+oTkMMQpFWNUMfFbgQ==} - engines: {node: '>=12.0.0'} - hasBin: true - dependencies: - chokidar: 3.4.3 - immutable: 4.3.0 - source-map-js: 1.0.2 - - /scss-parser/1.0.6: - resolution: {integrity: sha512-SH3TaoaJFzfAtqs3eG1j5IuHJkeEW5rKUPIjIN+ZorLAyJLHItQGnsgwHk76v25GtLtpT9IqfAcqK4vFWdiw+w==} - engines: {node: '>=6.0.0'} - dependencies: - invariant: 2.2.4 - lodash: 4.17.21 - - /semver-compare/1.0.0: - resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} - - /semver-diff/3.1.1: - resolution: {integrity: sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==} - engines: {node: '>=8'} - dependencies: - semver: 6.3.0 - - /semver/5.7.1: - resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} - hasBin: true - - /semver/6.3.0: - resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} - hasBin: true - - /semver/7.3.8: - resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - - /set-immediate-shim/1.0.1: - resolution: {integrity: sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ==} - engines: {node: '>=0.10.0'} - - /shebang-command/2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - dependencies: - shebang-regex: 3.0.0 - - /shebang-regex/3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - /side-channel/1.0.4: - resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.1.1 - object-inspect: 1.12.2 - dev: true - - /signal-exit/3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - /slash/3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - /sort-keys/4.2.0: - resolution: {integrity: sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==} - engines: {node: '>=8'} - dependencies: - is-plain-obj: 2.1.0 - - /source-map-js/1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} - engines: {node: '>=0.10.0'} - - /source-map/0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - /sourcemap-codec/1.4.8: - resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} - deprecated: Please use @jridgewell/sourcemap-codec instead - - /spdx-correct/3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - dependencies: - spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.13 - - /spdx-exceptions/2.3.0: - resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} - - /spdx-expression-parse/3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - dependencies: - spdx-exceptions: 2.3.0 - spdx-license-ids: 3.0.13 - - /spdx-license-ids/3.0.13: - resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==} - - /sprintf-js/1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - /ssri/8.0.1: - resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - - /stackframe/1.3.4: - resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} - - /strict-uri-encode/2.0.0: - resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} - engines: {node: '>=4'} - - /string-argv/0.3.1: - resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==} - engines: {node: '>=0.6.19'} - - /string-width/4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - /string.prototype.matchall/4.0.7: - resolution: {integrity: sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.1 - get-intrinsic: 1.1.1 - has-symbols: 1.0.3 - internal-slot: 1.0.3 - regexp.prototype.flags: 1.4.3 - side-channel: 1.0.4 - dev: true - - /string.prototype.trimend/1.0.5: - resolution: {integrity: sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.1 - dev: true - - /string.prototype.trimstart/1.0.5: - resolution: {integrity: sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.1 - dev: true - - /string_decoder/1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - dependencies: - safe-buffer: 5.1.2 - - /string_decoder/1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - dependencies: - safe-buffer: 5.2.1 - - /strip-ansi/6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - dependencies: - ansi-regex: 5.0.1 - - /strip-bom/3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - - /strip-bom/4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - - /strip-final-newline/2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - - /strip-indent/3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} - dependencies: - min-indent: 1.0.1 - - /strip-json-comments/2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - - /strip-json-comments/3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - /supports-color/5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - dependencies: - has-flag: 3.0.0 - - /supports-color/7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - dependencies: - has-flag: 4.0.0 - - /supports-preserve-symlinks-flag/1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - /tapable/1.1.3: - resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==} - engines: {node: '>=6'} - dev: true - - /tapable/2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} - engines: {node: '>=6'} - - /tar/6.1.13: - resolution: {integrity: sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==} - engines: {node: '>=10'} - dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 4.2.5 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 - - /text-table/0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - - /thenify-all/1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} - dependencies: - thenify: 3.3.1 - - /thenify/3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - dependencies: - any-promise: 1.3.0 - - /throat/6.0.2: - resolution: {integrity: sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==} - - /through/2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - - /tmp/0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} - dependencies: - os-tmpdir: 1.0.2 - - /to-fast-properties/2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} - - /to-readable-stream/1.0.0: - resolution: {integrity: sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==} - engines: {node: '>=6'} - - /to-regex-range/5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - dependencies: - is-number: 7.0.0 - - /tr46/0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - - /trim-newlines/3.0.1: - resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} - engines: {node: '>=8'} - - /true-case-path/2.2.1: - resolution: {integrity: sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q==} - - /tslib/1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - - /tslint/5.20.1_typescript@4.7.4: - resolution: {integrity: sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==} - engines: {node: '>=4.8.0'} - hasBin: true - peerDependencies: - typescript: '>=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev' - dependencies: - '@babel/code-frame': 7.12.13 - builtin-modules: 1.1.1 - chalk: 2.4.2 - commander: 2.20.3 - diff: 4.0.2 - glob: 7.1.7 - js-yaml: 3.14.1 - minimatch: 3.0.4 - mkdirp: 0.5.5 - resolve: 1.20.0 - semver: 5.7.1 - tslib: 1.14.1 - tsutils: 2.29.0_typescript@4.7.4 - typescript: 4.7.4 - dev: true - - /tslint/5.20.1_typescript@5.0.4: - resolution: {integrity: sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==} - engines: {node: '>=4.8.0'} - hasBin: true - peerDependencies: - typescript: '>=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev' - dependencies: - '@babel/code-frame': 7.12.13 - builtin-modules: 1.1.1 - chalk: 2.4.2 - commander: 2.20.3 - diff: 4.0.2 - glob: 7.1.7 - js-yaml: 3.14.1 - minimatch: 3.0.4 - mkdirp: 0.5.5 - resolve: 1.20.0 - semver: 5.7.1 - tslib: 1.14.1 - tsutils: 2.29.0_typescript@5.0.4 - typescript: 5.0.4 - dev: true - - /tsutils/2.29.0_typescript@4.7.4: - resolution: {integrity: sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==} - peerDependencies: - typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' - dependencies: - tslib: 1.14.1 - typescript: 4.7.4 - dev: true - - /tsutils/2.29.0_typescript@5.0.4: - resolution: {integrity: sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==} - peerDependencies: - typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' - dependencies: - tslib: 1.14.1 - typescript: 5.0.4 - dev: true - - /tsutils/3.21.0_typescript@4.7.4: - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - dependencies: - tslib: 1.14.1 - typescript: 4.7.4 - dev: true - - /tsutils/3.21.0_typescript@5.0.4: - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - dependencies: - tslib: 1.14.1 - typescript: 5.0.4 - dev: true - - /type-check/0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - dev: true - - /type-fest/0.18.1: - resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} - engines: {node: '>=10'} - - /type-fest/0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - - /type-fest/0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - /type-fest/0.6.0: - resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} - engines: {node: '>=8'} - - /type-fest/0.8.1: - resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} - engines: {node: '>=8'} - - /typedarray-to-buffer/3.1.5: - resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} - dependencies: - is-typedarray: 1.0.0 - - /typescript/4.7.4: - resolution: {integrity: sha1-GohZbRz0fVlQehvN+1ud/k1IgjU=} - engines: {node: '>=4.2.0'} - hasBin: true - dev: true - - /typescript/5.0.4: - resolution: {integrity: sha1-shf9IBGb1hqU1AESdOCrNpBY2js=} - engines: {node: '>=12.20'} - hasBin: true - dev: true - - /unbox-primitive/1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - dependencies: - call-bind: 1.0.2 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 - dev: true - - /unique-string/2.0.0: - resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} - engines: {node: '>=8'} - dependencies: - crypto-random-string: 2.0.0 - - /universalify/0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - - /update-notifier/5.1.0: - resolution: {integrity: sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==} - engines: {node: '>=10'} - dependencies: - boxen: 5.1.2 - chalk: 4.1.1 - configstore: 5.0.1 - has-yarn: 2.1.0 - import-lazy: 2.1.0 - is-ci: 2.0.0 - is-installed-globally: 0.4.0 - is-npm: 5.0.0 - is-yarn-global: 0.3.0 - latest-version: 5.1.0 - pupa: 2.1.1 - semver: 7.3.8 - semver-diff: 3.1.1 - xdg-basedir: 4.0.0 - - /uri-js/4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - dependencies: - punycode: 2.1.1 - dev: true - - /url-parse-lax/3.0.0: - resolution: {integrity: sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==} - engines: {node: '>=4'} - dependencies: - prepend-http: 2.0.0 - - /util-deprecate/1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - /v8-compile-cache/2.3.0: - resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} - dev: true - - /validate-npm-package-license/3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - dependencies: - spdx-correct: 3.2.0 - spdx-expression-parse: 3.0.1 - - /validate-npm-package-name/3.0.0: - resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} - dependencies: - builtins: 1.0.3 - - /validator/13.7.0: - resolution: {integrity: sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==} - engines: {node: '>= 0.10'} - - /watchpack/2.4.0: - resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} - engines: {node: '>=10.13.0'} - dependencies: - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - dev: true - - /wcwidth/1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - dependencies: - defaults: 1.0.4 - - /webidl-conversions/3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - - /whatwg-url/5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - - /which-boxed-primitive/1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - dependencies: - is-bigint: 1.0.2 - is-boolean-object: 1.1.1 - is-number-object: 1.0.5 - is-string: 1.0.7 - is-symbol: 1.0.4 - dev: true - - /which-pm/2.0.0: - resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} - engines: {node: '>=8.15'} - dependencies: - load-yaml-file: 0.2.0 - path-exists: 4.0.0 - - /which/1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true - dependencies: - isexe: 2.0.0 - - /which/2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - dependencies: - isexe: 2.0.0 - - /widest-line/3.1.0: - resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} - engines: {node: '>=8'} - dependencies: - string-width: 4.2.3 - - /word-wrap/1.2.3: - resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} - engines: {node: '>=0.10.0'} - dev: true - - /wordwrap/1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - - /wrap-ansi/7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - /wrappy/1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - /write-file-atomic/3.0.3: - resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} - dependencies: - imurmurhash: 0.1.4 - is-typedarray: 1.0.0 - signal-exit: 3.0.7 - typedarray-to-buffer: 3.1.5 - - /write-yaml-file/4.2.0: - resolution: {integrity: sha512-LwyucHy0uhWqbrOkh9cBluZBeNVxzHjDaE9mwepZG3n3ZlbM4v3ndrFw51zW/NXYFFqP+QWZ72ihtLWTh05e4Q==} - engines: {node: '>=10.13'} - dependencies: - js-yaml: 4.1.0 - write-file-atomic: 3.0.3 - - /xdg-basedir/4.0.0: - resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} - engines: {node: '>=8'} - - /xtend/4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - - /y18n/5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - /yallist/4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - /yaml/1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - - /yargs-parser/20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - - /yargs/16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - dependencies: - cliui: 7.0.4 - escalade: 3.1.1 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 20.2.9 - - /yocto-queue/0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - - /z-schema/5.0.3: - resolution: {integrity: sha512-sGvEcBOTNum68x9jCpCVGPFJ6mWnkD0YxOcddDlJHRx3tKdB2q8pCHExMVZo/AV/6geuVJXG7hljDaWG8+5GDw==} - engines: {node: '>=8.0.0'} - hasBin: true - dependencies: - lodash.get: 4.4.2 - lodash.isequal: 4.5.0 - validator: 13.7.0 - optionalDependencies: - commander: 2.20.3 - - file:../temp/tarballs/microsoft-rush-lib-5.98.0.tgz_@types+node@14.18.36: - resolution: {tarball: file:../temp/tarballs/microsoft-rush-lib-5.98.0.tgz} - id: file:../temp/tarballs/microsoft-rush-lib-5.98.0.tgz - name: '@microsoft/rush-lib' - version: 5.98.0 - engines: {node: '>=5.6.0'} - dependencies: - '@pnpm/link-bins': 5.3.25 - '@rushstack/heft-config-file': file:../temp/tarballs/rushstack-heft-config-file-0.12.3.tgz_@types+node@14.18.36 - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz_@types+node@14.18.36 - '@rushstack/package-deps-hash': file:../temp/tarballs/rushstack-package-deps-hash-4.0.18.tgz_@types+node@14.18.36 - '@rushstack/package-extractor': file:../temp/tarballs/rushstack-package-extractor-0.2.5.tgz_@types+node@14.18.36 - '@rushstack/rig-package': file:../temp/tarballs/rushstack-rig-package-0.3.19.tgz - '@rushstack/stream-collator': file:../temp/tarballs/rushstack-stream-collator-4.0.236.tgz_@types+node@14.18.36 - '@rushstack/terminal': file:../temp/tarballs/rushstack-terminal-0.5.11.tgz_@types+node@14.18.36 - '@rushstack/ts-command-line': file:../temp/tarballs/rushstack-ts-command-line-4.13.3.tgz - '@types/node-fetch': 2.6.2 - '@yarnpkg/lockfile': 1.0.2 - builtin-modules: 3.1.0 - cli-table: 0.3.11 - colors: 1.2.5 - dependency-path: 9.2.8 - figures: 3.0.0 - git-repo-info: 2.1.1 - glob: 7.0.6 - glob-escape: 0.0.2 - https-proxy-agent: 5.0.1 - ignore: 5.1.9 - inquirer: 7.3.3 - js-yaml: 3.13.1 - lodash: 4.17.21 - node-fetch: 2.6.7 - npm-check: 6.0.1 - npm-package-arg: 6.1.1 - read-package-tree: 5.1.6 - rxjs: 6.6.7 - semver: 7.3.8 - ssri: 8.0.1 - strict-uri-encode: 2.0.0 - tapable: 2.2.1 - tar: 6.1.13 - true-case-path: 2.2.1 - transitivePeerDependencies: - - '@types/node' - - encoding - - supports-color - - file:../temp/tarballs/rushstack-eslint-config-3.3.0.tgz_ucoohk2w7gukx6ccuul7rl7pnq: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-config-3.3.0.tgz} - id: file:../temp/tarballs/rushstack-eslint-config-3.3.0.tgz - name: '@rushstack/eslint-config' - version: 3.3.0 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '>=4.7.0' - dependencies: - '@rushstack/eslint-patch': file:../temp/tarballs/rushstack-eslint-patch-1.3.0.tgz - '@rushstack/eslint-plugin': file:../temp/tarballs/rushstack-eslint-plugin-0.12.0.tgz_ucoohk2w7gukx6ccuul7rl7pnq - '@rushstack/eslint-plugin-packlets': file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.7.0.tgz_ucoohk2w7gukx6ccuul7rl7pnq - '@rushstack/eslint-plugin-security': file:../temp/tarballs/rushstack-eslint-plugin-security-0.6.0.tgz_ucoohk2w7gukx6ccuul7rl7pnq - '@typescript-eslint/eslint-plugin': 5.59.7_7yosyjls7ieoemdl24ktrlsrzm - '@typescript-eslint/experimental-utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - '@typescript-eslint/parser': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - eslint: 8.7.0 - eslint-plugin-promise: 6.0.0_eslint@8.7.0 - eslint-plugin-react: 7.27.1_eslint@8.7.0 - eslint-plugin-tsdoc: 0.2.16 - typescript: 5.0.4 - transitivePeerDependencies: - - supports-color - dev: true - - file:../temp/tarballs/rushstack-eslint-config-3.3.0.tgz_valmiib6gbzc7jhcbpocdsabay: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-config-3.3.0.tgz} - id: file:../temp/tarballs/rushstack-eslint-config-3.3.0.tgz - name: '@rushstack/eslint-config' - version: 3.3.0 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '>=4.7.0' - dependencies: - '@rushstack/eslint-patch': file:../temp/tarballs/rushstack-eslint-patch-1.3.0.tgz - '@rushstack/eslint-plugin': file:../temp/tarballs/rushstack-eslint-plugin-0.12.0.tgz_valmiib6gbzc7jhcbpocdsabay - '@rushstack/eslint-plugin-packlets': file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.7.0.tgz_valmiib6gbzc7jhcbpocdsabay - '@rushstack/eslint-plugin-security': file:../temp/tarballs/rushstack-eslint-plugin-security-0.6.0.tgz_valmiib6gbzc7jhcbpocdsabay - '@typescript-eslint/eslint-plugin': 5.59.7_cmopfgbf56lh5wztbaq3kmg4gm - '@typescript-eslint/experimental-utils': 5.59.7_valmiib6gbzc7jhcbpocdsabay - '@typescript-eslint/parser': 5.59.7_valmiib6gbzc7jhcbpocdsabay - '@typescript-eslint/typescript-estree': 5.59.7_typescript@4.7.4 - eslint: 8.7.0 - eslint-plugin-promise: 6.0.0_eslint@8.7.0 - eslint-plugin-react: 7.27.1_eslint@8.7.0 - eslint-plugin-tsdoc: 0.2.16 - typescript: 4.7.4 - transitivePeerDependencies: - - supports-color - dev: true - - file:../temp/tarballs/rushstack-eslint-patch-1.3.0.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-patch-1.3.0.tgz} - name: '@rushstack/eslint-patch' - version: 1.3.0 - dev: true - - file:../temp/tarballs/rushstack-eslint-plugin-0.12.0.tgz_ucoohk2w7gukx6ccuul7rl7pnq: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-0.12.0.tgz} - id: file:../temp/tarballs/rushstack-eslint-plugin-0.12.0.tgz - name: '@rushstack/eslint-plugin' - version: 0.12.0 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@rushstack/tree-pattern': file:../temp/tarballs/rushstack-tree-pattern-0.2.4.tgz - '@typescript-eslint/experimental-utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - eslint: 8.7.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - file:../temp/tarballs/rushstack-eslint-plugin-0.12.0.tgz_valmiib6gbzc7jhcbpocdsabay: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-0.12.0.tgz} - id: file:../temp/tarballs/rushstack-eslint-plugin-0.12.0.tgz - name: '@rushstack/eslint-plugin' - version: 0.12.0 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@rushstack/tree-pattern': file:../temp/tarballs/rushstack-tree-pattern-0.2.4.tgz - '@typescript-eslint/experimental-utils': 5.59.7_valmiib6gbzc7jhcbpocdsabay - eslint: 8.7.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.7.0.tgz_ucoohk2w7gukx6ccuul7rl7pnq: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.7.0.tgz} - id: file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.7.0.tgz - name: '@rushstack/eslint-plugin-packlets' - version: 0.7.0 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@rushstack/tree-pattern': file:../temp/tarballs/rushstack-tree-pattern-0.2.4.tgz - '@typescript-eslint/experimental-utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - eslint: 8.7.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.7.0.tgz_valmiib6gbzc7jhcbpocdsabay: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.7.0.tgz} - id: file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.7.0.tgz - name: '@rushstack/eslint-plugin-packlets' - version: 0.7.0 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@rushstack/tree-pattern': file:../temp/tarballs/rushstack-tree-pattern-0.2.4.tgz - '@typescript-eslint/experimental-utils': 5.59.7_valmiib6gbzc7jhcbpocdsabay - eslint: 8.7.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - file:../temp/tarballs/rushstack-eslint-plugin-security-0.6.0.tgz_ucoohk2w7gukx6ccuul7rl7pnq: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-security-0.6.0.tgz} - id: file:../temp/tarballs/rushstack-eslint-plugin-security-0.6.0.tgz - name: '@rushstack/eslint-plugin-security' - version: 0.6.0 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@rushstack/tree-pattern': file:../temp/tarballs/rushstack-tree-pattern-0.2.4.tgz - '@typescript-eslint/experimental-utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - eslint: 8.7.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - file:../temp/tarballs/rushstack-eslint-plugin-security-0.6.0.tgz_valmiib6gbzc7jhcbpocdsabay: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-security-0.6.0.tgz} - id: file:../temp/tarballs/rushstack-eslint-plugin-security-0.6.0.tgz - name: '@rushstack/eslint-plugin-security' - version: 0.6.0 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@rushstack/tree-pattern': file:../temp/tarballs/rushstack-tree-pattern-0.2.4.tgz - '@typescript-eslint/experimental-utils': 5.59.7_valmiib6gbzc7jhcbpocdsabay - eslint: 8.7.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - file:../temp/tarballs/rushstack-heft-0.50.7.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-heft-0.50.7.tgz} - name: '@rushstack/heft' - version: 0.50.7 - engines: {node: '>=10.13.0'} - hasBin: true - dependencies: - '@rushstack/heft-config-file': file:../temp/tarballs/rushstack-heft-config-file-0.12.3.tgz - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz - '@rushstack/rig-package': file:../temp/tarballs/rushstack-rig-package-0.3.19.tgz - '@rushstack/ts-command-line': file:../temp/tarballs/rushstack-ts-command-line-4.13.3.tgz - '@types/tapable': 1.0.6 - argparse: 1.0.10 - chokidar: 3.4.3 - fast-glob: 3.2.11 - git-repo-info: 2.1.1 - ignore: 5.1.9 - tapable: 1.1.3 - true-case-path: 2.2.1 - watchpack: 2.4.0 - transitivePeerDependencies: - - '@types/node' - dev: true - - file:../temp/tarballs/rushstack-heft-config-file-0.12.3.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-heft-config-file-0.12.3.tgz} - name: '@rushstack/heft-config-file' - version: 0.12.3 - engines: {node: '>=10.13.0'} - dependencies: - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz - '@rushstack/rig-package': file:../temp/tarballs/rushstack-rig-package-0.3.19.tgz - jsonpath-plus: 4.0.0 - transitivePeerDependencies: - - '@types/node' - dev: true - - file:../temp/tarballs/rushstack-heft-config-file-0.12.3.tgz_@types+node@14.18.36: - resolution: {tarball: file:../temp/tarballs/rushstack-heft-config-file-0.12.3.tgz} - id: file:../temp/tarballs/rushstack-heft-config-file-0.12.3.tgz - name: '@rushstack/heft-config-file' - version: 0.12.3 - engines: {node: '>=10.13.0'} - dependencies: - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz_@types+node@14.18.36 - '@rushstack/rig-package': file:../temp/tarballs/rushstack-rig-package-0.3.19.tgz - jsonpath-plus: 4.0.0 - transitivePeerDependencies: - - '@types/node' - - file:../temp/tarballs/rushstack-heft-lint-plugin-0.0.0.tgz_@rushstack+heft@0.50.7: - resolution: {tarball: file:../temp/tarballs/rushstack-heft-lint-plugin-0.0.0.tgz} - id: file:../temp/tarballs/rushstack-heft-lint-plugin-0.0.0.tgz - name: '@rushstack/heft-lint-plugin' - version: 0.0.0 - peerDependencies: - '@rushstack/heft': '*' - dependencies: - '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.50.7.tgz - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz - semver: 7.3.8 - transitivePeerDependencies: - - '@types/node' - dev: true - - file:../temp/tarballs/rushstack-heft-typescript-plugin-0.0.0.tgz_@rushstack+heft@0.50.7: - resolution: {tarball: file:../temp/tarballs/rushstack-heft-typescript-plugin-0.0.0.tgz} - id: file:../temp/tarballs/rushstack-heft-typescript-plugin-0.0.0.tgz - name: '@rushstack/heft-typescript-plugin' - version: 0.0.0 - peerDependencies: - '@rushstack/heft': '*' - dependencies: - '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.50.7.tgz - '@rushstack/heft-config-file': file:../temp/tarballs/rushstack-heft-config-file-0.12.3.tgz - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz - '@types/tapable': 1.0.6 - semver: 7.3.8 - tapable: 1.1.3 - transitivePeerDependencies: - - '@types/node' - dev: true - - file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz} - name: '@rushstack/node-core-library' - version: 3.59.2 - peerDependencies: - '@types/node': '*' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - colors: 1.2.5 - fs-extra: 7.0.1 - import-lazy: 4.0.0 - jju: 1.4.0 - resolve: 1.22.1 - semver: 7.3.8 - z-schema: 5.0.3 - dev: true - - file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz_@types+node@14.18.36: - resolution: {tarball: file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz} - id: file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz - name: '@rushstack/node-core-library' - version: 3.59.2 - peerDependencies: - '@types/node': '*' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@types/node': 14.18.36 - colors: 1.2.5 - fs-extra: 7.0.1 - import-lazy: 4.0.0 - jju: 1.4.0 - resolve: 1.22.1 - semver: 7.3.8 - z-schema: 5.0.3 - - file:../temp/tarballs/rushstack-package-deps-hash-4.0.18.tgz_@types+node@14.18.36: - resolution: {tarball: file:../temp/tarballs/rushstack-package-deps-hash-4.0.18.tgz} - id: file:../temp/tarballs/rushstack-package-deps-hash-4.0.18.tgz - name: '@rushstack/package-deps-hash' - version: 4.0.18 - dependencies: - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz_@types+node@14.18.36 - transitivePeerDependencies: - - '@types/node' - - file:../temp/tarballs/rushstack-package-extractor-0.2.5.tgz_@types+node@14.18.36: - resolution: {tarball: file:../temp/tarballs/rushstack-package-extractor-0.2.5.tgz} - id: file:../temp/tarballs/rushstack-package-extractor-0.2.5.tgz - name: '@rushstack/package-extractor' - version: 0.2.5 - dependencies: - '@pnpm/link-bins': 5.3.25 - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz_@types+node@14.18.36 - '@rushstack/terminal': file:../temp/tarballs/rushstack-terminal-0.5.11.tgz_@types+node@14.18.36 - ignore: 5.1.9 - jszip: 3.8.0 - npm-packlist: 2.1.5 - transitivePeerDependencies: - - '@types/node' - - file:../temp/tarballs/rushstack-rig-package-0.3.19.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-rig-package-0.3.19.tgz} - name: '@rushstack/rig-package' - version: 0.3.19 - dependencies: - resolve: 1.22.1 - strip-json-comments: 3.1.1 - - file:../temp/tarballs/rushstack-rush-sdk-5.98.0.tgz_@types+node@14.18.36: - resolution: {tarball: file:../temp/tarballs/rushstack-rush-sdk-5.98.0.tgz} - id: file:../temp/tarballs/rushstack-rush-sdk-5.98.0.tgz - name: '@rushstack/rush-sdk' - version: 5.98.0 - dependencies: - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz_@types+node@14.18.36 - '@types/node-fetch': 2.6.2 - tapable: 2.2.1 - transitivePeerDependencies: - - '@types/node' - dev: false - - file:../temp/tarballs/rushstack-stream-collator-4.0.236.tgz_@types+node@14.18.36: - resolution: {tarball: file:../temp/tarballs/rushstack-stream-collator-4.0.236.tgz} - id: file:../temp/tarballs/rushstack-stream-collator-4.0.236.tgz - name: '@rushstack/stream-collator' - version: 4.0.236 - dependencies: - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz_@types+node@14.18.36 - '@rushstack/terminal': file:../temp/tarballs/rushstack-terminal-0.5.11.tgz_@types+node@14.18.36 - transitivePeerDependencies: - - '@types/node' - - file:../temp/tarballs/rushstack-terminal-0.5.11.tgz_@types+node@14.18.36: - resolution: {tarball: file:../temp/tarballs/rushstack-terminal-0.5.11.tgz} - id: file:../temp/tarballs/rushstack-terminal-0.5.11.tgz - name: '@rushstack/terminal' - version: 0.5.11 - peerDependencies: - '@types/node': '*' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.59.2.tgz_@types+node@14.18.36 - '@types/node': 14.18.36 - wordwrap: 1.0.0 - - file:../temp/tarballs/rushstack-tree-pattern-0.2.4.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-tree-pattern-0.2.4.tgz} - name: '@rushstack/tree-pattern' - version: 0.2.4 - dev: true - - file:../temp/tarballs/rushstack-ts-command-line-4.13.3.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-ts-command-line-4.13.3.tgz} - name: '@rushstack/ts-command-line' - version: 4.13.3 - dependencies: - '@types/argparse': 1.0.38 - argparse: 1.0.10 - colors: 1.2.5 - string-argv: 0.3.1 diff --git a/build-tests/install-test-workspace/workspace/package.json b/build-tests/install-test-workspace/workspace/package.json deleted file mode 100644 index 168f2dfd560..00000000000 --- a/build-tests/install-test-workspace/workspace/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "pnpm": { - "peerDependencyRules": { - "allowAny": [ - "@rushstack/heft" - ] - } - } -} diff --git a/build-tests/install-test-workspace/workspace/pnpm-workspace.yaml b/build-tests/install-test-workspace/workspace/pnpm-workspace.yaml deleted file mode 100644 index 4eb08f9ecac..00000000000 --- a/build-tests/install-test-workspace/workspace/pnpm-workspace.yaml +++ /dev/null @@ -1,5 +0,0 @@ -packages: - - rush-lib-test - - rush-sdk-test - - typescript-newest-test - - typescript-v4-test diff --git a/build-tests/install-test-workspace/workspace/rush-lib-test/package.json b/build-tests/install-test-workspace/workspace/rush-lib-test/package.json deleted file mode 100644 index 7a61cf98e58..00000000000 --- a/build-tests/install-test-workspace/workspace/rush-lib-test/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "rush-lib-test", - "version": "0.0.0", - "private": true, - "description": "A minimal example project that imports APIs from @rushstack/rush-lib", - "license": "MIT", - "scripts": { - "build": "rimraf ./lib/ && tsc", - "start": "node lib/start.js" - }, - "dependencies": { - "@microsoft/rush-lib": "*", - "colors": "^1.4.0" - }, - "devDependencies": { - "@types/node": "14.18.36", - "rimraf": "^4.1.2", - "typescript": "~5.0.4" - } -} diff --git a/build-tests/install-test-workspace/workspace/rush-lib-test/src/start.ts b/build-tests/install-test-workspace/workspace/rush-lib-test/src/start.ts deleted file mode 100644 index 2e12140f28b..00000000000 --- a/build-tests/install-test-workspace/workspace/rush-lib-test/src/start.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -console.log('rush-lib-test loading Rush configuration...'); - -// Important: Since we're calling an internal API, we need to use the unbundled .d.ts files -// instead of the normal .d.ts rollup -import { RushConfiguration } from '@microsoft/rush-lib/lib/'; - -const config = RushConfiguration.loadFromDefaultLocation(); -console.log(config.commonFolder); - -console.log('Calling an internal API...'); - -// Use a path-based import to access an internal API (do so at your own risk!) -import { VersionMismatchFinder } from '@microsoft/rush-lib/lib/logic/versionMismatch/VersionMismatchFinder'; - -VersionMismatchFinder.ensureConsistentVersions(config); diff --git a/build-tests/install-test-workspace/workspace/rush-lib-test/tsconfig.json b/build-tests/install-test-workspace/workspace/rush-lib-test/tsconfig.json deleted file mode 100644 index d297885ffd9..00000000000 --- a/build-tests/install-test-workspace/workspace/rush-lib-test/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/tsconfig", - - "compilerOptions": { - "outDir": "lib", - - "forceConsistentCasingInFileNames": true, - "jsx": "react", - "declaration": true, - "sourceMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "types": ["node"], - - "module": "commonjs", - "target": "es6", - "lib": ["es5", "es2015.collection", "es2015.iterable", "es2015.promise"] - }, - "include": ["**/*.ts"], - "exclude": ["node_modules", "lib"] -} diff --git a/build-tests/install-test-workspace/workspace/rush-sdk-test/package.json b/build-tests/install-test-workspace/workspace/rush-sdk-test/package.json deleted file mode 100644 index 6b46612c93e..00000000000 --- a/build-tests/install-test-workspace/workspace/rush-sdk-test/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "rush-sdk-test", - "version": "0.0.0", - "private": true, - "description": "A minimal example project that imports APIs from @rushstack/rush-sdk", - "license": "MIT", - "scripts": { - "build": "rimraf ./lib/ && tsc", - "start": "node lib/start.js" - }, - "dependencies": { - "@rushstack/rush-sdk": "*", - "colors": "^1.4.0" - }, - "devDependencies": { - "@microsoft/rush-lib": "*", - "@types/node": "14.18.36", - "typescript": "~5.0.4", - "rimraf": "^4.1.2" - } -} diff --git a/build-tests/install-test-workspace/workspace/rush-sdk-test/src/start.ts b/build-tests/install-test-workspace/workspace/rush-sdk-test/src/start.ts deleted file mode 100644 index 4eafb857e20..00000000000 --- a/build-tests/install-test-workspace/workspace/rush-sdk-test/src/start.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -console.log('rush-sdk-test loading Rush configuration...'); - -// Important: Since we're calling an internal API, we need to use the unbundled .d.ts files -// instead of the normal .d.ts rollup -import { RushConfiguration } from '@rushstack/rush-sdk/lib/'; - -const config = RushConfiguration.loadFromDefaultLocation(); -console.log(config.commonFolder); - -console.log('Calling an internal API...'); - -// Use a path-based import to access an internal API (do so at your own risk!) -import * as GitEmailPolicy from '@rushstack/rush-sdk/lib/logic/policy/GitEmailPolicy'; -console.log(GitEmailPolicy.getEmailExampleLines(config)); diff --git a/build-tests/install-test-workspace/workspace/rush-sdk-test/tsconfig.json b/build-tests/install-test-workspace/workspace/rush-sdk-test/tsconfig.json deleted file mode 100644 index db58331fccd..00000000000 --- a/build-tests/install-test-workspace/workspace/rush-sdk-test/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/tsconfig", - - "compilerOptions": { - "outDir": "lib", - - "forceConsistentCasingInFileNames": true, - "jsx": "react", - "declaration": true, - "sourceMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "types": ["node"], - - "module": "commonjs", - "target": "es6", - "lib": ["es5", "es2015.collection", "es2015.iterable", "es2015.promise"], - "rootDir": "src" - }, - "include": ["src/**/*.ts"] -} diff --git a/build-tests/install-test-workspace/workspace/typescript-newest-test/.eslintrc.js b/build-tests/install-test-workspace/workspace/typescript-newest-test/.eslintrc.js deleted file mode 100644 index 60160b354c4..00000000000 --- a/build-tests/install-test-workspace/workspace/typescript-newest-test/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/node'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/install-test-workspace/workspace/typescript-newest-test/config/heft.json b/build-tests/install-test-workspace/workspace/typescript-newest-test/config/heft.json deleted file mode 100644 index 747fd72a0d8..00000000000 --- a/build-tests/install-test-workspace/workspace/typescript-newest-test/config/heft.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", - - // TODO: Add comments - "phasesByName": { - "build": { - "cleanFiles": [{ "sourcePath": "lib" }, { "sourcePath": "dist" }], - "tasksByName": { - "typescript": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft-typescript-plugin" - } - }, - "lint": { - "taskDependencies": ["typescript"], - "taskPlugin": { - "pluginPackage": "@rushstack/heft-lint-plugin" - } - } - } - } - } -} diff --git a/build-tests/install-test-workspace/workspace/typescript-newest-test/config/rush-project.json b/build-tests/install-test-workspace/workspace/typescript-newest-test/config/rush-project.json deleted file mode 100644 index 247dc17187a..00000000000 --- a/build-tests/install-test-workspace/workspace/typescript-newest-test/config/rush-project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "operationSettings": [ - { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] - } - ] -} diff --git a/build-tests/install-test-workspace/workspace/typescript-newest-test/package.json b/build-tests/install-test-workspace/workspace/typescript-newest-test/package.json deleted file mode 100644 index 776c3eae121..00000000000 --- a/build-tests/install-test-workspace/workspace/typescript-newest-test/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "typescript-newest-test", - "description": "Building this project tests Heft with the newest supported TypeScript compiler version", - "version": "1.0.0", - "private": true, - "main": "lib/index.js", - "license": "MIT", - "scripts": { - "build": "heft build --clean --clean-cache" - }, - "devDependencies": { - "@rushstack/eslint-config": "*", - "@rushstack/heft": "*", - "@rushstack/heft-lint-plugin": "*", - "@rushstack/heft-typescript-plugin": "*", - "typescript": "~5.0.4", - "tslint": "~5.20.1", - "eslint": "~8.7.0" - } -} diff --git a/build-tests/install-test-workspace/workspace/typescript-newest-test/src/index.ts b/build-tests/install-test-workspace/workspace/typescript-newest-test/src/index.ts deleted file mode 100644 index 15a2bae17e3..00000000000 --- a/build-tests/install-test-workspace/workspace/typescript-newest-test/src/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -/** - * @public - */ -export class TestClass {} // tslint:disable-line:export-name diff --git a/build-tests/install-test-workspace/workspace/typescript-newest-test/tsconfig.json b/build-tests/install-test-workspace/workspace/typescript-newest-test/tsconfig.json deleted file mode 100644 index 082d42dab84..00000000000 --- a/build-tests/install-test-workspace/workspace/typescript-newest-test/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "outDir": "lib", - "rootDir": "src", - - "module": "commonjs", - "target": "es2017", - "lib": ["es2017"], - - "forceConsistentCasingInFileNames": true, - "jsx": "react", - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "noUnusedLocals": true, - "types": [] - }, - - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] -} diff --git a/build-tests/install-test-workspace/workspace/typescript-v4-test/.eslintrc.js b/build-tests/install-test-workspace/workspace/typescript-v4-test/.eslintrc.js deleted file mode 100644 index 60160b354c4..00000000000 --- a/build-tests/install-test-workspace/workspace/typescript-v4-test/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/node'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/install-test-workspace/workspace/typescript-v4-test/config/heft.json b/build-tests/install-test-workspace/workspace/typescript-v4-test/config/heft.json deleted file mode 100644 index 8631ceb24e4..00000000000 --- a/build-tests/install-test-workspace/workspace/typescript-v4-test/config/heft.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", - - // TODO: Add comments - "phasesByName": { - "build": { - "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }, { "sourcePath": "temp" }], - "tasksByName": { - "typescript": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft-typescript-plugin" - } - }, - "lint": { - "taskDependencies": ["typescript"], - "taskPlugin": { - "pluginPackage": "@rushstack/heft-lint-plugin" - } - } - } - } - } -} diff --git a/build-tests/install-test-workspace/workspace/typescript-v4-test/config/rush-project.json b/build-tests/install-test-workspace/workspace/typescript-v4-test/config/rush-project.json deleted file mode 100644 index 247dc17187a..00000000000 --- a/build-tests/install-test-workspace/workspace/typescript-v4-test/config/rush-project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "operationSettings": [ - { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] - } - ] -} diff --git a/build-tests/install-test-workspace/workspace/typescript-v4-test/package.json b/build-tests/install-test-workspace/workspace/typescript-v4-test/package.json deleted file mode 100644 index 6d851f87386..00000000000 --- a/build-tests/install-test-workspace/workspace/typescript-v4-test/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "typescript-v4-test", - "description": "Building this project tests Heft with TypeScript v4", - "version": "1.0.0", - "private": true, - "main": "lib/index.js", - "license": "MIT", - "scripts": { - "build": "heft build --clean --clean-cache" - }, - "devDependencies": { - "@rushstack/eslint-config": "*", - "@rushstack/heft": "*", - "@rushstack/heft-lint-plugin": "*", - "@rushstack/heft-typescript-plugin": "*", - "typescript": "~4.7.0", - "tslint": "~5.20.1", - "eslint": "~8.7.0" - } -} diff --git a/build-tests/install-test-workspace/workspace/typescript-v4-test/src/index.ts b/build-tests/install-test-workspace/workspace/typescript-v4-test/src/index.ts deleted file mode 100644 index 15a2bae17e3..00000000000 --- a/build-tests/install-test-workspace/workspace/typescript-v4-test/src/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -/** - * @public - */ -export class TestClass {} // tslint:disable-line:export-name diff --git a/build-tests/install-test-workspace/workspace/typescript-v4-test/tsconfig.json b/build-tests/install-test-workspace/workspace/typescript-v4-test/tsconfig.json deleted file mode 100644 index 082d42dab84..00000000000 --- a/build-tests/install-test-workspace/workspace/typescript-v4-test/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "outDir": "lib", - "rootDir": "src", - - "module": "commonjs", - "target": "es2017", - "lib": ["es2017"], - - "forceConsistentCasingInFileNames": true, - "jsx": "react", - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "noUnusedLocals": true, - "types": [] - }, - - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] -} diff --git a/build-tests/install-test-workspace/workspace/typescript-v4-test/tslint.json b/build-tests/install-test-workspace/workspace/typescript-v4-test/tslint.json deleted file mode 100644 index 56dfd9f2bb6..00000000000 --- a/build-tests/install-test-workspace/workspace/typescript-v4-test/tslint.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/tslint", - - "rules": { - "class-name": true, - "comment-format": [true, "check-space"], - "curly": true, - "eofline": false, - "forin": true, - "indent": [true, "spaces", 2], - "interface-name": true, - "label-position": true, - "max-line-length": [true, 120], - "member-access": true, - "member-ordering": [ - true, - { - "order": [ - "public-static-field", - "protected-static-field", - "private-static-field", - "public-instance-field", - "protected-instance-field", - "private-instance-field", - "public-static-method", - "protected-static-method", - "private-static-method", - "public-constructor", - "public-instance-method", - "protected-constructor", - "protected-instance-method", - "private-constructor", - "private-instance-method" - ] - } - ], - "no-arg": true, - "no-any": true, - "no-bitwise": true, - "no-consecutive-blank-lines": true, - "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], - "no-construct": true, - "no-debugger": true, - "no-duplicate-switch-case": true, - "no-duplicate-variable": true, - "no-empty": true, - "no-eval": true, - "no-floating-promises": true, - "no-inferrable-types": false, - "no-internal-module": true, - "no-null-keyword": true, - "no-shadowed-variable": true, - "no-string-literal": true, - "no-switch-case-fall-through": true, - "no-trailing-whitespace": true, - "no-unused-expression": true, - "no-var-keyword": true, - "object-literal-sort-keys": false, - "one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"], - "quotemark": [true, "single", "avoid-escape"], - "prefer-const": true, - "radix": true, - "semicolon": true, - "trailing-comma": [ - true, - { - "multiline": "never", - "singleline": "never" - } - ], - "triple-equals": [true, "allow-null-check"], - "typedef": [ - true, - "call-signature", - "parameter", - "property-declaration", - "variable-declaration", - "member-variable-declaration" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - } - ], - "use-isnan": true, - "variable-name": [true, "check-format", "allow-leading-underscore", "ban-keywords"], - "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"] - } -} diff --git a/build-tests/localization-plugin-test-01/build.js b/build-tests/localization-plugin-test-01/build.js deleted file mode 100644 index eef1c23c1a7..00000000000 --- a/build-tests/localization-plugin-test-01/build.js +++ /dev/null @@ -1,21 +0,0 @@ -const { FileSystem } = require('@rushstack/node-core-library'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -FileSystem.ensureEmptyFolder('dist-dev'); -FileSystem.ensureEmptyFolder('dist-prod'); -FileSystem.ensureEmptyFolder('lib'); -FileSystem.ensureEmptyFolder('temp'); - -// Run Webpack -executeCommand('node node_modules/webpack-cli/bin/cli'); - -console.log(`==> Finished build.js for ${path.basename(process.cwd())}`); diff --git a/build-tests/localization-plugin-test-01/config/heft.json b/build-tests/localization-plugin-test-01/config/heft.json new file mode 100644 index 00000000000..4d81cfade08 --- /dev/null +++ b/build-tests/localization-plugin-test-01/config/heft.json @@ -0,0 +1,29 @@ +/** + * Defines configuration used by core Heft. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + /** + * Optionally specifies another JSON config file that this file extends from. This provides a way for standard + * settings to be shared across multiple projects. + * + * To delete an inherited setting, set it to `null` in this file. + */ + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "cleanFiles": [{ "includeGlobs": ["dist-dev", "dist-prod", "lib", "temp"] }], + + "tasksByName": { + "webpack": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-webpack4-plugin" + } + } + } + } + } +} diff --git a/build-tests/localization-plugin-test-01/config/rig.json b/build-tests/localization-plugin-test-01/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/localization-plugin-test-01/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/localization-plugin-test-01/config/rush-project.json b/build-tests/localization-plugin-test-01/config/rush-project.json index 247dc17187a..f5d98263c8a 100644 --- a/build-tests/localization-plugin-test-01/config/rush-project.json +++ b/build-tests/localization-plugin-test-01/config/rush-project.json @@ -1,8 +1,12 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "extends": "local-node-rig/profiles/default/config/rush-project.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + "outputFolderNames": ["dist-dev", "dist-prod"] } ] } diff --git a/build-tests/localization-plugin-test-01/eslint.config.js b/build-tests/localization-plugin-test-01/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/build-tests/localization-plugin-test-01/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/localization-plugin-test-01/package.json b/build-tests/localization-plugin-test-01/package.json index 12c1d14dcaa..0543b91625f 100644 --- a/build-tests/localization-plugin-test-01/package.json +++ b/build-tests/localization-plugin-test-01/package.json @@ -4,22 +4,37 @@ "version": "0.1.0", "private": true, "scripts": { - "build": "node build.js", - "serve": "node serve.js", - "_phase:build": "node build.js" + "build": "heft build --clean", + "serve": "heft start", + "_phase:build": "heft run --only build -- --clean" }, "dependencies": { + "@rushstack/heft": "workspace:*", + "@rushstack/heft-webpack4-plugin": "workspace:*", + "@rushstack/set-webpack-public-path-plugin": "^4.1.16", "@rushstack/webpack4-localization-plugin": "workspace:*", "@rushstack/webpack4-module-minifier-plugin": "workspace:*", - "@rushstack/node-core-library": "workspace:*", - "@rushstack/set-webpack-public-path-plugin": "workspace:*", - "@types/webpack-env": "1.18.0", + "@types/webpack-env": "1.18.8", + "eslint": "~9.37.0", "html-webpack-plugin": "~4.5.2", - "ts-loader": "6.0.0", - "typescript": "~5.0.4", - "webpack": "~4.44.2", + "local-node-rig": "workspace:*", + "webpack": "~4.47.0", "webpack-bundle-analyzer": "~4.5.0", - "webpack-cli": "~3.3.2", "webpack-dev-server": "~4.9.3" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } } } diff --git a/build-tests/localization-plugin-test-01/serve.js b/build-tests/localization-plugin-test-01/serve.js deleted file mode 100644 index ffa5c333f00..00000000000 --- a/build-tests/localization-plugin-test-01/serve.js +++ /dev/null @@ -1,18 +0,0 @@ -const { FileSystem } = require('@rushstack/node-core-library'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -FileSystem.ensureEmptyFolder('dist'); -FileSystem.ensureEmptyFolder('lib'); -FileSystem.ensureEmptyFolder('temp'); - -// Run Webpack -executeCommand('node node_modules/webpack-dev-server/bin/webpack-dev-server'); diff --git a/build-tests/localization-plugin-test-01/src/chunks/chunkWithoutStrings.ts b/build-tests/localization-plugin-test-01/src/chunks/chunkWithoutStrings.ts index 5e19d5f1060..0113e165b98 100644 --- a/build-tests/localization-plugin-test-01/src/chunks/chunkWithoutStrings.ts +++ b/build-tests/localization-plugin-test-01/src/chunks/chunkWithoutStrings.ts @@ -1,5 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + export class ChunkWithoutStringsClass { public doStuff(): void { + // eslint-disable-next-line no-console console.log('STATIC STRING'); } } diff --git a/build-tests/localization-plugin-test-01/src/indexA.ts b/build-tests/localization-plugin-test-01/src/indexA.ts index c30ebb90d7b..5aaba8afbcd 100644 --- a/build-tests/localization-plugin-test-01/src/indexA.ts +++ b/build-tests/localization-plugin-test-01/src/indexA.ts @@ -1,6 +1,14 @@ -import(/* webpackChunkName: 'chunk-without-strings' */ './chunks/chunkWithoutStrings').then( - ({ ChunkWithoutStringsClass }) => { - const chunk = new ChunkWithoutStringsClass(); +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import(/* webpackChunkName: 'chunk-without-strings' */ './chunks/chunkWithoutStrings') + // eslint-disable-next-line @typescript-eslint/naming-convention + .then(({ ChunkWithoutStringsClass }) => { + const chunk: import('./chunks/chunkWithoutStrings').ChunkWithoutStringsClass = + new ChunkWithoutStringsClass(); chunk.doStuff(); - } -); + }) + .catch((error) => { + // eslint-disable-next-line no-console + console.log(error); + }); diff --git a/build-tests/localization-plugin-test-01/src/indexB.ts b/build-tests/localization-plugin-test-01/src/indexB.ts index 16401835981..2bcd3820a4b 100644 --- a/build-tests/localization-plugin-test-01/src/indexB.ts +++ b/build-tests/localization-plugin-test-01/src/indexB.ts @@ -1 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// eslint-disable-next-line no-console console.log('dostuff'); diff --git a/build-tests/localization-plugin-test-01/tsconfig.json b/build-tests/localization-plugin-test-01/tsconfig.json index 60caf3a15ab..27340e2d2e3 100644 --- a/build-tests/localization-plugin-test-01/tsconfig.json +++ b/build-tests/localization-plugin-test-01/tsconfig.json @@ -1,23 +1,7 @@ { + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "declaration": true, - "declarationMap": true, - "experimentalDecorators": true, - "forceConsistentCasingInFileNames": true, - "inlineSources": true, - "jsx": "react", - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "module": "esnext", "moduleResolution": "node", - "noUnusedLocals": true, - "sourceMap": true, - "strictNullChecks": true, - "target": "es5", - "types": ["webpack-env"], - - "outDir": "lib", - "rootDir": "src", "rootDirs": ["src", "temp/loc-json-ts"] - }, - "include": ["src/**/*.ts", "src/**/*.tsx"] + } } diff --git a/build-tests/localization-plugin-test-01/webpack.config.js b/build-tests/localization-plugin-test-01/webpack.config.js index b6e34c305c6..09db14220ae 100644 --- a/build-tests/localization-plugin-test-01/webpack.config.js +++ b/build-tests/localization-plugin-test-01/webpack.config.js @@ -1,40 +1,20 @@ 'use strict'; -const path = require('path'); -const webpack = require('webpack'); - const { LocalizationPlugin } = require('@rushstack/webpack4-localization-plugin'); const { ModuleMinifierPlugin, LocalMinifier } = require('@rushstack/webpack4-module-minifier-plugin'); const { SetPublicPathPlugin } = require('@rushstack/set-webpack-public-path-plugin'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const HtmlWebpackPlugin = require('html-webpack-plugin'); -function generateConfiguration(mode, outputFolderName) { +function generateConfiguration(mode, outputFolderName, webpack) { return { - mode: mode, - module: { - rules: [ - { - test: /\.tsx?$/, - loader: require.resolve('ts-loader'), - exclude: /(node_modules)/, - options: { - compiler: require.resolve('typescript'), - logLevel: 'ERROR', - configFile: path.resolve(__dirname, 'tsconfig.json') - } - } - ] - }, - resolve: { - extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'] - }, + mode, entry: { - 'localization-test-A': path.join(__dirname, 'src', 'indexA.ts'), - 'localization-test-B': path.join(__dirname, 'src', 'indexB.ts') + 'localization-test-A': `${__dirname}/lib-esm/indexA.js`, + 'localization-test-B': `${__dirname}/lib-esm/indexB.js` }, output: { - path: path.join(__dirname, outputFolderName), + path: `${__dirname}/${outputFolderName}`, filename: '[name]_[locale]_[contenthash].js', chunkFilename: '[id].[name]_[locale]_[contenthash].js' }, @@ -57,19 +37,19 @@ function generateConfiguration(mode, outputFolderName) { } }, typingsOptions: { - generatedTsFolder: path.resolve(__dirname, 'temp', 'loc-json-ts'), - sourceRoot: path.resolve(__dirname, 'src') + generatedTsFolder: `${__dirname}/temp/loc-json-ts`, + sourceRoot: `${__dirname}/src` }, localizationStats: { - dropPath: path.resolve(__dirname, 'temp', 'localization-stats.json') + dropPath: `${__dirname}/temp/localization-stats.json` } }), new BundleAnalyzerPlugin({ openAnalyzer: false, analyzerMode: 'static', - reportFilename: path.resolve(__dirname, 'temp', 'stats.html'), + reportFilename: `${__dirname}/temp/stats.html`, generateStatsFile: true, - statsFilename: path.resolve(__dirname, 'temp', 'stats.json'), + statsFilename: `${__dirname}/temp/stats.json`, logLevel: 'error' }), new SetPublicPathPlugin({ @@ -82,7 +62,7 @@ function generateConfiguration(mode, outputFolderName) { }; } -module.exports = [ - generateConfiguration('development', 'dist-dev'), - generateConfiguration('production', 'dist-prod') +module.exports = ({ webpack }) => [ + generateConfiguration('development', 'dist-dev', webpack), + generateConfiguration('production', 'dist-prod', webpack) ]; diff --git a/build-tests/localization-plugin-test-02/build.js b/build-tests/localization-plugin-test-02/build.js deleted file mode 100644 index eef1c23c1a7..00000000000 --- a/build-tests/localization-plugin-test-02/build.js +++ /dev/null @@ -1,21 +0,0 @@ -const { FileSystem } = require('@rushstack/node-core-library'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -FileSystem.ensureEmptyFolder('dist-dev'); -FileSystem.ensureEmptyFolder('dist-prod'); -FileSystem.ensureEmptyFolder('lib'); -FileSystem.ensureEmptyFolder('temp'); - -// Run Webpack -executeCommand('node node_modules/webpack-cli/bin/cli'); - -console.log(`==> Finished build.js for ${path.basename(process.cwd())}`); diff --git a/build-tests/localization-plugin-test-02/config/heft.json b/build-tests/localization-plugin-test-02/config/heft.json new file mode 100644 index 00000000000..6d560f084ee --- /dev/null +++ b/build-tests/localization-plugin-test-02/config/heft.json @@ -0,0 +1,47 @@ +/** + * Defines configuration used by core Heft. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + /** + * Optionally specifies another JSON config file that this file extends from. This provides a way for standard + * settings to be shared across multiple projects. + * + * To delete an inherited setting, set it to `null` in this file. + */ + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "cleanFiles": [{ "includeGlobs": ["dist-dev", "dist-prod", "lib", "temp"] }], + + "tasksByName": { + "loc-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-localization-typings-plugin", + "options": { + "generatedTsFolder": "temp/loc-json-ts", + "exportAsDefault": { + "interfaceDocumentationComment": "This interface represents a JSON object that has been loaded from a localization file.", + "valueDocumentationComment": "@public", + "inferInterfaceNameFromFilename": true + }, + "stringNamesToIgnore": ["__IGNORED_STRING__"], + "trimmedJsonOutputFolders": ["temp/loc-raw"] + } + } + }, + "typescript": { + "taskDependencies": ["loc-typings"] + }, + "webpack": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-webpack4-plugin" + } + } + } + } + } +} diff --git a/build-tests/localization-plugin-test-02/config/rig.json b/build-tests/localization-plugin-test-02/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/localization-plugin-test-02/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/localization-plugin-test-02/config/rush-project.json b/build-tests/localization-plugin-test-02/config/rush-project.json index 247dc17187a..f5d98263c8a 100644 --- a/build-tests/localization-plugin-test-02/config/rush-project.json +++ b/build-tests/localization-plugin-test-02/config/rush-project.json @@ -1,8 +1,12 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "extends": "local-node-rig/profiles/default/config/rush-project.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + "outputFolderNames": ["dist-dev", "dist-prod"] } ] } diff --git a/build-tests/localization-plugin-test-02/config/typescript.json b/build-tests/localization-plugin-test-02/config/typescript.json new file mode 100644 index 00000000000..a97ea7a3a75 --- /dev/null +++ b/build-tests/localization-plugin-test-02/config/typescript.json @@ -0,0 +1,12 @@ +/** + * Configures the TypeScript plugin for Heft. This plugin also manages linting. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", + + "extends": "local-node-rig/profiles/default/config/typescript.json", + + "staticAssetsToCopy": { + "fileExtensions": [".resx", ".json", ".resjson"] + } +} diff --git a/build-tests/localization-plugin-test-02/eslint.config.js b/build-tests/localization-plugin-test-02/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/build-tests/localization-plugin-test-02/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/localization-plugin-test-02/package.json b/build-tests/localization-plugin-test-02/package.json index 408cf58b866..5fdc6e99a5d 100644 --- a/build-tests/localization-plugin-test-02/package.json +++ b/build-tests/localization-plugin-test-02/package.json @@ -4,24 +4,38 @@ "version": "0.1.0", "private": true, "scripts": { - "build": "node build.js", - "serve": "node serve.js", - "_phase:build": "node build.js" + "build": "heft build --clean", + "start": "heft start", + "_phase:build": "heft run --only build -- --clean" }, "dependencies": { + "@rushstack/heft": "workspace:*", + "@rushstack/heft-localization-typings-plugin": "workspace:*", + "@rushstack/heft-webpack4-plugin": "workspace:*", + "@rushstack/set-webpack-public-path-plugin": "^4.1.16", "@rushstack/webpack4-localization-plugin": "workspace:*", "@rushstack/webpack4-module-minifier-plugin": "workspace:*", - "@rushstack/node-core-library": "workspace:*", - "@rushstack/set-webpack-public-path-plugin": "workspace:*", - "@types/lodash": "4.14.116", - "@types/webpack-env": "1.18.0", + "@types/webpack-env": "1.18.8", + "eslint": "~9.37.0", "html-webpack-plugin": "~4.5.2", - "lodash": "~4.17.15", - "ts-loader": "6.0.0", - "typescript": "~5.0.4", + "local-node-rig": "workspace:*", + "webpack": "~4.47.0", "webpack-bundle-analyzer": "~4.5.0", - "webpack-cli": "~3.3.2", - "webpack-dev-server": "~4.9.3", - "webpack": "~4.44.2" + "webpack-dev-server": "~4.9.3" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } } } diff --git a/build-tests/localization-plugin-test-02/serve.js b/build-tests/localization-plugin-test-02/serve.js deleted file mode 100644 index ffa5c333f00..00000000000 --- a/build-tests/localization-plugin-test-02/serve.js +++ /dev/null @@ -1,18 +0,0 @@ -const { FileSystem } = require('@rushstack/node-core-library'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -FileSystem.ensureEmptyFolder('dist'); -FileSystem.ensureEmptyFolder('lib'); -FileSystem.ensureEmptyFolder('temp'); - -// Run Webpack -executeCommand('node node_modules/webpack-dev-server/bin/webpack-dev-server'); diff --git a/build-tests/localization-plugin-test-02/src/chunks/chunkWithStrings.ts b/build-tests/localization-plugin-test-02/src/chunks/chunkWithStrings.ts index 87c09c23463..4b343b62cf3 100644 --- a/build-tests/localization-plugin-test-02/src/chunks/chunkWithStrings.ts +++ b/build-tests/localization-plugin-test-02/src/chunks/chunkWithStrings.ts @@ -1,9 +1,18 @@ -import * as lodash from 'lodash'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. -import * as strings from './strings2.loc.json'; +import strings from './strings2.loc.json'; + +function htmlEscape(str: string): string { + return str.replace( + /[&<>"']/g, + (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c] ?? c + ); +} export class ChunkWithStringsClass { public doStuff(): void { - console.log(lodash.escape(strings.string1)); + // eslint-disable-next-line no-console + console.log(htmlEscape(strings.string1)); } } diff --git a/build-tests/localization-plugin-test-02/src/chunks/chunkWithoutStrings.ts b/build-tests/localization-plugin-test-02/src/chunks/chunkWithoutStrings.ts index f9467f5c909..1f62f978da9 100644 --- a/build-tests/localization-plugin-test-02/src/chunks/chunkWithoutStrings.ts +++ b/build-tests/localization-plugin-test-02/src/chunks/chunkWithoutStrings.ts @@ -1,7 +1,16 @@ -import * as lodash from 'lodash'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +function htmlEscape(str: string): string { + return str.replace( + /[&<>"']/g, + (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c] ?? c + ); +} export class ChunkWithoutStringsClass { public doStuff(): void { - console.log(lodash.escape('STATIC STRING')); + // eslint-disable-next-line no-console + console.log(htmlEscape('STATIC STRING')); } } diff --git a/build-tests/localization-plugin-test-02/src/indexA.ts b/build-tests/localization-plugin-test-02/src/indexA.ts index 56097124821..65816657a49 100644 --- a/build-tests/localization-plugin-test-02/src/indexA.ts +++ b/build-tests/localization-plugin-test-02/src/indexA.ts @@ -1,9 +1,14 @@ -import { string1 } from './strings1.loc.json'; -import * as strings3 from './strings3.loc.json'; -import * as strings5 from './strings5.resx'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. -console.log(string1); +import strings1 from './strings1.loc.json'; +import strings3 from './strings3.resjson'; +import strings5 from './strings5.resx'; +// eslint-disable-next-line no-console +console.log(strings1.string1); + +// eslint-disable-next-line no-console console.log(strings3.string2); /*! Preserved comment */ //@preserve Another comment @@ -14,25 +19,42 @@ console.log(strings3.string2); * @lic Blah */ -import(/* webpackChunkName: 'chunk-with-strings' */ './chunks/chunkWithStrings').then( - ({ ChunkWithStringsClass }) => { - const chunk = new ChunkWithStringsClass(); +import(/* webpackChunkName: 'chunk-with-strings' */ './chunks/chunkWithStrings') + // eslint-disable-next-line @typescript-eslint/naming-convention + .then(({ ChunkWithStringsClass }) => { + const chunk: import('./chunks/chunkWithStrings').ChunkWithStringsClass = new ChunkWithStringsClass(); chunk.doStuff(); - } -); + }) + .catch((e) => { + // eslint-disable-next-line no-console + console.error(e); + }); -import(/* webpackChunkName: 'chunk-without-strings' */ './chunks/chunkWithoutStrings').then( - ({ ChunkWithoutStringsClass }) => { - const chunk = new ChunkWithoutStringsClass(); +import(/* webpackChunkName: 'chunk-without-strings' */ './chunks/chunkWithoutStrings') + // eslint-disable-next-line @typescript-eslint/naming-convention + .then(({ ChunkWithoutStringsClass }) => { + const chunk: import('./chunks/chunkWithoutStrings').ChunkWithoutStringsClass = + new ChunkWithoutStringsClass(); chunk.doStuff(); - } -); + }) + .catch((e) => { + // eslint-disable-next-line no-console + console.error(e); + }); // @ts-expect-error -import('non-existent').then(() => { - // Do nothing. -}); +import('non-existent') + .then(() => { + // Do nothing. + }) + .catch((e) => { + // eslint-disable-next-line no-console + console.error(e); + }); +// eslint-disable-next-line no-console console.log(strings5.string1); +// eslint-disable-next-line no-console console.log(strings5.stringWithQuotes); +// eslint-disable-next-line no-console console.log(strings5.stringWithTabsAndNewlines); diff --git a/build-tests/localization-plugin-test-02/src/indexB.ts b/build-tests/localization-plugin-test-02/src/indexB.ts index ee1d8396f4a..7eb6c75c7e8 100644 --- a/build-tests/localization-plugin-test-02/src/indexB.ts +++ b/build-tests/localization-plugin-test-02/src/indexB.ts @@ -1,7 +1,14 @@ -import { string1, string2 } from './strings3.loc.json'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import strings3 from './strings3.resjson'; + const strings4: string = require('./strings4.loc.json'); -console.log(string1); -console.log(string2); +// eslint-disable-next-line no-console +console.log(strings3.string1); +// eslint-disable-next-line no-console +console.log(strings3.string2); +// eslint-disable-next-line no-console console.log(strings4); diff --git a/build-tests/localization-plugin-test-02/src/indexC.ts b/build-tests/localization-plugin-test-02/src/indexC.ts index c30ebb90d7b..012f244687c 100644 --- a/build-tests/localization-plugin-test-02/src/indexC.ts +++ b/build-tests/localization-plugin-test-02/src/indexC.ts @@ -1,6 +1,14 @@ -import(/* webpackChunkName: 'chunk-without-strings' */ './chunks/chunkWithoutStrings').then( - ({ ChunkWithoutStringsClass }) => { - const chunk = new ChunkWithoutStringsClass(); +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import(/* webpackChunkName: 'chunk-without-strings' */ './chunks/chunkWithoutStrings') + // eslint-disable-next-line @typescript-eslint/naming-convention + .then(({ ChunkWithoutStringsClass }) => { + const chunk: import('./chunks/chunkWithoutStrings').ChunkWithoutStringsClass = + new ChunkWithoutStringsClass(); chunk.doStuff(); - } -); + }) + .catch((e) => { + // eslint-disable-next-line no-console + console.error(e); + }); diff --git a/build-tests/localization-plugin-test-02/src/strings3.loc.json b/build-tests/localization-plugin-test-02/src/strings3.loc.json deleted file mode 100644 index c85ab94c731..00000000000 --- a/build-tests/localization-plugin-test-02/src/strings3.loc.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "string1": { - "value": "string three with a \\ backslash", - "comment": "the third string" - }, - "string2": { - "value": "string four with an ' apostrophe", - "comment": "the fourth string" - }, - "string3": { - "value": "UNUSED STRING", - "comment": "UNUSED STRING" - } -} diff --git a/build-tests/localization-plugin-test-02/src/strings3.resjson b/build-tests/localization-plugin-test-02/src/strings3.resjson new file mode 100644 index 00000000000..b6a221cc406 --- /dev/null +++ b/build-tests/localization-plugin-test-02/src/strings3.resjson @@ -0,0 +1,10 @@ +{ + "string1": "string three with a \\ backslash", + "_string1.comment": "the third string", + + "string2": "string four with an ' apostrophe", + "_string2.comment": "the fourth string", + + "string3": "UNUSED STRING", + "_string3.comment": "UNUSED STRING" +} diff --git a/build-tests/localization-plugin-test-02/tsconfig.json b/build-tests/localization-plugin-test-02/tsconfig.json index 60caf3a15ab..27340e2d2e3 100644 --- a/build-tests/localization-plugin-test-02/tsconfig.json +++ b/build-tests/localization-plugin-test-02/tsconfig.json @@ -1,23 +1,7 @@ { + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "declaration": true, - "declarationMap": true, - "experimentalDecorators": true, - "forceConsistentCasingInFileNames": true, - "inlineSources": true, - "jsx": "react", - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "module": "esnext", "moduleResolution": "node", - "noUnusedLocals": true, - "sourceMap": true, - "strictNullChecks": true, - "target": "es5", - "types": ["webpack-env"], - - "outDir": "lib", - "rootDir": "src", "rootDirs": ["src", "temp/loc-json-ts"] - }, - "include": ["src/**/*.ts", "src/**/*.tsx"] + } } diff --git a/build-tests/localization-plugin-test-02/webpack.config.js b/build-tests/localization-plugin-test-02/webpack.config.js index 421cb97712e..dc3c7a6cabb 100644 --- a/build-tests/localization-plugin-test-02/webpack.config.js +++ b/build-tests/localization-plugin-test-02/webpack.config.js @@ -1,41 +1,21 @@ 'use strict'; -const path = require('path'); -const webpack = require('webpack'); - const { LocalizationPlugin } = require('@rushstack/webpack4-localization-plugin'); const { ModuleMinifierPlugin, WorkerPoolMinifier } = require('@rushstack/webpack4-module-minifier-plugin'); const { SetPublicPathPlugin } = require('@rushstack/set-webpack-public-path-plugin'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const HtmlWebpackPlugin = require('html-webpack-plugin'); -function generateConfiguration(mode, outputFolderName) { +function generateConfiguration(mode, outputFolderName, webpack) { return { mode: mode, - module: { - rules: [ - { - test: /\.tsx?$/, - loader: require.resolve('ts-loader'), - exclude: /(node_modules)/, - options: { - compiler: require.resolve('typescript'), - logLevel: 'ERROR', - configFile: path.resolve(__dirname, 'tsconfig.json') - } - } - ] - }, - resolve: { - extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'] - }, entry: { - 'localization-test-A': path.join(__dirname, 'src', 'indexA.ts'), - 'localization-test-B': path.join(__dirname, 'src', 'indexB.ts'), - 'localization-test-C': path.join(__dirname, 'src', 'indexC.ts') + 'localization-test-A': `${__dirname}/lib-esm/indexA.js`, + 'localization-test-B': `${__dirname}/lib-esm/indexB.js`, + 'localization-test-C': `${__dirname}/lib-esm/indexC.js` }, output: { - path: path.join(__dirname, outputFolderName), + path: `${__dirname}/${outputFolderName}`, filename: '[name]_[locale]_[contenthash].js', chunkFilename: '[id].[name]_[locale]_[contenthash].js' }, @@ -84,22 +64,17 @@ function generateConfiguration(mode, outputFolderName) { normalizeResxNewlines: 'crlf', ignoreMissingResxComments: true }, - typingsOptions: { - generatedTsFolder: path.resolve(__dirname, 'temp', 'loc-json-ts'), - sourceRoot: path.resolve(__dirname, 'src'), - processComment: (comment) => (comment ? `${comment} (processed)` : comment) - }, localizationStats: { - dropPath: path.resolve(__dirname, 'temp', 'localization-stats.json') + dropPath: `${__dirname}/temp/localization-stats.json` }, ignoreString: (filePath, stringName) => stringName === '__IGNORED_STRING__' }), new BundleAnalyzerPlugin({ openAnalyzer: false, analyzerMode: 'static', - reportFilename: path.resolve(__dirname, 'temp', 'stats.html'), + reportFilename: `${__dirname}/temp/stats.html`, generateStatsFile: true, - statsFilename: path.resolve(__dirname, 'temp', 'stats.json'), + statsFilename: `${__dirname}/temp/stats.json`, logLevel: 'error' }), new SetPublicPathPlugin({ @@ -112,7 +87,7 @@ function generateConfiguration(mode, outputFolderName) { }; } -module.exports = [ - generateConfiguration('development', 'dist-dev'), - generateConfiguration('production', 'dist-prod') +module.exports = ({ webpack }) => [ + generateConfiguration('development', 'dist-dev', webpack), + generateConfiguration('production', 'dist-prod', webpack) ]; diff --git a/build-tests/localization-plugin-test-03/build.js b/build-tests/localization-plugin-test-03/build.js deleted file mode 100644 index eef1c23c1a7..00000000000 --- a/build-tests/localization-plugin-test-03/build.js +++ /dev/null @@ -1,21 +0,0 @@ -const { FileSystem } = require('@rushstack/node-core-library'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -FileSystem.ensureEmptyFolder('dist-dev'); -FileSystem.ensureEmptyFolder('dist-prod'); -FileSystem.ensureEmptyFolder('lib'); -FileSystem.ensureEmptyFolder('temp'); - -// Run Webpack -executeCommand('node node_modules/webpack-cli/bin/cli'); - -console.log(`==> Finished build.js for ${path.basename(process.cwd())}`); diff --git a/build-tests/localization-plugin-test-03/config/heft.json b/build-tests/localization-plugin-test-03/config/heft.json new file mode 100644 index 00000000000..215e7ec40ca --- /dev/null +++ b/build-tests/localization-plugin-test-03/config/heft.json @@ -0,0 +1,27 @@ +/** + * Defines configuration used by core Heft. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "cleanFiles": [{ "includeGlobs": ["dist-dev", "dist-prod", "lib", "temp"] }], + + "tasksByName": { + "webpack": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-webpack4-plugin" + } + }, + + "typescript": { + // The webpack task generates some typings + "taskDependencies": ["webpack"] + } + } + } + } +} diff --git a/build-tests/localization-plugin-test-03/config/rig.json b/build-tests/localization-plugin-test-03/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/localization-plugin-test-03/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/localization-plugin-test-03/config/rush-project.json b/build-tests/localization-plugin-test-03/config/rush-project.json index 247dc17187a..f5d98263c8a 100644 --- a/build-tests/localization-plugin-test-03/config/rush-project.json +++ b/build-tests/localization-plugin-test-03/config/rush-project.json @@ -1,8 +1,12 @@ { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "extends": "local-node-rig/profiles/default/config/rush-project.json", + "operationSettings": [ { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] + "operationName": "_phase:build", + "outputFolderNames": ["dist-dev", "dist-prod"] } ] } diff --git a/build-tests/localization-plugin-test-03/config/typescript.json b/build-tests/localization-plugin-test-03/config/typescript.json new file mode 100644 index 00000000000..95215feff86 --- /dev/null +++ b/build-tests/localization-plugin-test-03/config/typescript.json @@ -0,0 +1,4 @@ +{ + // This project uses ts-loader + "additionalModuleKindsToEmit": [] +} diff --git a/build-tests/localization-plugin-test-03/eslint.config.js b/build-tests/localization-plugin-test-03/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/build-tests/localization-plugin-test-03/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/localization-plugin-test-03/package.json b/build-tests/localization-plugin-test-03/package.json index a0770ac7242..aee6c5bff74 100644 --- a/build-tests/localization-plugin-test-03/package.json +++ b/build-tests/localization-plugin-test-03/package.json @@ -4,21 +4,40 @@ "version": "0.1.0", "private": true, "scripts": { - "build": "node build.js", - "serve": "node serve.js", - "_phase:build": "node build.js" + "build": "heft build --clean", + "serve": "heft start", + "_phase:build": "heft run --only build -- --clean" }, "dependencies": { - "@rushstack/webpack4-localization-plugin": "workspace:*", + "@rushstack/heft": "workspace:*", + "@rushstack/heft-webpack4-plugin": "workspace:*", "@rushstack/node-core-library": "workspace:*", - "@rushstack/set-webpack-public-path-plugin": "workspace:*", - "@types/webpack-env": "1.18.0", + "@rushstack/set-webpack-public-path-plugin": "^4.1.16", + "@rushstack/webpack4-localization-plugin": "workspace:*", + "@rushstack/webpack4-module-minifier-plugin": "workspace:*", + "@types/webpack-env": "1.18.8", + "eslint": "~9.37.0", "html-webpack-plugin": "~4.5.2", + "local-node-rig": "workspace:*", "ts-loader": "6.0.0", - "typescript": "~5.0.4", + "typescript": "~5.8.2", + "webpack": "~4.47.0", "webpack-bundle-analyzer": "~4.5.0", - "webpack-cli": "~3.3.2", - "webpack-dev-server": "~4.9.3", - "webpack": "~4.44.2" + "webpack-dev-server": "~4.9.3" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } } } diff --git a/build-tests/localization-plugin-test-03/serve.js b/build-tests/localization-plugin-test-03/serve.js deleted file mode 100644 index ffa5c333f00..00000000000 --- a/build-tests/localization-plugin-test-03/serve.js +++ /dev/null @@ -1,18 +0,0 @@ -const { FileSystem } = require('@rushstack/node-core-library'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -FileSystem.ensureEmptyFolder('dist'); -FileSystem.ensureEmptyFolder('lib'); -FileSystem.ensureEmptyFolder('temp'); - -// Run Webpack -executeCommand('node node_modules/webpack-dev-server/bin/webpack-dev-server'); diff --git a/build-tests/localization-plugin-test-03/src/chunks/chunkWithStrings.ts b/build-tests/localization-plugin-test-03/src/chunks/chunkWithStrings.ts index faca96a0bf2..2aada85f5e0 100644 --- a/build-tests/localization-plugin-test-03/src/chunks/chunkWithStrings.ts +++ b/build-tests/localization-plugin-test-03/src/chunks/chunkWithStrings.ts @@ -1,7 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import strings from './strings2.loc.json'; export class ChunkWithStringsClass { public doStuff(): void { + // eslint-disable-next-line no-console console.log(strings.string1); } } diff --git a/build-tests/localization-plugin-test-03/src/chunks/chunkWithoutStrings.ts b/build-tests/localization-plugin-test-03/src/chunks/chunkWithoutStrings.ts index 5e19d5f1060..0113e165b98 100644 --- a/build-tests/localization-plugin-test-03/src/chunks/chunkWithoutStrings.ts +++ b/build-tests/localization-plugin-test-03/src/chunks/chunkWithoutStrings.ts @@ -1,5 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + export class ChunkWithoutStringsClass { public doStuff(): void { + // eslint-disable-next-line no-console console.log('STATIC STRING'); } } diff --git a/build-tests/localization-plugin-test-03/src/chunks/unnamedChunkWithStrings.ts b/build-tests/localization-plugin-test-03/src/chunks/unnamedChunkWithStrings.ts index dded761545a..de887bbcf84 100644 --- a/build-tests/localization-plugin-test-03/src/chunks/unnamedChunkWithStrings.ts +++ b/build-tests/localization-plugin-test-03/src/chunks/unnamedChunkWithStrings.ts @@ -1,9 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import strings2 from './strings2.loc.json'; import strings6 from './strings6.resx'; export class UnnamedChunkWithStringsClass { public doStuff(): void { + // eslint-disable-next-line no-console console.log(strings2.string1); + // eslint-disable-next-line no-console console.log(strings6.string); } } diff --git a/build-tests/localization-plugin-test-03/src/indexA.ts b/build-tests/localization-plugin-test-03/src/indexA.ts index fdef0d1bdff..37d82ce98d3 100644 --- a/build-tests/localization-plugin-test-03/src/indexA.ts +++ b/build-tests/localization-plugin-test-03/src/indexA.ts @@ -1,30 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import strings1 from './strings1.loc.json'; import strings3 from './strings3.resx.json'; import strings5 from './strings5.resx'; +// eslint-disable-next-line no-console console.log(strings1.string1); +// eslint-disable-next-line no-console console.log(strings3.string2); -import(/* webpackChunkName: 'chunk-with-strings' */ './chunks/chunkWithStrings').then( - ({ ChunkWithStringsClass }) => { - const chunk = new ChunkWithStringsClass(); +import(/* webpackChunkName: 'chunk-with-strings' */ './chunks/chunkWithStrings') + // eslint-disable-next-line @typescript-eslint/naming-convention + .then(({ ChunkWithStringsClass }) => { + const chunk: import('./chunks/chunkWithStrings').ChunkWithStringsClass = new ChunkWithStringsClass(); chunk.doStuff(); - } -); + }) + .catch((e) => { + // eslint-disable-next-line no-console + console.error(e); + }); -import(/* webpackChunkName: 'chunk-without-strings' */ './chunks/chunkWithoutStrings').then( - ({ ChunkWithoutStringsClass }) => { - const chunk = new ChunkWithoutStringsClass(); +import(/* webpackChunkName: 'chunk-without-strings' */ './chunks/chunkWithoutStrings') + // eslint-disable-next-line @typescript-eslint/naming-convention + .then(({ ChunkWithoutStringsClass }) => { + const chunk: import('./chunks/chunkWithoutStrings').ChunkWithoutStringsClass = + new ChunkWithoutStringsClass(); chunk.doStuff(); - } -); + }) + .catch((e) => { + // eslint-disable-next-line no-console + console.error(e); + }); -import('./chunks/unnamedChunkWithStrings').then(({ UnnamedChunkWithStringsClass }) => { - const chunk = new UnnamedChunkWithStringsClass(); - chunk.doStuff(); -}); +import('./chunks/unnamedChunkWithStrings') + // eslint-disable-next-line @typescript-eslint/naming-convention + .then(({ UnnamedChunkWithStringsClass }) => { + const chunk: import('./chunks/unnamedChunkWithStrings').UnnamedChunkWithStringsClass = + new UnnamedChunkWithStringsClass(); + chunk.doStuff(); + }) + .catch((e) => { + // eslint-disable-next-line no-console + console.error(e); + }); +// eslint-disable-next-line no-console console.log(strings5.string1); +// eslint-disable-next-line no-console console.log(strings5.stringWithQuotes); +// eslint-disable-next-line no-console console.log(require('./invalid-strings.loc.json')); diff --git a/build-tests/localization-plugin-test-03/src/indexB.ts b/build-tests/localization-plugin-test-03/src/indexB.ts index 8061551ff16..79a3b5bdc16 100644 --- a/build-tests/localization-plugin-test-03/src/indexB.ts +++ b/build-tests/localization-plugin-test-03/src/indexB.ts @@ -1,11 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import strings3 from './strings3.resx.json'; import strings6 from './strings7.resjson'; const strings4: string = require('./strings4.loc.json'); +// eslint-disable-next-line no-console console.log(strings3.string1); +// eslint-disable-next-line no-console console.log(strings3.string2); +// eslint-disable-next-line no-console console.log(strings4); +// eslint-disable-next-line no-console console.log(strings6.string); diff --git a/build-tests/localization-plugin-test-03/src/indexC.ts b/build-tests/localization-plugin-test-03/src/indexC.ts index 3fa730ce3b4..e26a7272c68 100644 --- a/build-tests/localization-plugin-test-03/src/indexC.ts +++ b/build-tests/localization-plugin-test-03/src/indexC.ts @@ -1,6 +1,13 @@ -import(/* webpackChunkName: 'chunk-with-strings' */ './chunks/chunkWithStrings').then( - ({ ChunkWithStringsClass }) => { - const chunk = new ChunkWithStringsClass(); +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import(/* webpackChunkName: 'chunk-with-strings' */ './chunks/chunkWithStrings') + // eslint-disable-next-line @typescript-eslint/naming-convention + .then(({ ChunkWithStringsClass }) => { + const chunk: import('./chunks/chunkWithStrings').ChunkWithStringsClass = new ChunkWithStringsClass(); chunk.doStuff(); - } -); + }) + .catch((e) => { + // eslint-disable-next-line no-console + console.error(e); + }); diff --git a/build-tests/localization-plugin-test-03/src/indexD.ts b/build-tests/localization-plugin-test-03/src/indexD.ts index c30ebb90d7b..e887b69021f 100644 --- a/build-tests/localization-plugin-test-03/src/indexD.ts +++ b/build-tests/localization-plugin-test-03/src/indexD.ts @@ -1,6 +1,16 @@ -import(/* webpackChunkName: 'chunk-without-strings' */ './chunks/chunkWithoutStrings').then( - ({ ChunkWithoutStringsClass }) => { - const chunk = new ChunkWithoutStringsClass(); - chunk.doStuff(); - } -); +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import(/* webpackChunkName: 'chunk-without-strings' */ './chunks/chunkWithoutStrings') + .then( + // eslint-disable-next-line @typescript-eslint/naming-convention + ({ ChunkWithoutStringsClass }) => { + const chunk: import('./chunks/chunkWithoutStrings').ChunkWithoutStringsClass = + new ChunkWithoutStringsClass(); + chunk.doStuff(); + } + ) + .catch((e) => { + // eslint-disable-next-line no-console + console.error(e); + }); diff --git a/build-tests/localization-plugin-test-03/tsconfig.json b/build-tests/localization-plugin-test-03/tsconfig.json index 60caf3a15ab..898e6469aff 100644 --- a/build-tests/localization-plugin-test-03/tsconfig.json +++ b/build-tests/localization-plugin-test-03/tsconfig.json @@ -1,23 +1,11 @@ { + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "declaration": true, - "declarationMap": true, - "experimentalDecorators": true, - "forceConsistentCasingInFileNames": true, - "inlineSources": true, - "jsx": "react", - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], "module": "esnext", + "outDir": "lib-esm", + "declarationDir": "lib-dts", "moduleResolution": "node", - "noUnusedLocals": true, - "sourceMap": true, - "strictNullChecks": true, - "target": "es5", - "types": ["webpack-env"], - - "outDir": "lib", - "rootDir": "src", + "incremental": false, "rootDirs": ["src", "temp/loc-json-ts"] - }, - "include": ["src/**/*.ts", "src/**/*.tsx"] + } } diff --git a/build-tests/localization-plugin-test-03/webpack.config.js b/build-tests/localization-plugin-test-03/webpack.config.js index 4ededc0d6ea..c0d97daef7d 100644 --- a/build-tests/localization-plugin-test-03/webpack.config.js +++ b/build-tests/localization-plugin-test-03/webpack.config.js @@ -1,15 +1,15 @@ 'use strict'; - const path = require('path'); -const webpack = require('webpack'); -const { JsonFile } = require('@rushstack/node-core-library'); +const { JsonFile, FileSystem } = require('@rushstack/node-core-library'); const { LocalizationPlugin } = require('@rushstack/webpack4-localization-plugin'); const { SetPublicPathPlugin } = require('@rushstack/set-webpack-public-path-plugin'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const HtmlWebpackPlugin = require('html-webpack-plugin'); +const { ModuleMinifierPlugin, WorkerPoolMinifier } = require('@rushstack/webpack4-module-minifier-plugin'); function resolveMissingString(localeNames, localizedResourcePath) { + debugger; let contextRelativePath = path.relative(__dirname, localizedResourcePath); contextRelativePath = contextRelativePath.replace(/\\/g, '/'); // Convert Windows paths to Unix paths if (!contextRelativePath.startsWith('.')) { @@ -18,28 +18,24 @@ function resolveMissingString(localeNames, localizedResourcePath) { const result = {}; for (const localeName of localeNames) { - const expectedCombinedStringsPath = path.resolve( - __dirname, - 'localization', - localeName, - 'combinedStringsData.json' - ); + const expectedCombinedStringsPath = `${__dirname}/localization/${localeName}/combinedStringsData.json`; try { const loadedCombinedStringsPath = JsonFile.load(expectedCombinedStringsPath); result[localeName] = loadedCombinedStringsPath[contextRelativePath]; } catch (e) { - if (e.code !== 'ENOENT' && e.code !== 'ENOTDIR') { + if (!FileSystem.isNotExistError(e)) { // File exists, but reading failed. throw e; } } } + return result; } -function generateConfiguration(mode, outputFolderName) { +function generateConfiguration(mode, outputFolderName, webpack) { return { - mode: mode, + mode, module: { rules: [ { @@ -49,27 +45,32 @@ function generateConfiguration(mode, outputFolderName) { options: { compiler: require.resolve('typescript'), logLevel: 'ERROR', - configFile: path.resolve(__dirname, 'tsconfig.json') + configFile: `${__dirname}/tsconfig.json` } } ] }, resolve: { - extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'] + extensions: ['.js', '.json', '.ts', '.tsx'] }, entry: { - 'localization-test-A': path.join(__dirname, 'src', 'indexA.ts'), - 'localization-test-B': path.join(__dirname, 'src', 'indexB.ts'), - 'localization-test-C': path.join(__dirname, 'src', 'indexC.ts'), - 'localization-test-D': path.join(__dirname, 'src', 'indexD.ts') + 'localization-test-A': `${__dirname}/src/indexA.ts`, + 'localization-test-B': `${__dirname}/src/indexB.ts`, + 'localization-test-C': `${__dirname}/src/indexC.ts`, + 'localization-test-D': `${__dirname}/src/indexD.ts` }, output: { - path: path.join(__dirname, outputFolderName), + path: `${__dirname}/${outputFolderName}`, filename: '[name]_[locale]_[contenthash].js', chunkFilename: '[id].[name]_[locale]_[contenthash].js' }, optimization: { - minimize: true + minimizer: [ + new ModuleMinifierPlugin({ + minifier: new WorkerPoolMinifier(), + useSourceMap: true + }) + ] }, plugins: [ new webpack.optimize.ModuleConcatenationPlugin(), @@ -116,22 +117,22 @@ function generateConfiguration(mode, outputFolderName) { normalizeResxNewlines: 'lf' }, typingsOptions: { - generatedTsFolder: path.resolve(__dirname, 'temp', 'loc-json-ts'), - secondaryGeneratedTsFolders: ['lib'], - sourceRoot: path.resolve(__dirname, 'src'), + generatedTsFolder: `${__dirname}/temp/loc-json-ts`, + secondaryGeneratedTsFolders: ['lib-commonjs'], + sourceRoot: `${__dirname}/src`, exportAsDefault: true }, localizationStats: { - dropPath: path.resolve(__dirname, 'temp', 'localization-stats.json') + dropPath: `${__dirname}/temp/localization-stats.json` }, globsToIgnore: ['**/invalid-strings.loc.json'] }), new BundleAnalyzerPlugin({ openAnalyzer: false, analyzerMode: 'static', - reportFilename: path.resolve(__dirname, 'temp', 'stats.html'), + reportFilename: `${__dirname}/temp/stats.html`, generateStatsFile: true, - statsFilename: path.resolve(__dirname, 'temp', 'stats.json'), + statsFilename: `${__dirname}/temp/stats.json`, logLevel: 'error' }), new SetPublicPathPlugin({ @@ -144,7 +145,7 @@ function generateConfiguration(mode, outputFolderName) { }; } -module.exports = [ - generateConfiguration('development', 'dist-dev'), - generateConfiguration('production', 'dist-prod') +module.exports = ({ webpack }) => [ + generateConfiguration('development', 'dist-dev', webpack), + generateConfiguration('production', 'dist-prod', webpack) ]; diff --git a/build-tests/package-extractor-test-01/package.json b/build-tests/package-extractor-test-01/package.json new file mode 100644 index 00000000000..0868658e9d2 --- /dev/null +++ b/build-tests/package-extractor-test-01/package.json @@ -0,0 +1,16 @@ +{ + "name": "package-extractor-test-01", + "description": "This project is used by tests in the @rushstack/package-extractor package.", + "version": "1.0.0", + "private": true, + "scripts": { + "_phase:build": "" + }, + "dependencies": { + "package-extractor-test-02": "workspace:*" + }, + "devDependencies": { + "package-extractor-test-03": "workspace:*", + "@types/node": "20.17.19" + } +} diff --git a/build-tests/package-extractor-test-01/src/index.js b/build-tests/package-extractor-test-01/src/index.js new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/build-tests/package-extractor-test-01/src/index.js @@ -0,0 +1 @@ +export {}; diff --git a/build-tests/package-extractor-test-01/src/subdir/file.js b/build-tests/package-extractor-test-01/src/subdir/file.js new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/build-tests/package-extractor-test-01/src/subdir/file.js @@ -0,0 +1 @@ +export {}; diff --git a/build-tests/package-extractor-test-02/package.json b/build-tests/package-extractor-test-02/package.json new file mode 100644 index 00000000000..89f2ec52a65 --- /dev/null +++ b/build-tests/package-extractor-test-02/package.json @@ -0,0 +1,12 @@ +{ + "name": "package-extractor-test-02", + "description": "This project is used by tests in the @rushstack/package-extractor package.", + "version": "1.0.0", + "private": true, + "scripts": { + "_phase:build": "" + }, + "dependencies": { + "package-extractor-test-03": "workspace:*" + } +} diff --git a/build-tests/package-extractor-test-02/src/index.js b/build-tests/package-extractor-test-02/src/index.js new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/build-tests/package-extractor-test-02/src/index.js @@ -0,0 +1 @@ +export {}; diff --git a/build-tests/package-extractor-test-03/package.json b/build-tests/package-extractor-test-03/package.json new file mode 100644 index 00000000000..360aa59fde4 --- /dev/null +++ b/build-tests/package-extractor-test-03/package.json @@ -0,0 +1,12 @@ +{ + "name": "package-extractor-test-03", + "description": "This project is used by tests in the @rushstack/package-extractor package.", + "version": "1.0.0", + "private": true, + "scripts": { + "_phase:build": "" + }, + "devDependencies": { + "@types/node": "ts3.9" + } +} diff --git a/build-tests/package-extractor-test-03/src/index.js b/build-tests/package-extractor-test-03/src/index.js new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/build-tests/package-extractor-test-03/src/index.js @@ -0,0 +1 @@ +export {}; diff --git a/build-tests/package-extractor-test-04/package.json b/build-tests/package-extractor-test-04/package.json new file mode 100644 index 00000000000..a410383f18e --- /dev/null +++ b/build-tests/package-extractor-test-04/package.json @@ -0,0 +1,12 @@ +{ + "name": "package-extractor-test-04", + "description": "This project is used by tests in the @rushstack/package-extractor package.", + "version": "1.0.0", + "private": true, + "scripts": { + "_phase:build": "" + }, + "dependencies": { + "@rushstack/node-core-library": "workspace:*" + } +} diff --git a/build-tests/package-extractor-test-04/src/index.js b/build-tests/package-extractor-test-04/src/index.js new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/build-tests/package-extractor-test-04/src/index.js @@ -0,0 +1 @@ +export {}; diff --git a/build-tests/package-extractor-test-05/package.json b/build-tests/package-extractor-test-05/package.json new file mode 100644 index 00000000000..a168568c337 --- /dev/null +++ b/build-tests/package-extractor-test-05/package.json @@ -0,0 +1,16 @@ +{ + "name": "package-extractor-test-05", + "description": "This project is used by tests in the @rushstack/package-extractor package.", + "version": "1.0.0", + "private": true, + "main": "./dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "_phase:build": "" + }, + "dependencies": { + "@rushstack/node-core-library": "workspace:*" + } +} diff --git a/build-tests/package-extractor-test-05/src/index.js b/build-tests/package-extractor-test-05/src/index.js new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/build-tests/package-extractor-test-05/src/index.js @@ -0,0 +1 @@ +export {}; diff --git a/build-tests/run-scenarios-helpers/config/rig.json b/build-tests/run-scenarios-helpers/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/run-scenarios-helpers/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/run-scenarios-helpers/eslint.config.js b/build-tests/run-scenarios-helpers/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/build-tests/run-scenarios-helpers/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/run-scenarios-helpers/package.json b/build-tests/run-scenarios-helpers/package.json new file mode 100644 index 00000000000..0ecf9ea3efc --- /dev/null +++ b/build-tests/run-scenarios-helpers/package.json @@ -0,0 +1,42 @@ +{ + "name": "run-scenarios-helpers", + "description": "Helpers for the *-scenarios test projects.", + "version": "1.0.0", + "private": true, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft build --clean" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "dependencies": { + "@microsoft/api-extractor": "workspace:*", + "@rushstack/node-core-library": "workspace:*" + } +} diff --git a/build-tests/run-scenarios-helpers/src/index.ts b/build-tests/run-scenarios-helpers/src/index.ts new file mode 100644 index 00000000000..f788a86850f --- /dev/null +++ b/build-tests/run-scenarios-helpers/src/index.ts @@ -0,0 +1,245 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IRunScriptOptions } from '@rushstack/heft'; +import { Async, FileSystem, type FolderItem, JsonFile, Text } from '@rushstack/node-core-library'; +import { + Extractor, + ExtractorConfig, + CompilerState, + type ExtractorResult, + type ExtractorMessage, + ConsoleMessageId, + ExtractorLogLevel +} from '@microsoft/api-extractor'; + +export interface IRunScenariosOptions { + libFolderPath: string; + additionalApiExtractorConfig?: {}; + afterApiExtractorAsync?: (scenarioFolderName: string) => Promise; +} + +export async function runScenariosAsync( + { + heftTaskSession: { + logger, + parameters: { production } + }, + heftConfiguration: { buildFolderPath } + }: IRunScriptOptions, + { libFolderPath, additionalApiExtractorConfig, afterApiExtractorAsync }: IRunScenariosOptions +): Promise { + const entryPoints: string[] = []; + const scenariosWithCustomCompilerOptions: string[] = []; + + const scenarioFolderNames: string[] = []; + const libDtsFolderPath: string = `${buildFolderPath}/lib-dts`; + const folderItems: FolderItem[] = await FileSystem.readFolderItemsAsync(libDtsFolderPath); + for (const folderItem of folderItems) { + if (folderItem.isDirectory()) { + scenarioFolderNames.push(folderItem.name); + } + } + + await Async.forEachAsync( + scenarioFolderNames, + async (scenarioFolderName) => { + const entryPoint: string = `${libDtsFolderPath}/${scenarioFolderName}/index.d.ts`; + entryPoints.push(entryPoint); + + const overridesPath: string = `${buildFolderPath}/src/${scenarioFolderName}/config/api-extractor-overrides.json`; + + let apiExtractorJsonOverrides: {} | undefined; + try { + apiExtractorJsonOverrides = await JsonFile.loadAsync(overridesPath); + } catch (e) { + if (!FileSystem.isNotExistError(e)) { + throw e; + } + } + + if (apiExtractorJsonOverrides && 'compiler' in apiExtractorJsonOverrides) { + scenariosWithCustomCompilerOptions.push(scenarioFolderName); + } + + const apiExtractorJson: {} = { + $schema: 'https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json', + + mainEntryPointFilePath: entryPoint, + + apiReport: { + enabled: true, + reportFolder: `/temp/etc/${scenarioFolderName}` + }, + + dtsRollup: { + enabled: true, + untrimmedFilePath: `/temp/etc/${scenarioFolderName}/rollup.d.ts` + }, + + docModel: { + enabled: true, + apiJsonFilePath: `/temp/etc/${scenarioFolderName}/.api.json` + }, + + newlineKind: 'os', + testMode: true, + + ...additionalApiExtractorConfig, + ...apiExtractorJsonOverrides + }; + + const apiExtractorJsonPath: string = `${buildFolderPath}/temp/configs/api-extractor-${scenarioFolderName}.json`; + + await Promise.all([ + JsonFile.saveAsync(apiExtractorJson, apiExtractorJsonPath, { ensureFolderExists: true }), + FileSystem.ensureFolderAsync(`${buildFolderPath}/temp/etc/${scenarioFolderName}`) + ]); + }, + { concurrency: 10 } + ); + + let baseCompilerState: CompilerState | undefined = undefined; + for (const scenarioFolderName of scenarioFolderNames) { + logger.terminal.writeLine(`Scenario: ${scenarioFolderName}`); + + // Run API Extractor programmatically + const apiExtractorJsonPath: string = `${buildFolderPath}/temp/configs/api-extractor-${scenarioFolderName}.json`; + const extractorConfig: ExtractorConfig = ExtractorConfig.loadFileAndPrepare(apiExtractorJsonPath); + + let compilerState: CompilerState; + if (scenariosWithCustomCompilerOptions.includes(scenarioFolderName)) { + logger.terminal.writeLine(`Using custom compiler state (${scenarioFolderName})`); + compilerState = CompilerState.create(extractorConfig, { + additionalEntryPoints: entryPoints + }); + } else { + if (!baseCompilerState) { + baseCompilerState = CompilerState.create(extractorConfig, { + additionalEntryPoints: entryPoints + }); + } + compilerState = baseCompilerState; + } + + const extractorResult: ExtractorResult = Extractor.invoke(extractorConfig, { + localBuild: true, + showVerboseMessages: true, + messageCallback: (message: ExtractorMessage) => { + switch (message.messageId) { + case ConsoleMessageId.ApiReportCreated: + // This script deletes the outputs for a clean build, so don't issue a warning if the file gets created + message.logLevel = ExtractorLogLevel.None; + break; + case ConsoleMessageId.Preamble: + // Less verbose output + message.logLevel = ExtractorLogLevel.None; + break; + } + }, + compilerState + }); + + if (extractorResult.errorCount > 0) { + logger.emitError(new Error(`Encountered ${extractorResult.errorCount} API Extractor error(s)`)); + } + + await afterApiExtractorAsync?.(scenarioFolderName); + } + + const baseInFolderPath: string = `${buildFolderPath}/temp/etc`; + const baseOutFolderPath: string = `${buildFolderPath}/etc`; + + const inFolderPaths: AsyncIterable = enumerateFolderPaths(baseInFolderPath, ''); + const outFolderPaths: AsyncIterable = enumerateFolderPaths(baseOutFolderPath, ''); + const outFolderPathsSet: Set = new Set(); + + for await (const outFolderPath of outFolderPaths) { + outFolderPathsSet.add(outFolderPath); + } + + const nonMatchingFiles: string[] = []; + await Async.forEachAsync( + inFolderPaths, + async (folderItemPath) => { + outFolderPathsSet.delete(folderItemPath); + + const sourceFileContents: string = await FileSystem.readFileAsync(baseInFolderPath + folderItemPath); + const outFilePath: string = baseOutFolderPath + folderItemPath; + let outFileContents: string | undefined; + try { + outFileContents = await FileSystem.readFileAsync(outFilePath); + } catch (e) { + if (!FileSystem.isNotExistError(e)) { + throw e; + } + } + + const normalizedSourceFileContents: string = Text.convertToLf(sourceFileContents); + const normalizedOutFileContents: string | undefined = outFileContents + ? Text.convertToLf(outFileContents) + : undefined; + + if (normalizedSourceFileContents !== normalizedOutFileContents) { + nonMatchingFiles.push(outFilePath); + if (!production) { + await FileSystem.writeFileAsync(outFilePath, normalizedSourceFileContents, { + ensureFolderExists: true + }); + } + } + }, + { concurrency: 10 } + ); + + if (outFolderPathsSet.size > 0) { + nonMatchingFiles.push(...outFolderPathsSet); + if (!production) { + await Async.forEachAsync( + outFolderPathsSet, + async (outFolderPath) => { + await FileSystem.deleteFileAsync(`${outFolderPath}/${outFolderPath}`); + }, + { concurrency: 10 } + ); + } + } + + if (nonMatchingFiles.length > 0) { + const errorLines: string[] = []; + for (const nonMatchingFile of nonMatchingFiles.sort()) { + errorLines.push(` ${nonMatchingFile}`); + } + + if (production) { + logger.emitError( + new Error( + 'The following file(s) do not match the expected output. Build this project in non-production ' + + `mode and commit the changes:\n${errorLines.join('\n')}` + ) + ); + } else { + logger.emitWarning( + new Error( + `The following file(s) do not match the expected output and must be committed to Git:\n` + + errorLines.join('\n') + ) + ); + } + } +} + +async function* enumerateFolderPaths( + absoluteFolderPath: string, + relativeFolderPath: string +): AsyncIterable { + const folderItems: FolderItem[] = await FileSystem.readFolderItemsAsync(absoluteFolderPath); + for (const folderItem of folderItems) { + const childRelativeFolderPath: string = `${relativeFolderPath}/${folderItem.name}`; + if (folderItem.isDirectory()) { + yield* enumerateFolderPaths(`${absoluteFolderPath}/${folderItem.name}`, childRelativeFolderPath); + } else { + yield childRelativeFolderPath; + } + } +} diff --git a/build-tests/run-scenarios-helpers/tsconfig.json b/build-tests/run-scenarios-helpers/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/build-tests/run-scenarios-helpers/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/.eslintrc.js b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/.eslintrc.js deleted file mode 100644 index 60160b354c4..00000000000 --- a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/node'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/config/rig.json b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/config/rig.json +++ b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/docker-compose.yml b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/docker-compose.yml index 28da694636c..47086ab2ddb 100644 --- a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/docker-compose.yml +++ b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/docker-compose.yml @@ -7,9 +7,6 @@ services: ports: - '9000:9000' - '9001:9001' - environment: - MINIO_ROOT_USER: minio - MINIO_ROOT_PASSWORD: minio123 healthcheck: test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live'] interval: 30s diff --git a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/eslint.config.js b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/eslint.config.js new file mode 100644 index 00000000000..95db6d06e12 --- /dev/null +++ b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node'); + +module.exports = [ + ...nodeProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/package.json b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/package.json index 7e50921c892..8318c777711 100644 --- a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/package.json +++ b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/package.json @@ -11,15 +11,30 @@ "start-proxy-server": "node ./lib/startProxyServer.js" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", + "@microsoft/rush-lib": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", - "@rushstack/rush-amazon-s3-build-cache-plugin": "workspace:*", "@rushstack/node-core-library": "workspace:*", - "@types/node": "14.18.36", - "eslint": "~8.7.0", - "typescript": "~5.0.4", + "@rushstack/rush-amazon-s3-build-cache-plugin": "workspace:*", + "@rushstack/terminal": "workspace:*", + "@types/http-proxy": "~1.17.8", + "@types/node": "20.17.19", + "eslint": "~9.37.0", "http-proxy": "~1.18.1", - "@types/http-proxy": "~1.17.8" + "local-node-rig": "workspace:*" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } } } diff --git a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/src/readObject.ts b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/src/readObject.ts index c954dd80205..5501dcf6d72 100644 --- a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/src/readObject.ts +++ b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/src/readObject.ts @@ -1,6 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import { AmazonS3Client } from '@rushstack/rush-amazon-s3-build-cache-plugin'; -import { WebClient } from '@rushstack/rush-amazon-s3-build-cache-plugin'; -import { ConsoleTerminalProvider, ITerminal, Terminal } from '@rushstack/node-core-library'; +import { WebClient } from '@microsoft/rush-lib/lib/utilities/WebClient'; +import { ConsoleTerminalProvider, type ITerminal, Terminal } from '@rushstack/terminal'; const webClient: WebClient = new WebClient(); @@ -31,17 +34,21 @@ async function main(): Promise { const response: Buffer | undefined = await client.getObjectAsync('rush-build-cache/testfile.txt'); if (response) { if (response.toString().match('remote file from the rush build cache')) { + // eslint-disable-next-line no-console console.log('✅ Success!'); } else { + // eslint-disable-next-line no-console console.log('❌ Error: response does not match the file in s3data/rush-build-cache/testfile.txt'); process.exit(1); } } else { + // eslint-disable-next-line no-console console.error('❌ Error: no response'); process.exit(1); } } main().catch((err) => { + // eslint-disable-next-line no-console console.error(err); process.exit(1); }); diff --git a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/src/startProxyServer.ts b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/src/startProxyServer.ts index e3a33757267..c7c6a54b9a8 100644 --- a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/src/startProxyServer.ts +++ b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/src/startProxyServer.ts @@ -1,5 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as http from 'node:http'; + import * as httpProxy from 'http-proxy'; -import * as http from 'http'; const proxy: httpProxy = httpProxy.createProxyServer({}); @@ -10,12 +14,14 @@ const server: http.Server = http.createServer((req, res) => { requestCount += 1; if (req.url && requestCount % 2 === 0 && !hasFailed[req.url]) { + // eslint-disable-next-line no-console console.log('failing', req.url); hasFailed[req.url] = true; res.statusCode = 500; res.end(); return; } else if (req.url) { + // eslint-disable-next-line no-console console.log('proxying', req.url); } diff --git a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/tsconfig.json b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/tsconfig.json index 2d179c7173f..7fed53142a3 100644 --- a/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/tsconfig.json +++ b/build-tests/rush-amazon-s3-build-cache-plugin-integration-test/tsconfig.json @@ -2,7 +2,8 @@ "$schema": "http://json.schemastore.org/tsconfig", "compilerOptions": { - "outDir": "lib", + "outDir": "lib-commonjs", + "declarationDir": "lib-dts", "rootDir": "src", "forceConsistentCasingInFileNames": true, @@ -20,6 +21,6 @@ "target": "es2017", "lib": ["es2017"] }, - "include": ["src/**/*.ts", "src/**/*.tsx"], - "exclude": ["node_modules", "lib"] + + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/build-tests/rush-lib-declaration-paths-test/.eslintrc.js b/build-tests/rush-lib-declaration-paths-test/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/build-tests/rush-lib-declaration-paths-test/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/rush-lib-declaration-paths-test/config/heft.json b/build-tests/rush-lib-declaration-paths-test/config/heft.json index 31269f36eca..857ca850bcb 100644 --- a/build-tests/rush-lib-declaration-paths-test/config/heft.json +++ b/build-tests/rush-lib-declaration-paths-test/config/heft.json @@ -1,38 +1,25 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", - "extends": "@rushstack/heft-node-rig/profiles/default/config/heft.json", + "extends": "local-node-rig/profiles/default/config/heft.json", "phasesByName": { "build": { - "cleanFiles": [{ "sourcePath": "src" }], + "cleanFiles": [{ "includeGlobs": ["src"] }], + "tasksByName": { "create-src": { - "taskEvent": { - "eventKind": "runScript", + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "run-script-plugin", "options": { "scriptPath": "./scripts/createSrc.js" } } }, - "copy-src-typings": { - "taskEvent": { - "eventKind": "copyFiles", - "options": { - "copyOperations": [ - { - "sourcePath": "node_modules/@microsoft/rush-lib/src", - "destinationFolders": ["src"], - "includeGlobs": ["npm-check-typings.d.ts"] - } - ] - } - } - }, - "typescript": { - "taskDependencies": ["create-src", "copy-src-typings"] + "taskDependencies": ["create-src"] } } } diff --git a/build-tests/rush-lib-declaration-paths-test/config/rig.json b/build-tests/rush-lib-declaration-paths-test/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/build-tests/rush-lib-declaration-paths-test/config/rig.json +++ b/build-tests/rush-lib-declaration-paths-test/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/build-tests/rush-lib-declaration-paths-test/eslint.config.js b/build-tests/rush-lib-declaration-paths-test/eslint.config.js new file mode 100644 index 00000000000..096c66fb598 --- /dev/null +++ b/build-tests/rush-lib-declaration-paths-test/eslint.config.js @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + }, + rules: { + // This project contains only unshipped generated TS code which doesn't contain the copyright header. + 'header/header': 'off' + } + } +]; diff --git a/build-tests/rush-lib-declaration-paths-test/package.json b/build-tests/rush-lib-declaration-paths-test/package.json index 63559034233..d5fe4b307b3 100644 --- a/build-tests/rush-lib-declaration-paths-test/package.json +++ b/build-tests/rush-lib-declaration-paths-test/package.json @@ -5,16 +5,31 @@ "private": true, "scripts": { "build": "heft build --clean", - "_phase:build": "heft build --clean" + "_phase:build": "heft run --only build -- --clean" }, "dependencies": { "@microsoft/rush-lib": "workspace:*" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/node-core-library": "workspace:*", - "@types/node": "14.18.36" + "@types/node": "20.17.19", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } } } diff --git a/build-tests/rush-lib-declaration-paths-test/scripts/createSrc.js b/build-tests/rush-lib-declaration-paths-test/scripts/createSrc.js index 39589e5941b..d61fb703b1a 100644 --- a/build-tests/rush-lib-declaration-paths-test/scripts/createSrc.js +++ b/build-tests/rush-lib-declaration-paths-test/scripts/createSrc.js @@ -1,4 +1,4 @@ -'ues strict'; +'use strict'; const { FileSystem, Import } = require('@rushstack/node-core-library'); @@ -26,8 +26,12 @@ module.exports = { } } - const indexFileLines = ['/// ', '']; - for await (const dtsPath of collectDtsPaths(`${rushLibPath}/lib`, '@microsoft/rush-lib/lib')) { + const indexFileLines = [ + '// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.', + '// See LICENSE in the project root for license information.', + '' + ]; + for await (const dtsPath of collectDtsPaths(`${rushLibPath}/lib-commonjs`, '@microsoft/rush-lib/lib')) { indexFileLines.push(`import '${dtsPath}';`); } diff --git a/build-tests/rush-lib-declaration-paths-test/tsconfig.json b/build-tests/rush-lib-declaration-paths-test/tsconfig.json index c67fe4658e6..dac21d04081 100644 --- a/build-tests/rush-lib-declaration-paths-test/tsconfig.json +++ b/build-tests/rush-lib-declaration-paths-test/tsconfig.json @@ -1,6 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - "compilerOptions": { - "types": ["node"] - } + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/build-tests/rush-mcp-example-plugin/.npmignore b/build-tests/rush-mcp-example-plugin/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/build-tests/rush-mcp-example-plugin/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/build-tests/rush-mcp-example-plugin/LICENSE b/build-tests/rush-mcp-example-plugin/LICENSE new file mode 100644 index 00000000000..5ad10fc49f8 --- /dev/null +++ b/build-tests/rush-mcp-example-plugin/LICENSE @@ -0,0 +1,24 @@ +rush-mcp-example-plugin + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/build-tests/rush-mcp-example-plugin/README.md b/build-tests/rush-mcp-example-plugin/README.md new file mode 100644 index 00000000000..8ca3190b2fc --- /dev/null +++ b/build-tests/rush-mcp-example-plugin/README.md @@ -0,0 +1,3 @@ +# rush-mcp-example-plugin + +This example project shows how to create a plugin for `@rushstack/mcp-server` diff --git a/build-tests/rush-mcp-example-plugin/config/rig.json b/build-tests/rush-mcp-example-plugin/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/rush-mcp-example-plugin/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/rush-mcp-example-plugin/package.json b/build-tests/rush-mcp-example-plugin/package.json new file mode 100644 index 00000000000..26b4de36942 --- /dev/null +++ b/build-tests/rush-mcp-example-plugin/package.json @@ -0,0 +1,41 @@ +{ + "name": "rush-mcp-example-plugin", + "version": "0.0.0", + "private": true, + "description": "Example showing how to create a plugin for @rushstack/mcp-server", + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean" + }, + "dependencies": {}, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "@rushstack/mcp-server": "workspace:*", + "local-node-rig": "workspace:*", + "local-eslint-config": "workspace:*" + }, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + } +} diff --git a/build-tests/rush-mcp-example-plugin/rush-mcp-plugin.json b/build-tests/rush-mcp-example-plugin/rush-mcp-plugin.json new file mode 100644 index 00000000000..cf875f15e32 --- /dev/null +++ b/build-tests/rush-mcp-example-plugin/rush-mcp-plugin.json @@ -0,0 +1,24 @@ +/** + * Every plugin package must contain a "rush-mcp-plugin.json" manifest in the top-level folder + * (next to package.json). + */ +{ + /** + * A name that uniquely identifies your plugin. Generally this should be the same name as + * the NPM package. If two NPM packages have the same pluginName, they cannot be loaded together. + */ + "pluginName": "rush-mcp-example-plugin", + + /** + * (OPTIONAL) Indicates that your plugin accepts a config file. The MCP server will load this + * file and provide it to the plugin. + * + * The config file path will be `/common/config/rush-mcp/.json`. + */ + "configFileSchema": "./lib-commonjs/rush-mcp-example-plugin.schema.json", + + /** + * The entry point, whose default export should be a class that implements + */ + "entryPoint": "./lib-commonjs/index.js" +} diff --git a/build-tests/rush-mcp-example-plugin/src/ExamplePlugin.ts b/build-tests/rush-mcp-example-plugin/src/ExamplePlugin.ts new file mode 100644 index 00000000000..d706e8c9a52 --- /dev/null +++ b/build-tests/rush-mcp-example-plugin/src/ExamplePlugin.ts @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IRushMcpPlugin, RushMcpPluginSession } from '@rushstack/mcp-server'; +import { StateCapitalTool } from './StateCapitalTool'; + +export interface IExamplePluginConfigFile { + capitalsByState: Record; +} + +export class ExamplePlugin implements IRushMcpPlugin { + public session: RushMcpPluginSession; + public configFile: IExamplePluginConfigFile | undefined = undefined; + + public constructor(session: RushMcpPluginSession, configFile: IExamplePluginConfigFile | undefined) { + this.session = session; + this.configFile = configFile; + } + + public async onInitializeAsync(): Promise { + this.session.registerTool({ toolName: 'state_capital' }, new StateCapitalTool(this)); + } +} diff --git a/build-tests/rush-mcp-example-plugin/src/StateCapitalTool.ts b/build-tests/rush-mcp-example-plugin/src/StateCapitalTool.ts new file mode 100644 index 00000000000..f6ad8ce6812 --- /dev/null +++ b/build-tests/rush-mcp-example-plugin/src/StateCapitalTool.ts @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IRushMcpTool, RushMcpPluginSession, CallToolResult, zodModule } from '@rushstack/mcp-server'; + +import type { ExamplePlugin } from './ExamplePlugin'; + +export class StateCapitalTool implements IRushMcpTool { + public readonly plugin: ExamplePlugin; + public readonly session: RushMcpPluginSession; + + public constructor(plugin: ExamplePlugin) { + this.plugin = plugin; + this.session = plugin.session; + } + + // ZOD relies on type inference generate a messy expression in the .d.ts file + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + public get schema() { + const zod: typeof zodModule = this.session.zod; + + return zod.object({ + state: zod.string().describe('The name of the state, in all lowercase') + }); + } + + public async executeAsync(input: zodModule.infer): Promise { + const capital: string | undefined = this.plugin.configFile?.capitalsByState[input.state]; + + return { + content: [ + { + type: 'text', + text: capital + ? `The capital of "${input.state}" is "${capital}"` + : `Unable to determine the answer from the data set.` + } + ] + }; + } +} diff --git a/build-tests/rush-mcp-example-plugin/src/index.ts b/build-tests/rush-mcp-example-plugin/src/index.ts new file mode 100644 index 00000000000..8866a23e566 --- /dev/null +++ b/build-tests/rush-mcp-example-plugin/src/index.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { RushMcpPluginSession, RushMcpPluginFactory } from '@rushstack/mcp-server'; +import { ExamplePlugin, type IExamplePluginConfigFile } from './ExamplePlugin'; + +function createPlugin( + session: RushMcpPluginSession, + configFile: IExamplePluginConfigFile | undefined +): ExamplePlugin { + return new ExamplePlugin(session, configFile); +} + +export default createPlugin satisfies RushMcpPluginFactory; diff --git a/build-tests/rush-mcp-example-plugin/src/rush-mcp-example-plugin.schema.json b/build-tests/rush-mcp-example-plugin/src/rush-mcp-example-plugin.schema.json new file mode 100644 index 00000000000..55da2c56a0e --- /dev/null +++ b/build-tests/rush-mcp-example-plugin/src/rush-mcp-example-plugin.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "State Capital Map", + "type": "object", + "required": ["capitalsByState"], + "properties": { + "$schema": { + "type": "string" + }, + "capitalsByState": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "A mapping of US state names (lowercase) to their capital cities." + } + }, + "additionalProperties": false +} diff --git a/build-tests/rush-mcp-example-plugin/tsconfig.json b/build-tests/rush-mcp-example-plugin/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/build-tests/rush-mcp-example-plugin/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/build-tests/rush-package-manager-integration-test/README.md b/build-tests/rush-package-manager-integration-test/README.md new file mode 100644 index 00000000000..5563850776f --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/README.md @@ -0,0 +1,97 @@ +# Rush Package Manager Integration Tests + +This directory contains integration tests for verifying Rush works correctly with different package managers after the tar 7.x upgrade. + +## Background + +Rush's npm and yarn modes use temp project tarballs (stored in `common/temp/projects/`) to simulate package installations. The tar library is used to: +1. **Create** tarballs from temp project folders (`TempProjectHelper.createTempProjectTarball`) +2. **Extract** tarballs during the linking process (`NpmLinkManager._linkProjectAsync`) + +These tests ensure the tar 7.x upgrade works correctly with these workflows. + +## Tests + +The test suite is written in TypeScript using `@rushstack/node-core-library` for cross-platform compatibility. + +### testNpmMode.ts +Tests Rush npm mode by: +- Initializing a Rush repo with `npmVersion` configured +- Creating two projects with dependencies +- Running `rush update` +- Running `rush install` +- Running `rush build` (verifies everything works end-to-end) + +### testYarnMode.ts +Tests Rush yarn mode by: +- Initializing a Rush repo with `yarnVersion` configured +- Creating two projects with dependencies +- Running `rush update` +- Running `rush install` +- Running `rush build` (verifies everything works end-to-end) + +## Prerequisites + +Before running these tests: +1. Build Rush locally: `rush build --to rush` +2. Build this test project: `rush build --to rush-package-manager-integration-test` +3. Ensure you have Node.js 18+ installed + +## Running the Tests + +```bash +# Build the test project first +cd build-tests/rush-package-manager-integration-test +rush build + +# Run all tests +npm run test +``` + +Or from the root of the repo: +```bash +rush build --to rush-package-manager-integration-test +cd build-tests/rush-package-manager-integration-test +npm run test +``` + +## What Gets Tested + +These integration tests verify: +- ✓ Temp project tarballs are created correctly using tar 7.x +- ✓ Tarballs are extracted correctly during `rush install` +- ✓ File permissions are preserved (tar filter function works) +- ✓ Dependencies are linked properly between projects +- ✓ The complete workflow (update → install → build) succeeds +- ✓ Built code executes correctly + +## Test Output + +Each test creates a temporary Rush repository in `/tmp/rush-package-manager-test/`: +- `/tmp/rush-package-manager-test/npm-test-repo/` - npm mode test repository +- `/tmp/rush-package-manager-test/yarn-test-repo/` - yarn mode test repository + +These directories are cleaned up at the start of each test run. + +## Implementation + +The tests use: +- **TypeScript** for type safety and better IDE support +- **@rushstack/node-core-library** for cross-platform file operations and process execution +- **TestHelper class** to encapsulate common test operations +- Modular test functions that can be run independently or together + +## Related Code + +The tar library is used in: +- `libraries/rush-lib/src/logic/TempProjectHelper.ts` - Creates tarballs +- `libraries/rush-lib/src/logic/npm/NpmLinkManager.ts` - Extracts tarballs + +## Troubleshooting + +If tests fail: +1. Check that Rush built successfully: `rush build --to rush` +2. Check that the test project built: `rush build --to rush-package-manager-integration-test` +3. Verify Node.js version: `node --version` (should be 18+) +4. Look for error messages in the test output +5. Inspect the temp test repo: `ls -la temp/npm-test-repo/common/temp/projects/` diff --git a/build-tests/rush-package-manager-integration-test/config/rig.json b/build-tests/rush-package-manager-integration-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/rush-package-manager-integration-test/eslint.config.js b/build-tests/rush-package-manager-integration-test/eslint.config.js new file mode 100644 index 00000000000..95db6d06e12 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node'); + +module.exports = [ + ...nodeProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/rush-package-manager-integration-test/package.json b/build-tests/rush-package-manager-integration-test/package.json new file mode 100644 index 00000000000..a812e495c73 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/package.json @@ -0,0 +1,35 @@ +{ + "name": "rush-package-manager-integration-test", + "version": "1.0.0", + "private": true, + "description": "Integration tests for non-pnpm package managers in Rush.", + "license": "MIT", + "scripts": { + "_phase:build": "heft build --clean", + "build": "heft build --clean", + "test": "node lib-commonjs/runTests.js" + }, + "devDependencies": { + "@microsoft/rush": "workspace:*", + "@microsoft/rush-lib": "workspace:*", + "@rushstack/heft": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "@rushstack/terminal": "workspace:*", + "local-node-rig": "workspace:*" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + } +} diff --git a/build-tests/rush-package-manager-integration-test/src/TestHelper.ts b/build-tests/rush-package-manager-integration-test/src/TestHelper.ts new file mode 100644 index 00000000000..e711014973d --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/src/TestHelper.ts @@ -0,0 +1,205 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import type * as child_process from 'node:child_process'; + +import { FileSystem, Executable, JsonFile, type JsonObject } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +/** + * Helper class for running integration tests with Rush package managers + */ +export class TestHelper { + private readonly _rushBinPath: string; + private readonly _terminal: ITerminal; + + public constructor(terminal: ITerminal) { + this._terminal = terminal; + // Resolve rush bin path from @microsoft/rush dependency + this._rushBinPath = require.resolve('@microsoft/rush/lib/start-dev'); + } + + /** + * Execute a Rush command using the locally-built Rush + */ + public async executeRushAsync(args: string[], workingDirectory: string): Promise { + this._terminal.writeLine(`Executing: ${process.argv0} ${this._rushBinPath} ${args.join(' ')}`); + + const childProcess: child_process.ChildProcess = Executable.spawn( + process.argv0, + [this._rushBinPath, ...args], + { + currentWorkingDirectory: workingDirectory, + stdio: 'inherit' + } + ); + + await Executable.waitForExitAsync(childProcess, { + throwOnNonZeroExitCode: true + }); + } + + /** + * Create a test Rush repository with the specified package manager + */ + public async createTestRepoAsync( + testRepoPath: string, + packageManagerType: 'npm' | 'yarn', + packageManagerVersion: string + ): Promise { + // Clean up previous test run and create empty test repo directory + this._terminal.writeLine(`Creating test repository at ${testRepoPath}...`); + await FileSystem.ensureEmptyFolderAsync(testRepoPath); + + // Initialize Rush repo + this._terminal.writeLine('Initializing Rush repo...'); + await this.executeRushAsync(['init'], testRepoPath); + + // Configure rush.json for the specified package manager + this._terminal.writeLine(`Configuring rush.json for ${packageManagerType} mode...`); + const rushJsonPath: string = path.join(testRepoPath, 'rush.json'); + const rushJson: JsonObject = await JsonFile.loadAsync(rushJsonPath); + + // Update package manager configuration + if (packageManagerType === 'npm') { + delete rushJson.pnpmVersion; + delete rushJson.yarnVersion; + rushJson.npmVersion = packageManagerVersion; + } else if (packageManagerType === 'yarn') { + delete rushJson.pnpmVersion; + delete rushJson.npmVersion; + rushJson.yarnVersion = packageManagerVersion; + } + + // Add test projects + rushJson.projects = [ + { + packageName: 'test-project-a', + projectFolder: 'projects/test-project-a' + }, + { + packageName: 'test-project-b', + projectFolder: 'projects/test-project-b' + } + ]; + + // Update nodeSupportedVersionRange to match current environment + rushJson.nodeSupportedVersionRange = '>=18.0.0'; + + await JsonFile.saveAsync(rushJson, rushJsonPath, { updateExistingFile: true }); + } + + /** + * Create a test project with the specified configuration + */ + public async createTestProjectAsync( + testRepoPath: string, + projectName: string, + version: string, + dependencies: Record, + buildScript: string + ): Promise { + const projectPath: string = path.join(testRepoPath, 'projects', projectName); + await FileSystem.ensureFolderAsync(projectPath); + + const packageJson: JsonObject = { + name: projectName, + version: version, + main: 'lib/index.js', + scripts: { + build: buildScript + }, + dependencies: dependencies + }; + + await JsonFile.saveAsync(packageJson, path.join(projectPath, 'package.json')); + } + + /** + * Verify that temp project tarballs were created + */ + public async verifyTempTarballsAsync(testRepoPath: string, projectNames: string[]): Promise { + this._terminal.writeLine('\nVerifying temp project tarballs were created...'); + for (const projectName of projectNames) { + const tarballPath: string = path.join(testRepoPath, 'common/temp/projects', `${projectName}.tgz`); + if (!(await FileSystem.existsAsync(tarballPath))) { + throw new Error(`ERROR: ${projectName}.tgz was not created!`); + } + } + this._terminal.writeLine('✓ Temp project tarballs created successfully'); + } + + /** + * Verify that dependencies are installed correctly + */ + public async verifyDependenciesAsync( + testRepoPath: string, + projectName: string, + expectedDependencies: string[] + ): Promise { + this._terminal.writeLine('\nVerifying node_modules structure...'); + const projectPath: string = path.join(testRepoPath, 'projects', projectName); + const projectNodeModules: string = path.join(projectPath, 'node_modules'); + + for (const dep of expectedDependencies) { + const depPath: string = path.join(projectNodeModules, dep); + if (!(await FileSystem.existsAsync(depPath))) { + throw new Error(`ERROR: ${dep} not found in ${projectName}!`); + } + + // Verify symlinks resolve correctly for local dependencies + if (dep.startsWith('test-project-')) { + const depRealPath: string = await FileSystem.getRealPathAsync(depPath); + const expectedRealPath: string = path.join(testRepoPath, 'projects', dep); + if (depRealPath !== expectedRealPath) { + throw new Error( + `ERROR: Symlink for ${dep} does not resolve correctly!\n` + + `Expected: ${expectedRealPath}\n` + + `Actual: ${depRealPath}` + ); + } + } + } + this._terminal.writeLine('✓ Dependencies installed correctly'); + } + + /** + * Verify that build outputs were created + */ + public async verifyBuildOutputsAsync(testRepoPath: string, projectNames: string[]): Promise { + this._terminal.writeLine('\nVerifying build outputs...'); + for (const projectName of projectNames) { + const outputPath: string = path.join(testRepoPath, 'projects', projectName, 'lib/index.js'); + if (!(await FileSystem.existsAsync(outputPath))) { + throw new Error(`ERROR: ${projectName} build output not found!`); + } + } + this._terminal.writeLine('✓ Build completed successfully'); + } + + /** + * Test that the built code executes correctly + */ + public async testBuiltCodeAsync(testRepoPath: string, projectName: string): Promise { + this._terminal.writeLine('\nTesting built code...'); + const projectLib: string = path.join(testRepoPath, 'projects', projectName, 'lib/index.js'); + + // Use forward slashes for require() path on all platforms + const projectLibPosix: string = projectLib.split(path.sep).join(path.posix.sep); + + // Use Executable.spawnSync to capture output + const result: string = Executable.spawnSync( + process.argv0, + ['-e', `const b = require('${projectLibPosix}'); console.log(b.test());`], + { + currentWorkingDirectory: testRepoPath + } + ).stdout.toString(); + + if (!result.includes('Using: Hello from A')) { + throw new Error('ERROR: Built code did not execute as expected!'); + } + this._terminal.writeLine('✓ Built code executes correctly'); + } +} diff --git a/build-tests/rush-package-manager-integration-test/src/runTests.ts b/build-tests/rush-package-manager-integration-test/src/runTests.ts new file mode 100644 index 00000000000..e531c6251bb --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/src/runTests.ts @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { Terminal, ConsoleTerminalProvider } from '@rushstack/terminal'; + +import { testNpmModeAsync } from './testNpmMode'; +import { testYarnModeAsync } from './testYarnMode'; + +/** + * Main test runner that executes all package manager integration tests + */ +async function runTestsAsync(): Promise { + const terminal: Terminal = new Terminal(new ConsoleTerminalProvider()); + + terminal.writeLine('=========================================='); + terminal.writeLine('Rush Package Manager Integration Tests'); + terminal.writeLine('=========================================='); + terminal.writeLine(''); + terminal.writeLine('These tests verify that the tar 7.x upgrade works correctly'); + terminal.writeLine('with different Rush package managers (npm, yarn).'); + terminal.writeLine(''); + terminal.writeLine('Tests will:'); + terminal.writeLine(' 1. Create Rush repos using locally-built Rush'); + terminal.writeLine(' 2. Add projects with dependencies'); + terminal.writeLine(' 3. Run rush update (creates and extracts temp tarballs)'); + terminal.writeLine(' 4. Run rush install (extracts tarballs)'); + terminal.writeLine(' 5. Run rush build (end-to-end verification)'); + terminal.writeLine(''); + + let testsPassed: number = 0; + let testsFailed: number = 0; + const failedTests: string[] = []; + + // Run npm mode test + terminal.writeLine('=========================================='); + terminal.writeLine('Running NPM mode test...'); + terminal.writeLine('=========================================='); + try { + await testNpmModeAsync(terminal); + testsPassed++; + } catch (error) { + testsFailed++; + failedTests.push('NPM mode'); + terminal.writeErrorLine('⚠️ NPM mode test FAILED'); + terminal.writeErrorLine(String(error)); + } + + // Run yarn mode test + terminal.writeLine('=========================================='); + terminal.writeLine('Running Yarn mode test...'); + terminal.writeLine('=========================================='); + try { + await testYarnModeAsync(terminal); + testsPassed++; + } catch (error) { + testsFailed++; + failedTests.push('Yarn mode'); + terminal.writeErrorLine('⚠️ Yarn mode test FAILED'); + terminal.writeErrorLine(String(error)); + } + + // Print summary + terminal.writeLine('=========================================='); + terminal.writeLine('Test Summary'); + terminal.writeLine('=========================================='); + terminal.writeLine(''); + terminal.writeLine(`Tests passed: ${testsPassed}`); + terminal.writeLine(`Tests failed: ${testsFailed}`); + terminal.writeLine(''); + + if (testsFailed > 0) { + terminal.writeLine('Failed tests:'); + for (const test of failedTests) { + terminal.writeLine(` - ${test}`); + } + terminal.writeLine(''); + terminal.writeLine('❌ Some tests failed'); + process.exit(1); + } else { + terminal.writeLine('✅ All tests passed!'); + terminal.writeLine(''); + terminal.writeLine('The tar 7.x upgrade is working correctly with:'); + terminal.writeLine(' - NPM package manager'); + terminal.writeLine(' - Yarn package manager'); + terminal.writeLine(''); + process.exit(0); + } +} + +// Run tests and handle errors +runTestsAsync().catch((error) => { + // eslint-disable-next-line no-console + console.error('Fatal error running tests:'); + // eslint-disable-next-line no-console + console.error(error); + process.exit(1); +}); diff --git a/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts b/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts new file mode 100644 index 00000000000..e10ffa64e3b --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as os from 'node:os'; +import * as path from 'node:path'; + +import type { ITerminal } from '@rushstack/terminal'; + +import { TestHelper } from './TestHelper'; + +/** + * Integration test for Rush npm mode with tar 7.x + * This test verifies that temp project tarballs work correctly with npm package manager + */ +export async function testNpmModeAsync(terminal: ITerminal): Promise { + const helper: TestHelper = new TestHelper(terminal); + // Use system temp directory to avoid rush init detecting parent rush.json + const testRepoPath: string = path.join(os.tmpdir(), 'rush-package-manager-test', 'npm-test-repo'); + + terminal.writeLine('=========================================='); + terminal.writeLine('Rush NPM Mode Integration Test'); + terminal.writeLine('=========================================='); + terminal.writeLine(''); + terminal.writeLine('This test verifies that tar 7.x changes work correctly with npm package manager'); + terminal.writeLine('by creating temp project tarballs and extracting them during rush install.'); + terminal.writeLine(''); + + // Create test repository with npm 6.14.15 (the version from rush init template) + await helper.createTestRepoAsync(testRepoPath, 'npm', '6.14.15'); + + // Create project A (dependency) + terminal.writeLine('Creating test-project-a...'); + await helper.createTestProjectAsync( + testRepoPath, + 'test-project-a', + '1.0.0', + { semver: '^7.5.4' }, + `node -e "const fs = require('fs'); fs.mkdirSync('lib', {recursive: true}); fs.writeFileSync('lib/index.js', 'module.exports = { greet: () => \\"Hello from A\\" };');"` + ); + + // Create project B (depends on A) + terminal.writeLine('Creating test-project-b...'); + await helper.createTestProjectAsync( + testRepoPath, + 'test-project-b', + '1.0.0', + { + 'test-project-a': '1.0.0', + moment: '^2.29.4' + }, + `node -e "const a = require('test-project-a'), fs = require('fs'); fs.mkdirSync('lib', {recursive: true}); fs.writeFileSync('lib/index.js', 'module.exports = { test: () => \\"Using: \\" + require(\\'test-project-a\\').greet() };');"` + ); + + // Run rush update (creates and extracts temp project tarballs) + terminal.writeLine(''); + terminal.writeLine("Running 'rush update' (creates and extracts temp project tarballs using tar 7.x)..."); + await helper.executeRushAsync(['update'], testRepoPath); + + // Verify temp project tarballs were created + await helper.verifyTempTarballsAsync(testRepoPath, ['test-project-a', 'test-project-b']); + + // Run rush install (extracts temp project tarballs) + terminal.writeLine(''); + terminal.writeLine("Running 'rush install' (extracts temp project tarballs using tar 7.x)..."); + await helper.executeRushAsync(['install'], testRepoPath); + + // Verify node_modules were populated correctly + await helper.verifyDependenciesAsync(testRepoPath, 'test-project-a', ['semver']); + await helper.verifyDependenciesAsync(testRepoPath, 'test-project-b', ['test-project-a']); + + // Run rush build + terminal.writeLine(''); + terminal.writeLine("Running 'rush build'..."); + await helper.executeRushAsync(['build'], testRepoPath); + + // Verify build outputs + await helper.verifyBuildOutputsAsync(testRepoPath, ['test-project-a', 'test-project-b']); + + // Test that the built code actually works + await helper.testBuiltCodeAsync(testRepoPath, 'test-project-b'); + + terminal.writeLine(''); + terminal.writeLine('=========================================='); + terminal.writeLine('✓ NPM Mode Integration Test PASSED'); + terminal.writeLine('=========================================='); + terminal.writeLine(''); + terminal.writeLine('The tar 7.x changes work correctly with npm mode:'); + terminal.writeLine(' - Temp project tarballs created successfully'); + terminal.writeLine(' - Tarballs extracted correctly during install'); + terminal.writeLine(' - Dependencies linked properly'); + terminal.writeLine(' - Build completed successfully'); + terminal.writeLine(''); +} diff --git a/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts b/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts new file mode 100644 index 00000000000..a7d1b826bc2 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as os from 'node:os'; +import * as path from 'node:path'; + +import type { ITerminal } from '@rushstack/terminal'; + +import { TestHelper } from './TestHelper'; + +/** + * Integration test for Rush yarn mode with tar 7.x + * This test verifies that temp project tarballs work correctly with yarn package manager + */ +export async function testYarnModeAsync(terminal: ITerminal): Promise { + const helper: TestHelper = new TestHelper(terminal); + // Use system temp directory to avoid rush init detecting parent rush.json + const testRepoPath: string = path.join(os.tmpdir(), 'rush-package-manager-test', 'yarn-test-repo'); + + terminal.writeLine('=========================================='); + terminal.writeLine('Rush Yarn Mode Integration Test'); + terminal.writeLine('=========================================='); + terminal.writeLine(''); + terminal.writeLine('This test verifies that tar 7.x changes work correctly with yarn package manager'); + terminal.writeLine('by creating temp project tarballs and extracting them during rush install.'); + terminal.writeLine(''); + + // Create test repository with yarn 1.9.4 (the version from rush init template) + await helper.createTestRepoAsync(testRepoPath, 'yarn', '1.9.4'); + + // Create project A (dependency) + terminal.writeLine('Creating test-project-a...'); + await helper.createTestProjectAsync( + testRepoPath, + 'test-project-a', + '1.0.0', + { semver: '^7.5.4' }, + `node -e "const fs = require('fs'); fs.mkdirSync('lib', {recursive: true}); fs.writeFileSync('lib/index.js', 'module.exports = { greet: () => \\"Hello from A\\" };');"` + ); + + // Create project B (depends on A) + terminal.writeLine('Creating test-project-b...'); + await helper.createTestProjectAsync( + testRepoPath, + 'test-project-b', + '1.0.0', + { + 'test-project-a': '1.0.0', + moment: '^2.29.4' + }, + `node -e "const a = require('test-project-a'), fs = require('fs'); fs.mkdirSync('lib', {recursive: true}); fs.writeFileSync('lib/index.js', 'module.exports = { test: () => \\"Using: \\" + require(\\'test-project-a\\').greet() };');"` + ); + + // Run rush update (creates and extracts temp project tarballs) + terminal.writeLine(''); + terminal.writeLine("Running 'rush update' (creates and extracts temp project tarballs using tar 7.x)..."); + await helper.executeRushAsync(['update'], testRepoPath); + + // Verify temp project tarballs were created + await helper.verifyTempTarballsAsync(testRepoPath, ['test-project-a', 'test-project-b']); + + // Run rush install (extracts temp project tarballs) + terminal.writeLine(''); + terminal.writeLine("Running 'rush install' (extracts temp project tarballs using tar 7.x)..."); + await helper.executeRushAsync(['install'], testRepoPath); + + // Verify node_modules were populated correctly + await helper.verifyDependenciesAsync(testRepoPath, 'test-project-a', ['semver']); + await helper.verifyDependenciesAsync(testRepoPath, 'test-project-b', ['test-project-a']); + + // Run rush build + terminal.writeLine(''); + terminal.writeLine("Running 'rush build'..."); + await helper.executeRushAsync(['build'], testRepoPath); + + // Verify build outputs + await helper.verifyBuildOutputsAsync(testRepoPath, ['test-project-a', 'test-project-b']); + + // Test that the built code actually works + await helper.testBuiltCodeAsync(testRepoPath, 'test-project-b'); + + terminal.writeLine(''); + terminal.writeLine('=========================================='); + terminal.writeLine('✓ Yarn Mode Integration Test PASSED'); + terminal.writeLine('=========================================='); + terminal.writeLine(''); + terminal.writeLine('The tar 7.x changes work correctly with yarn mode:'); + terminal.writeLine(' - Temp project tarballs created successfully'); + terminal.writeLine(' - Tarballs extracted correctly during install'); + terminal.writeLine(' - Dependencies linked properly'); + terminal.writeLine(' - Build completed successfully'); + terminal.writeLine(''); +} diff --git a/build-tests/rush-package-manager-integration-test/tsconfig.json b/build-tests/rush-package-manager-integration-test/tsconfig.json new file mode 100644 index 00000000000..b98cc4ab57c --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/build-tests/rush-project-change-analyzer-test/.eslintrc.js b/build-tests/rush-project-change-analyzer-test/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/build-tests/rush-project-change-analyzer-test/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/build-tests/rush-project-change-analyzer-test/config/rig.json b/build-tests/rush-project-change-analyzer-test/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/build-tests/rush-project-change-analyzer-test/config/rig.json +++ b/build-tests/rush-project-change-analyzer-test/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/build-tests/rush-project-change-analyzer-test/eslint.config.js b/build-tests/rush-project-change-analyzer-test/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/build-tests/rush-project-change-analyzer-test/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/rush-project-change-analyzer-test/package.json b/build-tests/rush-project-change-analyzer-test/package.json index b455ec8db5c..eed4151ea5d 100644 --- a/build-tests/rush-project-change-analyzer-test/package.json +++ b/build-tests/rush-project-change-analyzer-test/package.json @@ -11,12 +11,27 @@ }, "dependencies": { "@microsoft/rush-lib": "workspace:*", - "@rushstack/node-core-library": "workspace:*" + "@rushstack/terminal": "workspace:*" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@types/node": "14.18.36", - "@rushstack/heft-node-rig": "workspace:*" + "@types/node": "20.17.19", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } } } diff --git a/build-tests/rush-project-change-analyzer-test/src/start.ts b/build-tests/rush-project-change-analyzer-test/src/start.ts index 8149cb6e2eb..b26893d81a3 100644 --- a/build-tests/rush-project-change-analyzer-test/src/start.ts +++ b/build-tests/rush-project-change-analyzer-test/src/start.ts @@ -1,5 +1,8 @@ -import { RushConfiguration, ProjectChangeAnalyzer, RushConfigurationProject } from '@microsoft/rush-lib'; -import { Terminal, ConsoleTerminalProvider } from '@rushstack/node-core-library'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { RushConfiguration, ProjectChangeAnalyzer, type RushConfigurationProject } from '@microsoft/rush-lib'; +import { Terminal, ConsoleTerminalProvider } from '@rushstack/terminal'; async function runAsync(): Promise { const terminal: Terminal = new Terminal(new ConsoleTerminalProvider()); @@ -40,6 +43,9 @@ async function runAsync(): Promise { } process.exitCode = 1; -runAsync().then(() => { - process.exitCode = 0; -}, console.error); +runAsync() + .then(() => { + process.exitCode = 0; + }) + // eslint-disable-next-line no-console + .catch(console.error); diff --git a/build-tests/rush-project-change-analyzer-test/tsconfig.json b/build-tests/rush-project-change-analyzer-test/tsconfig.json index c67fe4658e6..dac21d04081 100644 --- a/build-tests/rush-project-change-analyzer-test/tsconfig.json +++ b/build-tests/rush-project-change-analyzer-test/tsconfig.json @@ -1,6 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - "compilerOptions": { - "types": ["node"] - } + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/.gitignore b/build-tests/rush-redis-cobuild-plugin-integration-test/.gitignore new file mode 100644 index 00000000000..97e8499abcc --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/.gitignore @@ -0,0 +1 @@ +redis-data/dump.rdb \ No newline at end of file diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/.vscode/tasks.json b/build-tests/rush-redis-cobuild-plugin-integration-test/.vscode/tasks.json new file mode 100644 index 00000000000..93aa001729c --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/.vscode/tasks.json @@ -0,0 +1,83 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "cobuild", + "dependsOrder": "sequence", + "dependsOn": ["update", "_cobuild"], + "problemMatcher": [] + }, + { + "type": "shell", + "label": "_cobuild", + "dependsOn": ["build 1", "build 2"], + "problemMatcher": [] + }, + { + "type": "shell", + "label": "update", + "command": "node ../../lib/runRush.js update", + "problemMatcher": [], + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "dedicated", + "showReuseMessage": true, + "clear": false + }, + "options": { + "cwd": "${workspaceFolder}/sandbox/repo" + } + }, + { + "type": "shell", + "label": "build 1", + "command": "node ../../lib/runRush.js --debug cobuild --timeline --parallelism 1 --verbose", + "problemMatcher": [], + "options": { + "cwd": "${workspaceFolder}/sandbox/repo", + "env": { + "RUSH_COBUILD_CONTEXT_ID": "integration-test", + "RUSH_COBUILD_RUNNER_ID": "runner1", + "RUSH_COBUILD_LEAF_PROJECT_LOG_ONLY_ALLOWED": "1", + "REDIS_PASS": "redis123" + } + }, + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "dedicated", + "showReuseMessage": true, + "clear": true + }, + "group": "build" + }, + { + "type": "shell", + "label": "build 2", + "command": "node ../../lib/runRush.js --debug cobuild --timeline --parallelism 1 --verbose", + "problemMatcher": [], + "options": { + "cwd": "${workspaceFolder}/sandbox/repo", + "env": { + "RUSH_COBUILD_CONTEXT_ID": "integration-test", + "RUSH_COBUILD_RUNNER_ID": "runner2", + "RUSH_COBUILD_LEAF_PROJECT_LOG_ONLY_ALLOWED": "1", + "REDIS_PASS": "redis123" + } + }, + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "dedicated", + "showReuseMessage": true, + "clear": true + }, + "group": "build" + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/README.md b/build-tests/rush-redis-cobuild-plugin-integration-test/README.md new file mode 100644 index 00000000000..0412bcc358d --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/README.md @@ -0,0 +1,173 @@ +# About + +This package enables integration testing of the `RedisCobuildLockProvider` by connecting to an actual Redis created using an [redis](https://hub.docker.com/_/redis) docker image. + +# Prerequisites + +Docker and docker compose must be installed + +# Start the Redis + +In this folder run `docker-compose up -d` + +# Stop the Redis + +In this folder run `docker-compose down` + +# Install and build the integration test code + +```sh +rush update +rush build -t rush-redis-cobuild-plugin-integration-test +``` + +# Run the test for lock provider + +```sh +# start the docker container: docker-compose up -d +# build the code: rushx build +rushx test-lock-provider +``` + +# Integration test in sandbox repo + +Sandbox repo folder: **build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo** + +```sh +cd sandbox/repo +node ../../lib/runRush.js update +``` + +You can also test sharded operations with cobuilds using the **build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo** +```sh +cd sandbox/sharded-repo +node ../../lib/runRush.js update +``` +You should expect to see multiple shards for operations `a` (15 shards), `b` (75) and `h` (50) and `e` (75). + +#### Case 1: Normal build, Cobuild is disabled because of missing RUSH_COBUILD_CONTEXT_ID + +1. Write to build cache + +```sh +rm -rf common/temp/build-cache && node ../../lib/runRush.js --debug cobuild +``` + +2. Read from build cache + +```sh +node ../../lib/runRush.js --debug cobuild +``` + +Expected behavior: Cobuild feature is disabled. Build cache is saved/restored as normal. + +#### Case 2: Cobuild enabled by specifying RUSH_COBUILD_CONTEXT_ID and Redis authentication + +1. Clear redis server + +```sh +(cd ../.. && docker compose down && docker compose up -d) +``` + +2. Run cobuilds + +```sh +rm -rf common/temp/build-cache && RUSH_COBUILD_CONTEXT_ID=foo REDIS_PASS=redis123 RUSH_COBUILD_RUNNER_ID=runner1 node ../../lib/runRush.js --debug cobuild +``` + +Expected behavior: Cobuild feature is enabled. Run command successfully. +You can also see cobuild related logs in the terminal. + +```sh +Running cobuild (runner foo/runner1) +Analyzing repo state... DONE (0.11 seconds) + +Executing a maximum of 10 simultaneous processes... + +==[ b (build) ]====================================================[ 1 of 9 ]== +Get completed_state(cobuild:completed:foo:2e477baf39a85b28fc40e63b417692fe8afcc023)_package(b)_phase(_phase:build): SUCCESS;2e477baf39a85b28fc40e63b417692fe8afcc023 +Get completed_state(cobuild:completed:foo:cfc620db4e74a6f0db41b1a86d0b5402966b97f3)_package(a)_phase(_phase:build): SUCCESS;cfc620db4e74a6f0db41b1a86d0b5402966b97f3 +Successfully acquired lock(cobuild:lock:foo:4c36160884a7a502f9894e8f0adae05c45c8cc4b)_package(b)_phase(_phase:build) to runner(runner1) and it expires in 30s +``` + +#### Case 3: Cobuild enabled, run two cobuild commands in parallel + +> Note: This test requires Visual Studio Code to be installed. + +1. Open predefined `.vscode/redis-cobuild.code-workspace` in Visual Studio Code. + +2. Clear redis server + +```sh +# Under rushstack/build-tests/rush-redis-cobuild-plugin-integration-test +docker compose down && docker compose up -d +``` + +3. Clear build cache + +```sh +# Under rushstack/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo +rm -rf common/temp/build-cache +``` + +4. Open command palette (Ctrl+Shift+P or Command+Shift+P) and select `Tasks: Run Task` and select `cobuild`. + +> In this step, two dedicated terminal windows will open. Running `rush cobuild` command under sandbox repo respectively. + +Expected behavior: Cobuild feature is enabled, cobuild related logs out in both terminals. + +#### Case 4: Cobuild enabled, run two cobuild commands in parallel, one of them failed + +> Note: This test requires Visual Studio Code to be installed. + +1. Open predefined `.vscode/redis-cobuild.code-workspace` in Visual Studio Code. + +2. Making the cobuild command of project "A" fails + +**sandbox/repo/projects/a/package.json** + +```diff + "scripts": { +- "_phase:build": "node ../build.js a", ++ "_phase:build": "exit 1", + } +``` + +3. Clear redis server + +```sh +# Under rushstack/build-tests/rush-redis-cobuild-plugin-integration-test +docker compose down && docker compose up -d +``` + +4. Clear build cache + +```sh +# Under rushstack/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo +rm -rf common/temp/build-cache +``` + +5. Open command palette (Ctrl+Shift+P or Command+Shift+P) and select `Tasks: Run Task` and select `cobuild`. + +Expected behavior: Cobuild feature is enabled, cobuild related logs out in both terminals. These two cobuild commands fail because of the failing build of project "A". And, one of them restored the failing build cache created by the other one. + +#### Case 5: Sharded cobuilds + +Enable the `allowCobuildWithoutCache` experiment in `experiments.json`. + +Navigate to the sandbox for sharded cobuilds, +```sh +cd sandbox/sharded-repo +``` + +Next, start up your Redis instance, +```sh +docker compose down && docker compose up -d +``` + +Then, open 2 terminals and run this in each (changing the RUSH_COBUILD_RUNNER_ID across the 2 terminals), +```sh +rm -rf common/temp/build-cache && RUSH_COBUILD_CONTEXT_ID=foo REDIS_PASS=redis123 RUSH_COBUILD_RUNNER_ID=runner1 node ../../lib/runRush.js cobuild -p 10 --timeline +``` + +If all goes well, you should see a bunch of operation with `- shard xx/yy`. Operations `h (build)` and `e (build)` are both sharded heavily and should be cobuild compatible. To validate changes you're making, ensure that the timeline view for all of the shards of those 2 operations are cobuilt across both terminals. If they're not, something is wrong with your update. \ No newline at end of file diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/config/rig.json b/build-tests/rush-redis-cobuild-plugin-integration-test/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/docker-compose.yml b/build-tests/rush-redis-cobuild-plugin-integration-test/docker-compose.yml new file mode 100644 index 00000000000..2b9a3f3722b --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3.7' + +services: + redis: + image: redis:6.2.10-alpine + command: redis-server --save "" --loglevel warning --requirepass redis123 + ports: + - '6379:6379' + volumes: + - ./redis-data:/data diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/eslint.config.js b/build-tests/rush-redis-cobuild-plugin-integration-test/eslint.config.js new file mode 100644 index 00000000000..95db6d06e12 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/eslint.config.js @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node'); + +module.exports = [ + ...nodeProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/package.json new file mode 100644 index 00000000000..d444fec1651 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/package.json @@ -0,0 +1,39 @@ +{ + "name": "rush-redis-cobuild-plugin-integration-test", + "version": "1.0.0", + "private": true, + "description": "Tests connecting to an redis server", + "license": "MIT", + "scripts": { + "_phase:build": "heft build --clean", + "build": "heft build --clean", + "test-lock-provider": "node ./lib/testLockProvider.js" + }, + "devDependencies": { + "@microsoft/rush-lib": "workspace:*", + "@rushstack/heft": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "@rushstack/rush-redis-cobuild-plugin": "workspace:*", + "@rushstack/terminal": "workspace:*", + "@types/http-proxy": "~1.17.8", + "@types/node": "20.17.19", + "eslint": "~9.37.0", + "http-proxy": "~1.18.1", + "local-node-rig": "workspace:*" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/.gitignore b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/.gitignore new file mode 100644 index 00000000000..9f8a577215f --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/.gitignore @@ -0,0 +1,8 @@ +# Rush temporary files +common/deploy/ +common/temp/ +common/autoinstallers/*/.npmrc +projects/*/dist/ +*.log +node_modules/ + diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush-plugins/rush-redis-cobuild-plugin.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush-plugins/rush-redis-cobuild-plugin.json new file mode 100644 index 00000000000..c27270adc35 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush-plugins/rush-redis-cobuild-plugin.json @@ -0,0 +1,4 @@ +{ + "url": "redis://localhost:6379", + "passwordEnvironmentVariable": "REDIS_PASS" +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/build-cache.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/build-cache.json new file mode 100644 index 00000000000..d09eaa6a04c --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/build-cache.json @@ -0,0 +1,92 @@ +/** + * This configuration file manages Rush's build cache feature. + * More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/build-cache.schema.json", + + /** + * (Required) EXPERIMENTAL - Set this to true to enable the build cache feature. + * + * See https://rushjs.io/pages/maintainer/build_cache/ for details about this experimental feature. + */ + "buildCacheEnabled": true, + + /** + * (Required) Choose where project build outputs will be cached. + * + * Possible values: "local-only", "azure-blob-storage", "amazon-s3" + */ + "cacheProvider": "local-only", + + /** + * Setting this property overrides the cache entry ID. If this property is set, it must contain + * a [hash] token. + * + * Other available tokens: + * - [projectName] + * - [projectName:normalize] + * - [phaseName] + * - [phaseName:normalize] + * - [phaseName:trimPrefix] + */ + // "cacheEntryNamePattern": "[projectName:normalize]-[phaseName:normalize]-[hash]" + + /** + * Use this configuration with "cacheProvider"="azure-blob-storage" + */ + "azureBlobStorageConfiguration": { + /** + * (Required) The name of the the Azure storage account to use for build cache. + */ + // "storageAccountName": "example", + /** + * (Required) The name of the container in the Azure storage account to use for build cache. + */ + // "storageContainerName": "my-container", + /** + * The Azure environment the storage account exists in. Defaults to AzurePublicCloud. + * + * Possible values: "AzurePublicCloud", "AzureChina", "AzureGermany", "AzureGovernment" + */ + // "azureEnvironment": "AzurePublicCloud", + /** + * An optional prefix for cache item blob names. + */ + // "blobPrefix": "my-prefix", + /** + * If set to true, allow writing to the cache. Defaults to false. + */ + // "isCacheWriteAllowed": true + }, + + /** + * Use this configuration with "cacheProvider"="amazon-s3" + */ + "amazonS3Configuration": { + /** + * (Required unless s3Endpoint is specified) The name of the bucket to use for build cache. + * Example: "my-bucket" + */ + // "s3Bucket": "my-bucket", + /** + * (Required unless s3Bucket is specified) The Amazon S3 endpoint of the bucket to use for build cache. + * This should not include any path; use the s3Prefix to set the path. + * Examples: "my-bucket.s3.us-east-2.amazonaws.com" or "http://localhost:9000" + */ + // "s3Endpoint": "https://my-bucket.s3.us-east-2.amazonaws.com", + /** + * (Required) The Amazon S3 region of the bucket to use for build cache. + * Example: "us-east-1" + */ + // "s3Region": "us-east-1", + /** + * An optional prefix ("folder") for cache items. It should not start with "/". + */ + // "s3Prefix": "my-prefix", + /** + * If set to true, allow writing to the cache. Defaults to false. + */ + // "isCacheWriteAllowed": true + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/cobuild.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/cobuild.json new file mode 100644 index 00000000000..4626f2211d4 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/cobuild.json @@ -0,0 +1,22 @@ +/** + * This configuration file manages Rush's cobuild feature. + * More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/cobuild.schema.json", + + /** + * (Required) EXPERIMENTAL - Set this to true to enable the cobuild feature. + * RUSH_COBUILD_CONTEXT_ID should always be specified as an environment variable with an non-empty string, + * otherwise the cobuild feature will be disabled. + */ + "cobuildFeatureEnabled": true, + + /** + * (Required) Choose where cobuild lock will be acquired. + * + * The lock provider is registered by the rush plugins. + * For example, @rushstack/rush-redis-cobuild-plugin registers the "redis" lock provider. + */ + "cobuildLockProvider": "redis" +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/command-line.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/command-line.json new file mode 100644 index 00000000000..c8c1ccc022d --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/command-line.json @@ -0,0 +1,336 @@ +/** + * This configuration file defines custom commands for the "rush" command-line. + * More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json", + + /** + * Custom "commands" introduce new verbs for the command-line. To see the help for these + * example commands, try "rush --help", "rush my-bulk-command --help", or + * "rush my-global-command --help". + */ + "commands": [ + { + "commandKind": "phased", + "summary": "Concurrent version of rush build", + "name": "cobuild", + "safeForSimultaneousRushProcesses": true, + "enableParallelism": true, + "incremental": true, + "phases": ["_phase:pre-build", "_phase:build"] + } + + // { + // /** + // * (Required) Determines the type of custom command. + // * Rush's "bulk" commands are invoked separately for each project. Rush will look in + // * each project's package.json file for a "scripts" entry whose name matches the + // * command name. By default, the command will run for every project in the repo, + // * according to the dependency graph (similar to how "rush build" works). + // * The set of projects can be restricted e.g. using the "--to" or "--from" parameters. + // */ + // "commandKind": "bulk", + // + // /** + // * (Required) The name that will be typed as part of the command line. This is also the name + // * of the "scripts" hook in the project's package.json file. + // * The name should be comprised of lower case words separated by hyphens or colons. The name should include an + // * English verb (e.g. "deploy"). Use a hyphen to separate words (e.g. "upload-docs"). A group of related commands + // * can be prefixed with a colon (e.g. "docs:generate", "docs:deploy", "docs:serve", etc). + // * + // * Note that if the "rebuild" command is overridden here, it becomes separated from the "build" command + // * and will call the "rebuild" script instead of the "build" script. + // */ + // "name": "my-bulk-command", + // + // /** + // * (Required) A short summary of the custom command to be shown when printing command line + // * help, e.g. "rush --help". + // */ + // "summary": "Example bulk custom command", + // + // /** + // * A detailed description of the command to be shown when printing command line + // * help (e.g. "rush --help my-command"). + // * If omitted, the "summary" text will be shown instead. + // * + // * Whenever you introduce commands/parameters, taking a little time to write meaningful + // * documentation can make a big difference for the developer experience in your repo. + // */ + // "description": "This is an example custom command that runs separately for each project", + // + // /** + // * By default, Rush operations acquire a lock file which prevents multiple commands from executing simultaneously + // * in the same repo folder. (For example, it would be a mistake to run "rush install" and "rush build" at the + // * same time.) If your command makes sense to run concurrently with other operations, + // * set "safeForSimultaneousRushProcesses" to true to disable this protection. + // * + // * In particular, this is needed for custom scripts that invoke other Rush commands. + // */ + // "safeForSimultaneousRushProcesses": false, + // + // /** + // * (Required) If true, then this command is safe to be run in parallel, i.e. executed + // * simultaneously for multiple projects. Similar to "rush build", regardless of parallelism + // * projects will not start processing until their dependencies have completed processing. + // */ + // "enableParallelism": false, + // + // /** + // * Normally projects will be processed according to their dependency order: a given project will not start + // * processing the command until all of its dependencies have completed. This restriction doesn't apply for + // * certain operations, for example a "clean" task that deletes output files. In this case + // * you can set "ignoreDependencyOrder" to true to increase parallelism. + // */ + // "ignoreDependencyOrder": false, + // + // /** + // * Normally Rush requires that each project's package.json has a "scripts" entry matching + // * the custom command name. To disable this check, set "ignoreMissingScript" to true; + // * projects with a missing definition will be skipped. + // */ + // "ignoreMissingScript": false, + // + // /** + // * When invoking shell scripts, Rush uses a heuristic to distinguish errors from warnings: + // * - If the shell script returns a nonzero process exit code, Rush interprets this as "one or more errors". + // * Error output is displayed in red, and it prevents Rush from attempting to process any downstream projects. + // * - If the shell script returns a zero process exit code but writes something to its stderr stream, + // * Rush interprets this as "one or more warnings". Warning output is printed in yellow, but does NOT prevent + // * Rush from processing downstream projects. + // * + // * Thus, warnings do not interfere with local development, but they will cause a CI job to fail, because + // * the Rush process itself returns a nonzero exit code if there are any warnings or errors. This is by design. + // * In an active monorepo, we've found that if you allow any warnings in your main branch, it inadvertently + // * teaches developers to ignore warnings, which quickly leads to a situation where so many "expected" warnings + // * have accumulated that warnings no longer serve any useful purpose. + // * + // * Sometimes a poorly behaved task will write output to stderr even though its operation was successful. + // * In that case, it's strongly recommended to fix the task. However, as a workaround you can set + // * allowWarningsInSuccessfulBuild=true, which causes Rush to return a nonzero exit code for errors only. + // * + // * Note: The default value is false. In Rush 5.7.x and earlier, the default value was true. + // */ + // "allowWarningsInSuccessfulBuild": false, + // + // /** + // * If true then this command will be incremental like the built-in "build" command + // */ + // "incremental": false, + // + // /** + // * (EXPERIMENTAL) Normally Rush terminates after the command finishes. If this option is set to "true" Rush + // * will instead enter a loop where it watches the file system for changes to the selected projects. Whenever a + // * change is detected, the command will be invoked again for the changed project and any selected projects that + // * directly or indirectly depend on it. + // * + // * For details, refer to the website article "Using watch mode". + // */ + // "watchForChanges": false, + // + // /** + // * (EXPERIMENTAL) Disable cache for this action. This may be useful if this command affects state outside of + // * projects' own folders. + // */ + // "disableBuildCache": false + // }, + // + // { + // /** + // * (Required) Determines the type of custom command. + // * Rush's "global" commands are invoked once for the entire repo. + // */ + // "commandKind": "global", + // + // "name": "my-global-command", + // "summary": "Example global custom command", + // "description": "This is an example custom command that runs once for the entire repo", + // + // "safeForSimultaneousRushProcesses": false, + // + // /** + // * (Required) A script that will be invoked using the OS shell. The working directory will be + // * the folder that contains rush.json. If custom parameters are associated with this command, their + // * values will be appended to the end of this string. + // */ + // "shellCommand": "node common/scripts/my-global-command.js", + // + // /** + // * If your "shellCommand" script depends on NPM packages, the recommended best practice is + // * to make it into a regular Rush project that builds using your normal toolchain. In cases where + // * the command needs to work without first having to run "rush build", the recommended practice + // * is to publish the project to an NPM registry and use common/scripts/install-run.js to launch it. + // * + // * Autoinstallers offer another possibility: They are folders under "common/autoinstallers" with + // * a package.json file and shrinkwrap file. Rush will automatically invoke the package manager to + // * install these dependencies before an associated command is invoked. Autoinstallers have the + // * advantage that they work even in a branch where "rush install" is broken, which makes them a + // * good solution for Git hook scripts. But they have the disadvantages of not being buildable + // * projects, and of increasing the overall installation footprint for your monorepo. + // * + // * The "autoinstallerName" setting must not contain a path and must be a valid NPM package name. + // * For example, the name "my-task" would map to "common/autoinstallers/my-task/package.json", and + // * the "common/autoinstallers/my-task/node_modules/.bin" folder would be added to the shell PATH when + // * invoking the "shellCommand". + // */ + // // "autoinstallerName": "my-task" + // } + ], + + "phases": [ + { + /** + * The name of the phase. Note that this value must start with the \"_phase:\" prefix. + */ + "name": "_phase:build", + /** + * The dependencies of this phase. + */ + "dependencies": { + "upstream": ["_phase:build"], + "self": ["_phase:pre-build"] + } + }, + { + /** + * The name of the phase. Note that this value must start with the \"_phase:\" prefix. + */ + "name": "_phase:pre-build", + /** + * The dependencies of this phase. + */ + "dependencies": { + "upstream": ["_phase:build"] + }, + "missingScriptBehavior": "silent" + } + ], + + /** + * Custom "parameters" introduce new parameters for specified Rush command-line commands. + * For example, you might define a "--production" parameter for the "rush build" command. + */ + "parameters": [ + // { + // /** + // * (Required) Determines the type of custom parameter. + // * A "flag" is a custom command-line parameter whose presence acts as an on/off switch. + // */ + // "parameterKind": "flag", + // + // /** + // * (Required) The long name of the parameter. It must be lower-case and use dash delimiters. + // */ + // "longName": "--my-flag", + // + // /** + // * An optional alternative short name for the parameter. It must be a dash followed by a single + // * lower-case or upper-case letter, which is case-sensitive. + // * + // * NOTE: The Rush developers recommend that automation scripts should always use the long name + // * to improve readability. The short name is only intended as a convenience for humans. + // * The alphabet letters run out quickly, and are difficult to memorize, so *only* use + // * a short name if you expect the parameter to be needed very often in everyday operations. + // */ + // "shortName": "-m", + // + // /** + // * (Required) A long description to be shown in the command-line help. + // * + // * Whenever you introduce commands/parameters, taking a little time to write meaningful + // * documentation can make a big difference for the developer experience in your repo. + // */ + // "description": "A custom flag parameter that is passed to the scripts that are invoked when building projects", + // + // /** + // * (Required) A list of custom commands and/or built-in Rush commands that this parameter may + // * be used with. The parameter will be appended to the shell command that Rush invokes. + // */ + // "associatedCommands": ["build", "rebuild"] + // }, + // + // { + // /** + // * (Required) Determines the type of custom parameter. + // * A "string" is a custom command-line parameter whose value is a simple text string. + // */ + // "parameterKind": "string", + // "longName": "--my-string", + // "description": "A custom string parameter for the \"my-global-command\" custom command", + // + // "associatedCommands": ["my-global-command"], + // + // /** + // * The name of the argument, which will be shown in the command-line help. + // * + // * For example, if the parameter name is '--count" and the argument name is "NUMBER", + // * then the command-line help would display "--count NUMBER". The argument name must + // * be comprised of upper-case letters, numbers, and underscores. It should be kept short. + // */ + // "argumentName": "SOME_TEXT", + // + // /** + // * If true, this parameter must be included with the command. The default is false. + // */ + // "required": false + // }, + // + // { + // /** + // * (Required) Determines the type of custom parameter. + // * A "choice" is a custom command-line parameter whose argument must be chosen from a list of + // * allowable alternatives. + // */ + // "parameterKind": "choice", + // "longName": "--my-choice", + // "description": "A custom choice parameter for the \"my-global-command\" custom command", + // + // "associatedCommands": ["my-global-command"], + // + // /** + // * If true, this parameter must be included with the command. The default is false. + // */ + // "required": false, + // + // /** + // * Normally if a parameter is omitted from the command line, it will not be passed + // * to the shell command. this value will be inserted by default. Whereas if a "defaultValue" + // * is defined, the parameter will always be passed to the shell command, and will use the + // * default value if unspecified. The value must be one of the defined alternatives. + // */ + // "defaultValue": "vanilla", + // + // /** + // * (Required) A list of alternative argument values that can be chosen for this parameter. + // */ + // "alternatives": [ + // { + // /** + // * A token that is one of the alternatives that can be used with the choice parameter, + // * e.g. "vanilla" in "--flavor vanilla". + // */ + // "name": "vanilla", + // + // /** + // * A detailed description for the alternative that can be shown in the command-line help. + // * + // * Whenever you introduce commands/parameters, taking a little time to write meaningful + // * documentation can make a big difference for the developer experience in your repo. + // */ + // "description": "Use the vanilla flavor (the default)" + // }, + // + // { + // "name": "chocolate", + // "description": "Use the chocolate flavor" + // }, + // + // { + // "name": "strawberry", + // "description": "Use the strawberry flavor" + // } + // ] + // } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/experiments.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/experiments.json new file mode 100644 index 00000000000..fef826208c3 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/experiments.json @@ -0,0 +1,55 @@ +/** + * This configuration file allows repo maintainers to enable and disable experimental + * Rush features. More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/experiments.schema.json", + + /** + * By default, 'rush install' passes --no-prefer-frozen-lockfile to 'pnpm install'. + * Set this option to true to pass '--frozen-lockfile' instead for faster installs. + */ + "usePnpmFrozenLockfileForRushInstall": true, + + /** + * By default, 'rush update' passes --no-prefer-frozen-lockfile to 'pnpm install'. + * Set this option to true to pass '--prefer-frozen-lockfile' instead to minimize shrinkwrap changes. + */ + "usePnpmPreferFrozenLockfileForRushUpdate": true, + + /** + * If using the 'preventManualShrinkwrapChanges' option, restricts the hash to only include the layout of external dependencies. + * Used to allow links between workspace projects or the addition/removal of references to existing dependency versions to not + * cause hash changes. + */ + "omitImportersFromPreventManualShrinkwrapChanges": true, + + /** + * If true, the chmod field in temporary project tar headers will not be normalized. + * This normalization can help ensure consistent tarball integrity across platforms. + */ + // "noChmodFieldInTarHeaderNormalization": true, + + /** + * If true, build caching will respect the allowWarningsInSuccessfulBuild flag and cache builds with warnings. + * This will not replay warnings from the cached build. + */ + // "buildCacheWithAllowWarningsInSuccessfulBuild": true, + + /** + * If true, the phased commands feature is enabled. To use this feature, create a "phased" command + * in common/config/rush/command-line.json. + */ + "phasedCommands": true + + /** + * If true, perform a clean install after when running `rush install` or `rush update` if the + * `.npmrc` file has changed since the last install. + */ + // "cleanInstallAfterNpmrcChanges": true, + + /** + * If true, print the outputs of shell commands defined in event hooks to the console. + */ + // "printEventHooksOutputToConsole": true +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/pnpm-lock.yaml b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/pnpm-lock.yaml new file mode 100644 index 00000000000..98b22e9d424 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/pnpm-lock.yaml @@ -0,0 +1,52 @@ +lockfileVersion: 5.4 + +importers: + + .: + specifiers: {} + + ../../projects/a: + specifiers: {} + + ../../projects/b: + specifiers: {} + + ../../projects/c: + specifiers: + b: workspace:* + dependencies: + b: link:../b + + ../../projects/d: + specifiers: + b: workspace:* + c: workspace:* + dependencies: + b: link:../b + c: link:../c + + ../../projects/e: + specifiers: + b: workspace:* + d: workspace:* + dependencies: + b: link:../b + d: link:../d + + ../../projects/f: + specifiers: + b: workspace:* + dependencies: + b: link:../b + + ../../projects/g: + specifiers: + b: workspace:* + dependencies: + b: link:../b + + ../../projects/h: + specifiers: + a: workspace:* + dependencies: + a: link:../a diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/repo-state.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/repo-state.json new file mode 100644 index 00000000000..0e7b144099d --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/config/rush/repo-state.json @@ -0,0 +1,4 @@ +// DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush. +{ + "preferredVersionsHash": "bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f" +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/scripts/install-run-rush-pnpm.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/scripts/install-run-rush-pnpm.js new file mode 100644 index 00000000000..72a7bfdf088 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/scripts/install-run-rush-pnpm.js @@ -0,0 +1,28 @@ +// THIS FILE WAS GENERATED BY A TOOL. ANY MANUAL MODIFICATIONS WILL GET OVERWRITTEN WHENEVER RUSH IS UPGRADED. +// +// This script is intended for usage in an automated build environment where the Rush command may not have +// been preinstalled, or may have an unpredictable version. This script will automatically install the version of Rush +// specified in the rush.json configuration file (if not already installed), and then pass a command-line to the +// rush-pnpm command. +// +// An example usage would be: +// +// node common/scripts/install-run-rush-pnpm.js pnpm-command +// +// For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ + +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +var __webpack_exports__ = {}; +/*!*****************************************************!*\ + !*** ./lib-esnext/scripts/install-run-rush-pnpm.js ***! + \*****************************************************/ + +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. +require('./install-run-rush'); +//# sourceMappingURL=install-run-rush-pnpm.js.map +module.exports = __webpack_exports__; +/******/ })() +; +//# sourceMappingURL=install-run-rush-pnpm.js.map \ No newline at end of file diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/scripts/install-run-rush.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/scripts/install-run-rush.js new file mode 100644 index 00000000000..008e64411b7 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/scripts/install-run-rush.js @@ -0,0 +1,215 @@ +// THIS FILE WAS GENERATED BY A TOOL. ANY MANUAL MODIFICATIONS WILL GET OVERWRITTEN WHENEVER RUSH IS UPGRADED. +// +// This script is intended for usage in an automated build environment where the Rush command may not have +// been preinstalled, or may have an unpredictable version. This script will automatically install the version of Rush +// specified in the rush.json configuration file (if not already installed), and then pass a command-line to it. +// An example usage would be: +// +// node common/scripts/install-run-rush.js install +// +// For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ + +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ 657147: +/*!*********************!*\ + !*** external "fs" ***! + \*********************/ +/***/ ((module) => { + +module.exports = require("fs"); + +/***/ }), + +/***/ 371017: +/*!***********************!*\ + !*** external "path" ***! + \***********************/ +/***/ ((module) => { + +module.exports = require("path"); + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ (() => { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = (module) => { +/******/ var getter = module && module.__esModule ? +/******/ () => (module['default']) : +/******/ () => (module); +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +(() => { +/*!************************************************!*\ + !*** ./lib-esnext/scripts/install-run-rush.js ***! + \************************************************/ +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! path */ 371017); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! fs */ 657147); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__); +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. +/* eslint-disable no-console */ + + +const { installAndRun, findRushJsonFolder, RUSH_JSON_FILENAME, runWithErrorAndStatusCode } = require('./install-run'); +const PACKAGE_NAME = '@microsoft/rush'; +const RUSH_PREVIEW_VERSION = 'RUSH_PREVIEW_VERSION'; +const INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE = 'INSTALL_RUN_RUSH_LOCKFILE_PATH'; +function _getRushVersion(logger) { + const rushPreviewVersion = process.env[RUSH_PREVIEW_VERSION]; + if (rushPreviewVersion !== undefined) { + logger.info(`Using Rush version from environment variable ${RUSH_PREVIEW_VERSION}=${rushPreviewVersion}`); + return rushPreviewVersion; + } + const rushJsonFolder = findRushJsonFolder(); + const rushJsonPath = path__WEBPACK_IMPORTED_MODULE_0__.join(rushJsonFolder, RUSH_JSON_FILENAME); + try { + const rushJsonContents = fs__WEBPACK_IMPORTED_MODULE_1__.readFileSync(rushJsonPath, 'utf-8'); + // Use a regular expression to parse out the rushVersion value because rush.json supports comments, + // but JSON.parse does not and we don't want to pull in more dependencies than we need to in this script. + const rushJsonMatches = rushJsonContents.match(/\"rushVersion\"\s*\:\s*\"([0-9a-zA-Z.+\-]+)\"/); + return rushJsonMatches[1]; + } + catch (e) { + throw new Error(`Unable to determine the required version of Rush from ${RUSH_JSON_FILENAME} (${rushJsonFolder}). ` + + `The 'rushVersion' field is either not assigned in ${RUSH_JSON_FILENAME} or was specified ` + + 'using an unexpected syntax.'); + } +} +function _getBin(scriptName) { + switch (scriptName.toLowerCase()) { + case 'install-run-rush-pnpm.js': + return 'rush-pnpm'; + case 'install-run-rushx.js': + return 'rushx'; + default: + return 'rush'; + } +} +function _run() { + const [nodePath /* Ex: /bin/node */, scriptPath /* /repo/common/scripts/install-run-rush.js */, ...packageBinArgs /* [build, --to, myproject] */] = process.argv; + // Detect if this script was directly invoked, or if the install-run-rushx script was invokved to select the + // appropriate binary inside the rush package to run + const scriptName = path__WEBPACK_IMPORTED_MODULE_0__.basename(scriptPath); + const bin = _getBin(scriptName); + if (!nodePath || !scriptPath) { + throw new Error('Unexpected exception: could not detect node path or script path'); + } + let commandFound = false; + let logger = { info: console.log, error: console.error }; + for (const arg of packageBinArgs) { + if (arg === '-q' || arg === '--quiet') { + // The -q/--quiet flag is supported by both `rush` and `rushx`, and will suppress + // any normal informational/diagnostic information printed during startup. + // + // To maintain the same user experience, the install-run* scripts pass along this + // flag but also use it to suppress any diagnostic information normally printed + // to stdout. + logger = { + info: () => { }, + error: console.error + }; + } + else if (!arg.startsWith('-') || arg === '-h' || arg === '--help') { + // We either found something that looks like a command (i.e. - doesn't start with a "-"), + // or we found the -h/--help flag, which can be run without a command + commandFound = true; + } + } + if (!commandFound) { + console.log(`Usage: ${scriptName} [args...]`); + if (scriptName === 'install-run-rush-pnpm.js') { + console.log(`Example: ${scriptName} pnpm-command`); + } + else if (scriptName === 'install-run-rush.js') { + console.log(`Example: ${scriptName} build --to myproject`); + } + else { + console.log(`Example: ${scriptName} custom-command`); + } + process.exit(1); + } + runWithErrorAndStatusCode(logger, () => { + const version = _getRushVersion(logger); + logger.info(`The ${RUSH_JSON_FILENAME} configuration requests Rush version ${version}`); + const lockFilePath = process.env[INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE]; + if (lockFilePath) { + logger.info(`Found ${INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE}="${lockFilePath}", installing with lockfile.`); + } + return installAndRun(logger, PACKAGE_NAME, version, bin, packageBinArgs, lockFilePath); + }); +} +_run(); +//# sourceMappingURL=install-run-rush.js.map +})(); + +module.exports = __webpack_exports__; +/******/ })() +; +//# sourceMappingURL=install-run-rush.js.map \ No newline at end of file diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/scripts/install-run-rushx.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/scripts/install-run-rushx.js new file mode 100644 index 00000000000..0a0235f29a3 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/scripts/install-run-rushx.js @@ -0,0 +1,28 @@ +// THIS FILE WAS GENERATED BY A TOOL. ANY MANUAL MODIFICATIONS WILL GET OVERWRITTEN WHENEVER RUSH IS UPGRADED. +// +// This script is intended for usage in an automated build environment where the Rush command may not have +// been preinstalled, or may have an unpredictable version. This script will automatically install the version of Rush +// specified in the rush.json configuration file (if not already installed), and then pass a command-line to the +// rushx command. +// +// An example usage would be: +// +// node common/scripts/install-run-rushx.js custom-command +// +// For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ + +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +var __webpack_exports__ = {}; +/*!*************************************************!*\ + !*** ./lib-esnext/scripts/install-run-rushx.js ***! + \*************************************************/ + +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. +require('./install-run-rush'); +//# sourceMappingURL=install-run-rushx.js.map +module.exports = __webpack_exports__; +/******/ })() +; +//# sourceMappingURL=install-run-rushx.js.map \ No newline at end of file diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/scripts/install-run.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/scripts/install-run.js new file mode 100644 index 00000000000..804dfd390d9 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/common/scripts/install-run.js @@ -0,0 +1,721 @@ +// THIS FILE WAS GENERATED BY A TOOL. ANY MANUAL MODIFICATIONS WILL GET OVERWRITTEN WHENEVER RUSH IS UPGRADED. +// +// This script is intended for usage in an automated build environment where a Node tool may not have +// been preinstalled, or may have an unpredictable version. This script will automatically install the specified +// version of the specified tool (if not already installed), and then pass a command-line to it. +// An example usage would be: +// +// node common/scripts/install-run.js qrcode@1.2.2 qrcode https://rushjs.io +// +// For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ + +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ 679877: +/*!************************************************!*\ + !*** ./lib-esnext/utilities/npmrcUtilities.js ***! + \************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "isVariableSetInNpmrcFile": () => (/* binding */ isVariableSetInNpmrcFile), +/* harmony export */ "syncNpmrc": () => (/* binding */ syncNpmrc) +/* harmony export */ }); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! fs */ 657147); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! path */ 371017); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. +// IMPORTANT - do not use any non-built-in libraries in this file + + +/** + * This function reads the content for given .npmrc file path, and also trims + * unusable lines from the .npmrc file. + * + * @returns + * The text of the the .npmrc. + */ +// create a global _combinedNpmrc for cache purpose +const _combinedNpmrcMap = new Map(); +function _trimNpmrcFile(sourceNpmrcPath, extraLines = []) { + const combinedNpmrcFromCache = _combinedNpmrcMap.get(sourceNpmrcPath); + if (combinedNpmrcFromCache !== undefined) { + return combinedNpmrcFromCache; + } + let npmrcFileLines = fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(sourceNpmrcPath).toString().split('\n'); + npmrcFileLines.push(...extraLines); + npmrcFileLines = npmrcFileLines.map((line) => (line || '').trim()); + const resultLines = []; + // This finds environment variable tokens that look like "${VAR_NAME}" + const expansionRegExp = /\$\{([^\}]+)\}/g; + // Comment lines start with "#" or ";" + const commentRegExp = /^\s*[#;]/; + // Trim out lines that reference environment variables that aren't defined + for (let line of npmrcFileLines) { + let lineShouldBeTrimmed = false; + //remove spaces before or after key and value + line = line + .split('=') + .map((lineToTrim) => lineToTrim.trim()) + .join('='); + // Ignore comment lines + if (!commentRegExp.test(line)) { + const environmentVariables = line.match(expansionRegExp); + if (environmentVariables) { + for (const token of environmentVariables) { + // Remove the leading "${" and the trailing "}" from the token + const environmentVariableName = token.substring(2, token.length - 1); + // Is the environment variable defined? + if (!process.env[environmentVariableName]) { + // No, so trim this line + lineShouldBeTrimmed = true; + break; + } + } + } + } + if (lineShouldBeTrimmed) { + // Example output: + // "; MISSING ENVIRONMENT VARIABLE: //my-registry.com/npm/:_authToken=${MY_AUTH_TOKEN}" + resultLines.push('; MISSING ENVIRONMENT VARIABLE: ' + line); + } + else { + resultLines.push(line); + } + } + const combinedNpmrc = resultLines.join('\n'); + //save the cache + _combinedNpmrcMap.set(sourceNpmrcPath, combinedNpmrc); + return combinedNpmrc; +} +/** + * As a workaround, copyAndTrimNpmrcFile() copies the .npmrc file to the target folder, and also trims + * unusable lines from the .npmrc file. + * + * Why are we trimming the .npmrc lines? NPM allows environment variables to be specified in + * the .npmrc file to provide different authentication tokens for different registry. + * However, if the environment variable is undefined, it expands to an empty string, which + * produces a valid-looking mapping with an invalid URL that causes an error. Instead, + * we'd prefer to skip that line and continue looking in other places such as the user's + * home directory. + * + * @returns + * The text of the the .npmrc with lines containing undefined variables commented out. + */ +function _copyAndTrimNpmrcFile(logger, sourceNpmrcPath, targetNpmrcPath, extraLines) { + logger.info(`Transforming ${sourceNpmrcPath}`); // Verbose + logger.info(` --> "${targetNpmrcPath}"`); + const combinedNpmrc = _trimNpmrcFile(sourceNpmrcPath, extraLines); + fs__WEBPACK_IMPORTED_MODULE_0__.writeFileSync(targetNpmrcPath, combinedNpmrc); + return combinedNpmrc; +} +/** + * syncNpmrc() copies the .npmrc file to the target folder, and also trims unusable lines from the .npmrc file. + * If the source .npmrc file not exist, then syncNpmrc() will delete an .npmrc that is found in the target folder. + * + * IMPORTANT: THIS CODE SHOULD BE KEPT UP TO DATE WITH Utilities._syncNpmrc() + * + * @returns + * The text of the the synced .npmrc, if one exists. If one does not exist, then undefined is returned. + */ +function syncNpmrc(sourceNpmrcFolder, targetNpmrcFolder, useNpmrcPublish, logger = { + // eslint-disable-next-line no-console + info: console.log, + // eslint-disable-next-line no-console + error: console.error +}, extraLines) { + const sourceNpmrcPath = path__WEBPACK_IMPORTED_MODULE_1__.join(sourceNpmrcFolder, !useNpmrcPublish ? '.npmrc' : '.npmrc-publish'); + const targetNpmrcPath = path__WEBPACK_IMPORTED_MODULE_1__.join(targetNpmrcFolder, '.npmrc'); + try { + if (fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(sourceNpmrcPath)) { + // Ensure the target folder exists + if (!fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(targetNpmrcFolder)) { + fs__WEBPACK_IMPORTED_MODULE_0__.mkdirSync(targetNpmrcFolder, { recursive: true }); + } + return _copyAndTrimNpmrcFile(logger, sourceNpmrcPath, targetNpmrcPath, extraLines); + } + else if (fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(targetNpmrcPath)) { + // If the source .npmrc doesn't exist and there is one in the target, delete the one in the target + logger.info(`Deleting ${targetNpmrcPath}`); // Verbose + fs__WEBPACK_IMPORTED_MODULE_0__.unlinkSync(targetNpmrcPath); + } + } + catch (e) { + throw new Error(`Error syncing .npmrc file: ${e}`); + } +} +function isVariableSetInNpmrcFile(sourceNpmrcFolder, variableKey) { + const sourceNpmrcPath = `${sourceNpmrcFolder}/.npmrc`; + //if .npmrc file does not exist, return false directly + if (!fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(sourceNpmrcPath)) { + return false; + } + const trimmedNpmrcFile = _trimNpmrcFile(sourceNpmrcPath); + const variableKeyRegExp = new RegExp(`^${variableKey}=`, 'm'); + return trimmedNpmrcFile.match(variableKeyRegExp) !== null; +} +//# sourceMappingURL=npmrcUtilities.js.map + +/***/ }), + +/***/ 532081: +/*!********************************!*\ + !*** external "child_process" ***! + \********************************/ +/***/ ((module) => { + +module.exports = require("child_process"); + +/***/ }), + +/***/ 657147: +/*!*********************!*\ + !*** external "fs" ***! + \*********************/ +/***/ ((module) => { + +module.exports = require("fs"); + +/***/ }), + +/***/ 822037: +/*!*********************!*\ + !*** external "os" ***! + \*********************/ +/***/ ((module) => { + +module.exports = require("os"); + +/***/ }), + +/***/ 371017: +/*!***********************!*\ + !*** external "path" ***! + \***********************/ +/***/ ((module) => { + +module.exports = require("path"); + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ (() => { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = (module) => { +/******/ var getter = module && module.__esModule ? +/******/ () => (module['default']) : +/******/ () => (module); +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +(() => { +/*!*******************************************!*\ + !*** ./lib-esnext/scripts/install-run.js ***! + \*******************************************/ +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "RUSH_JSON_FILENAME": () => (/* binding */ RUSH_JSON_FILENAME), +/* harmony export */ "findRushJsonFolder": () => (/* binding */ findRushJsonFolder), +/* harmony export */ "getNpmPath": () => (/* binding */ getNpmPath), +/* harmony export */ "installAndRun": () => (/* binding */ installAndRun), +/* harmony export */ "runWithErrorAndStatusCode": () => (/* binding */ runWithErrorAndStatusCode) +/* harmony export */ }); +/* harmony import */ var child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! child_process */ 532081); +/* harmony import */ var child_process__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(child_process__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! fs */ 657147); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var os__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! os */ 822037); +/* harmony import */ var os__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(os__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! path */ 371017); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var _utilities_npmrcUtilities__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../utilities/npmrcUtilities */ 679877); +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. +/* eslint-disable no-console */ + + + + + +const RUSH_JSON_FILENAME = 'rush.json'; +const RUSH_TEMP_FOLDER_ENV_VARIABLE_NAME = 'RUSH_TEMP_FOLDER'; +const INSTALL_RUN_LOCKFILE_PATH_VARIABLE = 'INSTALL_RUN_LOCKFILE_PATH'; +const INSTALLED_FLAG_FILENAME = 'installed.flag'; +const NODE_MODULES_FOLDER_NAME = 'node_modules'; +const PACKAGE_JSON_FILENAME = 'package.json'; +/** + * Parse a package specifier (in the form of name\@version) into name and version parts. + */ +function _parsePackageSpecifier(rawPackageSpecifier) { + rawPackageSpecifier = (rawPackageSpecifier || '').trim(); + const separatorIndex = rawPackageSpecifier.lastIndexOf('@'); + let name; + let version = undefined; + if (separatorIndex === 0) { + // The specifier starts with a scope and doesn't have a version specified + name = rawPackageSpecifier; + } + else if (separatorIndex === -1) { + // The specifier doesn't have a version + name = rawPackageSpecifier; + } + else { + name = rawPackageSpecifier.substring(0, separatorIndex); + version = rawPackageSpecifier.substring(separatorIndex + 1); + } + if (!name) { + throw new Error(`Invalid package specifier: ${rawPackageSpecifier}`); + } + return { name, version }; +} +let _npmPath = undefined; +/** + * Get the absolute path to the npm executable + */ +function getNpmPath() { + if (!_npmPath) { + try { + if (os__WEBPACK_IMPORTED_MODULE_2__.platform() === 'win32') { + // We're on Windows + const whereOutput = child_process__WEBPACK_IMPORTED_MODULE_0__.execSync('where npm', { stdio: [] }).toString(); + const lines = whereOutput.split(os__WEBPACK_IMPORTED_MODULE_2__.EOL).filter((line) => !!line); + // take the last result, we are looking for a .cmd command + // see https://github.com/microsoft/rushstack/issues/759 + _npmPath = lines[lines.length - 1]; + } + else { + // We aren't on Windows - assume we're on *NIX or Darwin + _npmPath = child_process__WEBPACK_IMPORTED_MODULE_0__.execSync('command -v npm', { stdio: [] }).toString(); + } + } + catch (e) { + throw new Error(`Unable to determine the path to the NPM tool: ${e}`); + } + _npmPath = _npmPath.trim(); + if (!fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(_npmPath)) { + throw new Error('The NPM executable does not exist'); + } + } + return _npmPath; +} +function _ensureFolder(folderPath) { + if (!fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(folderPath)) { + const parentDir = path__WEBPACK_IMPORTED_MODULE_3__.dirname(folderPath); + _ensureFolder(parentDir); + fs__WEBPACK_IMPORTED_MODULE_1__.mkdirSync(folderPath); + } +} +/** + * Create missing directories under the specified base directory, and return the resolved directory. + * + * Does not support "." or ".." path segments. + * Assumes the baseFolder exists. + */ +function _ensureAndJoinPath(baseFolder, ...pathSegments) { + let joinedPath = baseFolder; + try { + for (let pathSegment of pathSegments) { + pathSegment = pathSegment.replace(/[\\\/]/g, '+'); + joinedPath = path__WEBPACK_IMPORTED_MODULE_3__.join(joinedPath, pathSegment); + if (!fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(joinedPath)) { + fs__WEBPACK_IMPORTED_MODULE_1__.mkdirSync(joinedPath); + } + } + } + catch (e) { + throw new Error(`Error building local installation folder (${path__WEBPACK_IMPORTED_MODULE_3__.join(baseFolder, ...pathSegments)}): ${e}`); + } + return joinedPath; +} +function _getRushTempFolder(rushCommonFolder) { + const rushTempFolder = process.env[RUSH_TEMP_FOLDER_ENV_VARIABLE_NAME]; + if (rushTempFolder !== undefined) { + _ensureFolder(rushTempFolder); + return rushTempFolder; + } + else { + return _ensureAndJoinPath(rushCommonFolder, 'temp'); + } +} +/** + * Compare version strings according to semantic versioning. + * Returns a positive integer if "a" is a later version than "b", + * a negative integer if "b" is later than "a", + * and 0 otherwise. + */ +function _compareVersionStrings(a, b) { + const aParts = a.split(/[.-]/); + const bParts = b.split(/[.-]/); + const numberOfParts = Math.max(aParts.length, bParts.length); + for (let i = 0; i < numberOfParts; i++) { + if (aParts[i] !== bParts[i]) { + return (Number(aParts[i]) || 0) - (Number(bParts[i]) || 0); + } + } + return 0; +} +/** + * Resolve a package specifier to a static version + */ +function _resolvePackageVersion(logger, rushCommonFolder, { name, version }) { + if (!version) { + version = '*'; // If no version is specified, use the latest version + } + if (version.match(/^[a-zA-Z0-9\-\+\.]+$/)) { + // If the version contains only characters that we recognize to be used in static version specifiers, + // pass the version through + return version; + } + else { + // version resolves to + try { + const rushTempFolder = _getRushTempFolder(rushCommonFolder); + const sourceNpmrcFolder = path__WEBPACK_IMPORTED_MODULE_3__.join(rushCommonFolder, 'config', 'rush'); + (0,_utilities_npmrcUtilities__WEBPACK_IMPORTED_MODULE_4__.syncNpmrc)(sourceNpmrcFolder, rushTempFolder, undefined, logger); + const npmPath = getNpmPath(); + // This returns something that looks like: + // ``` + // [ + // "3.0.0", + // "3.0.1", + // ... + // "3.0.20" + // ] + // ``` + // + // if multiple versions match the selector, or + // + // ``` + // "3.0.0" + // ``` + // + // if only a single version matches. + const npmVersionSpawnResult = child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync(npmPath, ['view', `${name}@${version}`, 'version', '--no-update-notifier', '--json'], { + cwd: rushTempFolder, + stdio: [] + }); + if (npmVersionSpawnResult.status !== 0) { + throw new Error(`"npm view" returned error code ${npmVersionSpawnResult.status}`); + } + const npmViewVersionOutput = npmVersionSpawnResult.stdout.toString(); + const parsedVersionOutput = JSON.parse(npmViewVersionOutput); + const versions = Array.isArray(parsedVersionOutput) + ? parsedVersionOutput + : [parsedVersionOutput]; + let latestVersion = versions[0]; + for (let i = 1; i < versions.length; i++) { + const latestVersionCandidate = versions[i]; + if (_compareVersionStrings(latestVersionCandidate, latestVersion) > 0) { + latestVersion = latestVersionCandidate; + } + } + if (!latestVersion) { + throw new Error('No versions found for the specified version range.'); + } + return latestVersion; + } + catch (e) { + throw new Error(`Unable to resolve version ${version} of package ${name}: ${e}`); + } + } +} +let _rushJsonFolder; +/** + * Find the absolute path to the folder containing rush.json + */ +function findRushJsonFolder() { + if (!_rushJsonFolder) { + let basePath = __dirname; + let tempPath = __dirname; + do { + const testRushJsonPath = path__WEBPACK_IMPORTED_MODULE_3__.join(basePath, RUSH_JSON_FILENAME); + if (fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(testRushJsonPath)) { + _rushJsonFolder = basePath; + break; + } + else { + basePath = tempPath; + } + } while (basePath !== (tempPath = path__WEBPACK_IMPORTED_MODULE_3__.dirname(basePath))); // Exit the loop when we hit the disk root + if (!_rushJsonFolder) { + throw new Error(`Unable to find ${RUSH_JSON_FILENAME}.`); + } + } + return _rushJsonFolder; +} +/** + * Detects if the package in the specified directory is installed + */ +function _isPackageAlreadyInstalled(packageInstallFolder) { + try { + const flagFilePath = path__WEBPACK_IMPORTED_MODULE_3__.join(packageInstallFolder, INSTALLED_FLAG_FILENAME); + if (!fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(flagFilePath)) { + return false; + } + const fileContents = fs__WEBPACK_IMPORTED_MODULE_1__.readFileSync(flagFilePath).toString(); + return fileContents.trim() === process.version; + } + catch (e) { + return false; + } +} +/** + * Delete a file. Fail silently if it does not exist. + */ +function _deleteFile(file) { + try { + fs__WEBPACK_IMPORTED_MODULE_1__.unlinkSync(file); + } + catch (err) { + if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') { + throw err; + } + } +} +/** + * Removes the following files and directories under the specified folder path: + * - installed.flag + * - + * - node_modules + */ +function _cleanInstallFolder(rushTempFolder, packageInstallFolder, lockFilePath) { + try { + const flagFile = path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, INSTALLED_FLAG_FILENAME); + _deleteFile(flagFile); + const packageLockFile = path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, 'package-lock.json'); + if (lockFilePath) { + fs__WEBPACK_IMPORTED_MODULE_1__.copyFileSync(lockFilePath, packageLockFile); + } + else { + // Not running `npm ci`, so need to cleanup + _deleteFile(packageLockFile); + const nodeModulesFolder = path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, NODE_MODULES_FOLDER_NAME); + if (fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(nodeModulesFolder)) { + const rushRecyclerFolder = _ensureAndJoinPath(rushTempFolder, 'rush-recycler'); + fs__WEBPACK_IMPORTED_MODULE_1__.renameSync(nodeModulesFolder, path__WEBPACK_IMPORTED_MODULE_3__.join(rushRecyclerFolder, `install-run-${Date.now().toString()}`)); + } + } + } + catch (e) { + throw new Error(`Error cleaning the package install folder (${packageInstallFolder}): ${e}`); + } +} +function _createPackageJson(packageInstallFolder, name, version) { + try { + const packageJsonContents = { + name: 'ci-rush', + version: '0.0.0', + dependencies: { + [name]: version + }, + description: "DON'T WARN", + repository: "DON'T WARN", + license: 'MIT' + }; + const packageJsonPath = path__WEBPACK_IMPORTED_MODULE_3__.join(packageInstallFolder, PACKAGE_JSON_FILENAME); + fs__WEBPACK_IMPORTED_MODULE_1__.writeFileSync(packageJsonPath, JSON.stringify(packageJsonContents, undefined, 2)); + } + catch (e) { + throw new Error(`Unable to create package.json: ${e}`); + } +} +/** + * Run "npm install" in the package install folder. + */ +function _installPackage(logger, packageInstallFolder, name, version, command) { + try { + logger.info(`Installing ${name}...`); + const npmPath = getNpmPath(); + const result = child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync(npmPath, [command], { + stdio: 'inherit', + cwd: packageInstallFolder, + env: process.env + }); + if (result.status !== 0) { + throw new Error(`"npm ${command}" encountered an error`); + } + logger.info(`Successfully installed ${name}@${version}`); + } + catch (e) { + throw new Error(`Unable to install package: ${e}`); + } +} +/** + * Get the ".bin" path for the package. + */ +function _getBinPath(packageInstallFolder, binName) { + const binFolderPath = path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, NODE_MODULES_FOLDER_NAME, '.bin'); + const resolvedBinName = os__WEBPACK_IMPORTED_MODULE_2__.platform() === 'win32' ? `${binName}.cmd` : binName; + return path__WEBPACK_IMPORTED_MODULE_3__.resolve(binFolderPath, resolvedBinName); +} +/** + * Write a flag file to the package's install directory, signifying that the install was successful. + */ +function _writeFlagFile(packageInstallFolder) { + try { + const flagFilePath = path__WEBPACK_IMPORTED_MODULE_3__.join(packageInstallFolder, INSTALLED_FLAG_FILENAME); + fs__WEBPACK_IMPORTED_MODULE_1__.writeFileSync(flagFilePath, process.version); + } + catch (e) { + throw new Error(`Unable to create installed.flag file in ${packageInstallFolder}`); + } +} +function installAndRun(logger, packageName, packageVersion, packageBinName, packageBinArgs, lockFilePath = process.env[INSTALL_RUN_LOCKFILE_PATH_VARIABLE]) { + const rushJsonFolder = findRushJsonFolder(); + const rushCommonFolder = path__WEBPACK_IMPORTED_MODULE_3__.join(rushJsonFolder, 'common'); + const rushTempFolder = _getRushTempFolder(rushCommonFolder); + const packageInstallFolder = _ensureAndJoinPath(rushTempFolder, 'install-run', `${packageName}@${packageVersion}`); + if (!_isPackageAlreadyInstalled(packageInstallFolder)) { + // The package isn't already installed + _cleanInstallFolder(rushTempFolder, packageInstallFolder, lockFilePath); + const sourceNpmrcFolder = path__WEBPACK_IMPORTED_MODULE_3__.join(rushCommonFolder, 'config', 'rush'); + (0,_utilities_npmrcUtilities__WEBPACK_IMPORTED_MODULE_4__.syncNpmrc)(sourceNpmrcFolder, packageInstallFolder, undefined, logger); + _createPackageJson(packageInstallFolder, packageName, packageVersion); + const command = lockFilePath ? 'ci' : 'install'; + _installPackage(logger, packageInstallFolder, packageName, packageVersion, command); + _writeFlagFile(packageInstallFolder); + } + const statusMessage = `Invoking "${packageBinName} ${packageBinArgs.join(' ')}"`; + const statusMessageLine = new Array(statusMessage.length + 1).join('-'); + logger.info('\n' + statusMessage + '\n' + statusMessageLine + '\n'); + const binPath = _getBinPath(packageInstallFolder, packageBinName); + const binFolderPath = path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, NODE_MODULES_FOLDER_NAME, '.bin'); + // Windows environment variables are case-insensitive. Instead of using SpawnSyncOptions.env, we need to + // assign via the process.env proxy to ensure that we append to the right PATH key. + const originalEnvPath = process.env.PATH || ''; + let result; + try { + // Node.js on Windows can not spawn a file when the path has a space on it + // unless the path gets wrapped in a cmd friendly way and shell mode is used + const shouldUseShell = binPath.includes(' ') && os__WEBPACK_IMPORTED_MODULE_2__.platform() === 'win32'; + const platformBinPath = shouldUseShell ? `"${binPath}"` : binPath; + process.env.PATH = [binFolderPath, originalEnvPath].join(path__WEBPACK_IMPORTED_MODULE_3__.delimiter); + result = child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync(platformBinPath, packageBinArgs, { + stdio: 'inherit', + windowsVerbatimArguments: false, + shell: shouldUseShell, + cwd: process.cwd(), + env: process.env + }); + } + finally { + process.env.PATH = originalEnvPath; + } + if (result.status !== null) { + return result.status; + } + else { + throw result.error || new Error('An unknown error occurred.'); + } +} +function runWithErrorAndStatusCode(logger, fn) { + process.exitCode = 1; + try { + const exitCode = fn(); + process.exitCode = exitCode; + } + catch (e) { + logger.error('\n\n' + e.toString() + '\n\n'); + } +} +function _run() { + const [nodePath /* Ex: /bin/node */, scriptPath /* /repo/common/scripts/install-run-rush.js */, rawPackageSpecifier /* qrcode@^1.2.0 */, packageBinName /* qrcode */, ...packageBinArgs /* [-f, myproject/lib] */] = process.argv; + if (!nodePath) { + throw new Error('Unexpected exception: could not detect node path'); + } + if (path__WEBPACK_IMPORTED_MODULE_3__.basename(scriptPath).toLowerCase() !== 'install-run.js') { + // If install-run.js wasn't directly invoked, don't execute the rest of this function. Return control + // to the script that (presumably) imported this file + return; + } + if (process.argv.length < 4) { + console.log('Usage: install-run.js @ [args...]'); + console.log('Example: install-run.js qrcode@1.2.2 qrcode https://rushjs.io'); + process.exit(1); + } + const logger = { info: console.log, error: console.error }; + runWithErrorAndStatusCode(logger, () => { + const rushJsonFolder = findRushJsonFolder(); + const rushCommonFolder = _ensureAndJoinPath(rushJsonFolder, 'common'); + const packageSpecifier = _parsePackageSpecifier(rawPackageSpecifier); + const name = packageSpecifier.name; + const version = _resolvePackageVersion(logger, rushCommonFolder, packageSpecifier); + if (packageSpecifier.version !== version) { + console.log(`Resolved to ${name}@${version}`); + } + return installAndRun(logger, name, version, packageBinName, packageBinArgs); + }); +} +_run(); +//# sourceMappingURL=install-run.js.map +})(); + +module.exports = __webpack_exports__; +/******/ })() +; +//# sourceMappingURL=install-run.js.map \ No newline at end of file diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/a/config/rush-project.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/a/config/rush-project.json new file mode 100644 index 00000000000..ef7e47275c6 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/a/config/rush-project.json @@ -0,0 +1,12 @@ +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["dist"] + }, + { + "operationName": "cobuild", + "outputFolderNames": ["dist"] + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/a/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/a/package.json new file mode 100644 index 00000000000..98957112d5e --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/a/package.json @@ -0,0 +1,10 @@ +{ + "name": "a", + "version": "1.0.0", + "scripts": { + "cobuild": "node ../build.js a", + "build": "node ../build.js a", + "__phase:build": "exit 1", + "_phase:build": "node ../build.js a" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/b/config/rush-project.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/b/config/rush-project.json new file mode 100644 index 00000000000..ef7e47275c6 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/b/config/rush-project.json @@ -0,0 +1,12 @@ +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["dist"] + }, + { + "operationName": "cobuild", + "outputFolderNames": ["dist"] + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/b/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/b/package.json new file mode 100644 index 00000000000..8b17917b744 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/b/package.json @@ -0,0 +1,9 @@ +{ + "name": "b", + "version": "1.0.0", + "scripts": { + "cobuild": "node ../build.js", + "build": "node ../build.js", + "_phase:build": "node ../build.js b" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/build.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/build.js new file mode 100644 index 00000000000..15441feef29 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/build.js @@ -0,0 +1,14 @@ +/* eslint-env es6 */ +const path = require('path'); +const { FileSystem } = require('@rushstack/node-core-library'); + +const args = process.argv.slice(2); + +console.log('start', args.join(' ')); +setTimeout(() => { + const outputFolder = path.resolve(process.cwd(), 'dist'); + const outputFile = path.resolve(outputFolder, 'output.txt'); + FileSystem.ensureFolder(outputFolder); + FileSystem.writeFile(outputFile, `Hello world! ${args.join(' ')}`); + console.log('done'); +}, 2000); diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/c/config/rush-project.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/c/config/rush-project.json new file mode 100644 index 00000000000..ef7e47275c6 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/c/config/rush-project.json @@ -0,0 +1,12 @@ +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["dist"] + }, + { + "operationName": "cobuild", + "outputFolderNames": ["dist"] + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/c/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/c/package.json new file mode 100644 index 00000000000..738c1444ab0 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/c/package.json @@ -0,0 +1,12 @@ +{ + "name": "c", + "version": "1.0.0", + "scripts": { + "cobuild": "node ../build.js", + "build": "node ../build.js", + "_phase:build": "node ../build.js" + }, + "dependencies": { + "b": "workspace:*" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/d/config/rush-project.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/d/config/rush-project.json new file mode 100644 index 00000000000..ef7e47275c6 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/d/config/rush-project.json @@ -0,0 +1,12 @@ +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["dist"] + }, + { + "operationName": "cobuild", + "outputFolderNames": ["dist"] + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/d/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/d/package.json new file mode 100644 index 00000000000..67707275a1e --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/d/package.json @@ -0,0 +1,13 @@ +{ + "name": "d", + "version": "1.0.0", + "scripts": { + "cobuild": "node ../build.js", + "build": "node ../build.js", + "_phase:build": "node ../build.js" + }, + "dependencies": { + "b": "workspace:*", + "c": "workspace:*" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/e/config/rush-project.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/e/config/rush-project.json new file mode 100644 index 00000000000..ef7e47275c6 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/e/config/rush-project.json @@ -0,0 +1,12 @@ +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["dist"] + }, + { + "operationName": "cobuild", + "outputFolderNames": ["dist"] + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/e/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/e/package.json new file mode 100644 index 00000000000..0b91c05a805 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/e/package.json @@ -0,0 +1,13 @@ +{ + "name": "e", + "version": "1.0.0", + "scripts": { + "cobuild": "node ../build.js", + "build": "node ../build.js", + "_phase:build": "node ../build.js" + }, + "dependencies": { + "b": "workspace:*", + "d": "workspace:*" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/f/config/rush-project.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/f/config/rush-project.json new file mode 100644 index 00000000000..b2e4e3206a4 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/f/config/rush-project.json @@ -0,0 +1,13 @@ +{ + "disableBuildCacheForProject": true, + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["dist"] + }, + { + "operationName": "cobuild", + "outputFolderNames": ["dist"] + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/f/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/f/package.json new file mode 100644 index 00000000000..7bf2634a508 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/f/package.json @@ -0,0 +1,13 @@ +{ + "name": "f", + "version": "1.0.0", + "scripts": { + "cobuild": "node ../build.js", + "build": "node ../build.js", + "_phase:pre-build": "node ../pre-build.js", + "_phase:build": "node ../validate-pre-build.js && node ../build.js f" + }, + "dependencies": { + "b": "workspace:*" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/g/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/g/package.json new file mode 100644 index 00000000000..29cd2f39532 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/g/package.json @@ -0,0 +1,13 @@ +{ + "name": "g", + "version": "1.0.0", + "scripts": { + "cobuild": "node ../build.js", + "build": "node ../build.js", + "_phase:pre-build": "node ../pre-build.js", + "_phase:build": "node ../validate-pre-build.js && node ../build.js g" + }, + "dependencies": { + "b": "workspace:*" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/h/config/rush-project.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/h/config/rush-project.json new file mode 100644 index 00000000000..ef7e47275c6 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/h/config/rush-project.json @@ -0,0 +1,12 @@ +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["dist"] + }, + { + "operationName": "cobuild", + "outputFolderNames": ["dist"] + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/h/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/h/package.json new file mode 100644 index 00000000000..cb74fb60a7d --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/h/package.json @@ -0,0 +1,12 @@ +{ + "name": "h", + "version": "1.0.0", + "scripts": { + "cobuild": "", + "build": "", + "_phase:build": "" + }, + "dependencies": { + "a": "workspace:*" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/pre-build.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/pre-build.js new file mode 100644 index 00000000000..4d9f43e7afa --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/pre-build.js @@ -0,0 +1,11 @@ +/* eslint-env es6 */ +const path = require('path'); +const { FileSystem } = require('@rushstack/node-core-library'); + +setTimeout(() => { + const outputFolder = path.resolve(process.cwd(), 'dist'); + const outputFile = path.resolve(outputFolder, 'pre-build'); + FileSystem.ensureFolder(outputFolder); + FileSystem.writeFile(outputFile, `Hello world!`); + console.log('done'); +}, 2000); diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/validate-pre-build.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/validate-pre-build.js new file mode 100644 index 00000000000..218484000e3 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/projects/validate-pre-build.js @@ -0,0 +1,13 @@ +/* eslint-env es6 */ +const path = require('path'); +const { FileSystem } = require('@rushstack/node-core-library'); + +const outputFolder = path.resolve(process.cwd(), 'dist'); +const outputFile = path.resolve(outputFolder, 'pre-build'); + +if (!FileSystem.exists(outputFile)) { + console.error(`${outputFile} does not exist.`); + process.exit(1); +} + +console.log(`${outputFile} exists`); diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/rush.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/rush.json new file mode 100644 index 00000000000..012281bea0d --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/repo/rush.json @@ -0,0 +1,41 @@ +{ + "rushVersion": "5.80.0", + "pnpmVersion": "7.13.0", + "pnpmOptions": { + "useWorkspaces": true + }, + "projects": [ + { + "packageName": "a", + "projectFolder": "projects/a" + }, + { + "packageName": "b", + "projectFolder": "projects/b" + }, + { + "packageName": "c", + "projectFolder": "projects/c" + }, + { + "packageName": "d", + "projectFolder": "projects/d" + }, + { + "packageName": "e", + "projectFolder": "projects/e" + }, + { + "packageName": "f", + "projectFolder": "projects/f" + }, + { + "packageName": "g", + "projectFolder": "projects/g" + }, + { + "packageName": "h", + "projectFolder": "projects/h" + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/.gitignore b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/.gitignore new file mode 100644 index 00000000000..f41ed442681 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/.gitignore @@ -0,0 +1,8 @@ +# Rush temporary files +common/deploy/ +common/temp/ +common/autoinstallers/*/.npmrc +projects/*/dist/ +*.log +node_modules/ +.rush diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush-plugins/rush-redis-cobuild-plugin.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush-plugins/rush-redis-cobuild-plugin.json new file mode 100644 index 00000000000..c27270adc35 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush-plugins/rush-redis-cobuild-plugin.json @@ -0,0 +1,4 @@ +{ + "url": "redis://localhost:6379", + "passwordEnvironmentVariable": "REDIS_PASS" +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/build-cache.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/build-cache.json new file mode 100644 index 00000000000..d09eaa6a04c --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/build-cache.json @@ -0,0 +1,92 @@ +/** + * This configuration file manages Rush's build cache feature. + * More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/build-cache.schema.json", + + /** + * (Required) EXPERIMENTAL - Set this to true to enable the build cache feature. + * + * See https://rushjs.io/pages/maintainer/build_cache/ for details about this experimental feature. + */ + "buildCacheEnabled": true, + + /** + * (Required) Choose where project build outputs will be cached. + * + * Possible values: "local-only", "azure-blob-storage", "amazon-s3" + */ + "cacheProvider": "local-only", + + /** + * Setting this property overrides the cache entry ID. If this property is set, it must contain + * a [hash] token. + * + * Other available tokens: + * - [projectName] + * - [projectName:normalize] + * - [phaseName] + * - [phaseName:normalize] + * - [phaseName:trimPrefix] + */ + // "cacheEntryNamePattern": "[projectName:normalize]-[phaseName:normalize]-[hash]" + + /** + * Use this configuration with "cacheProvider"="azure-blob-storage" + */ + "azureBlobStorageConfiguration": { + /** + * (Required) The name of the the Azure storage account to use for build cache. + */ + // "storageAccountName": "example", + /** + * (Required) The name of the container in the Azure storage account to use for build cache. + */ + // "storageContainerName": "my-container", + /** + * The Azure environment the storage account exists in. Defaults to AzurePublicCloud. + * + * Possible values: "AzurePublicCloud", "AzureChina", "AzureGermany", "AzureGovernment" + */ + // "azureEnvironment": "AzurePublicCloud", + /** + * An optional prefix for cache item blob names. + */ + // "blobPrefix": "my-prefix", + /** + * If set to true, allow writing to the cache. Defaults to false. + */ + // "isCacheWriteAllowed": true + }, + + /** + * Use this configuration with "cacheProvider"="amazon-s3" + */ + "amazonS3Configuration": { + /** + * (Required unless s3Endpoint is specified) The name of the bucket to use for build cache. + * Example: "my-bucket" + */ + // "s3Bucket": "my-bucket", + /** + * (Required unless s3Bucket is specified) The Amazon S3 endpoint of the bucket to use for build cache. + * This should not include any path; use the s3Prefix to set the path. + * Examples: "my-bucket.s3.us-east-2.amazonaws.com" or "http://localhost:9000" + */ + // "s3Endpoint": "https://my-bucket.s3.us-east-2.amazonaws.com", + /** + * (Required) The Amazon S3 region of the bucket to use for build cache. + * Example: "us-east-1" + */ + // "s3Region": "us-east-1", + /** + * An optional prefix ("folder") for cache items. It should not start with "/". + */ + // "s3Prefix": "my-prefix", + /** + * If set to true, allow writing to the cache. Defaults to false. + */ + // "isCacheWriteAllowed": true + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/cobuild.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/cobuild.json new file mode 100644 index 00000000000..4626f2211d4 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/cobuild.json @@ -0,0 +1,22 @@ +/** + * This configuration file manages Rush's cobuild feature. + * More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/cobuild.schema.json", + + /** + * (Required) EXPERIMENTAL - Set this to true to enable the cobuild feature. + * RUSH_COBUILD_CONTEXT_ID should always be specified as an environment variable with an non-empty string, + * otherwise the cobuild feature will be disabled. + */ + "cobuildFeatureEnabled": true, + + /** + * (Required) Choose where cobuild lock will be acquired. + * + * The lock provider is registered by the rush plugins. + * For example, @rushstack/rush-redis-cobuild-plugin registers the "redis" lock provider. + */ + "cobuildLockProvider": "redis" +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/command-line.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/command-line.json new file mode 100644 index 00000000000..c8c1ccc022d --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/command-line.json @@ -0,0 +1,336 @@ +/** + * This configuration file defines custom commands for the "rush" command-line. + * More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json", + + /** + * Custom "commands" introduce new verbs for the command-line. To see the help for these + * example commands, try "rush --help", "rush my-bulk-command --help", or + * "rush my-global-command --help". + */ + "commands": [ + { + "commandKind": "phased", + "summary": "Concurrent version of rush build", + "name": "cobuild", + "safeForSimultaneousRushProcesses": true, + "enableParallelism": true, + "incremental": true, + "phases": ["_phase:pre-build", "_phase:build"] + } + + // { + // /** + // * (Required) Determines the type of custom command. + // * Rush's "bulk" commands are invoked separately for each project. Rush will look in + // * each project's package.json file for a "scripts" entry whose name matches the + // * command name. By default, the command will run for every project in the repo, + // * according to the dependency graph (similar to how "rush build" works). + // * The set of projects can be restricted e.g. using the "--to" or "--from" parameters. + // */ + // "commandKind": "bulk", + // + // /** + // * (Required) The name that will be typed as part of the command line. This is also the name + // * of the "scripts" hook in the project's package.json file. + // * The name should be comprised of lower case words separated by hyphens or colons. The name should include an + // * English verb (e.g. "deploy"). Use a hyphen to separate words (e.g. "upload-docs"). A group of related commands + // * can be prefixed with a colon (e.g. "docs:generate", "docs:deploy", "docs:serve", etc). + // * + // * Note that if the "rebuild" command is overridden here, it becomes separated from the "build" command + // * and will call the "rebuild" script instead of the "build" script. + // */ + // "name": "my-bulk-command", + // + // /** + // * (Required) A short summary of the custom command to be shown when printing command line + // * help, e.g. "rush --help". + // */ + // "summary": "Example bulk custom command", + // + // /** + // * A detailed description of the command to be shown when printing command line + // * help (e.g. "rush --help my-command"). + // * If omitted, the "summary" text will be shown instead. + // * + // * Whenever you introduce commands/parameters, taking a little time to write meaningful + // * documentation can make a big difference for the developer experience in your repo. + // */ + // "description": "This is an example custom command that runs separately for each project", + // + // /** + // * By default, Rush operations acquire a lock file which prevents multiple commands from executing simultaneously + // * in the same repo folder. (For example, it would be a mistake to run "rush install" and "rush build" at the + // * same time.) If your command makes sense to run concurrently with other operations, + // * set "safeForSimultaneousRushProcesses" to true to disable this protection. + // * + // * In particular, this is needed for custom scripts that invoke other Rush commands. + // */ + // "safeForSimultaneousRushProcesses": false, + // + // /** + // * (Required) If true, then this command is safe to be run in parallel, i.e. executed + // * simultaneously for multiple projects. Similar to "rush build", regardless of parallelism + // * projects will not start processing until their dependencies have completed processing. + // */ + // "enableParallelism": false, + // + // /** + // * Normally projects will be processed according to their dependency order: a given project will not start + // * processing the command until all of its dependencies have completed. This restriction doesn't apply for + // * certain operations, for example a "clean" task that deletes output files. In this case + // * you can set "ignoreDependencyOrder" to true to increase parallelism. + // */ + // "ignoreDependencyOrder": false, + // + // /** + // * Normally Rush requires that each project's package.json has a "scripts" entry matching + // * the custom command name. To disable this check, set "ignoreMissingScript" to true; + // * projects with a missing definition will be skipped. + // */ + // "ignoreMissingScript": false, + // + // /** + // * When invoking shell scripts, Rush uses a heuristic to distinguish errors from warnings: + // * - If the shell script returns a nonzero process exit code, Rush interprets this as "one or more errors". + // * Error output is displayed in red, and it prevents Rush from attempting to process any downstream projects. + // * - If the shell script returns a zero process exit code but writes something to its stderr stream, + // * Rush interprets this as "one or more warnings". Warning output is printed in yellow, but does NOT prevent + // * Rush from processing downstream projects. + // * + // * Thus, warnings do not interfere with local development, but they will cause a CI job to fail, because + // * the Rush process itself returns a nonzero exit code if there are any warnings or errors. This is by design. + // * In an active monorepo, we've found that if you allow any warnings in your main branch, it inadvertently + // * teaches developers to ignore warnings, which quickly leads to a situation where so many "expected" warnings + // * have accumulated that warnings no longer serve any useful purpose. + // * + // * Sometimes a poorly behaved task will write output to stderr even though its operation was successful. + // * In that case, it's strongly recommended to fix the task. However, as a workaround you can set + // * allowWarningsInSuccessfulBuild=true, which causes Rush to return a nonzero exit code for errors only. + // * + // * Note: The default value is false. In Rush 5.7.x and earlier, the default value was true. + // */ + // "allowWarningsInSuccessfulBuild": false, + // + // /** + // * If true then this command will be incremental like the built-in "build" command + // */ + // "incremental": false, + // + // /** + // * (EXPERIMENTAL) Normally Rush terminates after the command finishes. If this option is set to "true" Rush + // * will instead enter a loop where it watches the file system for changes to the selected projects. Whenever a + // * change is detected, the command will be invoked again for the changed project and any selected projects that + // * directly or indirectly depend on it. + // * + // * For details, refer to the website article "Using watch mode". + // */ + // "watchForChanges": false, + // + // /** + // * (EXPERIMENTAL) Disable cache for this action. This may be useful if this command affects state outside of + // * projects' own folders. + // */ + // "disableBuildCache": false + // }, + // + // { + // /** + // * (Required) Determines the type of custom command. + // * Rush's "global" commands are invoked once for the entire repo. + // */ + // "commandKind": "global", + // + // "name": "my-global-command", + // "summary": "Example global custom command", + // "description": "This is an example custom command that runs once for the entire repo", + // + // "safeForSimultaneousRushProcesses": false, + // + // /** + // * (Required) A script that will be invoked using the OS shell. The working directory will be + // * the folder that contains rush.json. If custom parameters are associated with this command, their + // * values will be appended to the end of this string. + // */ + // "shellCommand": "node common/scripts/my-global-command.js", + // + // /** + // * If your "shellCommand" script depends on NPM packages, the recommended best practice is + // * to make it into a regular Rush project that builds using your normal toolchain. In cases where + // * the command needs to work without first having to run "rush build", the recommended practice + // * is to publish the project to an NPM registry and use common/scripts/install-run.js to launch it. + // * + // * Autoinstallers offer another possibility: They are folders under "common/autoinstallers" with + // * a package.json file and shrinkwrap file. Rush will automatically invoke the package manager to + // * install these dependencies before an associated command is invoked. Autoinstallers have the + // * advantage that they work even in a branch where "rush install" is broken, which makes them a + // * good solution for Git hook scripts. But they have the disadvantages of not being buildable + // * projects, and of increasing the overall installation footprint for your monorepo. + // * + // * The "autoinstallerName" setting must not contain a path and must be a valid NPM package name. + // * For example, the name "my-task" would map to "common/autoinstallers/my-task/package.json", and + // * the "common/autoinstallers/my-task/node_modules/.bin" folder would be added to the shell PATH when + // * invoking the "shellCommand". + // */ + // // "autoinstallerName": "my-task" + // } + ], + + "phases": [ + { + /** + * The name of the phase. Note that this value must start with the \"_phase:\" prefix. + */ + "name": "_phase:build", + /** + * The dependencies of this phase. + */ + "dependencies": { + "upstream": ["_phase:build"], + "self": ["_phase:pre-build"] + } + }, + { + /** + * The name of the phase. Note that this value must start with the \"_phase:\" prefix. + */ + "name": "_phase:pre-build", + /** + * The dependencies of this phase. + */ + "dependencies": { + "upstream": ["_phase:build"] + }, + "missingScriptBehavior": "silent" + } + ], + + /** + * Custom "parameters" introduce new parameters for specified Rush command-line commands. + * For example, you might define a "--production" parameter for the "rush build" command. + */ + "parameters": [ + // { + // /** + // * (Required) Determines the type of custom parameter. + // * A "flag" is a custom command-line parameter whose presence acts as an on/off switch. + // */ + // "parameterKind": "flag", + // + // /** + // * (Required) The long name of the parameter. It must be lower-case and use dash delimiters. + // */ + // "longName": "--my-flag", + // + // /** + // * An optional alternative short name for the parameter. It must be a dash followed by a single + // * lower-case or upper-case letter, which is case-sensitive. + // * + // * NOTE: The Rush developers recommend that automation scripts should always use the long name + // * to improve readability. The short name is only intended as a convenience for humans. + // * The alphabet letters run out quickly, and are difficult to memorize, so *only* use + // * a short name if you expect the parameter to be needed very often in everyday operations. + // */ + // "shortName": "-m", + // + // /** + // * (Required) A long description to be shown in the command-line help. + // * + // * Whenever you introduce commands/parameters, taking a little time to write meaningful + // * documentation can make a big difference for the developer experience in your repo. + // */ + // "description": "A custom flag parameter that is passed to the scripts that are invoked when building projects", + // + // /** + // * (Required) A list of custom commands and/or built-in Rush commands that this parameter may + // * be used with. The parameter will be appended to the shell command that Rush invokes. + // */ + // "associatedCommands": ["build", "rebuild"] + // }, + // + // { + // /** + // * (Required) Determines the type of custom parameter. + // * A "string" is a custom command-line parameter whose value is a simple text string. + // */ + // "parameterKind": "string", + // "longName": "--my-string", + // "description": "A custom string parameter for the \"my-global-command\" custom command", + // + // "associatedCommands": ["my-global-command"], + // + // /** + // * The name of the argument, which will be shown in the command-line help. + // * + // * For example, if the parameter name is '--count" and the argument name is "NUMBER", + // * then the command-line help would display "--count NUMBER". The argument name must + // * be comprised of upper-case letters, numbers, and underscores. It should be kept short. + // */ + // "argumentName": "SOME_TEXT", + // + // /** + // * If true, this parameter must be included with the command. The default is false. + // */ + // "required": false + // }, + // + // { + // /** + // * (Required) Determines the type of custom parameter. + // * A "choice" is a custom command-line parameter whose argument must be chosen from a list of + // * allowable alternatives. + // */ + // "parameterKind": "choice", + // "longName": "--my-choice", + // "description": "A custom choice parameter for the \"my-global-command\" custom command", + // + // "associatedCommands": ["my-global-command"], + // + // /** + // * If true, this parameter must be included with the command. The default is false. + // */ + // "required": false, + // + // /** + // * Normally if a parameter is omitted from the command line, it will not be passed + // * to the shell command. this value will be inserted by default. Whereas if a "defaultValue" + // * is defined, the parameter will always be passed to the shell command, and will use the + // * default value if unspecified. The value must be one of the defined alternatives. + // */ + // "defaultValue": "vanilla", + // + // /** + // * (Required) A list of alternative argument values that can be chosen for this parameter. + // */ + // "alternatives": [ + // { + // /** + // * A token that is one of the alternatives that can be used with the choice parameter, + // * e.g. "vanilla" in "--flavor vanilla". + // */ + // "name": "vanilla", + // + // /** + // * A detailed description for the alternative that can be shown in the command-line help. + // * + // * Whenever you introduce commands/parameters, taking a little time to write meaningful + // * documentation can make a big difference for the developer experience in your repo. + // */ + // "description": "Use the vanilla flavor (the default)" + // }, + // + // { + // "name": "chocolate", + // "description": "Use the chocolate flavor" + // }, + // + // { + // "name": "strawberry", + // "description": "Use the strawberry flavor" + // } + // ] + // } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/experiments.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/experiments.json new file mode 100644 index 00000000000..14a02ec1f2f --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/experiments.json @@ -0,0 +1,57 @@ +/** + * This configuration file allows repo maintainers to enable and disable experimental + * Rush features. More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "../../../../../../../libraries/rush-lib/src/schemas/experiments.schema.json", + + /** + * By default, 'rush install' passes --no-prefer-frozen-lockfile to 'pnpm install'. + * Set this option to true to pass '--frozen-lockfile' instead for faster installs. + */ + "usePnpmFrozenLockfileForRushInstall": true, + + /** + * By default, 'rush update' passes --no-prefer-frozen-lockfile to 'pnpm install'. + * Set this option to true to pass '--prefer-frozen-lockfile' instead to minimize shrinkwrap changes. + */ + "usePnpmPreferFrozenLockfileForRushUpdate": true, + + /** + * If using the 'preventManualShrinkwrapChanges' option, restricts the hash to only include the layout of external dependencies. + * Used to allow links between workspace projects or the addition/removal of references to existing dependency versions to not + * cause hash changes. + */ + "omitImportersFromPreventManualShrinkwrapChanges": true, + + /** + * If true, the chmod field in temporary project tar headers will not be normalized. + * This normalization can help ensure consistent tarball integrity across platforms. + */ + // "noChmodFieldInTarHeaderNormalization": true, + + /** + * If true, build caching will respect the allowWarningsInSuccessfulBuild flag and cache builds with warnings. + * This will not replay warnings from the cached build. + */ + // "buildCacheWithAllowWarningsInSuccessfulBuild": true, + + /** + * If true, the phased commands feature is enabled. To use this feature, create a "phased" command + * in common/config/rush/command-line.json. + */ + "phasedCommands": true, + + /** + * If true, perform a clean install after when running `rush install` or `rush update` if the + * `.npmrc` file has changed since the last install. + */ + // "cleanInstallAfterNpmrcChanges": true, + + /** + * If true, print the outputs of shell commands defined in event hooks to the console. + */ + // "printEventHooksOutputToConsole": true + + "allowCobuildWithoutCache": true +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/pnpm-lock.yaml b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/pnpm-lock.yaml new file mode 100644 index 00000000000..98b22e9d424 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/pnpm-lock.yaml @@ -0,0 +1,52 @@ +lockfileVersion: 5.4 + +importers: + + .: + specifiers: {} + + ../../projects/a: + specifiers: {} + + ../../projects/b: + specifiers: {} + + ../../projects/c: + specifiers: + b: workspace:* + dependencies: + b: link:../b + + ../../projects/d: + specifiers: + b: workspace:* + c: workspace:* + dependencies: + b: link:../b + c: link:../c + + ../../projects/e: + specifiers: + b: workspace:* + d: workspace:* + dependencies: + b: link:../b + d: link:../d + + ../../projects/f: + specifiers: + b: workspace:* + dependencies: + b: link:../b + + ../../projects/g: + specifiers: + b: workspace:* + dependencies: + b: link:../b + + ../../projects/h: + specifiers: + a: workspace:* + dependencies: + a: link:../a diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/repo-state.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/repo-state.json new file mode 100644 index 00000000000..0e7b144099d --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/config/rush/repo-state.json @@ -0,0 +1,4 @@ +// DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush. +{ + "preferredVersionsHash": "bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f" +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/scripts/install-run-rush-pnpm.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/scripts/install-run-rush-pnpm.js new file mode 100644 index 00000000000..4b7aad5d586 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/scripts/install-run-rush-pnpm.js @@ -0,0 +1,32 @@ +// THIS FILE WAS GENERATED BY A TOOL. ANY MANUAL MODIFICATIONS WILL GET OVERWRITTEN WHENEVER RUSH IS UPGRADED. +// +// This script is intended for usage in an automated build environment where the Rush command may not have +// been preinstalled, or may have an unpredictable version. This script will automatically install the version of Rush +// specified in the rush.json configuration file (if not already installed), and then pass a command-line to the +// rush-pnpm command. +// +// An example usage would be: +// +// node common/scripts/install-run-rush-pnpm.js pnpm-command +// +// For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ +// +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See the @microsoft/rush package's LICENSE file for details. + +/******/ (() => { + // webpackBootstrap + /******/ 'use strict'; + var __webpack_exports__ = {}; + /*!*****************************************************!*\ + !*** ./lib-esnext/scripts/install-run-rush-pnpm.js ***! + \*****************************************************/ + + // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. + // See LICENSE in the project root for license information. + require('./install-run-rush'); + //# sourceMappingURL=install-run-rush-pnpm.js.map + module.exports = __webpack_exports__; + /******/ +})(); +//# sourceMappingURL=install-run-rush-pnpm.js.map diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/scripts/install-run-rush.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/scripts/install-run-rush.js new file mode 100644 index 00000000000..48da5907f9d --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/scripts/install-run-rush.js @@ -0,0 +1,245 @@ +// THIS FILE WAS GENERATED BY A TOOL. ANY MANUAL MODIFICATIONS WILL GET OVERWRITTEN WHENEVER RUSH IS UPGRADED. +// +// This script is intended for usage in an automated build environment where the Rush command may not have +// been preinstalled, or may have an unpredictable version. This script will automatically install the version of Rush +// specified in the rush.json configuration file (if not already installed), and then pass a command-line to it. +// An example usage would be: +// +// node common/scripts/install-run-rush.js install +// +// For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ +// +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See the @microsoft/rush package's LICENSE file for details. + +/******/ (() => { + // webpackBootstrap + /******/ 'use strict'; + /******/ var __webpack_modules__ = { + /***/ 657147: + /*!*********************!*\ + !*** external "fs" ***! + \*********************/ + /***/ (module) => { + module.exports = require('fs'); + + /***/ + }, + + /***/ 371017: + /*!***********************!*\ + !*** external "path" ***! + \***********************/ + /***/ (module) => { + module.exports = require('path'); + + /***/ + } + + /******/ + }; + /************************************************************************/ + /******/ // The module cache + /******/ var __webpack_module_cache__ = {}; + /******/ + /******/ // The require function + /******/ function __webpack_require__(moduleId) { + /******/ // Check if module is in cache + /******/ var cachedModule = __webpack_module_cache__[moduleId]; + /******/ if (cachedModule !== undefined) { + /******/ return cachedModule.exports; + /******/ + } + /******/ // Create a new module (and put it into the cache) + /******/ var module = (__webpack_module_cache__[moduleId] = { + /******/ // no module.id needed + /******/ // no module.loaded needed + /******/ exports: {} + /******/ + }); + /******/ + /******/ // Execute the module function + /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); + /******/ + /******/ // Return the exports of the module + /******/ return module.exports; + /******/ + } + /******/ + /************************************************************************/ + /******/ /* webpack/runtime/compat get default export */ + /******/ (() => { + /******/ // getDefaultExport function for compatibility with non-harmony modules + /******/ __webpack_require__.n = (module) => { + /******/ var getter = + module && module.__esModule ? /******/ () => module['default'] : /******/ () => module; + /******/ __webpack_require__.d(getter, { a: getter }); + /******/ return getter; + /******/ + }; + /******/ + })(); + /******/ + /******/ /* webpack/runtime/define property getters */ + /******/ (() => { + /******/ // define getter functions for harmony exports + /******/ __webpack_require__.d = (exports, definition) => { + /******/ for (var key in definition) { + /******/ if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { + /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); + /******/ + } + /******/ + } + /******/ + }; + /******/ + })(); + /******/ + /******/ /* webpack/runtime/hasOwnProperty shorthand */ + /******/ (() => { + /******/ __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop); + /******/ + })(); + /******/ + /******/ /* webpack/runtime/make namespace object */ + /******/ (() => { + /******/ // define __esModule on exports + /******/ __webpack_require__.r = (exports) => { + /******/ if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { + /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); + /******/ + } + /******/ Object.defineProperty(exports, '__esModule', { value: true }); + /******/ + }; + /******/ + })(); + /******/ + /************************************************************************/ + var __webpack_exports__ = {}; + // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. + (() => { + /*!************************************************!*\ + !*** ./lib-esnext/scripts/install-run-rush.js ***! + \************************************************/ + __webpack_require__.r(__webpack_exports__); + /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! path */ 371017); + /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/ __webpack_require__.n( + path__WEBPACK_IMPORTED_MODULE_0__ + ); + /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! fs */ 657147); + /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/ __webpack_require__.n( + fs__WEBPACK_IMPORTED_MODULE_1__ + ); + // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. + // See LICENSE in the project root for license information. + /* eslint-disable no-console */ + + const { + installAndRun, + findRushJsonFolder, + RUSH_JSON_FILENAME, + runWithErrorAndStatusCode + } = require('./install-run'); + const PACKAGE_NAME = '@microsoft/rush'; + const RUSH_PREVIEW_VERSION = 'RUSH_PREVIEW_VERSION'; + const INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE = 'INSTALL_RUN_RUSH_LOCKFILE_PATH'; + function _getRushVersion(logger) { + const rushPreviewVersion = process.env[RUSH_PREVIEW_VERSION]; + if (rushPreviewVersion !== undefined) { + logger.info( + `Using Rush version from environment variable ${RUSH_PREVIEW_VERSION}=${rushPreviewVersion}` + ); + return rushPreviewVersion; + } + const rushJsonFolder = findRushJsonFolder(); + const rushJsonPath = path__WEBPACK_IMPORTED_MODULE_0__.join(rushJsonFolder, RUSH_JSON_FILENAME); + try { + const rushJsonContents = fs__WEBPACK_IMPORTED_MODULE_1__.readFileSync(rushJsonPath, 'utf-8'); + // Use a regular expression to parse out the rushVersion value because rush.json supports comments, + // but JSON.parse does not and we don't want to pull in more dependencies than we need to in this script. + const rushJsonMatches = rushJsonContents.match(/\"rushVersion\"\s*\:\s*\"([0-9a-zA-Z.+\-]+)\"/); + return rushJsonMatches[1]; + } catch (e) { + throw new Error( + `Unable to determine the required version of Rush from ${RUSH_JSON_FILENAME} (${rushJsonFolder}). ` + + `The 'rushVersion' field is either not assigned in ${RUSH_JSON_FILENAME} or was specified ` + + 'using an unexpected syntax.' + ); + } + } + function _getBin(scriptName) { + switch (scriptName.toLowerCase()) { + case 'install-run-rush-pnpm.js': + return 'rush-pnpm'; + case 'install-run-rushx.js': + return 'rushx'; + default: + return 'rush'; + } + } + function _run() { + const [ + nodePath /* Ex: /bin/node */, + scriptPath /* /repo/common/scripts/install-run-rush.js */, + ...packageBinArgs /* [build, --to, myproject] */ + ] = process.argv; + // Detect if this script was directly invoked, or if the install-run-rushx script was invokved to select the + // appropriate binary inside the rush package to run + const scriptName = path__WEBPACK_IMPORTED_MODULE_0__.basename(scriptPath); + const bin = _getBin(scriptName); + if (!nodePath || !scriptPath) { + throw new Error('Unexpected exception: could not detect node path or script path'); + } + let commandFound = false; + let logger = { info: console.log, error: console.error }; + for (const arg of packageBinArgs) { + if (arg === '-q' || arg === '--quiet') { + // The -q/--quiet flag is supported by both `rush` and `rushx`, and will suppress + // any normal informational/diagnostic information printed during startup. + // + // To maintain the same user experience, the install-run* scripts pass along this + // flag but also use it to suppress any diagnostic information normally printed + // to stdout. + logger = { + info: () => {}, + error: console.error + }; + } else if (!arg.startsWith('-') || arg === '-h' || arg === '--help') { + // We either found something that looks like a command (i.e. - doesn't start with a "-"), + // or we found the -h/--help flag, which can be run without a command + commandFound = true; + } + } + if (!commandFound) { + console.log(`Usage: ${scriptName} [args...]`); + if (scriptName === 'install-run-rush-pnpm.js') { + console.log(`Example: ${scriptName} pnpm-command`); + } else if (scriptName === 'install-run-rush.js') { + console.log(`Example: ${scriptName} build --to myproject`); + } else { + console.log(`Example: ${scriptName} custom-command`); + } + process.exit(1); + } + runWithErrorAndStatusCode(logger, () => { + const version = _getRushVersion(logger); + logger.info(`The ${RUSH_JSON_FILENAME} configuration requests Rush version ${version}`); + const lockFilePath = process.env[INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE]; + if (lockFilePath) { + logger.info( + `Found ${INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE}="${lockFilePath}", installing with lockfile.` + ); + } + return installAndRun(logger, PACKAGE_NAME, version, bin, packageBinArgs, lockFilePath); + }); + } + _run(); + //# sourceMappingURL=install-run-rush.js.map + })(); + + module.exports = __webpack_exports__; + /******/ +})(); +//# sourceMappingURL=install-run-rush.js.map diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/scripts/install-run-rushx.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/scripts/install-run-rushx.js new file mode 100644 index 00000000000..f865303a384 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/scripts/install-run-rushx.js @@ -0,0 +1,32 @@ +// THIS FILE WAS GENERATED BY A TOOL. ANY MANUAL MODIFICATIONS WILL GET OVERWRITTEN WHENEVER RUSH IS UPGRADED. +// +// This script is intended for usage in an automated build environment where the Rush command may not have +// been preinstalled, or may have an unpredictable version. This script will automatically install the version of Rush +// specified in the rush.json configuration file (if not already installed), and then pass a command-line to the +// rushx command. +// +// An example usage would be: +// +// node common/scripts/install-run-rushx.js custom-command +// +// For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ +// +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See the @microsoft/rush package's LICENSE file for details. + +/******/ (() => { + // webpackBootstrap + /******/ 'use strict'; + var __webpack_exports__ = {}; + /*!*************************************************!*\ + !*** ./lib-esnext/scripts/install-run-rushx.js ***! + \*************************************************/ + + // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. + // See LICENSE in the project root for license information. + require('./install-run-rush'); + //# sourceMappingURL=install-run-rushx.js.map + module.exports = __webpack_exports__; + /******/ +})(); +//# sourceMappingURL=install-run-rushx.js.map diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/scripts/install-run.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/scripts/install-run.js new file mode 100644 index 00000000000..580ebb343e9 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/common/scripts/install-run.js @@ -0,0 +1,821 @@ +// THIS FILE WAS GENERATED BY A TOOL. ANY MANUAL MODIFICATIONS WILL GET OVERWRITTEN WHENEVER RUSH IS UPGRADED. +// +// This script is intended for usage in an automated build environment where a Node tool may not have +// been preinstalled, or may have an unpredictable version. This script will automatically install the specified +// version of the specified tool (if not already installed), and then pass a command-line to it. +// An example usage would be: +// +// node common/scripts/install-run.js qrcode@1.2.2 qrcode https://rushjs.io +// +// For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ +// +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See the @microsoft/rush package's LICENSE file for details. + +/******/ (() => { + // webpackBootstrap + /******/ 'use strict'; + /******/ var __webpack_modules__ = { + /***/ 679877: + /*!************************************************!*\ + !*** ./lib-esnext/utilities/npmrcUtilities.js ***! + \************************************************/ + /***/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ isVariableSetInNpmrcFile: () => /* binding */ isVariableSetInNpmrcFile, + /* harmony export */ syncNpmrc: () => /* binding */ syncNpmrc + /* harmony export */ + }); + /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! fs */ 657147); + /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = + /*#__PURE__*/ __webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__); + /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! path */ 371017); + /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = + /*#__PURE__*/ __webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); + // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. + // See LICENSE in the project root for license information. + // IMPORTANT - do not use any non-built-in libraries in this file + + /** + * This function reads the content for given .npmrc file path, and also trims + * unusable lines from the .npmrc file. + * + * @returns + * The text of the the .npmrc. + */ + // create a global _combinedNpmrc for cache purpose + const _combinedNpmrcMap = new Map(); + function _trimNpmrcFile(options) { + const { sourceNpmrcPath, linesToPrepend, linesToAppend } = options; + const combinedNpmrcFromCache = _combinedNpmrcMap.get(sourceNpmrcPath); + if (combinedNpmrcFromCache !== undefined) { + return combinedNpmrcFromCache; + } + let npmrcFileLines = []; + if (linesToPrepend) { + npmrcFileLines.push(...linesToPrepend); + } + if (fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(sourceNpmrcPath)) { + npmrcFileLines.push( + ...fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(sourceNpmrcPath).toString().split('\n') + ); + } + if (linesToAppend) { + npmrcFileLines.push(...linesToAppend); + } + npmrcFileLines = npmrcFileLines.map((line) => (line || '').trim()); + const resultLines = []; + // This finds environment variable tokens that look like "${VAR_NAME}" + const expansionRegExp = /\$\{([^\}]+)\}/g; + // Comment lines start with "#" or ";" + const commentRegExp = /^\s*[#;]/; + // Trim out lines that reference environment variables that aren't defined + for (let line of npmrcFileLines) { + let lineShouldBeTrimmed = false; + //remove spaces before or after key and value + line = line + .split('=') + .map((lineToTrim) => lineToTrim.trim()) + .join('='); + // Ignore comment lines + if (!commentRegExp.test(line)) { + const environmentVariables = line.match(expansionRegExp); + if (environmentVariables) { + for (const token of environmentVariables) { + // Remove the leading "${" and the trailing "}" from the token + const environmentVariableName = token.substring(2, token.length - 1); + // Is the environment variable defined? + if (!process.env[environmentVariableName]) { + // No, so trim this line + lineShouldBeTrimmed = true; + break; + } + } + } + } + if (lineShouldBeTrimmed) { + // Example output: + // "; MISSING ENVIRONMENT VARIABLE: //my-registry.com/npm/:_authToken=${MY_AUTH_TOKEN}" + resultLines.push('; MISSING ENVIRONMENT VARIABLE: ' + line); + } else { + resultLines.push(line); + } + } + const combinedNpmrc = resultLines.join('\n'); + //save the cache + _combinedNpmrcMap.set(sourceNpmrcPath, combinedNpmrc); + return combinedNpmrc; + } + function _copyAndTrimNpmrcFile(options) { + const { logger, sourceNpmrcPath, targetNpmrcPath, linesToPrepend, linesToAppend } = options; + logger.info(`Transforming ${sourceNpmrcPath}`); // Verbose + logger.info(` --> "${targetNpmrcPath}"`); + const combinedNpmrc = _trimNpmrcFile({ + sourceNpmrcPath, + linesToPrepend, + linesToAppend + }); + fs__WEBPACK_IMPORTED_MODULE_0__.writeFileSync(targetNpmrcPath, combinedNpmrc); + return combinedNpmrc; + } + function syncNpmrc(options) { + const { + sourceNpmrcFolder, + targetNpmrcFolder, + useNpmrcPublish, + logger = { + // eslint-disable-next-line no-console + info: console.log, + // eslint-disable-next-line no-console + error: console.error + }, + createIfMissing = false, + linesToAppend, + linesToPrepend + } = options; + const sourceNpmrcPath = path__WEBPACK_IMPORTED_MODULE_1__.join( + sourceNpmrcFolder, + !useNpmrcPublish ? '.npmrc' : '.npmrc-publish' + ); + const targetNpmrcPath = path__WEBPACK_IMPORTED_MODULE_1__.join(targetNpmrcFolder, '.npmrc'); + try { + if (fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(sourceNpmrcPath) || createIfMissing) { + // Ensure the target folder exists + if (!fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(targetNpmrcFolder)) { + fs__WEBPACK_IMPORTED_MODULE_0__.mkdirSync(targetNpmrcFolder, { recursive: true }); + } + return _copyAndTrimNpmrcFile({ + sourceNpmrcPath, + targetNpmrcPath, + logger, + linesToAppend, + linesToPrepend + }); + } else if (fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(targetNpmrcPath)) { + // If the source .npmrc doesn't exist and there is one in the target, delete the one in the target + logger.info(`Deleting ${targetNpmrcPath}`); // Verbose + fs__WEBPACK_IMPORTED_MODULE_0__.unlinkSync(targetNpmrcPath); + } + } catch (e) { + throw new Error(`Error syncing .npmrc file: ${e}`); + } + } + function isVariableSetInNpmrcFile(sourceNpmrcFolder, variableKey) { + const sourceNpmrcPath = `${sourceNpmrcFolder}/.npmrc`; + //if .npmrc file does not exist, return false directly + if (!fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(sourceNpmrcPath)) { + return false; + } + const trimmedNpmrcFile = _trimNpmrcFile({ sourceNpmrcPath }); + const variableKeyRegExp = new RegExp(`^${variableKey}=`, 'm'); + return trimmedNpmrcFile.match(variableKeyRegExp) !== null; + } + //# sourceMappingURL=npmrcUtilities.js.map + + /***/ + }, + + /***/ 532081: + /*!********************************!*\ + !*** external "child_process" ***! + \********************************/ + /***/ (module) => { + module.exports = require('child_process'); + + /***/ + }, + + /***/ 657147: + /*!*********************!*\ + !*** external "fs" ***! + \*********************/ + /***/ (module) => { + module.exports = require('fs'); + + /***/ + }, + + /***/ 822037: + /*!*********************!*\ + !*** external "os" ***! + \*********************/ + /***/ (module) => { + module.exports = require('os'); + + /***/ + }, + + /***/ 371017: + /*!***********************!*\ + !*** external "path" ***! + \***********************/ + /***/ (module) => { + module.exports = require('path'); + + /***/ + } + + /******/ + }; + /************************************************************************/ + /******/ // The module cache + /******/ var __webpack_module_cache__ = {}; + /******/ + /******/ // The require function + /******/ function __webpack_require__(moduleId) { + /******/ // Check if module is in cache + /******/ var cachedModule = __webpack_module_cache__[moduleId]; + /******/ if (cachedModule !== undefined) { + /******/ return cachedModule.exports; + /******/ + } + /******/ // Create a new module (and put it into the cache) + /******/ var module = (__webpack_module_cache__[moduleId] = { + /******/ // no module.id needed + /******/ // no module.loaded needed + /******/ exports: {} + /******/ + }); + /******/ + /******/ // Execute the module function + /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); + /******/ + /******/ // Return the exports of the module + /******/ return module.exports; + /******/ + } + /******/ + /************************************************************************/ + /******/ /* webpack/runtime/compat get default export */ + /******/ (() => { + /******/ // getDefaultExport function for compatibility with non-harmony modules + /******/ __webpack_require__.n = (module) => { + /******/ var getter = + module && module.__esModule ? /******/ () => module['default'] : /******/ () => module; + /******/ __webpack_require__.d(getter, { a: getter }); + /******/ return getter; + /******/ + }; + /******/ + })(); + /******/ + /******/ /* webpack/runtime/define property getters */ + /******/ (() => { + /******/ // define getter functions for harmony exports + /******/ __webpack_require__.d = (exports, definition) => { + /******/ for (var key in definition) { + /******/ if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { + /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); + /******/ + } + /******/ + } + /******/ + }; + /******/ + })(); + /******/ + /******/ /* webpack/runtime/hasOwnProperty shorthand */ + /******/ (() => { + /******/ __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop); + /******/ + })(); + /******/ + /******/ /* webpack/runtime/make namespace object */ + /******/ (() => { + /******/ // define __esModule on exports + /******/ __webpack_require__.r = (exports) => { + /******/ if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { + /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); + /******/ + } + /******/ Object.defineProperty(exports, '__esModule', { value: true }); + /******/ + }; + /******/ + })(); + /******/ + /************************************************************************/ + var __webpack_exports__ = {}; + // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. + (() => { + /*!*******************************************!*\ + !*** ./lib-esnext/scripts/install-run.js ***! + \*******************************************/ + __webpack_require__.r(__webpack_exports__); + /* harmony export */ __webpack_require__.d(__webpack_exports__, { + /* harmony export */ RUSH_JSON_FILENAME: () => /* binding */ RUSH_JSON_FILENAME, + /* harmony export */ findRushJsonFolder: () => /* binding */ findRushJsonFolder, + /* harmony export */ getNpmPath: () => /* binding */ getNpmPath, + /* harmony export */ installAndRun: () => /* binding */ installAndRun, + /* harmony export */ runWithErrorAndStatusCode: () => /* binding */ runWithErrorAndStatusCode + /* harmony export */ + }); + /* harmony import */ var child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( + /*! child_process */ 532081 + ); + /* harmony import */ var child_process__WEBPACK_IMPORTED_MODULE_0___default = + /*#__PURE__*/ __webpack_require__.n(child_process__WEBPACK_IMPORTED_MODULE_0__); + /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! fs */ 657147); + /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/ __webpack_require__.n( + fs__WEBPACK_IMPORTED_MODULE_1__ + ); + /* harmony import */ var os__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! os */ 822037); + /* harmony import */ var os__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/ __webpack_require__.n( + os__WEBPACK_IMPORTED_MODULE_2__ + ); + /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! path */ 371017); + /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/ __webpack_require__.n( + path__WEBPACK_IMPORTED_MODULE_3__ + ); + /* harmony import */ var _utilities_npmrcUtilities__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__( + /*! ../utilities/npmrcUtilities */ 679877 + ); + // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. + // See LICENSE in the project root for license information. + /* eslint-disable no-console */ + + const RUSH_JSON_FILENAME = 'rush.json'; + const RUSH_TEMP_FOLDER_ENV_VARIABLE_NAME = 'RUSH_TEMP_FOLDER'; + const INSTALL_RUN_LOCKFILE_PATH_VARIABLE = 'INSTALL_RUN_LOCKFILE_PATH'; + const INSTALLED_FLAG_FILENAME = 'installed.flag'; + const NODE_MODULES_FOLDER_NAME = 'node_modules'; + const PACKAGE_JSON_FILENAME = 'package.json'; + /** + * Parse a package specifier (in the form of name\@version) into name and version parts. + */ + function _parsePackageSpecifier(rawPackageSpecifier) { + rawPackageSpecifier = (rawPackageSpecifier || '').trim(); + const separatorIndex = rawPackageSpecifier.lastIndexOf('@'); + let name; + let version = undefined; + if (separatorIndex === 0) { + // The specifier starts with a scope and doesn't have a version specified + name = rawPackageSpecifier; + } else if (separatorIndex === -1) { + // The specifier doesn't have a version + name = rawPackageSpecifier; + } else { + name = rawPackageSpecifier.substring(0, separatorIndex); + version = rawPackageSpecifier.substring(separatorIndex + 1); + } + if (!name) { + throw new Error(`Invalid package specifier: ${rawPackageSpecifier}`); + } + return { name, version }; + } + let _npmPath = undefined; + /** + * Get the absolute path to the npm executable + */ + function getNpmPath() { + if (!_npmPath) { + try { + if (_isWindows()) { + // We're on Windows + const whereOutput = child_process__WEBPACK_IMPORTED_MODULE_0__ + .execSync('where npm', { stdio: [] }) + .toString(); + const lines = whereOutput.split(os__WEBPACK_IMPORTED_MODULE_2__.EOL).filter((line) => !!line); + // take the last result, we are looking for a .cmd command + // see https://github.com/microsoft/rushstack/issues/759 + _npmPath = lines[lines.length - 1]; + } else { + // We aren't on Windows - assume we're on *NIX or Darwin + _npmPath = child_process__WEBPACK_IMPORTED_MODULE_0__ + .execSync('command -v npm', { stdio: [] }) + .toString(); + } + } catch (e) { + throw new Error(`Unable to determine the path to the NPM tool: ${e}`); + } + _npmPath = _npmPath.trim(); + if (!fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(_npmPath)) { + throw new Error('The NPM executable does not exist'); + } + } + return _npmPath; + } + function _ensureFolder(folderPath) { + if (!fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(folderPath)) { + const parentDir = path__WEBPACK_IMPORTED_MODULE_3__.dirname(folderPath); + _ensureFolder(parentDir); + fs__WEBPACK_IMPORTED_MODULE_1__.mkdirSync(folderPath); + } + } + /** + * Create missing directories under the specified base directory, and return the resolved directory. + * + * Does not support "." or ".." path segments. + * Assumes the baseFolder exists. + */ + function _ensureAndJoinPath(baseFolder, ...pathSegments) { + let joinedPath = baseFolder; + try { + for (let pathSegment of pathSegments) { + pathSegment = pathSegment.replace(/[\\\/]/g, '+'); + joinedPath = path__WEBPACK_IMPORTED_MODULE_3__.join(joinedPath, pathSegment); + if (!fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(joinedPath)) { + fs__WEBPACK_IMPORTED_MODULE_1__.mkdirSync(joinedPath); + } + } + } catch (e) { + throw new Error( + `Error building local installation folder (${path__WEBPACK_IMPORTED_MODULE_3__.join(baseFolder, ...pathSegments)}): ${e}` + ); + } + return joinedPath; + } + function _getRushTempFolder(rushCommonFolder) { + const rushTempFolder = process.env[RUSH_TEMP_FOLDER_ENV_VARIABLE_NAME]; + if (rushTempFolder !== undefined) { + _ensureFolder(rushTempFolder); + return rushTempFolder; + } else { + return _ensureAndJoinPath(rushCommonFolder, 'temp'); + } + } + /** + * Compare version strings according to semantic versioning. + * Returns a positive integer if "a" is a later version than "b", + * a negative integer if "b" is later than "a", + * and 0 otherwise. + */ + function _compareVersionStrings(a, b) { + const aParts = a.split(/[.-]/); + const bParts = b.split(/[.-]/); + const numberOfParts = Math.max(aParts.length, bParts.length); + for (let i = 0; i < numberOfParts; i++) { + if (aParts[i] !== bParts[i]) { + return (Number(aParts[i]) || 0) - (Number(bParts[i]) || 0); + } + } + return 0; + } + /** + * Resolve a package specifier to a static version + */ + function _resolvePackageVersion(logger, rushCommonFolder, { name, version }) { + if (!version) { + version = '*'; // If no version is specified, use the latest version + } + if (version.match(/^[a-zA-Z0-9\-\+\.]+$/)) { + // If the version contains only characters that we recognize to be used in static version specifiers, + // pass the version through + return version; + } else { + // version resolves to + try { + const rushTempFolder = _getRushTempFolder(rushCommonFolder); + const sourceNpmrcFolder = path__WEBPACK_IMPORTED_MODULE_3__.join( + rushCommonFolder, + 'config', + 'rush' + ); + (0, _utilities_npmrcUtilities__WEBPACK_IMPORTED_MODULE_4__.syncNpmrc)({ + sourceNpmrcFolder, + targetNpmrcFolder: rushTempFolder, + logger + }); + const npmPath = getNpmPath(); + // This returns something that looks like: + // ``` + // [ + // "3.0.0", + // "3.0.1", + // ... + // "3.0.20" + // ] + // ``` + // + // if multiple versions match the selector, or + // + // ``` + // "3.0.0" + // ``` + // + // if only a single version matches. + const spawnSyncOptions = { + cwd: rushTempFolder, + stdio: [], + shell: _isWindows() + }; + const platformNpmPath = _getPlatformPath(npmPath); + const npmVersionSpawnResult = child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync( + platformNpmPath, + ['view', `${name}@${version}`, 'version', '--no-update-notifier', '--json'], + spawnSyncOptions + ); + if (npmVersionSpawnResult.status !== 0) { + throw new Error(`"npm view" returned error code ${npmVersionSpawnResult.status}`); + } + const npmViewVersionOutput = npmVersionSpawnResult.stdout.toString(); + const parsedVersionOutput = JSON.parse(npmViewVersionOutput); + const versions = Array.isArray(parsedVersionOutput) ? parsedVersionOutput : [parsedVersionOutput]; + let latestVersion = versions[0]; + for (let i = 1; i < versions.length; i++) { + const latestVersionCandidate = versions[i]; + if (_compareVersionStrings(latestVersionCandidate, latestVersion) > 0) { + latestVersion = latestVersionCandidate; + } + } + if (!latestVersion) { + throw new Error('No versions found for the specified version range.'); + } + return latestVersion; + } catch (e) { + throw new Error(`Unable to resolve version ${version} of package ${name}: ${e}`); + } + } + } + let _rushJsonFolder; + /** + * Find the absolute path to the folder containing rush.json + */ + function findRushJsonFolder() { + if (!_rushJsonFolder) { + let basePath = __dirname; + let tempPath = __dirname; + do { + const testRushJsonPath = path__WEBPACK_IMPORTED_MODULE_3__.join(basePath, RUSH_JSON_FILENAME); + if (fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(testRushJsonPath)) { + _rushJsonFolder = basePath; + break; + } else { + basePath = tempPath; + } + } while (basePath !== (tempPath = path__WEBPACK_IMPORTED_MODULE_3__.dirname(basePath))); // Exit the loop when we hit the disk root + if (!_rushJsonFolder) { + throw new Error(`Unable to find ${RUSH_JSON_FILENAME}.`); + } + } + return _rushJsonFolder; + } + /** + * Detects if the package in the specified directory is installed + */ + function _isPackageAlreadyInstalled(packageInstallFolder) { + try { + const flagFilePath = path__WEBPACK_IMPORTED_MODULE_3__.join( + packageInstallFolder, + INSTALLED_FLAG_FILENAME + ); + if (!fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(flagFilePath)) { + return false; + } + const fileContents = fs__WEBPACK_IMPORTED_MODULE_1__.readFileSync(flagFilePath).toString(); + return fileContents.trim() === process.version; + } catch (e) { + return false; + } + } + /** + * Delete a file. Fail silently if it does not exist. + */ + function _deleteFile(file) { + try { + fs__WEBPACK_IMPORTED_MODULE_1__.unlinkSync(file); + } catch (err) { + if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') { + throw err; + } + } + } + /** + * Removes the following files and directories under the specified folder path: + * - installed.flag + * - + * - node_modules + */ + function _cleanInstallFolder(rushTempFolder, packageInstallFolder, lockFilePath) { + try { + const flagFile = path__WEBPACK_IMPORTED_MODULE_3__.resolve( + packageInstallFolder, + INSTALLED_FLAG_FILENAME + ); + _deleteFile(flagFile); + const packageLockFile = path__WEBPACK_IMPORTED_MODULE_3__.resolve( + packageInstallFolder, + 'package-lock.json' + ); + if (lockFilePath) { + fs__WEBPACK_IMPORTED_MODULE_1__.copyFileSync(lockFilePath, packageLockFile); + } else { + // Not running `npm ci`, so need to cleanup + _deleteFile(packageLockFile); + const nodeModulesFolder = path__WEBPACK_IMPORTED_MODULE_3__.resolve( + packageInstallFolder, + NODE_MODULES_FOLDER_NAME + ); + if (fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(nodeModulesFolder)) { + const rushRecyclerFolder = _ensureAndJoinPath(rushTempFolder, 'rush-recycler'); + fs__WEBPACK_IMPORTED_MODULE_1__.renameSync( + nodeModulesFolder, + path__WEBPACK_IMPORTED_MODULE_3__.join( + rushRecyclerFolder, + `install-run-${Date.now().toString()}` + ) + ); + } + } + } catch (e) { + throw new Error(`Error cleaning the package install folder (${packageInstallFolder}): ${e}`); + } + } + function _createPackageJson(packageInstallFolder, name, version) { + try { + const packageJsonContents = { + name: 'ci-rush', + version: '0.0.0', + dependencies: { + [name]: version + }, + description: "DON'T WARN", + repository: "DON'T WARN", + license: 'MIT' + }; + const packageJsonPath = path__WEBPACK_IMPORTED_MODULE_3__.join( + packageInstallFolder, + PACKAGE_JSON_FILENAME + ); + fs__WEBPACK_IMPORTED_MODULE_1__.writeFileSync( + packageJsonPath, + JSON.stringify(packageJsonContents, undefined, 2) + ); + } catch (e) { + throw new Error(`Unable to create package.json: ${e}`); + } + } + /** + * Run "npm install" in the package install folder. + */ + function _installPackage(logger, packageInstallFolder, name, version, command) { + try { + logger.info(`Installing ${name}...`); + const npmPath = getNpmPath(); + const platformNpmPath = _getPlatformPath(npmPath); + const result = child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync(platformNpmPath, [command], { + stdio: 'inherit', + cwd: packageInstallFolder, + env: process.env, + shell: _isWindows() + }); + if (result.status !== 0) { + throw new Error(`"npm ${command}" encountered an error`); + } + logger.info(`Successfully installed ${name}@${version}`); + } catch (e) { + throw new Error(`Unable to install package: ${e}`); + } + } + /** + * Get the ".bin" path for the package. + */ + function _getBinPath(packageInstallFolder, binName) { + const binFolderPath = path__WEBPACK_IMPORTED_MODULE_3__.resolve( + packageInstallFolder, + NODE_MODULES_FOLDER_NAME, + '.bin' + ); + const resolvedBinName = _isWindows() ? `${binName}.cmd` : binName; + return path__WEBPACK_IMPORTED_MODULE_3__.resolve(binFolderPath, resolvedBinName); + } + /** + * Returns a cross-platform path - windows must enclose any path containing spaces within double quotes. + */ + function _getPlatformPath(platformPath) { + return _isWindows() && platformPath.includes(' ') ? `"${platformPath}"` : platformPath; + } + function _isWindows() { + return os__WEBPACK_IMPORTED_MODULE_2__.platform() === 'win32'; + } + /** + * Write a flag file to the package's install directory, signifying that the install was successful. + */ + function _writeFlagFile(packageInstallFolder) { + try { + const flagFilePath = path__WEBPACK_IMPORTED_MODULE_3__.join( + packageInstallFolder, + INSTALLED_FLAG_FILENAME + ); + fs__WEBPACK_IMPORTED_MODULE_1__.writeFileSync(flagFilePath, process.version); + } catch (e) { + throw new Error(`Unable to create installed.flag file in ${packageInstallFolder}`); + } + } + function installAndRun( + logger, + packageName, + packageVersion, + packageBinName, + packageBinArgs, + lockFilePath = process.env[INSTALL_RUN_LOCKFILE_PATH_VARIABLE] + ) { + const rushJsonFolder = findRushJsonFolder(); + const rushCommonFolder = path__WEBPACK_IMPORTED_MODULE_3__.join(rushJsonFolder, 'common'); + const rushTempFolder = _getRushTempFolder(rushCommonFolder); + const packageInstallFolder = _ensureAndJoinPath( + rushTempFolder, + 'install-run', + `${packageName}@${packageVersion}` + ); + if (!_isPackageAlreadyInstalled(packageInstallFolder)) { + // The package isn't already installed + _cleanInstallFolder(rushTempFolder, packageInstallFolder, lockFilePath); + const sourceNpmrcFolder = path__WEBPACK_IMPORTED_MODULE_3__.join(rushCommonFolder, 'config', 'rush'); + (0, _utilities_npmrcUtilities__WEBPACK_IMPORTED_MODULE_4__.syncNpmrc)({ + sourceNpmrcFolder, + targetNpmrcFolder: packageInstallFolder, + logger + }); + _createPackageJson(packageInstallFolder, packageName, packageVersion); + const command = lockFilePath ? 'ci' : 'install'; + _installPackage(logger, packageInstallFolder, packageName, packageVersion, command); + _writeFlagFile(packageInstallFolder); + } + const statusMessage = `Invoking "${packageBinName} ${packageBinArgs.join(' ')}"`; + const statusMessageLine = new Array(statusMessage.length + 1).join('-'); + logger.info('\n' + statusMessage + '\n' + statusMessageLine + '\n'); + const binPath = _getBinPath(packageInstallFolder, packageBinName); + const binFolderPath = path__WEBPACK_IMPORTED_MODULE_3__.resolve( + packageInstallFolder, + NODE_MODULES_FOLDER_NAME, + '.bin' + ); + // Windows environment variables are case-insensitive. Instead of using SpawnSyncOptions.env, we need to + // assign via the process.env proxy to ensure that we append to the right PATH key. + const originalEnvPath = process.env.PATH || ''; + let result; + try { + // `npm` bin stubs on Windows are `.cmd` files + // Node.js will not directly invoke a `.cmd` file unless `shell` is set to `true` + const platformBinPath = _getPlatformPath(binPath); + process.env.PATH = [binFolderPath, originalEnvPath].join(path__WEBPACK_IMPORTED_MODULE_3__.delimiter); + result = child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync(platformBinPath, packageBinArgs, { + stdio: 'inherit', + windowsVerbatimArguments: false, + shell: _isWindows(), + cwd: process.cwd(), + env: process.env + }); + } finally { + process.env.PATH = originalEnvPath; + } + if (result.status !== null) { + return result.status; + } else { + throw result.error || new Error('An unknown error occurred.'); + } + } + function runWithErrorAndStatusCode(logger, fn) { + process.exitCode = 1; + try { + const exitCode = fn(); + process.exitCode = exitCode; + } catch (e) { + logger.error('\n\n' + e.toString() + '\n\n'); + } + } + function _run() { + const [ + nodePath /* Ex: /bin/node */, + scriptPath /* /repo/common/scripts/install-run-rush.js */, + rawPackageSpecifier /* qrcode@^1.2.0 */, + packageBinName /* qrcode */, + ...packageBinArgs /* [-f, myproject/lib] */ + ] = process.argv; + if (!nodePath) { + throw new Error('Unexpected exception: could not detect node path'); + } + if (path__WEBPACK_IMPORTED_MODULE_3__.basename(scriptPath).toLowerCase() !== 'install-run.js') { + // If install-run.js wasn't directly invoked, don't execute the rest of this function. Return control + // to the script that (presumably) imported this file + return; + } + if (process.argv.length < 4) { + console.log('Usage: install-run.js @ [args...]'); + console.log('Example: install-run.js qrcode@1.2.2 qrcode https://rushjs.io'); + process.exit(1); + } + const logger = { info: console.log, error: console.error }; + runWithErrorAndStatusCode(logger, () => { + const rushJsonFolder = findRushJsonFolder(); + const rushCommonFolder = _ensureAndJoinPath(rushJsonFolder, 'common'); + const packageSpecifier = _parsePackageSpecifier(rawPackageSpecifier); + const name = packageSpecifier.name; + const version = _resolvePackageVersion(logger, rushCommonFolder, packageSpecifier); + if (packageSpecifier.version !== version) { + console.log(`Resolved to ${name}@${version}`); + } + return installAndRun(logger, name, version, packageBinName, packageBinArgs); + }); + } + _run(); + //# sourceMappingURL=install-run.js.map + })(); + + module.exports = __webpack_exports__; + /******/ +})(); +//# sourceMappingURL=install-run.js.map diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/a/config/rush-project.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/a/config/rush-project.json new file mode 100644 index 00000000000..f6fc90813a8 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/a/config/rush-project.json @@ -0,0 +1,17 @@ +{ + "$schema": "../../../../../../../libraries/rush-lib/src/schemas/rush-project.schema.json", + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["dist"], + "sharding": { + "count": 4, + "outputFolderArgumentFormat": "--output-directory=.rush/{phaseName}/shards/{shardIndex}" + } + }, + { + "operationName": "_phase:build:shard", + "weight": 4 + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/a/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/a/package.json new file mode 100644 index 00000000000..005e5fdeaa1 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/a/package.json @@ -0,0 +1,8 @@ +{ + "name": "a", + "version": "1.0.0", + "scripts": { + "_phase:build:shard": "node ../build.js a", + "_phase:build": "node ../collate a" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/b/config/rush-project.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/b/config/rush-project.json new file mode 100644 index 00000000000..105cd2e697c --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/b/config/rush-project.json @@ -0,0 +1,16 @@ +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["dist"], + "sharding": { + "count": 5, + "outputFolderArgumentFormat": "--output-directory=.rush/{phaseName}/shards/{shardIndex}" + } + }, + { + "operationName": "_phase:build:shard", + "weight": 10 + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/b/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/b/package.json new file mode 100644 index 00000000000..3a97f747893 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/b/package.json @@ -0,0 +1,8 @@ +{ + "name": "b", + "version": "1.0.0", + "scripts": { + "_phase:build": "node ../collate.js", + "_phase:build:shard": "node ../build.js b" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/build.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/build.js new file mode 100644 index 00000000000..dc1eeac8f81 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/build.js @@ -0,0 +1,30 @@ +/* eslint-env es6 */ +const path = require('path'); +const { FileSystem, Async } = require('@rushstack/node-core-library'); + +const args = process.argv.slice(2); + +const getArgument = (argumentName) => { + const index = args.findIndex((e) => e.includes(argumentName)); + return index >= 0 ? args[index].replace(`${argumentName}=`, '') : undefined; +}; + +const shard = getArgument('--shard'); + +const outputDir = getArgument('--output-directory'); + +const shardOutputDir = getArgument('--shard-output-directory'); + +const outputDirectory = shard ? (shardOutputDir ? shardOutputDir : outputDir) : undefined; + +async function runAsync() { + await Async.sleepAsync(500); + const outputFolder = shard ? path.resolve(outputDirectory) : path.resolve('dist'); + const outputFile = path.resolve(outputFolder, 'output.txt'); + FileSystem.writeFile(outputFile, `Hello world! ${args.join(' ')}`, { ensureFolderExists: true }); +} + +void runAsync().catch((err) => { + console.warn(err); + process.exit(1); +}); diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/c/config/rush-project.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/c/config/rush-project.json new file mode 100644 index 00000000000..78e1555f5a6 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/c/config/rush-project.json @@ -0,0 +1,8 @@ +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["dist"] + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/c/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/c/package.json new file mode 100644 index 00000000000..93b9d5d950e --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/c/package.json @@ -0,0 +1,10 @@ +{ + "name": "c", + "version": "1.0.0", + "scripts": { + "_phase:build": "node ../build.js" + }, + "dependencies": { + "b": "workspace:*" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/collate.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/collate.js new file mode 100644 index 00000000000..b81485c6b35 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/collate.js @@ -0,0 +1,34 @@ +/* eslint-env es6 */ +const path = require('path'); +const { FileSystem, Async } = require('@rushstack/node-core-library'); + +const args = process.argv.slice(2); + +const getArgument = (argumentName) => { + const index = args.findIndex((e) => e.includes(argumentName)); + return index >= 0 ? args[index].replace(`${argumentName}=`, '') : undefined; +}; + +const parentFolder = getArgument('--shard-parent-folder'); +const shards = +getArgument('--shard-count'); + +async function runAsync() { + await Async.sleepAsync(500); + + let output = ''; + for (let i = 1; i <= shards; i++) { + const outputFolder = path.resolve(parentFolder, `${i}`); + const outputFile = path.resolve(outputFolder, 'output.txt'); + FileSystem.ensureFolder(outputFolder); + output += FileSystem.readFile(outputFile, 'utf-8'); + output += '\n'; + } + const finalOutputFolder = path.resolve('coverage'); + const outputFile = path.resolve(finalOutputFolder, 'output.txt'); + FileSystem.writeFile(outputFile, output, { ensureFolderExists: true }); +} + +void runAsync().catch((err) => { + console.warn(err); + process.exit(1); +}); diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/d/config/rush-project.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/d/config/rush-project.json new file mode 100644 index 00000000000..ef7e47275c6 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/d/config/rush-project.json @@ -0,0 +1,12 @@ +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["dist"] + }, + { + "operationName": "cobuild", + "outputFolderNames": ["dist"] + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/d/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/d/package.json new file mode 100644 index 00000000000..c14d4454d2d --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/d/package.json @@ -0,0 +1,11 @@ +{ + "name": "d", + "version": "1.0.0", + "scripts": { + "_phase:build": "node ../build.js" + }, + "dependencies": { + "b": "workspace:*", + "c": "workspace:*" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/e/config/rush-project.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/e/config/rush-project.json new file mode 100644 index 00000000000..a5e85aedd69 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/e/config/rush-project.json @@ -0,0 +1,16 @@ +{ + "$schema": "../../../../../../../libraries/rush-lib/src/schemas/rush-project.schema.json", + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["dist"], + "sharding": { + "count": 75 + } + }, + { + "operationName": "_phase:build:shard", + "weight": 10 + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/e/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/e/package.json new file mode 100644 index 00000000000..b7ae63fe2ae --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/e/package.json @@ -0,0 +1,12 @@ +{ + "name": "e", + "version": "1.0.0", + "scripts": { + "_phase:build:shard": "node ../build.js e", + "_phase:build": "node ../collate.js" + }, + "dependencies": { + "b": "workspace:*", + "d": "workspace:*" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/f/config/rush-project.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/f/config/rush-project.json new file mode 100644 index 00000000000..a9e823993d0 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/f/config/rush-project.json @@ -0,0 +1,9 @@ +{ + "disableBuildCacheForProject": true, + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["dist"] + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/f/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/f/package.json new file mode 100644 index 00000000000..7bf2634a508 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/f/package.json @@ -0,0 +1,13 @@ +{ + "name": "f", + "version": "1.0.0", + "scripts": { + "cobuild": "node ../build.js", + "build": "node ../build.js", + "_phase:pre-build": "node ../pre-build.js", + "_phase:build": "node ../validate-pre-build.js && node ../build.js f" + }, + "dependencies": { + "b": "workspace:*" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/g/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/g/package.json new file mode 100644 index 00000000000..96dbe965e96 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/g/package.json @@ -0,0 +1,11 @@ +{ + "name": "g", + "version": "1.0.0", + "scripts": { + "_phase:pre-build": "node ../pre-build.js", + "_phase:build": "node ../validate-pre-build.js && node ../build.js g" + }, + "dependencies": { + "b": "workspace:*" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/h/config/rush-project.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/h/config/rush-project.json new file mode 100644 index 00000000000..748d3137142 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/h/config/rush-project.json @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../../../../libraries/rush-lib/src/schemas/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["dist"], + "sharding": { + "count": 50 + } + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/h/package.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/h/package.json new file mode 100644 index 00000000000..795b1398e5a --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/h/package.json @@ -0,0 +1,11 @@ +{ + "name": "h", + "version": "1.0.0", + "scripts": { + "_phase:build": "node ../collate h", + "_phase:build:shard": "node ../build h" + }, + "dependencies": { + "a": "workspace:*" + } +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/pre-build.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/pre-build.js new file mode 100644 index 00000000000..9c63cadd273 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/pre-build.js @@ -0,0 +1,17 @@ +/* eslint-env es6 */ +const path = require('path'); +const { FileSystem, Async } = require('@rushstack/node-core-library'); + +async function runAsync() { + await Async.sleepAsync(500); + + const outputFolder = path.resolve(process.cwd(), 'dist'); + const outputFile = path.resolve(outputFolder, 'pre-build'); + FileSystem.writeFile(outputFile, `Hello world!`, { ensureFolderExists: true }); + console.log('done'); +} + +void runAsync().catch((err) => { + console.warn(err); + process.exit(1); +}); diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/validate-pre-build.js b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/validate-pre-build.js new file mode 100644 index 00000000000..218484000e3 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/projects/validate-pre-build.js @@ -0,0 +1,13 @@ +/* eslint-env es6 */ +const path = require('path'); +const { FileSystem } = require('@rushstack/node-core-library'); + +const outputFolder = path.resolve(process.cwd(), 'dist'); +const outputFile = path.resolve(outputFolder, 'pre-build'); + +if (!FileSystem.exists(outputFile)) { + console.error(`${outputFile} does not exist.`); + process.exit(1); +} + +console.log(`${outputFile} exists`); diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/rush.json b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/rush.json new file mode 100644 index 00000000000..012281bea0d --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/sandbox/sharded-repo/rush.json @@ -0,0 +1,41 @@ +{ + "rushVersion": "5.80.0", + "pnpmVersion": "7.13.0", + "pnpmOptions": { + "useWorkspaces": true + }, + "projects": [ + { + "packageName": "a", + "projectFolder": "projects/a" + }, + { + "packageName": "b", + "projectFolder": "projects/b" + }, + { + "packageName": "c", + "projectFolder": "projects/c" + }, + { + "packageName": "d", + "projectFolder": "projects/d" + }, + { + "packageName": "e", + "projectFolder": "projects/e" + }, + { + "packageName": "f", + "projectFolder": "projects/f" + }, + { + "packageName": "g", + "projectFolder": "projects/g" + }, + { + "packageName": "h", + "projectFolder": "projects/h" + } + ] +} diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/src/paths.ts b/build-tests/rush-redis-cobuild-plugin-integration-test/src/paths.ts new file mode 100644 index 00000000000..c3dfdb36914 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/src/paths.ts @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +const sandboxRepoFolder: string = path.resolve(__dirname, '../sandbox/repo'); + +export { sandboxRepoFolder }; diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/src/runRush.ts b/build-tests/rush-redis-cobuild-plugin-integration-test/src/runRush.ts new file mode 100644 index 00000000000..953fa946c1c --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/src/runRush.ts @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { RushCommandLineParser } from '@microsoft/rush-lib/lib/cli/RushCommandLineParser'; +import * as rushLib from '@microsoft/rush-lib'; + +// Setup redis cobuild plugin +const builtInPluginConfigurations: rushLib._IBuiltInPluginConfiguration[] = []; + +const rushConfiguration: rushLib.RushConfiguration = rushLib.RushConfiguration.loadFromDefaultLocation({ + startingFolder: __dirname +}); +const project: rushLib.RushConfigurationProject | undefined = rushConfiguration.getProjectByName( + '@rushstack/rush-redis-cobuild-plugin' +); +if (!project) { + throw new Error('Project @rushstack/rush-redis-cobuild-plugin not found'); +} +builtInPluginConfigurations.push({ + packageName: '@rushstack/rush-redis-cobuild-plugin', + pluginName: 'rush-redis-cobuild-plugin', + pluginPackageFolder: project.projectFolder +}); + +async function rushRush(args: string[]): Promise { + const options: rushLib.ILaunchOptions = { + isManaged: false, + alreadyReportedNodeTooNewError: false, + builtInPluginConfigurations + }; + const parser: RushCommandLineParser = new RushCommandLineParser({ + alreadyReportedNodeTooNewError: options.alreadyReportedNodeTooNewError, + builtInPluginConfigurations: options.builtInPluginConfigurations + }); + // eslint-disable-next-line no-console + console.log(`Executing: rush ${args.join(' ')}`); + await parser + .executeAsync(args) + // eslint-disable-next-line no-console + .catch(console.error); // CommandLineParser.executeAsync() should never reject the promise +} + +// eslint-disable-next-line no-console +rushRush(process.argv.slice(2)).catch(console.error); diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/src/testLockProvider.ts b/build-tests/rush-redis-cobuild-plugin-integration-test/src/testLockProvider.ts new file mode 100644 index 00000000000..1e685b22611 --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/src/testLockProvider.ts @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { + RedisCobuildLockProvider, + type IRedisCobuildLockProviderOptions +} from '@rushstack/rush-redis-cobuild-plugin'; +import { ConsoleTerminalProvider } from '@rushstack/terminal'; +import { OperationStatus, type ICobuildContext, RushSession } from '@microsoft/rush-lib'; + +const options: IRedisCobuildLockProviderOptions = { + url: 'redis://localhost:6379', + password: 'redis123' // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Password used in unit test.")] +}; + +const rushSession: RushSession = new RushSession({ + terminalProvider: new ConsoleTerminalProvider(), + getIsDebugMode: () => true +}); + +async function main(): Promise { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const lockProvider: RedisCobuildLockProvider = new RedisCobuildLockProvider(options, rushSession as any); + await lockProvider.connectAsync(); + const context: ICobuildContext = { + contextId: 'context_id', + cacheId: 'cache_id', + lockKey: 'lock_key', + lockExpireTimeInSeconds: 30, + completedStateKey: 'completed_state_key', + clusterId: 'cluster_id', + runnerId: 'runner_id', + packageName: 'package_name', + phaseName: 'phase_name' + }; + await lockProvider.acquireLockAsync(context); + await lockProvider.renewLockAsync(context); + await lockProvider.setCompletedStateAsync(context, { + status: OperationStatus.Success, + cacheId: 'cache_id' + }); + const completedState = await lockProvider.getCompletedStateAsync(context); + // eslint-disable-next-line no-console + console.log('Completed state: ', completedState); + await lockProvider.disconnectAsync(); +} + +process.exitCode = 1; + +main() + .then(() => { + process.exitCode = 0; + }) + .catch((err) => { + // eslint-disable-next-line no-console + console.error(err); + }) + .finally(() => { + if (process.exitCode !== undefined) { + process.exit(process.exitCode); + } + }); diff --git a/build-tests/rush-redis-cobuild-plugin-integration-test/tsconfig.json b/build-tests/rush-redis-cobuild-plugin-integration-test/tsconfig.json new file mode 100644 index 00000000000..75355e8f91d --- /dev/null +++ b/build-tests/rush-redis-cobuild-plugin-integration-test/tsconfig.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib-commonjs", + "declarationDir": "lib-dts", + "rootDir": "src", + + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "esModuleInterop": true, + "strictNullChecks": true, + "noUnusedLocals": true, + "types": ["node"], + + "module": "commonjs", + "target": "es2017", + "lib": ["es2017", "DOM"] + }, + + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/build-tests/hashed-folder-copy-plugin-webpack4-test/.gitignore b/build-tests/set-webpack-public-path-plugin-test/.gitignore similarity index 100% rename from build-tests/hashed-folder-copy-plugin-webpack4-test/.gitignore rename to build-tests/set-webpack-public-path-plugin-test/.gitignore diff --git a/build-tests/set-webpack-public-path-plugin-test/config/heft.json b/build-tests/set-webpack-public-path-plugin-test/config/heft.json new file mode 100644 index 00000000000..fbb74a0db4e --- /dev/null +++ b/build-tests/set-webpack-public-path-plugin-test/config/heft.json @@ -0,0 +1,33 @@ +/** + * Defines configuration used by core Heft. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + // TODO: Add comments + "phasesByName": { + "build": { + "cleanFiles": [{ "includeGlobs": ["dist-dev", "dist-prod", "lib"] }], + + "tasksByName": { + "typescript": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-typescript-plugin" + } + }, + "lint": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-lint-plugin" + } + }, + "webpack": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-webpack5-plugin" + } + } + } + } + } +} diff --git a/build-tests/set-webpack-public-path-plugin-test/config/rush-project.json b/build-tests/set-webpack-public-path-plugin-test/config/rush-project.json new file mode 100644 index 00000000000..0dbbaa83178 --- /dev/null +++ b/build-tests/set-webpack-public-path-plugin-test/config/rush-project.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["lib-esm", "dist-dev", "dist-prod"] + } + ] +} diff --git a/build-tests/set-webpack-public-path-plugin-test/package.json b/build-tests/set-webpack-public-path-plugin-test/package.json new file mode 100644 index 00000000000..ad22692b7c5 --- /dev/null +++ b/build-tests/set-webpack-public-path-plugin-test/package.json @@ -0,0 +1,25 @@ +{ + "name": "set-webpack-public-path-plugin-test", + "description": "Building this project tests the set-webpack-public-path-plugin", + "version": "1.0.0", + "private": true, + "scripts": { + "build": "heft build --clean", + "start": "heft build-watch", + "_phase:build": "heft run --only build -- --clean" + }, + "devDependencies": { + "@rushstack/heft-lint-plugin": "workspace:*", + "@rushstack/heft-typescript-plugin": "workspace:*", + "@rushstack/heft-webpack5-plugin": "workspace:*", + "@rushstack/heft": "workspace:*", + "@rushstack/module-minifier": "workspace:*", + "@rushstack/set-webpack-public-path-plugin": "workspace:*", + "@rushstack/webpack5-module-minifier-plugin": "workspace:*", + "@types/webpack-env": "1.18.8", + "eslint": "~8.57.0", + "html-webpack-plugin": "~5.5.0", + "typescript": "~5.8.2", + "webpack": "~5.105.2" + } +} diff --git a/build-tests/set-webpack-public-path-plugin-webpack4-test/src/chunks/chunk.ts b/build-tests/set-webpack-public-path-plugin-test/src/chunks/chunk.ts similarity index 100% rename from build-tests/set-webpack-public-path-plugin-webpack4-test/src/chunks/chunk.ts rename to build-tests/set-webpack-public-path-plugin-test/src/chunks/chunk.ts diff --git a/build-tests/set-webpack-public-path-plugin-webpack4-test/src/index.ts b/build-tests/set-webpack-public-path-plugin-test/src/index.ts similarity index 100% rename from build-tests/set-webpack-public-path-plugin-webpack4-test/src/index.ts rename to build-tests/set-webpack-public-path-plugin-test/src/index.ts diff --git a/build-tests/set-webpack-public-path-plugin-test/tsconfig.json b/build-tests/set-webpack-public-path-plugin-test/tsconfig.json new file mode 100644 index 00000000000..cf3d039b7db --- /dev/null +++ b/build-tests/set-webpack-public-path-plugin-test/tsconfig.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib-esm", + "rootDir": "src", + + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strict": true, + "types": ["webpack-env"], + + "module": "esnext", + "moduleResolution": "node", + "target": "es5", + "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/build-tests/set-webpack-public-path-plugin-test/webpack.config.js b/build-tests/set-webpack-public-path-plugin-test/webpack.config.js new file mode 100644 index 00000000000..1fa002a56a4 --- /dev/null +++ b/build-tests/set-webpack-public-path-plugin-test/webpack.config.js @@ -0,0 +1,46 @@ +'use strict'; + +const { SetPublicPathPlugin } = require('@rushstack/set-webpack-public-path-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const { ModuleMinifierPlugin } = require('@rushstack/webpack5-module-minifier-plugin'); +const { WorkerPoolMinifier } = require('@rushstack/module-minifier'); + +function generateConfiguration(mode, outputFolderName) { + return { + mode: mode, + target: ['web', 'es5'], + entry: { + 'test-bundle': `${__dirname}/lib-esm/index.js` + }, + output: { + path: `${__dirname}/${outputFolderName}`, + filename: '[name]_[contenthash].js', + chunkFilename: '[id].[name]_[contenthash].js' + }, + plugins: [ + new SetPublicPathPlugin({ + scriptName: { + useAssetName: true + } + }), + new HtmlWebpackPlugin() + ], + optimization: { + minimizer: [ + new ModuleMinifierPlugin({ + minifier: new WorkerPoolMinifier({ + terserOptions: { + ecma: 5 + } + }), + useSourceMap: true + }) + ] + } + }; +} + +module.exports = [ + generateConfiguration('development', 'dist-dev'), + generateConfiguration('production', 'dist-prod') +]; diff --git a/build-tests/set-webpack-public-path-plugin-webpack4-test/config/heft.json b/build-tests/set-webpack-public-path-plugin-webpack4-test/config/heft.json deleted file mode 100644 index 0eb2d2880d6..00000000000 --- a/build-tests/set-webpack-public-path-plugin-webpack4-test/config/heft.json +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Defines configuration used by core Heft. - */ -{ - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", - - // TODO: Add comments - "phasesByName": { - "build": { - "cleanFiles": [{ "sourcePath": "dist-dev" }, { "sourcePath": "dist-prod" }, { "sourcePath": "lib" }], - "tasksByName": { - "typescript": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft-typescript-plugin" - } - }, - "lint": { - "taskDependencies": ["typescript"], - "taskPlugin": { - "pluginPackage": "@rushstack/heft-lint-plugin" - } - }, - "webpack": { - "taskDependencies": ["typescript"], - "taskPlugin": { - "pluginPackage": "@rushstack/heft-webpack4-plugin" - } - } - } - } - } -} diff --git a/build-tests/set-webpack-public-path-plugin-webpack4-test/config/rush-project.json b/build-tests/set-webpack-public-path-plugin-webpack4-test/config/rush-project.json deleted file mode 100644 index c861eda9bd6..00000000000 --- a/build-tests/set-webpack-public-path-plugin-webpack4-test/config/rush-project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "operationSettings": [ - { - "operationName": "build", - "outputFolderNames": ["lib", "dist-dev", "dist-prod"] - } - ] -} diff --git a/build-tests/set-webpack-public-path-plugin-webpack4-test/package.json b/build-tests/set-webpack-public-path-plugin-webpack4-test/package.json deleted file mode 100644 index d2e2b3c009f..00000000000 --- a/build-tests/set-webpack-public-path-plugin-webpack4-test/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "set-webpack-public-path-plugin-webpack4-test", - "description": "Building this project tests the set-webpack-public-path-plugin using Webpack 4", - "version": "1.0.0", - "private": true, - "scripts": { - "build": "heft build --clean", - "start": "heft build-watch", - "_phase:build": "heft run --only build -- --clean" - }, - "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft": "workspace:*", - "@rushstack/heft-lint-plugin": "workspace:*", - "@rushstack/heft-typescript-plugin": "workspace:*", - "@rushstack/heft-webpack4-plugin": "workspace:*", - "@rushstack/set-webpack-public-path-plugin": "workspace:*", - "@types/webpack-env": "1.18.0", - "eslint": "~8.7.0", - "html-webpack-plugin": "~4.5.2", - "typescript": "~5.0.4", - "webpack": "~4.44.2" - } -} diff --git a/build-tests/set-webpack-public-path-plugin-webpack4-test/tsconfig.json b/build-tests/set-webpack-public-path-plugin-webpack4-test/tsconfig.json deleted file mode 100644 index dad0392042f..00000000000 --- a/build-tests/set-webpack-public-path-plugin-webpack4-test/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/tsconfig", - - "compilerOptions": { - "outDir": "lib", - "rootDir": "src", - - "forceConsistentCasingInFileNames": true, - "jsx": "react", - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "inlineSources": true, - "experimentalDecorators": true, - "strict": true, - "types": ["webpack-env"], - - "module": "esnext", - "moduleResolution": "node", - "target": "es5", - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"] - }, - "include": ["src/**/*.ts", "src/**/*.tsx"] -} diff --git a/build-tests/set-webpack-public-path-plugin-webpack4-test/webpack.config.js b/build-tests/set-webpack-public-path-plugin-webpack4-test/webpack.config.js deleted file mode 100644 index 0e8f53728a9..00000000000 --- a/build-tests/set-webpack-public-path-plugin-webpack4-test/webpack.config.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -const { SetPublicPathPlugin } = require('@rushstack/set-webpack-public-path-plugin'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); - -function generateConfiguration(mode, outputFolderName) { - return { - mode: mode, - entry: { - 'test-bundle': `${__dirname}/lib/index.js` - }, - output: { - path: `${__dirname}/${outputFolderName}`, - filename: '[name]_[contenthash].js', - chunkFilename: '[id].[name]_[contenthash].js' - }, - plugins: [ - new SetPublicPathPlugin({ - scriptName: { - useAssetName: true - } - }), - new HtmlWebpackPlugin() - ] - }; -} - -module.exports = [ - generateConfiguration('development', 'dist-dev'), - generateConfiguration('production', 'dist-prod') -]; diff --git a/build-tests/ts-command-line-test/.vscode/launch.json b/build-tests/ts-command-line-test/.vscode/launch.json deleted file mode 100644 index ac7c0efca90..00000000000 --- a/build-tests/ts-command-line-test/.vscode/launch.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Widget CLI", - "program": "${workspaceFolder}/lib/start.js", - "args": [ "run", "1", "2", "3" ] - } - ] -} \ No newline at end of file diff --git a/build-tests/ts-command-line-test/README.md b/build-tests/ts-command-line-test/README.md deleted file mode 100644 index c85f938dbd0..00000000000 --- a/build-tests/ts-command-line-test/README.md +++ /dev/null @@ -1,139 +0,0 @@ -# ts-command-line-test - -This project folder is a minimal code sample illustrating how to make a command-line tool -using the [@rushstack/ts-command-line](https://www.npmjs.com/package/@rushstack/ts-command-line) library. -Building this project is one of the CI tests for the library. - -## Trying the demo - -Compile the project: -```sh -# clone the repo -$ git clone https://github.com/microsoft/rushstack -$ cd rushstack - -# build the code -$ rush install -$ rush rebuild - -# run the demo using Bash -$ cd build_tests/ts-command-line-test -$ ./widget.sh --help - -# OR, run the demo using Windows shell -$ cd build_tests\ts-command-line-test -$ widget --help -``` - -You should see something like this: - -``` -usage: widget [-h] [-v] ... - -The "widget" tool is a code sample for using the @rushstack/ts-command-line -library. - -Positional arguments: - - push Pushes a widget to the service - run This action (hypothetically) passes its command line - arguments to the shell to be executed. - -Optional arguments: - -h, --help Show this help message and exit. - -v, --verbose Show extra logging detail - -For detailed help about a specific command, use: widget -h -``` - -This top-level command line is defined in [WidgetCommandLine.ts](./src/WidgetCommandLine.ts). - -## Command line "actions" - -Actions are an optional feature of **ts-command-line**. They work like Git subcommands. -Our `widget` demo supports two actions, `push` and `run`. For example, if you type this: - -```sh -$ ./widget.sh push --help -``` - -...then you should see specialized help for the "push" action: - -``` -usage: widget push [-h] [-f] [--protocol {ftp,webdav,scp}] - -Here we provide a longer description of how our action works. - -Optional arguments: - -h, --help Show this help message and exit. - -f, --force Push and overwrite any existing state - --protocol {ftp,webdav,scp} - Specify the protocol to use. This parameter may - alternatively specified via the WIDGET_PROTOCOL - environment variable. The default value is "scp". -``` - -The "push" action is defined in [PushAction.ts](./src/PushAction.ts). - - -The demo prints its command line arguments when you invoke the action: - -```sh -$ ./widget.sh push --protocol webdav --force - -Business logic configured the logger: verbose=false -Received parameters: force=true, protocol="webdav" -Business logic did the work. -``` - -## Some advanced features - -The `run` command illustrates a couple other interesting features. It shows how to -use `defineCommandLineRemainder()` to capture the remainder of the command line arguments. - -``` -usage: widget run [-h] [--title TITLE] ... - -This demonstrates how to use the defineCommandLineRemainder() API. - -Positional arguments: - "..." The remaining arguments are passed along to the command - shell. - -Optional arguments: - -h, --help Show this help message and exit. - --title TITLE An optional title to show in the console window. This - parameter may alternatively specified via the WIDGET_TITLE - environment variable. -``` - -The "run" action is defined in [RunAction.ts](./src/RunAction.ts). - -Example invocation: - -```sh -$ ./widget.sh run --title "Hello" 1 2 3 - -Business logic configured the logger: verbose=false -Console Title: Hello -Arguments to be executed: ["1","2","3"] -``` - -Also, notice that `environmentVariable: 'WIDGET_TITLE'` allows the title to be specified using a -Bash environment variable: - -```sh -$ export WIDGET_TITLE="Default title" -$ ./widget.sh run 1 2 3 - -Business logic configured the logger: verbose=false -Console Title: Default title -Arguments to be executed: ["1","2","3"] -``` - -For more about environment variables, see the [IBaseCommandLineDefinition.environmentVariable](https://rushstack.io/pages/api/ts-command-line.ibasecommandlinedefinition.environmentvariable/) documentation. - -## More information - -See [@rushstack/ts-command-line](https://www.npmjs.com/package/@rushstack/ts-command-line) for details. - diff --git a/build-tests/ts-command-line-test/build.js b/build-tests/ts-command-line-test/build.js deleted file mode 100644 index 1de2bf979c8..00000000000 --- a/build-tests/ts-command-line-test/build.js +++ /dev/null @@ -1,20 +0,0 @@ -const fsx = require('fs-extra'); -const child_process = require('child_process'); -const path = require('path'); -const process = require('process'); - -function executeCommand(command) { - console.log('---> ' + command); - child_process.execSync(command, { stdio: 'inherit' }); -} - -// Clean the old build outputs -console.log(`==> Starting build.js for ${path.basename(process.cwd())}`); -fsx.emptyDirSync('dist'); -fsx.emptyDirSync('lib'); -fsx.emptyDirSync('temp'); - -// Run the TypeScript compiler -executeCommand('node node_modules/typescript/lib/tsc'); - -console.log(`==> Finished build.js for ${path.basename(process.cwd())}`); diff --git a/build-tests/ts-command-line-test/config/rush-project.json b/build-tests/ts-command-line-test/config/rush-project.json deleted file mode 100644 index 247dc17187a..00000000000 --- a/build-tests/ts-command-line-test/config/rush-project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "operationSettings": [ - { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] - } - ] -} diff --git a/build-tests/ts-command-line-test/package.json b/build-tests/ts-command-line-test/package.json deleted file mode 100644 index 7a557f8caa2..00000000000 --- a/build-tests/ts-command-line-test/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "ts-command-line-test", - "description": "Building this project is a regression test for ts-command-line", - "version": "1.0.0", - "private": true, - "scripts": { - "build": "node build.js", - "start": "node ./lib/start.js", - "_phase:build": "node build.js" - }, - "devDependencies": { - "@rushstack/ts-command-line": "workspace:*", - "@types/node": "14.18.36", - "fs-extra": "~7.0.1", - "typescript": "~5.0.4" - } -} diff --git a/build-tests/ts-command-line-test/src/BusinessLogic.ts b/build-tests/ts-command-line-test/src/BusinessLogic.ts deleted file mode 100644 index 2607ee80678..00000000000 --- a/build-tests/ts-command-line-test/src/BusinessLogic.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -export class BusinessLogic { - public static async doTheWork(force: boolean, protocol: string): Promise { - console.log(`Received parameters: force=${force}, protocol="${protocol}"`); - console.log(`Business logic did the work.`); - } - - public static configureLogger(verbose: boolean): void { - console.log(`Business logic configured the logger: verbose=${verbose}`); - } -} diff --git a/build-tests/ts-command-line-test/src/PushAction.ts b/build-tests/ts-command-line-test/src/PushAction.ts deleted file mode 100644 index e66ae4a7336..00000000000 --- a/build-tests/ts-command-line-test/src/PushAction.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { - CommandLineFlagParameter, - CommandLineAction, - CommandLineChoiceParameter -} from '@rushstack/ts-command-line'; -import { BusinessLogic } from './BusinessLogic'; - -export class PushAction extends CommandLineAction { - private _force: CommandLineFlagParameter; - private _protocol: CommandLineChoiceParameter; - - public constructor() { - super({ - actionName: 'push', - summary: 'Pushes a widget to the service', - documentation: 'Here we provide a longer description of how our action works.' - }); - } - - protected onExecute(): Promise { - // abstract - return BusinessLogic.doTheWork(this._force.value, this._protocol.value || '(none)'); - } - - protected onDefineParameters(): void { - // abstract - this._force = this.defineFlagParameter({ - parameterLongName: '--force', - parameterShortName: '-f', - description: 'Push and overwrite any existing state' - }); - - this._protocol = this.defineChoiceParameter({ - parameterLongName: '--protocol', - description: 'Specify the protocol to use', - alternatives: ['ftp', 'webdav', 'scp'], - environmentVariable: 'WIDGET_PROTOCOL', - defaultValue: 'scp' - }); - } -} diff --git a/build-tests/ts-command-line-test/src/WidgetCommandLine.ts b/build-tests/ts-command-line-test/src/WidgetCommandLine.ts deleted file mode 100644 index 01e73a61c5a..00000000000 --- a/build-tests/ts-command-line-test/src/WidgetCommandLine.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { CommandLineParser, CommandLineFlagParameter } from '@rushstack/ts-command-line'; -import { PushAction } from './PushAction'; -import { RunAction } from './RunAction'; -import { BusinessLogic } from './BusinessLogic'; - -export class WidgetCommandLine extends CommandLineParser { - private _verbose: CommandLineFlagParameter; - - public constructor() { - super({ - toolFilename: 'widget', - toolDescription: 'The "widget" tool is a code sample for using the @rushstack/ts-command-line library.' - }); - - this.addAction(new PushAction()); - this.addAction(new RunAction()); - } - - protected onDefineParameters(): void { - // abstract - this._verbose = this.defineFlagParameter({ - parameterLongName: '--verbose', - parameterShortName: '-v', - description: 'Show extra logging detail' - }); - } - - protected onExecute(): Promise { - // override - BusinessLogic.configureLogger(this._verbose.value); - return super.onExecute(); - } -} diff --git a/build-tests/ts-command-line-test/src/start.ts b/build-tests/ts-command-line-test/src/start.ts deleted file mode 100644 index b914fed9264..00000000000 --- a/build-tests/ts-command-line-test/src/start.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { WidgetCommandLine } from './WidgetCommandLine'; - -const commandLine: WidgetCommandLine = new WidgetCommandLine(); -commandLine.execute(); diff --git a/build-tests/ts-command-line-test/tsconfig.json b/build-tests/ts-command-line-test/tsconfig.json deleted file mode 100644 index 3b5e127e2c0..00000000000 --- a/build-tests/ts-command-line-test/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", - "forceConsistentCasingInFileNames": true, - "module": "commonjs", - "declaration": true, - "sourceMap": true, - "experimentalDecorators": true, - "strictNullChecks": true, - "types": ["node"], - "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "outDir": "lib" - }, - "include": ["src/**/*.ts"] -} diff --git a/build-tests/ts-command-line-test/widget.cmd b/build-tests/ts-command-line-test/widget.cmd deleted file mode 100644 index 413bf5ae5c4..00000000000 --- a/build-tests/ts-command-line-test/widget.cmd +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -node .\lib\start.js %* diff --git a/build-tests/ts-command-line-test/widget.sh b/build-tests/ts-command-line-test/widget.sh deleted file mode 100644 index aab14bc9142..00000000000 --- a/build-tests/ts-command-line-test/widget.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -exec node ./lib/start.js "$@" diff --git a/build-tests/set-webpack-public-path-plugin-webpack4-test/.gitignore b/build-tests/webpack-local-version-test/.gitignore similarity index 100% rename from build-tests/set-webpack-public-path-plugin-webpack4-test/.gitignore rename to build-tests/webpack-local-version-test/.gitignore diff --git a/build-tests/webpack-local-version-test/config/heft.json b/build-tests/webpack-local-version-test/config/heft.json new file mode 100644 index 00000000000..fdda2eb70f8 --- /dev/null +++ b/build-tests/webpack-local-version-test/config/heft.json @@ -0,0 +1,33 @@ +/** + * Defines configuration used by core Heft. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + // TODO: Add comments + "phasesByName": { + "build": { + "cleanFiles": [{ "includeGlobs": ["lib"] }], + + "tasksByName": { + "typescript": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-typescript-plugin" + } + }, + "lint": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-lint-plugin" + } + }, + "webpack": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-webpack5-plugin" + } + } + } + } + } +} diff --git a/build-tests/webpack-local-version-test/config/rush-project.json b/build-tests/webpack-local-version-test/config/rush-project.json new file mode 100644 index 00000000000..c1256b277a7 --- /dev/null +++ b/build-tests/webpack-local-version-test/config/rush-project.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["lib-esm", "dist"] + } + ] +} diff --git a/build-tests/webpack-local-version-test/package.json b/build-tests/webpack-local-version-test/package.json new file mode 100644 index 00000000000..c77095bd4d5 --- /dev/null +++ b/build-tests/webpack-local-version-test/package.json @@ -0,0 +1,22 @@ +{ + "name": "webpack-local-version-test", + "description": "Building this project tests the rig loading for the local version of webpack", + "version": "1.0.0", + "private": true, + "scripts": { + "build": "heft --debug build --clean", + "start": "heft build-watch", + "_phase:build": "heft run --only build -- --clean" + }, + "devDependencies": { + "@rushstack/heft-lint-plugin": "workspace:*", + "@rushstack/heft-typescript-plugin": "workspace:*", + "@rushstack/heft-webpack5-plugin": "workspace:*", + "@rushstack/heft": "workspace:*", + "@types/webpack-env": "1.18.8", + "eslint": "~9.25.1", + "html-webpack-plugin": "~5.5.0", + "typescript": "~5.8.2", + "webpack": "~5.105.0" + } +} diff --git a/build-tests/webpack-local-version-test/src/index.ts b/build-tests/webpack-local-version-test/src/index.ts new file mode 100644 index 00000000000..b0d8da56b50 --- /dev/null +++ b/build-tests/webpack-local-version-test/src/index.ts @@ -0,0 +1,3 @@ +console.log('Hello world!'); + +export const test = 'Hello world!'; diff --git a/build-tests/webpack-local-version-test/tsconfig.json b/build-tests/webpack-local-version-test/tsconfig.json new file mode 100644 index 00000000000..cf3d039b7db --- /dev/null +++ b/build-tests/webpack-local-version-test/tsconfig.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib-esm", + "rootDir": "src", + + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strict": true, + "types": ["webpack-env"], + + "module": "esnext", + "moduleResolution": "node", + "target": "es5", + "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"] +} diff --git a/build-tests/webpack-local-version-test/webpack.config.js b/build-tests/webpack-local-version-test/webpack.config.js new file mode 100644 index 00000000000..7a72077f946 --- /dev/null +++ b/build-tests/webpack-local-version-test/webpack.config.js @@ -0,0 +1,31 @@ +'use strict'; + +module.exports = ({ webpack }) => { + console.log(`Webpack version: ${webpack.version}`); + const localWebpack = require.resolve('webpack'); + const bundledWebpack = require.resolve('webpack', { + paths: [require.resolve('@rushstack/heft-webpack5-plugin')] + }); + const localWebpackInstance = require(localWebpack); + const bundledWebpackInstance = require(bundledWebpack); + if (localWebpack === bundledWebpack || localWebpackInstance === bundledWebpackInstance) { + throw new Error('Webpack versions match between bundled and local, cannot test rig loading.'); + } + if (webpack.version !== localWebpackInstance.version) { + throw new Error('Webpack is not the same version as the local installation'); + } + + // Verify that the Compiler instances match the local version. + if (webpack.Compiler !== localWebpackInstance.Compiler) { + throw new Error('Webpack instances do not match the local installation'); + } + if (webpack.Compiler === bundledWebpackInstance.Compiler) { + throw new Error('Received webpack instance is the same as the bundled version'); + } + return { + mode: 'development', + entry: { + 'test-bundle': `${__dirname}/lib-esm/index.js` + } + }; +}; diff --git a/common/autoinstallers/plugins/package.json b/common/autoinstallers/plugins/package.json new file mode 100644 index 00000000000..3fd5d246285 --- /dev/null +++ b/common/autoinstallers/plugins/package.json @@ -0,0 +1,8 @@ +{ + "name": "plugins", + "version": "1.0.0", + "private": true, + "dependencies": { + "@rushstack/rush-published-versions-json-plugin": "0.1.15" + } +} diff --git a/common/autoinstallers/plugins/pnpm-lock.yaml b/common/autoinstallers/plugins/pnpm-lock.yaml new file mode 100644 index 00000000000..0d71a2de247 --- /dev/null +++ b/common/autoinstallers/plugins/pnpm-lock.yaml @@ -0,0 +1,308 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@rushstack/rush-published-versions-json-plugin': + specifier: 0.1.15 + version: 0.1.15 + +packages: + + '@pnpm/lockfile.types@900.0.0': + resolution: {integrity: sha512-/4+3CAu4uIjx0ln1DYXNdj0qKJ3wyRDY+RS+eFzV6OHjreaTKWsF2WcjigYp1M5mxL4kj2RsRGgBGEyKtCfEWg==} + engines: {node: '>=18.12'} + + '@pnpm/patching.types@900.0.0': + resolution: {integrity: sha512-A/3kgRD4Xy2tBMPjOBdx5ZdgmpUobphzWkqDB72S5SIB6gdyCg32AUV0/aO12DwMxpT7kyqMhkkynUOPBfdlUQ==} + engines: {node: '>=18.12'} + + '@pnpm/types@900.0.0': + resolution: {integrity: sha512-GucC9h/EVbU03Kl7M/FqVes1s5RCQaGCW2f41lFA7VqqHWQElR6k1q33iF6f6fXDUSCdzB1IUxSq9ghP2J+8Pw==} + engines: {node: '>=18.12'} + + '@rushstack/credential-cache@0.2.21': + resolution: {integrity: sha512-8bs1WW7da5F5WE7gu35XrhCM/l1+n9ksTqTkdc+QzXwFNe5l1aRzInrqED5z2RLCvj2Mx+FwgyRrkPe8hJpTxg==} + + '@rushstack/lookup-by-path@0.10.10': + resolution: {integrity: sha512-bEPM3G65CeKR9sByZIF0lH75I5E4bGbTAuQIPRQLNeI7ISQJkQa5UenAp/e0HoPiLNfBB9bxySOEyq1qBjGlcA==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/node-core-library@5.23.3': + resolution: {integrity: sha512-f6uuza7Um65bwsIJgf0MRs7IPA5IG+A+zs1AYGQvpZmLjtTGdfHowhQw4kwF0pPhJCrU4UHNhK8Qa6tLygYZCA==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/package-deps-hash@4.7.23': + resolution: {integrity: sha512-lqzReyS1yGyBO6FIA6nnJr1/srjQS6f++o9T54YP6zUZ3wLoF6tm5Esc82JEo2t2aonQKpZKOVHoC+a2pAKCnQ==} + + '@rushstack/problem-matcher@0.2.1': + resolution: {integrity: sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/rush-published-versions-json-plugin@0.1.15': + resolution: {integrity: sha512-/XcQxEFfP3ab0KNh2oUkpMEvXqpLbMgnHIPHsIOA0QI+NAWikNGFSll/4Rop4I1ZXJQ62teeWYnGBOn7VQjVsg==} + + '@rushstack/rush-sdk@5.178.0': + resolution: {integrity: sha512-9dic8gRauC0CurAHNs6Muh2Nb9/M2GacuD28E3MTpMnPzC9hy7Lz4p5E5NO+piKFruhKOIICLiLNCRazSRvr3w==} + + '@rushstack/terminal@0.24.2': + resolution: {integrity: sha512-KB7PpvzDyKMw/RGU3TxOwxTs3OwZ4gq6+WHlTJN/JfQH4ezliNtWIqver78jTaAJyz/ZAAlJGH7a/M1WyFLFSw==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-uri@3.1.4: + resolution: {integrity: sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==} + + fs-extra@11.3.6: + resolution: {integrity: sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==} + engines: {node: '>=14.14'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + +snapshots: + + '@pnpm/lockfile.types@900.0.0': + dependencies: + '@pnpm/patching.types': 900.0.0 + '@pnpm/types': 900.0.0 + + '@pnpm/patching.types@900.0.0': {} + + '@pnpm/types@900.0.0': {} + + '@rushstack/credential-cache@0.2.21': + dependencies: + '@rushstack/node-core-library': 5.23.3 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/lookup-by-path@0.10.10': {} + + '@rushstack/node-core-library@5.23.3': + dependencies: + ajv: 8.20.0 + ajv-draft-04: 1.0.0(ajv@8.20.0) + ajv-formats: 3.0.1(ajv@8.20.0) + fs-extra: 11.3.6 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.12 + semver: 7.7.4 + + '@rushstack/package-deps-hash@4.7.23': + dependencies: + '@rushstack/node-core-library': 5.23.3 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/problem-matcher@0.2.1': {} + + '@rushstack/rush-published-versions-json-plugin@0.1.15': + dependencies: + '@rushstack/node-core-library': 5.23.3 + '@rushstack/rush-sdk': 5.178.0 + '@rushstack/terminal': 0.24.2 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/rush-sdk@5.178.0': + dependencies: + '@pnpm/lockfile.types-900': '@pnpm/lockfile.types@900.0.0' + '@rushstack/credential-cache': 0.2.21 + '@rushstack/lookup-by-path': 0.10.10 + '@rushstack/node-core-library': 5.23.3 + '@rushstack/package-deps-hash': 4.7.23 + '@rushstack/terminal': 0.24.2 + tapable: 2.2.1 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/terminal@0.24.2': + dependencies: + '@rushstack/node-core-library': 5.23.3 + '@rushstack/problem-matcher': 0.2.1 + supports-color: 8.1.1 + + ajv-draft-04@1.0.0(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv-formats@3.0.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.4 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + es-errors@1.3.0: {} + + fast-deep-equal@3.1.3: {} + + fast-uri@3.1.4: {} + + fs-extra@11.3.6: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + function-bind@1.1.2: {} + + graceful-fs@4.2.11: {} + + has-flag@4.0.0: {} + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + import-lazy@4.0.0: {} + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.4 + + jju@1.4.0: {} + + json-schema-traverse@1.0.0: {} + + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + path-parse@1.0.7: {} + + require-from-string@2.0.2: {} + + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + semver@7.7.4: {} + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tapable@2.2.1: {} + + universalify@2.0.1: {} diff --git a/common/autoinstallers/plugins/rush-plugins/@rushstack/rush-published-versions-json-plugin/rush-plugin-manifest.json b/common/autoinstallers/plugins/rush-plugins/@rushstack/rush-published-versions-json-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000000..03631898b98 --- /dev/null +++ b/common/autoinstallers/plugins/rush-plugins/@rushstack/rush-published-versions-json-plugin/rush-plugin-manifest.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-plugin-manifest.schema.json", + "plugins": [ + { + "pluginName": "rush-published-versions-json-plugin", + "description": "A Rush plugin for generating a JSON file containing the versions of all published packages in the monorepo.", + "entryPoint": "./lib-commonjs/index.js", + "associatedCommands": ["record-published-versions"], + "commandLineJsonFilePath": "./command-line.json" + } + ] +} diff --git a/common/autoinstallers/plugins/rush-plugins/@rushstack/rush-published-versions-json-plugin/rush-published-versions-json-plugin/command-line.json b/common/autoinstallers/plugins/rush-plugins/@rushstack/rush-published-versions-json-plugin/rush-published-versions-json-plugin/command-line.json new file mode 100644 index 00000000000..248cc35030b --- /dev/null +++ b/common/autoinstallers/plugins/rush-plugins/@rushstack/rush-published-versions-json-plugin/rush-published-versions-json-plugin/command-line.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json", + "commands": [ + { + "commandKind": "globalPlugin", + "name": "record-published-versions", + "summary": "Generates a JSON file recording the version numbers of all published packages.", + "safeForSimultaneousRushProcesses": true + } + ], + "parameters": [ + { + "parameterKind": "string", + "longName": "--output-path", + "shortName": "-o", + "argumentName": "FILE_PATH", + "description": "The path to the output JSON file. Relative paths are resolved from the repo root.", + "associatedCommands": ["record-published-versions"], + "required": true + } + ] +} diff --git a/common/autoinstallers/rush-prettier/package.json b/common/autoinstallers/rush-prettier/package.json index ce4353cd713..2b3a8597862 100644 --- a/common/autoinstallers/rush-prettier/package.json +++ b/common/autoinstallers/rush-prettier/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "dependencies": { - "pretty-quick": "3.1.3", - "prettier": "2.7.1" + "pretty-quick": "4.2.2", + "prettier": "3.6.2" } } diff --git a/common/autoinstallers/rush-prettier/pnpm-lock.yaml b/common/autoinstallers/rush-prettier/pnpm-lock.yaml index a6fdae36c03..abc23cf08b4 100644 --- a/common/autoinstallers/rush-prettier/pnpm-lock.yaml +++ b/common/autoinstallers/rush-prettier/pnpm-lock.yaml @@ -1,296 +1,84 @@ -lockfileVersion: 5.4 +lockfileVersion: '9.0' -specifiers: - prettier: 2.7.1 - pretty-quick: 3.1.3 +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false -dependencies: - prettier: 2.7.1 - pretty-quick: 3.1.3_prettier@2.7.1 +importers: -packages: - - /@types/minimatch/3.0.5: - resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} - dev: false - - /ansi-styles/4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 - dev: false - - /array-differ/3.0.0: - resolution: {integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==} - engines: {node: '>=8'} - dev: false - - /array-union/2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - dev: false - - /arrify/2.0.1: - resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} - engines: {node: '>=8'} - dev: false - - /balanced-match/1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: false - - /brace-expansion/1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - dev: false - - /chalk/3.0.0: - resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} - engines: {node: '>=8'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: false - - /color-convert/2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - dependencies: - color-name: 1.1.4 - dev: false - - /color-name/1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: false - - /concat-map/0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} - dev: false - - /cross-spawn/7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - dev: false - - /end-of-stream/1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - dependencies: - once: 1.4.0 - dev: false - - /execa/4.1.0: - resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} - engines: {node: '>=10'} + .: dependencies: - cross-spawn: 7.0.3 - get-stream: 5.2.0 - human-signals: 1.1.1 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - dev: false + prettier: + specifier: 3.6.2 + version: 3.6.2 + pretty-quick: + specifier: 4.2.2 + version: 4.2.2(prettier@3.6.2) - /find-up/4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - dev: false - - /get-stream/5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - dependencies: - pump: 3.0.0 - dev: false - - /has-flag/4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - dev: false +packages: - /human-signals/1.1.1: - resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} - engines: {node: '>=8.12.0'} - dev: false + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - /ignore/5.2.0: - resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} - dev: false - - /is-stream/2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: false - - /isexe/2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: false - /locate-path/5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - dependencies: - p-locate: 4.1.0 - dev: false - - /merge-stream/2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: false - - /mimic-fn/2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - dev: false - - /minimatch/3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - dependencies: - brace-expansion: 1.1.11 - dev: false - - /mri/1.2.0: + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} - dev: false - - /multimatch/4.0.0: - resolution: {integrity: sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==} - engines: {node: '>=8'} - dependencies: - '@types/minimatch': 3.0.5 - array-differ: 3.0.0 - array-union: 2.1.0 - arrify: 2.0.1 - minimatch: 3.1.2 - dev: false - - /npm-run-path/4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - dependencies: - path-key: 3.1.1 - dev: false - /once/1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - dependencies: - wrappy: 1.0.2 - dev: false + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - /onetime/5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - dependencies: - mimic-fn: 2.1.0 - dev: false - - /p-limit/2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - dependencies: - p-try: 2.2.0 - dev: false + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} - /p-locate/4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - dependencies: - p-limit: 2.3.0 - dev: false + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true - /p-try/2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - dev: false + pretty-quick@4.2.2: + resolution: {integrity: sha512-uAh96tBW1SsD34VhhDmWuEmqbpfYc/B3j++5MC/6b3Cb8Ow7NJsvKFhg0eoGu2xXX+o9RkahkTK6sUdd8E7g5w==} + engines: {node: '>=14'} + hasBin: true + peerDependencies: + prettier: ^3.0.0 - /path-exists/4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: false + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - /path-key/3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - dev: false + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - /prettier/2.7.1: - resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==} - engines: {node: '>=10.13.0'} - hasBin: true - dev: false +snapshots: - /pretty-quick/3.1.3_prettier@2.7.1: - resolution: {integrity: sha512-kOCi2FJabvuh1as9enxYmrnBC6tVMoVOenMaBqRfsvBHB0cbpYHjdQEpSglpASDFEXVwplpcGR4CLEaisYAFcA==} - engines: {node: '>=10.13'} - hasBin: true - peerDependencies: - prettier: '>=2.0.0' - dependencies: - chalk: 3.0.0 - execa: 4.1.0 - find-up: 4.1.0 - ignore: 5.2.0 - mri: 1.2.0 - multimatch: 4.0.0 - prettier: 2.7.1 - dev: false + '@pkgr/core@0.2.9': {} - /pump/3.0.0: - resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} - dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 - dev: false + ignore@7.0.5: {} - /shebang-command/2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - dependencies: - shebang-regex: 3.0.0 - dev: false + mri@1.2.0: {} - /shebang-regex/3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - dev: false + picocolors@1.1.1: {} - /signal-exit/3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: false + picomatch@4.0.3: {} - /strip-final-newline/2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - dev: false + prettier@3.6.2: {} - /supports-color/7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + pretty-quick@4.2.2(prettier@3.6.2): dependencies: - has-flag: 4.0.0 - dev: false + '@pkgr/core': 0.2.9 + ignore: 7.0.5 + mri: 1.2.0 + picocolors: 1.1.1 + picomatch: 4.0.3 + prettier: 3.6.2 + tinyexec: 0.3.2 + tslib: 2.8.1 - /which/2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - dependencies: - isexe: 2.0.0 - dev: false + tinyexec@0.3.2: {} - /wrappy/1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: false + tslib@2.8.1: {} diff --git a/common/changes/@microsoft/api-extractor-model/automated-bump-decoupled-deps_2026-07-20-18-12-58.json b/common/changes/@microsoft/api-extractor-model/automated-bump-decoupled-deps_2026-07-20-18-12-58.json new file mode 100644 index 00000000000..300fd9139cf --- /dev/null +++ b/common/changes/@microsoft/api-extractor-model/automated-bump-decoupled-deps_2026-07-20-18-12-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@microsoft/api-extractor-model" + } + ], + "packageName": "@microsoft/api-extractor-model", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@microsoft/api-extractor-model/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@microsoft/api-extractor-model/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..300fd9139cf --- /dev/null +++ b/common/changes/@microsoft/api-extractor-model/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@microsoft/api-extractor-model" + } + ], + "packageName": "@microsoft/api-extractor-model", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@microsoft/api-extractor-model/forbid-private-static_2026-07-19-02-14-57.json b/common/changes/@microsoft/api-extractor-model/forbid-private-static_2026-07-19-02-14-57.json new file mode 100644 index 00000000000..52f6a7d52bc --- /dev/null +++ b/common/changes/@microsoft/api-extractor-model/forbid-private-static_2026-07-19-02-14-57.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@microsoft/api-extractor-model" + } + ], + "packageName": "@microsoft/api-extractor-model", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@microsoft/api-extractor/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@microsoft/api-extractor/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..c767a964ce4 --- /dev/null +++ b/common/changes/@microsoft/api-extractor/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@microsoft/api-extractor" + } + ], + "packageName": "@microsoft/api-extractor", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@microsoft/rush/add-workspace-cycle-detection_2026-07-23-01-32.json b/common/changes/@microsoft/rush/add-workspace-cycle-detection_2026-07-23-01-32.json new file mode 100644 index 00000000000..970da9764da --- /dev/null +++ b/common/changes/@microsoft/rush/add-workspace-cycle-detection_2026-07-23-01-32.json @@ -0,0 +1,9 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "Add early validation to `rush install`/`rush update` that immediately fails with a meaningful error message if an undeclared cycle is detected among workspace packages, specifying the cycle path.", + "type": "minor" + } + ] +} diff --git a/common/changes/@microsoft/rush/copilot-remove-external-filter_2026-03-16-21-07.json b/common/changes/@microsoft/rush/copilot-remove-external-filter_2026-03-16-21-07.json new file mode 100644 index 00000000000..e91bc81bdc6 --- /dev/null +++ b/common/changes/@microsoft/rush/copilot-remove-external-filter_2026-03-16-21-07.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "comment": "In PnpmShrinkwrapFile getIntegrityForImporter, remove the external filter and include workspace-local link: dependencies by recursing into their importer entries, so shrinkwrap-deps.json hashes cover the full dependency tree.", + "type": "patch", + "packageName": "@microsoft/rush" + } + ], + "packageName": "@microsoft/rush" +} diff --git a/common/changes/@microsoft/rush/feat-log-for-build-cache_2023-05-23-11-22.json b/common/changes/@microsoft/rush/feat-log-for-build-cache_2023-05-23-11-22.json deleted file mode 100644 index cfbe0ac39f4..00000000000 --- a/common/changes/@microsoft/rush/feat-log-for-build-cache_2023-05-23-11-22.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@microsoft/rush", - "comment": "Use a separate temrinal for logging cache subsystem", - "type": "none" - } - ], - "packageName": "@microsoft/rush" -} \ No newline at end of file diff --git a/common/changes/@microsoft/rush/feature-custom-hooks-log-telemetry_2023-05-23-19-53.json b/common/changes/@microsoft/rush/feature-custom-hooks-log-telemetry_2023-05-23-19-53.json deleted file mode 100644 index 619202630be..00000000000 --- a/common/changes/@microsoft/rush/feature-custom-hooks-log-telemetry_2023-05-23-19-53.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@microsoft/rush", - "comment": "Expose beforeLog hook", - "type": "none" - } - ], - "packageName": "@microsoft/rush" -} \ No newline at end of file diff --git a/common/changes/@microsoft/rush/fix-pnpm11-subspace-global-pnpmfile_2026-08-04-08-30-00.json b/common/changes/@microsoft/rush/fix-pnpm11-subspace-global-pnpmfile_2026-08-04-08-30-00.json new file mode 100644 index 00000000000..8a316848947 --- /dev/null +++ b/common/changes/@microsoft/rush/fix-pnpm11-subspace-global-pnpmfile_2026-08-04-08-30-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "Fix cross-subspace `workspace:*` dependency failures with pnpm 11.", + "type": "patch" + } + ], + "packageName": "@microsoft/rush" +} diff --git a/common/changes/@microsoft/rush/release-heft-0.50.0-rc_2023-06-01-22-32.json b/common/changes/@microsoft/rush/release-heft-0.50.0-rc_2023-06-01-22-32.json deleted file mode 100644 index 863ae12e3b2..00000000000 --- a/common/changes/@microsoft/rush/release-heft-0.50.0-rc_2023-06-01-22-32.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "Convert to multi-phase Heft", - "type": "none", - "packageName": "@microsoft/rush" - } - ], - "packageName": "@microsoft/rush", - "email": "dmichon-msft@users.noreply.github.com" -} \ No newline at end of file diff --git a/common/changes/@microsoft/rush/rush-boot-parse-perf_2023-05-30-17-40.json b/common/changes/@microsoft/rush/rush-boot-parse-perf_2023-05-30-17-40.json deleted file mode 100644 index da83be36b94..00000000000 --- a/common/changes/@microsoft/rush/rush-boot-parse-perf_2023-05-30-17-40.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@microsoft/rush", - "comment": "Use `JSON.parse` instead of `jju` to parse `package.json` files for faster performance.", - "type": "none" - } - ], - "packageName": "@microsoft/rush" -} \ No newline at end of file diff --git a/common/changes/@microsoft/rush/rush-serve-dashboard-select-all_2026-08-07-15-26-00.json b/common/changes/@microsoft/rush/rush-serve-dashboard-select-all_2026-08-07-15-26-00.json new file mode 100644 index 00000000000..633261c2710 --- /dev/null +++ b/common/changes/@microsoft/rush/rush-serve-dashboard-select-all_2026-08-07-15-26-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "Allow Ctrl+A and Command+A to select text in Rush serve dashboard fields.", + "type": "patch" + } + ], + "packageName": "@microsoft/rush" +} diff --git a/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-02-25-01-32.json b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-02-25-01-32.json new file mode 100644 index 00000000000..4182a0eb140 --- /dev/null +++ b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-02-25-01-32.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-patch" + } + ], + "packageName": "@rushstack/eslint-patch", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-02-25-22-01.json b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-02-25-22-01.json new file mode 100644 index 00000000000..4182a0eb140 --- /dev/null +++ b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-02-25-22-01.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-patch" + } + ], + "packageName": "@rushstack/eslint-patch", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-03-09-15-36.json b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-03-09-15-36.json new file mode 100644 index 00000000000..4182a0eb140 --- /dev/null +++ b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-03-09-15-36.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-patch" + } + ], + "packageName": "@rushstack/eslint-patch", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-04-09-23-21.json b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-04-09-23-21.json new file mode 100644 index 00000000000..4182a0eb140 --- /dev/null +++ b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-04-09-23-21.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-patch" + } + ], + "packageName": "@rushstack/eslint-patch", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-04-11-00-51.json b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-04-11-00-51.json new file mode 100644 index 00000000000..4182a0eb140 --- /dev/null +++ b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-04-11-00-51.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-patch" + } + ], + "packageName": "@rushstack/eslint-patch", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-04-18-04-11.json b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-04-18-04-11.json new file mode 100644 index 00000000000..4182a0eb140 --- /dev/null +++ b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-04-18-04-11.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-patch" + } + ], + "packageName": "@rushstack/eslint-patch", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-04-20-23-56.json b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-04-20-23-56.json new file mode 100644 index 00000000000..4182a0eb140 --- /dev/null +++ b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-04-20-23-56.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-patch" + } + ], + "packageName": "@rushstack/eslint-patch", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-06-08-15-39.json b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-06-08-15-39.json new file mode 100644 index 00000000000..4182a0eb140 --- /dev/null +++ b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-06-08-15-39.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-patch" + } + ], + "packageName": "@rushstack/eslint-patch", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-06-13-00-40-21.json b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-06-13-00-40-21.json new file mode 100644 index 00000000000..4182a0eb140 --- /dev/null +++ b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-06-13-00-40-21.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-patch" + } + ], + "packageName": "@rushstack/eslint-patch", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-07-16-00-40-55.json b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-07-16-00-40-55.json new file mode 100644 index 00000000000..4182a0eb140 --- /dev/null +++ b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-07-16-00-40-55.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-patch" + } + ], + "packageName": "@rushstack/eslint-patch", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-07-20-18-12-58.json b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-07-20-18-12-58.json new file mode 100644 index 00000000000..4182a0eb140 --- /dev/null +++ b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-07-20-18-12-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-patch" + } + ], + "packageName": "@rushstack/eslint-patch", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..4182a0eb140 --- /dev/null +++ b/common/changes/@rushstack/eslint-patch/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-patch" + } + ], + "packageName": "@rushstack/eslint-patch", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-patch/bump-cyclics_2023-05-22-06-43.json b/common/changes/@rushstack/eslint-patch/bump-cyclics_2023-05-22-06-43.json deleted file mode 100644 index 6a61cc13329..00000000000 --- a/common/changes/@rushstack/eslint-patch/bump-cyclics_2023-05-22-06-43.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "", - "type": "none", - "packageName": "@rushstack/eslint-patch" - } - ], - "packageName": "@rushstack/eslint-patch", - "email": "iclanton@users.noreply.github.com" -} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-patch/bump-tsdoc-and-typescript-eslint_2026-02-25-03-24.json b/common/changes/@rushstack/eslint-patch/bump-tsdoc-and-typescript-eslint_2026-02-25-03-24.json new file mode 100644 index 00000000000..ceefa44d69e --- /dev/null +++ b/common/changes/@rushstack/eslint-patch/bump-tsdoc-and-typescript-eslint_2026-02-25-03-24.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/eslint-patch", + "comment": "", + "type": "none" + } + ], + "packageName": "@rushstack/eslint-patch" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-patch/normalize-npmrcs_2026-02-25-19-47.json b/common/changes/@rushstack/eslint-patch/normalize-npmrcs_2026-02-25-19-47.json new file mode 100644 index 00000000000..ceefa44d69e --- /dev/null +++ b/common/changes/@rushstack/eslint-patch/normalize-npmrcs_2026-02-25-19-47.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/eslint-patch", + "comment": "", + "type": "none" + } + ], + "packageName": "@rushstack/eslint-patch" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-02-25-22-01.json b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-02-25-22-01.json new file mode 100644 index 00000000000..7ee8e7c8b81 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-02-25-22-01.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-packlets" + } + ], + "packageName": "@rushstack/eslint-plugin-packlets", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-03-09-15-36.json b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-03-09-15-36.json new file mode 100644 index 00000000000..7ee8e7c8b81 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-03-09-15-36.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-packlets" + } + ], + "packageName": "@rushstack/eslint-plugin-packlets", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-04-09-23-21.json b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-04-09-23-21.json new file mode 100644 index 00000000000..7ee8e7c8b81 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-04-09-23-21.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-packlets" + } + ], + "packageName": "@rushstack/eslint-plugin-packlets", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-04-11-00-51.json b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-04-11-00-51.json new file mode 100644 index 00000000000..7ee8e7c8b81 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-04-11-00-51.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-packlets" + } + ], + "packageName": "@rushstack/eslint-plugin-packlets", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-04-18-04-11.json b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-04-18-04-11.json new file mode 100644 index 00000000000..7ee8e7c8b81 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-04-18-04-11.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-packlets" + } + ], + "packageName": "@rushstack/eslint-plugin-packlets", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-04-20-23-56.json b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-04-20-23-56.json new file mode 100644 index 00000000000..7ee8e7c8b81 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-04-20-23-56.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-packlets" + } + ], + "packageName": "@rushstack/eslint-plugin-packlets", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-06-08-15-39.json b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-06-08-15-39.json new file mode 100644 index 00000000000..7ee8e7c8b81 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-06-08-15-39.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-packlets" + } + ], + "packageName": "@rushstack/eslint-plugin-packlets", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-06-13-00-40-21.json b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-06-13-00-40-21.json new file mode 100644 index 00000000000..7ee8e7c8b81 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-06-13-00-40-21.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-packlets" + } + ], + "packageName": "@rushstack/eslint-plugin-packlets", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-07-16-00-40-55.json b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-07-16-00-40-55.json new file mode 100644 index 00000000000..7ee8e7c8b81 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-07-16-00-40-55.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-packlets" + } + ], + "packageName": "@rushstack/eslint-plugin-packlets", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-07-20-18-12-58.json b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-07-20-18-12-58.json new file mode 100644 index 00000000000..7ee8e7c8b81 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-07-20-18-12-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-packlets" + } + ], + "packageName": "@rushstack/eslint-plugin-packlets", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..7ee8e7c8b81 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-packlets/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-packlets" + } + ], + "packageName": "@rushstack/eslint-plugin-packlets", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-packlets/bump-cyclics_2023-05-22-06-43.json b/common/changes/@rushstack/eslint-plugin-packlets/forbid-private-static_2026-07-19-02-14-57.json similarity index 100% rename from common/changes/@rushstack/eslint-plugin-packlets/bump-cyclics_2023-05-22-06-43.json rename to common/changes/@rushstack/eslint-plugin-packlets/forbid-private-static_2026-07-19-02-14-57.json diff --git a/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-02-25-22-01.json b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-02-25-22-01.json new file mode 100644 index 00000000000..4b6878d5549 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-02-25-22-01.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-security" + } + ], + "packageName": "@rushstack/eslint-plugin-security", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-03-09-15-36.json b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-03-09-15-36.json new file mode 100644 index 00000000000..4b6878d5549 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-03-09-15-36.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-security" + } + ], + "packageName": "@rushstack/eslint-plugin-security", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-04-09-23-21.json b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-04-09-23-21.json new file mode 100644 index 00000000000..4b6878d5549 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-04-09-23-21.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-security" + } + ], + "packageName": "@rushstack/eslint-plugin-security", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-04-11-00-51.json b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-04-11-00-51.json new file mode 100644 index 00000000000..4b6878d5549 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-04-11-00-51.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-security" + } + ], + "packageName": "@rushstack/eslint-plugin-security", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-04-18-04-11.json b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-04-18-04-11.json new file mode 100644 index 00000000000..4b6878d5549 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-04-18-04-11.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-security" + } + ], + "packageName": "@rushstack/eslint-plugin-security", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-04-20-23-56.json b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-04-20-23-56.json new file mode 100644 index 00000000000..4b6878d5549 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-04-20-23-56.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-security" + } + ], + "packageName": "@rushstack/eslint-plugin-security", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-06-08-15-39.json b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-06-08-15-39.json new file mode 100644 index 00000000000..4b6878d5549 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-06-08-15-39.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-security" + } + ], + "packageName": "@rushstack/eslint-plugin-security", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-06-13-00-40-21.json b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-06-13-00-40-21.json new file mode 100644 index 00000000000..4b6878d5549 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-06-13-00-40-21.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-security" + } + ], + "packageName": "@rushstack/eslint-plugin-security", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-07-16-00-40-55.json b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-07-16-00-40-55.json new file mode 100644 index 00000000000..4b6878d5549 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-07-16-00-40-55.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-security" + } + ], + "packageName": "@rushstack/eslint-plugin-security", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-07-20-18-12-58.json b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-07-20-18-12-58.json new file mode 100644 index 00000000000..4b6878d5549 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-07-20-18-12-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-security" + } + ], + "packageName": "@rushstack/eslint-plugin-security", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..4b6878d5549 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin-security/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin-security" + } + ], + "packageName": "@rushstack/eslint-plugin-security", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin-security/bump-cyclics_2023-05-22-06-43.json b/common/changes/@rushstack/eslint-plugin-security/bump-cyclics_2023-05-22-06-43.json deleted file mode 100644 index e8c34c96411..00000000000 --- a/common/changes/@rushstack/eslint-plugin-security/bump-cyclics_2023-05-22-06-43.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "", - "type": "none", - "packageName": "@rushstack/eslint-plugin-security" - } - ], - "packageName": "@rushstack/eslint-plugin-security", - "email": "iclanton@users.noreply.github.com" -} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-02-25-22-01.json b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-02-25-22-01.json new file mode 100644 index 00000000000..eb5d63adacf --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-02-25-22-01.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin" + } + ], + "packageName": "@rushstack/eslint-plugin", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-03-09-15-36.json b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-03-09-15-36.json new file mode 100644 index 00000000000..eb5d63adacf --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-03-09-15-36.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin" + } + ], + "packageName": "@rushstack/eslint-plugin", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-04-09-23-21.json b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-04-09-23-21.json new file mode 100644 index 00000000000..eb5d63adacf --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-04-09-23-21.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin" + } + ], + "packageName": "@rushstack/eslint-plugin", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-04-11-00-51.json b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-04-11-00-51.json new file mode 100644 index 00000000000..eb5d63adacf --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-04-11-00-51.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin" + } + ], + "packageName": "@rushstack/eslint-plugin", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-04-18-04-11.json b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-04-18-04-11.json new file mode 100644 index 00000000000..eb5d63adacf --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-04-18-04-11.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin" + } + ], + "packageName": "@rushstack/eslint-plugin", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-04-20-23-56.json b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-04-20-23-56.json new file mode 100644 index 00000000000..eb5d63adacf --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-04-20-23-56.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin" + } + ], + "packageName": "@rushstack/eslint-plugin", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-06-08-15-39.json b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-06-08-15-39.json new file mode 100644 index 00000000000..eb5d63adacf --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-06-08-15-39.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin" + } + ], + "packageName": "@rushstack/eslint-plugin", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-06-13-00-40-21.json b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-06-13-00-40-21.json new file mode 100644 index 00000000000..eb5d63adacf --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-06-13-00-40-21.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin" + } + ], + "packageName": "@rushstack/eslint-plugin", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-07-16-00-40-55.json b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-07-16-00-40-55.json new file mode 100644 index 00000000000..eb5d63adacf --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-07-16-00-40-55.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin" + } + ], + "packageName": "@rushstack/eslint-plugin", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-07-20-18-12-58.json b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-07-20-18-12-58.json new file mode 100644 index 00000000000..eb5d63adacf --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-07-20-18-12-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin" + } + ], + "packageName": "@rushstack/eslint-plugin", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..eb5d63adacf --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/eslint-plugin" + } + ], + "packageName": "@rushstack/eslint-plugin", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin/bump-cyclics_2023-05-22-06-43.json b/common/changes/@rushstack/eslint-plugin/bump-cyclics_2023-05-22-06-43.json deleted file mode 100644 index 5669a1df6aa..00000000000 --- a/common/changes/@rushstack/eslint-plugin/bump-cyclics_2023-05-22-06-43.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "", - "type": "none", - "packageName": "@rushstack/eslint-plugin" - } - ], - "packageName": "@rushstack/eslint-plugin", - "email": "iclanton@users.noreply.github.com" -} \ No newline at end of file diff --git a/common/changes/@rushstack/eslint-plugin/improve-heft-sass-plugin-docs_2026-04-10-23-34.json b/common/changes/@rushstack/eslint-plugin/improve-heft-sass-plugin-docs_2026-04-10-23-34.json new file mode 100644 index 00000000000..dcf93469653 --- /dev/null +++ b/common/changes/@rushstack/eslint-plugin/improve-heft-sass-plugin-docs_2026-04-10-23-34.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/eslint-plugin", + "comment": "", + "type": "none" + } + ], + "packageName": "@rushstack/eslint-plugin" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-config-file/automated-bump-decoupled-deps_2026-07-20-18-12-58.json b/common/changes/@rushstack/heft-config-file/automated-bump-decoupled-deps_2026-07-20-18-12-58.json new file mode 100644 index 00000000000..5ebf376352e --- /dev/null +++ b/common/changes/@rushstack/heft-config-file/automated-bump-decoupled-deps_2026-07-20-18-12-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft-config-file" + } + ], + "packageName": "@rushstack/heft-config-file", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-config-file/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@rushstack/heft-config-file/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..5ebf376352e --- /dev/null +++ b/common/changes/@rushstack/heft-config-file/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft-config-file" + } + ], + "packageName": "@rushstack/heft-config-file", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@rushstack/heft/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..d773a7585e3 --- /dev/null +++ b/common/changes/@rushstack/heft/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/heft" + } + ], + "packageName": "@rushstack/heft", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/node-core-library/automated-bump-decoupled-deps_2026-07-20-18-12-58.json b/common/changes/@rushstack/node-core-library/automated-bump-decoupled-deps_2026-07-20-18-12-58.json new file mode 100644 index 00000000000..bf210f74baf --- /dev/null +++ b/common/changes/@rushstack/node-core-library/automated-bump-decoupled-deps_2026-07-20-18-12-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/node-core-library" + } + ], + "packageName": "@rushstack/node-core-library", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/node-core-library/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@rushstack/node-core-library/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..bf210f74baf --- /dev/null +++ b/common/changes/@rushstack/node-core-library/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/node-core-library" + } + ], + "packageName": "@rushstack/node-core-library", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/node-core-library/forbid-private-static_2026-07-19-02-14-57.json b/common/changes/@rushstack/node-core-library/forbid-private-static_2026-07-19-02-14-57.json new file mode 100644 index 00000000000..db57b2feb86 --- /dev/null +++ b/common/changes/@rushstack/node-core-library/forbid-private-static_2026-07-19-02-14-57.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/node-core-library" + } + ], + "packageName": "@rushstack/node-core-library", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/operation-graph/automated-bump-decoupled-deps_2026-07-20-18-12-58.json b/common/changes/@rushstack/operation-graph/automated-bump-decoupled-deps_2026-07-20-18-12-58.json new file mode 100644 index 00000000000..bebea85d5d6 --- /dev/null +++ b/common/changes/@rushstack/operation-graph/automated-bump-decoupled-deps_2026-07-20-18-12-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/operation-graph" + } + ], + "packageName": "@rushstack/operation-graph", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/operation-graph/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@rushstack/operation-graph/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..bebea85d5d6 --- /dev/null +++ b/common/changes/@rushstack/operation-graph/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/operation-graph" + } + ], + "packageName": "@rushstack/operation-graph", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-02-25-01-32.json b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-02-25-01-32.json new file mode 100644 index 00000000000..67a23d58850 --- /dev/null +++ b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-02-25-01-32.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/problem-matcher" + } + ], + "packageName": "@rushstack/problem-matcher", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-02-25-22-01.json b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-02-25-22-01.json new file mode 100644 index 00000000000..67a23d58850 --- /dev/null +++ b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-02-25-22-01.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/problem-matcher" + } + ], + "packageName": "@rushstack/problem-matcher", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-03-09-15-36.json b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-03-09-15-36.json new file mode 100644 index 00000000000..67a23d58850 --- /dev/null +++ b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-03-09-15-36.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/problem-matcher" + } + ], + "packageName": "@rushstack/problem-matcher", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-04-09-23-21.json b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-04-09-23-21.json new file mode 100644 index 00000000000..67a23d58850 --- /dev/null +++ b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-04-09-23-21.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/problem-matcher" + } + ], + "packageName": "@rushstack/problem-matcher", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-04-11-00-51.json b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-04-11-00-51.json new file mode 100644 index 00000000000..67a23d58850 --- /dev/null +++ b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-04-11-00-51.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/problem-matcher" + } + ], + "packageName": "@rushstack/problem-matcher", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-04-18-04-11.json b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-04-18-04-11.json new file mode 100644 index 00000000000..67a23d58850 --- /dev/null +++ b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-04-18-04-11.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/problem-matcher" + } + ], + "packageName": "@rushstack/problem-matcher", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-04-20-23-56.json b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-04-20-23-56.json new file mode 100644 index 00000000000..67a23d58850 --- /dev/null +++ b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-04-20-23-56.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/problem-matcher" + } + ], + "packageName": "@rushstack/problem-matcher", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-06-08-15-39.json b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-06-08-15-39.json new file mode 100644 index 00000000000..67a23d58850 --- /dev/null +++ b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-06-08-15-39.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/problem-matcher" + } + ], + "packageName": "@rushstack/problem-matcher", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-06-13-00-40-21.json b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-06-13-00-40-21.json new file mode 100644 index 00000000000..67a23d58850 --- /dev/null +++ b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-06-13-00-40-21.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/problem-matcher" + } + ], + "packageName": "@rushstack/problem-matcher", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-07-16-00-40-55.json b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-07-16-00-40-55.json new file mode 100644 index 00000000000..67a23d58850 --- /dev/null +++ b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-07-16-00-40-55.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/problem-matcher" + } + ], + "packageName": "@rushstack/problem-matcher", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-07-20-18-12-58.json b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-07-20-18-12-58.json new file mode 100644 index 00000000000..67a23d58850 --- /dev/null +++ b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-07-20-18-12-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/problem-matcher" + } + ], + "packageName": "@rushstack/problem-matcher", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..67a23d58850 --- /dev/null +++ b/common/changes/@rushstack/problem-matcher/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/problem-matcher" + } + ], + "packageName": "@rushstack/problem-matcher", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/problem-matcher/normalize-npmrcs_2026-02-25-19-56.json b/common/changes/@rushstack/problem-matcher/normalize-npmrcs_2026-02-25-19-56.json new file mode 100644 index 00000000000..222a84da6d7 --- /dev/null +++ b/common/changes/@rushstack/problem-matcher/normalize-npmrcs_2026-02-25-19-56.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/problem-matcher", + "comment": "", + "type": "none" + } + ], + "packageName": "@rushstack/problem-matcher" +} \ No newline at end of file diff --git a/common/changes/@rushstack/problem-matcher/publish-api-artifact_2026-02-22-22-14.json b/common/changes/@rushstack/problem-matcher/publish-api-artifact_2026-02-22-22-14.json new file mode 100644 index 00000000000..a8d616334fa --- /dev/null +++ b/common/changes/@rushstack/problem-matcher/publish-api-artifact_2026-02-22-22-14.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/problem-matcher" + } + ], + "packageName": "@rushstack/problem-matcher", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-04-18-04-11.json b/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-04-18-04-11.json new file mode 100644 index 00000000000..cc5c78afc2a --- /dev/null +++ b/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-04-18-04-11.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/rig-package" + } + ], + "packageName": "@rushstack/rig-package", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-04-20-23-56.json b/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-04-20-23-56.json new file mode 100644 index 00000000000..cc5c78afc2a --- /dev/null +++ b/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-04-20-23-56.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/rig-package" + } + ], + "packageName": "@rushstack/rig-package", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-06-08-15-39.json b/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-06-08-15-39.json new file mode 100644 index 00000000000..cc5c78afc2a --- /dev/null +++ b/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-06-08-15-39.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/rig-package" + } + ], + "packageName": "@rushstack/rig-package", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-06-13-00-40-21.json b/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-06-13-00-40-21.json new file mode 100644 index 00000000000..cc5c78afc2a --- /dev/null +++ b/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-06-13-00-40-21.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/rig-package" + } + ], + "packageName": "@rushstack/rig-package", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-07-16-00-40-55.json b/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-07-16-00-40-55.json new file mode 100644 index 00000000000..cc5c78afc2a --- /dev/null +++ b/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-07-16-00-40-55.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/rig-package" + } + ], + "packageName": "@rushstack/rig-package", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-07-20-18-12-58.json b/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-07-20-18-12-58.json new file mode 100644 index 00000000000..cc5c78afc2a --- /dev/null +++ b/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-07-20-18-12-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/rig-package" + } + ], + "packageName": "@rushstack/rig-package", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..cc5c78afc2a --- /dev/null +++ b/common/changes/@rushstack/rig-package/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/rig-package" + } + ], + "packageName": "@rushstack/rig-package", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/rig-package/bump-cyclics_2023-05-22-06-43.json b/common/changes/@rushstack/rig-package/forbid-private-static_2026-07-19-02-14-57.json similarity index 100% rename from common/changes/@rushstack/rig-package/bump-cyclics_2023-05-22-06-43.json rename to common/changes/@rushstack/rig-package/forbid-private-static_2026-07-19-02-14-57.json diff --git a/common/changes/@rushstack/rig-package/iclanton-ajv-fast-uri-consistency_2026-07-15-20-12.json b/common/changes/@rushstack/rig-package/iclanton-ajv-fast-uri-consistency_2026-07-15-20-12.json new file mode 100644 index 00000000000..f2c7f81beb9 --- /dev/null +++ b/common/changes/@rushstack/rig-package/iclanton-ajv-fast-uri-consistency_2026-07-15-20-12.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@rushstack/rig-package", + "comment": "Update `ajv` devDependency to `~8.20.0` for dependency consistency.", + "type": "none" + } + ], + "packageName": "@rushstack/rig-package", + "email": "rushbot@users.noreply.github.com" +} diff --git a/common/changes/@rushstack/terminal/automated-bump-decoupled-deps_2026-07-20-18-12-58.json b/common/changes/@rushstack/terminal/automated-bump-decoupled-deps_2026-07-20-18-12-58.json new file mode 100644 index 00000000000..da1765f19fc --- /dev/null +++ b/common/changes/@rushstack/terminal/automated-bump-decoupled-deps_2026-07-20-18-12-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/terminal" + } + ], + "packageName": "@rushstack/terminal", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/terminal/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@rushstack/terminal/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..da1765f19fc --- /dev/null +++ b/common/changes/@rushstack/terminal/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/terminal" + } + ], + "packageName": "@rushstack/terminal", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/terminal/forbid-private-static_2026-07-19-02-14-57.json b/common/changes/@rushstack/terminal/forbid-private-static_2026-07-19-02-14-57.json new file mode 100644 index 00000000000..13894830365 --- /dev/null +++ b/common/changes/@rushstack/terminal/forbid-private-static_2026-07-19-02-14-57.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/terminal" + } + ], + "packageName": "@rushstack/terminal", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-02-25-01-32.json b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-02-25-01-32.json new file mode 100644 index 00000000000..85eb782e9cb --- /dev/null +++ b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-02-25-01-32.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/tree-pattern" + } + ], + "packageName": "@rushstack/tree-pattern", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-02-25-22-01.json b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-02-25-22-01.json new file mode 100644 index 00000000000..85eb782e9cb --- /dev/null +++ b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-02-25-22-01.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/tree-pattern" + } + ], + "packageName": "@rushstack/tree-pattern", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-03-09-15-36.json b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-03-09-15-36.json new file mode 100644 index 00000000000..85eb782e9cb --- /dev/null +++ b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-03-09-15-36.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/tree-pattern" + } + ], + "packageName": "@rushstack/tree-pattern", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-04-09-23-21.json b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-04-09-23-21.json new file mode 100644 index 00000000000..85eb782e9cb --- /dev/null +++ b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-04-09-23-21.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/tree-pattern" + } + ], + "packageName": "@rushstack/tree-pattern", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-04-11-00-51.json b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-04-11-00-51.json new file mode 100644 index 00000000000..85eb782e9cb --- /dev/null +++ b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-04-11-00-51.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/tree-pattern" + } + ], + "packageName": "@rushstack/tree-pattern", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-04-18-04-11.json b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-04-18-04-11.json new file mode 100644 index 00000000000..85eb782e9cb --- /dev/null +++ b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-04-18-04-11.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/tree-pattern" + } + ], + "packageName": "@rushstack/tree-pattern", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-04-20-23-56.json b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-04-20-23-56.json new file mode 100644 index 00000000000..85eb782e9cb --- /dev/null +++ b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-04-20-23-56.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/tree-pattern" + } + ], + "packageName": "@rushstack/tree-pattern", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-06-08-15-39.json b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-06-08-15-39.json new file mode 100644 index 00000000000..85eb782e9cb --- /dev/null +++ b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-06-08-15-39.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/tree-pattern" + } + ], + "packageName": "@rushstack/tree-pattern", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-06-13-00-40-21.json b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-06-13-00-40-21.json new file mode 100644 index 00000000000..85eb782e9cb --- /dev/null +++ b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-06-13-00-40-21.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/tree-pattern" + } + ], + "packageName": "@rushstack/tree-pattern", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-07-16-00-40-55.json b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-07-16-00-40-55.json new file mode 100644 index 00000000000..85eb782e9cb --- /dev/null +++ b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-07-16-00-40-55.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/tree-pattern" + } + ], + "packageName": "@rushstack/tree-pattern", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-07-20-18-12-58.json b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-07-20-18-12-58.json new file mode 100644 index 00000000000..85eb782e9cb --- /dev/null +++ b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-07-20-18-12-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/tree-pattern" + } + ], + "packageName": "@rushstack/tree-pattern", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..85eb782e9cb --- /dev/null +++ b/common/changes/@rushstack/tree-pattern/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/tree-pattern" + } + ], + "packageName": "@rushstack/tree-pattern", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/bump-cyclics_2023-05-22-06-43.json b/common/changes/@rushstack/tree-pattern/forbid-private-static_2026-07-19-02-14-57.json similarity index 100% rename from common/changes/@rushstack/tree-pattern/bump-cyclics_2023-05-22-06-43.json rename to common/changes/@rushstack/tree-pattern/forbid-private-static_2026-07-19-02-14-57.json diff --git a/common/changes/@rushstack/tree-pattern/enelson-package-update_2023-01-27-06-53.json b/common/changes/@rushstack/tree-pattern/normalize-npmrcs_2026-02-25-19-56.json similarity index 100% rename from common/changes/@rushstack/tree-pattern/enelson-package-update_2023-01-27-06-53.json rename to common/changes/@rushstack/tree-pattern/normalize-npmrcs_2026-02-25-19-56.json diff --git a/common/changes/@rushstack/tree-pattern/octogonz-bump-decoupled_2023-01-28-02-57.json b/common/changes/@rushstack/tree-pattern/octogonz-bump-decoupled_2023-01-28-02-57.json deleted file mode 100644 index 120c33a1f7e..00000000000 --- a/common/changes/@rushstack/tree-pattern/octogonz-bump-decoupled_2023-01-28-02-57.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@rushstack/tree-pattern", - "comment": "", - "type": "none" - } - ], - "packageName": "@rushstack/tree-pattern" -} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/publish-api-artifact_2026-02-22-22-14.json b/common/changes/@rushstack/tree-pattern/publish-api-artifact_2026-02-22-22-14.json new file mode 100644 index 00000000000..619a10c75e3 --- /dev/null +++ b/common/changes/@rushstack/tree-pattern/publish-api-artifact_2026-02-22-22-14.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/tree-pattern" + } + ], + "packageName": "@rushstack/tree-pattern", + "email": "iclanton@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/tree-pattern/typescript-5_2023-05-05-23-13.json b/common/changes/@rushstack/tree-pattern/typescript-5_2023-05-05-23-13.json deleted file mode 100644 index 120c33a1f7e..00000000000 --- a/common/changes/@rushstack/tree-pattern/typescript-5_2023-05-05-23-13.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@rushstack/tree-pattern", - "comment": "", - "type": "none" - } - ], - "packageName": "@rushstack/tree-pattern" -} \ No newline at end of file diff --git a/common/changes/@rushstack/ts-command-line/automated-bump-decoupled-deps_2026-07-20-18-12-58.json b/common/changes/@rushstack/ts-command-line/automated-bump-decoupled-deps_2026-07-20-18-12-58.json new file mode 100644 index 00000000000..e4b2e545b21 --- /dev/null +++ b/common/changes/@rushstack/ts-command-line/automated-bump-decoupled-deps_2026-07-20-18-12-58.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/ts-command-line" + } + ], + "packageName": "@rushstack/ts-command-line", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/ts-command-line/automated-bump-decoupled-deps_2026-07-21-03-16-43.json b/common/changes/@rushstack/ts-command-line/automated-bump-decoupled-deps_2026-07-21-03-16-43.json new file mode 100644 index 00000000000..e4b2e545b21 --- /dev/null +++ b/common/changes/@rushstack/ts-command-line/automated-bump-decoupled-deps_2026-07-21-03-16-43.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "", + "type": "none", + "packageName": "@rushstack/ts-command-line" + } + ], + "packageName": "@rushstack/ts-command-line", + "email": "rushbot@users.noreply.github.com" +} \ No newline at end of file diff --git a/common/changes/@rushstack/ts-command-line/bump-cyclics_2023-05-22-06-43.json b/common/changes/@rushstack/ts-command-line/forbid-private-static_2026-07-19-02-14-57.json similarity index 100% rename from common/changes/@rushstack/ts-command-line/bump-cyclics_2023-05-22-06-43.json rename to common/changes/@rushstack/ts-command-line/forbid-private-static_2026-07-19-02-14-57.json diff --git a/common/config/azure-pipelines/ci.yaml b/common/config/azure-pipelines/ci.yaml deleted file mode 100644 index 279f5903579..00000000000 --- a/common/config/azure-pipelines/ci.yaml +++ /dev/null @@ -1,17 +0,0 @@ -pool: - vmImage: 'ubuntu-latest' -variables: - FORCE_COLOR: 1 -jobs: - - job: PRBuild - condition: succeeded() - strategy: - matrix: - 'NodeJs 14': - NodeVersion: 14 - 'NodeJs 16': - NodeVersion: 16 - - steps: - - checkout: self - - template: templates/build.yaml diff --git a/common/config/azure-pipelines/npm-post-publish.yaml b/common/config/azure-pipelines/npm-post-publish.yaml new file mode 100644 index 00000000000..04549b71076 --- /dev/null +++ b/common/config/azure-pipelines/npm-post-publish.yaml @@ -0,0 +1,271 @@ +parameters: + - name: delayMinutes + displayName: 'Minutes to wait for packages to propagate before running' + type: number + default: 5 + +name: 'Post-publish $(Date:yyyyMMdd).$(Rev:r) (triggered by $(resources.triggeringAlias))' + +variables: + - name: FORCE_COLOR + value: 1 + +# This pipeline is triggered only by pipeline resources (npm publish pipelines), +# not by CI pushes or PR builds. +trigger: none +pr: none + +resources: + pipelines: + - pipeline: npmPublish + source: 'rushstack NPM Publish' + trigger: + enabled: true + branches: + include: + - refs/heads/main + - pipeline: npmPublishRush + source: 'rushstack NPM Publish (rush)' + trigger: + enabled: true + branches: + include: + - refs/heads/main + repositories: + - repository: 1esPipelines + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release + - repository: rushstackWebsites + type: github + name: microsoft/rushstack-websites + endpoint: GitHubProjects + ref: refs/heads/main + +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines + parameters: + sdl: + sourceRepositoriesToScan: + exclude: + - repository: rushstackWebsites + pool: + name: Azure-Pipelines-1ESPT-ExDShared + os: windows + stages: + # ────────────────────────────────────────────────────────────────────────── + # Stage 0: Wait for packages to propagate to the npm registry + # ────────────────────────────────────────────────────────────────────────── + - stage: WaitForPropagation + displayName: 'Wait for npm propagation' + jobs: + - job: + displayName: 'Delay' + pool: server + timeoutInMinutes: 120 + steps: + - task: Delay@1 + displayName: 'Wait ${{ parameters.delayMinutes }} minute(s)' + inputs: + delayForMinutes: '${{ parameters.delayMinutes }}' + + # ────────────────────────────────────────────────────────────────────────── + # Stage 1: Bump decoupled local dependencies + # ────────────────────────────────────────────────────────────────────────── + - stage: BumpDecoupledDeps + displayName: 'Bump decoupled local dependencies' + dependsOn: WaitForPropagation + variables: + BranchName: 'automated/bump-decoupled-deps' + CommitMessage: 'chore: bump decoupled local dependencies' + jobs: + - job: + displayName: 'Bump decoupled dependencies and create PR' + pool: + name: publish-rushstack + os: linux + steps: + - checkout: self + persistCredentials: true + + - template: /common/config/azure-pipelines/templates/install-node.yaml@self + + - script: 'git config --local user.email rushbot@users.noreply.github.com' + displayName: 'git config email' + + - script: 'git config --local user.name Rushbot' + displayName: 'git config name' + + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: > + install + --to repo-toolbox + DisplayName: 'Rush Install' + + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: > + build + --to repo-toolbox + --verbose + DisplayName: 'Rush Build (repo-toolbox)' + + - template: /common/config/azure-pipelines/templates/run-repo-toolbox.yaml@self + parameters: + Arguments: 'bump-decoupled-local-dependencies' + DisplayName: 'Bump decoupled local dependencies' + + # If Rush itself was updated by bump-decoupled-local-dependencies, we need to bootstrap + # the new version and update the checked-in lockfile before running `rush update`. + # Otherwise install-run-rush.js would fail lockfile validation when trying to install + # the new version of Rush (the checked-in lockfile still references the old version). + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: '--help' + DisplayName: 'Install new Rush version (skip lockfile validation)' + DisableInstallRunRushLockfile: true + Condition: "eq(variables['RushWasUpdated'], 'true')" + + - bash: > + cp + "common/temp/install-run/@microsoft+rush@$(NewRushVersion)/package-lock.json" + common/config/validation/rush-package-lock.json + displayName: 'Update rush-package-lock.json (new Rush version)' + condition: eq(variables['RushWasUpdated'], 'true') + + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: 'update-autoinstaller --name plugins' + DisplayName: 'Rush Update Autoinstaller (plugins)' + + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: 'update' + DisplayName: 'Rush Update' + + - bash: | + set -e + + if git diff --quiet; then + echo "No changes detected. Skipping commit and PR." + echo "##vso[task.setvariable variable=HasChanges]false" + exit 0 + fi + + echo "##vso[task.setvariable variable=HasChanges]true" + + git checkout -B $(BranchName) + git add --all + git commit -m "$(CommitMessage)" + displayName: 'Commit dependency changes' + + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: > + change + --bulk + --bump-type none + --commit-message "chore: generate change files for decoupled dependency bump" + DisplayName: 'Generate change files' + Condition: "and(succeeded(), eq(variables.HasChanges, 'true'))" + + - template: /common/config/azure-pipelines/templates/push-and-create-github-pr.yaml@self + parameters: + BranchName: $(BranchName) + PrTitle: $(CommitMessage) + PrDescription: 'Automated PR to bump decoupled local dependencies to the latest published versions.' + + # ────────────────────────────────────────────────────────────────────────── + # Stage 2: Update API documentation on rushstack-websites + # ────────────────────────────────────────────────────────────────────────── + - stage: UpdateApiDocs + displayName: 'Update API documentation' + dependsOn: WaitForPropagation + variables: + BranchName: 'automated/update-api-docs' + CommitMessage: 'docs: update API documentation' + jobs: + - job: + displayName: 'Update API docs and create PR' + pool: + name: publish-rushstack + os: linux + steps: + - checkout: rushstackWebsites + persistCredentials: true + + - template: /common/config/azure-pipelines/templates/install-node.yaml@self + parameters: + NodeMajorVersion: 24 + + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: 'install' + DisplayName: 'Rush Install (rushstack-websites)' + # rushstack-websites doesn't have an `install-run-rush` lockfile checked in + DisableInstallRunRushLockfile: true + + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: > + build + --to-except api.rushstack.io + --verbose + DisplayName: 'Rush Build to-except api.rushstack.io (rushstack-websites)' + # rushstack-websites doesn't have an `install-run-rush` lockfile checked in + DisableInstallRunRushLockfile: true + + # Download the api artifact from the triggering publish pipeline. + # AzDO automatically resolves which pipeline resource triggered this run. + - task: DownloadPipelineArtifact@2 + displayName: 'Download API review files' + inputs: + source: specific + project: GitHubProjects + pipeline: 'rushstack NPM Publish' + preferTriggeringPipeline: true + runVersion: latest + artifact: api + path: $(Pipeline.Workspace)/api + + # Run api-documenter with the Docusaurus plugin from the + # api.rushstack.io project directory so it picks up + # config/api-documenter.json. + - script: > + npx @microsoft/api-documenter@latest + generate + --input-folder $(Pipeline.Workspace)/api + --output-folder ./docs/pages + displayName: 'Generate API documentation' + workingDirectory: websites/api.rushstack.io + + # Update the API docs folder in rushstack-websites and commit. + - bash: | + set -e + + git config --local user.email rushbot@users.noreply.github.com + git config --local user.name Rushbot + + # Move the generated nav data file to the expected location. + mv websites/api.rushstack.io/docs/api_nav.json websites/api.rushstack.io/data/api_nav.json + + # Check for changes (tracked and untracked) + if git diff --quiet && [ -z "$(git ls-files --others --exclude-standard)" ]; then + echo "No API documentation changes detected." + echo "##vso[task.setvariable variable=HasChanges]false" + exit 0 + fi + + echo "##vso[task.setvariable variable=HasChanges]true" + + git checkout -B $(BranchName) + git add --all + git commit -m "$(CommitMessage)" + displayName: 'Update API docs and commit' + + - template: /common/config/azure-pipelines/templates/push-and-create-github-pr.yaml@self + parameters: + BranchName: $(BranchName) + PrTitle: $(CommitMessage) + PrDescription: 'Automated PR to update API reference documentation from the latest published packages.' diff --git a/common/config/azure-pipelines/npm-publish-rush.yaml b/common/config/azure-pipelines/npm-publish-rush.yaml index e0ede34dbcf..70727beb73d 100644 --- a/common/config/azure-pipelines/npm-publish-rush.yaml +++ b/common/config/azure-pipelines/npm-publish-rush.yaml @@ -1,32 +1,82 @@ -pool: - vmImage: 'ubuntu-latest' +parameters: + - name: publishToNpmFeed + displayName: 'Publish to npm feed' + type: boolean + default: true + variables: - - name: NodeVersion - value: 14 - name: FORCE_COLOR value: 1 - name: SourceBranch value: $[ replace(replace(resources.repositories.self.ref, 'refs/heads/', ''), 'refs/pull/', 'refs/remotes/pull/') ] -steps: - - checkout: self - persistCredentials: true - - template: templates/build.yaml - - template: templates/bump-versions.yaml - parameters: - VersionPolicyName: noRush - BranchName: $(SourceBranch) - - template: templates/bump-versions.yaml - parameters: - VersionPolicyName: rush - BranchName: $(SourceBranch) - - script: 'node libraries/rush-lib/scripts/plugins-prepublish.js' - displayName: 'Prepublish workaround for rush-lib' - - template: templates/publish.yaml - parameters: - VersionPolicyName: noRush - BranchName: $(SourceBranch) - - template: templates/publish.yaml - parameters: - VersionPolicyName: rush - BranchName: $(SourceBranch) - - template: templates/record-published-versions.yaml + +resources: + repositories: + - repository: 1esPipelines + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release + +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines + parameters: + pool: + name: Azure-Pipelines-1ESPT-ExDShared + os: windows + stages: + - stage: + jobs: + - job: + pool: + name: publish-rushstack + os: linux + templateContext: + outputParentDirectory: $(Build.ArtifactStagingDirectory) + outputs: + - output: pipelineArtifact + targetPath: $(Build.ArtifactStagingDirectory)/published-versions + artifactName: published-versions + - output: pipelineArtifact + targetPath: $(Build.ArtifactStagingDirectory)/json-schemas + artifactName: json-schemas + - output: pipelineArtifact + targetPath: $(Build.ArtifactStagingDirectory)/packages + artifactName: packages + - output: pipelineArtifact + targetPath: $(Build.ArtifactStagingDirectory)/api + artifactName: api + steps: + - checkout: self + persistCredentials: true + + - template: /common/config/azure-pipelines/templates/install-node.yaml@self + + - template: /common/config/azure-pipelines/templates/build.yaml@self + + - template: /common/config/azure-pipelines/templates/bump-versions.yaml@self + parameters: + VersionPolicyName: noRush + BranchName: $(SourceBranch) + + - template: /common/config/azure-pipelines/templates/bump-versions.yaml@self + parameters: + VersionPolicyName: rush + BranchName: $(SourceBranch) + + - script: 'node libraries/rush-lib/scripts/plugins-prepublish.js' + displayName: 'Prepublish workaround for rush-lib' + + - template: /common/config/azure-pipelines/templates/pack.yaml@self + + - ${{ if eq(parameters.publishToNpmFeed, true) }}: + - template: /common/config/azure-pipelines/templates/publish.yaml@self + parameters: + VersionPolicyName: noRush + BranchName: $(SourceBranch) + + - template: /common/config/azure-pipelines/templates/publish.yaml@self + parameters: + VersionPolicyName: rush + BranchName: $(SourceBranch) + + - template: /common/config/azure-pipelines/templates/post-publish.yaml@self diff --git a/common/config/azure-pipelines/npm-publish.yaml b/common/config/azure-pipelines/npm-publish.yaml index c5e60b86b11..2b589c4a732 100644 --- a/common/config/azure-pipelines/npm-publish.yaml +++ b/common/config/azure-pipelines/npm-publish.yaml @@ -1,22 +1,72 @@ -pool: - vmImage: 'ubuntu-latest' +parameters: + - name: publishToNpmFeed + displayName: 'Publish to npm feed' + type: boolean + default: true + variables: - - name: NodeVersion - value: 14 - name: FORCE_COLOR value: 1 - name: SourceBranch value: $[ replace(replace(resources.repositories.self.ref, 'refs/heads/', ''), 'refs/pull/', 'refs/remotes/pull/') ] -steps: - - checkout: self - persistCredentials: true - - template: templates/build.yaml - - template: templates/bump-versions.yaml - parameters: - VersionPolicyName: noRush - BranchName: $(SourceBranch) - - template: templates/publish.yaml - parameters: - VersionPolicyName: noRush - BranchName: $(SourceBranch) - - template: templates/record-published-versions.yaml + +resources: + repositories: + - repository: 1esPipelines + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release + +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines + parameters: + pool: + name: Azure-Pipelines-1ESPT-ExDShared + os: windows + stages: + - stage: + jobs: + - job: + pool: + name: publish-rushstack + os: linux + templateContext: + outputParentDirectory: $(Build.ArtifactStagingDirectory) + outputs: + - output: pipelineArtifact + targetPath: $(Build.ArtifactStagingDirectory)/published-versions + artifactName: published-versions + - output: pipelineArtifact + targetPath: $(Build.ArtifactStagingDirectory)/json-schemas + artifactName: json-schemas + - output: pipelineArtifact + targetPath: $(Build.ArtifactStagingDirectory)/packages + artifactName: packages + - output: pipelineArtifact + targetPath: $(Build.ArtifactStagingDirectory)/api + artifactName: api + steps: + - checkout: self + persistCredentials: true + + - template: /common/config/azure-pipelines/templates/install-node.yaml@self + + - template: /common/config/azure-pipelines/templates/build.yaml@self + + - template: /common/config/azure-pipelines/templates/bump-versions.yaml@self + parameters: + VersionPolicyName: noRush + BranchName: $(SourceBranch) + + - script: 'node libraries/rush-lib/scripts/plugins-prepublish.js' + displayName: 'Prepublish workaround for rush-lib' + + - template: /common/config/azure-pipelines/templates/pack.yaml@self + + - ${{ if eq(parameters.publishToNpmFeed, true) }}: + - template: /common/config/azure-pipelines/templates/publish.yaml@self + parameters: + VersionPolicyName: noRush + BranchName: $(SourceBranch) + + - template: /common/config/azure-pipelines/templates/post-publish.yaml@self diff --git a/common/config/azure-pipelines/templates/build.yaml b/common/config/azure-pipelines/templates/build.yaml index e5bf5e395bc..b600ec1eb25 100644 --- a/common/config/azure-pipelines/templates/build.yaml +++ b/common/config/azure-pipelines/templates/build.yaml @@ -1,28 +1,41 @@ +parameters: + - name: BuildParameters + type: string + default: '' + steps: - - task: NodeTool@0 - displayName: 'Use Node $(NodeVersion).x' - inputs: - versionSpec: '$(NodeVersion).x' - checkLatest: true - script: 'git config --local user.email rushbot@users.noreply.github.com' displayName: 'git config email' + - script: 'git config --local user.name Rushbot' displayName: 'git config name' - - script: 'node common/scripts/install-run-rush.js change --verify' - displayName: 'Verify Change Logs' - - script: 'node common/scripts/install-run-rush.js install' - displayName: 'Rush Install' - - script: 'node common/scripts/install-run-rush.js retest --verbose --production' - displayName: 'Rush retest (install-run-rush)' - env: - # Prevent time-based browserslist update warning - # See https://github.com/microsoft/rushstack/issues/2981 - BROWSERSLIST_IGNORE_OLD_DATA: 1 - - script: 'node apps/rush/lib/start-dev.js test --verbose --production --timeline' - displayName: 'Rush test (rush-lib)' - env: - # Prevent time-based browserslist update warning - # See https://github.com/microsoft/rushstack/issues/2981 - BROWSERSLIST_IGNORE_OLD_DATA: 1 - - script: 'node repo-scripts/repo-toolbox/lib/start.js readme --verify' - displayName: 'Ensure repo README is up-to-date' + + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: > + change + --verify + DisplayName: 'Verify Change Logs' + + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: 'install' + DisplayName: 'Rush Install' + + # - bash: | + # /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + # echo ">>> Started xvfb" + # displayName: Start xvfb + # condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) + + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: > + retest + --verbose + --production ${{ parameters.BuildParameters }} + DisplayName: 'Rush retest (install-run-rush)' + + - script: 'npm run test' + workingDirectory: 'build-tests/rush-package-manager-integration-test' + displayName: 'Run package manager integration tests' diff --git a/common/config/azure-pipelines/templates/bump-versions.yaml b/common/config/azure-pipelines/templates/bump-versions.yaml index b2b461b987a..17f8922afbf 100644 --- a/common/config/azure-pipelines/templates/bump-versions.yaml +++ b/common/config/azure-pipelines/templates/bump-versions.yaml @@ -6,5 +6,11 @@ parameters: default: $(Build.SourceBranchName) steps: - - script: 'node common/scripts/install-run-rush.js version --bump --version-policy ${{ parameters.VersionPolicyName }} --target-branch ${{ parameters.BranchName }}' - displayName: 'Rush Version (Policy: ${{ parameters.VersionPolicyName }})' + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: > + version + --bump + --version-policy ${{ parameters.VersionPolicyName }} + --target-branch ${{ parameters.BranchName }} + DisplayName: 'Rush Version (Policy: ${{ parameters.VersionPolicyName }})' diff --git a/common/config/azure-pipelines/templates/install-node.yaml b/common/config/azure-pipelines/templates/install-node.yaml new file mode 100644 index 00000000000..c7f782a991c --- /dev/null +++ b/common/config/azure-pipelines/templates/install-node.yaml @@ -0,0 +1,16 @@ +parameters: + - name: NodeMajorVersion + type: number + default: 22 + +steps: + - task: NodeTool@0 + inputs: + versionSpec: '${{ parameters.NodeMajorVersion }}.x' + displayName: 'Install Node.js ${{ parameters.NodeMajorVersion }}' + + - bash: | + cat > "$HOME/.npmrc" <<'EOF' + registry=https://packagefeedproxy.microsoft.io/npm/ + EOF + displayName: 'Set user npm registry to packagefeedproxy' diff --git a/common/config/azure-pipelines/templates/install-run-rush.yaml b/common/config/azure-pipelines/templates/install-run-rush.yaml new file mode 100644 index 00000000000..2a44f1c471a --- /dev/null +++ b/common/config/azure-pipelines/templates/install-run-rush.yaml @@ -0,0 +1,31 @@ +# Runs install-run-rush.js with the specified CLI arguments and sets the +# INSTALL_RUN_RUSH_LOCKFILE_PATH environment variable. +parameters: + - name: Arguments + type: string + - name: DisplayName + type: string + - name: Condition + type: string + default: '' + - name: NpmAuthToken + type: string + default: '' + - name: RepoPath + type: string + default: '$(Build.SourcesDirectory)' + - name: DisableInstallRunRushLockfile + type: boolean + default: false + +steps: + - script: 'node common/scripts/install-run-rush.js ${{ parameters.Arguments }}' + displayName: '${{ parameters.DisplayName }}' + workingDirectory: ${{ parameters.RepoPath }} + ${{ if ne(parameters.Condition, '') }}: + condition: ${{ parameters.Condition }} + env: + ${{ if not(parameters.DisableInstallRunRushLockfile) }}: + INSTALL_RUN_RUSH_LOCKFILE_PATH: ${{ parameters.RepoPath }}/common/config/validation/rush-package-lock.json + ${{ if ne(parameters.NpmAuthToken, '') }}: + NPM_AUTH_TOKEN: ${{ parameters.NpmAuthToken }} diff --git a/common/config/azure-pipelines/templates/pack.yaml b/common/config/azure-pipelines/templates/pack.yaml new file mode 100644 index 00000000000..9021c171445 --- /dev/null +++ b/common/config/azure-pipelines/templates/pack.yaml @@ -0,0 +1,18 @@ +steps: + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: '--help' + DisplayName: 'Install Rush' + + - bash: 'rm -f "$HOME/.npmrc"' + displayName: 'Clear user npmrc for publish' + + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: > + publish + --publish + --pack + --include-all + --release-folder $(Build.ArtifactStagingDirectory)/packages + DisplayName: 'Rush Pack' diff --git a/common/config/azure-pipelines/templates/post-publish.yaml b/common/config/azure-pipelines/templates/post-publish.yaml new file mode 100644 index 00000000000..a08e554a06e --- /dev/null +++ b/common/config/azure-pipelines/templates/post-publish.yaml @@ -0,0 +1,23 @@ +steps: + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: > + record-published-versions + --output-path $(Build.ArtifactStagingDirectory)/published-versions/published-versions.json + DisplayName: 'Record Published Versions' + + - template: /common/config/azure-pipelines/templates/run-repo-toolbox.yaml@self + parameters: + Arguments: > + collect-project-files + --subfolder temp/json-schemas + --output-path $(Build.ArtifactStagingDirectory)/json-schemas + DisplayName: 'Collect JSON Schemas' + + - template: /common/config/azure-pipelines/templates/run-repo-toolbox.yaml@self + parameters: + Arguments: > + collect-project-files + --subfolder temp/api + --output-path $(Build.ArtifactStagingDirectory)/api + DisplayName: 'Collect API review files' diff --git a/common/config/azure-pipelines/templates/publish.yaml b/common/config/azure-pipelines/templates/publish.yaml index 281d7edae9b..578cac0f310 100644 --- a/common/config/azure-pipelines/templates/publish.yaml +++ b/common/config/azure-pipelines/templates/publish.yaml @@ -6,7 +6,22 @@ parameters: default: $(Build.SourceBranchName) steps: - - script: 'node common/scripts/install-run-rush.js publish --apply --publish --include-all --target-branch ${{ parameters.BranchName }} --add-commit-details --set-access-level public' - displayName: 'Rush Publish (Policy: ${{ parameters.VersionPolicyName }})' - env: - NPM_AUTH_TOKEN: $(npmToken) + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: '--help' + DisplayName: 'Install Rush' + + - bash: 'rm -f "$HOME/.npmrc"' + displayName: 'Clear user npmrc for publish' + + - template: /common/config/azure-pipelines/templates/install-run-rush.yaml@self + parameters: + Arguments: > + publish + --apply + --publish + --include-all + --target-branch ${{ parameters.BranchName }} + --add-commit-details --set-access-level public + DisplayName: 'Rush Publish (Policy: ${{ parameters.VersionPolicyName }})' + NpmAuthToken: $(npmToken) diff --git a/common/config/azure-pipelines/templates/push-and-create-github-pr.yaml b/common/config/azure-pipelines/templates/push-and-create-github-pr.yaml new file mode 100644 index 00000000000..2aa09ac32d6 --- /dev/null +++ b/common/config/azure-pipelines/templates/push-and-create-github-pr.yaml @@ -0,0 +1,119 @@ +parameters: + - name: BranchName + type: string + - name: PrTitle + type: string + - name: PrDescription + type: string + default: '' + - name: TargetBranch + type: string + default: 'main' + - name: HasChangesVariableName + type: string + default: 'HasChanges' + - name: WorkingDirectory + type: string + default: '$(Build.SourcesDirectory)' + +steps: + # Force-push the branch. This is safe because the branch (e.g. "automated/bump-decoupled-deps") + # is exclusively owned by this pipeline and is never manually committed to. + - bash: | + set -e + git push origin ${{ parameters.BranchName }} --force + displayName: 'Push branch' + condition: and(succeeded(), eq(variables['${{ parameters.HasChangesVariableName }}'], 'true')) + workingDirectory: ${{ parameters.WorkingDirectory }} + + - bash: | + set -e + + # ── Resolve the GitHub owner/repo from the git remote URL ── + # Handles both HTTPS (https://github.com/owner/repo.git) and SSH (git@github.com:owner/repo.git) URLs. + REPO_SLUG=$(git remote get-url origin | sed -E 's#.*github\.com[:/](.+/[^.]+)(\.git)?$#\1#') + echo "Repository: ${REPO_SLUG}" + OWNER=$(echo "${REPO_SLUG}" | cut -d/ -f1) + + # ── Extract credentials from the AzDO-managed git config ── + # When "persistCredentials: true" is set on the checkout step, AzDO injects an + # "http..extraheader" git config entry containing an "AUTHORIZATION: basic " + # header for the GitHub service connection. We reuse this for GitHub API calls so that + # no additional secrets or PATs need to be configured. + AUTH_HEADER=$(git config --get-regexp 'http\..*\.extraheader' | head -1 | sed 's/^[^ ]* //') + if [ -z "$AUTH_HEADER" ]; then + echo "##[error]Could not extract authorization header from git config. Ensure persistCredentials is enabled on the checkout step." + exit 1 + fi + + # ── Write credentials to a temporary curl config file ── + # This avoids passing the auth token as a command-line argument, which would be + # visible in process listings (e.g. "ps aux") and could leak into logs. + CURL_CONFIG=$(mktemp) + trap 'rm -f "$CURL_CONFIG"' EXIT + echo "-H \"${AUTH_HEADER}\"" > "$CURL_CONFIG" + echo '-H "Accept: application/vnd.github+json"' >> "$CURL_CONFIG" + + API_BASE="https://api.github.com/repos/${REPO_SLUG}" + + # ── GitHub API helper ── + # Calls the GitHub API using the temporary curl config file for auth headers. + # On success (2xx), prints the response body to stdout. + # On failure, prints the HTTP status and error body to stderr and returns non-zero. + github_api() { + local RESPONSE HTTP_CODE BODY + RESPONSE=$(curl -s -w "\n%{http_code}" -K "$CURL_CONFIG" "$@") + HTTP_CODE=$(echo "$RESPONSE" | tail -n1) + BODY=$(echo "$RESPONSE" | sed '$d') + + if [[ "$HTTP_CODE" -ge 200 && "$HTTP_CODE" -lt 300 ]]; then + echo "$BODY" + else + echo "##[error]GitHub API returned HTTP ${HTTP_CODE}:" >&2 + echo "$BODY" >&2 + return 1 + fi + } + + # ── Check for an existing open PR from this branch ── + # The GitHub "List pull requests" API filters by "head=OWNER:BRANCH" to find any + # open PR already targeting this branch. If one exists, we update it instead of + # creating a duplicate. + EXISTING_PR=$(github_api \ + "${API_BASE}/pulls?head=${OWNER}:${{ parameters.BranchName }}&state=open" \ + | jq '.[0].number // empty') + + if [ -n "$EXISTING_PR" ]; then + # ── Update existing PR ── + # Only the description is updated; the title is left as-is since the branch was + # already force-pushed with the new commits above. + echo "Updating existing PR #${EXISTING_PR}" + github_api -X PATCH \ + "${API_BASE}/pulls/${EXISTING_PR}" \ + -d "$(jq -n --arg body "$PR_BODY" '{body: $body}')" > /dev/null + else + # ── Create new PR ── + # jq --arg safely handles JSON escaping of the title and body, so special + # characters (quotes, newlines, etc.) in the parameter values are safe. + echo "Creating new PR" + github_api -X POST \ + "${API_BASE}/pulls" \ + -d "$(jq -n \ + --arg title "$PR_TITLE" \ + --arg body "$PR_BODY" \ + --arg head "${{ parameters.BranchName }}" \ + --arg base "${{ parameters.TargetBranch }}" \ + '{title: $title, body: $body, head: $head, base: $base}')" > /dev/null + fi + displayName: 'Create or update GitHub PR' + condition: and(succeeded(), eq(variables['${{ parameters.HasChangesVariableName }}'], 'true')) + # Pass PR title and description as environment variables rather than using + # ${{ }} template expansion inside the script. Template expansion would + # substitute the raw string into the Bash source code, which breaks if the + # value contains quotes or other shell metacharacters. Environment variables + # are set by the AzDO agent outside of the shell, so they are safe regardless + # of content. + workingDirectory: ${{ parameters.WorkingDirectory }} + env: + PR_TITLE: ${{ parameters.PrTitle }} + PR_BODY: ${{ parameters.PrDescription }} diff --git a/common/config/azure-pipelines/templates/record-published-versions.yaml b/common/config/azure-pipelines/templates/record-published-versions.yaml deleted file mode 100644 index 29ed653d063..00000000000 --- a/common/config/azure-pipelines/templates/record-published-versions.yaml +++ /dev/null @@ -1,6 +0,0 @@ -steps: - - script: 'node repo-scripts/repo-toolbox/lib/start.js record-versions --out-file $(Build.ArtifactStagingDirectory)/published-versions/published-versions.json' - displayName: 'Record Published Versions' - - publish: $(Build.ArtifactStagingDirectory)/published-versions - artifact: published-versions - displayName: 'Publish Artifact: published-versions' diff --git a/common/config/azure-pipelines/templates/run-repo-toolbox.yaml b/common/config/azure-pipelines/templates/run-repo-toolbox.yaml new file mode 100644 index 00000000000..7e8786d8866 --- /dev/null +++ b/common/config/azure-pipelines/templates/run-repo-toolbox.yaml @@ -0,0 +1,10 @@ +# Runs repo-toolbox with the specified CLI arguments. +parameters: + - name: Arguments + type: string + - name: DisplayName + type: string + +steps: + - script: 'node repo-scripts/repo-toolbox/lib-commonjs/start.js ${{ parameters.Arguments }}' + displayName: '${{ parameters.DisplayName }}' diff --git a/common/config/azure-pipelines/vscode-extension-publish.yaml b/common/config/azure-pipelines/vscode-extension-publish.yaml new file mode 100644 index 00000000000..0c23c46d4ef --- /dev/null +++ b/common/config/azure-pipelines/vscode-extension-publish.yaml @@ -0,0 +1,119 @@ +variables: + - name: FORCE_COLOR + value: 1 + +parameters: + - name: shouldPublish + type: boolean + default: true + - name: publishUnsigned + type: boolean + default: true + - name: ExtensionPublishConfig + type: object + default: + - key: 'debug-certificate-manager-vscode-extension' + projectRelativeAssetsDir: dist/vsix + vsixPath: 'extension.vsix' + manifestPath: 'extension.signature.manifest' + projectPath: '$(Build.SourcesDirectory)/vscode-extensions/debug-certificate-manager-vscode-extension' + - key: 'playwright-local-browser-server-vscode-extension' + projectRelativeAssetsDir: dist/vsix + vsixPath: 'extension.vsix' + manifestPath: 'extension.signature.manifest' + projectPath: '$(Build.SourcesDirectory)/vscode-extensions/playwright-local-browser-server-vscode-extension' + - key: 'rush-vscode-extension' + projectRelativeAssetsDir: dist/vsix + vsixPath: 'extension.vsix' + manifestPath: 'extension.signature.manifest' + projectPath: '$(Build.SourcesDirectory)/vscode-extensions/rush-vscode-extension' + +resources: + repositories: + - repository: 1esPipelines + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release + +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines + parameters: + pool: + name: Azure-Pipelines-1ESPT-ExDShared + os: windows + stages: + - stage: + jobs: + - job: + pool: + name: publish-rushstack + os: linux + templateContext: + outputs: + - ${{ each extension in parameters.ExtensionPublishConfig }}: + - output: pipelineArtifact + artifactName: ${{ extension.key }} + targetPath: ${{ extension.projectPath }}/${{ extension.projectRelativeAssetsDir }}/${{ extension.vsixPath }} + displayName: 'Publish Artifact: ${{ extension.key }}' + + steps: + - checkout: self + persistCredentials: true + + - template: /common/config/azure-pipelines/templates/install-node.yaml@self + + - template: /common/config/azure-pipelines/templates/build.yaml@self + + - ${{ if parameters.shouldPublish }}: + - task: AzureCLI@2 + displayName: 'Get managed identity user info' + inputs: + azureSubscription: 'rushstack-vscode-publish' + scriptType: bash + scriptLocation: inlineScript + inlineScript: | + az rest -u https://app.vssps.visualstudio.com/_apis/profile/profiles/me --resource 499b84ac-1321-427f-aa17-267ca6975798 + + - ${{ each extension in parameters.ExtensionPublishConfig }}: + - bash: cp ${{ extension.manifestPath }} extension.signature.p7s + workingDirectory: ${{ extension.projectPath }}/${{ extension.projectRelativeAssetsDir }} + displayName: 'Prepare manifest for signing: ${{ extension.key }}' + + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 + displayName: 'ESRP CodeSigning' + inputs: + connectedservicename: 'rushstack-esrp-codesign-client' + AppRegistrationClientId: 'ceb49532-1c6a-445c-8d34-91ab779bdf50' + AppRegistrationTenantId: 'cdc5aeea-15c5-4db6-b079-fcadd2505dc2' + AuthAKVName: 'rushstack-esrp' + AuthCertName: 'ceb49532-rushstack-esrp' + AuthSignCertName: 'rushstack-vs-marketplace-publisher-signing-certificate' + FolderPath: '${{ extension.projectPath }}/${{ extension.projectRelativeAssetsDir }}' + Pattern: 'extension.signature.p7s' + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-401405", + "operationSetCode": "VSCodePublisherSign", + "parameters": [], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + + - bash: node node_modules/@rushstack/heft/lib-commonjs/start.js verify-signature --vsix-path ${{ extension.projectRelativeAssetsDir }}/${{ extension.vsixPath }} --manifest-path ${{ extension.projectRelativeAssetsDir }}/${{ extension.manifestPath }} --signature-path ${{ extension.projectRelativeAssetsDir }}/extension.signature.p7s + displayName: 'Verify Signature: ${{ extension.key }}' + workingDirectory: ${{ extension.projectPath }} + + - task: AzureCLI@2 + displayName: 'Publish VSIX: ${{ extension.key }}' + inputs: + azureSubscription: rushstack-vscode-publish + scriptType: 'bash' + scriptLocation: 'inlineScript' + workingDirectory: ${{ extension.projectPath }} + ${{ if parameters.publishUnsigned }}: + inlineScript: node node_modules/@rushstack/heft/lib-commonjs/start.js publish-vsix --vsix-path ${{ extension.projectRelativeAssetsDir }}/${{ extension.vsixPath }} --publish-unsigned + ${{ else }}: + inlineScript: node node_modules/@rushstack/heft/lib-commonjs/start.js publish-vsix --vsix-path ${{ extension.projectRelativeAssetsDir }}/${{ extension.vsixPath }} --manifest-path ${{ extension.projectRelativeAssetsDir }}/${{ extension.manifestPath }} --signature-path ${{ extension.projectRelativeAssetsDir }}/extension.signature.p7s diff --git a/common/config/lockfile-explorer/lockfile-lint.json b/common/config/lockfile-explorer/lockfile-lint.json new file mode 100644 index 00000000000..72449103987 --- /dev/null +++ b/common/config/lockfile-explorer/lockfile-lint.json @@ -0,0 +1,42 @@ +/** + * Config file for Lockfile Lint. For more info, please visit: https://lfx.rushstack.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/lockfile-explorer/lockfile-lint.schema.json", + + /** + * The list of rules to be checked by Lockfile Lint. For each rule configuration, the + * type of rule is determined by the `rule` field. + */ + "rules": [ + // /** + // * The `restrict-versions` rule enforces that direct and indirect dependencies must + // * satisfy a specified version range. + // */ + // { + // "rule": "restrict-versions", + // + // /** + // * The name of a workspace project to analyze. + // */ + // "project": "@my-company/my-app", + // + // /** + // * Indicates the package versions to be checked. The `requiredVersions` key is + // * the name of an NPM package, and the value is a SemVer range. If the project has + // * that NPM package as a dependency, then its version must satisfy the SemVer range. + // * This check also applies to devDependencies and peerDependencies, as well as any + // * indirect dependencies of the project. + // */ + // "requiredVersions": { + // /** + // * For example, if `react-router` appears anywhere in the dependency graph of + // * `@my-company/my-app`, then it must be version 5 or 6. + // */ + // "react-router": "5.x || 6.x", + // "react": "^18.3.0", + // "react-dom": "^18.3.0" + // } + // } + ] +} diff --git a/common/config/rush-plugins/rush-serve-plugin.json b/common/config/rush-plugins/rush-serve-plugin.json new file mode 100644 index 00000000000..d42c234aeb5 --- /dev/null +++ b/common/config/rush-plugins/rush-serve-plugin.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-serve-plugin-options.schema.json", + "phasedCommands": ["start"], + "portParameterLongName": "--port", + "buildStatusWebSocketPath": "/ws", + "logServePath": "/logs", + "globalRouting": [ + { + "workspaceRelativeFolder": "rush-plugins/rush-serve-plugin/lib-esm/dashboard", + "servePath": "/dashboard", + "immutable": false + } + ] +} diff --git a/common/config/rush/.npmrc b/common/config/rush/.npmrc deleted file mode 100644 index dd0f6ef9f0d..00000000000 --- a/common/config/rush/.npmrc +++ /dev/null @@ -1,28 +0,0 @@ -# Rush uses this file to configure the NPM package registry during installation. It is applicable -# to PNPM, NPM, and Yarn package managers. It is used by operations such as "rush install", -# "rush update", and the "install-run.js" scripts. -# -# NOTE: The "rush publish" command uses .npmrc-publish instead. -# -# Before invoking the package manager, Rush will copy this file to the folder where installation -# is performed. The copied file will omit any config lines that reference environment variables -# that are undefined in that session; this avoids problems that would otherwise result due to -# a missing variable being replaced by an empty string. -# -# * * * SECURITY WARNING * * * -# -# It is NOT recommended to store authentication tokens in a text file on a lab machine, because -# other unrelated processes may be able to read the file. Also, the file may persist indefinitely, -# for example if the machine loses power. A safer practice is to pass the token via an -# environment variable, which can be referenced from .npmrc using ${} expansion. For example: -# -# //registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} -# -registry=https://registry.npmjs.org/ -always-auth=false -# No phantom dependencies allowed in this repository -# Don't hoist in common/temp/node_modules -public-hoist-pattern= -# Don't hoist in common/temp/node_modules/.pnpm/node_modules -hoist=false -hoist-pattern= diff --git a/common/config/rush/.npmrc-publish b/common/config/rush/.npmrc-publish index 2d302fe6c1a..a0ffdc9d322 100644 --- a/common/config/rush/.npmrc-publish +++ b/common/config/rush/.npmrc-publish @@ -1,24 +1,25 @@ -# This config file is very similar to common/config/rush/.npmrc, except that .npmrc-publish -# is used by the "rush publish" command, as publishing often involves different credentials -# and registries than other operations. -# -# Before invoking the package manager, Rush will copy this file to "common/temp/publish-home/.npmrc" -# and then temporarily map that folder as the "home directory" for the current user account. -# This enables the same settings to apply for each project folder that gets published. The copied file -# will omit any config lines that reference environment variables that are undefined in that session; -# this avoids problems that would otherwise result due to a missing variable being replaced by -# an empty string. -# -# * * * SECURITY WARNING * * * -# -# It is NOT recommended to store authentication tokens in a text file on a lab machine, because -# other unrelated processes may be able to read the file. Also, the file may persist indefinitely, -# for example if the machine loses power. A safer practice is to pass the token via an -# environment variable, which can be referenced from .npmrc using ${} expansion. For example: -# -# //registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} -# - -registry=https://registry.npmjs.org/ -always-auth=true -//registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} +# This config file is very similar to common/config/rush/.npmrc, except that .npmrc-publish +# is used by the "rush publish" command, as publishing often involves different credentials +# and registries than other operations. +# +# Before invoking the package manager, Rush will copy this file to "common/temp/publish-home/.npmrc" +# and then temporarily map that folder as the "home directory" for the current user account. +# This enables the same settings to apply for each project folder that gets published. The copied file +# will omit any config lines that reference environment variables that are undefined in that session; +# this avoids problems that would otherwise result due to a missing variable being replaced by +# an empty string. +# +# * * * SECURITY WARNING * * * +# +# It is NOT recommended to store authentication tokens in a text file on a lab machine, because +# other unrelated processes may be able to read the file. Also, the file may persist indefinitely, +# for example if the machine loses power. A safer practice is to pass the token via an +# environment variable, which can be referenced from .npmrc using ${} expansion. For example: +# +# //registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} +# + +registry=https://registry.npmjs.org/ +always-auth=true +//registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} + diff --git a/common/config/rush/.pnpmfile.cjs b/common/config/rush/.pnpmfile.cjs deleted file mode 100644 index 64915c96d11..00000000000 --- a/common/config/rush/.pnpmfile.cjs +++ /dev/null @@ -1,61 +0,0 @@ -'use strict'; - -/** - * When using the PNPM package manager, you can use pnpmfile.js to workaround - * dependencies that have mistakes in their package.json file. (This feature is - * functionally similar to Yarn's "resolutions".) - * - * For details, see the PNPM documentation: - * https://pnpm.js.org/docs/en/hooks.html - * - * IMPORTANT: SINCE THIS FILE CONTAINS EXECUTABLE CODE, MODIFYING IT IS LIKELY TO INVALIDATE - * ANY CACHED DEPENDENCY ANALYSIS. After any modification to pnpmfile.js, it's recommended to run - * "rush update --full" so that PNPM will recalculate all version selections. - */ -module.exports = { - hooks: { - readPackage - } -}; - -/** - * This hook is invoked during installation before a package's dependencies - * are selected. - * The `packageJson` parameter is the deserialized package.json - * contents for the package that is about to be installed. - * The `context` parameter provides a log() function. - * The return value is the updated object. - */ -function readPackage(packageJson, context) { - if (packageJson.name.startsWith('@radix-ui/')) { - if (packageJson.peerDependencies && packageJson.peerDependencies['react']) { - packageJson.peerDependencies['@types/react'] = '*'; - packageJson.peerDependencies['@types/react-dom'] = '*'; - } - } - - switch (packageJson.name) { - case '@jest/test-result': { - // The `@jest/test-result` package takes undeclared dependencies on `jest-haste-map` - // and `jest-resolve` - packageJson.dependencies['jest-haste-map'] = packageJson.version; - packageJson.dependencies['jest-resolve'] = packageJson.version; - } - - case '@serverless-stack/core': { - delete packageJson.dependencies['@typescript-eslint/eslint-plugin']; - delete packageJson.dependencies['eslint-config-serverless-stack']; - delete packageJson.dependencies['lerna']; - break; - } - - case 'tslint-microsoft-contrib': { - // The `tslint-microsoft-contrib` repo is archived so it can't be updated to TS 4.4+. - // unmet peer typescript@"^2.1.0 || ^3.0.0": found 4.5.5 - packageJson.peerDependencies['typescript'] = '*'; - break; - } - } - - return packageJson; -} diff --git a/common/config/rush/artifactory.json b/common/config/rush/artifactory.json new file mode 100644 index 00000000000..685fda23c0e --- /dev/null +++ b/common/config/rush/artifactory.json @@ -0,0 +1,103 @@ +/** + * This configuration file manages Rush integration with JFrog Artifactory services. + * More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/artifactory.schema.json", + + "packageRegistry": { + /** + * (Required) Set this to "true" to enable Rush to manage tokens for an Artifactory NPM registry. + * When enabled, "rush install" will automatically detect when the user's ~/.npmrc + * authentication token is missing or expired. And "rush setup" will prompt the user to + * renew their token. + * + * The default value is false. + */ + "enabled": false, + + /** + * (Required) Specify the URL of your NPM registry. This is the same URL that appears in + * your .npmrc file. It should look something like this example: + * + * https://your-company.jfrog.io/your-project/api/npm/npm-private/ + */ + "registryUrl": "", + + /** + * A list of custom strings that "rush setup" should add to the user's ~/.npmrc file at the time + * when the token is updated. This could be used for example to configure the company registry + * to be used whenever NPM is invoked as a standalone command (but it's not needed for Rush + * operations like "rush add" and "rush install", which get their mappings from the monorepo's + * common/config/rush/.npmrc file). + * + * NOTE: The ~/.npmrc settings are global for the user account on a given machine, so be careful + * about adding settings that may interfere with other work outside the monorepo. + */ + "userNpmrcLinesToAdd": [ + // "@example:registry=https://your-company.jfrog.io/your-project/api/npm/npm-private/" + ], + + /** + * (Required) Specifies the URL of the Artifactory control panel where the user can generate + * an API key. This URL is printed after the "visitWebsite" message. + * It should look something like this example: https://your-company.jfrog.io/ + * Specify an empty string to suppress this line entirely. + */ + "artifactoryWebsiteUrl": "", + + /** + * Uncomment this line to specify the type of credential to save in the user's ~/.npmrc file. + * The default is "password", which means the user's API token will be traded in for an + * npm password specific to that registry. Optionally you can specify "authToken", which + * will save the user's API token as credentials instead. + */ + // "credentialType": "password", + + /** + * These settings allow the "rush setup" interactive prompts to be customized, for + * example with messages specific to your team or configuration. Specify an empty string + * to suppress that message entirely. + */ + "messageOverrides": { + /** + * Overrides the message that normally says: + * "This monorepo consumes packages from an Artifactory private NPM registry." + */ + // "introduction": "", + /** + * Overrides the message that normally says: + * "Please contact the repository maintainers for help with setting up an Artifactory user account." + */ + // "obtainAnAccount": "", + /** + * Overrides the message that normally says: + * "Please open this URL in your web browser:" + * + * The "artifactoryWebsiteUrl" string is printed after this message. + */ + // "visitWebsite": "", + /** + * Overrides the message that normally says: + * "Your user name appears in the upper-right corner of the JFrog website." + */ + // "locateUserName": "", + /** + * Overrides the message that normally says: + * "Click 'Edit Profile' on the JFrog website. Click the 'Generate API Key' + * button if you haven't already done so previously." + */ + // "locateApiKey": "" + /** + * Overrides the message that normally prompts: + * "What is your Artifactory user name?" + */ + // "userNamePrompt": "" + /** + * Overrides the message that normally prompts: + * "What is your Artifactory API key?" + */ + // "apiKeyPrompt": "" + } + } +} diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index 8a8d72ffe65..a761addba56 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -4,6 +4,14 @@ "packages": [ { "name": "@fluentui/react", + "allowedCategories": [ "libraries", "vscode-extensions" ] + }, + { + "name": "@fluentui/react-components", + "allowedCategories": [ "vscode-extensions" ] + }, + { + "name": "@jridgewell/sourcemap-codec", "allowedCategories": [ "libraries" ] }, { @@ -32,18 +40,26 @@ }, { "name": "@reduxjs/toolkit", + "allowedCategories": [ "libraries", "vscode-extensions" ] + }, + { + "name": "@rushstack/problem-matcher", "allowedCategories": [ "libraries" ] }, { - "name": "@rushstack/rush-themed-ui", + "name": "@rushstack/rush-serve-dashboard", "allowedCategories": [ "libraries" ] }, { - "name": "axios", + "name": "@rushstack/rush-themed-ui", "allowedCategories": [ "libraries" ] }, { - "name": "cors", + "name": "@rushstack/rush-vscode-command-webview", + "allowedCategories": [ "vscode-extensions" ] + }, + { + "name": "axios", "allowedCategories": [ "libraries" ] }, { @@ -54,32 +70,44 @@ "name": "office-ui-fabric-core", "allowedCategories": [ "libraries" ] }, + { + "name": "prism-react-renderer", + "allowedCategories": [ "libraries" ] + }, { "name": "react", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, { "name": "react-dom", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] + }, + { + "name": "react-hook-form", + "allowedCategories": [ "vscode-extensions" ] }, { "name": "react-redux", - "allowedCategories": [ "libraries" ] + "allowedCategories": [ "libraries", "vscode-extensions" ] }, { "name": "redux", - "allowedCategories": [ "libraries" ] + "allowedCategories": [ "libraries", "vscode-extensions" ] }, { "name": "rxjs", "allowedCategories": [ "libraries" ] }, + { + "name": "scheduler", + "allowedCategories": [ "vscode-extensions" ] + }, { "name": "tslib", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, { - "name": "update-notifier", + "name": "zod", "allowedCategories": [ "libraries" ] } ] diff --git a/common/config/rush/build-cache.json b/common/config/rush/build-cache.json index fdbd9246a0a..59bc58ccb99 100644 --- a/common/config/rush/build-cache.json +++ b/common/config/rush/build-cache.json @@ -24,14 +24,21 @@ * a [hash] token. * * Other available tokens: - * - [projectName] - * - [projectName:normalize] - * - [phaseName] - * - [phaseName:normalize] - * - [phaseName:trimPrefix] + * - [projectName] Example: "@my-scope/my-project" + * - [projectName:normalize] Example: "my-scope+my-project" + * - [phaseName] Example: "_phase:test/api" + * - [phaseName:normalize] Example: "_phase:test+api" + * - [phaseName:trimPrefix] Example: "test/api" + * - [os] Example: "win32" + * - [arch] Example: "x64" */ "cacheEntryNamePattern": "[projectName:normalize]-[phaseName:normalize]-[hash]", + /** + * (Optional) Salt to inject during calculation of the cache key. This can be used to invalidate the cache for all projects when the salt changes. + */ + // "cacheHashSalt": "1", + /** * Use this configuration with "cacheProvider"="azure-blob-storage" */ @@ -57,7 +64,15 @@ /** * If set to true, allow writing to the cache. Defaults to false. */ - // "isCacheWriteAllowed": true + // "isCacheWriteAllowed": true, + /** + * The Entra ID login flow to use. Defaults to 'AdoCodespacesAuth' on GitHub Codespaces, 'InteractiveBrowser' otherwise. + */ + // "loginFlow": "InteractiveBrowser", + /** + * If set to true, reading the cache requires authentication. Defaults to false. + */ + // "readRequiresAuthentication": true }, /** @@ -88,5 +103,43 @@ * If set to true, allow writing to the cache. Defaults to false. */ // "isCacheWriteAllowed": true + }, + + /** + * Use this configuration with "cacheProvider"="http" + */ + "httpConfiguration": { + /** + * (Required) The URL of the server that stores the caches. + * Example: "https://build-cacches.example.com/" + */ + // "url": "https://build-cacches.example.com/", + /** + * (Optional) The HTTP method to use when writing to the cache (defaults to PUT). + * Should be one of PUT, POST, or PATCH. + * Example: "PUT" + */ + // "uploadMethod": "PUT", + /** + * (Optional) HTTP headers to pass to the cache server. + * Example: { "X-HTTP-Company-Id": "109283" } + */ + // "headers": {}, + /** + * (Optional) Shell command that prints the authorization token needed to communicate with the + * cache server, and exits with exit code 0. This command will be executed from the root of + * the monorepo. + * Example: { "exec": "node", "args": ["common/scripts/auth.js"] } + */ + // "tokenHandler": { "exec": "node", "args": ["common/scripts/auth.js"] }, + /** + * (Optional) Prefix for cache keys. + * Example: "my-company-" + */ + // "cacheKeyPrefix": "", + /** + * (Optional) If set to true, allow writing to the cache. Defaults to false. + */ + // "isCacheWriteAllowed": true } } diff --git a/common/config/rush/cobuild.json b/common/config/rush/cobuild.json new file mode 100644 index 00000000000..dbac6a071cc --- /dev/null +++ b/common/config/rush/cobuild.json @@ -0,0 +1,22 @@ +/** + * This configuration file manages Rush's cobuild feature. + * More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/cobuild.schema.json", + + /** + * (Required) EXPERIMENTAL - Set this to true to enable the cobuild feature. + * RUSH_COBUILD_CONTEXT_ID should always be specified as an environment variable with an non-empty string, + * otherwise the cobuild feature will be disabled. + */ + "cobuildFeatureEnabled": false, + + /** + * (Required) Choose where cobuild lock will be acquired. + * + * The lock provider is registered by the rush plugins. + * For example, @rushstack/rush-redis-cobuild-plugin registers the "redis" lock provider. + */ + "cobuildLockProvider": "redis" +} diff --git a/common/config/rush/command-line.json b/common/config/rush/command-line.json index 7846e753ae4..851603df42f 100644 --- a/common/config/rush/command-line.json +++ b/common/config/rush/command-line.json @@ -14,14 +14,14 @@ { "commandKind": "phased", "name": "build", - "phases": ["_phase:build"] + "phases": ["_phase:lite-build", "_phase:build"] }, { "commandKind": "phased", "name": "test", "summary": "Builds all projects and runs their tests.", - "phases": ["_phase:build", "_phase:test"], + "phases": ["_phase:lite-build", "_phase:build", "_phase:test"], "enableParallelism": true, "incremental": true }, @@ -30,7 +30,7 @@ "commandKind": "phased", "name": "retest", "summary": "Rebuilds all projects and reruns their tests.", - "phases": ["_phase:build", "_phase:test"], + "phases": ["_phase:lite-build", "_phase:build", "_phase:test"], "enableParallelism": true, "incremental": false }, @@ -45,12 +45,14 @@ "enableParallelism": true, "incremental": true, // Initial execution only uses the build phase so that all dependencies of watch phases have been built - "phases": ["_phase:build"], + "phases": ["_phase:lite-build", "_phase:build"], "watchOptions": { // Act as though `--watch` is always passed. If false, adds support for passing `--watch`. "alwaysWatch": true, + // Build a full watch graph so projects can become enabled without rebuilding the graph. + "includeAllProjectsInWatchGraph": true, // During watch recompilation run both build and test for affected projects - "watchPhases": ["_phase:build", "_phase:test"] + "watchPhases": ["_phase:lite-build", "_phase:build", "_phase:test"] } }, // { @@ -119,6 +121,14 @@ // "enableParallelism": false, // // /** + // * Controls whether weighted operations can start when the total weight would exceed the limit + // * but is currently below the limit. This setting only applies when "enableParallelism" is true + // * and operations have a "weight" property configured in their rush-project.json "operationSettings". + // * Choose true (the default) to favor parallelism. Choose false to strictly stay under the limit. + // */ + // "allowOversubscription": false, + // + // /** // * Normally projects will be processed according to their dependency order: a given project will not start // * processing the command until all of its dependencies have completed. This restriction doesn't apply for // * certain operations, for example a "clean" task that deletes output files. In this case @@ -234,19 +244,30 @@ "phases": [ { - "name": "_phase:build", + // Used for very simple builds that don't support CLI arguments like `--production` or `--fix` + "name": "_phase:lite-build", "dependencies": { "upstream": ["_phase:build"] }, - "ignoreMissingScript": true, + "missingScriptBehavior": "silent", + "allowWarningsOnSuccess": false + }, + { + "name": "_phase:build", + "dependencies": { + // Don't need to declare the dependency on _phase:build because it is transitive via _phase:lite-build + "self": ["_phase:lite-build"] + }, + "missingScriptBehavior": "log", "allowWarningsOnSuccess": false }, { "name": "_phase:test", "dependencies": { + // Dependency on _phase:lite-build is transitive via _phase:build "self": ["_phase:build"] }, - "ignoreMissingScript": true, + "missingScriptBehavior": "silent", "allowWarningsOnSuccess": false } ], @@ -476,6 +497,14 @@ "associatedPhases": ["_phase:build", "_phase:test"], "associatedCommands": ["build", "rebuild", "test", "retest"] }, + { + "longName": "--port", + "parameterKind": "integer", + "argumentName": "PORT", + "description": "The port to use for the server", + "associatedPhases": [], + "associatedCommands": ["start"] + }, { "longName": "--update-snapshots", "parameterKind": "flag", @@ -489,6 +518,13 @@ "description": "Perform a production build, including minification and localization steps", "associatedPhases": ["_phase:build", "_phase:test"], "associatedCommands": ["build", "rebuild", "test", "retest"] + }, + { + "longName": "--fix", + "parameterKind": "flag", + "description": "Automatically fix problems encountered while linting", + "associatedPhases": ["_phase:build"], + "associatedCommands": ["build", "rebuild", "test", "retest"] } ] } diff --git a/common/config/rush/common-versions.json b/common/config/rush/common-versions.json deleted file mode 100644 index 60bc27da376..00000000000 --- a/common/config/rush/common-versions.json +++ /dev/null @@ -1,112 +0,0 @@ -/** - * This configuration file specifies NPM dependency version selections that affect all projects - * in a Rush repo. More documentation is available on the Rush website: https://rushjs.io - */ -{ - "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/common-versions.schema.json", - - /** - * A table that specifies a "preferred version" for a given NPM package. This feature is typically used - * to hold back an indirect dependency to a specific older version, or to reduce duplication of indirect dependencies. - * - * The "preferredVersions" value can be any SemVer range specifier (e.g. "~1.2.3"). Rush injects these values into - * the "dependencies" field of the top-level common/temp/package.json, which influences how the package manager - * will calculate versions. The specific effect depends on your package manager. Generally it will have no - * effect on an incompatible or already constrained SemVer range. If you are using PNPM, similar effects can be - * achieved using the pnpmfile.js hook. See the Rush documentation for more details. - * - * After modifying this field, it's recommended to run "rush update --full" so that the package manager - * will recalculate all version selections. - */ - "preferredVersions": { - /** - * When someone asks for "^1.0.0" make sure they get "1.2.3" when working in this repo, - * instead of the latest version. - */ - // "some-library": "1.2.3" - - // This should be the TypeScript version that's used to build most of the projects in the repo. - // Preferring it avoids errors for indirect dependencies that request it as a peer dependency. - // It's also the newest supported compiler, used by most build tests and used as the bundled compiler - // engine for API Extractor. - "typescript": "~5.0.4", - - // Workaround for https://github.com/microsoft/rushstack/issues/1466 - "eslint": "~8.7.0" - }, - - /** - * When set to true, for all projects in the repo, all dependencies will be automatically added as preferredVersions, - * except in cases where different projects specify different version ranges for a given dependency. For older - * package managers, this tended to reduce duplication of indirect dependencies. However, it can sometimes cause - * trouble for indirect dependencies with incompatible peerDependencies ranges. - * - * The default value is true. If you're encountering installation errors related to peer dependencies, - * it's recommended to set this to false. - * - * After modifying this field, it's recommended to run "rush update --full" so that the package manager - * will recalculate all version selections. - */ - // "implicitlyPreferredVersions": false, - - /** - * The "rush check" command can be used to enforce that every project in the repo must specify - * the same SemVer range for a given dependency. However, sometimes exceptions are needed. - * The allowedAlternativeVersions table allows you to list other SemVer ranges that will be - * accepted by "rush check" for a given dependency. - * - * IMPORTANT: THIS TABLE IS FOR *ADDITIONAL* VERSION RANGES THAT ARE ALTERNATIVES TO THE - * USUAL VERSION (WHICH IS INFERRED BY LOOKING AT ALL PROJECTS IN THE REPO). - * This design avoids unnecessary churn in this file. - */ - "allowedAlternativeVersions": { - /** - * Used by build-tests/eslint-7-test - */ - "eslint": ["~7.30.0"], - /** - * For example, allow some projects to use an older TypeScript compiler - * (in addition to whatever "usual" version is being used by other projects in the repo): - */ - "typescript": [ - // "~5.0.4" is the (inferred, not alternative) range used by most projects in this repo - - // The oldest supported compiler, used by build-tests/api-extractor-lib1-test - "~2.9.2", - // For testing Heft with TS V3 - "~3.9.10", - // For testing Heft with TS V4 - "~4.9.5" - ], - "source-map": [ - "~0.6.1" // API Extractor is using an older version of source-map because newer versions are async - ], - "tapable": [ - "2.2.1", - "1.1.3" // heft plugin is using an older version of tapable - ], - // --- For Webpack 4 projects ---- - "css-loader": ["~5.2.7"], - "html-webpack-plugin": ["~4.5.2"], - "postcss-loader": ["~4.1.0"], - "sass-loader": ["~10.0.0"], - "sass": ["~1.3.0"], - "source-map-loader": ["~1.1.3"], - "style-loader": ["~2.0.0"], - "terser-webpack-plugin": ["~3.0.8"], - "terser": ["~4.8.0"], - "webpack": ["~4.44.2"], - "@types/node": [ - // These versions are used by testing projects - "ts2.9", - "ts3.9", - "ts4.9" - ], - "@types/jest": [ - // These versions are used by testing projects - "ts2.9", - "ts3.9", - "ts4.9" - ] - } -} diff --git a/common/config/rush/custom-tips.json b/common/config/rush/custom-tips.json new file mode 100644 index 00000000000..5f9ce32bfb8 --- /dev/null +++ b/common/config/rush/custom-tips.json @@ -0,0 +1,29 @@ +/** + * This configuration file allows repo maintainers to configure extra details to be + * printed alongside certain Rush messages. More documentation is available on the + * Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/custom-tips.schema.json", + + /** + * Custom tips allow you to annotate Rush's console messages with advice tailored for + * your specific monorepo. + */ + "customTips": [ + // { + // /** + // * (REQUIRED) An identifier indicating a message that may be printed by Rush. + // * If that message is printed, then this custom tip will be shown. + // * The list of available tip identifiers can be found on this page: + // * https://rushjs.io/pages/maintainer/custom_tips/ + // */ + // "tipId": "TIP_RUSH_INCONSISTENT_VERSIONS", + // + // /** + // * (REQUIRED) The message text to be displayed for this tip. + // */ + // "message": "For additional troubleshooting information, refer this wiki article:\n\nhttps://intranet.contoso.com/docs/pnpm-mismatch" + // } + ] +} diff --git a/common/config/rush/experiments.json b/common/config/rush/experiments.json index fef826208c3..ee3a9ebdaba 100644 --- a/common/config/rush/experiments.json +++ b/common/config/rush/experiments.json @@ -17,6 +17,13 @@ */ "usePnpmPreferFrozenLockfileForRushUpdate": true, + /** + * By default, 'rush update' runs as a single operation. + * Set this option to true to instead update the lockfile with `--lockfile-only`, then perform a `--frozen-lockfile` install. + * Necessary when using the `afterAllResolved` hook in .pnpmfile.cjs. + */ + // "usePnpmLockfileOnlyThenFrozenLockfileForRushUpdate": true, + /** * If using the 'preventManualShrinkwrapChanges' option, restricts the hash to only include the layout of external dependencies. * Used to allow links between workspace projects or the addition/removal of references to existing dependency versions to not @@ -37,10 +44,10 @@ // "buildCacheWithAllowWarningsInSuccessfulBuild": true, /** - * If true, the phased commands feature is enabled. To use this feature, create a "phased" command - * in common/config/rush/command-line.json. + * If true, build skipping will respect the allowWarningsInSuccessfulBuild flag and skip builds with warnings. + * This will not replay warnings from the skipped build. */ - "phasedCommands": true + // "buildSkipWithAllowWarningsInSuccessfulBuild": true, /** * If true, perform a clean install after when running `rush install` or `rush update` if the @@ -51,5 +58,87 @@ /** * If true, print the outputs of shell commands defined in event hooks to the console. */ - // "printEventHooksOutputToConsole": true + // "printEventHooksOutputToConsole": true, + + /** + * If true, Rush will not allow node_modules in the repo folder or in parent folders. + */ + // "forbidPhantomResolvableNodeModulesFolders": true, + + /** + * (UNDER DEVELOPMENT) For certain installation problems involving peer dependencies, PNPM cannot + * correctly satisfy versioning requirements without installing duplicate copies of a package inside the + * node_modules folder. This poses a problem for "workspace:*" dependencies, as they are normally + * installed by making a symlink to the local project source folder. PNPM's "injected dependencies" + * feature provides a model for copying the local project folder into node_modules, however copying + * must occur AFTER the dependency project is built and BEFORE the consuming project starts to build. + * The "pnpm-sync" tool manages this operation; see its documentation for details. + * Enable this experiment if you want "rush" and "rushx" commands to resync injected dependencies + * by invoking "pnpm-sync" during the build. + */ + "usePnpmSyncForInjectedDependencies": true, + + /** + * If set to true, Rush will generate a `project-impact-graph.yaml` file in the repository root during `rush update`. + */ + // "generateProjectImpactGraphDuringRushUpdate": true, + + /** + * If true, when running in watch mode, Rush will check for phase scripts named `_phase::ipc` and run them instead + * of `_phase:` if they exist. The created child process will be provided with an IPC channel and expected to persist + * across invocations. + */ + "useIPCScriptsInWatchMode": true, + + /** + * (UNDER DEVELOPMENT) The Rush alerts feature provides a way to send announcements to engineers + * working in the monorepo, by printing directly in the user's shell window when they invoke Rush commands. + * This ensures that important notices will be seen by anyone doing active development, since people often + * ignore normal discussion group messages or don't know to subscribe. + */ + // "rushAlerts": true, + + /** + * When using cobuilds, this experiment allows uncacheable operations to benefit from cobuild orchestration without using the build cache. + */ + // "allowCobuildWithoutCache": true, + + /** + * By default, rush perform a full scan of the entire repository. For example, Rush runs `git status` to check for local file changes. + * When this toggle is enabled, Rush will only scan specific paths, significantly speeding up Git operations. + */ + // "enableSubpathScan": true, + + /** + * Rush has a policy that normally requires Rush projects to specify `workspace:*` in package.json when depending + * on other projects in the workspace, unless they are explicitly declared as `decoupledLocalDependencies` + * in rush.json. Enabling this experiment will remove that requirement for dependencies belonging to a different + * subspace. This is useful for large product groups who work in separate subspaces and generally prefer to consume + * each other's packages via the NPM registry. + */ + // "exemptDecoupledDependenciesBetweenSubspaces": true, + + /** + * If true, when running on macOS, Rush will omit AppleDouble files (._*) from build cache archives + * when a companion file exists in the same directory. AppleDouble files are automatically created by + * macOS to store extended attributes on filesystems that don't support them, and should generally not + * be included in the shared build cache. + */ + "omitAppleDoubleFilesFromBuildCache": true, + + /** + * If true, "rush change --verify" will report errors if change files reference projects that do not + * exist in the Rush configuration, or if change files target a project that belongs to a lockstepped + * version policy but is not the policy's main project. + */ + "strictChangefileValidation": true, + + /** + * If true, the build cache will use file-based APIs to transfer cache entries to and from cloud storage. + * This avoids loading the entire cache entry into memory, which can prevent out-of-memory errors for large + * build outputs and allow cache entries to exceed the limit of a single Buffer. The cloud cache provider plugin + * must implement the optional file-based methods for this to take effect; otherwise it falls back to the + * buffer-based approach. + */ + "useDirectFileTransfersForBuildCache": true } diff --git a/common/config/rush/nonbrowser-approved-packages.json b/common/config/rush/nonbrowser-approved-packages.json index 20c80835eb7..0f1ebb8d9ab 100644 --- a/common/config/rush/nonbrowser-approved-packages.json +++ b/common/config/rush/nonbrowser-approved-packages.json @@ -2,6 +2,14 @@ { "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/approved-packages.schema.json", "packages": [ + { + "name": "@aws-sdk/client-sso-oidc", + "allowedCategories": [ "tests" ] + }, + { + "name": "@aws-sdk/client-sts", + "allowedCategories": [ "tests" ] + }, { "name": "@azure/identity", "allowedCategories": [ "libraries" ] @@ -14,6 +22,30 @@ "name": "@babel/core", "allowedCategories": [ "tests" ] }, + { + "name": "@eslint/eslintrc", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@inquirer/checkbox", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@inquirer/confirm", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@inquirer/input", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@inquirer/search", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@inquirer/select", + "allowedCategories": [ "libraries" ] + }, { "name": "@jest/core", "allowedCategories": [ "libraries" ] @@ -50,9 +82,13 @@ "name": "@microsoft/load-themed-styles", "allowedCategories": [ "libraries" ] }, + { + "name": "@microsoft/rush", + "allowedCategories": [ "tests" ] + }, { "name": "@microsoft/rush-lib", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, { "name": "@microsoft/teams-js", @@ -66,6 +102,10 @@ "name": "@microsoft/tsdoc-config", "allowedCategories": [ "libraries" ] }, + { + "name": "@modelcontextprotocol/sdk", + "allowedCategories": [ "libraries" ] + }, { "name": "@nodelib/fs.scandir", "allowedCategories": [ "libraries" ] @@ -74,26 +114,78 @@ "name": "@nodelib/fs.stat", "allowedCategories": [ "libraries" ] }, + { + "name": "@playwright/test", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@pnpm/dependency-path", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@pnpm/dependency-path-lockfile-pre-v10", + "allowedCategories": [ "libraries" ] + }, { "name": "@pnpm/link-bins", "allowedCategories": [ "libraries" ] }, + { + "name": "@pnpm/lockfile-file", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@pnpm/lockfile-types", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@pnpm/lockfile.fs", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@pnpm/lockfile.types", + "allowedCategories": [ "libraries" ] + }, { "name": "@pnpm/logger", "allowedCategories": [ "libraries" ] }, { - "name": "@rushstack/debug-certificate-manager", + "name": "@pnpm/types", "allowedCategories": [ "libraries" ] }, { - "name": "@rushstack/eslint-config", + "name": "@redis/client", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@rspack/core", "allowedCategories": [ "libraries", "tests" ] }, { - "name": "@rushstack/eslint-patch", + "name": "@rspack/dev-server", "allowedCategories": [ "libraries" ] }, + { + "name": "@rushstack/credential-cache", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@rushstack/debug-certificate-manager", + "allowedCategories": [ "libraries", "vscode-extensions" ] + }, + { + "name": "@rushstack/eslint-bulk", + "allowedCategories": [ "tests" ] + }, + { + "name": "@rushstack/eslint-config", + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] + }, + { + "name": "@rushstack/eslint-patch", + "allowedCategories": [ "libraries", "tests" ] + }, { "name": "@rushstack/eslint-plugin", "allowedCategories": [ "libraries" ] @@ -112,7 +204,7 @@ }, { "name": "@rushstack/heft", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, { "name": "@rushstack/heft-api-extractor-plugin", @@ -126,18 +218,38 @@ "name": "@rushstack/heft-dev-cert-plugin", "allowedCategories": [ "libraries", "tests" ] }, + { + "name": "@rushstack/heft-isolated-typescript-transpile-plugin", + "allowedCategories": [ "tests" ] + }, { "name": "@rushstack/heft-jest-plugin", "allowedCategories": [ "libraries", "tests" ] }, + { + "name": "@rushstack/heft-json-schema-typings-plugin", + "allowedCategories": [ "tests" ] + }, { "name": "@rushstack/heft-lint-plugin", "allowedCategories": [ "libraries", "tests" ] }, + { + "name": "@rushstack/heft-localization-typings-plugin", + "allowedCategories": [ "tests" ] + }, { "name": "@rushstack/heft-node-rig", + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] + }, + { + "name": "@rushstack/heft-rspack-plugin", "allowedCategories": [ "libraries", "tests" ] }, + { + "name": "@rushstack/heft-sass-load-themed-styles-plugin", + "allowedCategories": [ "tests" ] + }, { "name": "@rushstack/heft-sass-plugin", "allowedCategories": [ "libraries", "tests" ] @@ -146,17 +258,29 @@ "name": "@rushstack/heft-serverless-stack-plugin", "allowedCategories": [ "tests" ] }, + { + "name": "@rushstack/heft-static-asset-typings-plugin", + "allowedCategories": [ "libraries", "tests" ] + }, { "name": "@rushstack/heft-storybook-plugin", "allowedCategories": [ "tests" ] }, { "name": "@rushstack/heft-typescript-plugin", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] + }, + { + "name": "@rushstack/heft-vscode-extension-plugin", + "allowedCategories": [ "libraries", "vscode-extensions" ] + }, + { + "name": "@rushstack/heft-vscode-extension-rig", + "allowedCategories": [ "vscode-extensions" ] }, { "name": "@rushstack/heft-web-rig", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, { "name": "@rushstack/heft-webpack4-plugin", @@ -164,7 +288,7 @@ }, { "name": "@rushstack/heft-webpack5-plugin", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, { "name": "@rushstack/localization-utilities", @@ -174,13 +298,29 @@ "name": "@rushstack/lockfile-explorer-web", "allowedCategories": [ "libraries" ] }, + { + "name": "@rushstack/lookup-by-path", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@rushstack/mcp-server", + "allowedCategories": [ "libraries", "tests" ] + }, { "name": "@rushstack/module-minifier", "allowedCategories": [ "libraries", "tests" ] }, { "name": "@rushstack/node-core-library", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] + }, + { + "name": "@rushstack/npm-check-fork", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@rushstack/operation-graph", + "allowedCategories": [ "libraries" ] }, { "name": "@rushstack/package-deps-hash", @@ -188,6 +328,14 @@ }, { "name": "@rushstack/package-extractor", + "allowedCategories": [ "libraries", "vscode-extensions" ] + }, + { + "name": "@rushstack/playwright-browser-tunnel", + "allowedCategories": [ "vscode-extensions" ] + }, + { + "name": "@rushstack/real-node-module-path", "allowedCategories": [ "libraries" ] }, { @@ -202,13 +350,41 @@ "name": "@rushstack/rush-azure-storage-build-cache-plugin", "allowedCategories": [ "libraries" ] }, + { + "name": "@rushstack/rush-bridge-cache-plugin", + "allowedCategories": [ "libraries" ] + }, { "name": "@rushstack/rush-http-build-cache-plugin", "allowedCategories": [ "libraries" ] }, + { + "name": "@rushstack/rush-pnpm-kit-v10", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@rushstack/rush-pnpm-kit-v8", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@rushstack/rush-pnpm-kit-v9", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@rushstack/rush-redis-cobuild-plugin", + "allowedCategories": [ "tests" ] + }, + { + "name": "@rushstack/rush-resolver-cache-plugin", + "allowedCategories": [ "libraries" ] + }, { "name": "@rushstack/rush-sdk", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] + }, + { + "name": "@rushstack/rush-serve-plugin", + "allowedCategories": [ "libraries" ] }, { "name": "@rushstack/set-webpack-public-path-plugin", @@ -220,7 +396,11 @@ }, { "name": "@rushstack/terminal", - "allowedCategories": [ "libraries" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] + }, + { + "name": "@rushstack/tls-sync-vscode-shared", + "allowedCategories": [ "vscode-extensions" ] }, { "name": "@rushstack/tree-pattern", @@ -228,12 +408,16 @@ }, { "name": "@rushstack/ts-command-line", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, { "name": "@rushstack/typings-generator", "allowedCategories": [ "libraries" ] }, + { + "name": "@rushstack/vscode-shared", + "allowedCategories": [ "vscode-extensions" ] + }, { "name": "@rushstack/webpack-deep-imports-plugin", "allowedCategories": [ "libraries" ] @@ -244,6 +428,10 @@ }, { "name": "@rushstack/webpack-preserve-dynamic-require-plugin", + "allowedCategories": [ "libraries", "vscode-extensions" ] + }, + { + "name": "@rushstack/webpack-workspace-resolve-plugin", "allowedCategories": [ "libraries" ] }, { @@ -262,6 +450,10 @@ "name": "@rushstack/worker-pool", "allowedCategories": [ "libraries" ] }, + { + "name": "@rushstack/zipsync", + "allowedCategories": [ "libraries" ] + }, { "name": "@serverless-stack/aws-lambda-ric", "allowedCategories": [ "tests" ] @@ -302,30 +494,58 @@ "name": "@storybook/react", "allowedCategories": [ "tests" ] }, + { + "name": "@storybook/react-webpack5", + "allowedCategories": [ "libraries", "tests" ] + }, { "name": "@storybook/theming", "allowedCategories": [ "tests" ] }, + { + "name": "@swc/core", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@testing-library/dom", + "allowedCategories": [ "tests" ] + }, { "name": "@tsconfig/node14", "allowedCategories": [ "tests" ] }, { "name": "@typescript-eslint/eslint-plugin", - "allowedCategories": [ "libraries" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, { - "name": "@typescript-eslint/experimental-utils", + "name": "@typescript-eslint/parser", + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] + }, + { + "name": "@typescript-eslint/rule-tester", "allowedCategories": [ "libraries" ] }, { - "name": "@typescript-eslint/parser", - "allowedCategories": [ "libraries", "tests" ] + "name": "@typescript-eslint/types", + "allowedCategories": [ "libraries" ] }, { "name": "@typescript-eslint/typescript-estree", "allowedCategories": [ "libraries" ] }, + { + "name": "@typescript-eslint/utils", + "allowedCategories": [ "libraries" ] + }, + { + "name": "@vscode/test-electron", + "allowedCategories": [ "vscode-extensions" ] + }, + { + "name": "@vscode/vsce", + "allowedCategories": [ "libraries", "vscode-extensions" ] + }, { "name": "@yarnpkg/lockfile", "allowedCategories": [ "libraries" ] @@ -334,6 +554,14 @@ "name": "ajv", "allowedCategories": [ "libraries" ] }, + { + "name": "ajv-draft-04", + "allowedCategories": [ "libraries" ] + }, + { + "name": "ajv-formats", + "allowedCategories": [ "libraries" ] + }, { "name": "api-extractor-lib1-test", "allowedCategories": [ "tests" ] @@ -346,6 +574,14 @@ "name": "api-extractor-lib3-test", "allowedCategories": [ "tests" ] }, + { + "name": "api-extractor-lib4-test", + "allowedCategories": [ "tests" ] + }, + { + "name": "api-extractor-lib5-test", + "allowedCategories": [ "tests" ] + }, { "name": "api-extractor-test-01", "allowedCategories": [ "tests" ] @@ -370,10 +606,6 @@ "name": "babel-loader", "allowedCategories": [ "tests" ] }, - { - "name": "builtin-modules", - "allowedCategories": [ "libraries" ] - }, { "name": "buttono", "allowedCategories": [ "tests" ] @@ -383,17 +615,17 @@ "allowedCategories": [ "libraries" ] }, { - "name": "cli-table", + "name": "compression", "allowedCategories": [ "libraries" ] }, - { - "name": "colors", - "allowedCategories": [ "libraries", "tests" ] - }, { "name": "constructs", "allowedCategories": [ "tests" ] }, + { + "name": "cors", + "allowedCategories": [ "libraries" ] + }, { "name": "css-loader", "allowedCategories": [ "libraries", "tests" ] @@ -406,6 +638,10 @@ "name": "decache", "allowedCategories": [ "libraries" ] }, + { + "name": "decoupled-local-node-rig", + "allowedCategories": [ "libraries" ] + }, { "name": "diff", "allowedCategories": [ "libraries" ] @@ -414,9 +650,37 @@ "name": "doc-plugin-rush-stack", "allowedCategories": [ "libraries" ] }, + { + "name": "dotenv", + "allowedCategories": [ "libraries" ] + }, { "name": "eslint", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] + }, + { + "name": "eslint-import-resolver-node", + "allowedCategories": [ "libraries" ] + }, + { + "name": "eslint-plugin-deprecation", + "allowedCategories": [ "libraries" ] + }, + { + "name": "eslint-plugin-header", + "allowedCategories": [ "libraries" ] + }, + { + "name": "eslint-plugin-headers", + "allowedCategories": [ "libraries" ] + }, + { + "name": "eslint-plugin-import", + "allowedCategories": [ "libraries" ] + }, + { + "name": "eslint-plugin-jsdoc", + "allowedCategories": [ "libraries" ] }, { "name": "eslint-plugin-promise", @@ -426,6 +690,10 @@ "name": "eslint-plugin-react", "allowedCategories": [ "libraries" ] }, + { + "name": "eslint-plugin-react-hooks", + "allowedCategories": [ "libraries" ] + }, { "name": "eslint-plugin-tsdoc", "allowedCategories": [ "libraries" ] @@ -442,10 +710,6 @@ "name": "fastify", "allowedCategories": [ "tests" ] }, - { - "name": "figures", - "allowedCategories": [ "libraries" ] - }, { "name": "file-loader", "allowedCategories": [ "tests" ] @@ -460,14 +724,14 @@ }, { "name": "glob", - "allowedCategories": [ "libraries" ] + "allowedCategories": [ "libraries", "vscode-extensions" ] }, { - "name": "glob-escape", - "allowedCategories": [ "libraries" ] + "name": "heft-action-plugin", + "allowedCategories": [ "tests" ] }, { - "name": "heft-action-plugin", + "name": "heft-example-lifecycle-plugin", "allowedCategories": [ "tests" ] }, { @@ -487,15 +751,19 @@ "allowedCategories": [ "tests" ] }, { - "name": "heft-storybook-react-tutorial", + "name": "heft-storybook-v6-react-tutorial", "allowedCategories": [ "tests" ] }, { - "name": "heft-storybook-react-tutorial-storybook", + "name": "heft-storybook-v6-react-tutorial-storykit", "allowedCategories": [ "tests" ] }, { - "name": "heft-storybook-react-tutorial-storykit", + "name": "heft-storybook-v9-react-tutorial", + "allowedCategories": [ "tests" ] + }, + { + "name": "heft-storybook-v9-react-tutorial-storykit", "allowedCategories": [ "tests" ] }, { @@ -508,26 +776,26 @@ }, { "name": "html-webpack-plugin", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, { "name": "http-proxy", "allowedCategories": [ "tests" ] }, { - "name": "https-proxy-agent", + "name": "http2-express-bridge", "allowedCategories": [ "libraries" ] }, { - "name": "ignore", + "name": "https-proxy-agent", "allowedCategories": [ "libraries" ] }, { - "name": "import-lazy", + "name": "ignore", "allowedCategories": [ "libraries" ] }, { - "name": "inquirer", + "name": "import-lazy", "allowedCategories": [ "libraries" ] }, { @@ -546,6 +814,10 @@ "name": "jest-environment-node", "allowedCategories": [ "libraries" ] }, + { + "name": "jest-junit", + "allowedCategories": [ "libraries" ] + }, { "name": "jest-resolve", "allowedCategories": [ "libraries" ] @@ -566,6 +838,14 @@ "name": "js-yaml", "allowedCategories": [ "libraries" ] }, + { + "name": "json-schema-to-typescript", + "allowedCategories": [ "libraries" ] + }, + { + "name": "json-stable-stringify-without-jsonify", + "allowedCategories": [ "libraries" ] + }, { "name": "jsonpath-plus", "allowedCategories": [ "libraries" ] @@ -579,8 +859,16 @@ "allowedCategories": [ "libraries" ] }, { - "name": "lodash", - "allowedCategories": [ "libraries", "tests" ] + "name": "local-eslint-config", + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] + }, + { + "name": "local-node-rig", + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] + }, + { + "name": "local-web-rig", + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, { "name": "long", @@ -598,6 +886,10 @@ "name": "minimatch", "allowedCategories": [ "libraries" ] }, + { + "name": "mocha", + "allowedCategories": [ "vscode-extensions" ] + }, { "name": "node-fetch", "allowedCategories": [ "libraries" ] @@ -623,7 +915,31 @@ "allowedCategories": [ "libraries" ] }, { - "name": "open", + "name": "object-hash", + "allowedCategories": [ "libraries" ] + }, + { + "name": "package-extractor-test-02", + "allowedCategories": [ "tests" ] + }, + { + "name": "package-extractor-test-03", + "allowedCategories": [ "tests" ] + }, + { + "name": "package-json", + "allowedCategories": [ "libraries" ] + }, + { + "name": "playwright", + "allowedCategories": [ "libraries" ] + }, + { + "name": "playwright-core", + "allowedCategories": [ "libraries", "vscode-extensions" ] + }, + { + "name": "pnpm-sync-lib", "allowedCategories": [ "libraries" ] }, { @@ -646,6 +962,10 @@ "name": "pseudolocale", "allowedCategories": [ "libraries" ] }, + { + "name": "punycode", + "allowedCategories": [ "libraries" ] + }, { "name": "read-package-tree", "allowedCategories": [ "libraries" ] @@ -654,6 +974,10 @@ "name": "resolve", "allowedCategories": [ "libraries" ] }, + { + "name": "run-scenarios-helpers", + "allowedCategories": [ "tests" ] + }, { "name": "sass", "allowedCategories": [ "libraries", "tests" ] @@ -687,15 +1011,15 @@ "allowedCategories": [ "libraries" ] }, { - "name": "strict-uri-encode", - "allowedCategories": [ "libraries" ] + "name": "storybook", + "allowedCategories": [ "tests" ] }, { - "name": "string-argv", + "name": "strict-uri-encode", "allowedCategories": [ "libraries" ] }, { - "name": "strip-json-comments", + "name": "string-argv", "allowedCategories": [ "libraries" ] }, { @@ -706,6 +1030,10 @@ "name": "sudo", "allowedCategories": [ "libraries" ] }, + { + "name": "supports-color", + "allowedCategories": [ "libraries" ] + }, { "name": "tapable", "allowedCategories": [ "libraries", "tests" ] @@ -722,10 +1050,22 @@ "name": "terser-webpack-plugin", "allowedCategories": [ "libraries", "tests" ] }, + { + "name": "throat", + "allowedCategories": [ "libraries" ] + }, { "name": "timsort", "allowedCategories": [ "libraries" ] }, + { + "name": "tls-sync-vscode-ui-extension", + "allowedCategories": [ "vscode-extensions" ] + }, + { + "name": "tls-sync-vscode-workspace-extension", + "allowedCategories": [ "vscode-extensions" ] + }, { "name": "true-case-path", "allowedCategories": [ "libraries" ] @@ -738,29 +1078,29 @@ "name": "tslint", "allowedCategories": [ "libraries", "tests" ] }, - { - "name": "tslint-microsoft-contrib", - "allowedCategories": [ "tests" ] - }, { "name": "typescript", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, { "name": "url-loader", "allowedCategories": [ "libraries" ] }, + { + "name": "uuid", + "allowedCategories": [ "libraries" ] + }, { "name": "watchpack", "allowedCategories": [ "libraries" ] }, { "name": "webpack", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, { "name": "webpack-bundle-analyzer", - "allowedCategories": [ "libraries", "tests" ] + "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] }, { "name": "webpack-cli", @@ -782,12 +1122,16 @@ "name": "wordwrap", "allowedCategories": [ "libraries" ] }, + { + "name": "ws", + "allowedCategories": [ "libraries" ] + }, { "name": "xmldoc", "allowedCategories": [ "libraries" ] }, { - "name": "z-schema", + "name": "zod", "allowedCategories": [ "libraries" ] } ] diff --git a/common/config/rush/pnpm-config.json b/common/config/rush/pnpm-config.json index 50a1128c743..5c30f0c6991 100644 --- a/common/config/rush/pnpm-config.json +++ b/common/config/rush/pnpm-config.json @@ -1,6 +1,11 @@ /** * This configuration file provides settings specific to the PNPM package manager. * More documentation is available on the Rush website: https://rushjs.io + * + * Rush normally looks for this file in `common/config/rush/pnpm-config.json`. However, + * if `subspacesEnabled` is true in subspaces.json, then Rush will instead first look + * for `common/config/subspaces//pnpm-config.json`. (If the file exists in both places, + * then the file under `common/config/rush` is ignored.) */ { "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/pnpm-config.schema.json", @@ -19,6 +24,133 @@ */ "useWorkspaces": true, + /** + * This setting determines how PNPM chooses version numbers during `rush update`. + * For example, suppose `lib-x@3.0.0` depends on `"lib-y": "^1.2.3"` whose latest major + * releases are `1.8.9` and `2.3.4`. The resolution mode `lowest-direct` might choose + * `lib-y@1.2.3`, wheres `highest` will choose 1.8.9, and `time-based` will pick the + * highest compatible version at the time when `lib-x@3.0.0` itself was published (ensuring + * that the version could have been tested by the maintainer of "lib-x"). For local workspace + * projects, `time-based` instead works like `lowest-direct`, avoiding upgrades unless + * they are explicitly requested. Although `time-based` is the most robust option, it may be + * slightly slower with registries such as npmjs.com that have not implemented an optimization. + * + * IMPORTANT: Be aware that PNPM 8.0.0 initially defaulted to `lowest-direct` instead of + * `highest`, but PNPM reverted this decision in 8.6.12 because it caused confusion for users. + * Rush version 5.106.0 and newer avoids this confusion by consistently defaulting to + * `highest` when `resolutionMode` is not explicitly set in pnpm-config.json or .npmrc, + * regardless of your PNPM version. + * + * PNPM documentation: https://pnpm.io/npmrc#resolution-mode + * + * Possible values are: `highest`, `time-based`, and `lowest-direct`. + * The default is `highest`. + */ + // "resolutionMode": "time-based", + + /** + * This setting determines whether PNPM will automatically install (non-optional) + * missing peer dependencies instead of reporting an error. Doing so conveniently + * avoids the need to specify peer versions in package.json, but in a large monorepo + * this often creates worse problems. The reason is that peer dependency behavior + * is inherently complicated, and it is easier to troubleshoot consequences of an explicit + * version than an invisible heuristic. The original NPM RFC discussion pointed out + * some other problems with this feature: https://github.com/npm/rfcs/pull/43 + + * IMPORTANT: Without Rush, the setting defaults to true for PNPM 8 and newer; however, + * as of Rush version 5.109.0 the default is always false unless `autoInstallPeers` + * is specified in pnpm-config.json or .npmrc, regardless of your PNPM version. + + * PNPM documentation: https://pnpm.io/npmrc#auto-install-peers + + * The default value is false. + */ + // "autoInstallPeers": false, + + /** + * The minimum number of minutes that must pass after a version is published before pnpm will install it. + * This setting helps reduce the risk of installing compromised packages, as malicious releases are typically + * discovered and removed within a short time frame. + * + * For example, the following setting ensures that only packages released at least one day ago can be installed: + * + * "minimumReleaseAgeMinutes": 1440 + * + * (SUPPORTED ONLY IN PNPM 10.16.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#minimumreleaseage + * + * The default value is 0 (disabled). + */ + // "minimumReleaseAgeMinutes": 1440, + + /** + * An array of package names or patterns to exclude from the minimumReleaseAgeMinutes check. + * This allows certain trusted packages to be installed immediately after publication. + * Patterns are supported using glob syntax (e.g., "@myorg/*" to exclude all packages from an organization). + * + * For example: + * + * "minimumReleaseAgeExclude": ["webpack", "react", "@myorg/*"] + * + * (SUPPORTED ONLY IN PNPM 10.16.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#minimumreleaseageexclude + */ + // "minimumReleaseAgeExclude": ["@myorg/*"], + + /** + * The trust policy controls whether pnpm should block installation of package versions where + * the trust level has decreased (e.g., a package previously published with provenance is now + * published without it). Setting this to `"no-downgrade"` enables the protection. + * + * (SUPPORTED ONLY IN PNPM 10.21.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#trustpolicy + * + * Possible values are: `off` and `no-downgrade`. + * The default is `off`. + */ + // "trustPolicy": "no-downgrade", + + /** + * An array of package names or patterns to exclude from the trust policy check. + * These packages will be allowed to install even if their trust level has decreased. + * Patterns are supported using glob syntax (e.g., "@myorg/*" to exclude all packages + * from an organization). + * + * For example: + * + * "trustPolicyExclude": ["@babel/core@7.28.5", "chokidar@4.0.3", "@myorg/*"] + * + * (SUPPORTED ONLY IN PNPM 10.22.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#trustpolicyexclude + * + * The default value is []. + */ + // "trustPolicyExclude": ["@myorg/*"], + + /** + * The number of minutes after which pnpm will ignore trust level downgrades. Packages + * published longer ago than this threshold will not be blocked even if their trust level + * has decreased. This is useful when enabling strict trust policies, as it allows older versions + * of packages (which may lack a process for publishing with signatures or provenance) to be + * installed without manual exclusion, assuming they are safe due to their age. + * + * For example, the following setting ignores trust level changes for packages published + * more than 14 days ago: + * + * "trustPolicyIgnoreAfterMinutes": 20160 + * + * (SUPPORTED ONLY IN PNPM 10.27.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#trustpolicyignoreafter + * + * The default value is undefined (no exclusion). + */ + // "trustPolicyIgnoreAfterMinutes": 20160, + /** * If true, then Rush will add the `--strict-peer-dependencies` command-line parameter when * invoking PNPM. This causes `rush update` to fail if there are unsatisfied peer dependencies, @@ -76,6 +208,68 @@ */ "preventManualShrinkwrapChanges": true, + /** + * When a project uses `workspace:` to depend on another Rush project, PNPM normally installs + * it by creating a symlink under `node_modules`. This generally works well, but in certain + * cases such as differing `peerDependencies` versions, symlinking may cause trouble + * such as incorrectly satisfied versions. For such cases, the dependency can be declared + * as "injected", causing PNPM to copy its built output into `node_modules` like a real + * install from a registry. Details here: https://rushjs.io/pages/advanced/injected_deps/ + * + * When using Rush subspaces, these sorts of versioning problems are much more likely if + * `workspace:` refers to a project from a different subspace. This is because the symlink + * would point to a separate `node_modules` tree installed by a different PNPM lockfile. + * A comprehensive solution is to enable `alwaysInjectDependenciesFromOtherSubspaces`, + * which automatically treats all projects from other subspaces as injected dependencies + * without having to manually configure them. + * + * NOTE: Use carefully -- excessive file copying can slow down the `rush install` and + * `pnpm-sync` operations if too many dependencies become injected. + * + * The default value is false. + */ + // "alwaysInjectDependenciesFromOtherSubspaces": false, + + /** + * Defines the policies to be checked for the `pnpm-lock.yaml` file. + */ + "pnpmLockfilePolicies": { + /** + * This policy will cause "rush update" to report an error if `pnpm-lock.yaml` contains + * any SHA1 integrity hashes. + * + * For each NPM dependency, `pnpm-lock.yaml` normally stores an `integrity` hash. Although + * its main purpose is to detect corrupted or truncated network requests, this hash can also + * serve as a security fingerprint to protect against attacks that would substitute a + * malicious tarball, for example if a misconfigured .npmrc caused a machine to accidentally + * download a matching package name+version from npmjs.com instead of the private NPM registry. + * NPM originally used a SHA1 hash; this was insecure because an attacker can too easily craft + * a tarball with a matching fingerprint. For this reason, NPM later deprecated SHA1 and + * instead adopted a cryptographically strong SHA512 hash. Nonetheless, SHA1 hashes can + * occasionally reappear during "rush update", for example due to missing metadata fallbacks + * (https://github.com/orgs/pnpm/discussions/6194) or an incompletely migrated private registry. + * The `disallowInsecureSha1` policy prevents this, avoiding potential security/compliance alerts. + */ + // "disallowInsecureSha1": { + // /** + // * Enables the "disallowInsecureSha1" policy. The default value is false. + // */ + // "enabled": true, + // + // /** + // * In rare cases, a private NPM registry may continue to serve SHA1 hashes for very old + // * package versions, perhaps due to a caching issue or database migration glitch. To avoid + // * having to disable the "disallowInsecureSha1" policy for the entire monorepo, the problematic + // * package versions can be individually ignored. The "exemptPackageVersions" key is the + // * package name, and the array value lists exact version numbers to be ignored. + // */ + // "exemptPackageVersions": { + // "example1": ["1.0.0"], + // "example2": ["2.0.0", "2.0.1"] + // } + // } + }, + /** * The "globalOverrides" setting provides a simple mechanism for overriding version selections * for all dependencies of all projects in the monorepo workspace. The settings are copied @@ -91,9 +285,26 @@ "globalOverrides": { // "example1": "^1.0.0", // "example2": "npm:@company/example2@^1.0.0" + // TODO: Remove once https://github.com/dylang/npm-check/issues/499 // has been closed and a new version of `npm-check` is published. - "package-json": "^7" + "package-json": "^7", + + // Force @types/estree to 1.0.8 for webpack 5.103.0 compatibility + "@types/estree": "1.0.8", + + // The React 17 types depend on a specific version of the scheduler types + "@types/react@17.0.74>@types/scheduler": "0.16.8", + + // Newer versions of `cheerio` have a dependency on `undici`, which does not support Node 18. + // Remove when we drop support for Node 18 + "@vscode/vsce>cheerio": "1.0.0-rc.12", + + // `loader-utils@2.0.0` has a vulnerability + "loader-utils@^2.0.0": "2.0.4", + + // `fast-xml-parser@5.3.3` has a vulnerability + "fast-xml-parser@^5.3.3": "5.3.5" }, /** @@ -112,6 +323,11 @@ // "ignoreMissing": ["@eslint/*"], // "allowedVersions": { "react": "17" }, // "allowAny": ["@babel/*"] + // TODO: Remove once Heft is 1.0.0 + "allowAny": ["@rushstack/heft"], + "allowedVersions": { + "webpack": "^4 || ^5" + } }, /** @@ -128,6 +344,19 @@ * PNPM documentation: https://pnpm.io/package_json#pnpmpackageextensions */ "globalPackageExtensions": { + // "fork-ts-checker-webpack-plugin": { + // "dependencies": { + // "@babel/core": "1" + // }, + // "peerDependencies": { + // "eslint": ">= 6" + // }, + // "peerDependenciesMeta": { + // "eslint": { + // "optional": true + // } + // } + // } "@emotion/core": { "peerDependencies": { "@types/react": ">=16" @@ -152,6 +381,24 @@ } }, + "@emotion/utils": { + "dependencies": { + "@emotion/sheet": "^0.9.4" + } + }, + + "@jest/reporters": { + "dependencies": { + "@types/istanbul-lib-coverage": "2.0.4" + } + }, + + "@serverless-stack/resources": { + "dependencies": { + "esbuild": "*" + } + }, + "@storybook/addons": { "peerDependencies": { "@types/react": ">=16" @@ -164,77 +411,103 @@ } }, - "@storybook/router": { + "@storybook/react": { "peerDependencies": { + "@types/node": ">=12", "@types/react": ">=16" } }, - "emotion-theming": { + "@storybook/router": { "peerDependencies": { "@types/react": ">=16" } }, - "react-router": { - "peerDependencies": { - "@types/react": ">=16" + "@storybook/theming": { + "dependencies": { + "@emotion/serialize": "*", + "@emotion/utils": "*" } }, - "react-router-dom": { + "@types/compression": { "peerDependencies": { - "@types/react": ">=16" + "@types/express": "*" } }, - "@jest/reporters": { + "@types/webpack": { "dependencies": { - "@types/istanbul-lib-coverage": "2.0.4" + "anymatch": "^3" } }, - "@serverless-stack/resources": { + // Temporary workaround for https://github.com/typescript-eslint/typescript-eslint/issues/8259 + "@typescript-eslint/rule-tester": { "dependencies": { - "esbuild": "*" + "@types/semver": "*" } }, - "@storybook/react": { + "@typescript-eslint/types": { + "peerDependencies": { + "typescript": "*" + } + }, + + "collect-v8-coverage": { + "peerDependencies": { + "@types/node": ">=12" + } + }, + + "emotion-theming": { "peerDependencies": { - "@types/node": ">=12", "@types/react": ">=16" } }, - "@storybook/theming": { + "http-proxy-middleware": { "dependencies": { - "@emotion/serialize": "*", - "@emotion/utils": "*" + "@types/express": "*" } }, - "@types/webpack": { + "http2-express-bridge": { + "peerDependencies": { + "@types/express": "*" + } + }, + + "query-ast": { "dependencies": { - "anymatch": "^3" + "lodash": "~4.17.15" } }, - "@typescript-eslint/types": { + "react-router": { "peerDependencies": { - "typescript": "*" + "@types/react": ">=16" } }, - "collect-v8-coverage": { + "react-router-dom": { "peerDependencies": { - "@types/node": ">=12" + "@types/react": ">=16" } }, - "http-proxy-middleware": { + "sass-embedded": { "dependencies": { - "@types/express": "*" + // The types reference this package, which is a devDependency + "source-map-js": "^1.0.2" + } + }, + + "scss-parser": { + "dependencies": { + "lodash": "~4.17.15" } }, @@ -263,18 +536,6 @@ "optional": true } } - }, - - "scss-parser": { - "dependencies": { - "lodash": "~4.17.15" - } - }, - - "query-ast": { - "dependencies": { - "lodash": "~4.17.15" - } } }, @@ -290,6 +551,45 @@ * PNPM documentation: https://pnpm.io/package_json#pnpmneverbuiltdependencies */ "globalNeverBuiltDependencies": [ + // The postinstall script redundantly downloads the platform binding, which PNPM installs as an optional dependency. + "unrs-resolver" + ], + + /** + * The `globalOnlyBuiltDependencies` setting specifies which dependencies are permitted to run + * build scripts (`preinstall`, `install`, and `postinstall` lifecycle events). This is the inverse + * of `globalNeverBuiltDependencies`. In PNPM 10.x, build scripts are disabled by default for + * security, so this setting is required to explicitly permit specific packages to run their + * build scripts. The settings are written to the `onlyBuiltDependencies` field of the + * `pnpm-workspace.yaml` file that is generated by Rush during installation. + * + * (SUPPORTED ONLY IN PNPM 10.1.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#onlybuiltdependencies + * + * Example: + * "globalOnlyBuiltDependencies": [ + * "esbuild", + * "playwright", + * "@swc/core" + * ] + */ + // "globalOnlyBuiltDependencies": [ + // "esbuild" + // ], + + /** + * The `globalIgnoredOptionalDependencies` setting suppresses the installation of optional NPM + * dependencies specified in the list. This is useful when certain optional dependencies are + * not needed in your environment, such as platform-specific packages or dependencies that + * fail during installation but are not critical to your project. + * These settings are copied into the `pnpm.overrides` field of the `common/temp/package.json` + * file that is generated by Rush during installation, instructing PNPM to ignore the specified + * optional dependencies. + * + * PNPM documentation: https://pnpm.io/package_json#pnpmignoredoptionaldependencies + */ + "globalIgnoredOptionalDependencies": [ // "fsevents" ], diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml deleted file mode 100644 index ba64c38b17f..00000000000 --- a/common/config/rush/pnpm-lock.yaml +++ /dev/null @@ -1,22850 +0,0 @@ -lockfileVersion: 5.4 - -overrides: - package-json: ^7 - -packageExtensionsChecksum: 41333d2d0915bdc80c909c31c409a3a2 - -importers: - - .: - specifiers: {} - - ../../apps/api-documenter: - specifiers: - '@microsoft/api-extractor-model': workspace:* - '@microsoft/tsdoc': 0.14.2 - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/ts-command-line': workspace:* - '@types/js-yaml': 3.12.1 - '@types/node': 14.18.36 - '@types/resolve': 1.20.2 - colors: ~1.2.1 - js-yaml: ~3.13.1 - resolve: ~1.22.1 - dependencies: - '@microsoft/api-extractor-model': link:../../libraries/api-extractor-model - '@microsoft/tsdoc': 0.14.2 - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/ts-command-line': link:../../libraries/ts-command-line - colors: 1.2.5 - js-yaml: 3.13.1 - resolve: 1.22.1 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/js-yaml': 3.12.1 - '@types/node': 14.18.36 - '@types/resolve': 1.20.2 - - ../../apps/api-extractor: - specifiers: - '@microsoft/api-extractor-model': workspace:* - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': ~0.16.1 - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': 0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@rushstack/node-core-library': workspace:* - '@rushstack/rig-package': workspace:* - '@rushstack/ts-command-line': workspace:* - '@types/heft-jest': 1.0.1 - '@types/lodash': 4.14.116 - '@types/node': 14.18.36 - '@types/resolve': 1.20.2 - '@types/semver': 7.3.5 - colors: ~1.2.1 - lodash: ~4.17.15 - resolve: ~1.22.1 - semver: ~7.3.0 - source-map: ~0.6.1 - typescript: ~5.0.4 - dependencies: - '@microsoft/api-extractor-model': link:../../libraries/api-extractor-model - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/rig-package': link:../../libraries/rig-package - '@rushstack/ts-command-line': link:../../libraries/ts-command-line - colors: 1.2.5 - lodash: 4.17.21 - resolve: 1.22.1 - semver: 7.3.8 - source-map: 0.6.1 - typescript: 5.0.4 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': 0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_yqm7j6zyazsjiac2knmn54orge - '@types/heft-jest': 1.0.1 - '@types/lodash': 4.14.116 - '@types/node': 14.18.36 - '@types/resolve': 1.20.2 - '@types/semver': 7.3.5 - - ../../apps/heft: - specifiers: - '@microsoft/api-extractor': workspace:* - '@nodelib/fs.scandir': 2.1.5 - '@nodelib/fs.stat': 2.0.5 - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': 0.50.6 - '@rushstack/heft-config-file': workspace:* - '@rushstack/heft-node-rig': 1.13.0 - '@rushstack/node-core-library': workspace:* - '@rushstack/rig-package': workspace:* - '@rushstack/ts-command-line': workspace:* - '@types/argparse': 1.0.38 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/tapable': 1.0.6 - '@types/watchpack': 2.4.0 - argparse: ~1.0.9 - chokidar: ~3.4.0 - fast-glob: ~3.2.4 - git-repo-info: ~2.1.0 - ignore: ~5.1.6 - tapable: 1.1.3 - true-case-path: ~2.2.1 - typescript: ~5.0.4 - watchpack: 2.4.0 - dependencies: - '@rushstack/heft-config-file': link:../../libraries/heft-config-file - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/rig-package': link:../../libraries/rig-package - '@rushstack/ts-command-line': link:../../libraries/ts-command-line - '@types/tapable': 1.0.6 - argparse: 1.0.10 - chokidar: 3.4.3 - fast-glob: 3.2.12 - git-repo-info: 2.1.1 - ignore: 5.1.9 - tapable: 1.1.3 - true-case-path: 2.2.1 - watchpack: 2.4.0 - devDependencies: - '@microsoft/api-extractor': link:../api-extractor - '@nodelib/fs.scandir': 2.1.5 - '@nodelib/fs.stat': 2.0.5 - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': 0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_yqm7j6zyazsjiac2knmn54orge - '@types/argparse': 1.0.38 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/watchpack': 2.4.0 - typescript: 5.0.4 - - ../../apps/lockfile-explorer: - specifiers: - '@microsoft/rush-lib': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/lockfile-explorer-web': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/cors': ~2.8.12 - '@types/express': 4.17.13 - '@types/heft-jest': 1.0.1 - '@types/js-yaml': 3.12.1 - '@types/node': 14.18.36 - '@types/update-notifier': ~6.0.1 - colors: ~1.2.1 - cors: ~2.8.5 - express: 4.18.1 - js-yaml: ~3.13.1 - open: ~8.4.0 - update-notifier: ~5.1.0 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@types/express': 4.17.13 - colors: 1.2.5 - cors: 2.8.5 - express: 4.18.1 - js-yaml: 3.13.1 - open: 8.4.2 - update-notifier: 5.1.0 - devDependencies: - '@microsoft/rush-lib': link:../../libraries/rush-lib - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/lockfile-explorer-web': link:../lockfile-explorer-web - '@types/cors': 2.8.13 - '@types/heft-jest': 1.0.1 - '@types/js-yaml': 3.12.1 - '@types/node': 14.18.36 - '@types/update-notifier': 6.0.2 - - ../../apps/lockfile-explorer-web: - specifiers: - '@fluentui/react': ^8.96.1 - '@lifaon/path': ~2.1.0 - '@reduxjs/toolkit': ~1.8.6 - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-web-rig': workspace:* - '@rushstack/rush-themed-ui': workspace:* - '@types/heft-jest': 1.0.1 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - react: ~16.13.1 - react-dom: ~16.13.1 - react-redux: ~8.0.4 - redux: ~4.2.0 - dependencies: - '@fluentui/react': 8.106.9_tlqvpdqnq63ssdllbmshthdmo4 - '@lifaon/path': 2.1.0 - '@reduxjs/toolkit': 1.8.6_qfynotfwlyrsyq662adyrweaoe - '@rushstack/rush-themed-ui': link:../../libraries/rush-themed-ui - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - react-redux: 8.0.5_mq2cyprinb6qi7hdzoedcdddgq - redux: 4.2.1 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../heft - '@rushstack/heft-web-rig': link:../../rigs/heft-web-rig - '@types/heft-jest': 1.0.1 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - - ../../apps/rundown: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/ts-command-line': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - string-argv: ~0.3.1 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/ts-command-line': link:../../libraries/ts-command-line - string-argv: 0.3.1 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - - ../../apps/rush: - specifiers: - '@microsoft/rush-lib': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/rush-amazon-s3-build-cache-plugin': workspace:* - '@rushstack/rush-azure-storage-build-cache-plugin': workspace:* - '@rushstack/rush-http-build-cache-plugin': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/semver': 7.3.5 - colors: ~1.2.1 - semver: ~7.3.0 - dependencies: - '@microsoft/rush-lib': link:../../libraries/rush-lib - '@rushstack/node-core-library': link:../../libraries/node-core-library - colors: 1.2.5 - semver: 7.3.8 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/rush-amazon-s3-build-cache-plugin': link:../../rush-plugins/rush-amazon-s3-build-cache-plugin - '@rushstack/rush-azure-storage-build-cache-plugin': link:../../rush-plugins/rush-azure-storage-build-cache-plugin - '@rushstack/rush-http-build-cache-plugin': link:../../rush-plugins/rush-http-build-cache-plugin - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/semver': 7.3.5 - - ../../apps/trace-import: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/ts-command-line': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/resolve': 1.20.2 - '@types/semver': 7.3.5 - colors: ~1.2.1 - resolve: ~1.22.1 - semver: ~7.3.0 - typescript: ~5.0.4 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/ts-command-line': link:../../libraries/ts-command-line - colors: 1.2.5 - resolve: 1.22.1 - semver: 7.3.8 - typescript: 5.0.4 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/resolve': 1.20.2 - '@types/semver': 7.3.5 - - ../../build-tests-samples/heft-node-basic-tutorial: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: ~8.7.0 - typescript: ~5.0.4 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: 8.7.0 - typescript: 5.0.4 - - ../../build-tests-samples/heft-node-jest-tutorial: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: ~8.7.0 - typescript: ~5.0.4 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: 8.7.0 - typescript: 5.0.4 - - ../../build-tests-samples/heft-node-rig-tutorial: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - - ../../build-tests-samples/heft-serverless-stack-tutorial: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-serverless-stack-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@serverless-stack/aws-lambda-ric': ^2.0.12 - '@serverless-stack/cli': 0.67.0 - '@serverless-stack/resources': 0.67.0 - '@types/aws-lambda': 8.10.93 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - aws-cdk-lib: 2.7.0 - constructs: ~10.0.98 - eslint: ~8.7.0 - typescript: ~5.0.4 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-serverless-stack-plugin': link:../../heft-plugins/heft-serverless-stack-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@serverless-stack/aws-lambda-ric': 2.0.13 - '@serverless-stack/cli': 0.67.0_constructs@10.0.130 - '@serverless-stack/resources': 0.67.0 - '@types/aws-lambda': 8.10.93 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - aws-cdk-lib: 2.7.0_constructs@10.0.130 - constructs: 10.0.130 - eslint: 8.7.0 - typescript: 5.0.4 - - ../../build-tests-samples/heft-storybook-react-tutorial: - specifiers: - '@babel/core': ~7.20.0 - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-storybook-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@rushstack/heft-webpack4-plugin': workspace:* - '@storybook/react': ~6.4.18 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - css-loader: ~5.2.7 - eslint: ~8.7.0 - heft-storybook-react-tutorial-storykit: workspace:* - html-webpack-plugin: ~4.5.2 - react: ~16.13.1 - react-dom: ~16.13.1 - source-map-loader: ~1.1.3 - style-loader: ~2.0.0 - tslib: ~2.3.1 - typescript: ~5.0.4 - webpack: ~4.44.2 - dependencies: - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - tslib: 2.3.1 - devDependencies: - '@babel/core': 7.20.12 - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-storybook-plugin': link:../../heft-plugins/heft-storybook-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@rushstack/heft-webpack4-plugin': link:../../heft-plugins/heft-webpack4-plugin - '@storybook/react': 6.4.22_k3pzkoeaevtikrhe3xivvmfzgq - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - css-loader: 5.2.7_webpack@4.44.2 - eslint: 8.7.0 - heft-storybook-react-tutorial-storykit: link:../heft-storybook-react-tutorial-storykit - html-webpack-plugin: 4.5.2_webpack@4.44.2 - source-map-loader: 1.1.3_webpack@4.44.2 - style-loader: 2.0.0_webpack@4.44.2 - typescript: 5.0.4 - webpack: 4.44.2 - - ../../build-tests-samples/heft-storybook-react-tutorial-storykit: - specifiers: - '@babel/core': ~7.20.0 - '@storybook/addon-actions': ~6.4.18 - '@storybook/addon-essentials': ~6.4.18 - '@storybook/addon-links': ~6.4.18 - '@storybook/cli': ~6.4.18 - '@storybook/components': ~6.4.18 - '@storybook/core-events': ~6.4.18 - '@storybook/react': ~6.4.18 - '@storybook/theming': ~6.4.18 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - babel-loader: ~8.2.3 - css-loader: ~5.2.7 - jest: ~29.3.1 - react: ~16.13.1 - react-dom: ~16.13.1 - style-loader: ~2.0.0 - terser-webpack-plugin: ~3.0.8 - typescript: ~5.0.4 - webpack: ~4.44.2 - devDependencies: - '@babel/core': 7.20.12 - '@storybook/addon-actions': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/addon-essentials': 6.4.22_lwdgx3dbz5iyny2jmkfmtofory - '@storybook/addon-links': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/cli': 6.4.22_d66pwvnsbzorazhlf35vb7reqi - '@storybook/components': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core-events': 6.4.22 - '@storybook/react': 6.4.22_chiz7t5wmiss5c6b3yjvxsi5xy - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - babel-loader: 8.2.5_tb555f6titdaodihyrbadfrjbq - css-loader: 5.2.7_webpack@4.44.2 - jest: 29.3.1_@types+node@14.18.36 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - style-loader: 2.0.0_webpack@4.44.2 - terser-webpack-plugin: 3.0.8_webpack@4.44.2 - typescript: 5.0.4 - webpack: 4.44.2 - - ../../build-tests-samples/heft-web-rig-app-tutorial: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-web-rig': workspace:* - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - heft-web-rig-library-tutorial: workspace:* - react: ~16.13.1 - react-dom: ~16.13.1 - tslib: ~2.3.1 - typescript: ~5.0.4 - dependencies: - heft-web-rig-library-tutorial: link:../heft-web-rig-library-tutorial - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - tslib: 2.3.1 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-web-rig': link:../../rigs/heft-web-rig - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - typescript: 5.0.4 - - ../../build-tests-samples/heft-web-rig-library-tutorial: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-web-rig': workspace:* - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - react: ~16.13.1 - react-dom: ~16.13.1 - tslib: ~2.3.1 - typescript: ~5.0.4 - dependencies: - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - tslib: 2.3.1 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-web-rig': link:../../rigs/heft-web-rig - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - typescript: 5.0.4 - - ../../build-tests-samples/heft-webpack-basic-tutorial: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@rushstack/heft-webpack5-plugin': workspace:* - '@types/heft-jest': 1.0.1 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - css-loader: ~6.6.0 - eslint: ~8.7.0 - html-webpack-plugin: ~5.5.0 - react: ~16.13.1 - react-dom: ~16.13.1 - source-map-loader: ~3.0.1 - style-loader: ~3.3.1 - tslib: ~2.3.1 - typescript: ~5.0.4 - webpack: ~5.80.0 - dependencies: - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - tslib: 2.3.1 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@rushstack/heft-webpack5-plugin': link:../../heft-plugins/heft-webpack5-plugin - '@types/heft-jest': 1.0.1 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - css-loader: 6.6.0_webpack@5.80.0 - eslint: 8.7.0 - html-webpack-plugin: 5.5.0_webpack@5.80.0 - source-map-loader: 3.0.2_webpack@5.80.0 - style-loader: 3.3.2_webpack@5.80.0 - typescript: 5.0.4 - webpack: 5.80.0 - - ../../build-tests-samples/packlets-tutorial: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/node': 14.18.36 - eslint: ~8.7.0 - typescript: ~5.0.4 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/node': 14.18.36 - eslint: 8.7.0 - typescript: 5.0.4 - - ../../build-tests/api-documenter-scenarios: - specifiers: - '@microsoft/api-documenter': workspace:* - '@microsoft/api-extractor': workspace:* - '@microsoft/teams-js': 1.3.0-beta.4 - '@rushstack/node-core-library': workspace:* - '@types/jest': 29.2.5 - '@types/node': 14.18.36 - fs-extra: ~7.0.1 - typescript: ~5.0.4 - devDependencies: - '@microsoft/api-documenter': link:../../apps/api-documenter - '@microsoft/api-extractor': link:../../apps/api-extractor - '@microsoft/teams-js': 1.3.0-beta.4 - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@types/jest': 29.2.5 - '@types/node': 14.18.36 - fs-extra: 7.0.1 - typescript: 5.0.4 - - ../../build-tests/api-documenter-test: - specifiers: - '@microsoft/api-documenter': workspace:* - '@microsoft/api-extractor': workspace:* - '@types/jest': 29.2.5 - '@types/node': 14.18.36 - fs-extra: ~7.0.1 - typescript: ~5.0.4 - devDependencies: - '@microsoft/api-documenter': link:../../apps/api-documenter - '@microsoft/api-extractor': link:../../apps/api-extractor - '@types/jest': 29.2.5 - '@types/node': 14.18.36 - fs-extra: 7.0.1 - typescript: 5.0.4 - - ../../build-tests/api-extractor-lib1-test: - specifiers: - '@microsoft/api-extractor': workspace:* - fs-extra: ~7.0.1 - typescript: ~2.9.2 - devDependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - fs-extra: 7.0.1 - typescript: 2.9.2 - - ../../build-tests/api-extractor-lib2-test: - specifiers: - '@microsoft/api-extractor': workspace:* - '@types/jest': 29.2.5 - '@types/node': 14.18.36 - fs-extra: ~7.0.1 - typescript: ~5.0.4 - devDependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@types/jest': 29.2.5 - '@types/node': 14.18.36 - fs-extra: 7.0.1 - typescript: 5.0.4 - - ../../build-tests/api-extractor-lib3-test: - specifiers: - '@microsoft/api-extractor': workspace:* - '@types/jest': 29.2.5 - '@types/node': 14.18.36 - api-extractor-lib1-test: workspace:* - fs-extra: ~7.0.1 - typescript: ~5.0.4 - dependencies: - api-extractor-lib1-test: link:../api-extractor-lib1-test - devDependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@types/jest': 29.2.5 - '@types/node': 14.18.36 - fs-extra: 7.0.1 - typescript: 5.0.4 - - ../../build-tests/api-extractor-scenarios: - specifiers: - '@microsoft/api-extractor': workspace:* - '@microsoft/teams-js': 1.3.0-beta.4 - '@rushstack/node-core-library': workspace:* - '@types/jest': 29.2.5 - '@types/node': 14.18.36 - api-extractor-lib1-test: workspace:* - api-extractor-lib2-test: workspace:* - api-extractor-lib3-test: workspace:* - colors: ~1.2.1 - fs-extra: ~7.0.1 - typescript: ~5.0.4 - devDependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@microsoft/teams-js': 1.3.0-beta.4 - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@types/jest': 29.2.5 - '@types/node': 14.18.36 - api-extractor-lib1-test: link:../api-extractor-lib1-test - api-extractor-lib2-test: link:../api-extractor-lib2-test - api-extractor-lib3-test: link:../api-extractor-lib3-test - colors: 1.2.5 - fs-extra: 7.0.1 - typescript: 5.0.4 - - ../../build-tests/api-extractor-test-01: - specifiers: - '@microsoft/api-extractor': workspace:* - '@types/heft-jest': 1.0.1 - '@types/jest': 29.2.5 - '@types/long': 4.0.0 - '@types/node': 14.18.36 - fs-extra: ~7.0.1 - long: ^4.0.0 - typescript: ~5.0.4 - dependencies: - '@types/jest': 29.2.5 - '@types/long': 4.0.0 - long: 4.0.0 - devDependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - fs-extra: 7.0.1 - typescript: 5.0.4 - - ../../build-tests/api-extractor-test-02: - specifiers: - '@microsoft/api-extractor': workspace:* - '@types/node': 14.18.36 - '@types/semver': 7.3.5 - api-extractor-test-01: workspace:* - fs-extra: ~7.0.1 - semver: ~7.3.0 - typescript: ~5.0.4 - dependencies: - '@types/semver': 7.3.5 - api-extractor-test-01: link:../api-extractor-test-01 - semver: 7.3.8 - devDependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@types/node': 14.18.36 - fs-extra: 7.0.1 - typescript: 5.0.4 - - ../../build-tests/api-extractor-test-03: - specifiers: - '@types/jest': 29.2.5 - '@types/node': 14.18.36 - api-extractor-test-02: workspace:* - fs-extra: ~7.0.1 - typescript: ~5.0.4 - devDependencies: - '@types/jest': 29.2.5 - '@types/node': 14.18.36 - api-extractor-test-02: link:../api-extractor-test-02 - fs-extra: 7.0.1 - typescript: 5.0.4 - - ../../build-tests/api-extractor-test-04: - specifiers: - '@microsoft/api-extractor': workspace:* - api-extractor-lib1-test: workspace:* - fs-extra: ~7.0.1 - typescript: ~5.0.4 - dependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - api-extractor-lib1-test: link:../api-extractor-lib1-test - fs-extra: 7.0.1 - typescript: 5.0.4 - - ../../build-tests/eslint-7-test: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@types/node': 14.18.36 - '@typescript-eslint/parser': ~5.59.2 - eslint: ~7.30.0 - typescript: ~5.0.4 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/node': 14.18.36 - '@typescript-eslint/parser': 5.59.7_2aulnmwxyjhjxqmg3aruit533m - eslint: 7.30.0 - typescript: 5.0.4 - - ../../build-tests/hashed-folder-copy-plugin-webpack4-test: - specifiers: - '@rushstack/hashed-folder-copy-plugin': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@rushstack/heft-webpack4-plugin': workspace:* - '@rushstack/set-webpack-public-path-plugin': workspace:* - '@rushstack/webpack4-module-minifier-plugin': workspace:* - '@types/webpack-env': 1.18.0 - html-webpack-plugin: ~4.5.2 - typescript: ~5.0.4 - webpack: ~4.44.2 - webpack-bundle-analyzer: ~4.5.0 - devDependencies: - '@rushstack/hashed-folder-copy-plugin': link:../../webpack/hashed-folder-copy-plugin - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@rushstack/heft-webpack4-plugin': link:../../heft-plugins/heft-webpack4-plugin - '@rushstack/set-webpack-public-path-plugin': link:../../webpack/set-webpack-public-path-plugin - '@rushstack/webpack4-module-minifier-plugin': link:../../webpack/webpack4-module-minifier-plugin - '@types/webpack-env': 1.18.0 - html-webpack-plugin: 4.5.2_webpack@4.44.2 - typescript: 5.0.4 - webpack: 4.44.2 - webpack-bundle-analyzer: 4.5.0 - - ../../build-tests/hashed-folder-copy-plugin-webpack5-test: - specifiers: - '@rushstack/hashed-folder-copy-plugin': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@rushstack/heft-webpack5-plugin': workspace:* - '@types/webpack-env': 1.18.0 - html-webpack-plugin: ~4.5.2 - typescript: ~5.0.4 - webpack: ~5.80.0 - webpack-bundle-analyzer: ~4.5.0 - devDependencies: - '@rushstack/hashed-folder-copy-plugin': link:../../webpack/hashed-folder-copy-plugin - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@rushstack/heft-webpack5-plugin': link:../../heft-plugins/heft-webpack5-plugin - '@types/webpack-env': 1.18.0 - html-webpack-plugin: 4.5.2_webpack@5.80.0 - typescript: 5.0.4 - webpack: 5.80.0 - webpack-bundle-analyzer: 4.5.0 - - ../../build-tests/heft-copy-files-test: - specifiers: - '@rushstack/heft': workspace:* - devDependencies: - '@rushstack/heft': link:../../apps/heft - - ../../build-tests/heft-example-plugin-01: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/node': 14.18.36 - '@types/tapable': 1.0.6 - eslint: ~8.7.0 - tapable: 1.1.3 - typescript: ~5.0.4 - dependencies: - tapable: 1.1.3 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/node': 14.18.36 - '@types/tapable': 1.0.6 - eslint: 8.7.0 - typescript: 5.0.4 - - ../../build-tests/heft-example-plugin-02: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/node': 14.18.36 - eslint: ~8.7.0 - heft-example-plugin-01: workspace:* - typescript: ~5.0.4 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/node': 14.18.36 - eslint: 8.7.0 - heft-example-plugin-01: link:../heft-example-plugin-01 - typescript: 5.0.4 - - ../../build-tests/heft-fastify-test: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: ~8.7.0 - fastify: ~3.16.1 - typescript: ~5.0.4 - dependencies: - fastify: 3.16.2 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: 8.7.0 - typescript: 5.0.4 - - ../../build-tests/heft-jest-preset-test: - specifiers: - '@jest/types': 29.5.0 - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/heft-jest': 1.0.1 - eslint: ~8.7.0 - typescript: ~5.0.4 - devDependencies: - '@jest/types': 29.5.0 - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/heft-jest': 1.0.1 - eslint: 8.7.0 - typescript: 5.0.4 - - ../../build-tests/heft-jest-reporters-test: - specifiers: - '@jest/reporters': ~29.5.0 - '@jest/types': 29.5.0 - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/heft-jest': 1.0.1 - eslint: ~8.7.0 - typescript: ~5.0.4 - devDependencies: - '@jest/reporters': 29.5.0 - '@jest/types': 29.5.0 - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/heft-jest': 1.0.1 - eslint: 8.7.0 - typescript: 5.0.4 - - ../../build-tests/heft-minimal-rig-test: - specifiers: - '@microsoft/api-extractor': workspace:* - '@rushstack/heft-api-extractor-plugin': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - typescript: ~5.0.4 - dependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@rushstack/heft-api-extractor-plugin': link:../../heft-plugins/heft-api-extractor-plugin - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - typescript: 5.0.4 - - ../../build-tests/heft-minimal-rig-usage-test: - specifiers: - '@rushstack/heft': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - heft-minimal-rig-test: workspace:* - devDependencies: - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - heft-minimal-rig-test: link:../heft-minimal-rig-test - - ../../build-tests/heft-node-everything-esm-module-test: - specifiers: - '@microsoft/api-extractor': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-api-extractor-plugin': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: ~8.7.0 - heft-example-plugin-01: workspace:* - heft-example-plugin-02: workspace:* - tslint: ~5.20.1 - tslint-microsoft-contrib: ~6.2.0 - typescript: ~5.0.4 - devDependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-api-extractor-plugin': link:../../heft-plugins/heft-api-extractor-plugin - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: 8.7.0 - heft-example-plugin-01: link:../heft-example-plugin-01 - heft-example-plugin-02: link:../heft-example-plugin-02 - tslint: 5.20.1_typescript@5.0.4 - tslint-microsoft-contrib: 6.2.0_iya4g6zcyztd4u7rvedwwipq6a - typescript: 5.0.4 - - ../../build-tests/heft-node-everything-test: - specifiers: - '@microsoft/api-extractor': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-api-extractor-plugin': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: ~8.7.0 - heft-example-plugin-01: workspace:* - heft-example-plugin-02: workspace:* - tslint: ~5.20.1 - tslint-microsoft-contrib: ~6.2.0 - typescript: ~5.0.4 - devDependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-api-extractor-plugin': link:../../heft-plugins/heft-api-extractor-plugin - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: 8.7.0 - heft-example-plugin-01: link:../heft-example-plugin-01 - heft-example-plugin-02: link:../heft-example-plugin-02 - tslint: 5.20.1_typescript@5.0.4 - tslint-microsoft-contrib: 6.2.0_iya4g6zcyztd4u7rvedwwipq6a - typescript: 5.0.4 - - ../../build-tests/heft-parameter-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/node': 14.18.36 - eslint: ~8.7.0 - typescript: ~5.0.4 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/node': 14.18.36 - eslint: 8.7.0 - typescript: 5.0.4 - - ../../build-tests/heft-parameter-plugin-test: - specifiers: - '@rushstack/heft': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/heft-jest': 1.0.1 - heft-parameter-plugin: workspace:* - typescript: ~5.0.4 - devDependencies: - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@types/heft-jest': 1.0.1 - heft-parameter-plugin: link:../heft-parameter-plugin - typescript: 5.0.4 - - ../../build-tests/heft-sass-test: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-sass-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@rushstack/heft-webpack4-plugin': workspace:* - '@types/heft-jest': 1.0.1 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - autoprefixer: ~10.4.2 - buttono: ~1.0.2 - css-loader: ~5.2.7 - eslint: ~8.7.0 - html-webpack-plugin: ~4.5.2 - postcss: ~8.4.6 - postcss-loader: ~4.1.0 - react: ~16.13.1 - react-dom: ~16.13.1 - sass: ~1.3.0 - sass-loader: ~10.0.0 - style-loader: ~2.0.0 - typescript: ~5.0.4 - webpack: ~4.44.2 - dependencies: - buttono: 1.0.4 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-sass-plugin': link:../../heft-plugins/heft-sass-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@rushstack/heft-webpack4-plugin': link:../../heft-plugins/heft-webpack4-plugin - '@types/heft-jest': 1.0.1 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - autoprefixer: 10.4.14_postcss@8.4.21 - css-loader: 5.2.7_webpack@4.44.2 - eslint: 8.7.0 - html-webpack-plugin: 4.5.2_webpack@4.44.2 - postcss: 8.4.21 - postcss-loader: 4.1.0_q6bo6nn7or7rkhjb274oworunu - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - sass: 1.3.2 - sass-loader: 10.0.5_sass@1.3.2+webpack@4.44.2 - style-loader: 2.0.0_webpack@4.44.2 - typescript: 5.0.4 - webpack: 4.44.2 - - ../../build-tests/heft-typescript-composite-test: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/heft-jest': 1.0.1 - '@types/jest': 29.2.5 - '@types/webpack-env': 1.18.0 - eslint: ~8.7.0 - tslint: ~5.20.1 - tslint-microsoft-contrib: ~6.2.0 - typescript: ~5.0.4 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/heft-jest': 1.0.1 - '@types/jest': 29.2.5 - '@types/webpack-env': 1.18.0 - eslint: 8.7.0 - tslint: 5.20.1_typescript@5.0.4 - tslint-microsoft-contrib: 6.2.0_iya4g6zcyztd4u7rvedwwipq6a - typescript: 5.0.4 - - ../../build-tests/heft-typescript-v2-test: - specifiers: - '@microsoft/api-extractor': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-api-extractor-plugin': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/jest': ts2.9 - '@types/node': ts2.9 - tslint: ~5.20.1 - tslint-microsoft-contrib: ~6.2.0 - typescript: ~2.9.2 - devDependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-api-extractor-plugin': link:../../heft-plugins/heft-api-extractor-plugin - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/jest': 23.3.13 - '@types/node': 14.0.1 - tslint: 5.20.1_typescript@2.9.2 - tslint-microsoft-contrib: 6.2.0_ew7ikuw7vzbxz2yx5mufkmltai - typescript: 2.9.2 - - ../../build-tests/heft-typescript-v3-test: - specifiers: - '@microsoft/api-extractor': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-api-extractor-plugin': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/jest': ts3.9 - '@types/node': ts3.9 - tslint: ~5.20.1 - tslint-microsoft-contrib: ~6.2.0 - typescript: ~3.9.10 - devDependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-api-extractor-plugin': link:../../heft-plugins/heft-api-extractor-plugin - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/jest': 28.1.1 - '@types/node': 17.0.41 - tslint: 5.20.1_typescript@3.9.10 - tslint-microsoft-contrib: 6.2.0_67neen4t5xfhpau25dyc5p2yey - typescript: 3.9.10 - - ../../build-tests/heft-typescript-v4-test: - specifiers: - '@microsoft/api-extractor': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-api-extractor-plugin': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/jest': ts4.9 - '@types/node': ts4.9 - eslint: ~8.7.0 - tslint: ~5.20.1 - tslint-microsoft-contrib: ~6.2.0 - typescript: ~4.9.5 - devDependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-api-extractor-plugin': link:../../heft-plugins/heft-api-extractor-plugin - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/jest': 29.5.2 - '@types/node': 20.2.5 - eslint: 8.7.0 - tslint: 5.20.1_typescript@4.9.5 - tslint-microsoft-contrib: 6.2.0_uwqr5pcif4g7c56scrk6kqzf7i - typescript: 4.9.5 - - ../../build-tests/heft-web-rig-library-test: - specifiers: - '@rushstack/heft': workspace:* - '@rushstack/heft-web-rig': workspace:* - devDependencies: - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-web-rig': link:../../rigs/heft-web-rig - - ../../build-tests/heft-webpack4-everything-test: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-dev-cert-plugin': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@rushstack/heft-webpack4-plugin': workspace:* - '@types/heft-jest': 1.0.1 - '@types/webpack-env': 1.18.0 - eslint: ~8.7.0 - file-loader: ~6.0.0 - tslint: ~5.20.1 - tslint-microsoft-contrib: ~6.2.0 - typescript: ~5.0.4 - webpack: ~4.44.2 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-dev-cert-plugin': link:../../heft-plugins/heft-dev-cert-plugin - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@rushstack/heft-webpack4-plugin': link:../../heft-plugins/heft-webpack4-plugin - '@types/heft-jest': 1.0.1 - '@types/webpack-env': 1.18.0 - eslint: 8.7.0 - file-loader: 6.0.0_webpack@4.44.2 - tslint: 5.20.1_typescript@5.0.4 - tslint-microsoft-contrib: 6.2.0_iya4g6zcyztd4u7rvedwwipq6a - typescript: 5.0.4 - webpack: 4.44.2 - - ../../build-tests/heft-webpack5-everything-test: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-dev-cert-plugin': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@rushstack/heft-webpack5-plugin': workspace:* - '@rushstack/module-minifier': workspace:* - '@rushstack/webpack5-module-minifier-plugin': workspace:* - '@types/heft-jest': 1.0.1 - '@types/webpack-env': 1.18.0 - eslint: ~8.7.0 - html-webpack-plugin: ~5.5.0 - tslint: ~5.20.1 - tslint-microsoft-contrib: ~6.2.0 - typescript: ~5.0.4 - webpack: ~5.80.0 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-dev-cert-plugin': link:../../heft-plugins/heft-dev-cert-plugin - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@rushstack/heft-webpack5-plugin': link:../../heft-plugins/heft-webpack5-plugin - '@rushstack/module-minifier': link:../../libraries/module-minifier - '@rushstack/webpack5-module-minifier-plugin': link:../../webpack/webpack5-module-minifier-plugin - '@types/heft-jest': 1.0.1 - '@types/webpack-env': 1.18.0 - eslint: 8.7.0 - html-webpack-plugin: 5.5.0_webpack@5.80.0 - tslint: 5.20.1_typescript@5.0.4 - tslint-microsoft-contrib: 6.2.0_iya4g6zcyztd4u7rvedwwipq6a - typescript: 5.0.4 - webpack: 5.80.0 - - ../../build-tests/install-test-workspace: - specifiers: - '@microsoft/rush-lib': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/rush-sdk': workspace:* - devDependencies: - '@microsoft/rush-lib': link:../../libraries/rush-lib - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/rush-sdk': link:../../libraries/rush-sdk - - ../../build-tests/localization-plugin-test-01: - specifiers: - '@rushstack/node-core-library': workspace:* - '@rushstack/set-webpack-public-path-plugin': workspace:* - '@rushstack/webpack4-localization-plugin': workspace:* - '@rushstack/webpack4-module-minifier-plugin': workspace:* - '@types/webpack-env': 1.18.0 - html-webpack-plugin: ~4.5.2 - ts-loader: 6.0.0 - typescript: ~5.0.4 - webpack: ~4.44.2 - webpack-bundle-analyzer: ~4.5.0 - webpack-cli: ~3.3.2 - webpack-dev-server: ~4.9.3 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/set-webpack-public-path-plugin': link:../../webpack/set-webpack-public-path-plugin - '@rushstack/webpack4-localization-plugin': link:../../webpack/webpack4-localization-plugin - '@rushstack/webpack4-module-minifier-plugin': link:../../webpack/webpack4-module-minifier-plugin - '@types/webpack-env': 1.18.0 - html-webpack-plugin: 4.5.2_webpack@4.44.2 - ts-loader: 6.0.0_typescript@5.0.4 - typescript: 5.0.4 - webpack: 4.44.2_webpack-cli@3.3.12 - webpack-bundle-analyzer: 4.5.0 - webpack-cli: 3.3.12_webpack@4.44.2 - webpack-dev-server: 4.9.3_spfcq5ngldu5cvjikbre424ry4 - - ../../build-tests/localization-plugin-test-02: - specifiers: - '@rushstack/node-core-library': workspace:* - '@rushstack/set-webpack-public-path-plugin': workspace:* - '@rushstack/webpack4-localization-plugin': workspace:* - '@rushstack/webpack4-module-minifier-plugin': workspace:* - '@types/lodash': 4.14.116 - '@types/webpack-env': 1.18.0 - html-webpack-plugin: ~4.5.2 - lodash: ~4.17.15 - ts-loader: 6.0.0 - typescript: ~5.0.4 - webpack: ~4.44.2 - webpack-bundle-analyzer: ~4.5.0 - webpack-cli: ~3.3.2 - webpack-dev-server: ~4.9.3 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/set-webpack-public-path-plugin': link:../../webpack/set-webpack-public-path-plugin - '@rushstack/webpack4-localization-plugin': link:../../webpack/webpack4-localization-plugin - '@rushstack/webpack4-module-minifier-plugin': link:../../webpack/webpack4-module-minifier-plugin - '@types/lodash': 4.14.116 - '@types/webpack-env': 1.18.0 - html-webpack-plugin: 4.5.2_webpack@4.44.2 - lodash: 4.17.21 - ts-loader: 6.0.0_typescript@5.0.4 - typescript: 5.0.4 - webpack: 4.44.2_webpack-cli@3.3.12 - webpack-bundle-analyzer: 4.5.0 - webpack-cli: 3.3.12_webpack@4.44.2 - webpack-dev-server: 4.9.3_spfcq5ngldu5cvjikbre424ry4 - - ../../build-tests/localization-plugin-test-03: - specifiers: - '@rushstack/node-core-library': workspace:* - '@rushstack/set-webpack-public-path-plugin': workspace:* - '@rushstack/webpack4-localization-plugin': workspace:* - '@types/webpack-env': 1.18.0 - html-webpack-plugin: ~4.5.2 - ts-loader: 6.0.0 - typescript: ~5.0.4 - webpack: ~4.44.2 - webpack-bundle-analyzer: ~4.5.0 - webpack-cli: ~3.3.2 - webpack-dev-server: ~4.9.3 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/set-webpack-public-path-plugin': link:../../webpack/set-webpack-public-path-plugin - '@rushstack/webpack4-localization-plugin': link:../../webpack/webpack4-localization-plugin - '@types/webpack-env': 1.18.0 - html-webpack-plugin: 4.5.2_webpack@4.44.2 - ts-loader: 6.0.0_typescript@5.0.4 - typescript: 5.0.4 - webpack: 4.44.2_webpack-cli@3.3.12 - webpack-bundle-analyzer: 4.5.0 - webpack-cli: 3.3.12_webpack@4.44.2 - webpack-dev-server: 4.9.3_spfcq5ngldu5cvjikbre424ry4 - - ../../build-tests/rush-amazon-s3-build-cache-plugin-integration-test: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/rush-amazon-s3-build-cache-plugin': workspace:* - '@types/http-proxy': ~1.17.8 - '@types/node': 14.18.36 - eslint: ~8.7.0 - http-proxy: ~1.18.1 - typescript: ~5.0.4 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/rush-amazon-s3-build-cache-plugin': link:../../rush-plugins/rush-amazon-s3-build-cache-plugin - '@types/http-proxy': 1.17.10 - '@types/node': 14.18.36 - eslint: 8.7.0 - http-proxy: 1.18.1 - typescript: 5.0.4 - - ../../build-tests/rush-lib-declaration-paths-test: - specifiers: - '@microsoft/rush-lib': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/node': 14.18.36 - dependencies: - '@microsoft/rush-lib': link:../../libraries/rush-lib - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@types/node': 14.18.36 - - ../../build-tests/rush-project-change-analyzer-test: - specifiers: - '@microsoft/rush-lib': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/node': 14.18.36 - dependencies: - '@microsoft/rush-lib': link:../../libraries/rush-lib - '@rushstack/node-core-library': link:../../libraries/node-core-library - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/node': 14.18.36 - - ../../build-tests/set-webpack-public-path-plugin-webpack4-test: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@rushstack/heft-webpack4-plugin': workspace:* - '@rushstack/set-webpack-public-path-plugin': workspace:* - '@types/webpack-env': 1.18.0 - eslint: ~8.7.0 - html-webpack-plugin: ~4.5.2 - typescript: ~5.0.4 - webpack: ~4.44.2 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@rushstack/heft-webpack4-plugin': link:../../heft-plugins/heft-webpack4-plugin - '@rushstack/set-webpack-public-path-plugin': link:../../webpack/set-webpack-public-path-plugin - '@types/webpack-env': 1.18.0 - eslint: 8.7.0 - html-webpack-plugin: 4.5.2_webpack@4.44.2 - typescript: 5.0.4 - webpack: 4.44.2 - - ../../build-tests/ts-command-line-test: - specifiers: - '@rushstack/ts-command-line': workspace:* - '@types/node': 14.18.36 - fs-extra: ~7.0.1 - typescript: ~5.0.4 - devDependencies: - '@rushstack/ts-command-line': link:../../libraries/ts-command-line - '@types/node': 14.18.36 - fs-extra: 7.0.1 - typescript: 5.0.4 - - ../../eslint/eslint-config: - specifiers: - '@rushstack/eslint-patch': workspace:* - '@rushstack/eslint-plugin': workspace:* - '@rushstack/eslint-plugin-packlets': workspace:* - '@rushstack/eslint-plugin-security': workspace:* - '@typescript-eslint/eslint-plugin': ~5.59.2 - '@typescript-eslint/experimental-utils': ~5.59.2 - '@typescript-eslint/parser': ~5.59.2 - '@typescript-eslint/typescript-estree': ~5.59.2 - eslint: ~8.7.0 - eslint-plugin-promise: ~6.0.0 - eslint-plugin-react: ~7.27.1 - eslint-plugin-tsdoc: ~0.2.16 - typescript: ~5.0.4 - dependencies: - '@rushstack/eslint-patch': link:../eslint-patch - '@rushstack/eslint-plugin': link:../eslint-plugin - '@rushstack/eslint-plugin-packlets': link:../eslint-plugin-packlets - '@rushstack/eslint-plugin-security': link:../eslint-plugin-security - '@typescript-eslint/eslint-plugin': 5.59.7_7yosyjls7ieoemdl24ktrlsrzm - '@typescript-eslint/experimental-utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - '@typescript-eslint/parser': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - eslint-plugin-promise: 6.0.1_eslint@8.7.0 - eslint-plugin-react: 7.27.1_eslint@8.7.0 - eslint-plugin-tsdoc: 0.2.17 - devDependencies: - eslint: 8.7.0 - typescript: 5.0.4 - - ../../eslint/eslint-patch: - specifiers: - '@rushstack/heft': 0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@types/node': 14.18.36 - devDependencies: - '@rushstack/heft': 0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_yqm7j6zyazsjiac2knmn54orge - '@types/node': 14.18.36 - - ../../eslint/eslint-plugin: - specifiers: - '@rushstack/heft': 0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@rushstack/tree-pattern': workspace:* - '@types/eslint': 8.2.0 - '@types/estree': 0.0.50 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@typescript-eslint/experimental-utils': ~5.59.2 - '@typescript-eslint/parser': ~5.59.2 - '@typescript-eslint/typescript-estree': ~5.59.2 - eslint: ~8.7.0 - typescript: ~5.0.4 - dependencies: - '@rushstack/tree-pattern': link:../../libraries/tree-pattern - '@typescript-eslint/experimental-utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - devDependencies: - '@rushstack/heft': 0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_yqm7j6zyazsjiac2knmn54orge - '@types/eslint': 8.2.0 - '@types/estree': 0.0.50 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@typescript-eslint/parser': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - eslint: 8.7.0 - typescript: 5.0.4 - - ../../eslint/eslint-plugin-packlets: - specifiers: - '@rushstack/heft': 0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@rushstack/tree-pattern': workspace:* - '@types/eslint': 8.2.0 - '@types/estree': 0.0.50 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@typescript-eslint/experimental-utils': ~5.59.2 - '@typescript-eslint/parser': ~5.59.2 - '@typescript-eslint/typescript-estree': ~5.59.2 - eslint: ~8.7.0 - typescript: ~5.0.4 - dependencies: - '@rushstack/tree-pattern': link:../../libraries/tree-pattern - '@typescript-eslint/experimental-utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - devDependencies: - '@rushstack/heft': 0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_yqm7j6zyazsjiac2knmn54orge - '@types/eslint': 8.2.0 - '@types/estree': 0.0.50 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@typescript-eslint/parser': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - eslint: 8.7.0 - typescript: 5.0.4 - - ../../eslint/eslint-plugin-security: - specifiers: - '@rushstack/heft': 0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@rushstack/tree-pattern': workspace:* - '@types/eslint': 8.2.0 - '@types/estree': 0.0.50 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@typescript-eslint/experimental-utils': ~5.59.2 - '@typescript-eslint/parser': ~5.59.2 - '@typescript-eslint/typescript-estree': ~5.59.2 - eslint: ~8.7.0 - typescript: ~5.0.4 - dependencies: - '@rushstack/tree-pattern': link:../../libraries/tree-pattern - '@typescript-eslint/experimental-utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - devDependencies: - '@rushstack/heft': 0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_yqm7j6zyazsjiac2knmn54orge - '@types/eslint': 8.2.0 - '@types/estree': 0.0.50 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@typescript-eslint/parser': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - eslint: 8.7.0 - typescript: 5.0.4 - - ../../heft-plugins/heft-api-extractor-plugin: - specifiers: - '@microsoft/api-extractor': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-config-file': workspace:* - '@rushstack/heft-legacy': npm:@rushstack/heft@0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@rushstack/node-core-library': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/semver': 7.3.5 - semver: ~7.3.0 - typescript: ~5.0.4 - dependencies: - '@rushstack/heft-config-file': link:../../libraries/heft-config-file - '@rushstack/node-core-library': link:../../libraries/node-core-library - semver: 7.3.8 - devDependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-legacy': /@rushstack/heft/0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_4dchqclsvvgybys5sefjzqvuhm - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/semver': 7.3.5 - typescript: 5.0.4 - - ../../heft-plugins/heft-dev-cert-plugin: - specifiers: - '@microsoft/api-extractor': workspace:* - '@rushstack/debug-certificate-manager': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: ~8.7.0 - dependencies: - '@rushstack/debug-certificate-manager': link:../../libraries/debug-certificate-manager - devDependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: 8.7.0 - - ../../heft-plugins/heft-jest-plugin: - specifiers: - '@jest/core': ~29.5.0 - '@jest/reporters': ~29.5.0 - '@jest/transform': ~29.5.0 - '@jest/types': 29.5.0 - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-config-file': workspace:* - '@rushstack/heft-legacy': npm:@rushstack/heft@0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@rushstack/node-core-library': workspace:* - '@types/heft-jest': 1.0.1 - '@types/lodash': 4.14.116 - '@types/node': 14.18.36 - eslint: ~8.7.0 - jest-config: ~29.5.0 - jest-environment-jsdom: ~29.5.0 - jest-environment-node: ~29.5.0 - jest-resolve: ~29.5.0 - jest-snapshot: ~29.5.0 - jest-watch-select-projects: 2.0.0 - lodash: ~4.17.15 - typescript: ~5.0.4 - dependencies: - '@jest/core': 29.5.0 - '@jest/reporters': 29.5.0 - '@jest/transform': 29.5.0 - '@rushstack/heft-config-file': link:../../libraries/heft-config-file - '@rushstack/node-core-library': link:../../libraries/node-core-library - jest-config: 29.5.0_@types+node@14.18.36 - jest-resolve: 29.5.0 - jest-snapshot: 29.5.0 - lodash: 4.17.21 - devDependencies: - '@jest/types': 29.5.0 - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-legacy': /@rushstack/heft/0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_4dchqclsvvgybys5sefjzqvuhm - '@types/heft-jest': 1.0.1 - '@types/lodash': 4.14.116 - '@types/node': 14.18.36 - eslint: 8.7.0 - jest-environment-jsdom: 29.5.0 - jest-environment-node: 29.5.0 - jest-watch-select-projects: 2.0.0 - typescript: 5.0.4 - - ../../heft-plugins/heft-lint-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-legacy': npm:@rushstack/heft@0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@rushstack/heft-typescript-plugin': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/eslint': 8.2.0 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/semver': 7.3.5 - eslint: ~8.7.0 - semver: ~7.3.0 - tslint: ~5.20.1 - typescript: ~5.0.4 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - semver: 7.3.8 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-legacy': /@rushstack/heft/0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_4dchqclsvvgybys5sefjzqvuhm - '@rushstack/heft-typescript-plugin': link:../heft-typescript-plugin - '@types/eslint': 8.2.0 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/semver': 7.3.5 - eslint: 8.7.0 - tslint: 5.20.1_typescript@5.0.4 - typescript: 5.0.4 - - ../../heft-plugins/heft-sass-plugin: - specifiers: - '@microsoft/api-extractor': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-config-file': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/typings-generator': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: ~8.7.0 - postcss: ~8.4.6 - postcss-modules: ~1.5.0 - sass-embedded: ~1.62.0 - dependencies: - '@rushstack/heft-config-file': link:../../libraries/heft-config-file - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/typings-generator': link:../../libraries/typings-generator - postcss: 8.4.21 - postcss-modules: 1.5.0 - sass-embedded: 1.62.0 - devDependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: 8.7.0 - - ../../heft-plugins/heft-serverless-stack-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/heft-webpack4-plugin': workspace:* - '@rushstack/heft-webpack5-plugin': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/node': 14.18.36 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/heft-webpack4-plugin': link:../heft-webpack4-plugin - '@rushstack/heft-webpack5-plugin': link:../heft-webpack5-plugin - '@types/node': 14.18.36 - - ../../heft-plugins/heft-storybook-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/heft-webpack4-plugin': workspace:* - '@rushstack/heft-webpack5-plugin': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/node': 14.18.36 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/heft-webpack4-plugin': link:../heft-webpack4-plugin - '@rushstack/heft-webpack5-plugin': link:../heft-webpack5-plugin - '@types/node': 14.18.36 - - ../../heft-plugins/heft-typescript-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-config-file': workspace:* - '@rushstack/heft-legacy': npm:@rushstack/heft@0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@rushstack/node-core-library': workspace:* - '@types/node': 14.18.36 - '@types/semver': 7.3.5 - '@types/tapable': 1.0.6 - semver: ~7.3.0 - tapable: 1.1.3 - typescript: ~5.0.4 - dependencies: - '@rushstack/heft-config-file': link:../../libraries/heft-config-file - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@types/tapable': 1.0.6 - semver: 7.3.8 - tapable: 1.1.3 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-legacy': /@rushstack/heft/0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_4dchqclsvvgybys5sefjzqvuhm - '@types/node': 14.18.36 - '@types/semver': 7.3.5 - typescript: 5.0.4 - - ../../heft-plugins/heft-webpack4-plugin: - specifiers: - '@rushstack/debug-certificate-manager': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/node': 14.18.36 - '@types/tapable': 1.0.6 - '@types/watchpack': 2.4.0 - '@types/webpack': 4.41.32 - tapable: 1.1.3 - watchpack: 2.4.0 - webpack: ~4.44.2 - webpack-dev-server: ~4.9.3 - dependencies: - '@rushstack/debug-certificate-manager': link:../../libraries/debug-certificate-manager - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@types/tapable': 1.0.6 - tapable: 1.1.3 - watchpack: 2.4.0 - webpack-dev-server: 4.9.3_2jhnw6fokymnjfoumvhvkjoyjq - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/node': 14.18.36 - '@types/watchpack': 2.4.0 - '@types/webpack': 4.41.32 - webpack: 4.44.2 - - ../../heft-plugins/heft-webpack5-plugin: - specifiers: - '@rushstack/debug-certificate-manager': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/node': 14.18.36 - '@types/tapable': 1.0.6 - '@types/watchpack': 2.4.0 - tapable: 1.1.3 - watchpack: 2.4.0 - webpack: ~5.80.0 - webpack-dev-server: ~4.9.3 - dependencies: - '@rushstack/debug-certificate-manager': link:../../libraries/debug-certificate-manager - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@types/tapable': 1.0.6 - tapable: 1.1.3 - watchpack: 2.4.0 - webpack-dev-server: 4.9.3_webpack@5.80.0 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/node': 14.18.36 - '@types/watchpack': 2.4.0 - webpack: 5.80.0 - - ../../libraries/api-extractor-model: - specifiers: - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': ~0.16.1 - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': 0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@rushstack/node-core-library': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - dependencies: - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': link:../node-core-library - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': 0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_yqm7j6zyazsjiac2knmn54orge - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - - ../../libraries/debug-certificate-manager: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/node-forge': 1.0.4 - node-forge: ~1.3.1 - sudo: ~1.0.3 - dependencies: - '@rushstack/node-core-library': link:../node-core-library - node-forge: 1.3.1 - sudo: 1.0.3 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/node-forge': 1.0.4 - - ../../libraries/heft-config-file: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': 0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@rushstack/node-core-library': workspace:* - '@rushstack/rig-package': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - jsonpath-plus: ~4.0.0 - dependencies: - '@rushstack/node-core-library': link:../node-core-library - '@rushstack/rig-package': link:../rig-package - jsonpath-plus: 4.0.0 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': 0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_yqm7j6zyazsjiac2knmn54orge - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - - ../../libraries/load-themed-styles: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-web-rig': workspace:* - '@types/heft-jest': 1.0.1 - '@types/webpack-env': 1.18.0 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-web-rig': link:../../rigs/heft-web-rig - '@types/heft-jest': 1.0.1 - '@types/webpack-env': 1.18.0 - - ../../libraries/localization-utilities: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/typings-generator': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/xmldoc': 1.1.4 - pseudolocale: ~1.1.0 - xmldoc: ~1.1.2 - dependencies: - '@rushstack/node-core-library': link:../node-core-library - '@rushstack/typings-generator': link:../typings-generator - pseudolocale: 1.1.0 - xmldoc: 1.1.4 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/xmldoc': 1.1.4 - - ../../libraries/module-minifier: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/worker-pool': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/serialize-javascript': 5.0.2 - serialize-javascript: 6.0.0 - source-map: ~0.7.3 - terser: ^5.9.0 - dependencies: - '@rushstack/worker-pool': link:../worker-pool - serialize-javascript: 6.0.0 - source-map: 0.7.4 - terser: 5.16.8 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/serialize-javascript': 5.0.2 - - ../../libraries/node-core-library: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': 0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@types/fs-extra': 7.0.0 - '@types/heft-jest': 1.0.1 - '@types/jju': 1.4.1 - '@types/node': 14.18.36 - '@types/resolve': 1.20.2 - '@types/semver': 7.3.5 - colors: ~1.2.1 - fs-extra: ~7.0.1 - import-lazy: ~4.0.0 - jju: ~1.4.0 - resolve: ~1.22.1 - semver: ~7.3.0 - z-schema: ~5.0.2 - dependencies: - colors: 1.2.5 - fs-extra: 7.0.1 - import-lazy: 4.0.0 - jju: 1.4.0 - resolve: 1.22.1 - semver: 7.3.8 - z-schema: 5.0.5 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': 0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_yqm7j6zyazsjiac2knmn54orge - '@types/fs-extra': 7.0.0 - '@types/heft-jest': 1.0.1 - '@types/jju': 1.4.1 - '@types/node': 14.18.36 - '@types/resolve': 1.20.2 - '@types/semver': 7.3.5 - - ../../libraries/package-deps-hash: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - dependencies: - '@rushstack/node-core-library': link:../node-core-library - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - - ../../libraries/package-extractor: - specifiers: - '@pnpm/link-bins': ~5.3.7 - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/heft-webpack5-plugin': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/terminal': workspace:* - '@rushstack/webpack-preserve-dynamic-require-plugin': workspace:* - '@types/glob': 7.1.1 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/npm-packlist': ~1.1.1 - eslint: ~8.7.0 - ignore: ~5.1.6 - jszip: ~3.8.0 - npm-packlist: ~2.1.2 - webpack: ~5.80.0 - dependencies: - '@pnpm/link-bins': 5.3.25 - '@rushstack/node-core-library': link:../node-core-library - '@rushstack/terminal': link:../terminal - ignore: 5.1.9 - jszip: 3.8.0 - npm-packlist: 2.1.5 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/heft-webpack5-plugin': link:../../heft-plugins/heft-webpack5-plugin - '@rushstack/webpack-preserve-dynamic-require-plugin': link:../../webpack/preserve-dynamic-require-plugin - '@types/glob': 7.1.1 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/npm-packlist': 1.1.2 - eslint: 8.7.0 - webpack: 5.80.0 - - ../../libraries/rig-package: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': 0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/resolve': 1.20.2 - ajv: ~6.12.5 - resolve: ~1.22.1 - strip-json-comments: ~3.1.1 - dependencies: - resolve: 1.22.1 - strip-json-comments: 3.1.1 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': 0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_yqm7j6zyazsjiac2knmn54orge - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/resolve': 1.20.2 - ajv: 6.12.6 - - ../../libraries/rush-lib: - specifiers: - '@pnpm/link-bins': ~5.3.7 - '@pnpm/logger': 4.0.0 - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-config-file': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/heft-webpack5-plugin': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/package-deps-hash': workspace:* - '@rushstack/package-extractor': workspace:* - '@rushstack/rig-package': workspace:* - '@rushstack/stream-collator': workspace:* - '@rushstack/terminal': workspace:* - '@rushstack/ts-command-line': workspace:* - '@rushstack/webpack-deep-imports-plugin': workspace:* - '@rushstack/webpack-preserve-dynamic-require-plugin': workspace:* - '@types/cli-table': 0.3.0 - '@types/glob': 7.1.1 - '@types/inquirer': 7.3.1 - '@types/js-yaml': 3.12.1 - '@types/lodash': 4.14.116 - '@types/node': 14.18.36 - '@types/node-fetch': 2.6.2 - '@types/npm-package-arg': 6.1.0 - '@types/read-package-tree': 5.1.0 - '@types/semver': 7.3.5 - '@types/ssri': ~7.1.0 - '@types/strict-uri-encode': 2.0.0 - '@types/tar': 6.1.1 - '@types/webpack-env': 1.18.0 - '@yarnpkg/lockfile': ~1.0.2 - builtin-modules: ~3.1.0 - cli-table: ~0.3.1 - colors: ~1.2.1 - dependency-path: ~9.2.8 - figures: 3.0.0 - git-repo-info: ~2.1.0 - glob: ~7.0.5 - glob-escape: ~0.0.2 - https-proxy-agent: ~5.0.0 - ignore: ~5.1.6 - inquirer: ~7.3.3 - js-yaml: ~3.13.1 - lodash: ~4.17.15 - node-fetch: 2.6.7 - npm-check: ~6.0.1 - npm-package-arg: ~6.1.0 - read-package-tree: ~5.1.5 - rxjs: ~6.6.7 - semver: ~7.3.0 - ssri: ~8.0.0 - strict-uri-encode: ~2.0.0 - tapable: 2.2.1 - tar: ~6.1.11 - true-case-path: ~2.2.1 - webpack: ~5.80.0 - dependencies: - '@pnpm/link-bins': 5.3.25 - '@rushstack/heft-config-file': link:../heft-config-file - '@rushstack/node-core-library': link:../node-core-library - '@rushstack/package-deps-hash': link:../package-deps-hash - '@rushstack/package-extractor': link:../package-extractor - '@rushstack/rig-package': link:../rig-package - '@rushstack/stream-collator': link:../stream-collator - '@rushstack/terminal': link:../terminal - '@rushstack/ts-command-line': link:../ts-command-line - '@types/node-fetch': 2.6.2 - '@yarnpkg/lockfile': 1.0.2 - builtin-modules: 3.1.0 - cli-table: 0.3.11 - colors: 1.2.5 - dependency-path: 9.2.8 - figures: 3.0.0 - git-repo-info: 2.1.1 - glob: 7.0.6 - glob-escape: 0.0.2 - https-proxy-agent: 5.0.1 - ignore: 5.1.9 - inquirer: 7.3.3 - js-yaml: 3.13.1 - lodash: 4.17.21 - node-fetch: 2.6.7 - npm-check: 6.0.1 - npm-package-arg: 6.1.1 - read-package-tree: 5.1.6 - rxjs: 6.6.7 - semver: 7.3.8 - ssri: 8.0.1 - strict-uri-encode: 2.0.0 - tapable: 2.2.1 - tar: 6.1.13 - true-case-path: 2.2.1 - devDependencies: - '@pnpm/logger': 4.0.0 - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/heft-webpack5-plugin': link:../../heft-plugins/heft-webpack5-plugin - '@rushstack/webpack-deep-imports-plugin': link:../../webpack/webpack-deep-imports-plugin - '@rushstack/webpack-preserve-dynamic-require-plugin': link:../../webpack/preserve-dynamic-require-plugin - '@types/cli-table': 0.3.0 - '@types/glob': 7.1.1 - '@types/inquirer': 7.3.1 - '@types/js-yaml': 3.12.1 - '@types/lodash': 4.14.116 - '@types/node': 14.18.36 - '@types/npm-package-arg': 6.1.0 - '@types/read-package-tree': 5.1.0 - '@types/semver': 7.3.5 - '@types/ssri': 7.1.1 - '@types/strict-uri-encode': 2.0.0 - '@types/tar': 6.1.1 - '@types/webpack-env': 1.18.0 - webpack: 5.80.0 - - ../../libraries/rush-sdk: - specifiers: - '@microsoft/rush-lib': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/stream-collator': workspace:* - '@rushstack/terminal': workspace:* - '@rushstack/ts-command-line': workspace:* - '@types/node-fetch': 2.6.2 - '@types/semver': 7.3.5 - '@types/webpack-env': 1.18.0 - tapable: 2.2.1 - dependencies: - '@rushstack/node-core-library': link:../node-core-library - '@types/node-fetch': 2.6.2 - tapable: 2.2.1 - devDependencies: - '@microsoft/rush-lib': link:../rush-lib - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/stream-collator': link:../stream-collator - '@rushstack/terminal': link:../terminal - '@rushstack/ts-command-line': link:../ts-command-line - '@types/semver': 7.3.5 - '@types/webpack-env': 1.18.0 - - ../../libraries/rush-themed-ui: - specifiers: - '@radix-ui/colors': ~0.1.8 - '@radix-ui/react-checkbox': ~1.0.1 - '@radix-ui/react-icons': ~1.1.1 - '@radix-ui/react-scroll-area': ~1.0.2 - '@radix-ui/react-tabs': ~1.0.1 - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-web-rig': workspace:* - '@types/heft-jest': 1.0.1 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - react: ~16.13.1 - react-dom: ~16.13.1 - dependencies: - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - devDependencies: - '@radix-ui/colors': 0.1.8 - '@radix-ui/react-checkbox': 1.0.3_tlqvpdqnq63ssdllbmshthdmo4 - '@radix-ui/react-icons': 1.1.1_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-scroll-area': 1.0.3_tlqvpdqnq63ssdllbmshthdmo4 - '@radix-ui/react-tabs': 1.0.3_tlqvpdqnq63ssdllbmshthdmo4 - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-web-rig': link:../../rigs/heft-web-rig - '@types/heft-jest': 1.0.1 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/webpack-env': 1.18.0 - - ../../libraries/rushell: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - dependencies: - '@rushstack/node-core-library': link:../node-core-library - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - - ../../libraries/stream-collator: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/terminal': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - dependencies: - '@rushstack/node-core-library': link:../node-core-library - '@rushstack/terminal': link:../terminal - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - - ../../libraries/terminal: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/wordwrap': ~1.0.0 - colors: ~1.2.1 - wordwrap: ~1.0.0 - dependencies: - '@rushstack/node-core-library': link:../node-core-library - wordwrap: 1.0.0 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/wordwrap': 1.0.1 - colors: 1.2.5 - - ../../libraries/tree-pattern: - specifiers: - '@rushstack/eslint-config': 3.3.0 - '@rushstack/heft': 0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: ~7.30.0 - typescript: ~5.0.4 - devDependencies: - '@rushstack/eslint-config': 3.3.0_2aulnmwxyjhjxqmg3aruit533m - '@rushstack/heft': 0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_yqm7j6zyazsjiac2knmn54orge - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - eslint: 7.30.0 - typescript: 5.0.4 - - ../../libraries/ts-command-line: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': 0.50.6 - '@rushstack/heft-node-rig': 1.13.0 - '@types/argparse': 1.0.38 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - argparse: ~1.0.9 - colors: ~1.2.1 - string-argv: ~0.3.1 - dependencies: - '@types/argparse': 1.0.38 - argparse: 1.0.10 - colors: 1.2.5 - string-argv: 0.3.1 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': 0.50.6_@types+node@14.18.36 - '@rushstack/heft-node-rig': 1.13.0_yqm7j6zyazsjiac2knmn54orge - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - - ../../libraries/typings-generator: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/glob': 7.1.1 - '@types/node': 14.18.36 - chokidar: ~3.4.0 - glob: ~7.0.5 - dependencies: - '@rushstack/node-core-library': link:../node-core-library - chokidar: 3.4.3 - glob: 7.0.6 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/glob': 7.1.1 - '@types/node': 14.18.36 - - ../../libraries/worker-pool: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - - ../../repo-scripts/doc-plugin-rush-stack: - specifiers: - '@microsoft/api-documenter': workspace:* - '@microsoft/api-extractor-model': workspace:* - '@microsoft/tsdoc': 0.14.2 - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/js-yaml': 3.12.1 - '@types/node': 14.18.36 - js-yaml: ~3.13.1 - dependencies: - '@microsoft/api-documenter': link:../../apps/api-documenter - '@microsoft/api-extractor-model': link:../../libraries/api-extractor-model - '@microsoft/tsdoc': 0.14.2 - '@rushstack/node-core-library': link:../../libraries/node-core-library - js-yaml: 3.13.1 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/js-yaml': 3.12.1 - '@types/node': 14.18.36 - - ../../repo-scripts/generate-api-docs: - specifiers: - '@microsoft/api-documenter': workspace:* - '@rushstack/eslint-config': workspace:* - doc-plugin-rush-stack: workspace:* - devDependencies: - '@microsoft/api-documenter': link:../../apps/api-documenter - '@rushstack/eslint-config': link:../../eslint/eslint-config - doc-plugin-rush-stack: link:../doc-plugin-rush-stack - - ../../repo-scripts/repo-toolbox: - specifiers: - '@microsoft/rush-lib': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/ts-command-line': workspace:* - '@types/diff': 5.0.1 - '@types/node': 14.18.36 - diff: ~5.0.0 - dependencies: - '@microsoft/rush-lib': link:../../libraries/rush-lib - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/ts-command-line': link:../../libraries/ts-command-line - diff: 5.0.0 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/diff': 5.0.1 - '@types/node': 14.18.36 - - ../../rigs/heft-node-rig: - specifiers: - '@microsoft/api-extractor': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-api-extractor-plugin': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@types/heft-jest': 1.0.1 - eslint: ~8.7.0 - jest-environment-node: ~29.5.0 - typescript: ~5.0.4 - dependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@rushstack/heft-api-extractor-plugin': link:../../heft-plugins/heft-api-extractor-plugin - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@types/heft-jest': 1.0.1 - eslint: 8.7.0 - jest-environment-node: 29.5.0 - typescript: 5.0.4 - devDependencies: - '@rushstack/heft': link:../../apps/heft - - ../../rigs/heft-web-rig: - specifiers: - '@microsoft/api-extractor': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-api-extractor-plugin': workspace:* - '@rushstack/heft-jest-plugin': workspace:* - '@rushstack/heft-lint-plugin': workspace:* - '@rushstack/heft-sass-plugin': workspace:* - '@rushstack/heft-typescript-plugin': workspace:* - '@rushstack/heft-webpack5-plugin': workspace:* - '@types/heft-jest': 1.0.1 - autoprefixer: ~10.4.2 - css-loader: ~6.6.0 - css-minimizer-webpack-plugin: ~3.4.1 - eslint: ~8.7.0 - html-webpack-plugin: ~5.5.0 - jest-environment-jsdom: ~29.5.0 - mini-css-extract-plugin: ~2.5.3 - postcss: ~8.4.6 - postcss-loader: ~6.2.1 - sass: ~1.49.7 - sass-loader: ~12.4.0 - source-map-loader: ~3.0.1 - style-loader: ~3.3.1 - terser-webpack-plugin: ~5.3.1 - typescript: ~5.0.4 - url-loader: ~4.1.1 - webpack: ~5.80.0 - webpack-bundle-analyzer: ~4.5.0 - webpack-merge: ~5.8.0 - dependencies: - '@microsoft/api-extractor': link:../../apps/api-extractor - '@rushstack/heft-api-extractor-plugin': link:../../heft-plugins/heft-api-extractor-plugin - '@rushstack/heft-jest-plugin': link:../../heft-plugins/heft-jest-plugin - '@rushstack/heft-lint-plugin': link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-sass-plugin': link:../../heft-plugins/heft-sass-plugin - '@rushstack/heft-typescript-plugin': link:../../heft-plugins/heft-typescript-plugin - '@rushstack/heft-webpack5-plugin': link:../../heft-plugins/heft-webpack5-plugin - '@types/heft-jest': 1.0.1 - autoprefixer: 10.4.14_postcss@8.4.21 - css-loader: 6.6.0_webpack@5.80.0 - css-minimizer-webpack-plugin: 3.4.1_webpack@5.80.0 - eslint: 8.7.0 - html-webpack-plugin: 5.5.0_webpack@5.80.0 - jest-environment-jsdom: 29.5.0 - mini-css-extract-plugin: 2.5.3_webpack@5.80.0 - postcss: 8.4.21 - postcss-loader: 6.2.1_s3hlmk3dolibrgzfaoz6qln5l4 - sass: 1.49.11 - sass-loader: 12.4.0_eyzo4krppp6jzbzhebz7eme2km - source-map-loader: 3.0.2_webpack@5.80.0 - style-loader: 3.3.2_webpack@5.80.0 - terser-webpack-plugin: 5.3.7_webpack@5.80.0 - typescript: 5.0.4 - url-loader: 4.1.1_webpack@5.80.0 - webpack: 5.80.0 - webpack-bundle-analyzer: 4.5.0 - webpack-merge: 5.8.0 - devDependencies: - '@rushstack/heft': link:../../apps/heft - - ../../rush-plugins/rush-amazon-s3-build-cache-plugin: - specifiers: - '@microsoft/rush-lib': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/rush-sdk': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/node-fetch': 2.6.2 - https-proxy-agent: ~5.0.0 - node-fetch: 2.6.7 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/rush-sdk': link:../../libraries/rush-sdk - https-proxy-agent: 5.0.1 - node-fetch: 2.6.7 - devDependencies: - '@microsoft/rush-lib': link:../../libraries/rush-lib - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/node-fetch': 2.6.2 - - ../../rush-plugins/rush-azure-storage-build-cache-plugin: - specifiers: - '@azure/identity': ~2.1.0 - '@azure/storage-blob': ~12.11.0 - '@microsoft/rush-lib': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/rush-sdk': workspace:* - '@rushstack/terminal': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - dependencies: - '@azure/identity': 2.1.0 - '@azure/storage-blob': 12.11.0 - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/rush-sdk': link:../../libraries/rush-sdk - '@rushstack/terminal': link:../../libraries/terminal - devDependencies: - '@microsoft/rush-lib': link:../../libraries/rush-lib - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - - ../../rush-plugins/rush-http-build-cache-plugin: - specifiers: - '@microsoft/rush-lib': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/rush-sdk': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/node-fetch': 2.6.2 - https-proxy-agent: ~5.0.0 - node-fetch: 2.6.7 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/rush-sdk': link:../../libraries/rush-sdk - https-proxy-agent: 5.0.1 - node-fetch: 2.6.7 - devDependencies: - '@microsoft/rush-lib': link:../../libraries/rush-lib - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/node-fetch': 2.6.2 - - ../../rush-plugins/rush-litewatch-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/rush-sdk': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/rush-sdk': link:../../libraries/rush-sdk - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - - ../../rush-plugins/rush-serve-plugin: - specifiers: - '@rushstack/debug-certificate-manager': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-config-file': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/rig-package': workspace:* - '@rushstack/rush-sdk': workspace:* - '@rushstack/ts-command-line': workspace:* - '@types/express': 4.17.13 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - express: 4.18.1 - dependencies: - '@rushstack/debug-certificate-manager': link:../../libraries/debug-certificate-manager - '@rushstack/heft-config-file': link:../../libraries/heft-config-file - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/rig-package': link:../../libraries/rig-package - '@rushstack/rush-sdk': link:../../libraries/rush-sdk - '@rushstack/ts-command-line': link:../../libraries/ts-command-line - express: 4.18.1 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/express': 4.17.13 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - - ../../webpack/hashed-folder-copy-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/heft-webpack5-plugin': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/webpack-plugin-utilities': workspace:* - '@types/enhanced-resolve': 3.0.7 - '@types/glob': 7.1.1 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/webpack': 4.41.32 - glob: ~7.0.5 - webpack: ~4.44.2 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/webpack-plugin-utilities': link:../webpack-plugin-utilities - glob: 7.0.6 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/heft-webpack5-plugin': link:../../heft-plugins/heft-webpack5-plugin - '@types/enhanced-resolve': 3.0.7 - '@types/glob': 7.1.1 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/webpack': 4.41.32 - webpack: 4.44.2 - - ../../webpack/loader-load-themed-styles: - specifiers: - '@microsoft/load-themed-styles': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@types/loader-utils': 1.1.3 - '@types/node': 14.18.36 - '@types/webpack': 4.41.32 - loader-utils: 1.4.2 - dependencies: - loader-utils: 1.4.2 - devDependencies: - '@microsoft/load-themed-styles': link:../../libraries/load-themed-styles - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/loader-utils': 1.1.3 - '@types/node': 14.18.36 - '@types/webpack': 4.41.32 - - ../../webpack/loader-raw-script: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - loader-utils: 1.4.2 - dependencies: - loader-utils: 1.4.2 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - - ../../webpack/preserve-dynamic-require-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - webpack: ~5.80.0 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - webpack: 5.80.0 - - ../../webpack/set-webpack-public-path-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/heft-webpack5-plugin': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/webpack-plugin-utilities': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/tapable': 1.0.6 - '@types/webpack': 4.41.32 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@rushstack/webpack-plugin-utilities': link:../webpack-plugin-utilities - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/heft-webpack5-plugin': link:../../heft-plugins/heft-webpack5-plugin - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/tapable': 1.0.6 - '@types/webpack': 4.41.32 - - ../../webpack/webpack-deep-imports-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - webpack: ~5.80.0 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - webpack: 5.80.0 - - ../../webpack/webpack-embedded-dependencies-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/webpack-plugin-utilities': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - memfs: 3.4.3 - webpack: ~5.80.0 - dependencies: - '@rushstack/node-core-library': link:../../libraries/node-core-library - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/webpack-plugin-utilities': link:../webpack-plugin-utilities - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - memfs: 3.4.3 - webpack: 5.80.0 - - ../../webpack/webpack-plugin-utilities: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/tapable': 1.0.6 - memfs: 3.4.3 - webpack: ~5.80.0 - webpack-merge: ~5.8.0 - dependencies: - memfs: 3.4.3 - webpack-merge: 5.8.0 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/tapable': 1.0.6 - webpack: 5.80.0 - - ../../webpack/webpack4-localization-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/localization-utilities': workspace:* - '@rushstack/node-core-library': workspace:* - '@rushstack/set-webpack-public-path-plugin': workspace:* - '@types/loader-utils': 1.1.3 - '@types/minimatch': 3.0.5 - '@types/node': 14.18.36 - '@types/tapable': 1.0.6 - '@types/webpack': 4.41.32 - loader-utils: 1.4.2 - minimatch: ~3.0.3 - webpack: ~4.44.2 - dependencies: - '@rushstack/localization-utilities': link:../../libraries/localization-utilities - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@types/tapable': 1.0.6 - loader-utils: 1.4.2 - minimatch: 3.0.8 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/set-webpack-public-path-plugin': link:../set-webpack-public-path-plugin - '@types/loader-utils': 1.1.3 - '@types/minimatch': 3.0.5 - '@types/node': 14.18.36 - '@types/webpack': 4.41.32 - webpack: 4.44.2 - - ../../webpack/webpack4-module-minifier-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/module-minifier': workspace:* - '@rushstack/worker-pool': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/tapable': 1.0.6 - '@types/webpack': 4.41.32 - '@types/webpack-sources': 1.4.2 - tapable: 1.1.3 - webpack: ~4.44.2 - webpack-sources: ~1.4.3 - dependencies: - '@rushstack/module-minifier': link:../../libraries/module-minifier - '@rushstack/worker-pool': link:../../libraries/worker-pool - '@types/tapable': 1.0.6 - tapable: 1.1.3 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/webpack': 4.41.32 - '@types/webpack-sources': 1.4.2 - webpack: 4.44.2 - webpack-sources: 1.4.3 - - ../../webpack/webpack5-load-themed-styles-loader: - specifiers: - '@microsoft/load-themed-styles': workspace:* - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - css-loader: ~6.6.0 - memfs: 3.4.3 - webpack: ~5.80.0 - devDependencies: - '@microsoft/load-themed-styles': link:../../libraries/load-themed-styles - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/node-core-library': link:../../libraries/node-core-library - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - css-loader: 6.6.0_webpack@5.80.0 - memfs: 3.4.3 - webpack: 5.80.0 - - ../../webpack/webpack5-localization-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/localization-utilities': workspace:* - '@rushstack/node-core-library': workspace:* - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - memfs: 3.4.3 - webpack: ~5.80.0 - dependencies: - '@rushstack/localization-utilities': link:../../libraries/localization-utilities - '@rushstack/node-core-library': link:../../libraries/node-core-library - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - memfs: 3.4.3 - webpack: 5.80.0 - - ../../webpack/webpack5-module-minifier-plugin: - specifiers: - '@rushstack/eslint-config': workspace:* - '@rushstack/heft': workspace:* - '@rushstack/heft-node-rig': workspace:* - '@rushstack/module-minifier': workspace:* - '@rushstack/worker-pool': workspace:* - '@types/estree': 0.0.50 - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - '@types/tapable': 1.0.6 - memfs: 3.4.3 - tapable: 2.2.1 - webpack: ~5.80.0 - dependencies: - '@rushstack/worker-pool': link:../../libraries/worker-pool - '@types/estree': 0.0.50 - '@types/tapable': 1.0.6 - tapable: 2.2.1 - devDependencies: - '@rushstack/eslint-config': link:../../eslint/eslint-config - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-node-rig': link:../../rigs/heft-node-rig - '@rushstack/module-minifier': link:../../libraries/module-minifier - '@types/heft-jest': 1.0.1 - '@types/node': 14.18.36 - memfs: 3.4.3 - webpack: 5.80.0 - -packages: - - /@ampproject/remapping/2.2.0: - resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.1.1 - '@jridgewell/trace-mapping': 0.3.17 - - /@aws-cdk/aws-apigatewayv2-alpha/2.7.0-alpha.0_4k7vh4k5vxgov5k3ju6unzjkgi: - resolution: {integrity: sha512-NHm+Jet4Iz1YDEo7lik4ItfGU1w97jCqNKilET0kcPndtxynDJNVpD1O0ycOb9L6hhLtpT5I7Llutt9Dy5gjYA==} - engines: {node: '>= 14.15.0'} - peerDependencies: - aws-cdk-lib: ^2.7.0 - constructs: ^10.0.0 - dependencies: - aws-cdk-lib: 2.7.0_constructs@10.0.130 - constructs: 10.0.130 - dev: true - - /@aws-cdk/aws-apigatewayv2-authorizers-alpha/2.7.0-alpha.0_lhdwln6d4cy5rqcla435fclwy4: - resolution: {integrity: sha512-03VMs0IKvcm5xLan0PI+gczSQZfmYBJruqjB5Fn+VvH57kU7vu75Kgjs6gUc6CpoI38MH7QdamLs9eP9AvL/HQ==} - engines: {node: '>= 14.15.0'} - peerDependencies: - '@aws-cdk/aws-apigatewayv2-alpha': 2.7.0-alpha.0 - aws-cdk-lib: ^2.7.0 - constructs: ^10.0.0 - dependencies: - '@aws-cdk/aws-apigatewayv2-alpha': 2.7.0-alpha.0_4k7vh4k5vxgov5k3ju6unzjkgi - aws-cdk-lib: 2.7.0_constructs@10.0.130 - constructs: 10.0.130 - dev: true - - /@aws-cdk/aws-apigatewayv2-integrations-alpha/2.7.0-alpha.0_lhdwln6d4cy5rqcla435fclwy4: - resolution: {integrity: sha512-QayWlBXdnAXjDghrYHO/vHsViPx/mLb2Tx5xdGM5sgIICNAnnY3WBbZZC4WLIli3bnc22cxwFWGCWHuM/vfj5A==} - engines: {node: '>= 14.15.0'} - peerDependencies: - '@aws-cdk/aws-apigatewayv2-alpha': 2.7.0-alpha.0 - aws-cdk-lib: ^2.7.0 - constructs: ^10.0.0 - dependencies: - '@aws-cdk/aws-apigatewayv2-alpha': 2.7.0-alpha.0_4k7vh4k5vxgov5k3ju6unzjkgi - aws-cdk-lib: 2.7.0_constructs@10.0.130 - constructs: 10.0.130 - dev: true - - /@aws-cdk/aws-appsync-alpha/2.7.0-alpha.0_4k7vh4k5vxgov5k3ju6unzjkgi: - resolution: {integrity: sha512-4XyRBZG+hlAmyYv+xTJ1picE3eR9ImdbLpm/znde56/lOqOVBryP/h8xXL3EOAbOknqp7cnXAnjgV7BUOxLOdQ==} - engines: {node: '>= 14.15.0'} - peerDependencies: - aws-cdk-lib: ^2.7.0 - constructs: ^10.0.0 - dependencies: - aws-cdk-lib: 2.7.0_constructs@10.0.130 - constructs: 10.0.130 - dev: true - - /@aws-cdk/cfnspec/2.7.0: - resolution: {integrity: sha512-RZbQLtTYZzC/DZkdW8Kla/9dI+UMgIrzAoEXhgTU9U0lVjXQ/3bdmmwSgC1bi3ckRRGJAL3+UBqoj2SAWh9ypg==} - dependencies: - fs-extra: 9.1.0 - md5: 2.3.0 - dev: true - - /@aws-cdk/cloud-assembly-schema/2.7.0: - resolution: {integrity: sha512-vKTKLMPvzUhsYo3c4/EbMJq+bwIgHkwK0lV9fc5mQlnTUTyHe6nGIvyzmWWMd5BVEkgNzw+QdecxeeYJNu/doA==} - engines: {node: '>= 14.15.0'} - dependencies: - jsonschema: 1.4.1 - semver: 7.3.8 - dev: true - bundledDependencies: - - jsonschema - - semver - - /@aws-cdk/cloudformation-diff/2.7.0: - resolution: {integrity: sha512-2bu2+13K6rsGwV0T3W+sTQzw0MgYcvVvB7SYqRSIhDJ+1ylZby7+0IApAn+XuIUr6+1KmcedvNku7LEiUWV8DA==} - engines: {node: '>= 14.15.0'} - dependencies: - '@aws-cdk/cfnspec': 2.7.0 - '@types/node': 10.17.60 - chalk: 4.1.2 - diff: 5.0.0 - fast-deep-equal: 3.1.3 - string-width: 4.2.3 - table: 6.8.1 - dev: true - - /@aws-cdk/cx-api/2.7.0: - resolution: {integrity: sha512-rfjpWyouHy3zNFEdhy5M75x4NsHZerInNAKx3tMbBIYgu258AyYJzJCecTKDGPCD1zB/u0FzshqeiqxY7pjrPA==} - engines: {node: '>= 14.15.0'} - dependencies: - '@aws-cdk/cloud-assembly-schema': 2.7.0 - semver: 7.3.8 - dev: true - bundledDependencies: - - semver - - /@aws-cdk/region-info/2.7.0: - resolution: {integrity: sha512-2j1MC2BOsfjAVfdqJa5aXxLQK6AqFtEsR4QOUKmwnDOV9qRr0yDoY3eXUpB480B4EOIW2e3jLfjkPHKu/P2DSw==} - engines: {node: '>= 14.15.0'} - dev: true - - /@azure/abort-controller/1.1.0: - resolution: {integrity: sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==} - engines: {node: '>=12.0.0'} - dependencies: - tslib: 2.3.1 - dev: false - - /@azure/core-auth/1.4.0: - resolution: {integrity: sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==} - engines: {node: '>=12.0.0'} - dependencies: - '@azure/abort-controller': 1.1.0 - tslib: 2.3.1 - dev: false - - /@azure/core-client/1.7.2: - resolution: {integrity: sha512-ye5554gnVnXdfZ64hptUtETgacXoRWxYv1JF5MctoAzTSH5dXhDPZd9gOjDPyWMcLIk58pnP5+p5vGX6PYn1ag==} - engines: {node: '>=14.0.0'} - dependencies: - '@azure/abort-controller': 1.1.0 - '@azure/core-auth': 1.4.0 - '@azure/core-rest-pipeline': 1.10.2 - '@azure/core-tracing': 1.0.1 - '@azure/core-util': 1.2.0 - '@azure/logger': 1.0.4 - tslib: 2.3.1 - transitivePeerDependencies: - - supports-color - dev: false - - /@azure/core-http/2.3.1: - resolution: {integrity: sha512-cur03BUwV0Tbv81bQBOLafFB02B6G++K6F2O3IMl8pSE2QlXm3cu11bfyBNlDUKi5U+xnB3GC63ae3athhkx6Q==} - engines: {node: '>=14.0.0'} - dependencies: - '@azure/abort-controller': 1.1.0 - '@azure/core-auth': 1.4.0 - '@azure/core-tracing': 1.0.0-preview.13 - '@azure/core-util': 1.2.0 - '@azure/logger': 1.0.4 - '@types/node-fetch': 2.6.2 - '@types/tunnel': 0.0.3 - form-data: 4.0.0 - node-fetch: 2.6.7 - process: 0.11.10 - tough-cookie: 4.1.2 - tslib: 2.3.1 - tunnel: 0.0.6 - uuid: 8.3.2 - xml2js: 0.4.23 - transitivePeerDependencies: - - encoding - dev: false - - /@azure/core-lro/2.5.1: - resolution: {integrity: sha512-JHQy/bA3NOz2WuzOi5zEk6n/TJdAropupxUT521JIJvW7EXV2YN2SFYZrf/2RHeD28QAClGdynYadZsbmP+nyQ==} - engines: {node: '>=14.0.0'} - dependencies: - '@azure/abort-controller': 1.1.0 - '@azure/logger': 1.0.4 - tslib: 2.3.1 - dev: false - - /@azure/core-paging/1.5.0: - resolution: {integrity: sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.3.1 - dev: false - - /@azure/core-rest-pipeline/1.10.2: - resolution: {integrity: sha512-e3WzAsRKLor5EgK2bQqR1OY5D7VBqzORHtlqtygZZQGCYOIBsynqrZBa8MFD1Ue9r8TPtofOLditalnlQHS45Q==} - engines: {node: '>=14.0.0'} - dependencies: - '@azure/abort-controller': 1.1.0 - '@azure/core-auth': 1.4.0 - '@azure/core-tracing': 1.0.1 - '@azure/core-util': 1.2.0 - '@azure/logger': 1.0.4 - form-data: 4.0.0 - http-proxy-agent: 5.0.0 - https-proxy-agent: 5.0.1 - tslib: 2.3.1 - uuid: 8.3.2 - transitivePeerDependencies: - - supports-color - dev: false - - /@azure/core-tracing/1.0.0-preview.13: - resolution: {integrity: sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==} - engines: {node: '>=12.0.0'} - dependencies: - '@opentelemetry/api': 1.4.1 - tslib: 2.3.1 - dev: false - - /@azure/core-tracing/1.0.1: - resolution: {integrity: sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==} - engines: {node: '>=12.0.0'} - dependencies: - tslib: 2.3.1 - dev: false - - /@azure/core-util/1.2.0: - resolution: {integrity: sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng==} - engines: {node: '>=14.0.0'} - dependencies: - '@azure/abort-controller': 1.1.0 - tslib: 2.3.1 - dev: false - - /@azure/identity/2.1.0: - resolution: {integrity: sha512-BPDz1sK7Ul9t0l9YKLEa8PHqWU4iCfhGJ+ELJl6c8CP3TpJt2urNCbm0ZHsthmxRsYoMPbz2Dvzj30zXZVmAFw==} - engines: {node: '>=12.0.0'} - dependencies: - '@azure/abort-controller': 1.1.0 - '@azure/core-auth': 1.4.0 - '@azure/core-client': 1.7.2 - '@azure/core-rest-pipeline': 1.10.2 - '@azure/core-tracing': 1.0.1 - '@azure/core-util': 1.2.0 - '@azure/logger': 1.0.4 - '@azure/msal-browser': 2.34.0 - '@azure/msal-common': 7.6.0 - '@azure/msal-node': 1.16.0 - events: 3.3.0 - jws: 4.0.0 - open: 8.4.2 - stoppable: 1.1.0 - tslib: 2.3.1 - uuid: 8.3.2 - transitivePeerDependencies: - - supports-color - dev: false - - /@azure/logger/1.0.4: - resolution: {integrity: sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.3.1 - dev: false - - /@azure/msal-browser/2.34.0: - resolution: {integrity: sha512-stoXdlfAtyVIMOp1lS5PorgO5f66MGRi3Q1FBlXhVZFTsTfAWrNdSOx1m/PXWHskWE9aXO+NEzXVOoWmDNnvNA==} - engines: {node: '>=0.8.0'} - dependencies: - '@azure/msal-common': 11.0.0 - dev: false - - /@azure/msal-common/11.0.0: - resolution: {integrity: sha512-SZH8ObQ3Hq5v3ogVGBYJp1nNW7p+MtM4PH4wfNadBP9wf7K0beQHF9iOtRcjPOkwZf+ZD49oXqw91LndIkdk8g==} - engines: {node: '>=0.8.0'} - dev: false - - /@azure/msal-common/7.6.0: - resolution: {integrity: sha512-XqfbglUTVLdkHQ8F9UQJtKseRr3sSnr9ysboxtoswvaMVaEfvyLtMoHv9XdKUfOc0qKGzNgRFd9yRjIWVepl6Q==} - engines: {node: '>=0.8.0'} - dev: false - - /@azure/msal-node/1.16.0: - resolution: {integrity: sha512-eGXPp65i++mAIvziafbCH970TCeECB6iaQP7aRzZEjtU238cW4zKm40U8YxkiCn9rR1G2VeMHENB5h6WRk7ZCQ==} - engines: {node: 10 || 12 || 14 || 16 || 18} - dependencies: - '@azure/msal-common': 11.0.0 - jsonwebtoken: 9.0.0 - uuid: 8.3.2 - dev: false - - /@azure/storage-blob/12.11.0: - resolution: {integrity: sha512-na+FisoARuaOWaHWpmdtk3FeuTWf2VWamdJ9/TJJzj5ZdXPLC3juoDgFs6XVuJIoK30yuBpyFBEDXVRK4pB7Tg==} - engines: {node: '>=12.0.0'} - dependencies: - '@azure/abort-controller': 1.1.0 - '@azure/core-http': 2.3.1 - '@azure/core-lro': 2.5.1 - '@azure/core-paging': 1.5.0 - '@azure/core-tracing': 1.0.0-preview.13 - '@azure/logger': 1.0.4 - events: 3.3.0 - tslib: 2.3.1 - transitivePeerDependencies: - - encoding - dev: false - - /@babel/code-frame/7.12.11: - resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==} - dependencies: - '@babel/highlight': 7.18.6 - dev: true - - /@babel/code-frame/7.18.6: - resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.18.6 - - /@babel/compat-data/7.21.0: - resolution: {integrity: sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==} - engines: {node: '>=6.9.0'} - - /@babel/core/7.12.9: - resolution: {integrity: sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.21.3 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helpers': 7.21.0 - '@babel/parser': 7.21.3 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.3 - '@babel/types': 7.21.3 - convert-source-map: 1.9.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - lodash: 4.17.21 - resolve: 1.22.1 - semver: 5.7.1 - source-map: 0.5.7 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/core/7.20.12: - resolution: {integrity: sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.0 - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.21.3 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helpers': 7.21.0 - '@babel/parser': 7.21.3 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.3 - '@babel/types': 7.21.3 - convert-source-map: 1.9.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - - /@babel/generator/7.21.3: - resolution: {integrity: sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.3 - '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.17 - jsesc: 2.5.2 - - /@babel/helper-annotate-as-pure/7.18.6: - resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.3 - dev: true - - /@babel/helper-builder-binary-assignment-operator-visitor/7.18.9: - resolution: {integrity: sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-explode-assignable-expression': 7.18.6 - '@babel/types': 7.21.3 - dev: true - - /@babel/helper-compilation-targets/7.20.7_@babel+core@7.20.12: - resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/compat-data': 7.21.0 - '@babel/core': 7.20.12 - '@babel/helper-validator-option': 7.21.0 - browserslist: 4.21.5 - lru-cache: 5.1.1 - semver: 6.3.0 - - /@babel/helper-create-class-features-plugin/7.21.0_@babel+core@7.20.12: - resolution: {integrity: sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-member-expression-to-functions': 7.21.0 - '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-replace-supers': 7.20.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/helper-split-export-declaration': 7.18.6 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/helper-create-regexp-features-plugin/7.21.0_@babel+core@7.20.12: - resolution: {integrity: sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-annotate-as-pure': 7.18.6 - regexpu-core: 5.3.2 - dev: true - - /@babel/helper-define-polyfill-provider/0.1.5_@babel+core@7.20.12: - resolution: {integrity: sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg==} - peerDependencies: - '@babel/core': ^7.4.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 - '@babel/helper-module-imports': 7.18.6 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/traverse': 7.21.3 - debug: 4.3.4 - lodash.debounce: 4.0.8 - resolve: 1.22.1 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/helper-define-polyfill-provider/0.3.3_@babel+core@7.20.12: - resolution: {integrity: sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==} - peerDependencies: - '@babel/core': ^7.4.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - debug: 4.3.4 - lodash.debounce: 4.0.8 - resolve: 1.22.1 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/helper-environment-visitor/7.18.9: - resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} - engines: {node: '>=6.9.0'} - - /@babel/helper-explode-assignable-expression/7.18.6: - resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.3 - dev: true - - /@babel/helper-function-name/7.21.0: - resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.20.7 - '@babel/types': 7.21.3 - - /@babel/helper-hoist-variables/7.18.6: - resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.3 - - /@babel/helper-member-expression-to-functions/7.21.0: - resolution: {integrity: sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.3 - dev: true - - /@babel/helper-module-imports/7.18.6: - resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.3 - - /@babel/helper-module-transforms/7.21.2: - resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-module-imports': 7.18.6 - '@babel/helper-simple-access': 7.20.2 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/helper-validator-identifier': 7.19.1 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.3 - '@babel/types': 7.21.3 - transitivePeerDependencies: - - supports-color - - /@babel/helper-optimise-call-expression/7.18.6: - resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.3 - dev: true - - /@babel/helper-plugin-utils/7.10.4: - resolution: {integrity: sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==} - dev: true - - /@babel/helper-plugin-utils/7.20.2: - resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} - engines: {node: '>=6.9.0'} - - /@babel/helper-remap-async-to-generator/7.18.9_@babel+core@7.20.12: - resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.21.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/helper-replace-supers/7.20.7: - resolution: {integrity: sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-member-expression-to-functions': 7.21.0 - '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.3 - '@babel/types': 7.21.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/helper-simple-access/7.20.2: - resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.3 - - /@babel/helper-skip-transparent-expression-wrappers/7.20.0: - resolution: {integrity: sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.3 - dev: true - - /@babel/helper-split-export-declaration/7.18.6: - resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.3 - - /@babel/helper-string-parser/7.19.4: - resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} - engines: {node: '>=6.9.0'} - - /@babel/helper-validator-identifier/7.19.1: - resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} - engines: {node: '>=6.9.0'} - - /@babel/helper-validator-option/7.21.0: - resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} - engines: {node: '>=6.9.0'} - - /@babel/helper-wrap-function/7.20.5: - resolution: {integrity: sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-function-name': 7.21.0 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.3 - '@babel/types': 7.21.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/helpers/7.21.0: - resolution: {integrity: sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.3 - '@babel/types': 7.21.3 - transitivePeerDependencies: - - supports-color - - /@babel/highlight/7.18.6: - resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.19.1 - chalk: 2.4.2 - js-tokens: 4.0.0 - - /@babel/parser/7.16.4: - resolution: {integrity: sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==} - engines: {node: '>=6.0.0'} - hasBin: true - dev: false - - /@babel/parser/7.21.3: - resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==} - engines: {node: '>=6.0.0'} - hasBin: true - - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.20.7_@babel+core@7.20.12: - resolution: {integrity: sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.13.0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.20.12 - dev: true - - /@babel/plugin-proposal-async-generator-functions/7.20.7_@babel+core@7.20.12: - resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.12 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-proposal-class-properties/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-proposal-class-static-block/7.21.0_@babel+core@7.20.12: - resolution: {integrity: sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.12.0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.12 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-proposal-decorators/7.21.0_@babel+core@7.20.12: - resolution: {integrity: sha512-MfgX49uRrFUTL/HvWtmx3zmpyzMMr4MTj3d527MLlr/4RTT9G/ytFFP7qet2uM2Ve03b+BkpWUpK+lRXnQ+v9w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-replace-supers': 7.20.7 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/plugin-syntax-decorators': 7.21.0_@babel+core@7.20.12 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-proposal-dynamic-import/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12 - dev: true - - /@babel/plugin-proposal-export-default-from/7.18.10_@babel+core@7.20.12: - resolution: {integrity: sha512-5H2N3R2aQFxkV4PIBUR/i7PUSwgTZjouJKzI8eKswfIjT0PhvzkPn0t0wIS5zn6maQuvtT0t1oHtMUz61LOuow==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-export-default-from': 7.18.6_@babel+core@7.20.12 - dev: true - - /@babel/plugin-proposal-export-namespace-from/7.18.9_@babel+core@7.20.12: - resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.12 - dev: true - - /@babel/plugin-proposal-json-strings/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.12 - dev: true - - /@babel/plugin-proposal-logical-assignment-operators/7.20.7_@babel+core@7.20.12: - resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.12 - dev: true - - /@babel/plugin-proposal-nullish-coalescing-operator/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.12 - dev: true - - /@babel/plugin-proposal-numeric-separator/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.12 - dev: true - - /@babel/plugin-proposal-object-rest-spread/7.12.1_@babel+core@7.12.9: - resolution: {integrity: sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.12.9 - '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.12.9 - dev: true - - /@babel/plugin-proposal-object-rest-spread/7.20.7_@babel+core@7.20.12: - resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/compat-data': 7.21.0 - '@babel/core': 7.20.12 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.20.12 - dev: true - - /@babel/plugin-proposal-optional-catch-binding/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.12 - dev: true - - /@babel/plugin-proposal-optional-chaining/7.21.0_@babel+core@7.20.12: - resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.12 - dev: true - - /@babel/plugin-proposal-private-methods/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-proposal-private-property-in-object/7.21.0_@babel+core@7.20.12: - resolution: {integrity: sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.12 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-proposal-unicode-property-regex/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==} - engines: {node: '>=4'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-regexp-features-plugin': 7.21.0_@babel+core@7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.20.12: - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.20.12: - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.20.12: - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.20.12: - resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-syntax-decorators/7.21.0_@babel+core@7.20.12: - resolution: {integrity: sha512-tIoPpGBR8UuM4++ccWN3gifhVvQu7ZizuR1fklhRJrd5ewgbkUS+0KVFeWWxELtn18NTLoW32XV7zyOgIAiz+w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.20.12: - resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-syntax-export-default-from/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-Kr//z3ujSVNx6E9z9ih5xXXMqK07VVTuqPmqGe6Mss/zW5XPeLZeSDZoP9ab/hT4wPKqAgjl2PnhPrcpk8Seew==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.20.12: - resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-syntax-flow/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-syntax-import-assertions/7.20.0_@babel+core@7.20.12: - resolution: {integrity: sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.20.12: - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.20.12: - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-syntax-jsx/7.12.1_@babel+core@7.12.9: - resolution: {integrity: sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.20.12: - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.20.12: - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.20.12: - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.12.9: - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.20.12: - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.20.12: - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.20.12: - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.20.12: - resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.20.12: - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-syntax-typescript/7.20.0_@babel+core@7.20.12: - resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-transform-arrow-functions/7.20.7_@babel+core@7.20.12: - resolution: {integrity: sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-async-to-generator/7.20.7_@babel+core@7.20.12: - resolution: {integrity: sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-module-imports': 7.18.6 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.20.12 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-transform-block-scoped-functions/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-block-scoping/7.21.0_@babel+core@7.20.12: - resolution: {integrity: sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-classes/7.21.0_@babel+core@7.20.12: - resolution: {integrity: sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-replace-supers': 7.20.7 - '@babel/helper-split-export-declaration': 7.18.6 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-transform-computed-properties/7.20.7_@babel+core@7.20.12: - resolution: {integrity: sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/template': 7.20.7 - dev: true - - /@babel/plugin-transform-destructuring/7.21.3_@babel+core@7.20.12: - resolution: {integrity: sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-dotall-regex/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-regexp-features-plugin': 7.21.0_@babel+core@7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-duplicate-keys/7.18.9_@babel+core@7.20.12: - resolution: {integrity: sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-exponentiation-operator/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-flow-strip-types/7.21.0_@babel+core@7.20.12: - resolution: {integrity: sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-flow': 7.18.6_@babel+core@7.20.12 - dev: true - - /@babel/plugin-transform-for-of/7.21.0_@babel+core@7.20.12: - resolution: {integrity: sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-function-name/7.18.9_@babel+core@7.20.12: - resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-literals/7.18.9_@babel+core@7.20.12: - resolution: {integrity: sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-member-expression-literals/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-modules-amd/7.20.11_@babel+core@7.20.12: - resolution: {integrity: sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-transform-modules-commonjs/7.21.2_@babel+core@7.20.12: - resolution: {integrity: sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-simple-access': 7.20.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-transform-modules-systemjs/7.20.11_@babel+core@7.20.12: - resolution: {integrity: sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-identifier': 7.19.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-transform-modules-umd/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-transform-named-capturing-groups-regex/7.20.5_@babel+core@7.20.12: - resolution: {integrity: sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-regexp-features-plugin': 7.21.0_@babel+core@7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-new-target/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-object-super/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-replace-supers': 7.20.7 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-transform-parameters/7.21.3_@babel+core@7.12.9: - resolution: {integrity: sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-parameters/7.21.3_@babel+core@7.20.12: - resolution: {integrity: sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-property-literals/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-react-display-name/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-react-jsx-development/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/plugin-transform-react-jsx': 7.21.0_@babel+core@7.20.12 - dev: true - - /@babel/plugin-transform-react-jsx/7.21.0_@babel+core@7.20.12: - resolution: {integrity: sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-module-imports': 7.18.6 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.12 - '@babel/types': 7.21.3 - dev: true - - /@babel/plugin-transform-react-pure-annotations/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-regenerator/7.20.5_@babel+core@7.20.12: - resolution: {integrity: sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - regenerator-transform: 0.15.1 - dev: true - - /@babel/plugin-transform-reserved-words/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-shorthand-properties/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-spread/7.20.7_@babel+core@7.20.12: - resolution: {integrity: sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - dev: true - - /@babel/plugin-transform-sticky-regex/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-template-literals/7.18.9_@babel+core@7.20.12: - resolution: {integrity: sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-typeof-symbol/7.18.9_@babel+core@7.20.12: - resolution: {integrity: sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-typescript/7.21.3_@babel+core@7.20.12: - resolution: {integrity: sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.21.0_@babel+core@7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.20.12 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-transform-unicode-escapes/7.18.10_@babel+core@7.20.12: - resolution: {integrity: sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-unicode-regex/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-create-regexp-features-plugin': 7.21.0_@babel+core@7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/preset-env/7.20.2_@babel+core@7.20.12: - resolution: {integrity: sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/compat-data': 7.21.0 - '@babel/core': 7.20.12 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-async-generator-functions': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-class-static-block': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-proposal-dynamic-import': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-export-namespace-from': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-proposal-json-strings': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-logical-assignment-operators': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-numeric-separator': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-object-rest-spread': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-optional-catch-binding': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-private-property-in-object': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.12 - '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.20.12 - '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.12 - '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-import-assertions': 7.20.0_@babel+core@7.20.12 - '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.12 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.12 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.12 - '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.12 - '@babel/plugin-transform-arrow-functions': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-async-to-generator': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-block-scoped-functions': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-block-scoping': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-classes': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-computed-properties': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-destructuring': 7.21.3_@babel+core@7.20.12 - '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-duplicate-keys': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-exponentiation-operator': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-for-of': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-function-name': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-literals': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-member-expression-literals': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-modules-amd': 7.20.11_@babel+core@7.20.12 - '@babel/plugin-transform-modules-commonjs': 7.21.2_@babel+core@7.20.12 - '@babel/plugin-transform-modules-systemjs': 7.20.11_@babel+core@7.20.12 - '@babel/plugin-transform-modules-umd': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5_@babel+core@7.20.12 - '@babel/plugin-transform-new-target': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-object-super': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.20.12 - '@babel/plugin-transform-property-literals': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-regenerator': 7.20.5_@babel+core@7.20.12 - '@babel/plugin-transform-reserved-words': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-shorthand-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-spread': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-sticky-regex': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-template-literals': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-typeof-symbol': 7.18.9_@babel+core@7.20.12 - '@babel/plugin-transform-unicode-escapes': 7.18.10_@babel+core@7.20.12 - '@babel/plugin-transform-unicode-regex': 7.18.6_@babel+core@7.20.12 - '@babel/preset-modules': 0.1.5_@babel+core@7.20.12 - '@babel/types': 7.21.3 - babel-plugin-polyfill-corejs2: 0.3.3_@babel+core@7.20.12 - babel-plugin-polyfill-corejs3: 0.6.0_@babel+core@7.20.12 - babel-plugin-polyfill-regenerator: 0.4.1_@babel+core@7.20.12 - core-js-compat: 3.29.1 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/preset-flow/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-transform-flow-strip-types': 7.21.0_@babel+core@7.20.12 - dev: true - - /@babel/preset-modules/0.1.5_@babel+core@7.20.12: - resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.20.12 - '@babel/types': 7.21.3 - esutils: 2.0.3 - dev: true - - /@babel/preset-react/7.18.6_@babel+core@7.20.12: - resolution: {integrity: sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-transform-react-display-name': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-react-jsx': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-react-jsx-development': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-react-pure-annotations': 7.18.6_@babel+core@7.20.12 - dev: true - - /@babel/preset-typescript/7.21.0_@babel+core@7.20.12: - resolution: {integrity: sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-transform-typescript': 7.21.3_@babel+core@7.20.12 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/register/7.21.0_@babel+core@7.20.12: - resolution: {integrity: sha512-9nKsPmYDi5DidAqJaQooxIhsLJiNMkGr8ypQ8Uic7cIox7UCDsM7HuUGxdGT7mSDTYbqzIdsOWzfBton/YJrMw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - clone-deep: 4.0.1 - find-cache-dir: 2.1.0 - make-dir: 2.1.0 - pirates: 4.0.5 - source-map-support: 0.5.21 - dev: true - - /@babel/regjsgen/0.8.0: - resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} - dev: true - - /@babel/runtime/7.21.0: - resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.13.11 - - /@babel/template/7.20.7: - resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.18.6 - '@babel/parser': 7.21.3 - '@babel/types': 7.21.3 - - /@babel/traverse/7.21.3: - resolution: {integrity: sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.21.3 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.21.3 - '@babel/types': 7.21.3 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - /@babel/types/7.21.3: - resolution: {integrity: sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.19.4 - '@babel/helper-validator-identifier': 7.19.1 - to-fast-properties: 2.0.0 - - /@balena/dockerignore/1.0.2: - resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} - dev: true - - /@base2/pretty-print-object/1.0.1: - resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==} - dev: true - - /@bcoe/v8-coverage/0.2.3: - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - - /@bufbuild/protobuf/1.2.1: - resolution: {integrity: sha512-cwwGvLGqvoaOZmoP5+i4v/rbW+rHkguvTehuZyM2p/xpmaNSdT2h3B7kHw33aiffv35t1XrYHIkdJSEkSEMJuA==} - dev: false - - /@cnakazawa/watch/1.0.4: - resolution: {integrity: sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==} - engines: {node: '>=0.1.95'} - hasBin: true - dependencies: - exec-sh: 0.3.6 - minimist: 1.2.8 - dev: true - - /@colors/colors/1.5.0: - resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} - engines: {node: '>=0.1.90'} - requiresBuild: true - dev: true - optional: true - - /@devexpress/error-stack-parser/2.0.6: - resolution: {integrity: sha512-fneVypElGUH6Be39mlRZeAu00pccTlf4oVuzf9xPJD1cdEqI8NyAiQua/EW7lZdrbMUbgyXcJmfKPefhYius3A==} - dependencies: - stackframe: 1.3.4 - dev: false - - /@discoveryjs/json-ext/0.5.7: - resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} - engines: {node: '>=10.0.0'} - dev: true - - /@emotion/cache/10.0.29: - resolution: {integrity: sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==} - dependencies: - '@emotion/sheet': 0.9.4 - '@emotion/stylis': 0.8.5 - '@emotion/utils': 0.11.3 - '@emotion/weak-memoize': 0.2.5 - dev: true - - /@emotion/core/10.3.1_qjwx5m6wssz3lnb35xwkc3pz6q: - resolution: {integrity: sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww==} - peerDependencies: - '@types/react': '>=16' - react: '>=16.3.0' - dependencies: - '@babel/runtime': 7.21.0 - '@emotion/cache': 10.0.29 - '@emotion/css': 10.0.27 - '@emotion/serialize': 0.11.16 - '@emotion/sheet': 0.9.4 - '@emotion/utils': 0.11.3 - '@types/react': 16.14.23 - react: 16.13.1 - dev: true - - /@emotion/css/10.0.27: - resolution: {integrity: sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==} - dependencies: - '@emotion/serialize': 0.11.16 - '@emotion/utils': 0.11.3 - babel-plugin-emotion: 10.2.2 - dev: true - - /@emotion/hash/0.8.0: - resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} - dev: true - - /@emotion/hash/0.9.0: - resolution: {integrity: sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==} - dev: true - - /@emotion/is-prop-valid/0.8.8: - resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} - dependencies: - '@emotion/memoize': 0.7.4 - dev: true - - /@emotion/memoize/0.7.4: - resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} - dev: true - - /@emotion/memoize/0.8.0: - resolution: {integrity: sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==} - dev: true - - /@emotion/serialize/0.11.16: - resolution: {integrity: sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==} - dependencies: - '@emotion/hash': 0.8.0 - '@emotion/memoize': 0.7.4 - '@emotion/unitless': 0.7.5 - '@emotion/utils': 0.11.3 - csstype: 2.6.21 - dev: true - - /@emotion/serialize/1.1.1: - resolution: {integrity: sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==} - dependencies: - '@emotion/hash': 0.9.0 - '@emotion/memoize': 0.8.0 - '@emotion/unitless': 0.8.0 - '@emotion/utils': 1.2.0 - csstype: 3.1.1 - dev: true - - /@emotion/sheet/0.9.4: - resolution: {integrity: sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==} - dev: true - - /@emotion/styled-base/10.3.0_tpm53lxjhhnjmtj6wgjp3t3pxi: - resolution: {integrity: sha512-PBRqsVKR7QRNkmfH78hTSSwHWcwDpecH9W6heujWAcyp2wdz/64PP73s7fWS1dIPm8/Exc8JAzYS8dEWXjv60w==} - peerDependencies: - '@emotion/core': ^10.0.28 - '@types/react': '>=16' - react: '>=16.3.0' - dependencies: - '@babel/runtime': 7.21.0 - '@emotion/core': 10.3.1_qjwx5m6wssz3lnb35xwkc3pz6q - '@emotion/is-prop-valid': 0.8.8 - '@emotion/serialize': 0.11.16 - '@emotion/utils': 0.11.3 - '@types/react': 16.14.23 - react: 16.13.1 - dev: true - - /@emotion/styled/10.3.0_tpm53lxjhhnjmtj6wgjp3t3pxi: - resolution: {integrity: sha512-GgcUpXBBEU5ido+/p/mCT2/Xx+Oqmp9JzQRuC+a4lYM4i4LBBn/dWvc0rQ19N9ObA8/T4NWMrPNe79kMBDJqoQ==} - peerDependencies: - '@emotion/core': ^10.0.27 - '@types/react': '>=16' - react: '>=16.3.0' - dependencies: - '@emotion/core': 10.3.1_qjwx5m6wssz3lnb35xwkc3pz6q - '@emotion/styled-base': 10.3.0_tpm53lxjhhnjmtj6wgjp3t3pxi - '@types/react': 16.14.23 - babel-plugin-emotion: 10.2.2 - react: 16.13.1 - dev: true - - /@emotion/stylis/0.8.5: - resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==} - dev: true - - /@emotion/unitless/0.7.5: - resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} - dev: true - - /@emotion/unitless/0.8.0: - resolution: {integrity: sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==} - dev: true - - /@emotion/utils/0.11.3: - resolution: {integrity: sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==} - dev: true - - /@emotion/utils/1.2.0: - resolution: {integrity: sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==} - dev: true - - /@emotion/weak-memoize/0.2.5: - resolution: {integrity: sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==} - dev: true - - /@esbuild/android-arm/0.17.14: - resolution: {integrity: sha512-0CnlwnjDU8cks0yJLXfkaU/uoLyRf9VZJs4p1PskBr2AlAHeEsFEwJEo0of/Z3g+ilw5mpyDwThlxzNEIxOE4g==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/android-arm64/0.17.14: - resolution: {integrity: sha512-eLOpPO1RvtsP71afiFTvS7tVFShJBCT0txiv/xjFBo5a7R7Gjw7X0IgIaFoLKhqXYAXhahoXm7qAmRXhY4guJg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/android-x64/0.17.14: - resolution: {integrity: sha512-nrfQYWBfLGfSGLvRVlt6xi63B5IbfHm3tZCdu/82zuFPQ7zez4XjmRtF/wIRYbJQ/DsZrxJdEvYFE67avYXyng==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/darwin-arm64/0.17.14: - resolution: {integrity: sha512-eoSjEuDsU1ROwgBH/c+fZzuSyJUVXQTOIN9xuLs9dE/9HbV/A5IqdXHU1p2OfIMwBwOYJ9SFVGGldxeRCUJFyw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@esbuild/darwin-x64/0.17.14: - resolution: {integrity: sha512-zN0U8RWfrDttdFNkHqFYZtOH8hdi22z0pFm0aIJPsNC4QQZv7je8DWCX5iA4Zx6tRhS0CCc0XC2m7wKsbWEo5g==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@esbuild/freebsd-arm64/0.17.14: - resolution: {integrity: sha512-z0VcD4ibeZWVQCW1O7szaLxGsx54gcCnajEJMdYoYjLiq4g1jrP2lMq6pk71dbS5+7op/L2Aod+erw+EUr28/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/freebsd-x64/0.17.14: - resolution: {integrity: sha512-hd9mPcxfTgJlolrPlcXkQk9BMwNBvNBsVaUe5eNUqXut6weDQH8whcNaKNF2RO8NbpT6GY8rHOK2A9y++s+ehw==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-arm/0.17.14: - resolution: {integrity: sha512-BNTl+wSJ1omsH8s3TkQmIIIQHwvwJrU9u1ggb9XU2KTVM4TmthRIVyxSp2qxROJHhZuW/r8fht46/QE8hU8Qvg==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-arm64/0.17.14: - resolution: {integrity: sha512-FhAMNYOq3Iblcj9i+K0l1Fp/MHt+zBeRu/Qkf0LtrcFu3T45jcwB6A1iMsemQ42vR3GBhjNZJZTaCe3VFPbn9g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-ia32/0.17.14: - resolution: {integrity: sha512-91OK/lQ5y2v7AsmnFT+0EyxdPTNhov3y2CWMdizyMfxSxRqHazXdzgBKtlmkU2KYIc+9ZK3Vwp2KyXogEATYxQ==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-loong64/0.14.54: - resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-loong64/0.17.14: - resolution: {integrity: sha512-vp15H+5NR6hubNgMluqqKza85HcGJgq7t6rMH7O3Y6ApiOWPkvW2AJfNojUQimfTp6OUrACUXfR4hmpcENXoMQ==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-mips64el/0.17.14: - resolution: {integrity: sha512-90TOdFV7N+fgi6c2+GO9ochEkmm9kBAKnuD5e08GQMgMINOdOFHuYLPQ91RYVrnWwQ5683sJKuLi9l4SsbJ7Hg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-ppc64/0.17.14: - resolution: {integrity: sha512-NnBGeoqKkTugpBOBZZoktQQ1Yqb7aHKmHxsw43NddPB2YWLAlpb7THZIzsRsTr0Xw3nqiPxbA1H31ZMOG+VVPQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-riscv64/0.17.14: - resolution: {integrity: sha512-0qdlKScLXA8MGVy21JUKvMzCYWovctuP8KKqhtE5A6IVPq4onxXhSuhwDd2g5sRCzNDlDjitc5sX31BzDoL5Fw==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-s390x/0.17.14: - resolution: {integrity: sha512-Hdm2Jo1yaaOro4v3+6/zJk6ygCqIZuSDJHdHaf8nVH/tfOuoEX5Riv03Ka15LmQBYJObUTNS1UdyoMk0WUn9Ww==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-x64/0.17.14: - resolution: {integrity: sha512-8KHF17OstlK4DuzeF/KmSgzrTWQrkWj5boluiiq7kvJCiQVzUrmSkaBvcLB2UgHpKENO2i6BthPkmUhNDaJsVw==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/netbsd-x64/0.17.14: - resolution: {integrity: sha512-nVwpqvb3yyXztxIT2+VsxJhB5GCgzPdk1n0HHSnchRAcxqKO6ghXwHhJnr0j/B+5FSyEqSxF4q03rbA2fKXtUQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/openbsd-x64/0.17.14: - resolution: {integrity: sha512-1RZ7uQQ9zcy/GSAJL1xPdN7NDdOOtNEGiJalg/MOzeakZeTrgH/DoCkbq7TaPDiPhWqnDF+4bnydxRqQD7il6g==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/sunos-x64/0.17.14: - resolution: {integrity: sha512-nqMjDsFwv7vp7msrwWRysnM38Sd44PKmW8EzV01YzDBTcTWUpczQg6mGao9VLicXSgW/iookNK6AxeogNVNDZA==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-arm64/0.17.14: - resolution: {integrity: sha512-xrD0mccTKRBBIotrITV7WVQAwNJ5+1va6L0H9zN92v2yEdjfAN7864cUaZwJS7JPEs53bDTzKFbfqVlG2HhyKQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-ia32/0.17.14: - resolution: {integrity: sha512-nXpkz9bbJrLLyUTYtRotSS3t5b+FOuljg8LgLdINWFs3FfqZMtbnBCZFUmBzQPyxqU87F8Av+3Nco/M3hEcu1w==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-x64/0.17.14: - resolution: {integrity: sha512-gPQmsi2DKTaEgG14hc3CHXHp62k8g6qr0Pas+I4lUxRMugGSATh/Bi8Dgusoz9IQ0IfdrvLpco6kujEIBoaogA==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@eslint-community/eslint-utils/4.4.0_eslint@7.30.0: - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 7.30.0 - eslint-visitor-keys: 3.4.0 - dev: true - - /@eslint-community/eslint-utils/4.4.0_eslint@8.36.0: - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.36.0 - eslint-visitor-keys: 3.4.0 - dev: true - - /@eslint-community/eslint-utils/4.4.0_eslint@8.7.0: - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.7.0 - eslint-visitor-keys: 3.4.0 - dev: false - - /@eslint-community/regexpp/4.4.1: - resolution: {integrity: sha512-BISJ6ZE4xQsuL/FmsyRaiffpq977bMlsKfGHTQrOGFErfByxIe6iZTxPf/00Zon9b9a7iUykfQwejN3s2ZW/Bw==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - /@eslint/eslintrc/0.4.3: - resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 7.3.1 - globals: 13.20.0 - ignore: 4.0.6 - import-fresh: 3.3.0 - js-yaml: 3.13.1 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@eslint/eslintrc/1.4.1: - resolution: {integrity: sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.5.0 - globals: 13.20.0 - ignore: 5.2.4 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - /@eslint/eslintrc/2.0.1: - resolution: {integrity: sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.5.0 - globals: 13.20.0 - ignore: 5.2.4 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@eslint/js/8.36.0: - resolution: {integrity: sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - - /@fastify/ajv-compiler/1.1.0: - resolution: {integrity: sha512-gvCOUNpXsWrIQ3A4aXCLIdblL0tDq42BG/2Xw7oxbil9h11uow10ztS2GuFazNBfjbrsZ5nl+nPl5jDSjj5TSg==} - dependencies: - ajv: 6.12.6 - dev: false - - /@fastify/forwarded/1.0.0: - resolution: {integrity: sha512-VoO+6WD0aRz8bwgJZ8pkkxjq7o/782cQ1j945HWg0obZMgIadYW3Pew0+an+k1QL7IPZHM3db5WF6OP6x4ymMA==} - engines: {node: '>= 10'} - dev: false - - /@fastify/proxy-addr/3.0.0: - resolution: {integrity: sha512-ty7wnUd/GeSqKTC2Jozsl5xGbnxUnEFC0On2/zPv/8ixywipQmVZwuWvNGnBoitJ2wixwVqofwXNua8j6Y62lQ==} - dependencies: - '@fastify/forwarded': 1.0.0 - ipaddr.js: 2.0.1 - dev: false - - /@fluentui/date-time-utilities/8.5.6: - resolution: {integrity: sha512-BS5EgnB5GFLg4p84GWqCjt6Pbnjnz0RZA94FAfVOYoqHcnjLURZ1BkQuorGdwS7ipaE4AVgNaQsPq90PsAfuXw==} - dependencies: - '@fluentui/set-version': 8.2.6 - tslib: 2.3.1 - dev: false - - /@fluentui/dom-utilities/2.2.6: - resolution: {integrity: sha512-yJOEiFj/TfR307hzZn15kNocC0P3j2BltrAJznhgXywMKJhIczATFTfj2len7YMHxLttnR5yDz/oYpyBLSk4rw==} - dependencies: - '@fluentui/set-version': 8.2.6 - tslib: 2.3.1 - dev: false - - /@fluentui/font-icons-mdl2/8.5.13_qjwx5m6wssz3lnb35xwkc3pz6q: - resolution: {integrity: sha512-3JjYN7lJZefpBdNPoQRFOiNpyVw+BOGynMkmYglnGjEWlg3uodawNi9lnKBODXCy31OHh900n9pAPFyNpdCbBg==} - dependencies: - '@fluentui/set-version': 8.2.6 - '@fluentui/style-utilities': 8.9.6_qjwx5m6wssz3lnb35xwkc3pz6q - '@fluentui/utilities': 8.13.9_qjwx5m6wssz3lnb35xwkc3pz6q - tslib: 2.3.1 - transitivePeerDependencies: - - '@types/react' - - react - dev: false - - /@fluentui/foundation-legacy/8.2.33_qjwx5m6wssz3lnb35xwkc3pz6q: - resolution: {integrity: sha512-Z1Nl1hmyICAjTwaU2fvIGenzczRfyf0P3oaRniwac4gAa5MidE6QlpAkEcwCka2QEG/qUjcx/cMacDGLN/Dd0A==} - peerDependencies: - '@types/react': '>=16.8.0 <19.0.0' - react: '>=16.8.0 <19.0.0' - dependencies: - '@fluentui/merge-styles': 8.5.7 - '@fluentui/set-version': 8.2.6 - '@fluentui/style-utilities': 8.9.6_qjwx5m6wssz3lnb35xwkc3pz6q - '@fluentui/utilities': 8.13.9_qjwx5m6wssz3lnb35xwkc3pz6q - '@types/react': 16.14.23 - react: 16.13.1 - tslib: 2.3.1 - dev: false - - /@fluentui/keyboard-key/0.4.6: - resolution: {integrity: sha512-p59zLGs3ucDPc7ZVaPxVCaQsfNwERDt3n+yLE0w/FFBlPWJcOSkiZHIieMhqk5ur5YGzbgs9WppPrtxNga23fw==} - dependencies: - tslib: 2.3.1 - dev: false - - /@fluentui/merge-styles/8.5.7: - resolution: {integrity: sha512-t/mOQTigj51n7z6VPZ1nlb9getkzoLVhN0aUbOJUSD5qvu0gZqSBh7Y9xIP6QeYWF4q6wcZhEggo8HOgYqaWQw==} - dependencies: - '@fluentui/set-version': 8.2.6 - tslib: 2.3.1 - dev: false - - /@fluentui/react-focus/8.8.19_qjwx5m6wssz3lnb35xwkc3pz6q: - resolution: {integrity: sha512-Gswx0aOazRVFJONmcsj5o6SI/ebWQeZyBAlOsdFeSUuK38aRlVy4DbwVQ+5tQ675u3wmf/+Ln2a0xCixOAVvWg==} - peerDependencies: - '@types/react': '>=16.8.0 <19.0.0' - react: '>=16.8.0 <19.0.0' - dependencies: - '@fluentui/keyboard-key': 0.4.6 - '@fluentui/merge-styles': 8.5.7 - '@fluentui/set-version': 8.2.6 - '@fluentui/style-utilities': 8.9.6_qjwx5m6wssz3lnb35xwkc3pz6q - '@fluentui/utilities': 8.13.9_qjwx5m6wssz3lnb35xwkc3pz6q - '@types/react': 16.14.23 - react: 16.13.1 - tslib: 2.3.1 - dev: false - - /@fluentui/react-hooks/8.6.20_qjwx5m6wssz3lnb35xwkc3pz6q: - resolution: {integrity: sha512-vb90tgc0nGWvahE2zuPPtEpknIfAA0ABq7/ro7+CAcKgDx2sleGZKRGdzKXdYS026OxjQ8TN2K7/D3OI1v4Rjg==} - peerDependencies: - '@types/react': '>=16.8.0 <19.0.0' - react: '>=16.8.0 <19.0.0' - dependencies: - '@fluentui/react-window-provider': 2.2.9_qjwx5m6wssz3lnb35xwkc3pz6q - '@fluentui/set-version': 8.2.6 - '@fluentui/utilities': 8.13.9_qjwx5m6wssz3lnb35xwkc3pz6q - '@types/react': 16.14.23 - react: 16.13.1 - tslib: 2.3.1 - dev: false - - /@fluentui/react-portal-compat-context/9.0.5_qjwx5m6wssz3lnb35xwkc3pz6q: - resolution: {integrity: sha512-vgGvv74jPi/salcxv37TCm06lOFn44CfNLX5wZw5HQIe9LYGUw/J7vkaniwNIzmQZsn62Y+fVxDS6Sq5S823tA==} - peerDependencies: - '@types/react': '>=16.8.0 <19.0.0' - react: '>=16.8.0 <19.0.0' - dependencies: - '@swc/helpers': 0.4.14 - '@types/react': 16.14.23 - react: 16.13.1 - dev: false - - /@fluentui/react-window-provider/2.2.9_qjwx5m6wssz3lnb35xwkc3pz6q: - resolution: {integrity: sha512-BRa23ITjwUgewS9ynzCnW2bJIgaNHwhPUY0htLKcYSSv3fG7iib91B6FVC7QqmXDBTia00kqVul1TZz5G0qrlQ==} - peerDependencies: - '@types/react': '>=16.8.0 <19.0.0' - react: '>=16.8.0 <19.0.0' - dependencies: - '@fluentui/set-version': 8.2.6 - '@types/react': 16.14.23 - react: 16.13.1 - tslib: 2.3.1 - dev: false - - /@fluentui/react/8.106.9_tlqvpdqnq63ssdllbmshthdmo4: - resolution: {integrity: sha512-MVBeGgqHgw/pp3ueJmg+PRulQ9HobhUgiNQeyp9Q1caAiRb9Xzjebdv+qr4XNhVChAmyYN7FlaepSFwhX2LlNQ==} - peerDependencies: - '@types/react': '>=16.8.0 <19.0.0' - '@types/react-dom': '>=16.8.0 <19.0.0' - react: '>=16.8.0 <19.0.0' - react-dom: '>=16.8.0 <19.0.0' - dependencies: - '@fluentui/date-time-utilities': 8.5.6 - '@fluentui/font-icons-mdl2': 8.5.13_qjwx5m6wssz3lnb35xwkc3pz6q - '@fluentui/foundation-legacy': 8.2.33_qjwx5m6wssz3lnb35xwkc3pz6q - '@fluentui/merge-styles': 8.5.7 - '@fluentui/react-focus': 8.8.19_qjwx5m6wssz3lnb35xwkc3pz6q - '@fluentui/react-hooks': 8.6.20_qjwx5m6wssz3lnb35xwkc3pz6q - '@fluentui/react-portal-compat-context': 9.0.5_qjwx5m6wssz3lnb35xwkc3pz6q - '@fluentui/react-window-provider': 2.2.9_qjwx5m6wssz3lnb35xwkc3pz6q - '@fluentui/set-version': 8.2.6 - '@fluentui/style-utilities': 8.9.6_qjwx5m6wssz3lnb35xwkc3pz6q - '@fluentui/theme': 2.6.25_qjwx5m6wssz3lnb35xwkc3pz6q - '@fluentui/utilities': 8.13.9_qjwx5m6wssz3lnb35xwkc3pz6q - '@microsoft/load-themed-styles': 1.10.295 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - tslib: 2.3.1 - dev: false - - /@fluentui/set-version/8.2.6: - resolution: {integrity: sha512-zXIfscQ1ZAiEpHc5taMrDEtTP2NtPBGlz2HbOpZiQ3aj/xcnUT7nT73ctb+Q2bHIqlDCHEaFRQxy/HG6koGYAA==} - dependencies: - tslib: 2.3.1 - dev: false - - /@fluentui/style-utilities/8.9.6_qjwx5m6wssz3lnb35xwkc3pz6q: - resolution: {integrity: sha512-glhexQzJNnLws66Tb7a0WPStYVE1tRy0QWwbtOdIRXsd/3CA1FZse76itss8/yqGakPin2PElkej/jTKpaRWew==} - dependencies: - '@fluentui/merge-styles': 8.5.7 - '@fluentui/set-version': 8.2.6 - '@fluentui/theme': 2.6.25_qjwx5m6wssz3lnb35xwkc3pz6q - '@fluentui/utilities': 8.13.9_qjwx5m6wssz3lnb35xwkc3pz6q - '@microsoft/load-themed-styles': 1.10.295 - tslib: 2.3.1 - transitivePeerDependencies: - - '@types/react' - - react - dev: false - - /@fluentui/theme/2.6.25_qjwx5m6wssz3lnb35xwkc3pz6q: - resolution: {integrity: sha512-slp+Tk+FEDj6HtZNWzckEMPLZMYfe2bECz4hLj/aq2ok51f2ztVTM8rjjmiJjOAidcTirF/gdYVbayc/5MOKag==} - peerDependencies: - '@types/react': '>=16.8.0 <19.0.0' - react: '>=16.8.0 <19.0.0' - dependencies: - '@fluentui/merge-styles': 8.5.7 - '@fluentui/set-version': 8.2.6 - '@fluentui/utilities': 8.13.9_qjwx5m6wssz3lnb35xwkc3pz6q - '@types/react': 16.14.23 - react: 16.13.1 - tslib: 2.3.1 - dev: false - - /@fluentui/utilities/8.13.9_qjwx5m6wssz3lnb35xwkc3pz6q: - resolution: {integrity: sha512-8SkDFN+v3FZ2DNQtnRHnUxkY2tVQo6ojHVPWsR5WAbfKDAdlDUWxf5bM+U/8d4E4v49x4HpKY1fqsrx3hLAhyA==} - peerDependencies: - '@types/react': '>=16.8.0 <19.0.0' - react: '>=16.8.0 <19.0.0' - dependencies: - '@fluentui/dom-utilities': 2.2.6 - '@fluentui/merge-styles': 8.5.7 - '@fluentui/set-version': 8.2.6 - '@types/react': 16.14.23 - react: 16.13.1 - tslib: 2.3.1 - dev: false - - /@gar/promisify/1.1.3: - resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} - dev: true - - /@graphql-tools/load-files/6.6.1_graphql@15.8.0: - resolution: {integrity: sha512-nd4GOjdD68bdJkHfRepILb0gGwF63mJI7uD4oJuuf2Kzeq8LorKa6WfyxUhdMuLmZhnx10zdAlWPfwv1NOAL4Q==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - dependencies: - globby: 11.1.0 - graphql: 15.8.0 - tslib: 2.5.0 - unixify: 1.0.0 - dev: true - - /@graphql-tools/merge/6.2.17_graphql@15.8.0: - resolution: {integrity: sha512-G5YrOew39fZf16VIrc49q3c8dBqQDD0ax5LYPiNja00xsXDi0T9zsEWVt06ApjtSdSF6HDddlu5S12QjeN8Tow==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 - dependencies: - '@graphql-tools/schema': 8.5.1_graphql@15.8.0 - '@graphql-tools/utils': 8.0.2_graphql@15.8.0 - graphql: 15.8.0 - tslib: 2.3.1 - dev: true - - /@graphql-tools/merge/8.3.1_graphql@15.8.0: - resolution: {integrity: sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - dependencies: - '@graphql-tools/utils': 8.9.0_graphql@15.8.0 - graphql: 15.8.0 - tslib: 2.5.0 - dev: true - - /@graphql-tools/schema/8.5.1_graphql@15.8.0: - resolution: {integrity: sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - dependencies: - '@graphql-tools/merge': 8.3.1_graphql@15.8.0 - '@graphql-tools/utils': 8.9.0_graphql@15.8.0 - graphql: 15.8.0 - tslib: 2.5.0 - value-or-promise: 1.0.11 - dev: true - - /@graphql-tools/utils/8.0.2_graphql@15.8.0: - resolution: {integrity: sha512-gzkavMOgbhnwkHJYg32Adv6f+LxjbQmmbdD5Hty0+CWxvaiuJq+nU6tzb/7VSU4cwhbNLx/lGu2jbCPEW1McZQ==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 - dependencies: - graphql: 15.8.0 - tslib: 2.3.1 - dev: true - - /@graphql-tools/utils/8.9.0_graphql@15.8.0: - resolution: {integrity: sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - dependencies: - graphql: 15.8.0 - tslib: 2.5.0 - dev: true - - /@humanwhocodes/config-array/0.11.8: - resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} - engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@humanwhocodes/config-array/0.5.0: - resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==} - engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@humanwhocodes/config-array/0.9.5: - resolution: {integrity: sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==} - engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - /@humanwhocodes/module-importer/1.0.1: - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - dev: true - - /@humanwhocodes/object-schema/1.2.1: - resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} - - /@istanbuljs/load-nyc-config/1.1.0: - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.13.1 - resolve-from: 5.0.0 - - /@istanbuljs/schema/0.1.3: - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - - /@jest/console/29.5.0: - resolution: {integrity: sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@types/node': 14.18.36 - chalk: 4.1.2 - jest-message-util: 29.5.0 - jest-util: 29.5.0 - slash: 3.0.0 - - /@jest/core/29.5.0: - resolution: {integrity: sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/console': 29.5.0 - '@jest/reporters': 29.5.0 - '@jest/test-result': 29.5.0_@types+node@14.18.36 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 14.18.36 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.8.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.5.0 - jest-config: 29.5.0_@types+node@14.18.36 - jest-haste-map: 29.5.0 - jest-message-util: 29.5.0 - jest-regex-util: 29.4.3 - jest-resolve: 29.5.0 - jest-resolve-dependencies: 29.5.0 - jest-runner: 29.5.0 - jest-runtime: 29.5.0 - jest-snapshot: 29.5.0 - jest-util: 29.5.0 - jest-validate: 29.5.0 - jest-watcher: 29.5.0 - micromatch: 4.0.5 - pretty-format: 29.5.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - supports-color - - ts-node - - /@jest/environment/29.5.0: - resolution: {integrity: sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/fake-timers': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 14.18.36 - jest-mock: 29.5.0 - - /@jest/expect-utils/29.5.0: - resolution: {integrity: sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.4.3 - - /@jest/expect/29.5.0: - resolution: {integrity: sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - expect: 29.5.0 - jest-snapshot: 29.5.0 - transitivePeerDependencies: - - supports-color - - /@jest/fake-timers/29.5.0: - resolution: {integrity: sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@sinonjs/fake-timers': 10.0.2 - '@types/node': 14.18.36 - jest-message-util: 29.5.0 - jest-mock: 29.5.0 - jest-util: 29.5.0 - - /@jest/globals/29.5.0: - resolution: {integrity: sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.5.0 - '@jest/expect': 29.5.0 - '@jest/types': 29.5.0 - jest-mock: 29.5.0 - transitivePeerDependencies: - - supports-color - - /@jest/reporters/29.5.0: - resolution: {integrity: sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.5.0 - '@jest/test-result': 29.5.0_@types+node@14.18.36 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@jridgewell/trace-mapping': 0.3.17 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/node': 14.18.36 - chalk: 4.1.2 - collect-v8-coverage: 1.0.1_@types+node@14.18.36 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 5.2.1 - istanbul-lib-report: 3.0.0 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.5 - jest-message-util: 29.5.0 - jest-util: 29.5.0 - jest-worker: 29.5.0 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.1.0 - transitivePeerDependencies: - - supports-color - - /@jest/schemas/29.4.3: - resolution: {integrity: sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@sinclair/typebox': 0.25.24 - - /@jest/source-map/29.4.3: - resolution: {integrity: sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jridgewell/trace-mapping': 0.3.17 - callsites: 3.1.0 - graceful-fs: 4.2.11 - - /@jest/test-result/29.5.0_@types+node@14.18.36: - resolution: {integrity: sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.5.0 - '@jest/types': 29.5.0 - '@types/istanbul-lib-coverage': 2.0.4 - collect-v8-coverage: 1.0.1_@types+node@14.18.36 - jest-haste-map: 29.5.0 - jest-resolve: 29.5.0 - transitivePeerDependencies: - - '@types/node' - - /@jest/test-sequencer/29.5.0_@types+node@14.18.36: - resolution: {integrity: sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.5.0_@types+node@14.18.36 - graceful-fs: 4.2.11 - jest-haste-map: 29.5.0 - slash: 3.0.0 - transitivePeerDependencies: - - '@types/node' - - /@jest/transform/26.6.2: - resolution: {integrity: sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==} - engines: {node: '>= 10.14.2'} - dependencies: - '@babel/core': 7.20.12 - '@jest/types': 26.6.2 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 1.9.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 26.6.2 - jest-regex-util: 26.0.0 - jest-util: 26.6.2 - micromatch: 4.0.5 - pirates: 4.0.5 - slash: 3.0.0 - source-map: 0.6.1 - write-file-atomic: 3.0.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/transform/29.5.0: - resolution: {integrity: sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.20.12 - '@jest/types': 29.5.0 - '@jridgewell/trace-mapping': 0.3.17 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.5.0 - jest-regex-util: 29.4.3 - jest-util: 29.5.0 - micromatch: 4.0.5 - pirates: 4.0.5 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - - /@jest/types/26.6.2: - resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} - engines: {node: '>= 10.14.2'} - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 14.18.36 - '@types/yargs': 15.0.15 - chalk: 4.1.2 - dev: true - - /@jest/types/29.5.0: - resolution: {integrity: sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.4.3 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 14.18.36 - '@types/yargs': 17.0.24 - chalk: 4.1.2 - - /@jridgewell/gen-mapping/0.1.1: - resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 - - /@jridgewell/gen-mapping/0.3.2: - resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 - '@jridgewell/trace-mapping': 0.3.17 - - /@jridgewell/resolve-uri/3.1.0: - resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} - engines: {node: '>=6.0.0'} - - /@jridgewell/set-array/1.1.2: - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} - engines: {node: '>=6.0.0'} - - /@jridgewell/source-map/0.3.2: - resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==} - dependencies: - '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.17 - - /@jridgewell/sourcemap-codec/1.4.14: - resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} - - /@jridgewell/trace-mapping/0.3.17: - resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} - dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 - - /@jsii/check-node/1.50.0: - resolution: {integrity: sha512-CkL3EtRIxglzPraC2bR+plEw4pxrbCLUZRjTDxALjhJaO67SWyMUhtHkFerPH9vqIe7rQVkvrv6kJTwpNFRU5Q==} - engines: {node: '>= 10.3.0'} - dependencies: - chalk: 4.1.2 - semver: 7.3.8 - dev: true - - /@leichtgewicht/ip-codec/2.0.4: - resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==} - dev: false - - /@lifaon/path/2.1.0: - resolution: {integrity: sha512-E+eJpDdwenIQCaYMMuCnteR34qAvXtHhHKjZOPB+hK4+R1yGcmWLLAEl2aklxCHx6w5VCKc8imx9AT05FGHhBw==} - dev: false - - /@mdx-js/loader/1.6.22_react@16.13.1: - resolution: {integrity: sha512-9CjGwy595NaxAYp0hF9B/A0lH6C8Rms97e2JS9d3jVUtILn6pT5i5IV965ra3lIWc7Rs1GG1tBdVF7dCowYe6Q==} - dependencies: - '@mdx-js/mdx': 1.6.22 - '@mdx-js/react': 1.6.22_react@16.13.1 - loader-utils: 2.0.0 - transitivePeerDependencies: - - react - - supports-color - dev: true - - /@mdx-js/mdx/1.6.22: - resolution: {integrity: sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==} - dependencies: - '@babel/core': 7.12.9 - '@babel/plugin-syntax-jsx': 7.12.1_@babel+core@7.12.9 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.12.9 - '@mdx-js/util': 1.6.22 - babel-plugin-apply-mdx-type-prop: 1.6.22_@babel+core@7.12.9 - babel-plugin-extract-import-names: 1.6.22 - camelcase-css: 2.0.1 - detab: 2.0.4 - hast-util-raw: 6.0.1 - lodash.uniq: 4.5.0 - mdast-util-to-hast: 10.0.1 - remark-footnotes: 2.0.0 - remark-mdx: 1.6.22 - remark-parse: 8.0.3 - remark-squeeze-paragraphs: 4.0.0 - style-to-object: 0.3.0 - unified: 9.2.0 - unist-builder: 2.0.3 - unist-util-visit: 2.0.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@mdx-js/react/1.6.22_react@16.13.1: - resolution: {integrity: sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==} - peerDependencies: - react: ^16.13.1 || ^17.0.0 - dependencies: - react: 16.13.1 - dev: true - - /@mdx-js/util/1.6.22: - resolution: {integrity: sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==} - dev: true - - /@microsoft/api-extractor-model/7.27.0_@types+node@14.18.36: - resolution: {integrity: sha512-wHqIMiwSARmiuVLn/zmVpiRncq6hvBfC5GF+sjrN3w4FqVkqFYk7DetvfRNdy/3URdqqmYGrhJlcU9HpLnHOPg==} - dependencies: - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.59.1_@types+node@14.18.36 - transitivePeerDependencies: - - '@types/node' - dev: true - - /@microsoft/api-extractor/7.35.0_@types+node@14.18.36: - resolution: {integrity: sha512-yBGfPJeEtzk8sg2hE2/vOPRvnJBvstbWNGeyGV1jIEUSgytzQ0QPgPEkOsP2n7nBfnyRXmZaBa2vJPGOzVWy+g==} - hasBin: true - dependencies: - '@microsoft/api-extractor-model': 7.27.0_@types+node@14.18.36 - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.59.1_@types+node@14.18.36 - '@rushstack/rig-package': 0.3.19 - '@rushstack/ts-command-line': 4.13.3 - colors: 1.2.5 - lodash: 4.17.21 - resolve: 1.22.1 - semver: 7.3.8 - source-map: 0.6.1 - typescript: 5.0.4 - transitivePeerDependencies: - - '@types/node' - dev: true - - /@microsoft/load-themed-styles/1.10.295: - resolution: {integrity: sha512-W+IzEBw8a6LOOfRJM02dTT7BDZijxm+Z7lhtOAz1+y9vQm1Kdz9jlAO+qCEKsfxtUOmKilW8DIRqFw2aUgKeGg==} - dev: false - - /@microsoft/teams-js/1.3.0-beta.4: - resolution: {integrity: sha512-AxDfMpiVqh3hsqTxMEYtQoz866WB/sw/Jl0pgTLh6sMHHmIBNMd+E0pVcP9WNk8zTkr9LCphJ5SziU1C8BgZMA==} - dev: true - - /@microsoft/tsdoc-config/0.16.2: - resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==} - dependencies: - '@microsoft/tsdoc': 0.14.2 - ajv: 6.12.6 - jju: 1.4.0 - resolve: 1.19.0 - - /@microsoft/tsdoc/0.14.2: - resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} - - /@mrmlnc/readdir-enhanced/2.2.1: - resolution: {integrity: sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==} - engines: {node: '>=4'} - dependencies: - call-me-maybe: 1.0.2 - glob-to-regexp: 0.3.0 - dev: true - - /@nodelib/fs.scandir/2.1.5: - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - /@nodelib/fs.stat/1.1.3: - resolution: {integrity: sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==} - engines: {node: '>= 6'} - dev: true - - /@nodelib/fs.stat/2.0.5: - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - /@nodelib/fs.walk/1.2.8: - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.15.0 - - /@npmcli/fs/1.1.1: - resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==} - dependencies: - '@gar/promisify': 1.1.3 - semver: 7.3.8 - dev: true - - /@npmcli/move-file/1.1.2: - resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==} - engines: {node: '>=10'} - deprecated: This functionality has been moved to @npmcli/fs - dependencies: - mkdirp: 1.0.4 - rimraf: 3.0.2 - dev: true - - /@opentelemetry/api/1.4.1: - resolution: {integrity: sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==} - engines: {node: '>=8.0.0'} - dev: false - - /@pmmmwh/react-refresh-webpack-plugin/0.5.10_yceubsmjd6jm3woocckpqejnhy: - resolution: {integrity: sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==} - engines: {node: '>= 10.13'} - peerDependencies: - '@types/webpack': 4.x || 5.x - react-refresh: '>=0.10.0 <1.0.0' - sockjs-client: ^1.4.0 - type-fest: '>=0.17.0 <4.0.0' - webpack: '>=4.43.0 <6.0.0' - webpack-dev-server: 3.x || 4.x - webpack-hot-middleware: 2.x - webpack-plugin-serve: 0.x || 1.x - peerDependenciesMeta: - '@types/webpack': - optional: true - sockjs-client: - optional: true - type-fest: - optional: true - webpack-dev-server: - optional: true - webpack-hot-middleware: - optional: true - webpack-plugin-serve: - optional: true - dependencies: - ansi-html-community: 0.0.8 - common-path-prefix: 3.0.0 - core-js-pure: 3.29.1 - error-stack-parser: 2.1.4 - find-up: 5.0.0 - html-entities: 2.3.3 - loader-utils: 2.0.4 - react-refresh: 0.11.0 - schema-utils: 3.1.2 - source-map: 0.7.4 - webpack: 4.44.2 - dev: true - - /@pnpm/crypto.base32-hash/1.0.1: - resolution: {integrity: sha512-pzAXNn6KxTA3kbcI3iEnYs4vtH51XEVqmK/1EiD18MaPKylhqy8UvMJK3zKG+jeP82cqQbozcTGm4yOQ8i3vNw==} - engines: {node: '>=14.6'} - dependencies: - rfc4648: 1.5.2 - dev: false - - /@pnpm/error/1.4.0: - resolution: {integrity: sha512-vxkRrkneBPVmP23kyjnYwVOtipwlSl6UfL+h+Xa3TrABJTz5rYBXemlTsU5BzST8U4pD7YDkTb3SQu+MMuIDKA==} - engines: {node: '>=10.16'} - dev: false - - /@pnpm/link-bins/5.3.25: - resolution: {integrity: sha512-9Xq8lLNRHFDqvYPXPgaiKkZ4rtdsm7izwM/cUsFDc5IMnG0QYIVBXQbgwhz2UvjUotbJrvfKLJaCfA3NGBnLDg==} - engines: {node: '>=10.16'} - dependencies: - '@pnpm/error': 1.4.0 - '@pnpm/package-bins': 4.1.0 - '@pnpm/read-modules-dir': 2.0.3 - '@pnpm/read-package-json': 4.0.0 - '@pnpm/read-project-manifest': 1.1.7 - '@pnpm/types': 6.4.0 - '@zkochan/cmd-shim': 5.4.1 - is-subdir: 1.2.0 - is-windows: 1.0.2 - mz: 2.7.0 - normalize-path: 3.0.0 - p-settle: 4.1.1 - ramda: 0.27.2 - dev: false - - /@pnpm/logger/4.0.0: - resolution: {integrity: sha512-SIShw+k556e7S7tLZFVSIHjCdiVog1qWzcKW2RbLEHPItdisAFVNIe34kYd9fMSswTlSRLS/qRjw3ZblzWmJ9Q==} - engines: {node: '>=12.17'} - dependencies: - bole: 4.0.1 - ndjson: 2.0.0 - dev: true - - /@pnpm/package-bins/4.1.0: - resolution: {integrity: sha512-57/ioGYLBbVRR80Ux9/q2i3y8Q+uQADc3c+Yse8jr/60YLOi3jcWz13e2Jy+ANYtZI258Qc5wk2X077rp0Ly/Q==} - engines: {node: '>=10.16'} - dependencies: - '@pnpm/types': 6.4.0 - fast-glob: 3.2.12 - is-subdir: 1.2.0 - dev: false - - /@pnpm/read-modules-dir/2.0.3: - resolution: {integrity: sha512-i9OgRvSlxrTS9a2oXokhDxvQzDtfqtsooJ9jaGoHkznue5aFCTSrNZFQ6M18o8hC03QWfnxaKi0BtOvNkKu2+A==} - engines: {node: '>=10.13'} - dependencies: - mz: 2.7.0 - dev: false - - /@pnpm/read-package-json/4.0.0: - resolution: {integrity: sha512-1cr2tEwe4YU6SI0Hmg+wnsr6yxBt2iJtqv6wrF84On8pS9hx4A2PLw3CIgbwxaG0b+ur5wzhNogwl4qD5FLFNg==} - engines: {node: '>=10.16'} - dependencies: - '@pnpm/error': 1.4.0 - '@pnpm/types': 6.4.0 - load-json-file: 6.2.0 - normalize-package-data: 3.0.3 - dev: false - - /@pnpm/read-project-manifest/1.1.7: - resolution: {integrity: sha512-tj8ExXZeDcMmMUj7D292ETe/RiEirr1X1wpT6Zy85z2MrFYoG9jfCJpps40OdZBNZBhxbuKtGPWKVSgXD0yrVw==} - engines: {node: '>=10.16'} - dependencies: - '@pnpm/error': 1.4.0 - '@pnpm/types': 6.4.0 - '@pnpm/write-project-manifest': 1.1.7 - detect-indent: 6.1.0 - fast-deep-equal: 3.1.3 - graceful-fs: 4.2.4 - is-windows: 1.0.2 - json5: 2.2.3 - parse-json: 5.2.0 - read-yaml-file: 2.1.0 - sort-keys: 4.2.0 - strip-bom: 4.0.0 - dev: false - - /@pnpm/types/6.4.0: - resolution: {integrity: sha512-nco4+4sZqNHn60Y4VE/fbtlShCBqipyUO+nKRPvDHqLrecMW9pzHWMVRxk4nrMRoeowj3q0rX3GYRBa8lsHTAg==} - engines: {node: '>=10.16'} - dev: false - - /@pnpm/types/8.9.0: - resolution: {integrity: sha512-3MYHYm8epnciApn6w5Fzx6sepawmsNU7l6lvIq+ER22/DPSrr83YMhU/EQWnf4lORn2YyiXFj0FJSyJzEtIGmw==} - engines: {node: '>=14.6'} - dev: false - - /@pnpm/write-project-manifest/1.1.7: - resolution: {integrity: sha512-OLkDZSqkA1mkoPNPvLFXyI6fb0enCuFji6Zfditi/CLAo9kmIhQFmEUDu4krSB8i908EljG8YwL5Xjxzm5wsWA==} - engines: {node: '>=10.16'} - dependencies: - '@pnpm/types': 6.4.0 - json5: 2.2.3 - mz: 2.7.0 - write-file-atomic: 3.0.3 - write-yaml-file: 4.2.0 - dev: false - - /@polka/url/1.0.0-next.21: - resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} - - /@popperjs/core/2.11.7: - resolution: {integrity: sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==} - dev: true - - /@radix-ui/colors/0.1.8: - resolution: {integrity: sha512-jwRMXYwC0hUo0mv6wGpuw254Pd9p/R6Td5xsRpOmaWkUHlooNWqVcadgyzlRumMq3xfOTXwJReU0Jv+EIy4Jbw==} - dev: true - - /@radix-ui/number/1.0.0: - resolution: {integrity: sha512-Ofwh/1HX69ZfJRiRBMTy7rgjAzHmwe4kW9C9Y99HTRUcYLUuVT0KESFj15rPjRgKJs20GPq8Bm5aEDJ8DuA3vA==} - dependencies: - '@babel/runtime': 7.21.0 - dev: true - - /@radix-ui/primitive/1.0.0: - resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==} - dependencies: - '@babel/runtime': 7.21.0 - dev: true - - /@radix-ui/react-checkbox/1.0.3_tlqvpdqnq63ssdllbmshthdmo4: - resolution: {integrity: sha512-55B8/vKzTuzxllH5sGJO4zaBf9gYpJuJRRzaOKm+0oAefRnMvbf+Kgww7IOANVN0w3z7agFJgtnXaZl8Uj95AA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-compose-refs': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-context': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-presence': 1.0.0_tlqvpdqnq63ssdllbmshthdmo4 - '@radix-ui/react-primitive': 1.0.2_tlqvpdqnq63ssdllbmshthdmo4 - '@radix-ui/react-use-controllable-state': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-use-previous': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-use-size': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - dev: true - - /@radix-ui/react-collection/1.0.2_tlqvpdqnq63ssdllbmshthdmo4: - resolution: {integrity: sha512-s8WdQQ6wNXpaxdZ308KSr8fEWGrg4un8i4r/w7fhiS4ElRNjk5rRcl0/C6TANG2LvLOGIxtzo/jAg6Qf73TEBw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@radix-ui/react-compose-refs': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-context': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-primitive': 1.0.2_tlqvpdqnq63ssdllbmshthdmo4 - '@radix-ui/react-slot': 1.0.1_sftehf4lpwv3vmg4pl5jfvkfmm - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - dev: true - - /@radix-ui/react-compose-refs/1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm: - resolution: {integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - dev: true - - /@radix-ui/react-context/1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm: - resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - dev: true - - /@radix-ui/react-direction/1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm: - resolution: {integrity: sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - dev: true - - /@radix-ui/react-icons/1.1.1_sftehf4lpwv3vmg4pl5jfvkfmm: - resolution: {integrity: sha512-xc3wQC59rsFylVbSusQCrrM+6695ppF730Q6yqzhRdqDcRNWIm2R6ngpzBoSOQMcwnq4p805F+Gr7xo4fmtN1A==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.x || ^17.x || ^18.x - dependencies: - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - dev: true - - /@radix-ui/react-id/1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm: - resolution: {integrity: sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@radix-ui/react-use-layout-effect': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - dev: true - - /@radix-ui/react-presence/1.0.0_tlqvpdqnq63ssdllbmshthdmo4: - resolution: {integrity: sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@radix-ui/react-compose-refs': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-use-layout-effect': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - dev: true - - /@radix-ui/react-primitive/1.0.2_tlqvpdqnq63ssdllbmshthdmo4: - resolution: {integrity: sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@radix-ui/react-slot': 1.0.1_sftehf4lpwv3vmg4pl5jfvkfmm - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - dev: true - - /@radix-ui/react-roving-focus/1.0.3_tlqvpdqnq63ssdllbmshthdmo4: - resolution: {integrity: sha512-stjCkIoMe6h+1fWtXlA6cRfikdBzCLp3SnVk7c48cv/uy3DTGoXhN76YaOYUJuy3aEDvDIKwKR5KSmvrtPvQPQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-collection': 1.0.2_tlqvpdqnq63ssdllbmshthdmo4 - '@radix-ui/react-compose-refs': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-context': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-direction': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-id': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-primitive': 1.0.2_tlqvpdqnq63ssdllbmshthdmo4 - '@radix-ui/react-use-callback-ref': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-use-controllable-state': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - dev: true - - /@radix-ui/react-scroll-area/1.0.3_tlqvpdqnq63ssdllbmshthdmo4: - resolution: {integrity: sha512-sBX9j8Q+0/jReNObEAveKIGXJtk3xUoSIx4cMKygGtO128QJyVDn01XNOFsyvihKDCTcu7SINzQ2jPAZEhIQtw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@radix-ui/number': 1.0.0 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-compose-refs': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-context': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-direction': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-presence': 1.0.0_tlqvpdqnq63ssdllbmshthdmo4 - '@radix-ui/react-primitive': 1.0.2_tlqvpdqnq63ssdllbmshthdmo4 - '@radix-ui/react-use-callback-ref': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-use-layout-effect': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - dev: true - - /@radix-ui/react-slot/1.0.1_sftehf4lpwv3vmg4pl5jfvkfmm: - resolution: {integrity: sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@radix-ui/react-compose-refs': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - dev: true - - /@radix-ui/react-tabs/1.0.3_tlqvpdqnq63ssdllbmshthdmo4: - resolution: {integrity: sha512-4CkF/Rx1GcrusI/JZ1Rvyx4okGUs6wEenWA0RG/N+CwkRhTy7t54y7BLsWUXrAz/GRbBfHQg/Odfs/RoW0CiRA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-context': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-direction': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-id': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@radix-ui/react-presence': 1.0.0_tlqvpdqnq63ssdllbmshthdmo4 - '@radix-ui/react-primitive': 1.0.2_tlqvpdqnq63ssdllbmshthdmo4 - '@radix-ui/react-roving-focus': 1.0.3_tlqvpdqnq63ssdllbmshthdmo4 - '@radix-ui/react-use-controllable-state': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - dev: true - - /@radix-ui/react-use-callback-ref/1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm: - resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - dev: true - - /@radix-ui/react-use-controllable-state/1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm: - resolution: {integrity: sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@radix-ui/react-use-callback-ref': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - dev: true - - /@radix-ui/react-use-layout-effect/1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm: - resolution: {integrity: sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - dev: true - - /@radix-ui/react-use-previous/1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm: - resolution: {integrity: sha512-RG2K8z/K7InnOKpq6YLDmT49HGjNmrK+fr82UCVKT2sW0GYfVnYp4wZWBooT/EYfQ5faA9uIjvsuMMhH61rheg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - dev: true - - /@radix-ui/react-use-size/1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm: - resolution: {integrity: sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - dependencies: - '@babel/runtime': 7.21.0 - '@radix-ui/react-use-layout-effect': 1.0.0_sftehf4lpwv3vmg4pl5jfvkfmm - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - react: 16.13.1 - dev: true - - /@reduxjs/toolkit/1.8.6_qfynotfwlyrsyq662adyrweaoe: - resolution: {integrity: sha512-4Ia/Loc6WLmdSOzi7k5ff7dLK8CgG2b8aqpLsCAJhazAzGdp//YBUSaj0ceW6a3kDBDNRrq5CRwyCS0wBiL1ig==} - peerDependencies: - react: ^16.9.0 || ^17.0.0 || ^18 - react-redux: ^7.2.1 || ^8.0.2 - peerDependenciesMeta: - react: - optional: true - react-redux: - optional: true - dependencies: - immer: 9.0.21 - react: 16.13.1 - react-redux: 8.0.5_mq2cyprinb6qi7hdzoedcdddgq - redux: 4.2.1 - redux-thunk: 2.4.2_redux@4.2.1 - reselect: 4.1.7 - dev: false - - /@remix-run/router/1.4.0: - resolution: {integrity: sha512-BJ9SxXux8zAg991UmT8slpwpsd31K1dHHbD3Ba4VzD+liLQ4WAMSxQp2d2ZPRPfN0jN2NPRowcSSoM7lCaF08Q==} - engines: {node: '>=14'} - dev: true - - /@rushstack/eslint-config/3.3.0_2aulnmwxyjhjxqmg3aruit533m: - resolution: {integrity: sha512-7eqoCDc52QK07yTDD7txyv1/5kt5jPfo4NnLgqX3NlMNBCMWSuWTJyCPXLZiwVLWAOjcPSxN8/10WuEIQkGMiw==} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '>=4.7.0' - dependencies: - '@rushstack/eslint-patch': 1.3.0 - '@rushstack/eslint-plugin': 0.12.0_2aulnmwxyjhjxqmg3aruit533m - '@rushstack/eslint-plugin-packlets': 0.7.0_2aulnmwxyjhjxqmg3aruit533m - '@rushstack/eslint-plugin-security': 0.6.0_2aulnmwxyjhjxqmg3aruit533m - '@typescript-eslint/eslint-plugin': 5.59.7_c3hx2p4vcdwcxdeh6yw5lktxre - '@typescript-eslint/experimental-utils': 5.59.7_2aulnmwxyjhjxqmg3aruit533m - '@typescript-eslint/parser': 5.59.7_2aulnmwxyjhjxqmg3aruit533m - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - eslint: 7.30.0 - eslint-plugin-promise: 6.0.1_eslint@7.30.0 - eslint-plugin-react: 7.27.1_eslint@7.30.0 - eslint-plugin-tsdoc: 0.2.17 - typescript: 5.0.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@rushstack/eslint-patch/1.3.0: - resolution: {integrity: sha512-IthPJsJR85GhOkp3Hvp8zFOPK5ynKn6STyHa/WZpioK7E1aYDiBzpqQPrngc14DszIUkIrdd3k9Iu0XSzlP/1w==} - dev: true - - /@rushstack/eslint-plugin-packlets/0.7.0_2aulnmwxyjhjxqmg3aruit533m: - resolution: {integrity: sha512-ftvrRvN7a5dfpDidDtrqJHH25JvL4huqk3a0S4zv5Rlh1kz6sfPvaKosDQowzEHBIWLvAtTN+P8ygWoyL0/XYw==} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@rushstack/tree-pattern': 0.2.4 - '@typescript-eslint/experimental-utils': 5.59.7_2aulnmwxyjhjxqmg3aruit533m - eslint: 7.30.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@rushstack/eslint-plugin-security/0.6.0_2aulnmwxyjhjxqmg3aruit533m: - resolution: {integrity: sha512-gJFBGoCCofU34GGFtR3zEjymEsRr2wDLu2u13mHVcDzXyZ3EDlt6ImnJtmn8VRDLGjJ7QFPOiYMSZQaArxWmGg==} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@rushstack/tree-pattern': 0.2.4 - '@typescript-eslint/experimental-utils': 5.59.7_2aulnmwxyjhjxqmg3aruit533m - eslint: 7.30.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@rushstack/eslint-plugin/0.12.0_2aulnmwxyjhjxqmg3aruit533m: - resolution: {integrity: sha512-kDB35khQeoDjabzHkHDs/NgvNNZzogkoU/UfrXnNSJJlcCxOxmhyscUQn5OptbixiiYCOFZh9TN9v2yGBZ3vJQ==} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@rushstack/tree-pattern': 0.2.4 - '@typescript-eslint/experimental-utils': 5.59.7_2aulnmwxyjhjxqmg3aruit533m - eslint: 7.30.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@rushstack/heft-config-file/0.12.2_@types+node@14.18.36: - resolution: {integrity: sha512-luAU7LLtW50A3wv3U5YaFCpu0zn9TrAt6rcN28B3eD1sgCFbYH+VCmrPYzJEbzWXJhl1WpGbvObR6avVh5cc8w==} - engines: {node: '>=10.13.0'} - dependencies: - '@rushstack/node-core-library': 3.59.1_@types+node@14.18.36 - '@rushstack/rig-package': 0.3.19 - jsonpath-plus: 4.0.0 - transitivePeerDependencies: - - '@types/node' - dev: true - - /@rushstack/heft-jest-plugin/0.5.12_4dchqclsvvgybys5sefjzqvuhm: - resolution: {integrity: sha512-aXfjRBCeeot9gM4z3rNJihe1CkUMZi8uv60XvUAoms/xQeKa7C9qXwW2qiCCbwyZN6WVeIXE46sWeS18xaK2Gw==} - peerDependencies: - '@rushstack/heft': ^0.50.6 - dependencies: - '@jest/core': 29.5.0 - '@jest/reporters': 29.5.0 - '@jest/transform': 29.5.0 - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-config-file': 0.12.2_@types+node@14.18.36 - '@rushstack/node-core-library': 3.59.1_@types+node@14.18.36 - jest-config: 29.5.0_@types+node@14.18.36 - jest-resolve: 29.5.0 - jest-snapshot: 29.5.0 - lodash: 4.17.21 - transitivePeerDependencies: - - '@types/node' - - node-notifier - - supports-color - - ts-node - dev: true - - /@rushstack/heft-jest-plugin/0.5.12_yqm7j6zyazsjiac2knmn54orge: - resolution: {integrity: sha512-aXfjRBCeeot9gM4z3rNJihe1CkUMZi8uv60XvUAoms/xQeKa7C9qXwW2qiCCbwyZN6WVeIXE46sWeS18xaK2Gw==} - peerDependencies: - '@rushstack/heft': ^0.50.6 - dependencies: - '@jest/core': 29.5.0 - '@jest/reporters': 29.5.0 - '@jest/transform': 29.5.0 - '@rushstack/heft': 0.50.6_@types+node@14.18.36 - '@rushstack/heft-config-file': 0.12.2_@types+node@14.18.36 - '@rushstack/node-core-library': 3.59.1_@types+node@14.18.36 - jest-config: 29.5.0_@types+node@14.18.36 - jest-resolve: 29.5.0 - jest-snapshot: 29.5.0 - lodash: 4.17.21 - transitivePeerDependencies: - - '@types/node' - - node-notifier - - supports-color - - ts-node - dev: true - - /@rushstack/heft-node-rig/1.13.0_4dchqclsvvgybys5sefjzqvuhm: - resolution: {integrity: sha512-Mq6vL/hbeYtrjWnZfab8EEtJXP7OVlXB8CTvwWBktyEHwL88fmZlB7fV08bCpLyShavEuF2th3fsg1TDFLyR7g==} - peerDependencies: - '@rushstack/heft': ^0.50.6 - dependencies: - '@microsoft/api-extractor': 7.35.0_@types+node@14.18.36 - '@rushstack/heft': link:../../apps/heft - '@rushstack/heft-jest-plugin': 0.5.12_4dchqclsvvgybys5sefjzqvuhm - eslint: 8.7.0 - jest-environment-node: 29.5.0 - typescript: 5.0.4 - transitivePeerDependencies: - - '@types/node' - - node-notifier - - supports-color - - ts-node - dev: true - - /@rushstack/heft-node-rig/1.13.0_yqm7j6zyazsjiac2knmn54orge: - resolution: {integrity: sha512-Mq6vL/hbeYtrjWnZfab8EEtJXP7OVlXB8CTvwWBktyEHwL88fmZlB7fV08bCpLyShavEuF2th3fsg1TDFLyR7g==} - peerDependencies: - '@rushstack/heft': ^0.50.6 - dependencies: - '@microsoft/api-extractor': 7.35.0_@types+node@14.18.36 - '@rushstack/heft': 0.50.6_@types+node@14.18.36 - '@rushstack/heft-jest-plugin': 0.5.12_yqm7j6zyazsjiac2knmn54orge - eslint: 8.7.0 - jest-environment-node: 29.5.0 - typescript: 5.0.4 - transitivePeerDependencies: - - '@types/node' - - node-notifier - - supports-color - - ts-node - dev: true - - /@rushstack/heft/0.50.6_@types+node@14.18.36: - resolution: {integrity: sha512-Y7vD26LF64ZBbLn3jXq3/b8tUY1JqYxQJip1PrqWUXfMFwnIgiHwrntklTKOJ5xLYSY9TcNZcdP1XSb7jDrEjg==} - engines: {node: '>=10.13.0'} - hasBin: true - dependencies: - '@rushstack/heft-config-file': 0.12.2_@types+node@14.18.36 - '@rushstack/node-core-library': 3.59.1_@types+node@14.18.36 - '@rushstack/rig-package': 0.3.19 - '@rushstack/ts-command-line': 4.13.3 - '@types/tapable': 1.0.6 - argparse: 1.0.10 - chokidar: 3.4.3 - fast-glob: 3.2.12 - glob: 7.0.6 - glob-escape: 0.0.2 - prettier: 2.3.2 - semver: 7.3.8 - tapable: 1.1.3 - true-case-path: 2.2.1 - transitivePeerDependencies: - - '@types/node' - dev: true - - /@rushstack/node-core-library/3.59.1_@types+node@14.18.36: - resolution: {integrity: sha512-iy/xaEhXGpX+DY1ZzAtNA+QPw+9+TJh773Im+JxG4R1fu00/vWq470UOEj6upxlUxmp0JxhnmNRxzfptHrn/Uw==} - peerDependencies: - '@types/node': '*' - peerDependenciesMeta: - '@types/node': - optional: true - dependencies: - '@types/node': 14.18.36 - colors: 1.2.5 - fs-extra: 7.0.1 - import-lazy: 4.0.0 - jju: 1.4.0 - resolve: 1.22.1 - semver: 7.3.8 - z-schema: 5.0.5 - dev: true - - /@rushstack/rig-package/0.3.19: - resolution: {integrity: sha512-2d0/Gn+qjOYneZbiHjn4SjyDwq9I0WagV37z0F1V71G+yONgH7wlt3K/UoNiDkhA8gTHYPRo2jz3CvttybwSag==} - dependencies: - resolve: 1.22.1 - strip-json-comments: 3.1.1 - dev: true - - /@rushstack/tree-pattern/0.2.4: - resolution: {integrity: sha512-H8i0OinWsdKM1TKEKPeRRTw85e+/7AIFpxm7q1blceZJhuxRBjCGAUZvQXZK4CMLx75xPqh/h1t5WHwFmElAPA==} - dev: true - - /@rushstack/ts-command-line/4.13.3: - resolution: {integrity: sha512-6aQIv/o1EgsC/+SpgUyRmzg2QIAL6sudEzw3sWzJKwWuQTc5XRsyZpyldfE7WAmIqMXDao9QG35/NYORjHm5Zw==} - dependencies: - '@types/argparse': 1.0.38 - argparse: 1.0.10 - colors: 1.2.5 - string-argv: 0.3.1 - dev: true - - /@serverless-stack/aws-lambda-ric/2.0.13: - resolution: {integrity: sha512-Aj4X2wMW6O5/PQoKoBdQGC3LwQyGTgW1XZtF0rs07WE9s6Q+46zWaVgURQjoNmTNQKpHSGJYo6B+ycp9u7/CSA==} - hasBin: true - dependencies: - node-addon-api: 3.2.1 - node-gyp: 8.1.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@serverless-stack/cli/0.67.0_constructs@10.0.130: - resolution: {integrity: sha512-QVpFHqbVezHVHQP2wvVlomcOQ1CliEUq08vG2FcLX7tyAKi0EXlVNGOTx9AcA51jBPATtQFy8J1jeUXQeJ7ZbA==} - hasBin: true - dependencies: - '@aws-cdk/aws-apigatewayv2-alpha': 2.7.0-alpha.0_4k7vh4k5vxgov5k3ju6unzjkgi - '@serverless-stack/aws-lambda-ric': 2.0.13 - '@serverless-stack/core': 0.67.2 - '@serverless-stack/resources': 0.67.0 - aws-cdk: 2.7.0 - aws-cdk-lib: 2.7.0_constructs@10.0.130 - aws-sdk: 2.1344.0 - body-parser: 1.20.2 - chalk: 4.1.2 - chokidar: 3.5.3 - cross-spawn: 7.0.3 - detect-port-alt: 1.1.6 - esbuild: 0.12.29 - esbuild-runner: 2.2.2_esbuild@0.12.29 - express: 4.18.1 - fs-extra: 9.1.0 - remeda: 0.0.32 - source-map-support: 0.5.21 - ws: 7.5.9 - yargs: 15.4.1 - transitivePeerDependencies: - - bufferutil - - constructs - - supports-color - - utf-8-validate - dev: true - - /@serverless-stack/core/0.67.2: - resolution: {integrity: sha512-9Z7dDCWRu38EGR9XL9cD2pZBNtX1vrpij9r4mOAae5PUk3XqROfzoEqObIhkU78Qzl/N74bKMu75z0IzXqa2rA==} - dependencies: - '@trpc/server': 9.27.3 - async-retry: 1.3.3 - aws-cdk: 2.7.0 - aws-cdk-lib: 2.7.0_constructs@10.0.130 - aws-sdk: 2.1344.0 - chalk: 4.1.2 - chokidar: 3.5.3 - ci-info: 3.8.0 - conf: 10.2.0 - constructs: 10.0.130 - cross-spawn: 7.0.3 - dataloader: 2.2.2 - dendriform-immer-patch-optimiser: 2.1.3_immer@9.0.21 - dotenv: 10.0.0 - dotenv-expand: 5.1.0 - esbuild: 0.14.54 - eslint: 8.36.0 - express: 4.18.1 - fs-extra: 9.1.0 - immer: 9.0.21 - js-yaml: 4.1.0 - log4js: 6.9.1 - picomatch: 2.3.1 - remeda: 0.0.32 - typescript: 4.9.5 - uuid: 8.3.2 - xstate: 4.26.1 - zod: 3.21.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@serverless-stack/resources/0.67.0: - resolution: {integrity: sha512-9ySIlDBvbQ5jko+HhwCzjAWPwwj0NRnzs6SdOfU8IqK/FTBnQW/4FdeKwr/0dffnVdua2eymyfzOx4sL81l/Tw==} - dependencies: - '@aws-cdk/aws-apigatewayv2-alpha': 2.7.0-alpha.0_4k7vh4k5vxgov5k3ju6unzjkgi - '@aws-cdk/aws-apigatewayv2-authorizers-alpha': 2.7.0-alpha.0_lhdwln6d4cy5rqcla435fclwy4 - '@aws-cdk/aws-apigatewayv2-integrations-alpha': 2.7.0-alpha.0_lhdwln6d4cy5rqcla435fclwy4 - '@aws-cdk/aws-appsync-alpha': 2.7.0-alpha.0_4k7vh4k5vxgov5k3ju6unzjkgi - '@graphql-tools/load-files': 6.6.1_graphql@15.8.0 - '@graphql-tools/merge': 6.2.17_graphql@15.8.0 - '@serverless-stack/core': 0.67.2 - archiver: 5.3.1 - aws-cdk-lib: 2.7.0_constructs@10.0.130 - chalk: 4.1.2 - constructs: 10.0.130 - cross-spawn: 7.0.3 - esbuild: 0.17.14 - fs-extra: 9.1.0 - glob: 7.2.3 - graphql: 15.8.0 - zip-local: 0.3.5 - transitivePeerDependencies: - - supports-color - dev: true - - /@sinclair/typebox/0.25.24: - resolution: {integrity: sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==} - - /@sindresorhus/is/4.6.0: - resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} - engines: {node: '>=10'} - - /@sinonjs/commons/2.0.0: - resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==} - dependencies: - type-detect: 4.0.8 - - /@sinonjs/fake-timers/10.0.2: - resolution: {integrity: sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==} - dependencies: - '@sinonjs/commons': 2.0.0 - - /@storybook/addon-actions/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-t2w3iLXFul+R/1ekYxIEzUOZZmvEa7EzUAVAuCHP4i6x0jBnTTZ7sAIUVRaxVREPguH5IqI/2OklYhKanty2Yw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true - dependencies: - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/components': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core-events': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - core-js: 3.29.1 - fast-deep-equal: 3.1.3 - global: 4.4.0 - lodash: 4.17.21 - polished: 4.2.2 - prop-types: 15.8.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - react-inspector: 5.1.1_react@16.13.1 - regenerator-runtime: 0.13.11 - telejson: 5.3.3 - ts-dedent: 2.2.0 - util-deprecate: 1.0.2 - uuid-browser: 3.1.0 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@storybook/addon-backgrounds/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-xQIV1SsjjRXP7P5tUoGKv+pul1EY8lsV7iBXQb5eGbp4AffBj3qoYBSZbX4uiazl21o0MQiQoeIhhaPVaFIIGg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true - dependencies: - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/client-logger': 6.4.22 - '@storybook/components': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core-events': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - core-js: 3.29.1 - global: 4.4.0 - memoizerific: 1.11.3 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - ts-dedent: 2.2.0 - util-deprecate: 1.0.2 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@storybook/addon-controls/6.4.22_cwhzethedycb6rate7ab7puama: - resolution: {integrity: sha512-f/M/W+7UTEUnr/L6scBMvksq+ZA8GTfh3bomE5FtWyOyaFppq9k8daKAvdYNlzXAOrUUsoZVJDgpb20Z2VBiSQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true - dependencies: - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/client-logger': 6.4.22 - '@storybook/components': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core-common': 6.4.22_hnbseasiobfcne23xkxxj6pamu - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/node-logger': 6.4.22 - '@storybook/store': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - core-js: 3.29.1 - lodash: 4.17.21 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - ts-dedent: 2.2.0 - transitivePeerDependencies: - - '@types/react' - - eslint - - supports-color - - typescript - - vue-template-compiler - - webpack-cli - - webpack-command - dev: true - - /@storybook/addon-docs/6.4.22_b6sd5r7nr23z52j3q5uzhew3l4: - resolution: {integrity: sha512-9j+i+W+BGHJuRe4jUrqk6ubCzP4fc1xgFS2o8pakRiZgPn5kUQPdkticmsyh1XeEJifwhqjKJvkEDrcsleytDA==} - peerDependencies: - '@storybook/angular': 6.4.22 - '@storybook/html': 6.4.22 - '@storybook/react': 6.4.22 - '@storybook/vue': 6.4.22 - '@storybook/vue3': 6.4.22 - '@storybook/web-components': 6.4.22 - lit: ^2.0.0 - lit-html: ^1.4.1 || ^2.0.0 - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - svelte: ^3.31.2 - sveltedoc-parser: ^4.1.0 - vue: ^2.6.10 || ^3.0.0 - webpack: '*' - peerDependenciesMeta: - '@storybook/angular': - optional: true - '@storybook/html': - optional: true - '@storybook/react': - optional: true - '@storybook/vue': - optional: true - '@storybook/vue3': - optional: true - '@storybook/web-components': - optional: true - lit: - optional: true - lit-html: - optional: true - react: - optional: true - react-dom: - optional: true - svelte: - optional: true - sveltedoc-parser: - optional: true - vue: - optional: true - webpack: - optional: true - dependencies: - '@babel/core': 7.20.12 - '@babel/generator': 7.21.3 - '@babel/parser': 7.21.3 - '@babel/plugin-transform-react-jsx': 7.21.0_@babel+core@7.20.12 - '@babel/preset-env': 7.20.2_@babel+core@7.20.12 - '@jest/transform': 26.6.2 - '@mdx-js/loader': 1.6.22_react@16.13.1 - '@mdx-js/mdx': 1.6.22 - '@mdx-js/react': 1.6.22_react@16.13.1 - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/builder-webpack4': 6.4.22_cwhzethedycb6rate7ab7puama - '@storybook/client-logger': 6.4.22 - '@storybook/components': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core': 6.4.22_qa6fh5b4egdwkein2ymbymqf6i - '@storybook/core-events': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/csf-tools': 6.4.22 - '@storybook/node-logger': 6.4.22 - '@storybook/postinstall': 6.4.22 - '@storybook/preview-web': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/react': 6.4.22_chiz7t5wmiss5c6b3yjvxsi5xy - '@storybook/source-loader': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/store': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - acorn: 7.4.1 - acorn-jsx: 5.3.2_acorn@7.4.1 - acorn-walk: 7.2.0 - core-js: 3.29.1 - doctrine: 3.0.0 - escodegen: 2.0.0 - fast-deep-equal: 3.1.3 - global: 4.4.0 - html-tags: 3.2.0 - js-string-escape: 1.0.1 - loader-utils: 2.0.4 - lodash: 4.17.21 - nanoid: 3.3.6 - p-limit: 3.1.0 - prettier: 2.3.0 - prop-types: 15.8.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - react-element-to-jsx-string: 14.3.4_5owmthsvj5ictknaj3ev736ofq - regenerator-runtime: 0.13.11 - remark-external-links: 8.0.0 - remark-slug: 6.1.0 - ts-dedent: 2.2.0 - util-deprecate: 1.0.2 - webpack: 4.44.2 - transitivePeerDependencies: - - '@storybook/builder-webpack5' - - '@storybook/manager-webpack5' - - '@types/react' - - bufferutil - - encoding - - eslint - - supports-color - - typescript - - utf-8-validate - - vue-template-compiler - - webpack-cli - - webpack-command - dev: true - - /@storybook/addon-essentials/6.4.22_lwdgx3dbz5iyny2jmkfmtofory: - resolution: {integrity: sha512-GTv291fqvWq2wzm7MruBvCGuWaCUiuf7Ca3kzbQ/WqWtve7Y/1PDsqRNQLGZrQxkXU0clXCqY1XtkTrtA3WGFQ==} - peerDependencies: - '@babel/core': ^7.9.6 - '@storybook/vue': 6.4.22 - '@storybook/web-components': 6.4.22 - babel-loader: ^8.0.0 - lit-html: ^1.4.1 || ^2.0.0-rc.3 - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - webpack: '*' - peerDependenciesMeta: - '@storybook/vue': - optional: true - '@storybook/web-components': - optional: true - lit-html: - optional: true - react: - optional: true - react-dom: - optional: true - webpack: - optional: true - dependencies: - '@babel/core': 7.20.12 - '@storybook/addon-actions': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/addon-backgrounds': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/addon-controls': 6.4.22_cwhzethedycb6rate7ab7puama - '@storybook/addon-docs': 6.4.22_b6sd5r7nr23z52j3q5uzhew3l4 - '@storybook/addon-measure': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/addon-outline': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/addon-toolbars': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/addon-viewport': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/node-logger': 6.4.22 - babel-loader: 8.2.5_tb555f6titdaodihyrbadfrjbq - core-js: 3.29.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - ts-dedent: 2.2.0 - webpack: 4.44.2 - transitivePeerDependencies: - - '@storybook/angular' - - '@storybook/builder-webpack5' - - '@storybook/html' - - '@storybook/manager-webpack5' - - '@storybook/react' - - '@storybook/vue3' - - '@types/react' - - bufferutil - - encoding - - eslint - - lit - - supports-color - - svelte - - sveltedoc-parser - - typescript - - utf-8-validate - - vue - - vue-template-compiler - - webpack-cli - - webpack-command - dev: true - - /@storybook/addon-links/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-OSOyDnTXnmcplJHlXTYUTMkrfpLqxtHp2R69IXfAyI1e8WNDb79mXflrEXDA/RSNEliLkqYwCyYby7gDMGds5Q==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true - dependencies: - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/client-logger': 6.4.22 - '@storybook/core-events': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/router': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@types/qs': 6.9.7 - core-js: 3.29.1 - global: 4.4.0 - prop-types: 15.8.1 - qs: 6.11.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - ts-dedent: 2.2.0 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@storybook/addon-measure/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-CjDXoCNIXxNfXfgyJXPc0McjCcwN1scVNtHa9Ckr+zMjiQ8pPHY7wDZCQsG69KTqcWHiVfxKilI82456bcHYhQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true - dependencies: - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/client-logger': 6.4.22 - '@storybook/components': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core-events': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - core-js: 3.29.1 - global: 4.4.0 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@storybook/addon-outline/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-VIMEzvBBRbNnupGU7NV0ahpFFb6nKVRGYWGREjtABdFn2fdKr1YicOHFe/3U7hRGjb5gd+VazSvyUvhaKX9T7Q==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true - dependencies: - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/client-logger': 6.4.22 - '@storybook/components': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core-events': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - core-js: 3.29.1 - global: 4.4.0 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - ts-dedent: 2.2.0 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@storybook/addon-toolbars/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-FFyj6XDYpBBjcUu6Eyng7R805LUbVclEfydZjNiByAoDVyCde9Hb4sngFxn/T4fKAfBz/32HKVXd5iq4AHYtLg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true - dependencies: - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/components': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - core-js: 3.29.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@storybook/addon-viewport/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-6jk0z49LemeTblez5u2bYXYr6U+xIdLbywe3G283+PZCBbEDE6eNYy2d2HDL+LbCLbezJBLYPHPalElphjJIcw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true - dependencies: - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/client-logger': 6.4.22 - '@storybook/components': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core-events': 6.4.22 - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - core-js: 3.29.1 - global: 4.4.0 - memoizerific: 1.11.3 - prop-types: 15.8.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@storybook/addons/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-P/R+Jsxh7pawKLYo8MtE3QU/ilRFKbtCewV/T1o5U/gm8v7hKQdFz3YdRMAra4QuCY8bQIp7MKd2HrB5aH5a1A==} - peerDependencies: - '@types/react': '>=16' - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - dependencies: - '@storybook/api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/channels': 6.4.22 - '@storybook/client-logger': 6.4.22 - '@storybook/core-events': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/router': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@types/react': 16.14.23 - '@types/webpack-env': 1.18.0 - core-js: 3.29.1 - global: 4.4.0 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - dev: true - - /@storybook/api/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-lAVI3o2hKupYHXFTt+1nqFct942up5dHH6YD7SZZJGyW21dwKC3HK1IzCsTawq3fZAKkgWFgmOO649hKk60yKg==} - peerDependencies: - '@types/react': '>=16' - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - dependencies: - '@storybook/channels': 6.4.22 - '@storybook/client-logger': 6.4.22 - '@storybook/core-events': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/router': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/semver': 7.3.2 - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@types/react': 16.14.23 - core-js: 3.29.1 - fast-deep-equal: 3.1.3 - global: 4.4.0 - lodash: 4.17.21 - memoizerific: 1.11.3 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - store2: 2.14.2 - telejson: 5.3.3 - ts-dedent: 2.2.0 - util-deprecate: 1.0.2 - dev: true - - /@storybook/builder-webpack4/6.4.22_cwhzethedycb6rate7ab7puama: - resolution: {integrity: sha512-A+GgGtKGnBneRFSFkDarUIgUTI8pYFdLmUVKEAGdh2hL+vLXAz9A46sEY7C8LQ85XWa8TKy3OTDxqR4+4iWj3A==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@babel/core': 7.20.12 - '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-decorators': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-proposal-export-default-from': 7.18.10_@babel+core@7.20.12 - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-object-rest-spread': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-transform-arrow-functions': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-block-scoping': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-classes': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-destructuring': 7.21.3_@babel+core@7.20.12 - '@babel/plugin-transform-for-of': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.20.12 - '@babel/plugin-transform-shorthand-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-spread': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-template-literals': 7.18.9_@babel+core@7.20.12 - '@babel/preset-env': 7.20.2_@babel+core@7.20.12 - '@babel/preset-react': 7.18.6_@babel+core@7.20.12 - '@babel/preset-typescript': 7.21.0_@babel+core@7.20.12 - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/channel-postmessage': 6.4.22 - '@storybook/channels': 6.4.22 - '@storybook/client-api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/client-logger': 6.4.22 - '@storybook/components': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core-common': 6.4.22_hnbseasiobfcne23xkxxj6pamu - '@storybook/core-events': 6.4.22 - '@storybook/node-logger': 6.4.22 - '@storybook/preview-web': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/router': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/semver': 7.3.2 - '@storybook/store': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/ui': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@types/node': 14.18.36 - '@types/webpack': 4.41.32 - autoprefixer: 9.8.8 - babel-loader: 8.2.5_tb555f6titdaodihyrbadfrjbq - babel-plugin-macros: 2.8.0 - babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.20.12 - case-sensitive-paths-webpack-plugin: 2.4.0 - core-js: 3.29.1 - css-loader: 3.6.0_webpack@4.44.2 - file-loader: 6.2.0_webpack@4.44.2 - find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 4.1.6 - glob: 7.2.3 - glob-promise: 3.4.0_glob@7.2.3 - global: 4.4.0 - html-webpack-plugin: 4.5.2_webpack@4.44.2 - pnp-webpack-plugin: 1.6.4_typescript@5.0.4 - postcss: 7.0.39 - postcss-flexbugs-fixes: 4.2.1 - postcss-loader: 4.3.0_4a2i7aa2i6hzz4ngguaxzo4tzi - raw-loader: 4.0.2_webpack@4.44.2 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - stable: 0.1.8 - style-loader: 1.3.0_webpack@4.44.2 - terser-webpack-plugin: 4.2.3_webpack@4.44.2 - ts-dedent: 2.2.0 - typescript: 5.0.4 - url-loader: 4.1.1_zmzwotvrfu62vdeozbyveyswza - util-deprecate: 1.0.2 - webpack: 4.44.2 - webpack-dev-middleware: 3.7.3_2jhnw6fokymnjfoumvhvkjoyjq - webpack-filter-warnings-plugin: 1.2.1_webpack@4.44.2 - webpack-hot-middleware: 2.25.3 - webpack-virtual-modules: 0.2.2 - transitivePeerDependencies: - - '@types/react' - - eslint - - supports-color - - vue-template-compiler - - webpack-cli - - webpack-command - dev: true - - /@storybook/builder-webpack4/6.4.22_r4lisitj37giayzglrmebdqyz4: - resolution: {integrity: sha512-A+GgGtKGnBneRFSFkDarUIgUTI8pYFdLmUVKEAGdh2hL+vLXAz9A46sEY7C8LQ85XWa8TKy3OTDxqR4+4iWj3A==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@babel/core': 7.20.12 - '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-decorators': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-proposal-export-default-from': 7.18.10_@babel+core@7.20.12 - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-object-rest-spread': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-transform-arrow-functions': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-block-scoping': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-classes': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-destructuring': 7.21.3_@babel+core@7.20.12 - '@babel/plugin-transform-for-of': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.20.12 - '@babel/plugin-transform-shorthand-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-spread': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-template-literals': 7.18.9_@babel+core@7.20.12 - '@babel/preset-env': 7.20.2_@babel+core@7.20.12 - '@babel/preset-react': 7.18.6_@babel+core@7.20.12 - '@babel/preset-typescript': 7.21.0_@babel+core@7.20.12 - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/channel-postmessage': 6.4.22 - '@storybook/channels': 6.4.22 - '@storybook/client-api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/client-logger': 6.4.22 - '@storybook/components': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core-common': 6.4.22_ugpf6zoh4655pnxdhdfk6eh3ui - '@storybook/core-events': 6.4.22 - '@storybook/node-logger': 6.4.22 - '@storybook/preview-web': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/router': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/semver': 7.3.2 - '@storybook/store': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/ui': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@types/node': 14.18.36 - '@types/webpack': 4.41.32 - autoprefixer: 9.8.8 - babel-loader: 8.2.5_tb555f6titdaodihyrbadfrjbq - babel-plugin-macros: 2.8.0 - babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.20.12 - case-sensitive-paths-webpack-plugin: 2.4.0 - core-js: 3.29.1 - css-loader: 3.6.0_webpack@4.44.2 - file-loader: 6.2.0_webpack@4.44.2 - find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 4.1.6 - glob: 7.2.3 - glob-promise: 3.4.0_glob@7.2.3 - global: 4.4.0 - html-webpack-plugin: 4.5.2_webpack@4.44.2 - pnp-webpack-plugin: 1.6.4_typescript@5.0.4 - postcss: 7.0.39 - postcss-flexbugs-fixes: 4.2.1 - postcss-loader: 4.3.0_4a2i7aa2i6hzz4ngguaxzo4tzi - raw-loader: 4.0.2_webpack@4.44.2 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - stable: 0.1.8 - style-loader: 1.3.0_webpack@4.44.2 - terser-webpack-plugin: 4.2.3_webpack@4.44.2 - ts-dedent: 2.2.0 - typescript: 5.0.4 - url-loader: 4.1.1_zmzwotvrfu62vdeozbyveyswza - util-deprecate: 1.0.2 - webpack: 4.44.2 - webpack-dev-middleware: 3.7.3_2jhnw6fokymnjfoumvhvkjoyjq - webpack-filter-warnings-plugin: 1.2.1_webpack@4.44.2 - webpack-hot-middleware: 2.25.3 - webpack-virtual-modules: 0.2.2 - transitivePeerDependencies: - - '@types/react' - - eslint - - supports-color - - vue-template-compiler - - webpack-cli - - webpack-command - dev: true - - /@storybook/channel-postmessage/6.4.22: - resolution: {integrity: sha512-gt+0VZLszt2XZyQMh8E94TqjHZ8ZFXZ+Lv/Mmzl0Yogsc2H+6VzTTQO4sv0IIx6xLbpgG72g5cr8VHsxW5kuDQ==} - dependencies: - '@storybook/channels': 6.4.22 - '@storybook/client-logger': 6.4.22 - '@storybook/core-events': 6.4.22 - core-js: 3.29.1 - global: 4.4.0 - qs: 6.11.1 - telejson: 5.3.3 - dev: true - - /@storybook/channel-websocket/6.4.22: - resolution: {integrity: sha512-Bm/FcZ4Su4SAK5DmhyKKfHkr7HiHBui6PNutmFkASJInrL9wBduBfN8YQYaV7ztr8ezoHqnYRx8sj28jpwa6NA==} - dependencies: - '@storybook/channels': 6.4.22 - '@storybook/client-logger': 6.4.22 - core-js: 3.29.1 - global: 4.4.0 - telejson: 5.3.3 - dev: true - - /@storybook/channels/6.4.22: - resolution: {integrity: sha512-cfR74tu7MLah1A8Rru5sak71I+kH2e/sY6gkpVmlvBj4hEmdZp4Puj9PTeaKcMXh9DgIDPNA5mb8yvQH6VcyxQ==} - dependencies: - core-js: 3.29.1 - ts-dedent: 2.2.0 - util-deprecate: 1.0.2 - dev: true - - /@storybook/cli/6.4.22_d66pwvnsbzorazhlf35vb7reqi: - resolution: {integrity: sha512-Paj5JtiYG6HjYYEiLm0SGg6GJ+ebJSvfbbYx5W+MNiojyMwrzkof+G2VEGk5AbE2JSkXvDQJ/9B8/SuS94yqvA==} - hasBin: true - peerDependencies: - jest: '*' - dependencies: - '@babel/core': 7.20.12 - '@babel/preset-env': 7.20.2_@babel+core@7.20.12 - '@storybook/codemod': 6.4.22_@babel+preset-env@7.20.2 - '@storybook/core-common': 6.4.22_hnbseasiobfcne23xkxxj6pamu - '@storybook/csf-tools': 6.4.22 - '@storybook/node-logger': 6.4.22 - '@storybook/semver': 7.3.2 - boxen: 5.1.2 - chalk: 4.1.2 - commander: 6.2.1 - core-js: 3.29.1 - cross-spawn: 7.0.3 - envinfo: 7.8.1 - express: 4.18.1 - find-up: 5.0.0 - fs-extra: 9.1.0 - get-port: 5.1.1 - globby: 11.1.0 - jest: 29.3.1_@types+node@14.18.36 - jscodeshift: 0.13.1_@babel+preset-env@7.20.2 - json5: 2.2.3 - leven: 3.1.0 - prompts: 2.4.2 - puppeteer-core: 2.1.1 - read-pkg-up: 7.0.1 - shelljs: 0.8.5 - strip-json-comments: 3.1.1 - ts-dedent: 2.2.0 - update-notifier: 5.1.0 - transitivePeerDependencies: - - eslint - - react - - react-dom - - supports-color - - typescript - - vue-template-compiler - - webpack-cli - - webpack-command - dev: true - - /@storybook/client-api/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-sO6HJNtrrdit7dNXQcZMdlmmZG1k6TswH3gAyP/DoYajycrTwSJ6ovkarzkO+0QcJ+etgra4TEdTIXiGHBMe/A==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - dependencies: - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/channel-postmessage': 6.4.22 - '@storybook/channels': 6.4.22 - '@storybook/client-logger': 6.4.22 - '@storybook/core-events': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/store': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@types/qs': 6.9.7 - '@types/webpack-env': 1.18.0 - core-js: 3.29.1 - fast-deep-equal: 3.1.3 - global: 4.4.0 - lodash: 4.17.21 - memoizerific: 1.11.3 - qs: 6.11.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - store2: 2.14.2 - synchronous-promise: 2.0.17 - ts-dedent: 2.2.0 - util-deprecate: 1.0.2 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@storybook/client-logger/6.4.22: - resolution: {integrity: sha512-LXhxh/lcDsdGnK8kimqfhu3C0+D2ylCSPPQNbU0IsLRmTfbpQYMdyl0XBjPdHiRVwlL7Gkw5OMjYemQgJ02zlw==} - dependencies: - core-js: 3.29.1 - global: 4.4.0 - dev: true - - /@storybook/codemod/6.4.22_@babel+preset-env@7.20.2: - resolution: {integrity: sha512-xqnTKUQU2W3vS3dce9s4bYhy15tIfAHIzog37jqpKYOHnByXpPj/KkluGePtv5I6cvMxqP8IhQzn+Eh/lVjM4Q==} - dependencies: - '@babel/types': 7.21.3 - '@mdx-js/mdx': 1.6.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/csf-tools': 6.4.22 - '@storybook/node-logger': 6.4.22 - core-js: 3.29.1 - cross-spawn: 7.0.3 - globby: 11.1.0 - jscodeshift: 0.13.1_@babel+preset-env@7.20.2 - lodash: 4.17.21 - prettier: 2.3.0 - recast: 0.19.1 - regenerator-runtime: 0.13.11 - transitivePeerDependencies: - - '@babel/preset-env' - - supports-color - dev: true - - /@storybook/components/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-dCbXIJF9orMvH72VtAfCQsYbe57OP7fAADtR6YTwfCw9Sm1jFuZr8JbblQ1HcrXEoJG21nOyad3Hm5EYVb/sBw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - dependencies: - '@popperjs/core': 2.11.7 - '@storybook/client-logger': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@types/color-convert': 2.0.0 - '@types/overlayscrollbars': 1.12.1 - '@types/react-syntax-highlighter': 11.0.5 - color-convert: 2.0.1 - core-js: 3.29.1 - fast-deep-equal: 3.1.3 - global: 4.4.0 - lodash: 4.17.21 - markdown-to-jsx: 7.2.0_react@16.13.1 - memoizerific: 1.11.3 - overlayscrollbars: 1.13.3 - polished: 4.2.2 - prop-types: 15.8.1 - react: 16.13.1 - react-colorful: 5.6.1_5owmthsvj5ictknaj3ev736ofq - react-dom: 16.13.1_react@16.13.1 - react-popper-tooltip: 3.1.1_5owmthsvj5ictknaj3ev736ofq - react-syntax-highlighter: 13.5.3_react@16.13.1 - react-textarea-autosize: 8.4.1_qjwx5m6wssz3lnb35xwkc3pz6q - regenerator-runtime: 0.13.11 - ts-dedent: 2.2.0 - util-deprecate: 1.0.2 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@storybook/core-client/6.4.22_qa6fh5b4egdwkein2ymbymqf6i: - resolution: {integrity: sha512-uHg4yfCBeM6eASSVxStWRVTZrAnb4FT6X6v/xDqr4uXCpCttZLlBzrSDwPBLNNLtCa7ntRicHM8eGKIOD5lMYQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - typescript: '*' - webpack: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/channel-postmessage': 6.4.22 - '@storybook/channel-websocket': 6.4.22 - '@storybook/client-api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/client-logger': 6.4.22 - '@storybook/core-events': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/preview-web': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/store': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/ui': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - airbnb-js-shims: 2.2.1 - ansi-to-html: 0.6.15 - core-js: 3.29.1 - global: 4.4.0 - lodash: 4.17.21 - qs: 6.11.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - ts-dedent: 2.2.0 - typescript: 5.0.4 - unfetch: 4.2.0 - util-deprecate: 1.0.2 - webpack: 4.44.2 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@storybook/core-common/6.4.22_hnbseasiobfcne23xkxxj6pamu: - resolution: {integrity: sha512-PD3N/FJXPNRHeQS2zdgzYFtqPLdi3MLwAicbnw+U3SokcsspfsAuyYHZOYZgwO8IAEKy6iCc7TpBdiSJZ/vAKQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@babel/core': 7.20.12 - '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-decorators': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-proposal-export-default-from': 7.18.10_@babel+core@7.20.12 - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-object-rest-spread': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-transform-arrow-functions': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-block-scoping': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-classes': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-destructuring': 7.21.3_@babel+core@7.20.12 - '@babel/plugin-transform-for-of': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.20.12 - '@babel/plugin-transform-shorthand-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-spread': 7.20.7_@babel+core@7.20.12 - '@babel/preset-env': 7.20.2_@babel+core@7.20.12 - '@babel/preset-react': 7.18.6_@babel+core@7.20.12 - '@babel/preset-typescript': 7.21.0_@babel+core@7.20.12 - '@babel/register': 7.21.0_@babel+core@7.20.12 - '@storybook/node-logger': 6.4.22 - '@storybook/semver': 7.3.2 - '@types/node': 14.18.36 - '@types/pretty-hrtime': 1.0.1 - babel-loader: 8.2.5_tb555f6titdaodihyrbadfrjbq - babel-plugin-macros: 3.1.0 - babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.20.12 - chalk: 4.1.2 - core-js: 3.29.1 - express: 4.18.1 - file-system-cache: 1.1.0 - find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 6.5.3_2if2pfw4ytlihdsiqpdavzlwg4 - fs-extra: 9.1.0 - glob: 7.2.3 - handlebars: 4.7.7 - interpret: 2.2.0 - json5: 2.2.3 - lazy-universal-dotenv: 3.0.1 - picomatch: 2.3.1 - pkg-dir: 5.0.0 - pretty-hrtime: 1.0.3 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - resolve-from: 5.0.0 - slash: 3.0.0 - telejson: 5.3.3 - ts-dedent: 2.2.0 - typescript: 5.0.4 - util-deprecate: 1.0.2 - webpack: 4.44.2 - transitivePeerDependencies: - - eslint - - supports-color - - vue-template-compiler - - webpack-cli - - webpack-command - dev: true - - /@storybook/core-common/6.4.22_ugpf6zoh4655pnxdhdfk6eh3ui: - resolution: {integrity: sha512-PD3N/FJXPNRHeQS2zdgzYFtqPLdi3MLwAicbnw+U3SokcsspfsAuyYHZOYZgwO8IAEKy6iCc7TpBdiSJZ/vAKQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@babel/core': 7.20.12 - '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-decorators': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-proposal-export-default-from': 7.18.10_@babel+core@7.20.12 - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-object-rest-spread': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-transform-arrow-functions': 7.20.7_@babel+core@7.20.12 - '@babel/plugin-transform-block-scoping': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-classes': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-destructuring': 7.21.3_@babel+core@7.20.12 - '@babel/plugin-transform-for-of': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.20.12 - '@babel/plugin-transform-shorthand-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-spread': 7.20.7_@babel+core@7.20.12 - '@babel/preset-env': 7.20.2_@babel+core@7.20.12 - '@babel/preset-react': 7.18.6_@babel+core@7.20.12 - '@babel/preset-typescript': 7.21.0_@babel+core@7.20.12 - '@babel/register': 7.21.0_@babel+core@7.20.12 - '@storybook/node-logger': 6.4.22 - '@storybook/semver': 7.3.2 - '@types/node': 14.18.36 - '@types/pretty-hrtime': 1.0.1 - babel-loader: 8.2.5_tb555f6titdaodihyrbadfrjbq - babel-plugin-macros: 3.1.0 - babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.20.12 - chalk: 4.1.2 - core-js: 3.29.1 - express: 4.18.1 - file-system-cache: 1.1.0 - find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 6.5.3_e3gvcbqyz74feggzy4n3jv5qrm - fs-extra: 9.1.0 - glob: 7.2.3 - handlebars: 4.7.7 - interpret: 2.2.0 - json5: 2.2.3 - lazy-universal-dotenv: 3.0.1 - picomatch: 2.3.1 - pkg-dir: 5.0.0 - pretty-hrtime: 1.0.3 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - resolve-from: 5.0.0 - slash: 3.0.0 - telejson: 5.3.3 - ts-dedent: 2.2.0 - typescript: 5.0.4 - util-deprecate: 1.0.2 - webpack: 4.44.2 - transitivePeerDependencies: - - eslint - - supports-color - - vue-template-compiler - - webpack-cli - - webpack-command - dev: true - - /@storybook/core-events/6.4.22: - resolution: {integrity: sha512-5GYY5+1gd58Gxjqex27RVaX6qbfIQmJxcbzbNpXGNSqwqAuIIepcV1rdCVm6I4C3Yb7/AQ3cN5dVbf33QxRIwA==} - dependencies: - core-js: 3.29.1 - dev: true - - /@storybook/core-server/6.4.22_cwhzethedycb6rate7ab7puama: - resolution: {integrity: sha512-wFh3e2fa0un1d4+BJP+nd3FVWUO7uHTqv3OGBfOmzQMKp4NU1zaBNdSQG7Hz6mw0fYPBPZgBjPfsJRwIYLLZyw==} - peerDependencies: - '@storybook/builder-webpack5': 6.4.22 - '@storybook/manager-webpack5': 6.4.22 - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - typescript: '*' - peerDependenciesMeta: - '@storybook/builder-webpack5': - optional: true - '@storybook/manager-webpack5': - optional: true - typescript: - optional: true - dependencies: - '@discoveryjs/json-ext': 0.5.7 - '@storybook/builder-webpack4': 6.4.22_cwhzethedycb6rate7ab7puama - '@storybook/core-client': 6.4.22_qa6fh5b4egdwkein2ymbymqf6i - '@storybook/core-common': 6.4.22_hnbseasiobfcne23xkxxj6pamu - '@storybook/core-events': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/csf-tools': 6.4.22 - '@storybook/manager-webpack4': 6.4.22_cwhzethedycb6rate7ab7puama - '@storybook/node-logger': 6.4.22 - '@storybook/semver': 7.3.2 - '@storybook/store': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@types/node': 14.18.36 - '@types/node-fetch': 2.6.2 - '@types/pretty-hrtime': 1.0.1 - '@types/webpack': 4.41.32 - better-opn: 2.1.1 - boxen: 5.1.2 - chalk: 4.1.2 - cli-table3: 0.6.3 - commander: 6.2.1 - compression: 1.7.4 - core-js: 3.29.1 - cpy: 8.1.2 - detect-port: 1.5.1 - express: 4.18.1 - file-system-cache: 1.1.0 - fs-extra: 9.1.0 - globby: 11.1.0 - ip: 1.1.8 - lodash: 4.17.21 - node-fetch: 2.6.7 - pretty-hrtime: 1.0.3 - prompts: 2.4.2 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - serve-favicon: 2.5.0 - slash: 3.0.0 - telejson: 5.3.3 - ts-dedent: 2.2.0 - typescript: 5.0.4 - util-deprecate: 1.0.2 - watchpack: 2.4.0 - webpack: 4.44.2 - ws: 8.13.0 - transitivePeerDependencies: - - '@types/react' - - bufferutil - - encoding - - eslint - - supports-color - - utf-8-validate - - vue-template-compiler - - webpack-cli - - webpack-command - dev: true - - /@storybook/core-server/6.4.22_r4lisitj37giayzglrmebdqyz4: - resolution: {integrity: sha512-wFh3e2fa0un1d4+BJP+nd3FVWUO7uHTqv3OGBfOmzQMKp4NU1zaBNdSQG7Hz6mw0fYPBPZgBjPfsJRwIYLLZyw==} - peerDependencies: - '@storybook/builder-webpack5': 6.4.22 - '@storybook/manager-webpack5': 6.4.22 - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - typescript: '*' - peerDependenciesMeta: - '@storybook/builder-webpack5': - optional: true - '@storybook/manager-webpack5': - optional: true - typescript: - optional: true - dependencies: - '@discoveryjs/json-ext': 0.5.7 - '@storybook/builder-webpack4': 6.4.22_r4lisitj37giayzglrmebdqyz4 - '@storybook/core-client': 6.4.22_qa6fh5b4egdwkein2ymbymqf6i - '@storybook/core-common': 6.4.22_ugpf6zoh4655pnxdhdfk6eh3ui - '@storybook/core-events': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/csf-tools': 6.4.22 - '@storybook/manager-webpack4': 6.4.22_r4lisitj37giayzglrmebdqyz4 - '@storybook/node-logger': 6.4.22 - '@storybook/semver': 7.3.2 - '@storybook/store': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@types/node': 14.18.36 - '@types/node-fetch': 2.6.2 - '@types/pretty-hrtime': 1.0.1 - '@types/webpack': 4.41.32 - better-opn: 2.1.1 - boxen: 5.1.2 - chalk: 4.1.2 - cli-table3: 0.6.3 - commander: 6.2.1 - compression: 1.7.4 - core-js: 3.29.1 - cpy: 8.1.2 - detect-port: 1.5.1 - express: 4.18.1 - file-system-cache: 1.1.0 - fs-extra: 9.1.0 - globby: 11.1.0 - ip: 1.1.8 - lodash: 4.17.21 - node-fetch: 2.6.7 - pretty-hrtime: 1.0.3 - prompts: 2.4.2 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - serve-favicon: 2.5.0 - slash: 3.0.0 - telejson: 5.3.3 - ts-dedent: 2.2.0 - typescript: 5.0.4 - util-deprecate: 1.0.2 - watchpack: 2.4.0 - webpack: 4.44.2 - ws: 8.13.0 - transitivePeerDependencies: - - '@types/react' - - bufferutil - - encoding - - eslint - - supports-color - - utf-8-validate - - vue-template-compiler - - webpack-cli - - webpack-command - dev: true - - /@storybook/core/6.4.22_hzk7bpzeiaw7tscmchnmaav4om: - resolution: {integrity: sha512-KZYJt7GM5NgKFXbPRZZZPEONZ5u/tE/cRbMdkn/zWN3He8+VP+65/tz8hbriI/6m91AWVWkBKrODSkeq59NgRA==} - peerDependencies: - '@storybook/builder-webpack5': 6.4.22 - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - typescript: '*' - webpack: '*' - peerDependenciesMeta: - '@storybook/builder-webpack5': - optional: true - typescript: - optional: true - dependencies: - '@storybook/core-client': 6.4.22_qa6fh5b4egdwkein2ymbymqf6i - '@storybook/core-server': 6.4.22_r4lisitj37giayzglrmebdqyz4 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - typescript: 5.0.4 - webpack: 4.44.2 - transitivePeerDependencies: - - '@storybook/manager-webpack5' - - '@types/react' - - bufferutil - - encoding - - eslint - - supports-color - - utf-8-validate - - vue-template-compiler - - webpack-cli - - webpack-command - dev: true - - /@storybook/core/6.4.22_qa6fh5b4egdwkein2ymbymqf6i: - resolution: {integrity: sha512-KZYJt7GM5NgKFXbPRZZZPEONZ5u/tE/cRbMdkn/zWN3He8+VP+65/tz8hbriI/6m91AWVWkBKrODSkeq59NgRA==} - peerDependencies: - '@storybook/builder-webpack5': 6.4.22 - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - typescript: '*' - webpack: '*' - peerDependenciesMeta: - '@storybook/builder-webpack5': - optional: true - typescript: - optional: true - dependencies: - '@storybook/core-client': 6.4.22_qa6fh5b4egdwkein2ymbymqf6i - '@storybook/core-server': 6.4.22_cwhzethedycb6rate7ab7puama - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - typescript: 5.0.4 - webpack: 4.44.2 - transitivePeerDependencies: - - '@storybook/manager-webpack5' - - '@types/react' - - bufferutil - - encoding - - eslint - - supports-color - - utf-8-validate - - vue-template-compiler - - webpack-cli - - webpack-command - dev: true - - /@storybook/csf-tools/6.4.22: - resolution: {integrity: sha512-LMu8MZAiQspJAtMBLU2zitsIkqQv7jOwX7ih5JrXlyaDticH7l2j6Q+1mCZNWUOiMTizj0ivulmUsSaYbpToSw==} - dependencies: - '@babel/core': 7.20.12 - '@babel/generator': 7.21.3 - '@babel/parser': 7.21.3 - '@babel/plugin-transform-react-jsx': 7.21.0_@babel+core@7.20.12 - '@babel/preset-env': 7.20.2_@babel+core@7.20.12 - '@babel/traverse': 7.21.3 - '@babel/types': 7.21.3 - '@mdx-js/mdx': 1.6.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - core-js: 3.29.1 - fs-extra: 9.1.0 - global: 4.4.0 - js-string-escape: 1.0.1 - lodash: 4.17.21 - prettier: 2.3.0 - regenerator-runtime: 0.13.11 - ts-dedent: 2.2.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@storybook/csf/0.0.2--canary.87bc651.0: - resolution: {integrity: sha512-ajk1Uxa+rBpFQHKrCcTmJyQBXZ5slfwHVEaKlkuFaW77it8RgbPJp/ccna3sgoi8oZ7FkkOyvv1Ve4SmwFqRqw==} - dependencies: - lodash: 4.17.21 - dev: true - - /@storybook/manager-webpack4/6.4.22_cwhzethedycb6rate7ab7puama: - resolution: {integrity: sha512-nzhDMJYg0vXdcG0ctwE6YFZBX71+5NYaTGkxg3xT7gbgnP1YFXn9gVODvgq3tPb3gcRapjyOIxUa20rV+r8edA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@babel/core': 7.20.12 - '@babel/plugin-transform-template-literals': 7.18.9_@babel+core@7.20.12 - '@babel/preset-react': 7.18.6_@babel+core@7.20.12 - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core-client': 6.4.22_qa6fh5b4egdwkein2ymbymqf6i - '@storybook/core-common': 6.4.22_hnbseasiobfcne23xkxxj6pamu - '@storybook/node-logger': 6.4.22 - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/ui': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@types/node': 14.18.36 - '@types/webpack': 4.41.32 - babel-loader: 8.2.5_tb555f6titdaodihyrbadfrjbq - case-sensitive-paths-webpack-plugin: 2.4.0 - chalk: 4.1.2 - core-js: 3.29.1 - css-loader: 3.6.0_webpack@4.44.2 - express: 4.18.1 - file-loader: 6.2.0_webpack@4.44.2 - file-system-cache: 1.1.0 - find-up: 5.0.0 - fs-extra: 9.1.0 - html-webpack-plugin: 4.5.2_webpack@4.44.2 - node-fetch: 2.6.7 - pnp-webpack-plugin: 1.6.4_typescript@5.0.4 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - read-pkg-up: 7.0.1 - regenerator-runtime: 0.13.11 - resolve-from: 5.0.0 - style-loader: 1.3.0_webpack@4.44.2 - telejson: 5.3.3 - terser-webpack-plugin: 4.2.3_webpack@4.44.2 - ts-dedent: 2.2.0 - typescript: 5.0.4 - url-loader: 4.1.1_zmzwotvrfu62vdeozbyveyswza - util-deprecate: 1.0.2 - webpack: 4.44.2 - webpack-dev-middleware: 3.7.3_2jhnw6fokymnjfoumvhvkjoyjq - webpack-virtual-modules: 0.2.2 - transitivePeerDependencies: - - '@types/react' - - encoding - - eslint - - supports-color - - vue-template-compiler - - webpack-cli - - webpack-command - dev: true - - /@storybook/manager-webpack4/6.4.22_r4lisitj37giayzglrmebdqyz4: - resolution: {integrity: sha512-nzhDMJYg0vXdcG0ctwE6YFZBX71+5NYaTGkxg3xT7gbgnP1YFXn9gVODvgq3tPb3gcRapjyOIxUa20rV+r8edA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@babel/core': 7.20.12 - '@babel/plugin-transform-template-literals': 7.18.9_@babel+core@7.20.12 - '@babel/preset-react': 7.18.6_@babel+core@7.20.12 - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core-client': 6.4.22_qa6fh5b4egdwkein2ymbymqf6i - '@storybook/core-common': 6.4.22_ugpf6zoh4655pnxdhdfk6eh3ui - '@storybook/node-logger': 6.4.22 - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/ui': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@types/node': 14.18.36 - '@types/webpack': 4.41.32 - babel-loader: 8.2.5_tb555f6titdaodihyrbadfrjbq - case-sensitive-paths-webpack-plugin: 2.4.0 - chalk: 4.1.2 - core-js: 3.29.1 - css-loader: 3.6.0_webpack@4.44.2 - express: 4.18.1 - file-loader: 6.2.0_webpack@4.44.2 - file-system-cache: 1.1.0 - find-up: 5.0.0 - fs-extra: 9.1.0 - html-webpack-plugin: 4.5.2_webpack@4.44.2 - node-fetch: 2.6.7 - pnp-webpack-plugin: 1.6.4_typescript@5.0.4 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - read-pkg-up: 7.0.1 - regenerator-runtime: 0.13.11 - resolve-from: 5.0.0 - style-loader: 1.3.0_webpack@4.44.2 - telejson: 5.3.3 - terser-webpack-plugin: 4.2.3_webpack@4.44.2 - ts-dedent: 2.2.0 - typescript: 5.0.4 - url-loader: 4.1.1_zmzwotvrfu62vdeozbyveyswza - util-deprecate: 1.0.2 - webpack: 4.44.2 - webpack-dev-middleware: 3.7.3_2jhnw6fokymnjfoumvhvkjoyjq - webpack-virtual-modules: 0.2.2 - transitivePeerDependencies: - - '@types/react' - - encoding - - eslint - - supports-color - - vue-template-compiler - - webpack-cli - - webpack-command - dev: true - - /@storybook/node-logger/6.4.22: - resolution: {integrity: sha512-sUXYFqPxiqM7gGH7gBXvO89YEO42nA4gBicJKZjj9e+W4QQLrftjF9l+mAw2K0mVE10Bn7r4pfs5oEZ0aruyyA==} - dependencies: - '@types/npmlog': 4.1.4 - chalk: 4.1.2 - core-js: 3.29.1 - npmlog: 5.0.1 - pretty-hrtime: 1.0.3 - dev: true - - /@storybook/postinstall/6.4.22: - resolution: {integrity: sha512-LdIvA+l70Mp5FSkawOC16uKocefc+MZLYRHqjTjgr7anubdi6y7W4n9A7/Yw4IstZHoknfL88qDj/uK5N+Ahzw==} - dependencies: - core-js: 3.29.1 - dev: true - - /@storybook/preview-web/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-sWS+sgvwSvcNY83hDtWUUL75O2l2LY/GTAS0Zp2dh3WkObhtuJ/UehftzPZlZmmv7PCwhb4Q3+tZDKzMlFxnKQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - dependencies: - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/channel-postmessage': 6.4.22 - '@storybook/client-logger': 6.4.22 - '@storybook/core-events': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/store': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - ansi-to-html: 0.6.15 - core-js: 3.29.1 - global: 4.4.0 - lodash: 4.17.21 - qs: 6.11.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - synchronous-promise: 2.0.17 - ts-dedent: 2.2.0 - unfetch: 4.2.0 - util-deprecate: 1.0.2 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@storybook/react-docgen-typescript-plugin/1.0.2-canary.253f8c1.0_2if2pfw4ytlihdsiqpdavzlwg4: - resolution: {integrity: sha512-mmoRG/rNzAiTbh+vGP8d57dfcR2aP+5/Ll03KKFyfy5FqWFm/Gh7u27ikx1I3LmVMI8n6jh5SdWMkMKon7/tDw==} - peerDependencies: - typescript: '>= 3.x' - webpack: '>= 4' - dependencies: - debug: 4.3.4 - endent: 2.1.0 - find-cache-dir: 3.3.2 - flat-cache: 3.0.4 - micromatch: 4.0.5 - react-docgen-typescript: 2.2.2_typescript@5.0.4 - tslib: 2.3.1 - typescript: 5.0.4 - webpack: 4.44.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@storybook/react/6.4.22_chiz7t5wmiss5c6b3yjvxsi5xy: - resolution: {integrity: sha512-5BFxtiguOcePS5Ty/UoH7C6odmvBYIZutfiy4R3Ua6FYmtxac5vP9r5KjCz1IzZKT8mCf4X+PuK1YvDrPPROgQ==} - engines: {node: '>=10.13.0'} - hasBin: true - peerDependencies: - '@babel/core': ^7.11.5 - '@types/node': '>=12' - '@types/react': '>=16' - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - typescript: '*' - peerDependenciesMeta: - '@babel/core': - optional: true - typescript: - optional: true - dependencies: - '@babel/core': 7.20.12 - '@babel/preset-flow': 7.18.6_@babel+core@7.20.12 - '@babel/preset-react': 7.18.6_@babel+core@7.20.12 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10_yceubsmjd6jm3woocckpqejnhy - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core': 6.4.22_qa6fh5b4egdwkein2ymbymqf6i - '@storybook/core-common': 6.4.22_hnbseasiobfcne23xkxxj6pamu - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/node-logger': 6.4.22 - '@storybook/react-docgen-typescript-plugin': 1.0.2-canary.253f8c1.0_2if2pfw4ytlihdsiqpdavzlwg4 - '@storybook/semver': 7.3.2 - '@storybook/store': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@types/node': 14.18.36 - '@types/react': 16.14.23 - '@types/webpack-env': 1.18.0 - babel-plugin-add-react-displayname: 0.0.5 - babel-plugin-named-asset-import: 0.3.8_@babel+core@7.20.12 - babel-plugin-react-docgen: 4.2.1 - core-js: 3.29.1 - global: 4.4.0 - lodash: 4.17.21 - prop-types: 15.8.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - react-refresh: 0.11.0 - read-pkg-up: 7.0.1 - regenerator-runtime: 0.13.11 - ts-dedent: 2.2.0 - typescript: 5.0.4 - webpack: 4.44.2 - transitivePeerDependencies: - - '@storybook/builder-webpack5' - - '@storybook/manager-webpack5' - - '@types/webpack' - - bufferutil - - encoding - - eslint - - sockjs-client - - supports-color - - type-fest - - utf-8-validate - - vue-template-compiler - - webpack-cli - - webpack-command - - webpack-dev-server - - webpack-hot-middleware - - webpack-plugin-serve - dev: true - - /@storybook/react/6.4.22_k3pzkoeaevtikrhe3xivvmfzgq: - resolution: {integrity: sha512-5BFxtiguOcePS5Ty/UoH7C6odmvBYIZutfiy4R3Ua6FYmtxac5vP9r5KjCz1IzZKT8mCf4X+PuK1YvDrPPROgQ==} - engines: {node: '>=10.13.0'} - hasBin: true - peerDependencies: - '@babel/core': ^7.11.5 - '@types/node': '>=12' - '@types/react': '>=16' - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - typescript: '*' - peerDependenciesMeta: - '@babel/core': - optional: true - typescript: - optional: true - dependencies: - '@babel/core': 7.20.12 - '@babel/preset-flow': 7.18.6_@babel+core@7.20.12 - '@babel/preset-react': 7.18.6_@babel+core@7.20.12 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10_yceubsmjd6jm3woocckpqejnhy - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core': 6.4.22_hzk7bpzeiaw7tscmchnmaav4om - '@storybook/core-common': 6.4.22_ugpf6zoh4655pnxdhdfk6eh3ui - '@storybook/csf': 0.0.2--canary.87bc651.0 - '@storybook/node-logger': 6.4.22 - '@storybook/react-docgen-typescript-plugin': 1.0.2-canary.253f8c1.0_2if2pfw4ytlihdsiqpdavzlwg4 - '@storybook/semver': 7.3.2 - '@storybook/store': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@types/node': 14.18.36 - '@types/react': 16.14.23 - '@types/webpack-env': 1.18.0 - babel-plugin-add-react-displayname: 0.0.5 - babel-plugin-named-asset-import: 0.3.8_@babel+core@7.20.12 - babel-plugin-react-docgen: 4.2.1 - core-js: 3.29.1 - global: 4.4.0 - lodash: 4.17.21 - prop-types: 15.8.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - react-refresh: 0.11.0 - read-pkg-up: 7.0.1 - regenerator-runtime: 0.13.11 - ts-dedent: 2.2.0 - typescript: 5.0.4 - webpack: 4.44.2 - transitivePeerDependencies: - - '@storybook/builder-webpack5' - - '@storybook/manager-webpack5' - - '@types/webpack' - - bufferutil - - encoding - - eslint - - sockjs-client - - supports-color - - type-fest - - utf-8-validate - - vue-template-compiler - - webpack-cli - - webpack-command - - webpack-dev-server - - webpack-hot-middleware - - webpack-plugin-serve - dev: true - - /@storybook/router/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-zeuE8ZgFhNerQX8sICQYNYL65QEi3okyzw7ynF58Ud6nRw4fMxSOHcj2T+nZCIU5ufozRL4QWD/Rg9P2s/HtLw==} - peerDependencies: - '@types/react': '>=16' - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - dependencies: - '@storybook/client-logger': 6.4.22 - '@types/react': 16.14.23 - core-js: 3.29.1 - fast-deep-equal: 3.1.3 - global: 4.4.0 - history: 5.0.0 - lodash: 4.17.21 - memoizerific: 1.11.3 - qs: 6.11.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - react-router: 6.9.0_qjwx5m6wssz3lnb35xwkc3pz6q - react-router-dom: 6.9.0_e4p5kqppx5gth2ijr2xdvk24ma - ts-dedent: 2.2.0 - dev: true - - /@storybook/semver/7.3.2: - resolution: {integrity: sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==} - engines: {node: '>=10'} - hasBin: true - dependencies: - core-js: 3.29.1 - find-up: 4.1.0 - dev: true - - /@storybook/source-loader/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-O4RxqPgRyOgAhssS6q1Rtc8LiOvPBpC1EqhCYWRV3K+D2EjFarfQMpjgPj18hC+QzpUSfzoBZYqsMECewEuLNw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - dependencies: - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/client-logger': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - core-js: 3.29.1 - estraverse: 5.3.0 - global: 4.4.0 - loader-utils: 2.0.4 - lodash: 4.17.21 - prettier: 2.3.0 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@storybook/store/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-lrmcZtYJLc2emO+1l6AG4Txm9445K6Pyv9cGAuhOJ9Kks0aYe0YtvMkZVVry0RNNAIv6Ypz72zyKc/QK+tZLAQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - dependencies: - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/client-logger': 6.4.22 - '@storybook/core-events': 6.4.22 - '@storybook/csf': 0.0.2--canary.87bc651.0 - core-js: 3.29.1 - fast-deep-equal: 3.1.3 - global: 4.4.0 - lodash: 4.17.21 - memoizerific: 1.11.3 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - regenerator-runtime: 0.13.11 - slash: 3.0.0 - stable: 0.1.8 - synchronous-promise: 2.0.17 - ts-dedent: 2.2.0 - util-deprecate: 1.0.2 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@storybook/theming/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-NVMKH/jxSPtnMTO4VCN1k47uztq+u9fWv4GSnzq/eezxdGg9ceGL4/lCrNGoNajht9xbrsZ4QvsJ/V2sVGM8wA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - dependencies: - '@emotion/core': 10.3.1_qjwx5m6wssz3lnb35xwkc3pz6q - '@emotion/is-prop-valid': 0.8.8 - '@emotion/serialize': 1.1.1 - '@emotion/styled': 10.3.0_tpm53lxjhhnjmtj6wgjp3t3pxi - '@emotion/utils': 1.2.0 - '@storybook/client-logger': 6.4.22 - core-js: 3.29.1 - deep-object-diff: 1.1.9 - emotion-theming: 10.3.0_tpm53lxjhhnjmtj6wgjp3t3pxi - global: 4.4.0 - memoizerific: 1.11.3 - polished: 4.2.2 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - resolve-from: 5.0.0 - ts-dedent: 2.2.0 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@storybook/ui/6.4.22_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-UVjMoyVsqPr+mkS1L7m30O/xrdIEgZ5SCWsvqhmyMUok3F3tRB+6M+OA5Yy+cIVfvObpA7MhxirUT1elCGXsWQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - dependencies: - '@emotion/core': 10.3.1_qjwx5m6wssz3lnb35xwkc3pz6q - '@storybook/addons': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/api': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/channels': 6.4.22 - '@storybook/client-logger': 6.4.22 - '@storybook/components': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/core-events': 6.4.22 - '@storybook/router': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - '@storybook/semver': 7.3.2 - '@storybook/theming': 6.4.22_e4p5kqppx5gth2ijr2xdvk24ma - copy-to-clipboard: 3.3.3 - core-js: 3.29.1 - core-js-pure: 3.29.1 - downshift: 6.1.12_react@16.13.1 - emotion-theming: 10.3.0_tpm53lxjhhnjmtj6wgjp3t3pxi - fuse.js: 3.6.1 - global: 4.4.0 - lodash: 4.17.21 - markdown-to-jsx: 7.2.0_react@16.13.1 - memoizerific: 1.11.3 - polished: 4.2.2 - qs: 6.11.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - react-draggable: 4.4.5_5owmthsvj5ictknaj3ev736ofq - react-helmet-async: 1.3.0_5owmthsvj5ictknaj3ev736ofq - react-sizeme: 3.0.2 - regenerator-runtime: 0.13.11 - resolve-from: 5.0.0 - store2: 2.14.2 - transitivePeerDependencies: - - '@types/react' - dev: true - - /@swc/helpers/0.4.14: - resolution: {integrity: sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==} - dependencies: - tslib: 2.5.0 - dev: false - - /@szmarczak/http-timer/4.0.6: - resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} - engines: {node: '>=10'} - dependencies: - defer-to-connect: 2.0.1 - - /@tootallnate/once/1.1.2: - resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} - engines: {node: '>= 6'} - dev: true - - /@tootallnate/once/2.0.0: - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} - engines: {node: '>= 10'} - - /@trpc/server/9.27.3: - resolution: {integrity: sha512-RHWD9xjE+A9UaQCVYkqjl0sbGaHfvlUqJH3e1I57F2ztJbMeFYoP47pVgjkg0CLYSuRDa3imtD4dVDZ4DcODjQ==} - dev: true - - /@trysound/sax/0.2.0: - resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} - engines: {node: '>=10.13.0'} - dev: false - - /@types/argparse/1.0.38: - resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} - - /@types/aws-lambda/8.10.93: - resolution: {integrity: sha512-Vsyi9ogDAY3REZDjYnXMRJJa62SDvxHXxJI5nGDQdZW058dDE+av/anynN2rLKbCKXDRNw3D/sQmqxVflZFi4A==} - dev: true - - /@types/babel__core/7.20.0: - resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==} - dependencies: - '@babel/parser': 7.21.3 - '@babel/types': 7.21.3 - '@types/babel__generator': 7.6.4 - '@types/babel__template': 7.4.1 - '@types/babel__traverse': 7.18.3 - - /@types/babel__generator/7.6.4: - resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} - dependencies: - '@babel/types': 7.21.3 - - /@types/babel__template/7.4.1: - resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} - dependencies: - '@babel/parser': 7.21.3 - '@babel/types': 7.21.3 - - /@types/babel__traverse/7.18.3: - resolution: {integrity: sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==} - dependencies: - '@babel/types': 7.21.3 - - /@types/body-parser/1.19.2: - resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} - dependencies: - '@types/connect': 3.4.35 - '@types/node': 14.18.36 - - /@types/bonjour/3.5.10: - resolution: {integrity: sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==} - dependencies: - '@types/node': 14.18.36 - dev: false - - /@types/cacheable-request/6.0.3: - resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} - dependencies: - '@types/http-cache-semantics': 4.0.1 - '@types/keyv': 3.1.4 - '@types/node': 14.18.36 - '@types/responselike': 1.0.0 - - /@types/cli-table/0.3.0: - resolution: {integrity: sha512-QnZUISJJXyhyD6L1e5QwXDV/A5i2W1/gl6D6YMc8u0ncPepbv/B4w3S+izVvtAg60m6h+JP09+Y/0zF2mojlFQ==} - dev: true - - /@types/color-convert/2.0.0: - resolution: {integrity: sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ==} - dependencies: - '@types/color-name': 1.1.1 - dev: true - - /@types/color-name/1.1.1: - resolution: {integrity: sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==} - dev: true - - /@types/configstore/6.0.0: - resolution: {integrity: sha512-GUvNiia85zTDDIx0iPrtF3pI8dwrQkfuokEqxqPDE55qxH0U5SZz4awVZjiJLWN2ZZRkXCUqgsMUbygXY+kytA==} - dev: true - - /@types/connect-history-api-fallback/1.3.5: - resolution: {integrity: sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==} - dependencies: - '@types/express-serve-static-core': 4.17.33 - '@types/node': 14.18.36 - dev: false - - /@types/connect/3.4.35: - resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} - dependencies: - '@types/node': 14.18.36 - - /@types/cors/2.8.13: - resolution: {integrity: sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==} - dependencies: - '@types/node': 14.18.36 - dev: true - - /@types/diff/5.0.1: - resolution: {integrity: sha512-XIpxU6Qdvp1ZE6Kr3yrkv1qgUab0fyf4mHYvW8N3Bx3PCsbN6or1q9/q72cv5jIFWolaGH08U9XyYoLLIykyKQ==} - dev: true - - /@types/enhanced-resolve/3.0.7: - resolution: {integrity: sha512-H23Fzk0BCz4LoKq1ricnLSRQuzoXTv57bGUwC+Cn84kKPaoHIS7bhFhfy4DzMeSBxoXc6jFziYoqpCab1U511w==} - dependencies: - '@types/node': 14.18.36 - '@types/tapable': 0.2.5 - dev: true - - /@types/eslint-scope/3.7.4: - resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} - dependencies: - '@types/eslint': 8.2.0 - '@types/estree': 0.0.50 - - /@types/eslint/8.2.0: - resolution: {integrity: sha512-74hbvsnc+7TEDa1z5YLSe4/q8hGYB3USNvCuzHUJrjPV6hXaq8IXcngCrHkuvFt0+8rFz7xYXrHgNayIX0UZvQ==} - dependencies: - '@types/estree': 0.0.50 - '@types/json-schema': 7.0.11 - - /@types/estree/0.0.50: - resolution: {integrity: sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==} - - /@types/estree/1.0.1: - resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} - - /@types/events/3.0.0: - resolution: {integrity: sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==} - dev: true - - /@types/express-serve-static-core/4.17.33: - resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==} - dependencies: - '@types/node': 14.18.36 - '@types/qs': 6.9.7 - '@types/range-parser': 1.2.4 - - /@types/express/4.17.13: - resolution: {integrity: sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==} - dependencies: - '@types/body-parser': 1.19.2 - '@types/express-serve-static-core': 4.17.33 - '@types/qs': 6.9.7 - '@types/serve-static': 1.15.1 - - /@types/fs-extra/7.0.0: - resolution: {integrity: sha512-ndoMMbGyuToTy4qB6Lex/inR98nPiNHacsgMPvy+zqMLgSxbt8VtWpDArpGp69h1fEDQHn1KB+9DWD++wgbwYA==} - dependencies: - '@types/node': 14.18.36 - dev: true - - /@types/glob/7.1.1: - resolution: {integrity: sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==} - dependencies: - '@types/events': 3.0.0 - '@types/minimatch': 3.0.5 - '@types/node': 14.18.36 - dev: true - - /@types/graceful-fs/4.1.6: - resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} - dependencies: - '@types/node': 14.18.36 - - /@types/hast/2.3.4: - resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==} - dependencies: - '@types/unist': 2.0.6 - dev: true - - /@types/heft-jest/1.0.1: - resolution: {integrity: sha512-cF2iEUpvGh2WgLowHVAdjI05xuDo+GwCA8hGV3Q5PBl8apjd6BTcpPFQ2uPlfUM7BLpgur2xpYo8VeBXopMI4A==} - dependencies: - '@types/jest': 29.2.5 - - /@types/hoist-non-react-statics/3.3.1: - resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==} - dependencies: - '@types/react': 16.14.23 - hoist-non-react-statics: 3.3.2 - dev: false - - /@types/html-minifier-terser/5.1.2: - resolution: {integrity: sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==} - - /@types/html-minifier-terser/6.1.0: - resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} - - /@types/http-cache-semantics/4.0.1: - resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} - - /@types/http-proxy/1.17.10: - resolution: {integrity: sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==} - dependencies: - '@types/node': 14.18.36 - - /@types/inquirer/7.3.1: - resolution: {integrity: sha512-osD38QVIfcdgsPCT0V3lD7eH0OFurX71Jft18bZrsVQWVRt6TuxRzlr0GJLrxoHZR2V5ph7/qP8se/dcnI7o0g==} - dependencies: - '@types/through': 0.0.30 - rxjs: 6.6.7 - dev: true - - /@types/is-function/1.0.1: - resolution: {integrity: sha512-A79HEEiwXTFtfY+Bcbo58M2GRYzCr9itHWzbzHVFNEYCcoU/MMGwYYf721gBrnhpj1s6RGVVha/IgNFnR0Iw/Q==} - dev: true - - /@types/istanbul-lib-coverage/2.0.4: - resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} - - /@types/istanbul-lib-report/3.0.0: - resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - - /@types/istanbul-reports/3.0.1: - resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} - dependencies: - '@types/istanbul-lib-report': 3.0.0 - - /@types/jest/23.3.13: - resolution: {integrity: sha512-ePl4l+7dLLmCucIwgQHAgjiepY++qcI6nb8eAwGNkB6OxmTe3Z9rQU3rSpomqu42PCCnlThZbOoxsf+qylJsLA==} - dev: true - - /@types/jest/28.1.1: - resolution: {integrity: sha512-C2p7yqleUKtCkVjlOur9BWVA4HgUQmEj/HWCt5WzZ5mLXrWnyIfl0wGuArc+kBXsy0ZZfLp+7dywB4HtSVYGVA==} - dependencies: - jest-matcher-utils: 27.5.1 - pretty-format: 27.5.1 - dev: true - - /@types/jest/29.2.5: - resolution: {integrity: sha512-H2cSxkKgVmqNHXP7TC2L/WUorrZu8ZigyRywfVzv6EyBlxj39n4C00hjXYQWsbwqgElaj/CiAeSRmk5GoaKTgw==} - dependencies: - expect: 29.5.0 - pretty-format: 29.5.0 - - /@types/jest/29.5.2: - resolution: {integrity: sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg==} - dependencies: - expect: 29.5.0 - pretty-format: 29.5.0 - dev: true - - /@types/jju/1.4.1: - resolution: {integrity: sha512-LFt+YA7Lv2IZROMwokZKiPNORAV5N3huMs3IKnzlE430HWhWYZ8b+78HiwJXJJP1V2IEjinyJURuRJfGoaFSIA==} - dev: true - - /@types/js-yaml/3.12.1: - resolution: {integrity: sha512-SGGAhXLHDx+PK4YLNcNGa6goPf9XRWQNAUUbffkwVGGXIxmDKWyGGL4inzq2sPmExu431Ekb9aEMn9BkPqEYFA==} - dev: true - - /@types/jsdom/20.0.1: - resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} - dependencies: - '@types/node': 14.18.36 - '@types/tough-cookie': 4.0.2 - parse5: 7.1.2 - - /@types/json-schema/7.0.11: - resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} - - /@types/keyv/3.1.4: - resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} - dependencies: - '@types/node': 14.18.36 - - /@types/loader-utils/1.1.3: - resolution: {integrity: sha512-euKGFr2oCB3ASBwG39CYJMR3N9T0nanVqXdiH7Zu/Nqddt6SmFRxytq/i2w9LQYNQekEtGBz+pE3qG6fQTNvRg==} - dependencies: - '@types/node': 14.18.36 - '@types/webpack': 4.41.32 - dev: true - - /@types/lodash/4.14.116: - resolution: {integrity: sha512-lRnAtKnxMXcYYXqOiotTmJd74uawNWuPnsnPrrO7HiFuE3npE2iQhfABatbYDyxTNqZNuXzcKGhw37R7RjBFLg==} - - /@types/long/4.0.0: - resolution: {integrity: sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==} - dev: false - - /@types/mdast/3.0.11: - resolution: {integrity: sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==} - dependencies: - '@types/unist': 2.0.6 - dev: true - - /@types/mime-types/2.1.1: - resolution: {integrity: sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==} - dev: true - - /@types/mime/3.0.1: - resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==} - - /@types/minimatch/3.0.5: - resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} - - /@types/minimist/1.2.2: - resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} - dev: false - - /@types/minipass/3.3.5: - resolution: {integrity: sha512-M2BLHQdEmDmH671h0GIlOQQJrgezd1vNqq7PVj1VOsHZ2uQQb4iPiQIl0SlMdhxZPUsLIfEklmeEHXg8DJRewA==} - deprecated: This is a stub types definition. minipass provides its own type definitions, so you do not need this installed. - dependencies: - minipass: 4.2.5 - dev: true - - /@types/node-fetch/2.6.2: - resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} - dependencies: - '@types/node': 14.18.36 - form-data: 3.0.1 - - /@types/node-forge/1.0.4: - resolution: {integrity: sha512-UpX8LTRrarEZPQvQqF5/6KQAqZolOVckH7txWdlsWIJrhBFFtwEUTcqeDouhrJl6t0F7Wg5cyUOAqqF8a6hheg==} - dependencies: - '@types/node': 14.18.36 - dev: true - - /@types/node/10.17.60: - resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} - dev: true - - /@types/node/14.0.1: - resolution: {integrity: sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA==} - dev: true - - /@types/node/14.18.36: - resolution: {integrity: sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==} - - /@types/node/17.0.41: - resolution: {integrity: sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw==} - dev: true - - /@types/node/20.2.5: - resolution: {integrity: sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==} - dev: true - - /@types/normalize-package-data/2.4.1: - resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} - - /@types/npm-package-arg/6.1.0: - resolution: {integrity: sha512-vbt5fb0y1svMhu++1lwtKmZL76d0uPChFlw7kEzyUmTwfmpHRcFb8i0R8ElT69q/L+QLgK2hgECivIAvaEDwag==} - dev: true - - /@types/npm-packlist/1.1.2: - resolution: {integrity: sha512-9NYoEH87t90e6dkaQOuUTY/R1xUE0a67sXzJBuAB+b+/z4FysHFD19g/O154ToGjyWqKYkezVUtuBdtfd4hyfw==} - dev: true - - /@types/npmlog/4.1.4: - resolution: {integrity: sha512-WKG4gTr8przEZBiJ5r3s8ZIAoMXNbOgQ+j/d5O4X3x6kZJRLNvyUJuUK/KoG3+8BaOHPhp2m7WC6JKKeovDSzQ==} - dev: true - - /@types/overlayscrollbars/1.12.1: - resolution: {integrity: sha512-V25YHbSoKQN35UasHf0EKD9U2vcmexRSp78qa8UglxFH8H3D+adEa9zGZwrqpH4TdvqeMrgMqVqsLB4woAryrQ==} - dev: true - - /@types/parse-json/4.0.0: - resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} - - /@types/parse5/5.0.3: - resolution: {integrity: sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==} - dev: true - - /@types/prettier/2.7.2: - resolution: {integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==} - - /@types/pretty-hrtime/1.0.1: - resolution: {integrity: sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ==} - dev: true - - /@types/prop-types/15.7.5: - resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} - - /@types/qs/6.9.7: - resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} - - /@types/range-parser/1.2.4: - resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} - - /@types/react-dom/16.9.14: - resolution: {integrity: sha512-FIX2AVmPTGP30OUJ+0vadeIFJJ07Mh1m+U0rxfgyW34p3rTlXI+nlenvAxNn4BP36YyI9IJ/+UJ7Wu22N1pI7A==} - dependencies: - '@types/react': 16.14.23 - - /@types/react-syntax-highlighter/11.0.5: - resolution: {integrity: sha512-VIOi9i2Oj5XsmWWoB72p3KlZoEbdRAcechJa8Ztebw7bDl2YmR+odxIqhtJGp1q2EozHs02US+gzxJ9nuf56qg==} - dependencies: - '@types/react': 16.14.23 - dev: true - - /@types/react/16.14.23: - resolution: {integrity: sha512-WngBZLuSkP4IAgPi0HOsGCHo6dn3CcuLQnCfC17VbA7YBgipZiZoTOhObwl/93DsFW0Y2a/ZXeonpW4DxirEJg==} - dependencies: - '@types/prop-types': 15.7.5 - '@types/scheduler': 0.16.3 - csstype: 3.1.1 - - /@types/read-package-tree/5.1.0: - resolution: {integrity: sha512-QEaGDX5COe5Usog79fca6PEycs59075O/W0QcOJjVNv+ZQ26xjqxg8sWu63Lwdt4KAI08gb4Muho1EbEKs3YFw==} - dev: true - - /@types/resolve/1.20.2: - resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - dev: true - - /@types/responselike/1.0.0: - resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} - dependencies: - '@types/node': 14.18.36 - - /@types/retry/0.12.0: - resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} - dev: false - - /@types/scheduler/0.16.3: - resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} - - /@types/semver/7.3.5: - resolution: {integrity: sha512-iotVxtCCsPLRAvxMFFgxL8HD2l4mAZ2Oin7/VJ2ooWO0VOK4EGOGmZWZn1uCq7RofR3I/1IOSjCHlFT71eVK0Q==} - - /@types/semver/7.5.0: - resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} - - /@types/serialize-javascript/5.0.2: - resolution: {integrity: sha512-BRLlwZzRoZukGaBtcUxkLsZsQfWZpvog6MZk3PWQO9Q6pXmXFzjU5iGzZ+943evp6tkkbN98N1Z31KT0UG1yRw==} - dev: true - - /@types/serve-index/1.9.1: - resolution: {integrity: sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==} - dependencies: - '@types/express': 4.17.13 - dev: false - - /@types/serve-static/1.15.1: - resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==} - dependencies: - '@types/mime': 3.0.1 - '@types/node': 14.18.36 - - /@types/sockjs/0.3.33: - resolution: {integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==} - dependencies: - '@types/node': 14.18.36 - dev: false - - /@types/source-list-map/0.1.2: - resolution: {integrity: sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==} - - /@types/ssri/7.1.1: - resolution: {integrity: sha512-DPP/jkDaqGiyU75MyMURxLWyYLwKSjnAuGe9ZCsLp9QZOpXmDfuevk769F0BS86TmRuD5krnp06qw9nSoNO+0g==} - dependencies: - '@types/node': 14.18.36 - dev: true - - /@types/stack-utils/2.0.1: - resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} - - /@types/strict-uri-encode/2.0.0: - resolution: {integrity: sha512-R6vDd7CHxcWMzv5wfVhR3qyCRVQoZKwVd6kit0rkozTThRZSXZKEW2Kz3AxfVqq9+UyJAz1g8Q+bJ3CL6NzztQ==} - dev: true - - /@types/tapable/0.2.5: - resolution: {integrity: sha512-dEoVvo/I9QFomyhY+4Q6Qk+I+dhG59TYceZgC6Q0mCifVPErx6Y83PNTKGDS5e9h9Eti6q0S2mm16BU6iQK+3w==} - dev: true - - /@types/tapable/1.0.6: - resolution: {integrity: sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==} - - /@types/tar/6.1.1: - resolution: {integrity: sha512-8mto3YZfVpqB1CHMaYz1TUYIQfZFbh/QbEq5Hsn6D0ilCfqRVCdalmc89B7vi3jhl9UYIk+dWDABShNfOkv5HA==} - dependencies: - '@types/minipass': 3.3.5 - '@types/node': 14.18.36 - dev: true - - /@types/through/0.0.30: - resolution: {integrity: sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==} - dependencies: - '@types/node': 14.18.36 - dev: true - - /@types/tough-cookie/4.0.2: - resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==} - - /@types/tunnel/0.0.3: - resolution: {integrity: sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==} - dependencies: - '@types/node': 14.18.36 - dev: false - - /@types/uglify-js/3.17.1: - resolution: {integrity: sha512-GkewRA4i5oXacU/n4MA9+bLgt5/L3F1mKrYvFGm7r2ouLXhRKjuWwo9XHNnbx6WF3vlGW21S3fCvgqxvxXXc5g==} - dependencies: - source-map: 0.6.1 - - /@types/unist/2.0.6: - resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} - dev: true - - /@types/update-notifier/6.0.2: - resolution: {integrity: sha512-/OKZaYpzHqBO9D+IzqAB7VfC/fx+xl/CM1upD6D56QPNyvAsFULAVLfjlBIGCl1ibU1rKqZV45xOj018WqHhlA==} - dependencies: - '@types/configstore': 6.0.0 - boxen: 7.0.2 - dev: true - - /@types/use-sync-external-store/0.0.3: - resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==} - dev: false - - /@types/watchpack/2.4.0: - resolution: {integrity: sha512-PSAD+o9hezvfUFFzrYB/PO6Je7kwiZ2BSnB3/EZ9le+jTDKB6x5NJ96WWzQz1h/AyGJ/de3/1KpuBTkUFZm77A==} - dependencies: - '@types/graceful-fs': 4.1.6 - '@types/node': 14.18.36 - dev: true - - /@types/webpack-env/1.18.0: - resolution: {integrity: sha512-56/MAlX5WMsPVbOg7tAxnYvNYMMWr/QJiIp6BxVSW3JJXUVzzOn64qW8TzQyMSqSUFM2+PVI4aUHcHOzIz/1tg==} - - /@types/webpack-sources/1.4.2: - resolution: {integrity: sha512-77T++JyKow4BQB/m9O96n9d/UUHWLQHlcqXb9Vsf4F1+wKNrrlWNFPDLKNT92RJnCSL6CieTc+NDXtCVZswdTw==} - dependencies: - '@types/node': 14.18.36 - '@types/source-list-map': 0.1.2 - source-map: 0.7.4 - - /@types/webpack/4.41.32: - resolution: {integrity: sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==} - dependencies: - '@types/node': 14.18.36 - '@types/tapable': 1.0.6 - '@types/uglify-js': 3.17.1 - '@types/webpack-sources': 1.4.2 - anymatch: 3.1.3 - source-map: 0.6.1 - - /@types/wordwrap/1.0.1: - resolution: {integrity: sha512-xe+rWyom8xn0laMWH3M7elOpWj2rDQk+3f13RAur89GKsf4FO5qmBNtXXtwepFo2XNgQI0nePdCEStoHFnNvWg==} - dev: true - - /@types/ws/8.5.4: - resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==} - dependencies: - '@types/node': 14.18.36 - dev: false - - /@types/xmldoc/1.1.4: - resolution: {integrity: sha512-a/ONNCf9itbmzEz1ohx0Fv5TLJzXIPQTapxFu+DlYlDtn9UcAa1OhnrOOMwbU8125hFjrkJKL3qllD7vO5Bivw==} - dev: true - - /@types/yargs-parser/21.0.0: - resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} - - /@types/yargs/15.0.15: - resolution: {integrity: sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg==} - dependencies: - '@types/yargs-parser': 21.0.0 - dev: true - - /@types/yargs/17.0.24: - resolution: {integrity: sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==} - dependencies: - '@types/yargs-parser': 21.0.0 - - /@typescript-eslint/eslint-plugin/5.59.7_7yosyjls7ieoemdl24ktrlsrzm: - resolution: {integrity: sha512-BL+jYxUFIbuYwy+4fF86k5vdT9lT0CNJ6HtwrIvGh0PhH8s0yy5rjaKH2fDCrz5ITHy07WCzVGNvAmjJh4IJFA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@eslint-community/regexpp': 4.4.1 - '@typescript-eslint/parser': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - '@typescript-eslint/scope-manager': 5.59.7_typescript@5.0.4 - '@typescript-eslint/type-utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - '@typescript-eslint/utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - debug: 4.3.4 - eslint: 8.7.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.4 - natural-compare-lite: 1.4.0 - semver: 7.3.8 - tsutils: 3.21.0_typescript@5.0.4 - typescript: 5.0.4 - transitivePeerDependencies: - - supports-color - dev: false - - /@typescript-eslint/eslint-plugin/5.59.7_c3hx2p4vcdwcxdeh6yw5lktxre: - resolution: {integrity: sha512-BL+jYxUFIbuYwy+4fF86k5vdT9lT0CNJ6HtwrIvGh0PhH8s0yy5rjaKH2fDCrz5ITHy07WCzVGNvAmjJh4IJFA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@eslint-community/regexpp': 4.4.1 - '@typescript-eslint/parser': 5.59.7_2aulnmwxyjhjxqmg3aruit533m - '@typescript-eslint/scope-manager': 5.59.7_typescript@5.0.4 - '@typescript-eslint/type-utils': 5.59.7_2aulnmwxyjhjxqmg3aruit533m - '@typescript-eslint/utils': 5.59.7_2aulnmwxyjhjxqmg3aruit533m - debug: 4.3.4 - eslint: 7.30.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.4 - natural-compare-lite: 1.4.0 - semver: 7.3.8 - tsutils: 3.21.0_typescript@5.0.4 - typescript: 5.0.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/experimental-utils/5.59.7_2aulnmwxyjhjxqmg3aruit533m: - resolution: {integrity: sha512-jqM0Cjfvta/sBlY1MxdXYv853/dJUC2wmUWnKoG2srwp0njNGQ6Zu/XLWoRFiLvocQbzBbpHkPFwKgC2UqyovA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@typescript-eslint/utils': 5.59.7_2aulnmwxyjhjxqmg3aruit533m - eslint: 7.30.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@typescript-eslint/experimental-utils/5.59.7_ucoohk2w7gukx6ccuul7rl7pnq: - resolution: {integrity: sha512-jqM0Cjfvta/sBlY1MxdXYv853/dJUC2wmUWnKoG2srwp0njNGQ6Zu/XLWoRFiLvocQbzBbpHkPFwKgC2UqyovA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@typescript-eslint/utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - eslint: 8.7.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: false - - /@typescript-eslint/parser/5.59.7_2aulnmwxyjhjxqmg3aruit533m: - resolution: {integrity: sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/scope-manager': 5.59.7_typescript@5.0.4 - '@typescript-eslint/types': 5.59.7_typescript@5.0.4 - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - debug: 4.3.4 - eslint: 7.30.0 - typescript: 5.0.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/parser/5.59.7_ucoohk2w7gukx6ccuul7rl7pnq: - resolution: {integrity: sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/scope-manager': 5.59.7_typescript@5.0.4 - '@typescript-eslint/types': 5.59.7_typescript@5.0.4 - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - debug: 4.3.4 - eslint: 8.7.0 - typescript: 5.0.4 - transitivePeerDependencies: - - supports-color - - /@typescript-eslint/scope-manager/5.59.7_typescript@5.0.4: - resolution: {integrity: sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.59.7_typescript@5.0.4 - '@typescript-eslint/visitor-keys': 5.59.7_typescript@5.0.4 - transitivePeerDependencies: - - typescript - - /@typescript-eslint/type-utils/5.59.7_2aulnmwxyjhjxqmg3aruit533m: - resolution: {integrity: sha512-ozuz/GILuYG7osdY5O5yg0QxXUAEoI4Go3Do5xeu+ERH9PorHBPSdvD3Tjp2NN2bNLh1NJQSsQu2TPu/Ly+HaQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - '@typescript-eslint/utils': 5.59.7_2aulnmwxyjhjxqmg3aruit533m - debug: 4.3.4 - eslint: 7.30.0 - tsutils: 3.21.0_typescript@5.0.4 - typescript: 5.0.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/type-utils/5.59.7_ucoohk2w7gukx6ccuul7rl7pnq: - resolution: {integrity: sha512-ozuz/GILuYG7osdY5O5yg0QxXUAEoI4Go3Do5xeu+ERH9PorHBPSdvD3Tjp2NN2bNLh1NJQSsQu2TPu/Ly+HaQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - '@typescript-eslint/utils': 5.59.7_ucoohk2w7gukx6ccuul7rl7pnq - debug: 4.3.4 - eslint: 8.7.0 - tsutils: 3.21.0_typescript@5.0.4 - typescript: 5.0.4 - transitivePeerDependencies: - - supports-color - dev: false - - /@typescript-eslint/types/5.59.7_typescript@5.0.4: - resolution: {integrity: sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - dependencies: - typescript: 5.0.4 - - /@typescript-eslint/typescript-estree/5.59.7_typescript@5.0.4: - resolution: {integrity: sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 5.59.7_typescript@5.0.4 - '@typescript-eslint/visitor-keys': 5.59.7_typescript@5.0.4 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.3.8 - tsutils: 3.21.0_typescript@5.0.4 - typescript: 5.0.4 - transitivePeerDependencies: - - supports-color - - /@typescript-eslint/utils/5.59.7_2aulnmwxyjhjxqmg3aruit533m: - resolution: {integrity: sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0_eslint@7.30.0 - '@types/json-schema': 7.0.11 - '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 5.59.7_typescript@5.0.4 - '@typescript-eslint/types': 5.59.7_typescript@5.0.4 - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - eslint: 7.30.0 - eslint-scope: 5.1.1 - semver: 7.3.8 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@typescript-eslint/utils/5.59.7_ucoohk2w7gukx6ccuul7rl7pnq: - resolution: {integrity: sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0_eslint@8.7.0 - '@types/json-schema': 7.0.11 - '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 5.59.7_typescript@5.0.4 - '@typescript-eslint/types': 5.59.7_typescript@5.0.4 - '@typescript-eslint/typescript-estree': 5.59.7_typescript@5.0.4 - eslint: 8.7.0 - eslint-scope: 5.1.1 - semver: 7.3.8 - transitivePeerDependencies: - - supports-color - - typescript - dev: false - - /@typescript-eslint/visitor-keys/5.59.7_typescript@5.0.4: - resolution: {integrity: sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.59.7_typescript@5.0.4 - eslint-visitor-keys: 3.4.0 - transitivePeerDependencies: - - typescript - - /@vue/compiler-core/3.2.47: - resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==} - dependencies: - '@babel/parser': 7.21.3 - '@vue/shared': 3.2.47 - estree-walker: 2.0.2 - source-map: 0.6.1 - dev: false - - /@vue/compiler-dom/3.2.47: - resolution: {integrity: sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==} - dependencies: - '@vue/compiler-core': 3.2.47 - '@vue/shared': 3.2.47 - dev: false - - /@vue/compiler-sfc/3.2.47: - resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==} - dependencies: - '@babel/parser': 7.21.3 - '@vue/compiler-core': 3.2.47 - '@vue/compiler-dom': 3.2.47 - '@vue/compiler-ssr': 3.2.47 - '@vue/reactivity-transform': 3.2.47 - '@vue/shared': 3.2.47 - estree-walker: 2.0.2 - magic-string: 0.25.9 - postcss: 8.4.21 - source-map: 0.6.1 - dev: false - - /@vue/compiler-ssr/3.2.47: - resolution: {integrity: sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==} - dependencies: - '@vue/compiler-dom': 3.2.47 - '@vue/shared': 3.2.47 - dev: false - - /@vue/reactivity-transform/3.2.47: - resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==} - dependencies: - '@babel/parser': 7.21.3 - '@vue/compiler-core': 3.2.47 - '@vue/shared': 3.2.47 - estree-walker: 2.0.2 - magic-string: 0.25.9 - dev: false - - /@vue/shared/3.2.47: - resolution: {integrity: sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==} - dev: false - - /@webassemblyjs/ast/1.11.5: - resolution: {integrity: sha512-LHY/GSAZZRpsNQH+/oHqhRQ5FT7eoULcBqgfyTB5nQHogFnK3/7QoN7dLnwSE/JkUAF0SrRuclT7ODqMFtWxxQ==} - dependencies: - '@webassemblyjs/helper-numbers': 1.11.5 - '@webassemblyjs/helper-wasm-bytecode': 1.11.5 - - /@webassemblyjs/ast/1.9.0: - resolution: {integrity: sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==} - dependencies: - '@webassemblyjs/helper-module-context': 1.9.0 - '@webassemblyjs/helper-wasm-bytecode': 1.9.0 - '@webassemblyjs/wast-parser': 1.9.0 - - /@webassemblyjs/floating-point-hex-parser/1.11.5: - resolution: {integrity: sha512-1j1zTIC5EZOtCplMBG/IEwLtUojtwFVwdyVMbL/hwWqbzlQoJsWCOavrdnLkemwNoC/EOwtUFch3fuo+cbcXYQ==} - - /@webassemblyjs/floating-point-hex-parser/1.9.0: - resolution: {integrity: sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==} - - /@webassemblyjs/helper-api-error/1.11.5: - resolution: {integrity: sha512-L65bDPmfpY0+yFrsgz8b6LhXmbbs38OnwDCf6NpnMUYqa+ENfE5Dq9E42ny0qz/PdR0LJyq/T5YijPnU8AXEpA==} - - /@webassemblyjs/helper-api-error/1.9.0: - resolution: {integrity: sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==} - - /@webassemblyjs/helper-buffer/1.11.5: - resolution: {integrity: sha512-fDKo1gstwFFSfacIeH5KfwzjykIE6ldh1iH9Y/8YkAZrhmu4TctqYjSh7t0K2VyDSXOZJ1MLhht/k9IvYGcIxg==} - - /@webassemblyjs/helper-buffer/1.9.0: - resolution: {integrity: sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==} - - /@webassemblyjs/helper-code-frame/1.9.0: - resolution: {integrity: sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==} - dependencies: - '@webassemblyjs/wast-printer': 1.9.0 - - /@webassemblyjs/helper-fsm/1.9.0: - resolution: {integrity: sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==} - - /@webassemblyjs/helper-module-context/1.9.0: - resolution: {integrity: sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==} - dependencies: - '@webassemblyjs/ast': 1.9.0 - - /@webassemblyjs/helper-numbers/1.11.5: - resolution: {integrity: sha512-DhykHXM0ZABqfIGYNv93A5KKDw/+ywBFnuWybZZWcuzWHfbp21wUfRkbtz7dMGwGgT4iXjWuhRMA2Mzod6W4WA==} - dependencies: - '@webassemblyjs/floating-point-hex-parser': 1.11.5 - '@webassemblyjs/helper-api-error': 1.11.5 - '@xtuc/long': 4.2.2 - - /@webassemblyjs/helper-wasm-bytecode/1.11.5: - resolution: {integrity: sha512-oC4Qa0bNcqnjAowFn7MPCETQgDYytpsfvz4ujZz63Zu/a/v71HeCAAmZsgZ3YVKec3zSPYytG3/PrRCqbtcAvA==} - - /@webassemblyjs/helper-wasm-bytecode/1.9.0: - resolution: {integrity: sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==} - - /@webassemblyjs/helper-wasm-section/1.11.5: - resolution: {integrity: sha512-uEoThA1LN2NA+K3B9wDo3yKlBfVtC6rh0i4/6hvbz071E8gTNZD/pT0MsBf7MeD6KbApMSkaAK0XeKyOZC7CIA==} - dependencies: - '@webassemblyjs/ast': 1.11.5 - '@webassemblyjs/helper-buffer': 1.11.5 - '@webassemblyjs/helper-wasm-bytecode': 1.11.5 - '@webassemblyjs/wasm-gen': 1.11.5 - - /@webassemblyjs/helper-wasm-section/1.9.0: - resolution: {integrity: sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==} - dependencies: - '@webassemblyjs/ast': 1.9.0 - '@webassemblyjs/helper-buffer': 1.9.0 - '@webassemblyjs/helper-wasm-bytecode': 1.9.0 - '@webassemblyjs/wasm-gen': 1.9.0 - - /@webassemblyjs/ieee754/1.11.5: - resolution: {integrity: sha512-37aGq6qVL8A8oPbPrSGMBcp38YZFXcHfiROflJn9jxSdSMMM5dS5P/9e2/TpaJuhE+wFrbukN2WI6Hw9MH5acg==} - dependencies: - '@xtuc/ieee754': 1.2.0 - - /@webassemblyjs/ieee754/1.9.0: - resolution: {integrity: sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==} - dependencies: - '@xtuc/ieee754': 1.2.0 - - /@webassemblyjs/leb128/1.11.5: - resolution: {integrity: sha512-ajqrRSXaTJoPW+xmkfYN6l8VIeNnR4vBOTQO9HzR7IygoCcKWkICbKFbVTNMjMgMREqXEr0+2M6zukzM47ZUfQ==} - dependencies: - '@xtuc/long': 4.2.2 - - /@webassemblyjs/leb128/1.9.0: - resolution: {integrity: sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==} - dependencies: - '@xtuc/long': 4.2.2 - - /@webassemblyjs/utf8/1.11.5: - resolution: {integrity: sha512-WiOhulHKTZU5UPlRl53gHR8OxdGsSOxqfpqWeA2FmcwBMaoEdz6b2x2si3IwC9/fSPLfe8pBMRTHVMk5nlwnFQ==} - - /@webassemblyjs/utf8/1.9.0: - resolution: {integrity: sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==} - - /@webassemblyjs/wasm-edit/1.11.5: - resolution: {integrity: sha512-C0p9D2fAu3Twwqvygvf42iGCQ4av8MFBLiTb+08SZ4cEdwzWx9QeAHDo1E2k+9s/0w1DM40oflJOpkZ8jW4HCQ==} - dependencies: - '@webassemblyjs/ast': 1.11.5 - '@webassemblyjs/helper-buffer': 1.11.5 - '@webassemblyjs/helper-wasm-bytecode': 1.11.5 - '@webassemblyjs/helper-wasm-section': 1.11.5 - '@webassemblyjs/wasm-gen': 1.11.5 - '@webassemblyjs/wasm-opt': 1.11.5 - '@webassemblyjs/wasm-parser': 1.11.5 - '@webassemblyjs/wast-printer': 1.11.5 - - /@webassemblyjs/wasm-edit/1.9.0: - resolution: {integrity: sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==} - dependencies: - '@webassemblyjs/ast': 1.9.0 - '@webassemblyjs/helper-buffer': 1.9.0 - '@webassemblyjs/helper-wasm-bytecode': 1.9.0 - '@webassemblyjs/helper-wasm-section': 1.9.0 - '@webassemblyjs/wasm-gen': 1.9.0 - '@webassemblyjs/wasm-opt': 1.9.0 - '@webassemblyjs/wasm-parser': 1.9.0 - '@webassemblyjs/wast-printer': 1.9.0 - - /@webassemblyjs/wasm-gen/1.11.5: - resolution: {integrity: sha512-14vteRlRjxLK9eSyYFvw1K8Vv+iPdZU0Aebk3j6oB8TQiQYuO6hj9s4d7qf6f2HJr2khzvNldAFG13CgdkAIfA==} - dependencies: - '@webassemblyjs/ast': 1.11.5 - '@webassemblyjs/helper-wasm-bytecode': 1.11.5 - '@webassemblyjs/ieee754': 1.11.5 - '@webassemblyjs/leb128': 1.11.5 - '@webassemblyjs/utf8': 1.11.5 - - /@webassemblyjs/wasm-gen/1.9.0: - resolution: {integrity: sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==} - dependencies: - '@webassemblyjs/ast': 1.9.0 - '@webassemblyjs/helper-wasm-bytecode': 1.9.0 - '@webassemblyjs/ieee754': 1.9.0 - '@webassemblyjs/leb128': 1.9.0 - '@webassemblyjs/utf8': 1.9.0 - - /@webassemblyjs/wasm-opt/1.11.5: - resolution: {integrity: sha512-tcKwlIXstBQgbKy1MlbDMlXaxpucn42eb17H29rawYLxm5+MsEmgPzeCP8B1Cl69hCice8LeKgZpRUAPtqYPgw==} - dependencies: - '@webassemblyjs/ast': 1.11.5 - '@webassemblyjs/helper-buffer': 1.11.5 - '@webassemblyjs/wasm-gen': 1.11.5 - '@webassemblyjs/wasm-parser': 1.11.5 - - /@webassemblyjs/wasm-opt/1.9.0: - resolution: {integrity: sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==} - dependencies: - '@webassemblyjs/ast': 1.9.0 - '@webassemblyjs/helper-buffer': 1.9.0 - '@webassemblyjs/wasm-gen': 1.9.0 - '@webassemblyjs/wasm-parser': 1.9.0 - - /@webassemblyjs/wasm-parser/1.11.5: - resolution: {integrity: sha512-SVXUIwsLQlc8srSD7jejsfTU83g7pIGr2YYNb9oHdtldSxaOhvA5xwvIiWIfcX8PlSakgqMXsLpLfbbJ4cBYew==} - dependencies: - '@webassemblyjs/ast': 1.11.5 - '@webassemblyjs/helper-api-error': 1.11.5 - '@webassemblyjs/helper-wasm-bytecode': 1.11.5 - '@webassemblyjs/ieee754': 1.11.5 - '@webassemblyjs/leb128': 1.11.5 - '@webassemblyjs/utf8': 1.11.5 - - /@webassemblyjs/wasm-parser/1.9.0: - resolution: {integrity: sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==} - dependencies: - '@webassemblyjs/ast': 1.9.0 - '@webassemblyjs/helper-api-error': 1.9.0 - '@webassemblyjs/helper-wasm-bytecode': 1.9.0 - '@webassemblyjs/ieee754': 1.9.0 - '@webassemblyjs/leb128': 1.9.0 - '@webassemblyjs/utf8': 1.9.0 - - /@webassemblyjs/wast-parser/1.9.0: - resolution: {integrity: sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==} - dependencies: - '@webassemblyjs/ast': 1.9.0 - '@webassemblyjs/floating-point-hex-parser': 1.9.0 - '@webassemblyjs/helper-api-error': 1.9.0 - '@webassemblyjs/helper-code-frame': 1.9.0 - '@webassemblyjs/helper-fsm': 1.9.0 - '@xtuc/long': 4.2.2 - - /@webassemblyjs/wast-printer/1.11.5: - resolution: {integrity: sha512-f7Pq3wvg3GSPUPzR0F6bmI89Hdb+u9WXrSKc4v+N0aV0q6r42WoF92Jp2jEorBEBRoRNXgjp53nBniDXcqZYPA==} - dependencies: - '@webassemblyjs/ast': 1.11.5 - '@xtuc/long': 4.2.2 - - /@webassemblyjs/wast-printer/1.9.0: - resolution: {integrity: sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==} - dependencies: - '@webassemblyjs/ast': 1.9.0 - '@webassemblyjs/wast-parser': 1.9.0 - '@xtuc/long': 4.2.2 - - /@xtuc/ieee754/1.2.0: - resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} - - /@xtuc/long/4.2.2: - resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} - - /@yarnpkg/lockfile/1.0.2: - resolution: {integrity: sha512-MqJ00WXw89ga0rK6GZkdmmgv3bAsxpJixyTthjcix73O44pBqotyU2BejBkLuIsaOBI6SEu77vAnSyLe5iIHkw==} - dev: false - - /@zkochan/cmd-shim/5.4.1: - resolution: {integrity: sha512-odWb1qUzt0dIOEUPyWBEpFDYQPRjEMr/dbHHAfgBkVkYR9aO7Zo+I7oYWrXIxl+cKlC7+49ftPm8uJxL1MA9kw==} - engines: {node: '>=10.13'} - dependencies: - cmd-extension: 1.0.2 - graceful-fs: 4.2.11 - is-windows: 1.0.2 - dev: false - - /abab/2.0.6: - resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} - - /abbrev/1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - dev: true - - /abstract-logging/2.0.1: - resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} - dev: false - - /accepts/1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} - dependencies: - mime-types: 2.1.35 - negotiator: 0.6.3 - - /acorn-globals/7.0.1: - resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} - dependencies: - acorn: 8.8.2 - acorn-walk: 8.2.0 - - /acorn-import-assertions/1.8.0_acorn@8.8.2: - resolution: {integrity: sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==} - peerDependencies: - acorn: ^8 - dependencies: - acorn: 8.8.2 - - /acorn-jsx/5.3.2_acorn@7.4.1: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 7.4.1 - dev: true - - /acorn-jsx/5.3.2_acorn@8.8.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.8.2 - - /acorn-walk/7.2.0: - resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} - engines: {node: '>=0.4.0'} - dev: true - - /acorn-walk/8.2.0: - resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} - engines: {node: '>=0.4.0'} - - /acorn/6.4.2: - resolution: {integrity: sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==} - engines: {node: '>=0.4.0'} - hasBin: true - - /acorn/7.4.1: - resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /acorn/8.8.2: - resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} - engines: {node: '>=0.4.0'} - hasBin: true - - /address/1.2.2: - resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} - engines: {node: '>= 10.0.0'} - dev: true - - /agent-base/5.1.1: - resolution: {integrity: sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==} - engines: {node: '>= 6.0.0'} - dev: true - - /agent-base/6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - dependencies: - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - - /agentkeepalive/4.3.0: - resolution: {integrity: sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==} - engines: {node: '>= 8.0.0'} - dependencies: - debug: 4.3.4 - depd: 2.0.0 - humanize-ms: 1.2.1 - transitivePeerDependencies: - - supports-color - dev: true - - /aggregate-error/3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} - dependencies: - clean-stack: 2.2.0 - indent-string: 4.0.0 - dev: true - - /airbnb-js-shims/2.2.1: - resolution: {integrity: sha512-wJNXPH66U2xjgo1Zwyjf9EydvJ2Si94+vSdk6EERcBfB2VZkeltpqIats0cqIZMLCXP3zcyaUKGYQeIBT6XjsQ==} - dependencies: - array-includes: 3.1.6 - array.prototype.flat: 1.3.1 - array.prototype.flatmap: 1.3.1 - es5-shim: 4.6.7 - es6-shim: 0.35.8 - function.prototype.name: 1.1.5 - globalthis: 1.0.3 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - object.getownpropertydescriptors: 2.1.5 - object.values: 1.1.6 - promise.allsettled: 1.0.6 - promise.prototype.finally: 3.1.4 - string.prototype.matchall: 4.0.8 - string.prototype.padend: 3.1.4 - string.prototype.padstart: 3.1.4 - symbol.prototype.description: 1.0.5 - dev: true - - /ajv-errors/1.0.1_ajv@6.12.6: - resolution: {integrity: sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==} - peerDependencies: - ajv: '>=5.0.0' - dependencies: - ajv: 6.12.6 - - /ajv-formats/2.1.1: - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependenciesMeta: - ajv: - optional: true - dependencies: - ajv: 8.12.0 - - /ajv-keywords/3.5.2_ajv@6.12.6: - resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} - peerDependencies: - ajv: ^6.9.1 - dependencies: - ajv: 6.12.6 - - /ajv-keywords/5.1.0_ajv@8.12.0: - resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} - peerDependencies: - ajv: ^8.8.2 - dependencies: - ajv: 8.12.0 - fast-deep-equal: 3.1.3 - dev: false - - /ajv/6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - /ajv/8.12.0: - resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} - dependencies: - fast-deep-equal: 3.1.3 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - uri-js: 4.4.1 - - /ansi-align/3.0.1: - resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} - dependencies: - string-width: 4.2.3 - - /ansi-colors/3.2.4: - resolution: {integrity: sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==} - engines: {node: '>=6'} - dev: true - - /ansi-colors/4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - dev: true - - /ansi-escapes/4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.21.3 - - /ansi-html-community/0.0.8: - resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} - engines: {'0': node >= 0.8.0} - hasBin: true - - /ansi-regex/2.1.1: - resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} - engines: {node: '>=0.10.0'} - - /ansi-regex/4.1.1: - resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} - engines: {node: '>=6'} - dev: false - - /ansi-regex/5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - /ansi-regex/6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - dev: true - - /ansi-styles/2.2.1: - resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} - engines: {node: '>=0.10.0'} - dev: false - - /ansi-styles/3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - dependencies: - color-convert: 1.9.3 - - /ansi-styles/4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 - - /ansi-styles/5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - /ansi-styles/6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - dev: true - - /ansi-to-html/0.6.15: - resolution: {integrity: sha512-28ijx2aHJGdzbs+O5SNQF65r6rrKYnkuwTYm8lZlChuoJ9P1vVzIpWO20sQTqTPDXYp6NFwk326vApTtLVFXpQ==} - engines: {node: '>=8.0.0'} - hasBin: true - dependencies: - entities: 2.2.0 - dev: true - - /any-promise/1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - dev: false - - /anymatch/2.0.0: - resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==} - dependencies: - micromatch: 3.1.10 - normalize-path: 2.1.1 - - /anymatch/3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - /app-root-dir/1.0.2: - resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==} - dev: true - - /aproba/1.2.0: - resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==} - - /aproba/2.0.0: - resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} - dev: true - - /archiver-utils/2.1.0: - resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} - engines: {node: '>= 6'} - dependencies: - glob: 7.2.3 - graceful-fs: 4.2.11 - lazystream: 1.0.1 - lodash.defaults: 4.2.0 - lodash.difference: 4.5.0 - lodash.flatten: 4.4.0 - lodash.isplainobject: 4.0.6 - lodash.union: 4.6.0 - normalize-path: 3.0.0 - readable-stream: 2.3.8 - dev: true - - /archiver/5.3.1: - resolution: {integrity: sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==} - engines: {node: '>= 10'} - dependencies: - archiver-utils: 2.1.0 - async: 3.2.4 - buffer-crc32: 0.2.13 - readable-stream: 3.6.2 - readdir-glob: 1.1.2 - tar-stream: 2.2.0 - zip-stream: 4.1.0 - dev: true - - /archy/1.0.0: - resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} - dev: false - - /are-we-there-yet/1.1.7: - resolution: {integrity: sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==} - dependencies: - delegates: 1.0.0 - readable-stream: 2.3.8 - dev: true - - /are-we-there-yet/2.0.0: - resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} - engines: {node: '>=10'} - dependencies: - delegates: 1.0.0 - readable-stream: 3.6.2 - dev: true - - /argparse/1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - dependencies: - sprintf-js: 1.0.3 - - /argparse/2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - /arr-diff/4.0.0: - resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} - engines: {node: '>=0.10.0'} - - /arr-flatten/1.1.0: - resolution: {integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==} - engines: {node: '>=0.10.0'} - - /arr-union/3.1.0: - resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==} - engines: {node: '>=0.10.0'} - - /array-buffer-byte-length/1.0.0: - resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} - dependencies: - call-bind: 1.0.2 - is-array-buffer: 3.0.2 - - /array-differ/3.0.0: - resolution: {integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==} - engines: {node: '>=8'} - dev: false - - /array-flatten/1.1.1: - resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - - /array-flatten/2.1.2: - resolution: {integrity: sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==} - dev: false - - /array-includes/3.1.6: - resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - get-intrinsic: 1.2.0 - is-string: 1.0.7 - - /array-union/1.0.2: - resolution: {integrity: sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==} - engines: {node: '>=0.10.0'} - dependencies: - array-uniq: 1.0.3 - dev: true - - /array-union/2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - /array-uniq/1.0.3: - resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==} - engines: {node: '>=0.10.0'} - dev: true - - /array-unique/0.3.2: - resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==} - engines: {node: '>=0.10.0'} - - /array.prototype.flat/1.3.1: - resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-shim-unscopables: 1.0.0 - dev: true - - /array.prototype.flatmap/1.3.1: - resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-shim-unscopables: 1.0.0 - - /array.prototype.map/1.0.5: - resolution: {integrity: sha512-gfaKntvwqYIuC7mLLyv2wzZIJqrRhn5PZ9EfFejSx6a78sV7iDsGpG9P+3oUPtm1Rerqm6nrKS4FYuTIvWfo3g==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-array-method-boxes-properly: 1.0.0 - is-string: 1.0.7 - dev: true - - /array.prototype.reduce/1.0.5: - resolution: {integrity: sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-array-method-boxes-properly: 1.0.0 - is-string: 1.0.7 - - /arrify/1.0.1: - resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} - engines: {node: '>=0.10.0'} - dev: false - - /arrify/2.0.1: - resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} - engines: {node: '>=8'} - - /asap/2.0.6: - resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - dev: false - - /asn1.js/5.4.1: - resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} - dependencies: - bn.js: 4.12.0 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - safer-buffer: 2.1.2 - - /assert/1.5.0: - resolution: {integrity: sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==} - dependencies: - object-assign: 4.1.1 - util: 0.10.3 - - /assign-symbols/1.0.0: - resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} - engines: {node: '>=0.10.0'} - - /ast-types/0.13.3: - resolution: {integrity: sha512-XTZ7xGML849LkQP86sWdQzfhwbt3YwIO6MqbX9mUNYY98VKaaVZP7YNNm70IpwecbkkxmfC5IYAzOQ/2p29zRA==} - engines: {node: '>=4'} - dev: true - - /ast-types/0.13.4: - resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} - engines: {node: '>=4'} - dependencies: - tslib: 2.3.1 - dev: true - - /ast-types/0.14.2: - resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==} - engines: {node: '>=4'} - dependencies: - tslib: 2.3.1 - dev: true - - /astral-regex/2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} - dev: true - - /async-each/1.0.6: - resolution: {integrity: sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==} - optional: true - - /async-limiter/1.0.1: - resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} - dev: true - - /async-retry/1.3.3: - resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} - dependencies: - retry: 0.13.1 - dev: true - - /async/1.5.2: - resolution: {integrity: sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==} - dev: true - - /async/3.2.4: - resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} - dev: true - - /asynckit/0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - - /at-least-node/1.0.0: - resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} - engines: {node: '>= 4.0.0'} - dev: true - - /atob/2.1.2: - resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} - engines: {node: '>= 4.5.0'} - hasBin: true - - /atomic-sleep/1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} - dev: false - - /atomically/1.7.0: - resolution: {integrity: sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==} - engines: {node: '>=10.12.0'} - dev: true - - /autoprefixer/10.4.14_postcss@8.4.21: - resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} - engines: {node: ^10 || ^12 || >=14} - hasBin: true - peerDependencies: - postcss: ^8.1.0 - dependencies: - browserslist: 4.21.5 - caniuse-lite: 1.0.30001470 - fraction.js: 4.2.0 - normalize-range: 0.1.2 - picocolors: 1.0.0 - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - - /autoprefixer/9.8.8: - resolution: {integrity: sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==} - hasBin: true - dependencies: - browserslist: 4.21.5 - caniuse-lite: 1.0.30001470 - normalize-range: 0.1.2 - num2fraction: 1.2.2 - picocolors: 0.2.1 - postcss: 7.0.39 - postcss-value-parser: 4.2.0 - dev: true - - /available-typed-arrays/1.0.5: - resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} - engines: {node: '>= 0.4'} - - /avvio/7.2.5: - resolution: {integrity: sha512-AOhBxyLVdpOad3TujtC9kL/9r3HnTkxwQ5ggOsYrvvZP1cCFvzHWJd5XxZDFuTn+IN8vkKSG5SEJrd27vCSbeA==} - dependencies: - archy: 1.0.0 - debug: 4.3.4 - fastq: 1.15.0 - queue-microtask: 1.2.3 - transitivePeerDependencies: - - supports-color - dev: false - - /aws-cdk-lib/2.7.0_constructs@10.0.130: - resolution: {integrity: sha512-9mxm9WD5rioZxTCQ6FqMzkZ0NH5+4oBFtlNL10duXJ+P+7Zcs82mHHycX7wjD689Gnl6hZla/DOlUV04HQvNhw==} - engines: {node: '>= 14.15.0'} - peerDependencies: - constructs: ^10.0.0 - dependencies: - '@balena/dockerignore': 1.0.2 - case: 1.6.3 - constructs: 10.0.130 - fs-extra: 9.1.0 - ignore: 5.2.4 - jsonschema: 1.4.1 - minimatch: 3.1.2 - punycode: 2.3.0 - semver: 7.3.8 - yaml: 1.10.2 - dev: true - bundledDependencies: - - '@balena/dockerignore' - - case - - fs-extra - - ignore - - jsonschema - - minimatch - - punycode - - semver - - yaml - - /aws-cdk/2.7.0: - resolution: {integrity: sha512-hZdvrFuN6AoT7hYM1lOrZBO5knE0X3lC/+uoIuZFbSu0AkZViM0pwF1pHWW+un//PWzIDSb/nzx7I08LjcpQ7Q==} - engines: {node: '>= 14.15.0'} - hasBin: true - dependencies: - '@aws-cdk/cloud-assembly-schema': 2.7.0 - '@aws-cdk/cloudformation-diff': 2.7.0 - '@aws-cdk/cx-api': 2.7.0 - '@aws-cdk/region-info': 2.7.0 - '@jsii/check-node': 1.50.0 - archiver: 5.3.1 - aws-sdk: 2.1344.0 - camelcase: 6.3.0 - cdk-assets: 2.7.0 - chalk: 4.1.2 - chokidar: 3.5.3 - decamelize: 5.0.1 - fs-extra: 9.1.0 - glob: 7.2.3 - json-diff: 0.7.4 - minimatch: 3.0.8 - promptly: 3.2.0 - proxy-agent: 5.0.0 - semver: 7.3.8 - source-map-support: 0.5.21 - strip-ansi: 6.0.1 - table: 6.8.1 - uuid: 8.3.2 - wrap-ansi: 7.0.0 - yaml: 1.10.2 - yargs: 16.2.0 - transitivePeerDependencies: - - supports-color - dev: true - - /aws-sdk/2.1344.0: - resolution: {integrity: sha512-dOYkxyw5wSeX+UqGhVE4wXbLmw1z4t65jAYz4PdqXASbhB1YS5gFgTYnUg20psbzPnYV83KvUToWw5Sbc8tWcQ==} - engines: {node: '>= 10.0.0'} - dependencies: - buffer: 4.9.2 - events: 1.1.1 - ieee754: 1.1.13 - jmespath: 0.16.0 - querystring: 0.2.0 - sax: 1.2.1 - url: 0.10.3 - util: 0.12.5 - uuid: 8.0.0 - xml2js: 0.4.19 - dev: true - - /babel-core/7.0.0-bridge.0_@babel+core@7.20.12: - resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - dev: true - - /babel-jest/29.5.0_@babel+core@7.20.12: - resolution: {integrity: sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - dependencies: - '@babel/core': 7.20.12 - '@jest/transform': 29.5.0 - '@types/babel__core': 7.20.0 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.5.0_@babel+core@7.20.12 - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - - /babel-loader/8.2.5_tb555f6titdaodihyrbadfrjbq: - resolution: {integrity: sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==} - engines: {node: '>= 8.9'} - peerDependencies: - '@babel/core': ^7.0.0 - webpack: '>=2' - dependencies: - '@babel/core': 7.20.12 - find-cache-dir: 3.3.2 - loader-utils: 2.0.4 - make-dir: 3.1.0 - schema-utils: 2.7.1 - webpack: 4.44.2 - dev: true - - /babel-plugin-add-react-displayname/0.0.5: - resolution: {integrity: sha512-LY3+Y0XVDYcShHHorshrDbt4KFWL4bSeniCtl4SYZbask+Syngk1uMPCeN9+nSiZo6zX5s0RTq/J9Pnaaf/KHw==} - dev: true - - /babel-plugin-apply-mdx-type-prop/1.6.22_@babel+core@7.12.9: - resolution: {integrity: sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==} - peerDependencies: - '@babel/core': ^7.11.6 - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.10.4 - '@mdx-js/util': 1.6.22 - dev: true - - /babel-plugin-emotion/10.2.2: - resolution: {integrity: sha512-SMSkGoqTbTyUTDeuVuPIWifPdUGkTk1Kf9BWRiXIOIcuyMfsdp2EjeiiFvOzX8NOBvEh/ypKYvUh2rkgAJMCLA==} - dependencies: - '@babel/helper-module-imports': 7.18.6 - '@emotion/hash': 0.8.0 - '@emotion/memoize': 0.7.4 - '@emotion/serialize': 0.11.16 - babel-plugin-macros: 2.8.0 - babel-plugin-syntax-jsx: 6.18.0 - convert-source-map: 1.9.0 - escape-string-regexp: 1.0.5 - find-root: 1.1.0 - source-map: 0.5.7 - dev: true - - /babel-plugin-extract-import-names/1.6.22: - resolution: {integrity: sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==} - dependencies: - '@babel/helper-plugin-utils': 7.10.4 - dev: true - - /babel-plugin-istanbul/6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - dependencies: - '@babel/helper-plugin-utils': 7.20.2 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - - /babel-plugin-jest-hoist/29.5.0: - resolution: {integrity: sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/template': 7.20.7 - '@babel/types': 7.21.3 - '@types/babel__core': 7.20.0 - '@types/babel__traverse': 7.18.3 - - /babel-plugin-macros/2.8.0: - resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==} - dependencies: - '@babel/runtime': 7.21.0 - cosmiconfig: 6.0.0 - resolve: 1.22.1 - dev: true - - /babel-plugin-macros/3.1.0: - resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} - engines: {node: '>=10', npm: '>=6'} - dependencies: - '@babel/runtime': 7.21.0 - cosmiconfig: 7.1.0 - resolve: 1.22.1 - dev: true - - /babel-plugin-named-asset-import/0.3.8_@babel+core@7.20.12: - resolution: {integrity: sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==} - peerDependencies: - '@babel/core': ^7.1.0 - dependencies: - '@babel/core': 7.20.12 - dev: true - - /babel-plugin-polyfill-corejs2/0.3.3_@babel+core@7.20.12: - resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/compat-data': 7.21.0 - '@babel/core': 7.20.12 - '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - dev: true - - /babel-plugin-polyfill-corejs3/0.1.7_@babel+core@7.20.12: - resolution: {integrity: sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-define-polyfill-provider': 0.1.5_@babel+core@7.20.12 - core-js-compat: 3.29.1 - transitivePeerDependencies: - - supports-color - dev: true - - /babel-plugin-polyfill-corejs3/0.6.0_@babel+core@7.20.12: - resolution: {integrity: sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12 - core-js-compat: 3.29.1 - transitivePeerDependencies: - - supports-color - dev: true - - /babel-plugin-polyfill-regenerator/0.4.1_@babel+core@7.20.12: - resolution: {integrity: sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.20.12 - '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.20.12 - transitivePeerDependencies: - - supports-color - dev: true - - /babel-plugin-react-docgen/4.2.1: - resolution: {integrity: sha512-UQ0NmGHj/HAqi5Bew8WvNfCk8wSsmdgNd8ZdMjBCICtyCJCq9LiqgqvjCYe570/Wg7AQArSq1VQ60Dd/CHN7mQ==} - dependencies: - ast-types: 0.14.2 - lodash: 4.17.21 - react-docgen: 5.4.3 - transitivePeerDependencies: - - supports-color - dev: true - - /babel-plugin-syntax-jsx/6.18.0: - resolution: {integrity: sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==} - dev: true - - /babel-preset-current-node-syntax/1.0.1_@babel+core@7.20.12: - resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.20.12 - '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.20.12 - '@babel/plugin-syntax-bigint': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.20.12 - '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.20.12 - '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.12 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.12 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.12 - '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.12 - - /babel-preset-jest/29.5.0_@babel+core@7.20.12: - resolution: {integrity: sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.20.12 - babel-plugin-jest-hoist: 29.5.0 - babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.12 - - /bail/1.0.5: - resolution: {integrity: sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==} - dev: true - - /balanced-match/1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - /base/0.11.2: - resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==} - engines: {node: '>=0.10.0'} - dependencies: - cache-base: 1.0.1 - class-utils: 0.3.6 - component-emitter: 1.3.0 - define-property: 1.0.0 - isobject: 3.0.1 - mixin-deep: 1.3.2 - pascalcase: 0.1.1 - - /base64-js/1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - /batch-processor/1.0.0: - resolution: {integrity: sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==} - dev: true - - /batch/0.6.1: - resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} - dev: false - - /better-opn/2.1.1: - resolution: {integrity: sha512-kIPXZS5qwyKiX/HcRvDYfmBQUa8XP17I0mYZZ0y4UhpYOSvtsLHDYqmomS+Mj20aDvD3knEiQ0ecQy2nhio3yA==} - engines: {node: '>8.0.0'} - dependencies: - open: 7.4.2 - dev: true - - /better-path-resolve/1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} - dependencies: - is-windows: 1.0.2 - dev: false - - /big.js/5.2.2: - resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} - - /binary-extensions/1.13.1: - resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==} - engines: {node: '>=0.10.0'} - optional: true - - /binary-extensions/2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} - engines: {node: '>=8'} - - /bindings/1.5.0: - resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - requiresBuild: true - dependencies: - file-uri-to-path: 1.0.0 - optional: true - - /bl/4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - - /bluebird/3.7.2: - resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - - /bn.js/4.12.0: - resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} - - /bn.js/5.2.1: - resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} - - /body-parser/1.20.0: - resolution: {integrity: sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.10.3 - raw-body: 2.5.1 - type-is: 1.6.18 - unpipe: 1.0.0 - - /body-parser/1.20.2: - resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.11.0 - raw-body: 2.5.2 - type-is: 1.6.18 - unpipe: 1.0.0 - dev: true - - /bole/4.0.1: - resolution: {integrity: sha512-42r0aSOJFJti2l6LasBHq2BuWJzohGs349olQnH/ETlJo87XnoWw7UT8pGE6UstjxzOKkwz7tjoFcmSr6L16vg==} - dependencies: - fast-safe-stringify: 2.1.1 - individual: 3.0.0 - dev: true - - /bonjour-service/1.1.1: - resolution: {integrity: sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==} - dependencies: - array-flatten: 2.1.2 - dns-equal: 1.0.0 - fast-deep-equal: 3.1.3 - multicast-dns: 7.2.5 - dev: false - - /boolbase/1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - - /boxen/5.1.2: - resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} - engines: {node: '>=10'} - dependencies: - ansi-align: 3.0.1 - camelcase: 6.3.0 - chalk: 4.1.2 - cli-boxes: 2.2.1 - string-width: 4.2.3 - type-fest: 0.20.2 - widest-line: 3.1.0 - wrap-ansi: 7.0.0 - - /boxen/7.0.2: - resolution: {integrity: sha512-1Z4UJabXUP1/R9rLpoU3O2lEMnG3pPLAs/ZD2lF3t2q7qD5lM8rqbtnvtvm4N0wEyNlE+9yZVTVAGmd1V5jabg==} - engines: {node: '>=14.16'} - dependencies: - ansi-align: 3.0.1 - camelcase: 7.0.1 - chalk: 5.2.0 - cli-boxes: 3.0.0 - string-width: 5.1.2 - type-fest: 2.19.0 - widest-line: 4.0.1 - wrap-ansi: 8.1.0 - dev: true - - /brace-expansion/1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - /brace-expansion/2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - dependencies: - balanced-match: 1.0.2 - dev: true - - /braces/2.3.2: - resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==} - engines: {node: '>=0.10.0'} - dependencies: - arr-flatten: 1.1.0 - array-unique: 0.3.2 - extend-shallow: 2.0.1 - fill-range: 4.0.0 - isobject: 3.0.1 - repeat-element: 1.1.4 - snapdragon: 0.8.2 - snapdragon-node: 2.1.1 - split-string: 3.1.0 - to-regex: 3.0.2 - - /braces/3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} - engines: {node: '>=8'} - dependencies: - fill-range: 7.0.1 - - /brorand/1.1.0: - resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} - - /browserify-aes/1.2.0: - resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} - dependencies: - buffer-xor: 1.0.3 - cipher-base: 1.0.4 - create-hash: 1.2.0 - evp_bytestokey: 1.0.3 - inherits: 2.0.4 - safe-buffer: 5.2.1 - - /browserify-cipher/1.0.1: - resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} - dependencies: - browserify-aes: 1.2.0 - browserify-des: 1.0.2 - evp_bytestokey: 1.0.3 - - /browserify-des/1.0.2: - resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} - dependencies: - cipher-base: 1.0.4 - des.js: 1.0.1 - inherits: 2.0.4 - safe-buffer: 5.2.1 - - /browserify-rsa/4.1.0: - resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==} - dependencies: - bn.js: 5.2.1 - randombytes: 2.1.0 - - /browserify-sign/4.2.1: - resolution: {integrity: sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==} - dependencies: - bn.js: 5.2.1 - browserify-rsa: 4.1.0 - create-hash: 1.2.0 - create-hmac: 1.1.7 - elliptic: 6.5.4 - inherits: 2.0.4 - parse-asn1: 5.1.6 - readable-stream: 3.6.2 - safe-buffer: 5.2.1 - - /browserify-zlib/0.2.0: - resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} - dependencies: - pako: 1.0.11 - - /browserslist/4.21.5: - resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001470 - electron-to-chromium: 1.4.341 - node-releases: 2.0.10 - update-browserslist-db: 1.0.10_browserslist@4.21.5 - - /bser/2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - dependencies: - node-int64: 0.4.0 - - /buffer-builder/0.2.0: - resolution: {integrity: sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==} - dev: false - - /buffer-crc32/0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - dev: true - - /buffer-equal-constant-time/1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - dev: false - - /buffer-from/1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - /buffer-xor/1.0.3: - resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} - - /buffer/4.9.2: - resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==} - dependencies: - base64-js: 1.5.1 - ieee754: 1.1.13 - isarray: 1.0.0 - - /buffer/5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - /builtin-modules/1.1.1: - resolution: {integrity: sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==} - engines: {node: '>=0.10.0'} - dev: true - - /builtin-modules/3.1.0: - resolution: {integrity: sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==} - engines: {node: '>=6'} - dev: false - - /builtin-status-codes/3.0.0: - resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} - - /builtins/1.0.3: - resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} - dev: false - - /buttono/1.0.4: - resolution: {integrity: sha512-aLOeyK3zrhZnqvH6LzwIbjur8mkKhW8Xl3/jolX+RCJnGG354+L48q1SJWdky89uhQ/mBlTxY/d0x8+ciE0ZWw==} - dev: false - - /bytes/3.0.0: - resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} - engines: {node: '>= 0.8'} - - /bytes/3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - - /c8/7.13.0: - resolution: {integrity: sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA==} - engines: {node: '>=10.12.0'} - hasBin: true - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@istanbuljs/schema': 0.1.3 - find-up: 5.0.0 - foreground-child: 2.0.0 - istanbul-lib-coverage: 3.2.0 - istanbul-lib-report: 3.0.0 - istanbul-reports: 3.1.5 - rimraf: 3.0.2 - test-exclude: 6.0.0 - v8-to-istanbul: 9.1.0 - yargs: 16.2.0 - yargs-parser: 20.2.9 - dev: true - - /cacache/12.0.4: - resolution: {integrity: sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==} - dependencies: - bluebird: 3.7.2 - chownr: 1.1.4 - figgy-pudding: 3.5.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - infer-owner: 1.0.4 - lru-cache: 5.1.1 - mississippi: 3.0.0 - mkdirp: 0.5.6 - move-concurrently: 1.0.1 - promise-inflight: 1.0.1 - rimraf: 2.7.1 - ssri: 6.0.2 - unique-filename: 1.1.1 - y18n: 4.0.3 - - /cacache/15.3.0: - resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==} - engines: {node: '>= 10'} - dependencies: - '@npmcli/fs': 1.1.1 - '@npmcli/move-file': 1.1.2 - chownr: 2.0.0 - fs-minipass: 2.1.0 - glob: 7.2.3 - infer-owner: 1.0.4 - lru-cache: 6.0.0 - minipass: 3.3.6 - minipass-collect: 1.0.2 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - mkdirp: 1.0.4 - p-map: 4.0.0 - promise-inflight: 1.0.1 - rimraf: 3.0.2 - ssri: 8.0.1 - tar: 6.1.13 - unique-filename: 1.1.1 - dev: true - - /cache-base/1.0.1: - resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==} - engines: {node: '>=0.10.0'} - dependencies: - collection-visit: 1.0.0 - component-emitter: 1.3.0 - get-value: 2.0.6 - has-value: 1.0.0 - isobject: 3.0.1 - set-value: 2.0.1 - to-object-path: 0.3.0 - union-value: 1.0.1 - unset-value: 1.0.0 - - /cacheable-lookup/5.0.4: - resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} - engines: {node: '>=10.6.0'} - - /cacheable-request/7.0.2: - resolution: {integrity: sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==} - engines: {node: '>=8'} - dependencies: - clone-response: 1.0.3 - get-stream: 5.2.0 - http-cache-semantics: 4.1.1 - keyv: 4.5.2 - lowercase-keys: 2.0.0 - normalize-url: 6.1.0 - responselike: 2.0.1 - - /call-bind/1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} - dependencies: - function-bind: 1.1.1 - get-intrinsic: 1.2.0 - - /call-me-maybe/1.0.2: - resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} - dev: true - - /callsite-record/4.1.5: - resolution: {integrity: sha512-OqeheDucGKifjQRx524URgV4z4NaKjocGhygTptDea+DLROre4ZEecA4KXDq+P7qlGCohYVNOh3qr+y5XH5Ftg==} - dependencies: - '@devexpress/error-stack-parser': 2.0.6 - '@types/lodash': 4.14.116 - callsite: 1.0.0 - chalk: 2.4.2 - highlight-es: 1.0.3 - lodash: 4.17.21 - pinkie-promise: 2.0.1 - dev: false - - /callsite/1.0.0: - resolution: {integrity: sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==} - dev: false - - /callsites/3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - /camel-case/4.1.2: - resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} - dependencies: - pascal-case: 3.1.2 - tslib: 2.3.1 - - /camelcase-css/2.0.1: - resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} - engines: {node: '>= 6'} - dev: true - - /camelcase-keys/6.2.2: - resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} - engines: {node: '>=8'} - dependencies: - camelcase: 5.3.1 - map-obj: 4.3.0 - quick-lru: 4.0.1 - dev: false - - /camelcase/5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - /camelcase/6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - - /camelcase/7.0.1: - resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} - engines: {node: '>=14.16'} - dev: true - - /caniuse-api/3.0.0: - resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - dependencies: - browserslist: 4.21.5 - caniuse-lite: 1.0.30001470 - lodash.memoize: 4.1.2 - lodash.uniq: 4.5.0 - dev: false - - /caniuse-lite/1.0.30001470: - resolution: {integrity: sha512-065uNwY6QtHCBOExzbV6m236DDhYCCtPmQUCoQtwkVqzud8v5QPidoMr6CoMkC2nfp6nksjttqWQRRh75LqUmA==} - - /capture-exit/2.0.0: - resolution: {integrity: sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==} - engines: {node: 6.* || 8.* || >= 10.*} - dependencies: - rsvp: 4.8.5 - dev: true - - /case-sensitive-paths-webpack-plugin/2.4.0: - resolution: {integrity: sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==} - engines: {node: '>=4'} - dev: true - - /case/1.6.3: - resolution: {integrity: sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==} - engines: {node: '>= 0.8.0'} - dev: true - - /ccount/1.1.0: - resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==} - dev: true - - /cdk-assets/2.7.0: - resolution: {integrity: sha512-6MMQ9/iXSXPQNOD2PsHRmE+0+qVTAS2Wjx/1FGtTT5rTGN3bRqnq8KQtkWyNz/RanvEgrouR9dSPY+tQzfdHmw==} - engines: {node: '>= 14.15.0'} - hasBin: true - dependencies: - '@aws-cdk/cloud-assembly-schema': 2.7.0 - '@aws-cdk/cx-api': 2.7.0 - archiver: 5.3.1 - aws-sdk: 2.1344.0 - glob: 7.2.3 - mime: 2.6.0 - yargs: 16.2.0 - dev: true - - /chalk/1.1.3: - resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} - engines: {node: '>=0.10.0'} - dependencies: - ansi-styles: 2.2.1 - escape-string-regexp: 1.0.5 - has-ansi: 2.0.0 - strip-ansi: 3.0.1 - supports-color: 2.0.0 - dev: false - - /chalk/2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - - /chalk/3.0.0: - resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} - engines: {node: '>=8'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: true - - /chalk/4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - /chalk/5.2.0: - resolution: {integrity: sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - dev: true - - /char-regex/1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - - /character-entities-legacy/1.1.4: - resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} - dev: true - - /character-entities/1.2.4: - resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} - dev: true - - /character-reference-invalid/1.1.4: - resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} - dev: true - - /chardet/0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - dev: false - - /charenc/0.0.2: - resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} - dev: true - - /chokidar/2.1.8: - resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==} - deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies - dependencies: - anymatch: 2.0.0 - async-each: 1.0.6 - braces: 2.3.2 - glob-parent: 3.1.0 - inherits: 2.0.4 - is-binary-path: 1.0.1 - is-glob: 4.0.3 - normalize-path: 3.0.0 - path-is-absolute: 1.0.1 - readdirp: 2.2.1 - upath: 1.2.0 - optionalDependencies: - fsevents: 1.2.13 - optional: true - - /chokidar/3.4.3: - resolution: {integrity: sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==} - engines: {node: '>= 8.10.0'} - dependencies: - anymatch: 3.1.3 - braces: 3.0.2 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.5.0 - optionalDependencies: - fsevents: 2.1.3 - - /chokidar/3.5.3: - resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} - engines: {node: '>= 8.10.0'} - dependencies: - anymatch: 3.1.3 - braces: 3.0.2 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.2 - - /chownr/1.1.4: - resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - - /chownr/2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - - /chrome-trace-event/1.0.3: - resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} - engines: {node: '>=6.0'} - - /ci-info/2.0.0: - resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} - - /ci-info/3.8.0: - resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} - engines: {node: '>=8'} - - /cipher-base/1.0.4: - resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - - /cjs-module-lexer/1.2.2: - resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} - - /class-utils/0.3.6: - resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} - engines: {node: '>=0.10.0'} - dependencies: - arr-union: 3.1.0 - define-property: 0.2.5 - isobject: 3.0.1 - static-extend: 0.1.2 - - /clean-css/4.2.4: - resolution: {integrity: sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==} - engines: {node: '>= 4.0'} - dependencies: - source-map: 0.6.1 - - /clean-css/5.3.2: - resolution: {integrity: sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==} - engines: {node: '>= 10.0'} - dependencies: - source-map: 0.6.1 - - /clean-stack/2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - dev: true - - /cli-boxes/2.2.1: - resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} - engines: {node: '>=6'} - - /cli-boxes/3.0.0: - resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} - engines: {node: '>=10'} - dev: true - - /cli-color/2.0.3: - resolution: {integrity: sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==} - engines: {node: '>=0.10'} - dependencies: - d: 1.0.1 - es5-ext: 0.10.62 - es6-iterator: 2.0.3 - memoizee: 0.4.15 - timers-ext: 0.1.7 - dev: true - - /cli-cursor/3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} - dependencies: - restore-cursor: 3.1.0 - dev: false - - /cli-spinners/2.7.0: - resolution: {integrity: sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==} - engines: {node: '>=6'} - dev: false - - /cli-table/0.3.11: - resolution: {integrity: sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==} - engines: {node: '>= 0.2.0'} - dependencies: - colors: 1.0.3 - dev: false - - /cli-table3/0.6.3: - resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==} - engines: {node: 10.* || >= 12.*} - dependencies: - string-width: 4.2.3 - optionalDependencies: - '@colors/colors': 1.5.0 - dev: true - - /cli-width/3.0.0: - resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} - engines: {node: '>= 10'} - dev: false - - /cliui/5.0.0: - resolution: {integrity: sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==} - dependencies: - string-width: 3.1.0 - strip-ansi: 5.2.0 - wrap-ansi: 5.1.0 - dev: false - - /cliui/6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - dev: true - - /cliui/7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - /cliui/8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - dev: true - - /clone-deep/4.0.1: - resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} - engines: {node: '>=6'} - dependencies: - is-plain-object: 2.0.4 - kind-of: 6.0.3 - shallow-clone: 3.0.1 - - /clone-response/1.0.3: - resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} - dependencies: - mimic-response: 1.0.1 - - /clone/1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - dev: false - - /clsx/1.2.1: - resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} - engines: {node: '>=6'} - dev: true - - /cmd-extension/1.0.2: - resolution: {integrity: sha512-iWDjmP8kvsMdBmLTHxFaqXikO8EdFRDfim7k6vUHglY/2xJ5jLrPsnQGijdfp4U+sr/BeecG0wKm02dSIAeQ1g==} - engines: {node: '>=10'} - dev: false - - /co/4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - - /code-point-at/1.1.0: - resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} - engines: {node: '>=0.10.0'} - dev: true - - /collapse-white-space/1.0.6: - resolution: {integrity: sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==} - dev: true - - /collect-v8-coverage/1.0.1_@types+node@14.18.36: - resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} - peerDependencies: - '@types/node': '>=12' - dependencies: - '@types/node': 14.18.36 - - /collection-visit/1.0.0: - resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} - engines: {node: '>=0.10.0'} - dependencies: - map-visit: 1.0.0 - object-visit: 1.0.1 - - /color-convert/1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - dependencies: - color-name: 1.1.3 - - /color-convert/2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - dependencies: - color-name: 1.1.4 - - /color-name/1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - - /color-name/1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - /color-support/1.1.3: - resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} - hasBin: true - dev: true - - /colord/2.9.3: - resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} - dev: false - - /colorette/2.0.19: - resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} - dev: false - - /colors/1.0.3: - resolution: {integrity: sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==} - engines: {node: '>=0.1.90'} - dev: false - - /colors/1.2.5: - resolution: {integrity: sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==} - engines: {node: '>=0.1.90'} - - /combined-stream/1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - dependencies: - delayed-stream: 1.0.0 - - /comma-separated-tokens/1.0.8: - resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} - dev: true - - /commander/10.0.0: - resolution: {integrity: sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==} - engines: {node: '>=14'} - dev: false - - /commander/2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - - /commander/4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} - - /commander/6.2.1: - resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} - engines: {node: '>= 6'} - dev: true - - /commander/7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} - engines: {node: '>= 10'} - - /commander/8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - - /commander/9.5.0: - resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} - engines: {node: ^12.20.0 || >=14} - requiresBuild: true - optional: true - - /common-path-prefix/3.0.0: - resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} - dev: true - - /commondir/1.0.1: - resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} - - /component-emitter/1.3.0: - resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} - - /compress-commons/4.1.1: - resolution: {integrity: sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==} - engines: {node: '>= 10'} - dependencies: - buffer-crc32: 0.2.13 - crc32-stream: 4.0.2 - normalize-path: 3.0.0 - readable-stream: 3.6.2 - dev: true - - /compressible/2.0.18: - resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} - engines: {node: '>= 0.6'} - dependencies: - mime-db: 1.52.0 - - /compression/1.7.4: - resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==} - engines: {node: '>= 0.8.0'} - dependencies: - accepts: 1.3.8 - bytes: 3.0.0 - compressible: 2.0.18 - debug: 2.6.9 - on-headers: 1.0.2 - safe-buffer: 5.1.2 - vary: 1.1.2 - - /compute-scroll-into-view/1.0.20: - resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} - dev: true - - /concat-map/0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - /concat-stream/1.6.2: - resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} - engines: {'0': node >= 0.8} - dependencies: - buffer-from: 1.1.2 - inherits: 2.0.4 - readable-stream: 2.3.8 - typedarray: 0.0.6 - - /conf/10.2.0: - resolution: {integrity: sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==} - engines: {node: '>=12'} - dependencies: - ajv: 8.12.0 - ajv-formats: 2.1.1 - atomically: 1.7.0 - debounce-fn: 4.0.0 - dot-prop: 6.0.1 - env-paths: 2.2.1 - json-schema-typed: 7.0.3 - onetime: 5.1.2 - pkg-up: 3.1.0 - semver: 7.3.8 - dev: true - - /configstore/5.0.1: - resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} - engines: {node: '>=8'} - dependencies: - dot-prop: 5.3.0 - graceful-fs: 4.2.11 - make-dir: 3.1.0 - unique-string: 2.0.0 - write-file-atomic: 3.0.3 - xdg-basedir: 4.0.0 - - /connect-history-api-fallback/2.0.0: - resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} - engines: {node: '>=0.8'} - dev: false - - /console-browserify/1.2.0: - resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} - - /console-control-strings/1.1.0: - resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - dev: true - - /constants-browserify/1.0.0: - resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} - - /constructs/10.0.130: - resolution: {integrity: sha512-9LYBePJHHnuXCr42eN0T4+O8xXHRxxak6G/UX+avt8ZZ/SNE9HFbFD8a+FKP8ixSNzzaEamDMswrMwPPTtU8cA==} - engines: {node: '>= 12.7.0'} - dev: true - - /content-disposition/0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} - dependencies: - safe-buffer: 5.2.1 - - /content-type/1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - - /convert-source-map/1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - - /convert-source-map/2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - - /cookie-signature/1.0.6: - resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - - /cookie/0.5.0: - resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} - engines: {node: '>= 0.6'} - - /copy-concurrently/1.0.5: - resolution: {integrity: sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==} - dependencies: - aproba: 1.2.0 - fs-write-stream-atomic: 1.0.10 - iferr: 0.1.5 - mkdirp: 0.5.6 - rimraf: 2.7.1 - run-queue: 1.0.3 - - /copy-descriptor/0.1.1: - resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} - engines: {node: '>=0.10.0'} - - /copy-to-clipboard/3.3.3: - resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} - dependencies: - toggle-selection: 1.0.6 - dev: true - - /core-js-compat/3.29.1: - resolution: {integrity: sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA==} - dependencies: - browserslist: 4.21.5 - dev: true - - /core-js-pure/3.29.1: - resolution: {integrity: sha512-4En6zYVi0i0XlXHVz/bi6l1XDjCqkKRq765NXuX+SnaIatlE96Odt5lMLjdxUiNI1v9OXI5DSLWYPlmTfkTktg==} - requiresBuild: true - dev: true - - /core-js/3.29.1: - resolution: {integrity: sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==} - requiresBuild: true - dev: true - - /core-util-is/1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - - /cors/2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - dev: false - - /cosmiconfig/6.0.0: - resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} - engines: {node: '>=8'} - dependencies: - '@types/parse-json': 4.0.0 - import-fresh: 3.3.0 - parse-json: 5.2.0 - path-type: 4.0.0 - yaml: 1.10.2 - dev: true - - /cosmiconfig/7.1.0: - resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} - engines: {node: '>=10'} - dependencies: - '@types/parse-json': 4.0.0 - import-fresh: 3.3.0 - parse-json: 5.2.0 - path-type: 4.0.0 - yaml: 1.10.2 - - /cp-file/7.0.0: - resolution: {integrity: sha512-0Cbj7gyvFVApzpK/uhCtQ/9kE9UnYpxMzaq5nQQC/Dh4iaj5fxp7iEFIullrYwzj8nf0qnsI1Qsx34hAeAebvw==} - engines: {node: '>=8'} - dependencies: - graceful-fs: 4.2.11 - make-dir: 3.1.0 - nested-error-stacks: 2.1.1 - p-event: 4.2.0 - dev: true - - /cpy/8.1.2: - resolution: {integrity: sha512-dmC4mUesv0OYH2kNFEidtf/skUwv4zePmGeepjyyJ0qTo5+8KhA1o99oIAwVVLzQMAeDJml74d6wPPKb6EZUTg==} - engines: {node: '>=8'} - dependencies: - arrify: 2.0.1 - cp-file: 7.0.0 - globby: 9.2.0 - has-glob: 1.0.0 - junk: 3.1.0 - nested-error-stacks: 2.1.1 - p-all: 2.1.0 - p-filter: 2.1.0 - p-map: 3.0.0 - dev: true - - /crc-32/1.2.2: - resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} - engines: {node: '>=0.8'} - hasBin: true - dev: true - - /crc32-stream/4.0.2: - resolution: {integrity: sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==} - engines: {node: '>= 10'} - dependencies: - crc-32: 1.2.2 - readable-stream: 3.6.2 - dev: true - - /create-ecdh/4.0.4: - resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} - dependencies: - bn.js: 4.12.0 - elliptic: 6.5.4 - - /create-hash/1.2.0: - resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} - dependencies: - cipher-base: 1.0.4 - inherits: 2.0.4 - md5.js: 1.3.5 - ripemd160: 2.0.2 - sha.js: 2.4.11 - - /create-hmac/1.1.7: - resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} - dependencies: - cipher-base: 1.0.4 - create-hash: 1.2.0 - inherits: 2.0.4 - ripemd160: 2.0.2 - safe-buffer: 5.2.1 - sha.js: 2.4.11 - - /cross-spawn/6.0.5: - resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} - engines: {node: '>=4.8'} - dependencies: - nice-try: 1.0.5 - path-key: 2.0.1 - semver: 5.7.1 - shebang-command: 1.2.0 - which: 1.3.1 - - /cross-spawn/7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - /crypt/0.0.2: - resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} - dev: true - - /crypto-browserify/3.12.0: - resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==} - dependencies: - browserify-cipher: 1.0.1 - browserify-sign: 4.2.1 - create-ecdh: 4.0.4 - create-hash: 1.2.0 - create-hmac: 1.1.7 - diffie-hellman: 5.0.3 - inherits: 2.0.4 - pbkdf2: 3.1.2 - public-encrypt: 4.0.3 - randombytes: 2.1.0 - randomfill: 1.0.4 - - /crypto-random-string/2.0.0: - resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} - engines: {node: '>=8'} - - /css-declaration-sorter/6.4.0_postcss@8.4.21: - resolution: {integrity: sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==} - engines: {node: ^10 || ^12 || >=14} - peerDependencies: - postcss: ^8.0.9 - dependencies: - postcss: 8.4.21 - dev: false - - /css-loader/3.6.0_webpack@4.44.2: - resolution: {integrity: sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==} - engines: {node: '>= 8.9.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - dependencies: - camelcase: 5.3.1 - cssesc: 3.0.0 - icss-utils: 4.1.1 - loader-utils: 1.4.2 - normalize-path: 3.0.0 - postcss: 7.0.39 - postcss-modules-extract-imports: 2.0.0 - postcss-modules-local-by-default: 3.0.3 - postcss-modules-scope: 2.2.0 - postcss-modules-values: 3.0.0 - postcss-value-parser: 4.2.0 - schema-utils: 2.7.1 - semver: 6.3.0 - webpack: 4.44.2 - dev: true - - /css-loader/5.2.7_webpack@4.44.2: - resolution: {integrity: sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.27.0 || ^5.0.0 - dependencies: - icss-utils: 5.1.0_postcss@8.4.21 - loader-utils: 2.0.4 - postcss: 8.4.21 - postcss-modules-extract-imports: 3.0.0_postcss@8.4.21 - postcss-modules-local-by-default: 4.0.0_postcss@8.4.21 - postcss-modules-scope: 3.0.0_postcss@8.4.21 - postcss-modules-values: 4.0.0_postcss@8.4.21 - postcss-value-parser: 4.2.0 - schema-utils: 3.1.1 - semver: 7.3.8 - webpack: 4.44.2 - dev: true - - /css-loader/6.6.0_webpack@5.80.0: - resolution: {integrity: sha512-FK7H2lisOixPT406s5gZM1S3l8GrfhEBT3ZiL2UX1Ng1XWs0y2GPllz/OTyvbaHe12VgQrIXIzuEGVlbUhodqg==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^5.0.0 - dependencies: - icss-utils: 5.1.0_postcss@8.4.21 - postcss: 8.4.21 - postcss-modules-extract-imports: 3.0.0_postcss@8.4.21 - postcss-modules-local-by-default: 4.0.0_postcss@8.4.21 - postcss-modules-scope: 3.0.0_postcss@8.4.21 - postcss-modules-values: 4.0.0_postcss@8.4.21 - postcss-value-parser: 4.2.0 - semver: 7.3.8 - webpack: 5.80.0 - - /css-minimizer-webpack-plugin/3.4.1_webpack@5.80.0: - resolution: {integrity: sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==} - engines: {node: '>= 12.13.0'} - peerDependencies: - '@parcel/css': '*' - clean-css: '*' - csso: '*' - esbuild: '*' - webpack: ^5.0.0 - peerDependenciesMeta: - '@parcel/css': - optional: true - clean-css: - optional: true - csso: - optional: true - esbuild: - optional: true - dependencies: - cssnano: 5.1.15_postcss@8.4.21 - jest-worker: 27.5.1 - postcss: 8.4.21 - schema-utils: 4.0.0 - serialize-javascript: 6.0.0 - source-map: 0.6.1 - webpack: 5.80.0 - dev: false - - /css-modules-loader-core/1.1.0: - resolution: {integrity: sha512-XWOBwgy5nwBn76aA+6ybUGL/3JBnCtBX9Ay9/OWIpzKYWlVHMazvJ+WtHumfi+xxdPF440cWK7JCYtt8xDifew==} - dependencies: - icss-replace-symbols: 1.1.0 - postcss: 6.0.1 - postcss-modules-extract-imports: 1.1.0 - postcss-modules-local-by-default: 1.2.0 - postcss-modules-scope: 1.1.0 - postcss-modules-values: 1.3.0 - dev: false - - /css-select/4.3.0: - resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} - dependencies: - boolbase: 1.0.0 - css-what: 6.1.0 - domhandler: 4.3.1 - domutils: 2.8.0 - nth-check: 2.1.1 - - /css-selector-tokenizer/0.7.3: - resolution: {integrity: sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==} - dependencies: - cssesc: 3.0.0 - fastparse: 1.1.2 - dev: false - - /css-tree/1.1.3: - resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} - engines: {node: '>=8.0.0'} - dependencies: - mdn-data: 2.0.14 - source-map: 0.6.1 - dev: false - - /css-what/6.1.0: - resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} - engines: {node: '>= 6'} - - /cssesc/3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - - /cssnano-preset-default/5.2.14_postcss@8.4.21: - resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - css-declaration-sorter: 6.4.0_postcss@8.4.21 - cssnano-utils: 3.1.0_postcss@8.4.21 - postcss: 8.4.21 - postcss-calc: 8.2.4_postcss@8.4.21 - postcss-colormin: 5.3.1_postcss@8.4.21 - postcss-convert-values: 5.1.3_postcss@8.4.21 - postcss-discard-comments: 5.1.2_postcss@8.4.21 - postcss-discard-duplicates: 5.1.0_postcss@8.4.21 - postcss-discard-empty: 5.1.1_postcss@8.4.21 - postcss-discard-overridden: 5.1.0_postcss@8.4.21 - postcss-merge-longhand: 5.1.7_postcss@8.4.21 - postcss-merge-rules: 5.1.4_postcss@8.4.21 - postcss-minify-font-values: 5.1.0_postcss@8.4.21 - postcss-minify-gradients: 5.1.1_postcss@8.4.21 - postcss-minify-params: 5.1.4_postcss@8.4.21 - postcss-minify-selectors: 5.2.1_postcss@8.4.21 - postcss-normalize-charset: 5.1.0_postcss@8.4.21 - postcss-normalize-display-values: 5.1.0_postcss@8.4.21 - postcss-normalize-positions: 5.1.1_postcss@8.4.21 - postcss-normalize-repeat-style: 5.1.1_postcss@8.4.21 - postcss-normalize-string: 5.1.0_postcss@8.4.21 - postcss-normalize-timing-functions: 5.1.0_postcss@8.4.21 - postcss-normalize-unicode: 5.1.1_postcss@8.4.21 - postcss-normalize-url: 5.1.0_postcss@8.4.21 - postcss-normalize-whitespace: 5.1.1_postcss@8.4.21 - postcss-ordered-values: 5.1.3_postcss@8.4.21 - postcss-reduce-initial: 5.1.2_postcss@8.4.21 - postcss-reduce-transforms: 5.1.0_postcss@8.4.21 - postcss-svgo: 5.1.0_postcss@8.4.21 - postcss-unique-selectors: 5.1.1_postcss@8.4.21 - dev: false - - /cssnano-utils/3.1.0_postcss@8.4.21: - resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - dev: false - - /cssnano/5.1.15_postcss@8.4.21: - resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - cssnano-preset-default: 5.2.14_postcss@8.4.21 - lilconfig: 2.1.0 - postcss: 8.4.21 - yaml: 1.10.2 - dev: false - - /csso/4.2.0: - resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} - engines: {node: '>=8.0.0'} - dependencies: - css-tree: 1.1.3 - dev: false - - /cssom/0.3.8: - resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} - - /cssom/0.5.0: - resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} - - /cssstyle/2.3.0: - resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} - engines: {node: '>=8'} - dependencies: - cssom: 0.3.8 - - /csstype/2.6.21: - resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==} - dev: true - - /csstype/3.1.1: - resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} - - /cyclist/1.0.1: - resolution: {integrity: sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==} - - /d/1.0.1: - resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} - dependencies: - es5-ext: 0.10.62 - type: 1.2.0 - dev: true - - /data-uri-to-buffer/3.0.1: - resolution: {integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==} - engines: {node: '>= 6'} - dev: true - - /data-urls/3.0.2: - resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} - engines: {node: '>=12'} - dependencies: - abab: 2.0.6 - whatwg-mimetype: 3.0.0 - whatwg-url: 11.0.0 - - /dataloader/2.2.2: - resolution: {integrity: sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==} - dev: true - - /date-format/4.0.14: - resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} - engines: {node: '>=4.0'} - dev: true - - /debounce-fn/4.0.0: - resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==} - engines: {node: '>=10'} - dependencies: - mimic-fn: 3.1.0 - dev: true - - /debug/2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - dependencies: - ms: 2.0.0 - - /debug/3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - dependencies: - ms: 2.1.3 - dev: true - - /debug/4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - - /debuglog/1.0.1: - resolution: {integrity: sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==} - dev: false - - /decamelize-keys/1.1.1: - resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} - engines: {node: '>=0.10.0'} - dependencies: - decamelize: 1.2.0 - map-obj: 1.0.1 - dev: false - - /decamelize/1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - - /decamelize/5.0.1: - resolution: {integrity: sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==} - engines: {node: '>=10'} - dev: true - - /decimal.js/10.4.3: - resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} - - /decode-uri-component/0.2.2: - resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} - engines: {node: '>=0.10'} - - /decompress-response/6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - dependencies: - mimic-response: 3.1.0 - - /dedent/0.7.0: - resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} - - /deep-extend/0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - - /deep-is/0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - - /deep-object-diff/1.1.9: - resolution: {integrity: sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==} - dev: true - - /deepmerge/4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - - /default-gateway/6.0.3: - resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==} - engines: {node: '>= 10'} - dependencies: - execa: 5.1.1 - dev: false - - /defaults/1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - dependencies: - clone: 1.0.4 - dev: false - - /defer-to-connect/2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - - /define-lazy-prop/2.0.0: - resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} - engines: {node: '>=8'} - dev: false - - /define-properties/1.2.0: - resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} - engines: {node: '>= 0.4'} - dependencies: - has-property-descriptors: 1.0.0 - object-keys: 1.1.1 - - /define-property/0.2.5: - resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==} - engines: {node: '>=0.10.0'} - dependencies: - is-descriptor: 0.1.6 - - /define-property/1.0.0: - resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} - engines: {node: '>=0.10.0'} - dependencies: - is-descriptor: 1.0.2 - - /define-property/2.0.2: - resolution: {integrity: sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==} - engines: {node: '>=0.10.0'} - dependencies: - is-descriptor: 1.0.2 - isobject: 3.0.1 - - /degenerator/3.0.2: - resolution: {integrity: sha512-c0mef3SNQo56t6urUU6tdQAs+ThoD0o9B9MJ8HEt7NQcGEILCRFqQb7ZbP9JAv+QF1Ky5plydhMR/IrqWDm+TQ==} - engines: {node: '>= 6'} - dependencies: - ast-types: 0.13.4 - escodegen: 1.14.3 - esprima: 4.0.1 - vm2: 3.9.14 - dev: true - - /delayed-stream/1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - - /delegates/1.0.0: - resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - dev: true - - /dendriform-immer-patch-optimiser/2.1.3_immer@9.0.21: - resolution: {integrity: sha512-QG2IegUCdlhycVwsBOJ7SNd18PgzyWPxBivTzuF0E1KFxaU47fHy/frud74A9E66a4WXyFFp9FLLC2XQDkVj7g==} - engines: {node: '>=10'} - peerDependencies: - immer: '9' - dependencies: - immer: 9.0.21 - dev: true - - /depcheck/1.4.3: - resolution: {integrity: sha512-vy8xe1tlLFu7t4jFyoirMmOR7x7N601ubU9Gkifyr9z8rjBFtEdWHDBMqXyk6OkK+94NXutzddVXJuo0JlUQKQ==} - engines: {node: '>=10'} - hasBin: true - dependencies: - '@babel/parser': 7.16.4 - '@babel/traverse': 7.21.3 - '@vue/compiler-sfc': 3.2.47 - camelcase: 6.3.0 - cosmiconfig: 7.1.0 - debug: 4.3.4 - deps-regex: 0.1.4 - ignore: 5.2.4 - is-core-module: 2.11.0 - js-yaml: 3.14.1 - json5: 2.2.3 - lodash: 4.17.21 - minimatch: 3.1.2 - multimatch: 5.0.0 - please-upgrade-node: 3.2.0 - query-ast: 1.0.5 - readdirp: 3.6.0 - require-package-name: 2.0.1 - resolve: 1.22.1 - sass: 1.49.11 - scss-parser: 1.0.6 - semver: 7.3.8 - yargs: 16.2.0 - transitivePeerDependencies: - - supports-color - dev: false - - /depd/1.1.2: - resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} - engines: {node: '>= 0.6'} - dev: false - - /depd/2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - - /dependency-path/9.2.8: - resolution: {integrity: sha512-S0OhIK7sIyAsph8hVH/LMCTDL3jozKtlrPx3dMQrlE2nAlXTquTT+AcOufphDMTQqLkfn4acvfiem9I1IWZ4jQ==} - engines: {node: '>=14.6'} - dependencies: - '@pnpm/crypto.base32-hash': 1.0.1 - '@pnpm/types': 8.9.0 - encode-registry: 3.0.0 - semver: 7.3.8 - dev: false - - /deps-regex/0.1.4: - resolution: {integrity: sha512-3tzwGYogSJi8HoG93R5x9NrdefZQOXgHgGih/7eivloOq6yC6O+yoFxZnkgP661twvfILONfoKRdF9GQOGx2RA==} - dev: false - - /des.js/1.0.1: - resolution: {integrity: sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==} - dependencies: - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - - /destroy/1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - - /detab/2.0.4: - resolution: {integrity: sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g==} - dependencies: - repeat-string: 1.6.1 - dev: true - - /detect-file/1.0.0: - resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} - engines: {node: '>=0.10.0'} - dev: false - - /detect-indent/6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - dev: false - - /detect-newline/3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} - - /detect-node/2.1.0: - resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} - dev: false - - /detect-port-alt/1.1.6: - resolution: {integrity: sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==} - engines: {node: '>= 4.2.1'} - hasBin: true - dependencies: - address: 1.2.2 - debug: 2.6.9 - dev: true - - /detect-port/1.5.1: - resolution: {integrity: sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==} - hasBin: true - dependencies: - address: 1.2.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: true - - /dezalgo/1.0.4: - resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} - dependencies: - asap: 2.0.6 - wrappy: 1.0.2 - dev: false - - /diff-sequences/27.5.1: - resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dev: true - - /diff-sequences/29.4.3: - resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - /diff/4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dev: true - - /diff/5.0.0: - resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} - engines: {node: '>=0.3.1'} - - /diffie-hellman/5.0.3: - resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} - dependencies: - bn.js: 4.12.0 - miller-rabin: 4.0.1 - randombytes: 2.1.0 - - /difflib/0.2.4: - resolution: {integrity: sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==} - dependencies: - heap: 0.2.7 - dev: true - - /dir-glob/2.2.2: - resolution: {integrity: sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==} - engines: {node: '>=4'} - dependencies: - path-type: 3.0.0 - dev: true - - /dir-glob/3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - dependencies: - path-type: 4.0.0 - - /dns-equal/1.0.0: - resolution: {integrity: sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==} - dev: false - - /dns-packet/5.5.0: - resolution: {integrity: sha512-USawdAUzRkV6xrqTjiAEp6M9YagZEzWcSUaZTcIFAiyQWW1SoI6KyId8y2+/71wbgHKQAKd+iupLv4YvEwYWvA==} - engines: {node: '>=6'} - dependencies: - '@leichtgewicht/ip-codec': 2.0.4 - dev: false - - /doctrine/2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - dependencies: - esutils: 2.0.3 - - /doctrine/3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dependencies: - esutils: 2.0.3 - - /dom-converter/0.2.0: - resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==} - dependencies: - utila: 0.4.0 - - /dom-serializer/1.4.1: - resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} - dependencies: - domelementtype: 2.3.0 - domhandler: 4.3.1 - entities: 2.2.0 - - /dom-walk/0.1.2: - resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==} - dev: true - - /domain-browser/1.2.0: - resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==} - engines: {node: '>=0.4', npm: '>=1.2'} - - /domelementtype/2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - - /domexception/4.0.0: - resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} - engines: {node: '>=12'} - dependencies: - webidl-conversions: 7.0.0 - - /domhandler/4.3.1: - resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} - engines: {node: '>= 4'} - dependencies: - domelementtype: 2.3.0 - - /domutils/2.8.0: - resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} - dependencies: - dom-serializer: 1.4.1 - domelementtype: 2.3.0 - domhandler: 4.3.1 - - /dot-case/3.0.4: - resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} - dependencies: - no-case: 3.0.4 - tslib: 2.3.1 - - /dot-prop/5.3.0: - resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} - engines: {node: '>=8'} - dependencies: - is-obj: 2.0.0 - - /dot-prop/6.0.1: - resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} - engines: {node: '>=10'} - dependencies: - is-obj: 2.0.0 - dev: true - - /dotenv-expand/5.1.0: - resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} - dev: true - - /dotenv/10.0.0: - resolution: {integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==} - engines: {node: '>=10'} - dev: true - - /dotenv/8.6.0: - resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} - engines: {node: '>=10'} - dev: true - - /downshift/6.1.12_react@16.13.1: - resolution: {integrity: sha512-7XB/iaSJVS4T8wGFT3WRXmSF1UlBHAA40DshZtkrIscIN+VC+Lh363skLxFTvJwtNgHxAMDGEHT4xsyQFWL+UA==} - peerDependencies: - react: '>=16.12.0' - dependencies: - '@babel/runtime': 7.21.0 - compute-scroll-into-view: 1.0.20 - prop-types: 15.8.1 - react: 16.13.1 - react-is: 17.0.2 - tslib: 2.3.1 - dev: true - - /dreamopt/0.8.0: - resolution: {integrity: sha512-vyJTp8+mC+G+5dfgsY+r3ckxlz+QMX40VjPQsZc5gxVAxLmi64TBoVkP54A/pRAXMXsbu2GMMBrZPxNv23waMg==} - engines: {node: '>=0.4.0'} - dependencies: - wordwrap: 1.0.0 - dev: true - - /duplexer/0.1.2: - resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - - /duplexify/3.7.1: - resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==} - dependencies: - end-of-stream: 1.4.4 - inherits: 2.0.4 - readable-stream: 2.3.8 - stream-shift: 1.0.1 - - /eastasianwidth/0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: true - - /ecdsa-sig-formatter/1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - dependencies: - safe-buffer: 5.2.1 - dev: false - - /ee-first/1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - - /electron-to-chromium/1.4.341: - resolution: {integrity: sha512-R4A8VfUBQY9WmAhuqY5tjHRf5fH2AAf6vqitBOE0y6u2PgHgqHSrhZmu78dIX3fVZtjqlwJNX1i2zwC3VpHtQQ==} - - /element-resize-detector/1.2.4: - resolution: {integrity: sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==} - dependencies: - batch-processor: 1.0.0 - dev: true - - /elliptic/6.5.4: - resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} - dependencies: - bn.js: 4.12.0 - brorand: 1.1.0 - hash.js: 1.1.7 - hmac-drbg: 1.0.1 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - - /emittery/0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} - - /emoji-regex/7.0.3: - resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==} - dev: false - - /emoji-regex/8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - /emoji-regex/9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - dev: true - - /emojis-list/3.0.0: - resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} - engines: {node: '>= 4'} - - /emotion-theming/10.3.0_tpm53lxjhhnjmtj6wgjp3t3pxi: - resolution: {integrity: sha512-mXiD2Oj7N9b6+h/dC6oLf9hwxbtKHQjoIqtodEyL8CpkN4F3V4IK/BT4D0C7zSs4BBFOu4UlPJbvvBLa88SGEA==} - peerDependencies: - '@emotion/core': ^10.0.27 - '@types/react': '>=16' - react: '>=16.3.0' - dependencies: - '@babel/runtime': 7.21.0 - '@emotion/core': 10.3.1_qjwx5m6wssz3lnb35xwkc3pz6q - '@emotion/weak-memoize': 0.2.5 - '@types/react': 16.14.23 - hoist-non-react-statics: 3.3.2 - react: 16.13.1 - dev: true - - /encode-registry/3.0.0: - resolution: {integrity: sha512-2fRYji8K6FwYuQ6EPBKR/J9mcqb7kIoNqt1vGvJr3NrvKfncRiNm00Oxo6gi/YJF8R5Sp2bNFSFdGKTG0rje1Q==} - engines: {node: '>=10'} - dependencies: - mem: 8.1.1 - dev: false - - /encodeurl/1.0.2: - resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} - engines: {node: '>= 0.8'} - - /encoding/0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - requiresBuild: true - dependencies: - iconv-lite: 0.6.3 - dev: true - optional: true - - /end-of-stream/1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - dependencies: - once: 1.4.0 - - /endent/2.1.0: - resolution: {integrity: sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w==} - dependencies: - dedent: 0.7.0 - fast-json-parse: 1.0.3 - objectorarray: 1.0.5 - dev: true - - /enhanced-resolve/4.5.0: - resolution: {integrity: sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==} - engines: {node: '>=6.9.0'} - dependencies: - graceful-fs: 4.2.11 - memory-fs: 0.5.0 - tapable: 1.1.3 - - /enhanced-resolve/5.13.0: - resolution: {integrity: sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg==} - engines: {node: '>=10.13.0'} - dependencies: - graceful-fs: 4.2.11 - tapable: 2.2.1 - - /enquirer/2.3.6: - resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} - engines: {node: '>=8.6'} - dependencies: - ansi-colors: 4.1.3 - dev: true - - /entities/2.2.0: - resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} - - /entities/4.4.0: - resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==} - engines: {node: '>=0.12'} - - /env-paths/2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - dev: true - - /envinfo/7.8.1: - resolution: {integrity: sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==} - engines: {node: '>=4'} - hasBin: true - dev: true - - /err-code/2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - dev: true - - /errno/0.1.8: - resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} - hasBin: true - dependencies: - prr: 1.0.1 - - /error-ex/1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - dependencies: - is-arrayish: 0.2.1 - - /error-stack-parser/2.1.4: - resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} - dependencies: - stackframe: 1.3.4 - dev: true - - /es-abstract/1.21.2: - resolution: {integrity: sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==} - engines: {node: '>= 0.4'} - dependencies: - array-buffer-byte-length: 1.0.0 - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - es-set-tostringtag: 2.0.1 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.5 - get-intrinsic: 1.2.0 - get-symbol-description: 1.0.0 - globalthis: 1.0.3 - gopd: 1.0.1 - has: 1.0.3 - has-property-descriptors: 1.0.0 - has-proto: 1.0.1 - has-symbols: 1.0.3 - internal-slot: 1.0.5 - is-array-buffer: 3.0.2 - is-callable: 1.2.7 - is-negative-zero: 2.0.2 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 - is-string: 1.0.7 - is-typed-array: 1.1.10 - is-weakref: 1.0.2 - object-inspect: 1.12.3 - object-keys: 1.1.1 - object.assign: 4.1.4 - regexp.prototype.flags: 1.4.3 - safe-regex-test: 1.0.0 - string.prototype.trim: 1.2.7 - string.prototype.trimend: 1.0.6 - string.prototype.trimstart: 1.0.6 - typed-array-length: 1.0.4 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.9 - - /es-array-method-boxes-properly/1.0.0: - resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} - - /es-get-iterator/1.1.3: - resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 - has-symbols: 1.0.3 - is-arguments: 1.1.1 - is-map: 2.0.2 - is-set: 2.0.2 - is-string: 1.0.7 - isarray: 2.0.5 - stop-iteration-iterator: 1.0.0 - dev: true - - /es-module-lexer/1.2.1: - resolution: {integrity: sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==} - - /es-set-tostringtag/2.0.1: - resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.0 - has: 1.0.3 - has-tostringtag: 1.0.0 - - /es-shim-unscopables/1.0.0: - resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} - dependencies: - has: 1.0.3 - - /es-to-primitive/1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} - dependencies: - is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 - - /es5-ext/0.10.62: - resolution: {integrity: sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==} - engines: {node: '>=0.10'} - requiresBuild: true - dependencies: - es6-iterator: 2.0.3 - es6-symbol: 3.1.3 - next-tick: 1.1.0 - dev: true - - /es5-shim/4.6.7: - resolution: {integrity: sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ==} - engines: {node: '>=0.4.0'} - dev: true - - /es6-iterator/2.0.3: - resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} - dependencies: - d: 1.0.1 - es5-ext: 0.10.62 - es6-symbol: 3.1.3 - dev: true - - /es6-shim/0.35.8: - resolution: {integrity: sha512-Twf7I2v4/1tLoIXMT8HlqaBSS5H2wQTs2wx3MNYCI8K1R1/clXyCazrcVCPm/FuO9cyV8+leEaZOWD5C253NDg==} - dev: true - - /es6-symbol/3.1.3: - resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==} - dependencies: - d: 1.0.1 - ext: 1.7.0 - dev: true - - /es6-weak-map/2.0.3: - resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==} - dependencies: - d: 1.0.1 - es5-ext: 0.10.62 - es6-iterator: 2.0.3 - es6-symbol: 3.1.3 - dev: true - - /esbuild-android-64/0.14.54: - resolution: {integrity: sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /esbuild-android-arm64/0.14.54: - resolution: {integrity: sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /esbuild-darwin-64/0.14.54: - resolution: {integrity: sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /esbuild-darwin-arm64/0.14.54: - resolution: {integrity: sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /esbuild-freebsd-64/0.14.54: - resolution: {integrity: sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-freebsd-arm64/0.14.54: - resolution: {integrity: sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-32/0.14.54: - resolution: {integrity: sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-64/0.14.54: - resolution: {integrity: sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-arm/0.14.54: - resolution: {integrity: sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-arm64/0.14.54: - resolution: {integrity: sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-mips64le/0.14.54: - resolution: {integrity: sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-ppc64le/0.14.54: - resolution: {integrity: sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-riscv64/0.14.54: - resolution: {integrity: sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-s390x/0.14.54: - resolution: {integrity: sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-netbsd-64/0.14.54: - resolution: {integrity: sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-openbsd-64/0.14.54: - resolution: {integrity: sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-runner/2.2.2_esbuild@0.12.29: - resolution: {integrity: sha512-fRFVXcmYVmSmtYm2mL8RlUASt2TDkGh3uRcvHFOKNr/T58VrfVeKD9uT9nlgxk96u0LS0ehS/GY7Da/bXWKkhw==} - hasBin: true - peerDependencies: - esbuild: '*' - dependencies: - esbuild: 0.12.29 - source-map-support: 0.5.21 - tslib: 2.4.0 - dev: true - - /esbuild-sunos-64/0.14.54: - resolution: {integrity: sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-32/0.14.54: - resolution: {integrity: sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-64/0.14.54: - resolution: {integrity: sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-arm64/0.14.54: - resolution: {integrity: sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild/0.12.29: - resolution: {integrity: sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==} - hasBin: true - requiresBuild: true - dev: true - - /esbuild/0.14.54: - resolution: {integrity: sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/linux-loong64': 0.14.54 - esbuild-android-64: 0.14.54 - esbuild-android-arm64: 0.14.54 - esbuild-darwin-64: 0.14.54 - esbuild-darwin-arm64: 0.14.54 - esbuild-freebsd-64: 0.14.54 - esbuild-freebsd-arm64: 0.14.54 - esbuild-linux-32: 0.14.54 - esbuild-linux-64: 0.14.54 - esbuild-linux-arm: 0.14.54 - esbuild-linux-arm64: 0.14.54 - esbuild-linux-mips64le: 0.14.54 - esbuild-linux-ppc64le: 0.14.54 - esbuild-linux-riscv64: 0.14.54 - esbuild-linux-s390x: 0.14.54 - esbuild-netbsd-64: 0.14.54 - esbuild-openbsd-64: 0.14.54 - esbuild-sunos-64: 0.14.54 - esbuild-windows-32: 0.14.54 - esbuild-windows-64: 0.14.54 - esbuild-windows-arm64: 0.14.54 - dev: true - - /esbuild/0.17.14: - resolution: {integrity: sha512-vOO5XhmVj/1XQR9NQ1UPq6qvMYL7QFJU57J5fKBKBKxp17uDt5PgxFDb4A2nEiXhr1qQs4x0F5+66hVVw4ruNw==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/android-arm': 0.17.14 - '@esbuild/android-arm64': 0.17.14 - '@esbuild/android-x64': 0.17.14 - '@esbuild/darwin-arm64': 0.17.14 - '@esbuild/darwin-x64': 0.17.14 - '@esbuild/freebsd-arm64': 0.17.14 - '@esbuild/freebsd-x64': 0.17.14 - '@esbuild/linux-arm': 0.17.14 - '@esbuild/linux-arm64': 0.17.14 - '@esbuild/linux-ia32': 0.17.14 - '@esbuild/linux-loong64': 0.17.14 - '@esbuild/linux-mips64el': 0.17.14 - '@esbuild/linux-ppc64': 0.17.14 - '@esbuild/linux-riscv64': 0.17.14 - '@esbuild/linux-s390x': 0.17.14 - '@esbuild/linux-x64': 0.17.14 - '@esbuild/netbsd-x64': 0.17.14 - '@esbuild/openbsd-x64': 0.17.14 - '@esbuild/sunos-x64': 0.17.14 - '@esbuild/win32-arm64': 0.17.14 - '@esbuild/win32-ia32': 0.17.14 - '@esbuild/win32-x64': 0.17.14 - dev: true - - /escalade/3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} - engines: {node: '>=6'} - - /escape-goat/2.1.1: - resolution: {integrity: sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==} - engines: {node: '>=8'} - - /escape-html/1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - - /escape-string-regexp/1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - - /escape-string-regexp/2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - - /escape-string-regexp/4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - /escodegen/1.14.3: - resolution: {integrity: sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==} - engines: {node: '>=4.0'} - hasBin: true - dependencies: - esprima: 4.0.1 - estraverse: 4.3.0 - esutils: 2.0.3 - optionator: 0.8.3 - optionalDependencies: - source-map: 0.6.1 - dev: true - - /escodegen/2.0.0: - resolution: {integrity: sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==} - engines: {node: '>=6.0'} - hasBin: true - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionator: 0.8.3 - optionalDependencies: - source-map: 0.6.1 - - /eslint-plugin-promise/6.0.1_eslint@7.30.0: - resolution: {integrity: sha512-uM4Tgo5u3UWQiroOyDEsYcVMOo7re3zmno0IZmB5auxoaQNIceAbXEkSt8RNrKtaYehARHG06pYK6K1JhtP0Zw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - dependencies: - eslint: 7.30.0 - dev: true - - /eslint-plugin-promise/6.0.1_eslint@8.7.0: - resolution: {integrity: sha512-uM4Tgo5u3UWQiroOyDEsYcVMOo7re3zmno0IZmB5auxoaQNIceAbXEkSt8RNrKtaYehARHG06pYK6K1JhtP0Zw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - dependencies: - eslint: 8.7.0 - dev: false - - /eslint-plugin-react/7.27.1_eslint@7.30.0: - resolution: {integrity: sha512-meyunDjMMYeWr/4EBLTV1op3iSG3mjT/pz5gti38UzfM4OPpNc2m0t2xvKCOMU5D6FSdd34BIMFOvQbW+i8GAA==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 - doctrine: 2.1.0 - eslint: 7.30.0 - estraverse: 5.3.0 - jsx-ast-utils: 3.3.3 - minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - object.hasown: 1.1.2 - object.values: 1.1.6 - prop-types: 15.8.1 - resolve: 2.0.0-next.4 - semver: 6.3.0 - string.prototype.matchall: 4.0.8 - dev: true - - /eslint-plugin-react/7.27.1_eslint@8.7.0: - resolution: {integrity: sha512-meyunDjMMYeWr/4EBLTV1op3iSG3mjT/pz5gti38UzfM4OPpNc2m0t2xvKCOMU5D6FSdd34BIMFOvQbW+i8GAA==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 - doctrine: 2.1.0 - eslint: 8.7.0 - estraverse: 5.3.0 - jsx-ast-utils: 3.3.3 - minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - object.hasown: 1.1.2 - object.values: 1.1.6 - prop-types: 15.8.1 - resolve: 2.0.0-next.4 - semver: 6.3.0 - string.prototype.matchall: 4.0.8 - dev: false - - /eslint-plugin-tsdoc/0.2.17: - resolution: {integrity: sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==} - dependencies: - '@microsoft/tsdoc': 0.14.2 - '@microsoft/tsdoc-config': 0.16.2 - - /eslint-scope/4.0.3: - resolution: {integrity: sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==} - engines: {node: '>=4.0.0'} - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - - /eslint-scope/5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - - /eslint-scope/7.1.1: - resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - /eslint-utils/2.1.0: - resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} - engines: {node: '>=6'} - dependencies: - eslint-visitor-keys: 1.3.0 - dev: true - - /eslint-utils/3.0.0_eslint@8.7.0: - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - dependencies: - eslint: 8.7.0 - eslint-visitor-keys: 2.1.0 - - /eslint-visitor-keys/1.3.0: - resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} - engines: {node: '>=4'} - dev: true - - /eslint-visitor-keys/2.1.0: - resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} - engines: {node: '>=10'} - - /eslint-visitor-keys/3.4.0: - resolution: {integrity: sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - /eslint/7.30.0: - resolution: {integrity: sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg==} - engines: {node: ^10.12.0 || >=12.0.0} - hasBin: true - dependencies: - '@babel/code-frame': 7.12.11 - '@eslint/eslintrc': 0.4.3 - '@humanwhocodes/config-array': 0.5.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - enquirer: 2.3.6 - escape-string-regexp: 4.0.0 - eslint-scope: 5.1.1 - eslint-utils: 2.1.0 - eslint-visitor-keys: 2.1.0 - espree: 7.3.1 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - functional-red-black-tree: 1.0.1 - glob-parent: 5.1.2 - globals: 13.20.0 - ignore: 4.0.6 - import-fresh: 3.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - js-yaml: 3.13.1 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.1 - progress: 2.0.3 - regexpp: 3.2.0 - semver: 7.3.8 - strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 - table: 6.8.1 - text-table: 0.2.0 - v8-compile-cache: 2.3.0 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint/8.36.0: - resolution: {integrity: sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true - dependencies: - '@eslint-community/eslint-utils': 4.4.0_eslint@8.36.0 - '@eslint-community/regexpp': 4.4.1 - '@eslint/eslintrc': 2.0.1 - '@eslint/js': 8.36.0 - '@humanwhocodes/config-array': 0.11.8 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.1.1 - eslint-visitor-keys: 3.4.0 - espree: 9.5.0 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.20.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.4 - import-fresh: 3.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-sdsl: 4.4.0 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.1 - strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint/8.7.0: - resolution: {integrity: sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true - dependencies: - '@eslint/eslintrc': 1.4.1 - '@humanwhocodes/config-array': 0.9.5 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.1.1 - eslint-utils: 3.0.0_eslint@8.7.0 - eslint-visitor-keys: 3.4.0 - espree: 9.5.0 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - functional-red-black-tree: 1.0.1 - glob-parent: 6.0.2 - globals: 13.20.0 - ignore: 5.2.4 - import-fresh: 3.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.1 - regexpp: 3.2.0 - strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 - text-table: 0.2.0 - v8-compile-cache: 2.3.0 - transitivePeerDependencies: - - supports-color - - /espree/7.3.1: - resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - acorn: 7.4.1 - acorn-jsx: 5.3.2_acorn@7.4.1 - eslint-visitor-keys: 1.3.0 - dev: true - - /espree/9.5.0: - resolution: {integrity: sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.8.2 - acorn-jsx: 5.3.2_acorn@8.8.2 - eslint-visitor-keys: 3.4.0 - - /esprima/4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - /esquery/1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} - engines: {node: '>=0.10'} - dependencies: - estraverse: 5.3.0 - - /esrecurse/4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - dependencies: - estraverse: 5.3.0 - - /estraverse/4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - - /estraverse/5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - - /estree-to-babel/3.2.1: - resolution: {integrity: sha512-YNF+mZ/Wu2FU/gvmzuWtYc8rloubL7wfXCTgouFrnjGVXPA/EeYYA7pupXWrb3Iv1cTBeSSxxJIbK23l4MRNqg==} - engines: {node: '>=8.3.0'} - dependencies: - '@babel/traverse': 7.21.3 - '@babel/types': 7.21.3 - c8: 7.13.0 - transitivePeerDependencies: - - supports-color - dev: true - - /estree-walker/2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - dev: false - - /esutils/2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - /etag/1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - - /event-emitter/0.3.5: - resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} - dependencies: - d: 1.0.1 - es5-ext: 0.10.62 - dev: true - - /eventemitter3/4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - - /events/1.1.1: - resolution: {integrity: sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==} - engines: {node: '>=0.4.x'} - dev: true - - /events/3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - - /evp_bytestokey/1.0.3: - resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} - dependencies: - md5.js: 1.3.5 - safe-buffer: 5.2.1 - - /exec-sh/0.3.6: - resolution: {integrity: sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==} - dev: true - - /execa/1.0.0: - resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==} - engines: {node: '>=6'} - dependencies: - cross-spawn: 6.0.5 - get-stream: 4.1.0 - is-stream: 1.1.0 - npm-run-path: 2.0.2 - p-finally: 1.0.0 - signal-exit: 3.0.7 - strip-eof: 1.0.0 - dev: true - - /execa/5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - /exit/0.1.2: - resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} - engines: {node: '>= 0.8.0'} - - /expand-brackets/2.1.4: - resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==} - engines: {node: '>=0.10.0'} - dependencies: - debug: 2.6.9 - define-property: 0.2.5 - extend-shallow: 2.0.1 - posix-character-classes: 0.1.1 - regex-not: 1.0.2 - snapdragon: 0.8.2 - to-regex: 3.0.2 - - /expand-tilde/2.0.2: - resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} - engines: {node: '>=0.10.0'} - dependencies: - homedir-polyfill: 1.0.3 - dev: false - - /expect/29.5.0: - resolution: {integrity: sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/expect-utils': 29.5.0 - jest-get-type: 29.4.3 - jest-matcher-utils: 29.5.0 - jest-message-util: 29.5.0 - jest-util: 29.5.0 - - /express/4.18.1: - resolution: {integrity: sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==} - engines: {node: '>= 0.10.0'} - dependencies: - accepts: 1.3.8 - array-flatten: 1.1.1 - body-parser: 1.20.0 - content-disposition: 0.5.4 - content-type: 1.0.5 - cookie: 0.5.0 - cookie-signature: 1.0.6 - debug: 2.6.9 - depd: 2.0.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 1.2.0 - fresh: 0.5.2 - http-errors: 2.0.0 - merge-descriptors: 1.0.1 - methods: 1.1.2 - on-finished: 2.4.1 - parseurl: 1.3.3 - path-to-regexp: 0.1.7 - proxy-addr: 2.0.7 - qs: 6.10.3 - range-parser: 1.2.1 - safe-buffer: 5.2.1 - send: 0.18.0 - serve-static: 1.15.0 - setprototypeof: 1.2.0 - statuses: 2.0.1 - type-is: 1.6.18 - utils-merge: 1.0.1 - vary: 1.1.2 - - /ext/1.7.0: - resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} - dependencies: - type: 2.7.2 - dev: true - - /extend-shallow/2.0.1: - resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} - engines: {node: '>=0.10.0'} - dependencies: - is-extendable: 0.1.1 - - /extend-shallow/3.0.2: - resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==} - engines: {node: '>=0.10.0'} - dependencies: - assign-symbols: 1.0.0 - is-extendable: 1.0.1 - - /extend/3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - dev: true - - /external-editor/3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} - dependencies: - chardet: 0.7.0 - iconv-lite: 0.4.24 - tmp: 0.0.33 - dev: false - - /extglob/2.0.4: - resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==} - engines: {node: '>=0.10.0'} - dependencies: - array-unique: 0.3.2 - define-property: 1.0.0 - expand-brackets: 2.1.4 - extend-shallow: 2.0.1 - fragment-cache: 0.2.1 - regex-not: 1.0.2 - snapdragon: 0.8.2 - to-regex: 3.0.2 - - /extract-zip/1.7.0: - resolution: {integrity: sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==} - hasBin: true - dependencies: - concat-stream: 1.6.2 - debug: 2.6.9 - mkdirp: 0.5.6 - yauzl: 2.10.0 - dev: true - - /fast-decode-uri-component/1.0.1: - resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} - dev: false - - /fast-deep-equal/3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - /fast-glob/2.2.7: - resolution: {integrity: sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==} - engines: {node: '>=4.0.0'} - dependencies: - '@mrmlnc/readdir-enhanced': 2.2.1 - '@nodelib/fs.stat': 1.1.3 - glob-parent: 3.1.0 - is-glob: 4.0.3 - merge2: 1.4.1 - micromatch: 3.1.10 - dev: true - - /fast-glob/3.2.12: - resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} - engines: {node: '>=8.6.0'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.5 - - /fast-json-parse/1.0.3: - resolution: {integrity: sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==} - dev: true - - /fast-json-stable-stringify/2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - /fast-json-stringify/2.7.13: - resolution: {integrity: sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA==} - engines: {node: '>= 10.0.0'} - dependencies: - ajv: 6.12.6 - deepmerge: 4.3.1 - rfdc: 1.3.0 - string-similarity: 4.0.4 - dev: false - - /fast-levenshtein/2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - - /fast-redact/3.1.2: - resolution: {integrity: sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==} - engines: {node: '>=6'} - dev: false - - /fast-safe-stringify/2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - - /fastify-error/0.3.1: - resolution: {integrity: sha512-oCfpcsDndgnDVgiI7bwFKAun2dO+4h84vBlkWsWnz/OUK9Reff5UFoFl241xTiLeHWX/vU9zkDVXqYUxjOwHcQ==} - dev: false - - /fastify-warning/0.2.0: - resolution: {integrity: sha512-s1EQguBw/9qtc1p/WTY4eq9WMRIACkj+HTcOIK1in4MV5aFaQC9ZCIt0dJ7pr5bIf4lPpHvAtP2ywpTNgs7hqw==} - deprecated: This module renamed to process-warning - dev: false - - /fastify/3.16.2: - resolution: {integrity: sha512-tdu0fz6wk9AbtD91AbzZGjKgEQLcIy7rT2vEzTUL/zifAMS/L7ViKY9p9k3g3yCRnIQzYzxH2RAbvYZaTbKasw==} - engines: {node: '>=10.16.0'} - dependencies: - '@fastify/ajv-compiler': 1.1.0 - '@fastify/proxy-addr': 3.0.0 - abstract-logging: 2.0.1 - avvio: 7.2.5 - fast-json-stringify: 2.7.13 - fastify-error: 0.3.1 - fastify-warning: 0.2.0 - find-my-way: 4.5.1 - flatstr: 1.0.12 - light-my-request: 4.12.0 - pino: 6.14.0 - readable-stream: 3.6.2 - rfdc: 1.3.0 - secure-json-parse: 2.7.0 - semver: 7.3.8 - tiny-lru: 7.0.6 - transitivePeerDependencies: - - supports-color - dev: false - - /fastparse/1.1.2: - resolution: {integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==} - dev: false - - /fastq/1.15.0: - resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} - dependencies: - reusify: 1.0.4 - - /fault/1.0.4: - resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} - dependencies: - format: 0.2.2 - dev: true - - /faye-websocket/0.11.4: - resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} - engines: {node: '>=0.8.0'} - dependencies: - websocket-driver: 0.7.4 - dev: false - - /fb-watchman/2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - dependencies: - bser: 2.1.1 - - /fd-slicer/1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - dependencies: - pend: 1.2.0 - dev: true - - /figgy-pudding/3.5.2: - resolution: {integrity: sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==} - - /figures/3.0.0: - resolution: {integrity: sha512-HKri+WoWoUgr83pehn/SIgLOMZ9nAWC6dcGj26RY2R4F50u4+RTUz0RCrUlOV3nKRAICW1UGzyb+kcX2qK1S/g==} - engines: {node: '>=8'} - dependencies: - escape-string-regexp: 1.0.5 - dev: false - - /file-entry-cache/6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flat-cache: 3.0.4 - - /file-loader/6.0.0_webpack@4.44.2: - resolution: {integrity: sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - dependencies: - loader-utils: 2.0.4 - schema-utils: 2.7.1 - webpack: 4.44.2 - dev: true - - /file-loader/6.2.0_webpack@4.44.2: - resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - dependencies: - loader-utils: 2.0.4 - schema-utils: 3.1.2 - webpack: 4.44.2 - dev: true - - /file-system-cache/1.1.0: - resolution: {integrity: sha512-IzF5MBq+5CR0jXx5RxPe4BICl/oEhBSXKaL9fLhAXrIfIUS77Hr4vzrYyqYMHN6uTt+BOqi3fDCTjjEBCjERKw==} - dependencies: - fs-extra: 10.1.0 - ramda: 0.28.0 - dev: true - - /file-uri-to-path/1.0.0: - resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - requiresBuild: true - optional: true - - /file-uri-to-path/2.0.0: - resolution: {integrity: sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==} - engines: {node: '>= 6'} - dev: true - - /fill-range/4.0.0: - resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==} - engines: {node: '>=0.10.0'} - dependencies: - extend-shallow: 2.0.1 - is-number: 3.0.0 - repeat-string: 1.6.1 - to-regex-range: 2.1.1 - - /fill-range/7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} - engines: {node: '>=8'} - dependencies: - to-regex-range: 5.0.1 - - /finalhandler/1.2.0: - resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} - engines: {node: '>= 0.8'} - dependencies: - debug: 2.6.9 - encodeurl: 1.0.2 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.1 - unpipe: 1.0.0 - - /find-cache-dir/2.1.0: - resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==} - engines: {node: '>=6'} - dependencies: - commondir: 1.0.1 - make-dir: 2.1.0 - pkg-dir: 3.0.0 - - /find-cache-dir/3.3.2: - resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} - engines: {node: '>=8'} - dependencies: - commondir: 1.0.1 - make-dir: 3.1.0 - pkg-dir: 4.2.0 - dev: true - - /find-my-way/4.5.1: - resolution: {integrity: sha512-kE0u7sGoUFbMXcOG/xpkmz4sRLCklERnBcg7Ftuu1iAxsfEt2S46RLJ3Sq7vshsEy2wJT2hZxE58XZK27qa8kg==} - engines: {node: '>=10'} - dependencies: - fast-decode-uri-component: 1.0.1 - fast-deep-equal: 3.1.3 - safe-regex2: 2.0.0 - semver-store: 0.3.0 - dev: false - - /find-root/1.1.0: - resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} - dev: true - - /find-up/3.0.0: - resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} - engines: {node: '>=6'} - dependencies: - locate-path: 3.0.0 - - /find-up/4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - - /find-up/5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - /find-yarn-workspace-root2/1.2.16: - resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} - dependencies: - micromatch: 4.0.5 - pkg-dir: 4.2.0 - dev: false - - /findup-sync/3.0.0: - resolution: {integrity: sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==} - engines: {node: '>= 0.10'} - dependencies: - detect-file: 1.0.0 - is-glob: 4.0.3 - micromatch: 3.1.10 - resolve-dir: 1.0.1 - dev: false - - /flat-cache/3.0.4: - resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flatted: 3.2.7 - rimraf: 3.0.2 - - /flatstr/1.0.12: - resolution: {integrity: sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==} - dev: false - - /flatted/3.2.7: - resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} - - /flow-parser/0.202.1: - resolution: {integrity: sha512-IA8mhyNEUtzAKh+lj1yNDLFiUr1NSwPC+exQgghQNARFU/DeWGpoNmuYYzMDFIYsOdVdDoTJTxRc+/cS9CVvNg==} - engines: {node: '>=0.4.0'} - dev: true - - /flush-write-stream/1.1.1: - resolution: {integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==} - dependencies: - inherits: 2.0.4 - readable-stream: 2.3.8 - - /follow-redirects/1.15.2: - resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - - /for-each/0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - dependencies: - is-callable: 1.2.7 - - /for-in/1.0.2: - resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} - engines: {node: '>=0.10.0'} - - /foreground-child/2.0.0: - resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} - engines: {node: '>=8.0.0'} - dependencies: - cross-spawn: 7.0.3 - signal-exit: 3.0.7 - dev: true - - /fork-ts-checker-webpack-plugin/4.1.6: - resolution: {integrity: sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==} - engines: {node: '>=6.11.5', yarn: '>=1.0.0'} - dependencies: - '@babel/code-frame': 7.18.6 - chalk: 2.4.2 - micromatch: 3.1.10 - minimatch: 3.1.2 - semver: 5.7.1 - tapable: 1.1.3 - worker-rpc: 0.1.1 - dev: true - - /fork-ts-checker-webpack-plugin/6.5.3_2if2pfw4ytlihdsiqpdavzlwg4: - resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==} - engines: {node: '>=10', yarn: '>=1.0.0'} - peerDependencies: - eslint: '>= 6' - typescript: '>= 2.7' - vue-template-compiler: '*' - webpack: '>= 4' - peerDependenciesMeta: - eslint: - optional: true - vue-template-compiler: - optional: true - dependencies: - '@babel/code-frame': 7.18.6 - '@types/json-schema': 7.0.11 - chalk: 4.1.2 - chokidar: 3.5.3 - cosmiconfig: 6.0.0 - deepmerge: 4.3.1 - fs-extra: 9.1.0 - glob: 7.2.3 - memfs: 3.4.3 - minimatch: 3.1.2 - schema-utils: 2.7.0 - semver: 7.3.8 - tapable: 1.1.3 - typescript: 5.0.4 - webpack: 4.44.2 - dev: true - - /fork-ts-checker-webpack-plugin/6.5.3_e3gvcbqyz74feggzy4n3jv5qrm: - resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==} - engines: {node: '>=10', yarn: '>=1.0.0'} - peerDependencies: - eslint: '>= 6' - typescript: '>= 2.7' - vue-template-compiler: '*' - webpack: '>= 4' - peerDependenciesMeta: - eslint: - optional: true - vue-template-compiler: - optional: true - dependencies: - '@babel/code-frame': 7.18.6 - '@types/json-schema': 7.0.11 - chalk: 4.1.2 - chokidar: 3.5.3 - cosmiconfig: 6.0.0 - deepmerge: 4.3.1 - eslint: 8.7.0 - fs-extra: 9.1.0 - glob: 7.2.3 - memfs: 3.4.3 - minimatch: 3.1.2 - schema-utils: 2.7.0 - semver: 7.3.8 - tapable: 1.1.3 - typescript: 5.0.4 - webpack: 4.44.2 - dev: true - - /form-data/3.0.1: - resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} - engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - - /form-data/4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - - /format/0.2.2: - resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} - engines: {node: '>=0.4.x'} - dev: true - - /forwarded/0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - - /fraction.js/4.2.0: - resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} - - /fragment-cache/0.2.1: - resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} - engines: {node: '>=0.10.0'} - dependencies: - map-cache: 0.2.2 - - /fresh/0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} - - /from2/2.3.0: - resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==} - dependencies: - inherits: 2.0.4 - readable-stream: 2.3.8 - - /fs-constants/1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - dev: true - - /fs-extra/10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.0 - dev: true - - /fs-extra/7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - /fs-extra/8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - dev: true - - /fs-extra/9.1.0: - resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} - engines: {node: '>=10'} - dependencies: - at-least-node: 1.0.0 - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.0 - dev: true - - /fs-minipass/2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - - /fs-monkey/1.0.3: - resolution: {integrity: sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==} - - /fs-write-stream-atomic/1.0.10: - resolution: {integrity: sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==} - dependencies: - graceful-fs: 4.2.11 - iferr: 0.1.5 - imurmurhash: 0.1.4 - readable-stream: 2.3.8 - - /fs.realpath/1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - /fsevents/1.2.13: - resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==} - engines: {node: '>= 4.0'} - os: [darwin] - deprecated: The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2 - requiresBuild: true - dependencies: - bindings: 1.5.0 - nan: 2.17.0 - optional: true - - /fsevents/2.1.3: - resolution: {integrity: sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - deprecated: '"Please update to latest v2.3 or v2.2"' - requiresBuild: true - optional: true - - /fsevents/2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - optional: true - - /ftp/0.3.10: - resolution: {integrity: sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==} - engines: {node: '>=0.8.0'} - dependencies: - readable-stream: 1.1.14 - xregexp: 2.0.0 - dev: true - - /function-bind/1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - - /function.prototype.name/1.1.5: - resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - functions-have-names: 1.2.3 - - /functional-red-black-tree/1.0.1: - resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} - - /functions-have-names/1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - - /fuse.js/3.6.1: - resolution: {integrity: sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw==} - engines: {node: '>=6'} - dev: true - - /gauge/2.7.4: - resolution: {integrity: sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==} - dependencies: - aproba: 1.2.0 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - object-assign: 4.1.1 - signal-exit: 3.0.7 - string-width: 1.0.2 - strip-ansi: 3.0.1 - wide-align: 1.1.5 - dev: true - - /gauge/3.0.2: - resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} - engines: {node: '>=10'} - dependencies: - aproba: 2.0.0 - color-support: 1.1.3 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - object-assign: 4.1.1 - signal-exit: 3.0.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wide-align: 1.1.5 - dev: true - - /generic-names/2.0.1: - resolution: {integrity: sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ==} - dependencies: - loader-utils: 1.4.2 - dev: false - - /gensync/1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - - /get-caller-file/2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - /get-intrinsic/1.2.0: - resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} - dependencies: - function-bind: 1.1.1 - has: 1.0.3 - has-symbols: 1.0.3 - - /get-package-type/0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} - - /get-port/5.1.1: - resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} - engines: {node: '>=8'} - dev: true - - /get-stream/4.1.0: - resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} - engines: {node: '>=6'} - dependencies: - pump: 3.0.0 - dev: true - - /get-stream/5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - dependencies: - pump: 3.0.0 - - /get-stream/6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - /get-symbol-description/1.0.0: - resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 - - /get-uri/3.0.2: - resolution: {integrity: sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==} - engines: {node: '>= 6'} - dependencies: - '@tootallnate/once': 1.1.2 - data-uri-to-buffer: 3.0.1 - debug: 4.3.4 - file-uri-to-path: 2.0.0 - fs-extra: 8.1.0 - ftp: 0.3.10 - transitivePeerDependencies: - - supports-color - dev: true - - /get-value/2.0.6: - resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} - engines: {node: '>=0.10.0'} - - /git-repo-info/2.1.1: - resolution: {integrity: sha512-8aCohiDo4jwjOwma4FmYFd3i97urZulL8XL24nIPxuE+GZnfsAyy/g2Shqx6OjUiFKUXZM+Yy+KHnOmmA3FVcg==} - engines: {node: '>= 4.0'} - dev: false - - /github-slugger/1.5.0: - resolution: {integrity: sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==} - dev: true - - /giturl/1.0.1: - resolution: {integrity: sha512-wQourBdI13n8tbjcZTDl6k+ZrCRMU6p9vfp9jknZq+zfWc8xXNztpZFM4XkPHVzHcMSUZxEMYYKZjIGkPlei6Q==} - engines: {node: '>= 0.10.0'} - dev: false - - /glob-escape/0.0.2: - resolution: {integrity: sha512-L/cXYz8x7qer1HAyUQ+mbjcUsJVdpRxpAf7CwqHoNBs9vTpABlGfNN4tzkDxt+u3Z7ZncVyKlCNPtzb0R/7WbA==} - engines: {node: '>= 0.10'} - - /glob-parent/3.1.0: - resolution: {integrity: sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==} - dependencies: - is-glob: 3.1.0 - path-dirname: 1.0.2 - - /glob-parent/5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - dependencies: - is-glob: 4.0.3 - - /glob-parent/6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - dependencies: - is-glob: 4.0.3 - - /glob-promise/3.4.0_glob@7.2.3: - resolution: {integrity: sha512-q08RJ6O+eJn+dVanerAndJwIcumgbDdYiUT7zFQl3Wm1xD6fBKtah7H8ZJChj4wP+8C+QfeVy8xautR7rdmKEw==} - engines: {node: '>=4'} - peerDependencies: - glob: '*' - dependencies: - '@types/glob': 7.1.1 - glob: 7.2.3 - dev: true - - /glob-to-regexp/0.3.0: - resolution: {integrity: sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig==} - dev: true - - /glob-to-regexp/0.4.1: - resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - - /glob/7.0.6: - resolution: {integrity: sha512-f8c0rE8JiCxpa52kWPAOa3ZaYEnzofDzCQLCn3Vdk0Z5OVLq3BsRFJI4S4ykpeVW6QMGBUkMeUpoEgWnMTnw5Q==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.0.8 - once: 1.4.0 - path-is-absolute: 1.0.1 - - /glob/7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - /global-dirs/3.0.1: - resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} - engines: {node: '>=10'} - dependencies: - ini: 2.0.0 - - /global-modules/1.0.0: - resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==} - engines: {node: '>=0.10.0'} - dependencies: - global-prefix: 1.0.2 - is-windows: 1.0.2 - resolve-dir: 1.0.1 - dev: false - - /global-modules/2.0.0: - resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} - engines: {node: '>=6'} - dependencies: - global-prefix: 3.0.0 - dev: false - - /global-prefix/1.0.2: - resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==} - engines: {node: '>=0.10.0'} - dependencies: - expand-tilde: 2.0.2 - homedir-polyfill: 1.0.3 - ini: 1.3.8 - is-windows: 1.0.2 - which: 1.3.1 - dev: false - - /global-prefix/3.0.0: - resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} - engines: {node: '>=6'} - dependencies: - ini: 1.3.8 - kind-of: 6.0.3 - which: 1.3.1 - dev: false - - /global/4.4.0: - resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==} - dependencies: - min-document: 2.19.0 - process: 0.11.10 - dev: true - - /globals/11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - - /globals/13.20.0: - resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.20.2 - - /globalthis/1.0.3: - resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} - engines: {node: '>= 0.4'} - dependencies: - define-properties: 1.2.0 - - /globby/11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.2.12 - ignore: 5.2.4 - merge2: 1.4.1 - slash: 3.0.0 - - /globby/9.2.0: - resolution: {integrity: sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==} - engines: {node: '>=6'} - dependencies: - '@types/glob': 7.1.1 - array-union: 1.0.2 - dir-glob: 2.2.2 - fast-glob: 2.2.7 - glob: 7.2.3 - ignore: 4.0.6 - pify: 4.0.1 - slash: 2.0.0 - dev: true - - /gopd/1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - dependencies: - get-intrinsic: 1.2.0 - - /got/11.8.6: - resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} - engines: {node: '>=10.19.0'} - dependencies: - '@sindresorhus/is': 4.6.0 - '@szmarczak/http-timer': 4.0.6 - '@types/cacheable-request': 6.0.3 - '@types/responselike': 1.0.0 - cacheable-lookup: 5.0.4 - cacheable-request: 7.0.2 - decompress-response: 6.0.0 - http2-wrapper: 1.0.3 - lowercase-keys: 2.0.0 - p-cancelable: 2.1.1 - responselike: 2.0.1 - - /graceful-fs/4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - /graceful-fs/4.2.4: - resolution: {integrity: sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==} - dev: false - - /grapheme-splitter/1.0.4: - resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} - - /graphql/15.8.0: - resolution: {integrity: sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==} - engines: {node: '>= 10.x'} - dev: true - - /gzip-size/6.0.0: - resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} - engines: {node: '>=10'} - dependencies: - duplexer: 0.1.2 - - /handle-thing/2.0.1: - resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} - dev: false - - /handlebars/4.7.7: - resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==} - engines: {node: '>=0.4.7'} - hasBin: true - dependencies: - minimist: 1.2.8 - neo-async: 2.6.2 - source-map: 0.6.1 - wordwrap: 1.0.0 - optionalDependencies: - uglify-js: 3.17.4 - dev: true - - /hard-rejection/2.1.0: - resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} - engines: {node: '>=6'} - dev: false - - /has-ansi/2.0.0: - resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} - engines: {node: '>=0.10.0'} - dependencies: - ansi-regex: 2.1.1 - dev: false - - /has-bigints/1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - - /has-flag/1.0.0: - resolution: {integrity: sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==} - engines: {node: '>=0.10.0'} - dev: false - - /has-flag/3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - - /has-flag/4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - /has-glob/1.0.0: - resolution: {integrity: sha512-D+8A457fBShSEI3tFCj65PAbT++5sKiFtdCdOam0gnfBgw9D277OERk+HM9qYJXmdVLZ/znez10SqHN0BBQ50g==} - engines: {node: '>=0.10.0'} - dependencies: - is-glob: 3.1.0 - dev: true - - /has-property-descriptors/1.0.0: - resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} - dependencies: - get-intrinsic: 1.2.0 - - /has-proto/1.0.1: - resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} - engines: {node: '>= 0.4'} - - /has-symbols/1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - - /has-tostringtag/1.0.0: - resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} - engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.0.3 - - /has-unicode/2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - dev: true - - /has-value/0.3.1: - resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} - engines: {node: '>=0.10.0'} - dependencies: - get-value: 2.0.6 - has-values: 0.1.4 - isobject: 2.1.0 - - /has-value/1.0.0: - resolution: {integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==} - engines: {node: '>=0.10.0'} - dependencies: - get-value: 2.0.6 - has-values: 1.0.0 - isobject: 3.0.1 - - /has-values/0.1.4: - resolution: {integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==} - engines: {node: '>=0.10.0'} - - /has-values/1.0.0: - resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==} - engines: {node: '>=0.10.0'} - dependencies: - is-number: 3.0.0 - kind-of: 4.0.0 - - /has-yarn/2.1.0: - resolution: {integrity: sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==} - engines: {node: '>=8'} - - /has/1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} - dependencies: - function-bind: 1.1.1 - - /hash-base/3.1.0: - resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} - engines: {node: '>=4'} - dependencies: - inherits: 2.0.4 - readable-stream: 3.6.2 - safe-buffer: 5.2.1 - - /hash.js/1.1.7: - resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} - dependencies: - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - - /hast-to-hyperscript/9.0.1: - resolution: {integrity: sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==} - dependencies: - '@types/unist': 2.0.6 - comma-separated-tokens: 1.0.8 - property-information: 5.6.0 - space-separated-tokens: 1.1.5 - style-to-object: 0.3.0 - unist-util-is: 4.1.0 - web-namespaces: 1.1.4 - dev: true - - /hast-util-from-parse5/6.0.1: - resolution: {integrity: sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==} - dependencies: - '@types/parse5': 5.0.3 - hastscript: 6.0.0 - property-information: 5.6.0 - vfile: 4.2.1 - vfile-location: 3.2.0 - web-namespaces: 1.1.4 - dev: true - - /hast-util-parse-selector/2.2.5: - resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} - dev: true - - /hast-util-raw/6.0.1: - resolution: {integrity: sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig==} - dependencies: - '@types/hast': 2.3.4 - hast-util-from-parse5: 6.0.1 - hast-util-to-parse5: 6.0.0 - html-void-elements: 1.0.5 - parse5: 6.0.1 - unist-util-position: 3.1.0 - vfile: 4.2.1 - web-namespaces: 1.1.4 - xtend: 4.0.2 - zwitch: 1.0.5 - dev: true - - /hast-util-to-parse5/6.0.0: - resolution: {integrity: sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ==} - dependencies: - hast-to-hyperscript: 9.0.1 - property-information: 5.6.0 - web-namespaces: 1.1.4 - xtend: 4.0.2 - zwitch: 1.0.5 - dev: true - - /hastscript/6.0.0: - resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} - dependencies: - '@types/hast': 2.3.4 - comma-separated-tokens: 1.0.8 - hast-util-parse-selector: 2.2.5 - property-information: 5.6.0 - space-separated-tokens: 1.1.5 - dev: true - - /he/1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - - /heap/0.2.7: - resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==} - dev: true - - /highlight-es/1.0.3: - resolution: {integrity: sha512-s/SIX6yp/5S1p8aC/NRDC1fwEb+myGIfp8/TzZz0rtAv8fzsdX7vGl3Q1TrXCsczFq8DI3CBFBCySPClfBSdbg==} - dependencies: - chalk: 2.4.2 - is-es2016-keyword: 1.0.0 - js-tokens: 3.0.2 - dev: false - - /highlight.js/10.7.3: - resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - dev: true - - /history/5.0.0: - resolution: {integrity: sha512-3NyRMKIiFSJmIPdq7FxkNMJkQ7ZEtVblOQ38VtKaA0zZMW1Eo6Q6W8oDKEflr1kNNTItSnk4JMCO1deeSgbLLg==} - dependencies: - '@babel/runtime': 7.21.0 - dev: true - - /hmac-drbg/1.0.1: - resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} - dependencies: - hash.js: 1.1.7 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - - /hoist-non-react-statics/3.3.2: - resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - dependencies: - react-is: 16.13.1 - - /homedir-polyfill/1.0.3: - resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} - engines: {node: '>=0.10.0'} - dependencies: - parse-passwd: 1.0.0 - dev: false - - /hosted-git-info/2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - - /hosted-git-info/4.1.0: - resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} - engines: {node: '>=10'} - dependencies: - lru-cache: 6.0.0 - dev: false - - /hpack.js/2.1.6: - resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} - dependencies: - inherits: 2.0.4 - obuf: 1.1.2 - readable-stream: 2.3.8 - wbuf: 1.7.3 - dev: false - - /html-encoding-sniffer/3.0.0: - resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} - engines: {node: '>=12'} - dependencies: - whatwg-encoding: 2.0.0 - - /html-entities/2.3.3: - resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} - - /html-escaper/2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - - /html-minifier-terser/5.1.1: - resolution: {integrity: sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==} - engines: {node: '>=6'} - hasBin: true - dependencies: - camel-case: 4.1.2 - clean-css: 4.2.4 - commander: 4.1.1 - he: 1.2.0 - param-case: 3.0.4 - relateurl: 0.2.7 - terser: 4.8.1 - - /html-minifier-terser/6.1.0: - resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} - engines: {node: '>=12'} - hasBin: true - dependencies: - camel-case: 4.1.2 - clean-css: 5.3.2 - commander: 8.3.0 - he: 1.2.0 - param-case: 3.0.4 - relateurl: 0.2.7 - terser: 5.16.8 - - /html-tags/3.2.0: - resolution: {integrity: sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==} - engines: {node: '>=8'} - dev: true - - /html-void-elements/1.0.5: - resolution: {integrity: sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==} - dev: true - - /html-webpack-plugin/4.5.2_webpack@4.44.2: - resolution: {integrity: sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A==} - engines: {node: '>=6.9'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - dependencies: - '@types/html-minifier-terser': 5.1.2 - '@types/tapable': 1.0.6 - '@types/webpack': 4.41.32 - html-minifier-terser: 5.1.1 - loader-utils: 1.4.2 - lodash: 4.17.21 - pretty-error: 2.1.2 - tapable: 1.1.3 - util.promisify: 1.0.0 - webpack: 4.44.2 - - /html-webpack-plugin/4.5.2_webpack@5.80.0: - resolution: {integrity: sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A==} - engines: {node: '>=6.9'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - dependencies: - '@types/html-minifier-terser': 5.1.2 - '@types/tapable': 1.0.6 - '@types/webpack': 4.41.32 - html-minifier-terser: 5.1.1 - loader-utils: 1.4.2 - lodash: 4.17.21 - pretty-error: 2.1.2 - tapable: 1.1.3 - util.promisify: 1.0.0 - webpack: 5.80.0 - dev: true - - /html-webpack-plugin/5.5.0_webpack@5.80.0: - resolution: {integrity: sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==} - engines: {node: '>=10.13.0'} - peerDependencies: - webpack: ^5.20.0 - dependencies: - '@types/html-minifier-terser': 6.1.0 - html-minifier-terser: 6.1.0 - lodash: 4.17.21 - pretty-error: 4.0.0 - tapable: 2.2.1 - webpack: 5.80.0 - - /htmlparser2/6.1.0: - resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} - dependencies: - domelementtype: 2.3.0 - domhandler: 4.3.1 - domutils: 2.8.0 - entities: 2.2.0 - - /http-cache-semantics/4.1.1: - resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - - /http-deceiver/1.2.7: - resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} - dev: false - - /http-errors/1.6.3: - resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} - engines: {node: '>= 0.6'} - dependencies: - depd: 1.1.2 - inherits: 2.0.3 - setprototypeof: 1.1.0 - statuses: 1.5.0 - dev: false - - /http-errors/2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - - /http-parser-js/0.5.8: - resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} - dev: false - - /http-proxy-agent/4.0.1: - resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} - engines: {node: '>= 6'} - dependencies: - '@tootallnate/once': 1.1.2 - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: true - - /http-proxy-agent/5.0.0: - resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} - engines: {node: '>= 6'} - dependencies: - '@tootallnate/once': 2.0.0 - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - - /http-proxy-middleware/2.0.6: - resolution: {integrity: sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==} - engines: {node: '>=12.0.0'} - peerDependenciesMeta: - '@types/express': - optional: true - dependencies: - '@types/express': 4.17.13 - '@types/http-proxy': 1.17.10 - http-proxy: 1.18.1 - is-glob: 4.0.3 - is-plain-obj: 3.0.0 - micromatch: 4.0.5 - transitivePeerDependencies: - - debug - dev: false - - /http-proxy/1.18.1: - resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} - engines: {node: '>=8.0.0'} - dependencies: - eventemitter3: 4.0.7 - follow-redirects: 1.15.2 - requires-port: 1.0.0 - transitivePeerDependencies: - - debug - - /http2-wrapper/1.0.3: - resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} - engines: {node: '>=10.19.0'} - dependencies: - quick-lru: 5.1.1 - resolve-alpn: 1.2.1 - - /https-browserify/1.0.0: - resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} - - /https-proxy-agent/4.0.0: - resolution: {integrity: sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==} - engines: {node: '>= 6.0.0'} - dependencies: - agent-base: 5.1.1 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: true - - /https-proxy-agent/5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - - /human-signals/2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - - /humanize-ms/1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - dependencies: - ms: 2.1.3 - dev: true - - /iconv-lite/0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - dependencies: - safer-buffer: 2.1.2 - - /iconv-lite/0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - dependencies: - safer-buffer: 2.1.2 - - /icss-replace-symbols/1.1.0: - resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==} - dev: false - - /icss-utils/4.1.1: - resolution: {integrity: sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==} - engines: {node: '>= 6'} - dependencies: - postcss: 7.0.39 - dev: true - - /icss-utils/5.1.0_postcss@8.4.21: - resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - dependencies: - postcss: 8.4.21 - - /ieee754/1.1.13: - resolution: {integrity: sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==} - - /ieee754/1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - /iferr/0.1.5: - resolution: {integrity: sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==} - - /ignore-walk/3.0.4: - resolution: {integrity: sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==} - dependencies: - minimatch: 3.1.2 - dev: false - - /ignore/4.0.6: - resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} - engines: {node: '>= 4'} - dev: true - - /ignore/5.1.9: - resolution: {integrity: sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==} - engines: {node: '>= 4'} - dev: false - - /ignore/5.2.4: - resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} - engines: {node: '>= 4'} - - /immediate/3.0.6: - resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - dev: false - - /immer/9.0.21: - resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} - - /immutable/4.3.0: - resolution: {integrity: sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==} - dev: false - - /import-fresh/3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - /import-lazy/2.1.0: - resolution: {integrity: sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==} - engines: {node: '>=4'} - - /import-lazy/4.0.0: - resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} - engines: {node: '>=8'} - - /import-local/2.0.0: - resolution: {integrity: sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==} - engines: {node: '>=6'} - hasBin: true - dependencies: - pkg-dir: 3.0.0 - resolve-cwd: 2.0.0 - dev: false - - /import-local/3.1.0: - resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} - engines: {node: '>=8'} - hasBin: true - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - dev: true - - /imurmurhash/0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - /indent-string/4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - - /individual/3.0.0: - resolution: {integrity: sha512-rUY5vtT748NMRbEMrTNiFfy29BgGZwGXUi2NFUVMWQrogSLzlJvQV9eeMWi+g1aVaQ53tpyLAQtd5x/JH0Nh1g==} - dev: true - - /infer-owner/1.0.4: - resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} - - /inflight/1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - /inherits/2.0.1: - resolution: {integrity: sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==} - - /inherits/2.0.3: - resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} - - /inherits/2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - /ini/1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - - /ini/2.0.0: - resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} - engines: {node: '>=10'} - - /inline-style-parser/0.1.1: - resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} - dev: true - - /inpath/1.0.2: - resolution: {integrity: sha512-DTt55ovuYFC62a8oJxRjV2MmTPUdxN43Gd8I2ZgawxbAha6PvJkDQy/RbZGFCJF5IXrpp4PAYtW1w3aV7jXkew==} - dev: false - - /inquirer/7.3.3: - resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==} - engines: {node: '>=8.0.0'} - dependencies: - ansi-escapes: 4.3.2 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-width: 3.0.0 - external-editor: 3.1.0 - figures: 3.0.0 - lodash: 4.17.21 - mute-stream: 0.0.8 - run-async: 2.4.1 - rxjs: 6.6.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - through: 2.3.8 - dev: false - - /internal-slot/1.0.5: - resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.0 - has: 1.0.3 - side-channel: 1.0.4 - - /interpret/1.4.0: - resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} - engines: {node: '>= 0.10'} - - /interpret/2.2.0: - resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==} - engines: {node: '>= 0.10'} - dev: true - - /invariant/2.2.4: - resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - dependencies: - loose-envify: 1.4.0 - - /ip/1.1.8: - resolution: {integrity: sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==} - dev: true - - /ip/2.0.0: - resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} - dev: true - - /ipaddr.js/1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - - /ipaddr.js/2.0.1: - resolution: {integrity: sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==} - engines: {node: '>= 10'} - dev: false - - /is-absolute-url/3.0.3: - resolution: {integrity: sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==} - engines: {node: '>=8'} - dev: true - - /is-accessor-descriptor/0.1.6: - resolution: {integrity: sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==} - engines: {node: '>=0.10.0'} - dependencies: - kind-of: 3.2.2 - - /is-accessor-descriptor/1.0.0: - resolution: {integrity: sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==} - engines: {node: '>=0.10.0'} - dependencies: - kind-of: 6.0.3 - - /is-alphabetical/1.0.4: - resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} - dev: true - - /is-alphanumerical/1.0.4: - resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} - dependencies: - is-alphabetical: 1.0.4 - is-decimal: 1.0.4 - dev: true - - /is-arguments/1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - dev: true - - /is-array-buffer/3.0.2: - resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 - is-typed-array: 1.1.10 - - /is-arrayish/0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - /is-bigint/1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} - dependencies: - has-bigints: 1.0.2 - - /is-binary-path/1.0.1: - resolution: {integrity: sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==} - engines: {node: '>=0.10.0'} - dependencies: - binary-extensions: 1.13.1 - optional: true - - /is-binary-path/2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - dependencies: - binary-extensions: 2.2.0 - - /is-boolean-object/1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - - /is-buffer/1.1.6: - resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} - - /is-buffer/2.0.5: - resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} - engines: {node: '>=4'} - dev: true - - /is-callable/1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - /is-ci/2.0.0: - resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==} - hasBin: true - dependencies: - ci-info: 2.0.0 - - /is-core-module/2.11.0: - resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} - dependencies: - has: 1.0.3 - - /is-data-descriptor/0.1.4: - resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==} - engines: {node: '>=0.10.0'} - dependencies: - kind-of: 3.2.2 - - /is-data-descriptor/1.0.0: - resolution: {integrity: sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==} - engines: {node: '>=0.10.0'} - dependencies: - kind-of: 6.0.3 - - /is-date-object/1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - - /is-decimal/1.0.4: - resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} - dev: true - - /is-descriptor/0.1.6: - resolution: {integrity: sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==} - engines: {node: '>=0.10.0'} - dependencies: - is-accessor-descriptor: 0.1.6 - is-data-descriptor: 0.1.4 - kind-of: 5.1.0 - - /is-descriptor/1.0.2: - resolution: {integrity: sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==} - engines: {node: '>=0.10.0'} - dependencies: - is-accessor-descriptor: 1.0.0 - is-data-descriptor: 1.0.0 - kind-of: 6.0.3 - - /is-docker/2.2.1: - resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} - engines: {node: '>=8'} - hasBin: true - - /is-dom/1.1.0: - resolution: {integrity: sha512-u82f6mvhYxRPKpw8V1N0W8ce1xXwOrQtgGcxl6UCL5zBmZu3is/18K0rR7uFCnMDuAsS/3W54mGL4vsaFUQlEQ==} - dependencies: - is-object: 1.0.2 - is-window: 1.0.2 - dev: true - - /is-es2016-keyword/1.0.0: - resolution: {integrity: sha512-JtZWPUwjdbQ1LIo9OSZ8MdkWEve198ors27vH+RzUUvZXXZkzXCxFnlUhzWYxy5IexQSRiXVw9j2q/tHMmkVYQ==} - dev: false - - /is-extendable/0.1.1: - resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} - engines: {node: '>=0.10.0'} - - /is-extendable/1.0.1: - resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==} - engines: {node: '>=0.10.0'} - dependencies: - is-plain-object: 2.0.4 - - /is-extglob/2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - /is-fullwidth-code-point/1.0.0: - resolution: {integrity: sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==} - engines: {node: '>=0.10.0'} - dependencies: - number-is-nan: 1.0.1 - dev: true - - /is-fullwidth-code-point/2.0.0: - resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} - engines: {node: '>=4'} - dev: false - - /is-fullwidth-code-point/3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - /is-function/1.0.2: - resolution: {integrity: sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==} - dev: true - - /is-generator-fn/2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - - /is-generator-function/1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - dev: true - - /is-glob/3.1.0: - resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==} - engines: {node: '>=0.10.0'} - dependencies: - is-extglob: 2.1.1 - - /is-glob/4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - dependencies: - is-extglob: 2.1.1 - - /is-hexadecimal/1.0.4: - resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} - dev: true - - /is-installed-globally/0.4.0: - resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} - engines: {node: '>=10'} - dependencies: - global-dirs: 3.0.1 - is-path-inside: 3.0.3 - - /is-interactive/1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - dev: false - - /is-lambda/1.0.1: - resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - dev: true - - /is-map/2.0.2: - resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} - dev: true - - /is-negative-zero/2.0.2: - resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} - engines: {node: '>= 0.4'} - - /is-npm/5.0.0: - resolution: {integrity: sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==} - engines: {node: '>=10'} - - /is-number-object/1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - - /is-number/3.0.0: - resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} - engines: {node: '>=0.10.0'} - dependencies: - kind-of: 3.2.2 - - /is-number/7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - /is-obj/2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - - /is-object/1.0.2: - resolution: {integrity: sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==} - dev: true - - /is-path-inside/3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - - /is-plain-obj/1.1.0: - resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} - engines: {node: '>=0.10.0'} - dev: false - - /is-plain-obj/2.1.0: - resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} - engines: {node: '>=8'} - - /is-plain-obj/3.0.0: - resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} - engines: {node: '>=10'} - dev: false - - /is-plain-object/2.0.4: - resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} - engines: {node: '>=0.10.0'} - dependencies: - isobject: 3.0.1 - - /is-plain-object/5.0.0: - resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} - engines: {node: '>=0.10.0'} - dev: true - - /is-potential-custom-element-name/1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - - /is-promise/2.2.2: - resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} - dev: true - - /is-regex/1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - - /is-set/2.0.2: - resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} - dev: true - - /is-shared-array-buffer/1.0.2: - resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} - dependencies: - call-bind: 1.0.2 - - /is-stream/1.1.0: - resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} - engines: {node: '>=0.10.0'} - dev: true - - /is-stream/2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - /is-string/1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - - /is-subdir/1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} - dependencies: - better-path-resolve: 1.0.0 - dev: false - - /is-symbol/1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.0.3 - - /is-typed-array/1.1.10: - resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} - engines: {node: '>= 0.4'} - dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.0 - - /is-typedarray/1.0.0: - resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - - /is-unicode-supported/0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - dev: false - - /is-weakref/1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - dependencies: - call-bind: 1.0.2 - - /is-whitespace-character/1.0.4: - resolution: {integrity: sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==} - dev: true - - /is-window/1.0.2: - resolution: {integrity: sha512-uj00kdXyZb9t9RcAUAwMZAnkBUwdYGhYlt7djMXhfyhUCzwNba50tIiBKR7q0l7tdoBtFVw/3JmLY6fI3rmZmg==} - dev: true - - /is-windows/1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - - /is-word-character/1.0.4: - resolution: {integrity: sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==} - dev: true - - /is-wsl/1.1.0: - resolution: {integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==} - engines: {node: '>=4'} - - /is-wsl/2.2.0: - resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} - engines: {node: '>=8'} - dependencies: - is-docker: 2.2.1 - - /is-yarn-global/0.3.0: - resolution: {integrity: sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==} - - /isarray/0.0.1: - resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} - dev: true - - /isarray/1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - - /isarray/2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - dev: true - - /isexe/2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - /isobject/2.1.0: - resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} - engines: {node: '>=0.10.0'} - dependencies: - isarray: 1.0.0 - - /isobject/3.0.1: - resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} - engines: {node: '>=0.10.0'} - - /isobject/4.0.0: - resolution: {integrity: sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==} - engines: {node: '>=0.10.0'} - dev: true - - /istanbul-lib-coverage/3.2.0: - resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} - engines: {node: '>=8'} - - /istanbul-lib-instrument/5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - dependencies: - '@babel/core': 7.20.12 - '@babel/parser': 7.21.3 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - - /istanbul-lib-report/3.0.0: - resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} - engines: {node: '>=8'} - dependencies: - istanbul-lib-coverage: 3.2.0 - make-dir: 3.1.0 - supports-color: 7.2.0 - - /istanbul-lib-source-maps/4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} - engines: {node: '>=10'} - dependencies: - debug: 4.3.4 - istanbul-lib-coverage: 3.2.0 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color - - /istanbul-reports/3.1.5: - resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} - engines: {node: '>=8'} - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.0 - - /iterate-iterator/1.0.2: - resolution: {integrity: sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==} - dev: true - - /iterate-value/1.0.2: - resolution: {integrity: sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==} - dependencies: - es-get-iterator: 1.1.3 - iterate-iterator: 1.0.2 - dev: true - - /jest-changed-files/29.5.0: - resolution: {integrity: sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - execa: 5.1.1 - p-limit: 3.1.0 - - /jest-circus/29.5.0: - resolution: {integrity: sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.5.0 - '@jest/expect': 29.5.0 - '@jest/test-result': 29.5.0_@types+node@14.18.36 - '@jest/types': 29.5.0 - '@types/node': 14.18.36 - chalk: 4.1.2 - co: 4.6.0 - dedent: 0.7.0 - is-generator-fn: 2.1.0 - jest-each: 29.5.0 - jest-matcher-utils: 29.5.0 - jest-message-util: 29.5.0 - jest-runtime: 29.5.0 - jest-snapshot: 29.5.0 - jest-util: 29.5.0 - p-limit: 3.1.0 - pretty-format: 29.5.0 - pure-rand: 6.0.1 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - supports-color - - /jest-cli/29.5.0_@types+node@14.18.36: - resolution: {integrity: sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.5.0 - '@jest/test-result': 29.5.0_@types+node@14.18.36 - '@jest/types': 29.5.0 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - import-local: 3.1.0 - jest-config: 29.5.0_@types+node@14.18.36 - jest-util: 29.5.0 - jest-validate: 29.5.0 - prompts: 2.4.2 - yargs: 17.7.1 - transitivePeerDependencies: - - '@types/node' - - supports-color - - ts-node - dev: true - - /jest-config/29.5.0_@types+node@14.18.36: - resolution: {integrity: sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - dependencies: - '@babel/core': 7.20.12 - '@jest/test-sequencer': 29.5.0_@types+node@14.18.36 - '@jest/types': 29.5.0 - '@types/node': 14.18.36 - babel-jest: 29.5.0_@babel+core@7.20.12 - chalk: 4.1.2 - ci-info: 3.8.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.5.0 - jest-environment-node: 29.5.0 - jest-get-type: 29.4.3 - jest-regex-util: 29.4.3 - jest-resolve: 29.5.0 - jest-runner: 29.5.0 - jest-util: 29.5.0 - jest-validate: 29.5.0 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.5.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - /jest-diff/27.5.1: - resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 27.5.1 - jest-get-type: 27.5.1 - pretty-format: 27.5.1 - dev: true - - /jest-diff/29.5.0: - resolution: {integrity: sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 29.4.3 - jest-get-type: 29.4.3 - pretty-format: 29.5.0 - - /jest-docblock/29.4.3: - resolution: {integrity: sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - detect-newline: 3.1.0 - - /jest-each/29.5.0: - resolution: {integrity: sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - chalk: 4.1.2 - jest-get-type: 29.4.3 - jest-util: 29.5.0 - pretty-format: 29.5.0 - - /jest-environment-jsdom/29.5.0: - resolution: {integrity: sha512-/KG8yEK4aN8ak56yFVdqFDzKNHgF4BAymCx2LbPNPsUshUlfAl0eX402Xm1pt+eoG9SLZEUVifqXtX8SK74KCw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - canvas: ^2.5.0 - peerDependenciesMeta: - canvas: - optional: true - dependencies: - '@jest/environment': 29.5.0 - '@jest/fake-timers': 29.5.0 - '@jest/types': 29.5.0 - '@types/jsdom': 20.0.1 - '@types/node': 14.18.36 - jest-mock: 29.5.0 - jest-util: 29.5.0 - jsdom: 20.0.3 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - /jest-environment-node/29.5.0: - resolution: {integrity: sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.5.0 - '@jest/fake-timers': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 14.18.36 - jest-mock: 29.5.0 - jest-util: 29.5.0 - - /jest-get-type/27.5.1: - resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dev: true - - /jest-get-type/29.4.3: - resolution: {integrity: sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - /jest-haste-map/26.6.2: - resolution: {integrity: sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==} - engines: {node: '>= 10.14.2'} - dependencies: - '@jest/types': 26.6.2 - '@types/graceful-fs': 4.1.6 - '@types/node': 14.18.36 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 26.0.0 - jest-serializer: 26.6.2 - jest-util: 26.6.2 - jest-worker: 26.6.2 - micromatch: 4.0.5 - sane: 4.1.0 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.2 - dev: true - - /jest-haste-map/29.5.0: - resolution: {integrity: sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@types/graceful-fs': 4.1.6 - '@types/node': 14.18.36 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.4.3 - jest-util: 29.5.0 - jest-worker: 29.5.0 - micromatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.2 - - /jest-leak-detector/29.5.0: - resolution: {integrity: sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.4.3 - pretty-format: 29.5.0 - - /jest-matcher-utils/27.5.1: - resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 27.5.1 - jest-get-type: 27.5.1 - pretty-format: 27.5.1 - dev: true - - /jest-matcher-utils/29.5.0: - resolution: {integrity: sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 29.5.0 - jest-get-type: 29.4.3 - pretty-format: 29.5.0 - - /jest-message-util/29.5.0: - resolution: {integrity: sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/code-frame': 7.18.6 - '@jest/types': 29.5.0 - '@types/stack-utils': 2.0.1 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.5 - pretty-format: 29.5.0 - slash: 3.0.0 - stack-utils: 2.0.6 - - /jest-mock/29.5.0: - resolution: {integrity: sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@types/node': 14.18.36 - jest-util: 29.5.0 - - /jest-pnp-resolver/1.2.3_jest-resolve@29.5.0: - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - dependencies: - jest-resolve: 29.5.0 - - /jest-regex-util/26.0.0: - resolution: {integrity: sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==} - engines: {node: '>= 10.14.2'} - dev: true - - /jest-regex-util/29.4.3: - resolution: {integrity: sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - /jest-resolve-dependencies/29.5.0: - resolution: {integrity: sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-regex-util: 29.4.3 - jest-snapshot: 29.5.0 - transitivePeerDependencies: - - supports-color - - /jest-resolve/29.5.0: - resolution: {integrity: sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.5.0 - jest-pnp-resolver: 1.2.3_jest-resolve@29.5.0 - jest-util: 29.5.0 - jest-validate: 29.5.0 - resolve: 1.22.1 - resolve.exports: 2.0.2 - slash: 3.0.0 - - /jest-runner/29.5.0: - resolution: {integrity: sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.5.0 - '@jest/environment': 29.5.0 - '@jest/test-result': 29.5.0_@types+node@14.18.36 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 14.18.36 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.11 - jest-docblock: 29.4.3 - jest-environment-node: 29.5.0 - jest-haste-map: 29.5.0 - jest-leak-detector: 29.5.0 - jest-message-util: 29.5.0 - jest-resolve: 29.5.0 - jest-runtime: 29.5.0 - jest-util: 29.5.0 - jest-watcher: 29.5.0 - jest-worker: 29.5.0 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - - /jest-runtime/29.5.0: - resolution: {integrity: sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.5.0 - '@jest/fake-timers': 29.5.0 - '@jest/globals': 29.5.0 - '@jest/source-map': 29.4.3 - '@jest/test-result': 29.5.0_@types+node@14.18.36 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 14.18.36 - chalk: 4.1.2 - cjs-module-lexer: 1.2.2 - collect-v8-coverage: 1.0.1_@types+node@14.18.36 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 29.5.0 - jest-message-util: 29.5.0 - jest-mock: 29.5.0 - jest-regex-util: 29.4.3 - jest-resolve: 29.5.0 - jest-snapshot: 29.5.0 - jest-util: 29.5.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - - /jest-serializer/26.6.2: - resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==} - engines: {node: '>= 10.14.2'} - dependencies: - '@types/node': 14.18.36 - graceful-fs: 4.2.11 - dev: true - - /jest-snapshot/29.5.0: - resolution: {integrity: sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.20.12 - '@babel/generator': 7.21.3 - '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.20.12 - '@babel/traverse': 7.21.3 - '@babel/types': 7.21.3 - '@jest/expect-utils': 29.5.0 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@types/babel__traverse': 7.18.3 - '@types/prettier': 2.7.2 - babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.12 - chalk: 4.1.2 - expect: 29.5.0 - graceful-fs: 4.2.11 - jest-diff: 29.5.0 - jest-get-type: 29.4.3 - jest-matcher-utils: 29.5.0 - jest-message-util: 29.5.0 - jest-util: 29.5.0 - natural-compare: 1.4.0 - pretty-format: 29.5.0 - semver: 7.3.8 - transitivePeerDependencies: - - supports-color - - /jest-util/26.6.2: - resolution: {integrity: sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==} - engines: {node: '>= 10.14.2'} - dependencies: - '@jest/types': 26.6.2 - '@types/node': 14.18.36 - chalk: 4.1.2 - graceful-fs: 4.2.11 - is-ci: 2.0.0 - micromatch: 4.0.5 - dev: true - - /jest-util/29.5.0: - resolution: {integrity: sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@types/node': 14.18.36 - chalk: 4.1.2 - ci-info: 3.8.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 - - /jest-validate/29.5.0: - resolution: {integrity: sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.4.3 - leven: 3.1.0 - pretty-format: 29.5.0 - - /jest-watch-select-projects/2.0.0: - resolution: {integrity: sha512-j00nW4dXc2NiCW6znXgFLF9g8PJ0zP25cpQ1xRro/HU2GBfZQFZD0SoXnAlaoKkIY4MlfTMkKGbNXFpvCdjl1w==} - dependencies: - ansi-escapes: 4.3.2 - chalk: 3.0.0 - prompts: 2.4.2 - dev: true - - /jest-watcher/29.5.0: - resolution: {integrity: sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.5.0_@types+node@14.18.36 - '@jest/types': 29.5.0 - '@types/node': 14.18.36 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.5.0 - string-length: 4.0.2 - - /jest-worker/26.6.2: - resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} - engines: {node: '>= 10.13.0'} - dependencies: - '@types/node': 14.18.36 - merge-stream: 2.0.0 - supports-color: 7.2.0 - dev: true - - /jest-worker/27.5.1: - resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} - engines: {node: '>= 10.13.0'} - dependencies: - '@types/node': 14.18.36 - merge-stream: 2.0.0 - supports-color: 8.1.1 - - /jest-worker/29.5.0: - resolution: {integrity: sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@types/node': 14.18.36 - jest-util: 29.5.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 - - /jest/29.3.1_@types+node@14.18.36: - resolution: {integrity: sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.5.0 - '@jest/types': 29.5.0 - import-local: 3.1.0 - jest-cli: 29.5.0_@types+node@14.18.36 - transitivePeerDependencies: - - '@types/node' - - supports-color - - ts-node - dev: true - - /jju/1.4.0: - resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} - - /jmespath/0.16.0: - resolution: {integrity: sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==} - engines: {node: '>= 0.6.0'} - dev: true - - /js-sdsl/4.4.0: - resolution: {integrity: sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==} - dev: true - - /js-string-escape/1.0.1: - resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} - engines: {node: '>= 0.8'} - dev: true - - /js-tokens/3.0.2: - resolution: {integrity: sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==} - dev: false - - /js-tokens/4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - /js-yaml/3.13.1: - resolution: {integrity: sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==} - hasBin: true - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - - /js-yaml/3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - dev: false - - /js-yaml/4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - dependencies: - argparse: 2.0.1 - - /jscodeshift/0.13.1_@babel+preset-env@7.20.2: - resolution: {integrity: sha512-lGyiEbGOvmMRKgWk4vf+lUrCWO/8YR8sUR3FKF1Cq5fovjZDlIcw3Hu5ppLHAnEXshVffvaM0eyuY/AbOeYpnQ==} - hasBin: true - peerDependencies: - '@babel/preset-env': ^7.1.6 - dependencies: - '@babel/core': 7.20.12 - '@babel/parser': 7.21.3 - '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-proposal-optional-chaining': 7.21.0_@babel+core@7.20.12 - '@babel/plugin-transform-modules-commonjs': 7.21.2_@babel+core@7.20.12 - '@babel/preset-env': 7.20.2_@babel+core@7.20.12 - '@babel/preset-flow': 7.18.6_@babel+core@7.20.12 - '@babel/preset-typescript': 7.21.0_@babel+core@7.20.12 - '@babel/register': 7.21.0_@babel+core@7.20.12 - babel-core: 7.0.0-bridge.0_@babel+core@7.20.12 - chalk: 4.1.2 - flow-parser: 0.202.1 - graceful-fs: 4.2.11 - micromatch: 3.1.10 - neo-async: 2.6.2 - node-dir: 0.1.17 - recast: 0.20.5 - temp: 0.8.4 - write-file-atomic: 2.4.3 - transitivePeerDependencies: - - supports-color - dev: true - - /jsdom/20.0.3: - resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} - engines: {node: '>=14'} - peerDependencies: - canvas: ^2.5.0 - peerDependenciesMeta: - canvas: - optional: true - dependencies: - abab: 2.0.6 - acorn: 8.8.2 - acorn-globals: 7.0.1 - cssom: 0.5.0 - cssstyle: 2.3.0 - data-urls: 3.0.2 - decimal.js: 10.4.3 - domexception: 4.0.0 - escodegen: 2.0.0 - form-data: 4.0.0 - html-encoding-sniffer: 3.0.0 - http-proxy-agent: 5.0.0 - https-proxy-agent: 5.0.1 - is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.2 - parse5: 7.1.2 - saxes: 6.0.0 - symbol-tree: 3.2.4 - tough-cookie: 4.1.2 - w3c-xmlserializer: 4.0.0 - webidl-conversions: 7.0.0 - whatwg-encoding: 2.0.0 - whatwg-mimetype: 3.0.0 - whatwg-url: 11.0.0 - ws: 8.13.0 - xml-name-validator: 4.0.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - /jsesc/0.5.0: - resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} - hasBin: true - dev: true - - /jsesc/2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true - - /json-buffer/3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - /json-diff/0.7.4: - resolution: {integrity: sha512-FJ2P+ShDbzu9epF+kCKgoSUhPIUW7Ta7A4XlIT0L5LzgaR/z1TBF1mm0XhRGj8RlA3Xm0j+c/FsWOHDtuoYejA==} - hasBin: true - dependencies: - cli-color: 2.0.3 - difflib: 0.2.4 - dreamopt: 0.8.0 - dev: true - - /json-parse-better-errors/1.0.2: - resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} - - /json-parse-even-better-errors/2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - - /json-schema-traverse/0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - /json-schema-traverse/1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - /json-schema-typed/7.0.3: - resolution: {integrity: sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==} - dev: true - - /json-stable-stringify-without-jsonify/1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - - /json-stringify-safe/5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - dev: true - - /json5/1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true - dependencies: - minimist: 1.2.8 - - /json5/2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - /jsonfile/4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - optionalDependencies: - graceful-fs: 4.2.11 - - /jsonfile/6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - dependencies: - universalify: 2.0.0 - optionalDependencies: - graceful-fs: 4.2.11 - dev: true - - /jsonpath-plus/4.0.0: - resolution: {integrity: sha512-e0Jtg4KAzDJKKwzbLaUtinCn0RZseWBVRTRGihSpvFlM3wTR7ExSp+PTdeTsDrLNJUe7L7JYJe8mblHX5SCT6A==} - engines: {node: '>=10.0'} - - /jsonschema/1.4.1: - resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==} - dev: true - - /jsonwebtoken/9.0.0: - resolution: {integrity: sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==} - engines: {node: '>=12', npm: '>=6'} - dependencies: - jws: 3.2.2 - lodash: 4.17.21 - ms: 2.1.3 - semver: 7.3.8 - dev: false - - /jsx-ast-utils/3.3.3: - resolution: {integrity: sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==} - engines: {node: '>=4.0'} - dependencies: - array-includes: 3.1.6 - object.assign: 4.1.4 - - /jszip/2.7.0: - resolution: {integrity: sha512-JIsRKRVC3gTRo2vM4Wy9WBC3TRcfnIZU8k65Phi3izkvPH975FowRYtKGT6PxevA0XnJ/yO8b0QwV0ydVyQwfw==} - dependencies: - pako: 1.0.11 - dev: true - - /jszip/3.8.0: - resolution: {integrity: sha512-cnpQrXvFSLdsR9KR5/x7zdf6c3m8IhZfZzSblFEHSqBaVwD2nvJ4CuCKLyvKvwBgZm08CgfSoiTBQLm5WW9hGw==} - dependencies: - lie: 3.3.0 - pako: 1.0.11 - readable-stream: 2.3.8 - set-immediate-shim: 1.0.1 - dev: false - - /junk/3.1.0: - resolution: {integrity: sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==} - engines: {node: '>=8'} - dev: true - - /jwa/1.4.1: - resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - dev: false - - /jwa/2.0.0: - resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==} - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - dev: false - - /jws/3.2.2: - resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} - dependencies: - jwa: 1.4.1 - safe-buffer: 5.2.1 - dev: false - - /jws/4.0.0: - resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} - dependencies: - jwa: 2.0.0 - safe-buffer: 5.2.1 - dev: false - - /keyv/4.5.2: - resolution: {integrity: sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==} - dependencies: - json-buffer: 3.0.1 - - /kind-of/3.2.2: - resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} - engines: {node: '>=0.10.0'} - dependencies: - is-buffer: 1.1.6 - - /kind-of/4.0.0: - resolution: {integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==} - engines: {node: '>=0.10.0'} - dependencies: - is-buffer: 1.1.6 - - /kind-of/5.1.0: - resolution: {integrity: sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==} - engines: {node: '>=0.10.0'} - - /kind-of/6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - - /kleur/3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - dev: true - - /klona/2.0.6: - resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} - engines: {node: '>= 8'} - - /latest-version/5.1.0: - resolution: {integrity: sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==} - engines: {node: '>=8'} - dependencies: - package-json: 7.0.0 - - /lazy-universal-dotenv/3.0.1: - resolution: {integrity: sha512-prXSYk799h3GY3iOWnC6ZigYzMPjxN2svgjJ9shk7oMadSNX3wXy0B6F32PMJv7qtMnrIbUxoEHzbutvxR2LBQ==} - engines: {node: '>=6.0.0', npm: '>=6.0.0', yarn: '>=1.0.0'} - dependencies: - '@babel/runtime': 7.21.0 - app-root-dir: 1.0.2 - core-js: 3.29.1 - dotenv: 8.6.0 - dotenv-expand: 5.1.0 - dev: true - - /lazystream/1.0.1: - resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} - engines: {node: '>= 0.6.3'} - dependencies: - readable-stream: 2.3.8 - dev: true - - /leven/3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - - /levn/0.3.0: - resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.1.2 - type-check: 0.3.2 - - /levn/0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - /lie/3.3.0: - resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} - dependencies: - immediate: 3.0.6 - dev: false - - /light-my-request/4.12.0: - resolution: {integrity: sha512-0y+9VIfJEsPVzK5ArSIJ8Dkxp8QMP7/aCuxCUtG/tr9a2NoOf/snATE/OUc05XUplJCEnRh6gTkH7xh9POt1DQ==} - dependencies: - ajv: 8.12.0 - cookie: 0.5.0 - process-warning: 1.0.0 - set-cookie-parser: 2.6.0 - dev: false - - /lilconfig/2.1.0: - resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} - engines: {node: '>=10'} - dev: false - - /lines-and-columns/1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - /load-json-file/6.2.0: - resolution: {integrity: sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==} - engines: {node: '>=8'} - dependencies: - graceful-fs: 4.2.11 - parse-json: 5.2.0 - strip-bom: 4.0.0 - type-fest: 0.6.0 - dev: false - - /load-yaml-file/0.2.0: - resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} - engines: {node: '>=6'} - dependencies: - graceful-fs: 4.2.11 - js-yaml: 3.13.1 - pify: 4.0.1 - strip-bom: 3.0.0 - dev: false - - /loader-runner/2.4.0: - resolution: {integrity: sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==} - engines: {node: '>=4.3.0 <5.0.0 || >=5.10'} - - /loader-runner/4.3.0: - resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} - engines: {node: '>=6.11.5'} - - /loader-utils/1.4.2: - resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==} - engines: {node: '>=4.0.0'} - dependencies: - big.js: 5.2.2 - emojis-list: 3.0.0 - json5: 1.0.2 - - /loader-utils/2.0.0: - resolution: {integrity: sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==} - engines: {node: '>=8.9.0'} - dependencies: - big.js: 5.2.2 - emojis-list: 3.0.0 - json5: 2.2.3 - dev: true - - /loader-utils/2.0.4: - resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} - engines: {node: '>=8.9.0'} - dependencies: - big.js: 5.2.2 - emojis-list: 3.0.0 - json5: 2.2.3 - - /locate-path/3.0.0: - resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} - engines: {node: '>=6'} - dependencies: - p-locate: 3.0.0 - path-exists: 3.0.0 - - /locate-path/5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - dependencies: - p-locate: 4.1.0 - - /locate-path/6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - dependencies: - p-locate: 5.0.0 - - /lodash.camelcase/4.3.0: - resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - dev: false - - /lodash.debounce/4.0.8: - resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - dev: true - - /lodash.defaults/4.2.0: - resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} - dev: true - - /lodash.difference/4.5.0: - resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} - dev: true - - /lodash.flatten/4.4.0: - resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} - dev: true - - /lodash.get/4.4.2: - resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} - - /lodash.isequal/4.5.0: - resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - - /lodash.isplainobject/4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - dev: true - - /lodash.memoize/4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - dev: false - - /lodash.merge/4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - /lodash.truncate/4.4.2: - resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} - dev: true - - /lodash.union/4.6.0: - resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} - dev: true - - /lodash.uniq/4.5.0: - resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - - /lodash/4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - /log-symbols/4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - dev: false - - /log4js/6.9.1: - resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==} - engines: {node: '>=8.0'} - dependencies: - date-format: 4.0.14 - debug: 4.3.4 - flatted: 3.2.7 - rfdc: 1.3.0 - streamroller: 3.1.5 - transitivePeerDependencies: - - supports-color - dev: true - - /long/4.0.0: - resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} - dev: false - - /loose-envify/1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - dependencies: - js-tokens: 4.0.0 - - /lower-case/2.0.2: - resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} - dependencies: - tslib: 2.3.1 - - /lowercase-keys/2.0.0: - resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} - engines: {node: '>=8'} - - /lowlight/1.20.0: - resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} - dependencies: - fault: 1.0.4 - highlight.js: 10.7.3 - dev: true - - /lru-cache/5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - dependencies: - yallist: 3.1.1 - - /lru-cache/6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - dependencies: - yallist: 4.0.0 - - /lru-queue/0.1.0: - resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} - dependencies: - es5-ext: 0.10.62 - dev: true - - /magic-string/0.25.9: - resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} - dependencies: - sourcemap-codec: 1.4.8 - dev: false - - /make-dir/2.1.0: - resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} - engines: {node: '>=6'} - dependencies: - pify: 4.0.1 - semver: 5.7.1 - - /make-dir/3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} - engines: {node: '>=8'} - dependencies: - semver: 6.3.0 - - /make-fetch-happen/8.0.14: - resolution: {integrity: sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ==} - engines: {node: '>= 10'} - dependencies: - agentkeepalive: 4.3.0 - cacache: 15.3.0 - http-cache-semantics: 4.1.1 - http-proxy-agent: 4.0.1 - https-proxy-agent: 5.0.1 - is-lambda: 1.0.1 - lru-cache: 6.0.0 - minipass: 3.3.6 - minipass-collect: 1.0.2 - minipass-fetch: 1.4.1 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - promise-retry: 2.0.1 - socks-proxy-agent: 5.0.1 - ssri: 8.0.1 - transitivePeerDependencies: - - supports-color - dev: true - - /makeerror/1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - dependencies: - tmpl: 1.0.5 - - /map-age-cleaner/0.1.3: - resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==} - engines: {node: '>=6'} - dependencies: - p-defer: 1.0.0 - dev: false - - /map-cache/0.2.2: - resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} - engines: {node: '>=0.10.0'} - - /map-obj/1.0.1: - resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} - engines: {node: '>=0.10.0'} - dev: false - - /map-obj/4.3.0: - resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} - engines: {node: '>=8'} - dev: false - - /map-or-similar/1.5.0: - resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==} - dev: true - - /map-visit/1.0.0: - resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==} - engines: {node: '>=0.10.0'} - dependencies: - object-visit: 1.0.1 - - /markdown-escapes/1.0.4: - resolution: {integrity: sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==} - dev: true - - /markdown-to-jsx/7.2.0_react@16.13.1: - resolution: {integrity: sha512-3l4/Bigjm4bEqjCR6Xr+d4DtM1X6vvtGsMGSjJYyep8RjjIvcWtrXBS8Wbfe1/P+atKNMccpsraESIaWVplzVg==} - engines: {node: '>= 10'} - peerDependencies: - react: '>= 0.14.0' - dependencies: - react: 16.13.1 - dev: true - - /md5.js/1.3.5: - resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} - dependencies: - hash-base: 3.1.0 - inherits: 2.0.4 - safe-buffer: 5.2.1 - - /md5/2.3.0: - resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} - dependencies: - charenc: 0.0.2 - crypt: 0.0.2 - is-buffer: 1.1.6 - dev: true - - /mdast-squeeze-paragraphs/4.0.0: - resolution: {integrity: sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ==} - dependencies: - unist-util-remove: 2.1.0 - dev: true - - /mdast-util-definitions/4.0.0: - resolution: {integrity: sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==} - dependencies: - unist-util-visit: 2.0.3 - dev: true - - /mdast-util-to-hast/10.0.1: - resolution: {integrity: sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==} - dependencies: - '@types/mdast': 3.0.11 - '@types/unist': 2.0.6 - mdast-util-definitions: 4.0.0 - mdurl: 1.0.1 - unist-builder: 2.0.3 - unist-util-generated: 1.1.6 - unist-util-position: 3.1.0 - unist-util-visit: 2.0.3 - dev: true - - /mdast-util-to-string/1.1.0: - resolution: {integrity: sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==} - dev: true - - /mdn-data/2.0.14: - resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} - dev: false - - /mdurl/1.0.1: - resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} - dev: true - - /media-typer/0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - - /mem/8.1.1: - resolution: {integrity: sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==} - engines: {node: '>=10'} - dependencies: - map-age-cleaner: 0.1.3 - mimic-fn: 3.1.0 - dev: false - - /memfs/3.4.3: - resolution: {integrity: sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==} - engines: {node: '>= 4.0.0'} - dependencies: - fs-monkey: 1.0.3 - - /memoizee/0.4.15: - resolution: {integrity: sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==} - dependencies: - d: 1.0.1 - es5-ext: 0.10.62 - es6-weak-map: 2.0.3 - event-emitter: 0.3.5 - is-promise: 2.2.2 - lru-queue: 0.1.0 - next-tick: 1.1.0 - timers-ext: 0.1.7 - dev: true - - /memoizerific/1.11.3: - resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==} - dependencies: - map-or-similar: 1.5.0 - dev: true - - /memory-fs/0.4.1: - resolution: {integrity: sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==} - dependencies: - errno: 0.1.8 - readable-stream: 2.3.8 - - /memory-fs/0.5.0: - resolution: {integrity: sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==} - engines: {node: '>=4.3.0 <5.0.0 || >=5.10'} - dependencies: - errno: 0.1.8 - readable-stream: 2.3.8 - - /meow/9.0.0: - resolution: {integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==} - engines: {node: '>=10'} - dependencies: - '@types/minimist': 1.2.2 - camelcase-keys: 6.2.2 - decamelize: 1.2.0 - decamelize-keys: 1.1.1 - hard-rejection: 2.1.0 - minimist-options: 4.1.0 - normalize-package-data: 3.0.3 - read-pkg-up: 7.0.1 - redent: 3.0.0 - trim-newlines: 3.0.1 - type-fest: 0.18.1 - yargs-parser: 20.2.9 - dev: false - - /merge-descriptors/1.0.1: - resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} - - /merge-stream/2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - /merge2/1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - /methods/1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} - - /microevent.ts/0.1.1: - resolution: {integrity: sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==} - dev: true - - /micromatch/3.1.10: - resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==} - engines: {node: '>=0.10.0'} - dependencies: - arr-diff: 4.0.0 - array-unique: 0.3.2 - braces: 2.3.2 - define-property: 2.0.2 - extend-shallow: 3.0.2 - extglob: 2.0.4 - fragment-cache: 0.2.1 - kind-of: 6.0.3 - nanomatch: 1.2.13 - object.pick: 1.3.0 - regex-not: 1.0.2 - snapdragon: 0.8.2 - to-regex: 3.0.2 - - /micromatch/4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} - engines: {node: '>=8.6'} - dependencies: - braces: 3.0.2 - picomatch: 2.3.1 - - /miller-rabin/4.0.1: - resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} - hasBin: true - dependencies: - bn.js: 4.12.0 - brorand: 1.1.0 - - /mime-db/1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - /mime-types/2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - dependencies: - mime-db: 1.52.0 - - /mime/1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - - /mime/2.6.0: - resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} - engines: {node: '>=4.0.0'} - hasBin: true - dev: true - - /mimic-fn/2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - /mimic-fn/3.1.0: - resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==} - engines: {node: '>=8'} - - /mimic-response/1.0.1: - resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} - engines: {node: '>=4'} - - /mimic-response/3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - - /min-document/2.19.0: - resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==} - dependencies: - dom-walk: 0.1.2 - dev: true - - /min-indent/1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - - /mini-css-extract-plugin/2.5.3_webpack@5.80.0: - resolution: {integrity: sha512-YseMB8cs8U/KCaAGQoqYmfUuhhGW0a9p9XvWXrxVOkE3/IiISTLw4ALNt7JR5B2eYauFM+PQGSbXMDmVbR7Tfw==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^5.0.0 - dependencies: - schema-utils: 4.0.0 - webpack: 5.80.0 - dev: false - - /minimalistic-assert/1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - - /minimalistic-crypto-utils/1.0.1: - resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - - /minimatch/3.0.8: - resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} - dependencies: - brace-expansion: 1.1.11 - - /minimatch/3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - dependencies: - brace-expansion: 1.1.11 - - /minimatch/5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - dependencies: - brace-expansion: 2.0.1 - dev: true - - /minimist-options/4.1.0: - resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} - engines: {node: '>= 6'} - dependencies: - arrify: 1.0.1 - is-plain-obj: 1.1.0 - kind-of: 6.0.3 - dev: false - - /minimist/1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - /minipass-collect/1.0.2: - resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - dev: true - - /minipass-fetch/1.4.1: - resolution: {integrity: sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==} - engines: {node: '>=8'} - dependencies: - minipass: 3.3.6 - minipass-sized: 1.0.3 - minizlib: 2.1.2 - optionalDependencies: - encoding: 0.1.13 - dev: true - - /minipass-flush/1.0.5: - resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - dev: true - - /minipass-pipeline/1.2.4: - resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} - engines: {node: '>=8'} - dependencies: - minipass: 3.3.6 - dev: true - - /minipass-sized/1.0.3: - resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} - engines: {node: '>=8'} - dependencies: - minipass: 3.3.6 - dev: true - - /minipass/3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - dependencies: - yallist: 4.0.0 - - /minipass/4.2.5: - resolution: {integrity: sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==} - engines: {node: '>=8'} - - /minizlib/2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - - /mississippi/3.0.0: - resolution: {integrity: sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==} - engines: {node: '>=4.0.0'} - dependencies: - concat-stream: 1.6.2 - duplexify: 3.7.1 - end-of-stream: 1.4.4 - flush-write-stream: 1.1.1 - from2: 2.3.0 - parallel-transform: 1.2.0 - pump: 3.0.0 - pumpify: 1.5.1 - stream-each: 1.2.3 - through2: 2.0.5 - - /mixin-deep/1.3.2: - resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==} - engines: {node: '>=0.10.0'} - dependencies: - for-in: 1.0.2 - is-extendable: 1.0.1 - - /mkdirp/0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - dependencies: - minimist: 1.2.8 - - /mkdirp/1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - - /move-concurrently/1.0.1: - resolution: {integrity: sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==} - dependencies: - aproba: 1.2.0 - copy-concurrently: 1.0.5 - fs-write-stream-atomic: 1.0.10 - mkdirp: 0.5.6 - rimraf: 2.7.1 - run-queue: 1.0.3 - - /mrmime/1.0.1: - resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} - engines: {node: '>=10'} - - /ms/2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - - /ms/2.1.1: - resolution: {integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==} - dev: true - - /ms/2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - - /ms/2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - /multicast-dns/7.2.5: - resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} - hasBin: true - dependencies: - dns-packet: 5.5.0 - thunky: 1.1.0 - dev: false - - /multimatch/5.0.0: - resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} - engines: {node: '>=10'} - dependencies: - '@types/minimatch': 3.0.5 - array-differ: 3.0.0 - array-union: 2.1.0 - arrify: 2.0.1 - minimatch: 3.1.2 - dev: false - - /mute-stream/0.0.8: - resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} - - /mz/2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 - dev: false - - /nan/2.17.0: - resolution: {integrity: sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==} - requiresBuild: true - optional: true - - /nanoid/3.3.6: - resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - /nanomatch/1.2.13: - resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} - engines: {node: '>=0.10.0'} - dependencies: - arr-diff: 4.0.0 - array-unique: 0.3.2 - define-property: 2.0.2 - extend-shallow: 3.0.2 - fragment-cache: 0.2.1 - is-windows: 1.0.2 - kind-of: 6.0.3 - object.pick: 1.3.0 - regex-not: 1.0.2 - snapdragon: 0.8.2 - to-regex: 3.0.2 - - /natural-compare-lite/1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - - /natural-compare/1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - /ndjson/2.0.0: - resolution: {integrity: sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ==} - engines: {node: '>=10'} - hasBin: true - dependencies: - json-stringify-safe: 5.0.1 - minimist: 1.2.8 - readable-stream: 3.6.2 - split2: 3.2.2 - through2: 4.0.2 - dev: true - - /negotiator/0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - - /neo-async/2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - - /nested-error-stacks/2.1.1: - resolution: {integrity: sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==} - dev: true - - /netmask/2.0.2: - resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} - engines: {node: '>= 0.4.0'} - dev: true - - /next-tick/1.1.0: - resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} - dev: true - - /nice-try/1.0.5: - resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} - - /no-case/3.0.4: - resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - dependencies: - lower-case: 2.0.2 - tslib: 2.3.1 - - /node-addon-api/3.2.1: - resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} - dev: true - - /node-dir/0.1.17: - resolution: {integrity: sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==} - engines: {node: '>= 0.10.5'} - dependencies: - minimatch: 3.0.8 - dev: true - - /node-emoji/1.11.0: - resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} - dependencies: - lodash: 4.17.21 - dev: false - - /node-fetch/2.6.7: - resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - dependencies: - whatwg-url: 5.0.0 - - /node-forge/1.3.1: - resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} - engines: {node: '>= 6.13.0'} - dev: false - - /node-gyp/8.1.0: - resolution: {integrity: sha512-o2elh1qt7YUp3lkMwY3/l4KF3j/A3fI/Qt4NH+CQQgPJdqGE9y7qnP84cjIWN27Q0jJkrSAhCVDg+wBVNBYdBg==} - engines: {node: '>= 10.12.0'} - hasBin: true - dependencies: - env-paths: 2.2.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - make-fetch-happen: 8.0.14 - nopt: 5.0.0 - npmlog: 4.1.2 - rimraf: 3.0.2 - semver: 7.3.8 - tar: 6.1.13 - which: 2.0.2 - transitivePeerDependencies: - - supports-color - dev: true - - /node-int64/0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - - /node-libs-browser/2.2.1: - resolution: {integrity: sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==} - dependencies: - assert: 1.5.0 - browserify-zlib: 0.2.0 - buffer: 4.9.2 - console-browserify: 1.2.0 - constants-browserify: 1.0.0 - crypto-browserify: 3.12.0 - domain-browser: 1.2.0 - events: 3.3.0 - https-browserify: 1.0.0 - os-browserify: 0.3.0 - path-browserify: 0.0.1 - process: 0.11.10 - punycode: 1.4.1 - querystring-es3: 0.2.1 - readable-stream: 2.3.8 - stream-browserify: 2.0.2 - stream-http: 2.8.3 - string_decoder: 1.3.0 - timers-browserify: 2.0.12 - tty-browserify: 0.0.0 - url: 0.11.0 - util: 0.11.1 - vm-browserify: 1.1.2 - - /node-releases/2.0.10: - resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} - - /nopt/5.0.0: - resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} - engines: {node: '>=6'} - hasBin: true - dependencies: - abbrev: 1.1.1 - dev: true - - /normalize-package-data/2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.1 - semver: 5.7.1 - validate-npm-package-license: 3.0.4 - - /normalize-package-data/3.0.3: - resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} - engines: {node: '>=10'} - dependencies: - hosted-git-info: 4.1.0 - is-core-module: 2.11.0 - semver: 7.3.8 - validate-npm-package-license: 3.0.4 - dev: false - - /normalize-path/2.1.1: - resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} - engines: {node: '>=0.10.0'} - dependencies: - remove-trailing-separator: 1.1.0 - - /normalize-path/3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - /normalize-range/0.1.2: - resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} - engines: {node: '>=0.10.0'} - - /normalize-url/6.1.0: - resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} - engines: {node: '>=10'} - - /npm-bundled/1.1.2: - resolution: {integrity: sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==} - dependencies: - npm-normalize-package-bin: 1.0.1 - dev: false - - /npm-check/6.0.1: - resolution: {integrity: sha512-tlEhXU3689VLUHYEZTS/BC61vfeN2xSSZwoWDT6WLuenZTpDmGmNT5mtl15erTR0/A15ldK06/NEKg9jYJ9OTQ==} - engines: {node: '>=10.9.0'} - hasBin: true - dependencies: - callsite-record: 4.1.5 - chalk: 4.1.2 - co: 4.6.0 - depcheck: 1.4.3 - execa: 5.1.1 - giturl: 1.0.1 - global-modules: 2.0.0 - globby: 11.1.0 - inquirer: 7.3.3 - is-ci: 2.0.0 - lodash: 4.17.21 - meow: 9.0.0 - minimatch: 3.0.8 - node-emoji: 1.11.0 - ora: 5.4.1 - package-json: 7.0.0 - path-exists: 4.0.0 - pkg-dir: 5.0.0 - preferred-pm: 3.0.3 - rc-config-loader: 4.1.2 - semver: 7.3.8 - semver-diff: 3.1.1 - strip-ansi: 6.0.1 - text-table: 0.2.0 - throat: 6.0.2 - update-notifier: 5.1.0 - xtend: 4.0.2 - transitivePeerDependencies: - - supports-color - dev: false - - /npm-normalize-package-bin/1.0.1: - resolution: {integrity: sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==} - dev: false - - /npm-package-arg/6.1.1: - resolution: {integrity: sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==} - dependencies: - hosted-git-info: 2.8.9 - osenv: 0.1.5 - semver: 5.7.1 - validate-npm-package-name: 3.0.0 - dev: false - - /npm-packlist/2.1.5: - resolution: {integrity: sha512-KCfK3Vi2F+PH1klYauoQzg81GQ8/GGjQRKYY6tRnpQUPKTs/1gBZSRWtTEd7jGdSn1LZL7gpAmJT+BcS55k2XQ==} - engines: {node: '>=10'} - hasBin: true - dependencies: - glob: 7.2.3 - ignore-walk: 3.0.4 - npm-bundled: 1.1.2 - npm-normalize-package-bin: 1.0.1 - dev: false - - /npm-run-path/2.0.2: - resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} - engines: {node: '>=4'} - dependencies: - path-key: 2.0.1 - dev: true - - /npm-run-path/4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - dependencies: - path-key: 3.1.1 - - /npmlog/4.1.2: - resolution: {integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==} - dependencies: - are-we-there-yet: 1.1.7 - console-control-strings: 1.1.0 - gauge: 2.7.4 - set-blocking: 2.0.0 - dev: true - - /npmlog/5.0.1: - resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} - dependencies: - are-we-there-yet: 2.0.0 - console-control-strings: 1.1.0 - gauge: 3.0.2 - set-blocking: 2.0.0 - dev: true - - /nth-check/2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - dependencies: - boolbase: 1.0.0 - - /num2fraction/1.2.2: - resolution: {integrity: sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==} - dev: true - - /number-is-nan/1.0.1: - resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} - engines: {node: '>=0.10.0'} - dev: true - - /nwsapi/2.2.2: - resolution: {integrity: sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==} - - /object-assign/4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - /object-copy/0.1.0: - resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==} - engines: {node: '>=0.10.0'} - dependencies: - copy-descriptor: 0.1.1 - define-property: 0.2.5 - kind-of: 3.2.2 - - /object-inspect/1.12.3: - resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} - - /object-keys/1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - /object-visit/1.0.1: - resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==} - engines: {node: '>=0.10.0'} - dependencies: - isobject: 3.0.1 - - /object.assign/4.1.4: - resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - has-symbols: 1.0.3 - object-keys: 1.1.1 - - /object.entries/1.1.6: - resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - - /object.fromentries/2.0.6: - resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - - /object.getownpropertydescriptors/2.1.5: - resolution: {integrity: sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==} - engines: {node: '>= 0.8'} - dependencies: - array.prototype.reduce: 1.0.5 - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - - /object.hasown/1.1.2: - resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} - dependencies: - define-properties: 1.2.0 - es-abstract: 1.21.2 - - /object.pick/1.3.0: - resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} - engines: {node: '>=0.10.0'} - dependencies: - isobject: 3.0.1 - - /object.values/1.1.6: - resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - - /objectorarray/1.0.5: - resolution: {integrity: sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg==} - dev: true - - /obuf/1.1.2: - resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} - dev: false - - /on-finished/2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - dependencies: - ee-first: 1.1.1 - - /on-headers/1.0.2: - resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} - engines: {node: '>= 0.8'} - - /once/1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - dependencies: - wrappy: 1.0.2 - - /onetime/5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - dependencies: - mimic-fn: 2.1.0 - - /open/7.4.2: - resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} - engines: {node: '>=8'} - dependencies: - is-docker: 2.2.1 - is-wsl: 2.2.0 - dev: true - - /open/8.4.2: - resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} - engines: {node: '>=12'} - dependencies: - define-lazy-prop: 2.0.0 - is-docker: 2.2.1 - is-wsl: 2.2.0 - dev: false - - /opener/1.5.2: - resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} - hasBin: true - - /optionator/0.8.3: - resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} - engines: {node: '>= 0.8.0'} - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.3.0 - prelude-ls: 1.1.2 - type-check: 0.3.2 - word-wrap: 1.2.3 - - /optionator/0.9.1: - resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} - engines: {node: '>= 0.8.0'} - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.3 - - /ora/5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} - dependencies: - bl: 4.1.0 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-spinners: 2.7.0 - is-interactive: 1.0.0 - is-unicode-supported: 0.1.0 - log-symbols: 4.1.0 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - dev: false - - /os-browserify/0.3.0: - resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} - - /os-homedir/1.0.2: - resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} - engines: {node: '>=0.10.0'} - dev: false - - /os-tmpdir/1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - dev: false - - /osenv/0.1.5: - resolution: {integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==} - dependencies: - os-homedir: 1.0.2 - os-tmpdir: 1.0.2 - dev: false - - /overlayscrollbars/1.13.3: - resolution: {integrity: sha512-1nB/B5kaakJuHXaLXLRK0bUIilWhUGT6q5g+l2s5vqYdLle/sd0kscBHkQC1kuuDg9p9WR4MTdySDOPbeL/86g==} - dev: true - - /p-all/2.1.0: - resolution: {integrity: sha512-HbZxz5FONzz/z2gJfk6bFca0BCiSRF8jU3yCsWOen/vR6lZjfPOu/e7L3uFzTW1i0H8TlC3vqQstEJPQL4/uLA==} - engines: {node: '>=6'} - dependencies: - p-map: 2.1.0 - dev: true - - /p-cancelable/2.1.1: - resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} - engines: {node: '>=8'} - - /p-defer/1.0.0: - resolution: {integrity: sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==} - engines: {node: '>=4'} - dev: false - - /p-event/4.2.0: - resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} - engines: {node: '>=8'} - dependencies: - p-timeout: 3.2.0 - dev: true - - /p-filter/2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} - dependencies: - p-map: 2.1.0 - dev: true - - /p-finally/1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} - engines: {node: '>=4'} - dev: true - - /p-limit/2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - dependencies: - p-try: 2.2.0 - - /p-limit/3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - dependencies: - yocto-queue: 0.1.0 - - /p-locate/3.0.0: - resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} - engines: {node: '>=6'} - dependencies: - p-limit: 2.3.0 - - /p-locate/4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - dependencies: - p-limit: 2.3.0 - - /p-locate/5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - dependencies: - p-limit: 3.1.0 - - /p-map/2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - dev: true - - /p-map/3.0.0: - resolution: {integrity: sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==} - engines: {node: '>=8'} - dependencies: - aggregate-error: 3.1.0 - dev: true - - /p-map/4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} - dependencies: - aggregate-error: 3.1.0 - dev: true - - /p-reflect/2.1.0: - resolution: {integrity: sha512-paHV8NUz8zDHu5lhr/ngGWQiW067DK/+IbJ+RfZ4k+s8y4EKyYCz8pGYWjxCg35eHztpJAt+NUgvN4L+GCbPlg==} - engines: {node: '>=8'} - dev: false - - /p-retry/4.6.2: - resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} - engines: {node: '>=8'} - dependencies: - '@types/retry': 0.12.0 - retry: 0.13.1 - dev: false - - /p-settle/4.1.1: - resolution: {integrity: sha512-6THGh13mt3gypcNMm0ADqVNCcYa3BK6DWsuJWFCuEKP1rpY+OKGp7gaZwVmLspmic01+fsg/fN57MfvDzZ/PuQ==} - engines: {node: '>=10'} - dependencies: - p-limit: 2.3.0 - p-reflect: 2.1.0 - dev: false - - /p-timeout/3.2.0: - resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} - engines: {node: '>=8'} - dependencies: - p-finally: 1.0.0 - dev: true - - /p-try/2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - /pac-proxy-agent/5.0.0: - resolution: {integrity: sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==} - engines: {node: '>= 8'} - dependencies: - '@tootallnate/once': 1.1.2 - agent-base: 6.0.2 - debug: 4.3.4 - get-uri: 3.0.2 - http-proxy-agent: 4.0.1 - https-proxy-agent: 5.0.1 - pac-resolver: 5.0.1 - raw-body: 2.5.2 - socks-proxy-agent: 5.0.1 - transitivePeerDependencies: - - supports-color - dev: true - - /pac-resolver/5.0.1: - resolution: {integrity: sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==} - engines: {node: '>= 8'} - dependencies: - degenerator: 3.0.2 - ip: 1.1.8 - netmask: 2.0.2 - dev: true - - /package-json/7.0.0: - resolution: {integrity: sha512-CHJqc94AA8YfSLHGQT3DbvSIuE12NLFekpM4n7LRrAd3dOJtA911+4xe9q6nC3/jcKraq7nNS9VxgtT0KC+diA==} - engines: {node: '>=12'} - dependencies: - got: 11.8.6 - registry-auth-token: 4.2.2 - registry-url: 5.1.0 - semver: 7.3.8 - - /pako/1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - - /parallel-transform/1.2.0: - resolution: {integrity: sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==} - dependencies: - cyclist: 1.0.1 - inherits: 2.0.4 - readable-stream: 2.3.8 - - /param-case/3.0.4: - resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} - dependencies: - dot-case: 3.0.4 - tslib: 2.3.1 - - /parent-module/1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - dependencies: - callsites: 3.1.0 - - /parse-asn1/5.1.6: - resolution: {integrity: sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==} - dependencies: - asn1.js: 5.4.1 - browserify-aes: 1.2.0 - evp_bytestokey: 1.0.3 - pbkdf2: 3.1.2 - safe-buffer: 5.2.1 - - /parse-entities/2.0.0: - resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} - dependencies: - character-entities: 1.2.4 - character-entities-legacy: 1.1.4 - character-reference-invalid: 1.1.4 - is-alphanumerical: 1.0.4 - is-decimal: 1.0.4 - is-hexadecimal: 1.0.4 - dev: true - - /parse-json/5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - dependencies: - '@babel/code-frame': 7.18.6 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - - /parse-passwd/1.0.0: - resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} - engines: {node: '>=0.10.0'} - dev: false - - /parse5/6.0.1: - resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} - dev: true - - /parse5/7.1.2: - resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} - dependencies: - entities: 4.4.0 - - /parseurl/1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - - /pascal-case/3.1.2: - resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} - dependencies: - no-case: 3.0.4 - tslib: 2.3.1 - - /pascalcase/0.1.1: - resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} - engines: {node: '>=0.10.0'} - - /path-browserify/0.0.1: - resolution: {integrity: sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==} - - /path-dirname/1.0.2: - resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==} - - /path-exists/3.0.0: - resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} - engines: {node: '>=4'} - - /path-exists/4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - /path-is-absolute/1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - /path-key/2.0.1: - resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} - engines: {node: '>=4'} - - /path-key/3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - /path-parse/1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - /path-to-regexp/0.1.7: - resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} - - /path-type/3.0.0: - resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} - engines: {node: '>=4'} - dependencies: - pify: 3.0.0 - dev: true - - /path-type/4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - /pbkdf2/3.1.2: - resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} - engines: {node: '>=0.12'} - dependencies: - create-hash: 1.2.0 - create-hmac: 1.1.7 - ripemd160: 2.0.2 - safe-buffer: 5.2.1 - sha.js: 2.4.11 - - /pend/1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - dev: true - - /picocolors/0.2.1: - resolution: {integrity: sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==} - - /picocolors/1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - - /picomatch/2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - /pidof/1.0.2: - resolution: {integrity: sha512-LLJhTVEUCZnotdAM5rd7KiTdLGgk6i763/hsd5pO+8yuF7mdgg0ob8w/98KrTAcPsj6YzGrkFLPVtBOr1uW2ag==} - dev: false - - /pify/3.0.0: - resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} - engines: {node: '>=4'} - dev: true - - /pify/4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - - /pinkie-promise/2.0.1: - resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} - engines: {node: '>=0.10.0'} - dependencies: - pinkie: 2.0.4 - dev: false - - /pinkie/2.0.4: - resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} - engines: {node: '>=0.10.0'} - dev: false - - /pino-std-serializers/3.2.0: - resolution: {integrity: sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==} - dev: false - - /pino/6.14.0: - resolution: {integrity: sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==} - hasBin: true - dependencies: - fast-redact: 3.1.2 - fast-safe-stringify: 2.1.1 - flatstr: 1.0.12 - pino-std-serializers: 3.2.0 - process-warning: 1.0.0 - quick-format-unescaped: 4.0.4 - sonic-boom: 1.4.1 - dev: false - - /pirates/4.0.5: - resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} - engines: {node: '>= 6'} - - /pkg-dir/3.0.0: - resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==} - engines: {node: '>=6'} - dependencies: - find-up: 3.0.0 - - /pkg-dir/4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - dependencies: - find-up: 4.1.0 - - /pkg-dir/5.0.0: - resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} - engines: {node: '>=10'} - dependencies: - find-up: 5.0.0 - - /pkg-up/3.1.0: - resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} - engines: {node: '>=8'} - dependencies: - find-up: 3.0.0 - dev: true - - /please-upgrade-node/3.2.0: - resolution: {integrity: sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==} - dependencies: - semver-compare: 1.0.0 - dev: false - - /pnp-webpack-plugin/1.6.4_typescript@5.0.4: - resolution: {integrity: sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==} - engines: {node: '>=6'} - dependencies: - ts-pnp: 1.2.0_typescript@5.0.4 - transitivePeerDependencies: - - typescript - dev: true - - /polished/4.2.2: - resolution: {integrity: sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==} - engines: {node: '>=10'} - dependencies: - '@babel/runtime': 7.21.0 - dev: true - - /posix-character-classes/0.1.1: - resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==} - engines: {node: '>=0.10.0'} - - /postcss-calc/8.2.4_postcss@8.4.21: - resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} - peerDependencies: - postcss: ^8.2.2 - dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-colormin/5.3.1_postcss@8.4.21: - resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - browserslist: 4.21.5 - caniuse-api: 3.0.0 - colord: 2.9.3 - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-convert-values/5.1.3_postcss@8.4.21: - resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - browserslist: 4.21.5 - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-discard-comments/5.1.2_postcss@8.4.21: - resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - dev: false - - /postcss-discard-duplicates/5.1.0_postcss@8.4.21: - resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - dev: false - - /postcss-discard-empty/5.1.1_postcss@8.4.21: - resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - dev: false - - /postcss-discard-overridden/5.1.0_postcss@8.4.21: - resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - dev: false - - /postcss-flexbugs-fixes/4.2.1: - resolution: {integrity: sha512-9SiofaZ9CWpQWxOwRh1b/r85KD5y7GgvsNt1056k6OYLvWUun0czCvogfJgylC22uJTwW1KzY3Gz65NZRlvoiQ==} - dependencies: - postcss: 7.0.39 - dev: true - - /postcss-loader/4.1.0_q6bo6nn7or7rkhjb274oworunu: - resolution: {integrity: sha512-vbCkP70F3Q9PIk6d47aBwjqAMI4LfkXCoyxj+7NPNuVIwfTGdzv2KVQes59/RuxMniIgsYQCFSY42P3+ykJfaw==} - engines: {node: '>= 10.13.0'} - peerDependencies: - postcss: ^7.0.0 || ^8.0.1 - webpack: ^4.0.0 || ^5.0.0 - dependencies: - cosmiconfig: 7.1.0 - klona: 2.0.6 - loader-utils: 2.0.4 - postcss: 8.4.21 - schema-utils: 3.1.1 - semver: 7.3.8 - webpack: 4.44.2 - dev: true - - /postcss-loader/4.3.0_4a2i7aa2i6hzz4ngguaxzo4tzi: - resolution: {integrity: sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==} - engines: {node: '>= 10.13.0'} - peerDependencies: - postcss: ^7.0.0 || ^8.0.1 - webpack: ^4.0.0 || ^5.0.0 - dependencies: - cosmiconfig: 7.1.0 - klona: 2.0.6 - loader-utils: 2.0.4 - postcss: 7.0.39 - schema-utils: 3.1.2 - semver: 7.3.8 - webpack: 4.44.2 - dev: true - - /postcss-loader/6.2.1_s3hlmk3dolibrgzfaoz6qln5l4: - resolution: {integrity: sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==} - engines: {node: '>= 12.13.0'} - peerDependencies: - postcss: ^7.0.0 || ^8.0.1 - webpack: ^5.0.0 - dependencies: - cosmiconfig: 7.1.0 - klona: 2.0.6 - postcss: 8.4.21 - semver: 7.3.8 - webpack: 5.80.0 - dev: false - - /postcss-merge-longhand/5.1.7_postcss@8.4.21: - resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - stylehacks: 5.1.1_postcss@8.4.21 - dev: false - - /postcss-merge-rules/5.1.4_postcss@8.4.21: - resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - browserslist: 4.21.5 - caniuse-api: 3.0.0 - cssnano-utils: 3.1.0_postcss@8.4.21 - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false - - /postcss-minify-font-values/5.1.0_postcss@8.4.21: - resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-minify-gradients/5.1.1_postcss@8.4.21: - resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - colord: 2.9.3 - cssnano-utils: 3.1.0_postcss@8.4.21 - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-minify-params/5.1.4_postcss@8.4.21: - resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - browserslist: 4.21.5 - cssnano-utils: 3.1.0_postcss@8.4.21 - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-minify-selectors/5.2.1_postcss@8.4.21: - resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false - - /postcss-modules-extract-imports/1.1.0: - resolution: {integrity: sha512-zF9+UIEvtpeqMGxhpeT9XaIevQSrBBCz9fi7SwfkmjVacsSj8DY5eFVgn+wY8I9vvdDDwK5xC8Myq4UkoLFIkA==} - dependencies: - postcss: 6.0.1 - dev: false - - /postcss-modules-extract-imports/2.0.0: - resolution: {integrity: sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==} - engines: {node: '>= 6'} - dependencies: - postcss: 7.0.39 - dev: true - - /postcss-modules-extract-imports/3.0.0_postcss@8.4.21: - resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - dependencies: - postcss: 8.4.21 - - /postcss-modules-local-by-default/1.2.0: - resolution: {integrity: sha512-X4cquUPIaAd86raVrBwO8fwRfkIdbwFu7CTfEOjiZQHVQwlHRSkTgH5NLDmMm5+1hQO8u6dZ+TOOJDbay1hYpA==} - dependencies: - css-selector-tokenizer: 0.7.3 - postcss: 6.0.1 - dev: false - - /postcss-modules-local-by-default/3.0.3: - resolution: {integrity: sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==} - engines: {node: '>= 6'} - dependencies: - icss-utils: 4.1.1 - postcss: 7.0.39 - postcss-selector-parser: 6.0.11 - postcss-value-parser: 4.2.0 - dev: true - - /postcss-modules-local-by-default/4.0.0_postcss@8.4.21: - resolution: {integrity: sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - dependencies: - icss-utils: 5.1.0_postcss@8.4.21 - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - postcss-value-parser: 4.2.0 - - /postcss-modules-scope/1.1.0: - resolution: {integrity: sha512-LTYwnA4C1He1BKZXIx1CYiHixdSe9LWYVKadq9lK5aCCMkoOkFyZ7aigt+srfjlRplJY3gIol6KUNefdMQJdlw==} - dependencies: - css-selector-tokenizer: 0.7.3 - postcss: 6.0.1 - dev: false - - /postcss-modules-scope/2.2.0: - resolution: {integrity: sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==} - engines: {node: '>= 6'} - dependencies: - postcss: 7.0.39 - postcss-selector-parser: 6.0.11 - dev: true - - /postcss-modules-scope/3.0.0_postcss@8.4.21: - resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - - /postcss-modules-values/1.3.0: - resolution: {integrity: sha512-i7IFaR9hlQ6/0UgFuqM6YWaCfA1Ej8WMg8A5DggnH1UGKJvTV/ugqq/KaULixzzOi3T/tF6ClBXcHGCzdd5unA==} - dependencies: - icss-replace-symbols: 1.1.0 - postcss: 6.0.1 - dev: false - - /postcss-modules-values/3.0.0: - resolution: {integrity: sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==} - dependencies: - icss-utils: 4.1.1 - postcss: 7.0.39 - dev: true - - /postcss-modules-values/4.0.0_postcss@8.4.21: - resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - dependencies: - icss-utils: 5.1.0_postcss@8.4.21 - postcss: 8.4.21 - - /postcss-modules/1.5.0: - resolution: {integrity: sha512-KiAihzcV0TxTTNA5OXreyIXctuHOfR50WIhqBpc8pe0Q5dcs/Uap9EVlifOI9am7zGGdGOJQ6B1MPYKo2UxgOg==} - dependencies: - css-modules-loader-core: 1.1.0 - generic-names: 2.0.1 - lodash.camelcase: 4.3.0 - postcss: 7.0.39 - string-hash: 1.1.3 - dev: false - - /postcss-normalize-charset/5.1.0_postcss@8.4.21: - resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - dev: false - - /postcss-normalize-display-values/5.1.0_postcss@8.4.21: - resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-normalize-positions/5.1.1_postcss@8.4.21: - resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-normalize-repeat-style/5.1.1_postcss@8.4.21: - resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-normalize-string/5.1.0_postcss@8.4.21: - resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-normalize-timing-functions/5.1.0_postcss@8.4.21: - resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-normalize-unicode/5.1.1_postcss@8.4.21: - resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - browserslist: 4.21.5 - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-normalize-url/5.1.0_postcss@8.4.21: - resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - normalize-url: 6.1.0 - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-normalize-whitespace/5.1.1_postcss@8.4.21: - resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-ordered-values/5.1.3_postcss@8.4.21: - resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - cssnano-utils: 3.1.0_postcss@8.4.21 - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-reduce-initial/5.1.2_postcss@8.4.21: - resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - browserslist: 4.21.5 - caniuse-api: 3.0.0 - postcss: 8.4.21 - dev: false - - /postcss-reduce-transforms/5.1.0_postcss@8.4.21: - resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-selector-parser/6.0.11: - resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==} - engines: {node: '>=4'} - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - - /postcss-svgo/5.1.0_postcss@8.4.21: - resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - svgo: 2.8.0 - dev: false - - /postcss-unique-selectors/5.1.1_postcss@8.4.21: - resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false - - /postcss-value-parser/4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - - /postcss/6.0.1: - resolution: {integrity: sha512-VbGX1LQgQbf9l3cZ3qbUuC3hGqIEOGQFHAEHQ/Diaeo0yLgpgK5Rb8J+OcamIfQ9PbAU/fzBjVtQX3AhJHUvZw==} - engines: {node: '>=4.0.0'} - dependencies: - chalk: 1.1.3 - source-map: 0.5.7 - supports-color: 3.2.3 - dev: false - - /postcss/7.0.39: - resolution: {integrity: sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==} - engines: {node: '>=6.0.0'} - dependencies: - picocolors: 0.2.1 - source-map: 0.6.1 - - /postcss/8.4.21: - resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.6 - picocolors: 1.0.0 - source-map-js: 1.0.2 - - /preferred-pm/3.0.3: - resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==} - engines: {node: '>=10'} - dependencies: - find-up: 5.0.0 - find-yarn-workspace-root2: 1.2.16 - path-exists: 4.0.0 - which-pm: 2.0.0 - dev: false - - /prelude-ls/1.1.2: - resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} - engines: {node: '>= 0.8.0'} - - /prelude-ls/1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - - /prettier/2.3.0: - resolution: {integrity: sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==} - engines: {node: '>=10.13.0'} - hasBin: true - dev: true - - /prettier/2.3.2: - resolution: {integrity: sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==} - engines: {node: '>=10.13.0'} - hasBin: true - dev: true - - /pretty-error/2.1.2: - resolution: {integrity: sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==} - dependencies: - lodash: 4.17.21 - renderkid: 2.0.7 - - /pretty-error/4.0.0: - resolution: {integrity: sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==} - dependencies: - lodash: 4.17.21 - renderkid: 3.0.0 - - /pretty-format/27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - ansi-regex: 5.0.1 - ansi-styles: 5.2.0 - react-is: 17.0.2 - dev: true - - /pretty-format/29.5.0: - resolution: {integrity: sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.4.3 - ansi-styles: 5.2.0 - react-is: 18.2.0 - - /pretty-hrtime/1.0.3: - resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} - engines: {node: '>= 0.8'} - dev: true - - /prismjs/1.27.0: - resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} - engines: {node: '>=6'} - dev: true - - /prismjs/1.29.0: - resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} - engines: {node: '>=6'} - dev: true - - /private/0.1.8: - resolution: {integrity: sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==} - engines: {node: '>= 0.6'} - dev: true - - /process-nextick-args/2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - /process-warning/1.0.0: - resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} - dev: false - - /process/0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - - /progress/2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - dev: true - - /promise-inflight/1.0.1: - resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} - - /promise-retry/2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} - dependencies: - err-code: 2.0.3 - retry: 0.12.0 - dev: true - - /promise.allsettled/1.0.6: - resolution: {integrity: sha512-22wJUOD3zswWFqgwjNHa1965LvqTX87WPu/lreY2KSd7SVcERfuZ4GfUaOnJNnvtoIv2yXT/W00YIGMetXtFXg==} - engines: {node: '>= 0.4'} - dependencies: - array.prototype.map: 1.0.5 - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - get-intrinsic: 1.2.0 - iterate-value: 1.0.2 - dev: true - - /promise.prototype.finally/3.1.4: - resolution: {integrity: sha512-nNc3YbgMfLzqtqvO/q5DP6RR0SiHI9pUPGzyDf1q+usTwCN2kjvAnJkBb7bHe3o+fFSBPpsGMoYtaSi+LTNqng==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true - - /promptly/3.2.0: - resolution: {integrity: sha512-WnR9obtgW+rG4oUV3hSnNGl1pHm3V1H/qD9iJBumGSmVsSC5HpZOLuu8qdMb6yCItGfT7dcRszejr/5P3i9Pug==} - dependencies: - read: 1.0.7 - dev: true - - /prompts/2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - dev: true - - /prop-types/15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - - /property-information/5.6.0: - resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} - dependencies: - xtend: 4.0.2 - dev: true - - /proxy-addr/2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - - /proxy-agent/5.0.0: - resolution: {integrity: sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==} - engines: {node: '>= 8'} - dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - http-proxy-agent: 4.0.1 - https-proxy-agent: 5.0.1 - lru-cache: 5.1.1 - pac-proxy-agent: 5.0.0 - proxy-from-env: 1.1.0 - socks-proxy-agent: 5.0.1 - transitivePeerDependencies: - - supports-color - dev: true - - /proxy-from-env/1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - dev: true - - /prr/1.0.1: - resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} - - /pseudolocale/1.1.0: - resolution: {integrity: sha512-OZ8I/hwYEJ3beN3IEcNnt8EpcqblH0/x23hulKBXjs+WhTTEle+ijCHCkh2bd+cIIeCuCwSCbBe93IthGG6hLw==} - dependencies: - commander: 10.0.0 - dev: false - - /psl/1.9.0: - resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - - /public-encrypt/4.0.3: - resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} - dependencies: - bn.js: 4.12.0 - browserify-rsa: 4.1.0 - create-hash: 1.2.0 - parse-asn1: 5.1.6 - randombytes: 2.1.0 - safe-buffer: 5.2.1 - - /pump/2.0.1: - resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==} - dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 - - /pump/3.0.0: - resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} - dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 - - /pumpify/1.5.1: - resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==} - dependencies: - duplexify: 3.7.1 - inherits: 2.0.4 - pump: 2.0.1 - - /punycode/1.3.2: - resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==} - - /punycode/1.4.1: - resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} - - /punycode/2.3.0: - resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} - engines: {node: '>=6'} - - /pupa/2.1.1: - resolution: {integrity: sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==} - engines: {node: '>=8'} - dependencies: - escape-goat: 2.1.1 - - /puppeteer-core/2.1.1: - resolution: {integrity: sha512-n13AWriBMPYxnpbb6bnaY5YoY6rGj8vPLrz6CZF3o0qJNEwlcfJVxBzYZ0NJsQ21UbdJoijPCDrM++SUVEz7+w==} - engines: {node: '>=8.16.0'} - dependencies: - '@types/mime-types': 2.1.1 - debug: 4.3.4 - extract-zip: 1.7.0 - https-proxy-agent: 4.0.0 - mime: 2.6.0 - mime-types: 2.1.35 - progress: 2.0.3 - proxy-from-env: 1.1.0 - rimraf: 2.7.1 - ws: 6.2.2 - transitivePeerDependencies: - - supports-color - dev: true - - /pure-rand/6.0.1: - resolution: {integrity: sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==} - - /q/1.5.1: - resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} - engines: {node: '>=0.6.0', teleport: '>=0.2.0'} - dev: true - - /qs/6.10.3: - resolution: {integrity: sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==} - engines: {node: '>=0.6'} - dependencies: - side-channel: 1.0.4 - - /qs/6.11.0: - resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} - engines: {node: '>=0.6'} - dependencies: - side-channel: 1.0.4 - dev: true - - /qs/6.11.1: - resolution: {integrity: sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==} - engines: {node: '>=0.6'} - dependencies: - side-channel: 1.0.4 - dev: true - - /query-ast/1.0.5: - resolution: {integrity: sha512-JK+1ma4YDuLjvKKcz9JZ70G+CM9qEOs/l1cZzstMMfwKUabTJ9sud5jvDGrUNuv03yKUgs82bLkHXJkDyhRmBw==} - dependencies: - invariant: 2.2.4 - lodash: 4.17.21 - dev: false - - /querystring-es3/0.2.1: - resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} - engines: {node: '>=0.4.x'} - - /querystring/0.2.0: - resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==} - engines: {node: '>=0.4.x'} - deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. - - /querystringify/2.2.0: - resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - - /queue-microtask/1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - /quick-format-unescaped/4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - dev: false - - /quick-lru/4.0.1: - resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} - engines: {node: '>=8'} - dev: false - - /quick-lru/5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} - - /ramda/0.27.2: - resolution: {integrity: sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA==} - dev: false - - /ramda/0.28.0: - resolution: {integrity: sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==} - dev: true - - /randombytes/2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - dependencies: - safe-buffer: 5.2.1 - - /randomfill/1.0.4: - resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} - dependencies: - randombytes: 2.1.0 - safe-buffer: 5.2.1 - - /range-parser/1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} - - /raw-body/2.5.1: - resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} - engines: {node: '>= 0.8'} - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - - /raw-body/2.5.2: - resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} - engines: {node: '>= 0.8'} - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - dev: true - - /raw-loader/4.0.2_webpack@4.44.2: - resolution: {integrity: sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - dependencies: - loader-utils: 2.0.4 - schema-utils: 3.1.2 - webpack: 4.44.2 - dev: true - - /rc-config-loader/4.1.2: - resolution: {integrity: sha512-qKTnVWFl9OQYKATPzdfaZIbTxcHziQl92zYSxYC6umhOqyAsoj8H8Gq/+aFjAso68sBdjTz3A7omqeAkkF1MWg==} - dependencies: - debug: 4.3.4 - js-yaml: 4.1.0 - json5: 2.2.3 - require-from-string: 2.0.2 - transitivePeerDependencies: - - supports-color - dev: false - - /rc/1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true - dependencies: - deep-extend: 0.6.0 - ini: 1.3.8 - minimist: 1.2.8 - strip-json-comments: 2.0.1 - - /react-colorful/5.6.1_5owmthsvj5ictknaj3ev736ofq: - resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - dependencies: - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - dev: true - - /react-docgen-typescript/2.2.2_typescript@5.0.4: - resolution: {integrity: sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==} - peerDependencies: - typescript: '>= 4.3.x' - dependencies: - typescript: 5.0.4 - dev: true - - /react-docgen/5.4.3: - resolution: {integrity: sha512-xlLJyOlnfr8lLEEeaDZ+X2J/KJoe6Nr9AzxnkdQWush5hz2ZSu66w6iLMOScMmxoSHWpWMn+k3v5ZiyCfcWsOA==} - engines: {node: '>=8.10.0'} - hasBin: true - dependencies: - '@babel/core': 7.20.12 - '@babel/generator': 7.21.3 - '@babel/runtime': 7.21.0 - ast-types: 0.14.2 - commander: 2.20.3 - doctrine: 3.0.0 - estree-to-babel: 3.2.1 - neo-async: 2.6.2 - node-dir: 0.1.17 - strip-indent: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /react-dom/16.13.1_react@16.13.1: - resolution: {integrity: sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==} - peerDependencies: - react: ^16.13.1 - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - prop-types: 15.8.1 - react: 16.13.1 - scheduler: 0.19.1 - - /react-draggable/4.4.5_5owmthsvj5ictknaj3ev736ofq: - resolution: {integrity: sha512-OMHzJdyJbYTZo4uQE393fHcqqPYsEtkjfMgvCHr6rejT+Ezn4OZbNyGH50vv+SunC1RMvwOTSWkEODQLzw1M9g==} - peerDependencies: - react: '>= 16.3.0' - react-dom: '>= 16.3.0' - dependencies: - clsx: 1.2.1 - prop-types: 15.8.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - dev: true - - /react-element-to-jsx-string/14.3.4_5owmthsvj5ictknaj3ev736ofq: - resolution: {integrity: sha512-t4ZwvV6vwNxzujDQ+37bspnLwA4JlgUPWhLjBJWsNIDceAf6ZKUTCjdm08cN6WeZ5pTMKiCJkmAYnpmR4Bm+dg==} - peerDependencies: - react: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 - react-dom: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 - dependencies: - '@base2/pretty-print-object': 1.0.1 - is-plain-object: 5.0.0 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - react-is: 17.0.2 - dev: true - - /react-fast-compare/3.2.1: - resolution: {integrity: sha512-xTYf9zFim2pEif/Fw16dBiXpe0hoy5PxcD8+OwBnTtNLfIm3g6WxhKNurY+6OmdH1u6Ta/W/Vl6vjbYP1MFnDg==} - dev: true - - /react-helmet-async/1.3.0_5owmthsvj5ictknaj3ev736ofq: - resolution: {integrity: sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==} - peerDependencies: - react: ^16.6.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@babel/runtime': 7.21.0 - invariant: 2.2.4 - prop-types: 15.8.1 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - react-fast-compare: 3.2.1 - shallowequal: 1.1.0 - dev: true - - /react-inspector/5.1.1_react@16.13.1: - resolution: {integrity: sha512-GURDaYzoLbW8pMGXwYPDBIv6nqei4kK7LPRZ9q9HCZF54wqXz/dnylBp/kfE9XmekBhHvLDdcYeyIwSrvtOiWg==} - peerDependencies: - react: ^16.8.4 || ^17.0.0 - dependencies: - '@babel/runtime': 7.21.0 - is-dom: 1.1.0 - prop-types: 15.8.1 - react: 16.13.1 - dev: true - - /react-is/16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - - /react-is/17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - dev: true - - /react-is/18.2.0: - resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} - - /react-popper-tooltip/3.1.1_5owmthsvj5ictknaj3ev736ofq: - resolution: {integrity: sha512-EnERAnnKRptQBJyaee5GJScWNUKQPDD2ywvzZyUjst/wj5U64C8/CnSYLNEmP2hG0IJ3ZhtDxE8oDN+KOyavXQ==} - peerDependencies: - react: ^16.6.0 || ^17.0.0 - react-dom: ^16.6.0 || ^17.0.0 - dependencies: - '@babel/runtime': 7.21.0 - '@popperjs/core': 2.11.7 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - react-popper: 2.3.0_23cw4gxxxjiblpsmqaotin7ivi - dev: true - - /react-popper/2.3.0_23cw4gxxxjiblpsmqaotin7ivi: - resolution: {integrity: sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==} - peerDependencies: - '@popperjs/core': ^2.0.0 - react: ^16.8.0 || ^17 || ^18 - react-dom: ^16.8.0 || ^17 || ^18 - dependencies: - '@popperjs/core': 2.11.7 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - react-fast-compare: 3.2.1 - warning: 4.0.3 - dev: true - - /react-redux/8.0.5_mq2cyprinb6qi7hdzoedcdddgq: - resolution: {integrity: sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==} - peerDependencies: - '@types/react': ^16.8 || ^17.0 || ^18.0 - '@types/react-dom': ^16.8 || ^17.0 || ^18.0 - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - react-native: '>=0.59' - redux: ^4 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - react-dom: - optional: true - react-native: - optional: true - redux: - optional: true - dependencies: - '@babel/runtime': 7.21.0 - '@types/hoist-non-react-statics': 3.3.1 - '@types/react': 16.14.23 - '@types/react-dom': 16.9.14 - '@types/use-sync-external-store': 0.0.3 - hoist-non-react-statics: 3.3.2 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - react-is: 18.2.0 - redux: 4.2.1 - use-sync-external-store: 1.2.0_react@16.13.1 - dev: false - - /react-refresh/0.11.0: - resolution: {integrity: sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==} - engines: {node: '>=0.10.0'} - dev: true - - /react-router-dom/6.9.0_e4p5kqppx5gth2ijr2xdvk24ma: - resolution: {integrity: sha512-/seUAPY01VAuwkGyVBPCn1OXfVbaWGGu4QN9uj0kCPcTyNYgL1ldZpxZUpRU7BLheKQI4Twtl/OW2nHRF1u26Q==} - engines: {node: '>=14'} - peerDependencies: - '@types/react': '>=16' - react: '>=16.8' - react-dom: '>=16.8' - dependencies: - '@remix-run/router': 1.4.0 - '@types/react': 16.14.23 - react: 16.13.1 - react-dom: 16.13.1_react@16.13.1 - react-router: 6.9.0_qjwx5m6wssz3lnb35xwkc3pz6q - dev: true - - /react-router/6.9.0_qjwx5m6wssz3lnb35xwkc3pz6q: - resolution: {integrity: sha512-51lKevGNUHrt6kLuX3e/ihrXoXCa9ixY/nVWRLlob4r/l0f45x3SzBvYJe3ctleLUQQ5fVa4RGgJOTH7D9Umhw==} - engines: {node: '>=14'} - peerDependencies: - '@types/react': '>=16' - react: '>=16.8' - dependencies: - '@remix-run/router': 1.4.0 - '@types/react': 16.14.23 - react: 16.13.1 - dev: true - - /react-sizeme/3.0.2: - resolution: {integrity: sha512-xOIAOqqSSmKlKFJLO3inBQBdymzDuXx4iuwkNcJmC96jeiOg5ojByvL+g3MW9LPEsojLbC6pf68zOfobK8IPlw==} - dependencies: - element-resize-detector: 1.2.4 - invariant: 2.2.4 - shallowequal: 1.1.0 - throttle-debounce: 3.0.1 - dev: true - - /react-syntax-highlighter/13.5.3_react@16.13.1: - resolution: {integrity: sha512-crPaF+QGPeHNIblxxCdf2Lg936NAHKhNhuMzRL3F9ct6aYXL3NcZtCL0Rms9+qVo6Y1EQLdXGypBNSbPL/r+qg==} - peerDependencies: - react: '>= 0.14.0' - dependencies: - '@babel/runtime': 7.21.0 - highlight.js: 10.7.3 - lowlight: 1.20.0 - prismjs: 1.29.0 - react: 16.13.1 - refractor: 3.6.0 - dev: true - - /react-textarea-autosize/8.4.1_qjwx5m6wssz3lnb35xwkc3pz6q: - resolution: {integrity: sha512-aD2C+qK6QypknC+lCMzteOdIjoMbNlgSFmJjCV+DrfTPwp59i/it9mMNf2HDzvRjQgKAyBDPyLJhcrzElf2U4Q==} - engines: {node: '>=10'} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@babel/runtime': 7.21.0 - react: 16.13.1 - use-composed-ref: 1.3.0_react@16.13.1 - use-latest: 1.2.1_qjwx5m6wssz3lnb35xwkc3pz6q - transitivePeerDependencies: - - '@types/react' - dev: true - - /react/16.13.1: - resolution: {integrity: sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==} - engines: {node: '>=0.10.0'} - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - prop-types: 15.8.1 - - /read-package-json/2.1.2: - resolution: {integrity: sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==} - dependencies: - glob: 7.2.3 - json-parse-even-better-errors: 2.3.1 - normalize-package-data: 2.5.0 - npm-normalize-package-bin: 1.0.1 - dev: false - - /read-package-tree/5.1.6: - resolution: {integrity: sha512-FCX1aT3GWyY658wzDICef4p+n0dB+ENRct8E/Qyvppj6xVpOYerBHfUu7OP5Rt1/393Tdglguf5ju5DEX4wZNg==} - deprecated: The functionality that this package provided is now in @npmcli/arborist - dependencies: - debuglog: 1.0.1 - dezalgo: 1.0.4 - once: 1.4.0 - read-package-json: 2.1.2 - readdir-scoped-modules: 1.1.0 - dev: false - - /read-pkg-up/7.0.1: - resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} - engines: {node: '>=8'} - dependencies: - find-up: 4.1.0 - read-pkg: 5.2.0 - type-fest: 0.8.1 - - /read-pkg/5.2.0: - resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} - engines: {node: '>=8'} - dependencies: - '@types/normalize-package-data': 2.4.1 - normalize-package-data: 2.5.0 - parse-json: 5.2.0 - type-fest: 0.6.0 - - /read-yaml-file/2.1.0: - resolution: {integrity: sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==} - engines: {node: '>=10.13'} - dependencies: - js-yaml: 4.1.0 - strip-bom: 4.0.0 - dev: false - - /read/1.0.7: - resolution: {integrity: sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==} - engines: {node: '>=0.8'} - dependencies: - mute-stream: 0.0.8 - - /readable-stream/1.1.14: - resolution: {integrity: sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==} - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 0.0.1 - string_decoder: 0.10.31 - dev: true - - /readable-stream/2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - - /readable-stream/3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - - /readdir-glob/1.1.2: - resolution: {integrity: sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==} - dependencies: - minimatch: 5.1.6 - dev: true - - /readdir-scoped-modules/1.1.0: - resolution: {integrity: sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==} - deprecated: This functionality has been moved to @npmcli/fs - dependencies: - debuglog: 1.0.1 - dezalgo: 1.0.4 - graceful-fs: 4.2.11 - once: 1.4.0 - dev: false - - /readdirp/2.2.1: - resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==} - engines: {node: '>=0.10'} - dependencies: - graceful-fs: 4.2.11 - micromatch: 3.1.10 - readable-stream: 2.3.8 - optional: true - - /readdirp/3.5.0: - resolution: {integrity: sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==} - engines: {node: '>=8.10.0'} - dependencies: - picomatch: 2.3.1 - - /readdirp/3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - dependencies: - picomatch: 2.3.1 - - /recast/0.19.1: - resolution: {integrity: sha512-8FCjrBxjeEU2O6I+2hyHyBFH1siJbMBLwIRvVr1T3FD2cL754sOaJDsJ/8h3xYltasbJ8jqWRIhMuDGBSiSbjw==} - engines: {node: '>= 4'} - dependencies: - ast-types: 0.13.3 - esprima: 4.0.1 - private: 0.1.8 - source-map: 0.6.1 - dev: true - - /recast/0.20.5: - resolution: {integrity: sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ==} - engines: {node: '>= 4'} - dependencies: - ast-types: 0.14.2 - esprima: 4.0.1 - source-map: 0.6.1 - tslib: 2.3.1 - dev: true - - /rechoir/0.6.2: - resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} - engines: {node: '>= 0.10'} - dependencies: - resolve: 1.22.1 - dev: true - - /redent/3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} - dependencies: - indent-string: 4.0.0 - strip-indent: 3.0.0 - dev: false - - /redux-thunk/2.4.2_redux@4.2.1: - resolution: {integrity: sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==} - peerDependencies: - redux: ^4 - dependencies: - redux: 4.2.1 - dev: false - - /redux/4.2.1: - resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} - dependencies: - '@babel/runtime': 7.21.0 - dev: false - - /refractor/3.6.0: - resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} - dependencies: - hastscript: 6.0.0 - parse-entities: 2.0.0 - prismjs: 1.27.0 - dev: true - - /regenerate-unicode-properties/10.1.0: - resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==} - engines: {node: '>=4'} - dependencies: - regenerate: 1.4.2 - dev: true - - /regenerate/1.4.2: - resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} - dev: true - - /regenerator-runtime/0.13.11: - resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} - - /regenerator-transform/0.15.1: - resolution: {integrity: sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==} - dependencies: - '@babel/runtime': 7.21.0 - dev: true - - /regex-not/1.0.2: - resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==} - engines: {node: '>=0.10.0'} - dependencies: - extend-shallow: 3.0.2 - safe-regex: 1.1.0 - - /regexp.prototype.flags/1.4.3: - resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - functions-have-names: 1.2.3 - - /regexpp/3.2.0: - resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} - engines: {node: '>=8'} - - /regexpu-core/5.3.2: - resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} - engines: {node: '>=4'} - dependencies: - '@babel/regjsgen': 0.8.0 - regenerate: 1.4.2 - regenerate-unicode-properties: 10.1.0 - regjsparser: 0.9.1 - unicode-match-property-ecmascript: 2.0.0 - unicode-match-property-value-ecmascript: 2.1.0 - dev: true - - /registry-auth-token/4.2.2: - resolution: {integrity: sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==} - engines: {node: '>=6.0.0'} - dependencies: - rc: 1.2.8 - - /registry-url/5.1.0: - resolution: {integrity: sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==} - engines: {node: '>=8'} - dependencies: - rc: 1.2.8 - - /regjsparser/0.9.1: - resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} - hasBin: true - dependencies: - jsesc: 0.5.0 - dev: true - - /relateurl/0.2.7: - resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} - engines: {node: '>= 0.10'} - - /remark-external-links/8.0.0: - resolution: {integrity: sha512-5vPSX0kHoSsqtdftSHhIYofVINC8qmp0nctkeU9YoJwV3YfiBRiI6cbFRJ0oI/1F9xS+bopXG0m2KS8VFscuKA==} - dependencies: - extend: 3.0.2 - is-absolute-url: 3.0.3 - mdast-util-definitions: 4.0.0 - space-separated-tokens: 1.1.5 - unist-util-visit: 2.0.3 - dev: true - - /remark-footnotes/2.0.0: - resolution: {integrity: sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==} - dev: true - - /remark-mdx/1.6.22: - resolution: {integrity: sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==} - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.10.4 - '@babel/plugin-proposal-object-rest-spread': 7.12.1_@babel+core@7.12.9 - '@babel/plugin-syntax-jsx': 7.12.1_@babel+core@7.12.9 - '@mdx-js/util': 1.6.22 - is-alphabetical: 1.0.4 - remark-parse: 8.0.3 - unified: 9.2.0 - transitivePeerDependencies: - - supports-color - dev: true - - /remark-parse/8.0.3: - resolution: {integrity: sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==} - dependencies: - ccount: 1.1.0 - collapse-white-space: 1.0.6 - is-alphabetical: 1.0.4 - is-decimal: 1.0.4 - is-whitespace-character: 1.0.4 - is-word-character: 1.0.4 - markdown-escapes: 1.0.4 - parse-entities: 2.0.0 - repeat-string: 1.6.1 - state-toggle: 1.0.3 - trim: 0.0.1 - trim-trailing-lines: 1.1.4 - unherit: 1.1.3 - unist-util-remove-position: 2.0.1 - vfile-location: 3.2.0 - xtend: 4.0.2 - dev: true - - /remark-slug/6.1.0: - resolution: {integrity: sha512-oGCxDF9deA8phWvxFuyr3oSJsdyUAxMFbA0mZ7Y1Sas+emILtO+e5WutF9564gDsEN4IXaQXm5pFo6MLH+YmwQ==} - dependencies: - github-slugger: 1.5.0 - mdast-util-to-string: 1.1.0 - unist-util-visit: 2.0.3 - dev: true - - /remark-squeeze-paragraphs/4.0.0: - resolution: {integrity: sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==} - dependencies: - mdast-squeeze-paragraphs: 4.0.0 - dev: true - - /remeda/0.0.32: - resolution: {integrity: sha512-FEdl8ONpqY7AvvMHG5WYdomc0mGf2khHPUDu6QvNkOq4Wjkw5BvzWM4QyksAQ/US1sFIIRG8TVBn6iJx6HbRrA==} - dev: true - - /remove-trailing-separator/1.1.0: - resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} - - /renderkid/2.0.7: - resolution: {integrity: sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==} - dependencies: - css-select: 4.3.0 - dom-converter: 0.2.0 - htmlparser2: 6.1.0 - lodash: 4.17.21 - strip-ansi: 3.0.1 - - /renderkid/3.0.0: - resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==} - dependencies: - css-select: 4.3.0 - dom-converter: 0.2.0 - htmlparser2: 6.1.0 - lodash: 4.17.21 - strip-ansi: 6.0.1 - - /repeat-element/1.1.4: - resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==} - engines: {node: '>=0.10.0'} - - /repeat-string/1.6.1: - resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} - engines: {node: '>=0.10'} - - /require-directory/2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - /require-from-string/2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - - /require-main-filename/2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - - /require-package-name/2.0.1: - resolution: {integrity: sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==} - dev: false - - /requires-port/1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - - /reselect/4.1.7: - resolution: {integrity: sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==} - dev: false - - /resolve-alpn/1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - - /resolve-cwd/2.0.0: - resolution: {integrity: sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==} - engines: {node: '>=4'} - dependencies: - resolve-from: 3.0.0 - dev: false - - /resolve-cwd/3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - dependencies: - resolve-from: 5.0.0 - dev: true - - /resolve-dir/1.0.1: - resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==} - engines: {node: '>=0.10.0'} - dependencies: - expand-tilde: 2.0.2 - global-modules: 1.0.0 - dev: false - - /resolve-from/3.0.0: - resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==} - engines: {node: '>=4'} - dev: false - - /resolve-from/4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - /resolve-from/5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - /resolve-url/0.2.1: - resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} - deprecated: https://github.com/lydell/resolve-url#deprecated - - /resolve.exports/2.0.2: - resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} - engines: {node: '>=10'} - - /resolve/1.19.0: - resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} - dependencies: - is-core-module: 2.11.0 - path-parse: 1.0.7 - - /resolve/1.22.1: - resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} - hasBin: true - dependencies: - is-core-module: 2.11.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - /resolve/2.0.0-next.4: - resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} - hasBin: true - dependencies: - is-core-module: 2.11.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - /responselike/2.0.1: - resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} - dependencies: - lowercase-keys: 2.0.0 - - /restore-cursor/3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - dev: false - - /ret/0.1.15: - resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} - engines: {node: '>=0.12'} - - /ret/0.2.2: - resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} - engines: {node: '>=4'} - dev: false - - /retry/0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - dev: true - - /retry/0.13.1: - resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} - engines: {node: '>= 4'} - - /reusify/1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - /rfc4648/1.5.2: - resolution: {integrity: sha512-tLOizhR6YGovrEBLatX1sdcuhoSCXddw3mqNVAcKxGJ+J0hFeJ+SjeWCv5UPA/WU3YzWPPuCVYgXBKZUPGpKtg==} - dev: false - - /rfdc/1.3.0: - resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} - - /rimraf/2.6.3: - resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} - hasBin: true - dependencies: - glob: 7.2.3 - dev: true - - /rimraf/2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - hasBin: true - dependencies: - glob: 7.2.3 - - /rimraf/3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - hasBin: true - dependencies: - glob: 7.2.3 - - /ripemd160/2.0.2: - resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} - dependencies: - hash-base: 3.1.0 - inherits: 2.0.4 - - /rsvp/4.8.5: - resolution: {integrity: sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==} - engines: {node: 6.* || >= 7.*} - dev: true - - /run-async/2.4.1: - resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} - engines: {node: '>=0.12.0'} - dev: false - - /run-parallel/1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - dependencies: - queue-microtask: 1.2.3 - - /run-queue/1.0.3: - resolution: {integrity: sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==} - dependencies: - aproba: 1.2.0 - - /rxjs/6.6.7: - resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} - engines: {npm: '>=2.0.0'} - dependencies: - tslib: 1.14.1 - - /rxjs/7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} - dependencies: - tslib: 2.3.1 - dev: false - - /safe-buffer/5.1.1: - resolution: {integrity: sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==} - dev: true - - /safe-buffer/5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - - /safe-buffer/5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - /safe-regex-test/1.0.0: - resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 - is-regex: 1.1.4 - - /safe-regex/1.1.0: - resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} - dependencies: - ret: 0.1.15 - - /safe-regex2/2.0.0: - resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} - dependencies: - ret: 0.2.2 - dev: false - - /safer-buffer/2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - /sane/4.1.0: - resolution: {integrity: sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==} - engines: {node: 6.* || 8.* || >= 10.*} - deprecated: some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added - hasBin: true - dependencies: - '@cnakazawa/watch': 1.0.4 - anymatch: 2.0.0 - capture-exit: 2.0.0 - exec-sh: 0.3.6 - execa: 1.0.0 - fb-watchman: 2.0.2 - micromatch: 3.1.10 - minimist: 1.2.8 - walker: 1.0.8 - dev: true - - /sass-embedded-darwin-arm64/1.62.0: - resolution: {integrity: sha512-bYEM6DY7kteOd/aJXUisiavm8B1acRhpIn+rhzKZeTn87kUW5RzZv2nKaSmb1vUd4ZptDGaJ144qz/d20rnogQ==} - engines: {node: '>=14.0.0'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - - /sass-embedded-darwin-x64/1.62.0: - resolution: {integrity: sha512-2sBQ4uWjZbf8TKXF8Aq7N0p5V2tKUr4zX9gQAiKvm1NBYwsW22+m8D34heOWu50ikpIxebvt7i/z7hafH5kzKg==} - engines: {node: '>=14.0.0'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - - /sass-embedded-linux-arm/1.62.0: - resolution: {integrity: sha512-0lz9Ids/OzKiOK+fd5wo/fHBGJ5lCHbcRsjDnU0CIMWkUmMt7yhcFABWB/TUofS5XvrohYbGqs+yKP3X0oGX3g==} - engines: {node: '>=14.0.0'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /sass-embedded-linux-arm64/1.62.0: - resolution: {integrity: sha512-FexUt8aE7I7fJub3N6+NsDdbPRP/O8o400qpbEbY7BWgiWEdpr81OBulQZY/2LzZUnz9keUhfpmltNY3SNg3kg==} - engines: {node: '>=14.0.0'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /sass-embedded-linux-ia32/1.62.0: - resolution: {integrity: sha512-VpDHtMIwcoWqDsiskjhDYAle0SJV4mUiZJTXg5RkMzoX1ZyNiVz+uNaZ88kDqcGXsWpe2i0sIlljD4ryaiMAhA==} - engines: {node: '>=14.0.0'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /sass-embedded-linux-x64/1.62.0: - resolution: {integrity: sha512-dntYMsu0QonlerFB8VDlzxoJcpMEtN9lPHstKOQ6rk6hbSFPvcI8MqqUomlOjmpakKeVrpyZ04nm9jHrzlFmYg==} - engines: {node: '>=14.0.0'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /sass-embedded-win32-ia32/1.62.0: - resolution: {integrity: sha512-rTCZCVkQa6XcreyQ8gYqnsEG13HCzqKoN2mCvIuGwJro8IjyT2PzWauouO0M06T0FLH0pc3EvKdKaLdtijf9AQ==} - engines: {node: '>=14.0.0'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: false - optional: true - - /sass-embedded-win32-x64/1.62.0: - resolution: {integrity: sha512-g6DZBPGfIDKLBarvYRVKJ+7rJAHJXkOQQVrYSWm22klA9ZNZ0CaVyqLqejttZPKGreD8h/xh2uz/s6w/P900Sw==} - engines: {node: '>=14.0.0'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: false - optional: true - - /sass-embedded/1.62.0: - resolution: {integrity: sha512-SwTIG6UmrMiT94/v8G+2pPf6i+XwY4hOQxm8HZl0ld0st2KdGDj/SBXDznFl7+sJ6tFq6hvVvrB9rW5Nj7EhuQ==} - engines: {node: '>=14.0.0'} - dependencies: - '@bufbuild/protobuf': 1.2.1 - buffer-builder: 0.2.0 - immutable: 4.3.0 - rxjs: 7.8.1 - supports-color: 8.1.1 - optionalDependencies: - sass-embedded-darwin-arm64: 1.62.0 - sass-embedded-darwin-x64: 1.62.0 - sass-embedded-linux-arm: 1.62.0 - sass-embedded-linux-arm64: 1.62.0 - sass-embedded-linux-ia32: 1.62.0 - sass-embedded-linux-x64: 1.62.0 - sass-embedded-win32-ia32: 1.62.0 - sass-embedded-win32-x64: 1.62.0 - dev: false - - /sass-loader/10.0.5_sass@1.3.2+webpack@4.44.2: - resolution: {integrity: sha512-2LqoNPtKkZq/XbXNQ4C64GFEleSEHKv6NPSI+bMC/l+jpEXGJhiRYkAQToO24MR7NU4JRY2RpLpJ/gjo2Uf13w==} - engines: {node: '>= 10.13.0'} - peerDependencies: - fibers: '>= 3.1.0' - node-sass: ^4.0.0 || ^5.0.0 - sass: ^1.3.0 - webpack: ^4.36.0 || ^5.0.0 - peerDependenciesMeta: - fibers: - optional: true - node-sass: - optional: true - sass: - optional: true - dependencies: - klona: 2.0.6 - loader-utils: 2.0.4 - neo-async: 2.6.2 - sass: 1.3.2 - schema-utils: 3.1.1 - semver: 7.3.8 - webpack: 4.44.2 - dev: true - - /sass-loader/12.4.0_eyzo4krppp6jzbzhebz7eme2km: - resolution: {integrity: sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==} - engines: {node: '>= 12.13.0'} - peerDependencies: - fibers: '>= 3.1.0' - node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - sass: ^1.3.0 - webpack: ^5.0.0 - peerDependenciesMeta: - fibers: - optional: true - node-sass: - optional: true - sass: - optional: true - dependencies: - klona: 2.0.6 - neo-async: 2.6.2 - sass: 1.49.11 - webpack: 5.80.0 - dev: false - - /sass/1.3.2: - resolution: {integrity: sha512-1dBIuVtEc5lcgHaEUY8FE50YlTZB59pyodpaVoPkBppxm9JcE6X2u+IcVitMxoQnvJvpjk8esR7UlnbNmFTH+Q==} - engines: {node: '>=0.11.8'} - hasBin: true - dev: true - - /sass/1.49.11: - resolution: {integrity: sha512-wvS/geXgHUGs6A/4ud5BFIWKO1nKd7wYIGimDk4q4GFkJicILActpv9ueMT4eRGSsp1BdKHuw1WwAHXbhsJELQ==} - engines: {node: '>=12.0.0'} - hasBin: true - dependencies: - chokidar: 3.4.3 - immutable: 4.3.0 - source-map-js: 1.0.2 - dev: false - - /sax/1.2.1: - resolution: {integrity: sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==} - dev: true - - /sax/1.2.4: - resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} - dev: false - - /saxes/6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} - engines: {node: '>=v12.22.7'} - dependencies: - xmlchars: 2.2.0 - - /scheduler/0.19.1: - resolution: {integrity: sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==} - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - - /schema-utils/1.0.0: - resolution: {integrity: sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==} - engines: {node: '>= 4'} - dependencies: - ajv: 6.12.6 - ajv-errors: 1.0.1_ajv@6.12.6 - ajv-keywords: 3.5.2_ajv@6.12.6 - - /schema-utils/2.7.0: - resolution: {integrity: sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==} - engines: {node: '>= 8.9.0'} - dependencies: - '@types/json-schema': 7.0.11 - ajv: 6.12.6 - ajv-keywords: 3.5.2_ajv@6.12.6 - dev: true - - /schema-utils/2.7.1: - resolution: {integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==} - engines: {node: '>= 8.9.0'} - dependencies: - '@types/json-schema': 7.0.11 - ajv: 6.12.6 - ajv-keywords: 3.5.2_ajv@6.12.6 - dev: true - - /schema-utils/3.1.1: - resolution: {integrity: sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==} - engines: {node: '>= 10.13.0'} - dependencies: - '@types/json-schema': 7.0.11 - ajv: 6.12.6 - ajv-keywords: 3.5.2_ajv@6.12.6 - - /schema-utils/3.1.2: - resolution: {integrity: sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==} - engines: {node: '>= 10.13.0'} - dependencies: - '@types/json-schema': 7.0.11 - ajv: 6.12.6 - ajv-keywords: 3.5.2_ajv@6.12.6 - - /schema-utils/4.0.0: - resolution: {integrity: sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==} - engines: {node: '>= 12.13.0'} - dependencies: - '@types/json-schema': 7.0.11 - ajv: 8.12.0 - ajv-formats: 2.1.1 - ajv-keywords: 5.1.0_ajv@8.12.0 - dev: false - - /scss-parser/1.0.6: - resolution: {integrity: sha512-SH3TaoaJFzfAtqs3eG1j5IuHJkeEW5rKUPIjIN+ZorLAyJLHItQGnsgwHk76v25GtLtpT9IqfAcqK4vFWdiw+w==} - engines: {node: '>=6.0.0'} - dependencies: - invariant: 2.2.4 - lodash: 4.17.21 - dev: false - - /secure-json-parse/2.7.0: - resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} - dev: false - - /select-hose/2.0.0: - resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} - dev: false - - /selfsigned/2.1.1: - resolution: {integrity: sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==} - engines: {node: '>=10'} - dependencies: - node-forge: 1.3.1 - dev: false - - /semver-compare/1.0.0: - resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} - dev: false - - /semver-diff/3.1.1: - resolution: {integrity: sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==} - engines: {node: '>=8'} - dependencies: - semver: 6.3.0 - - /semver-store/0.3.0: - resolution: {integrity: sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg==} - dev: false - - /semver/5.7.1: - resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} - hasBin: true - - /semver/6.3.0: - resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} - hasBin: true - - /semver/7.3.8: - resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - - /send/0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} - engines: {node: '>= 0.8.0'} - dependencies: - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 0.5.2 - http-errors: 2.0.0 - mime: 1.6.0 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.1 - - /serialize-javascript/4.0.0: - resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} - dependencies: - randombytes: 2.1.0 - - /serialize-javascript/5.0.1: - resolution: {integrity: sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==} - dependencies: - randombytes: 2.1.0 - dev: true - - /serialize-javascript/6.0.0: - resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} - dependencies: - randombytes: 2.1.0 - dev: false - - /serialize-javascript/6.0.1: - resolution: {integrity: sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==} - dependencies: - randombytes: 2.1.0 - - /serve-favicon/2.5.0: - resolution: {integrity: sha512-FMW2RvqNr03x+C0WxTyu6sOv21oOjkq5j8tjquWccwa6ScNyGFOGJVpuS1NmTVGBAHS07xnSKotgf2ehQmf9iA==} - engines: {node: '>= 0.8.0'} - dependencies: - etag: 1.8.1 - fresh: 0.5.2 - ms: 2.1.1 - parseurl: 1.3.3 - safe-buffer: 5.1.1 - dev: true - - /serve-index/1.9.1: - resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} - engines: {node: '>= 0.8.0'} - dependencies: - accepts: 1.3.8 - batch: 0.6.1 - debug: 2.6.9 - escape-html: 1.0.3 - http-errors: 1.6.3 - mime-types: 2.1.35 - parseurl: 1.3.3 - dev: false - - /serve-static/1.15.0: - resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} - engines: {node: '>= 0.8.0'} - dependencies: - encodeurl: 1.0.2 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 0.18.0 - - /set-blocking/2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - - /set-cookie-parser/2.6.0: - resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} - dev: false - - /set-immediate-shim/1.0.1: - resolution: {integrity: sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ==} - engines: {node: '>=0.10.0'} - dev: false - - /set-value/2.0.1: - resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==} - engines: {node: '>=0.10.0'} - dependencies: - extend-shallow: 2.0.1 - is-extendable: 0.1.1 - is-plain-object: 2.0.4 - split-string: 3.1.0 - - /setimmediate/1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - - /setprototypeof/1.1.0: - resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} - dev: false - - /setprototypeof/1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - - /sha.js/2.4.11: - resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} - hasBin: true - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - - /shallow-clone/3.0.1: - resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} - engines: {node: '>=8'} - dependencies: - kind-of: 6.0.3 - - /shallowequal/1.1.0: - resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} - dev: true - - /shebang-command/1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} - dependencies: - shebang-regex: 1.0.0 - - /shebang-command/2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - dependencies: - shebang-regex: 3.0.0 - - /shebang-regex/1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - - /shebang-regex/3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - /shelljs/0.8.5: - resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} - engines: {node: '>=4'} - hasBin: true - dependencies: - glob: 7.0.6 - interpret: 1.4.0 - rechoir: 0.6.2 - dev: true - - /side-channel/1.0.4: - resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 - object-inspect: 1.12.3 - - /signal-exit/3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - /sirv/1.0.19: - resolution: {integrity: sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==} - engines: {node: '>= 10'} - dependencies: - '@polka/url': 1.0.0-next.21 - mrmime: 1.0.1 - totalist: 1.1.0 - - /sisteransi/1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - dev: true - - /slash/2.0.0: - resolution: {integrity: sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==} - engines: {node: '>=6'} - dev: true - - /slash/3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - /slice-ansi/4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - dev: true - - /smart-buffer/4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - dev: true - - /snapdragon-node/2.1.1: - resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==} - engines: {node: '>=0.10.0'} - dependencies: - define-property: 1.0.0 - isobject: 3.0.1 - snapdragon-util: 3.0.1 - - /snapdragon-util/3.0.1: - resolution: {integrity: sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==} - engines: {node: '>=0.10.0'} - dependencies: - kind-of: 3.2.2 - - /snapdragon/0.8.2: - resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==} - engines: {node: '>=0.10.0'} - dependencies: - base: 0.11.2 - debug: 2.6.9 - define-property: 0.2.5 - extend-shallow: 2.0.1 - map-cache: 0.2.2 - source-map: 0.5.7 - source-map-resolve: 0.5.3 - use: 3.1.1 - - /sockjs/0.3.24: - resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} - dependencies: - faye-websocket: 0.11.4 - uuid: 8.3.2 - websocket-driver: 0.7.4 - dev: false - - /socks-proxy-agent/5.0.1: - resolution: {integrity: sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==} - engines: {node: '>= 6'} - dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - socks: 2.7.1 - transitivePeerDependencies: - - supports-color - dev: true - - /socks/2.7.1: - resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==} - engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} - dependencies: - ip: 2.0.0 - smart-buffer: 4.2.0 - dev: true - - /sonic-boom/1.4.1: - resolution: {integrity: sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==} - dependencies: - atomic-sleep: 1.0.0 - flatstr: 1.0.12 - dev: false - - /sort-keys/4.2.0: - resolution: {integrity: sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==} - engines: {node: '>=8'} - dependencies: - is-plain-obj: 2.1.0 - dev: false - - /source-list-map/2.0.1: - resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==} - - /source-map-js/1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} - engines: {node: '>=0.10.0'} - - /source-map-loader/1.1.3_webpack@4.44.2: - resolution: {integrity: sha512-6YHeF+XzDOrT/ycFJNI53cgEsp/tHTMl37hi7uVyqFAlTXW109JazaQCkbc+jjoL2637qkH1amLi+JzrIpt5lA==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - dependencies: - abab: 2.0.6 - iconv-lite: 0.6.3 - loader-utils: 2.0.4 - schema-utils: 3.1.1 - source-map: 0.6.1 - webpack: 4.44.2 - whatwg-mimetype: 2.3.0 - dev: true - - /source-map-loader/3.0.2_webpack@5.80.0: - resolution: {integrity: sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^5.0.0 - dependencies: - abab: 2.0.6 - iconv-lite: 0.6.3 - source-map-js: 1.0.2 - webpack: 5.80.0 - - /source-map-resolve/0.5.3: - resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==} - deprecated: See https://github.com/lydell/source-map-resolve#deprecated - dependencies: - atob: 2.1.2 - decode-uri-component: 0.2.2 - resolve-url: 0.2.1 - source-map-url: 0.4.1 - urix: 0.1.0 - - /source-map-support/0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - /source-map-support/0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - /source-map-url/0.4.1: - resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} - deprecated: See https://github.com/lydell/source-map-url#deprecated - - /source-map/0.5.7: - resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} - engines: {node: '>=0.10.0'} - - /source-map/0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - /source-map/0.7.4: - resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} - engines: {node: '>= 8'} - - /sourcemap-codec/1.4.8: - resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} - deprecated: Please use @jridgewell/sourcemap-codec instead - dev: false - - /space-separated-tokens/1.1.5: - resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} - dev: true - - /spdx-correct/3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - dependencies: - spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.13 - - /spdx-exceptions/2.3.0: - resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} - - /spdx-expression-parse/3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - dependencies: - spdx-exceptions: 2.3.0 - spdx-license-ids: 3.0.13 - - /spdx-license-ids/3.0.13: - resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==} - - /spdy-transport/3.0.0: - resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} - dependencies: - debug: 4.3.4 - detect-node: 2.1.0 - hpack.js: 2.1.6 - obuf: 1.1.2 - readable-stream: 3.6.2 - wbuf: 1.7.3 - transitivePeerDependencies: - - supports-color - dev: false - - /spdy/4.0.2: - resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} - engines: {node: '>=6.0.0'} - dependencies: - debug: 4.3.4 - handle-thing: 2.0.1 - http-deceiver: 1.2.7 - select-hose: 2.0.0 - spdy-transport: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: false - - /split-string/3.1.0: - resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} - engines: {node: '>=0.10.0'} - dependencies: - extend-shallow: 3.0.2 - - /split2/3.2.2: - resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} - dependencies: - readable-stream: 3.6.2 - dev: true - - /sprintf-js/1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - /ssri/6.0.2: - resolution: {integrity: sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==} - dependencies: - figgy-pudding: 3.5.2 - - /ssri/8.0.1: - resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - - /stable/0.1.8: - resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} - deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' - - /stack-utils/2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - dependencies: - escape-string-regexp: 2.0.0 - - /stackframe/1.3.4: - resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} - - /state-toggle/1.0.3: - resolution: {integrity: sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==} - dev: true - - /static-extend/0.1.2: - resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} - engines: {node: '>=0.10.0'} - dependencies: - define-property: 0.2.5 - object-copy: 0.1.0 - - /statuses/1.5.0: - resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} - engines: {node: '>= 0.6'} - dev: false - - /statuses/2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - - /stop-iteration-iterator/1.0.0: - resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} - engines: {node: '>= 0.4'} - dependencies: - internal-slot: 1.0.5 - dev: true - - /stoppable/1.1.0: - resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} - engines: {node: '>=4', npm: '>=6'} - dev: false - - /store2/2.14.2: - resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==} - dev: true - - /stream-browserify/2.0.2: - resolution: {integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==} - dependencies: - inherits: 2.0.4 - readable-stream: 2.3.8 - - /stream-each/1.2.3: - resolution: {integrity: sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==} - dependencies: - end-of-stream: 1.4.4 - stream-shift: 1.0.1 - - /stream-http/2.8.3: - resolution: {integrity: sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==} - dependencies: - builtin-status-codes: 3.0.0 - inherits: 2.0.4 - readable-stream: 2.3.8 - to-arraybuffer: 1.0.1 - xtend: 4.0.2 - - /stream-shift/1.0.1: - resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==} - - /streamroller/3.1.5: - resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==} - engines: {node: '>=8.0'} - dependencies: - date-format: 4.0.14 - debug: 4.3.4 - fs-extra: 8.1.0 - transitivePeerDependencies: - - supports-color - dev: true - - /strict-uri-encode/2.0.0: - resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} - engines: {node: '>=4'} - dev: false - - /string-argv/0.3.1: - resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==} - engines: {node: '>=0.6.19'} - - /string-hash/1.1.3: - resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==} - dev: false - - /string-length/4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 - - /string-similarity/4.0.4: - resolution: {integrity: sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==} - dev: false - - /string-width/1.0.2: - resolution: {integrity: sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==} - engines: {node: '>=0.10.0'} - dependencies: - code-point-at: 1.1.0 - is-fullwidth-code-point: 1.0.0 - strip-ansi: 3.0.1 - dev: true - - /string-width/3.1.0: - resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==} - engines: {node: '>=6'} - dependencies: - emoji-regex: 7.0.3 - is-fullwidth-code-point: 2.0.0 - strip-ansi: 5.2.0 - dev: false - - /string-width/4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - /string-width/5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.0.1 - dev: true - - /string.prototype.matchall/4.0.8: - resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - get-intrinsic: 1.2.0 - has-symbols: 1.0.3 - internal-slot: 1.0.5 - regexp.prototype.flags: 1.4.3 - side-channel: 1.0.4 - - /string.prototype.padend/3.1.4: - resolution: {integrity: sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true - - /string.prototype.padstart/3.1.4: - resolution: {integrity: sha512-XqOHj8horGsF+zwxraBvMTkBFM28sS/jHBJajh17JtJKA92qazidiQbLosV4UA18azvLOVKYo/E3g3T9Y5826w==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true - - /string.prototype.trim/1.2.7: - resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - - /string.prototype.trimend/1.0.6: - resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - - /string.prototype.trimstart/1.0.6: - resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - - /string_decoder/0.10.31: - resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} - dev: true - - /string_decoder/1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - dependencies: - safe-buffer: 5.1.2 - - /string_decoder/1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - dependencies: - safe-buffer: 5.2.1 - - /strip-ansi/3.0.1: - resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} - engines: {node: '>=0.10.0'} - dependencies: - ansi-regex: 2.1.1 - - /strip-ansi/5.2.0: - resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} - engines: {node: '>=6'} - dependencies: - ansi-regex: 4.1.1 - dev: false - - /strip-ansi/6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - dependencies: - ansi-regex: 5.0.1 - - /strip-ansi/7.0.1: - resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==} - engines: {node: '>=12'} - dependencies: - ansi-regex: 6.0.1 - dev: true - - /strip-bom/3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - dev: false - - /strip-bom/4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - - /strip-eof/1.0.0: - resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} - engines: {node: '>=0.10.0'} - dev: true - - /strip-final-newline/2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - - /strip-indent/3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} - dependencies: - min-indent: 1.0.1 - - /strip-json-comments/2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - - /strip-json-comments/3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - /style-loader/1.3.0_webpack@4.44.2: - resolution: {integrity: sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q==} - engines: {node: '>= 8.9.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - dependencies: - loader-utils: 2.0.4 - schema-utils: 2.7.1 - webpack: 4.44.2 - dev: true - - /style-loader/2.0.0_webpack@4.44.2: - resolution: {integrity: sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - dependencies: - loader-utils: 2.0.4 - schema-utils: 3.1.1 - webpack: 4.44.2 - dev: true - - /style-loader/3.3.2_webpack@5.80.0: - resolution: {integrity: sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^5.0.0 - dependencies: - webpack: 5.80.0 - - /style-to-object/0.3.0: - resolution: {integrity: sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==} - dependencies: - inline-style-parser: 0.1.1 - dev: true - - /stylehacks/5.1.1_postcss@8.4.21: - resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - browserslist: 4.21.5 - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false - - /sudo/1.0.3: - resolution: {integrity: sha512-3xMsaPg+8Xm+4LQm0b2V+G3lz3YxtDBzlqiU8CXw2AOIIDSvC1kBxIxBjnoCTq8dTTXAy23m58g6mdClUocpmQ==} - engines: {node: '>=0.8'} - dependencies: - inpath: 1.0.2 - pidof: 1.0.2 - read: 1.0.7 - dev: false - - /supports-color/2.0.0: - resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} - engines: {node: '>=0.8.0'} - dev: false - - /supports-color/3.2.3: - resolution: {integrity: sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==} - engines: {node: '>=0.8.0'} - dependencies: - has-flag: 1.0.0 - dev: false - - /supports-color/5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - dependencies: - has-flag: 3.0.0 - - /supports-color/6.1.0: - resolution: {integrity: sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==} - engines: {node: '>=6'} - dependencies: - has-flag: 3.0.0 - dev: false - - /supports-color/7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - dependencies: - has-flag: 4.0.0 - - /supports-color/8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - dependencies: - has-flag: 4.0.0 - - /supports-preserve-symlinks-flag/1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - /svgo/2.8.0: - resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} - engines: {node: '>=10.13.0'} - hasBin: true - dependencies: - '@trysound/sax': 0.2.0 - commander: 7.2.0 - css-select: 4.3.0 - css-tree: 1.1.3 - csso: 4.2.0 - picocolors: 1.0.0 - stable: 0.1.8 - dev: false - - /symbol-tree/3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - - /symbol.prototype.description/1.0.5: - resolution: {integrity: sha512-x738iXRYsrAt9WBhRCVG5BtIC3B7CUkFwbHW2zOvGtwM33s7JjrCDyq8V0zgMYVb5ymsL8+qkzzpANH63CPQaQ==} - engines: {node: '>= 0.11.15'} - dependencies: - call-bind: 1.0.2 - get-symbol-description: 1.0.0 - has-symbols: 1.0.3 - object.getownpropertydescriptors: 2.1.5 - dev: true - - /synchronous-promise/2.0.17: - resolution: {integrity: sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==} - dev: true - - /table/6.8.1: - resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==} - engines: {node: '>=10.0.0'} - dependencies: - ajv: 8.12.0 - lodash.truncate: 4.4.2 - slice-ansi: 4.0.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - dev: true - - /tapable/1.1.3: - resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==} - engines: {node: '>=6'} - - /tapable/2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} - engines: {node: '>=6'} - - /tar-stream/2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} - dependencies: - bl: 4.1.0 - end-of-stream: 1.4.4 - fs-constants: 1.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - dev: true - - /tar/6.1.13: - resolution: {integrity: sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==} - engines: {node: '>=10'} - dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 4.2.5 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 - - /telejson/5.3.3: - resolution: {integrity: sha512-PjqkJZpzEggA9TBpVtJi1LVptP7tYtXB6rEubwlHap76AMjzvOdKX41CxyaW7ahhzDU1aftXnMCx5kAPDZTQBA==} - dependencies: - '@types/is-function': 1.0.1 - global: 4.4.0 - is-function: 1.0.2 - is-regex: 1.1.4 - is-symbol: 1.0.4 - isobject: 4.0.0 - lodash: 4.17.21 - memoizerific: 1.11.3 - dev: true - - /temp/0.8.4: - resolution: {integrity: sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==} - engines: {node: '>=6.0.0'} - dependencies: - rimraf: 2.6.3 - dev: true - - /terser-webpack-plugin/1.4.5_webpack@4.44.2: - resolution: {integrity: sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==} - engines: {node: '>= 6.9.0'} - peerDependencies: - webpack: ^4.0.0 - dependencies: - cacache: 12.0.4 - find-cache-dir: 2.1.0 - is-wsl: 1.1.0 - schema-utils: 1.0.0 - serialize-javascript: 4.0.0 - source-map: 0.6.1 - terser: 4.8.1 - webpack: 4.44.2 - webpack-sources: 1.4.3 - worker-farm: 1.7.0 - - /terser-webpack-plugin/3.0.8_webpack@4.44.2: - resolution: {integrity: sha512-ygwK8TYMRTYtSyLB2Mhnt90guQh989CIq/mL/2apwi6rA15Xys4ydNUiH4ah6EZCfQxSk26ZFQilZ4IQ6IZw6A==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - dependencies: - cacache: 15.3.0 - find-cache-dir: 3.3.2 - jest-worker: 26.6.2 - p-limit: 3.1.0 - schema-utils: 2.7.1 - serialize-javascript: 4.0.0 - source-map: 0.6.1 - terser: 4.8.1 - webpack: 4.44.2 - webpack-sources: 1.4.3 - dev: true - - /terser-webpack-plugin/4.2.3_webpack@4.44.2: - resolution: {integrity: sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - dependencies: - cacache: 15.3.0 - find-cache-dir: 3.3.2 - jest-worker: 26.6.2 - p-limit: 3.1.0 - schema-utils: 3.1.2 - serialize-javascript: 5.0.1 - source-map: 0.6.1 - terser: 5.16.8 - webpack: 4.44.2 - webpack-sources: 1.4.3 - dev: true - - /terser-webpack-plugin/5.3.7_webpack@5.80.0: - resolution: {integrity: sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==} - engines: {node: '>= 10.13.0'} - peerDependencies: - '@swc/core': '*' - esbuild: '*' - uglify-js: '*' - webpack: ^5.1.0 - peerDependenciesMeta: - '@swc/core': - optional: true - esbuild: - optional: true - uglify-js: - optional: true - dependencies: - '@jridgewell/trace-mapping': 0.3.17 - jest-worker: 27.5.1 - schema-utils: 3.1.1 - serialize-javascript: 6.0.1 - terser: 5.16.8 - webpack: 5.80.0 - - /terser/4.8.1: - resolution: {integrity: sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - commander: 2.20.3 - source-map: 0.6.1 - source-map-support: 0.5.21 - - /terser/5.16.8: - resolution: {integrity: sha512-QI5g1E/ef7d+PsDifb+a6nnVgC4F22Bg6T0xrBrz6iloVB4PUkkunp6V8nzoOOZJIzjWVdAGqCdlKlhLq/TbIA==} - engines: {node: '>=10'} - hasBin: true - dependencies: - '@jridgewell/source-map': 0.3.2 - acorn: 8.8.2 - commander: 2.20.3 - source-map-support: 0.5.21 - - /test-exclude/6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 7.2.3 - minimatch: 3.1.2 - - /text-table/0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - - /thenify-all/1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} - dependencies: - thenify: 3.3.1 - dev: false - - /thenify/3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - dependencies: - any-promise: 1.3.0 - dev: false - - /throat/6.0.2: - resolution: {integrity: sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==} - dev: false - - /throttle-debounce/3.0.1: - resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} - engines: {node: '>=10'} - dev: true - - /through/2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: false - - /through2/2.0.5: - resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} - dependencies: - readable-stream: 2.3.8 - xtend: 4.0.2 - - /through2/4.0.2: - resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} - dependencies: - readable-stream: 3.6.2 - dev: true - - /thunky/1.1.0: - resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} - dev: false - - /timers-browserify/2.0.12: - resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} - engines: {node: '>=0.6.0'} - dependencies: - setimmediate: 1.0.5 - - /timers-ext/0.1.7: - resolution: {integrity: sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==} - dependencies: - es5-ext: 0.10.62 - next-tick: 1.1.0 - dev: true - - /tiny-lru/7.0.6: - resolution: {integrity: sha512-zNYO0Kvgn5rXzWpL0y3RS09sMK67eGaQj9805jlK9G6pSadfriTczzLHFXa/xcW4mIRfmlB9HyQ/+SgL0V1uow==} - engines: {node: '>=6'} - dev: false - - /tmp/0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} - dependencies: - os-tmpdir: 1.0.2 - dev: false - - /tmpl/1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - - /to-arraybuffer/1.0.1: - resolution: {integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==} - - /to-fast-properties/2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} - - /to-object-path/0.3.0: - resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==} - engines: {node: '>=0.10.0'} - dependencies: - kind-of: 3.2.2 - - /to-regex-range/2.1.1: - resolution: {integrity: sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==} - engines: {node: '>=0.10.0'} - dependencies: - is-number: 3.0.0 - repeat-string: 1.6.1 - - /to-regex-range/5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - dependencies: - is-number: 7.0.0 - - /to-regex/3.0.2: - resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==} - engines: {node: '>=0.10.0'} - dependencies: - define-property: 2.0.2 - extend-shallow: 3.0.2 - regex-not: 1.0.2 - safe-regex: 1.1.0 - - /toggle-selection/1.0.6: - resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} - dev: true - - /toidentifier/1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - - /totalist/1.1.0: - resolution: {integrity: sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==} - engines: {node: '>=6'} - - /tough-cookie/4.1.2: - resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==} - engines: {node: '>=6'} - dependencies: - psl: 1.9.0 - punycode: 2.3.0 - universalify: 0.2.0 - url-parse: 1.5.10 - - /tr46/0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - - /tr46/3.0.0: - resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} - engines: {node: '>=12'} - dependencies: - punycode: 2.3.0 - - /trim-newlines/3.0.1: - resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} - engines: {node: '>=8'} - dev: false - - /trim-trailing-lines/1.1.4: - resolution: {integrity: sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==} - dev: true - - /trim/0.0.1: - resolution: {integrity: sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==} - deprecated: Use String.prototype.trim() instead - dev: true - - /trough/1.0.5: - resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==} - dev: true - - /true-case-path/2.2.1: - resolution: {integrity: sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q==} - - /ts-dedent/2.2.0: - resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} - engines: {node: '>=6.10'} - dev: true - - /ts-loader/6.0.0_typescript@5.0.4: - resolution: {integrity: sha512-lszy+D41R0Te2+loZxADWS+E1+Z55A+i3dFfFie1AZHL++65JRKVDBPQgeWgRrlv5tbxdU3zOtXp8b7AFR6KEg==} - engines: {node: '>=8.6'} - peerDependencies: - typescript: '*' - dependencies: - chalk: 2.4.2 - enhanced-resolve: 4.5.0 - loader-utils: 1.4.2 - micromatch: 4.0.5 - semver: 6.3.0 - typescript: 5.0.4 - dev: false - - /ts-pnp/1.2.0_typescript@5.0.4: - resolution: {integrity: sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==} - engines: {node: '>=6'} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - typescript: 5.0.4 - dev: true - - /tslib/1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - - /tslib/2.3.1: - resolution: {integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==} - - /tslib/2.4.0: - resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} - dev: true - - /tslib/2.5.0: - resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} - - /tslint-microsoft-contrib/6.2.0_67neen4t5xfhpau25dyc5p2yey: - resolution: {integrity: sha512-6tfi/2tHqV/3CL77pULBcK+foty11Rr0idRDxKnteTaKm6gWF9qmaCNU17HVssOuwlYNyOmd9Jsmjd+1t3a3qw==} - peerDependencies: - tslint: ^5.1.0 - typescript: '*' - dependencies: - tslint: 5.20.1_typescript@3.9.10 - tsutils: 2.28.0_typescript@3.9.10 - typescript: 3.9.10 - dev: true - - /tslint-microsoft-contrib/6.2.0_ew7ikuw7vzbxz2yx5mufkmltai: - resolution: {integrity: sha512-6tfi/2tHqV/3CL77pULBcK+foty11Rr0idRDxKnteTaKm6gWF9qmaCNU17HVssOuwlYNyOmd9Jsmjd+1t3a3qw==} - peerDependencies: - tslint: ^5.1.0 - typescript: '*' - dependencies: - tslint: 5.20.1_typescript@2.9.2 - tsutils: 2.28.0_typescript@2.9.2 - typescript: 2.9.2 - dev: true - - /tslint-microsoft-contrib/6.2.0_iya4g6zcyztd4u7rvedwwipq6a: - resolution: {integrity: sha512-6tfi/2tHqV/3CL77pULBcK+foty11Rr0idRDxKnteTaKm6gWF9qmaCNU17HVssOuwlYNyOmd9Jsmjd+1t3a3qw==} - peerDependencies: - tslint: ^5.1.0 - typescript: '*' - dependencies: - tslint: 5.20.1_typescript@5.0.4 - tsutils: 2.28.0_typescript@5.0.4 - typescript: 5.0.4 - dev: true - - /tslint-microsoft-contrib/6.2.0_uwqr5pcif4g7c56scrk6kqzf7i: - resolution: {integrity: sha512-6tfi/2tHqV/3CL77pULBcK+foty11Rr0idRDxKnteTaKm6gWF9qmaCNU17HVssOuwlYNyOmd9Jsmjd+1t3a3qw==} - peerDependencies: - tslint: ^5.1.0 - typescript: '*' - dependencies: - tslint: 5.20.1_typescript@4.9.5 - tsutils: 2.28.0_typescript@4.9.5 - typescript: 4.9.5 - dev: true - - /tslint/5.20.1_typescript@2.9.2: - resolution: {integrity: sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==} - engines: {node: '>=4.8.0'} - hasBin: true - peerDependencies: - typescript: '>=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev' - dependencies: - '@babel/code-frame': 7.18.6 - builtin-modules: 1.1.1 - chalk: 2.4.2 - commander: 2.20.3 - diff: 4.0.2 - glob: 7.2.3 - js-yaml: 3.13.1 - minimatch: 3.1.2 - mkdirp: 0.5.6 - resolve: 1.22.1 - semver: 5.7.1 - tslib: 1.14.1 - tsutils: 2.29.0_typescript@2.9.2 - typescript: 2.9.2 - dev: true - - /tslint/5.20.1_typescript@3.9.10: - resolution: {integrity: sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==} - engines: {node: '>=4.8.0'} - hasBin: true - peerDependencies: - typescript: '>=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev' - dependencies: - '@babel/code-frame': 7.18.6 - builtin-modules: 1.1.1 - chalk: 2.4.2 - commander: 2.20.3 - diff: 4.0.2 - glob: 7.2.3 - js-yaml: 3.13.1 - minimatch: 3.1.2 - mkdirp: 0.5.6 - resolve: 1.22.1 - semver: 5.7.1 - tslib: 1.14.1 - tsutils: 2.29.0_typescript@3.9.10 - typescript: 3.9.10 - dev: true - - /tslint/5.20.1_typescript@4.9.5: - resolution: {integrity: sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==} - engines: {node: '>=4.8.0'} - hasBin: true - peerDependencies: - typescript: '>=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev' - dependencies: - '@babel/code-frame': 7.18.6 - builtin-modules: 1.1.1 - chalk: 2.4.2 - commander: 2.20.3 - diff: 4.0.2 - glob: 7.2.3 - js-yaml: 3.13.1 - minimatch: 3.1.2 - mkdirp: 0.5.6 - resolve: 1.22.1 - semver: 5.7.1 - tslib: 1.14.1 - tsutils: 2.29.0_typescript@4.9.5 - typescript: 4.9.5 - dev: true - - /tslint/5.20.1_typescript@5.0.4: - resolution: {integrity: sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==} - engines: {node: '>=4.8.0'} - hasBin: true - peerDependencies: - typescript: '>=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev' - dependencies: - '@babel/code-frame': 7.18.6 - builtin-modules: 1.1.1 - chalk: 2.4.2 - commander: 2.20.3 - diff: 4.0.2 - glob: 7.2.3 - js-yaml: 3.13.1 - minimatch: 3.1.2 - mkdirp: 0.5.6 - resolve: 1.22.1 - semver: 5.7.1 - tslib: 1.14.1 - tsutils: 2.29.0_typescript@5.0.4 - typescript: 5.0.4 - dev: true - - /tsutils/2.28.0_typescript@2.9.2: - resolution: {integrity: sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==} - peerDependencies: - typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' - dependencies: - tslib: 1.14.1 - typescript: 2.9.2 - dev: true - - /tsutils/2.28.0_typescript@3.9.10: - resolution: {integrity: sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==} - peerDependencies: - typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' - dependencies: - tslib: 1.14.1 - typescript: 3.9.10 - dev: true - - /tsutils/2.28.0_typescript@4.9.5: - resolution: {integrity: sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==} - peerDependencies: - typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' - dependencies: - tslib: 1.14.1 - typescript: 4.9.5 - dev: true - - /tsutils/2.28.0_typescript@5.0.4: - resolution: {integrity: sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==} - peerDependencies: - typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' - dependencies: - tslib: 1.14.1 - typescript: 5.0.4 - dev: true - - /tsutils/2.29.0_typescript@2.9.2: - resolution: {integrity: sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==} - peerDependencies: - typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' - dependencies: - tslib: 1.14.1 - typescript: 2.9.2 - dev: true - - /tsutils/2.29.0_typescript@3.9.10: - resolution: {integrity: sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==} - peerDependencies: - typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' - dependencies: - tslib: 1.14.1 - typescript: 3.9.10 - dev: true - - /tsutils/2.29.0_typescript@4.9.5: - resolution: {integrity: sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==} - peerDependencies: - typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' - dependencies: - tslib: 1.14.1 - typescript: 4.9.5 - dev: true - - /tsutils/2.29.0_typescript@5.0.4: - resolution: {integrity: sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==} - peerDependencies: - typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' - dependencies: - tslib: 1.14.1 - typescript: 5.0.4 - dev: true - - /tsutils/3.21.0_typescript@5.0.4: - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - dependencies: - tslib: 1.14.1 - typescript: 5.0.4 - - /tty-browserify/0.0.0: - resolution: {integrity: sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==} - - /tunnel/0.0.6: - resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} - engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} - dev: false - - /type-check/0.3.2: - resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.1.2 - - /type-check/0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - - /type-detect/4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - - /type-fest/0.18.1: - resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} - engines: {node: '>=10'} - dev: false - - /type-fest/0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - - /type-fest/0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - /type-fest/0.6.0: - resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} - engines: {node: '>=8'} - - /type-fest/0.8.1: - resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} - engines: {node: '>=8'} - - /type-fest/2.19.0: - resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} - engines: {node: '>=12.20'} - dev: true - - /type-is/1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} - dependencies: - media-typer: 0.3.0 - mime-types: 2.1.35 - - /type/1.2.0: - resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} - dev: true - - /type/2.7.2: - resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} - dev: true - - /typed-array-length/1.0.4: - resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} - dependencies: - call-bind: 1.0.2 - for-each: 0.3.3 - is-typed-array: 1.1.10 - - /typedarray-to-buffer/3.1.5: - resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} - dependencies: - is-typedarray: 1.0.0 - - /typedarray/0.0.6: - resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - - /typescript/2.9.2: - resolution: {integrity: sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: true - - /typescript/3.9.10: - resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: true - - /typescript/4.9.5: - resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: true - - /typescript/5.0.4: - resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} - engines: {node: '>=12.20'} - hasBin: true - - /uglify-js/3.17.4: - resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} - engines: {node: '>=0.8.0'} - hasBin: true - requiresBuild: true - dev: true - optional: true - - /unbox-primitive/1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - dependencies: - call-bind: 1.0.2 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 - - /unfetch/4.2.0: - resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} - dev: true - - /unherit/1.1.3: - resolution: {integrity: sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==} - dependencies: - inherits: 2.0.4 - xtend: 4.0.2 - dev: true - - /unicode-canonical-property-names-ecmascript/2.0.0: - resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} - engines: {node: '>=4'} - dev: true - - /unicode-match-property-ecmascript/2.0.0: - resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} - engines: {node: '>=4'} - dependencies: - unicode-canonical-property-names-ecmascript: 2.0.0 - unicode-property-aliases-ecmascript: 2.1.0 - dev: true - - /unicode-match-property-value-ecmascript/2.1.0: - resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==} - engines: {node: '>=4'} - dev: true - - /unicode-property-aliases-ecmascript/2.1.0: - resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} - engines: {node: '>=4'} - dev: true - - /unified/9.2.0: - resolution: {integrity: sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==} - dependencies: - bail: 1.0.5 - extend: 3.0.2 - is-buffer: 2.0.5 - is-plain-obj: 2.1.0 - trough: 1.0.5 - vfile: 4.2.1 - dev: true - - /union-value/1.0.1: - resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==} - engines: {node: '>=0.10.0'} - dependencies: - arr-union: 3.1.0 - get-value: 2.0.6 - is-extendable: 0.1.1 - set-value: 2.0.1 - - /unique-filename/1.1.1: - resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==} - dependencies: - unique-slug: 2.0.2 - - /unique-slug/2.0.2: - resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==} - dependencies: - imurmurhash: 0.1.4 - - /unique-string/2.0.0: - resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} - engines: {node: '>=8'} - dependencies: - crypto-random-string: 2.0.0 - - /unist-builder/2.0.3: - resolution: {integrity: sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==} - dev: true - - /unist-util-generated/1.1.6: - resolution: {integrity: sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==} - dev: true - - /unist-util-is/4.1.0: - resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==} - dev: true - - /unist-util-position/3.1.0: - resolution: {integrity: sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==} - dev: true - - /unist-util-remove-position/2.0.1: - resolution: {integrity: sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==} - dependencies: - unist-util-visit: 2.0.3 - dev: true - - /unist-util-remove/2.1.0: - resolution: {integrity: sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q==} - dependencies: - unist-util-is: 4.1.0 - dev: true - - /unist-util-stringify-position/2.0.3: - resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} - dependencies: - '@types/unist': 2.0.6 - dev: true - - /unist-util-visit-parents/3.1.1: - resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==} - dependencies: - '@types/unist': 2.0.6 - unist-util-is: 4.1.0 - dev: true - - /unist-util-visit/2.0.3: - resolution: {integrity: sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==} - dependencies: - '@types/unist': 2.0.6 - unist-util-is: 4.1.0 - unist-util-visit-parents: 3.1.1 - dev: true - - /universalify/0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - - /universalify/0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} - engines: {node: '>= 4.0.0'} - - /universalify/2.0.0: - resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} - engines: {node: '>= 10.0.0'} - dev: true - - /unixify/1.0.0: - resolution: {integrity: sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==} - engines: {node: '>=0.10.0'} - dependencies: - normalize-path: 2.1.1 - dev: true - - /unpipe/1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - - /unset-value/1.0.0: - resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==} - engines: {node: '>=0.10.0'} - dependencies: - has-value: 0.3.1 - isobject: 3.0.1 - - /upath/1.2.0: - resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} - engines: {node: '>=4'} - optional: true - - /update-browserslist-db/1.0.10_browserslist@4.21.5: - resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.21.5 - escalade: 3.1.1 - picocolors: 1.0.0 - - /update-notifier/5.1.0: - resolution: {integrity: sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==} - engines: {node: '>=10'} - dependencies: - boxen: 5.1.2 - chalk: 4.1.2 - configstore: 5.0.1 - has-yarn: 2.1.0 - import-lazy: 2.1.0 - is-ci: 2.0.0 - is-installed-globally: 0.4.0 - is-npm: 5.0.0 - is-yarn-global: 0.3.0 - latest-version: 5.1.0 - pupa: 2.1.1 - semver: 7.3.8 - semver-diff: 3.1.1 - xdg-basedir: 4.0.0 - - /uri-js/4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - dependencies: - punycode: 2.3.0 - - /urix/0.1.0: - resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} - deprecated: Please see https://github.com/lydell/urix#deprecated - - /url-loader/4.1.1_webpack@5.80.0: - resolution: {integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==} - engines: {node: '>= 10.13.0'} - peerDependencies: - file-loader: '*' - webpack: ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - file-loader: - optional: true - dependencies: - loader-utils: 2.0.4 - mime-types: 2.1.35 - schema-utils: 3.1.1 - webpack: 5.80.0 - dev: false - - /url-loader/4.1.1_zmzwotvrfu62vdeozbyveyswza: - resolution: {integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==} - engines: {node: '>= 10.13.0'} - peerDependencies: - file-loader: '*' - webpack: ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - file-loader: - optional: true - dependencies: - file-loader: 6.2.0_webpack@4.44.2 - loader-utils: 2.0.4 - mime-types: 2.1.35 - schema-utils: 3.1.1 - webpack: 4.44.2 - dev: true - - /url-parse/1.5.10: - resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - dependencies: - querystringify: 2.2.0 - requires-port: 1.0.0 - - /url/0.10.3: - resolution: {integrity: sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==} - dependencies: - punycode: 1.3.2 - querystring: 0.2.0 - dev: true - - /url/0.11.0: - resolution: {integrity: sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==} - dependencies: - punycode: 1.3.2 - querystring: 0.2.0 - - /use-composed-ref/1.3.0_react@16.13.1: - resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - react: 16.13.1 - dev: true - - /use-isomorphic-layout-effect/1.1.2_qjwx5m6wssz3lnb35xwkc3pz6q: - resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@types/react': 16.14.23 - react: 16.13.1 - dev: true - - /use-latest/1.2.1_qjwx5m6wssz3lnb35xwkc3pz6q: - resolution: {integrity: sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@types/react': 16.14.23 - react: 16.13.1 - use-isomorphic-layout-effect: 1.1.2_qjwx5m6wssz3lnb35xwkc3pz6q - dev: true - - /use-sync-external-store/1.2.0_react@16.13.1: - resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - react: 16.13.1 - dev: false - - /use/3.1.1: - resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} - engines: {node: '>=0.10.0'} - - /util-deprecate/1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - /util.promisify/1.0.0: - resolution: {integrity: sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==} - dependencies: - define-properties: 1.2.0 - object.getownpropertydescriptors: 2.1.5 - - /util/0.10.3: - resolution: {integrity: sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==} - dependencies: - inherits: 2.0.1 - - /util/0.11.1: - resolution: {integrity: sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==} - dependencies: - inherits: 2.0.3 - - /util/0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} - dependencies: - inherits: 2.0.4 - is-arguments: 1.1.1 - is-generator-function: 1.0.10 - is-typed-array: 1.1.10 - which-typed-array: 1.1.9 - dev: true - - /utila/0.4.0: - resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} - - /utils-merge/1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} - engines: {node: '>= 0.4.0'} - - /uuid-browser/3.1.0: - resolution: {integrity: sha512-dsNgbLaTrd6l3MMxTtouOCFw4CBFc/3a+GgYA2YyrJvyQ1u6q4pcu3ktLoUZ/VN/Aw9WsauazbgsgdfVWgAKQg==} - dev: true - - /uuid/3.4.0: - resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} - deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. - hasBin: true - dev: true - - /uuid/8.0.0: - resolution: {integrity: sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==} - hasBin: true - dev: true - - /uuid/8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - - /v8-compile-cache/2.3.0: - resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} - - /v8-to-istanbul/9.1.0: - resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} - engines: {node: '>=10.12.0'} - dependencies: - '@jridgewell/trace-mapping': 0.3.17 - '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.9.0 - - /validate-npm-package-license/3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - dependencies: - spdx-correct: 3.2.0 - spdx-expression-parse: 3.0.1 - - /validate-npm-package-name/3.0.0: - resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} - dependencies: - builtins: 1.0.3 - dev: false - - /validator/13.9.0: - resolution: {integrity: sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==} - engines: {node: '>= 0.10'} - - /value-or-promise/1.0.11: - resolution: {integrity: sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==} - engines: {node: '>=12'} - dev: true - - /vary/1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - - /vfile-location/3.2.0: - resolution: {integrity: sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==} - dev: true - - /vfile-message/2.0.4: - resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==} - dependencies: - '@types/unist': 2.0.6 - unist-util-stringify-position: 2.0.3 - dev: true - - /vfile/4.2.1: - resolution: {integrity: sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==} - dependencies: - '@types/unist': 2.0.6 - is-buffer: 2.0.5 - unist-util-stringify-position: 2.0.3 - vfile-message: 2.0.4 - dev: true - - /vm-browserify/1.1.2: - resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} - - /vm2/3.9.14: - resolution: {integrity: sha512-HgvPHYHeQy8+QhzlFryvSteA4uQLBCOub02mgqdR+0bN/akRZ48TGB1v0aCv7ksyc0HXx16AZtMHKS38alc6TA==} - engines: {node: '>=6.0'} - hasBin: true - dependencies: - acorn: 8.8.2 - acorn-walk: 8.2.0 - dev: true - - /w3c-xmlserializer/4.0.0: - resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} - engines: {node: '>=14'} - dependencies: - xml-name-validator: 4.0.0 - - /walker/1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - dependencies: - makeerror: 1.0.12 - - /warning/4.0.3: - resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} - dependencies: - loose-envify: 1.4.0 - dev: true - - /watchpack-chokidar2/2.0.1: - resolution: {integrity: sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==} - requiresBuild: true - dependencies: - chokidar: 2.1.8 - optional: true - - /watchpack/1.7.5: - resolution: {integrity: sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==} - dependencies: - graceful-fs: 4.2.11 - neo-async: 2.6.2 - optionalDependencies: - chokidar: 3.5.3 - watchpack-chokidar2: 2.0.1 - - /watchpack/2.4.0: - resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} - engines: {node: '>=10.13.0'} - dependencies: - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - - /wbuf/1.7.3: - resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} - dependencies: - minimalistic-assert: 1.0.1 - dev: false - - /wcwidth/1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - dependencies: - defaults: 1.0.4 - dev: false - - /web-namespaces/1.1.4: - resolution: {integrity: sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==} - dev: true - - /webidl-conversions/3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - - /webidl-conversions/7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - - /webpack-bundle-analyzer/4.5.0: - resolution: {integrity: sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==} - engines: {node: '>= 10.13.0'} - hasBin: true - dependencies: - acorn: 8.8.2 - acorn-walk: 8.2.0 - chalk: 4.1.2 - commander: 7.2.0 - gzip-size: 6.0.0 - lodash: 4.17.21 - opener: 1.5.2 - sirv: 1.0.19 - ws: 7.5.9 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - /webpack-cli/3.3.12_webpack@4.44.2: - resolution: {integrity: sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag==} - engines: {node: '>=6.11.5'} - hasBin: true - peerDependencies: - webpack: 4.x.x - dependencies: - chalk: 2.4.2 - cross-spawn: 6.0.5 - enhanced-resolve: 4.5.0 - findup-sync: 3.0.0 - global-modules: 2.0.0 - import-local: 2.0.0 - interpret: 1.4.0 - loader-utils: 1.4.2 - supports-color: 6.1.0 - v8-compile-cache: 2.3.0 - webpack: 4.44.2_webpack-cli@3.3.12 - yargs: 13.3.2 - dev: false - - /webpack-dev-middleware/3.7.3_2jhnw6fokymnjfoumvhvkjoyjq: - resolution: {integrity: sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==} - engines: {node: '>= 6'} - peerDependencies: - '@types/webpack': ^4 - webpack: ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - '@types/webpack': - optional: true - dependencies: - '@types/webpack': 4.41.32 - memory-fs: 0.4.1 - mime: 2.6.0 - mkdirp: 0.5.6 - range-parser: 1.2.1 - webpack: 4.44.2 - webpack-log: 2.0.0 - dev: true - - /webpack-dev-middleware/5.3.3_2jhnw6fokymnjfoumvhvkjoyjq: - resolution: {integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==} - engines: {node: '>= 12.13.0'} - peerDependencies: - '@types/webpack': ^4 - webpack: ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - '@types/webpack': - optional: true - dependencies: - '@types/webpack': 4.41.32 - colorette: 2.0.19 - memfs: 3.4.3 - mime-types: 2.1.35 - range-parser: 1.2.1 - schema-utils: 4.0.0 - webpack: 4.44.2 - dev: false - - /webpack-dev-middleware/5.3.3_webpack@4.44.2: - resolution: {integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==} - engines: {node: '>= 12.13.0'} - peerDependencies: - '@types/webpack': ^4 - webpack: ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - '@types/webpack': - optional: true - dependencies: - colorette: 2.0.19 - memfs: 3.4.3 - mime-types: 2.1.35 - range-parser: 1.2.1 - schema-utils: 4.0.0 - webpack: 4.44.2_webpack-cli@3.3.12 - dev: false - - /webpack-dev-middleware/5.3.3_webpack@5.80.0: - resolution: {integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==} - engines: {node: '>= 12.13.0'} - peerDependencies: - '@types/webpack': ^4 - webpack: ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - '@types/webpack': - optional: true - dependencies: - colorette: 2.0.19 - memfs: 3.4.3 - mime-types: 2.1.35 - range-parser: 1.2.1 - schema-utils: 4.0.0 - webpack: 5.80.0 - dev: false - - /webpack-dev-server/4.9.3_2jhnw6fokymnjfoumvhvkjoyjq: - resolution: {integrity: sha512-3qp/eoboZG5/6QgiZ3llN8TUzkSpYg1Ko9khWX1h40MIEUNS2mDoIa8aXsPfskER+GbTvs/IJZ1QTBBhhuetSw==} - engines: {node: '>= 12.13.0'} - hasBin: true - peerDependencies: - '@types/webpack': ^4 - webpack: ^4.37.0 || ^5.0.0 - webpack-cli: '*' - peerDependenciesMeta: - '@types/webpack': - optional: true - webpack-cli: - optional: true - dependencies: - '@types/bonjour': 3.5.10 - '@types/connect-history-api-fallback': 1.3.5 - '@types/express': 4.17.13 - '@types/express-serve-static-core': 4.17.33 - '@types/serve-index': 1.9.1 - '@types/serve-static': 1.15.1 - '@types/sockjs': 0.3.33 - '@types/webpack': 4.41.32 - '@types/ws': 8.5.4 - ansi-html-community: 0.0.8 - anymatch: 3.1.3 - bonjour-service: 1.1.1 - chokidar: 3.5.3 - colorette: 2.0.19 - compression: 1.7.4 - connect-history-api-fallback: 2.0.0 - default-gateway: 6.0.3 - express: 4.18.1 - graceful-fs: 4.2.11 - html-entities: 2.3.3 - http-proxy-middleware: 2.0.6 - ipaddr.js: 2.0.1 - open: 8.4.2 - p-retry: 4.6.2 - rimraf: 3.0.2 - schema-utils: 4.0.0 - selfsigned: 2.1.1 - serve-index: 1.9.1 - sockjs: 0.3.24 - spdy: 4.0.2 - webpack: 4.44.2 - webpack-dev-middleware: 5.3.3_2jhnw6fokymnjfoumvhvkjoyjq - ws: 8.13.0 - transitivePeerDependencies: - - bufferutil - - debug - - supports-color - - utf-8-validate - dev: false - - /webpack-dev-server/4.9.3_spfcq5ngldu5cvjikbre424ry4: - resolution: {integrity: sha512-3qp/eoboZG5/6QgiZ3llN8TUzkSpYg1Ko9khWX1h40MIEUNS2mDoIa8aXsPfskER+GbTvs/IJZ1QTBBhhuetSw==} - engines: {node: '>= 12.13.0'} - hasBin: true - peerDependencies: - '@types/webpack': ^4 - webpack: ^4.37.0 || ^5.0.0 - webpack-cli: '*' - peerDependenciesMeta: - '@types/webpack': - optional: true - webpack-cli: - optional: true - dependencies: - '@types/bonjour': 3.5.10 - '@types/connect-history-api-fallback': 1.3.5 - '@types/express': 4.17.13 - '@types/express-serve-static-core': 4.17.33 - '@types/serve-index': 1.9.1 - '@types/serve-static': 1.15.1 - '@types/sockjs': 0.3.33 - '@types/ws': 8.5.4 - ansi-html-community: 0.0.8 - anymatch: 3.1.3 - bonjour-service: 1.1.1 - chokidar: 3.5.3 - colorette: 2.0.19 - compression: 1.7.4 - connect-history-api-fallback: 2.0.0 - default-gateway: 6.0.3 - express: 4.18.1 - graceful-fs: 4.2.11 - html-entities: 2.3.3 - http-proxy-middleware: 2.0.6 - ipaddr.js: 2.0.1 - open: 8.4.2 - p-retry: 4.6.2 - rimraf: 3.0.2 - schema-utils: 4.0.0 - selfsigned: 2.1.1 - serve-index: 1.9.1 - sockjs: 0.3.24 - spdy: 4.0.2 - webpack: 4.44.2_webpack-cli@3.3.12 - webpack-cli: 3.3.12_webpack@4.44.2 - webpack-dev-middleware: 5.3.3_webpack@4.44.2 - ws: 8.13.0 - transitivePeerDependencies: - - bufferutil - - debug - - supports-color - - utf-8-validate - dev: false - - /webpack-dev-server/4.9.3_webpack@5.80.0: - resolution: {integrity: sha512-3qp/eoboZG5/6QgiZ3llN8TUzkSpYg1Ko9khWX1h40MIEUNS2mDoIa8aXsPfskER+GbTvs/IJZ1QTBBhhuetSw==} - engines: {node: '>= 12.13.0'} - hasBin: true - peerDependencies: - '@types/webpack': ^4 - webpack: ^4.37.0 || ^5.0.0 - webpack-cli: '*' - peerDependenciesMeta: - '@types/webpack': - optional: true - webpack-cli: - optional: true - dependencies: - '@types/bonjour': 3.5.10 - '@types/connect-history-api-fallback': 1.3.5 - '@types/express': 4.17.13 - '@types/express-serve-static-core': 4.17.33 - '@types/serve-index': 1.9.1 - '@types/serve-static': 1.15.1 - '@types/sockjs': 0.3.33 - '@types/ws': 8.5.4 - ansi-html-community: 0.0.8 - anymatch: 3.1.3 - bonjour-service: 1.1.1 - chokidar: 3.5.3 - colorette: 2.0.19 - compression: 1.7.4 - connect-history-api-fallback: 2.0.0 - default-gateway: 6.0.3 - express: 4.18.1 - graceful-fs: 4.2.11 - html-entities: 2.3.3 - http-proxy-middleware: 2.0.6 - ipaddr.js: 2.0.1 - open: 8.4.2 - p-retry: 4.6.2 - rimraf: 3.0.2 - schema-utils: 4.0.0 - selfsigned: 2.1.1 - serve-index: 1.9.1 - sockjs: 0.3.24 - spdy: 4.0.2 - webpack: 5.80.0 - webpack-dev-middleware: 5.3.3_webpack@5.80.0 - ws: 8.13.0 - transitivePeerDependencies: - - bufferutil - - debug - - supports-color - - utf-8-validate - dev: false - - /webpack-filter-warnings-plugin/1.2.1_webpack@4.44.2: - resolution: {integrity: sha512-Ez6ytc9IseDMLPo0qCuNNYzgtUl8NovOqjIq4uAU8LTD4uoa1w1KpZyyzFtLTEMZpkkOkLfL9eN+KGYdk1Qtwg==} - engines: {node: '>= 4.3 < 5.0.0 || >= 5.10'} - peerDependencies: - webpack: ^2.0.0 || ^3.0.0 || ^4.0.0 - dependencies: - webpack: 4.44.2 - dev: true - - /webpack-hot-middleware/2.25.3: - resolution: {integrity: sha512-IK/0WAHs7MTu1tzLTjio73LjS3Ov+VvBKQmE8WPlJutgG5zT6Urgq/BbAdRrHTRpyzK0dvAvFh1Qg98akxgZpA==} - dependencies: - ansi-html-community: 0.0.8 - html-entities: 2.3.3 - strip-ansi: 6.0.1 - dev: true - - /webpack-log/2.0.0: - resolution: {integrity: sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==} - engines: {node: '>= 6'} - dependencies: - ansi-colors: 3.2.4 - uuid: 3.4.0 - dev: true - - /webpack-merge/5.8.0: - resolution: {integrity: sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==} - engines: {node: '>=10.0.0'} - dependencies: - clone-deep: 4.0.1 - wildcard: 2.0.0 - dev: false - - /webpack-sources/1.4.3: - resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==} - dependencies: - source-list-map: 2.0.1 - source-map: 0.6.1 - - /webpack-sources/3.2.3: - resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} - engines: {node: '>=10.13.0'} - - /webpack-virtual-modules/0.2.2: - resolution: {integrity: sha512-kDUmfm3BZrei0y+1NTHJInejzxfhtU8eDj2M7OKb2IWrPFAeO1SOH2KuQ68MSZu9IGEHcxbkKKR1v18FrUSOmA==} - dependencies: - debug: 3.2.7 - dev: true - - /webpack/4.44.2: - resolution: {integrity: sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==} - engines: {node: '>=6.11.5'} - hasBin: true - peerDependencies: - webpack-cli: '*' - webpack-command: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - webpack-command: - optional: true - dependencies: - '@webassemblyjs/ast': 1.9.0 - '@webassemblyjs/helper-module-context': 1.9.0 - '@webassemblyjs/wasm-edit': 1.9.0 - '@webassemblyjs/wasm-parser': 1.9.0 - acorn: 6.4.2 - ajv: 6.12.6 - ajv-keywords: 3.5.2_ajv@6.12.6 - chrome-trace-event: 1.0.3 - enhanced-resolve: 4.5.0 - eslint-scope: 4.0.3 - json-parse-better-errors: 1.0.2 - loader-runner: 2.4.0 - loader-utils: 1.4.2 - memory-fs: 0.4.1 - micromatch: 3.1.10 - mkdirp: 0.5.6 - neo-async: 2.6.2 - node-libs-browser: 2.2.1 - schema-utils: 1.0.0 - tapable: 1.1.3 - terser-webpack-plugin: 1.4.5_webpack@4.44.2 - watchpack: 1.7.5 - webpack-sources: 1.4.3 - - /webpack/4.44.2_webpack-cli@3.3.12: - resolution: {integrity: sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==} - engines: {node: '>=6.11.5'} - hasBin: true - peerDependencies: - webpack-cli: '*' - webpack-command: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - webpack-command: - optional: true - dependencies: - '@webassemblyjs/ast': 1.9.0 - '@webassemblyjs/helper-module-context': 1.9.0 - '@webassemblyjs/wasm-edit': 1.9.0 - '@webassemblyjs/wasm-parser': 1.9.0 - acorn: 6.4.2 - ajv: 6.12.6 - ajv-keywords: 3.5.2_ajv@6.12.6 - chrome-trace-event: 1.0.3 - enhanced-resolve: 4.5.0 - eslint-scope: 4.0.3 - json-parse-better-errors: 1.0.2 - loader-runner: 2.4.0 - loader-utils: 1.4.2 - memory-fs: 0.4.1 - micromatch: 3.1.10 - mkdirp: 0.5.6 - neo-async: 2.6.2 - node-libs-browser: 2.2.1 - schema-utils: 1.0.0 - tapable: 1.1.3 - terser-webpack-plugin: 1.4.5_webpack@4.44.2 - watchpack: 1.7.5 - webpack-cli: 3.3.12_webpack@4.44.2 - webpack-sources: 1.4.3 - dev: false - - /webpack/5.80.0: - resolution: {integrity: sha512-OIMiq37XK1rWO8mH9ssfFKZsXg4n6klTEDL7S8/HqbAOBBaiy8ABvXvz0dDCXeEF9gqwxSvVk611zFPjS8hJxA==} - engines: {node: '>=10.13.0'} - hasBin: true - peerDependencies: - webpack-cli: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - dependencies: - '@types/eslint-scope': 3.7.4 - '@types/estree': 1.0.1 - '@webassemblyjs/ast': 1.11.5 - '@webassemblyjs/wasm-edit': 1.11.5 - '@webassemblyjs/wasm-parser': 1.11.5 - acorn: 8.8.2 - acorn-import-assertions: 1.8.0_acorn@8.8.2 - browserslist: 4.21.5 - chrome-trace-event: 1.0.3 - enhanced-resolve: 5.13.0 - es-module-lexer: 1.2.1 - eslint-scope: 5.1.1 - events: 3.3.0 - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.0 - mime-types: 2.1.35 - neo-async: 2.6.2 - schema-utils: 3.1.2 - tapable: 2.2.1 - terser-webpack-plugin: 5.3.7_webpack@5.80.0 - watchpack: 2.4.0 - webpack-sources: 3.2.3 - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - - /websocket-driver/0.7.4: - resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} - engines: {node: '>=0.8.0'} - dependencies: - http-parser-js: 0.5.8 - safe-buffer: 5.2.1 - websocket-extensions: 0.1.4 - dev: false - - /websocket-extensions/0.1.4: - resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} - engines: {node: '>=0.8.0'} - dev: false - - /whatwg-encoding/2.0.0: - resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} - engines: {node: '>=12'} - dependencies: - iconv-lite: 0.6.3 - - /whatwg-mimetype/2.3.0: - resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} - dev: true - - /whatwg-mimetype/3.0.0: - resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} - engines: {node: '>=12'} - - /whatwg-url/11.0.0: - resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} - engines: {node: '>=12'} - dependencies: - tr46: 3.0.0 - webidl-conversions: 7.0.0 - - /whatwg-url/5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - - /which-boxed-primitive/1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 - - /which-module/2.0.0: - resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==} - - /which-pm/2.0.0: - resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} - engines: {node: '>=8.15'} - dependencies: - load-yaml-file: 0.2.0 - path-exists: 4.0.0 - dev: false - - /which-typed-array/1.1.9: - resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} - engines: {node: '>= 0.4'} - dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.0 - is-typed-array: 1.1.10 - - /which/1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true - dependencies: - isexe: 2.0.0 - - /which/2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - dependencies: - isexe: 2.0.0 - - /wide-align/1.1.5: - resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} - dependencies: - string-width: 1.0.2 - dev: true - - /widest-line/3.1.0: - resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} - engines: {node: '>=8'} - dependencies: - string-width: 4.2.3 - - /widest-line/4.0.1: - resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} - engines: {node: '>=12'} - dependencies: - string-width: 5.1.2 - dev: true - - /wildcard/2.0.0: - resolution: {integrity: sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==} - dev: false - - /word-wrap/1.2.3: - resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} - engines: {node: '>=0.10.0'} - - /wordwrap/1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - - /worker-farm/1.7.0: - resolution: {integrity: sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==} - dependencies: - errno: 0.1.8 - - /worker-rpc/0.1.1: - resolution: {integrity: sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==} - dependencies: - microevent.ts: 0.1.1 - dev: true - - /wrap-ansi/5.1.0: - resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==} - engines: {node: '>=6'} - dependencies: - ansi-styles: 3.2.1 - string-width: 3.1.0 - strip-ansi: 5.2.0 - dev: false - - /wrap-ansi/6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - dev: true - - /wrap-ansi/7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - /wrap-ansi/8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.0.1 - dev: true - - /wrappy/1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - /write-file-atomic/2.4.3: - resolution: {integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==} - dependencies: - graceful-fs: 4.2.11 - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - dev: true - - /write-file-atomic/3.0.3: - resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} - dependencies: - imurmurhash: 0.1.4 - is-typedarray: 1.0.0 - signal-exit: 3.0.7 - typedarray-to-buffer: 3.1.5 - - /write-file-atomic/4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - - /write-yaml-file/4.2.0: - resolution: {integrity: sha512-LwyucHy0uhWqbrOkh9cBluZBeNVxzHjDaE9mwepZG3n3ZlbM4v3ndrFw51zW/NXYFFqP+QWZ72ihtLWTh05e4Q==} - engines: {node: '>=10.13'} - dependencies: - js-yaml: 4.1.0 - write-file-atomic: 3.0.3 - dev: false - - /ws/6.2.2: - resolution: {integrity: sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==} - dependencies: - async-limiter: 1.0.1 - dev: true - - /ws/7.5.9: - resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - /ws/8.13.0: - resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - /xdg-basedir/4.0.0: - resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} - engines: {node: '>=8'} - - /xml-name-validator/4.0.0: - resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} - engines: {node: '>=12'} - - /xml2js/0.4.19: - resolution: {integrity: sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==} - dependencies: - sax: 1.2.1 - xmlbuilder: 9.0.7 - dev: true - - /xml2js/0.4.23: - resolution: {integrity: sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==} - engines: {node: '>=4.0.0'} - dependencies: - sax: 1.2.4 - xmlbuilder: 11.0.1 - dev: false - - /xmlbuilder/11.0.1: - resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} - engines: {node: '>=4.0'} - dev: false - - /xmlbuilder/9.0.7: - resolution: {integrity: sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==} - engines: {node: '>=4.0'} - dev: true - - /xmlchars/2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - - /xmldoc/1.1.4: - resolution: {integrity: sha512-rQshsBGR5s7pUNENTEncpI2LTCuzicri0DyE4SCV5XmS0q81JS8j1iPijP0Q5c4WLGbKh3W92hlOwY6N9ssW1w==} - dependencies: - sax: 1.2.4 - dev: false - - /xregexp/2.0.0: - resolution: {integrity: sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==} - dev: true - - /xstate/4.26.1: - resolution: {integrity: sha512-JLofAEnN26l/1vbODgsDa+Phqa61PwDlxWu8+2pK+YbXf+y9pQSDLRvcYH2H1kkeUBA5fGp+xFL/zfE8jNMw4g==} - dev: true - - /xtend/4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - - /y18n/4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - - /y18n/5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - /yallist/3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - - /yallist/4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - /yaml/1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - - /yargs-parser/13.1.2: - resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==} - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 - dev: false - - /yargs-parser/18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 - dev: true - - /yargs-parser/20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - - /yargs-parser/21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - dev: true - - /yargs/13.3.2: - resolution: {integrity: sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==} - dependencies: - cliui: 5.0.0 - find-up: 3.0.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 3.1.0 - which-module: 2.0.0 - y18n: 4.0.3 - yargs-parser: 13.1.2 - dev: false - - /yargs/15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} - dependencies: - cliui: 6.0.0 - decamelize: 1.2.0 - find-up: 4.1.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 4.2.3 - which-module: 2.0.0 - y18n: 4.0.3 - yargs-parser: 18.1.3 - dev: true - - /yargs/16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - dependencies: - cliui: 7.0.4 - escalade: 3.1.1 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 20.2.9 - - /yargs/17.7.1: - resolution: {integrity: sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==} - engines: {node: '>=12'} - dependencies: - cliui: 8.0.1 - escalade: 3.1.1 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - dev: true - - /yauzl/2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - dependencies: - buffer-crc32: 0.2.13 - fd-slicer: 1.1.0 - dev: true - - /yocto-queue/0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - - /z-schema/5.0.5: - resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} - engines: {node: '>=8.0.0'} - hasBin: true - dependencies: - lodash.get: 4.4.2 - lodash.isequal: 4.5.0 - validator: 13.9.0 - optionalDependencies: - commander: 9.5.0 - - /zip-local/0.3.5: - resolution: {integrity: sha512-GRV3D5TJY+/PqyeRm5CYBs7xVrKTKzljBoEXvocZu0HJ7tPEcgpSOYa2zFIsCZWgKWMuc4U3yMFgFkERGFIB9w==} - dependencies: - async: 1.5.2 - graceful-fs: 4.2.11 - jszip: 2.7.0 - q: 1.5.1 - dev: true - - /zip-stream/4.1.0: - resolution: {integrity: sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==} - engines: {node: '>= 10'} - dependencies: - archiver-utils: 2.1.0 - compress-commons: 4.1.1 - readable-stream: 3.6.2 - dev: true - - /zod/3.21.4: - resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} - dev: true - - /zwitch/1.0.5: - resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} - dev: true diff --git a/common/config/rush/repo-state.json b/common/config/rush/repo-state.json deleted file mode 100644 index 9a3a7870ad2..00000000000 --- a/common/config/rush/repo-state.json +++ /dev/null @@ -1,5 +0,0 @@ -// DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush. -{ - "pnpmShrinkwrapHash": "87a247261b46ac48af0ea3b9155806c604cc4ffe", - "preferredVersionsHash": "1926a5b12ac8f4ab41e76503a0d1d0dccc9c0e06" -} diff --git a/common/config/rush/rush-plugins.json b/common/config/rush/rush-plugins.json index bee0e46c491..ad93f5d3910 100644 --- a/common/config/rush/rush-plugins.json +++ b/common/config/rush/rush-plugins.json @@ -25,5 +25,10 @@ // */ // "autoinstallerName": "rush-plugins" // } + { + "packageName": "@rushstack/rush-published-versions-json-plugin", + "pluginName": "rush-published-versions-json-plugin", + "autoinstallerName": "plugins" + } ] } diff --git a/common/config/rush/subspaces.json b/common/config/rush/subspaces.json new file mode 100644 index 00000000000..0f6f76b6f7b --- /dev/null +++ b/common/config/rush/subspaces.json @@ -0,0 +1,35 @@ +/** + * This configuration file manages the experimental "subspaces" feature for Rush, + * which allows multiple PNPM lockfiles to be used in a single Rush workspace. + * For full documentation, please see https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/subspaces.schema.json", + + /** + * Set this flag to "true" to enable usage of subspaces. + */ + "subspacesEnabled": true, + + /** + * (DEPRECATED) This is a temporary workaround for migrating from an earlier prototype + * of this feature: https://github.com/microsoft/rushstack/pull/3481 + * It allows subspaces with only one project to store their config files in the project folder. + */ + "splitWorkspaceCompatibility": false, + + /** + * When a command such as "rush update" is invoked without the "--subspace" or "--to" + * parameters, Rush will install all subspaces. In a huge monorepo with numerous subspaces, + * this would be extremely slow. Set "preventSelectingAllSubspaces" to true to avoid this + * mistake by always requiring selection parameters for commands such as "rush update". + */ + "preventSelectingAllSubspaces": false, + + /** + * The list of subspace names, which should be lowercase alphanumeric words separated by + * hyphens, for example "my-subspace". The corresponding config files will have paths + * such as "common/config/subspaces/my-subspace/package-lock.yaml". + */ + "subspaceNames": ["build-tests-subspace"] +} diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json index 7bafbd41a9d..a4b06e1f0f4 100644 --- a/common/config/rush/version-policies.json +++ b/common/config/rush/version-policies.json @@ -40,7 +40,7 @@ // * When creating a release branch in Git, this field should be updated according to the // * type of release. // * - // * Valid values are: "prerelease", "release", "minor", "patch", "major" + // * Valid values are: "prerelease", "preminor", "minor", "patch", "major" // */ // "nextBump": "prerelease", // @@ -102,8 +102,8 @@ { "policyName": "rush", "definitionName": "lockStepVersion", - "version": "5.98.0", - "nextBump": "minor", + "version": "5.178.1", + "nextBump": "patch", "mainProject": "@microsoft/rush" } ] diff --git a/common/config/subspaces/build-tests-subspace/.npmrc b/common/config/subspaces/build-tests-subspace/.npmrc new file mode 100644 index 00000000000..9ce3b02e82e --- /dev/null +++ b/common/config/subspaces/build-tests-subspace/.npmrc @@ -0,0 +1,32 @@ +# Rush uses this file to configure the NPM package registry during installation. It is applicable +# to PNPM, NPM, and Yarn package managers. It is used by operations such as "rush install", +# "rush update", and the "install-run.js" scripts. +# +# NOTE: The "rush publish" command uses .npmrc-publish instead. +# +# Before invoking the package manager, Rush will generate an .npmrc in the folder where installation +# is performed. This generated file will omit any config lines that reference environment variables +# that are undefined in that session; this avoids problems that would otherwise result due to +# a missing variable being replaced by an empty string. +# +# If "subspacesEnabled" is true in subspaces.json, the generated file will merge settings from +# "common/config/rush/.npmrc" and "common/config/subspaces//.npmrc", with the latter taking +# precedence. +# +# * * * SECURITY WARNING * * * +# +# It is NOT recommended to store authentication tokens in a text file on a lab machine, because +# other unrelated processes may be able to read that file. Also, the file may persist indefinitely, +# for example if the machine loses power. A safer practice is to pass the token via an +# environment variable, which can be referenced from .npmrc using ${} expansion. For example: +# +# //registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} +# +registry=https://packagefeedproxy.microsoft.io/npm/ +always-auth=false +# No phantom dependencies allowed in this repository +# Don't hoist in common/temp/node_modules +public-hoist-pattern= +# Don't hoist in common/temp/node_modules/.pnpm/node_modules +hoist=false +hoist-pattern= diff --git a/common/config/subspaces/build-tests-subspace/.pnpmfile.cjs b/common/config/subspaces/build-tests-subspace/.pnpmfile.cjs new file mode 100644 index 00000000000..b7821599ca7 --- /dev/null +++ b/common/config/subspaces/build-tests-subspace/.pnpmfile.cjs @@ -0,0 +1,68 @@ +'use strict'; + +/** + * When using the PNPM package manager, you can use pnpmfile.js to workaround + * dependencies that have mistakes in their package.json file. (This feature is + * functionally similar to Yarn's "resolutions".) + * + * For details, see the PNPM documentation: + * https://pnpm.io/pnpmfile#hooks + * + * IMPORTANT: SINCE THIS FILE CONTAINS EXECUTABLE CODE, MODIFYING IT IS LIKELY TO INVALIDATE + * ANY CACHED DEPENDENCY ANALYSIS. After any modification to pnpmfile.js, it's recommended to run + * "rush update --full" so that PNPM will recalculate all version selections. + */ +module.exports = { + hooks: { + readPackage + } +}; + +function fixUndeclaredDependency(packageJson, dependencyName) { + packageJson.dependencies[dependencyName] = + packageJson.dependencies[dependencyName] || + packageJson.devDependencies?.[dependencyName] || + packageJson.version; +} + +/** + * This hook is invoked during installation before a package's dependencies + * are selected. + * The `packageJson` parameter is the deserialized package.json + * contents for the package that is about to be installed. + * The `context` parameter provides a log() function. + * The return value is the updated object. + */ +function readPackage(packageJson, context) { + if (packageJson.name.startsWith('@radix-ui/')) { + if (packageJson.peerDependencies && packageJson.peerDependencies['react']) { + packageJson.peerDependencies['@types/react'] = '*'; + packageJson.peerDependencies['@types/react-dom'] = '*'; + } + } + + switch (packageJson.name) { + case '@jest/test-result': { + // The `@jest/test-result` package takes undeclared dependencies on `jest-haste-map` + // and `jest-resolve` + fixUndeclaredDependency(packageJson, 'jest-haste-map'); + fixUndeclaredDependency(packageJson, 'jest-resolve'); + } + + case '@serverless-stack/core': { + delete packageJson.dependencies['@typescript-eslint/eslint-plugin']; + delete packageJson.dependencies['eslint-config-serverless-stack']; + delete packageJson.dependencies['lerna']; + break; + } + + case '@typescript-eslint/rule-tester': { + // The `@typescript-eslint/rule-tester` package takes an undeclared dependency + // on `@typescript-eslint/parser` + fixUndeclaredDependency(packageJson, '@typescript-eslint/parser'); + break; + } + } + + return packageJson; +} diff --git a/common/config/subspaces/build-tests-subspace/common-versions.json b/common/config/subspaces/build-tests-subspace/common-versions.json new file mode 100644 index 00000000000..0d5cf26de52 --- /dev/null +++ b/common/config/subspaces/build-tests-subspace/common-versions.json @@ -0,0 +1,123 @@ +/** + * This configuration file specifies NPM dependency version selections that affect all projects + * in a Rush repo. More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/common-versions.schema.json", + + /** + * A table that specifies a "preferred version" for a given NPM package. This feature is typically used + * to hold back an indirect dependency to a specific older version, or to reduce duplication of indirect dependencies. + * + * The "preferredVersions" value can be any SemVer range specifier (e.g. "~1.2.3"). Rush injects these values into + * the "dependencies" field of the top-level common/temp/package.json, which influences how the package manager + * will calculate versions. The specific effect depends on your package manager. Generally it will have no + * effect on an incompatible or already constrained SemVer range. If you are using PNPM, similar effects can be + * achieved using the pnpmfile.js hook. See the Rush documentation for more details. + * + * After modifying this field, it's recommended to run "rush update --full" so that the package manager + * will recalculate all version selections. + */ + "preferredVersions": { + /** + * When someone asks for "^1.0.0" make sure they get "1.2.3" when working in this repo, + * instead of the latest version. + */ + // "some-library": "1.2.3" + + // This should be the TypeScript version that's used to build most of the projects in the repo. + // Preferring it avoids errors for indirect dependencies that request it as a peer dependency. + // It's also the newest supported compiler, used by most build tests and used as the bundled compiler + // engine for API Extractor. + "typescript": "~5.8.2", + + // Workaround for https://github.com/microsoft/rushstack/issues/1466 + "eslint": "~9.25.1" + }, + + /** + * When set to true, for all projects in the repo, all dependencies will be automatically added as preferredVersions, + * except in cases where different projects specify different version ranges for a given dependency. For older + * package managers, this tended to reduce duplication of indirect dependencies. However, it can sometimes cause + * trouble for indirect dependencies with incompatible peerDependencies ranges. + * + * The default value is true. If you're encountering installation errors related to peer dependencies, + * it's recommended to set this to false. + * + * After modifying this field, it's recommended to run "rush update --full" so that the package manager + * will recalculate all version selections. + */ + // "implicitlyPreferredVersions": false, + + /** + * If you would like the version specifiers for your dependencies to be consistent, then + * uncomment this line. This is effectively similar to running "rush check" before any + * of the following commands: + * + * rush install, rush update, rush link, rush version, rush publish + * + * In some cases you may want this turned on, but need to allow certain packages to use a different + * version. In those cases, you will need to add an entry to the "allowedAlternativeVersions" + * section of the common-versions.json. + * + * In the case that subspaces is enabled, this setting will take effect at a subspace level. + */ + "ensureConsistentVersions": true, + + /** + * The "rush check" command can be used to enforce that every project in the repo must specify + * the same SemVer range for a given dependency. However, sometimes exceptions are needed. + * The allowedAlternativeVersions table allows you to list other SemVer ranges that will be + * accepted by "rush check" for a given dependency. + * + * IMPORTANT: THIS TABLE IS FOR *ADDITIONAL* VERSION RANGES THAT ARE ALTERNATIVES TO THE + * USUAL VERSION (WHICH IS INFERRED BY LOOKING AT ALL PROJECTS IN THE REPO). + * This design avoids unnecessary churn in this file. + */ + "allowedAlternativeVersions": { + /** + * For example, allow some projects to use an older TypeScript compiler + * (in addition to whatever "usual" version is being used by other projects in the repo): + */ + "typescript": [ + // "~5.0.4" is the (inferred, not alternative) range used by most projects in this repo + + // The oldest supported compiler, used by build-tests/api-extractor-lib1-test + "~2.9.2", + // For testing Heft with TS V3 + "~3.9.10", + // For testing Heft with TS V4 + "~4.9.5" + ], + "source-map": [ + "~0.6.1" // API Extractor is using an older version of source-map because newer versions are async + ], + "tapable": [ + "2.2.1", + "1.1.3" // heft plugin is using an older version of tapable + ], + // --- For Webpack 4 projects ---- + "css-loader": ["~5.2.7"], + "html-webpack-plugin": ["~4.5.2"], + "postcss-loader": ["~4.1.0"], + "sass-loader": ["~10.0.0"], + "sass": ["~1.3.0"], + "source-map-loader": ["~1.1.3"], + "style-loader": ["~2.0.0"], + "terser-webpack-plugin": ["~3.0.8"], + "terser": ["~4.8.0"], + "webpack": ["~4.47.0"], + "@types/node": [ + // These versions are used by testing projects + "ts2.9", + "ts3.9", + "ts4.9" + ], + "@types/jest": [ + // These versions are used by testing projects + "ts2.9", + "ts3.9", + "ts4.9" + ] + } +} diff --git a/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml b/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml new file mode 100644 index 00000000000..de9cfb62372 --- /dev/null +++ b/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml @@ -0,0 +1,8344 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false + +overrides: + package-json: ^7 + '@types/estree': 1.0.8 + '@types/react@17.0.74>@types/scheduler': 0.16.8 + '@vscode/vsce>cheerio': 1.0.0-rc.12 + loader-utils@^2.0.0: 2.0.4 + fast-xml-parser@^5.3.3: 5.3.5 + +packageExtensionsChecksum: sha256-8fXYR9X9qRA57SZJJSADz6C9KMP6QQYYut4DHyehah0= + +pnpmfileChecksum: sha256-E1T7OJ3DLTjpDqf4RdJzK9VDtAxgm4gDEQCLYdHD8nI= + +importers: + + .: {} + + ../../../build-tests-subspace/rush-lib-test: + dependencies: + '@microsoft/rush-lib': + specifier: file:../../libraries/rush-lib + version: file:../../../libraries/rush-lib(@types/node@20.17.19) + '@rushstack/terminal': + specifier: file:../../libraries/terminal + version: file:../../../libraries/terminal(@types/node@20.17.19) + dependenciesMeta: + '@microsoft/rush-lib': + injected: true + '@rushstack/heft': + injected: true + '@rushstack/terminal': + injected: true + local-node-rig: + injected: true + devDependencies: + '@rushstack/heft': + specifier: file:../../apps/heft + version: file:../../../apps/heft(@types/node@20.17.19) + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.25.1 + version: 9.25.1 + local-node-rig: + specifier: file:../../rigs/local-node-rig + version: file:../../../rigs/local-node-rig + + ../../../build-tests-subspace/rush-sdk-test: + dependencies: + '@rushstack/rush-sdk': + specifier: file:../../libraries/rush-sdk + version: file:../../../libraries/rush-sdk(@types/node@20.17.19) + dependenciesMeta: + '@microsoft/rush-lib': + injected: true + '@rushstack/heft': + injected: true + '@rushstack/rush-sdk': + injected: true + local-node-rig: + injected: true + devDependencies: + '@microsoft/rush-lib': + specifier: file:../../libraries/rush-lib + version: file:../../../libraries/rush-lib(@types/node@20.17.19) + '@rushstack/heft': + specifier: file:../../apps/heft + version: file:../../../apps/heft(@types/node@20.17.19) + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.25.1 + version: 9.25.1 + local-node-rig: + specifier: file:../../rigs/local-node-rig + version: file:../../../rigs/local-node-rig + + ../../../build-tests-subspace/typescript-newest-test: + dependenciesMeta: + '@rushstack/heft': + injected: true + local-node-rig: + injected: true + devDependencies: + '@rushstack/heft': + specifier: file:../../apps/heft + version: file:../../../apps/heft(@types/node@20.17.19) + eslint: + specifier: ~9.25.1 + version: 9.25.1 + local-node-rig: + specifier: file:../../rigs/local-node-rig + version: file:../../../rigs/local-node-rig + typescript: + specifier: ~5.8.2 + version: 5.8.3 + + ../../../build-tests-subspace/typescript-v4-test: + dependenciesMeta: + '@rushstack/eslint-config': + injected: true + '@rushstack/heft': + injected: true + '@rushstack/heft-lint-plugin': + injected: true + '@rushstack/heft-typescript-plugin': + injected: true + devDependencies: + '@rushstack/eslint-config': + specifier: file:../../eslint/eslint-config + version: file:../../../eslint/eslint-config(eslint@9.25.1)(typescript@4.9.5) + '@rushstack/heft': + specifier: file:../../apps/heft + version: file:../../../apps/heft(@types/node@20.17.19) + '@rushstack/heft-lint-plugin': + specifier: file:../../heft-plugins/heft-lint-plugin + version: file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@file:../../../apps/heft(@types/node@20.17.19))(@types/node@20.17.19) + '@rushstack/heft-typescript-plugin': + specifier: file:../../heft-plugins/heft-typescript-plugin + version: file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@file:../../../apps/heft(@types/node@20.17.19))(@types/node@20.17.19) + eslint: + specifier: ~9.25.1 + version: 9.25.1 + tslint: + specifier: ~5.20.1 + version: 5.20.1(typescript@4.9.5) + typescript: + specifier: ~4.9.5 + version: 4.9.5 + + ../../../build-tests/webpack-local-version-test: + devDependencies: + '@rushstack/heft': + specifier: link:../../apps/heft + version: link:../../apps/heft + '@rushstack/heft-lint-plugin': + specifier: link:../../heft-plugins/heft-lint-plugin + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: link:../../heft-plugins/heft-typescript-plugin + version: link:../../heft-plugins/heft-typescript-plugin + '@rushstack/heft-webpack5-plugin': + specifier: link:../../heft-plugins/heft-webpack5-plugin + version: link:../../heft-plugins/heft-webpack5-plugin + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~9.25.1 + version: 9.25.1 + html-webpack-plugin: + specifier: ~5.5.0 + version: 5.5.4(webpack@5.105.4) + typescript: + specifier: ~5.8.2 + version: 5.8.3 + webpack: + specifier: ~5.105.0 + version: 5.105.4 + +packages: + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.28.6': + resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.28.6': + resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.28.6': + resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@emnapi/core@1.9.2': + resolution: {integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==} + + '@emnapi/runtime@1.9.2': + resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@es-joy/jsdoccomment@0.49.0': + resolution: {integrity: sha512-xjZTSFgECpb9Ohuk5yMX5RhUEbfeQcuOp8IF60e+wyzWEF0M5xeSgqsfLtvPEX8BIyOX9saZqzuGPmZ8oWc+5Q==} + engines: {node: '>=16'} + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.20.1': + resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.2.3': + resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.13.0': + resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.16.0': + resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.25.1': + resolution: {integrity: sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.37.0': + resolution: {integrity: sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.2.8': + resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@inquirer/ansi@2.0.5': + resolution: {integrity: sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/checkbox@5.1.3': + resolution: {integrity: sha512-+G7I8CT+EHv/hasNfUl3P37DVoMoZfpA+2FXmM54dA8MxYle1YqucxbacxHalw1iAFSdKNEDTGNV7F+j1Ldqcg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@6.0.11': + resolution: {integrity: sha512-pTpHjg0iEIRMYV/7oCZUMf27/383E6Wyhfc/MY+AVQGEoUobffIYWOK9YLP2XFRGz/9i6WlTQh1CkFVIo2Y7XA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@11.1.8': + resolution: {integrity: sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@2.0.5': + resolution: {integrity: sha512-NsSs4kzfm12lNetHwAn3GEuH317IzpwrMCbOuMIVytpjnJ90YYHNwdRgYGuKmVxwuIqSgqk3M5qqQt1cDk0tGQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/input@5.0.11': + resolution: {integrity: sha512-twUWidn4ocPO8qi6fRM7tNWt7W1FOnOZqQ+/+PsfLUacMR5rFLDPK9ql0nBPwxi0oELbo8T5NhRs8B2+qQEqFQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@4.1.7': + resolution: {integrity: sha512-1y7+0N65AWk5RdlXH/Kn13txf3IjIQ7OEfhCEkDTU+h5wKMLq8DUF3P6z+/kLSxDGDtQT1dRBWEUC3o/VvImsQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@5.1.3': + resolution: {integrity: sha512-zYyqWgGQi3NhBcNq4Isc5rB3oEdQEh1Q/EcAnOW0FK4MpnXWkvSBYgA4cYrTM4A9UB573omouZbnL9JJ74Mq3A==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@4.0.5': + resolution: {integrity: sha512-aetVUNeKNc/VriqXlw1NRSW0zhMBB0W4bNbWRJgzRl/3d0QNDQFfk0GO5SDdtjMZVg6o8ZKEiadd7SCCzoOn5Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/console@30.3.0': + resolution: {integrity: sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/core@30.3.0': + resolution: {integrity: sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/diff-sequences@30.3.0': + resolution: {integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/environment@30.3.0': + resolution: {integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/expect-utils@30.3.0': + resolution: {integrity: sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/expect@30.3.0': + resolution: {integrity: sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/fake-timers@30.3.0': + resolution: {integrity: sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/get-type@30.1.0': + resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/globals@30.3.0': + resolution: {integrity: sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/pattern@30.0.1': + resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/reporters@30.3.0': + resolution: {integrity: sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@30.0.5': + resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/snapshot-utils@30.3.0': + resolution: {integrity: sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/source-map@30.0.1': + resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/test-result@30.3.0': + resolution: {integrity: sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/test-sequencer@30.3.0': + resolution: {integrity: sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/transform@30.3.0': + resolution: {integrity: sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/types@30.3.0': + resolution: {integrity: sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@jsep-plugin/assignment@1.3.0': + resolution: {integrity: sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==} + engines: {node: '>= 10.16.0'} + peerDependencies: + jsep: ^0.4.0||^1.0.0 + + '@jsep-plugin/regex@1.0.4': + resolution: {integrity: sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==} + engines: {node: '>= 10.16.0'} + peerDependencies: + jsep: ^0.4.0||^1.0.0 + + '@microsoft/api-extractor-model@file:../../../libraries/api-extractor-model': + resolution: {directory: ../../../libraries/api-extractor-model, type: directory} + + '@microsoft/api-extractor@file:../../../apps/api-extractor': + resolution: {directory: ../../../apps/api-extractor, type: directory} + hasBin: true + + '@microsoft/rush-lib@file:../../../libraries/rush-lib': + resolution: {directory: ../../../libraries/rush-lib, type: directory} + engines: {node: '>=5.6.0'} + + '@microsoft/tsdoc-config@0.18.1': + resolution: {integrity: sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==} + + '@microsoft/tsdoc@0.16.0': + resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} + + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@pnpm/constants@1001.3.1': + resolution: {integrity: sha512-2hf0s4pVrVEH8RvdJJ7YRKjQdiG8m0iAT26TTqXnCbK30kKwJW69VLmP5tED5zstmDRXcOeH5eRcrpkdwczQ9g==} + engines: {node: '>=18.12'} + + '@pnpm/constants@7.1.1': + resolution: {integrity: sha512-31pZqMtjwV+Vaq7MaPrT1EoDFSYwye3dp6BiHIGRJmVThCQwySRKM7hCvqqI94epNkqFAAYoWrNynWoRYosGdw==} + engines: {node: '>=16.14'} + + '@pnpm/crypto.base32-hash@1.0.1': + resolution: {integrity: sha512-pzAXNn6KxTA3kbcI3iEnYs4vtH51XEVqmK/1EiD18MaPKylhqy8UvMJK3zKG+jeP82cqQbozcTGm4yOQ8i3vNw==} + engines: {node: '>=14.6'} + + '@pnpm/crypto.base32-hash@2.0.0': + resolution: {integrity: sha512-3ttOeHBpmWRbgJrpDQ8Nwd3W8s8iuiP5YZM0JRyKWaMtX8lu9d7/AKyxPmhYsMJuN+q/1dwHa7QFeDZJ53b0oA==} + engines: {node: '>=16.14'} + + '@pnpm/crypto.base32-hash@3.0.1': + resolution: {integrity: sha512-DM4RR/tvB7tMb2FekL0Q97A5PCXNyEC+6ht8SaufAUFSJNxeozqHw9PHTZR03mzjziPzNQLOld0pNINBX3srtw==} + engines: {node: '>=18.12'} + + '@pnpm/crypto.hash@1000.1.1': + resolution: {integrity: sha512-lb5kwXaOXdIW/4bkLLmtM9HEVRvp2eIvp+TrdawcPoaptgA/5f0/sRG0P52BF8dFqeNDj+1tGdqH89WQEqJnxA==} + engines: {node: '>=18.12'} + + '@pnpm/crypto.hash@1000.2.2': + resolution: {integrity: sha512-W8pLZvXWLlGG5p0Z2nCvtBhlM6uuTcbAbsS15wlGS31jBBJKJW2udLoFeM7qfWPo7E2PqRPGxca7APpVYAjJhw==} + engines: {node: '>=18.12'} + + '@pnpm/crypto.polyfill@1.0.0': + resolution: {integrity: sha512-WbmsqqcUXKKaAF77ox1TQbpZiaQcr26myuMUu+WjUtoWYgD3VP6iKYEvSx35SZ6G2L316lu+pv+40A2GbWJc1w==} + engines: {node: '>=18.12'} + + '@pnpm/crypto.polyfill@1000.1.0': + resolution: {integrity: sha512-tNe7a6U4rCpxLMBaR0SIYTdjxGdL0Vwb3G1zY8++sPtHSvy7qd54u8CIB0Z+Y6t5tc9pNYMYCMwhE/wdSY7ltg==} + engines: {node: '>=18.12'} + + '@pnpm/dependency-path@1000.0.9': + resolution: {integrity: sha512-0AhabApfiq3EEYeed5HKQEU3ftkrfyKTNgkMH9esGdp2yc+62Zu7eWFf8WW6IGyitDQPLWGYjSEWDC9Bvv8nPg==} + engines: {node: '>=18.12'} + + '@pnpm/dependency-path@1001.1.10': + resolution: {integrity: sha512-PNImtV2SmNTDpLi4HdN86tJPmsOeIxm4VhmxgBVsMrJPEBfkNEWFcflR3wU6XVn/26g9qWdvlNHaawtCjeB93Q==} + engines: {node: '>=18.12'} + + '@pnpm/dependency-path@2.1.8': + resolution: {integrity: sha512-ywBaTjy0iSEF7lH3DlF8UXrdL2bw4AQFV2tTOeNeY7wc1W5CE+RHSJhf9MXBYcZPesqGRrPiU7Pimj3l05L9VA==} + engines: {node: '>=16.14'} + + '@pnpm/dependency-path@5.1.7': + resolution: {integrity: sha512-MKCyaTy1r9fhBXAnhDZNBVgo6ThPnicwJEG203FDp7pGhD7NruS/FhBI+uMd7GNsK3D7aIFCDAgbWpNTXn/eWw==} + engines: {node: '>=18.12'} + + '@pnpm/error@1.4.0': + resolution: {integrity: sha512-vxkRrkneBPVmP23kyjnYwVOtipwlSl6UfL+h+Xa3TrABJTz5rYBXemlTsU5BzST8U4pD7YDkTb3SQu+MMuIDKA==} + engines: {node: '>=10.16'} + + '@pnpm/error@1000.1.0': + resolution: {integrity: sha512-Dqc2IJJPjUatwc9Letw+vG29rnaMrDGi5g6WCx1HiZYm0obXbTmLygeRafMbgf+sLKXrWE1shOeiayQuczBdoA==} + engines: {node: '>=18.12'} + + '@pnpm/error@5.0.3': + resolution: {integrity: sha512-ONJU5cUeoeJSy50qOYsMZQHTA/9QKmGgh1ATfEpCLgtbdwqUiwD9MxHNeXUYYI/pocBCz6r1ZCFqiQvO+8SUKA==} + engines: {node: '>=16.14'} + + '@pnpm/git-utils@1.0.0': + resolution: {integrity: sha512-lUI+XrzOJN4zdPGOGnFUrmtXAXpXi8wD8OI0nWOZmlh+raqbLzC3VkXu1zgaduOK6YonOcnQW88O+ojav1rAdA==} + engines: {node: '>=16.14'} + + '@pnpm/git-utils@1000.0.0': + resolution: {integrity: sha512-W6isNTNgB26n6dZUgwCw6wly+uHQ2Zh5QiRKY1HHMbLAlsnZOxsSNGnuS9euKWHxDftvPfU7uR8XB5x95T5zPQ==} + engines: {node: '>=18.12'} + + '@pnpm/graceful-fs@1000.0.0': + resolution: {integrity: sha512-RvMEliAmcfd/4UoaYQ93DLQcFeqit78jhYmeJJVPxqFGmj0jEcb9Tu0eAOXr7tGP3eJHpgvPbTU4o6pZ1bJhxg==} + engines: {node: '>=18.12'} + + '@pnpm/graceful-fs@1000.1.0': + resolution: {integrity: sha512-EsMX4slK0qJN2AR0/AYohY5m0HQNYGMNe+jhN74O994zp22/WbX+PbkIKyw3UQn39yQm2+z6SgwklDxbeapsmQ==} + engines: {node: '>=18.12'} + + '@pnpm/link-bins@5.3.25': + resolution: {integrity: sha512-9Xq8lLNRHFDqvYPXPgaiKkZ4rtdsm7izwM/cUsFDc5IMnG0QYIVBXQbgwhz2UvjUotbJrvfKLJaCfA3NGBnLDg==} + engines: {node: '>=10.16'} + + '@pnpm/lockfile-file@8.1.8': + resolution: {integrity: sha512-bRadYzGFyFtwiynwp4Mkn7NDNHkgKvJ9xtjsCT5XiE6S8wpzS3W8yx2WzHGk9Mm1J/2wM0F52+NzCWhlz5eIqA==} + engines: {node: '>=16.14'} + peerDependencies: + '@pnpm/logger': ^5.0.0 + + '@pnpm/lockfile-types@5.1.5': + resolution: {integrity: sha512-02FP0HynzX+2DcuPtuMy7PH+kLIC0pevAydAOK+zug2bwdlSLErlvSkc+4+3dw60eRWgUXUqyfO2eR/Ansdbng==} + engines: {node: '>=16.14'} + + '@pnpm/lockfile.fs@1001.1.32': + resolution: {integrity: sha512-I+aHBjbgDy2Ftxla8FVZkx/ARuKOyGag1zaCuVuZDzH4Xb2ETUuTeGAf1GTr1XqM7UnCNse1GKgr5KZJ0cz43w==} + engines: {node: '>=18.12'} + peerDependencies: + '@pnpm/logger': ^1001.0.1 + + '@pnpm/lockfile.merger@1001.0.20': + resolution: {integrity: sha512-93MKB5fObr49PMRoDZVcUewe2uuR6TRj8In0y1CeXeDzXY1SPVKZsODCVvAA2z2UxZ1YXKcw9Oaak31E9ln5CQ==} + engines: {node: '>=18.12'} + + '@pnpm/lockfile.types@1001.1.0': + resolution: {integrity: sha512-/rfDUV8M9iMm0QXahHPv6SD6eKNkrMXlhECJVhDkdL4NIifcv6/HZwYtxd0PIndExz04+OE+iV9K8zKG9i/OEA==} + engines: {node: '>=18.12'} + + '@pnpm/lockfile.types@1002.1.0': + resolution: {integrity: sha512-Oa9Fhwo4Ipodj3hyUPC5wUt5ucVkuttyct2DbFUkB79Fq5HL9MHHQ+JFYh03eajmLqWrN1t8+6DbmcKqRtNjNg==} + engines: {node: '>=18.12'} + + '@pnpm/lockfile.types@900.0.0': + resolution: {integrity: sha512-/4+3CAu4uIjx0ln1DYXNdj0qKJ3wyRDY+RS+eFzV6OHjreaTKWsF2WcjigYp1M5mxL4kj2RsRGgBGEyKtCfEWg==} + engines: {node: '>=18.12'} + + '@pnpm/lockfile.utils@1004.0.3': + resolution: {integrity: sha512-02hFBFk/BGmZYhqd7paBjJm5mHy7GwI6DOJL25dakctRIPCr2kUZkPs/DH3WLKAjNLykEh7Dp/dPs5rnUsUE5g==} + engines: {node: '>=18.12'} + + '@pnpm/logger@1001.0.1': + resolution: {integrity: sha512-gdwlAMXC4Wc0s7Dmg/4wNybMEd/4lSd9LsXQxeg/piWY0PPXjgz1IXJWnVScx6dZRaaodWP3c1ornrw8mZdFZw==} + engines: {node: '>=18.12'} + + '@pnpm/logger@5.0.0': + resolution: {integrity: sha512-YfcB2QrX+Wx1o6LD1G2Y2fhDhOix/bAY/oAnMpHoNLsKkWIRbt1oKLkIFvxBMzLwAEPqnYWguJrYC+J6i4ywbw==} + engines: {node: '>=12.17'} + + '@pnpm/merge-lockfile-changes@5.0.7': + resolution: {integrity: sha512-fYmX1+EHv3wg7l4A9FCEkjgEBIHaY6JosknkLk3pL8dbB9k6unjIrF9f2onNtpj3XUlWxZ3aBw9THk/Bf6hKow==} + engines: {node: '>=16.14'} + + '@pnpm/object.key-sorting@1000.0.1': + resolution: {integrity: sha512-YTJCXyUGOrJuj4QqhSKqZa1vlVAm82h1/uw00ZmD/kL2OViggtyUwWyIe62kpwWVPwEYixfGjfvaFKVJy2mjzA==} + engines: {node: '>=18.12'} + + '@pnpm/package-bins@4.1.0': + resolution: {integrity: sha512-57/ioGYLBbVRR80Ux9/q2i3y8Q+uQADc3c+Yse8jr/60YLOi3jcWz13e2Jy+ANYtZI258Qc5wk2X077rp0Ly/Q==} + engines: {node: '>=10.16'} + + '@pnpm/patching.types@1000.1.0': + resolution: {integrity: sha512-Zib2ysLctRnWM4KXXlljR44qSKwyEqYmLk+8VPBDBEK3l5Gp5mT3N4ix9E4qjYynvFqahumsxzOfxOYQhUGMGw==} + engines: {node: '>=18.12'} + + '@pnpm/patching.types@900.0.0': + resolution: {integrity: sha512-A/3kgRD4Xy2tBMPjOBdx5ZdgmpUobphzWkqDB72S5SIB6gdyCg32AUV0/aO12DwMxpT7kyqMhkkynUOPBfdlUQ==} + engines: {node: '>=18.12'} + + '@pnpm/pick-fetcher@1001.0.0': + resolution: {integrity: sha512-Zl8npMjFSS1gSGM27KkbmfmeOuwU2MCxRFIofAUo/PkqOE2IzzXr0yzB1XYJM8Ml1nUXt9BHfwAlUQKC5MdBLA==} + engines: {node: '>=18.12'} + + '@pnpm/ramda@0.28.1': + resolution: {integrity: sha512-zcAG+lvU0fMziNeGXpPyCyCJYp5ZVrPElEE4t14jAmViaihohocZ+dDkcRIyAomox8pQsuZnv1EyHR+pOhmUWw==} + + '@pnpm/read-modules-dir@2.0.3': + resolution: {integrity: sha512-i9OgRvSlxrTS9a2oXokhDxvQzDtfqtsooJ9jaGoHkznue5aFCTSrNZFQ6M18o8hC03QWfnxaKi0BtOvNkKu2+A==} + engines: {node: '>=10.13'} + + '@pnpm/read-package-json@4.0.0': + resolution: {integrity: sha512-1cr2tEwe4YU6SI0Hmg+wnsr6yxBt2iJtqv6wrF84On8pS9hx4A2PLw3CIgbwxaG0b+ur5wzhNogwl4qD5FLFNg==} + engines: {node: '>=10.16'} + + '@pnpm/read-project-manifest@1.1.7': + resolution: {integrity: sha512-tj8ExXZeDcMmMUj7D292ETe/RiEirr1X1wpT6Zy85z2MrFYoG9jfCJpps40OdZBNZBhxbuKtGPWKVSgXD0yrVw==} + engines: {node: '>=10.16'} + + '@pnpm/resolver-base@1005.4.1': + resolution: {integrity: sha512-47zGgACkbZWLOmM61kaE0nkqxiYx63C6DJ4wzDsdj0iXDZJ9SJEl+T035pkhquHe8XEh3YxvwMg2BRyZSgmZ9Q==} + engines: {node: '>=18.12'} + + '@pnpm/types@1000.6.0': + resolution: {integrity: sha512-6PsMNe98VKPGcg6LnXSW/LE3YfJ77nj+bPKiRjYRWAQLZ+xXjEQRaR0dAuyjCmchlv4wR/hpnMVRS21/fCod5w==} + engines: {node: '>=18.12'} + + '@pnpm/types@1000.7.0': + resolution: {integrity: sha512-1s7FvDqmOEIeFGLUj/VO8sF5lGFxeE/1WALrBpfZhDnMXY/x8FbmuygTTE5joWifebcZ8Ww8Kw2CgBoStsIevQ==} + engines: {node: '>=18.12'} + + '@pnpm/types@1001.3.0': + resolution: {integrity: sha512-NLTXheat/u7OEGg5M5vF6Z85zx8uKUZE0+whtX/sbFV2XL48RdnOWGPTKYuVVkv8M+launaLUTgGEXNs/ess2w==} + engines: {node: '>=18.12'} + + '@pnpm/types@12.2.0': + resolution: {integrity: sha512-5RtwWhX39j89/Tmyv2QSlpiNjErA357T/8r1Dkg+2lD3P7RuS7Xi2tChvmOC3VlezEFNcWnEGCOeKoGRkDuqFA==} + engines: {node: '>=18.12'} + + '@pnpm/types@6.4.0': + resolution: {integrity: sha512-nco4+4sZqNHn60Y4VE/fbtlShCBqipyUO+nKRPvDHqLrecMW9pzHWMVRxk4nrMRoeowj3q0rX3GYRBa8lsHTAg==} + engines: {node: '>=10.16'} + + '@pnpm/types@8.9.0': + resolution: {integrity: sha512-3MYHYm8epnciApn6w5Fzx6sepawmsNU7l6lvIq+ER22/DPSrr83YMhU/EQWnf4lORn2YyiXFj0FJSyJzEtIGmw==} + engines: {node: '>=14.6'} + + '@pnpm/types@9.4.2': + resolution: {integrity: sha512-g1hcF8Nv4gd76POilz9gD4LITAPXOe5nX4ijgr8ixCbLQZfcpYiMfJ+C1RlMNRUDo8vhlNB4O3bUlxmT6EAQXA==} + engines: {node: '>=16.14'} + + '@pnpm/types@900.0.0': + resolution: {integrity: sha512-GucC9h/EVbU03Kl7M/FqVes1s5RCQaGCW2f41lFA7VqqHWQElR6k1q33iF6f6fXDUSCdzB1IUxSq9ghP2J+8Pw==} + engines: {node: '>=18.12'} + + '@pnpm/util.lex-comparator@1.0.0': + resolution: {integrity: sha512-3aBQPHntVgk5AweBWZn+1I/fqZ9krK/w01197aYVkAJQGftb+BVWgEepxY5GChjSW12j52XX+CmfynYZ/p0DFQ==} + engines: {node: '>=12.22.0'} + + '@pnpm/util.lex-comparator@3.0.2': + resolution: {integrity: sha512-blFO4Ws97tWv/SNE6N39ZdGmZBrocXnBOfVp0ln4kELmns4pGPZizqyRtR8EjfOLMLstbmNCTReBoDvLz1isVg==} + engines: {node: '>=18.12'} + + '@pnpm/write-project-manifest@1.1.7': + resolution: {integrity: sha512-OLkDZSqkA1mkoPNPvLFXyI6fb0enCuFji6Zfditi/CLAo9kmIhQFmEUDu4krSB8i908EljG8YwL5Xjxzm5wsWA==} + engines: {node: '>=10.16'} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@rushstack/credential-cache@file:../../../libraries/credential-cache': + resolution: {directory: ../../../libraries/credential-cache, type: directory} + + '@rushstack/eslint-config@file:../../../eslint/eslint-config': + resolution: {directory: ../../../eslint/eslint-config, type: directory} + peerDependencies: + eslint: ^8.57.0 || ^9.25.1 + typescript: '>=4.7.0' + + '@rushstack/eslint-patch@file:../../../eslint/eslint-patch': + resolution: {directory: ../../../eslint/eslint-patch, type: directory} + + '@rushstack/eslint-plugin-packlets@file:../../../eslint/eslint-plugin-packlets': + resolution: {directory: ../../../eslint/eslint-plugin-packlets, type: directory} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + + '@rushstack/eslint-plugin-security@file:../../../eslint/eslint-plugin-security': + resolution: {directory: ../../../eslint/eslint-plugin-security, type: directory} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + + '@rushstack/eslint-plugin@file:../../../eslint/eslint-plugin': + resolution: {directory: ../../../eslint/eslint-plugin, type: directory} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + + '@rushstack/heft-api-extractor-plugin@file:../../../heft-plugins/heft-api-extractor-plugin': + resolution: {directory: ../../../heft-plugins/heft-api-extractor-plugin, type: directory} + peerDependencies: + '@rushstack/heft': 1.2.21 + + '@rushstack/heft-config-file@file:../../../libraries/heft-config-file': + resolution: {directory: ../../../libraries/heft-config-file, type: directory} + engines: {node: '>=10.13.0'} + + '@rushstack/heft-jest-plugin@file:../../../heft-plugins/heft-jest-plugin': + resolution: {directory: ../../../heft-plugins/heft-jest-plugin, type: directory} + peerDependencies: + '@rushstack/heft': ^1.2.21 + '@types/jest': ^30.0.0 + jest-environment-jsdom: ^30.3.0 + jest-environment-node: ^30.3.0 + peerDependenciesMeta: + '@types/jest': + optional: true + jest-environment-jsdom: + optional: true + jest-environment-node: + optional: true + + '@rushstack/heft-lint-plugin@file:../../../heft-plugins/heft-lint-plugin': + resolution: {directory: ../../../heft-plugins/heft-lint-plugin, type: directory} + peerDependencies: + '@rushstack/heft': 1.2.21 + + '@rushstack/heft-node-rig@file:../../../rigs/heft-node-rig': + resolution: {directory: ../../../rigs/heft-node-rig, type: directory} + peerDependencies: + '@rushstack/heft': ^1.2.21 + + '@rushstack/heft-typescript-plugin@file:../../../heft-plugins/heft-typescript-plugin': + resolution: {directory: ../../../heft-plugins/heft-typescript-plugin, type: directory} + peerDependencies: + '@rushstack/heft': 1.2.21 + + '@rushstack/heft@file:../../../apps/heft': + resolution: {directory: ../../../apps/heft, type: directory} + engines: {node: '>=10.13.0'} + hasBin: true + + '@rushstack/lookup-by-path@file:../../../libraries/lookup-by-path': + resolution: {directory: ../../../libraries/lookup-by-path, type: directory} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/node-core-library@file:../../../libraries/node-core-library': + resolution: {directory: ../../../libraries/node-core-library, type: directory} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/npm-check-fork@file:../../../libraries/npm-check-fork': + resolution: {directory: ../../../libraries/npm-check-fork, type: directory} + + '@rushstack/operation-graph@file:../../../libraries/operation-graph': + resolution: {directory: ../../../libraries/operation-graph, type: directory} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/package-deps-hash@file:../../../libraries/package-deps-hash': + resolution: {directory: ../../../libraries/package-deps-hash, type: directory} + + '@rushstack/package-extractor@file:../../../libraries/package-extractor': + resolution: {directory: ../../../libraries/package-extractor, type: directory} + + '@rushstack/problem-matcher@file:../../../libraries/problem-matcher': + resolution: {directory: ../../../libraries/problem-matcher, type: directory} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/rig-package@file:../../../libraries/rig-package': + resolution: {directory: ../../../libraries/rig-package, type: directory} + + '@rushstack/rush-pnpm-kit-v10@file:../../../libraries/rush-pnpm-kit-v10': + resolution: {directory: ../../../libraries/rush-pnpm-kit-v10, type: directory} + + '@rushstack/rush-pnpm-kit-v8@file:../../../libraries/rush-pnpm-kit-v8': + resolution: {directory: ../../../libraries/rush-pnpm-kit-v8, type: directory} + + '@rushstack/rush-pnpm-kit-v9@file:../../../libraries/rush-pnpm-kit-v9': + resolution: {directory: ../../../libraries/rush-pnpm-kit-v9, type: directory} + + '@rushstack/rush-sdk@file:../../../libraries/rush-sdk': + resolution: {directory: ../../../libraries/rush-sdk, type: directory} + + '@rushstack/stream-collator@file:../../../libraries/stream-collator': + resolution: {directory: ../../../libraries/stream-collator, type: directory} + + '@rushstack/terminal@file:../../../libraries/terminal': + resolution: {directory: ../../../libraries/terminal, type: directory} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/tree-pattern@file:../../../libraries/tree-pattern': + resolution: {directory: ../../../libraries/tree-pattern, type: directory} + + '@rushstack/ts-command-line@file:../../../libraries/ts-command-line': + resolution: {directory: ../../../libraries/ts-command-line, type: directory} + + '@sinclair/typebox@0.34.49': + resolution: {integrity: sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@15.3.0': + resolution: {integrity: sha512-m2xozxSfCIxjDdvbhIWazlP2i2aha/iUmbl94alpsIbd3iLTfeXgfBVbwyWogB6l++istyGZqamgA/EcqYf+Bg==} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/argparse@1.0.38': + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/html-minifier-terser@6.1.0': + resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} + + '@types/istanbul-lib-coverage@2.0.4': + resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@30.0.0': + resolution: {integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@20.17.19': + resolution: {integrity: sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/tapable@1.0.6': + resolution: {integrity: sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==} + + '@types/webpack-env@1.18.8': + resolution: {integrity: sha512-G9eAoJRMLjcvN4I08wB5I7YofOb/kaJNd5uoCMX+LbKXTPCF+ZIHuqTnFaK9Jz1rgs035f9JUPUhNFtqgucy/A==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.35': + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} + + '@typescript-eslint/eslint-plugin@8.56.1': + resolution: {integrity: sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.56.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.56.1': + resolution: {integrity: sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.56.1': + resolution: {integrity: sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.56.1': + resolution: {integrity: sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.56.1': + resolution: {integrity: sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.56.1': + resolution: {integrity: sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.56.1': + resolution: {integrity: sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + + '@typescript-eslint/typescript-estree@8.56.1': + resolution: {integrity: sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.56.1': + resolution: {integrity: sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.56.1': + resolution: {integrity: sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.11.1': + resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.11.1': + resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + cpu: [x64] + os: [win32] + + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + '@yarnpkg/lockfile@1.0.2': + resolution: {integrity: sha512-MqJ00WXw89ga0rK6GZkdmmgv3bAsxpJixyTthjcix73O44pBqotyU2BejBkLuIsaOBI6SEu77vAnSyLe5iIHkw==} + + '@zkochan/cmd-shim@5.4.1': + resolution: {integrity: sha512-odWb1qUzt0dIOEUPyWBEpFDYQPRjEMr/dbHHAfgBkVkYR9aO7Zo+I7oYWrXIxl+cKlC7+49ftPm8uJxL1MA9kw==} + engines: {node: '>=10.13'} + + '@zkochan/js-yaml@0.0.11': + resolution: {integrity: sha512-SO+h5Jg079r2JvGle0jbdtk1EY7ppu6TGzmfWTp3Gy61IEb1OVKBocJ6ydTn4++nYFNfRKYenI2MniZQwsM9KQ==} + hasBin: true + + '@zkochan/js-yaml@0.0.6': + resolution: {integrity: sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg==} + hasBin: true + + '@zkochan/rimraf@2.1.3': + resolution: {integrity: sha512-mCfR3gylCzPC+iqdxEA6z5SxJeOgzgbwmyxanKriIne5qZLswDe/M43aD3p5MNzwzXRhbZg/OX+MpES6Zk1a6A==} + engines: {node: '>=12.10'} + + '@zkochan/rimraf@3.0.2': + resolution: {integrity: sha512-GBf4ua7ogWTr7fATnzk/JLowZDBnBJMm8RkMaC/KcvxZ9gxbMWix0/jImd815LmqKyIHZ7h7lADRddGMdGBuCA==} + engines: {node: '>=18.12'} + + '@zkochan/which@2.0.3': + resolution: {integrity: sha512-C1ReN7vt2/2O0fyTsx5xnbQuxBrmG5NMSbcIkPKCCfCTJgpZBsuRYzFXHj3nVq8vTfK7vxHUmzfCpSHgO7j4rg==} + engines: {node: '>= 8'} + hasBin: true + + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@4.1.1: + resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} + engines: {node: '>=6'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + are-docs-informative@0.0.2: + resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} + engines: {node: '>=14'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + babel-jest@30.3.0: + resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@babel/core': ^7.11.0 || ^8.0.0-0 + + babel-plugin-istanbul@7.0.1: + resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} + engines: {node: '>=12'} + + babel-plugin-jest-hoist@30.3.0: + resolution: {integrity: sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + babel-preset-current-node-syntax@1.2.0: + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} + peerDependencies: + '@babel/core': ^7.0.0 || ^8.0.0-0 + + babel-preset-jest@30.3.0: + resolution: {integrity: sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@babel/core': ^7.11.0 || ^8.0.0-beta.1 + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.10.13: + resolution: {integrity: sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw==} + engines: {node: '>=6.0.0'} + hasBin: true + + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + bole@5.0.28: + resolution: {integrity: sha512-l+yybyZLV7zTD6EuGxoXsilpER1ctMCpdOqjSYNigJJma39ha85fzCtYccPx06oR1u7uCQLOcUAFFzvfXVBmuQ==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@1.1.13: + resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==} + + brace-expansion@2.0.3: + resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==} + + brace-expansion@5.0.5: + resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + engines: {node: 18 || 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + builtin-modules@1.1.1: + resolution: {integrity: sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==} + engines: {node: '>=0.10.0'} + + builtins@1.0.3: + resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001784: + resolution: {integrity: sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} + + cjs-module-lexer@2.2.0: + resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} + + clean-css@5.3.3: + resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} + engines: {node: '>= 10.0'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cmd-extension@1.0.2: + resolution: {integrity: sha512-iWDjmP8kvsMdBmLTHxFaqXikO8EdFRDfim7k6vUHglY/2xJ5jLrPsnQGijdfp4U+sr/BeecG0wKm02dSIAeQ1g==} + engines: {node: '>=10'} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + collect-v8-coverage@1.0.3: + resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} + peerDependencies: + '@types/node': '>=12' + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + comment-parser@1.4.1: + resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} + engines: {node: '>= 12.0.0'} + + comver-to-semver@1.0.0: + resolution: {integrity: sha512-gcGtbRxjwROQOdXLUWH1fQAXqThUVRZ219aAwgtX3KfYw429/Zv6EIJRf5TBSzWdAGwePmqH7w70WTaX4MDqag==} + engines: {node: '>=12.17'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-select@4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debuglog@1.0.1: + resolution: {integrity: sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + + dedent@1.7.2: + resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + dependency-path@9.2.8: + resolution: {integrity: sha512-S0OhIK7sIyAsph8hVH/LMCTDL3jozKtlrPx3dMQrlE2nAlXTquTT+AcOufphDMTQqLkfn4acvfiem9I1IWZ4jQ==} + engines: {node: '>=14.6'} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + + diff@4.0.4: + resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} + engines: {node: '>=0.3.1'} + + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dom-converter@0.2.0: + resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==} + + dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + + domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dotenv@16.4.7: + resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + electron-to-chromium@1.5.331: + resolution: {integrity: sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==} + + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + encode-registry@3.0.1: + resolution: {integrity: sha512-6qOwkl1g0fv0DN3Y3ggr2EaZXN71aoAqPp3p/pVaWSBSIo+YjLOWN61Fva43oVyQNPf7kgm8lkudzlzojwE2jw==} + engines: {node: '>=10'} + + enhanced-resolve@5.20.1: + resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} + engines: {node: '>=10.13.0'} + + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.3.1: + resolution: {integrity: sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + eslint: '*' + peerDependenciesMeta: + eslint: + optional: true + + eslint-plugin-header@3.1.1: + resolution: {integrity: sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg==} + peerDependencies: + eslint: '>=7.7.0' + + eslint-plugin-headers@1.2.1: + resolution: {integrity: sha512-1L41t3DPrXFP6YLK+sAj0xDMGVHpQwI+uGefDwc1bKP91q65AIZoXzQgI7MjZJxB6sK8/vYhXMD8x0V8xLNxJA==} + engines: {node: ^16.0.0 || >= 18.0.0} + peerDependencies: + eslint: '>=7' + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + + eslint-plugin-jsdoc@50.6.11: + resolution: {integrity: sha512-k4+MnBCGR8cuIB5MZ++FGd4gbXxjob2rX1Nq0q3nWFF4xSGZENTgTLZSjb+u9B8SAnP6lpGV2FJrBjllV3pVSg==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 + + eslint-plugin-promise@7.2.1: + resolution: {integrity: sha512-SWKjd+EuvWkYaS+uN2csvj0KoP43YTu7+phKQ5v+xw6+A0gutVX2yqCeCkC3uLCJFiPfR2dD8Es5L7yUsmvEaA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-plugin-tsdoc@0.5.2: + resolution: {integrity: sha512-BlvqjWZdBJDIPO/YU3zcPCF23CvjYT3gyu63yo6b609NNV3D1b6zceAREy2xnweuBoDpZcLNuPyAUq9cvx6bbQ==} + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.25.1: + resolution: {integrity: sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + eslint@9.37.0: + resolution: {integrity: sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exit-x@0.2.2: + resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} + engines: {node: '>= 0.8.0'} + + expect@30.3.0: + resolution: {integrity: sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fast-wrap-ansi@0.2.0: + resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fs-extra@11.3.4: + resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==} + engines: {node: '>=14.14'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-npm-tarball-url@2.1.0: + resolution: {integrity: sha512-ro+DiMu5DXgRBabqXupW38h7WPZ9+Ad8UjwhvsmmN8w1sU7ab0nzAXvVZ4kqYg57OrqomRtJvepX5/xvFKNtjA==} + engines: {node: '>=12.17'} + + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + git-repo-info@2.1.1: + resolution: {integrity: sha512-8aCohiDo4jwjOwma4FmYFd3i97urZulL8XL24nIPxuE+GZnfsAyy/g2Shqx6OjUiFKUXZM+Yy+KHnOmmA3FVcg==} + engines: {node: '>= 4.0'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graceful-fs@4.2.4: + resolution: {integrity: sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + + hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + html-minifier-terser@6.1.0: + resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} + engines: {node: '>=12'} + hasBin: true + + html-webpack-plugin@5.5.4: + resolution: {integrity: sha512-3wNSaVVxdxcu0jd4FpQFoICdqgxs4zIQQvj+2yQKFfBOnLETQ6X5CDWdeasuGlSsooFlMkEioWDTqBv1wvw5Iw==} + engines: {node: '>=10.13.0'} + peerDependencies: + webpack: ^5.20.0 + + htmlparser2@6.1.0: + resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + ignore-walk@5.0.1: + resolution: {integrity: sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + ignore@5.1.9: + resolution: {integrity: sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==} + engines: {node: '>= 4'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + individual@3.0.0: + resolution: {integrity: sha512-rUY5vtT748NMRbEMrTNiFfy29BgGZwGXUi2NFUVMWQrogSLzlJvQV9eeMWi+g1aVaQ53tpyLAQtd5x/JH0Nh1g==} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jest-changed-files@30.3.0: + resolution: {integrity: sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-circus@30.3.0: + resolution: {integrity: sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-config@30.3.0: + resolution: {integrity: sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@types/node': '*' + esbuild-register: '>=3.4.0' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + esbuild-register: + optional: true + ts-node: + optional: true + + jest-diff@30.3.0: + resolution: {integrity: sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-docblock@30.2.0: + resolution: {integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-each@30.3.0: + resolution: {integrity: sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-environment-node@30.3.0: + resolution: {integrity: sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-haste-map@30.3.0: + resolution: {integrity: sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-junit@12.3.0: + resolution: {integrity: sha512-+NmE5ogsEjFppEl90GChrk7xgz8xzvF0f+ZT5AnhW6suJC93gvQtmQjfyjDnE0Z2nXJqEkxF0WXlvjG/J+wn/g==} + engines: {node: '>=10.12.0'} + + jest-leak-detector@30.3.0: + resolution: {integrity: sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-matcher-utils@30.3.0: + resolution: {integrity: sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-message-util@30.3.0: + resolution: {integrity: sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-mock@30.3.0: + resolution: {integrity: sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@30.0.1: + resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-resolve-dependencies@30.3.0: + resolution: {integrity: sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-resolve@30.3.0: + resolution: {integrity: sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-runner@30.3.0: + resolution: {integrity: sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-runtime@30.3.0: + resolution: {integrity: sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-snapshot@30.3.0: + resolution: {integrity: sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-util@30.3.0: + resolution: {integrity: sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-validate@30.3.0: + resolution: {integrity: sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-watcher@30.3.0: + resolution: {integrity: sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + jest-worker@30.3.0: + resolution: {integrity: sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsdoc-type-pratt-parser@4.1.0: + resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} + engines: {node: '>=12.0.0'} + + jsep@1.4.0: + resolution: {integrity: sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==} + engines: {node: '>= 10.16.0'} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + + jsonpath-plus@10.3.0: + resolution: {integrity: sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==} + engines: {node: '>=18.0.0'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + jszip@3.8.0: + resolution: {integrity: sha512-cnpQrXvFSLdsR9KR5/x7zdf6c3m8IhZfZzSblFEHSqBaVwD2nvJ4CuCKLyvKvwBgZm08CgfSoiTBQLm5WW9hGw==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + load-json-file@6.2.0: + resolution: {integrity: sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==} + engines: {node: '>=8'} + + loader-runner@4.3.1: + resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} + engines: {node: '>=6.11.5'} + + local-eslint-config@file:../../../eslint/local-eslint-config: + resolution: {directory: ../../../eslint/local-eslint-config, type: directory} + peerDependencies: + eslint: ^9.25.1 + typescript: '>=4.7.0' + + local-node-rig@file:../../../rigs/local-node-rig: + resolution: {directory: ../../../rigs/local-node-rig, type: directory} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + + map-age-cleaner@0.1.3: + resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==} + engines: {node: '>=6'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mem@8.1.1: + resolution: {integrity: sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==} + engines: {node: '>=10'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-fn@3.1.0: + resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==} + engines: {node: '>=8'} + + minimatch@10.2.3: + resolution: {integrity: sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==} + engines: {node: 18 || 20 || >=22} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + ndjson@2.0.0: + resolution: {integrity: sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ==} + engines: {node: '>=10'} + hasBin: true + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-exports-info@1.6.0: + resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} + engines: {node: '>= 0.4'} + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-releases@2.0.37: + resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==} + + normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + + normalize-package-data@3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-bundled@2.0.1: + resolution: {integrity: sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + npm-normalize-package-bin@1.0.1: + resolution: {integrity: sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==} + + npm-normalize-package-bin@2.0.0: + resolution: {integrity: sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + npm-package-arg@6.1.1: + resolution: {integrity: sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==} + + npm-packlist@5.1.3: + resolution: {integrity: sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + os-homedir@1.0.2: + resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} + engines: {node: '>=0.10.0'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + osenv@0.1.5: + resolution: {integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==} + deprecated: This package is no longer supported. + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-defer@1.0.0: + resolution: {integrity: sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==} + engines: {node: '>=4'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-reflect@2.1.0: + resolution: {integrity: sha512-paHV8NUz8zDHu5lhr/ngGWQiW067DK/+IbJ+RfZ4k+s8y4EKyYCz8pGYWjxCg35eHztpJAt+NUgvN4L+GCbPlg==} + engines: {node: '>=8'} + + p-settle@4.1.1: + resolution: {integrity: sha512-6THGh13mt3gypcNMm0ADqVNCcYa3BK6DWsuJWFCuEKP1rpY+OKGp7gaZwVmLspmic01+fsg/fN57MfvDzZ/PuQ==} + engines: {node: '>=10'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-imports-exports@0.2.4: + resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-statements@1.0.11: + resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} + + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-name@1.0.0: + resolution: {integrity: sha512-/dcAb5vMXH0f51yvMuSUqFpxUcA8JelbRmE5mW/p4CUJxrNgK24IkstnV7ENtg2IDGBOu6izKTG6eilbnbNKWQ==} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pnpm-sync-lib@0.3.4: + resolution: {integrity: sha512-ZgRR+j6B+VUrolPBswPvXBnCyxg39Zfw3ShNCTuCrOFG1V29V4EyXaA1rDDMjdhpF85QYp2NEUjeHAm02A2E/A==} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + pretty-error@4.0.0: + resolution: {integrity: sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==} + + pretty-format@30.3.0: + resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pure-rand@7.0.1: + resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + ramda@0.27.2: + resolution: {integrity: sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA==} + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + read-package-json@2.1.2: + resolution: {integrity: sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==} + deprecated: This package is no longer supported. Please use @npmcli/package-json instead. + + read-package-tree@5.1.6: + resolution: {integrity: sha512-FCX1aT3GWyY658wzDICef4p+n0dB+ENRct8E/Qyvppj6xVpOYerBHfUu7OP5Rt1/393Tdglguf5ju5DEX4wZNg==} + deprecated: The functionality that this package provided is now in @npmcli/arborist + + read-yaml-file@2.1.0: + resolution: {integrity: sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==} + engines: {node: '>=10.13'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdir-scoped-modules@1.1.0: + resolution: {integrity: sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==} + deprecated: This functionality has been moved to @npmcli/fs + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + relateurl@0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + + renderkid@3.0.0: + resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.6: + resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} + engines: {node: '>= 0.4'} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfc4648@1.5.4: + resolution: {integrity: sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@6.6.7: + resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} + engines: {npm: '>=2.0.0'} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-execa@0.1.2: + resolution: {integrity: sha512-vdTshSQ2JsRCgT8eKZWNJIL26C6bVqy1SOmuCMlKHegVeo8KYRobRrefOdUq9OozSPUUiSxrylteeRmLOMFfWg==} + engines: {node: '>=12'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + schema-utils@4.3.3: + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} + engines: {node: '>= 10.13.0'} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-immediate-shim@1.0.1: + resolution: {integrity: sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ==} + engines: {node: '>=0.10.0'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + sort-keys@4.2.0: + resolution: {integrity: sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==} + engines: {node: '>=8'} + + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-expression-parse@4.0.0: + resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} + + spdx-license-ids@3.0.23: + resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==} + + split2@3.2.2: + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + ssri@10.0.5: + resolution: {integrity: sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ssri@8.0.1: + resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} + engines: {node: '>= 8'} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + strict-uri-encode@2.0.0: + resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} + engines: {node: '>=4'} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@5.2.0: + resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} + engines: {node: '>=6'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + synckit@0.11.12: + resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} + engines: {node: ^14.18.0 || >=16.0.0} + + tapable@1.1.3: + resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==} + engines: {node: '>=6'} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + tapable@2.3.2: + resolution: {integrity: sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==} + engines: {node: '>=6'} + + tar@7.5.13: + resolution: {integrity: sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==} + engines: {node: '>=18'} + + terser-webpack-plugin@5.4.0: + resolution: {integrity: sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@5.46.1: + resolution: {integrity: sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==} + engines: {node: '>=10'} + hasBin: true + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + through2@4.0.2: + resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + true-case-path@2.2.1: + resolution: {integrity: sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q==} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tslint@5.20.1: + resolution: {integrity: sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==} + engines: {node: '>=4.8.0'} + hasBin: true + peerDependencies: + typescript: '>=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev' + + tsutils@2.29.0: + resolution: {integrity: sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==} + peerDependencies: + typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + + typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unrs-resolver@1.11.1: + resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + utila@0.4.0: + resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + validate-npm-package-name@3.0.0: + resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} + + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + + watchpack@2.4.0: + resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} + engines: {node: '>=10.13.0'} + + watchpack@2.5.1: + resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} + engines: {node: '>=10.13.0'} + + webpack-sources@3.3.4: + resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} + engines: {node: '>=10.13.0'} + + webpack@5.105.4: + resolution: {integrity: sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + write-yaml-file@4.2.0: + resolution: {integrity: sha512-LwyucHy0uhWqbrOkh9cBluZBeNVxzHjDaE9mwepZG3n3ZlbM4v3ndrFw51zW/NXYFFqP+QWZ72ihtLWTh05e4Q==} + engines: {node: '>=10.13'} + + xml@1.0.1: + resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.2': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@bcoe/v8-coverage@0.2.3': {} + + '@emnapi/core@1.9.2': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.9.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@es-joy/jsdoccomment@0.49.0': + dependencies: + comment-parser: 1.4.1 + esquery: 1.7.0 + jsdoc-type-pratt-parser: 4.1.0 + + '@eslint-community/eslint-utils@4.9.1(eslint@9.25.1)': + dependencies: + eslint: 9.25.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.1(eslint@9.37.0)': + dependencies: + eslint: 9.37.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.20.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.2.3': {} + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.13.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.16.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.5': + dependencies: + ajv: 6.14.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.25.1': {} + + '@eslint/js@9.37.0': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.2.8': + dependencies: + '@eslint/core': 0.13.0 + levn: 0.4.1 + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@inquirer/ansi@2.0.5': {} + + '@inquirer/checkbox@5.1.3(@types/node@20.17.19)': + dependencies: + '@inquirer/ansi': 2.0.5 + '@inquirer/core': 11.1.8(@types/node@20.17.19) + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@20.17.19) + optionalDependencies: + '@types/node': 20.17.19 + + '@inquirer/confirm@6.0.11(@types/node@20.17.19)': + dependencies: + '@inquirer/core': 11.1.8(@types/node@20.17.19) + '@inquirer/type': 4.0.5(@types/node@20.17.19) + optionalDependencies: + '@types/node': 20.17.19 + + '@inquirer/core@11.1.8(@types/node@20.17.19)': + dependencies: + '@inquirer/ansi': 2.0.5 + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@20.17.19) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.0 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 20.17.19 + + '@inquirer/figures@2.0.5': {} + + '@inquirer/input@5.0.11(@types/node@20.17.19)': + dependencies: + '@inquirer/core': 11.1.8(@types/node@20.17.19) + '@inquirer/type': 4.0.5(@types/node@20.17.19) + optionalDependencies: + '@types/node': 20.17.19 + + '@inquirer/search@4.1.7(@types/node@20.17.19)': + dependencies: + '@inquirer/core': 11.1.8(@types/node@20.17.19) + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@20.17.19) + optionalDependencies: + '@types/node': 20.17.19 + + '@inquirer/select@5.1.3(@types/node@20.17.19)': + dependencies: + '@inquirer/ansi': 2.0.5 + '@inquirer/core': 11.1.8(@types/node@20.17.19) + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@20.17.19) + optionalDependencies: + '@types/node': 20.17.19 + + '@inquirer/type@4.0.5(@types/node@20.17.19)': + optionalDependencies: + '@types/node': 20.17.19 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.2 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jest/console@30.3.0': + dependencies: + '@jest/types': 30.3.0 + '@types/node': 20.17.19 + chalk: 4.1.2 + jest-message-util: 30.3.0 + jest-util: 30.3.0 + slash: 3.0.0 + + '@jest/core@30.3.0': + dependencies: + '@jest/console': 30.3.0 + '@jest/pattern': 30.0.1 + '@jest/reporters': 30.3.0 + '@jest/test-result': 30.3.0(@types/node@20.17.19) + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 20.17.19 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 4.4.0 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-changed-files: 30.3.0 + jest-config: 30.3.0(@types/node@20.17.19) + jest-haste-map: 30.3.0 + jest-message-util: 30.3.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.3.0 + jest-resolve-dependencies: 30.3.0 + jest-runner: 30.3.0 + jest-runtime: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + jest-watcher: 30.3.0 + pretty-format: 30.3.0 + slash: 3.0.0 + transitivePeerDependencies: + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + + '@jest/diff-sequences@30.3.0': {} + + '@jest/environment@30.3.0': + dependencies: + '@jest/fake-timers': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 20.17.19 + jest-mock: 30.3.0 + + '@jest/expect-utils@30.3.0': + dependencies: + '@jest/get-type': 30.1.0 + + '@jest/expect@30.3.0': + dependencies: + expect: 30.3.0 + jest-snapshot: 30.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@30.3.0': + dependencies: + '@jest/types': 30.3.0 + '@sinonjs/fake-timers': 15.3.0 + '@types/node': 20.17.19 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 + jest-util: 30.3.0 + + '@jest/get-type@30.1.0': {} + + '@jest/globals@30.3.0': + dependencies: + '@jest/environment': 30.3.0 + '@jest/expect': 30.3.0 + '@jest/types': 30.3.0 + jest-mock: 30.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/pattern@30.0.1': + dependencies: + '@types/node': 20.17.19 + jest-regex-util: 30.0.1 + + '@jest/reporters@30.3.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 30.3.0 + '@jest/test-result': 30.3.0(@types/node@20.17.19) + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@jridgewell/trace-mapping': 0.3.31 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/node': 20.17.19 + chalk: 4.1.2 + collect-v8-coverage: 1.0.3(@types/node@20.17.19) + exit-x: 0.2.2 + glob: 10.5.0 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + jest-message-util: 30.3.0 + jest-util: 30.3.0 + jest-worker: 30.3.0 + slash: 3.0.0 + string-length: 4.0.2 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@30.0.5': + dependencies: + '@sinclair/typebox': 0.34.49 + + '@jest/snapshot-utils@30.3.0': + dependencies: + '@jest/types': 30.3.0 + chalk: 4.1.2 + graceful-fs: 4.2.11 + natural-compare: 1.4.0 + + '@jest/source-map@30.0.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@30.3.0(@types/node@20.17.19)': + dependencies: + '@jest/console': 30.3.0 + '@jest/types': 30.3.0 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.3(@types/node@20.17.19) + jest-haste-map: 30.3.0 + jest-resolve: 30.3.0 + transitivePeerDependencies: + - '@types/node' + + '@jest/test-sequencer@30.3.0(@types/node@20.17.19)': + dependencies: + '@jest/test-result': 30.3.0(@types/node@20.17.19) + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + slash: 3.0.0 + transitivePeerDependencies: + - '@types/node' + + '@jest/transform@30.3.0': + dependencies: + '@babel/core': 7.29.0 + '@jest/types': 30.3.0 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 7.0.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + jest-regex-util: 30.0.1 + jest-util: 30.3.0 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + + '@jest/types@30.3.0': + dependencies: + '@jest/pattern': 30.0.1 + '@jest/schemas': 30.0.5 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 20.17.19 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jsep-plugin/assignment@1.3.0(jsep@1.4.0)': + dependencies: + jsep: 1.4.0 + + '@jsep-plugin/regex@1.0.4(jsep@1.4.0)': + dependencies: + jsep: 1.4.0 + + '@microsoft/api-extractor-model@file:../../../libraries/api-extractor-model(@types/node@20.17.19)': + dependencies: + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.1 + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + transitivePeerDependencies: + - '@types/node' + + '@microsoft/api-extractor@file:../../../apps/api-extractor(@types/node@20.17.19)': + dependencies: + '@microsoft/api-extractor-model': file:../../../libraries/api-extractor-model(@types/node@20.17.19) + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.1 + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + '@rushstack/rig-package': file:../../../libraries/rig-package + '@rushstack/terminal': file:../../../libraries/terminal(@types/node@20.17.19) + '@rushstack/ts-command-line': file:../../../libraries/ts-command-line(@types/node@20.17.19) + diff: 8.0.4 + minimatch: 10.2.3 + resolve: 1.22.11 + semver: 7.7.4 + source-map: 0.6.1 + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/node' + + '@microsoft/rush-lib@file:../../../libraries/rush-lib(@types/node@20.17.19)': + dependencies: + '@inquirer/checkbox': 5.1.3(@types/node@20.17.19) + '@inquirer/confirm': 6.0.11(@types/node@20.17.19) + '@inquirer/input': 5.0.11(@types/node@20.17.19) + '@inquirer/search': 4.1.7(@types/node@20.17.19) + '@inquirer/select': 5.1.3(@types/node@20.17.19) + '@pnpm/link-bins': 5.3.25 + '@rushstack/credential-cache': file:../../../libraries/credential-cache(@types/node@20.17.19) + '@rushstack/heft-config-file': file:../../../libraries/heft-config-file(@types/node@20.17.19) + '@rushstack/lookup-by-path': file:../../../libraries/lookup-by-path(@types/node@20.17.19) + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + '@rushstack/npm-check-fork': file:../../../libraries/npm-check-fork(@types/node@20.17.19) + '@rushstack/package-deps-hash': file:../../../libraries/package-deps-hash(@types/node@20.17.19) + '@rushstack/package-extractor': file:../../../libraries/package-extractor(@types/node@20.17.19) + '@rushstack/rig-package': file:../../../libraries/rig-package + '@rushstack/rush-pnpm-kit-v10': file:../../../libraries/rush-pnpm-kit-v10 + '@rushstack/rush-pnpm-kit-v8': file:../../../libraries/rush-pnpm-kit-v8 + '@rushstack/rush-pnpm-kit-v9': file:../../../libraries/rush-pnpm-kit-v9 + '@rushstack/stream-collator': file:../../../libraries/stream-collator(@types/node@20.17.19) + '@rushstack/terminal': file:../../../libraries/terminal(@types/node@20.17.19) + '@rushstack/ts-command-line': file:../../../libraries/ts-command-line(@types/node@20.17.19) + '@yarnpkg/lockfile': 1.0.2 + dependency-path: 9.2.8 + dotenv: 16.4.7 + fast-glob: 3.3.3 + git-repo-info: 2.1.1 + https-proxy-agent: 5.0.1 + ignore: 5.1.9 + js-yaml: 4.1.1 + npm-package-arg: 6.1.1 + object-hash: 3.0.0 + pnpm-sync-lib: 0.3.4 + read-package-tree: 5.1.6 + rxjs: 6.6.7 + semver: 7.7.4 + ssri: 8.0.1 + strict-uri-encode: 2.0.0 + tapable: 2.2.1 + tar: 7.5.13 + true-case-path: 2.2.1 + transitivePeerDependencies: + - '@types/node' + - supports-color + + '@microsoft/tsdoc-config@0.18.1': + dependencies: + '@microsoft/tsdoc': 0.16.0 + ajv: 8.18.0 + jju: 1.4.0 + resolve: 1.22.11 + + '@microsoft/tsdoc@0.16.0': {} + + '@napi-rs/wasm-runtime@0.2.12': + dependencies: + '@emnapi/core': 1.9.2 + '@emnapi/runtime': 1.9.2 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pkgr/core@0.2.9': {} + + '@pnpm/constants@1001.3.1': {} + + '@pnpm/constants@7.1.1': {} + + '@pnpm/crypto.base32-hash@1.0.1': + dependencies: + rfc4648: 1.5.4 + + '@pnpm/crypto.base32-hash@2.0.0': + dependencies: + rfc4648: 1.5.4 + + '@pnpm/crypto.base32-hash@3.0.1': + dependencies: + '@pnpm/crypto.polyfill': 1.0.0 + rfc4648: 1.5.4 + + '@pnpm/crypto.hash@1000.1.1': + dependencies: + '@pnpm/crypto.polyfill': 1000.1.0 + '@pnpm/graceful-fs': 1000.0.0 + ssri: 10.0.5 + + '@pnpm/crypto.hash@1000.2.2': + dependencies: + '@pnpm/crypto.polyfill': 1000.1.0 + '@pnpm/graceful-fs': 1000.1.0 + ssri: 10.0.5 + + '@pnpm/crypto.polyfill@1.0.0': {} + + '@pnpm/crypto.polyfill@1000.1.0': {} + + '@pnpm/dependency-path@1000.0.9': + dependencies: + '@pnpm/crypto.hash': 1000.1.1 + '@pnpm/types': 1000.6.0 + semver: 7.7.4 + + '@pnpm/dependency-path@1001.1.10': + dependencies: + '@pnpm/crypto.hash': 1000.2.2 + '@pnpm/types': 1001.3.0 + semver: 7.7.4 + + '@pnpm/dependency-path@2.1.8': + dependencies: + '@pnpm/crypto.base32-hash': 2.0.0 + '@pnpm/types': 9.4.2 + encode-registry: 3.0.1 + semver: 7.7.4 + + '@pnpm/dependency-path@5.1.7': + dependencies: + '@pnpm/crypto.base32-hash': 3.0.1 + '@pnpm/types': 12.2.0 + semver: 7.7.4 + + '@pnpm/error@1.4.0': {} + + '@pnpm/error@1000.1.0': + dependencies: + '@pnpm/constants': 1001.3.1 + + '@pnpm/error@5.0.3': + dependencies: + '@pnpm/constants': 7.1.1 + + '@pnpm/git-utils@1.0.0': + dependencies: + execa: safe-execa@0.1.2 + + '@pnpm/git-utils@1000.0.0': + dependencies: + execa: safe-execa@0.1.2 + + '@pnpm/graceful-fs@1000.0.0': + dependencies: + graceful-fs: 4.2.11 + + '@pnpm/graceful-fs@1000.1.0': + dependencies: + graceful-fs: 4.2.11 + + '@pnpm/link-bins@5.3.25': + dependencies: + '@pnpm/error': 1.4.0 + '@pnpm/package-bins': 4.1.0 + '@pnpm/read-modules-dir': 2.0.3 + '@pnpm/read-package-json': 4.0.0 + '@pnpm/read-project-manifest': 1.1.7 + '@pnpm/types': 6.4.0 + '@zkochan/cmd-shim': 5.4.1 + is-subdir: 1.2.0 + is-windows: 1.0.2 + mz: 2.7.0 + normalize-path: 3.0.0 + p-settle: 4.1.1 + ramda: 0.27.2 + + '@pnpm/lockfile-file@8.1.8(@pnpm/logger@5.0.0)': + dependencies: + '@pnpm/constants': 7.1.1 + '@pnpm/dependency-path': 2.1.8 + '@pnpm/error': 5.0.3 + '@pnpm/git-utils': 1.0.0 + '@pnpm/lockfile-types': 5.1.5 + '@pnpm/logger': 5.0.0 + '@pnpm/merge-lockfile-changes': 5.0.7 + '@pnpm/types': 9.4.2 + '@pnpm/util.lex-comparator': 1.0.0 + '@zkochan/rimraf': 2.1.3 + comver-to-semver: 1.0.0 + js-yaml: '@zkochan/js-yaml@0.0.6' + normalize-path: 3.0.0 + ramda: '@pnpm/ramda@0.28.1' + semver: 7.7.4 + sort-keys: 4.2.0 + strip-bom: 4.0.0 + write-file-atomic: 5.0.1 + + '@pnpm/lockfile-types@5.1.5': + dependencies: + '@pnpm/types': 9.4.2 + + '@pnpm/lockfile.fs@1001.1.32(@pnpm/logger@1001.0.1)': + dependencies: + '@pnpm/constants': 1001.3.1 + '@pnpm/dependency-path': 1001.1.10 + '@pnpm/error': 1000.1.0 + '@pnpm/git-utils': 1000.0.0 + '@pnpm/lockfile.merger': 1001.0.20 + '@pnpm/lockfile.types': 1002.1.0 + '@pnpm/lockfile.utils': 1004.0.3 + '@pnpm/logger': 1001.0.1 + '@pnpm/object.key-sorting': 1000.0.1 + '@pnpm/types': 1001.3.0 + '@zkochan/rimraf': 3.0.2 + comver-to-semver: 1.0.0 + js-yaml: '@zkochan/js-yaml@0.0.11' + normalize-path: 3.0.0 + ramda: '@pnpm/ramda@0.28.1' + semver: 7.7.4 + strip-bom: 4.0.0 + write-file-atomic: 5.0.1 + + '@pnpm/lockfile.merger@1001.0.20': + dependencies: + '@pnpm/lockfile.types': 1002.1.0 + '@pnpm/types': 1001.3.0 + comver-to-semver: 1.0.0 + ramda: '@pnpm/ramda@0.28.1' + semver: 7.7.4 + + '@pnpm/lockfile.types@1001.1.0': + dependencies: + '@pnpm/patching.types': 1000.1.0 + '@pnpm/types': 1000.7.0 + + '@pnpm/lockfile.types@1002.1.0': + dependencies: + '@pnpm/patching.types': 1000.1.0 + '@pnpm/resolver-base': 1005.4.1 + '@pnpm/types': 1001.3.0 + + '@pnpm/lockfile.types@900.0.0': + dependencies: + '@pnpm/patching.types': 900.0.0 + '@pnpm/types': 900.0.0 + + '@pnpm/lockfile.utils@1004.0.3': + dependencies: + '@pnpm/dependency-path': 1001.1.10 + '@pnpm/lockfile.types': 1002.1.0 + '@pnpm/pick-fetcher': 1001.0.0 + '@pnpm/resolver-base': 1005.4.1 + '@pnpm/types': 1001.3.0 + get-npm-tarball-url: 2.1.0 + ramda: '@pnpm/ramda@0.28.1' + + '@pnpm/logger@1001.0.1': + dependencies: + bole: 5.0.28 + split2: 4.2.0 + + '@pnpm/logger@5.0.0': + dependencies: + bole: 5.0.28 + ndjson: 2.0.0 + + '@pnpm/merge-lockfile-changes@5.0.7': + dependencies: + '@pnpm/lockfile-types': 5.1.5 + comver-to-semver: 1.0.0 + ramda: '@pnpm/ramda@0.28.1' + semver: 7.7.4 + + '@pnpm/object.key-sorting@1000.0.1': + dependencies: + '@pnpm/util.lex-comparator': 3.0.2 + sort-keys: 4.2.0 + + '@pnpm/package-bins@4.1.0': + dependencies: + '@pnpm/types': 6.4.0 + fast-glob: 3.3.3 + is-subdir: 1.2.0 + + '@pnpm/patching.types@1000.1.0': {} + + '@pnpm/patching.types@900.0.0': {} + + '@pnpm/pick-fetcher@1001.0.0': {} + + '@pnpm/ramda@0.28.1': {} + + '@pnpm/read-modules-dir@2.0.3': + dependencies: + mz: 2.7.0 + + '@pnpm/read-package-json@4.0.0': + dependencies: + '@pnpm/error': 1.4.0 + '@pnpm/types': 6.4.0 + load-json-file: 6.2.0 + normalize-package-data: 3.0.3 + + '@pnpm/read-project-manifest@1.1.7': + dependencies: + '@pnpm/error': 1.4.0 + '@pnpm/types': 6.4.0 + '@pnpm/write-project-manifest': 1.1.7 + detect-indent: 6.1.0 + fast-deep-equal: 3.1.3 + graceful-fs: 4.2.4 + is-windows: 1.0.2 + json5: 2.2.3 + parse-json: 5.2.0 + read-yaml-file: 2.1.0 + sort-keys: 4.2.0 + strip-bom: 4.0.0 + + '@pnpm/resolver-base@1005.4.1': + dependencies: + '@pnpm/types': 1001.3.0 + + '@pnpm/types@1000.6.0': {} + + '@pnpm/types@1000.7.0': {} + + '@pnpm/types@1001.3.0': {} + + '@pnpm/types@12.2.0': {} + + '@pnpm/types@6.4.0': {} + + '@pnpm/types@8.9.0': {} + + '@pnpm/types@9.4.2': {} + + '@pnpm/types@900.0.0': {} + + '@pnpm/util.lex-comparator@1.0.0': {} + + '@pnpm/util.lex-comparator@3.0.2': {} + + '@pnpm/write-project-manifest@1.1.7': + dependencies: + '@pnpm/types': 6.4.0 + json5: 2.2.3 + mz: 2.7.0 + write-file-atomic: 3.0.3 + write-yaml-file: 4.2.0 + + '@rtsao/scc@1.1.0': {} + + '@rushstack/credential-cache@file:../../../libraries/credential-cache(@types/node@20.17.19)': + dependencies: + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + transitivePeerDependencies: + - '@types/node' + + '@rushstack/eslint-config@file:../../../eslint/eslint-config(eslint@9.25.1)(typescript@4.9.5)': + dependencies: + '@rushstack/eslint-patch': file:../../../eslint/eslint-patch + '@rushstack/eslint-plugin': file:../../../eslint/eslint-plugin(eslint@9.25.1)(typescript@4.9.5) + '@rushstack/eslint-plugin-packlets': file:../../../eslint/eslint-plugin-packlets(eslint@9.25.1)(typescript@4.9.5) + '@rushstack/eslint-plugin-security': file:../../../eslint/eslint-plugin-security(eslint@9.25.1)(typescript@4.9.5) + '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.25.1)(typescript@4.9.5))(eslint@9.25.1)(typescript@4.9.5) + '@typescript-eslint/parser': 8.56.1(eslint@9.25.1)(typescript@4.9.5) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@4.9.5) + '@typescript-eslint/utils': 8.56.1(eslint@9.25.1)(typescript@4.9.5) + eslint: 9.25.1 + eslint-plugin-promise: 7.2.1(eslint@9.25.1) + eslint-plugin-react: 7.37.5(eslint@9.25.1) + eslint-plugin-tsdoc: 0.5.2(eslint@9.25.1)(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@rushstack/eslint-config@file:../../../eslint/eslint-config(eslint@9.37.0)(typescript@5.8.3)': + dependencies: + '@rushstack/eslint-patch': file:../../../eslint/eslint-patch + '@rushstack/eslint-plugin': file:../../../eslint/eslint-plugin(eslint@9.37.0)(typescript@5.8.3) + '@rushstack/eslint-plugin-packlets': file:../../../eslint/eslint-plugin-packlets(eslint@9.37.0)(typescript@5.8.3) + '@rushstack/eslint-plugin-security': file:../../../eslint/eslint-plugin-security(eslint@9.37.0)(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.37.0)(typescript@5.8.3))(eslint@9.37.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.56.1(eslint@9.37.0)(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.3) + eslint: 9.37.0 + eslint-plugin-promise: 7.2.1(eslint@9.37.0) + eslint-plugin-react: 7.37.5(eslint@9.37.0) + eslint-plugin-tsdoc: 0.5.2(eslint@9.37.0)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@rushstack/eslint-patch@file:../../../eslint/eslint-patch': {} + + '@rushstack/eslint-plugin-packlets@file:../../../eslint/eslint-plugin-packlets(eslint@9.25.1)(typescript@4.9.5)': + dependencies: + '@rushstack/tree-pattern': file:../../../libraries/tree-pattern + '@typescript-eslint/utils': 8.56.1(eslint@9.25.1)(typescript@4.9.5) + eslint: 9.25.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin-packlets@file:../../../eslint/eslint-plugin-packlets(eslint@9.37.0)(typescript@5.8.3)': + dependencies: + '@rushstack/tree-pattern': file:../../../libraries/tree-pattern + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.3) + eslint: 9.37.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin-security@file:../../../eslint/eslint-plugin-security(eslint@9.25.1)(typescript@4.9.5)': + dependencies: + '@rushstack/tree-pattern': file:../../../libraries/tree-pattern + '@typescript-eslint/utils': 8.56.1(eslint@9.25.1)(typescript@4.9.5) + eslint: 9.25.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin-security@file:../../../eslint/eslint-plugin-security(eslint@9.37.0)(typescript@5.8.3)': + dependencies: + '@rushstack/tree-pattern': file:../../../libraries/tree-pattern + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.3) + eslint: 9.37.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin@file:../../../eslint/eslint-plugin(eslint@9.25.1)(typescript@4.9.5)': + dependencies: + '@rushstack/tree-pattern': file:../../../libraries/tree-pattern + '@typescript-eslint/utils': 8.56.1(eslint@9.25.1)(typescript@4.9.5) + eslint: 9.25.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin@file:../../../eslint/eslint-plugin(eslint@9.37.0)(typescript@5.8.3)': + dependencies: + '@rushstack/tree-pattern': file:../../../libraries/tree-pattern + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.3) + eslint: 9.37.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/heft-api-extractor-plugin@file:../../../heft-plugins/heft-api-extractor-plugin(@rushstack/heft@file:../../../apps/heft(@types/node@20.17.19))(@types/node@20.17.19)': + dependencies: + '@rushstack/heft': file:../../../apps/heft(@types/node@20.17.19) + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + semver: 7.7.4 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/heft-config-file@file:../../../libraries/heft-config-file(@types/node@20.17.19)': + dependencies: + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + '@rushstack/rig-package': file:../../../libraries/rig-package + '@rushstack/terminal': file:../../../libraries/terminal(@types/node@20.17.19) + jsonpath-plus: 10.3.0 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/heft-jest-plugin@file:../../../heft-plugins/heft-jest-plugin(@rushstack/heft@file:../../../apps/heft(@types/node@20.17.19))(@types/jest@30.0.0)(@types/node@20.17.19)(jest-environment-node@30.3.0)': + dependencies: + '@jest/core': 30.3.0 + '@jest/reporters': 30.3.0 + '@jest/transform': 30.3.0 + '@rushstack/heft': file:../../../apps/heft(@types/node@20.17.19) + '@rushstack/heft-config-file': file:../../../libraries/heft-config-file(@types/node@20.17.19) + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + '@rushstack/terminal': file:../../../libraries/terminal(@types/node@20.17.19) + jest-config: 30.3.0(@types/node@20.17.19) + jest-resolve: 30.3.0 + jest-snapshot: 30.3.0 + optionalDependencies: + '@types/jest': 30.0.0 + jest-environment-node: 30.3.0 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - node-notifier + - supports-color + - ts-node + + '@rushstack/heft-lint-plugin@file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@file:../../../apps/heft(@types/node@20.17.19))(@types/node@20.17.19)': + dependencies: + '@rushstack/heft': file:../../../apps/heft(@types/node@20.17.19) + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + json-stable-stringify-without-jsonify: 1.0.1 + semver: 7.7.4 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/heft-node-rig@file:../../../rigs/heft-node-rig(@rushstack/heft@file:../../../apps/heft(@types/node@20.17.19))(@types/node@20.17.19)': + dependencies: + '@microsoft/api-extractor': file:../../../apps/api-extractor(@types/node@20.17.19) + '@rushstack/eslint-config': file:../../../eslint/eslint-config(eslint@9.37.0)(typescript@5.8.3) + '@rushstack/heft': file:../../../apps/heft(@types/node@20.17.19) + '@rushstack/heft-api-extractor-plugin': file:../../../heft-plugins/heft-api-extractor-plugin(@rushstack/heft@file:../../../apps/heft(@types/node@20.17.19))(@types/node@20.17.19) + '@rushstack/heft-jest-plugin': file:../../../heft-plugins/heft-jest-plugin(@rushstack/heft@file:../../../apps/heft(@types/node@20.17.19))(@types/jest@30.0.0)(@types/node@20.17.19)(jest-environment-node@30.3.0) + '@rushstack/heft-lint-plugin': file:../../../heft-plugins/heft-lint-plugin(@rushstack/heft@file:../../../apps/heft(@types/node@20.17.19))(@types/node@20.17.19) + '@rushstack/heft-typescript-plugin': file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@file:../../../apps/heft(@types/node@20.17.19))(@types/node@20.17.19) + '@types/jest': 30.0.0 + eslint: 9.37.0 + jest-environment-node: 30.3.0 + typescript: 5.8.3 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - jest-environment-jsdom + - jiti + - node-notifier + - supports-color + - ts-node + + '@rushstack/heft-typescript-plugin@file:../../../heft-plugins/heft-typescript-plugin(@rushstack/heft@file:../../../apps/heft(@types/node@20.17.19))(@types/node@20.17.19)': + dependencies: + '@rushstack/heft': file:../../../apps/heft(@types/node@20.17.19) + '@rushstack/heft-config-file': file:../../../libraries/heft-config-file(@types/node@20.17.19) + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + '@types/tapable': 1.0.6 + semver: 7.7.4 + tapable: 1.1.3 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/heft@file:../../../apps/heft(@types/node@20.17.19)': + dependencies: + '@rushstack/heft-config-file': file:../../../libraries/heft-config-file(@types/node@20.17.19) + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + '@rushstack/operation-graph': file:../../../libraries/operation-graph(@types/node@20.17.19) + '@rushstack/rig-package': file:../../../libraries/rig-package + '@rushstack/terminal': file:../../../libraries/terminal(@types/node@20.17.19) + '@rushstack/ts-command-line': file:../../../libraries/ts-command-line(@types/node@20.17.19) + '@types/tapable': 1.0.6 + fast-glob: 3.3.3 + git-repo-info: 2.1.1 + ignore: 5.1.9 + tapable: 1.1.3 + watchpack: 2.4.0 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/lookup-by-path@file:../../../libraries/lookup-by-path(@types/node@20.17.19)': + optionalDependencies: + '@types/node': 20.17.19 + + '@rushstack/node-core-library@file:../../../libraries/node-core-library(@types/node@20.17.19)': + dependencies: + ajv: 8.20.0 + ajv-draft-04: 1.0.0(ajv@8.20.0) + ajv-formats: 3.0.1 + fs-extra: 11.3.4 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.11 + semver: 7.7.4 + optionalDependencies: + '@types/node': 20.17.19 + + '@rushstack/npm-check-fork@file:../../../libraries/npm-check-fork(@types/node@20.17.19)': + dependencies: + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + semver: 7.7.4 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/operation-graph@file:../../../libraries/operation-graph(@types/node@20.17.19)': + dependencies: + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + '@rushstack/terminal': file:../../../libraries/terminal(@types/node@20.17.19) + optionalDependencies: + '@types/node': 20.17.19 + + '@rushstack/package-deps-hash@file:../../../libraries/package-deps-hash(@types/node@20.17.19)': + dependencies: + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + transitivePeerDependencies: + - '@types/node' + + '@rushstack/package-extractor@file:../../../libraries/package-extractor(@types/node@20.17.19)': + dependencies: + '@pnpm/link-bins': 5.3.25 + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + '@rushstack/terminal': file:../../../libraries/terminal(@types/node@20.17.19) + '@rushstack/ts-command-line': file:../../../libraries/ts-command-line(@types/node@20.17.19) + ignore: 5.1.9 + jszip: 3.8.0 + minimatch: 10.2.3 + npm-packlist: 5.1.3 + semver: 7.7.4 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/problem-matcher@file:../../../libraries/problem-matcher(@types/node@20.17.19)': + optionalDependencies: + '@types/node': 20.17.19 + + '@rushstack/rig-package@file:../../../libraries/rig-package': + dependencies: + jju: 1.4.0 + resolve: 1.22.11 + + '@rushstack/rush-pnpm-kit-v10@file:../../../libraries/rush-pnpm-kit-v10': + dependencies: + '@pnpm/dependency-path-pnpm-v10': '@pnpm/dependency-path@1000.0.9' + '@pnpm/lockfile.fs-pnpm-lock-v9': '@pnpm/lockfile.fs@1001.1.32(@pnpm/logger@1001.0.1)' + '@pnpm/logger': 1001.0.1 + + '@rushstack/rush-pnpm-kit-v8@file:../../../libraries/rush-pnpm-kit-v8': + dependencies: + '@pnpm/dependency-path-pnpm-v8': '@pnpm/dependency-path@2.1.8' + '@pnpm/lockfile-file-pnpm-lock-v6': '@pnpm/lockfile-file@8.1.8(@pnpm/logger@5.0.0)' + '@pnpm/logger': 5.0.0 + + '@rushstack/rush-pnpm-kit-v9@file:../../../libraries/rush-pnpm-kit-v9': + dependencies: + '@pnpm/dependency-path-pnpm-v9': '@pnpm/dependency-path@5.1.7' + '@pnpm/lockfile.fs-pnpm-lock-v9': '@pnpm/lockfile.fs@1001.1.32(@pnpm/logger@1001.0.1)' + '@pnpm/logger': 1001.0.1 + + '@rushstack/rush-sdk@file:../../../libraries/rush-sdk(@types/node@20.17.19)': + dependencies: + '@pnpm/lockfile.types-900': '@pnpm/lockfile.types@900.0.0' + '@rushstack/credential-cache': file:../../../libraries/credential-cache(@types/node@20.17.19) + '@rushstack/lookup-by-path': file:../../../libraries/lookup-by-path(@types/node@20.17.19) + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + '@rushstack/package-deps-hash': file:../../../libraries/package-deps-hash(@types/node@20.17.19) + '@rushstack/terminal': file:../../../libraries/terminal(@types/node@20.17.19) + tapable: 2.2.1 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/stream-collator@file:../../../libraries/stream-collator(@types/node@20.17.19)': + dependencies: + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + '@rushstack/terminal': file:../../../libraries/terminal(@types/node@20.17.19) + transitivePeerDependencies: + - '@types/node' + + '@rushstack/terminal@file:../../../libraries/terminal(@types/node@20.17.19)': + dependencies: + '@rushstack/node-core-library': file:../../../libraries/node-core-library(@types/node@20.17.19) + '@rushstack/problem-matcher': file:../../../libraries/problem-matcher(@types/node@20.17.19) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 20.17.19 + + '@rushstack/tree-pattern@file:../../../libraries/tree-pattern': {} + + '@rushstack/ts-command-line@file:../../../libraries/ts-command-line(@types/node@20.17.19)': + dependencies: + '@rushstack/terminal': file:../../../libraries/terminal(@types/node@20.17.19) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + + '@sinclair/typebox@0.34.49': {} + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@15.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/argparse@1.0.38': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.8 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + + '@types/estree@1.0.8': {} + + '@types/html-minifier-terser@6.1.0': {} + + '@types/istanbul-lib-coverage@2.0.4': {} + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@30.0.0': + dependencies: + expect: 30.3.0 + pretty-format: 30.3.0 + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@20.17.19': + dependencies: + undici-types: 6.19.8 + + '@types/stack-utils@2.0.3': {} + + '@types/tapable@1.0.6': {} + + '@types/webpack-env@1.18.8': {} + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.35': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.25.1)(typescript@4.9.5))(eslint@9.25.1)(typescript@4.9.5)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.56.1(eslint@9.25.1)(typescript@4.9.5) + '@typescript-eslint/scope-manager': 8.56.1(typescript@4.9.5) + '@typescript-eslint/type-utils': 8.56.1(eslint@9.25.1)(typescript@4.9.5) + '@typescript-eslint/utils': 8.56.1(eslint@9.25.1)(typescript@4.9.5) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@4.9.5) + eslint: 9.25.1 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.37.0)(typescript@5.8.3))(eslint@9.37.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.56.1(eslint@9.37.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.56.1(typescript@5.8.3) + '@typescript-eslint/type-utils': 8.56.1(eslint@9.37.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@5.8.3) + eslint: 9.37.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.56.1(eslint@9.25.1)(typescript@4.9.5)': + dependencies: + '@typescript-eslint/scope-manager': 8.56.1(typescript@4.9.5) + '@typescript-eslint/types': 8.56.1(typescript@4.9.5) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@4.9.5) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@4.9.5) + debug: 4.4.3 + eslint: 9.25.1 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.56.1(eslint@9.37.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.56.1(typescript@5.8.3) + '@typescript-eslint/types': 8.56.1(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@5.8.3) + debug: 4.4.3 + eslint: 9.37.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.56.1(typescript@4.9.5)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@4.9.5) + '@typescript-eslint/types': 8.56.1(typescript@4.9.5) + debug: 4.4.3 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.56.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.8.3) + '@typescript-eslint/types': 8.56.1(typescript@5.8.3) + debug: 4.4.3 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.56.1(typescript@4.9.5)': + dependencies: + '@typescript-eslint/types': 8.56.1(typescript@4.9.5) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@4.9.5) + transitivePeerDependencies: + - typescript + + '@typescript-eslint/scope-manager@8.56.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/types': 8.56.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@5.8.3) + transitivePeerDependencies: + - typescript + + '@typescript-eslint/tsconfig-utils@8.56.1(typescript@4.9.5)': + dependencies: + typescript: 4.9.5 + + '@typescript-eslint/tsconfig-utils@8.56.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@typescript-eslint/type-utils@8.56.1(eslint@9.25.1)(typescript@4.9.5)': + dependencies: + '@typescript-eslint/types': 8.56.1(typescript@4.9.5) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@4.9.5) + '@typescript-eslint/utils': 8.56.1(eslint@9.25.1)(typescript@4.9.5) + debug: 4.4.3 + eslint: 9.25.1 + ts-api-utils: 2.5.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/type-utils@8.56.1(eslint@9.37.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/types': 8.56.1(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.3) + debug: 4.4.3 + eslint: 9.37.0 + ts-api-utils: 2.5.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.56.1(typescript@4.9.5)': + dependencies: + typescript: 4.9.5 + + '@typescript-eslint/types@8.56.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@typescript-eslint/typescript-estree@8.56.1(typescript@4.9.5)': + dependencies: + '@typescript-eslint/project-service': 8.56.1(typescript@4.9.5) + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@4.9.5) + '@typescript-eslint/types': 8.56.1(typescript@4.9.5) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@4.9.5) + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.5.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.56.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/project-service': 8.56.1(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.8.3) + '@typescript-eslint/types': 8.56.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@5.8.3) + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.5.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.56.1(eslint@9.25.1)(typescript@4.9.5)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.25.1) + '@typescript-eslint/scope-manager': 8.56.1(typescript@4.9.5) + '@typescript-eslint/types': 8.56.1(typescript@4.9.5) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@4.9.5) + eslint: 9.25.1 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.56.1(eslint@9.37.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.37.0) + '@typescript-eslint/scope-manager': 8.56.1(typescript@5.8.3) + '@typescript-eslint/types': 8.56.1(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.8.3) + eslint: 9.37.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.56.1(typescript@4.9.5)': + dependencies: + '@typescript-eslint/types': 8.56.1(typescript@4.9.5) + eslint-visitor-keys: 5.0.1 + transitivePeerDependencies: + - typescript + + '@typescript-eslint/visitor-keys@8.56.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/types': 8.56.1(typescript@5.8.3) + eslint-visitor-keys: 5.0.1 + transitivePeerDependencies: + - typescript + + '@ungap/structured-clone@1.3.0': {} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + optional: true + + '@unrs/resolver-binding-android-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + dependencies: + '@napi-rs/wasm-runtime': 0.2.12 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + optional: true + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + '@yarnpkg/lockfile@1.0.2': {} + + '@zkochan/cmd-shim@5.4.1': + dependencies: + cmd-extension: 1.0.2 + graceful-fs: 4.2.11 + is-windows: 1.0.2 + + '@zkochan/js-yaml@0.0.11': + dependencies: + argparse: 2.0.1 + + '@zkochan/js-yaml@0.0.6': + dependencies: + argparse: 2.0.1 + + '@zkochan/rimraf@2.1.3': + dependencies: + rimraf: 3.0.2 + + '@zkochan/rimraf@3.0.2': {} + + '@zkochan/which@2.0.3': + dependencies: + isexe: 2.0.0 + + acorn-import-phases@1.0.4(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + ajv-draft-04@1.0.0(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv-formats@2.1.1: + dependencies: + ajv: 8.18.0 + + ajv-formats@3.0.1: + dependencies: + ajv: 8.20.0 + + ajv-keywords@5.1.0(ajv@8.18.0): + dependencies: + ajv: 8.18.0 + fast-deep-equal: 3.1.3 + + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@4.1.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.3: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + are-docs-informative@0.0.2: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + asap@2.0.6: {} + + async-function@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + babel-jest@30.3.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@jest/transform': 30.3.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 7.0.1 + babel-preset-jest: 30.3.0(@babel/core@7.29.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-istanbul@7.0.1: + dependencies: + '@babel/helper-plugin-utils': 7.28.6 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 6.0.3 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@30.3.0: + dependencies: + '@types/babel__core': 7.20.5 + + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) + + babel-preset-jest@30.3.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + babel-plugin-jest-hoist: 30.3.0 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.10.13: {} + + better-path-resolve@1.0.0: + dependencies: + is-windows: 1.0.2 + + bole@5.0.28: + dependencies: + fast-safe-stringify: 2.1.1 + individual: 3.0.0 + + boolbase@1.0.0: {} + + brace-expansion@1.1.13: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.3: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.5: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.13 + caniuse-lite: 1.0.30001784 + electron-to-chromium: 1.5.331 + node-releases: 2.0.37 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + buffer-from@1.1.2: {} + + builtin-modules@1.1.1: {} + + builtins@1.0.3: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camel-case@4.1.2: + dependencies: + pascal-case: 3.1.2 + tslib: 2.8.1 + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001784: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + char-regex@1.0.2: {} + + chownr@3.0.0: {} + + chrome-trace-event@1.0.4: {} + + ci-info@4.4.0: {} + + cjs-module-lexer@2.2.0: {} + + clean-css@5.3.3: + dependencies: + source-map: 0.6.1 + + cli-width@4.1.0: {} + + cmd-extension@1.0.2: {} + + co@4.6.0: {} + + collect-v8-coverage@1.0.3(@types/node@20.17.19): + dependencies: + '@types/node': 20.17.19 + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + commander@2.20.3: {} + + commander@8.3.0: {} + + comment-parser@1.4.1: {} + + comver-to-semver@1.0.0: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + core-util-is@1.0.3: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-select@4.3.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + + css-what@6.2.2: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + debuglog@1.0.1: {} + + dedent@1.7.2: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + dependency-path@9.2.8: + dependencies: + '@pnpm/crypto.base32-hash': 1.0.1 + '@pnpm/types': 8.9.0 + encode-registry: 3.0.1 + semver: 7.7.4 + + detect-indent@6.1.0: {} + + detect-newline@3.1.0: {} + + dezalgo@1.0.4: + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + + diff@4.0.4: {} + + diff@8.0.4: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dom-converter@0.2.0: + dependencies: + utila: 0.4.0 + + dom-serializer@1.4.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + + domelementtype@2.3.0: {} + + domhandler@4.3.1: + dependencies: + domelementtype: 2.3.0 + + domutils@2.8.0: + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dotenv@16.4.7: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + electron-to-chromium@1.5.331: {} + + emittery@0.13.1: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + encode-registry@3.0.1: + dependencies: + mem: 8.1.1 + + enhanced-resolve@5.20.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.2 + + entities@2.2.0: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-abstract@1.24.1: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.3.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + math-intrinsics: 1.1.0 + safe-array-concat: 1.1.3 + + es-module-lexer@2.0.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + escalade@3.2.0: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.11 + + eslint-module-utils@2.12.1(eslint@9.37.0): + dependencies: + debug: 3.2.7 + optionalDependencies: + eslint: 9.37.0 + + eslint-plugin-header@3.1.1(eslint@9.37.0): + dependencies: + eslint: 9.37.0 + + eslint-plugin-headers@1.2.1(eslint@9.37.0): + dependencies: + eslint: 9.37.0 + + eslint-plugin-import@2.32.0(eslint@9.37.0): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.37.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(eslint@9.37.0) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + + eslint-plugin-jsdoc@50.6.11(eslint@9.37.0): + dependencies: + '@es-joy/jsdoccomment': 0.49.0 + are-docs-informative: 0.0.2 + comment-parser: 1.4.1 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint: 9.37.0 + espree: 10.4.0 + esquery: 1.7.0 + parse-imports-exports: 0.2.4 + semver: 7.7.4 + spdx-expression-parse: 4.0.0 + transitivePeerDependencies: + - supports-color + + eslint-plugin-promise@7.2.1(eslint@9.25.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.25.1) + eslint: 9.25.1 + + eslint-plugin-promise@7.2.1(eslint@9.37.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.37.0) + eslint: 9.37.0 + + eslint-plugin-react-hooks@5.2.0(eslint@9.37.0): + dependencies: + eslint: 9.37.0 + + eslint-plugin-react@7.37.5(eslint@9.25.1): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.1 + eslint: 9.25.1 + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-plugin-react@7.37.5(eslint@9.37.0): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.1 + eslint: 9.37.0 + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-plugin-tsdoc@0.5.2(eslint@9.25.1)(typescript@4.9.5): + dependencies: + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.1 + '@typescript-eslint/utils': 8.56.1(eslint@9.25.1)(typescript@4.9.5) + transitivePeerDependencies: + - eslint + - supports-color + - typescript + + eslint-plugin-tsdoc@0.5.2(eslint@9.37.0)(typescript@5.8.3): + dependencies: + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.1 + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.3) + transitivePeerDependencies: + - eslint + - supports-color + - typescript + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.25.1: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.25.1) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.20.1 + '@eslint/config-helpers': 0.2.3 + '@eslint/core': 0.13.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.25.1 + '@eslint/plugin-kit': 0.2.8 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + eslint@9.37.0: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.37.0) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.16.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.37.0 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + esprima@4.0.1: {} + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + events@3.3.0: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit-x@0.2.2: {} + + expect@30.3.0: + dependencies: + '@jest/expect-utils': 30.3.0 + '@jest/get-type': 30.1.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 + jest-util: 30.3.0 + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-safe-stringify@2.1.1: {} + + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-uri@3.1.0: {} + + fast-wrap-ansi@0.2.0: + dependencies: + fast-string-width: 3.0.2 + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fs-extra@11.3.4: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-npm-tarball-url@2.1.0: {} + + get-package-type@0.1.0: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@6.0.1: {} + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + git-repo-info@2.1.1: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regexp@0.4.1: {} + + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + + glob@8.1.0: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.9 + once: 1.4.0 + + globals@14.0.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graceful-fs@4.2.4: {} + + has-bigints@1.1.0: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + hosted-git-info@2.8.9: {} + + hosted-git-info@4.1.0: + dependencies: + lru-cache: 6.0.0 + + html-escaper@2.0.2: {} + + html-minifier-terser@6.1.0: + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 8.3.0 + he: 1.2.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.46.1 + + html-webpack-plugin@5.5.4(webpack@5.105.4): + dependencies: + '@types/html-minifier-terser': 6.1.0 + html-minifier-terser: 6.1.0 + lodash: 4.18.1 + pretty-error: 4.0.0 + tapable: 2.3.2 + webpack: 5.105.4 + + htmlparser2@6.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 2.2.0 + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + ignore-walk@5.0.1: + dependencies: + minimatch: 5.1.9 + + ignore@5.1.9: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + immediate@3.0.6: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-lazy@4.0.0: {} + + imurmurhash@0.1.4: {} + + individual@3.0.0: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-arrayish@0.2.1: {} + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@3.0.0: {} + + is-generator-fn@2.1.0: {} + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-plain-obj@2.1.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@2.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-subdir@1.2.0: + dependencies: + better-path-resolve: 1.0.0 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + + is-typedarray@1.0.0: {} + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-windows@1.0.2: {} + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.2 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jest-changed-files@30.3.0: + dependencies: + execa: 5.1.1 + jest-util: 30.3.0 + p-limit: 3.1.0 + + jest-circus@30.3.0: + dependencies: + '@jest/environment': 30.3.0 + '@jest/expect': 30.3.0 + '@jest/test-result': 30.3.0(@types/node@20.17.19) + '@jest/types': 30.3.0 + '@types/node': 20.17.19 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.7.2 + is-generator-fn: 2.1.0 + jest-each: 30.3.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-runtime: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 + p-limit: 3.1.0 + pretty-format: 30.3.0 + pure-rand: 7.0.1 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-config@30.3.0(@types/node@20.17.19): + dependencies: + '@babel/core': 7.29.0 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.3.0(@types/node@20.17.19) + '@jest/types': 30.3.0 + babel-jest: 30.3.0(@babel/core@7.29.0) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.3.0 + jest-docblock: 30.2.0 + jest-environment-node: 30.3.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.3.0 + jest-runner: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + parse-json: 5.2.0 + pretty-format: 30.3.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.17.19 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@30.3.0: + dependencies: + '@jest/diff-sequences': 30.3.0 + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + pretty-format: 30.3.0 + + jest-docblock@30.2.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@30.3.0: + dependencies: + '@jest/get-type': 30.1.0 + '@jest/types': 30.3.0 + chalk: 4.1.2 + jest-util: 30.3.0 + pretty-format: 30.3.0 + + jest-environment-node@30.3.0: + dependencies: + '@jest/environment': 30.3.0 + '@jest/fake-timers': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 20.17.19 + jest-mock: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + + jest-haste-map@30.3.0: + dependencies: + '@jest/types': 30.3.0 + '@types/node': 20.17.19 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 30.0.1 + jest-util: 30.3.0 + jest-worker: 30.3.0 + picomatch: 4.0.4 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-junit@12.3.0: + dependencies: + mkdirp: 1.0.4 + strip-ansi: 5.2.0 + uuid: 8.3.2 + xml: 1.0.1 + + jest-leak-detector@30.3.0: + dependencies: + '@jest/get-type': 30.1.0 + pretty-format: 30.3.0 + + jest-matcher-utils@30.3.0: + dependencies: + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + jest-diff: 30.3.0 + pretty-format: 30.3.0 + + jest-message-util@30.3.0: + dependencies: + '@babel/code-frame': 7.29.0 + '@jest/types': 30.3.0 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + picomatch: 4.0.4 + pretty-format: 30.3.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@30.3.0: + dependencies: + '@jest/types': 30.3.0 + '@types/node': 20.17.19 + jest-util: 30.3.0 + + jest-pnp-resolver@1.2.3(jest-resolve@30.3.0): + optionalDependencies: + jest-resolve: 30.3.0 + + jest-regex-util@30.0.1: {} + + jest-resolve-dependencies@30.3.0: + dependencies: + jest-regex-util: 30.0.1 + jest-snapshot: 30.3.0 + transitivePeerDependencies: + - supports-color + + jest-resolve@30.3.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + jest-pnp-resolver: 1.2.3(jest-resolve@30.3.0) + jest-util: 30.3.0 + jest-validate: 30.3.0 + slash: 3.0.0 + unrs-resolver: 1.11.1 + + jest-runner@30.3.0: + dependencies: + '@jest/console': 30.3.0 + '@jest/environment': 30.3.0 + '@jest/test-result': 30.3.0(@types/node@20.17.19) + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 20.17.19 + chalk: 4.1.2 + emittery: 0.13.1 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-docblock: 30.2.0 + jest-environment-node: 30.3.0 + jest-haste-map: 30.3.0 + jest-leak-detector: 30.3.0 + jest-message-util: 30.3.0 + jest-resolve: 30.3.0 + jest-runtime: 30.3.0 + jest-util: 30.3.0 + jest-watcher: 30.3.0 + jest-worker: 30.3.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@30.3.0: + dependencies: + '@jest/environment': 30.3.0 + '@jest/fake-timers': 30.3.0 + '@jest/globals': 30.3.0 + '@jest/source-map': 30.0.1 + '@jest/test-result': 30.3.0(@types/node@20.17.19) + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 20.17.19 + chalk: 4.1.2 + cjs-module-lexer: 2.2.0 + collect-v8-coverage: 1.0.3(@types/node@20.17.19) + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-snapshot@30.3.0: + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/types': 7.29.0 + '@jest/expect-utils': 30.3.0 + '@jest/get-type': 30.1.0 + '@jest/snapshot-utils': 30.3.0 + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + chalk: 4.1.2 + expect: 30.3.0 + graceful-fs: 4.2.11 + jest-diff: 30.3.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-util: 30.3.0 + pretty-format: 30.3.0 + semver: 7.7.4 + synckit: 0.11.12 + transitivePeerDependencies: + - supports-color + + jest-util@30.3.0: + dependencies: + '@jest/types': 30.3.0 + '@types/node': 20.17.19 + chalk: 4.1.2 + ci-info: 4.4.0 + graceful-fs: 4.2.11 + picomatch: 4.0.4 + + jest-validate@30.3.0: + dependencies: + '@jest/get-type': 30.1.0 + '@jest/types': 30.3.0 + camelcase: 6.3.0 + chalk: 4.1.2 + leven: 3.1.0 + pretty-format: 30.3.0 + + jest-watcher@30.3.0: + dependencies: + '@jest/test-result': 30.3.0(@types/node@20.17.19) + '@jest/types': 30.3.0 + '@types/node': 20.17.19 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 30.3.0 + string-length: 4.0.2 + + jest-worker@27.5.1: + dependencies: + '@types/node': 20.17.19 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest-worker@30.3.0: + dependencies: + '@types/node': 20.17.19 + '@ungap/structured-clone': 1.3.0 + jest-util: 30.3.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jju@1.4.0: {} + + js-tokens@4.0.0: {} + + js-yaml@3.14.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsdoc-type-pratt-parser@4.1.0: {} + + jsep@1.4.0: {} + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json-stringify-safe@5.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + json5@2.2.3: {} + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonpath-plus@10.3.0: + dependencies: + '@jsep-plugin/assignment': 1.3.0(jsep@1.4.0) + '@jsep-plugin/regex': 1.0.4(jsep@1.4.0) + jsep: 1.4.0 + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + jszip@3.8.0: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + set-immediate-shim: 1.0.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + leven@3.1.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lie@3.3.0: + dependencies: + immediate: 3.0.6 + + lines-and-columns@1.2.4: {} + + load-json-file@6.2.0: + dependencies: + graceful-fs: 4.2.11 + parse-json: 5.2.0 + strip-bom: 4.0.0 + type-fest: 0.6.0 + + loader-runner@4.3.1: {} + + local-eslint-config@file:../../../eslint/local-eslint-config(eslint@9.37.0)(typescript@5.8.3): + dependencies: + '@rushstack/eslint-config': file:../../../eslint/eslint-config(eslint@9.37.0)(typescript@5.8.3) + '@rushstack/eslint-patch': file:../../../eslint/eslint-patch + '@rushstack/eslint-plugin': file:../../../eslint/eslint-plugin(eslint@9.37.0)(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.37.0)(typescript@5.8.3))(eslint@9.37.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.56.1(eslint@9.37.0)(typescript@5.8.3) + eslint: 9.37.0 + eslint-import-resolver-node: 0.3.9 + eslint-plugin-header: 3.1.1(eslint@9.37.0) + eslint-plugin-headers: 1.2.1(eslint@9.37.0) + eslint-plugin-import: 2.32.0(eslint@9.37.0) + eslint-plugin-jsdoc: 50.6.11(eslint@9.37.0) + eslint-plugin-react-hooks: 5.2.0(eslint@9.37.0) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + local-node-rig@file:../../../rigs/local-node-rig: + dependencies: + '@microsoft/api-extractor': file:../../../apps/api-extractor(@types/node@20.17.19) + '@rushstack/eslint-patch': file:../../../eslint/eslint-patch + '@rushstack/heft': file:../../../apps/heft(@types/node@20.17.19) + '@rushstack/heft-node-rig': file:../../../rigs/heft-node-rig(@rushstack/heft@file:../../../apps/heft(@types/node@20.17.19))(@types/node@20.17.19) + '@types/jest': 30.0.0 + '@types/node': 20.17.19 + eslint: 9.37.0 + jest-junit: 12.3.0 + local-eslint-config: file:../../../eslint/local-eslint-config(eslint@9.37.0)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - babel-plugin-macros + - esbuild-register + - jest-environment-jsdom + - jiti + - node-notifier + - supports-color + - ts-node + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + lodash@4.18.1: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + make-dir@4.0.0: + dependencies: + semver: 7.7.4 + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + map-age-cleaner@0.1.3: + dependencies: + p-defer: 1.0.0 + + math-intrinsics@1.1.0: {} + + mem@8.1.1: + dependencies: + map-age-cleaner: 0.1.3 + mimic-fn: 3.1.0 + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-fn@2.1.0: {} + + mimic-fn@3.1.0: {} + + minimatch@10.2.3: + dependencies: + brace-expansion: 5.0.5 + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.5 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.13 + + minimatch@5.1.9: + dependencies: + brace-expansion: 2.0.3 + + minimatch@9.0.9: + dependencies: + brace-expansion: 2.0.3 + + minimist@1.2.8: {} + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@7.1.3: {} + + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + + mkdirp@1.0.4: {} + + ms@2.1.3: {} + + mute-stream@3.0.0: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + + ndjson@2.0.0: + dependencies: + json-stringify-safe: 5.0.1 + minimist: 1.2.8 + readable-stream: 3.6.2 + split2: 3.2.2 + through2: 4.0.2 + + neo-async@2.6.2: {} + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-exports-info@1.6.0: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + + node-int64@0.4.0: {} + + node-releases@2.0.37: {} + + normalize-package-data@2.5.0: + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.11 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + + normalize-package-data@3.0.3: + dependencies: + hosted-git-info: 4.1.0 + is-core-module: 2.16.1 + semver: 7.7.4 + validate-npm-package-license: 3.0.4 + + normalize-path@3.0.0: {} + + npm-bundled@2.0.1: + dependencies: + npm-normalize-package-bin: 2.0.0 + + npm-normalize-package-bin@1.0.1: {} + + npm-normalize-package-bin@2.0.0: {} + + npm-package-arg@6.1.1: + dependencies: + hosted-git-info: 2.8.9 + osenv: 0.1.5 + semver: 5.7.2 + validate-npm-package-name: 3.0.0 + + npm-packlist@5.1.3: + dependencies: + glob: 8.1.0 + ignore-walk: 5.0.1 + npm-bundled: 2.0.1 + npm-normalize-package-bin: 2.0.0 + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + os-homedir@1.0.2: {} + + os-tmpdir@1.0.2: {} + + osenv@0.1.5: + dependencies: + os-homedir: 1.0.2 + os-tmpdir: 1.0.2 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-defer@1.0.0: {} + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-reflect@2.1.0: {} + + p-settle@4.1.1: + dependencies: + p-limit: 2.3.0 + p-reflect: 2.1.0 + + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + pako@1.0.11: {} + + param-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-imports-exports@0.2.4: + dependencies: + parse-statements: 1.0.11 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-statements@1.0.11: {} + + pascal-case@3.1.2: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-name@1.0.0: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + pirates@4.0.7: {} + + pnpm-sync-lib@0.3.4: + dependencies: + '@pnpm/dependency-path-pnpm-v10': '@pnpm/dependency-path@1000.0.9' + '@pnpm/dependency-path-pnpm-v8': '@pnpm/dependency-path@2.1.8' + '@pnpm/dependency-path-pnpm-v9': '@pnpm/dependency-path@5.1.7' + '@pnpm/lockfile-types-pnpm-lock-v6': '@pnpm/lockfile-types@5.1.5' + '@pnpm/lockfile.types-pnpm-lock-v9': '@pnpm/lockfile.types@1001.1.0' + yaml: 2.9.0 + + possible-typed-array-names@1.1.0: {} + + prelude-ls@1.2.1: {} + + pretty-error@4.0.0: + dependencies: + lodash: 4.18.1 + renderkid: 3.0.0 + + pretty-format@30.3.0: + dependencies: + '@jest/schemas': 30.0.5 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + process-nextick-args@2.0.1: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + punycode@2.3.1: {} + + pure-rand@7.0.1: {} + + queue-microtask@1.2.3: {} + + ramda@0.27.2: {} + + react-is@16.13.1: {} + + react-is@18.3.1: {} + + read-package-json@2.1.2: + dependencies: + glob: 7.2.3 + json-parse-even-better-errors: 2.3.1 + normalize-package-data: 2.5.0 + npm-normalize-package-bin: 1.0.1 + + read-package-tree@5.1.6: + dependencies: + debuglog: 1.0.1 + dezalgo: 1.0.4 + once: 1.4.0 + read-package-json: 2.1.2 + readdir-scoped-modules: 1.1.0 + + read-yaml-file@2.1.0: + dependencies: + js-yaml: 4.1.1 + strip-bom: 4.0.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdir-scoped-modules@1.1.0: + dependencies: + debuglog: 1.0.1 + dezalgo: 1.0.4 + graceful-fs: 4.2.11 + once: 1.4.0 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + relateurl@0.2.7: {} + + renderkid@3.0.0: + dependencies: + css-select: 4.3.0 + dom-converter: 0.2.0 + htmlparser2: 6.1.0 + lodash: 4.18.1 + strip-ansi: 6.0.1 + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.6: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.1 + node-exports-info: 1.6.0 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + rfc4648@1.5.4: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@6.6.7: + dependencies: + tslib: 1.14.1 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-execa@0.1.2: + dependencies: + '@zkochan/which': 2.0.3 + execa: 5.1.1 + path-name: 1.0.0 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + schema-utils@4.3.3: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.18.0 + ajv-formats: 2.1.1 + ajv-keywords: 5.1.0(ajv@8.18.0) + + semver@5.7.2: {} + + semver@6.3.1: {} + + semver@7.7.4: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-immediate-shim@1.0.1: {} + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + slash@3.0.0: {} + + sort-keys@4.2.0: + dependencies: + is-plain-obj: 2.1.0 + + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.23 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.23 + + spdx-expression-parse@4.0.0: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.23 + + spdx-license-ids@3.0.23: {} + + split2@3.2.2: + dependencies: + readable-stream: 3.6.2 + + split2@4.2.0: {} + + sprintf-js@1.0.3: {} + + ssri@10.0.5: + dependencies: + minipass: 7.1.3 + + ssri@8.0.1: + dependencies: + minipass: 3.3.6 + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + strict-uri-encode@2.0.0: {} + + string-argv@0.3.2: {} + + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.1 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@5.2.0: + dependencies: + ansi-regex: 4.1.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + strip-bom@3.0.0: {} + + strip-bom@4.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + synckit@0.11.12: + dependencies: + '@pkgr/core': 0.2.9 + + tapable@1.1.3: {} + + tapable@2.2.1: {} + + tapable@2.3.2: {} + + tar@7.5.13: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + + terser-webpack-plugin@5.4.0(webpack@5.105.4): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + terser: 5.46.1 + webpack: 5.105.4 + + terser@5.46.1: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.16.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.5 + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + through2@4.0.2: + dependencies: + readable-stream: 3.6.2 + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tmpl@1.0.5: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + true-case-path@2.2.1: {} + + ts-api-utils@2.5.0(typescript@4.9.5): + dependencies: + typescript: 4.9.5 + + ts-api-utils@2.5.0(typescript@5.8.3): + dependencies: + typescript: 5.8.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@1.14.1: {} + + tslib@2.8.1: {} + + tslint@5.20.1(typescript@4.9.5): + dependencies: + '@babel/code-frame': 7.29.0 + builtin-modules: 1.1.1 + chalk: 2.4.2 + commander: 2.20.3 + diff: 4.0.4 + glob: 7.2.3 + js-yaml: 3.14.2 + minimatch: 3.1.5 + mkdirp: 0.5.6 + resolve: 1.22.11 + semver: 5.7.2 + tslib: 1.14.1 + tsutils: 2.29.0(typescript@4.9.5) + typescript: 4.9.5 + + tsutils@2.29.0(typescript@4.9.5): + dependencies: + tslib: 1.14.1 + typescript: 4.9.5 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-detect@4.0.8: {} + + type-fest@0.21.3: {} + + type-fest@0.6.0: {} + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typedarray-to-buffer@3.1.5: + dependencies: + is-typedarray: 1.0.0 + + typescript@4.9.5: {} + + typescript@5.8.3: {} + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@6.19.8: {} + + universalify@2.0.1: {} + + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.11.1 + '@unrs/resolver-binding-android-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-x64': 1.11.1 + '@unrs/resolver-binding-freebsd-x64': 1.11.1 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 + '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-musl': 1.11.1 + '@unrs/resolver-binding-wasm32-wasi': 1.11.1 + '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 + '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 + '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + utila@0.4.0: {} + + uuid@8.3.2: {} + + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + validate-npm-package-name@3.0.0: + dependencies: + builtins: 1.0.3 + + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + + watchpack@2.4.0: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + watchpack@2.5.1: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + webpack-sources@3.3.4: {} + + webpack@5.105.4: + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.16.0 + acorn-import-phases: 1.0.4(acorn@8.16.0) + browserslist: 4.28.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.20.1 + es-module-lexer: 2.0.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.2 + terser-webpack-plugin: 5.4.0(webpack@5.105.4) + watchpack: 2.5.1 + webpack-sources: 3.3.4 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.20 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + + wrappy@1.0.2: {} + + write-file-atomic@3.0.3: + dependencies: + imurmurhash: 0.1.4 + is-typedarray: 1.0.0 + signal-exit: 3.0.7 + typedarray-to-buffer: 3.1.5 + + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + + write-yaml-file@4.2.0: + dependencies: + js-yaml: 4.1.1 + write-file-atomic: 3.0.3 + + xml@1.0.1: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yallist@5.0.0: {} + + yaml@2.9.0: {} + + yocto-queue@0.1.0: {} diff --git a/common/config/subspaces/build-tests-subspace/repo-state.json b/common/config/subspaces/build-tests-subspace/repo-state.json new file mode 100644 index 00000000000..9dc67416852 --- /dev/null +++ b/common/config/subspaces/build-tests-subspace/repo-state.json @@ -0,0 +1,6 @@ +// DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush. +{ + "pnpmShrinkwrapHash": "49bbe38c2fde750eb6d700136d33f577898ccb22", + "preferredVersionsHash": "550b4cee0bef4e97db6c6aad726df5149d20e7d9", + "packageJsonInjectedDependenciesHash": "dacc324b7b6d9e35baf57460cae419ef162589d6" +} diff --git a/common/config/subspaces/default/.npmrc b/common/config/subspaces/default/.npmrc new file mode 100644 index 00000000000..9ce3b02e82e --- /dev/null +++ b/common/config/subspaces/default/.npmrc @@ -0,0 +1,32 @@ +# Rush uses this file to configure the NPM package registry during installation. It is applicable +# to PNPM, NPM, and Yarn package managers. It is used by operations such as "rush install", +# "rush update", and the "install-run.js" scripts. +# +# NOTE: The "rush publish" command uses .npmrc-publish instead. +# +# Before invoking the package manager, Rush will generate an .npmrc in the folder where installation +# is performed. This generated file will omit any config lines that reference environment variables +# that are undefined in that session; this avoids problems that would otherwise result due to +# a missing variable being replaced by an empty string. +# +# If "subspacesEnabled" is true in subspaces.json, the generated file will merge settings from +# "common/config/rush/.npmrc" and "common/config/subspaces//.npmrc", with the latter taking +# precedence. +# +# * * * SECURITY WARNING * * * +# +# It is NOT recommended to store authentication tokens in a text file on a lab machine, because +# other unrelated processes may be able to read that file. Also, the file may persist indefinitely, +# for example if the machine loses power. A safer practice is to pass the token via an +# environment variable, which can be referenced from .npmrc using ${} expansion. For example: +# +# //registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} +# +registry=https://packagefeedproxy.microsoft.io/npm/ +always-auth=false +# No phantom dependencies allowed in this repository +# Don't hoist in common/temp/node_modules +public-hoist-pattern= +# Don't hoist in common/temp/node_modules/.pnpm/node_modules +hoist=false +hoist-pattern= diff --git a/common/config/subspaces/default/.pnpmfile.cjs b/common/config/subspaces/default/.pnpmfile.cjs new file mode 100644 index 00000000000..b7821599ca7 --- /dev/null +++ b/common/config/subspaces/default/.pnpmfile.cjs @@ -0,0 +1,68 @@ +'use strict'; + +/** + * When using the PNPM package manager, you can use pnpmfile.js to workaround + * dependencies that have mistakes in their package.json file. (This feature is + * functionally similar to Yarn's "resolutions".) + * + * For details, see the PNPM documentation: + * https://pnpm.io/pnpmfile#hooks + * + * IMPORTANT: SINCE THIS FILE CONTAINS EXECUTABLE CODE, MODIFYING IT IS LIKELY TO INVALIDATE + * ANY CACHED DEPENDENCY ANALYSIS. After any modification to pnpmfile.js, it's recommended to run + * "rush update --full" so that PNPM will recalculate all version selections. + */ +module.exports = { + hooks: { + readPackage + } +}; + +function fixUndeclaredDependency(packageJson, dependencyName) { + packageJson.dependencies[dependencyName] = + packageJson.dependencies[dependencyName] || + packageJson.devDependencies?.[dependencyName] || + packageJson.version; +} + +/** + * This hook is invoked during installation before a package's dependencies + * are selected. + * The `packageJson` parameter is the deserialized package.json + * contents for the package that is about to be installed. + * The `context` parameter provides a log() function. + * The return value is the updated object. + */ +function readPackage(packageJson, context) { + if (packageJson.name.startsWith('@radix-ui/')) { + if (packageJson.peerDependencies && packageJson.peerDependencies['react']) { + packageJson.peerDependencies['@types/react'] = '*'; + packageJson.peerDependencies['@types/react-dom'] = '*'; + } + } + + switch (packageJson.name) { + case '@jest/test-result': { + // The `@jest/test-result` package takes undeclared dependencies on `jest-haste-map` + // and `jest-resolve` + fixUndeclaredDependency(packageJson, 'jest-haste-map'); + fixUndeclaredDependency(packageJson, 'jest-resolve'); + } + + case '@serverless-stack/core': { + delete packageJson.dependencies['@typescript-eslint/eslint-plugin']; + delete packageJson.dependencies['eslint-config-serverless-stack']; + delete packageJson.dependencies['lerna']; + break; + } + + case '@typescript-eslint/rule-tester': { + // The `@typescript-eslint/rule-tester` package takes an undeclared dependency + // on `@typescript-eslint/parser` + fixUndeclaredDependency(packageJson, '@typescript-eslint/parser'); + break; + } + } + + return packageJson; +} diff --git a/common/config/subspaces/default/common-versions.json b/common/config/subspaces/default/common-versions.json new file mode 100644 index 00000000000..40fc6f8597a --- /dev/null +++ b/common/config/subspaces/default/common-versions.json @@ -0,0 +1,166 @@ +/** + * This configuration file specifies NPM dependency version selections that affect all projects + * in a Rush repo. More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/common-versions.schema.json", + + /** + * A table that specifies a "preferred version" for a given NPM package. This feature is typically used + * to hold back an indirect dependency to a specific older version, or to reduce duplication of indirect dependencies. + * + * The "preferredVersions" value can be any SemVer range specifier (e.g. "~1.2.3"). Rush injects these values into + * the "dependencies" field of the top-level common/temp/package.json, which influences how the package manager + * will calculate versions. The specific effect depends on your package manager. Generally it will have no + * effect on an incompatible or already constrained SemVer range. If you are using PNPM, similar effects can be + * achieved using the pnpmfile.js hook. See the Rush documentation for more details. + * + * After modifying this field, it's recommended to run "rush update --full" so that the package manager + * will recalculate all version selections. + */ + "preferredVersions": { + /** + * When someone asks for "^1.0.0" make sure they get "1.2.3" when working in this repo, + * instead of the latest version. + */ + // "some-library": "1.2.3" + + // This should be the TypeScript version that's used to build most of the projects in the repo. + // Preferring it avoids errors for indirect dependencies that request it as a peer dependency. + // It's also the newest supported compiler, used by most build tests and used as the bundled compiler + // engine for API Extractor. + "typescript": "~5.8.2", + + // This should be the ESLint version that's used to build most of the projects in the repo. + "eslint": "~9.37.0", + + // Updated minimatch and its types to latest major version to resolve ReDoS vulnerability + "minimatch": "10.2.3" + }, + + /** + * When set to true, for all projects in the repo, all dependencies will be automatically added as preferredVersions, + * except in cases where different projects specify different version ranges for a given dependency. For older + * package managers, this tended to reduce duplication of indirect dependencies. However, it can sometimes cause + * trouble for indirect dependencies with incompatible peerDependencies ranges. + * + * The default value is true. If you're encountering installation errors related to peer dependencies, + * it's recommended to set this to false. + * + * After modifying this field, it's recommended to run "rush update --full" so that the package manager + * will recalculate all version selections. + */ + // "implicitlyPreferredVersions": false, + + /** + * If you would like the version specifiers for your dependencies to be consistent, then + * uncomment this line. This is effectively similar to running "rush check" before any + * of the following commands: + * + * rush install, rush update, rush link, rush version, rush publish + * + * In some cases you may want this turned on, but need to allow certain packages to use a different + * version. In those cases, you will need to add an entry to the "allowedAlternativeVersions" + * section of the common-versions.json. + * + * In the case that subspaces is enabled, this setting will take effect at a subspace level. + */ + "ensureConsistentVersions": true, + + /** + * The "rush check" command can be used to enforce that every project in the repo must specify + * the same SemVer range for a given dependency. However, sometimes exceptions are needed. + * The allowedAlternativeVersions table allows you to list other SemVer ranges that will be + * accepted by "rush check" for a given dependency. + * + * IMPORTANT: THIS TABLE IS FOR *ADDITIONAL* VERSION RANGES THAT ARE ALTERNATIVES TO THE + * USUAL VERSION (WHICH IS INFERRED BY LOOKING AT ALL PROJECTS IN THE REPO). + * This design avoids unnecessary churn in this file. + */ + "allowedAlternativeVersions": { + // Allow Lockfile Explorer to support PNPM 9.x + // TODO: Remove this after Rush adds support for PNPM 9.x + "@pnpm/lockfile.types": ["1002.0.1"], + "@typescript-eslint/parser": [ + "~6.19.0" // Used by build-tests/eslint-7(-*)-test / build-tests/eslint-bulk-suppressions-test-legacy + ], + "eslint": [ + "7.7.0", // Used by build-tests/eslint-7-7-test + "7.11.0", // Used by build-tests/eslint-7-11-test + "~7.30.0", // Used by build-tests/eslint-7-test + "8.6.0", // Used by build-tests/eslint-bulk-suppressions-test-legacy + "8.23.1", // Used by build-tests/eslint-bulk-suppressions-test-legacy + "~8.57.0" // Used by build-tests/eslint-bulk-suppressions-test + ], + /** + * For example, allow some projects to use an older TypeScript compiler + * (in addition to whatever "usual" version is being used by other projects in the repo): + */ + "typescript": [ + // "~5.0.4" is the (inferred, not alternative) range used by most projects in this repo + + // The oldest supported compiler, used by build-tests/api-extractor-lib1-test + "~2.9.2", + // For testing Heft with TS V3 + "~3.9.10", + // For testing Heft with TS V4 + "~4.9.5", + + "5.8.2", + // API Extractor bundles a specific TypeScript version because it calls internal APIs + "5.9.3" + ], + "source-map": [ + "~0.6.1" // API Extractor is using an older version of source-map because newer versions are async + ], + "tapable": [ + "2.2.1", + "1.1.3" // heft plugin is using an older version of tapable + ], + // --- For Webpack 4 projects ---- + "css-loader": ["~5.2.7"], + "html-webpack-plugin": ["~4.5.2"], + "postcss-loader": ["~4.1.0"], + "sass-loader": ["~10.0.0"], + "sass": ["~1.3.0"], + "source-map-loader": ["~1.1.3"], + "style-loader": ["~2.0.0"], + "terser-webpack-plugin": ["~3.0.8"], + "terser": ["~4.8.0"], + "webpack": ["~4.47.0"], + "webpack-dev-server": ["~4.9.3"], + "@types/node": [ + // These versions are used by testing projects + "ts2.9", + "ts3.9", + "ts4.9" + ], + "@types/jest": [ + // These versions are used by testing projects + "ts2.9", + "ts3.9", + "ts4.9" + ], + "@rushstack/eslint-config": [ + // This is used by the ESLint 7 build tests + "3.7.1" + ], + "@rushstack/set-webpack-public-path-plugin": [ + // This is used by the webpack 4 localization plugin tests + "^4.1.16" + ], + "@pnpm/logger": [ + // For pnpm kit v8 + "~5.0.0", + // For pnpm kit v9, v10 + "~1001.0.0" + ], + // These are used in heft-storybook-v6-react-tutorial-storykit and heft-storybook-v6-react-tutorial + "@types/react": ["17.0.74"], + "@types/react-dom": ["17.0.25"], + "react": ["~17.0.2"], + "react-dom": ["~17.0.2"], + "@storybook/cli": ["~6.4.18"], + "@storybook/react": ["~6.4.18"] + } +} diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml new file mode 100644 index 00000000000..4ec1b48ee88 --- /dev/null +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -0,0 +1,38485 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false + +overrides: + package-json: ^7 + '@types/estree': 1.0.8 + '@types/react@17.0.74>@types/scheduler': 0.16.8 + '@vscode/vsce>cheerio': 1.0.0-rc.12 + loader-utils@^2.0.0: 2.0.4 + fast-xml-parser@^5.3.3: 5.3.5 + +packageExtensionsChecksum: sha256-8fXYR9X9qRA57SZJJSADz6C9KMP6QQYYut4DHyehah0= + +pnpmfileChecksum: sha256-E1T7OJ3DLTjpDqf4RdJzK9VDtAxgm4gDEQCLYdHD8nI= + +importers: + + .: {} + + ../../../apps/api-documenter: + dependencies: + '@microsoft/api-extractor-model': + specifier: workspace:* + version: link:../../libraries/api-extractor-model + '@microsoft/tsdoc': + specifier: ~0.16.0 + version: 0.16.0 + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + js-yaml: + specifier: ~4.1.0 + version: 4.1.1 + resolve: + specifier: ~1.22.1 + version: 1.22.11 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../heft + '@types/js-yaml': + specifier: 4.0.9 + version: 4.0.9 + '@types/resolve': + specifier: 1.20.2 + version: 1.20.2 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../apps/api-extractor: + dependencies: + '@microsoft/api-extractor-model': + specifier: workspace:* + version: link:../../libraries/api-extractor-model + '@microsoft/tsdoc': + specifier: ~0.16.0 + version: 0.16.0 + '@microsoft/tsdoc-config': + specifier: ~0.18.1 + version: 0.18.1 + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rig-package': + specifier: workspace:* + version: link:../../libraries/rig-package + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + diff: + specifier: ~8.0.2 + version: 8.0.4 + minimatch: + specifier: 10.2.3 + version: 10.2.3 + resolve: + specifier: ~1.22.1 + version: 1.22.11 + semver: + specifier: ~7.7.4 + version: 7.7.4 + source-map: + specifier: ~0.6.1 + version: 0.6.1 + typescript: + specifier: 5.9.3 + version: 5.9.3 + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + '@types/resolve': + specifier: 1.20.2 + version: 1.20.2 + '@types/semver': + specifier: 7.7.1 + version: 7.7.1 + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + + ../../../apps/cpu-profile-summarizer: + dependencies: + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + '@rushstack/worker-pool': + specifier: workspace:* + version: link:../../libraries/worker-pool + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../apps/heft: + dependencies: + '@rushstack/heft-config-file': + specifier: workspace:* + version: link:../../libraries/heft-config-file + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/operation-graph': + specifier: workspace:* + version: link:../../libraries/operation-graph + '@rushstack/rig-package': + specifier: workspace:* + version: link:../../libraries/rig-package + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + '@types/tapable': + specifier: 1.0.6 + version: 1.0.6 + fast-glob: + specifier: ~3.3.1 + version: 3.3.3 + git-repo-info: + specifier: ~2.1.0 + version: 2.1.1 + ignore: + specifier: ~5.1.6 + version: 5.1.9 + tapable: + specifier: 1.1.3 + version: 1.1.3 + watchpack: + specifier: 2.4.0 + version: 2.4.0 + devDependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../api-extractor + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + '@types/watchpack': + specifier: 2.4.0 + version: 2.4.0 + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + + ../../../apps/lockfile-explorer: + dependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@pnpm/dependency-path-lockfile-pre-v9': + specifier: npm:@pnpm/dependency-path@~2.1.2 + version: '@pnpm/dependency-path@2.1.8' + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + cors: + specifier: ~2.8.5 + version: 2.8.6 + express: + specifier: 4.21.1 + version: 4.21.1 + js-yaml: + specifier: ~4.1.0 + version: 4.1.1 + semver: + specifier: ~7.7.4 + version: 7.7.4 + tslib: + specifier: ~2.8.1 + version: 2.8.1 + devDependencies: + '@pnpm/lockfile.types': + specifier: 1002.0.1 + version: 1002.0.1 + '@pnpm/types': + specifier: 1000.8.0 + version: 1000.8.0 + '@rushstack/heft': + specifier: workspace:* + version: link:../heft + '@rushstack/lockfile-explorer-web': + specifier: workspace:* + version: link:../lockfile-explorer-web + '@types/cors': + specifier: ~2.8.12 + version: 2.8.19 + '@types/express': + specifier: 4.17.21 + version: 4.17.21 + '@types/js-yaml': + specifier: 4.0.9 + version: 4.0.9 + '@types/semver': + specifier: 7.7.1 + version: 7.7.1 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../apps/lockfile-explorer-web: + dependencies: + '@reduxjs/toolkit': + specifier: ~2.11.2 + version: 2.11.2(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.4)(redux@5.0.1))(react@19.2.4) + '@rushstack/rush-themed-ui': + specifier: workspace:* + version: link:../../libraries/rush-themed-ui + prism-react-renderer: + specifier: ~2.4.1 + version: 2.4.1(react@19.2.4) + react: + specifier: ~19.2.3 + version: 19.2.4 + react-dom: + specifier: ~19.2.3 + version: 19.2.4(react@19.2.4) + react-redux: + specifier: ~9.2.0 + version: 9.2.0(@types/react@19.2.7)(react@19.2.4)(redux@5.0.1) + redux: + specifier: ~5.0.1 + version: 5.0.1 + tslib: + specifier: ~2.8.1 + version: 2.8.1 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../heft + '@types/react': + specifier: 19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: 19.2.3 + version: 19.2.3(@types/react@19.2.7) + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-web-rig: + specifier: workspace:* + version: link:../../rigs/local-web-rig + typescript: + specifier: 5.8.2 + version: 5.8.2 + + ../../../apps/playwright-browser-tunnel: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + playwright: + specifier: 1.56.1 + version: 1.56.1 + string-argv: + specifier: ~0.3.1 + version: 0.3.2 + ws: + specifier: ~8.21.0 + version: 8.21.0 + devDependencies: + '@playwright/test': + specifier: ~1.56.1 + version: 1.56.1 + '@rushstack/heft': + specifier: workspace:* + version: link:../heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/semver': + specifier: 7.7.1 + version: 7.7.1 + '@types/ws': + specifier: 8.18.1 + version: 8.18.1 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + playwright-core: + specifier: ~1.56.1 + version: 1.56.1 + + ../../../apps/rundown: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + string-argv: + specifier: ~0.3.1 + version: 0.3.2 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../apps/rush: + dependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + semver: + specifier: ~7.7.4 + version: 7.7.4 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../heft + '@rushstack/rush-amazon-s3-build-cache-plugin': + specifier: workspace:* + version: link:../../rush-plugins/rush-amazon-s3-build-cache-plugin + '@rushstack/rush-azure-storage-build-cache-plugin': + specifier: workspace:* + version: link:../../rush-plugins/rush-azure-storage-build-cache-plugin + '@rushstack/rush-http-build-cache-plugin': + specifier: workspace:* + version: link:../../rush-plugins/rush-http-build-cache-plugin + '@rushstack/rush-serve-plugin': + specifier: workspace:* + version: link:../../rush-plugins/rush-serve-plugin + '@types/semver': + specifier: 7.7.1 + version: 7.7.1 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../apps/rush-mcp-server: + dependencies: + '@modelcontextprotocol/sdk': + specifier: ~1.10.2 + version: 1.10.2 + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + zod: + specifier: ~3.25.76 + version: 3.25.76 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../apps/rush-serve-dashboard: + dependencies: + tslib: + specifier: ~2.8.1 + version: 2.8.1 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-web-rig: + specifier: workspace:* + version: link:../../rigs/local-web-rig + + ../../../apps/trace-import: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + resolve: + specifier: ~1.22.1 + version: 1.22.11 + semver: + specifier: ~7.7.4 + version: 7.7.4 + typescript: + specifier: ~5.8.2 + version: 5.8.2 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../heft + '@types/resolve': + specifier: 1.20.2 + version: 1.20.2 + '@types/semver': + specifier: 7.7.1 + version: 7.7.1 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../apps/zipsync: + dependencies: + '@rushstack/lookup-by-path': + specifier: workspace:* + version: link:../../libraries/lookup-by-path + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + typescript: + specifier: ~5.8.2 + version: 5.8.2 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests-samples/heft-node-basic-tutorial: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests-samples/heft-node-jest-tutorial: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests-samples/heft-node-rig-tutorial: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-node-rig': + specifier: workspace:* + version: link:../../rigs/heft-node-rig + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + + ../../../build-tests-samples/heft-serverless-stack-tutorial: + devDependencies: + '@aws-sdk/client-sso-oidc': + specifier: ^3.567.0 + version: 3.1023.0 + '@aws-sdk/client-sts': + specifier: ^3.567.0 + version: 3.1023.0 + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-serverless-stack-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-serverless-stack-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@serverless-stack/aws-lambda-ric': + specifier: ^2.0.12 + version: 2.0.13 + '@serverless-stack/cli': + specifier: 1.18.4 + version: 1.18.4(constructs@10.0.130) + '@serverless-stack/resources': + specifier: 1.18.4 + version: 1.18.4 + '@types/aws-lambda': + specifier: 8.10.93 + version: 8.10.93 + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + aws-cdk-lib: + specifier: 2.189.1 + version: 2.189.1(constructs@10.0.130) + constructs: + specifier: ~10.0.98 + version: 10.0.130 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests-samples/heft-storybook-v6-react-tutorial: + dependencies: + react: + specifier: ~17.0.2 + version: 17.0.2 + react-dom: + specifier: ~17.0.2 + version: 17.0.2(react@17.0.2) + tslib: + specifier: ~2.8.1 + version: 2.8.1 + devDependencies: + '@babel/core': + specifier: ~7.20.0 + version: 7.20.12 + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-storybook-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-storybook-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@rushstack/heft-webpack4-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack4-plugin + '@rushstack/webpack4-module-minifier-plugin': + specifier: workspace:* + version: link:../../webpack/webpack4-module-minifier-plugin + '@storybook/react': + specifier: ~6.4.18 + version: 6.4.22(@babel/core@7.20.12)(@types/node@20.17.19)(@types/react@17.0.74)(@types/webpack@4.41.32)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(type-fest@0.21.3)(typescript@5.8.2)(webpack-dev-server@5.2.3(@types/webpack@4.41.32)(webpack@4.47.0))(webpack-hot-middleware@2.26.1) + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/react': + specifier: 17.0.74 + version: 17.0.74 + '@types/react-dom': + specifier: 17.0.25 + version: 17.0.25 + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + css-loader: + specifier: ~5.2.7 + version: 5.2.7(webpack@4.47.0) + eslint: + specifier: ~9.37.0 + version: 9.37.0 + heft-storybook-v6-react-tutorial-storykit: + specifier: workspace:* + version: link:../heft-storybook-v6-react-tutorial-storykit + html-webpack-plugin: + specifier: ~4.5.2 + version: 4.5.2(webpack@4.47.0) + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + source-map-loader: + specifier: ~1.1.3 + version: 1.1.3(webpack@4.47.0) + style-loader: + specifier: ~2.0.0 + version: 2.0.0(webpack@4.47.0) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + webpack: + specifier: ~4.47.0 + version: 4.47.0 + + ../../../build-tests-samples/heft-storybook-v6-react-tutorial-app: + dependencies: + heft-storybook-v6-react-tutorial: + specifier: 'workspace: *' + version: link:../heft-storybook-v6-react-tutorial + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-storybook-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-storybook-plugin + heft-storybook-v6-react-tutorial-storykit: + specifier: workspace:* + version: link:../heft-storybook-v6-react-tutorial-storykit + + ../../../build-tests-samples/heft-storybook-v6-react-tutorial-storykit: + dependencies: + '@babel/core': + specifier: ~7.20.0 + version: 7.20.12 + '@storybook/addon-actions': + specifier: ~6.4.18 + version: 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/addon-essentials': + specifier: ~6.4.18 + version: 6.4.22(@babel/core@7.20.12)(@storybook/react@6.4.22(@babel/core@7.20.12)(@types/node@20.17.19)(@types/react@17.0.74)(@types/webpack@4.41.32)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(type-fest@0.21.3)(typescript@5.8.2)(webpack-dev-server@5.2.3(@types/webpack@4.41.32)(webpack@4.47.0))(webpack-hot-middleware@2.26.1))(@types/react@17.0.74)(babel-loader@8.2.5(@babel/core@7.20.12)(webpack@4.47.0))(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)(webpack@4.47.0) + '@storybook/addon-links': + specifier: ~6.4.18 + version: 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/cli': + specifier: ~6.4.18 + version: 6.4.22(eslint@9.37.0)(jest@29.3.1(@types/node@20.17.19)(babel-plugin-macros@3.1.0))(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2) + '@storybook/components': + specifier: ~6.4.18 + version: 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/core-events': + specifier: ~6.4.18 + version: 6.4.22 + '@storybook/react': + specifier: ~6.4.18 + version: 6.4.22(@babel/core@7.20.12)(@types/node@20.17.19)(@types/react@17.0.74)(@types/webpack@4.41.32)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(type-fest@0.21.3)(typescript@5.8.2)(webpack-dev-server@5.2.3(@types/webpack@4.41.32)(webpack@4.47.0))(webpack-hot-middleware@2.26.1) + '@storybook/theming': + specifier: ~6.4.18 + version: 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/react': + specifier: 17.0.74 + version: 17.0.74 + '@types/react-dom': + specifier: 17.0.25 + version: 17.0.25 + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + babel-loader: + specifier: ~8.2.3 + version: 8.2.5(@babel/core@7.20.12)(webpack@4.47.0) + css-loader: + specifier: ~5.2.7 + version: 5.2.7(webpack@4.47.0) + jest: + specifier: ~29.3.1 + version: 29.3.1(@types/node@20.17.19)(babel-plugin-macros@3.1.0) + react: + specifier: ~17.0.2 + version: 17.0.2 + react-dom: + specifier: ~17.0.2 + version: 17.0.2(react@17.0.2) + style-loader: + specifier: ~2.0.0 + version: 2.0.0(webpack@4.47.0) + terser-webpack-plugin: + specifier: ~3.0.8 + version: 3.0.8(webpack@4.47.0) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + webpack: + specifier: ~4.47.0 + version: 4.47.0 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-web-rig: + specifier: workspace:* + version: link:../../rigs/local-web-rig + + ../../../build-tests-samples/heft-storybook-v9-react-tutorial: + dependencies: + react: + specifier: ~19.2.3 + version: 19.2.4 + react-dom: + specifier: ~19.2.3 + version: 19.2.4(react@19.2.4) + tslib: + specifier: ~2.8.1 + version: 2.8.1 + devDependencies: + '@babel/core': + specifier: ~7.20.0 + version: 7.20.12 + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-storybook-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-storybook-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@rushstack/heft-webpack5-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack5-plugin + '@rushstack/module-minifier': + specifier: workspace:* + version: link:../../libraries/module-minifier + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/webpack5-module-minifier-plugin': + specifier: workspace:* + version: link:../../webpack/webpack5-module-minifier-plugin + '@storybook/react': + specifier: ~9.1.6 + version: 9.1.20(@types/node@20.17.19)(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1))(typescript@5.8.2) + '@storybook/react-webpack5': + specifier: ~9.1.6 + version: 9.1.20(@rspack/core@1.6.8(@swc/helpers@0.5.21))(@types/node@20.17.19)(@types/react@19.2.7)(@types/webpack@4.41.32)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1))(typescript@5.8.2) + '@testing-library/dom': + specifier: ~7.21.4 + version: 7.21.8 + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/react': + specifier: 19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: 19.2.3 + version: 19.2.3(@types/react@19.2.7) + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + css-loader: + specifier: ~5.2.7 + version: 5.2.7(webpack@5.105.4) + eslint: + specifier: ~9.37.0 + version: 9.37.0 + heft-storybook-v9-react-tutorial-storykit: + specifier: workspace:* + version: link:../heft-storybook-v9-react-tutorial-storykit + html-webpack-plugin: + specifier: ~5.5.0 + version: 5.5.4(webpack@5.105.4) + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + source-map-loader: + specifier: ~1.1.3 + version: 1.1.3(webpack@5.105.4) + storybook: + specifier: ~9.1.6 + version: 9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1) + style-loader: + specifier: ~2.0.0 + version: 2.0.0(webpack@5.105.4) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../build-tests-samples/heft-storybook-v9-react-tutorial-app: + dependencies: + heft-storybook-v9-react-tutorial: + specifier: 'workspace: *' + version: link:../heft-storybook-v9-react-tutorial + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-storybook-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-storybook-plugin + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + heft-storybook-v9-react-tutorial-storykit: + specifier: workspace:* + version: link:../heft-storybook-v9-react-tutorial-storykit + + ../../../build-tests-samples/heft-storybook-v9-react-tutorial-storykit: + dependencies: + '@babel/core': + specifier: ~7.20.0 + version: 7.20.12 + '@storybook/cli': + specifier: ~9.1.6 + version: 9.1.20(@babel/preset-env@7.29.2(@babel/core@7.20.12))(@testing-library/dom@7.21.8)(prettier@3.8.1) + '@storybook/react': + specifier: ~9.1.6 + version: 9.1.20(@types/node@20.17.19)(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1))(typescript@5.8.2) + '@storybook/react-webpack5': + specifier: ~9.1.6 + version: 9.1.20(@rspack/core@1.6.8(@swc/helpers@0.5.21))(@types/node@20.17.19)(@types/react@19.2.7)(@types/webpack@4.41.32)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1))(typescript@5.8.2) + '@testing-library/dom': + specifier: ~7.21.4 + version: 7.21.8 + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/react': + specifier: 19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: 19.2.3 + version: 19.2.3(@types/react@19.2.7) + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + babel-loader: + specifier: ~8.2.3 + version: 8.2.5(@babel/core@7.20.12)(webpack@5.105.4) + css-loader: + specifier: ~5.2.7 + version: 5.2.7(webpack@5.105.4) + jest: + specifier: ~29.3.1 + version: 29.3.1(@types/node@20.17.19)(babel-plugin-macros@3.1.0) + react: + specifier: ~19.2.3 + version: 19.2.4 + react-dom: + specifier: ~19.2.3 + version: 19.2.4(react@19.2.4) + storybook: + specifier: ~9.1.6 + version: 9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1) + style-loader: + specifier: ~2.0.0 + version: 2.0.0(webpack@5.105.4) + terser-webpack-plugin: + specifier: ~3.0.8 + version: 3.0.8(webpack@5.105.4) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + webpack: + specifier: ~5.105.2 + version: 5.105.4 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-web-rig: + specifier: workspace:* + version: link:../../rigs/local-web-rig + + ../../../build-tests-samples/heft-web-rig-app-tutorial: + dependencies: + heft-web-rig-library-tutorial: + specifier: workspace:* + version: link:../heft-web-rig-library-tutorial + react: + specifier: ~19.2.3 + version: 19.2.4 + react-dom: + specifier: ~19.2.3 + version: 19.2.4(react@19.2.4) + tslib: + specifier: ~2.8.1 + version: 2.8.1 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-web-rig': + specifier: workspace:* + version: link:../../rigs/heft-web-rig + '@types/react': + specifier: 19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: 19.2.3 + version: 19.2.3(@types/react@19.2.7) + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + + ../../../build-tests-samples/heft-web-rig-library-tutorial: + dependencies: + react: + specifier: ~19.2.3 + version: 19.2.4 + react-dom: + specifier: ~19.2.3 + version: 19.2.4(react@19.2.4) + tslib: + specifier: ~2.8.1 + version: 2.8.1 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-web-rig': + specifier: workspace:* + version: link:../../rigs/heft-web-rig + '@types/react': + specifier: 19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: 19.2.3 + version: 19.2.3(@types/react@19.2.7) + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + + ../../../build-tests-samples/heft-webpack-basic-tutorial: + dependencies: + react: + specifier: ~19.2.3 + version: 19.2.4 + react-dom: + specifier: ~19.2.3 + version: 19.2.4(react@19.2.4) + tslib: + specifier: ~2.8.1 + version: 2.8.1 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@rushstack/heft-webpack5-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack5-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/react': + specifier: 19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: 19.2.3 + version: 19.2.3(@types/react@19.2.7) + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + css-loader: + specifier: ~6.6.0 + version: 6.6.0(webpack@5.105.4) + eslint: + specifier: ~9.37.0 + version: 9.37.0 + html-webpack-plugin: + specifier: ~5.5.0 + version: 5.5.4(webpack@5.105.4) + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + source-map-loader: + specifier: ~3.0.1 + version: 3.0.2(webpack@5.105.4) + style-loader: + specifier: ~3.3.1 + version: 3.3.4(webpack@5.105.4) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../build-tests-samples/packlets-tutorial: + devDependencies: + '@rushstack/eslint-config': + specifier: workspace:* + version: link:../../eslint/eslint-config + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~8.57.0 + version: 8.57.1 + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/api-documenter-scenarios: + devDependencies: + '@microsoft/api-documenter': + specifier: workspace:* + version: link:../../apps/api-documenter + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + run-scenarios-helpers: + specifier: workspace:* + version: link:../run-scenarios-helpers + + ../../../build-tests/api-documenter-test: + devDependencies: + '@microsoft/api-documenter': + specifier: workspace:* + version: link:../../apps/api-documenter + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/api-extractor-d-cts-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/api-extractor-d-mts-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/api-extractor-lib1-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + typescript: + specifier: ~2.9.2 + version: 2.9.2 + + ../../../build-tests/api-extractor-lib2-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/api-extractor-lib3-test: + dependencies: + api-extractor-lib1-test: + specifier: workspace:* + version: link:../api-extractor-lib1-test + devDependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/api-extractor-lib4-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/api-extractor-lib5-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/api-extractor-scenarios: + dependencies: + api-extractor-lib1-test: + specifier: workspace:* + version: link:../api-extractor-lib1-test + devDependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@microsoft/teams-js': + specifier: 1.3.0-beta.4 + version: 1.3.0-beta.4 + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + api-extractor-lib2-test: + specifier: workspace:* + version: link:../api-extractor-lib2-test + api-extractor-lib3-test: + specifier: workspace:* + version: link:../api-extractor-lib3-test + api-extractor-lib4-test: + specifier: workspace:* + version: link:../api-extractor-lib4-test + api-extractor-lib5-test: + specifier: workspace:* + version: link:../api-extractor-lib5-test + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + run-scenarios-helpers: + specifier: workspace:* + version: link:../run-scenarios-helpers + + ../../../build-tests/api-extractor-test-01: + dependencies: + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/long': + specifier: 4.0.0 + version: 4.0.0 + long: + specifier: ^4.0.0 + version: 4.0.0 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/api-extractor-test-02: + dependencies: + '@types/long': + specifier: 4.0.0 + version: 4.0.0 + '@types/semver': + specifier: 7.7.1 + version: 7.7.1 + api-extractor-test-01: + specifier: workspace:* + version: link:../api-extractor-test-01 + semver: + specifier: ~7.7.4 + version: 7.7.4 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/api-extractor-test-03: + dependencies: + api-extractor-test-02: + specifier: workspace:* + version: link:../api-extractor-test-02 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/api-extractor-test-04: + dependencies: + api-extractor-lib1-test: + specifier: workspace:* + version: link:../api-extractor-lib1-test + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/api-extractor-test-05: + dependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/eslint-7-11-test: + devDependencies: + '@rushstack/eslint-config': + specifier: 3.7.1 + version: 3.7.1(eslint@7.11.0)(typescript@5.8.2) + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@typescript-eslint/parser': + specifier: ~6.19.0 + version: 6.19.1(eslint@7.11.0)(typescript@5.8.2) + eslint: + specifier: 7.11.0 + version: 7.11.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/eslint-7-7-test: + devDependencies: + '@rushstack/eslint-config': + specifier: 3.7.1 + version: 3.7.1(eslint@7.7.0)(typescript@5.8.2) + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@typescript-eslint/parser': + specifier: ~6.19.0 + version: 6.19.1(eslint@7.7.0)(typescript@5.8.2) + eslint: + specifier: 7.7.0 + version: 7.7.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/eslint-7-test: + devDependencies: + '@rushstack/eslint-config': + specifier: 3.7.1 + version: 3.7.1(eslint@7.30.0)(typescript@5.8.2) + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@typescript-eslint/parser': + specifier: ~6.19.0 + version: 6.19.1(eslint@7.30.0)(typescript@5.8.2) + eslint: + specifier: ~7.30.0 + version: 7.30.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/eslint-8-test: + devDependencies: + '@rushstack/eslint-config': + specifier: workspace:* + version: link:../../eslint/eslint-config + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@typescript-eslint/parser': + specifier: ~8.56.1 + version: 8.56.1(eslint@8.57.1)(typescript@5.8.2) + eslint: + specifier: ~8.57.0 + version: 8.57.1 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/eslint-9-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@typescript-eslint/parser': + specifier: ~8.56.1 + version: 8.56.1(eslint@9.37.0)(typescript@5.8.2) + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/eslint-bulk-suppressions-test: + devDependencies: + '@rushstack/eslint-bulk': + specifier: workspace:* + version: link:../../eslint/eslint-bulk + '@rushstack/eslint-config': + specifier: workspace:* + version: link:../../eslint/eslint-config + '@rushstack/eslint-patch': + specifier: workspace:* + version: link:../../eslint/eslint-patch + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@typescript-eslint/parser': + specifier: ~8.56.1 + version: 8.56.1(eslint@8.57.1)(typescript@5.8.2) + eslint: + specifier: ~8.57.0 + version: 8.57.1 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/eslint-bulk-suppressions-test-flat: + devDependencies: + '@rushstack/eslint-bulk': + specifier: workspace:* + version: link:../../eslint/eslint-bulk + '@rushstack/eslint-patch': + specifier: workspace:* + version: link:../../eslint/eslint-patch + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@typescript-eslint/parser': + specifier: ~8.56.1 + version: 8.56.1(eslint@9.37.0)(typescript@5.8.2) + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/eslint-bulk-suppressions-test-legacy: + devDependencies: + '@rushstack/eslint-bulk': + specifier: workspace:* + version: link:../../eslint/eslint-bulk + '@rushstack/eslint-config': + specifier: 3.7.1 + version: 3.7.1(eslint@8.57.1)(typescript@5.8.2) + '@rushstack/eslint-patch': + specifier: workspace:* + version: link:../../eslint/eslint-patch + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@typescript-eslint/parser': + specifier: ~8.56.1 + version: 8.56.1(eslint@8.57.1)(typescript@5.8.2) + eslint: + specifier: ~8.57.0 + version: 8.57.1 + eslint-8.23: + specifier: npm:eslint@8.23.1 + version: eslint@8.23.1 + eslint-oldest: + specifier: npm:eslint@8.6.0 + version: eslint@8.6.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/esm-node-import-test: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/hashed-folder-copy-plugin-webpack5-test: + devDependencies: + '@rushstack/hashed-folder-copy-plugin': + specifier: workspace:* + version: link:../../webpack/hashed-folder-copy-plugin + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@rushstack/heft-webpack5-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack5-plugin + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + html-webpack-plugin: + specifier: ~5.5.0 + version: 5.5.4(webpack@5.105.4) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + webpack: + specifier: ~5.105.2 + version: 5.105.4 + webpack-bundle-analyzer: + specifier: ~4.5.0 + version: 4.5.0 + + ../../../build-tests/heft-copy-files-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + + ../../../build-tests/heft-example-lifecycle-plugin: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/heft-example-plugin-01: + dependencies: + tapable: + specifier: 1.1.3 + version: 1.1.3 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/tapable': + specifier: 1.0.6 + version: 1.0.6 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/heft-example-plugin-02: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + heft-example-plugin-01: + specifier: workspace:* + version: link:../heft-example-plugin-01 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/heft-fastify-test: + dependencies: + fastify: + specifier: ~3.16.1 + version: 3.16.2 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/heft-jest-preset-test: + devDependencies: + '@jest/types': + specifier: 30.3.0 + version: 30.3.0 + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/heft-jest-reporters-test: + devDependencies: + '@jest/reporters': + specifier: ~30.3.0 + version: 30.3.0 + '@jest/types': + specifier: 30.3.0 + version: 30.3.0 + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/heft-json-schema-typings-plugin-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-json-schema-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-json-schema-typings-plugin + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/heft-minimal-rig-test: + dependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/heft-api-extractor-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-api-extractor-plugin + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/heft-minimal-rig-usage-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + heft-minimal-rig-test: + specifier: workspace:* + version: link:../heft-minimal-rig-test + + ../../../build-tests/heft-node-everything-esm-module-test: + devDependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-api-extractor-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-api-extractor-plugin + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-static-asset-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-static-asset-typings-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + heft-example-plugin-01: + specifier: workspace:* + version: link:../heft-example-plugin-01 + heft-example-plugin-02: + specifier: workspace:* + version: link:../heft-example-plugin-02 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + tslint: + specifier: ~5.20.1 + version: 5.20.1(typescript@5.8.2) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/heft-node-everything-test: + devDependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-api-extractor-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-api-extractor-plugin + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-static-asset-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-static-asset-typings-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + heft-example-lifecycle-plugin: + specifier: workspace:* + version: link:../heft-example-lifecycle-plugin + heft-example-plugin-01: + specifier: workspace:* + version: link:../heft-example-plugin-01 + heft-example-plugin-02: + specifier: workspace:* + version: link:../heft-example-plugin-02 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + tslint: + specifier: ~5.20.1 + version: 5.20.1(typescript@5.8.2) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/heft-parameter-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/heft-parameter-plugin-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + heft-parameter-plugin: + specifier: workspace:* + version: link:../heft-parameter-plugin + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/heft-rspack-everything-test: + devDependencies: + '@rspack/core': + specifier: ~1.6.0-beta.0 + version: 1.6.8(@swc/helpers@0.5.21) + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-dev-cert-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-dev-cert-plugin + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-rspack-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-rspack-plugin + '@rushstack/heft-static-asset-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-static-asset-typings-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/heft-sass-test: + dependencies: + buttono: + specifier: ~1.0.2 + version: 1.0.4 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-sass-load-themed-styles-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-sass-load-themed-styles-plugin + '@rushstack/heft-sass-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-sass-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@rushstack/heft-webpack4-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack4-plugin + '@rushstack/webpack4-module-minifier-plugin': + specifier: workspace:* + version: link:../../webpack/webpack4-module-minifier-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/react': + specifier: 19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: 19.2.3 + version: 19.2.3(@types/react@19.2.7) + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + autoprefixer: + specifier: ~10.4.2 + version: 10.4.27(postcss@8.5.12) + css-loader: + specifier: ~5.2.7 + version: 5.2.7(webpack@4.47.0) + eslint: + specifier: ~9.37.0 + version: 9.37.0 + html-webpack-plugin: + specifier: ~4.5.2 + version: 4.5.2(webpack@4.47.0) + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + postcss: + specifier: ~8.5.10 + version: 8.5.12 + postcss-loader: + specifier: ~4.1.0 + version: 4.1.0(postcss@8.5.12)(webpack@4.47.0) + react: + specifier: ~19.2.3 + version: 19.2.4 + react-dom: + specifier: ~19.2.3 + version: 19.2.4(react@19.2.4) + style-loader: + specifier: ~2.0.0 + version: 2.0.0(webpack@4.47.0) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + webpack: + specifier: ~4.47.0 + version: 4.47.0 + + ../../../build-tests/heft-swc-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-isolated-typescript-transpile-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-isolated-typescript-transpile-plugin + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/heft-typescript-composite-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + tslint: + specifier: ~5.20.1 + version: 5.20.1(typescript@5.8.2) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../build-tests/heft-typescript-v2-test: + devDependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-api-extractor-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-api-extractor-plugin + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/jest': + specifier: ts2.9 + version: 23.3.13 + '@types/node': + specifier: ts2.9 + version: 14.0.1 + tslint: + specifier: ~5.20.1 + version: 5.20.1(typescript@2.9.2) + typescript: + specifier: ~2.9.2 + version: 2.9.2 + + ../../../build-tests/heft-typescript-v3-test: + devDependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-api-extractor-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-api-extractor-plugin + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/jest': + specifier: ts3.9 + version: 28.1.1 + '@types/node': + specifier: ts3.9 + version: 17.0.41 + tslint: + specifier: ~5.20.1 + version: 5.20.1(typescript@3.9.10) + typescript: + specifier: ~3.9.10 + version: 3.9.10 + + ../../../build-tests/heft-typescript-v4-test: + devDependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/eslint-config': + specifier: 4.6.4 + version: 4.6.4(eslint@8.57.1)(typescript@4.9.5) + '@rushstack/eslint-patch': + specifier: workspace:* + version: link:../../eslint/eslint-patch + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-api-extractor-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-api-extractor-plugin + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/jest': + specifier: ts4.9 + version: 29.5.14 + '@types/node': + specifier: ts4.9 + version: 22.9.3 + eslint: + specifier: ~8.57.0 + version: 8.57.1 + tslint: + specifier: ~5.20.1 + version: 5.20.1(typescript@4.9.5) + typescript: + specifier: ~4.9.5 + version: 4.9.5 + + ../../../build-tests/heft-web-rig-library-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-web-rig': + specifier: workspace:* + version: link:../../rigs/heft-web-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + + ../../../build-tests/heft-webpack4-everything-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-dev-cert-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-dev-cert-plugin + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-static-asset-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-static-asset-typings-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@rushstack/heft-webpack4-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack4-plugin + '@rushstack/module-minifier': + specifier: workspace:* + version: link:../../libraries/module-minifier + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/webpack4-module-minifier-plugin': + specifier: workspace:* + version: link:../../webpack/webpack4-module-minifier-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + file-loader: + specifier: ~6.0.0 + version: 6.0.0(webpack@4.47.0) + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + source-map-loader: + specifier: ~1.1.3 + version: 1.1.3(webpack@4.47.0) + tslint: + specifier: ~5.20.1 + version: 5.20.1(typescript@5.8.2) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + webpack: + specifier: ~4.47.0 + version: 4.47.0 + + ../../../build-tests/heft-webpack5-everything-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-dev-cert-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-dev-cert-plugin + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-static-asset-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-static-asset-typings-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@rushstack/heft-webpack5-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack5-plugin + '@rushstack/module-minifier': + specifier: workspace:* + version: link:../../libraries/module-minifier + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + '@rushstack/webpack5-module-minifier-plugin': + specifier: workspace:* + version: link:../../webpack/webpack5-module-minifier-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + html-webpack-plugin: + specifier: ~5.5.0 + version: 5.5.4(webpack@5.105.4) + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + source-map-loader: + specifier: ~3.0.1 + version: 3.0.2(webpack@5.105.4) + tslint: + specifier: ~5.20.1 + version: 5.20.1(typescript@5.8.2) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../build-tests/localization-plugin-test-01: + dependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-webpack4-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack4-plugin + '@rushstack/set-webpack-public-path-plugin': + specifier: ^4.1.16 + version: 4.1.16(@types/node@22.9.3)(@types/webpack@4.41.32)(webpack@4.47.0) + '@rushstack/webpack4-localization-plugin': + specifier: workspace:* + version: link:../../webpack/webpack4-localization-plugin + '@rushstack/webpack4-module-minifier-plugin': + specifier: workspace:* + version: link:../../webpack/webpack4-module-minifier-plugin + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + html-webpack-plugin: + specifier: ~4.5.2 + version: 4.5.2(webpack@4.47.0) + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + webpack: + specifier: ~4.47.0 + version: 4.47.0 + webpack-bundle-analyzer: + specifier: ~4.5.0 + version: 4.5.0 + webpack-dev-server: + specifier: ~4.9.3 + version: 4.9.3(@types/webpack@4.41.32)(webpack@4.47.0) + + ../../../build-tests/localization-plugin-test-02: + dependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-localization-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-localization-typings-plugin + '@rushstack/heft-webpack4-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack4-plugin + '@rushstack/set-webpack-public-path-plugin': + specifier: ^4.1.16 + version: 4.1.16(@types/node@22.9.3)(@types/webpack@4.41.32)(webpack@4.47.0) + '@rushstack/webpack4-localization-plugin': + specifier: workspace:* + version: link:../../webpack/webpack4-localization-plugin + '@rushstack/webpack4-module-minifier-plugin': + specifier: workspace:* + version: link:../../webpack/webpack4-module-minifier-plugin + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + html-webpack-plugin: + specifier: ~4.5.2 + version: 4.5.2(webpack@4.47.0) + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + webpack: + specifier: ~4.47.0 + version: 4.47.0 + webpack-bundle-analyzer: + specifier: ~4.5.0 + version: 4.5.0 + webpack-dev-server: + specifier: ~4.9.3 + version: 4.9.3(@types/webpack@4.41.32)(webpack@4.47.0) + + ../../../build-tests/localization-plugin-test-03: + dependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-webpack4-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack4-plugin + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/set-webpack-public-path-plugin': + specifier: ^4.1.16 + version: 4.1.16(@types/node@22.9.3)(@types/webpack@4.41.32)(webpack@4.47.0) + '@rushstack/webpack4-localization-plugin': + specifier: workspace:* + version: link:../../webpack/webpack4-localization-plugin + '@rushstack/webpack4-module-minifier-plugin': + specifier: workspace:* + version: link:../../webpack/webpack4-module-minifier-plugin + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + html-webpack-plugin: + specifier: ~4.5.2 + version: 4.5.2(webpack@4.47.0) + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + ts-loader: + specifier: 6.0.0 + version: 6.0.0(typescript@5.8.2) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + webpack: + specifier: ~4.47.0 + version: 4.47.0 + webpack-bundle-analyzer: + specifier: ~4.5.0 + version: 4.5.0 + webpack-dev-server: + specifier: ~4.9.3 + version: 4.9.3(@types/webpack@4.41.32)(webpack@4.47.0) + + ../../../build-tests/package-extractor-test-01: + dependencies: + package-extractor-test-02: + specifier: workspace:* + version: link:../package-extractor-test-02 + devDependencies: + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + package-extractor-test-03: + specifier: workspace:* + version: link:../package-extractor-test-03 + + ../../../build-tests/package-extractor-test-02: + dependencies: + package-extractor-test-03: + specifier: workspace:* + version: link:../package-extractor-test-03 + + ../../../build-tests/package-extractor-test-03: + devDependencies: + '@types/node': + specifier: ts3.9 + version: 17.0.41 + + ../../../build-tests/package-extractor-test-04: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + + ../../../build-tests/package-extractor-test-05: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + + ../../../build-tests/run-scenarios-helpers: + dependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/rush-amazon-s3-build-cache-plugin-integration-test: + devDependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-amazon-s3-build-cache-plugin': + specifier: workspace:* + version: link:../../rush-plugins/rush-amazon-s3-build-cache-plugin + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@types/http-proxy': + specifier: ~1.17.8 + version: 1.17.17 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + http-proxy: + specifier: ~1.18.1 + version: 1.18.1 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/rush-lib-declaration-paths-test: + dependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/rush-mcp-example-plugin: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/mcp-server': + specifier: workspace:* + version: link:../../apps/rush-mcp-server + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/rush-package-manager-integration-test: + devDependencies: + '@microsoft/rush': + specifier: workspace:* + version: link:../../apps/rush + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/rush-project-change-analyzer-test: + dependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/rush-redis-cobuild-plugin-integration-test: + devDependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-redis-cobuild-plugin': + specifier: workspace:* + version: link:../../rush-plugins/rush-redis-cobuild-plugin + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@types/http-proxy': + specifier: ~1.17.8 + version: 1.17.17 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + http-proxy: + specifier: ~1.18.1 + version: 1.18.1 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../build-tests/set-webpack-public-path-plugin-test: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@rushstack/heft-webpack5-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack5-plugin + '@rushstack/module-minifier': + specifier: workspace:* + version: link:../../libraries/module-minifier + '@rushstack/set-webpack-public-path-plugin': + specifier: workspace:* + version: link:../../webpack/set-webpack-public-path-plugin + '@rushstack/webpack5-module-minifier-plugin': + specifier: workspace:* + version: link:../../webpack/webpack5-module-minifier-plugin + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~8.57.0 + version: 8.57.1 + html-webpack-plugin: + specifier: ~5.5.0 + version: 5.5.4(webpack@5.105.4) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../eslint/eslint-bulk: + devDependencies: + '@rushstack/eslint-patch': + specifier: workspace:* + version: link:../eslint-patch + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../eslint/eslint-config: + dependencies: + '@rushstack/eslint-patch': + specifier: workspace:* + version: link:../eslint-patch + '@rushstack/eslint-plugin': + specifier: workspace:* + version: link:../eslint-plugin + '@rushstack/eslint-plugin-packlets': + specifier: workspace:* + version: link:../eslint-plugin-packlets + '@rushstack/eslint-plugin-security': + specifier: workspace:* + version: link:../eslint-plugin-security + '@typescript-eslint/eslint-plugin': + specifier: ~8.56.1 + version: 8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.37.0)(typescript@5.8.2))(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/parser': + specifier: ~8.56.1 + version: 8.56.1(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/typescript-estree': + specifier: ~8.56.1 + version: 8.56.1(typescript@5.8.2) + '@typescript-eslint/utils': + specifier: ~8.56.1 + version: 8.56.1(eslint@9.37.0)(typescript@5.8.2) + eslint-plugin-promise: + specifier: ~7.2.1 + version: 7.2.1(eslint@9.37.0) + eslint-plugin-react: + specifier: ~7.37.5 + version: 7.37.5(eslint@9.37.0) + eslint-plugin-tsdoc: + specifier: ~0.5.1 + version: 0.5.2(eslint@9.37.0)(typescript@5.8.2) + devDependencies: + eslint: + specifier: ~9.37.0 + version: 9.37.0 + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../eslint/eslint-patch: + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + '@types/eslint-8': + specifier: npm:@types/eslint@8.56.10 + version: '@types/eslint@8.56.10' + '@types/eslint-9': + specifier: npm:@types/eslint@9.6.1 + version: '@types/eslint@9.6.1' + '@typescript-eslint/types': + specifier: ~8.56.1 + version: 8.56.1(typescript@5.8.2) + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + eslint-8: + specifier: npm:eslint@~8.57.0 + version: eslint@8.57.1 + eslint-9: + specifier: npm:eslint@~9.25.1 + version: eslint@9.25.1 + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../eslint/eslint-plugin: + dependencies: + '@rushstack/tree-pattern': + specifier: workspace:* + version: link:../../libraries/tree-pattern + '@typescript-eslint/utils': + specifier: ~8.56.1 + version: 8.56.1(eslint@9.37.0)(typescript@5.8.2) + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + '@typescript-eslint/parser': + specifier: ~8.56.1 + version: 8.56.1(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/rule-tester': + specifier: ~8.56.1 + version: 8.56.1(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/types': + specifier: ~8.56.1 + version: 8.56.1(typescript@5.8.2) + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../eslint/eslint-plugin-packlets: + dependencies: + '@rushstack/tree-pattern': + specifier: workspace:* + version: link:../../libraries/tree-pattern + '@typescript-eslint/utils': + specifier: ~8.56.1 + version: 8.56.1(eslint@9.37.0)(typescript@5.8.2) + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + '@typescript-eslint/parser': + specifier: ~8.56.1 + version: 8.56.1(eslint@9.37.0)(typescript@5.8.2) + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../eslint/eslint-plugin-security: + dependencies: + '@rushstack/tree-pattern': + specifier: workspace:* + version: link:../../libraries/tree-pattern + '@typescript-eslint/utils': + specifier: ~8.56.1 + version: 8.56.1(eslint@9.37.0)(typescript@5.8.2) + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + '@typescript-eslint/parser': + specifier: ~8.56.1 + version: 8.56.1(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/rule-tester': + specifier: ~8.56.1 + version: 8.56.1(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/typescript-estree': + specifier: ~8.56.1 + version: 8.56.1(typescript@5.8.2) + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../eslint/local-eslint-config: + dependencies: + '@rushstack/eslint-config': + specifier: workspace:* + version: link:../eslint-config + '@rushstack/eslint-patch': + specifier: workspace:* + version: link:../eslint-patch + '@rushstack/eslint-plugin': + specifier: workspace:* + version: link:../eslint-plugin + '@typescript-eslint/eslint-plugin': + specifier: ~8.56.1 + version: 8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.37.0)(typescript@5.8.2))(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/parser': + specifier: ~8.56.1 + version: 8.56.1(eslint@9.37.0)(typescript@5.8.2) + eslint-import-resolver-node: + specifier: 0.3.9 + version: 0.3.9 + eslint-plugin-header: + specifier: ~3.1.1 + version: 3.1.1(eslint@9.37.0) + eslint-plugin-headers: + specifier: ~1.2.1 + version: 1.2.1(eslint@9.37.0) + eslint-plugin-import: + specifier: 2.32.0 + version: 2.32.0(eslint@9.37.0) + eslint-plugin-jsdoc: + specifier: 50.6.11 + version: 50.6.11(eslint@9.37.0) + eslint-plugin-react-hooks: + specifier: 5.2.0 + version: 5.2.0(eslint@9.37.0) + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../heft-plugins/heft-api-extractor-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + semver: + specifier: ~7.7.4 + version: 7.7.4 + devDependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@types/semver': + specifier: 7.7.1 + version: 7.7.1 + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + + ../../../heft-plugins/heft-dev-cert-plugin: + dependencies: + '@rushstack/debug-certificate-manager': + specifier: workspace:* + version: link:../../libraries/debug-certificate-manager + devDependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../heft-plugins/heft-isolated-typescript-transpile-plugin: + dependencies: + '@rushstack/lookup-by-path': + specifier: workspace:* + version: link:../../libraries/lookup-by-path + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@swc/core': + specifier: 1.7.10 + version: 1.7.10(@swc/helpers@0.5.21) + '@types/tapable': + specifier: 1.0.6 + version: 1.0.6 + tapable: + specifier: 1.1.3 + version: 1.1.3 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../heft-typescript-plugin + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../heft-plugins/heft-jest-plugin: + dependencies: + '@jest/core': + specifier: ~30.3.0 + version: 30.3.0 + '@jest/reporters': + specifier: ~30.3.0 + version: 30.3.0 + '@jest/transform': + specifier: ~30.3.0 + version: 30.3.0 + '@rushstack/heft-config-file': + specifier: workspace:* + version: link:../../libraries/heft-config-file + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + jest-config: + specifier: ~30.3.0 + version: 30.3.0(@types/node@20.17.19) + jest-resolve: + specifier: ~30.3.0 + version: 30.3.0 + jest-snapshot: + specifier: ~30.3.0 + version: 30.3.0 + devDependencies: + '@jest/types': + specifier: 30.3.0 + version: 30.3.0 + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + jest-environment-jsdom: + specifier: ~30.3.0 + version: 30.3.0 + jest-environment-node: + specifier: ~30.3.0 + version: 30.3.0 + jest-watch-select-projects: + specifier: 2.0.0 + version: 2.0.0 + + ../../../heft-plugins/heft-json-schema-typings-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/typings-generator': + specifier: workspace:* + version: link:../../libraries/typings-generator + json-schema-to-typescript: + specifier: ~15.0.4 + version: 15.0.4 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../heft-plugins/heft-lint-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + json-stable-stringify-without-jsonify: + specifier: 1.0.1 + version: 1.0.1 + semver: + specifier: ~7.7.4 + version: 7.7.4 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../heft-typescript-plugin + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@types/eslint': + specifier: 9.6.1 + version: 9.6.1 + '@types/eslint-8': + specifier: npm:@types/eslint@8.56.10 + version: '@types/eslint@8.56.10' + '@types/json-stable-stringify-without-jsonify': + specifier: 1.0.2 + version: 1.0.2 + '@types/semver': + specifier: 7.7.1 + version: 7.7.1 + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + eslint-8: + specifier: npm:eslint@~8.57.0 + version: eslint@8.57.1 + tslint: + specifier: ~5.20.1 + version: 5.20.1(typescript@5.8.2) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../heft-plugins/heft-localization-typings-plugin: + dependencies: + '@rushstack/localization-utilities': + specifier: workspace:* + version: link:../../libraries/localization-utilities + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../heft-plugins/heft-rspack-plugin: + dependencies: + '@rspack/dev-server': + specifier: ^1.1.4 + version: 1.2.1(@rspack/core@1.6.8(@swc/helpers@0.5.21))(@types/webpack@4.41.32)(webpack@5.105.4) + '@rushstack/debug-certificate-manager': + specifier: workspace:* + version: link:../../libraries/debug-certificate-manager + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + tapable: + specifier: 2.3.0 + version: 2.3.0 + watchpack: + specifier: 2.4.0 + version: 2.4.0 + webpack: + specifier: ~5.105.2 + version: 5.105.4 + devDependencies: + '@rspack/core': + specifier: ~1.6.0-beta.0 + version: 1.6.8(@swc/helpers@0.5.21) + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@types/watchpack': + specifier: 2.4.0 + version: 2.4.0 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../heft-plugins/heft-sass-load-themed-styles-plugin: + dependencies: + '@microsoft/load-themed-styles': + specifier: workspace:* + version: link:../../libraries/load-themed-styles + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-sass-plugin': + specifier: workspace:* + version: link:../heft-sass-plugin + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../heft-plugins/heft-sass-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@types/tapable': + specifier: 1.0.6 + version: 1.0.6 + postcss: + specifier: ~8.5.10 + version: 8.5.12 + postcss-modules: + specifier: ~6.0.0 + version: 6.0.1(postcss@8.5.12) + sass-embedded: + specifier: ~1.85.1 + version: 1.85.1 + tapable: + specifier: 1.1.3 + version: 1.1.3 + devDependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../heft-plugins/heft-serverless-stack-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-webpack4-plugin': + specifier: workspace:* + version: link:../heft-webpack4-plugin + '@rushstack/heft-webpack5-plugin': + specifier: workspace:* + version: link:../heft-webpack5-plugin + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../heft-plugins/heft-static-asset-typings-plugin: + dependencies: + '@rushstack/heft-config-file': + specifier: workspace:* + version: link:../../libraries/heft-config-file + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/typings-generator': + specifier: workspace:* + version: link:../../libraries/typings-generator + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../heft-plugins/heft-storybook-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-rspack-plugin': + specifier: workspace:* + version: link:../heft-rspack-plugin + '@rushstack/heft-webpack4-plugin': + specifier: workspace:* + version: link:../heft-webpack4-plugin + '@rushstack/heft-webpack5-plugin': + specifier: workspace:* + version: link:../heft-webpack5-plugin + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../heft-plugins/heft-typescript-plugin: + dependencies: + '@rushstack/heft-config-file': + specifier: workspace:* + version: link:../../libraries/heft-config-file + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@types/tapable': + specifier: 1.0.6 + version: 1.0.6 + semver: + specifier: ~7.7.4 + version: 7.7.4 + tapable: + specifier: 1.1.3 + version: 1.1.3 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@types/semver': + specifier: 7.7.1 + version: 7.7.1 + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../heft-plugins/heft-vscode-extension-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@vscode/vsce': + specifier: 3.2.1 + version: 3.2.1 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../heft-plugins/heft-webpack4-plugin: + dependencies: + '@rushstack/debug-certificate-manager': + specifier: workspace:* + version: link:../../libraries/debug-certificate-manager + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@types/tapable': + specifier: 1.0.6 + version: 1.0.6 + tapable: + specifier: 1.1.3 + version: 1.1.3 + watchpack: + specifier: 2.4.0 + version: 2.4.0 + webpack-dev-server: + specifier: ~4.9.3 + version: 4.9.3(@types/webpack@4.41.32)(webpack@4.47.0) + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@types/watchpack': + specifier: 2.4.0 + version: 2.4.0 + '@types/webpack': + specifier: 4.41.32 + version: 4.41.32 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + webpack: + specifier: ~4.47.0 + version: 4.47.0 + + ../../../heft-plugins/heft-webpack5-plugin: + dependencies: + '@rushstack/debug-certificate-manager': + specifier: workspace:* + version: link:../../libraries/debug-certificate-manager + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@types/tapable': + specifier: 1.0.6 + version: 1.0.6 + tapable: + specifier: 1.1.3 + version: 1.1.3 + watchpack: + specifier: 2.4.0 + version: 2.4.0 + webpack-dev-server: + specifier: ^5.1.0 + version: 5.2.3(webpack@5.105.4) + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@types/watchpack': + specifier: 2.4.0 + version: 2.4.0 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../libraries/api-extractor-model: + dependencies: + '@microsoft/tsdoc': + specifier: ~0.16.0 + version: 0.16.0 + '@microsoft/tsdoc-config': + specifier: ~0.18.1 + version: 0.18.1 + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + + ../../../libraries/credential-cache: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../libraries/debug-certificate-manager: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../terminal + node-forge: + specifier: ~1.4.0 + version: 1.4.0 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/node-forge': + specifier: 1.3.14 + version: 1.3.14 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../libraries/heft-config-file: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + '@rushstack/rig-package': + specifier: workspace:* + version: link:../rig-package + '@rushstack/terminal': + specifier: workspace:* + version: link:../terminal + jsonpath-plus: + specifier: ~10.3.0 + version: 10.3.0 + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + + ../../../libraries/load-themed-styles: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-web-rig: + specifier: workspace:* + version: link:../../rigs/local-web-rig + + ../../../libraries/localization-utilities: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../terminal + '@rushstack/typings-generator': + specifier: workspace:* + version: link:../typings-generator + pseudolocale: + specifier: ~1.1.0 + version: 1.1.0 + xmldoc: + specifier: ~1.1.2 + version: 1.1.4 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/xmldoc': + specifier: 1.1.4 + version: 1.1.4 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../libraries/lookup-by-path: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../libraries/module-minifier: + dependencies: + '@rushstack/worker-pool': + specifier: workspace:* + version: link:../worker-pool + serialize-javascript: + specifier: 7.0.5 + version: 7.0.5 + source-map: + specifier: ~0.7.3 + version: 0.7.6 + terser: + specifier: ^5.9.0 + version: 5.46.1 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/serialize-javascript': + specifier: 5.0.4 + version: 5.0.4 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../libraries/node-core-library: + dependencies: + ajv: + specifier: ~8.20.0 + version: 8.20.0 + ajv-draft-04: + specifier: ~1.0.0 + version: 1.0.0(ajv@8.20.0) + ajv-formats: + specifier: ~3.0.1 + version: 3.0.1(ajv@8.20.0) + fs-extra: + specifier: ~11.3.0 + version: 11.3.4 + import-lazy: + specifier: ~4.0.0 + version: 4.0.0 + jju: + specifier: ~1.4.0 + version: 1.4.0 + resolve: + specifier: ~1.22.1 + version: 1.22.11 + semver: + specifier: ~7.7.4 + version: 7.7.4 + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + '@rushstack/problem-matcher': + specifier: workspace:* + version: link:../problem-matcher + '@types/fs-extra': + specifier: 7.0.0 + version: 7.0.0 + '@types/jju': + specifier: 1.4.1 + version: 1.4.1 + '@types/resolve': + specifier: 1.20.2 + version: 1.20.2 + '@types/semver': + specifier: 7.7.1 + version: 7.7.1 + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + + ../../../libraries/npm-check-fork: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + semver: + specifier: ~7.7.4 + version: 7.7.4 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/semver': + specifier: 7.7.1 + version: 7.7.1 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../libraries/operation-graph: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../terminal + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + + ../../../libraries/package-deps-hash: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../libraries/package-extractor: + dependencies: + '@pnpm/link-bins': + specifier: ~5.3.7 + version: 5.3.25 + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../ts-command-line + ignore: + specifier: ~5.1.6 + version: 5.1.9 + jszip: + specifier: ~3.8.0 + version: 3.8.0 + minimatch: + specifier: 10.2.3 + version: 10.2.3 + npm-packlist: + specifier: ~5.1.3 + version: 5.1.3 + semver: + specifier: ~7.7.4 + version: 7.7.4 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-webpack5-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack5-plugin + '@rushstack/webpack-preserve-dynamic-require-plugin': + specifier: workspace:* + version: link:../../webpack/preserve-dynamic-require-plugin + '@types/glob': + specifier: 7.1.1 + version: 7.1.1 + '@types/npm-packlist': + specifier: ~1.1.1 + version: 1.1.2 + '@types/semver': + specifier: 7.7.1 + version: 7.7.1 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../libraries/problem-matcher: + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + + ../../../libraries/rig-package: + dependencies: + jju: + specifier: ~1.4.0 + version: 1.4.0 + resolve: + specifier: ~1.22.1 + version: 1.22.11 + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + '@types/jju': + specifier: 1.4.1 + version: 1.4.1 + '@types/resolve': + specifier: 1.20.2 + version: 1.20.2 + ajv: + specifier: ~8.20.0 + version: 8.20.0 + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + + ../../../libraries/rush-lib: + dependencies: + '@inquirer/checkbox': + specifier: ~5.1.3 + version: 5.1.3(@types/node@22.9.3) + '@inquirer/confirm': + specifier: ~6.0.11 + version: 6.0.11(@types/node@22.9.3) + '@inquirer/input': + specifier: ~5.0.11 + version: 5.0.11(@types/node@22.9.3) + '@inquirer/search': + specifier: ~4.1.7 + version: 4.1.7(@types/node@22.9.3) + '@inquirer/select': + specifier: ~5.1.3 + version: 5.1.3(@types/node@22.9.3) + '@pnpm/link-bins': + specifier: ~5.3.7 + version: 5.3.25 + '@rushstack/credential-cache': + specifier: workspace:* + version: link:../credential-cache + '@rushstack/heft-config-file': + specifier: workspace:* + version: link:../heft-config-file + '@rushstack/lookup-by-path': + specifier: workspace:* + version: link:../lookup-by-path + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + '@rushstack/npm-check-fork': + specifier: workspace:* + version: link:../npm-check-fork + '@rushstack/package-deps-hash': + specifier: workspace:* + version: link:../package-deps-hash + '@rushstack/package-extractor': + specifier: workspace:* + version: link:../package-extractor + '@rushstack/rig-package': + specifier: workspace:* + version: link:../rig-package + '@rushstack/rush-pnpm-kit-v10': + specifier: workspace:* + version: link:../rush-pnpm-kit-v10 + '@rushstack/rush-pnpm-kit-v8': + specifier: workspace:* + version: link:../rush-pnpm-kit-v8 + '@rushstack/rush-pnpm-kit-v9': + specifier: workspace:* + version: link:../rush-pnpm-kit-v9 + '@rushstack/stream-collator': + specifier: workspace:* + version: link:../stream-collator + '@rushstack/terminal': + specifier: workspace:* + version: link:../terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../ts-command-line + '@yarnpkg/lockfile': + specifier: ~1.0.2 + version: 1.0.2 + dependency-path: + specifier: ~9.2.8 + version: 9.2.8 + dotenv: + specifier: ~16.4.7 + version: 16.4.7 + fast-glob: + specifier: ~3.3.1 + version: 3.3.3 + git-repo-info: + specifier: ~2.1.0 + version: 2.1.1 + https-proxy-agent: + specifier: ~5.0.0 + version: 5.0.1 + ignore: + specifier: ~5.1.6 + version: 5.1.9 + js-yaml: + specifier: ~4.1.0 + version: 4.1.1 + npm-package-arg: + specifier: ~6.1.0 + version: 6.1.1 + object-hash: + specifier: 3.0.0 + version: 3.0.0 + pnpm-sync-lib: + specifier: 0.3.4 + version: 0.3.4 + read-package-tree: + specifier: ~5.1.5 + version: 5.1.6 + rxjs: + specifier: ~6.6.7 + version: 6.6.7 + semver: + specifier: ~7.7.4 + version: 7.7.4 + ssri: + specifier: ~8.0.0 + version: 8.0.1 + strict-uri-encode: + specifier: ~2.0.0 + version: 2.0.0 + tapable: + specifier: 2.2.1 + version: 2.2.1 + tar: + specifier: ~7.5.6 + version: 7.5.13 + true-case-path: + specifier: ~2.2.1 + version: 2.2.1 + devDependencies: + '@pnpm/lockfile.types-900': + specifier: npm:@pnpm/lockfile.types@~900.0.0 + version: '@pnpm/lockfile.types@900.0.0' + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-webpack5-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack5-plugin + '@rushstack/operation-graph': + specifier: workspace:* + version: link:../operation-graph + '@rushstack/webpack-deep-imports-plugin': + specifier: workspace:* + version: link:../../webpack/webpack-deep-imports-plugin + '@rushstack/webpack-preserve-dynamic-require-plugin': + specifier: workspace:* + version: link:../../webpack/preserve-dynamic-require-plugin + '@types/js-yaml': + specifier: 4.0.9 + version: 4.0.9 + '@types/npm-package-arg': + specifier: 6.1.0 + version: 6.1.0 + '@types/object-hash': + specifier: ~3.0.6 + version: 3.0.6 + '@types/read-package-tree': + specifier: 5.1.0 + version: 5.1.0 + '@types/semver': + specifier: 7.7.1 + version: 7.7.1 + '@types/ssri': + specifier: ~7.1.0 + version: 7.1.5 + '@types/strict-uri-encode': + specifier: 2.0.0 + version: 2.0.0 + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../libraries/rush-pnpm-kit-v10: + dependencies: + '@pnpm/dependency-path-pnpm-v10': + specifier: npm:@pnpm/dependency-path@~1000.0.9 + version: '@pnpm/dependency-path@1000.0.9' + '@pnpm/lockfile.fs-pnpm-lock-v9': + specifier: npm:@pnpm/lockfile.fs@~1001.1.11 + version: '@pnpm/lockfile.fs@1001.1.32(@pnpm/logger@1001.0.1)' + '@pnpm/logger': + specifier: ~1001.0.0 + version: 1001.0.1 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../libraries/rush-pnpm-kit-v8: + dependencies: + '@pnpm/dependency-path-pnpm-v8': + specifier: npm:@pnpm/dependency-path@~2.1.8 + version: '@pnpm/dependency-path@2.1.8' + '@pnpm/lockfile-file-pnpm-lock-v6': + specifier: npm:@pnpm/lockfile-file@~8.1.8 + version: '@pnpm/lockfile-file@8.1.8(@pnpm/logger@5.0.0)' + '@pnpm/logger': + specifier: ~5.0.0 + version: 5.0.0 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../libraries/rush-pnpm-kit-v9: + dependencies: + '@pnpm/dependency-path-pnpm-v9': + specifier: npm:@pnpm/dependency-path@~5.1.7 + version: '@pnpm/dependency-path@5.1.7' + '@pnpm/lockfile.fs-pnpm-lock-v9': + specifier: npm:@pnpm/lockfile.fs@~1001.1.11 + version: '@pnpm/lockfile.fs@1001.1.32(@pnpm/logger@1001.0.1)' + '@pnpm/logger': + specifier: ~1001.0.0 + version: 1001.0.1 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../libraries/rush-sdk: + dependencies: + '@pnpm/lockfile.types-900': + specifier: npm:@pnpm/lockfile.types@~900.0.0 + version: '@pnpm/lockfile.types@900.0.0' + '@rushstack/credential-cache': + specifier: workspace:* + version: link:../credential-cache + '@rushstack/lookup-by-path': + specifier: workspace:* + version: link:../lookup-by-path + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + '@rushstack/package-deps-hash': + specifier: workspace:* + version: link:../package-deps-hash + '@rushstack/terminal': + specifier: workspace:* + version: link:../terminal + tapable: + specifier: 2.2.1 + version: 2.2.1 + devDependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../rush-lib + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-webpack5-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack5-plugin + '@rushstack/stream-collator': + specifier: workspace:* + version: link:../stream-collator + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../ts-command-line + '@rushstack/webpack-preserve-dynamic-require-plugin': + specifier: workspace:* + version: link:../../webpack/preserve-dynamic-require-plugin + '@types/semver': + specifier: 7.7.1 + version: 7.7.1 + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../libraries/rush-themed-ui: + dependencies: + react: + specifier: ~19.2.3 + version: 19.2.4 + react-dom: + specifier: ~19.2.3 + version: 19.2.4(react@19.2.4) + devDependencies: + '@radix-ui/colors': + specifier: ~3.0.0 + version: 3.0.0 + '@radix-ui/react-checkbox': + specifier: ~1.3.3 + version: 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-icons': + specifier: ~1.3.2 + version: 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-scroll-area': + specifier: ~1.2.10 + version: 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-tabs': + specifier: ~1.1.13 + version: 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/react': + specifier: 19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: 19.2.3 + version: 19.2.3(@types/react@19.2.7) + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-web-rig: + specifier: workspace:* + version: link:../../rigs/local-web-rig + + ../../../libraries/rushell: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../libraries/stream-collator: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../terminal + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../libraries/terminal: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + '@rushstack/problem-matcher': + specifier: workspace:* + version: link:../problem-matcher + supports-color: + specifier: ~8.1.1 + version: 8.1.1 + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + '@types/supports-color': + specifier: 8.1.3 + version: 8.1.3 + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0(supports-color@8.1.1) + + ../../../libraries/tree-pattern: + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + + ../../../libraries/ts-command-line: + dependencies: + '@rushstack/terminal': + specifier: workspace:* + version: link:../terminal + '@types/argparse': + specifier: 1.0.38 + version: 1.0.38 + argparse: + specifier: ~1.0.9 + version: 1.0.10 + string-argv: + specifier: ~0.3.1 + version: 0.3.2 + devDependencies: + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@22.9.3) + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + decoupled-local-node-rig: + specifier: workspace:* + version: link:../../rigs/decoupled-local-node-rig + eslint: + specifier: ~9.37.0 + version: 9.37.0 + + ../../../libraries/typings-generator: + dependencies: + '@jridgewell/sourcemap-codec': + specifier: ~1.5.5 + version: 1.5.5 + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../terminal + chokidar: + specifier: ~3.6.0 + version: 3.6.0 + fast-glob: + specifier: ~3.3.1 + version: 3.3.3 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../libraries/worker-pool: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../repo-scripts/doc-plugin-rush-stack: + dependencies: + '@microsoft/api-documenter': + specifier: workspace:* + version: link:../../apps/api-documenter + '@microsoft/api-extractor-model': + specifier: workspace:* + version: link:../../libraries/api-extractor-model + '@microsoft/tsdoc': + specifier: ~0.16.0 + version: 0.16.0 + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + js-yaml: + specifier: ~4.1.0 + version: 4.1.1 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/js-yaml': + specifier: 4.0.9 + version: 4.0.9 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../repo-scripts/generate-api-docs: + devDependencies: + '@microsoft/api-documenter': + specifier: workspace:* + version: link:../../apps/api-documenter + doc-plugin-rush-stack: + specifier: workspace:* + version: link:../doc-plugin-rush-stack + + ../../../repo-scripts/repo-toolbox: + dependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + diff: + specifier: ~8.0.2 + version: 8.0.4 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../rigs/decoupled-local-node-rig: + dependencies: + '@microsoft/api-extractor': + specifier: 7.58.12 + version: 7.58.12(@types/node@20.17.19) + '@rushstack/eslint-config': + specifier: 4.6.4 + version: 4.6.4(eslint@9.37.0)(typescript@5.8.2) + '@rushstack/eslint-patch': + specifier: 1.16.1 + version: 1.16.1 + '@rushstack/eslint-plugin': + specifier: 0.23.2 + version: 0.23.2(eslint@9.37.0)(typescript@5.8.2) + '@rushstack/heft': + specifier: 1.2.22 + version: 1.2.22(@types/node@20.17.19) + '@rushstack/heft-node-rig': + specifier: 2.11.45 + version: 2.11.45(@rushstack/heft@1.2.22(@types/node@20.17.19))(@types/node@20.17.19)(jest-environment-jsdom@30.3.0) + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@typescript-eslint/eslint-plugin': + specifier: ~8.56.1 + version: 8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.37.0)(typescript@5.8.2))(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/parser': + specifier: ~8.56.1 + version: 8.56.1(eslint@9.37.0)(typescript@5.8.2) + eslint: + specifier: ~9.37.0 + version: 9.37.0 + eslint-import-resolver-node: + specifier: 0.3.9 + version: 0.3.9 + eslint-plugin-header: + specifier: ~3.1.1 + version: 3.1.1(eslint@9.37.0) + eslint-plugin-headers: + specifier: ~1.2.1 + version: 1.2.1(eslint@9.37.0) + eslint-plugin-import: + specifier: 2.32.0 + version: 2.32.0(eslint@9.37.0) + eslint-plugin-jsdoc: + specifier: 50.6.11 + version: 50.6.11(eslint@9.37.0) + eslint-plugin-react-hooks: + specifier: 5.2.0 + version: 5.2.0(eslint@9.37.0) + jest-junit: + specifier: 12.3.0 + version: 12.3.0 + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../rigs/heft-node-rig: + dependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/eslint-config': + specifier: workspace:* + version: link:../../eslint/eslint-config + '@rushstack/heft-api-extractor-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-api-extractor-plugin + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + jest-environment-node: + specifier: ~30.3.0 + version: 30.3.0 + typescript: + specifier: ~5.8.2 + version: 5.8.2 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + + ../../../rigs/heft-vscode-extension-rig: + dependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/heft-node-rig': + specifier: workspace:* + version: link:../heft-node-rig + '@rushstack/heft-vscode-extension-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-vscode-extension-plugin + '@rushstack/heft-webpack5-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack5-plugin + '@rushstack/webpack-preserve-dynamic-require-plugin': + specifier: workspace:* + version: link:../../webpack/preserve-dynamic-require-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + jest-environment-node: + specifier: ~30.3.0 + version: 30.3.0 + typescript: + specifier: ~5.8.2 + version: 5.8.2 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + + ../../../rigs/heft-web-rig: + dependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/eslint-config': + specifier: workspace:* + version: link:../../eslint/eslint-config + '@rushstack/heft-api-extractor-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-api-extractor-plugin + '@rushstack/heft-jest-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-jest-plugin + '@rushstack/heft-lint-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-sass-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-sass-plugin + '@rushstack/heft-static-asset-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-static-asset-typings-plugin + '@rushstack/heft-typescript-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-typescript-plugin + '@rushstack/heft-webpack5-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack5-plugin + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + autoprefixer: + specifier: ~10.4.2 + version: 10.4.27(postcss@8.5.12) + css-loader: + specifier: ~6.6.0 + version: 6.6.0(webpack@5.105.4) + css-minimizer-webpack-plugin: + specifier: ~3.4.1 + version: 3.4.1(webpack@5.105.4) + eslint: + specifier: ~9.37.0 + version: 9.37.0 + html-webpack-plugin: + specifier: ~5.5.0 + version: 5.5.4(webpack@5.105.4) + jest-environment-jsdom: + specifier: ~30.3.0 + version: 30.3.0 + mini-css-extract-plugin: + specifier: ~2.5.3 + version: 2.5.3(webpack@5.105.4) + postcss: + specifier: ~8.5.10 + version: 8.5.12 + postcss-loader: + specifier: ~6.2.1 + version: 6.2.1(postcss@8.5.12)(webpack@5.105.4) + sass: + specifier: ~1.49.7 + version: 1.49.11 + sass-loader: + specifier: ~12.4.0 + version: 12.4.0(sass@1.49.11)(webpack@5.105.4) + source-map-loader: + specifier: ~3.0.1 + version: 3.0.2(webpack@5.105.4) + style-loader: + specifier: ~3.3.1 + version: 3.3.4(webpack@5.105.4) + terser-webpack-plugin: + specifier: ~5.3.1 + version: 5.3.17(webpack@5.105.4) + typescript: + specifier: ~5.8.2 + version: 5.8.2 + url-loader: + specifier: ~4.1.1 + version: 4.1.1(webpack@5.105.4) + webpack: + specifier: ~5.105.2 + version: 5.105.4 + webpack-bundle-analyzer: + specifier: ~4.5.0 + version: 4.5.0 + webpack-merge: + specifier: ~5.8.0 + version: 5.8.0 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + + ../../../rigs/local-node-rig: + dependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/eslint-patch': + specifier: workspace:* + version: link:../../eslint/eslint-patch + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-node-rig': + specifier: workspace:* + version: link:../heft-node-rig + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + jest-junit: + specifier: 12.3.0 + version: 12.3.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../rigs/local-web-rig: + dependencies: + '@microsoft/api-extractor': + specifier: workspace:* + version: link:../../apps/api-extractor + '@rushstack/eslint-patch': + specifier: workspace:* + version: link:../../eslint/eslint-patch + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-web-rig': + specifier: workspace:* + version: link:../heft-web-rig + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + jest-junit: + specifier: 12.3.0 + version: 12.3.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + typescript: + specifier: ~5.8.2 + version: 5.8.2 + + ../../../rush-plugins/rush-amazon-s3-build-cache-plugin: + dependencies: + '@rushstack/credential-cache': + specifier: workspace:* + version: link:../../libraries/credential-cache + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + https-proxy-agent: + specifier: ~5.0.0 + version: 5.0.1 + devDependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../rush-plugins/rush-azure-storage-build-cache-plugin: + dependencies: + '@azure/identity': + specifier: ~4.13.1 + version: 4.13.1 + '@azure/storage-blob': + specifier: ~12.31.0 + version: 12.31.0 + '@rushstack/credential-cache': + specifier: workspace:* + version: link:../../libraries/credential-cache + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + devDependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../rush-plugins/rush-bridge-cache-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../rush-plugins/rush-buildxl-graph-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + devDependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../rush-plugins/rush-http-build-cache-plugin: + dependencies: + '@rushstack/credential-cache': + specifier: workspace:* + version: link:../../libraries/credential-cache + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + https-proxy-agent: + specifier: ~5.0.0 + version: 5.0.1 + devDependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../rush-plugins/rush-litewatch-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../rush-plugins/rush-mcp-docs-plugin: + dependencies: + '@rushstack/mcp-server': + specifier: workspace:* + version: link:../../apps/rush-mcp-server + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-eslint-config: + specifier: workspace:* + version: link:../../eslint/local-eslint-config + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../rush-plugins/rush-published-versions-json-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../rush-plugins/rush-redis-cobuild-plugin: + dependencies: + '@redis/client': + specifier: ~5.8.2 + version: 5.8.3 + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + devDependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../rush-plugins/rush-resolver-cache-plugin: + dependencies: + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/lookup-by-path': + specifier: workspace:* + version: link:../../libraries/lookup-by-path + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/webpack-workspace-resolve-plugin': + specifier: workspace:* + version: link:../../webpack/webpack-workspace-resolve-plugin + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../rush-plugins/rush-serve-plugin: + dependencies: + '@rushstack/debug-certificate-manager': + specifier: workspace:* + version: link:../../libraries/debug-certificate-manager + '@rushstack/heft-config-file': + specifier: workspace:* + version: link:../../libraries/heft-config-file + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rig-package': + specifier: workspace:* + version: link:../../libraries/rig-package + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + compression: + specifier: ~1.7.4 + version: 1.7.5 + cors: + specifier: ~2.8.5 + version: 2.8.6 + express: + specifier: 4.21.1 + version: 4.21.1 + http2-express-bridge: + specifier: ~1.0.7 + version: 1.0.7(@types/express@4.17.21) + ws: + specifier: ~8.21.0 + version: 8.21.0 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-webpack5-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-webpack5-plugin + '@rushstack/rush-serve-dashboard': + specifier: workspace:* + version: link:../../apps/rush-serve-dashboard + '@types/compression': + specifier: ~1.7.2 + version: 1.7.5(@types/express@4.17.21) + '@types/cors': + specifier: ~2.8.12 + version: 2.8.19 + '@types/express': + specifier: 4.17.21 + version: 4.17.21 + '@types/ws': + specifier: 8.18.1 + version: 8.18.1 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + html-webpack-plugin: + specifier: ~5.5.0 + version: 5.5.4(webpack@5.105.4) + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../vscode-extensions/debug-certificate-manager-vscode-extension: + dependencies: + '@rushstack/debug-certificate-manager': + specifier: workspace:* + version: link:../../libraries/debug-certificate-manager + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/vscode-shared': + specifier: workspace:* + version: link:../vscode-shared + tslib: + specifier: ~2.8.1 + version: 2.8.1 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-vscode-extension-rig': + specifier: workspace:* + version: link:../../rigs/heft-vscode-extension-rig + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/vscode': + specifier: 1.103.0 + version: 1.103.0 + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + + ../../../vscode-extensions/playwright-local-browser-server-vscode-extension: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/playwright-browser-tunnel': + specifier: workspace:* + version: link:../../apps/playwright-browser-tunnel + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/vscode-shared': + specifier: workspace:* + version: link:../vscode-shared + playwright-core: + specifier: ~1.56.1 + version: 1.56.1 + tslib: + specifier: ~2.8.1 + version: 2.8.1 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-node-rig': + specifier: workspace:* + version: link:../../rigs/heft-node-rig + '@rushstack/heft-vscode-extension-rig': + specifier: workspace:* + version: link:../../rigs/heft-vscode-extension-rig + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/vscode': + specifier: 1.103.0 + version: 1.103.0 + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + + ../../../vscode-extensions/rush-vscode-command-webview: + dependencies: + '@fluentui/react': + specifier: ~8.125.3 + version: 8.125.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-components': + specifier: ~9.72.9 + version: 9.72.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@reduxjs/toolkit': + specifier: ~2.11.2 + version: 2.11.2(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.4)(redux@5.0.1))(react@19.2.4) + react: + specifier: ~19.2.3 + version: 19.2.4 + react-dom: + specifier: ~19.2.3 + version: 19.2.4(react@19.2.4) + react-hook-form: + specifier: ~7.69.0 + version: 7.69.0(react@19.2.4) + react-redux: + specifier: ~9.2.0 + version: 9.2.0(@types/react@19.2.7)(react@19.2.4)(redux@5.0.1) + redux: + specifier: ~5.0.1 + version: 5.0.1 + tslib: + specifier: ~2.8.1 + version: 2.8.1 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + '@types/react': + specifier: 19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: 19.2.3 + version: 19.2.3(@types/react@19.2.7) + '@types/react-redux': + specifier: ~7.1.22 + version: 7.1.34 + '@types/vscode': + specifier: 1.103.0 + version: 1.103.0 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + html-webpack-plugin: + specifier: ~5.5.0 + version: 5.5.4(webpack@5.105.4) + local-web-rig: + specifier: workspace:* + version: link:../../rigs/local-web-rig + webpack: + specifier: ~5.105.2 + version: 5.105.4 + webpack-bundle-analyzer: + specifier: ~4.5.0 + version: 4.5.0 + + ../../../vscode-extensions/rush-vscode-extension: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/rush-sdk': + specifier: workspace:* + version: link:../../libraries/rush-sdk + '@rushstack/rush-vscode-command-webview': + specifier: workspace:* + version: link:../rush-vscode-command-webview + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/ts-command-line': + specifier: workspace:* + version: link:../../libraries/ts-command-line + devDependencies: + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-vscode-extension-rig': + specifier: workspace:* + version: link:../../rigs/heft-vscode-extension-rig + '@types/glob': + specifier: 7.1.1 + version: 7.1.1 + '@types/mocha': + specifier: 10.0.6 + version: 10.0.6 + '@types/vscode': + specifier: 1.103.0 + version: 1.103.0 + '@types/webpack-env': + specifier: 1.18.8 + version: 1.18.8 + '@vscode/test-electron': + specifier: ^1.6.2 + version: 1.6.2 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + glob: + specifier: ~7.0.5 + version: 7.0.6 + mocha: + specifier: ^10.1.0 + version: 10.8.2 + + ../../../vscode-extensions/vscode-shared: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/heft-node-rig': + specifier: workspace:* + version: link:../../rigs/heft-node-rig + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/vscode': + specifier: 1.103.0 + version: 1.103.0 + + ../../../webpack/hashed-folder-copy-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/webpack-plugin-utilities': + specifier: workspace:* + version: link:../webpack-plugin-utilities + fast-glob: + specifier: ~3.3.1 + version: 3.3.3 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/estree': + specifier: 1.0.8 + version: 1.0.8 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + memfs: + specifier: 4.12.0 + version: 4.12.0 + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../webpack/loader-load-themed-styles: + dependencies: + loader-utils: + specifier: 1.4.2 + version: 1.4.2 + devDependencies: + '@microsoft/load-themed-styles': + specifier: workspace:* + version: link:../../libraries/load-themed-styles + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/loader-utils': + specifier: 1.1.3 + version: 1.1.3 + '@types/webpack': + specifier: 4.41.32 + version: 4.41.32 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../webpack/loader-raw-script: + dependencies: + loader-utils: + specifier: 1.4.2 + version: 1.4.2 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + + ../../../webpack/preserve-dynamic-require-plugin: + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../webpack/set-webpack-public-path-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/webpack-plugin-utilities': + specifier: workspace:* + version: link:../webpack-plugin-utilities + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + memfs: + specifier: 4.12.0 + version: 4.12.0 + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../webpack/webpack-deep-imports-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../webpack/webpack-embedded-dependencies-plugin: + dependencies: + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/webpack-plugin-utilities': + specifier: workspace:* + version: link:../webpack-plugin-utilities + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + memfs: + specifier: 4.12.0 + version: 4.12.0 + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../webpack/webpack-plugin-utilities: + dependencies: + '@types/estree': + specifier: 1.0.8 + version: 1.0.8 + memfs: + specifier: 4.12.0 + version: 4.12.0 + webpack-merge: + specifier: ~5.8.0 + version: 5.8.0 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/tapable': + specifier: 1.0.6 + version: 1.0.6 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../webpack/webpack-workspace-resolve-plugin: + dependencies: + '@rushstack/lookup-by-path': + specifier: workspace:* + version: link:../../libraries/lookup-by-path + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + memfs: + specifier: 4.12.0 + version: 4.12.0 + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../webpack/webpack4-localization-plugin: + dependencies: + '@rushstack/localization-utilities': + specifier: workspace:* + version: link:../../libraries/localization-utilities + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@types/tapable': + specifier: 1.0.6 + version: 1.0.6 + loader-utils: + specifier: 1.4.2 + version: 1.4.2 + minimatch: + specifier: 10.2.3 + version: 10.2.3 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/set-webpack-public-path-plugin': + specifier: ^4.1.16 + version: 4.1.16(@types/node@20.17.19)(@types/webpack@4.41.32)(webpack@4.47.0) + '@types/loader-utils': + specifier: 1.1.3 + version: 1.1.3 + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/webpack': + specifier: 4.41.32 + version: 4.41.32 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + webpack: + specifier: ~4.47.0 + version: 4.47.0 + + ../../../webpack/webpack4-module-minifier-plugin: + dependencies: + '@rushstack/module-minifier': + specifier: workspace:* + version: link:../../libraries/module-minifier + '@rushstack/worker-pool': + specifier: workspace:* + version: link:../../libraries/worker-pool + '@types/tapable': + specifier: 1.0.6 + version: 1.0.6 + tapable: + specifier: 1.1.3 + version: 1.1.3 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + '@types/webpack': + specifier: 4.41.32 + version: 4.41.32 + '@types/webpack-sources': + specifier: 1.4.2 + version: 1.4.2 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + webpack: + specifier: ~4.47.0 + version: 4.47.0 + webpack-sources: + specifier: ~1.4.3 + version: 1.4.3 + + ../../../webpack/webpack5-load-themed-styles-loader: + devDependencies: + '@microsoft/load-themed-styles': + specifier: workspace:* + version: link:../../libraries/load-themed-styles + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + css-loader: + specifier: ~6.6.0 + version: 6.6.0(webpack@5.105.4) + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + memfs: + specifier: 4.12.0 + version: 4.12.0 + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../webpack/webpack5-localization-plugin: + dependencies: + '@rushstack/localization-utilities': + specifier: workspace:* + version: link:../../libraries/localization-utilities + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + memfs: + specifier: 4.12.0 + version: 4.12.0 + webpack: + specifier: ~5.105.2 + version: 5.105.4 + + ../../../webpack/webpack5-module-minifier-plugin: + dependencies: + '@rushstack/worker-pool': + specifier: workspace:* + version: link:../../libraries/worker-pool + '@types/estree': + specifier: 1.0.8 + version: 1.0.8 + '@types/tapable': + specifier: 1.0.6 + version: 1.0.6 + tapable: + specifier: 2.2.1 + version: 2.2.1 + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/module-minifier': + specifier: workspace:* + version: link:../../libraries/module-minifier + '@types/node': + specifier: 20.17.19 + version: 20.17.19 + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + memfs: + specifier: 4.12.0 + version: 4.12.0 + webpack: + specifier: ~5.105.2 + version: 5.105.4 + +packages: + + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@apidevtools/json-schema-ref-parser@11.9.3': + resolution: {integrity: sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==} + engines: {node: '>= 16'} + + '@asamuzakjp/css-color@3.2.0': + resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + + '@aws-cdk/asset-awscli-v1@2.2.273': + resolution: {integrity: sha512-X57HYUtHt9BQrlrzUNcMyRsDUCoakYNnY6qh5lNwRCHPtQoTfXmuISkfLk0AjLkcbS5lw1LLTQFiQhTDXfiTvg==} + + '@aws-cdk/asset-node-proxy-agent-v6@2.1.1': + resolution: {integrity: sha512-We4bmHaowOPHr+IQR4/FyTGjRfjgBj4ICMjtqmJeBDWad3Q/6St12NT07leNtyuukv2qMhtSZJQorD8KpKTwRA==} + + '@aws-cdk/aws-apigatewayv2-alpha@2.50.0-alpha.0': + resolution: {integrity: sha512-dttWDqy+nTg/fD9y0egvj7/zdnOVEo0qyGsep1RV+p16R3F4ObMKyPVIg15fz57tK//Gp/i1QgXsZaSqbcWHOg==} + engines: {node: '>= 14.15.0'} + deprecated: This package has been stabilized and moved to aws-cdk-lib + peerDependencies: + aws-cdk-lib: ^2.50.0 + constructs: ^10.0.0 + + '@aws-cdk/aws-apigatewayv2-authorizers-alpha@2.50.0-alpha.0': + resolution: {integrity: sha512-lMXnSpUSOYtCxoAxauNkGJZLsKMonHgd9rzlFUK2zxE7aC1lVwb4qYX4X9WJdvIExkFOHSZQzOTKM6SZqusssw==} + engines: {node: '>= 14.15.0'} + deprecated: This package has been stabilized and moved to aws-cdk-lib + peerDependencies: + '@aws-cdk/aws-apigatewayv2-alpha': 2.50.0-alpha.0 + aws-cdk-lib: ^2.50.0 + constructs: ^10.0.0 + + '@aws-cdk/aws-apigatewayv2-integrations-alpha@2.50.0-alpha.0': + resolution: {integrity: sha512-XEhz4HsU0HtQJnbs9XSb/yPN/1EEYAOZthWRKyniS9IWeGruVjEhWndoXpu0S7w+M5Bni7D9wrCTkqTgmTEvlw==} + engines: {node: '>= 14.15.0'} + deprecated: This package has been stabilized and moved to aws-cdk-lib + peerDependencies: + '@aws-cdk/aws-apigatewayv2-alpha': 2.50.0-alpha.0 + aws-cdk-lib: ^2.50.0 + constructs: ^10.0.0 + + '@aws-cdk/aws-appsync-alpha@2.50.0-alpha.0': + resolution: {integrity: sha512-ZA5M1z5MKOS+m68MMs5YySVFOjOdzrR6F+22Atx6mrCcAD9E5PypZ7tVSwtWYVYvoUnGMI7Bv5Umc3n4DCnjkg==} + engines: {node: '>= 14.15.0'} + peerDependencies: + aws-cdk-lib: ^2.50.0 + constructs: ^10.0.0 + + '@aws-cdk/cloud-assembly-schema@41.2.0': + resolution: {integrity: sha512-JaulVS6z9y5+u4jNmoWbHZRs9uGOnmn/ktXygNWKNu1k6lF3ad4so3s18eRu15XCbUIomxN9WPYT6Ehh7hzONw==} + engines: {node: '>= 14.15.0'} + bundledDependencies: + - jsonschema + - semver + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-codebuild@3.1023.0': + resolution: {integrity: sha512-zR0nfb68pOwpkoKOOJBZDbtOAnjqPjY7AALTpyEBtlslDY+fAmCV4npOpOCm/OoCPZ3eiS7YOo0X6vL8XTeT7w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-sso-oidc@3.1023.0': + resolution: {integrity: sha512-X8ftAZlat9fV3xi3DLjBtUBwaLkK5W9w90sXTFWNzTE1s39ySrUvm3Ln+DXhoMUSN08kh3D4Fhb6WcGjLsd1zw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-sts@3.1023.0': + resolution: {integrity: sha512-5ERoqfMPotFE1Co2HKfCxNrf5yd2oMn7DzRlFnTYk14FTetO8iqy3bfK9foUoqDgdIdYoEChwgguGJsmI8FRBQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.973.26': + resolution: {integrity: sha512-A/E6n2W42ruU+sfWk+mMUOyVXbsSgGrY3MJ9/0Az5qUdG67y8I6HYzzoAa+e/lzxxl1uCYmEL6BTMi9ZiZnplQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.24': + resolution: {integrity: sha512-FWg8uFmT6vQM7VuzELzwVo5bzExGaKHdubn0StjgrcU5FvuLExUe+k06kn/40uKv59rYzhez8eFNM4yYE/Yb/w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.26': + resolution: {integrity: sha512-CY4ppZ+qHYqcXqBVi//sdHST1QK3KzOEiLtpLsc9W2k2vfZPKExGaQIsOwcyvjpjUEolotitmd3mUNY56IwDEA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.972.28': + resolution: {integrity: sha512-wXYvq3+uQcZV7k+bE4yDXCTBdzWTU9x/nMiKBfzInmv6yYK1veMK0AKvRfRBd72nGWYKcL6AxwiPg9z/pYlgpw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.28': + resolution: {integrity: sha512-ZSTfO6jqUTCysbdBPtEX5OUR//3rbD0lN7jO3sQeS2Gjr/Y+DT6SbIJ0oT2cemNw3UzKu97sNONd1CwNMthuZQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.29': + resolution: {integrity: sha512-clSzDcvndpFJAggLDnDb36sPdlZYyEs5Zm6zgZjjUhwsJgSWiWKwFIXUVBcbruidNyBdbpOv2tNDL9sX8y3/0g==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.24': + resolution: {integrity: sha512-Q2k/XLrFXhEztPHqj4SLCNID3hEPdlhh1CDLBpNnM+1L8fq7P+yON9/9M1IGN/dA5W45v44ylERfXtDAlmMNmw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.972.28': + resolution: {integrity: sha512-IoUlmKMLEITFn1SiCTjPfR6KrE799FBo5baWyk/5Ppar2yXZoUdaRqZzJzK6TcJxx450M8m8DbpddRVYlp5R/A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.28': + resolution: {integrity: sha512-d+6h0SD8GGERzKe27v5rOzNGKOl0D+l0bWJdqrxH8WSQzHzjsQFIAPgIeOTUwBHVsKKwtSxc91K/SWax6XgswQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-host-header@3.972.8': + resolution: {integrity: sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-logger@3.972.8': + resolution: {integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.972.9': + resolution: {integrity: sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-user-agent@3.972.28': + resolution: {integrity: sha512-cfWZFlVh7Va9lRay4PN2A9ARFzaBYcA097InT5M2CdRS05ECF5yaz86jET8Wsl2WcyKYEvVr/QNmKtYtafUHtQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/nested-clients@3.996.18': + resolution: {integrity: sha512-c7ZSIXrESxHKx2Mcopgd8AlzZgoXMr20fkx5ViPWPOLBvmyhw9VwJx/Govg8Ef/IhEon5R9l53Z8fdYSEmp6VA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/region-config-resolver@3.972.10': + resolution: {integrity: sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1021.0': + resolution: {integrity: sha512-TKY6h9spUk3OLs5v1oAgW9mAeBE3LAGNBwJokLy96wwmd4W2v/tYlXseProyed9ValDj2u1jK/4Rg1T+1NXyJA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.973.6': + resolution: {integrity: sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-endpoints@3.996.5': + resolution: {integrity: sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.5': + resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-user-agent-browser@3.972.8': + resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==} + + '@aws-sdk/util-user-agent-node@3.973.14': + resolution: {integrity: sha512-vNSB/DYaPOyujVZBg/zUznH9QC142MaTHVmaFlF7uzzfg3CgT9f/l4C0Yi+vU/tbBhxVcXVB90Oohk5+o+ZbWw==} + engines: {node: '>=20.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.972.16': + resolution: {integrity: sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.4': + resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} + engines: {node: '>=18.0.0'} + + '@azure/abort-controller@2.1.2': + resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==} + engines: {node: '>=18.0.0'} + + '@azure/core-auth@1.10.1': + resolution: {integrity: sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==} + engines: {node: '>=20.0.0'} + + '@azure/core-client@1.10.1': + resolution: {integrity: sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==} + engines: {node: '>=20.0.0'} + + '@azure/core-http-compat@2.3.2': + resolution: {integrity: sha512-Tf6ltdKzOJEgxZeWLCjMxrxbodB/ZeCbzzA1A2qHbhzAjzjHoBVSUeSl/baT/oHAxhc4qdqVaDKnc2+iE932gw==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@azure/core-client': ^1.10.0 + '@azure/core-rest-pipeline': ^1.22.0 + + '@azure/core-lro@2.7.2': + resolution: {integrity: sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==} + engines: {node: '>=18.0.0'} + + '@azure/core-paging@1.6.2': + resolution: {integrity: sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==} + engines: {node: '>=18.0.0'} + + '@azure/core-rest-pipeline@1.23.0': + resolution: {integrity: sha512-Evs1INHo+jUjwHi1T6SG6Ua/LHOQBCLuKEEE6efIpt4ZOoNonaT1kP32GoOcdNDbfqsD2445CPri3MubBy5DEQ==} + engines: {node: '>=20.0.0'} + + '@azure/core-tracing@1.3.1': + resolution: {integrity: sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==} + engines: {node: '>=20.0.0'} + + '@azure/core-util@1.13.1': + resolution: {integrity: sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==} + engines: {node: '>=20.0.0'} + + '@azure/core-xml@1.5.0': + resolution: {integrity: sha512-D/sdlJBMJfx7gqoj66PKVmhDDaU6TKA49ptcolxdas29X7AfvLTmfAGLjAcIMBK7UZ2o4lygHIqVckOlQU3xWw==} + engines: {node: '>=20.0.0'} + + '@azure/identity@4.13.1': + resolution: {integrity: sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw==} + engines: {node: '>=20.0.0'} + + '@azure/logger@1.3.0': + resolution: {integrity: sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==} + engines: {node: '>=20.0.0'} + + '@azure/msal-browser@5.6.3': + resolution: {integrity: sha512-sTjMtUm+bJpENU/1WlRzHEsgEHppZDZ1EtNyaOODg/sQBtMxxJzGB+MOCM+T2Q5Qe1fKBrdxUmjyRxm0r7Ez9w==} + engines: {node: '>=0.8.0'} + + '@azure/msal-common@16.4.1': + resolution: {integrity: sha512-Bl8f+w37xkXsYh7QRkAKCFGYtWMYuOVO7Lv+BxILrvGz3HbIEF22Pt0ugyj0QPOl6NLrHcnNUQ9yeew98P/5iw==} + engines: {node: '>=0.8.0'} + + '@azure/msal-node@5.1.2': + resolution: {integrity: sha512-DoeSJ9U5KPAIZoHsPywvfEj2MhBniQe0+FSpjLUTdWoIkI999GB5USkW6nNEHnIaLVxROHXvprWA1KzdS1VQ4A==} + engines: {node: '>=20'} + + '@azure/storage-blob@12.31.0': + resolution: {integrity: sha512-DBgNv10aCSxopt92DkTDD0o9xScXeBqPKGmR50FPZQaEcH4JLQ+GEOGEDv19V5BMkB7kxr+m4h6il/cCDPvmHg==} + engines: {node: '>=20.0.0'} + + '@azure/storage-common@12.3.0': + resolution: {integrity: sha512-/OFHhy86aG5Pe8dP5tsp+BuJ25JOAl9yaMU3WZbkeoiFMHFtJ7tu5ili7qEdBXNW9G5lDB19trwyI6V49F/8iQ==} + engines: {node: '>=20.0.0'} + + '@babel/code-frame@7.12.11': + resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==} + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.12.9': + resolution: {integrity: sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.20.12': + resolution: {integrity: sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.28.6': + resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.28.5': + resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.1.5': + resolution: {integrity: sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg==} + peerDependencies: + '@babel/core': ^7.4.0-0 + + '@babel/helper-define-polyfill-provider@0.6.8': + resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.28.5': + resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.10.4': + resolution: {integrity: sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==} + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.28.6': + resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.28.6': + resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.25.9': + resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': + resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6': + resolution: {integrity: sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-class-properties@7.18.6': + resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-decorators@7.29.0': + resolution: {integrity: sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-export-default-from@7.27.1': + resolution: {integrity: sha512-hjlsMBl1aJc5lp8MoCDEZCiYzlgdRAShOjAfRw6X+GlpLpUPU7c3XNLsKFZbQk/1cRzBlJ7CXg3xJAJMrFa1Uw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6': + resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-object-rest-spread@7.12.1': + resolution: {integrity: sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-object-rest-spread@7.20.7': + resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-optional-chaining@7.21.0': + resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-private-methods@7.18.6': + resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-decorators@7.28.6': + resolution: {integrity: sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-dynamic-import@7.8.3': + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-flow@7.28.6': + resolution: {integrity: sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.28.6': + resolution: {integrity: sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.28.6': + resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.12.1': + resolution: {integrity: sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.28.6': + resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.28.6': + resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.29.0': + resolution: {integrity: sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.28.6': + resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.28.6': + resolution: {integrity: sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.28.6': + resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.28.6': + resolution: {integrity: sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.28.6': + resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.28.6': + resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.28.5': + resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.28.6': + resolution: {integrity: sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.28.6': + resolution: {integrity: sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.28.6': + resolution: {integrity: sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-flow-strip-types@7.27.1': + resolution: {integrity: sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.28.6': + resolution: {integrity: sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.28.6': + resolution: {integrity: sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.28.6': + resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.29.0': + resolution: {integrity: sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': + resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.28.6': + resolution: {integrity: sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.28.6': + resolution: {integrity: sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.28.6': + resolution: {integrity: sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.28.6': + resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.28.6': + resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.28.6': + resolution: {integrity: sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-display-name@7.28.0': + resolution: {integrity: sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-development@7.27.1': + resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx@7.28.6': + resolution: {integrity: sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-pure-annotations@7.27.1': + resolution: {integrity: sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.0': + resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.28.6': + resolution: {integrity: sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.28.6': + resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.6': + resolution: {integrity: sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.28.6': + resolution: {integrity: sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6': + resolution: {integrity: sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.29.2': + resolution: {integrity: sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-flow@7.27.1': + resolution: {integrity: sha512-ez3a2it5Fn6P54W8QkbfIyyIbxlXvcxyWHHvno1Wg0Ej5eiJY5hBb8ExttoIOJJk7V2dZE6prP7iby5q2aQ0Lg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-react@7.28.5': + resolution: {integrity: sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.28.5': + resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/register@7.28.6': + resolution: {integrity: sha512-pgcbbEl/dWQYb6L6Yew6F94rdwygfuv+vJ/tXfwIOYAfPB6TNWpXUMEtEq3YuTeHRdvMIhvz13bkT9CNaS+wqA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime-corejs3@7.29.2': + resolution: {integrity: sha512-Lc94FOD5+0aXhdb0Tdg3RUtqT6yWbI/BbFWvlaSJ3gAb9Ks+99nHRDKADVqC37er4eCB0fHyWT+y+K3QOvJKbw==} + engines: {node: '>=6.9.0'} + + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@base2/pretty-print-object@1.0.1': + resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@bufbuild/protobuf@2.11.0': + resolution: {integrity: sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==} + + '@cnakazawa/watch@1.0.4': + resolution: {integrity: sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==} + engines: {node: '>=0.1.95'} + hasBin: true + + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + + '@ctrl/tinycolor@3.6.1': + resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==} + engines: {node: '>=10'} + + '@discoveryjs/json-ext@0.5.7': + resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} + engines: {node: '>=10.0.0'} + + '@emnapi/core@1.9.2': + resolution: {integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==} + + '@emnapi/runtime@1.9.2': + resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@emotion/cache@10.0.29': + resolution: {integrity: sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==} + + '@emotion/core@10.3.1': + resolution: {integrity: sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww==} + peerDependencies: + '@types/react': '>=16' + react: '>=16.3.0' + + '@emotion/css@10.0.27': + resolution: {integrity: sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==} + + '@emotion/hash@0.8.0': + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@0.8.8': + resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} + + '@emotion/memoize@0.7.4': + resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/serialize@0.11.16': + resolution: {integrity: sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==} + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@0.9.4': + resolution: {integrity: sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==} + + '@emotion/styled-base@10.3.0': + resolution: {integrity: sha512-PBRqsVKR7QRNkmfH78hTSSwHWcwDpecH9W6heujWAcyp2wdz/64PP73s7fWS1dIPm8/Exc8JAzYS8dEWXjv60w==} + peerDependencies: + '@emotion/core': ^10.0.28 + '@types/react': '>=16' + react: '>=16.3.0' + + '@emotion/styled@10.3.0': + resolution: {integrity: sha512-GgcUpXBBEU5ido+/p/mCT2/Xx+Oqmp9JzQRuC+a4lYM4i4LBBn/dWvc0rQ19N9ObA8/T4NWMrPNe79kMBDJqoQ==} + peerDependencies: + '@emotion/core': ^10.0.27 + '@types/react': '>=16' + react: '>=16.3.0' + + '@emotion/stylis@0.8.5': + resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==} + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/unitless@0.7.5': + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + + '@emotion/utils@0.11.3': + resolution: {integrity: sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==} + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.2.5': + resolution: {integrity: sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==} + + '@es-joy/jsdoccomment@0.49.0': + resolution: {integrity: sha512-xjZTSFgECpb9Ohuk5yMX5RhUEbfeQcuOp8IF60e+wyzWEF0M5xeSgqsfLtvPEX8BIyOX9saZqzuGPmZ8oWc+5Q==} + engines: {node: '>=16'} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.28.0': + resolution: {integrity: sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.28.0': + resolution: {integrity: sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.28.0': + resolution: {integrity: sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.28.0': + resolution: {integrity: sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.28.0': + resolution: {integrity: sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.0': + resolution: {integrity: sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.28.0': + resolution: {integrity: sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.0': + resolution: {integrity: sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.28.0': + resolution: {integrity: sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.28.0': + resolution: {integrity: sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.28.0': + resolution: {integrity: sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.14.54': + resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.28.0': + resolution: {integrity: sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.28.0': + resolution: {integrity: sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.28.0': + resolution: {integrity: sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.0': + resolution: {integrity: sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.28.0': + resolution: {integrity: sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.28.0': + resolution: {integrity: sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-arm64@0.28.0': + resolution: {integrity: sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.0': + resolution: {integrity: sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.28.0': + resolution: {integrity: sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.0': + resolution: {integrity: sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/openharmony-arm64@0.28.0': + resolution: {integrity: sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.28.0': + resolution: {integrity: sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.28.0': + resolution: {integrity: sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.28.0': + resolution: {integrity: sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.28.0': + resolution: {integrity: sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.20.1': + resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.2.3': + resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.13.0': + resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.16.0': + resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@0.1.3': + resolution: {integrity: sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==} + engines: {node: ^10.12.0 || >=12.0.0} + + '@eslint/eslintrc@0.4.3': + resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==} + engines: {node: ^10.12.0 || >=12.0.0} + + '@eslint/eslintrc@1.4.1': + resolution: {integrity: sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@9.25.1': + resolution: {integrity: sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.37.0': + resolution: {integrity: sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.2.8': + resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@fastify/ajv-compiler@1.1.0': + resolution: {integrity: sha512-gvCOUNpXsWrIQ3A4aXCLIdblL0tDq42BG/2Xw7oxbil9h11uow10ztS2GuFazNBfjbrsZ5nl+nPl5jDSjj5TSg==} + + '@fastify/forwarded@1.0.0': + resolution: {integrity: sha512-VoO+6WD0aRz8bwgJZ8pkkxjq7o/782cQ1j945HWg0obZMgIadYW3Pew0+an+k1QL7IPZHM3db5WF6OP6x4ymMA==} + engines: {node: '>= 10'} + + '@fastify/proxy-addr@3.0.0': + resolution: {integrity: sha512-ty7wnUd/GeSqKTC2Jozsl5xGbnxUnEFC0On2/zPv/8ixywipQmVZwuWvNGnBoitJ2wixwVqofwXNua8j6Y62lQ==} + + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + + '@floating-ui/devtools@0.2.3': + resolution: {integrity: sha512-ZTcxTvgo9CRlP7vJV62yCxdqmahHTGpSTi5QaTDgGoyQq0OyjaVZhUhXv/qdkQFOI3Sxlfmz0XGG4HaZMsDf8Q==} + peerDependencies: + '@floating-ui/dom': ^1.0.0 + + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + + '@fluentui/date-time-utilities@8.6.11': + resolution: {integrity: sha512-zq49tveFzmzwgaJ73rVvxu9+rqhPBIAJSbevciIQnmvv6dlh2GzZcL14Zevk9QV+q6CWaF6yzvhT11E2TpAv8Q==} + + '@fluentui/dom-utilities@2.3.10': + resolution: {integrity: sha512-6WDImiLqTOpkEtfUKSStcTDpzmJfL6ZammomcjawN9xH/8u8G3Hx72CIt2MNck9giw/oUlNLJFdWRAjeP3rmPQ==} + + '@fluentui/font-icons-mdl2@8.5.72': + resolution: {integrity: sha512-RsdXbnu77uahoFu8GQMyLLeO5FyT+5AvtXhYjm662rs1NaEo89FcbJUjG9UZ2OkWPCNoGmhiFoOVPJwx0TQ6+g==} + + '@fluentui/foundation-legacy@8.6.5': + resolution: {integrity: sha512-ZI8idXy9LMbMS8ixmoUCBfzWUhZyhNp1L2IpX7Nr2MDrAqBbmZcmltCEUMFGpjevI0CDT0H2fRXpWlGbh31+4A==} + peerDependencies: + '@types/react': '>=16.8.0 <20.0.0' + react: '>=16.8.0 <20.0.0' + + '@fluentui/keyboard-key@0.4.23': + resolution: {integrity: sha512-9GXeyUqNJUdg5JiQUZeGPiKnRzMRi9YEUn1l9zq6X/imYdMhxHrxpVZS12129cBfgvPyxt9ceJpywSfmLWqlKA==} + + '@fluentui/keyboard-keys@9.0.8': + resolution: {integrity: sha512-iUSJUUHAyTosnXK8O2Ilbfxma+ZyZPMua5vB028Ys96z80v+LFwntoehlFsdH3rMuPsA8GaC1RE7LMezwPBPdw==} + + '@fluentui/merge-styles@8.6.14': + resolution: {integrity: sha512-vghuHFAfQgS9WLIIs4kgDOCh/DHd5vGIddP4/bzposhlAVLZR6wUBqldm9AuCdY88r5LyCRMavVJLV+Up3xdvA==} + + '@fluentui/priority-overflow@9.3.0': + resolution: {integrity: sha512-yaBC0R4e+4ZlCWDulB5S+xBrlnLwfzdg68GaarCqQO8OHjLg7Ah05xTj7PsAYcoHeEg/9vYeBwGXBpRO8+Tjqw==} + + '@fluentui/react-accordion@9.10.0': + resolution: {integrity: sha512-EwjRfBdC3esMEP++PddyF7bVMSv9+t2W8AY5GkNcwDsqAW3D4zhlvxXBAb3qmpgXy4qMxRWGL8cEaiWgMpH1sg==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-alert@9.0.0-beta.132': + resolution: {integrity: sha512-yIn9Ybx36YBrHIW9epmqr5GXMkSbwI7a1eN/8m710s1aLw38n5P/GF/6t9fyiv/qz9RPMHM6Y/GNTP6/v/Z+9A==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-aria@9.17.10': + resolution: {integrity: sha512-KqS2XcdN84XsgVG4fAESyOBfixN7zbObWfQVLNZ2gZrp2b1hPGVYfQ6J4WOO0vXMKYp0rre/QMOgDm6/srL0XQ==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-avatar@9.11.0': + resolution: {integrity: sha512-3MogJIiOGilKh9y/sWy0Cali1tpvWQNwcs2ryL7EVXi5xwTfKQM/WEgEnW2z+KtumDQUsRqlCHCSoi+x+BF8Qg==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-badge@9.5.1': + resolution: {integrity: sha512-OHS15ovGFPShrAA9U+hCyloJEyffC9gdif0a27AOIB9aVlF/hTzG7toxxulcg4ar4F9X3xXk/uccCCa2kzK0Gw==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-breadcrumb@9.4.0': + resolution: {integrity: sha512-QpCjYlM3JTMnNwh/sDehDbuAVjTcgSfjkPdSmFaPk2lPHpER32CBcJVhheP9en2U5NbW1e+Gtvq8y06RN8FCWw==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-button@9.9.0': + resolution: {integrity: sha512-aH3aSjKyxIiNb9jJOUaaIq47w7jP5ESFSRzvMjcWOETvlWo4QgNqEOOsYqpcltM1OrQZ0sTy/isxppRcyMDlcQ==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-card@9.6.0': + resolution: {integrity: sha512-vgBvhtSzQDa01aOP9zdhJXFLsZAiDVslRfX3HmlIo1pAMt8w+PBq+ypDp1wxM7HPFpj9+RYcERRKtf4MSNP9Nw==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-carousel@9.9.6': + resolution: {integrity: sha512-Ae7DKwQsidRBjUQeiXffRUi8i/26jMgJd24rDVLeQUvoUhs+z/SA9iZN/QMuNl02E291MAEruENKzzkshvfYfg==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-checkbox@9.6.0': + resolution: {integrity: sha512-GMgB1Yx2WP6cISIZoRTyXp2VkJBR8t1+wRyY63RRcofL/ziqqBhz++kl317lbVv7QxnXZh6KlVuoPROWFDQuaw==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-color-picker@9.2.15': + resolution: {integrity: sha512-RMmawl7g4gUYLuTQG2QwCcR9fGC+vDD+snsBlXtObpj/cKpeDmYif46g88pYv86jeIXY1zsjINmLpELmz+uFmw==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-combobox@9.17.0': + resolution: {integrity: sha512-04JTIrXCAbG8HnczFVzJsUJO+NJQ2d/JPynXlmTq7KCMw0BssiF//7IAPFnTiMYmS7jcwc9Uh4ZeFrw+czA79g==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-components@9.72.11': + resolution: {integrity: sha512-fetbBztVDJLeYREcYsBx2LO2D5svO9emBc4OMC/tRmwKtMPbfu3lIl+81kiyj1+kfK9zzdvFnySGkoAU5RXv0g==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-context-selector@9.2.15': + resolution: {integrity: sha512-QymBntFLJNZ9VfTOaBn2ApUSSSC5UuDW8ZcgPJPA+06XEFH+U9Zny2d9QAg1xYNYwIGWahWGQ+7ATOuLxtB8Jw==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + scheduler: '>=0.19.0' + + '@fluentui/react-dialog@9.17.3': + resolution: {integrity: sha512-rF5l8n5yhaB//ZHns0my3Tviir7R8NVyRgTtvV2gLhG58YM7qpm54oraG83uwlXCcZp0wlg2LuIe1cZ559ex1A==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-divider@9.7.0': + resolution: {integrity: sha512-U8Nhrghjeh+XCGM4B7aHYosd6fXaxHC3MpZi7DB0xQ20ljn5cSTpBt4Yvl+tB9ld2+/eM8wekx1GVKyI4yWa3g==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-drawer@9.11.6': + resolution: {integrity: sha512-E+k3eKVb/xKPm2RH5Q1xBjL89NeB1GXtYHO6qRlhQ9auYVTlaBCR7f/ZfIIJJ2x8MzfntQljyl94VARtmZYnyA==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-field@9.5.0': + resolution: {integrity: sha512-yGjB9RXqKrolkkjyAsKVdrH2Xeinj+vromrSCJelgMJ3Q3D6YkExHQzgtdzqo0fVPppnEA4oDKL3Vqqnz/G5Ug==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-focus@8.10.5': + resolution: {integrity: sha512-Jix/4i7ABjgj4a7Ac4JTAWxJkgytpwYTuSM7rtQEfRa4kSRy9E1Ak7NibFexm1kkUkBkFTnp9x1dE27rv+ECJQ==} + peerDependencies: + '@types/react': '>=16.8.0 <20.0.0' + react: '>=16.8.0 <20.0.0' + + '@fluentui/react-hooks@8.10.2': + resolution: {integrity: sha512-HAd5cX50yKW/LljWlwt+FpSpdS/pNJutk9kMb7FyzxfoGBulL7sj6vX2HvxhSKyJMRKuTstXTdfJmsh22+3W3w==} + peerDependencies: + '@types/react': '>=16.8.0 <20.0.0' + react: '>=16.8.0 <20.0.0' + + '@fluentui/react-icons@2.0.323': + resolution: {integrity: sha512-BWFvdg8Er3668fri7o5RVqdfDO3jIg0OvJmUl5EWg6lO7TeC8A+OTggjzqO+J062ONaHPHpQ9IHbnYQ+QXGwXg==} + peerDependencies: + react: '>=16.8.0 <20.0.0' + + '@fluentui/react-image@9.4.0': + resolution: {integrity: sha512-BpcBlmkukm7YYf6PTCbAIMkeCXc8+7aq2eMADsxF5gFD8j3d5lBY3cKByOWRM1NvXcMXmqXr/hQP+ovqNAHzEA==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-infobutton@9.0.0-beta.109': + resolution: {integrity: sha512-5OUJG3V0G9DvP8zG0ixrBIr1rrg/NDAgwqLkr9kPqzYHibg7RiBvNrnmH/IYnSGPkLpOAFfVGD+BTp0ui+uNww==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-infolabel@9.4.19': + resolution: {integrity: sha512-b/3ETF5DPgHcRUcj85iGyiEXUFozFq+IY6tPcyCiUcmIoKScD8McFaHozjpaVqngLbCz0uKNNA0JDy1x/T2ItQ==} + peerDependencies: + '@types/react': '>=16.8.0 <20.0.0' + '@types/react-dom': '>=16.8.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.8.0 <20.0.0' + + '@fluentui/react-input@9.8.1': + resolution: {integrity: sha512-ZlMeYBf1EQg4alI5+9gfx3Icmq3xibPiIYeARtFzOKJ2XzpnD4d/yswx3IDkzXCbqw9rSHtHV03vEeYLUPPTGw==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-jsx-runtime@9.4.1': + resolution: {integrity: sha512-ZodSm7jRa4kaLKDi+emfHFMP/IDnYwFQQAI2BdtKbVrvfwvzPRprGcnTgivnqKBT1ROvKOCY2ddz7+yZzesnNw==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + + '@fluentui/react-label@9.4.0': + resolution: {integrity: sha512-joQ7YNz2dgwDd134sc7e8/vxfFKBUT5AdWx0apT0ohWKgh7RBjB3AdXsaJ8FaMKMNZIGTxZVsP4hHcGsWMTAFw==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-link@9.8.0': + resolution: {integrity: sha512-TH5LS4iuQ4jYzlR84A4n7lQTKaJuiuuGFHMIxoEqtKeMoL9F5AiabuBs6m7Q7clSdTrrcRMNzXLuEFarQrzGTQ==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-list@9.6.13': + resolution: {integrity: sha512-MIP0XKxU68m8VsBCyNBame46nnZ94FCNUArw9T2JuumyKMgV07C+sNhXCe9BCVpUr8e2Hfofo7CZjAsXWDZ0nw==} + peerDependencies: + '@types/react': '>=16.8.0 <20.0.0' + '@types/react-dom': '>=16.8.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.8.0 <20.0.0' + + '@fluentui/react-menu@9.24.0': + resolution: {integrity: sha512-HqIwEM6lPropSHUnbPFufLYdkAIVca87XbNQHCTes4QSLeaF4oEjlBH60rIqQ52k78FwZuUFIciWkSChxJ9ekg==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-message-bar@9.6.23': + resolution: {integrity: sha512-mGnFmYWx6tq36OMTdVtJmxyn3j0p+Shll3+w4W2fW8fcOVSeyrnZ++HLmpurUkVzwI2xR2lL842kxC3GtbwmNw==} + peerDependencies: + '@types/react': '>=16.8.0 <20.0.0' + '@types/react-dom': '>=16.8.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.8.0 <20.0.0' + + '@fluentui/react-motion-components-preview@0.15.3': + resolution: {integrity: sha512-dUH2+GmEWX9q2ojx70VfFLRqzA9fR4YISC6daXkz3iPx4PtesTDn7jwsuXXquaAhltJeBptJ8+K4jbtBrwCMYQ==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-motion@9.14.0': + resolution: {integrity: sha512-gOy8+fUP1KQRM/J6mRhioCMmUrHW9jbLF0DZ9T8nKPQsLrLaSXHxnnI8DcKZjlYc2fKuZitBnbpximgff6HajQ==} + peerDependencies: + '@types/react': '>=16.8.0 <20.0.0' + '@types/react-dom': '>=16.8.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.8.0 <20.0.0' + + '@fluentui/react-nav@9.3.23': + resolution: {integrity: sha512-Z9hA70n5i62sO9IJItkX5+v1F7Lo/539joPaHCLHHca+rySQQZKqy8zLRIfLbh/qF8Nm04ywY19Qt14XjI59cQ==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-overflow@9.7.1': + resolution: {integrity: sha512-Ml1GlcLrAUv31d9WN15WGOZv32gzDtZD5Mp1MOQ3ichDfTtxrswIch7MDzZ8hLMGf/7Y2IzBpV8iFR1XdSrGBA==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-persona@9.7.2': + resolution: {integrity: sha512-u6buhC6Haf8YewBnZAzi49YCwiC8vt0O0YPADemk+4uJ8bhCnayzLxMYGuQ95XO4HFhvVnSPEYjMDdKrMO1hIw==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-popover@9.14.1': + resolution: {integrity: sha512-EODa5yWSfDLPDurjWoZXfkf2ccnbQQbk3s1XYRzxA6RDfdVqUI5W64RJzHWBiNhOLzQEhd6Qb4e6Mshj4FSbdQ==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-portal-compat-context@9.0.15': + resolution: {integrity: sha512-DpV+qtFvM3dmH1j8ZD+YcM5vaTvmQPHUAx6tQnnmIoYJWs2R0wU/L5p2EajXy7zSg74jrDbDRxzaziamoOaJdg==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + + '@fluentui/react-portal@9.8.11': + resolution: {integrity: sha512-2eg4MdW7e2UGRYWPg05GCytAjWYNd55YOP9+iUDINoQwwto9oeFTtZRyn08HYw37cSNqoH24qGz/VBctzTkqDA==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-positioning@9.22.0': + resolution: {integrity: sha512-i3DLC4jd4MoYSZMYLKQNUTpkjKAJ0snIcihvkrjt2jpvv34CifKJhqVtjFQ470pRW4XNx/pBBX07vdXpA3poxA==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-progress@9.5.0': + resolution: {integrity: sha512-VcWXI6UJfBkrDuC/e9oR4YBlpnLUE+FqRRjMG4mVXV+AJzFiljF3mQkFAj94G6dsr54TcoDXC6oydgXLCOTW2A==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-provider@9.22.15': + resolution: {integrity: sha512-a+ImgL9DOlylDM4UYPnxQTA3yXxbVj+O0iNEyTZ6fMzdMsHzpALU4GAq6tOyW4L7RaQtRBmNpVfwTCEKpqaTJQ==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-radio@9.6.1': + resolution: {integrity: sha512-QBoV6l8fVLP+H9Tigq/Y6boiEqMDRhhVMkIfUiWFbnsU/Uc7J5fxW8GoNqzMmoOmC7yvQ/g4jsoTQF27+PzK5w==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-rating@9.4.0': + resolution: {integrity: sha512-qVesFNgQ7uuX8z9d8xqxIXn5ax06xffgBr/eAuZfqVYZG5aRrPHHRoiWf0HDrYD4Lb/HRBLPtbNihNxhXj/LEA==} + peerDependencies: + '@types/react': '>=16.8.0 <20.0.0' + '@types/react-dom': '>=16.8.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.8.0 <20.0.0' + + '@fluentui/react-search@9.4.1': + resolution: {integrity: sha512-Lv2zhPad7SDhMd5NeabXluw4y0Gov9YxDkJhjShMnkiN3yCOA5tlVviNvRXOXxy0gS//d8CiGJ5mBT1bzz2Rrw==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-select@9.5.0': + resolution: {integrity: sha512-pGOD6MBwQsiHKkEdNmVrTavcfC9pOjt4nz/DRlFD444j6iR1PALlus5cNOp7A0JOnGDDvW+1afIvgySCqN0oiA==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-shared-contexts@9.26.2': + resolution: {integrity: sha512-upKXkwlIp5oIhELr4clAZXQkuCd4GDXM6GZEz8BOmRO+PnxyqmycCXvxDxsmi6XN+0vkGM4joiIgkB14o/FctQ==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + + '@fluentui/react-skeleton@9.7.1': + resolution: {integrity: sha512-9WniFEe6gbhkZuBurpQNFmMMhP/Ox84Xm9/iu6q8OmnRkFCyZrEuCFlWGDffnBREKIJqE0VJn5ZrUYWMMh45KA==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-slider@9.6.1': + resolution: {integrity: sha512-ytF1gOEho8DrI817H8WCBsck1RXOlW7JRXYtu9VwH3SnDRM2Jz1CNxbou80+BpvyR1KKkvCc/JSgREgUAnkRAQ==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-spinbutton@9.6.1': + resolution: {integrity: sha512-szqGlEfeJYkBzszEWBjj7ux522ckw9YtKAH0CS0Npd0xcY1GFkdywPwJMOoRUhsO08BOhv6P70Wlx0eYqURgIA==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-spinner@9.8.1': + resolution: {integrity: sha512-vSM5FwjASEor8NBOJx/1MLp8VCw7+pOJqZSvMn29LrUmMbgSZ6CifZFx0GfiX+1fM0EZ2/pqJzFFHpoQQubAyw==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-swatch-picker@9.5.1': + resolution: {integrity: sha512-7rs4dgnFMV2m/2A1tkevrVfThVEJs9crnVWCiSE4XADb9hFp7mqVyN8dKbQCJJMXODLF/Bc90nTCtLV8WaEj4Q==} + peerDependencies: + '@types/react': '>=16.8.0 <20.0.0' + '@types/react-dom': '>=16.8.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.8.0 <20.0.0' + + '@fluentui/react-switch@9.7.1': + resolution: {integrity: sha512-61zJhxG9UBcZ+5T/Dk9yzOJDCOc2ZMZef/ImgIMB4lVsyWs/3n/ec/PKPwjp9SNz2FhQvayhMytEbGzri00jGw==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-table@9.19.14': + resolution: {integrity: sha512-IZ3tDqlQDC+R6nzX4thU8A7Aw3BMhbBZ5tgMOHnW733Xfton7wqKiumjsGJBnef3I48mqnBHJZQEzWBgzLsdqg==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-tabs@9.12.0': + resolution: {integrity: sha512-gKCi1XNDYRvF6R5wETeQptzQRVBlM7VETaQHS/ue1x7+Vo42MbWMtYOmvqeg5CPjqy2hAwch0IA9bzWEQAm2ZA==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-tabster@9.26.13': + resolution: {integrity: sha512-uOuJj7jn1ME52Vc685/Ielf6srK/sfFQA5zBIbXIvy2Eisfp7R1RmJe2sXWoszz/Fu/XDkPwdM/GLv23N3vrvQ==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-tag-picker@9.8.5': + resolution: {integrity: sha512-uhZUWDdg7zmQNjb1/5YI3l6agSDg/yFFaYZDH4eQDOmKIm35jAT2GmEMZVomZZVW/dDhZpezfMWZA5r442cZYQ==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-tags@9.8.0': + resolution: {integrity: sha512-O/Kf8pFgS0/eguzDCPm8FmrPG64dU36xTI1uYKwgF6iVOpmWFjk+7aPQtkoFHQzVwl1iLUL4mQFSutR4A8s38Q==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-teaching-popover@9.6.20': + resolution: {integrity: sha512-XB/SJXdJabulcDBp6z4NNSFOcAnaOoIUZdmzqpx09UxtQwU/eFnYvZw/k1SI8Nc7IpHBgjzId8gHy6jvaN8JHw==} + peerDependencies: + '@types/react': '>=16.8.0 <20.0.0' + '@types/react-dom': '>=16.8.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.8.0 <20.0.0' + + '@fluentui/react-text@9.6.15': + resolution: {integrity: sha512-YB1azhq8MGfnYTGlEAX1mzcFZ6CvqkkaxaCogU4TM9BtPgQ1YUAxE01RMenl8VVi8W9hNbJKkuc8R8GzYwzT4Q==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-textarea@9.7.1': + resolution: {integrity: sha512-YG0j202PRLDLZZDn8QQgREd4Ery2fDYMYb2HUvFdfo6MuSXMvv0RCKEUBCgajIXsHwT31Hsg5+xzM40X4jlOBg==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-theme@9.2.1': + resolution: {integrity: sha512-lJxfz7LmmglFz+c9C41qmMqaRRZZUPtPPl9DWQ79vH+JwZd4dkN7eA78OTRwcGCOTPEKoLTX72R+EFaWEDlX+w==} + + '@fluentui/react-toast@9.7.16': + resolution: {integrity: sha512-Yq4yJboYqtdL5pNJBIYlSdT/kR6m449O95taJCh/msXJyRgqQZ46EmpTcwsxu3D55LTHbqI6Vxu+AikDYH1W7w==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-toolbar@9.7.7': + resolution: {integrity: sha512-49nrRvGqJfdXhwaKZfNIcTiZSqTbThNG8uCa0FvJ88cO11PRPGcr5s6u3plUVxDXUKXpZJ7PKr/TTA0MvP7yIg==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-tooltip@9.10.0': + resolution: {integrity: sha512-+aM0S1mcXy8XKKWgU3TocqTxHjcai7fHns3KwONLJPTp3jXTjyqEoj/o4XX1ka2IM3gdOFfyUU0Gfvw708dn9w==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-tree@9.15.16': + resolution: {integrity: sha512-WP4WjbF/UWCp0JKaZsMFtah/kXu+mxqN8/kghppRYfVHWzLiMgFAPB/OzrGejLNwx+ai3t2dHOIHxXHnR1jYHA==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-utilities@9.26.2': + resolution: {integrity: sha512-Yp2GGNoWifj8Z/VVir4HyRumRsqXnLJd4IP/Y70vEm9ruAvyqUvfn+1lQUuA+k/Reqw8GI+Ix7FTo3rogixZBg==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + + '@fluentui/react-virtualizer@9.0.0-alpha.109': + resolution: {integrity: sha512-pFnbPQ7VeXFQi2+dBVLscdBkhJ0ez7IIPjqaP1VTyJxqnkVyBoIvtX9Y6cL/eK+6aQ97fQ+ZOVZjnCHSsvoB/g==} + peerDependencies: + '@types/react': '>=16.14.0 <20.0.0' + '@types/react-dom': '>=16.9.0 <20.0.0' + react: '>=16.14.0 <20.0.0' + react-dom: '>=16.14.0 <20.0.0' + + '@fluentui/react-window-provider@2.3.2': + resolution: {integrity: sha512-T15zFPIWr9De8hNkapne7YyvcxclyTK2bMXXHZwbWLkVeH/lGHRG0CIy/calNGKa86wuzMJhq8iqFW2W6+EwVQ==} + peerDependencies: + '@types/react': '>=16.8.0 <20.0.0' + react: '>=16.8.0 <20.0.0' + + '@fluentui/react@8.125.5': + resolution: {integrity: sha512-7+tFsQuTlxlg16wSJpngbX+2I1ISa7AL6ip/a8GkLkKR6gcGlkIvK03ixE63fJTCeMHFTJNExcKbdWydAC5WDQ==} + peerDependencies: + '@types/react': '>=16.8.0 <20.0.0' + '@types/react-dom': '>=16.8.0 <20.0.0' + react: '>=16.8.0 <20.0.0' + react-dom: '>=16.8.0 <20.0.0' + + '@fluentui/set-version@8.2.24': + resolution: {integrity: sha512-8uNi2ThvNgF+6d3q2luFVVdk/wZV0AbRfJ85kkvf2+oSRY+f6QVK0w13vMorNhA5puumKcZniZoAfUF02w7NSg==} + + '@fluentui/style-utilities@8.15.0': + resolution: {integrity: sha512-g+hmc2z5iHMI1j4DqihYSws9ERzuT44mjfNGE1ywYqCB8MAzNzAPpyiosWOtI4cWZUQfnqzokpdSKkYF3quM8A==} + + '@fluentui/theme@2.7.2': + resolution: {integrity: sha512-UXGNfGa/1bLmYrOpmHXdvyc7CzlNSKUQAADweTncbNoMF1DvscWEjPj5kxFgCmOU8wVtvvn4GraNNUSWtNxeeA==} + peerDependencies: + '@types/react': '>=16.8.0 <20.0.0' + react: '>=16.8.0 <20.0.0' + + '@fluentui/tokens@1.0.0-alpha.23': + resolution: {integrity: sha512-uxrzF9Z+J10naP0pGS7zPmzSkspSS+3OJDmYIK3o1nkntQrgBXq3dBob4xSlTDm5aOQ0kw6EvB9wQgtlyy4eKQ==} + + '@fluentui/utilities@8.17.2': + resolution: {integrity: sha512-TmeWVtGN+Lk0mch7tuRcbkeMdrBwltI68fvQbPwcNLo4igFtTInMmjEnVJGa7pBQN5lQAmHYqB9IJI6RZU/t6w==} + peerDependencies: + '@types/react': '>=16.8.0 <20.0.0' + react: '>=16.8.0 <20.0.0' + + '@gar/promisify@1.1.3': + resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} + + '@griffel/core@1.20.1': + resolution: {integrity: sha512-ld1mX04zpmeHn8agx4slSEh8kJ+8or3Y0x9gsJNKSKn6GdCkZBSiGUh+oBXCBn8RKzz8l60TA9IhVSStnyKekA==} + + '@griffel/react@1.6.1': + resolution: {integrity: sha512-mNM4/+dIXzqeHboWpVZ1/jiwTAYNc5/8y/V/HasnQ2QXnV6gSUYpeUk/0n6IFU3NJmVJly9JrLSfNo0hM/IFeA==} + peerDependencies: + react: '>=16.8.0 <20.0.0' + + '@griffel/style-types@1.4.0': + resolution: {integrity: sha512-vNDfOGV7RN/XkA7vxgf7Z5HgW8eiBm5cHT9wQPhsKB4pxWom5u6eQ9CkYE5mCCTSPl9H6Nd1NBai04d4P6BD7Q==} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/config-array@0.10.7': + resolution: {integrity: sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/config-array@0.5.0': + resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/config-array@0.9.5': + resolution: {integrity: sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/gitignore-to-minimatch@1.0.2': + resolution: {integrity: sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@1.2.1': + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + deprecated: Use @eslint/object-schema instead + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@inquirer/ansi@2.0.5': + resolution: {integrity: sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/checkbox@5.1.3': + resolution: {integrity: sha512-+G7I8CT+EHv/hasNfUl3P37DVoMoZfpA+2FXmM54dA8MxYle1YqucxbacxHalw1iAFSdKNEDTGNV7F+j1Ldqcg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@6.0.11': + resolution: {integrity: sha512-pTpHjg0iEIRMYV/7oCZUMf27/383E6Wyhfc/MY+AVQGEoUobffIYWOK9YLP2XFRGz/9i6WlTQh1CkFVIo2Y7XA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@11.1.8': + resolution: {integrity: sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@2.0.5': + resolution: {integrity: sha512-NsSs4kzfm12lNetHwAn3GEuH317IzpwrMCbOuMIVytpjnJ90YYHNwdRgYGuKmVxwuIqSgqk3M5qqQt1cDk0tGQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/input@5.0.11': + resolution: {integrity: sha512-twUWidn4ocPO8qi6fRM7tNWt7W1FOnOZqQ+/+PsfLUacMR5rFLDPK9ql0nBPwxi0oELbo8T5NhRs8B2+qQEqFQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@4.1.7': + resolution: {integrity: sha512-1y7+0N65AWk5RdlXH/Kn13txf3IjIQ7OEfhCEkDTU+h5wKMLq8DUF3P6z+/kLSxDGDtQT1dRBWEUC3o/VvImsQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@5.1.3': + resolution: {integrity: sha512-zYyqWgGQi3NhBcNq4Isc5rB3oEdQEh1Q/EcAnOW0FK4MpnXWkvSBYgA4cYrTM4A9UB573omouZbnL9JJ74Mq3A==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@4.0.5': + resolution: {integrity: sha512-aetVUNeKNc/VriqXlw1NRSW0zhMBB0W4bNbWRJgzRl/3d0QNDQFfk0GO5SDdtjMZVg6o8ZKEiadd7SCCzoOn5Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/cliui@9.0.0': + resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} + engines: {node: '>=18'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/console@29.7.0': + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/console@30.3.0': + resolution: {integrity: sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/core@29.5.0': + resolution: {integrity: sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/core@29.7.0': + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/core@30.3.0': + resolution: {integrity: sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/diff-sequences@30.3.0': + resolution: {integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/environment-jsdom-abstract@30.3.0': + resolution: {integrity: sha512-0hNFs5N6We3DMCwobzI0ydhkY10sT1tZSC0AAiy+0g2Dt/qEWgrcV5BrMxPczhe41cxW4qm6X+jqZaUdpZIajA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + jsdom: '*' + peerDependenciesMeta: + canvas: + optional: true + + '@jest/environment@29.7.0': + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/environment@30.3.0': + resolution: {integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/expect-utils@29.7.0': + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect-utils@30.3.0': + resolution: {integrity: sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/expect@29.7.0': + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect@30.3.0': + resolution: {integrity: sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/fake-timers@29.7.0': + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/fake-timers@30.3.0': + resolution: {integrity: sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/get-type@30.1.0': + resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/globals@29.7.0': + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/globals@30.3.0': + resolution: {integrity: sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/pattern@30.0.1': + resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/reporters@29.7.0': + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/reporters@30.3.0': + resolution: {integrity: sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/schemas@30.0.5': + resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/snapshot-utils@30.3.0': + resolution: {integrity: sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/source-map@29.6.3': + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/source-map@30.0.1': + resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/test-result@29.7.0': + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-result@30.3.0': + resolution: {integrity: sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/test-sequencer@29.7.0': + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-sequencer@30.3.0': + resolution: {integrity: sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/transform@26.6.2': + resolution: {integrity: sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==} + engines: {node: '>= 10.14.2'} + + '@jest/transform@29.7.0': + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/transform@30.3.0': + resolution: {integrity: sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/types@25.5.0': + resolution: {integrity: sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==} + engines: {node: '>= 8.3'} + + '@jest/types@26.6.2': + resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} + engines: {node: '>= 10.14.2'} + + '@jest/types@29.5.0': + resolution: {integrity: sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/types@30.3.0': + resolution: {integrity: sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@jsdevtools/ono@7.1.3': + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + + '@jsep-plugin/assignment@1.3.0': + resolution: {integrity: sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==} + engines: {node: '>= 10.16.0'} + peerDependencies: + jsep: ^0.4.0||^1.0.0 + + '@jsep-plugin/regex@1.0.4': + resolution: {integrity: sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==} + engines: {node: '>= 10.16.0'} + peerDependencies: + jsep: ^0.4.0||^1.0.0 + + '@jsonjoy.com/base64@1.1.2': + resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/base64@17.67.0': + resolution: {integrity: sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/buffers@1.2.1': + resolution: {integrity: sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/buffers@17.67.0': + resolution: {integrity: sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/codegen@1.0.0': + resolution: {integrity: sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/codegen@17.67.0': + resolution: {integrity: sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-core@4.57.1': + resolution: {integrity: sha512-YrEi/ZPmgc+GfdO0esBF04qv8boK9Dg9WpRQw/+vM8Qt3nnVIJWIa8HwZ/LXVZ0DB11XUROM8El/7yYTJX+WtA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-fsa@4.57.1': + resolution: {integrity: sha512-ooEPvSW/HQDivPDPZMibHGKZf/QS4WRir1czGZmXmp3MsQqLECZEpN0JobrD8iV9BzsuwdIv+PxtWX9WpPLsIA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-builtins@4.57.1': + resolution: {integrity: sha512-XHkFKQ5GSH3uxm8c3ZYXVrexGdscpWKIcMWKFQpMpMJc8gA3AwOMBJXJlgpdJqmrhPyQXxaY9nbkNeYpacC0Og==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-to-fsa@4.57.1': + resolution: {integrity: sha512-pqGHyWWzNck4jRfaGV39hkqpY5QjRUQ/nRbNT7FYbBa0xf4bDG+TE1Gt2KWZrSkrkZZDE3qZUjYMbjwSliX6pg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-utils@4.57.1': + resolution: {integrity: sha512-vp+7ZzIB8v43G+GLXTS4oDUSQmhAsRz532QmmWBbdYA20s465JvwhkSFvX9cVTqRRAQg+vZ7zWDaIEh0lFe2gw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node@4.57.1': + resolution: {integrity: sha512-3YaKhP8gXEKN+2O49GLNfNb5l2gbnCFHyAaybbA2JkkbQP3dpdef7WcUaHAulg/c5Dg4VncHsA3NWAUSZMR5KQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-print@4.57.1': + resolution: {integrity: sha512-Ynct7ZJmfk6qoXDOKfpovNA36ITUx8rChLmRQtW08J73VOiuNsU8PB6d/Xs7fxJC2ohWR3a5AqyjmLojfrw5yw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-snapshot@4.57.1': + resolution: {integrity: sha512-/oG8xBNFMbDXTq9J7vepSA1kerS5vpgd3p5QZSPd+nX59uwodGJftI51gDYyHRpP57P3WCQf7LHtBYPqwUg2Bg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@1.21.0': + resolution: {integrity: sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@17.67.0': + resolution: {integrity: sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@1.0.2': + resolution: {integrity: sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@17.67.0': + resolution: {integrity: sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@1.9.0': + resolution: {integrity: sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@17.67.0': + resolution: {integrity: sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@leichtgewicht/ip-codec@2.0.5': + resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} + + '@mdx-js/loader@1.6.22': + resolution: {integrity: sha512-9CjGwy595NaxAYp0hF9B/A0lH6C8Rms97e2JS9d3jVUtILn6pT5i5IV965ra3lIWc7Rs1GG1tBdVF7dCowYe6Q==} + + '@mdx-js/mdx@1.6.22': + resolution: {integrity: sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==} + + '@mdx-js/react@1.6.22': + resolution: {integrity: sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==} + peerDependencies: + react: ^16.13.1 || ^17.0.0 + + '@mdx-js/util@1.6.22': + resolution: {integrity: sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==} + + '@microsoft/api-extractor-model@7.33.10': + resolution: {integrity: sha512-uPUK17xGxeQ3av6TN7awp+dTTSTvx8fZC0XFL1gyt7hFapYuxND3XLXH8vkmI7UjfW92oogzFAFqHSifmJROaw==} + + '@microsoft/api-extractor@7.58.12': + resolution: {integrity: sha512-VNpgC/1LaroLbn+UKmwDYspq+9r3EHyj4vJ3qUy/g8vB0rKt+1Wgs7WFj/JGpgxhG8SNyXs1XwYwe7nqkk8IDg==} + hasBin: true + + '@microsoft/load-themed-styles@1.10.295': + resolution: {integrity: sha512-W+IzEBw8a6LOOfRJM02dTT7BDZijxm+Z7lhtOAz1+y9vQm1Kdz9jlAO+qCEKsfxtUOmKilW8DIRqFw2aUgKeGg==} + + '@microsoft/teams-js@1.3.0-beta.4': + resolution: {integrity: sha512-AxDfMpiVqh3hsqTxMEYtQoz866WB/sw/Jl0pgTLh6sMHHmIBNMd+E0pVcP9WNk8zTkr9LCphJ5SziU1C8BgZMA==} + deprecated: Package no longer supported. Use at your own risk + + '@microsoft/tsdoc-config@0.17.0': + resolution: {integrity: sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==} + + '@microsoft/tsdoc-config@0.18.1': + resolution: {integrity: sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==} + + '@microsoft/tsdoc@0.15.0': + resolution: {integrity: sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==} + + '@microsoft/tsdoc@0.16.0': + resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} + + '@modelcontextprotocol/sdk@1.10.2': + resolution: {integrity: sha512-rb6AMp2DR4SN+kc6L1ta2NCpApyA9WYNx3CrTSZvGxq9wH71bRur+zRqPfg0vQ9mjywR7qZdX2RGHOPq3ss+tA==} + engines: {node: '>=18'} + + '@module-federation/error-codes@0.21.6': + resolution: {integrity: sha512-MLJUCQ05KnoVl8xd6xs9a5g2/8U+eWmVxg7xiBMeR0+7OjdWUbHwcwgVFatRIwSZvFgKHfWEiI7wsU1q1XbTRQ==} + + '@module-federation/runtime-core@0.21.6': + resolution: {integrity: sha512-5Hd1Y5qp5lU/aTiK66lidMlM/4ji2gr3EXAtJdreJzkY+bKcI5+21GRcliZ4RAkICmvdxQU5PHPL71XmNc7Lsw==} + + '@module-federation/runtime-tools@0.21.6': + resolution: {integrity: sha512-fnP+ZOZTFeBGiTAnxve+axGmiYn2D60h86nUISXjXClK3LUY1krUfPgf6MaD4YDJ4i51OGXZWPekeMe16pkd8Q==} + + '@module-federation/runtime@0.21.6': + resolution: {integrity: sha512-+caXwaQqwTNh+CQqyb4mZmXq7iEemRDrTZQGD+zyeH454JAYnJ3s/3oDFizdH6245pk+NiqDyOOkHzzFQorKhQ==} + + '@module-federation/sdk@0.21.6': + resolution: {integrity: sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw==} + + '@module-federation/webpack-bundler-runtime@0.21.6': + resolution: {integrity: sha512-7zIp3LrcWbhGuFDTUMLJ2FJvcwjlddqhWGxi/MW3ur1a+HaO8v5tF2nl+vElKmbG1DFLU/52l3PElVcWf/YcsQ==} + + '@mrmlnc/readdir-enhanced@2.2.1': + resolution: {integrity: sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==} + engines: {node: '>=4'} + + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + + '@napi-rs/wasm-runtime@1.0.7': + resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@1.1.3': + resolution: {integrity: sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==} + engines: {node: '>= 6'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@npmcli/fs@1.1.1': + resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==} + + '@npmcli/move-file@1.1.2': + resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==} + engines: {node: '>=10'} + deprecated: This functionality has been moved to @npmcli/fs + + '@peculiar/asn1-cms@2.6.1': + resolution: {integrity: sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw==} + + '@peculiar/asn1-csr@2.6.1': + resolution: {integrity: sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w==} + + '@peculiar/asn1-ecc@2.6.1': + resolution: {integrity: sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g==} + + '@peculiar/asn1-pfx@2.6.1': + resolution: {integrity: sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw==} + + '@peculiar/asn1-pkcs8@2.6.1': + resolution: {integrity: sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw==} + + '@peculiar/asn1-pkcs9@2.6.1': + resolution: {integrity: sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw==} + + '@peculiar/asn1-rsa@2.6.1': + resolution: {integrity: sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA==} + + '@peculiar/asn1-schema@2.6.0': + resolution: {integrity: sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==} + + '@peculiar/asn1-x509-attr@2.6.1': + resolution: {integrity: sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ==} + + '@peculiar/asn1-x509@2.6.1': + resolution: {integrity: sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA==} + + '@peculiar/x509@1.14.3': + resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==} + engines: {node: '>=20.0.0'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@playwright/test@1.56.1': + resolution: {integrity: sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==} + engines: {node: '>=18'} + hasBin: true + + '@pmmmwh/react-refresh-webpack-plugin@0.5.17': + resolution: {integrity: sha512-tXDyE1/jzFsHXjhRZQ3hMl0IVhYe5qula43LDWIhVfjp9G/nT5OQY5AORVOrkEGAUltBJOfOWeETbmhm6kHhuQ==} + engines: {node: '>= 10.13'} + peerDependencies: + '@types/webpack': 4.x || 5.x + react-refresh: '>=0.10.0 <1.0.0' + sockjs-client: ^1.4.0 + type-fest: '>=0.17.0 <5.0.0' + webpack: '>=4.43.0 <6.0.0' + webpack-dev-server: 3.x || 4.x || 5.x + webpack-hot-middleware: 2.x + webpack-plugin-serve: 0.x || 1.x + peerDependenciesMeta: + '@types/webpack': + optional: true + sockjs-client: + optional: true + type-fest: + optional: true + webpack-dev-server: + optional: true + webpack-hot-middleware: + optional: true + webpack-plugin-serve: + optional: true + + '@pnpm/constants@1001.3.1': + resolution: {integrity: sha512-2hf0s4pVrVEH8RvdJJ7YRKjQdiG8m0iAT26TTqXnCbK30kKwJW69VLmP5tED5zstmDRXcOeH5eRcrpkdwczQ9g==} + engines: {node: '>=18.12'} + + '@pnpm/constants@7.1.1': + resolution: {integrity: sha512-31pZqMtjwV+Vaq7MaPrT1EoDFSYwye3dp6BiHIGRJmVThCQwySRKM7hCvqqI94epNkqFAAYoWrNynWoRYosGdw==} + engines: {node: '>=16.14'} + + '@pnpm/crypto.base32-hash@1.0.1': + resolution: {integrity: sha512-pzAXNn6KxTA3kbcI3iEnYs4vtH51XEVqmK/1EiD18MaPKylhqy8UvMJK3zKG+jeP82cqQbozcTGm4yOQ8i3vNw==} + engines: {node: '>=14.6'} + + '@pnpm/crypto.base32-hash@2.0.0': + resolution: {integrity: sha512-3ttOeHBpmWRbgJrpDQ8Nwd3W8s8iuiP5YZM0JRyKWaMtX8lu9d7/AKyxPmhYsMJuN+q/1dwHa7QFeDZJ53b0oA==} + engines: {node: '>=16.14'} + + '@pnpm/crypto.base32-hash@3.0.1': + resolution: {integrity: sha512-DM4RR/tvB7tMb2FekL0Q97A5PCXNyEC+6ht8SaufAUFSJNxeozqHw9PHTZR03mzjziPzNQLOld0pNINBX3srtw==} + engines: {node: '>=18.12'} + + '@pnpm/crypto.hash@1000.1.1': + resolution: {integrity: sha512-lb5kwXaOXdIW/4bkLLmtM9HEVRvp2eIvp+TrdawcPoaptgA/5f0/sRG0P52BF8dFqeNDj+1tGdqH89WQEqJnxA==} + engines: {node: '>=18.12'} + + '@pnpm/crypto.hash@1000.2.2': + resolution: {integrity: sha512-W8pLZvXWLlGG5p0Z2nCvtBhlM6uuTcbAbsS15wlGS31jBBJKJW2udLoFeM7qfWPo7E2PqRPGxca7APpVYAjJhw==} + engines: {node: '>=18.12'} + + '@pnpm/crypto.polyfill@1.0.0': + resolution: {integrity: sha512-WbmsqqcUXKKaAF77ox1TQbpZiaQcr26myuMUu+WjUtoWYgD3VP6iKYEvSx35SZ6G2L316lu+pv+40A2GbWJc1w==} + engines: {node: '>=18.12'} + + '@pnpm/crypto.polyfill@1000.1.0': + resolution: {integrity: sha512-tNe7a6U4rCpxLMBaR0SIYTdjxGdL0Vwb3G1zY8++sPtHSvy7qd54u8CIB0Z+Y6t5tc9pNYMYCMwhE/wdSY7ltg==} + engines: {node: '>=18.12'} + + '@pnpm/dependency-path@1000.0.9': + resolution: {integrity: sha512-0AhabApfiq3EEYeed5HKQEU3ftkrfyKTNgkMH9esGdp2yc+62Zu7eWFf8WW6IGyitDQPLWGYjSEWDC9Bvv8nPg==} + engines: {node: '>=18.12'} + + '@pnpm/dependency-path@1001.1.10': + resolution: {integrity: sha512-PNImtV2SmNTDpLi4HdN86tJPmsOeIxm4VhmxgBVsMrJPEBfkNEWFcflR3wU6XVn/26g9qWdvlNHaawtCjeB93Q==} + engines: {node: '>=18.12'} + + '@pnpm/dependency-path@2.1.8': + resolution: {integrity: sha512-ywBaTjy0iSEF7lH3DlF8UXrdL2bw4AQFV2tTOeNeY7wc1W5CE+RHSJhf9MXBYcZPesqGRrPiU7Pimj3l05L9VA==} + engines: {node: '>=16.14'} + + '@pnpm/dependency-path@5.1.7': + resolution: {integrity: sha512-MKCyaTy1r9fhBXAnhDZNBVgo6ThPnicwJEG203FDp7pGhD7NruS/FhBI+uMd7GNsK3D7aIFCDAgbWpNTXn/eWw==} + engines: {node: '>=18.12'} + + '@pnpm/error@1.4.0': + resolution: {integrity: sha512-vxkRrkneBPVmP23kyjnYwVOtipwlSl6UfL+h+Xa3TrABJTz5rYBXemlTsU5BzST8U4pD7YDkTb3SQu+MMuIDKA==} + engines: {node: '>=10.16'} + + '@pnpm/error@1000.1.0': + resolution: {integrity: sha512-Dqc2IJJPjUatwc9Letw+vG29rnaMrDGi5g6WCx1HiZYm0obXbTmLygeRafMbgf+sLKXrWE1shOeiayQuczBdoA==} + engines: {node: '>=18.12'} + + '@pnpm/error@5.0.3': + resolution: {integrity: sha512-ONJU5cUeoeJSy50qOYsMZQHTA/9QKmGgh1ATfEpCLgtbdwqUiwD9MxHNeXUYYI/pocBCz6r1ZCFqiQvO+8SUKA==} + engines: {node: '>=16.14'} + + '@pnpm/git-utils@1.0.0': + resolution: {integrity: sha512-lUI+XrzOJN4zdPGOGnFUrmtXAXpXi8wD8OI0nWOZmlh+raqbLzC3VkXu1zgaduOK6YonOcnQW88O+ojav1rAdA==} + engines: {node: '>=16.14'} + + '@pnpm/git-utils@1000.0.0': + resolution: {integrity: sha512-W6isNTNgB26n6dZUgwCw6wly+uHQ2Zh5QiRKY1HHMbLAlsnZOxsSNGnuS9euKWHxDftvPfU7uR8XB5x95T5zPQ==} + engines: {node: '>=18.12'} + + '@pnpm/graceful-fs@1000.0.0': + resolution: {integrity: sha512-RvMEliAmcfd/4UoaYQ93DLQcFeqit78jhYmeJJVPxqFGmj0jEcb9Tu0eAOXr7tGP3eJHpgvPbTU4o6pZ1bJhxg==} + engines: {node: '>=18.12'} + + '@pnpm/graceful-fs@1000.1.0': + resolution: {integrity: sha512-EsMX4slK0qJN2AR0/AYohY5m0HQNYGMNe+jhN74O994zp22/WbX+PbkIKyw3UQn39yQm2+z6SgwklDxbeapsmQ==} + engines: {node: '>=18.12'} + + '@pnpm/link-bins@5.3.25': + resolution: {integrity: sha512-9Xq8lLNRHFDqvYPXPgaiKkZ4rtdsm7izwM/cUsFDc5IMnG0QYIVBXQbgwhz2UvjUotbJrvfKLJaCfA3NGBnLDg==} + engines: {node: '>=10.16'} + + '@pnpm/lockfile-file@8.1.8': + resolution: {integrity: sha512-bRadYzGFyFtwiynwp4Mkn7NDNHkgKvJ9xtjsCT5XiE6S8wpzS3W8yx2WzHGk9Mm1J/2wM0F52+NzCWhlz5eIqA==} + engines: {node: '>=16.14'} + peerDependencies: + '@pnpm/logger': ^5.0.0 + + '@pnpm/lockfile-types@5.1.5': + resolution: {integrity: sha512-02FP0HynzX+2DcuPtuMy7PH+kLIC0pevAydAOK+zug2bwdlSLErlvSkc+4+3dw60eRWgUXUqyfO2eR/Ansdbng==} + engines: {node: '>=16.14'} + + '@pnpm/lockfile.fs@1001.1.32': + resolution: {integrity: sha512-I+aHBjbgDy2Ftxla8FVZkx/ARuKOyGag1zaCuVuZDzH4Xb2ETUuTeGAf1GTr1XqM7UnCNse1GKgr5KZJ0cz43w==} + engines: {node: '>=18.12'} + peerDependencies: + '@pnpm/logger': ^1001.0.1 + + '@pnpm/lockfile.merger@1001.0.20': + resolution: {integrity: sha512-93MKB5fObr49PMRoDZVcUewe2uuR6TRj8In0y1CeXeDzXY1SPVKZsODCVvAA2z2UxZ1YXKcw9Oaak31E9ln5CQ==} + engines: {node: '>=18.12'} + + '@pnpm/lockfile.types@1001.1.0': + resolution: {integrity: sha512-/rfDUV8M9iMm0QXahHPv6SD6eKNkrMXlhECJVhDkdL4NIifcv6/HZwYtxd0PIndExz04+OE+iV9K8zKG9i/OEA==} + engines: {node: '>=18.12'} + + '@pnpm/lockfile.types@1002.0.1': + resolution: {integrity: sha512-anzBtzb78rf2KRExS8R38v4nyiU7b9ZMUsyzRdWpo+rfCmLUupjIxvasVlDgsf5pV7tbcBPASOamQ2G5V8IGAQ==} + engines: {node: '>=18.12'} + + '@pnpm/lockfile.types@1002.1.0': + resolution: {integrity: sha512-Oa9Fhwo4Ipodj3hyUPC5wUt5ucVkuttyct2DbFUkB79Fq5HL9MHHQ+JFYh03eajmLqWrN1t8+6DbmcKqRtNjNg==} + engines: {node: '>=18.12'} + + '@pnpm/lockfile.types@900.0.0': + resolution: {integrity: sha512-/4+3CAu4uIjx0ln1DYXNdj0qKJ3wyRDY+RS+eFzV6OHjreaTKWsF2WcjigYp1M5mxL4kj2RsRGgBGEyKtCfEWg==} + engines: {node: '>=18.12'} + + '@pnpm/lockfile.utils@1004.0.3': + resolution: {integrity: sha512-02hFBFk/BGmZYhqd7paBjJm5mHy7GwI6DOJL25dakctRIPCr2kUZkPs/DH3WLKAjNLykEh7Dp/dPs5rnUsUE5g==} + engines: {node: '>=18.12'} + + '@pnpm/logger@1001.0.1': + resolution: {integrity: sha512-gdwlAMXC4Wc0s7Dmg/4wNybMEd/4lSd9LsXQxeg/piWY0PPXjgz1IXJWnVScx6dZRaaodWP3c1ornrw8mZdFZw==} + engines: {node: '>=18.12'} + + '@pnpm/logger@5.0.0': + resolution: {integrity: sha512-YfcB2QrX+Wx1o6LD1G2Y2fhDhOix/bAY/oAnMpHoNLsKkWIRbt1oKLkIFvxBMzLwAEPqnYWguJrYC+J6i4ywbw==} + engines: {node: '>=12.17'} + + '@pnpm/merge-lockfile-changes@5.0.7': + resolution: {integrity: sha512-fYmX1+EHv3wg7l4A9FCEkjgEBIHaY6JosknkLk3pL8dbB9k6unjIrF9f2onNtpj3XUlWxZ3aBw9THk/Bf6hKow==} + engines: {node: '>=16.14'} + + '@pnpm/object.key-sorting@1000.0.1': + resolution: {integrity: sha512-YTJCXyUGOrJuj4QqhSKqZa1vlVAm82h1/uw00ZmD/kL2OViggtyUwWyIe62kpwWVPwEYixfGjfvaFKVJy2mjzA==} + engines: {node: '>=18.12'} + + '@pnpm/package-bins@4.1.0': + resolution: {integrity: sha512-57/ioGYLBbVRR80Ux9/q2i3y8Q+uQADc3c+Yse8jr/60YLOi3jcWz13e2Jy+ANYtZI258Qc5wk2X077rp0Ly/Q==} + engines: {node: '>=10.16'} + + '@pnpm/patching.types@1000.1.0': + resolution: {integrity: sha512-Zib2ysLctRnWM4KXXlljR44qSKwyEqYmLk+8VPBDBEK3l5Gp5mT3N4ix9E4qjYynvFqahumsxzOfxOYQhUGMGw==} + engines: {node: '>=18.12'} + + '@pnpm/patching.types@900.0.0': + resolution: {integrity: sha512-A/3kgRD4Xy2tBMPjOBdx5ZdgmpUobphzWkqDB72S5SIB6gdyCg32AUV0/aO12DwMxpT7kyqMhkkynUOPBfdlUQ==} + engines: {node: '>=18.12'} + + '@pnpm/pick-fetcher@1001.0.0': + resolution: {integrity: sha512-Zl8npMjFSS1gSGM27KkbmfmeOuwU2MCxRFIofAUo/PkqOE2IzzXr0yzB1XYJM8Ml1nUXt9BHfwAlUQKC5MdBLA==} + engines: {node: '>=18.12'} + + '@pnpm/ramda@0.28.1': + resolution: {integrity: sha512-zcAG+lvU0fMziNeGXpPyCyCJYp5ZVrPElEE4t14jAmViaihohocZ+dDkcRIyAomox8pQsuZnv1EyHR+pOhmUWw==} + + '@pnpm/read-modules-dir@2.0.3': + resolution: {integrity: sha512-i9OgRvSlxrTS9a2oXokhDxvQzDtfqtsooJ9jaGoHkznue5aFCTSrNZFQ6M18o8hC03QWfnxaKi0BtOvNkKu2+A==} + engines: {node: '>=10.13'} + + '@pnpm/read-package-json@4.0.0': + resolution: {integrity: sha512-1cr2tEwe4YU6SI0Hmg+wnsr6yxBt2iJtqv6wrF84On8pS9hx4A2PLw3CIgbwxaG0b+ur5wzhNogwl4qD5FLFNg==} + engines: {node: '>=10.16'} + + '@pnpm/read-project-manifest@1.1.7': + resolution: {integrity: sha512-tj8ExXZeDcMmMUj7D292ETe/RiEirr1X1wpT6Zy85z2MrFYoG9jfCJpps40OdZBNZBhxbuKtGPWKVSgXD0yrVw==} + engines: {node: '>=10.16'} + + '@pnpm/resolver-base@1005.0.1': + resolution: {integrity: sha512-NBha12KjFMKwaG1BWTCtgr/RprNQhXItCBkzc8jZuVU0itAHRQhEykexna9K8XjAtYxZ9rhvir0T5a7fTB23yQ==} + engines: {node: '>=18.12'} + + '@pnpm/resolver-base@1005.4.1': + resolution: {integrity: sha512-47zGgACkbZWLOmM61kaE0nkqxiYx63C6DJ4wzDsdj0iXDZJ9SJEl+T035pkhquHe8XEh3YxvwMg2BRyZSgmZ9Q==} + engines: {node: '>=18.12'} + + '@pnpm/types@1000.6.0': + resolution: {integrity: sha512-6PsMNe98VKPGcg6LnXSW/LE3YfJ77nj+bPKiRjYRWAQLZ+xXjEQRaR0dAuyjCmchlv4wR/hpnMVRS21/fCod5w==} + engines: {node: '>=18.12'} + + '@pnpm/types@1000.7.0': + resolution: {integrity: sha512-1s7FvDqmOEIeFGLUj/VO8sF5lGFxeE/1WALrBpfZhDnMXY/x8FbmuygTTE5joWifebcZ8Ww8Kw2CgBoStsIevQ==} + engines: {node: '>=18.12'} + + '@pnpm/types@1000.8.0': + resolution: {integrity: sha512-yx86CGHHquWAI0GgKIuV/RnYewcf5fVFZemC45C/K2cX0uV8GB8TUP541ZrokWola2fZx5sn1vL7xzbceRZfoQ==} + engines: {node: '>=18.12'} + + '@pnpm/types@1001.3.0': + resolution: {integrity: sha512-NLTXheat/u7OEGg5M5vF6Z85zx8uKUZE0+whtX/sbFV2XL48RdnOWGPTKYuVVkv8M+launaLUTgGEXNs/ess2w==} + engines: {node: '>=18.12'} + + '@pnpm/types@12.2.0': + resolution: {integrity: sha512-5RtwWhX39j89/Tmyv2QSlpiNjErA357T/8r1Dkg+2lD3P7RuS7Xi2tChvmOC3VlezEFNcWnEGCOeKoGRkDuqFA==} + engines: {node: '>=18.12'} + + '@pnpm/types@6.4.0': + resolution: {integrity: sha512-nco4+4sZqNHn60Y4VE/fbtlShCBqipyUO+nKRPvDHqLrecMW9pzHWMVRxk4nrMRoeowj3q0rX3GYRBa8lsHTAg==} + engines: {node: '>=10.16'} + + '@pnpm/types@8.9.0': + resolution: {integrity: sha512-3MYHYm8epnciApn6w5Fzx6sepawmsNU7l6lvIq+ER22/DPSrr83YMhU/EQWnf4lORn2YyiXFj0FJSyJzEtIGmw==} + engines: {node: '>=14.6'} + + '@pnpm/types@9.4.2': + resolution: {integrity: sha512-g1hcF8Nv4gd76POilz9gD4LITAPXOe5nX4ijgr8ixCbLQZfcpYiMfJ+C1RlMNRUDo8vhlNB4O3bUlxmT6EAQXA==} + engines: {node: '>=16.14'} + + '@pnpm/types@900.0.0': + resolution: {integrity: sha512-GucC9h/EVbU03Kl7M/FqVes1s5RCQaGCW2f41lFA7VqqHWQElR6k1q33iF6f6fXDUSCdzB1IUxSq9ghP2J+8Pw==} + engines: {node: '>=18.12'} + + '@pnpm/util.lex-comparator@1.0.0': + resolution: {integrity: sha512-3aBQPHntVgk5AweBWZn+1I/fqZ9krK/w01197aYVkAJQGftb+BVWgEepxY5GChjSW12j52XX+CmfynYZ/p0DFQ==} + engines: {node: '>=12.22.0'} + + '@pnpm/util.lex-comparator@3.0.2': + resolution: {integrity: sha512-blFO4Ws97tWv/SNE6N39ZdGmZBrocXnBOfVp0ln4kELmns4pGPZizqyRtR8EjfOLMLstbmNCTReBoDvLz1isVg==} + engines: {node: '>=18.12'} + + '@pnpm/write-project-manifest@1.1.7': + resolution: {integrity: sha512-OLkDZSqkA1mkoPNPvLFXyI6fb0enCuFji6Zfditi/CLAo9kmIhQFmEUDu4krSB8i908EljG8YwL5Xjxzm5wsWA==} + engines: {node: '>=10.16'} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + + '@pothos/core@3.41.2': + resolution: {integrity: sha512-iR1gqd93IyD/snTW47HwKSsRCrvnJaYwjVNcUG8BztZPqMxyJKPAnjPHAgu1XB82KEdysrNqIUnXqnzZIs08QA==} + peerDependencies: + graphql: '>=15.1.0' + + '@radix-ui/colors@3.0.0': + resolution: {integrity: sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==} + + '@radix-ui/number@1.1.1': + resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} + + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + + '@radix-ui/react-checkbox@1.3.3': + resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-icons@1.3.2': + resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc + + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.11': + resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-scroll-area@1.2.10': + resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-tabs@1.1.13': + resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.1': + resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@redis/client@5.8.3': + resolution: {integrity: sha512-MZVUE+l7LmMIYlIjubPosruJ9ltSLGFmJqsXApTqPLyHLjsJUSAbAJb/A3N34fEqean4ddiDkdWzNu4ZKPvRUg==} + engines: {node: '>= 18'} + + '@reduxjs/toolkit@2.11.2': + resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + + '@remix-run/router@1.23.2': + resolution: {integrity: sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==} + engines: {node: '>=14.0.0'} + + '@rollup/rollup-linux-x64-gnu@4.53.3': + resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} + cpu: [x64] + os: [linux] + + '@rspack/binding-darwin-arm64@1.6.8': + resolution: {integrity: sha512-e8CTQtzaeGnf+BIzR7wRMUwKfIg0jd/sxMRc1Vd0bCMHBhSN9EsGoMuJJaKeRrSmy2nwMCNWHIG+TvT1CEKg+A==} + cpu: [arm64] + os: [darwin] + + '@rspack/binding-darwin-x64@1.6.8': + resolution: {integrity: sha512-ku1XpTEPt6Za11zhpFWhfwrTQogcgi9RJrOUVC4FESiPO9aKyd4hJ+JiPgLY0MZOqsptK6vEAgOip+uDVXrCpg==} + cpu: [x64] + os: [darwin] + + '@rspack/binding-linux-arm64-gnu@1.6.8': + resolution: {integrity: sha512-fvZX6xZPvBT8qipSpvkKMX5M7yd2BSpZNCZXcefw6gA3uC7LI3gu+er0LrDXY1PtPzVuHTyDx+abwWpagV3PiQ==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-arm64-musl@1.6.8': + resolution: {integrity: sha512-++XMKcMNrt59HcFBLnRaJcn70k3X0GwkAegZBVpel8xYIAgvoXT5+L8P1ExId/yTFxqedaz8DbcxQnNmMozviw==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-x64-gnu@1.6.8': + resolution: {integrity: sha512-tv3BWkTE1TndfX+DsE1rSTg8fBevCxujNZ3MlfZ22Wfy9x1FMXTJlWG8VIOXmaaJ1wUHzv8S7cE2YUUJ2LuiCg==} + cpu: [x64] + os: [linux] + + '@rspack/binding-linux-x64-musl@1.6.8': + resolution: {integrity: sha512-DCGgZ5/in1O3FjHWqXnDsncRy+48cMhfuUAAUyl0yDj1NpsZu9pP+xfGLvGcQTiYrVl7IH9Aojf1eShP/77WGA==} + cpu: [x64] + os: [linux] + + '@rspack/binding-wasm32-wasi@1.6.8': + resolution: {integrity: sha512-VUwdhl/lI4m6o1OGCZ9JwtMjTV/yLY5VZTQdEPKb40JMTlmZ5MBlr5xk7ByaXXYHr6I+qnqEm73iMKQvg6iknw==} + cpu: [wasm32] + + '@rspack/binding-win32-arm64-msvc@1.6.8': + resolution: {integrity: sha512-23YX7zlOZlub+nPGDBUzktb4D5D6ETUAluKjXEeHIZ9m7fSlEYBnGL66YE+3t1DHXGd0OqsdwlvrNGcyo6EXDQ==} + cpu: [arm64] + os: [win32] + + '@rspack/binding-win32-ia32-msvc@1.6.8': + resolution: {integrity: sha512-cFgRE3APxrY4AEdooVk2LtipwNNT/9mrnjdC5lVbsIsz+SxvGbZR231bxDJEqP15+RJOaD07FO1sIjINFqXMEg==} + cpu: [ia32] + os: [win32] + + '@rspack/binding-win32-x64-msvc@1.6.8': + resolution: {integrity: sha512-cIuhVsZYd3o3Neo1JSAhJYw6BDvlxaBoqvgwRkG1rs0ExFmEmgYyG7ip9pFKnKNWph/tmW3rDYypmEfjs1is7g==} + cpu: [x64] + os: [win32] + + '@rspack/binding@1.6.8': + resolution: {integrity: sha512-lUeL4mbwGo+nqRKqFDCm9vH2jv9FNMVt1X8jqayWRcOCPlj/2UVMEFgqjR7Pp2vlvnTKq//31KbDBJmDZq31RQ==} + + '@rspack/core@1.6.8': + resolution: {integrity: sha512-FolcIAH5FW4J2FET+qwjd1kNeFbCkd0VLuIHO0thyolEjaPSxw5qxG67DA7BZGm6PVcoiSgPLks1DL6eZ8c+fA==} + engines: {node: '>=18.12.0'} + peerDependencies: + '@swc/helpers': '>=0.5.1' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@rspack/dev-server@1.2.1': + resolution: {integrity: sha512-e/ARvskYn2Qdd02qLvc0i6H9BnOmzP0xGHS2XCr7GZ3t2k5uC5ZlLkeN1iEebU0FkAW+6ot89NahFo3nupKuww==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@rspack/core': '*' + + '@rspack/lite-tapable@1.1.0': + resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@rushstack/eslint-config@3.7.1': + resolution: {integrity: sha512-LFoVMbvHj2WbfPjJixqHztCl6yMRSY2a1V2mqfQAjb49n7B06N+FZH5c0o6VmO+96fR1l0PC0DazLeHhRf+uug==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '>=4.7.0' + + '@rushstack/eslint-config@4.6.4': + resolution: {integrity: sha512-nMd5JxzOqkICanNf2He3xebU4txXT5IiQ6ovMeJt5Ou72J+DZzor2kfVGKfQi6uBR2fyhZGYBOahwxnduH2bbA==} + peerDependencies: + eslint: ^8.57.0 || ^9.25.1 + typescript: '>=4.7.0' + + '@rushstack/eslint-patch@1.10.4': + resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==} + + '@rushstack/eslint-patch@1.16.1': + resolution: {integrity: sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==} + + '@rushstack/eslint-plugin-packlets@0.15.2': + resolution: {integrity: sha512-mSW24B5Q1xGm/ANNlzZ+hPIqz8A1JCNeJwHOWOh7dcf43RxcRPqguhllE/7LhwIaly3IH43lUWFBwcamSvJzgw==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + + '@rushstack/eslint-plugin-packlets@0.9.2': + resolution: {integrity: sha512-rZofSLJpwyP7Xo6e4eKYkI7N4JM5PycvPuoX5IEK08PgxPDm/k5pdltH9DkIKnmWvLrxIMU+85VrB5xnjbK0RQ==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@rushstack/eslint-plugin-security@0.14.2': + resolution: {integrity: sha512-9iQwRJyQuMr+Qqj8N56/bqPZtBftuU0i0KEpV/K1dOnyl7IO7yPx4spLP3Hkjej7m6Qa0xnScGDRTvlg7j21Kg==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + + '@rushstack/eslint-plugin-security@0.8.2': + resolution: {integrity: sha512-AkY8BXanfV+RZLaifBglBpWYbR4vJNzYEj6C2m9TLDsRhZPW0h/rUHw6XDVpORhqJYCOXxoZcIwWnKenPbzDuQ==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@rushstack/eslint-plugin@0.15.2': + resolution: {integrity: sha512-oS3ENewjwEj+42jek1MQb2IETUd3On4tDgkuda2Mo7fbourygFZodhPDQYsj6aYFvwwn+FNLk4wjcghSQrCLqA==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@rushstack/eslint-plugin@0.23.2': + resolution: {integrity: sha512-yMvd/jW/gUZ2IdXaOD5mr+DBU0UUjNgsZ/aDrFfzijIPvqdjtMich0gR68VBxvZjciUDQX0Zm4rfezvkxv/bRQ==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + + '@rushstack/heft-api-extractor-plugin@1.3.22': + resolution: {integrity: sha512-IdZ3rrdsswEbOXYmJUe4+vAqWKhEGwt18/ntIkIXpbpH7/plQxuIqpmnlRVF9cdivIb741AmzBS8fG8Fx23Y3A==} + peerDependencies: + '@rushstack/heft': 1.2.22 + + '@rushstack/heft-config-file@0.20.12': + resolution: {integrity: sha512-IFbjnQN/slbygZ/zINx5tp1XNKMkpIKOiQCNAy3plcGXTt4iH9CCyOvOct61z1ODI/lPoK1YDDLHCVatFqCXfw==} + engines: {node: '>=10.13.0'} + + '@rushstack/heft-jest-plugin@2.0.12': + resolution: {integrity: sha512-Z+DdZHMyPl3N7gmzDg2mDdAdofCNyaBWxt/58CVZncf5RHoSvh2mCzxtUIl8gefblPpky30wi1SW638gAPcQZw==} + peerDependencies: + '@rushstack/heft': ^1.2.22 + '@types/jest': ^30.0.0 + jest-environment-jsdom: ^30.3.0 + jest-environment-node: ^30.3.0 + peerDependenciesMeta: + '@types/jest': + optional: true + jest-environment-jsdom: + optional: true + jest-environment-node: + optional: true + + '@rushstack/heft-lint-plugin@1.2.22': + resolution: {integrity: sha512-hspUoHH2aaqFYN7hB1wH5tKNbFVNDzu6o8lmBoVWhLw4GqsEIckgMm65pWHbB0/dWJ+5+2nluTpe2g+MryuGBg==} + peerDependencies: + '@rushstack/heft': 1.2.22 + + '@rushstack/heft-node-rig@2.11.45': + resolution: {integrity: sha512-Th4a+fB20CUtSt5iZi59dTX5ovIRlGfQmwAPReKjYwQ9AEahAtMCVodYLeyNA9q6NnbJNmCuwLLQFaESWn3D4g==} + peerDependencies: + '@rushstack/heft': ^1.2.22 + + '@rushstack/heft-typescript-plugin@1.3.17': + resolution: {integrity: sha512-AEZ8viO7uTO0Ep6I2qYkTyNC/flBBP1aek1pZVI4sWu6cyx9nPgSE0R7HmR8Mgt+HrE1Vc11iVVjH6CAwGu4iw==} + peerDependencies: + '@rushstack/heft': 1.2.22 + + '@rushstack/heft@1.2.22': + resolution: {integrity: sha512-KqB6DlDOaOibpQDPAztwYrjoRQt/FRWsuu3m2kO6a2Y5Q9/AaAWVxVKJMjHlyMIgGI/Bkt7VXFmogE9lULox4w==} + engines: {node: '>=10.13.0'} + hasBin: true + + '@rushstack/node-core-library@3.63.0': + resolution: {integrity: sha512-Q7B3dVpBQF1v+mUfxNcNZh5uHVR8ntcnkN5GYjbBLrxUYHBGKbnCM+OdcN+hzCpFlLBH6Ob0dEHhZ0spQwf24A==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/node-core-library@5.23.3': + resolution: {integrity: sha512-f6uuza7Um65bwsIJgf0MRs7IPA5IG+A+zs1AYGQvpZmLjtTGdfHowhQw4kwF0pPhJCrU4UHNhK8Qa6tLygYZCA==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/operation-graph@0.6.11': + resolution: {integrity: sha512-m/G3HtCqMi2D+vRoQKPKqyW8U1b7+Cb4PGpB73QWiLrARu34yGBcXiHzss2o3m7nhnT/MumJsdCO+8COsYmDBQ==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/problem-matcher@0.2.1': + resolution: {integrity: sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/rig-package@0.7.3': + resolution: {integrity: sha512-aAA518n6wxxjCfnTAOjQnm7ngNE0FVHxHAw2pxKlIhxrMn0XQjGcXKF0oKWpjBgJOmsaJpVob/v+zr3zxgPWuA==} + + '@rushstack/set-webpack-public-path-plugin@4.1.16': + resolution: {integrity: sha512-9YD76OHSYr3pqJwc3wcxIFL1kSxPUyw3xThaZrJDBumMRdAEx7Wj3J0xkPtri5BS06yi49fIC1Di75CxeworzA==} + peerDependencies: + '@types/webpack': ^4.39.8 + peerDependenciesMeta: + '@types/webpack': + optional: true + + '@rushstack/terminal@0.24.2': + resolution: {integrity: sha512-KB7PpvzDyKMw/RGU3TxOwxTs3OwZ4gq6+WHlTJN/JfQH4ezliNtWIqver78jTaAJyz/ZAAlJGH7a/M1WyFLFSw==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/tree-pattern@0.3.4': + resolution: {integrity: sha512-9uROnkiHWsQqxW6HirXABfTRlgzhYp6tevbYIGkwKQ09VaayUBkvFvt/urDKMwlo+tGU0iQQLuVige6c48wTgw==} + + '@rushstack/tree-pattern@0.4.1': + resolution: {integrity: sha512-eFuLBUWUfWQ42u5i25qO1VpTOg6nW2PXaLVwpmjm5tHpPREht0k0L2jsYht8iVvQ732odEeVnkXVcf2nIwbGxA==} + + '@rushstack/ts-command-line@5.3.12': + resolution: {integrity: sha512-Vg2n24arSf7JvUNga2DMHYTbxnVq9L5OtVCp4Gfr8YC/kmL/bmdR8FQhGcpMzMYNY9Vdw3+TaimG11Sf+z1Tpw==} + + '@rushstack/webpack-plugin-utilities@0.3.16': + resolution: {integrity: sha512-0Xb0GESYEyv6Q7hzANZ8RIWa3seiJiCKBNNG83znQwMZ9l0bfnoJzZ3cYODkofoK0E8/nr4hTsn/pWKommf6Mw==} + peerDependencies: + '@types/webpack': ^4.39.8 + webpack: ^5.35.1 + peerDependenciesMeta: + '@types/webpack': + optional: true + webpack: + optional: true + + '@serverless-stack/aws-lambda-ric@2.0.13': + resolution: {integrity: sha512-Aj4X2wMW6O5/PQoKoBdQGC3LwQyGTgW1XZtF0rs07WE9s6Q+46zWaVgURQjoNmTNQKpHSGJYo6B+ycp9u7/CSA==} + hasBin: true + + '@serverless-stack/cli@1.18.4': + resolution: {integrity: sha512-eEG3brlbF/ptIo/s69Hcrn185CVkLWHpmtOmere7+lMPkmy1vxNhWIUuic+LNG0yweK+sg4uMVipREyvwblNDQ==} + hasBin: true + + '@serverless-stack/core@1.18.4': + resolution: {integrity: sha512-j6eoGoZbADbLsc95ZJZQ3nWkXqdlfayx1xEWE0UpFjxthKUi8qZUONd7NOEyTHiR0yYV1NrANcWur2alvn+vlA==} + + '@serverless-stack/resources@1.18.4': + resolution: {integrity: sha512-rryGU74daEYut9ZCvji0SjanKnLEgGAjzQj3LiFCZ6xzty+stR7cJtbfbk/M0rta/tG8vjzVr2xZ/qLUYjdJqg==} + + '@sinclair/typebox@0.27.10': + resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} + + '@sinclair/typebox@0.34.49': + resolution: {integrity: sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==} + + '@sindresorhus/is@4.6.0': + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + + '@sindresorhus/merge-streams@2.3.0': + resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} + engines: {node: '>=18'} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + + '@sinonjs/fake-timers@15.3.0': + resolution: {integrity: sha512-m2xozxSfCIxjDdvbhIWazlP2i2aha/iUmbl94alpsIbd3iLTfeXgfBVbwyWogB6l++istyGZqamgA/EcqYf+Bg==} + + '@smithy/config-resolver@4.4.13': + resolution: {integrity: sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.23.13': + resolution: {integrity: sha512-J+2TT9D6oGsUVXVEMvz8h2EmdVnkBiy2auCie4aSJMvKlzUtO5hqjEzXhoCUkIMo7gAYjbQcN0g/MMSXEhDs1Q==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.2.12': + resolution: {integrity: sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.3.15': + resolution: {integrity: sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.2.12': + resolution: {integrity: sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.2.12': + resolution: {integrity: sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.2.2': + resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.2.12': + resolution: {integrity: sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.4.28': + resolution: {integrity: sha512-p1gfYpi91CHcs5cBq982UlGlDrxoYUX6XdHSo91cQ2KFuz6QloHosO7Jc60pJiVmkWrKOV8kFYlGFFbQ2WUKKQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.4.46': + resolution: {integrity: sha512-SpvWNNOPOrKQGUqZbEPO+es+FRXMWvIyzUKUOYdDgdlA6BdZj/R58p4umoQ76c2oJC44PiM7mKizyyex1IJzow==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.2.16': + resolution: {integrity: sha512-beqfV+RZ9RSv+sQqor3xroUUYgRFCGRw6niGstPG8zO9LgTl0B0MCucxjmrH/2WwksQN7UUgI7KNANoZv+KALA==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.2.12': + resolution: {integrity: sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.3.12': + resolution: {integrity: sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.5.1': + resolution: {integrity: sha512-ejjxdAXjkPIs9lyYyVutOGNOraqUE9v/NjGMKwwFrfOM354wfSD8lmlj8hVwUzQmlLLF4+udhfCX9Exnbmvfzw==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.2.12': + resolution: {integrity: sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.3.12': + resolution: {integrity: sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.2.12': + resolution: {integrity: sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.2.12': + resolution: {integrity: sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.2.12': + resolution: {integrity: sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.4.7': + resolution: {integrity: sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.3.12': + resolution: {integrity: sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.12.8': + resolution: {integrity: sha512-aJaAX7vHe5i66smoSSID7t4rKY08PbD8EBU7DOloixvhOozfYWdcSYE4l6/tjkZ0vBZhGjheWzB2mh31sLgCMA==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.13.1': + resolution: {integrity: sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.2.12': + resolution: {integrity: sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-base64@4.3.2': + resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-browser@4.2.2': + resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-node@4.2.3': + resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.2.2': + resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-config-provider@4.2.2': + resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-browser@4.3.44': + resolution: {integrity: sha512-eZg6XzaCbVr2S5cAErU5eGBDaOVTuTo1I65i4tQcHENRcZ8rMWhQy1DaIYUSLyZjsfXvmCqZrstSMYyGFocvHA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.2.48': + resolution: {integrity: sha512-FqOKTlqSaoV3nzO55pMs5NBnZX8EhoI0DGmn9kbYeXWppgHD6dchyuj2HLqp4INJDJbSrj6OFYJkAh/WhSzZPg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.3.3': + resolution: {integrity: sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.2.2': + resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.2.12': + resolution: {integrity: sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.2.13': + resolution: {integrity: sha512-qQQsIvL0MGIbUjeSrg0/VlQ3jGNKyM3/2iU3FPNgy01z+Sp4OvcaxbgIoFOTvB61ZoohtutuOvOcgmhbD0katQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.5.21': + resolution: {integrity: sha512-KzSg+7KKywLnkoKejRtIBXDmwBfjGvg1U1i/etkC7XSWUyFCoLno1IohV2c74IzQqdhX5y3uE44r/8/wuK+A7Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.2.2': + resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.2.2': + resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} + engines: {node: '>=18.0.0'} + + '@smithy/uuid@1.1.2': + resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} + engines: {node: '>=18.0.0'} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + + '@storybook/addon-actions@6.4.22': + resolution: {integrity: sha512-t2w3iLXFul+R/1ekYxIEzUOZZmvEa7EzUAVAuCHP4i6x0jBnTTZ7sAIUVRaxVREPguH5IqI/2OklYhKanty2Yw==} + deprecated: 'SECURITY: Upgrade to v6.5 or above' + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + '@storybook/addon-backgrounds@6.4.22': + resolution: {integrity: sha512-xQIV1SsjjRXP7P5tUoGKv+pul1EY8lsV7iBXQb5eGbp4AffBj3qoYBSZbX4uiazl21o0MQiQoeIhhaPVaFIIGg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + '@storybook/addon-controls@6.4.22': + resolution: {integrity: sha512-f/M/W+7UTEUnr/L6scBMvksq+ZA8GTfh3bomE5FtWyOyaFppq9k8daKAvdYNlzXAOrUUsoZVJDgpb20Z2VBiSQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + '@storybook/addon-docs@6.4.22': + resolution: {integrity: sha512-9j+i+W+BGHJuRe4jUrqk6ubCzP4fc1xgFS2o8pakRiZgPn5kUQPdkticmsyh1XeEJifwhqjKJvkEDrcsleytDA==} + peerDependencies: + '@storybook/angular': 6.4.22 + '@storybook/html': 6.4.22 + '@storybook/react': 6.4.22 + '@storybook/vue': 6.4.22 + '@storybook/vue3': 6.4.22 + '@storybook/web-components': 6.4.22 + lit: ^2.0.0 + lit-html: ^1.4.1 || ^2.0.0 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + svelte: ^3.31.2 + sveltedoc-parser: ^4.1.0 + vue: ^2.6.10 || ^3.0.0 + webpack: '*' + peerDependenciesMeta: + '@storybook/angular': + optional: true + '@storybook/html': + optional: true + '@storybook/react': + optional: true + '@storybook/vue': + optional: true + '@storybook/vue3': + optional: true + '@storybook/web-components': + optional: true + lit: + optional: true + lit-html: + optional: true + react: + optional: true + react-dom: + optional: true + svelte: + optional: true + sveltedoc-parser: + optional: true + vue: + optional: true + webpack: + optional: true + + '@storybook/addon-essentials@6.4.22': + resolution: {integrity: sha512-GTv291fqvWq2wzm7MruBvCGuWaCUiuf7Ca3kzbQ/WqWtve7Y/1PDsqRNQLGZrQxkXU0clXCqY1XtkTrtA3WGFQ==} + peerDependencies: + '@babel/core': ^7.9.6 + '@storybook/vue': 6.4.22 + '@storybook/web-components': 6.4.22 + babel-loader: ^8.0.0 + lit-html: ^1.4.1 || ^2.0.0-rc.3 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + webpack: '*' + peerDependenciesMeta: + '@storybook/vue': + optional: true + '@storybook/web-components': + optional: true + lit-html: + optional: true + react: + optional: true + react-dom: + optional: true + webpack: + optional: true + + '@storybook/addon-links@6.4.22': + resolution: {integrity: sha512-OSOyDnTXnmcplJHlXTYUTMkrfpLqxtHp2R69IXfAyI1e8WNDb79mXflrEXDA/RSNEliLkqYwCyYby7gDMGds5Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + '@storybook/addon-measure@6.4.22': + resolution: {integrity: sha512-CjDXoCNIXxNfXfgyJXPc0McjCcwN1scVNtHa9Ckr+zMjiQ8pPHY7wDZCQsG69KTqcWHiVfxKilI82456bcHYhQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + '@storybook/addon-outline@6.4.22': + resolution: {integrity: sha512-VIMEzvBBRbNnupGU7NV0ahpFFb6nKVRGYWGREjtABdFn2fdKr1YicOHFe/3U7hRGjb5gd+VazSvyUvhaKX9T7Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + '@storybook/addon-toolbars@6.4.22': + resolution: {integrity: sha512-FFyj6XDYpBBjcUu6Eyng7R805LUbVclEfydZjNiByAoDVyCde9Hb4sngFxn/T4fKAfBz/32HKVXd5iq4AHYtLg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + '@storybook/addon-viewport@6.4.22': + resolution: {integrity: sha512-6jk0z49LemeTblez5u2bYXYr6U+xIdLbywe3G283+PZCBbEDE6eNYy2d2HDL+LbCLbezJBLYPHPalElphjJIcw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + '@storybook/addons@6.4.22': + resolution: {integrity: sha512-P/R+Jsxh7pawKLYo8MtE3QU/ilRFKbtCewV/T1o5U/gm8v7hKQdFz3YdRMAra4QuCY8bQIp7MKd2HrB5aH5a1A==} + peerDependencies: + '@types/react': '>=16' + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + + '@storybook/api@6.4.22': + resolution: {integrity: sha512-lAVI3o2hKupYHXFTt+1nqFct942up5dHH6YD7SZZJGyW21dwKC3HK1IzCsTawq3fZAKkgWFgmOO649hKk60yKg==} + deprecated: 'SECURITY: Upgrade to v6.5 or above' + peerDependencies: + '@types/react': '>=16' + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + + '@storybook/builder-webpack4@6.4.22': + resolution: {integrity: sha512-A+GgGtKGnBneRFSFkDarUIgUTI8pYFdLmUVKEAGdh2hL+vLXAz9A46sEY7C8LQ85XWa8TKy3OTDxqR4+4iWj3A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@storybook/builder-webpack5@9.1.20': + resolution: {integrity: sha512-SN8n6NgfKUD73k9RMDTp0sxHkaEuOLlUWV2VVeXUj+HjacCDLopDXSxMcLsFP5+uSHYLBk4DQiX7EsD0rx8AJw==} + peerDependencies: + storybook: ^9.1.20 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@storybook/channel-postmessage@6.4.22': + resolution: {integrity: sha512-gt+0VZLszt2XZyQMh8E94TqjHZ8ZFXZ+Lv/Mmzl0Yogsc2H+6VzTTQO4sv0IIx6xLbpgG72g5cr8VHsxW5kuDQ==} + + '@storybook/channel-websocket@6.4.22': + resolution: {integrity: sha512-Bm/FcZ4Su4SAK5DmhyKKfHkr7HiHBui6PNutmFkASJInrL9wBduBfN8YQYaV7ztr8ezoHqnYRx8sj28jpwa6NA==} + + '@storybook/channels@6.4.22': + resolution: {integrity: sha512-cfR74tu7MLah1A8Rru5sak71I+kH2e/sY6gkpVmlvBj4hEmdZp4Puj9PTeaKcMXh9DgIDPNA5mb8yvQH6VcyxQ==} + + '@storybook/cli@6.4.22': + resolution: {integrity: sha512-Paj5JtiYG6HjYYEiLm0SGg6GJ+ebJSvfbbYx5W+MNiojyMwrzkof+G2VEGk5AbE2JSkXvDQJ/9B8/SuS94yqvA==} + hasBin: true + peerDependencies: + jest: '*' + + '@storybook/cli@9.1.20': + resolution: {integrity: sha512-9YR9+akCrs84r34Iu3CmpAQ7cNk+7SiSfERVNGja8/fyzrJwUOM1qvo4kqluyfXazZDm1yJIqKx6tJMRKsoueg==} + hasBin: true + + '@storybook/client-api@6.4.22': + resolution: {integrity: sha512-sO6HJNtrrdit7dNXQcZMdlmmZG1k6TswH3gAyP/DoYajycrTwSJ6ovkarzkO+0QcJ+etgra4TEdTIXiGHBMe/A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + + '@storybook/client-logger@6.4.22': + resolution: {integrity: sha512-LXhxh/lcDsdGnK8kimqfhu3C0+D2ylCSPPQNbU0IsLRmTfbpQYMdyl0XBjPdHiRVwlL7Gkw5OMjYemQgJ02zlw==} + + '@storybook/codemod@6.4.22': + resolution: {integrity: sha512-xqnTKUQU2W3vS3dce9s4bYhy15tIfAHIzog37jqpKYOHnByXpPj/KkluGePtv5I6cvMxqP8IhQzn+Eh/lVjM4Q==} + + '@storybook/codemod@9.1.20': + resolution: {integrity: sha512-M7N7Ek73D3dEW2ZgUJOBQRRRyE6L1cNRz2AtqPEMyG0p654LDPFGGnYinbj9/KpaxBmUlQbtBbI/ri23E8XTaA==} + + '@storybook/components@6.4.22': + resolution: {integrity: sha512-dCbXIJF9orMvH72VtAfCQsYbe57OP7fAADtR6YTwfCw9Sm1jFuZr8JbblQ1HcrXEoJG21nOyad3Hm5EYVb/sBw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + + '@storybook/core-client@6.4.22': + resolution: {integrity: sha512-uHg4yfCBeM6eASSVxStWRVTZrAnb4FT6X6v/xDqr4uXCpCttZLlBzrSDwPBLNNLtCa7ntRicHM8eGKIOD5lMYQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + webpack: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@storybook/core-common@6.4.22': + resolution: {integrity: sha512-PD3N/FJXPNRHeQS2zdgzYFtqPLdi3MLwAicbnw+U3SokcsspfsAuyYHZOYZgwO8IAEKy6iCc7TpBdiSJZ/vAKQ==} + deprecated: 'SECURITY: Upgrade to v6.5 or above' + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@storybook/core-events@6.4.22': + resolution: {integrity: sha512-5GYY5+1gd58Gxjqex27RVaX6qbfIQmJxcbzbNpXGNSqwqAuIIepcV1rdCVm6I4C3Yb7/AQ3cN5dVbf33QxRIwA==} + + '@storybook/core-server@6.4.22': + resolution: {integrity: sha512-wFh3e2fa0un1d4+BJP+nd3FVWUO7uHTqv3OGBfOmzQMKp4NU1zaBNdSQG7Hz6mw0fYPBPZgBjPfsJRwIYLLZyw==} + deprecated: 'SECURITY: Upgrade to v6.5 or above' + peerDependencies: + '@storybook/builder-webpack5': 6.4.22 + '@storybook/manager-webpack5': 6.4.22 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + '@storybook/builder-webpack5': + optional: true + '@storybook/manager-webpack5': + optional: true + typescript: + optional: true + + '@storybook/core-webpack@9.1.20': + resolution: {integrity: sha512-GaH54yOx2I/1HUNHdxD3+kbbEE2xoC9sp7+8HxGC0fofEiyK/nlExo0tIX4+LRXC3T7hI+alWEc9bHgkmyLJMg==} + peerDependencies: + storybook: ^9.1.20 + + '@storybook/core@6.4.22': + resolution: {integrity: sha512-KZYJt7GM5NgKFXbPRZZZPEONZ5u/tE/cRbMdkn/zWN3He8+VP+65/tz8hbriI/6m91AWVWkBKrODSkeq59NgRA==} + peerDependencies: + '@storybook/builder-webpack5': 6.4.22 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + webpack: '*' + peerDependenciesMeta: + '@storybook/builder-webpack5': + optional: true + typescript: + optional: true + + '@storybook/csf-tools@6.4.22': + resolution: {integrity: sha512-LMu8MZAiQspJAtMBLU2zitsIkqQv7jOwX7ih5JrXlyaDticH7l2j6Q+1mCZNWUOiMTizj0ivulmUsSaYbpToSw==} + + '@storybook/csf@0.0.2--canary.87bc651.0': + resolution: {integrity: sha512-ajk1Uxa+rBpFQHKrCcTmJyQBXZ5slfwHVEaKlkuFaW77it8RgbPJp/ccna3sgoi8oZ7FkkOyvv1Ve4SmwFqRqw==} + + '@storybook/global@5.0.0': + resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} + + '@storybook/manager-webpack4@6.4.22': + resolution: {integrity: sha512-nzhDMJYg0vXdcG0ctwE6YFZBX71+5NYaTGkxg3xT7gbgnP1YFXn9gVODvgq3tPb3gcRapjyOIxUa20rV+r8edA==} + deprecated: 'SECURITY: Upgrade to v6 or above' + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@storybook/node-logger@6.4.22': + resolution: {integrity: sha512-sUXYFqPxiqM7gGH7gBXvO89YEO42nA4gBicJKZjj9e+W4QQLrftjF9l+mAw2K0mVE10Bn7r4pfs5oEZ0aruyyA==} + + '@storybook/postinstall@6.4.22': + resolution: {integrity: sha512-LdIvA+l70Mp5FSkawOC16uKocefc+MZLYRHqjTjgr7anubdi6y7W4n9A7/Yw4IstZHoknfL88qDj/uK5N+Ahzw==} + + '@storybook/preset-react-webpack@9.1.20': + resolution: {integrity: sha512-/PPsRJVqRhW5P0Ff58AN7wuPxda2et8a5iUN3ebkol9r/zmc17QPzhqbIEDoa1jTC7DYa1pYgXvxbU+fY6lhrQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + storybook: ^9.1.20 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@storybook/preview-web@6.4.22': + resolution: {integrity: sha512-sWS+sgvwSvcNY83hDtWUUL75O2l2LY/GTAS0Zp2dh3WkObhtuJ/UehftzPZlZmmv7PCwhb4Q3+tZDKzMlFxnKQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + + '@storybook/react-docgen-typescript-plugin@1.0.2-canary.253f8c1.0': + resolution: {integrity: sha512-mmoRG/rNzAiTbh+vGP8d57dfcR2aP+5/Ll03KKFyfy5FqWFm/Gh7u27ikx1I3LmVMI8n6jh5SdWMkMKon7/tDw==} + peerDependencies: + typescript: '>= 3.x' + webpack: '>= 4' + + '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0': + resolution: {integrity: sha512-KUqXC3oa9JuQ0kZJLBhVdS4lOneKTOopnNBK4tUAgoxWQ3u/IjzdueZjFr7gyBrXMoU6duutk3RQR9u8ZpYJ4Q==} + peerDependencies: + typescript: '>= 4.x' + webpack: '>= 4' + + '@storybook/react-dom-shim@9.1.20': + resolution: {integrity: sha512-UYdZavfPwHEqCKMqPssUOlyFVZiJExLxnSHwkICSZBmw3gxXJcp1aXWs7PvoZdWz2K4ztl3IcKErXXHeiY6w+A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + storybook: ^9.1.20 + + '@storybook/react-webpack5@9.1.20': + resolution: {integrity: sha512-t5/+UenrE5h0hfsxcB6FOj3pV2YhrrPVpzaHlybgdhzzkPEQSUd34laWi82N74exqcjVLoDwWSkl3M2g1xoaMg==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + storybook: ^9.1.20 + typescript: '>= 4.9.x' + peerDependenciesMeta: + typescript: + optional: true + + '@storybook/react@6.4.22': + resolution: {integrity: sha512-5BFxtiguOcePS5Ty/UoH7C6odmvBYIZutfiy4R3Ua6FYmtxac5vP9r5KjCz1IzZKT8mCf4X+PuK1YvDrPPROgQ==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + '@babel/core': ^7.11.5 + '@types/node': '>=12' + '@types/react': '>=16' + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + '@babel/core': + optional: true + typescript: + optional: true + + '@storybook/react@9.1.20': + resolution: {integrity: sha512-TJhqzggs7HCvLhTXKfx8HodnVq9YizsB2J31s9v6olU0UCxbCY+FYaCF+XdE8qUCyefGRZgHKzGBIczJ/q9e2g==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@types/node': '>=12' + '@types/react': '>=16' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + storybook: ^9.1.20 + typescript: '>= 4.9.x' + peerDependenciesMeta: + typescript: + optional: true + + '@storybook/router@6.4.22': + resolution: {integrity: sha512-zeuE8ZgFhNerQX8sICQYNYL65QEi3okyzw7ynF58Ud6nRw4fMxSOHcj2T+nZCIU5ufozRL4QWD/Rg9P2s/HtLw==} + peerDependencies: + '@types/react': '>=16' + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + + '@storybook/semver@7.3.2': + resolution: {integrity: sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==} + engines: {node: '>=10'} + hasBin: true + + '@storybook/source-loader@6.4.22': + resolution: {integrity: sha512-O4RxqPgRyOgAhssS6q1Rtc8LiOvPBpC1EqhCYWRV3K+D2EjFarfQMpjgPj18hC+QzpUSfzoBZYqsMECewEuLNw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + + '@storybook/store@6.4.22': + resolution: {integrity: sha512-lrmcZtYJLc2emO+1l6AG4Txm9445K6Pyv9cGAuhOJ9Kks0aYe0YtvMkZVVry0RNNAIv6Ypz72zyKc/QK+tZLAQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + + '@storybook/theming@6.4.22': + resolution: {integrity: sha512-NVMKH/jxSPtnMTO4VCN1k47uztq+u9fWv4GSnzq/eezxdGg9ceGL4/lCrNGoNajht9xbrsZ4QvsJ/V2sVGM8wA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + + '@storybook/ui@6.4.22': + resolution: {integrity: sha512-UVjMoyVsqPr+mkS1L7m30O/xrdIEgZ5SCWsvqhmyMUok3F3tRB+6M+OA5Yy+cIVfvObpA7MhxirUT1elCGXsWQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + + '@swc/core-darwin-arm64@1.7.10': + resolution: {integrity: sha512-TYp4x/9w/C/yMU1olK5hTKq/Hi7BjG71UJ4V1U1WxI1JA3uokjQ/GoktDfmH5V5pX4dgGSOJwUe2RjoN8Z/XnA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.7.10': + resolution: {integrity: sha512-P3LJjAWh5yLc6p5IUwV5LgRfA3R1oDCZDMabYyb2BVQuJTD4MfegW9DhBcUUF5dhBLwq3191KpLVzE+dLTbiXw==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.7.10': + resolution: {integrity: sha512-yGOFjE7w/akRTmqGY3FvWYrqbxO7OB2N2FHj2LO5HtzXflfoABb5RyRvdEquX+17J6mEpu4EwjYNraTD/WHIEQ==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.7.10': + resolution: {integrity: sha512-SPWsgWHfdWKKjLrYlvhxcdBJ7Ruy6crJbPoE9NfD95eJEjMnS2yZTqj2ChFsY737WeyhWYlHzgYhYOVCp83YwQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.7.10': + resolution: {integrity: sha512-PUi50bkNqnBL3Z/Zq6jSfwgN9A/taA6u2Zou0tjDJi7oVdpjdr7SxNgCGzMJ/nNg5D/IQn1opM1jktMvpsPAuQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-x64-gnu@1.7.10': + resolution: {integrity: sha512-Sc+pY55gknCAmBQBR6DhlA7jZSxHaLSDb5Sevzi6DOFMXR79NpA6zWTNKwp1GK2AnRIkbAfvYLgOxS5uWTFVpg==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.7.10': + resolution: {integrity: sha512-g5NKx2LXaGd0K26hmEts1Cvb7ptIvq3MHSgr6/D1tRPcDZw1Sp0dYsmyOv0ho4F5GOJyiCooG3oE9FXdb7jIpQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-win32-arm64-msvc@1.7.10': + resolution: {integrity: sha512-plRIsOcfy9t9Q/ivm5DA7I0HaIvfAWPbI+bvVRrr3C/1K2CSqnqZJjEWOAmx2LiyipijNnEaFYuLBp0IkGuJpg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.7.10': + resolution: {integrity: sha512-GntrVNT23viHtbfzmlK8lfBiKeajH24GzbDT7qXhnoO20suUPcyYZxyvCb4gWM2zu8ZBTPHNlqfrNsriQCZ+lQ==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.7.10': + resolution: {integrity: sha512-uXIF8GuSappe1imm6Lf7pHGepfCBjDQlS+qTqvEGE0wZAsL1IVATK9P/cH/OCLfJXeQDTLeSYmrpwjtXNt46tQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.7.10': + resolution: {integrity: sha512-l0xrFwBQ9atizhmV94yC2nwcecTk/oftofwMNPiFMGe56dqdmi2ArHaTV3PCtMlgaUH6rGCehoRMt5OrCI1ktg==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '*' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/helpers@0.5.21': + resolution: {integrity: sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==} + + '@swc/types@0.1.26': + resolution: {integrity: sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==} + + '@szmarczak/http-timer@4.0.6': + resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} + engines: {node: '>=10'} + + '@testing-library/dom@7.21.8': + resolution: {integrity: sha512-iK1rJubFoeD5gxCryokwh09tnJa1Y4doNDbNFYYqOqz6ELwB1+kEAwlezA5xwMi8QrK7xg+1/aBMzb9X/A/EmA==} + engines: {node: '>=10'} + + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/user-event@14.6.1': + resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + + '@tootallnate/once@1.1.2': + resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} + engines: {node: '>= 6'} + + '@trpc/server@9.27.4': + resolution: {integrity: sha512-yw0omUrxGp8+gEAuieZFeXB4bCqFvmyCDL3GOBv+Q6+cK0m5824ViHZKPgK5DYG1ijN/lbi1hP3UVKywPN7rbQ==} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/argparse@1.0.38': + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + + '@types/aria-query@4.2.2': + resolution: {integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==} + + '@types/aws-lambda@8.10.93': + resolution: {integrity: sha512-Vsyi9ogDAY3REZDjYnXMRJJa62SDvxHXxJI5nGDQdZW058dDE+av/anynN2rLKbCKXDRNw3D/sQmqxVflZFi4A==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/bonjour@3.5.13': + resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} + + '@types/cacheable-request@6.0.3': + resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/color-convert@2.0.4': + resolution: {integrity: sha512-Ub1MmDdyZ7mX//g25uBAoH/mWGd9swVbt8BseymnaE18SU4po/PjmCrHxqIIRjBo3hV/vh1KGr0eMxUhp+t+dQ==} + + '@types/color-name@1.1.5': + resolution: {integrity: sha512-j2K5UJqGTxeesj6oQuGpMgifpT5k9HprgQd8D1Y0lOFqKHl3PJu5GMeS4Y5EgjS55AE6OQxf8mPED9uaGbf4Cg==} + + '@types/compression@1.7.5': + resolution: {integrity: sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg==} + peerDependencies: + '@types/express': '*' + + '@types/connect-history-api-fallback@1.5.4': + resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/cors@2.8.19': + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} + + '@types/cross-spawn@6.0.6': + resolution: {integrity: sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/doctrine@0.0.9': + resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==} + + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@8.56.10': + resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/events@3.0.3': + resolution: {integrity: sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==} + + '@types/express-serve-static-core@4.19.8': + resolution: {integrity: sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==} + + '@types/express-serve-static-core@5.1.1': + resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} + + '@types/express@4.17.21': + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + + '@types/express@4.17.25': + resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==} + + '@types/fs-extra@7.0.0': + resolution: {integrity: sha512-ndoMMbGyuToTy4qB6Lex/inR98nPiNHacsgMPvy+zqMLgSxbt8VtWpDArpGp69h1fEDQHn1KB+9DWD++wgbwYA==} + + '@types/glob@7.1.1': + resolution: {integrity: sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==} + + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + + '@types/hast@2.3.10': + resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} + + '@types/hoist-non-react-statics@3.3.7': + resolution: {integrity: sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==} + peerDependencies: + '@types/react': '*' + + '@types/html-minifier-terser@5.1.2': + resolution: {integrity: sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==} + + '@types/html-minifier-terser@6.1.0': + resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} + + '@types/http-cache-semantics@4.2.0': + resolution: {integrity: sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + + '@types/http-proxy@1.17.17': + resolution: {integrity: sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==} + + '@types/is-function@1.0.3': + resolution: {integrity: sha512-/CLhCW79JUeLKznI6mbVieGbl4QU5Hfn+6udw1YHZoofASjbQ5zaP5LzAUZYDpRYEjS4/P+DhEgyJ/PQmGGTWw==} + + '@types/istanbul-lib-coverage@2.0.4': + resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@1.1.2': + resolution: {integrity: sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@23.3.13': + resolution: {integrity: sha512-ePl4l+7dLLmCucIwgQHAgjiepY++qcI6nb8eAwGNkB6OxmTe3Z9rQU3rSpomqu42PCCnlThZbOoxsf+qylJsLA==} + + '@types/jest@28.1.1': + resolution: {integrity: sha512-C2p7yqleUKtCkVjlOur9BWVA4HgUQmEj/HWCt5WzZ5mLXrWnyIfl0wGuArc+kBXsy0ZZfLp+7dywB4HtSVYGVA==} + + '@types/jest@29.5.14': + resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} + + '@types/jest@30.0.0': + resolution: {integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==} + + '@types/jju@1.4.1': + resolution: {integrity: sha512-LFt+YA7Lv2IZROMwokZKiPNORAV5N3huMs3IKnzlE430HWhWYZ8b+78HiwJXJJP1V2IEjinyJURuRJfGoaFSIA==} + + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + + '@types/jsdom@21.1.7': + resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json-stable-stringify-without-jsonify@1.0.2': + resolution: {integrity: sha512-X/Kn5f5fv1KBGqGDaegrj72Dlh+qEKN3ELwMAB6RdVlVzkf6NTeEnJpgR/Hr0AlpgTlYq/Vd0U3f79lavn6aDA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/keyv@3.1.4': + resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + + '@types/loader-utils@1.1.3': + resolution: {integrity: sha512-euKGFr2oCB3ASBwG39CYJMR3N9T0nanVqXdiH7Zu/Nqddt6SmFRxytq/i2w9LQYNQekEtGBz+pE3qG6fQTNvRg==} + + '@types/lodash@4.17.23': + resolution: {integrity: sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==} + + '@types/long@4.0.0': + resolution: {integrity: sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==} + + '@types/mdast@3.0.15': + resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} + + '@types/mime-types@2.1.4': + resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/minimatch@6.0.0': + resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} + deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. + + '@types/mocha@10.0.6': + resolution: {integrity: sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==} + + '@types/node-fetch@2.6.13': + resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} + + '@types/node-forge@1.3.14': + resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==} + + '@types/node@14.0.1': + resolution: {integrity: sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA==} + + '@types/node@14.18.63': + resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} + + '@types/node@17.0.41': + resolution: {integrity: sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw==} + + '@types/node@20.17.19': + resolution: {integrity: sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==} + + '@types/node@22.9.3': + resolution: {integrity: sha512-F3u1fs/fce3FFk+DAxbxc78DF8x0cY09RRL8GnXLmkJ1jvx3TtPdWoTT5/NiYfI5ASqXBmfqJi9dZ3gxMx4lzw==} + + '@types/normalize-package-data@2.4.4': + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + + '@types/npm-package-arg@6.1.0': + resolution: {integrity: sha512-vbt5fb0y1svMhu++1lwtKmZL76d0uPChFlw7kEzyUmTwfmpHRcFb8i0R8ElT69q/L+QLgK2hgECivIAvaEDwag==} + + '@types/npm-packlist@1.1.2': + resolution: {integrity: sha512-9NYoEH87t90e6dkaQOuUTY/R1xUE0a67sXzJBuAB+b+/z4FysHFD19g/O154ToGjyWqKYkezVUtuBdtfd4hyfw==} + + '@types/npmlog@4.1.6': + resolution: {integrity: sha512-0l3z16vnlJGl2Mi/rgJFrdwfLZ4jfNYgE6ZShEpjqhHuGTqdEzNles03NpYHwUMVYZa+Tj46UxKIEpE78lQ3DQ==} + + '@types/object-hash@3.0.6': + resolution: {integrity: sha512-fOBV8C1FIu2ELinoILQ+ApxcUKz4ngq+IWUYrxSGjXzzjUALijilampwkMgEtJ+h2njAW3pi853QpzNVCHB73w==} + + '@types/overlayscrollbars@1.12.5': + resolution: {integrity: sha512-1yMmgFrq1DQ3sCHyb3DNfXnE0dB463MjG47ugX3cyade3sOt3U8Fjxk/Com0JJguTLPtw766TSDaO4NC65Wgkw==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/parse5@5.0.3': + resolution: {integrity: sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==} + + '@types/pretty-hrtime@1.0.3': + resolution: {integrity: sha512-nj39q0wAIdhwn7DGUyT9irmsKK1tV0bd5WFEhgpqNTMFZ8cE+jieuTphCW0tfdm47S2zVT5mr09B28b1chmQMA==} + + '@types/prismjs@1.26.6': + resolution: {integrity: sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/qs@6.15.0': + resolution: {integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/react-dom@17.0.25': + resolution: {integrity: sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react-redux@7.1.34': + resolution: {integrity: sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==} + + '@types/react-syntax-highlighter@11.0.5': + resolution: {integrity: sha512-VIOi9i2Oj5XsmWWoB72p3KlZoEbdRAcechJa8Ztebw7bDl2YmR+odxIqhtJGp1q2EozHs02US+gzxJ9nuf56qg==} + + '@types/react@17.0.74': + resolution: {integrity: sha512-nBtFGaeTMzpiL/p73xbmCi00SiCQZDTJUk9ZuHOLtil3nI+y7l269LHkHIAYpav99ZwGnPJzuJsJpfLXjiQ52g==} + + '@types/react@19.2.7': + resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} + + '@types/read-package-tree@5.1.0': + resolution: {integrity: sha512-QEaGDX5COe5Usog79fca6PEycs59075O/W0QcOJjVNv+ZQ26xjqxg8sWu63Lwdt4KAI08gb4Muho1EbEKs3YFw==} + + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + + '@types/responselike@1.0.3': + resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + + '@types/retry@0.12.0': + resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + + '@types/retry@0.12.2': + resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} + + '@types/scheduler@0.16.8': + resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} + + '@types/semver@7.7.1': + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + + '@types/send@0.17.6': + resolution: {integrity: sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==} + + '@types/send@1.2.1': + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} + + '@types/serialize-javascript@5.0.4': + resolution: {integrity: sha512-Z2R7UKFuNWCP8eoa2o9e5rkD3hmWxx/1L0CYz0k2BZzGh0PhEVMp9kfGiqEml/0IglwNERXZ2hwNzIrSz/KHTA==} + + '@types/serve-index@1.9.4': + resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} + + '@types/serve-static@1.15.10': + resolution: {integrity: sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==} + + '@types/serve-static@2.2.0': + resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} + + '@types/sockjs@0.3.36': + resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} + + '@types/source-list-map@0.1.6': + resolution: {integrity: sha512-5JcVt1u5HDmlXkwOD2nslZVllBBc7HDuOICfiZah2Z0is8M8g+ddAEawbmd3VjedfDHBzxCaXLs07QEmb7y54g==} + + '@types/ssri@7.1.5': + resolution: {integrity: sha512-odD/56S3B51liILSk5aXJlnYt99S6Rt9EFDDqGtJM26rKHApHcwyU/UoYHrzKkdkHMAIquGWCuHtQTbes+FRQw==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/strict-uri-encode@2.0.0': + resolution: {integrity: sha512-R6vDd7CHxcWMzv5wfVhR3qyCRVQoZKwVd6kit0rkozTThRZSXZKEW2Kz3AxfVqq9+UyJAz1g8Q+bJ3CL6NzztQ==} + + '@types/supports-color@8.1.3': + resolution: {integrity: sha512-Hy6UMpxhE3j1tLpl27exp1XqHD7n8chAiNPzWfz16LPZoMMoSc4dzLl6w9qijkEb/r5O1ozdu1CWGA2L83ZeZg==} + + '@types/tapable@1.0.6': + resolution: {integrity: sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==} + + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + + '@types/uglify-js@3.17.5': + resolution: {integrity: sha512-TU+fZFBTBcXj/GpDpDaBmgWk/gn96kMZ+uocaFUlV2f8a6WdMzzI44QBCmGcCiYR0Y6ZlNRiyUyKKt5nl/lbzQ==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + + '@types/vscode@1.103.0': + resolution: {integrity: sha512-o4hanZAQdNfsKecexq9L3eHICd0AAvdbLk6hA60UzGXbGH/q8b/9xv2RgR7vV3ZcHuyKVq7b37IGd/+gM4Tu+Q==} + + '@types/watchpack@2.4.0': + resolution: {integrity: sha512-PSAD+o9hezvfUFFzrYB/PO6Je7kwiZ2BSnB3/EZ9le+jTDKB6x5NJ96WWzQz1h/AyGJ/de3/1KpuBTkUFZm77A==} + + '@types/webpack-env@1.18.8': + resolution: {integrity: sha512-G9eAoJRMLjcvN4I08wB5I7YofOb/kaJNd5uoCMX+LbKXTPCF+ZIHuqTnFaK9Jz1rgs035f9JUPUhNFtqgucy/A==} + + '@types/webpack-sources@1.4.2': + resolution: {integrity: sha512-77T++JyKow4BQB/m9O96n9d/UUHWLQHlcqXb9Vsf4F1+wKNrrlWNFPDLKNT92RJnCSL6CieTc+NDXtCVZswdTw==} + + '@types/webpack@4.41.32': + resolution: {integrity: sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@types/xmldoc@1.1.4': + resolution: {integrity: sha512-a/ONNCf9itbmzEz1ohx0Fv5TLJzXIPQTapxFu+DlYlDtn9UcAa1OhnrOOMwbU8125hFjrkJKL3qllD7vO5Bivw==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@15.0.20': + resolution: {integrity: sha512-KIkX+/GgfFitlASYCGoSF+T4XRXhOubJLhkLVtSfsRTe9jWMmuM2g28zQ41BtPTG7TRBb2xHW+LCNVE9QR/vsg==} + + '@types/yargs@17.0.35': + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} + + '@typescript-eslint/eslint-plugin@6.19.1': + resolution: {integrity: sha512-roQScUGFruWod9CEyoV5KlCYrubC/fvG8/1zXuT0WTcxX87GnMMmnksMwSg99lo1xiKrBzw2icsJPMAw1OtKxg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/eslint-plugin@8.56.1': + resolution: {integrity: sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.56.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@6.19.1': + resolution: {integrity: sha512-WEfX22ziAh6pRE9jnbkkLGp/4RhTpffr2ZK5bJ18M8mIfA8A+k97U9ZyaXCEJRlmMHh7R9MJZWXp/r73DzINVQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@8.56.1': + resolution: {integrity: sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.56.1': + resolution: {integrity: sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/rule-tester@8.56.1': + resolution: {integrity: sha512-EWuV5Vq1EFYJEOVcILyWPO35PjnT0c6tv99PCpD12PgfZae5/Jo+F17hGjsEs2Moe+Dy1J7KIr8y037cK8+/rQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + + '@typescript-eslint/scope-manager@6.19.1': + resolution: {integrity: sha512-4CdXYjKf6/6aKNMSly/BP4iCSOpvMmqtDzRtqFyyAae3z5kkqEjKndR5vDHL8rSuMIIWP8u4Mw4VxLyxZW6D5w==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/scope-manager@8.56.1': + resolution: {integrity: sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.56.1': + resolution: {integrity: sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@6.19.1': + resolution: {integrity: sha512-0vdyld3ecfxJuddDjACUvlAeYNrHP/pDeQk2pWBR2ESeEzQhg52DF53AbI9QCBkYE23lgkhLCZNkHn2hEXXYIg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/type-utils@8.56.1': + resolution: {integrity: sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@6.19.1': + resolution: {integrity: sha512-6+bk6FEtBhvfYvpHsDgAL3uo4BfvnTnoge5LrrCj2eJN8g3IJdLTD4B/jK3Q6vo4Ql/Hoip9I8aB6fF+6RfDqg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + + '@typescript-eslint/types@8.56.1': + resolution: {integrity: sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + + '@typescript-eslint/typescript-estree@6.19.1': + resolution: {integrity: sha512-aFdAxuhzBFRWhy+H20nYu19+Km+gFfwNO4TEqyszkMcgBDYQjmPJ61erHxuT2ESJXhlhrO7I5EFIlZ+qGR8oVA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/typescript-estree@8.56.1': + resolution: {integrity: sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@6.19.1': + resolution: {integrity: sha512-JvjfEZuP5WoMqwh9SPAPDSHSg9FBHHGhjPugSRxu5jMfjvBpq5/sGTD+9M9aQ5sh6iJ8AY/Kk/oUYVEMAPwi7w==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + + '@typescript-eslint/utils@8.56.1': + resolution: {integrity: sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@6.19.1': + resolution: {integrity: sha512-gkdtIO+xSO/SmI0W68DBg4u1KElmIUo3vXzgHyGPs6cxgB0sa3TlptRAAE0hUY1hM6FcDKEv7aIwiTGm76cXfQ==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/visitor-keys@8.56.1': + resolution: {integrity: sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typespec/ts-http-runtime@0.3.4': + resolution: {integrity: sha512-CI0NhTrz4EBaa0U+HaaUZrJhPoso8sG7ZFya8uQoBA57fjzrjRSv87ekCjLZOFExN+gXE/z0xuN2QfH4H2HrLQ==} + engines: {node: '>=20.0.0'} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.11.1': + resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.11.1': + resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + cpu: [x64] + os: [win32] + + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + '@vscode/test-electron@1.6.2': + resolution: {integrity: sha512-W01ajJEMx6223Y7J5yaajGjVs1QfW3YGkkOJHVKfAMEqNB1ZHN9wCcViehv5ZwVSSJnjhu6lYEYgwBdHtCxqhQ==} + engines: {node: '>=8.9.3'} + + '@vscode/vsce-sign-alpine-arm64@2.0.6': + resolution: {integrity: sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==} + cpu: [arm64] + os: [alpine] + + '@vscode/vsce-sign-alpine-x64@2.0.6': + resolution: {integrity: sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==} + cpu: [x64] + os: [alpine] + + '@vscode/vsce-sign-darwin-arm64@2.0.6': + resolution: {integrity: sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==} + cpu: [arm64] + os: [darwin] + + '@vscode/vsce-sign-darwin-x64@2.0.6': + resolution: {integrity: sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==} + cpu: [x64] + os: [darwin] + + '@vscode/vsce-sign-linux-arm64@2.0.6': + resolution: {integrity: sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==} + cpu: [arm64] + os: [linux] + + '@vscode/vsce-sign-linux-arm@2.0.6': + resolution: {integrity: sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==} + cpu: [arm] + os: [linux] + + '@vscode/vsce-sign-linux-x64@2.0.6': + resolution: {integrity: sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==} + cpu: [x64] + os: [linux] + + '@vscode/vsce-sign-win32-arm64@2.0.6': + resolution: {integrity: sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==} + cpu: [arm64] + os: [win32] + + '@vscode/vsce-sign-win32-x64@2.0.6': + resolution: {integrity: sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==} + cpu: [x64] + os: [win32] + + '@vscode/vsce-sign@2.0.9': + resolution: {integrity: sha512-8IvaRvtFyzUnGGl3f5+1Cnor3LqaUWvhaUjAYO8Y39OUYlOf3cRd+dowuQYLpZcP3uwSG+mURwjEBOSq4SOJ0g==} + + '@vscode/vsce@3.2.1': + resolution: {integrity: sha512-AY9vBjwExakK1c0cI/3NN2Ey0EgiKLBye/fxl/ue+o4q6RZ7N+xzd1jAD6eI6eBeMVANi617+V2rxIAkDPco2Q==} + engines: {node: '>= 20'} + hasBin: true + + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/ast@1.9.0': + resolution: {integrity: sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/floating-point-hex-parser@1.9.0': + resolution: {integrity: sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-api-error@1.9.0': + resolution: {integrity: sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-buffer@1.9.0': + resolution: {integrity: sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==} + + '@webassemblyjs/helper-code-frame@1.9.0': + resolution: {integrity: sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==} + + '@webassemblyjs/helper-fsm@1.9.0': + resolution: {integrity: sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==} + + '@webassemblyjs/helper-module-context@1.9.0': + resolution: {integrity: sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-bytecode@1.9.0': + resolution: {integrity: sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/helper-wasm-section@1.9.0': + resolution: {integrity: sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/ieee754@1.9.0': + resolution: {integrity: sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/leb128@1.9.0': + resolution: {integrity: sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/utf8@1.9.0': + resolution: {integrity: sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-edit@1.9.0': + resolution: {integrity: sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-gen@1.9.0': + resolution: {integrity: sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-opt@1.9.0': + resolution: {integrity: sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wasm-parser@1.9.0': + resolution: {integrity: sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==} + + '@webassemblyjs/wast-parser@1.9.0': + resolution: {integrity: sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@webassemblyjs/wast-printer@1.9.0': + resolution: {integrity: sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + '@yarnpkg/lockfile@1.0.2': + resolution: {integrity: sha512-MqJ00WXw89ga0rK6GZkdmmgv3bAsxpJixyTthjcix73O44pBqotyU2BejBkLuIsaOBI6SEu77vAnSyLe5iIHkw==} + + '@zkochan/cmd-shim@5.4.1': + resolution: {integrity: sha512-odWb1qUzt0dIOEUPyWBEpFDYQPRjEMr/dbHHAfgBkVkYR9aO7Zo+I7oYWrXIxl+cKlC7+49ftPm8uJxL1MA9kw==} + engines: {node: '>=10.13'} + + '@zkochan/js-yaml@0.0.11': + resolution: {integrity: sha512-SO+h5Jg079r2JvGle0jbdtk1EY7ppu6TGzmfWTp3Gy61IEb1OVKBocJ6ydTn4++nYFNfRKYenI2MniZQwsM9KQ==} + hasBin: true + + '@zkochan/js-yaml@0.0.6': + resolution: {integrity: sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg==} + hasBin: true + + '@zkochan/rimraf@2.1.3': + resolution: {integrity: sha512-mCfR3gylCzPC+iqdxEA6z5SxJeOgzgbwmyxanKriIne5qZLswDe/M43aD3p5MNzwzXRhbZg/OX+MpES6Zk1a6A==} + engines: {node: '>=12.10'} + + '@zkochan/rimraf@3.0.2': + resolution: {integrity: sha512-GBf4ua7ogWTr7fATnzk/JLowZDBnBJMm8RkMaC/KcvxZ9gxbMWix0/jImd815LmqKyIHZ7h7lADRddGMdGBuCA==} + engines: {node: '>=18.12'} + + '@zkochan/which@2.0.3': + resolution: {integrity: sha512-C1ReN7vt2/2O0fyTsx5xnbQuxBrmG5NMSbcIkPKCCfCTJgpZBsuRYzFXHj3nVq8vTfK7vxHUmzfCpSHgO7j4rg==} + engines: {node: '>= 8'} + hasBin: true + + abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + deprecated: Use your platform's native atob() and btoa() methods instead + + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + + abstract-logging@2.0.1: + resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@7.2.0: + resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} + engines: {node: '>=0.4.0'} + + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + engines: {node: '>=0.4.0'} + + acorn@6.4.2: + resolution: {integrity: sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + acorn@7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + address@1.2.2: + resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} + engines: {node: '>= 10.0.0'} + + agent-base@5.1.1: + resolution: {integrity: sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==} + engines: {node: '>= 6.0.0'} + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + + airbnb-js-shims@2.2.1: + resolution: {integrity: sha512-wJNXPH66U2xjgo1Zwyjf9EydvJ2Si94+vSdk6EERcBfB2VZkeltpqIats0cqIZMLCXP3zcyaUKGYQeIBT6XjsQ==} + + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-errors@1.0.1: + resolution: {integrity: sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==} + peerDependencies: + ajv: '>=5.0.0' + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + + ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-colors@3.2.4: + resolution: {integrity: sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==} + engines: {node: '>=6'} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-html-community@0.0.8: + resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} + engines: {'0': node >= 0.8.0} + hasBin: true + + ansi-html@0.0.9: + resolution: {integrity: sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg==} + engines: {'0': node >= 0.8.0} + hasBin: true + + ansi-regex@2.1.1: + resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} + engines: {node: '>=0.10.0'} + + ansi-regex@4.1.1: + resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} + engines: {node: '>=6'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + ansi-to-html@0.6.15: + resolution: {integrity: sha512-28ijx2aHJGdzbs+O5SNQF65r6rrKYnkuwTYm8lZlChuoJ9P1vVzIpWO20sQTqTPDXYp6NFwk326vApTtLVFXpQ==} + engines: {node: '>=8.0.0'} + hasBin: true + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@2.0.0: + resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + app-root-dir@1.0.2: + resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==} + + aproba@1.2.0: + resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==} + + aproba@2.1.0: + resolution: {integrity: sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==} + + archiver-utils@2.1.0: + resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} + engines: {node: '>= 6'} + + archiver-utils@3.0.4: + resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} + engines: {node: '>= 10'} + + archiver@5.3.2: + resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} + engines: {node: '>= 10'} + + archy@1.0.0: + resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} + + are-docs-informative@0.0.2: + resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} + engines: {node: '>=14'} + + are-we-there-yet@1.1.7: + resolution: {integrity: sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==} + deprecated: This package is no longer supported. + + are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@4.2.2: + resolution: {integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==} + engines: {node: '>=6.0'} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + arr-diff@4.0.0: + resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} + engines: {node: '>=0.10.0'} + + arr-flatten@1.1.0: + resolution: {integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==} + engines: {node: '>=0.10.0'} + + arr-union@3.1.0: + resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==} + engines: {node: '>=0.10.0'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array-union@1.0.2: + resolution: {integrity: sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==} + engines: {node: '>=0.10.0'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array-uniq@1.0.3: + resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==} + engines: {node: '>=0.10.0'} + + array-unique@0.3.2: + resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==} + engines: {node: '>=0.10.0'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.map@1.0.8: + resolution: {integrity: sha512-YocPM7bYYu2hXGxWpb5vwZ8cMeudNHYtYBcUDY4Z1GWa53qcnQMWSl25jeBHNzitjl9HW2AWW4ro/S/nftUaOQ==} + engines: {node: '>= 0.4'} + + array.prototype.reduce@1.0.8: + resolution: {integrity: sha512-DwuEqgXFBwbmZSRqt3BpQigWNUoqw9Ml2dTWdF3B2zQlQX4OeUE0zyuzX0fX0IbTvjdkZbcBTU3idgpO78qkTw==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + arrify@2.0.1: + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} + + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + + asn1.js@4.10.1: + resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} + + asn1js@3.0.7: + resolution: {integrity: sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==} + engines: {node: '>=12.0.0'} + + assert@1.5.1: + resolution: {integrity: sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + assign-symbols@1.0.0: + resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} + engines: {node: '>=0.10.0'} + + ast-types@0.13.3: + resolution: {integrity: sha512-XTZ7xGML849LkQP86sWdQzfhwbt3YwIO6MqbX9mUNYY98VKaaVZP7YNNm70IpwecbkkxmfC5IYAzOQ/2p29zRA==} + engines: {node: '>=4'} + + ast-types@0.14.2: + resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==} + engines: {node: '>=4'} + + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + + astral-regex@1.0.0: + resolution: {integrity: sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==} + engines: {node: '>=4'} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + async-each@1.0.6: + resolution: {integrity: sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + async-limiter@1.0.1: + resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} + + async-retry@1.3.3: + resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} + + async@1.5.2: + resolution: {integrity: sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + + atob@2.1.2: + resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} + engines: {node: '>= 4.5.0'} + hasBin: true + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + atomically@1.7.0: + resolution: {integrity: sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==} + engines: {node: '>=10.12.0'} + + autoprefixer@10.4.27: + resolution: {integrity: sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + autoprefixer@9.8.8: + resolution: {integrity: sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==} + hasBin: true + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + avvio@7.2.5: + resolution: {integrity: sha512-AOhBxyLVdpOad3TujtC9kL/9r3HnTkxwQ5ggOsYrvvZP1cCFvzHWJd5XxZDFuTn+IN8vkKSG5SEJrd27vCSbeA==} + + aws-cdk-lib@2.189.1: + resolution: {integrity: sha512-9JU0yUr2iRTJ1oCPrHyx7hOtBDWyUfyOcdb6arlumJnMcQr2cyAMASY8HuAXHc8Y10ipVp8dRTW+J4/132IIYA==} + engines: {node: '>= 14.15.0'} + peerDependencies: + constructs: ^10.0.0 + bundledDependencies: + - '@balena/dockerignore' + - case + - fs-extra + - ignore + - jsonschema + - minimatch + - punycode + - semver + - table + - yaml + - mime-types + + aws-cdk-lib@2.50.0: + resolution: {integrity: sha512-deDbZTI7oyu3rqUyqjwhP6tnUO8MD70lE98yR65xiYty4yXBpsWKbeH3s1wNLpLAWS3hWJYyMtjZ4ZfC35NtVg==} + engines: {node: '>= 14.15.0'} + peerDependencies: + constructs: ^10.0.0 + bundledDependencies: + - '@balena/dockerignore' + - case + - fs-extra + - ignore + - jsonschema + - minimatch + - punycode + - semver + - yaml + + aws-cdk@2.50.0: + resolution: {integrity: sha512-55vmKTf2DZRqioumVfXn+S0H9oAbpRK3HFHY8EjZ5ykR5tq2+XiMWEZkYduX2HJhVAeHJJIS6h+Okk3smZjeqw==} + engines: {node: '>= 14.15.0'} + hasBin: true + + aws-sdk@2.1693.0: + resolution: {integrity: sha512-cJmb8xEnVLT+R6fBS5sn/EFJiX7tUnDaPtOPZ1vFbOJtd0fnZn/Ky2XGgsvvoeliWeH7mL3TWSX5zXXGSQV6gQ==} + engines: {node: '>= 10.0.0'} + deprecated: The AWS SDK for JavaScript (v2) has reached end-of-support, and no longer receives updates. Please migrate your code to use AWS SDK for JavaScript (v3). More info https://a.co/cUPnyil + + azure-devops-node-api@12.5.0: + resolution: {integrity: sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==} + + babel-core@7.0.0-bridge.0: + resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + babel-jest@29.7.0: + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + + babel-jest@30.3.0: + resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@babel/core': ^7.11.0 || ^8.0.0-0 + + babel-loader@8.2.5: + resolution: {integrity: sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==} + engines: {node: '>= 8.9'} + peerDependencies: + '@babel/core': ^7.0.0 + webpack: '>=2' + + babel-plugin-add-react-displayname@0.0.5: + resolution: {integrity: sha512-LY3+Y0XVDYcShHHorshrDbt4KFWL4bSeniCtl4SYZbask+Syngk1uMPCeN9+nSiZo6zX5s0RTq/J9Pnaaf/KHw==} + + babel-plugin-apply-mdx-type-prop@1.6.22: + resolution: {integrity: sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==} + peerDependencies: + '@babel/core': ^7.11.6 + + babel-plugin-emotion@10.2.2: + resolution: {integrity: sha512-SMSkGoqTbTyUTDeuVuPIWifPdUGkTk1Kf9BWRiXIOIcuyMfsdp2EjeiiFvOzX8NOBvEh/ypKYvUh2rkgAJMCLA==} + + babel-plugin-extract-import-names@1.6.22: + resolution: {integrity: sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==} + + babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + + babel-plugin-istanbul@7.0.1: + resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} + engines: {node: '>=12'} + + babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + babel-plugin-jest-hoist@30.3.0: + resolution: {integrity: sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + babel-plugin-macros@2.8.0: + resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + babel-plugin-named-asset-import@0.3.8: + resolution: {integrity: sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==} + peerDependencies: + '@babel/core': ^7.1.0 + + babel-plugin-polyfill-corejs2@0.4.17: + resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.1.7: + resolution: {integrity: sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + babel-plugin-polyfill-corejs3@0.14.2: + resolution: {integrity: sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.8: + resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-react-docgen@4.2.1: + resolution: {integrity: sha512-UQ0NmGHj/HAqi5Bew8WvNfCk8wSsmdgNd8ZdMjBCICtyCJCq9LiqgqvjCYe570/Wg7AQArSq1VQ60Dd/CHN7mQ==} + + babel-plugin-syntax-jsx@6.18.0: + resolution: {integrity: sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==} + + babel-preset-current-node-syntax@1.2.0: + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} + peerDependencies: + '@babel/core': ^7.0.0 || ^8.0.0-0 + + babel-preset-jest@29.6.3: + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + + babel-preset-jest@30.3.0: + resolution: {integrity: sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@babel/core': ^7.11.0 || ^8.0.0-beta.1 + + bail@1.0.5: + resolution: {integrity: sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + base@0.11.2: + resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==} + engines: {node: '>=0.10.0'} + + baseline-browser-mapping@2.10.13: + resolution: {integrity: sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw==} + engines: {node: '>=6.0.0'} + hasBin: true + + batch-processor@1.0.0: + resolution: {integrity: sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==} + + batch@0.6.1: + resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + + better-opn@2.1.1: + resolution: {integrity: sha512-kIPXZS5qwyKiX/HcRvDYfmBQUa8XP17I0mYZZ0y4UhpYOSvtsLHDYqmomS+Mj20aDvD3knEiQ0ecQy2nhio3yA==} + engines: {node: '>8.0.0'} + + better-opn@3.0.2: + resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==} + engines: {node: '>=12.0.0'} + + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + + binary-extensions@1.13.1: + resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==} + engines: {node: '>=0.10.0'} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + binary@0.3.0: + resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + bluebird@3.4.7: + resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} + + bluebird@3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + + bn.js@4.12.3: + resolution: {integrity: sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==} + + bn.js@5.2.3: + resolution: {integrity: sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==} + + body-parser@1.20.3: + resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + body-parser@1.20.4: + resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + body-parser@2.2.2: + resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} + engines: {node: '>=18'} + + bole@5.0.28: + resolution: {integrity: sha512-l+yybyZLV7zTD6EuGxoXsilpER1ctMCpdOqjSYNigJJma39ha85fzCtYccPx06oR1u7uCQLOcUAFFzvfXVBmuQ==} + + bonjour-service@1.3.0: + resolution: {integrity: sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + + boxen@5.1.2: + resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} + engines: {node: '>=10'} + + brace-expansion@1.1.13: + resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==} + + brace-expansion@2.0.3: + resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==} + + brace-expansion@5.0.5: + resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + engines: {node: 18 || 20 || >=22} + + braces@2.3.2: + resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==} + engines: {node: '>=0.10.0'} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + + browser-stdout@1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + + browserify-aes@1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + + browserify-cipher@1.0.1: + resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} + + browserify-des@1.0.2: + resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} + + browserify-rsa@4.1.1: + resolution: {integrity: sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==} + engines: {node: '>= 0.10'} + + browserify-sign@4.2.5: + resolution: {integrity: sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==} + engines: {node: '>= 0.10'} + + browserify-zlib@0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + + buffer-builder@0.2.0: + resolution: {integrity: sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==} + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer-indexof-polyfill@1.0.2: + resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} + engines: {node: '>=0.10'} + + buffer-xor@1.0.3: + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + + buffer@4.9.2: + resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffers@0.1.1: + resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==} + engines: {node: '>=0.2.0'} + + builtin-modules@1.1.1: + resolution: {integrity: sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==} + engines: {node: '>=0.10.0'} + + builtin-status-codes@3.0.0: + resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} + + builtins@1.0.3: + resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} + + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + + buttono@1.0.4: + resolution: {integrity: sha512-aLOeyK3zrhZnqvH6LzwIbjur8mkKhW8Xl3/jolX+RCJnGG354+L48q1SJWdky89uhQ/mBlTxY/d0x8+ciE0ZWw==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + bytestreamjs@2.0.1: + resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} + engines: {node: '>=6.0.0'} + + c8@7.14.0: + resolution: {integrity: sha512-i04rtkkcNcCf7zsQcSv/T9EbUn4RXQ6mropeMcjFOsQXQ0iGLAr/xT6TImQg4+U9hmNpN9XdvPkjUL1IzbgxJw==} + engines: {node: '>=10.12.0'} + hasBin: true + + cacache@12.0.4: + resolution: {integrity: sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==} + + cacache@15.3.0: + resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==} + engines: {node: '>= 10'} + + cache-base@1.0.1: + resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==} + engines: {node: '>=0.10.0'} + + cacheable-lookup@5.0.4: + resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} + engines: {node: '>=10.6.0'} + + cacheable-request@7.0.4: + resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-api@3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + + caniuse-lite@1.0.30001784: + resolution: {integrity: sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==} + + capture-exit@2.0.0: + resolution: {integrity: sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==} + engines: {node: 6.* || 8.* || >= 10.*} + + case-sensitive-paths-webpack-plugin@2.4.0: + resolution: {integrity: sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==} + engines: {node: '>=4'} + + ccount@1.1.0: + resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + chainsaw@0.1.0: + resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + character-entities-legacy@1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + + character-entities@1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + + character-reference-invalid@1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.0.0-rc.12: + resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} + engines: {node: '>= 6'} + + chokidar@2.1.8: + resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + ci-info@2.0.0: + resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} + + cipher-base@1.0.7: + resolution: {integrity: sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==} + engines: {node: '>= 0.10'} + + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + + cjs-module-lexer@2.2.0: + resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} + + class-utils@0.3.6: + resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} + engines: {node: '>=0.10.0'} + + clean-css@4.2.4: + resolution: {integrity: sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==} + engines: {node: '>= 4.0'} + + clean-css@5.3.3: + resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} + engines: {node: '>= 10.0'} + + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + cli-boxes@2.2.1: + resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} + engines: {node: '>=6'} + + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone-deep@4.0.1: + resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} + engines: {node: '>=6'} + + clone-response@1.0.3: + resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + + cmd-extension@1.0.2: + resolution: {integrity: sha512-iWDjmP8kvsMdBmLTHxFaqXikO8EdFRDfim7k6vUHglY/2xJ5jLrPsnQGijdfp4U+sr/BeecG0wKm02dSIAeQ1g==} + engines: {node: '>=10'} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + cockatiel@3.2.1: + resolution: {integrity: sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==} + engines: {node: '>=16'} + + code-point-at@1.1.0: + resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} + engines: {node: '>=0.10.0'} + + collapse-white-space@1.0.6: + resolution: {integrity: sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==} + + collect-v8-coverage@1.0.3: + resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} + peerDependencies: + '@types/node': '>=12' + + collection-visit@1.0.0: + resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} + engines: {node: '>=0.10.0'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + colorjs.io@0.5.2: + resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==} + + colors@1.2.5: + resolution: {integrity: sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==} + engines: {node: '>=0.1.90'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comma-separated-tokens@1.0.8: + resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + commander@6.2.1: + resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} + engines: {node: '>= 6'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + + comment-parser@1.4.1: + resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} + engines: {node: '>= 12.0.0'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + + compress-commons@4.1.2: + resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} + engines: {node: '>= 10'} + + compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + + compression@1.7.5: + resolution: {integrity: sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==} + engines: {node: '>= 0.8.0'} + + compression@1.8.1: + resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} + engines: {node: '>= 0.8.0'} + + compute-scroll-into-view@1.0.20: + resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} + + comver-to-semver@1.0.0: + resolution: {integrity: sha512-gcGtbRxjwROQOdXLUWH1fQAXqThUVRZ219aAwgtX3KfYw429/Zv6EIJRf5TBSzWdAGwePmqH7w70WTaX4MDqag==} + engines: {node: '>=12.17'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + + conf@10.2.0: + resolution: {integrity: sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==} + engines: {node: '>=12'} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + configstore@5.0.1: + resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} + engines: {node: '>=8'} + + connect-history-api-fallback@2.0.0: + resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} + engines: {node: '>=0.8'} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + console-browserify@1.2.0: + resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} + + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + + constants-browserify@1.0.0: + resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} + + constructs@10.0.130: + resolution: {integrity: sha512-9LYBePJHHnuXCr42eN0T4+O8xXHRxxak6G/UX+avt8ZZ/SNE9HFbFD8a+FKP8ixSNzzaEamDMswrMwPPTtU8cA==} + engines: {node: '>= 12.7.0'} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-disposition@1.0.1: + resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} + engines: {node: '>=18'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} + engines: {node: '>= 0.6'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + copy-concurrently@1.0.5: + resolution: {integrity: sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==} + deprecated: This package is no longer supported. + + copy-descriptor@0.1.1: + resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} + engines: {node: '>=0.10.0'} + + copy-to-clipboard@3.3.3: + resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + + core-js-compat@3.49.0: + resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} + + core-js-pure@3.49.0: + resolution: {integrity: sha512-XM4RFka59xATyJv/cS3O3Kml72hQXUeGRuuTmMYFxwzc9/7C8OYTaIR/Ji+Yt8DXzsFLNhat15cE/JP15HrCgw==} + + core-js@3.49.0: + resolution: {integrity: sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + + cosmiconfig@6.0.0: + resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} + engines: {node: '>=8'} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cp-file@7.0.0: + resolution: {integrity: sha512-0Cbj7gyvFVApzpK/uhCtQ/9kE9UnYpxMzaq5nQQC/Dh4iaj5fxp7iEFIullrYwzj8nf0qnsI1Qsx34hAeAebvw==} + engines: {node: '>=8'} + + cpy@8.1.2: + resolution: {integrity: sha512-dmC4mUesv0OYH2kNFEidtf/skUwv4zePmGeepjyyJ0qTo5+8KhA1o99oIAwVVLzQMAeDJml74d6wPPKb6EZUTg==} + engines: {node: '>=8'} + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@4.0.3: + resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} + engines: {node: '>= 10'} + + create-ecdh@4.0.4: + resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + + create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + + create-hmac@1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + + create-jest@29.7.0: + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + + create-storybook@9.1.20: + resolution: {integrity: sha512-6Y1bwGJAxdjWiIdQAoXUIbsaF5AOpaeQmmCX5AbiUbVVneODngUbxeNudOJ72nK3ebqpr563G8qNfxWrPQ8Wrw==} + hasBin: true + + cross-spawn@6.0.6: + resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==} + engines: {node: '>=4.8'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crypto-browserify@3.12.1: + resolution: {integrity: sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==} + engines: {node: '>= 0.10'} + + crypto-random-string@2.0.0: + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} + + css-declaration-sorter@6.4.1: + resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==} + engines: {node: ^10 || ^12 || >=14} + peerDependencies: + postcss: ^8.0.9 + + css-loader@3.6.0: + resolution: {integrity: sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==} + engines: {node: '>= 8.9.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + css-loader@5.2.7: + resolution: {integrity: sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.27.0 || ^5.0.0 + + css-loader@6.11.0: + resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==} + engines: {node: '>= 12.13.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + webpack: ^5.0.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + css-loader@6.6.0: + resolution: {integrity: sha512-FK7H2lisOixPT406s5gZM1S3l8GrfhEBT3ZiL2UX1Ng1XWs0y2GPllz/OTyvbaHe12VgQrIXIzuEGVlbUhodqg==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + css-minimizer-webpack-plugin@3.4.1: + resolution: {integrity: sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==} + engines: {node: '>= 12.13.0'} + peerDependencies: + '@parcel/css': '*' + clean-css: '*' + csso: '*' + esbuild: '*' + webpack: ^5.0.0 + peerDependenciesMeta: + '@parcel/css': + optional: true + clean-css: + optional: true + csso: + optional: true + esbuild: + optional: true + + css-select@4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssnano-preset-default@5.2.14: + resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + cssnano-utils@3.1.0: + resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + cssnano@5.1.15: + resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + csso@4.2.0: + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + engines: {node: '>=8.0.0'} + + cssstyle@4.6.0: + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} + engines: {node: '>=18'} + + csstype@2.6.21: + resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + cyclist@1.0.2: + resolution: {integrity: sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA==} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + date-format@4.0.14: + resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} + engines: {node: '>=4.0'} + + debounce-fn@4.0.0: + resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==} + engines: {node: '>=10'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debuglog@1.0.1: + resolution: {integrity: sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + decamelize@4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + dedent@0.7.0: + resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} + + dedent@1.7.2: + resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deep-object-diff@1.1.9: + resolution: {integrity: sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + engines: {node: '>=18'} + + default-browser@5.5.0: + resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} + engines: {node: '>=18'} + + default-gateway@6.0.3: + resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==} + engines: {node: '>= 10'} + + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + define-property@0.2.5: + resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==} + engines: {node: '>=0.10.0'} + + define-property@1.0.0: + resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} + engines: {node: '>=0.10.0'} + + define-property@2.0.2: + resolution: {integrity: sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==} + engines: {node: '>=0.10.0'} + + defu@6.1.6: + resolution: {integrity: sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + dendriform-immer-patch-optimiser@2.1.3: + resolution: {integrity: sha512-QG2IegUCdlhycVwsBOJ7SNd18PgzyWPxBivTzuF0E1KFxaU47fHy/frud74A9E66a4WXyFFp9FLLC2XQDkVj7g==} + engines: {node: '>=10'} + peerDependencies: + immer: '9' + + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dependency-path@9.2.8: + resolution: {integrity: sha512-S0OhIK7sIyAsph8hVH/LMCTDL3jozKtlrPx3dMQrlE2nAlXTquTT+AcOufphDMTQqLkfn4acvfiem9I1IWZ4jQ==} + engines: {node: '>=14.6'} + + des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + + destroy@1.0.4: + resolution: {integrity: sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detab@2.0.4: + resolution: {integrity: sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g==} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + + detect-port-alt@1.1.6: + resolution: {integrity: sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==} + engines: {node: '>= 4.2.1'} + hasBin: true + + detect-port@1.6.1: + resolution: {integrity: sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==} + engines: {node: '>= 4.0.0'} + hasBin: true + + dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + + diff-sequences@27.5.1: + resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + diff@4.0.4: + resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} + engines: {node: '>=0.3.1'} + + diff@5.2.2: + resolution: {integrity: sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==} + engines: {node: '>=0.3.1'} + + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + + diffie-hellman@5.0.3: + resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + + dir-glob@2.2.2: + resolution: {integrity: sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==} + engines: {node: '>=4'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dns-packet@5.6.1: + resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} + engines: {node: '>=6'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dom-accessibility-api@0.4.7: + resolution: {integrity: sha512-5+GzhTpCQYHz4NjL8loYTDVBnXIjNLBadWQBKxXk+osFEplLt3EsSYBu2YZcdZ8QqrvCHgW6TSMGMbmgfhrn2g==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + + dom-converter@0.2.0: + resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==} + + dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + dom-walk@0.1.2: + resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==} + + domain-browser@1.2.0: + resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==} + engines: {node: '>=0.4', npm: '>=1.2'} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + + dot-prop@6.0.1: + resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} + engines: {node: '>=10'} + + dotenv-expand@5.1.0: + resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} + + dotenv@10.0.0: + resolution: {integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==} + engines: {node: '>=10'} + + dotenv@16.4.7: + resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} + engines: {node: '>=12'} + + dotenv@8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} + + downshift@6.1.12: + resolution: {integrity: sha512-7XB/iaSJVS4T8wGFT3WRXmSF1UlBHAA40DshZtkrIscIN+VC+Lh363skLxFTvJwtNgHxAMDGEHT4xsyQFWL+UA==} + peerDependencies: + react: '>=16.12.0' + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + duplexer2@0.1.4: + resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + duplexify@3.7.1: + resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.331: + resolution: {integrity: sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==} + + element-resize-detector@1.2.4: + resolution: {integrity: sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==} + + elliptic@6.6.1: + resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} + + embla-carousel-autoplay@8.6.0: + resolution: {integrity: sha512-OBu5G3nwaSXkZCo1A6LTaFMZ8EpkYbwIaH+bPqdBnDGQ2fh4+NbzjXjs2SktoPNKCtflfVMc75njaDHOYXcrsA==} + peerDependencies: + embla-carousel: 8.6.0 + + embla-carousel-fade@8.6.0: + resolution: {integrity: sha512-qaYsx5mwCz72ZrjlsXgs1nKejSrW+UhkbOMwLgfRT7w2LtdEB03nPRI06GHuHv5ac2USvbEiX2/nAHctcDwvpg==} + peerDependencies: + embla-carousel: 8.6.0 + + embla-carousel@8.6.0: + resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==} + + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + + emoji-regex@7.0.3: + resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + + emotion-theming@10.3.0: + resolution: {integrity: sha512-mXiD2Oj7N9b6+h/dC6oLf9hwxbtKHQjoIqtodEyL8CpkN4F3V4IK/BT4D0C7zSs4BBFOu4UlPJbvvBLa88SGEA==} + peerDependencies: + '@emotion/core': ^10.0.27 + '@types/react': '>=16' + react: '>=16.3.0' + + encode-registry@3.0.1: + resolution: {integrity: sha512-6qOwkl1g0fv0DN3Y3ggr2EaZXN71aoAqPp3p/pVaWSBSIo+YjLOWN61Fva43oVyQNPf7kgm8lkudzlzojwE2jw==} + engines: {node: '>=10'} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + endent@2.1.0: + resolution: {integrity: sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w==} + + enhanced-resolve@4.5.0: + resolution: {integrity: sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==} + engines: {node: '>=6.9.0'} + + enhanced-resolve@5.20.1: + resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} + engines: {node: '>=10.13.0'} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + envinfo@7.21.0: + resolution: {integrity: sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==} + engines: {node: '>=4'} + hasBin: true + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + + errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} + engines: {node: '>= 0.4'} + + es-array-method-boxes-properly@1.0.0: + resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-get-iterator@1.1.3: + resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + + es-iterator-helpers@1.3.1: + resolution: {integrity: sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + es-toolkit@1.45.1: + resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==} + + es5-shim@4.6.7: + resolution: {integrity: sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ==} + engines: {node: '>=0.4.0'} + + es6-shim@0.35.8: + resolution: {integrity: sha512-Twf7I2v4/1tLoIXMT8HlqaBSS5H2wQTs2wx3MNYCI8K1R1/clXyCazrcVCPm/FuO9cyV8+leEaZOWD5C253NDg==} + + esbuild-android-64@0.14.54: + resolution: {integrity: sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + esbuild-android-arm64@0.14.54: + resolution: {integrity: sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + esbuild-darwin-64@0.14.54: + resolution: {integrity: sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + esbuild-darwin-arm64@0.14.54: + resolution: {integrity: sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + esbuild-freebsd-64@0.14.54: + resolution: {integrity: sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + esbuild-freebsd-arm64@0.14.54: + resolution: {integrity: sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + esbuild-linux-32@0.14.54: + resolution: {integrity: sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + esbuild-linux-64@0.14.54: + resolution: {integrity: sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + esbuild-linux-arm64@0.14.54: + resolution: {integrity: sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + esbuild-linux-arm@0.14.54: + resolution: {integrity: sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + esbuild-linux-mips64le@0.14.54: + resolution: {integrity: sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + esbuild-linux-ppc64le@0.14.54: + resolution: {integrity: sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + esbuild-linux-riscv64@0.14.54: + resolution: {integrity: sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + esbuild-linux-s390x@0.14.54: + resolution: {integrity: sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + esbuild-netbsd-64@0.14.54: + resolution: {integrity: sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + esbuild-openbsd-64@0.14.54: + resolution: {integrity: sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + esbuild-register@3.6.0: + resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} + peerDependencies: + esbuild: '>=0.12 <1' + + esbuild-runner@2.2.2: + resolution: {integrity: sha512-fRFVXcmYVmSmtYm2mL8RlUASt2TDkGh3uRcvHFOKNr/T58VrfVeKD9uT9nlgxk96u0LS0ehS/GY7Da/bXWKkhw==} + hasBin: true + peerDependencies: + esbuild: '*' + + esbuild-sunos-64@0.14.54: + resolution: {integrity: sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + esbuild-windows-32@0.14.54: + resolution: {integrity: sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + esbuild-windows-64@0.14.54: + resolution: {integrity: sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + esbuild-windows-arm64@0.14.54: + resolution: {integrity: sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + esbuild@0.14.54: + resolution: {integrity: sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.28.0: + resolution: {integrity: sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-goat@2.1.1: + resolution: {integrity: sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==} + engines: {node: '>=8'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + eslint: '*' + peerDependenciesMeta: + eslint: + optional: true + + eslint-plugin-header@3.1.1: + resolution: {integrity: sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg==} + peerDependencies: + eslint: '>=7.7.0' + + eslint-plugin-headers@1.2.1: + resolution: {integrity: sha512-1L41t3DPrXFP6YLK+sAj0xDMGVHpQwI+uGefDwc1bKP91q65AIZoXzQgI7MjZJxB6sK8/vYhXMD8x0V8xLNxJA==} + engines: {node: ^16.0.0 || >= 18.0.0} + peerDependencies: + eslint: '>=7' + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + + eslint-plugin-jsdoc@50.6.11: + resolution: {integrity: sha512-k4+MnBCGR8cuIB5MZ++FGd4gbXxjob2rX1Nq0q3nWFF4xSGZENTgTLZSjb+u9B8SAnP6lpGV2FJrBjllV3pVSg==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 + + eslint-plugin-promise@6.1.1: + resolution: {integrity: sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + + eslint-plugin-promise@7.2.1: + resolution: {integrity: sha512-SWKjd+EuvWkYaS+uN2csvj0KoP43YTu7+phKQ5v+xw6+A0gutVX2yqCeCkC3uLCJFiPfR2dD8Es5L7yUsmvEaA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react@7.33.2: + resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-plugin-tsdoc@0.3.0: + resolution: {integrity: sha512-0MuFdBrrJVBjT/gyhkP2BqpD0np1NxNLfQ38xXDlSs/KVVpKI2A6vN7jx2Rve/CyUsvOsMGwp9KKrinv7q9g3A==} + + eslint-plugin-tsdoc@0.5.2: + resolution: {integrity: sha512-BlvqjWZdBJDIPO/YU3zcPCF23CvjYT3gyu63yo6b609NNV3D1b6zceAREy2xnweuBoDpZcLNuPyAUq9cvx6bbQ==} + + eslint-scope@4.0.3: + resolution: {integrity: sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==} + engines: {node: '>=4.0.0'} + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-utils@2.1.0: + resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} + engines: {node: '>=6'} + + eslint-utils@3.0.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + + eslint-visitor-keys@1.3.0: + resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} + engines: {node: '>=4'} + + eslint-visitor-keys@2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@7.11.0: + resolution: {integrity: sha512-G9+qtYVCHaDi1ZuWzBsOWo2wSwd70TXnU6UHA3cTYHp7gCTXZcpggWFoUVAMRarg68qtPoNfFbzPh+VdOgmwmw==} + engines: {node: ^10.12.0 || >=12.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + eslint@7.30.0: + resolution: {integrity: sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg==} + engines: {node: ^10.12.0 || >=12.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + eslint@7.7.0: + resolution: {integrity: sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg==} + engines: {node: ^10.12.0 || >=12.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + eslint@8.23.1: + resolution: {integrity: sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + eslint@8.6.0: + resolution: {integrity: sha512-UvxdOJ7mXFlw7iuHZA4jmzPaUqIw54mZrv+XPYKNbKdLR0et4rf60lIZUU9kiNtnzzMzGWxMV+tQ7uG7JG8DPw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + eslint@9.25.1: + resolution: {integrity: sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + eslint@9.37.0: + resolution: {integrity: sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@7.3.1: + resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==} + engines: {node: ^10.12.0 || >=12.0.0} + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-to-babel@3.2.1: + resolution: {integrity: sha512-YNF+mZ/Wu2FU/gvmzuWtYc8rloubL7wfXCTgouFrnjGVXPA/EeYYA7pupXWrb3Iv1cTBeSSxxJIbK23l4MRNqg==} + engines: {node: '>=8.3.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + events@1.1.1: + resolution: {integrity: sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==} + engines: {node: '>=0.4.x'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + eventsource-parser@3.0.6: + resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + + evp_bytestokey@1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + + exec-sh@0.3.6: + resolution: {integrity: sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==} + + execa@1.0.0: + resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==} + engines: {node: '>=6'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exit-x@0.2.2: + resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} + engines: {node: '>= 0.8.0'} + + exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + + expand-brackets@2.1.4: + resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==} + engines: {node: '>=0.10.0'} + + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + + expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + expect@30.3.0: + resolution: {integrity: sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + express-rate-limit@7.5.1: + resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@4.21.1: + resolution: {integrity: sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==} + engines: {node: '>= 0.10.0'} + + express@4.22.1: + resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} + engines: {node: '>= 0.10.0'} + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + + extend-shallow@3.0.2: + resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==} + engines: {node: '>=0.10.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + extglob@2.0.4: + resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==} + engines: {node: '>=0.10.0'} + + extract-zip@1.7.0: + resolution: {integrity: sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==} + hasBin: true + + fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@2.2.7: + resolution: {integrity: sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==} + engines: {node: '>=4.0.0'} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-parse@1.0.3: + resolution: {integrity: sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-json-stringify@2.7.13: + resolution: {integrity: sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA==} + engines: {node: '>= 10.0.0'} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fast-wrap-ansi@0.2.0: + resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} + + fast-xml-parser@5.3.5: + resolution: {integrity: sha512-JeaA2Vm9ffQKp9VjvfzObuMCjUYAp5WDYhRYL5LrBPY/jUDlUtOvDfot0vKSkB9tuX885BDHjtw4fZadD95wnA==} + hasBin: true + + fastify-error@0.3.1: + resolution: {integrity: sha512-oCfpcsDndgnDVgiI7bwFKAun2dO+4h84vBlkWsWnz/OUK9Reff5UFoFl241xTiLeHWX/vU9zkDVXqYUxjOwHcQ==} + + fastify-warning@0.2.0: + resolution: {integrity: sha512-s1EQguBw/9qtc1p/WTY4eq9WMRIACkj+HTcOIK1in4MV5aFaQC9ZCIt0dJ7pr5bIf4lPpHvAtP2ywpTNgs7hqw==} + deprecated: This module renamed to process-warning + + fastify@3.16.2: + resolution: {integrity: sha512-tdu0fz6wk9AbtD91AbzZGjKgEQLcIy7rT2vEzTUL/zifAMS/L7ViKY9p9k3g3yCRnIQzYzxH2RAbvYZaTbKasw==} + engines: {node: '>=10.16.0'} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fault@1.0.4: + resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + + faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + figgy-pudding@3.5.2: + resolution: {integrity: sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==} + deprecated: This module is no longer supported. + + file-entry-cache@5.0.1: + resolution: {integrity: sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==} + engines: {node: '>=4'} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + file-loader@6.0.0: + resolution: {integrity: sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + file-loader@6.2.0: + resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + file-system-cache@1.1.0: + resolution: {integrity: sha512-IzF5MBq+5CR0jXx5RxPe4BICl/oEhBSXKaL9fLhAXrIfIUS77Hr4vzrYyqYMHN6uTt+BOqi3fDCTjjEBCjERKw==} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + fill-range@4.0.0: + resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==} + engines: {node: '>=0.10.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.3.1: + resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} + engines: {node: '>= 0.8'} + + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} + engines: {node: '>= 0.8'} + + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + + find-cache-dir@2.1.0: + resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==} + engines: {node: '>=6'} + + find-cache-dir@3.3.2: + resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} + engines: {node: '>=8'} + + find-my-way@4.5.1: + resolution: {integrity: sha512-kE0u7sGoUFbMXcOG/xpkmz4sRLCklERnBcg7Ftuu1iAxsfEt2S46RLJ3Sq7vshsEy2wJT2hZxE58XZK27qa8kg==} + engines: {node: '>=10'} + + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + + find-up@3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-up@7.0.0: + resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} + engines: {node: '>=18'} + + flat-cache@2.0.1: + resolution: {integrity: sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==} + engines: {node: '>=4'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + flatstr@1.0.12: + resolution: {integrity: sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==} + + flatted@2.0.2: + resolution: {integrity: sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + flow-parser@0.307.1: + resolution: {integrity: sha512-MIkG26VVtubK0OKgqY17oaMDgCIPgeEMt+XcdNho+aHldUH0uWkQ1uhf8TGxac99vOPTPpUh5OSK5LAmtXUvZQ==} + engines: {node: '>=0.4.0'} + + flush-write-stream@1.1.1: + resolution: {integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + for-in@1.0.2: + resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} + engines: {node: '>=0.10.0'} + + foreground-child@2.0.0: + resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} + engines: {node: '>=8.0.0'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fork-ts-checker-webpack-plugin@4.1.6: + resolution: {integrity: sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==} + engines: {node: '>=6.11.5', yarn: '>=1.0.0'} + + fork-ts-checker-webpack-plugin@6.5.3: + resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==} + engines: {node: '>=10', yarn: '>=1.0.0'} + peerDependencies: + eslint: '>= 6' + typescript: '>= 2.7' + vue-template-compiler: '*' + webpack: '>= 4' + peerDependenciesMeta: + eslint: + optional: true + vue-template-compiler: + optional: true + + fork-ts-checker-webpack-plugin@8.0.0: + resolution: {integrity: sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==} + engines: {node: '>=12.13.0', yarn: '>=1.0.0'} + peerDependencies: + typescript: '>3.6.0' + webpack: ^5.11.0 + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + format@0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + fragment-cache@0.2.1: + resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} + engines: {node: '>=0.10.0'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + from2@2.3.0: + resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.3.4: + resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==} + engines: {node: '>=14.14'} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs-monkey@1.0.3: + resolution: {integrity: sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==} + + fs-monkey@1.1.0: + resolution: {integrity: sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==} + + fs-write-stream-atomic@1.0.10: + resolution: {integrity: sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==} + deprecated: This package is no longer supported. + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@1.2.13: + resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==} + engines: {node: '>= 4.0'} + os: [darwin] + deprecated: Upgrade to fsevents v2 to mitigate potential security issues + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fstream@1.0.12: + resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==} + engines: {node: '>=0.6'} + deprecated: This package is no longer supported. + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functional-red-black-tree@1.0.1: + resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + fuse.js@3.6.1: + resolution: {integrity: sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw==} + engines: {node: '>=6'} + + gauge@2.7.4: + resolution: {integrity: sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==} + deprecated: This package is no longer supported. + + gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + generic-names@4.0.0: + resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-npm-tarball-url@2.1.0: + resolution: {integrity: sha512-ro+DiMu5DXgRBabqXupW38h7WPZ9+Ad8UjwhvsmmN8w1sU7ab0nzAXvVZ4kqYg57OrqomRtJvepX5/xvFKNtjA==} + engines: {node: '>=12.17'} + + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + get-port@5.1.1: + resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} + engines: {node: '>=8'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@4.1.0: + resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} + engines: {node: '>=6'} + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-value@2.0.6: + resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} + engines: {node: '>=0.10.0'} + + giget@1.2.5: + resolution: {integrity: sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug==} + hasBin: true + + git-repo-info@2.1.1: + resolution: {integrity: sha512-8aCohiDo4jwjOwma4FmYFd3i97urZulL8XL24nIPxuE+GZnfsAyy/g2Shqx6OjUiFKUXZM+Yy+KHnOmmA3FVcg==} + engines: {node: '>= 4.0'} + + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + + github-slugger@1.5.0: + resolution: {integrity: sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==} + + glob-parent@3.1.0: + resolution: {integrity: sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob-promise@3.4.0: + resolution: {integrity: sha512-q08RJ6O+eJn+dVanerAndJwIcumgbDdYiUT7zFQl3Wm1xD6fBKtah7H8ZJChj4wP+8C+QfeVy8xautR7rdmKEw==} + engines: {node: '>=4'} + peerDependencies: + glob: '*' + + glob-to-regex.js@1.2.0: + resolution: {integrity: sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + glob-to-regexp@0.3.0: + resolution: {integrity: sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig==} + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + + glob@11.1.0: + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + engines: {node: 20 || >=22} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + + glob@7.0.6: + resolution: {integrity: sha512-f8c0rE8JiCxpa52kWPAOa3ZaYEnzofDzCQLCn3Vdk0Z5OVLq3BsRFJI4S4ykpeVW6QMGBUkMeUpoEgWnMTnw5Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + global-dirs@3.0.1: + resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} + engines: {node: '>=10'} + + global@4.4.0: + resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==} + + globals@12.4.0: + resolution: {integrity: sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==} + engines: {node: '>=8'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@14.1.0: + resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} + engines: {node: '>=18'} + + globby@9.2.0: + resolution: {integrity: sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==} + engines: {node: '>=6'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + got@11.8.6: + resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} + engines: {node: '>=10.19.0'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graceful-fs@4.2.4: + resolution: {integrity: sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==} + + grapheme-splitter@1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + graphql@16.13.2: + resolution: {integrity: sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + + gzip-size@6.0.0: + resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} + engines: {node: '>=10'} + + handle-thing@2.0.1: + resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} + + handlebars@4.7.9: + resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} + engines: {node: '>=0.4.7'} + hasBin: true + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-glob@1.0.0: + resolution: {integrity: sha512-D+8A457fBShSEI3tFCj65PAbT++5sKiFtdCdOam0gnfBgw9D277OERk+HM9qYJXmdVLZ/znez10SqHN0BBQ50g==} + engines: {node: '>=0.10.0'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + + has-value@0.3.1: + resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} + engines: {node: '>=0.10.0'} + + has-value@1.0.0: + resolution: {integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==} + engines: {node: '>=0.10.0'} + + has-values@0.1.4: + resolution: {integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==} + engines: {node: '>=0.10.0'} + + has-values@1.0.0: + resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==} + engines: {node: '>=0.10.0'} + + has-yarn@2.1.0: + resolution: {integrity: sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==} + engines: {node: '>=8'} + + hash-base@3.0.5: + resolution: {integrity: sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==} + engines: {node: '>= 0.10'} + + hash-base@3.1.2: + resolution: {integrity: sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==} + engines: {node: '>= 0.8'} + + hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hast-to-hyperscript@9.0.1: + resolution: {integrity: sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==} + + hast-util-from-parse5@6.0.1: + resolution: {integrity: sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==} + + hast-util-parse-selector@2.2.5: + resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} + + hast-util-raw@6.0.1: + resolution: {integrity: sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig==} + + hast-util-to-parse5@6.0.0: + resolution: {integrity: sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ==} + + hastscript@6.0.0: + resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + + history@5.0.0: + resolution: {integrity: sha512-3NyRMKIiFSJmIPdq7FxkNMJkQ7ZEtVblOQ38VtKaA0zZMW1Eo6Q6W8oDKEflr1kNNTItSnk4JMCO1deeSgbLLg==} + + hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + + hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + + hpack.js@2.1.6: + resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + html-minifier-terser@5.1.1: + resolution: {integrity: sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==} + engines: {node: '>=6'} + hasBin: true + + html-minifier-terser@6.1.0: + resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} + engines: {node: '>=12'} + hasBin: true + + html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} + + html-void-elements@1.0.5: + resolution: {integrity: sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==} + + html-webpack-plugin@4.5.2: + resolution: {integrity: sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A==} + engines: {node: '>=6.9'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + html-webpack-plugin@5.5.4: + resolution: {integrity: sha512-3wNSaVVxdxcu0jd4FpQFoICdqgxs4zIQQvj+2yQKFfBOnLETQ6X5CDWdeasuGlSsooFlMkEioWDTqBv1wvw5Iw==} + engines: {node: '>=10.13.0'} + peerDependencies: + webpack: ^5.20.0 + + htmlparser2@6.1.0: + resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} + + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + http-deceiver@1.2.7: + resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} + + http-errors@1.8.1: + resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} + engines: {node: '>= 0.6'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + http-parser-js@0.5.10: + resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} + + http-proxy-agent@4.0.1: + resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} + engines: {node: '>= 6'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + http-proxy-middleware@2.0.9: + resolution: {integrity: sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==} + engines: {node: '>=12.0.0'} + + http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + + http2-express-bridge@1.0.7: + resolution: {integrity: sha512-bmzZSyn3nuzXRqs/+WgH7IGOQYMCIZNJeqTJ/1AoDgMPTSP5wXQCxPGsdUbGzzxwiHrMwyT4Z7t8ccbsKqiHrw==} + engines: {node: '>= 10.0.0'} + peerDependencies: + '@types/express': '*' + + http2-wrapper@1.0.3: + resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} + engines: {node: '>=10.19.0'} + + https-browserify@1.0.0: + resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} + + https-proxy-agent@4.0.0: + resolution: {integrity: sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==} + engines: {node: '>= 6.0.0'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + hyperdyperid@1.2.0: + resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} + engines: {node: '>=10.18'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + + icss-utils@4.1.1: + resolution: {integrity: sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==} + engines: {node: '>= 6'} + + icss-utils@5.1.0: + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + ieee754@1.1.13: + resolution: {integrity: sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + iferr@0.1.5: + resolution: {integrity: sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==} + + ignore-walk@5.0.1: + resolution: {integrity: sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + ignore@4.0.6: + resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} + engines: {node: '>= 4'} + + ignore@5.1.9: + resolution: {integrity: sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==} + engines: {node: '>= 4'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + + immer@11.1.4: + resolution: {integrity: sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==} + + immer@9.0.21: + resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} + + immutable@4.3.8: + resolution: {integrity: sha512-d/Ld9aLbKpNwyl0KiM2CT1WYvkitQ1TSvmRtkcV8FKStiDoA7Slzgjmb/1G2yhKM1p0XeNOieaTbFZmU1d3Xuw==} + + immutable@5.1.5: + resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-lazy@2.1.0: + resolution: {integrity: sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==} + engines: {node: '>=4'} + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} + hasBin: true + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + + individual@3.0.0: + resolution: {integrity: sha512-rUY5vtT748NMRbEMrTNiFfy29BgGZwGXUi2NFUVMWQrogSLzlJvQV9eeMWi+g1aVaQ53tpyLAQtd5x/JH0Nh1g==} + + infer-owner@1.0.4: + resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + + inline-style-parser@0.1.1: + resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + interpret@1.4.0: + resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} + engines: {node: '>= 0.10'} + + interpret@2.2.0: + resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==} + engines: {node: '>= 0.10'} + + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + + ip-address@10.1.0: + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + engines: {node: '>= 12'} + + ip@1.1.9: + resolution: {integrity: sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + ipaddr.js@2.3.0: + resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} + engines: {node: '>= 10'} + + is-absolute-url@3.0.3: + resolution: {integrity: sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==} + engines: {node: '>=8'} + + is-accessor-descriptor@1.0.1: + resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==} + engines: {node: '>= 0.10'} + + is-alphabetical@1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + + is-alphanumerical@1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-binary-path@1.0.1: + resolution: {integrity: sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==} + engines: {node: '>=0.10.0'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + + is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-ci@2.0.0: + resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==} + hasBin: true + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-descriptor@1.0.1: + resolution: {integrity: sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-decimal@1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + + is-descriptor@0.1.7: + resolution: {integrity: sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==} + engines: {node: '>= 0.4'} + + is-descriptor@1.0.3: + resolution: {integrity: sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==} + engines: {node: '>= 0.4'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-dom@1.1.0: + resolution: {integrity: sha512-u82f6mvhYxRPKpw8V1N0W8ce1xXwOrQtgGcxl6UCL5zBmZu3is/18K0rR7uFCnMDuAsS/3W54mGL4vsaFUQlEQ==} + + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + + is-extendable@1.0.1: + resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==} + engines: {node: '>=0.10.0'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@1.0.0: + resolution: {integrity: sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@2.0.0: + resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} + engines: {node: '>=4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-function@1.0.2: + resolution: {integrity: sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==} + + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@3.1.0: + resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hexadecimal@1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-installed-globally@0.4.0: + resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} + engines: {node: '>=10'} + + is-lambda@1.0.1: + resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-network-error@1.3.1: + resolution: {integrity: sha512-6QCxa49rQbmUWLfk0nuGqzql9U8uaV2H6279bRErPBHe/109hCzsLUBUHfbEtvLIHBd6hyXbgedBSHevm43Edw==} + engines: {node: '>=16'} + + is-npm@5.0.0: + resolution: {integrity: sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==} + engines: {node: '>=10'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@3.0.0: + resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-object@1.0.2: + resolution: {integrity: sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + + is-plain-obj@3.0.0: + resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} + engines: {node: '>=10'} + + is-plain-object@2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@1.1.0: + resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} + engines: {node: '>=0.10.0'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is-whitespace-character@1.0.4: + resolution: {integrity: sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==} + + is-window@1.0.2: + resolution: {integrity: sha512-uj00kdXyZb9t9RcAUAwMZAnkBUwdYGhYlt7djMXhfyhUCzwNba50tIiBKR7q0l7tdoBtFVw/3JmLY6fI3rmZmg==} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + is-word-character@1.0.4: + resolution: {integrity: sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==} + + is-wsl@1.1.0: + resolution: {integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==} + engines: {node: '>=4'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} + engines: {node: '>=16'} + + is-yarn-global@0.3.0: + resolution: {integrity: sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isobject@2.1.0: + resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} + engines: {node: '>=0.10.0'} + + isobject@3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + + isobject@4.0.0: + resolution: {integrity: sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==} + engines: {node: '>=0.10.0'} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + iterate-iterator@1.0.2: + resolution: {integrity: sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==} + + iterate-value@1.0.2: + resolution: {integrity: sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jackspeak@4.2.3: + resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} + engines: {node: 20 || >=22} + + jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-changed-files@30.3.0: + resolution: {integrity: sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-circus@30.3.0: + resolution: {integrity: sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-cli@29.7.0: + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@29.7.0: + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + + jest-config@30.3.0: + resolution: {integrity: sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@types/node': '*' + esbuild-register: '>=3.4.0' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + esbuild-register: + optional: true + ts-node: + optional: true + + jest-diff@27.5.1: + resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-diff@30.3.0: + resolution: {integrity: sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-docblock@30.2.0: + resolution: {integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-each@30.3.0: + resolution: {integrity: sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-environment-jsdom@30.3.0: + resolution: {integrity: sha512-RLEOJy6ip1lpw0yqJ8tB3i88FC7VBz7i00Zvl2qF71IdxjS98gC9/0SPWYIBVXHm5hgCYK0PAlSlnHGGy9RoMg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-environment-node@30.3.0: + resolution: {integrity: sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-get-type@27.5.1: + resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-haste-map@26.6.2: + resolution: {integrity: sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==} + engines: {node: '>= 10.14.2'} + + jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-haste-map@30.3.0: + resolution: {integrity: sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-junit@12.3.0: + resolution: {integrity: sha512-+NmE5ogsEjFppEl90GChrk7xgz8xzvF0f+ZT5AnhW6suJC93gvQtmQjfyjDnE0Z2nXJqEkxF0WXlvjG/J+wn/g==} + engines: {node: '>=10.12.0'} + + jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-leak-detector@30.3.0: + resolution: {integrity: sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-matcher-utils@27.5.1: + resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-matcher-utils@30.3.0: + resolution: {integrity: sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-message-util@30.3.0: + resolution: {integrity: sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-mock@30.3.0: + resolution: {integrity: sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@26.0.0: + resolution: {integrity: sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==} + engines: {node: '>= 10.14.2'} + + jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-regex-util@30.0.1: + resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve-dependencies@30.3.0: + resolution: {integrity: sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve@30.3.0: + resolution: {integrity: sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runner@30.3.0: + resolution: {integrity: sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runtime@30.3.0: + resolution: {integrity: sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-serializer@26.6.2: + resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==} + engines: {node: '>= 10.14.2'} + + jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-snapshot@30.3.0: + resolution: {integrity: sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-util@26.6.2: + resolution: {integrity: sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==} + engines: {node: '>= 10.14.2'} + + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-util@30.3.0: + resolution: {integrity: sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-validate@30.3.0: + resolution: {integrity: sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-watch-select-projects@2.0.0: + resolution: {integrity: sha512-j00nW4dXc2NiCW6znXgFLF9g8PJ0zP25cpQ1xRro/HU2GBfZQFZD0SoXnAlaoKkIY4MlfTMkKGbNXFpvCdjl1w==} + + jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-watcher@30.3.0: + resolution: {integrity: sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-worker@26.6.2: + resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} + engines: {node: '>= 10.13.0'} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-worker@30.3.0: + resolution: {integrity: sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest@29.3.1: + resolution: {integrity: sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + + jmespath@0.16.0: + resolution: {integrity: sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==} + engines: {node: '>= 0.6.0'} + + js-sdsl@4.4.2: + resolution: {integrity: sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==} + + js-string-escape@1.0.1: + resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} + engines: {node: '>= 0.8'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jscodeshift@0.13.1: + resolution: {integrity: sha512-lGyiEbGOvmMRKgWk4vf+lUrCWO/8YR8sUR3FKF1Cq5fovjZDlIcw3Hu5ppLHAnEXshVffvaM0eyuY/AbOeYpnQ==} + hasBin: true + peerDependencies: + '@babel/preset-env': ^7.1.6 + + jscodeshift@0.15.2: + resolution: {integrity: sha512-FquR7Okgmc4Sd0aEDwqho3rEiKR3BdvuG9jfdHjLJ6JQoWSMpavug3AoIfnfWhxFlf+5pzQh8qjqz0DWFrNQzA==} + hasBin: true + peerDependencies: + '@babel/preset-env': ^7.1.6 + peerDependenciesMeta: + '@babel/preset-env': + optional: true + + jsdoc-type-pratt-parser@4.1.0: + resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} + engines: {node: '>=12.0.0'} + + jsdom@26.1.0: + resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jsep@1.4.0: + resolution: {integrity: sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==} + engines: {node: '>= 10.16.0'} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-better-errors@1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-to-typescript@15.0.4: + resolution: {integrity: sha512-Su9oK8DR4xCmDsLlyvadkXzX6+GGXJpbhwoLtOGArAG61dvbW4YQmSEno2y66ahpIdmLMg6YUf/QHLgiwvkrHQ==} + engines: {node: '>=16.0.0'} + hasBin: true + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema-typed@7.0.3: + resolution: {integrity: sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + + jsonpath-plus@10.3.0: + resolution: {integrity: sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==} + engines: {node: '>=18.0.0'} + hasBin: true + + jsonwebtoken@9.0.3: + resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} + engines: {node: '>=12', npm: '>=6'} + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + jszip@2.7.0: + resolution: {integrity: sha512-JIsRKRVC3gTRo2vM4Wy9WBC3TRcfnIZU8k65Phi3izkvPH975FowRYtKGT6PxevA0XnJ/yO8b0QwV0ydVyQwfw==} + + jszip@3.8.0: + resolution: {integrity: sha512-cnpQrXvFSLdsR9KR5/x7zdf6c3m8IhZfZzSblFEHSqBaVwD2nvJ4CuCKLyvKvwBgZm08CgfSoiTBQLm5WW9hGw==} + + junk@3.1.0: + resolution: {integrity: sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==} + engines: {node: '>=8'} + + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} + + keyborg@2.6.0: + resolution: {integrity: sha512-o5kvLbuTF+o326CMVYpjlaykxqYP9DphFQZ2ZpgrvBouyvOxyEB7oqe8nOLFpiV5VCtz0D3pt8gXQYWpLpBnmA==} + + keytar@7.9.0: + resolution: {integrity: sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kind-of@3.2.2: + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} + engines: {node: '>=0.10.0'} + + kind-of@4.0.0: + resolution: {integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==} + engines: {node: '>=0.10.0'} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + + kysely-codegen@0.6.2: + resolution: {integrity: sha512-AWiaSQ0CBuiHsB3ubBFCf8838BaG8sypdjWi7tCJoNcAvJo1Ls8WO+1YWOi6IAjU4fBO4kG/t1rj4EnSVQwm9A==} + hasBin: true + peerDependencies: + better-sqlite3: ^7.6.2 + kysely: '>=0.19.12' + mysql2: ^2.3.3 + pg: ^8.7.3 + peerDependenciesMeta: + better-sqlite3: + optional: true + mysql2: + optional: true + pg: + optional: true + + kysely-data-api@0.1.4: + resolution: {integrity: sha512-7xgXbNuhsBAOi3PWAc5vETt0kMPCMH9qeOSsmkoVVqhvswa9v3lWUxGOQGhg9ABQqFyTbJe+JdLgd/wChIMiFw==} + peerDependencies: + aws-sdk: 2.x + kysely: 0.x + + kysely@0.21.6: + resolution: {integrity: sha512-DNecGKzzYtx2OumPJ8inrVFsSfq1lNHLFZDJvXMQxqbrTFElqq70VLR3DiK0P9fw4pB+xXTYvLiLurWiYqgk3w==} + engines: {node: '>=14.0.0'} + + latest-version@5.1.0: + resolution: {integrity: sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==} + engines: {node: '>=8'} + + launch-editor@2.13.2: + resolution: {integrity: sha512-4VVDnbOpLXy/s8rdRCSXb+zfMeFR0WlJWpET1iA9CQdlZDfwyLjUuGQzXU4VeOoey6AicSAluWan7Etga6Kcmg==} + + lazy-universal-dotenv@3.0.1: + resolution: {integrity: sha512-prXSYk799h3GY3iOWnC6ZigYzMPjxN2svgjJ9shk7oMadSNX3wXy0B6F32PMJv7qtMnrIbUxoEHzbutvxR2LBQ==} + engines: {node: '>=6.0.0', npm: '>=6.0.0', yarn: '>=1.0.0'} + + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + + light-my-request@4.12.0: + resolution: {integrity: sha512-0y+9VIfJEsPVzK5ArSIJ8Dkxp8QMP7/aCuxCUtG/tr9a2NoOf/snATE/OUc05XUplJCEnRh6gTkH7xh9POt1DQ==} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + + listenercount@1.0.1: + resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==} + + load-json-file@6.2.0: + resolution: {integrity: sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==} + engines: {node: '>=8'} + + loader-runner@2.4.0: + resolution: {integrity: sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==} + engines: {node: '>=4.3.0 <5.0.0 || >=5.10'} + + loader-runner@4.3.1: + resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} + engines: {node: '>=6.11.5'} + + loader-utils@1.4.2: + resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==} + engines: {node: '>=4.0.0'} + + loader-utils@2.0.4: + resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} + engines: {node: '>=8.9.0'} + + loader-utils@3.3.1: + resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} + engines: {node: '>= 12.13.0'} + + locate-path@3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.difference@4.5.0: + resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} + + lodash.flatten@4.4.0: + resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} + + lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + + lodash.union@4.6.0: + resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log4js@6.9.1: + resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==} + engines: {node: '>=8.0'} + + long@4.0.0: + resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lowercase-keys@2.0.0: + resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} + engines: {node: '>=8'} + + lowlight@1.20.0: + resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@11.2.7: + resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-fetch-happen@8.0.14: + resolution: {integrity: sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ==} + engines: {node: '>= 10'} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + + map-age-cleaner@0.1.3: + resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==} + engines: {node: '>=6'} + + map-cache@0.2.2: + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} + engines: {node: '>=0.10.0'} + + map-or-similar@1.5.0: + resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==} + + map-visit@1.0.0: + resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==} + engines: {node: '>=0.10.0'} + + markdown-escapes@1.0.4: + resolution: {integrity: sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==} + + markdown-it@14.1.1: + resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==} + hasBin: true + + markdown-to-jsx@7.7.17: + resolution: {integrity: sha512-7mG/1feQ0TX5I7YyMZVDgCC/y2I3CiEhIRQIhyov9nGBP5eoVrOXXHuL5ZP8GRfxVZKRiXWJgwXkb9It+nQZfQ==} + engines: {node: '>= 10'} + peerDependencies: + react: '>= 0.14.0' + peerDependenciesMeta: + react: + optional: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + + mdast-squeeze-paragraphs@4.0.0: + resolution: {integrity: sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ==} + + mdast-util-definitions@4.0.0: + resolution: {integrity: sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==} + + mdast-util-to-hast@10.0.1: + resolution: {integrity: sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==} + + mdast-util-to-string@1.1.0: + resolution: {integrity: sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==} + + mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + + mdurl@1.0.1: + resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} + + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + mem@8.1.1: + resolution: {integrity: sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==} + engines: {node: '>=10'} + + memfs@3.4.3: + resolution: {integrity: sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==} + engines: {node: '>= 4.0.0'} + + memfs@3.5.3: + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} + engines: {node: '>= 4.0.0'} + + memfs@4.12.0: + resolution: {integrity: sha512-74wDsex5tQDSClVkeK1vtxqYCAgCoXxx+K4NSHzgU/muYVYByFqa+0RnrPO9NM6naWm1+G9JmZ0p6QHhXmeYfA==} + engines: {node: '>= 4.0.0'} + + memfs@4.57.1: + resolution: {integrity: sha512-WvzrWPwMQT+PtbX2Et64R4qXKK0fj/8pO85MrUCzymX3twwCiJCdvntW3HdhG1teLJcHDDLIKx5+c3HckWYZtQ==} + + memoizerific@1.11.3: + resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==} + + memory-fs@0.4.1: + resolution: {integrity: sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==} + + memory-fs@0.5.0: + resolution: {integrity: sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==} + engines: {node: '>=4.3.0 <5.0.0 || >=5.10'} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + microevent.ts@0.1.1: + resolution: {integrity: sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==} + + micromatch@3.1.10: + resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==} + engines: {node: '>=0.10.0'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + miller-rabin@4.0.1: + resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} + hasBin: true + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-fn@3.1.0: + resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==} + engines: {node: '>=8'} + + mimic-response@1.0.1: + resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} + engines: {node: '>=4'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + min-document@2.19.2: + resolution: {integrity: sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + mini-css-extract-plugin@2.5.3: + resolution: {integrity: sha512-YseMB8cs8U/KCaAGQoqYmfUuhhGW0a9p9XvWXrxVOkE3/IiISTLw4ALNt7JR5B2eYauFM+PQGSbXMDmVbR7Tfw==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + + minimatch@10.2.3: + resolution: {integrity: sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + + minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass-collect@1.0.2: + resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} + engines: {node: '>= 8'} + + minipass-fetch@1.4.1: + resolution: {integrity: sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==} + engines: {node: '>=8'} + + minipass-flush@1.0.7: + resolution: {integrity: sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + + mississippi@3.0.0: + resolution: {integrity: sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==} + engines: {node: '>=4.0.0'} + + mixin-deep@1.3.2: + resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==} + engines: {node: '>=0.10.0'} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + + mocha@10.8.2: + resolution: {integrity: sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==} + engines: {node: '>= 14.0.0'} + hasBin: true + + move-concurrently@1.0.1: + resolution: {integrity: sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==} + deprecated: This package is no longer supported. + + mrmime@1.0.1: + resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} + engines: {node: '>=10'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multicast-dns@7.2.5: + resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} + hasBin: true + + mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nan@2.26.2: + resolution: {integrity: sha512-0tTvBTYkt3tdGw22nrAy50x7gpbGCCFH3AFcyS5WiUu7Eu4vWlri1woE6qHBSfy11vksDqkiwjOnlR7WV8G1Hw==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanomatch@1.2.13: + resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} + engines: {node: '>=0.10.0'} + + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + ndjson@2.0.0: + resolution: {integrity: sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ==} + engines: {node: '>=10'} + hasBin: true + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + nested-error-stacks@2.1.1: + resolution: {integrity: sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==} + + nice-try@1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-abi@3.89.0: + resolution: {integrity: sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==} + engines: {node: '>=10'} + + node-abort-controller@3.1.1: + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + + node-addon-api@3.2.1: + resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} + + node-addon-api@4.3.0: + resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==} + + node-dir@0.1.17: + resolution: {integrity: sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==} + engines: {node: '>= 0.10.5'} + + node-exports-info@1.6.0: + resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} + engines: {node: '>= 0.4'} + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-forge@1.4.0: + resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} + engines: {node: '>= 6.13.0'} + + node-gyp@8.1.0: + resolution: {integrity: sha512-o2elh1qt7YUp3lkMwY3/l4KF3j/A3fI/Qt4NH+CQQgPJdqGE9y7qnP84cjIWN27Q0jJkrSAhCVDg+wBVNBYdBg==} + engines: {node: '>= 10.12.0'} + hasBin: true + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-libs-browser@2.2.1: + resolution: {integrity: sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==} + + node-releases@2.0.37: + resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==} + + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + + normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + + normalize-package-data@3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + + normalize-path@2.1.1: + resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} + engines: {node: '>=0.10.0'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + + npm-bundled@2.0.1: + resolution: {integrity: sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + npm-normalize-package-bin@1.0.1: + resolution: {integrity: sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==} + + npm-normalize-package-bin@2.0.0: + resolution: {integrity: sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + npm-package-arg@6.1.1: + resolution: {integrity: sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==} + + npm-packlist@5.1.3: + resolution: {integrity: sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true + + npm-run-path@2.0.2: + resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} + engines: {node: '>=4'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + npmlog@4.1.2: + resolution: {integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==} + deprecated: This package is no longer supported. + + npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + num2fraction@1.2.2: + resolution: {integrity: sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==} + + number-is-nan@1.0.1: + resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} + engines: {node: '>=0.10.0'} + + nwsapi@2.2.23: + resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==} + + nypm@0.5.4: + resolution: {integrity: sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA==} + engines: {node: ^14.16.0 || >=16.10.0} + hasBin: true + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-copy@0.1.0: + resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object-visit@1.0.1: + resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==} + engines: {node: '>=0.10.0'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.getownpropertydescriptors@2.1.9: + resolution: {integrity: sha512-mt8YM6XwsTTovI+kdZdHSxoyF2DI59up034orlC9NfweclcWOt7CVascNNLp6U+bjFVCVCIh9PwS76tDM/rH8g==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.hasown@1.1.4: + resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} + engines: {node: '>= 0.4'} + + object.pick@1.3.0: + resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} + engines: {node: '>=0.10.0'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + objectorarray@1.0.5: + resolution: {integrity: sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg==} + + obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + + on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.0.2: + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} + + on-headers@1.1.0: + resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + + open@7.4.2: + resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} + engines: {node: '>=8'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + opener@1.5.2: + resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} + hasBin: true + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + os-browserify@0.3.0: + resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} + + os-homedir@1.0.2: + resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} + engines: {node: '>=0.10.0'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + osenv@0.1.5: + resolution: {integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==} + deprecated: This package is no longer supported. + + overlayscrollbars@1.13.3: + resolution: {integrity: sha512-1nB/B5kaakJuHXaLXLRK0bUIilWhUGT6q5g+l2s5vqYdLle/sd0kscBHkQC1kuuDg9p9WR4MTdySDOPbeL/86g==} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-all@2.1.0: + resolution: {integrity: sha512-HbZxz5FONzz/z2gJfk6bFca0BCiSRF8jU3yCsWOen/vR6lZjfPOu/e7L3uFzTW1i0H8TlC3vqQstEJPQL4/uLA==} + engines: {node: '>=6'} + + p-cancelable@2.1.1: + resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} + engines: {node: '>=8'} + + p-defer@1.0.0: + resolution: {integrity: sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==} + engines: {node: '>=4'} + + p-event@4.2.0: + resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} + engines: {node: '>=8'} + + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + + p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-map@3.0.0: + resolution: {integrity: sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==} + engines: {node: '>=8'} + + p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + + p-reflect@2.1.0: + resolution: {integrity: sha512-paHV8NUz8zDHu5lhr/ngGWQiW067DK/+IbJ+RfZ4k+s8y4EKyYCz8pGYWjxCg35eHztpJAt+NUgvN4L+GCbPlg==} + engines: {node: '>=8'} + + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} + + p-retry@6.2.1: + resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} + engines: {node: '>=16.17'} + + p-settle@4.1.1: + resolution: {integrity: sha512-6THGh13mt3gypcNMm0ADqVNCcYa3BK6DWsuJWFCuEKP1rpY+OKGp7gaZwVmLspmic01+fsg/fN57MfvDzZ/PuQ==} + engines: {node: '>=10'} + + p-timeout@3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + package-json@7.0.0: + resolution: {integrity: sha512-CHJqc94AA8YfSLHGQT3DbvSIuE12NLFekpM4n7LRrAd3dOJtA911+4xe9q6nC3/jcKraq7nNS9VxgtT0KC+diA==} + engines: {node: '>=12'} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + parallel-transform@1.2.0: + resolution: {integrity: sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==} + + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-asn1@5.1.9: + resolution: {integrity: sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==} + engines: {node: '>= 0.10'} + + parse-entities@2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + + parse-imports-exports@0.2.4: + resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-semver@1.1.1: + resolution: {integrity: sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==} + + parse-statements@1.0.11: + resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} + + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + + pascalcase@0.1.1: + resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} + engines: {node: '>=0.10.0'} + + path-browserify@0.0.1: + resolution: {integrity: sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==} + + path-dirname@1.0.2: + resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==} + + path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-name@1.0.0: + resolution: {integrity: sha512-/dcAb5vMXH0f51yvMuSUqFpxUcA8JelbRmE5mW/p4CUJxrNgK24IkstnV7ENtg2IDGBOu6izKTG6eilbnbNKWQ==} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + + path-to-regexp@0.1.10: + resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} + + path-to-regexp@0.1.13: + resolution: {integrity: sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==} + + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + + path-type@3.0.0: + resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} + engines: {node: '>=4'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + path-type@6.0.0: + resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} + engines: {node: '>=18'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + pbkdf2@3.1.5: + resolution: {integrity: sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==} + engines: {node: '>= 0.10'} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + + picocolors@0.2.1: + resolution: {integrity: sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pino-std-serializers@3.2.0: + resolution: {integrity: sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==} + + pino@6.14.0: + resolution: {integrity: sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==} + hasBin: true + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + + pkg-dir@3.0.0: + resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==} + engines: {node: '>=6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + pkg-dir@5.0.0: + resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} + engines: {node: '>=10'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-up@3.1.0: + resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} + engines: {node: '>=8'} + + pkijs@3.4.0: + resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==} + engines: {node: '>=16.0.0'} + + playwright-core@1.56.1: + resolution: {integrity: sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.56.1: + resolution: {integrity: sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==} + engines: {node: '>=18'} + hasBin: true + + pnp-webpack-plugin@1.6.4: + resolution: {integrity: sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==} + engines: {node: '>=6'} + + pnpm-sync-lib@0.3.4: + resolution: {integrity: sha512-ZgRR+j6B+VUrolPBswPvXBnCyxg39Zfw3ShNCTuCrOFG1V29V4EyXaA1rDDMjdhpF85QYp2NEUjeHAm02A2E/A==} + + polished@4.3.1: + resolution: {integrity: sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==} + engines: {node: '>=10'} + + posix-character-classes@0.1.1: + resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==} + engines: {node: '>=0.10.0'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss-calc@8.2.4: + resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} + peerDependencies: + postcss: ^8.2.2 + + postcss-colormin@5.3.1: + resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-convert-values@5.1.3: + resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-comments@5.1.2: + resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-duplicates@5.1.0: + resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-empty@5.1.1: + resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-overridden@5.1.0: + resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-flexbugs-fixes@4.2.1: + resolution: {integrity: sha512-9SiofaZ9CWpQWxOwRh1b/r85KD5y7GgvsNt1056k6OYLvWUun0czCvogfJgylC22uJTwW1KzY3Gz65NZRlvoiQ==} + + postcss-loader@4.1.0: + resolution: {integrity: sha512-vbCkP70F3Q9PIk6d47aBwjqAMI4LfkXCoyxj+7NPNuVIwfTGdzv2KVQes59/RuxMniIgsYQCFSY42P3+ykJfaw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + postcss: ^7.0.0 || ^8.0.1 + webpack: ^4.0.0 || ^5.0.0 + + postcss-loader@4.3.0: + resolution: {integrity: sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==} + engines: {node: '>= 10.13.0'} + peerDependencies: + postcss: ^7.0.0 || ^8.0.1 + webpack: ^4.0.0 || ^5.0.0 + + postcss-loader@6.2.1: + resolution: {integrity: sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==} + engines: {node: '>= 12.13.0'} + peerDependencies: + postcss: ^7.0.0 || ^8.0.1 + webpack: ^5.0.0 + + postcss-merge-longhand@5.1.7: + resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-merge-rules@5.1.4: + resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-font-values@5.1.0: + resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-gradients@5.1.1: + resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-params@5.1.4: + resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-selectors@5.2.1: + resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-modules-extract-imports@2.0.0: + resolution: {integrity: sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==} + engines: {node: '>= 6'} + + postcss-modules-extract-imports@3.1.0: + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-local-by-default@3.0.3: + resolution: {integrity: sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==} + engines: {node: '>= 6'} + + postcss-modules-local-by-default@4.2.0: + resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-scope@2.2.0: + resolution: {integrity: sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==} + engines: {node: '>= 6'} + + postcss-modules-scope@3.2.1: + resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-values@3.0.0: + resolution: {integrity: sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==} + + postcss-modules-values@4.0.0: + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules@6.0.1: + resolution: {integrity: sha512-zyo2sAkVvuZFFy0gc2+4O+xar5dYlaVy/ebO24KT0ftk/iJevSNyPyQellsBLlnccwh7f6V6Y4GvuKRYToNgpQ==} + peerDependencies: + postcss: ^8.0.0 + + postcss-normalize-charset@5.1.0: + resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-display-values@5.1.0: + resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-positions@5.1.1: + resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-repeat-style@5.1.1: + resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-string@5.1.0: + resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-timing-functions@5.1.0: + resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-unicode@5.1.1: + resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-url@5.1.0: + resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-whitespace@5.1.1: + resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-ordered-values@5.1.3: + resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-reduce-initial@5.1.2: + resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-reduce-transforms@5.1.0: + resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-selector-parser@7.1.1: + resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} + engines: {node: '>=4'} + + postcss-svgo@5.1.0: + resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-unique-selectors@5.1.1: + resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@7.0.39: + resolution: {integrity: sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==} + engines: {node: '>=6.0.0'} + + postcss@8.4.49: + resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.12: + resolution: {integrity: sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==} + engines: {node: ^10 || ^12 || >=14} + + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. + hasBin: true + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@2.3.0: + resolution: {integrity: sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==} + engines: {node: '>=10.13.0'} + hasBin: true + + prettier@3.8.1: + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + engines: {node: '>=14'} + hasBin: true + + pretty-error@2.1.2: + resolution: {integrity: sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==} + + pretty-error@4.0.0: + resolution: {integrity: sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==} + + pretty-format@25.5.0: + resolution: {integrity: sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==} + engines: {node: '>= 8.3'} + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + pretty-format@30.3.0: + resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + pretty-hrtime@1.0.3: + resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} + engines: {node: '>= 0.8'} + + prism-react-renderer@2.4.1: + resolution: {integrity: sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==} + peerDependencies: + react: '>=16.0.0' + + prismjs@1.27.0: + resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} + engines: {node: '>=6'} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + private@0.1.8: + resolution: {integrity: sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==} + engines: {node: '>= 0.6'} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process-warning@1.0.0: + resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + promise-inflight@1.0.1: + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + + promise.allsettled@1.0.7: + resolution: {integrity: sha512-hezvKvQQmsFkOdrZfYxUxkyxl8mgFQeT259Ajj9PXdbg9VzBCWrItOev72JyWxkCD5VSSqAeHmlN3tWx4DlmsA==} + engines: {node: '>= 0.4'} + + promise.prototype.finally@3.1.8: + resolution: {integrity: sha512-aVDtsXOml9iuMJzUco9J1je/UrIT3oMYfWkCTiUhkt+AvZw72q4dUZnR/R/eB3h5GeAagQVXvM1ApoYniJiwoA==} + engines: {node: '>= 0.4'} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + property-information@5.6.0: + resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + + pseudolocale@1.1.0: + resolution: {integrity: sha512-OZ8I/hwYEJ3beN3IEcNnt8EpcqblH0/x23hulKBXjs+WhTTEle+ijCHCkh2bd+cIIeCuCwSCbBe93IthGG6hLw==} + + public-encrypt@4.0.3: + resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + + pump@2.0.1: + resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==} + + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + + pumpify@1.5.1: + resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==} + + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + + punycode@1.3.2: + resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==} + + punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pupa@2.1.1: + resolution: {integrity: sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==} + engines: {node: '>=8'} + + puppeteer-core@2.1.1: + resolution: {integrity: sha512-n13AWriBMPYxnpbb6bnaY5YoY6rGj8vPLrz6CZF3o0qJNEwlcfJVxBzYZ0NJsQ21UbdJoijPCDrM++SUVEz7+w==} + engines: {node: '>=8.16.0'} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + + pure-rand@7.0.1: + resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} + + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.1.5: + resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} + engines: {node: '>=16.0.0'} + + q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + deprecated: |- + You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. + + (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + qs@6.14.2: + resolution: {integrity: sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==} + engines: {node: '>=0.6'} + + qs@6.15.0: + resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} + engines: {node: '>=0.6'} + + querystring-es3@0.2.1: + resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} + engines: {node: '>=0.4.x'} + + querystring@0.2.0: + resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==} + engines: {node: '>=0.4.x'} + deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + ramda@0.27.2: + resolution: {integrity: sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA==} + + ramda@0.28.0: + resolution: {integrity: sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + randomfill@1.0.4: + resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + + raw-body@2.5.3: + resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} + engines: {node: '>= 0.8'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + + raw-loader@4.0.2: + resolution: {integrity: sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + react-colorful@5.6.1: + resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + react-docgen-typescript@2.4.0: + resolution: {integrity: sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==} + peerDependencies: + typescript: '>= 4.3.x' + + react-docgen@5.4.3: + resolution: {integrity: sha512-xlLJyOlnfr8lLEEeaDZ+X2J/KJoe6Nr9AzxnkdQWush5hz2ZSu66w6iLMOScMmxoSHWpWMn+k3v5ZiyCfcWsOA==} + engines: {node: '>=8.10.0'} + hasBin: true + + react-docgen@7.1.1: + resolution: {integrity: sha512-hlSJDQ2synMPKFZOsKo9Hi8WWZTC7POR8EmWvTSjow+VDgKzkmjQvFm2fk0tmRw+f0vTOIYKlarR0iL4996pdg==} + engines: {node: '>=16.14.0'} + + react-dom@17.0.2: + resolution: {integrity: sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==} + peerDependencies: + react: 17.0.2 + + react-dom@19.2.4: + resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} + peerDependencies: + react: ^19.2.4 + + react-draggable@4.5.0: + resolution: {integrity: sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + + react-element-to-jsx-string@14.3.4: + resolution: {integrity: sha512-t4ZwvV6vwNxzujDQ+37bspnLwA4JlgUPWhLjBJWsNIDceAf6ZKUTCjdm08cN6WeZ5pTMKiCJkmAYnpmR4Bm+dg==} + peerDependencies: + react: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 + react-dom: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 + + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-helmet-async@1.3.0: + resolution: {integrity: sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==} + peerDependencies: + react: ^16.6.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0 + + react-hook-form@7.69.0: + resolution: {integrity: sha512-yt6ZGME9f4F6WHwevrvpAjh42HMvocuSnSIHUGycBqXIJdhqGSPQzTpGF+1NLREk/58IdPxEMfPcFCjlMhclGw==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + + react-inspector@5.1.1: + resolution: {integrity: sha512-GURDaYzoLbW8pMGXwYPDBIv6nqei4kK7LPRZ9q9HCZF54wqXz/dnylBp/kfE9XmekBhHvLDdcYeyIwSrvtOiWg==} + peerDependencies: + react: ^16.8.4 || ^17.0.0 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-popper-tooltip@3.1.1: + resolution: {integrity: sha512-EnERAnnKRptQBJyaee5GJScWNUKQPDD2ywvzZyUjst/wj5U64C8/CnSYLNEmP2hG0IJ3ZhtDxE8oDN+KOyavXQ==} + peerDependencies: + react: ^16.6.0 || ^17.0.0 + react-dom: ^16.6.0 || ^17.0.0 + + react-popper@2.3.0: + resolution: {integrity: sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==} + peerDependencies: + '@popperjs/core': ^2.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + + react-redux@9.2.0: + resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + + react-refresh@0.11.0: + resolution: {integrity: sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==} + engines: {node: '>=0.10.0'} + + react-router-dom@6.30.3: + resolution: {integrity: sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': '>=16' + react: '>=16.8' + react-dom: '>=16.8' + + react-router@6.30.3: + resolution: {integrity: sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': '>=16' + react: '>=16.8' + + react-sizeme@3.0.2: + resolution: {integrity: sha512-xOIAOqqSSmKlKFJLO3inBQBdymzDuXx4iuwkNcJmC96jeiOg5ojByvL+g3MW9LPEsojLbC6pf68zOfobK8IPlw==} + + react-syntax-highlighter@13.5.3: + resolution: {integrity: sha512-crPaF+QGPeHNIblxxCdf2Lg936NAHKhNhuMzRL3F9ct6aYXL3NcZtCL0Rms9+qVo6Y1EQLdXGypBNSbPL/r+qg==} + peerDependencies: + react: '>= 0.14.0' + + react-textarea-autosize@8.5.9: + resolution: {integrity: sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react@17.0.2: + resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==} + engines: {node: '>=0.10.0'} + + react@19.2.4: + resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} + engines: {node: '>=0.10.0'} + + read-package-json@2.1.2: + resolution: {integrity: sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==} + deprecated: This package is no longer supported. Please use @npmcli/package-json instead. + + read-package-tree@5.1.6: + resolution: {integrity: sha512-FCX1aT3GWyY658wzDICef4p+n0dB+ENRct8E/Qyvppj6xVpOYerBHfUu7OP5Rt1/393Tdglguf5ju5DEX4wZNg==} + deprecated: The functionality that this package provided is now in @npmcli/arborist + + read-pkg-up@7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + + read-pkg@5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + + read-yaml-file@2.1.0: + resolution: {integrity: sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==} + engines: {node: '>=10.13'} + + read@1.0.7: + resolution: {integrity: sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==} + engines: {node: '>=0.8'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + + readdir-scoped-modules@1.1.0: + resolution: {integrity: sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==} + deprecated: This functionality has been moved to @npmcli/fs + + readdirp@2.2.1: + resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==} + engines: {node: '>=0.10'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + recast@0.19.1: + resolution: {integrity: sha512-8FCjrBxjeEU2O6I+2hyHyBFH1siJbMBLwIRvVr1T3FD2cL754sOaJDsJ/8h3xYltasbJ8jqWRIhMuDGBSiSbjw==} + engines: {node: '>= 4'} + + recast@0.20.5: + resolution: {integrity: sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ==} + engines: {node: '>= 4'} + + recast@0.23.11: + resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} + engines: {node: '>= 4'} + + rechoir@0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} + engines: {node: '>= 0.10'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + redux-thunk@3.1.0: + resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} + peerDependencies: + redux: ^5.0.0 + + redux@4.2.1: + resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + refractor@3.6.0: + resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} + + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + + regex-not@1.0.2: + resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==} + engines: {node: '>=0.10.0'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + regexpp@3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + registry-auth-token@4.2.2: + resolution: {integrity: sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==} + engines: {node: '>=6.0.0'} + + registry-url@5.1.0: + resolution: {integrity: sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==} + engines: {node: '>=8'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.0: + resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} + hasBin: true + + relateurl@0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + + remark-external-links@8.0.0: + resolution: {integrity: sha512-5vPSX0kHoSsqtdftSHhIYofVINC8qmp0nctkeU9YoJwV3YfiBRiI6cbFRJ0oI/1F9xS+bopXG0m2KS8VFscuKA==} + + remark-footnotes@2.0.0: + resolution: {integrity: sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==} + + remark-mdx@1.6.22: + resolution: {integrity: sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==} + + remark-parse@8.0.3: + resolution: {integrity: sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==} + + remark-slug@6.1.0: + resolution: {integrity: sha512-oGCxDF9deA8phWvxFuyr3oSJsdyUAxMFbA0mZ7Y1Sas+emILtO+e5WutF9564gDsEN4IXaQXm5pFo6MLH+YmwQ==} + + remark-squeeze-paragraphs@4.0.0: + resolution: {integrity: sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==} + + remeda@0.0.32: + resolution: {integrity: sha512-FEdl8ONpqY7AvvMHG5WYdomc0mGf2khHPUDu6QvNkOq4Wjkw5BvzWM4QyksAQ/US1sFIIRG8TVBn6iJx6HbRrA==} + + remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + + renderkid@2.0.7: + resolution: {integrity: sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==} + + renderkid@3.0.0: + resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==} + + repeat-element@1.1.4: + resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==} + engines: {node: '>=0.10.0'} + + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + + resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-url@0.2.1: + resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} + deprecated: https://github.com/lydell/resolve-url#deprecated + + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.6: + resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} + engines: {node: '>= 0.4'} + hasBin: true + + responselike@2.0.1: + resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} + + ret@0.1.15: + resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} + engines: {node: '>=0.12'} + + ret@0.2.2: + resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} + engines: {node: '>=4'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfc4648@1.5.4: + resolution: {integrity: sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@2.6.3: + resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + ripemd160@2.0.3: + resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} + engines: {node: '>= 0.8'} + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + + rsvp@4.8.5: + resolution: {integrity: sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==} + engines: {node: 6.* || >= 7.*} + + rtl-css-js@1.16.1: + resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} + + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + run-queue@1.0.3: + resolution: {integrity: sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==} + + rxjs@6.6.7: + resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} + engines: {npm: '>=2.0.0'} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-execa@0.1.2: + resolution: {integrity: sha512-vdTshSQ2JsRCgT8eKZWNJIL26C6bVqy1SOmuCMlKHegVeo8KYRobRrefOdUq9OozSPUUiSxrylteeRmLOMFfWg==} + engines: {node: '>=12'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safe-regex2@2.0.0: + resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} + + safe-regex@1.1.0: + resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sane@4.1.0: + resolution: {integrity: sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==} + engines: {node: 6.* || 8.* || >= 10.*} + deprecated: some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added + hasBin: true + + sass-embedded-android-arm64@1.85.1: + resolution: {integrity: sha512-27oRheqNA3SJM2hAxpVbs7mCKUwKPWmEEhyiNFpBINb5ELVLg+Ck5RsGg+SJmo130ul5YX0vinmVB5uPWc8X5w==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [android] + + sass-embedded-android-arm@1.85.1: + resolution: {integrity: sha512-GkcgUGMZtEF9gheuE1dxCU0ZSAifuaFXi/aX7ZXvjtdwmTl9Zc/OHR9oiUJkc8IW9UI7H8TuwlTAA8+SwgwIeQ==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [android] + + sass-embedded-android-ia32@1.85.1: + resolution: {integrity: sha512-f3x16NyRgtXFksIaO/xXKrUhttUBv8V0XsAR2Dhdb/yz4yrDrhzw9Wh8fmw7PlQqECcQvFaoDr3XIIM6lKzasw==} + engines: {node: '>=14.0.0'} + cpu: [ia32] + os: [android] + + sass-embedded-android-riscv64@1.85.1: + resolution: {integrity: sha512-IP6OijpJ8Mqo7XqCe0LsuZVbAxEFVboa0kXqqR5K55LebEplsTIA2GnmRyMay3Yr/2FVGsZbCb6Wlgkw23eCiA==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [android] + + sass-embedded-android-x64@1.85.1: + resolution: {integrity: sha512-Mh7CA53wR3ADvXAYipFc/R3vV4PVOzoKwWzPxmq+7i8UZrtsVjKONxGtqWe9JG1mna0C9CRZAx0sv/BzbOJxWg==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [android] + + sass-embedded-darwin-arm64@1.85.1: + resolution: {integrity: sha512-msWxzhvcP9hqGVegxVePVEfv9mVNTlUgGr6k7O7Ihji702mbtrH/lKwF4aRkkt4g1j7tv10+JtQXmTNi/pi9kA==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [darwin] + + sass-embedded-darwin-x64@1.85.1: + resolution: {integrity: sha512-J4UFHUiyI9Z+mwYMwz11Ky9TYr3hY1fCxeQddjNGL/+ovldtb0yAIHvoVM0BGprQDm5JqhtUk8KyJ3RMJqpaAA==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [darwin] + + sass-embedded-linux-arm64@1.85.1: + resolution: {integrity: sha512-jGadetB03BMFG2rq3OXub/uvC/lGpbQOiLGEz3NLb2nRZWyauRhzDtvZqkr6BEhxgIWtMtz2020yD8ZJSw/r2w==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [linux] + + sass-embedded-linux-arm@1.85.1: + resolution: {integrity: sha512-X0fDh95nNSw1wfRlnkE4oscoEA5Au4nnk785s9jghPFkTBg+A+5uB6trCjf0fM22+Iw6kiP4YYmDdw3BqxAKLQ==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [linux] + + sass-embedded-linux-ia32@1.85.1: + resolution: {integrity: sha512-7HlYY90d9mitDtNi5s+S+5wYZrTVbkBH2/kf7ixrzh2BFfT0YM81UHLJRnGX93y9aOMBL6DSZAIfkt1RsV9bkQ==} + engines: {node: '>=14.0.0'} + cpu: [ia32] + os: [linux] + + sass-embedded-linux-musl-arm64@1.85.1: + resolution: {integrity: sha512-FLkIT0p18XOkR6wryJ13LqGBDsrYev2dRk9dtiU18NCpNXruKsdBQ1ZnWHVKB3h1dA9lFyEEisC0sooKdNfeOQ==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [linux] + + sass-embedded-linux-musl-arm@1.85.1: + resolution: {integrity: sha512-5vcdEqE8QZnu6i6shZo7x2N36V7YUoFotWj2rGekII5ty7Nkaj+VtZhUEOp9tAzEOlaFuDp5CyO1kUCvweT64A==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [linux] + + sass-embedded-linux-musl-ia32@1.85.1: + resolution: {integrity: sha512-N1093T84zQJor1yyIAdYScB5eAuQarGK1tKgZ4uTnxVlgA7Xi1lXV8Eh7ox9sDqKCaWkVQ3MjqU26vYRBeRWyw==} + engines: {node: '>=14.0.0'} + cpu: [ia32] + os: [linux] + + sass-embedded-linux-musl-riscv64@1.85.1: + resolution: {integrity: sha512-WRsZS/7qlfYXsa93FBpSruieuURIu7ySfFhzYfF1IbKrNAGwmbduutkHZh2ddm5/vQMvQ0Rdosgv+CslaQHMcw==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [linux] + + sass-embedded-linux-musl-x64@1.85.1: + resolution: {integrity: sha512-+OlLIilA5TnP0YEqTQ8yZtkW+bJIQYvzoGoNLUEskeyeGuOiIyn2CwL6G4JQB4xZQFaxPHb7JD3EueFkQbH0Pw==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [linux] + + sass-embedded-linux-riscv64@1.85.1: + resolution: {integrity: sha512-mKKlOwMGLN7yP1p0gB5yG/HX4fYLnpWaqstNuOOXH+fOzTaNg0+1hALg0H0CDIqypPO74M5MS9T6FAJZGdT6dQ==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [linux] + + sass-embedded-linux-x64@1.85.1: + resolution: {integrity: sha512-uKRTv0z8NgtHV7xSren78+yoWB79sNi7TMqI7Bxd8fcRNIgHQSA8QBdF8led2ETC004hr8h71BrY60RPO+SSvA==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [linux] + + sass-embedded-win32-arm64@1.85.1: + resolution: {integrity: sha512-/GMiZXBOc6AEMBC3g25Rp+x8fq9Z6Ql7037l5rajBPhZ+DdFwtdHY0Ou3oIU6XuWUwD06U3ii4XufXVFhsP6PA==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [win32] + + sass-embedded-win32-ia32@1.85.1: + resolution: {integrity: sha512-L+4BWkKKBGFOKVQ2PQ5HwFfkM5FvTf1Xx2VSRvEWt9HxPXp6SPDho6zC8fqNQ3hSjoaoASEIJcSvgfdQYO0gdg==} + engines: {node: '>=14.0.0'} + cpu: [ia32] + os: [win32] + + sass-embedded-win32-x64@1.85.1: + resolution: {integrity: sha512-/FO0AGKWxVfCk4GKsC0yXWBpUZdySe3YAAbQQL0lL6xUd1OiUY8Kow6g4Kc1TB/+z0iuQKKTqI/acJMEYl4iTQ==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [win32] + + sass-embedded@1.85.1: + resolution: {integrity: sha512-0i+3h2Df/c71afluxC1SXqyyMmJlnKWfu9ZGdzwuKRM1OftEa2XM2myt5tR36CF3PanYrMjFKtRIj8PfSf838w==} + engines: {node: '>=16.0.0'} + hasBin: true + + sass-loader@12.4.0: + resolution: {integrity: sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==} + engines: {node: '>= 12.13.0'} + peerDependencies: + fibers: '>= 3.1.0' + node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + sass: ^1.3.0 + webpack: ^5.0.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + + sass@1.49.11: + resolution: {integrity: sha512-wvS/geXgHUGs6A/4ud5BFIWKO1nKd7wYIGimDk4q4GFkJicILActpv9ueMT4eRGSsp1BdKHuw1WwAHXbhsJELQ==} + engines: {node: '>=12.0.0'} + hasBin: true + + sax@1.2.1: + resolution: {integrity: sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==} + + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.20.2: + resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + schema-utils@1.0.0: + resolution: {integrity: sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==} + engines: {node: '>= 4'} + + schema-utils@2.7.0: + resolution: {integrity: sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==} + engines: {node: '>= 8.9.0'} + + schema-utils@2.7.1: + resolution: {integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==} + engines: {node: '>= 8.9.0'} + + schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + + schema-utils@4.3.3: + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} + engines: {node: '>= 10.13.0'} + + secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + + select-hose@2.0.0: + resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} + + selfsigned@2.4.1: + resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} + engines: {node: '>=10'} + + selfsigned@5.5.0: + resolution: {integrity: sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==} + engines: {node: '>=18'} + + semver-diff@3.1.1: + resolution: {integrity: sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==} + engines: {node: '>=8'} + + semver-store@0.3.0: + resolution: {integrity: sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg==} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + send@0.17.2: + resolution: {integrity: sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==} + engines: {node: '>= 0.8.0'} + + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + engines: {node: '>= 0.8.0'} + + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + engines: {node: '>= 0.8.0'} + + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + serialize-javascript@4.0.0: + resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} + + serialize-javascript@5.0.1: + resolution: {integrity: sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + serialize-javascript@7.0.5: + resolution: {integrity: sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==} + engines: {node: '>=20.0.0'} + + serve-favicon@2.5.1: + resolution: {integrity: sha512-JndLBslCLA/ebr7rS3d+/EKkzTsTi1jI2T9l+vHfAaGJ7A7NhtDpSZ0lx81HCNWnnE0yHncG+SSnVf9IMxOwXQ==} + engines: {node: '>= 0.8.0'} + + serve-index@1.9.2: + resolution: {integrity: sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.2: + resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-immediate-shim@1.0.1: + resolution: {integrity: sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ==} + engines: {node: '>=0.10.0'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + set-value@2.0.1: + resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==} + engines: {node: '>=0.10.0'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + sha.js@2.4.12: + resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} + engines: {node: '>= 0.10'} + hasBin: true + + shallow-clone@3.0.1: + resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} + engines: {node: '>=8'} + + shallowequal@1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + + shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + + shelljs@0.8.5: + resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} + engines: {node: '>=4'} + hasBin: true + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + + sirv@1.0.19: + resolution: {integrity: sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==} + engines: {node: '>= 10'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@2.0.0: + resolution: {integrity: sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==} + engines: {node: '>=6'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slice-ansi@2.1.0: + resolution: {integrity: sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==} + engines: {node: '>=6'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + snapdragon-node@2.1.1: + resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==} + engines: {node: '>=0.10.0'} + + snapdragon-util@3.0.1: + resolution: {integrity: sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==} + engines: {node: '>=0.10.0'} + + snapdragon@0.8.2: + resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==} + engines: {node: '>=0.10.0'} + + sockjs@0.3.24: + resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} + + socks-proxy-agent@5.0.1: + resolution: {integrity: sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==} + engines: {node: '>= 6'} + + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + sonic-boom@1.4.1: + resolution: {integrity: sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==} + + sort-keys@4.2.0: + resolution: {integrity: sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==} + engines: {node: '>=8'} + + source-list-map@2.0.1: + resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-loader@1.1.3: + resolution: {integrity: sha512-6YHeF+XzDOrT/ycFJNI53cgEsp/tHTMl37hi7uVyqFAlTXW109JazaQCkbc+jjoL2637qkH1amLi+JzrIpt5lA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + source-map-loader@3.0.2: + resolution: {integrity: sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + source-map-resolve@0.5.3: + resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==} + deprecated: See https://github.com/lydell/source-map-resolve#deprecated + + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map-url@0.4.1: + resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} + deprecated: See https://github.com/lydell/source-map-url#deprecated + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + space-separated-tokens@1.1.5: + resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-expression-parse@4.0.0: + resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} + + spdx-license-ids@3.0.23: + resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==} + + spdy-transport@3.0.0: + resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} + + spdy@4.0.2: + resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} + engines: {node: '>=6.0.0'} + + split-string@3.1.0: + resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} + engines: {node: '>=0.10.0'} + + split2@3.2.2: + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + ssri@10.0.5: + resolution: {integrity: sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ssri@6.0.2: + resolution: {integrity: sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==} + + ssri@8.0.1: + resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} + engines: {node: '>= 8'} + + stable@0.1.8: + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + + state-toggle@1.0.3: + resolution: {integrity: sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==} + + static-extend@0.1.2: + resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} + engines: {node: '>=0.10.0'} + + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + store2@2.14.4: + resolution: {integrity: sha512-srTItn1GOvyvOycgxjAnPA63FZNwy0PTyUBFMHRM+hVFltAeoh0LmNBz9SZqUS9mMqGk8rfyWyXn3GH5ReJ8Zw==} + + storybook@9.1.20: + resolution: {integrity: sha512-6rME2tww6PFhm96iG2Xx44yzwLDWBiDWy+kJ2ub6x90werSTOiuo+tZJ94BgCfFutR0tEfLRIq59s+Zg6YyChA==} + hasBin: true + peerDependencies: + prettier: ^2 || ^3 + peerDependenciesMeta: + prettier: + optional: true + + stream-browserify@2.0.2: + resolution: {integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==} + + stream-each@1.2.3: + resolution: {integrity: sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==} + + stream-http@2.8.3: + resolution: {integrity: sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==} + + stream-shift@1.0.3: + resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + + streamroller@3.1.5: + resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==} + engines: {node: '>=8.0'} + + strict-uri-encode@2.0.0: + resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} + engines: {node: '>=4'} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-hash@1.1.3: + resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-similarity@4.0.4: + resolution: {integrity: sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + + string-width@1.0.2: + resolution: {integrity: sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==} + engines: {node: '>=0.10.0'} + + string-width@3.1.0: + resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==} + engines: {node: '>=6'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.padend@3.1.6: + resolution: {integrity: sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==} + engines: {node: '>= 0.4'} + + string.prototype.padstart@3.1.7: + resolution: {integrity: sha512-hc5ZFzw8H2Bl4AeHxE5s+CniFg+bPcr7lRRS189GCM6KhJQBACNRhtMsdcnpBNbjc1XisnUOqbP0c94RZU4GCw==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@3.0.1: + resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} + engines: {node: '>=0.10.0'} + + strip-ansi@5.2.0: + resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} + engines: {node: '>=6'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-eof@1.0.0: + resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} + engines: {node: '>=0.10.0'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-indent@4.1.1: + resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} + engines: {node: '>=12'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strnum@2.2.2: + resolution: {integrity: sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==} + + style-loader@1.3.0: + resolution: {integrity: sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q==} + engines: {node: '>= 8.9.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + style-loader@2.0.0: + resolution: {integrity: sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + style-loader@3.3.4: + resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + style-to-object@0.3.0: + resolution: {integrity: sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==} + + stylehacks@5.1.1: + resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svgo@2.8.2: + resolution: {integrity: sha512-TyzE4NVGLUFy+H/Uy4N6c3G0HEeprsVfge6Lmq+0FdQQ/zqoVYB62IsBZORsiL+o96s6ff/V6/3UQo/C0cgCAA==} + engines: {node: '>=10.13.0'} + hasBin: true + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + symbol.prototype.description@1.0.7: + resolution: {integrity: sha512-HHGLabwmDRorfrwBGt3dD6iakQ1gNxbNK1jRb3rvr8XVsHmbAzaMdZGJtzL2W8IXdwfm3GEdw27qG86CWpuqOQ==} + engines: {node: '>= 0.4'} + + sync-child-process@1.0.2: + resolution: {integrity: sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==} + engines: {node: '>=16.0.0'} + + sync-message-port@1.2.0: + resolution: {integrity: sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg==} + engines: {node: '>=16.0.0'} + + synchronous-promise@2.0.17: + resolution: {integrity: sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==} + + synckit@0.11.12: + resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} + engines: {node: ^14.18.0 || >=16.0.0} + + table@5.4.6: + resolution: {integrity: sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==} + engines: {node: '>=6.0.0'} + + table@6.9.0: + resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} + engines: {node: '>=10.0.0'} + + tabster@8.7.0: + resolution: {integrity: sha512-AKYquti8AdWzuqJdQo4LUMQDZrHoYQy6V+8yUq2PmgLZV10EaB+8BD0nWOfC/3TBp4mPNg4fbHkz6SFtkr0PpA==} + + tapable@1.1.3: + resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==} + engines: {node: '>=6'} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + tar-fs@2.1.4: + resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + tar@7.5.13: + resolution: {integrity: sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==} + engines: {node: '>=18'} + + telejson@5.3.3: + resolution: {integrity: sha512-PjqkJZpzEggA9TBpVtJi1LVptP7tYtXB6rEubwlHap76AMjzvOdKX41CxyaW7ahhzDU1aftXnMCx5kAPDZTQBA==} + deprecated: 'SECURITY: Upgrade to v6 or above' + + temp@0.8.4: + resolution: {integrity: sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==} + engines: {node: '>=6.0.0'} + + terser-webpack-plugin@1.4.6: + resolution: {integrity: sha512-2lBVf/VMVIddjSn3GqbT90GvIJ/eYXJkt8cTzU7NbjKqK8fwv18Ftr4PlbF46b/e88743iZFL5Dtr/rC4hjIeA==} + engines: {node: '>= 6.9.0'} + peerDependencies: + webpack: ^4.0.0 + + terser-webpack-plugin@3.0.8: + resolution: {integrity: sha512-ygwK8TYMRTYtSyLB2Mhnt90guQh989CIq/mL/2apwi6rA15Xys4ydNUiH4ah6EZCfQxSk26ZFQilZ4IQ6IZw6A==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + terser-webpack-plugin@4.2.3: + resolution: {integrity: sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + terser-webpack-plugin@5.3.17: + resolution: {integrity: sha512-YR7PtUp6GMU91BgSJmlaX/rS2lGDbAF7D+Wtq7hRO+MiljNmodYvqslzCFiYVAgW+Qoaaia/QUIP4lGXufjdZw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@4.8.1: + resolution: {integrity: sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==} + engines: {node: '>=6.0.0'} + hasBin: true + + terser@5.46.1: + resolution: {integrity: sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==} + engines: {node: '>=10'} + hasBin: true + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + thingies@2.6.0: + resolution: {integrity: sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==} + engines: {node: '>=10.18'} + peerDependencies: + tslib: ^2 + + throttle-debounce@3.0.1: + resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} + engines: {node: '>=10'} + + through2@2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + + through2@4.0.2: + resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + + thunky@1.1.0: + resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} + + timers-browserify@2.0.12: + resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} + engines: {node: '>=0.6.0'} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tiny-lru@7.0.6: + resolution: {integrity: sha512-zNYO0Kvgn5rXzWpL0y3RS09sMK67eGaQj9805jlK9G6pSadfriTczzLHFXa/xcW4mIRfmlB9HyQ/+SgL0V1uow==} + engines: {node: '>=6'} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + hasBin: true + + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + + to-arraybuffer@1.0.1: + resolution: {integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==} + + to-buffer@1.2.2: + resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} + engines: {node: '>= 0.4'} + + to-object-path@0.3.0: + resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==} + engines: {node: '>=0.10.0'} + + to-regex-range@2.1.1: + resolution: {integrity: sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==} + engines: {node: '>=0.10.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + to-regex@3.0.2: + resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==} + engines: {node: '>=0.10.0'} + + toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + totalist@1.1.0: + resolution: {integrity: sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==} + engines: {node: '>=6'} + + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@5.1.1: + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} + engines: {node: '>=18'} + + traverse@0.3.9: + resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==} + + tree-dump@1.1.0: + resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + trim-trailing-lines@1.1.4: + resolution: {integrity: sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==} + + trim@0.0.1: + resolution: {integrity: sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==} + deprecated: Use String.prototype.trim() instead + + trough@1.0.5: + resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==} + + true-case-path@2.2.1: + resolution: {integrity: sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q==} + + ts-api-utils@1.4.3: + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-dedent@2.2.0: + resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} + engines: {node: '>=6.10'} + + ts-loader@6.0.0: + resolution: {integrity: sha512-lszy+D41R0Te2+loZxADWS+E1+Z55A+i3dFfFie1AZHL++65JRKVDBPQgeWgRrlv5tbxdU3zOtXp8b7AFR6KEg==} + engines: {node: '>=8.6'} + peerDependencies: + typescript: '*' + + ts-pnp@1.2.0: + resolution: {integrity: sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==} + engines: {node: '>=6'} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.4.0: + resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tslint@5.20.1: + resolution: {integrity: sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==} + engines: {node: '>=4.8.0'} + hasBin: true + peerDependencies: + typescript: '>=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev' + + tsutils@2.29.0: + resolution: {integrity: sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==} + peerDependencies: + typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' + + tsyringe@4.10.0: + resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==} + engines: {node: '>= 6.0.0'} + + tty-browserify@0.0.0: + resolution: {integrity: sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==} + + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + + tunnel@0.0.6: + resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} + engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + + type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typed-rest-client@1.8.11: + resolution: {integrity: sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==} + + typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + + typescript@2.9.2: + resolution: {integrity: sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==} + engines: {node: '>=4.2.0'} + hasBin: true + + typescript@3.9.10: + resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==} + engines: {node: '>=4.2.0'} + hasBin: true + + typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + + typescript@5.8.2: + resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + + ufo@1.6.3: + resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + underscore@1.13.8: + resolution: {integrity: sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + unfetch@4.2.0: + resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} + + unherit@1.1.3: + resolution: {integrity: sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + unified@9.2.0: + resolution: {integrity: sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==} + + union-value@1.0.1: + resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==} + engines: {node: '>=0.10.0'} + + unique-filename@1.1.1: + resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==} + + unique-slug@2.0.2: + resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==} + + unique-string@2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + + unist-builder@2.0.3: + resolution: {integrity: sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==} + + unist-util-generated@1.1.6: + resolution: {integrity: sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==} + + unist-util-is@4.1.0: + resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==} + + unist-util-position@3.1.0: + resolution: {integrity: sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==} + + unist-util-remove-position@2.0.1: + resolution: {integrity: sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==} + + unist-util-remove@2.1.0: + resolution: {integrity: sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q==} + + unist-util-stringify-position@2.0.3: + resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} + + unist-util-visit-parents@3.1.1: + resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==} + + unist-util-visit@2.0.3: + resolution: {integrity: sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + unrs-resolver@1.11.1: + resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + + unset-value@1.0.0: + resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==} + engines: {node: '>=0.10.0'} + + unzipper@0.10.14: + resolution: {integrity: sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==} + + upath@1.2.0: + resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} + engines: {node: '>=4'} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + update-notifier@5.1.0: + resolution: {integrity: sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==} + engines: {node: '>=10'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + urix@0.1.0: + resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} + deprecated: Please see https://github.com/lydell/urix#deprecated + + url-join@4.0.1: + resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} + + url-loader@4.1.1: + resolution: {integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + file-loader: '*' + webpack: ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + file-loader: + optional: true + + url@0.10.3: + resolution: {integrity: sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==} + + url@0.11.4: + resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} + engines: {node: '>= 0.4'} + + use-composed-ref@1.4.0: + resolution: {integrity: sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-isomorphic-layout-effect@1.2.1: + resolution: {integrity: sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-latest@1.3.0: + resolution: {integrity: sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + use@3.1.1: + resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} + engines: {node: '>=0.10.0'} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util.promisify@1.0.0: + resolution: {integrity: sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==} + + util@0.10.4: + resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==} + + util@0.11.1: + resolution: {integrity: sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==} + + util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + + utila@0.4.0: + resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid-browser@3.1.0: + resolution: {integrity: sha512-dsNgbLaTrd6l3MMxTtouOCFw4CBFc/3a+GgYA2YyrJvyQ1u6q4pcu3ktLoUZ/VN/Aw9WsauazbgsgdfVWgAKQg==} + deprecated: Package no longer supported and required. Use the uuid package or crypto.randomUUID instead + + uuid@3.4.0: + resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} + deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. + hasBin: true + + uuid@8.0.0: + resolution: {integrity: sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==} + hasBin: true + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + v8-compile-cache@2.4.0: + resolution: {integrity: sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==} + + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + validate-npm-package-name@3.0.0: + resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} + + validator@13.15.35: + resolution: {integrity: sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==} + engines: {node: '>= 0.10'} + + varint@6.0.0: + resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vfile-location@3.2.0: + resolution: {integrity: sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==} + + vfile-message@2.0.4: + resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==} + + vfile@4.2.1: + resolution: {integrity: sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==} + + vm-browserify@1.1.2: + resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + + warning@4.0.3: + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + + watchpack-chokidar2@2.0.1: + resolution: {integrity: sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==} + + watchpack@1.7.5: + resolution: {integrity: sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==} + + watchpack@2.4.0: + resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} + engines: {node: '>=10.13.0'} + + watchpack@2.5.1: + resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} + engines: {node: '>=10.13.0'} + + wbuf@1.7.3: + resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} + + web-namespaces@1.1.4: + resolution: {integrity: sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + webpack-bundle-analyzer@4.5.0: + resolution: {integrity: sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==} + engines: {node: '>= 10.13.0'} + hasBin: true + + webpack-dev-middleware@3.7.3: + resolution: {integrity: sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==} + engines: {node: '>= 6'} + peerDependencies: + '@types/webpack': ^4 + webpack: ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + '@types/webpack': + optional: true + + webpack-dev-middleware@5.3.4: + resolution: {integrity: sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==} + engines: {node: '>= 12.13.0'} + peerDependencies: + '@types/webpack': ^4 + webpack: ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + '@types/webpack': + optional: true + + webpack-dev-middleware@6.1.3: + resolution: {integrity: sha512-A4ChP0Qj8oGociTs6UdlRUGANIGrCDL3y+pmQMc+dSsraXHCatFpmMey4mYELA+juqwUqwQsUgJJISXl1KWmiw==} + engines: {node: '>= 14.15.0'} + peerDependencies: + '@types/webpack': ^4 + webpack: ^5.0.0 + peerDependenciesMeta: + '@types/webpack': + optional: true + webpack: + optional: true + + webpack-dev-middleware@7.4.5: + resolution: {integrity: sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@types/webpack': ^4 + webpack: ^5.0.0 + peerDependenciesMeta: + '@types/webpack': + optional: true + webpack: + optional: true + + webpack-dev-server@4.9.3: + resolution: {integrity: sha512-3qp/eoboZG5/6QgiZ3llN8TUzkSpYg1Ko9khWX1h40MIEUNS2mDoIa8aXsPfskER+GbTvs/IJZ1QTBBhhuetSw==} + engines: {node: '>= 12.13.0'} + hasBin: true + peerDependencies: + '@types/webpack': ^4 + webpack: ^4.37.0 || ^5.0.0 + webpack-cli: '*' + peerDependenciesMeta: + '@types/webpack': + optional: true + webpack-cli: + optional: true + + webpack-dev-server@5.2.3: + resolution: {integrity: sha512-9Gyu2F7+bg4Vv+pjbovuYDhHX+mqdqITykfzdM9UyKqKHlsE5aAjRhR+oOEfXW5vBeu8tarzlJFIZva4ZjAdrQ==} + engines: {node: '>= 18.12.0'} + hasBin: true + peerDependencies: + '@types/webpack': ^4 + webpack: ^5.0.0 + webpack-cli: '*' + peerDependenciesMeta: + '@types/webpack': + optional: true + webpack: + optional: true + webpack-cli: + optional: true + + webpack-filter-warnings-plugin@1.2.1: + resolution: {integrity: sha512-Ez6ytc9IseDMLPo0qCuNNYzgtUl8NovOqjIq4uAU8LTD4uoa1w1KpZyyzFtLTEMZpkkOkLfL9eN+KGYdk1Qtwg==} + engines: {node: '>= 4.3 < 5.0.0 || >= 5.10'} + peerDependencies: + webpack: ^2.0.0 || ^3.0.0 || ^4.0.0 + + webpack-hot-middleware@2.26.1: + resolution: {integrity: sha512-khZGfAeJx6I8K9zKohEWWYN6KDlVw2DHownoe+6Vtwj1LP9WFgegXnVMSkZ/dBEBtXFwrkkydsaPFlB7f8wU2A==} + + webpack-log@2.0.0: + resolution: {integrity: sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==} + engines: {node: '>= 6'} + + webpack-merge@5.8.0: + resolution: {integrity: sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==} + engines: {node: '>=10.0.0'} + + webpack-sources@1.4.3: + resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==} + + webpack-sources@3.3.4: + resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} + engines: {node: '>=10.13.0'} + + webpack-virtual-modules@0.2.2: + resolution: {integrity: sha512-kDUmfm3BZrei0y+1NTHJInejzxfhtU8eDj2M7OKb2IWrPFAeO1SOH2KuQ68MSZu9IGEHcxbkKKR1v18FrUSOmA==} + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + webpack@4.47.0: + resolution: {integrity: sha512-td7fYwgLSrky3fI1EuU5cneU4+pbH6GgOfuKNS1tNPcfdGinGELAqsb/BP4nnvZyKSG2i/xFGU7+n2PvZA8HJQ==} + engines: {node: '>=6.11.5'} + hasBin: true + peerDependencies: + webpack-cli: '*' + webpack-command: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + webpack-command: + optional: true + + webpack@5.105.4: + resolution: {integrity: sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + websocket-driver@0.7.4: + resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-mimetype@2.3.0: + resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.2.0: + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + + widest-line@3.1.0: + resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} + engines: {node: '>=8'} + + wildcard@2.0.1: + resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + + worker-farm@1.7.0: + resolution: {integrity: sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==} + + worker-rpc@0.1.1: + resolution: {integrity: sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==} + + workerpool@6.5.1: + resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@2.4.3: + resolution: {integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==} + + write-file-atomic@3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + + write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + write-yaml-file@4.2.0: + resolution: {integrity: sha512-LwyucHy0uhWqbrOkh9cBluZBeNVxzHjDaE9mwepZG3n3ZlbM4v3ndrFw51zW/NXYFFqP+QWZ72ihtLWTh05e4Q==} + engines: {node: '>=10.13'} + + write@1.0.3: + resolution: {integrity: sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==} + engines: {node: '>=4'} + + ws@6.2.3: + resolution: {integrity: sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==} + + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + + xdg-basedir@4.0.0: + resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} + engines: {node: '>=8'} + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xml2js@0.5.0: + resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} + engines: {node: '>=4.0.0'} + + xml2js@0.6.2: + resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} + engines: {node: '>=4.0.0'} + + xml@1.0.1: + resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==} + + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + xmldoc@1.1.4: + resolution: {integrity: sha512-rQshsBGR5s7pUNENTEncpI2LTCuzicri0DyE4SCV5XmS0q81JS8j1iPijP0Q5c4WLGbKh3W92hlOwY6N9ssW1w==} + + xstate@4.26.1: + resolution: {integrity: sha512-JLofAEnN26l/1vbODgsDa+Phqa61PwDlxWu8+2pK+YbXf+y9pQSDLRvcYH2H1kkeUBA5fGp+xFL/zfE8jNMw4g==} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml@1.10.3: + resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} + engines: {node: '>= 6'} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs-unparser@2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + + yazl@2.5.1: + resolution: {integrity: sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.2.2: + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} + engines: {node: '>=12.20'} + + z-schema@5.0.5: + resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} + engines: {node: '>=8.0.0'} + hasBin: true + + zip-local@0.3.5: + resolution: {integrity: sha512-GRV3D5TJY+/PqyeRm5CYBs7xVrKTKzljBoEXvocZu0HJ7tPEcgpSOYa2zFIsCZWgKWMuc4U3yMFgFkERGFIB9w==} + + zip-stream@4.1.1: + resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} + engines: {node: '>= 10'} + + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + peerDependencies: + zod: ^3.25.28 || ^4 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zwitch@1.0.5: + resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} + +snapshots: + + '@adobe/css-tools@4.4.4': {} + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@apidevtools/json-schema-ref-parser@11.9.3': + dependencies: + '@jsdevtools/ono': 7.1.3 + '@types/json-schema': 7.0.15 + js-yaml: 4.1.1 + + '@asamuzakjp/css-color@3.2.0': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 10.4.3 + + '@aws-cdk/asset-awscli-v1@2.2.273': {} + + '@aws-cdk/asset-node-proxy-agent-v6@2.1.1': {} + + '@aws-cdk/aws-apigatewayv2-alpha@2.50.0-alpha.0(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130)': + dependencies: + aws-cdk-lib: 2.50.0(constructs@10.0.130) + constructs: 10.0.130 + + '@aws-cdk/aws-apigatewayv2-authorizers-alpha@2.50.0-alpha.0(@aws-cdk/aws-apigatewayv2-alpha@2.50.0-alpha.0(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130))(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130)': + dependencies: + '@aws-cdk/aws-apigatewayv2-alpha': 2.50.0-alpha.0(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130) + aws-cdk-lib: 2.50.0(constructs@10.0.130) + constructs: 10.0.130 + + '@aws-cdk/aws-apigatewayv2-integrations-alpha@2.50.0-alpha.0(@aws-cdk/aws-apigatewayv2-alpha@2.50.0-alpha.0(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130))(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130)': + dependencies: + '@aws-cdk/aws-apigatewayv2-alpha': 2.50.0-alpha.0(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130) + aws-cdk-lib: 2.50.0(constructs@10.0.130) + constructs: 10.0.130 + + '@aws-cdk/aws-appsync-alpha@2.50.0-alpha.0(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130)': + dependencies: + aws-cdk-lib: 2.50.0(constructs@10.0.130) + constructs: 10.0.130 + + '@aws-cdk/cloud-assembly-schema@41.2.0': {} + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-locate-window': 3.965.5 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.6 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-codebuild@3.1023.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.26 + '@aws-sdk/credential-provider-node': 3.972.29 + '@aws-sdk/middleware-host-header': 3.972.8 + '@aws-sdk/middleware-logger': 3.972.8 + '@aws-sdk/middleware-recursion-detection': 3.972.9 + '@aws-sdk/middleware-user-agent': 3.972.28 + '@aws-sdk/region-config-resolver': 3.972.10 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@aws-sdk/util-user-agent-browser': 3.972.8 + '@aws-sdk/util-user-agent-node': 3.973.14 + '@smithy/config-resolver': 4.4.13 + '@smithy/core': 3.23.13 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/hash-node': 4.2.12 + '@smithy/invalid-dependency': 4.2.12 + '@smithy/middleware-content-length': 4.2.12 + '@smithy/middleware-endpoint': 4.4.28 + '@smithy/middleware-retry': 4.4.46 + '@smithy/middleware-serde': 4.2.16 + '@smithy/middleware-stack': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/node-http-handler': 4.5.1 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.8 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.44 + '@smithy/util-defaults-mode-node': 4.2.48 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.13 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sso-oidc@3.1023.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.26 + '@aws-sdk/credential-provider-node': 3.972.29 + '@aws-sdk/middleware-host-header': 3.972.8 + '@aws-sdk/middleware-logger': 3.972.8 + '@aws-sdk/middleware-recursion-detection': 3.972.9 + '@aws-sdk/middleware-user-agent': 3.972.28 + '@aws-sdk/region-config-resolver': 3.972.10 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@aws-sdk/util-user-agent-browser': 3.972.8 + '@aws-sdk/util-user-agent-node': 3.973.14 + '@smithy/config-resolver': 4.4.13 + '@smithy/core': 3.23.13 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/hash-node': 4.2.12 + '@smithy/invalid-dependency': 4.2.12 + '@smithy/middleware-content-length': 4.2.12 + '@smithy/middleware-endpoint': 4.4.28 + '@smithy/middleware-retry': 4.4.46 + '@smithy/middleware-serde': 4.2.16 + '@smithy/middleware-stack': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/node-http-handler': 4.5.1 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.8 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.44 + '@smithy/util-defaults-mode-node': 4.2.48 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.13 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sts@3.1023.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.26 + '@aws-sdk/credential-provider-node': 3.972.29 + '@aws-sdk/middleware-host-header': 3.972.8 + '@aws-sdk/middleware-logger': 3.972.8 + '@aws-sdk/middleware-recursion-detection': 3.972.9 + '@aws-sdk/middleware-user-agent': 3.972.28 + '@aws-sdk/region-config-resolver': 3.972.10 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@aws-sdk/util-user-agent-browser': 3.972.8 + '@aws-sdk/util-user-agent-node': 3.973.14 + '@smithy/config-resolver': 4.4.13 + '@smithy/core': 3.23.13 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/hash-node': 4.2.12 + '@smithy/invalid-dependency': 4.2.12 + '@smithy/middleware-content-length': 4.2.12 + '@smithy/middleware-endpoint': 4.4.28 + '@smithy/middleware-retry': 4.4.46 + '@smithy/middleware-serde': 4.2.16 + '@smithy/middleware-stack': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/node-http-handler': 4.5.1 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.8 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.44 + '@smithy/util-defaults-mode-node': 4.2.48 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.13 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.973.26': + dependencies: + '@aws-sdk/types': 3.973.6 + '@aws-sdk/xml-builder': 3.972.16 + '@smithy/core': 3.23.13 + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/signature-v4': 5.3.12 + '@smithy/smithy-client': 4.12.8 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.24': + dependencies: + '@aws-sdk/core': 3.973.26 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.26': + dependencies: + '@aws-sdk/core': 3.973.26 + '@aws-sdk/types': 3.973.6 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/node-http-handler': 4.5.1 + '@smithy/property-provider': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.8 + '@smithy/types': 4.13.1 + '@smithy/util-stream': 4.5.21 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.972.28': + dependencies: + '@aws-sdk/core': 3.973.26 + '@aws-sdk/credential-provider-env': 3.972.24 + '@aws-sdk/credential-provider-http': 3.972.26 + '@aws-sdk/credential-provider-login': 3.972.28 + '@aws-sdk/credential-provider-process': 3.972.24 + '@aws-sdk/credential-provider-sso': 3.972.28 + '@aws-sdk/credential-provider-web-identity': 3.972.28 + '@aws-sdk/nested-clients': 3.996.18 + '@aws-sdk/types': 3.973.6 + '@smithy/credential-provider-imds': 4.2.12 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-login@3.972.28': + dependencies: + '@aws-sdk/core': 3.973.26 + '@aws-sdk/nested-clients': 3.996.18 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.972.29': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.24 + '@aws-sdk/credential-provider-http': 3.972.26 + '@aws-sdk/credential-provider-ini': 3.972.28 + '@aws-sdk/credential-provider-process': 3.972.24 + '@aws-sdk/credential-provider-sso': 3.972.28 + '@aws-sdk/credential-provider-web-identity': 3.972.28 + '@aws-sdk/types': 3.973.6 + '@smithy/credential-provider-imds': 4.2.12 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.972.24': + dependencies: + '@aws-sdk/core': 3.973.26 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.972.28': + dependencies: + '@aws-sdk/core': 3.973.26 + '@aws-sdk/nested-clients': 3.996.18 + '@aws-sdk/token-providers': 3.1021.0 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.972.28': + dependencies: + '@aws-sdk/core': 3.973.26 + '@aws-sdk/nested-clients': 3.996.18 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/middleware-host-header@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.972.9': + dependencies: + '@aws-sdk/types': 3.973.6 + '@aws/lambda-invoke-store': 0.2.4 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.972.28': + dependencies: + '@aws-sdk/core': 3.973.26 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@smithy/core': 3.23.13 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-retry': 4.2.13 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.996.18': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.26 + '@aws-sdk/middleware-host-header': 3.972.8 + '@aws-sdk/middleware-logger': 3.972.8 + '@aws-sdk/middleware-recursion-detection': 3.972.9 + '@aws-sdk/middleware-user-agent': 3.972.28 + '@aws-sdk/region-config-resolver': 3.972.10 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@aws-sdk/util-user-agent-browser': 3.972.8 + '@aws-sdk/util-user-agent-node': 3.973.14 + '@smithy/config-resolver': 4.4.13 + '@smithy/core': 3.23.13 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/hash-node': 4.2.12 + '@smithy/invalid-dependency': 4.2.12 + '@smithy/middleware-content-length': 4.2.12 + '@smithy/middleware-endpoint': 4.4.28 + '@smithy/middleware-retry': 4.4.46 + '@smithy/middleware-serde': 4.2.16 + '@smithy/middleware-stack': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/node-http-handler': 4.5.1 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.8 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.44 + '@smithy/util-defaults-mode-node': 4.2.48 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.13 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.972.10': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/config-resolver': 4.4.13 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1021.0': + dependencies: + '@aws-sdk/core': 3.973.26 + '@aws-sdk/nested-clients': 3.996.18 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.973.6': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.996.5': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-endpoints': 3.3.3 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.5': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.972.8': + dependencies: + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.973.14': + dependencies: + '@aws-sdk/middleware-user-agent': 3.972.28 + '@aws-sdk/types': 3.973.6 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.16': + dependencies: + '@smithy/types': 4.13.1 + fast-xml-parser: 5.3.5 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.4': {} + + '@azure/abort-controller@2.1.2': + dependencies: + tslib: 2.8.1 + + '@azure/core-auth@1.10.1': + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-util': 1.13.1 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@azure/core-client@1.10.1': + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-auth': 1.10.1 + '@azure/core-rest-pipeline': 1.23.0 + '@azure/core-tracing': 1.3.1 + '@azure/core-util': 1.13.1 + '@azure/logger': 1.3.0 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@azure/core-http-compat@2.3.2(@azure/core-client@1.10.1)(@azure/core-rest-pipeline@1.23.0)': + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-client': 1.10.1 + '@azure/core-rest-pipeline': 1.23.0 + + '@azure/core-lro@2.7.2': + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-util': 1.13.1 + '@azure/logger': 1.3.0 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@azure/core-paging@1.6.2': + dependencies: + tslib: 2.8.1 + + '@azure/core-rest-pipeline@1.23.0': + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-auth': 1.10.1 + '@azure/core-tracing': 1.3.1 + '@azure/core-util': 1.13.1 + '@azure/logger': 1.3.0 + '@typespec/ts-http-runtime': 0.3.4 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@azure/core-tracing@1.3.1': + dependencies: + tslib: 2.8.1 + + '@azure/core-util@1.13.1': + dependencies: + '@azure/abort-controller': 2.1.2 + '@typespec/ts-http-runtime': 0.3.4 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@azure/core-xml@1.5.0': + dependencies: + fast-xml-parser: 5.3.5 + tslib: 2.8.1 + + '@azure/identity@4.13.1': + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-auth': 1.10.1 + '@azure/core-client': 1.10.1 + '@azure/core-rest-pipeline': 1.23.0 + '@azure/core-tracing': 1.3.1 + '@azure/core-util': 1.13.1 + '@azure/logger': 1.3.0 + '@azure/msal-browser': 5.6.3 + '@azure/msal-node': 5.1.2 + open: 10.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@azure/logger@1.3.0': + dependencies: + '@typespec/ts-http-runtime': 0.3.4 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@azure/msal-browser@5.6.3': + dependencies: + '@azure/msal-common': 16.4.1 + + '@azure/msal-common@16.4.1': {} + + '@azure/msal-node@5.1.2': + dependencies: + '@azure/msal-common': 16.4.1 + jsonwebtoken: 9.0.3 + uuid: 8.3.2 + + '@azure/storage-blob@12.31.0': + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-auth': 1.10.1 + '@azure/core-client': 1.10.1 + '@azure/core-http-compat': 2.3.2(@azure/core-client@1.10.1)(@azure/core-rest-pipeline@1.23.0) + '@azure/core-lro': 2.7.2 + '@azure/core-paging': 1.6.2 + '@azure/core-rest-pipeline': 1.23.0 + '@azure/core-tracing': 1.3.1 + '@azure/core-util': 1.13.1 + '@azure/core-xml': 1.5.0 + '@azure/logger': 1.3.0 + '@azure/storage-common': 12.3.0(@azure/core-client@1.10.1) + events: 3.3.0 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@azure/storage-common@12.3.0(@azure/core-client@1.10.1)': + dependencies: + '@azure/abort-controller': 2.1.2 + '@azure/core-auth': 1.10.1 + '@azure/core-http-compat': 2.3.2(@azure/core-client@1.10.1)(@azure/core-rest-pipeline@1.23.0) + '@azure/core-rest-pipeline': 1.23.0 + '@azure/core-tracing': 1.3.1 + '@azure/core-util': 1.13.1 + '@azure/logger': 1.3.0 + events: 3.3.0 + tslib: 2.8.1 + transitivePeerDependencies: + - '@azure/core-client' + - supports-color + + '@babel/code-frame@7.12.11': + dependencies: + '@babel/highlight': 7.25.9 + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.12.9': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.12.9) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + convert-source-map: 1.9.0 + debug: 4.4.3(supports-color@8.1.1) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + lodash: 4.18.1 + resolve: 1.22.11 + semver: 5.7.2 + source-map: 0.5.7 + transitivePeerDependencies: + - supports-color + + '@babel/core@7.20.12': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.20.12) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + convert-source-map: 1.9.0 + debug: 4.4.3(supports-color@8.1.1) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@8.1.1) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.20.12) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.1.5(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + debug: 4.4.3(supports-color@8.1.1) + lodash.debounce: 4.0.8 + resolve: 1.22.11 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + debug: 4.4.3(supports-color@8.1.1) + lodash.debounce: 4.0.8 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.28.5': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.12.9)': + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-plugin-utils@7.10.4': {} + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/highlight@7.25.9': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/parser@7.29.2': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.20.12) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-decorators@7.29.0(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-decorators': 7.28.6(@babel/core@7.20.12) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-export-default-from@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.20.12) + + '@babel/plugin-proposal-object-rest-spread@7.12.1(@babel/core@7.12.9)': + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.12.9) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.12.9) + + '@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.20.12)': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/core': 7.20.12 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.20.12) + + '@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.20.12) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-decorators@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-flow@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-flow@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-jsx@7.12.1(@babel/core@7.12.9)': + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.12.9)': + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.20.12) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.20.12) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.20.12) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/template': 7.28.6 + + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.20.12) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.20.12) + + '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.29.0) + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.20.12) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.20.12) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.20.12) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.12.9)': + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.20.12) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.20.12) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-spread@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.20.12) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.20.12) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/preset-env@7.29.2(@babel/core@7.20.12)': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/core': 7.20.12 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.20.12) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.20.12) + '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.20.12) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.20.12) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.20.12) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.20.12) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.20.12) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.20.12) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.20.12) + '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.20.12) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.20.12) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.20.12) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.20.12) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.20.12) + core-js-compat: 3.49.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-flow@7.27.1(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.20.12) + + '@babel/preset-flow@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0) + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/types': 7.29.0 + esutils: 2.0.3 + + '@babel/preset-react@7.28.5(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.20.12) + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.20.12) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.28.5(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.20.12) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/register@7.28.6(@babel/core@7.20.12)': + dependencies: + '@babel/core': 7.20.12 + clone-deep: 4.0.1 + find-cache-dir: 2.1.0 + make-dir: 2.1.0 + pirates: 4.0.7 + source-map-support: 0.5.21 + + '@babel/register@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + clone-deep: 4.0.1 + find-cache-dir: 2.1.0 + make-dir: 2.1.0 + pirates: 4.0.7 + source-map-support: 0.5.21 + + '@babel/runtime-corejs3@7.29.2': + dependencies: + core-js-pure: 3.49.0 + + '@babel/runtime@7.29.2': {} + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@base2/pretty-print-object@1.0.1': {} + + '@bcoe/v8-coverage@0.2.3': {} + + '@bufbuild/protobuf@2.11.0': {} + + '@cnakazawa/watch@1.0.4': + dependencies: + exec-sh: 0.3.6 + minimist: 1.2.8 + + '@colors/colors@1.5.0': + optional: true + + '@csstools/color-helpers@5.1.0': {} + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.1.0 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-tokenizer@3.0.4': {} + + '@ctrl/tinycolor@3.6.1': {} + + '@discoveryjs/json-ext@0.5.7': {} + + '@emnapi/core@1.9.2': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.9.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emotion/cache@10.0.29': + dependencies: + '@emotion/sheet': 0.9.4 + '@emotion/stylis': 0.8.5 + '@emotion/utils': 0.11.3 + '@emotion/weak-memoize': 0.2.5 + + '@emotion/core@10.3.1(@types/react@17.0.74)(react@17.0.2)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/cache': 10.0.29 + '@emotion/css': 10.0.27 + '@emotion/serialize': 0.11.16 + '@emotion/sheet': 0.9.4 + '@emotion/utils': 0.11.3 + '@types/react': 17.0.74 + react: 17.0.2 + transitivePeerDependencies: + - supports-color + + '@emotion/css@10.0.27': + dependencies: + '@emotion/serialize': 0.11.16 + '@emotion/utils': 0.11.3 + babel-plugin-emotion: 10.2.2 + transitivePeerDependencies: + - supports-color + + '@emotion/hash@0.8.0': {} + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@0.8.8': + dependencies: + '@emotion/memoize': 0.7.4 + + '@emotion/memoize@0.7.4': {} + + '@emotion/memoize@0.9.0': {} + + '@emotion/serialize@0.11.16': + dependencies: + '@emotion/hash': 0.8.0 + '@emotion/memoize': 0.7.4 + '@emotion/unitless': 0.7.5 + '@emotion/utils': 0.11.3 + csstype: 2.6.21 + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.2.3 + + '@emotion/sheet@0.9.4': {} + + '@emotion/styled-base@10.3.0(@emotion/core@10.3.1(@types/react@17.0.74)(react@17.0.2))(@types/react@17.0.74)(react@17.0.2)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/core': 10.3.1(@types/react@17.0.74)(react@17.0.2) + '@emotion/is-prop-valid': 0.8.8 + '@emotion/serialize': 0.11.16 + '@emotion/utils': 0.11.3 + '@types/react': 17.0.74 + react: 17.0.2 + + '@emotion/styled@10.3.0(@emotion/core@10.3.1(@types/react@17.0.74)(react@17.0.2))(@types/react@17.0.74)(react@17.0.2)': + dependencies: + '@emotion/core': 10.3.1(@types/react@17.0.74)(react@17.0.2) + '@emotion/styled-base': 10.3.0(@emotion/core@10.3.1(@types/react@17.0.74)(react@17.0.2))(@types/react@17.0.74)(react@17.0.2) + '@types/react': 17.0.74 + babel-plugin-emotion: 10.2.2 + react: 17.0.2 + transitivePeerDependencies: + - supports-color + + '@emotion/stylis@0.8.5': {} + + '@emotion/unitless@0.10.0': {} + + '@emotion/unitless@0.7.5': {} + + '@emotion/utils@0.11.3': + dependencies: + '@emotion/sheet': 0.9.4 + + '@emotion/utils@1.4.2': + dependencies: + '@emotion/sheet': 0.9.4 + + '@emotion/weak-memoize@0.2.5': {} + + '@es-joy/jsdoccomment@0.49.0': + dependencies: + comment-parser: 1.4.1 + esquery: 1.7.0 + jsdoc-type-pratt-parser: 4.1.0 + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/aix-ppc64@0.28.0': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.28.0': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-arm@0.28.0': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/android-x64@0.28.0': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.28.0': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.28.0': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.28.0': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.28.0': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.28.0': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-arm@0.28.0': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.28.0': + optional: true + + '@esbuild/linux-loong64@0.14.54': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.28.0': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.28.0': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.28.0': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.28.0': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.28.0': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/linux-x64@0.28.0': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.28.0': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.28.0': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.28.0': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.28.0': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.28.0': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.28.0': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.28.0': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.28.0': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@esbuild/win32-x64@0.28.0': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@7.11.0)': + dependencies: + eslint: 7.11.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.1(eslint@7.30.0)': + dependencies: + eslint: 7.30.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.1(eslint@7.7.0)': + dependencies: + eslint: 7.7.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.1(eslint@9.37.0(supports-color@8.1.1))': + dependencies: + eslint: 9.37.0(supports-color@8.1.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.1(eslint@9.37.0)': + dependencies: + eslint: 9.37.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.20.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-array@0.21.2(supports-color@8.1.1)': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.2.3': {} + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.13.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.16.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@0.1.3': + dependencies: + ajv: 6.14.0 + debug: 4.4.3(supports-color@8.1.1) + espree: 7.3.1 + globals: 12.4.0 + ignore: 4.0.6 + import-fresh: 3.3.1 + js-yaml: 3.14.2 + lodash: 4.18.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/eslintrc@0.4.3': + dependencies: + ajv: 6.14.0 + debug: 4.4.3(supports-color@8.1.1) + espree: 7.3.1 + globals: 13.24.0 + ignore: 4.0.6 + import-fresh: 3.3.1 + js-yaml: 3.14.2 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/eslintrc@1.4.1': + dependencies: + ajv: 6.14.0 + debug: 4.4.3(supports-color@8.1.1) + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.14.0 + debug: 4.4.3(supports-color@8.1.1) + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/eslintrc@3.3.5(supports-color@8.1.1)': + dependencies: + ajv: 6.14.0 + debug: 4.4.3(supports-color@8.1.1) + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.1': {} + + '@eslint/js@9.25.1': {} + + '@eslint/js@9.37.0': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.2.8': + dependencies: + '@eslint/core': 0.13.0 + levn: 0.4.1 + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@fastify/ajv-compiler@1.1.0': + dependencies: + ajv: 6.14.0 + + '@fastify/forwarded@1.0.0': {} + + '@fastify/proxy-addr@3.0.0': + dependencies: + '@fastify/forwarded': 1.0.0 + ipaddr.js: 2.3.0 + + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/devtools@0.2.3(@floating-ui/dom@1.7.6)': + dependencies: + '@floating-ui/dom': 1.7.6 + + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/utils@0.2.11': {} + + '@fluentui/date-time-utilities@8.6.11': + dependencies: + '@fluentui/set-version': 8.2.24 + tslib: 2.8.1 + + '@fluentui/dom-utilities@2.3.10': + dependencies: + '@fluentui/set-version': 8.2.24 + tslib: 2.8.1 + + '@fluentui/font-icons-mdl2@8.5.72(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@fluentui/set-version': 8.2.24 + '@fluentui/style-utilities': 8.15.0(@types/react@19.2.7)(react@19.2.4) + '@fluentui/utilities': 8.17.2(@types/react@19.2.7)(react@19.2.4) + tslib: 2.8.1 + transitivePeerDependencies: + - '@types/react' + - react + + '@fluentui/foundation-legacy@8.6.5(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@fluentui/merge-styles': 8.6.14 + '@fluentui/set-version': 8.2.24 + '@fluentui/style-utilities': 8.15.0(@types/react@19.2.7)(react@19.2.4) + '@fluentui/utilities': 8.17.2(@types/react@19.2.7)(react@19.2.4) + '@types/react': 19.2.7 + react: 19.2.4 + tslib: 2.8.1 + + '@fluentui/keyboard-key@0.4.23': + dependencies: + tslib: 2.8.1 + + '@fluentui/keyboard-keys@9.0.8': + dependencies: + '@swc/helpers': 0.5.21 + + '@fluentui/merge-styles@8.6.14': + dependencies: + '@fluentui/set-version': 8.2.24 + tslib: 2.8.1 + + '@fluentui/priority-overflow@9.3.0': + dependencies: + '@swc/helpers': 0.5.21 + + '@fluentui/react-accordion@9.10.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-motion': 9.14.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-motion-components-preview': 0.15.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-alert@9.0.0-beta.132(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-avatar': 9.11.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-button': 9.9.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-aria@9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-avatar@9.11.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-badge': 9.5.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-popover': 9.14.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-tooltip': 9.10.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-badge@9.5.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-breadcrumb@9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-button': 9.9.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-link': 9.8.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-button@9.9.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-card@9.6.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-text': 9.6.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-carousel@9.9.6(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-button': 9.9.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-tooltip': 9.10.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + embla-carousel: 8.6.0 + embla-carousel-autoplay: 8.6.0(embla-carousel@8.6.0) + embla-carousel-fade: 8.6.0(embla-carousel@8.6.0) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-checkbox@9.6.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-field': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-label': 9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-color-picker@9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@ctrl/tinycolor': 3.6.1 + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-combobox@9.17.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-field': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-portal': 9.8.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-positioning': 9.22.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-components@9.72.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-accordion': 9.10.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-alert': 9.0.0-beta.132(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-avatar': 9.11.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-badge': 9.5.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-breadcrumb': 9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-button': 9.9.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-card': 9.6.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-carousel': 9.9.6(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-checkbox': 9.6.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-color-picker': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-combobox': 9.17.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-dialog': 9.17.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-divider': 9.7.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-drawer': 9.11.6(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-field': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-image': 9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-infobutton': 9.0.0-beta.109(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-infolabel': 9.4.19(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-input': 9.8.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-label': 9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-link': 9.8.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-list': 9.6.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-menu': 9.24.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-message-bar': 9.6.23(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-motion': 9.14.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-nav': 9.3.23(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-overflow': 9.7.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-persona': 9.7.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-popover': 9.14.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-portal': 9.8.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-positioning': 9.22.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-progress': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-provider': 9.22.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-radio': 9.6.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-rating': 9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-search': 9.4.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-select': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-skeleton': 9.7.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-slider': 9.6.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-spinbutton': 9.6.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-spinner': 9.8.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-swatch-picker': 9.5.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-switch': 9.7.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-table': 9.19.14(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-tabs': 9.12.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-tag-picker': 9.8.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-tags': 9.8.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-teaching-popover': 9.6.20(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-text': 9.6.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-textarea': 9.7.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-toast': 9.7.16(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-toolbar': 9.7.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-tooltip': 9.10.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-tree': 9.15.16(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-virtualizer': 9.0.0-alpha.109(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-context-selector@9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + scheduler: 0.27.0 + + '@fluentui/react-dialog@9.17.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-motion': 9.14.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-motion-components-preview': 0.15.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-portal': 9.8.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-divider@9.7.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-drawer@9.11.6(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-dialog': 9.17.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-motion': 9.14.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-motion-components-preview': 0.15.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-portal': 9.8.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-field@9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-label': 9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-focus@8.10.5(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@fluentui/keyboard-key': 0.4.23 + '@fluentui/merge-styles': 8.6.14 + '@fluentui/set-version': 8.2.24 + '@fluentui/style-utilities': 8.15.0(@types/react@19.2.7)(react@19.2.4) + '@fluentui/utilities': 8.17.2(@types/react@19.2.7)(react@19.2.4) + '@types/react': 19.2.7 + react: 19.2.4 + tslib: 2.8.1 + + '@fluentui/react-hooks@8.10.2(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@fluentui/react-window-provider': 2.3.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/set-version': 8.2.24 + '@fluentui/utilities': 8.17.2(@types/react@19.2.7)(react@19.2.4) + '@types/react': 19.2.7 + react: 19.2.4 + tslib: 2.8.1 + + '@fluentui/react-icons@2.0.323(react@19.2.4)': + dependencies: + '@griffel/react': 1.6.1(react@19.2.4) + react: 19.2.4 + tslib: 2.8.1 + + '@fluentui/react-image@9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-infobutton@9.0.0-beta.109(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-label': 9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-popover': 9.14.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-infolabel@9.4.19(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-label': 9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-popover': 9.14.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-input@9.8.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-field': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-jsx-runtime@9.4.1(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + react: 19.2.4 + + '@fluentui/react-label@9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-link@9.8.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-list@9.6.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-checkbox': 9.6.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-menu@9.24.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-motion': 9.14.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-motion-components-preview': 0.15.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-portal': 9.8.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-positioning': 9.22.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-message-bar@9.6.23(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-button': 9.9.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-link': 9.8.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-motion': 9.14.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-motion-components-preview': 0.15.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-motion-components-preview@0.15.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-motion': 9.14.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-motion@9.14.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-nav@9.3.23(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-button': 9.9.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-divider': 9.7.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-drawer': 9.11.6(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-motion': 9.14.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-motion-components-preview': 0.15.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-tooltip': 9.10.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-overflow@9.7.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/priority-overflow': 9.3.0 + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-persona@9.7.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-avatar': 9.11.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-badge': 9.5.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-popover@9.14.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-motion': 9.14.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-motion-components-preview': 0.15.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-portal': 9.8.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-positioning': 9.22.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-portal-compat-context@9.0.15(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + react: 19.2.4 + + '@fluentui/react-portal@9.8.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-positioning@9.22.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@floating-ui/devtools': 0.2.3(@floating-ui/dom@1.7.6) + '@floating-ui/dom': 1.7.6 + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + use-sync-external-store: 1.6.0(react@19.2.4) + + '@fluentui/react-progress@9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-field': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-motion': 9.14.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-provider@9.22.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/core': 1.20.1 + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-radio@9.6.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-field': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-label': 9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-rating@9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-search@9.4.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-input': 9.8.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-select@9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-field': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-shared-contexts@9.26.2(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@fluentui/react-theme': 9.2.1 + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + react: 19.2.4 + + '@fluentui/react-skeleton@9.7.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-field': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-slider@9.6.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-field': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-spinbutton@9.6.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-field': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-spinner@9.8.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-label': 9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-swatch-picker@9.5.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-field': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-switch@9.7.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-field': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-label': 9.4.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-table@9.19.14(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-avatar': 9.11.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-checkbox': 9.6.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-radio': 9.6.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-tabs@9.12.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-tabster@9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + keyborg: 2.6.0 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + tabster: 8.7.0 + + '@fluentui/react-tag-picker@9.8.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-combobox': 9.17.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-field': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-portal': 9.8.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-positioning': 9.22.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-tags': 9.8.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-tags@9.8.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-avatar': 9.11.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-teaching-popover@9.6.20(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-button': 9.9.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-popover': 9.14.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + use-sync-external-store: 1.6.0(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-text@9.6.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-textarea@9.7.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-field': 9.5.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-theme@9.2.1': + dependencies: + '@fluentui/tokens': 1.0.0-alpha.23 + '@swc/helpers': 0.5.21 + + '@fluentui/react-toast@9.7.16(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-motion': 9.14.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-motion-components-preview': 0.15.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-portal': 9.8.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-toolbar@9.7.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/react-button': 9.9.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-divider': 9.7.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-radio': 9.6.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-tooltip@9.10.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-portal': 9.8.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-positioning': 9.22.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-tree@9.15.16(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-aria': 9.17.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-avatar': 9.11.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-button': 9.9.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-checkbox': 9.6.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-context-selector': 9.2.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-icons': 2.0.323(react@19.2.4) + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-motion': 9.14.0(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-motion-components-preview': 0.15.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-radio': 9.6.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(scheduler@0.27.0) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-tabster': 9.26.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@fluentui/react-theme': 9.2.1 + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - scheduler + + '@fluentui/react-utilities@9.26.2(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@fluentui/keyboard-keys': 9.0.8 + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + react: 19.2.4 + + '@fluentui/react-virtualizer@9.0.0-alpha.109(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/react-jsx-runtime': 9.4.1(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-shared-contexts': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-utilities': 9.26.2(@types/react@19.2.7)(react@19.2.4) + '@griffel/react': 1.6.1(react@19.2.4) + '@swc/helpers': 0.5.21 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@fluentui/react-window-provider@2.3.2(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@fluentui/set-version': 8.2.24 + '@types/react': 19.2.7 + react: 19.2.4 + tslib: 2.8.1 + + '@fluentui/react@8.125.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@fluentui/date-time-utilities': 8.6.11 + '@fluentui/font-icons-mdl2': 8.5.72(@types/react@19.2.7)(react@19.2.4) + '@fluentui/foundation-legacy': 8.6.5(@types/react@19.2.7)(react@19.2.4) + '@fluentui/merge-styles': 8.6.14 + '@fluentui/react-focus': 8.10.5(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-hooks': 8.10.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-portal-compat-context': 9.0.15(@types/react@19.2.7)(react@19.2.4) + '@fluentui/react-window-provider': 2.3.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/set-version': 8.2.24 + '@fluentui/style-utilities': 8.15.0(@types/react@19.2.7)(react@19.2.4) + '@fluentui/theme': 2.7.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/utilities': 8.17.2(@types/react@19.2.7)(react@19.2.4) + '@microsoft/load-themed-styles': 1.10.295 + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + tslib: 2.8.1 + + '@fluentui/set-version@8.2.24': + dependencies: + tslib: 2.8.1 + + '@fluentui/style-utilities@8.15.0(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@fluentui/merge-styles': 8.6.14 + '@fluentui/set-version': 8.2.24 + '@fluentui/theme': 2.7.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/utilities': 8.17.2(@types/react@19.2.7)(react@19.2.4) + '@microsoft/load-themed-styles': 1.10.295 + tslib: 2.8.1 + transitivePeerDependencies: + - '@types/react' + - react + + '@fluentui/theme@2.7.2(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@fluentui/merge-styles': 8.6.14 + '@fluentui/set-version': 8.2.24 + '@fluentui/utilities': 8.17.2(@types/react@19.2.7)(react@19.2.4) + '@types/react': 19.2.7 + react: 19.2.4 + tslib: 2.8.1 + + '@fluentui/tokens@1.0.0-alpha.23': + dependencies: + '@swc/helpers': 0.5.21 + + '@fluentui/utilities@8.17.2(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@fluentui/dom-utilities': 2.3.10 + '@fluentui/merge-styles': 8.6.14 + '@fluentui/react-window-provider': 2.3.2(@types/react@19.2.7)(react@19.2.4) + '@fluentui/set-version': 8.2.24 + '@types/react': 19.2.7 + react: 19.2.4 + tslib: 2.8.1 + + '@gar/promisify@1.1.3': {} + + '@griffel/core@1.20.1': + dependencies: + '@emotion/hash': 0.9.2 + '@griffel/style-types': 1.4.0 + csstype: 3.2.3 + rtl-css-js: 1.16.1 + stylis: 4.3.6 + tslib: 2.8.1 + + '@griffel/react@1.6.1(react@19.2.4)': + dependencies: + '@griffel/core': 1.20.1 + react: 19.2.4 + tslib: 2.8.1 + + '@griffel/style-types@1.4.0': + dependencies: + csstype: 3.2.3 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/config-array@0.10.7': + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/config-array@0.5.0': + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/config-array@0.9.5': + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/gitignore-to-minimatch@1.0.2': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@1.2.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@inquirer/ansi@2.0.5': {} + + '@inquirer/checkbox@5.1.3(@types/node@22.9.3)': + dependencies: + '@inquirer/ansi': 2.0.5 + '@inquirer/core': 11.1.8(@types/node@22.9.3) + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@22.9.3) + optionalDependencies: + '@types/node': 22.9.3 + + '@inquirer/confirm@6.0.11(@types/node@22.9.3)': + dependencies: + '@inquirer/core': 11.1.8(@types/node@22.9.3) + '@inquirer/type': 4.0.5(@types/node@22.9.3) + optionalDependencies: + '@types/node': 22.9.3 + + '@inquirer/core@11.1.8(@types/node@22.9.3)': + dependencies: + '@inquirer/ansi': 2.0.5 + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@22.9.3) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.0 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 22.9.3 + + '@inquirer/figures@2.0.5': {} + + '@inquirer/input@5.0.11(@types/node@22.9.3)': + dependencies: + '@inquirer/core': 11.1.8(@types/node@22.9.3) + '@inquirer/type': 4.0.5(@types/node@22.9.3) + optionalDependencies: + '@types/node': 22.9.3 + + '@inquirer/search@4.1.7(@types/node@22.9.3)': + dependencies: + '@inquirer/core': 11.1.8(@types/node@22.9.3) + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@22.9.3) + optionalDependencies: + '@types/node': 22.9.3 + + '@inquirer/select@5.1.3(@types/node@22.9.3)': + dependencies: + '@inquirer/ansi': 2.0.5 + '@inquirer/core': 11.1.8(@types/node@22.9.3) + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@22.9.3) + optionalDependencies: + '@types/node': 22.9.3 + + '@inquirer/type@4.0.5(@types/node@22.9.3)': + optionalDependencies: + '@types/node': 22.9.3 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/cliui@9.0.0': {} + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.2 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.9.3 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + + '@jest/console@30.3.0': + dependencies: + '@jest/types': 30.3.0 + '@types/node': 22.9.3 + chalk: 4.1.2 + jest-message-util: 30.3.0 + jest-util: 30.3.0 + slash: 3.0.0 + + '@jest/core@29.5.0(babel-plugin-macros@3.1.0)': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0(@types/node@22.9.3) + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.9.3 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@22.9.3)(babel-plugin-macros@3.1.0) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/core@29.7.0(babel-plugin-macros@3.1.0)': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0(@types/node@22.9.3) + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.9.3 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@22.9.3)(babel-plugin-macros@3.1.0) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/core@30.3.0': + dependencies: + '@jest/console': 30.3.0 + '@jest/pattern': 30.0.1 + '@jest/reporters': 30.3.0 + '@jest/test-result': 30.3.0(@types/node@22.9.3) + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 22.9.3 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 4.4.0 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-changed-files: 30.3.0 + jest-config: 30.3.0(@types/node@22.9.3) + jest-haste-map: 30.3.0 + jest-message-util: 30.3.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.3.0 + jest-resolve-dependencies: 30.3.0 + jest-runner: 30.3.0 + jest-runtime: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + jest-watcher: 30.3.0 + pretty-format: 30.3.0 + slash: 3.0.0 + transitivePeerDependencies: + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + + '@jest/diff-sequences@30.3.0': {} + + '@jest/environment-jsdom-abstract@30.3.0(jsdom@26.1.0)': + dependencies: + '@jest/environment': 30.3.0 + '@jest/fake-timers': 30.3.0 + '@jest/types': 30.3.0 + '@types/jsdom': 21.1.7 + '@types/node': 22.9.3 + jest-mock: 30.3.0 + jest-util: 30.3.0 + jsdom: 26.1.0 + + '@jest/environment@29.7.0': + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.9.3 + jest-mock: 29.7.0 + + '@jest/environment@30.3.0': + dependencies: + '@jest/fake-timers': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 22.9.3 + jest-mock: 30.3.0 + + '@jest/expect-utils@29.7.0': + dependencies: + jest-get-type: 29.6.3 + + '@jest/expect-utils@30.3.0': + dependencies: + '@jest/get-type': 30.1.0 + + '@jest/expect@29.7.0': + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/expect@30.3.0': + dependencies: + expect: 30.3.0 + jest-snapshot: 30.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 22.9.3 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + '@jest/fake-timers@30.3.0': + dependencies: + '@jest/types': 30.3.0 + '@sinonjs/fake-timers': 15.3.0 + '@types/node': 22.9.3 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 + jest-util: 30.3.0 + + '@jest/get-type@30.1.0': {} + + '@jest/globals@29.7.0': + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/globals@30.3.0': + dependencies: + '@jest/environment': 30.3.0 + '@jest/expect': 30.3.0 + '@jest/types': 30.3.0 + jest-mock: 30.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/pattern@30.0.1': + dependencies: + '@types/node': 22.9.3 + jest-regex-util: 30.0.1 + + '@jest/reporters@29.7.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0(@types/node@22.9.3) + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/node': 22.9.3 + chalk: 4.1.2 + collect-v8-coverage: 1.0.3(@types/node@22.9.3) + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.2.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@30.3.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 30.3.0 + '@jest/test-result': 30.3.0(@types/node@22.9.3) + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@jridgewell/trace-mapping': 0.3.31 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/node': 22.9.3 + chalk: 4.1.2 + collect-v8-coverage: 1.0.3(@types/node@22.9.3) + exit-x: 0.2.2 + glob: 10.5.0 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + jest-message-util: 30.3.0 + jest-util: 30.3.0 + jest-worker: 30.3.0 + slash: 3.0.0 + string-length: 4.0.2 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.10 + + '@jest/schemas@30.0.5': + dependencies: + '@sinclair/typebox': 0.34.49 + + '@jest/snapshot-utils@30.3.0': + dependencies: + '@jest/types': 30.3.0 + chalk: 4.1.2 + graceful-fs: 4.2.11 + natural-compare: 1.4.0 + + '@jest/source-map@29.6.3': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/source-map@30.0.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@29.7.0(@types/node@20.17.19)': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.3(@types/node@20.17.19) + jest-haste-map: 29.7.0 + jest-resolve: 29.7.0 + transitivePeerDependencies: + - '@types/node' + + '@jest/test-result@29.7.0(@types/node@22.9.3)': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.3(@types/node@22.9.3) + jest-haste-map: 29.7.0 + jest-resolve: 29.7.0 + transitivePeerDependencies: + - '@types/node' + + '@jest/test-result@30.3.0(@types/node@20.17.19)': + dependencies: + '@jest/console': 30.3.0 + '@jest/types': 30.3.0 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.3(@types/node@20.17.19) + jest-haste-map: 30.3.0 + jest-resolve: 30.3.0 + transitivePeerDependencies: + - '@types/node' + + '@jest/test-result@30.3.0(@types/node@22.9.3)': + dependencies: + '@jest/console': 30.3.0 + '@jest/types': 30.3.0 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.3(@types/node@22.9.3) + jest-haste-map: 30.3.0 + jest-resolve: 30.3.0 + transitivePeerDependencies: + - '@types/node' + + '@jest/test-sequencer@29.7.0(@types/node@20.17.19)': + dependencies: + '@jest/test-result': 29.7.0(@types/node@20.17.19) + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + transitivePeerDependencies: + - '@types/node' + + '@jest/test-sequencer@29.7.0(@types/node@22.9.3)': + dependencies: + '@jest/test-result': 29.7.0(@types/node@22.9.3) + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + transitivePeerDependencies: + - '@types/node' + + '@jest/test-sequencer@30.3.0(@types/node@20.17.19)': + dependencies: + '@jest/test-result': 30.3.0(@types/node@20.17.19) + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + slash: 3.0.0 + transitivePeerDependencies: + - '@types/node' + + '@jest/test-sequencer@30.3.0(@types/node@22.9.3)': + dependencies: + '@jest/test-result': 30.3.0(@types/node@22.9.3) + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + slash: 3.0.0 + transitivePeerDependencies: + - '@types/node' + + '@jest/transform@26.6.2': + dependencies: + '@babel/core': 7.20.12 + '@jest/types': 26.6.2 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 1.9.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 26.6.2 + jest-regex-util: 26.0.0 + jest-util: 26.6.2 + micromatch: 4.0.8 + pirates: 4.0.7 + slash: 3.0.0 + source-map: 0.6.1 + write-file-atomic: 3.0.3 + transitivePeerDependencies: + - supports-color + + '@jest/transform@29.7.0': + dependencies: + '@babel/core': 7.20.12 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.8 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/transform@30.3.0': + dependencies: + '@babel/core': 7.29.0 + '@jest/types': 30.3.0 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 7.0.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + jest-regex-util: 30.0.1 + jest-util: 30.3.0 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + + '@jest/types@25.5.0': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 1.1.2 + '@types/yargs': 15.0.20 + chalk: 3.0.0 + + '@jest/types@26.6.2': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 22.9.3 + '@types/yargs': 15.0.20 + chalk: 4.1.2 + + '@jest/types@29.5.0': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 22.9.3 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 22.9.3 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + + '@jest/types@30.3.0': + dependencies: + '@jest/pattern': 30.0.1 + '@jest/schemas': 30.0.5 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 22.9.3 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jsdevtools/ono@7.1.3': {} + + '@jsep-plugin/assignment@1.3.0(jsep@1.4.0)': + dependencies: + jsep: 1.4.0 + + '@jsep-plugin/regex@1.0.4(jsep@1.4.0)': + dependencies: + jsep: 1.4.0 + + '@jsonjoy.com/base64@1.1.2(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/base64@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/buffers@1.2.1(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/buffers@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/codegen@1.0.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/codegen@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/fs-core@4.57.1(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.1(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-fsa@4.57.1(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.1(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-builtins@4.57.1(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-to-fsa@4.57.1(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-fsa': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.1(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-utils@4.57.1(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.57.1(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node@4.57.1(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.57.1(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-print@4.57.1(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-utils': 4.57.1(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-snapshot@4.57.1(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/json-pack': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@1.21.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 1.0.2(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@1.0.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@1.9.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@leichtgewicht/ip-codec@2.0.5': {} + + '@mdx-js/loader@1.6.22(react@17.0.2)': + dependencies: + '@mdx-js/mdx': 1.6.22 + '@mdx-js/react': 1.6.22(react@17.0.2) + loader-utils: 2.0.4 + transitivePeerDependencies: + - react + - supports-color + + '@mdx-js/mdx@1.6.22': + dependencies: + '@babel/core': 7.12.9 + '@babel/plugin-syntax-jsx': 7.12.1(@babel/core@7.12.9) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.12.9) + '@mdx-js/util': 1.6.22 + babel-plugin-apply-mdx-type-prop: 1.6.22(@babel/core@7.12.9) + babel-plugin-extract-import-names: 1.6.22 + camelcase-css: 2.0.1 + detab: 2.0.4 + hast-util-raw: 6.0.1 + lodash.uniq: 4.5.0 + mdast-util-to-hast: 10.0.1 + remark-footnotes: 2.0.0 + remark-mdx: 1.6.22 + remark-parse: 8.0.3 + remark-squeeze-paragraphs: 4.0.0 + style-to-object: 0.3.0 + unified: 9.2.0 + unist-builder: 2.0.3 + unist-util-visit: 2.0.3 + transitivePeerDependencies: + - supports-color + + '@mdx-js/react@1.6.22(react@17.0.2)': + dependencies: + react: 17.0.2 + + '@mdx-js/util@1.6.22': {} + + '@microsoft/api-extractor-model@7.33.10(@types/node@20.17.19)': + dependencies: + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.1 + '@rushstack/node-core-library': 5.23.3(@types/node@20.17.19) + transitivePeerDependencies: + - '@types/node' + + '@microsoft/api-extractor@7.58.12(@types/node@20.17.19)': + dependencies: + '@microsoft/api-extractor-model': 7.33.10(@types/node@20.17.19) + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.1 + '@rushstack/node-core-library': 5.23.3(@types/node@20.17.19) + '@rushstack/rig-package': 0.7.3 + '@rushstack/terminal': 0.24.2(@types/node@20.17.19) + '@rushstack/ts-command-line': 5.3.12(@types/node@20.17.19) + diff: 8.0.4 + minimatch: 10.2.3 + resolve: 1.22.11 + semver: 7.7.4 + source-map: 0.6.1 + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/node' + + '@microsoft/load-themed-styles@1.10.295': {} + + '@microsoft/teams-js@1.3.0-beta.4': {} + + '@microsoft/tsdoc-config@0.17.0': + dependencies: + '@microsoft/tsdoc': 0.15.0 + ajv: 8.12.0 + jju: 1.4.0 + resolve: 1.22.11 + + '@microsoft/tsdoc-config@0.18.1': + dependencies: + '@microsoft/tsdoc': 0.16.0 + ajv: 8.18.0 + jju: 1.4.0 + resolve: 1.22.11 + + '@microsoft/tsdoc@0.15.0': {} + + '@microsoft/tsdoc@0.16.0': {} + + '@modelcontextprotocol/sdk@1.10.2': + dependencies: + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + express: 5.2.1 + express-rate-limit: 7.5.1(express@5.2.1) + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.2(zod@3.25.76) + transitivePeerDependencies: + - supports-color + + '@module-federation/error-codes@0.21.6': {} + + '@module-federation/runtime-core@0.21.6': + dependencies: + '@module-federation/error-codes': 0.21.6 + '@module-federation/sdk': 0.21.6 + + '@module-federation/runtime-tools@0.21.6': + dependencies: + '@module-federation/runtime': 0.21.6 + '@module-federation/webpack-bundler-runtime': 0.21.6 + + '@module-federation/runtime@0.21.6': + dependencies: + '@module-federation/error-codes': 0.21.6 + '@module-federation/runtime-core': 0.21.6 + '@module-federation/sdk': 0.21.6 + + '@module-federation/sdk@0.21.6': {} + + '@module-federation/webpack-bundler-runtime@0.21.6': + dependencies: + '@module-federation/runtime': 0.21.6 + '@module-federation/sdk': 0.21.6 + + '@mrmlnc/readdir-enhanced@2.2.1': + dependencies: + call-me-maybe: 1.0.2 + glob-to-regexp: 0.3.0 + + '@napi-rs/wasm-runtime@0.2.12': + dependencies: + '@emnapi/core': 1.9.2 + '@emnapi/runtime': 1.9.2 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@napi-rs/wasm-runtime@1.0.7': + dependencies: + '@emnapi/core': 1.9.2 + '@emnapi/runtime': 1.9.2 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@noble/hashes@1.4.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@1.1.3': {} + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@npmcli/fs@1.1.1': + dependencies: + '@gar/promisify': 1.1.3 + semver: 7.7.4 + + '@npmcli/move-file@1.1.2': + dependencies: + mkdirp: 1.0.4 + rimraf: 3.0.2 + + '@peculiar/asn1-cms@2.6.1': + dependencies: + '@peculiar/asn1-schema': 2.6.0 + '@peculiar/asn1-x509': 2.6.1 + '@peculiar/asn1-x509-attr': 2.6.1 + asn1js: 3.0.7 + tslib: 2.8.1 + + '@peculiar/asn1-csr@2.6.1': + dependencies: + '@peculiar/asn1-schema': 2.6.0 + '@peculiar/asn1-x509': 2.6.1 + asn1js: 3.0.7 + tslib: 2.8.1 + + '@peculiar/asn1-ecc@2.6.1': + dependencies: + '@peculiar/asn1-schema': 2.6.0 + '@peculiar/asn1-x509': 2.6.1 + asn1js: 3.0.7 + tslib: 2.8.1 + + '@peculiar/asn1-pfx@2.6.1': + dependencies: + '@peculiar/asn1-cms': 2.6.1 + '@peculiar/asn1-pkcs8': 2.6.1 + '@peculiar/asn1-rsa': 2.6.1 + '@peculiar/asn1-schema': 2.6.0 + asn1js: 3.0.7 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs8@2.6.1': + dependencies: + '@peculiar/asn1-schema': 2.6.0 + '@peculiar/asn1-x509': 2.6.1 + asn1js: 3.0.7 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs9@2.6.1': + dependencies: + '@peculiar/asn1-cms': 2.6.1 + '@peculiar/asn1-pfx': 2.6.1 + '@peculiar/asn1-pkcs8': 2.6.1 + '@peculiar/asn1-schema': 2.6.0 + '@peculiar/asn1-x509': 2.6.1 + '@peculiar/asn1-x509-attr': 2.6.1 + asn1js: 3.0.7 + tslib: 2.8.1 + + '@peculiar/asn1-rsa@2.6.1': + dependencies: + '@peculiar/asn1-schema': 2.6.0 + '@peculiar/asn1-x509': 2.6.1 + asn1js: 3.0.7 + tslib: 2.8.1 + + '@peculiar/asn1-schema@2.6.0': + dependencies: + asn1js: 3.0.7 + pvtsutils: 1.3.6 + tslib: 2.8.1 + + '@peculiar/asn1-x509-attr@2.6.1': + dependencies: + '@peculiar/asn1-schema': 2.6.0 + '@peculiar/asn1-x509': 2.6.1 + asn1js: 3.0.7 + tslib: 2.8.1 + + '@peculiar/asn1-x509@2.6.1': + dependencies: + '@peculiar/asn1-schema': 2.6.0 + asn1js: 3.0.7 + pvtsutils: 1.3.6 + tslib: 2.8.1 + + '@peculiar/x509@1.14.3': + dependencies: + '@peculiar/asn1-cms': 2.6.1 + '@peculiar/asn1-csr': 2.6.1 + '@peculiar/asn1-ecc': 2.6.1 + '@peculiar/asn1-pkcs9': 2.6.1 + '@peculiar/asn1-rsa': 2.6.1 + '@peculiar/asn1-schema': 2.6.0 + '@peculiar/asn1-x509': 2.6.1 + pvtsutils: 1.3.6 + reflect-metadata: 0.2.2 + tslib: 2.8.1 + tsyringe: 4.10.0 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pkgr/core@0.2.9': {} + + '@playwright/test@1.56.1': + dependencies: + playwright: 1.56.1 + + '@pmmmwh/react-refresh-webpack-plugin@0.5.17(@types/webpack@4.41.32)(react-refresh@0.11.0)(type-fest@0.21.3)(webpack-dev-server@5.2.3(@types/webpack@4.41.32)(webpack@4.47.0))(webpack-hot-middleware@2.26.1)(webpack@4.47.0)': + dependencies: + ansi-html: 0.0.9 + core-js-pure: 3.49.0 + error-stack-parser: 2.1.4 + html-entities: 2.6.0 + loader-utils: 2.0.4 + react-refresh: 0.11.0 + schema-utils: 4.3.3 + source-map: 0.7.6 + webpack: 4.47.0 + optionalDependencies: + '@types/webpack': 4.41.32 + type-fest: 0.21.3 + webpack-dev-server: 5.2.3(@types/webpack@4.41.32)(webpack@4.47.0) + webpack-hot-middleware: 2.26.1 + + '@pnpm/constants@1001.3.1': {} + + '@pnpm/constants@7.1.1': {} + + '@pnpm/crypto.base32-hash@1.0.1': + dependencies: + rfc4648: 1.5.4 + + '@pnpm/crypto.base32-hash@2.0.0': + dependencies: + rfc4648: 1.5.4 + + '@pnpm/crypto.base32-hash@3.0.1': + dependencies: + '@pnpm/crypto.polyfill': 1.0.0 + rfc4648: 1.5.4 + + '@pnpm/crypto.hash@1000.1.1': + dependencies: + '@pnpm/crypto.polyfill': 1000.1.0 + '@pnpm/graceful-fs': 1000.0.0 + ssri: 10.0.5 + + '@pnpm/crypto.hash@1000.2.2': + dependencies: + '@pnpm/crypto.polyfill': 1000.1.0 + '@pnpm/graceful-fs': 1000.1.0 + ssri: 10.0.5 + + '@pnpm/crypto.polyfill@1.0.0': {} + + '@pnpm/crypto.polyfill@1000.1.0': {} + + '@pnpm/dependency-path@1000.0.9': + dependencies: + '@pnpm/crypto.hash': 1000.1.1 + '@pnpm/types': 1000.6.0 + semver: 7.7.4 + + '@pnpm/dependency-path@1001.1.10': + dependencies: + '@pnpm/crypto.hash': 1000.2.2 + '@pnpm/types': 1001.3.0 + semver: 7.7.4 + + '@pnpm/dependency-path@2.1.8': + dependencies: + '@pnpm/crypto.base32-hash': 2.0.0 + '@pnpm/types': 9.4.2 + encode-registry: 3.0.1 + semver: 7.7.4 + + '@pnpm/dependency-path@5.1.7': + dependencies: + '@pnpm/crypto.base32-hash': 3.0.1 + '@pnpm/types': 12.2.0 + semver: 7.7.4 + + '@pnpm/error@1.4.0': {} + + '@pnpm/error@1000.1.0': + dependencies: + '@pnpm/constants': 1001.3.1 + + '@pnpm/error@5.0.3': + dependencies: + '@pnpm/constants': 7.1.1 + + '@pnpm/git-utils@1.0.0': + dependencies: + execa: safe-execa@0.1.2 + + '@pnpm/git-utils@1000.0.0': + dependencies: + execa: safe-execa@0.1.2 + + '@pnpm/graceful-fs@1000.0.0': + dependencies: + graceful-fs: 4.2.11 + + '@pnpm/graceful-fs@1000.1.0': + dependencies: + graceful-fs: 4.2.11 + + '@pnpm/link-bins@5.3.25': + dependencies: + '@pnpm/error': 1.4.0 + '@pnpm/package-bins': 4.1.0 + '@pnpm/read-modules-dir': 2.0.3 + '@pnpm/read-package-json': 4.0.0 + '@pnpm/read-project-manifest': 1.1.7 + '@pnpm/types': 6.4.0 + '@zkochan/cmd-shim': 5.4.1 + is-subdir: 1.2.0 + is-windows: 1.0.2 + mz: 2.7.0 + normalize-path: 3.0.0 + p-settle: 4.1.1 + ramda: 0.27.2 + + '@pnpm/lockfile-file@8.1.8(@pnpm/logger@5.0.0)': + dependencies: + '@pnpm/constants': 7.1.1 + '@pnpm/dependency-path': 2.1.8 + '@pnpm/error': 5.0.3 + '@pnpm/git-utils': 1.0.0 + '@pnpm/lockfile-types': 5.1.5 + '@pnpm/logger': 5.0.0 + '@pnpm/merge-lockfile-changes': 5.0.7 + '@pnpm/types': 9.4.2 + '@pnpm/util.lex-comparator': 1.0.0 + '@zkochan/rimraf': 2.1.3 + comver-to-semver: 1.0.0 + js-yaml: '@zkochan/js-yaml@0.0.6' + normalize-path: 3.0.0 + ramda: '@pnpm/ramda@0.28.1' + semver: 7.7.4 + sort-keys: 4.2.0 + strip-bom: 4.0.0 + write-file-atomic: 5.0.1 + + '@pnpm/lockfile-types@5.1.5': + dependencies: + '@pnpm/types': 9.4.2 + + '@pnpm/lockfile.fs@1001.1.32(@pnpm/logger@1001.0.1)': + dependencies: + '@pnpm/constants': 1001.3.1 + '@pnpm/dependency-path': 1001.1.10 + '@pnpm/error': 1000.1.0 + '@pnpm/git-utils': 1000.0.0 + '@pnpm/lockfile.merger': 1001.0.20 + '@pnpm/lockfile.types': 1002.1.0 + '@pnpm/lockfile.utils': 1004.0.3 + '@pnpm/logger': 1001.0.1 + '@pnpm/object.key-sorting': 1000.0.1 + '@pnpm/types': 1001.3.0 + '@zkochan/rimraf': 3.0.2 + comver-to-semver: 1.0.0 + js-yaml: '@zkochan/js-yaml@0.0.11' + normalize-path: 3.0.0 + ramda: '@pnpm/ramda@0.28.1' + semver: 7.7.4 + strip-bom: 4.0.0 + write-file-atomic: 5.0.1 + + '@pnpm/lockfile.merger@1001.0.20': + dependencies: + '@pnpm/lockfile.types': 1002.1.0 + '@pnpm/types': 1001.3.0 + comver-to-semver: 1.0.0 + ramda: '@pnpm/ramda@0.28.1' + semver: 7.7.4 + + '@pnpm/lockfile.types@1001.1.0': + dependencies: + '@pnpm/patching.types': 1000.1.0 + '@pnpm/types': 1000.7.0 + + '@pnpm/lockfile.types@1002.0.1': + dependencies: + '@pnpm/patching.types': 1000.1.0 + '@pnpm/resolver-base': 1005.0.1 + '@pnpm/types': 1000.8.0 + + '@pnpm/lockfile.types@1002.1.0': + dependencies: + '@pnpm/patching.types': 1000.1.0 + '@pnpm/resolver-base': 1005.4.1 + '@pnpm/types': 1001.3.0 + + '@pnpm/lockfile.types@900.0.0': + dependencies: + '@pnpm/patching.types': 900.0.0 + '@pnpm/types': 900.0.0 + + '@pnpm/lockfile.utils@1004.0.3': + dependencies: + '@pnpm/dependency-path': 1001.1.10 + '@pnpm/lockfile.types': 1002.1.0 + '@pnpm/pick-fetcher': 1001.0.0 + '@pnpm/resolver-base': 1005.4.1 + '@pnpm/types': 1001.3.0 + get-npm-tarball-url: 2.1.0 + ramda: '@pnpm/ramda@0.28.1' + + '@pnpm/logger@1001.0.1': + dependencies: + bole: 5.0.28 + split2: 4.2.0 + + '@pnpm/logger@5.0.0': + dependencies: + bole: 5.0.28 + ndjson: 2.0.0 + + '@pnpm/merge-lockfile-changes@5.0.7': + dependencies: + '@pnpm/lockfile-types': 5.1.5 + comver-to-semver: 1.0.0 + ramda: '@pnpm/ramda@0.28.1' + semver: 7.7.4 + + '@pnpm/object.key-sorting@1000.0.1': + dependencies: + '@pnpm/util.lex-comparator': 3.0.2 + sort-keys: 4.2.0 + + '@pnpm/package-bins@4.1.0': + dependencies: + '@pnpm/types': 6.4.0 + fast-glob: 3.3.3 + is-subdir: 1.2.0 + + '@pnpm/patching.types@1000.1.0': {} + + '@pnpm/patching.types@900.0.0': {} + + '@pnpm/pick-fetcher@1001.0.0': {} + + '@pnpm/ramda@0.28.1': {} + + '@pnpm/read-modules-dir@2.0.3': + dependencies: + mz: 2.7.0 + + '@pnpm/read-package-json@4.0.0': + dependencies: + '@pnpm/error': 1.4.0 + '@pnpm/types': 6.4.0 + load-json-file: 6.2.0 + normalize-package-data: 3.0.3 + + '@pnpm/read-project-manifest@1.1.7': + dependencies: + '@pnpm/error': 1.4.0 + '@pnpm/types': 6.4.0 + '@pnpm/write-project-manifest': 1.1.7 + detect-indent: 6.1.0 + fast-deep-equal: 3.1.3 + graceful-fs: 4.2.4 + is-windows: 1.0.2 + json5: 2.2.3 + parse-json: 5.2.0 + read-yaml-file: 2.1.0 + sort-keys: 4.2.0 + strip-bom: 4.0.0 + + '@pnpm/resolver-base@1005.0.1': + dependencies: + '@pnpm/types': 1000.8.0 + + '@pnpm/resolver-base@1005.4.1': + dependencies: + '@pnpm/types': 1001.3.0 + + '@pnpm/types@1000.6.0': {} + + '@pnpm/types@1000.7.0': {} + + '@pnpm/types@1000.8.0': {} + + '@pnpm/types@1001.3.0': {} + + '@pnpm/types@12.2.0': {} + + '@pnpm/types@6.4.0': {} + + '@pnpm/types@8.9.0': {} + + '@pnpm/types@9.4.2': {} + + '@pnpm/types@900.0.0': {} + + '@pnpm/util.lex-comparator@1.0.0': {} + + '@pnpm/util.lex-comparator@3.0.2': {} + + '@pnpm/write-project-manifest@1.1.7': + dependencies: + '@pnpm/types': 6.4.0 + json5: 2.2.3 + mz: 2.7.0 + write-file-atomic: 3.0.3 + write-yaml-file: 4.2.0 + + '@polka/url@1.0.0-next.29': {} + + '@popperjs/core@2.11.8': {} + + '@pothos/core@3.41.2(graphql@16.13.2)': + dependencies: + graphql: 16.13.2 + optional: true + + '@radix-ui/colors@3.0.0': {} + + '@radix-ui/number@1.1.1': {} + + '@radix-ui/primitive@1.1.3': {} + + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-use-previous': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-use-size': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-slot': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + + '@radix-ui/react-compose-refs@1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.7 + + '@radix-ui/react-context@1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.7 + + '@radix-ui/react-direction@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.7 + + '@radix-ui/react-icons@1.3.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + + '@radix-ui/react-id@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.7 + + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-compose-refs': 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-direction': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-id': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-context': 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-direction': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + + '@radix-ui/react-slot@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.7 + + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-direction': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-id': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + + '@radix-ui/react-use-callback-ref@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.7 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.7 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.7 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.7 + + '@radix-ui/react-use-previous@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.7 + + '@radix-ui/react-use-size@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react@19.2.4) + '@types/react-dom': 19.2.3(@types/react@19.2.7) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.7 + + '@redis/client@5.8.3': + dependencies: + cluster-key-slot: 1.1.2 + + '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.4)(redux@5.0.1))(react@19.2.4)': + dependencies: + '@standard-schema/spec': 1.1.0 + '@standard-schema/utils': 0.3.0 + immer: 11.1.4 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.1.1 + optionalDependencies: + react: 19.2.4 + react-redux: 9.2.0(@types/react@19.2.7)(react@19.2.4)(redux@5.0.1) + + '@remix-run/router@1.23.2': {} + + '@rollup/rollup-linux-x64-gnu@4.53.3': + optional: true + + '@rspack/binding-darwin-arm64@1.6.8': + optional: true + + '@rspack/binding-darwin-x64@1.6.8': + optional: true + + '@rspack/binding-linux-arm64-gnu@1.6.8': + optional: true + + '@rspack/binding-linux-arm64-musl@1.6.8': + optional: true + + '@rspack/binding-linux-x64-gnu@1.6.8': + optional: true + + '@rspack/binding-linux-x64-musl@1.6.8': + optional: true + + '@rspack/binding-wasm32-wasi@1.6.8': + dependencies: + '@napi-rs/wasm-runtime': 1.0.7 + optional: true + + '@rspack/binding-win32-arm64-msvc@1.6.8': + optional: true + + '@rspack/binding-win32-ia32-msvc@1.6.8': + optional: true + + '@rspack/binding-win32-x64-msvc@1.6.8': + optional: true + + '@rspack/binding@1.6.8': + optionalDependencies: + '@rspack/binding-darwin-arm64': 1.6.8 + '@rspack/binding-darwin-x64': 1.6.8 + '@rspack/binding-linux-arm64-gnu': 1.6.8 + '@rspack/binding-linux-arm64-musl': 1.6.8 + '@rspack/binding-linux-x64-gnu': 1.6.8 + '@rspack/binding-linux-x64-musl': 1.6.8 + '@rspack/binding-wasm32-wasi': 1.6.8 + '@rspack/binding-win32-arm64-msvc': 1.6.8 + '@rspack/binding-win32-ia32-msvc': 1.6.8 + '@rspack/binding-win32-x64-msvc': 1.6.8 + + '@rspack/core@1.6.8(@swc/helpers@0.5.21)': + dependencies: + '@module-federation/runtime-tools': 0.21.6 + '@rspack/binding': 1.6.8 + '@rspack/lite-tapable': 1.1.0 + optionalDependencies: + '@swc/helpers': 0.5.21 + + '@rspack/dev-server@1.2.1(@rspack/core@1.6.8(@swc/helpers@0.5.21))(@types/webpack@4.41.32)(webpack@5.105.4)': + dependencies: + '@rspack/core': 1.6.8(@swc/helpers@0.5.21) + '@types/bonjour': 3.5.13 + '@types/connect-history-api-fallback': 1.5.4 + '@types/express': 4.17.25 + '@types/express-serve-static-core': 4.19.8 + '@types/serve-index': 1.9.4 + '@types/serve-static': 1.15.10 + '@types/sockjs': 0.3.36 + '@types/ws': 8.18.1 + ansi-html-community: 0.0.8 + bonjour-service: 1.3.0 + chokidar: 3.6.0 + colorette: 2.0.20 + compression: 1.8.1 + connect-history-api-fallback: 2.0.0 + express: 4.22.1 + graceful-fs: 4.2.11 + http-proxy-middleware: 2.0.9 + ipaddr.js: 2.3.0 + launch-editor: 2.13.2 + open: 10.2.0 + p-retry: 6.2.1 + schema-utils: 4.3.3 + selfsigned: 2.4.1 + serve-index: 1.9.2 + sockjs: 0.3.24 + spdy: 4.0.2 + webpack-dev-middleware: 7.4.5(@types/webpack@4.41.32)(webpack@5.105.4) + ws: 8.21.0 + transitivePeerDependencies: + - '@types/webpack' + - bufferutil + - debug + - supports-color + - utf-8-validate + - webpack + + '@rspack/lite-tapable@1.1.0': {} + + '@rtsao/scc@1.1.0': {} + + '@rushstack/eslint-config@3.7.1(eslint@7.11.0)(typescript@5.8.2)': + dependencies: + '@rushstack/eslint-patch': 1.10.4 + '@rushstack/eslint-plugin': 0.15.2(eslint@7.11.0)(typescript@5.8.2) + '@rushstack/eslint-plugin-packlets': 0.9.2(eslint@7.11.0)(typescript@5.8.2) + '@rushstack/eslint-plugin-security': 0.8.2(eslint@7.11.0)(typescript@5.8.2) + '@typescript-eslint/eslint-plugin': 6.19.1(@typescript-eslint/parser@6.19.1(eslint@7.11.0)(typescript@5.8.2))(eslint@7.11.0)(typescript@5.8.2) + '@typescript-eslint/parser': 6.19.1(eslint@7.11.0)(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + '@typescript-eslint/utils': 6.19.1(eslint@7.11.0)(typescript@5.8.2) + eslint: 7.11.0 + eslint-plugin-promise: 6.1.1(eslint@7.11.0) + eslint-plugin-react: 7.33.2(eslint@7.11.0) + eslint-plugin-tsdoc: 0.3.0 + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@rushstack/eslint-config@3.7.1(eslint@7.30.0)(typescript@5.8.2)': + dependencies: + '@rushstack/eslint-patch': 1.10.4 + '@rushstack/eslint-plugin': 0.15.2(eslint@7.30.0)(typescript@5.8.2) + '@rushstack/eslint-plugin-packlets': 0.9.2(eslint@7.30.0)(typescript@5.8.2) + '@rushstack/eslint-plugin-security': 0.8.2(eslint@7.30.0)(typescript@5.8.2) + '@typescript-eslint/eslint-plugin': 6.19.1(@typescript-eslint/parser@6.19.1(eslint@7.30.0)(typescript@5.8.2))(eslint@7.30.0)(typescript@5.8.2) + '@typescript-eslint/parser': 6.19.1(eslint@7.30.0)(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + '@typescript-eslint/utils': 6.19.1(eslint@7.30.0)(typescript@5.8.2) + eslint: 7.30.0 + eslint-plugin-promise: 6.1.1(eslint@7.30.0) + eslint-plugin-react: 7.33.2(eslint@7.30.0) + eslint-plugin-tsdoc: 0.3.0 + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@rushstack/eslint-config@3.7.1(eslint@7.7.0)(typescript@5.8.2)': + dependencies: + '@rushstack/eslint-patch': 1.10.4 + '@rushstack/eslint-plugin': 0.15.2(eslint@7.7.0)(typescript@5.8.2) + '@rushstack/eslint-plugin-packlets': 0.9.2(eslint@7.7.0)(typescript@5.8.2) + '@rushstack/eslint-plugin-security': 0.8.2(eslint@7.7.0)(typescript@5.8.2) + '@typescript-eslint/eslint-plugin': 6.19.1(@typescript-eslint/parser@6.19.1(eslint@7.7.0)(typescript@5.8.2))(eslint@7.7.0)(typescript@5.8.2) + '@typescript-eslint/parser': 6.19.1(eslint@7.7.0)(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + '@typescript-eslint/utils': 6.19.1(eslint@7.7.0)(typescript@5.8.2) + eslint: 7.7.0 + eslint-plugin-promise: 6.1.1(eslint@7.7.0) + eslint-plugin-react: 7.33.2(eslint@7.7.0) + eslint-plugin-tsdoc: 0.3.0 + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@rushstack/eslint-config@3.7.1(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@rushstack/eslint-patch': 1.10.4 + '@rushstack/eslint-plugin': 0.15.2(eslint@8.57.1)(typescript@5.8.2) + '@rushstack/eslint-plugin-packlets': 0.9.2(eslint@8.57.1)(typescript@5.8.2) + '@rushstack/eslint-plugin-security': 0.8.2(eslint@8.57.1)(typescript@5.8.2) + '@typescript-eslint/eslint-plugin': 6.19.1(@typescript-eslint/parser@6.19.1(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2) + '@typescript-eslint/parser': 6.19.1(eslint@8.57.1)(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + '@typescript-eslint/utils': 6.19.1(eslint@8.57.1)(typescript@5.8.2) + eslint: 8.57.1 + eslint-plugin-promise: 6.1.1(eslint@8.57.1) + eslint-plugin-react: 7.33.2(eslint@8.57.1) + eslint-plugin-tsdoc: 0.3.0 + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@rushstack/eslint-config@4.6.4(eslint@8.57.1)(typescript@4.9.5)': + dependencies: + '@rushstack/eslint-patch': 1.16.1 + '@rushstack/eslint-plugin': 0.23.2(eslint@8.57.1)(typescript@4.9.5) + '@rushstack/eslint-plugin-packlets': 0.15.2(eslint@8.57.1)(typescript@4.9.5) + '@rushstack/eslint-plugin-security': 0.14.2(eslint@8.57.1)(typescript@4.9.5) + '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1)(typescript@4.9.5) + '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@4.9.5) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@4.9.5) + '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@4.9.5) + eslint: 8.57.1 + eslint-plugin-promise: 7.2.1(eslint@8.57.1) + eslint-plugin-react: 7.37.5(eslint@8.57.1) + eslint-plugin-tsdoc: 0.5.2(eslint@8.57.1)(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@rushstack/eslint-config@4.6.4(eslint@9.37.0)(typescript@5.8.2)': + dependencies: + '@rushstack/eslint-patch': 1.16.1 + '@rushstack/eslint-plugin': 0.23.2(eslint@9.37.0)(typescript@5.8.2) + '@rushstack/eslint-plugin-packlets': 0.15.2(eslint@9.37.0)(typescript@5.8.2) + '@rushstack/eslint-plugin-security': 0.14.2(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.37.0)(typescript@5.8.2))(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/parser': 8.56.1(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.8.2) + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.2) + eslint: 9.37.0 + eslint-plugin-promise: 7.2.1(eslint@9.37.0) + eslint-plugin-react: 7.37.5(eslint@9.37.0) + eslint-plugin-tsdoc: 0.5.2(eslint@9.37.0)(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@rushstack/eslint-patch@1.10.4': {} + + '@rushstack/eslint-patch@1.16.1': {} + + '@rushstack/eslint-plugin-packlets@0.15.2(eslint@8.57.1)(typescript@4.9.5)': + dependencies: + '@rushstack/tree-pattern': 0.4.1 + '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@4.9.5) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin-packlets@0.15.2(eslint@9.37.0)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.4.1 + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.2) + eslint: 9.37.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin-packlets@0.9.2(eslint@7.11.0)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.3.4 + '@typescript-eslint/utils': 6.19.1(eslint@7.11.0)(typescript@5.8.2) + eslint: 7.11.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin-packlets@0.9.2(eslint@7.30.0)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.3.4 + '@typescript-eslint/utils': 6.19.1(eslint@7.30.0)(typescript@5.8.2) + eslint: 7.30.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin-packlets@0.9.2(eslint@7.7.0)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.3.4 + '@typescript-eslint/utils': 6.19.1(eslint@7.7.0)(typescript@5.8.2) + eslint: 7.7.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin-packlets@0.9.2(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.3.4 + '@typescript-eslint/utils': 6.19.1(eslint@8.57.1)(typescript@5.8.2) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin-security@0.14.2(eslint@8.57.1)(typescript@4.9.5)': + dependencies: + '@rushstack/tree-pattern': 0.4.1 + '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@4.9.5) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin-security@0.14.2(eslint@9.37.0)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.4.1 + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.2) + eslint: 9.37.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin-security@0.8.2(eslint@7.11.0)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.3.4 + '@typescript-eslint/utils': 6.19.1(eslint@7.11.0)(typescript@5.8.2) + eslint: 7.11.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin-security@0.8.2(eslint@7.30.0)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.3.4 + '@typescript-eslint/utils': 6.19.1(eslint@7.30.0)(typescript@5.8.2) + eslint: 7.30.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin-security@0.8.2(eslint@7.7.0)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.3.4 + '@typescript-eslint/utils': 6.19.1(eslint@7.7.0)(typescript@5.8.2) + eslint: 7.7.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin-security@0.8.2(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.3.4 + '@typescript-eslint/utils': 6.19.1(eslint@8.57.1)(typescript@5.8.2) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin@0.15.2(eslint@7.11.0)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.3.4 + '@typescript-eslint/utils': 6.19.1(eslint@7.11.0)(typescript@5.8.2) + eslint: 7.11.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin@0.15.2(eslint@7.30.0)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.3.4 + '@typescript-eslint/utils': 6.19.1(eslint@7.30.0)(typescript@5.8.2) + eslint: 7.30.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin@0.15.2(eslint@7.7.0)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.3.4 + '@typescript-eslint/utils': 6.19.1(eslint@7.7.0)(typescript@5.8.2) + eslint: 7.7.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin@0.15.2(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.3.4 + '@typescript-eslint/utils': 6.19.1(eslint@8.57.1)(typescript@5.8.2) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin@0.23.2(eslint@8.57.1)(typescript@4.9.5)': + dependencies: + '@rushstack/tree-pattern': 0.4.1 + '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@4.9.5) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/eslint-plugin@0.23.2(eslint@9.37.0)(typescript@5.8.2)': + dependencies: + '@rushstack/tree-pattern': 0.4.1 + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.2) + eslint: 9.37.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@rushstack/heft-api-extractor-plugin@1.3.22(@rushstack/heft@1.2.22(@types/node@20.17.19))(@types/node@20.17.19)': + dependencies: + '@rushstack/heft': 1.2.22(@types/node@20.17.19) + '@rushstack/node-core-library': 5.23.3(@types/node@20.17.19) + semver: 7.7.4 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/heft-config-file@0.20.12(@types/node@20.17.19)': + dependencies: + '@rushstack/node-core-library': 5.23.3(@types/node@20.17.19) + '@rushstack/rig-package': 0.7.3 + '@rushstack/terminal': 0.24.2(@types/node@20.17.19) + jsonpath-plus: 10.3.0 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/heft-config-file@0.20.12(@types/node@22.9.3)': + dependencies: + '@rushstack/node-core-library': 5.23.3(@types/node@22.9.3) + '@rushstack/rig-package': 0.7.3 + '@rushstack/terminal': 0.24.2(@types/node@22.9.3) + jsonpath-plus: 10.3.0 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/heft-jest-plugin@2.0.12(@rushstack/heft@1.2.22(@types/node@20.17.19))(@types/jest@30.0.0)(@types/node@20.17.19)(jest-environment-jsdom@30.3.0)(jest-environment-node@30.3.0)': + dependencies: + '@jest/core': 30.3.0 + '@jest/reporters': 30.3.0 + '@jest/transform': 30.3.0 + '@rushstack/heft': 1.2.22(@types/node@20.17.19) + '@rushstack/heft-config-file': 0.20.12(@types/node@20.17.19) + '@rushstack/node-core-library': 5.23.3(@types/node@20.17.19) + '@rushstack/terminal': 0.24.2(@types/node@20.17.19) + jest-config: 30.3.0(@types/node@20.17.19) + jest-resolve: 30.3.0 + jest-snapshot: 30.3.0 + optionalDependencies: + '@types/jest': 30.0.0 + jest-environment-jsdom: 30.3.0 + jest-environment-node: 30.3.0 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - node-notifier + - supports-color + - ts-node + + '@rushstack/heft-lint-plugin@1.2.22(@rushstack/heft@1.2.22(@types/node@20.17.19))(@types/node@20.17.19)': + dependencies: + '@rushstack/heft': 1.2.22(@types/node@20.17.19) + '@rushstack/node-core-library': 5.23.3(@types/node@20.17.19) + json-stable-stringify-without-jsonify: 1.0.1 + semver: 7.7.4 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/heft-node-rig@2.11.45(@rushstack/heft@1.2.22(@types/node@20.17.19))(@types/node@20.17.19)(jest-environment-jsdom@30.3.0)': + dependencies: + '@microsoft/api-extractor': 7.58.12(@types/node@20.17.19) + '@rushstack/eslint-config': 4.6.4(eslint@9.37.0)(typescript@5.8.2) + '@rushstack/heft': 1.2.22(@types/node@20.17.19) + '@rushstack/heft-api-extractor-plugin': 1.3.22(@rushstack/heft@1.2.22(@types/node@20.17.19))(@types/node@20.17.19) + '@rushstack/heft-jest-plugin': 2.0.12(@rushstack/heft@1.2.22(@types/node@20.17.19))(@types/jest@30.0.0)(@types/node@20.17.19)(jest-environment-jsdom@30.3.0)(jest-environment-node@30.3.0) + '@rushstack/heft-lint-plugin': 1.2.22(@rushstack/heft@1.2.22(@types/node@20.17.19))(@types/node@20.17.19) + '@rushstack/heft-typescript-plugin': 1.3.17(@rushstack/heft@1.2.22(@types/node@20.17.19))(@types/node@20.17.19) + '@types/jest': 30.0.0 + eslint: 9.37.0 + jest-environment-node: 30.3.0 + typescript: 5.8.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - jest-environment-jsdom + - jiti + - node-notifier + - supports-color + - ts-node + + '@rushstack/heft-typescript-plugin@1.3.17(@rushstack/heft@1.2.22(@types/node@20.17.19))(@types/node@20.17.19)': + dependencies: + '@rushstack/heft': 1.2.22(@types/node@20.17.19) + '@rushstack/heft-config-file': 0.20.12(@types/node@20.17.19) + '@rushstack/node-core-library': 5.23.3(@types/node@20.17.19) + '@types/tapable': 1.0.6 + semver: 7.7.4 + tapable: 1.1.3 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/heft@1.2.22(@types/node@20.17.19)': + dependencies: + '@rushstack/heft-config-file': 0.20.12(@types/node@20.17.19) + '@rushstack/node-core-library': 5.23.3(@types/node@20.17.19) + '@rushstack/operation-graph': 0.6.11(@types/node@20.17.19) + '@rushstack/rig-package': 0.7.3 + '@rushstack/terminal': 0.24.2(@types/node@20.17.19) + '@rushstack/ts-command-line': 5.3.12(@types/node@20.17.19) + '@types/tapable': 1.0.6 + fast-glob: 3.3.3 + git-repo-info: 2.1.1 + ignore: 5.1.9 + tapable: 1.1.3 + watchpack: 2.4.0 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/heft@1.2.22(@types/node@22.9.3)': + dependencies: + '@rushstack/heft-config-file': 0.20.12(@types/node@22.9.3) + '@rushstack/node-core-library': 5.23.3(@types/node@22.9.3) + '@rushstack/operation-graph': 0.6.11(@types/node@22.9.3) + '@rushstack/rig-package': 0.7.3 + '@rushstack/terminal': 0.24.2(@types/node@22.9.3) + '@rushstack/ts-command-line': 5.3.12(@types/node@22.9.3) + '@types/tapable': 1.0.6 + fast-glob: 3.3.3 + git-repo-info: 2.1.1 + ignore: 5.1.9 + tapable: 1.1.3 + watchpack: 2.4.0 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/node-core-library@3.63.0(@types/node@20.17.19)': + dependencies: + colors: 1.2.5 + fs-extra: 7.0.1 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.11 + semver: 7.5.4 + z-schema: 5.0.5 + optionalDependencies: + '@types/node': 20.17.19 + + '@rushstack/node-core-library@3.63.0(@types/node@22.9.3)': + dependencies: + colors: 1.2.5 + fs-extra: 7.0.1 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.11 + semver: 7.5.4 + z-schema: 5.0.5 + optionalDependencies: + '@types/node': 22.9.3 + + '@rushstack/node-core-library@5.23.3(@types/node@20.17.19)': + dependencies: + ajv: 8.20.0 + ajv-draft-04: 1.0.0(ajv@8.20.0) + ajv-formats: 3.0.1(ajv@8.20.0) + fs-extra: 11.3.4 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.11 + semver: 7.7.4 + optionalDependencies: + '@types/node': 20.17.19 + + '@rushstack/node-core-library@5.23.3(@types/node@22.9.3)': + dependencies: + ajv: 8.20.0 + ajv-draft-04: 1.0.0(ajv@8.20.0) + ajv-formats: 3.0.1(ajv@8.20.0) + fs-extra: 11.3.4 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.11 + semver: 7.7.4 + optionalDependencies: + '@types/node': 22.9.3 + + '@rushstack/operation-graph@0.6.11(@types/node@20.17.19)': + dependencies: + '@rushstack/node-core-library': 5.23.3(@types/node@20.17.19) + '@rushstack/terminal': 0.24.2(@types/node@20.17.19) + optionalDependencies: + '@types/node': 20.17.19 + + '@rushstack/operation-graph@0.6.11(@types/node@22.9.3)': + dependencies: + '@rushstack/node-core-library': 5.23.3(@types/node@22.9.3) + '@rushstack/terminal': 0.24.2(@types/node@22.9.3) + optionalDependencies: + '@types/node': 22.9.3 + + '@rushstack/problem-matcher@0.2.1(@types/node@20.17.19)': + optionalDependencies: + '@types/node': 20.17.19 + + '@rushstack/problem-matcher@0.2.1(@types/node@22.9.3)': + optionalDependencies: + '@types/node': 22.9.3 + + '@rushstack/rig-package@0.7.3': + dependencies: + jju: 1.4.0 + resolve: 1.22.11 + + '@rushstack/set-webpack-public-path-plugin@4.1.16(@types/node@20.17.19)(@types/webpack@4.41.32)(webpack@4.47.0)': + dependencies: + '@rushstack/node-core-library': 3.63.0(@types/node@20.17.19) + '@rushstack/webpack-plugin-utilities': 0.3.16(@types/webpack@4.41.32)(webpack@4.47.0) + optionalDependencies: + '@types/webpack': 4.41.32 + transitivePeerDependencies: + - '@types/node' + - webpack + + '@rushstack/set-webpack-public-path-plugin@4.1.16(@types/node@22.9.3)(@types/webpack@4.41.32)(webpack@4.47.0)': + dependencies: + '@rushstack/node-core-library': 3.63.0(@types/node@22.9.3) + '@rushstack/webpack-plugin-utilities': 0.3.16(@types/webpack@4.41.32)(webpack@4.47.0) + optionalDependencies: + '@types/webpack': 4.41.32 + transitivePeerDependencies: + - '@types/node' + - webpack + + '@rushstack/terminal@0.24.2(@types/node@20.17.19)': + dependencies: + '@rushstack/node-core-library': 5.23.3(@types/node@20.17.19) + '@rushstack/problem-matcher': 0.2.1(@types/node@20.17.19) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 20.17.19 + + '@rushstack/terminal@0.24.2(@types/node@22.9.3)': + dependencies: + '@rushstack/node-core-library': 5.23.3(@types/node@22.9.3) + '@rushstack/problem-matcher': 0.2.1(@types/node@22.9.3) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 22.9.3 + + '@rushstack/tree-pattern@0.3.4': {} + + '@rushstack/tree-pattern@0.4.1': {} + + '@rushstack/ts-command-line@5.3.12(@types/node@20.17.19)': + dependencies: + '@rushstack/terminal': 0.24.2(@types/node@20.17.19) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/ts-command-line@5.3.12(@types/node@22.9.3)': + dependencies: + '@rushstack/terminal': 0.24.2(@types/node@22.9.3) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + + '@rushstack/webpack-plugin-utilities@0.3.16(@types/webpack@4.41.32)(webpack@4.47.0)': + dependencies: + memfs: 3.4.3 + webpack-merge: 5.8.0 + optionalDependencies: + '@types/webpack': 4.41.32 + webpack: 4.47.0 + + '@serverless-stack/aws-lambda-ric@2.0.13': + dependencies: + node-addon-api: 3.2.1 + node-gyp: 8.1.0 + transitivePeerDependencies: + - supports-color + + '@serverless-stack/cli@1.18.4(constructs@10.0.130)': + dependencies: + '@aws-cdk/aws-apigatewayv2-alpha': 2.50.0-alpha.0(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130) + '@serverless-stack/core': 1.18.4 + '@serverless-stack/resources': 1.18.4 + aws-cdk: 2.50.0 + aws-cdk-lib: 2.50.0(constructs@10.0.130) + aws-sdk: 2.1693.0 + body-parser: 1.20.4 + chalk: 4.1.2 + chokidar: 3.6.0 + cross-spawn: 7.0.6 + detect-port-alt: 1.1.6 + esbuild: 0.14.54 + esbuild-runner: 2.2.2(esbuild@0.14.54) + express: 4.21.1 + fs-extra: 9.1.0 + remeda: 0.0.32 + source-map-support: 0.5.21 + ws: 8.21.0 + yargs: 15.4.1 + transitivePeerDependencies: + - aws-crt + - better-sqlite3 + - bufferutil + - constructs + - mysql2 + - pg + - supports-color + - utf-8-validate + + '@serverless-stack/core@1.18.4': + dependencies: + '@serverless-stack/aws-lambda-ric': 2.0.13 + '@trpc/server': 9.27.4 + acorn: 8.16.0 + acorn-walk: 8.3.5 + async-retry: 1.3.3 + aws-cdk: 2.50.0 + aws-cdk-lib: 2.50.0(constructs@10.0.130) + aws-sdk: 2.1693.0 + chalk: 4.1.2 + chokidar: 3.6.0 + ci-info: 3.9.0 + conf: 10.2.0 + constructs: 10.0.130 + cross-spawn: 7.0.6 + dendriform-immer-patch-optimiser: 2.1.3(immer@9.0.21) + dotenv: 10.0.0 + dotenv-expand: 5.1.0 + esbuild: 0.14.54 + escodegen: 2.1.0 + express: 4.21.1 + fs-extra: 9.1.0 + immer: 9.0.21 + js-yaml: 4.1.1 + kysely: 0.21.6 + kysely-codegen: 0.6.2(kysely@0.21.6) + kysely-data-api: 0.1.4(aws-sdk@2.1693.0)(kysely@0.21.6) + log4js: 6.9.1 + picomatch: 2.3.2 + remeda: 0.0.32 + semver: 7.7.4 + typescript: 4.9.5 + uuid: 8.3.2 + ws: 8.21.0 + xstate: 4.26.1 + zip-local: 0.3.5 + optionalDependencies: + '@pothos/core': 3.41.2(graphql@16.13.2) + graphql: 16.13.2 + transitivePeerDependencies: + - better-sqlite3 + - bufferutil + - mysql2 + - pg + - supports-color + - utf-8-validate + + '@serverless-stack/resources@1.18.4': + dependencies: + '@aws-cdk/aws-apigatewayv2-alpha': 2.50.0-alpha.0(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130) + '@aws-cdk/aws-apigatewayv2-authorizers-alpha': 2.50.0-alpha.0(@aws-cdk/aws-apigatewayv2-alpha@2.50.0-alpha.0(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130))(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130) + '@aws-cdk/aws-apigatewayv2-integrations-alpha': 2.50.0-alpha.0(@aws-cdk/aws-apigatewayv2-alpha@2.50.0-alpha.0(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130))(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130) + '@aws-cdk/aws-appsync-alpha': 2.50.0-alpha.0(aws-cdk-lib@2.50.0(constructs@10.0.130))(constructs@10.0.130) + '@aws-sdk/client-codebuild': 3.1023.0 + '@serverless-stack/core': 1.18.4 + archiver: 5.3.2 + aws-cdk-lib: 2.50.0(constructs@10.0.130) + chalk: 4.1.2 + constructs: 10.0.130 + cross-spawn: 7.0.6 + esbuild: 0.28.0 + fs-extra: 9.1.0 + glob: 7.2.3 + indent-string: 5.0.0 + zip-local: 0.3.5 + optionalDependencies: + graphql: 16.13.2 + transitivePeerDependencies: + - aws-crt + - better-sqlite3 + - bufferutil + - mysql2 + - pg + - supports-color + - utf-8-validate + + '@sinclair/typebox@0.27.10': {} + + '@sinclair/typebox@0.34.49': {} + + '@sindresorhus/is@4.6.0': {} + + '@sindresorhus/merge-streams@2.3.0': {} + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@sinonjs/fake-timers@15.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@smithy/config-resolver@4.4.13': + dependencies: + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + tslib: 2.8.1 + + '@smithy/core@3.23.13': + dependencies: + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-stream': 4.5.21 + '@smithy/util-utf8': 4.2.2 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.2.12': + dependencies: + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.3.15': + dependencies: + '@smithy/protocol-http': 5.3.12 + '@smithy/querystring-builder': 4.2.12 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + tslib: 2.8.1 + + '@smithy/hash-node@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/is-array-buffer@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/middleware-content-length@4.2.12': + dependencies: + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/middleware-endpoint@4.4.28': + dependencies: + '@smithy/core': 3.23.13 + '@smithy/middleware-serde': 4.2.16 + '@smithy/node-config-provider': 4.3.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-middleware': 4.2.12 + tslib: 2.8.1 + + '@smithy/middleware-retry@4.4.46': + dependencies: + '@smithy/node-config-provider': 4.3.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/service-error-classification': 4.2.12 + '@smithy/smithy-client': 4.12.8 + '@smithy/types': 4.13.1 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.13 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + + '@smithy/middleware-serde@4.2.16': + dependencies: + '@smithy/core': 3.23.13 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/middleware-stack@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/node-config-provider@4.3.12': + dependencies: + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.5.1': + dependencies: + '@smithy/protocol-http': 5.3.12 + '@smithy/querystring-builder': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/property-provider@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/protocol-http@5.3.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/querystring-builder@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + '@smithy/util-uri-escape': 4.2.2 + tslib: 2.8.1 + + '@smithy/querystring-parser@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/service-error-classification@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + + '@smithy/shared-ini-file-loader@4.4.7': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/signature-v4@5.3.12': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-uri-escape': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/smithy-client@4.12.8': + dependencies: + '@smithy/core': 3.23.13 + '@smithy/middleware-endpoint': 4.4.28 + '@smithy/middleware-stack': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-stream': 4.5.21 + tslib: 2.8.1 + + '@smithy/types@4.13.1': + dependencies: + tslib: 2.8.1 + + '@smithy/url-parser@4.2.12': + dependencies: + '@smithy/querystring-parser': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/util-base64@4.3.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-body-length-browser@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-body-length-node@4.2.3': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-buffer-from@4.2.2': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-config-provider@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-defaults-mode-browser@4.3.44': + dependencies: + '@smithy/property-provider': 4.2.12 + '@smithy/smithy-client': 4.12.8 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/util-defaults-mode-node@4.2.48': + dependencies: + '@smithy/config-resolver': 4.4.13 + '@smithy/credential-provider-imds': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/smithy-client': 4.12.8 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/util-endpoints@3.3.3': + dependencies: + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/util-hex-encoding@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-middleware@4.2.12': + dependencies: + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/util-retry@4.2.13': + dependencies: + '@smithy/service-error-classification': 4.2.12 + '@smithy/types': 4.13.1 + tslib: 2.8.1 + + '@smithy/util-stream@4.5.21': + dependencies: + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/node-http-handler': 4.5.1 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-uri-escape@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@4.2.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + tslib: 2.8.1 + + '@smithy/uuid@1.1.2': + dependencies: + tslib: 2.8.1 + + '@standard-schema/spec@1.1.0': {} + + '@standard-schema/utils@0.3.0': {} + + '@storybook/addon-actions@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/api': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/components': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/core-events': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + '@storybook/theming': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + core-js: 3.49.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.18.1 + polished: 4.3.1 + prop-types: 15.8.1 + react-inspector: 5.1.1(react@17.0.2) + regenerator-runtime: 0.13.11 + telejson: 5.3.3 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + uuid-browser: 3.1.0 + optionalDependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@storybook/addon-backgrounds@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/api': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/client-logger': 6.4.22 + '@storybook/components': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/core-events': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + '@storybook/theming': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + core-js: 3.49.0 + global: 4.4.0 + memoizerific: 1.11.3 + regenerator-runtime: 0.13.11 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + optionalDependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@storybook/addon-controls@6.4.22(@types/react@17.0.74)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)': + dependencies: + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/api': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/client-logger': 6.4.22 + '@storybook/components': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/core-common': 6.4.22(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2) + '@storybook/csf': 0.0.2--canary.87bc651.0 + '@storybook/node-logger': 6.4.22 + '@storybook/store': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/theming': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + core-js: 3.49.0 + lodash: 4.18.1 + ts-dedent: 2.2.0 + optionalDependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + transitivePeerDependencies: + - '@types/react' + - eslint + - supports-color + - typescript + - vue-template-compiler + - webpack-cli + - webpack-command + + '@storybook/addon-docs@6.4.22(@storybook/react@6.4.22(@babel/core@7.20.12)(@types/node@20.17.19)(@types/react@17.0.74)(@types/webpack@4.41.32)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(type-fest@0.21.3)(typescript@5.8.2)(webpack-dev-server@5.2.3(@types/webpack@4.41.32)(webpack@4.47.0))(webpack-hot-middleware@2.26.1))(@types/react@17.0.74)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)(webpack@4.47.0)': + dependencies: + '@babel/core': 7.20.12 + '@babel/generator': 7.29.1 + '@babel/parser': 7.29.2 + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.20.12) + '@babel/preset-env': 7.29.2(@babel/core@7.20.12) + '@jest/transform': 26.6.2 + '@mdx-js/loader': 1.6.22(react@17.0.2) + '@mdx-js/mdx': 1.6.22 + '@mdx-js/react': 1.6.22(react@17.0.2) + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/api': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/builder-webpack4': 6.4.22(@types/react@17.0.74)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2) + '@storybook/client-logger': 6.4.22 + '@storybook/components': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/core': 6.4.22(@types/react@17.0.74)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)(webpack@4.47.0) + '@storybook/core-events': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + '@storybook/csf-tools': 6.4.22 + '@storybook/node-logger': 6.4.22 + '@storybook/postinstall': 6.4.22 + '@storybook/preview-web': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/source-loader': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/store': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/theming': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + acorn: 7.4.1 + acorn-jsx: 5.3.2(acorn@7.4.1) + acorn-walk: 7.2.0 + core-js: 3.49.0 + doctrine: 3.0.0 + escodegen: 2.1.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + html-tags: 3.3.1 + js-string-escape: 1.0.1 + loader-utils: 2.0.4 + lodash: 4.18.1 + nanoid: 3.3.11 + p-limit: 3.1.0 + prettier: 2.3.0 + prop-types: 15.8.1 + react-element-to-jsx-string: 14.3.4(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + regenerator-runtime: 0.13.11 + remark-external-links: 8.0.0 + remark-slug: 6.1.0 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + optionalDependencies: + '@storybook/react': 6.4.22(@babel/core@7.20.12)(@types/node@20.17.19)(@types/react@17.0.74)(@types/webpack@4.41.32)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(type-fest@0.21.3)(typescript@5.8.2)(webpack-dev-server@5.2.3(@types/webpack@4.41.32)(webpack@4.47.0))(webpack-hot-middleware@2.26.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + webpack: 4.47.0 + transitivePeerDependencies: + - '@storybook/builder-webpack5' + - '@storybook/manager-webpack5' + - '@types/react' + - bufferutil + - encoding + - eslint + - supports-color + - typescript + - utf-8-validate + - vue-template-compiler + - webpack-cli + - webpack-command + + '@storybook/addon-essentials@6.4.22(@babel/core@7.20.12)(@storybook/react@6.4.22(@babel/core@7.20.12)(@types/node@20.17.19)(@types/react@17.0.74)(@types/webpack@4.41.32)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(type-fest@0.21.3)(typescript@5.8.2)(webpack-dev-server@5.2.3(@types/webpack@4.41.32)(webpack@4.47.0))(webpack-hot-middleware@2.26.1))(@types/react@17.0.74)(babel-loader@8.2.5(@babel/core@7.20.12)(webpack@4.47.0))(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)(webpack@4.47.0)': + dependencies: + '@babel/core': 7.20.12 + '@storybook/addon-actions': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/addon-backgrounds': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/addon-controls': 6.4.22(@types/react@17.0.74)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2) + '@storybook/addon-docs': 6.4.22(@storybook/react@6.4.22(@babel/core@7.20.12)(@types/node@20.17.19)(@types/react@17.0.74)(@types/webpack@4.41.32)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(type-fest@0.21.3)(typescript@5.8.2)(webpack-dev-server@5.2.3(@types/webpack@4.41.32)(webpack@4.47.0))(webpack-hot-middleware@2.26.1))(@types/react@17.0.74)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)(webpack@4.47.0) + '@storybook/addon-measure': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/addon-outline': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/addon-toolbars': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/addon-viewport': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/api': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/node-logger': 6.4.22 + babel-loader: 8.2.5(@babel/core@7.20.12)(webpack@4.47.0) + core-js: 3.49.0 + regenerator-runtime: 0.13.11 + ts-dedent: 2.2.0 + optionalDependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + webpack: 4.47.0 + transitivePeerDependencies: + - '@storybook/angular' + - '@storybook/builder-webpack5' + - '@storybook/html' + - '@storybook/manager-webpack5' + - '@storybook/react' + - '@storybook/vue3' + - '@types/react' + - bufferutil + - encoding + - eslint + - lit + - supports-color + - svelte + - sveltedoc-parser + - typescript + - utf-8-validate + - vue + - vue-template-compiler + - webpack-cli + - webpack-command + + '@storybook/addon-links@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/client-logger': 6.4.22 + '@storybook/core-events': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + '@storybook/router': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@types/qs': 6.15.0 + core-js: 3.49.0 + global: 4.4.0 + prop-types: 15.8.1 + qs: 6.15.0 + regenerator-runtime: 0.13.11 + ts-dedent: 2.2.0 + optionalDependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@storybook/addon-measure@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/api': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/client-logger': 6.4.22 + '@storybook/components': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/core-events': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + core-js: 3.49.0 + global: 4.4.0 + optionalDependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@storybook/addon-outline@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/api': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/client-logger': 6.4.22 + '@storybook/components': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/core-events': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + core-js: 3.49.0 + global: 4.4.0 + regenerator-runtime: 0.13.11 + ts-dedent: 2.2.0 + optionalDependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@storybook/addon-toolbars@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/api': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/components': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/theming': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + core-js: 3.49.0 + regenerator-runtime: 0.13.11 + optionalDependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@storybook/addon-viewport@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/api': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/client-logger': 6.4.22 + '@storybook/components': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/core-events': 6.4.22 + '@storybook/theming': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + core-js: 3.49.0 + global: 4.4.0 + memoizerific: 1.11.3 + prop-types: 15.8.1 + regenerator-runtime: 0.13.11 + optionalDependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@storybook/addons@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@storybook/api': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/channels': 6.4.22 + '@storybook/client-logger': 6.4.22 + '@storybook/core-events': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + '@storybook/router': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/theming': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@types/react': 17.0.74 + '@types/webpack-env': 1.18.8 + core-js: 3.49.0 + global: 4.4.0 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + regenerator-runtime: 0.13.11 + transitivePeerDependencies: + - supports-color + + '@storybook/api@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@storybook/channels': 6.4.22 + '@storybook/client-logger': 6.4.22 + '@storybook/core-events': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + '@storybook/router': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@types/react': 17.0.74 + core-js: 3.49.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.18.1 + memoizerific: 1.11.3 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + regenerator-runtime: 0.13.11 + store2: 2.14.4 + telejson: 5.3.3 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - supports-color + + '@storybook/builder-webpack4@6.4.22(@types/react@17.0.74)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)': + dependencies: + '@babel/core': 7.20.12 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.20.12) + '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.20.12) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.20.12) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.20.12) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.20.12) + '@babel/preset-env': 7.29.2(@babel/core@7.20.12) + '@babel/preset-react': 7.28.5(@babel/core@7.20.12) + '@babel/preset-typescript': 7.28.5(@babel/core@7.20.12) + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/api': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/channel-postmessage': 6.4.22 + '@storybook/channels': 6.4.22 + '@storybook/client-api': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/client-logger': 6.4.22 + '@storybook/components': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/core-common': 6.4.22(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2) + '@storybook/core-events': 6.4.22 + '@storybook/node-logger': 6.4.22 + '@storybook/preview-web': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/router': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/semver': 7.3.2 + '@storybook/store': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/theming': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/ui': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@types/node': 14.18.63 + '@types/webpack': 4.41.32 + autoprefixer: 9.8.8 + babel-loader: 8.2.5(@babel/core@7.20.12)(webpack@4.47.0) + babel-plugin-macros: 2.8.0 + babel-plugin-polyfill-corejs3: 0.1.7(@babel/core@7.20.12) + case-sensitive-paths-webpack-plugin: 2.4.0 + core-js: 3.49.0 + css-loader: 3.6.0(webpack@4.47.0) + file-loader: 6.2.0(webpack@4.47.0) + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 4.1.6 + glob: 7.2.3 + glob-promise: 3.4.0(glob@7.2.3) + global: 4.4.0 + html-webpack-plugin: 4.5.2(webpack@4.47.0) + pnp-webpack-plugin: 1.6.4(typescript@5.8.2) + postcss: 7.0.39 + postcss-flexbugs-fixes: 4.2.1 + postcss-loader: 4.3.0(postcss@7.0.39)(webpack@4.47.0) + raw-loader: 4.0.2(webpack@4.47.0) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + stable: 0.1.8 + style-loader: 1.3.0(webpack@4.47.0) + terser-webpack-plugin: 4.2.3(webpack@4.47.0) + ts-dedent: 2.2.0 + url-loader: 4.1.1(file-loader@6.2.0(webpack@4.47.0))(webpack@4.47.0) + util-deprecate: 1.0.2 + webpack: 4.47.0 + webpack-dev-middleware: 3.7.3(@types/webpack@4.41.32)(webpack@4.47.0) + webpack-filter-warnings-plugin: 1.2.1(webpack@4.47.0) + webpack-hot-middleware: 2.26.1 + webpack-virtual-modules: 0.2.2 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - '@types/react' + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + + '@storybook/builder-webpack5@9.1.20(@rspack/core@1.6.8(@swc/helpers@0.5.21))(@types/webpack@4.41.32)(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1))(typescript@5.8.2)': + dependencies: + '@storybook/core-webpack': 9.1.20(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1)) + case-sensitive-paths-webpack-plugin: 2.4.0 + cjs-module-lexer: 1.4.3 + css-loader: 6.11.0(@rspack/core@1.6.8(@swc/helpers@0.5.21))(webpack@5.105.4) + es-module-lexer: 1.7.0 + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.8.2)(webpack@5.105.4) + html-webpack-plugin: 5.5.4(webpack@5.105.4) + magic-string: 0.30.21 + storybook: 9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1) + style-loader: 3.3.4(webpack@5.105.4) + terser-webpack-plugin: 5.3.17(webpack@5.105.4) + ts-dedent: 2.2.0 + webpack: 5.105.4 + webpack-dev-middleware: 6.1.3(@types/webpack@4.41.32)(webpack@5.105.4) + webpack-hot-middleware: 2.26.1 + webpack-virtual-modules: 0.6.2 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - '@rspack/core' + - '@swc/core' + - '@types/webpack' + - esbuild + - uglify-js + - webpack-cli + + '@storybook/channel-postmessage@6.4.22': + dependencies: + '@storybook/channels': 6.4.22 + '@storybook/client-logger': 6.4.22 + '@storybook/core-events': 6.4.22 + core-js: 3.49.0 + global: 4.4.0 + qs: 6.15.0 + telejson: 5.3.3 + + '@storybook/channel-websocket@6.4.22': + dependencies: + '@storybook/channels': 6.4.22 + '@storybook/client-logger': 6.4.22 + core-js: 3.49.0 + global: 4.4.0 + telejson: 5.3.3 + + '@storybook/channels@6.4.22': + dependencies: + core-js: 3.49.0 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + + '@storybook/cli@6.4.22(eslint@9.37.0)(jest@29.3.1(@types/node@20.17.19)(babel-plugin-macros@3.1.0))(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)': + dependencies: + '@babel/core': 7.20.12 + '@babel/preset-env': 7.29.2(@babel/core@7.20.12) + '@storybook/codemod': 6.4.22(@babel/preset-env@7.29.2(@babel/core@7.20.12)) + '@storybook/core-common': 6.4.22(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2) + '@storybook/csf-tools': 6.4.22 + '@storybook/node-logger': 6.4.22 + '@storybook/semver': 7.3.2 + boxen: 5.1.2 + chalk: 4.1.2 + commander: 6.2.1 + core-js: 3.49.0 + cross-spawn: 7.0.6 + envinfo: 7.21.0 + express: 4.21.1 + find-up: 5.0.0 + fs-extra: 9.1.0 + get-port: 5.1.1 + globby: 11.1.0 + jest: 29.3.1(@types/node@20.17.19)(babel-plugin-macros@3.1.0) + jscodeshift: 0.13.1(@babel/preset-env@7.29.2(@babel/core@7.20.12)) + json5: 2.2.3 + leven: 3.1.0 + prompts: 2.4.2 + puppeteer-core: 2.1.1 + read-pkg-up: 7.0.1 + shelljs: 0.8.5 + strip-json-comments: 3.1.1 + ts-dedent: 2.2.0 + update-notifier: 5.1.0 + transitivePeerDependencies: + - eslint + - react + - react-dom + - supports-color + - typescript + - vue-template-compiler + - webpack-cli + - webpack-command + + '@storybook/cli@9.1.20(@babel/preset-env@7.29.2(@babel/core@7.20.12))(@testing-library/dom@7.21.8)(prettier@3.8.1)': + dependencies: + '@storybook/codemod': 9.1.20(@babel/preset-env@7.29.2(@babel/core@7.20.12))(@testing-library/dom@7.21.8) + '@types/semver': 7.7.1 + commander: 12.1.0 + create-storybook: 9.1.20 + giget: 1.2.5 + jscodeshift: 0.15.2(@babel/preset-env@7.29.2(@babel/core@7.20.12)) + storybook: 9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1) + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@babel/preset-env' + - '@testing-library/dom' + - bufferutil + - msw + - prettier + - supports-color + - utf-8-validate + - vite + + '@storybook/client-api@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/channel-postmessage': 6.4.22 + '@storybook/channels': 6.4.22 + '@storybook/client-logger': 6.4.22 + '@storybook/core-events': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + '@storybook/store': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@types/qs': 6.15.0 + '@types/webpack-env': 1.18.8 + core-js: 3.49.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.18.1 + memoizerific: 1.11.3 + qs: 6.15.0 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + regenerator-runtime: 0.13.11 + store2: 2.14.4 + synchronous-promise: 2.0.17 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@storybook/client-logger@6.4.22': + dependencies: + core-js: 3.49.0 + global: 4.4.0 + + '@storybook/codemod@6.4.22(@babel/preset-env@7.29.2(@babel/core@7.20.12))': + dependencies: + '@babel/types': 7.29.0 + '@mdx-js/mdx': 1.6.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + '@storybook/csf-tools': 6.4.22 + '@storybook/node-logger': 6.4.22 + core-js: 3.49.0 + cross-spawn: 7.0.6 + globby: 11.1.0 + jscodeshift: 0.13.1(@babel/preset-env@7.29.2(@babel/core@7.20.12)) + lodash: 4.18.1 + prettier: 2.3.0 + recast: 0.19.1 + regenerator-runtime: 0.13.11 + transitivePeerDependencies: + - '@babel/preset-env' + - supports-color + + '@storybook/codemod@9.1.20(@babel/preset-env@7.29.2(@babel/core@7.20.12))(@testing-library/dom@7.21.8)': + dependencies: + '@types/cross-spawn': 6.0.6 + cross-spawn: 7.0.6 + es-toolkit: 1.45.1 + globby: 14.1.0 + jscodeshift: 0.15.2(@babel/preset-env@7.29.2(@babel/core@7.20.12)) + prettier: 3.8.1 + storybook: 9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1) + tiny-invariant: 1.3.3 + transitivePeerDependencies: + - '@babel/preset-env' + - '@testing-library/dom' + - bufferutil + - msw + - supports-color + - utf-8-validate + - vite + + '@storybook/components@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@popperjs/core': 2.11.8 + '@storybook/client-logger': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + '@storybook/theming': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@types/color-convert': 2.0.4 + '@types/overlayscrollbars': 1.12.5 + '@types/react-syntax-highlighter': 11.0.5 + color-convert: 2.0.1 + core-js: 3.49.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.18.1 + markdown-to-jsx: 7.7.17(react@17.0.2) + memoizerific: 1.11.3 + overlayscrollbars: 1.13.3 + polished: 4.3.1 + prop-types: 15.8.1 + react: 17.0.2 + react-colorful: 5.6.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + react-dom: 17.0.2(react@17.0.2) + react-popper-tooltip: 3.1.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + react-syntax-highlighter: 13.5.3(react@17.0.2) + react-textarea-autosize: 8.5.9(@types/react@17.0.74)(react@17.0.2) + regenerator-runtime: 0.13.11 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@storybook/core-client@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)(webpack@4.47.0)': + dependencies: + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/channel-postmessage': 6.4.22 + '@storybook/channel-websocket': 6.4.22 + '@storybook/client-api': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/client-logger': 6.4.22 + '@storybook/core-events': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + '@storybook/preview-web': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/store': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/ui': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + airbnb-js-shims: 2.2.1 + ansi-to-html: 0.6.15 + core-js: 3.49.0 + global: 4.4.0 + lodash: 4.18.1 + qs: 6.15.0 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + regenerator-runtime: 0.13.11 + ts-dedent: 2.2.0 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + webpack: 4.47.0 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@storybook/core-common@6.4.22(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)': + dependencies: + '@babel/core': 7.20.12 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.20.12) + '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.20.12) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.20.12) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.20.12) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.20.12) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.20.12) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.20.12) + '@babel/preset-env': 7.29.2(@babel/core@7.20.12) + '@babel/preset-react': 7.28.5(@babel/core@7.20.12) + '@babel/preset-typescript': 7.28.5(@babel/core@7.20.12) + '@babel/register': 7.28.6(@babel/core@7.20.12) + '@storybook/node-logger': 6.4.22 + '@storybook/semver': 7.3.2 + '@types/node': 14.18.63 + '@types/pretty-hrtime': 1.0.3 + babel-loader: 8.2.5(@babel/core@7.20.12)(webpack@4.47.0) + babel-plugin-macros: 3.1.0 + babel-plugin-polyfill-corejs3: 0.1.7(@babel/core@7.20.12) + chalk: 4.1.2 + core-js: 3.49.0 + express: 4.21.1 + file-system-cache: 1.1.0 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 6.5.3(eslint@9.37.0)(typescript@5.8.2)(webpack@4.47.0) + fs-extra: 9.1.0 + glob: 7.2.3 + handlebars: 4.7.9 + interpret: 2.2.0 + json5: 2.2.3 + lazy-universal-dotenv: 3.0.1 + picomatch: 2.3.2 + pkg-dir: 5.0.0 + pretty-hrtime: 1.0.3 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + resolve-from: 5.0.0 + slash: 3.0.0 + telejson: 5.3.3 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + webpack: 4.47.0 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + + '@storybook/core-events@6.4.22': + dependencies: + core-js: 3.49.0 + + '@storybook/core-server@6.4.22(@types/react@17.0.74)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)': + dependencies: + '@discoveryjs/json-ext': 0.5.7 + '@storybook/builder-webpack4': 6.4.22(@types/react@17.0.74)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2) + '@storybook/core-client': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)(webpack@4.47.0) + '@storybook/core-common': 6.4.22(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2) + '@storybook/core-events': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + '@storybook/csf-tools': 6.4.22 + '@storybook/manager-webpack4': 6.4.22(@types/react@17.0.74)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2) + '@storybook/node-logger': 6.4.22 + '@storybook/semver': 7.3.2 + '@storybook/store': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@types/node': 14.18.63 + '@types/node-fetch': 2.6.13 + '@types/pretty-hrtime': 1.0.3 + '@types/webpack': 4.41.32 + better-opn: 2.1.1 + boxen: 5.1.2 + chalk: 4.1.2 + cli-table3: 0.6.5 + commander: 6.2.1 + compression: 1.7.5 + core-js: 3.49.0 + cpy: 8.1.2 + detect-port: 1.6.1 + express: 4.21.1 + file-system-cache: 1.1.0 + fs-extra: 9.1.0 + globby: 11.1.0 + ip: 1.1.9 + lodash: 4.18.1 + node-fetch: 2.7.0(encoding@0.1.13) + pretty-hrtime: 1.0.3 + prompts: 2.4.2 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + regenerator-runtime: 0.13.11 + serve-favicon: 2.5.1 + slash: 3.0.0 + telejson: 5.3.3 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + watchpack: 2.4.0 + webpack: 4.47.0 + ws: 8.21.0 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - '@types/react' + - bufferutil + - encoding + - eslint + - supports-color + - utf-8-validate + - vue-template-compiler + - webpack-cli + - webpack-command + + '@storybook/core-webpack@9.1.20(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1))': + dependencies: + storybook: 9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1) + ts-dedent: 2.2.0 + + '@storybook/core@6.4.22(@types/react@17.0.74)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)(webpack@4.47.0)': + dependencies: + '@storybook/core-client': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)(webpack@4.47.0) + '@storybook/core-server': 6.4.22(@types/react@17.0.74)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + webpack: 4.47.0 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - '@storybook/manager-webpack5' + - '@types/react' + - bufferutil + - encoding + - eslint + - supports-color + - utf-8-validate + - vue-template-compiler + - webpack-cli + - webpack-command + + '@storybook/csf-tools@6.4.22': + dependencies: + '@babel/core': 7.20.12 + '@babel/generator': 7.29.1 + '@babel/parser': 7.29.2 + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.20.12) + '@babel/preset-env': 7.29.2(@babel/core@7.20.12) + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@mdx-js/mdx': 1.6.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + core-js: 3.49.0 + fs-extra: 9.1.0 + global: 4.4.0 + js-string-escape: 1.0.1 + lodash: 4.18.1 + prettier: 2.3.0 + regenerator-runtime: 0.13.11 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - supports-color + + '@storybook/csf@0.0.2--canary.87bc651.0': + dependencies: + lodash: 4.18.1 + + '@storybook/global@5.0.0': {} + + '@storybook/manager-webpack4@6.4.22(@types/react@17.0.74)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)': + dependencies: + '@babel/core': 7.20.12 + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.20.12) + '@babel/preset-react': 7.28.5(@babel/core@7.20.12) + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/core-client': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)(webpack@4.47.0) + '@storybook/core-common': 6.4.22(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2) + '@storybook/node-logger': 6.4.22 + '@storybook/theming': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/ui': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@types/node': 14.18.63 + '@types/webpack': 4.41.32 + babel-loader: 8.2.5(@babel/core@7.20.12)(webpack@4.47.0) + case-sensitive-paths-webpack-plugin: 2.4.0 + chalk: 4.1.2 + core-js: 3.49.0 + css-loader: 3.6.0(webpack@4.47.0) + express: 4.21.1 + file-loader: 6.2.0(webpack@4.47.0) + file-system-cache: 1.1.0 + find-up: 5.0.0 + fs-extra: 9.1.0 + html-webpack-plugin: 4.5.2(webpack@4.47.0) + node-fetch: 2.7.0(encoding@0.1.13) + pnp-webpack-plugin: 1.6.4(typescript@5.8.2) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + read-pkg-up: 7.0.1 + regenerator-runtime: 0.13.11 + resolve-from: 5.0.0 + style-loader: 1.3.0(webpack@4.47.0) + telejson: 5.3.3 + terser-webpack-plugin: 4.2.3(webpack@4.47.0) + ts-dedent: 2.2.0 + url-loader: 4.1.1(file-loader@6.2.0(webpack@4.47.0))(webpack@4.47.0) + util-deprecate: 1.0.2 + webpack: 4.47.0 + webpack-dev-middleware: 3.7.3(@types/webpack@4.41.32)(webpack@4.47.0) + webpack-virtual-modules: 0.2.2 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - '@types/react' + - encoding + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + + '@storybook/node-logger@6.4.22': + dependencies: + '@types/npmlog': 4.1.6 + chalk: 4.1.2 + core-js: 3.49.0 + npmlog: 5.0.1 + pretty-hrtime: 1.0.3 + + '@storybook/postinstall@6.4.22': + dependencies: + core-js: 3.49.0 + + '@storybook/preset-react-webpack@9.1.20(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1))(typescript@5.8.2)': + dependencies: + '@storybook/core-webpack': 9.1.20(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1)) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.8.2)(webpack@5.105.4) + '@types/semver': 7.7.1 + find-up: 7.0.0 + magic-string: 0.30.21 + react: 19.2.4 + react-docgen: 7.1.1 + react-dom: 19.2.4(react@19.2.4) + resolve: 1.22.11 + semver: 7.7.4 + storybook: 9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1) + tsconfig-paths: 4.2.0 + webpack: 5.105.4 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - supports-color + - uglify-js + - webpack-cli + + '@storybook/preview-web@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/channel-postmessage': 6.4.22 + '@storybook/client-logger': 6.4.22 + '@storybook/core-events': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + '@storybook/store': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + ansi-to-html: 0.6.15 + core-js: 3.49.0 + global: 4.4.0 + lodash: 4.18.1 + qs: 6.15.0 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + regenerator-runtime: 0.13.11 + synchronous-promise: 2.0.17 + ts-dedent: 2.2.0 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@storybook/react-docgen-typescript-plugin@1.0.2-canary.253f8c1.0(typescript@5.8.2)(webpack@4.47.0)': + dependencies: + debug: 4.4.3(supports-color@8.1.1) + endent: 2.1.0 + find-cache-dir: 3.3.2 + flat-cache: 3.2.0 + micromatch: 4.0.8 + react-docgen-typescript: 2.4.0(typescript@5.8.2) + tslib: 2.8.1 + typescript: 5.8.2 + webpack: 4.47.0 + transitivePeerDependencies: + - supports-color + + '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.8.2)(webpack@5.105.4)': + dependencies: + debug: 4.4.3(supports-color@8.1.1) + endent: 2.1.0 + find-cache-dir: 3.3.2 + flat-cache: 3.2.0 + micromatch: 4.0.8 + react-docgen-typescript: 2.4.0(typescript@5.8.2) + tslib: 2.8.1 + typescript: 5.8.2 + webpack: 5.105.4 + transitivePeerDependencies: + - supports-color + + '@storybook/react-dom-shim@9.1.20(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1))': + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + storybook: 9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1) + + '@storybook/react-webpack5@9.1.20(@rspack/core@1.6.8(@swc/helpers@0.5.21))(@types/node@20.17.19)(@types/react@19.2.7)(@types/webpack@4.41.32)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1))(typescript@5.8.2)': + dependencies: + '@storybook/builder-webpack5': 9.1.20(@rspack/core@1.6.8(@swc/helpers@0.5.21))(@types/webpack@4.41.32)(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1))(typescript@5.8.2) + '@storybook/preset-react-webpack': 9.1.20(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1))(typescript@5.8.2) + '@storybook/react': 9.1.20(@types/node@20.17.19)(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1))(typescript@5.8.2) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + storybook: 9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - '@rspack/core' + - '@swc/core' + - '@types/node' + - '@types/react' + - '@types/webpack' + - esbuild + - supports-color + - uglify-js + - webpack-cli + + '@storybook/react@6.4.22(@babel/core@7.20.12)(@types/node@20.17.19)(@types/react@17.0.74)(@types/webpack@4.41.32)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(type-fest@0.21.3)(typescript@5.8.2)(webpack-dev-server@5.2.3(@types/webpack@4.41.32)(webpack@4.47.0))(webpack-hot-middleware@2.26.1)': + dependencies: + '@babel/preset-flow': 7.27.1(@babel/core@7.20.12) + '@babel/preset-react': 7.28.5(@babel/core@7.20.12) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.17(@types/webpack@4.41.32)(react-refresh@0.11.0)(type-fest@0.21.3)(webpack-dev-server@5.2.3(@types/webpack@4.41.32)(webpack@4.47.0))(webpack-hot-middleware@2.26.1)(webpack@4.47.0) + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/core': 6.4.22(@types/react@17.0.74)(encoding@0.1.13)(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2)(webpack@4.47.0) + '@storybook/core-common': 6.4.22(eslint@9.37.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.2) + '@storybook/csf': 0.0.2--canary.87bc651.0 + '@storybook/node-logger': 6.4.22 + '@storybook/react-docgen-typescript-plugin': 1.0.2-canary.253f8c1.0(typescript@5.8.2)(webpack@4.47.0) + '@storybook/semver': 7.3.2 + '@storybook/store': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@types/node': 20.17.19 + '@types/react': 17.0.74 + '@types/webpack-env': 1.18.8 + babel-plugin-add-react-displayname: 0.0.5 + babel-plugin-named-asset-import: 0.3.8(@babel/core@7.20.12) + babel-plugin-react-docgen: 4.2.1 + core-js: 3.49.0 + global: 4.4.0 + lodash: 4.18.1 + prop-types: 15.8.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-refresh: 0.11.0 + read-pkg-up: 7.0.1 + regenerator-runtime: 0.13.11 + ts-dedent: 2.2.0 + webpack: 4.47.0 + optionalDependencies: + '@babel/core': 7.20.12 + typescript: 5.8.2 + transitivePeerDependencies: + - '@storybook/builder-webpack5' + - '@storybook/manager-webpack5' + - '@types/webpack' + - bufferutil + - encoding + - eslint + - sockjs-client + - supports-color + - type-fest + - utf-8-validate + - vue-template-compiler + - webpack-cli + - webpack-command + - webpack-dev-server + - webpack-hot-middleware + - webpack-plugin-serve + + '@storybook/react@9.1.20(@types/node@20.17.19)(@types/react@19.2.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1))(typescript@5.8.2)': + dependencies: + '@storybook/global': 5.0.0 + '@storybook/react-dom-shim': 9.1.20(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1)) + '@types/node': 20.17.19 + '@types/react': 19.2.7 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + storybook: 9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1) + optionalDependencies: + typescript: 5.8.2 + + '@storybook/router@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@storybook/client-logger': 6.4.22 + '@types/react': 17.0.74 + core-js: 3.49.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + history: 5.0.0 + lodash: 4.18.1 + memoizerific: 1.11.3 + qs: 6.15.0 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-router: 6.30.3(@types/react@17.0.74)(react@17.0.2) + react-router-dom: 6.30.3(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + ts-dedent: 2.2.0 + + '@storybook/semver@7.3.2': + dependencies: + core-js: 3.49.0 + find-up: 4.1.0 + + '@storybook/source-loader@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/client-logger': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + core-js: 3.49.0 + estraverse: 5.3.0 + global: 4.4.0 + loader-utils: 2.0.4 + lodash: 4.18.1 + prettier: 2.3.0 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + regenerator-runtime: 0.13.11 + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@storybook/store@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/client-logger': 6.4.22 + '@storybook/core-events': 6.4.22 + '@storybook/csf': 0.0.2--canary.87bc651.0 + core-js: 3.49.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.18.1 + memoizerific: 1.11.3 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + regenerator-runtime: 0.13.11 + slash: 3.0.0 + stable: 0.1.8 + synchronous-promise: 2.0.17 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@storybook/theming@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@emotion/core': 10.3.1(@types/react@17.0.74)(react@17.0.2) + '@emotion/is-prop-valid': 0.8.8 + '@emotion/serialize': 1.3.3 + '@emotion/styled': 10.3.0(@emotion/core@10.3.1(@types/react@17.0.74)(react@17.0.2))(@types/react@17.0.74)(react@17.0.2) + '@emotion/utils': 1.4.2 + '@storybook/client-logger': 6.4.22 + core-js: 3.49.0 + deep-object-diff: 1.1.9 + emotion-theming: 10.3.0(@emotion/core@10.3.1(@types/react@17.0.74)(react@17.0.2))(@types/react@17.0.74)(react@17.0.2) + global: 4.4.0 + memoizerific: 1.11.3 + polished: 4.3.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + resolve-from: 5.0.0 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@storybook/ui@6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@emotion/core': 10.3.1(@types/react@17.0.74)(react@17.0.2) + '@storybook/addons': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/api': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/channels': 6.4.22 + '@storybook/client-logger': 6.4.22 + '@storybook/components': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/core-events': 6.4.22 + '@storybook/router': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.4.22(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + copy-to-clipboard: 3.3.3 + core-js: 3.49.0 + core-js-pure: 3.49.0 + downshift: 6.1.12(react@17.0.2) + emotion-theming: 10.3.0(@emotion/core@10.3.1(@types/react@17.0.74)(react@17.0.2))(@types/react@17.0.74)(react@17.0.2) + fuse.js: 3.6.1 + global: 4.4.0 + lodash: 4.18.1 + markdown-to-jsx: 7.7.17(react@17.0.2) + memoizerific: 1.11.3 + polished: 4.3.1 + qs: 6.15.0 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-draggable: 4.5.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + react-helmet-async: 1.3.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + react-sizeme: 3.0.2 + regenerator-runtime: 0.13.11 + resolve-from: 5.0.0 + store2: 2.14.4 + transitivePeerDependencies: + - '@types/react' + - supports-color + + '@swc/core-darwin-arm64@1.7.10': + optional: true + + '@swc/core-darwin-x64@1.7.10': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.7.10': + optional: true + + '@swc/core-linux-arm64-gnu@1.7.10': + optional: true + + '@swc/core-linux-arm64-musl@1.7.10': + optional: true + + '@swc/core-linux-x64-gnu@1.7.10': + optional: true + + '@swc/core-linux-x64-musl@1.7.10': + optional: true + + '@swc/core-win32-arm64-msvc@1.7.10': + optional: true + + '@swc/core-win32-ia32-msvc@1.7.10': + optional: true + + '@swc/core-win32-x64-msvc@1.7.10': + optional: true + + '@swc/core@1.7.10(@swc/helpers@0.5.21)': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.26 + optionalDependencies: + '@swc/core-darwin-arm64': 1.7.10 + '@swc/core-darwin-x64': 1.7.10 + '@swc/core-linux-arm-gnueabihf': 1.7.10 + '@swc/core-linux-arm64-gnu': 1.7.10 + '@swc/core-linux-arm64-musl': 1.7.10 + '@swc/core-linux-x64-gnu': 1.7.10 + '@swc/core-linux-x64-musl': 1.7.10 + '@swc/core-win32-arm64-msvc': 1.7.10 + '@swc/core-win32-ia32-msvc': 1.7.10 + '@swc/core-win32-x64-msvc': 1.7.10 + '@swc/helpers': 0.5.21 + + '@swc/counter@0.1.3': {} + + '@swc/helpers@0.5.21': + dependencies: + tslib: 2.8.1 + + '@swc/types@0.1.26': + dependencies: + '@swc/counter': 0.1.3 + + '@szmarczak/http-timer@4.0.6': + dependencies: + defer-to-connect: 2.0.1 + + '@testing-library/dom@7.21.8': + dependencies: + '@babel/runtime': 7.29.2 + '@types/aria-query': 4.2.2 + aria-query: 4.2.2 + dom-accessibility-api: 0.4.7 + pretty-format: 25.5.0 + + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.4.4 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/user-event@14.6.1(@testing-library/dom@7.21.8)': + dependencies: + '@testing-library/dom': 7.21.8 + + '@tootallnate/once@1.1.2': {} + + '@trpc/server@9.27.4': {} + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/argparse@1.0.38': {} + + '@types/aria-query@4.2.2': {} + + '@types/aws-lambda@8.10.93': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 22.9.3 + + '@types/bonjour@3.5.13': + dependencies: + '@types/node': 22.9.3 + + '@types/cacheable-request@6.0.3': + dependencies: + '@types/http-cache-semantics': 4.2.0 + '@types/keyv': 3.1.4 + '@types/node': 22.9.3 + '@types/responselike': 1.0.3 + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/color-convert@2.0.4': + dependencies: + '@types/color-name': 1.1.5 + + '@types/color-name@1.1.5': {} + + '@types/compression@1.7.5(@types/express@4.17.21)': + dependencies: + '@types/express': 4.17.21 + + '@types/connect-history-api-fallback@1.5.4': + dependencies: + '@types/express-serve-static-core': 5.1.1 + '@types/node': 22.9.3 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 22.9.3 + + '@types/cors@2.8.19': + dependencies: + '@types/node': 22.9.3 + + '@types/cross-spawn@6.0.6': + dependencies: + '@types/node': 22.9.3 + + '@types/deep-eql@4.0.2': {} + + '@types/doctrine@0.0.9': {} + + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.8 + + '@types/eslint@8.56.10': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + + '@types/estree@1.0.8': {} + + '@types/events@3.0.3': {} + + '@types/express-serve-static-core@4.19.8': + dependencies: + '@types/node': 22.9.3 + '@types/qs': 6.15.0 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.1 + + '@types/express-serve-static-core@5.1.1': + dependencies: + '@types/node': 22.9.3 + '@types/qs': 6.15.0 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.1 + + '@types/express@4.17.21': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 4.19.8 + '@types/qs': 6.15.0 + '@types/serve-static': 2.2.0 + + '@types/express@4.17.25': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 4.19.8 + '@types/qs': 6.15.0 + '@types/serve-static': 1.15.10 + + '@types/fs-extra@7.0.0': + dependencies: + '@types/node': 22.9.3 + + '@types/glob@7.1.1': + dependencies: + '@types/events': 3.0.3 + '@types/minimatch': 6.0.0 + '@types/node': 22.9.3 + + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 22.9.3 + + '@types/hast@2.3.10': + dependencies: + '@types/unist': 2.0.11 + + '@types/hoist-non-react-statics@3.3.7(@types/react@19.2.7)': + dependencies: + '@types/react': 19.2.7 + hoist-non-react-statics: 3.3.2 + + '@types/html-minifier-terser@5.1.2': {} + + '@types/html-minifier-terser@6.1.0': {} + + '@types/http-cache-semantics@4.2.0': {} + + '@types/http-errors@2.0.5': {} + + '@types/http-proxy@1.17.17': + dependencies: + '@types/node': 22.9.3 + + '@types/is-function@1.0.3': {} + + '@types/istanbul-lib-coverage@2.0.4': {} + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@1.1.2': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-lib-report': 3.0.3 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@23.3.13': {} + + '@types/jest@28.1.1': + dependencies: + jest-matcher-utils: 27.5.1 + pretty-format: 27.5.1 + + '@types/jest@29.5.14': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + + '@types/jest@30.0.0': + dependencies: + expect: 30.3.0 + pretty-format: 30.3.0 + + '@types/jju@1.4.1': {} + + '@types/js-yaml@4.0.9': {} + + '@types/jsdom@21.1.7': + dependencies: + '@types/node': 22.9.3 + '@types/tough-cookie': 4.0.5 + parse5: 7.3.0 + + '@types/json-schema@7.0.15': {} + + '@types/json-stable-stringify-without-jsonify@1.0.2': {} + + '@types/json5@0.0.29': {} + + '@types/keyv@3.1.4': + dependencies: + '@types/node': 22.9.3 + + '@types/loader-utils@1.1.3': + dependencies: + '@types/node': 22.9.3 + '@types/webpack': 4.41.32 + + '@types/lodash@4.17.23': {} + + '@types/long@4.0.0': {} + + '@types/mdast@3.0.15': + dependencies: + '@types/unist': 2.0.11 + + '@types/mime-types@2.1.4': {} + + '@types/mime@1.3.5': {} + + '@types/minimatch@6.0.0': + dependencies: + minimatch: 10.2.3 + + '@types/mocha@10.0.6': {} + + '@types/node-fetch@2.6.13': + dependencies: + '@types/node': 22.9.3 + form-data: 4.0.5 + + '@types/node-forge@1.3.14': + dependencies: + '@types/node': 22.9.3 + + '@types/node@14.0.1': {} + + '@types/node@14.18.63': {} + + '@types/node@17.0.41': {} + + '@types/node@20.17.19': + dependencies: + undici-types: 6.19.8 + + '@types/node@22.9.3': + dependencies: + undici-types: 6.19.8 + + '@types/normalize-package-data@2.4.4': {} + + '@types/npm-package-arg@6.1.0': {} + + '@types/npm-packlist@1.1.2': {} + + '@types/npmlog@4.1.6': + dependencies: + '@types/node': 22.9.3 + + '@types/object-hash@3.0.6': {} + + '@types/overlayscrollbars@1.12.5': {} + + '@types/parse-json@4.0.2': {} + + '@types/parse5@5.0.3': {} + + '@types/pretty-hrtime@1.0.3': {} + + '@types/prismjs@1.26.6': {} + + '@types/prop-types@15.7.15': {} + + '@types/qs@6.15.0': {} + + '@types/range-parser@1.2.7': {} + + '@types/react-dom@17.0.25': + dependencies: + '@types/react': 17.0.74 + + '@types/react-dom@19.2.3(@types/react@19.2.7)': + dependencies: + '@types/react': 19.2.7 + + '@types/react-redux@7.1.34': + dependencies: + '@types/hoist-non-react-statics': 3.3.7(@types/react@19.2.7) + '@types/react': 19.2.7 + hoist-non-react-statics: 3.3.2 + redux: 4.2.1 + + '@types/react-syntax-highlighter@11.0.5': + dependencies: + '@types/react': 19.2.7 + + '@types/react@17.0.74': + dependencies: + '@types/prop-types': 15.7.15 + '@types/scheduler': 0.16.8 + csstype: 3.2.3 + + '@types/react@19.2.7': + dependencies: + csstype: 3.2.3 + + '@types/read-package-tree@5.1.0': {} + + '@types/resolve@1.20.2': {} + + '@types/responselike@1.0.3': + dependencies: + '@types/node': 22.9.3 + + '@types/retry@0.12.0': {} + + '@types/retry@0.12.2': {} + + '@types/scheduler@0.16.8': {} + + '@types/semver@7.7.1': {} + + '@types/send@0.17.6': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 22.9.3 + + '@types/send@1.2.1': + dependencies: + '@types/node': 22.9.3 + + '@types/serialize-javascript@5.0.4': {} + + '@types/serve-index@1.9.4': + dependencies: + '@types/express': 4.17.21 + + '@types/serve-static@1.15.10': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 22.9.3 + '@types/send': 0.17.6 + + '@types/serve-static@2.2.0': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 22.9.3 + + '@types/sockjs@0.3.36': + dependencies: + '@types/node': 22.9.3 + + '@types/source-list-map@0.1.6': {} + + '@types/ssri@7.1.5': + dependencies: + '@types/node': 22.9.3 + + '@types/stack-utils@2.0.3': {} + + '@types/strict-uri-encode@2.0.0': {} + + '@types/supports-color@8.1.3': {} + + '@types/tapable@1.0.6': {} + + '@types/tough-cookie@4.0.5': {} + + '@types/uglify-js@3.17.5': + dependencies: + source-map: 0.6.1 + + '@types/unist@2.0.11': {} + + '@types/use-sync-external-store@0.0.6': {} + + '@types/vscode@1.103.0': {} + + '@types/watchpack@2.4.0': + dependencies: + '@types/graceful-fs': 4.1.9 + '@types/node': 22.9.3 + + '@types/webpack-env@1.18.8': {} + + '@types/webpack-sources@1.4.2': + dependencies: + '@types/node': 22.9.3 + '@types/source-list-map': 0.1.6 + source-map: 0.7.6 + + '@types/webpack@4.41.32': + dependencies: + '@types/node': 22.9.3 + '@types/tapable': 1.0.6 + '@types/uglify-js': 3.17.5 + '@types/webpack-sources': 1.4.2 + anymatch: 3.1.3 + source-map: 0.6.1 + + '@types/ws@8.18.1': + dependencies: + '@types/node': 22.9.3 + + '@types/xmldoc@1.1.4': {} + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@15.0.20': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@types/yargs@17.0.35': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@typescript-eslint/eslint-plugin@6.19.1(@typescript-eslint/parser@6.19.1(eslint@7.11.0)(typescript@5.8.2))(eslint@7.11.0)(typescript@5.8.2)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 6.19.1(eslint@7.11.0)(typescript@5.8.2) + '@typescript-eslint/scope-manager': 6.19.1(typescript@5.8.2) + '@typescript-eslint/type-utils': 6.19.1(eslint@7.11.0)(typescript@5.8.2) + '@typescript-eslint/utils': 6.19.1(eslint@7.11.0)(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 6.19.1(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 7.11.0 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + semver: 7.7.4 + ts-api-utils: 1.4.3(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/eslint-plugin@6.19.1(@typescript-eslint/parser@6.19.1(eslint@7.30.0)(typescript@5.8.2))(eslint@7.30.0)(typescript@5.8.2)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 6.19.1(eslint@7.30.0)(typescript@5.8.2) + '@typescript-eslint/scope-manager': 6.19.1(typescript@5.8.2) + '@typescript-eslint/type-utils': 6.19.1(eslint@7.30.0)(typescript@5.8.2) + '@typescript-eslint/utils': 6.19.1(eslint@7.30.0)(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 6.19.1(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 7.30.0 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + semver: 7.7.4 + ts-api-utils: 1.4.3(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/eslint-plugin@6.19.1(@typescript-eslint/parser@6.19.1(eslint@7.7.0)(typescript@5.8.2))(eslint@7.7.0)(typescript@5.8.2)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 6.19.1(eslint@7.7.0)(typescript@5.8.2) + '@typescript-eslint/scope-manager': 6.19.1(typescript@5.8.2) + '@typescript-eslint/type-utils': 6.19.1(eslint@7.7.0)(typescript@5.8.2) + '@typescript-eslint/utils': 6.19.1(eslint@7.7.0)(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 6.19.1(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 7.7.0 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + semver: 7.7.4 + ts-api-utils: 1.4.3(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/eslint-plugin@6.19.1(@typescript-eslint/parser@6.19.1(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 6.19.1(eslint@8.57.1)(typescript@5.8.2) + '@typescript-eslint/scope-manager': 6.19.1(typescript@5.8.2) + '@typescript-eslint/type-utils': 6.19.1(eslint@8.57.1)(typescript@5.8.2) + '@typescript-eslint/utils': 6.19.1(eslint@8.57.1)(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 6.19.1(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 8.57.1 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + semver: 7.7.4 + ts-api-utils: 1.4.3(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1)(typescript@4.9.5)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.56.1(eslint@8.57.1)(typescript@4.9.5) + '@typescript-eslint/scope-manager': 8.56.1(typescript@4.9.5) + '@typescript-eslint/type-utils': 8.56.1(eslint@8.57.1)(typescript@4.9.5) + '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@4.9.5) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@4.9.5) + eslint: 8.57.1 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.37.0)(typescript@5.8.2))(eslint@9.37.0)(typescript@5.8.2)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.56.1(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/scope-manager': 8.56.1(typescript@5.8.2) + '@typescript-eslint/type-utils': 8.56.1(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@5.8.2) + eslint: 9.37.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@6.19.1(eslint@7.11.0)(typescript@5.8.2)': + dependencies: + '@typescript-eslint/scope-manager': 6.19.1(typescript@5.8.2) + '@typescript-eslint/types': 6.19.1(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 6.19.1(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 7.11.0 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@6.19.1(eslint@7.30.0)(typescript@5.8.2)': + dependencies: + '@typescript-eslint/scope-manager': 6.19.1(typescript@5.8.2) + '@typescript-eslint/types': 6.19.1(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 6.19.1(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 7.30.0 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@6.19.1(eslint@7.7.0)(typescript@5.8.2)': + dependencies: + '@typescript-eslint/scope-manager': 6.19.1(typescript@5.8.2) + '@typescript-eslint/types': 6.19.1(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 6.19.1(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 7.7.0 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@6.19.1(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@typescript-eslint/scope-manager': 6.19.1(typescript@5.8.2) + '@typescript-eslint/types': 6.19.1(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 6.19.1(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 8.57.1 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@4.9.5)': + dependencies: + '@typescript-eslint/scope-manager': 8.56.1(typescript@4.9.5) + '@typescript-eslint/types': 8.56.1(typescript@4.9.5) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@4.9.5) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@4.9.5) + debug: 4.4.3(supports-color@8.1.1) + eslint: 8.57.1 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.56.1(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@typescript-eslint/scope-manager': 8.56.1(typescript@5.8.2) + '@typescript-eslint/types': 8.56.1(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 8.57.1 + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.56.1(eslint@9.37.0)(typescript@5.8.2)': + dependencies: + '@typescript-eslint/scope-manager': 8.56.1(typescript@5.8.2) + '@typescript-eslint/types': 8.56.1(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 9.37.0 + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.56.1(typescript@4.9.5)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@4.9.5) + '@typescript-eslint/types': 8.56.1(typescript@4.9.5) + debug: 4.4.3(supports-color@8.1.1) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.56.1(typescript@5.8.2)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.8.2) + '@typescript-eslint/types': 8.56.1(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/rule-tester@8.56.1(eslint@9.37.0)(typescript@5.8.2)': + dependencies: + '@types/semver': 7.7.1 + '@typescript-eslint/parser': 8.56.1(eslint@9.37.0)(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.8.2) + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.2) + ajv: 6.14.0 + eslint: 9.37.0 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/scope-manager@6.19.1(typescript@5.8.2)': + dependencies: + '@typescript-eslint/types': 6.19.1(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 6.19.1(typescript@5.8.2) + transitivePeerDependencies: + - typescript + + '@typescript-eslint/scope-manager@8.56.1(typescript@4.9.5)': + dependencies: + '@typescript-eslint/types': 8.56.1(typescript@4.9.5) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@4.9.5) + transitivePeerDependencies: + - typescript + + '@typescript-eslint/scope-manager@8.56.1(typescript@5.8.2)': + dependencies: + '@typescript-eslint/types': 8.56.1(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@5.8.2) + transitivePeerDependencies: + - typescript + + '@typescript-eslint/tsconfig-utils@8.56.1(typescript@4.9.5)': + dependencies: + typescript: 4.9.5 + + '@typescript-eslint/tsconfig-utils@8.56.1(typescript@5.8.2)': + dependencies: + typescript: 5.8.2 + + '@typescript-eslint/type-utils@6.19.1(eslint@7.11.0)(typescript@5.8.2)': + dependencies: + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + '@typescript-eslint/utils': 6.19.1(eslint@7.11.0)(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 7.11.0 + ts-api-utils: 1.4.3(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/type-utils@6.19.1(eslint@7.30.0)(typescript@5.8.2)': + dependencies: + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + '@typescript-eslint/utils': 6.19.1(eslint@7.30.0)(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 7.30.0 + ts-api-utils: 1.4.3(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/type-utils@6.19.1(eslint@7.7.0)(typescript@5.8.2)': + dependencies: + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + '@typescript-eslint/utils': 6.19.1(eslint@7.7.0)(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 7.7.0 + ts-api-utils: 1.4.3(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/type-utils@6.19.1(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + '@typescript-eslint/utils': 6.19.1(eslint@8.57.1)(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 8.57.1 + ts-api-utils: 1.4.3(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/type-utils@8.56.1(eslint@8.57.1)(typescript@4.9.5)': + dependencies: + '@typescript-eslint/types': 8.56.1(typescript@4.9.5) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@4.9.5) + '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@4.9.5) + debug: 4.4.3(supports-color@8.1.1) + eslint: 8.57.1 + ts-api-utils: 2.5.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/type-utils@8.56.1(eslint@9.37.0)(typescript@5.8.2)': + dependencies: + '@typescript-eslint/types': 8.56.1(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.8.2) + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + eslint: 9.37.0 + ts-api-utils: 2.5.0(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@6.19.1(typescript@5.8.2)': + dependencies: + typescript: 5.8.2 + + '@typescript-eslint/types@8.56.1(typescript@4.9.5)': + dependencies: + typescript: 4.9.5 + + '@typescript-eslint/types@8.56.1(typescript@5.8.2)': + dependencies: + typescript: 5.8.2 + + '@typescript-eslint/typescript-estree@6.19.1(typescript@5.8.2)': + dependencies: + '@typescript-eslint/types': 6.19.1(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 6.19.1(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.7.4 + ts-api-utils: 1.4.3(typescript@5.8.2) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.56.1(typescript@4.9.5)': + dependencies: + '@typescript-eslint/project-service': 8.56.1(typescript@4.9.5) + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@4.9.5) + '@typescript-eslint/types': 8.56.1(typescript@4.9.5) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@4.9.5) + debug: 4.4.3(supports-color@8.1.1) + minimatch: 10.2.3 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.5.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.56.1(typescript@5.8.2)': + dependencies: + '@typescript-eslint/project-service': 8.56.1(typescript@5.8.2) + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.8.2) + '@typescript-eslint/types': 8.56.1(typescript@5.8.2) + '@typescript-eslint/visitor-keys': 8.56.1(typescript@5.8.2) + debug: 4.4.3(supports-color@8.1.1) + minimatch: 10.2.3 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.5.0(typescript@5.8.2) + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@6.19.1(eslint@7.11.0)(typescript@5.8.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@7.11.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.7.1 + '@typescript-eslint/scope-manager': 6.19.1(typescript@5.8.2) + '@typescript-eslint/types': 6.19.1(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + eslint: 7.11.0 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/utils@6.19.1(eslint@7.30.0)(typescript@5.8.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@7.30.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.7.1 + '@typescript-eslint/scope-manager': 6.19.1(typescript@5.8.2) + '@typescript-eslint/types': 6.19.1(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + eslint: 7.30.0 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/utils@6.19.1(eslint@7.7.0)(typescript@5.8.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@7.7.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.7.1 + '@typescript-eslint/scope-manager': 6.19.1(typescript@5.8.2) + '@typescript-eslint/types': 6.19.1(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + eslint: 7.7.0 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/utils@6.19.1(eslint@8.57.1)(typescript@5.8.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@types/json-schema': 7.0.15 + '@types/semver': 7.7.1 + '@typescript-eslint/scope-manager': 6.19.1(typescript@5.8.2) + '@typescript-eslint/types': 6.19.1(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.8.2) + eslint: 8.57.1 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/utils@8.56.1(eslint@8.57.1)(typescript@4.9.5)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@typescript-eslint/scope-manager': 8.56.1(typescript@4.9.5) + '@typescript-eslint/types': 8.56.1(typescript@4.9.5) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@4.9.5) + eslint: 8.57.1 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.56.1(eslint@9.37.0)(typescript@5.8.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.37.0) + '@typescript-eslint/scope-manager': 8.56.1(typescript@5.8.2) + '@typescript-eslint/types': 8.56.1(typescript@5.8.2) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.8.2) + eslint: 9.37.0 + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@6.19.1(typescript@5.8.2)': + dependencies: + '@typescript-eslint/types': 6.19.1(typescript@5.8.2) + eslint-visitor-keys: 3.4.3 + transitivePeerDependencies: + - typescript + + '@typescript-eslint/visitor-keys@8.56.1(typescript@4.9.5)': + dependencies: + '@typescript-eslint/types': 8.56.1(typescript@4.9.5) + eslint-visitor-keys: 5.0.1 + transitivePeerDependencies: + - typescript + + '@typescript-eslint/visitor-keys@8.56.1(typescript@5.8.2)': + dependencies: + '@typescript-eslint/types': 8.56.1(typescript@5.8.2) + eslint-visitor-keys: 5.0.1 + transitivePeerDependencies: + - typescript + + '@typespec/ts-http-runtime@0.3.4': + dependencies: + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@ungap/structured-clone@1.3.0': {} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + optional: true + + '@unrs/resolver-binding-android-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + dependencies: + '@napi-rs/wasm-runtime': 0.2.12 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + optional: true + + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.4 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + '@vscode/test-electron@1.6.2': + dependencies: + http-proxy-agent: 4.0.1 + https-proxy-agent: 5.0.1 + rimraf: 3.0.2 + unzipper: 0.10.14 + transitivePeerDependencies: + - supports-color + + '@vscode/vsce-sign-alpine-arm64@2.0.6': + optional: true + + '@vscode/vsce-sign-alpine-x64@2.0.6': + optional: true + + '@vscode/vsce-sign-darwin-arm64@2.0.6': + optional: true + + '@vscode/vsce-sign-darwin-x64@2.0.6': + optional: true + + '@vscode/vsce-sign-linux-arm64@2.0.6': + optional: true + + '@vscode/vsce-sign-linux-arm@2.0.6': + optional: true + + '@vscode/vsce-sign-linux-x64@2.0.6': + optional: true + + '@vscode/vsce-sign-win32-arm64@2.0.6': + optional: true + + '@vscode/vsce-sign-win32-x64@2.0.6': + optional: true + + '@vscode/vsce-sign@2.0.9': + optionalDependencies: + '@vscode/vsce-sign-alpine-arm64': 2.0.6 + '@vscode/vsce-sign-alpine-x64': 2.0.6 + '@vscode/vsce-sign-darwin-arm64': 2.0.6 + '@vscode/vsce-sign-darwin-x64': 2.0.6 + '@vscode/vsce-sign-linux-arm': 2.0.6 + '@vscode/vsce-sign-linux-arm64': 2.0.6 + '@vscode/vsce-sign-linux-x64': 2.0.6 + '@vscode/vsce-sign-win32-arm64': 2.0.6 + '@vscode/vsce-sign-win32-x64': 2.0.6 + + '@vscode/vsce@3.2.1': + dependencies: + '@azure/identity': 4.13.1 + '@vscode/vsce-sign': 2.0.9 + azure-devops-node-api: 12.5.0 + chalk: 2.4.2 + cheerio: 1.0.0-rc.12 + cockatiel: 3.2.1 + commander: 6.2.1 + form-data: 4.0.5 + glob: 11.1.0 + hosted-git-info: 4.1.0 + jsonc-parser: 3.3.1 + leven: 3.1.0 + markdown-it: 14.1.1 + mime: 1.6.0 + minimatch: 3.1.5 + parse-semver: 1.1.1 + read: 1.0.7 + semver: 7.7.4 + tmp: 0.2.5 + typed-rest-client: 1.8.11 + url-join: 4.0.1 + xml2js: 0.5.0 + yauzl: 2.10.0 + yazl: 2.5.1 + optionalDependencies: + keytar: 7.9.0 + transitivePeerDependencies: + - supports-color + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/ast@1.9.0': + dependencies: + '@webassemblyjs/helper-module-context': 1.9.0 + '@webassemblyjs/helper-wasm-bytecode': 1.9.0 + '@webassemblyjs/wast-parser': 1.9.0 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/floating-point-hex-parser@1.9.0': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.9.0': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-buffer@1.9.0': {} + + '@webassemblyjs/helper-code-frame@1.9.0': + dependencies: + '@webassemblyjs/wast-printer': 1.9.0 + + '@webassemblyjs/helper-fsm@1.9.0': {} + + '@webassemblyjs/helper-module-context@1.9.0': + dependencies: + '@webassemblyjs/ast': 1.9.0 + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-bytecode@1.9.0': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/helper-wasm-section@1.9.0': + dependencies: + '@webassemblyjs/ast': 1.9.0 + '@webassemblyjs/helper-buffer': 1.9.0 + '@webassemblyjs/helper-wasm-bytecode': 1.9.0 + '@webassemblyjs/wasm-gen': 1.9.0 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/ieee754@1.9.0': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/leb128@1.9.0': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/utf8@1.9.0': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-edit@1.9.0': + dependencies: + '@webassemblyjs/ast': 1.9.0 + '@webassemblyjs/helper-buffer': 1.9.0 + '@webassemblyjs/helper-wasm-bytecode': 1.9.0 + '@webassemblyjs/helper-wasm-section': 1.9.0 + '@webassemblyjs/wasm-gen': 1.9.0 + '@webassemblyjs/wasm-opt': 1.9.0 + '@webassemblyjs/wasm-parser': 1.9.0 + '@webassemblyjs/wast-printer': 1.9.0 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-gen@1.9.0': + dependencies: + '@webassemblyjs/ast': 1.9.0 + '@webassemblyjs/helper-wasm-bytecode': 1.9.0 + '@webassemblyjs/ieee754': 1.9.0 + '@webassemblyjs/leb128': 1.9.0 + '@webassemblyjs/utf8': 1.9.0 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-opt@1.9.0': + dependencies: + '@webassemblyjs/ast': 1.9.0 + '@webassemblyjs/helper-buffer': 1.9.0 + '@webassemblyjs/wasm-gen': 1.9.0 + '@webassemblyjs/wasm-parser': 1.9.0 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-parser@1.9.0': + dependencies: + '@webassemblyjs/ast': 1.9.0 + '@webassemblyjs/helper-api-error': 1.9.0 + '@webassemblyjs/helper-wasm-bytecode': 1.9.0 + '@webassemblyjs/ieee754': 1.9.0 + '@webassemblyjs/leb128': 1.9.0 + '@webassemblyjs/utf8': 1.9.0 + + '@webassemblyjs/wast-parser@1.9.0': + dependencies: + '@webassemblyjs/ast': 1.9.0 + '@webassemblyjs/floating-point-hex-parser': 1.9.0 + '@webassemblyjs/helper-api-error': 1.9.0 + '@webassemblyjs/helper-code-frame': 1.9.0 + '@webassemblyjs/helper-fsm': 1.9.0 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/wast-printer@1.9.0': + dependencies: + '@webassemblyjs/ast': 1.9.0 + '@webassemblyjs/wast-parser': 1.9.0 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + '@yarnpkg/lockfile@1.0.2': {} + + '@zkochan/cmd-shim@5.4.1': + dependencies: + cmd-extension: 1.0.2 + graceful-fs: 4.2.11 + is-windows: 1.0.2 + + '@zkochan/js-yaml@0.0.11': + dependencies: + argparse: 2.0.1 + + '@zkochan/js-yaml@0.0.6': + dependencies: + argparse: 2.0.1 + + '@zkochan/rimraf@2.1.3': + dependencies: + rimraf: 3.0.2 + + '@zkochan/rimraf@3.0.2': {} + + '@zkochan/which@2.0.3': + dependencies: + isexe: 2.0.0 + + abab@2.0.6: {} + + abbrev@1.1.1: {} + + abstract-logging@2.0.1: {} + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + + acorn-import-phases@1.0.4(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn-jsx@5.3.2(acorn@7.4.1): + dependencies: + acorn: 7.4.1 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn-walk@7.2.0: {} + + acorn-walk@8.3.5: + dependencies: + acorn: 8.16.0 + + acorn@6.4.2: {} + + acorn@7.4.1: {} + + acorn@8.16.0: {} + + address@1.2.2: {} + + agent-base@5.1.1: {} + + agent-base@6.0.2: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + agent-base@7.1.4: {} + + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + + aggregate-error@3.1.0: + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + + airbnb-js-shims@2.2.1: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + es5-shim: 4.6.7 + es6-shim: 0.35.8 + function.prototype.name: 1.1.8 + globalthis: 1.0.4 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.getownpropertydescriptors: 2.1.9 + object.values: 1.2.1 + promise.allsettled: 1.0.7 + promise.prototype.finally: 3.1.8 + string.prototype.matchall: 4.0.12 + string.prototype.padend: 3.1.6 + string.prototype.padstart: 3.1.7 + symbol.prototype.description: 1.0.7 + + ajv-draft-04@1.0.0(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv-errors@1.0.1(ajv@6.14.0): + dependencies: + ajv: 6.14.0 + + ajv-formats@2.1.1: + dependencies: + ajv: 8.20.0 + + ajv-formats@3.0.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv-keywords@3.5.2(ajv@6.14.0): + dependencies: + ajv: 6.14.0 + + ajv-keywords@5.1.0(ajv@8.20.0): + dependencies: + ajv: 8.20.0 + fast-deep-equal: 3.1.3 + + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.12.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + + ansi-colors@3.2.4: {} + + ansi-colors@4.1.3: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-html-community@0.0.8: {} + + ansi-html@0.0.9: {} + + ansi-regex@2.1.1: {} + + ansi-regex@4.1.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.3: {} + + ansi-to-html@0.6.15: + dependencies: + entities: 2.2.0 + + any-promise@1.3.0: {} + + anymatch@2.0.0: + dependencies: + micromatch: 3.1.10 + normalize-path: 2.1.1 + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + app-root-dir@1.0.2: {} + + aproba@1.2.0: {} + + aproba@2.1.0: {} + + archiver-utils@2.1.0: + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 2.3.8 + + archiver-utils@3.0.4: + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + + archiver@5.3.2: + dependencies: + archiver-utils: 2.1.0 + async: 3.2.6 + buffer-crc32: 0.2.13 + readable-stream: 3.6.2 + readdir-glob: 1.1.3 + tar-stream: 2.2.0 + zip-stream: 4.1.1 + + archy@1.0.0: {} + + are-docs-informative@0.0.2: {} + + are-we-there-yet@1.1.7: + dependencies: + delegates: 1.0.0 + readable-stream: 2.3.8 + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + aria-query@4.2.2: + dependencies: + '@babel/runtime': 7.29.2 + '@babel/runtime-corejs3': 7.29.2 + + aria-query@5.3.2: {} + + arr-diff@4.0.0: {} + + arr-flatten@1.1.0: {} + + arr-union@3.1.0: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-flatten@1.1.1: {} + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array-union@1.0.2: + dependencies: + array-uniq: 1.0.3 + + array-union@2.1.0: {} + + array-uniq@1.0.3: {} + + array-unique@0.3.2: {} + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.map@1.0.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-array-method-boxes-properly: 1.0.0 + es-object-atoms: 1.1.1 + is-string: 1.1.1 + + array.prototype.reduce@1.0.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-array-method-boxes-properly: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + is-string: 1.1.1 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + arrify@2.0.1: {} + + asap@2.0.6: {} + + asn1.js@4.10.1: + dependencies: + bn.js: 4.12.3 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + asn1js@3.0.7: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.1.5 + tslib: 2.8.1 + + assert@1.5.1: + dependencies: + object.assign: 4.1.7 + util: 0.10.4 + + assertion-error@2.0.1: {} + + assign-symbols@1.0.0: {} + + ast-types@0.13.3: {} + + ast-types@0.14.2: + dependencies: + tslib: 2.8.1 + + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 + + astral-regex@1.0.0: {} + + astral-regex@2.0.0: {} + + async-each@1.0.6: + optional: true + + async-function@1.0.0: {} + + async-limiter@1.0.1: {} + + async-retry@1.3.3: + dependencies: + retry: 0.13.1 + + async@1.5.2: {} + + async@3.2.6: {} + + asynckit@0.4.0: {} + + at-least-node@1.0.0: {} + + atob@2.1.2: {} + + atomic-sleep@1.0.0: {} + + atomically@1.7.0: {} + + autoprefixer@10.4.27(postcss@8.5.12): + dependencies: + browserslist: 4.28.2 + caniuse-lite: 1.0.30001784 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + autoprefixer@9.8.8: + dependencies: + browserslist: 4.28.2 + caniuse-lite: 1.0.30001784 + normalize-range: 0.1.2 + num2fraction: 1.2.2 + picocolors: 0.2.1 + postcss: 7.0.39 + postcss-value-parser: 4.2.0 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + avvio@7.2.5: + dependencies: + archy: 1.0.0 + debug: 4.4.3(supports-color@8.1.1) + fastq: 1.20.1 + queue-microtask: 1.2.3 + transitivePeerDependencies: + - supports-color + + aws-cdk-lib@2.189.1(constructs@10.0.130): + dependencies: + '@aws-cdk/asset-awscli-v1': 2.2.273 + '@aws-cdk/asset-node-proxy-agent-v6': 2.1.1 + '@aws-cdk/cloud-assembly-schema': 41.2.0 + constructs: 10.0.130 + + aws-cdk-lib@2.50.0(constructs@10.0.130): + dependencies: + constructs: 10.0.130 + + aws-cdk@2.50.0: + optionalDependencies: + fsevents: 2.3.2 + + aws-sdk@2.1693.0: + dependencies: + buffer: 4.9.2 + events: 1.1.1 + ieee754: 1.1.13 + jmespath: 0.16.0 + querystring: 0.2.0 + sax: 1.2.1 + url: 0.10.3 + util: 0.12.5 + uuid: 8.0.0 + xml2js: 0.6.2 + + azure-devops-node-api@12.5.0: + dependencies: + tunnel: 0.0.6 + typed-rest-client: 1.8.11 + + babel-core@7.0.0-bridge.0(@babel/core@7.20.12): + dependencies: + '@babel/core': 7.20.12 + + babel-core@7.0.0-bridge.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + + babel-jest@29.7.0(@babel/core@7.20.12): + dependencies: + '@babel/core': 7.20.12 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.20.12) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-jest@30.3.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@jest/transform': 30.3.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 7.0.1 + babel-preset-jest: 30.3.0(@babel/core@7.29.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-loader@8.2.5(@babel/core@7.20.12)(webpack@4.47.0): + dependencies: + '@babel/core': 7.20.12 + find-cache-dir: 3.3.2 + loader-utils: 2.0.4 + make-dir: 3.1.0 + schema-utils: 2.7.1 + webpack: 4.47.0 + + babel-loader@8.2.5(@babel/core@7.20.12)(webpack@5.105.4): + dependencies: + '@babel/core': 7.20.12 + find-cache-dir: 3.3.2 + loader-utils: 2.0.4 + make-dir: 3.1.0 + schema-utils: 2.7.1 + webpack: 5.105.4 + + babel-plugin-add-react-displayname@0.0.5: {} + + babel-plugin-apply-mdx-type-prop@1.6.22(@babel/core@7.12.9): + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.10.4 + '@mdx-js/util': 1.6.22 + + babel-plugin-emotion@10.2.2: + dependencies: + '@babel/helper-module-imports': 7.28.6 + '@emotion/hash': 0.8.0 + '@emotion/memoize': 0.7.4 + '@emotion/serialize': 0.11.16 + babel-plugin-macros: 2.8.0 + babel-plugin-syntax-jsx: 6.18.0 + convert-source-map: 1.9.0 + escape-string-regexp: 1.0.5 + find-root: 1.1.0 + source-map: 0.5.7 + transitivePeerDependencies: + - supports-color + + babel-plugin-extract-import-names@1.6.22: + dependencies: + '@babel/helper-plugin-utils': 7.10.4 + + babel-plugin-istanbul@6.1.1: + dependencies: + '@babel/helper-plugin-utils': 7.28.6 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-istanbul@7.0.1: + dependencies: + '@babel/helper-plugin-utils': 7.28.6 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 6.0.3 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + + babel-plugin-jest-hoist@30.3.0: + dependencies: + '@types/babel__core': 7.20.5 + + babel-plugin-macros@2.8.0: + dependencies: + '@babel/runtime': 7.29.2 + cosmiconfig: 6.0.0 + resolve: 1.22.11 + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.29.2 + cosmiconfig: 7.1.0 + resolve: 1.22.11 + + babel-plugin-named-asset-import@0.3.8(@babel/core@7.20.12): + dependencies: + '@babel/core': 7.20.12 + + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.20.12): + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/core': 7.20.12 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.20.12) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.1.7(@babel/core@7.20.12): + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-define-polyfill-provider': 0.1.5(@babel/core@7.20.12) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.20.12): + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.20.12) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.20.12): + dependencies: + '@babel/core': 7.20.12 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.20.12) + transitivePeerDependencies: + - supports-color + + babel-plugin-react-docgen@4.2.1: + dependencies: + ast-types: 0.14.2 + lodash: 4.18.1 + react-docgen: 5.4.3 + transitivePeerDependencies: + - supports-color + + babel-plugin-syntax-jsx@6.18.0: {} + + babel-preset-current-node-syntax@1.2.0(@babel/core@7.20.12): + dependencies: + '@babel/core': 7.20.12 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.20.12) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.20.12) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.20.12) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.20.12) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.20.12) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.20.12) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.20.12) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.20.12) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.20.12) + + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) + + babel-preset-jest@29.6.3(@babel/core@7.20.12): + dependencies: + '@babel/core': 7.20.12 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.20.12) + + babel-preset-jest@30.3.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + babel-plugin-jest-hoist: 30.3.0 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + + bail@1.0.5: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + base64-js@1.5.1: {} + + base@0.11.2: + dependencies: + cache-base: 1.0.1 + class-utils: 0.3.6 + component-emitter: 1.3.1 + define-property: 1.0.0 + isobject: 3.0.1 + mixin-deep: 1.3.2 + pascalcase: 0.1.1 + + baseline-browser-mapping@2.10.13: {} + + batch-processor@1.0.0: {} + + batch@0.6.1: {} + + better-opn@2.1.1: + dependencies: + open: 7.4.2 + + better-opn@3.0.2: + dependencies: + open: 8.4.2 + + better-path-resolve@1.0.0: + dependencies: + is-windows: 1.0.2 + + big-integer@1.6.52: {} + + big.js@5.2.2: {} + + binary-extensions@1.13.1: + optional: true + + binary-extensions@2.3.0: {} + + binary@0.3.0: + dependencies: + buffers: 0.1.1 + chainsaw: 0.1.0 + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + optional: true + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + bluebird@3.4.7: {} + + bluebird@3.7.2: {} + + bn.js@4.12.3: {} + + bn.js@5.2.3: {} + + body-parser@1.20.3: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.13.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + + body-parser@1.20.4: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.14.2 + raw-body: 2.5.3 + type-is: 1.6.18 + unpipe: 1.0.0 + + body-parser@2.2.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3(supports-color@8.1.1) + http-errors: 2.0.1 + iconv-lite: 0.7.2 + on-finished: 2.4.1 + qs: 6.15.0 + raw-body: 3.0.2 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + bole@5.0.28: + dependencies: + fast-safe-stringify: 2.1.1 + individual: 3.0.0 + + bonjour-service@1.3.0: + dependencies: + fast-deep-equal: 3.1.3 + multicast-dns: 7.2.5 + + boolbase@1.0.0: {} + + bowser@2.14.1: {} + + boxen@5.1.2: + dependencies: + ansi-align: 3.0.1 + camelcase: 6.3.0 + chalk: 4.1.2 + cli-boxes: 2.2.1 + string-width: 4.2.3 + type-fest: 0.20.2 + widest-line: 3.1.0 + wrap-ansi: 7.0.0 + + brace-expansion@1.1.13: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.3: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.5: + dependencies: + balanced-match: 4.0.4 + + braces@2.3.2: + dependencies: + arr-flatten: 1.1.0 + array-unique: 0.3.2 + extend-shallow: 2.0.1 + fill-range: 4.0.0 + isobject: 3.0.1 + repeat-element: 1.1.4 + snapdragon: 0.8.2 + snapdragon-node: 2.1.1 + split-string: 3.1.0 + to-regex: 3.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + brorand@1.1.0: {} + + browser-stdout@1.3.1: {} + + browserify-aes@1.2.0: + dependencies: + buffer-xor: 1.0.3 + cipher-base: 1.0.7 + create-hash: 1.2.0 + evp_bytestokey: 1.0.3 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + browserify-cipher@1.0.1: + dependencies: + browserify-aes: 1.2.0 + browserify-des: 1.0.2 + evp_bytestokey: 1.0.3 + + browserify-des@1.0.2: + dependencies: + cipher-base: 1.0.7 + des.js: 1.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + browserify-rsa@4.1.1: + dependencies: + bn.js: 5.2.3 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + browserify-sign@4.2.5: + dependencies: + bn.js: 5.2.3 + browserify-rsa: 4.1.1 + create-hash: 1.2.0 + create-hmac: 1.1.7 + elliptic: 6.6.1 + inherits: 2.0.4 + parse-asn1: 5.1.9 + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + + browserify-zlib@0.2.0: + dependencies: + pako: 1.0.11 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.13 + caniuse-lite: 1.0.30001784 + electron-to-chromium: 1.5.331 + node-releases: 2.0.37 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + buffer-builder@0.2.0: {} + + buffer-crc32@0.2.13: {} + + buffer-equal-constant-time@1.0.1: {} + + buffer-from@1.1.2: {} + + buffer-indexof-polyfill@1.0.2: {} + + buffer-xor@1.0.3: {} + + buffer@4.9.2: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + isarray: 1.0.0 + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffers@0.1.1: {} + + builtin-modules@1.1.1: {} + + builtin-status-codes@3.0.0: {} + + builtins@1.0.3: {} + + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + + buttono@1.0.4: {} + + bytes@3.1.2: {} + + bytestreamjs@2.0.1: {} + + c8@7.14.0: + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@istanbuljs/schema': 0.1.3 + find-up: 5.0.0 + foreground-child: 2.0.0 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + rimraf: 3.0.2 + test-exclude: 6.0.0 + v8-to-istanbul: 9.3.0 + yargs: 16.2.0 + yargs-parser: 20.2.9 + + cacache@12.0.4: + dependencies: + bluebird: 3.7.2 + chownr: 1.1.4 + figgy-pudding: 3.5.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + infer-owner: 1.0.4 + lru-cache: 5.1.1 + mississippi: 3.0.0 + mkdirp: 0.5.6 + move-concurrently: 1.0.1 + promise-inflight: 1.0.1 + rimraf: 2.7.1 + ssri: 6.0.2 + unique-filename: 1.1.1 + y18n: 4.0.3 + + cacache@15.3.0: + dependencies: + '@npmcli/fs': 1.1.1 + '@npmcli/move-file': 1.1.2 + chownr: 2.0.0 + fs-minipass: 2.1.0 + glob: 7.2.3 + infer-owner: 1.0.4 + lru-cache: 6.0.0 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-flush: 1.0.7 + minipass-pipeline: 1.2.4 + mkdirp: 1.0.4 + p-map: 4.0.0 + promise-inflight: 1.0.1 + rimraf: 3.0.2 + ssri: 8.0.1 + tar: 6.2.1 + unique-filename: 1.1.1 + + cache-base@1.0.1: + dependencies: + collection-visit: 1.0.0 + component-emitter: 1.3.1 + get-value: 2.0.6 + has-value: 1.0.0 + isobject: 3.0.1 + set-value: 2.0.1 + to-object-path: 0.3.0 + union-value: 1.0.1 + unset-value: 1.0.0 + + cacheable-lookup@5.0.4: {} + + cacheable-request@7.0.4: + dependencies: + clone-response: 1.0.3 + get-stream: 5.2.0 + http-cache-semantics: 4.2.0 + keyv: 4.5.4 + lowercase-keys: 2.0.0 + normalize-url: 6.1.0 + responselike: 2.0.1 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + call-me-maybe@1.0.2: {} + + callsites@3.1.0: {} + + camel-case@4.1.2: + dependencies: + pascal-case: 3.1.2 + tslib: 2.8.1 + + camelcase-css@2.0.1: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-api@3.0.0: + dependencies: + browserslist: 4.28.2 + caniuse-lite: 1.0.30001784 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + + caniuse-lite@1.0.30001784: {} + + capture-exit@2.0.0: + dependencies: + rsvp: 4.8.5 + + case-sensitive-paths-webpack-plugin@2.4.0: {} + + ccount@1.1.0: {} + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chainsaw@0.1.0: + dependencies: + traverse: 0.3.9 + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + char-regex@1.0.2: {} + + character-entities-legacy@1.1.4: {} + + character-entities@1.2.4: {} + + character-reference-invalid@1.1.4: {} + + check-error@2.1.3: {} + + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.2.2 + css-what: 6.2.2 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.0.0-rc.12: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + htmlparser2: 8.0.2 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + + chokidar@2.1.8: + dependencies: + anymatch: 2.0.0 + async-each: 1.0.6 + braces: 2.3.2 + glob-parent: 3.1.0 + inherits: 2.0.4 + is-binary-path: 1.0.1 + is-glob: 4.0.3 + normalize-path: 3.0.0 + path-is-absolute: 1.0.1 + readdirp: 2.2.1 + upath: 1.2.0 + optionalDependencies: + fsevents: 1.2.13 + optional: true + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chownr@1.1.4: {} + + chownr@2.0.0: {} + + chownr@3.0.0: {} + + chrome-trace-event@1.0.4: {} + + ci-info@2.0.0: {} + + ci-info@3.9.0: {} + + ci-info@4.4.0: {} + + cipher-base@1.0.7: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + citty@0.1.6: + dependencies: + consola: 3.4.2 + + cjs-module-lexer@1.4.3: {} + + cjs-module-lexer@2.2.0: {} + + class-utils@0.3.6: + dependencies: + arr-union: 3.1.0 + define-property: 0.2.5 + isobject: 3.0.1 + static-extend: 0.1.2 + + clean-css@4.2.4: + dependencies: + source-map: 0.6.1 + + clean-css@5.3.3: + dependencies: + source-map: 0.6.1 + + clean-stack@2.2.0: {} + + cli-boxes@2.2.1: {} + + cli-table3@0.6.5: + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + + cli-width@4.1.0: {} + + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone-deep@4.0.1: + dependencies: + is-plain-object: 2.0.4 + kind-of: 6.0.3 + shallow-clone: 3.0.1 + + clone-response@1.0.3: + dependencies: + mimic-response: 1.0.1 + + clsx@2.1.1: {} + + cluster-key-slot@1.1.2: {} + + cmd-extension@1.0.2: {} + + co@4.6.0: {} + + cockatiel@3.2.1: {} + + code-point-at@1.1.0: {} + + collapse-white-space@1.0.6: {} + + collect-v8-coverage@1.0.3(@types/node@20.17.19): + dependencies: + '@types/node': 20.17.19 + + collect-v8-coverage@1.0.3(@types/node@22.9.3): + dependencies: + '@types/node': 22.9.3 + + collection-visit@1.0.0: + dependencies: + map-visit: 1.0.0 + object-visit: 1.0.1 + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + color-support@1.1.3: {} + + colord@2.9.3: {} + + colorette@2.0.20: {} + + colorjs.io@0.5.2: {} + + colors@1.2.5: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comma-separated-tokens@1.0.8: {} + + commander@12.1.0: {} + + commander@14.0.3: {} + + commander@2.20.3: {} + + commander@4.1.1: {} + + commander@6.2.1: {} + + commander@7.2.0: {} + + commander@8.3.0: {} + + commander@9.5.0: + optional: true + + comment-parser@1.4.1: {} + + commondir@1.0.1: {} + + component-emitter@1.3.1: {} + + compress-commons@4.1.2: + dependencies: + buffer-crc32: 0.2.13 + crc32-stream: 4.0.3 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + + compressible@2.0.18: + dependencies: + mime-db: 1.54.0 + + compression@1.7.5: + dependencies: + bytes: 3.1.2 + compressible: 2.0.18 + debug: 2.6.9 + negotiator: 0.6.4 + on-headers: 1.0.2 + safe-buffer: 5.2.1 + vary: 1.1.2 + + compression@1.8.1: + dependencies: + bytes: 3.1.2 + compressible: 2.0.18 + debug: 2.6.9 + negotiator: 0.6.4 + on-headers: 1.1.0 + safe-buffer: 5.2.1 + vary: 1.1.2 + + compute-scroll-into-view@1.0.20: {} + + comver-to-semver@1.0.0: {} + + concat-map@0.0.1: {} + + concat-stream@1.6.2: + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 2.3.8 + typedarray: 0.0.6 + + conf@10.2.0: + dependencies: + ajv: 8.20.0 + ajv-formats: 2.1.1 + atomically: 1.7.0 + debounce-fn: 4.0.0 + dot-prop: 6.0.1 + env-paths: 2.2.1 + json-schema-typed: 7.0.3 + onetime: 5.1.2 + pkg-up: 3.1.0 + semver: 7.7.4 + + confbox@0.1.8: {} + + configstore@5.0.1: + dependencies: + dot-prop: 5.3.0 + graceful-fs: 4.2.11 + make-dir: 3.1.0 + unique-string: 2.0.0 + write-file-atomic: 3.0.3 + xdg-basedir: 4.0.0 + + connect-history-api-fallback@2.0.0: {} + + consola@3.4.2: {} + + console-browserify@1.2.0: {} + + console-control-strings@1.1.0: {} + + constants-browserify@1.0.0: {} + + constructs@10.0.130: {} + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-disposition@1.0.1: {} + + content-type@1.0.5: {} + + convert-source-map@1.9.0: {} + + convert-source-map@2.0.0: {} + + cookie-signature@1.0.6: {} + + cookie-signature@1.0.7: {} + + cookie-signature@1.2.2: {} + + cookie@0.5.0: {} + + cookie@0.7.1: {} + + cookie@0.7.2: {} + + copy-concurrently@1.0.5: + dependencies: + aproba: 1.2.0 + fs-write-stream-atomic: 1.0.10 + iferr: 0.1.5 + mkdirp: 0.5.6 + rimraf: 2.7.1 + run-queue: 1.0.3 + + copy-descriptor@0.1.1: {} + + copy-to-clipboard@3.3.3: + dependencies: + toggle-selection: 1.0.6 + + core-js-compat@3.49.0: + dependencies: + browserslist: 4.28.2 + + core-js-pure@3.49.0: {} + + core-js@3.49.0: {} + + core-util-is@1.0.3: {} + + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig@6.0.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.3 + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.3 + + cp-file@7.0.0: + dependencies: + graceful-fs: 4.2.11 + make-dir: 3.1.0 + nested-error-stacks: 2.1.1 + p-event: 4.2.0 + + cpy@8.1.2: + dependencies: + arrify: 2.0.1 + cp-file: 7.0.0 + globby: 9.2.0 + has-glob: 1.0.0 + junk: 3.1.0 + nested-error-stacks: 2.1.1 + p-all: 2.1.0 + p-filter: 2.1.0 + p-map: 3.0.0 + + crc-32@1.2.2: {} + + crc32-stream@4.0.3: + dependencies: + crc-32: 1.2.2 + readable-stream: 3.6.2 + + create-ecdh@4.0.4: + dependencies: + bn.js: 4.12.3 + elliptic: 6.6.1 + + create-hash@1.2.0: + dependencies: + cipher-base: 1.0.7 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.3 + sha.js: 2.4.12 + + create-hmac@1.1.7: + dependencies: + cipher-base: 1.0.7 + create-hash: 1.2.0 + inherits: 2.0.4 + ripemd160: 2.0.3 + safe-buffer: 5.2.1 + sha.js: 2.4.12 + + create-jest@29.7.0(@types/node@20.17.19)(babel-plugin-macros@3.1.0): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@20.17.19)(babel-plugin-macros@3.1.0) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + create-storybook@9.1.20: + dependencies: + semver: 7.7.4 + + cross-spawn@6.0.6: + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.2 + shebang-command: 1.2.0 + which: 1.3.1 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypto-browserify@3.12.1: + dependencies: + browserify-cipher: 1.0.1 + browserify-sign: 4.2.5 + create-ecdh: 4.0.4 + create-hash: 1.2.0 + create-hmac: 1.1.7 + diffie-hellman: 5.0.3 + hash-base: 3.0.5 + inherits: 2.0.4 + pbkdf2: 3.1.5 + public-encrypt: 4.0.3 + randombytes: 2.1.0 + randomfill: 1.0.4 + + crypto-random-string@2.0.0: {} + + css-declaration-sorter@6.4.1(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + + css-loader@3.6.0(webpack@4.47.0): + dependencies: + camelcase: 5.3.1 + cssesc: 3.0.0 + icss-utils: 4.1.1 + loader-utils: 1.4.2 + normalize-path: 3.0.0 + postcss: 7.0.39 + postcss-modules-extract-imports: 2.0.0 + postcss-modules-local-by-default: 3.0.3 + postcss-modules-scope: 2.2.0 + postcss-modules-values: 3.0.0 + postcss-value-parser: 4.2.0 + schema-utils: 2.7.1 + semver: 6.3.1 + webpack: 4.47.0 + + css-loader@5.2.7(webpack@4.47.0): + dependencies: + icss-utils: 5.1.0(postcss@8.4.49) + loader-utils: 2.0.4 + postcss: 8.4.49 + postcss-modules-extract-imports: 3.1.0(postcss@8.4.49) + postcss-modules-local-by-default: 4.2.0(postcss@8.4.49) + postcss-modules-scope: 3.2.1(postcss@8.4.49) + postcss-modules-values: 4.0.0(postcss@8.4.49) + postcss-value-parser: 4.2.0 + schema-utils: 3.3.0 + semver: 7.7.4 + webpack: 4.47.0 + + css-loader@5.2.7(webpack@5.105.4): + dependencies: + icss-utils: 5.1.0(postcss@8.4.49) + loader-utils: 2.0.4 + postcss: 8.4.49 + postcss-modules-extract-imports: 3.1.0(postcss@8.4.49) + postcss-modules-local-by-default: 4.2.0(postcss@8.4.49) + postcss-modules-scope: 3.2.1(postcss@8.4.49) + postcss-modules-values: 4.0.0(postcss@8.4.49) + postcss-value-parser: 4.2.0 + schema-utils: 3.3.0 + semver: 7.7.4 + webpack: 5.105.4 + + css-loader@6.11.0(@rspack/core@1.6.8(@swc/helpers@0.5.21))(webpack@5.105.4): + dependencies: + icss-utils: 5.1.0(postcss@8.5.12) + postcss: 8.5.12 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.12) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.12) + postcss-modules-scope: 3.2.1(postcss@8.5.12) + postcss-modules-values: 4.0.0(postcss@8.5.12) + postcss-value-parser: 4.2.0 + semver: 7.7.4 + optionalDependencies: + '@rspack/core': 1.6.8(@swc/helpers@0.5.21) + webpack: 5.105.4 + + css-loader@6.6.0(webpack@5.105.4): + dependencies: + icss-utils: 5.1.0(postcss@8.4.49) + postcss: 8.4.49 + postcss-modules-extract-imports: 3.1.0(postcss@8.4.49) + postcss-modules-local-by-default: 4.2.0(postcss@8.4.49) + postcss-modules-scope: 3.2.1(postcss@8.4.49) + postcss-modules-values: 4.0.0(postcss@8.4.49) + postcss-value-parser: 4.2.0 + semver: 7.7.4 + webpack: 5.105.4 + + css-minimizer-webpack-plugin@3.4.1(webpack@5.105.4): + dependencies: + cssnano: 5.1.15(postcss@8.5.12) + jest-worker: 27.5.1 + postcss: 8.5.12 + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + source-map: 0.6.1 + webpack: 5.105.4 + + css-select@4.3.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-tree@1.1.3: + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + + css-what@6.2.2: {} + + css.escape@1.5.1: {} + + cssesc@3.0.0: {} + + cssnano-preset-default@5.2.14(postcss@8.5.12): + dependencies: + css-declaration-sorter: 6.4.1(postcss@8.5.12) + cssnano-utils: 3.1.0(postcss@8.5.12) + postcss: 8.5.12 + postcss-calc: 8.2.4(postcss@8.5.12) + postcss-colormin: 5.3.1(postcss@8.5.12) + postcss-convert-values: 5.1.3(postcss@8.5.12) + postcss-discard-comments: 5.1.2(postcss@8.5.12) + postcss-discard-duplicates: 5.1.0(postcss@8.5.12) + postcss-discard-empty: 5.1.1(postcss@8.5.12) + postcss-discard-overridden: 5.1.0(postcss@8.5.12) + postcss-merge-longhand: 5.1.7(postcss@8.5.12) + postcss-merge-rules: 5.1.4(postcss@8.5.12) + postcss-minify-font-values: 5.1.0(postcss@8.5.12) + postcss-minify-gradients: 5.1.1(postcss@8.5.12) + postcss-minify-params: 5.1.4(postcss@8.5.12) + postcss-minify-selectors: 5.2.1(postcss@8.5.12) + postcss-normalize-charset: 5.1.0(postcss@8.5.12) + postcss-normalize-display-values: 5.1.0(postcss@8.5.12) + postcss-normalize-positions: 5.1.1(postcss@8.5.12) + postcss-normalize-repeat-style: 5.1.1(postcss@8.5.12) + postcss-normalize-string: 5.1.0(postcss@8.5.12) + postcss-normalize-timing-functions: 5.1.0(postcss@8.5.12) + postcss-normalize-unicode: 5.1.1(postcss@8.5.12) + postcss-normalize-url: 5.1.0(postcss@8.5.12) + postcss-normalize-whitespace: 5.1.1(postcss@8.5.12) + postcss-ordered-values: 5.1.3(postcss@8.5.12) + postcss-reduce-initial: 5.1.2(postcss@8.5.12) + postcss-reduce-transforms: 5.1.0(postcss@8.5.12) + postcss-svgo: 5.1.0(postcss@8.5.12) + postcss-unique-selectors: 5.1.1(postcss@8.5.12) + + cssnano-utils@3.1.0(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + + cssnano@5.1.15(postcss@8.5.12): + dependencies: + cssnano-preset-default: 5.2.14(postcss@8.5.12) + lilconfig: 2.1.0 + postcss: 8.5.12 + yaml: 1.10.3 + + csso@4.2.0: + dependencies: + css-tree: 1.1.3 + + cssstyle@4.6.0: + dependencies: + '@asamuzakjp/css-color': 3.2.0 + rrweb-cssom: 0.8.0 + + csstype@2.6.21: {} + + csstype@3.2.3: {} + + cyclist@1.0.2: {} + + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + date-format@4.0.14: {} + + debounce-fn@4.0.0: + dependencies: + mimic-fn: 3.1.0 + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3(supports-color@8.1.1): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 + + debuglog@1.0.1: {} + + decamelize@1.2.0: {} + + decamelize@4.0.0: {} + + decimal.js@10.6.0: {} + + decode-uri-component@0.2.2: {} + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + dedent@0.7.0: {} + + dedent@1.7.2: {} + + dedent@1.7.2(babel-plugin-macros@3.1.0): + optionalDependencies: + babel-plugin-macros: 3.1.0 + + deep-eql@5.0.2: {} + + deep-extend@0.6.0: {} + + deep-is@0.1.4: {} + + deep-object-diff@1.1.9: {} + + deepmerge@4.3.1: {} + + default-browser-id@5.0.1: {} + + default-browser@5.5.0: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + + default-gateway@6.0.3: + dependencies: + execa: 5.1.1 + + defer-to-connect@2.0.1: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-lazy-prop@2.0.0: {} + + define-lazy-prop@3.0.0: {} + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + define-property@0.2.5: + dependencies: + is-descriptor: 0.1.7 + + define-property@1.0.0: + dependencies: + is-descriptor: 1.0.3 + + define-property@2.0.2: + dependencies: + is-descriptor: 1.0.3 + isobject: 3.0.1 + + defu@6.1.6: {} + + delayed-stream@1.0.0: {} + + delegates@1.0.0: {} + + dendriform-immer-patch-optimiser@2.1.3(immer@9.0.21): + dependencies: + immer: 9.0.21 + + depd@1.1.2: {} + + depd@2.0.0: {} + + dependency-path@9.2.8: + dependencies: + '@pnpm/crypto.base32-hash': 1.0.1 + '@pnpm/types': 8.9.0 + encode-registry: 3.0.1 + semver: 7.7.4 + + des.js@1.1.0: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + destroy@1.0.4: {} + + destroy@1.2.0: {} + + detab@2.0.4: + dependencies: + repeat-string: 1.6.1 + + detect-indent@6.1.0: {} + + detect-libc@2.1.2: + optional: true + + detect-newline@3.1.0: {} + + detect-node@2.1.0: {} + + detect-port-alt@1.1.6: + dependencies: + address: 1.2.2 + debug: 2.6.9 + + detect-port@1.6.1: + dependencies: + address: 1.2.2 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + dezalgo@1.0.4: + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + + diff-sequences@27.5.1: {} + + diff-sequences@29.6.3: {} + + diff@4.0.4: {} + + diff@5.2.2: {} + + diff@8.0.4: {} + + diffie-hellman@5.0.3: + dependencies: + bn.js: 4.12.3 + miller-rabin: 4.0.1 + randombytes: 2.1.0 + + dir-glob@2.2.2: + dependencies: + path-type: 3.0.0 + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dns-packet@5.6.1: + dependencies: + '@leichtgewicht/ip-codec': 2.0.5 + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dom-accessibility-api@0.4.7: {} + + dom-accessibility-api@0.6.3: {} + + dom-converter@0.2.0: + dependencies: + utila: 0.4.0 + + dom-serializer@1.4.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + dom-walk@0.1.2: {} + + domain-browser@1.2.0: {} + + domelementtype@2.3.0: {} + + domhandler@4.3.1: + dependencies: + domelementtype: 2.3.0 + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@2.8.0: + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + + dot-prop@6.0.1: + dependencies: + is-obj: 2.0.0 + + dotenv-expand@5.1.0: {} + + dotenv@10.0.0: {} + + dotenv@16.4.7: {} + + dotenv@8.6.0: {} + + downshift@6.1.12(react@17.0.2): + dependencies: + '@babel/runtime': 7.29.2 + compute-scroll-into-view: 1.0.20 + prop-types: 15.8.1 + react: 17.0.2 + react-is: 17.0.2 + tslib: 2.8.1 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + duplexer2@0.1.4: + dependencies: + readable-stream: 2.3.8 + + duplexer@0.1.2: {} + + duplexify@3.7.1: + dependencies: + end-of-stream: 1.4.5 + inherits: 2.0.4 + readable-stream: 2.3.8 + stream-shift: 1.0.3 + + eastasianwidth@0.2.0: {} + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.331: {} + + element-resize-detector@1.2.4: + dependencies: + batch-processor: 1.0.0 + + elliptic@6.6.1: + dependencies: + bn.js: 4.12.3 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + embla-carousel-autoplay@8.6.0(embla-carousel@8.6.0): + dependencies: + embla-carousel: 8.6.0 + + embla-carousel-fade@8.6.0(embla-carousel@8.6.0): + dependencies: + embla-carousel: 8.6.0 + + embla-carousel@8.6.0: {} + + emittery@0.13.1: {} + + emoji-regex@7.0.3: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + emojis-list@3.0.0: {} + + emotion-theming@10.3.0(@emotion/core@10.3.1(@types/react@17.0.74)(react@17.0.2))(@types/react@17.0.74)(react@17.0.2): + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/core': 10.3.1(@types/react@17.0.74)(react@17.0.2) + '@emotion/weak-memoize': 0.2.5 + '@types/react': 17.0.74 + hoist-non-react-statics: 3.3.2 + react: 17.0.2 + + encode-registry@3.0.1: + dependencies: + mem: 8.1.1 + + encodeurl@1.0.2: {} + + encodeurl@2.0.0: {} + + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + endent@2.1.0: + dependencies: + dedent: 0.7.0 + fast-json-parse: 1.0.3 + objectorarray: 1.0.5 + + enhanced-resolve@4.5.0: + dependencies: + graceful-fs: 4.2.11 + memory-fs: 0.5.0 + tapable: 1.1.3 + + enhanced-resolve@5.20.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + + entities@2.2.0: {} + + entities@4.5.0: {} + + entities@6.0.1: {} + + env-paths@2.2.1: {} + + envinfo@7.21.0: {} + + err-code@2.0.3: {} + + errno@0.1.8: + dependencies: + prr: 1.0.1 + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + + es-abstract@1.24.1: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 + + es-array-method-boxes-properly@1.0.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-get-iterator@1.1.3: + dependencies: + call-bind: 1.0.8 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + is-arguments: 1.2.0 + is-map: 2.0.3 + is-set: 2.0.3 + is-string: 1.1.1 + isarray: 2.0.5 + stop-iteration-iterator: 1.1.0 + + es-iterator-helpers@1.3.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + math-intrinsics: 1.1.0 + safe-array-concat: 1.1.3 + + es-module-lexer@1.7.0: {} + + es-module-lexer@2.0.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + es-toolkit@1.45.1: {} + + es5-shim@4.6.7: {} + + es6-shim@0.35.8: {} + + esbuild-android-64@0.14.54: + optional: true + + esbuild-android-arm64@0.14.54: + optional: true + + esbuild-darwin-64@0.14.54: + optional: true + + esbuild-darwin-arm64@0.14.54: + optional: true + + esbuild-freebsd-64@0.14.54: + optional: true + + esbuild-freebsd-arm64@0.14.54: + optional: true + + esbuild-linux-32@0.14.54: + optional: true + + esbuild-linux-64@0.14.54: + optional: true + + esbuild-linux-arm64@0.14.54: + optional: true + + esbuild-linux-arm@0.14.54: + optional: true + + esbuild-linux-mips64le@0.14.54: + optional: true + + esbuild-linux-ppc64le@0.14.54: + optional: true + + esbuild-linux-riscv64@0.14.54: + optional: true + + esbuild-linux-s390x@0.14.54: + optional: true + + esbuild-netbsd-64@0.14.54: + optional: true + + esbuild-openbsd-64@0.14.54: + optional: true + + esbuild-register@3.6.0(esbuild@0.25.12): + dependencies: + debug: 4.4.3(supports-color@8.1.1) + esbuild: 0.25.12 + transitivePeerDependencies: + - supports-color + + esbuild-runner@2.2.2(esbuild@0.14.54): + dependencies: + esbuild: 0.14.54 + source-map-support: 0.5.21 + tslib: 2.4.0 + + esbuild-sunos-64@0.14.54: + optional: true + + esbuild-windows-32@0.14.54: + optional: true + + esbuild-windows-64@0.14.54: + optional: true + + esbuild-windows-arm64@0.14.54: + optional: true + + esbuild@0.14.54: + optionalDependencies: + '@esbuild/linux-loong64': 0.14.54 + esbuild-android-64: 0.14.54 + esbuild-android-arm64: 0.14.54 + esbuild-darwin-64: 0.14.54 + esbuild-darwin-arm64: 0.14.54 + esbuild-freebsd-64: 0.14.54 + esbuild-freebsd-arm64: 0.14.54 + esbuild-linux-32: 0.14.54 + esbuild-linux-64: 0.14.54 + esbuild-linux-arm: 0.14.54 + esbuild-linux-arm64: 0.14.54 + esbuild-linux-mips64le: 0.14.54 + esbuild-linux-ppc64le: 0.14.54 + esbuild-linux-riscv64: 0.14.54 + esbuild-linux-s390x: 0.14.54 + esbuild-netbsd-64: 0.14.54 + esbuild-openbsd-64: 0.14.54 + esbuild-sunos-64: 0.14.54 + esbuild-windows-32: 0.14.54 + esbuild-windows-64: 0.14.54 + esbuild-windows-arm64: 0.14.54 + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + esbuild@0.28.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.0 + '@esbuild/android-arm': 0.28.0 + '@esbuild/android-arm64': 0.28.0 + '@esbuild/android-x64': 0.28.0 + '@esbuild/darwin-arm64': 0.28.0 + '@esbuild/darwin-x64': 0.28.0 + '@esbuild/freebsd-arm64': 0.28.0 + '@esbuild/freebsd-x64': 0.28.0 + '@esbuild/linux-arm': 0.28.0 + '@esbuild/linux-arm64': 0.28.0 + '@esbuild/linux-ia32': 0.28.0 + '@esbuild/linux-loong64': 0.28.0 + '@esbuild/linux-mips64el': 0.28.0 + '@esbuild/linux-ppc64': 0.28.0 + '@esbuild/linux-riscv64': 0.28.0 + '@esbuild/linux-s390x': 0.28.0 + '@esbuild/linux-x64': 0.28.0 + '@esbuild/netbsd-arm64': 0.28.0 + '@esbuild/netbsd-x64': 0.28.0 + '@esbuild/openbsd-arm64': 0.28.0 + '@esbuild/openbsd-x64': 0.28.0 + '@esbuild/openharmony-arm64': 0.28.0 + '@esbuild/sunos-x64': 0.28.0 + '@esbuild/win32-arm64': 0.28.0 + '@esbuild/win32-ia32': 0.28.0 + '@esbuild/win32-x64': 0.28.0 + + escalade@3.2.0: {} + + escape-goat@2.1.1: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.11 + + eslint-module-utils@2.12.1(eslint@9.37.0): + dependencies: + debug: 3.2.7 + optionalDependencies: + eslint: 9.37.0 + + eslint-plugin-header@3.1.1(eslint@9.37.0): + dependencies: + eslint: 9.37.0 + + eslint-plugin-headers@1.2.1(eslint@9.37.0): + dependencies: + eslint: 9.37.0 + + eslint-plugin-import@2.32.0(eslint@9.37.0): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.37.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(eslint@9.37.0) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + + eslint-plugin-jsdoc@50.6.11(eslint@9.37.0): + dependencies: + '@es-joy/jsdoccomment': 0.49.0 + are-docs-informative: 0.0.2 + comment-parser: 1.4.1 + debug: 4.4.3(supports-color@8.1.1) + escape-string-regexp: 4.0.0 + eslint: 9.37.0 + espree: 10.4.0 + esquery: 1.7.0 + parse-imports-exports: 0.2.4 + semver: 7.7.4 + spdx-expression-parse: 4.0.0 + transitivePeerDependencies: + - supports-color + + eslint-plugin-promise@6.1.1(eslint@7.11.0): + dependencies: + eslint: 7.11.0 + + eslint-plugin-promise@6.1.1(eslint@7.30.0): + dependencies: + eslint: 7.30.0 + + eslint-plugin-promise@6.1.1(eslint@7.7.0): + dependencies: + eslint: 7.7.0 + + eslint-plugin-promise@6.1.1(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-plugin-promise@7.2.1(eslint@8.57.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + eslint: 8.57.1 + + eslint-plugin-promise@7.2.1(eslint@9.37.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.37.0) + eslint: 9.37.0 + + eslint-plugin-react-hooks@5.2.0(eslint@9.37.0): + dependencies: + eslint: 9.37.0 + + eslint-plugin-react@7.33.2(eslint@7.11.0): + dependencies: + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.1 + eslint: 7.11.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.hasown: 1.1.4 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + + eslint-plugin-react@7.33.2(eslint@7.30.0): + dependencies: + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.1 + eslint: 7.30.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.hasown: 1.1.4 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + + eslint-plugin-react@7.33.2(eslint@7.7.0): + dependencies: + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.1 + eslint: 7.7.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.hasown: 1.1.4 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + + eslint-plugin-react@7.33.2(eslint@8.57.1): + dependencies: + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.1 + eslint: 8.57.1 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.hasown: 1.1.4 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + + eslint-plugin-react@7.37.5(eslint@8.57.1): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.1 + eslint: 8.57.1 + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-plugin-react@7.37.5(eslint@9.37.0): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.1 + eslint: 9.37.0 + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-plugin-tsdoc@0.3.0: + dependencies: + '@microsoft/tsdoc': 0.15.0 + '@microsoft/tsdoc-config': 0.17.0 + + eslint-plugin-tsdoc@0.5.2(eslint@8.57.1)(typescript@4.9.5): + dependencies: + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.1 + '@typescript-eslint/utils': 8.56.1(eslint@8.57.1)(typescript@4.9.5) + transitivePeerDependencies: + - eslint + - supports-color + - typescript + + eslint-plugin-tsdoc@0.5.2(eslint@9.37.0)(typescript@5.8.2): + dependencies: + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.1 + '@typescript-eslint/utils': 8.56.1(eslint@9.37.0)(typescript@5.8.2) + transitivePeerDependencies: + - eslint + - supports-color + - typescript + + eslint-scope@4.0.3: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-utils@2.1.0: + dependencies: + eslint-visitor-keys: 1.3.0 + + eslint-utils@3.0.0(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 2.1.0 + + eslint-visitor-keys@1.3.0: {} + + eslint-visitor-keys@2.1.0: {} + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@7.11.0: + dependencies: + '@babel/code-frame': 7.29.0 + '@eslint/eslintrc': 0.1.3 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + doctrine: 3.0.0 + enquirer: 2.4.1 + eslint-scope: 5.1.1 + eslint-utils: 2.1.0 + eslint-visitor-keys: 2.1.0 + espree: 7.3.1 + esquery: 1.7.0 + esutils: 2.0.3 + file-entry-cache: 5.0.1 + functional-red-black-tree: 1.0.1 + glob-parent: 5.1.2 + globals: 12.4.0 + ignore: 4.0.6 + import-fresh: 3.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + js-yaml: 3.14.2 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash: 4.18.1 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + progress: 2.0.3 + regexpp: 3.2.0 + semver: 7.7.4 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + table: 5.4.6 + text-table: 0.2.0 + v8-compile-cache: 2.4.0 + transitivePeerDependencies: + - supports-color + + eslint@7.30.0: + dependencies: + '@babel/code-frame': 7.12.11 + '@eslint/eslintrc': 0.4.3 + '@humanwhocodes/config-array': 0.5.0 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + doctrine: 3.0.0 + enquirer: 2.4.1 + escape-string-regexp: 4.0.0 + eslint-scope: 5.1.1 + eslint-utils: 2.1.0 + eslint-visitor-keys: 2.1.0 + espree: 7.3.1 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + functional-red-black-tree: 1.0.1 + glob-parent: 5.1.2 + globals: 13.24.0 + ignore: 4.0.6 + import-fresh: 3.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + js-yaml: 3.14.2 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + progress: 2.0.3 + regexpp: 3.2.0 + semver: 7.7.4 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + table: 6.9.0 + text-table: 0.2.0 + v8-compile-cache: 2.4.0 + transitivePeerDependencies: + - supports-color + + eslint@7.7.0: + dependencies: + '@babel/code-frame': 7.29.0 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + doctrine: 3.0.0 + enquirer: 2.4.1 + eslint-scope: 5.1.1 + eslint-utils: 2.1.0 + eslint-visitor-keys: 1.3.0 + espree: 7.3.1 + esquery: 1.7.0 + esutils: 2.0.3 + file-entry-cache: 5.0.1 + functional-red-black-tree: 1.0.1 + glob-parent: 5.1.2 + globals: 12.4.0 + ignore: 4.0.6 + import-fresh: 3.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + js-yaml: 3.14.2 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash: 4.18.1 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + progress: 2.0.3 + regexpp: 3.2.0 + semver: 7.7.4 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + table: 5.4.6 + text-table: 0.2.0 + v8-compile-cache: 2.4.0 + transitivePeerDependencies: + - supports-color + + eslint@8.23.1: + dependencies: + '@eslint/eslintrc': 1.4.1 + '@humanwhocodes/config-array': 0.10.7 + '@humanwhocodes/gitignore-to-minimatch': 1.0.2 + '@humanwhocodes/module-importer': 1.0.1 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-utils: 3.0.0(eslint@8.57.1) + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + globby: 11.1.0 + grapheme-splitter: 1.0.4 + ignore: 5.3.2 + import-fresh: 3.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + js-sdsl: 4.4.2 + js-yaml: 4.1.1 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + regexpp: 3.2.0 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + eslint@8.57.1: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.37.0) + '@eslint-community/regexpp': 4.12.2 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.3.0 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.1 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + eslint@8.6.0: + dependencies: + '@eslint/eslintrc': 1.4.1 + '@humanwhocodes/config-array': 0.9.5 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + doctrine: 3.0.0 + enquirer: 2.4.1 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-utils: 3.0.0(eslint@8.57.1) + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + functional-red-black-tree: 1.0.1 + glob-parent: 6.0.2 + globals: 13.24.0 + ignore: 4.0.6 + import-fresh: 3.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + js-yaml: 4.1.1 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + progress: 2.0.3 + regexpp: 3.2.0 + semver: 7.7.4 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + v8-compile-cache: 2.4.0 + transitivePeerDependencies: + - supports-color + + eslint@9.25.1: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.37.0) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.20.1 + '@eslint/config-helpers': 0.2.3 + '@eslint/core': 0.13.0 + '@eslint/eslintrc': 3.3.5(supports-color@8.1.1) + '@eslint/js': 9.25.1 + '@eslint/plugin-kit': 0.2.8 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + eslint@9.37.0: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.37.0) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2(supports-color@8.1.1) + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.16.0 + '@eslint/eslintrc': 3.3.5(supports-color@8.1.1) + '@eslint/js': 9.37.0 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + eslint@9.37.0(supports-color@8.1.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.37.0(supports-color@8.1.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2(supports-color@8.1.1) + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.16.0 + '@eslint/eslintrc': 3.3.5(supports-color@8.1.1) + '@eslint/js': 9.37.0 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + espree@7.3.1: + dependencies: + acorn: 7.4.1 + acorn-jsx: 5.3.2(acorn@7.4.1) + eslint-visitor-keys: 1.3.0 + + espree@9.6.1: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 3.4.3 + + esprima@4.0.1: {} + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + estree-to-babel@3.2.1: + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + c8: 7.14.0 + transitivePeerDependencies: + - supports-color + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + esutils@2.0.3: {} + + etag@1.8.1: {} + + eventemitter3@4.0.7: {} + + events@1.1.1: {} + + events@3.3.0: {} + + eventsource-parser@3.0.6: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.6 + + evp_bytestokey@1.0.3: + dependencies: + md5.js: 1.3.5 + safe-buffer: 5.2.1 + + exec-sh@0.3.6: {} + + execa@1.0.0: + dependencies: + cross-spawn: 6.0.6 + get-stream: 4.1.0 + is-stream: 1.1.0 + npm-run-path: 2.0.2 + p-finally: 1.0.0 + signal-exit: 3.0.7 + strip-eof: 1.0.0 + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit-x@0.2.2: {} + + exit@0.1.2: {} + + expand-brackets@2.1.4: + dependencies: + debug: 2.6.9 + define-property: 0.2.5 + extend-shallow: 2.0.1 + posix-character-classes: 0.1.1 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + + expand-template@2.0.3: + optional: true + + expect@29.7.0: + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + + expect@30.3.0: + dependencies: + '@jest/expect-utils': 30.3.0 + '@jest/get-type': 30.1.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 + jest-util: 30.3.0 + + express-rate-limit@7.5.1(express@5.2.1): + dependencies: + express: 5.2.1 + + express@4.21.1: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.3 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.1 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.10 + proxy-addr: 2.0.7 + qs: 6.13.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.0 + serve-static: 1.16.2 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + + express@4.22.1: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.4 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.0.7 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.2 + fresh: 0.5.2 + http-errors: 2.0.1 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.13 + proxy-addr: 2.0.7 + qs: 6.14.2 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.2 + serve-static: 1.16.3 + setprototypeof: 1.2.0 + statuses: 2.0.2 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.2 + content-disposition: 1.0.1 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3(supports-color@8.1.1) + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.15.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + + extend-shallow@3.0.2: + dependencies: + assign-symbols: 1.0.0 + is-extendable: 1.0.1 + + extend@3.0.2: {} + + extglob@2.0.4: + dependencies: + array-unique: 0.3.2 + define-property: 1.0.0 + expand-brackets: 2.1.4 + extend-shallow: 2.0.1 + fragment-cache: 0.2.1 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + + extract-zip@1.7.0: + dependencies: + concat-stream: 1.6.2 + debug: 2.6.9 + mkdirp: 0.5.6 + yauzl: 2.10.0 + + fast-decode-uri-component@1.0.1: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@2.2.7: + dependencies: + '@mrmlnc/readdir-enhanced': 2.2.1 + '@nodelib/fs.stat': 1.1.3 + glob-parent: 3.1.0 + is-glob: 4.0.3 + merge2: 1.4.1 + micromatch: 3.1.10 + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-parse@1.0.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-json-stringify@2.7.13: + dependencies: + ajv: 6.14.0 + deepmerge: 4.3.1 + rfdc: 1.4.1 + string-similarity: 4.0.4 + + fast-levenshtein@2.0.6: {} + + fast-redact@3.5.0: {} + + fast-safe-stringify@2.1.1: {} + + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-uri@3.1.0: {} + + fast-wrap-ansi@0.2.0: + dependencies: + fast-string-width: 3.0.2 + + fast-xml-parser@5.3.5: + dependencies: + strnum: 2.2.2 + + fastify-error@0.3.1: {} + + fastify-warning@0.2.0: {} + + fastify@3.16.2: + dependencies: + '@fastify/ajv-compiler': 1.1.0 + '@fastify/proxy-addr': 3.0.0 + abstract-logging: 2.0.1 + avvio: 7.2.5 + fast-json-stringify: 2.7.13 + fastify-error: 0.3.1 + fastify-warning: 0.2.0 + find-my-way: 4.5.1 + flatstr: 1.0.12 + light-my-request: 4.12.0 + pino: 6.14.0 + readable-stream: 3.6.2 + rfdc: 1.4.1 + secure-json-parse: 2.7.0 + semver: 7.7.4 + tiny-lru: 7.0.6 + transitivePeerDependencies: + - supports-color + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fault@1.0.4: + dependencies: + format: 0.2.2 + + faye-websocket@0.11.4: + dependencies: + websocket-driver: 0.7.4 + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + figgy-pudding@3.5.2: {} + + file-entry-cache@5.0.1: + dependencies: + flat-cache: 2.0.1 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + file-loader@6.0.0(webpack@4.47.0): + dependencies: + loader-utils: 2.0.4 + schema-utils: 2.7.1 + webpack: 4.47.0 + + file-loader@6.2.0(webpack@4.47.0): + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 4.47.0 + + file-system-cache@1.1.0: + dependencies: + fs-extra: 10.1.0 + ramda: 0.28.0 + + file-uri-to-path@1.0.0: + optional: true + + fill-range@4.0.0: + dependencies: + extend-shallow: 2.0.1 + is-number: 3.0.0 + repeat-string: 1.6.1 + to-regex-range: 2.1.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@1.3.1: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + + finalhandler@1.3.2: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + unpipe: 1.0.0 + + finalhandler@2.1.1: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + find-cache-dir@2.1.0: + dependencies: + commondir: 1.0.1 + make-dir: 2.1.0 + pkg-dir: 3.0.0 + + find-cache-dir@3.3.2: + dependencies: + commondir: 1.0.1 + make-dir: 3.1.0 + pkg-dir: 4.2.0 + + find-my-way@4.5.1: + dependencies: + fast-decode-uri-component: 1.0.1 + fast-deep-equal: 3.1.3 + safe-regex2: 2.0.0 + semver-store: 0.3.0 + + find-root@1.1.0: {} + + find-up@3.0.0: + dependencies: + locate-path: 3.0.0 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-up@7.0.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + unicorn-magic: 0.1.0 + + flat-cache@2.0.1: + dependencies: + flatted: 2.0.2 + rimraf: 2.6.3 + write: 1.0.3 + + flat-cache@3.2.0: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + rimraf: 3.0.2 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flat@5.0.2: {} + + flatstr@1.0.12: {} + + flatted@2.0.2: {} + + flatted@3.4.2: {} + + flow-parser@0.307.1: {} + + flush-write-stream@1.1.1: + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.8 + + follow-redirects@1.15.11: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + for-in@1.0.2: {} + + foreground-child@2.0.0: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 3.0.7 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fork-ts-checker-webpack-plugin@4.1.6: + dependencies: + '@babel/code-frame': 7.29.0 + chalk: 2.4.2 + micromatch: 3.1.10 + minimatch: 3.1.5 + semver: 5.7.2 + tapable: 1.1.3 + worker-rpc: 0.1.1 + + fork-ts-checker-webpack-plugin@6.5.3(eslint@9.37.0)(typescript@5.8.2)(webpack@4.47.0): + dependencies: + '@babel/code-frame': 7.29.0 + '@types/json-schema': 7.0.15 + chalk: 4.1.2 + chokidar: 3.6.0 + cosmiconfig: 6.0.0 + deepmerge: 4.3.1 + fs-extra: 9.1.0 + glob: 7.2.3 + memfs: 3.5.3 + minimatch: 3.1.5 + schema-utils: 2.7.0 + semver: 7.7.4 + tapable: 1.1.3 + typescript: 5.8.2 + webpack: 4.47.0 + optionalDependencies: + eslint: 9.37.0 + + fork-ts-checker-webpack-plugin@8.0.0(typescript@5.8.2)(webpack@5.105.4): + dependencies: + '@babel/code-frame': 7.29.0 + chalk: 4.1.2 + chokidar: 3.6.0 + cosmiconfig: 7.1.0 + deepmerge: 4.3.1 + fs-extra: 10.1.0 + memfs: 3.5.3 + minimatch: 3.1.5 + node-abort-controller: 3.1.1 + schema-utils: 3.3.0 + semver: 7.7.4 + tapable: 2.3.0 + typescript: 5.8.2 + webpack: 5.105.4 + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + format@0.2.2: {} + + forwarded@0.2.0: {} + + fraction.js@5.3.4: {} + + fragment-cache@0.2.1: + dependencies: + map-cache: 0.2.2 + + fresh@0.5.2: {} + + fresh@2.0.0: {} + + from2@2.3.0: + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.8 + + fs-constants@1.0.0: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-extra@11.3.4: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@9.1.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs-monkey@1.0.3: {} + + fs-monkey@1.1.0: {} + + fs-write-stream-atomic@1.0.10: + dependencies: + graceful-fs: 4.2.11 + iferr: 0.1.5 + imurmurhash: 0.1.4 + readable-stream: 2.3.8 + + fs.realpath@1.0.0: {} + + fsevents@1.2.13: + dependencies: + bindings: 1.5.0 + nan: 2.26.2 + optional: true + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + fstream@1.0.12: + dependencies: + graceful-fs: 4.2.11 + inherits: 2.0.4 + mkdirp: 0.5.6 + rimraf: 2.7.1 + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functional-red-black-tree@1.0.1: {} + + functions-have-names@1.2.3: {} + + fuse.js@3.6.1: {} + + gauge@2.7.4: + dependencies: + aproba: 1.2.0 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 1.0.2 + strip-ansi: 3.0.1 + wide-align: 1.1.5 + + gauge@3.0.2: + dependencies: + aproba: 2.1.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + + generator-function@2.0.1: {} + + generic-names@4.0.0: + dependencies: + loader-utils: 3.3.1 + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-npm-tarball-url@2.1.0: {} + + get-package-type@0.1.0: {} + + get-port@5.1.1: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@4.1.0: + dependencies: + pump: 3.0.4 + + get-stream@5.2.0: + dependencies: + pump: 3.0.4 + + get-stream@6.0.1: {} + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-value@2.0.6: {} + + giget@1.2.5: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + defu: 6.1.6 + node-fetch-native: 1.6.7 + nypm: 0.5.4 + pathe: 2.0.3 + tar: 6.2.1 + + git-repo-info@2.1.1: {} + + github-from-package@0.0.0: + optional: true + + github-slugger@1.5.0: {} + + glob-parent@3.1.0: + dependencies: + is-glob: 3.1.0 + path-dirname: 1.0.2 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob-promise@3.4.0(glob@7.2.3): + dependencies: + '@types/glob': 7.1.1 + glob: 7.2.3 + + glob-to-regex.js@1.2.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + glob-to-regexp@0.3.0: {} + + glob-to-regexp@0.4.1: {} + + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@11.1.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.2.3 + minimatch: 10.2.3 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.2 + + glob@7.0.6: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + + glob@8.1.0: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.9 + once: 1.4.0 + + global-dirs@3.0.1: + dependencies: + ini: 2.0.0 + + global@4.4.0: + dependencies: + min-document: 2.19.2 + process: 0.11.10 + + globals@12.4.0: + dependencies: + type-fest: 0.8.1 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globals@14.0.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globby@14.1.0: + dependencies: + '@sindresorhus/merge-streams': 2.3.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + path-type: 6.0.0 + slash: 5.1.0 + unicorn-magic: 0.3.0 + + globby@9.2.0: + dependencies: + '@types/glob': 7.1.1 + array-union: 1.0.2 + dir-glob: 2.2.2 + fast-glob: 2.2.7 + glob: 7.2.3 + ignore: 4.0.6 + pify: 4.0.1 + slash: 2.0.0 + + gopd@1.2.0: {} + + got@11.8.6: + dependencies: + '@sindresorhus/is': 4.6.0 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/responselike': 1.0.3 + cacheable-lookup: 5.0.4 + cacheable-request: 7.0.4 + decompress-response: 6.0.0 + http2-wrapper: 1.0.3 + lowercase-keys: 2.0.0 + p-cancelable: 2.1.1 + responselike: 2.0.1 + + graceful-fs@4.2.11: {} + + graceful-fs@4.2.4: {} + + grapheme-splitter@1.0.4: {} + + graphemer@1.4.0: {} + + graphql@16.13.2: + optional: true + + gzip-size@6.0.0: + dependencies: + duplexer: 0.1.2 + + handle-thing@2.0.1: {} + + handlebars@4.7.9: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + + has-bigints@1.1.0: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-glob@1.0.0: + dependencies: + is-glob: 3.1.0 + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + has-unicode@2.0.1: {} + + has-value@0.3.1: + dependencies: + get-value: 2.0.6 + has-values: 0.1.4 + isobject: 2.1.0 + + has-value@1.0.0: + dependencies: + get-value: 2.0.6 + has-values: 1.0.0 + isobject: 3.0.1 + + has-values@0.1.4: {} + + has-values@1.0.0: + dependencies: + is-number: 3.0.0 + kind-of: 4.0.0 + + has-yarn@2.1.0: {} + + hash-base@3.0.5: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + + hash-base@3.1.2: + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + hash.js@1.1.7: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hast-to-hyperscript@9.0.1: + dependencies: + '@types/unist': 2.0.11 + comma-separated-tokens: 1.0.8 + property-information: 5.6.0 + space-separated-tokens: 1.1.5 + style-to-object: 0.3.0 + unist-util-is: 4.1.0 + web-namespaces: 1.1.4 + + hast-util-from-parse5@6.0.1: + dependencies: + '@types/parse5': 5.0.3 + hastscript: 6.0.0 + property-information: 5.6.0 + vfile: 4.2.1 + vfile-location: 3.2.0 + web-namespaces: 1.1.4 + + hast-util-parse-selector@2.2.5: {} + + hast-util-raw@6.0.1: + dependencies: + '@types/hast': 2.3.10 + hast-util-from-parse5: 6.0.1 + hast-util-to-parse5: 6.0.0 + html-void-elements: 1.0.5 + parse5: 6.0.1 + unist-util-position: 3.1.0 + vfile: 4.2.1 + web-namespaces: 1.1.4 + xtend: 4.0.2 + zwitch: 1.0.5 + + hast-util-to-parse5@6.0.0: + dependencies: + hast-to-hyperscript: 9.0.1 + property-information: 5.6.0 + web-namespaces: 1.1.4 + xtend: 4.0.2 + zwitch: 1.0.5 + + hastscript@6.0.0: + dependencies: + '@types/hast': 2.3.10 + comma-separated-tokens: 1.0.8 + hast-util-parse-selector: 2.2.5 + property-information: 5.6.0 + space-separated-tokens: 1.1.5 + + he@1.2.0: {} + + highlight.js@10.7.3: {} + + history@5.0.0: + dependencies: + '@babel/runtime': 7.29.2 + + hmac-drbg@1.0.1: + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + hosted-git-info@2.8.9: {} + + hosted-git-info@4.1.0: + dependencies: + lru-cache: 6.0.0 + + hpack.js@2.1.6: + dependencies: + inherits: 2.0.4 + obuf: 1.1.2 + readable-stream: 2.3.8 + wbuf: 1.7.3 + + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + html-entities@2.6.0: {} + + html-escaper@2.0.2: {} + + html-minifier-terser@5.1.1: + dependencies: + camel-case: 4.1.2 + clean-css: 4.2.4 + commander: 4.1.1 + he: 1.2.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 4.8.1 + + html-minifier-terser@6.1.0: + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 8.3.0 + he: 1.2.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.46.1 + + html-tags@3.3.1: {} + + html-void-elements@1.0.5: {} + + html-webpack-plugin@4.5.2(webpack@4.47.0): + dependencies: + '@types/html-minifier-terser': 5.1.2 + '@types/tapable': 1.0.6 + '@types/webpack': 4.41.32 + html-minifier-terser: 5.1.1 + loader-utils: 1.4.2 + lodash: 4.18.1 + pretty-error: 2.1.2 + tapable: 1.1.3 + util.promisify: 1.0.0 + webpack: 4.47.0 + + html-webpack-plugin@5.5.4(webpack@5.105.4): + dependencies: + '@types/html-minifier-terser': 6.1.0 + html-minifier-terser: 6.1.0 + lodash: 4.18.1 + pretty-error: 4.0.0 + tapable: 2.3.0 + webpack: 5.105.4 + + htmlparser2@6.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 2.2.0 + + htmlparser2@8.0.2: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + + http-cache-semantics@4.2.0: {} + + http-deceiver@1.2.7: {} + + http-errors@1.8.1: + dependencies: + depd: 1.1.2 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 1.5.0 + toidentifier: 1.0.1 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + http-parser-js@0.5.10: {} + + http-proxy-agent@4.0.1: + dependencies: + '@tootallnate/once': 1.1.2 + agent-base: 6.0.2 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + http-proxy-middleware@2.0.9: + dependencies: + '@types/express': 4.17.21 + '@types/http-proxy': 1.17.17 + http-proxy: 1.18.1 + is-glob: 4.0.3 + is-plain-obj: 3.0.0 + micromatch: 4.0.8 + transitivePeerDependencies: + - debug + + http-proxy@1.18.1: + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.15.11 + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + + http2-express-bridge@1.0.7(@types/express@4.17.21): + dependencies: + '@types/express': 4.17.21 + merge-descriptors: 1.0.3 + send: 0.17.2 + setprototypeof: 1.2.0 + + http2-wrapper@1.0.3: + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + + https-browserify@1.0.0: {} + + https-proxy-agent@4.0.0: + dependencies: + agent-base: 5.1.1 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + + hyperdyperid@1.2.0: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + + icss-utils@4.1.1: + dependencies: + postcss: 7.0.39 + + icss-utils@5.1.0(postcss@8.4.49): + dependencies: + postcss: 8.4.49 + + icss-utils@5.1.0(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + + ieee754@1.1.13: {} + + ieee754@1.2.1: {} + + iferr@0.1.5: {} + + ignore-walk@5.0.1: + dependencies: + minimatch: 5.1.9 + + ignore@4.0.6: {} + + ignore@5.1.9: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + immediate@3.0.6: {} + + immer@11.1.4: {} + + immer@9.0.21: {} + + immutable@4.3.8: {} + + immutable@5.1.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-lazy@2.1.0: {} + + import-lazy@4.0.0: {} + + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + indent-string@5.0.0: {} + + individual@3.0.0: {} + + infer-owner@1.0.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.3: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ini@2.0.0: {} + + inline-style-parser@0.1.1: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + interpret@1.4.0: {} + + interpret@2.2.0: {} + + invariant@2.2.4: + dependencies: + loose-envify: 1.4.0 + + ip-address@10.1.0: {} + + ip@1.1.9: {} + + ipaddr.js@1.9.1: {} + + ipaddr.js@2.3.0: {} + + is-absolute-url@3.0.3: {} + + is-accessor-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + + is-alphabetical@1.0.4: {} + + is-alphanumerical@1.0.4: + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-arrayish@0.2.1: {} + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-binary-path@1.0.1: + dependencies: + binary-extensions: 1.13.1 + optional: true + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-buffer@1.1.6: {} + + is-buffer@2.0.5: {} + + is-callable@1.2.7: {} + + is-ci@2.0.0: + dependencies: + ci-info: 2.0.0 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-decimal@1.0.4: {} + + is-descriptor@0.1.7: + dependencies: + is-accessor-descriptor: 1.0.1 + is-data-descriptor: 1.0.1 + + is-descriptor@1.0.3: + dependencies: + is-accessor-descriptor: 1.0.1 + is-data-descriptor: 1.0.1 + + is-docker@2.2.1: {} + + is-docker@3.0.0: {} + + is-dom@1.1.0: + dependencies: + is-object: 1.0.2 + is-window: 1.0.2 + + is-extendable@0.1.1: {} + + is-extendable@1.0.1: + dependencies: + is-plain-object: 2.0.4 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@1.0.0: + dependencies: + number-is-nan: 1.0.1 + + is-fullwidth-code-point@2.0.0: {} + + is-fullwidth-code-point@3.0.0: {} + + is-function@1.0.2: {} + + is-generator-fn@2.1.0: {} + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@3.1.0: + dependencies: + is-extglob: 2.1.1 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@1.0.4: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-installed-globally@0.4.0: + dependencies: + global-dirs: 3.0.1 + is-path-inside: 3.0.3 + + is-lambda@1.0.1: {} + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-network-error@1.3.1: {} + + is-npm@5.0.0: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@3.0.0: + dependencies: + kind-of: 3.2.2 + + is-number@7.0.0: {} + + is-obj@2.0.0: {} + + is-object@1.0.2: {} + + is-path-inside@3.0.3: {} + + is-plain-obj@2.1.0: {} + + is-plain-obj@3.0.0: {} + + is-plain-object@2.0.4: + dependencies: + isobject: 3.0.1 + + is-plain-object@5.0.0: {} + + is-potential-custom-element-name@1.0.1: {} + + is-promise@4.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@1.1.0: {} + + is-stream@2.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-subdir@1.2.0: + dependencies: + better-path-resolve: 1.0.0 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + + is-typedarray@1.0.0: {} + + is-unicode-supported@0.1.0: {} + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-whitespace-character@1.0.4: {} + + is-window@1.0.2: {} + + is-windows@1.0.2: {} + + is-word-character@1.0.4: {} + + is-wsl@1.1.0: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + is-wsl@3.1.1: + dependencies: + is-inside-container: 1.0.0 + + is-yarn-global@0.3.0: {} + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + isobject@2.1.0: + dependencies: + isarray: 1.0.0 + + isobject@3.0.1: {} + + isobject@4.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.20.12 + '@babel/parser': 7.29.2 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.2 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + debug: 4.4.3(supports-color@8.1.1) + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + iterate-iterator@1.0.2: {} + + iterate-value@1.0.2: + dependencies: + es-get-iterator: 1.1.3 + iterate-iterator: 1.0.2 + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jackspeak@4.2.3: + dependencies: + '@isaacs/cliui': 9.0.0 + + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + + jest-changed-files@30.3.0: + dependencies: + execa: 5.1.1 + jest-util: 30.3.0 + p-limit: 3.1.0 + + jest-circus@29.7.0(babel-plugin-macros@3.1.0): + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0(@types/node@22.9.3) + '@jest/types': 29.6.3 + '@types/node': 22.9.3 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.7.2(babel-plugin-macros@3.1.0) + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-circus@30.3.0: + dependencies: + '@jest/environment': 30.3.0 + '@jest/expect': 30.3.0 + '@jest/test-result': 30.3.0(@types/node@22.9.3) + '@jest/types': 30.3.0 + '@types/node': 22.9.3 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.7.2 + is-generator-fn: 2.1.0 + jest-each: 30.3.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-runtime: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 + p-limit: 3.1.0 + pretty-format: 30.3.0 + pure-rand: 7.0.1 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@29.7.0(@types/node@20.17.19)(babel-plugin-macros@3.1.0): + dependencies: + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0) + '@jest/test-result': 29.7.0(@types/node@20.17.19) + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@20.17.19)(babel-plugin-macros@3.1.0) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@20.17.19)(babel-plugin-macros@3.1.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-config@29.7.0(@types/node@20.17.19)(babel-plugin-macros@3.1.0): + dependencies: + '@babel/core': 7.20.12 + '@jest/test-sequencer': 29.7.0(@types/node@20.17.19) + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.20.12) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0(babel-plugin-macros@3.1.0) + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.17.19 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-config@29.7.0(@types/node@22.9.3)(babel-plugin-macros@3.1.0): + dependencies: + '@babel/core': 7.20.12 + '@jest/test-sequencer': 29.7.0(@types/node@22.9.3) + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.20.12) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0(babel-plugin-macros@3.1.0) + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.9.3 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-config@30.3.0(@types/node@20.17.19): + dependencies: + '@babel/core': 7.29.0 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.3.0(@types/node@20.17.19) + '@jest/types': 30.3.0 + babel-jest: 30.3.0(@babel/core@7.29.0) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.3.0 + jest-docblock: 30.2.0 + jest-environment-node: 30.3.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.3.0 + jest-runner: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + parse-json: 5.2.0 + pretty-format: 30.3.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.17.19 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-config@30.3.0(@types/node@22.9.3): + dependencies: + '@babel/core': 7.29.0 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.3.0(@types/node@22.9.3) + '@jest/types': 30.3.0 + babel-jest: 30.3.0(@babel/core@7.29.0) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.3.0 + jest-docblock: 30.2.0 + jest-environment-node: 30.3.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.3.0 + jest-runner: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + parse-json: 5.2.0 + pretty-format: 30.3.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.9.3 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@27.5.1: + dependencies: + chalk: 4.1.2 + diff-sequences: 27.5.1 + jest-get-type: 27.5.1 + pretty-format: 27.5.1 + + jest-diff@29.7.0: + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-diff@30.3.0: + dependencies: + '@jest/diff-sequences': 30.3.0 + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + pretty-format: 30.3.0 + + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + + jest-docblock@30.2.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + + jest-each@30.3.0: + dependencies: + '@jest/get-type': 30.1.0 + '@jest/types': 30.3.0 + chalk: 4.1.2 + jest-util: 30.3.0 + pretty-format: 30.3.0 + + jest-environment-jsdom@30.3.0: + dependencies: + '@jest/environment': 30.3.0 + '@jest/environment-jsdom-abstract': 30.3.0(jsdom@26.1.0) + jsdom: 26.1.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.9.3 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + jest-environment-node@30.3.0: + dependencies: + '@jest/environment': 30.3.0 + '@jest/fake-timers': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 22.9.3 + jest-mock: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + + jest-get-type@27.5.1: {} + + jest-get-type@29.6.3: {} + + jest-haste-map@26.6.2: + dependencies: + '@jest/types': 26.6.2 + '@types/graceful-fs': 4.1.9 + '@types/node': 22.9.3 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 26.0.0 + jest-serializer: 26.6.2 + jest-util: 26.6.2 + jest-worker: 26.6.2 + micromatch: 4.0.8 + sane: 4.1.0 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-haste-map@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 22.9.3 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-haste-map@30.3.0: + dependencies: + '@jest/types': 30.3.0 + '@types/node': 22.9.3 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 30.0.1 + jest-util: 30.3.0 + jest-worker: 30.3.0 + picomatch: 4.0.4 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-junit@12.3.0: + dependencies: + mkdirp: 1.0.4 + strip-ansi: 5.2.0 + uuid: 8.3.2 + xml: 1.0.1 + + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-leak-detector@30.3.0: + dependencies: + '@jest/get-type': 30.1.0 + pretty-format: 30.3.0 + + jest-matcher-utils@27.5.1: + dependencies: + chalk: 4.1.2 + jest-diff: 27.5.1 + jest-get-type: 27.5.1 + pretty-format: 27.5.1 + + jest-matcher-utils@29.7.0: + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-matcher-utils@30.3.0: + dependencies: + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + jest-diff: 30.3.0 + pretty-format: 30.3.0 + + jest-message-util@29.7.0: + dependencies: + '@babel/code-frame': 7.29.0 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-message-util@30.3.0: + dependencies: + '@babel/code-frame': 7.29.0 + '@jest/types': 30.3.0 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + picomatch: 4.0.4 + pretty-format: 30.3.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.9.3 + jest-util: 29.7.0 + + jest-mock@30.3.0: + dependencies: + '@jest/types': 30.3.0 + '@types/node': 22.9.3 + jest-util: 30.3.0 + + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + optionalDependencies: + jest-resolve: 29.7.0 + + jest-pnp-resolver@1.2.3(jest-resolve@30.3.0): + optionalDependencies: + jest-resolve: 30.3.0 + + jest-regex-util@26.0.0: {} + + jest-regex-util@29.6.3: {} + + jest-regex-util@30.0.1: {} + + jest-resolve-dependencies@29.7.0: + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + jest-resolve-dependencies@30.3.0: + dependencies: + jest-regex-util: 30.0.1 + jest-snapshot: 30.3.0 + transitivePeerDependencies: + - supports-color + + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.11 + resolve.exports: 2.0.3 + slash: 3.0.0 + + jest-resolve@30.3.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + jest-pnp-resolver: 1.2.3(jest-resolve@30.3.0) + jest-util: 30.3.0 + jest-validate: 30.3.0 + slash: 3.0.0 + unrs-resolver: 1.11.1 + + jest-runner@29.7.0: + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0(@types/node@22.9.3) + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.9.3 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runner@30.3.0: + dependencies: + '@jest/console': 30.3.0 + '@jest/environment': 30.3.0 + '@jest/test-result': 30.3.0(@types/node@22.9.3) + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 22.9.3 + chalk: 4.1.2 + emittery: 0.13.1 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-docblock: 30.2.0 + jest-environment-node: 30.3.0 + jest-haste-map: 30.3.0 + jest-leak-detector: 30.3.0 + jest-message-util: 30.3.0 + jest-resolve: 30.3.0 + jest-runtime: 30.3.0 + jest-util: 30.3.0 + jest-watcher: 30.3.0 + jest-worker: 30.3.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0(@types/node@22.9.3) + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.9.3 + chalk: 4.1.2 + cjs-module-lexer: 1.4.3 + collect-v8-coverage: 1.0.3(@types/node@22.9.3) + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-runtime@30.3.0: + dependencies: + '@jest/environment': 30.3.0 + '@jest/fake-timers': 30.3.0 + '@jest/globals': 30.3.0 + '@jest/source-map': 30.0.1 + '@jest/test-result': 30.3.0(@types/node@22.9.3) + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 22.9.3 + chalk: 4.1.2 + cjs-module-lexer: 2.2.0 + collect-v8-coverage: 1.0.3(@types/node@22.9.3) + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-serializer@26.6.2: + dependencies: + '@types/node': 22.9.3 + graceful-fs: 4.2.11 + + jest-snapshot@29.7.0: + dependencies: + '@babel/core': 7.20.12 + '@babel/generator': 7.29.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.20.12) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.20.12) + '@babel/types': 7.29.0 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.20.12) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + + jest-snapshot@30.3.0: + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/types': 7.29.0 + '@jest/expect-utils': 30.3.0 + '@jest/get-type': 30.1.0 + '@jest/snapshot-utils': 30.3.0 + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + chalk: 4.1.2 + expect: 30.3.0 + graceful-fs: 4.2.11 + jest-diff: 30.3.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-util: 30.3.0 + pretty-format: 30.3.0 + semver: 7.7.4 + synckit: 0.11.12 + transitivePeerDependencies: + - supports-color + + jest-util@26.6.2: + dependencies: + '@jest/types': 26.6.2 + '@types/node': 22.9.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + is-ci: 2.0.0 + micromatch: 4.0.8 + + jest-util@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.9.3 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.2 + + jest-util@30.3.0: + dependencies: + '@jest/types': 30.3.0 + '@types/node': 22.9.3 + chalk: 4.1.2 + ci-info: 4.4.0 + graceful-fs: 4.2.11 + picomatch: 4.0.4 + + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + + jest-validate@30.3.0: + dependencies: + '@jest/get-type': 30.1.0 + '@jest/types': 30.3.0 + camelcase: 6.3.0 + chalk: 4.1.2 + leven: 3.1.0 + pretty-format: 30.3.0 + + jest-watch-select-projects@2.0.0: + dependencies: + ansi-escapes: 4.3.2 + chalk: 3.0.0 + prompts: 2.4.2 + + jest-watcher@29.7.0: + dependencies: + '@jest/test-result': 29.7.0(@types/node@22.9.3) + '@jest/types': 29.6.3 + '@types/node': 22.9.3 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + + jest-watcher@30.3.0: + dependencies: + '@jest/test-result': 30.3.0(@types/node@22.9.3) + '@jest/types': 30.3.0 + '@types/node': 22.9.3 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 30.3.0 + string-length: 4.0.2 + + jest-worker@26.6.2: + dependencies: + '@types/node': 22.9.3 + merge-stream: 2.0.0 + supports-color: 7.2.0 + + jest-worker@27.5.1: + dependencies: + '@types/node': 22.9.3 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest-worker@29.7.0: + dependencies: + '@types/node': 22.9.3 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest-worker@30.3.0: + dependencies: + '@types/node': 22.9.3 + '@ungap/structured-clone': 1.3.0 + jest-util: 30.3.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@29.3.1(@types/node@20.17.19)(babel-plugin-macros@3.1.0): + dependencies: + '@jest/core': 29.5.0(babel-plugin-macros@3.1.0) + '@jest/types': 29.5.0 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@20.17.19)(babel-plugin-macros@3.1.0) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jju@1.4.0: {} + + jmespath@0.16.0: {} + + js-sdsl@4.4.2: {} + + js-string-escape@1.0.1: {} + + js-tokens@4.0.0: {} + + js-yaml@3.14.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jscodeshift@0.13.1(@babel/preset-env@7.29.2(@babel/core@7.20.12)): + dependencies: + '@babel/core': 7.20.12 + '@babel/parser': 7.29.2 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.20.12) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.20.12) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.20.12) + '@babel/preset-env': 7.29.2(@babel/core@7.20.12) + '@babel/preset-flow': 7.27.1(@babel/core@7.20.12) + '@babel/preset-typescript': 7.28.5(@babel/core@7.20.12) + '@babel/register': 7.28.6(@babel/core@7.20.12) + babel-core: 7.0.0-bridge.0(@babel/core@7.20.12) + chalk: 4.1.2 + flow-parser: 0.307.1 + graceful-fs: 4.2.11 + micromatch: 3.1.10 + neo-async: 2.6.2 + node-dir: 0.1.17 + recast: 0.20.5 + temp: 0.8.4 + write-file-atomic: 2.4.3 + transitivePeerDependencies: + - supports-color + + jscodeshift@0.15.2(@babel/preset-env@7.29.2(@babel/core@7.20.12)): + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.2 + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) + '@babel/preset-flow': 7.27.1(@babel/core@7.29.0) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) + '@babel/register': 7.28.6(@babel/core@7.29.0) + babel-core: 7.0.0-bridge.0(@babel/core@7.29.0) + chalk: 4.1.2 + flow-parser: 0.307.1 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + neo-async: 2.6.2 + node-dir: 0.1.17 + recast: 0.23.11 + temp: 0.8.4 + write-file-atomic: 2.4.3 + optionalDependencies: + '@babel/preset-env': 7.29.2(@babel/core@7.20.12) + transitivePeerDependencies: + - supports-color + + jsdoc-type-pratt-parser@4.1.0: {} + + jsdom@26.1.0: + dependencies: + cssstyle: 4.6.0 + data-urls: 5.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.23 + parse5: 7.3.0 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.21.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jsep@1.4.0: {} + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-better-errors@1.0.2: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-to-typescript@15.0.4: + dependencies: + '@apidevtools/json-schema-ref-parser': 11.9.3 + '@types/json-schema': 7.0.15 + '@types/lodash': 4.17.23 + is-glob: 4.0.3 + js-yaml: 4.1.1 + lodash: 4.18.1 + minimist: 1.2.8 + prettier: 3.8.1 + tinyglobby: 0.2.15 + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-schema-typed@7.0.3: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json-stringify-safe@5.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + json5@2.2.3: {} + + jsonc-parser@3.3.1: {} + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonpath-plus@10.3.0: + dependencies: + '@jsep-plugin/assignment': 1.3.0(jsep@1.4.0) + '@jsep-plugin/regex': 1.0.4(jsep@1.4.0) + jsep: 1.4.0 + + jsonwebtoken@9.0.3: + dependencies: + jws: 4.0.1 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.7.4 + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + jszip@2.7.0: + dependencies: + pako: 1.0.11 + + jszip@3.8.0: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + set-immediate-shim: 1.0.1 + + junk@3.1.0: {} + + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.1: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + + keyborg@2.6.0: {} + + keytar@7.9.0: + dependencies: + node-addon-api: 4.3.0 + prebuild-install: 7.1.3 + optional: true + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kind-of@3.2.2: + dependencies: + is-buffer: 1.1.6 + + kind-of@4.0.0: + dependencies: + is-buffer: 1.1.6 + + kind-of@6.0.3: {} + + kleur@3.0.3: {} + + klona@2.0.6: {} + + kysely-codegen@0.6.2(kysely@0.21.6): + dependencies: + chalk: 4.1.2 + dotenv: 16.4.7 + kysely: 0.21.6 + micromatch: 4.0.8 + minimist: 1.2.8 + + kysely-data-api@0.1.4(aws-sdk@2.1693.0)(kysely@0.21.6): + dependencies: + aws-sdk: 2.1693.0 + kysely: 0.21.6 + + kysely@0.21.6: {} + + latest-version@5.1.0: + dependencies: + package-json: 7.0.0 + + launch-editor@2.13.2: + dependencies: + picocolors: 1.1.1 + shell-quote: 1.8.3 + + lazy-universal-dotenv@3.0.1: + dependencies: + '@babel/runtime': 7.29.2 + app-root-dir: 1.0.2 + core-js: 3.49.0 + dotenv: 8.6.0 + dotenv-expand: 5.1.0 + + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + + leven@3.1.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lie@3.3.0: + dependencies: + immediate: 3.0.6 + + light-my-request@4.12.0: + dependencies: + ajv: 8.20.0 + cookie: 0.5.0 + process-warning: 1.0.0 + set-cookie-parser: 2.7.2 + + lilconfig@2.1.0: {} + + lines-and-columns@1.2.4: {} + + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + + listenercount@1.0.1: {} + + load-json-file@6.2.0: + dependencies: + graceful-fs: 4.2.11 + parse-json: 5.2.0 + strip-bom: 4.0.0 + type-fest: 0.6.0 + + loader-runner@2.4.0: {} + + loader-runner@4.3.1: {} + + loader-utils@1.4.2: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 1.0.2 + + loader-utils@2.0.4: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 2.2.3 + + loader-utils@3.3.1: {} + + locate-path@3.0.0: + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash.camelcase@4.3.0: {} + + lodash.debounce@4.0.8: {} + + lodash.defaults@4.2.0: {} + + lodash.difference@4.5.0: {} + + lodash.flatten@4.4.0: {} + + lodash.get@4.4.2: {} + + lodash.includes@4.3.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isequal@4.5.0: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + + lodash.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash.once@4.1.1: {} + + lodash.truncate@4.4.2: {} + + lodash.union@4.6.0: {} + + lodash.uniq@4.5.0: {} + + lodash@4.18.1: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log4js@6.9.1: + dependencies: + date-format: 4.0.14 + debug: 4.4.3(supports-color@8.1.1) + flatted: 3.4.2 + rfdc: 1.4.1 + streamroller: 3.1.5 + transitivePeerDependencies: + - supports-color + + long@4.0.0: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + loupe@3.2.1: {} + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lowercase-keys@2.0.0: {} + + lowlight@1.20.0: + dependencies: + fault: 1.0.4 + highlight.js: 10.7.3 + + lru-cache@10.4.3: {} + + lru-cache@11.2.7: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + make-dir@2.1.0: + dependencies: + pify: 4.0.1 + semver: 5.7.2 + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.4 + + make-fetch-happen@8.0.14: + dependencies: + agentkeepalive: 4.6.0 + cacache: 15.3.0 + http-cache-semantics: 4.2.0 + http-proxy-agent: 4.0.1 + https-proxy-agent: 5.0.1 + is-lambda: 1.0.1 + lru-cache: 6.0.0 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-fetch: 1.4.1 + minipass-flush: 1.0.7 + minipass-pipeline: 1.2.4 + promise-retry: 2.0.1 + socks-proxy-agent: 5.0.1 + ssri: 8.0.1 + transitivePeerDependencies: + - supports-color + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + map-age-cleaner@0.1.3: + dependencies: + p-defer: 1.0.0 + + map-cache@0.2.2: {} + + map-or-similar@1.5.0: {} + + map-visit@1.0.0: + dependencies: + object-visit: 1.0.1 + + markdown-escapes@1.0.4: {} + + markdown-it@14.1.1: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + markdown-to-jsx@7.7.17(react@17.0.2): + optionalDependencies: + react: 17.0.2 + + math-intrinsics@1.1.0: {} + + md5.js@1.3.5: + dependencies: + hash-base: 3.0.5 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + mdast-squeeze-paragraphs@4.0.0: + dependencies: + unist-util-remove: 2.1.0 + + mdast-util-definitions@4.0.0: + dependencies: + unist-util-visit: 2.0.3 + + mdast-util-to-hast@10.0.1: + dependencies: + '@types/mdast': 3.0.15 + '@types/unist': 2.0.11 + mdast-util-definitions: 4.0.0 + mdurl: 1.0.1 + unist-builder: 2.0.3 + unist-util-generated: 1.1.6 + unist-util-position: 3.1.0 + unist-util-visit: 2.0.3 + + mdast-util-to-string@1.1.0: {} + + mdn-data@2.0.14: {} + + mdurl@1.0.1: {} + + mdurl@2.0.0: {} + + media-typer@0.3.0: {} + + media-typer@1.1.0: {} + + mem@8.1.1: + dependencies: + map-age-cleaner: 0.1.3 + mimic-fn: 3.1.0 + + memfs@3.4.3: + dependencies: + fs-monkey: 1.0.3 + + memfs@3.5.3: + dependencies: + fs-monkey: 1.1.0 + + memfs@4.12.0: + dependencies: + '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + memfs@4.57.1: + dependencies: + '@jsonjoy.com/fs-core': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-fsa': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-to-fsa': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.57.1(tslib@2.8.1) + '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + memoizerific@1.11.3: + dependencies: + map-or-similar: 1.5.0 + + memory-fs@0.4.1: + dependencies: + errno: 0.1.8 + readable-stream: 2.3.8 + + memory-fs@0.5.0: + dependencies: + errno: 0.1.8 + readable-stream: 2.3.8 + + merge-descriptors@1.0.3: {} + + merge-descriptors@2.0.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + methods@1.1.2: {} + + microevent.ts@0.1.1: {} + + micromatch@3.1.10: + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + braces: 2.3.2 + define-property: 2.0.2 + extend-shallow: 3.0.2 + extglob: 2.0.4 + fragment-cache: 0.2.1 + kind-of: 6.0.3 + nanomatch: 1.2.13 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + miller-rabin@4.0.1: + dependencies: + bn.js: 4.12.3 + brorand: 1.1.0 + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mime@1.6.0: {} + + mime@2.6.0: {} + + mimic-fn@2.1.0: {} + + mimic-fn@3.1.0: {} + + mimic-response@1.0.1: {} + + mimic-response@3.1.0: {} + + min-document@2.19.2: + dependencies: + dom-walk: 0.1.2 + + min-indent@1.0.1: {} + + mini-css-extract-plugin@2.5.3(webpack@5.105.4): + dependencies: + schema-utils: 4.3.3 + webpack: 5.105.4 + + minimalistic-assert@1.0.1: {} + + minimalistic-crypto-utils@1.0.1: {} + + minimatch@10.2.3: + dependencies: + brace-expansion: 5.0.5 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.13 + + minimatch@5.1.9: + dependencies: + brace-expansion: 2.0.3 + + minimatch@9.0.3: + dependencies: + brace-expansion: 2.0.3 + + minimatch@9.0.9: + dependencies: + brace-expansion: 2.0.3 + + minimist@1.2.8: {} + + minipass-collect@1.0.2: + dependencies: + minipass: 3.3.6 + + minipass-fetch@1.4.1: + dependencies: + minipass: 3.3.6 + minipass-sized: 1.0.3 + minizlib: 2.1.2 + optionalDependencies: + encoding: 0.1.13 + + minipass-flush@1.0.7: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass-sized@1.0.3: + dependencies: + minipass: 3.3.6 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minipass@7.1.3: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + + mississippi@3.0.0: + dependencies: + concat-stream: 1.6.2 + duplexify: 3.7.1 + end-of-stream: 1.4.5 + flush-write-stream: 1.1.1 + from2: 2.3.0 + parallel-transform: 1.2.0 + pump: 3.0.4 + pumpify: 1.5.1 + stream-each: 1.2.3 + through2: 2.0.5 + + mixin-deep@1.3.2: + dependencies: + for-in: 1.0.2 + is-extendable: 1.0.1 + + mkdirp-classic@0.5.3: + optional: true + + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + + mkdirp@1.0.4: {} + + mlly@1.8.2: + dependencies: + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.3 + + mocha@10.8.2: + dependencies: + ansi-colors: 4.1.3 + browser-stdout: 1.3.1 + chokidar: 3.6.0 + debug: 4.4.3(supports-color@8.1.1) + diff: 5.2.2 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 8.1.0 + he: 1.2.0 + js-yaml: 4.1.1 + log-symbols: 4.1.0 + minimatch: 5.1.9 + ms: 2.1.3 + serialize-javascript: 6.0.2 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + workerpool: 6.5.1 + yargs: 16.2.0 + yargs-parser: 20.2.9 + yargs-unparser: 2.0.0 + + move-concurrently@1.0.1: + dependencies: + aproba: 1.2.0 + copy-concurrently: 1.0.5 + fs-write-stream-atomic: 1.0.10 + mkdirp: 0.5.6 + rimraf: 2.7.1 + run-queue: 1.0.3 + + mrmime@1.0.1: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + multicast-dns@7.2.5: + dependencies: + dns-packet: 5.6.1 + thunky: 1.1.0 + + mute-stream@0.0.8: {} + + mute-stream@3.0.0: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nan@2.26.2: + optional: true + + nanoid@3.3.11: {} + + nanomatch@1.2.13: + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + define-property: 2.0.2 + extend-shallow: 3.0.2 + fragment-cache: 0.2.1 + is-windows: 1.0.2 + kind-of: 6.0.3 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + + napi-build-utils@2.0.0: + optional: true + + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + + ndjson@2.0.0: + dependencies: + json-stringify-safe: 5.0.1 + minimist: 1.2.8 + readable-stream: 3.6.2 + split2: 3.2.2 + through2: 4.0.2 + + negotiator@0.6.3: {} + + negotiator@0.6.4: {} + + negotiator@1.0.0: {} + + neo-async@2.6.2: {} + + nested-error-stacks@2.1.1: {} + + nice-try@1.0.5: {} + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-abi@3.89.0: + dependencies: + semver: 7.7.4 + optional: true + + node-abort-controller@3.1.1: {} + + node-addon-api@3.2.1: {} + + node-addon-api@4.3.0: + optional: true + + node-dir@0.1.17: + dependencies: + minimatch: 3.1.5 + + node-exports-info@1.6.0: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + + node-fetch-native@1.6.7: {} + + node-fetch@2.7.0(encoding@0.1.13): + dependencies: + whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 + + node-forge@1.4.0: {} + + node-gyp@8.1.0: + dependencies: + env-paths: 2.2.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + make-fetch-happen: 8.0.14 + nopt: 5.0.0 + npmlog: 4.1.2 + rimraf: 3.0.2 + semver: 7.7.4 + tar: 6.2.1 + which: 2.0.2 + transitivePeerDependencies: + - supports-color + + node-int64@0.4.0: {} + + node-libs-browser@2.2.1: + dependencies: + assert: 1.5.1 + browserify-zlib: 0.2.0 + buffer: 4.9.2 + console-browserify: 1.2.0 + constants-browserify: 1.0.0 + crypto-browserify: 3.12.1 + domain-browser: 1.2.0 + events: 3.3.0 + https-browserify: 1.0.0 + os-browserify: 0.3.0 + path-browserify: 0.0.1 + process: 0.11.10 + punycode: 1.4.1 + querystring-es3: 0.2.1 + readable-stream: 2.3.8 + stream-browserify: 2.0.2 + stream-http: 2.8.3 + string_decoder: 1.3.0 + timers-browserify: 2.0.12 + tty-browserify: 0.0.0 + url: 0.11.4 + util: 0.11.1 + vm-browserify: 1.1.2 + + node-releases@2.0.37: {} + + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + + normalize-package-data@2.5.0: + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.11 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + + normalize-package-data@3.0.3: + dependencies: + hosted-git-info: 4.1.0 + is-core-module: 2.16.1 + semver: 7.7.4 + validate-npm-package-license: 3.0.4 + + normalize-path@2.1.1: + dependencies: + remove-trailing-separator: 1.1.0 + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + normalize-url@6.1.0: {} + + npm-bundled@2.0.1: + dependencies: + npm-normalize-package-bin: 2.0.0 + + npm-normalize-package-bin@1.0.1: {} + + npm-normalize-package-bin@2.0.0: {} + + npm-package-arg@6.1.1: + dependencies: + hosted-git-info: 2.8.9 + osenv: 0.1.5 + semver: 5.7.2 + validate-npm-package-name: 3.0.0 + + npm-packlist@5.1.3: + dependencies: + glob: 8.1.0 + ignore-walk: 5.0.1 + npm-bundled: 2.0.1 + npm-normalize-package-bin: 2.0.0 + + npm-run-path@2.0.2: + dependencies: + path-key: 2.0.1 + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + npmlog@4.1.2: + dependencies: + are-we-there-yet: 1.1.7 + console-control-strings: 1.1.0 + gauge: 2.7.4 + set-blocking: 2.0.0 + + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + num2fraction@1.2.2: {} + + number-is-nan@1.0.1: {} + + nwsapi@2.2.23: {} + + nypm@0.5.4: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + pathe: 2.0.3 + pkg-types: 1.3.1 + tinyexec: 0.3.2 + ufo: 1.6.3 + + object-assign@4.1.1: {} + + object-copy@0.1.0: + dependencies: + copy-descriptor: 0.1.1 + define-property: 0.2.5 + kind-of: 3.2.2 + + object-hash@3.0.0: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object-visit@1.0.1: + dependencies: + isobject: 3.0.1 + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + + object.getownpropertydescriptors@2.1.9: + dependencies: + array.prototype.reduce: 1.0.8 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + gopd: 1.2.0 + safe-array-concat: 1.1.3 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + object.hasown@1.1.4: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + + object.pick@1.3.0: + dependencies: + isobject: 3.0.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + objectorarray@1.0.5: {} + + obuf@1.1.2: {} + + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + on-headers@1.0.2: {} + + on-headers@1.1.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + open@10.2.0: + dependencies: + default-browser: 5.5.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + + open@7.4.2: + dependencies: + is-docker: 2.2.1 + is-wsl: 2.2.0 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + opener@1.5.2: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + os-browserify@0.3.0: {} + + os-homedir@1.0.2: {} + + os-tmpdir@1.0.2: {} + + osenv@0.1.5: + dependencies: + os-homedir: 1.0.2 + os-tmpdir: 1.0.2 + + overlayscrollbars@1.13.3: {} + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-all@2.1.0: + dependencies: + p-map: 2.1.0 + + p-cancelable@2.1.1: {} + + p-defer@1.0.0: {} + + p-event@4.2.0: + dependencies: + p-timeout: 3.2.0 + + p-filter@2.1.0: + dependencies: + p-map: 2.1.0 + + p-finally@1.0.0: {} + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.2 + + p-locate@3.0.0: + dependencies: + p-limit: 2.3.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + p-map@2.1.0: {} + + p-map@3.0.0: + dependencies: + aggregate-error: 3.1.0 + + p-map@4.0.0: + dependencies: + aggregate-error: 3.1.0 + + p-reflect@2.1.0: {} + + p-retry@4.6.2: + dependencies: + '@types/retry': 0.12.0 + retry: 0.13.1 + + p-retry@6.2.1: + dependencies: + '@types/retry': 0.12.2 + is-network-error: 1.3.1 + retry: 0.13.1 + + p-settle@4.1.1: + dependencies: + p-limit: 2.3.0 + p-reflect: 2.1.0 + + p-timeout@3.2.0: + dependencies: + p-finally: 1.0.0 + + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + package-json@7.0.0: + dependencies: + got: 11.8.6 + registry-auth-token: 4.2.2 + registry-url: 5.1.0 + semver: 7.7.4 + + pako@1.0.11: {} + + parallel-transform@1.2.0: + dependencies: + cyclist: 1.0.2 + inherits: 2.0.4 + readable-stream: 2.3.8 + + param-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-asn1@5.1.9: + dependencies: + asn1.js: 4.10.1 + browserify-aes: 1.2.0 + evp_bytestokey: 1.0.3 + pbkdf2: 3.1.5 + safe-buffer: 5.2.1 + + parse-entities@2.0.0: + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + + parse-imports-exports@0.2.4: + dependencies: + parse-statements: 1.0.11 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-semver@1.1.1: + dependencies: + semver: 5.7.2 + + parse-statements@1.0.11: {} + + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.3.0 + + parse5@6.0.1: {} + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + parseurl@1.3.3: {} + + pascal-case@3.1.2: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + pascalcase@0.1.1: {} + + path-browserify@0.0.1: {} + + path-dirname@1.0.2: {} + + path-exists@3.0.0: {} + + path-exists@4.0.0: {} + + path-exists@5.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@2.0.1: {} + + path-key@3.1.1: {} + + path-name@1.0.0: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + + path-scurry@2.0.2: + dependencies: + lru-cache: 11.2.7 + minipass: 7.1.3 + + path-to-regexp@0.1.10: {} + + path-to-regexp@0.1.13: {} + + path-to-regexp@8.4.2: {} + + path-type@3.0.0: + dependencies: + pify: 3.0.0 + + path-type@4.0.0: {} + + path-type@6.0.0: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + pbkdf2@3.1.5: + dependencies: + create-hash: 1.2.0 + create-hmac: 1.1.7 + ripemd160: 2.0.3 + safe-buffer: 5.2.1 + sha.js: 2.4.12 + to-buffer: 1.2.2 + + pend@1.2.0: {} + + picocolors@0.2.1: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + pify@3.0.0: {} + + pify@4.0.1: {} + + pino-std-serializers@3.2.0: {} + + pino@6.14.0: + dependencies: + fast-redact: 3.5.0 + fast-safe-stringify: 2.1.1 + flatstr: 1.0.12 + pino-std-serializers: 3.2.0 + process-warning: 1.0.0 + quick-format-unescaped: 4.0.4 + sonic-boom: 1.4.1 + + pirates@4.0.7: {} + + pkce-challenge@5.0.1: {} + + pkg-dir@3.0.0: + dependencies: + find-up: 3.0.0 + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + pkg-dir@5.0.0: + dependencies: + find-up: 5.0.0 + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + + pkg-up@3.1.0: + dependencies: + find-up: 3.0.0 + + pkijs@3.4.0: + dependencies: + '@noble/hashes': 1.4.0 + asn1js: 3.0.7 + bytestreamjs: 2.0.1 + pvtsutils: 1.3.6 + pvutils: 1.1.5 + tslib: 2.8.1 + + playwright-core@1.56.1: {} + + playwright@1.56.1: + dependencies: + playwright-core: 1.56.1 + optionalDependencies: + fsevents: 2.3.2 + + pnp-webpack-plugin@1.6.4(typescript@5.8.2): + dependencies: + ts-pnp: 1.2.0(typescript@5.8.2) + transitivePeerDependencies: + - typescript + + pnpm-sync-lib@0.3.4: + dependencies: + '@pnpm/dependency-path-pnpm-v10': '@pnpm/dependency-path@1000.0.9' + '@pnpm/dependency-path-pnpm-v8': '@pnpm/dependency-path@2.1.8' + '@pnpm/dependency-path-pnpm-v9': '@pnpm/dependency-path@5.1.7' + '@pnpm/lockfile-types-pnpm-lock-v6': '@pnpm/lockfile-types@5.1.5' + '@pnpm/lockfile.types-pnpm-lock-v9': '@pnpm/lockfile.types@1001.1.0' + yaml: 2.9.0 + + polished@4.3.1: + dependencies: + '@babel/runtime': 7.29.2 + + posix-character-classes@0.1.1: {} + + possible-typed-array-names@1.1.0: {} + + postcss-calc@8.2.4(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + + postcss-colormin@5.3.1(postcss@8.5.12): + dependencies: + browserslist: 4.28.2 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-convert-values@5.1.3(postcss@8.5.12): + dependencies: + browserslist: 4.28.2 + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-discard-comments@5.1.2(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + + postcss-discard-duplicates@5.1.0(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + + postcss-discard-empty@5.1.1(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + + postcss-discard-overridden@5.1.0(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + + postcss-flexbugs-fixes@4.2.1: + dependencies: + postcss: 7.0.39 + + postcss-loader@4.1.0(postcss@8.5.12)(webpack@4.47.0): + dependencies: + cosmiconfig: 7.1.0 + klona: 2.0.6 + loader-utils: 2.0.4 + postcss: 8.5.12 + schema-utils: 3.3.0 + semver: 7.7.4 + webpack: 4.47.0 + + postcss-loader@4.3.0(postcss@7.0.39)(webpack@4.47.0): + dependencies: + cosmiconfig: 7.1.0 + klona: 2.0.6 + loader-utils: 2.0.4 + postcss: 7.0.39 + schema-utils: 3.3.0 + semver: 7.7.4 + webpack: 4.47.0 + + postcss-loader@6.2.1(postcss@8.5.12)(webpack@5.105.4): + dependencies: + cosmiconfig: 7.1.0 + klona: 2.0.6 + postcss: 8.5.12 + semver: 7.7.4 + webpack: 5.105.4 + + postcss-merge-longhand@5.1.7(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + stylehacks: 5.1.1(postcss@8.5.12) + + postcss-merge-rules@5.1.4(postcss@8.5.12): + dependencies: + browserslist: 4.28.2 + caniuse-api: 3.0.0 + cssnano-utils: 3.1.0(postcss@8.5.12) + postcss: 8.5.12 + postcss-selector-parser: 6.1.2 + + postcss-minify-font-values@5.1.0(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-minify-gradients@5.1.1(postcss@8.5.12): + dependencies: + colord: 2.9.3 + cssnano-utils: 3.1.0(postcss@8.5.12) + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-minify-params@5.1.4(postcss@8.5.12): + dependencies: + browserslist: 4.28.2 + cssnano-utils: 3.1.0(postcss@8.5.12) + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-minify-selectors@5.2.1(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + postcss-selector-parser: 6.1.2 + + postcss-modules-extract-imports@2.0.0: + dependencies: + postcss: 7.0.39 + + postcss-modules-extract-imports@3.1.0(postcss@8.4.49): + dependencies: + postcss: 8.4.49 + + postcss-modules-extract-imports@3.1.0(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + + postcss-modules-local-by-default@3.0.3: + dependencies: + icss-utils: 4.1.1 + postcss: 7.0.39 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + + postcss-modules-local-by-default@4.2.0(postcss@8.4.49): + dependencies: + icss-utils: 5.1.0(postcss@8.4.49) + postcss: 8.4.49 + postcss-selector-parser: 7.1.1 + postcss-value-parser: 4.2.0 + + postcss-modules-local-by-default@4.2.0(postcss@8.5.12): + dependencies: + icss-utils: 5.1.0(postcss@8.5.12) + postcss: 8.5.12 + postcss-selector-parser: 7.1.1 + postcss-value-parser: 4.2.0 + + postcss-modules-scope@2.2.0: + dependencies: + postcss: 7.0.39 + postcss-selector-parser: 6.1.2 + + postcss-modules-scope@3.2.1(postcss@8.4.49): + dependencies: + postcss: 8.4.49 + postcss-selector-parser: 7.1.1 + + postcss-modules-scope@3.2.1(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + postcss-selector-parser: 7.1.1 + + postcss-modules-values@3.0.0: + dependencies: + icss-utils: 4.1.1 + postcss: 7.0.39 + + postcss-modules-values@4.0.0(postcss@8.4.49): + dependencies: + icss-utils: 5.1.0(postcss@8.4.49) + postcss: 8.4.49 + + postcss-modules-values@4.0.0(postcss@8.5.12): + dependencies: + icss-utils: 5.1.0(postcss@8.5.12) + postcss: 8.5.12 + + postcss-modules@6.0.1(postcss@8.5.12): + dependencies: + generic-names: 4.0.0 + icss-utils: 5.1.0(postcss@8.5.12) + lodash.camelcase: 4.3.0 + postcss: 8.5.12 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.12) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.12) + postcss-modules-scope: 3.2.1(postcss@8.5.12) + postcss-modules-values: 4.0.0(postcss@8.5.12) + string-hash: 1.1.3 + + postcss-normalize-charset@5.1.0(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + + postcss-normalize-display-values@5.1.0(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-normalize-positions@5.1.1(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-normalize-repeat-style@5.1.1(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-normalize-string@5.1.0(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-normalize-timing-functions@5.1.0(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-normalize-unicode@5.1.1(postcss@8.5.12): + dependencies: + browserslist: 4.28.2 + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-normalize-url@5.1.0(postcss@8.5.12): + dependencies: + normalize-url: 6.1.0 + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-normalize-whitespace@5.1.1(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-ordered-values@5.1.3(postcss@8.5.12): + dependencies: + cssnano-utils: 3.1.0(postcss@8.5.12) + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-reduce-initial@5.1.2(postcss@8.5.12): + dependencies: + browserslist: 4.28.2 + caniuse-api: 3.0.0 + postcss: 8.5.12 + + postcss-reduce-transforms@5.1.0(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@7.1.1: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-svgo@5.1.0(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + postcss-value-parser: 4.2.0 + svgo: 2.8.2 + + postcss-unique-selectors@5.1.1(postcss@8.5.12): + dependencies: + postcss: 8.5.12 + postcss-selector-parser: 6.1.2 + + postcss-value-parser@4.2.0: {} + + postcss@7.0.39: + dependencies: + picocolors: 0.2.1 + source-map: 0.6.1 + + postcss@8.4.49: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.12: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.1.2 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.89.0 + pump: 3.0.4 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.4 + tunnel-agent: 0.6.0 + optional: true + + prelude-ls@1.2.1: {} + + prettier@2.3.0: {} + + prettier@3.8.1: {} + + pretty-error@2.1.2: + dependencies: + lodash: 4.18.1 + renderkid: 2.0.7 + + pretty-error@4.0.0: + dependencies: + lodash: 4.18.1 + renderkid: 3.0.0 + + pretty-format@25.5.0: + dependencies: + '@jest/types': 25.5.0 + ansi-regex: 5.0.1 + ansi-styles: 4.3.0 + react-is: 16.13.1 + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + pretty-format@30.3.0: + dependencies: + '@jest/schemas': 30.0.5 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + pretty-hrtime@1.0.3: {} + + prism-react-renderer@2.4.1(react@19.2.4): + dependencies: + '@types/prismjs': 1.26.6 + clsx: 2.1.1 + react: 19.2.4 + + prismjs@1.27.0: {} + + prismjs@1.30.0: {} + + private@0.1.8: {} + + process-nextick-args@2.0.1: {} + + process-warning@1.0.0: {} + + process@0.11.10: {} + + progress@2.0.3: {} + + promise-inflight@1.0.1: {} + + promise-retry@2.0.1: + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + + promise.allsettled@1.0.7: + dependencies: + array.prototype.map: 1.0.8 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + get-intrinsic: 1.3.0 + iterate-value: 1.0.2 + + promise.prototype.finally@3.1.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + set-function-name: 2.0.2 + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + property-information@5.6.0: + dependencies: + xtend: 4.0.2 + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + proxy-from-env@1.1.0: {} + + prr@1.0.1: {} + + pseudolocale@1.1.0: + dependencies: + commander: 14.0.3 + + public-encrypt@4.0.3: + dependencies: + bn.js: 4.12.3 + browserify-rsa: 4.1.1 + create-hash: 1.2.0 + parse-asn1: 5.1.9 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + pump@2.0.1: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + pumpify@1.5.1: + dependencies: + duplexify: 3.7.1 + inherits: 2.0.4 + pump: 2.0.1 + + punycode.js@2.3.1: {} + + punycode@1.3.2: {} + + punycode@1.4.1: {} + + punycode@2.3.1: {} + + pupa@2.1.1: + dependencies: + escape-goat: 2.1.1 + + puppeteer-core@2.1.1: + dependencies: + '@types/mime-types': 2.1.4 + debug: 4.4.3(supports-color@8.1.1) + extract-zip: 1.7.0 + https-proxy-agent: 4.0.0 + mime: 2.6.0 + mime-types: 2.1.35 + progress: 2.0.3 + proxy-from-env: 1.1.0 + rimraf: 2.7.1 + ws: 6.2.3 + transitivePeerDependencies: + - supports-color + + pure-rand@6.1.0: {} + + pure-rand@7.0.1: {} + + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.1.5: {} + + q@1.5.1: {} + + qs@6.13.0: + dependencies: + side-channel: 1.1.0 + + qs@6.14.2: + dependencies: + side-channel: 1.1.0 + + qs@6.15.0: + dependencies: + side-channel: 1.1.0 + + querystring-es3@0.2.1: {} + + querystring@0.2.0: {} + + queue-microtask@1.2.3: {} + + quick-format-unescaped@4.0.4: {} + + quick-lru@5.1.1: {} + + ramda@0.27.2: {} + + ramda@0.28.0: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + randomfill@1.0.4: + dependencies: + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + range-parser@1.2.1: {} + + raw-body@2.5.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + raw-body@2.5.3: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + unpipe: 1.0.0 + + raw-loader@4.0.2(webpack@4.47.0): + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 4.47.0 + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + react-colorful@5.6.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2): + dependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + + react-docgen-typescript@2.4.0(typescript@5.8.2): + dependencies: + typescript: 5.8.2 + + react-docgen@5.4.3: + dependencies: + '@babel/core': 7.20.12 + '@babel/generator': 7.29.1 + '@babel/runtime': 7.29.2 + ast-types: 0.14.2 + commander: 2.20.3 + doctrine: 3.0.0 + estree-to-babel: 3.2.1 + neo-async: 2.6.2 + node-dir: 0.1.17 + strip-indent: 3.0.0 + transitivePeerDependencies: + - supports-color + + react-docgen@7.1.1: + dependencies: + '@babel/core': 7.20.12 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + '@types/doctrine': 0.0.9 + '@types/resolve': 1.20.2 + doctrine: 3.0.0 + resolve: 1.22.11 + strip-indent: 4.1.1 + transitivePeerDependencies: + - supports-color + + react-dom@17.0.2(react@17.0.2): + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react: 17.0.2 + scheduler: 0.20.2 + + react-dom@19.2.4(react@19.2.4): + dependencies: + react: 19.2.4 + scheduler: 0.27.0 + + react-draggable@4.5.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2): + dependencies: + clsx: 2.1.1 + prop-types: 15.8.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + + react-element-to-jsx-string@14.3.4(react-dom@17.0.2(react@17.0.2))(react@17.0.2): + dependencies: + '@base2/pretty-print-object': 1.0.1 + is-plain-object: 5.0.0 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-is: 17.0.2 + + react-fast-compare@3.2.2: {} + + react-helmet-async@1.3.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2): + dependencies: + '@babel/runtime': 7.29.2 + invariant: 2.2.4 + prop-types: 15.8.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-fast-compare: 3.2.2 + shallowequal: 1.1.0 + + react-hook-form@7.69.0(react@19.2.4): + dependencies: + react: 19.2.4 + + react-inspector@5.1.1(react@17.0.2): + dependencies: + '@babel/runtime': 7.29.2 + is-dom: 1.1.0 + prop-types: 15.8.1 + react: 17.0.2 + + react-is@16.13.1: {} + + react-is@17.0.2: {} + + react-is@18.3.1: {} + + react-popper-tooltip@3.1.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2): + dependencies: + '@babel/runtime': 7.29.2 + '@popperjs/core': 2.11.8 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-popper: 2.3.0(@popperjs/core@2.11.8)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + + react-popper@2.3.0(@popperjs/core@2.11.8)(react-dom@17.0.2(react@17.0.2))(react@17.0.2): + dependencies: + '@popperjs/core': 2.11.8 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-fast-compare: 3.2.2 + warning: 4.0.3 + + react-redux@9.2.0(@types/react@19.2.7)(react@19.2.4)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 19.2.4 + use-sync-external-store: 1.6.0(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.7 + redux: 5.0.1 + + react-refresh@0.11.0: {} + + react-router-dom@6.30.3(@types/react@17.0.74)(react-dom@17.0.2(react@17.0.2))(react@17.0.2): + dependencies: + '@remix-run/router': 1.23.2 + '@types/react': 17.0.74 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-router: 6.30.3(@types/react@17.0.74)(react@17.0.2) + + react-router@6.30.3(@types/react@17.0.74)(react@17.0.2): + dependencies: + '@remix-run/router': 1.23.2 + '@types/react': 17.0.74 + react: 17.0.2 + + react-sizeme@3.0.2: + dependencies: + element-resize-detector: 1.2.4 + invariant: 2.2.4 + shallowequal: 1.1.0 + throttle-debounce: 3.0.1 + + react-syntax-highlighter@13.5.3(react@17.0.2): + dependencies: + '@babel/runtime': 7.29.2 + highlight.js: 10.7.3 + lowlight: 1.20.0 + prismjs: 1.30.0 + react: 17.0.2 + refractor: 3.6.0 + + react-textarea-autosize@8.5.9(@types/react@17.0.74)(react@17.0.2): + dependencies: + '@babel/runtime': 7.29.2 + react: 17.0.2 + use-composed-ref: 1.4.0(@types/react@17.0.74)(react@17.0.2) + use-latest: 1.3.0(@types/react@17.0.74)(react@17.0.2) + transitivePeerDependencies: + - '@types/react' + + react@17.0.2: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + + react@19.2.4: {} + + read-package-json@2.1.2: + dependencies: + glob: 7.2.3 + json-parse-even-better-errors: 2.3.1 + normalize-package-data: 2.5.0 + npm-normalize-package-bin: 1.0.1 + + read-package-tree@5.1.6: + dependencies: + debuglog: 1.0.1 + dezalgo: 1.0.4 + once: 1.4.0 + read-package-json: 2.1.2 + readdir-scoped-modules: 1.1.0 + + read-pkg-up@7.0.1: + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + + read-pkg@5.2.0: + dependencies: + '@types/normalize-package-data': 2.4.4 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + + read-yaml-file@2.1.0: + dependencies: + js-yaml: 4.1.1 + strip-bom: 4.0.0 + + read@1.0.7: + dependencies: + mute-stream: 0.0.8 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.9 + + readdir-scoped-modules@1.1.0: + dependencies: + debuglog: 1.0.1 + dezalgo: 1.0.4 + graceful-fs: 4.2.11 + once: 1.4.0 + + readdirp@2.2.1: + dependencies: + graceful-fs: 4.2.11 + micromatch: 3.1.10 + readable-stream: 2.3.8 + optional: true + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.2 + + recast@0.19.1: + dependencies: + ast-types: 0.13.3 + esprima: 4.0.1 + private: 0.1.8 + source-map: 0.6.1 + + recast@0.20.5: + dependencies: + ast-types: 0.14.2 + esprima: 4.0.1 + source-map: 0.6.1 + tslib: 2.8.1 + + recast@0.23.11: + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + + rechoir@0.6.2: + dependencies: + resolve: 1.22.11 + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@4.2.1: + dependencies: + '@babel/runtime': 7.29.2 + + redux@5.0.1: {} + + reflect-metadata@0.2.2: {} + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + refractor@3.6.0: + dependencies: + hastscript: 6.0.0 + parse-entities: 2.0.0 + prismjs: 1.27.0 + + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regenerator-runtime@0.13.11: {} + + regex-not@1.0.2: + dependencies: + extend-shallow: 3.0.2 + safe-regex: 1.1.0 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + regexpp@3.2.0: {} + + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.0 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + registry-auth-token@4.2.2: + dependencies: + rc: 1.2.8 + + registry-url@5.1.0: + dependencies: + rc: 1.2.8 + + regjsgen@0.8.0: {} + + regjsparser@0.13.0: + dependencies: + jsesc: 3.1.0 + + relateurl@0.2.7: {} + + remark-external-links@8.0.0: + dependencies: + extend: 3.0.2 + is-absolute-url: 3.0.3 + mdast-util-definitions: 4.0.0 + space-separated-tokens: 1.1.5 + unist-util-visit: 2.0.3 + + remark-footnotes@2.0.0: {} + + remark-mdx@1.6.22: + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.10.4 + '@babel/plugin-proposal-object-rest-spread': 7.12.1(@babel/core@7.12.9) + '@babel/plugin-syntax-jsx': 7.12.1(@babel/core@7.12.9) + '@mdx-js/util': 1.6.22 + is-alphabetical: 1.0.4 + remark-parse: 8.0.3 + unified: 9.2.0 + transitivePeerDependencies: + - supports-color + + remark-parse@8.0.3: + dependencies: + ccount: 1.1.0 + collapse-white-space: 1.0.6 + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + is-whitespace-character: 1.0.4 + is-word-character: 1.0.4 + markdown-escapes: 1.0.4 + parse-entities: 2.0.0 + repeat-string: 1.6.1 + state-toggle: 1.0.3 + trim: 0.0.1 + trim-trailing-lines: 1.1.4 + unherit: 1.1.3 + unist-util-remove-position: 2.0.1 + vfile-location: 3.2.0 + xtend: 4.0.2 + + remark-slug@6.1.0: + dependencies: + github-slugger: 1.5.0 + mdast-util-to-string: 1.1.0 + unist-util-visit: 2.0.3 + + remark-squeeze-paragraphs@4.0.0: + dependencies: + mdast-squeeze-paragraphs: 4.0.0 + + remeda@0.0.32: {} + + remove-trailing-separator@1.1.0: {} + + renderkid@2.0.7: + dependencies: + css-select: 4.3.0 + dom-converter: 0.2.0 + htmlparser2: 6.1.0 + lodash: 4.18.1 + strip-ansi: 3.0.1 + + renderkid@3.0.0: + dependencies: + css-select: 4.3.0 + dom-converter: 0.2.0 + htmlparser2: 6.1.0 + lodash: 4.18.1 + strip-ansi: 6.0.1 + + repeat-element@1.1.4: {} + + repeat-string@1.6.1: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + require-main-filename@2.0.0: {} + + requires-port@1.0.0: {} + + reselect@5.1.1: {} + + resolve-alpn@1.2.1: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-url@0.2.1: {} + + resolve.exports@2.0.3: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.6: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.1 + node-exports-info: 1.6.0 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + responselike@2.0.1: + dependencies: + lowercase-keys: 2.0.0 + + ret@0.1.15: {} + + ret@0.2.2: {} + + retry@0.12.0: {} + + retry@0.13.1: {} + + reusify@1.1.0: {} + + rfc4648@1.5.4: {} + + rfdc@1.4.1: {} + + rimraf@2.6.3: + dependencies: + glob: 7.2.3 + + rimraf@2.7.1: + dependencies: + glob: 7.2.3 + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + ripemd160@2.0.3: + dependencies: + hash-base: 3.1.2 + inherits: 2.0.4 + + router@2.2.0: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color + + rrweb-cssom@0.8.0: {} + + rsvp@4.8.5: {} + + rtl-css-js@1.16.1: + dependencies: + '@babel/runtime': 7.29.2 + + run-applescript@7.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + run-queue@1.0.3: + dependencies: + aproba: 1.2.0 + + rxjs@6.6.7: + dependencies: + tslib: 1.14.1 + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-execa@0.1.2: + dependencies: + '@zkochan/which': 2.0.3 + execa: 5.1.1 + path-name: 1.0.0 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safe-regex2@2.0.0: + dependencies: + ret: 0.2.2 + + safe-regex@1.1.0: + dependencies: + ret: 0.1.15 + + safer-buffer@2.1.2: {} + + sane@4.1.0: + dependencies: + '@cnakazawa/watch': 1.0.4 + anymatch: 2.0.0 + capture-exit: 2.0.0 + exec-sh: 0.3.6 + execa: 1.0.0 + fb-watchman: 2.0.2 + micromatch: 3.1.10 + minimist: 1.2.8 + walker: 1.0.8 + + sass-embedded-android-arm64@1.85.1: + optional: true + + sass-embedded-android-arm@1.85.1: + optional: true + + sass-embedded-android-ia32@1.85.1: + optional: true + + sass-embedded-android-riscv64@1.85.1: + optional: true + + sass-embedded-android-x64@1.85.1: + optional: true + + sass-embedded-darwin-arm64@1.85.1: + optional: true + + sass-embedded-darwin-x64@1.85.1: + optional: true + + sass-embedded-linux-arm64@1.85.1: + optional: true + + sass-embedded-linux-arm@1.85.1: + optional: true + + sass-embedded-linux-ia32@1.85.1: + optional: true + + sass-embedded-linux-musl-arm64@1.85.1: + optional: true + + sass-embedded-linux-musl-arm@1.85.1: + optional: true + + sass-embedded-linux-musl-ia32@1.85.1: + optional: true + + sass-embedded-linux-musl-riscv64@1.85.1: + optional: true + + sass-embedded-linux-musl-x64@1.85.1: + optional: true + + sass-embedded-linux-riscv64@1.85.1: + optional: true + + sass-embedded-linux-x64@1.85.1: + optional: true + + sass-embedded-win32-arm64@1.85.1: + optional: true + + sass-embedded-win32-ia32@1.85.1: + optional: true + + sass-embedded-win32-x64@1.85.1: + optional: true + + sass-embedded@1.85.1: + dependencies: + '@bufbuild/protobuf': 2.11.0 + buffer-builder: 0.2.0 + colorjs.io: 0.5.2 + immutable: 5.1.5 + rxjs: 7.8.2 + source-map-js: 1.2.1 + supports-color: 8.1.1 + sync-child-process: 1.0.2 + varint: 6.0.0 + optionalDependencies: + sass-embedded-android-arm: 1.85.1 + sass-embedded-android-arm64: 1.85.1 + sass-embedded-android-ia32: 1.85.1 + sass-embedded-android-riscv64: 1.85.1 + sass-embedded-android-x64: 1.85.1 + sass-embedded-darwin-arm64: 1.85.1 + sass-embedded-darwin-x64: 1.85.1 + sass-embedded-linux-arm: 1.85.1 + sass-embedded-linux-arm64: 1.85.1 + sass-embedded-linux-ia32: 1.85.1 + sass-embedded-linux-musl-arm: 1.85.1 + sass-embedded-linux-musl-arm64: 1.85.1 + sass-embedded-linux-musl-ia32: 1.85.1 + sass-embedded-linux-musl-riscv64: 1.85.1 + sass-embedded-linux-musl-x64: 1.85.1 + sass-embedded-linux-riscv64: 1.85.1 + sass-embedded-linux-x64: 1.85.1 + sass-embedded-win32-arm64: 1.85.1 + sass-embedded-win32-ia32: 1.85.1 + sass-embedded-win32-x64: 1.85.1 + + sass-loader@12.4.0(sass@1.49.11)(webpack@5.105.4): + dependencies: + klona: 2.0.6 + neo-async: 2.6.2 + webpack: 5.105.4 + optionalDependencies: + sass: 1.49.11 + + sass@1.49.11: + dependencies: + chokidar: 3.6.0 + immutable: 4.3.8 + source-map-js: 1.2.1 + + sax@1.2.1: {} + + sax@1.6.0: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scheduler@0.20.2: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + + scheduler@0.27.0: {} + + schema-utils@1.0.0: + dependencies: + ajv: 6.14.0 + ajv-errors: 1.0.1(ajv@6.14.0) + ajv-keywords: 3.5.2(ajv@6.14.0) + + schema-utils@2.7.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.14.0 + ajv-keywords: 3.5.2(ajv@6.14.0) + + schema-utils@2.7.1: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.14.0 + ajv-keywords: 3.5.2(ajv@6.14.0) + + schema-utils@3.3.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.14.0 + ajv-keywords: 3.5.2(ajv@6.14.0) + + schema-utils@4.3.3: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.20.0 + ajv-formats: 2.1.1 + ajv-keywords: 5.1.0(ajv@8.20.0) + + secure-json-parse@2.7.0: {} + + select-hose@2.0.0: {} + + selfsigned@2.4.1: + dependencies: + '@types/node-forge': 1.3.14 + node-forge: 1.4.0 + + selfsigned@5.5.0: + dependencies: + '@peculiar/x509': 1.14.3 + pkijs: 3.4.0 + + semver-diff@3.1.1: + dependencies: + semver: 6.3.1 + + semver-store@0.3.0: {} + + semver@5.7.2: {} + + semver@6.3.1: {} + + semver@7.5.4: + dependencies: + lru-cache: 6.0.0 + + semver@7.7.4: {} + + send@0.17.2: + dependencies: + debug: 2.6.9 + depd: 1.1.2 + destroy: 1.0.4 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 1.8.1 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.3.0 + range-parser: 1.2.1 + statuses: 1.5.0 + + send@0.19.0: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + + send@0.19.2: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.1 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + + send@1.2.1: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serialize-javascript@4.0.0: + dependencies: + randombytes: 2.1.0 + + serialize-javascript@5.0.1: + dependencies: + randombytes: 2.1.0 + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + serialize-javascript@7.0.5: {} + + serve-favicon@2.5.1: + dependencies: + etag: 1.8.1 + fresh: 0.5.2 + ms: 2.1.3 + parseurl: 1.3.3 + safe-buffer: 5.2.1 + + serve-index@1.9.2: + dependencies: + accepts: 1.3.8 + batch: 0.6.1 + debug: 2.6.9 + escape-html: 1.0.3 + http-errors: 1.8.1 + mime-types: 2.1.35 + parseurl: 1.3.3 + + serve-static@1.16.2: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.0 + + serve-static@1.16.3: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.2 + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + + set-blocking@2.0.0: {} + + set-cookie-parser@2.7.2: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-immediate-shim@1.0.1: {} + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + set-value@2.0.1: + dependencies: + extend-shallow: 2.0.1 + is-extendable: 0.1.1 + is-plain-object: 2.0.4 + split-string: 3.1.0 + + setimmediate@1.0.5: {} + + setprototypeof@1.2.0: {} + + sha.js@2.4.12: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + shallow-clone@3.0.1: + dependencies: + kind-of: 6.0.3 + + shallowequal@1.1.0: {} + + shebang-command@1.2.0: + dependencies: + shebang-regex: 1.0.0 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@1.0.0: {} + + shebang-regex@3.0.0: {} + + shell-quote@1.8.3: {} + + shelljs@0.8.5: + dependencies: + glob: 7.0.6 + interpret: 1.4.0 + rechoir: 0.6.2 + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + simple-concat@1.0.1: + optional: true + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + optional: true + + sirv@1.0.19: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 1.0.1 + totalist: 1.1.0 + + sisteransi@1.0.5: {} + + slash@2.0.0: {} + + slash@3.0.0: {} + + slash@5.1.0: {} + + slice-ansi@2.1.0: + dependencies: + ansi-styles: 3.2.1 + astral-regex: 1.0.0 + is-fullwidth-code-point: 2.0.0 + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + smart-buffer@4.2.0: {} + + snapdragon-node@2.1.1: + dependencies: + define-property: 1.0.0 + isobject: 3.0.1 + snapdragon-util: 3.0.1 + + snapdragon-util@3.0.1: + dependencies: + kind-of: 3.2.2 + + snapdragon@0.8.2: + dependencies: + base: 0.11.2 + debug: 2.6.9 + define-property: 0.2.5 + extend-shallow: 2.0.1 + map-cache: 0.2.2 + source-map: 0.5.7 + source-map-resolve: 0.5.3 + use: 3.1.1 + + sockjs@0.3.24: + dependencies: + faye-websocket: 0.11.4 + uuid: 8.3.2 + websocket-driver: 0.7.4 + + socks-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3(supports-color@8.1.1) + socks: 2.8.7 + transitivePeerDependencies: + - supports-color + + socks@2.8.7: + dependencies: + ip-address: 10.1.0 + smart-buffer: 4.2.0 + + sonic-boom@1.4.1: + dependencies: + atomic-sleep: 1.0.0 + flatstr: 1.0.12 + + sort-keys@4.2.0: + dependencies: + is-plain-obj: 2.1.0 + + source-list-map@2.0.1: {} + + source-map-js@1.2.1: {} + + source-map-loader@1.1.3(webpack@4.47.0): + dependencies: + abab: 2.0.6 + iconv-lite: 0.6.3 + loader-utils: 2.0.4 + schema-utils: 3.3.0 + source-map: 0.6.1 + webpack: 4.47.0 + whatwg-mimetype: 2.3.0 + + source-map-loader@1.1.3(webpack@5.105.4): + dependencies: + abab: 2.0.6 + iconv-lite: 0.6.3 + loader-utils: 2.0.4 + schema-utils: 3.3.0 + source-map: 0.6.1 + webpack: 5.105.4 + whatwg-mimetype: 2.3.0 + + source-map-loader@3.0.2(webpack@5.105.4): + dependencies: + abab: 2.0.6 + iconv-lite: 0.6.3 + source-map-js: 1.2.1 + webpack: 5.105.4 + + source-map-resolve@0.5.3: + dependencies: + atob: 2.1.2 + decode-uri-component: 0.2.2 + resolve-url: 0.2.1 + source-map-url: 0.4.1 + urix: 0.1.0 + + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-url@0.4.1: {} + + source-map@0.5.7: {} + + source-map@0.6.1: {} + + source-map@0.7.6: {} + + space-separated-tokens@1.1.5: {} + + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.23 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.23 + + spdx-expression-parse@4.0.0: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.23 + + spdx-license-ids@3.0.23: {} + + spdy-transport@3.0.0: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + detect-node: 2.1.0 + hpack.js: 2.1.6 + obuf: 1.1.2 + readable-stream: 3.6.2 + wbuf: 1.7.3 + transitivePeerDependencies: + - supports-color + + spdy@4.0.2: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + handle-thing: 2.0.1 + http-deceiver: 1.2.7 + select-hose: 2.0.0 + spdy-transport: 3.0.0 + transitivePeerDependencies: + - supports-color + + split-string@3.1.0: + dependencies: + extend-shallow: 3.0.2 + + split2@3.2.2: + dependencies: + readable-stream: 3.6.2 + + split2@4.2.0: {} + + sprintf-js@1.0.3: {} + + ssri@10.0.5: + dependencies: + minipass: 7.1.3 + + ssri@6.0.2: + dependencies: + figgy-pudding: 3.5.2 + + ssri@8.0.1: + dependencies: + minipass: 3.3.6 + + stable@0.1.8: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + stackframe@1.3.4: {} + + state-toggle@1.0.3: {} + + static-extend@0.1.2: + dependencies: + define-property: 0.2.5 + object-copy: 0.1.0 + + statuses@1.5.0: {} + + statuses@2.0.1: {} + + statuses@2.0.2: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + store2@2.14.4: {} + + storybook@9.1.20(@testing-library/dom@7.21.8)(prettier@3.8.1): + dependencies: + '@storybook/global': 5.0.0 + '@testing-library/jest-dom': 6.9.1 + '@testing-library/user-event': 14.6.1(@testing-library/dom@7.21.8) + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4 + '@vitest/spy': 3.2.4 + better-opn: 3.0.2 + esbuild: 0.25.12 + esbuild-register: 3.6.0(esbuild@0.25.12) + recast: 0.23.11 + semver: 7.7.4 + ws: 8.21.0 + optionalDependencies: + prettier: 3.8.1 + transitivePeerDependencies: + - '@testing-library/dom' + - bufferutil + - msw + - supports-color + - utf-8-validate + - vite + + stream-browserify@2.0.2: + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.8 + + stream-each@1.2.3: + dependencies: + end-of-stream: 1.4.5 + stream-shift: 1.0.3 + + stream-http@2.8.3: + dependencies: + builtin-status-codes: 3.0.0 + inherits: 2.0.4 + readable-stream: 2.3.8 + to-arraybuffer: 1.0.1 + xtend: 4.0.2 + + stream-shift@1.0.3: {} + + streamroller@3.1.5: + dependencies: + date-format: 4.0.14 + debug: 4.4.3(supports-color@8.1.1) + fs-extra: 8.1.0 + transitivePeerDependencies: + - supports-color + + strict-uri-encode@2.0.0: {} + + string-argv@0.3.2: {} + + string-hash@1.1.3: {} + + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-similarity@4.0.4: {} + + string-width@1.0.2: + dependencies: + code-point-at: 1.1.0 + is-fullwidth-code-point: 1.0.0 + strip-ansi: 3.0.1 + + string-width@3.1.0: + dependencies: + emoji-regex: 7.0.3 + is-fullwidth-code-point: 2.0.0 + strip-ansi: 5.2.0 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.padend@3.1.6: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + + string.prototype.padstart@3.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.1 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@3.0.1: + dependencies: + ansi-regex: 2.1.1 + + strip-ansi@5.2.0: + dependencies: + ansi-regex: 4.1.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + strip-bom@3.0.0: {} + + strip-bom@4.0.0: {} + + strip-eof@1.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-indent@4.1.1: {} + + strip-json-comments@2.0.1: {} + + strip-json-comments@3.1.1: {} + + strnum@2.2.2: {} + + style-loader@1.3.0(webpack@4.47.0): + dependencies: + loader-utils: 2.0.4 + schema-utils: 2.7.1 + webpack: 4.47.0 + + style-loader@2.0.0(webpack@4.47.0): + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 4.47.0 + + style-loader@2.0.0(webpack@5.105.4): + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 5.105.4 + + style-loader@3.3.4(webpack@5.105.4): + dependencies: + webpack: 5.105.4 + + style-to-object@0.3.0: + dependencies: + inline-style-parser: 0.1.1 + + stylehacks@5.1.1(postcss@8.5.12): + dependencies: + browserslist: 4.28.2 + postcss: 8.5.12 + postcss-selector-parser: 6.1.2 + + stylis@4.3.6: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svgo@2.8.2: + dependencies: + commander: 7.2.0 + css-select: 4.3.0 + css-tree: 1.1.3 + csso: 4.2.0 + picocolors: 1.1.1 + sax: 1.6.0 + stable: 0.1.8 + + symbol-tree@3.2.4: {} + + symbol.prototype.description@1.0.7: + dependencies: + call-bind: 1.0.8 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-symbol-description: 1.1.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + object.getownpropertydescriptors: 2.1.9 + + sync-child-process@1.0.2: + dependencies: + sync-message-port: 1.2.0 + + sync-message-port@1.2.0: {} + + synchronous-promise@2.0.17: {} + + synckit@0.11.12: + dependencies: + '@pkgr/core': 0.2.9 + + table@5.4.6: + dependencies: + ajv: 6.14.0 + lodash: 4.18.1 + slice-ansi: 2.1.0 + string-width: 3.1.0 + + table@6.9.0: + dependencies: + ajv: 8.20.0 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + tabster@8.7.0: + dependencies: + keyborg: 2.6.0 + tslib: 2.8.1 + optionalDependencies: + '@rollup/rollup-linux-x64-gnu': 4.53.3 + + tapable@1.1.3: {} + + tapable@2.2.1: {} + + tapable@2.3.0: {} + + tar-fs@2.1.4: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.4 + tar-stream: 2.2.0 + optional: true + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + tar@7.5.13: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + + telejson@5.3.3: + dependencies: + '@types/is-function': 1.0.3 + global: 4.4.0 + is-function: 1.0.2 + is-regex: 1.2.1 + is-symbol: 1.1.1 + isobject: 4.0.0 + lodash: 4.18.1 + memoizerific: 1.11.3 + + temp@0.8.4: + dependencies: + rimraf: 2.6.3 + + terser-webpack-plugin@1.4.6(webpack@4.47.0): + dependencies: + cacache: 12.0.4 + find-cache-dir: 2.1.0 + is-wsl: 1.1.0 + schema-utils: 1.0.0 + serialize-javascript: 4.0.0 + source-map: 0.6.1 + terser: 4.8.1 + webpack: 4.47.0 + webpack-sources: 1.4.3 + worker-farm: 1.7.0 + + terser-webpack-plugin@3.0.8(webpack@4.47.0): + dependencies: + cacache: 15.3.0 + find-cache-dir: 3.3.2 + jest-worker: 26.6.2 + p-limit: 3.1.0 + schema-utils: 2.7.1 + serialize-javascript: 4.0.0 + source-map: 0.6.1 + terser: 4.8.1 + webpack: 4.47.0 + webpack-sources: 1.4.3 + + terser-webpack-plugin@3.0.8(webpack@5.105.4): + dependencies: + cacache: 15.3.0 + find-cache-dir: 3.3.2 + jest-worker: 26.6.2 + p-limit: 3.1.0 + schema-utils: 2.7.1 + serialize-javascript: 4.0.0 + source-map: 0.6.1 + terser: 4.8.1 + webpack: 5.105.4 + webpack-sources: 1.4.3 + + terser-webpack-plugin@4.2.3(webpack@4.47.0): + dependencies: + cacache: 15.3.0 + find-cache-dir: 3.3.2 + jest-worker: 26.6.2 + p-limit: 3.1.0 + schema-utils: 3.3.0 + serialize-javascript: 5.0.1 + source-map: 0.6.1 + terser: 5.46.1 + webpack: 4.47.0 + webpack-sources: 1.4.3 + + terser-webpack-plugin@5.3.17(webpack@5.105.4): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + terser: 5.46.1 + webpack: 5.105.4 + + terser@4.8.1: + dependencies: + commander: 2.20.3 + source-map: 0.6.1 + source-map-support: 0.5.21 + + terser@5.46.1: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.16.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.5 + + text-table@0.2.0: {} + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + thingies@2.6.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + throttle-debounce@3.0.1: {} + + through2@2.0.5: + dependencies: + readable-stream: 2.3.8 + xtend: 4.0.2 + + through2@4.0.2: + dependencies: + readable-stream: 3.6.2 + + thunky@1.1.0: {} + + timers-browserify@2.0.12: + dependencies: + setimmediate: 1.0.5 + + tiny-invariant@1.3.3: {} + + tiny-lru@7.0.6: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + + tldts-core@6.1.86: {} + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 + + tmp@0.2.5: {} + + tmpl@1.0.5: {} + + to-arraybuffer@1.0.1: {} + + to-buffer@1.2.2: + dependencies: + isarray: 2.0.5 + safe-buffer: 5.2.1 + typed-array-buffer: 1.0.3 + + to-object-path@0.3.0: + dependencies: + kind-of: 3.2.2 + + to-regex-range@2.1.1: + dependencies: + is-number: 3.0.0 + repeat-string: 1.6.1 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + to-regex@3.0.2: + dependencies: + define-property: 2.0.2 + extend-shallow: 3.0.2 + regex-not: 1.0.2 + safe-regex: 1.1.0 + + toggle-selection@1.0.6: {} + + toidentifier@1.0.1: {} + + totalist@1.1.0: {} + + tough-cookie@5.1.2: + dependencies: + tldts: 6.1.86 + + tr46@0.0.3: {} + + tr46@5.1.1: + dependencies: + punycode: 2.3.1 + + traverse@0.3.9: {} + + tree-dump@1.1.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + trim-trailing-lines@1.1.4: {} + + trim@0.0.1: {} + + trough@1.0.5: {} + + true-case-path@2.2.1: {} + + ts-api-utils@1.4.3(typescript@5.8.2): + dependencies: + typescript: 5.8.2 + + ts-api-utils@2.5.0(typescript@4.9.5): + dependencies: + typescript: 4.9.5 + + ts-api-utils@2.5.0(typescript@5.8.2): + dependencies: + typescript: 5.8.2 + + ts-dedent@2.2.0: {} + + ts-loader@6.0.0(typescript@5.8.2): + dependencies: + chalk: 2.4.2 + enhanced-resolve: 4.5.0 + loader-utils: 1.4.2 + micromatch: 4.0.8 + semver: 6.3.1 + typescript: 5.8.2 + + ts-pnp@1.2.0(typescript@5.8.2): + optionalDependencies: + typescript: 5.8.2 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@1.14.1: {} + + tslib@2.4.0: {} + + tslib@2.8.1: {} + + tslint@5.20.1(typescript@2.9.2): + dependencies: + '@babel/code-frame': 7.29.0 + builtin-modules: 1.1.1 + chalk: 2.4.2 + commander: 2.20.3 + diff: 4.0.4 + glob: 7.2.3 + js-yaml: 3.14.2 + minimatch: 3.1.5 + mkdirp: 0.5.6 + resolve: 1.22.11 + semver: 5.7.2 + tslib: 1.14.1 + tsutils: 2.29.0(typescript@2.9.2) + typescript: 2.9.2 + + tslint@5.20.1(typescript@3.9.10): + dependencies: + '@babel/code-frame': 7.29.0 + builtin-modules: 1.1.1 + chalk: 2.4.2 + commander: 2.20.3 + diff: 4.0.4 + glob: 7.2.3 + js-yaml: 3.14.2 + minimatch: 3.1.5 + mkdirp: 0.5.6 + resolve: 1.22.11 + semver: 5.7.2 + tslib: 1.14.1 + tsutils: 2.29.0(typescript@3.9.10) + typescript: 3.9.10 + + tslint@5.20.1(typescript@4.9.5): + dependencies: + '@babel/code-frame': 7.29.0 + builtin-modules: 1.1.1 + chalk: 2.4.2 + commander: 2.20.3 + diff: 4.0.4 + glob: 7.2.3 + js-yaml: 3.14.2 + minimatch: 3.1.5 + mkdirp: 0.5.6 + resolve: 1.22.11 + semver: 5.7.2 + tslib: 1.14.1 + tsutils: 2.29.0(typescript@4.9.5) + typescript: 4.9.5 + + tslint@5.20.1(typescript@5.8.2): + dependencies: + '@babel/code-frame': 7.29.0 + builtin-modules: 1.1.1 + chalk: 2.4.2 + commander: 2.20.3 + diff: 4.0.4 + glob: 7.2.3 + js-yaml: 3.14.2 + minimatch: 3.1.5 + mkdirp: 0.5.6 + resolve: 1.22.11 + semver: 5.7.2 + tslib: 1.14.1 + tsutils: 2.29.0(typescript@5.8.2) + typescript: 5.8.2 + + tsutils@2.29.0(typescript@2.9.2): + dependencies: + tslib: 1.14.1 + typescript: 2.9.2 + + tsutils@2.29.0(typescript@3.9.10): + dependencies: + tslib: 1.14.1 + typescript: 3.9.10 + + tsutils@2.29.0(typescript@4.9.5): + dependencies: + tslib: 1.14.1 + typescript: 4.9.5 + + tsutils@2.29.0(typescript@5.8.2): + dependencies: + tslib: 1.14.1 + typescript: 5.8.2 + + tsyringe@4.10.0: + dependencies: + tslib: 1.14.1 + + tty-browserify@0.0.0: {} + + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + optional: true + + tunnel@0.0.6: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-detect@4.0.8: {} + + type-fest@0.20.2: {} + + type-fest@0.21.3: {} + + type-fest@0.6.0: {} + + type-fest@0.8.1: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.2 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typed-rest-client@1.8.11: + dependencies: + qs: 6.15.0 + tunnel: 0.0.6 + underscore: 1.13.8 + + typedarray-to-buffer@3.1.5: + dependencies: + is-typedarray: 1.0.0 + + typedarray@0.0.6: {} + + typescript@2.9.2: {} + + typescript@3.9.10: {} + + typescript@4.9.5: {} + + typescript@5.8.2: {} + + typescript@5.9.3: {} + + uc.micro@2.1.0: {} + + ufo@1.6.3: {} + + uglify-js@3.19.3: + optional: true + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + underscore@1.13.8: {} + + undici-types@6.19.8: {} + + unfetch@4.2.0: {} + + unherit@1.1.3: + dependencies: + inherits: 2.0.4 + xtend: 4.0.2 + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + + unicorn-magic@0.1.0: {} + + unicorn-magic@0.3.0: {} + + unified@9.2.0: + dependencies: + bail: 1.0.5 + extend: 3.0.2 + is-buffer: 2.0.5 + is-plain-obj: 2.1.0 + trough: 1.0.5 + vfile: 4.2.1 + + union-value@1.0.1: + dependencies: + arr-union: 3.1.0 + get-value: 2.0.6 + is-extendable: 0.1.1 + set-value: 2.0.1 + + unique-filename@1.1.1: + dependencies: + unique-slug: 2.0.2 + + unique-slug@2.0.2: + dependencies: + imurmurhash: 0.1.4 + + unique-string@2.0.0: + dependencies: + crypto-random-string: 2.0.0 + + unist-builder@2.0.3: {} + + unist-util-generated@1.1.6: {} + + unist-util-is@4.1.0: {} + + unist-util-position@3.1.0: {} + + unist-util-remove-position@2.0.1: + dependencies: + unist-util-visit: 2.0.3 + + unist-util-remove@2.1.0: + dependencies: + unist-util-is: 4.1.0 + + unist-util-stringify-position@2.0.3: + dependencies: + '@types/unist': 2.0.11 + + unist-util-visit-parents@3.1.1: + dependencies: + '@types/unist': 2.0.11 + unist-util-is: 4.1.0 + + unist-util-visit@2.0.3: + dependencies: + '@types/unist': 2.0.11 + unist-util-is: 4.1.0 + unist-util-visit-parents: 3.1.1 + + universalify@0.1.2: {} + + universalify@2.0.1: {} + + unpipe@1.0.0: {} + + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.11.1 + '@unrs/resolver-binding-android-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-x64': 1.11.1 + '@unrs/resolver-binding-freebsd-x64': 1.11.1 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 + '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-musl': 1.11.1 + '@unrs/resolver-binding-wasm32-wasi': 1.11.1 + '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 + '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 + '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + + unset-value@1.0.0: + dependencies: + has-value: 0.3.1 + isobject: 3.0.1 + + unzipper@0.10.14: + dependencies: + big-integer: 1.6.52 + binary: 0.3.0 + bluebird: 3.4.7 + buffer-indexof-polyfill: 1.0.2 + duplexer2: 0.1.4 + fstream: 1.0.12 + graceful-fs: 4.2.11 + listenercount: 1.0.1 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + + upath@1.2.0: + optional: true + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + update-notifier@5.1.0: + dependencies: + boxen: 5.1.2 + chalk: 4.1.2 + configstore: 5.0.1 + has-yarn: 2.1.0 + import-lazy: 2.1.0 + is-ci: 2.0.0 + is-installed-globally: 0.4.0 + is-npm: 5.0.0 + is-yarn-global: 0.3.0 + latest-version: 5.1.0 + pupa: 2.1.1 + semver: 7.7.4 + semver-diff: 3.1.1 + xdg-basedir: 4.0.0 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + urix@0.1.0: {} + + url-join@4.0.1: {} + + url-loader@4.1.1(file-loader@6.2.0(webpack@4.47.0))(webpack@4.47.0): + dependencies: + loader-utils: 2.0.4 + mime-types: 2.1.35 + schema-utils: 3.3.0 + webpack: 4.47.0 + optionalDependencies: + file-loader: 6.2.0(webpack@4.47.0) + + url-loader@4.1.1(webpack@5.105.4): + dependencies: + loader-utils: 2.0.4 + mime-types: 2.1.35 + schema-utils: 3.3.0 + webpack: 5.105.4 + + url@0.10.3: + dependencies: + punycode: 1.3.2 + querystring: 0.2.0 + + url@0.11.4: + dependencies: + punycode: 1.4.1 + qs: 6.15.0 + + use-composed-ref@1.4.0(@types/react@17.0.74)(react@17.0.2): + dependencies: + react: 17.0.2 + optionalDependencies: + '@types/react': 17.0.74 + + use-isomorphic-layout-effect@1.2.1(@types/react@17.0.74)(react@17.0.2): + dependencies: + react: 17.0.2 + optionalDependencies: + '@types/react': 17.0.74 + + use-latest@1.3.0(@types/react@17.0.74)(react@17.0.2): + dependencies: + react: 17.0.2 + use-isomorphic-layout-effect: 1.2.1(@types/react@17.0.74)(react@17.0.2) + optionalDependencies: + '@types/react': 17.0.74 + + use-sync-external-store@1.6.0(react@19.2.4): + dependencies: + react: 19.2.4 + + use@3.1.1: {} + + util-deprecate@1.0.2: {} + + util.promisify@1.0.0: + dependencies: + define-properties: 1.2.1 + object.getownpropertydescriptors: 2.1.9 + + util@0.10.4: + dependencies: + inherits: 2.0.3 + + util@0.11.1: + dependencies: + inherits: 2.0.3 + + util@0.12.5: + dependencies: + inherits: 2.0.4 + is-arguments: 1.2.0 + is-generator-function: 1.1.2 + is-typed-array: 1.1.15 + which-typed-array: 1.1.20 + + utila@0.4.0: {} + + utils-merge@1.0.1: {} + + uuid-browser@3.1.0: {} + + uuid@3.4.0: {} + + uuid@8.0.0: {} + + uuid@8.3.2: {} + + v8-compile-cache@2.4.0: {} + + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + validate-npm-package-name@3.0.0: + dependencies: + builtins: 1.0.3 + + validator@13.15.35: {} + + varint@6.0.0: {} + + vary@1.1.2: {} + + vfile-location@3.2.0: {} + + vfile-message@2.0.4: + dependencies: + '@types/unist': 2.0.11 + unist-util-stringify-position: 2.0.3 + + vfile@4.2.1: + dependencies: + '@types/unist': 2.0.11 + is-buffer: 2.0.5 + unist-util-stringify-position: 2.0.3 + vfile-message: 2.0.4 + + vm-browserify@1.1.2: {} + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + + warning@4.0.3: + dependencies: + loose-envify: 1.4.0 + + watchpack-chokidar2@2.0.1: + dependencies: + chokidar: 2.1.8 + optional: true + + watchpack@1.7.5: + dependencies: + graceful-fs: 4.2.11 + neo-async: 2.6.2 + optionalDependencies: + chokidar: 3.6.0 + watchpack-chokidar2: 2.0.1 + + watchpack@2.4.0: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + watchpack@2.5.1: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + wbuf@1.7.3: + dependencies: + minimalistic-assert: 1.0.1 + + web-namespaces@1.1.4: {} + + webidl-conversions@3.0.1: {} + + webidl-conversions@7.0.0: {} + + webpack-bundle-analyzer@4.5.0: + dependencies: + acorn: 8.16.0 + acorn-walk: 8.3.5 + chalk: 4.1.2 + commander: 7.2.0 + gzip-size: 6.0.0 + lodash: 4.18.1 + opener: 1.5.2 + sirv: 1.0.19 + ws: 7.5.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + webpack-dev-middleware@3.7.3(@types/webpack@4.41.32)(webpack@4.47.0): + dependencies: + memory-fs: 0.4.1 + mime: 2.6.0 + mkdirp: 0.5.6 + range-parser: 1.2.1 + webpack: 4.47.0 + webpack-log: 2.0.0 + optionalDependencies: + '@types/webpack': 4.41.32 + + webpack-dev-middleware@5.3.4(@types/webpack@4.41.32)(webpack@4.47.0): + dependencies: + colorette: 2.0.20 + memfs: 3.5.3 + mime-types: 2.1.35 + range-parser: 1.2.1 + schema-utils: 4.3.3 + webpack: 4.47.0 + optionalDependencies: + '@types/webpack': 4.41.32 + + webpack-dev-middleware@6.1.3(@types/webpack@4.41.32)(webpack@5.105.4): + dependencies: + colorette: 2.0.20 + memfs: 3.5.3 + mime-types: 2.1.35 + range-parser: 1.2.1 + schema-utils: 4.3.3 + optionalDependencies: + '@types/webpack': 4.41.32 + webpack: 5.105.4 + + webpack-dev-middleware@7.4.5(@types/webpack@4.41.32)(webpack@4.47.0): + dependencies: + colorette: 2.0.20 + memfs: 4.57.1 + mime-types: 3.0.2 + on-finished: 2.4.1 + range-parser: 1.2.1 + schema-utils: 4.3.3 + optionalDependencies: + '@types/webpack': 4.41.32 + webpack: 4.47.0 + optional: true + + webpack-dev-middleware@7.4.5(@types/webpack@4.41.32)(webpack@5.105.4): + dependencies: + colorette: 2.0.20 + memfs: 4.57.1 + mime-types: 3.0.2 + on-finished: 2.4.1 + range-parser: 1.2.1 + schema-utils: 4.3.3 + optionalDependencies: + '@types/webpack': 4.41.32 + webpack: 5.105.4 + + webpack-dev-middleware@7.4.5(webpack@5.105.4): + dependencies: + colorette: 2.0.20 + memfs: 4.57.1 + mime-types: 3.0.2 + on-finished: 2.4.1 + range-parser: 1.2.1 + schema-utils: 4.3.3 + optionalDependencies: + webpack: 5.105.4 + + webpack-dev-server@4.9.3(@types/webpack@4.41.32)(webpack@4.47.0): + dependencies: + '@types/bonjour': 3.5.13 + '@types/connect-history-api-fallback': 1.5.4 + '@types/express': 4.17.21 + '@types/express-serve-static-core': 5.1.1 + '@types/serve-index': 1.9.4 + '@types/serve-static': 1.15.10 + '@types/sockjs': 0.3.36 + '@types/ws': 8.18.1 + ansi-html-community: 0.0.8 + anymatch: 3.1.3 + bonjour-service: 1.3.0 + chokidar: 3.6.0 + colorette: 2.0.20 + compression: 1.7.5 + connect-history-api-fallback: 2.0.0 + default-gateway: 6.0.3 + express: 4.21.1 + graceful-fs: 4.2.11 + html-entities: 2.6.0 + http-proxy-middleware: 2.0.9 + ipaddr.js: 2.3.0 + open: 8.4.2 + p-retry: 4.6.2 + rimraf: 3.0.2 + schema-utils: 4.3.3 + selfsigned: 2.4.1 + serve-index: 1.9.2 + sockjs: 0.3.24 + spdy: 4.0.2 + webpack: 4.47.0 + webpack-dev-middleware: 5.3.4(@types/webpack@4.41.32)(webpack@4.47.0) + ws: 8.21.0 + optionalDependencies: + '@types/webpack': 4.41.32 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + webpack-dev-server@5.2.3(@types/webpack@4.41.32)(webpack@4.47.0): + dependencies: + '@types/bonjour': 3.5.13 + '@types/connect-history-api-fallback': 1.5.4 + '@types/express': 4.17.25 + '@types/express-serve-static-core': 4.19.8 + '@types/serve-index': 1.9.4 + '@types/serve-static': 1.15.10 + '@types/sockjs': 0.3.36 + '@types/ws': 8.18.1 + ansi-html-community: 0.0.8 + anymatch: 3.1.3 + bonjour-service: 1.3.0 + chokidar: 3.6.0 + colorette: 2.0.20 + compression: 1.8.1 + connect-history-api-fallback: 2.0.0 + express: 4.22.1 + graceful-fs: 4.2.11 + http-proxy-middleware: 2.0.9 + ipaddr.js: 2.3.0 + launch-editor: 2.13.2 + open: 10.2.0 + p-retry: 6.2.1 + schema-utils: 4.3.3 + selfsigned: 5.5.0 + serve-index: 1.9.2 + sockjs: 0.3.24 + spdy: 4.0.2 + webpack-dev-middleware: 7.4.5(@types/webpack@4.41.32)(webpack@4.47.0) + ws: 8.21.0 + optionalDependencies: + '@types/webpack': 4.41.32 + webpack: 4.47.0 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + optional: true + + webpack-dev-server@5.2.3(webpack@5.105.4): + dependencies: + '@types/bonjour': 3.5.13 + '@types/connect-history-api-fallback': 1.5.4 + '@types/express': 4.17.25 + '@types/express-serve-static-core': 4.19.8 + '@types/serve-index': 1.9.4 + '@types/serve-static': 1.15.10 + '@types/sockjs': 0.3.36 + '@types/ws': 8.18.1 + ansi-html-community: 0.0.8 + anymatch: 3.1.3 + bonjour-service: 1.3.0 + chokidar: 3.6.0 + colorette: 2.0.20 + compression: 1.8.1 + connect-history-api-fallback: 2.0.0 + express: 4.22.1 + graceful-fs: 4.2.11 + http-proxy-middleware: 2.0.9 + ipaddr.js: 2.3.0 + launch-editor: 2.13.2 + open: 10.2.0 + p-retry: 6.2.1 + schema-utils: 4.3.3 + selfsigned: 5.5.0 + serve-index: 1.9.2 + sockjs: 0.3.24 + spdy: 4.0.2 + webpack-dev-middleware: 7.4.5(webpack@5.105.4) + ws: 8.21.0 + optionalDependencies: + webpack: 5.105.4 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + webpack-filter-warnings-plugin@1.2.1(webpack@4.47.0): + dependencies: + webpack: 4.47.0 + + webpack-hot-middleware@2.26.1: + dependencies: + ansi-html-community: 0.0.8 + html-entities: 2.6.0 + strip-ansi: 6.0.1 + + webpack-log@2.0.0: + dependencies: + ansi-colors: 3.2.4 + uuid: 3.4.0 + + webpack-merge@5.8.0: + dependencies: + clone-deep: 4.0.1 + wildcard: 2.0.1 + + webpack-sources@1.4.3: + dependencies: + source-list-map: 2.0.1 + source-map: 0.6.1 + + webpack-sources@3.3.4: {} + + webpack-virtual-modules@0.2.2: + dependencies: + debug: 3.2.7 + + webpack-virtual-modules@0.6.2: {} + + webpack@4.47.0: + dependencies: + '@webassemblyjs/ast': 1.9.0 + '@webassemblyjs/helper-module-context': 1.9.0 + '@webassemblyjs/wasm-edit': 1.9.0 + '@webassemblyjs/wasm-parser': 1.9.0 + acorn: 6.4.2 + ajv: 6.14.0 + ajv-keywords: 3.5.2(ajv@6.14.0) + chrome-trace-event: 1.0.4 + enhanced-resolve: 4.5.0 + eslint-scope: 4.0.3 + json-parse-better-errors: 1.0.2 + loader-runner: 2.4.0 + loader-utils: 1.4.2 + memory-fs: 0.4.1 + micromatch: 3.1.10 + mkdirp: 0.5.6 + neo-async: 2.6.2 + node-libs-browser: 2.2.1 + schema-utils: 1.0.0 + tapable: 1.1.3 + terser-webpack-plugin: 1.4.6(webpack@4.47.0) + watchpack: 1.7.5 + webpack-sources: 1.4.3 + + webpack@5.105.4: + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.16.0 + acorn-import-phases: 1.0.4(acorn@8.16.0) + browserslist: 4.28.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.20.1 + es-module-lexer: 2.0.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.17(webpack@5.105.4) + watchpack: 2.5.1 + webpack-sources: 3.3.4 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + websocket-driver@0.7.4: + dependencies: + http-parser-js: 0.5.10 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 + + websocket-extensions@0.1.4: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@2.3.0: {} + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.2.0: + dependencies: + tr46: 5.1.1 + webidl-conversions: 7.0.0 + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.20 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-module@2.0.1: {} + + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + + widest-line@3.1.0: + dependencies: + string-width: 4.2.3 + + wildcard@2.0.1: {} + + word-wrap@1.2.5: {} + + wordwrap@1.0.0: {} + + worker-farm@1.7.0: + dependencies: + errno: 0.1.8 + + worker-rpc@0.1.1: + dependencies: + microevent.ts: 0.1.1 + + workerpool@6.5.1: {} + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + + wrappy@1.0.2: {} + + write-file-atomic@2.4.3: + dependencies: + graceful-fs: 4.2.11 + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + + write-file-atomic@3.0.3: + dependencies: + imurmurhash: 0.1.4 + is-typedarray: 1.0.0 + signal-exit: 3.0.7 + typedarray-to-buffer: 3.1.5 + + write-file-atomic@4.0.2: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + + write-yaml-file@4.2.0: + dependencies: + js-yaml: 4.1.1 + write-file-atomic: 3.0.3 + + write@1.0.3: + dependencies: + mkdirp: 0.5.6 + + ws@6.2.3: + dependencies: + async-limiter: 1.0.1 + + ws@7.5.10: {} + + ws@8.21.0: {} + + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.1 + + xdg-basedir@4.0.0: {} + + xml-name-validator@5.0.0: {} + + xml2js@0.5.0: + dependencies: + sax: 1.6.0 + xmlbuilder: 11.0.1 + + xml2js@0.6.2: + dependencies: + sax: 1.6.0 + xmlbuilder: 11.0.1 + + xml@1.0.1: {} + + xmlbuilder@11.0.1: {} + + xmlchars@2.2.0: {} + + xmldoc@1.1.4: + dependencies: + sax: 1.6.0 + + xstate@4.26.1: {} + + xtend@4.0.2: {} + + y18n@4.0.3: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yallist@5.0.0: {} + + yaml@1.10.3: {} + + yaml@2.9.0: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs-parser@20.2.9: {} + + yargs-parser@21.1.1: {} + + yargs-unparser@2.0.0: + dependencies: + camelcase: 6.3.0 + decamelize: 4.0.0 + flat: 5.0.2 + is-plain-obj: 2.1.0 + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + + yazl@2.5.1: + dependencies: + buffer-crc32: 0.2.13 + + yocto-queue@0.1.0: {} + + yocto-queue@1.2.2: {} + + z-schema@5.0.5: + dependencies: + lodash.get: 4.4.2 + lodash.isequal: 4.5.0 + validator: 13.15.35 + optionalDependencies: + commander: 9.5.0 + + zip-local@0.3.5: + dependencies: + async: 1.5.2 + graceful-fs: 4.2.11 + jszip: 2.7.0 + q: 1.5.1 + + zip-stream@4.1.1: + dependencies: + archiver-utils: 3.0.4 + compress-commons: 4.1.2 + readable-stream: 3.6.2 + + zod-to-json-schema@3.25.2(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} + + zwitch@1.0.5: {} diff --git a/common/config/subspaces/default/repo-state.json b/common/config/subspaces/default/repo-state.json new file mode 100644 index 00000000000..61a8682c971 --- /dev/null +++ b/common/config/subspaces/default/repo-state.json @@ -0,0 +1,5 @@ +// DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush. +{ + "pnpmShrinkwrapHash": "0cdaaac7c5ac76a646450777edcb5277afddf107", + "preferredVersionsHash": "029c99bd6e65c5e1f25e2848340509811ff9753c" +} diff --git a/common/config/validation/rush-package-lock.json b/common/config/validation/rush-package-lock.json new file mode 100644 index 00000000000..eefe20fb51e --- /dev/null +++ b/common/config/validation/rush-package-lock.json @@ -0,0 +1,4796 @@ +{ + "name": "ci-rush", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ci-rush", + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "@microsoft/rush": "5.178.0" + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.2.0.tgz", + "integrity": "sha512-fNAjWnA/nZ2jz31kxR/AqRaUT8ewHBw/WuBIosK0moMy1C9e5ValbDfFdIxJzVOOYaYkV/b2F1S4H/aHiqfVQg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.11.0.tgz", + "integrity": "sha512-IUZydyTUkDnYdstOW9pFOOUQlBjAepK5teihDE3x6yxsPJs/hsAaaYpeGxdxrgtOiJbBKSjKW7MDk7AEhb4LRg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.11.0.tgz", + "integrity": "sha512-JjQWO6akOck45PH/XBrxzsQGAiKrfFl4m5iggJ0ItMIz5omRufOXWpqCPpdjKN3vKDzlSUvFjaMb7Zwf0gvAdA==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-http-compat": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.5.0.tgz", + "integrity": "sha512-BoSmXPx2er1Ai+wKlDvj29jIQespCNBwEmKyZVHO2kEFsWbGjAjwMCGzug3DJM5/QYIV3vej0S1zcU5bq9fa8w==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2" + }, + "engines": { + "node": ">=22.0.0" + }, + "peerDependencies": { + "@azure/core-client": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0" + } + }, + "node_modules/@azure/core-lro": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", + "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-paging": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.7.0.tgz", + "integrity": "sha512-7GEAoIsaoBr6KELNRb8nypowCqvk8dnCHFCYg4XD4lOQGY2GqjQg5IhkRjyBFRO18CGSMq05PaNqSOE9GQro3g==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.25.0.tgz", + "integrity": "sha512-bMs8ekJLjX8wPV+9IPBges1SLPyuDtE9g5gLDWOpxzKcoOFQnpLGkbcT1tdw3FaAmDS1gnPmMmJ6y/T5B96kIA==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.4.0.tgz", + "integrity": "sha512-eGwxD0AtncrxeBM4tG8R55Pc3rdX1hNW2WibJAgYpCVA6E93mvvVH+LcssoVjOBrSKWS55yEIHsk0X8ctHmfOQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.14.0.tgz", + "integrity": "sha512-9n2pWK61veAuN0V20t9lOuoV4CFMdyAZ1ygZzvBGk/pBBJRib/PjL9PLXa/aI2CcPpyHfqVsxxqLCYl6uZlfDw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-xml": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.6.0.tgz", + "integrity": "sha512-e7lX/dk//F6Qf7BB6PTY4+p2yuOQtyOeHGyapYHNwqSp2OnYpwQt49A/Nin2XmKBQ69pwagR4k/lQBq8lbHQkA==", + "license": "MIT", + "dependencies": { + "fast-xml-parser": "^5.5.9", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.1.tgz", + "integrity": "sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^5.5.0", + "@azure/msal-node": "^5.1.0", + "open": "^10.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.4.0.tgz", + "integrity": "sha512-rbAE25KUfjU/s3XHUdJgceoCP5dEOpMx85J04kF+QMdta73XkuG9JGHHinch+XIoKpBdqljin+KqURpJriSzLA==", + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.17.1.tgz", + "integrity": "sha512-zBhRGzABKSI7hfWh5EaZmril5ybZ7imBN1qEZl5sDTaelr+l8SnPjZO50Q4dnKnm347YPIlBMSnXKZyh3Yu5DQ==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.11.2" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "16.11.2", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.11.2.tgz", + "integrity": "sha512-yDhtBOGDCdK9ipQ9g3+wmlMEPnZx2pXaDicDd9jYyR1L+7lEbvEohTDmF5qejZDutZY3m9pWPxeYxzNC701A2w==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.4.1.tgz", + "integrity": "sha512-yqgoyOIMCH7TNaSLMBTP+4LUlbMMf1zgC8nzOFG95lmW82CmsAEtUT0J93e4BdqDcnX5qle/9X+yb7A8Mw9M0g==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.11.2", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@azure/storage-blob": { + "version": "12.31.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.31.0.tgz", + "integrity": "sha512-DBgNv10aCSxopt92DkTDD0o9xScXeBqPKGmR50FPZQaEcH4JLQ+GEOGEDv19V5BMkB7kxr+m4h6il/cCDPvmHg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.3", + "@azure/core-http-compat": "^2.2.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.6.2", + "@azure/core-rest-pipeline": "^1.19.1", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/core-xml": "^1.4.5", + "@azure/logger": "^1.1.4", + "@azure/storage-common": "^12.3.0", + "events": "^3.0.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/storage-common": { + "version": "12.4.1", + "resolved": "https://registry.npmjs.org/@azure/storage-common/-/storage-common-12.4.1.tgz", + "integrity": "sha512-t14unw/WofGDUi7TKJrsyXyPsN+NLgRm7hMaq0llxNmTIzt7f257+6LE6FKIJPh88zLj6M7LPvzve0fEYg/L3A==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-http-compat": "^2.2.0", + "@azure/core-rest-pipeline": "^1.24.0", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.1.4", + "events": "^3.3.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@inquirer/ansi": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.7.tgz", + "integrity": "sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==", + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.1.5.tgz", + "integrity": "sha512-Jmf9tgBHIEK5SAOB7swYfStqmtkZb00xOTpSQmkoGEpdxOTpJi9RS0A8bkfDPHTTItZRJrRdZrEMu25wyj0VfQ==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.5", + "@inquirer/core": "^11.1.10", + "@inquirer/figures": "^2.0.5", + "@inquirer/type": "^4.0.5" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.0.13.tgz", + "integrity": "sha512-wkGPC7yJ5WJk1DJ5SX7fzk+gfj4BM8cf5dDDi71B/551xHrdsZVRJOC0WyikXd0pEsb/9cLniuE4atbsMqmFkw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.1.10", + "@inquirer/type": "^4.0.5" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.2.1.tgz", + "integrity": "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.7", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.7.tgz", + "integrity": "sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==", + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + } + }, + "node_modules/@inquirer/input": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.0.13.tgz", + "integrity": "sha512-0l0jCHlJnXIV8CTxwQC0C+5Ziq8WP22edWgmciW2xYvoeoSck4v5FvCS1ctKdqLLR0dUo93uAHgWHywgBSoRyw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.1.10", + "@inquirer/type": "^4.0.5" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.1.9.tgz", + "integrity": "sha512-ZlbM28Q9lmLkFPNAIv+ZuY530n5Km8U1WW48oYEvDhe9yc2uL3m3t+JSdRUkQlk5fuIuskgiIVjcb7czFzQpuA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.1.10", + "@inquirer/figures": "^2.0.5", + "@inquirer/type": "^4.0.5" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.1.5.tgz", + "integrity": "sha512-6SRg6kHfK/sjLXOsuqNebuir+sjwrf/iWuRUnXgB2slzEewppI1WfzeS16XxDcOQmXBruMmmB9Cgrz7wsAxqMg==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.5", + "@inquirer/core": "^11.1.10", + "@inquirer/figures": "^2.0.5", + "@inquirer/type": "^4.0.5" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.7.tgz", + "integrity": "sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==", + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@isaacs/fs-minipass/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@microsoft/rush": { + "version": "5.178.0", + "resolved": "https://registry.npmjs.org/@microsoft/rush/-/rush-5.178.0.tgz", + "integrity": "sha512-HnzmhUG3jaEm++VAhObai+KLlMczgqGqh6GYs0vUvk9LKOcKCrZd/kr2I+2gh5T/kOeZdRNAYH7Yx1TI7Ftukg==", + "license": "MIT", + "dependencies": { + "@microsoft/rush-lib": "5.178.0", + "@rushstack/node-core-library": "5.23.3", + "@rushstack/terminal": "0.24.2", + "semver": "~7.7.4" + }, + "bin": { + "rush": "bin/rush", + "rush-pnpm": "bin/rush-pnpm", + "rushx": "bin/rushx" + }, + "engines": { + "node": ">=5.6.0" + } + }, + "node_modules/@microsoft/rush-lib": { + "version": "5.178.0", + "resolved": "https://registry.npmjs.org/@microsoft/rush-lib/-/rush-lib-5.178.0.tgz", + "integrity": "sha512-028TQ0KwZX8XC9X91YSoLhJnGYvP2fv0RPw0cygPXswzjjZAtoFirsf9WiLwcpdj2x6xWv7mvtChHDSTA25msg==", + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "~5.1.3", + "@inquirer/confirm": "~6.0.11", + "@inquirer/input": "~5.0.11", + "@inquirer/search": "~4.1.7", + "@inquirer/select": "~5.1.3", + "@pnpm/link-bins": "~5.3.7", + "@rushstack/credential-cache": "0.2.21", + "@rushstack/heft-config-file": "0.20.12", + "@rushstack/lookup-by-path": "0.10.10", + "@rushstack/node-core-library": "5.23.3", + "@rushstack/npm-check-fork": "0.2.21", + "@rushstack/package-deps-hash": "4.7.23", + "@rushstack/package-extractor": "0.13.9", + "@rushstack/rig-package": "0.7.3", + "@rushstack/rush-amazon-s3-build-cache-plugin": "5.178.0", + "@rushstack/rush-azure-storage-build-cache-plugin": "5.178.0", + "@rushstack/rush-http-build-cache-plugin": "5.178.0", + "@rushstack/rush-pnpm-kit-v10": "0.2.22", + "@rushstack/rush-pnpm-kit-v8": "0.2.22", + "@rushstack/rush-pnpm-kit-v9": "0.2.22", + "@rushstack/stream-collator": "4.2.21", + "@rushstack/terminal": "0.24.2", + "@rushstack/ts-command-line": "5.3.12", + "@yarnpkg/lockfile": "~1.0.2", + "dependency-path": "~9.2.8", + "dotenv": "~16.4.7", + "fast-glob": "~3.3.1", + "git-repo-info": "~2.1.0", + "https-proxy-agent": "~5.0.0", + "ignore": "~5.1.6", + "js-yaml": "~4.1.0", + "npm-package-arg": "~6.1.0", + "object-hash": "3.0.0", + "pnpm-sync-lib": "0.3.4", + "read-package-tree": "~5.1.5", + "rxjs": "~6.6.7", + "semver": "~7.7.4", + "ssri": "~8.0.0", + "strict-uri-encode": "~2.0.0", + "tapable": "2.2.1", + "tar": "~7.5.6", + "true-case-path": "~2.2.1" + }, + "engines": { + "node": ">=5.6.0" + } + }, + "node_modules/@nodable/entities": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.0.0.tgz", + "integrity": "sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pnpm/constants": { + "version": "1001.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/constants/-/constants-1001.3.1.tgz", + "integrity": "sha512-2hf0s4pVrVEH8RvdJJ7YRKjQdiG8m0iAT26TTqXnCbK30kKwJW69VLmP5tED5zstmDRXcOeH5eRcrpkdwczQ9g==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/crypto.base32-hash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@pnpm/crypto.base32-hash/-/crypto.base32-hash-2.0.0.tgz", + "integrity": "sha512-3ttOeHBpmWRbgJrpDQ8Nwd3W8s8iuiP5YZM0JRyKWaMtX8lu9d7/AKyxPmhYsMJuN+q/1dwHa7QFeDZJ53b0oA==", + "license": "MIT", + "dependencies": { + "rfc4648": "^1.5.2" + }, + "engines": { + "node": ">=16.14" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/crypto.hash": { + "version": "1000.1.1", + "resolved": "https://registry.npmjs.org/@pnpm/crypto.hash/-/crypto.hash-1000.1.1.tgz", + "integrity": "sha512-lb5kwXaOXdIW/4bkLLmtM9HEVRvp2eIvp+TrdawcPoaptgA/5f0/sRG0P52BF8dFqeNDj+1tGdqH89WQEqJnxA==", + "license": "MIT", + "dependencies": { + "@pnpm/crypto.polyfill": "1000.1.0", + "@pnpm/graceful-fs": "1000.0.0", + "ssri": "10.0.5" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/crypto.hash/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/@pnpm/crypto.hash/node_modules/ssri": { + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz", + "integrity": "sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@pnpm/crypto.polyfill": { + "version": "1000.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/crypto.polyfill/-/crypto.polyfill-1000.1.0.tgz", + "integrity": "sha512-tNe7a6U4rCpxLMBaR0SIYTdjxGdL0Vwb3G1zY8++sPtHSvy7qd54u8CIB0Z+Y6t5tc9pNYMYCMwhE/wdSY7ltg==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/dependency-path": { + "version": "1001.1.11", + "resolved": "https://registry.npmjs.org/@pnpm/dependency-path/-/dependency-path-1001.1.11.tgz", + "integrity": "sha512-3i58Mbe8ev0d7wIZGEyTdkk5QZZZCCMA10LSujascqrt/Hczb8my165Jkc4+cFJAzAmZAn/34lCCzHCZyOQl1Q==", + "license": "MIT", + "dependencies": { + "@pnpm/crypto.hash": "1000.2.2", + "@pnpm/types": "1001.3.1", + "semver": "^7.7.4" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/dependency-path-pnpm-v10": { + "name": "@pnpm/dependency-path", + "version": "1000.0.9", + "resolved": "https://registry.npmjs.org/@pnpm/dependency-path/-/dependency-path-1000.0.9.tgz", + "integrity": "sha512-0AhabApfiq3EEYeed5HKQEU3ftkrfyKTNgkMH9esGdp2yc+62Zu7eWFf8WW6IGyitDQPLWGYjSEWDC9Bvv8nPg==", + "license": "MIT", + "dependencies": { + "@pnpm/crypto.hash": "1000.1.1", + "@pnpm/types": "1000.6.0", + "semver": "^7.7.1" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/dependency-path-pnpm-v10/node_modules/@pnpm/types": { + "version": "1000.6.0", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-1000.6.0.tgz", + "integrity": "sha512-6PsMNe98VKPGcg6LnXSW/LE3YfJ77nj+bPKiRjYRWAQLZ+xXjEQRaR0dAuyjCmchlv4wR/hpnMVRS21/fCod5w==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/dependency-path-pnpm-v8": { + "name": "@pnpm/dependency-path", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@pnpm/dependency-path/-/dependency-path-2.1.8.tgz", + "integrity": "sha512-ywBaTjy0iSEF7lH3DlF8UXrdL2bw4AQFV2tTOeNeY7wc1W5CE+RHSJhf9MXBYcZPesqGRrPiU7Pimj3l05L9VA==", + "license": "MIT", + "dependencies": { + "@pnpm/crypto.base32-hash": "2.0.0", + "@pnpm/types": "9.4.2", + "encode-registry": "^3.0.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=16.14" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/dependency-path-pnpm-v8/node_modules/@pnpm/types": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-9.4.2.tgz", + "integrity": "sha512-g1hcF8Nv4gd76POilz9gD4LITAPXOe5nX4ijgr8ixCbLQZfcpYiMfJ+C1RlMNRUDo8vhlNB4O3bUlxmT6EAQXA==", + "license": "MIT", + "engines": { + "node": ">=16.14" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/dependency-path-pnpm-v9": { + "name": "@pnpm/dependency-path", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/@pnpm/dependency-path/-/dependency-path-5.1.7.tgz", + "integrity": "sha512-MKCyaTy1r9fhBXAnhDZNBVgo6ThPnicwJEG203FDp7pGhD7NruS/FhBI+uMd7GNsK3D7aIFCDAgbWpNTXn/eWw==", + "license": "MIT", + "dependencies": { + "@pnpm/crypto.base32-hash": "3.0.1", + "@pnpm/types": "12.2.0", + "semver": "^7.6.2" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/dependency-path-pnpm-v9/node_modules/@pnpm/crypto.base32-hash": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@pnpm/crypto.base32-hash/-/crypto.base32-hash-3.0.1.tgz", + "integrity": "sha512-DM4RR/tvB7tMb2FekL0Q97A5PCXNyEC+6ht8SaufAUFSJNxeozqHw9PHTZR03mzjziPzNQLOld0pNINBX3srtw==", + "license": "MIT", + "dependencies": { + "@pnpm/crypto.polyfill": "1.0.0", + "rfc4648": "^1.5.3" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/dependency-path-pnpm-v9/node_modules/@pnpm/crypto.polyfill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pnpm/crypto.polyfill/-/crypto.polyfill-1.0.0.tgz", + "integrity": "sha512-WbmsqqcUXKKaAF77ox1TQbpZiaQcr26myuMUu+WjUtoWYgD3VP6iKYEvSx35SZ6G2L316lu+pv+40A2GbWJc1w==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/dependency-path-pnpm-v9/node_modules/@pnpm/types": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-12.2.0.tgz", + "integrity": "sha512-5RtwWhX39j89/Tmyv2QSlpiNjErA357T/8r1Dkg+2lD3P7RuS7Xi2tChvmOC3VlezEFNcWnEGCOeKoGRkDuqFA==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/dependency-path/node_modules/@pnpm/crypto.hash": { + "version": "1000.2.2", + "resolved": "https://registry.npmjs.org/@pnpm/crypto.hash/-/crypto.hash-1000.2.2.tgz", + "integrity": "sha512-W8pLZvXWLlGG5p0Z2nCvtBhlM6uuTcbAbsS15wlGS31jBBJKJW2udLoFeM7qfWPo7E2PqRPGxca7APpVYAjJhw==", + "license": "MIT", + "dependencies": { + "@pnpm/crypto.polyfill": "1000.1.0", + "@pnpm/graceful-fs": "1000.1.0", + "ssri": "10.0.5" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/dependency-path/node_modules/@pnpm/graceful-fs": { + "version": "1000.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/graceful-fs/-/graceful-fs-1000.1.0.tgz", + "integrity": "sha512-EsMX4slK0qJN2AR0/AYohY5m0HQNYGMNe+jhN74O994zp22/WbX+PbkIKyw3UQn39yQm2+z6SgwklDxbeapsmQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/dependency-path/node_modules/@pnpm/types": { + "version": "1001.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-1001.3.1.tgz", + "integrity": "sha512-8dvQ/12/Zko+R2+f7guZPXDMMRVgsJlCDx3HtRGd+sRaFKqn52Er7tUDCqCvdcrBbSsW+75bDt6RCmEOJ9Ewwg==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/dependency-path/node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/@pnpm/dependency-path/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/@pnpm/dependency-path/node_modules/ssri": { + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz", + "integrity": "sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@pnpm/error": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@pnpm/error/-/error-1.4.0.tgz", + "integrity": "sha512-vxkRrkneBPVmP23kyjnYwVOtipwlSl6UfL+h+Xa3TrABJTz5rYBXemlTsU5BzST8U4pD7YDkTb3SQu+MMuIDKA==", + "license": "MIT", + "engines": { + "node": ">=10.16" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/git-utils": { + "version": "1000.0.0", + "resolved": "https://registry.npmjs.org/@pnpm/git-utils/-/git-utils-1000.0.0.tgz", + "integrity": "sha512-W6isNTNgB26n6dZUgwCw6wly+uHQ2Zh5QiRKY1HHMbLAlsnZOxsSNGnuS9euKWHxDftvPfU7uR8XB5x95T5zPQ==", + "license": "MIT", + "dependencies": { + "execa": "npm:safe-execa@0.1.2" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/graceful-fs": { + "version": "1000.0.0", + "resolved": "https://registry.npmjs.org/@pnpm/graceful-fs/-/graceful-fs-1000.0.0.tgz", + "integrity": "sha512-RvMEliAmcfd/4UoaYQ93DLQcFeqit78jhYmeJJVPxqFGmj0jEcb9Tu0eAOXr7tGP3eJHpgvPbTU4o6pZ1bJhxg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/graceful-fs/node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/@pnpm/link-bins": { + "version": "5.3.25", + "resolved": "https://registry.npmjs.org/@pnpm/link-bins/-/link-bins-5.3.25.tgz", + "integrity": "sha512-9Xq8lLNRHFDqvYPXPgaiKkZ4rtdsm7izwM/cUsFDc5IMnG0QYIVBXQbgwhz2UvjUotbJrvfKLJaCfA3NGBnLDg==", + "license": "MIT", + "dependencies": { + "@pnpm/error": "1.4.0", + "@pnpm/package-bins": "4.1.0", + "@pnpm/read-modules-dir": "2.0.3", + "@pnpm/read-package-json": "4.0.0", + "@pnpm/read-project-manifest": "1.1.7", + "@pnpm/types": "6.4.0", + "@zkochan/cmd-shim": "^5.0.0", + "is-subdir": "^1.1.1", + "is-windows": "^1.0.2", + "mz": "^2.7.0", + "normalize-path": "^3.0.0", + "p-settle": "^4.1.1", + "ramda": "^0.27.1" + }, + "engines": { + "node": ">=10.16" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile-types": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@pnpm/lockfile-types/-/lockfile-types-5.1.5.tgz", + "integrity": "sha512-02FP0HynzX+2DcuPtuMy7PH+kLIC0pevAydAOK+zug2bwdlSLErlvSkc+4+3dw60eRWgUXUqyfO2eR/Ansdbng==", + "license": "MIT", + "dependencies": { + "@pnpm/types": "9.4.2" + }, + "engines": { + "node": ">=16.14" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile-types-pnpm-lock-v6": { + "name": "@pnpm/lockfile-types", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@pnpm/lockfile-types/-/lockfile-types-5.1.5.tgz", + "integrity": "sha512-02FP0HynzX+2DcuPtuMy7PH+kLIC0pevAydAOK+zug2bwdlSLErlvSkc+4+3dw60eRWgUXUqyfO2eR/Ansdbng==", + "license": "MIT", + "dependencies": { + "@pnpm/types": "9.4.2" + }, + "engines": { + "node": ">=16.14" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile-types-pnpm-lock-v6/node_modules/@pnpm/types": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-9.4.2.tgz", + "integrity": "sha512-g1hcF8Nv4gd76POilz9gD4LITAPXOe5nX4ijgr8ixCbLQZfcpYiMfJ+C1RlMNRUDo8vhlNB4O3bUlxmT6EAQXA==", + "license": "MIT", + "engines": { + "node": ">=16.14" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile-types/node_modules/@pnpm/types": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-9.4.2.tgz", + "integrity": "sha512-g1hcF8Nv4gd76POilz9gD4LITAPXOe5nX4ijgr8ixCbLQZfcpYiMfJ+C1RlMNRUDo8vhlNB4O3bUlxmT6EAQXA==", + "license": "MIT", + "engines": { + "node": ">=16.14" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.fs-pnpm-lock-v9": { + "name": "@pnpm/lockfile.fs", + "version": "1001.1.35", + "resolved": "https://registry.npmjs.org/@pnpm/lockfile.fs/-/lockfile.fs-1001.1.35.tgz", + "integrity": "sha512-DzpuzgFGQ4g/O4zCldHlZ2IWK92foORTL7kzhh0i5iVwnXGhtMGy+mlbKTHL+YkI7gzVnQBKuGB/bJ8yc1TqOQ==", + "license": "MIT", + "dependencies": { + "@pnpm/constants": "1001.3.1", + "@pnpm/dependency-path": "1001.1.11", + "@pnpm/error": "1000.1.0", + "@pnpm/git-utils": "1000.0.0", + "@pnpm/lockfile.merger": "1001.0.22", + "@pnpm/lockfile.types": "1002.1.2", + "@pnpm/lockfile.utils": "1004.0.6", + "@pnpm/object.key-sorting": "1000.0.1", + "@pnpm/types": "1001.3.1", + "@zkochan/rimraf": "^3.0.2", + "comver-to-semver": "^1.0.0", + "js-yaml": "npm:@zkochan/js-yaml@0.0.11", + "normalize-path": "^3.0.0", + "ramda": "npm:@pnpm/ramda@0.28.1", + "semver": "^7.7.4", + "strip-bom": "^4.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + }, + "peerDependencies": { + "@pnpm/logger": "^1001.0.1" + } + }, + "node_modules/@pnpm/lockfile.fs-pnpm-lock-v9/node_modules/@pnpm/error": { + "version": "1000.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/error/-/error-1000.1.0.tgz", + "integrity": "sha512-Dqc2IJJPjUatwc9Letw+vG29rnaMrDGi5g6WCx1HiZYm0obXbTmLygeRafMbgf+sLKXrWE1shOeiayQuczBdoA==", + "license": "MIT", + "dependencies": { + "@pnpm/constants": "1001.3.1" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.fs-pnpm-lock-v9/node_modules/@pnpm/types": { + "version": "1001.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-1001.3.1.tgz", + "integrity": "sha512-8dvQ/12/Zko+R2+f7guZPXDMMRVgsJlCDx3HtRGd+sRaFKqn52Er7tUDCqCvdcrBbSsW+75bDt6RCmEOJ9Ewwg==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.fs-pnpm-lock-v9/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/@pnpm/lockfile.fs-pnpm-lock-v9/node_modules/js-yaml": { + "name": "@zkochan/js-yaml", + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.11.tgz", + "integrity": "sha512-SO+h5Jg079r2JvGle0jbdtk1EY7ppu6TGzmfWTp3Gy61IEb1OVKBocJ6ydTn4++nYFNfRKYenI2MniZQwsM9KQ==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@pnpm/lockfile.fs-pnpm-lock-v9/node_modules/ramda": { + "name": "@pnpm/ramda", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@pnpm/ramda/-/ramda-0.28.1.tgz", + "integrity": "sha512-zcAG+lvU0fMziNeGXpPyCyCJYp5ZVrPElEE4t14jAmViaihohocZ+dDkcRIyAomox8pQsuZnv1EyHR+pOhmUWw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, + "node_modules/@pnpm/lockfile.fs-pnpm-lock-v9/node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@pnpm/lockfile.merger": { + "version": "1001.0.22", + "resolved": "https://registry.npmjs.org/@pnpm/lockfile.merger/-/lockfile.merger-1001.0.22.tgz", + "integrity": "sha512-+EaejT0d4mbmtrJthLM6VvDfCmpe8M6QsBQMa+mt0TOS82zyYlIeu9MB/e17pJtGStER8aOKX5yizfq1kGCiAQ==", + "license": "MIT", + "dependencies": { + "@pnpm/lockfile.types": "1002.1.2", + "@pnpm/types": "1001.3.1", + "comver-to-semver": "^1.0.0", + "ramda": "npm:@pnpm/ramda@0.28.1", + "semver": "^7.7.4" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.merger/node_modules/@pnpm/types": { + "version": "1001.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-1001.3.1.tgz", + "integrity": "sha512-8dvQ/12/Zko+R2+f7guZPXDMMRVgsJlCDx3HtRGd+sRaFKqn52Er7tUDCqCvdcrBbSsW+75bDt6RCmEOJ9Ewwg==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.merger/node_modules/ramda": { + "name": "@pnpm/ramda", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@pnpm/ramda/-/ramda-0.28.1.tgz", + "integrity": "sha512-zcAG+lvU0fMziNeGXpPyCyCJYp5ZVrPElEE4t14jAmViaihohocZ+dDkcRIyAomox8pQsuZnv1EyHR+pOhmUWw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, + "node_modules/@pnpm/lockfile.types": { + "version": "1002.1.2", + "resolved": "https://registry.npmjs.org/@pnpm/lockfile.types/-/lockfile.types-1002.1.2.tgz", + "integrity": "sha512-kHgOnG4QKGkG7NzDRgGP9krH58ykQL8nNNuy4THT5f9Gq7QXUaiXzYsknd10IZ8ORi/fJ5qJpO6h8zlVZD2DQw==", + "license": "MIT", + "dependencies": { + "@pnpm/patching.types": "1000.1.0", + "@pnpm/resolver-base": "1005.4.3", + "@pnpm/types": "1001.3.1" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.types-900": { + "name": "@pnpm/lockfile.types", + "version": "900.0.0", + "resolved": "https://registry.npmjs.org/@pnpm/lockfile.types/-/lockfile.types-900.0.0.tgz", + "integrity": "sha512-/4+3CAu4uIjx0ln1DYXNdj0qKJ3wyRDY+RS+eFzV6OHjreaTKWsF2WcjigYp1M5mxL4kj2RsRGgBGEyKtCfEWg==", + "license": "MIT", + "dependencies": { + "@pnpm/patching.types": "900.0.0", + "@pnpm/types": "900.0.0" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.types-900/node_modules/@pnpm/patching.types": { + "version": "900.0.0", + "resolved": "https://registry.npmjs.org/@pnpm/patching.types/-/patching.types-900.0.0.tgz", + "integrity": "sha512-A/3kgRD4Xy2tBMPjOBdx5ZdgmpUobphzWkqDB72S5SIB6gdyCg32AUV0/aO12DwMxpT7kyqMhkkynUOPBfdlUQ==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.types-900/node_modules/@pnpm/types": { + "version": "900.0.0", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-900.0.0.tgz", + "integrity": "sha512-GucC9h/EVbU03Kl7M/FqVes1s5RCQaGCW2f41lFA7VqqHWQElR6k1q33iF6f6fXDUSCdzB1IUxSq9ghP2J+8Pw==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.types-pnpm-lock-v9": { + "name": "@pnpm/lockfile.types", + "version": "1001.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/lockfile.types/-/lockfile.types-1001.1.0.tgz", + "integrity": "sha512-/rfDUV8M9iMm0QXahHPv6SD6eKNkrMXlhECJVhDkdL4NIifcv6/HZwYtxd0PIndExz04+OE+iV9K8zKG9i/OEA==", + "license": "MIT", + "dependencies": { + "@pnpm/patching.types": "1000.1.0", + "@pnpm/types": "1000.7.0" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.types-pnpm-lock-v9/node_modules/@pnpm/types": { + "version": "1000.7.0", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-1000.7.0.tgz", + "integrity": "sha512-1s7FvDqmOEIeFGLUj/VO8sF5lGFxeE/1WALrBpfZhDnMXY/x8FbmuygTTE5joWifebcZ8Ww8Kw2CgBoStsIevQ==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.types/node_modules/@pnpm/types": { + "version": "1001.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-1001.3.1.tgz", + "integrity": "sha512-8dvQ/12/Zko+R2+f7guZPXDMMRVgsJlCDx3HtRGd+sRaFKqn52Er7tUDCqCvdcrBbSsW+75bDt6RCmEOJ9Ewwg==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.utils": { + "version": "1004.0.6", + "resolved": "https://registry.npmjs.org/@pnpm/lockfile.utils/-/lockfile.utils-1004.0.6.tgz", + "integrity": "sha512-FsQluHJGrYEJ9YWy+Jp/6pAdzwLyGXPDOezkuaLgi+lqs8/C5r7CKj6YfdZIR1u3FJ+LstQ1MXLXPlGU3n/6ig==", + "license": "MIT", + "dependencies": { + "@pnpm/dependency-path": "1001.1.11", + "@pnpm/error": "1000.1.0", + "@pnpm/lockfile.types": "1002.1.2", + "@pnpm/resolver-base": "1005.4.3", + "@pnpm/types": "1001.3.1", + "get-npm-tarball-url": "^2.1.0", + "ramda": "npm:@pnpm/ramda@0.28.1" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.utils/node_modules/@pnpm/error": { + "version": "1000.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/error/-/error-1000.1.0.tgz", + "integrity": "sha512-Dqc2IJJPjUatwc9Letw+vG29rnaMrDGi5g6WCx1HiZYm0obXbTmLygeRafMbgf+sLKXrWE1shOeiayQuczBdoA==", + "license": "MIT", + "dependencies": { + "@pnpm/constants": "1001.3.1" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.utils/node_modules/@pnpm/types": { + "version": "1001.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-1001.3.1.tgz", + "integrity": "sha512-8dvQ/12/Zko+R2+f7guZPXDMMRVgsJlCDx3HtRGd+sRaFKqn52Er7tUDCqCvdcrBbSsW+75bDt6RCmEOJ9Ewwg==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/lockfile.utils/node_modules/ramda": { + "name": "@pnpm/ramda", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@pnpm/ramda/-/ramda-0.28.1.tgz", + "integrity": "sha512-zcAG+lvU0fMziNeGXpPyCyCJYp5ZVrPElEE4t14jAmViaihohocZ+dDkcRIyAomox8pQsuZnv1EyHR+pOhmUWw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, + "node_modules/@pnpm/logger": { + "version": "1001.0.1", + "resolved": "https://registry.npmjs.org/@pnpm/logger/-/logger-1001.0.1.tgz", + "integrity": "sha512-gdwlAMXC4Wc0s7Dmg/4wNybMEd/4lSd9LsXQxeg/piWY0PPXjgz1IXJWnVScx6dZRaaodWP3c1ornrw8mZdFZw==", + "license": "MIT", + "dependencies": { + "bole": "^5.0.17", + "split2": "^4.2.0" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/merge-lockfile-changes": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@pnpm/merge-lockfile-changes/-/merge-lockfile-changes-5.0.7.tgz", + "integrity": "sha512-fYmX1+EHv3wg7l4A9FCEkjgEBIHaY6JosknkLk3pL8dbB9k6unjIrF9f2onNtpj3XUlWxZ3aBw9THk/Bf6hKow==", + "license": "MIT", + "dependencies": { + "@pnpm/lockfile-types": "5.1.5", + "comver-to-semver": "^1.0.0", + "ramda": "npm:@pnpm/ramda@0.28.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=16.14" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/merge-lockfile-changes/node_modules/ramda": { + "name": "@pnpm/ramda", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@pnpm/ramda/-/ramda-0.28.1.tgz", + "integrity": "sha512-zcAG+lvU0fMziNeGXpPyCyCJYp5ZVrPElEE4t14jAmViaihohocZ+dDkcRIyAomox8pQsuZnv1EyHR+pOhmUWw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, + "node_modules/@pnpm/object.key-sorting": { + "version": "1000.0.1", + "resolved": "https://registry.npmjs.org/@pnpm/object.key-sorting/-/object.key-sorting-1000.0.1.tgz", + "integrity": "sha512-YTJCXyUGOrJuj4QqhSKqZa1vlVAm82h1/uw00ZmD/kL2OViggtyUwWyIe62kpwWVPwEYixfGjfvaFKVJy2mjzA==", + "license": "MIT", + "dependencies": { + "@pnpm/util.lex-comparator": "^3.0.2", + "sort-keys": "^4.2.0" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/package-bins": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/package-bins/-/package-bins-4.1.0.tgz", + "integrity": "sha512-57/ioGYLBbVRR80Ux9/q2i3y8Q+uQADc3c+Yse8jr/60YLOi3jcWz13e2Jy+ANYtZI258Qc5wk2X077rp0Ly/Q==", + "license": "MIT", + "dependencies": { + "@pnpm/types": "6.4.0", + "fast-glob": "^3.2.4", + "is-subdir": "^1.1.1" + }, + "engines": { + "node": ">=10.16" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/patching.types": { + "version": "1000.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/patching.types/-/patching.types-1000.1.0.tgz", + "integrity": "sha512-Zib2ysLctRnWM4KXXlljR44qSKwyEqYmLk+8VPBDBEK3l5Gp5mT3N4ix9E4qjYynvFqahumsxzOfxOYQhUGMGw==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/read-modules-dir": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@pnpm/read-modules-dir/-/read-modules-dir-2.0.3.tgz", + "integrity": "sha512-i9OgRvSlxrTS9a2oXokhDxvQzDtfqtsooJ9jaGoHkznue5aFCTSrNZFQ6M18o8hC03QWfnxaKi0BtOvNkKu2+A==", + "license": "MIT", + "dependencies": { + "mz": "^2.7.0" + }, + "engines": { + "node": ">=10.13" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/read-package-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@pnpm/read-package-json/-/read-package-json-4.0.0.tgz", + "integrity": "sha512-1cr2tEwe4YU6SI0Hmg+wnsr6yxBt2iJtqv6wrF84On8pS9hx4A2PLw3CIgbwxaG0b+ur5wzhNogwl4qD5FLFNg==", + "license": "MIT", + "dependencies": { + "@pnpm/error": "1.4.0", + "@pnpm/types": "6.4.0", + "load-json-file": "^6.2.0", + "normalize-package-data": "^3.0.2" + }, + "engines": { + "node": ">=10.16" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/read-project-manifest": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@pnpm/read-project-manifest/-/read-project-manifest-1.1.7.tgz", + "integrity": "sha512-tj8ExXZeDcMmMUj7D292ETe/RiEirr1X1wpT6Zy85z2MrFYoG9jfCJpps40OdZBNZBhxbuKtGPWKVSgXD0yrVw==", + "license": "MIT", + "dependencies": { + "@pnpm/error": "1.4.0", + "@pnpm/types": "6.4.0", + "@pnpm/write-project-manifest": "1.1.7", + "detect-indent": "^6.0.0", + "fast-deep-equal": "^3.1.3", + "graceful-fs": "4.2.4", + "is-windows": "^1.0.2", + "json5": "^2.1.3", + "parse-json": "^5.1.0", + "read-yaml-file": "^2.0.0", + "sort-keys": "^4.1.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=10.16" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/resolver-base": { + "version": "1005.4.3", + "resolved": "https://registry.npmjs.org/@pnpm/resolver-base/-/resolver-base-1005.4.3.tgz", + "integrity": "sha512-c97+Njdw6nVYnPpX/K0VIJizMj667iicUqC8WzLqEx9XJVw9WPrxSrZTE+lZ9e1F0tImFY3ycub8gLevJGsDiA==", + "license": "MIT", + "dependencies": { + "@pnpm/types": "1001.3.1" + }, + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/resolver-base/node_modules/@pnpm/types": { + "version": "1001.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-1001.3.1.tgz", + "integrity": "sha512-8dvQ/12/Zko+R2+f7guZPXDMMRVgsJlCDx3HtRGd+sRaFKqn52Er7tUDCqCvdcrBbSsW+75bDt6RCmEOJ9Ewwg==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/types": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-6.4.0.tgz", + "integrity": "sha512-nco4+4sZqNHn60Y4VE/fbtlShCBqipyUO+nKRPvDHqLrecMW9pzHWMVRxk4nrMRoeowj3q0rX3GYRBa8lsHTAg==", + "license": "MIT", + "engines": { + "node": ">=10.16" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@pnpm/util.lex-comparator": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/util.lex-comparator/-/util.lex-comparator-3.0.2.tgz", + "integrity": "sha512-blFO4Ws97tWv/SNE6N39ZdGmZBrocXnBOfVp0ln4kELmns4pGPZizqyRtR8EjfOLMLstbmNCTReBoDvLz1isVg==", + "license": "MIT", + "engines": { + "node": ">=18.12" + } + }, + "node_modules/@pnpm/write-project-manifest": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@pnpm/write-project-manifest/-/write-project-manifest-1.1.7.tgz", + "integrity": "sha512-OLkDZSqkA1mkoPNPvLFXyI6fb0enCuFji6Zfditi/CLAo9kmIhQFmEUDu4krSB8i908EljG8YwL5Xjxzm5wsWA==", + "license": "MIT", + "dependencies": { + "@pnpm/types": "6.4.0", + "json5": "^2.1.3", + "mz": "^2.7.0", + "write-file-atomic": "^3.0.3", + "write-yaml-file": "^4.1.3" + }, + "engines": { + "node": ">=10.16" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@rushstack/credential-cache": { + "version": "0.2.21", + "resolved": "https://registry.npmjs.org/@rushstack/credential-cache/-/credential-cache-0.2.21.tgz", + "integrity": "sha512-8bs1WW7da5F5WE7gu35XrhCM/l1+n9ksTqTkdc+QzXwFNe5l1aRzInrqED5z2RLCvj2Mx+FwgyRrkPe8hJpTxg==", + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.23.3" + } + }, + "node_modules/@rushstack/heft-config-file": { + "version": "0.20.12", + "resolved": "https://registry.npmjs.org/@rushstack/heft-config-file/-/heft-config-file-0.20.12.tgz", + "integrity": "sha512-IFbjnQN/slbygZ/zINx5tp1XNKMkpIKOiQCNAy3plcGXTt4iH9CCyOvOct61z1ODI/lPoK1YDDLHCVatFqCXfw==", + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.23.3", + "@rushstack/rig-package": "0.7.3", + "@rushstack/terminal": "0.24.2", + "jsonpath-plus": "~10.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@rushstack/lookup-by-path": { + "version": "0.10.10", + "resolved": "https://registry.npmjs.org/@rushstack/lookup-by-path/-/lookup-by-path-0.10.10.tgz", + "integrity": "sha512-bEPM3G65CeKR9sByZIF0lH75I5E4bGbTAuQIPRQLNeI7ISQJkQa5UenAp/e0HoPiLNfBB9bxySOEyq1qBjGlcA==", + "license": "MIT", + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library": { + "version": "5.23.3", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.23.3.tgz", + "integrity": "sha512-f6uuza7Um65bwsIJgf0MRs7IPA5IG+A+zs1AYGQvpZmLjtTGdfHowhQw4kwF0pPhJCrU4UHNhK8Qa6tLygYZCA==", + "license": "MIT", + "dependencies": { + "ajv": "~8.20.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~11.3.0", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.7.4" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/npm-check-fork": { + "version": "0.2.21", + "resolved": "https://registry.npmjs.org/@rushstack/npm-check-fork/-/npm-check-fork-0.2.21.tgz", + "integrity": "sha512-KVejprYDK7oYviQQx4pTP6xKZ7IPAmnDE06izIK9fq9tkuzuaqPNaKHLy5GlElLbqs5LEXzRRwAnbkFdQWui3w==", + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.23.3", + "semver": "~7.7.4" + } + }, + "node_modules/@rushstack/package-deps-hash": { + "version": "4.7.23", + "resolved": "https://registry.npmjs.org/@rushstack/package-deps-hash/-/package-deps-hash-4.7.23.tgz", + "integrity": "sha512-lqzReyS1yGyBO6FIA6nnJr1/srjQS6f++o9T54YP6zUZ3wLoF6tm5Esc82JEo2t2aonQKpZKOVHoC+a2pAKCnQ==", + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.23.3" + } + }, + "node_modules/@rushstack/package-extractor": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/@rushstack/package-extractor/-/package-extractor-0.13.9.tgz", + "integrity": "sha512-ndOYrThsnnfNtnQjweA8X+2us4/AkcX5MYS/vOVj18l8eRJRV8f+HfiC8D6qc2P265BBAaDc1y+VWJsCw25z0Q==", + "license": "MIT", + "dependencies": { + "@pnpm/link-bins": "~5.3.7", + "@rushstack/node-core-library": "5.23.3", + "@rushstack/terminal": "0.24.2", + "@rushstack/ts-command-line": "5.3.12", + "ignore": "~5.1.6", + "jszip": "~3.8.0", + "minimatch": "10.2.3", + "npm-packlist": "~5.1.3", + "semver": "~7.7.4" + } + }, + "node_modules/@rushstack/problem-matcher": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@rushstack/problem-matcher/-/problem-matcher-0.2.1.tgz", + "integrity": "sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==", + "license": "MIT", + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/rig-package": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.7.3.tgz", + "integrity": "sha512-aAA518n6wxxjCfnTAOjQnm7ngNE0FVHxHAw2pxKlIhxrMn0XQjGcXKF0oKWpjBgJOmsaJpVob/v+zr3zxgPWuA==", + "license": "MIT", + "dependencies": { + "jju": "~1.4.0", + "resolve": "~1.22.1" + } + }, + "node_modules/@rushstack/rush-amazon-s3-build-cache-plugin": { + "version": "5.178.0", + "resolved": "https://registry.npmjs.org/@rushstack/rush-amazon-s3-build-cache-plugin/-/rush-amazon-s3-build-cache-plugin-5.178.0.tgz", + "integrity": "sha512-NjKIxne2TqpslN5lj5Mwn+8Wb13ee3UQoKd3FkykD3/KT662R4X2LLzjfNl+NW2UrXoX6aZPkcS8MSM3caGRbg==", + "license": "MIT", + "dependencies": { + "@rushstack/credential-cache": "0.2.21", + "@rushstack/node-core-library": "5.23.3", + "@rushstack/rush-sdk": "5.178.0", + "@rushstack/terminal": "0.24.2", + "https-proxy-agent": "~5.0.0" + } + }, + "node_modules/@rushstack/rush-azure-storage-build-cache-plugin": { + "version": "5.178.0", + "resolved": "https://registry.npmjs.org/@rushstack/rush-azure-storage-build-cache-plugin/-/rush-azure-storage-build-cache-plugin-5.178.0.tgz", + "integrity": "sha512-GW6b1NhcuBmjMwMV08JwsmmSwpC99w7hNJF1hmROWr9QM6ZW7Gn1SMKpdNcMwPDfGGp0LGx6GVRqJxzwuY8ejg==", + "license": "MIT", + "dependencies": { + "@azure/identity": "~4.13.1", + "@azure/storage-blob": "~12.31.0", + "@rushstack/credential-cache": "0.2.21", + "@rushstack/node-core-library": "5.23.3", + "@rushstack/rush-sdk": "5.178.0", + "@rushstack/terminal": "0.24.2" + } + }, + "node_modules/@rushstack/rush-http-build-cache-plugin": { + "version": "5.178.0", + "resolved": "https://registry.npmjs.org/@rushstack/rush-http-build-cache-plugin/-/rush-http-build-cache-plugin-5.178.0.tgz", + "integrity": "sha512-OcZubwQbO75nhiFNdsaPcUaleqD4Vo1IIy1zHggTiclTX0g/kXmwmxtigNcO7WvjWU0wuIy1MMYlGWtZuBg0yg==", + "license": "MIT", + "dependencies": { + "@rushstack/credential-cache": "0.2.21", + "@rushstack/node-core-library": "5.23.3", + "@rushstack/rush-sdk": "5.178.0", + "https-proxy-agent": "~5.0.0" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v10": { + "version": "0.2.22", + "resolved": "https://registry.npmjs.org/@rushstack/rush-pnpm-kit-v10/-/rush-pnpm-kit-v10-0.2.22.tgz", + "integrity": "sha512-wX52kiS5b7rbZvw4ksJlIZkCIF1svJAxYYw9MTDLj3swmFSnVyecuKhi1jKFbcJueawHEBzG6bwFAZXtYKd5Kg==", + "license": "MIT", + "dependencies": { + "@pnpm/dependency-path-pnpm-v10": "npm:@pnpm/dependency-path@~1000.0.9", + "@pnpm/lockfile.fs-pnpm-lock-v9": "npm:@pnpm/lockfile.fs@~1001.1.11", + "@pnpm/logger": "~1001.0.0" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v8": { + "version": "0.2.22", + "resolved": "https://registry.npmjs.org/@rushstack/rush-pnpm-kit-v8/-/rush-pnpm-kit-v8-0.2.22.tgz", + "integrity": "sha512-2CpvI0W+mNABuEjOhJOgwn8FMo7DDZjgU4++wnNq6UnfWj8i1hDSsUmAfxdqq8fLNQu/nCZAQ8RSe4PpgH9MHw==", + "license": "MIT", + "dependencies": { + "@pnpm/dependency-path-pnpm-v8": "npm:@pnpm/dependency-path@~2.1.8", + "@pnpm/lockfile-file-pnpm-lock-v6": "npm:@pnpm/lockfile-file@~8.1.8", + "@pnpm/logger": "~5.0.0" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v8/node_modules/@pnpm/constants": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@pnpm/constants/-/constants-7.1.1.tgz", + "integrity": "sha512-31pZqMtjwV+Vaq7MaPrT1EoDFSYwye3dp6BiHIGRJmVThCQwySRKM7hCvqqI94epNkqFAAYoWrNynWoRYosGdw==", + "license": "MIT", + "engines": { + "node": ">=16.14" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v8/node_modules/@pnpm/dependency-path": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@pnpm/dependency-path/-/dependency-path-2.1.8.tgz", + "integrity": "sha512-ywBaTjy0iSEF7lH3DlF8UXrdL2bw4AQFV2tTOeNeY7wc1W5CE+RHSJhf9MXBYcZPesqGRrPiU7Pimj3l05L9VA==", + "license": "MIT", + "dependencies": { + "@pnpm/crypto.base32-hash": "2.0.0", + "@pnpm/types": "9.4.2", + "encode-registry": "^3.0.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=16.14" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v8/node_modules/@pnpm/error": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@pnpm/error/-/error-5.0.3.tgz", + "integrity": "sha512-ONJU5cUeoeJSy50qOYsMZQHTA/9QKmGgh1ATfEpCLgtbdwqUiwD9MxHNeXUYYI/pocBCz6r1ZCFqiQvO+8SUKA==", + "license": "MIT", + "dependencies": { + "@pnpm/constants": "7.1.1" + }, + "engines": { + "node": ">=16.14" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v8/node_modules/@pnpm/git-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pnpm/git-utils/-/git-utils-1.0.0.tgz", + "integrity": "sha512-lUI+XrzOJN4zdPGOGnFUrmtXAXpXi8wD8OI0nWOZmlh+raqbLzC3VkXu1zgaduOK6YonOcnQW88O+ojav1rAdA==", + "license": "MIT", + "dependencies": { + "execa": "npm:safe-execa@0.1.2" + }, + "engines": { + "node": ">=16.14" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v8/node_modules/@pnpm/lockfile-file-pnpm-lock-v6": { + "name": "@pnpm/lockfile-file", + "version": "8.1.8", + "resolved": "https://registry.npmjs.org/@pnpm/lockfile-file/-/lockfile-file-8.1.8.tgz", + "integrity": "sha512-bRadYzGFyFtwiynwp4Mkn7NDNHkgKvJ9xtjsCT5XiE6S8wpzS3W8yx2WzHGk9Mm1J/2wM0F52+NzCWhlz5eIqA==", + "license": "MIT", + "dependencies": { + "@pnpm/constants": "7.1.1", + "@pnpm/dependency-path": "2.1.8", + "@pnpm/error": "5.0.3", + "@pnpm/git-utils": "1.0.0", + "@pnpm/lockfile-types": "5.1.5", + "@pnpm/merge-lockfile-changes": "5.0.7", + "@pnpm/types": "9.4.2", + "@pnpm/util.lex-comparator": "1.0.0", + "@zkochan/rimraf": "^2.1.3", + "comver-to-semver": "^1.0.0", + "js-yaml": "npm:@zkochan/js-yaml@0.0.6", + "normalize-path": "^3.0.0", + "ramda": "npm:@pnpm/ramda@0.28.1", + "semver": "^7.5.4", + "sort-keys": "^4.2.0", + "strip-bom": "^4.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": ">=16.14" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + }, + "peerDependencies": { + "@pnpm/logger": "^5.0.0" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v8/node_modules/@pnpm/logger": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pnpm/logger/-/logger-5.0.0.tgz", + "integrity": "sha512-YfcB2QrX+Wx1o6LD1G2Y2fhDhOix/bAY/oAnMpHoNLsKkWIRbt1oKLkIFvxBMzLwAEPqnYWguJrYC+J6i4ywbw==", + "license": "MIT", + "dependencies": { + "bole": "^5.0.0", + "ndjson": "^2.0.0" + }, + "engines": { + "node": ">=12.17" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v8/node_modules/@pnpm/types": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-9.4.2.tgz", + "integrity": "sha512-g1hcF8Nv4gd76POilz9gD4LITAPXOe5nX4ijgr8ixCbLQZfcpYiMfJ+C1RlMNRUDo8vhlNB4O3bUlxmT6EAQXA==", + "license": "MIT", + "engines": { + "node": ">=16.14" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v8/node_modules/@pnpm/util.lex-comparator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pnpm/util.lex-comparator/-/util.lex-comparator-1.0.0.tgz", + "integrity": "sha512-3aBQPHntVgk5AweBWZn+1I/fqZ9krK/w01197aYVkAJQGftb+BVWgEepxY5GChjSW12j52XX+CmfynYZ/p0DFQ==", + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v8/node_modules/@zkochan/rimraf": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@zkochan/rimraf/-/rimraf-2.1.3.tgz", + "integrity": "sha512-mCfR3gylCzPC+iqdxEA6z5SxJeOgzgbwmyxanKriIne5qZLswDe/M43aD3p5MNzwzXRhbZg/OX+MpES6Zk1a6A==", + "license": "MIT", + "dependencies": { + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.10" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v8/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/@rushstack/rush-pnpm-kit-v8/node_modules/js-yaml": { + "name": "@zkochan/js-yaml", + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz", + "integrity": "sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v8/node_modules/ramda": { + "name": "@pnpm/ramda", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@pnpm/ramda/-/ramda-0.28.1.tgz", + "integrity": "sha512-zcAG+lvU0fMziNeGXpPyCyCJYp5ZVrPElEE4t14jAmViaihohocZ+dDkcRIyAomox8pQsuZnv1EyHR+pOhmUWw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v8/node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@rushstack/rush-pnpm-kit-v9": { + "version": "0.2.22", + "resolved": "https://registry.npmjs.org/@rushstack/rush-pnpm-kit-v9/-/rush-pnpm-kit-v9-0.2.22.tgz", + "integrity": "sha512-WStbnk3Wjx9q/o51h62/egWME1LEHJsIypXFqv1hggA2/ZL90S68zXRse6ibdrXjfUantovZUOiEybgjbwCYIA==", + "license": "MIT", + "dependencies": { + "@pnpm/dependency-path-pnpm-v9": "npm:@pnpm/dependency-path@~5.1.7", + "@pnpm/lockfile.fs-pnpm-lock-v9": "npm:@pnpm/lockfile.fs@~1001.1.11", + "@pnpm/logger": "~1001.0.0" + } + }, + "node_modules/@rushstack/rush-sdk": { + "version": "5.178.0", + "resolved": "https://registry.npmjs.org/@rushstack/rush-sdk/-/rush-sdk-5.178.0.tgz", + "integrity": "sha512-9dic8gRauC0CurAHNs6Muh2Nb9/M2GacuD28E3MTpMnPzC9hy7Lz4p5E5NO+piKFruhKOIICLiLNCRazSRvr3w==", + "license": "MIT", + "dependencies": { + "@pnpm/lockfile.types-900": "npm:@pnpm/lockfile.types@~900.0.0", + "@rushstack/credential-cache": "0.2.21", + "@rushstack/lookup-by-path": "0.10.10", + "@rushstack/node-core-library": "5.23.3", + "@rushstack/package-deps-hash": "4.7.23", + "@rushstack/terminal": "0.24.2", + "tapable": "2.2.1" + } + }, + "node_modules/@rushstack/stream-collator": { + "version": "4.2.21", + "resolved": "https://registry.npmjs.org/@rushstack/stream-collator/-/stream-collator-4.2.21.tgz", + "integrity": "sha512-jVECE9nYiYsuCH1wZtX7rPYeMFK17t81a/1QnCMAs61ILhf6EBk7NbIt01WVyC07zx/EaBEL2giwHOAYWRybrA==", + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.23.3", + "@rushstack/terminal": "0.24.2" + } + }, + "node_modules/@rushstack/terminal": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.24.2.tgz", + "integrity": "sha512-KB7PpvzDyKMw/RGU3TxOwxTs3OwZ4gq6+WHlTJN/JfQH4ezliNtWIqver78jTaAJyz/ZAAlJGH7a/M1WyFLFSw==", + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.23.3", + "@rushstack/problem-matcher": "0.2.1", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/ts-command-line": { + "version": "5.3.12", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.3.12.tgz", + "integrity": "sha512-Vg2n24arSf7JvUNga2DMHYTbxnVq9L5OtVCp4Gfr8YC/kmL/bmdR8FQhGcpMzMYNY9Vdw3+TaimG11Sf+z1Tpw==", + "license": "MIT", + "dependencies": { + "@rushstack/terminal": "0.24.2", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" + } + }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "license": "MIT" + }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.7.tgz", + "integrity": "sha512-JVUD8X2tfDMWjcjLs4yVxxVrS8yR5vnh386GAXT9Qj79nBxxXSaHFQZg5FweLmT8HlPQ3kii6noUB+Z9RN7DvQ==", + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@typespec/ts-http-runtime/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.0.2.tgz", + "integrity": "sha512-MqJ00WXw89ga0rK6GZkdmmgv3bAsxpJixyTthjcix73O44pBqotyU2BejBkLuIsaOBI6SEu77vAnSyLe5iIHkw==", + "license": "BSD-2-Clause" + }, + "node_modules/@zkochan/cmd-shim": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@zkochan/cmd-shim/-/cmd-shim-5.4.1.tgz", + "integrity": "sha512-odWb1qUzt0dIOEUPyWBEpFDYQPRjEMr/dbHHAfgBkVkYR9aO7Zo+I7oYWrXIxl+cKlC7+49ftPm8uJxL1MA9kw==", + "license": "BSD-2-Clause", + "dependencies": { + "cmd-extension": "^1.0.2", + "graceful-fs": "^4.2.10", + "is-windows": "^1.0.2" + }, + "engines": { + "node": ">=10.13" + } + }, + "node_modules/@zkochan/cmd-shim/node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/@zkochan/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@zkochan/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-GBf4ua7ogWTr7fATnzk/JLowZDBnBJMm8RkMaC/KcvxZ9gxbMWix0/jImd815LmqKyIHZ7h7lADRddGMdGBuCA==", + "license": "MIT", + "engines": { + "node": ">=18.12" + } + }, + "node_modules/@zkochan/which": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@zkochan/which/-/which-2.0.3.tgz", + "integrity": "sha512-C1ReN7vt2/2O0fyTsx5xnbQuxBrmG5NMSbcIkPKCCfCTJgpZBsuRYzFXHj3nVq8vTfK7vxHUmzfCpSHgO7j4rg==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/better-path-resolve": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", + "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", + "license": "MIT", + "dependencies": { + "is-windows": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/bole": { + "version": "5.0.29", + "resolved": "https://registry.npmjs.org/bole/-/bole-5.0.29.tgz", + "integrity": "sha512-eYR9i2ubLv5/4TFGyZsQ1cVH4jF9+qLJA72Aow+E7ZZQfqHqQNUZeX3w+pVWF76PQyjl5eDKf2xylyOOX76ozA==", + "license": "MIT", + "dependencies": { + "fast-safe-stringify": "^2.0.7", + "individual": "^3.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", + "license": "MIT" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cmd-extension": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cmd-extension/-/cmd-extension-1.0.2.tgz", + "integrity": "sha512-iWDjmP8kvsMdBmLTHxFaqXikO8EdFRDfim7k6vUHglY/2xJ5jLrPsnQGijdfp4U+sr/BeecG0wKm02dSIAeQ1g==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/comver-to-semver": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/comver-to-semver/-/comver-to-semver-1.0.0.tgz", + "integrity": "sha512-gcGtbRxjwROQOdXLUWH1fQAXqThUVRZ219aAwgtX3KfYw429/Zv6EIJRf5TBSzWdAGwePmqH7w70WTaX4MDqag==", + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dependency-path": { + "version": "9.2.8", + "resolved": "https://registry.npmjs.org/dependency-path/-/dependency-path-9.2.8.tgz", + "integrity": "sha512-S0OhIK7sIyAsph8hVH/LMCTDL3jozKtlrPx3dMQrlE2nAlXTquTT+AcOufphDMTQqLkfn4acvfiem9I1IWZ4jQ==", + "license": "MIT", + "dependencies": { + "@pnpm/crypto.base32-hash": "1.0.1", + "@pnpm/types": "8.9.0", + "encode-registry": "^3.0.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=14.6" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/dependency-path/node_modules/@pnpm/crypto.base32-hash": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pnpm/crypto.base32-hash/-/crypto.base32-hash-1.0.1.tgz", + "integrity": "sha512-pzAXNn6KxTA3kbcI3iEnYs4vtH51XEVqmK/1EiD18MaPKylhqy8UvMJK3zKG+jeP82cqQbozcTGm4yOQ8i3vNw==", + "license": "MIT", + "dependencies": { + "rfc4648": "^1.5.1" + }, + "engines": { + "node": ">=14.6" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/dependency-path/node_modules/@pnpm/types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@pnpm/types/-/types-8.9.0.tgz", + "integrity": "sha512-3MYHYm8epnciApn6w5Fzx6sepawmsNU7l6lvIq+ER22/DPSrr83YMhU/EQWnf4lORn2YyiXFj0FJSyJzEtIGmw==", + "license": "MIT", + "engines": { + "node": ">=14.6" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/encode-registry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/encode-registry/-/encode-registry-3.0.1.tgz", + "integrity": "sha512-6qOwkl1g0fv0DN3Y3ggr2EaZXN71aoAqPp3p/pVaWSBSIo+YjLOWN61Fva43oVyQNPf7kgm8lkudzlzojwE2jw==", + "license": "MIT", + "dependencies": { + "mem": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "name": "safe-execa", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/safe-execa/-/safe-execa-0.1.2.tgz", + "integrity": "sha512-vdTshSQ2JsRCgT8eKZWNJIL26C6bVqy1SOmuCMlKHegVeo8KYRobRrefOdUq9OozSPUUiSxrylteeRmLOMFfWg==", + "license": "MIT", + "dependencies": { + "@zkochan/which": "^2.0.3", + "execa": "^5.1.1", + "path-name": "^1.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/execa/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-uri": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fast-xml-builder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.0.tgz", + "integrity": "sha512-F74cZEdCvuw9P41GAC3rod4X04jjWGM1JPEv/GWSqFTWLsdyMSBMBMlm9Hk3GLBgLBbdBNY8yee0pQh2RBVESQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.6.2", + "xml-naming": "^0.3.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.10.1.tgz", + "integrity": "sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^3.0.0", + "fast-xml-builder": "^1.2.0", + "is-unsafe": "^2.0.0", + "path-expression-matcher": "^1.6.2", + "strnum": "^2.4.1", + "xml-naming": "^0.3.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-extra": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.6.tgz", + "integrity": "sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-npm-tarball-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/get-npm-tarball-url/-/get-npm-tarball-url-2.1.0.tgz", + "integrity": "sha512-ro+DiMu5DXgRBabqXupW38h7WPZ9+Ad8UjwhvsmmN8w1sU7ab0nzAXvVZ4kqYg57OrqomRtJvepX5/xvFKNtjA==", + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-repo-info": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/git-repo-info/-/git-repo-info-2.1.1.tgz", + "integrity": "sha512-8aCohiDo4jwjOwma4FmYFd3i97urZulL8XL24nIPxuE+GZnfsAyy/g2Shqx6OjUiFKUXZM+Yy+KHnOmmA3FVcg==", + "license": "MIT", + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ignore": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", + "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", + "license": "ISC", + "dependencies": { + "minimatch": "^5.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ignore-walk/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/individual": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/individual/-/individual-3.0.0.tgz", + "integrity": "sha512-rUY5vtT748NMRbEMrTNiFfy29BgGZwGXUi2NFUVMWQrogSLzlJvQV9eeMWi+g1aVaQ53tpyLAQtd5x/JH0Nh1g==" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-subdir": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz", + "integrity": "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==", + "license": "MIT", + "dependencies": { + "better-path-resolve": "1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" + }, + "node_modules/is-unsafe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.0.tgz", + "integrity": "sha512-2LdV822R+wmI86unXA93WCFpL6g+av8ynWk0nrHyJqGop5VoocYsSLFgN8jrfalT6iGeLNM4KXuVSsULP53kEA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js-yaml/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/jsep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpath-plus": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz", + "integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==", + "license": "MIT", + "dependencies": { + "@jsep-plugin/assignment": "^1.3.0", + "@jsep-plugin/regex": "^1.0.4", + "jsep": "^1.4.0" + }, + "bin": { + "jsonpath": "bin/jsonpath-cli.js", + "jsonpath-plus": "bin/jsonpath-cli.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jszip": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.8.0.tgz", + "integrity": "sha512-cnpQrXvFSLdsR9KR5/x7zdf6c3m8IhZfZzSblFEHSqBaVwD2nvJ4CuCKLyvKvwBgZm08CgfSoiTBQLm5WW9hGw==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/load-json-file": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz", + "integrity": "sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.15", + "parse-json": "^5.0.0", + "strip-bom": "^4.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "license": "MIT", + "dependencies": { + "p-defer": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mem": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz", + "integrity": "sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==", + "license": "MIT", + "dependencies": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/mem?sponsor=1" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/minimatch": { + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", + "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", + "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/ndjson": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ndjson/-/ndjson-2.0.0.tgz", + "integrity": "sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ==", + "license": "BSD-3-Clause", + "dependencies": { + "json-stringify-safe": "^5.0.1", + "minimist": "^1.2.5", + "readable-stream": "^3.6.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "ndjson": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ndjson/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ndjson/node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "license": "ISC", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", + "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-package-arg": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", + "license": "ISC", + "dependencies": { + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "node_modules/npm-package-arg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "license": "ISC" + }, + "node_modules/npm-package-arg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-packlist": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", + "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", + "license": "ISC", + "dependencies": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onetime/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "node_modules/p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-reflect": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-reflect/-/p-reflect-2.1.0.tgz", + "integrity": "sha512-paHV8NUz8zDHu5lhr/ngGWQiW067DK/+IbJ+RfZ4k+s8y4EKyYCz8pGYWjxCg35eHztpJAt+NUgvN4L+GCbPlg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-settle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/p-settle/-/p-settle-4.1.1.tgz", + "integrity": "sha512-6THGh13mt3gypcNMm0ADqVNCcYa3BK6DWsuJWFCuEKP1rpY+OKGp7gaZwVmLspmic01+fsg/fN57MfvDzZ/PuQ==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.2", + "p-reflect": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-expression-matcher": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", + "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-name/-/path-name-1.0.0.tgz", + "integrity": "sha512-/dcAb5vMXH0f51yvMuSUqFpxUcA8JelbRmE5mW/p4CUJxrNgK24IkstnV7ENtg2IDGBOu6izKTG6eilbnbNKWQ==", + "license": "MIT" + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pnpm-sync-lib": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/pnpm-sync-lib/-/pnpm-sync-lib-0.3.4.tgz", + "integrity": "sha512-ZgRR+j6B+VUrolPBswPvXBnCyxg39Zfw3ShNCTuCrOFG1V29V4EyXaA1rDDMjdhpF85QYp2NEUjeHAm02A2E/A==", + "license": "MIT", + "dependencies": { + "@pnpm/dependency-path-pnpm-v10": "npm:@pnpm/dependency-path@^1000.0.9", + "@pnpm/dependency-path-pnpm-v8": "npm:@pnpm/dependency-path@^2.1.8", + "@pnpm/dependency-path-pnpm-v9": "npm:@pnpm/dependency-path@^5.1.7", + "@pnpm/lockfile-types-pnpm-lock-v6": "npm:@pnpm/lockfile-types@^5.1.5", + "@pnpm/lockfile.types-pnpm-lock-v9": "npm:@pnpm/lockfile.types@^1001.0.8", + "yaml": "^2.8.3" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/ramda": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.2.tgz", + "integrity": "sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA==", + "license": "MIT" + }, + "node_modules/read-package-json": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", + "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", + "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", + "license": "ISC", + "dependencies": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "node_modules/read-package-json/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/read-package-json/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/read-package-json/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-package-json/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "license": "ISC" + }, + "node_modules/read-package-json/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/read-package-json/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-package-json/node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "license": "ISC" + }, + "node_modules/read-package-json/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-package-tree": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.1.6.tgz", + "integrity": "sha512-FCX1aT3GWyY658wzDICef4p+n0dB+ENRct8E/Qyvppj6xVpOYerBHfUu7OP5Rt1/393Tdglguf5ju5DEX4wZNg==", + "deprecated": "The functionality that this package provided is now in @npmcli/arborist", + "license": "ISC", + "dependencies": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "once": "^1.3.0", + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0" + } + }, + "node_modules/read-yaml-file": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/read-yaml-file/-/read-yaml-file-2.1.0.tgz", + "integrity": "sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==", + "license": "MIT", + "dependencies": { + "js-yaml": "^4.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=10.13" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/readdir-scoped-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "license": "ISC", + "dependencies": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfc4648": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/rfc4648/-/rfc4648-1.5.4.tgz", + "integrity": "sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sort-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz", + "integrity": "sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==", + "license": "MIT", + "dependencies": { + "is-plain-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "license": "CC0-1.0" + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strnum": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz", + "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "anynum": "^1.0.1" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "7.5.20", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.20.tgz", + "integrity": "sha512-9FcyK4PA6+WbzlTM9WhQm6vB5W7cP7dUiPsv1g7YDwEQnQ1CGpK3MGlKk/ITVWMk05kHZuBhmVhiv8LZoy/PFQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/true-case-path": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-2.2.1.tgz", + "integrity": "sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q==", + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "license": "ISC", + "dependencies": { + "builtins": "^1.0.3" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/write-yaml-file": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/write-yaml-file/-/write-yaml-file-4.2.0.tgz", + "integrity": "sha512-LwyucHy0uhWqbrOkh9cBluZBeNVxzHjDaE9mwepZG3n3ZlbM4v3ndrFw51zW/NXYFFqP+QWZ72ihtLWTh05e4Q==", + "license": "MIT", + "dependencies": { + "js-yaml": "^4.0.0", + "write-file-atomic": "^3.0.3" + }, + "engines": { + "node": ">=10.13" + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml-naming": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", + "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + } + } +} diff --git a/common/docs/rfcs/images/4230/subspaces-figure-1.excalidraw.png b/common/docs/rfcs/images/4230/subspaces-figure-1.excalidraw.png new file mode 100644 index 00000000000..4ebcfd7c656 Binary files /dev/null and b/common/docs/rfcs/images/4230/subspaces-figure-1.excalidraw.png differ diff --git a/common/docs/rfcs/rfc-4230-rush-subspaces.md b/common/docs/rfcs/rfc-4230-rush-subspaces.md new file mode 100644 index 00000000000..8bb28775129 --- /dev/null +++ b/common/docs/rfcs/rfc-4230-rush-subspaces.md @@ -0,0 +1,242 @@ +# [RFC #4320](https://github.com/microsoft/rushstack/issues/4230): Rush Subspaces + +RFC maintainers: [@chengcyber](https://github.com/chengcyber), [@octogonz](https://github.com/octogonz) + +## Motivation + +The PNPM package manager provides a **workspace** feature that allows multiple projects to be managed as a group. The workspace is defined by `pnpm-workspace.yaml`, which in a Rush monorepo is generated from `rush.json`. That workspace has a **package lockfile** `pnpm-lock.yaml` that is essentially an installation plan, tracking the installed version of every dependency for your projects. + +## More than one lockfile + +When projects share a lockfile, the versions of NPM dependencies are centrally coordinated, which mostly involves choosing the right version numbers to avoid problems such as side-by-side versions, doppelgangers, and unsatisfied peer dependencies. For a crash course in these topics, see the [Lockfile Explorer docs](https://lfx.rushstack.io/). + +Centrally coordinating lockfiles brings some challenges: + +- **Consistency assumption:** In a healthy monorepo, most projects will use a consistent set of toolchains and versions, or at least a small number of such sets (perhaps one set of versions for experimental projects, one for stable projects, etc.). The `.pnpmfile.cjs` override rules can then mostly involve forcing projects to conform to one of those established sets. This assumption does not apply very well for projects whose dependencies wildly different from the rest of the monorepo, such as a project that was developed externally and then moved into the monorepo. + (This RFC was originally proposed by TikTok, whose monorepo has an abundance of such projects.) + +- **Collateral effects:** When someone updates a version and regenerates `pnpm-lock.yaml`, this may affect the version choices for shared dependencies being used by other unrelated projects. Those projects must then be tested, and fixed if a break occurs. (TikTok has many projects that rely primarily on manual testing, which is costly.) + +- **Git merge conflicts:** Git pull requests will encounter merge conflicts if multiple PRs have modified the same NPM dependencies in `pnpm-lock.yaml`. If the file is frequently churned, it can become a "mutex" that requires each PR to be built and merged one at a time, greatly reducing parallelization. + +- **Unrealistic library tests:** When publishing NPM packages for external consumption, it can be beneficial for a test project to use a real installation of the library project rather than relying on `workspace:*` linking. Using a real installation can reveal bugs such as incorrect `.npmignore` globs, that otherwise would not be discovered until after the release is published. (A prototype of this idea was implemented by the [install-test-workspace project](https://github.com/microsoft/rushstack/tree/main/build-tests/install-test-workspace) in the Rush Stack monorepo, discussed in [pnpm#3510](https://github.com/pnpm/pnpm/issues/3510).) + +All of the above problems can be reduced by breaking apart the single centrally coordinated file into multiple decoupled lockfiles; however, doing so creates new problems whose trouble increases according to the number of lockfiles. In the subsequent sections, we'll be contrasting 3 different models: + +1. **1 lockfile:** the established convention for PNPM workspaces +2. **700+ lockfiles:** our current "split workspace" fork of Rush, roughly one lockfile per project +3. **20 lockfiles:** this new "subspaces" proposal, roughly one lockfile per team + +## Current situation: A split workspace + +PNPM supports a feature called **split workspace** enabled via the `.npmrc` setting [shared-workspace-lockfile=false](https://pnpm.io/npmrc#shared-workspace-lockfile). With this model, every project gets its own `pnpm-lock.yaml` file, and `workspace:*` dependencies are installed as symlinks pointing to the corresponding library project folder. Such links are equivalent to what `npm link` would create, and therefore do not correctly satisfy `package.json` dependencies. For that reason PNPM has deprecated this feature. Nonetheless, TikTok has been using it privately via a forked version of Rush tracked by [Rush Stack PR #3481 (split workspace)](https://github.com/microsoft/rushstack/pull/3481). Our fork adapts Rush to support multiple lockfiles while also preserving the usual "common" lockfile, with the goal that over time split lockfiles would be eliminated by eventually migrating all projects into the common lockfile. + +> Note that with the "split workspace" feature there is still only one `pnpm-workspace.yaml` file, so this terminology is a bit misleading -- the lockfile is being split, not the workspace file. + +The split workspace feature has two major drawbacks: + +1. **Not scalable:** We currently have over 700 split lockfiles. Because each lockfile gets installed separately, our total install time is approximately 700x slower than a conventional single-lockfile monorepo. This cost is somewhat hidden in our setup because each CI pipeline only installs a small subset of lockfiles, distributed across hundreds of VMs, one for each pipeline. But even if the runtime cost is acceptable, consuming so many VM resources is not financially acceptable. + +2. **Incorrect installation model:** As mentioned, this installation does not correctly satisfy `package.json` version requirements. For example, if `my-app` depends on `react@16.0.0` and `my-library` also depends on `react@16.0.0`, two distinct copies of `react` will be installed in the `node_modules` folder, which we call a **split workspace doppelganger.** This happens because each `pnpm-lock.yaml` is processed essentially as an independent installation. Attempts to fix this problem are equivalent to reverting to a centralized lockfile. + +For these reasons, the Rush maintainers have been reluctant to accept **PR #3481** as an official feature. + +## Subspaces (formerly "injected workspaces") + +Let's propose a new feature called **subspaces** that divides the workspace into named groups of projects. (In earlier discussions, we called this same feature "injected workspaces.") Each project belongs to exactly one subspace, and each subspace has one lockfile and associated configuration such as `.pnpmfile.cjs`, `common-versions.json`, etc. This can solve both of the problems identified above: + +1. **Scalable:** Whereas the split workspace feature introduced one lockfile for every project, subspaces allow splitting conservatively, according to meaningful purposes. For example, we might define one subspace for "bleeding edge projects," one for "testing libraries," and one for "everything else." Or perhaps one subspace per team. A large monrepo could have 20 subspaces but not 700+. + +2. **Correct installation model:** When projects belong to separate lockfiles, instead of treating `workspace:*` as a blind `npm link` into a separate universe of versions, subspaces will instead perform an "injected" install. This terminology comes from PNPM's [injected: true](https://pnpm.io/package_json#dependenciesmetainjected) feature. It simulates what would happen if the library project was first published to an NPM registry (for example [Verdaccio](https://verdaccio.org/) on `localhost`) and then installed normally by the dependent project. Rush's `install-test-workspace` project achieves the same result by running `pnpm pack` in the library folder to produce a tarball, then using `.pnpmfile.cjs` to replace `workspace:*` with a `file:` reference to that tarball. + +### Observation #1: We need a postbuild event + +Whichever way that injected installs are implemented, an important consequence is that the library project gets copied instead of symlinked into the consumer's `node_modules` folder. This fundamentally changes the developer workflow: + +A conventional workflow only needs to perform `rush install` once: + +```bash +# 1. this will link my-app/node_modules/my-lib --> libraries/my-lib +rush install + +# 2. Make some changes to my-lib, which is a dependency of my-app + +# 3. Rebuild my-lib and my-app +rush build --to my-app + +# 4. everything is now good, repeat from step 2 +``` + +Whenever `my-lib` is modified and rebuilt, `my-app` automatically reflects those changes, because `my-app/node_modules/my-lib` is a symlink pointing to the build outputs. By contrast, with an injected install, step #1 makes a copy of `libraries/my-lib`, which does not update automatically. In step #3 we must redo this copy, and copying must occur AFTER `my-lib` is built, but BEFORE `my-app` is built. + +How to accomplish that? It implies a new project lifecycle event such as **postbuild** (for `my-lib`) or **prebuild** (for `my-app`). + +- **prebuild challenges:** If each project syncs its injected folders before building, then the main problem is change detection. "Building" could mean any operation in that folder, for example any `npm run do-something` command, and such commands can be chained together. Efficient filesystem change detection is very difficult without a live process such as [chokidar](https://www.npmjs.com/package/chokidar) or [watchman](https://facebook.github.io/watchman/), and every such project needs this logic. With PNPM symlinking, two different projects may have an injected `node_modules` subfolder that ends up symlinking to the same final target; in this case, a mutex may be required to prevent two concurrent **prebuild** actions from overwriting each other's outputs. + +- **postbuild challenges:** On the other hand, if the library itself updates all of its injected copies after building, then watching is not necessary; it's relatively easy to know when the library has finished building. The mutex problem is also simpler, or avoided entirely if we don't allow concurrent builds in the same folder. The main challenge is registering/unregistering the folders to be updated, since in theory any PNPM workspace could introduce a new injected install relationship, or the project folder might get moved, or abandoned but left on disk. + +Our proposal chooses the "postbuild" approach because it seems to be easier to implement and more efficient for Rush's use case, but perhaps ultimately both approaches can be supported. + +This is a nontrivial change: PNPM's "injected" feature is not widely used today, for the exact reason that it provides no event for updating the injected copies, and thus is only practical for non-built projects such as plain .js source files without any transformations. The PNPM maintainers perhaps hesitated to introduce such an event as it is unconventional and may break assumptions of existing tools. Rush monorepos are in a better position to adopt such a model, given to our focus on centrally managed monorepos with a more formalized structure. + +### Observation #2: Subspace topology is surprisingly flexible + +Consider the following diagram: + + + +NPM package dependencies must form a directed graph without cycles, and this determines build order. This suggests that our lockfiles should also avoid cycles: for example, if `S1` depends on `S2` via `A->B` then perhaps we should not allow `S2` to depend on `S1` via `C->D`. Surprisingly, it turns out that this constraint is unnecessary. Injected dependencies are installed as if the tarball was fetched from an NPM registry, and recall that NPM registries store `package.json` files but not lockfiles. In this way each subspace lockfile is essentially a self-contained installation plan that gets generated based on the `package.json` file of the library project, but without ever consulting the other lockfile. Thus, the above diagram poses no problems. The project build order must of course still follow the directed acyclic graph, with the "postbuild" event copying the package contents. + +### Observation #3: Which dependencies to inject? + +In PNPM's implementation, there is only one lockfile, and `injected: true` is manually configured in `package.json` for specific dependency package names. How should an engineer know when to enable this setting? In other words, when should a `workspace:*` dependency get installed via injecting instead of folder symlinking? + +As a clue to this problem, recall that PNPM's installation model has a longstanding limitation that `workspace:*` dependencies do not correctly satisfy peer dependencies, because peer dependencies are satisfied by making copies of the package folder (**peer doppelgangers**). In practice this can usually be mitigated for example by enforcing consistent versions across the monorepo, or by using Webpack aliases to override module resolution, but these mitigations are hacks. The `injected: true` feature originally arose as a correct solution. + +Here is the complete list of cases where injected copying is required (assuming we are unwilling to mitigate the problem in some other way): + +1. If a local project depends on another local project via `workspace:*` and needs to satisfy a peer dependency (including implicit peer dependencies resulting from transitive dependencies) +2. In our new subspaces proposal, injecting is required wherever a `workspace:*` dependency refers to a project in a separate subspace +3. Even if it is not theoretically required, injecting can be enabled manually for more accurate testing of published libraries (the `install-test-workspace` scenario mentioned earlier) + +Note that cases #1 and #2 could be automatically inferred -- we don't really need to require engineers to manually configure an `injected: true` setting. In fact it would not be theoretically incorrect to always inject every dependency, except that in practice copying is significantly more expensive than symlinking, and of course it also requires our unconventional "postbuild" lifecycle event. + +### Observation #4: Two entirely independent features + +Thinking more deeply about that last point, we are proposing two entirely separate features: + +1. Multiple lockfiles which are defined using subspaces +2. Injected installation with a "postbuild" lifecycle event to that updates the folder copies under `node_modules` + +Each feature could be used by itself: + +- **#1 without #2:** Subspaces could be used without injected installation, instead handling `workspace:*` by creating simple symlinks as was done with the split workspace feature. This is undesirable because it produces an incorrect solution. But we should implement it, since it will help with migrating from a split workspace, by allowing split lockfiles to be replaced by equivalent subspaces. +- **#2 without #1:** Injected installation could be used without subspaces, as exemplified by PNPM's `injected: true` feature. We should support this in the final design, however doing so probably requires designing config files and policies to manage such settings in a large scale problem domain. In order to postpone that work, for our initial implementation we will make a simplifying assumption: + +_**Initial implementation:** A dependency will be injected if-and-only-if it is a `workspace:*` reference that refers to a project external to the lockfile/subspace that is being installed._ + +## Design Details + +### Configuring subspaces + +Subspaces will be enabled using a new config file `common/config/rush/subspaces.json`, whose format will be: + +**common/config/rush/subspaces.json** + +```js +{ + "useSubspaces": true, + + // Names must be lowercase and separated by dashes. + // To avoid mistakes, common/config/subspaces/ subfolder + // cannot be used unless its name appears in this array. + "subspaceNames": [ "default", "react19", "install-test" ] +} +``` + +The lockfile and associated config files for each subspace will be `common/config/subspaces` folder: + +``` +common/config/subspaces//pnpm-lock.yaml +common/config/subspaces//.pnpmfile.cjs +common/config/subspaces//.npmrc +common/config/subspaces//common-versions.json +``` + +Subspaces will also allow for a global configuration file for npmrc settings to apply for all subspaces. This global configuration file will be located in the common rush directory: `common/config/rush/.npmrc-global` + +As noted in the [PR #3481 discussion](https://github.com/microsoft/rushstack/pull/3481#discussion_r901277915), Rush's current strategy of installing directly into `common/temp` makes it difficult to introduce additional PNPM installations without phantom dependency folders. To address this problem, when `useSubspaces=true`, the top-level `common/temp/node_modules` folder will not be created at all. Instead, lockfiles will get installed to subfolders with the naming pattern `common/temp/subspaces//`. + +Rush projects will be mapped to a subspace using a new project-specific field in rush.json: + +**rush.json** + +```js +"projects": [ + "my-project": { + "packageName": "@acme/my-project", + "projectFolder": "apps/my-project", + "subspace": "react18" + } +] +``` + +If the `"subspaceNames"` array in **subspaces.json** includes the name `"default"`, then the `"subspace"` field can be omitted for **rush.json** projects; in that case the project will be mapped to the default subspace. + +### The pnpm-sync command + +We propose to introduce a new command line tool called `pnpm-sync` that should be invoked for any library projects that have been installed as an injected dependency. Doing so updates the installed copies of this library's outputs. This command should be invoked whenever the library project has been rebuilt in any way, at the end of that operation, and before building any dependent projects. The command is so-named because we eventually hope to contribute it back to the PNPM project as a package manager feature, rather than making it a Rush-specific tool. + +In a vanilla PNPM workspace, we could introduce `"postbuild"` as an actual [NPM lifecycle event](https://docs.npmjs.com/cli/v6/using-npm/scripts) to invoke `pnpm-sync`: + +**package.json** + +```js +{ + "name": "my-library", + "version": "1.0.0", + "scripts": { + "build": "heft build --clean", + "postbuild": "pnpm-sync" + } + . . . +``` + +In a Rush monorepo, it is probably better invoked via a dedicated [Rush phase](https://rushjs.io/pages/maintainer/phased_builds/). + +The `pnpm-sync` command will perform the following operations: + +1. Look for a machine-generated file `/node_modules/.pnpm-sync.json` which contains an inventory of injected folders to be updated. In our initial implementation, this file will get generated by `rush install` or `rush update`. Later, `pnpm install` will manage it natively. +2. Calculate the list of files to be copied, using the same logic as `pnpm pack` which consults the [.npmignore](https://docs.npmjs.com/cli/v7/using-npm/developers#keeping-files-out-of-your-package) file and/or `files` field of **package.json**. +3. Copy those files into each target folder. + +Here's a suggested format for `.pnpm-sync.json`: + +**<project-folder>/node_modules/.pnpm-sync.json** + +```js +{ + "postbuildInjectedCopy": { + /** + * The project folder to be copied, relative to the folder containing ".pnpm-sync.json". + * The "pnpm-sync" command will look for package.json and .npmignore in this folder + * and apply the same filtering as "pnpm pack". + */ + "sourceFolder": "../..", + + "targetFolders": [ + { + /** + * The target path containing an injected copy that "pnpm-sync" should update. + * This path is relative to the folder containing pnpm-sync.json, and typically + * should point into the physical ".pnpm" subfolder for a given PNPM lockfile. + */ + "folderPath": "../../node_modules/.pnpm/file+..+shared+my-library/node_modules/my-library" + }, + { + // Here's an example for a hypothetical peer doppelganger of our "my-library" + "folderPath": "../../node_modules/.pnpm/file+..+shared+my-library_react@16.14.0/node_modules/my-library" + } + ] + } +} +``` + +### rush-inject-workspace-prototype repository + +[@chengcyber](https://github.com/chengcyber) has created this repository to help with studying `pnpm-sync` folder structures: + +https://github.com/chengcyber/rush-inject-workspace-prototype + +It illustrates how two PNPM workspaces could be configured to automatically perform injection for cross-space dependencies, by using `.pnpmfile.cjs` to automatically rewrite `workspace:*` to links to `file:` links, similar to the approach of his **PR #3481**. It also illustrates how this might be adapted to a Rush feature. He found that PNPM 6 has different handling of `file:` from later versions of PNPM. + +## Interaction with other Rush features + +Our experience with [Rush Stack PR #3481 (split workspace)](https://github.com/microsoft/rushstack/pull/3481) found that operational changes to `pnpm install` have relatively little impact on other Rush features. For example: + +- The `rush build` cache works with **PR #3481** and correctly calculates cache keys based on `node_modules` dependencies. +- `rush publish` does some rewriting of `workspace:*` dependencies, but he heuristic that it uses does not seem to assume that the referenced project is really in the same **pnpm-workspace.yaml** file. +- `rush deploy` should work essentially the with injected installations for subspaces, since the underlying [@rushstack/package-extractor](https://github.com/microsoft/rushstack/tree/main/libraries/package-extractor) engine is driven by Node.js module resolution, and is largely independent of how those `node_modules` folders or symlinks were created. +- **PR #3481** integrated with Rush [project selectors](https://rushjs.io/pages/developer/selecting_subsets/), for example `rush list --only split:true`. For subspaces, we can implement something similar, for example `rush list --only space:my-subspace-name`. + +**PR #3481** did not attempt to apply Rush policies to projects with split lockfile; policies only applied to the so-called "common" lockfile. Generalizing these policies across subspaces will be nontrivial work, so we've proposed to implement that as a secondary stage of work. diff --git a/common/git-hooks/pre-commit b/common/git-hooks/pre-commit old mode 100644 new mode 100755 index 4575c83ab76..ecda2ba7104 --- a/common/git-hooks/pre-commit +++ b/common/git-hooks/pre-commit @@ -1,9 +1,9 @@ -#!/bin/sh -# Called by "git commit" with no arguments. The hook should -# exit with non-zero status after issuing an appropriate message if -# it wants to stop the commit. - -# Invoke the "rush prettier" custom command to reformat files whenever they -# are committed. The command is defined in common/config/rush/command-line.json -# and uses the "rush-prettier" autoinstaller. -node common/scripts/install-run-rush.js prettier || exit $? +#!/bin/sh +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. + +# Invoke the "rush prettier" custom command to reformat files whenever they +# are committed. The command is defined in common/config/rush/command-line.json +# and uses the "rush-prettier" autoinstaller. +node common/scripts/install-run-rush.js prettier || exit $? diff --git a/common/reviews/api/api-documenter.api.md b/common/reviews/api/api-documenter.api.md index d7d7a641340..83a4d498dae 100644 --- a/common/reviews/api/api-documenter.api.md +++ b/common/reviews/api/api-documenter.api.md @@ -4,8 +4,8 @@ ```ts -import { ApiItem } from '@microsoft/api-extractor-model'; -import { ApiModel } from '@microsoft/api-extractor-model'; +import type { ApiItem } from '@microsoft/api-extractor-model'; +import type { ApiModel } from '@microsoft/api-extractor-model'; // @public export interface IApiDocumenterPluginManifest { diff --git a/common/reviews/api/api-extractor-model.api.md b/common/reviews/api/api-extractor-model.api.md index dd3f5dab530..24a490492dc 100644 --- a/common/reviews/api/api-extractor-model.api.md +++ b/common/reviews/api/api-extractor-model.api.md @@ -48,13 +48,13 @@ export namespace ApiAbstractMixin { // @public export class ApiCallSignature extends ApiCallSignature_base { constructor(options: IApiCallSignatureOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) static getContainerKey(overloadIndex: number): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; } @@ -63,22 +63,22 @@ export class ApiCallSignature extends ApiCallSignature_base { // @public export class ApiClass extends ApiClass_base { constructor(options: IApiClassOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; readonly extendsType: HeritageType | undefined; // (undocumented) static getContainerKey(name: string): string; get implementsTypes(): ReadonlyArray; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; // Warning: (ae-forgotten-export) The symbol "DeserializerContext" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "IApiClassJson" needs to be exported by the entry point index.d.ts // - // @override (undocumented) + // (undocumented) static onDeserializeInto(options: Partial, context: DeserializerContext, jsonObject: IApiClassJson): void; - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; } @@ -87,13 +87,13 @@ export class ApiClass extends ApiClass_base { // @public export class ApiConstructor extends ApiConstructor_base { constructor(options: IApiConstructorOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) static getContainerKey(overloadIndex: number): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; } @@ -102,13 +102,13 @@ export class ApiConstructor extends ApiConstructor_base { // @public export class ApiConstructSignature extends ApiConstructSignature_base { constructor(options: IApiConstructSignatureOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) static getContainerKey(overloadIndex: number): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; } @@ -122,9 +122,9 @@ export class ApiDeclaredItem extends ApiDocumentedItem { getExcerptWithModifiers(): string; // Warning: (ae-forgotten-export) The symbol "IApiDeclaredItemJson" needs to be exported by the entry point index.d.ts // - // @override (undocumented) + // (undocumented) static onDeserializeInto(options: Partial, context: DeserializerContext, jsonObject: IApiDeclaredItemJson): void; - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; get sourceLocation(): SourceLocation; } @@ -132,11 +132,11 @@ export class ApiDeclaredItem extends ApiDocumentedItem { // @public export class ApiDocumentedItem extends ApiItem { constructor(options: IApiDocumentedItemOptions); - // @override (undocumented) + // (undocumented) static onDeserializeInto(options: Partial, context: DeserializerContext, jsonObject: IApiItemJson): void; // Warning: (ae-forgotten-export) The symbol "IApiDocumentedItemJson" needs to be exported by the entry point index.d.ts // - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; // (undocumented) get tsdocComment(): tsdoc.DocComment | undefined; @@ -147,12 +147,12 @@ export class ApiDocumentedItem extends ApiItem { // @public export class ApiEntryPoint extends ApiEntryPoint_base { constructor(options: IApiEntryPointOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; get importPath(): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; } @@ -161,17 +161,17 @@ export class ApiEntryPoint extends ApiEntryPoint_base { // @public export class ApiEnum extends ApiEnum_base { constructor(options: IApiEnumOptions); - // @override (undocumented) + // (undocumented) addMember(member: ApiEnumMember): void; - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) static getContainerKey(name: string): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; - // @override (undocumented) + // (undocumented) get members(): ReadonlyArray; } @@ -180,13 +180,13 @@ export class ApiEnum extends ApiEnum_base { // @public export class ApiEnumMember extends ApiEnumMember_base { constructor(options: IApiEnumMemberOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) static getContainerKey(name: string): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; } @@ -196,7 +196,7 @@ export function ApiExportedMixin(baseCla // @public export interface ApiExportedMixin extends ApiItem { readonly isExported: boolean; - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; } @@ -210,13 +210,13 @@ export namespace ApiExportedMixin { // @public export class ApiFunction extends ApiFunction_base { constructor(options: IApiFunctionOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) static getContainerKey(name: string, overloadIndex: number): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; } @@ -225,13 +225,13 @@ export class ApiFunction extends ApiFunction_base { // @public export class ApiIndexSignature extends ApiIndexSignature_base { constructor(options: IApiIndexSignatureOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) static getContainerKey(overloadIndex: number): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; } @@ -243,7 +243,7 @@ export interface ApiInitializerMixin extends ApiItem { readonly initializerExcerpt?: Excerpt; // Warning: (ae-forgotten-export) The symbol "IApiInitializerMixinJson" needs to be exported by the entry point index.d.ts // - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; } @@ -257,20 +257,20 @@ export namespace ApiInitializerMixin { // @public export class ApiInterface extends ApiInterface_base { constructor(options: IApiInterfaceOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; get extendsTypes(): ReadonlyArray; // (undocumented) static getContainerKey(name: string): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; // Warning: (ae-forgotten-export) The symbol "IApiInterfaceJson" needs to be exported by the entry point index.d.ts // - // @override (undocumented) + // (undocumented) static onDeserializeInto(options: Partial, context: DeserializerContext, jsonObject: IApiInterfaceJson): void; - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; } @@ -319,7 +319,7 @@ export interface ApiItemContainerMixin extends ApiItem { // @internal _getMergedSiblingsForMember(memberApiItem: ApiItem): ReadonlyArray; readonly preserveMemberOrder: boolean; - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; tryGetMemberByKey(containerKey: string): ApiItem | undefined; } @@ -378,13 +378,13 @@ export enum ApiItemKind { // @public export class ApiMethod extends ApiMethod_base { constructor(options: IApiMethodOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) static getContainerKey(name: string, isStatic: boolean, overloadIndex: number): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; } @@ -393,13 +393,13 @@ export class ApiMethod extends ApiMethod_base { // @public export class ApiMethodSignature extends ApiMethodSignature_base { constructor(options: IApiMethodSignatureOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) static getContainerKey(name: string, overloadIndex: number): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; } @@ -408,13 +408,13 @@ export class ApiMethodSignature extends ApiMethodSignature_base { // @public export class ApiModel extends ApiModel_base { constructor(); - // @override (undocumented) + // (undocumented) addMember(member: ApiPackage): void; - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; // (undocumented) loadPackage(apiJsonFilename: string): ApiPackage; @@ -431,7 +431,7 @@ export function ApiNameMixin(baseClass: // @public export interface ApiNameMixin extends ApiItem { readonly name: string; - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; } @@ -445,13 +445,13 @@ export namespace ApiNameMixin { // @public export class ApiNamespace extends ApiNamespace_base { constructor(options: IApiNamespaceOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) static getContainerKey(name: string): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; } @@ -461,7 +461,7 @@ export function ApiOptionalMixin(baseCla // @public export interface ApiOptionalMixin extends ApiItem { readonly isOptional: boolean; - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; } @@ -475,23 +475,23 @@ export namespace ApiOptionalMixin { // @public export class ApiPackage extends ApiPackage_base { constructor(options: IApiPackageOptions); - // @override (undocumented) + // (undocumented) addMember(member: ApiEntryPoint): void; - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) get entryPoints(): ReadonlyArray; // (undocumented) findEntryPointsByPath(importPath: string): ReadonlyArray; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; // (undocumented) static loadFromJsonFile(apiJsonFilename: string): ApiPackage; // Warning: (ae-forgotten-export) The symbol "IApiPackageJson" needs to be exported by the entry point index.d.ts // - // @override (undocumented) + // (undocumented) static onDeserializeInto(options: Partial, context: DeserializerContext, jsonObject: IApiPackageJson): void; // (undocumented) get projectFolderUrl(): string | undefined; @@ -521,13 +521,13 @@ export namespace ApiParameterListMixin { // @public export class ApiProperty extends ApiProperty_base { constructor(options: IApiPropertyOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) static getContainerKey(name: string, isStatic: boolean): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; } @@ -539,23 +539,23 @@ export class ApiPropertyItem extends ApiPropertyItem_base { get isEventProperty(): boolean; // Warning: (ae-forgotten-export) The symbol "IApiPropertyItemJson" needs to be exported by the entry point index.d.ts // - // @override (undocumented) + // (undocumented) static onDeserializeInto(options: Partial, context: DeserializerContext, jsonObject: IApiPropertyItemJson): void; readonly propertyTypeExcerpt: Excerpt; - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; } // @public export class ApiPropertySignature extends ApiPropertyItem { constructor(options: IApiPropertySignatureOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) static getContainerKey(name: string): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; } @@ -565,7 +565,7 @@ export function ApiProtectedMixin(baseCl // @public export interface ApiProtectedMixin extends ApiItem { readonly isProtected: boolean; - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; } @@ -595,7 +595,7 @@ export function ApiReleaseTagMixin(baseC // @public export interface ApiReleaseTagMixin extends ApiItem { readonly releaseTag: ReleaseTag; - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; } @@ -612,7 +612,7 @@ export interface ApiReturnTypeMixin extends ApiItem { readonly returnTypeExcerpt: Excerpt; // Warning: (ae-forgotten-export) The symbol "IApiReturnTypeMixinJson" needs to be exported by the entry point index.d.ts // - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; } @@ -627,7 +627,7 @@ export function ApiStaticMixin(baseClass // @public export interface ApiStaticMixin extends ApiItem { readonly isStatic: boolean; - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; } @@ -641,19 +641,19 @@ export namespace ApiStaticMixin { // @public export class ApiTypeAlias extends ApiTypeAlias_base { constructor(options: IApiTypeAliasOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) static getContainerKey(name: string): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; // Warning: (ae-forgotten-export) The symbol "IApiTypeAliasJson" needs to be exported by the entry point index.d.ts // - // @override (undocumented) + // (undocumented) static onDeserializeInto(options: Partial, context: DeserializerContext, jsonObject: IApiTypeAliasJson): void; - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; readonly typeExcerpt: Excerpt; } @@ -678,19 +678,19 @@ export namespace ApiTypeParameterListMixin { // @public export class ApiVariable extends ApiVariable_base { constructor(options: IApiVariableOptions); - // @beta @override (undocumented) + // @beta (undocumented) buildCanonicalReference(): DeclarationReference; - // @override (undocumented) + // (undocumented) get containerKey(): string; // (undocumented) static getContainerKey(name: string): string; - // @override (undocumented) + // (undocumented) get kind(): ApiItemKind; // Warning: (ae-forgotten-export) The symbol "IApiVariableJson" needs to be exported by the entry point index.d.ts // - // @override (undocumented) + // (undocumented) static onDeserializeInto(options: Partial, context: DeserializerContext, jsonObject: IApiVariableJson): void; - // @override (undocumented) + // (undocumented) serializeInto(jsonObject: Partial): void; readonly variableTypeExcerpt: Excerpt; } diff --git a/common/reviews/api/api-extractor.api.md b/common/reviews/api/api-extractor.api.md index 9dbf65f43ff..f44ecd8e92b 100644 --- a/common/reviews/api/api-extractor.api.md +++ b/common/reviews/api/api-extractor.api.md @@ -6,14 +6,18 @@ import { EnumMemberOrder } from '@microsoft/api-extractor-model'; import { INodePackageJson } from '@rushstack/node-core-library'; +import { IRigConfig } from '@rushstack/rig-package'; import { JsonSchema } from '@rushstack/node-core-library'; import { NewlineKind } from '@rushstack/node-core-library'; import { PackageJsonLookup } from '@rushstack/node-core-library'; -import { RigConfig } from '@rushstack/rig-package'; -import * as tsdoc from '@microsoft/tsdoc'; +import { ReleaseTag } from '@microsoft/api-extractor-model'; +import type * as tsdoc from '@microsoft/tsdoc'; import { TSDocConfigFile } from '@microsoft/tsdoc-config'; import { TSDocConfiguration } from '@microsoft/tsdoc'; +// @public +export type ApiReportVariant = 'public' | 'beta' | 'alpha' | 'complete'; + // @public export class CompilerState { static create(extractorConfig: ExtractorConfig, options?: ICompilerStateCreateOptions): CompilerState; @@ -21,9 +25,10 @@ export class CompilerState { } // @public -export const enum ConsoleMessageId { +export enum ConsoleMessageId { ApiReportCopied = "console-api-report-copied", ApiReportCreated = "console-api-report-created", + ApiReportDiff = "console-api-report-diff", ApiReportFolderMissing = "console-api-report-folder-missing", ApiReportNotCopied = "console-api-report-not-copied", ApiReportUnchanged = "console-api-report-unchanged", @@ -32,6 +37,7 @@ export const enum ConsoleMessageId { FoundTSDocMetadata = "console-found-tsdoc-metadata", Preamble = "console-preamble", UsingCustomTSDocConfig = "console-using-custom-tsdoc-config", + WritingApiReport = "console-writing-api-report", WritingDocModelFile = "console-writing-doc-model-file", WritingDtsRollup = "console-writing-dts-rollup" } @@ -44,7 +50,7 @@ export class Extractor { static get version(): string; } -// @public +// @public @sealed export class ExtractorConfig { readonly alphaTrimmedFilePath: string; readonly apiJsonFilePath: string; @@ -52,7 +58,8 @@ export class ExtractorConfig { readonly apiReportIncludeForgottenExports: boolean; readonly betaTrimmedFilePath: string; readonly bundledPackages: string[]; - readonly docModelEnabled: boolean; + // @beta + readonly docModelGenerationOptions: IApiModelGenerationOptions | undefined; readonly docModelIncludeForgottenExports: boolean; readonly enumMemberOrder: EnumMemberOrder; static readonly FILENAME: 'api-extractor.json'; @@ -74,10 +81,16 @@ export class ExtractorConfig { readonly projectFolder: string; readonly projectFolderUrl: string | undefined; readonly publicTrimmedFilePath: string; - readonly reportFilePath: string; - readonly reportTempFilePath: string; + readonly reportConfigs: readonly IExtractorConfigApiReport[]; + // @deprecated + get reportFilePath(): string; + readonly reportFolder: string; + // @deprecated + get reportTempFilePath(): string; + readonly reportTempFolder: string; readonly rollupEnabled: boolean; readonly skipLibCheck: boolean; + readonly tagsToReport: Readonly>; readonly testMode: boolean; static tryLoadForFolder(options: IExtractorConfigLoadForFolderOptions): IExtractorConfigPrepareOptions | undefined; readonly tsconfigFilePath: string; @@ -91,7 +104,7 @@ export class ExtractorConfig { } // @public -export const enum ExtractorLogLevel { +export enum ExtractorLogLevel { Error = "error", Info = "info", None = "none", @@ -122,7 +135,7 @@ export class ExtractorMessage { } // @public -export const enum ExtractorMessageCategory { +export enum ExtractorMessageCategory { Compiler = "Compiler", Console = "console", Extractor = "Extractor", @@ -130,7 +143,7 @@ export const enum ExtractorMessageCategory { } // @public -export const enum ExtractorMessageId { +export enum ExtractorMessageId { CyclicInheritDoc = "ae-cyclic-inherit-doc", DifferentReleaseTags = "ae-different-release-tags", ExtraReleaseTag = "ae-extra-release-tag", @@ -144,6 +157,7 @@ export const enum ExtractorMessageId { PreapprovedBadReleaseTag = "ae-preapproved-bad-release-tag", PreapprovedUnsupportedType = "ae-preapproved-unsupported-type", SetterWithDocs = "ae-setter-with-docs", + Undocumented = "ae-undocumented", UnresolvedInheritDocBase = "ae-unresolved-inheritdoc-base", UnresolvedInheritDocReference = "ae-unresolved-inheritdoc-reference", UnresolvedLink = "ae-unresolved-link", @@ -162,6 +176,11 @@ export class ExtractorResult { readonly warningCount: number; } +// @beta (undocumented) +export interface IApiModelGenerationOptions { + releaseTagsToTrim: Set; +} + // @public export interface ICompilerStateCreateOptions { additionalEntryPoints?: string[]; @@ -175,6 +194,8 @@ export interface IConfigApiReport { reportFileName?: string; reportFolder?: string; reportTempFolder?: string; + reportVariants?: ApiReportVariant[]; + tagsToReport?: Readonly>; } // @public @@ -190,6 +211,7 @@ export interface IConfigDocModel { enabled: boolean; includeForgottenExports?: boolean; projectFolderUrl?: string; + releaseTagsToTrim?: ReleaseTagForTrim[]; } // @public @@ -238,10 +260,16 @@ export interface IConfigTsdocMetadata { tsdocMetadataFilePath?: string; } +// @public +export interface IExtractorConfigApiReport { + fileName: string; + variant: ApiReportVariant; +} + // @public export interface IExtractorConfigLoadForFolderOptions { packageJsonLookup?: PackageJsonLookup; - rigConfig?: RigConfig; + rigConfig?: IRigConfig; startingFolder: string; } @@ -261,6 +289,7 @@ export interface IExtractorInvokeOptions { compilerState?: CompilerState; localBuild?: boolean; messageCallback?: (message: ExtractorMessage) => void; + printApiReportDiff?: boolean; showDiagnostics?: boolean; showVerboseMessages?: boolean; typescriptCompilerFolder?: string; @@ -278,4 +307,7 @@ export interface IExtractorMessagesConfig { tsdocMessageReporting?: IConfigMessageReportingTable; } +// @public +export type ReleaseTagForTrim = '@internal' | '@alpha' | '@beta' | '@public'; + ``` diff --git a/common/reviews/api/credential-cache.api.md b/common/reviews/api/credential-cache.api.md new file mode 100644 index 00000000000..f6563cc246a --- /dev/null +++ b/common/reviews/api/credential-cache.api.md @@ -0,0 +1,50 @@ +## API Report File for "@rushstack/credential-cache" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public (undocumented) +export class CredentialCache implements Disposable { + // (undocumented) + [Symbol.dispose](): void; + // (undocumented) + deleteCacheEntry(cacheId: string): void; + // (undocumented) + dispose(): void; + // (undocumented) + static initializeAsync(options: ICredentialCacheOptions): Promise; + // (undocumented) + saveIfModifiedAsync(): Promise; + // (undocumented) + setCacheEntry(cacheId: string, entry: ICredentialCacheEntry): void; + // (undocumented) + trimExpiredEntries(): void; + // (undocumented) + tryGetCacheEntry(cacheId: string): ICredentialCacheEntry | undefined; + // (undocumented) + static usingAsync(options: ICredentialCacheOptions, doActionAsync: (credentialCache: CredentialCache) => Promise | void): Promise; +} + +// @public (undocumented) +export interface ICredentialCacheEntry { + // (undocumented) + credential: string; + // (undocumented) + credentialMetadata?: object; + // (undocumented) + expires?: Date; +} + +// @public (undocumented) +export interface ICredentialCacheOptions { + // (undocumented) + cacheFilePath?: string; + // (undocumented) + supportEditing: boolean; +} + +// @public +export const RUSH_USER_FOLDER_NAME: '.rush-user'; + +``` diff --git a/common/reviews/api/debug-certificate-manager.api.md b/common/reviews/api/debug-certificate-manager.api.md index 1bdac84b0a4..536abe8b1f3 100644 --- a/common/reviews/api/debug-certificate-manager.api.md +++ b/common/reviews/api/debug-certificate-manager.api.md @@ -4,18 +4,20 @@ ```ts -import { ITerminal } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; // @public export class CertificateManager { - constructor(); - ensureCertificateAsync(canGenerateNewCertificate: boolean, terminal: ITerminal, generationOptions?: ICertificateGenerationOptions): Promise; + constructor(options?: ICertificateManagerOptions); + readonly certificateStore: CertificateStore; + ensureCertificateAsync(canGenerateNewCertificate: boolean, terminal: ITerminal, options?: ICertificateGenerationOptions): Promise; untrustCertificateAsync(terminal: ITerminal): Promise; + validateCertificateAsync(terminal: ITerminal, options?: ICertificateGenerationOptions): Promise; } // @public export class CertificateStore { - constructor(); + constructor(options?: ICertificateStoreOptions); get caCertificateData(): string | undefined; set caCertificateData(certificate: string | undefined); get caCertificatePath(): string; @@ -24,6 +26,8 @@ export class CertificateStore { get certificatePath(): string; get keyData(): string | undefined; set keyData(key: string | undefined); + get keyPath(): string; + get storePath(): string; } // @public @@ -39,9 +43,29 @@ export interface ICertificate { // @public export interface ICertificateGenerationOptions { + skipCertificateTrust?: boolean; subjectAltNames?: ReadonlyArray; subjectIPAddresses?: ReadonlyArray; validityInDays?: number; } +// @public +export interface ICertificateManagerOptions extends ICertificateStoreOptions { +} + +// @public +export interface ICertificateStoreOptions { + caCertificateFilename?: string; + certificateFilename?: string; + keyFilename?: string; + storePath?: string; +} + +// @public +export interface ICertificateValidationResult { + certificate?: ICertificate; + isValid: boolean; + validationMessages: string[]; +} + ``` diff --git a/common/reviews/api/hashed-folder-copy-plugin.api.md b/common/reviews/api/hashed-folder-copy-plugin.api.md index fb7ba34c4c5..a2bee927a2f 100644 --- a/common/reviews/api/hashed-folder-copy-plugin.api.md +++ b/common/reviews/api/hashed-folder-copy-plugin.api.md @@ -4,11 +4,10 @@ ```ts -import type * as webpack from 'webpack'; +import type webpack from 'webpack'; // @public (undocumented) -export class HashedFolderCopyPlugin implements webpack.Plugin { - constructor(); +export class HashedFolderCopyPlugin implements webpack.WebpackPluginInstance { // (undocumented) apply(compiler: webpack.Compiler): void; } diff --git a/common/reviews/api/heft-config-file.api.md b/common/reviews/api/heft-config-file.api.md index f73abcc5e94..f432abc2fca 100644 --- a/common/reviews/api/heft-config-file.api.md +++ b/common/reviews/api/heft-config-file.api.md @@ -4,45 +4,54 @@ ```ts -import { ITerminal } from '@rushstack/node-core-library'; -import { RigConfig } from '@rushstack/rig-package'; +import type { IRigConfig } from '@rushstack/rig-package'; +import type { ITerminal } from '@rushstack/terminal'; + +// @beta @deprecated (undocumented) +export const ConfigurationFile: typeof ProjectConfigurationFile; + +// @beta @deprecated (undocumented) +export type ConfigurationFile = ProjectConfigurationFile; // @beta (undocumented) -export class ConfigurationFile { - constructor(options: IConfigurationFileOptions); +export abstract class ConfigurationFileBase { + constructor(options: IConfigurationFileOptions); // @internal (undocumented) static _formatPathForLogging: (path: string) => string; getObjectSourceFilePath(obj: TObject): string | undefined; getPropertyOriginalValue(options: IOriginalValueOptions): TValue | undefined; - loadConfigurationFileForProjectAsync(terminal: ITerminal, projectPath: string, rigConfig?: RigConfig): Promise; - readonly projectRelativeFilePath: string; - tryLoadConfigurationFileForProjectAsync(terminal: ITerminal, projectPath: string, rigConfig?: RigConfig): Promise; + getSchemaPropertyOriginalValue(obj: TObject): string | undefined; + // (undocumented) + protected _loadConfigurationFileInnerWithCache(terminal: ITerminal, resolvedConfigurationFilePath: string, projectFolderPath: string | undefined, onConfigurationFileNotFound?: IOnConfigurationFileNotFoundCallback): TConfigurationFile; + // (undocumented) + protected _loadConfigurationFileInnerWithCacheAsync(terminal: ITerminal, resolvedConfigurationFilePath: string, projectFolderPath: string | undefined, onFileNotFound?: IOnConfigurationFileNotFoundCallback): Promise; } +// @beta +export type CustomValidationFunction = (configurationFile: TConfigurationFile, resolvedConfigurationFilePathForLogging: string, terminal: ITerminal) => boolean; + // @beta (undocumented) -export type IConfigurationFileOptions = IConfigurationFileOptionsWithJsonSchemaFilePath | IConfigurationFileOptionsWithJsonSchemaObject; +export type IConfigurationFileOptions = IConfigurationFileOptionsWithJsonSchemaFilePath | IConfigurationFileOptionsWithJsonSchemaObject; // @beta (undocumented) export interface IConfigurationFileOptionsBase { + customValidationFunction?: CustomValidationFunction; jsonPathMetadata?: IJsonPathsMetadata; - projectRelativeFilePath: string; propertyInheritance?: IPropertiesInheritance; propertyInheritanceDefaults?: IPropertyInheritanceDefaults; } // @beta (undocumented) -export interface IConfigurationFileOptionsWithJsonSchemaFilePath extends IConfigurationFileOptionsBase { - // (undocumented) - jsonSchemaObject?: never; +export type IConfigurationFileOptionsWithJsonSchemaFilePath = IConfigurationFileOptionsBase & TExtraOptions & { jsonSchemaPath: string; -} + jsonSchemaObject?: never; +}; // @beta (undocumented) -export interface IConfigurationFileOptionsWithJsonSchemaObject extends IConfigurationFileOptionsBase { +export type IConfigurationFileOptionsWithJsonSchemaObject = IConfigurationFileOptionsBase & TExtraOptions & { jsonSchemaObject: object; - // (undocumented) jsonSchemaPath?: never; -} +}; // @beta export interface ICustomJsonPathMetadata { @@ -62,6 +71,7 @@ export type IJsonPathMetadata = ICustomJsonPathMetadata | INonCustomJsonPa export interface IJsonPathMetadataResolverOptions { configurationFile: Partial; configurationFilePath: string; + projectFolderPath?: string; propertyName: string; propertyValue: string; } @@ -72,12 +82,23 @@ export interface IJsonPathsMetadata { [jsonPath: string]: IJsonPathMetadata; } +// @beta +export const InheritanceType: { + readonly append: "append"; + readonly merge: "merge"; + readonly replace: "replace"; + readonly custom: "custom"; +}; + +// @beta (undocumented) +export type InheritanceType = (typeof InheritanceType)[keyof typeof InheritanceType]; + // @beta (undocumented) -export enum InheritanceType { - append = "append", - custom = "custom", - merge = "merge", - replace = "replace" +export namespace InheritanceType { + export type append = typeof InheritanceType.append; + export type custom = typeof InheritanceType.custom; + export type merge = typeof InheritanceType.merge; + export type replace = typeof InheritanceType.replace; } // @beta @@ -85,6 +106,9 @@ export interface INonCustomJsonPathMetadata { pathResolutionMethod?: PathResolutionMethod.NodeResolve | PathResolutionMethod.nodeResolve | PathResolutionMethod.resolvePathRelativeToConfigurationFile | PathResolutionMethod.resolvePathRelativeToProjectRoot; } +// @beta +export type IOnConfigurationFileNotFoundCallback = (resolvedConfigurationFilePathForLogging: string) => string | undefined; + // @beta (undocumented) export interface IOriginalValueOptions { // (undocumented) @@ -93,6 +117,14 @@ export interface IOriginalValueOptions { propertyName: keyof TParentProperty; } +// @beta (undocumented) +export interface IProjectConfigurationFileOptions { + projectRelativeFilePath: string; +} + +// @beta +export type IProjectConfigurationFileSpecification = IConfigurationFileOptions; + // @beta (undocumented) export type IPropertiesInheritance = { [propertyName in keyof TConfigurationFile]?: IPropertyInheritance | ICustomPropertyInheritance; @@ -113,16 +145,55 @@ export interface IPropertyInheritanceDefaults { } // @beta (undocumented) -export enum PathResolutionMethod { - custom = "custom", +export class NonProjectConfigurationFile extends ConfigurationFileBase { + loadConfigurationFile(terminal: ITerminal, filePath: string): TConfigurationFile; + loadConfigurationFileAsync(terminal: ITerminal, filePath: string): Promise; + tryLoadConfigurationFile(terminal: ITerminal, filePath: string): TConfigurationFile | undefined; + tryLoadConfigurationFileAsync(terminal: ITerminal, filePath: string): Promise; +} + +// @beta +export const PathResolutionMethod: { + readonly resolvePathRelativeToConfigurationFile: "resolvePathRelativeToConfigurationFile"; + readonly resolvePathRelativeToProjectRoot: "resolvePathRelativeToProjectRoot"; + readonly NodeResolve: "NodeResolve"; + readonly nodeResolve: "nodeResolve"; + readonly custom: "custom"; +}; + +// @beta (undocumented) +export type PathResolutionMethod = (typeof PathResolutionMethod)[keyof typeof PathResolutionMethod]; + +// @beta (undocumented) +export namespace PathResolutionMethod { + export type custom = typeof PathResolutionMethod.custom; // @deprecated - NodeResolve = "NodeResolve", - nodeResolve = "nodeResolve", - resolvePathRelativeToConfigurationFile = "resolvePathRelativeToConfigurationFile", - resolvePathRelativeToProjectRoot = "resolvePathRelativeToProjectRoot" + export type NodeResolve = typeof PathResolutionMethod.NodeResolve; + export type nodeResolve = typeof PathResolutionMethod.nodeResolve; + export type resolvePathRelativeToConfigurationFile = typeof PathResolutionMethod.resolvePathRelativeToConfigurationFile; + export type resolvePathRelativeToProjectRoot = typeof PathResolutionMethod.resolvePathRelativeToProjectRoot; +} + +// @beta (undocumented) +export class ProjectConfigurationFile extends ConfigurationFileBase { + constructor(options: IProjectConfigurationFileSpecification); + loadConfigurationFileForProject(terminal: ITerminal, projectPath: string, rigConfig?: IRigConfig): TConfigurationFile; + loadConfigurationFileForProjectAsync(terminal: ITerminal, projectPath: string, rigConfig?: IRigConfig): Promise; + readonly projectRelativeFilePath: string; + tryLoadConfigurationFileForProject(terminal: ITerminal, projectPath: string, rigConfig?: IRigConfig): TConfigurationFile | undefined; + tryLoadConfigurationFileForProjectAsync(terminal: ITerminal, projectPath: string, rigConfig?: IRigConfig): Promise; } // @beta (undocumented) export type PropertyInheritanceCustomFunction = (currentObject: TObject, parentObject: TObject) => TObject; +// @beta +function stripAnnotations(obj: TObject): TObject; + +declare namespace TestUtilities { + export { + stripAnnotations + } +} + ``` diff --git a/common/reviews/api/heft-isolated-typescript-transpile-plugin.api.md b/common/reviews/api/heft-isolated-typescript-transpile-plugin.api.md new file mode 100644 index 00000000000..38675bb2a95 --- /dev/null +++ b/common/reviews/api/heft-isolated-typescript-transpile-plugin.api.md @@ -0,0 +1,27 @@ +## API Report File for "@rushstack/heft-isolated-typescript-transpile-plugin" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { Options } from '@swc/core'; +import { SyncWaterfallHook } from 'tapable'; +import { _TTypeScript } from '@rushstack/heft-typescript-plugin'; + +// @beta (undocumented) +export interface ISwcIsolatedTranspilePluginAccessor { + // (undocumented) + hooks: { + getSwcOptions: SyncWaterfallHook; + }; +} + +// @public (undocumented) +export type ModuleKind = keyof typeof _TTypeScript.ModuleKind; + +// @public (undocumented) +export type ScriptTarget = keyof typeof _TTypeScript.ScriptTarget; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/common/reviews/api/heft-rspack-plugin.api.md b/common/reviews/api/heft-rspack-plugin.api.md new file mode 100644 index 00000000000..2d2020909d9 --- /dev/null +++ b/common/reviews/api/heft-rspack-plugin.api.md @@ -0,0 +1,67 @@ +## API Report File for "@rushstack/heft-rspack-plugin" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { AsyncParallelHook } from 'tapable'; +import type { AsyncSeriesBailHook } from 'tapable'; +import type { AsyncSeriesHook } from 'tapable'; +import type { AsyncSeriesWaterfallHook } from 'tapable'; +import type { HeftConfiguration } from '@rushstack/heft'; +import type { IHeftTaskSession } from '@rushstack/heft'; +import { rspackCore } from '@rspack/core'; +import type * as TRspack from '@rspack/core'; +import type * as TRspackDevServer from '@rspack/dev-server'; + +// @beta (undocumented) +export type IRspackConfiguration = TRspack.Configuration | TRspack.Configuration[]; + +// @beta +export interface IRspackConfigurationFnEnvironment { + heftConfiguration: HeftConfiguration; + prod: boolean; + production: boolean; + rspack: RspackCoreImport; + taskSession: IHeftTaskSession; +} + +// @beta (undocumented) +export interface IRspackConfigurationWithDevServer extends TRspack.Configuration { + // (undocumented) + devServer?: TRspackDevServer.Configuration; +} + +// @beta (undocumented) +export interface IRspackPluginAccessor { + readonly hooks: IRspackPluginAccessorHooks; + readonly parameters: IRspackPluginAccessorParameters; +} + +// @beta (undocumented) +export interface IRspackPluginAccessorHooks { + readonly onAfterConfigure: AsyncParallelHook<[IRspackConfiguration], never>; + readonly onConfigure: AsyncSeriesHook<[IRspackConfiguration], never>; + readonly onEmitStats: AsyncParallelHook<[TRspack.Stats | TRspack.MultiStats], never>; + readonly onGetWatchOptions: AsyncSeriesWaterfallHook<[ + Parameters[0], + Readonly + ], never>; + readonly onLoadConfiguration: AsyncSeriesBailHook<[], IRspackConfiguration | undefined | false>; +} + +// @beta (undocumented) +export interface IRspackPluginAccessorParameters { + readonly isServeMode: boolean; +} + +// @beta (undocumented) +export const PluginName: 'rspack-plugin'; + +// @beta (undocumented) +export type RspackCoreImport = rspackCore; + +// @beta +export const STAGE_LOAD_LOCAL_CONFIG: 1000; + +``` diff --git a/common/reviews/api/heft-typescript-plugin.api.md b/common/reviews/api/heft-typescript-plugin.api.md index cd5698fc3ac..51276bd9191 100644 --- a/common/reviews/api/heft-typescript-plugin.api.md +++ b/common/reviews/api/heft-typescript-plugin.api.md @@ -5,20 +5,44 @@ ```ts import type { HeftConfiguration } from '@rushstack/heft'; -import { ITerminal } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; +import semver from 'semver'; import { SyncHook } from 'tapable'; -import type * as TTypescript from 'typescript'; +import type * as _TTypeScript from 'typescript'; + +// @internal (undocumented) +export function _getTsconfigFilePath(heftConfiguration: HeftConfiguration, tsconfigRelativePath: string | undefined): string; + +// @internal (undocumented) +export interface _IBaseTypeScriptTool { + // (undocumented) + system: TSystem; + // Warning: (ae-forgotten-export) The symbol "ExtendedTypeScript" needs to be exported by the entry point index.d.ts + // + // (undocumented) + ts: ExtendedTypeScript; + // (undocumented) + typeScriptToolPath: string; +} // @beta (undocumented) export interface IChangedFilesHookOptions { // (undocumented) - changedFiles?: ReadonlySet; + changedFiles?: ReadonlySet<_TTypeScript.SourceFile>; // (undocumented) - program: TTypescript.Program; + program: _TTypeScript.Program; +} + +// @internal (undocumented) +export interface _ICompilerCapabilities { + incrementalProgram: boolean; + solutionBuilder: boolean; } // @beta (undocumented) export interface IEmitModuleKind { + // (undocumented) + emitModulePackageJson?: boolean; // (undocumented) jsExtensionOverride?: string; // (undocumented) @@ -27,6 +51,40 @@ export interface IEmitModuleKind { outFolderName: string; } +// @internal (undocumented) +export interface _ILoadedTypeScriptTool { + // (undocumented) + capabilities: _ICompilerCapabilities; + // (undocumented) + tool: _IBaseTypeScriptTool; + // (undocumented) + typescriptParsedVersion: semver.SemVer; + // (undocumented) + typescriptVersion: string; +} + +// @internal (undocumented) +export interface _ILoadTsconfigOptions { + // (undocumented) + tool: _IBaseTypeScriptTool; + // (undocumented) + tsCacheFilePath?: string; + // (undocumented) + tsconfigPath: string; +} + +// @internal (undocumented) +export interface _ILoadTypeScriptToolOptions { + // (undocumented) + buildProjectReferences?: boolean; + // (undocumented) + heftConfiguration: HeftConfiguration; + // (undocumented) + onlyResolveSymlinksInNodeModules?: boolean; + // (undocumented) + terminal: ITerminal; +} + // @beta (undocumented) export interface IPartialTsconfig { // (undocumented) @@ -55,6 +113,7 @@ export interface ITypeScriptConfigurationJson { buildProjectReferences?: boolean; emitCjsExtensionForCommonJS?: boolean | undefined; emitMjsExtensionForESModule?: boolean | undefined; + onlyResolveSymlinksInNodeModules?: boolean; // (undocumented) project?: string; staticAssetsToCopy?: IStaticAssetsCopyConfiguration; @@ -70,9 +129,17 @@ export interface ITypeScriptPluginAccessor { // @beta (undocumented) export function loadPartialTsconfigFileAsync(heftConfiguration: HeftConfiguration, terminal: ITerminal, typeScriptConfigurationJson: ITypeScriptConfigurationJson | undefined): Promise; +// @internal (undocumented) +export function _loadTsconfig(options: _ILoadTsconfigOptions): _TTypeScript.ParsedCommandLine; + // @beta (undocumented) export function loadTypeScriptConfigurationFileAsync(heftConfiguration: HeftConfiguration, terminal: ITerminal): Promise; +// @internal (undocumented) +export function _loadTypeScriptToolAsync(options: _ILoadTypeScriptToolOptions): Promise<_ILoadedTypeScriptTool>; + +export { _TTypeScript } + // @public export const TypeScriptPluginName: 'typescript-plugin'; diff --git a/common/reviews/api/heft-webpack4-plugin.api.md b/common/reviews/api/heft-webpack4-plugin.api.md index dfe10d7431e..65305cc3dbe 100644 --- a/common/reviews/api/heft-webpack4-plugin.api.md +++ b/common/reviews/api/heft-webpack4-plugin.api.md @@ -52,6 +52,9 @@ export interface IWebpackPluginAccessorParameters { // @public (undocumented) export const PluginName: 'webpack4-plugin'; +// @public +export const STAGE_LOAD_LOCAL_CONFIG: 1000; + // (No @packageDocumentation comment for this package) ``` diff --git a/common/reviews/api/heft-webpack5-plugin.api.md b/common/reviews/api/heft-webpack5-plugin.api.md index a239b63185e..e7212e5014b 100644 --- a/common/reviews/api/heft-webpack5-plugin.api.md +++ b/common/reviews/api/heft-webpack5-plugin.api.md @@ -7,6 +7,7 @@ import type { AsyncParallelHook } from 'tapable'; import type { AsyncSeriesBailHook } from 'tapable'; import type { AsyncSeriesHook } from 'tapable'; +import type { AsyncSeriesWaterfallHook } from 'tapable'; import type { Configuration } from 'webpack-dev-server'; import type { HeftConfiguration } from '@rushstack/heft'; import type { IHeftTaskSession } from '@rushstack/heft'; @@ -41,6 +42,7 @@ export interface IWebpackPluginAccessorHooks { readonly onAfterConfigure: AsyncParallelHook; readonly onConfigure: AsyncSeriesHook; readonly onEmitStats: AsyncParallelHook; + readonly onGetWatchOptions: AsyncSeriesWaterfallHook[0], Readonly, never>; readonly onLoadConfiguration: AsyncSeriesBailHook; } @@ -52,6 +54,9 @@ export interface IWebpackPluginAccessorParameters { // @public (undocumented) export const PluginName: 'webpack5-plugin'; +// @public +export const STAGE_LOAD_LOCAL_CONFIG: 1000; + // (No @packageDocumentation comment for this package) ``` diff --git a/common/reviews/api/heft.api.md b/common/reviews/api/heft.api.md index 7614b2d5b51..96f4281126f 100644 --- a/common/reviews/api/heft.api.md +++ b/common/reviews/api/heft.api.md @@ -4,6 +4,8 @@ ```ts +/// + import { AsyncParallelHook } from 'tapable'; import { AsyncSeriesWaterfallHook } from 'tapable'; import { CommandLineChoiceListParameter } from '@rushstack/ts-command-line'; @@ -14,28 +16,29 @@ import { CommandLineIntegerParameter } from '@rushstack/ts-command-line'; import { CommandLineParameter } from '@rushstack/ts-command-line'; import { CommandLineStringListParameter } from '@rushstack/ts-command-line'; import { CommandLineStringParameter } from '@rushstack/ts-command-line'; +import { CustomValidationFunction } from '@rushstack/heft-config-file'; +import * as fs from 'node:fs'; +import { ICustomJsonPathMetadata } from '@rushstack/heft-config-file'; +import { ICustomPropertyInheritance } from '@rushstack/heft-config-file'; +import { IJsonPathMetadata } from '@rushstack/heft-config-file'; +import { IJsonPathMetadataResolverOptions } from '@rushstack/heft-config-file'; +import { IJsonPathsMetadata } from '@rushstack/heft-config-file'; +import { InheritanceType } from '@rushstack/heft-config-file'; +import { INonCustomJsonPathMetadata } from '@rushstack/heft-config-file'; +import { IOriginalValueOptions } from '@rushstack/heft-config-file'; import { IPackageJson } from '@rushstack/node-core-library'; -import { ITerminal } from '@rushstack/node-core-library'; -import { ITerminalProvider } from '@rushstack/node-core-library'; -import { RigConfig } from '@rushstack/rig-package'; - -// @beta -export class CancellationToken { - // @internal - constructor(options?: _ICancellationTokenOptions); - get isCancelled(): boolean; - get onCancelledPromise(): Promise; -} - -// @beta -export class CancellationTokenSource { - constructor(options?: ICancellationTokenSourceOptions); - cancel(): void; - get isCancelled(): boolean; - // @internal (undocumented) - get _onCancelledPromise(): Promise; - get token(): CancellationToken; -} +import { IProjectConfigurationFileSpecification } from '@rushstack/heft-config-file'; +import { IPropertiesInheritance } from '@rushstack/heft-config-file'; +import { IPropertyInheritance } from '@rushstack/heft-config-file'; +import { IPropertyInheritanceDefaults } from '@rushstack/heft-config-file'; +import { IRigConfig } from '@rushstack/rig-package'; +import { ITerminal } from '@rushstack/terminal'; +import { ITerminalProvider } from '@rushstack/terminal'; +import type { Operation } from '@rushstack/operation-graph'; +import type { OperationGroupRecord } from '@rushstack/operation-graph'; +import { PathResolutionMethod } from '@rushstack/heft-config-file'; +import { PropertyInheritanceCustomFunction } from '@rushstack/heft-config-file'; +import type { SyncHook } from 'tapable'; export { CommandLineChoiceListParameter } @@ -53,36 +56,49 @@ export { CommandLineStringListParameter } export { CommandLineStringParameter } +declare namespace ConfigurationFile { + export { + CustomValidationFunction, + ICustomJsonPathMetadata, + ICustomPropertyInheritance, + IJsonPathMetadata, + IJsonPathMetadataResolverOptions, + IJsonPathsMetadata, + INonCustomJsonPathMetadata, + IOriginalValueOptions, + IProjectConfigurationFileSpecification, + IPropertiesInheritance, + IPropertyInheritance, + IPropertyInheritanceDefaults, + InheritanceType, + PathResolutionMethod, + PropertyInheritanceCustomFunction + } +} +export { ConfigurationFile } + // @public export type GlobFn = (pattern: string | string[], options?: IGlobOptions | undefined) => Promise; // @public (undocumented) export class HeftConfiguration { - get buildFolderPath(): string; - get cacheFolderPath(): string; + readonly buildFolderPath: string; // @internal _checkForRigAsync(): Promise; - get globalTerminal(): ITerminal; + readonly globalTerminal: ITerminal; get heftPackageJson(): IPackageJson; // @internal (undocumented) static initialize(options: _IHeftConfigurationInitializationOptions): HeftConfiguration; + readonly numberOfCores: number; get projectConfigFolderPath(): string; get projectPackageJson(): IPackageJson; - get rigConfig(): RigConfig; + get rigConfig(): IRigConfig; get rigPackageResolver(): IRigPackageResolver; + get slashNormalizedBuildFolderPath(): string; get tempFolderPath(): string; - get terminalProvider(): ITerminalProvider; -} - -// @internal -export interface _ICancellationTokenOptions { - cancellationTokenSource?: CancellationTokenSource; - isCancelled?: boolean; -} - -// @beta -export interface ICancellationTokenSourceOptions { - delayMs?: number; + readonly terminalProvider: ITerminalProvider; + tryLoadProjectConfigurationFile(options: IProjectConfigurationFileSpecification, terminal: ITerminal): TConfigFile | undefined; + tryLoadProjectConfigurationFileAsync(options: IProjectConfigurationFileSpecification, terminal: ITerminal): Promise; } // @public @@ -101,7 +117,7 @@ export interface IFileSelectionSpecifier { excludeGlobs?: string[]; fileExtensions?: string[]; includeGlobs?: string[]; - sourcePath: string; + sourcePath?: string; } // @public @@ -115,13 +131,13 @@ export interface IGlobOptions { // @internal (undocumented) export interface _IHeftConfigurationInitializationOptions { cwd: string; + numberOfCores: number; terminalProvider: ITerminalProvider; } // @public export interface IHeftDefaultParameters { readonly clean: boolean; - readonly cleanCache: boolean; readonly debug: boolean; readonly locales: Iterable; readonly production: boolean; @@ -137,8 +153,11 @@ export interface IHeftLifecycleCleanHookOptions { // @public export interface IHeftLifecycleHooks { clean: AsyncParallelHook; - // (undocumented) + phaseFinish: SyncHook; + phaseStart: SyncHook; recordMetrics: AsyncParallelHook; + taskFinish: SyncHook; + taskStart: SyncHook; toolFinish: AsyncParallelHook; toolStart: AsyncParallelHook; } @@ -149,7 +168,6 @@ export interface IHeftLifecyclePlugin extends IHeftPlugin; + // (undocumented) + consumingPhases: ReadonlySet; + // (undocumented) + dependencyPhases: ReadonlySet; + // (undocumented) + readonly phaseDescription: string | undefined; + // (undocumented) + readonly phaseName: string; + // (undocumented) + tasks: ReadonlySet; + // (undocumented) + tasksByName: ReadonlyMap; +} + +// @public (undocumented) +export interface IHeftPhaseFinishHookOptions { + // (undocumented) + operation: OperationGroupRecord; +} + +// @public +export interface IHeftPhaseOperationMetadata { + // (undocumented) + phase: IHeftPhase; +} + +// @public (undocumented) +export interface IHeftPhaseStartHookOptions { + // (undocumented) + operation: OperationGroupRecord; +} + // @public export interface IHeftPlugin { readonly accessor?: object; @@ -190,12 +250,30 @@ export interface IHeftRecordMetricsHookOptions { metricName: string; } +// @public (undocumented) +export interface IHeftTask { + // (undocumented) + readonly consumingTasks: ReadonlySet; + // (undocumented) + readonly dependencyTasks: ReadonlySet; + // (undocumented) + readonly parentPhase: IHeftPhase; + // (undocumented) + readonly taskName: string; +} + // @public export interface IHeftTaskFileOperations { copyOperations: Set; deleteOperations: Set; } +// @public (undocumented) +export interface IHeftTaskFinishHookOptions { + // (undocumented) + operation: Operation; +} + // @public export interface IHeftTaskHooks { readonly registerFileOperations: AsyncSeriesWaterfallHook; @@ -203,6 +281,14 @@ export interface IHeftTaskHooks { readonly runIncremental: AsyncParallelHook; } +// @public +export interface IHeftTaskOperationMetadata { + // (undocumented) + phase: IHeftPhase; + // (undocumented) + task: IHeftTask; +} + // @public export interface IHeftTaskPlugin extends IHeftPlugin { } @@ -210,26 +296,34 @@ export interface IHeftTaskPlugin extends IHeftPlugin void; + readonly watchFs: IWatchFileSystem; readonly watchGlobAsync: WatchGlobFn; } // @public export interface IHeftTaskSession { - readonly cacheFolderPath: string; readonly hooks: IHeftTaskHooks; readonly logger: IScopedLogger; readonly parameters: IHeftParameters; + readonly parsedCommandLine: IHeftParsedCommandLine; requestAccessToPluginByName(pluginToAccessPackage: string, pluginToAccessName: string, pluginApply: (pluginAccessor: T) => void): void; readonly taskName: string; readonly tempFolderPath: string; } +// @public (undocumented) +export interface IHeftTaskStartHookOptions { + // (undocumented) + operation: Operation; +} + // @public export interface IIncrementalCopyOperation extends ICopyOperation { onlyIfChanged?: boolean; @@ -237,6 +331,7 @@ export interface IIncrementalCopyOperation extends ICopyOperation { // @public (undocumented) export interface IMetricsData { + bootDurationMs: number; command: string; commandParameters: Record; encounteredError?: boolean; @@ -246,6 +341,7 @@ export interface IMetricsData { machineProcessor: string; machineTotalMemoryMB: number; taskTotalExecutionMs: number; + totalUptimeMs: number; } // @internal (undocumented) @@ -256,6 +352,11 @@ export interface _IPerformanceData { taskTotalExecutionMs: number; } +// @public +export interface IReaddirOptions { + withFileTypes: true; +} + // @public export interface IRigPackageResolver { // (undocumented) @@ -294,6 +395,22 @@ export interface IWatchedFileState { changed: boolean; } +// @public +export interface IWatchFileSystem { + getStateAndTrack(filePath: string): IWatchedFileState; + getStateAndTrackAsync(filePath: string): Promise; + lstat(filePath: string, callback: StatCallback): void; + lstatSync(filePath: string): fs.Stats; + readdir(filePath: string, callback: ReaddirStringCallback): void; + // (undocumented) + readdir(filePath: string, options: IReaddirOptions, callback: ReaddirDirentCallback): void; + readdirSync(filePath: string): string[]; + // (undocumented) + readdirSync(filePath: string, options: IReaddirOptions): fs.Dirent[]; + stat(filePath: string, callback: StatCallback): void; + statSync(filePath: string): fs.Stats; +} + // @internal export class _MetricsCollector { recordAsync(command: string, performanceData?: Partial<_IPerformanceData>, parameters?: Record): Promise; @@ -302,6 +419,15 @@ export class _MetricsCollector { setStartTime(): void; } +// @public +export type ReaddirDirentCallback = (error: NodeJS.ErrnoException | null, files: fs.Dirent[]) => void; + +// @public +export type ReaddirStringCallback = (error: NodeJS.ErrnoException | null, files: string[]) => void; + +// @public +export type StatCallback = (error: NodeJS.ErrnoException | null, stats: fs.Stats) => void; + // @public export type WatchGlobFn = (pattern: string | string[], options?: IGlobOptions | undefined) => Promise>; diff --git a/common/reviews/api/localization-utilities.api.md b/common/reviews/api/localization-utilities.api.md index 8496e99298c..535f11d6373 100644 --- a/common/reviews/api/localization-utilities.api.md +++ b/common/reviews/api/localization-utilities.api.md @@ -4,7 +4,9 @@ ```ts -import { ITerminal } from '@rushstack/node-core-library'; +import { IExportAsDefaultOptions } from '@rushstack/typings-generator'; +import type { ISourcePosition } from '@rushstack/typings-generator'; +import type { ITerminal } from '@rushstack/terminal'; import { ITypingsGeneratorBaseOptions } from '@rushstack/typings-generator'; import { NewlineKind } from '@rushstack/node-core-library'; import { StringValuesTypingsGenerator } from '@rushstack/typings-generator'; @@ -15,6 +17,11 @@ export function getPseudolocalizer(options: IPseudolocaleOptions): (str: string) // @public (undocumented) export type IgnoreStringFunction = (filePath: string, stringName: string) => boolean; +// @public (undocumented) +export interface IInferInterfaceNameExportAsDefaultOptions extends Omit { + inferInterfaceNameFromFilename?: boolean; +} + // @public (undocumented) export interface ILocalizationFile { // (undocumented) @@ -25,6 +32,7 @@ export interface ILocalizationFile { export interface ILocalizedString { // (undocumented) comment?: string; + sourcePosition?: ISourcePosition; // (undocumented) value: string; } @@ -78,26 +86,22 @@ export interface IPseudolocaleOptions { // @public (undocumented) export interface ITypingsGeneratorOptions extends ITypingsGeneratorBaseOptions { - // (undocumented) - exportAsDefault?: boolean; - // (undocumented) + exportAsDefault?: boolean | IExportAsDefaultOptions | IInferInterfaceNameExportAsDefaultOptions; ignoreMissingResxComments?: boolean | undefined; - // (undocumented) ignoreString?: IgnoreStringFunction; - // (undocumented) - processComment?: (comment: string | undefined, resxFilePath: string, stringName: string) => string | undefined; - // (undocumented) + processComment?: (comment: string | undefined, relativeFilePath: string, stringName: string) => string | undefined; resxNewlineNormalization?: NewlineKind | undefined; + trimmedJsonOutputFolders?: string[] | undefined; } // @public (undocumented) export function parseLocFile(options: IParseLocFileOptions): ILocalizationFile; // @public (undocumented) -export function parseLocJson({ content, filePath, ignoreString }: IParseFileOptions): ILocalizationFile; +export function parseLocJson(input: IParseFileOptions): ILocalizationFile; // @public (undocumented) -export function parseResJson({ content, ignoreString, filePath }: IParseFileOptions): ILocalizationFile; +export function parseResJson(input: IParseFileOptions): ILocalizationFile; // @public (undocumented) export function parseResx(options: IParseResxOptions): ILocalizationFile; diff --git a/common/reviews/api/lookup-by-path.api.md b/common/reviews/api/lookup-by-path.api.md new file mode 100644 index 00000000000..dec5eefc235 --- /dev/null +++ b/common/reviews/api/lookup-by-path.api.md @@ -0,0 +1,88 @@ +## API Report File for "@rushstack/lookup-by-path" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @beta +export function getFirstDifferenceInCommonNodes(options: IGetFirstDifferenceInCommonNodesOptions): string | undefined; + +// @beta +export interface IGetFirstDifferenceInCommonNodesOptions { + delimiter?: string; + equals?: (a: TItem, b: TItem) => boolean; + first: IReadonlyPathTrieNode; + prefix?: string; + second: IReadonlyPathTrieNode; +} + +// @beta +export interface ILookupByPathJson { + delimiter: string; + tree: ISerializedPathTrieNode; + values: TSerialized[]; +} + +// @beta +export interface IPrefixMatch { + index: number; + lastMatch?: IPrefixMatch; + value: TItem; +} + +// @beta +export interface IReadonlyLookupByPath extends Iterable<[string, TItem]> { + [Symbol.iterator](query?: string, delimiter?: string): IterableIterator<[string, TItem]>; + entries(query?: string, delimiter?: string): IterableIterator<[string, TItem]>; + findChildPath(childPath: string, delimiter?: string): TItem | undefined; + findChildPathFromSegments(childPathSegments: Iterable): TItem | undefined; + findLongestPrefixMatch(query: string, delimiter?: string): IPrefixMatch | undefined; + get(query: string, delimiter?: string): TItem | undefined; + getNodeAtPrefix(query: string, delimiter?: string): IReadonlyPathTrieNode | undefined; + groupByChild(infoByPath: Map, delimiter?: string): Map>; + has(query: string, delimiter?: string): boolean; + get size(): number; + toJson(serializeValue: (value: TItem) => TSerialized): ILookupByPathJson; + // (undocumented) + get tree(): IReadonlyPathTrieNode; +} + +// @beta +export interface IReadonlyPathTrieNode { + readonly children: ReadonlyMap> | undefined; + readonly value: TItem | undefined; +} + +// @beta +export interface ISerializedPathTrieNode { + children?: Record; + valueIndex?: number; +} + +// @beta +export class LookupByPath implements IReadonlyLookupByPath { + [Symbol.iterator](query?: string, delimiter?: string): IterableIterator<[string, TItem]>; + constructor(entries?: Iterable<[string, TItem]>, delimiter?: string); + clear(): this; + deleteItem(query: string, delimeter?: string): boolean; + deleteSubtree(query: string, delimeter?: string): boolean; + readonly delimiter: string; + entries(query?: string, delimiter?: string): IterableIterator<[string, TItem]>; + findChildPath(childPath: string, delimiter?: string): TItem | undefined; + findChildPathFromSegments(childPathSegments: Iterable): TItem | undefined; + findLongestPrefixMatch(query: string, delimiter?: string): IPrefixMatch | undefined; + static fromJson(json: ILookupByPathJson, deserializeValue: (serialized: TSerialized) => TItem): LookupByPath; + get(key: string, delimiter?: string): TItem | undefined; + getNodeAtPrefix(query: string, delimiter?: string): IReadonlyPathTrieNode | undefined; + groupByChild(infoByPath: Map, delimiter?: string): Map>; + has(key: string, delimiter?: string): boolean; + static iteratePathSegments(serializedPath: string, delimiter?: string): Iterable; + setItem(serializedPath: string, value: TItem, delimiter?: string): this; + setItemFromSegments(pathSegments: Iterable, value: TItem): this; + get size(): number; + toJson(serializeValue: (value: TItem) => TSerialized): ILookupByPathJson; + // (undocumented) + get tree(): IReadonlyPathTrieNode; +} + +``` diff --git a/common/reviews/api/mcp-server.api.md b/common/reviews/api/mcp-server.api.md new file mode 100644 index 00000000000..5903120d31a --- /dev/null +++ b/common/reviews/api/mcp-server.api.md @@ -0,0 +1,50 @@ +## API Report File for "@rushstack/mcp-server" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types'; +import type * as zodModule from 'zod'; + +// @public (undocumented) +export type CallToolResult = zodModule.infer; + +export { CallToolResultSchema } + +// @public +export interface IRegisterToolOptions { + // (undocumented) + description?: string; + // (undocumented) + toolName: string; +} + +// @public +export interface IRushMcpPlugin { + // (undocumented) + onInitializeAsync(): Promise; +} + +// @public +export interface IRushMcpTool = zodModule.ZodObject> { + // (undocumented) + executeAsync(input: zodModule.infer): Promise; + // (undocumented) + readonly schema: TSchema; +} + +// @public +export type RushMcpPluginFactory = (session: RushMcpPluginSession, configFile: TConfigFile | undefined) => IRushMcpPlugin; + +// @public +export abstract class RushMcpPluginSession { + // (undocumented) + abstract registerTool(options: IRegisterToolOptions, tool: IRushMcpTool): void; + // (undocumented) + readonly zod: typeof zodModule; +} + +export { zodModule } + +``` diff --git a/common/reviews/api/module-minifier.api.md b/common/reviews/api/module-minifier.api.md index 126078c517f..e4aa1c13a60 100644 --- a/common/reviews/api/module-minifier.api.md +++ b/common/reviews/api/module-minifier.api.md @@ -6,9 +6,10 @@ /// -import { MessagePort } from 'worker_threads'; import { MinifyOptions } from 'terser'; import type { RawSourceMap } from 'source-map'; +import type { ResourceLimits } from 'node:worker_threads'; +import type * as WorkerThreads from 'node:worker_threads'; // @public export function getIdentifier(ordinal: number): string; @@ -22,7 +23,9 @@ export interface ILocalMinifierOptions { // @public export interface IMinifierConnection { configHash: string; + // @deprecated (undocumented) disconnect(): Promise; + disconnectAsync(): Promise; } // @public @@ -60,7 +63,9 @@ export interface IModuleMinificationSuccessResult { // @public export interface IModuleMinifier { + // @deprecated (undocumented) connect(): Promise; + connectAsync(): Promise; minify: IModuleMinifierFunction; } @@ -75,24 +80,27 @@ export interface IWorkerPoolMinifierOptions { maxThreads?: number; terserOptions?: MinifyOptions; verbose?: boolean; + workerResourceLimits?: ResourceLimits; } // @public export class LocalMinifier implements IModuleMinifier { constructor(options: ILocalMinifierOptions); - // (undocumented) + // @deprecated (undocumented) connect(): Promise; + connectAsync(): Promise; minify(request: IModuleMinificationRequest, callback: IModuleMinificationCallback): void; } // @public export class MessagePortMinifier implements IModuleMinifier { - constructor(port: MessagePort); - // (undocumented) + constructor(port: WorkerThreads.MessagePort); + // @deprecated (undocumented) connect(): Promise; + connectAsync(): Promise; minify(request: IModuleMinificationRequest, callback: IModuleMinificationCallback): void; // (undocumented) - readonly port: MessagePort; + readonly port: WorkerThreads.MessagePort; } export { MinifyOptions } @@ -102,16 +110,18 @@ export function _minifySingleFileAsync(request: IModuleMinificationRequest, ters // @public export class NoopMinifier implements IModuleMinifier { - // (undocumented) + // @deprecated (undocumented) connect(): Promise; + connectAsync(): Promise; minify(request: IModuleMinificationRequest, callback: IModuleMinificationCallback): void; } // @public export class WorkerPoolMinifier implements IModuleMinifier { constructor(options: IWorkerPoolMinifierOptions); - // (undocumented) + // @deprecated (undocumented) connect(): Promise; + connectAsync(): Promise; // (undocumented) get maxThreads(): number; set maxThreads(threads: number); diff --git a/common/reviews/api/node-core-library.api.md b/common/reviews/api/node-core-library.api.md index 7aeb8d3a89c..bb10f9b3d72 100644 --- a/common/reviews/api/node-core-library.api.md +++ b/common/reviews/api/node-core-library.api.md @@ -6,10 +6,9 @@ /// -import * as child_process from 'child_process'; -import * as fs from 'fs'; -import { Writable } from 'stream'; -import { WritableOptions } from 'stream'; +import * as child_process from 'node:child_process'; +import * as fs from 'node:fs'; +import * as nodePath from 'node:path'; // @public export enum AlreadyExistsBehavior { @@ -26,17 +25,27 @@ export class AlreadyReportedError extends Error { } // @public -export class AnsiEscape { - static formatForTests(text: string, options?: IAnsiEscapeConvertForTestsOptions): string; - static removeCodes(text: string): string; -} +function areDeepEqual(a: TObject, b: TObject): boolean; -// @beta +// @public export class Async { - static forEachAsync(iterable: Iterable | AsyncIterable, callback: (entry: TEntry, arrayIndex: number) => Promise, options?: IAsyncParallelismOptions | undefined): Promise; - static mapAsync(iterable: Iterable | AsyncIterable, callback: (entry: TEntry, arrayIndex: number) => Promise, options?: IAsyncParallelismOptions | undefined): Promise; - static runWithRetriesAsync({ action, maxRetries, retryDelayMs }: IRunWithRetriesOptions): Promise; - static sleep(ms: number): Promise; + static forEachAsync(iterable: Iterable | AsyncIterable, callback: (entry: TEntry, arrayIndex: number) => Promise, options?: (IAsyncParallelismOptions & { + weighted?: false; + }) | undefined): Promise; + static forEachAsync(iterable: Iterable | AsyncIterable, callback: (entry: TEntry, arrayIndex: number) => Promise, options: IAsyncParallelismOptions & { + weighted: true; + }): Promise; + static getSignal(): [Promise, () => void, (err: Error) => void]; + static mapAsync(iterable: Iterable | AsyncIterable, callback: (entry: TEntry, arrayIndex: number) => Promise, options?: (IAsyncParallelismOptions & { + weighted?: false; + }) | undefined): Promise; + static mapAsync(iterable: Iterable | AsyncIterable, callback: (entry: TEntry, arrayIndex: number) => Promise, options: IAsyncParallelismOptions & { + weighted: true; + }): Promise; + static runWithRetriesAsync(input: IRunWithRetriesOptions): Promise; + static runWithTimeoutAsync(input: IRunWithTimeoutOptions): Promise; + static sleepAsync(ms: number): Promise; + static validateWeightedIterable(operation: IWeighted): void; } // @public @@ -52,91 +61,12 @@ export type Brand = T & { __brand: BrandTag; }; -// @beta -export class Colors { - // (undocumented) - static black(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static blackBackground(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static blink(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static blue(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static blueBackground(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static bold(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static cyan(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static cyanBackground(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static dim(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static gray(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static grayBackground(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static green(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static greenBackground(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static hidden(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static invertColor(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static magenta(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static magentaBackground(text: string | IColorableSequence): IColorableSequence; - // @internal - static _normalizeStringOrColorableSequence(value: string | IColorableSequence): IColorableSequence; - // (undocumented) - static red(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static redBackground(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static underline(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static white(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static whiteBackground(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static yellow(text: string | IColorableSequence): IColorableSequence; - // (undocumented) - static yellowBackground(text: string | IColorableSequence): IColorableSequence; -} - -// @beta -export enum ColorValue { - // (undocumented) - Black = 0, - // (undocumented) - Blue = 4, - // (undocumented) - Cyan = 6, - // (undocumented) - Gray = 8, - // (undocumented) - Green = 2, - // (undocumented) - Magenta = 5, - // (undocumented) - Red = 1, - // (undocumented) - White = 7, - // (undocumented) - Yellow = 3 -} - -// @beta -export class ConsoleTerminalProvider implements ITerminalProvider { - constructor(options?: Partial); - debugEnabled: boolean; - get eolCharacter(): string; - get supportsColor(): boolean; - verboseEnabled: boolean; - write(data: string, severity: TerminalProviderSeverity): void; +declare namespace Disposables { + export { + polyfillDisposeSymbols + } } +export { Disposables } // @public export enum Encoding { @@ -179,9 +109,16 @@ export class EnvironmentMap { // @public export class Executable { + static getProcessInfoById(): Map; + static getProcessInfoByIdAsync(): Promise>; + static getProcessInfoByName(): Map; + static getProcessInfoByNameAsync(): Promise>; static spawn(filename: string, args: string[], options?: IExecutableSpawnOptions): child_process.ChildProcess; static spawnSync(filename: string, args: string[], options?: IExecutableSpawnSyncOptions): child_process.SpawnSyncReturns; static tryResolve(filename: string, options?: IExecutableResolveOptions): string | undefined; + static waitForExitAsync(childProcess: child_process.ChildProcess, options: IWaitForExitWithStringOptions): Promise>; + static waitForExitAsync(childProcess: child_process.ChildProcess, options: IWaitForExitWithBufferOptions): Promise>; + static waitForExitAsync(childProcess: child_process.ChildProcess, options?: IWaitForExitOptions): Promise; } // @public @@ -191,9 +128,9 @@ export type ExecutableStdioMapping = 'pipe' | 'ignore' | 'inherit' | ExecutableS export type ExecutableStdioStreamMapping = 'pipe' | 'ignore' | 'inherit' | NodeJS.WritableStream | NodeJS.ReadableStream | number | undefined; // @public -export enum FileConstants { - PackageJson = "package.json" -} +export const FileConstants: { + readonly PackageJson: "package.json"; +}; // @public export class FileError extends Error { @@ -205,11 +142,11 @@ export class FileError extends Error { // @internal (undocumented) static _environmentVariableIsAbsolutePath: boolean; getFormattedErrorMessage(options?: IFileErrorFormattingOptions): string; + static getProblemMatcher(options?: Pick): IProblemPattern; readonly line: number | undefined; readonly projectFolder: string; // @internal (undocumented) static _sanitizedEnvironmentVariable: string | undefined; - // @override toString(): string; } @@ -220,7 +157,7 @@ export type FileLocationStyle = 'Unix' | 'VisualStudio'; export class FileSystem { static appendToFile(filePath: string, contents: string | Buffer, options?: IFileSystemWriteFileOptions): void; static appendToFileAsync(filePath: string, contents: string | Buffer, options?: IFileSystemWriteFileOptions): Promise; - static changePosixModeBits(path: string, mode: PosixModeBits): void; + static changePosixModeBits(path: string, modeBits: PosixModeBits): void; static changePosixModeBitsAsync(path: string, mode: PosixModeBits): Promise; static copyFile(options: IFileSystemCopyFileOptions): void; static copyFileAsync(options: IFileSystemCopyFileOptions): Promise; @@ -228,12 +165,15 @@ export class FileSystem { static copyFilesAsync(options: IFileSystemCopyFilesAsyncOptions): Promise; static createHardLink(options: IFileSystemCreateLinkOptions): void; static createHardLinkAsync(options: IFileSystemCreateLinkOptions): Promise; + static createReadStream(filePath: string): FileSystemReadStream; static createSymbolicLinkFile(options: IFileSystemCreateLinkOptions): void; static createSymbolicLinkFileAsync(options: IFileSystemCreateLinkOptions): Promise; static createSymbolicLinkFolder(options: IFileSystemCreateLinkOptions): void; static createSymbolicLinkFolderAsync(options: IFileSystemCreateLinkOptions): Promise; static createSymbolicLinkJunction(options: IFileSystemCreateLinkOptions): void; static createSymbolicLinkJunctionAsync(options: IFileSystemCreateLinkOptions): Promise; + static createWriteStream(filePath: string, options?: IFileSystemCreateWriteStreamOptions): FileSystemWriteStream; + static createWriteStreamAsync(filePath: string, options?: IFileSystemCreateWriteStreamOptions): Promise; static deleteFile(filePath: string, options?: IFileSystemDeleteFileOptions): void; static deleteFileAsync(filePath: string, options?: IFileSystemDeleteFileOptions): Promise; static deleteFolder(folderPath: string): void; @@ -267,10 +207,6 @@ export class FileSystem { static readFileAsync(filePath: string, options?: IFileSystemReadFileOptions): Promise; static readFileToBuffer(filePath: string): Buffer; static readFileToBufferAsync(filePath: string): Promise; - // @deprecated (undocumented) - static readFolder(folderPath: string, options?: IFileSystemReadFolderOptions): string[]; - // @deprecated (undocumented) - static readFolderAsync(folderPath: string, options?: IFileSystemReadFolderOptions): Promise; static readFolderItemNames(folderPath: string, options?: IFileSystemReadFolderOptions): string[]; static readFolderItemNamesAsync(folderPath: string, options?: IFileSystemReadFolderOptions): Promise; static readFolderItems(folderPath: string, options?: IFileSystemReadFolderOptions): FolderItem[]; @@ -279,6 +215,8 @@ export class FileSystem { static readLinkAsync(path: string): Promise; static updateTimes(path: string, times: IFileSystemUpdateTimeParameters): void; static updateTimesAsync(path: string, times: IFileSystemUpdateTimeParameters): Promise; + static writeBuffersToFile(filePath: string, contents: ReadonlyArray, options?: IFileSystemWriteBinaryFileOptions): void; + static writeBuffersToFileAsync(filePath: string, contents: ReadonlyArray, options?: IFileSystemWriteBinaryFileOptions): Promise; static writeFile(filePath: string, contents: string | Buffer, options?: IFileSystemWriteFileOptions): void; static writeFileAsync(filePath: string, contents: string | Buffer, options?: IFileSystemWriteFileOptions): Promise; } @@ -289,59 +227,49 @@ export type FileSystemCopyFilesAsyncFilter = (sourcePath: string, destinationPat // @public export type FileSystemCopyFilesFilter = (sourcePath: string, destinationPath: string) => boolean; +// @public +export type FileSystemReadStream = fs.ReadStream; + // @public export type FileSystemStats = fs.Stats; +// @public +export type FileSystemWriteStream = fs.WriteStream; + // @public export class FileWriter { close(): void; readonly filePath: string; + getStatistics(): FileSystemStats; static open(filePath: string, flags?: IFileWriterFlags): FileWriter; write(text: string): void; } // @public -export enum FolderConstants { - Git = ".git", - NodeModules = "node_modules" -} +export const FolderConstants: { + readonly Git: ".git"; + readonly NodeModules: "node_modules"; +}; // @public export type FolderItem = fs.Dirent; // @public -export interface IAnsiEscapeConvertForTestsOptions { - encodeNewlines?: boolean; -} +function getHomeFolder(): string; -// @beta +// @public export interface IAsyncParallelismOptions { + allowOversubscription?: boolean; concurrency?: number; + weighted?: boolean; } -// @beta (undocumented) -export interface IColorableSequence { - // (undocumented) - backgroundColor?: ColorValue; - // (undocumented) - foregroundColor?: ColorValue; - // (undocumented) - isEol?: boolean; - // (undocumented) - text: string; +// @public +export interface IDependenciesMetaTable { // (undocumented) - textAttributes?: TextAttribute[]; -} - -// @beta -export interface IConsoleTerminalProviderOptions { - debugEnabled: boolean; - verboseEnabled: boolean; -} - -// @beta -export interface IDynamicPrefixProxyTerminalProviderOptions extends IPrefixProxyTerminalProviderOptionsBase { - getPrefix: () => string; + [dependencyName: string]: { + injected?: boolean; + }; } // @public @@ -416,15 +344,18 @@ export interface IFileSystemCreateLinkOptions { newLinkPath: string; } +// @public +export interface IFileSystemCreateWriteStreamOptions extends IFileSystemWriteFileOptionsBase { +} + // @public export interface IFileSystemDeleteFileOptions { throwIfNotExists?: boolean; } // @public -export interface IFileSystemMoveOptions { +export interface IFileSystemMoveOptions extends IFileSystemWriteFileOptionsBase { destinationPath: string; - ensureFolderExists?: boolean; overwrite?: boolean; sourcePath: string; } @@ -447,9 +378,17 @@ export interface IFileSystemUpdateTimeParameters { } // @public -export interface IFileSystemWriteFileOptions { +export interface IFileSystemWriteBinaryFileOptions extends IFileSystemWriteFileOptionsBase { +} + +// @public +export interface IFileSystemWriteFileOptions extends IFileSystemWriteBinaryFileOptions { convertLineEndings?: NewlineKind; encoding?: Encoding; +} + +// @public (undocumented) +export interface IFileSystemWriteFileOptionsBase { ensureFolderExists?: boolean; } @@ -490,6 +429,7 @@ export interface IImportResolvePackageAsyncOptions extends IImportResolveAsyncOp // @public export interface IImportResolvePackageOptions extends IImportResolveOptions { packageName: string; + useNodeJSResolver?: boolean; } // @public @@ -509,25 +449,46 @@ export interface IJsonFileSaveOptions extends IJsonFileStringifyOptions { } // @public -export interface IJsonFileStringifyOptions { +export interface IJsonFileStringifyOptions extends IJsonFileParseOptions { headerComment?: string; ignoreUndefinedValues?: boolean; newlineConversion?: NewlineKind; prettyFormatting?: boolean; } +// @public +export interface IJsonSchemaCustomFormat { + type: T extends string ? 'string' : T extends number ? 'number' : never; + validate: (data: T) => boolean; +} + // @public export interface IJsonSchemaErrorInfo { details: string; } // @public -export interface IJsonSchemaFromFileOptions { +export type IJsonSchemaFromFileOptions = IJsonSchemaLoadOptions; + +// @public +export type IJsonSchemaFromObjectOptions = IJsonSchemaLoadOptions; + +// @public +export interface IJsonSchemaLoadOptions { + customFormats?: Record | IJsonSchemaCustomFormat>; dependentSchemas?: JsonSchema[]; + // @beta + rejectVendorExtensionKeywords?: boolean; + schemaVersion?: JsonSchemaVersion; } // @public -export interface IJsonSchemaValidateOptions { +export interface IJsonSchemaValidateObjectWithOptions { + ignoreSchemaField?: boolean; +} + +// @public +export interface IJsonSchemaValidateOptions extends IJsonSchemaValidateObjectWithOptions { customErrorHeader?: string; } @@ -542,10 +503,13 @@ export class Import { // @public export interface INodePackageJson { - bin?: string; + bin?: string | Record; dependencies?: IPackageJsonDependencyTable; + dependenciesMeta?: IDependenciesMetaTable; description?: string; devDependencies?: IPackageJsonDependencyTable; + exports?: string | string[] | Record; + files?: string[]; homepage?: string; license?: string; main?: string; @@ -560,6 +524,7 @@ export interface INodePackageJson { // @beta tsdocMetadata?: string; types?: string; + typesVersions?: Record>; typings?: string; version?: string; } @@ -568,7 +533,7 @@ export interface INodePackageJson { export class InternalError extends Error { constructor(message: string); static breakInDebugger: boolean; - // @override (undocumented) + // (undocumented) toString(): string; readonly unformattedMessage: string; } @@ -583,6 +548,19 @@ export interface IPackageJsonDependencyTable { [dependencyName: string]: string; } +// @public +export interface IPackageJsonExports { + 'node-addons'?: string | IPackageJsonExports; + browser?: string | IPackageJsonExports; + default?: string | IPackageJsonExports; + development?: string | IPackageJsonExports; + import?: string | IPackageJsonExports; + node?: string | IPackageJsonExports; + production?: string | IPackageJsonExports; + require?: string | IPackageJsonExports; + types?: string | IPackageJsonExports; +} + // @public export interface IPackageJsonLookupParameters { loadExtraFields?: boolean; @@ -641,12 +619,27 @@ export interface IPeerDependenciesMetaTable { }; } -// @beta (undocumented) -export type IPrefixProxyTerminalProviderOptions = IStaticPrefixProxyTerminalProviderOptions | IDynamicPrefixProxyTerminalProviderOptions; +// @public +export interface IProblemPattern { + code?: number; + column?: number; + endColumn?: number; + endLine?: number; + file?: number; + line?: number; + location?: number; + loop?: boolean; + message: number; + regexp: string; + severity?: number; +} -// @beta (undocumented) -export interface IPrefixProxyTerminalProviderOptionsBase { - terminalProvider: ITerminalProvider; +// @public +export interface IProcessInfo { + childProcessInfos: IProcessInfo[]; + parentProcessInfo: IProcessInfo | undefined; + processId: number; + processName: string; } // @public @@ -656,25 +649,37 @@ export interface IProtectableMapParameters { onSet?: (source: ProtectableMap, key: K, value: V) => V; } -// @beta (undocumented) -export interface IRunWithRetriesOptions { +// @public +export interface IReadLinesFromIterableOptions { + encoding?: Encoding; + ignoreEmptyLines?: boolean; +} + +// @public +export interface IRealNodeModulePathResolverOptions { // (undocumented) - action: () => Promise | TResult; + fs?: Partial>; + ignoreMissingPaths?: boolean; // (undocumented) + path?: Partial>; +} + +// @public (undocumented) +export interface IRunWithRetriesOptions { + action: (retryCount: number) => Promise | TResult; maxRetries: number; - // (undocumented) retryDelayMs?: number; } -// @beta -export interface IStaticPrefixProxyTerminalProviderOptions extends IPrefixProxyTerminalProviderOptionsBase { - prefix: string; +// @public (undocumented) +export interface IRunWithTimeoutOptions { + action: () => Promise | TResult; + timeoutMessage?: string; + timeoutMs: number; } -// @beta (undocumented) -export interface IStringBufferOutputOptions { - normalizeSpecialCharacters: boolean; -} +// @public +function isRecord(value: unknown): value is Record; // @public export interface IStringBuilder { @@ -687,34 +692,38 @@ export interface ISubprocessOptions { detached: boolean; } -// @beta (undocumented) -export interface ITerminal { - registerProvider(provider: ITerminalProvider): void; - unregisterProvider(provider: ITerminalProvider): void; - write(...messageParts: (string | IColorableSequence)[]): void; - writeDebug(...messageParts: (string | IColorableSequence)[]): void; - writeDebugLine(...messageParts: (string | IColorableSequence)[]): void; - writeError(...messageParts: (string | IColorableSequence)[]): void; - writeErrorLine(...messageParts: (string | IColorableSequence)[]): void; - writeLine(...messageParts: (string | IColorableSequence)[]): void; - writeVerbose(...messageParts: (string | IColorableSequence)[]): void; - writeVerboseLine(...messageParts: (string | IColorableSequence)[]): void; - writeWarning(...messageParts: (string | IColorableSequence)[]): void; - writeWarningLine(...messageParts: (string | IColorableSequence)[]): void; +// @public +export interface IWaitForExitOptions { + encoding?: BufferEncoding | 'buffer'; + throwOnNonZeroExitCode?: boolean; + throwOnSignal?: boolean; } -// @beta -export interface ITerminalProvider { - eolCharacter: string; - supportsColor: boolean; - write(data: string, severity: TerminalProviderSeverity): void; +// @public +export interface IWaitForExitResult extends IWaitForExitResultWithoutOutput { + stderr: T; + stdout: T; } -// @beta -export interface ITerminalWritableOptions { - severity: TerminalProviderSeverity; - terminal: ITerminal; - writableOptions?: WritableOptions; +// @public +export interface IWaitForExitResultWithoutOutput { + exitCode: number | null; + signal: string | null; +} + +// @public +export interface IWaitForExitWithBufferOptions extends IWaitForExitOptions { + encoding: 'buffer'; +} + +// @public +export interface IWaitForExitWithStringOptions extends IWaitForExitOptions { + encoding: BufferEncoding; +} + +// @public (undocumented) +export interface IWeighted { + weight: number; } // @public @@ -745,12 +754,15 @@ export type JsonObject = any; export class JsonSchema { ensureCompiled(): void; static fromFile(filename: string, options?: IJsonSchemaFromFileOptions): JsonSchema; - static fromLoadedObject(schemaObject: JsonObject): JsonSchema; + static fromLoadedObject(schemaObject: JsonObject, options?: IJsonSchemaFromObjectOptions): JsonSchema; get shortName(): string; validateObject(jsonObject: JsonObject, filenameForErrors: string, options?: IJsonSchemaValidateOptions): void; - validateObjectWithCallback(jsonObject: JsonObject, errorCallback: (errorInfo: IJsonSchemaErrorInfo) => void): void; + validateObjectWithCallback(jsonObject: JsonObject, errorCallback: (errorInfo: IJsonSchemaErrorInfo) => void, options?: IJsonSchemaValidateObjectWithOptions): void; } +// @public +export type JsonSchemaVersion = 'draft-04' | 'draft-07'; + // @public export enum JsonSyntax { Json5 = "json5", @@ -770,8 +782,6 @@ export class LegacyAdapters { // (undocumented) static convertCallbackToPromise(fn: (arg1: TArg1, arg2: TArg2, arg3: TArg3, arg4: TArg4, cb: LegacyCallback) => void, arg1: TArg1, arg2: TArg2, arg3: TArg3, arg4: TArg4): Promise; static scrubError(error: Error | string | any): Error; - // @deprecated - static sortStable(array: T[], compare?: (a: T, b: T) => number): void; } // @public @@ -779,7 +789,9 @@ export type LegacyCallback = (error: TError | null | undefined, // @public export class LockFile { + // @deprecated (undocumented) static acquire(resourceFolder: string, resourceName: string, maxWaitMs?: number): Promise; + static acquireAsync(resourceFolder: string, resourceName: string, maxWaitMs?: number): Promise; get dirtyWhenAcquired(): boolean; get filePath(): string; static getLockFilePath(resourceFolder: string, resourceName: string, pid?: number): string; @@ -796,6 +808,21 @@ export class MapExtensions { }; } +// @public +function mergeWith(target: TTarget, source: TSource, customizer?: MergeWithCustomizer): TTarget; + +// @public +type MergeWithCustomizer = (objValue: unknown, srcValue: unknown, key: string) => unknown; + +// @public +export class MinimumHeap { + constructor(comparator: (a: T, b: T) => number); + peek(): T | undefined; + poll(): T | undefined; + push(item: T): void; + get size(): number; +} + // @public export enum NewlineKind { CrLf = "\r\n", @@ -803,6 +830,16 @@ export enum NewlineKind { OsDefault = "os" } +declare namespace Objects { + export { + areDeepEqual, + isRecord, + MergeWithCustomizer, + mergeWith + } +} +export { Objects } + // @public export class PackageJsonLookup { constructor(parameters?: IPackageJsonLookupParameters); @@ -853,6 +890,9 @@ export class Path { static isUnderOrEqual(childPath: string, parentFolderPath: string): boolean; } +// @public +function polyfillDisposeSymbols(): void; + // @public export enum PosixModeBits { AllExecute = 73, @@ -870,17 +910,6 @@ export enum PosixModeBits { UserWrite = 128 } -// @beta -export class PrefixProxyTerminalProvider implements ITerminalProvider { - constructor(options: IPrefixProxyTerminalProviderOptions); - // @override (undocumented) - get eolCharacter(): string; - // @override (undocumented) - get supportsColor(): boolean; - // @override (undocumented) - write(data: string, severity: TerminalProviderSeverity): void; -} - // @public export class ProtectableMap { constructor(parameters: IProtectableMapParameters); @@ -894,30 +923,25 @@ export class ProtectableMap { get size(): number; } +// @public +export class RealNodeModulePathResolver { + constructor(options?: IRealNodeModulePathResolverOptions); + clearCache(): void; + readonly realNodeModulePath: (input: string) => string; +} + // @public export class Sort { static compareByValue(x: any, y: any): number; static isSorted(collection: Iterable, comparer?: (x: any, y: any) => number): boolean; static isSortedBy(collection: Iterable, keySelector: (element: T) => any, comparer?: (x: any, y: any) => number): boolean; static sortBy(array: T[], keySelector: (element: T) => any, comparer?: (x: any, y: any) => number): void; + static sortKeys> | unknown[]>(object: T): T; static sortMapKeys(map: Map, keyComparer?: (x: K, y: K) => number): void; static sortSet(set: Set, comparer?: (x: T, y: T) => number): void; static sortSetBy(set: Set, keySelector: (element: T) => any, keyComparer?: (x: T, y: T) => number): void; } -// @beta -export class StringBufferTerminalProvider implements ITerminalProvider { - constructor(supportsColor?: boolean); - get eolCharacter(): string; - getDebugOutput(options?: IStringBufferOutputOptions): string; - getErrorOutput(options?: IStringBufferOutputOptions): string; - getOutput(options?: IStringBufferOutputOptions): string; - getVerbose(options?: IStringBufferOutputOptions): string; - getWarningOutput(options?: IStringBufferOutputOptions): string; - get supportsColor(): boolean; - write(data: string, severity: TerminalProviderSeverity): void; -} - // @public export class StringBuilder implements IStringBuilder { constructor(); @@ -932,44 +956,6 @@ export class SubprocessTerminator { static readonly RECOMMENDED_OPTIONS: ISubprocessOptions; } -// @beta -export class Terminal implements ITerminal { - constructor(provider: ITerminalProvider); - registerProvider(provider: ITerminalProvider): void; - unregisterProvider(provider: ITerminalProvider): void; - write(...messageParts: (string | IColorableSequence)[]): void; - writeDebug(...messageParts: (string | IColorableSequence)[]): void; - writeDebugLine(...messageParts: (string | IColorableSequence)[]): void; - writeError(...messageParts: (string | IColorableSequence)[]): void; - writeErrorLine(...messageParts: (string | IColorableSequence)[]): void; - writeLine(...messageParts: (string | IColorableSequence)[]): void; - writeVerbose(...messageParts: (string | IColorableSequence)[]): void; - writeVerboseLine(...messageParts: (string | IColorableSequence)[]): void; - writeWarning(...messageParts: (string | IColorableSequence)[]): void; - writeWarningLine(...messageParts: (string | IColorableSequence)[]): void; -} - -// @beta -export enum TerminalProviderSeverity { - // (undocumented) - debug = 4, - // (undocumented) - error = 2, - // (undocumented) - log = 0, - // (undocumented) - verbose = 3, - // (undocumented) - warning = 1 -} - -// @beta -export class TerminalWritable extends Writable { - constructor(options: ITerminalWritableOptions); - // (undocumented) - _write(chunk: string | Buffer | Uint8Array, encoding: string, callback: (error?: Error | null) => void): void; -} - // @public export class Text { static convertTo(input: string, newlineKind: NewlineKind): string; @@ -980,24 +966,16 @@ export class Text { static getNewline(newlineKind: NewlineKind): string; static padEnd(s: string, minimumLength: number, paddingCharacter?: string): string; static padStart(s: string, minimumLength: number, paddingCharacter?: string): string; + static readLinesFromIterable(iterable: Iterable, options?: IReadLinesFromIterableOptions): Generator; + static readLinesFromIterableAsync(iterable: AsyncIterable, options?: IReadLinesFromIterableOptions): AsyncGenerator; static replaceAll(input: string, searchValue: string, replaceValue: string): string; - static truncateWithEllipsis(s: string, maximumLength: number): string; -} - -// @beta -export enum TextAttribute { - // (undocumented) - Blink = 3, - // (undocumented) - Bold = 0, + static reverse(s: string): string; + static splitByNewLines(s: undefined): undefined; // (undocumented) - Dim = 1, + static splitByNewLines(s: string): string[]; // (undocumented) - Hidden = 5, - // (undocumented) - InvertColor = 4, - // (undocumented) - Underline = 2 + static splitByNewLines(s: string | undefined): string[] | undefined; + static truncateWithEllipsis(s: string, maximumLength: number): string; } // @public @@ -1006,4 +984,11 @@ export class TypeUuid { static registerClass(targetClass: any, typeUuid: string): void; } +declare namespace User { + export { + getHomeFolder + } +} +export { User } + ``` diff --git a/common/reviews/api/operation-graph.api.md b/common/reviews/api/operation-graph.api.md new file mode 100644 index 00000000000..261057fb62a --- /dev/null +++ b/common/reviews/api/operation-graph.api.md @@ -0,0 +1,258 @@ +## API Report File for "@rushstack/operation-graph" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +/// + +import type { ITerminal } from '@rushstack/terminal'; + +// @beta +export type CommandMessageFromHost = ICancelCommandMessage | IExitCommandMessage | IRunCommandMessage | ISyncCommandMessage; + +// @beta +export type EventMessageFromClient = IRequestRunEventMessage | IAfterExecuteEventMessage | ISyncEventMessage; + +// @beta +export interface IAfterExecuteEventMessage { + // (undocumented) + event: 'after-execute'; + // (undocumented) + status: OperationStatus; +} + +// @beta +export interface ICancelCommandMessage { + // (undocumented) + command: 'cancel'; +} + +// @beta +export interface IExecuteOperationContext extends Omit { + afterExecute(operation: Operation, state: IOperationState): void; + beforeExecute(operation: Operation, state: IOperationState): void; + queueWork(workFn: () => Promise, priority: number): Promise; + requestRun?: OperationRequestRunCallback; + terminal: ITerminal; +} + +// @beta +export interface IExitCommandMessage { + // (undocumented) + command: 'exit'; +} + +// @beta +export interface IOperationExecutionOptions { + // (undocumented) + abortSignal: AbortSignal; + // (undocumented) + afterExecuteOperation?: (operation: Operation) => void; + // (undocumented) + afterExecuteOperationGroup?: (operationGroup: OperationGroupRecord) => void; + // (undocumented) + beforeExecuteOperation?: (operation: Operation) => void; + // (undocumented) + beforeExecuteOperationGroup?: (operationGroup: OperationGroupRecord) => void; + // (undocumented) + parallelism: number; + // (undocumented) + requestRun?: OperationRequestRunCallback; + // (undocumented) + terminal: ITerminal; +} + +// @beta +export interface IOperationOptions { + group?: OperationGroupRecord | undefined; + metadata?: TMetadata | undefined; + name: string; + runner?: IOperationRunner | undefined; + weight?: number | undefined; +} + +// @beta +export interface IOperationRunner { + executeAsync(context: IOperationRunnerContext): Promise; + readonly name: string; + silent: boolean; +} + +// @beta +export interface IOperationRunnerContext { + abortSignal: AbortSignal; + isFirstRun: boolean; + requestRun?: (detail?: string) => void; +} + +// @beta +export interface IOperationState { + error: OperationError | undefined; + hasBeenRun: boolean; + status: OperationStatus; + stopwatch: Stopwatch; +} + +// @beta +export interface IOperationStates { + readonly lastState: Readonly | undefined; + readonly state: Readonly | undefined; +} + +// @beta +export type IPCHost = Pick; + +// @beta +export interface IRequestRunEventMessage { + detail?: string; + // (undocumented) + event: 'requestRun'; + requestor: string; +} + +// @beta +export interface IRunCommandMessage { + // (undocumented) + command: 'run'; +} + +// @beta +export interface ISyncCommandMessage { + // (undocumented) + command: 'sync'; +} + +// @beta +export interface ISyncEventMessage { + // (undocumented) + event: 'sync'; + // (undocumented) + status: OperationStatus; +} + +// @beta +export interface IWatchLoopOptions { + executeAsync: (state: IWatchLoopState) => Promise; + onAbort: () => void; + onBeforeExecute: () => void; + onRequestRun: OperationRequestRunCallback; +} + +// @beta +export interface IWatchLoopState { + // (undocumented) + get abortSignal(): AbortSignal; + // (undocumented) + requestRun: OperationRequestRunCallback; +} + +// @beta +export class Operation implements IOperationStates { + constructor(options: IOperationOptions); + // (undocumented) + addDependency(dependency: Operation): void; + readonly consumers: Set>; + criticalPathLength: number | undefined; + // (undocumented) + deleteDependency(dependency: Operation): void; + readonly dependencies: Set>; + // @internal (undocumented) + _executeAsync(context: IExecuteOperationContext): Promise; + readonly group: OperationGroupRecord | undefined; + lastState: IOperationState | undefined; + // (undocumented) + readonly metadata: TMetadata; + readonly name: string; + // (undocumented) + reset(): void; + runner: IOperationRunner | undefined; + state: IOperationState | undefined; + weight: number; +} + +// @beta +export class OperationError extends Error { + constructor(type: string, message: string); + // (undocumented) + get message(): string; + // (undocumented) + toString(): string; + // (undocumented) + protected _type: string; +} + +// @beta +export class OperationExecutionManager { + constructor(operations: ReadonlySet>); + executeAsync(executionOptions: IOperationExecutionOptions): Promise; +} + +// @beta +export class OperationGroupRecord { + constructor(name: string, metadata?: TMetadata); + // (undocumented) + addOperation(operation: Operation): void; + // (undocumented) + get duration(): number; + // (undocumented) + get finished(): boolean; + // (undocumented) + get hasCancellations(): boolean; + // (undocumented) + get hasFailures(): boolean; + // (undocumented) + readonly metadata: TMetadata; + // (undocumented) + readonly name: string; + // (undocumented) + reset(): void; + // (undocumented) + setOperationAsComplete(operation: Operation, state: IOperationState): void; + // (undocumented) + startTimer(): void; +} + +// @beta +export type OperationRequestRunCallback = (requestor: string, detail?: string) => void; + +// @beta +export enum OperationStatus { + Aborted = "ABORTED", + Blocked = "BLOCKED", + Executing = "EXECUTING", + Failure = "FAILURE", + NoOp = "NO OP", + Ready = "READY", + Success = "SUCCESS", + Waiting = "WAITING" +} + +// @public +export class Stopwatch { + constructor(); + get duration(): number; + get endTime(): number | undefined; + // (undocumented) + get isRunning(): boolean; + reset(): Stopwatch; + static start(): Stopwatch; + start(): Stopwatch; + get startTime(): number | undefined; + stop(): Stopwatch; + toString(): string; +} + +// @beta +export class WatchLoop implements IWatchLoopState { + constructor(options: IWatchLoopOptions); + get abortSignal(): AbortSignal; + requestRun: OperationRequestRunCallback; + runIPCAsync(host?: IPCHost): Promise; + runUntilAbortedAsync(abortSignal: AbortSignal, onWaiting: () => void): Promise; + runUntilStableAsync(abortSignal: AbortSignal): Promise; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/common/reviews/api/package-deps-hash.api.md b/common/reviews/api/package-deps-hash.api.md index def3204392e..1de2d1b706f 100644 --- a/common/reviews/api/package-deps-hash.api.md +++ b/common/reviews/api/package-deps-hash.api.md @@ -7,6 +7,9 @@ // @public export function ensureGitMinimumVersion(gitPath?: string): void; +// @beta +export function getDetailedRepoStateAsync(rootDirectory: string, additionalRelativePathsToHash?: string[], gitPath?: string, filterPath?: string[]): Promise; + // @public export function getGitHashForFiles(filesToHash: string[], packagePath: string, gitPath?: string): Map; @@ -20,7 +23,18 @@ export function getRepoChanges(currentWorkingDirectory: string, revision?: strin export function getRepoRoot(currentWorkingDirectory: string, gitPath?: string): string; // @beta -export function getRepoStateAsync(rootDirectory: string, additionalRelativePathsToHash?: string[], gitPath?: string): Promise>; +export function getRepoStateAsync(rootDirectory: string, additionalRelativePathsToHash?: string[], gitPath?: string, filterPath?: string[]): Promise>; + +// @beta +export function hashFilesAsync(rootDirectory: string, filesToHash: Iterable | AsyncIterable, gitPath?: string): Promise>; + +// @beta +export interface IDetailedRepoState { + files: Map; + hasSubmodules: boolean; + hasUncommittedChanges: boolean; + symlinks: Map; +} // @beta export interface IFileDiffStatus { diff --git a/common/reviews/api/package-extractor.api.md b/common/reviews/api/package-extractor.api.md index da60437e10d..1b0cfb0139c 100644 --- a/common/reviews/api/package-extractor.api.md +++ b/common/reviews/api/package-extractor.api.md @@ -5,10 +5,19 @@ ```ts import { IPackageJson } from '@rushstack/node-core-library'; -import { ITerminal } from '@rushstack/node-core-library'; +import { ITerminal } from '@rushstack/terminal'; + +// @public +export interface IExtractorDependencyConfiguration { + dependencyName: string; + dependencyVersionRange: string; + patternsToExclude?: string[]; + patternsToInclude?: string[]; +} // @public export interface IExtractorMetadataJson { + files: string[]; links: ILinkInfo[]; mainProjectName: string; projects: IProjectInfoJson[]; @@ -18,18 +27,21 @@ export interface IExtractorMetadataJson { export interface IExtractorOptions { createArchiveFilePath?: string; createArchiveOnly?: boolean; + dependencyConfigurations?: IExtractorDependencyConfiguration[]; folderToCopy?: string; includeDevDependencies?: boolean; includeNpmIgnoreFiles?: boolean; - linkCreation?: 'default' | 'script' | 'none'; + linkCreation?: LinkCreationMode; + linkCreationScriptPath?: string; mainProjectName: string; overwriteExisting: boolean; pnpmInstallFolder?: string; projectConfigurations: IExtractorProjectConfiguration[]; sourceRootFolder: string; + subspaces?: IExtractorSubspace[]; targetRootFolder: string; terminal: ITerminal; - transformPackageJson?: (packageJson: IPackageJson) => IPackageJson | undefined; + transformPackageJson?: (packageJson: IPackageJson) => IPackageJson; } // @public @@ -37,10 +49,20 @@ export interface IExtractorProjectConfiguration { additionalDependenciesToInclude?: string[]; additionalProjectsToInclude?: string[]; dependenciesToExclude?: string[]; + patternsToExclude?: string[]; + patternsToInclude?: string[]; projectFolder: string; projectName: string; } +// @public +export interface IExtractorSubspace { + pnpmInstallFolder?: string; + pnpmNodeModulesHoistingEnabled?: boolean; + subspaceName: string; + transformPackageJson?: (packageJson: IPackageJson) => IPackageJson; +} + // @public export interface ILinkInfo { kind: 'fileLink' | 'folderLink'; @@ -54,9 +76,14 @@ export interface IProjectInfoJson { projectName: string; } +// @public +export type LinkCreationMode = 'default' | 'script' | 'none'; + // @public export class PackageExtractor { extractAsync(options: IExtractorOptions): Promise; + // @beta + static getPackageIncludedFilesAsync(packageRootPath: string): Promise; } // (No @packageDocumentation comment for this package) diff --git a/common/reviews/api/playwright-browser-tunnel.api.md b/common/reviews/api/playwright-browser-tunnel.api.md new file mode 100644 index 00000000000..2cdb88416a7 --- /dev/null +++ b/common/reviews/api/playwright-browser-tunnel.api.md @@ -0,0 +1,119 @@ +## API Report File for "@rushstack/playwright-browser-tunnel" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { Browser } from 'playwright-core'; +import { ITerminal } from '@rushstack/terminal'; +import type { LaunchOptions } from 'playwright-core'; + +// @beta +export type BrowserName = 'chromium' | 'firefox' | 'webkit'; + +// @beta +export function createTunneledBrowserAsync(browserName: BrowserName, launchOptions: LaunchOptions, logger?: ITerminal, port?: number): Promise; + +// @beta +export const EXTENSION_INSTALLED_FILENAME: string; + +// @beta +export function getNormalizedErrorString(error: unknown): string; + +// @beta +export interface IDisposableTunneledBrowser { + [Symbol.asyncDispose]: () => Promise; + browser: Browser; +} + +// @beta +export interface IDisposableTunneledBrowserConnection { + [Symbol.dispose]: () => void; + closePromise: Promise; + remoteEndpoint: string; +} + +// @beta +export interface IHandshake { + // (undocumented) + action: 'handshake'; + // (undocumented) + browserName: BrowserName; + // (undocumented) + launchOptions: LaunchOptions; + // (undocumented) + playwrightVersion: string; +} + +// @beta +export interface ILaunchOptionsAllowlist { + allowedOptions: string[]; + version: number; +} + +// @beta +export interface ILaunchOptionsValidationResult { + deniedOptions: Array; + filteredOptions: LaunchOptions; + isValid: boolean; + warnings: string[]; +} + +// @beta +export type IPlaywrightTunnelOptions = { + terminal: ITerminal; + onStatusChange: (status: TunnelStatus) => void; + playwrightInstallPath: string; + onBeforeLaunch?: (handshake: IHandshake) => Promise | boolean; +} & ({ + mode: 'poll-connection'; + wsEndpoint: string; +} | { + mode: 'wait-for-incoming-connection'; + listenPort: number; +}); + +// @beta +export function isExtensionInstalledAsync(): Promise; + +// @beta +export const LAUNCH_OPTIONS_ALLOWLIST_FILENAME: string; + +// @beta +export class LaunchOptionsValidator { + static addToAllowlistAsync(option: keyof LaunchOptions): Promise; + static clearAllowlistAsync(): Promise; + static getAllowlistDescription(): string; + static getAllowlistFilePath(): string; + static readAllowlistAsync(): Promise; + static removeFromAllowlistAsync(option: keyof LaunchOptions): Promise; + static validateLaunchOptionsAsync(launchOptions: LaunchOptions, terminal?: ITerminal): Promise; + static writeAllowlistAsync(allowlist: ILaunchOptionsAllowlist): Promise; +} + +// @beta +export class PlaywrightTunnel { + // (undocumented) + [Symbol.asyncDispose](): Promise; + constructor(options: IPlaywrightTunnelOptions); + // (undocumented) + cleanTempFilesAsync(): Promise; + // (undocumented) + startAsync(options?: { + keepRunning?: boolean; + }): Promise; + // (undocumented) + get status(): TunnelStatus; + // (undocumented) + stopAsync(): Promise; + // (undocumented) + waitForCloseAsync(): Promise; +} + +// @beta +export function tunneledBrowserConnection(logger: ITerminal, port?: number, playwrightVersion?: string): Promise; + +// @beta +export type TunnelStatus = 'waiting-for-connection' | 'browser-server-running' | 'stopped' | 'setting-up-browser-server' | 'error'; + +``` diff --git a/common/reviews/api/problem-matcher.api.md b/common/reviews/api/problem-matcher.api.md new file mode 100644 index 00000000000..895a1ddb3fe --- /dev/null +++ b/common/reviews/api/problem-matcher.api.md @@ -0,0 +1,55 @@ +## API Report File for "@rushstack/problem-matcher" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public +export interface IProblem { + readonly code?: string; + readonly column?: number; + readonly endColumn?: number; + readonly endLine?: number; + readonly file?: string; + readonly line?: number; + readonly matcherName: string; + readonly message: string; + readonly severity?: ProblemSeverity; +} + +// @public +export interface IProblemMatcher { + exec(line: string): IProblem | false; + flush?(): IProblem[]; + readonly name: string; +} + +// @public +export interface IProblemMatcherJson { + name: string; + pattern: IProblemPattern | IProblemPattern[]; + severity?: ProblemSeverity; +} + +// @public +export interface IProblemPattern { + code?: number; + column?: number; + endColumn?: number; + endLine?: number; + file?: number; + line?: number; + location?: number; + loop?: boolean; + message: number; + regexp: string; + severity?: number; +} + +// @public +export function parseProblemMatchersJson(problemMatchers: IProblemMatcherJson[]): IProblemMatcher[]; + +// @public +export type ProblemSeverity = 'error' | 'warning' | 'info'; + +``` diff --git a/common/reviews/api/real-node-module-path.api.md b/common/reviews/api/real-node-module-path.api.md new file mode 100644 index 00000000000..751844943b5 --- /dev/null +++ b/common/reviews/api/real-node-module-path.api.md @@ -0,0 +1,17 @@ +## API Report File for "@rushstack/real-node-module-path" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +/// + +// @public +export function clearCache(): void; + +// @public +export const realNodeModulePath: (input: string) => string; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/common/reviews/api/rig-package.api.md b/common/reviews/api/rig-package.api.md index f01bc2f44af..d79f609abb6 100644 --- a/common/reviews/api/rig-package.api.md +++ b/common/reviews/api/rig-package.api.md @@ -11,6 +11,21 @@ export interface ILoadForProjectFolderOptions { projectFolderPath: string; } +// @public +export interface IRigConfig { + readonly filePath: string; + getResolvedProfileFolder(): string; + getResolvedProfileFolderAsync(): Promise; + readonly projectFolderOriginalPath: string; + readonly projectFolderPath: string; + readonly relativeProfileFolderPath: string; + readonly rigFound: boolean; + readonly rigPackageName: string; + readonly rigProfile: string; + tryResolveConfigFilePath(configFileRelativePath: string): string | undefined; + tryResolveConfigFilePathAsync(configFileRelativePath: string): Promise; +} + // @public export interface IRigConfigJson { rigPackageName: string; @@ -18,7 +33,7 @@ export interface IRigConfigJson { } // @public -export class RigConfig { +export class RigConfig implements IRigConfig { readonly filePath: string; getResolvedProfileFolder(): string; getResolvedProfileFolderAsync(): Promise; diff --git a/common/reviews/api/rush-amazon-s3-build-cache-plugin.api.md b/common/reviews/api/rush-amazon-s3-build-cache-plugin.api.md index 230a9a429d3..05745fddbb8 100644 --- a/common/reviews/api/rush-amazon-s3-build-cache-plugin.api.md +++ b/common/reviews/api/rush-amazon-s3-build-cache-plugin.api.md @@ -6,15 +6,16 @@ /// -import * as fetch from 'node-fetch'; import type { IRushPlugin } from '@rushstack/rush-sdk'; -import { ITerminal } from '@rushstack/node-core-library'; +import { ITerminal } from '@rushstack/terminal'; import type { RushConfiguration } from '@rushstack/rush-sdk'; import type { RushSession } from '@rushstack/rush-sdk'; +import { WebClient } from '@rushstack/rush-sdk/lib/utilities/WebClient'; // @public export class AmazonS3Client { constructor(credentials: IAmazonS3Credentials | undefined, options: IAmazonS3BuildCacheProviderOptionsAdvanced, webClient: WebClient, terminal: ITerminal); + downloadObjectToFileAsync(objectName: string, localFilePath: string): Promise; // (undocumented) getObjectAsync(objectName: string): Promise; // (undocumented) @@ -25,6 +26,7 @@ export class AmazonS3Client { static tryDeserializeCredentials(credentialString: string | undefined): IAmazonS3Credentials | undefined; // (undocumented) uploadObjectAsync(objectName: string, objectBuffer: Buffer): Promise; + uploadObjectFromFileAsync(objectName: string, localFilePath: string): Promise; // (undocumented) static UriEncode(input: string): string; } @@ -61,22 +63,6 @@ export interface IAmazonS3Credentials { sessionToken: string | undefined; } -// Warning: (ae-forgotten-export) The symbol "IWebFetchOptionsBase" needs to be exported by the entry point index.d.ts -// -// @public -export interface IGetFetchOptions extends IWebFetchOptionsBase { - // (undocumented) - verb: 'GET' | never; -} - -// @public -export interface IPutFetchOptions extends IWebFetchOptionsBase { - // (undocumented) - body?: Buffer; - // (undocumented) - verb: 'PUT'; -} - // @public (undocumented) class RushAmazonS3BuildCachePlugin implements IRushPlugin { // (undocumented) @@ -86,30 +72,6 @@ class RushAmazonS3BuildCachePlugin implements IRushPlugin { } export default RushAmazonS3BuildCachePlugin; -// @public -export class WebClient { - constructor(); - // (undocumented) - accept: string | undefined; - // (undocumented) - addBasicAuthHeader(userName: string, password: string): void; - // (undocumented) - fetchAsync(url: string, options?: IGetFetchOptions | IPutFetchOptions): Promise; - // (undocumented) - static mergeHeaders(target: fetch.Headers, source: fetch.Headers): void; - // Warning: (ae-forgotten-export) The symbol "WebClientProxy" needs to be exported by the entry point index.d.ts - // - // (undocumented) - proxy: WebClientProxy; - // (undocumented) - readonly standardHeaders: fetch.Headers; - // (undocumented) - userAgent: string | undefined; -} - -// @public -export type WebClientResponse = fetch.Response; - // (No @packageDocumentation comment for this package) ``` diff --git a/common/reviews/api/rush-azure-storage-build-cache-plugin.api.md b/common/reviews/api/rush-azure-storage-build-cache-plugin.api.md index 15ad72b0b0f..f093e5a8a62 100644 --- a/common/reviews/api/rush-azure-storage-build-cache-plugin.api.md +++ b/common/reviews/api/rush-azure-storage-build-cache-plugin.api.md @@ -5,19 +5,28 @@ ```ts import { AzureAuthorityHosts } from '@azure/identity'; -import { DeviceCodeCredential } from '@azure/identity'; -import type { ICredentialCacheEntry } from '@rushstack/rush-sdk'; +import { CredentialCache } from '@rushstack/credential-cache'; +import { DeviceCodeCredentialOptions } from '@azure/identity'; +import type { ICredentialCacheEntry } from '@rushstack/credential-cache'; +import { InteractiveBrowserCredentialNodeOptions } from '@azure/identity'; import type { IRushPlugin } from '@rushstack/rush-sdk'; -import type { ITerminal } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; import type { RushConfiguration } from '@rushstack/rush-sdk'; import type { RushSession } from '@rushstack/rush-sdk'; +import { TokenCredential } from '@azure/identity'; // @public (undocumented) export abstract class AzureAuthenticationBase { constructor(options: IAzureAuthenticationBaseOptions); // (undocumented) + protected readonly _additionalDeviceCodeCredentialOptions: DeviceCodeCredentialOptions | undefined; + // (undocumented) + protected readonly _additionalInteractiveCredentialOptions: InteractiveBrowserCredentialNodeOptions | undefined; + // (undocumented) protected readonly _azureEnvironment: AzureEnvironmentName; // (undocumented) + protected get _credentialCacheId(): string; + // (undocumented) protected abstract readonly _credentialKindForLogging: string; // (undocumented) protected abstract readonly _credentialNameForCache: string; @@ -25,16 +34,22 @@ export abstract class AzureAuthenticationBase { protected readonly _credentialUpdateCommandForLogging: string | undefined; // (undocumented) deleteCachedCredentialsAsync(terminal: ITerminal): Promise; + // (undocumented) + protected readonly _failoverOrder: { + [key in LoginFlowType]?: LoginFlowType; + } | undefined; protected abstract _getCacheIdParts(): string[]; // (undocumented) - protected abstract _getCredentialFromDeviceCodeAsync(terminal: ITerminal, deviceCodeCredential: DeviceCodeCredential): Promise; + protected abstract _getCredentialFromTokenAsync(terminal: ITerminal, tokenCredential: TokenCredential, credentialsCache: CredentialCache): Promise; + // (undocumented) + protected readonly _loginFlow: LoginFlowType; // (undocumented) tryGetCachedCredentialAsync(options?: ITryGetCachedCredentialOptionsThrow | ITryGetCachedCredentialOptionsIgnore): Promise; // (undocumented) tryGetCachedCredentialAsync(options: ITryGetCachedCredentialOptionsLogWarning): Promise; // (undocumented) updateCachedCredentialAsync(terminal: ITerminal, credential: string): Promise; - updateCachedCredentialInteractiveAsync(terminal: ITerminal, onlyIfExistingCredentialExpiresAfter?: Date): Promise; + updateCachedCredentialInteractiveAsync(terminal: ITerminal, onlyIfExistingCredentialExpiresBefore?: Date): Promise; } // @public (undocumented) @@ -50,7 +65,7 @@ export class AzureStorageAuthentication extends AzureAuthenticationBase { // (undocumented) protected _getCacheIdParts(): string[]; // (undocumented) - protected _getCredentialFromDeviceCodeAsync(terminal: ITerminal, deviceCodeCredential: DeviceCodeCredential): Promise; + protected _getCredentialFromTokenAsync(terminal: ITerminal, tokenCredential: TokenCredential): Promise; // (undocumented) protected readonly _isCacheWriteAllowedByConfiguration: boolean; // (undocumented) @@ -70,6 +85,9 @@ export interface IAzureAuthenticationBaseOptions { azureEnvironment?: AzureEnvironmentName; // (undocumented) credentialUpdateCommandForLogging?: string | undefined; + // (undocumented) + loginFlow?: LoginFlowType; + loginFlowFailover?: LoginFlowFailoverMap; } // @public (undocumented) @@ -80,6 +98,8 @@ export interface IAzureStorageAuthenticationOptions extends IAzureAuthentication storageAccountName: string; // (undocumented) storageContainerName: string; + // (undocumented) + storageEndpoint?: string; } // @public (undocumented) @@ -116,6 +136,14 @@ export interface ITryGetCachedCredentialOptionsThrow extends ITryGetCachedCreden expiredCredentialBehavior: 'throwError'; } +// @public (undocumented) +export type LoginFlowFailoverMap = { + readonly [LoginFlow in LoginFlowType]?: Exclude; +}; + +// @public (undocumented) +export type LoginFlowType = 'DeviceCode' | 'InteractiveBrowser' | 'AdoCodespacesAuth' | 'VisualStudioCode' | 'AzureCli' | 'AzureDeveloperCli' | 'AzurePowerShell'; + // @public (undocumented) class RushAzureStorageBuildCachePlugin implements IRushPlugin { // (undocumented) diff --git a/common/reviews/api/rush-buildxl-graph-plugin.api.md b/common/reviews/api/rush-buildxl-graph-plugin.api.md new file mode 100644 index 00000000000..dbec0c82af6 --- /dev/null +++ b/common/reviews/api/rush-buildxl-graph-plugin.api.md @@ -0,0 +1,49 @@ +## API Report File for "@rushstack/rush-buildxl-graph-plugin" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { IRushPlugin } from '@rushstack/rush-sdk'; +import { RushConfiguration } from '@rushstack/rush-sdk'; +import { RushSession } from '@rushstack/rush-sdk'; + +// @public +class DropBuildGraphPlugin implements IRushPlugin { + constructor(options: IDropGraphPluginOptions); + // (undocumented) + apply(session: RushSession, rushConfiguration: RushConfiguration): void; + // (undocumented) + readonly pluginName: string; +} +export default DropBuildGraphPlugin; + +// @public +export interface IBuildXLRushGraph { + // (undocumented) + nodes: IGraphNode[]; + // (undocumented) + repoSettings: { + commonTempFolder: string; + }; +} + +// @public (undocumented) +export interface IDropGraphPluginOptions { + buildXLCommandNames: string[]; +} + +// @public (undocumented) +export interface IGraphNode { + cacheable?: false; + command: string; + dependencies: string[]; + id: string; + package: string; + task: string; + workingDirectory: string; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/common/reviews/api/rush-lib.api.md b/common/reviews/api/rush-lib.api.md index 7b786b9f4cc..1583568c3a4 100644 --- a/common/reviews/api/rush-lib.api.md +++ b/common/reviews/api/rush-lib.api.md @@ -7,19 +7,32 @@ /// import { AsyncParallelHook } from 'tapable'; +import { AsyncSeriesBailHook } from 'tapable'; import { AsyncSeriesHook } from 'tapable'; import { AsyncSeriesWaterfallHook } from 'tapable'; import type { CollatedWriter } from '@rushstack/stream-collator'; import type { CommandLineParameter } from '@rushstack/ts-command-line'; +import { CommandLineParameterKind } from '@rushstack/ts-command-line'; +import { CredentialCache } from '@rushstack/credential-cache'; import { HookMap } from 'tapable'; +import { ICredentialCacheEntry } from '@rushstack/credential-cache'; +import { ICredentialCacheOptions } from '@rushstack/credential-cache'; +import { IFileDiffStatus } from '@rushstack/package-deps-hash'; import { IPackageJson } from '@rushstack/node-core-library'; -import { ITerminal } from '@rushstack/node-core-library'; -import { ITerminalProvider } from '@rushstack/node-core-library'; +import { IPrefixMatch } from '@rushstack/lookup-by-path'; +import type { IProblemCollector } from '@rushstack/terminal'; +import { ITerminal } from '@rushstack/terminal'; +import { ITerminalProvider } from '@rushstack/terminal'; +import { JsonNull } from '@rushstack/node-core-library'; import { JsonObject } from '@rushstack/node-core-library'; +import { LookupByPath } from '@rushstack/lookup-by-path'; import { PackageNameParser } from '@rushstack/node-core-library'; +import type { PerformanceEntry as PerformanceEntry_2 } from 'node:perf_hooks'; import type { StdioSummarizer } from '@rushstack/terminal'; import { SyncHook } from 'tapable'; -import { Terminal } from '@rushstack/node-core-library'; +import { SyncWaterfallHook } from 'tapable'; +import { Terminal } from '@rushstack/terminal'; +import type { TerminalWritable } from '@rushstack/terminal'; // @public export class ApprovedPackagesConfiguration { @@ -60,6 +73,7 @@ export class ApprovedPackagesPolicy { // @beta export class BuildCacheConfiguration { readonly buildCacheEnabled: boolean; + readonly cacheHashSalt: string | undefined; cacheWriteEnabled: boolean; readonly cloudCacheProvider: ICloudBuildCacheProvider | undefined; static getBuildCacheConfigFilePath(rushConfiguration: RushConfiguration): string; @@ -95,36 +109,102 @@ export class ChangeManager { // @beta (undocumented) export type CloudBuildCacheProviderFactory = (buildCacheJson: IBuildCacheJson) => ICloudBuildCacheProvider | Promise; +// @beta +export class CobuildConfiguration { + readonly cobuildContextId: string | undefined; + readonly cobuildFeatureEnabled: boolean; + readonly cobuildLeafProjectLogOnlyAllowed: boolean; + readonly cobuildRunnerId: string; + readonly cobuildWithoutCacheAllowed: boolean; + // (undocumented) + createLockProviderAsync(terminal: ITerminal): Promise; + // (undocumented) + destroyLockProviderAsync(): Promise; + // (undocumented) + static getCobuildConfigFilePath(rushConfiguration: RushConfiguration): string; + // (undocumented) + getCobuildLockProvider(): ICobuildLockProvider; + static tryLoadAsync(terminal: ITerminal, rushConfiguration: RushConfiguration, rushSession: RushSession): Promise; +} + +// @beta (undocumented) +export type CobuildLockProviderFactory = (cobuildJson: ICobuildJson) => ICobuildLockProvider | Promise; + // @public export class CommonVersionsConfiguration { readonly allowedAlternativeVersions: Map>; + readonly ensureConsistentVersions: boolean; readonly filePath: string; getAllPreferredVersions(): Map; getPreferredVersionsHash(): string; readonly implicitlyPreferredVersions: boolean | undefined; - static loadFromFile(jsonFilename: string): CommonVersionsConfiguration; + // @deprecated (undocumented) + static loadFromFile(jsonFilePath: string, rushConfiguration?: RushConfiguration): CommonVersionsConfiguration; + static loadFromFileAsync(jsonFilePath: string, rushConfiguration?: RushConfiguration): Promise; readonly preferredVersions: Map; + // @deprecated (undocumented) save(): boolean; + saveAsync(): Promise; } -// @beta (undocumented) -export class CredentialCache { +export { CredentialCache } + +// @beta +export enum CustomTipId { + // (undocumented) + TIP_PNPM_INVALID_NODE_VERSION = "TIP_PNPM_INVALID_NODE_VERSION", + // (undocumented) + TIP_PNPM_MISMATCHED_RELEASE_CHANNEL = "TIP_PNPM_MISMATCHED_RELEASE_CHANNEL", + // (undocumented) + TIP_PNPM_NO_MATCHING_VERSION = "TIP_PNPM_NO_MATCHING_VERSION", + // (undocumented) + TIP_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE = "TIP_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE", + // (undocumented) + TIP_PNPM_OUTDATED_LOCKFILE = "TIP_PNPM_OUTDATED_LOCKFILE", + // (undocumented) + TIP_PNPM_PEER_DEP_ISSUES = "TIP_PNPM_PEER_DEP_ISSUES", // (undocumented) - deleteCacheEntry(cacheId: string): void; + TIP_PNPM_TARBALL_INTEGRITY = "TIP_PNPM_TARBALL_INTEGRITY", // (undocumented) - dispose(): void; + TIP_PNPM_UNEXPECTED_STORE = "TIP_PNPM_UNEXPECTED_STORE", // (undocumented) - static initializeAsync(options: ICredentialCacheOptions): Promise; + TIP_RUSH_DISALLOW_INSECURE_SHA1 = "TIP_RUSH_DISALLOW_INSECURE_SHA1", // (undocumented) - saveIfModifiedAsync(): Promise; + TIP_RUSH_INCONSISTENT_VERSIONS = "TIP_RUSH_INCONSISTENT_VERSIONS" +} + +// @beta +export class CustomTipsConfiguration { + constructor(configFilePath: string); + static customTipRegistry: Readonly>; // (undocumented) - setCacheEntry(cacheId: string, entry: ICredentialCacheEntry): void; + readonly providedCustomTipsByTipId: ReadonlyMap; + // @internal + _showErrorTip(terminal: ITerminal, tipId: CustomTipId): void; + // @internal + _showInfoTip(terminal: ITerminal, tipId: CustomTipId): void; + // @internal + _showTip(terminal: ITerminal, tipId: CustomTipId): void; + // @internal + _showWarningTip(terminal: ITerminal, tipId: CustomTipId): void; +} + +// @beta +export enum CustomTipSeverity { // (undocumented) - trimExpiredEntries(): void; + Error = "Error", // (undocumented) - tryGetCacheEntry(cacheId: string): ICredentialCacheEntry | undefined; + Info = "Info", // (undocumented) - static usingAsync(options: ICredentialCacheOptions, doActionAsync: (credentialCache: CredentialCache) => Promise | void): Promise; + Warning = "Warning" +} + +// @beta +export enum CustomTipType { + // (undocumented) + pnpm = "pnpm", + // (undocumented) + rush = "rush" } // @public (undocumented) @@ -148,16 +228,23 @@ export class EnvironmentConfiguration { static get allowWarningsInSuccessfulBuild(): boolean; static get buildCacheCredential(): string | undefined; static get buildCacheEnabled(): boolean | undefined; + static get buildCacheOverrideJson(): string | undefined; + static get buildCacheOverrideJsonFilePath(): string | undefined; static get buildCacheWriteAllowed(): boolean | undefined; + static get cobuildContextId(): string | undefined; + static get cobuildLeafProjectLogOnlyAllowed(): boolean | undefined; + static get cobuildRunnerId(): string | undefined; // Warning: (ae-forgotten-export) The symbol "IEnvironment" needs to be exported by the entry point index.d.ts // // @internal static _getRushGlobalFolderOverride(processEnv: IEnvironment): string | undefined; static get gitBinaryPath(): string | undefined; + static get hasBeenValidated(): boolean; // (undocumented) static parseBooleanEnvironmentVariable(name: string, value: string | undefined): boolean | undefined; static get pnpmStorePathOverride(): string | undefined; static get pnpmVerifyStoreIntegrity(): boolean | undefined; + static get quietMode(): boolean; static reset(): void; static get rushGlobalFolderOverride(): string | undefined; static get rushTempFolderOverride(): string | undefined; @@ -181,19 +268,30 @@ export const EnvironmentVariableNames: { readonly RUSH_BUILD_CACHE_CREDENTIAL: "RUSH_BUILD_CACHE_CREDENTIAL"; readonly RUSH_BUILD_CACHE_ENABLED: "RUSH_BUILD_CACHE_ENABLED"; readonly RUSH_BUILD_CACHE_WRITE_ALLOWED: "RUSH_BUILD_CACHE_WRITE_ALLOWED"; + readonly RUSH_BUILD_CACHE_OVERRIDE_JSON: "RUSH_BUILD_CACHE_OVERRIDE_JSON"; + readonly RUSH_BUILD_CACHE_OVERRIDE_JSON_FILE_PATH: "RUSH_BUILD_CACHE_OVERRIDE_JSON_FILE_PATH"; + readonly RUSH_COBUILD_CONTEXT_ID: "RUSH_COBUILD_CONTEXT_ID"; + readonly RUSH_COBUILD_RUNNER_ID: "RUSH_COBUILD_RUNNER_ID"; + readonly RUSH_COBUILD_LEAF_PROJECT_LOG_ONLY_ALLOWED: "RUSH_COBUILD_LEAF_PROJECT_LOG_ONLY_ALLOWED"; readonly RUSH_GIT_BINARY_PATH: "RUSH_GIT_BINARY_PATH"; readonly RUSH_TAR_BINARY_PATH: "RUSH_TAR_BINARY_PATH"; - readonly RUSH_LIB_PATH: "_RUSH_LIB_PATH"; + readonly _RUSH_RECURSIVE_RUSHX_CALL: "_RUSH_RECURSIVE_RUSHX_CALL"; + readonly _RUSH_LIB_PATH: "_RUSH_LIB_PATH"; readonly RUSH_INVOKED_FOLDER: "RUSH_INVOKED_FOLDER"; + readonly RUSH_INVOKED_ARGS: "RUSH_INVOKED_ARGS"; + readonly RUSH_QUIET_MODE: "RUSH_QUIET_MODE"; }; // @beta -export enum Event { +enum Event_2 { postRushBuild = 4, postRushInstall = 2, + postRushx = 6, preRushBuild = 3, - preRushInstall = 1 + preRushInstall = 1, + preRushx = 5 } +export { Event_2 as Event } // @beta export class EventHooks { @@ -201,13 +299,13 @@ export class EventHooks { // // @internal constructor(eventHooksJson: IEventHooksJson); - get(event: Event): string[]; + get(event: Event_2): string[]; } // @public export class ExperimentsConfiguration { // @internal - constructor(jsonFileName: string); + constructor(jsonFilePath: string); // @beta readonly configuration: Readonly; } @@ -215,14 +313,35 @@ export class ExperimentsConfiguration { // @beta export class FileSystemBuildCacheProvider { constructor(options: IFileSystemBuildCacheProviderOptions); - getCacheEntryPath(cacheId: string): string; + readonly getCacheEntryPath: (cacheId: string) => string; tryGetCacheEntryPathByIdAsync(terminal: ITerminal, cacheId: string): Promise; trySetCacheEntryBufferAsync(terminal: ITerminal, cacheId: string, entryBuffer: Buffer): Promise; } +// @internal +export class _FlagFile { + constructor(folderPath: string, flagName: string, initialState: TState); + clearAsync(): Promise; + createAsync(): Promise; + isValidAsync(): Promise; + readonly path: string; + protected _state: TState; +} + // @beta export type GetCacheEntryIdFunction = (options: IGenerateCacheEntryIdOptions) => string; +// @beta +export type GetInputsSnapshotAsyncFn = () => Promise; + +// @alpha (undocumented) +export interface IBaseOperationExecutionResult { + getStateHash(): string; + getStateHashComponents(): IOperationStateHashComponents; + readonly metadataFolderPath: string; + readonly operation: Operation; +} + // @internal (undocumented) export interface _IBuiltInPluginConfiguration extends _IRushPluginConfigurationBase { // (undocumented) @@ -235,16 +354,61 @@ export interface ICloudBuildCacheProvider { deleteCachedCredentialsAsync(terminal: ITerminal): Promise; // (undocumented) readonly isCacheWriteAllowed: boolean; + tryDownloadCacheEntryToFileAsync?(terminal: ITerminal, cacheId: string, localFilePath: string): Promise; // (undocumented) tryGetCacheEntryBufferByIdAsync(terminal: ITerminal, cacheId: string): Promise; // (undocumented) trySetCacheEntryBufferAsync(terminal: ITerminal, cacheId: string, entryBuffer: Buffer): Promise; + tryUploadCacheEntryFromFileAsync?(terminal: ITerminal, cacheId: string, localFilePath: string): Promise; // (undocumented) updateCachedCredentialAsync(terminal: ITerminal, credential: string): Promise; // (undocumented) updateCachedCredentialInteractiveAsync(terminal: ITerminal): Promise; } +// @beta (undocumented) +export interface ICobuildCompletedState { + cacheId: string; + // (undocumented) + status: OperationStatus.Success | OperationStatus.SuccessWithWarning | OperationStatus.Failure; +} + +// @beta (undocumented) +export interface ICobuildContext { + cacheId: string; + clusterId: string; + completedStateKey: string; + contextId: string; + lockExpireTimeInSeconds: number; + lockKey: string; + packageName: string; + phaseName: string; + runnerId: string; +} + +// @beta (undocumented) +export interface ICobuildJson { + // (undocumented) + cobuildFeatureEnabled: boolean; + // (undocumented) + cobuildLockProvider: string; +} + +// @beta (undocumented) +export interface ICobuildLockProvider { + acquireLockAsync(context: Readonly): Promise; + connectAsync(): Promise; + disconnectAsync(): Promise; + getCompletedStateAsync(context: Readonly): Promise; + renewLockAsync(context: Readonly): Promise; + setCompletedStateAsync(context: Readonly, state: ICobuildCompletedState): Promise; +} + +// @alpha +export interface IConfigurableOperation extends IBaseOperationExecutionResult { + enabled: boolean; +} + // @public export interface IConfigurationEnvironment { [environmentVariableName: string]: IConfigurationEnvironmentVariable; @@ -259,32 +423,42 @@ export interface IConfigurationEnvironmentVariable { // @alpha export interface ICreateOperationsContext { readonly buildCacheConfiguration: BuildCacheConfiguration | undefined; + readonly changedProjectsOnly: boolean; + readonly cobuildConfiguration: CobuildConfiguration | undefined; readonly customParameters: ReadonlyMap; + readonly generateFullGraph?: boolean; + readonly includePhaseDeps: boolean; readonly isIncrementalBuildAllowed: boolean; - readonly isInitial: boolean; readonly isWatch: boolean; - readonly phaseOriginal: ReadonlySet; + readonly parallelism: Parallelism; readonly phaseSelection: ReadonlySet; - readonly projectChangeAnalyzer: ProjectChangeAnalyzer; + readonly projectConfigurations: ReadonlyMap; readonly projectSelection: ReadonlySet; - readonly projectsInUnknownState: ReadonlySet; readonly rushConfiguration: RushConfiguration; } -// @beta (undocumented) -export interface ICredentialCacheEntry { - // (undocumented) - credential: string; - // (undocumented) - credentialMetadata?: object; +export { ICredentialCacheEntry } + +export { ICredentialCacheOptions } + +// @beta +export interface ICustomTipInfo { + isMatch?: (str: string) => boolean; + severity: CustomTipSeverity; // (undocumented) - expires?: Date; + tipId: CustomTipId; + type: CustomTipType; } -// @beta (undocumented) -export interface ICredentialCacheOptions { - // (undocumented) - supportEditing: boolean; +// @beta +export interface ICustomTipItemJson { + message: string; + tipId: CustomTipId; +} + +// @beta +export interface ICustomTipsJson { + customTips?: ICustomTipItemJson[]; } // @beta (undocumented) @@ -301,15 +475,26 @@ export interface IExecutionResult { // @beta export interface IExperimentsJson { + allowCobuildWithoutCache?: boolean; buildCacheWithAllowWarningsInSuccessfulBuild?: boolean; + buildSkipWithAllowWarningsInSuccessfulBuild?: boolean; cleanInstallAfterNpmrcChanges?: boolean; + enableSubpathScan?: boolean; + exemptDecoupledDependenciesBetweenSubspaces?: boolean; forbidPhantomResolvableNodeModulesFolders?: boolean; + generateProjectImpactGraphDuringRushUpdate?: boolean; noChmodFieldInTarHeaderNormalization?: boolean; + omitAppleDoubleFilesFromBuildCache?: boolean; omitImportersFromPreventManualShrinkwrapChanges?: boolean; - phasedCommands?: boolean; printEventHooksOutputToConsole?: boolean; + rushAlerts?: boolean; + strictChangefileValidation?: boolean; + useDirectFileTransfersForBuildCache?: boolean; + useIPCScriptsInWatchMode?: boolean; usePnpmFrozenLockfileForRushInstall?: boolean; + usePnpmLockfileOnlyThenFrozenLockfileForRushUpdate?: boolean; usePnpmPreferFrozenLockfileForRushUpdate?: boolean; + usePnpmSyncForInjectedDependencies?: boolean; } // @beta @@ -328,6 +513,7 @@ export interface IGenerateCacheEntryIdOptions { // @beta (undocumented) export interface IGetChangedProjectsOptions { enableFiltering: boolean; + excludeVersionOnlyChanges?: boolean; includeExternalDependencies: boolean; // (undocumented) shouldFetch?: boolean; @@ -335,10 +521,29 @@ export interface IGetChangedProjectsOptions { targetBranchName: string; // (undocumented) terminal: ITerminal; + // (undocumented) + variant?: string; } // @beta export interface IGlobalCommand extends IRushCommand { + getCustomParametersByLongName(longName: string): TParameter; + setHandled(): void; +} + +// @public +export interface IIndividualVersionJson extends IVersionPolicyJson { + // (undocumented) + lockedMajor?: number; +} + +// @beta +export interface IInputsSnapshot { + getOperationOwnStateHash(project: IRushConfigurationProjectForSnapshot, operationName?: string): string; + getTrackedFileHashesForOperation(project: IRushConfigurationProjectForSnapshot, operationName?: string): ReadonlyMap; + readonly hashes: ReadonlyMap; + readonly hasUncommittedChanges: boolean; + readonly rootDirectory: string; } // @public @@ -350,12 +555,23 @@ export interface ILaunchOptions { terminalProvider?: ITerminalProvider; } -// @internal (undocumented) -export interface _ILockfileValidityCheckOptions { +// @public +export interface ILockStepVersionJson extends IVersionPolicyJson { + // (undocumented) + mainProject?: string; // (undocumented) - rushVerb?: string; + nextBump?: string; // (undocumented) - statePropertiesToIgnore?: string[]; + version: string; +} + +// @alpha +export interface ILogFilePaths { + error: string; + jsonl: string; + jsonlFolder: string; + text: string; + textFolder: string; } // @beta (undocumented) @@ -368,15 +584,13 @@ export interface ILogger { // @public export class IndividualVersionPolicy extends VersionPolicy { - // Warning: (ae-forgotten-export) The symbol "IIndividualVersionJson" needs to be exported by the entry point index.d.ts - // // @internal constructor(versionPolicyJson: IIndividualVersionJson); bump(bumpType?: BumpType, identifier?: string): void; ensure(project: IPackageJson, force?: boolean): IPackageJson | undefined; - // @internal - get _json(): IIndividualVersionJson; - readonly lockedMajor: number | undefined; + // @internal (undocumented) + readonly _json: IIndividualVersionJson; + get lockedMajor(): number | undefined; validate(versionString: string, packageName: string): void; } @@ -384,45 +598,109 @@ export class IndividualVersionPolicy extends VersionPolicy { export interface _INpmOptionsJson extends IPackageManagerOptionsJsonBase { } +// @internal (undocumented) +export interface _IOperationBuildCacheOptions { + buildCacheConfiguration: BuildCacheConfiguration; + excludeAppleDoubleFiles: boolean; + terminal: ITerminal; + useDirectFileTransfersForBuildCache: boolean; +} + // @alpha -export interface IOperationExecutionResult { +export interface IOperationExecutionResult extends IBaseOperationExecutionResult, IOperationLastState { + readonly enabled: boolean; readonly error: Error | undefined; + readonly logFilePaths: ILogFilePaths | undefined; readonly nonCachedDurationMs: number | undefined; + readonly problemCollector: IProblemCollector; + readonly silent: boolean; readonly status: OperationStatus; readonly stdioSummarizer: StdioSummarizer; readonly stopwatch: IStopwatchResult; } +// @alpha +export interface IOperationGraph { + readonly abortController: AbortController; + abortCurrentIterationAsync(): Promise; + addTerminalDestination(destination: TerminalWritable): void; + allowOversubscription: boolean; + closeRunnersAsync(operations?: Iterable): Promise; + debugMode: boolean; + executeScheduledIterationAsync(): Promise; + readonly hasScheduledIteration: boolean; + readonly hooks: OperationGraphHooks; + invalidateOperations(operations?: Iterable, reason?: string): void; + readonly operations: ReadonlySet; + get parallelism(): number; + set parallelism(value: Parallelism); + pauseNextIteration: boolean; + quietMode: boolean; + removeTerminalDestination(destination: TerminalWritable, close?: boolean): boolean; + readonly resultByOperation: ReadonlyMap; + scheduleIterationAsync(options: IOperationGraphIterationOptions): Promise; + setEnabledStates(operations: Iterable, targetState: Operation['enabled'], mode: 'safe' | 'unsafe'): boolean; + readonly status: OperationStatus; + readonly terminalDestinations: ReadonlySet; +} + +// @alpha +export interface IOperationGraphContext extends ICreateOperationsContext { + readonly initialSnapshot?: IInputsSnapshot; +} + +// @alpha +export interface IOperationGraphIterationOptions { + // (undocumented) + inputsSnapshot?: IInputsSnapshot; + startTime?: number; +} + +// @beta +export interface IOperationLastState { + readonly status: OperationStatus; +} + // @internal (undocumented) export interface _IOperationMetadata { + // (undocumented) + cobuildContextId: string | undefined; + // (undocumented) + cobuildRunnerId: string | undefined; // (undocumented) durationInSeconds: number; // (undocumented) errorLogPath: string; // (undocumented) + logChunksPath: string; + // (undocumented) logPath: string; } // @internal (undocumented) export interface _IOperationMetadataManagerOptions { // (undocumented) - phase: IPhase; - // (undocumented) - rushProject: RushConfigurationProject; + operation: Operation; } // @alpha export interface IOperationOptions { - phase?: IPhase | undefined; - project?: RushConfigurationProject | undefined; + enabled?: OperationEnabledState; + logFilenameIdentifier: string; + phase: IPhase; + project: RushConfigurationProject; runner?: IOperationRunner | undefined; + settings?: IOperationSettings | undefined; } // @beta export interface IOperationRunner { - executeAsync(context: IOperationRunnerContext): Promise; - isCacheWriteAllowed: boolean; - isSkipAllowed: boolean; + cacheable: boolean; + closeAsync?(): Promise; + executeAsync(context: IOperationRunnerContext, lastState?: IOperationLastState): Promise; + getConfigHash(): string; + readonly isActive?: boolean; + readonly isNoOp?: boolean; readonly name: string; reportTiming: boolean; silent: boolean; @@ -433,13 +711,35 @@ export interface IOperationRunner { export interface IOperationRunnerContext { collatedWriter: CollatedWriter; debugMode: boolean; + environment: IEnvironment | undefined; + error?: Error; + getInvalidateCallback(): (reason: string) => void; // @internal - _operationMetadataManager?: _OperationMetadataManager; + _operationMetadataManager: _OperationMetadataManager; quietMode: boolean; - stdioSummarizer: StdioSummarizer; + runWithTerminalAsync(callback: (terminal: ITerminal, terminalProvider: ITerminalProvider) => Promise, options: { + createLogFile: boolean; + logFileSuffix?: string; + }): Promise; + status: OperationStatus; stopwatch: IStopwatchResult; } +// @alpha (undocumented) +export interface IOperationSettings { + allowCobuildWithoutCache?: boolean; + dependsOnAdditionalFiles?: string[]; + dependsOnEnvVars?: string[]; + dependsOnNodeVersion?: boolean | NodeVersionGranularity; + disableBuildCacheForOperation?: boolean; + ignoreChangedProjectsOnlyFlag?: boolean; + operationName: string; + outputFolderNames?: string[]; + parameterNamesToIgnore?: string[]; + sharding?: IRushPhaseSharding; + weight?: number | `${number}%`; +} + // @internal (undocumented) export interface _IOperationStateFileOptions { // (undocumented) @@ -448,8 +748,19 @@ export interface _IOperationStateFileOptions { projectFolder: string; } +// @alpha +export interface IOperationStateHashComponents { + readonly config: string; + readonly dependencies: readonly string[]; + readonly local: string; +} + // @internal (undocumented) export interface _IOperationStateJson { + // (undocumented) + cobuildContextId: string | undefined; + // (undocumented) + cobuildRunnerId: string | undefined; // (undocumented) nonCachedDurationMs: number; } @@ -459,6 +770,12 @@ export interface IPackageManagerOptionsJsonBase { environmentVariables?: IConfigurationEnvironment; } +// @beta +export interface IParallelismScalar { + // (undocumented) + readonly scalar: number; +} + // @alpha export interface IPhase { allowWarningsOnSuccess: boolean; @@ -481,38 +798,137 @@ export type IPhaseBehaviorForMissingScript = 'silent' | 'log' | 'error'; export interface IPhasedCommand extends IRushCommand { // @alpha readonly hooks: PhasedCommandHooks; + // @alpha + readonly sessionAbortController: AbortController; +} + +// @alpha +export interface IPhasedCommandPlugin { + apply(hooks: PhasedCommandHooks): void; +} + +// @public +export interface IPnpmLockfilePolicies { + disallowInsecureSha1?: { + enabled: boolean; + exemptPackageVersions: Record; + }; } // @internal export interface _IPnpmOptionsJson extends IPackageManagerOptionsJsonBase { + // (undocumented) + $schema?: string; + alwaysFullInstall?: boolean; + alwaysInjectDependenciesFromOtherSubspaces?: boolean; + autoInstallPeers?: boolean; + globalAllowBuilds?: Record; globalAllowedDeprecatedVersions?: Record; + globalCatalogs?: Record>; + globalIgnoredOptionalDependencies?: string[]; globalNeverBuiltDependencies?: string[]; + globalOnlyBuiltDependencies?: string[]; globalOverrides?: Record; - // Warning: (ae-forgotten-export) The symbol "IPnpmPackageExtension" needs to be exported by the entry point index.d.ts globalPackageExtensions?: Record; globalPatchedDependencies?: Record; - // Warning: (ae-forgotten-export) The symbol "IPnpmPeerDependencyRules" needs to be exported by the entry point index.d.ts globalPeerDependencyRules?: IPnpmPeerDependencyRules; - pnpmStore?: PnpmStoreOptions; + // @deprecated (undocumented) + minimumReleaseAge?: number; + minimumReleaseAgeExclude?: string[]; + minimumReleaseAgeMinutes?: number; + pnpmLockfilePolicies?: IPnpmLockfilePolicies; + pnpmStore?: PnpmStoreLocation; preventManualShrinkwrapChanges?: boolean; + resolutionMode?: PnpmResolutionMode; strictPeerDependencies?: boolean; + trustPolicy?: PnpmTrustPolicy; + trustPolicyExclude?: string[]; + trustPolicyIgnoreAfterMinutes?: number; unsupportedPackageJsonSettings?: unknown; useWorkspaces?: boolean; } -// @beta -export interface IPrefixMatch { +// @public (undocumented) +export interface IPnpmPackageExtension { + // (undocumented) + dependencies?: Record; + // (undocumented) + optionalDependencies?: Record; + // (undocumented) + peerDependencies?: Record; + // (undocumented) + peerDependenciesMeta?: IPnpmPeerDependenciesMeta; +} + +// @public (undocumented) +export interface IPnpmPeerDependenciesMeta { + // (undocumented) + [packageName: string]: { + optional?: boolean; + }; +} + +// @public (undocumented) +export interface IPnpmPeerDependencyRules { + // (undocumented) + allowAny?: string[]; // (undocumented) - index: number; + allowedVersions?: Record; // (undocumented) - value: TItem; + ignoreMissing?: string[]; } +export { IPrefixMatch } + +// @internal (undocumented) +export type _IProjectBuildCacheOptions = _IOperationBuildCacheOptions & { + projectOutputFolderNames: ReadonlyArray; + project: RushConfigurationProject; + operationStateHash: string; + phaseName: string; +}; + // @beta export interface IRushCommand { readonly actionName: string; } +// @beta +export interface IRushCommandLineAction { + // (undocumented) + actionName: string; + // (undocumented) + parameters: IRushCommandLineParameter[]; +} + +// @beta +export interface IRushCommandLineParameter { + readonly description: string; + readonly environmentVariable?: string; + readonly kind: keyof typeof CommandLineParameterKind; + readonly longName: string; + readonly required?: boolean; + readonly shortName?: string; +} + +// @beta +export interface IRushCommandLineSpec { + // (undocumented) + actions: IRushCommandLineAction[]; +} + +// @beta (undocumented) +export type IRushConfigurationProjectForSnapshot = Pick; + +// @alpha (undocumented) +export interface IRushPhaseSharding { + count: number; + outputFolderArgumentFormat?: string; + shardArgumentFormat?: string; + // @deprecated (undocumented) + shardOperationSettings?: unknown; +} + // @beta (undocumented) export interface IRushPlugin { // (undocumented) @@ -527,6 +943,14 @@ export interface _IRushPluginConfigurationBase { pluginName: string; } +// @internal +export interface _IRushProjectJson { + disableBuildCacheForProject?: boolean; + incrementalBuildIgnoredGlobs?: string[]; + // (undocumented) + operationSettings?: IOperationSettings[]; +} + // @beta (undocumented) export interface IRushSessionOptions { // (undocumented) @@ -553,6 +977,7 @@ export interface ITelemetryData { readonly machineInfo?: ITelemetryMachineInfo; readonly name: string; readonly operationResults?: Record; + readonly performanceEntries?: readonly PerformanceEntry_2[]; readonly platform?: string; readonly result: 'Succeeded' | 'Failed'; readonly rushVersion?: string; @@ -575,6 +1000,7 @@ export interface ITelemetryOperationResult { nonCachedDurationMs?: number; result: string; startTimestampMs?: number; + wasExecutedOnThisMachine?: boolean; } // @public @@ -583,52 +1009,46 @@ export interface ITryFindRushJsonLocationOptions { startingFolder?: string; } -// @internal -export interface _IYarnOptionsJson extends IPackageManagerOptionsJsonBase { - ignoreEngines?: boolean; +// @public +export interface IVersionPolicyJson { + // (undocumented) + definitionName: string; + // Warning: (ae-forgotten-export) The symbol "IVersionPolicyDependencyJson" needs to be exported by the entry point index.d.ts + // + // (undocumented) + dependencies?: IVersionPolicyDependencyJson; + // (undocumented) + exemptFromRushChange?: boolean; + // (undocumented) + includeEmailInChangeFile?: boolean; + // (undocumented) + policyName: string; } // @internal -export class _LastInstallFlag { - constructor(folderPath: string, state?: JsonObject); - checkValidAndReportStoreIssues(options: _ILockfileValidityCheckOptions & { - rushVerb: string; - }): boolean; - clear(): void; - create(): void; - protected get flagName(): string; - isValid(options?: _ILockfileValidityCheckOptions): boolean; - readonly path: string; +export interface _IYarnOptionsJson extends IPackageManagerOptionsJsonBase { + ignoreEngines?: boolean; } // @public export class LockStepVersionPolicy extends VersionPolicy { - // Warning: (ae-forgotten-export) The symbol "ILockStepVersionJson" needs to be exported by the entry point index.d.ts - // // @internal constructor(versionPolicyJson: ILockStepVersionJson); bump(bumpType?: BumpType, identifier?: string): void; ensure(project: IPackageJson, force?: boolean): IPackageJson | undefined; - // @internal - get _json(): ILockStepVersionJson; - readonly mainProject: string | undefined; - readonly nextBump: BumpType | undefined; + // @internal (undocumented) + readonly _json: ILockStepVersionJson; + get mainProject(): string | undefined; + get nextBump(): BumpType | undefined; update(newVersionString: string): boolean; validate(versionString: string, packageName: string): void; get version(): string; } -// @beta -export class LookupByPath { - constructor(entries?: Iterable<[string, TItem]>, delimiter?: string); - readonly delimiter: string; - findChildPath(childPath: string): TItem | undefined; - findChildPathFromSegments(childPathSegments: Iterable): TItem | undefined; - findLongestPrefixMatch(query: string): IPrefixMatch | undefined; - static iteratePathSegments(serializedPath: string, delimiter?: string): Iterable; - setItem(serializedPath: string, value: TItem): this; - setItemFromSegments(pathSegments: Iterable, value: TItem): this; -} +export { LookupByPath } + +// @alpha +export type NodeVersionGranularity = 'major' | 'minor' | 'patch'; // @public export class NpmOptionsConfiguration extends PackageManagerOptionsConfigurationBase { @@ -638,32 +1058,96 @@ export class NpmOptionsConfiguration extends PackageManagerOptionsConfigurationB // @alpha export class Operation { - constructor(options?: IOperationOptions); + constructor(options: IOperationOptions); addDependency(dependency: Operation): void; - readonly associatedPhase: IPhase | undefined; - readonly associatedProject: RushConfigurationProject | undefined; + readonly associatedPhase: IPhase; + readonly associatedProject: RushConfigurationProject; readonly consumers: ReadonlySet; deleteDependency(dependency: Operation): void; readonly dependencies: ReadonlySet; - get name(): string | undefined; + enabled: OperationEnabledState; + get isNoOp(): boolean; + logFilenameIdentifier: string; + get name(): string; runner: IOperationRunner | undefined; - weight: number; + settings: IOperationSettings | undefined; + weight: Parallelism; +} + +// @internal (undocumented) +export class _OperationBuildCache { + // (undocumented) + get cacheId(): string | undefined; + // (undocumented) + static forOperation(executionResult: IBaseOperationExecutionResult, options: _IOperationBuildCacheOptions): _OperationBuildCache; + // (undocumented) + static getOperationBuildCache(options: _IProjectBuildCacheOptions): _OperationBuildCache; + // (undocumented) + tryRestoreFromCacheAsync(terminal: ITerminal, specifiedCacheId?: string): Promise; + // (undocumented) + trySetCacheEntryAsync(terminal: ITerminal, specifiedCacheId?: string): Promise; +} + +// @alpha +export type OperationEnabledState = boolean | 'ignore-dependency-changes'; + +// @alpha +export class OperationGraphHooks { + readonly afterExecuteIterationAsync: AsyncSeriesWaterfallHook<[ + OperationStatus, + ReadonlyMap, + IOperationGraphIterationOptions + ]>; + readonly afterExecuteOperationAsync: AsyncSeriesHook<[ + IOperationRunnerContext & IOperationExecutionResult + ]>; + readonly beforeExecuteIterationAsync: AsyncSeriesBailHook<[ + ReadonlyMap, + IOperationGraphIterationOptions + ], OperationStatus | undefined | void>; + readonly beforeExecuteOperationAsync: AsyncSeriesBailHook<[ + IOperationRunnerContext & IOperationExecutionResult + ], OperationStatus | undefined>; + readonly beforeLog: SyncHook; + readonly configureIteration: SyncHook<[ + ReadonlyMap, + ReadonlyMap, + IOperationGraphIterationOptions + ]>; + readonly createEnvironmentForOperation: SyncWaterfallHook<[ + IEnvironment, + IOperationRunnerContext & IOperationExecutionResult + ]>; + readonly onEnableStatesChanged: SyncHook<[ReadonlySet]>; + readonly onExecutionStatesUpdated: SyncHook<[ReadonlySet]>; + readonly onGraphStateChanged: SyncHook<[IOperationGraph]>; + readonly onIdle: SyncHook; + readonly onInvalidateOperations: SyncHook<[Iterable, string | undefined]>; + readonly onIterationScheduled: SyncHook<[ReadonlyMap]>; } // @internal export class _OperationMetadataManager { constructor(options: _IOperationMetadataManagerOptions); - get relativeFilepaths(): string[]; // (undocumented) - saveAsync({ durationInSeconds, logPath, errorLogPath }: _IOperationMetadata): Promise; + readonly logFilenameIdentifier: string; + get metadataFolderPath(): string; + // (undocumented) + saveAsync(input: _IOperationMetadata): Promise; // (undocumented) readonly stateFile: _OperationStateFile; // (undocumented) - tryRestoreAsync({ terminal, logPath, errorLogPath }: { + tryRestoreAsync(input: { + terminalProvider: ITerminalProvider; terminal: ITerminal; - logPath: string; errorLogPath: string; + cobuildContextId?: string; + cobuildRunnerId?: string; }): Promise; + // (undocumented) + tryRestoreStopwatch(originalStopwatch: IStopwatchResult): IStopwatchResult; + // (undocumented) + wasCobuilt: boolean; } // @internal @@ -683,15 +1167,18 @@ export class _OperationStateFile { // @beta export enum OperationStatus { + Aborted = "ABORTED", Blocked = "BLOCKED", Executing = "EXECUTING", Failure = "FAILURE", FromCache = "FROM CACHE", NoOp = "NO OP", + Queued = "QUEUED", Ready = "READY", Skipped = "SKIPPED", Success = "SUCCESS", - SuccessWithWarning = "SUCCESS WITH WARNINGS" + SuccessWithWarning = "SUCCESS WITH WARNINGS", + Waiting = "WAITING" } // @public (undocumented) @@ -707,6 +1194,15 @@ export class PackageJsonDependency { get version(): string; } +// @public (undocumented) +export class PackageJsonDependencyMeta { + constructor(name: string, injected: boolean, onChange: () => void); + // (undocumented) + get injected(): boolean; + // (undocumented) + readonly name: string; +} + // @public (undocumented) export class PackageJsonEditor { // @internal @@ -714,20 +1210,25 @@ export class PackageJsonEditor { // (undocumented) addOrUpdateDependency(packageName: string, newVersion: string, dependencyType: DependencyType): void; get dependencyList(): ReadonlyArray; + get dependencyMetaList(): ReadonlyArray; get devDependencyList(): ReadonlyArray; // (undocumented) readonly filePath: string; // (undocumented) static fromObject(object: IPackageJson, filename: string): PackageJsonEditor; - // (undocumented) + // @deprecated (undocumented) static load(filePath: string): PackageJsonEditor; // (undocumented) + static loadAsync(filePath: string): Promise; + // (undocumented) get name(): string; // (undocumented) removeDependency(packageName: string, dependencyType: DependencyType): void; get resolutionsList(): ReadonlyArray; - // (undocumented) + // @deprecated (undocumented) saveIfModified(): boolean; + // (undocumented) + saveIfModifiedAsync(): Promise; saveToObject(): IPackageJson; // (undocumented) tryGetDependency(packageName: string): PackageJsonDependency | undefined; @@ -756,20 +1257,29 @@ export abstract class PackageManagerOptionsConfigurationBase implements IPackage readonly environmentVariables?: IConfigurationEnvironment; } +// @beta +export type Parallelism = number | IParallelismScalar; + // @alpha export class PhasedCommandHooks { - readonly afterExecuteOperations: AsyncSeriesHook<[IExecutionResult, ICreateOperationsContext]>; - readonly beforeExecuteOperations: AsyncSeriesHook<[Map]>; - readonly beforeLog: SyncHook; - readonly createOperations: AsyncSeriesWaterfallHook<[Set, ICreateOperationsContext]>; - readonly onOperationStatusChanged: SyncHook<[IOperationExecutionResult]>; - readonly waitingForChanges: SyncHook; + readonly createOperationsAsync: AsyncSeriesWaterfallHook<[ + Set, + ICreateOperationsContext + ]>; + readonly onGraphCreatedAsync: AsyncSeriesHook<[IOperationGraph, IOperationGraphContext]>; } // @public export class PnpmOptionsConfiguration extends PackageManagerOptionsConfigurationBase { + readonly alwaysFullInstall: boolean | undefined; + readonly alwaysInjectDependenciesFromOtherSubspaces: boolean | undefined; + readonly autoInstallPeers: boolean | undefined; + readonly globalAllowBuilds: Record | undefined; readonly globalAllowedDeprecatedVersions: Record | undefined; + readonly globalCatalogs: Record> | undefined; + readonly globalIgnoredOptionalDependencies: string[] | undefined; readonly globalNeverBuiltDependencies: string[] | undefined; + readonly globalOnlyBuiltDependencies: string[] | undefined; readonly globalOverrides: Record | undefined; readonly globalPackageExtensions: Record | undefined; get globalPatchedDependencies(): Record | undefined; @@ -777,50 +1287,71 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration // (undocumented) readonly jsonFilename: string | undefined; // @internal (undocumented) - static loadFromJsonFileOrThrow(jsonFilename: string, commonTempFolder: string): PnpmOptionsConfiguration; + static loadFromJsonFileOrThrow(jsonFilePath: string, commonTempFolder: string): PnpmOptionsConfiguration; // @internal (undocumented) static loadFromJsonObject(json: _IPnpmOptionsJson, commonTempFolder: string): PnpmOptionsConfiguration; - readonly pnpmStore: PnpmStoreOptions; + // @deprecated (undocumented) + get minimumReleaseAge(): number | undefined; + readonly minimumReleaseAgeExclude: string[] | undefined; + readonly minimumReleaseAgeMinutes: number | undefined; + readonly pnpmLockfilePolicies: IPnpmLockfilePolicies | undefined; + readonly pnpmStore: PnpmStoreLocation; readonly pnpmStorePath: string; readonly preventManualShrinkwrapChanges: boolean; + readonly resolutionMode: PnpmResolutionMode | undefined; readonly strictPeerDependencies: boolean; + readonly trustPolicy: PnpmTrustPolicy | undefined; + readonly trustPolicyExclude: string[] | undefined; + readonly trustPolicyIgnoreAfterMinutes: number | undefined; readonly unsupportedPackageJsonSettings: unknown | undefined; + updateGlobalAllowBuilds(allowBuilds: Record | undefined): void; + updateGlobalCatalogsAsync(catalogs: Record> | undefined): Promise; + // @deprecated + updateGlobalOnlyBuiltDependencies(onlyBuiltDependencies: string[] | undefined): void; + updateGlobalOnlyBuiltDependenciesAsync(onlyBuiltDependencies: string[] | undefined): Promise; updateGlobalPatchedDependencies(patchedDependencies: Record | undefined): void; readonly useWorkspaces: boolean; } // @public -export type PnpmStoreOptions = 'local' | 'global'; +export type PnpmResolutionMode = 'highest' | 'time-based' | 'lowest-direct'; + +// @public +export type PnpmStoreLocation = 'local' | 'global'; + +// @public @deprecated (undocumented) +export type PnpmStoreOptions = PnpmStoreLocation; + +// @public +export type PnpmTrustPolicy = 'no-downgrade' | 'off'; // @beta (undocumented) export class ProjectChangeAnalyzer { constructor(rushConfiguration: RushConfiguration); - // Warning: (ae-forgotten-export) The symbol "IRawRepoState" needs to be exported by the entry point index.d.ts - // // @internal (undocumented) - _ensureInitializedAsync(terminal: ITerminal): Promise; - // (undocumented) _filterProjectDataAsync(project: RushConfigurationProject, unfilteredProjectData: Map, rootDir: string, terminal: ITerminal): Promise>; getChangedProjectsAsync(options: IGetChangedProjectsOptions): Promise>; + // (undocumented) + protected getChangesByProject(lookup: LookupByPath, changedFiles: Map): Map>; // @internal - _tryGetProjectDependenciesAsync(project: RushConfigurationProject, terminal: ITerminal): Promise | undefined>; - // @internal - _tryGetProjectStateHashAsync(project: RushConfigurationProject, terminal: ITerminal): Promise; + _tryGetSnapshotProviderAsync(projectConfigurations: ReadonlyMap, terminal: ITerminal, projectSelection?: ReadonlySet): Promise; } // @public export class RepoStateFile { readonly filePath: string; get isValid(): boolean; - static loadFromFile(jsonFilename: string, variant: string | undefined): RepoStateFile; + static loadFromFile(jsonFilename: string): RepoStateFile; + get packageJsonInjectedDependenciesHash(): string | undefined; + get pnpmCatalogsHash(): string | undefined; get pnpmShrinkwrapHash(): string | undefined; get preferredVersionsHash(): string | undefined; - refreshState(rushConfiguration: RushConfiguration): boolean; + refreshState(rushConfiguration: RushConfiguration, subspace: Subspace | undefined, variant?: string): boolean; } // @public export class Rush { - static launch(launcherVersion: string, arg: ILaunchOptions): void; + static launch(launcherVersion: string, options: ILaunchOptions): void; static launchRushPnpm(launcherVersion: string, options: ILaunchOptions): void; static launchRushX(launcherVersion: string, options: ILaunchOptions): void; // (undocumented) @@ -830,13 +1361,17 @@ export class Rush { static get version(): string; } +// @beta +export class RushCommandLine { + // (undocumented) + static getCliSpec(rushJsonFolder: string): IRushCommandLineSpec; +} + // @public export class RushConfiguration { readonly allowMostlyStandardPackageNames: boolean; readonly approvedPackagesPolicy: ApprovedPackagesPolicy; readonly changesFolder: string; - // @deprecated - get committedShrinkwrapFilename(): string; get commonAutoinstallersFolder(): string; readonly commonFolder: string; readonly commonRushConfigFolder: string; @@ -844,25 +1379,48 @@ export class RushConfiguration { readonly commonTempFolder: string; // @deprecated get commonVersions(): CommonVersionsConfiguration; - get currentInstalledVariant(): string | undefined; - readonly currentVariantJsonFilename: string; + readonly currentVariantJsonFilePath: string; + // Warning: (ae-forgotten-export) The symbol "ICurrentVariantJson" needs to be exported by the entry point index.d.ts + // + // @internal (undocumented) + _currentVariantJsonLoadingPromise: Promise | undefined; + // @beta + readonly customTipsConfiguration: CustomTipsConfiguration; + // @beta + readonly customTipsConfigurationFilePath: string; + // @beta (undocumented) + get defaultSubspace(): Subspace; + // @deprecated readonly ensureConsistentVersions: boolean; + // @internal + readonly _ensureConsistentVersionsJsonValue: boolean | undefined; // @beta readonly eventHooks: EventHooks; // @beta readonly experimentsConfiguration: ExperimentsConfiguration; findProjectByShorthandName(shorthandProjectName: string): RushConfigurationProject | undefined; findProjectByTempName(tempProjectName: string): RushConfigurationProject | undefined; - getCommittedShrinkwrapFilename(variant?: string | undefined): string; - getCommonVersions(variant?: string | undefined): CommonVersionsConfiguration; - getCommonVersionsFilePath(variant?: string | undefined): string; - getImplicitlyPreferredVersions(variant?: string | undefined): Map; - getPnpmfilePath(variant?: string | undefined): string; + // @deprecated (undocumented) + getCommittedShrinkwrapFilename(subspace?: Subspace, variant?: string): string; + // @deprecated (undocumented) + getCommonVersions(subspace?: Subspace, variant?: string): CommonVersionsConfiguration; + // @deprecated (undocumented) + getCommonVersionsFilePath(subspace?: Subspace, variant?: string): string; + getCurrentlyInstalledVariantAsync(): Promise; + getImplicitlyPreferredVersions(subspace?: Subspace, variant?: string): Map; + // @deprecated (undocumented) + getPnpmfilePath(subspace?: Subspace, variant?: string): string; getProjectByName(projectName: string): RushConfigurationProject | undefined; // @beta (undocumented) getProjectLookupForRoot(rootPath: string): LookupByPath; - getRepoState(variant?: string | undefined): RepoStateFile; - getRepoStateFilePath(variant?: string | undefined): string; + // @deprecated (undocumented) + getRepoState(subspace?: Subspace): RepoStateFile; + // @deprecated (undocumented) + getRepoStateFilePath(subspace?: Subspace): string; + // @beta (undocumented) + getSubspace(subspaceName: string): Subspace; + // @beta + getSubspacesForProjects(projects: Iterable): ReadonlySet; readonly gitAllowedEmailRegExps: string[]; readonly gitChangefilesCommitMessage: string | undefined; readonly gitChangeLogUpdateCommitMessage: string | undefined; @@ -870,6 +1428,7 @@ export class RushConfiguration { readonly gitTagSeparator: string | undefined; readonly gitVersionBumpCommitMessage: string | undefined; readonly hotfixChangeEnabled: boolean; + readonly isPnpm: boolean; static loadFromConfigurationFile(rushJsonFilename: string): RushConfiguration; // (undocumented) static loadFromDefaultLocation(options?: ITryFindRushJsonLocationOptions): RushConfiguration; @@ -888,8 +1447,8 @@ export class RushConfiguration { readonly projectFolderMinDepth: number; // (undocumented) get projects(): RushConfigurationProject[]; - // (undocumented) - get projectsByName(): Map; + // @beta (undocumented) + get projectsByName(): ReadonlyMap; // @beta get projectsByTag(): ReadonlyMap>; readonly repositoryDefaultBranch: string; @@ -909,15 +1468,26 @@ export class RushConfiguration { readonly _rushPluginsConfiguration: RushPluginsConfiguration; readonly shrinkwrapFilename: string; get shrinkwrapFilePhrase(): string; + // @beta + get subspaces(): readonly Subspace[]; + // @beta + readonly subspacesConfiguration: SubspacesConfiguration | undefined; + readonly subspacesFeatureEnabled: boolean; readonly suppressNodeLtsWarning: boolean; // @beta readonly telemetryEnabled: boolean; - readonly tempShrinkwrapFilename: string; - readonly tempShrinkwrapPreinstallFilename: string; + // @deprecated + get tempShrinkwrapFilename(): string; + // @deprecated + get tempShrinkwrapPreinstallFilename(): string; static tryFindRushJsonLocation(options?: ITryFindRushJsonLocationOptions): string | undefined; tryGetProjectForPath(currentFolderPath: string): RushConfigurationProject | undefined; + // @beta (undocumented) + tryGetSubspace(subspaceName: string): Subspace | undefined; // (undocumented) static tryLoadFromDefaultLocation(options?: ITryFindRushJsonLocationOptions): RushConfiguration | undefined; + // @beta + readonly variants: ReadonlySet; // @beta (undocumented) readonly versionPolicyConfiguration: VersionPolicyConfiguration; // @beta (undocumented) @@ -932,6 +1502,8 @@ export class RushConfigurationProject { // // @internal constructor(options: IRushConfigurationProjectOptions); + // @beta + readonly configuredSubspaceName: string | undefined; get consumingProjects(): ReadonlySet; // @deprecated get cyclicDependencyProjects(): Set; @@ -956,6 +1528,7 @@ export class RushConfigurationProject { readonly rushConfiguration: RushConfiguration; get shouldPublish(): boolean; readonly skipRushCheck: boolean; + readonly subspace: Subspace; // @beta readonly tags: ReadonlySet; readonly tempProjectName: string; @@ -968,55 +1541,73 @@ export class RushConfigurationProject { // @beta export class RushConstants { - static readonly artifactoryFilename: string; - static readonly browserApprovedPackagesFilename: string; - static readonly buildCacheFilename: string; - static readonly buildCacheVersion: number; - static readonly buildCommandName: string; + static readonly artifactoryFilename: 'artifactory.json'; + static readonly browserApprovedPackagesFilename: 'browser-approved-packages.json'; + static readonly buildCacheFilename: 'build-cache.json'; + static readonly buildCacheVersion: 1; + static readonly buildCommandName: 'build'; static readonly bulkCommandKind: 'bulk'; static readonly bypassPolicyFlagLongName: '--bypass-policy'; - static readonly changeFilesFolderName: string; - static readonly commandLineFilename: string; - static readonly commonFolderName: string; - static readonly commonVersionsFilename: string; - static readonly defaultMaxInstallAttempts: number; - static readonly defaultWatchDebounceMs: number; - static readonly experimentsFilename: string; + static readonly changeFilesFolderName: 'changes'; + static readonly cobuildFilename: 'cobuild.json'; + static readonly commandLineFilename: 'command-line.json'; + static readonly commonFolderName: 'common'; + static readonly commonVersionsFilename: 'common-versions.json'; + static readonly currentVariantsFilename: 'current-variants.json'; + static readonly customTipsFilename: 'custom-tips.json'; + static readonly defaultMaxInstallAttempts: 1; + static readonly defaultSubspaceName: 'default'; + static readonly defaultWatchDebounceMs: 1000; + static readonly experimentsFilename: 'experiments.json'; static readonly globalCommandKind: 'global'; - static readonly hashDelimiter: string; - static readonly nodeModulesFolderName: string; - static readonly nonbrowserApprovedPackagesFilename: string; - static readonly npmShrinkwrapFilename: string; + static readonly globalPluginCommandKind: 'globalPlugin'; + static readonly hashDelimiter: '|'; + static readonly lastLinkFlagFilename: 'last-link'; + static readonly mergeQueueIgnoreFileName: '.mergequeueignore'; + static readonly nodeModulesFolderName: 'node_modules'; + static readonly nonbrowserApprovedPackagesFilename: 'nonbrowser-approved-packages.json'; + static readonly npmShrinkwrapFilename: 'npm-shrinkwrap.json'; static readonly phasedCommandKind: 'phased'; static readonly phaseNamePrefix: '_phase:'; // @deprecated - static readonly pinnedVersionsFilename: string; - static readonly pnpmConfigFilename: string; - static readonly pnpmfileV1Filename: string; - static readonly pnpmfileV6Filename: string; - static readonly pnpmPatchesFolderName: string; - static readonly pnpmV3ShrinkwrapFilename: string; - static readonly projectRushFolderName: string; - static readonly projectShrinkwrapFilename: string; - static readonly rebuildCommandName: string; - static readonly repoStateFilename: string; - static readonly rushLogsFolderName: string; - static readonly rushPackageName: string; - static readonly rushPluginManifestFilename: string; - static readonly rushPluginsConfigFilename: string; - static readonly rushProjectConfigFilename: string; - static readonly rushRecyclerFolderName: string; - static readonly rushTempFolderName: string; - static readonly rushTempNpmScope: string; - static readonly rushTempProjectsFolderName: string; - static readonly rushUserConfigurationFolderName: string; - static readonly rushVariantsFolderName: string; - static readonly rushWebSiteUrl: string; - // (undocumented) - static readonly updateCloudCredentialsCommandName: string; - // (undocumented) - static readonly versionPoliciesFilename: string; - static readonly yarnShrinkwrapFilename: string; + static readonly pinnedVersionsFilename: 'pinned-versions.json'; + static readonly pnpmConfigFilename: 'pnpm-config.json'; + static readonly pnpmfileGlobalFilename: 'global-pnpmfile.cjs'; + static readonly pnpmfileV1Filename: 'pnpmfile.js'; + static readonly pnpmfileV6Filename: '.pnpmfile.cjs'; + static readonly pnpmModulesFilename: '.modules.yaml'; + static readonly pnpmPatchesCommonFolderName: `pnpm-patches`; + static readonly pnpmPatchesFolderName: 'patches'; + static readonly pnpmSyncFilename: '.pnpm-sync.json'; + static readonly pnpmV3ShrinkwrapFilename: 'pnpm-lock.yaml'; + static readonly pnpmVirtualStoreFolderName: '.pnpm'; + static readonly pnpmWorkspaceFileName: 'pnpm-workspace.yaml'; + static readonly projectImpactGraphFilename: 'project-impact-graph.yaml'; + static readonly projectRushFolderName: '.rush'; + static readonly projectShrinkwrapFilename: 'shrinkwrap-deps.json'; + static readonly rebuildCommandName: 'rebuild'; + static readonly repoStateFilename: 'repo-state.json'; + static readonly rushAlertsConfigFilename: 'rush-alerts.json'; + static readonly rushHotlinkStateFilename: 'rush-hotlink-state.json'; + static readonly rushJsonFilename: 'rush.json'; + static readonly rushLogsFolderName: 'rush-logs'; + static readonly rushPackageName: '@microsoft/rush'; + static readonly rushPluginManifestFilename: 'rush-plugin-manifest.json'; + static readonly rushPluginsConfigFilename: 'rush-plugins.json'; + static readonly rushProjectConfigFilename: 'rush-project.json'; + static readonly rushRecyclerFolderName: 'rush-recycler'; + static readonly rushTempFolderName: 'temp'; + static readonly rushTempNpmScope: '@rush-temp'; + static readonly rushTempProjectsFolderName: 'projects'; + static readonly rushUserConfigurationFolderName: '.rush-user'; + static readonly rushVariantsFolderName: 'variants'; + static readonly rushWebSiteUrl: 'https://rushjs.io'; + static readonly subspacesConfigFilename: 'subspaces.json'; + // (undocumented) + static readonly updateCloudCredentialsCommandName: 'update-cloud-credentials'; + // (undocumented) + static readonly versionPoliciesFilename: 'version-policies.json'; + static readonly yarnShrinkwrapFilename: 'yarn.lock'; } // @internal @@ -1033,13 +1624,43 @@ export class _RushInternals { // @beta export class RushLifecycleHooks { - beforeInstall: AsyncSeriesHook; - flushTelemetry: AsyncParallelHook<[ReadonlyArray]>; - initialize: AsyncSeriesHook; - runAnyGlobalCustomCommand: AsyncSeriesHook; - runAnyPhasedCommand: AsyncSeriesHook; - runGlobalCustomCommand: HookMap>; - runPhasedCommand: HookMap>; + readonly afterInstall: AsyncSeriesHook<[ + command: IRushCommand, + subspace: Subspace, + variant: string | undefined + ]>; + readonly beforeInstall: AsyncSeriesHook<[ + command: IRushCommand, + subspace: Subspace, + variant: string | undefined + ]>; + readonly flushTelemetry: AsyncParallelHook<[ReadonlyArray]>; + readonly initialize: AsyncSeriesHook; + readonly runAnyGlobalCustomCommand: AsyncSeriesHook; + readonly runAnyPhasedCommand: AsyncSeriesHook; + readonly runGlobalCustomCommand: HookMap>; + readonly runPhasedCommand: HookMap>; +} + +// @alpha +export class RushProjectConfiguration { + readonly disableBuildCacheForProject: boolean; + getCacheDisabledReason(trackedFileNames: Iterable, phaseName: string, isNoOp: boolean): string | undefined; + static getCacheDisabledReasonForProject(options: { + projectConfiguration: RushProjectConfiguration | undefined; + trackedFileNames: Iterable; + phaseName: string; + isNoOp: boolean; + }): string | undefined; + readonly incrementalBuildIgnoredGlobs: ReadonlyArray; + // (undocumented) + readonly operationSettingsByOperationName: ReadonlyMap>; + // (undocumented) + readonly project: RushConfigurationProject; + static tryLoadForProjectAsync(project: RushConfigurationProject, terminal: ITerminal): Promise; + static tryLoadForProjectsAsync(projects: Iterable, terminal: ITerminal): Promise>; + static tryLoadIgnoreGlobsForProjectAsync(project: RushConfigurationProject, terminal: ITerminal): Promise | undefined>; + validatePhaseConfiguration(phases: Iterable, terminal: ITerminal): void; } // @beta (undocumented) @@ -1048,12 +1669,16 @@ export class RushSession { // (undocumented) getCloudBuildCacheProviderFactory(cacheProviderName: string): CloudBuildCacheProviderFactory | undefined; // (undocumented) + getCobuildLockProviderFactory(cobuildLockProviderName: string): CobuildLockProviderFactory | undefined; + // (undocumented) getLogger(name: string): ILogger; // (undocumented) readonly hooks: RushLifecycleHooks; // (undocumented) registerCloudBuildCacheProviderFactory(cacheProviderName: string, factory: CloudBuildCacheProviderFactory): void; // (undocumented) + registerCobuildLockProviderFactory(cobuildLockProviderName: string, factory: CobuildLockProviderFactory): void; + // (undocumented) get terminalProvider(): ITerminalProvider; } @@ -1066,25 +1691,94 @@ export class RushUserConfiguration { static initializeAsync(): Promise; } +// @public +export class Subspace { + // Warning: (ae-forgotten-export) The symbol "ISubspaceOptions" needs to be exported by the entry point index.d.ts + constructor(options: ISubspaceOptions); + // @internal (undocumented) + _addProject(project: RushConfigurationProject): void; + // @beta + contains(project: RushConfigurationProject): boolean; + // @deprecated (undocumented) + getCommittedShrinkwrapFilename(): string; + // @beta + getCommittedShrinkwrapFilePath(variant?: string): string; + // @beta + getCommonVersions(variant?: string): CommonVersionsConfiguration; + // @beta + getCommonVersionsFilePath(variant?: string): string; + // @beta + getPackageJsonInjectedDependenciesHash(variant?: string): string | undefined; + getPnpmCatalogsHash(): string | undefined; + // @beta + getPnpmConfigFilePath(): string; + // @beta + getPnpmfilePath(variant?: string): string; + // @beta + getPnpmOptions(): PnpmOptionsConfiguration | undefined; + // @beta + getProjects(): RushConfigurationProject[]; + // @beta + getRepoState(): RepoStateFile; + // @beta + getRepoStateFilePath(): string; + // @beta + getSubspaceConfigFolderPath(): string; + // @beta + getSubspacePnpmPatchesFolderPath(): string; + // @beta + getSubspaceTempFolderPath(): string; + // @beta + getTempShrinkwrapFilename(): string; + // @deprecated (undocumented) + getTempShrinkwrapPreinstallFilename(subspaceName?: string | undefined): string; + // @beta + getTempShrinkwrapPreinstallFilePath(): string; + // @beta + getVariantDependentSubspaceConfigFolderPath(variant: string | undefined): string; + // @beta + shouldEnsureConsistentVersions(variant?: string): boolean; + // (undocumented) + readonly subspaceName: string; +} + +// @beta +export class SubspacesConfiguration { + static explainIfInvalidSubspaceName(subspaceName: string, splitWorkspaceCompatibility?: boolean): string | undefined; + readonly preventSelectingAllSubspaces: boolean; + static requireValidSubspaceName(subspaceName: string, splitWorkspaceCompatibility?: boolean): void; + readonly splitWorkspaceCompatibility: boolean; + readonly subspaceJsonFilePath: string; + readonly subspaceNames: ReadonlySet; + // (undocumented) + readonly subspacesEnabled: boolean; + // (undocumented) + static tryLoadFromConfigurationFile(subspaceJsonFilePath: string): SubspacesConfiguration | undefined; + // (undocumented) + static tryLoadFromDefaultLocation(rushConfiguration: RushConfiguration): SubspacesConfiguration | undefined; +} + // @public export abstract class VersionPolicy { - // Warning: (ae-forgotten-export) The symbol "IVersionPolicyJson" needs to be exported by the entry point index.d.ts - // // @internal constructor(versionPolicyJson: IVersionPolicyJson); abstract bump(bumpType?: BumpType, identifier?: string): void; - readonly definitionName: VersionPolicyDefinitionName; + get definitionName(): VersionPolicyDefinitionName; abstract ensure(project: IPackageJson, force?: boolean): IPackageJson | undefined; - readonly exemptFromRushChange: boolean; - readonly includeEmailInChangeFile: boolean; + get exemptFromRushChange(): boolean; + get includeEmailInChangeFile(): boolean; get isLockstepped(): boolean; // @internal - abstract get _json(): IVersionPolicyJson; + readonly _json: IVersionPolicyJson; // @internal static load(versionPolicyJson: IVersionPolicyJson): VersionPolicy | undefined; - readonly policyName: string; + get policyName(): string; + // @deprecated (undocumented) setDependenciesBeforeCommit(packageName: string, configuration: RushConfiguration): void; + setDependenciesBeforeCommitAsync(packageName: string, configuration: RushConfiguration): Promise; + // @deprecated (undocumented) setDependenciesBeforePublish(packageName: string, configuration: RushConfiguration): void; + setDependenciesBeforePublishAsync(packageName: string, configuration: RushConfiguration): Promise; abstract validate(versionString: string, packageName: string): void; } @@ -1095,7 +1789,7 @@ export class VersionPolicyConfiguration { bump(versionPolicyName?: string, bumpType?: BumpType, identifier?: string, shouldCommit?: boolean): void; getVersionPolicy(policyName: string): VersionPolicy; update(versionPolicyName: string, newVersion: string, shouldCommit?: boolean): void; - validate(projectsByName: Map): void; + validate(projectsByName: ReadonlyMap): void; readonly versionPolicies: Map; } diff --git a/common/reviews/api/rush-pnpm-kit-v10.api.md b/common/reviews/api/rush-pnpm-kit-v10.api.md new file mode 100644 index 00000000000..8a6d0f4c60e --- /dev/null +++ b/common/reviews/api/rush-pnpm-kit-v10.api.md @@ -0,0 +1,39 @@ +## API Report File for "@rushstack/rush-pnpm-kit-v10" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { DependencyPath } from '@pnpm/dependency-path-pnpm-v10'; +import { depPathToFilename } from '@pnpm/dependency-path-pnpm-v10'; +import { indexOfPeersSuffix } from '@pnpm/dependency-path-pnpm-v10'; +import type { LogBase } from '@pnpm/logger'; +import { parse } from '@pnpm/dependency-path-pnpm-v10'; +import { readWantedLockfile } from '@pnpm/lockfile.fs-pnpm-lock-v9'; +import { removeSuffix } from '@pnpm/dependency-path-pnpm-v10'; + +declare namespace dependencyPath { + export { + depPathToFilename, + indexOfPeersSuffix, + parse, + removeSuffix, + DependencyPath + } +} + +declare namespace lockfileFs { + export { + readWantedLockfile + } +} + +declare namespace logger { + export { + LogBase + } +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/common/reviews/api/rush-pnpm-kit-v8.api.md b/common/reviews/api/rush-pnpm-kit-v8.api.md new file mode 100644 index 00000000000..e6d5e5bdd27 --- /dev/null +++ b/common/reviews/api/rush-pnpm-kit-v8.api.md @@ -0,0 +1,35 @@ +## API Report File for "@rushstack/rush-pnpm-kit-v8" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { depPathToFilename } from '@pnpm/dependency-path-pnpm-v8'; +import { indexOfPeersSuffix } from '@pnpm/dependency-path-pnpm-v8'; +import type { LogBase } from '@pnpm/logger'; +import { parse } from '@pnpm/dependency-path-pnpm-v8'; +import { readWantedLockfile } from '@pnpm/lockfile-file-pnpm-lock-v6'; + +declare namespace dependencyPath { + export { + depPathToFilename, + indexOfPeersSuffix, + parse + } +} + +declare namespace lockfileFs { + export { + readWantedLockfile + } +} + +declare namespace logger { + export { + LogBase + } +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/common/reviews/api/rush-pnpm-kit-v9.api.md b/common/reviews/api/rush-pnpm-kit-v9.api.md new file mode 100644 index 00000000000..c94a618c571 --- /dev/null +++ b/common/reviews/api/rush-pnpm-kit-v9.api.md @@ -0,0 +1,39 @@ +## API Report File for "@rushstack/rush-pnpm-kit-v9" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { DependencyPath } from '@pnpm/dependency-path-pnpm-v9'; +import { depPathToFilename } from '@pnpm/dependency-path-pnpm-v9'; +import { indexOfPeersSuffix } from '@pnpm/dependency-path-pnpm-v9'; +import type { LogBase } from '@pnpm/logger'; +import { parse } from '@pnpm/dependency-path-pnpm-v9'; +import { readWantedLockfile } from '@pnpm/lockfile.fs-pnpm-lock-v9'; +import { removeSuffix } from '@pnpm/dependency-path-pnpm-v9'; + +declare namespace dependencyPath { + export { + depPathToFilename, + indexOfPeersSuffix, + parse, + removeSuffix, + DependencyPath + } +} + +declare namespace lockfileFs { + export { + readWantedLockfile + } +} + +declare namespace logger { + export { + LogBase + } +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/common/reviews/api/rush-redis-cobuild-plugin.api.md b/common/reviews/api/rush-redis-cobuild-plugin.api.md new file mode 100644 index 00000000000..a28011c7be2 --- /dev/null +++ b/common/reviews/api/rush-redis-cobuild-plugin.api.md @@ -0,0 +1,57 @@ +## API Report File for "@rushstack/rush-redis-cobuild-plugin" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +/// + +import type { ICobuildCompletedState } from '@rushstack/rush-sdk'; +import type { ICobuildContext } from '@rushstack/rush-sdk'; +import type { ICobuildLockProvider } from '@rushstack/rush-sdk'; +import type { IRushPlugin } from '@rushstack/rush-sdk'; +import type { RedisClientOptions } from '@redis/client'; +import type { RushConfiguration } from '@rushstack/rush-sdk'; +import type { RushSession } from '@rushstack/rush-sdk'; + +// @beta +export interface IRedisCobuildLockProviderOptions extends RedisClientOptions { + passwordEnvironmentVariable?: string; +} + +// Warning: (ae-incompatible-release-tags) The symbol "IRushRedisCobuildPluginOptions" is marked as @public, but its signature references "IRedisCobuildLockProviderOptions" which is marked as @beta +// +// @public (undocumented) +export type IRushRedisCobuildPluginOptions = IRedisCobuildLockProviderOptions; + +// @beta (undocumented) +export class RedisCobuildLockProvider implements ICobuildLockProvider { + constructor(options: IRedisCobuildLockProviderOptions, rushSession: RushSession); + acquireLockAsync(context: ICobuildContext): Promise; + // (undocumented) + connectAsync(): Promise; + // (undocumented) + disconnectAsync(): Promise; + // (undocumented) + static expandOptionsWithEnvironmentVariables(options: IRedisCobuildLockProviderOptions, environment?: NodeJS.ProcessEnv): IRedisCobuildLockProviderOptions; + // (undocumented) + getCompletedStateAsync(context: ICobuildContext): Promise; + // (undocumented) + renewLockAsync(context: ICobuildContext): Promise; + // (undocumented) + setCompletedStateAsync(context: ICobuildContext, state: ICobuildCompletedState): Promise; +} + +// @public (undocumented) +class RushRedisCobuildPlugin implements IRushPlugin { + constructor(options: IRushRedisCobuildPluginOptions); + // (undocumented) + apply(rushSession: RushSession, rushConfiguration: RushConfiguration): void; + // (undocumented) + pluginName: string; +} +export default RushRedisCobuildPlugin; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/common/reviews/api/rush-resolver-cache-plugin.api.md b/common/reviews/api/rush-resolver-cache-plugin.api.md new file mode 100644 index 00000000000..2ea472f1340 --- /dev/null +++ b/common/reviews/api/rush-resolver-cache-plugin.api.md @@ -0,0 +1,22 @@ +## API Report File for "@rushstack/rush-resolver-cache-plugin" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { IRushPlugin } from '@rushstack/rush-sdk'; +import type { RushConfiguration } from '@rushstack/rush-sdk'; +import type { RushSession } from '@rushstack/rush-sdk'; + +// @beta +class RushResolverCachePlugin implements IRushPlugin { + // (undocumented) + apply(rushSession: RushSession, rushConfiguration: RushConfiguration): void; + // (undocumented) + readonly pluginName: 'RushResolverCachePlugin'; +} +export default RushResolverCachePlugin; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/common/reviews/api/rush-sdk.api.md b/common/reviews/api/rush-sdk.api.md new file mode 100644 index 00000000000..8816b7c1edd --- /dev/null +++ b/common/reviews/api/rush-sdk.api.md @@ -0,0 +1,39 @@ +## API Report File for "@rushstack/rush-sdk" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +/// + +// @public +export interface ILoadSdkAsyncOptions { + abortSignal?: AbortSignal; + onNotifyEvent?: SdkNotifyEventCallback; + rushJsonSearchFolder?: string; +} + +// @public +export interface IProgressBarCallbackLogMessage { + kind: 'info' | 'debug'; + text: string; +} + +// @public +export interface ISdkCallbackEvent { + logMessage: IProgressBarCallbackLogMessage | undefined; + progressPercent: number | undefined; +} + +// @public +export class RushSdkLoader { + static get isLoaded(): boolean; + static loadAsync(options?: ILoadSdkAsyncOptions): Promise; +} + +// @public +export type SdkNotifyEventCallback = (sdkEvent: ISdkCallbackEvent) => void; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/common/reviews/api/rush-themed-ui.api.md b/common/reviews/api/rush-themed-ui.api.md index 69176682dd2..ed51c785cff 100644 --- a/common/reviews/api/rush-themed-ui.api.md +++ b/common/reviews/api/rush-themed-ui.api.md @@ -7,15 +7,15 @@ import { default as React_2 } from 'react'; // @public -export const Button: ({ children, disabled, onClick }: IButtonProps) => JSX.Element; +export const Button: (input: IButtonProps) => React_2.ReactElement; // @public -export const Checkbox: ({ label, isChecked, onChecked }: ICheckboxProps) => JSX.Element; +export const Checkbox: (input: ICheckboxProps) => React_2.ReactElement; // @public export interface IButtonProps { // (undocumented) - children: JSX.Element | string; + children: React_2.ReactElement | string; // (undocumented) disabled?: boolean; // (undocumented) @@ -45,7 +45,7 @@ export interface IInputProps { } // @public -export const Input: ({ value, placeholder, onChange, type }: IInputProps) => JSX.Element; +export const Input: (input: IInputProps) => React_2.ReactElement; // @public export interface IScrollAreaProps { @@ -72,7 +72,7 @@ export interface ITabsProps { // (undocumented) onChange?: (value: any) => void; // (undocumented) - renderChildren?: () => JSX.Element; + renderChildren?: () => React_2.ReactElement; // (undocumented) value: string; } @@ -92,13 +92,13 @@ export interface ITextProps { } // @public -export const ScrollArea: ({ children }: IScrollAreaProps) => JSX.Element; +export const ScrollArea: (input: IScrollAreaProps) => React_2.ReactElement; // @public -export const Tabs: ({ items, def, value, onChange, renderChildren }: ITabsProps) => JSX.Element; +export const Tabs: (input: ITabsProps) => React_2.ReactElement; // @public -const Text_2: ({ type, bold, children, className, size }: ITextProps) => JSX.Element; +const Text_2: (input: ITextProps) => React_2.ReactElement; export { Text_2 as Text } // @public diff --git a/common/reviews/api/set-webpack-public-path-plugin.api.md b/common/reviews/api/set-webpack-public-path-plugin.api.md index 95725f8b24c..a56b1fd1fd8 100644 --- a/common/reviews/api/set-webpack-public-path-plugin.api.md +++ b/common/reviews/api/set-webpack-public-path-plugin.api.md @@ -4,41 +4,59 @@ ```ts -import type * as Webpack from 'webpack'; +import type webpack from 'webpack'; -// @public -export function getGlobalRegisterCode(debug?: boolean): string; +// @public (undocumented) +export interface IScriptNameAssetNameOptions { + useAssetName: true; +} + +// @public (undocumented) +export type IScriptNameOptions = IScriptNameAssetNameOptions | IScriptNameRegexOptions; + +// @public (undocumented) +export interface IScriptNameRegexOptions { + isTokenized?: boolean; + name: string; +} // @public export interface ISetWebpackPublicPathOptions { getPostProcessScript?: (varName: string) => string; preferLastFoundScript?: boolean; - publicPath?: string; regexVariable?: string; - skipDetection?: boolean; - systemJs?: boolean; - urlPrefix?: string; } // @public export interface ISetWebpackPublicPathPluginOptions extends ISetWebpackPublicPathOptions { - scriptName?: { - useAssetName?: boolean; - name?: string; - isTokenized?: boolean; - }; + scriptName: IScriptNameOptions; } -// @public (undocumented) -export const registryVariableName: string; +// @public +export class SetPublicPathCurrentScriptPlugin extends SetPublicPathPluginBase { + constructor(); + // (undocumented) + protected _applyCompilation(thisWebpack: typeof webpack, compilation: webpack.Compilation): void; +} // @public -export class SetPublicPathPlugin implements Webpack.Plugin { +export class SetPublicPathPlugin extends SetPublicPathPluginBase { constructor(options: ISetWebpackPublicPathPluginOptions); // (undocumented) - apply(compiler: Webpack.Compiler): void; + protected _applyCompilation(thisWebpack: typeof webpack, compilation: webpack.Compilation): void; // (undocumented) - options: ISetWebpackPublicPathPluginOptions; + readonly options: ISetWebpackPublicPathPluginOptions; } +// @public (undocumented) +export abstract class SetPublicPathPluginBase implements webpack.WebpackPluginInstance { + constructor(pluginName: string); + // (undocumented) + apply(compiler: webpack.Compiler): void; + // (undocumented) + protected abstract _applyCompilation(thisWebpack: typeof webpack, compilation: webpack.Compilation): void; +} + +// (No @packageDocumentation comment for this package) + ``` diff --git a/common/reviews/api/stream-collator.api.md b/common/reviews/api/stream-collator.api.md index a8cdd5e5866..6c209cdd5b4 100644 --- a/common/reviews/api/stream-collator.api.md +++ b/common/reviews/api/stream-collator.api.md @@ -25,9 +25,7 @@ export class CollatedWriter extends TerminalWritable { // @internal (undocumented) _flushBufferedChunks(): void; get isActive(): boolean; - // (undocumented) onClose(): void; - // (undocumented) onWriteChunk(chunk: ITerminalChunk): void; // (undocumented) readonly taskName: string; diff --git a/common/reviews/api/terminal.api.md b/common/reviews/api/terminal.api.md index 31a3d8f656b..7dd9d256c60 100644 --- a/common/reviews/api/terminal.api.md +++ b/common/reviews/api/terminal.api.md @@ -4,9 +4,23 @@ ```ts -import { Brand } from '@rushstack/node-core-library'; -import { ITerminal } from '@rushstack/node-core-library'; +/// + +import type { Brand } from '@rushstack/node-core-library'; +import type { IProblem } from '@rushstack/problem-matcher'; +import type { IProblemMatcher } from '@rushstack/problem-matcher'; +import type { IProblemMatcherJson } from '@rushstack/problem-matcher'; import { NewlineKind } from '@rushstack/node-core-library'; +import { Writable } from 'node:stream'; +import { WritableOptions } from 'node:stream'; + +// @public +export class AnsiEscape { + static formatForTests(text: string, options?: IAnsiEscapeConvertForTestsOptions): string; + // (undocumented) + static getEscapeSequenceForAnsiCode(code: number): string; + static removeCodes(text: string): string; +} // @public export class CallbackWritable extends TerminalWritable { @@ -15,6 +29,72 @@ export class CallbackWritable extends TerminalWritable { protected onWriteChunk(chunk: ITerminalChunk): void; } +// @public +export class Colorize { + // (undocumented) + static black(text: string): string; + // (undocumented) + static blackBackground(text: string): string; + // (undocumented) + static blink(text: string): string; + // (undocumented) + static blue(text: string): string; + // (undocumented) + static blueBackground(text: string): string; + // (undocumented) + static bold(text: string): string; + // (undocumented) + static cyan(text: string): string; + // (undocumented) + static cyanBackground(text: string): string; + // (undocumented) + static dim(text: string): string; + // (undocumented) + static gray(text: string): string; + // (undocumented) + static grayBackground(text: string): string; + // (undocumented) + static green(text: string): string; + // (undocumented) + static greenBackground(text: string): string; + // (undocumented) + static hidden(text: string): string; + // (undocumented) + static invertColor(text: string): string; + // (undocumented) + static magenta(text: string): string; + // (undocumented) + static magentaBackground(text: string): string; + // (undocumented) + static rainbow(text: string): string; + // (undocumented) + static red(text: string): string; + // (undocumented) + static redBackground(text: string): string; + // (undocumented) + static underline(text: string): string; + // (undocumented) + static white(text: string): string; + // (undocumented) + static whiteBackground(text: string): string; + // (undocumented) + static yellow(text: string): string; + // (undocumented) + static yellowBackground(text: string): string; +} + +// @beta +export class ConsoleTerminalProvider implements ITerminalProvider { + constructor(options?: Partial); + debugEnabled: boolean; + get eolCharacter(): string; + // (undocumented) + static readonly supportsColor: boolean; + readonly supportsColor: boolean; + verboseEnabled: boolean; + write(data: string, severity: TerminalProviderSeverity): void; +} + // @public export const DEFAULT_CONSOLE_WIDTH: number; @@ -25,25 +105,95 @@ export class DiscardStdoutTransform extends TerminalTransform { protected onWriteChunk(chunk: ITerminalChunk): void; } +// @beta (undocumented) +export interface IAllStringBufferOutput { + // (undocumented) + debug: string; + // (undocumented) + error: string; + // (undocumented) + log: string; + // (undocumented) + verbose: string; + // (undocumented) + warning: string; +} + +// @public +export interface IAnsiEscapeConvertForTestsOptions { + encodeNewlines?: boolean; +} + // @public export interface ICallbackWritableOptions { // (undocumented) onWriteChunk: (chunk: ITerminalChunk) => void; } +// @beta +export interface IConsoleTerminalProviderOptions { + debugEnabled: boolean; + verboseEnabled: boolean; +} + // @beta export interface IDiscardStdoutTransformOptions extends ITerminalTransformOptions { } +// @beta +export interface IDynamicPrefixProxyTerminalProviderOptions extends IPrefixProxyTerminalProviderOptionsBase { + getPrefix: () => string; +} + // @public export interface INormalizeNewlinesTextRewriterOptions { ensureNewlineAtEnd?: boolean; newlineKind: NewlineKind; } +// @beta (undocumented) +export interface IOutputChunk { + // (undocumented) + severity: TerminalProviderSeverityName; + // (undocumented) + text: string; +} + +// @beta (undocumented) +export type IPrefixProxyTerminalProviderOptions = IStaticPrefixProxyTerminalProviderOptions | IDynamicPrefixProxyTerminalProviderOptions; + +// @beta (undocumented) +export interface IPrefixProxyTerminalProviderOptionsBase { + terminalProvider: ITerminalProvider; +} + +// @public +export interface IPrintMessageInBoxOptions { + borderColor?: (text: string) => string; + boxWidth?: number; + messageColor?: (text: string) => string; +} + +// @beta +export interface IProblemCollector { + get problems(): ReadonlySet; +} + +// @beta +export interface IProblemCollectorOptions extends ITerminalWritableOptions { + matcherJson?: IProblemMatcherJson[]; + matchers?: IProblemMatcher[]; + onProblem?: (problem: IProblem) => void; +} + // @public export interface ISplitterTransformOptions extends ITerminalWritableOptions { - destinations: TerminalWritable[]; + destinations: Iterable; +} + +// @beta +export interface IStaticPrefixProxyTerminalProviderOptions extends IPrefixProxyTerminalProviderOptionsBase { + prefix: string; } // @beta @@ -52,17 +202,86 @@ export interface IStdioLineTransformOptions extends ITerminalTransformOptions { } // @beta -export interface IStdioSummarizerOptions { +export interface IStdioSummarizerOptions extends ITerminalWritableOptions { leadingLines?: number; trailingLines?: number; } +// @beta (undocumented) +export interface IStringBufferOutputChunksOptions extends IStringBufferOutputOptions { + asLines?: boolean; +} + +// @beta (undocumented) +export interface IStringBufferOutputOptions { + normalizeSpecialCharacters?: boolean; +} + +// @beta (undocumented) +export interface ITerminal { + registerProvider(provider: ITerminalProvider): void; + unregisterProvider(provider: ITerminalProvider): void; + write(...messageParts: TerminalWriteParameters): void; + writeDebug(...messageParts: TerminalWriteParameters): void; + writeDebugLine(...messageParts: TerminalWriteParameters): void; + writeError(...messageParts: TerminalWriteParameters): void; + writeErrorLine(...messageParts: TerminalWriteParameters): void; + writeLine(...messageParts: TerminalWriteParameters): void; + writeVerbose(...messageParts: TerminalWriteParameters): void; + writeVerboseLine(...messageParts: TerminalWriteParameters): void; + writeWarning(...messageParts: TerminalWriteParameters): void; + writeWarningLine(...messageParts: TerminalWriteParameters): void; +} + // @public export interface ITerminalChunk { kind: TerminalChunkKind; text: string; } +// @beta +export interface ITerminalProvider { + eolCharacter: string; + supportsColor: boolean; + write(data: string, severity: TerminalProviderSeverity): void; +} + +// @beta +export interface ITerminalStreamWritableOptions { + severity: TerminalProviderSeverity; + terminal: ITerminal; + writableOptions?: WritableOptions; +} + +// @public +export interface ITerminalTableChars { + bottom: string; + bottomCenter: string; + bottomLeft: string; + bottomRight: string; + centerCenter: string; + horizontalCenter: string; + left: string; + leftCenter: string; + right: string; + rightCenter: string; + top: string; + topCenter: string; + topLeft: string; + topRight: string; + verticalCenter: string; +} + +// @public +export interface ITerminalTableOptions { + borderCharacters?: Partial; + borderColor?: (text: string) => string; + borderless?: boolean; + colWidths?: number[]; + head?: string[]; + headingColor?: (text: string) => string; +} + // @public export interface ITerminalTransformOptions extends ITerminalWritableOptions { destination: TerminalWritable; @@ -74,6 +293,11 @@ export interface ITerminalWritableOptions { preventAutoclose?: boolean; } +// @beta (undocumented) +export interface ITerminalWriteOptions { + doNotOverrideSgrCodes?: boolean; +} + // @public export interface ITextRewriterTransformOptions extends ITerminalTransformOptions { ensureNewlineAtEnd?: boolean; @@ -96,6 +320,13 @@ export class MockWritable extends TerminalWritable { reset(): void; } +// @beta +export class NoOpTerminalProvider implements ITerminalProvider { + get eolCharacter(): string; + get supportsColor(): boolean; + write(data: string, severity: TerminalProviderSeverity): void; +} + // @public export class NormalizeNewlinesTextRewriter extends TextRewriter { constructor(options: INormalizeNewlinesTextRewriterOptions); @@ -110,11 +341,40 @@ export class NormalizeNewlinesTextRewriter extends TextRewriter { process(unknownState: TextRewriterState, text: string): string; } +// @beta +export class PrefixProxyTerminalProvider implements ITerminalProvider { + constructor(options: IPrefixProxyTerminalProviderOptions); + // (undocumented) + get eolCharacter(): string; + // (undocumented) + get supportsColor(): boolean; + // (undocumented) + write(data: string, severity: TerminalProviderSeverity): void; +} + // @public export class PrintUtilities { static getConsoleWidth(): number | undefined; + // Warning: (ae-incompatible-release-tags) The symbol "printMessageInBox" is marked as @public, but its signature references "ITerminal" which is marked as @beta + static printMessageInBox(message: string, terminal: ITerminal, options?: IPrintMessageInBoxOptions): void; + // Warning: (ae-incompatible-release-tags) The symbol "printMessageInBox" is marked as @public, but its signature references "ITerminal" which is marked as @beta + // + // @deprecated (undocumented) static printMessageInBox(message: string, terminal: ITerminal, boxWidth?: number): void; static wrapWords(text: string, maxLineLength?: number, indent?: number): string; + static wrapWords(text: string, maxLineLength?: number, linePrefix?: string): string; + static wrapWords(text: string, maxLineLength?: number, indentOrLinePrefix?: number | string): string; + static wrapWordsToLines(text: string, maxLineLength?: number, indent?: number): string[]; + static wrapWordsToLines(text: string, maxLineLength?: number, linePrefix?: string): string[]; + static wrapWordsToLines(text: string, maxLineLength?: number, indentOrLinePrefix?: number | string): string[]; +} + +// @beta +export class ProblemCollector extends TerminalWritable implements IProblemCollector { + constructor(options: IProblemCollectorOptions); + protected onClose(): void; + protected onWriteChunk(chunk: ITerminalChunk): void; + get problems(): ReadonlySet; } // @public @@ -130,12 +390,14 @@ export class RemoveColorsTextRewriter extends TextRewriter { // @public export class SplitterTransform extends TerminalWritable { constructor(options: ISplitterTransformOptions); + addDestination(destination: TerminalWritable): void; // (undocumented) - readonly destinations: ReadonlyArray; + get destinations(): ReadonlySet; // (undocumented) protected onClose(): void; // (undocumented) protected onWriteChunk(chunk: ITerminalChunk): void; + removeDestination(destination: TerminalWritable, close?: boolean): boolean; } // @beta @@ -165,19 +427,96 @@ export class StdioWritable extends TerminalWritable { protected onWriteChunk(chunk: ITerminalChunk): void; } +// @beta +export class StringBufferTerminalProvider implements ITerminalProvider { + constructor(supportsColor?: boolean); + get eolCharacter(): string; + getAllOutput(sparse?: false, options?: IStringBufferOutputOptions): IAllStringBufferOutput; + // (undocumented) + getAllOutput(sparse: true, options?: IStringBufferOutputOptions): Partial; + getAllOutputAsChunks(options?: IStringBufferOutputChunksOptions & { + asLines?: false; + }): IOutputChunk[]; + // (undocumented) + getAllOutputAsChunks(options: IStringBufferOutputChunksOptions & { + asLines: true; + }): `[${string}] ${string}`[]; + getDebugOutput(options?: IStringBufferOutputOptions): string; + getErrorOutput(options?: IStringBufferOutputOptions): string; + getOutput(options?: IStringBufferOutputOptions): string; + // @deprecated (undocumented) + getVerbose(options?: IStringBufferOutputOptions): string; + getVerboseOutput(options?: IStringBufferOutputOptions): string; + getWarningOutput(options?: IStringBufferOutputOptions): string; + readonly supportsColor: boolean; + write(text: string, severity: TerminalProviderSeverity): void; +} + +// @beta +export class Terminal implements ITerminal { + constructor(provider: ITerminalProvider); + registerProvider(provider: ITerminalProvider): void; + unregisterProvider(provider: ITerminalProvider): void; + write(...messageParts: TerminalWriteParameters): void; + writeDebug(...messageParts: TerminalWriteParameters): void; + writeDebugLine(...messageParts: TerminalWriteParameters): void; + writeError(...messageParts: TerminalWriteParameters): void; + writeErrorLine(...messageParts: TerminalWriteParameters): void; + writeLine(...messageParts: TerminalWriteParameters): void; + writeVerbose(...messageParts: TerminalWriteParameters): void; + writeVerboseLine(...messageParts: TerminalWriteParameters): void; + writeWarning(...messageParts: TerminalWriteParameters): void; + writeWarningLine(...messageParts: TerminalWriteParameters): void; +} + // @public -export const enum TerminalChunkKind { +export enum TerminalChunkKind { Stderr = "E", Stdout = "O" } +// @beta +export enum TerminalProviderSeverity { + // (undocumented) + debug = 4, + // (undocumented) + error = 2, + // (undocumented) + log = 0, + // (undocumented) + verbose = 3, + // (undocumented) + warning = 1 +} + +// @beta (undocumented) +export type TerminalProviderSeverityName = keyof typeof TerminalProviderSeverity; + +// @beta +export class TerminalStreamWritable extends Writable { + constructor(options: ITerminalStreamWritableOptions); + // (undocumented) + _write(chunk: string | Buffer | Uint8Array, encoding: string, callback: (error?: Error | null) => void): void; +} + +// @public +export class TerminalTable { + constructor(options?: ITerminalTableOptions); + // (undocumented) + getLines(): string[]; + // Warning: (ae-incompatible-release-tags) The symbol "printToTerminal" is marked as @public, but its signature references "ITerminal" which is marked as @beta + printToTerminal(terminal: ITerminal): void; + push(...rows: string[][]): void; + toString(): string; +} + // @public export abstract class TerminalTransform extends TerminalWritable { constructor(options: ITerminalTransformOptions); // @sealed protected autocloseDestination(): void; readonly destination: TerminalWritable; - // @override (undocumented) + // (undocumented) protected onClose(): void; readonly preventDestinationAutoclose: boolean; } @@ -198,6 +537,9 @@ export abstract class TerminalWritable { writeChunk(chunk: ITerminalChunk): void; } +// @beta (undocumented) +export type TerminalWriteParameters = string[] | [...string[], ITerminalWriteOptions]; + // @public export abstract class TextRewriter { abstract close(state: TextRewriterState): string; diff --git a/common/reviews/api/ts-command-line.api.md b/common/reviews/api/ts-command-line.api.md index 1c67ef1e51d..e7534624832 100644 --- a/common/reviews/api/ts-command-line.api.md +++ b/common/reviews/api/ts-command-line.api.md @@ -6,6 +6,18 @@ import * as argparse from 'argparse'; +// @public +export class AliasCommandLineAction extends CommandLineAction { + constructor(options: IAliasCommandLineActionOptions); + readonly defaultParameters: ReadonlyArray; + protected onExecuteAsync(): Promise; + // @internal + _processParsedData(parserOptions: ICommandLineParserOptions, data: _ICommandLineParserData): void; + // @internal (undocumented) + _registerDefinedParameters(state: _IRegisterDefinedParametersState): void; + readonly targetAction: CommandLineAction; +} + // @public export abstract class CommandLineAction extends CommandLineParameterProvider { constructor(options: ICommandLineActionOptions); @@ -14,60 +26,55 @@ export abstract class CommandLineAction extends CommandLineParameterProvider { _buildParser(actionsSubParser: argparse.SubParser): void; readonly documentation: string; // @internal - _execute(): Promise; + _executeAsync(): Promise; // @internal - protected _getArgumentParser(): argparse.ArgumentParser; - protected abstract onExecute(): Promise; - // @internal - _processParsedData(parserOptions: ICommandLineParserOptions, data: _ICommandLineParserData): void; + _getArgumentParser(): argparse.ArgumentParser; + protected abstract onExecuteAsync(): Promise; readonly summary: string; } // @public -export class CommandLineChoiceListParameter extends CommandLineParameter { +export class CommandLineChoiceListParameter extends CommandLineParameterBase { // @internal - constructor(definition: ICommandLineChoiceListDefinition); - readonly alternatives: ReadonlyArray; - // @override + constructor(definition: ICommandLineChoiceListDefinition); + readonly alternatives: ReadonlySet; appendToArgList(argList: string[]): void; - readonly completions: (() => Promise) | undefined; - get kind(): CommandLineParameterKind; + readonly completions: (() => Promise | ReadonlySet>) | undefined; + readonly kind: CommandLineParameterKind.ChoiceList; // @internal - _setValue(data: any): void; - get values(): ReadonlyArray; + _setValue(data: unknown): void; + get values(): ReadonlyArray; } // @public -export class CommandLineChoiceParameter extends CommandLineParameter { +export class CommandLineChoiceParameter extends CommandLineParameterBase { // @internal - constructor(definition: ICommandLineChoiceDefinition); - readonly alternatives: ReadonlyArray; - // @override + constructor(definition: ICommandLineChoiceDefinition); + readonly alternatives: ReadonlySet; appendToArgList(argList: string[]): void; - readonly completions: (() => Promise) | undefined; - readonly defaultValue: string | undefined; + readonly completions: (() => Promise | ReadonlySet>) | undefined; + readonly defaultValue: TChoice | undefined; // @internal _getSupplementaryNotes(supplementaryNotes: string[]): void; - get kind(): CommandLineParameterKind; + readonly kind: CommandLineParameterKind.Choice; // @internal - _setValue(data: any): void; - get value(): string | undefined; + _setValue(data: unknown): void; + get value(): TChoice | undefined; } // @public -export const enum CommandLineConstants { +export enum CommandLineConstants { TabCompletionActionName = "tab-complete" } // @public -export class CommandLineFlagParameter extends CommandLineParameter { +export class CommandLineFlagParameter extends CommandLineParameterBase { // @internal constructor(definition: ICommandLineFlagDefinition); - // @override appendToArgList(argList: string[]): void; - get kind(): CommandLineParameterKind; + readonly kind: CommandLineParameterKind.Flag; // @internal - _setValue(data: any): void; + _setValue(data: unknown): void; get value(): boolean; } @@ -80,11 +87,10 @@ export class CommandLineHelper { export class CommandLineIntegerListParameter extends CommandLineParameterWithArgument { // @internal constructor(definition: ICommandLineIntegerListDefinition); - // @override appendToArgList(argList: string[]): void; - get kind(): CommandLineParameterKind; + readonly kind: CommandLineParameterKind.IntegerList; // @internal - _setValue(data: any): void; + _setValue(data: unknown): void; get values(): ReadonlyArray; } @@ -92,21 +98,24 @@ export class CommandLineIntegerListParameter extends CommandLineParameterWithArg export class CommandLineIntegerParameter extends CommandLineParameterWithArgument { // @internal constructor(definition: ICommandLineIntegerDefinition); - // @override appendToArgList(argList: string[]): void; readonly defaultValue: number | undefined; // @internal _getSupplementaryNotes(supplementaryNotes: string[]): void; - get kind(): CommandLineParameterKind; + readonly kind: CommandLineParameterKind.Integer; // @internal - _setValue(data: any): void; + _setValue(data: unknown): void; get value(): number | undefined; } +// @public (undocumented) +export type CommandLineParameter = CommandLineChoiceListParameter | CommandLineChoiceParameter | CommandLineFlagParameter | CommandLineIntegerListParameter | CommandLineIntegerParameter | CommandLineStringListParameter | CommandLineStringParameter; + // @public -export abstract class CommandLineParameter { +export abstract class CommandLineParameterBase { // @internal constructor(definition: IBaseCommandLineDefinition); + readonly allowNonStandardEnvironmentVariableNames: boolean | undefined; abstract appendToArgList(argList: string[]): void; readonly description: string; readonly environmentVariable: string | undefined; @@ -118,15 +127,21 @@ export abstract class CommandLineParameter { readonly parameterScope: string | undefined; // @internal _parserKey: string | undefined; - protected reportInvalidData(data: any): never; + // @internal (undocumented) + _postParse?: () => void; + // @internal (undocumented) + _preParse?: () => void; + protected reportInvalidData(data: unknown): never; readonly required: boolean; readonly scopedLongName: string | undefined; // @internal - abstract _setValue(data: any): void; - readonly shortName: string | undefined; + abstract _setValue(data: unknown): void; + get shortName(): string | undefined; readonly undocumentedSynonyms: string[] | undefined; // (undocumented) protected validateDefaultValue(hasDefaultValue: boolean): void; + // @internal (undocumented) + _validateValue?: () => void; } // @public @@ -144,15 +159,49 @@ export enum CommandLineParameterKind { export abstract class CommandLineParameterProvider { // @internal constructor(); - defineChoiceListParameter(definition: ICommandLineChoiceListDefinition): CommandLineChoiceListParameter; - defineChoiceParameter(definition: ICommandLineChoiceDefinition): CommandLineChoiceParameter; + // @internal (undocumented) + readonly _ambiguousParameterParserKeysByName: Map; + // @internal (undocumented) + protected _defineAmbiguousParameter(name: string): string; + defineChoiceListParameter(definition: ICommandLineChoiceListDefinition): CommandLineChoiceListParameter; + defineChoiceParameter(definition: ICommandLineChoiceDefinition & { + required: false | undefined; + defaultValue: undefined; + }): CommandLineChoiceParameter; + defineChoiceParameter(definition: ICommandLineChoiceDefinition & { + required: true; + }): IRequiredCommandLineChoiceParameter; + defineChoiceParameter(definition: ICommandLineChoiceDefinition & { + defaultValue: TChoice; + }): IRequiredCommandLineChoiceParameter; + defineChoiceParameter(definition: ICommandLineChoiceDefinition): CommandLineChoiceParameter; defineCommandLineRemainder(definition: ICommandLineRemainderDefinition): CommandLineRemainder; defineFlagParameter(definition: ICommandLineFlagDefinition): CommandLineFlagParameter; defineIntegerListParameter(definition: ICommandLineIntegerListDefinition): CommandLineIntegerListParameter; + defineIntegerParameter(definition: ICommandLineIntegerDefinition & { + required: false | undefined; + defaultValue: undefined; + }): CommandLineIntegerParameter; + defineIntegerParameter(definition: ICommandLineIntegerDefinition & { + required: true; + }): IRequiredCommandLineIntegerParameter; + defineIntegerParameter(definition: ICommandLineIntegerDefinition & { + defaultValue: number; + }): IRequiredCommandLineIntegerParameter; defineIntegerParameter(definition: ICommandLineIntegerDefinition): CommandLineIntegerParameter; // @internal (undocumented) protected _defineParameter(parameter: CommandLineParameter): void; defineStringListParameter(definition: ICommandLineStringListDefinition): CommandLineStringListParameter; + defineStringParameter(definition: ICommandLineStringDefinition & { + required: false | undefined; + defaultValue: undefined; + }): CommandLineStringParameter; + defineStringParameter(definition: ICommandLineStringDefinition & { + required: true; + }): IRequiredCommandLineStringParameter; + defineStringParameter(definition: ICommandLineStringDefinition & { + defaultValue: string; + }): IRequiredCommandLineStringParameter; defineStringParameter(definition: ICommandLineStringDefinition): CommandLineStringParameter; // @internal protected abstract _getArgumentParser(): argparse.ArgumentParser; @@ -164,42 +213,49 @@ export abstract class CommandLineParameterProvider { getParameterStringMap(): Record; getStringListParameter(parameterLongName: string, parameterScope?: string): CommandLineStringListParameter; getStringParameter(parameterLongName: string, parameterScope?: string): CommandLineStringParameter; - protected onDefineParameters?(): void; get parameters(): ReadonlyArray; get parametersProcessed(): boolean; parseScopedLongName(scopedLongName: string): IScopedLongNameParseResult; + // @internal + _postParse(): void; + // @internal + _preParse(): void; + // @internal + _processParsedData(parserOptions: ICommandLineParserOptions, data: _ICommandLineParserData): void; + // (undocumented) + protected _registerAmbiguousParameter(name: string, parserKey: string): void; // @internal (undocumented) - protected _processParsedData(parserOptions: ICommandLineParserOptions, data: _ICommandLineParserData): void; + _registerDefinedParameters(state: _IRegisterDefinedParametersState): void; // @internal (undocumented) - _registerDefinedParameters(): void; + protected readonly _registeredParameterParserKeysByName: Map; // @internal (undocumented) - protected _registerParameter(parameter: CommandLineParameter, useScopedLongName: boolean): void; + protected _registerParameter(parameter: CommandLineParameter, useScopedLongName: boolean, ignoreShortName: boolean): void; get remainder(): CommandLineRemainder | undefined; renderHelpText(): string; renderUsageText(): string; } // @public -export abstract class CommandLineParameterWithArgument extends CommandLineParameter { +export abstract class CommandLineParameterWithArgument extends CommandLineParameterBase { // @internal constructor(definition: IBaseCommandLineDefinitionWithArgument); readonly argumentName: string; - readonly completions: (() => Promise) | undefined; + readonly getCompletionsAsync: (() => Promise | ReadonlySet>) | undefined; } // @public -export abstract class CommandLineParser extends CommandLineParameterProvider { +export class CommandLineParser extends CommandLineParameterProvider { constructor(options: ICommandLineParserOptions); get actions(): ReadonlyArray; addAction(action: CommandLineAction): void; - execute(args?: string[]): Promise; - executeWithoutErrorHandling(args?: string[]): Promise; + executeAsync(args?: string[]): Promise; + executeWithoutErrorHandlingAsync(args?: string[]): Promise; getAction(actionName: string): CommandLineAction; // @internal protected _getArgumentParser(): argparse.ArgumentParser; - protected onExecute(): Promise; + protected onExecuteAsync(): Promise; // @internal (undocumented) - _registerDefinedParameters(): void; + _registerDefinedParameters(state: _IRegisterDefinedParametersState): void; selectedAction: CommandLineAction | undefined; tryGetAction(actionName: string): CommandLineAction | undefined; } @@ -208,11 +264,10 @@ export abstract class CommandLineParser extends CommandLineParameterProvider { export class CommandLineRemainder { // @internal constructor(definition: ICommandLineRemainderDefinition); - // @override appendToArgList(argList: string[]): void; readonly description: string; // @internal - _setValue(data: any): void; + _setValue(data: unknown): void; get values(): ReadonlyArray; } @@ -220,11 +275,10 @@ export class CommandLineRemainder { export class CommandLineStringListParameter extends CommandLineParameterWithArgument { // @internal constructor(definition: ICommandLineStringListDefinition); - // @override appendToArgList(argList: string[]): void; - get kind(): CommandLineParameterKind; + readonly kind: CommandLineParameterKind.StringList; // @internal - _setValue(data: any): void; + _setValue(data: unknown): void; get values(): ReadonlyArray; } @@ -232,29 +286,37 @@ export class CommandLineStringListParameter extends CommandLineParameterWithArgu export class CommandLineStringParameter extends CommandLineParameterWithArgument { // @internal constructor(definition: ICommandLineStringDefinition); - // @override appendToArgList(argList: string[]): void; readonly defaultValue: string | undefined; // @internal _getSupplementaryNotes(supplementaryNotes: string[]): void; - get kind(): CommandLineParameterKind; + readonly kind: CommandLineParameterKind.String; // @internal - _setValue(data: any): void; + _setValue(data: unknown): void; get value(): string | undefined; } // @public (undocumented) export class DynamicCommandLineAction extends CommandLineAction { // (undocumented) - protected onExecute(): Promise; + protected onExecuteAsync(): Promise; } // @public (undocumented) export class DynamicCommandLineParser extends CommandLineParser { } +// @public +export interface IAliasCommandLineActionOptions { + aliasName: string; + defaultParameters?: string[]; + targetAction: CommandLineAction; + toolFilename: string; +} + // @public export interface IBaseCommandLineDefinition { + allowNonStandardEnvironmentVariableNames?: boolean; description: string; environmentVariable?: string; parameterGroup?: string | typeof SCOPING_PARAMETER_GROUP; @@ -268,7 +330,7 @@ export interface IBaseCommandLineDefinition { // @public export interface IBaseCommandLineDefinitionWithArgument extends IBaseCommandLineDefinition { argumentName: string; - completions?: () => Promise; + getCompletionsAsync?: () => Promise | ReadonlySet>; } // @public @@ -279,16 +341,16 @@ export interface ICommandLineActionOptions { } // @public -export interface ICommandLineChoiceDefinition extends IBaseCommandLineDefinition { - alternatives: string[]; - completions?: () => Promise; - defaultValue?: string; +export interface ICommandLineChoiceDefinition extends IBaseCommandLineDefinition { + alternatives: ReadonlyArray | ReadonlySet; + completions?: () => Promise | ReadonlySet>; + defaultValue?: TChoice; } // @public -export interface ICommandLineChoiceListDefinition extends IBaseCommandLineDefinition { - alternatives: string[]; - completions?: () => Promise; +export interface ICommandLineChoiceListDefinition extends IBaseCommandLineDefinition { + alternatives: ReadonlyArray | ReadonlySet; + completions?: () => Promise | ReadonlySet>; } // @public @@ -310,6 +372,10 @@ export interface _ICommandLineParserData { [key: string]: any; // (undocumented) action: string; + // (undocumented) + aliasAction?: string; + // (undocumented) + aliasDocumentation?: string; } // @public @@ -334,6 +400,29 @@ export interface ICommandLineStringDefinition extends IBaseCommandLineDefinition export interface ICommandLineStringListDefinition extends IBaseCommandLineDefinitionWithArgument { } +// @internal +export interface _IRegisterDefinedParametersState { + parentParameterNames: Set; +} + +// @public +export interface IRequiredCommandLineChoiceParameter extends CommandLineChoiceParameter { + // (undocumented) + readonly value: TChoice; +} + +// @public +export interface IRequiredCommandLineIntegerParameter extends CommandLineIntegerParameter { + // (undocumented) + readonly value: number; +} + +// @public +export interface IRequiredCommandLineStringParameter extends CommandLineStringParameter { + // (undocumented) + readonly value: string; +} + // @public export interface IScopedLongNameParseResult { longName: string; @@ -346,16 +435,16 @@ export abstract class ScopedCommandLineAction extends CommandLineAction { // @internal (undocumented) protected _defineParameter(parameter: CommandLineParameter): void; // @internal - _execute(): Promise; + _executeAsync(): Promise; // @internal protected _getScopedCommandLineParser(): CommandLineParser; - protected onDefineParameters(): void; protected abstract onDefineScopedParameters(scopedParameterProvider: CommandLineParameterProvider): void; - protected onDefineUnscopedParameters?(): void; - protected abstract onExecute(): Promise; + protected abstract onExecuteAsync(): Promise; get parameters(): ReadonlyArray; // @internal _processParsedData(parserOptions: ICommandLineParserOptions, data: _ICommandLineParserData): void; + // @internal (undocumented) + _registerDefinedParameters(state: _IRegisterDefinedParametersState): void; static readonly ScopingParameterGroup: typeof SCOPING_PARAMETER_GROUP; } diff --git a/common/reviews/api/typings-generator.api.md b/common/reviews/api/typings-generator.api.md index b3fdbc77b42..ca5acb0922a 100644 --- a/common/reviews/api/typings-generator.api.md +++ b/common/reviews/api/typings-generator.api.md @@ -4,30 +4,72 @@ ```ts -import { ITerminal } from '@rushstack/node-core-library'; +import { ITerminal } from '@rushstack/terminal'; + +// @public +export interface IDeclarationMapping { + generatedColumn: number; + generatedLine: number; + sourcePosition: ISourcePosition; +} + +// @public (undocumented) +export interface IExportAsDefaultOptions { + // @deprecated (undocumented) + documentationComment?: string; + interfaceDocumentationComment?: string; + interfaceName?: string; + valueDocumentationComment?: string; +} + +// @public +export interface IGeneratedTypings { + declarationMappings?: readonly IDeclarationMapping[]; + typingsData: string; +} + +// @public +export interface ISourcePosition { + // (undocumented) + column: number; + // (undocumented) + line: number; +} // @public (undocumented) -export interface IStringValuesTypingsGeneratorOptions extends ITypingsGeneratorOptions { - exportAsDefault?: boolean; +export interface IStringValuesTypingsGeneratorBaseOptions { + exportAsDefault?: boolean | IExportAsDefaultOptions; + // @deprecated (undocumented) exportAsDefaultInterfaceName?: string; } +// @public (undocumented) +export interface IStringValuesTypingsGeneratorOptions extends ITypingsGeneratorOptions, IStringValuesTypingsGeneratorBaseOptions { +} + +// @public (undocumented) +export interface IStringValuesTypingsGeneratorOptionsWithCustomReadFile extends ITypingsGeneratorOptionsWithCustomReadFile, IStringValuesTypingsGeneratorBaseOptions { +} + // @public (undocumented) export interface IStringValueTyping { // (undocumented) comment?: string; // (undocumented) exportName: string; + sourcePosition?: ISourcePosition; } // @public (undocumented) export interface IStringValueTypings { + exportAsDefault?: boolean | IExportAsDefaultOptions; // (undocumented) typings: IStringValueTyping[]; } // @public (undocumented) export interface ITypingsGeneratorBaseOptions { + generateDeclarationMaps?: boolean; // (undocumented) generatedTsFolder: string; // (undocumented) @@ -41,36 +83,56 @@ export interface ITypingsGeneratorBaseOptions { } // @public (undocumented) -export interface ITypingsGeneratorOptions extends ITypingsGeneratorBaseOptions { +export interface ITypingsGeneratorOptions extends ITypingsGeneratorOptionsWithoutReadFile { + // (undocumented) + readFile?: ReadFile; +} + +// @public +export interface ITypingsGeneratorOptionsWithCustomReadFile extends ITypingsGeneratorOptionsWithoutReadFile { + // (undocumented) + readFile: ReadFile; +} + +// @public (undocumented) +export interface ITypingsGeneratorOptionsWithoutReadFile extends ITypingsGeneratorBaseOptions { // (undocumented) fileExtensions: string[]; - // @deprecated (undocumented) - filesToIgnore?: string[]; // (undocumented) getAdditionalOutputFiles?: (relativePath: string) => string[]; // (undocumented) - parseAndGenerateTypings: (fileContents: string, filePath: string, relativePath: string) => TTypingsResult | Promise; + parseAndGenerateTypings: (fileContents: TFileContents, filePath: string, relativePath: string) => TTypingsResult | Promise; } +// @public (undocumented) +export type ReadFile = (filePath: string, relativePath: string) => Promise | TFileContents; + +// @public +export function serializeDeclarationMap(mappings: readonly IDeclarationMapping[], generatedFileName: string, sourcePath: string, generatedLineOffset: number): string; + // @public -export class StringValuesTypingsGenerator extends TypingsGenerator { - constructor(options: IStringValuesTypingsGeneratorOptions); +export class StringValuesTypingsGenerator extends TypingsGenerator { + constructor(options: TFileContents extends string ? IStringValuesTypingsGeneratorOptions : never); + constructor(options: IStringValuesTypingsGeneratorOptionsWithCustomReadFile); } // @public -export class TypingsGenerator { - constructor(options: ITypingsGeneratorOptions); +export class TypingsGenerator { + constructor(options: TFileContents extends string ? ITypingsGeneratorOptions : never); + constructor(options: ITypingsGeneratorOptionsWithCustomReadFile); generateTypingsAsync(relativeFilePaths?: string[]): Promise; // (undocumented) getOutputFilePaths(relativePath: string): string[]; readonly ignoredFileGlobs: readonly string[]; readonly inputFileGlob: string; // (undocumented) - protected _options: ITypingsGeneratorOptions; + protected readonly _options: ITypingsGeneratorOptionsWithCustomReadFile; registerDependency(consumer: string, rawDependency: string): void; // (undocumented) runWatcherAsync(): Promise; readonly sourceFolderPath: string; + // (undocumented) + protected readonly terminal: ITerminal; } ``` diff --git a/common/reviews/api/webpack-plugin-utilities.api.md b/common/reviews/api/webpack-plugin-utilities.api.md index 3b2bf0ac56f..18f18316f0b 100644 --- a/common/reviews/api/webpack-plugin-utilities.api.md +++ b/common/reviews/api/webpack-plugin-utilities.api.md @@ -5,11 +5,16 @@ ```ts import type { Configuration } from 'webpack'; +import type { Expression } from 'estree'; import { IFs } from 'memfs'; import type { MultiStats } from 'webpack'; +import type { SpreadElement } from 'estree'; import type { Stats } from 'webpack'; import type * as Webpack from 'webpack'; +// @beta +export function evaluateConstantEstreeExpression(node: Expression | SpreadElement): TNode; + // @public function getTestingWebpackCompilerAsync(entry: string, additionalConfig?: Configuration, memFs?: IFs): Promise<(Stats | MultiStats) | undefined>; diff --git a/common/reviews/api/webpack-workspace-resolve-plugin.api.md b/common/reviews/api/webpack-workspace-resolve-plugin.api.md new file mode 100644 index 00000000000..ef0216174c9 --- /dev/null +++ b/common/reviews/api/webpack-workspace-resolve-plugin.api.md @@ -0,0 +1,69 @@ +## API Report File for "@rushstack/webpack-workspace-resolve-plugin" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { Compiler } from 'webpack'; +import { IPrefixMatch } from '@rushstack/lookup-by-path'; +import { LookupByPath } from '@rushstack/lookup-by-path'; +import type { WebpackPluginInstance } from 'webpack'; + +// @beta +export type IPathNormalizationFunction = ((input: string) => string) | undefined; + +// @beta +export interface IResolveContext { + descriptionFileRoot: string; + findDependency(request: string): IPrefixMatch | undefined; +} + +// @beta +export interface IResolverCacheFile { + basePath: string; + contexts: ISerializedResolveContext[]; +} + +// @beta +export interface ISerializedResolveContext { + deps?: Record; + dirInfoFiles?: string[]; + name: string; + root: string; +} + +// @beta +export interface IWorkspaceLayoutCacheOptions { + cacheData: IResolverCacheFile; + resolverPathSeparator?: '/' | '\\'; +} + +// @beta +export interface IWorkspaceResolvePluginOptions { + cache: WorkspaceLayoutCache; + resolverNames?: Iterable; +} + +// @beta +export class WorkspaceLayoutCache { + constructor(options: IWorkspaceLayoutCacheOptions); + readonly contextForPackage: WeakMap>; + readonly contextLookup: LookupByPath; + // (undocumented) + readonly normalizeToPlatform: IPathNormalizationFunction; + // (undocumented) + readonly normalizeToSlash: IPathNormalizationFunction; + // (undocumented) + readonly resolverPathSeparator: string; +} + +// @beta +export class WorkspaceResolvePlugin implements WebpackPluginInstance { + constructor(options: IWorkspaceResolvePluginOptions); + // (undocumented) + apply(compiler: Compiler): void; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/common/reviews/api/webpack4-localization-plugin.api.md b/common/reviews/api/webpack4-localization-plugin.api.md index f818d6d5ace..9c8f51cc3ec 100644 --- a/common/reviews/api/webpack4-localization-plugin.api.md +++ b/common/reviews/api/webpack4-localization-plugin.api.md @@ -4,10 +4,10 @@ ```ts -import { IgnoreStringFunction } from '@rushstack/localization-utilities'; +import type { IgnoreStringFunction } from '@rushstack/localization-utilities'; import { ILocalizationFile } from '@rushstack/localization-utilities'; -import { IPseudolocaleOptions } from '@rushstack/localization-utilities'; -import { ITerminal } from '@rushstack/node-core-library'; +import type { IPseudolocaleOptions } from '@rushstack/localization-utilities'; +import type { ITerminal } from '@rushstack/terminal'; import * as Webpack from 'webpack'; // @public (undocumented) diff --git a/common/reviews/api/webpack4-module-minifier-plugin.api.md b/common/reviews/api/webpack4-module-minifier-plugin.api.md index e2811f5b7d4..a664bae677f 100644 --- a/common/reviews/api/webpack4-module-minifier-plugin.api.md +++ b/common/reviews/api/webpack4-module-minifier-plugin.api.md @@ -5,7 +5,7 @@ ```ts import type { AsyncSeriesWaterfallHook } from 'tapable'; -import { Compiler } from 'webpack'; +import type { Compiler } from 'webpack'; import { getIdentifier } from '@rushstack/module-minifier'; import { ILocalMinifierOptions } from '@rushstack/module-minifier'; import { IMinifierConnection } from '@rushstack/module-minifier'; @@ -20,7 +20,7 @@ import { IWorkerPoolMinifierOptions } from '@rushstack/module-minifier'; import { LocalMinifier } from '@rushstack/module-minifier'; import { MessagePortMinifier } from '@rushstack/module-minifier'; import { NoopMinifier } from '@rushstack/module-minifier'; -import { Plugin } from 'webpack'; +import type { Plugin } from 'webpack'; import type { ReplaceSource } from 'webpack-sources'; import { Source } from 'webpack-sources'; import type { SyncWaterfallHook } from 'tapable'; diff --git a/common/reviews/api/webpack5-localization-plugin.api.md b/common/reviews/api/webpack5-localization-plugin.api.md index 99fec787b26..b985e49c966 100644 --- a/common/reviews/api/webpack5-localization-plugin.api.md +++ b/common/reviews/api/webpack5-localization-plugin.api.md @@ -4,13 +4,21 @@ ```ts +/// + import type { Chunk } from 'webpack'; +import type { Compilation } from 'webpack'; import type { Compiler } from 'webpack'; import { ILocalizationFile } from '@rushstack/localization-utilities'; import type { IPseudolocaleOptions } from '@rushstack/localization-utilities'; import type { LoaderContext } from 'webpack'; import type { WebpackPluginInstance } from 'webpack'; +// @internal (undocumented) +export interface _ICustomDataPlaceholder extends IValuePlaceholderBase { + valueForLocaleFn: ValueForLocaleFn; +} + // @public (undocumented) export interface IDefaultLocaleOptions { fillMissingTranslationStrings?: boolean; @@ -40,10 +48,12 @@ export interface ILocaleFileObject { // @public export interface ILocalizationPluginOptions { + formatLocaleForFilename?: (locale: string) => string; globsToIgnore?: string[]; localizationStats?: ILocalizationStatsOptions; localizedData: ILocalizedData; noStringsLocaleName?: string; + realContentHash?: boolean; runtimeLocaleExpression?: string; } @@ -73,7 +83,7 @@ export interface ILocalizationStatsEntrypoint { // @public export interface ILocalizationStatsOptions { - callback?: (stats: ILocalizationStats) => void; + callback?: (stats: ILocalizationStats, compilation: Compilation) => void; dropPath?: string; } @@ -116,12 +126,24 @@ export interface IPseudolocalesOptions { export type IResolvedMissingTranslations = ReadonlyMap; // @public (undocumented) -export interface _IStringPlaceholder { +interface IStringPlaceholder extends IValuePlaceholderBase { locFilePath: string; stringName: string; + translations: ReadonlyMap>; +} +export { IStringPlaceholder } +export { IStringPlaceholder as _IStringPlaceholder } + +// @public (undocumented) +export interface ITrueHashPluginOptions { + hashFunction?: (contents: string | Buffer) => string; + stageOverride?: number; +} + +// @public (undocumented) +export interface IValuePlaceholderBase { suffix: string; value: string; - valuesByLocale: Map; } // @public @@ -131,13 +153,27 @@ export class LocalizationPlugin implements WebpackPluginInstance { addDefaultLocFileAsync(context: LoaderContext<{}>, localizedFileKey: string, localizedResourceData: ILocalizationFile): Promise>; apply(compiler: Compiler): void; // @internal (undocumented) - getDataForSerialNumber(serialNumber: string): _IStringPlaceholder | undefined; + _getCustomDataForSerialNumber(suffix: string): _ICustomDataPlaceholder | undefined; + // @beta (undocumented) + getCustomDataPlaceholderForValueFunction(valueForLocaleFn: ValueForLocaleFn, placeholderUniqueId: string): string; // (undocumented) - getPlaceholder(localizedFileKey: string, stringName: string): _IStringPlaceholder | undefined; + getPlaceholder(localizedFileKey: string, stringName: string): IStringPlaceholder | undefined; + // @internal (undocumented) + _getStringDataForSerialNumber(suffix: string): IStringPlaceholder | undefined; + // @internal (undocumented) + readonly _options: ILocalizationPluginOptions; +} + +// @public (undocumented) +export class TrueHashPlugin implements WebpackPluginInstance { + constructor(options?: ITrueHashPluginOptions); // (undocumented) - readonly stringKeys: Map; + apply(compiler: Compiler): void; } +// @public (undocumented) +export type ValueForLocaleFn = (locale: string, chunk: Chunk) => string; + // (No @packageDocumentation comment for this package) ``` diff --git a/common/reviews/api/webpack5-module-minifier-plugin.api.md b/common/reviews/api/webpack5-module-minifier-plugin.api.md index 825f134b3b6..3d5d843e80f 100644 --- a/common/reviews/api/webpack5-module-minifier-plugin.api.md +++ b/common/reviews/api/webpack5-module-minifier-plugin.api.md @@ -59,6 +59,7 @@ export interface IFactoryMeta { // @public export interface IModuleInfo { id: string | number; + isShorthand?: boolean; module: Module; source: sources.Source; } @@ -110,6 +111,12 @@ export interface IRenderedModulePosition { // @public export const MODULE_WRAPPER_PREFIX: '__MINIFY_MODULE__('; +// @public +export const MODULE_WRAPPER_SHORTHAND_PREFIX: `${typeof MODULE_WRAPPER_PREFIX}{__DEFAULT_ID__`; + +// @public +export const MODULE_WRAPPER_SHORTHAND_SUFFIX: `}${typeof MODULE_WRAPPER_SUFFIX}`; + // @public export const MODULE_WRAPPER_SUFFIX: ');'; diff --git a/common/reviews/api/worker-pool.api.md b/common/reviews/api/worker-pool.api.md index 83d458d5576..5761d9d92ce 100644 --- a/common/reviews/api/worker-pool.api.md +++ b/common/reviews/api/worker-pool.api.md @@ -6,7 +6,8 @@ /// -import { Worker } from 'worker_threads'; +import { ResourceLimits } from 'node:worker_threads'; +import { Worker } from 'node:worker_threads'; // Warning: (ae-internal-missing-underscore) The name "IWorkerPoolOptions" should be prefixed with an underscore because the declaration is marked as @internal // @@ -17,6 +18,7 @@ export interface IWorkerPoolOptions { onWorkerDestroyed?: () => void; prepareWorker?: (worker: Worker) => void; workerData?: unknown; + workerResourceLimits?: ResourceLimits; workerScriptPath: string; } diff --git a/common/scripts/install-run-rush-pnpm.js b/common/scripts/install-run-rush-pnpm.js index 5c149955de6..0fcb04975dd 100644 --- a/common/scripts/install-run-rush-pnpm.js +++ b/common/scripts/install-run-rush-pnpm.js @@ -10,16 +10,19 @@ // node common/scripts/install-run-rush-pnpm.js pnpm-command // // For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ +// +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See the @microsoft/rush package's LICENSE file for details. /******/ (() => { // webpackBootstrap /******/ "use strict"; var __webpack_exports__ = {}; -/*!*****************************************************!*\ - !*** ./lib-esnext/scripts/install-run-rush-pnpm.js ***! - \*****************************************************/ +/*!***************************************************************!*\ + !*** ./lib-intermediate-esm/scripts/install-run-rush-pnpm.js ***! + \***************************************************************/ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See the @microsoft/rush package's LICENSE file for license information. +// See LICENSE in the project root for license information. require('./install-run-rush'); //# sourceMappingURL=install-run-rush-pnpm.js.map module.exports = __webpack_exports__; diff --git a/common/scripts/install-run-rush.js b/common/scripts/install-run-rush.js index cada1eded21..5b1da11a79b 100644 --- a/common/scripts/install-run-rush.js +++ b/common/scripts/install-run-rush.js @@ -8,30 +8,33 @@ // node common/scripts/install-run-rush.js install // // For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ +// +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See the @microsoft/rush package's LICENSE file for details. /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ -/***/ 657147: -/*!*********************!*\ - !*** external "fs" ***! - \*********************/ -/***/ ((module) => { +/***/ 973024 +/*!**************************!*\ + !*** external "node:fs" ***! + \**************************/ +(module) { -module.exports = require("fs"); +module.exports = require("node:fs"); -/***/ }), +/***/ }, -/***/ 371017: -/*!***********************!*\ - !*** external "path" ***! - \***********************/ -/***/ ((module) => { +/***/ 176760 +/*!****************************!*\ + !*** external "node:path" ***! + \****************************/ +(module) { -module.exports = require("path"); +module.exports = require("node:path"); -/***/ }) +/***/ } /******/ }); /************************************************************************/ @@ -53,6 +56,12 @@ module.exports = require("path"); /******/ }; /******/ /******/ // Execute the module function +/******/ if (!(moduleId in __webpack_modules__)) { +/******/ delete __webpack_module_cache__[moduleId]; +/******/ var e = new Error("Cannot find module '" + moduleId + "'"); +/******/ e.code = 'MODULE_NOT_FOUND'; +/******/ throw e; +/******/ } /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module @@ -102,23 +111,25 @@ module.exports = require("path"); /******/ /************************************************************************/ var __webpack_exports__ = {}; -// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk. (() => { -/*!************************************************!*\ - !*** ./lib-esnext/scripts/install-run-rush.js ***! - \************************************************/ +/*!**********************************************************!*\ + !*** ./lib-intermediate-esm/scripts/install-run-rush.js ***! + \**********************************************************/ __webpack_require__.r(__webpack_exports__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! path */ 371017); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! fs */ 657147); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! node:path */ 176760); +/* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(node_path__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! node:fs */ 973024); +/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(node_fs__WEBPACK_IMPORTED_MODULE_1__); // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See the @microsoft/rush package's LICENSE file for license information. +// See LICENSE in the project root for license information. +/* eslint-disable no-console */ const { installAndRun, findRushJsonFolder, RUSH_JSON_FILENAME, runWithErrorAndStatusCode } = require('./install-run'); const PACKAGE_NAME = '@microsoft/rush'; const RUSH_PREVIEW_VERSION = 'RUSH_PREVIEW_VERSION'; +const RUSH_QUIET_MODE = 'RUSH_QUIET_MODE'; const INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE = 'INSTALL_RUN_RUSH_LOCKFILE_PATH'; function _getRushVersion(logger) { const rushPreviewVersion = process.env[RUSH_PREVIEW_VERSION]; @@ -127,17 +138,17 @@ function _getRushVersion(logger) { return rushPreviewVersion; } const rushJsonFolder = findRushJsonFolder(); - const rushJsonPath = path__WEBPACK_IMPORTED_MODULE_0__.join(rushJsonFolder, RUSH_JSON_FILENAME); + const rushJsonPath = node_path__WEBPACK_IMPORTED_MODULE_0__.join(rushJsonFolder, RUSH_JSON_FILENAME); try { - const rushJsonContents = fs__WEBPACK_IMPORTED_MODULE_1__.readFileSync(rushJsonPath, 'utf-8'); + const rushJsonContents = node_fs__WEBPACK_IMPORTED_MODULE_1__.readFileSync(rushJsonPath, 'utf-8'); // Use a regular expression to parse out the rushVersion value because rush.json supports comments, // but JSON.parse does not and we don't want to pull in more dependencies than we need to in this script. const rushJsonMatches = rushJsonContents.match(/\"rushVersion\"\s*\:\s*\"([0-9a-zA-Z.+\-]+)\"/); return rushJsonMatches[1]; } catch (e) { - throw new Error(`Unable to determine the required version of Rush from rush.json (${rushJsonFolder}). ` + - "The 'rushVersion' field is either not assigned in rush.json or was specified " + + throw new Error(`Unable to determine the required version of Rush from ${RUSH_JSON_FILENAME} (${rushJsonFolder}). ` + + `The 'rushVersion' field is either not assigned in ${RUSH_JSON_FILENAME} or was specified ` + 'using an unexpected syntax.'); } } @@ -155,13 +166,14 @@ function _run() { const [nodePath /* Ex: /bin/node */, scriptPath /* /repo/common/scripts/install-run-rush.js */, ...packageBinArgs /* [build, --to, myproject] */] = process.argv; // Detect if this script was directly invoked, or if the install-run-rushx script was invokved to select the // appropriate binary inside the rush package to run - const scriptName = path__WEBPACK_IMPORTED_MODULE_0__.basename(scriptPath); + const scriptName = node_path__WEBPACK_IMPORTED_MODULE_0__.basename(scriptPath); const bin = _getBin(scriptName); if (!nodePath || !scriptPath) { throw new Error('Unexpected exception: could not detect node path or script path'); } let commandFound = false; - let logger = { info: console.log, error: console.error }; + const quietModeEnvValue = process.env[RUSH_QUIET_MODE]; + let quiet = quietModeEnvValue === '1' || quietModeEnvValue === 'true'; for (const arg of packageBinArgs) { if (arg === '-q' || arg === '--quiet') { // The -q/--quiet flag is supported by both `rush` and `rushx`, and will suppress @@ -170,10 +182,7 @@ function _run() { // To maintain the same user experience, the install-run* scripts pass along this // flag but also use it to suppress any diagnostic information normally printed // to stdout. - logger = { - info: () => { }, - error: console.error - }; + quiet = true; } else if (!arg.startsWith('-') || arg === '-h' || arg === '--help') { // We either found something that looks like a command (i.e. - doesn't start with a "-"), @@ -194,9 +203,12 @@ function _run() { } process.exit(1); } + const logger = quiet + ? { info: () => { }, error: console.error } + : { info: console.log, error: console.error }; runWithErrorAndStatusCode(logger, () => { const version = _getRushVersion(logger); - logger.info(`The rush.json configuration requests Rush version ${version}`); + logger.info(`The ${RUSH_JSON_FILENAME} configuration requests Rush version ${version}`); const lockFilePath = process.env[INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE]; if (lockFilePath) { logger.info(`Found ${INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE}="${lockFilePath}", installing with lockfile.`); diff --git a/common/scripts/install-run-rushx.js b/common/scripts/install-run-rushx.js index b05df262bc2..67d51a05b56 100644 --- a/common/scripts/install-run-rushx.js +++ b/common/scripts/install-run-rushx.js @@ -10,16 +10,19 @@ // node common/scripts/install-run-rushx.js custom-command // // For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ +// +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See the @microsoft/rush package's LICENSE file for details. /******/ (() => { // webpackBootstrap /******/ "use strict"; var __webpack_exports__ = {}; -/*!*************************************************!*\ - !*** ./lib-esnext/scripts/install-run-rushx.js ***! - \*************************************************/ +/*!***********************************************************!*\ + !*** ./lib-intermediate-esm/scripts/install-run-rushx.js ***! + \***********************************************************/ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See the @microsoft/rush package's LICENSE file for license information. +// See LICENSE in the project root for license information. require('./install-run-rush'); //# sourceMappingURL=install-run-rushx.js.map module.exports = __webpack_exports__; diff --git a/common/scripts/install-run.js b/common/scripts/install-run.js index 68b1b56fc58..75d6014e61c 100644 --- a/common/scripts/install-run.js +++ b/common/scripts/install-run.js @@ -8,158 +8,346 @@ // node common/scripts/install-run.js qrcode@1.2.2 qrcode https://rushjs.io // // For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/ +// +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See the @microsoft/rush package's LICENSE file for details. /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ -/***/ 679877: -/*!************************************************!*\ - !*** ./lib-esnext/utilities/npmrcUtilities.js ***! - \************************************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { +/***/ 953844 +/*!**************************************************************!*\ + !*** ./lib-intermediate-esm/utilities/executionUtilities.js ***! + \**************************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "syncNpmrc": () => (/* binding */ syncNpmrc) +/* harmony export */ IS_WINDOWS: () => (/* binding */ IS_WINDOWS), +/* harmony export */ escapeArgumentIfNeeded: () => (/* binding */ escapeArgumentIfNeeded) /* harmony export */ }); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! fs */ 657147); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! path */ 371017); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. +const IS_WINDOWS = process.platform === 'win32'; +function escapeArgumentIfNeeded(command, isWindows = IS_WINDOWS) { + if (command.includes(' ')) { + if (isWindows) { + // Windows: use double quotes and escape internal double quotes + return `"${command.replace(/"/g, '""')}"`; + } + else { + // Unix: use JSON.stringify for proper escaping + return JSON.stringify(command); + } + } + else { + return command; + } +} +//# sourceMappingURL=executionUtilities.js.map + +/***/ }, + +/***/ 359480 +/*!**********************************************************!*\ + !*** ./lib-intermediate-esm/utilities/npmrcUtilities.js ***! + \**********************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ isVariableSetInNpmrcFile: () => (/* binding */ isVariableSetInNpmrcFile), +/* harmony export */ syncNpmrc: () => (/* binding */ syncNpmrc), +/* harmony export */ trimNpmrcFileLines: () => (/* binding */ trimNpmrcFileLines) +/* harmony export */ }); +/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! node:fs */ 973024); +/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(node_fs__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! node:path */ 176760); +/* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(node_path__WEBPACK_IMPORTED_MODULE_1__); // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. // IMPORTANT - do not use any non-built-in libraries in this file /** - * As a workaround, copyAndTrimNpmrcFile() copies the .npmrc file to the target folder, and also trims + * This function reads the content for given .npmrc file path, and also trims * unusable lines from the .npmrc file. * - * Why are we trimming the .npmrc lines? NPM allows environment variables to be specified in - * the .npmrc file to provide different authentication tokens for different registry. - * However, if the environment variable is undefined, it expands to an empty string, which - * produces a valid-looking mapping with an invalid URL that causes an error. Instead, - * we'd prefer to skip that line and continue looking in other places such as the user's - * home directory. - * * @returns * The text of the the .npmrc. */ -function _copyAndTrimNpmrcFile(logger, sourceNpmrcPath, targetNpmrcPath) { - logger.info(`Transforming ${sourceNpmrcPath}`); // Verbose - logger.info(` --> "${targetNpmrcPath}"`); - let npmrcFileLines = fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(sourceNpmrcPath).toString().split('\n'); +function _trimNpmrcFile(options) { + const { sourceNpmrcPath, linesToPrepend, linesToAppend, supportEnvVarFallbackSyntax, filterNpmIncompatibleProperties, env = process.env } = options; + let npmrcFileLines = []; + if (linesToPrepend) { + npmrcFileLines.push(...linesToPrepend); + } + if (node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(sourceNpmrcPath)) { + npmrcFileLines.push(...node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(sourceNpmrcPath).toString().split('\n')); + } + if (linesToAppend) { + npmrcFileLines.push(...linesToAppend); + } npmrcFileLines = npmrcFileLines.map((line) => (line || '').trim()); + const resultLines = trimNpmrcFileLines(npmrcFileLines, env, supportEnvVarFallbackSyntax, filterNpmIncompatibleProperties); + const combinedNpmrc = resultLines.join('\n'); + return combinedNpmrc; +} +/** + * List of npmrc properties that are not supported by npm but may be present in the config. + * These include pnpm-specific properties and deprecated npm properties. + */ +const NPM_INCOMPATIBLE_PROPERTIES = new Set([ + // pnpm-specific hoisting configuration + 'hoist', + 'hoist-pattern', + 'public-hoist-pattern', + 'shamefully-hoist', + // Deprecated or unknown npm properties that cause warnings + 'email', + 'publish-branch' +]); +/** + * List of registry-scoped npmrc property suffixes that are pnpm-specific. + * These are properties like "//registry.example.com/:tokenHelper" where "tokenHelper" + * is the suffix after the last colon. + */ +const NPM_INCOMPATIBLE_REGISTRY_SCOPED_PROPERTIES = new Set([ + // pnpm-specific token helper properties + 'tokenHelper', + 'urlTokenHelper' +]); +/** + * Regular expression to extract property names from .npmrc lines. + * Matches everything before '=', '[', or whitespace to capture the property name. + * Note: The 'g' flag is intentionally omitted since we only need the first match. + * Examples: + * "registry=https://..." -> matches "registry" + * "hoist-pattern[]=..." -> matches "hoist-pattern" + */ +const PROPERTY_NAME_REGEX = /^([^=\[\s]+)/; +/** + * Regular expression to extract environment variable names and optional fallback values. + * Matches patterns like: + * nameString -> group 1: nameString, group 2: undefined + * nameString-fallbackString -> group 1: nameString, group 2: fallbackString + * nameString:-fallbackString -> group 1: nameString, group 2: fallbackString + */ +const ENV_VAR_WITH_FALLBACK_REGEX = /^(?[^:-]+)(?::?-(?.+))?$/; +/** + * + * @param npmrcFileLines The npmrc file's lines + * @param env The environment variables object + * @param supportEnvVarFallbackSyntax Whether to support fallback values in the form of `${VAR_NAME:-fallback}` + * @param filterNpmIncompatibleProperties Whether to filter out properties that npm doesn't understand + * @returns An array of processed npmrc file lines with undefined environment variables and npm-incompatible properties commented out + */ +function trimNpmrcFileLines(npmrcFileLines, env, supportEnvVarFallbackSyntax, filterNpmIncompatibleProperties = false) { + var _a, _b, _c; const resultLines = []; // This finds environment variable tokens that look like "${VAR_NAME}" const expansionRegExp = /\$\{([^\}]+)\}/g; // Comment lines start with "#" or ";" const commentRegExp = /^\s*[#;]/; // Trim out lines that reference environment variables that aren't defined - for (const line of npmrcFileLines) { + for (let line of npmrcFileLines) { let lineShouldBeTrimmed = false; + let trimReason = ''; + //remove spaces before or after key and value + line = line + .split('=') + .map((lineToTrim) => lineToTrim.trim()) + .join('='); // Ignore comment lines if (!commentRegExp.test(line)) { - const environmentVariables = line.match(expansionRegExp); - if (environmentVariables) { - for (const token of environmentVariables) { - // Remove the leading "${" and the trailing "}" from the token - const environmentVariableName = token.substring(2, token.length - 1); - // Is the environment variable defined? - if (!process.env[environmentVariableName]) { - // No, so trim this line - lineShouldBeTrimmed = true; - break; + // Check if this is a property that npm doesn't understand + if (filterNpmIncompatibleProperties) { + // Extract the property name (everything before the '=' or '[') + const match = line.match(PROPERTY_NAME_REGEX); + if (match) { + const propertyName = match[1]; + // Check if this is a registry-scoped property (starts with "//" like "//registry.npmjs.org/:_authToken") + const isRegistryScoped = propertyName.startsWith('//'); + if (isRegistryScoped) { + // For registry-scoped properties, check if the suffix (after the last colon) is npm-incompatible + // Example: "//registry.example.com/:tokenHelper" -> suffix is "tokenHelper" + const lastColonIndex = propertyName.lastIndexOf(':'); + if (lastColonIndex !== -1) { + const registryPropertySuffix = propertyName.substring(lastColonIndex + 1); + if (NPM_INCOMPATIBLE_REGISTRY_SCOPED_PROPERTIES.has(registryPropertySuffix)) { + lineShouldBeTrimmed = true; + trimReason = 'NPM_INCOMPATIBLE_PROPERTY'; + } + } + } + else { + // For non-registry-scoped properties, check the full property name + if (NPM_INCOMPATIBLE_PROPERTIES.has(propertyName)) { + lineShouldBeTrimmed = true; + trimReason = 'NPM_INCOMPATIBLE_PROPERTY'; + } + } + } + } + // Check for undefined environment variables + if (!lineShouldBeTrimmed) { + const environmentVariables = line.match(expansionRegExp); + if (environmentVariables) { + for (const token of environmentVariables) { + /** + * Remove the leading "${" and the trailing "}" from the token + * + * ${nameString} -> nameString + * ${nameString-fallbackString} -> name-fallbackString + * ${nameString:-fallbackString} -> name:-fallbackString + */ + const nameWithFallback = token.slice(2, -1); + let environmentVariableName; + let fallback; + if (supportEnvVarFallbackSyntax) { + /** + * Get the environment variable name and fallback value. + * + * name fallback + * nameString -> nameString undefined + * nameString-fallbackString -> nameString fallbackString + * nameString:-fallbackString -> nameString fallbackString + */ + const matched = nameWithFallback.match(ENV_VAR_WITH_FALLBACK_REGEX); + environmentVariableName = (_b = (_a = matched === null || matched === void 0 ? void 0 : matched.groups) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : nameWithFallback; + fallback = (_c = matched === null || matched === void 0 ? void 0 : matched.groups) === null || _c === void 0 ? void 0 : _c.fallback; + } + else { + environmentVariableName = nameWithFallback; + } + // Is the environment variable and fallback value defined. + if (!env[environmentVariableName] && !fallback) { + // No, so trim this line + lineShouldBeTrimmed = true; + trimReason = 'MISSING_ENVIRONMENT_VARIABLE'; + break; + } } } } } if (lineShouldBeTrimmed) { - // Example output: - // "; MISSING ENVIRONMENT VARIABLE: //my-registry.com/npm/:_authToken=${MY_AUTH_TOKEN}" - resultLines.push('; MISSING ENVIRONMENT VARIABLE: ' + line); + // Comment out the line with appropriate reason + if (trimReason === 'NPM_INCOMPATIBLE_PROPERTY') { + // Example output: + // "; UNSUPPORTED BY NPM: email=test@example.com" + resultLines.push('; UNSUPPORTED BY NPM: ' + line); + } + else { + // Example output: + // "; MISSING ENVIRONMENT VARIABLE: //my-registry.com/npm/:_authToken=${MY_AUTH_TOKEN}" + resultLines.push('; MISSING ENVIRONMENT VARIABLE: ' + line); + } } else { resultLines.push(line); } } - const combinedNpmrc = resultLines.join('\n'); - fs__WEBPACK_IMPORTED_MODULE_0__.writeFileSync(targetNpmrcPath, combinedNpmrc); + return resultLines; +} +function _copyAndTrimNpmrcFile(options) { + const { logger, sourceNpmrcPath, targetNpmrcPath } = options; + logger.info(`Transforming ${sourceNpmrcPath}`); // Verbose + logger.info(` --> "${targetNpmrcPath}"`); + const combinedNpmrc = _trimNpmrcFile(options); + node_fs__WEBPACK_IMPORTED_MODULE_0__.writeFileSync(targetNpmrcPath, combinedNpmrc); return combinedNpmrc; } -/** - * syncNpmrc() copies the .npmrc file to the target folder, and also trims unusable lines from the .npmrc file. - * If the source .npmrc file not exist, then syncNpmrc() will delete an .npmrc that is found in the target folder. - * - * IMPORTANT: THIS CODE SHOULD BE KEPT UP TO DATE WITH Utilities._syncNpmrc() - * - * @returns - * The text of the the synced .npmrc, if one exists. If one does not exist, then undefined is returned. - */ -function syncNpmrc(sourceNpmrcFolder, targetNpmrcFolder, useNpmrcPublish, logger = { - info: console.log, - error: console.error -}) { - const sourceNpmrcPath = path__WEBPACK_IMPORTED_MODULE_1__.join(sourceNpmrcFolder, !useNpmrcPublish ? '.npmrc' : '.npmrc-publish'); - const targetNpmrcPath = path__WEBPACK_IMPORTED_MODULE_1__.join(targetNpmrcFolder, '.npmrc'); +function syncNpmrc(options) { + const { sourceNpmrcFolder, targetNpmrcFolder, useNpmrcPublish, logger = { + // eslint-disable-next-line no-console + info: console.log, + // eslint-disable-next-line no-console + error: console.error + }, createIfMissing = false } = options; + const sourceNpmrcPath = node_path__WEBPACK_IMPORTED_MODULE_1__.join(sourceNpmrcFolder, !useNpmrcPublish ? '.npmrc' : '.npmrc-publish'); + const targetNpmrcPath = node_path__WEBPACK_IMPORTED_MODULE_1__.join(targetNpmrcFolder, '.npmrc'); try { - if (fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(sourceNpmrcPath)) { - return _copyAndTrimNpmrcFile(logger, sourceNpmrcPath, targetNpmrcPath); + if (node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(sourceNpmrcPath) || createIfMissing) { + // Ensure the target folder exists + if (!node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(targetNpmrcFolder)) { + node_fs__WEBPACK_IMPORTED_MODULE_0__.mkdirSync(targetNpmrcFolder, { recursive: true }); + } + return _copyAndTrimNpmrcFile({ + sourceNpmrcPath, + targetNpmrcPath, + logger, + ...options + }); } - else if (fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(targetNpmrcPath)) { + else if (node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(targetNpmrcPath)) { // If the source .npmrc doesn't exist and there is one in the target, delete the one in the target logger.info(`Deleting ${targetNpmrcPath}`); // Verbose - fs__WEBPACK_IMPORTED_MODULE_0__.unlinkSync(targetNpmrcPath); + node_fs__WEBPACK_IMPORTED_MODULE_0__.unlinkSync(targetNpmrcPath); } } catch (e) { throw new Error(`Error syncing .npmrc file: ${e}`); } } +function isVariableSetInNpmrcFile(sourceNpmrcFolder, variableKey, supportEnvVarFallbackSyntax) { + const sourceNpmrcPath = `${sourceNpmrcFolder}/.npmrc`; + //if .npmrc file does not exist, return false directly + if (!node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(sourceNpmrcPath)) { + return false; + } + const trimmedNpmrcFile = _trimNpmrcFile({ + sourceNpmrcPath, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties: false + }); + const variableKeyRegExp = new RegExp(`^${variableKey}=`, 'm'); + return trimmedNpmrcFile.match(variableKeyRegExp) !== null; +} //# sourceMappingURL=npmrcUtilities.js.map -/***/ }), +/***/ }, -/***/ 532081: -/*!********************************!*\ - !*** external "child_process" ***! - \********************************/ -/***/ ((module) => { +/***/ 731421 +/*!*************************************!*\ + !*** external "node:child_process" ***! + \*************************************/ +(module) { -module.exports = require("child_process"); +module.exports = require("node:child_process"); -/***/ }), +/***/ }, -/***/ 657147: -/*!*********************!*\ - !*** external "fs" ***! - \*********************/ -/***/ ((module) => { +/***/ 973024 +/*!**************************!*\ + !*** external "node:fs" ***! + \**************************/ +(module) { -module.exports = require("fs"); +module.exports = require("node:fs"); -/***/ }), +/***/ }, -/***/ 822037: -/*!*********************!*\ - !*** external "os" ***! - \*********************/ -/***/ ((module) => { +/***/ 848161 +/*!**************************!*\ + !*** external "node:os" ***! + \**************************/ +(module) { -module.exports = require("os"); +module.exports = require("node:os"); -/***/ }), +/***/ }, -/***/ 371017: -/*!***********************!*\ - !*** external "path" ***! - \***********************/ -/***/ ((module) => { +/***/ 176760 +/*!****************************!*\ + !*** external "node:path" ***! + \****************************/ +(module) { -module.exports = require("path"); +module.exports = require("node:path"); -/***/ }) +/***/ } /******/ }); /************************************************************************/ @@ -181,6 +369,12 @@ module.exports = require("path"); /******/ }; /******/ /******/ // Execute the module function +/******/ if (!(moduleId in __webpack_modules__)) { +/******/ delete __webpack_module_cache__[moduleId]; +/******/ var e = new Error("Cannot find module '" + moduleId + "'"); +/******/ e.code = 'MODULE_NOT_FOUND'; +/******/ throw e; +/******/ } /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module @@ -230,30 +424,33 @@ module.exports = require("path"); /******/ /************************************************************************/ var __webpack_exports__ = {}; -// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk. (() => { -/*!*******************************************!*\ - !*** ./lib-esnext/scripts/install-run.js ***! - \*******************************************/ +/*!*****************************************************!*\ + !*** ./lib-intermediate-esm/scripts/install-run.js ***! + \*****************************************************/ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "RUSH_JSON_FILENAME": () => (/* binding */ RUSH_JSON_FILENAME), -/* harmony export */ "findRushJsonFolder": () => (/* binding */ findRushJsonFolder), -/* harmony export */ "getNpmPath": () => (/* binding */ getNpmPath), -/* harmony export */ "installAndRun": () => (/* binding */ installAndRun), -/* harmony export */ "runWithErrorAndStatusCode": () => (/* binding */ runWithErrorAndStatusCode) +/* harmony export */ RUSH_JSON_FILENAME: () => (/* binding */ RUSH_JSON_FILENAME), +/* harmony export */ findRushJsonFolder: () => (/* binding */ findRushJsonFolder), +/* harmony export */ getNpmPath: () => (/* binding */ getNpmPath), +/* harmony export */ installAndRun: () => (/* binding */ installAndRun), +/* harmony export */ runWithErrorAndStatusCode: () => (/* binding */ runWithErrorAndStatusCode) /* harmony export */ }); -/* harmony import */ var child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! child_process */ 532081); -/* harmony import */ var child_process__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(child_process__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! fs */ 657147); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var os__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! os */ 822037); -/* harmony import */ var os__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(os__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! path */ 371017); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _utilities_npmrcUtilities__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../utilities/npmrcUtilities */ 679877); +/* harmony import */ var node_child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! node:child_process */ 731421); +/* harmony import */ var node_child_process__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(node_child_process__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! node:fs */ 973024); +/* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(node_fs__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var node_os__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! node:os */ 848161); +/* harmony import */ var node_os__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(node_os__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! node:path */ 176760); +/* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(node_path__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var _utilities_npmrcUtilities__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../utilities/npmrcUtilities */ 359480); +/* harmony import */ var _utilities_executionUtilities__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../utilities/executionUtilities */ 953844); // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See the @microsoft/rush package's LICENSE file for license information. +// See LICENSE in the project root for license information. +/* eslint-disable no-console */ + @@ -297,34 +494,34 @@ let _npmPath = undefined; function getNpmPath() { if (!_npmPath) { try { - if (os__WEBPACK_IMPORTED_MODULE_2__.platform() === 'win32') { + if (_utilities_executionUtilities__WEBPACK_IMPORTED_MODULE_5__.IS_WINDOWS) { // We're on Windows - const whereOutput = child_process__WEBPACK_IMPORTED_MODULE_0__.execSync('where npm', { stdio: [] }).toString(); - const lines = whereOutput.split(os__WEBPACK_IMPORTED_MODULE_2__.EOL).filter((line) => !!line); + const whereOutput = node_child_process__WEBPACK_IMPORTED_MODULE_0__.execSync('where npm', { stdio: [] }).toString(); + const lines = whereOutput.split(node_os__WEBPACK_IMPORTED_MODULE_2__.EOL).filter((line) => !!line); // take the last result, we are looking for a .cmd command // see https://github.com/microsoft/rushstack/issues/759 _npmPath = lines[lines.length - 1]; } else { // We aren't on Windows - assume we're on *NIX or Darwin - _npmPath = child_process__WEBPACK_IMPORTED_MODULE_0__.execSync('command -v npm', { stdio: [] }).toString(); + _npmPath = node_child_process__WEBPACK_IMPORTED_MODULE_0__.execSync('command -v npm', { stdio: [] }).toString(); } } catch (e) { throw new Error(`Unable to determine the path to the NPM tool: ${e}`); } _npmPath = _npmPath.trim(); - if (!fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(_npmPath)) { + if (!node_fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(_npmPath)) { throw new Error('The NPM executable does not exist'); } } return _npmPath; } function _ensureFolder(folderPath) { - if (!fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(folderPath)) { - const parentDir = path__WEBPACK_IMPORTED_MODULE_3__.dirname(folderPath); + if (!node_fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(folderPath)) { + const parentDir = node_path__WEBPACK_IMPORTED_MODULE_3__.dirname(folderPath); _ensureFolder(parentDir); - fs__WEBPACK_IMPORTED_MODULE_1__.mkdirSync(folderPath); + node_fs__WEBPACK_IMPORTED_MODULE_1__.mkdirSync(folderPath); } } /** @@ -338,14 +535,14 @@ function _ensureAndJoinPath(baseFolder, ...pathSegments) { try { for (let pathSegment of pathSegments) { pathSegment = pathSegment.replace(/[\\\/]/g, '+'); - joinedPath = path__WEBPACK_IMPORTED_MODULE_3__.join(joinedPath, pathSegment); - if (!fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(joinedPath)) { - fs__WEBPACK_IMPORTED_MODULE_1__.mkdirSync(joinedPath); + joinedPath = node_path__WEBPACK_IMPORTED_MODULE_3__.join(joinedPath, pathSegment); + if (!node_fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(joinedPath)) { + node_fs__WEBPACK_IMPORTED_MODULE_1__.mkdirSync(joinedPath); } } } catch (e) { - throw new Error(`Error building local installation folder (${path__WEBPACK_IMPORTED_MODULE_3__.join(baseFolder, ...pathSegments)}): ${e}`); + throw new Error(`Error building local installation folder (${node_path__WEBPACK_IMPORTED_MODULE_3__.join(baseFolder, ...pathSegments)}): ${e}`); } return joinedPath; } @@ -359,6 +556,23 @@ function _getRushTempFolder(rushCommonFolder) { return _ensureAndJoinPath(rushCommonFolder, 'temp'); } } +/** + * Compare version strings according to semantic versioning. + * Returns a positive integer if "a" is a later version than "b", + * a negative integer if "b" is later than "a", + * and 0 otherwise. + */ +function _compareVersionStrings(a, b) { + const aParts = a.split(/[.-]/); + const bParts = b.split(/[.-]/); + const numberOfParts = Math.max(aParts.length, bParts.length); + for (let i = 0; i < numberOfParts; i++) { + if (aParts[i] !== bParts[i]) { + return (Number(aParts[i]) || 0) - (Number(bParts[i]) || 0); + } + } + return 0; +} /** * Resolve a package specifier to a static version */ @@ -375,33 +589,54 @@ function _resolvePackageVersion(logger, rushCommonFolder, { name, version }) { // version resolves to try { const rushTempFolder = _getRushTempFolder(rushCommonFolder); - const sourceNpmrcFolder = path__WEBPACK_IMPORTED_MODULE_3__.join(rushCommonFolder, 'config', 'rush'); - (0,_utilities_npmrcUtilities__WEBPACK_IMPORTED_MODULE_4__.syncNpmrc)(sourceNpmrcFolder, rushTempFolder, undefined, logger); - const npmPath = getNpmPath(); + const sourceNpmrcFolder = node_path__WEBPACK_IMPORTED_MODULE_3__.join(rushCommonFolder, 'config', 'rush'); + (0,_utilities_npmrcUtilities__WEBPACK_IMPORTED_MODULE_4__.syncNpmrc)({ + sourceNpmrcFolder, + targetNpmrcFolder: rushTempFolder, + logger, + supportEnvVarFallbackSyntax: false, + // Always filter npm-incompatible properties in install-run scripts. + // Any warnings will be shown when running Rush commands directly. + filterNpmIncompatibleProperties: true + }); // This returns something that looks like: - // @microsoft/rush@3.0.0 '3.0.0' - // @microsoft/rush@3.0.1 '3.0.1' - // ... - // @microsoft/rush@3.0.20 '3.0.20' - // - const npmVersionSpawnResult = child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync(npmPath, ['view', `${name}@${version}`, 'version', '--no-update-notifier'], { + // ``` + // [ + // "3.0.0", + // "3.0.1", + // ... + // "3.0.20" + // ] + // ``` + // + // if multiple versions match the selector, or + // + // ``` + // "3.0.0" + // ``` + // + // if only a single version matches. + const npmVersionSpawnResult = _runNpmConfirmSuccess(['view', `${name}@${version}`, 'version', '--no-update-notifier', '--json'], { cwd: rushTempFolder, - stdio: [] - }); - if (npmVersionSpawnResult.status !== 0) { - throw new Error(`"npm view" returned error code ${npmVersionSpawnResult.status}`); - } + stdio: [], + env: process.env + }, 'npm view'); const npmViewVersionOutput = npmVersionSpawnResult.stdout.toString(); - const versionLines = npmViewVersionOutput.split('\n').filter((line) => !!line); - const latestVersion = versionLines[versionLines.length - 1]; + const parsedVersionOutput = JSON.parse(npmViewVersionOutput); + const versions = Array.isArray(parsedVersionOutput) + ? parsedVersionOutput + : [parsedVersionOutput]; + let latestVersion = versions[0]; + for (let i = 1; i < versions.length; i++) { + const latestVersionCandidate = versions[i]; + if (_compareVersionStrings(latestVersionCandidate, latestVersion) > 0) { + latestVersion = latestVersionCandidate; + } + } if (!latestVersion) { throw new Error('No versions found for the specified version range.'); } - const versionMatches = latestVersion.match(/^.+\s\'(.+)\'$/); - if (!versionMatches) { - throw new Error(`Invalid npm output ${latestVersion}`); - } - return versionMatches[1]; + return latestVersion; } catch (e) { throw new Error(`Unable to resolve version ${version} of package ${name}: ${e}`); @@ -417,17 +652,17 @@ function findRushJsonFolder() { let basePath = __dirname; let tempPath = __dirname; do { - const testRushJsonPath = path__WEBPACK_IMPORTED_MODULE_3__.join(basePath, RUSH_JSON_FILENAME); - if (fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(testRushJsonPath)) { + const testRushJsonPath = node_path__WEBPACK_IMPORTED_MODULE_3__.join(basePath, RUSH_JSON_FILENAME); + if (node_fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(testRushJsonPath)) { _rushJsonFolder = basePath; break; } else { basePath = tempPath; } - } while (basePath !== (tempPath = path__WEBPACK_IMPORTED_MODULE_3__.dirname(basePath))); // Exit the loop when we hit the disk root + } while (basePath !== (tempPath = node_path__WEBPACK_IMPORTED_MODULE_3__.dirname(basePath))); // Exit the loop when we hit the disk root if (!_rushJsonFolder) { - throw new Error('Unable to find rush.json.'); + throw new Error(`Unable to find ${RUSH_JSON_FILENAME}.`); } } return _rushJsonFolder; @@ -437,11 +672,11 @@ function findRushJsonFolder() { */ function _isPackageAlreadyInstalled(packageInstallFolder) { try { - const flagFilePath = path__WEBPACK_IMPORTED_MODULE_3__.join(packageInstallFolder, INSTALLED_FLAG_FILENAME); - if (!fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(flagFilePath)) { + const flagFilePath = node_path__WEBPACK_IMPORTED_MODULE_3__.join(packageInstallFolder, INSTALLED_FLAG_FILENAME); + if (!node_fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(flagFilePath)) { return false; } - const fileContents = fs__WEBPACK_IMPORTED_MODULE_1__.readFileSync(flagFilePath).toString(); + const fileContents = node_fs__WEBPACK_IMPORTED_MODULE_1__.readFileSync(flagFilePath).toString(); return fileContents.trim() === process.version; } catch (e) { @@ -453,7 +688,7 @@ function _isPackageAlreadyInstalled(packageInstallFolder) { */ function _deleteFile(file) { try { - fs__WEBPACK_IMPORTED_MODULE_1__.unlinkSync(file); + node_fs__WEBPACK_IMPORTED_MODULE_1__.unlinkSync(file); } catch (err) { if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') { @@ -469,19 +704,19 @@ function _deleteFile(file) { */ function _cleanInstallFolder(rushTempFolder, packageInstallFolder, lockFilePath) { try { - const flagFile = path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, INSTALLED_FLAG_FILENAME); + const flagFile = node_path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, INSTALLED_FLAG_FILENAME); _deleteFile(flagFile); - const packageLockFile = path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, 'package-lock.json'); + const packageLockFile = node_path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, 'package-lock.json'); if (lockFilePath) { - fs__WEBPACK_IMPORTED_MODULE_1__.copyFileSync(lockFilePath, packageLockFile); + node_fs__WEBPACK_IMPORTED_MODULE_1__.copyFileSync(lockFilePath, packageLockFile); } else { // Not running `npm ci`, so need to cleanup _deleteFile(packageLockFile); - const nodeModulesFolder = path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, NODE_MODULES_FOLDER_NAME); - if (fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(nodeModulesFolder)) { + const nodeModulesFolder = node_path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, NODE_MODULES_FOLDER_NAME); + if (node_fs__WEBPACK_IMPORTED_MODULE_1__.existsSync(nodeModulesFolder)) { const rushRecyclerFolder = _ensureAndJoinPath(rushTempFolder, 'rush-recycler'); - fs__WEBPACK_IMPORTED_MODULE_1__.renameSync(nodeModulesFolder, path__WEBPACK_IMPORTED_MODULE_3__.join(rushRecyclerFolder, `install-run-${Date.now().toString()}`)); + node_fs__WEBPACK_IMPORTED_MODULE_1__.renameSync(nodeModulesFolder, node_path__WEBPACK_IMPORTED_MODULE_3__.join(rushRecyclerFolder, `install-run-${Date.now().toString()}`)); } } } @@ -501,8 +736,8 @@ function _createPackageJson(packageInstallFolder, name, version) { repository: "DON'T WARN", license: 'MIT' }; - const packageJsonPath = path__WEBPACK_IMPORTED_MODULE_3__.join(packageInstallFolder, PACKAGE_JSON_FILENAME); - fs__WEBPACK_IMPORTED_MODULE_1__.writeFileSync(packageJsonPath, JSON.stringify(packageJsonContents, undefined, 2)); + const packageJsonPath = node_path__WEBPACK_IMPORTED_MODULE_3__.join(packageInstallFolder, PACKAGE_JSON_FILENAME); + node_fs__WEBPACK_IMPORTED_MODULE_1__.writeFileSync(packageJsonPath, JSON.stringify(packageJsonContents, undefined, 2)); } catch (e) { throw new Error(`Unable to create package.json: ${e}`); @@ -511,18 +746,14 @@ function _createPackageJson(packageInstallFolder, name, version) { /** * Run "npm install" in the package install folder. */ -function _installPackage(logger, packageInstallFolder, name, version, command) { +function _installPackage(logger, packageInstallFolder, name, version, npmCommand) { try { logger.info(`Installing ${name}...`); - const npmPath = getNpmPath(); - const result = child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync(npmPath, [command], { + _runNpmConfirmSuccess([npmCommand], { stdio: 'inherit', cwd: packageInstallFolder, env: process.env - }); - if (result.status !== 0) { - throw new Error(`"npm ${command}" encountered an error`); - } + }, `npm ${npmCommand}`); logger.info(`Successfully installed ${name}@${version}`); } catch (e) { @@ -533,59 +764,113 @@ function _installPackage(logger, packageInstallFolder, name, version, command) { * Get the ".bin" path for the package. */ function _getBinPath(packageInstallFolder, binName) { - const binFolderPath = path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, NODE_MODULES_FOLDER_NAME, '.bin'); - const resolvedBinName = os__WEBPACK_IMPORTED_MODULE_2__.platform() === 'win32' ? `${binName}.cmd` : binName; - return path__WEBPACK_IMPORTED_MODULE_3__.resolve(binFolderPath, resolvedBinName); + const binFolderPath = node_path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, NODE_MODULES_FOLDER_NAME, '.bin'); + const resolvedBinName = _utilities_executionUtilities__WEBPACK_IMPORTED_MODULE_5__.IS_WINDOWS ? `${binName}.cmd` : binName; + return node_path__WEBPACK_IMPORTED_MODULE_3__.resolve(binFolderPath, resolvedBinName); +} +function _buildShellCommand(command, args) { + const escapedCommand = (0,_utilities_executionUtilities__WEBPACK_IMPORTED_MODULE_5__.escapeArgumentIfNeeded)(command); + const escapedArgs = args.map((arg) => (0,_utilities_executionUtilities__WEBPACK_IMPORTED_MODULE_5__.escapeArgumentIfNeeded)(arg)); + return [escapedCommand, ...escapedArgs].join(' '); } /** * Write a flag file to the package's install directory, signifying that the install was successful. */ function _writeFlagFile(packageInstallFolder) { try { - const flagFilePath = path__WEBPACK_IMPORTED_MODULE_3__.join(packageInstallFolder, INSTALLED_FLAG_FILENAME); - fs__WEBPACK_IMPORTED_MODULE_1__.writeFileSync(flagFilePath, process.version); + const flagFilePath = node_path__WEBPACK_IMPORTED_MODULE_3__.join(packageInstallFolder, INSTALLED_FLAG_FILENAME); + node_fs__WEBPACK_IMPORTED_MODULE_1__.writeFileSync(flagFilePath, process.version); } catch (e) { throw new Error(`Unable to create installed.flag file in ${packageInstallFolder}`); } } +/** + * Run npm under the platform's shell and throw if it didn't succeed. + */ +function _runNpmConfirmSuccess(args, options, commandNameForLogging) { + const command = getNpmPath(); + let result; + if (_utilities_executionUtilities__WEBPACK_IMPORTED_MODULE_5__.IS_WINDOWS) { + result = node_child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync(_buildShellCommand(command, args), { + ...options, + shell: true, + windowsVerbatimArguments: false + }); + } + else { + result = node_child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync(command, args, options); + } + if (result.status !== 0) { + if (!result.status) { + // Is status null or undefined? + if (result.error) { + throw new Error(`"${commandNameForLogging}" failed: ${result.error.message.toString()}`); + } + else if (result.signal) { + throw new Error(`"${commandNameForLogging}" was terminated by signal: ${result.signal}`); + } + else { + throw new Error(`"${commandNameForLogging}" failed for an unknown reason`); + } + } + else { + throw new Error(`"${commandNameForLogging}" returned error code ${result.status}`); + } + } + return result; +} function installAndRun(logger, packageName, packageVersion, packageBinName, packageBinArgs, lockFilePath = process.env[INSTALL_RUN_LOCKFILE_PATH_VARIABLE]) { const rushJsonFolder = findRushJsonFolder(); - const rushCommonFolder = path__WEBPACK_IMPORTED_MODULE_3__.join(rushJsonFolder, 'common'); + const rushCommonFolder = node_path__WEBPACK_IMPORTED_MODULE_3__.join(rushJsonFolder, 'common'); const rushTempFolder = _getRushTempFolder(rushCommonFolder); const packageInstallFolder = _ensureAndJoinPath(rushTempFolder, 'install-run', `${packageName}@${packageVersion}`); if (!_isPackageAlreadyInstalled(packageInstallFolder)) { // The package isn't already installed _cleanInstallFolder(rushTempFolder, packageInstallFolder, lockFilePath); - const sourceNpmrcFolder = path__WEBPACK_IMPORTED_MODULE_3__.join(rushCommonFolder, 'config', 'rush'); - (0,_utilities_npmrcUtilities__WEBPACK_IMPORTED_MODULE_4__.syncNpmrc)(sourceNpmrcFolder, packageInstallFolder, undefined, logger); + const sourceNpmrcFolder = node_path__WEBPACK_IMPORTED_MODULE_3__.join(rushCommonFolder, 'config', 'rush'); + (0,_utilities_npmrcUtilities__WEBPACK_IMPORTED_MODULE_4__.syncNpmrc)({ + sourceNpmrcFolder, + targetNpmrcFolder: packageInstallFolder, + logger, + supportEnvVarFallbackSyntax: false, + // Always filter npm-incompatible properties in install-run scripts. + // Any warnings will be shown when running Rush commands directly. + filterNpmIncompatibleProperties: true + }); _createPackageJson(packageInstallFolder, packageName, packageVersion); - const command = lockFilePath ? 'ci' : 'install'; - _installPackage(logger, packageInstallFolder, packageName, packageVersion, command); + const installCommand = lockFilePath ? 'ci' : 'install'; + _installPackage(logger, packageInstallFolder, packageName, packageVersion, installCommand); _writeFlagFile(packageInstallFolder); } const statusMessage = `Invoking "${packageBinName} ${packageBinArgs.join(' ')}"`; const statusMessageLine = new Array(statusMessage.length + 1).join('-'); logger.info('\n' + statusMessage + '\n' + statusMessageLine + '\n'); const binPath = _getBinPath(packageInstallFolder, packageBinName); - const binFolderPath = path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, NODE_MODULES_FOLDER_NAME, '.bin'); + const binFolderPath = node_path__WEBPACK_IMPORTED_MODULE_3__.resolve(packageInstallFolder, NODE_MODULES_FOLDER_NAME, '.bin'); // Windows environment variables are case-insensitive. Instead of using SpawnSyncOptions.env, we need to // assign via the process.env proxy to ensure that we append to the right PATH key. const originalEnvPath = process.env.PATH || ''; let result; try { - // Node.js on Windows can not spawn a file when the path has a space on it - // unless the path gets wrapped in a cmd friendly way and shell mode is used - const shouldUseShell = binPath.includes(' ') && os__WEBPACK_IMPORTED_MODULE_2__.platform() === 'win32'; - const platformBinPath = shouldUseShell ? `"${binPath}"` : binPath; - process.env.PATH = [binFolderPath, originalEnvPath].join(path__WEBPACK_IMPORTED_MODULE_3__.delimiter); - result = child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync(platformBinPath, packageBinArgs, { + process.env.PATH = [binFolderPath, originalEnvPath].join(node_path__WEBPACK_IMPORTED_MODULE_3__.delimiter); + const spawnOptions = { stdio: 'inherit', - windowsVerbatimArguments: false, - shell: shouldUseShell, cwd: process.cwd(), env: process.env - }); + }; + if (_utilities_executionUtilities__WEBPACK_IMPORTED_MODULE_5__.IS_WINDOWS) { + result = node_child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync(_buildShellCommand(binPath, packageBinArgs), { + ...spawnOptions, + windowsVerbatimArguments: false, + // `npm` bin stubs on Windows are `.cmd` files + // Node.js will not directly invoke a `.cmd` file unless `shell` is set to `true` + shell: true + }); + } + else { + result = node_child_process__WEBPACK_IMPORTED_MODULE_0__.spawnSync(binPath, packageBinArgs, spawnOptions); + } } finally { process.env.PATH = originalEnvPath; @@ -610,9 +895,10 @@ function runWithErrorAndStatusCode(logger, fn) { function _run() { const [nodePath /* Ex: /bin/node */, scriptPath /* /repo/common/scripts/install-run-rush.js */, rawPackageSpecifier /* qrcode@^1.2.0 */, packageBinName /* qrcode */, ...packageBinArgs /* [-f, myproject/lib] */] = process.argv; if (!nodePath) { - throw new Error('Unexpected exception: could not detect node path'); + throw new Error('Could not detect node path'); } - if (path__WEBPACK_IMPORTED_MODULE_3__.basename(scriptPath).toLowerCase() !== 'install-run.js') { + const scriptFileName = node_path__WEBPACK_IMPORTED_MODULE_3__.basename(scriptPath).toLowerCase(); + if (scriptFileName !== 'install-run.js' && scriptFileName !== 'install-run') { // If install-run.js wasn't directly invoked, don't execute the rest of this function. Return control // to the script that (presumably) imported this file return; diff --git a/docs/rush/phased-commands.md b/docs/rush/phased-commands.md new file mode 100644 index 00000000000..9a4908d53fd --- /dev/null +++ b/docs/rush/phased-commands.md @@ -0,0 +1,436 @@ +# Phased Command Execution and Plugin Architecture + +This document describes the architecture for Rush's phased command system, including how commands execute, how the operation graph is managed, and how plugins can hook into the process. + +## Overview + +A **phased command** (e.g. `rush build`, `rush start`) runs a set of **operations** across projects, potentially in parallel, potentially in **watch mode** where the command runs indefinitely and re-executes operations when source files change. + +The key classes involved are: + +- `PhasedScriptAction` — parses CLI args, orchestrates the command lifecycle, owns `PhasedCommandHooks` +- `OperationGraph` — manages the stateful execution of operations across the entire session +- `ProjectWatcher` — drives watch mode by observing file system changes and scheduling new iterations +- `PhasedCommandHooks` — the plugin API surface exposed to Rush plugins via `runAnyPhasedCommand` / `runPhasedCommand` + +--- + +## Command Lifecycle + +### 1. Plugin registration + +Before any work begins, `PhasedScriptAction.runAsync()` applies built-in plugins and fires session hooks: + +```text +hooks.runAnyPhasedCommand → hooks.runPhasedCommand[actionName] +``` + +Built-in plugins applied (in order): + +1. `PhasedOperationPlugin` — generates the default operation graph from phases and project selection +2. `ShardedPhasedOperationPlugin` — splices in sharded phases +3. `ShellOperationRunnerPlugin` — assigns `ShellOperationRunner` to operations with scripts +4. `ValidateOperationsPlugin` — validates `rush-project.json` entries +5. Conditional plugins: `ConsoleTimelinePlugin`, `NodeDiagnosticDirPlugin`, `OperationResultSummarizerPlugin` +6. Cache plugins (one of): `CacheableOperationPlugin`, `LegacySkipPlugin`, or none (full rebuild) +7. `IPCOperationRunnerPlugin` — in watch mode, enables long-running processes via IPC + +### 2. Graph creation (`createOperationsAsync`) + +`PhasedCommandHooks.createOperationsAsync` is fired with an empty `Set` and an `ICreateOperationsContext`. Each tap in the waterfall may add, remove, or mutate operations. **This hook is invoked exactly once per session**, regardless of how many watch iterations occur. The resulting `Operation` objects are reused for the entire lifetime of the session. Operations cannot be added to or removed from the graph after this hook completes. + +**`ICreateOperationsContext` fields:** + +| Field | Description | +| --- | --- | +| `buildCacheConfiguration` | Build cache config, if enabled | +| `changedProjectsOnly` | Whether `--changed-projects-only` was passed | +| `cobuildConfiguration` | Cobuild config, if enabled | +| `customParameters` | Map of longName → CLI parameter | +| `includePhaseDeps` | Whether phase dependencies are auto-included | +| `isIncrementalBuildAllowed` | False for `rush rebuild` | +| `isWatch` | True if running in watch mode | +| `parallelism` | Configured max parallelism | +| `phaseSelection` | Set of phases to run | +| `projectConfigurations` | Loaded `rush-project.json` data | +| `projectSelection` | Set of selected projects | +| `generateFullGraph` | True when `includeAllProjectsInWatchGraph` is set and in watch mode | +| `rushConfiguration` | The Rush configuration | + +### 3. OperationGraph construction + +After `createOperationsAsync`, Rush constructs an `OperationGraph` from the resulting operations and fires: + +```text +PhasedCommandHooks.onGraphCreatedAsync(operationGraph: IOperationGraph, context: IOperationGraphContext) +``` + +`IOperationGraphContext` extends `ICreateOperationsContext` with: + +- `initialSnapshot?: IInputsSnapshot` — the current file system state (used to seed incremental build detection) + +Plugins that tap `onGraphCreatedAsync` should register their hooks on `operationGraph.hooks` (`OperationGraphHooks`) for all per-iteration and per-operation behavior. + +### 4. Execution + +**Non-watch mode:** `graph.executeAsync(iterationOptions)` returns a `Promise` that resolves when the single iteration has finished. `IExecutionResult` contains: + +- `status: OperationStatus` — the overall outcome (`Success`, `Failure`, `NoOp`, etc.) +- `operationResults: ReadonlyMap` — per-operation results for the iteration + +**Watch mode:** `graph.executeAsync(iterationOptions)` returns a `Promise` for the **initial** iteration only. After that promise resolves, a `ProjectWatcher` observes file system changes. When changes are detected (after a debounce), `ProjectWatcher` calls `graph.scheduleIterationAsync(...)`, which queues a new iteration. Subsequent iterations are driven internally and their results are available via `graph.resultByOperation`. After each iteration completes with no queued work, `graph.hooks.onIdle` fires and the graph enters an idle state until the next change. + +--- + +## OperationGraphHooks + +All hooks in `OperationGraphHooks` are accessible via `operationGraph.hooks`. These are registered during `onGraphCreatedAsync`. + +### Per-iteration hooks + +#### `configureIteration` (Sync) + +```ts +SyncHook<[ + ReadonlyMap, // initialRecords — mutable enabled state + ReadonlyMap, // lastExecutedRecords — results from prior run + IOperationGraphIterationOptions +]> +``` + +Called synchronously before an iteration is queued. Use this to enable/disable operations based on which projects changed. **Must be synchronous** — the graph may be mid-execution when this fires and the `lastExecutedRecords` map must remain stable. + +When `lastExecutedRecords` is empty, this is the first iteration of the session. An operation has no entry in `lastExecutedRecords` if it has never reached a completed terminal state (`Success`, `SuccessWithWarning`, `Failure`, `FromCache`, or `NoOp`) — for example if it was `Aborted`, `Blocked`, or `Skipped` in all prior iterations. + +#### `onIterationScheduled` (Sync) + +```ts +SyncHook<[ReadonlyMap]> +``` + +Fires after an iteration is scheduled but before any operations execute. Useful for snapshotting planned work or pre-computing auxiliary data (e.g. dashboard rendering). + +#### `beforeExecuteIterationAsync` (AsyncSeriesBail) + +```ts +AsyncSeriesBailHook< + [ReadonlyMap, IOperationGraphIterationOptions], + OperationStatus | undefined | void +> +``` + +Fires at the start of executing a scheduled iteration. If any tap returns an `OperationStatus`, the remaining taps are skipped and the iteration ends immediately with that status; all non-started operations are marked `Aborted`. + +#### `afterExecuteIterationAsync` (AsyncSeriesWaterfall) + +```ts +AsyncSeriesWaterfallHook<[ + OperationStatus, + ReadonlyMap, + IOperationGraphIterationOptions +]> +``` + +Fires after all operations in an iteration have reached a final state. Taps may modify the `OperationStatus` that is returned. + +#### `onIdle` (Sync) + +Fires when the graph is idle and watching for file changes. Only relevant in watch mode. + +### Per-operation hooks + +#### `beforeExecuteOperationAsync` (AsyncSeriesBail) + +```ts +AsyncSeriesBailHook< + [IOperationRunnerContext & IOperationExecutionResult], + OperationStatus | undefined +> +``` + +Fires before executing a single operation. If a tap returns a status, the runner is skipped and the operation is assigned that status (used by cache plugins to short-circuit with `FromCache`). + +#### `afterExecuteOperationAsync` (AsyncSeries) + +```ts +AsyncSeriesHook<[IOperationRunnerContext & IOperationExecutionResult]> +``` + +Fires after a single operation completes. + +#### `createEnvironmentForOperation` (SyncWaterfall) + +```ts +SyncWaterfallHook<[IEnvironment, IOperationRunnerContext & IOperationExecutionResult]> +``` + +Called to construct the environment variables passed to the operation's shell runner. Taps can add, remove, or override environment variables. + +### State change hooks + +#### `onExecutionStatesUpdated` (Sync) + +```ts +SyncHook<[ReadonlySet]> +``` + +Batched hook invoked when one or more operation statuses change within the same microtask. Rather than firing once per individual status change, all changes that occur within a single microtask are collected into one set and delivered together. This reduces redundant renders or notifications when many operations update simultaneously (e.g. when a batch of operations are marked `Blocked` after an upstream failure). + +#### `onEnableStatesChanged` (Sync) + +```ts +SyncHook<[ReadonlySet]> +``` + +Fires when `IOperationGraph.setEnabledStates()` changes the `enabled` flag on any operations. + +#### `onGraphStateChanged` (Sync) + +```ts +SyncHook<[IOperationGraph]> +``` + +Fires when any observable property of the graph changes (parallelism, quiet/debug mode, `pauseNextIteration`, status, scheduled iteration availability). Used to drive reactive UIs. + +#### `onInvalidateOperations` (Sync) + +```ts +SyncHook<[Iterable, string | undefined]> +``` + +Fires when `IOperationGraph.invalidateOperations()` marks operations as `Ready` for re-execution. + +--- + +## IOperationGraph API + +`IOperationGraph` (implemented by `OperationGraph`) is the main handle for plugins to interact with the execution session at runtime. + +### Configuration properties + +| Property | Description | +| --- | --- | +| `parallelism` | Max concurrent operations (writable) | +| `debugMode` | Verbose debug output (writable) | +| `quietMode` | Suppress per-operation output except errors (writable) | +| `pauseNextIteration` | When true, scheduled iterations will not auto-execute (writable) | + +### Read-only state + +| Property | Description | +| --- | --- | +| `operations` | All operations in the graph (session-long set) | +| `resultByOperation` | Per-operation result records, updated live as each operation executes | +| `status` | Overall execution status (`Ready`, `Executing`, `Success`, `Failure`, etc.) | +| `hasScheduledIteration` | True if an iteration is queued but not yet running | +| `abortController` | Session-level `AbortController`; abort this to terminate watch mode | + +### Methods + +| Method | Description | +| --- | --- | +| `scheduleIterationAsync(options)` | Queue a new iteration; returns `true` if scheduled, `false` if nothing to do | +| `executeScheduledIterationAsync()` | Execute the currently queued iteration | +| `executeAsync(options)` | Convenience: schedule + execute in one call; returns `Promise` with `status` and `operationResults` | +| `abortCurrentIterationAsync()` | Cancel the in-flight iteration | +| `invalidateOperations(operations?, reason?)` | Mark operations as needing re-execution | +| `setEnabledStates(operations, targetState, mode)` | Enable or disable operations (`'safe'` mode respects dependencies) | +| `closeRunnersAsync(operations?)` | Dispose long-running IPC runners | +| `addTerminalDestination(dest)` | Attach an additional `TerminalWritable` for output | +| `removeTerminalDestination(dest, close?)` | Detach a terminal destination | + +--- + +## Watch Mode — `includeAllProjectsInWatchGraph` + +When the `watchOptions.includeAllProjectsInWatchGraph` flag is set to `true` in `command-line.json`, Rush builds the operation graph with **all projects** in `rush.json`, not just the CLI-selected subset. The `generateFullGraph` property on `ICreateOperationsContext` reflects this. + +This enables plugins (such as `rush-serve-plugin`) to dynamically enable/disable individual operations in the graph during the watch session by calling `IOperationGraph.setEnabledStates()`. For example, an HTTP server or WebSocket endpoint can receive a message saying "enable project X" and call `setEnabledStates` to bring it into the build graph without needing to restart the watch session. + +--- + +## IOperationRunner + +Each `Operation` carries a `runner: IOperationRunner` that performs the actual work. In watch mode the same runner instance is called once per iteration for the lifetime of the session, so runners must be written to handle multiple invocations. + +### `executeAsync(context, lastState?)` + +```ts +executeAsync(context: IOperationRunnerContext, lastState?: {}): Promise +``` + +`lastState` is the `IOperationExecutionResult` from the most recent iteration in which this operation **reached a completed terminal state** (`Success`, `SuccessWithWarning`, `Failure`, `FromCache`, or `NoOp`). It is `undefined` when: + +- This is the first time the runner has ever been called, or +- Every prior iteration either did not reach this operation (aborted before execution began) or left it in a non-completing state (`Skipped`, `Blocked`, `Aborted`). + +Runners use `lastState` to choose between a full initial build and an incremental build: + +```ts +async executeAsync(context: IOperationRunnerContext, lastState?: {}): Promise { + if (lastState === undefined) { + // No completed prior result — run full build + } else { + // Prior result exists; check lastState.status if the outcome matters + // e.g. re-run full build if the previous run failed + } +} +``` + +`ShellOperationRunner` uses this to select between the `initialCommand` and `incrementalCommand` scripts defined in `rush-project.json`. + +### `isActive` + +```ts +readonly isActive?: boolean; +``` + +Optional. Set to `true` while the runner owns a live background resource (e.g. a running dev server or file watcher). Tooling such as the live dashboard uses this to represent an operation as "in progress" even when it is not currently executing an iteration. The runner is responsible for updating this property as the resource starts and stops. + +### `closeAsync` + +```ts +closeAsync?(): Promise; +``` + +Optional. Called by `IOperationGraph.closeRunnersAsync()` when the session ends (triggered by the `abortController` abort signal). Must be **idempotent** — it may be called before any `executeAsync` call has occurred, or after the runner has already been closed. Failing to implement it defensively can cause errors during watch-mode teardown. + +### Long-lived runner example + +```ts +class MyWatchRunner implements IOperationRunner { + readonly name: string; + cacheable = false; + reportTiming = true; + silent = false; + warningsAreAllowed = false; + + private _server: MyServer | undefined; + + get isActive(): boolean { + return this._server !== undefined; + } + + async executeAsync(context: IOperationRunnerContext, lastState?: {}): Promise { + if (!this._server) { + this._server = await MyServer.startAsync(); + } else { + await this._server.reloadAsync(); + } + return OperationStatus.Success; + } + + getConfigHash(): string { return ''; } + + async closeAsync(): Promise { + await this._server?.stopAsync(); + this._server = undefined; + } +} +``` + +--- + +## Plugin patterns + +### Session context in graph hooks + +Session-scoped data from `IOperationGraphContext` (build cache config, project selection, phase selection, etc.) is available in the `onGraphCreatedAsync` callback and can be captured by closure for use in all graph hooks registered within it: + +```ts +hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph, context) => { + const { buildCacheConfiguration, projectSelection } = context; + + graph.hooks.beforeExecuteIterationAsync.tapPromise(PLUGIN_NAME, async (records, iterationOptions) => { + // buildCacheConfiguration and projectSelection available via closure + }); +}); +``` + +### Self-invalidation from runners + +Long-lived runners (e.g. file watchers, IPC processes) that detect stale outputs between iterations can request re-execution via `context.getInvalidateCallback()`. This returns a `(reason: string) => void` callback that delegates to `IOperationGraph.invalidateOperations()` under the hood, marking the operation as `Ready` and scheduling a new iteration. The returned callback captures only the minimal state needed, so callers should store it rather than retaining the full context. + +Because `context` is always available (not just after the first completed run), runners can obtain the callback on their very first execution — there is no need to wait for a previous result. + +```ts +class MyWatchRunner implements IOperationRunner { + async executeAsync(context: IOperationRunnerContext, lastState?: {}): Promise { + const invalidate = context.getInvalidateCallback(); + // ... do work, set up watchers that call invalidate('files changed') + return OperationStatus.Success; + } +} +``` + +### Session teardown + +To run cleanup when the watch session ends, listen to the abort signal on the graph: + +```ts +hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph) => { + graph.abortController.signal.addEventListener('abort', () => { + void myResource.dispose(); + }, { once: true }); +}); +``` + +Note that there is no built-in mechanism to await parallel teardown tasks before the process exits. `graph.closeRunnersAsync()` awaits all runner `closeAsync` calls, but other async cleanup must be coordinated manually. + +--- + +## Plugin example + +```ts +import type { IPhasedCommandPlugin, PhasedCommandHooks } from '@microsoft/rush-lib'; + +export class MyPlugin implements IPhasedCommandPlugin { + apply(hooks: PhasedCommandHooks): void { + // Step 1: Add operations to the graph + hooks.createOperationsAsync.tapPromise('MyPlugin', async (operations, context) => { + // Inspect context, optionally add new Operations + return operations; + }); + + // Step 2: Tap into the graph after it is created + hooks.onGraphCreatedAsync.tapPromise('MyPlugin', async (graph, context) => { + // Configure per-iteration behavior + graph.hooks.configureIteration.tap('MyPlugin', (initialRecords, lastResults, iterationContext) => { + // Enable/disable operations based on what changed + }); + + graph.hooks.beforeExecuteIterationAsync.tapPromise('MyPlugin', async (results, iterationContext) => { + // Optionally short-circuit the iteration + }); + + graph.hooks.afterExecuteIterationAsync.tapPromise('MyPlugin', async (status, results, context) => { + // Post-iteration reporting + return status; + }); + + graph.hooks.onIdle.tap('MyPlugin', () => { + // Display idle status + }); + + graph.hooks.onGraphStateChanged.tap('MyPlugin', (operationGraph) => { + // React to property changes (good for live dashboards) + }); + }); + } +} +``` + +--- + +## Key source files + +| File | Description | +| --- | --- | +| `libraries/rush-lib/src/pluginFramework/PhasedCommandHooks.ts` | All hook and interface definitions for phased commands | +| `libraries/rush-lib/src/logic/operations/OperationGraph.ts` | `OperationGraph` implementation (`IOperationGraph`) | +| `libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts` | Command entry point; orchestrates the full lifecycle | +| `libraries/rush-lib/src/logic/ProjectWatcher.ts` | File system watcher; drives watch mode iterations | +| `libraries/rush-lib/src/logic/operations/Operation.ts` | `Operation` node in the graph | +| `libraries/rush-lib/src/logic/operations/OperationExecutionRecord.ts` | Per-iteration execution state for an operation | +| `libraries/rush-lib/src/logic/operations/IOperationExecutionResult.ts` | Result interfaces | +| `libraries/rush-lib/src/logic/operations/IOperationRunner.ts` | `IOperationRunner` interface — `executeAsync`, `lastState`, `isActive`, `closeAsync` | diff --git a/docs/rush/plugin-migration-guide.md b/docs/rush/plugin-migration-guide.md new file mode 100644 index 00000000000..5ea09366bb3 --- /dev/null +++ b/docs/rush/plugin-migration-guide.md @@ -0,0 +1,419 @@ +# Rush Plugin Migration Guide: Phased Command Hooks + +This guide covers the breaking changes to the Rush plugin API for phased commands introduced in the watch-mode overhaul. The execution engine is now stateful across an entire Rush watch session, and the hook surface has been reorganized accordingly. + +## Summary of changes + +| Old (removed) | New | +| --- | --- | +| `PhasedCommandHooks.createOperations` | `PhasedCommandHooks.createOperationsAsync` | +| `PhasedCommandHooks.beforeExecuteOperations` | `operationGraph.hooks.beforeExecuteIterationAsync` | +| `PhasedCommandHooks.afterExecuteOperations` | `operationGraph.hooks.afterExecuteIterationAsync` | +| `PhasedCommandHooks.onOperationStatusChanged` | `operationGraph.hooks.onExecutionStatesUpdated` | +| `PhasedCommandHooks.beforeExecuteOperation` | `operationGraph.hooks.beforeExecuteOperationAsync` | +| `PhasedCommandHooks.afterExecuteOperation` | `operationGraph.hooks.afterExecuteOperationAsync` | +| `PhasedCommandHooks.createEnvironmentForOperation` | `operationGraph.hooks.createEnvironmentForOperation` | +| `PhasedCommandHooks.waitingForChanges` | `operationGraph.hooks.onIdle` | +| `PhasedCommandHooks.shutdownAsync` | `IOperationGraph.abortController` signal + `closeRunnersAsync()` | +| `PhasedCommandHooks.beforeLog` | `operationGraph.hooks.beforeLog` | +| `IExecuteOperationsContext` | `IOperationGraphIterationOptions` (iteration scope) + `IOperationGraphContext` (session scope) | +| `WeightedOperationPlugin` | Removed — weight assignment is now part of `PhasedOperationPlugin` | + +--- + +## Core migration pattern + +Previously, all hooks were tapped directly on the `PhasedCommandHooks` object passed to `apply()`. All per-iteration and per-operation hooks have moved to a new `OperationGraphHooks` class that lives on the `IOperationGraph` object, which is created once per session and persists across watch iterations. + +The new entry point for these hooks is `PhasedCommandHooks.onGraphCreatedAsync`. Register all graph-level hook taps inside this callback. + +### Before + +```typescript +export class MyPlugin implements IPhasedCommandPlugin { + apply(hooks: PhasedCommandHooks): void { + hooks.createOperations.tapPromise(PLUGIN_NAME, async (operations, context) => { + // mutate operations + return operations; + }); + + hooks.beforeExecuteOperations.tapPromise(PLUGIN_NAME, async (records, context) => { + // runs before each iteration + }); + + hooks.afterExecuteOperations.tapPromise(PLUGIN_NAME, async (result, context) => { + // runs after each iteration + }); + + hooks.beforeExecuteOperation.tapPromise(PLUGIN_NAME, async (record) => { + // runs before a single operation + return undefined; + }); + + hooks.afterExecuteOperation.tapPromise(PLUGIN_NAME, async (record) => { + // runs after a single operation + }); + + hooks.waitingForChanges.tap(PLUGIN_NAME, () => { + // watch mode idle + }); + } +} +``` + +### After + +```typescript +export class MyPlugin implements IPhasedCommandPlugin { + apply(hooks: PhasedCommandHooks): void { + hooks.createOperationsAsync.tapPromise(PLUGIN_NAME, async (operations, context) => { + // mutate operations + return operations; + }); + + hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph, context) => { + graph.hooks.beforeExecuteIterationAsync.tapPromise(PLUGIN_NAME, async (records, iterationContext) => { + // runs before each iteration + }); + + graph.hooks.afterExecuteIterationAsync.tapPromise(PLUGIN_NAME, async (status, records, iterationContext) => { + // runs after each iteration + return status; + }); + + graph.hooks.beforeExecuteOperationAsync.tapPromise(PLUGIN_NAME, async (record) => { + // runs before a single operation + return undefined; + }); + + graph.hooks.afterExecuteOperationAsync.tapPromise(PLUGIN_NAME, async (record) => { + // runs after a single operation + }); + + graph.hooks.onIdle.tap(PLUGIN_NAME, () => { + // watch mode idle + }); + }); + } +} +``` + +--- + +## Hook-by-hook migration + +### `createOperations` → `createOperationsAsync` + +The hook is now async-only and has been renamed to reflect this. + +**Critically, `createOperationsAsync` is now invoked exactly once per session**, regardless of how many watch iterations occur. Previously, each watch iteration called `createOperations` anew; now the same set of `Operation` objects is reused for the entire session. Plugins must not assume the hook will fire again after the initial call. Any per-iteration logic that was previously placed in `createOperations` (e.g. inspecting `isInitial` or `projectsInUnknownState`) must move to `operationGraph.hooks.configureIteration`. + +```typescript +// Before +hooks.createOperations.tap(PLUGIN_NAME, (operations, context) => { + return operations; +}); + +// After +hooks.createOperationsAsync.tap(PLUGIN_NAME, (operations, context) => { + return operations; +}); +// or async: +hooks.createOperationsAsync.tapPromise(PLUGIN_NAME, async (operations, context) => { + return operations; +}); +``` + +### `beforeExecuteOperations` → `operationGraph.hooks.beforeExecuteIterationAsync` + +The context parameter has changed type. `IExecuteOperationsContext` is replaced by `IOperationGraphIterationOptions`, which only carries the iteration-scoped data (`inputsSnapshot` and `startTime`). Session-scoped data (build cache config, project selection, etc.) is available via closure from the `onGraphCreatedAsync` callback parameter. + +The return signature of `beforeExecuteIterationAsync` is a bail hook: returning an `OperationStatus` short-circuits the iteration immediately. + +```typescript +// Before +hooks.beforeExecuteOperations.tapPromise(PLUGIN_NAME, async (records, context) => { + const { inputsSnapshot } = context; + // context also had: isInitial, projectsInUnknownState, etc. +}); + +// After +hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph, sessionContext) => { + graph.hooks.beforeExecuteIterationAsync.tapPromise( + PLUGIN_NAME, + async (records, iterationOptions) => { + const { inputsSnapshot } = iterationOptions; + // sessionContext has: buildCacheConfiguration, projectSelection, etc. + // Return an OperationStatus to abort the iteration early, or return undefined to continue. + return undefined; + } + ); +}); +``` + +### `afterExecuteOperations` → `operationGraph.hooks.afterExecuteIterationAsync` + +The result parameter changed from `IExecutionResult` (an object with `status` and `operationResults`) to two separate parameters. The hook is now a waterfall on the status value. + +```typescript +// Before +hooks.afterExecuteOperations.tapPromise(PLUGIN_NAME, async (result, context) => { + const { status, operationResults } = result; +}); + +// After +hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph, context) => { + graph.hooks.afterExecuteIterationAsync.tapPromise( + PLUGIN_NAME, + async (status, operationResults, iterationOptions) => { + // Must return the (possibly modified) status + return status; + } + ); +}); +``` + +### `onOperationStatusChanged` → `operationGraph.hooks.onExecutionStatesUpdated` + +The old hook fired once per individual status change. The new hook is **batched**: it fires once per microtask with all changes that occurred within that tick, reducing unnecessary renders or notifications when many operations update simultaneously. + +```typescript +// Before — fires per individual change +hooks.onOperationStatusChanged.tap(PLUGIN_NAME, (record) => { + refreshUI(record); +}); + +// After — fires with a set of all changes in a microtask +hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph) => { + graph.hooks.onExecutionStatesUpdated.tap(PLUGIN_NAME, (changedRecords) => { + for (const record of changedRecords) { + refreshUI(record); + } + }); +}); +``` + +### `beforeExecuteOperation` → `operationGraph.hooks.beforeExecuteOperationAsync` + +The hook has moved to the graph and gained the `Async` suffix to match naming conventions. + +```typescript +// Before +hooks.beforeExecuteOperation.tapPromise(PLUGIN_NAME, async (record) => { + return OperationStatus.FromCache; // short-circuit +}); + +// After +hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph) => { + graph.hooks.beforeExecuteOperationAsync.tapPromise(PLUGIN_NAME, async (record) => { + return OperationStatus.FromCache; // short-circuit + }); +}); +``` + +### `afterExecuteOperation` → `operationGraph.hooks.afterExecuteOperationAsync` + +The hook has moved to the graph and gained the `Async` suffix. + +```typescript +// Before +hooks.afterExecuteOperation.tapPromise(PLUGIN_NAME, async (record) => { }); + +// After +hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph) => { + graph.hooks.afterExecuteOperationAsync.tapPromise(PLUGIN_NAME, async (record) => { }); +}); +``` + +### `createEnvironmentForOperation` → `operationGraph.hooks.createEnvironmentForOperation` + +The hook is now on the graph rather than on `PhasedCommandHooks`. + +```typescript +// Before +hooks.createEnvironmentForOperation.tap(PLUGIN_NAME, (env, record) => { + return { ...env, MY_VAR: 'value' }; +}); + +// After +hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph) => { + graph.hooks.createEnvironmentForOperation.tap(PLUGIN_NAME, (env, record) => { + return { ...env, MY_VAR: 'value' }; + }); +}); +``` + +### `waitingForChanges` → `operationGraph.hooks.onIdle` + +The hook has moved to the graph and been renamed with the standard `on` prefix. + +```typescript +// Before +hooks.waitingForChanges.tap(PLUGIN_NAME, () => { }); + +// After +hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph) => { + graph.hooks.onIdle.tap(PLUGIN_NAME, () => { }); +}); +``` + +### `shutdownAsync` → `IOperationGraph.abortController` + `closeRunnersAsync()` + +The old `shutdownAsync` parallel hook had no direct equivalent. Shutdown is now signalled through the `AbortController` on the graph. To run cleanup logic when the session ends, listen to the abort signal. To shut down long-running runners (e.g. watch-mode IPC processes), call `graph.closeRunnersAsync()`. + +```typescript +// Before +hooks.shutdownAsync.tapPromise(PLUGIN_NAME, async () => { + await myResource.dispose(); +}); + +// After +hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph) => { + graph.abortController.signal.addEventListener('abort', () => { + void myResource.dispose(); + }, { once: true }); +}); +``` + +--- + +## `ICreateOperationsContext` — removed fields + +Several fields that were on `ICreateOperationsContext` have been removed. Some have direct replacements; others reflect capabilities that no longer exist in the same form. + +| Removed field | Notes | +| --- | --- | +| `isInitial` | Removed. Previously `true` on the first run, `false` on subsequent watch iterations. Because `createOperationsAsync` is now invoked only once per session, the distinction between initial and non-initial is tracked differently — see below. | +| `projectsInUnknownState` | Removed. Previously the set of projects with changed or unknown inputs. This information is now computed per-iteration inside `configureIteration` via `IInputsSnapshot` comparisons. | +| `phaseOriginal` | Removed. Was the pre-expansion set of phases. The watch phases and initial phases are now determined by the command configuration and are not exposed on the context. | +| `invalidateOperation` | Removed. Runners should use `context.getInvalidateCallback()` from `executeAsync`. Plugins can use `IOperationGraph.invalidateOperations()`, available via the graph reference from `onGraphCreatedAsync`. | + +--- + +## Features that are no longer possible + +### Distinguishing initial vs. subsequent runs + +`ICreateOperationsContext` no longer has an `isInitial` flag, and `createOperationsAsync` fires only once — so the hook itself has no iteration-level context at all. The appropriate places to detect first vs. subsequent runs are: + +**In `configureIteration` (plugin hooks):** The second parameter `lastExecutedRecords` is empty on the very first run and non-empty on subsequent ones: + +```typescript +graph.hooks.configureIteration.tap(PLUGIN_NAME, (currentStates, lastExecutedRecords) => { + const isFirstRun = lastExecutedRecords.size === 0; +}); +``` + +**In `IOperationRunner.executeAsync` (custom runners):** The runner is called with a `lastState` parameter. On the first execution `lastState` is `undefined`; on subsequent iterations it holds the `IOperationExecutionResult` from the most recent iteration in which the operation **actually executed to a terminal state**. Runners should use this to decide whether to run an incremental command or a full initial build: + +```typescript +class MyRunner implements IOperationRunner { + async executeAsync(context: IOperationRunnerContext, lastState?: {}): Promise { + if (lastState === undefined) { + // First execution, or the operation was never able to complete in a prior iteration + // (e.g. it was aborted, blocked, or skipped every time) — run full build + } else { + // Operation previously completed — can use incremental strategy + } + // ... + } +} +``` + +This is how `ShellOperationRunner` selects between the `initialCommand` and `incrementalCommand` scripts defined in `rush-project.json`. + +> **Note:** `lastState` is only populated if the operation reached a completed terminal state (`Success`, `SuccessWithWarning`, `Failure`, `FromCache`, or `NoOp`) in a prior iteration. If the previous iteration was aborted before the operation began executing, or if the operation was `Skipped` or `Blocked`, `lastState` will still be `undefined` on the next call. Runners must not assume that a non-`undefined` `lastState` means the previous run succeeded — check `lastState.status` if the prior outcome matters for your incremental logic. +> +> To request re-execution from a long-lived runner, use `context.getInvalidateCallback()` on the `IOperationRunnerContext` to obtain a `(reason: string) => void` callback. This is available from the very first call to `executeAsync`, regardless of whether a previous result exists. + +### Long-lived runners across watch iterations + +Because the same `Operation` objects (and their `runner` instances) are reused for the entire session, custom `IOperationRunner` implementations must be written to handle multiple calls to `executeAsync` on the same instance. A runner that holds external resources — such as a file watcher, a dev server, or a long-running child process — is responsible for managing those resources across iterations. + +Key points for custom runner authors: + +- **`lastState` signals prior completion, not just re-execution.** The `lastState` parameter passed to `executeAsync` is `undefined` on the first call and on any call where the operation did not reach a completed terminal state in the previous iteration (e.g. it was aborted before it began, or was `Skipped`/`Blocked`). It is non-`undefined` only when the operation previously completed with `Success`, `SuccessWithWarning`, `Failure`, `FromCache`, or `NoOp`. Use `lastState` — and `lastState.status` if the prior outcome matters — to decide whether to perform a full or incremental build. + +- **`context.getInvalidateCallback()` for self-invalidation.** Long-lived runners that detect stale outputs (e.g. file watchers, IPC processes) can call `context.getInvalidateCallback()` to obtain a lightweight `(reason: string) => void` callback that requests re-execution. This is available from the very first `executeAsync` call, regardless of whether a previous result exists. Store the returned callback rather than the full context to avoid retaining unnecessary references. + +- **`isActive` tracks background ownership.** If your runner starts a background process (e.g. a dev server) that remains running between iterations, set `isActive = true` for as long as that resource is owned. This allows the dashboard and other tooling to correctly represent the operation's state. + +- **`closeAsync` must be idempotent.** Rush calls `graph.closeRunnersAsync()` when the session ends (on `AbortController` abort). If your runner implements `closeAsync`, it may be called with or without a prior `executeAsync` call, or after the runner has already been closed. Implement it defensively. + +- **State from one iteration does not automatically carry over.** The `IOperationExecutionResult` passed as `lastState` is a snapshot of the previous result. Mutable runner state (e.g. file handles, cached data) must be managed by the runner itself — it is not serialized or restored by Rush. + +```typescript +class MyWatchRunner implements IOperationRunner { + private _server: MyServer | undefined; + + get isActive(): boolean { + return this._server !== undefined; + } + + async executeAsync(context: IOperationRunnerContext, lastState?: {}): Promise { + if (!this._server) { + // First execution — start the server + this._server = await MyServer.startAsync(); + } else { + // Subsequent execution — reload changed files + await this._server.reloadAsync(); + } + return OperationStatus.Success; + } + + async closeAsync(): Promise { + await this._server?.stopAsync(); + this._server = undefined; + } +} +``` + +### Accessing `projectsInUnknownState` from context + +The set of projects with unknown/changed state is no longer pre-computed and passed as context. Plugins that previously consumed `projectsInUnknownState` to decide which operations to run should instead tap `configureIteration` and use the `inputsSnapshot` from `IOperationGraphIterationOptions` to compare state hashes. The built-in logic for this is handled by `PhasedOperationPlugin` and `LegacySkipPlugin`. + +### Parallel shutdown via `shutdownAsync` + +`shutdownAsync` was an `AsyncParallelHook` that allowed multiple plugins to run cleanup concurrently. The replacement via `AbortController` signal listeners achieves a similar result for fire-and-forget cleanup, but there is no built-in mechanism to await all parallel teardown tasks before the process exits. If your plugin needs to perform async cleanup and have it awaited, use `closeRunnersAsync` (for operation runners) or coordinate through the graph's `abortController.signal` and manage your own promises. + +### Mutating the `enabled` state of operations from `createOperations` + +Previously, some plugins disabled operations by mutating the record map returned from `beforeExecuteOperations`. The new model separates graph construction (session-long) from iteration configuration. Operations can only have their `enabled` state changed in `configureIteration` (synchronous) or via `IOperationGraph.setEnabledStates()` at any time. Operations cannot be added or removed from the graph after `createOperationsAsync` completes. + +### `WeightedOperationPlugin` + +This plugin, which assigned operation weights from `rush-project.json` settings, has been removed. Weight assignment is now performed directly by `PhasedOperationPlugin` during graph construction. External plugins that previously called `new WeightedOperationPlugin().apply(hooks)` should remove that call — the behavior is built in. + +--- + +## Accessing session context from graph hooks + +Session-scoped data from `IOperationGraphContext` (build cache config, project selection, phase selection, etc.) is available via closure in the `onGraphCreatedAsync` callback. Store what you need in local variables: + +```typescript +hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph, context) => { + const { buildCacheConfiguration, projectSelection } = context; + + graph.hooks.beforeExecuteIterationAsync.tapPromise(PLUGIN_NAME, async (records, iterationOptions) => { + // buildCacheConfiguration and projectSelection are available here via closure + }); +}); +``` + +## Self-invalidation from runners + +Long-lived runners that need to request re-execution (e.g. a file watcher detecting changes, or an IPC process reporting stale outputs) should call `context.getInvalidateCallback()` on the `IOperationRunnerContext`. This returns a lightweight `(reason: string) => void` callback that delegates to `IOperationGraph.invalidateOperations()` under the hood. Store the returned callback rather than retaining the full context to avoid memory leaks: + +```typescript +class MyWatchRunner implements IOperationRunner { + async executeAsync(context: IOperationRunnerContext, lastState?: {}): Promise { + const invalidate = context.getInvalidateCallback(); + // ... set up watchers that call invalidate('files changed') + return OperationStatus.Success; + } +} +``` + +Because `context` is always available (not just after a completed prior run), runners can obtain the callback on their very first execution. + +> **Note:** If you still need `IOperationGraph` for other purposes (e.g. `setEnabledStates`), the closure pattern via `onGraphCreatedAsync` is still valid. But for simple self-invalidation, prefer `getInvalidateCallback()`. diff --git a/eslint/eslint-bulk/.npmignore b/eslint/eslint-bulk/.npmignore new file mode 100755 index 00000000000..f7a40e10213 --- /dev/null +++ b/eslint/eslint-bulk/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/eslint/eslint-bulk/CHANGELOG.json b/eslint/eslint-bulk/CHANGELOG.json new file mode 100644 index 00000000000..2ad8a1b644c --- /dev/null +++ b/eslint/eslint-bulk/CHANGELOG.json @@ -0,0 +1,1776 @@ +{ + "name": "@rushstack/eslint-bulk", + "entries": [ + { + "version": "0.5.22", + "tag": "@rushstack/eslint-bulk_v0.5.22", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "0.5.21", + "tag": "@rushstack/eslint-bulk_v0.5.21", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "0.5.20", + "tag": "@rushstack/eslint-bulk_v0.5.20", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "0.5.19", + "tag": "@rushstack/eslint-bulk_v0.5.19", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "0.5.18", + "tag": "@rushstack/eslint-bulk_v0.5.18", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "0.5.17", + "tag": "@rushstack/eslint-bulk_v0.5.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "0.5.16", + "tag": "@rushstack/eslint-bulk_v0.5.16", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "0.5.15", + "tag": "@rushstack/eslint-bulk_v0.5.15", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "0.5.14", + "tag": "@rushstack/eslint-bulk_v0.5.14", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "0.5.13", + "tag": "@rushstack/eslint-bulk_v0.5.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "0.5.12", + "tag": "@rushstack/eslint-bulk_v0.5.12", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "0.5.11", + "tag": "@rushstack/eslint-bulk_v0.5.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "0.5.10", + "tag": "@rushstack/eslint-bulk_v0.5.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "0.5.9", + "tag": "@rushstack/eslint-bulk_v0.5.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "0.5.8", + "tag": "@rushstack/eslint-bulk_v0.5.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "0.5.7", + "tag": "@rushstack/eslint-bulk_v0.5.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "0.5.6", + "tag": "@rushstack/eslint-bulk_v0.5.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "0.5.5", + "tag": "@rushstack/eslint-bulk_v0.5.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "0.5.4", + "tag": "@rushstack/eslint-bulk_v0.5.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "0.5.3", + "tag": "@rushstack/eslint-bulk_v0.5.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "0.5.2", + "tag": "@rushstack/eslint-bulk_v0.5.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "0.5.1", + "tag": "@rushstack/eslint-bulk_v0.5.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.16.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "0.5.0", + "tag": "@rushstack/eslint-bulk_v0.5.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "0.4.15", + "tag": "@rushstack/eslint-bulk_v0.4.15", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "0.4.14", + "tag": "@rushstack/eslint-bulk_v0.4.14", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "0.4.13", + "tag": "@rushstack/eslint-bulk_v0.4.13", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "0.4.12", + "tag": "@rushstack/eslint-bulk_v0.4.12", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "0.4.11", + "tag": "@rushstack/eslint-bulk_v0.4.11", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "0.4.10", + "tag": "@rushstack/eslint-bulk_v0.4.10", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "0.4.9", + "tag": "@rushstack/eslint-bulk_v0.4.9", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "0.4.8", + "tag": "@rushstack/eslint-bulk_v0.4.8", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + } + ] + } + }, + { + "version": "0.4.7", + "tag": "@rushstack/eslint-bulk_v0.4.7", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + } + ] + } + }, + { + "version": "0.4.6", + "tag": "@rushstack/eslint-bulk_v0.4.6", + "date": "Wed, 12 Nov 2025 01:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.15.0`" + } + ] + } + }, + { + "version": "0.4.5", + "tag": "@rushstack/eslint-bulk_v0.4.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + } + ] + } + }, + { + "version": "0.4.4", + "tag": "@rushstack/eslint-bulk_v0.4.4", + "date": "Tue, 04 Nov 2025 08:15:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + } + ] + } + }, + { + "version": "0.4.3", + "tag": "@rushstack/eslint-bulk_v0.4.3", + "date": "Fri, 24 Oct 2025 11:22:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.14.1`" + } + ] + } + }, + { + "version": "0.4.2", + "tag": "@rushstack/eslint-bulk_v0.4.2", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + } + ] + } + }, + { + "version": "0.4.1", + "tag": "@rushstack/eslint-bulk_v0.4.1", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + } + ] + } + }, + { + "version": "0.4.0", + "tag": "@rushstack/eslint-bulk_v0.4.0", + "date": "Mon, 13 Oct 2025 15:13:02 GMT", + "comments": { + "minor": [ + { + "comment": "Bump `eslint` to `~9.37.0` and the `@typescript-eslint/*` packages to `~8.46.0`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.14.0`" + } + ] + } + }, + { + "version": "0.3.1", + "tag": "@rushstack/eslint-bulk_v0.3.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + } + ] + } + }, + { + "version": "0.3.0", + "tag": "@rushstack/eslint-bulk_v0.3.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/eslint-bulk_v0.2.7", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/eslint-bulk_v0.2.6", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/eslint-bulk_v0.2.5", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/eslint-bulk_v0.2.4", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/eslint-bulk_v0.2.3", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/eslint-bulk_v0.2.2", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/eslint-bulk_v0.2.1", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/eslint-bulk_v0.2.0", + "date": "Thu, 26 Jun 2025 18:57:04 GMT", + "comments": { + "minor": [ + { + "comment": "Update for compatibility with ESLint flat configuration files" + } + ] + } + }, + { + "version": "0.1.95", + "tag": "@rushstack/eslint-bulk_v0.1.95", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + } + ] + } + }, + { + "version": "0.1.94", + "tag": "@rushstack/eslint-bulk_v0.1.94", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + } + ] + } + }, + { + "version": "0.1.93", + "tag": "@rushstack/eslint-bulk_v0.1.93", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + } + ] + } + }, + { + "version": "0.1.92", + "tag": "@rushstack/eslint-bulk_v0.1.92", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + } + ] + } + }, + { + "version": "0.1.91", + "tag": "@rushstack/eslint-bulk_v0.1.91", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + } + ] + } + }, + { + "version": "0.1.90", + "tag": "@rushstack/eslint-bulk_v0.1.90", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + } + ] + } + }, + { + "version": "0.1.89", + "tag": "@rushstack/eslint-bulk_v0.1.89", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + } + ] + } + }, + { + "version": "0.1.88", + "tag": "@rushstack/eslint-bulk_v0.1.88", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + } + ] + } + }, + { + "version": "0.1.87", + "tag": "@rushstack/eslint-bulk_v0.1.87", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + } + ] + } + }, + { + "version": "0.1.86", + "tag": "@rushstack/eslint-bulk_v0.1.86", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + } + ] + } + }, + { + "version": "0.1.85", + "tag": "@rushstack/eslint-bulk_v0.1.85", + "date": "Tue, 25 Mar 2025 15:11:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + } + ] + } + }, + { + "version": "0.1.84", + "tag": "@rushstack/eslint-bulk_v0.1.84", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + } + ] + } + }, + { + "version": "0.1.83", + "tag": "@rushstack/eslint-bulk_v0.1.83", + "date": "Wed, 12 Mar 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + } + ] + } + }, + { + "version": "0.1.82", + "tag": "@rushstack/eslint-bulk_v0.1.82", + "date": "Tue, 11 Mar 2025 02:12:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + } + ] + } + }, + { + "version": "0.1.81", + "tag": "@rushstack/eslint-bulk_v0.1.81", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + } + ] + } + }, + { + "version": "0.1.80", + "tag": "@rushstack/eslint-bulk_v0.1.80", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + } + ] + } + }, + { + "version": "0.1.79", + "tag": "@rushstack/eslint-bulk_v0.1.79", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + } + ] + } + }, + { + "version": "0.1.78", + "tag": "@rushstack/eslint-bulk_v0.1.78", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + } + ] + } + }, + { + "version": "0.1.77", + "tag": "@rushstack/eslint-bulk_v0.1.77", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + } + ] + } + }, + { + "version": "0.1.76", + "tag": "@rushstack/eslint-bulk_v0.1.76", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + } + ] + } + }, + { + "version": "0.1.75", + "tag": "@rushstack/eslint-bulk_v0.1.75", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + } + ] + } + }, + { + "version": "0.1.74", + "tag": "@rushstack/eslint-bulk_v0.1.74", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + } + ] + } + }, + { + "version": "0.1.73", + "tag": "@rushstack/eslint-bulk_v0.1.73", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + } + ] + } + }, + { + "version": "0.1.72", + "tag": "@rushstack/eslint-bulk_v0.1.72", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + } + ] + } + }, + { + "version": "0.1.71", + "tag": "@rushstack/eslint-bulk_v0.1.71", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + } + ] + } + }, + { + "version": "0.1.70", + "tag": "@rushstack/eslint-bulk_v0.1.70", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + } + ] + } + }, + { + "version": "0.1.69", + "tag": "@rushstack/eslint-bulk_v0.1.69", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + } + ] + } + }, + { + "version": "0.1.68", + "tag": "@rushstack/eslint-bulk_v0.1.68", + "date": "Tue, 03 Dec 2024 16:11:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + } + ] + } + }, + { + "version": "0.1.67", + "tag": "@rushstack/eslint-bulk_v0.1.67", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + } + ] + } + }, + { + "version": "0.1.66", + "tag": "@rushstack/eslint-bulk_v0.1.66", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + } + ] + } + }, + { + "version": "0.1.65", + "tag": "@rushstack/eslint-bulk_v0.1.65", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + } + ] + } + }, + { + "version": "0.1.64", + "tag": "@rushstack/eslint-bulk_v0.1.64", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + } + ] + } + }, + { + "version": "0.1.63", + "tag": "@rushstack/eslint-bulk_v0.1.63", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + } + ] + } + }, + { + "version": "0.1.62", + "tag": "@rushstack/eslint-bulk_v0.1.62", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + } + ] + } + }, + { + "version": "0.1.61", + "tag": "@rushstack/eslint-bulk_v0.1.61", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + } + ] + } + }, + { + "version": "0.1.60", + "tag": "@rushstack/eslint-bulk_v0.1.60", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + } + ] + } + }, + { + "version": "0.1.59", + "tag": "@rushstack/eslint-bulk_v0.1.59", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + } + ] + } + }, + { + "version": "0.1.58", + "tag": "@rushstack/eslint-bulk_v0.1.58", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + } + ] + } + }, + { + "version": "0.1.57", + "tag": "@rushstack/eslint-bulk_v0.1.57", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + } + ] + } + }, + { + "version": "0.1.56", + "tag": "@rushstack/eslint-bulk_v0.1.56", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + } + ] + } + }, + { + "version": "0.1.55", + "tag": "@rushstack/eslint-bulk_v0.1.55", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + } + ] + } + }, + { + "version": "0.1.54", + "tag": "@rushstack/eslint-bulk_v0.1.54", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + } + ] + } + }, + { + "version": "0.1.53", + "tag": "@rushstack/eslint-bulk_v0.1.53", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + } + ] + } + }, + { + "version": "0.1.52", + "tag": "@rushstack/eslint-bulk_v0.1.52", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + } + ] + } + }, + { + "version": "0.1.51", + "tag": "@rushstack/eslint-bulk_v0.1.51", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + } + ] + } + }, + { + "version": "0.1.50", + "tag": "@rushstack/eslint-bulk_v0.1.50", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + } + ] + } + }, + { + "version": "0.1.49", + "tag": "@rushstack/eslint-bulk_v0.1.49", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + } + ] + } + }, + { + "version": "0.1.48", + "tag": "@rushstack/eslint-bulk_v0.1.48", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + } + ] + } + }, + { + "version": "0.1.47", + "tag": "@rushstack/eslint-bulk_v0.1.47", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + } + ] + } + }, + { + "version": "0.1.46", + "tag": "@rushstack/eslint-bulk_v0.1.46", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + } + ] + } + }, + { + "version": "0.1.45", + "tag": "@rushstack/eslint-bulk_v0.1.45", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + } + ] + } + }, + { + "version": "0.1.44", + "tag": "@rushstack/eslint-bulk_v0.1.44", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + } + ] + } + }, + { + "version": "0.1.43", + "tag": "@rushstack/eslint-bulk_v0.1.43", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + } + ] + } + }, + { + "version": "0.1.42", + "tag": "@rushstack/eslint-bulk_v0.1.42", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + } + ] + } + }, + { + "version": "0.1.41", + "tag": "@rushstack/eslint-bulk_v0.1.41", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + } + ] + } + }, + { + "version": "0.1.40", + "tag": "@rushstack/eslint-bulk_v0.1.40", + "date": "Fri, 24 May 2024 00:15:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + } + ] + } + }, + { + "version": "0.1.39", + "tag": "@rushstack/eslint-bulk_v0.1.39", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + } + ] + } + }, + { + "version": "0.1.38", + "tag": "@rushstack/eslint-bulk_v0.1.38", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + } + ] + } + }, + { + "version": "0.1.37", + "tag": "@rushstack/eslint-bulk_v0.1.37", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + } + ] + } + }, + { + "version": "0.1.36", + "tag": "@rushstack/eslint-bulk_v0.1.36", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + } + ] + } + }, + { + "version": "0.1.35", + "tag": "@rushstack/eslint-bulk_v0.1.35", + "date": "Sat, 11 May 2024 00:12:09 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where the tool will not correctly execute if the installed eslint path contains a space." + } + ] + } + }, + { + "version": "0.1.34", + "tag": "@rushstack/eslint-bulk_v0.1.34", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + } + ] + } + }, + { + "version": "0.1.33", + "tag": "@rushstack/eslint-bulk_v0.1.33", + "date": "Wed, 08 May 2024 22:23:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + } + ] + } + }, + { + "version": "0.1.32", + "tag": "@rushstack/eslint-bulk_v0.1.32", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + } + ] + } + }, + { + "version": "0.1.31", + "tag": "@rushstack/eslint-bulk_v0.1.31", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + } + ] + } + }, + { + "version": "0.1.30", + "tag": "@rushstack/eslint-bulk_v0.1.30", + "date": "Tue, 09 Apr 2024 18:08:23 GMT", + "comments": { + "patch": [ + { + "comment": "Attempt to resolve eslint as a dependency of the current project before falling back to a globally-installed copy." + } + ] + } + }, + { + "version": "0.1.29", + "tag": "@rushstack/eslint-bulk_v0.1.29", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + } + ] + } + }, + { + "version": "0.1.28", + "tag": "@rushstack/eslint-bulk_v0.1.28", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + } + ] + } + }, + { + "version": "0.1.27", + "tag": "@rushstack/eslint-bulk_v0.1.27", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + } + ] + } + }, + { + "version": "0.1.26", + "tag": "@rushstack/eslint-bulk_v0.1.26", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + } + ] + } + }, + { + "version": "0.1.25", + "tag": "@rushstack/eslint-bulk_v0.1.25", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + } + ] + } + }, + { + "version": "0.1.24", + "tag": "@rushstack/eslint-bulk_v0.1.24", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + } + ] + } + }, + { + "version": "0.1.23", + "tag": "@rushstack/eslint-bulk_v0.1.23", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + } + ] + } + }, + { + "version": "0.1.22", + "tag": "@rushstack/eslint-bulk_v0.1.22", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + } + ] + } + }, + { + "version": "0.1.21", + "tag": "@rushstack/eslint-bulk_v0.1.21", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + } + ] + } + }, + { + "version": "0.1.20", + "tag": "@rushstack/eslint-bulk_v0.1.20", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + } + ] + } + }, + { + "version": "0.1.19", + "tag": "@rushstack/eslint-bulk_v0.1.19", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + } + ] + } + }, + { + "version": "0.1.18", + "tag": "@rushstack/eslint-bulk_v0.1.18", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + } + ] + } + }, + { + "version": "0.1.17", + "tag": "@rushstack/eslint-bulk_v0.1.17", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + } + ] + } + }, + { + "version": "0.1.16", + "tag": "@rushstack/eslint-bulk_v0.1.16", + "date": "Tue, 20 Feb 2024 16:10:53 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + } + ] + } + }, + { + "version": "0.1.15", + "tag": "@rushstack/eslint-bulk_v0.1.15", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + } + ] + } + }, + { + "version": "0.1.14", + "tag": "@rushstack/eslint-bulk_v0.1.14", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + } + ] + } + }, + { + "version": "0.1.13", + "tag": "@rushstack/eslint-bulk_v0.1.13", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + } + ] + } + }, + { + "version": "0.1.12", + "tag": "@rushstack/eslint-bulk_v0.1.12", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + } + ] + } + }, + { + "version": "0.1.11", + "tag": "@rushstack/eslint-bulk_v0.1.11", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + } + ] + } + }, + { + "version": "0.1.10", + "tag": "@rushstack/eslint-bulk_v0.1.10", + "date": "Thu, 25 Jan 2024 23:03:57 GMT", + "comments": { + "patch": [ + { + "comment": "Some minor documentation updates" + } + ] + } + }, + { + "version": "0.1.9", + "tag": "@rushstack/eslint-bulk_v0.1.9", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + } + ] + } + }, + { + "version": "0.1.8", + "tag": "@rushstack/eslint-bulk_v0.1.8", + "date": "Wed, 24 Jan 2024 07:38:34 GMT", + "comments": { + "patch": [ + { + "comment": "Update documentation" + } + ] + } + }, + { + "version": "0.1.7", + "tag": "@rushstack/eslint-bulk_v0.1.7", + "date": "Tue, 23 Jan 2024 20:12:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + } + ] + } + }, + { + "version": "0.1.6", + "tag": "@rushstack/eslint-bulk_v0.1.6", + "date": "Tue, 23 Jan 2024 16:15:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + } + ] + } + }, + { + "version": "0.1.5", + "tag": "@rushstack/eslint-bulk_v0.1.5", + "date": "Tue, 16 Jan 2024 18:30:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + } + ] + } + }, + { + "version": "0.1.4", + "tag": "@rushstack/eslint-bulk_v0.1.4", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + } + ] + } + }, + { + "version": "0.1.3", + "tag": "@rushstack/eslint-bulk_v0.1.3", + "date": "Wed, 20 Dec 2023 01:09:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/eslint-bulk_v0.1.2", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/eslint-bulk_v0.1.1", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/eslint-bulk_v0.1.0", + "date": "Wed, 22 Nov 2023 01:45:18 GMT", + "comments": { + "minor": [ + { + "comment": "Initial release of `@rushstack/eslint-bulk` package" + } + ] + } + } + ] +} diff --git a/eslint/eslint-bulk/CHANGELOG.md b/eslint/eslint-bulk/CHANGELOG.md new file mode 100644 index 00000000000..ea081580fdf --- /dev/null +++ b/eslint/eslint-bulk/CHANGELOG.md @@ -0,0 +1,751 @@ +# Change Log - @rushstack/eslint-bulk + +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 0.5.22 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 0.5.21 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 0.5.20 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.5.19 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.5.18 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 0.5.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.5.16 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 0.5.15 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 0.5.14 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 0.5.13 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.5.12 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 0.5.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.5.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 0.5.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 0.5.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.5.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 0.5.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.5.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 0.5.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.5.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 0.5.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 0.5.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.5.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.4.15 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 0.4.14 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 0.4.13 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 0.4.12 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 0.4.11 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 0.4.10 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 0.4.9 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 0.4.8 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 0.4.7 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 0.4.6 +Wed, 12 Nov 2025 01:57:54 GMT + +_Version update only_ + +## 0.4.5 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 0.4.4 +Tue, 04 Nov 2025 08:15:15 GMT + +_Version update only_ + +## 0.4.3 +Fri, 24 Oct 2025 11:22:09 GMT + +_Version update only_ + +## 0.4.2 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 0.4.1 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 0.4.0 +Mon, 13 Oct 2025 15:13:02 GMT + +### Minor changes + +- Bump `eslint` to `~9.37.0` and the `@typescript-eslint/*` packages to `~8.46.0`. + +## 0.3.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 0.3.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 0.2.7 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 0.2.6 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.2.5 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.2.4 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.2.3 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.2.2 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 0.2.1 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.2.0 +Thu, 26 Jun 2025 18:57:04 GMT + +### Minor changes + +- Update for compatibility with ESLint flat configuration files + +## 0.1.95 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.1.94 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.1.93 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.1.92 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.1.91 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.1.90 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.1.89 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.1.88 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 0.1.87 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 0.1.86 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.1.85 +Tue, 25 Mar 2025 15:11:16 GMT + +_Version update only_ + +## 0.1.84 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.1.83 +Wed, 12 Mar 2025 00:11:32 GMT + +_Version update only_ + +## 0.1.82 +Tue, 11 Mar 2025 02:12:34 GMT + +_Version update only_ + +## 0.1.81 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.1.80 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.1.79 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.1.78 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 0.1.77 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.1.76 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.1.75 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.1.74 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 0.1.73 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.1.72 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.1.71 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.1.70 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.1.69 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.1.68 +Tue, 03 Dec 2024 16:11:08 GMT + +_Version update only_ + +## 0.1.67 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.1.66 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.1.65 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.1.64 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.1.63 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.1.62 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.1.61 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.1.60 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.1.59 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.1.58 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.1.57 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.1.56 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.1.55 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.1.54 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.1.53 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.1.52 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 0.1.51 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 0.1.50 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.1.49 +Tue, 16 Jul 2024 00:36:21 GMT + +_Version update only_ + +## 0.1.48 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.1.47 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.1.46 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.1.45 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 0.1.44 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.1.43 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.1.42 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.1.41 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.1.40 +Fri, 24 May 2024 00:15:08 GMT + +_Version update only_ + +## 0.1.39 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 0.1.38 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.1.37 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.1.36 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.1.35 +Sat, 11 May 2024 00:12:09 GMT + +### Patches + +- Fix an issue where the tool will not correctly execute if the installed eslint path contains a space. + +## 0.1.34 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.1.33 +Wed, 08 May 2024 22:23:50 GMT + +_Version update only_ + +## 0.1.32 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 0.1.31 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.1.30 +Tue, 09 Apr 2024 18:08:23 GMT + +### Patches + +- Attempt to resolve eslint as a dependency of the current project before falling back to a globally-installed copy. + +## 0.1.29 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 0.1.28 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 0.1.27 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 0.1.26 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 0.1.25 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 0.1.24 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 0.1.23 +Thu, 29 Feb 2024 07:11:45 GMT + +_Version update only_ + +## 0.1.22 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.1.21 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.1.20 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.1.19 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.1.18 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.1.17 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.1.16 +Tue, 20 Feb 2024 16:10:53 GMT + +_Version update only_ + +## 0.1.15 +Mon, 19 Feb 2024 21:54:27 GMT + +_Version update only_ + +## 0.1.14 +Sat, 17 Feb 2024 06:24:35 GMT + +_Version update only_ + +## 0.1.13 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 0.1.12 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.1.11 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.1.10 +Thu, 25 Jan 2024 23:03:57 GMT + +### Patches + +- Some minor documentation updates + +## 0.1.9 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.1.8 +Wed, 24 Jan 2024 07:38:34 GMT + +### Patches + +- Update documentation + +## 0.1.7 +Tue, 23 Jan 2024 20:12:58 GMT + +_Version update only_ + +## 0.1.6 +Tue, 23 Jan 2024 16:15:06 GMT + +_Version update only_ + +## 0.1.5 +Tue, 16 Jan 2024 18:30:11 GMT + +_Version update only_ + +## 0.1.4 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.1.3 +Wed, 20 Dec 2023 01:09:46 GMT + +_Version update only_ + +## 0.1.2 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.1.1 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.1.0 +Wed, 22 Nov 2023 01:45:18 GMT + +### Minor changes + +- Initial release of `@rushstack/eslint-bulk` package + diff --git a/eslint/eslint-bulk/LICENSE b/eslint/eslint-bulk/LICENSE new file mode 100644 index 00000000000..bd913f236dd --- /dev/null +++ b/eslint/eslint-bulk/LICENSE @@ -0,0 +1,24 @@ +@rushstack/eslint-bulk + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/eslint/eslint-bulk/README.md b/eslint/eslint-bulk/README.md new file mode 100755 index 00000000000..4f17d9a855d --- /dev/null +++ b/eslint/eslint-bulk/README.md @@ -0,0 +1,52 @@ +# @rushstack/eslint-bulk + +This package provides the command-line interface (CLI) for the **ESLint bulk suppressions** +feature from `@rushstack/eslint-patch`. + +### Setting it up + +👉 Before using this tool, you will first need to install and configure the +[@rushstack/eslint-patch](https://www.npmjs.com/package/@rushstack/eslint-patch) package. + +See the [eslint-bulk-suppressions documentation](https://www.npmjs.com/package/@rushstack/eslint-patch#eslint-bulk-suppressions-feature) +for details. + +### Typical workflow + +1. Checkout your `main` branch, which is in a clean state where ESLint reports no violations. +2. Update your configuration to enable the latest lint rules; ESLint now reports thousands of legacy violations. +3. Run `eslint-bulk suppress --all ./src` to update **.eslint-bulk-suppressions.json.** +4. ESLint now no longer reports violations, so commit the results to Git and merge your pull request. +5. Over time, engineers may improve some of the suppressed code, in which case the associated suppressions are no longer needed. +6. Run `eslint-bulk prune` periodically to find and remove unnecessary suppressions from **.eslint-bulk-suppressions.json**, ensuring that new violations will now get caught in those scopes. + +### "eslint-bulk suppress" command + +```bash +eslint-bulk suppress --rule NAME1 [--rule NAME2...] PATH1 [PATH2...] +eslint-bulk suppress --all PATH1 [PATH2...] +``` + +Use this command to automatically generate bulk suppressions for the specified lint rules and file paths. +The path argument is a [glob pattern](https://en.wikipedia.org/wiki/Glob_(programming)) with the same syntax +as path arguments for the `eslint` command. + + +### "eslint-bulk prune" command + +Use this command to automatically delete all unnecessary suppression entries in all +**.eslint-bulk-suppressions.json** files under the current working directory. + +```bash +eslint-bulk prune +``` + +# Links + +- [CHANGELOG.md](https://github.com/microsoft/rushstack/blob/main/eslint/eslint-bulk/CHANGELOG.md) - Find + out what's new in the latest version + +- [`@rushstack/eslint-patch`](https://www.npmjs.com/package/@rushstack/eslint-patch) required companion package + + +`@rushstack/eslint-bulk` is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/eslint/eslint-bulk/bin/eslint-bulk b/eslint/eslint-bulk/bin/eslint-bulk new file mode 100755 index 00000000000..eef2fc27066 --- /dev/null +++ b/eslint/eslint-bulk/bin/eslint-bulk @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('../lib-commonjs/start.js'); diff --git a/eslint/eslint-bulk/config/rig.json b/eslint/eslint-bulk/config/rig.json new file mode 100755 index 00000000000..165ffb001f5 --- /dev/null +++ b/eslint/eslint-bulk/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/eslint/eslint-bulk/eslint.config.js b/eslint/eslint-bulk/eslint.config.js new file mode 100644 index 00000000000..ceb5a1bee40 --- /dev/null +++ b/eslint/eslint-bulk/eslint.config.js @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + }, + rules: { + 'no-console': 'off' + } + } +]; diff --git a/eslint/eslint-bulk/package.json b/eslint/eslint-bulk/package.json new file mode 100755 index 00000000000..8b08e2d7e82 --- /dev/null +++ b/eslint/eslint-bulk/package.json @@ -0,0 +1,59 @@ +{ + "name": "@rushstack/eslint-bulk", + "version": "0.5.22", + "description": "Roll out new ESLint rules in a large monorepo without cluttering up your code with \"eslint-ignore-next-line\"", + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "license": "MIT", + "repository": { + "url": "https://github.com/microsoft/rushstack.git", + "type": "git", + "directory": "eslint/eslint-bulk" + }, + "homepage": "https://rushstack.io", + "bin": { + "eslint-bulk": "./bin/eslint-bulk" + }, + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean", + "start": "node ./lib-commonjs/start.js" + }, + "keywords": [ + "eslintrc", + "eslint", + "bulk", + "legacy", + "retroactive", + "disable", + "ignore", + "suppression", + "monkey", + "patch" + ], + "devDependencies": { + "@rushstack/eslint-patch": "workspace:*", + "@rushstack/heft": "workspace:*", + "@types/node": "20.17.19", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "sideEffects": [ + "lib-commonjs/start.js", + "lib-esm/start.js" + ] +} diff --git a/eslint/eslint-bulk/src/start.ts b/eslint/eslint-bulk/src/start.ts new file mode 100644 index 00000000000..e2ea84ba852 --- /dev/null +++ b/eslint/eslint-bulk/src/start.ts @@ -0,0 +1,186 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { + type ExecSyncOptionsWithBufferEncoding, + type SpawnSyncOptionsWithBufferEncoding, + execSync, + spawnSync +} from 'node:child_process'; +import * as process from 'node:process'; +import * as fs from 'node:fs'; + +import type { + ESLINT_BULK_STDOUT_START_DELIMETER as ESLINT_BULK_STDOUT_START_DELIMETER_TYPE, + ESLINT_BULK_STDOUT_END_DELIMETER as ESLINT_BULK_STDOUT_END_DELIMETER_TYPE, + ESLINT_PACKAGE_NAME_ENV_VAR_NAME as ESLINT_PACKAGE_NAME_ENV_VAR_NAME_TYPE +} from '@rushstack/eslint-patch/lib/eslint-bulk-suppressions/constants'; + +const ESLINT_BULK_STDOUT_START_DELIMETER: typeof ESLINT_BULK_STDOUT_START_DELIMETER_TYPE = + 'RUSHSTACK_ESLINT_BULK_START'; +const ESLINT_BULK_STDOUT_END_DELIMETER: typeof ESLINT_BULK_STDOUT_END_DELIMETER_TYPE = + 'RUSHSTACK_ESLINT_BULK_END'; +const ESLINT_PACKAGE_NAME_ENV_VAR_NAME: typeof ESLINT_PACKAGE_NAME_ENV_VAR_NAME_TYPE = + '_RUSHSTACK_ESLINT_PACKAGE_NAME'; +const BULK_SUPPRESSIONS_CLI_ESLINT_PACKAGE_NAME: string = + process.env[ESLINT_PACKAGE_NAME_ENV_VAR_NAME] ?? 'eslint'; + +const ESLINT_CONFIG_FILES: string[] = [ + 'eslint.config.js', + 'eslint.config.cjs', + 'eslint.config.mjs', + '.eslintrc.js', + '.eslintrc.cjs' +]; + +interface IEslintBulkConfigurationJson { + /** + * `@rushtack/eslint`-bulk should report an error if its package.json is older than this number + */ + minCliVersion: string; + /** + * `@rushtack/eslint-bulk` will invoke this entry point + */ + cliEntryPoint: string; +} + +function findPatchPath(): string { + const candidatePaths: string[] = ESLINT_CONFIG_FILES.map((fileName) => `${process.cwd()}/${fileName}`); + let eslintConfigPath: string | undefined; + for (const candidatePath of candidatePaths) { + if (fs.existsSync(candidatePath)) { + eslintConfigPath = candidatePath; + break; + } + } + + if (!eslintConfigPath) { + console.error( + '@rushstack/eslint-bulk: Please run this command from the directory that contains one of the following ' + + `ESLint configuration files: ${ESLINT_CONFIG_FILES.join(', ')}` + ); + process.exit(1); + } + + const env: NodeJS.ProcessEnv = { ...process.env, _RUSHSTACK_ESLINT_BULK_DETECT: 'true' }; + + let eslintPackageJsonPath: string | undefined; + try { + eslintPackageJsonPath = require.resolve(`${BULK_SUPPRESSIONS_CLI_ESLINT_PACKAGE_NAME}/package.json`, { + paths: [process.cwd()] + }); + } catch (e) { + if (e.code !== 'MODULE_NOT_FOUND') { + throw e; + } + } + + let eslintBinPath: string | undefined; + if (eslintPackageJsonPath) { + eslintPackageJsonPath = eslintPackageJsonPath.replace(/\\/g, '/'); + const packagePath: string = eslintPackageJsonPath.substring(0, eslintPackageJsonPath.lastIndexOf('/')); + const { bin: { eslint: relativeEslintBinPath } = {} }: { bin?: Record } = require( + eslintPackageJsonPath + ); + if (relativeEslintBinPath) { + eslintBinPath = `${packagePath}/${relativeEslintBinPath}`; + } else { + console.warn( + `@rushstack/eslint-bulk: The eslint package resolved at "${packagePath}" does not contain an eslint bin path. ` + + 'Attempting to use a globally-installed eslint instead.' + ); + } + } else { + console.log( + '@rushstack/eslint-bulk: Unable to resolve the eslint package as a dependency of the current project. ' + + 'Attempting to use a globally-installed eslint instead.' + ); + } + + const eslintArgs: string[] = ['--stdin', '--config']; + const spawnOrExecOptions: SpawnSyncOptionsWithBufferEncoding & ExecSyncOptionsWithBufferEncoding = { + env, + input: '', + stdio: 'pipe' + }; + let runEslintFn: () => Buffer; + if (eslintBinPath) { + runEslintFn = () => + spawnSync(process.argv0, [eslintBinPath, ...eslintArgs, eslintConfigPath], spawnOrExecOptions).stdout; + } else { + // Try to use a globally-installed eslint if a local package was not found + runEslintFn = () => execSync(`eslint ${eslintArgs.join(' ')} "${eslintConfigPath}"`, spawnOrExecOptions); + } + + let stdout: Buffer; + try { + stdout = runEslintFn(); + } catch (e) { + console.error('@rushstack/eslint-bulk: Error finding patch path: ' + e.message); + process.exit(1); + } + + const regex: RegExp = new RegExp( + `${ESLINT_BULK_STDOUT_START_DELIMETER}(.*?)${ESLINT_BULK_STDOUT_END_DELIMETER}` + ); + const match: RegExpMatchArray | null = stdout.toString().match(regex); + + if (match) { + // The configuration data will look something like this: + // + // RUSHSTACK_ESLINT_BULK_START{"minCliVersion":"0.0.0","cliEntryPoint":"path/to/eslint-bulk.js"}RUSHSTACK_ESLINT_BULK_END + const configurationJson: string = match[1].trim(); + let configuration: IEslintBulkConfigurationJson; + try { + configuration = JSON.parse(configurationJson); + if (!configuration.minCliVersion || !configuration.cliEntryPoint) { + throw new Error('Required field is missing'); + } + } catch (e) { + console.error('@rushstack/eslint-bulk: Error parsing patch configuration object:' + e.message); + process.exit(1); + } + + const myVersion: string = require('../package.json').version; + const myVersionParts: number[] = myVersion.split('.').map((x) => parseInt(x, 10)); + const minVersion: string = configuration.minCliVersion; + const minVersionParts: number[] = minVersion.split('.').map((x) => parseInt(x, 10)); + if ( + myVersionParts.length !== 3 || + minVersionParts.length !== 3 || + myVersionParts.some((x) => isNaN(x)) || + minVersionParts.some((x) => isNaN(x)) + ) { + console.error(`@rushstack/eslint-bulk: Unable to compare versions "${myVersion}" and "${minVersion}"`); + process.exit(1); + } + + for (let i: number = 0; i < 3; ++i) { + if (myVersionParts[i] > minVersionParts[i]) { + break; + } + if (myVersionParts[i] < minVersionParts[i]) { + console.error( + `@rushstack/eslint-bulk: The @rushstack/eslint-bulk version ${myVersion} is too old;` + + ` please upgrade to ${minVersion} or newer.` + ); + process.exit(1); + } + } + + return configuration.cliEntryPoint; + } + + console.error( + '@rushstack/eslint-bulk: Error finding patch path. Are you sure the package you are in has @rushstack/eslint-patch as a direct or indirect dependency?' + ); + process.exit(1); +} + +const patchPath: string = findPatchPath(); +try { + require(patchPath); +} catch (e) { + console.error(`@rushstack/eslint-bulk: Error running patch at ${patchPath}:\n` + e.message); + process.exit(1); +} diff --git a/eslint/eslint-bulk/tsconfig.json b/eslint/eslint-bulk/tsconfig.json new file mode 100644 index 00000000000..b2e15527599 --- /dev/null +++ b/eslint/eslint-bulk/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", + + "compilerOptions": { + "types": ["node"] + } +} diff --git a/eslint/eslint-config/.npmignore b/eslint/eslint-config/.npmignore index 77d9225c8e5..31e20769649 100644 --- a/eslint/eslint-config/.npmignore +++ b/eslint/eslint-config/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,20 +21,42 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- + +!/*.js +!/*.[cm]js +!/*.d.ts +!/*.d.[cm]ts -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +!flat/**/*.js +!flat/**/*.[cm]js +!flat/**/*.d.ts +!flat/**/*.d.[cm]ts -# (Add your project-specific overrides here) -!*.js !mixins/*.js +!mixins/*.[cm]js +!mixins/*.d.ts +!mixins/*.d.[cm]ts + !patch/*.js +!patch/*.[cm]js +!patch/*.d.ts +!patch/*.d.[cm]ts + !profile/*.js +!profile/*.[cm]js +!profile/*.d.ts +!profile/*.d.[cm]ts + diff --git a/eslint/eslint-config/CHANGELOG.json b/eslint/eslint-config/CHANGELOG.json index 51d23925b96..e6acfd01876 100644 --- a/eslint/eslint-config/CHANGELOG.json +++ b/eslint/eslint-config/CHANGELOG.json @@ -1,6 +1,655 @@ { "name": "@rushstack/eslint-config", "entries": [ + { + "version": "4.6.4", + "tag": "@rushstack/eslint-config_v4.6.4", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "patch": [ + { + "comment": "Bump `eslint-plugin-tsdoc` to `~0.5.1` to mitigate CVE-2026-26996." + }, + { + "comment": "Bump `@typescript-eslint/*` dependencies to `~8.56.1`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-packlets\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-security\" to `0.14.2`" + } + ] + } + }, + { + "version": "4.6.3", + "tag": "@rushstack/eslint-config_v4.6.3", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.16.1`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-packlets\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-security\" to `0.14.1`" + } + ] + } + }, + { + "version": "4.6.2", + "tag": "@rushstack/eslint-config_v4.6.2", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "patch": [ + { + "comment": "Add `sideEffects` field to package.json." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-packlets\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-security\" to `0.14.0`" + } + ] + } + }, + { + "version": "4.6.1", + "tag": "@rushstack/eslint-config_v4.6.1", + "date": "Wed, 12 Nov 2025 01:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.15.0`" + } + ] + } + }, + { + "version": "4.6.0", + "tag": "@rushstack/eslint-config_v4.6.0", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `eslint-plugin-tsdoc` dependency to `~0.5.0`." + } + ] + } + }, + { + "version": "4.5.4", + "tag": "@rushstack/eslint-config_v4.5.4", + "date": "Tue, 11 Nov 2025 16:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.22.1`" + } + ] + } + }, + { + "version": "4.5.3", + "tag": "@rushstack/eslint-config_v4.5.3", + "date": "Fri, 24 Oct 2025 11:22:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.14.1`" + } + ] + } + }, + { + "version": "4.5.2", + "tag": "@rushstack/eslint-config_v4.5.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.22.0`" + } + ] + } + }, + { + "version": "4.5.1", + "tag": "@rushstack/eslint-config_v4.5.1", + "date": "Tue, 14 Oct 2025 15:13:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.21.1`" + } + ] + } + }, + { + "version": "4.5.0", + "tag": "@rushstack/eslint-config_v4.5.0", + "date": "Mon, 13 Oct 2025 15:13:02 GMT", + "comments": { + "minor": [ + { + "comment": "Bump `eslint` to `~9.37.0` and the `@typescript-eslint/*` packages to `~8.46.0`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-packlets\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-security\" to `0.13.0`" + } + ] + } + }, + { + "version": "4.4.1", + "tag": "@rushstack/eslint-config_v4.4.1", + "date": "Fri, 03 Oct 2025 20:10:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-packlets\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-security\" to `0.12.0`" + } + ] + } + }, + { + "version": "4.4.0", + "tag": "@rushstack/eslint-config_v4.4.0", + "date": "Thu, 26 Jun 2025 18:57:04 GMT", + "comments": { + "minor": [ + { + "comment": "Add flat config compatible versions of profiles and mixins. These are located under the `/flat/*` path." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-packlets\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-security\" to `0.11.0`" + } + ] + } + }, + { + "version": "4.3.0", + "tag": "@rushstack/eslint-config_v4.3.0", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `@typescript-eslint/*` packages to add support for TypeScript 5.8." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-packlets\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-security\" to `0.10.0`" + } + ] + } + }, + { + "version": "4.2.0", + "tag": "@rushstack/eslint-config_v4.2.0", + "date": "Sat, 01 Mar 2025 07:23:16 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `@typescript-eslint/*` dependencies to `~8.24.0` to support newer versions of TypeScript." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-packlets\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-security\" to `0.9.0`" + } + ] + } + }, + { + "version": "4.1.1", + "tag": "@rushstack/eslint-config_v4.1.1", + "date": "Tue, 07 Jan 2025 16:11:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.10.5`" + } + ] + } + }, + { + "version": "4.1.0", + "tag": "@rushstack/eslint-config_v4.1.0", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "minor": [ + { + "comment": "Update TSDoc dependencies." + } + ] + } + }, + { + "version": "4.0.2", + "tag": "@rushstack/eslint-config_v4.0.2", + "date": "Thu, 19 Sep 2024 00:11:08 GMT", + "comments": { + "patch": [ + { + "comment": "Fix ESLint broken links" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.16.1`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-security\" to `0.8.3`" + } + ] + } + }, + { + "version": "4.0.1", + "tag": "@rushstack/eslint-config_v4.0.1", + "date": "Wed, 14 Aug 2024 22:37:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.16.0`" + } + ] + } + }, + { + "version": "4.0.0", + "tag": "@rushstack/eslint-config_v4.0.0", + "date": "Tue, 13 Aug 2024 18:17:05 GMT", + "comments": { + "major": [ + { + "comment": "[BREAKING CHANGE] Bump \"@typescript-eslint/eslint-plugin\" to \"~8.1.0\" and \"@typescript-eslint/eslint-parser\" to \"~8.1.0\". Due to these changes, node@>=17.0.0 and eslint@^8.57.0 are now required due to breaking changes in the newer rules set." + } + ] + } + }, + { + "version": "3.7.1", + "tag": "@rushstack/eslint-config_v3.7.1", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-packlets\" to `0.9.2`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-security\" to `0.8.2`" + } + ] + } + }, + { + "version": "3.7.0", + "tag": "@rushstack/eslint-config_v3.7.0", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `eslint-plugin-tsdoc` plugin." + } + ] + } + }, + { + "version": "3.6.10", + "tag": "@rushstack/eslint-config_v3.6.10", + "date": "Fri, 17 May 2024 00:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.10.3`" + } + ] + } + }, + { + "version": "3.6.9", + "tag": "@rushstack/eslint-config_v3.6.9", + "date": "Wed, 10 Apr 2024 21:59:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.10.2`" + } + ] + } + }, + { + "version": "3.6.8", + "tag": "@rushstack/eslint-config_v3.6.8", + "date": "Fri, 29 Mar 2024 05:46:41 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.10.1`" + } + ] + } + }, + { + "version": "3.6.7", + "tag": "@rushstack/eslint-config_v3.6.7", + "date": "Thu, 28 Mar 2024 18:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.10.0`" + } + ] + } + }, + { + "version": "3.6.6", + "tag": "@rushstack/eslint-config_v3.6.6", + "date": "Wed, 27 Mar 2024 19:47:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.9.0`" + } + ] + } + }, + { + "version": "3.6.5", + "tag": "@rushstack/eslint-config_v3.6.5", + "date": "Wed, 20 Mar 2024 02:09:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.8.0`" + } + ] + } + }, + { + "version": "3.6.4", + "tag": "@rushstack/eslint-config_v3.6.4", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-packlets\" to `0.9.1`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-security\" to `0.8.1`" + } + ] + } + }, + { + "version": "3.6.3", + "tag": "@rushstack/eslint-config_v3.6.3", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.15.0`" + } + ] + } + }, + { + "version": "3.6.2", + "tag": "@rushstack/eslint-config_v3.6.2", + "date": "Thu, 25 Jan 2024 23:03:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.7.2`" + } + ] + } + }, + { + "version": "3.6.1", + "tag": "@rushstack/eslint-config_v3.6.1", + "date": "Wed, 24 Jan 2024 07:38:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.7.1`" + } + ] + } + }, + { + "version": "3.6.0", + "tag": "@rushstack/eslint-config_v3.6.0", + "date": "Tue, 16 Jan 2024 18:30:10 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for TypeScript 5.3 with @typescript-eslint 6.19.x" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-packlets\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-security\" to `0.8.0`" + } + ] + } + }, + { + "version": "3.5.1", + "tag": "@rushstack/eslint-config_v3.5.1", + "date": "Fri, 15 Dec 2023 01:10:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.6.1`" + } + ] + } + }, + { + "version": "3.5.0", + "tag": "@rushstack/eslint-config_v3.5.0", + "date": "Wed, 22 Nov 2023 01:45:18 GMT", + "comments": { + "minor": [ + { + "comment": "Added eslint-bulk-suppressions to @rushstack/eslint-config dependencies, allowing it to be used in all projects that use rushstack's eslint-config" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.6.0`" + } + ] + } + }, + { + "version": "3.4.1", + "tag": "@rushstack/eslint-config_v3.4.1", + "date": "Sun, 01 Oct 2023 02:56:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.5.1`" + } + ] + } + }, + { + "version": "3.4.0", + "tag": "@rushstack/eslint-config_v3.4.0", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "minor": [ + { + "comment": "Add an optional patch which can be used to allow ESLint to extend configurations from packages that do not have the \"eslint-config-\" prefix" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-packlets\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-security\" to `0.7.1`" + } + ] + } + }, + { + "version": "3.3.4", + "tag": "@rushstack/eslint-config_v3.3.4", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-packlets\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-plugin-security\" to `0.7.0`" + } + ] + } + }, + { + "version": "3.3.3", + "tag": "@rushstack/eslint-config_v3.3.3", + "date": "Tue, 08 Aug 2023 07:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.3.3`" + } + ] + } + }, + { + "version": "3.3.2", + "tag": "@rushstack/eslint-config_v3.3.2", + "date": "Thu, 15 Jun 2023 00:21:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.3.2`" + } + ] + } + }, + { + "version": "3.3.1", + "tag": "@rushstack/eslint-config_v3.3.1", + "date": "Wed, 07 Jun 2023 22:45:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-patch\" to `1.3.1`" + } + ] + } + }, { "version": "3.3.0", "tag": "@rushstack/eslint-config_v3.3.0", diff --git a/eslint/eslint-config/CHANGELOG.md b/eslint/eslint-config/CHANGELOG.md index 9be9b02e626..0b791c4d751 100644 --- a/eslint/eslint-config/CHANGELOG.md +++ b/eslint/eslint-config/CHANGELOG.md @@ -1,6 +1,237 @@ # Change Log - @rushstack/eslint-config -This log was last generated on Mon, 22 May 2023 06:34:32 GMT and should not be manually modified. +This log was last generated on Wed, 25 Feb 2026 21:39:42 GMT and should not be manually modified. + +## 4.6.4 +Wed, 25 Feb 2026 21:39:42 GMT + +### Patches + +- Bump `eslint-plugin-tsdoc` to `~0.5.1` to mitigate CVE-2026-26996. +- Bump `@typescript-eslint/*` dependencies to `~8.56.1`. + +## 4.6.3 +Fri, 20 Feb 2026 00:15:04 GMT + +_Version update only_ + +## 4.6.2 +Thu, 19 Feb 2026 00:04:52 GMT + +### Patches + +- Add `sideEffects` field to package.json. + +## 4.6.1 +Wed, 12 Nov 2025 01:57:54 GMT + +_Version update only_ + +## 4.6.0 +Wed, 12 Nov 2025 01:12:56 GMT + +### Minor changes + +- Bump the `eslint-plugin-tsdoc` dependency to `~0.5.0`. + +## 4.5.4 +Tue, 11 Nov 2025 16:13:26 GMT + +_Version update only_ + +## 4.5.3 +Fri, 24 Oct 2025 11:22:09 GMT + +_Version update only_ + +## 4.5.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 4.5.1 +Tue, 14 Oct 2025 15:13:22 GMT + +_Version update only_ + +## 4.5.0 +Mon, 13 Oct 2025 15:13:02 GMT + +### Minor changes + +- Bump `eslint` to `~9.37.0` and the `@typescript-eslint/*` packages to `~8.46.0`. + +## 4.4.1 +Fri, 03 Oct 2025 20:10:00 GMT + +_Version update only_ + +## 4.4.0 +Thu, 26 Jun 2025 18:57:04 GMT + +### Minor changes + +- Add flat config compatible versions of profiles and mixins. These are located under the `/flat/*` path. + +## 4.3.0 +Tue, 11 Mar 2025 02:12:33 GMT + +### Minor changes + +- Bump the `@typescript-eslint/*` packages to add support for TypeScript 5.8. + +## 4.2.0 +Sat, 01 Mar 2025 07:23:16 GMT + +### Minor changes + +- Bump the `@typescript-eslint/*` dependencies to `~8.24.0` to support newer versions of TypeScript. + +## 4.1.1 +Tue, 07 Jan 2025 16:11:06 GMT + +_Version update only_ + +## 4.1.0 +Sat, 23 Nov 2024 01:18:55 GMT + +### Minor changes + +- Update TSDoc dependencies. + +## 4.0.2 +Thu, 19 Sep 2024 00:11:08 GMT + +### Patches + +- Fix ESLint broken links + +## 4.0.1 +Wed, 14 Aug 2024 22:37:32 GMT + +_Version update only_ + +## 4.0.0 +Tue, 13 Aug 2024 18:17:05 GMT + +### Breaking changes + +- [BREAKING CHANGE] Bump "@typescript-eslint/eslint-plugin" to "~8.1.0" and "@typescript-eslint/eslint-parser" to "~8.1.0". Due to these changes, node@>=17.0.0 and eslint@^8.57.0 are now required due to breaking changes in the newer rules set. + +## 3.7.1 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 3.7.0 +Wed, 29 May 2024 00:10:52 GMT + +### Minor changes + +- Bump the `eslint-plugin-tsdoc` plugin. + +## 3.6.10 +Fri, 17 May 2024 00:10:40 GMT + +_Version update only_ + +## 3.6.9 +Wed, 10 Apr 2024 21:59:39 GMT + +_Version update only_ + +## 3.6.8 +Fri, 29 Mar 2024 05:46:41 GMT + +_Version update only_ + +## 3.6.7 +Thu, 28 Mar 2024 18:11:12 GMT + +_Version update only_ + +## 3.6.6 +Wed, 27 Mar 2024 19:47:21 GMT + +_Version update only_ + +## 3.6.5 +Wed, 20 Mar 2024 02:09:14 GMT + +_Version update only_ + +## 3.6.4 +Sat, 17 Feb 2024 06:24:35 GMT + +_Version update only_ + +## 3.6.3 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 3.6.2 +Thu, 25 Jan 2024 23:03:57 GMT + +_Version update only_ + +## 3.6.1 +Wed, 24 Jan 2024 07:38:34 GMT + +_Version update only_ + +## 3.6.0 +Tue, 16 Jan 2024 18:30:10 GMT + +### Minor changes + +- Add support for TypeScript 5.3 with @typescript-eslint 6.19.x + +## 3.5.1 +Fri, 15 Dec 2023 01:10:06 GMT + +_Version update only_ + +## 3.5.0 +Wed, 22 Nov 2023 01:45:18 GMT + +### Minor changes + +- Added eslint-bulk-suppressions to @rushstack/eslint-config dependencies, allowing it to be used in all projects that use rushstack's eslint-config + +## 3.4.1 +Sun, 01 Oct 2023 02:56:30 GMT + +_Version update only_ + +## 3.4.0 +Tue, 26 Sep 2023 09:30:33 GMT + +### Minor changes + +- Add an optional patch which can be used to allow ESLint to extend configurations from packages that do not have the "eslint-config-" prefix + +## 3.3.4 +Fri, 15 Sep 2023 00:36:58 GMT + +_Version update only_ + +## 3.3.3 +Tue, 08 Aug 2023 07:10:39 GMT + +_Version update only_ + +## 3.3.2 +Thu, 15 Jun 2023 00:21:01 GMT + +_Version update only_ + +## 3.3.1 +Wed, 07 Jun 2023 22:45:16 GMT + +_Version update only_ ## 3.3.0 Mon, 22 May 2023 06:34:32 GMT diff --git a/eslint/eslint-config/README.md b/eslint/eslint-config/README.md index ee999ee5e16..cff35291a57 100644 --- a/eslint/eslint-config/README.md +++ b/eslint/eslint-config/README.md @@ -44,7 +44,7 @@ designed around the the requirements of large teams and projects. - **Explicit:** The ruleset does not import any "recommended" templates from other ESLint packages. This avoids worrying about precedence issues due to import order. It also eliminates confusion caused by files overriding/undoing settings from another file. Each rule is configured once, in one - [easy-to-read file](https://github.com/microsoft/rushstack/blob/main/stack/eslint-config/profile/_common.js). + [easy-to-read file](https://github.com/microsoft/rushstack/blob/main/eslint/eslint-config/profile/_common.js). - **Minimal configuration:** To use this ruleset, your **.eslintrc.js** will need to choose one **"profile"** and possibly one or two **"mixins"** that cover special cases. Beyond that, our goal is to reduce monorepo @@ -268,7 +268,7 @@ module.exports = { ## Links - [CHANGELOG.md]( - https://github.com/microsoft/rushstack/blob/main/stack/eslint-config/CHANGELOG.md) - Find + https://github.com/microsoft/rushstack/blob/main/eslint/eslint-config/CHANGELOG.md) - Find out what's new in the latest version `@rushstack/eslint-config` is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/eslint/eslint-config/config/rush-project.json b/eslint/eslint-config/config/rush-project.json deleted file mode 100644 index 247dc17187a..00000000000 --- a/eslint/eslint-config/config/rush-project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "operationSettings": [ - { - "operationName": "build", - "outputFolderNames": ["lib", "dist"] - } - ] -} diff --git a/eslint/eslint-config/flat/mixins/friendly-locals.js b/eslint/eslint-config/flat/mixins/friendly-locals.js new file mode 100644 index 00000000000..e63e96783ca --- /dev/null +++ b/eslint/eslint-config/flat/mixins/friendly-locals.js @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// For the first 5 years of Rush, our lint rules required explicit types for most declarations +// such as function parameters, function return values, and exported variables. Although more verbose, +// declaring types (instead of relying on type inference) encourages engineers to create interfaces +// that inspire discussions about data structure design. It also makes source files easier +// to understand for code reviewers who may be unfamiliar with a particular project. Once developers get +// used to the extra work of declaring types, it turns out to be a surprisingly popular practice. +// +// However in 2020, to make adoption easier for existing projects, this rule was relaxed. Explicit +// type declarations are now optional for local variables (although still required in other contexts). +// See this GitHub issue for background: +// +// https://github.com/microsoft/rushstack/issues/2206 +// +// If you are onboarding a large existing code base, this new default will make adoption easier. +// +// On the other hand, if your top priority is to make source files more friendly for other +// people to read, enable the "@rushstack/eslint-config/mixins/friendly-locals" mixin. +// It will restore the requirement that local variables should have explicit type declarations. +// +// IMPORTANT: Mixins must be included in your ESLint configuration AFTER the profile + +const typescriptEslintPlugin = require('@typescript-eslint/eslint-plugin'); + +module.exports = [ + { + files: ['**/*.ts', '**/*.tsx'], + plugins: { + '@typescript-eslint': typescriptEslintPlugin + }, + rules: { + '@rushstack/typedef-var': 'off', // <--- disabled by the mixin + + '@typescript-eslint/typedef': [ + 'warn', + { + arrayDestructuring: false, + arrowParameter: false, + memberVariableDeclaration: true, + objectDestructuring: false, + parameter: true, + propertyDeclaration: true, + + variableDeclaration: true, // <--- reenabled by the mixin + + variableDeclarationIgnoreFunction: true + } + ] + } + }, + { + files: [ + // Test files + '**/*.test.ts', + '**/*.test.tsx', + '**/*.spec.ts', + '**/*.spec.tsx', + + // Facebook convention + '**/__mocks__/**/*.ts', + '**/__mocks__/**/*.tsx', + '**/__tests__/**/*.ts', + '**/__tests__/**/*.tsx', + + // Microsoft convention + '**/test/**/*.ts', + '**/test/**/*.tsx' + ], + plugins: { + '@typescript-eslint': typescriptEslintPlugin + }, + rules: { + '@typescript-eslint/typedef': [ + 'warn', + { + arrayDestructuring: false, + arrowParameter: false, + memberVariableDeclaration: true, + objectDestructuring: false, + parameter: true, + propertyDeclaration: true, + variableDeclaration: false, // <--- special case for test files + variableDeclarationIgnoreFunction: true + } + ] + } + } +]; diff --git a/eslint/eslint-config/flat/mixins/packlets.js b/eslint/eslint-config/flat/mixins/packlets.js new file mode 100644 index 00000000000..0c1f22487dd --- /dev/null +++ b/eslint/eslint-config/flat/mixins/packlets.js @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// This mixin implements the "packlet" formalism for organizing source files. +// For more information, see the documentation here: +// https://www.npmjs.com/package/@rushstack/eslint-plugin-packlets +// +// IMPORTANT: Mixins must be included in your ESLint configuration AFTER the profile + +const rushstackPackletsEslintPlugin = require('@rushstack/eslint-plugin-packlets'); + +module.exports = { + files: ['**/*.ts', '**/*.tsx'], + plugins: { + '@rushstack/packlets': rushstackPackletsEslintPlugin + }, + rules: { + '@rushstack/packlets/mechanics': 'warn', + '@rushstack/packlets/circular-deps': 'warn' + } +}; diff --git a/eslint/eslint-config/flat/mixins/react.js b/eslint/eslint-config/flat/mixins/react.js new file mode 100644 index 00000000000..8ee798c1e0b --- /dev/null +++ b/eslint/eslint-config/flat/mixins/react.js @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// This mixin applies some additional checks for projects using the React library. For more information, +// please see the README.md for "@rushstack/eslint-config". +// +// IMPORTANT: Mixins must be included in your ESLint configuration AFTER the profile + +const reactEslintPlugin = require('eslint-plugin-react'); + +module.exports = [ + { + files: ['**/*.ts', '**/*.tsx'], + plugins: { + react: reactEslintPlugin + }, + settings: { + react: { + // The default value is "detect". Automatic detection works by loading the entire React library + // into the linter's process, which is inefficient. It is recommended to specify the version + // explicity. For details, see README.md for "@rushstack/eslint-config". + version: 'detect' + } + }, + rules: { + // RATIONALE: When React components are added to an array, they generally need a "key". + 'react/jsx-key': 'warn', + + // RATIONALE: Catches a common coding practice that significantly impacts performance. + 'react/jsx-no-bind': 'warn', + + // RATIONALE: Catches a common coding mistake. + 'react/jsx-no-comment-textnodes': 'warn', + + // RATIONALE: Security risk. + 'react/jsx-no-target-blank': 'warn', + + // RATIONALE: Fixes the no-unused-vars rule to make it compatible with React + 'react/jsx-uses-react': 'warn', + + // RATIONALE: Fixes the no-unused-vars rule to make it compatible with React + 'react/jsx-uses-vars': 'warn', + + // RATIONALE: Catches a common coding mistake. + 'react/no-children-prop': 'warn', + + // RATIONALE: Catches a common coding mistake. + 'react/no-danger-with-children': 'warn', + + // RATIONALE: Avoids usage of deprecated APIs. + // + // Note that the set of deprecated APIs is determined by the "react.version" setting. + 'react/no-deprecated': 'warn', + + // RATIONALE: Catches a common coding mistake. + 'react/no-direct-mutation-state': 'warn', + + // RATIONALE: Catches some common coding mistakes. + 'react/no-unescaped-entities': 'warn', + + // RATIONALE: Avoids a potential performance problem. + 'react/no-find-dom-node': 'warn', + + // RATIONALE: Deprecated API. + 'react/no-is-mounted': 'warn', + + // RATIONALE: Deprecated API. + 'react/no-render-return-value': 'warn', + + // RATIONALE: Deprecated API. + 'react/no-string-refs': 'warn', + + // RATIONALE: Improves syntax for some cases that are not already handled by Prettier. + 'react/self-closing-comp': 'warn' + } + } +]; diff --git a/eslint/eslint-config/flat/mixins/tsdoc.js b/eslint/eslint-config/flat/mixins/tsdoc.js new file mode 100644 index 00000000000..2b1009259bf --- /dev/null +++ b/eslint/eslint-config/flat/mixins/tsdoc.js @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// This mixin validates code comments to ensure that they follow the TSDoc standard. For more +// information please see the README.md for @rushstack/eslint-config. +// +// IMPORTANT: Mixins must be included in your ESLint configuration AFTER the profile + +const tsdocEslintPlugin = require('eslint-plugin-tsdoc'); + +module.exports = [ + { + files: ['**/*.ts', '**/*.tsx'], + plugins: { + tsdoc: tsdocEslintPlugin + }, + rules: { + 'tsdoc/syntax': 'warn' + } + } +]; diff --git a/eslint/eslint-config/flat/patch/eslint-bulk-suppressions.js b/eslint/eslint-config/flat/patch/eslint-bulk-suppressions.js new file mode 100644 index 00000000000..12c37b253da --- /dev/null +++ b/eslint/eslint-config/flat/patch/eslint-bulk-suppressions.js @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +require('@rushstack/eslint-patch/eslint-bulk-suppressions'); diff --git a/eslint/eslint-config/flat/profile/_common.js b/eslint/eslint-config/flat/profile/_common.js new file mode 100644 index 00000000000..4513f97c146 --- /dev/null +++ b/eslint/eslint-config/flat/profile/_common.js @@ -0,0 +1,777 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// Rule severity guidelines +// ------------------------ +// +// Errors are generally printed in red, and may prevent other build tasks from running (e.g. unit tests). +// Developers should never ignore errors. Warnings are generally printed in yellow, and do not block local +// development, although they must be fixed/suppressed before merging. Developers will commonly ignore warnings +// until their feature is working. +// +// Rules that should be a WARNING: +// - An issue that is very common in partially implemented work (e.g. missing type declaration) +// - An issue that "keeps things nice" but otherwise doesn't affect the meaning of the code (e.g. naming convention) +// - Security rules -- developers may need to temporarily introduce "insecure" expressions while debugging; +// if our policy forces them to suppress the lint rule, they may forget to reenable it later. +// +// Rules that should be an ERROR: +// - An issue that is very likely to be a typo (e.g. "x = x;") +// - An issue that catches code that is likely to malfunction (e.g. unterminated promise chain) +// - An obsolete language feature that nobody should be using for any good reason + +const { globalIgnores } = require('eslint/config'); +const promiseEslintPlugin = require('eslint-plugin-promise'); +const typescriptEslintPlugin = require('@typescript-eslint/eslint-plugin'); +const typescriptEslintParser = require('@typescript-eslint/parser'); +const rushstackEslintPlugin = require('@rushstack/eslint-plugin'); +const rushstackSecurityEslintPlugin = require('@rushstack/eslint-plugin-security'); +const { expandNamingConventionSelectors } = require('./_macros'); + +const commonNamingConventionSelectors = [ + { + // We should be stricter about 'enumMember', but it often functions legitimately as an ad hoc namespace. + selectors: ['variable', 'enumMember', 'function'], + + format: ['camelCase', 'UPPER_CASE', 'PascalCase'], + leadingUnderscore: 'allow', + + filter: { + regex: [ + // This is a special exception for naming patterns that use an underscore to separate two camel-cased + // parts. Example: "checkBox1_onChanged" or "_checkBox1_onChanged" + '^_?[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*_[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*$' + ] + .map((x) => `(${x})`) + .join('|'), + match: false + } + }, + + { + selectors: ['parameter'], + + format: ['camelCase'], + + filter: { + regex: [ + // Silently accept names with a double-underscore prefix; we would like to be more strict about this, + // pending a fix for https://github.com/typescript-eslint/typescript-eslint/issues/2240 + '^__' + ] + .map((x) => `(${x})`) + .join('|'), + match: false + } + }, + + // Genuine properties + { + selectors: ['parameterProperty', 'accessor'], + enforceLeadingUnderscoreWhenPrivate: true, + + format: ['camelCase', 'UPPER_CASE'], + + filter: { + regex: [ + // Silently accept names with a double-underscore prefix; we would like to be more strict about this, + // pending a fix for https://github.com/typescript-eslint/typescript-eslint/issues/2240 + '^__', + // Ignore quoted identifiers such as { "X+Y": 123 }. Currently @typescript-eslint/naming-convention + // cannot detect whether an identifier is quoted or not, so we simply assume that it is quoted + // if-and-only-if it contains characters that require quoting. + '[^a-zA-Z0-9_]', + // This is a special exception for naming patterns that use an underscore to separate two camel-cased + // parts. Example: "checkBox1_onChanged" or "_checkBox1_onChanged" + '^_?[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*_[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*$' + ] + .map((x) => `(${x})`) + .join('|'), + match: false + } + }, + + // Properties that incorrectly match other contexts + // See issue https://github.com/typescript-eslint/typescript-eslint/issues/2244 + { + selectors: ['property'], + enforceLeadingUnderscoreWhenPrivate: true, + + // The @typescript-eslint/naming-convention "property" selector matches cases like this: + // + // someLegacyApiWeCannotChange.invokeMethod({ SomeProperty: 123 }); + // + // and this: + // + // const { CONSTANT1, CONSTANT2 } = someNamespace.constants; + // + // Thus for now "property" is more like a variable than a class member. + format: ['camelCase', 'UPPER_CASE', 'PascalCase'], + leadingUnderscore: 'allow', + + filter: { + regex: [ + // Silently accept names with a double-underscore prefix; we would like to be more strict about this, + // pending a fix for https://github.com/typescript-eslint/typescript-eslint/issues/2240 + '^__', + // Ignore quoted identifiers such as { "X+Y": 123 }. Currently @typescript-eslint/naming-convention + // cannot detect whether an identifier is quoted or not, so we simply assume that it is quoted + // if-and-only-if it contains characters that require quoting. + '[^a-zA-Z0-9_]', + // This is a special exception for naming patterns that use an underscore to separate two camel-cased + // parts. Example: "checkBox1_onChanged" or "_checkBox1_onChanged" + '^_?[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*_[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*$' + ] + .map((x) => `(${x})`) + .join('|'), + match: false + } + }, + + { + selectors: ['method'], + enforceLeadingUnderscoreWhenPrivate: true, + + // A PascalCase method can arise somewhat legitimately in this way: + // + // class MyClass { + // public static MyReactButton(props: IButtonProps): React.ReactElement { + // . . . + // } + // } + format: ['camelCase', 'PascalCase'], + leadingUnderscore: 'allow', + + filter: { + regex: [ + // Silently accept names with a double-underscore prefix; we would like to be more strict about this, + // pending a fix for https://github.com/typescript-eslint/typescript-eslint/issues/2240 + '^__', + // This is a special exception for naming patterns that use an underscore to separate two camel-cased + // parts. Example: "checkBox1_onChanged" or "_checkBox1_onChanged" + '^_?[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*_[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*$' + ] + .map((x) => `(${x})`) + .join('|'), + match: false + } + }, + + // Types should use PascalCase + { + // Group selector for: class, interface, typeAlias, enum, typeParameter + selectors: ['class', 'typeAlias', 'enum', 'typeParameter'], + format: ['PascalCase'], + leadingUnderscore: 'allow' + }, + + { + selectors: ['interface'], + + // It is very common for a class to implement an interface of the same name. + // For example, the Widget class may implement the IWidget interface. The "I" prefix + // avoids the need to invent a separate name such as "AbstractWidget" or "WidgetInterface". + // In TypeScript it is also common to declare interfaces that are implemented by primitive + // objects, here the "I" prefix also helps by avoiding spurious conflicts with classes + // by the same name. + format: ['PascalCase'], + + custom: { + regex: '^_?I[A-Z]', + match: true + } + } +]; + +const commonConfig = [ + // Manually authored .d.ts files are generally used to describe external APIs that are not expected + // to follow our coding conventions. Linting those files tends to produce a lot of spurious suppressions, + // so we simply ignore them. + globalIgnores(['**/*.d.ts']), + + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parser: typescriptEslintParser, + parserOptions: { + // The "project" path is resolved relative to parserOptions.tsconfigRootDir. + // Your local .eslintrc.js must specify that parserOptions.tsconfigRootDir=__dirname. + project: './tsconfig.json', + + // Allow parsing of newer ECMAScript constructs used in TypeScript source code. Although tsconfig.json + // may allow only a small subset of ES2018 features, this liberal setting ensures that ESLint will correctly + // parse whatever is encountered. + ecmaVersion: 2018, + + sourceType: 'module' + } + }, + plugins: { + '@rushstack': rushstackEslintPlugin, + '@rushstack/security': rushstackSecurityEslintPlugin, + '@typescript-eslint': typescriptEslintPlugin, + promise: promiseEslintPlugin + }, + rules: { + // ==================================================================== + // CUSTOM RULES + // ==================================================================== + + // RATIONALE: See the @rushstack/eslint-plugin documentation + '@rushstack/no-new-null': 'warn', + + // RATIONALE: See the @rushstack/eslint-plugin documentation + '@rushstack/typedef-var': 'warn', + + // RATIONALE: See the @rushstack/eslint-plugin documentation + // This is enabled and classified as an error because it is required when using Heft. + // It's not required when using ts-jest, but still a good practice. + '@rushstack/hoist-jest-mock': 'error', + + // ==================================================================== + // SECURITY RULES + // ==================================================================== + + // RATIONALE: This rule is used to prevent the use of insecure regular expressions, which can lead to + // security vulnerabilities such as ReDoS (Regular Expression Denial of Service). + '@rushstack/security/no-unsafe-regexp': 'warn', + + // ==================================================================== + // TYPESCRIPT RULES + // ==================================================================== + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + '@typescript-eslint/adjacent-overload-signatures': 'warn', + + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + '@typescript-eslint/no-unsafe-function-type': 'warn', + + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + '@typescript-eslint/no-wrapper-object-types': 'warn', + + // RATIONALE: We require "x as number" instead of "x" to avoid conflicts with JSX. + '@typescript-eslint/consistent-type-assertions': 'warn', + + // RATIONALE: We prefer "interface IBlah { x: number }" over "type Blah = { x: number }" + // because code is more readable when it is built from stereotypical forms + // (interfaces, enums, functions, etc.) instead of freeform type algebra. + '@typescript-eslint/consistent-type-definitions': 'warn', + + // RATIONALE: Code is more readable when the type of every variable is immediately obvious. + // Even if the compiler may be able to infer a type, this inference will be unavailable + // to a person who is reviewing a GitHub diff. This rule makes writing code harder, + // but writing code is a much less important activity than reading it. + // + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + '@typescript-eslint/explicit-function-return-type': [ + 'warn', + { + allowExpressions: true, + allowTypedFunctionExpressions: true, + allowHigherOrderFunctions: false + } + ], + + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + '@typescript-eslint/explicit-member-accessibility': 'warn', + + // RATIONALE: Object-oriented programming organizes code into "classes" that associate + // data structures (the class's fields) and the operations performed on those + // data structures (the class's members). Studying the fields often reveals the "idea" + // behind a class. The choice of which class a field belongs to may greatly impact + // the code readability and complexity. Thus, we group the fields prominently at the top + // of the class declaration. We do NOT enforce sorting based on public/protected/private + // or static/instance, because these designations tend to change as code evolves, and + // reordering methods produces spurious diffs that make PRs hard to read. For classes + // with lots of methods, alphabetization is probably a more useful secondary ordering. + '@typescript-eslint/member-ordering': [ + 'warn', + { + default: 'never', + classes: ['field', 'constructor', 'method'] + } + ], + + // NOTE: This new rule replaces several deprecated rules from @typescript-eslint/eslint-plugin@2.3.3: + // + // - @typescript-eslint/camelcase + // - @typescript-eslint/class-name-casing + // - @typescript-eslint/interface-name-prefix + // - @typescript-eslint/member-naming + // + // Docs: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/naming-convention.md + '@typescript-eslint/naming-convention': [ + 'warn', + ...expandNamingConventionSelectors(commonNamingConventionSelectors) + ], + + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + '@typescript-eslint/no-array-constructor': 'warn', + + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + // + // RATIONALE: The "any" keyword disables static type checking, the main benefit of using TypeScript. + // This rule should be suppressed only in very special cases such as JSON.stringify() + // where the type really can be anything. Even if the type is flexible, another type + // may be more appropriate such as "unknown", "{}", or "Record". + '@typescript-eslint/no-explicit-any': 'warn', + + // RATIONALE: The #1 rule of promises is that every promise chain must be terminated by a catch() + // handler. Thus wherever a Promise arises, the code must either append a catch handler, + // or else return the object to a caller (who assumes this responsibility). Unterminated + // promise chains are a serious issue. Besides causing errors to be silently ignored, + // they can also cause a NodeJS process to terminate unexpectedly. + '@typescript-eslint/no-floating-promises': [ + 'error', + { + checkThenables: true + } + ], + + // RATIONALE: Catches a common coding mistake. + '@typescript-eslint/no-for-in-array': 'error', + + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + '@typescript-eslint/no-misused-new': 'error', + + // RATIONALE: The "namespace" keyword is not recommended for organizing code because JavaScript lacks + // a "using" statement to traverse namespaces. Nested namespaces prevent certain bundler + // optimizations. If you are declaring loose functions/variables, it's better to make them + // static members of a class, since classes support property getters and their private + // members are accessible by unit tests. Also, the exercise of choosing a meaningful + // class name tends to produce more discoverable APIs: for example, search+replacing + // the function "reverse()" is likely to return many false matches, whereas if we always + // write "Text.reverse()" is more unique. For large scale organization, it's recommended + // to decompose your code into separate NPM packages, which ensures that component + // dependencies are tracked more conscientiously. + // + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + '@typescript-eslint/no-namespace': [ + 'warn', + { + // Discourage "namespace" in .ts and .tsx files + allowDeclarations: false, + + // Allow it in .d.ts files that describe legacy libraries + allowDefinitionFiles: false + } + ], + + // RATIONALE: Parameter properties provide a shorthand such as "constructor(public title: string)" + // that avoids the effort of declaring "title" as a field. This TypeScript feature makes + // code easier to write, but arguably sacrifices readability: In the notes for + // "@typescript-eslint/member-ordering" we pointed out that fields are central to + // a class's design, so we wouldn't want to bury them in a constructor signature + // just to save some typing. + // + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + '@typescript-eslint/parameter-properties': 'warn', + + // RATIONALE: When left in shipping code, unused variables often indicate a mistake. Dead code + // may impact performance. + // + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + vars: 'all', + // Unused function arguments often indicate a mistake in JavaScript code. However in TypeScript code, + // the compiler catches most of those mistakes, and unused arguments are fairly common for type signatures + // that are overriding a base class method or implementing an interface. + args: 'none', + // Unused error arguments are common and useful for inspection when a debugger is attached. + caughtErrors: 'none' + } + ], + + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + '@typescript-eslint/no-use-before-define': [ + 'error', + { + // Base ESLint options + + // We set functions=false so that functions can be ordered based on exported/local visibility + // similar to class methods. Also the base lint rule incorrectly flags a legitimate case like: + // + // function a(n: number): void { + // if (n > 0) { + // b(n-1); // lint error + // } + // } + // function b(n: number): void { + // if (n > 0) { + // a(n-1); + // } + // } + functions: false, + classes: true, + variables: true, + + // TypeScript extensions + + enums: true, + typedefs: true + // ignoreTypeReferences: true + } + ], + + // TODO: This is a good rule for web browser apps, but it is commonly needed API for Node.js tools. + // '@typescript-eslint/no-var-requires': 'error', + + // RATIONALE: The "module" keyword is deprecated except when describing legacy libraries. + // + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + '@typescript-eslint/prefer-namespace-keyword': 'warn', + + // RATIONALE: We require explicit type annotations, even when the compiler could infer the type. + // This can be a controversial policy because it makes code more verbose. There are + // a couple downsides to type inference, however. First, it is not always available. + // For example, when reviewing a pull request or examining a Git history, we may see + // code like this: + // + // // What is the type of "y" here? The compiler knows, but the + // // person reading the code may have no clue. + // const x = f.(); + // const y = x.z; + // + // Second, relying on implicit types also discourages design discussions and documentation. + // Consider this example: + // + // // Where's the documentation for "correlation" and "inventory"? + // // Where would you even write the TSDoc comments? + // function g() { + // return { correlation: 123, inventory: 'xyz' }; + // } + // + // Implicit types make sense for small scale scenarios, where everyone is familiar with + // the project, and code should be "easy to write". Explicit types are preferable + // for large scale scenarios, where people regularly work with source files they've never + // seen before, and code should be "easy to read." + // + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + '@typescript-eslint/typedef': [ + 'warn', + { + arrayDestructuring: false, + arrowParameter: false, + memberVariableDeclaration: true, + objectDestructuring: false, + parameter: true, + propertyDeclaration: true, + + // This case is handled by our "@rushstack/typedef-var" rule + variableDeclaration: false, + + // Normally we require type declarations for class members. However, that rule is relaxed + // for situations where we need to bind the "this" pointer for a callback. For example, consider + // this event handler for a React component: + // + // class MyComponent { + // public render(): React.ReactNode { + // return ( + // click me + // ); + // } + // + // // The assignment here avoids the need for "this._onClick.bind(this)" + // private _onClick = (event: React.MouseEvent): void => { + // console.log("Clicked! " + this.props.title); + // }; + // } + // + // This coding style has limitations and should be used sparingly. For example, "_onClick" + // will not participate correctly in "virtual"/"override" inheritance. + // + // NOTE: This option affects both "memberVariableDeclaration" and "variableDeclaration" options. + variableDeclarationIgnoreFunction: true + } + ], + + // ==================================================================== + // RECOMMENDED RULES + // ==================================================================== + + // RATIONALE: This rule warns if setters are defined without getters, which is probably a mistake. + 'accessor-pairs': 'error', + + // RATIONALE: In TypeScript, if you write x["y"] instead of x.y, it disables type checking. + 'dot-notation': [ + 'warn', + { + allowPattern: '^_' + } + ], + + // RATIONALE: Catches code that is likely to be incorrect + eqeqeq: 'error', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'for-direction': 'warn', + + // RATIONALE: Catches a common coding mistake. + 'guard-for-in': 'error', + + // RATIONALE: If you have more than 2,000 lines in a single source file, it's probably time + // to split up your code. + 'max-lines': ['warn', { max: 2000 }], + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-async-promise-executor': 'error', + + // RATIONALE: "|" and "&" are relatively rare, and are more likely to appear as a mistake when + // someone meant "||" or "&&". (But nobody types the other operators by mistake.) + 'no-bitwise': [ + 'warn', + { + allow: [ + '^', + // "|", + // "&", + '<<', + '>>', + '>>>', + '^=', + // "|=", + //"&=", + '<<=', + '>>=', + '>>>=', + '~' + ] + } + ], + + // RATIONALE: Deprecated language feature. + 'no-caller': 'error', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-compare-neg-zero': 'error', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-cond-assign': 'error', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-constant-condition': 'warn', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-control-regex': 'error', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-debugger': 'warn', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-delete-var': 'error', + + // RATIONALE: Catches code that is likely to be incorrect + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-duplicate-case': 'error', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-empty': 'warn', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-empty-character-class': 'error', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-empty-pattern': 'warn', + + // RATIONALE: Eval is a security concern and a performance concern. + 'no-eval': 'warn', + + // RATIONALE: Catches code that is likely to be incorrect + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-ex-assign': 'error', + + // RATIONALE: System types are global and should not be tampered with in a scalable code base. + // If two different libraries (or two versions of the same library) both try to modify + // a type, only one of them can win. Polyfills are acceptable because they implement + // a standardized interoperable contract, but polyfills are generally coded in plain + // JavaScript. + 'no-extend-native': 'error', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-extra-boolean-cast': 'warn', + + 'no-extra-label': 'warn', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-fallthrough': 'error', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-func-assign': 'warn', + + // RATIONALE: Catches a common coding mistake. + 'no-implied-eval': 'error', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-invalid-regexp': 'error', + + // RATIONALE: Catches a common coding mistake. + 'no-label-var': 'error', + + // RATIONALE: Eliminates redundant code. + 'no-lone-blocks': 'warn', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-misleading-character-class': 'error', + + // RATIONALE: Catches a common coding mistake. + 'no-multi-str': 'error', + + // RATIONALE: It's generally a bad practice to call "new Thing()" without assigning the result to + // a variable. Either it's part of an awkward expression like "(new Thing()).doSomething()", + // or else implies that the constructor is doing nontrivial computations, which is often + // a poor class design. + 'no-new': 'warn', + + // RATIONALE: Obsolete language feature that is deprecated. + 'no-new-func': 'error', + + // RATIONALE: Obsolete language feature that is deprecated. + 'no-new-object': 'error', + + // RATIONALE: Obsolete notation. + 'no-new-wrappers': 'warn', + + // RATIONALE: Catches code that is likely to be incorrect + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-octal': 'error', + + // RATIONALE: Catches code that is likely to be incorrect + 'no-octal-escape': 'error', + + // RATIONALE: Catches code that is likely to be incorrect + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-regex-spaces': 'error', + + // RATIONALE: Catches a common coding mistake. + 'no-return-assign': 'error', + + // RATIONALE: Security risk. + 'no-script-url': 'warn', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-self-assign': 'error', + + // RATIONALE: Catches a common coding mistake. + 'no-self-compare': 'error', + + // RATIONALE: This avoids statements such as "while (a = next(), a && a.length);" that use + // commas to create compound expressions. In general code is more readable if each + // step is split onto a separate line. This also makes it easier to set breakpoints + // in the debugger. + 'no-sequences': 'error', + + // RATIONALE: Catches code that is likely to be incorrect + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-shadow-restricted-names': 'error', + + // RATIONALE: Obsolete language feature that is deprecated. + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-sparse-arrays': 'error', + + // RATIONALE: Although in theory JavaScript allows any possible data type to be thrown as an exception, + // such flexibility adds pointless complexity, by requiring every catch block to test + // the type of the object that it receives. Whereas if catch blocks can always assume + // that their object implements the "Error" contract, then the code is simpler, and + // we generally get useful additional information like a call stack. + 'no-throw-literal': 'error', + + // RATIONALE: Catches a common coding mistake. + 'no-unmodified-loop-condition': 'warn', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-unsafe-finally': 'error', + + // RATIONALE: Catches a common coding mistake. + 'no-unused-expressions': 'warn', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-unused-labels': 'warn', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-useless-catch': 'warn', + + // RATIONALE: Avoids a potential performance problem. + 'no-useless-concat': 'warn', + + // RATIONALE: The "var" keyword is deprecated because of its confusing "hoisting" behavior. + // Always use "let" or "const" instead. + // + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + 'no-var': 'error', + + // RATIONALE: Generally not needed in modern code. + 'no-void': 'error', + + // RATIONALE: Obsolete language feature that is deprecated. + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'no-with': 'error', + + // RATIONALE: Makes logic easier to understand, since constants always have a known value + // @typescript-eslint\eslint-plugin\dist\configs\eslint-recommended.js + 'prefer-const': 'warn', + + // RATIONALE: Catches a common coding mistake where "resolve" and "reject" are confused. + 'promise/param-names': 'error', + + // RATIONALE: Catches code that is likely to be incorrect + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'require-atomic-updates': 'error', + + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'require-yield': 'warn', + + // "Use strict" is redundant when using the TypeScript compiler. + strict: ['error', 'never'], + + // RATIONALE: Catches code that is likely to be incorrect + // STANDARDIZED BY: eslint\conf\eslint-recommended.js + 'use-isnan': 'error' + + // The "no-restricted-syntax" rule is a general purpose pattern matcher that we can use to experiment with + // new rules. If a rule works well, we should convert it to a proper rule so it gets its own name + // for suppressions and documentation. + // How it works: https://eslint.org/docs/rules/no-restricted-syntax + // AST visualizer: https://astexplorer.net/ + // Debugger: http://estools.github.io/esquery/ + // + // "no-restricted-syntax": [ + // ], + } + }, + + // ==================================================================== + // TESTING RULES + // ==================================================================== + { + files: [ + // Test files + '**/*.test.ts', + '**/*.test.tsx', + '**/*.spec.ts', + '**/*.spec.tsx', + + // Facebook convention + '**/__mocks__/*.ts', + '**/__mocks__/*.tsx', + '**/__tests__/*.ts', + '**/__tests__/*.tsx', + + // Microsoft convention + '**/test/*.ts', + '**/test/*.tsx' + ], + rules: { + // Unit tests sometimes use a standalone statement like "new Thing(123);" to test a constructor. + 'no-new': 'off', + + // Jest's mocking API is designed in a way that produces compositional data types that often have + // no concise description. Since test code does not ship, and typically does not introduce new + // concepts or algorithms, the usual arguments for prioritizing readability over writability can be + // relaxed in this case. + '@rushstack/typedef-var': 'off' + } + } +]; + +module.exports = { commonNamingConventionSelectors, commonConfig }; diff --git a/eslint/eslint-config/flat/profile/_macros.js b/eslint/eslint-config/flat/profile/_macros.js new file mode 100644 index 00000000000..4d95857abc1 --- /dev/null +++ b/eslint/eslint-config/flat/profile/_macros.js @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// This is a workaround for the @typescript-eslint/naming-convention rule, whose options currently +// support a "selector" field that cannot match multiple selectors. This function receives an input +// array such as: +// +// [ +// { +// selectors: ['class', 'typeAlias', 'enum'], +// format: ['PascalCase'] +// }, +// . . . +// ] +// +// ...and transforms "selectors" -> "selector, returning an array with expanded entries like this: +// +// [ +// { +// selector: 'class', +// format: ['PascalCase'] +// }, +// { +// selector: 'typeAlias', +// format: ['PascalCase'] +// }, +// { +// selector: 'enum', +// format: ['PascalCase'] +// }, +// . . . +// ] +// +// It also supports a "enforceLeadingUnderscoreWhenPrivate" macro that expands this: +// +// [ +// { +// selectors: ['property'], +// enforceLeadingUnderscoreWhenPrivate: true, +// format: ['camelCase'] +// }, +// . . . +// ] +// +// ...to produce this: +// +// [ +// { +// selector: 'property', +// +// leadingUnderscore: 'allow', +// format: ['camelCase'] +// }, +// { +// selector: 'property', +// modifiers: ['private'], +// +// leadingUnderscore: 'require', +// format: ['camelCase'] +// }, +// . . . +// ] +function expandNamingConventionSelectors(inputBlocks) { + const firstPassBlocks = []; + + // Expand "selectors" --> "selector" + for (const block of inputBlocks) { + for (const selector of block.selectors) { + const expandedBlock = { ...block }; + delete expandedBlock.selectors; + expandedBlock.selector = selector; + firstPassBlocks.push(expandedBlock); + } + } + + // Expand "enforceLeadingUnderscoreWhenPrivate" --> "leadingUnderscore" + const secondPassBlocks = []; + for (const block of firstPassBlocks) { + if (block.enforceLeadingUnderscoreWhenPrivate) { + const expandedBlock1 = { + ...block, + leadingUnderscore: 'allow' + }; + delete expandedBlock1.enforceLeadingUnderscoreWhenPrivate; + secondPassBlocks.push(expandedBlock1); + + const expandedBlock2 = { + ...block, + modifiers: [...(block.modifiers ?? []), 'private'], + leadingUnderscore: 'require' + }; + delete expandedBlock2.enforceLeadingUnderscoreWhenPrivate; + secondPassBlocks.push(expandedBlock2); + } else { + secondPassBlocks.push(block); + } + } + + return secondPassBlocks; +} + +module.exports = { + expandNamingConventionSelectors: expandNamingConventionSelectors +}; diff --git a/eslint/eslint-config/flat/profile/node-trusted-tool.js b/eslint/eslint-config/flat/profile/node-trusted-tool.js new file mode 100644 index 00000000000..a6a05c4b061 --- /dev/null +++ b/eslint/eslint-config/flat/profile/node-trusted-tool.js @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// This profile enables lint rules intended for a Node.js project whose inputs will always +// come from a developer or other trusted source. Most build system tasks are like this, +// since they operate on exclusively files prepared by a developer. +// +// This profile disables certain security rules that would otherwise prohibit APIs that could +// cause a denial-of-service by consuming too many resources, or which might interact with +// the filesystem in unsafe ways. Such activities are safe and commonplace for a trusted tool. +// +// DO NOT use this profile for a library project that might also be loaded by a Node.js service; +// use "@rushstack/eslint-config/profiles/node" instead. + +const { commonConfig } = require('./_common'); + +module.exports = [ + ...commonConfig, + { + files: ['**/*.ts', '**/*.tsx'], + rules: { + // This is disabled for trusted tools because the tool is known to be safe. + '@rushstack/security/no-unsafe-regexp': 'off' + } + } +]; diff --git a/eslint/eslint-config/flat/profile/node.js b/eslint/eslint-config/flat/profile/node.js new file mode 100644 index 00000000000..e18325a793a --- /dev/null +++ b/eslint/eslint-config/flat/profile/node.js @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// This profile enables lint rules intended for a general Node.js project, typically a web service. +// It enables security rules that assume the service could receive malicious inputs from an +// untrusted user. If that is not the case, consider using the "node-trusted-tool" profile instead. + +const { commonConfig } = require('./_common'); + +module.exports = [...commonConfig]; diff --git a/eslint/eslint-config/flat/profile/web-app.js b/eslint/eslint-config/flat/profile/web-app.js new file mode 100644 index 00000000000..8d254fdd8d9 --- /dev/null +++ b/eslint/eslint-config/flat/profile/web-app.js @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// This profile enables lint rules intended for a web application. It enables security rules +// that are relevant to web browser APIs such as DOM. +// +// Also use this profile if you are creating a library that can be consumed by both Node.js +// and web applications. + +const { commonConfig } = require('./_common'); + +module.exports = [...commonConfig]; diff --git a/eslint/eslint-config/package.json b/eslint/eslint-config/package.json index 3280c6e13fe..3738b2aebbd 100644 --- a/eslint/eslint-config/package.json +++ b/eslint/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/eslint-config", - "version": "3.3.0", + "version": "4.6.4", "description": "A TypeScript ESLint ruleset designed for large teams and projects", "license": "MIT", "repository": { @@ -23,7 +23,7 @@ "typescript" ], "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.25.1", "typescript": ">=4.7.0" }, "dependencies": { @@ -31,16 +31,17 @@ "@rushstack/eslint-plugin": "workspace:*", "@rushstack/eslint-plugin-packlets": "workspace:*", "@rushstack/eslint-plugin-security": "workspace:*", - "@typescript-eslint/eslint-plugin": "~5.59.2", - "@typescript-eslint/experimental-utils": "~5.59.2", - "@typescript-eslint/parser": "~5.59.2", - "@typescript-eslint/typescript-estree": "~5.59.2", - "eslint-plugin-promise": "~6.0.0", - "eslint-plugin-react": "~7.27.1", - "eslint-plugin-tsdoc": "~0.2.16" + "@typescript-eslint/eslint-plugin": "~8.56.1", + "@typescript-eslint/utils": "~8.56.1", + "@typescript-eslint/parser": "~8.56.1", + "@typescript-eslint/typescript-estree": "~8.56.1", + "eslint-plugin-promise": "~7.2.1", + "eslint-plugin-react": "~7.37.5", + "eslint-plugin-tsdoc": "~0.5.1" }, "devDependencies": { - "eslint": "~8.7.0", - "typescript": "~5.0.4" - } + "eslint": "~9.37.0", + "typescript": "~5.8.2" + }, + "sideEffects": false } diff --git a/eslint/eslint-config/patch/custom-config-package-names.js b/eslint/eslint-config/patch/custom-config-package-names.js new file mode 100644 index 00000000000..20341195020 --- /dev/null +++ b/eslint/eslint-config/patch/custom-config-package-names.js @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +require('@rushstack/eslint-patch/custom-config-package-names'); diff --git a/eslint/eslint-config/patch/eslint-bulk-suppressions.js b/eslint/eslint-config/patch/eslint-bulk-suppressions.js new file mode 100644 index 00000000000..12c37b253da --- /dev/null +++ b/eslint/eslint-config/patch/eslint-bulk-suppressions.js @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +require('@rushstack/eslint-patch/eslint-bulk-suppressions'); diff --git a/eslint/eslint-config/profile/_common.js b/eslint/eslint-config/profile/_common.js index d2cec825c88..584628cd15f 100644 --- a/eslint/eslint-config/profile/_common.js +++ b/eslint/eslint-config/profile/_common.js @@ -3,6 +3,161 @@ const macros = require('./_macros'); +const namingConventionRuleOptions = [ + { + // We should be stricter about 'enumMember', but it often functions legitimately as an ad hoc namespace. + selectors: ['variable', 'enumMember', 'function'], + + format: ['camelCase', 'UPPER_CASE', 'PascalCase'], + leadingUnderscore: 'allow', + + filter: { + regex: [ + // This is a special exception for naming patterns that use an underscore to separate two camel-cased + // parts. Example: "checkBox1_onChanged" or "_checkBox1_onChanged" + '^_?[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*_[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*$' + ] + .map((x) => `(${x})`) + .join('|'), + match: false + } + }, + + { + selectors: ['parameter'], + + format: ['camelCase'], + + filter: { + regex: [ + // Silently accept names with a double-underscore prefix; we would like to be more strict about this, + // pending a fix for https://github.com/typescript-eslint/typescript-eslint/issues/2240 + '^__' + ] + .map((x) => `(${x})`) + .join('|'), + match: false + } + }, + + // Genuine properties + { + selectors: ['parameterProperty', 'accessor'], + enforceLeadingUnderscoreWhenPrivate: true, + + format: ['camelCase', 'UPPER_CASE'], + + filter: { + regex: [ + // Silently accept names with a double-underscore prefix; we would like to be more strict about this, + // pending a fix for https://github.com/typescript-eslint/typescript-eslint/issues/2240 + '^__', + // Ignore quoted identifiers such as { "X+Y": 123 }. Currently @typescript-eslint/naming-convention + // cannot detect whether an identifier is quoted or not, so we simply assume that it is quoted + // if-and-only-if it contains characters that require quoting. + '[^a-zA-Z0-9_]', + // This is a special exception for naming patterns that use an underscore to separate two camel-cased + // parts. Example: "checkBox1_onChanged" or "_checkBox1_onChanged" + '^_?[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*_[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*$' + ] + .map((x) => `(${x})`) + .join('|'), + match: false + } + }, + + // Properties that incorrectly match other contexts + // See issue https://github.com/typescript-eslint/typescript-eslint/issues/2244 + { + selectors: ['property'], + enforceLeadingUnderscoreWhenPrivate: true, + + // The @typescript-eslint/naming-convention "property" selector matches cases like this: + // + // someLegacyApiWeCannotChange.invokeMethod({ SomeProperty: 123 }); + // + // and this: + // + // const { CONSTANT1, CONSTANT2 } = someNamespace.constants; + // + // Thus for now "property" is more like a variable than a class member. + format: ['camelCase', 'UPPER_CASE', 'PascalCase'], + leadingUnderscore: 'allow', + + filter: { + regex: [ + // Silently accept names with a double-underscore prefix; we would like to be more strict about this, + // pending a fix for https://github.com/typescript-eslint/typescript-eslint/issues/2240 + '^__', + // Ignore quoted identifiers such as { "X+Y": 123 }. Currently @typescript-eslint/naming-convention + // cannot detect whether an identifier is quoted or not, so we simply assume that it is quoted + // if-and-only-if it contains characters that require quoting. + '[^a-zA-Z0-9_]', + // This is a special exception for naming patterns that use an underscore to separate two camel-cased + // parts. Example: "checkBox1_onChanged" or "_checkBox1_onChanged" + '^_?[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*_[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*$' + ] + .map((x) => `(${x})`) + .join('|'), + match: false + } + }, + + { + selectors: ['method'], + enforceLeadingUnderscoreWhenPrivate: true, + + // A PascalCase method can arise somewhat legitimately in this way: + // + // class MyClass { + // public static MyReactButton(props: IButtonProps): React.ReactElement { + // . . . + // } + // } + format: ['camelCase', 'PascalCase'], + leadingUnderscore: 'allow', + + filter: { + regex: [ + // Silently accept names with a double-underscore prefix; we would like to be more strict about this, + // pending a fix for https://github.com/typescript-eslint/typescript-eslint/issues/2240 + '^__', + // This is a special exception for naming patterns that use an underscore to separate two camel-cased + // parts. Example: "checkBox1_onChanged" or "_checkBox1_onChanged" + '^_?[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*_[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*$' + ] + .map((x) => `(${x})`) + .join('|'), + match: false + } + }, + + // Types should use PascalCase + { + // Group selector for: class, interface, typeAlias, enum, typeParameter + selectors: ['class', 'typeAlias', 'enum', 'typeParameter'], + format: ['PascalCase'], + leadingUnderscore: 'allow' + }, + + { + selectors: ['interface'], + + // It is very common for a class to implement an interface of the same name. + // For example, the Widget class may implement the IWidget interface. The "I" prefix + // avoids the need to invent a separate name such as "AbstractWidget" or "WidgetInterface". + // In TypeScript it is also common to declare interfaces that are implemented by primitive + // objects, here the "I" prefix also helps by avoiding spurious conflicts with classes + // by the same name. + format: ['PascalCase'], + + custom: { + regex: '^_?I[A-Z]', + match: true + } + } +]; + // Rule severity guidelines // ------------------------ // @@ -105,54 +260,10 @@ function buildRules(profile) { '@typescript-eslint/adjacent-overload-signatures': 'warn', // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json - // - // CONFIGURATION: By default, these are banned: String, Boolean, Number, Object, Symbol - '@typescript-eslint/ban-types': [ - 'warn', - { - extendDefaults: false, // (the complete list is in this file) - types: { - String: { - message: 'Use "string" instead', - fixWith: 'string' - }, - Boolean: { - message: 'Use "boolean" instead', - fixWith: 'boolean' - }, - Number: { - message: 'Use "number" instead', - fixWith: 'number' - }, - Object: { - message: 'Use "object" instead, or else define a proper TypeScript type:' - }, - Symbol: { - message: 'Use "symbol" instead', - fixWith: 'symbol' - }, - Function: { - message: [ - 'The "Function" type accepts any function-like value.', - 'It provides no type safety when calling the function, which can be a common source of bugs.', - 'It also accepts things like class declarations, which will throw at runtime as they will not be called with "new".', - 'If you are expecting the function to accept certain arguments, you should explicitly define the function shape.' - ].join('\n') - } - - // This is a good idea, but before enabling it we need to put some thought into the recommended - // coding practices; the default suggestions are too vague. - // - // '{}': { - // message: [ - // '"{}" actually means "any non-nullish value".', - // '- If you want a type meaning "any object", you probably want "Record" instead.', - // '- If you want a type meaning "any value", you probably want "unknown" instead.' - // ].join('\n') - // } - } - } - ], + '@typescript-eslint/no-unsafe-function-type': 'warn', + + // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json + '@typescript-eslint/no-wrapper-object-types': 'warn', // RATIONALE: We require "x as number" instead of "x" to avoid conflicts with JSX. '@typescript-eslint/consistent-type-assertions': 'warn', @@ -207,160 +318,7 @@ function buildRules(profile) { // Docs: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/naming-convention.md '@typescript-eslint/naming-convention': [ 'warn', - ...macros.expandNamingConventionSelectors([ - { - // We should be stricter about 'enumMember', but it often functions legitimately as an ad hoc namespace. - selectors: ['variable', 'enumMember', 'function'], - - format: ['camelCase', 'UPPER_CASE', 'PascalCase'], - leadingUnderscore: 'allow', - - filter: { - regex: [ - // This is a special exception for naming patterns that use an underscore to separate two camel-cased - // parts. Example: "checkBox1_onChanged" or "_checkBox1_onChanged" - '^_?[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*_[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*$' - ] - .map((x) => `(${x})`) - .join('|'), - match: false - } - }, - - { - selectors: ['parameter'], - - format: ['camelCase'], - - filter: { - regex: [ - // Silently accept names with a double-underscore prefix; we would like to be more strict about this, - // pending a fix for https://github.com/typescript-eslint/typescript-eslint/issues/2240 - '^__' - ] - .map((x) => `(${x})`) - .join('|'), - match: false - } - }, - - // Genuine properties - { - selectors: ['parameterProperty', 'accessor'], - enforceLeadingUnderscoreWhenPrivate: true, - - format: ['camelCase', 'UPPER_CASE'], - - filter: { - regex: [ - // Silently accept names with a double-underscore prefix; we would like to be more strict about this, - // pending a fix for https://github.com/typescript-eslint/typescript-eslint/issues/2240 - '^__', - // Ignore quoted identifiers such as { "X+Y": 123 }. Currently @typescript-eslint/naming-convention - // cannot detect whether an identifier is quoted or not, so we simply assume that it is quoted - // if-and-only-if it contains characters that require quoting. - '[^a-zA-Z0-9_]', - // This is a special exception for naming patterns that use an underscore to separate two camel-cased - // parts. Example: "checkBox1_onChanged" or "_checkBox1_onChanged" - '^_?[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*_[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*$' - ] - .map((x) => `(${x})`) - .join('|'), - match: false - } - }, - - // Properties that incorrectly match other contexts - // See issue https://github.com/typescript-eslint/typescript-eslint/issues/2244 - { - selectors: ['property'], - enforceLeadingUnderscoreWhenPrivate: true, - - // The @typescript-eslint/naming-convention "property" selector matches cases like this: - // - // someLegacyApiWeCannotChange.invokeMethod({ SomeProperty: 123 }); - // - // and this: - // - // const { CONSTANT1, CONSTANT2 } = someNamespace.constants; - // - // Thus for now "property" is more like a variable than a class member. - format: ['camelCase', 'UPPER_CASE', 'PascalCase'], - leadingUnderscore: 'allow', - - filter: { - regex: [ - // Silently accept names with a double-underscore prefix; we would like to be more strict about this, - // pending a fix for https://github.com/typescript-eslint/typescript-eslint/issues/2240 - '^__', - // Ignore quoted identifiers such as { "X+Y": 123 }. Currently @typescript-eslint/naming-convention - // cannot detect whether an identifier is quoted or not, so we simply assume that it is quoted - // if-and-only-if it contains characters that require quoting. - '[^a-zA-Z0-9_]', - // This is a special exception for naming patterns that use an underscore to separate two camel-cased - // parts. Example: "checkBox1_onChanged" or "_checkBox1_onChanged" - '^_?[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*_[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*$' - ] - .map((x) => `(${x})`) - .join('|'), - match: false - } - }, - - { - selectors: ['method'], - enforceLeadingUnderscoreWhenPrivate: true, - - // A PascalCase method can arise somewhat legitimately in this way: - // - // class MyClass { - // public static MyReactButton(props: IButtonProps): JSX.Element { - // . . . - // } - // } - format: ['camelCase', 'PascalCase'], - leadingUnderscore: 'allow', - - filter: { - regex: [ - // Silently accept names with a double-underscore prefix; we would like to be more strict about this, - // pending a fix for https://github.com/typescript-eslint/typescript-eslint/issues/2240 - '^__', - // This is a special exception for naming patterns that use an underscore to separate two camel-cased - // parts. Example: "checkBox1_onChanged" or "_checkBox1_onChanged" - '^_?[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*_[a-z][a-z0-9]*([A-Z][a-z]?[a-z0-9]*)*$' - ] - .map((x) => `(${x})`) - .join('|'), - match: false - } - }, - - // Types should use PascalCase - { - // Group selector for: class, interface, typeAlias, enum, typeParameter - selectors: ['class', 'typeAlias', 'enum', 'typeParameter'], - format: ['PascalCase'], - leadingUnderscore: 'allow' - }, - - { - selectors: ['interface'], - - // It is very common for a class to implement an interface of the same name. - // For example, the Widget class may implement the IWidget interface. The "I" prefix - // avoids the need to invent a separate name such as "AbstractWidget" or "WidgetInterface". - // In TypeScript it is also common to declare interfaces that are implemented by primitive - // objects, here the "I" prefix also helps by avoiding spurious conflicts with classes - // by the same name. - format: ['PascalCase'], - - custom: { - regex: '^_?I[A-Z]', - match: true - } - } - ]) + ...macros.expandNamingConventionSelectors(namingConventionRuleOptions) ], // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json @@ -379,7 +337,12 @@ function buildRules(profile) { // or else return the object to a caller (who assumes this responsibility). Unterminated // promise chains are a serious issue. Besides causing errors to be silently ignored, // they can also cause a NodeJS process to terminate unexpectedly. - '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-floating-promises': [ + 'error', + { + checkThenables: true + } + ], // RATIONALE: Catches a common coding mistake. '@typescript-eslint/no-for-in-array': 'error', @@ -431,7 +394,9 @@ function buildRules(profile) { // Unused function arguments often indicate a mistake in JavaScript code. However in TypeScript code, // the compiler catches most of those mistakes, and unused arguments are fairly common for type signatures // that are overriding a base class method or implementing an interface. - args: 'none' + args: 'none', + // Unused error arguments are common and useful for inspection when a debugger is attached. + caughtErrors: 'none' } ], @@ -834,4 +799,6 @@ function buildRules(profile) { ] }; } + exports.buildRules = buildRules; +exports.namingConventionRuleOptions = namingConventionRuleOptions; diff --git a/eslint/eslint-config/profile/_macros.js b/eslint/eslint-config/profile/_macros.js index 2d9a34bcba6..87c8487b314 100644 --- a/eslint/eslint-config/profile/_macros.js +++ b/eslint/eslint-config/profile/_macros.js @@ -86,7 +86,7 @@ function expandNamingConventionSelectors(inputBlocks) { const expandedBlock2 = { ...block, - modifiers: ['private'], + modifiers: [...(block.modifiers ?? []), 'private'], leadingUnderscore: 'require' }; delete expandedBlock2.enforceLeadingUnderscoreWhenPrivate; diff --git a/eslint/eslint-patch/.npmignore b/eslint/eslint-patch/.npmignore index 8bd427b47d5..f7a40e10213 100644 --- a/eslint/eslint-patch/.npmignore +++ b/eslint/eslint-patch/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,18 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) -!*.js -gulpfile.js +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/eslint/eslint-patch/CHANGELOG.json b/eslint/eslint-patch/CHANGELOG.json index c11db6cb532..85bd9f91c5f 100644 --- a/eslint/eslint-patch/CHANGELOG.json +++ b/eslint/eslint-patch/CHANGELOG.json @@ -1,6 +1,346 @@ { "name": "@rushstack/eslint-patch", "entries": [ + { + "version": "1.16.1", + "tag": "@rushstack/eslint-patch_v1.16.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ] + } + }, + { + "version": "1.16.0", + "tag": "@rushstack/eslint-patch_v1.16.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ] + } + }, + { + "version": "1.15.0", + "tag": "@rushstack/eslint-patch_v1.15.0", + "date": "Wed, 12 Nov 2025 01:57:54 GMT", + "comments": { + "minor": [ + { + "comment": "In ESLint >= 9.37, report bulk suppressed messages as suppressed messages rather than removing them completely." + } + ] + } + }, + { + "version": "1.14.1", + "tag": "@rushstack/eslint-patch_v1.14.1", + "date": "Fri, 24 Oct 2025 11:22:09 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where suppressed rule violations still show up in the report in ESLint >=9.37.0." + }, + { + "comment": "Fix an issue where the ESLint process will crash when running in the ESLint VSCode extension in ESLint >=9.37.0." + } + ] + } + }, + { + "version": "1.14.0", + "tag": "@rushstack/eslint-patch_v1.14.0", + "date": "Mon, 13 Oct 2025 15:13:02 GMT", + "comments": { + "minor": [ + { + "comment": "Bump `eslint` to `~9.37.0` and the `@typescript-eslint/*` packages to `~8.46.0`." + } + ] + } + }, + { + "version": "1.13.0", + "tag": "@rushstack/eslint-patch_v1.13.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ] + } + }, + { + "version": "1.12.0", + "tag": "@rushstack/eslint-patch_v1.12.0", + "date": "Thu, 26 Jun 2025 18:57:04 GMT", + "comments": { + "minor": [ + { + "comment": "Update for compatibility with ESLint 9" + } + ] + } + }, + { + "version": "1.11.0", + "tag": "@rushstack/eslint-patch_v1.11.0", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `@typescript-eslint/*` packages to add support for TypeScript 5.8." + } + ] + } + }, + { + "version": "1.10.5", + "tag": "@rushstack/eslint-patch_v1.10.5", + "date": "Tue, 07 Jan 2025 16:11:06 GMT", + "comments": { + "patch": [ + { + "comment": "Fix a performance issue when locating \".eslint-bulk-suppressions.json\"." + } + ] + } + }, + { + "version": "1.10.4", + "tag": "@rushstack/eslint-patch_v1.10.4", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ] + } + }, + { + "version": "1.10.3", + "tag": "@rushstack/eslint-patch_v1.10.3", + "date": "Fri, 17 May 2024 00:10:40 GMT", + "comments": { + "patch": [ + { + "comment": "[eslint-patch] Allow use of ESLint v9" + } + ] + } + }, + { + "version": "1.10.2", + "tag": "@rushstack/eslint-patch_v1.10.2", + "date": "Wed, 10 Apr 2024 21:59:39 GMT", + "comments": { + "patch": [ + { + "comment": "Bump maximum supported ESLint version for the bulk-suppressions tool to `8.57.0`." + } + ] + } + }, + { + "version": "1.10.1", + "tag": "@rushstack/eslint-patch_v1.10.1", + "date": "Fri, 29 Mar 2024 05:46:41 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where the `eslint-bulk prune` command would crash if a bulk suppressions file exists that speicifies no suppressions." + }, + { + "comment": "Exit with success under normal conditions." + } + ] + } + }, + { + "version": "1.10.0", + "tag": "@rushstack/eslint-patch_v1.10.0", + "date": "Thu, 28 Mar 2024 18:11:12 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue with running `eslint-bulk prune` in a project with suppressions that refer to deleted files." + } + ], + "minor": [ + { + "comment": "Delete the `.eslint-bulk-suppressions.json` file during pruning if all suppressions have been eliminated." + } + ] + } + }, + { + "version": "1.9.0", + "tag": "@rushstack/eslint-patch_v1.9.0", + "date": "Wed, 27 Mar 2024 19:47:21 GMT", + "comments": { + "minor": [ + { + "comment": "Fix an issue where `eslint-bulk prune` does not work if there are no files to lint in the project root." + } + ] + } + }, + { + "version": "1.8.0", + "tag": "@rushstack/eslint-patch_v1.8.0", + "date": "Wed, 20 Mar 2024 02:09:14 GMT", + "comments": { + "minor": [ + { + "comment": "Refactor the bulk-suppressions feature to fix some performance issues." + } + ], + "patch": [ + { + "comment": "Fix an issue where linting issues that were already suppressed via suppression comments were recorded in the bulk suppressions list." + } + ] + } + }, + { + "version": "1.7.2", + "tag": "@rushstack/eslint-patch_v1.7.2", + "date": "Thu, 25 Jan 2024 23:03:57 GMT", + "comments": { + "patch": [ + { + "comment": "Some minor documentation updates" + } + ] + } + }, + { + "version": "1.7.1", + "tag": "@rushstack/eslint-patch_v1.7.1", + "date": "Wed, 24 Jan 2024 07:38:34 GMT", + "comments": { + "patch": [ + { + "comment": "Update documentation" + } + ] + } + }, + { + "version": "1.7.0", + "tag": "@rushstack/eslint-patch_v1.7.0", + "date": "Tue, 16 Jan 2024 18:30:10 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for TypeScript 5.3 with @typescript-eslint 6.19.x" + } + ] + } + }, + { + "version": "1.6.1", + "tag": "@rushstack/eslint-patch_v1.6.1", + "date": "Fri, 15 Dec 2023 01:10:06 GMT", + "comments": { + "patch": [ + { + "comment": "Fix bulk suppression patch's eslintrc detection in polyrepos" + } + ] + } + }, + { + "version": "1.6.0", + "tag": "@rushstack/eslint-patch_v1.6.0", + "date": "Wed, 22 Nov 2023 01:45:18 GMT", + "comments": { + "minor": [ + { + "comment": "Add an experimental new feature for ESLint bulk suppressions; for details see GitHub #4303" + } + ] + } + }, + { + "version": "1.5.1", + "tag": "@rushstack/eslint-patch_v1.5.1", + "date": "Sun, 01 Oct 2023 02:56:29 GMT", + "comments": { + "patch": [ + { + "comment": "Fix patch compatibility with ESLint 7 for versions matching <7.12.0" + } + ] + } + }, + { + "version": "1.5.0", + "tag": "@rushstack/eslint-patch_v1.5.0", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "minor": [ + { + "comment": "Add an optional patch which can be used to allow ESLint to extend configurations from packages that do not have the \"eslint-config-\" prefix" + } + ] + } + }, + { + "version": "1.4.0", + "tag": "@rushstack/eslint-patch_v1.4.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ] + } + }, + { + "version": "1.3.3", + "tag": "@rushstack/eslint-patch_v1.3.3", + "date": "Tue, 08 Aug 2023 07:10:39 GMT", + "comments": { + "patch": [ + { + "comment": "Fix patching for running eslint via eslint/use-at-your-own-risk, which VS Code's eslint extension does when enabling flat config support" + } + ] + } + }, + { + "version": "1.3.2", + "tag": "@rushstack/eslint-patch_v1.3.2", + "date": "Thu, 15 Jun 2023 00:21:01 GMT", + "comments": { + "patch": [ + { + "comment": "[eslint-patch] add invalid importer path test to ESLint 7.x || 8.x block" + } + ] + } + }, + { + "version": "1.3.1", + "tag": "@rushstack/eslint-patch_v1.3.1", + "date": "Wed, 07 Jun 2023 22:45:16 GMT", + "comments": { + "patch": [ + { + "comment": "Add test for invalid importer path to fallback to relative path when loading eslint 6 plugins" + } + ] + } + }, { "version": "1.3.0", "tag": "@rushstack/eslint-patch_v1.3.0", diff --git a/eslint/eslint-patch/CHANGELOG.md b/eslint/eslint-patch/CHANGELOG.md index 842ca3953ec..26bf8dfb852 100644 --- a/eslint/eslint-patch/CHANGELOG.md +++ b/eslint/eslint-patch/CHANGELOG.md @@ -1,6 +1,205 @@ # Change Log - @rushstack/eslint-patch -This log was last generated on Mon, 22 May 2023 06:34:32 GMT and should not be manually modified. +This log was last generated on Fri, 20 Feb 2026 00:15:04 GMT and should not be manually modified. + +## 1.16.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.16.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.15.0 +Wed, 12 Nov 2025 01:57:54 GMT + +### Minor changes + +- In ESLint >= 9.37, report bulk suppressed messages as suppressed messages rather than removing them completely. + +## 1.14.1 +Fri, 24 Oct 2025 11:22:09 GMT + +### Patches + +- Fix an issue where suppressed rule violations still show up in the report in ESLint >=9.37.0. +- Fix an issue where the ESLint process will crash when running in the ESLint VSCode extension in ESLint >=9.37.0. + +## 1.14.0 +Mon, 13 Oct 2025 15:13:02 GMT + +### Minor changes + +- Bump `eslint` to `~9.37.0` and the `@typescript-eslint/*` packages to `~8.46.0`. + +## 1.13.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 1.12.0 +Thu, 26 Jun 2025 18:57:04 GMT + +### Minor changes + +- Update for compatibility with ESLint 9 + +## 1.11.0 +Tue, 11 Mar 2025 02:12:33 GMT + +### Minor changes + +- Bump the `@typescript-eslint/*` packages to add support for TypeScript 5.8. + +## 1.10.5 +Tue, 07 Jan 2025 16:11:06 GMT + +### Patches + +- Fix a performance issue when locating ".eslint-bulk-suppressions.json". + +## 1.10.4 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 1.10.3 +Fri, 17 May 2024 00:10:40 GMT + +### Patches + +- [eslint-patch] Allow use of ESLint v9 + +## 1.10.2 +Wed, 10 Apr 2024 21:59:39 GMT + +### Patches + +- Bump maximum supported ESLint version for the bulk-suppressions tool to `8.57.0`. + +## 1.10.1 +Fri, 29 Mar 2024 05:46:41 GMT + +### Patches + +- Fix an issue where the `eslint-bulk prune` command would crash if a bulk suppressions file exists that speicifies no suppressions. +- Exit with success under normal conditions. + +## 1.10.0 +Thu, 28 Mar 2024 18:11:12 GMT + +### Minor changes + +- Delete the `.eslint-bulk-suppressions.json` file during pruning if all suppressions have been eliminated. + +### Patches + +- Fix an issue with running `eslint-bulk prune` in a project with suppressions that refer to deleted files. + +## 1.9.0 +Wed, 27 Mar 2024 19:47:21 GMT + +### Minor changes + +- Fix an issue where `eslint-bulk prune` does not work if there are no files to lint in the project root. + +## 1.8.0 +Wed, 20 Mar 2024 02:09:14 GMT + +### Minor changes + +- Refactor the bulk-suppressions feature to fix some performance issues. + +### Patches + +- Fix an issue where linting issues that were already suppressed via suppression comments were recorded in the bulk suppressions list. + +## 1.7.2 +Thu, 25 Jan 2024 23:03:57 GMT + +### Patches + +- Some minor documentation updates + +## 1.7.1 +Wed, 24 Jan 2024 07:38:34 GMT + +### Patches + +- Update documentation + +## 1.7.0 +Tue, 16 Jan 2024 18:30:10 GMT + +### Minor changes + +- Add support for TypeScript 5.3 with @typescript-eslint 6.19.x + +## 1.6.1 +Fri, 15 Dec 2023 01:10:06 GMT + +### Patches + +- Fix bulk suppression patch's eslintrc detection in polyrepos + +## 1.6.0 +Wed, 22 Nov 2023 01:45:18 GMT + +### Minor changes + +- Add an experimental new feature for ESLint bulk suppressions; for details see GitHub #4303 + +## 1.5.1 +Sun, 01 Oct 2023 02:56:29 GMT + +### Patches + +- Fix patch compatibility with ESLint 7 for versions matching <7.12.0 + +## 1.5.0 +Tue, 26 Sep 2023 09:30:33 GMT + +### Minor changes + +- Add an optional patch which can be used to allow ESLint to extend configurations from packages that do not have the "eslint-config-" prefix + +## 1.4.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 1.3.3 +Tue, 08 Aug 2023 07:10:39 GMT + +### Patches + +- Fix patching for running eslint via eslint/use-at-your-own-risk, which VS Code's eslint extension does when enabling flat config support + +## 1.3.2 +Thu, 15 Jun 2023 00:21:01 GMT + +### Patches + +- [eslint-patch] add invalid importer path test to ESLint 7.x || 8.x block + +## 1.3.1 +Wed, 07 Jun 2023 22:45:16 GMT + +### Patches + +- Add test for invalid importer path to fallback to relative path when loading eslint 6 plugins ## 1.3.0 Mon, 22 May 2023 06:34:32 GMT diff --git a/eslint/eslint-patch/README.md b/eslint/eslint-patch/README.md index 623b88bff01..36fa4922816 100644 --- a/eslint/eslint-patch/README.md +++ b/eslint/eslint-patch/README.md @@ -1,51 +1,198 @@ # @rushstack/eslint-patch -A patch that improves how ESLint loads plugins when working in a monorepo with a reusable toolchain +Enhance [ESLint](https://eslint.org/) with better support for large scale monorepos! +This is a runtime patch that enables new/experimental features for ESLint. It operates as a "monkey patch" +that gets loaded with **.eslintrc.js** and modifies the ESLint engine in memory. This approach works +with your existing ESLint version (no need to install a forked ESLint), and is fully interoperable with +companion tools such as the ESLint extensions for VS Code and WebStorm. -## What it does +This package provides several independently loadable features: + +- **eslint-bulk-suppressions**: enables you to roll out new lint rules in your monorepo without having to + clutter up source files with thousands of machine-generated `// eslint-ignore-next-line` directives. + Instead, the "bulk suppressions" for legacy violations are managed in a separate file called + **.eslint-bulk-suppressions.json**. + +- **modern-module-resolution**: allows an ESLint config package to provide plugin dependencies, avoiding the + problem where hundreds of projects in a monorepo need to copy+paste the same `"devDependencies"` in + every **package.json** file. + + > **NOTE:** ESLint 8.21.0 has now introduced a new `ESLINT_USE_FLAT_CONFIG` mode that may reduce the need + for the `modern-module-resolution` patch. + +- **custom-config-package-names**: enables [rig packages](https://heft.rushstack.io/pages/intro/rig_packages/) + to provide shareable configs for ESLint, by removing the requirement that `eslint-config` must appear in + the NPM package name. + +Contributions welcome! If you have more ideas for experimental ESLint enhancements that might benefit +large scale monorepos, consider adding them to this patch. + + +# eslint-bulk-suppressions feature + + + +### What it does + +As your monorepo evolves and grows, there's an ongoing need to expand and improve lint rules. But whenever a +new rule is enabled, there may be hundreds or thousands of "legacy violations" in existing source files. +How to handle that? We could fix the old code, but that's often prohibitively expensive and may even cause +regressions. We could disable the rule for those projects or files, but we want new code to follow the rule. +An effective solution is to inject thousands of `// eslint-ignore-next-line` lines, but these "bulk suppressions" +have an unintended side effect: It normalizes the practice of suppressing lint rules. If people get used to +seeing `// eslint-ignore-next-line` everywhere, nobody will notice when humans suppress the rules for new code. +That would undermine the mission of establishing better code standards. + +The `eslint-bulk-suppressions` feature introduces a way to store machine-generated suppressions in a separate +file **.eslint-bulk-suppressions.json** which can even be protected using `CODEOWNERS` policies, since that file +will generally only change when new lint rules are introduced, or in occasional circumstances when existing files +are being moved or renamed. In this way `// eslint-ignore-next-line` remains a directive written by humans +and hopefully rarely needed. + + +### Why it's a patch + +As with `modern-module-resolution`, our hope is for this feature to eventually be incorporated as an official +feature of ESLint. Starting out as an unofficial patch allows faster iteration and community feedback. + + +### How to use it + +1. Add `@rushstack/eslint-patch` as a dependency of your project: + + ```bash + cd your-project + npm install --save-dev @rushstack/eslint-patch + ``` + +2. Globally install the [`@rushstack/eslint-bulk`](https://www.npmjs.com/package/@rushstack/eslint-bulk) + command line interface (CLI) package. For example: + + ```bash + npm install --global @rushstack/eslint-bulk + ``` + + This installs the `eslint-bulk` shell command for managing the **.eslint-bulk-suppressions.json** files. + With it you can generate new suppressions as well as "prune" old suppressions that are no longer needed. + +3. Load the patch by adding the following `require()` statement as the first line of + your **.eslintrc.js** file. For example: + + **.eslintrc.js** + ```js + require("@rushstack/eslint-patch/eslint-bulk-suppressions"); // 👈 add this line + + module.exports = { + rules: { + rule1: 'error', + rule2: 'warning' + }, + parserOptions: { tsconfigRootDir: __dirname } + }; + ``` + +Typical workflow: + +1. Checkout your `main` branch, which is in a clean state where ESLint reports no violations. +2. Update your configuration to enable the latest lint rules; ESLint now reports thousands of legacy violations. +3. Run `eslint-bulk suppress --all ./src` to update **.eslint-bulk-suppressions.json.** +4. ESLint now no longer reports violations, so commit the results to Git and merge your pull request. +5. Over time, engineers may improve some of the suppressed code, in which case the associated suppressions are no longer needed. +6. Run `eslint-bulk prune` periodically to find and remove unnecessary suppressions from **.eslint-bulk-suppressions.json**, ensuring that new violations will now get caught in those scopes. + +### "eslint-bulk suppress" command + +```bash +eslint-bulk suppress --rule NAME1 [--rule NAME2...] PATH1 [PATH2...] +eslint-bulk suppress --all PATH1 [PATH2...] +``` + +Use this command to automatically generate bulk suppressions for the specified lint rules and file paths. +The path argument is a [glob pattern](https://en.wikipedia.org/wiki/Glob_(programming)) with the same syntax +as path arguments for the `eslint` command. + + +### "eslint-bulk prune" command + +Use this command to automatically delete all unnecessary suppression entries in all +**.eslint-bulk-suppressions.json** files under the current working directory. + +```bash +eslint-bulk prune +``` + +### Implementation notes + +The `eslint-bulk` command is a thin wrapper whose behavior is actually provided by the patch itself. +In this way, if your monorepo contains projects using different versions of this package, the same globally +installed `eslint-bulk` command can be used under any project folder, and it will always invoke the correct +version of the engine compatible with that project. Because the patch is loaded by ESLint, the `eslint-bulk` +command must be invoked in a project folder that contains an **.eslintrc.js** configuration with correctly +installed **package.json** dependencies. + +Here's an example of the bulk suppressions file content: + +**.eslint-bulk-suppressions.json** +```js +{ + "suppressions": [ + { + "rule": "no-var", + "file": "./src/your-file.ts", + "scopeId": ".ExampleClass.exampleMethod" + } + ] +} +``` +The `rule` field is the ESLint rule name. The `file` field is the source file path, relative to the **eslintrc.js** file. The `scopeId` is a special string built from the names of containing structures. (For implementation details, take a look at the [calculateScopeId()](https://github.com/microsoft/rushstack/blob/e95c51088341f01516ee5a7639d57c3f6dce8772/eslint/eslint-patch/src/eslint-bulk-suppressions/bulk-suppressions-patch.ts#L52) function.) The `scopeId` identifies a region of code where the rule should be suppressed, while being reasonably stable across edits of the source file. + +# modern-module-resolution feature + +### What it does This patch is a workaround for a longstanding [ESLint feature request](https://github.com/eslint/eslint/issues/3458) -that would allow a shared ESLint config to bring along its own plugins, rather than imposing peer dependencies +that would allow a shareable ESLint config to bring along its own plugins, rather than imposing peer dependencies on every consumer of the config. In a monorepo scenario, this enables your lint setup to be consolidated in a single NPM package. Doing so greatly reduces the copy+pasting and version management for all the other projects that use your standard lint rule set, but don't want to be bothered with the details. -ESLint provides partial solutions such as the `--resolve-plugins-relative-to` CLI option, however they are -awkward to use. For example, the VS Code extension for ESLint must be manually configured with this CLI option. -If some developers use other editors such as WebStorm, a different manual configuration is needed. -Also, the `--resolve-plugins-relative-to` parameter does not support multiple paths, for example if a config package -builds upon another package that also provides plugins. See -[this discussion](https://github.com/eslint/eslint/issues/3458#issuecomment-516666620) -for additional technical background. +> **NOTE:** ESLint 8.21.0 has now introduced a new `ESLINT_USE_FLAT_CONFIG` mode that may reduce the need +> for this patch. -## Why it's a patch +### Why it's a patch -ESLint's long awaited module resolver overhaul still has not materialized as of ESLint 8. As a stopgap, -we created a small **.eslintrc.js** patch that solves the problem adequately for most real world scenarios. -This patch was proposed as an ESLint feature with [PR 12460](https://github.com/eslint/eslint/pull/12460), however -the maintainers were not able to accept it unless it is reworked into a fully correct design. Such a requirement -would impose the same hurdles as the original GitHub issue; thus, it seems best to stay with the patch approach. +We initially proposed this feature in a pull request for the official ESLint back in 2019, however the +maintainers preferred to implement a more comprehensive overhaul of the ESLint config engine. It ultimately +shipped with the experimental new `ESLINT_USE_FLAT_CONFIG` mode (still opt-in as of ESLint 8). +While waiting for that, Rush Stack's `modern-module-resolution` patch provided a reliable interim solution. +We will continue to maintain this patch as long as it is being widely used, but we encourage you to check out +`ESLINT_USE_FLAT_CONFIG` and see if it meets your needs. -Since the patch is now in wide use, we've converted it into a proper NPM package to simplify maintenance. +### How to use it -## How to use it +1. Add `@rushstack/eslint-patch` as a dependency of your project: -Add a `require()` call to the to top of the **.eslintrc.js** file for each project that depends on your shared -ESLint config, for example: + ```bash + cd your-project + npm install --save-dev @rushstack/eslint-patch + ``` -**.eslintrc.js** -```ts -require("@rushstack/eslint-patch/modern-module-resolution"); +2. Add a `require()` call to the to top of the **.eslintrc.js** file for each project that depends + on your shareable ESLint config, for example: -// Add your "extends" boilerplate here, for example: -module.exports = { - extends: ['@your-company/eslint-config'], - parserOptions: { tsconfigRootDir: __dirname } -}; -``` + **.eslintrc.js** + ```ts + require("@rushstack/eslint-patch/modern-module-resolution"); // 👈 add this line + + // Add your "extends" boilerplate here, for example: + module.exports = { + extends: ['@your-company/eslint-config'], + parserOptions: { tsconfigRootDir: __dirname } + }; + ``` With this change, the local project no longer needs any ESLint plugins in its **package.json** file. Instead, the hypothetical `@your-company/eslint-config` NPM package would declare the plugins as its @@ -55,14 +202,51 @@ This patch works by modifying the ESLint engine so that its module resolver will the referencing config file, rather than the project folder. The patch is compatible with ESLint 6, 7, and 8. It also works with any editor extensions that load ESLint as a library. -For an even leaner setup, `@your-company/eslint-config` can provide the patch as its own dependency. See -[@rushstack/eslint-config](https://www.npmjs.com/package/@rushstack/eslint-config) for a real world example -and recommended approach. +For an even leaner setup, `@your-company/eslint-config` can provide the patches as its own dependency. +See [@rushstack/eslint-config](https://github.com/microsoft/rushstack/blob/main/eslint/eslint-config/patch/modern-module-resolution.js) for a real world example. + + +# custom-config-package-names feature +### What it does -## Links +Load the `custom-config-package-names` patch to remove ESLint's +[naming requirement](https://eslint.org/docs/latest/extend/shareable-configs) +that `eslint-config` must be part of the NPM package name for shareable configs. + +This is useful because Rush Stack's [rig package](https://heft.rushstack.io/pages/intro/rig_packages/) +specification defines a way for many different tooling configurations and dependencies to be shared +via a single NPM package, for example +[`@rushstack/heft-web-rig`](https://www.npmjs.com/package/@rushstack/heft-web-rig). +Rigs avoid a lot of copy+pasting of dependencies in a large scale monorepo. +Rig packages always include the `-rig` suffix in their name. It doesn't make sense to enforce +that `eslint-config` should also appear in the name of a package that includes shareable configs +for many other tools besides ESLint. + +### How to use it + +Continuing the example above, to load this patch you would add a second line to your config file: + +**.eslintrc.js** +```ts +require("@rushstack/eslint-patch/modern-module-resolution"); +require("@rushstack/eslint-patch/custom-config-package-names"); // 👈 add this line + +// Add your "extends" boilerplate here, for example: +module.exports = { + extends: [ + '@your-company/build-rig/profile/default/includes/eslint/node' // Notice the package name does not start with "eslint-config-" + ], + parserOptions: { tsconfigRootDir: __dirname } +}; +``` + + +# Links - [CHANGELOG.md](https://github.com/microsoft/rushstack/blob/main/eslint/eslint-patch/CHANGELOG.md) - Find out what's new in the latest version +- [`@rushstack/eslint-bulk`](https://www.npmjs.com/package/@rushstack/eslint-bulk) CLI package + `@rushstack/eslint-patch` is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/eslint/eslint-patch/config/rig.json b/eslint/eslint-patch/config/rig.json index 6ac88a96368..cc98dea43dd 100644 --- a/eslint/eslint-patch/config/rig.json +++ b/eslint/eslint-patch/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "decoupled-local-node-rig" } diff --git a/eslint/eslint-patch/eslint.config.js b/eslint/eslint-patch/eslint.config.js new file mode 100644 index 00000000000..98ad23894fd --- /dev/null +++ b/eslint/eslint-patch/eslint.config.js @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); +const tsdocMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/tsdoc'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + ...tsdocMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + }, + rules: { + 'no-console': 'off' + } + } +]; diff --git a/eslint/eslint-patch/modern-module-resolution.js b/eslint/eslint-patch/modern-module-resolution.js deleted file mode 100644 index 921317824f4..00000000000 --- a/eslint/eslint-patch/modern-module-resolution.js +++ /dev/null @@ -1 +0,0 @@ -require('./lib/modern-module-resolution'); diff --git a/eslint/eslint-patch/package.json b/eslint/eslint-patch/package.json index 85050870218..77c780390a0 100644 --- a/eslint/eslint-patch/package.json +++ b/eslint/eslint-patch/package.json @@ -1,8 +1,45 @@ { "name": "@rushstack/eslint-patch", - "version": "1.3.0", - "description": "A patch that improves how ESLint loads plugins when working in a monorepo with a reusable toolchain", - "main": "lib/usage.js", + "version": "1.16.1", + "description": "Enhance ESLint with better support for large scale monorepos", + "main": "./lib-commonjs/usage.js", + "module": "./lib-esm/usage.js", + "exports": { + ".": { + "node": "./lib-commonjs/usage.js", + "import": "./lib-esm/usage.js", + "require": "./lib-commonjs/usage.js" + }, + "./modern-module-resolution": { + "node": "./lib-commonjs/modern-module-resolution.js", + "import": "./lib-esm/modern-module-resolution.js", + "require": "./lib-commonjs/modern-module-resolution.js" + }, + "./custom-config-package-names": { + "node": "./lib-commonjs/custom-config-package-names.js", + "import": "./lib-esm/custom-config-package-names.js", + "require": "./lib-commonjs/custom-config-package-names.js" + }, + "./eslint-bulk-suppressions": { + "node": "./lib-commonjs/eslint-bulk-suppressions/index.js", + "import": "./lib-esm/eslint-bulk-suppressions/index.js", + "require": "./lib-commonjs/eslint-bulk-suppressions/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "license": "MIT", "repository": { "url": "https://github.com/microsoft/rushstack.git", @@ -12,7 +49,7 @@ "homepage": "https://rushstack.io", "scripts": { "build": "heft build --clean", - "_phase:build": "heft build --clean" + "_phase:build": "heft run --only build -- --clean" }, "keywords": [ "eslintrc", @@ -22,11 +59,30 @@ "resolver", "plugin", "relative", - "package" + "package", + "bulk", + "suppressions", + "monorepo", + "monkey", + "patch" ], "devDependencies": { - "@rushstack/heft": "0.50.6", - "@rushstack/heft-node-rig": "1.13.0", - "@types/node": "14.18.36" - } + "@rushstack/heft": "1.2.22", + "@types/eslint-8": "npm:@types/eslint@8.56.10", + "@types/eslint-9": "npm:@types/eslint@9.6.1", + "@typescript-eslint/types": "~8.56.1", + "decoupled-local-node-rig": "workspace:*", + "eslint-8": "npm:eslint@~8.57.0", + "eslint-9": "npm:eslint@~9.25.1", + "eslint": "~9.37.0", + "typescript": "~5.8.2" + }, + "sideEffects": [ + "lib-esm/modern-module-resolution.js", + "lib-esm/custom-config-package-names.js", + "lib-esm/eslint-bulk-suppressions/index.js", + "lib-commonjs/modern-module-resolution.js", + "lib-commonjs/custom-config-package-names.js", + "lib-commonjs/eslint-bulk-suppressions/index.js" + ] } diff --git a/eslint/eslint-patch/src/_patch-base.ts b/eslint/eslint-patch/src/_patch-base.ts new file mode 100644 index 00000000000..d4fa0056538 --- /dev/null +++ b/eslint/eslint-patch/src/_patch-base.ts @@ -0,0 +1,342 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// This is a workaround for https://github.com/eslint/eslint/issues/3458 +// +// To correct how ESLint searches for plugin packages, add this line to the top of your project's .eslintrc.js file: +// +// require("@rushstack/eslint-patch/modern-module-resolution"); +// + +import path from 'node:path'; + +const isModuleResolutionError: (ex: unknown) => boolean = (ex) => + typeof ex === 'object' && !!ex && 'code' in ex && (ex as { code: unknown }).code === 'MODULE_NOT_FOUND'; + +const FLAT_CONFIG_REGEX: RegExp = /eslint\.config\.(cjs|mjs|js)$/i; + +// Ex: +// at async ESLint.lintFiles (C:\\path\\to\\\\eslint\\lib\\eslint\\eslint.js:720:21) +const NODE_STACK_REGEX: RegExp = + /^\s*at (?:((?:\[object object\])?[^\\/]+(?: \[as \S+\])?) )?\(?(.*?)(?::(\d+)| (\d+))(?::(\d+))?\)?\s*$/i; + +interface INodeStackFrame { + file: string; + method?: string; + lineNumber: number; + column?: number; +} + +function parseNodeStack(stack: string): INodeStackFrame | undefined { + const stackTraceMatch: RegExpExecArray | null = NODE_STACK_REGEX.exec(stack); + if (!stackTraceMatch) { + return undefined; + } + + return { + file: stackTraceMatch[2], + method: stackTraceMatch[1], + lineNumber: parseInt(stackTraceMatch[3], 10), + column: stackTraceMatch[4] ? parseInt(stackTraceMatch[4], 10) : undefined + }; +} + +function getStackTrace(): INodeStackFrame[] { + const stackObj: { stack?: string } = {}; + const originalStackTraceLimit: number = Error.stackTraceLimit; + Error.stackTraceLimit = Infinity; + Error.captureStackTrace(stackObj, getStackTrace); + Error.stackTraceLimit = originalStackTraceLimit; + if (!stackObj.stack) { + throw new Error('Unable to capture stack trace'); + } + + const { stack } = stackObj; + const stackLines: string[] = stack.split('\n'); + const frames: INodeStackFrame[] = []; + for (const line of stackLines) { + const frame: INodeStackFrame | undefined = parseNodeStack(line); + if (frame) { + frames.push(frame); + } + } + + return frames; +} + +// Module path for eslintrc.cjs +// Example: ".../@eslint/eslintrc/dist/eslintrc.cjs" +let eslintrcBundlePath: string | undefined = undefined; + +// Module path for config-array-factory.js +// Example: ".../@eslint/eslintrc/lib/config-array-factory" +let configArrayFactoryPath: string | undefined = undefined; + +// Module path for relative-module-resolver.js +// Example: ".../@eslint/eslintrc/lib/shared/relative-module-resolver" +let moduleResolverPath: string | undefined = undefined; + +// Module path for naming.js +// Example: ".../@eslint/eslintrc/lib/shared/naming" +let namingPath: string | undefined = undefined; + +// Folder path where ESLint's package.json can be found +// Example: ".../node_modules/eslint" +let eslintFolder: string | undefined = undefined; + +// Probe for the ESLint >=9.0.0 flat config layout: +for (let currentModule: NodeModule = module; ; ) { + if (FLAT_CONFIG_REGEX.test(currentModule.filename)) { + // Obtain the stack trace of the current module, since the + // parent module of a flat config is undefined. From the + // stack trace, we can find the ESLint folder. + const stackTrace: INodeStackFrame[] = getStackTrace(); + const targetFrame: INodeStackFrame | undefined = stackTrace.find( + (frame: INodeStackFrame) => frame.file && frame.file.endsWith('eslint.js') + ); + if (targetFrame) { + // Walk up the path and continuously attempt to resolve the ESLint folder + let currentPath: string | undefined = targetFrame.file; + while (currentPath) { + const potentialPath: string = path.dirname(currentPath); + if (potentialPath === currentPath) { + break; + } + currentPath = potentialPath; + try { + eslintFolder = path.dirname(require.resolve('eslint/package.json', { paths: [currentPath] })); + break; + } catch (ex: unknown) { + if (!isModuleResolutionError(ex)) { + throw ex; + } + } + } + } + + if (eslintFolder) { + const eslintrcFolderPath: string = path.dirname( + require.resolve('@eslint/eslintrc/package.json', { paths: [eslintFolder] }) + ); + eslintrcBundlePath = path.join(eslintrcFolderPath, 'dist/eslintrc.cjs'); + } + + break; + } + + if (!currentModule.parent) { + break; + } + currentModule = currentModule.parent; +} + +if (!eslintFolder) { + // Probe for the ESLint >=8.0.0 layout: + for (let currentModule: NodeModule = module; ; ) { + if (!eslintrcBundlePath) { + if (currentModule.filename.endsWith('eslintrc.cjs')) { + // For ESLint >=8.0.0, all @eslint/eslintrc code is bundled at this path: + // .../@eslint/eslintrc/dist/eslintrc.cjs + try { + const eslintrcFolderPath: string = path.dirname( + require.resolve('@eslint/eslintrc/package.json', { paths: [currentModule.path] }) + ); + + // Make sure we actually resolved the module in our call path + // and not some other spurious dependency. + const resolvedEslintrcBundlePath: string = path.join(eslintrcFolderPath, 'dist/eslintrc.cjs'); + if (resolvedEslintrcBundlePath === currentModule.filename) { + eslintrcBundlePath = resolvedEslintrcBundlePath; + } + } catch (ex: unknown) { + // Module resolution failures are expected, as we're walking + // up our require stack to look for eslint. All other errors + // are re-thrown. + if (!isModuleResolutionError(ex)) { + throw ex; + } + } + } + } else { + // Next look for a file in ESLint's folder + // .../eslint/lib/cli-engine/cli-engine.js + try { + const eslintCandidateFolder: string = path.dirname( + require.resolve('eslint/package.json', { + paths: [currentModule.path] + }) + ); + + // Make sure we actually resolved the module in our call path + // and not some other spurious dependency. + if (currentModule.filename.startsWith(eslintCandidateFolder + path.sep)) { + eslintFolder = eslintCandidateFolder; + break; + } + } catch (ex: unknown) { + // Module resolution failures are expected, as we're walking + // up our require stack to look for eslint. All other errors + // are re-thrown. + if (!isModuleResolutionError(ex)) { + throw ex; + } + } + } + + if (!currentModule.parent) { + break; + } + currentModule = currentModule.parent; + } +} + +if (!eslintFolder) { + // Probe for the ESLint >=7.12.0 layout: + for (let currentModule: NodeModule = module; ; ) { + if (!configArrayFactoryPath) { + // For ESLint >=7.12.0, config-array-factory.js is at this path: + // .../@eslint/eslintrc/lib/config-array-factory.js + try { + const eslintrcFolder: string = path.dirname( + require.resolve('@eslint/eslintrc/package.json', { + paths: [currentModule.path] + }) + ); + + const resolvedConfigArrayFactoryPath: string = path.join( + eslintrcFolder, + '/lib/config-array-factory.js' + ); + if (resolvedConfigArrayFactoryPath === currentModule.filename) { + configArrayFactoryPath = resolvedConfigArrayFactoryPath; + moduleResolverPath = `${eslintrcFolder}/lib/shared/relative-module-resolver`; + namingPath = `${eslintrcFolder}/lib/shared/naming`; + } + } catch (ex: unknown) { + // Module resolution failures are expected, as we're walking + // up our require stack to look for eslint. All other errors + // are re-thrown. + if (!isModuleResolutionError(ex)) { + throw ex; + } + } + } else if (currentModule.filename.endsWith('cli-engine.js')) { + // Next look for a file in ESLint's folder + // .../eslint/lib/cli-engine/cli-engine.js + try { + const eslintCandidateFolder: string = path.dirname( + require.resolve('eslint/package.json', { + paths: [currentModule.path] + }) + ); + + if (path.join(eslintCandidateFolder, 'lib/cli-engine/cli-engine.js') === currentModule.filename) { + eslintFolder = eslintCandidateFolder; + break; + } + } catch (ex: unknown) { + // Module resolution failures are expected, as we're walking + // up our require stack to look for eslint. All other errors + // are rethrown. + if (!isModuleResolutionError(ex)) { + throw ex; + } + } + } + + if (!currentModule.parent) { + break; + } + currentModule = currentModule.parent; + } +} + +if (!eslintFolder) { + // Probe for the <7.12.0 layout: + for (let currentModule: NodeModule = module; ; ) { + // For ESLint <7.12.0, config-array-factory.js was at this path: + // .../eslint/lib/cli-engine/config-array-factory.js + if (/[\\/]eslint[\\/]lib[\\/]cli-engine[\\/]config-array-factory\.js$/i.test(currentModule.filename)) { + eslintFolder = path.join(path.dirname(currentModule.filename), '../..'); + configArrayFactoryPath = `${eslintFolder}/lib/cli-engine/config-array-factory`; + moduleResolverPath = `${eslintFolder}/lib/shared/relative-module-resolver`; + + // The naming module was moved to @eslint/eslintrc in ESLint 7.8.0, which is also when the @eslint/eslintrc + // package was created and added to ESLint, so we need to probe for whether it's in the old or new location. + let eslintrcFolder: string | undefined; + try { + eslintrcFolder = path.dirname( + require.resolve('@eslint/eslintrc/package.json', { + paths: [currentModule.path] + }) + ); + } catch (ex: unknown) { + if (!isModuleResolutionError(ex)) { + throw ex; + } + } + + namingPath = `${eslintrcFolder ?? eslintFolder}/lib/shared/naming`; + break; + } + + if (!currentModule.parent) { + // This was tested with ESLint 6.1.0 .. 7.12.1. + throw new Error( + 'Failed to patch ESLint because the calling module was not recognized.\n' + + 'If you are using a newer ESLint version that may be unsupported, please create a GitHub issue:\n' + + 'https://github.com/microsoft/rushstack/issues' + ); + } + currentModule = currentModule.parent; + } +} + +// Detect the ESLint package version +const eslintPackageJsonPath: string = `${eslintFolder}/package.json`; +const eslintPackageObject: { version: string } = require(eslintPackageJsonPath); +export const eslintPackageVersion: string = eslintPackageObject.version; +const ESLINT_MAJOR_VERSION: number = parseInt(eslintPackageVersion, 10); +if (isNaN(ESLINT_MAJOR_VERSION)) { + throw new Error( + `Unable to parse ESLint version "${eslintPackageVersion}" in file "${eslintPackageJsonPath}"` + ); +} + +if (!(ESLINT_MAJOR_VERSION >= 6 && ESLINT_MAJOR_VERSION <= 9)) { + throw new Error( + 'The ESLint patch script has only been tested with ESLint version 6.x, 7.x, 8.x, and 9.x.' + + ` (Your version: ${eslintPackageVersion})\n` + + 'Consider reporting a GitHub issue:\n' + + 'https://github.com/microsoft/rushstack/issues' + ); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +let configArrayFactory: any; +if (ESLINT_MAJOR_VERSION >= 8 && eslintrcBundlePath) { + configArrayFactory = require(eslintrcBundlePath).Legacy.ConfigArrayFactory; +} else if (configArrayFactoryPath) { + configArrayFactory = require(configArrayFactoryPath).ConfigArrayFactory; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +let ModuleResolver: { resolve: any }; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +let Naming: { normalizePackageName: any }; +if (ESLINT_MAJOR_VERSION >= 8 && eslintrcBundlePath) { + ModuleResolver = require(eslintrcBundlePath).Legacy.ModuleResolver; + Naming = require(eslintrcBundlePath).Legacy.naming; +} else if (moduleResolverPath && namingPath) { + ModuleResolver = require(moduleResolverPath); + Naming = require(namingPath); +} + +export { + eslintFolder, + configArrayFactory, + ModuleResolver, + Naming, + ESLINT_MAJOR_VERSION, + isModuleResolutionError +}; diff --git a/eslint/eslint-patch/src/custom-config-package-names.ts b/eslint/eslint-patch/src/custom-config-package-names.ts new file mode 100644 index 00000000000..6184b774b88 --- /dev/null +++ b/eslint/eslint-patch/src/custom-config-package-names.ts @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// This is a workaround for ESLint's requirement to consume shareable configurations from package names prefixed +// with "eslint-config". +// +// To remove this requirement, add this line to the top of your project's .eslintrc.js file: +// +// require("@rushstack/eslint-patch/custom-config-package-names"); +// +import { configArrayFactory, ModuleResolver, Naming } from './_patch-base'; + +if (!configArrayFactory.__loadExtendedShareableConfigPatched) { + configArrayFactory.__loadExtendedShareableConfigPatched = true; + // eslint-disable-next-line @typescript-eslint/typedef + const originalLoadExtendedShareableConfig = configArrayFactory.prototype._loadExtendedShareableConfig; + + // Common between ESLint versions + // https://github.com/eslint/eslintrc/blob/242d569020dfe4f561e4503787b99ec016337457/lib/config-array-factory.js#L910 + configArrayFactory.prototype._loadExtendedShareableConfig = function (extendName: string): unknown { + const originalResolve: (moduleName: string, relativeToPath: string) => string = ModuleResolver.resolve; + try { + ModuleResolver.resolve = function (moduleName: string, relativeToPath: string): string { + try { + return originalResolve.call(this, moduleName, relativeToPath); + } catch (e) { + // Only change the name we resolve if we cannot find the normalized module, since it is + // valid to rely on the normalized package name. Use the originally provided module path + // instead of the normalized module path. + if ( + (e as NodeJS.ErrnoException)?.code === 'MODULE_NOT_FOUND' && + moduleName !== extendName && + moduleName === Naming.normalizePackageName(extendName, 'eslint-config') + ) { + return originalResolve.call(this, extendName, relativeToPath); + } else { + throw e; + } + } + }; + return originalLoadExtendedShareableConfig.apply(this, arguments); + } finally { + ModuleResolver.resolve = originalResolve; + } + }; +} diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/ast-guards.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/ast-guards.ts new file mode 100644 index 00000000000..a308b49f7fb --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/ast-guards.ts @@ -0,0 +1,272 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { TSESTree } from '@typescript-eslint/types'; + +export function isArrayExpression(node: TSESTree.Node): node is TSESTree.ArrayExpression { + return node.type === 'ArrayExpression'; +} + +export function isArrowFunctionExpression(node: TSESTree.Node): node is TSESTree.ArrowFunctionExpression { + return node.type === 'ArrowFunctionExpression'; +} + +/** default parameters */ +export function isAssignmentPattern(node: TSESTree.Node): node is TSESTree.AssignmentPattern { + return node.type === 'AssignmentPattern'; +} + +export function isClassDeclaration(node: TSESTree.Node): node is TSESTree.ClassDeclaration { + return node.type === 'ClassDeclaration'; +} + +export function isClassExpression(node: TSESTree.Node): node is TSESTree.ClassExpression { + return node.type === 'ClassExpression'; +} + +export function isExportDefaultDeclaration(node: TSESTree.Node): node is TSESTree.ExportDefaultDeclaration { + return node.type === 'ExportDefaultDeclaration'; +} + +export function isExpression(node: TSESTree.Node): node is TSESTree.Expression { + return node.type.includes('Expression'); +} + +export function isFunctionDeclaration(node: TSESTree.Node): node is TSESTree.FunctionDeclaration { + return node.type === 'FunctionDeclaration'; +} + +export function isFunctionExpression(node: TSESTree.Node): node is TSESTree.FunctionExpression { + return node.type === 'FunctionExpression'; +} + +export function isIdentifier(node: TSESTree.Node): node is TSESTree.Identifier { + return node.type === 'Identifier'; +} + +export function isLiteral(node: TSESTree.Node): node is TSESTree.Literal { + return node.type === 'Literal'; +} + +export function isMethodDefinition(node: TSESTree.Node): node is TSESTree.MethodDefinition { + return node.type === 'MethodDefinition'; +} + +export function isObjectExpression(node: TSESTree.Node): node is TSESTree.ObjectExpression { + return node.type === 'ObjectExpression'; +} + +export function isPrivateIdentifier(node: TSESTree.Node): node is TSESTree.PrivateIdentifier { + return node.type === 'PrivateIdentifier'; +} + +export function isProperty(node: TSESTree.Node): node is TSESTree.Property { + return node.type === 'Property'; +} + +export function isPropertyDefinition(node: TSESTree.Node): node is TSESTree.PropertyDefinition { + return node.type === 'PropertyDefinition'; +} + +export function isTSEnumDeclaration(node: TSESTree.Node): node is TSESTree.TSEnumDeclaration { + return node.type === 'TSEnumDeclaration'; +} + +export function isTSInterfaceDeclaration(node: TSESTree.Node): node is TSESTree.TSInterfaceDeclaration { + return node.type === 'TSInterfaceDeclaration'; +} + +export function isTSModuleDeclaration(node: TSESTree.Node): node is TSESTree.TSModuleDeclaration { + return node.type === 'TSModuleDeclaration'; +} + +export function isTSQualifiedName(node: TSESTree.Node): node is TSESTree.TSQualifiedName { + return node.type === 'TSQualifiedName'; +} + +export function isTSTypeAliasDeclaration(node: TSESTree.Node): node is TSESTree.TSTypeAliasDeclaration { + return node.type === 'TSTypeAliasDeclaration'; +} + +export function isVariableDeclarator(node: TSESTree.Node): node is TSESTree.VariableDeclarator { + return node.type === 'VariableDeclarator'; +} + +// Compound Type Guards for @typescript-eslint/types ast-spec compound types +export function isClassDeclarationWithName(node: TSESTree.Node): node is TSESTree.ClassDeclarationWithName { + return isClassDeclaration(node) && node.id !== null; +} + +export function isClassPropertyNameNonComputed( + node: TSESTree.Node +): node is TSESTree.ClassPropertyNameNonComputed { + return isPrivateIdentifier(node) || isPropertyNameNonComputed(node); +} + +export function isFunctionDeclarationWithName( + node: TSESTree.Node +): node is TSESTree.FunctionDeclarationWithName { + return isFunctionDeclaration(node) && node.id !== null; +} + +export function isNumberLiteral(node: TSESTree.Node): node is TSESTree.NumberLiteral { + return isLiteral(node) && typeof node.value === 'number'; +} + +export function isPropertyNameNonComputed(node: TSESTree.Node): node is TSESTree.PropertyNameNonComputed { + return isIdentifier(node) || isNumberLiteral(node) || isStringLiteral(node); +} + +export function isStringLiteral(node: TSESTree.Node): node is TSESTree.StringLiteral { + return isLiteral(node) && typeof node.value === 'string'; +} + +// Custom compound types +export interface IClassExpressionWithName extends TSESTree.ClassExpression { + id: TSESTree.Identifier; +} + +export function isClassExpressionWithName(node: TSESTree.Node): node is IClassExpressionWithName { + return isClassExpression(node) && node.id !== null; +} +export interface IFunctionExpressionWithName extends TSESTree.FunctionExpression { + id: TSESTree.Identifier; +} + +export function isFunctionExpressionWithName(node: TSESTree.Node): node is IFunctionExpressionWithName { + return isFunctionExpression(node) && node.id !== null; +} + +export type NormalAnonymousExpression = + | TSESTree.ArrowFunctionExpression + | TSESTree.ClassExpression + | TSESTree.FunctionExpression + | TSESTree.ObjectExpression; + +export function isNormalAnonymousExpression(node: TSESTree.Node): node is NormalAnonymousExpression { + const ANONYMOUS_EXPRESSION_GUARDS: ((node: TSESTree.Node) => boolean)[] = [ + isArrowFunctionExpression, + isClassExpression, + isFunctionExpression, + isObjectExpression + ]; + return ANONYMOUS_EXPRESSION_GUARDS.some((guard) => guard(node)); +} + +export interface INormalAssignmentPattern extends TSESTree.AssignmentPattern { + left: TSESTree.Identifier; +} + +export function isNormalAssignmentPattern(node: TSESTree.Node): node is INormalAssignmentPattern { + return isAssignmentPattern(node) && isIdentifier(node.left); +} + +export interface INormalClassPropertyDefinition extends TSESTree.PropertyDefinitionNonComputedName { + key: TSESTree.PrivateIdentifier | TSESTree.Identifier; + value: TSESTree.Expression; +} + +export function isNormalClassPropertyDefinition(node: TSESTree.Node): node is INormalClassPropertyDefinition { + return ( + isPropertyDefinition(node) && + (isIdentifier(node.key) || isPrivateIdentifier(node.key)) && + node.value !== null + ); +} + +export interface INormalMethodDefinition extends TSESTree.MethodDefinitionNonComputedName { + key: TSESTree.PrivateIdentifier | TSESTree.Identifier; +} + +export function isNormalMethodDefinition(node: TSESTree.Node): node is INormalMethodDefinition { + return isMethodDefinition(node) && (isIdentifier(node.key) || isPrivateIdentifier(node.key)); +} + +export interface INormalObjectProperty extends TSESTree.PropertyNonComputedName { + key: TSESTree.Identifier; +} + +export function isNormalObjectProperty(node: TSESTree.Node): node is INormalObjectProperty { + return isProperty(node) && (isIdentifier(node.key) || isPrivateIdentifier(node.key)); +} + +export type INormalVariableDeclarator = TSESTree.LetOrConstOrVarDeclaration & { + id: TSESTree.Identifier; + init: TSESTree.Expression; +}; + +export function isNormalVariableDeclarator(node: TSESTree.Node): node is INormalVariableDeclarator { + return isVariableDeclarator(node) && isIdentifier(node.id) && node.init !== null; +} + +export interface INormalAssignmentPatternWithAnonymousExpressionAssigned extends INormalAssignmentPattern { + right: NormalAnonymousExpression; +} + +export function isNormalAssignmentPatternWithAnonymousExpressionAssigned( + node: TSESTree.Node +): node is INormalAssignmentPatternWithAnonymousExpressionAssigned { + return isNormalAssignmentPattern(node) && isNormalAnonymousExpression(node.right); +} + +export type INormalVariableDeclaratorWithAnonymousExpressionAssigned = INormalVariableDeclarator & { + init: NormalAnonymousExpression; +}; + +export function isNormalVariableDeclaratorWithAnonymousExpressionAssigned( + node: TSESTree.Node +): node is INormalVariableDeclaratorWithAnonymousExpressionAssigned { + return isNormalVariableDeclarator(node) && isNormalAnonymousExpression(node.init); +} + +export interface INormalObjectPropertyWithAnonymousExpressionAssigned extends INormalObjectProperty { + value: NormalAnonymousExpression; +} + +export function isNormalObjectPropertyWithAnonymousExpressionAssigned( + node: TSESTree.Node +): node is INormalObjectPropertyWithAnonymousExpressionAssigned { + return isNormalObjectProperty(node) && isNormalAnonymousExpression(node.value); +} + +export interface INormalClassPropertyDefinitionWithAnonymousExpressionAssigned + extends INormalClassPropertyDefinition { + value: NormalAnonymousExpression; +} + +export function isNormalClassPropertyDefinitionWithAnonymousExpressionAssigned( + node: TSESTree.Node +): node is INormalClassPropertyDefinitionWithAnonymousExpressionAssigned { + return isNormalClassPropertyDefinition(node) && isNormalAnonymousExpression(node.value); +} + +export type NodeWithName = + | TSESTree.ClassDeclarationWithName + | TSESTree.FunctionDeclarationWithName + | IClassExpressionWithName + | IFunctionExpressionWithName + | INormalVariableDeclaratorWithAnonymousExpressionAssigned + | INormalObjectPropertyWithAnonymousExpressionAssigned + | INormalClassPropertyDefinitionWithAnonymousExpressionAssigned + | INormalAssignmentPatternWithAnonymousExpressionAssigned + | INormalMethodDefinition + | TSESTree.TSEnumDeclaration + | TSESTree.TSInterfaceDeclaration + | TSESTree.TSTypeAliasDeclaration; + +export function isNodeWithName(node: TSESTree.Node): node is NodeWithName { + return ( + isClassDeclarationWithName(node) || + isFunctionDeclarationWithName(node) || + isClassExpressionWithName(node) || + isFunctionExpressionWithName(node) || + isNormalVariableDeclaratorWithAnonymousExpressionAssigned(node) || + isNormalObjectPropertyWithAnonymousExpressionAssigned(node) || + isNormalClassPropertyDefinitionWithAnonymousExpressionAssigned(node) || + isNormalAssignmentPatternWithAnonymousExpressionAssigned(node) || + isNormalMethodDefinition(node) || + isTSEnumDeclaration(node) || + isTSInterfaceDeclaration(node) || + isTSTypeAliasDeclaration(node) + ); +} diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/bulk-suppressions-file.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/bulk-suppressions-file.ts new file mode 100644 index 00000000000..2526196eca7 --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/bulk-suppressions-file.ts @@ -0,0 +1,209 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import fs from 'node:fs'; + +import { VSCODE_PID_ENV_VAR_NAME } from './constants'; + +export interface ISuppression { + file: string; + scopeId: string; + rule: string; +} + +export interface IBulkSuppressionsConfig { + serializedSuppressions: Set; + jsonObject: IBulkSuppressionsJson; + newSerializedSuppressions: Set; + newJsonObject: IBulkSuppressionsJson; +} + +export interface IBulkSuppressionsJson { + suppressions: ISuppression[]; +} + +const IS_RUNNING_IN_VSCODE: boolean = process.env[VSCODE_PID_ENV_VAR_NAME] !== undefined; +const TEN_SECONDS_MS: number = 10 * 1000; +const SUPPRESSIONS_JSON_FILENAME: string = '.eslint-bulk-suppressions.json'; + +function throwIfAnythingOtherThanNotExistError(e: NodeJS.ErrnoException): void | never { + if (e?.code !== 'ENOENT') { + // Throw an error if any other error than file not found + throw e; + } +} + +interface ICachedBulkSuppressionsConfig { + readTime: number; + suppressionsConfig: IBulkSuppressionsConfig; +} +const suppressionsJsonByFolderPath: Map = new Map(); +export function getSuppressionsConfigForEslintConfigFolderPath( + eslintConfigFolderPath: string +): IBulkSuppressionsConfig { + const cachedSuppressionsConfig: ICachedBulkSuppressionsConfig | undefined = + suppressionsJsonByFolderPath.get(eslintConfigFolderPath); + + let shouldLoad: boolean; + let suppressionsConfig: IBulkSuppressionsConfig; + if (cachedSuppressionsConfig) { + shouldLoad = IS_RUNNING_IN_VSCODE && cachedSuppressionsConfig.readTime < Date.now() - TEN_SECONDS_MS; + suppressionsConfig = cachedSuppressionsConfig.suppressionsConfig; + } else { + shouldLoad = true; + } + + if (shouldLoad) { + const suppressionsPath: string = `${eslintConfigFolderPath}/${SUPPRESSIONS_JSON_FILENAME}`; + let rawJsonFile: string | undefined; + try { + rawJsonFile = fs.readFileSync(suppressionsPath).toString(); + } catch (e) { + throwIfAnythingOtherThanNotExistError(e); + } + + if (!rawJsonFile) { + suppressionsConfig = { + serializedSuppressions: new Set(), + jsonObject: { suppressions: [] }, + newSerializedSuppressions: new Set(), + newJsonObject: { suppressions: [] } + }; + } else { + const jsonObject: IBulkSuppressionsJson = JSON.parse(rawJsonFile); + validateSuppressionsJson(jsonObject); + + const serializedSuppressions: Set = new Set(); + for (const suppression of jsonObject.suppressions) { + serializedSuppressions.add(serializeSuppression(suppression)); + } + + suppressionsConfig = { + serializedSuppressions, + jsonObject, + newSerializedSuppressions: new Set(), + newJsonObject: { suppressions: [] } + }; + } + + suppressionsJsonByFolderPath.set(eslintConfigFolderPath, { readTime: Date.now(), suppressionsConfig }); + } + + return suppressionsConfig!; +} + +export function getAllBulkSuppressionsConfigsByEslintConfigFolderPath(): [string, IBulkSuppressionsConfig][] { + const result: [string, IBulkSuppressionsConfig][] = []; + for (const [eslintConfigFolderPath, { suppressionsConfig }] of suppressionsJsonByFolderPath) { + result.push([eslintConfigFolderPath, suppressionsConfig]); + } + + return result; +} + +export function writeSuppressionsJsonToFile( + eslintConfigFolderPath: string, + suppressionsConfig: IBulkSuppressionsConfig +): void { + suppressionsJsonByFolderPath.set(eslintConfigFolderPath, { readTime: Date.now(), suppressionsConfig }); + const suppressionsPath: string = `${eslintConfigFolderPath}/${SUPPRESSIONS_JSON_FILENAME}`; + if (suppressionsConfig.jsonObject.suppressions.length === 0) { + deleteFile(suppressionsPath); + } else { + suppressionsConfig.jsonObject.suppressions.sort(compareSuppressions); + fs.writeFileSync(suppressionsPath, JSON.stringify(suppressionsConfig.jsonObject, undefined, 2)); + } +} + +export function deleteBulkSuppressionsFileInEslintConfigFolder(eslintConfigFolderPath: string): void { + const suppressionsPath: string = `${eslintConfigFolderPath}/${SUPPRESSIONS_JSON_FILENAME}`; + deleteFile(suppressionsPath); +} + +function deleteFile(filePath: string): void { + try { + fs.unlinkSync(filePath); + } catch (e) { + throwIfAnythingOtherThanNotExistError(e); + } +} + +export function serializeSuppression({ file, scopeId, rule }: ISuppression): string { + return `${file}|${scopeId}|${rule}`; +} + +function compareSuppressions(a: ISuppression, b: ISuppression): -1 | 0 | 1 { + if (a.file < b.file) { + return -1; + } else if (a.file > b.file) { + return 1; + } else if (a.scopeId < b.scopeId) { + return -1; + } else if (a.scopeId > b.scopeId) { + return 1; + } else if (a.rule < b.rule) { + return -1; + } else if (a.rule > b.rule) { + return 1; + } else { + return 0; + } +} + +function validateSuppressionsJson(json: IBulkSuppressionsJson): json is IBulkSuppressionsJson { + if (typeof json !== 'object') { + throw new Error(`Invalid JSON object: ${JSON.stringify(json, null, 2)}`); + } + + if (!json) { + throw new Error('JSON object is null.'); + } + + const EXPECTED_ROOT_PROPERTY_NAMES: Set = new Set(['suppressions']); + + for (const propertyName of Object.getOwnPropertyNames(json)) { + if (!EXPECTED_ROOT_PROPERTY_NAMES.has(propertyName as keyof IBulkSuppressionsJson)) { + throw new Error(`Unexpected property name: ${propertyName}`); + } + } + + const { suppressions } = json; + if (!suppressions) { + throw new Error('Missing "suppressions" property.'); + } + + if (!Array.isArray(suppressions)) { + throw new Error('"suppressions" property is not an array.'); + } + + const EXPECTED_SUPPRESSION_PROPERTY_NAMES: Set = new Set(['file', 'scopeId', 'rule']); + for (const suppression of suppressions) { + if (typeof suppression !== 'object') { + throw new Error(`Invalid suppression: ${JSON.stringify(suppression, null, 2)}`); + } + + if (!suppression) { + throw new Error(`Suppression is null: ${JSON.stringify(suppression, null, 2)}`); + } + + for (const propertyName of Object.getOwnPropertyNames(suppression)) { + if (!EXPECTED_SUPPRESSION_PROPERTY_NAMES.has(propertyName as keyof ISuppression)) { + throw new Error(`Unexpected property name: ${propertyName}`); + } + } + + for (const propertyName of EXPECTED_SUPPRESSION_PROPERTY_NAMES) { + if (!suppression.hasOwnProperty(propertyName)) { + throw new Error( + `Missing "${propertyName}" property in suppression: ${JSON.stringify(suppression, null, 2)}` + ); + } else if (typeof suppression[propertyName] !== 'string') { + throw new Error( + `"${propertyName}" property in suppression is not a string: ${JSON.stringify(suppression, null, 2)}` + ); + } + } + } + + return true; +} diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/bulk-suppressions-patch.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/bulk-suppressions-patch.ts new file mode 100644 index 00000000000..549ad21b2bf --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/bulk-suppressions-patch.ts @@ -0,0 +1,273 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import fs from 'node:fs'; + +import type { TSESTree } from '@typescript-eslint/types'; + +import * as Guards from './ast-guards'; +import { eslintFolder } from '../_patch-base'; +import { + ESLINT_BULK_ENABLE_ENV_VAR_NAME, + ESLINT_BULK_PRUNE_ENV_VAR_NAME, + ESLINT_BULK_SUPPRESS_ENV_VAR_NAME +} from './constants'; +import { + getSuppressionsConfigForEslintConfigFolderPath, + serializeSuppression, + type IBulkSuppressionsConfig, + type ISuppression, + writeSuppressionsJsonToFile, + getAllBulkSuppressionsConfigsByEslintConfigFolderPath +} from './bulk-suppressions-file'; + +const ESLINT_CONFIG_FILENAMES: string[] = [ + 'eslint.config.js', + 'eslint.config.cjs', + 'eslint.config.mjs', + '.eslintrc.js', + '.eslintrc.cjs' + // Several other filenames are allowed, but this patch requires that it be loaded via a JS config file, + // so we only need to check for the JS-based filenames +]; +const SUPPRESSION_SYMBOL: unique symbol = Symbol('suppression'); +const ESLINT_BULK_SUPPRESS_ENV_VAR_VALUE: string | undefined = process.env[ESLINT_BULK_SUPPRESS_ENV_VAR_NAME]; +const SUPPRESS_ALL_RULES: boolean = ESLINT_BULK_SUPPRESS_ENV_VAR_VALUE === '*'; +const RULES_TO_SUPPRESS: Set | undefined = ESLINT_BULK_SUPPRESS_ENV_VAR_VALUE + ? new Set(ESLINT_BULK_SUPPRESS_ENV_VAR_VALUE.split(',')) + : undefined; + +interface IProblem { + [SUPPRESSION_SYMBOL]?: { + config: IBulkSuppressionsConfig; + suppression: ISuppression; + serializedSuppression: string; + }; +} + +function getNodeName(node: TSESTree.Node): string | undefined { + if (Guards.isClassDeclarationWithName(node)) { + return node.id.name; + } else if (Guards.isFunctionDeclarationWithName(node)) { + return node.id.name; + } else if (Guards.isClassExpressionWithName(node)) { + return node.id.name; + } else if (Guards.isFunctionExpressionWithName(node)) { + return node.id.name; + } else if (Guards.isNormalVariableDeclaratorWithAnonymousExpressionAssigned(node)) { + return node.id.name; + } else if (Guards.isNormalObjectPropertyWithAnonymousExpressionAssigned(node)) { + return node.key.name; + } else if (Guards.isNormalClassPropertyDefinitionWithAnonymousExpressionAssigned(node)) { + return node.key.name; + } else if (Guards.isNormalAssignmentPatternWithAnonymousExpressionAssigned(node)) { + return node.left.name; + } else if (Guards.isNormalMethodDefinition(node)) { + return node.key.name; + } else if (Guards.isTSEnumDeclaration(node)) { + return node.id.name; + } else if (Guards.isTSInterfaceDeclaration(node)) { + return node.id.name; + } else if (Guards.isTSTypeAliasDeclaration(node)) { + return node.id.name; + } +} + +type NodeWithParent = TSESTree.Node & { parent?: TSESTree.Node }; + +function calculateScopeId(node: NodeWithParent | undefined): string { + const scopeIds: string[] = []; + for (let current: NodeWithParent | undefined = node; current; current = current.parent) { + const scopeIdForASTNode: string | undefined = getNodeName(current); + if (scopeIdForASTNode !== undefined) { + scopeIds.unshift(scopeIdForASTNode); + } + } + + if (scopeIds.length === 0) { + return '.'; + } else { + return '.' + scopeIds.join('.'); + } +} + +const eslintConfigPathByFileOrFolderPath: Map = new Map(); + +function findEslintConfigFolderPathForNormalizedFileAbsolutePath(normalizedFilePath: string): string { + const cachedFolderPathForFilePath: string | undefined = + eslintConfigPathByFileOrFolderPath.get(normalizedFilePath); + if (cachedFolderPathForFilePath) { + return cachedFolderPathForFilePath; + } + const normalizedFileFolderPath: string = normalizedFilePath.substring( + 0, + normalizedFilePath.lastIndexOf('/') + ); + + const pathsToCache: string[] = [normalizedFilePath]; + let eslintConfigFolderPath: string | undefined; + findEslintConfigFileLoop: for ( + let currentFolder: string = normalizedFileFolderPath; + currentFolder; // 'something'.substring(0, -1) is '' + currentFolder = currentFolder.substring(0, currentFolder.lastIndexOf('/')) + ) { + const cachedEslintrcFolderPath: string | undefined = + eslintConfigPathByFileOrFolderPath.get(currentFolder); + if (cachedEslintrcFolderPath) { + // Need to cache this result into the intermediate paths + eslintConfigFolderPath = cachedEslintrcFolderPath; + break; + } + + pathsToCache.push(currentFolder); + for (const eslintConfigFilename of ESLINT_CONFIG_FILENAMES) { + if (fs.existsSync(`${currentFolder}/${eslintConfigFilename}`)) { + eslintConfigFolderPath = currentFolder; + break findEslintConfigFileLoop; + } + } + } + + if (eslintConfigFolderPath) { + for (const checkedFolder of pathsToCache) { + eslintConfigPathByFileOrFolderPath.set(checkedFolder, eslintConfigFolderPath); + } + + return eslintConfigFolderPath; + } else { + throw new Error(`Cannot locate an ESLint configuration file for ${normalizedFilePath}`); + } +} + +// One-line insert into the ruleContext report method to prematurely exit if the ESLint problem has been suppressed +export function shouldBulkSuppress(params: { + filename: string; + currentNode: TSESTree.Node; + ruleId: string; + problem: IProblem; +}): boolean { + // Use this ENV variable to turn off eslint-bulk-suppressions functionality, default behavior is on + if (process.env[ESLINT_BULK_ENABLE_ENV_VAR_NAME] === 'false') { + return false; + } + + const { filename: fileAbsolutePath, currentNode, ruleId: rule, problem } = params; + const normalizedFileAbsolutePath: string = fileAbsolutePath.replace(/\\/g, '/'); + const eslintConfigDirectory: string = + findEslintConfigFolderPathForNormalizedFileAbsolutePath(normalizedFileAbsolutePath); + const fileRelativePath: string = normalizedFileAbsolutePath.substring(eslintConfigDirectory.length + 1); + const scopeId: string = calculateScopeId(currentNode); + const suppression: ISuppression = { file: fileRelativePath, scopeId, rule }; + + const config: IBulkSuppressionsConfig = + getSuppressionsConfigForEslintConfigFolderPath(eslintConfigDirectory); + const serializedSuppression: string = serializeSuppression(suppression); + const currentNodeIsSuppressed: boolean = config.serializedSuppressions.has(serializedSuppression); + + if (currentNodeIsSuppressed || SUPPRESS_ALL_RULES || RULES_TO_SUPPRESS?.has(suppression.rule)) { + problem[SUPPRESSION_SYMBOL] = { + suppression, + serializedSuppression, + config + }; + } + + return process.env[ESLINT_BULK_PRUNE_ENV_VAR_NAME] !== '1' && currentNodeIsSuppressed; +} + +export function prune(): void { + for (const [ + eslintConfigFolderPath, + suppressionsConfig + ] of getAllBulkSuppressionsConfigsByEslintConfigFolderPath()) { + if (suppressionsConfig) { + const { newSerializedSuppressions, newJsonObject } = suppressionsConfig; + const newSuppressionsConfig: IBulkSuppressionsConfig = { + serializedSuppressions: newSerializedSuppressions, + jsonObject: newJsonObject, + newSerializedSuppressions: new Set(), + newJsonObject: { suppressions: [] } + }; + + writeSuppressionsJsonToFile(eslintConfigFolderPath, newSuppressionsConfig); + } + } +} + +export function write(): void { + for (const [ + eslintrcFolderPath, + suppressionsConfig + ] of getAllBulkSuppressionsConfigsByEslintConfigFolderPath()) { + if (suppressionsConfig) { + writeSuppressionsJsonToFile(eslintrcFolderPath, suppressionsConfig); + } + } +} + +// utility function for linter-patch.js to make require statements that use relative paths in linter.js work in linter-patch.js +export function requireFromPathToLinterJS( + importPath: string +): import('eslint-9').Linter | import('eslint-8').Linter { + if (!eslintFolder) { + return require(importPath); + } + + const pathToLinterFolder: string = `${eslintFolder}/lib/linter`; + const moduleAbsolutePath: string = require.resolve(importPath, { paths: [pathToLinterFolder] }); + return require(moduleAbsolutePath); +} + +export function patchClass(originalClass: new () => T, patchedClass: new () => U): void { + // Get all the property names of the patched class prototype + const patchedProperties: string[] = Object.getOwnPropertyNames(patchedClass.prototype); + + // Loop through all the properties + for (const prop of patchedProperties) { + // Override the property in the original class + originalClass.prototype[prop] = patchedClass.prototype[prop]; + } + + // Handle getters and setters + for (const [prop, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(patchedClass.prototype))) { + if (descriptor.get || descriptor.set) { + Object.defineProperty(originalClass.prototype, prop, descriptor); + } + } +} + +/** + * This returns a wrapped version of the "verify" function from ESLint's Linter class + * that postprocesses rule violations that weren't suppressed by comments. This postprocessing + * records suppressions that weren't otherwise suppressed by comments to be used + * by the "suppress" and "prune" commands. + */ +export function extendVerifyFunction( + originalFn: (this: unknown, ...args: unknown[]) => IProblem[] | undefined +): (this: unknown, ...args: unknown[]) => IProblem[] | undefined { + return function (this: unknown, ...args: unknown[]): IProblem[] | undefined { + const problems: IProblem[] | undefined = originalFn.apply(this, args); + if (problems) { + for (const problem of problems) { + if (problem[SUPPRESSION_SYMBOL]) { + const { + serializedSuppression, + suppression, + config: { + newSerializedSuppressions, + jsonObject: { suppressions }, + newJsonObject: { suppressions: newSuppressions } + } + } = problem[SUPPRESSION_SYMBOL]; + if (!newSerializedSuppressions.has(serializedSuppression)) { + newSerializedSuppressions.add(serializedSuppression); + newSuppressions.push(suppression); + suppressions.push(suppression); + } + } + } + } + + return problems; + }; +} diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/prune.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/prune.ts new file mode 100755 index 00000000000..ecbc702a63b --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/prune.ts @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import fs from 'node:fs'; + +import { printPruneHelp } from './utils/print-help'; +import { runEslintAsync } from './runEslint'; +import { ESLINT_BULK_PRUNE_ENV_VAR_NAME } from '../constants'; +import { + deleteBulkSuppressionsFileInEslintConfigFolder, + getSuppressionsConfigForEslintConfigFolderPath +} from '../bulk-suppressions-file'; + +export async function pruneAsync(): Promise { + const args: string[] = process.argv.slice(3); + + if (args.includes('--help') || args.includes('-h')) { + printPruneHelp(); + process.exit(0); + } + + if (args.length > 0) { + throw new Error(`@rushstack/eslint-bulk: Unknown arguments: ${args.join(' ')}`); + } + + const normalizedCwd: string = process.cwd().replace(/\\/g, '/'); + const allFiles: string[] = await getAllFilesWithExistingSuppressionsForCwdAsync(normalizedCwd); + if (allFiles.length > 0) { + process.env[ESLINT_BULK_PRUNE_ENV_VAR_NAME] = '1'; + console.log(`Pruning suppressions for ${allFiles.length} files...`); + await runEslintAsync(allFiles, 'prune'); + } else { + console.log('No files with existing suppressions found.'); + deleteBulkSuppressionsFileInEslintConfigFolder(normalizedCwd); + } +} + +async function getAllFilesWithExistingSuppressionsForCwdAsync(normalizedCwd: string): Promise { + const { jsonObject: bulkSuppressionsConfigJson } = + getSuppressionsConfigForEslintConfigFolderPath(normalizedCwd); + const allFiles: Set = new Set(); + for (const { file: filePath } of bulkSuppressionsConfigJson.suppressions) { + allFiles.add(filePath); + } + + const allFilesArray: string[] = Array.from(allFiles); + + const allExistingFiles: string[] = []; + // TODO: limit parallelism here with something similar to `Async.forEachAsync` from `node-core-library`. + await Promise.all( + allFilesArray.map(async (filePath: string) => { + try { + await fs.promises.access(filePath, fs.constants.F_OK); + allExistingFiles.push(filePath); + } catch { + // Doesn't exist - ignore + } + }) + ); + + console.log(`Found ${allExistingFiles.length} files with existing suppressions.`); + const deletedCount: number = allFilesArray.length - allExistingFiles.length; + if (deletedCount > 0) { + console.log(`${deletedCount} files with suppressions were deleted.`); + } + + return allExistingFiles; +} diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/runEslint.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/runEslint.ts new file mode 100644 index 00000000000..8d73302aa8a --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/runEslint.ts @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { ESLint as TEslintLegacy } from 'eslint-8'; +import type { ESLint as TEslint } from 'eslint-9'; + +import { getEslintPathAndVersion } from './utils/get-eslint-cli'; + +export async function runEslintAsync(files: string[], mode: 'suppress' | 'prune'): Promise { + const cwd: string = process.cwd(); + const [eslintPath, eslintVersion] = getEslintPathAndVersion(cwd); + const { ESLint }: typeof import('eslint-9') | typeof import('eslint-8') = require(eslintPath); + + let eslint: TEslint | TEslintLegacy; + const majorVersion: number = parseInt(eslintVersion, 10); + if (majorVersion < 9) { + eslint = new ESLint({ cwd, useEslintrc: true }); + } else { + eslint = new ESLint({ cwd }); + } + + let results: (TEslint.LintResult | TEslintLegacy.LintResult)[]; + try { + results = await eslint.lintFiles(files); + } catch (e) { + throw new Error(`@rushstack/eslint-bulk execution error: ${e.message}`); + } + + const { write, prune } = await import('../bulk-suppressions-patch'); + switch (mode) { + case 'suppress': { + await write(); + break; + } + + case 'prune': { + await prune(); + break; + } + } + + if (results.length > 0) { + const stylishFormatter: TEslint.Formatter | TEslintLegacy.Formatter = await eslint.loadFormatter(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const formattedResults: string = await Promise.resolve(stylishFormatter.format(results as any)); + console.log(formattedResults); + } + + console.log( + '@rushstack/eslint-bulk: Successfully pruned unused suppressions in all .eslint-bulk-suppressions.json ' + + `files under directory ${cwd}` + ); +} diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/start.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/start.ts new file mode 100644 index 00000000000..b871a6149f3 --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/start.ts @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { pruneAsync } from './prune'; +import { suppressAsync } from './suppress'; +import { isCorrectCwd } from './utils/is-correct-cwd'; +import { printHelp } from './utils/print-help'; + +if (process.argv.includes('-h') || process.argv.includes('-H') || process.argv.includes('--help')) { + printHelp(); + process.exit(0); +} + +if (process.argv.length < 3) { + printHelp(); + process.exit(1); +} + +if (!isCorrectCwd(process.cwd())) { + console.error( + '@rushstack/eslint-bulk: Please call this command from the directory that contains .eslintrc.js or .eslintrc.cjs' + ); + process.exit(1); +} + +const subcommand: string = process.argv[2]; +let processPromise: Promise; +switch (subcommand) { + case 'suppress': { + processPromise = suppressAsync(); + break; + } + + case 'prune': { + processPromise = pruneAsync(); + break; + } + + default: { + console.error('@rushstack/eslint-bulk: Unknown subcommand: ' + subcommand); + process.exit(1); + } +} + +processPromise.catch((e) => { + if (e instanceof Error) { + console.error(e.message); + process.exit(1); + } + + throw e; +}); diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/suppress.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/suppress.ts new file mode 100755 index 00000000000..c8ba1eafa43 --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/suppress.ts @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { printSuppressHelp } from './utils/print-help'; +import { runEslintAsync } from './runEslint'; +import { ESLINT_BULK_SUPPRESS_ENV_VAR_NAME } from '../constants'; + +interface IParsedArgs { + rules: string[]; + all: boolean; + files: string[]; +} + +export async function suppressAsync(): Promise { + const args: string[] = process.argv.slice(3); + + if (args.includes('--help') || args.includes('-h')) { + printSuppressHelp(); + process.exit(0); + } + + // Use reduce to create an object with all the parsed arguments + const parsedArgs: IParsedArgs = args.reduce<{ + rules: string[]; + all: boolean; + files: string[]; + }>( + (acc, arg, index, arr) => { + if (arg === '--rule') { + // continue because next arg should be the rule + } else if (index > 0 && arr[index - 1] === '--rule' && arr[index + 1]) { + acc.rules.push(arg); + } else if (arg === '--all') { + acc.all = true; + } else if (arg.startsWith('--')) { + throw new Error(`@rushstack/eslint-bulk: Unknown option: ${arg}`); + } else { + acc.files.push(arg); + } + return acc; + }, + { rules: [], all: false, files: [] } + ); + + if (parsedArgs.files.length === 0) { + throw new Error( + '@rushstack/eslint-bulk: Files argument is required. Use glob patterns to specify files or use ' + + '`.` to suppress all files for the specified rules.' + ); + } + + if (parsedArgs.rules.length === 0 && !parsedArgs.all) { + throw new Error( + '@rushstack/eslint-bulk: Please specify at least one rule to suppress. Use --all to suppress all rules.' + ); + } + + // Find the index of the last argument that starts with '--' + const lastOptionIndex: number = args + .map((arg, i) => (arg.startsWith('--') ? i : -1)) + .reduce((lastIndex, currentIndex) => Math.max(lastIndex, currentIndex), -1); + + // Check if options come before files + if (parsedArgs.files.some((file) => args.indexOf(file) < lastOptionIndex)) { + throw new Error( + '@rushstack/eslint-bulk: Unable to parse command line arguments. All options should come before files argument.' + ); + } + + if (parsedArgs.all) { + process.env[ESLINT_BULK_SUPPRESS_ENV_VAR_NAME] = '*'; + } else if (parsedArgs.rules.length > 0) { + process.env[ESLINT_BULK_SUPPRESS_ENV_VAR_NAME] = parsedArgs.rules.join(','); + } + + await runEslintAsync(parsedArgs.files, 'suppress'); + + if (parsedArgs.all) { + console.log(`@rushstack/eslint-bulk: Successfully suppressed all rules for file(s) ${parsedArgs.files}`); + } else if (parsedArgs.rules.length > 0) { + console.log( + `@rushstack/eslint-bulk: Successfully suppressed rules ${parsedArgs.rules} for file(s) ${parsedArgs.files}` + ); + } +} diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/utils/get-eslint-cli.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/utils/get-eslint-cli.ts new file mode 100755 index 00000000000..be55dea24c9 --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/utils/get-eslint-cli.ts @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; + +import { BULK_SUPPRESSIONS_CLI_ESLINT_PACKAGE_NAME } from '../../constants'; + +// When this list is updated, update the `eslint-bulk-suppressions-newest-test` +// and/or the `eslint-bulk-suppressions-newest-test` projects' eslint dependencies. +const TESTED_VERSIONS: Set = new Set([ + '8.6.0', + '8.7.0', + '8.21.0', + '8.22.0', + '8.23.0', + '8.23.1', + '8.57.0', + '9.25.1', + '9.37.0' +]); + +export function getEslintPathAndVersion(packagePath: string): [string, string] { + // Try to find a local ESLint installation, the one that should be listed as a dev dependency in package.json + // and installed in node_modules + try { + const localEslintApiPath: string = require.resolve(BULK_SUPPRESSIONS_CLI_ESLINT_PACKAGE_NAME, { + paths: [packagePath] + }); + const localEslintPath: string = path.dirname(path.dirname(localEslintApiPath)); + const { version: localEslintVersion } = require(`${localEslintPath}/package.json`); + + if (!TESTED_VERSIONS.has(localEslintVersion)) { + console.warn( + '@rushstack/eslint-bulk: Be careful, the installed ESLint version has not been tested with eslint-bulk.' + ); + } + + return [localEslintApiPath, localEslintVersion]; + } catch (e1) { + try { + const { + dependencies, + devDependencies + }: { + dependencies: Record | undefined; + devDependencies: Record | undefined; + } = require(`${packagePath}/package.json`); + + if (devDependencies?.eslint) { + throw new Error( + '@rushstack/eslint-bulk: eslint is specified as a dev dependency in package.json, ' + + 'but eslint-bulk cannot find it in node_modules.' + ); + } else if (dependencies?.eslint) { + throw new Error( + '@rushstack/eslint-bulk: eslint is specified as a dependency in package.json, ' + + 'but eslint-bulk cannot find it in node_modules.' + ); + } else { + throw new Error('@rushstack/eslint-bulk: eslint is not specified as a dependency in package.json.'); + } + } catch (e2) { + throw new Error( + "@rushstack/eslint-bulk: This command must be run in the same folder as a project's package.json file." + ); + } + } +} diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/utils/is-correct-cwd.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/utils/is-correct-cwd.ts new file mode 100755 index 00000000000..eff0c18cb68 --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/utils/is-correct-cwd.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import fs from 'node:fs'; + +export function isCorrectCwd(cwd: string): boolean { + return ( + fs.existsSync(`${cwd}/eslint.config.js`) || + fs.existsSync(`${cwd}/eslint.config.cjs`) || + fs.existsSync(`${cwd}/eslint.config.mjs`) || + fs.existsSync(`${cwd}/.eslintrc.js`) || + fs.existsSync(`${cwd}/.eslintrc.cjs`) + ); +} diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/utils/print-help.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/utils/print-help.ts new file mode 100644 index 00000000000..a0a8212dead --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/utils/print-help.ts @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { wrapWordsToLines } from './wrap-words-to-lines'; + +export function printPruneHelp(): void { + const help: string = `eslint-bulk prune + +Usage: + +eslint-bulk prune + +This command is a thin wrapper around ESLint that communicates with @rushstack/eslint-patch to delete all unused suppression entries in all .eslint-bulk-suppressions.json files under the current working directory.`; + + const wrapped: string[] = wrapWordsToLines(help); + for (const line of wrapped) { + console.log(line); + } +} + +export function printHelp(): void { + const help: string = `eslint-bulk + +Usage: + +eslint-bulk suppress --rule RULENAME1 [--rule RULENAME2...] PATH1 [PATH2...] +eslint-bulk suppress --all PATH1 [PATH2...] +eslint-bulk suppress --help + +eslint-bulk prune +eslint-bulk prune --help + +eslint-bulk --help + +This command line tool is a thin wrapper around ESLint that communicates with @rushstack/eslint-patch to suppress or prune unused suppressions in the local .eslint-bulk-suppressions.json file. + +Commands: + eslint-bulk suppress [options] + Use this command to generate a new .eslint-bulk-suppressions.json file or add suppression entries to the existing file. Specify the files and rules you want to suppress. + Please run "eslint-bulk suppress --help" to learn more. + + eslint-bulk prune + Use this command to delete all unused suppression entries in all .eslint-bulk-suppressions.json files under the current working directory. + Please run "eslint-bulk prune --help" to learn more. +`; + + const wrapped: string[] = wrapWordsToLines(help); + for (const line of wrapped) { + console.log(line); + } +} + +export function printSuppressHelp(): void { + const help: string = `eslint-bulk suppress [options] + +Usage: + +eslint-bulk suppress --rule RULENAME1 [--rule RULENAME2...] PATH1 [PATH2...] +eslint-bulk suppress --all PATH1 [PATH2...] +eslint-bulk suppress --help + +This command is a thin wrapper around ESLint that communicates with @rushstack/eslint-patch to either generate a new .eslint-bulk-suppressions.json file or add suppression entries to the existing file. Specify the files and rules you want to suppress. + +Argument: + + Glob patterns for paths to suppress, same as eslint files argument. Should be relative to the project root. + +Options: + -h, -H, --help + Display this help message. + + -R, --rule + The full name of the ESLint rule you want to bulk-suppress. Specify multiple rules with --rule NAME1 --rule RULENAME2. + + -A, --all + Bulk-suppress all rules in the specified file patterns.`; + + const wrapped: string[] = wrapWordsToLines(help); + for (const line of wrapped) { + console.log(line); + } +} diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/utils/wrap-words-to-lines.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/utils/wrap-words-to-lines.ts new file mode 100755 index 00000000000..b38de3bd20a --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/cli/utils/wrap-words-to-lines.ts @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// ---------------------------------------------------------------------------------------------------------- +// TO AVOID EXTRA DEPENDENCIES, THE CODE IN THIS FILE WAS BORROWED FROM: +// +// rushstack/libraries/terminal/src/PrintUtilities.ts +// +// KEEP IT IN SYNC WITH THAT FILE. +// ---------------------------------------------------------------------------------------------------------- + +/** + * Applies word wrapping and returns an array of lines. + * + * @param text - The text to wrap + * @param maxLineLength - The maximum length of a line, defaults to the console width + * @param indent - The number of spaces to indent the wrapped lines, defaults to 0 + */ +export function wrapWordsToLines(text: string, maxLineLength?: number, indent?: number): string[]; +/** + * Applies word wrapping and returns an array of lines. + * + * @param text - The text to wrap + * @param maxLineLength - The maximum length of a line, defaults to the console width + * @param linePrefix - The string to prefix each line with, defaults to '' + */ +export function wrapWordsToLines(text: string, maxLineLength?: number, linePrefix?: string): string[]; +/** + * Applies word wrapping and returns an array of lines. + * + * @param text - The text to wrap + * @param maxLineLength - The maximum length of a line, defaults to the console width + * @param indentOrLinePrefix - The number of spaces to indent the wrapped lines or the string to prefix + * each line with, defaults to no prefix + */ +export function wrapWordsToLines( + text: string, + maxLineLength?: number, + indentOrLinePrefix?: number | string +): string[]; +export function wrapWordsToLines( + text: string, + maxLineLength?: number, + indentOrLinePrefix?: number | string +): string[] { + let linePrefix: string; + switch (typeof indentOrLinePrefix) { + case 'number': + linePrefix = ' '.repeat(indentOrLinePrefix); + break; + case 'string': + linePrefix = indentOrLinePrefix; + break; + default: + linePrefix = ''; + break; + } + + const linePrefixLength: number = linePrefix.length; + + if (!maxLineLength) { + maxLineLength = process.stdout.getWindowSize()[0]; + } + + // Apply word wrapping and the provided line prefix, while also respecting existing newlines + // and prefix spaces that may exist in the text string already. + const lines: string[] = text.split(/\r?\n/); + + const wrappedLines: string[] = []; + for (const line of lines) { + if (line.length + linePrefixLength <= maxLineLength) { + wrappedLines.push(linePrefix + line); + } else { + const lineAdditionalPrefix: string = line.match(/^\s*/)?.[0] || ''; + const whitespaceRegexp: RegExp = /\s+/g; + let currentWhitespaceMatch: RegExpExecArray | null = null; + let previousWhitespaceMatch: RegExpExecArray | undefined; + let currentLineStartIndex: number = lineAdditionalPrefix.length; + let previousBreakRanOver: boolean = false; + while ((currentWhitespaceMatch = whitespaceRegexp.exec(line)) !== null) { + if (currentWhitespaceMatch.index + linePrefixLength - currentLineStartIndex > maxLineLength) { + let whitespaceToSplitAt: RegExpExecArray | undefined; + if ( + !previousWhitespaceMatch || + // Handle the case where there are two words longer than the maxLineLength in a row + previousBreakRanOver + ) { + whitespaceToSplitAt = currentWhitespaceMatch; + } else { + whitespaceToSplitAt = previousWhitespaceMatch; + } + + wrappedLines.push( + linePrefix + + lineAdditionalPrefix + + line.substring(currentLineStartIndex, whitespaceToSplitAt.index) + ); + previousBreakRanOver = whitespaceToSplitAt.index - currentLineStartIndex > maxLineLength; + currentLineStartIndex = whitespaceToSplitAt.index + whitespaceToSplitAt[0].length; + } else { + previousBreakRanOver = false; + } + + previousWhitespaceMatch = currentWhitespaceMatch; + } + + if (currentLineStartIndex < line.length) { + wrappedLines.push(linePrefix + lineAdditionalPrefix + line.substring(currentLineStartIndex)); + } + } + } + + return wrappedLines; +} diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/constants.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/constants.ts new file mode 100644 index 00000000000..69be3edb8e5 --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/constants.ts @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export const ESLINT_BULK_PATCH_PATH_ENV_VAR_NAME: 'RUSHSTACK_ESLINT_BULK_PATCH_PATH' = + 'RUSHSTACK_ESLINT_BULK_PATCH_PATH'; +export const ESLINT_BULK_SUPPRESS_ENV_VAR_NAME: 'RUSHSTACK_ESLINT_BULK_SUPPRESS' = + 'RUSHSTACK_ESLINT_BULK_SUPPRESS'; +export const ESLINT_BULK_ENABLE_ENV_VAR_NAME: 'ESLINT_BULK_ENABLE' = 'ESLINT_BULK_ENABLE'; +export const ESLINT_BULK_PRUNE_ENV_VAR_NAME: 'ESLINT_BULK_PRUNE' = 'ESLINT_BULK_PRUNE'; +export const ESLINT_BULK_DETECT_ENV_VAR_NAME: '_RUSHSTACK_ESLINT_BULK_DETECT' = + '_RUSHSTACK_ESLINT_BULK_DETECT'; +export const ESLINT_BULK_FORCE_REGENERATE_PATCH_ENV_VAR_NAME: 'RUSHSTACK_ESLINT_BULK_FORCE_REGENERATE_PATCH' = + 'RUSHSTACK_ESLINT_BULK_FORCE_REGENERATE_PATCH'; +export const VSCODE_PID_ENV_VAR_NAME: 'VSCODE_PID' = 'VSCODE_PID'; + +export const ESLINT_BULK_STDOUT_START_DELIMETER: 'RUSHSTACK_ESLINT_BULK_START' = + 'RUSHSTACK_ESLINT_BULK_START'; +export const ESLINT_BULK_STDOUT_END_DELIMETER: 'RUSHSTACK_ESLINT_BULK_END' = 'RUSHSTACK_ESLINT_BULK_END'; + +export const ESLINT_PACKAGE_NAME_ENV_VAR_NAME: '_RUSHSTACK_ESLINT_PACKAGE_NAME' = + '_RUSHSTACK_ESLINT_PACKAGE_NAME'; + +export const BULK_SUPPRESSIONS_CLI_ESLINT_PACKAGE_NAME: string = + process.env[ESLINT_PACKAGE_NAME_ENV_VAR_NAME] ?? 'eslint'; diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/generate-patched-file.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/generate-patched-file.ts new file mode 100644 index 00000000000..5a0107537a3 --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/generate-patched-file.ts @@ -0,0 +1,399 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import fs from 'node:fs'; + +import { + ESLINT_BULK_FORCE_REGENERATE_PATCH_ENV_VAR_NAME, + ESLINT_BULK_PATCH_PATH_ENV_VAR_NAME +} from './constants'; + +/** + * Dynamically generate file to properly patch many versions of ESLint + * @param inputFilePath - Must be an iteration of https://github.com/eslint/eslint/blob/main/lib/linter/linter.js + * @param outputFilePath - Some small changes to linter.js + */ +export function generatePatchedLinterJsFileIfDoesNotExist( + inputFilePath: string, + outputFilePath: string, + eslintPackageVersion: string +): void { + const generateEnvVarValue: string | undefined = + process.env[ESLINT_BULK_FORCE_REGENERATE_PATCH_ENV_VAR_NAME]; + if (generateEnvVarValue !== 'true' && generateEnvVarValue !== '1' && fs.existsSync(outputFilePath)) { + return; + } + + const [majorVersionString, minorVersionString] = eslintPackageVersion.split('.'); + const majorVersion: number = parseInt(majorVersionString, 10); + const minorVersion: number = parseInt(minorVersionString, 10); + + const inputFile: string = fs.readFileSync(inputFilePath).toString(); + + let inputIndex: number = 0; + + /** + * Extract from the stream until marker is reached. When matching marker, + * ignore whitespace in the stream and in the marker. Return the extracted text. + */ + function scanUntilMarker(marker: string): string { + const trimmedMarker: string = marker.replace(/\s/g, ''); + + let output: string = ''; + let trimmed: string = ''; + + while (inputIndex < inputFile.length) { + const char: string = inputFile[inputIndex++]; + output += char; + if (!/^\s$/.test(char)) { + trimmed += char; + } + if (trimmed.endsWith(trimmedMarker)) { + return output; + } + } + + throw new Error('Unexpected end of input while looking for ' + JSON.stringify(marker)); + } + + function scanUntilNewline(): string { + let output: string = ''; + + while (inputIndex < inputFile.length) { + const char: string = inputFile[inputIndex++]; + output += char; + if (char === '\n') { + return output; + } + } + + throw new Error('Unexpected end of input while looking for new line'); + } + + function scanUntilEnd(): string { + const output: string = inputFile.substring(inputIndex); + inputIndex = inputFile.length; + return output; + } + + const markerForStartOfClassMethodSpaces: string = '\n */\n '; + const markerForStartOfClassMethodTabs: string = '\n\t */\n\t'; + function indexOfStartOfClassMethod(input: string, position?: number): { index: number; marker?: string } { + let startOfClassMethodIndex: number = input.indexOf(markerForStartOfClassMethodSpaces, position); + if (startOfClassMethodIndex === -1) { + startOfClassMethodIndex = input.indexOf(markerForStartOfClassMethodTabs, position); + if (startOfClassMethodIndex === -1) { + return { index: startOfClassMethodIndex }; + } + return { index: startOfClassMethodIndex, marker: markerForStartOfClassMethodTabs }; + } + return { index: startOfClassMethodIndex, marker: markerForStartOfClassMethodSpaces }; + } + + /** + * Returns index of next public method + * @param fromIndex - index of inputFile to search if public method still exists + * @returns -1 if public method does not exist or index of next public method + */ + function getIndexOfNextMethod(fromIndex: number): { index: number; isPublic?: boolean } { + const rest: string = inputFile.substring(fromIndex); + + const endOfClassIndex: number = rest.indexOf('\n}'); + + const { index: startOfClassMethodIndex, marker: startOfClassMethodMarker } = + indexOfStartOfClassMethod(rest); + + if ( + startOfClassMethodIndex === -1 || + !startOfClassMethodMarker || + startOfClassMethodIndex > endOfClassIndex + ) { + return { index: -1 }; + } + + const afterMarkerIndex: number = startOfClassMethodIndex + startOfClassMethodMarker.length; + + const isPublicMethod: boolean = + rest[afterMarkerIndex] !== '_' && + rest[afterMarkerIndex] !== '#' && + !rest.substring(afterMarkerIndex, rest.indexOf('\n', afterMarkerIndex)).includes('static') && + !rest.substring(afterMarkerIndex, rest.indexOf('\n', afterMarkerIndex)).includes('constructor'); + + return { index: fromIndex + afterMarkerIndex, isPublic: isPublicMethod }; + } + + function scanUntilIndex(indexToScanTo: number): string { + const output: string = inputFile.substring(inputIndex, indexToScanTo); + inputIndex = indexToScanTo; + return output; + } + + let outputFile: string = ''; + + // Match this: + // //------------------------------------------------------------------------------ + // // Requirements + // //------------------------------------------------------------------------------ + outputFile += scanUntilMarker('// Requirements'); + outputFile += scanUntilMarker('//--'); + outputFile += scanUntilNewline(); + + outputFile += ` +// --- BEGIN MONKEY PATCH --- +const bulkSuppressionsPatch = require(process.env.${ESLINT_BULK_PATCH_PATH_ENV_VAR_NAME}); +const requireFromPathToLinterJS = bulkSuppressionsPatch.requireFromPathToLinterJS; +`; + + // Match this: + // //------------------------------------------------------------------------------ + // // Typedefs + // //------------------------------------------------------------------------------ + const requireSection: string = scanUntilMarker('// Typedefs'); + + // Match something like this: + // + // const path = require('path'), + // eslintScope = require('eslint-scope'), + // evk = require('eslint-visitor-keys'), + // + // Convert to something like this: + // + // const path = require('path'), + // eslintScope = requireFromPathToLinterJS('eslint-scope'), + // evk = requireFromPathToLinterJS('eslint-visitor-keys'), + // + outputFile += requireSection.replace(/require\s*\((?:'([^']+)'|"([^"]+)")\)/g, (match, p1, p2) => { + const importPath: string = p1 ?? p2 ?? ''; + + if (importPath !== 'path') { + if (p1) { + return `requireFromPathToLinterJS('${p1}')`; + } + if (p2) { + return `requireFromPathToLinterJS("${p2}")`; + } + } + + // Keep as-is + return match; + }); + outputFile += `--- END MONKEY PATCH --- +`; + + if (majorVersion >= 9) { + if (minorVersion >= 37) { + outputFile += scanUntilMarker('const visitor = new SourceCodeVisitor();'); + } else { + outputFile += scanUntilMarker('const emitter = createEmitter();'); + } + + outputFile += ` + // --- BEGIN MONKEY PATCH --- + let currentNode = undefined; + // --- END MONKEY PATCH ---`; + } + + // Match this (9.25.1): + // ``` + // if (reportTranslator === null) { + // reportTranslator = createReportTranslator({ + // ruleId, + // severity, + // sourceCode, + // messageIds, + // disableFixes + // }); + // } + // const problem = reportTranslator(...args); + // + // if (problem.fix && !(rule.meta && rule.meta.fixable)) { + // throw new Error("Fixable rules must set the `meta.fixable` property to \"code\" or \"whitespace\"."); + // } + // ``` + // Or this (9.37.0): + // ``` + // const problem = report.addRuleMessage( + // ruleId, + // severity, + // ...args, + // ); + // + // if (problem.fix && !(rule.meta && rule.meta.fixable)) { + // throw new Error( + // 'Fixable rules must set the `meta.fixable` property to "code" or "whitespace".', + // ); + // } + // ``` + // + // Convert to something like this (9.25.1): + // ``` + // if (reportTranslator === null) { + // reportTranslator = createReportTranslator({ + // ruleId, + // severity, + // sourceCode, + // messageIds, + // disableFixes + // }); + // } + // const problem = reportTranslator(...args); + // // --- BEGIN MONKEY PATCH --- + // if (bulkSuppressionsPatch.shouldBulkSuppress({ filename, currentNode: args[0]?.node ?? currentNode, ruleId, problem })) return; + // // --- END MONKEY PATCH --- + // + // if (problem.fix && !(rule.meta && rule.meta.fixable)) { + // throw new Error("Fixable rules must set the `meta.fixable` property to \"code\" or \"whitespace\"."); + // } + // ``` + // Or this (9.37.0): + // ``` + // const problem = report.addRuleMessage( + // ruleId, + // severity, + // ...args, + // ); + // // --- BEGIN MONKEY PATCH --- + // if (bulkSuppressionsPatch.shouldBulkSuppress({ filename, currentNode: args[0]?.node ?? currentNode, ruleId, problem })) return; + // // --- END MONKEY PATCH --- + // + // if (problem.fix && !(rule.meta && rule.meta.fixable)) { + // throw new Error( + // 'Fixable rules must set the `meta.fixable` property to "code" or "whitespace".', + // ); + // } + // ``` + if (majorVersion > 9 || (majorVersion === 9 && minorVersion >= 37)) { + outputFile += scanUntilMarker('const problem = report.addRuleMessage('); + outputFile += scanUntilMarker('ruleId,'); + outputFile += scanUntilMarker('severity,'); + outputFile += scanUntilMarker('...args,'); + outputFile += scanUntilMarker(');'); + } else { + outputFile += scanUntilMarker('const problem = reportTranslator(...args);'); + } + + outputFile += ` + // --- BEGIN MONKEY PATCH ---`; + if (majorVersion > 9 || (majorVersion === 9 && minorVersion >= 37)) { + outputFile += ` + if (bulkSuppressionsPatch.shouldBulkSuppress({ filename, currentNode: args[0]?.node ?? currentNode, ruleId, problem })) { + problem.suppressions ??= []; problem.suppressions.push({kind:"bulk",justification:""}); + }`; + } else { + outputFile += ` + if (bulkSuppressionsPatch.shouldBulkSuppress({ filename, currentNode: args[0]?.node ?? currentNode, ruleId, problem })) return;`; + } + + outputFile += ` + // --- END MONKEY PATCH ---`; + + // + // Match this: + // ``` + // Object.keys(ruleListeners).forEach(selector => { + // ... + // }); + // ``` + // + // Convert to something like this (9.25.1): + // ``` + // Object.keys(ruleListeners).forEach(selector => { + // // --- BEGIN MONKEY PATCH --- + // emitter.on(selector, (...args) => { currentNode = args[args.length - 1]; }); + // // --- END MONKEY PATCH --- + // ... + // }); + // ``` + // Or this (9.37.0): + // ``` + // Object.keys(ruleListeners).forEach(selector => { + // // --- BEGIN MONKEY PATCH --- + // visitor.add(selector, (...args) => { currentNode = args[args.length - 1]; }); + // // --- END MONKEY PATCH --- + // ... + // }); + // ``` + if (majorVersion >= 9) { + outputFile += scanUntilMarker('Object.keys(ruleListeners).forEach(selector => {'); + outputFile += ` + // --- BEGIN MONKEY PATCH --- +`; + if (minorVersion >= 37) { + outputFile += `visitor.add(selector, (...args) => { currentNode = args[args.length - 1]; });`; + } else { + outputFile += `emitter.on(selector, (...args) => { currentNode = args[args.length - 1]; });`; + } + + outputFile += ` + // --- END MONKEY PATCH ---`; + } + + outputFile += scanUntilMarker('class Linter {'); + outputFile += scanUntilNewline(); + outputFile += ` + // --- BEGIN MONKEY PATCH --- + /** + * We intercept ESLint execution at the .eslintrc.js file, but unfortunately the Linter class is + * initialized before the .eslintrc.js file is executed. This means the internalSlotsMap that all + * the patched methods refer to is not initialized. This method checks if the internalSlotsMap is + * initialized, and if not, initializes it. + */ + _conditionallyReinitialize({ cwd, configType } = {}) { + if (internalSlotsMap.get(this) === undefined) { + internalSlotsMap.set(this, { + cwd: normalizeCwd(cwd), + flags: [], + lastConfigArray: null, + lastSourceCode: null, + lastSuppressedMessages: [], + configType, // TODO: Remove after flat config conversion + parserMap: new Map([['espree', espree]]), + ruleMap: new Rules() + }); + + this.version = pkg.version; + } + } + // --- END MONKEY PATCH --- +`; + + const privateMethodNames: string[] = []; + let { index: indexOfNextMethod, isPublic } = getIndexOfNextMethod(inputIndex); + + while (indexOfNextMethod !== -1) { + outputFile += scanUntilIndex(indexOfNextMethod); + if (isPublic) { + // Inject the monkey patch at the start of the public method + outputFile += scanUntilNewline(); + outputFile += ` // --- BEGIN MONKEY PATCH --- + this._conditionallyReinitialize(); + // --- END MONKEY PATCH --- +`; + } else if (inputFile[inputIndex] === '#') { + // Replace the '#' private method with a '_' private method, so that our monkey patch + // can still call it. Otherwise, we get the following error during execution: + // TypeError: Receiver must be an instance of class Linter + const privateMethodName: string = scanUntilMarker('('); + // Remove the '(' at the end and stash it, since we need to escape it for the regex later + privateMethodNames.push(privateMethodName.slice(0, -1)); + outputFile += `_${privateMethodName.slice(1)}`; + } + + const indexResult: { index: number; isPublic?: boolean } = getIndexOfNextMethod(inputIndex); + indexOfNextMethod = indexResult.index; + isPublic = indexResult.isPublic; + } + + outputFile += scanUntilEnd(); + + // Do a second pass to find and replace all calls to private methods with the patched versions. + if (privateMethodNames.length) { + const privateMethodCallRegex: RegExp = new RegExp(`\.(${privateMethodNames.join('|')})\\(`, 'g'); + outputFile = outputFile.replace(privateMethodCallRegex, (match, privateMethodName) => { + // Replace the leading '#' with a leading '_' + return `._${privateMethodName.slice(1)}(`; + }); + } + + fs.writeFileSync(outputFilePath, outputFile); +} diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/index.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/index.ts new file mode 100644 index 00000000000..c11870137a9 --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/index.ts @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { eslintFolder, eslintPackageVersion } from '../_patch-base'; +import { findAndConsoleLogPatchPathCli, getPathToLinterJS, ensurePathToGeneratedPatch } from './path-utils'; +import { patchClass, extendVerifyFunction } from './bulk-suppressions-patch'; +import { generatePatchedLinterJsFileIfDoesNotExist } from './generate-patched-file'; +import { ESLINT_BULK_DETECT_ENV_VAR_NAME, ESLINT_BULK_PATCH_PATH_ENV_VAR_NAME } from './constants'; + +if (!eslintFolder) { + console.error( + '@rushstack/eslint-patch/eslint-bulk-suppressions: Could not find ESLint installation to patch.' + ); + + process.exit(1); +} + +const eslintBulkDetectEnvVarValue: string | undefined = process.env[ESLINT_BULK_DETECT_ENV_VAR_NAME]; +if (eslintBulkDetectEnvVarValue === 'true' || eslintBulkDetectEnvVarValue === '1') { + findAndConsoleLogPatchPathCli(); + process.exit(0); +} + +const pathToLinterJS: string = getPathToLinterJS(); + +process.env[ESLINT_BULK_PATCH_PATH_ENV_VAR_NAME] = require.resolve('./bulk-suppressions-patch'); + +const pathToGeneratedPatch: string = ensurePathToGeneratedPatch(); +generatePatchedLinterJsFileIfDoesNotExist(pathToLinterJS, pathToGeneratedPatch, eslintPackageVersion); +const { Linter: LinterPatch } = require(pathToGeneratedPatch); +LinterPatch.prototype.verify = extendVerifyFunction(LinterPatch.prototype.verify); + +const { Linter } = require(pathToLinterJS); + +patchClass(Linter, LinterPatch); diff --git a/eslint/eslint-patch/src/eslint-bulk-suppressions/path-utils.ts b/eslint/eslint-patch/src/eslint-bulk-suppressions/path-utils.ts new file mode 100644 index 00000000000..62e54bcd31b --- /dev/null +++ b/eslint/eslint-patch/src/eslint-bulk-suppressions/path-utils.ts @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import fs from 'node:fs'; +import os from 'node:os'; + +import { eslintFolder, eslintPackageVersion } from '../_patch-base'; +import { + ESLINT_BULK_DETECT_ENV_VAR_NAME, + ESLINT_BULK_STDOUT_END_DELIMETER, + ESLINT_BULK_STDOUT_START_DELIMETER +} from './constants'; +import currentPackageJson from '../../package.json'; + +interface IConfiguration { + minCliVersion: string; + cliEntryPoint: string; +} + +const CURRENT_PACKAGE_VERSION: string = currentPackageJson.version; + +export function findAndConsoleLogPatchPathCli(): void { + const eslintBulkDetectEnvVarValue: string | undefined = process.env[ESLINT_BULK_DETECT_ENV_VAR_NAME]; + if (eslintBulkDetectEnvVarValue !== 'true' && eslintBulkDetectEnvVarValue !== '1') { + return; + } + + const configuration: IConfiguration = { + /** + * `@rushstack/eslint-bulk` should report an error if its package.json is older than this number + */ + minCliVersion: '0.0.0', + /** + * `@rushstack/eslint-bulk` will invoke this entry point + */ + cliEntryPoint: require.resolve('../exports/eslint-bulk') + }; + + console.log( + ESLINT_BULK_STDOUT_START_DELIMETER + JSON.stringify(configuration) + ESLINT_BULK_STDOUT_END_DELIMETER + ); +} + +export function getPathToLinterJS(): string { + if (!eslintFolder) { + throw new Error('Cannot find ESLint installation to patch.'); + } + + return `${eslintFolder}/lib/linter/linter.js`; +} + +export function ensurePathToGeneratedPatch(): string { + const patchesFolderPath: string = `${os.tmpdir()}/rushstack-eslint-bulk-${CURRENT_PACKAGE_VERSION}/patches`; + fs.mkdirSync(patchesFolderPath, { recursive: true }); + const pathToGeneratedPatch: string = `${patchesFolderPath}/linter-patch-v${eslintPackageVersion}.js`; + return pathToGeneratedPatch; +} diff --git a/eslint/eslint-patch/src/exports/eslint-bulk.ts b/eslint/eslint-patch/src/exports/eslint-bulk.ts new file mode 100644 index 00000000000..b096074c206 --- /dev/null +++ b/eslint/eslint-patch/src/exports/eslint-bulk.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// "lib/exports/eslint-bulk" is the entry point for the @rushstack/eslint-bulk command line front end. + +import '../eslint-bulk-suppressions/cli/start'; diff --git a/eslint/eslint-patch/src/modern-module-resolution.ts b/eslint/eslint-patch/src/modern-module-resolution.ts index 04412c0ea70..3b0e0003dd5 100644 --- a/eslint/eslint-patch/src/modern-module-resolution.ts +++ b/eslint/eslint-patch/src/modern-module-resolution.ts @@ -7,213 +7,39 @@ // // require("@rushstack/eslint-patch/modern-module-resolution"); // -const path = require('path'); -const fs = require('fs'); -const isModuleResolutionError: (ex: unknown) => boolean = (ex) => - typeof ex === 'object' && !!ex && 'code' in ex && (ex as { code: unknown }).code === 'MODULE_NOT_FOUND'; +import { + configArrayFactory, + ModuleResolver, + isModuleResolutionError, + ESLINT_MAJOR_VERSION +} from './_patch-base'; -// Module path for eslintrc.cjs -// Example: ".../@eslint/eslintrc/dist/eslintrc.cjs" -let eslintrcBundlePath: string | undefined = undefined; +// error: "The argument 'filename' must be a file URL object, file URL string, or absolute path string. Received ''" +const isInvalidImporterPath: (ex: unknown) => boolean = (ex) => + (ex as { code: unknown } | undefined)?.code === 'ERR_INVALID_ARG_VALUE'; -// Module path for config-array-factory.js -// Example: ".../@eslint/eslintrc/lib/config-array-factory" -let configArrayFactoryPath: string | undefined = undefined; +if (!configArrayFactory.__loadPluginPatched) { + configArrayFactory.__loadPluginPatched = true; + // eslint-disable-next-line @typescript-eslint/typedef + const originalLoadPlugin = configArrayFactory.prototype._loadPlugin; -// Module path for relative-module-resolver.js -// Example: ".../@eslint/eslintrc/lib/shared/relative-module-resolver" -let moduleResolverPath: string | undefined = undefined; - -// Folder path where ESLint's package.json can be found -// Example: ".../node_modules/eslint" -let eslintFolder: string | undefined = undefined; - -// Probe for the ESLint >=8.0.0 layout: -for (let currentModule = module; ; ) { - if (!eslintrcBundlePath) { - // For ESLint >=8.0.0, all @eslint/eslintrc code is bundled at this path: - // .../@eslint/eslintrc/dist/eslintrc.cjs - try { - const eslintrcFolder = path.dirname( - require.resolve('@eslint/eslintrc/package.json', { paths: [currentModule.path] }) - ); - - // Make sure we actually resolved the module in our call path - // and not some other spurious dependency. - if (path.join(eslintrcFolder, 'dist/eslintrc.cjs') === currentModule.filename) { - eslintrcBundlePath = path.join(eslintrcFolder, 'dist/eslintrc.cjs'); - } - } catch (ex: unknown) { - // Module resolution failures are expected, as we're walking - // up our require stack to look for eslint. All other errors - // are rethrown. - if (!isModuleResolutionError(ex)) { - throw ex; - } - } - } else { - // Next look for a file in ESLint's folder - // .../eslint/lib/cli-engine/cli-engine.js - try { - const eslintCandidateFolder = path.dirname( - require.resolve('eslint/package.json', { - paths: [currentModule.path] - }) - ); - - // Make sure we actually resolved the module in our call path - // and not some other spurious dependency. - if (path.join(eslintCandidateFolder, 'lib/cli-engine/cli-engine.js') === currentModule.filename) { - eslintFolder = eslintCandidateFolder; - break; - } - } catch (ex: unknown) { - // Module resolution failures are expected, as we're walking - // up our require stack to look for eslint. All other errors - // are rethrown. - if (!isModuleResolutionError(ex)) { - throw ex; - } - } - } - - if (!currentModule.parent) { - break; - } - currentModule = currentModule.parent; -} - -if (!eslintFolder) { - // Probe for the ESLint >=7.8.0 layout: - for (let currentModule = module; ; ) { - if (!configArrayFactoryPath) { - // For ESLint >=7.8.0, config-array-factory.js is at this path: - // .../@eslint/eslintrc/lib/config-array-factory.js - try { - const eslintrcFolder = path.dirname( - require.resolve('@eslint/eslintrc/package.json', { - paths: [currentModule.path] - }) - ); - - if (path.join(eslintrcFolder, '/lib/config-array-factory.js') == currentModule.filename) { - configArrayFactoryPath = path.join(eslintrcFolder, 'lib/config-array-factory.js'); - moduleResolverPath = path.join(eslintrcFolder, 'lib/shared/relative-module-resolver'); - } - } catch (ex: unknown) { - // Module resolution failures are expected, as we're walking - // up our require stack to look for eslint. All other errors - // are rethrown. - if (!isModuleResolutionError(ex)) { - throw ex; - } - } - } else { - // Next look for a file in ESLint's folder - // .../eslint/lib/cli-engine/cli-engine.js - try { - const eslintCandidateFolder = path.dirname( - require.resolve('eslint/package.json', { - paths: [currentModule.path] - }) - ); - - if (path.join(eslintCandidateFolder, 'lib/cli-engine/cli-engine.js') == currentModule.filename) { - eslintFolder = eslintCandidateFolder; - break; - } - } catch (ex: unknown) { - // Module resolution failures are expected, as we're walking - // up our require stack to look for eslint. All other errors - // are rethrown. - if (!isModuleResolutionError(ex)) { - throw ex; - } - } - } - - if (!currentModule.parent) { - break; - } - currentModule = currentModule.parent; - } -} - -if (!eslintFolder) { - // Probe for the <7.8.0 layout: - for (let currentModule = module; ; ) { - // For ESLint <7.8.0, config-array-factory.js was at this path: - // .../eslint/lib/cli-engine/config-array-factory.js - if (/[\\/]eslint[\\/]lib[\\/]cli-engine[\\/]config-array-factory\.js$/i.test(currentModule.filename)) { - eslintFolder = path.join(path.dirname(currentModule.filename), '../..'); - configArrayFactoryPath = path.join(eslintFolder, 'lib/cli-engine/config-array-factory'); - moduleResolverPath = path.join(eslintFolder, 'lib/shared/relative-module-resolver'); - break; - } - - if (!currentModule.parent) { - // This was tested with ESLint 6.1.0 .. 7.12.1. - throw new Error( - 'Failed to patch ESLint because the calling module was not recognized.\n' + - 'If you are using a newer ESLint version that may be unsupported, please create a GitHub issue:\n' + - 'https://github.com/microsoft/rushstack/issues' - ); - } - currentModule = currentModule.parent; - } -} - -// Detect the ESLint package version -const eslintPackageJson = fs.readFileSync(path.join(eslintFolder, 'package.json')).toString(); -const eslintPackageObject = JSON.parse(eslintPackageJson); -const eslintPackageVersion = eslintPackageObject.version; -const versionMatch = /^([0-9]+)\./.exec(eslintPackageVersion); // parse the SemVer MAJOR part -if (!versionMatch) { - throw new Error('Unable to parse ESLint version: ' + eslintPackageVersion); -} -const eslintMajorVersion = Number(versionMatch[1]); -if (!(eslintMajorVersion >= 6 && eslintMajorVersion <= 8)) { - throw new Error( - 'The patch-eslint.js script has only been tested with ESLint version 6.x, 7.x, and 8.x.' + - ` (Your version: ${eslintPackageVersion})\n` + - 'Consider reporting a GitHub issue:\n' + - 'https://github.com/microsoft/rushstack/issues' - ); -} - -let ConfigArrayFactory; -if (eslintMajorVersion === 8) { - ConfigArrayFactory = require(eslintrcBundlePath!).Legacy.ConfigArrayFactory; -} else { - ConfigArrayFactory = require(configArrayFactoryPath!).ConfigArrayFactory; -} -if (!ConfigArrayFactory.__patched) { - ConfigArrayFactory.__patched = true; - - let ModuleResolver: { resolve: any }; - if (eslintMajorVersion === 8) { - ModuleResolver = require(eslintrcBundlePath!).Legacy.ModuleResolver; - } else { - ModuleResolver = require(moduleResolverPath!); - } - const originalLoadPlugin = ConfigArrayFactory.prototype._loadPlugin; - - if (eslintMajorVersion === 6) { + if (ESLINT_MAJOR_VERSION === 6) { // ESLint 6.x - ConfigArrayFactory.prototype._loadPlugin = function ( + // https://github.com/eslint/eslint/blob/9738f8cc864d769988ccf42bb70f524444df1349/lib/cli-engine/config-array-factory.js#L915 + configArrayFactory.prototype._loadPlugin = function ( name: string, importerPath: string, importerName: string ) { - const originalResolve = ModuleResolver.resolve; + const originalResolve: (moduleName: string, relativeToPath: string) => string = ModuleResolver.resolve; try { ModuleResolver.resolve = function (moduleName: string, relativeToPath: string) { try { // resolve using importerPath instead of relativeToPath return originalResolve.call(this, moduleName, importerPath); } catch (e) { - if (isModuleResolutionError(e)) { + if (isModuleResolutionError(e) || isInvalidImporterPath(e)) { return originalResolve.call(this, moduleName, relativeToPath); } throw e; @@ -226,15 +52,17 @@ if (!ConfigArrayFactory.__patched) { }; } else { // ESLint 7.x || 8.x - ConfigArrayFactory.prototype._loadPlugin = function (name: string, ctx: Record) { - const originalResolve = ModuleResolver.resolve; + // https://github.com/eslint/eslintrc/blob/242d569020dfe4f561e4503787b99ec016337457/lib/config-array-factory.js#L1023 + configArrayFactory.prototype._loadPlugin = function (name: string, ctx: Record) { + const originalResolve: (moduleName: string, relativeToPath: string | unknown) => string = + ModuleResolver.resolve; try { ModuleResolver.resolve = function (moduleName: string, relativeToPath: string) { try { // resolve using ctx.filePath instead of relativeToPath return originalResolve.call(this, moduleName, ctx.filePath); } catch (e) { - if (isModuleResolutionError(e)) { + if (isModuleResolutionError(e) || isInvalidImporterPath(e)) { return originalResolve.call(this, moduleName, relativeToPath); } throw e; diff --git a/eslint/eslint-patch/tsconfig.json b/eslint/eslint-patch/tsconfig.json index 7512871fdbf..1a33d17b873 100644 --- a/eslint/eslint-patch/tsconfig.json +++ b/eslint/eslint-patch/tsconfig.json @@ -1,7 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["node"] - } + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/eslint/eslint-plugin-packlets/.eslintrc.js.disabled b/eslint/eslint-plugin-packlets/.eslintrc.js.disabled deleted file mode 100644 index 02b5abc2856..00000000000 --- a/eslint/eslint-plugin-packlets/.eslintrc.js.disabled +++ /dev/null @@ -1,5 +0,0 @@ -NOTE: We do not invoke ESLint on this project's source files, because ESLint's module resolution (as of 6.x) is naive -and fairly brittle. It gets confused between the dependencies of this project, versus the dependencies of -@rushstack/eslint-config (which imports the previously published version of this project). Normally we solve -this problem by using Rush's "decoupledLocalDependencies" feature, but that fails because ESLint does not correctly -implement NodeJS module resolution. diff --git a/eslint/eslint-plugin-packlets/.npmignore b/eslint/eslint-plugin-packlets/.npmignore index 0164a20d7a9..f7a40e10213 100644 --- a/eslint/eslint-plugin-packlets/.npmignore +++ b/eslint/eslint-plugin-packlets/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,16 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/eslint/eslint-plugin-packlets/CHANGELOG.json b/eslint/eslint-plugin-packlets/CHANGELOG.json index 9875e56a62e..ab7d9891835 100644 --- a/eslint/eslint-plugin-packlets/CHANGELOG.json +++ b/eslint/eslint-plugin-packlets/CHANGELOG.json @@ -1,6 +1,187 @@ { "name": "@rushstack/eslint-plugin-packlets", "entries": [ + { + "version": "0.15.2", + "tag": "@rushstack/eslint-plugin-packlets_v0.15.2", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "patch": [ + { + "comment": "Bump `@typescript-eslint/*` dependencies to `~8.56.1`." + } + ] + } + }, + { + "version": "0.15.1", + "tag": "@rushstack/eslint-plugin-packlets_v0.15.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.4.1`" + } + ] + } + }, + { + "version": "0.15.0", + "tag": "@rushstack/eslint-plugin-packlets_v0.15.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.4.0`" + } + ] + } + }, + { + "version": "0.14.0", + "tag": "@rushstack/eslint-plugin-packlets_v0.14.0", + "date": "Mon, 13 Oct 2025 15:13:02 GMT", + "comments": { + "minor": [ + { + "comment": "Bump `eslint` to `~9.37.0` and the `@typescript-eslint/*` packages to `~8.46.0`." + } + ] + } + }, + { + "version": "0.13.0", + "tag": "@rushstack/eslint-plugin-packlets_v0.13.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ] + } + }, + { + "version": "0.12.0", + "tag": "@rushstack/eslint-plugin-packlets_v0.12.0", + "date": "Thu, 26 Jun 2025 18:57:04 GMT", + "comments": { + "minor": [ + { + "comment": "Update for compatibility with ESLint 9" + } + ] + } + }, + { + "version": "0.11.0", + "tag": "@rushstack/eslint-plugin-packlets_v0.11.0", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `@typescript-eslint/*` packages to add support for TypeScript 5.8." + } + ] + } + }, + { + "version": "0.10.0", + "tag": "@rushstack/eslint-plugin-packlets_v0.10.0", + "date": "Sat, 01 Mar 2025 07:23:16 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `@typescript-eslint/*` dependencies to `~8.24.0` to support newer versions of TypeScript." + } + ] + } + }, + { + "version": "0.9.2", + "tag": "@rushstack/eslint-plugin-packlets_v0.9.2", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.4`" + } + ] + } + }, + { + "version": "0.9.1", + "tag": "@rushstack/eslint-plugin-packlets_v0.9.1", + "date": "Sat, 17 Feb 2024 06:24:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.3`" + } + ] + } + }, + { + "version": "0.9.0", + "tag": "@rushstack/eslint-plugin-packlets_v0.9.0", + "date": "Tue, 16 Jan 2024 18:30:10 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for TypeScript 5.3 with @typescript-eslint 6.19.x" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.2`" + } + ] + } + }, + { + "version": "0.8.1", + "tag": "@rushstack/eslint-plugin-packlets_v0.8.1", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.1`" + } + ] + } + }, + { + "version": "0.8.0", + "tag": "@rushstack/eslint-plugin-packlets_v0.8.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.0`" + } + ] + } + }, { "version": "0.7.0", "tag": "@rushstack/eslint-plugin-packlets_v0.7.0", diff --git a/eslint/eslint-plugin-packlets/CHANGELOG.md b/eslint/eslint-plugin-packlets/CHANGELOG.md index e6eb17a5605..cd04b30e91b 100644 --- a/eslint/eslint-plugin-packlets/CHANGELOG.md +++ b/eslint/eslint-plugin-packlets/CHANGELOG.md @@ -1,6 +1,93 @@ # Change Log - @rushstack/eslint-plugin-packlets -This log was last generated on Mon, 22 May 2023 06:34:32 GMT and should not be manually modified. +This log was last generated on Wed, 25 Feb 2026 21:39:42 GMT and should not be manually modified. + +## 0.15.2 +Wed, 25 Feb 2026 21:39:42 GMT + +### Patches + +- Bump `@typescript-eslint/*` dependencies to `~8.56.1`. + +## 0.15.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.15.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.14.0 +Mon, 13 Oct 2025 15:13:02 GMT + +### Minor changes + +- Bump `eslint` to `~9.37.0` and the `@typescript-eslint/*` packages to `~8.46.0`. + +## 0.13.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 0.12.0 +Thu, 26 Jun 2025 18:57:04 GMT + +### Minor changes + +- Update for compatibility with ESLint 9 + +## 0.11.0 +Tue, 11 Mar 2025 02:12:33 GMT + +### Minor changes + +- Bump the `@typescript-eslint/*` packages to add support for TypeScript 5.8. + +## 0.10.0 +Sat, 01 Mar 2025 07:23:16 GMT + +### Minor changes + +- Bump the `@typescript-eslint/*` dependencies to `~8.24.0` to support newer versions of TypeScript. + +## 0.9.2 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.9.1 +Sat, 17 Feb 2024 06:24:34 GMT + +_Version update only_ + +## 0.9.0 +Tue, 16 Jan 2024 18:30:10 GMT + +### Minor changes + +- Add support for TypeScript 5.3 with @typescript-eslint 6.19.x + +## 0.8.1 +Tue, 26 Sep 2023 09:30:33 GMT + +_Version update only_ + +## 0.8.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 ## 0.7.0 Mon, 22 May 2023 06:34:32 GMT diff --git a/eslint/eslint-plugin-packlets/config/jest.config.json b/eslint/eslint-plugin-packlets/config/jest.config.json index 4bb17bde3ee..7c0f9ccc9d6 100644 --- a/eslint/eslint-plugin-packlets/config/jest.config.json +++ b/eslint/eslint-plugin-packlets/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "decoupled-local-node-rig/profiles/default/config/jest.config.json" } diff --git a/eslint/eslint-plugin-packlets/config/rig.json b/eslint/eslint-plugin-packlets/config/rig.json index 6ac88a96368..cc98dea43dd 100644 --- a/eslint/eslint-plugin-packlets/config/rig.json +++ b/eslint/eslint-plugin-packlets/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "decoupled-local-node-rig" } diff --git a/eslint/eslint-plugin-packlets/eslint.config.js b/eslint/eslint-plugin-packlets/eslint.config.js new file mode 100644 index 00000000000..f83aea7d1b7 --- /dev/null +++ b/eslint/eslint-plugin-packlets/eslint.config.js @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); +const tsdocMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/tsdoc'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + ...tsdocMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/eslint/eslint-plugin-packlets/package.json b/eslint/eslint-plugin-packlets/package.json index 58377dcb5b4..6863c681f62 100644 --- a/eslint/eslint-plugin-packlets/package.json +++ b/eslint/eslint-plugin-packlets/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/eslint-plugin-packlets", - "version": "0.7.0", + "version": "0.15.2", "description": "A lightweight alternative to NPM packages for organizing source files within a single project", "license": "MIT", "repository": { @@ -15,30 +15,49 @@ "packlets", "rules" ], - "main": "lib/index.js", - "typings": "lib/index.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "scripts": { "build": "heft build --clean", - "_phase:build": "heft build --clean", - "_phase:test": "heft test --no-build" + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" }, "dependencies": { "@rushstack/tree-pattern": "workspace:*", - "@typescript-eslint/experimental-utils": "~5.59.2" + "@typescript-eslint/utils": "~8.56.1" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" }, "devDependencies": { - "@rushstack/heft": "0.50.6", - "@rushstack/heft-node-rig": "1.13.0", - "@types/eslint": "8.2.0", - "@types/estree": "0.0.50", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "@typescript-eslint/parser": "~5.59.2", - "@typescript-eslint/typescript-estree": "~5.59.2", - "eslint": "~8.7.0", - "typescript": "~5.0.4" - } + "@rushstack/heft": "1.2.22", + "@typescript-eslint/parser": "~8.56.1", + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0", + "typescript": "~5.8.2" + }, + "sideEffects": false } diff --git a/eslint/eslint-plugin-packlets/src/DependencyAnalyzer.ts b/eslint/eslint-plugin-packlets/src/DependencyAnalyzer.ts index a74fc3e6022..71ad4b8609b 100644 --- a/eslint/eslint-plugin-packlets/src/DependencyAnalyzer.ts +++ b/eslint/eslint-plugin-packlets/src/DependencyAnalyzer.ts @@ -4,7 +4,7 @@ import type * as ts from 'typescript'; import { Path } from './Path'; -import { PackletAnalyzer } from './PackletAnalyzer'; +import type { PackletAnalyzer } from './PackletAnalyzer'; enum RefFileKind { Import, @@ -15,7 +15,7 @@ enum RefFileKind { // TypeScript compiler internal: // Version range: >= 3.6.0, <= 4.2.0 // https://github.com/microsoft/TypeScript/blob/5ecdcef4cecfcdc86bd681b377636422447507d7/src/compiler/program.ts#L541 -interface RefFile { +interface IRefFile { // The absolute path of the module that was imported. // (Normalized to an all lowercase ts.Path string.) referencedFileName: string; @@ -47,21 +47,21 @@ enum FileIncludeKind { // TypeScript compiler internal: // Version range: > 4.2.0 // https://github.com/microsoft/TypeScript/blob/2eca17d7c1a3fb2b077f3a910d5019d74b6f07a0/src/compiler/types.ts#L3748 -type FileIncludeReason = { +interface IFileIncludeReason { kind: FileIncludeKind; file: string | undefined; -}; +} interface ITsProgramInternals extends ts.Program { // TypeScript compiler internal: // Version range: >= 3.6.0, <= 4.2.0 // https://github.com/microsoft/TypeScript/blob/5ecdcef4cecfcdc86bd681b377636422447507d7/src/compiler/types.ts#L3723 - getRefFileMap?: () => Map | undefined; + getRefFileMap?: () => Map | undefined; // TypeScript compiler internal: // Version range: > 4.2.0 // https://github.com/microsoft/TypeScript/blob/2eca17d7c1a3fb2b077f3a910d5019d74b6f07a0/src/compiler/types.ts#L3871 - getFileIncludeReasons?: () => Map; + getFileIncludeReasons?: () => Map; } /** @@ -89,93 +89,79 @@ interface IImportListNode extends IPackletImport { previousNode: IImportListNode | undefined; } -export class DependencyAnalyzer { - /** - * @param packletName - the packlet to be checked next in our traversal - * @param startingPackletName - the packlet that we started with; if the traversal reaches this packlet, - * then a circular dependency has been detected - * @param refFileMap - the compiler's `refFileMap` data structure describing import relationships - * @param fileIncludeReasonsMap - the compiler's data structure describing import relationships - * @param program - the compiler's `ts.Program` object - * @param packletsFolderPath - the absolute path of the "src/packlets" folder. - * @param visitedPacklets - the set of packlets that have already been visited in this traversal - * @param previousNode - a linked list of import statements that brought us to this step in the traversal - */ - private static _walkImports( - packletName: string, - startingPackletName: string, - refFileMap: Map | undefined, - fileIncludeReasonsMap: Map | undefined, - program: ts.Program, - packletsFolderPath: string, - visitedPacklets: Set, - previousNode: IImportListNode | undefined - ): IImportListNode | undefined { - visitedPacklets.add(packletName); +/** + * @param packletName - the packlet to be checked next in our traversal + * @param startingPackletName - the packlet that we started with; if the traversal reaches this packlet, + * then a circular dependency has been detected + * @param refFileMap - the compiler's `refFileMap` data structure describing import relationships + * @param fileIncludeReasonsMap - the compiler's data structure describing import relationships + * @param program - the compiler's `ts.Program` object + * @param packletsFolderPath - the absolute path of the "src/packlets" folder. + * @param visitedPacklets - the set of packlets that have already been visited in this traversal + * @param previousNode - a linked list of import statements that brought us to this step in the traversal + */ +function _walkImports( + packletName: string, + startingPackletName: string, + refFileMap: Map | undefined, + fileIncludeReasonsMap: Map | undefined, + program: ts.Program, + packletsFolderPath: string, + visitedPacklets: Set, + previousNode: IImportListNode | undefined +): IImportListNode | undefined { + visitedPacklets.add(packletName); - const packletEntryPoint: string = Path.join(packletsFolderPath, packletName, 'index'); + const packletEntryPoint: string = Path.join(packletsFolderPath, packletName, 'index'); - const tsSourceFile: ts.SourceFile | undefined = - program.getSourceFile(packletEntryPoint + '.ts') || program.getSourceFile(packletEntryPoint + '.tsx'); - if (!tsSourceFile) { - return undefined; - } + const tsSourceFile: ts.SourceFile | undefined = + program.getSourceFile(packletEntryPoint + '.ts') || program.getSourceFile(packletEntryPoint + '.tsx'); + if (!tsSourceFile) { + return undefined; + } - const referencingFilePaths: string[] = []; + const referencingFilePaths: string[] = []; - if (refFileMap) { - // TypeScript version range: >= 3.6.0, <= 4.2.0 - const refFiles: RefFile[] | undefined = refFileMap.get((tsSourceFile as any).path as any); - if (refFiles) { - for (const refFile of refFiles) { - if (refFile.kind === RefFileKind.Import) { - referencingFilePaths.push(refFile.file); - } + if (refFileMap) { + // TypeScript version range: >= 3.6.0, <= 4.2.0 + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const refFiles: IRefFile[] | undefined = refFileMap.get((tsSourceFile as any).path); + if (refFiles) { + for (const refFile of refFiles) { + if (refFile.kind === RefFileKind.Import) { + referencingFilePaths.push(refFile.file); } } - } else if (fileIncludeReasonsMap) { - // Typescript version range: > 4.2.0 - const fileIncludeReasons: FileIncludeReason[] | undefined = fileIncludeReasonsMap.get( - (tsSourceFile as any).path as any - ); - if (fileIncludeReasons) { - for (const fileIncludeReason of fileIncludeReasons) { - if (fileIncludeReason.kind === FileIncludeKind.Import) { - if (fileIncludeReason.file) { - referencingFilePaths.push(fileIncludeReason.file); - } + } + } else if (fileIncludeReasonsMap) { + // Typescript version range: > 4.2.0 + const fileIncludeReasons: IFileIncludeReason[] | undefined = fileIncludeReasonsMap.get( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (tsSourceFile as any).path + ); + if (fileIncludeReasons) { + for (const fileIncludeReason of fileIncludeReasons) { + if (fileIncludeReason.kind === FileIncludeKind.Import) { + if (fileIncludeReason.file) { + referencingFilePaths.push(fileIncludeReason.file); } } } } + } - for (const referencingFilePath of referencingFilePaths) { - // Is it a reference to a packlet? - if (Path.isUnder(referencingFilePath, packletsFolderPath)) { - const referencingRelativePath: string = Path.relative(packletsFolderPath, referencingFilePath); - const referencingPathParts: string[] = referencingRelativePath.split(/[\/\\]+/); - const referencingPackletName: string = referencingPathParts[0]; - - // Did we return to where we started from? - if (referencingPackletName === startingPackletName) { - // Ignore the degenerate case where the starting node imports itself, - // since @rushstack/packlets/mechanics will already report that. - if (previousNode) { - // Make a new linked list node to record this step of the traversal - const importListNode: IImportListNode = { - previousNode: previousNode, - fromFilePath: referencingFilePath, - packletName: packletName - }; - - // The traversal has returned to the packlet that we started from; - // this means we have detected a circular dependency - return importListNode; - } - } + for (const referencingFilePath of referencingFilePaths) { + // Is it a reference to a packlet? + if (Path.isUnder(referencingFilePath, packletsFolderPath)) { + const referencingRelativePath: string = Path.relative(packletsFolderPath, referencingFilePath); + const referencingPathParts: string[] = referencingRelativePath.split(/[\/\\]+/); + const referencingPackletName: string = referencingPathParts[0]; - // Have we already analyzed this packlet? - if (!visitedPacklets.has(referencingPackletName)) { + // Did we return to where we started from? + if (referencingPackletName === startingPackletName) { + // Ignore the degenerate case where the starting node imports itself, + // since @rushstack/packlets/mechanics will already report that. + if (previousNode) { // Make a new linked list node to record this step of the traversal const importListNode: IImportListNode = { previousNode: previousNode, @@ -183,26 +169,42 @@ export class DependencyAnalyzer { packletName: packletName }; - const result: IImportListNode | undefined = DependencyAnalyzer._walkImports( - referencingPackletName, - startingPackletName, - refFileMap, - fileIncludeReasonsMap, - program, - packletsFolderPath, - visitedPacklets, - importListNode - ); - if (result) { - return result; - } + // The traversal has returned to the packlet that we started from; + // this means we have detected a circular dependency + return importListNode; } } - } - return undefined; + // Have we already analyzed this packlet? + if (!visitedPacklets.has(referencingPackletName)) { + // Make a new linked list node to record this step of the traversal + const importListNode: IImportListNode = { + previousNode: previousNode, + fromFilePath: referencingFilePath, + packletName: packletName + }; + + const result: IImportListNode | undefined = _walkImports( + referencingPackletName, + startingPackletName, + refFileMap, + fileIncludeReasonsMap, + program, + packletsFolderPath, + visitedPacklets, + importListNode + ); + if (result) { + return result; + } + } + } } + return undefined; +} + +export class DependencyAnalyzer { /** * For the specified packlet, trace all modules that import it, looking for a circular dependency * between packlets. If found, an array is returned describing the import statements that cause @@ -236,8 +238,8 @@ export class DependencyAnalyzer { ): IPackletImport[] | undefined { const programInternals: ITsProgramInternals = program; - let refFileMap: Map | undefined; - let fileIncludeReasonsMap: Map | undefined; + let refFileMap: Map | undefined; + let fileIncludeReasonsMap: Map | undefined; if (programInternals.getRefFileMap) { // TypeScript version range: >= 3.6.0, <= 4.2.0 @@ -255,7 +257,7 @@ export class DependencyAnalyzer { const visitedPacklets: Set = new Set(); - const listNode: IImportListNode | undefined = DependencyAnalyzer._walkImports( + const listNode: IImportListNode | undefined = _walkImports( packletName, packletName, refFileMap, diff --git a/eslint/eslint-plugin-packlets/src/PackletAnalyzer.ts b/eslint/eslint-plugin-packlets/src/PackletAnalyzer.ts index 795642f89e8..7c0e98e621d 100644 --- a/eslint/eslint-plugin-packlets/src/PackletAnalyzer.ts +++ b/eslint/eslint-plugin-packlets/src/PackletAnalyzer.ts @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as fs from 'fs'; +import * as fs from 'node:fs'; + import { Path } from './Path'; export type InputFileMessageIds = @@ -22,9 +23,9 @@ export interface IAnalyzerError { data?: Readonly>; } -export class PackletAnalyzer { - private static _validPackletName: RegExp = /^[a-z0-9]+(-[a-z0-9]+)*$/; +const _validPackletName: RegExp = /^[a-z0-9]+(-[a-z0-9]+)*$/; +export class PackletAnalyzer { /** * The input file being linted. * @@ -74,14 +75,13 @@ export class PackletAnalyzer { this.isEntryPoint = false; // Example: /path/to/my-project/src - let srcFolderPath: string | undefined; if (!tsconfigFilePath) { this.error = { messageId: 'missing-tsconfig' }; return; } - srcFolderPath = Path.join(Path.dirname(tsconfigFilePath), 'src'); + const srcFolderPath: string = Path.join(Path.dirname(tsconfigFilePath), 'src'); if (!fs.existsSync(srcFolderPath)) { this.error = { messageId: 'missing-src-folder', data: { srcFolderPath } }; @@ -104,7 +104,7 @@ export class PackletAnalyzer { const expectedPackletsFolder: string = Path.join(srcFolderPath, 'packlets'); - for (let i = 0; i < pathParts.length; ++i) { + for (let i: number = 0; i < pathParts.length; ++i) { const pathPart: string = pathParts[i]; if (pathPart.toUpperCase() === 'PACKLETS') { if (pathPart !== 'packlets') { @@ -148,7 +148,7 @@ export class PackletAnalyzer { const thirdPartWithoutExtension: string = Path.parse(thirdPart).name; if (thirdPartWithoutExtension.toUpperCase() === 'INDEX') { - if (!PackletAnalyzer._validPackletName.test(packletName)) { + if (!_validPackletName.test(packletName)) { this.error = { messageId: 'invalid-packlet-name', data: { packletName } }; return; } @@ -164,7 +164,10 @@ export class PackletAnalyzer { } } - public static analyzeInputFile(inputFilePath: string, tsconfigFilePath: string | undefined) { + public static analyzeInputFile( + inputFilePath: string, + tsconfigFilePath: string | undefined + ): PackletAnalyzer { return new PackletAnalyzer(inputFilePath, tsconfigFilePath); } diff --git a/eslint/eslint-plugin-packlets/src/Path.ts b/eslint/eslint-plugin-packlets/src/Path.ts index e46d697c38b..e5a015bcfb6 100644 --- a/eslint/eslint-plugin-packlets/src/Path.ts +++ b/eslint/eslint-plugin-packlets/src/Path.ts @@ -1,11 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as fs from 'fs'; +import * as path from 'node:path'; +import * as fs from 'node:fs'; export type ParsedPath = path.ParsedPath; +const RELATIVE_PATH_REGEXP: RegExp = /^[.\/\\]+$/; + export class Path { /** * Whether the filesystem is assumed to be case sensitive for Path operations. @@ -28,78 +30,11 @@ export class Path { * * @see {@link https://nodejs.org/en/docs/guides/working-with-different-filesystems/} */ - public static usingCaseSensitive: boolean = Path._detectCaseSensitive(); - - private static _detectCaseSensitive(): boolean { - // Can our own file be accessed using a path with different case? If so, then the filesystem is case-insensitive. - return !fs.existsSync(__filename.toUpperCase()); - } - - // Removes redundant trailing slashes from a path. - private static _trimTrailingSlashes(inputPath: string): string { - // Examples: - // "/a/b///\\" --> "/a/b" - // "/" --> "/" - return inputPath.replace(/(?<=[^\/\\])[\/\\]+$/, ''); - } - - // An implementation of path.relative() that is case-insensitive. - private static _relativeCaseInsensitive(from: string, to: string): string { - // path.relative() apples path.normalize() and also trims any trailing slashes. - // Since we'll be matching toNormalized against result, we need to do that for our string as well. - const normalizedTo: string = Path._trimTrailingSlashes(path.normalize(to)); - - // We start by converting everything to uppercase and call path.relative() - const uppercasedFrom: string = from.toUpperCase(); - const uppercasedTo: string = normalizedTo.toUpperCase(); - - // The result will be all uppercase because its inputs were uppercased - const uppercasedResult: string = path.relative(uppercasedFrom, uppercasedTo); - - // Are there any cased characters in the result? - if (uppercasedResult.toLowerCase() === uppercasedResult) { - // No cased characters - // Example: "../.." - return uppercasedResult; - } - - // Example: - // from="/a/b/c" - // to="/a/b/d/e" - // - // fromNormalized="/A/B/C" - // toNormalized="/A/B/D/E" - // - // result="../D/E" - // - // Scan backwards comparing uppercasedResult versus uppercasedTo, stopping at the first place where they differ. - let resultIndex: number = uppercasedResult.length; - let toIndex: number = normalizedTo.length; - for (;;) { - if (resultIndex === 0 || toIndex === 0) { - // Stop if we reach the start of the string - break; - } - - if (uppercasedResult.charCodeAt(resultIndex - 1) !== uppercasedTo.charCodeAt(toIndex - 1)) { - // Stop before we reach a character that is different - break; - } - - --resultIndex; - --toIndex; - } - - // Replace the matching part with the properly cased substring from the "normalizedTo" input - // - // Example: - // ".." + "/d/e" = "../d/e" - return uppercasedResult.substring(0, resultIndex) + normalizedTo.substring(toIndex); - } + public static usingCaseSensitive: boolean = _detectCaseSensitive(); public static relative(from: string, to: string): string { if (!Path.usingCaseSensitive) { - return Path._relativeCaseInsensitive(from, to); + return _relativeCaseInsensitive(from, to); } return path.relative(from, to); } @@ -126,8 +61,6 @@ export class Path { // -------------------------------------------------------------------------------------------------------- // The operations below are borrowed from @rushstack/node-core-library - private static _relativePathRegex: RegExp = /^[.\/\\]+$/; - /** * Returns true if "childPath" is located inside the "parentFolderPath" folder * or one of its child folders. Note that "parentFolderPath" is not considered to be @@ -140,7 +73,7 @@ export class Path { */ public static isUnder(childPath: string, parentFolderPath: string): boolean { const relativePath: string = Path.relative(childPath, parentFolderPath); - return Path._relativePathRegex.test(relativePath); + return RELATIVE_PATH_REGEXP.test(relativePath); } /** @@ -164,3 +97,70 @@ export class Path { return inputPath.split('\\').join('/'); } } + +function _detectCaseSensitive(): boolean { + // Can our own file be accessed using a path with different case? If so, then the filesystem is case-insensitive. + return !fs.existsSync(__filename.toUpperCase()); +} + +// Removes redundant trailing slashes from a path. +function _trimTrailingSlashes(inputPath: string): string { + // Examples: + // "/a/b///\\" --> "/a/b" + // "/" --> "/" + return inputPath.replace(/(?<=[^\/\\])[\/\\]+$/, ''); +} + +// An implementation of path.relative() that is case-insensitive. +export function _relativeCaseInsensitive(from: string, to: string): string { + // path.relative() apples path.normalize() and also trims any trailing slashes. + // Since we'll be matching toNormalized against result, we need to do that for our string as well. + const normalizedTo: string = _trimTrailingSlashes(path.normalize(to)); + + // We start by converting everything to uppercase and call path.relative() + const uppercasedFrom: string = from.toUpperCase(); + const uppercasedTo: string = normalizedTo.toUpperCase(); + + // The result will be all uppercase because its inputs were uppercased + const uppercasedResult: string = path.relative(uppercasedFrom, uppercasedTo); + + // Are there any cased characters in the result? + if (uppercasedResult.toLowerCase() === uppercasedResult) { + // No cased characters + // Example: "../.." + return uppercasedResult; + } + + // Example: + // from="/a/b/c" + // to="/a/b/d/e" + // + // fromNormalized="/A/B/C" + // toNormalized="/A/B/D/E" + // + // result="../D/E" + // + // Scan backwards comparing uppercasedResult versus uppercasedTo, stopping at the first place where they differ. + let resultIndex: number = uppercasedResult.length; + let toIndex: number = normalizedTo.length; + for (;;) { + if (resultIndex === 0 || toIndex === 0) { + // Stop if we reach the start of the string + break; + } + + if (uppercasedResult.charCodeAt(resultIndex - 1) !== uppercasedTo.charCodeAt(toIndex - 1)) { + // Stop before we reach a character that is different + break; + } + + --resultIndex; + --toIndex; + } + + // Replace the matching part with the properly cased substring from the "normalizedTo" input + // + // Example: + // ".." + "/d/e" = "../d/e" + return uppercasedResult.substring(0, resultIndex) + normalizedTo.substring(toIndex); +} diff --git a/eslint/eslint-plugin-packlets/src/circular-deps.ts b/eslint/eslint-plugin-packlets/src/circular-deps.ts index cb1c2141ccd..e36b0546e2c 100644 --- a/eslint/eslint-plugin-packlets/src/circular-deps.ts +++ b/eslint/eslint-plugin-packlets/src/circular-deps.ts @@ -2,13 +2,11 @@ // See LICENSE in the project root for license information. import type * as ts from 'typescript'; -import * as path from 'path'; - -import type { ParserServices, TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; -import { ESLintUtils } from '@typescript-eslint/experimental-utils'; +import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; +import { ESLintUtils } from '@typescript-eslint/utils'; import { PackletAnalyzer } from './PackletAnalyzer'; -import { DependencyAnalyzer, IPackletImport } from './DependencyAnalyzer'; +import { DependencyAnalyzer, type IPackletImport } from './DependencyAnalyzer'; import { Path } from './Path'; export type MessageIds = 'circular-import'; @@ -27,9 +25,7 @@ const circularDeps: TSESLint.RuleModule = { ], docs: { description: 'Check for circular dependencies between packlets', - // Deprecated in ESLint v8; Keep for backwards compatibility - category: 'Best Practices', - recommended: 'warn', + recommended: 'recommended', url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin-packlets' } as TSESLint.RuleMetaDataDocs }, @@ -40,7 +36,7 @@ const circularDeps: TSESLint.RuleModule = { // Example: /path/to/my-project/tsconfig.json const program: ts.Program = ESLintUtils.getParserServices(context).program; - const tsconfigFilePath: string | undefined = program.getCompilerOptions()['configFilePath'] as string; + const tsconfigFilePath: string | undefined = program.getCompilerOptions().configFilePath as string; const packletAnalyzer: PackletAnalyzer = PackletAnalyzer.analyzeInputFile( inputFilePath, diff --git a/eslint/eslint-plugin-packlets/src/index.ts b/eslint/eslint-plugin-packlets/src/index.ts index 9da7e86e448..7958fa842df 100644 --- a/eslint/eslint-plugin-packlets/src/index.ts +++ b/eslint/eslint-plugin-packlets/src/index.ts @@ -1,14 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { TSESLint } from '@typescript-eslint/experimental-utils'; +import type { TSESLint } from '@typescript-eslint/utils'; + import { mechanics } from './mechanics'; import { circularDeps } from './circular-deps'; import { readme } from './readme'; interface IPlugin { rules: { [ruleName: string]: TSESLint.RuleModule }; - configs: { [ruleName: string]: any }; + configs: { [ruleName: string]: unknown }; } const plugin: IPlugin = { diff --git a/eslint/eslint-plugin-packlets/src/mechanics.ts b/eslint/eslint-plugin-packlets/src/mechanics.ts index 13eaaf5b8bb..1c299d88117 100644 --- a/eslint/eslint-plugin-packlets/src/mechanics.ts +++ b/eslint/eslint-plugin-packlets/src/mechanics.ts @@ -1,10 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; -import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/experimental-utils'; +import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils'; -import { PackletAnalyzer, IAnalyzerError, InputFileMessageIds, ImportMessageIds } from './PackletAnalyzer'; +import { + PackletAnalyzer, + type IAnalyzerError, + type InputFileMessageIds, + type ImportMessageIds +} from './PackletAnalyzer'; export type MessageIds = InputFileMessageIds | ImportMessageIds; type Options = []; @@ -42,9 +47,7 @@ const mechanics: TSESLint.RuleModule = { ], docs: { description: 'Check that file paths and imports follow the basic mechanics for the packlet formalism', - // Deprecated in ESLint v8; Keep for backwards compatibility - category: 'Best Practices', - recommended: 'warn', + recommended: 'recommended', url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin-packlets' } as TSESLint.RuleMetaDataDocs }, @@ -56,7 +59,7 @@ const mechanics: TSESLint.RuleModule = { // Example: /path/to/my-project/tsconfig.json const tsconfigFilePath: string | undefined = ESLintUtils.getParserServices( context - ).program.getCompilerOptions()['configFilePath'] as string; + ).program.getCompilerOptions().configFilePath as string; const packletAnalyzer: PackletAnalyzer = PackletAnalyzer.analyzeInputFile( inputFilePath, @@ -92,6 +95,7 @@ const mechanics: TSESLint.RuleModule = { // ExportAllDeclaration matches these forms: // export * from '../../packlets/other-packlet'; // export * as X from '../../packlets/other-packlet'; + // eslint-disable-next-line @typescript-eslint/naming-convention 'ImportDeclaration, ExportNamedDeclaration, ExportAllDeclaration': ( node: TSESTree.ImportDeclaration | TSESTree.ExportNamedDeclaration | TSESTree.ExportAllDeclaration ): void => { @@ -99,7 +103,7 @@ const mechanics: TSESLint.RuleModule = { if (packletAnalyzer.projectUsesPacklets) { // Extract the import/export module path // Example: "../../packlets/other-packlet" - const modulePath = node.source.value; + const modulePath: string = node.source.value; if (typeof modulePath !== 'string') { return; } diff --git a/eslint/eslint-plugin-packlets/src/readme.ts b/eslint/eslint-plugin-packlets/src/readme.ts index 95e9eb90aef..20ceb014c09 100644 --- a/eslint/eslint-plugin-packlets/src/readme.ts +++ b/eslint/eslint-plugin-packlets/src/readme.ts @@ -1,10 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as fs from 'fs'; -import type { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; -import { ESLintUtils } from '@typescript-eslint/experimental-utils'; +import * as path from 'node:path'; +import * as fs from 'node:fs'; + +import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; +import { ESLintUtils } from '@typescript-eslint/utils'; import { PackletAnalyzer } from './PackletAnalyzer'; @@ -42,10 +43,8 @@ const readme: TSESLint.RuleModule = { docs: { description: 'Require each packlet folder to have a README.md file summarizing its purpose and usage', - // Deprecated in ESLint v8; Keep for backwards compatibility - category: 'Best Practices', // Too strict to be recommended in the default configuration - recommended: false, + recommended: 'strict', url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin-packlets' } as TSESLint.RuleMetaDataDocs }, @@ -59,7 +58,7 @@ const readme: TSESLint.RuleModule = { // Example: /path/to/my-project/tsconfig.json const tsconfigFilePath: string | undefined = ESLintUtils.getParserServices( context - ).program.getCompilerOptions()['configFilePath'] as string; + ).program.getCompilerOptions().configFilePath as string; const packletAnalyzer: PackletAnalyzer = PackletAnalyzer.analyzeInputFile( inputFilePath, diff --git a/eslint/eslint-plugin-packlets/src/test/Path.test.ts b/eslint/eslint-plugin-packlets/src/test/Path.test.ts index a023b3d64db..74f74d8ac2f 100644 --- a/eslint/eslint-plugin-packlets/src/test/Path.test.ts +++ b/eslint/eslint-plugin-packlets/src/test/Path.test.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { Path } from '../Path'; +import * as path from 'node:path'; +import { Path, _relativeCaseInsensitive } from '../Path'; function toPosixPath(value: string): string { return value.replace(/[\\\/]/g, '/'); @@ -11,8 +11,8 @@ function toNativePath(value: string): string { return value.replace(/[\\\/]/g, path.sep); } -function relativeCaseInsensitive(from: string, to: string) { - return toPosixPath(Path['_relativeCaseInsensitive'](toNativePath(from), toNativePath(to))); +function relativeCaseInsensitive(from: string, to: string): string { + return toPosixPath(_relativeCaseInsensitive(toNativePath(from), toNativePath(to))); } describe(Path.name, () => { diff --git a/eslint/eslint-plugin-packlets/tsconfig.json b/eslint/eslint-plugin-packlets/tsconfig.json index fbc2f5c0a6c..e98df1ad324 100644 --- a/eslint/eslint-plugin-packlets/tsconfig.json +++ b/eslint/eslint-plugin-packlets/tsconfig.json @@ -1,7 +1,7 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "types": ["heft-jest", "node"] + "module": "Node16" } } diff --git a/eslint/eslint-plugin-security/.eslintrc.js.disabled b/eslint/eslint-plugin-security/.eslintrc.js.disabled deleted file mode 100644 index 02b5abc2856..00000000000 --- a/eslint/eslint-plugin-security/.eslintrc.js.disabled +++ /dev/null @@ -1,5 +0,0 @@ -NOTE: We do not invoke ESLint on this project's source files, because ESLint's module resolution (as of 6.x) is naive -and fairly brittle. It gets confused between the dependencies of this project, versus the dependencies of -@rushstack/eslint-config (which imports the previously published version of this project). Normally we solve -this problem by using Rush's "decoupledLocalDependencies" feature, but that fails because ESLint does not correctly -implement NodeJS module resolution. diff --git a/eslint/eslint-plugin-security/.npmignore b/eslint/eslint-plugin-security/.npmignore index 302dbc5b019..f7a40e10213 100644 --- a/eslint/eslint-plugin-security/.npmignore +++ b/eslint/eslint-plugin-security/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,16 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/eslint/eslint-plugin-security/CHANGELOG.json b/eslint/eslint-plugin-security/CHANGELOG.json index 02d3bfcb222..c5fb2519f1c 100644 --- a/eslint/eslint-plugin-security/CHANGELOG.json +++ b/eslint/eslint-plugin-security/CHANGELOG.json @@ -1,6 +1,199 @@ { "name": "@rushstack/eslint-plugin-security", "entries": [ + { + "version": "0.14.2", + "tag": "@rushstack/eslint-plugin-security_v0.14.2", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "patch": [ + { + "comment": "Bump `@typescript-eslint/*` dependencies to `~8.56.1`." + } + ] + } + }, + { + "version": "0.14.1", + "tag": "@rushstack/eslint-plugin-security_v0.14.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.4.1`" + } + ] + } + }, + { + "version": "0.14.0", + "tag": "@rushstack/eslint-plugin-security_v0.14.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.4.0`" + } + ] + } + }, + { + "version": "0.13.0", + "tag": "@rushstack/eslint-plugin-security_v0.13.0", + "date": "Mon, 13 Oct 2025 15:13:02 GMT", + "comments": { + "minor": [ + { + "comment": "Bump `eslint` to `~9.37.0` and the `@typescript-eslint/*` packages to `~8.46.0`." + } + ] + } + }, + { + "version": "0.12.0", + "tag": "@rushstack/eslint-plugin-security_v0.12.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ] + } + }, + { + "version": "0.11.0", + "tag": "@rushstack/eslint-plugin-security_v0.11.0", + "date": "Thu, 26 Jun 2025 18:57:04 GMT", + "comments": { + "minor": [ + { + "comment": "Update for compatibility with ESLint 9" + } + ] + } + }, + { + "version": "0.10.0", + "tag": "@rushstack/eslint-plugin-security_v0.10.0", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `@typescript-eslint/*` packages to add support for TypeScript 5.8." + } + ] + } + }, + { + "version": "0.9.0", + "tag": "@rushstack/eslint-plugin-security_v0.9.0", + "date": "Sat, 01 Mar 2025 07:23:16 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `@typescript-eslint/*` dependencies to `~8.24.0` to support newer versions of TypeScript." + } + ] + } + }, + { + "version": "0.8.3", + "tag": "@rushstack/eslint-plugin-security_v0.8.3", + "date": "Thu, 19 Sep 2024 00:11:08 GMT", + "comments": { + "patch": [ + { + "comment": "Fix ESLint broken links" + } + ] + } + }, + { + "version": "0.8.2", + "tag": "@rushstack/eslint-plugin-security_v0.8.2", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.4`" + } + ] + } + }, + { + "version": "0.8.1", + "tag": "@rushstack/eslint-plugin-security_v0.8.1", + "date": "Sat, 17 Feb 2024 06:24:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.3`" + } + ] + } + }, + { + "version": "0.8.0", + "tag": "@rushstack/eslint-plugin-security_v0.8.0", + "date": "Tue, 16 Jan 2024 18:30:10 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for TypeScript 5.3 with @typescript-eslint 6.19.x" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.2`" + } + ] + } + }, + { + "version": "0.7.1", + "tag": "@rushstack/eslint-plugin-security_v0.7.1", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.1`" + } + ] + } + }, + { + "version": "0.7.0", + "tag": "@rushstack/eslint-plugin-security_v0.7.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.0`" + } + ] + } + }, { "version": "0.6.0", "tag": "@rushstack/eslint-plugin-security_v0.6.0", diff --git a/eslint/eslint-plugin-security/CHANGELOG.md b/eslint/eslint-plugin-security/CHANGELOG.md index 3bb2aad5686..ae60335a39f 100644 --- a/eslint/eslint-plugin-security/CHANGELOG.md +++ b/eslint/eslint-plugin-security/CHANGELOG.md @@ -1,6 +1,100 @@ # Change Log - @rushstack/eslint-plugin-security -This log was last generated on Mon, 22 May 2023 06:34:32 GMT and should not be manually modified. +This log was last generated on Wed, 25 Feb 2026 21:39:42 GMT and should not be manually modified. + +## 0.14.2 +Wed, 25 Feb 2026 21:39:42 GMT + +### Patches + +- Bump `@typescript-eslint/*` dependencies to `~8.56.1`. + +## 0.14.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.14.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.13.0 +Mon, 13 Oct 2025 15:13:02 GMT + +### Minor changes + +- Bump `eslint` to `~9.37.0` and the `@typescript-eslint/*` packages to `~8.46.0`. + +## 0.12.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 0.11.0 +Thu, 26 Jun 2025 18:57:04 GMT + +### Minor changes + +- Update for compatibility with ESLint 9 + +## 0.10.0 +Tue, 11 Mar 2025 02:12:33 GMT + +### Minor changes + +- Bump the `@typescript-eslint/*` packages to add support for TypeScript 5.8. + +## 0.9.0 +Sat, 01 Mar 2025 07:23:16 GMT + +### Minor changes + +- Bump the `@typescript-eslint/*` dependencies to `~8.24.0` to support newer versions of TypeScript. + +## 0.8.3 +Thu, 19 Sep 2024 00:11:08 GMT + +### Patches + +- Fix ESLint broken links + +## 0.8.2 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.8.1 +Sat, 17 Feb 2024 06:24:34 GMT + +_Version update only_ + +## 0.8.0 +Tue, 16 Jan 2024 18:30:10 GMT + +### Minor changes + +- Add support for TypeScript 5.3 with @typescript-eslint 6.19.x + +## 0.7.1 +Tue, 26 Sep 2023 09:30:33 GMT + +_Version update only_ + +## 0.7.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 ## 0.6.0 Mon, 22 May 2023 06:34:32 GMT diff --git a/eslint/eslint-plugin-security/README.md b/eslint/eslint-plugin-security/README.md index a585bfcda4d..861aafa4320 100644 --- a/eslint/eslint-plugin-security/README.md +++ b/eslint/eslint-plugin-security/README.md @@ -66,7 +66,7 @@ function isInteger(s: string): boolean { ## Links - [CHANGELOG.md]( - https://github.com/microsoft/rushstack/blob/main/stack/eslint-plugin-security/CHANGELOG.md) - Find + https://github.com/microsoft/rushstack/blob/main/eslint/eslint-plugin-security/CHANGELOG.md) - Find out what's new in the latest version `@rushstack/eslint-plugin-security` is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/eslint/eslint-plugin-security/config/jest.config.json b/eslint/eslint-plugin-security/config/jest.config.json index 4bb17bde3ee..7c0f9ccc9d6 100644 --- a/eslint/eslint-plugin-security/config/jest.config.json +++ b/eslint/eslint-plugin-security/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "decoupled-local-node-rig/profiles/default/config/jest.config.json" } diff --git a/eslint/eslint-plugin-security/config/rig.json b/eslint/eslint-plugin-security/config/rig.json index 6ac88a96368..cc98dea43dd 100644 --- a/eslint/eslint-plugin-security/config/rig.json +++ b/eslint/eslint-plugin-security/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "decoupled-local-node-rig" } diff --git a/eslint/eslint-plugin-security/eslint.config.js b/eslint/eslint-plugin-security/eslint.config.js new file mode 100644 index 00000000000..f83aea7d1b7 --- /dev/null +++ b/eslint/eslint-plugin-security/eslint.config.js @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); +const tsdocMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/tsdoc'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + ...tsdocMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/eslint/eslint-plugin-security/package.json b/eslint/eslint-plugin-security/package.json index a61132195bd..091a40bb999 100644 --- a/eslint/eslint-plugin-security/package.json +++ b/eslint/eslint-plugin-security/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/eslint-plugin-security", - "version": "0.6.0", + "version": "0.14.2", "description": "An ESLint plugin providing rules that identify common security vulnerabilities for browser applications, Node.js tools, and Node.js services", "license": "MIT", "repository": { @@ -14,30 +14,51 @@ "eslint-config", "security" ], - "main": "lib/index.js", - "typings": "lib/index.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "scripts": { "build": "heft build --clean", - "_phase:build": "heft build --clean", - "_phase:test": "heft test --no-build" + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" }, "dependencies": { "@rushstack/tree-pattern": "workspace:*", - "@typescript-eslint/experimental-utils": "~5.59.2" + "@typescript-eslint/utils": "~8.56.1" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" }, "devDependencies": { - "@rushstack/heft": "0.50.6", - "@rushstack/heft-node-rig": "1.13.0", - "@types/eslint": "8.2.0", - "@types/estree": "0.0.50", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "@typescript-eslint/parser": "~5.59.2", - "@typescript-eslint/typescript-estree": "~5.59.2", - "eslint": "~8.7.0", - "typescript": "~5.0.4" - } + "@rushstack/heft": "1.2.22", + "@typescript-eslint/parser": "~8.56.1", + "@typescript-eslint/rule-tester": "~8.56.1", + "@typescript-eslint/typescript-estree": "~8.56.1", + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0", + "typescript": "~5.8.2" + }, + "sideEffects": false } diff --git a/eslint/eslint-plugin-security/src/index.ts b/eslint/eslint-plugin-security/src/index.ts index 7b07a14eafb..8e2d433001c 100644 --- a/eslint/eslint-plugin-security/src/index.ts +++ b/eslint/eslint-plugin-security/src/index.ts @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { TSESLint } from '@typescript-eslint/experimental-utils'; +import type { TSESLint } from '@typescript-eslint/utils'; + import { noUnsafeRegExp } from './no-unsafe-regexp'; interface IPlugin { diff --git a/eslint/eslint-plugin-security/src/no-unsafe-regexp.test.ts b/eslint/eslint-plugin-security/src/no-unsafe-regexp.test.ts deleted file mode 100644 index a525b1df478..00000000000 --- a/eslint/eslint-plugin-security/src/no-unsafe-regexp.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { ESLintUtils } from '@typescript-eslint/experimental-utils'; -import { noUnsafeRegExp } from './no-unsafe-regexp'; - -const { RuleTester } = ESLintUtils; -const ruleTester = new RuleTester({ - /* - * The underlying API requires an absolute path. `@typescript-eslint/utils` calls `require.resolve()` on the input - * and forces it to be of type '@typescript-eslint/parser' but does not have a dependency on `@typescript-eslint/parser` - * This means that it will always fail to resolve in a strict environment. - * Fortunately `require.resolve(absolutePath)` returns `absolutePath`, so we can resolve it first and cast. - */ - parser: require.resolve('@typescript-eslint/parser') as '@typescript-eslint/parser' -}); - -ruleTester.run('no-unsafe-regexp', noUnsafeRegExp, { - invalid: [ - { - // prettier-ignore - code: [ - 'function f(s: string) {', - ' const r1 = new RegExp(s);', - '}' - ].join('\n'), - errors: [{ messageId: 'error-unsafe-regexp' }] - } - ], - valid: [ - { - code: 'const r1 = new RegExp(".*");' - } - ] -}); diff --git a/eslint/eslint-plugin-security/src/no-unsafe-regexp.ts b/eslint/eslint-plugin-security/src/no-unsafe-regexp.ts index 53b7f8c793e..999d04c636b 100644 --- a/eslint/eslint-plugin-security/src/no-unsafe-regexp.ts +++ b/eslint/eslint-plugin-security/src/no-unsafe-regexp.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; +import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import { TreePattern } from '@rushstack/tree-pattern'; @@ -60,9 +60,7 @@ const noUnsafeRegExp: TSESLint.RuleModule = { description: 'Requires regular expressions to be constructed from string constants rather than dynamically' + ' building strings at runtime.', - // Deprecated in ESLint v8; Keep for backwards compatibility - category: 'Best Practices', - recommended: 'warn', + recommended: 'strict', url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin-security' } as TSESLint.RuleMetaDataDocs }, diff --git a/eslint/eslint-plugin-security/src/test/no-unsafe-regexp.test.ts b/eslint/eslint-plugin-security/src/test/no-unsafe-regexp.test.ts new file mode 100644 index 00000000000..d321eb03619 --- /dev/null +++ b/eslint/eslint-plugin-security/src/test/no-unsafe-regexp.test.ts @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as parser from '@typescript-eslint/parser'; +import { RuleTester } from '@typescript-eslint/rule-tester'; +import { noUnsafeRegExp } from '../no-unsafe-regexp'; + +const ruleTester = new RuleTester({ languageOptions: { parser } }); +ruleTester.run('no-unsafe-regexp', noUnsafeRegExp, { + invalid: [ + { + // prettier-ignore + code: [ + 'function f(s: string) {', + ' const r1 = new RegExp(s);', + '}' + ].join('\n'), + errors: [{ messageId: 'error-unsafe-regexp' }] + } + ], + valid: [ + { + code: 'const r1 = new RegExp(".*");' + } + ] +}); diff --git a/eslint/eslint-plugin-security/tsconfig.json b/eslint/eslint-plugin-security/tsconfig.json index fbc2f5c0a6c..e98df1ad324 100644 --- a/eslint/eslint-plugin-security/tsconfig.json +++ b/eslint/eslint-plugin-security/tsconfig.json @@ -1,7 +1,7 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "types": ["heft-jest", "node"] + "module": "Node16" } } diff --git a/eslint/eslint-plugin/.eslintrc.js.disabled b/eslint/eslint-plugin/.eslintrc.js.disabled deleted file mode 100644 index 02b5abc2856..00000000000 --- a/eslint/eslint-plugin/.eslintrc.js.disabled +++ /dev/null @@ -1,5 +0,0 @@ -NOTE: We do not invoke ESLint on this project's source files, because ESLint's module resolution (as of 6.x) is naive -and fairly brittle. It gets confused between the dependencies of this project, versus the dependencies of -@rushstack/eslint-config (which imports the previously published version of this project). Normally we solve -this problem by using Rush's "decoupledLocalDependencies" feature, but that fails because ESLint does not correctly -implement NodeJS module resolution. diff --git a/eslint/eslint-plugin/.npmignore b/eslint/eslint-plugin/.npmignore index 0164a20d7a9..f7a40e10213 100644 --- a/eslint/eslint-plugin/.npmignore +++ b/eslint/eslint-plugin/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,16 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/eslint/eslint-plugin/CHANGELOG.json b/eslint/eslint-plugin/CHANGELOG.json index c9fc72a2fe1..bb87cd719a9 100644 --- a/eslint/eslint-plugin/CHANGELOG.json +++ b/eslint/eslint-plugin/CHANGELOG.json @@ -1,6 +1,272 @@ { "name": "@rushstack/eslint-plugin", "entries": [ + { + "version": "0.23.2", + "tag": "@rushstack/eslint-plugin_v0.23.2", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "patch": [ + { + "comment": "Bump `@typescript-eslint/*` dependencies to `~8.56.1`." + } + ] + } + }, + { + "version": "0.23.1", + "tag": "@rushstack/eslint-plugin_v0.23.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.4.1`" + } + ] + } + }, + { + "version": "0.23.0", + "tag": "@rushstack/eslint-plugin_v0.23.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.4.0`" + } + ] + } + }, + { + "version": "0.22.1", + "tag": "@rushstack/eslint-plugin_v0.22.1", + "date": "Tue, 11 Nov 2025 16:13:26 GMT", + "comments": { + "patch": [ + { + "comment": "Fix calculation of project root folder when using the ESLint extension in VS Code. Report the paths being compared in 'no-external-local-imports' rule violations." + } + ] + } + }, + { + "version": "0.22.0", + "tag": "@rushstack/eslint-plugin_v0.22.0", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "minor": [ + { + "comment": "Introduce a `@rushstack/import-requires-chunk-name` rule. This rule requires that dynamic imports include a Webpack chunk name magic comment." + }, + { + "comment": "Introduce a `@rushstack/pair-react-dom-render-unmount` rule. This rule requires that every React DOM `render` call has a matching `unmountComponentAtNode` call." + } + ], + "patch": [ + { + "comment": "Include missing rule documentation." + } + ] + } + }, + { + "version": "0.21.1", + "tag": "@rushstack/eslint-plugin_v0.21.1", + "date": "Tue, 14 Oct 2025 15:13:22 GMT", + "comments": { + "patch": [ + { + "comment": "Added documentation for the @rushstack/typedef-var ESLint rule. This clarifies the rule's rationale (readability over writability) and explicitly lists all local variable exemptions, resolving confusion around its usage." + } + ] + } + }, + { + "version": "0.21.0", + "tag": "@rushstack/eslint-plugin_v0.21.0", + "date": "Mon, 13 Oct 2025 15:13:02 GMT", + "comments": { + "minor": [ + { + "comment": "Bump `eslint` to `~9.37.0` and the `@typescript-eslint/*` packages to `~8.46.0`." + } + ] + } + }, + { + "version": "0.20.0", + "tag": "@rushstack/eslint-plugin_v0.20.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ] + } + }, + { + "version": "0.19.0", + "tag": "@rushstack/eslint-plugin_v0.19.0", + "date": "Thu, 26 Jun 2025 18:57:04 GMT", + "comments": { + "minor": [ + { + "comment": "Update for compatibility with ESLint 9" + } + ] + } + }, + { + "version": "0.18.0", + "tag": "@rushstack/eslint-plugin_v0.18.0", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `@typescript-eslint/*` packages to add support for TypeScript 5.8." + } + ] + } + }, + { + "version": "0.17.0", + "tag": "@rushstack/eslint-plugin_v0.17.0", + "date": "Sat, 01 Mar 2025 07:23:16 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `@typescript-eslint/*` dependencies to `~8.24.0` to support newer versions of TypeScript." + } + ] + } + }, + { + "version": "0.16.1", + "tag": "@rushstack/eslint-plugin_v0.16.1", + "date": "Thu, 19 Sep 2024 00:11:08 GMT", + "comments": { + "patch": [ + { + "comment": "Fix ESLint broken links" + } + ] + } + }, + { + "version": "0.16.0", + "tag": "@rushstack/eslint-plugin_v0.16.0", + "date": "Wed, 14 Aug 2024 22:37:32 GMT", + "comments": { + "minor": [ + { + "comment": "Add 4 new ESLint rules: \"@rushstack/no-backslash-imports\", used to prevent backslashes in import and require statements; \"@rushstack/no-external-local-imports\", used to prevent referencing external depedencies in import and require statements; \"@rushstack/no-transitive-dependency-imports\", used to prevent referencing transitive dependencies (ie. dependencies of dependencies) in import and require statements; and \"@rushstack/normalized-imports\", used to ensure that the most direct path to a dependency is provided in import and require statements" + } + ] + } + }, + { + "version": "0.15.2", + "tag": "@rushstack/eslint-plugin_v0.15.2", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.4`" + } + ] + } + }, + { + "version": "0.15.1", + "tag": "@rushstack/eslint-plugin_v0.15.1", + "date": "Sat, 17 Feb 2024 06:24:34 GMT", + "comments": { + "patch": [ + { + "comment": "Fix broken link to API documentation" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.3`" + } + ] + } + }, + { + "version": "0.15.0", + "tag": "@rushstack/eslint-plugin_v0.15.0", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "minor": [ + { + "comment": "Allow using `as const` in `typedef-var`" + } + ] + } + }, + { + "version": "0.14.0", + "tag": "@rushstack/eslint-plugin_v0.14.0", + "date": "Tue, 16 Jan 2024 18:30:10 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for TypeScript 5.3 with @typescript-eslint 6.19.x" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.2`" + } + ] + } + }, + { + "version": "0.13.1", + "tag": "@rushstack/eslint-plugin_v0.13.1", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.1`" + } + ] + } + }, + { + "version": "0.13.0", + "tag": "@rushstack/eslint-plugin_v0.13.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/tree-pattern\" to `0.3.0`" + } + ] + } + }, { "version": "0.12.0", "tag": "@rushstack/eslint-plugin_v0.12.0", diff --git a/eslint/eslint-plugin/CHANGELOG.md b/eslint/eslint-plugin/CHANGELOG.md index 5ac7f180b0f..2d8d24527bc 100644 --- a/eslint/eslint-plugin/CHANGELOG.md +++ b/eslint/eslint-plugin/CHANGELOG.md @@ -1,6 +1,142 @@ # Change Log - @rushstack/eslint-plugin -This log was last generated on Mon, 22 May 2023 06:34:32 GMT and should not be manually modified. +This log was last generated on Wed, 25 Feb 2026 21:39:42 GMT and should not be manually modified. + +## 0.23.2 +Wed, 25 Feb 2026 21:39:42 GMT + +### Patches + +- Bump `@typescript-eslint/*` dependencies to `~8.56.1`. + +## 0.23.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.23.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.22.1 +Tue, 11 Nov 2025 16:13:26 GMT + +### Patches + +- Fix calculation of project root folder when using the ESLint extension in VS Code. Report the paths being compared in 'no-external-local-imports' rule violations. + +## 0.22.0 +Wed, 22 Oct 2025 00:57:54 GMT + +### Minor changes + +- Introduce a `@rushstack/import-requires-chunk-name` rule. This rule requires that dynamic imports include a Webpack chunk name magic comment. +- Introduce a `@rushstack/pair-react-dom-render-unmount` rule. This rule requires that every React DOM `render` call has a matching `unmountComponentAtNode` call. + +### Patches + +- Include missing rule documentation. + +## 0.21.1 +Tue, 14 Oct 2025 15:13:22 GMT + +### Patches + +- Added documentation for the @rushstack/typedef-var ESLint rule. This clarifies the rule's rationale (readability over writability) and explicitly lists all local variable exemptions, resolving confusion around its usage. + +## 0.21.0 +Mon, 13 Oct 2025 15:13:02 GMT + +### Minor changes + +- Bump `eslint` to `~9.37.0` and the `@typescript-eslint/*` packages to `~8.46.0`. + +## 0.20.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 0.19.0 +Thu, 26 Jun 2025 18:57:04 GMT + +### Minor changes + +- Update for compatibility with ESLint 9 + +## 0.18.0 +Tue, 11 Mar 2025 02:12:33 GMT + +### Minor changes + +- Bump the `@typescript-eslint/*` packages to add support for TypeScript 5.8. + +## 0.17.0 +Sat, 01 Mar 2025 07:23:16 GMT + +### Minor changes + +- Bump the `@typescript-eslint/*` dependencies to `~8.24.0` to support newer versions of TypeScript. + +## 0.16.1 +Thu, 19 Sep 2024 00:11:08 GMT + +### Patches + +- Fix ESLint broken links + +## 0.16.0 +Wed, 14 Aug 2024 22:37:32 GMT + +### Minor changes + +- Add 4 new ESLint rules: "@rushstack/no-backslash-imports", used to prevent backslashes in import and require statements; "@rushstack/no-external-local-imports", used to prevent referencing external depedencies in import and require statements; "@rushstack/no-transitive-dependency-imports", used to prevent referencing transitive dependencies (ie. dependencies of dependencies) in import and require statements; and "@rushstack/normalized-imports", used to ensure that the most direct path to a dependency is provided in import and require statements + +## 0.15.2 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.15.1 +Sat, 17 Feb 2024 06:24:34 GMT + +### Patches + +- Fix broken link to API documentation + +## 0.15.0 +Wed, 07 Feb 2024 01:11:18 GMT + +### Minor changes + +- Allow using `as const` in `typedef-var` + +## 0.14.0 +Tue, 16 Jan 2024 18:30:10 GMT + +### Minor changes + +- Add support for TypeScript 5.3 with @typescript-eslint 6.19.x + +## 0.13.1 +Tue, 26 Sep 2023 09:30:33 GMT + +_Version update only_ + +## 0.13.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 ## 0.12.0 Mon, 22 May 2023 06:34:32 GMT diff --git a/eslint/eslint-plugin/README.md b/eslint/eslint-plugin/README.md index d1ab3447e02..b76d3142ea2 100644 --- a/eslint/eslint-plugin/README.md +++ b/eslint/eslint-plugin/README.md @@ -55,6 +55,166 @@ let y: typeof import('./file'); jest.mock('./file'); // okay ``` +## `@rushstack/import-requires-chunk-name` + +Require each dynamic `import()` used for code splitting to specify exactly one Webpack chunk name via a magic comment. + +#### Rule Details + +When using dynamic `import()` to create separately loaded chunks, Webpack (and compatible bundlers such as Rspack) can assign a deterministic name if a `/* webpackChunkName: 'my-chunk' */` or `// webpackChunkName: "my-chunk"` magic comment is provided. Without an explicit name, the bundler falls back to autogenerated identifiers (often numeric or hashed), which: + +- Are less stable across refactors, hurting long‑term caching +- Make bundle analysis and performance troubleshooting harder +- Can produce confusing diffs during code reviews + +This rule enforces that: + +1. Every `import()` expression used for code splitting includes a chunk name magic comment inside its parentheses. +2. Exactly one chunk name is declared. Multiple chunk name comments (or a single comment containing multiple comma‑separated `webpackChunkName` entries) are flagged. + +The chunk name must appear in either a block or line comment inside the `import()` call. Accepted forms: + +```ts +import(/* webpackChunkName: 'feature-settings' */ './feature/settings'); +import( + /* webpackChunkName: "feature-settings" */ + './feature/settings' +); +import( + // webpackChunkName: "feature-settings" + './feature/settings' +); +``` + +No options are currently supported. + +For background on magic comments, see: https://webpack.js.org/api/module-methods/#magic-comments + +#### Examples + +The following patterns are considered problems when `@rushstack/import-requires-chunk-name` is enabled: + +```ts +// Missing chunk name +import('./feature/settings'); // error +``` + +```ts +// Multiple chunk name comments +import( + /* webpackChunkName: 'feature-settings' */ + /* webpackChunkName: 'feature-settings-alt' */ + './feature/settings' +); // error +``` + +```ts +// Multiple chunk names in a single comment (comma separated) +import( + /* webpackChunkName: 'feature-settings', webpackChunkName: 'feature-settings-alt' */ + './feature/settings' +); // error +``` + +The following patterns are NOT considered problems: + +```ts +// Single block comment with one chunk name +import(/* webpackChunkName: 'feature-settings' */ './feature/settings'); +``` + +```ts +// Multiline formatting with a block comment +import( + /* webpackChunkName: 'feature-settings' */ + './feature/settings' +); +``` + +```ts +// Line comment form +import( + // webpackChunkName: 'feature-settings' + './feature/settings' +); +``` + +#### Notes + +- If your bundler does not understand Webpack magic comments (e.g. plain Node ESM loader), disable this rule for that project. +- Choose stable, descriptive chunk names-avoid including hashes, timestamps, or environment‑specific tokens. +- Chunk names share a global namespace in the final bundle; avoid collisions to keep analysis clear. + +#### Rationale + +Explicit chunk naming improves cache hit rates, observability, and maintainability. Enforcing the practice via an ESLint rule prevents missing or duplicate declarations that could lead to unpredictable bundle naming. + +## `@rushstack/no-backslash-imports` + +Prevent import and export specifiers from using Windows-style backslashes in module paths. + +#### Rule Details + +JavaScript module specifiers always use POSIX forward slashes. Using backslashes (e.g. `import './src\utils'`) can lead to inconsistent behavior across tools, and may break resolution in some environments. This rule flags any import or export whose source contains a `\` character and provides an autofix that replaces backslashes with `/`. + +#### Examples + +The following patterns are considered problems when `@rushstack/no-backslash-imports` is enabled: + +```ts +import helper from './lib\\helper'; // error (autofix -> './lib/helper') +export * from './data\\items'; // error +``` + +The following patterns are NOT considered problems: + +```ts +import helper from './lib/helper'; +export * from '../data/items'; +``` + +#### Notes + +- Works for `import`, dynamic `import()`, and `export ... from` forms. +- Loader/query strings (e.g. `raw-loader!./file`) are preserved during the fix; only path separators are changed. + +#### Rationale + +Forward slashes are portable and avoid subtle cross-platform inconsistencies. Autofixing reduces churn and enforces a predictable style. + +## `@rushstack/no-external-local-imports` + +Prevent relative imports that reach outside the configured TypeScript `rootDir` (if specified) or outside the package boundary. + +#### Rule Details + +Local relative imports should refer only to files that are part of the compiling unit: either under the package directory or (when a `rootDir` is configured) under that root. Reaching outside can accidentally couple a package to sibling projects, untracked build inputs, or files excluded from type checking. This rule resolves each relative import/ export source and ensures the target is contained within the effective root. If not, it is flagged. + +#### Examples + +Assume `rootDir` is `src` and the package folder is `/repo/packages/example`: + +```ts +// In /repo/packages/example/src/components/Button.ts +import '../utils/file'; // error if '../utils/file' is outside src +import '../../../other-package/src/index'; // error (outside package root) +``` + +```ts +// In /repo/packages/example/src/index.ts +import './utils/file'; // passes (inside rootDir) +``` + +#### Notes + +- Only relative specifiers are checked. Package specifiers (`react`, `lodash`) are ignored. +- If no `rootDir` is defined, the package directory acts as the boundary. +- Useful for enforcing project isolation in monorepos. + +#### Rationale + +Prevents accidental dependencies on files that aren’t part of the compilation or publishing surface, improving encapsulation and build reproducibility. + ## `@rushstack/no-new-null` Prevent usage of the JavaScript `null` value, while allowing code to access existing APIs that @@ -81,10 +241,9 @@ If you are designing a new JSON file format, it's a good idea to avoid `null` en there are better representations that convey more information about an item that is unknown, omitted, or disabled. If you do need to declare types for JSON structures containing `null`, rather than suppressing the lint rule, you can use a specialized -[JsonNull](https://rushstack.io/pages/api/node-core-library.jsonnull/) +[JsonNull](https://api.rushstack.io/pages/node-core-library.jsonnull/) type as provided by [@rushstack/node-core-library](https://www.npmjs.com/package/@rushstack/node-core-library). - #### Examples The following patterns are considered problems when `@rushstack/no-new-null` is enabled: @@ -179,6 +338,47 @@ if (x === null) { // comparisons are okay } ``` +## `@rushstack/no-transitive-dependency-imports` + +Prevent importing modules from transitive dependencies that are not declared in the package’s direct dependency list. + +#### Rule Details + +Packages should only import modules from their own direct dependencies. Importing a transitive dependency (available only because another dependency pulled it in) creates hidden coupling and can break when versions change. This rule detects any import path containing multiple `node_modules` segments (for relative paths) or any direct reference to a nested `node_modules` folder for package specifiers, flagging such usages. + +Allowed exception: a single relative traversal into `node_modules` (e.g. `import '../node_modules/some-pkg/dist/index.js'`) is tolerated to support bypassing package `exports` fields intentionally. Additional traversals are disallowed. + +#### Examples + +The following patterns are considered problems when `@rushstack/no-transitive-dependency-imports` is enabled: + +```ts +// Transitive dependency via deep relative path +import '../../node_modules/some-pkg/node_modules/other-pkg/lib/internal'; // error (multiple node_modules segments) + +// Direct package import that resolves into nested node_modules (caught via parsing) +import 'other-pkg/node_modules/inner-pkg'; // error +``` + +The following patterns are NOT considered problems: + +```ts +// Direct dependency +import 'react'; + +// Single bypass to reach a file export +import '../node_modules/some-pkg/dist/index.js'; +``` + +#### Notes + +- Encourages declaring needed dependencies explicitly in `package.json`. +- Reduces breakage due to indirect version changes. + +#### Rationale + +Explicit declarations keep dependency graphs understandable and maintainable; avoiding transitive imports prevents fragile build outcomes. + ## `@rushstack/no-untyped-underscore` (Opt-in) Prevent TypeScript code from accessing legacy JavaScript members whose name has an underscore prefix. @@ -225,11 +425,201 @@ enum E { let e: E._PrivateMember = E._PrivateMember; // okay, because _PrivateMember is declared by E ``` +## `@rushstack/normalized-imports` + +Require relative import paths to be written in a normalized minimal form and autofix unnecessary directory traversals. + +#### Rule Details + +Developers sometimes write relative paths with redundant traversals (e.g. `import '../module'` when already in the parent, or `import '././utils'`). This rule computes the shortest relative path between the importing file and target, rewrites it using POSIX separators, and ensures a leading `./` is present when needed. Non-relative (package) imports are ignored. + +If the provided path differs from the normalized form, the rule reports it and autofixes to the canonical specifier while preserving loader/query suffixes. + +#### Examples + +The following patterns are considered problems when `@rushstack/normalized-imports` is enabled: + +```ts +// Redundant parent traversal +import '../currentDir/utils'; // error (autofix -> './utils') + +// Repeated ./ segments +import '././components/Button'; // error (autofix -> './components/Button') +``` + +The following patterns are NOT considered problems: + +```ts +import './utils'; +import '../shared/types'; +``` + +#### Notes + +- Only relative paths (`./` or `../`) are normalized. +- Helps produce deterministic diff noise and cleaner refactors. + +#### Rationale + +Consistent relative paths improve readability and make large-scale moves/renames less error-prone. + +## `@rushstack/pair-react-dom-render-unmount` + +Require ReactDOM (legacy) render trees created in a file to be explicitly unmounted in that same file to avoid memory leaks. + +#### Rule Details + +React 18 introduced `ReactDOM.createRoot()` and `root.unmount()`, but many codebases still use the legacy APIs: + +- `ReactDOM.render(element, container)` +- `ReactDOM.unmountComponentAtNode(container)` + +If a component tree is rendered and the container node is later discarded without an explicit unmount, detached DOM nodes and event handlers may remain in memory. This rule enforces a simple pairing discipline: the total number of render calls in a file must match the total number of unmount calls. If they differ, every render and unmount in the file is flagged so the developer can reconcile them. + +The rule detects both namespace invocations (e.g. `ReactDOM.render(...)`) and separately imported named functions (e.g. `import { render, unmountComponentAtNode } from 'react-dom'`). Default or namespace imports (e.g. `import * as ReactDOM from 'react-dom'` or `import ReactDOM from 'react-dom'`) are supported. + +No configuration options are currently supported. + +#### Examples + +The following patterns are considered problems when `@rushstack/pair-react-dom-render-unmount` is enabled: + +```ts +import * as ReactDOM from 'react-dom'; +ReactDOM.render( , document.getElementById('root')); +// Missing matching unmount +``` + +```ts +import { render } from 'react-dom'; +render( , document.getElementById('root')); +// Missing matching unmountComponentAtNode +``` + +```ts +import { unmountComponentAtNode } from 'react-dom'; +// Unmount without a corresponding render in this file +unmountComponentAtNode(document.getElementById('root')!); +``` + +```ts +import { render, unmountComponentAtNode } from 'react-dom'; +render( , a); +render( , b); +// Only one unmount +unmountComponentAtNode(a); +// "b"'s render is not paired +``` + +The following patterns are NOT considered problems: + +```ts +import * as ReactDOM from 'react-dom'; +const rootEl = document.getElementById('root'); +ReactDOM.render( , rootEl); +ReactDOM.unmountComponentAtNode(rootEl!); +``` + +```ts +import { render, unmountComponentAtNode } from 'react-dom'; +render( , a); +render( , b); +unmountComponentAtNode(a); +unmountComponentAtNode(b); +// All renders paired +``` + +```ts +// No legacy ReactDOM render/unmount usage in this file +// (e.g. uses React 18 createRoot API or just defines components) - rule passes +``` + +#### Notes + +- The rule does not attempt dataflow analysis to verify the same container node is passed; it only enforces count parity. +- Modern React apps using `createRoot()` should migrate to pairing `root.unmount()`. This legacy rule helps older code until migration is complete. +- Multiple files can coordinate unmounting (e.g. via a shared cleanup utility); in that case this rule will flag the imbalance-consider colocating the unmount or disabling the rule for that file. + +#### Rationale + +Unpaired legacy renders are a common cause of memory leaks and test pollution. A lightweight count-based heuristic catches most oversights without requiring complex static analysis. + +## `@rushstack/typedef-var` + +Require explicit type annotations for top-level variable declarations, while exempting local variables within function or method scopes. + +#### Rule Details + +This rule is implemented to supplement the deprecated `@typescript-eslint/typedef` rule. The `@typescript-eslint/typedef` rule was deprecated based on the judgment that "unnecessary type annotations, where type inference is sufficient, can be cumbersome to maintain and generally reduce code readability." + +However, we prioritize code reading and maintenance over code authorship. That is, even when the compiler can infer a type, this rule enforces explicit type annotations to ensure that a code reviewer (e.g., when viewing a GitHub Diff) does not have to rely entirely on inference and can immediately ascertain a variable's type. This approach makes writing code harder but significantly improves the more crucial activity of reading and reviewing code. + +Therefore, the `@rushstack/typedef-var` rule enforces type annotations for all variable declarations outside of local function or class method scopes. This includes the module's top-level scope and any block scopes that do not belong to a function or method. + +To balance this strictness with code authoring convenience, the rule deliberately relaxes the type annotation requirement for the following local variable declarations: + +- Variable declarations within a function body. +- Variable declarations within a class method. +- Variables declared via object or array destructuring assignments. + +#### Examples + +The following patterns are considered problems when `@rushstack/typedef-var` is enabled: + +```ts +// Top-level declarations lack explicit type annotations +const x = 123; // error + +let x = 123; // error + +var x = 123; // error +``` + +```ts +// Declaration within a non-function block scope +{ + const x = 123; // error +} +``` + +The following patterns are NOT considered problems: + +```ts +// Local variables inside function expressions are exempt +function f() { const x = 123; } // passes + +const f = () => { const x = 123; }; // passes + +const f = function() { const x = 123; } // passes +``` + +```ts +// Local variables inside class methods are exempt +class C { + public m(): void { + const x = 123; // passes + } +} + +class C { + public m = (): void => { + const x = 123; // passes + } +} +``` + +```ts +// Array and Object Destructuring assignments are exempt +let { a, b } = { // passes + a: 123, + b: 234 +} +``` ## Links - [CHANGELOG.md]( - https://github.com/microsoft/rushstack/blob/main/stack/eslint-plugin/CHANGELOG.md) - Find + https://github.com/microsoft/rushstack/blob/main/eslint/eslint-plugin/CHANGELOG.md) - Find out what's new in the latest version `@rushstack/eslint-plugin` is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/eslint/eslint-plugin/config/jest.config.json b/eslint/eslint-plugin/config/jest.config.json index 4bb17bde3ee..7c0f9ccc9d6 100644 --- a/eslint/eslint-plugin/config/jest.config.json +++ b/eslint/eslint-plugin/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "decoupled-local-node-rig/profiles/default/config/jest.config.json" } diff --git a/eslint/eslint-plugin/config/rig.json b/eslint/eslint-plugin/config/rig.json index 6ac88a96368..cc98dea43dd 100644 --- a/eslint/eslint-plugin/config/rig.json +++ b/eslint/eslint-plugin/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "decoupled-local-node-rig" } diff --git a/eslint/eslint-plugin/eslint.config.js b/eslint/eslint-plugin/eslint.config.js new file mode 100644 index 00000000000..f83aea7d1b7 --- /dev/null +++ b/eslint/eslint-plugin/eslint.config.js @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); +const tsdocMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/tsdoc'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + ...tsdocMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/eslint/eslint-plugin/package.json b/eslint/eslint-plugin/package.json index a00f1542257..54265e09ed4 100644 --- a/eslint/eslint-plugin/package.json +++ b/eslint/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/eslint-plugin", - "version": "0.12.0", + "version": "0.23.2", "description": "An ESLint plugin providing supplementary rules for use with the @rushstack/eslint-config package", "license": "MIT", "repository": { @@ -18,30 +18,51 @@ "scale", "typescript" ], - "main": "lib/index.js", - "typings": "lib/index.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "scripts": { "build": "heft build --clean", - "_phase:build": "heft build --clean", - "_phase:test": "heft test --no-build" + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" }, "dependencies": { "@rushstack/tree-pattern": "workspace:*", - "@typescript-eslint/experimental-utils": "~5.59.2" + "@typescript-eslint/utils": "~8.56.1" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" }, "devDependencies": { - "@rushstack/heft": "0.50.6", - "@rushstack/heft-node-rig": "1.13.0", - "@types/eslint": "8.2.0", - "@types/estree": "0.0.50", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "@typescript-eslint/parser": "~5.59.2", - "@typescript-eslint/typescript-estree": "~5.59.2", - "eslint": "~8.7.0", - "typescript": "~5.0.4" - } + "@rushstack/heft": "1.2.22", + "@typescript-eslint/parser": "~8.56.1", + "@typescript-eslint/rule-tester": "~8.56.1", + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0", + "typescript": "~5.8.2", + "@typescript-eslint/types": "~8.56.1" + }, + "sideEffects": false } diff --git a/eslint/eslint-plugin/src/LintUtilities.ts b/eslint/eslint-plugin/src/LintUtilities.ts new file mode 100644 index 00000000000..fb610ba7a99 --- /dev/null +++ b/eslint/eslint-plugin/src/LintUtilities.ts @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { ESLintUtils, TSESTree, type TSESLint } from '@typescript-eslint/utils'; +import type { CompilerOptions, Program } from 'typescript'; + +export interface IParsedImportSpecifier { + loader?: string; + importTarget: string; + loaderOptions?: string; +} + +// Regex to parse out the import target from the specifier. Expected formats are: +// - '' +// - '!' +// - '?' +// - '!?' +const LOADER_CAPTURE_GROUP: 'loader' = 'loader'; +const IMPORT_TARGET_CAPTURE_GROUP: 'importTarget' = 'importTarget'; +const LOADER_OPTIONS_CAPTURE_GROUP: 'loaderOptions' = 'loaderOptions'; +const SPECIFIER_REGEX: RegExp = new RegExp( + `^((?<${LOADER_CAPTURE_GROUP}>(!|-!|!!).+)!)?` + + `(?<${IMPORT_TARGET_CAPTURE_GROUP}>[^!?]+)` + + `(\\?(?<${LOADER_OPTIONS_CAPTURE_GROUP}>.*))?$` +); + +export function getFilePathFromContext(context: TSESLint.RuleContext): string { + return context.physicalFilename || context.filename; +} + +export function getRootDirectoryFromContext( + context: TSESLint.RuleContext +): string | undefined { + /* + * Precedence of root directory resolution: + * 1. parserOptions.tsconfigRootDir if available (since set by repo maintainer) + * 2. tsconfig.json directory if available (but might be in a subfolder) + * 3. TS Program current directory if available + * 4. ESLint working directory (probably wrong, but better than nothing?) + */ + const tsConfigRootDir: string | undefined = context.parserOptions?.tsconfigRootDir; + if (tsConfigRootDir) { + return tsConfigRootDir; + } + + try { + const program: Program | null | undefined = ( + context.sourceCode?.parserServices ?? ESLintUtils.getParserServices(context) + ).program; + const compilerOptions: CompilerOptions | undefined = program?.getCompilerOptions(); + + const tsConfigPath: string | undefined = compilerOptions?.configFilePath as string | undefined; + if (tsConfigPath) { + const tsConfigDir: string = path.dirname(tsConfigPath); + return tsConfigDir; + } + + // Next, try to get the current directory from the TS program + const rootDirectory: string | undefined = program?.getCurrentDirectory(); + if (rootDirectory) { + return rootDirectory; + } + } catch { + // Ignore the error if we cannot retrieve a TS program + } + + // Last resort: use ESLint's current working directory + return context.getCwd?.(); +} + +export function parseImportSpecifierFromExpression( + importExpression: TSESTree.Expression +): IParsedImportSpecifier | undefined { + if ( + !importExpression || + importExpression.type !== TSESTree.AST_NODE_TYPES.Literal || + typeof importExpression.value !== 'string' + ) { + // Can't determine the path of the import target, return + return undefined; + } + + // Extract the target of the import, stripping out webpack loaders and query strings. The regex will + // also ensure that the import target is a relative path. + const specifierMatch: RegExpMatchArray | null = importExpression.value.match(SPECIFIER_REGEX); + if (!specifierMatch?.groups) { + // Can't determine the path of the import target, return + return undefined; + } + + const loader: string | undefined = specifierMatch.groups[LOADER_CAPTURE_GROUP]; + const importTarget: string = specifierMatch.groups[IMPORT_TARGET_CAPTURE_GROUP]; + const loaderOptions: string | undefined = specifierMatch.groups[LOADER_OPTIONS_CAPTURE_GROUP]; + return { loader, importTarget, loaderOptions }; +} + +export function serializeImportSpecifier(parsedImportPath: IParsedImportSpecifier): string { + const { loader, importTarget, loaderOptions } = parsedImportPath; + return `${loader ? `${loader}!` : ''}${importTarget}${loaderOptions ? `?${loaderOptions}` : ''}`; +} + +export function getImportPathFromExpression( + importExpression: TSESTree.Expression, + relativeImportsOnly: boolean = true +): string | undefined { + const parsedImportSpecifier: IParsedImportSpecifier | undefined = + parseImportSpecifierFromExpression(importExpression); + if ( + !parsedImportSpecifier || + (relativeImportsOnly && !parsedImportSpecifier.importTarget.startsWith('.')) + ) { + // The import target isn't a path, return + return undefined; + } + return parsedImportSpecifier?.importTarget; +} + +export function getImportAbsolutePathFromExpression( + context: TSESLint.RuleContext, + importExpression: TSESTree.Expression, + relativeImportsOnly: boolean = true +): string | undefined { + const importPath: string | undefined = getImportPathFromExpression(importExpression, relativeImportsOnly); + if (importPath === undefined) { + // Can't determine the absolute path of the import target, return + return undefined; + } + + const filePath: string = getFilePathFromContext(context); + const fileDirectory: string = path.dirname(filePath); + + // Combine the import path with the absolute path of the file parent directory to get the + // absolute path of the import target + return path.resolve(fileDirectory, importPath); +} diff --git a/eslint/eslint-plugin/src/hoist-jest-mock.ts b/eslint/eslint-plugin/src/hoist-jest-mock.ts index 3dd0163f12b..4178cab0d13 100644 --- a/eslint/eslint-plugin/src/hoist-jest-mock.ts +++ b/eslint/eslint-plugin/src/hoist-jest-mock.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; +import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as hoistJestMockPatterns from './hoistJestMockPatterns'; @@ -11,7 +11,7 @@ type Options = []; // Jest APIs that need to be hoisted // Based on HOIST_METHODS from ts-jest -const HOIST_METHODS = ['mock', 'unmock', 'enableAutomock', 'disableAutomock', 'deepUnmock']; +const HOIST_METHODS: string[] = ['mock', 'unmock', 'enableAutomock', 'disableAutomock', 'deepUnmock']; const hoistJestMock: TSESLint.RuleModule = { defaultOptions: [], @@ -36,9 +36,7 @@ const hoistJestMock: TSESLint.RuleModule = { ' "hoist" these calls, however this can produce counterintuitive results. Instead, the hoist-jest-mocks' + ' lint rule requires developers to manually hoist these calls. For technical background, please read the' + ' Jest documentation here: https://jestjs.io/docs/en/es6-class-mocks', - // Deprecated in ESLint v8; Keep for backwards compatibility - category: 'Possible Errors', - recommended: 'error', + recommended: 'recommended', url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin' } as TSESLint.RuleMetaDataDocs }, @@ -143,7 +141,7 @@ const hoistJestMock: TSESLint.RuleModule = { if (firstImportNode === undefined) { // EXAMPLE: export * from "Y"; // IGNORE: export type { Y } from "Y"; - if ((node as any as TSESTree.ExportNamedDeclaration).exportKind !== 'type') { + if ((node as unknown as TSESTree.ExportNamedDeclaration).exportKind !== 'type') { firstImportNode = node; } } diff --git a/eslint/eslint-plugin/src/import-requires-chunk-name.ts b/eslint/eslint-plugin/src/import-requires-chunk-name.ts new file mode 100644 index 00000000000..8b9bc6b9ef6 --- /dev/null +++ b/eslint/eslint-plugin/src/import-requires-chunk-name.ts @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { TSESTree, TSESLint } from '@typescript-eslint/utils'; + +export const MESSAGE_ID_CHUNK_NAME: 'error-import-requires-chunk-name' = 'error-import-requires-chunk-name'; +export const MESSAGE_ID_SINGLE_CHUNK_NAME: 'error-import-requires-single-chunk-name' = + 'error-import-requires-single-chunk-name'; +type RuleModule = TSESLint.RuleModule; +type RuleContext = TSESLint.RuleContext< + typeof MESSAGE_ID_CHUNK_NAME | typeof MESSAGE_ID_SINGLE_CHUNK_NAME, + [] +>; + +const importRequiresChunkNameRule: RuleModule = { + defaultOptions: [], + meta: { + type: 'problem', + messages: { + [MESSAGE_ID_CHUNK_NAME]: + 'Usage of "import(...)" for code splitting requires a /* webpackChunkName: \'...\' */ comment', + [MESSAGE_ID_SINGLE_CHUNK_NAME]: + 'Usage of "import(...)" for code splitting cannot specify multiple /* webpackChunkName: \'...\' */ comments' + }, + schema: [], + docs: { + description: 'Requires that calls to "import(...)" for code splitting include a Webpack chunk name', + url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin' + } + }, + create: (context: RuleContext) => { + const sourceCode: Readonly = context.sourceCode; + const webpackChunkNameRegex: RegExp = /^webpackChunkName\s*:\s*('[^']+'|"[^"]+")$/; + + return { + ImportExpression: (node: TSESTree.ImportExpression) => { + const nodeComments: TSESTree.Comment[] = sourceCode.getCommentsInside(node); + const webpackChunkNameEntries: string[] = []; + for (const comment of nodeComments) { + const webpackChunkNameMatches: string[] = comment.value + .split(',') + .map((c) => c.trim()) + .filter((c) => !!c.match(webpackChunkNameRegex)); + webpackChunkNameEntries.push(...webpackChunkNameMatches); + } + + if (webpackChunkNameEntries.length === 0) { + context.report({ node, messageId: MESSAGE_ID_CHUNK_NAME }); + } else if (webpackChunkNameEntries.length !== 1) { + context.report({ node, messageId: MESSAGE_ID_SINGLE_CHUNK_NAME }); + } + } + }; + } +}; + +export { importRequiresChunkNameRule }; diff --git a/eslint/eslint-plugin/src/index.ts b/eslint/eslint-plugin/src/index.ts index 4ca439d4350..61f0c64f23e 100644 --- a/eslint/eslint-plugin/src/index.ts +++ b/eslint/eslint-plugin/src/index.ts @@ -1,13 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { TSESLint } from '@typescript-eslint/experimental-utils'; +import type { TSESLint } from '@typescript-eslint/utils'; import { hoistJestMock } from './hoist-jest-mock'; +import { noBackslashImportsRule } from './no-backslash-imports'; +import { noExternalLocalImportsRule } from './no-external-local-imports'; import { noNewNullRule } from './no-new-null'; import { noNullRule } from './no-null'; +import { noTransitiveDependencyImportsRule } from './no-transitive-dependency-imports'; import { noUntypedUnderscoreRule } from './no-untyped-underscore'; +import { normalizedImportsRule } from './normalized-imports'; import { typedefVar } from './typedef-var'; +import { importRequiresChunkNameRule } from './import-requires-chunk-name'; +import { pairReactDomRenderUnmountRule } from './pair-react-dom-render-unmount'; interface IPlugin { rules: { [ruleName: string]: TSESLint.RuleModule }; @@ -18,17 +24,35 @@ const plugin: IPlugin = { // Full name: "@rushstack/hoist-jest-mock" 'hoist-jest-mock': hoistJestMock, + // Full name: "@rushstack/no-backslash-imports" + 'no-backslash-imports': noBackslashImportsRule, + + // Full name: "@rushstack/no-external-local-imports" + 'no-external-local-imports': noExternalLocalImportsRule, + // Full name: "@rushstack/no-new-null" 'no-new-null': noNewNullRule, // Full name: "@rushstack/no-null" 'no-null': noNullRule, + // Full name: "@rushstack/no-transitive-dependency-imports" + 'no-transitive-dependency-imports': noTransitiveDependencyImportsRule, + // Full name: "@rushstack/no-untyped-underscore" 'no-untyped-underscore': noUntypedUnderscoreRule, + // Full name: "@rushstack/normalized-imports" + 'normalized-imports': normalizedImportsRule, + // Full name: "@rushstack/typedef-var" - 'typedef-var': typedefVar + 'typedef-var': typedefVar, + + // Full name: "@rushstack/import-requires-chunk-name" + 'import-requires-chunk-name': importRequiresChunkNameRule, + + // Full name: "@rushstack/pair-react-dom-render-unmount" + 'pair-react-dom-render-unmount': pairReactDomRenderUnmountRule } }; diff --git a/eslint/eslint-plugin/src/no-backslash-imports.ts b/eslint/eslint-plugin/src/no-backslash-imports.ts new file mode 100644 index 00000000000..82fbb1bb791 --- /dev/null +++ b/eslint/eslint-plugin/src/no-backslash-imports.ts @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { TSESTree, TSESLint } from '@typescript-eslint/utils'; + +import { + parseImportSpecifierFromExpression, + serializeImportSpecifier, + type IParsedImportSpecifier +} from './LintUtilities'; + +export const MESSAGE_ID: 'no-backslash-imports' = 'no-backslash-imports'; +type RuleModule = TSESLint.RuleModule; +type RuleContext = TSESLint.RuleContext; + +export const noBackslashImportsRule: RuleModule = { + defaultOptions: [], + meta: { + type: 'problem', + messages: { + [MESSAGE_ID]: 'The specified import target path contains backslashes.' + }, + schema: [], + docs: { + description: 'Prevents imports using paths that use backslashes', + url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin' + }, + fixable: 'code' + }, + create: (context: RuleContext) => { + const checkImportExpression: (importExpression: TSESTree.Expression | null) => void = ( + importExpression: TSESTree.Expression | null + ) => { + if (!importExpression) { + // Can't validate, return + return; + } + + // Determine the target file path and find the most direct relative path from the source file + const importSpecifier: IParsedImportSpecifier | undefined = + parseImportSpecifierFromExpression(importExpression); + if (importSpecifier === undefined) { + // Can't validate, return + return; + } + + // Check if the import path contains backslashes. If it does, suggest a fix to replace them with forward + // slashes. + const { importTarget } = importSpecifier; + if (importTarget.includes('\\')) { + context.report({ + node: importExpression, + messageId: MESSAGE_ID, + fix: (fixer: TSESLint.RuleFixer) => { + const normalizedSpecifier: IParsedImportSpecifier = { + ...importSpecifier, + importTarget: importTarget.replace(/\\/g, '/') + }; + return fixer.replaceText(importExpression, `'${serializeImportSpecifier(normalizedSpecifier)}'`); + } + }); + } + }; + + return { + ImportDeclaration: (node: TSESTree.ImportDeclaration) => checkImportExpression(node.source), + ImportExpression: (node: TSESTree.ImportExpression) => checkImportExpression(node.source), + ExportAllDeclaration: (node: TSESTree.ExportAllDeclaration) => checkImportExpression(node.source), + ExportNamedDeclaration: (node: TSESTree.ExportNamedDeclaration) => checkImportExpression(node.source) + }; + } +}; diff --git a/eslint/eslint-plugin/src/no-external-local-imports.ts b/eslint/eslint-plugin/src/no-external-local-imports.ts new file mode 100644 index 00000000000..e4ae7bae204 --- /dev/null +++ b/eslint/eslint-plugin/src/no-external-local-imports.ts @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import type { TSESTree, TSESLint } from '@typescript-eslint/utils'; + +import { getRootDirectoryFromContext, getImportAbsolutePathFromExpression } from './LintUtilities'; + +export const MESSAGE_ID: 'error-external-local-imports' = 'error-external-local-imports'; +type RuleModule = TSESLint.RuleModule; +type RuleContext = TSESLint.RuleContext; + +const _relativePathRegex: RegExp = /^[.\/\\]+$/; + +export const noExternalLocalImportsRule: RuleModule = { + defaultOptions: [], + meta: { + type: 'problem', + messages: { + [MESSAGE_ID]: + 'The specified import target "{{ importAbsolutePath }}" is not under the root directory, "{{ rootDirectory }}". Ensure that ' + + 'all local import targets are either under the "parserOptions.tsconfigRootDir" specified in your eslint.config.js (if one ' + + 'exists) or else under the folder that contains your tsconfig.json.' + }, + schema: [], + docs: { + description: + 'Prevents referencing relative imports that are either not under the "parserOptions.tsconfigRootDir" specified in ' + + 'your eslint.config.js (if one exists) or else not under the folder that contains your tsconfig.json.', + url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin' + } + }, + create: (context: RuleContext) => { + const rootDirectory: string | undefined = getRootDirectoryFromContext(context); + const checkImportExpression: (importExpression: TSESTree.Expression | null) => void = ( + importExpression: TSESTree.Expression | null + ) => { + if (!importExpression || !rootDirectory) { + // Can't validate, return + return; + } + + // Get the relative path between the target and the root. If the target is under the root, then the resulting + // relative path should be a series of "../" segments. + const importAbsolutePath: string | undefined = getImportAbsolutePathFromExpression( + context, + importExpression + ); + if (!importAbsolutePath) { + // Can't validate, return + return; + } + + const relativePathToRoot: string = path.relative(importAbsolutePath, rootDirectory); + if (!_relativePathRegex.test(relativePathToRoot)) { + context.report({ + node: importExpression, + messageId: MESSAGE_ID, + data: { importAbsolutePath, rootDirectory } + }); + } + }; + + return { + ImportDeclaration: (node: TSESTree.ImportDeclaration) => checkImportExpression(node.source), + ImportExpression: (node: TSESTree.ImportExpression) => checkImportExpression(node.source), + ExportAllDeclaration: (node: TSESTree.ExportAllDeclaration) => checkImportExpression(node.source), + ExportNamedDeclaration: (node: TSESTree.ExportNamedDeclaration) => checkImportExpression(node.source) + }; + } +}; diff --git a/eslint/eslint-plugin/src/no-new-null.ts b/eslint/eslint-plugin/src/no-new-null.ts index 5f667da5264..36d3e82db74 100644 --- a/eslint/eslint-plugin/src/no-new-null.ts +++ b/eslint/eslint-plugin/src/no-new-null.ts @@ -1,15 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; +import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; type MessageIds = 'error-new-usage-of-null'; type Options = []; -type Accessible = { +interface IAccessible { accessibility?: TSESTree.Accessibility; -}; +} const noNewNullRule: TSESLint.RuleModule = { defaultOptions: [], @@ -30,9 +30,7 @@ const noNewNullRule: TSESLint.RuleModule = { 'Prevent usage of JavaScript\'s "null" keyword in new type declarations. To avoid hampering usage' + ' of preexisting APIs that require "null", the rule ignores declarations that are local variables,' + ' private members, or types that are not exported.', - // Deprecated in ESLint v8; Keep for backwards compatibility - category: 'Stylistic Issues', - recommended: 'error', + recommended: 'recommended', url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin' } as TSESLint.RuleMetaDataDocs }, @@ -41,15 +39,15 @@ const noNewNullRule: TSESLint.RuleModule = { /** * Returns true if the accessibility is not explicitly set to private or protected, e.g. class properties, methods. */ - function isPubliclyAccessible(node?: Accessible): boolean { - const accessibility = node?.accessibility; + function isPubliclyAccessible(node?: IAccessible): boolean { + const accessibility: TSESTree.Accessibility | undefined = node?.accessibility; return !(accessibility === 'private' || accessibility === 'protected'); } /** * Let's us check the accessibility field of certain types of nodes */ - function isAccessible(node?: unknown): node is Accessible { + function isAccessible(node?: unknown): node is IAccessible { if (!node) { return false; } @@ -106,7 +104,7 @@ const noNewNullRule: TSESLint.RuleModule = { } return { - TSNullKeyword(node): void { + TSNullKeyword(node: TSESTree.TSNullKeyword): void { if (isNewNull(node.parent)) { context.report({ node, messageId: 'error-new-usage-of-null' }); } diff --git a/eslint/eslint-plugin/src/no-null.ts b/eslint/eslint-plugin/src/no-null.ts index 94474113b9d..3525d0128a4 100644 --- a/eslint/eslint-plugin/src/no-null.ts +++ b/eslint/eslint-plugin/src/no-null.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { TSESTree, TSESLint } from '@typescript-eslint/experimental-utils'; +import type { TSESTree, TSESLint } from '@typescript-eslint/utils'; type MessageIds = 'error-usage-of-null'; type Options = []; @@ -17,9 +17,7 @@ const noNullRule: TSESLint.RuleModule = { schema: [], docs: { description: 'Prevent usage of JavaScript\'s "null" keyword', - // Deprecated in ESLint v8; Keep for backwards compatibility - category: 'Stylistic Issues', - recommended: 'error', + recommended: 'recommended', url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin' } as TSESLint.RuleMetaDataDocs }, diff --git a/eslint/eslint-plugin/src/no-transitive-dependency-imports.ts b/eslint/eslint-plugin/src/no-transitive-dependency-imports.ts new file mode 100644 index 00000000000..e7677826f14 --- /dev/null +++ b/eslint/eslint-plugin/src/no-transitive-dependency-imports.ts @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { TSESTree, TSESLint } from '@typescript-eslint/utils'; + +import { parseImportSpecifierFromExpression, type IParsedImportSpecifier } from './LintUtilities'; + +export const MESSAGE_ID: 'error-transitive-dependency-imports' = 'error-transitive-dependency-imports'; +type RuleModule = TSESLint.RuleModule; +type RuleContext = TSESLint.RuleContext; + +const NODE_MODULES_PATH_SEGMENT: '/node_modules/' = '/node_modules/'; + +export const noTransitiveDependencyImportsRule: RuleModule = { + defaultOptions: [], + meta: { + type: 'problem', + messages: { + [MESSAGE_ID]: 'The specified import targets a transitive dependency.' + }, + schema: [], + docs: { + description: + 'Prevents referencing imports that are transitive dependencies, ie. imports that are not ' + + 'direct dependencies of the package.', + url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin' + } + }, + create: (context: RuleContext) => { + const checkImportExpression: (importExpression: TSESTree.Expression | null) => void = ( + importExpression: TSESTree.Expression | null + ) => { + if (!importExpression) { + // Can't validate, return + return; + } + + const importSpecifier: IParsedImportSpecifier | undefined = + parseImportSpecifierFromExpression(importExpression); + if (importSpecifier === undefined) { + // Can't validate, return + return; + } + + // Check to see if node_modules is mentioned in the normalized import path more than once if + // the path is relative, or if it is mentioned at all if the path is to a package. + const { importTarget } = importSpecifier; + const isRelative: boolean = importTarget.startsWith('.'); + let nodeModulesIndex: number = importTarget.indexOf(NODE_MODULES_PATH_SEGMENT); + if (nodeModulesIndex >= 0 && isRelative) { + // We allow relative paths to node_modules one layer deep to deal with bypassing exports + nodeModulesIndex = importTarget.indexOf( + NODE_MODULES_PATH_SEGMENT, + nodeModulesIndex + NODE_MODULES_PATH_SEGMENT.length - 1 + ); + } + if (nodeModulesIndex >= 0) { + context.report({ node: importExpression, messageId: MESSAGE_ID }); + } + }; + + return { + ImportDeclaration: (node: TSESTree.ImportDeclaration) => checkImportExpression(node.source), + ImportExpression: (node: TSESTree.ImportExpression) => checkImportExpression(node.source), + ExportAllDeclaration: (node: TSESTree.ExportAllDeclaration) => checkImportExpression(node.source), + ExportNamedDeclaration: (node: TSESTree.ExportNamedDeclaration) => checkImportExpression(node.source) + }; + } +}; diff --git a/eslint/eslint-plugin/src/no-untyped-underscore.ts b/eslint/eslint-plugin/src/no-untyped-underscore.ts index 4225c72fb56..01f948f2873 100644 --- a/eslint/eslint-plugin/src/no-untyped-underscore.ts +++ b/eslint/eslint-plugin/src/no-untyped-underscore.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { TSESTree, TSESLint, ParserServices } from '@typescript-eslint/experimental-utils'; -import * as ts from 'typescript'; +import type { TSESTree, TSESLint, ParserServices } from '@typescript-eslint/utils'; +import type * as ts from 'typescript'; type MessageIds = 'error-untyped-underscore'; type Options = []; @@ -21,14 +21,13 @@ const noUntypedUnderscoreRule: TSESLint.RuleModule = { description: 'Prevent TypeScript code from accessing legacy JavaScript members' + ' whose names have an underscore prefix', - // Deprecated in ESLint v8; Keep for backwards compatibility - category: 'Stylistic Issues', - recommended: false, + recommended: 'strict', url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin' } as TSESLint.RuleMetaDataDocs }, create: (context: TSESLint.RuleContext) => { - const parserServices: ParserServices | undefined = context.parserServices; + const parserServices: Partial | undefined = + context.sourceCode?.parserServices ?? context.parserServices; if (!parserServices || !parserServices.program || !parserServices.esTreeNodeToTSNodeMap) { throw new Error( 'This rule requires your ESLint configuration to define the "parserOptions.project"' + @@ -49,7 +48,7 @@ const noUntypedUnderscoreRule: TSESLint.RuleModule = { return; // no match } if (memberObject.type === 'Identifier') { - if (memberObject.name === 'this' || memberObject.name == 'that') { + if (memberObject.name === 'this' || memberObject.name === 'that') { return; // no match } } diff --git a/eslint/eslint-plugin/src/normalized-imports.ts b/eslint/eslint-plugin/src/normalized-imports.ts new file mode 100644 index 00000000000..1534642a20c --- /dev/null +++ b/eslint/eslint-plugin/src/normalized-imports.ts @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import type { TSESTree, TSESLint } from '@typescript-eslint/utils'; + +import { + getFilePathFromContext, + parseImportSpecifierFromExpression, + serializeImportSpecifier, + type IParsedImportSpecifier +} from './LintUtilities'; + +export const MESSAGE_ID: 'error-normalized-imports' = 'error-normalized-imports'; +type RuleModule = TSESLint.RuleModule; +type RuleContext = TSESLint.RuleContext; + +export const normalizedImportsRule: RuleModule = { + defaultOptions: [], + meta: { + type: 'suggestion', + messages: { + [MESSAGE_ID]: 'The specified import target path was not provided in a normalized form.' + }, + schema: [], + docs: { + description: + 'Prevents and normalizes references to relative imports using paths that make unnecessary ' + + 'traversals (ex. "../blah/module" in directory "blah" -> "./module")', + url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin' + }, + fixable: 'code' + }, + create: (context: RuleContext) => { + const checkImportExpression: (importExpression: TSESTree.Expression | null) => void = ( + importExpression: TSESTree.Expression | null + ) => { + if (!importExpression) { + // Can't validate, return + return; + } + + // Determine the target file path and find the most direct relative path from the source file + const importSpecifier: IParsedImportSpecifier | undefined = + parseImportSpecifierFromExpression(importExpression); + if (!importSpecifier || !importSpecifier.importTarget.startsWith('.')) { + // Can't validate, return + return; + } + const { importTarget } = importSpecifier; + const parentDirectory: string = path.dirname(getFilePathFromContext(context)); + const absoluteImportPath: string = path.resolve(parentDirectory, importTarget); + const relativeImportPath: string = path.relative(parentDirectory, absoluteImportPath); + + // Reconstruct the import target using posix separators and manually re-add the leading './' if needed + let normalizedImportPath: string = + path.sep !== '/' ? relativeImportPath.replace(/\\/g, '/') : relativeImportPath; + if (!normalizedImportPath.startsWith('.')) { + normalizedImportPath = `.${normalizedImportPath ? '/' : ''}${normalizedImportPath}`; + } + + // If they don't match, suggest the normalized path as a fix + if (importTarget !== normalizedImportPath) { + context.report({ + node: importExpression, + messageId: MESSAGE_ID, + fix: (fixer: TSESLint.RuleFixer) => { + // Re-include stripped loader and query strings, if provided + const normalizedSpecifier: string = serializeImportSpecifier({ + ...importSpecifier, + importTarget: normalizedImportPath + }); + return fixer.replaceText(importExpression, `'${normalizedSpecifier}'`); + } + }); + } + }; + + return { + ImportDeclaration: (node: TSESTree.ImportDeclaration) => checkImportExpression(node.source), + ImportExpression: (node: TSESTree.ImportExpression) => checkImportExpression(node.source), + ExportAllDeclaration: (node: TSESTree.ExportAllDeclaration) => checkImportExpression(node.source), + ExportNamedDeclaration: (node: TSESTree.ExportNamedDeclaration) => checkImportExpression(node.source) + }; + } +}; diff --git a/eslint/eslint-plugin/src/pair-react-dom-render-unmount.ts b/eslint/eslint-plugin/src/pair-react-dom-render-unmount.ts new file mode 100644 index 00000000000..7c4647b5492 --- /dev/null +++ b/eslint/eslint-plugin/src/pair-react-dom-render-unmount.ts @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { TSESTree, type TSESLint } from '@typescript-eslint/utils'; + +export const MESSAGE_ID: 'error-pair-react-dom-render-unmount' = 'error-pair-react-dom-render-unmount'; +type RuleModule = TSESLint.RuleModule; +type RuleContext = TSESLint.RuleContext; + +const pairReactDomRenderUnmountRule: RuleModule = { + defaultOptions: [], + meta: { + type: 'problem', + messages: { + [MESSAGE_ID]: 'Pair the render and unmount calls to avoid memory leaks.' + }, + schema: [], + docs: { + description: + 'Pair ReactDOM "render" and "unmount" calls in one file.' + + ' If a ReactDOM render tree is not unmounted when disposed, it will cause a memory leak.', + url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin' + } + }, + create: (context: RuleContext) => { + const renderCallExpressions: TSESTree.CallExpression[] = []; + const unmountCallExpressions: TSESTree.CallExpression[] = []; + + let reactDomImportNamespaceName: string | undefined; + let reactDomRenderFunctionName: string | undefined; + let reactDomUnmountFunctionName: string | undefined; + + const isFunctionCallExpression: ( + node: TSESTree.CallExpression, + methodName: string | undefined + ) => boolean = (node: TSESTree.CallExpression, methodName: string | undefined) => { + return node.callee.type === TSESTree.AST_NODE_TYPES.Identifier && node.callee.name === methodName; + }; + + const isNamespaceCallExpression: ( + node: TSESTree.CallExpression, + namespaceName: string | undefined, + methodName: string | undefined + ) => boolean = ( + node: TSESTree.CallExpression, + namespaceName: string | undefined, + methodName: string | undefined + ) => { + if (node.callee.type === TSESTree.AST_NODE_TYPES.MemberExpression) { + const { object, property } = node.callee; + if (object.type === TSESTree.AST_NODE_TYPES.Identifier && object.name === namespaceName) { + return ( + (property.type === TSESTree.AST_NODE_TYPES.Identifier && property.name === methodName) || + (property.type === TSESTree.AST_NODE_TYPES.Literal && property.value === methodName) + ); + } + } + return false; + }; + + return { + ImportDeclaration: (node: TSESTree.ImportDeclaration) => { + // Extract the name for the 'react-dom' namespace import + if (node.source.value === 'react-dom') { + if (!reactDomImportNamespaceName) { + const namespaceSpecifier: TSESTree.ImportClause | undefined = node.specifiers.find( + (s) => s.type === TSESTree.AST_NODE_TYPES.ImportNamespaceSpecifier + ); + if (namespaceSpecifier) { + reactDomImportNamespaceName = namespaceSpecifier.local.name; + } else { + const defaultSpecifier: TSESTree.ImportClause | undefined = node.specifiers.find( + (s) => s.type === TSESTree.AST_NODE_TYPES.ImportDefaultSpecifier + ); + if (defaultSpecifier) { + reactDomImportNamespaceName = defaultSpecifier.local.name; + } + } + } + + if (!reactDomRenderFunctionName || !reactDomUnmountFunctionName) { + const importSpecifiers: TSESTree.ImportSpecifier[] = node.specifiers.filter( + (s) => s.type === TSESTree.AST_NODE_TYPES.ImportSpecifier + ) as TSESTree.ImportSpecifier[]; + for (const importSpecifier of importSpecifiers) { + const name: string | undefined = + 'name' in importSpecifier.imported ? importSpecifier.imported.name : undefined; + if (name === 'render') { + reactDomRenderFunctionName = importSpecifier.local.name; + } else if (name === 'unmountComponentAtNode') { + reactDomUnmountFunctionName = importSpecifier.local.name; + } + } + } + } + }, + CallExpression: (node: TSESTree.CallExpression) => { + if ( + isNamespaceCallExpression(node, reactDomImportNamespaceName, 'render') || + isFunctionCallExpression(node, reactDomRenderFunctionName) + ) { + renderCallExpressions.push(node); + } else if ( + isNamespaceCallExpression(node, reactDomImportNamespaceName, 'unmountComponentAtNode') || + isFunctionCallExpression(node, reactDomUnmountFunctionName) + ) { + unmountCallExpressions.push(node); + } + }, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'Program:exit': (node: TSESTree.Program) => { + if (renderCallExpressions.length !== unmountCallExpressions.length) { + renderCallExpressions.concat(unmountCallExpressions).forEach((callExpression) => { + context.report({ node: callExpression, messageId: MESSAGE_ID }); + }); + } + } + }; + } +}; + +export { pairReactDomRenderUnmountRule }; diff --git a/eslint/eslint-plugin/src/test/fixtures/file.ts b/eslint/eslint-plugin/src/test/fixtures/file.ts new file mode 100644 index 00000000000..b2d2e4d1b84 --- /dev/null +++ b/eslint/eslint-plugin/src/test/fixtures/file.ts @@ -0,0 +1,2 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. diff --git a/eslint/eslint-plugin/src/test/fixtures/tsconfig.json b/eslint/eslint-plugin/src/test/fixtures/tsconfig.json new file mode 100644 index 00000000000..7e9126b848c --- /dev/null +++ b/eslint/eslint-plugin/src/test/fixtures/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "jsx": "preserve", + "target": "es5", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "lib": ["es2015", "es2017", "esnext"], + "experimentalDecorators": true + }, + "include": ["file.ts"] +} diff --git a/eslint/eslint-plugin/src/test/hoist-jest-mock.test.ts b/eslint/eslint-plugin/src/test/hoist-jest-mock.test.ts index b921a46442d..49e4af553cc 100644 --- a/eslint/eslint-plugin/src/test/hoist-jest-mock.test.ts +++ b/eslint/eslint-plugin/src/test/hoist-jest-mock.test.ts @@ -1,23 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ESLintUtils } from '@typescript-eslint/experimental-utils'; +import type { RuleTester } from '@typescript-eslint/rule-tester'; + +import { getRuleTesterWithProject } from './ruleTester'; import { hoistJestMock } from '../hoist-jest-mock'; -const { RuleTester } = ESLintUtils; -const ruleTester = new RuleTester({ - /* - * The underlying API requires an absolute path. `@typescript-eslint/utils` calls `require.resolve()` on the input - * and forces it to be of type '@typescript-eslint/parser' but does not have a dependency on `@typescript-eslint/parser` - * This means that it will always fail to resolve in a strict environment. - * Fortunately `require.resolve(absolutePath)` returns `absolutePath`, so we can resolve it first and cast. - */ - parser: require.resolve('@typescript-eslint/parser') as '@typescript-eslint/parser', - parserOptions: { - ecmaVersion: 2018, - sourceType: 'module' - } -}); +const ruleTester: RuleTester = getRuleTesterWithProject(); // These are the CODE_WITH_HOISTING cases from ts-jest's hoist-jest.spec.ts const INVALID_EXAMPLE_CODE = [ diff --git a/eslint/eslint-plugin/src/test/import-requires-chunk-name.test.ts b/eslint/eslint-plugin/src/test/import-requires-chunk-name.test.ts new file mode 100644 index 00000000000..c06d4771304 --- /dev/null +++ b/eslint/eslint-plugin/src/test/import-requires-chunk-name.test.ts @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { RuleTester } from '@typescript-eslint/rule-tester'; + +import { getRuleTesterWithProject } from './ruleTester'; +import { importRequiresChunkNameRule } from '../import-requires-chunk-name'; + +const ruleTester: RuleTester = getRuleTesterWithProject(); + +ruleTester.run('import-requires-chunk-name', importRequiresChunkNameRule, { + invalid: [ + { + code: [ + 'import(', + ' /* webpackChunkName: "my-chunk-name" */', + ' /* webpackChunkName: "my-chunk-name2" */', + " 'module'", + ')' + ].join('\n'), + errors: [{ messageId: 'error-import-requires-single-chunk-name' }] + }, + { + code: [ + 'import(', + ' // webpackChunkName: "my-chunk-name"', + ' // webpackChunkName: "my-chunk-name2"', + " 'module'", + ')' + ].join('\n'), + errors: [{ messageId: 'error-import-requires-single-chunk-name' }] + }, + { + code: "import('module')", + errors: [{ messageId: 'error-import-requires-chunk-name' }] + } + ], + valid: [ + { + code: 'import(/* webpackChunkName: "my-chunk-name" */\'module\')' + }, + { + code: ['import(', ' /* webpackChunkName: "my-chunk-name" */', " 'module'", ')'].join('\n') + }, + { + code: ['import(', ' // webpackChunkName: "my-chunk-name"', " 'module'", ')'].join('\n') + } + ] +}); diff --git a/eslint/eslint-plugin/src/test/no-backslash-imports.test.ts b/eslint/eslint-plugin/src/test/no-backslash-imports.test.ts new file mode 100644 index 00000000000..a508ff60138 --- /dev/null +++ b/eslint/eslint-plugin/src/test/no-backslash-imports.test.ts @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { RuleTester, TestCaseError } from '@typescript-eslint/rule-tester'; + +import { getRuleTesterWithProject } from './ruleTester'; +import { noBackslashImportsRule, MESSAGE_ID } from '../no-backslash-imports'; + +const ruleTester: RuleTester = getRuleTesterWithProject(); +const expectedErrors: TestCaseError[] = [{ messageId: MESSAGE_ID }]; + +ruleTester.run('no-backslash-imports', noBackslashImportsRule, { + invalid: [ + // Test variants + { + code: "import blah from '.\\\\foo\\\\bar'", + errors: expectedErrors, + output: "import blah from './foo/bar'" + }, + { + code: "import * as blah from '.\\\\foo\\\\bar'", + errors: expectedErrors, + output: "import * as blah from './foo/bar'" + }, + { + code: "import { blah } from '.\\\\foo\\\\bar'", + errors: expectedErrors, + output: "import { blah } from './foo/bar'" + }, + { + code: "import { _blah as Blah } from '.\\\\foo\\\\bar'", + errors: expectedErrors, + output: "import { _blah as Blah } from './foo/bar'" + }, + { + code: "import blah, { _blah as Blah } from '.\\\\foo\\\\bar'", + errors: expectedErrors, + output: "import blah, { _blah as Blah } from './foo/bar'" + }, + { + code: "import blah, * as Blah from '.\\\\foo\\\\bar'", + errors: expectedErrors, + output: "import blah, * as Blah from './foo/bar'" + }, + { + code: "import '.\\\\foo\\\\bar'", + errors: expectedErrors, + output: "import './foo/bar'" + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!.\\\\foo\\\\bar'", + errors: expectedErrors, + output: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./foo/bar'" + }, + { + code: "import blah from '.\\\\foo\\\\bar?source'", + errors: expectedErrors, + output: "import blah from './foo/bar?source'" + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!.\\\\foo\\\\bar?source'", + errors: expectedErrors, + output: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./foo/bar?source'" + }, + { + code: "export * from '.\\\\foo\\\\bar'", + errors: expectedErrors, + output: "export * from './foo/bar'" + }, + // { + // code: "export * as blah from './foo/../foo/bar'", + // errors: expectedErrors, + // output: "export * as blah from './foo/bar'" + // }, + { + code: "export { blah } from '.\\\\foo\\\\bar'", + errors: expectedErrors, + output: "export { blah } from './foo/bar'" + }, + { + code: "export { _blah as Blah } from '.\\\\foo\\\\bar'", + errors: expectedErrors, + output: "export { _blah as Blah } from './foo/bar'" + }, + { + code: "export { default } from '.\\\\foo\\\\bar'", + errors: expectedErrors, + output: "export { default } from './foo/bar'" + }, + // Test async imports + { + code: "const blah = await import('.\\\\foo\\\\bar')", + errors: expectedErrors, + output: "const blah = await import('./foo/bar')" + } + ], + valid: [ + // Test variants + { + code: "import blah from './foo/bar'" + }, + { + code: "import * as blah from './foo/bar'" + }, + { + code: "import { blah } from './foo/bar'" + }, + { + code: "import { _blah as Blah } from './foo/bar'" + }, + { + code: "import blah, { _blah as Blah } from './foo/bar'" + }, + { + code: "import blah, * as Blah from './foo/bar'" + }, + { + code: "import './foo/bar'" + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./foo/bar'" + }, + { + code: "import blah from './foo/bar?source'" + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./foo/bar?source'" + }, + { + code: "export * from './foo/bar'" + }, + // { + // code: "export * as blah from './foo/bar'" + // }, + { + code: "export { blah } from './foo/bar'" + }, + { + code: "export { _blah as Blah } from './foo/bar'" + }, + { + code: "export { default } from './foo/bar'" + }, + // Test async imports + { + code: "const blah = await import('./foo/bar')" + } + ] +}); diff --git a/eslint/eslint-plugin/src/test/no-external-local-imports.test.ts b/eslint/eslint-plugin/src/test/no-external-local-imports.test.ts new file mode 100644 index 00000000000..c18b995dac9 --- /dev/null +++ b/eslint/eslint-plugin/src/test/no-external-local-imports.test.ts @@ -0,0 +1,171 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { RuleTester } from '@typescript-eslint/rule-tester'; + +import { getRuleTesterWithoutProject } from './ruleTester'; +import { noExternalLocalImportsRule } from '../no-external-local-imports'; + +const ruleTester: RuleTester = getRuleTesterWithoutProject(); + +// The root in the test cases is the immediate directory +ruleTester.run('no-external-local-imports', noExternalLocalImportsRule, { + invalid: [ + // Test variants + { + code: "import blah from '../foo'", + errors: [{ messageId: 'error-external-local-imports' }] + }, + { + code: "import * as blah from '../foo'", + errors: [{ messageId: 'error-external-local-imports' }] + }, + { + code: "import { blah } from '../foo'", + errors: [{ messageId: 'error-external-local-imports' }] + }, + { + code: "import { _blah as Blah } from '../foo'", + errors: [{ messageId: 'error-external-local-imports' }] + }, + { + code: "import blah, { _blah as Blah } from '../foo'", + errors: [{ messageId: 'error-external-local-imports' }] + }, + { + code: "import blah, * as Blah from '../foo'", + errors: [{ messageId: 'error-external-local-imports' }] + }, + { + code: "import '../foo'", + errors: [{ messageId: 'error-external-local-imports' }] + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!../foo'", + errors: [{ messageId: 'error-external-local-imports' }] + }, + { + code: "import blah from '../foo?source'", + errors: [{ messageId: 'error-external-local-imports' }] + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!../foo?source'", + errors: [{ messageId: 'error-external-local-imports' }] + }, + { + code: "export * from '../foo'", + errors: [{ messageId: 'error-external-local-imports' }] + }, + // { + // code: "export * as blah from '../foo'", + // errors: [{ messageId: 'error-external-local-imports' }] + // }, + { + code: "export { blah } from '../foo'", + errors: [{ messageId: 'error-external-local-imports' }] + }, + { + code: "export { _blah as Blah } from '../foo'", + errors: [{ messageId: 'error-external-local-imports' }] + }, + { + code: "export { default } from '../foo'", + errors: [{ messageId: 'error-external-local-imports' }] + }, + // Test importing from outside of tsconfigRootDir + { + code: "import blah from '../foo'", + errors: [{ messageId: 'error-external-local-imports' }], + filename: `${__dirname}/blah/test.ts`, + languageOptions: { + parserOptions: { + tsconfigRootDir: `${__dirname}/blah` + } + } + }, + // Test async imports + { + code: "const blah = await import('../foo')", + errors: [{ messageId: 'error-external-local-imports' }] + }, + { + code: "const blah = await import('../foo')", + errors: [{ messageId: 'error-external-local-imports' }], + filename: `${__dirname}/blah/test.ts`, + languageOptions: { + parserOptions: { + tsconfigRootDir: `${__dirname}/blah` + } + } + } + ], + valid: [ + // Test variants + { + code: "import blah from './foo'" + }, + { + code: "import * as blah from './foo'" + }, + { + code: "import { blah } from './foo'" + }, + { + code: "import { _blah as Blah } from './foo'" + }, + { + code: "import blah, { _blah as Blah } from './foo'" + }, + { + code: "import blah, * as Blah from './foo'" + }, + { + code: "import './foo'" + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./foo'" + }, + { + code: "import blah from './foo?source'" + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./foo?source'" + }, + { + code: "export * from './foo'" + }, + // { + // code: "export * as blah from './foo'" + // }, + { + code: "export { blah } from './foo'" + }, + { + code: "export { _blah as Blah } from './foo'" + }, + { + code: "export { default } from './foo'" + }, + // Test that importing vertically within the project is valid + { + code: "import blah from '../foo/bar'", + filename: 'blah2/test.ts' + }, + { + code: "import blah from '../../foo/bar'", + filename: 'blah2/foo3/test.ts' + }, + // Test async imports + { + code: "const blah = await import('./foo')" + }, + { + code: "const blah = await import('../foo/bar')", + filename: 'blah2/test.ts' + }, + { + code: "const blah = await import('../../foo/bar')", + filename: 'blah2/foo3/test.ts' + } + ] +}); diff --git a/eslint/eslint-plugin/src/test/no-new-null.test.ts b/eslint/eslint-plugin/src/test/no-new-null.test.ts index 9c08f48f0b8..7ae2fdf8045 100644 --- a/eslint/eslint-plugin/src/test/no-new-null.test.ts +++ b/eslint/eslint-plugin/src/test/no-new-null.test.ts @@ -1,19 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ESLintUtils } from '@typescript-eslint/experimental-utils'; +import type { RuleTester } from '@typescript-eslint/rule-tester'; + +import { getRuleTesterWithProject } from './ruleTester'; import { noNewNullRule } from '../no-new-null'; -const { RuleTester } = ESLintUtils; -const ruleTester = new RuleTester({ - /* - * The underlying API requires an absolute path. `@typescript-eslint/utils` calls `require.resolve()` on the input - * and forces it to be of type '@typescript-eslint/parser' but does not have a dependency on `@typescript-eslint/parser` - * This means that it will always fail to resolve in a strict environment. - * Fortunately `require.resolve(absolutePath)` returns `absolutePath`, so we can resolve it first and cast. - */ - parser: require.resolve('@typescript-eslint/parser') as '@typescript-eslint/parser' -}); +const ruleTester: RuleTester = getRuleTesterWithProject(); ruleTester.run('no-new-null', noNewNullRule, { invalid: [ diff --git a/eslint/eslint-plugin/src/test/no-transitive-dependency-imports.test.ts b/eslint/eslint-plugin/src/test/no-transitive-dependency-imports.test.ts new file mode 100644 index 00000000000..9d61892ce34 --- /dev/null +++ b/eslint/eslint-plugin/src/test/no-transitive-dependency-imports.test.ts @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { RuleTester, TestCaseError } from '@typescript-eslint/rule-tester'; + +import { getRuleTesterWithProject } from './ruleTester'; +import { noTransitiveDependencyImportsRule, MESSAGE_ID } from '../no-transitive-dependency-imports'; + +const ruleTester: RuleTester = getRuleTesterWithProject(); +const expectedErrors: TestCaseError[] = [{ messageId: MESSAGE_ID }]; + +ruleTester.run('no-transitive-dependency-imports', noTransitiveDependencyImportsRule, { + invalid: [ + // Test variants + { + code: "import blah from './node_modules/foo/node_modules/bar'", + errors: expectedErrors + }, + { + code: "import * as blah from './node_modules/foo/node_modules/bar'", + errors: expectedErrors + }, + { + code: "import { blah } from './node_modules/foo/node_modules/bar'", + errors: expectedErrors + }, + { + code: "import { _blah as Blah } from './node_modules/foo/node_modules/bar'", + errors: expectedErrors + }, + { + code: "import blah, { _blah as Blah } from './node_modules/foo/node_modules/bar'", + errors: expectedErrors + }, + { + code: "import blah, * as Blah from './node_modules/foo/node_modules/bar'", + errors: expectedErrors + }, + { + code: "import './node_modules/foo/node_modules/bar'", + errors: expectedErrors + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./node_modules/foo/node_modules/bar'", + errors: expectedErrors + }, + { + code: "import blah from './node_modules/foo/node_modules/bar?source'", + errors: expectedErrors + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./node_modules/foo/node_modules/bar?source'", + errors: expectedErrors + }, + { + code: "export * from './node_modules/foo/node_modules/bar'", + errors: expectedErrors + }, + // { + // code: "export * as blah from './node_modules/foo/node_modules/bar'", + // errors: expectedErrors + // }, + { + code: "export { blah } from './node_modules/foo/node_modules/bar'", + errors: expectedErrors + }, + { + code: "export { _blah as Blah } from './node_modules/foo/node_modules/bar'", + errors: expectedErrors + }, + { + code: "export { default } from './node_modules/foo/node_modules/bar'", + errors: expectedErrors + }, + // Test async imports + { + code: "const blah = await import('./node_modules/foo/node_modules/bar')", + errors: expectedErrors + } + ], + valid: [ + // Test variants + { + code: "import blah from './node_modules/foo'" + }, + { + code: "import * as blah from './node_modules/foo'" + }, + { + code: "import { blah } from './node_modules/foo'" + }, + { + code: "import { _blah as Blah } from './node_modules/foo'" + }, + { + code: "import blah, { _blah as Blah } from './node_modules/foo'" + }, + { + code: "import blah, * as Blah from './node_modules/foo'" + }, + { + code: "import './node_modules/foo'" + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./node_modules/foo'" + }, + { + code: "import blah from './node_modules/foo?source'" + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./node_modules/foo?source'" + }, + { + code: "export * from './node_modules/foo'" + }, + // { + // code: "export * as blah from './node_modules/foo'" + // }, + { + code: "export { blah } from './node_modules/foo'" + }, + { + code: "export { _blah as Blah } from './node_modules/foo'" + }, + { + code: "export { default } from './node_modules/foo'" + }, + // Test async imports + { + code: "const blah = await import('./node_modules/foo')" + } + ] +}); diff --git a/eslint/eslint-plugin/src/test/no-untyped-underscore.test.ts b/eslint/eslint-plugin/src/test/no-untyped-underscore.test.ts index ae89fba73be..388646d7ecd 100644 --- a/eslint/eslint-plugin/src/test/no-untyped-underscore.test.ts +++ b/eslint/eslint-plugin/src/test/no-untyped-underscore.test.ts @@ -1,19 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ESLintUtils } from '@typescript-eslint/experimental-utils'; +import type { RuleTester } from '@typescript-eslint/rule-tester'; + +import { getRuleTesterWithProject } from './ruleTester'; import { noUntypedUnderscoreRule } from '../no-untyped-underscore'; -const { RuleTester } = ESLintUtils; -const ruleTester = new RuleTester({ - /* - * The underlying API requires an absolute path. `@typescript-eslint/utils` calls `require.resolve()` on the input - * and forces it to be of type '@typescript-eslint/parser' but does not have a dependency on `@typescript-eslint/parser` - * This means that it will always fail to resolve in a strict environment. - * Fortunately `require.resolve(absolutePath)` returns `absolutePath`, so we can resolve it first and cast. - */ - parser: require.resolve('@typescript-eslint/parser') as '@typescript-eslint/parser' -}); +const ruleTester: RuleTester = getRuleTesterWithProject(); ruleTester.run('no-untyped-underscore', noUntypedUnderscoreRule, { invalid: [ diff --git a/eslint/eslint-plugin/src/test/normalized-imports.test.ts b/eslint/eslint-plugin/src/test/normalized-imports.test.ts new file mode 100644 index 00000000000..b65a88565c2 --- /dev/null +++ b/eslint/eslint-plugin/src/test/normalized-imports.test.ts @@ -0,0 +1,215 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { RuleTester, TestCaseError } from '@typescript-eslint/rule-tester'; + +import { getRuleTesterWithoutProject } from './ruleTester'; +import { normalizedImportsRule, MESSAGE_ID } from '../normalized-imports'; + +const ruleTester: RuleTester = getRuleTesterWithoutProject(); + +const expectedErrors: TestCaseError[] = [{ messageId: MESSAGE_ID }]; + +// The root in the test cases is the immediate directory +ruleTester.run('normalized-imports', normalizedImportsRule, { + invalid: [ + // Test variants + { + code: "import blah from './foo/../foo/bar'", + errors: expectedErrors, + output: "import blah from './foo/bar'" + }, + { + code: "import * as blah from './foo/../foo/bar'", + errors: expectedErrors, + output: "import * as blah from './foo/bar'" + }, + { + code: "import { blah } from './foo/../foo/bar'", + errors: expectedErrors, + output: "import { blah } from './foo/bar'" + }, + { + code: "import { _blah as Blah } from './foo/../foo/bar'", + errors: expectedErrors, + output: "import { _blah as Blah } from './foo/bar'" + }, + { + code: "import blah, { _blah as Blah } from './foo/../foo/bar'", + errors: expectedErrors, + output: "import blah, { _blah as Blah } from './foo/bar'" + }, + { + code: "import blah, * as Blah from './foo/../foo/bar'", + errors: expectedErrors, + output: "import blah, * as Blah from './foo/bar'" + }, + { + code: "import './foo/../foo/bar'", + errors: expectedErrors, + output: "import './foo/bar'" + }, + // While directory imports aren't ideal, especially from the immediate directory, the path is normalized + { + code: "import blah from './'", + errors: expectedErrors, + output: "import blah from '.'" + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./foo/../foo/bar'", + errors: expectedErrors, + output: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./foo/bar'" + }, + { + code: "import blah from './foo/../foo/bar?source'", + errors: expectedErrors, + output: "import blah from './foo/bar?source'" + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./foo/../foo/bar?source'", + errors: expectedErrors, + output: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./foo/bar?source'" + }, + { + code: "export * from './foo/../foo/bar'", + errors: expectedErrors, + output: "export * from './foo/bar'" + }, + // { + // code: "export * as blah from './foo/../foo/bar'", + // errors: expectedErrors, + // output: "export * as blah from './foo/bar'" + // }, + { + code: "export { blah } from './foo/../foo/bar'", + errors: expectedErrors, + output: "export { blah } from './foo/bar'" + }, + { + code: "export { _blah as Blah } from './foo/../foo/bar'", + errors: expectedErrors, + output: "export { _blah as Blah } from './foo/bar'" + }, + { + code: "export { default } from './foo/../foo/bar'", + errors: expectedErrors, + output: "export { default } from './foo/bar'" + }, + // Test leaving and re-entering the current directory + { + code: "import blah from '../foo/bar'", + errors: expectedErrors, + output: "import blah from './bar'", + filename: 'foo/test.ts' + }, + { + code: "import blah from '../../foo/foo2/bar'", + errors: expectedErrors, + output: "import blah from './bar'", + filename: 'foo/foo2/test.ts' + }, + { + code: "import blah from '../../foo/bar'", + errors: expectedErrors, + output: "import blah from '../bar'", + filename: 'foo/foo2/test.ts' + }, + // Test async imports + { + code: "const blah = await import('./foo/../foo/bar')", + errors: expectedErrors, + output: "const blah = await import('./foo/bar')" + }, + { + code: "const blah = await import('../foo/bar')", + errors: expectedErrors, + output: "const blah = await import('./bar')", + filename: 'foo/test.ts' + }, + { + code: "const blah = await import('../../foo/foo2/bar')", + errors: expectedErrors, + output: "const blah = await import('./bar')", + filename: 'foo/foo2/test.ts' + }, + { + code: "const blah = await import('../../foo/bar')", + errors: expectedErrors, + output: "const blah = await import('../bar')", + filename: 'foo/foo2/test.ts' + } + ], + valid: [ + // Test variants + { + code: "import blah from './foo/bar'" + }, + { + code: "import * as blah from './foo/bar'" + }, + { + code: "import { blah } from './foo/bar'" + }, + { + code: "import { _blah as Blah } from './foo/bar'" + }, + { + code: "import blah, { _blah as Blah } from './foo/bar'" + }, + { + code: "import blah, * as Blah from './foo/bar'" + }, + { + code: "import './foo/bar'" + }, + // While directory imports aren't ideal, especially from the immediate directory, the path is normalized + { + code: "import blah from '.'" + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./foo/bar'" + }, + { + code: "import blah from './foo/bar?source'" + }, + { + code: "import blah from '!!file-loader?name=image_[name]_[hash:8][ext]!./foo/bar?source'" + }, + { + code: "export * from './foo/bar'" + }, + // { + // code: "export * as blah from './foo/bar'" + // }, + { + code: "export { blah } from './foo/bar'" + }, + { + code: "export { _blah as Blah } from './foo/bar'" + }, + { + code: "export { default } from './foo/bar'" + }, + // Test that importing vertically is valid + { + code: "import blah from '../foo/bar'", + filename: 'foo2/test.ts' + }, + { + code: "import blah from '../../foo/bar'", + filename: 'foo2/foo3/test.ts' + }, + // Test async imports + { + code: "const blah = await import('./foo/bar')" + }, + { + code: "const blah = await import('../foo/bar')", + filename: 'foo2/test.ts' + }, + { + code: "const blah = import('../../foo/bar')", + filename: 'foo2/foo3/test.ts' + } + ] +}); diff --git a/eslint/eslint-plugin/src/test/pair-react-dom-render-unmount.test.ts b/eslint/eslint-plugin/src/test/pair-react-dom-render-unmount.test.ts new file mode 100644 index 00000000000..a5027f07807 --- /dev/null +++ b/eslint/eslint-plugin/src/test/pair-react-dom-render-unmount.test.ts @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { RuleTester } from '@typescript-eslint/rule-tester'; + +import { getRuleTesterWithProject } from './ruleTester'; +import { pairReactDomRenderUnmountRule } from '../pair-react-dom-render-unmount'; + +const ruleTester: RuleTester = getRuleTesterWithProject(); + +ruleTester.run('pair-react-dom-render-unmount', pairReactDomRenderUnmountRule, { + invalid: [ + { + code: [ + "import ReactDOM from 'react-dom';", + 'ReactDOM.render();', + 'ReactDOM.render();', + 'ReactDOM.render();', + 'ReactDOM.unmountComponentAtNode();', + 'ReactDOM.unmountComponentAtNode();' + ].join('\n'), + errors: [ + { messageId: 'error-pair-react-dom-render-unmount', line: 2 }, + { messageId: 'error-pair-react-dom-render-unmount', line: 3 }, + { messageId: 'error-pair-react-dom-render-unmount', line: 4 }, + { messageId: 'error-pair-react-dom-render-unmount', line: 5 }, + { messageId: 'error-pair-react-dom-render-unmount', line: 6 } + ] + }, + { + code: ["import * as ReactDOM from 'react-dom';", 'ReactDOM.render();'].join('\n'), + errors: [{ messageId: 'error-pair-react-dom-render-unmount', line: 2 }] + }, + { + code: ["import ReactDOM from 'react-dom';", 'ReactDOM.unmountComponentAtNode();'].join('\n'), + errors: [{ messageId: 'error-pair-react-dom-render-unmount', line: 2 }] + }, + { + code: [ + "import { render, unmountComponentAtNode } from 'react-dom';", + 'render();', + 'unmountComponentAtNode();', + 'unmountComponentAtNode();' + ].join('\n'), + errors: [ + { messageId: 'error-pair-react-dom-render-unmount', line: 2 }, + { messageId: 'error-pair-react-dom-render-unmount', line: 3 }, + { messageId: 'error-pair-react-dom-render-unmount', line: 4 } + ] + }, + { + code: [ + "import { render as ReactRender, unmountComponentAtNode as ReactUnmount } from 'react-dom';", + 'ReactRender();', + 'ReactUnmount();', + 'ReactUnmount();' + ].join('\n'), + errors: [ + { messageId: 'error-pair-react-dom-render-unmount', line: 2 }, + { messageId: 'error-pair-react-dom-render-unmount', line: 3 }, + { messageId: 'error-pair-react-dom-render-unmount', line: 4 } + ] + } + ], + valid: [ + { + code: [ + "import ReactDOM from 'react-dom';", + 'ReactDOM.render();', + 'ReactDOM.render();', + 'ReactDOM.render();', + 'ReactDOM.unmountComponentAtNode();', + 'ReactDOM.unmountComponentAtNode();', + 'ReactDOM.unmountComponentAtNode();' + ].join('\n') + }, + { + code: [ + "import * as ReactDOM from 'react-dom';", + 'ReactDOM.render();', + 'ReactDOM.unmountComponentAtNode();' + ].join('\n') + }, + { + code: [ + "import ReactDOM from 'react-dom';", + 'ReactDOM.render();', + 'ReactDOM.unmountComponentAtNode();' + ].join('\n') + }, + { + code: [ + "import { render, unmountComponentAtNode } from 'react-dom';", + 'render();', + 'unmountComponentAtNode();' + ].join('\n') + }, + { + code: [ + "import { render as ReactRender, unmountComponentAtNode as ReactUnmount } from 'react-dom';", + 'ReactRender();', + 'ReactUnmount();' + ].join('\n') + } + ] +}); diff --git a/eslint/eslint-plugin/src/test/ruleTester.ts b/eslint/eslint-plugin/src/test/ruleTester.ts new file mode 100644 index 00000000000..82c75a5bad4 --- /dev/null +++ b/eslint/eslint-plugin/src/test/ruleTester.ts @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as parser from '@typescript-eslint/parser'; +import { RuleTester } from '@typescript-eslint/rule-tester'; + +export function getRuleTesterWithoutProject(): RuleTester { + return new RuleTester({ + languageOptions: { + parser + } + }); +} + +export function getRuleTesterWithProject(): RuleTester { + return new RuleTester({ + languageOptions: { + parser, + parserOptions: { + sourceType: 'module', + // Do not run under 'lib" folder + tsconfigRootDir: `${__dirname}/../../src/test/fixtures`, + project: './tsconfig.json' + } + } + }); +} diff --git a/eslint/eslint-plugin/src/test/typedef-var.test.ts b/eslint/eslint-plugin/src/test/typedef-var.test.ts index 1dd833a2fcb..7f7b9b69bf7 100644 --- a/eslint/eslint-plugin/src/test/typedef-var.test.ts +++ b/eslint/eslint-plugin/src/test/typedef-var.test.ts @@ -1,19 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ESLintUtils } from '@typescript-eslint/experimental-utils'; +import type { RuleTester } from '@typescript-eslint/rule-tester'; + +import { getRuleTesterWithProject } from './ruleTester'; import { typedefVar } from '../typedef-var'; -const { RuleTester } = ESLintUtils; -const ruleTester = new RuleTester({ - /* - * The underlying API requires an absolute path. `@typescript-eslint/utils` calls `require.resolve()` on the input - * and forces it to be of type '@typescript-eslint/parser' but does not have a dependency on `@typescript-eslint/parser` - * This means that it will always fail to resolve in a strict environment. - * Fortunately `require.resolve(absolutePath)` returns `absolutePath`, so we can resolve it first and cast. - */ - parser: require.resolve('@typescript-eslint/parser') as '@typescript-eslint/parser' -}); +const ruleTester: RuleTester = getRuleTesterWithProject(); ruleTester.run('typedef-var', typedefVar, { invalid: [ @@ -47,6 +40,15 @@ ruleTester.run('typedef-var', typedefVar, { { code: 'for (const x of []) { }' }, + { + code: 'const x = 1 as const;' + }, + { + code: 'const x: 1 = 1;' + }, + { + code: 'const x: number = 1;' + }, { // prettier-ignore code: [ diff --git a/eslint/eslint-plugin/src/typedef-var.ts b/eslint/eslint-plugin/src/typedef-var.ts index 75927796e3c..cd74234a0c2 100644 --- a/eslint/eslint-plugin/src/typedef-var.ts +++ b/eslint/eslint-plugin/src/typedef-var.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; +import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; type MessageIds = 'expected-typedef' | 'expected-typedef-named'; type Options = []; @@ -24,9 +24,7 @@ const typedefVar: TSESLint.RuleModule = { docs: { description: 'Supplements the "@typescript-eslint/typedef" rule by relaxing the requirements for local variables', - // Deprecated in ESLint v8; Keep for backwards compatibility - category: 'Stylistic Issues', - recommended: 'error', + recommended: 'recommended', url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin' } as TSESLint.RuleMetaDataDocs }, @@ -46,12 +44,22 @@ const typedefVar: TSESLint.RuleModule = { } return { - VariableDeclarator(node): void { + VariableDeclarator(node: TSESTree.VariableDeclarator): void { if (node.id.typeAnnotation) { // An explicit type declaration was provided return; } + if ( + node.init?.type === AST_NODE_TYPES.TSAsExpression && + node.init.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference && + node.init.typeAnnotation.typeName.type === AST_NODE_TYPES.Identifier && + node.init.typeAnnotation.typeName.name === 'const' + ) { + // An `as const` type declaration was provided + return; + } + // These are @typescript-eslint/typedef exemptions if ( node.id.type === AST_NODE_TYPES.ArrayPattern /* ArrayDestructuring */ || @@ -96,16 +104,19 @@ const typedefVar: TSESLint.RuleModule = { // const NODE = 123; // } // } + // eslint-disable-next-line no-fallthrough case AST_NODE_TYPES.MethodDefinition: // let f = function() { // const NODE = 123; // } + // eslint-disable-next-line no-fallthrough case AST_NODE_TYPES.FunctionExpression: // let f = () => { // const NODE = 123; // } + // eslint-disable-next-line no-fallthrough case AST_NODE_TYPES.ArrowFunctionExpression: // Stop traversing and don't report an error return; diff --git a/eslint/eslint-plugin/tsconfig.json b/eslint/eslint-plugin/tsconfig.json index fbc2f5c0a6c..09aacf59d98 100644 --- a/eslint/eslint-plugin/tsconfig.json +++ b/eslint/eslint-plugin/tsconfig.json @@ -1,7 +1,11 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "types": ["heft-jest", "node"] + "module": "Node16", + + // TODO: Update the rest of the repo to target ES2020 + "target": "ES2020", + "lib": ["ES2020"] } } diff --git a/eslint/local-eslint-config/.gitignore b/eslint/local-eslint-config/.gitignore new file mode 100644 index 00000000000..281714b6678 --- /dev/null +++ b/eslint/local-eslint-config/.gitignore @@ -0,0 +1,3 @@ +/flat/mixins +/flat/patch +/flat/profile \ No newline at end of file diff --git a/eslint/local-eslint-config/.npmignore b/eslint/local-eslint-config/.npmignore new file mode 100644 index 00000000000..ad53b8deba3 --- /dev/null +++ b/eslint/local-eslint-config/.npmignore @@ -0,0 +1,38 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- + +!/flat/** diff --git a/eslint/local-eslint-config/config/heft.json b/eslint/local-eslint-config/config/heft.json new file mode 100644 index 00000000000..14fee0004b8 --- /dev/null +++ b/eslint/local-eslint-config/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "phasesByName": { + "build": { + "cleanFiles": [{ "includeGlobs": ["flat/**"] }], + + "tasksByName": { + "copy-contents": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "node_modules/decoupled-local-node-rig/profiles/default/includes/eslint", + "destinationFolders": ["."], + "includeGlobs": ["**"] + } + ] + } + } + } + } + } + } +} diff --git a/eslint/local-eslint-config/config/rush-project.json b/eslint/local-eslint-config/config/rush-project.json new file mode 100644 index 00000000000..a2c8bc8a483 --- /dev/null +++ b/eslint/local-eslint-config/config/rush-project.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + + "operationSettings": [ + { + "operationName": "_phase:lite-build", + "outputFolderNames": ["flat"] + } + ] +} diff --git a/eslint/local-eslint-config/package.json b/eslint/local-eslint-config/package.json new file mode 100644 index 00000000000..2a579e046ea --- /dev/null +++ b/eslint/local-eslint-config/package.json @@ -0,0 +1,33 @@ +{ + "name": "local-eslint-config", + "version": "1.0.0", + "private": true, + "description": "An ESLint configuration consumed projects inside the rushstack repo.", + "scripts": { + "build": "heft build --clean", + "_phase:lite-build": "heft build --clean" + }, + "peerDependencies": { + "eslint": "^9.25.1", + "typescript": ">=4.7.0" + }, + "devDependencies": { + "eslint": "~9.37.0", + "typescript": "~5.8.2", + "@rushstack/heft": "1.2.22", + "decoupled-local-node-rig": "workspace:*" + }, + "dependencies": { + "@rushstack/eslint-config": "workspace:*", + "@rushstack/eslint-patch": "workspace:*", + "@rushstack/eslint-plugin": "workspace:*", + "@typescript-eslint/eslint-plugin": "~8.56.1", + "@typescript-eslint/parser": "~8.56.1", + "eslint-import-resolver-node": "0.3.9", + "eslint-plugin-header": "~3.1.1", + "eslint-plugin-headers": "~1.2.1", + "eslint-plugin-import": "2.32.0", + "eslint-plugin-jsdoc": "50.6.11", + "eslint-plugin-react-hooks": "5.2.0" + } +} diff --git a/heft-plugins/heft-api-extractor-plugin/.eslintrc.js b/heft-plugins/heft-api-extractor-plugin/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/heft-plugins/heft-api-extractor-plugin/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/heft-plugins/heft-api-extractor-plugin/.npmignore b/heft-plugins/heft-api-extractor-plugin/.npmignore index 768cd348769..f7a40e10213 100644 --- a/heft-plugins/heft-api-extractor-plugin/.npmignore +++ b/heft-plugins/heft-api-extractor-plugin/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,17 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) -!heft-plugin.json \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-api-extractor-plugin/CHANGELOG.json b/heft-plugins/heft-api-extractor-plugin/CHANGELOG.json index b56579d333e..6b50501c015 100644 --- a/heft-plugins/heft-api-extractor-plugin/CHANGELOG.json +++ b/heft-plugins/heft-api-extractor-plugin/CHANGELOG.json @@ -1,6 +1,3478 @@ { "name": "@rushstack/heft-api-extractor-plugin", "entries": [ + { + "version": "1.3.22", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.22", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "1.3.21", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.21", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "1.3.20", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.20", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "1.3.19", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.19", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "1.3.18", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.18", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "1.3.17", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "1.3.16", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.16", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "1.3.15", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.15", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "1.3.14", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.14", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "patch": [ + { + "comment": "Bump semver." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "1.3.13", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "1.3.12", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.12", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "1.3.11", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "1.3.10", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "1.3.9", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "1.3.8", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "1.3.7", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "1.3.6", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "1.3.5", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "1.3.4", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "1.3.3", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "1.3.2", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "1.3.1", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "1.3.0", + "tag": "@rushstack/heft-api-extractor-plugin_v1.3.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "1.2.10", + "tag": "@rushstack/heft-api-extractor-plugin_v1.2.10", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "1.2.9", + "tag": "@rushstack/heft-api-extractor-plugin_v1.2.9", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "1.2.8", + "tag": "@rushstack/heft-api-extractor-plugin_v1.2.8", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "1.2.7", + "tag": "@rushstack/heft-api-extractor-plugin_v1.2.7", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "1.2.6", + "tag": "@rushstack/heft-api-extractor-plugin_v1.2.6", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "1.2.5", + "tag": "@rushstack/heft-api-extractor-plugin_v1.2.5", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "1.2.4", + "tag": "@rushstack/heft-api-extractor-plugin_v1.2.4", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "1.2.3", + "tag": "@rushstack/heft-api-extractor-plugin_v1.2.3", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "1.2.2", + "tag": "@rushstack/heft-api-extractor-plugin_v1.2.2", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.5` to `1.1.6`" + } + ] + } + }, + { + "version": "1.2.1", + "tag": "@rushstack/heft-api-extractor-plugin_v1.2.1", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.4` to `1.1.5`" + } + ] + } + }, + { + "version": "1.2.0", + "tag": "@rushstack/heft-api-extractor-plugin_v1.2.0", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "minor": [ + { + "comment": "Include a `printApiReportDiff` option in the `config/api-extractor-task.json` config file that, when set to `\"production\"` (and the `--production` flag is specified) or `\"always\"`, causes a diff of the API report (*.api.md) to be printed if the report is changed. This is useful for diagnosing issues that only show up in CI." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.3` to `1.1.4`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft-api-extractor-plugin_v1.1.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.2` to `1.1.3`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft-api-extractor-plugin_v1.1.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.1` to `1.1.2`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft-api-extractor-plugin_v1.1.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.0` to `1.1.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-api-extractor-plugin_v1.1.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.0.0` to `1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-api-extractor-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.75.0` to `1.0.0`" + } + ] + } + }, + { + "version": "0.4.14", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.14", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.5` to `0.75.0`" + } + ] + } + }, + { + "version": "0.4.13", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.13", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.4` to `0.74.5`" + } + ] + } + }, + { + "version": "0.4.12", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.12", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.3` to `0.74.4`" + } + ] + } + }, + { + "version": "0.4.11", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.11", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.2` to `0.74.3`" + } + ] + } + }, + { + "version": "0.4.10", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.10", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.1` to `0.74.2`" + } + ] + } + }, + { + "version": "0.4.9", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.9", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.0` to `0.74.1`" + } + ] + } + }, + { + "version": "0.4.8", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.8", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.6` to `0.74.0`" + } + ] + } + }, + { + "version": "0.4.7", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.7", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.5` to `0.73.6`" + } + ] + } + }, + { + "version": "0.4.6", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.6", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.4` to `0.73.5`" + } + ] + } + }, + { + "version": "0.4.5", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.5", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.3` to `0.73.4`" + } + ] + } + }, + { + "version": "0.4.4", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.4", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.2` to `0.73.3`" + } + ] + } + }, + { + "version": "0.4.3", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.3", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.1` to `0.73.2`" + } + ] + } + }, + { + "version": "0.4.2", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.2", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "patch": [ + { + "comment": "Update documentation for `extends`" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.0` to `0.73.1`" + } + ] + } + }, + { + "version": "0.4.1", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.1", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.72.0` to `0.73.0`" + } + ] + } + }, + { + "version": "0.4.0", + "tag": "@rushstack/heft-api-extractor-plugin_v0.4.0", + "date": "Wed, 09 Apr 2025 00:11:02 GMT", + "comments": { + "minor": [ + { + "comment": "Use `tryLoadProjectConfigurationFileAsync` Heft API to remove direct dependency on `@rushstack/heft-config-file`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.71.2` to `0.72.0`" + } + ] + } + }, + { + "version": "0.3.77", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.77", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.71.1` to `0.71.2`" + } + ] + } + }, + { + "version": "0.3.76", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.76", + "date": "Tue, 25 Mar 2025 15:11:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.71.0` to `0.71.1`" + } + ] + } + }, + { + "version": "0.3.75", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.75", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.70.1` to `0.71.0`" + } + ] + } + }, + { + "version": "0.3.74", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.74", + "date": "Wed, 12 Mar 2025 00:11:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.70.0` to `0.70.1`" + } + ] + } + }, + { + "version": "0.3.73", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.73", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.69.3` to `0.70.0`" + } + ] + } + }, + { + "version": "0.3.72", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.72", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.69.2` to `0.69.3`" + } + ] + } + }, + { + "version": "0.3.71", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.71", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.51.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.69.1` to `0.69.2`" + } + ] + } + }, + { + "version": "0.3.70", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.70", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.51.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.69.0` to `0.69.1`" + } + ] + } + }, + { + "version": "0.3.69", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.69", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.18` to `0.69.0`" + } + ] + } + }, + { + "version": "0.3.68", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.68", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.50.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.17` to `0.68.18`" + } + ] + } + }, + { + "version": "0.3.67", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.67", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.16` to `0.68.17`" + } + ] + } + }, + { + "version": "0.3.66", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.66", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.5`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.50.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.15` to `0.68.16`" + } + ] + } + }, + { + "version": "0.3.65", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.65", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.14` to `0.68.15`" + } + ] + } + }, + { + "version": "0.3.64", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.64", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.49.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.13` to `0.68.14`" + } + ] + } + }, + { + "version": "0.3.63", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.63", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.49.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.12` to `0.68.13`" + } + ] + } + }, + { + "version": "0.3.62", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.62", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.49.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.11` to `0.68.12`" + } + ] + } + }, + { + "version": "0.3.61", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.61", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.48.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.10` to `0.68.11`" + } + ] + } + }, + { + "version": "0.3.60", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.60", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.9` to `0.68.10`" + } + ] + } + }, + { + "version": "0.3.59", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.59", + "date": "Tue, 03 Dec 2024 16:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.8` to `0.68.9`" + } + ] + } + }, + { + "version": "0.3.58", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.58", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.48.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.7` to `0.68.8`" + } + ] + } + }, + { + "version": "0.3.57", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.57", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.6` to `0.68.7`" + } + ] + } + }, + { + "version": "0.3.56", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.56", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.5` to `0.68.6`" + } + ] + } + }, + { + "version": "0.3.55", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.55", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.4` to `0.68.5`" + } + ] + } + }, + { + "version": "0.3.54", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.54", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.3` to `0.68.4`" + } + ] + } + }, + { + "version": "0.3.53", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.53", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.2` to `0.68.3`" + } + ] + } + }, + { + "version": "0.3.52", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.52", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.1` to `0.68.2`" + } + ] + } + }, + { + "version": "0.3.51", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.51", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.0` to `0.68.1`" + } + ] + } + }, + { + "version": "0.3.50", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.50", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.67.2` to `0.68.0`" + } + ] + } + }, + { + "version": "0.3.49", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.49", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.67.1` to `0.67.2`" + } + ] + } + }, + { + "version": "0.3.48", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.48", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.67.0` to `0.67.1`" + } + ] + } + }, + { + "version": "0.3.47", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.47", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.26` to `0.67.0`" + } + ] + } + }, + { + "version": "0.3.46", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.46", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.25` to `0.66.26`" + } + ] + } + }, + { + "version": "0.3.45", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.45", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.24` to `0.66.25`" + } + ] + } + }, + { + "version": "0.3.44", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.44", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.23` to `0.66.24`" + } + ] + } + }, + { + "version": "0.3.43", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.43", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.22` to `0.66.23`" + } + ] + } + }, + { + "version": "0.3.42", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.42", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.21` to `0.66.22`" + } + ] + } + }, + { + "version": "0.3.41", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.41", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.20` to `0.66.21`" + } + ] + } + }, + { + "version": "0.3.40", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.40", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.19` to `0.66.20`" + } + ] + } + }, + { + "version": "0.3.39", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.39", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.18` to `0.66.19`" + } + ] + } + }, + { + "version": "0.3.38", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.38", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.17` to `0.66.18`" + } + ] + } + }, + { + "version": "0.3.37", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.37", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.25`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.46.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.16` to `0.66.17`" + } + ] + } + }, + { + "version": "0.3.36", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.36", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.24`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.46.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.15` to `0.66.16`" + } + ] + } + }, + { + "version": "0.3.35", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.35", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.46.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.14` to `0.66.15`" + } + ] + } + }, + { + "version": "0.3.34", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.34", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.23`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.45.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.13` to `0.66.14`" + } + ] + } + }, + { + "version": "0.3.33", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.33", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.22`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.45.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.12` to `0.66.13`" + } + ] + } + }, + { + "version": "0.3.32", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.32", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.21`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.44.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.11` to `0.66.12`" + } + ] + } + }, + { + "version": "0.3.31", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.31", + "date": "Fri, 24 May 2024 00:15:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.44.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.10` to `0.66.11`" + } + ] + } + }, + { + "version": "0.3.30", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.30", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.20`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.9` to `0.66.10`" + } + ] + } + }, + { + "version": "0.3.29", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.29", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.8` to `0.66.9`" + } + ] + } + }, + { + "version": "0.3.28", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.28", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.19`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.7` to `0.66.8`" + } + ] + } + }, + { + "version": "0.3.27", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.27", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.18`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.6` to `0.66.7`" + } + ] + } + }, + { + "version": "0.3.26", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.26", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.17`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.5` to `0.66.6`" + } + ] + } + }, + { + "version": "0.3.25", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.25", + "date": "Wed, 08 May 2024 22:23:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.4` to `0.66.5`" + } + ] + } + }, + { + "version": "0.3.24", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.24", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.16`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.3` to `0.66.4`" + } + ] + } + }, + { + "version": "0.3.23", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.23", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.15`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.2` to `0.66.3`" + } + ] + } + }, + { + "version": "0.3.22", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.22", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.1` to `0.66.2`" + } + ] + } + }, + { + "version": "0.3.21", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.21", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.0` to `0.66.1`" + } + ] + } + }, + { + "version": "0.3.20", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.20", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.10` to `0.66.0`" + } + ] + } + }, + { + "version": "0.3.19", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.19", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.9` to `0.65.10`" + } + ] + } + }, + { + "version": "0.3.18", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.18", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.8` to `0.65.9`" + } + ] + } + }, + { + "version": "0.3.17", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.17", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.7` to `0.65.8`" + } + ] + } + }, + { + "version": "0.3.16", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.16", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.6` to `0.65.7`" + } + ] + } + }, + { + "version": "0.3.15", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.15", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.41.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.5` to `0.65.6`" + } + ] + } + }, + { + "version": "0.3.14", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.14", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.14`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.41.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.4` to `0.65.5`" + } + ] + } + }, + { + "version": "0.3.13", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.13", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.3` to `0.65.4`" + } + ] + } + }, + { + "version": "0.3.12", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.12", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.13`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.2` to `0.65.3`" + } + ] + } + }, + { + "version": "0.3.11", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.11", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.1` to `0.65.2`" + } + ] + } + }, + { + "version": "0.3.10", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.10", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.0` to `0.65.1`" + } + ] + } + }, + { + "version": "0.3.9", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.9", + "date": "Tue, 20 Feb 2024 16:10:53 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.8` to `0.65.0`" + } + ] + } + }, + { + "version": "0.3.8", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.8", + "date": "Mon, 19 Feb 2024 21:54:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.11`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.7` to `0.64.8`" + } + ] + } + }, + { + "version": "0.3.7", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.7", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.10`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.6` to `0.64.7`" + } + ] + } + }, + { + "version": "0.3.6", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.6", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.5` to `0.64.6`" + } + ] + } + }, + { + "version": "0.3.5", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.5", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.4` to `0.64.5`" + } + ] + } + }, + { + "version": "0.3.4", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.4", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.3` to `0.64.4`" + } + ] + } + }, + { + "version": "0.3.3", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.3", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.2` to `0.64.3`" + } + ] + } + }, + { + "version": "0.3.2", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.2", + "date": "Tue, 23 Jan 2024 20:12:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.1` to `0.64.2`" + } + ] + } + }, + { + "version": "0.3.1", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.1", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.0` to `0.64.1`" + } + ] + } + }, + { + "version": "0.3.0", + "tag": "@rushstack/heft-api-extractor-plugin_v0.3.0", + "date": "Tue, 16 Jan 2024 18:30:10 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for TypeScript 5.3" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.6` to `0.64.0`" + } + ] + } + }, + { + "version": "0.2.16", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.16", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.5` to `0.63.6`" + } + ] + } + }, + { + "version": "0.2.15", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.15", + "date": "Wed, 20 Dec 2023 01:09:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.4` to `0.63.5`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.3` to `0.63.4`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.2` to `0.63.3`" + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.1` to `0.63.2`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.0` to `0.63.1`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.10", + "date": "Mon, 30 Oct 2023 23:36:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.62.3` to `0.63.0`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.9", + "date": "Sun, 01 Oct 2023 02:56:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.62.2` to `0.62.3`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.62.1` to `0.62.2`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.62.0` to `0.62.1`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.6", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.61.3` to `0.62.0`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.61.2` to `0.61.3`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.61.1` to `0.61.2`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.61.0` to `0.61.1`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.2", + "date": "Fri, 22 Sep 2023 00:05:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.60.0` to `0.61.0`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.1", + "date": "Tue, 19 Sep 2023 15:21:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.59.0` to `0.60.0`" + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/heft-api-extractor-plugin_v0.2.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + }, + { + "comment": "Add \"runInWatchMode\" task configuration flag to support invocation when Heft is in watch mode. Does not currently offer any performance benefit." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.58.2` to `0.59.0`" + } + ] + } + }, + { + "version": "0.1.18", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.18", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.58.1` to `0.58.2`" + } + ] + } + }, + { + "version": "0.1.17", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.17", + "date": "Sat, 29 Jul 2023 00:22:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.58.0` to `0.58.1`" + } + ] + } + }, + { + "version": "0.1.16", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.16", + "date": "Thu, 20 Jul 2023 20:47:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.57.1` to `0.58.0`" + } + ] + } + }, + { + "version": "0.1.15", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.15", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "patch": [ + { + "comment": "Updated semver dependency" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.57.0` to `0.57.1`" + } + ] + } + }, + { + "version": "0.1.14", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.14", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.56.3` to `0.57.0`" + } + ] + } + }, + { + "version": "0.1.13", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.13", + "date": "Wed, 12 Jul 2023 15:20:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.56.2` to `0.56.3`" + } + ] + } + }, + { + "version": "0.1.12", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.12", + "date": "Fri, 07 Jul 2023 00:19:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.56.1` to `0.56.2`" + } + ] + } + }, + { + "version": "0.1.11", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.11", + "date": "Thu, 06 Jul 2023 00:16:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.56.0` to `0.56.1`" + } + ] + } + }, + { + "version": "0.1.10", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.10", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.55.2` to `0.56.0`" + } + ] + } + }, + { + "version": "0.1.9", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.9", + "date": "Thu, 15 Jun 2023 00:21:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.12.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.35.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.55.1` to `0.55.2`" + } + ] + } + }, + { + "version": "0.1.8", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.8", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.55.0` to `0.55.1`" + } + ] + } + }, + { + "version": "0.1.7", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.7", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.54.0` to `0.55.0`" + } + ] + } + }, + { + "version": "0.1.6", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.6", + "date": "Tue, 13 Jun 2023 01:49:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.35.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.53.1` to `0.54.0`" + } + ] + } + }, + { + "version": "0.1.5", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.5", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.53.0` to `0.53.1`" + } + ] + } + }, + { + "version": "0.1.4", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.4", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.52.2` to `0.53.0`" + } + ] + } + }, + { + "version": "0.1.3", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.3", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.52.1` to `0.52.2`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.2", + "date": "Thu, 08 Jun 2023 00:20:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.52.0` to `0.52.1`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/heft-api-extractor-plugin_v0.1.1", + "date": "Wed, 07 Jun 2023 22:45:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.12.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.35.2`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.51.0` to `0.52.0`" + } + ] + } + }, { "version": "0.1.0", "tag": "@rushstack/heft-api-extractor-plugin_v0.1.0", diff --git a/heft-plugins/heft-api-extractor-plugin/CHANGELOG.md b/heft-plugins/heft-api-extractor-plugin/CHANGELOG.md index 2d3ae7d262b..6a4ecf0c016 100644 --- a/heft-plugins/heft-api-extractor-plugin/CHANGELOG.md +++ b/heft-plugins/heft-api-extractor-plugin/CHANGELOG.md @@ -1,6 +1,870 @@ # Change Log - @rushstack/heft-api-extractor-plugin -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 1.3.22 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 1.3.21 +Fri, 17 Jul 2026 00:15:59 GMT + +_Version update only_ + +## 1.3.20 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.3.19 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 1.3.18 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 1.3.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.3.16 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.3.15 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 1.3.14 +Sat, 18 Apr 2026 00:15:16 GMT + +### Patches + +- Bump semver. + +## 1.3.13 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.3.12 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.3.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.3.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.3.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.3.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.3.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.3.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.3.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 1.3.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.3.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.3.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.3.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.3.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.2.10 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.2.9 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.2.8 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.2.7 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.2.6 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.2.5 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 1.2.4 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 1.2.3 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.2.2 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.2.1 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.2.0 +Tue, 04 Nov 2025 08:15:14 GMT + +### Minor changes + +- Include a `printApiReportDiff` option in the `config/api-extractor-task.json` config file that, when set to `"production"` (and the `--production` flag is specified) or `"always"`, causes a diff of the API report (*.api.md) to be printed if the report is changed. This is useful for diagnosing issues that only show up in CI. + +## 1.1.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.1.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.1.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 1.1.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.4.14 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.4.13 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.4.12 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.4.11 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.4.10 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 0.4.9 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.4.8 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.4.7 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.4.6 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.4.5 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.4.4 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.4.3 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.4.2 +Thu, 17 Apr 2025 00:11:21 GMT + +### Patches + +- Update documentation for `extends` + +## 0.4.1 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 0.4.0 +Wed, 09 Apr 2025 00:11:02 GMT + +### Minor changes + +- Use `tryLoadProjectConfigurationFileAsync` Heft API to remove direct dependency on `@rushstack/heft-config-file`. + +## 0.3.77 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.3.76 +Tue, 25 Mar 2025 15:11:16 GMT + +_Version update only_ + +## 0.3.75 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.3.74 +Wed, 12 Mar 2025 00:11:31 GMT + +_Version update only_ + +## 0.3.73 +Tue, 11 Mar 2025 02:12:33 GMT + +_Version update only_ + +## 0.3.72 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.3.71 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.3.70 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.3.69 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 0.3.68 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.3.67 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.3.66 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.3.65 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 0.3.64 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.3.63 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.3.62 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.3.61 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.3.60 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.3.59 +Tue, 03 Dec 2024 16:11:07 GMT + +_Version update only_ + +## 0.3.58 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.3.57 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.3.56 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.3.55 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.3.54 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.3.53 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.3.52 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.3.51 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.3.50 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.3.49 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.3.48 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.3.47 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.3.46 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.3.45 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.3.44 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.3.43 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 0.3.42 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 0.3.41 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.3.40 +Tue, 16 Jul 2024 00:36:21 GMT + +_Version update only_ + +## 0.3.39 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.3.38 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.3.37 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.3.36 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 0.3.35 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.3.34 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.3.33 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.3.32 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.3.31 +Fri, 24 May 2024 00:15:08 GMT + +_Version update only_ + +## 0.3.30 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 0.3.29 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.3.28 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.3.27 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.3.26 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.3.25 +Wed, 08 May 2024 22:23:50 GMT + +_Version update only_ + +## 0.3.24 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 0.3.23 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.3.22 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 0.3.21 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 0.3.20 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 0.3.19 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 0.3.18 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 0.3.17 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 0.3.16 +Thu, 29 Feb 2024 07:11:45 GMT + +_Version update only_ + +## 0.3.15 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.3.14 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.3.13 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.3.12 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.3.11 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.3.10 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.3.9 +Tue, 20 Feb 2024 16:10:53 GMT + +_Version update only_ + +## 0.3.8 +Mon, 19 Feb 2024 21:54:26 GMT + +_Version update only_ + +## 0.3.7 +Sat, 17 Feb 2024 06:24:35 GMT + +_Version update only_ + +## 0.3.6 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 0.3.5 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.3.4 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.3.3 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.3.2 +Tue, 23 Jan 2024 20:12:58 GMT + +_Version update only_ + +## 0.3.1 +Tue, 23 Jan 2024 16:15:05 GMT + +_Version update only_ + +## 0.3.0 +Tue, 16 Jan 2024 18:30:10 GMT + +### Minor changes + +- Add support for TypeScript 5.3 + +## 0.2.16 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.2.15 +Wed, 20 Dec 2023 01:09:46 GMT + +_Version update only_ + +## 0.2.14 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.2.13 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.2.12 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 0.2.11 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 0.2.10 +Mon, 30 Oct 2023 23:36:37 GMT + +_Version update only_ + +## 0.2.9 +Sun, 01 Oct 2023 02:56:30 GMT + +_Version update only_ + +## 0.2.8 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 0.2.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.2.6 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 0.2.5 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 0.2.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.2.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 0.2.2 +Fri, 22 Sep 2023 00:05:51 GMT + +_Version update only_ + +## 0.2.1 +Tue, 19 Sep 2023 15:21:51 GMT + +_Version update only_ + +## 0.2.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 +- Add "runInWatchMode" task configuration flag to support invocation when Heft is in watch mode. Does not currently offer any performance benefit. + +## 0.1.18 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 0.1.17 +Sat, 29 Jul 2023 00:22:51 GMT + +_Version update only_ + +## 0.1.16 +Thu, 20 Jul 2023 20:47:28 GMT + +_Version update only_ + +## 0.1.15 +Wed, 19 Jul 2023 00:20:31 GMT + +### Patches + +- Updated semver dependency + +## 0.1.14 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 0.1.13 +Wed, 12 Jul 2023 15:20:40 GMT + +_Version update only_ + +## 0.1.12 +Fri, 07 Jul 2023 00:19:33 GMT + +_Version update only_ + +## 0.1.11 +Thu, 06 Jul 2023 00:16:20 GMT + +_Version update only_ + +## 0.1.10 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 0.1.9 +Thu, 15 Jun 2023 00:21:02 GMT + +_Version update only_ + +## 0.1.8 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 0.1.7 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 0.1.6 +Tue, 13 Jun 2023 01:49:01 GMT + +_Version update only_ + +## 0.1.5 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 0.1.4 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 0.1.3 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 0.1.2 +Thu, 08 Jun 2023 00:20:03 GMT + +_Version update only_ + +## 0.1.1 +Wed, 07 Jun 2023 22:45:16 GMT + +_Version update only_ ## 0.1.0 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/heft-plugins/heft-api-extractor-plugin/config/heft.json b/heft-plugins/heft-api-extractor-plugin/config/heft.json new file mode 100644 index 00000000000..8d1359f022f --- /dev/null +++ b/heft-plugins/heft-api-extractor-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-api-extractor-plugin/config/rig.json b/heft-plugins/heft-api-extractor-plugin/config/rig.json index 6ac88a96368..cc98dea43dd 100644 --- a/heft-plugins/heft-api-extractor-plugin/config/rig.json +++ b/heft-plugins/heft-api-extractor-plugin/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "decoupled-local-node-rig" } diff --git a/heft-plugins/heft-api-extractor-plugin/eslint.config.js b/heft-plugins/heft-api-extractor-plugin/eslint.config.js new file mode 100644 index 00000000000..f83aea7d1b7 --- /dev/null +++ b/heft-plugins/heft-api-extractor-plugin/eslint.config.js @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); +const tsdocMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/tsdoc'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + ...tsdocMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-api-extractor-plugin/heft-plugin.json b/heft-plugins/heft-api-extractor-plugin/heft-plugin.json index 3a91b990cd1..54a11f5ea15 100644 --- a/heft-plugins/heft-api-extractor-plugin/heft-plugin.json +++ b/heft-plugins/heft-api-extractor-plugin/heft-plugin.json @@ -1,10 +1,10 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-plugin.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", "taskPlugins": [ { "pluginName": "api-extractor-plugin", - "entryPoint": "./lib/ApiExtractorPlugin" + "entryPoint": "./lib-commonjs/ApiExtractorPlugin" } ] } diff --git a/heft-plugins/heft-api-extractor-plugin/package.json b/heft-plugins/heft-api-extractor-plugin/package.json index 39ae1b18bd7..167c54004cb 100644 --- a/heft-plugins/heft-api-extractor-plugin/package.json +++ b/heft-plugins/heft-api-extractor-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/heft-api-extractor-plugin", - "version": "0.1.0", + "version": "1.3.22", "description": "A Heft plugin for API Extractor", "repository": { "type": "git", @@ -10,28 +10,42 @@ "homepage": "https://rushstack.io/pages/heft/overview/", "license": "MIT", "scripts": { - "build": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged build --clean", - "start": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged test --clean --watch", - "_phase:build": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged build --clean", - "_phase:test": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged test --no-build" + "build": "heft test --clean", + "start": "heft build-watch", + "_phase:build": "heft run --only build -- --clean" }, "peerDependencies": { - "@rushstack/heft": "0.51.0" + "@rushstack/heft": "1.2.22" }, "dependencies": { - "@rushstack/heft-config-file": "workspace:*", "@rushstack/node-core-library": "workspace:*", - "semver": "~7.3.0" + "semver": "~7.7.4" }, "devDependencies": { "@microsoft/api-extractor": "workspace:*", - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-legacy": "npm:@rushstack/heft@0.50.6", - "@rushstack/heft-node-rig": "1.13.0", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "@types/semver": "7.3.5", - "typescript": "~5.0.4" - } + "@rushstack/terminal": "workspace:*", + "@types/semver": "7.7.1", + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0" + }, + "exports": { + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "sideEffects": false } diff --git a/heft-plugins/heft-api-extractor-plugin/src/ApiExtractorPlugin.ts b/heft-plugins/heft-api-extractor-plugin/src/ApiExtractorPlugin.ts index 68418dc375f..8ab77d46808 100644 --- a/heft-plugins/heft-api-extractor-plugin/src/ApiExtractorPlugin.ts +++ b/heft-plugins/heft-api-extractor-plugin/src/ApiExtractorPlugin.ts @@ -6,22 +6,29 @@ import type { IHeftTaskPlugin, IHeftTaskRunHookOptions, IHeftTaskSession, - HeftConfiguration + HeftConfiguration, + IHeftTaskRunIncrementalHookOptions, + ConfigurationFile } from '@rushstack/heft'; -import { ConfigurationFile } from '@rushstack/heft-config-file'; -import { ApiExtractorRunner } from './ApiExtractorRunner'; +import { invokeApiExtractorAsync } from './ApiExtractorRunner'; +import apiExtractorConfigSchema from './schemas/api-extractor-task.schema.json'; // eslint-disable-next-line @rushstack/no-new-null const UNINITIALIZED: null = null; const PLUGIN_NAME: string = 'api-extractor-plugin'; -const TASK_CONFIG_SCHEMA_PATH: string = `${__dirname}/schemas/api-extractor-task.schema.json`; const TASK_CONFIG_RELATIVE_PATH: string = './config/api-extractor-task.json'; const EXTRACTOR_CONFIG_FILENAME: typeof TApiExtractor.ExtractorConfig.FILENAME = 'api-extractor.json'; const LEGACY_EXTRACTOR_CONFIG_RELATIVE_PATH: string = `./${EXTRACTOR_CONFIG_FILENAME}`; const EXTRACTOR_CONFIG_RELATIVE_PATH: string = `./config/${EXTRACTOR_CONFIG_FILENAME}`; +const API_EXTRACTOR_CONFIG_SPECIFICATION: ConfigurationFile.IProjectConfigurationFileSpecification = + { + projectRelativeFilePath: TASK_CONFIG_RELATIVE_PATH, + jsonSchemaObject: apiExtractorConfigSchema + }; + export interface IApiExtractorConfigurationResult { apiExtractorPackage: typeof TApiExtractor; apiExtractorConfiguration: TApiExtractor.ExtractorConfig; @@ -39,17 +46,31 @@ export interface IApiExtractorTaskConfiguration { * `IExtractorInvokeOptions.typescriptCompilerFolder` API option. This option defaults to false. */ useProjectTypescriptVersion?: boolean; + + /** + * If set to true, do a full run of api-extractor on every build. + */ + runInWatchMode?: boolean; + + /** + * Controls whether API Extractor prints a diff of the API report file if it's changed. + * If set to `"production"`, this will only be printed if Heft is run in `--production` + * mode, and if set to `"always"`, this will always be printed if the API report is changed. + * This corresponds to API Extractor's `IExtractorInvokeOptions.printApiReportDiff` API option. + * This option defaults to `"never"`. + */ + printApiReportDiff?: 'production' | 'always' | 'never'; } export default class ApiExtractorPlugin implements IHeftTaskPlugin { private _apiExtractor: typeof TApiExtractor | undefined; private _apiExtractorConfigurationFilePath: string | undefined | typeof UNINITIALIZED = UNINITIALIZED; - private _apiExtractorTaskConfigurationFileLoader: - | ConfigurationFile - | undefined; + private _printedWatchWarning: boolean = false; public apply(taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration): void { - taskSession.hooks.run.tapPromise(PLUGIN_NAME, async (runOptions: IHeftTaskRunHookOptions) => { + const runAsync = async ( + runOptions: IHeftTaskRunHookOptions & Partial + ): Promise => { const result: IApiExtractorConfigurationResult | undefined = await this._getApiExtractorConfigurationAsync(taskSession, heftConfiguration); if (result) { @@ -61,12 +82,10 @@ export default class ApiExtractorPlugin implements IHeftTaskPlugin { result.apiExtractorConfiguration ); } - }); + }; - taskSession.hooks.runIncremental.tapPromise(PLUGIN_NAME, async () => { - // Warn since don't need to run API Extractor when in watch mode. - taskSession.logger.terminal.writeWarningLine("API Extractor isn't currently supported in watch mode."); - }); + taskSession.hooks.run.tapPromise(PLUGIN_NAME, runAsync); + taskSession.hooks.runIncremental.tapPromise(PLUGIN_NAME, runAsync); } private async _getApiExtractorConfigurationFilePathAsync( @@ -144,58 +163,55 @@ export default class ApiExtractorPlugin implements IHeftTaskPlugin { return this._apiExtractor; } - private async _getApiExtractorTaskConfigurationAsync( - taskSession: IHeftTaskSession, - heftConfiguration: HeftConfiguration - ): Promise { - if (!this._apiExtractorTaskConfigurationFileLoader) { - this._apiExtractorTaskConfigurationFileLoader = new ConfigurationFile({ - projectRelativeFilePath: TASK_CONFIG_RELATIVE_PATH, - jsonSchemaPath: TASK_CONFIG_SCHEMA_PATH - }); - } - - return await this._apiExtractorTaskConfigurationFileLoader.tryLoadConfigurationFileForProjectAsync( - taskSession.logger.terminal, - heftConfiguration.buildFolderPath, - heftConfiguration.rigConfig - ); - } - private async _runApiExtractorAsync( taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration, - runOptions: IHeftTaskRunHookOptions, + runOptions: IHeftTaskRunHookOptions & Partial, apiExtractor: typeof TApiExtractor, apiExtractorConfiguration: TApiExtractor.ExtractorConfig ): Promise { - // TODO: Handle watch mode - // if (watchMode) { - // taskSession.logger.terminal.writeWarningLine("API Extractor isn't currently supported in --watch mode."); - // return; - // } - - const apiExtractorTaskConfiguration: IApiExtractorTaskConfiguration | undefined = - await this._getApiExtractorTaskConfigurationAsync(taskSession, heftConfiguration); + const { + runInWatchMode, + useProjectTypescriptVersion, + printApiReportDiff: printApiReportDiffOption + } = (await heftConfiguration.tryLoadProjectConfigurationFileAsync( + API_EXTRACTOR_CONFIG_SPECIFICATION, + taskSession.logger.terminal + )) ?? {}; + + if (runOptions.requestRun) { + if (!runInWatchMode) { + if (!this._printedWatchWarning) { + this._printedWatchWarning = true; + taskSession.logger.terminal.writeWarningLine( + "API Extractor isn't currently enabled in watch mode." + ); + } + return; + } + } let typescriptPackagePath: string | undefined; - if (apiExtractorTaskConfiguration?.useProjectTypescriptVersion) { + if (useProjectTypescriptVersion) { typescriptPackagePath = await heftConfiguration.rigPackageResolver.resolvePackageAsync( 'typescript', taskSession.logger.terminal ); } - const apiExtractorRunner: ApiExtractorRunner = new ApiExtractorRunner({ + const production: boolean = taskSession.parameters.production; + const printApiReportDiff: boolean = + printApiReportDiffOption === 'always' || (printApiReportDiffOption === 'production' && production); + + // Run API Extractor + await invokeApiExtractorAsync({ apiExtractor, apiExtractorConfiguration, typescriptPackagePath, buildFolder: heftConfiguration.buildFolderPath, - production: taskSession.parameters.production, - scopedLogger: taskSession.logger + production, + scopedLogger: taskSession.logger, + printApiReportDiff }); - - // Run API Extractor - await apiExtractorRunner.invokeAsync(); } } diff --git a/heft-plugins/heft-api-extractor-plugin/src/ApiExtractorRunner.ts b/heft-plugins/heft-api-extractor-plugin/src/ApiExtractorRunner.ts index 0ce811cf99e..ae5fa860b2d 100644 --- a/heft-plugins/heft-api-extractor-plugin/src/ApiExtractorRunner.ts +++ b/heft-plugins/heft-api-extractor-plugin/src/ApiExtractorRunner.ts @@ -2,8 +2,9 @@ // See LICENSE in the project root for license information. import * as semver from 'semver'; + import type { IScopedLogger } from '@rushstack/heft'; -import { type ITerminal, FileError, InternalError } from '@rushstack/node-core-library'; +import { FileError, InternalError } from '@rushstack/node-core-library'; import type * as TApiExtractor from '@microsoft/api-extractor'; export interface IApiExtractorRunnerConfiguration { @@ -18,7 +19,7 @@ export interface IApiExtractorRunnerConfiguration { apiExtractorConfiguration: TApiExtractor.ExtractorConfig; /** - * The imported @microsoft/api-extractor package + * The imported \@microsoft/api-extractor package */ apiExtractor: typeof TApiExtractor; @@ -38,104 +39,113 @@ export interface IApiExtractorRunnerConfiguration { * The scoped logger to use for logging */ scopedLogger: IScopedLogger; + + /** + * {@inheritdoc IApiExtractorTaskConfiguration.printApiReportDiff} + */ + printApiReportDiff: boolean | undefined; } const MIN_SUPPORTED_MAJOR_VERSION: number = 7; const MIN_SUPPORTED_MINOR_VERSION: number = 10; -export class ApiExtractorRunner { - private readonly _configuration: IApiExtractorRunnerConfiguration; - private readonly _scopedLogger: IScopedLogger; - private readonly _terminal: ITerminal; - private readonly _apiExtractor: typeof TApiExtractor; - - public constructor(configuration: IApiExtractorRunnerConfiguration) { - this._configuration = configuration; - this._apiExtractor = configuration.apiExtractor; - this._scopedLogger = configuration.scopedLogger; - this._terminal = configuration.scopedLogger.terminal; +export async function invokeApiExtractorAsync( + configuration: IApiExtractorRunnerConfiguration +): Promise { + const { + scopedLogger, + apiExtractor, + buildFolder, + production, + typescriptPackagePath, + apiExtractorConfiguration, + printApiReportDiff + } = configuration; + const { terminal } = scopedLogger; + + terminal.writeLine(`Using API Extractor version ${apiExtractor.Extractor.version}`); + + const apiExtractorVersion: semver.SemVer | null = semver.parse(apiExtractor.Extractor.version); + if ( + !apiExtractorVersion || + apiExtractorVersion.major < MIN_SUPPORTED_MAJOR_VERSION || + (apiExtractorVersion.major === MIN_SUPPORTED_MAJOR_VERSION && + apiExtractorVersion.minor < MIN_SUPPORTED_MINOR_VERSION) + ) { + scopedLogger.emitWarning(new Error(`Heft requires API Extractor version 7.10.0 or newer`)); } - public async invokeAsync(): Promise { - this._scopedLogger.terminal.writeLine( - `Using API Extractor version ${this._apiExtractor.Extractor.version}` - ); - - const apiExtractorVersion: semver.SemVer | null = semver.parse(this._apiExtractor.Extractor.version); - if ( - !apiExtractorVersion || - apiExtractorVersion.major < MIN_SUPPORTED_MAJOR_VERSION || - (apiExtractorVersion.major === MIN_SUPPORTED_MAJOR_VERSION && - apiExtractorVersion.minor < MIN_SUPPORTED_MINOR_VERSION) - ) { - this._scopedLogger.emitWarning(new Error(`Heft requires API Extractor version 7.10.0 or newer`)); - } - - const extractorConfig: TApiExtractor.ExtractorConfig = this._configuration.apiExtractorConfiguration; - const extractorOptions: TApiExtractor.IExtractorInvokeOptions = { - localBuild: !this._configuration.production, - typescriptCompilerFolder: this._configuration.typescriptPackagePath, - messageCallback: (message: TApiExtractor.ExtractorMessage) => { - switch (message.logLevel) { - case this._apiExtractor.ExtractorLogLevel.Error: - case this._apiExtractor.ExtractorLogLevel.Warning: { + const extractorOptions: TApiExtractor.IExtractorInvokeOptions = { + localBuild: !production, + typescriptCompilerFolder: typescriptPackagePath, + // Always show verbose messages - we'll decide what to do with them in the callback + showVerboseMessages: true, + printApiReportDiff, + messageCallback: (message: TApiExtractor.ExtractorMessage) => { + const { logLevel, sourceFilePath, messageId, text, sourceFileLine, sourceFileColumn } = message; + switch (logLevel) { + case apiExtractor.ExtractorLogLevel.Error: + case apiExtractor.ExtractorLogLevel.Warning: { + if (messageId === apiExtractor.ConsoleMessageId.ApiReportDiff) { + // Re-route this to the normal terminal output so it doesn't show up in the list of warnings/errors + terminal.writeLine(text); + } else { let errorToEmit: Error | undefined; - if (message.sourceFilePath) { - errorToEmit = new FileError(`(${message.messageId}) ${message.text}`, { - absolutePath: message.sourceFilePath, - projectFolder: this._configuration.buildFolder, - line: message.sourceFileLine, - column: message.sourceFileColumn + if (sourceFilePath) { + errorToEmit = new FileError(`(${messageId}) ${text}`, { + absolutePath: sourceFilePath, + projectFolder: buildFolder, + line: sourceFileLine, + column: sourceFileColumn }); } else { - errorToEmit = new Error(message.text); + errorToEmit = new Error(text); } - if (message.logLevel === this._apiExtractor.ExtractorLogLevel.Error) { - this._scopedLogger.emitError(errorToEmit); - } else if (message.logLevel === this._apiExtractor.ExtractorLogLevel.Warning) { - this._scopedLogger.emitWarning(errorToEmit); + if (logLevel === apiExtractor.ExtractorLogLevel.Error) { + scopedLogger.emitError(errorToEmit); + } else if (logLevel === apiExtractor.ExtractorLogLevel.Warning) { + scopedLogger.emitWarning(errorToEmit); } else { // Should never happen, but just in case - throw new InternalError(`Unexpected log level: ${message.logLevel}`); + throw new InternalError(`Unexpected log level: ${logLevel}`); } - break; } - case this._apiExtractor.ExtractorLogLevel.Verbose: { - this._terminal.writeVerboseLine(message.text); - break; - } + break; + } - case this._apiExtractor.ExtractorLogLevel.Info: { - this._terminal.writeLine(message.text); - break; - } + case apiExtractor.ExtractorLogLevel.Verbose: { + terminal.writeVerboseLine(text); + break; + } - case this._apiExtractor.ExtractorLogLevel.None: { - // Ignore messages with ExtractorLogLevel.None - break; - } + case apiExtractor.ExtractorLogLevel.Info: { + terminal.writeLine(text); + break; + } - default: - this._scopedLogger.emitError( - new Error(`Unexpected API Extractor log level: ${message.logLevel}`) - ); + case apiExtractor.ExtractorLogLevel.None: { + // Ignore messages with ExtractorLogLevel.None + break; } - message.handled = true; + default: + scopedLogger.emitError(new Error(`Unexpected API Extractor log level: ${logLevel}`)); } - }; - const apiExtractorResult: TApiExtractor.ExtractorResult = this._apiExtractor.Extractor.invoke( - extractorConfig, - extractorOptions - ); - - if (!apiExtractorResult.succeeded) { - this._scopedLogger.emitError(new Error('API Extractor failed.')); - } else if (apiExtractorResult.apiReportChanged && this._configuration.production) { - this._scopedLogger.emitError(new Error('API Report changed while in production mode.')); + message.handled = true; } + }; + + const apiExtractorResult: TApiExtractor.ExtractorResult = apiExtractor.Extractor.invoke( + apiExtractorConfiguration, + extractorOptions + ); + + if (!apiExtractorResult.succeeded) { + scopedLogger.emitError(new Error('API Extractor failed.')); + } else if (apiExtractorResult.apiReportChanged && production) { + scopedLogger.emitError(new Error('API Report changed while in production mode.')); } } diff --git a/heft-plugins/heft-api-extractor-plugin/src/schemas/api-extractor-task.schema.json b/heft-plugins/heft-api-extractor-plugin/src/schemas/api-extractor-task.schema.json index ba4768cbbc5..84e0fe3f689 100644 --- a/heft-plugins/heft-api-extractor-plugin/src/schemas/api-extractor-task.schema.json +++ b/heft-plugins/heft-api-extractor-plugin/src/schemas/api-extractor-task.schema.json @@ -13,13 +13,24 @@ }, "extends": { - "description": "Optionally specifies another JSON config file that this file extends from. This provides a way for standard settings to be shared across multiple projects.", + "description": "Optionally specifies another JSON config file that this file extends from. This provides a way for standard settings to be shared across multiple projects. To delete an inherited setting, set it to `null` in this file.", "type": "string" }, "useProjectTypescriptVersion": { "type": "boolean", "description": "If set to true, use the project's TypeScript compiler version for API Extractor's analysis. API Extractor's included TypeScript compiler can generally correctly analyze typings generated by older compilers, and referencing the project's compiler can cause issues. If issues are encountered with API Extractor's included compiler, set this option to true. This corresponds to API Extractor's --typescript-compiler-folder CLI option and IExtractorInvokeOptions.typescriptCompilerFolder API option. This option defaults to false." + }, + + "runInWatchMode": { + "type": "boolean", + "description": "If set to true, api-extractor will be run even in watch mode. This option defaults to false." + }, + + "printApiReportDiff": { + "type": "string", + "description": "Controls whether API Extractor prints a diff of the API report file if it's changed. If set to `\"production\"`, this will only be printed if Heft is run in `--production` mode, and if set to `\"always\"`, this will always be printed if the API report is changed. This corresponds to API Extractor's `IExtractorInvokeOptions.printApiReportDiff` API option. This option defaults to `\"never\"`.", + "enum": ["always", "production", "never"] } } } diff --git a/heft-plugins/heft-api-extractor-plugin/tsconfig.json b/heft-plugins/heft-api-extractor-plugin/tsconfig.json index 7512871fdbf..1a33d17b873 100644 --- a/heft-plugins/heft-api-extractor-plugin/tsconfig.json +++ b/heft-plugins/heft-api-extractor-plugin/tsconfig.json @@ -1,7 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["node"] - } + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/heft-plugins/heft-dev-cert-plugin/.eslintrc.js b/heft-plugins/heft-dev-cert-plugin/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/heft-plugins/heft-dev-cert-plugin/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/heft-plugins/heft-dev-cert-plugin/.npmignore b/heft-plugins/heft-dev-cert-plugin/.npmignore index 869bde13daf..f7a40e10213 100644 --- a/heft-plugins/heft-dev-cert-plugin/.npmignore +++ b/heft-plugins/heft-dev-cert-plugin/.npmignore @@ -8,24 +8,29 @@ !/lib/** !/lib-*/** !/dist/** -!ThirdPartyNotice.txt +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json !heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt # Ignore certain patterns that should not get published. /dist/*.stats.* /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-dev-cert-plugin/CHANGELOG.json b/heft-plugins/heft-dev-cert-plugin/CHANGELOG.json index 1e8c18237c4..91b87fa3e84 100644 --- a/heft-plugins/heft-dev-cert-plugin/CHANGELOG.json +++ b/heft-plugins/heft-dev-cert-plugin/CHANGELOG.json @@ -1,6 +1,3685 @@ { "name": "@rushstack/heft-dev-cert-plugin", "entries": [ + { + "version": "1.1.23", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.23", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.23`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "1.1.22", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.22", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.22`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "1.1.21", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.21", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.21`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "1.1.20", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.20", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.20`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "1.1.19", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.19", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.19`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "1.1.18", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.18", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.18`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "1.1.17", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.17", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.17`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "1.1.16", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.16", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.16`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "1.1.15", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.15", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.15`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "1.1.14", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.14", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.14`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "1.1.13", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.13", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "1.1.12", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.12", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.12`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "1.1.11", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.11", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.11`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "1.1.10", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.10", + "date": "Thu, 02 Apr 2026 00:14:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.10`" + } + ] + } + }, + { + "version": "1.1.9", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.9`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "1.1.8", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.8`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "1.1.7", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.7`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "1.1.6", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.6`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "1.1.5", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.5`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "1.1.4", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-dev-cert-plugin_v1.1.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "1.0.15", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.15", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.14`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "1.0.14", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.14", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.13`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "1.0.13", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.13", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.12`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "1.0.12", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.12", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.11`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "1.0.11", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.11", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.10`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "1.0.10", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.10", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.9`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "1.0.9", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.9", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.8`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "1.0.8", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.8", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.7`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "1.0.7", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.7", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.6`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.5` to `1.1.6`" + } + ] + } + }, + { + "version": "1.0.6", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.6", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.5`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.4` to `1.1.5`" + } + ] + } + }, + { + "version": "1.0.5", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.5", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.3` to `1.1.4`" + } + ] + } + }, + { + "version": "1.0.4", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.4", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.2` to `1.1.3`" + } + ] + } + }, + { + "version": "1.0.3", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.3", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.1` to `1.1.2`" + } + ] + } + }, + { + "version": "1.0.2", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.2", + "date": "Wed, 08 Oct 2025 00:13:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.0` to `1.1.1`" + } + ] + } + }, + { + "version": "1.0.1", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.1", + "date": "Fri, 03 Oct 2025 20:10:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.0.0` to `1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-dev-cert-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.9`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.75.0` to `1.0.0`" + } + ] + } + }, + { + "version": "0.4.113", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.113", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.8`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.5` to `0.75.0`" + } + ] + } + }, + { + "version": "0.4.112", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.112", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.7`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.4` to `0.74.5`" + } + ] + } + }, + { + "version": "0.4.111", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.111", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.6`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.3` to `0.74.4`" + } + ] + } + }, + { + "version": "0.4.110", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.110", + "date": "Fri, 29 Aug 2025 00:08:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.5`" + } + ] + } + }, + { + "version": "0.4.109", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.109", + "date": "Tue, 26 Aug 2025 00:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.4`" + } + ] + } + }, + { + "version": "0.4.108", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.108", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.2` to `0.74.3`" + } + ] + } + }, + { + "version": "0.4.107", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.107", + "date": "Fri, 01 Aug 2025 00:12:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.1` to `0.74.2`" + } + ] + } + }, + { + "version": "0.4.106", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.106", + "date": "Sat, 26 Jul 2025 00:12:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.1`" + } + ] + } + }, + { + "version": "0.4.105", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.105", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.0` to `0.74.1`" + } + ] + } + }, + { + "version": "0.4.104", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.104", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.6` to `0.74.0`" + } + ] + } + }, + { + "version": "0.4.103", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.103", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.36`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.5` to `0.73.6`" + } + ] + } + }, + { + "version": "0.4.102", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.102", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.35`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.4` to `0.73.5`" + } + ] + } + }, + { + "version": "0.4.101", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.101", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.34`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.3` to `0.73.4`" + } + ] + } + }, + { + "version": "0.4.100", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.100", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.2` to `0.73.3`" + } + ] + } + }, + { + "version": "0.4.99", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.99", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.32`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.1` to `0.73.2`" + } + ] + } + }, + { + "version": "0.4.98", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.98", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.31`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.0` to `0.73.1`" + } + ] + } + }, + { + "version": "0.4.97", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.97", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.72.0` to `0.73.0`" + } + ] + } + }, + { + "version": "0.4.96", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.96", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.2` to `0.72.0`" + } + ] + } + }, + { + "version": "0.4.95", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.95", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.28`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.1` to `0.71.2`" + } + ] + } + }, + { + "version": "0.4.94", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.94", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.27`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.0` to `0.71.1`" + } + ] + } + }, + { + "version": "0.4.93", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.93", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.1` to `0.71.0`" + } + ] + } + }, + { + "version": "0.4.92", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.92", + "date": "Wed, 12 Mar 2025 00:11:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.0` to `0.70.1`" + } + ] + } + }, + { + "version": "0.4.91", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.91", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.24`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.3` to `0.70.0`" + } + ] + } + }, + { + "version": "0.4.90", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.90", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.23`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.2` to `0.69.3`" + } + ] + } + }, + { + "version": "0.4.89", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.89", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.22`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.51.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.1` to `0.69.2`" + } + ] + } + }, + { + "version": "0.4.88", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.88", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.21`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.51.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.0` to `0.69.1`" + } + ] + } + }, + { + "version": "0.4.87", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.87", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.18` to `0.69.0`" + } + ] + } + }, + { + "version": "0.4.86", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.86", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.19`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.50.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.17` to `0.68.18`" + } + ] + } + }, + { + "version": "0.4.85", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.85", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.16` to `0.68.17`" + } + ] + } + }, + { + "version": "0.4.84", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.84", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.17`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.50.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.15` to `0.68.16`" + } + ] + } + }, + { + "version": "0.4.83", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.83", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.14` to `0.68.15`" + } + ] + } + }, + { + "version": "0.4.82", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.82", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.15`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.49.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.13` to `0.68.14`" + } + ] + } + }, + { + "version": "0.4.81", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.81", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.14`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.49.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.12` to `0.68.13`" + } + ] + } + }, + { + "version": "0.4.80", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.80", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.13`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.49.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.11` to `0.68.12`" + } + ] + } + }, + { + "version": "0.4.79", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.79", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.12`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.48.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.10` to `0.68.11`" + } + ] + } + }, + { + "version": "0.4.78", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.78", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.9` to `0.68.10`" + } + ] + } + }, + { + "version": "0.4.77", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.77", + "date": "Tue, 03 Dec 2024 16:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.8` to `0.68.9`" + } + ] + } + }, + { + "version": "0.4.76", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.76", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.9`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.48.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.7` to `0.68.8`" + } + ] + } + }, + { + "version": "0.4.75", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.75", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.8`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.6` to `0.68.7`" + } + ] + } + }, + { + "version": "0.4.74", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.74", + "date": "Thu, 24 Oct 2024 00:15:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.5` to `0.68.6`" + } + ] + } + }, + { + "version": "0.4.73", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.73", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.4` to `0.68.5`" + } + ] + } + }, + { + "version": "0.4.72", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.72", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.5`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.3` to `0.68.4`" + } + ] + } + }, + { + "version": "0.4.71", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.71", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.2` to `0.68.3`" + } + ] + } + }, + { + "version": "0.4.70", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.70", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.1` to `0.68.2`" + } + ] + } + }, + { + "version": "0.4.69", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.69", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.0` to `0.68.1`" + } + ] + } + }, + { + "version": "0.4.68", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.68", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.2` to `0.68.0`" + } + ] + } + }, + { + "version": "0.4.67", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.67", + "date": "Sat, 21 Sep 2024 00:10:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.0`" + } + ] + } + }, + { + "version": "0.4.66", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.66", + "date": "Fri, 13 Sep 2024 00:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.66`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.1` to `0.67.2`" + } + ] + } + }, + { + "version": "0.4.65", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.65", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.65`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.0` to `0.67.1`" + } + ] + } + }, + { + "version": "0.4.64", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.64", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.64`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.26` to `0.67.0`" + } + ] + } + }, + { + "version": "0.4.63", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.63", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.63`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.25` to `0.66.26`" + } + ] + } + }, + { + "version": "0.4.62", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.62", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.62`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.24` to `0.66.25`" + } + ] + } + }, + { + "version": "0.4.61", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.61", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.61`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.23` to `0.66.24`" + } + ] + } + }, + { + "version": "0.4.60", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.60", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.60`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.22` to `0.66.23`" + } + ] + } + }, + { + "version": "0.4.59", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.59", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.59`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.21` to `0.66.22`" + } + ] + } + }, + { + "version": "0.4.58", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.58", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.58`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.20` to `0.66.21`" + } + ] + } + }, + { + "version": "0.4.57", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.57", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.57`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.19` to `0.66.20`" + } + ] + } + }, + { + "version": "0.4.56", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.56", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.56`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.18` to `0.66.19`" + } + ] + } + }, + { + "version": "0.4.55", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.55", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.55`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.17` to `0.66.18`" + } + ] + } + }, + { + "version": "0.4.54", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.54", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.54`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.46.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.16` to `0.66.17`" + } + ] + } + }, + { + "version": "0.4.53", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.53", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.53`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.46.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.15` to `0.66.16`" + } + ] + } + }, + { + "version": "0.4.52", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.52", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.52`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.46.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.14` to `0.66.15`" + } + ] + } + }, + { + "version": "0.4.51", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.51", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.51`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.45.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.13` to `0.66.14`" + } + ] + } + }, + { + "version": "0.4.50", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.50", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.50`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.45.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.12` to `0.66.13`" + } + ] + } + }, + { + "version": "0.4.49", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.49", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.49`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.44.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.11` to `0.66.12`" + } + ] + } + }, + { + "version": "0.4.48", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.48", + "date": "Fri, 24 May 2024 00:15:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.48`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.44.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.10` to `0.66.11`" + } + ] + } + }, + { + "version": "0.4.47", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.47", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.47`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.9` to `0.66.10`" + } + ] + } + }, + { + "version": "0.4.46", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.46", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.46`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.8` to `0.66.9`" + } + ] + } + }, + { + "version": "0.4.45", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.45", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.45`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.7` to `0.66.8`" + } + ] + } + }, + { + "version": "0.4.44", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.44", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.44`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.6` to `0.66.7`" + } + ] + } + }, + { + "version": "0.4.43", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.43", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.43`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.5` to `0.66.6`" + } + ] + } + }, + { + "version": "0.4.42", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.42", + "date": "Wed, 08 May 2024 22:23:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.42`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.4` to `0.66.5`" + } + ] + } + }, + { + "version": "0.4.41", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.41", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.41`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.3` to `0.66.4`" + } + ] + } + }, + { + "version": "0.4.40", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.40", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.40`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.2` to `0.66.3`" + } + ] + } + }, + { + "version": "0.4.39", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.39", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.39`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.1` to `0.66.2`" + } + ] + } + }, + { + "version": "0.4.38", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.38", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.0` to `0.66.1`" + } + ] + } + }, + { + "version": "0.4.37", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.37", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.10` to `0.66.0`" + } + ] + } + }, + { + "version": "0.4.36", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.36", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.36`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.9` to `0.65.10`" + } + ] + } + }, + { + "version": "0.4.35", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.35", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.35`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.8` to `0.65.9`" + } + ] + } + }, + { + "version": "0.4.34", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.34", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.34`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.7` to `0.65.8`" + } + ] + } + }, + { + "version": "0.4.33", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.33", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.33`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.6` to `0.65.7`" + } + ] + } + }, + { + "version": "0.4.32", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.32", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.32`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.41.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.5` to `0.65.6`" + } + ] + } + }, + { + "version": "0.4.31", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.31", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.31`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.41.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.4` to `^0.65.5`" + } + ] + } + }, + { + "version": "0.4.30", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.30", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.3` to `^0.65.4`" + } + ] + } + }, + { + "version": "0.4.29", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.29", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.29`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.2` to `^0.65.3`" + } + ] + } + }, + { + "version": "0.4.28", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.28", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.28`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.1` to `^0.65.2`" + } + ] + } + }, + { + "version": "0.4.27", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.27", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.27`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.0` to `^0.65.1`" + } + ] + } + }, + { + "version": "0.4.26", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.26", + "date": "Tue, 20 Feb 2024 16:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.8` to `^0.65.0`" + } + ] + } + }, + { + "version": "0.4.25", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.25", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.25`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.7` to `^0.64.8`" + } + ] + } + }, + { + "version": "0.4.24", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.24", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.24`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.6` to `^0.64.7`" + } + ] + } + }, + { + "version": "0.4.23", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.23", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.23`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.5` to `^0.64.6`" + } + ] + } + }, + { + "version": "0.4.22", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.22", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.22`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.4` to `^0.64.5`" + } + ] + } + }, + { + "version": "0.4.21", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.21", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.21`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.3` to `^0.64.4`" + } + ] + } + }, + { + "version": "0.4.20", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.20", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.20`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.2` to `^0.64.3`" + } + ] + } + }, + { + "version": "0.4.19", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.19", + "date": "Tue, 23 Jan 2024 20:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.19`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.1` to `^0.64.2`" + } + ] + } + }, + { + "version": "0.4.18", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.18", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.18`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.0` to `^0.64.1`" + } + ] + } + }, + { + "version": "0.4.17", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.17", + "date": "Tue, 16 Jan 2024 18:30:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.6` to `^0.64.0`" + } + ] + } + }, + { + "version": "0.4.16", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.16", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.16`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.5` to `^0.63.6`" + } + ] + } + }, + { + "version": "0.4.15", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.15", + "date": "Wed, 20 Dec 2023 01:09:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.15`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.4` to `^0.63.5`" + } + ] + } + }, + { + "version": "0.4.14", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.14`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.3` to `^0.63.4`" + } + ] + } + }, + { + "version": "0.4.13", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.13`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.2` to `^0.63.3`" + } + ] + } + }, + { + "version": "0.4.12", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.12`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.1` to `^0.63.2`" + } + ] + } + }, + { + "version": "0.4.11", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.11`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.0` to `^0.63.1`" + } + ] + } + }, + { + "version": "0.4.10", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.10", + "date": "Mon, 30 Oct 2023 23:36:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.10`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.3` to `^0.63.0`" + } + ] + } + }, + { + "version": "0.4.9", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.9", + "date": "Sun, 01 Oct 2023 02:56:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.9`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.2` to `^0.62.3`" + } + ] + } + }, + { + "version": "0.4.8", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.8`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.1` to `^0.62.2`" + } + ] + } + }, + { + "version": "0.4.7", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.7`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.0` to `^0.62.1`" + } + ] + } + }, + { + "version": "0.4.6", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.6", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.3` to `^0.62.0`" + } + ] + } + }, + { + "version": "0.4.5", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.2` to `^0.61.3`" + } + ] + } + }, + { + "version": "0.4.4", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.1` to `^0.61.2`" + } + ] + } + }, + { + "version": "0.4.3", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.0` to `^0.61.1`" + } + ] + } + }, + { + "version": "0.4.2", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.2", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.60.0` to `^0.61.0`" + } + ] + } + }, + { + "version": "0.4.1", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.1", + "date": "Tue, 19 Sep 2023 15:21:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.59.0` to `^0.60.0`" + } + ] + } + }, + { + "version": "0.4.0", + "tag": "@rushstack/heft-dev-cert-plugin_v0.4.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.2` to `^0.59.0`" + } + ] + } + }, + { + "version": "0.3.26", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.26", + "date": "Wed, 13 Sep 2023 00:32:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.56`" + } + ] + } + }, + { + "version": "0.3.25", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.25", + "date": "Tue, 08 Aug 2023 07:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.55`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.1` to `^0.58.2`" + } + ] + } + }, + { + "version": "0.3.24", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.24", + "date": "Mon, 31 Jul 2023 15:19:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.54`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.21`" + } + ] + } + }, + { + "version": "0.3.23", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.23", + "date": "Sat, 29 Jul 2023 00:22:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.53`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.0` to `^0.58.1`" + } + ] + } + }, + { + "version": "0.3.22", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.22", + "date": "Thu, 20 Jul 2023 20:47:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.52`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.57.1` to `^0.58.0`" + } + ] + } + }, + { + "version": "0.3.21", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.21", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.51`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.57.0` to `^0.57.1`" + } + ] + } + }, + { + "version": "0.3.20", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.20", + "date": "Fri, 14 Jul 2023 15:20:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.50`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.17`" + } + ] + } + }, + { + "version": "0.3.19", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.19", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.49`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.3` to `^0.57.0`" + } + ] + } + }, + { + "version": "0.3.18", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.18", + "date": "Wed, 12 Jul 2023 15:20:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.48`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.2` to `^0.56.3`" + } + ] + } + }, + { + "version": "0.3.17", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.17", + "date": "Wed, 12 Jul 2023 00:23:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.47`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.14`" + } + ] + } + }, + { + "version": "0.3.16", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.16", + "date": "Fri, 07 Jul 2023 00:19:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.46`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.1` to `^0.56.2`" + } + ] + } + }, + { + "version": "0.3.15", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.15", + "date": "Thu, 06 Jul 2023 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.45`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.0` to `^0.56.1`" + } + ] + } + }, + { + "version": "0.3.14", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.14", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.44`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.11`" + } + ] + } + }, + { + "version": "0.3.13", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.13", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.43`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.2` to `^0.56.0`" + } + ] + } + }, + { + "version": "0.3.12", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.12", + "date": "Thu, 15 Jun 2023 00:21:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.42`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.35.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.1` to `^0.55.2`" + } + ] + } + }, + { + "version": "0.3.11", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.11", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.41`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.0` to `^0.55.1`" + } + ] + } + }, + { + "version": "0.3.10", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.10", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.40`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.54.0` to `^0.55.0`" + } + ] + } + }, + { + "version": "0.3.9", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.9", + "date": "Tue, 13 Jun 2023 01:49:01 GMT", + "comments": { + "patch": [ + { + "comment": "Bump webpack to v5.82.1" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.39`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.35.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.53.1` to `^0.54.0`" + } + ] + } + }, + { + "version": "0.3.8", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.8", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.53.0` to `^0.53.1`" + } + ] + } + }, + { + "version": "0.3.7", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.7", + "date": "Fri, 09 Jun 2023 15:23:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.4`" + } + ] + } + }, + { + "version": "0.3.6", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.6", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.2` to `^0.53.0`" + } + ] + } + }, + { + "version": "0.3.5", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.5", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.1` to `^0.52.2`" + } + ] + } + }, + { + "version": "0.3.4", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.4", + "date": "Thu, 08 Jun 2023 00:20:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.0` to `^0.52.1`" + } + ] + } + }, + { + "version": "0.3.3", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.3", + "date": "Wed, 07 Jun 2023 22:45:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.33`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.35.2`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.51.0` to `^0.52.0`" + } + ] + } + }, + { + "version": "0.3.2", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.2", + "date": "Tue, 06 Jun 2023 02:52:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.1.0`" + } + ] + } + }, + { + "version": "0.3.1", + "tag": "@rushstack/heft-dev-cert-plugin_v0.3.1", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.0.1`" + } + ] + } + }, { "version": "0.3.0", "tag": "@rushstack/heft-dev-cert-plugin_v0.3.0", diff --git a/heft-plugins/heft-dev-cert-plugin/CHANGELOG.md b/heft-plugins/heft-dev-cert-plugin/CHANGELOG.md index b0aed8df31a..26027383ff4 100644 --- a/heft-plugins/heft-dev-cert-plugin/CHANGELOG.md +++ b/heft-plugins/heft-dev-cert-plugin/CHANGELOG.md @@ -1,6 +1,922 @@ # Change Log - @rushstack/heft-dev-cert-plugin -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 1.1.23 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 1.1.22 +Fri, 17 Jul 2026 00:15:59 GMT + +_Version update only_ + +## 1.1.21 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.1.20 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 1.1.19 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 1.1.18 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.1.17 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.1.16 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 1.1.15 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 1.1.14 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.1.13 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.1.12 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.1.11 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.1.10 +Thu, 02 Apr 2026 00:14:38 GMT + +_Version update only_ + +## 1.1.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.1.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.1.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.1.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.1.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 1.1.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.1.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.1.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.1.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.1.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.0.15 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.0.14 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.0.13 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.0.12 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.0.11 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.0.10 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 1.0.9 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 1.0.8 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.0.7 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.0.6 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.0.5 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 1.0.4 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.0.3 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.0.2 +Wed, 08 Oct 2025 00:13:28 GMT + +_Version update only_ + +## 1.0.1 +Fri, 03 Oct 2025 20:10:00 GMT + +_Version update only_ + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.4.113 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.4.112 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.4.111 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.4.110 +Fri, 29 Aug 2025 00:08:01 GMT + +_Version update only_ + +## 0.4.109 +Tue, 26 Aug 2025 00:12:57 GMT + +_Version update only_ + +## 0.4.108 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.4.107 +Fri, 01 Aug 2025 00:12:48 GMT + +_Version update only_ + +## 0.4.106 +Sat, 26 Jul 2025 00:12:22 GMT + +_Version update only_ + +## 0.4.105 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.4.104 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.4.103 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.4.102 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.4.101 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.4.100 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.4.99 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.4.98 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.4.97 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 0.4.96 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 0.4.95 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.4.94 +Tue, 25 Mar 2025 15:11:15 GMT + +_Version update only_ + +## 0.4.93 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.4.92 +Wed, 12 Mar 2025 00:11:31 GMT + +_Version update only_ + +## 0.4.91 +Tue, 11 Mar 2025 02:12:33 GMT + +_Version update only_ + +## 0.4.90 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.4.89 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.4.88 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.4.87 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 0.4.86 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.4.85 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.4.84 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.4.83 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 0.4.82 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.4.81 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.4.80 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.4.79 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.4.78 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.4.77 +Tue, 03 Dec 2024 16:11:07 GMT + +_Version update only_ + +## 0.4.76 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.4.75 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.4.74 +Thu, 24 Oct 2024 00:15:47 GMT + +_Version update only_ + +## 0.4.73 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.4.72 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.4.71 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.4.70 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.4.69 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.4.68 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.4.67 +Sat, 21 Sep 2024 00:10:27 GMT + +_Version update only_ + +## 0.4.66 +Fri, 13 Sep 2024 00:11:42 GMT + +_Version update only_ + +## 0.4.65 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.4.64 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.4.63 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.4.62 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.4.61 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.4.60 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 0.4.59 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 0.4.58 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.4.57 +Tue, 16 Jul 2024 00:36:21 GMT + +_Version update only_ + +## 0.4.56 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.4.55 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.4.54 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.4.53 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 0.4.52 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.4.51 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.4.50 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.4.49 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.4.48 +Fri, 24 May 2024 00:15:08 GMT + +_Version update only_ + +## 0.4.47 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 0.4.46 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.4.45 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.4.44 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.4.43 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.4.42 +Wed, 08 May 2024 22:23:50 GMT + +_Version update only_ + +## 0.4.41 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 0.4.40 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.4.39 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 0.4.38 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 0.4.37 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 0.4.36 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 0.4.35 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 0.4.34 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 0.4.33 +Thu, 29 Feb 2024 07:11:45 GMT + +_Version update only_ + +## 0.4.32 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.4.31 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.4.30 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.4.29 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.4.28 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.4.27 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.4.26 +Tue, 20 Feb 2024 16:10:52 GMT + +_Version update only_ + +## 0.4.25 +Mon, 19 Feb 2024 21:54:27 GMT + +_Version update only_ + +## 0.4.24 +Sat, 17 Feb 2024 06:24:35 GMT + +_Version update only_ + +## 0.4.23 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 0.4.22 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.4.21 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.4.20 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.4.19 +Tue, 23 Jan 2024 20:12:57 GMT + +_Version update only_ + +## 0.4.18 +Tue, 23 Jan 2024 16:15:05 GMT + +_Version update only_ + +## 0.4.17 +Tue, 16 Jan 2024 18:30:11 GMT + +_Version update only_ + +## 0.4.16 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.4.15 +Wed, 20 Dec 2023 01:09:45 GMT + +_Version update only_ + +## 0.4.14 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.4.13 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.4.12 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 0.4.11 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 0.4.10 +Mon, 30 Oct 2023 23:36:37 GMT + +_Version update only_ + +## 0.4.9 +Sun, 01 Oct 2023 02:56:29 GMT + +_Version update only_ + +## 0.4.8 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 0.4.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.4.6 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 0.4.5 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 0.4.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.4.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 0.4.2 +Fri, 22 Sep 2023 00:05:50 GMT + +_Version update only_ + +## 0.4.1 +Tue, 19 Sep 2023 15:21:52 GMT + +_Version update only_ + +## 0.4.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 0.3.26 +Wed, 13 Sep 2023 00:32:29 GMT + +_Version update only_ + +## 0.3.25 +Tue, 08 Aug 2023 07:10:39 GMT + +_Version update only_ + +## 0.3.24 +Mon, 31 Jul 2023 15:19:05 GMT + +_Version update only_ + +## 0.3.23 +Sat, 29 Jul 2023 00:22:50 GMT + +_Version update only_ + +## 0.3.22 +Thu, 20 Jul 2023 20:47:28 GMT + +_Version update only_ + +## 0.3.21 +Wed, 19 Jul 2023 00:20:31 GMT + +_Version update only_ + +## 0.3.20 +Fri, 14 Jul 2023 15:20:45 GMT + +_Version update only_ + +## 0.3.19 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 0.3.18 +Wed, 12 Jul 2023 15:20:39 GMT + +_Version update only_ + +## 0.3.17 +Wed, 12 Jul 2023 00:23:29 GMT + +_Version update only_ + +## 0.3.16 +Fri, 07 Jul 2023 00:19:32 GMT + +_Version update only_ + +## 0.3.15 +Thu, 06 Jul 2023 00:16:19 GMT + +_Version update only_ + +## 0.3.14 +Tue, 04 Jul 2023 00:18:47 GMT + +_Version update only_ + +## 0.3.13 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 0.3.12 +Thu, 15 Jun 2023 00:21:01 GMT + +_Version update only_ + +## 0.3.11 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 0.3.10 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 0.3.9 +Tue, 13 Jun 2023 01:49:01 GMT + +### Patches + +- Bump webpack to v5.82.1 + +## 0.3.8 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 0.3.7 +Fri, 09 Jun 2023 15:23:15 GMT + +_Version update only_ + +## 0.3.6 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 0.3.5 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 0.3.4 +Thu, 08 Jun 2023 00:20:02 GMT + +_Version update only_ + +## 0.3.3 +Wed, 07 Jun 2023 22:45:17 GMT + +_Version update only_ + +## 0.3.2 +Tue, 06 Jun 2023 02:52:51 GMT + +_Version update only_ + +## 0.3.1 +Mon, 05 Jun 2023 21:45:21 GMT + +_Version update only_ ## 0.3.0 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/heft-plugins/heft-dev-cert-plugin/config/rig.json b/heft-plugins/heft-dev-cert-plugin/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/heft-plugins/heft-dev-cert-plugin/config/rig.json +++ b/heft-plugins/heft-dev-cert-plugin/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/heft-plugins/heft-dev-cert-plugin/eslint.config.js b/heft-plugins/heft-dev-cert-plugin/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/heft-plugins/heft-dev-cert-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-dev-cert-plugin/heft-plugin.json b/heft-plugins/heft-dev-cert-plugin/heft-plugin.json index 444fbcce5e6..c8e7e137a5d 100644 --- a/heft-plugins/heft-dev-cert-plugin/heft-plugin.json +++ b/heft-plugins/heft-dev-cert-plugin/heft-plugin.json @@ -1,16 +1,16 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-plugin.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", "lifecyclePlugins": [], "taskPlugins": [ { "pluginName": "trust-dev-certificate-plugin", - "entryPoint": "./lib/TrustDevCertificatePlugin" + "entryPoint": "./lib-commonjs/TrustDevCertificatePlugin" }, { "pluginName": "untrust-dev-certificate-plugin", - "entryPoint": "./lib/UntrustDevCertificatePlugin" + "entryPoint": "./lib-commonjs/UntrustDevCertificatePlugin" } ] } diff --git a/heft-plugins/heft-dev-cert-plugin/package.json b/heft-plugins/heft-dev-cert-plugin/package.json index 0a0847588c2..11c160466b7 100644 --- a/heft-plugins/heft-dev-cert-plugin/package.json +++ b/heft-plugins/heft-dev-cert-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/heft-dev-cert-plugin", - "version": "0.3.0", + "version": "1.1.23", "description": "A Heft plugin for generating and using local development certificates", "repository": { "type": "git", @@ -16,18 +16,33 @@ "_phase:test": "heft run --only test -- --clean" }, "peerDependencies": { - "@rushstack/heft": "^0.51.0" + "@rushstack/heft": "^1.2.22" }, "dependencies": { "@rushstack/debug-certificate-manager": "workspace:*" }, "devDependencies": { "@microsoft/api-extractor": "workspace:*", - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", "@rushstack/heft": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "eslint": "~8.7.0" - } + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "sideEffects": false } diff --git a/heft-plugins/heft-dev-cert-plugin/tsconfig.json b/heft-plugins/heft-dev-cert-plugin/tsconfig.json index 7512871fdbf..dac21d04081 100644 --- a/heft-plugins/heft-dev-cert-plugin/tsconfig.json +++ b/heft-plugins/heft-dev-cert-plugin/tsconfig.json @@ -1,7 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["node"] - } + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/.npmignore b/heft-plugins/heft-isolated-typescript-transpile-plugin/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/CHANGELOG.json b/heft-plugins/heft-isolated-typescript-transpile-plugin/CHANGELOG.json new file mode 100644 index 00000000000..4a4a7111bfa --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/CHANGELOG.json @@ -0,0 +1,1526 @@ +{ + "name": "@rushstack/heft-isolated-typescript-transpile-plugin", + "entries": [ + { + "version": "1.2.22", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.22", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.21` to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.17`" + } + ] + } + }, + { + "version": "1.2.21", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.21", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.10`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.20` to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.16`" + } + ] + } + }, + { + "version": "1.2.20", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.20", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.19` to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.15`" + } + ] + } + }, + { + "version": "1.2.19", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.19", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.18` to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.14`" + } + ] + } + }, + { + "version": "1.2.18", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.18", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.17` to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.13`" + } + ] + } + }, + { + "version": "1.2.17", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.16` to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.12`" + } + ] + } + }, + { + "version": "1.2.16", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.16", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.15` to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.11`" + } + ] + } + }, + { + "version": "1.2.15", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.15", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.14` to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.10`" + } + ] + } + }, + { + "version": "1.2.14", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.14", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.13` to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.9`" + } + ] + } + }, + { + "version": "1.2.13", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.12` to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.8`" + } + ] + } + }, + { + "version": "1.2.12", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.12", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.11` to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.7`" + } + ] + } + }, + { + "version": "1.2.11", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.10` to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.6`" + } + ] + } + }, + { + "version": "1.2.10", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.9` to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.5`" + } + ] + } + }, + { + "version": "1.2.9", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.8` to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.4`" + } + ] + } + }, + { + "version": "1.2.8", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.7` to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.3`" + } + ] + } + }, + { + "version": "1.2.7", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.6` to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.2`" + } + ] + } + }, + { + "version": "1.2.6", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.5` to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.1`" + } + ] + } + }, + { + "version": "1.2.5", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.4` to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.3.0`" + } + ] + } + }, + { + "version": "1.2.4", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.3` to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.2.4`" + } + ] + } + }, + { + "version": "1.2.3", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.2` to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.2.3`" + } + ] + } + }, + { + "version": "1.2.2", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.1` to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.2.2`" + } + ] + } + }, + { + "version": "1.2.1", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.0` to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.2.1`" + } + ] + } + }, + { + "version": "1.2.0", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.2.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.14` to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.2.0`" + } + ] + } + }, + { + "version": "1.1.14", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.13` to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.14`" + } + ] + } + }, + { + "version": "1.1.13", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.12` to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.13`" + } + ] + } + }, + { + "version": "1.1.12", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.11` to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.12`" + } + ] + } + }, + { + "version": "1.1.11", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.10` to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.11`" + } + ] + } + }, + { + "version": "1.1.10", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.10", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.9` to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.10`" + } + ] + } + }, + { + "version": "1.1.9", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.9", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.8` to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.9`" + } + ] + } + }, + { + "version": "1.1.8", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.8", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.7` to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.8`" + } + ] + } + }, + { + "version": "1.1.7", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.6` to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.7`" + } + ] + } + }, + { + "version": "1.1.6", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.6", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.5` to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.6`" + } + ] + } + }, + { + "version": "1.1.5", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.4` to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.5`" + } + ] + } + }, + { + "version": "1.1.4", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.4", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.3` to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.4`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.2` to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.3`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.1` to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.2`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.0` to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.1.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.0.0` to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.75.0` to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^1.0.0`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.2.5", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.5` to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.15`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.2.4", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.7.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.4` to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.14`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.2.3", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.7.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.3` to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.13`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.2.2", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "patch": [ + { + "comment": "Manually process wildcard directories for watching instead of watching all read directories." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.7.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.2` to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.12`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.2.1", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.1` to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.11`" + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.2.0", + "date": "Mon, 28 Jul 2025 15:11:56 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for watch mode." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.10`" + } + ] + } + }, + { + "version": "0.1.16", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.16", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.7.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.0` to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.9`" + } + ] + } + }, + { + "version": "0.1.15", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.15", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.7.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.6` to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.8`" + } + ] + } + }, + { + "version": "0.1.14", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.14", + "date": "Tue, 13 May 2025 20:32:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.7.0`" + } + ] + } + }, + { + "version": "0.1.13", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.13", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.6.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.5` to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.7`" + } + ] + } + }, + { + "version": "0.1.12", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.12", + "date": "Thu, 08 May 2025 00:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.6.0`" + } + ] + } + }, + { + "version": "0.1.11", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.11", + "date": "Tue, 06 May 2025 15:11:28 GMT", + "comments": { + "patch": [ + { + "comment": "Fix source map comment in emitted files. Fix processing of \"outDir\" field to allow normal relative path formats (\"./lib\", or \"lib\" as opposed to \"/lib\")." + } + ] + } + }, + { + "version": "0.1.10", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.10", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.5.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.4` to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.6`" + } + ] + } + }, + { + "version": "0.1.9", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.9", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.5.22`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.3` to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.5`" + } + ] + } + }, + { + "version": "0.1.8", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.8", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.5.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.2` to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.4`" + } + ] + } + }, + { + "version": "0.1.7", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.7", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.5.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.1` to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.3`" + } + ] + } + }, + { + "version": "0.1.6", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.6", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.5.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.0` to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.2`" + } + ] + } + }, + { + "version": "0.1.5", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.5", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.5.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.72.0` to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.1`" + } + ] + } + }, + { + "version": "0.1.4", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.4", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.5.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.2` to `0.72.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.9.0`" + } + ] + } + }, + { + "version": "0.1.3", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.3", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.5.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.8.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.1` to `0.71.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.8.2`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.2", + "date": "Tue, 25 Mar 2025 15:11:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/lookup-by-path\" to `0.5.15`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.0` to `0.71.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `^0.8.1`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.1", + "date": "Fri, 14 Mar 2025 03:43:40 GMT", + "comments": { + "patch": [ + { + "comment": "Fix a casing issue in the `heft-plugin.json` `entryPoint` field." + }, + { + "comment": "Fix an issue where the `rootPath` `tsconfig.json` property wasn't supported." + }, + { + "comment": "Fix a crash when there are zero files to transpile." + }, + { + "comment": "Fix an issue with the paths in sourcemaps." + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/heft-isolated-typescript-transpile-plugin_v0.1.0", + "date": "Wed, 12 Mar 2025 23:12:56 GMT", + "comments": { + "minor": [ + { + "comment": "Initial release." + } + ] + } + } + ] +} diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/CHANGELOG.md b/heft-plugins/heft-isolated-typescript-transpile-plugin/CHANGELOG.md new file mode 100644 index 00000000000..154a655928f --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/CHANGELOG.md @@ -0,0 +1,335 @@ +# Change Log - @rushstack/heft-isolated-typescript-transpile-plugin + +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 1.2.22 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 1.2.21 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 1.2.20 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.2.19 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 1.2.18 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 1.2.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.2.16 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.2.15 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 1.2.14 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 1.2.13 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.2.12 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.2.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.2.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.2.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.2.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.2.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.2.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.2.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 1.2.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.2.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.2.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.2.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.2.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.1.14 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.1.13 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.1.12 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.1.11 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.1.10 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.1.9 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 1.1.8 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 1.1.7 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.1.6 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.1.5 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.1.4 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 1.1.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.1.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.1.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 1.1.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.2.5 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.2.4 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.2.3 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.2.2 +Tue, 19 Aug 2025 20:45:02 GMT + +### Patches + +- Manually process wildcard directories for watching instead of watching all read directories. + +## 0.2.1 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 0.2.0 +Mon, 28 Jul 2025 15:11:56 GMT + +### Minor changes + +- Add support for watch mode. + +## 0.1.16 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.1.15 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.1.14 +Tue, 13 May 2025 20:32:55 GMT + +_Version update only_ + +## 0.1.13 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.1.12 +Thu, 08 May 2025 00:11:15 GMT + +_Version update only_ + +## 0.1.11 +Tue, 06 May 2025 15:11:28 GMT + +### Patches + +- Fix source map comment in emitted files. Fix processing of "outDir" field to allow normal relative path formats ("./lib", or "lib" as opposed to "/lib"). + +## 0.1.10 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.1.9 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.1.8 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.1.7 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.1.6 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.1.5 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 0.1.4 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 0.1.3 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.1.2 +Tue, 25 Mar 2025 15:11:16 GMT + +_Version update only_ + +## 0.1.1 +Fri, 14 Mar 2025 03:43:40 GMT + +### Patches + +- Fix a casing issue in the `heft-plugin.json` `entryPoint` field. +- Fix an issue where the `rootPath` `tsconfig.json` property wasn't supported. +- Fix a crash when there are zero files to transpile. +- Fix an issue with the paths in sourcemaps. + +## 0.1.0 +Wed, 12 Mar 2025 23:12:56 GMT + +### Minor changes + +- Initial release. + diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/LICENSE b/heft-plugins/heft-isolated-typescript-transpile-plugin/LICENSE new file mode 100644 index 00000000000..f071d3e89e3 --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/LICENSE @@ -0,0 +1,24 @@ +@rushstack/heft-isolated-typescript-transpile-plugin + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/README.md b/heft-plugins/heft-isolated-typescript-transpile-plugin/README.md new file mode 100644 index 00000000000..4d049da7f89 --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/README.md @@ -0,0 +1,12 @@ +# @rushstack/heft-isolated-typescript-transpile-plugin + +This is a Heft plugin for using swc as an isolated module transpiler +during the "build" stage. + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/heft-plugins/heft-isolated-typescript-transpile-plugin/CHANGELOG.md) - Find + out what's new in the latest version + +Heft is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/config/api-extractor.json b/heft-plugins/heft-isolated-typescript-transpile-plugin/config/api-extractor.json new file mode 100644 index 00000000000..5f6b2655ac8 --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/config/api-extractor.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json", + + "docModel": { + "enabled": false + }, + + "dtsRollup": { + "enabled": true, + "betaTrimmedFilePath": "/dist/.d.ts" + } +} diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/config/heft.json b/heft-plugins/heft-isolated-typescript-transpile-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/config/rig.json b/heft-plugins/heft-isolated-typescript-transpile-plugin/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/eslint.config.js b/heft-plugins/heft-isolated-typescript-transpile-plugin/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/heft-plugin.json b/heft-plugins/heft-isolated-typescript-transpile-plugin/heft-plugin.json new file mode 100644 index 00000000000..e94bf9c0815 --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/heft-plugin.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", + + "taskPlugins": [ + { + "pluginName": "swc-isolated-transpile-plugin", + "entryPoint": "./lib-commonjs/SwcIsolatedTranspilePlugin", + "optionsSchema": "./lib-commonjs/schemas/swc-isolated-transpile-plugin.schema.json" + } + ] +} diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/package.json b/heft-plugins/heft-isolated-typescript-transpile-plugin/package.json new file mode 100644 index 00000000000..d243ebea815 --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/package.json @@ -0,0 +1,63 @@ +{ + "name": "@rushstack/heft-isolated-typescript-transpile-plugin", + "version": "1.2.22", + "description": "Heft plugin for transpiling TypeScript with SWC", + "repository": { + "type": "git", + "url": "https://github.com/microsoft/rushstack.git", + "directory": "heft-plugins/heft-isolated-typescript-transpile-plugin" + }, + "homepage": "https://rushstack.io/pages/heft/overview/", + "license": "MIT", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/heft-isolated-typescript-transpile-plugin.d.ts", + "exports": { + ".": { + "types": "./dist/heft-isolated-typescript-transpile-plugin.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "scripts": { + "build": "heft build --clean", + "start": "heft test --clean --watch", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "peerDependencies": { + "@rushstack/heft": "^1.2.22", + "@rushstack/heft-typescript-plugin": "workspace:^" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "@rushstack/heft-typescript-plugin": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "dependencies": { + "@rushstack/lookup-by-path": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "@swc/core": "1.7.10", + "@types/tapable": "1.0.6", + "tapable": "1.1.3" + }, + "sideEffects": false +} diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/src/SwcIsolatedTranspilePlugin.ts b/heft-plugins/heft-isolated-typescript-transpile-plugin/src/SwcIsolatedTranspilePlugin.ts new file mode 100644 index 00000000000..2dd940fc342 --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/src/SwcIsolatedTranspilePlugin.ts @@ -0,0 +1,486 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { Dirent } from 'node:fs'; +import path from 'node:path'; +import { type ChildProcess, fork } from 'node:child_process'; + +import type { + Config, + JscTarget, + ModuleConfig, + Options as SwcOptions, + ParserConfig, + ReactConfig, + TransformConfig +} from '@swc/core'; +import { SyncWaterfallHook } from 'tapable'; + +import { Async, Path } from '@rushstack/node-core-library'; +import type { + HeftConfiguration, + IHeftTaskPlugin, + IHeftTaskSession, + IScopedLogger, + IWatchFileSystem, + IWatchedFileState +} from '@rushstack/heft'; +import { LookupByPath } from '@rushstack/lookup-by-path'; +import { + _loadTypeScriptToolAsync as loadTypeScriptToolAsync, + _loadTsconfig as loadTsconfig, + type _TTypeScript as TTypeScript, + _getTsconfigFilePath as getTsconfigFilePath +} from '@rushstack/heft-typescript-plugin'; + +import type { + ISwcIsolatedTranspileOptions, + IWorkerResult, + ITransformTask, + IEmitKind, + ITransformModulesRequestMessage +} from './types'; + +/** + * @public + */ +export type ModuleKind = keyof typeof TTypeScript.ModuleKind; + +const TSC_TO_SWC_MODULE_MAP: Record = { + CommonJS: 'commonjs', + ES2015: 'es6', + ES2020: 'es6', + ES2022: 'es6', + ESNext: 'es6', + Node16: 'nodenext', + Node18: 'nodenext', + NodeNext: 'nodenext', + AMD: 'amd', + None: undefined, + UMD: 'umd', + System: undefined, + Preserve: undefined +}; + +/** + * @public + */ +export type ScriptTarget = keyof typeof TTypeScript.ScriptTarget; + +const TSC_TO_SWC_TARGET_MAP: Record = { + ES2015: 'es2015', + ES2016: 'es2016', + ES2017: 'es2017', + ES2018: 'es2018', + ES2019: 'es2019', + ES2020: 'es2020', + ES2021: 'es2021', + ES2022: 'es2022', + ES2023: 'es2023', + ES2024: 'es2024', + ESNext: 'esnext', + Latest: 'esnext', + ES5: 'es5', + ES3: 'es3', + JSON: undefined +}; + +const PLUGIN_NAME: 'swc-isolated-transpile-plugin' = 'swc-isolated-transpile-plugin'; + +/** + * @beta + */ +export interface ISwcIsolatedTranspilePluginAccessor { + hooks: { + /** + * This hook will get called for each module kind and script target that that will be emitted. + * + * @internalRemarks + * In the future, consider replacing this with a HookMap. + */ + getSwcOptions: SyncWaterfallHook; + }; +} + +/** + * @public + */ +export default class SwcIsolatedTranspilePlugin implements IHeftTaskPlugin { + /** + * @beta + */ + public accessor: ISwcIsolatedTranspilePluginAccessor; + + public constructor() { + this.accessor = { + hooks: { + getSwcOptions: new SyncWaterfallHook(['swcOptions', 'format', 'target']) + } + }; + } + + public apply( + heftSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + pluginOptions: ISwcIsolatedTranspileOptions = {} + ): void { + heftSession.hooks.run.tapPromise(PLUGIN_NAME, async () => { + const { logger } = heftSession; + + await transpileProjectAsync(heftConfiguration, pluginOptions, logger, this.accessor); + }); + + heftSession.hooks.runIncremental.tapPromise(PLUGIN_NAME, async (incrementalOptions) => { + const { logger } = heftSession; + + await transpileProjectAsync( + heftConfiguration, + pluginOptions, + logger, + this.accessor, + () => incrementalOptions.watchFs + ); + }); + } +} + +async function transpileProjectAsync( + heftConfiguration: HeftConfiguration, + pluginOptions: ISwcIsolatedTranspileOptions, + logger: IScopedLogger, + { hooks: { getSwcOptions: getSwcOptionsHook } }: ISwcIsolatedTranspilePluginAccessor, + getWatchFs?: (() => IWatchFileSystem) | undefined +): Promise { + const { buildFolderPath } = heftConfiguration; + const { emitKinds = [] } = pluginOptions; + + const { tool } = await loadTypeScriptToolAsync({ + terminal: logger.terminal, + heftConfiguration + }); + const { ts } = tool; + + const tsconfigPath: string = getTsconfigFilePath(heftConfiguration, pluginOptions.tsConfigPath); + const parsedTsConfig: TTypeScript.ParsedCommandLine | undefined = loadTsconfig({ tool, tsconfigPath }); + + if (!parsedTsConfig) { + logger.terminal.writeLine('tsconfig.json not found. Skipping parse and transpile for this project.'); + return; + } + + if (getWatchFs && parsedTsConfig.wildcardDirectories) { + // If the tsconfig has wildcard directories, we need to ensure that they are watched for file changes. + const directoryQueue: Map = new Map(); + for (const [wildcardDirectory, type] of Object.entries(parsedTsConfig.wildcardDirectories)) { + directoryQueue.set(path.normalize(wildcardDirectory), type === ts.WatchDirectoryFlags.Recursive); + } + + if (directoryQueue.size > 0) { + const watchFs: IWatchFileSystem = getWatchFs(); + + for (const [wildcardDirectory, isRecursive] of directoryQueue) { + const dirents: Dirent[] = watchFs.readdirSync(wildcardDirectory, { + withFileTypes: true + }); + + if (isRecursive) { + for (const dirent of dirents) { + if (dirent.isDirectory()) { + // Using path.join because we want platform-normalized paths. + const absoluteDirentPath: string = path.join(wildcardDirectory, dirent.name); + directoryQueue.set(absoluteDirentPath, true); + } + } + } + } + + logger.terminal.writeDebugLine(`Watching for changes in ${directoryQueue.size} directories`); + } + } + + if (emitKinds.length < 1) { + throw new Error( + 'One or more emit kinds must be specified in the plugin options. To disable SWC transpilation, ' + + 'point "tsConfigPath" at a nonexistent file.' + ); + } + + logger.terminal.writeDebugLine('Loaded tsconfig', JSON.stringify(parsedTsConfig, undefined, 2)); + + const { fileNames: filesFromTsConfig, options: tsConfigOptions } = parsedTsConfig; + const { sourceMap, sourceRoot, experimentalDecorators, inlineSourceMap, useDefineForClassFields } = + tsConfigOptions; + + const rootDirs: Set = new Set(tsConfigOptions.rootDirs); + if (tsConfigOptions.rootDir) { + rootDirs.add(tsConfigOptions.rootDir); + } + + const rootDirsPaths: LookupByPath = new LookupByPath(); + for (const rootDir of rootDirs) { + rootDirsPaths.setItem(rootDir, rootDir.length); + } + + const sourceFilePaths: string[] = filesFromTsConfig.filter((filePath) => !filePath.endsWith('.d.ts')); + const changedFilePaths: string[] = getWatchFs ? [] : sourceFilePaths; + if (getWatchFs) { + const watchFs: IWatchFileSystem = getWatchFs(); + await Async.forEachAsync( + sourceFilePaths, + async (file: string) => { + const fileState: IWatchedFileState = await watchFs.getStateAndTrackAsync(path.normalize(file)); + if (fileState.changed) { + changedFilePaths.push(file); + } + }, + { + concurrency: 4 + } + ); + } + + if (changedFilePaths.length < 1) { + logger.terminal.writeLine('No changed files found. Skipping transpile.'); + return; + } + + changedFilePaths.sort(); + + logger.terminal.writeVerboseLine('Reading Config'); + + const srcDir: string = Path.convertToSlashes( + path.resolve(buildFolderPath, tsConfigOptions.rootDir ?? 'src') + ); + + const sourceMaps: Config['sourceMaps'] = inlineSourceMap ? 'inline' : sourceMap; + const externalSourceMaps: boolean = sourceMaps === true; + + interface IOptionsByExtension { + ts: string; + tsx: string; + } + + function getOptionsByExtension({ formatOverride, targetOverride }: IEmitKind): IOptionsByExtension { + const format: ModuleConfig['type'] | undefined = + formatOverride !== undefined ? TSC_TO_SWC_MODULE_MAP[formatOverride] : undefined; + if (format === undefined) { + throw new Error(`Unsupported Module Kind: ${formatOverride && ts.ModuleKind[formatOverride]} for swc`); + } + + logger.terminal.writeVerboseLine(`Transpiling to format: ${format}`); + + const target: JscTarget | undefined = + targetOverride !== undefined ? TSC_TO_SWC_TARGET_MAP[targetOverride] : undefined; + if (target === undefined) { + throw new Error(`Unsupported Target: ${target && ts.ScriptTarget[target]} for swc`); + } + + logger.terminal.writeVerboseLine(`Transpiling to target: ${target}`); + + const moduleConfig: ModuleConfig = { + type: format, + noInterop: tsConfigOptions.esModuleInterop === false + }; + + const parser: ParserConfig = { + syntax: 'typescript', + decorators: experimentalDecorators, + dynamicImport: true, + tsx: false + }; + + // https://github.com/swc-project/swc-node/blob/e6cd8b83d1ce76a0abf770f52425704e5d2872c6/packages/register/read-default-tsconfig.ts#L131C7-L139C20 + const react: Partial | undefined = + (tsConfigOptions.jsxFactory ?? + tsConfigOptions.jsxFragmentFactory ?? + tsConfigOptions.jsx ?? + tsConfigOptions.jsxImportSource) + ? { + pragma: tsConfigOptions.jsxFactory, + pragmaFrag: tsConfigOptions.jsxFragmentFactory, + importSource: tsConfigOptions.jsxImportSource ?? 'react', + runtime: (tsConfigOptions.jsx ?? 0) >= ts.JsxEmit.ReactJSX ? 'automatic' : 'classic', + useBuiltins: true + } + : undefined; + + let options: SwcOptions = { + cwd: buildFolderPath, + root: srcDir, + rootMode: 'root', + configFile: false, + swcrc: false, + minify: false, + + sourceMaps: externalSourceMaps, + inputSourceMap: true, + sourceRoot, + isModule: true, + + module: moduleConfig, + jsc: { + target, + externalHelpers: tsConfigOptions.importHelpers, + parser, + transform: { + legacyDecorator: experimentalDecorators, + react, + useDefineForClassFields, + // This property is not included in the types, but is what makes swc-jest work + hidden: { + jest: format === 'commonjs' + } + } as TransformConfig + } + }; + + if (getSwcOptionsHook.isUsed()) { + options = getSwcOptionsHook.call(options, formatOverride, targetOverride); + } + + logger.terminal.writeVerboseLine(`Transpile options: ${JSON.stringify(options, undefined, 2)}}`); + logger.terminal.writeDebugLine(`Transpile options: ${options}`); + + const tsOptions: string = JSON.stringify(options); + parser.tsx = true; + const tsxOptions: string = JSON.stringify(options); + + return { + ts: tsOptions, + tsx: tsxOptions + }; + } + + const outputOptions: Map = new Map(); + for (const emitKind of emitKinds) { + const { outDir } = emitKind; + outputOptions.set(normalizeRelativeDir(outDir), getOptionsByExtension(emitKind)); + } + + const tasks: ITransformTask[] = []; + const requestMessage: ITransformModulesRequestMessage = { + tasks, + options: [] + }; + + const indexForOptions: Map = new Map(); + for (const srcFilePath of changedFilePaths) { + const rootPrefixLength: number | undefined = rootDirsPaths.findChildPath(srcFilePath); + + if (rootPrefixLength === undefined) { + throw new Error(`Could not determine root prefix for ${srcFilePath}}`); + } + + const relativeSrcFilePath: string = srcFilePath.slice(rootPrefixLength); + const extensionIndex: number = relativeSrcFilePath.lastIndexOf('.'); + const tsx: boolean = endsWithCharacterX(relativeSrcFilePath); + + const relativeJsFilePath: string = `${relativeSrcFilePath.slice(0, extensionIndex)}.js`; + for (const [outputPrefix, optionsByExtension] of outputOptions) { + const jsFilePath: string = `${outputPrefix}${relativeJsFilePath}`; + const mapFilePath: string | undefined = externalSourceMaps ? `${jsFilePath}.map` : undefined; + const absoluteMapFilePath: string = `${buildFolderPath}/${mapFilePath}`; + const relativeMapSrcFilePath: string = Path.convertToSlashes( + path.relative(path.dirname(absoluteMapFilePath), srcFilePath) + ); + + const options: string = tsx ? optionsByExtension.tsx : optionsByExtension.ts; + let optionsIndex: number | undefined = indexForOptions.get(options); + if (optionsIndex === undefined) { + optionsIndex = requestMessage.options.push(options) - 1; + indexForOptions.set(options, optionsIndex); + } + const item: ITransformTask = { + srcFilePath, + relativeSrcFilePath: relativeMapSrcFilePath, + optionsIndex, + jsFilePath, + mapFilePath + }; + + tasks.push(item); + } + } + + logger.terminal.writeLine(`Transpiling ${changedFilePaths.length} changed source files...`); + + const result: IWorkerResult = await new Promise((resolve, reject) => { + const workerPath: string = require.resolve('./TranspileWorker.js'); + const concurrency: number = Math.min(4, tasks.length, heftConfiguration.numberOfCores); + + // Due to https://github.com/rust-lang/rust/issues/91979 using worker_threads is not recommended for swc & napi-rs, + // so we use child_process.fork instead. + const childProcess: ChildProcess = fork(workerPath, [buildFolderPath, `${concurrency}`]); + + childProcess.once('message', (message) => { + // Shut down the worker. + childProcess.send(false); + // Node IPC messages are deserialized automatically. + resolve(message as IWorkerResult); + }); + + childProcess.once('error', (error: Error) => { + reject(error); + }); + + childProcess.once('close', (closeExitCode: number, closeSignal: NodeJS.Signals | null) => { + if (closeSignal) { + reject(new Error(`Child process exited with signal: ${closeSignal}`)); + } else if (closeExitCode !== 0) { + reject(new Error(`Child process exited with code: ${closeExitCode}`)); + } + }); + + childProcess.send(requestMessage); + }); + + const { errors, timings: transformTimes, durationMs } = result; + + printTiming(logger, transformTimes, 'Transformed'); + + logger.terminal.writeLine(`Finished transpiling files in ${durationMs.toFixed(2)}ms`); + + const sortedErrors: [string, string][] = errors.sort((x, y): number => { + const xPath: string = x[0]; + const yPath: string = y[0]; + return xPath > yPath ? 1 : xPath < yPath ? -1 : 0; + }); + + for (const [, error] of sortedErrors) { + logger.emitError(new Error(error)); + } +} + +function printTiming(logger: IScopedLogger, times: [string, number][], descriptor: string): void { + times.sort((x, y): number => { + return y[1] - x[1]; + }); + + const timesCount: number = times.length; + logger.terminal.writeVerboseLine(`${descriptor} ${timesCount} files at ${process.uptime()}`); + if (timesCount > 0) { + logger.terminal.writeVerboseLine(`Slowest files:`); + for (let i: number = 0, len: number = Math.min(timesCount, 10); i < len; i++) { + const [fileName, time] = times[i]; + + logger.terminal.writeVerboseLine(`- ${fileName}: ${time.toFixed(2)}ms`); + } + + const medianIndex: number = timesCount >> 1; + const [medianFileName, medianTime] = times[medianIndex]; + + logger.terminal.writeVerboseLine(`Median: (${medianFileName}): ${medianTime.toFixed(2)}ms`); + } +} + +function normalizeRelativeDir(relativeDir: string): string { + return relativeDir.startsWith('./') + ? relativeDir.slice(2) + : relativeDir.startsWith('/') + ? relativeDir.slice(1) + : relativeDir; +} + +function endsWithCharacterX(filePath: string): boolean { + return filePath.charCodeAt(filePath.length - 1) === 120; +} diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/src/TranspileWorker.ts b/heft-plugins/heft-isolated-typescript-transpile-plugin/src/TranspileWorker.ts new file mode 100644 index 00000000000..06c119173f1 --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/src/TranspileWorker.ts @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { mkdirSync, writeFileSync } from 'node:fs'; +import { basename, dirname } from 'node:path'; + +import type { Output } from '@swc/core'; +import { transformFile } from '@swc/core/binding'; + +import { Async } from '@rushstack/node-core-library/lib/Async'; + +import type { IWorkerResult, ITransformTask, ITransformModulesRequestMessage } from './types'; + +interface ISourceMap { + version: 3; + sources: string[]; + sourcesContent?: string[]; + sourceRoot?: string; + names: string[]; + mappings: string; +} + +const [buildFolderPath, concurrency] = process.argv.slice(-2); + +if (!buildFolderPath) { + throw new Error(`buildFolderPath argument not provided to child_process`); +} + +const handleMessageAsync = async (message: ITransformModulesRequestMessage | false): Promise => { + if (!message) { + process.off('message', handleMessageAsync); + return; + } + + const groupStart: number = performance.now(); + const { tasks, options } = message; + + const optionsBuffers: Buffer[] = options.map((option) => Buffer.from(option)); + + const timings: [string, number][] = []; + const errors: [string, string][] = []; + + const createdFolders: Set = new Set(); + + function createFolder(folderPath: string): void { + if (!createdFolders.has(folderPath)) { + mkdirSync(`${buildFolderPath}/${folderPath}`, { recursive: true }); + createdFolders.add(folderPath); + let slashIndex: number = folderPath.lastIndexOf('/'); + while (slashIndex >= 0) { + folderPath = folderPath.slice(0, slashIndex); + createdFolders.add(folderPath); + slashIndex = folderPath.lastIndexOf('/'); + } + } + } + + await Async.forEachAsync( + tasks, + async (task: ITransformTask) => { + const { srcFilePath, relativeSrcFilePath, optionsIndex, jsFilePath, mapFilePath } = task; + + let result: Output | undefined; + + const start: number = performance.now(); + + try { + result = await transformFile(srcFilePath, true, optionsBuffers[optionsIndex]); + } catch (error) { + errors.push([jsFilePath, error.stack ?? error.toString()]); + return; + } finally { + const end: number = performance.now(); + timings.push([jsFilePath, end - start]); + } + + if (result) { + createFolder(dirname(jsFilePath)); + + let { code, map } = result; + + if (mapFilePath && map) { + code += `\n//# sourceMappingURL=./${basename(mapFilePath)}`; + const parsedMap: ISourceMap = JSON.parse(map); + parsedMap.sources[0] = relativeSrcFilePath; + map = JSON.stringify(parsedMap); + writeFileSync(`${buildFolderPath}/${mapFilePath}`, map, 'utf8'); + } + + writeFileSync(`${buildFolderPath}/${jsFilePath}`, code, 'utf8'); + } + }, + { + concurrency: parseInt(concurrency, 10) + } + ); + const groupEnd: number = performance.now(); + + const result: IWorkerResult = { + errors, + timings, + durationMs: groupEnd - groupStart + }; + + if (!process.send) { + throw new Error(`process.send is not available in process`); + } + process.send(result); +}; + +process.on('message', handleMessageAsync); diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/src/index.ts b/heft-plugins/heft-isolated-typescript-transpile-plugin/src/index.ts new file mode 100644 index 00000000000..80befe57c80 --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/src/index.ts @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export type { + ISwcIsolatedTranspilePluginAccessor, + ModuleKind, + ScriptTarget +} from './SwcIsolatedTranspilePlugin'; diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/src/schemas/swc-isolated-transpile-plugin.schema.json b/heft-plugins/heft-isolated-typescript-transpile-plugin/src/schemas/swc-isolated-transpile-plugin.schema.json new file mode 100644 index 00000000000..f87ab47a031 --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/src/schemas/swc-isolated-transpile-plugin.schema.json @@ -0,0 +1,32 @@ +{ + "type": "object", + "additionalProperties": false, + "properties": { + "tsConfigPath": { + "type": "string", + "description": "The path to the tsconfig.json file" + }, + "emitKinds": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["outDir", "formatOverride", "targetOverride"], + "properties": { + "outDir": { + "type": "string", + "description": "The output directory for the transpiled files" + }, + "formatOverride": { + "type": "string", + "description": "The output format for transpiled files. See type ModuleKind in TypeScript for valid values." + }, + "targetOverride": { + "type": "string", + "description": "The target for transpiled files. See type ScriptTarget in TypeScript for valid values." + } + } + } + } + } +} diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/src/types.ts b/heft-plugins/heft-isolated-typescript-transpile-plugin/src/types.ts new file mode 100644 index 00000000000..0926fba7f7e --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/src/types.ts @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { ModuleKind, ScriptTarget } from './SwcIsolatedTranspilePlugin'; + +export interface IProjectOptions { + buildFolder: string; +} + +export interface IEmitKind { + outDir: string; + formatOverride: ModuleKind; + targetOverride: ScriptTarget; +} + +export interface ISwcIsolatedTranspileOptions { + tsConfigPath?: string; + emitKinds?: IEmitKind[]; +} + +export interface IWorkerData { + buildFolderPath: string; + concurrency: number; +} + +export interface IWorkerResult { + errors: [string, string][]; + timings: [string, number][]; + durationMs: number; +} + +export interface ITransformTask { + srcFilePath: string; + relativeSrcFilePath: string; + optionsIndex: number; + jsFilePath: string; + mapFilePath: string | undefined; +} + +export interface ITransformModulesRequestMessage { + options: string[]; + tasks: ITransformTask[]; +} diff --git a/heft-plugins/heft-isolated-typescript-transpile-plugin/tsconfig.json b/heft-plugins/heft-isolated-typescript-transpile-plugin/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/heft-plugins/heft-isolated-typescript-transpile-plugin/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/heft-plugins/heft-jest-plugin/.eslintrc.js b/heft-plugins/heft-jest-plugin/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/heft-plugins/heft-jest-plugin/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/heft-plugins/heft-jest-plugin/.npmignore b/heft-plugins/heft-jest-plugin/.npmignore index 65b383d1692..f7a40e10213 100644 --- a/heft-plugins/heft-jest-plugin/.npmignore +++ b/heft-plugins/heft-jest-plugin/.npmignore @@ -8,25 +8,29 @@ !/lib/** !/lib-*/** !/dist/** -!ThirdPartyNotice.txt +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json !heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt # Ignore certain patterns that should not get published. /dist/*.stats.* /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) -!/includes/** \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-jest-plugin/CHANGELOG.json b/heft-plugins/heft-jest-plugin/CHANGELOG.json index dff2f65a509..86b696885bf 100644 --- a/heft-plugins/heft-jest-plugin/CHANGELOG.json +++ b/heft-plugins/heft-jest-plugin/CHANGELOG.json @@ -1,6 +1,3349 @@ { "name": "@rushstack/heft-jest-plugin", "entries": [ + { + "version": "2.0.12", + "tag": "@rushstack/heft-jest-plugin_v2.0.12", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "2.0.11", + "tag": "@rushstack/heft-jest-plugin_v2.0.11", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "patch": [ + { + "comment": "Replace `@override` with the `override` keyword." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "2.0.10", + "tag": "@rushstack/heft-jest-plugin_v2.0.10", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.11`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "2.0.9", + "tag": "@rushstack/heft-jest-plugin_v2.0.9", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "2.0.8", + "tag": "@rushstack/heft-jest-plugin_v2.0.8", + "date": "Wed, 10 Jun 2026 00:15:42 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where the `--test-path-pattern` parameter was ignored, causing all tests to run. In Jest 30 the `Config.Argv` field was renamed from `testPathPattern` to `testPathPatterns`." + } + ] + } + }, + { + "version": "2.0.7", + "tag": "@rushstack/heft-jest-plugin_v2.0.7", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "2.0.6", + "tag": "@rushstack/heft-jest-plugin_v2.0.6", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.10`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "2.0.5", + "tag": "@rushstack/heft-jest-plugin_v2.0.5", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "2.0.4", + "tag": "@rushstack/heft-jest-plugin_v2.0.4", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.9`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "2.0.3", + "tag": "@rushstack/heft-jest-plugin_v2.0.3", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "2.0.2", + "tag": "@rushstack/heft-jest-plugin_v2.0.2", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "patch": [ + { + "comment": "Remove dependecy on `lodash`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "2.0.1", + "tag": "@rushstack/heft-jest-plugin_v2.0.1", + "date": "Wed, 15 Apr 2026 17:59:12 GMT", + "comments": { + "patch": [ + { + "comment": "Remove the built-in `jest-global-setup` script as Jest 30 ships with the `mocked` function as `jest.mocked`, so including it as a global is now nonstandard." + } + ] + } + }, + { + "version": "2.0.0", + "tag": "@rushstack/heft-jest-plugin_v2.0.0", + "date": "Tue, 14 Apr 2026 01:25:46 GMT", + "comments": { + "major": [ + { + "comment": "Bump jest to 30.3.0 to address CVE GHSA-vpq2-c234-7xj6" + } + ] + } + }, + { + "version": "1.2.12", + "tag": "@rushstack/heft-jest-plugin_v1.2.12", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "1.2.11", + "tag": "@rushstack/heft-jest-plugin_v1.2.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "1.2.10", + "tag": "@rushstack/heft-jest-plugin_v1.2.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "patch": [ + { + "comment": "Bump lodash 4.18.1 to address CVEs GHSA-r5fr-rjxr-66jc, GHSA-f23m-r3pf-42rh" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "1.2.9", + "tag": "@rushstack/heft-jest-plugin_v1.2.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "1.2.8", + "tag": "@rushstack/heft-jest-plugin_v1.2.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "1.2.7", + "tag": "@rushstack/heft-jest-plugin_v1.2.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "1.2.6", + "tag": "@rushstack/heft-jest-plugin_v1.2.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "1.2.5", + "tag": "@rushstack/heft-jest-plugin_v1.2.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "1.2.4", + "tag": "@rushstack/heft-jest-plugin_v1.2.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "1.2.3", + "tag": "@rushstack/heft-jest-plugin_v1.2.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "patch": [ + { + "comment": "Add missing \"./includes/*.json\" to the package.json \"exports\" field so that Jest config files like \"@rushstack/heft-jest-plugin/includes/jest-shared.config.json\" are importable." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "1.2.2", + "tag": "@rushstack/heft-jest-plugin_v1.2.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "1.2.1", + "tag": "@rushstack/heft-jest-plugin_v1.2.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "1.2.0", + "tag": "@rushstack/heft-jest-plugin_v1.2.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "1.1.14", + "tag": "@rushstack/heft-jest-plugin_v1.1.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "patch": [ + { + "comment": "Upgrade `lodash` dependency from `~4.17.15` to `~4.17.23`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "1.1.13", + "tag": "@rushstack/heft-jest-plugin_v1.1.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "1.1.12", + "tag": "@rushstack/heft-jest-plugin_v1.1.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "1.1.11", + "tag": "@rushstack/heft-jest-plugin_v1.1.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "1.1.10", + "tag": "@rushstack/heft-jest-plugin_v1.1.10", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "1.1.9", + "tag": "@rushstack/heft-jest-plugin_v1.1.9", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "1.1.8", + "tag": "@rushstack/heft-jest-plugin_v1.1.8", + "date": "Mon, 05 Jan 2026 16:12:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "1.1.7", + "tag": "@rushstack/heft-jest-plugin_v1.1.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "1.1.6", + "tag": "@rushstack/heft-jest-plugin_v1.1.6", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.5` to `1.1.6`" + } + ] + } + }, + { + "version": "1.1.5", + "tag": "@rushstack/heft-jest-plugin_v1.1.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.4` to `1.1.5`" + } + ] + } + }, + { + "version": "1.1.4", + "tag": "@rushstack/heft-jest-plugin_v1.1.4", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.3` to `1.1.4`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft-jest-plugin_v1.1.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.2` to `1.1.3`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft-jest-plugin_v1.1.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.1` to `1.1.2`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft-jest-plugin_v1.1.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.0` to `1.1.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-jest-plugin_v1.1.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.0.0` to `1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-jest-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.75.0` to `1.0.0`" + } + ] + } + }, + { + "version": "0.16.15", + "tag": "@rushstack/heft-jest-plugin_v0.16.15", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.5` to `0.75.0`" + } + ] + } + }, + { + "version": "0.16.14", + "tag": "@rushstack/heft-jest-plugin_v0.16.14", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.4` to `0.74.5`" + } + ] + } + }, + { + "version": "0.16.13", + "tag": "@rushstack/heft-jest-plugin_v0.16.13", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.3` to `0.74.4`" + } + ] + } + }, + { + "version": "0.16.12", + "tag": "@rushstack/heft-jest-plugin_v0.16.12", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.2` to `0.74.3`" + } + ] + } + }, + { + "version": "0.16.11", + "tag": "@rushstack/heft-jest-plugin_v0.16.11", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.1` to `0.74.2`" + } + ] + } + }, + { + "version": "0.16.10", + "tag": "@rushstack/heft-jest-plugin_v0.16.10", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.0` to `0.74.1`" + } + ] + } + }, + { + "version": "0.16.9", + "tag": "@rushstack/heft-jest-plugin_v0.16.9", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.6` to `0.74.0`" + } + ] + } + }, + { + "version": "0.16.8", + "tag": "@rushstack/heft-jest-plugin_v0.16.8", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.5` to `0.73.6`" + } + ] + } + }, + { + "version": "0.16.7", + "tag": "@rushstack/heft-jest-plugin_v0.16.7", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.4` to `0.73.5`" + } + ] + } + }, + { + "version": "0.16.6", + "tag": "@rushstack/heft-jest-plugin_v0.16.6", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.3` to `0.73.4`" + } + ] + } + }, + { + "version": "0.16.5", + "tag": "@rushstack/heft-jest-plugin_v0.16.5", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.2` to `0.73.3`" + } + ] + } + }, + { + "version": "0.16.4", + "tag": "@rushstack/heft-jest-plugin_v0.16.4", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.1` to `0.73.2`" + } + ] + } + }, + { + "version": "0.16.3", + "tag": "@rushstack/heft-jest-plugin_v0.16.3", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.0` to `0.73.1`" + } + ] + } + }, + { + "version": "0.16.2", + "tag": "@rushstack/heft-jest-plugin_v0.16.2", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.72.0` to `0.73.0`" + } + ] + } + }, + { + "version": "0.16.1", + "tag": "@rushstack/heft-jest-plugin_v0.16.1", + "date": "Wed, 09 Apr 2025 00:11:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.2` to `0.72.0`" + } + ] + } + }, + { + "version": "0.16.0", + "tag": "@rushstack/heft-jest-plugin_v0.16.0", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "minor": [ + { + "comment": "(BREAKING CHANGE) Update `jest-string-mock-transform` to emit slash-normalized relative paths to files, rather than absolute paths, to ensure portability of snapshots." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.1` to `0.71.2`" + } + ] + } + }, + { + "version": "0.15.3", + "tag": "@rushstack/heft-jest-plugin_v0.15.3", + "date": "Tue, 25 Mar 2025 15:11:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.0` to `0.71.1`" + } + ] + } + }, + { + "version": "0.15.2", + "tag": "@rushstack/heft-jest-plugin_v0.15.2", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.1` to `0.71.0`" + } + ] + } + }, + { + "version": "0.15.1", + "tag": "@rushstack/heft-jest-plugin_v0.15.1", + "date": "Wed, 12 Mar 2025 00:11:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.0` to `0.70.1`" + } + ] + } + }, + { + "version": "0.15.0", + "tag": "@rushstack/heft-jest-plugin_v0.15.0", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "minor": [ + { + "comment": "Use `useNodeJSResolver: true` in `Import.resolvePackage` calls." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.3` to `0.70.0`" + } + ] + } + }, + { + "version": "0.14.13", + "tag": "@rushstack/heft-jest-plugin_v0.14.13", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.2` to `0.69.3`" + } + ] + } + }, + { + "version": "0.14.12", + "tag": "@rushstack/heft-jest-plugin_v0.14.12", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.1` to `0.69.2`" + } + ] + } + }, + { + "version": "0.14.11", + "tag": "@rushstack/heft-jest-plugin_v0.14.11", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.0` to `0.69.1`" + } + ] + } + }, + { + "version": "0.14.10", + "tag": "@rushstack/heft-jest-plugin_v0.14.10", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.18` to `0.69.0`" + } + ] + } + }, + { + "version": "0.14.9", + "tag": "@rushstack/heft-jest-plugin_v0.14.9", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.17` to `0.68.18`" + } + ] + } + }, + { + "version": "0.14.8", + "tag": "@rushstack/heft-jest-plugin_v0.14.8", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.16` to `0.68.17`" + } + ] + } + }, + { + "version": "0.14.7", + "tag": "@rushstack/heft-jest-plugin_v0.14.7", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.15` to `0.68.16`" + } + ] + } + }, + { + "version": "0.14.6", + "tag": "@rushstack/heft-jest-plugin_v0.14.6", + "date": "Fri, 07 Feb 2025 01:10:49 GMT", + "comments": { + "patch": [ + { + "comment": "Extend heft-jest-plugin json schema to match HeftJestConfiguration" + } + ] + } + }, + { + "version": "0.14.5", + "tag": "@rushstack/heft-jest-plugin_v0.14.5", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.14` to `0.68.15`" + } + ] + } + }, + { + "version": "0.14.4", + "tag": "@rushstack/heft-jest-plugin_v0.14.4", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.13` to `0.68.14`" + } + ] + } + }, + { + "version": "0.14.3", + "tag": "@rushstack/heft-jest-plugin_v0.14.3", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.12` to `0.68.13`" + } + ] + } + }, + { + "version": "0.14.2", + "tag": "@rushstack/heft-jest-plugin_v0.14.2", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.11` to `0.68.12`" + } + ] + } + }, + { + "version": "0.14.1", + "tag": "@rushstack/heft-jest-plugin_v0.14.1", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.10` to `0.68.11`" + } + ] + } + }, + { + "version": "0.14.0", + "tag": "@rushstack/heft-jest-plugin_v0.14.0", + "date": "Tue, 10 Dec 2024 07:32:19 GMT", + "comments": { + "minor": [ + { + "comment": "Inject `punycode` into the NodeJS module cache in Node versions 22 and above to work around a deprecation warning." + } + ] + } + }, + { + "version": "0.13.3", + "tag": "@rushstack/heft-jest-plugin_v0.13.3", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.9` to `0.68.10`" + } + ] + } + }, + { + "version": "0.13.2", + "tag": "@rushstack/heft-jest-plugin_v0.13.2", + "date": "Tue, 03 Dec 2024 16:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.8` to `0.68.9`" + } + ] + } + }, + { + "version": "0.13.1", + "tag": "@rushstack/heft-jest-plugin_v0.13.1", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "patch": [ + { + "comment": "Fix a bug in `jest-node-modules-symlink-resolver` with respect to evaluating paths that don't exist. Expected behavior in that situation is to return the input path." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.7` to `0.68.8`" + } + ] + } + }, + { + "version": "0.13.0", + "tag": "@rushstack/heft-jest-plugin_v0.13.0", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "minor": [ + { + "comment": "Add a custom resolver that only resolves symlinks that are within node_modules." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.6` to `0.68.7`" + } + ] + } + }, + { + "version": "0.12.18", + "tag": "@rushstack/heft-jest-plugin_v0.12.18", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.5` to `0.68.6`" + } + ] + } + }, + { + "version": "0.12.17", + "tag": "@rushstack/heft-jest-plugin_v0.12.17", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.4` to `0.68.5`" + } + ] + } + }, + { + "version": "0.12.16", + "tag": "@rushstack/heft-jest-plugin_v0.12.16", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.3` to `0.68.4`" + } + ] + } + }, + { + "version": "0.12.15", + "tag": "@rushstack/heft-jest-plugin_v0.12.15", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.2` to `0.68.3`" + } + ] + } + }, + { + "version": "0.12.14", + "tag": "@rushstack/heft-jest-plugin_v0.12.14", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.1` to `0.68.2`" + } + ] + } + }, + { + "version": "0.12.13", + "tag": "@rushstack/heft-jest-plugin_v0.12.13", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.0` to `0.68.1`" + } + ] + } + }, + { + "version": "0.12.12", + "tag": "@rushstack/heft-jest-plugin_v0.12.12", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.2` to `0.68.0`" + } + ] + } + }, + { + "version": "0.12.11", + "tag": "@rushstack/heft-jest-plugin_v0.12.11", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.1` to `0.67.2`" + } + ] + } + }, + { + "version": "0.12.10", + "tag": "@rushstack/heft-jest-plugin_v0.12.10", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.0` to `0.67.1`" + } + ] + } + }, + { + "version": "0.12.9", + "tag": "@rushstack/heft-jest-plugin_v0.12.9", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.26` to `0.67.0`" + } + ] + } + }, + { + "version": "0.12.8", + "tag": "@rushstack/heft-jest-plugin_v0.12.8", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.25` to `0.66.26`" + } + ] + } + }, + { + "version": "0.12.7", + "tag": "@rushstack/heft-jest-plugin_v0.12.7", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.24` to `0.66.25`" + } + ] + } + }, + { + "version": "0.12.6", + "tag": "@rushstack/heft-jest-plugin_v0.12.6", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.23` to `0.66.24`" + } + ] + } + }, + { + "version": "0.12.5", + "tag": "@rushstack/heft-jest-plugin_v0.12.5", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.22` to `0.66.23`" + } + ] + } + }, + { + "version": "0.12.4", + "tag": "@rushstack/heft-jest-plugin_v0.12.4", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.21` to `0.66.22`" + } + ] + } + }, + { + "version": "0.12.3", + "tag": "@rushstack/heft-jest-plugin_v0.12.3", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.20` to `0.66.21`" + } + ] + } + }, + { + "version": "0.12.2", + "tag": "@rushstack/heft-jest-plugin_v0.12.2", + "date": "Tue, 16 Jul 2024 00:36:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.19` to `0.66.20`" + } + ] + } + }, + { + "version": "0.12.1", + "tag": "@rushstack/heft-jest-plugin_v0.12.1", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.18` to `0.66.19`" + } + ] + } + }, + { + "version": "0.12.0", + "tag": "@rushstack/heft-jest-plugin_v0.12.0", + "date": "Tue, 11 Jun 2024 00:21:28 GMT", + "comments": { + "minor": [ + { + "comment": "Update the test reporter to report unchecked snapshots." + } + ] + } + }, + { + "version": "0.11.39", + "tag": "@rushstack/heft-jest-plugin_v0.11.39", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.17` to `0.66.18`" + } + ] + } + }, + { + "version": "0.11.38", + "tag": "@rushstack/heft-jest-plugin_v0.11.38", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.25`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.16` to `0.66.17`" + } + ] + } + }, + { + "version": "0.11.37", + "tag": "@rushstack/heft-jest-plugin_v0.11.37", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.24`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.15` to `0.66.16`" + } + ] + } + }, + { + "version": "0.11.36", + "tag": "@rushstack/heft-jest-plugin_v0.11.36", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.14` to `0.66.15`" + } + ] + } + }, + { + "version": "0.11.35", + "tag": "@rushstack/heft-jest-plugin_v0.11.35", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.23`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.13` to `0.66.14`" + } + ] + } + }, + { + "version": "0.11.34", + "tag": "@rushstack/heft-jest-plugin_v0.11.34", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.22`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.12` to `0.66.13`" + } + ] + } + }, + { + "version": "0.11.33", + "tag": "@rushstack/heft-jest-plugin_v0.11.33", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.21`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.11` to `0.66.12`" + } + ] + } + }, + { + "version": "0.11.32", + "tag": "@rushstack/heft-jest-plugin_v0.11.32", + "date": "Fri, 24 May 2024 00:15:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.10` to `0.66.11`" + } + ] + } + }, + { + "version": "0.11.31", + "tag": "@rushstack/heft-jest-plugin_v0.11.31", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.20`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.9` to `0.66.10`" + } + ] + } + }, + { + "version": "0.11.30", + "tag": "@rushstack/heft-jest-plugin_v0.11.30", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.8` to `0.66.9`" + } + ] + } + }, + { + "version": "0.11.29", + "tag": "@rushstack/heft-jest-plugin_v0.11.29", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.19`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.7` to `0.66.8`" + } + ] + } + }, + { + "version": "0.11.28", + "tag": "@rushstack/heft-jest-plugin_v0.11.28", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.18`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.6` to `0.66.7`" + } + ] + } + }, + { + "version": "0.11.27", + "tag": "@rushstack/heft-jest-plugin_v0.11.27", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.17`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.5` to `0.66.6`" + } + ] + } + }, + { + "version": "0.11.26", + "tag": "@rushstack/heft-jest-plugin_v0.11.26", + "date": "Wed, 08 May 2024 22:23:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.4` to `0.66.5`" + } + ] + } + }, + { + "version": "0.11.25", + "tag": "@rushstack/heft-jest-plugin_v0.11.25", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.16`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.3` to `0.66.4`" + } + ] + } + }, + { + "version": "0.11.24", + "tag": "@rushstack/heft-jest-plugin_v0.11.24", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.15`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.2` to `0.66.3`" + } + ] + } + }, + { + "version": "0.11.23", + "tag": "@rushstack/heft-jest-plugin_v0.11.23", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.1` to `0.66.2`" + } + ] + } + }, + { + "version": "0.11.22", + "tag": "@rushstack/heft-jest-plugin_v0.11.22", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.0` to `0.66.1`" + } + ] + } + }, + { + "version": "0.11.21", + "tag": "@rushstack/heft-jest-plugin_v0.11.21", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.10` to `0.66.0`" + } + ] + } + }, + { + "version": "0.11.20", + "tag": "@rushstack/heft-jest-plugin_v0.11.20", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.9` to `0.65.10`" + } + ] + } + }, + { + "version": "0.11.19", + "tag": "@rushstack/heft-jest-plugin_v0.11.19", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.8` to `0.65.9`" + } + ] + } + }, + { + "version": "0.11.18", + "tag": "@rushstack/heft-jest-plugin_v0.11.18", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.7` to `0.65.8`" + } + ] + } + }, + { + "version": "0.11.17", + "tag": "@rushstack/heft-jest-plugin_v0.11.17", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.6` to `0.65.7`" + } + ] + } + }, + { + "version": "0.11.16", + "tag": "@rushstack/heft-jest-plugin_v0.11.16", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.5` to `0.65.6`" + } + ] + } + }, + { + "version": "0.11.15", + "tag": "@rushstack/heft-jest-plugin_v0.11.15", + "date": "Mon, 26 Feb 2024 16:10:56 GMT", + "comments": { + "patch": [ + { + "comment": "Make `@rushstack/terminal` a dependency because the reporter has a runtime dependency on that package." + } + ] + } + }, + { + "version": "0.11.14", + "tag": "@rushstack/heft-jest-plugin_v0.11.14", + "date": "Thu, 22 Feb 2024 05:54:17 GMT", + "comments": { + "patch": [ + { + "comment": "Add a missing dependency on `@rushstack/terminal`" + } + ] + } + }, + { + "version": "0.11.13", + "tag": "@rushstack/heft-jest-plugin_v0.11.13", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.3` to `^0.65.4`" + } + ] + } + }, + { + "version": "0.11.12", + "tag": "@rushstack/heft-jest-plugin_v0.11.12", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.13`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.2` to `^0.65.3`" + } + ] + } + }, + { + "version": "0.11.11", + "tag": "@rushstack/heft-jest-plugin_v0.11.11", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.1` to `^0.65.2`" + } + ] + } + }, + { + "version": "0.11.10", + "tag": "@rushstack/heft-jest-plugin_v0.11.10", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.0` to `^0.65.1`" + } + ] + } + }, + { + "version": "0.11.9", + "tag": "@rushstack/heft-jest-plugin_v0.11.9", + "date": "Tue, 20 Feb 2024 16:10:53 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.8` to `^0.65.0`" + } + ] + } + }, + { + "version": "0.11.8", + "tag": "@rushstack/heft-jest-plugin_v0.11.8", + "date": "Mon, 19 Feb 2024 21:54:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.11`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.7` to `^0.64.8`" + } + ] + } + }, + { + "version": "0.11.7", + "tag": "@rushstack/heft-jest-plugin_v0.11.7", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.10`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.6` to `^0.64.7`" + } + ] + } + }, + { + "version": "0.11.6", + "tag": "@rushstack/heft-jest-plugin_v0.11.6", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.5` to `^0.64.6`" + } + ] + } + }, + { + "version": "0.11.5", + "tag": "@rushstack/heft-jest-plugin_v0.11.5", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.4` to `^0.64.5`" + } + ] + } + }, + { + "version": "0.11.4", + "tag": "@rushstack/heft-jest-plugin_v0.11.4", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.3` to `^0.64.4`" + } + ] + } + }, + { + "version": "0.11.3", + "tag": "@rushstack/heft-jest-plugin_v0.11.3", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.2` to `^0.64.3`" + } + ] + } + }, + { + "version": "0.11.2", + "tag": "@rushstack/heft-jest-plugin_v0.11.2", + "date": "Tue, 23 Jan 2024 20:12:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.1` to `^0.64.2`" + } + ] + } + }, + { + "version": "0.11.1", + "tag": "@rushstack/heft-jest-plugin_v0.11.1", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.0` to `^0.64.1`" + } + ] + } + }, + { + "version": "0.11.0", + "tag": "@rushstack/heft-jest-plugin_v0.11.0", + "date": "Tue, 16 Jan 2024 18:30:10 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for TypeScript 5.3" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.6` to `^0.64.0`" + } + ] + } + }, + { + "version": "0.10.8", + "tag": "@rushstack/heft-jest-plugin_v0.10.8", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.5` to `^0.63.6`" + } + ] + } + }, + { + "version": "0.10.7", + "tag": "@rushstack/heft-jest-plugin_v0.10.7", + "date": "Wed, 20 Dec 2023 01:09:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.4` to `^0.63.5`" + } + ] + } + }, + { + "version": "0.10.6", + "tag": "@rushstack/heft-jest-plugin_v0.10.6", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.3` to `^0.63.4`" + } + ] + } + }, + { + "version": "0.10.5", + "tag": "@rushstack/heft-jest-plugin_v0.10.5", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.2` to `^0.63.3`" + } + ] + } + }, + { + "version": "0.10.4", + "tag": "@rushstack/heft-jest-plugin_v0.10.4", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.1` to `^0.63.2`" + } + ] + } + }, + { + "version": "0.10.3", + "tag": "@rushstack/heft-jest-plugin_v0.10.3", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.0` to `^0.63.1`" + } + ] + } + }, + { + "version": "0.10.2", + "tag": "@rushstack/heft-jest-plugin_v0.10.2", + "date": "Mon, 30 Oct 2023 23:36:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.3` to `^0.63.0`" + } + ] + } + }, + { + "version": "0.10.1", + "tag": "@rushstack/heft-jest-plugin_v0.10.1", + "date": "Thu, 26 Oct 2023 00:27:48 GMT", + "comments": { + "patch": [ + { + "comment": "Add an option (`enableNodeEnvManagement`) to ensure that the NODE_ENV environment variable is set to `\"test\"` during test execution." + } + ] + } + }, + { + "version": "0.10.0", + "tag": "@rushstack/heft-jest-plugin_v0.10.0", + "date": "Mon, 23 Oct 2023 15:18:38 GMT", + "comments": { + "minor": [ + { + "comment": "Use Jest verbose logging when `heft --debug test` or `heft test --verbose` is specified" + }, + { + "comment": "Fix an issue where `silent: true` was ignored when specified in `jest.config.json`" + } + ] + } + }, + { + "version": "0.9.9", + "tag": "@rushstack/heft-jest-plugin_v0.9.9", + "date": "Sun, 01 Oct 2023 02:56:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.2` to `^0.62.3`" + } + ] + } + }, + { + "version": "0.9.8", + "tag": "@rushstack/heft-jest-plugin_v0.9.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.1` to `^0.62.2`" + } + ] + } + }, + { + "version": "0.9.7", + "tag": "@rushstack/heft-jest-plugin_v0.9.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.0` to `^0.62.1`" + } + ] + } + }, + { + "version": "0.9.6", + "tag": "@rushstack/heft-jest-plugin_v0.9.6", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.3` to `^0.62.0`" + } + ] + } + }, + { + "version": "0.9.5", + "tag": "@rushstack/heft-jest-plugin_v0.9.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.2` to `^0.61.3`" + } + ] + } + }, + { + "version": "0.9.4", + "tag": "@rushstack/heft-jest-plugin_v0.9.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.1` to `^0.61.2`" + } + ] + } + }, + { + "version": "0.9.3", + "tag": "@rushstack/heft-jest-plugin_v0.9.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.0` to `^0.61.1`" + } + ] + } + }, + { + "version": "0.9.2", + "tag": "@rushstack/heft-jest-plugin_v0.9.2", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.60.0` to `^0.61.0`" + } + ] + } + }, + { + "version": "0.9.1", + "tag": "@rushstack/heft-jest-plugin_v0.9.1", + "date": "Tue, 19 Sep 2023 15:21:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.59.0` to `^0.60.0`" + } + ] + } + }, + { + "version": "0.9.0", + "tag": "@rushstack/heft-jest-plugin_v0.9.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Add a `--log-heap-usage` flag that includes memory usage analysis in each test run." + }, + { + "comment": "Update @types/node from 14 to 18" + } + ], + "patch": [ + { + "comment": "Wait for first test run to be scheduled in initial invocation in watch mode." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.2` to `^0.59.0`" + } + ] + } + }, + { + "version": "0.8.1", + "tag": "@rushstack/heft-jest-plugin_v0.8.1", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.1` to `^0.58.2`" + } + ] + } + }, + { + "version": "0.8.0", + "tag": "@rushstack/heft-jest-plugin_v0.8.0", + "date": "Mon, 31 Jul 2023 15:19:05 GMT", + "comments": { + "minor": [ + { + "comment": "Make `jest-environment-jsdom` and `jest-environment-node` optional peerDependencies." + } + ] + } + }, + { + "version": "0.7.18", + "tag": "@rushstack/heft-jest-plugin_v0.7.18", + "date": "Sat, 29 Jul 2023 00:22:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.0` to `^0.58.1`" + } + ] + } + }, + { + "version": "0.7.17", + "tag": "@rushstack/heft-jest-plugin_v0.7.17", + "date": "Thu, 20 Jul 2023 20:47:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.57.1` to `^0.58.0`" + } + ] + } + }, + { + "version": "0.7.16", + "tag": "@rushstack/heft-jest-plugin_v0.7.16", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.57.0` to `^0.57.1`" + } + ] + } + }, + { + "version": "0.7.15", + "tag": "@rushstack/heft-jest-plugin_v0.7.15", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.3` to `^0.57.0`" + } + ] + } + }, + { + "version": "0.7.14", + "tag": "@rushstack/heft-jest-plugin_v0.7.14", + "date": "Wed, 12 Jul 2023 15:20:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.2` to `^0.56.3`" + } + ] + } + }, + { + "version": "0.7.13", + "tag": "@rushstack/heft-jest-plugin_v0.7.13", + "date": "Fri, 07 Jul 2023 00:19:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.1` to `^0.56.2`" + } + ] + } + }, + { + "version": "0.7.12", + "tag": "@rushstack/heft-jest-plugin_v0.7.12", + "date": "Thu, 06 Jul 2023 00:16:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.0` to `^0.56.1`" + } + ] + } + }, + { + "version": "0.7.11", + "tag": "@rushstack/heft-jest-plugin_v0.7.11", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.2` to `^0.56.0`" + } + ] + } + }, + { + "version": "0.7.10", + "tag": "@rushstack/heft-jest-plugin_v0.7.10", + "date": "Thu, 15 Jun 2023 00:21:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.12.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.1` to `^0.55.2`" + } + ] + } + }, + { + "version": "0.7.9", + "tag": "@rushstack/heft-jest-plugin_v0.7.9", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.0` to `^0.55.1`" + } + ] + } + }, + { + "version": "0.7.8", + "tag": "@rushstack/heft-jest-plugin_v0.7.8", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.54.0` to `^0.55.0`" + } + ] + } + }, + { + "version": "0.7.7", + "tag": "@rushstack/heft-jest-plugin_v0.7.7", + "date": "Tue, 13 Jun 2023 01:49:01 GMT", + "comments": { + "patch": [ + { + "comment": "Add support for using '-u' for the '--update-snapshots' parameter and '-t' for the '--test-name-pattern' parameter" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.53.1` to `^0.54.0`" + } + ] + } + }, + { + "version": "0.7.6", + "tag": "@rushstack/heft-jest-plugin_v0.7.6", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.53.0` to `^0.53.1`" + } + ] + } + }, + { + "version": "0.7.5", + "tag": "@rushstack/heft-jest-plugin_v0.7.5", + "date": "Fri, 09 Jun 2023 15:23:15 GMT", + "comments": { + "patch": [ + { + "comment": "Added --test-path-ignore-patterns support for subtractive test selection to complement existing additive support." + } + ] + } + }, + { + "version": "0.7.4", + "tag": "@rushstack/heft-jest-plugin_v0.7.4", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.2` to `^0.53.0`" + } + ] + } + }, + { + "version": "0.7.3", + "tag": "@rushstack/heft-jest-plugin_v0.7.3", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.1` to `^0.52.2`" + } + ] + } + }, + { + "version": "0.7.2", + "tag": "@rushstack/heft-jest-plugin_v0.7.2", + "date": "Thu, 08 Jun 2023 00:20:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.0` to `^0.52.1`" + } + ] + } + }, + { + "version": "0.7.1", + "tag": "@rushstack/heft-jest-plugin_v0.7.1", + "date": "Wed, 07 Jun 2023 22:45:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.12.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.51.0` to `^0.52.0`" + } + ] + } + }, + { + "version": "0.7.0", + "tag": "@rushstack/heft-jest-plugin_v0.7.0", + "date": "Tue, 06 Jun 2023 02:52:51 GMT", + "comments": { + "minor": [ + { + "comment": "Adds a new base config for web projects, jest-web.config.json. Adds the \"customExportConditions\" field to both base configs with sensible defaults." + } + ] + } + }, { "version": "0.6.0", "tag": "@rushstack/heft-jest-plugin_v0.6.0", diff --git a/heft-plugins/heft-jest-plugin/CHANGELOG.md b/heft-plugins/heft-jest-plugin/CHANGELOG.md index 912fb2d8a2f..33ca5355b8a 100644 --- a/heft-plugins/heft-jest-plugin/CHANGELOG.md +++ b/heft-plugins/heft-jest-plugin/CHANGELOG.md @@ -1,6 +1,971 @@ # Change Log - @rushstack/heft-jest-plugin -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 2.0.12 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 2.0.11 +Fri, 17 Jul 2026 00:15:59 GMT + +### Patches + +- Replace `@override` with the `override` keyword. + +## 2.0.10 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 2.0.9 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 2.0.8 +Wed, 10 Jun 2026 00:15:42 GMT + +### Patches + +- Fix an issue where the `--test-path-pattern` parameter was ignored, causing all tests to run. In Jest 30 the `Config.Argv` field was renamed from `testPathPattern` to `testPathPatterns`. + +## 2.0.7 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 2.0.6 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 2.0.5 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 2.0.4 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 2.0.3 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 2.0.2 +Fri, 17 Apr 2026 15:14:57 GMT + +### Patches + +- Remove dependecy on `lodash`. + +## 2.0.1 +Wed, 15 Apr 2026 17:59:12 GMT + +### Patches + +- Remove the built-in `jest-global-setup` script as Jest 30 ships with the `mocked` function as `jest.mocked`, so including it as a global is now nonstandard. + +## 2.0.0 +Tue, 14 Apr 2026 01:25:46 GMT + +### Breaking changes + +- Bump jest to 30.3.0 to address CVE GHSA-vpq2-c234-7xj6 + +## 1.2.12 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.2.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.2.10 +Sat, 04 Apr 2026 00:14:00 GMT + +### Patches + +- Bump lodash 4.18.1 to address CVEs GHSA-r5fr-rjxr-66jc, GHSA-f23m-r3pf-42rh + +## 1.2.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.2.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.2.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.2.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.2.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 1.2.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.2.3 +Mon, 23 Feb 2026 00:42:21 GMT + +### Patches + +- Add missing "./includes/*.json" to the package.json "exports" field so that Jest config files like "@rushstack/heft-jest-plugin/includes/jest-shared.config.json" are importable. + +## 1.2.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.2.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.2.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.1.14 +Sat, 07 Feb 2026 01:13:26 GMT + +### Patches + +- Upgrade `lodash` dependency from `~4.17.15` to `~4.17.23`. + +## 1.1.13 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.1.12 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.1.11 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.1.10 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.1.9 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 1.1.8 +Mon, 05 Jan 2026 16:12:50 GMT + +_Version update only_ + +## 1.1.7 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.1.6 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.1.5 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.1.4 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 1.1.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.1.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.1.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 1.1.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.16.15 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.16.14 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.16.13 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.16.12 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.16.11 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 0.16.10 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.16.9 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.16.8 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.16.7 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.16.6 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.16.5 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.16.4 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.16.3 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.16.2 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 0.16.1 +Wed, 09 Apr 2025 00:11:02 GMT + +_Version update only_ + +## 0.16.0 +Fri, 04 Apr 2025 18:34:35 GMT + +### Minor changes + +- (BREAKING CHANGE) Update `jest-string-mock-transform` to emit slash-normalized relative paths to files, rather than absolute paths, to ensure portability of snapshots. + +## 0.15.3 +Tue, 25 Mar 2025 15:11:16 GMT + +_Version update only_ + +## 0.15.2 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.15.1 +Wed, 12 Mar 2025 00:11:31 GMT + +_Version update only_ + +## 0.15.0 +Tue, 11 Mar 2025 02:12:33 GMT + +### Minor changes + +- Use `useNodeJSResolver: true` in `Import.resolvePackage` calls. + +## 0.14.13 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.14.12 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.14.11 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.14.10 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 0.14.9 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.14.8 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.14.7 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.14.6 +Fri, 07 Feb 2025 01:10:49 GMT + +### Patches + +- Extend heft-jest-plugin json schema to match HeftJestConfiguration + +## 0.14.5 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 0.14.4 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.14.3 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.14.2 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.14.1 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.14.0 +Tue, 10 Dec 2024 07:32:19 GMT + +### Minor changes + +- Inject `punycode` into the NodeJS module cache in Node versions 22 and above to work around a deprecation warning. + +## 0.13.3 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.13.2 +Tue, 03 Dec 2024 16:11:07 GMT + +_Version update only_ + +## 0.13.1 +Sat, 23 Nov 2024 01:18:55 GMT + +### Patches + +- Fix a bug in `jest-node-modules-symlink-resolver` with respect to evaluating paths that don't exist. Expected behavior in that situation is to return the input path. + +## 0.13.0 +Fri, 22 Nov 2024 01:10:43 GMT + +### Minor changes + +- Add a custom resolver that only resolves symlinks that are within node_modules. + +## 0.12.18 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.12.17 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.12.16 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.12.15 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.12.14 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.12.13 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.12.12 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.12.11 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.12.10 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.12.9 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.12.8 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.12.7 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.12.6 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.12.5 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 0.12.4 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 0.12.3 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.12.2 +Tue, 16 Jul 2024 00:36:22 GMT + +_Version update only_ + +## 0.12.1 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.12.0 +Tue, 11 Jun 2024 00:21:28 GMT + +### Minor changes + +- Update the test reporter to report unchecked snapshots. + +## 0.11.39 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.11.38 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.11.37 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 0.11.36 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.11.35 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.11.34 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.11.33 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.11.32 +Fri, 24 May 2024 00:15:09 GMT + +_Version update only_ + +## 0.11.31 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 0.11.30 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.11.29 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.11.28 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.11.27 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.11.26 +Wed, 08 May 2024 22:23:51 GMT + +_Version update only_ + +## 0.11.25 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 0.11.24 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.11.23 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 0.11.22 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 0.11.21 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 0.11.20 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 0.11.19 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 0.11.18 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 0.11.17 +Thu, 29 Feb 2024 07:11:45 GMT + +_Version update only_ + +## 0.11.16 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.11.15 +Mon, 26 Feb 2024 16:10:56 GMT + +### Patches + +- Make `@rushstack/terminal` a dependency because the reporter has a runtime dependency on that package. + +## 0.11.14 +Thu, 22 Feb 2024 05:54:17 GMT + +### Patches + +- Add a missing dependency on `@rushstack/terminal` + +## 0.11.13 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.11.12 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.11.11 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.11.10 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.11.9 +Tue, 20 Feb 2024 16:10:53 GMT + +_Version update only_ + +## 0.11.8 +Mon, 19 Feb 2024 21:54:26 GMT + +_Version update only_ + +## 0.11.7 +Sat, 17 Feb 2024 06:24:35 GMT + +_Version update only_ + +## 0.11.6 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 0.11.5 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.11.4 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.11.3 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.11.2 +Tue, 23 Jan 2024 20:12:58 GMT + +_Version update only_ + +## 0.11.1 +Tue, 23 Jan 2024 16:15:05 GMT + +_Version update only_ + +## 0.11.0 +Tue, 16 Jan 2024 18:30:10 GMT + +### Minor changes + +- Add support for TypeScript 5.3 + +## 0.10.8 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.10.7 +Wed, 20 Dec 2023 01:09:46 GMT + +_Version update only_ + +## 0.10.6 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.10.5 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.10.4 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 0.10.3 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 0.10.2 +Mon, 30 Oct 2023 23:36:38 GMT + +_Version update only_ + +## 0.10.1 +Thu, 26 Oct 2023 00:27:48 GMT + +### Patches + +- Add an option (`enableNodeEnvManagement`) to ensure that the NODE_ENV environment variable is set to `"test"` during test execution. + +## 0.10.0 +Mon, 23 Oct 2023 15:18:38 GMT + +### Minor changes + +- Use Jest verbose logging when `heft --debug test` or `heft test --verbose` is specified +- Fix an issue where `silent: true` was ignored when specified in `jest.config.json` + +## 0.9.9 +Sun, 01 Oct 2023 02:56:30 GMT + +_Version update only_ + +## 0.9.8 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 0.9.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.9.6 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 0.9.5 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 0.9.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.9.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 0.9.2 +Fri, 22 Sep 2023 00:05:50 GMT + +_Version update only_ + +## 0.9.1 +Tue, 19 Sep 2023 15:21:51 GMT + +_Version update only_ + +## 0.9.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Add a `--log-heap-usage` flag that includes memory usage analysis in each test run. +- Update @types/node from 14 to 18 + +### Patches + +- Wait for first test run to be scheduled in initial invocation in watch mode. + +## 0.8.1 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 0.8.0 +Mon, 31 Jul 2023 15:19:05 GMT + +### Minor changes + +- Make `jest-environment-jsdom` and `jest-environment-node` optional peerDependencies. + +## 0.7.18 +Sat, 29 Jul 2023 00:22:51 GMT + +_Version update only_ + +## 0.7.17 +Thu, 20 Jul 2023 20:47:29 GMT + +_Version update only_ + +## 0.7.16 +Wed, 19 Jul 2023 00:20:31 GMT + +_Version update only_ + +## 0.7.15 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 0.7.14 +Wed, 12 Jul 2023 15:20:40 GMT + +_Version update only_ + +## 0.7.13 +Fri, 07 Jul 2023 00:19:33 GMT + +_Version update only_ + +## 0.7.12 +Thu, 06 Jul 2023 00:16:20 GMT + +_Version update only_ + +## 0.7.11 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 0.7.10 +Thu, 15 Jun 2023 00:21:02 GMT + +_Version update only_ + +## 0.7.9 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 0.7.8 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 0.7.7 +Tue, 13 Jun 2023 01:49:01 GMT + +### Patches + +- Add support for using '-u' for the '--update-snapshots' parameter and '-t' for the '--test-name-pattern' parameter + +## 0.7.6 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 0.7.5 +Fri, 09 Jun 2023 15:23:15 GMT + +### Patches + +- Added --test-path-ignore-patterns support for subtractive test selection to complement existing additive support. + +## 0.7.4 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 0.7.3 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 0.7.2 +Thu, 08 Jun 2023 00:20:03 GMT + +_Version update only_ + +## 0.7.1 +Wed, 07 Jun 2023 22:45:17 GMT + +_Version update only_ + +## 0.7.0 +Tue, 06 Jun 2023 02:52:51 GMT + +### Minor changes + +- Adds a new base config for web projects, jest-web.config.json. Adds the "customExportConditions" field to both base configs with sensible defaults. ## 0.6.0 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/heft-plugins/heft-jest-plugin/UPGRADING.md b/heft-plugins/heft-jest-plugin/UPGRADING.md index db10be2aabc..300a23afa1a 100644 --- a/heft-plugins/heft-jest-plugin/UPGRADING.md +++ b/heft-plugins/heft-jest-plugin/UPGRADING.md @@ -1,5 +1,27 @@ # Upgrade notes for @rushstack/heft-jest-plugin +### Version 1.3.0 (jest-environment-jsdom 30.3.0) + +This release upgrades `jest-environment-jsdom` from 29.5.0 to 30.3.0, which ships with **jsdom 26** instead of jsdom 21. It also upgrades all other Jest packages (`@jest/core`, `jest-config`, etc.) to `~30.3.0`. + +The `punycode` injection workaround introduced in a prior release (which redirected `jest-environment-jsdom` through a patched wrapper to suppress `DEP0040` deprecation warnings on Node ≥ 22) has been removed. jsdom 26 no longer triggers `DEP0040`. + +**Breaking changes from Jest 29 → Jest 30 that may affect your tests:** + +- **`window.location` is now immutable.** `Object.defineProperty(window, 'location', { value: ... })` throws a `TypeError` in jsdom 26. Use `window.history.pushState()` or `window.history.replaceState()` to change the URL in tests instead. + +- **Deprecated matcher aliases have been removed.** Replace usages before upgrading: + - `expect(fn).toBeCalled()` → `expect(fn).toHaveBeenCalled()` + - `expect(fn).toBeCalledWith(...)` → `expect(fn).toHaveBeenCalledWith(...)` + - `expect(fn).toReturn()` → `expect(fn).toHaveReturned()` + - `expect(fn).toThrowError(msg)` → `expect(fn).toThrow(msg)` + +- **Snapshots must be regenerated.** The error `cause` property is now included in snapshot output. Run `jest --updateSnapshot` after upgrading. + +- **`toEqual()` no longer matches non-enumerable object properties** by default. + +- **Node.js minimum is 18** (drops Node 14, 16, 19, 21). TypeScript minimum is 5.4. + ### Version 0.6.0 BREAKING CHANGE: The `testFiles` option in `config/jest.config.json` should now specify the path to compiled CommonJS files, *not* TypeScript source files. Snapshot resolution depends on the presence of source maps in the output folder. diff --git a/heft-plugins/heft-jest-plugin/config/heft.json b/heft-plugins/heft-jest-plugin/config/heft.json new file mode 100644 index 00000000000..8d1359f022f --- /dev/null +++ b/heft-plugins/heft-jest-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-jest-plugin/config/jest.config.json b/heft-plugins/heft-jest-plugin/config/jest.config.json new file mode 100644 index 00000000000..7c0f9ccc9d6 --- /dev/null +++ b/heft-plugins/heft-jest-plugin/config/jest.config.json @@ -0,0 +1,3 @@ +{ + "extends": "decoupled-local-node-rig/profiles/default/config/jest.config.json" +} diff --git a/heft-plugins/heft-jest-plugin/config/rig.json b/heft-plugins/heft-jest-plugin/config/rig.json index 6ac88a96368..cc98dea43dd 100644 --- a/heft-plugins/heft-jest-plugin/config/rig.json +++ b/heft-plugins/heft-jest-plugin/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "decoupled-local-node-rig" } diff --git a/heft-plugins/heft-jest-plugin/eslint.config.js b/heft-plugins/heft-jest-plugin/eslint.config.js new file mode 100644 index 00000000000..e54effd122a --- /dev/null +++ b/heft-plugins/heft-jest-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-jest-plugin/heft-plugin.json b/heft-plugins/heft-jest-plugin/heft-plugin.json index 60d0926ce67..52c65540566 100644 --- a/heft-plugins/heft-jest-plugin/heft-plugin.json +++ b/heft-plugins/heft-jest-plugin/heft-plugin.json @@ -1,11 +1,11 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-plugin.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", "taskPlugins": [ { "pluginName": "jest-plugin", - "entryPoint": "./lib/JestPlugin", - "optionsSchema": "./lib/schemas/heft-jest-plugin.schema.json", + "entryPoint": "./lib-commonjs/JestPlugin", + "optionsSchema": "./lib-commonjs/schemas/heft-jest-plugin.schema.json", "parameterScope": "jest", "parameters": [ @@ -36,6 +36,11 @@ "argumentName": "SOURCE_FILE", "description": "Find and run the tests that cover a source file that was passed in as an argument. This corresponds to the \"--findRelatedTests\" parameter in Jest's documentation. This parameter is not compatible with watch mode." }, + { + "longName": "--log-heap-usage", + "parameterKind": "flag", + "description": "Logs the heap usage after every test. Useful to debug memory leaks. Use together with --expose-gc in node." + }, { "longName": "--max-workers", "parameterKind": "string", @@ -49,10 +54,17 @@ }, { "longName": "--test-name-pattern", + "shortName": "-t", "parameterKind": "string", "argumentName": "REGEXP", "description": "Run only tests with a name that matches a regular expression. The REGEXP is matched against the full name, which is a combination of the test name and all its surrounding describe blocks. This corresponds to the \"--testNamePattern\" parameter in Jest's documentation." }, + { + "longName": "--test-path-ignore-patterns", + "parameterKind": "string", + "argumentName": "REGEXP", + "description": "Avoid running tests with a source file path that matches one ore more regular expressions. On Windows you will need to use \"/\" instead of \"\\\". This corresponds to the \"--testPathIgnorePatterns\" parameter in Jest's documentation." + }, { "longName": "--test-path-pattern", "parameterKind": "string", @@ -67,6 +79,7 @@ }, { "longName": "--update-snapshots", + "shortName": "-u", "parameterKind": "flag", "description": "Update Jest snapshots while running the tests. This corresponds to the \"--updateSnapshots\" parameter in Jest." } diff --git a/heft-plugins/heft-jest-plugin/includes/jest-shared.config.json b/heft-plugins/heft-jest-plugin/includes/jest-shared.config.json index efee2086242..4c93c4da8fd 100644 --- a/heft-plugins/heft-jest-plugin/includes/jest-shared.config.json +++ b/heft-plugins/heft-jest-plugin/includes/jest-shared.config.json @@ -18,7 +18,7 @@ // "Adding '/lib' here enables lib/__mocks__ to be used for mocking Node.js system modules "roots": ["/lib"], - "testMatch": ["/lib/**/*.test.js"], + "testMatch": ["/lib/**/*.test.{cjs,js}"], "testPathIgnorePatterns": ["/node_modules/"], // Code coverage tracking is disabled by default; set this to true to enable it @@ -30,9 +30,9 @@ "coverageProvider": "v8", "collectCoverageFrom": [ - "lib/**/*.js", + "lib/**/*.{cjs,js}", "!lib/**/*.d.ts", - "!lib/**/*.test.js", + "!lib/**/*.test.{cjs,js}", "!lib/**/test/**", "!lib/**/__tests__/**", "!lib/**/__fixtures__/**", @@ -40,8 +40,11 @@ ], "coveragePathIgnorePatterns": ["/node_modules/"], + "testEnvironment": "jest-environment-node", + "testEnvironmentOptions": { - "url": "http://localhost/" + "url": "http://localhost/", + "customExportConditions": ["require", "node"] }, // Retain pre-Jest 29 snapshot behavior @@ -50,19 +53,19 @@ "printBasicPrototype": true }, - "snapshotResolver": "../lib/exports/jest-source-map-snapshot-resolver.js", + "snapshotResolver": "../lib-commonjs/exports/jest-source-map-snapshot-resolver.js", // Instruct jest not to run the transformer pipeline by default on JS files. The output files from TypeScript // will already be fully transformed, so this avoids redundant file system operations. "transformIgnorePatterns": ["\\.c?js$"], // jest-identity-mock-transform returns a proxy for exported key/value pairs, where Webpack would return a module - // jest-string-mock-transform returns the filename, where Webpack would return a URL + // jest-string-mock-transform returns the filename, relative to the current working directory, where Webpack would return a URL // When using the heft-jest-plugin, these will be replaced with the resolved module location "transform": { - "\\.(css|sass|scss)$": "../lib/exports/jest-identity-mock-transform.js", + "\\.(css|sass|scss)$": "../lib-commonjs/exports/jest-identity-mock-transform.js", - "\\.(aac|eot|gif|jpeg|jpg|m4a|mp3|mp4|oga|otf|png|svg|ttf|wav|webm|webp|woff|woff2)$": "../lib/exports/jest-string-mock-transform.js" + "\\.(aac|eot|gif|jpeg|jpg|m4a|mp3|mp4|oga|otf|png|svg|ttf|wav|webm|webp|woff|woff2)$": "../lib-commonjs/exports/jest-string-mock-transform.js" }, // The modulePathIgnorePatterns below accepts these sorts of paths: @@ -75,8 +78,5 @@ "moduleFileExtensions": ["cjs", "js", "json", "node"], // When using the heft-jest-plugin, these will be replaced with the resolved module location - "setupFiles": ["../lib/exports/jest-global-setup.js"], - - // When using the heft-jest-plugin, these will be replaced with the resolved module location - "resolver": "../lib/exports/jest-improved-resolver.js" + "resolver": "../lib-commonjs/exports/jest-improved-resolver.js" } diff --git a/heft-plugins/heft-jest-plugin/includes/jest-web.config.json b/heft-plugins/heft-jest-plugin/includes/jest-web.config.json new file mode 100644 index 00000000000..0780a8d2d2f --- /dev/null +++ b/heft-plugins/heft-jest-plugin/includes/jest-web.config.json @@ -0,0 +1,37 @@ +{ + "extends": "./jest-shared.config.json", + + "testEnvironment": "jest-environment-jsdom", + + "testEnvironmentOptions": { + "url": "http://localhost/", + + // For web projects, we write ESM output (with "import" statements") into the "lib" folder + // to be processed by Webpack or other tree-shaking bundlers. + // We also write CommonJS output (with "require()" calls") into the "lib-commonjs" folder + // to be processed by Jest. We do this because the Jest requires the --experimental-vm-modules flag + // in order to load ESM, and also because the interop story between CommonJS and ESM in NodeJs is + // very finicky. + // + // The jest-environment-jsdom package now sets "customExportConditions" to ["browser"], + // which often selects an ESM entry point when resolving packages. This is incorrect for + // our setup. The setting below fixes that. For details, refer to these docs: + // https://nodejs.org/api/packages.html#conditional-exports + "customExportConditions": ["require", "node", "umd"] + }, + + // For web projects, `lib/` is normally ESM, so we route to the CommonJS output in `lib-commonjs/` instead. + "roots": ["/lib-commonjs"], + + "testMatch": ["/lib-commonjs/**/*.test.js"], + + "collectCoverageFrom": [ + "lib-commonjs/**/*.js", + "!lib-commonjs/**/*.d.ts", + "!lib-commonjs/**/*.test.js", + "!lib-commonjs/**/test/**", + "!lib-commonjs/**/__tests__/**", + "!lib-commonjs/**/__fixtures__/**", + "!lib-commonjs/**/__mocks__/**" + ] +} diff --git a/heft-plugins/heft-jest-plugin/package.json b/heft-plugins/heft-jest-plugin/package.json index 5042b729188..21fbbdf342b 100644 --- a/heft-plugins/heft-jest-plugin/package.json +++ b/heft-plugins/heft-jest-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/heft-jest-plugin", - "version": "0.6.0", + "version": "2.0.12", "description": "Heft plugin for Jest", "repository": { "type": "git", @@ -10,38 +10,67 @@ "homepage": "https://rushstack.io/pages/heft/overview/", "license": "MIT", "scripts": { - "build": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged build --clean", - "start": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged test --clean --watch", - "_phase:build": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged build --clean", - "_phase:test": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged test --no-build" + "build": "heft test --clean", + "start": "heft build-watch", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" }, "peerDependencies": { - "@rushstack/heft": "^0.51.0" + "@rushstack/heft": "^1.2.22", + "@types/jest": "^30.0.0", + "jest-environment-jsdom": "^30.3.0", + "jest-environment-node": "^30.3.0" + }, + "peerDependenciesMeta": { + "@types/jest": { + "optional": true + }, + "jest-environment-jsdom": { + "optional": true + }, + "jest-environment-node": { + "optional": true + } }, "dependencies": { - "@jest/core": "~29.5.0", - "@jest/reporters": "~29.5.0", - "@jest/transform": "~29.5.0", + "@jest/core": "~30.3.0", + "@jest/reporters": "~30.3.0", + "@jest/transform": "~30.3.0", "@rushstack/heft-config-file": "workspace:*", "@rushstack/node-core-library": "workspace:*", - "jest-config": "~29.5.0", - "jest-resolve": "~29.5.0", - "jest-snapshot": "~29.5.0", - "lodash": "~4.17.15" + "@rushstack/terminal": "workspace:*", + "jest-config": "~30.3.0", + "jest-resolve": "~30.3.0", + "jest-snapshot": "~30.3.0" }, "devDependencies": { - "@jest/types": "29.5.0", - "@rushstack/eslint-config": "workspace:*", + "@jest/types": "30.3.0", "@rushstack/heft": "workspace:*", - "@rushstack/heft-legacy": "npm:@rushstack/heft@0.50.6", - "@rushstack/heft-node-rig": "1.13.0", - "@types/heft-jest": "1.0.1", - "@types/lodash": "4.14.116", - "@types/node": "14.18.36", - "eslint": "~8.7.0", - "jest-environment-jsdom": "~29.5.0", - "jest-environment-node": "~29.5.0", - "jest-watch-select-projects": "2.0.0", - "typescript": "~5.0.4" - } + "@types/node": "20.17.19", + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0", + "jest-environment-jsdom": "~30.3.0", + "jest-environment-node": "~30.3.0", + "jest-watch-select-projects": "2.0.0" + }, + "exports": { + "./includes/*.json": "./includes/*.json", + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "sideEffects": false } diff --git a/heft-plugins/heft-jest-plugin/src/HeftJestReporter.ts b/heft-plugins/heft-jest-plugin/src/HeftJestReporter.ts index 31accb2b54e..3f4e7089c3c 100644 --- a/heft-plugins/heft-jest-plugin/src/HeftJestReporter.ts +++ b/heft-plugins/heft-jest-plugin/src/HeftJestReporter.ts @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { ITerminal, Colors, InternalError, Text, IColorableSequence } from '@rushstack/node-core-library'; -import { +import * as path from 'node:path'; + +import type { Reporter, Test, TestResult, @@ -13,6 +13,8 @@ import { Config } from '@jest/reporters'; +import { InternalError, Text } from '@rushstack/node-core-library'; +import { type ITerminal, Colorize } from '@rushstack/terminal'; import type { HeftConfiguration, IScopedLogger } from '@rushstack/heft'; export interface IHeftJestReporterOptions { @@ -43,51 +45,74 @@ export default class HeftJestReporter implements Reporter { this._debugMode = options.debugMode; } + // eslint-disable-next-line @typescript-eslint/naming-convention public async onTestStart(test: Test): Promise { this._terminal.writeLine( - Colors.whiteBackground(Colors.black('START')), + Colorize.whiteBackground(Colorize.black('START')), ` ${this._getTestPath(test.path)}` ); } + // eslint-disable-next-line @typescript-eslint/naming-convention public async onTestResult( test: Test, testResult: TestResult, aggregatedResult: AggregatedResult ): Promise { this._writeConsoleOutput(testResult); - const { numPassingTests, numFailingTests, failureMessage, testExecError, perfStats } = testResult; + const { + numPassingTests, + numFailingTests, + failureMessage, + testExecError, + perfStats, + memoryUsage, + snapshot: { updated: updatedSnapshots, added: addedSnapshots, unchecked: uncheckedSnapshots } + } = testResult; // Calculate the suite duration time from the test result. This is necessary because Jest doesn't // provide the duration on the 'test' object (at least not as of Jest 25), and other reporters // (ex. jest-junit) only use perfStats: // https://github.com/jest-community/jest-junit/blob/12da1a20217a9b6f30858013175319c1256f5b15/utils/buildJsonResults.js#L112 const duration: string = perfStats ? `${((perfStats.end - perfStats.start) / 1000).toFixed(3)}s` : '?'; + + // calculate memoryUsage to MB reference -> https://jestjs.io/docs/cli#--logheapusage + const memUsage: string = memoryUsage ? `, ${Math.floor(memoryUsage / 1000000)}MB heap size` : ''; + const message: string = ` ${this._getTestPath(test.path)} ` + - `(duration: ${duration}, ${numPassingTests} passed, ${numFailingTests} failed)`; + `(duration: ${duration}, ${numPassingTests} passed, ${numFailingTests} failed${memUsage})`; if (numFailingTests > 0) { - this._terminal.writeLine(Colors.redBackground(Colors.black('FAIL')), message); + this._terminal.writeLine(Colorize.redBackground(Colorize.black('FAIL')), message); } else if (testExecError) { - this._terminal.writeLine(Colors.redBackground(Colors.black(`FAIL (${testExecError.type})`)), message); + this._terminal.writeLine( + Colorize.redBackground(Colorize.black(`FAIL (${testExecError.type})`)), + message + ); } else { - this._terminal.writeLine(Colors.greenBackground(Colors.black('PASS')), message); + this._terminal.writeLine(Colorize.greenBackground(Colorize.black('PASS')), message); } if (failureMessage) { this._terminal.writeErrorLine(failureMessage); } - if (testResult.snapshot.updated) { + if (updatedSnapshots) { this._terminal.writeErrorLine( - `Updated ${this._formatWithPlural(testResult.snapshot.updated, 'snapshot', 'snapshots')}` + `Updated ${this._formatWithPlural(updatedSnapshots, 'snapshot', 'snapshots')}` ); } - if (testResult.snapshot.added) { + if (addedSnapshots) { this._terminal.writeErrorLine( - `Added ${this._formatWithPlural(testResult.snapshot.added, 'snapshot', 'snapshots')}` + `Added ${this._formatWithPlural(addedSnapshots, 'snapshot', 'snapshots')}` + ); + } + + if (uncheckedSnapshots) { + this._terminal.writeWarningLine( + `${this._formatWithPlural(uncheckedSnapshots, 'snapshot was', 'snapshots were')} not checked` ); } } @@ -157,13 +182,14 @@ export default class HeftJestReporter implements Reporter { const PAD_LENGTH: number = 13; // "console.error" is the longest label const paddedLabel: string = '|' + label.padStart(PAD_LENGTH) + '|'; - const prefix: IColorableSequence = debug ? Colors.yellow(paddedLabel) : Colors.cyan(paddedLabel); + const prefix: string = debug ? Colorize.yellow(paddedLabel) : Colorize.cyan(paddedLabel); for (const line of lines) { this._terminal.writeLine(prefix, ' ' + line); } } + // eslint-disable-next-line @typescript-eslint/naming-convention public async onRunStart( aggregatedResult: AggregatedResult, options: ReporterOnStartOptions @@ -176,20 +202,33 @@ export default class HeftJestReporter implements Reporter { ); } + // eslint-disable-next-line @typescript-eslint/naming-convention public async onRunComplete(contexts: Set, results: AggregatedResult): Promise { - const { numPassedTests, numFailedTests, numTotalTests, numRuntimeErrorTestSuites } = results; + const { + numPassedTests, + numFailedTests, + numTotalTests, + numRuntimeErrorTestSuites, + snapshot: { uncheckedKeysByFile: uncheckedSnapshotsByFile } + } = results; this._terminal.writeLine(); this._terminal.writeLine('Tests finished:'); const successesText: string = ` Successes: ${numPassedTests}`; - this._terminal.writeLine(numPassedTests > 0 ? Colors.green(successesText) : successesText); + this._terminal.writeLine(numPassedTests > 0 ? Colorize.green(successesText) : successesText); const failText: string = ` Failures: ${numFailedTests}`; - this._terminal.writeLine(numFailedTests > 0 ? Colors.red(failText) : failText); + this._terminal.writeLine(numFailedTests > 0 ? Colorize.red(failText) : failText); if (numRuntimeErrorTestSuites) { - this._terminal.writeLine(Colors.red(` Failed test suites: ${numRuntimeErrorTestSuites}`)); + this._terminal.writeLine(Colorize.red(` Failed test suites: ${numRuntimeErrorTestSuites}`)); + } + + if (uncheckedSnapshotsByFile.length > 0) { + this._terminal.writeWarningLine( + ` Test suites with unchecked snapshots: ${uncheckedSnapshotsByFile.length}` + ); } this._terminal.writeLine(` Total: ${numTotalTests}`); diff --git a/heft-plugins/heft-jest-plugin/src/JestPlugin.ts b/heft-plugins/heft-jest-plugin/src/JestPlugin.ts index a8669344171..6ad53a10a48 100644 --- a/heft-plugins/heft-jest-plugin/src/JestPlugin.ts +++ b/heft-plugins/heft-jest-plugin/src/JestPlugin.ts @@ -4,13 +4,13 @@ // Load the Jest patches before anything else loads import './patches/jestWorkerPatch'; -import type { EventEmitter } from 'events'; -import * as path from 'path'; +import type { EventEmitter } from 'node:events'; +import * as path from 'node:path'; import type { AggregatedResult } from '@jest/reporters'; import type { Config } from '@jest/types'; import { resolveRunner, resolveSequencer, resolveTestEnvironment, resolveWatchPlugin } from 'jest-resolve'; -import { mergeWith, isObject } from 'lodash'; + import type { HeftConfiguration, IScopedLogger, @@ -24,24 +24,19 @@ import type { CommandLineStringListParameter } from '@rushstack/heft'; import { - ConfigurationFile, + ProjectConfigurationFile, type ICustomJsonPathMetadata, type IJsonPathMetadataResolverOptions, InheritanceType, PathResolutionMethod } from '@rushstack/heft-config-file'; -import { - FileSystem, - Path, - Import, - JsonFile, - PackageName, - type ITerminal -} from '@rushstack/node-core-library'; +import { FileSystem, Path, Import, JsonFile, Objects, PackageName } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; import type { IHeftJestReporterOptions } from './HeftJestReporter'; import { jestResolve } from './JestUtils'; import { TerminalWritableStream } from './TerminalWritableStream'; +import anythingSchema from './schemas/anything.schema.json'; const jestPluginSymbol: unique symbol = Symbol('heft-jest-plugin'); interface IWithJestPlugin { @@ -100,14 +95,17 @@ export interface IJestPluginOptions { detectOpenHandles?: boolean; disableCodeCoverage?: boolean; disableConfigurationModuleResolution?: boolean; + enableNodeEnvManagement?: boolean; findRelatedTests?: string[]; maxWorkers?: string; passWithNoTests?: boolean; silent?: boolean; testNamePattern?: string; + testPathIgnorePatterns?: string; testPathPattern?: string; testTimeout?: number; updateSnapshots?: boolean; + logHeapUsage?: boolean; } export interface IHeftJestConfiguration extends Config.InitialOptions {} @@ -124,6 +122,7 @@ const PLUGIN_NAME: 'jest-plugin' = 'jest-plugin'; const PLUGIN_PACKAGE_NAME: '@rushstack/heft-jest-plugin' = '@rushstack/heft-jest-plugin'; const PLUGIN_PACKAGE_FOLDER: string = path.resolve(__dirname, '..'); const JEST_CONFIGURATION_LOCATION: 'config/jest.config.json' = `config/jest.config.json`; +export const JEST_CONFIG_JSDOM_PACKAGE_NAME: 'jest-environment-jsdom' = 'jest-environment-jsdom'; const ROOTDIR_TOKEN: '' = ''; const CONFIGDIR_TOKEN: '' = ''; @@ -133,6 +132,8 @@ const JSONPATHPROPERTY_REGEX: RegExp = /^\$\['([^']+)'\]/; const JEST_CONFIG_PACKAGE_FOLDER: string = path.dirname(require.resolve('jest-config')); +let _jestConfigurationFileLoader: ProjectConfigurationFile | undefined; + interface IPendingTestRun { (): Promise; } @@ -141,8 +142,6 @@ interface IPendingTestRun { * @internal */ export default class JestPlugin implements IHeftTaskPlugin { - private static _jestConfigurationFileLoader: ConfigurationFile | undefined; - private _jestPromise: Promise | undefined; private _pendingTestRuns: Set = new Set(); private _executing: boolean = false; @@ -150,6 +149,16 @@ export default class JestPlugin implements IHeftTaskPlugin { private _jestOutputStream: TerminalWritableStream | undefined; private _changedFiles: Set = new Set(); private _requestRun!: () => void; + private _nodeEnvSet: boolean | undefined; + + private _resolveFirstRunQueued!: () => void; + private _firstRunQueuedPromise: Promise; + + public constructor() { + this._firstRunQueuedPromise = new Promise((resolve) => { + this._resolveFirstRunQueued = resolve; + }); + } public static getJestPlugin(object: object): JestPlugin | undefined { return (object as IWithJestPlugin)[jestPluginSymbol]; @@ -157,8 +166,6 @@ export default class JestPlugin implements IHeftTaskPlugin { /** * Setup the hooks and custom CLI options for the Jest plugin. - * - * @override */ public apply( taskSession: IHeftTaskSession, @@ -177,12 +184,16 @@ export default class JestPlugin implements IHeftTaskPlugin { const silentParameter: CommandLineFlagParameter = parameters.getFlagParameter('--silent'); const updateSnapshotsParameter: CommandLineFlagParameter = parameters.getFlagParameter('--update-snapshots'); + const logHeapUsageParameter: CommandLineFlagParameter = parameters.getFlagParameter('--log-heap-usage'); // Strings const configParameter: CommandLineStringParameter = parameters.getStringParameter('--config'); const maxWorkersParameter: CommandLineStringParameter = parameters.getStringParameter('--max-workers'); const testNamePatternParameter: CommandLineStringParameter = parameters.getStringParameter('--test-name-pattern'); + const testPathIgnorePatternsParameter: CommandLineStringParameter = parameters.getStringParameter( + '--test-path-ignore-patterns' + ); const testPathPatternParameter: CommandLineStringParameter = parameters.getStringParameter('--test-path-pattern'); @@ -194,12 +205,13 @@ export default class JestPlugin implements IHeftTaskPlugin { const testTimeoutParameter: CommandLineIntegerParameter = parameters.getIntegerParameter('--test-timeout-ms'); - const combinedOptions: IJestPluginOptions = { + const options: IJestPluginOptions = { ...pluginOptions, configurationPath: configParameter.value || pluginOptions?.configurationPath, debugHeftReporter: debugHeftReporterParameter.value || pluginOptions?.debugHeftReporter, detectOpenHandles: detectOpenHandlesParameter.value || pluginOptions?.detectOpenHandles, disableCodeCoverage: disableCodeCoverageParameter.value || pluginOptions?.disableCodeCoverage, + logHeapUsage: logHeapUsageParameter.value || pluginOptions?.logHeapUsage, findRelatedTests: findRelatedTestsParameter.values.length ? Array.from(findRelatedTestsParameter.values) : pluginOptions?.findRelatedTests, @@ -208,13 +220,15 @@ export default class JestPlugin implements IHeftTaskPlugin { passWithNoTests: true, silent: silentParameter.value || pluginOptions?.silent, testNamePattern: testNamePatternParameter.value || pluginOptions?.testNamePattern, + testPathIgnorePatterns: testPathIgnorePatternsParameter.value || pluginOptions?.testPathIgnorePatterns, testPathPattern: testPathPatternParameter.value || pluginOptions?.testPathPattern, testTimeout: testTimeoutParameter.value ?? pluginOptions?.testTimeout, - updateSnapshots: updateSnapshotsParameter.value || pluginOptions?.updateSnapshots + updateSnapshots: updateSnapshotsParameter.value || pluginOptions?.updateSnapshots, + enableNodeEnvManagement: pluginOptions?.enableNodeEnvManagement ?? true }; taskSession.hooks.run.tapPromise(PLUGIN_NAME, async (runOptions: IHeftTaskRunHookOptions) => { - await this._runJestAsync(taskSession, heftConfiguration, combinedOptions); + await this._runJestAsync(taskSession, heftConfiguration, options); }); taskSession.hooks.runIncremental.tapPromise( @@ -223,7 +237,7 @@ export default class JestPlugin implements IHeftTaskPlugin { await this._runJestWatchAsync( taskSession, heftConfiguration, - combinedOptions, + options, runIncrementalOptions.requestRun ); } @@ -241,6 +255,8 @@ export default class JestPlugin implements IHeftTaskPlugin { const logger: IScopedLogger = taskSession.logger; const terminal: ITerminal = logger.terminal; + this._setNodeEnvIfRequested(options, logger); + const { getVersion, runCLI } = await import(`@jest/core`); terminal.writeLine(`Using Jest version ${getVersion()}`); @@ -263,6 +279,8 @@ export default class JestPlugin implements IHeftTaskPlugin { results: jestResults } = await runCLI(jestArgv, [buildFolderPath]); + this._resetNodeEnv(); + if (jestResults.numFailedTests > 0) { logger.emitError( new Error( @@ -306,6 +324,7 @@ export default class JestPlugin implements IHeftTaskPlugin { readConfigs: IReadConfigs; } = require(jestConfigPath); const { readConfigs: originalReadConfigs } = jestConfigModule; + // eslint-disable-next-line func-style const readConfigs: typeof originalReadConfigs = async function wrappedReadConfigs( this: void, argv: Config.Argv, @@ -320,7 +339,7 @@ export default class JestPlugin implements IHeftTaskPlugin { }); return { - // There are other propeties on this object + // There are other properties on this object ...result, globalConfig: extendedGlobalConfig }; @@ -339,6 +358,7 @@ export default class JestPlugin implements IHeftTaskPlugin { } = require(watchModulePath); const { default: originalWatch } = watchModule; + // eslint-disable-next-line func-style const watch: IJestWatch = function patchedWatch( this: void, initialGlobalConfig: Config.GlobalConfig, @@ -388,6 +408,7 @@ export default class JestPlugin implements IHeftTaskPlugin { default: (params: IRunJestParams) => Promise; } = require(runJestModulePath); const { default: originalRunJest } = runJestModule; + // eslint-disable-next-line func-style const runJest: typeof originalRunJest = function patchedRunJest( this: void, params: IRunJestParams @@ -436,6 +457,7 @@ export default class JestPlugin implements IHeftTaskPlugin { return result; }); + host._resolveFirstRunQueued(); }); }; @@ -463,7 +485,14 @@ export default class JestPlugin implements IHeftTaskPlugin { this._jestPromise = runCLI(jestArgv, [buildFolderPath]); } + // Wait for the initial run to be queued. + await this._firstRunQueuedPromise; + // Explicitly wait an async tick for any file watchers + await Promise.resolve(); + if (pendingTestRuns.size > 0) { + this._setNodeEnvIfRequested(options, logger); + this._executing = true; for (const pendingTestRun of pendingTestRuns) { pendingTestRuns.delete(pendingTestRun); @@ -489,6 +518,8 @@ export default class JestPlugin implements IHeftTaskPlugin { } } + this._resetNodeEnv(); + if (!logger.hasErrors) { // If we ran tests and they succeeded, consider the files to no longer be changed. // This might be overly-permissive, but there isn't a great way to identify if the changes @@ -549,11 +580,24 @@ export default class JestPlugin implements IHeftTaskPlugin { jestConfig.displayName = heftConfiguration.projectPackageJson.name; } + let silent: boolean | undefined; + if (taskSession.parameters.verbose || taskSession.parameters.debug) { + // If Heft's "--verbose" or "--debug" parameters were used, then we're debugging Jest problems, + // so we always want to see "console.log()" even if jest.config.json asked to suppress it. + // If someone really dislikes that, we could expose "--silent" in the Heft CLI, + // but it is a confusing combination. + silent = false; + } else { + // If "silent" is specified via IJestPluginOptions, that takes precedence over jest.config.json + silent = options.silent ?? jestConfig.silent ?? false; + } + const jestArgv: Config.Argv = { // In debug mode, avoid forking separate processes that are difficult to debug runInBand: taskSession.parameters.debug, debug: taskSession.parameters.debug, detectOpenHandles: options.detectOpenHandles || false, + logHeapUsage: options.logHeapUsage || false, // Use the temp folder. Cache is unreliable, so we want it cleared on every --clean run cacheDirectory: taskSession.tempFolderPath, @@ -562,9 +606,26 @@ export default class JestPlugin implements IHeftTaskPlugin { listTests: false, rootDir: buildFolderPath, - silent: options.silent || false, + // What these fields mean for Jest: + // + // If "silent" is true: + // - Jest discards all console.log() output and there is no way to retrieve it + // + // If "silent" is false and "verbose" is false: + // - Jest uses BufferedConsole which doesn't show console.log() until after the test run completes, + // which is annoying in the debugger. The output is formatted nicely using HeftJestReporter. + // + // If "silent" is false and "verbose" is true: + // - Jest uses CustomConsole which logs immediately, but shows ugly call stacks with each log. + // + // If "verbose" is true (regardless of "silent"): + // - Jest reports include detailed results for every test, even if all tests passed within a test suite. + silent, + verbose: taskSession.parameters.verbose || taskSession.parameters.debug, + testNamePattern: options.testNamePattern, - testPathPattern: options.testPathPattern ? [options.testPathPattern] : undefined, + testPathIgnorePatterns: options.testPathIgnorePatterns ? [options.testPathIgnorePatterns] : undefined, + testPathPatterns: options.testPathPattern ? [options.testPathPattern] : undefined, testTimeout: options.testTimeout, maxWorkers: options.maxWorkers, @@ -580,13 +641,12 @@ export default class JestPlugin implements IHeftTaskPlugin { if (!options.debugHeftReporter) { // Extract the reporters and transform to include the Heft reporter by default - (jestArgv as unknown as { reporters: JestReporterConfig[] }).reporters = - JestPlugin._extractHeftJestReporters( - taskSession, - heftConfiguration, - jestConfig, - projectRelativeFilePath - ); + (jestArgv as unknown as { reporters: JestReporterConfig[] }).reporters = _extractHeftJestReporters( + taskSession, + heftConfiguration, + jestConfig, + projectRelativeFilePath + ); } else { logger.emitWarning( new Error('The "--debug-heft-reporter" parameter was specified; disabling HeftJestReporter') @@ -615,16 +675,12 @@ export default class JestPlugin implements IHeftTaskPlugin { public static _getJestConfigurationLoader( buildFolder: string, projectRelativeFilePath: string - ): ConfigurationFile { - if (!JestPlugin._jestConfigurationFileLoader) { - // Bypass Jest configuration validation - const schemaPath: string = `${__dirname}/schemas/anything.schema.json`; - + ): ProjectConfigurationFile { + if (!_jestConfigurationFileLoader) { // By default, ConfigurationFile will replace all objects, so we need to provide merge functions for these const shallowObjectInheritanceFunc: | undefined>( currentObject: T, parentObject: T - // eslint-disable-next-line @typescript-eslint/no-explicit-any ) => T = (currentObject: T, parentObject?: T): T => { // Merged in this order to ensure that the currentObject properties take priority in order-of-definition, // since Jest executes them in this order. For example, if the extended Jest configuration contains a @@ -642,30 +698,35 @@ export default class JestPlugin implements IHeftTaskPlugin { currentObject: T, parentObject: T ) => T = (currentObject: T, parentObject: T): T => { - return mergeWith(parentObject || {}, currentObject || {}, (value: T, source: T) => { + return Objects.mergeWith(parentObject || {}, currentObject || {}, (value, source) => { // Need to use a custom inheritance function instead of "InheritanceType.merge" since // some properties are allowed to have different types which may be incompatible with // merging. - if (!isObject(source)) { + if (source === null || typeof source !== 'object') { return source; } - return Array.isArray(value) ? [...value, ...(source as Array)] : { ...value, ...source }; + if (Array.isArray(source)) { + return Array.isArray(value) ? [...value, ...source] : source; + } + if (Objects.isRecord(source)) { + return { ...(Objects.isRecord(value) ? value : {}), ...source }; + } + return source; }) as T; }; - const tokenResolveMetadata: ICustomJsonPathMetadata = - JestPlugin._getJsonPathMetadata({ - rootDir: buildFolder - }); - const jestResolveMetadata: ICustomJsonPathMetadata = - JestPlugin._getJsonPathMetadata({ - rootDir: buildFolder, - resolveAsModule: true - }); + const tokenResolveMetadata: ICustomJsonPathMetadata = _getJsonPathMetadata({ + rootDir: buildFolder + }); + const jestResolveMetadata: ICustomJsonPathMetadata = _getJsonPathMetadata({ + rootDir: buildFolder, + resolveAsModule: true + }); - JestPlugin._jestConfigurationFileLoader = new ConfigurationFile({ + _jestConfigurationFileLoader = new ProjectConfigurationFile({ projectRelativeFilePath: projectRelativeFilePath, - jsonSchemaPath: schemaPath, + // Bypass Jest configuration validation + jsonSchemaObject: anythingSchema, propertyInheritance: { moduleNameMapper: { inheritanceType: InheritanceType.custom, @@ -732,260 +793,281 @@ export default class JestPlugin implements IHeftTaskPlugin { }); } - return JestPlugin._jestConfigurationFileLoader; + return _jestConfigurationFileLoader; } - private static _extractHeftJestReporters( - taskSession: IHeftTaskSession, - heftConfiguration: HeftConfiguration, - config: IHeftJestConfiguration, - projectRelativeFilePath: string - ): JestReporterConfig[] { - let isUsingHeftReporter: boolean = false; - - const logger: IScopedLogger = taskSession.logger; - const terminal: ITerminal = logger.terminal; - const reporterOptions: IHeftJestReporterOptions = { - heftConfiguration, - logger, - debugMode: taskSession.parameters.debug - }; - if (Array.isArray(config.reporters)) { - // Harvest all the array indices that need to modified before altering the array - const heftReporterIndices: number[] = JestPlugin._findIndexes(config.reporters, 'default'); - - // Replace 'default' reporter with the heft reporter - // This may clobber default reporters options - if (heftReporterIndices.length > 0) { - const heftReporter: Config.ReporterConfig = JestPlugin._getHeftJestReporterConfig(reporterOptions); - for (const index of heftReporterIndices) { - config.reporters[index] = heftReporter; + private _setNodeEnvIfRequested(options: IJestPluginOptions, logger: IScopedLogger): void { + if (options.enableNodeEnvManagement) { + if (process.env.NODE_ENV) { + if (process.env.NODE_ENV !== 'test') { + // In the future, we may consider just setting this and not warning + logger.emitWarning( + new Error(`NODE_ENV variable is set and it's not "test". NODE_ENV=${process.env.NODE_ENV}`) + ); } - isUsingHeftReporter = true; + } else { + process.env.NODE_ENV = 'test'; + this._nodeEnvSet = true; } - } else if (typeof config.reporters === 'undefined' || config.reporters === null) { - // Otherwise if no reporters are specified install only the heft reporter - config.reporters = [JestPlugin._getHeftJestReporterConfig(reporterOptions)]; - isUsingHeftReporter = true; - } else { - // Making a note if Heft cannot understand the reporter entry in Jest config - // Not making this an error or warning because it does not warrant blocking a dev or CI test pass - // If the Jest config is truly wrong Jest itself is in a better position to report what is wrong with the config - terminal.writeVerboseLine( - `The 'reporters' entry in Jest config '${projectRelativeFilePath}' is in an unexpected format. Was ` + - 'expecting an array of reporters' - ); } + } - if (!isUsingHeftReporter) { - terminal.writeVerboseLine( - `HeftJestReporter was not specified in Jest config '${projectRelativeFilePath}'. Consider adding a ` + - "'default' entry in the reporters array." - ); + private _resetNodeEnv(): void { + // unset the NODE_ENV only if we have set it + if (this._nodeEnvSet) { + delete process.env.NODE_ENV; } + } +} - // Since we're injecting the HeftConfiguration, we need to pass these args directly and not through serialization - const reporters: JestReporterConfig[] = config.reporters; - config.reporters = undefined; - return reporters; +function _extractHeftJestReporters( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + config: IHeftJestConfiguration, + projectRelativeFilePath: string +): JestReporterConfig[] { + let isUsingHeftReporter: boolean = false; + + const logger: IScopedLogger = taskSession.logger; + const terminal: ITerminal = logger.terminal; + const reporterOptions: IHeftJestReporterOptions = { + heftConfiguration, + logger, + debugMode: taskSession.parameters.debug + }; + if (Array.isArray(config.reporters)) { + // Harvest all the array indices that need to modified before altering the array + const heftReporterIndices: number[] = _findIndexes(config.reporters, 'default'); + + // Replace 'default' reporter with the heft reporter + // This may clobber default reporters options + if (heftReporterIndices.length > 0) { + const heftReporter: Config.ReporterConfig = _getHeftJestReporterConfig(reporterOptions); + for (const index of heftReporterIndices) { + config.reporters[index] = heftReporter; + } + isUsingHeftReporter = true; + } + } else if (typeof config.reporters === 'undefined' || config.reporters === null) { + // Otherwise if no reporters are specified install only the heft reporter + config.reporters = [_getHeftJestReporterConfig(reporterOptions)]; + isUsingHeftReporter = true; + } else { + // Making a note if Heft cannot understand the reporter entry in Jest config + // Not making this an error or warning because it does not warrant blocking a dev or CI test pass + // If the Jest config is truly wrong Jest itself is in a better position to report what is wrong with the config + terminal.writeVerboseLine( + `The 'reporters' entry in Jest config '${projectRelativeFilePath}' is in an unexpected format. Was ` + + 'expecting an array of reporters' + ); } - /** - * Returns the reporter config using the HeftJestReporter and the provided options. - */ - private static _getHeftJestReporterConfig( - reporterOptions: IHeftJestReporterOptions - ): Config.ReporterConfig { - return [ - `${__dirname}/HeftJestReporter.js`, - reporterOptions as Record - ]; + if (!isUsingHeftReporter) { + terminal.writeVerboseLine( + `HeftJestReporter was not specified in Jest config '${projectRelativeFilePath}'. Consider adding a ` + + "'default' entry in the reporters array." + ); } - /** - * Resolve all specified properties to an absolute path using Jest resolution. In addition, the following - * transforms will be applied to the provided propertyValue before resolution: - * - replace `` with the same rootDir - * - replace `` with the directory containing the current configuration file - * - replace `` with the path to the resolved package (NOT module) - */ - private static _getJsonPathMetadata( - options: IJestResolutionOptions - ): ICustomJsonPathMetadata { - return { - customResolver: (resolverOptions: IJsonPathMetadataResolverOptions) => { - const { propertyName, configurationFilePath, configurationFile } = resolverOptions; - let { propertyValue } = resolverOptions; - - const configDir: string = path.dirname(configurationFilePath); - const parsedPropertyName: string | undefined = propertyName?.match(JSONPATHPROPERTY_REGEX)?.[1]; - - function requireResolveFunction(request: string): string { - return require.resolve(request, { - paths: [configDir, PLUGIN_PACKAGE_FOLDER, JEST_CONFIG_PACKAGE_FOLDER] - }); - } + // Since we're injecting the HeftConfiguration, we need to pass these args directly and not through serialization + const reporters: JestReporterConfig[] = config.reporters; + config.reporters = undefined; + return reporters; +} - // Compare with replaceRootDirInPath() from here: - // https://github.com/facebook/jest/blob/5f4dd187d89070d07617444186684c20d9213031/packages/jest-config/src/utils.ts#L58 - if (propertyValue.startsWith(ROOTDIR_TOKEN)) { - // Example: /path/to/file.js - const restOfPath: string = path.normalize('./' + propertyValue.slice(ROOTDIR_TOKEN.length)); - propertyValue = path.resolve(options.rootDir, restOfPath); - } else if (propertyValue.startsWith(CONFIGDIR_TOKEN)) { - // Example: /path/to/file.js - const restOfPath: string = path.normalize('./' + propertyValue.slice(CONFIGDIR_TOKEN.length)); - propertyValue = path.resolve(configDir, restOfPath); - } else { - // Example: /path/to/file.js - const packageDirMatches: RegExpExecArray | null = PACKAGEDIR_REGEX.exec(propertyValue); - if (packageDirMatches !== null) { - const packageName: string | undefined = packageDirMatches.groups?.[PACKAGE_CAPTUREGROUP]; - if (!packageName) { - throw new Error( - `Could not parse package name from "packageDir" token ` + - (parsedPropertyName ? `of property "${parsedPropertyName}" ` : '') + - `in "${configDir}".` - ); - } +/** + * Returns the reporter config using the HeftJestReporter and the provided options. + */ +function _getHeftJestReporterConfig(reporterOptions: IHeftJestReporterOptions): Config.ReporterConfig { + return [ + `${__dirname}/HeftJestReporter.js`, + reporterOptions as Record + ]; +} - if (!PackageName.isValidName(packageName)) { - throw new Error( - `Module paths are not supported when using the "packageDir" token ` + - (parsedPropertyName ? `of property "${parsedPropertyName}" ` : '') + - `in "${configDir}". Only a package name is allowed.` - ); - } +/** + * Resolve all specified properties to an absolute path using Jest resolution. In addition, the following + * transforms will be applied to the provided propertyValue before resolution: + * - replace `` with the same rootDir + * - replace `` with the directory containing the current configuration file + * - replace `` with the path to the resolved package (NOT module) + */ +function _getJsonPathMetadata( + options: IJestResolutionOptions +): ICustomJsonPathMetadata { + return { + customResolver: (resolverOptions: IJsonPathMetadataResolverOptions) => { + const { propertyName, configurationFilePath, configurationFile } = resolverOptions; + let { propertyValue } = resolverOptions; + + const configDir: string = path.dirname(configurationFilePath); + const parsedPropertyName: string | undefined = propertyName?.match(JSONPATHPROPERTY_REGEX)?.[1]; + + function requireResolveFunction(request: string): string { + return require.resolve(request, { + paths: [configDir, PLUGIN_PACKAGE_FOLDER, JEST_CONFIG_PACKAGE_FOLDER] + }); + } - // Resolve to the package directory (not the module referenced by the package). The normal resolution - // method will generally not be able to find @rushstack/heft-jest-plugin from a project that is - // using a rig. Since it is important, and it is our own package, we resolve it manually as a special - // case. - const resolvedPackagePath: string = - packageName === PLUGIN_PACKAGE_NAME - ? PLUGIN_PACKAGE_FOLDER - : Import.resolvePackage({ baseFolderPath: configDir, packageName }); - // First entry is the entire match - const restOfPath: string = path.normalize( - './' + propertyValue.slice(packageDirMatches[0].length) + // Compare with replaceRootDirInPath() from here: + // https://github.com/facebook/jest/blob/5f4dd187d89070d07617444186684c20d9213031/packages/jest-config/src/utils.ts#L58 + if (propertyValue.startsWith(ROOTDIR_TOKEN)) { + // Example: /path/to/file.js + const restOfPath: string = path.normalize('./' + propertyValue.slice(ROOTDIR_TOKEN.length)); + propertyValue = path.resolve(options.rootDir, restOfPath); + } else if (propertyValue.startsWith(CONFIGDIR_TOKEN)) { + // Example: /path/to/file.js + const restOfPath: string = path.normalize('./' + propertyValue.slice(CONFIGDIR_TOKEN.length)); + propertyValue = path.resolve(configDir, restOfPath); + } else { + // Example: /path/to/file.js + const packageDirMatches: RegExpExecArray | null = PACKAGEDIR_REGEX.exec(propertyValue); + if (packageDirMatches !== null) { + const packageName: string | undefined = packageDirMatches.groups?.[PACKAGE_CAPTUREGROUP]; + if (!packageName) { + throw new Error( + `Could not parse package name from "packageDir" token ` + + (parsedPropertyName ? `of property "${parsedPropertyName}" ` : '') + + `in "${configDir}".` ); - propertyValue = path.resolve(resolvedPackagePath, restOfPath); } - } - // Return early, since the remainder of this function is used to resolve module paths - if (!options.resolveAsModule) { - return propertyValue; - } + if (!PackageName.isValidName(packageName)) { + throw new Error( + `Module paths are not supported when using the "packageDir" token ` + + (parsedPropertyName ? `of property "${parsedPropertyName}" ` : '') + + `in "${configDir}". Only a package name is allowed.` + ); + } - // Example: @rushstack/heft-jest-plugin - if (propertyValue === PLUGIN_PACKAGE_NAME) { - return PLUGIN_PACKAGE_FOLDER; + // Resolve to the package directory (not the module referenced by the package). The normal resolution + // method will generally not be able to find @rushstack/heft-jest-plugin from a project that is + // using a rig. Since it is important, and it is our own package, we resolve it manually as a special + // case. + const resolvedPackagePath: string = + packageName === PLUGIN_PACKAGE_NAME + ? PLUGIN_PACKAGE_FOLDER + : Import.resolvePackage({ baseFolderPath: configDir, packageName, useNodeJSResolver: true }); + // First entry is the entire match + const restOfPath: string = path.normalize('./' + propertyValue.slice(packageDirMatches[0].length)); + propertyValue = path.resolve(resolvedPackagePath, restOfPath); } + } - // Example: @rushstack/heft-jest-plugin/path/to/file.js - if (propertyValue.startsWith(PLUGIN_PACKAGE_NAME)) { - const restOfPath: string = path.normalize('./' + propertyValue.slice(PLUGIN_PACKAGE_NAME.length)); - return path.join(PLUGIN_PACKAGE_FOLDER, restOfPath); - } + // Return early, since the remainder of this function is used to resolve module paths + if (!options.resolveAsModule) { + return propertyValue; + } - // Use the Jest-provided resolvers to resolve the module paths - switch (parsedPropertyName) { - case 'testRunner': - return resolveRunner(/*resolver:*/ undefined, { - rootDir: configDir, - filePath: propertyValue, - requireResolveFunction - }); - case 'testSequencer': - return resolveSequencer(/*resolver:*/ undefined, { - rootDir: configDir, - filePath: propertyValue, - requireResolveFunction - }); - case 'testEnvironment': - return resolveTestEnvironment({ - rootDir: configDir, - testEnvironment: propertyValue, - requireResolveFunction - }); - case 'watchPlugins': - return resolveWatchPlugin(/*resolver:*/ undefined, { - rootDir: configDir, - filePath: propertyValue, - requireResolveFunction - }); - case 'preset': - // Do not allow use of presets and extends together, since that would create a - // confusing heirarchy. - if ( - configurationFile.preset && - (configurationFile as IHeftJestConfigurationWithExtends).extends - ) { - throw new Error( - `The configuration file at "${configurationFilePath}" cannot specify both "preset" and ` + - `"extends" properties.` - ); - } + // Example: @rushstack/heft-jest-plugin + if (propertyValue === PLUGIN_PACKAGE_NAME) { + return PLUGIN_PACKAGE_FOLDER; + } - // Preset is an odd value, since it can either be a relative path to a preset module - // from the rootDir, or a path to the parent directory of a preset module. So to - // determine which it is, we will attempt to resolve it as a module from the rootDir, - // as per the spec. If it resolves, then we will return the relative path to the - // resolved value from the rootDir. If it does not resolve, then we will return the - // original value to allow Jest to resolve within the target directory. - // See: https://github.com/jestjs/jest/blob/268afca708199c0e64ef26f35995907faf4454ff/packages/jest-config/src/normalize.ts#L123 - // eslint-disable-next-line @rushstack/no-null - let resolvedValue: string | null | undefined; - try { - resolvedValue = jestResolve(/*resolver:*/ undefined, { - rootDir: options.rootDir, - filePath: propertyValue, - key: propertyName - }); - } catch (e) { - // Swallow - } - if (resolvedValue) { - // Jest will resolve relative module paths to files only if they use forward slashes. - // They must also start with a '.' otherwise the preset resolution will assume it is a - // folder path and will path.join() it with the default 'jest-preset' filename. - // See: https://github.com/jestjs/jest/blob/268afca708199c0e64ef26f35995907faf4454ff/packages/jest-config/src/normalize.ts#L123 - return Path.convertToSlashes(`./${path.relative(options.rootDir, resolvedValue)}`); - } else { - return propertyValue; - } - default: - // We know the value will be non-null since resolve will throw an error if it is null - // and non-optional - return jestResolve(/*resolver:*/ undefined, { - rootDir: configDir, + // Example: @rushstack/heft-jest-plugin/path/to/file.js + if (propertyValue.startsWith(PLUGIN_PACKAGE_NAME)) { + const restOfPath: string = path.normalize('./' + propertyValue.slice(PLUGIN_PACKAGE_NAME.length)); + return path.join(PLUGIN_PACKAGE_FOLDER, restOfPath); + } + + // Use the Jest-provided resolvers to resolve the module paths + switch (parsedPropertyName) { + case 'testRunner': + return resolveRunner(/*resolver:*/ undefined, { + rootDir: configDir, + filePath: propertyValue, + requireResolveFunction + }); + + case 'testSequencer': + return resolveSequencer(/*resolver:*/ undefined, { + rootDir: configDir, + filePath: propertyValue, + requireResolveFunction + }); + + case 'testEnvironment': + return resolveTestEnvironment({ + rootDir: configDir, + testEnvironment: propertyValue, + requireResolveFunction + }); + + case 'watchPlugins': + return resolveWatchPlugin(/*resolver:*/ undefined, { + rootDir: configDir, + filePath: propertyValue, + requireResolveFunction + }); + + case 'preset': + // Do not allow use of presets and extends together, since that would create a + // confusing hierarchy. + if (configurationFile.preset && (configurationFile as IHeftJestConfigurationWithExtends).extends) { + throw new Error( + `The configuration file at "${configurationFilePath}" cannot specify both "preset" and ` + + `"extends" properties.` + ); + } + + // Preset is an odd value, since it can either be a relative path to a preset module + // from the rootDir, or a path to the parent directory of a preset module. So to + // determine which it is, we will attempt to resolve it as a module from the rootDir, + // as per the spec. If it resolves, then we will return the relative path to the + // resolved value from the rootDir. If it does not resolve, then we will return the + // original value to allow Jest to resolve within the target directory. + // See: https://github.com/jestjs/jest/blob/268afca708199c0e64ef26f35995907faf4454ff/packages/jest-config/src/normalize.ts#L123 + + let resolvedValue: string | null | undefined; + try { + resolvedValue = jestResolve(/*resolver:*/ undefined, { + rootDir: options.rootDir, filePath: propertyValue, key: propertyName - })!; - } - }, - pathResolutionMethod: PathResolutionMethod.custom - }; - } + }); + } catch (e) { + // Swallow + } + if (resolvedValue) { + // Jest will resolve relative module paths to files only if they use forward slashes. + // They must also start with a '.' otherwise the preset resolution will assume it is a + // folder path and will path.join() it with the default 'jest-preset' filename. + // See: https://github.com/jestjs/jest/blob/268afca708199c0e64ef26f35995907faf4454ff/packages/jest-config/src/normalize.ts#L123 + return Path.convertToSlashes(`./${path.relative(options.rootDir, resolvedValue)}`); + } else { + return propertyValue; + } - /** - * Finds the indices of jest reporters with a given name - */ - private static _findIndexes(items: JestReporterConfig[], search: string): number[] { - const result: number[] = []; + default: + // We know the value will be non-null since resolve will throw an error if it is null + // and non-optional + return jestResolve(/*resolver:*/ undefined, { + rootDir: configDir, + filePath: propertyValue, + key: propertyName + })!; + } + }, + pathResolutionMethod: PathResolutionMethod.custom + }; +} - for (let index: number = 0; index < items.length; index++) { - const item: JestReporterConfig = items[index]; +/** + * Finds the indices of jest reporters with a given name + */ +function _findIndexes(items: JestReporterConfig[], search: string): number[] { + const result: number[] = []; - // Item is either a string or a tuple of [reporterName: string, options: unknown] - if (item === search) { - result.push(index); - } else if (typeof item !== 'undefined' && item !== null && item[0] === search) { - result.push(index); - } - } + for (let index: number = 0; index < items.length; index++) { + const item: JestReporterConfig = items[index]; - return result; + // Item is either a string or a tuple of [reporterName: string, options: unknown] + if (item === search) { + result.push(index); + } else if (typeof item !== 'undefined' && item !== null && item[0] === search) { + result.push(index); + } } + + return result; } diff --git a/heft-plugins/heft-jest-plugin/src/JestRealPathPatch.ts b/heft-plugins/heft-jest-plugin/src/JestRealPathPatch.ts new file mode 100644 index 00000000000..7b596eb6853 --- /dev/null +++ b/heft-plugins/heft-jest-plugin/src/JestRealPathPatch.ts @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { RealNodeModulePathResolver } from '@rushstack/node-core-library/lib/RealNodeModulePath'; +import type { IPackageJson } from '@rushstack/node-core-library'; + +const jestResolvePackageFolder: string = path.dirname(require.resolve('jest-resolve/package.json')); + +const jestUtilPackageFolder: string = path.dirname( + require.resolve('jest-util/package.json', { paths: [jestResolvePackageFolder] }) +); + +const { realNodeModulePath }: RealNodeModulePathResolver = new RealNodeModulePathResolver(); + +const customTryRealpath = (input: string): string => { + try { + return realNodeModulePath(input); + } catch (error) { + // Not using the helper from FileSystem here because this code loads in every Jest worker process + // and FileSystem has a lot of extra dependencies + // These error codes cloned from the logic in jest-util's tryRealpath.js + if (error.code !== 'ENOENT' && error.code !== 'EISDIR') { + throw error; + } + } + return input; +}; + +const jestUtilPackageJson: IPackageJson = require(path.join(`${jestUtilPackageFolder}/package.json`)); +const jestUtilMajorVersion: number = parseInt(jestUtilPackageJson.version, 10); + +if (jestUtilMajorVersion >= 30) { + // jest-util 30+: everything is bundled in index.js. + // tryRealpath is exported as a non-configurable getter, so we can't set it directly. + // Instead, replace the require-cache entry with an object that shadows the getter. + const jestUtilIndexPath: string = require.resolve('jest-util', { + paths: [jestResolvePackageFolder] + }); + const jestUtilExports: object = require(jestUtilIndexPath); + const patchedExports: object = Object.create(jestUtilExports); + Object.defineProperty(patchedExports, 'tryRealpath', { + value: customTryRealpath, + writable: true, + enumerable: true, + configurable: true + }); + require.cache[jestUtilIndexPath]!.exports = patchedExports; +} else { + // jest-util < 30: tryRealpath is a standalone module; replace its default export. + const jestUtilTryRealpathPath: string = `${jestUtilPackageFolder}/build/tryRealpath.js`; + const tryRealpathModule: { + default: (filePath: string) => string; + } = require(jestUtilTryRealpathPath); + tryRealpathModule.default = customTryRealpath; +} diff --git a/heft-plugins/heft-jest-plugin/src/JestUtils.ts b/heft-plugins/heft-jest-plugin/src/JestUtils.ts index dc92937d473..89e6b5069c8 100644 --- a/heft-plugins/heft-jest-plugin/src/JestUtils.ts +++ b/heft-plugins/heft-jest-plugin/src/JestUtils.ts @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { createHash } from 'crypto'; +import * as path from 'node:path'; +import { createHash } from 'node:crypto'; + import { default as JestResolver } from 'jest-resolve'; import type { TransformOptions } from '@jest/transform'; diff --git a/heft-plugins/heft-jest-plugin/src/SourceMapSnapshotResolver.ts b/heft-plugins/heft-jest-plugin/src/SourceMapSnapshotResolver.ts index 2e3124ab10f..6dd1791ff19 100644 --- a/heft-plugins/heft-jest-plugin/src/SourceMapSnapshotResolver.ts +++ b/heft-plugins/heft-jest-plugin/src/SourceMapSnapshotResolver.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as fs from 'fs'; -import * as path from 'path'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; function findSourcePath(testPath: string, snapshotExtension: string): string { const sourceMapFilePath: string = `${testPath}.map`; diff --git a/heft-plugins/heft-jest-plugin/src/TerminalWritableStream.ts b/heft-plugins/heft-jest-plugin/src/TerminalWritableStream.ts index 886da8928d0..285bd520478 100644 --- a/heft-plugins/heft-jest-plugin/src/TerminalWritableStream.ts +++ b/heft-plugins/heft-jest-plugin/src/TerminalWritableStream.ts @@ -1,5 +1,9 @@ -import type { ITerminal } from '@rushstack/node-core-library'; -import { Writable } from 'stream'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { Writable } from 'node:stream'; + +import type { ITerminal } from '@rushstack/terminal'; // Regex to filter out screen clearing directives // Can't use the AnsiEscape.removeCodes() function from node-core-library because we are only diff --git a/heft-plugins/heft-jest-plugin/src/exports/jest-global-setup.ts b/heft-plugins/heft-jest-plugin/src/exports/jest-global-setup.ts deleted file mode 100644 index c046bfefa4a..00000000000 --- a/heft-plugins/heft-jest-plugin/src/exports/jest-global-setup.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -/** - * This is implementation of the `mocked()` global API declared by `@rushstack/heft-jest`. - * The jest-shared.config.json configuration tells Jest to execute this file when setting - * up the test environment. This makes the API available to each test. - */ -// eslint-disable-next-line -(global as any)['mocked'] = function (item: unknown): unknown { - return item; -}; diff --git a/heft-plugins/heft-jest-plugin/src/exports/jest-node-modules-symlink-resolver.ts b/heft-plugins/heft-jest-plugin/src/exports/jest-node-modules-symlink-resolver.ts new file mode 100644 index 00000000000..5c4f9fb6e70 --- /dev/null +++ b/heft-plugins/heft-jest-plugin/src/exports/jest-node-modules-symlink-resolver.ts @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import '../JestRealPathPatch'; +// Using this syntax because HeftJestResolver uses `export =` syntax. +import resolver = require('../HeftJestResolver'); +export = resolver; diff --git a/heft-plugins/heft-jest-plugin/src/patches/jestWorkerPatch.ts b/heft-plugins/heft-jest-plugin/src/patches/jestWorkerPatch.ts index 6c606363676..a2ca979a865 100644 --- a/heft-plugins/heft-jest-plugin/src/patches/jestWorkerPatch.ts +++ b/heft-plugins/heft-jest-plugin/src/patches/jestWorkerPatch.ts @@ -1,8 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { Import, FileSystem } from '@rushstack/node-core-library'; +/* eslint-disable no-console */ + +import * as path from 'node:path'; +import { createRequire } from 'node:module'; + +import { Import, FileSystem, type IPackageJson } from '@rushstack/node-core-library'; // This patch is a fix for a problem where Jest reports this error spuriously on a machine that is under heavy load: // @@ -41,51 +45,61 @@ function applyPatch(): void { try { let contextFolder: string = __dirname; // Resolve the "@jest/core" package relative to Heft - contextFolder = Import.resolvePackage({ packageName: '@jest/core', baseFolderPath: contextFolder }); + contextFolder = Import.resolvePackage({ + packageName: '@jest/core', + baseFolderPath: contextFolder, + useNodeJSResolver: true + }); // Resolve the "@jest/reporters" package relative to "@jest/core" - contextFolder = Import.resolvePackage({ packageName: '@jest/reporters', baseFolderPath: contextFolder }); + contextFolder = Import.resolvePackage({ + packageName: '@jest/reporters', + baseFolderPath: contextFolder, + useNodeJSResolver: true + }); // Resolve the "jest-worker" package relative to "@jest/reporters" const jestWorkerFolder: string = Import.resolvePackage({ packageName: 'jest-worker', - baseFolderPath: contextFolder + baseFolderPath: contextFolder, + useNodeJSResolver: true }); - const baseWorkerPoolPath: string = path.join(jestWorkerFolder, 'build/base/BaseWorkerPool.js'); - const baseWorkerPoolFilename: string = path.basename(baseWorkerPoolPath); // BaseWorkerPool.js + // jest-worker 30.x switched to a webpack-bundled single-file architecture. + // For 29.x and earlier, patch build/base/BaseWorkerPool.js directly. + // For 30.x and later, patch build/index.js (the webpack bundle). + const jestWorkerPackageJson: IPackageJson = require(`${jestWorkerFolder}/package.json`); + const jestWorkerMajorVersion: number = parseInt(jestWorkerPackageJson.version, 10); + const isBundled: boolean = jestWorkerMajorVersion >= 30; - if (!FileSystem.exists(baseWorkerPoolPath)) { - throw new Error( - 'The BaseWorkerPool.js file was not found in the expected location:\n' + baseWorkerPoolPath - ); - } + const targetPath: string = isBundled + ? `${jestWorkerFolder}/build/index.js` + : `${jestWorkerFolder}/build/base/BaseWorkerPool.js`; - // Load the module - const baseWorkerPoolModule: IBaseWorkerPoolModule = require(baseWorkerPoolPath); - - // Obtain the metadata for the module - let baseWorkerPoolModuleMetadata: NodeModule | undefined = undefined; - for (const childModule of module.children) { - if (path.basename(childModule.filename || '').toUpperCase() === baseWorkerPoolFilename.toUpperCase()) { - if (baseWorkerPoolModuleMetadata) { - throw new Error('More than one child module matched while detecting Node.js module metadata'); - } - baseWorkerPoolModuleMetadata = childModule; + // Load the original file contents + let originalFileContent: string; + try { + originalFileContent = FileSystem.readFile(targetPath); + } catch (e) { + if (FileSystem.isNotExistError(e)) { + throw new Error( + `The ${path.basename(targetPath)} file was not found in the expected location:\n` + targetPath + ); + } else { + throw e; } } - - if (!baseWorkerPoolModuleMetadata) { - throw new Error('Failed to detect the Node.js module metadata for BaseWorkerPool.js'); + // Load the module + const targetModule: IBaseWorkerPoolModule = require(targetPath); + // Obtain the metadata for the module from require.cache. + // We can't use module.children because if the module was already in the cache before this + // require() call (loaded transitively by something else), Node.js returns the cached result + // without appending it to module.children. + // require.resolve() gives us the exact key Node.js uses in require.cache. + const resolvedTargetPath: string = require.resolve(targetPath); + const targetModuleMetadata: NodeModule | undefined = require.cache[resolvedTargetPath]; + if (!targetModuleMetadata) { + throw new Error(`Failed to detect the Node.js module metadata for ${path.basename(targetPath)}`); } - // Load the original file contents - const originalFileContent: string = FileSystem.readFile(baseWorkerPoolPath); - - // Add boilerplate so that eval() will return the exports - let patchedCode: string = - '// PATCHED BY HEFT USING eval()\n\nexports = {}\n' + - originalFileContent + - '\n// return value:\nexports'; - // Apply the patch. We will replace this: // // const FORCE_EXIT_DELAY = 500; @@ -94,7 +108,7 @@ function applyPatch(): void { // // const FORCE_EXIT_DELAY = 7000; let matched: boolean = false; - patchedCode = patchedCode.replace( + const patchedCode: string = originalFileContent.replace( /(const\s+FORCE_EXIT_DELAY\s*=\s*)(\d+)(\s*\;)/, (matchedString: string, leftPart: string, middlePart: string, rightPart: string): string => { matched = true; @@ -103,24 +117,47 @@ function applyPatch(): void { ); if (!matched) { - throw new Error('The expected pattern was not found in the file:\n' + baseWorkerPoolPath); + throw new Error('The expected pattern was not found in the file:\n' + targetPath); } - function evalInContext(): IBaseWorkerPoolModule { - // Remap the require() function for the eval() context + if (isBundled) { + // jest-worker 30.x: webpack bundle uses `module.exports = __webpack_exports__` at the top level. + // Shadow `module` with a shim so that assignment writes to our object, then copy the + // resulting exports over the already-cached module exports in-place. + function evalInContextBundled(): Record { + // createRequire(targetPath) produces a proper require function with resolve/cache/etc. + // and the right module-resolution context (resolves relative to jest-worker's build dir). + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const require: NodeRequire = createRequire(targetPath); + // Shadow `module` so the bundle's `module.exports = ...` writes to our shim + const module: { exports: Record } = { exports: {} }; + // eslint-disable-next-line no-eval + eval(patchedCode); + return module.exports; + } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - function require(modulePath: string): void { - return baseWorkerPoolModuleMetadata!.require(modulePath); + const patchedExports: Record = evalInContextBundled(); + // Can't mutate the cached exports in-place (webpack defines them as read-only getters). + // Replace the exports object in the require cache entirely so future require() calls + // return the patched version. + require.cache[targetModuleMetadata.filename]!.exports = patchedExports; + } else { + // jest-worker < 30: BaseWorkerPool.js uses bare `exports`, wrap for eval return value + const wrappedCode: string = + '// PATCHED BY HEFT USING eval()\n\nexports = {}\n' + patchedCode + '\n// return value:\nexports'; + + function evalInContext(): IBaseWorkerPoolModule { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + function require(modulePath: string): void { + return targetModuleMetadata!.require(modulePath); + } + // eslint-disable-next-line no-eval + return eval(wrappedCode); } - // eslint-disable-next-line no-eval - return eval(patchedCode); + const patchedModule: IBaseWorkerPoolModule = evalInContext(); + targetModule.default = patchedModule.default; } - - const patchedModule: IBaseWorkerPoolModule = evalInContext(); - - baseWorkerPoolModule.default = patchedModule.default; } catch (e) { console.error(); console.error(`ERROR: ${patchName} failed to patch the "jest-worker" package:`); diff --git a/heft-plugins/heft-jest-plugin/src/schemas/heft-jest-plugin.schema.json b/heft-plugins/heft-jest-plugin/src/schemas/heft-jest-plugin.schema.json index 2efdd6cb184..af5a94ddccd 100644 --- a/heft-plugins/heft-jest-plugin/src/schemas/heft-jest-plugin.schema.json +++ b/heft-plugins/heft-jest-plugin/src/schemas/heft-jest-plugin.schema.json @@ -7,10 +7,20 @@ "additionalProperties": false, "properties": { + "configurationPath": { + "title": "Path to Jest configuration file", + "description": "If provided, this path will be used to load Jest configuration. Otherwise, Jest configuration will be loaded from \"/config/jest.config.json\".", + "type": "string" + }, "disableConfigurationModuleResolution": { "title": "Disable Configuration Module Resolution", "description": "If set to true, modules specified in the Jest configuration will be resolved using Jest default (rootDir-relative) resolution. Otherwise, modules will be resolved using Node module resolution.", "type": "boolean" + }, + "enableNodeEnvManagement": { + "title": "Enable management of the NODE_ENV variable", + "description": "If set to false, heft-jest-plugin will not set or unset the NODE_ENV variable. Otherwise, NODE_ENV will be set to `test` before execution and cleared after. If the NODE_ENV value is already set to a value that is not `test`, warning message appears.", + "type": "boolean" } } } diff --git a/heft-plugins/heft-jest-plugin/src/test/JestPlugin.test.ts b/heft-plugins/heft-jest-plugin/src/test/JestPlugin.test.ts index e5e0116f30a..15aafc11cfe 100644 --- a/heft-plugins/heft-jest-plugin/src/test/JestPlugin.test.ts +++ b/heft-plugins/heft-jest-plugin/src/test/JestPlugin.test.ts @@ -1,13 +1,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; import type { Config } from '@jest/types'; import type { IHeftTaskSession, HeftConfiguration, CommandLineParameter } from '@rushstack/heft'; -import { ConfigurationFile } from '@rushstack/heft-config-file'; -import { Import, JsonFile, StringBufferTerminalProvider, Terminal } from '@rushstack/node-core-library'; +import type { ProjectConfigurationFile } from '@rushstack/heft-config-file'; +import { Import, JsonFile } from '@rushstack/node-core-library'; +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; -import { default as JestPlugin, IHeftJestConfiguration } from '../JestPlugin'; +import { + JEST_CONFIG_JSDOM_PACKAGE_NAME, + default as JestPlugin, + type IHeftJestConfiguration +} from '../JestPlugin'; interface IPartialHeftPluginJson { taskPlugins?: { @@ -70,16 +75,16 @@ describe('JestConfigLoader', () => { }); it('resolves extended config modules', async () => { - // Because we require the built modules, we need to set our rootDir to be in the 'lib' folder, since transpilation + // Because we require the built modules, we need to set our rootDir to be in the 'lib-commonjs' folder, since transpilation // means that we don't run on the built test assets directly - const rootDir: string = path.resolve(__dirname, '..', '..', 'lib', 'test', 'project1'); - const loader: ConfigurationFile = JestPlugin._getJestConfigurationLoader( + const rootDir: string = path.resolve(__dirname, '..', '..', 'lib-commonjs', 'test', 'project1'); + const loader: ProjectConfigurationFile = JestPlugin._getJestConfigurationLoader( rootDir, 'config/jest.config.json' ); const loadedConfig: IHeftJestConfiguration = await loader.loadConfigurationFileForProjectAsync( terminal, - path.join(__dirname, '..', '..', 'lib', 'test', 'project1') + rootDir ); expect(loadedConfig.preset).toBe(undefined); @@ -157,16 +162,16 @@ describe('JestConfigLoader', () => { }); it('resolves extended package modules', async () => { - // Because we require the built modules, we need to set our rootDir to be in the 'lib' folder, since transpilation + // Because we require the built modules, we need to set our rootDir to be in the 'lib-commonjs' folder, since transpilation // means that we don't run on the built test assets directly - const rootDir: string = path.resolve(__dirname, '..', '..', 'lib', 'test', 'project2'); - const loader: ConfigurationFile = JestPlugin._getJestConfigurationLoader( + const rootDir: string = path.resolve(__dirname, '..', '..', 'lib-commonjs', 'test', 'project2'); + const loader: ProjectConfigurationFile = JestPlugin._getJestConfigurationLoader( rootDir, 'config/jest.config.json' ); const loadedConfig: IHeftJestConfiguration = await loader.loadConfigurationFileForProjectAsync( terminal, - path.resolve(__dirname, '..', '..', 'lib', 'test', 'project2') + rootDir ); expect(loadedConfig.setupFiles?.length).toBe(1); @@ -177,4 +182,25 @@ describe('JestConfigLoader', () => { expect(loadedConfig.testEnvironment).toContain('jest-environment-jsdom'); expect(loadedConfig.testEnvironment).toMatch(/index.js$/); }); + + it('the default web config const matches the name in the config JSON file', async () => { + const { testEnvironment } = await JsonFile.loadAsync(`${__dirname}/../../includes/jest-web.config.json`); + expect(testEnvironment).toEqual(JEST_CONFIG_JSDOM_PACKAGE_NAME); + }); + + it('resolves jest-environment-jsdom from the extends config', async () => { + // Because we require the built modules, we need to set our rootDir to be in the 'lib-commonjs' folder, since transpilation + // means that we don't run on the built test assets directly + const rootDir: string = path.resolve(__dirname, '..', '..', 'lib-commonjs', 'test', 'project3'); + const loader: ProjectConfigurationFile = JestPlugin._getJestConfigurationLoader( + rootDir, + 'config/jest.config.json' + ); + const loadedConfig: IHeftJestConfiguration = await loader.loadConfigurationFileForProjectAsync( + terminal, + rootDir + ); + expect(loadedConfig.testEnvironment).toContain('jest-environment-jsdom'); + expect(loadedConfig.testEnvironment).toMatch(/index.js$/); + }); }); diff --git a/heft-plugins/heft-jest-plugin/src/test/project1/a/b/globalSetupFile1.ts b/heft-plugins/heft-jest-plugin/src/test/project1/a/b/globalSetupFile1.ts index e69de29bb2d..b2d2e4d1b84 100644 --- a/heft-plugins/heft-jest-plugin/src/test/project1/a/b/globalSetupFile1.ts +++ b/heft-plugins/heft-jest-plugin/src/test/project1/a/b/globalSetupFile1.ts @@ -0,0 +1,2 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. diff --git a/heft-plugins/heft-jest-plugin/src/test/project1/a/b/mockTransformModule1.ts b/heft-plugins/heft-jest-plugin/src/test/project1/a/b/mockTransformModule1.ts index d81d1e46689..4cc179f0780 100644 --- a/heft-plugins/heft-jest-plugin/src/test/project1/a/b/mockTransformModule1.ts +++ b/heft-plugins/heft-jest-plugin/src/test/project1/a/b/mockTransformModule1.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + module.exports = { moduleValue: 'zzz' }; diff --git a/heft-plugins/heft-jest-plugin/src/test/project1/a/b/mockTransformModule2.ts b/heft-plugins/heft-jest-plugin/src/test/project1/a/b/mockTransformModule2.ts index d81d1e46689..4cc179f0780 100644 --- a/heft-plugins/heft-jest-plugin/src/test/project1/a/b/mockTransformModule2.ts +++ b/heft-plugins/heft-jest-plugin/src/test/project1/a/b/mockTransformModule2.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + module.exports = { moduleValue: 'zzz' }; diff --git a/heft-plugins/heft-jest-plugin/src/test/project1/a/b/mockWatchPlugin.ts b/heft-plugins/heft-jest-plugin/src/test/project1/a/b/mockWatchPlugin.ts index d81d1e46689..4cc179f0780 100644 --- a/heft-plugins/heft-jest-plugin/src/test/project1/a/b/mockWatchPlugin.ts +++ b/heft-plugins/heft-jest-plugin/src/test/project1/a/b/mockWatchPlugin.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + module.exports = { moduleValue: 'zzz' }; diff --git a/heft-plugins/heft-jest-plugin/src/test/project1/a/b/setupFile1.ts b/heft-plugins/heft-jest-plugin/src/test/project1/a/b/setupFile1.ts index e69de29bb2d..b2d2e4d1b84 100644 --- a/heft-plugins/heft-jest-plugin/src/test/project1/a/b/setupFile1.ts +++ b/heft-plugins/heft-jest-plugin/src/test/project1/a/b/setupFile1.ts @@ -0,0 +1,2 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. diff --git a/heft-plugins/heft-jest-plugin/src/test/project1/a/b/setupFile2.ts b/heft-plugins/heft-jest-plugin/src/test/project1/a/b/setupFile2.ts index e69de29bb2d..b2d2e4d1b84 100644 --- a/heft-plugins/heft-jest-plugin/src/test/project1/a/b/setupFile2.ts +++ b/heft-plugins/heft-jest-plugin/src/test/project1/a/b/setupFile2.ts @@ -0,0 +1,2 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. diff --git a/heft-plugins/heft-jest-plugin/src/test/project1/a/c/d/globalSetupFile2.ts b/heft-plugins/heft-jest-plugin/src/test/project1/a/c/d/globalSetupFile2.ts index e69de29bb2d..b2d2e4d1b84 100644 --- a/heft-plugins/heft-jest-plugin/src/test/project1/a/c/d/globalSetupFile2.ts +++ b/heft-plugins/heft-jest-plugin/src/test/project1/a/c/d/globalSetupFile2.ts @@ -0,0 +1,2 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. diff --git a/heft-plugins/heft-jest-plugin/src/test/project1/a/c/d/mockReporter2.ts b/heft-plugins/heft-jest-plugin/src/test/project1/a/c/d/mockReporter2.ts index e69de29bb2d..b2d2e4d1b84 100644 --- a/heft-plugins/heft-jest-plugin/src/test/project1/a/c/d/mockReporter2.ts +++ b/heft-plugins/heft-jest-plugin/src/test/project1/a/c/d/mockReporter2.ts @@ -0,0 +1,2 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. diff --git a/heft-plugins/heft-jest-plugin/src/test/project1/a/c/mockReporter1.ts b/heft-plugins/heft-jest-plugin/src/test/project1/a/c/mockReporter1.ts index e69de29bb2d..b2d2e4d1b84 100644 --- a/heft-plugins/heft-jest-plugin/src/test/project1/a/c/mockReporter1.ts +++ b/heft-plugins/heft-jest-plugin/src/test/project1/a/c/mockReporter1.ts @@ -0,0 +1,2 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. diff --git a/heft-plugins/heft-jest-plugin/src/test/project1/a/c/mockTransformModule3.ts b/heft-plugins/heft-jest-plugin/src/test/project1/a/c/mockTransformModule3.ts index e69de29bb2d..b2d2e4d1b84 100644 --- a/heft-plugins/heft-jest-plugin/src/test/project1/a/c/mockTransformModule3.ts +++ b/heft-plugins/heft-jest-plugin/src/test/project1/a/c/mockTransformModule3.ts @@ -0,0 +1,2 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. diff --git a/heft-plugins/heft-jest-plugin/src/test/project3/config/jest.config.json b/heft-plugins/heft-jest-plugin/src/test/project3/config/jest.config.json new file mode 100644 index 00000000000..d052e597d38 --- /dev/null +++ b/heft-plugins/heft-jest-plugin/src/test/project3/config/jest.config.json @@ -0,0 +1,3 @@ +{ + "extends": "../../../../includes/jest-web.config.json" +} diff --git a/heft-plugins/heft-jest-plugin/src/transformers/IdentityMockTransformer.ts b/heft-plugins/heft-jest-plugin/src/transformers/IdentityMockTransformer.ts index b076523f52b..c6423bcad8f 100644 --- a/heft-plugins/heft-jest-plugin/src/transformers/IdentityMockTransformer.ts +++ b/heft-plugins/heft-jest-plugin/src/transformers/IdentityMockTransformer.ts @@ -1,10 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { FileSystem } from '@rushstack/node-core-library'; +import * as path from 'node:path'; + import type { SyncTransformer, TransformedSource, TransformOptions } from '@jest/transform'; +import { FileSystem } from '@rushstack/node-core-library'; + // The transpiled output for IdentityMockProxy.ts const proxyCode: string = FileSystem.readFile(path.join(__dirname, '..', 'identityMock.js')).toString(); diff --git a/heft-plugins/heft-jest-plugin/src/transformers/StringMockTransformer.ts b/heft-plugins/heft-jest-plugin/src/transformers/StringMockTransformer.ts index 7b2d587f7cd..23766ffc9de 100644 --- a/heft-plugins/heft-jest-plugin/src/transformers/StringMockTransformer.ts +++ b/heft-plugins/heft-jest-plugin/src/transformers/StringMockTransformer.ts @@ -1,8 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import { relative } from 'node:path'; + import type { SyncTransformer, TransformedSource, TransformOptions } from '@jest/transform'; +const isWindows: boolean = process.platform === 'win32'; + /** * This Jest transform handles imports of data files (e.g. .png, .jpg) that would normally be * processed by a Webpack's file-loader. Instead of actually loading the resource, we return the file's name. @@ -11,9 +15,13 @@ import type { SyncTransformer, TransformedSource, TransformOptions } from '@jest */ export class StringMockTransformer implements SyncTransformer { public process(sourceText: string, sourcePath: string, options: TransformOptions): TransformedSource { - // For a file called "myImage.png", this will generate a JS module that exports the literal string "myImage.png" + // heft-jest-plugin enforces that config.rootDir will always be the project root folder. + const relativePath: string = relative(options.config.rootDir, sourcePath); + const normalizedRelativePath: string = isWindows ? relativePath.replace(/\\/g, '/') : relativePath; + // For a file called "myImage.png", this will generate a JS module that exports the slash-normalized relative + // path from the current working directory to "myImage.png" return { - code: `module.exports = ${JSON.stringify(sourcePath)};` + code: `module.exports = ${JSON.stringify(normalizedRelativePath)};` }; } } diff --git a/heft-plugins/heft-jest-plugin/tsconfig.json b/heft-plugins/heft-jest-plugin/tsconfig.json index b44063ce961..7b03eaec26f 100644 --- a/heft-plugins/heft-jest-plugin/tsconfig.json +++ b/heft-plugins/heft-jest-plugin/tsconfig.json @@ -1,7 +1,8 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "types": ["node", "heft-jest"] + // TODO: Remove when the repo is updated to ES2020 + "target": "es2018" } } diff --git a/heft-plugins/heft-json-schema-typings-plugin/.npmignore b/heft-plugins/heft-json-schema-typings-plugin/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-json-schema-typings-plugin/CHANGELOG.json b/heft-plugins/heft-json-schema-typings-plugin/CHANGELOG.json new file mode 100644 index 00000000000..132c8740c0e --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/CHANGELOG.json @@ -0,0 +1,994 @@ +{ + "name": "@rushstack/heft-json-schema-typings-plugin", + "entries": [ + { + "version": "1.2.23", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.23", + "date": "Tue, 04 Aug 2026 00:17:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.17.0`" + } + ] + } + }, + { + "version": "1.2.22", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.22", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "1.2.21", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.21", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "1.2.20", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.20", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "1.2.19", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.19", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "1.2.18", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.18", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "1.2.17", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "1.2.16", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.16", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "1.2.15", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.15", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "1.2.14", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.14", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "1.2.13", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "1.2.12", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.12", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "1.2.11", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "1.2.10", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "1.2.9", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "1.2.8", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "1.2.7", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "1.2.6", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "1.2.5", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.5", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "1.2.4", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "1.2.3", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "1.2.2", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "1.2.1", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "1.2.0", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.2.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + }, + { + "comment": "Add support for the `x-tsdoc-release-tag` custom property in JSON schema files. When present (e.g. `\"x-tsdoc-release-tag\": \"@beta\"`), the specified TSDoc release tag is injected into the generated `.d.ts` declarations, allowing API Extractor to apply the correct release level when these types are re-exported from package entry points." + }, + { + "comment": "Add a `formatWithPrettier` option (defaults to `false`) to skip prettier formatting of generated typings." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "1.1.14", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "1.1.13", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "1.1.12", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "1.1.11", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "1.1.10", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.10", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "1.1.9", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.9", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "1.1.8", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.8", + "date": "Mon, 05 Jan 2026 16:12:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "1.1.7", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "1.1.6", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.6", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.5` to `1.1.6`" + } + ] + } + }, + { + "version": "1.1.5", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.4` to `1.1.5`" + } + ] + } + }, + { + "version": "1.1.4", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.4", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.3` to `1.1.4`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.2` to `1.1.3`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.1` to `1.1.2`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.0` to `1.1.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.1.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.0.0` to `1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-json-schema-typings-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.47`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.75.0` to `1.0.0`" + } + ] + } + }, + { + "version": "0.1.6", + "tag": "@rushstack/heft-json-schema-typings-plugin_v0.1.6", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.46`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.5` to `0.75.0`" + } + ] + } + }, + { + "version": "0.1.5", + "tag": "@rushstack/heft-json-schema-typings-plugin_v0.1.5", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.45`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.4` to `0.74.5`" + } + ] + } + }, + { + "version": "0.1.4", + "tag": "@rushstack/heft-json-schema-typings-plugin_v0.1.4", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.44`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.3` to `0.74.4`" + } + ] + } + }, + { + "version": "0.1.3", + "tag": "@rushstack/heft-json-schema-typings-plugin_v0.1.3", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.43`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.2` to `0.74.3`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/heft-json-schema-typings-plugin_v0.1.2", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.42`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.1` to `0.74.2`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/heft-json-schema-typings-plugin_v0.1.1", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.41`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.0` to `0.74.1`" + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/heft-json-schema-typings-plugin_v0.1.0", + "date": "Wed, 09 Jul 2025 04:01:17 GMT", + "comments": { + "minor": [ + { + "comment": "Initial release." + } + ] + } + } + ] +} diff --git a/heft-plugins/heft-json-schema-typings-plugin/CHANGELOG.md b/heft-plugins/heft-json-schema-typings-plugin/CHANGELOG.md new file mode 100644 index 00000000000..cefcae09479 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/CHANGELOG.md @@ -0,0 +1,251 @@ +# Change Log - @rushstack/heft-json-schema-typings-plugin + +This log was last generated on Tue, 04 Aug 2026 00:17:24 GMT and should not be manually modified. + +## 1.2.23 +Tue, 04 Aug 2026 00:17:24 GMT + +_Version update only_ + +## 1.2.22 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 1.2.21 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 1.2.20 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.2.19 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 1.2.18 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 1.2.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.2.16 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.2.15 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 1.2.14 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 1.2.13 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.2.12 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.2.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.2.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.2.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.2.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.2.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.2.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.2.5 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 1.2.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.2.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.2.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.2.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.2.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. +- Add support for the `x-tsdoc-release-tag` custom property in JSON schema files. When present (e.g. `"x-tsdoc-release-tag": "@beta"`), the specified TSDoc release tag is injected into the generated `.d.ts` declarations, allowing API Extractor to apply the correct release level when these types are re-exported from package entry points. +- Add a `formatWithPrettier` option (defaults to `false`) to skip prettier formatting of generated typings. + +## 1.1.14 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.1.13 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.1.12 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.1.11 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.1.10 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.1.9 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 1.1.8 +Mon, 05 Jan 2026 16:12:50 GMT + +_Version update only_ + +## 1.1.7 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.1.6 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.1.5 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.1.4 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 1.1.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.1.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.1.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 1.1.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.1.6 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.1.5 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.1.4 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.1.3 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.1.2 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 0.1.1 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.1.0 +Wed, 09 Jul 2025 04:01:17 GMT + +### Minor changes + +- Initial release. + diff --git a/heft-plugins/heft-json-schema-typings-plugin/LICENSE b/heft-plugins/heft-json-schema-typings-plugin/LICENSE new file mode 100644 index 00000000000..4c95bdfe909 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/LICENSE @@ -0,0 +1,24 @@ +@rushstack/heft-json-schema-typings-plugin + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/heft-plugins/heft-json-schema-typings-plugin/README.md b/heft-plugins/heft-json-schema-typings-plugin/README.md new file mode 100644 index 00000000000..69af8891ba2 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/README.md @@ -0,0 +1,106 @@ +# @rushstack/heft-json-schema-typings-plugin + +This is a Heft plugin that generates TypeScript `.d.ts` typings from JSON Schema files +(files matching `*.schema.json`). It uses the +[json-schema-to-typescript](https://www.npmjs.com/package/json-schema-to-typescript) library +to produce type declarations that can be imported alongside the schema at build time. + +## Setup + +1. Add the plugin as a `devDependency` of your project: + + ```bash + rush add -p @rushstack/heft-json-schema-typings-plugin --dev + ``` + +2. Load the plugin in your project's **heft.json** configuration: + + ```jsonc + { + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "phasesByName": { + "build": { + "tasksByName": { + "json-schema-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-json-schema-typings-plugin", + "options": { + // (Optional) Defaults shown below + // "srcFolder": "src", + // "generatedTsFolders": ["temp/schemas-ts"], + // "formatWithPrettier": false + } + } + } + } + } + } + } + ``` + +3. Place your `*.schema.json` files under the source folder (default: `src/`). + The plugin will generate a corresponding `.d.ts` file for each schema. + +## Plugin options + +| Option | Type | Default | Description | +| -------------------- | ---------- | -------------------- | -------------------------------------------------------------------------------- | +| `srcFolder` | `string` | `"src"` | Source directory to scan for `*.schema.json` files. | +| `generatedTsFolders` | `string[]` | `["temp/schemas-ts"]`| Output directories for the generated `.d.ts` files. | +| `formatWithPrettier` | `boolean` | `false` | When `true`, format generated typings with [prettier](https://prettier.io/). Requires `prettier` as an installed dependency. | + +## Vendor extension: `x-tsdoc-release-tag` + +The plugin recognises a custom vendor extension property called **`x-tsdoc-release-tag`** in +your JSON Schema files. When present at the top level of a schema, its value (a +[TSDoc release tag](https://tsdoc.org/pages/spec/tag_kinds/#release-tags) such as `@public`, +`@beta`, `@alpha`, or `@internal`) is injected into JSDoc comments of every exported +declaration in the generated `.d.ts` file. + +This is useful when the generated types are re-exported from a package entry point that is +processed by [API Extractor](https://api-extractor.com/), which uses release tags to +determine the API surface visibility. + +### Example + +**my-config.schema.json** + +```json +{ + "x-tsdoc-release-tag": "@public", + "title": "My Config", + "type": "object", + "properties": { + "name": { "type": "string" } + }, + "additionalProperties": false +} +``` + +**Generated output (my-config.schema.json.d.ts)** + +```ts +/** + * @public + */ +export interface MyConfig { + name?: string; +} +``` + +The `x-tsdoc-release-tag` property is stripped from the schema before type generation, so it +does not affect the shape of the emitted types. The value must be a single lowercase word +starting with `@` (for example `@public` or `@beta`); invalid values cause a build error. + +> **Note:** `@rushstack/node-core-library`'s `JsonSchema` class accepts vendor extension +> keywords matching the `x--` pattern by default, so schema files containing +> `x-tsdoc-release-tag` will validate without any additional configuration. + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/heft-plugins/heft-json-schema-typings-plugin/CHANGELOG.md) - Find + out what's new in the latest version +- [@rushstack/heft](https://www.npmjs.com/package/@rushstack/heft) - Heft is a config-driven toolchain that invokes popular tools such as TypeScript, ESLint, Jest, Webpack, and API Extractor. + +Heft is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/heft-plugins/heft-json-schema-typings-plugin/config/heft.json b/heft-plugins/heft-json-schema-typings-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-json-schema-typings-plugin/config/jest.config.json b/heft-plugins/heft-json-schema-typings-plugin/config/jest.config.json new file mode 100644 index 00000000000..7b2eb73199f --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/config/jest.config.json @@ -0,0 +1,6 @@ +{ + "extends": "local-node-rig/profiles/default/config/jest.config.json", + "moduleNameMapper": { + "^prettier$": "/jestMocks/prettier.js" + } +} diff --git a/heft-plugins/heft-json-schema-typings-plugin/config/jestMocks/prettier.js b/heft-plugins/heft-json-schema-typings-plugin/config/jestMocks/prettier.js new file mode 100644 index 00000000000..1fb34a72b8c --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/config/jestMocks/prettier.js @@ -0,0 +1,6 @@ +// Stub for prettier. json-schema-to-typescript eagerly require('prettier') at +// module load time. Prettier v3's CJS entry does a top-level dynamic import() +// which crashes inside Jest's VM sandbox on Node 22+. Since compile() is called +// with format: false, prettier is never invoked - this stub just prevents the +// module-load crash. +module.exports = {}; diff --git a/heft-plugins/heft-json-schema-typings-plugin/config/rig.json b/heft-plugins/heft-json-schema-typings-plugin/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/heft-plugins/heft-json-schema-typings-plugin/eslint.config.js b/heft-plugins/heft-json-schema-typings-plugin/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-json-schema-typings-plugin/heft-plugin.json b/heft-plugins/heft-json-schema-typings-plugin/heft-plugin.json new file mode 100644 index 00000000000..9d40b9828d2 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/heft-plugin.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", + + "taskPlugins": [ + { + "pluginName": "json-schema-typings-plugin", + "entryPoint": "./lib-commonjs/JsonSchemaTypingsPlugin", + "optionsSchema": "./lib-commonjs/schemas/heft-json-schema-typings-plugin.schema.json" + } + ] +} diff --git a/heft-plugins/heft-json-schema-typings-plugin/package.json b/heft-plugins/heft-json-schema-typings-plugin/package.json new file mode 100644 index 00000000000..515d5a8ba50 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/package.json @@ -0,0 +1,51 @@ +{ + "name": "@rushstack/heft-json-schema-typings-plugin", + "version": "1.2.23", + "description": "A Heft plugin for generating TypeScript typings from JSON schema files.", + "repository": { + "type": "git", + "url": "https://github.com/microsoft/rushstack.git", + "directory": "heft-plugins/heft-json-schema-typings-plugin" + }, + "homepage": "https://rushstack.io/pages/heft/overview/", + "license": "MIT", + "scripts": { + "build": "heft test --clean", + "start": "heft build-watch", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "peerDependencies": { + "@rushstack/heft": "1.2.22" + }, + "dependencies": { + "@rushstack/node-core-library": "workspace:*", + "@rushstack/typings-generator": "workspace:*", + "json-schema-to-typescript": "~15.0.4" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "@rushstack/terminal": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "exports": { + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "sideEffects": false +} diff --git a/heft-plugins/heft-json-schema-typings-plugin/src/JsonSchemaTypingsGenerator.ts b/heft-plugins/heft-json-schema-typings-plugin/src/JsonSchemaTypingsGenerator.ts new file mode 100644 index 00000000000..deb30f2742b --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/src/JsonSchemaTypingsGenerator.ts @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { compile } from 'json-schema-to-typescript'; + +import { type ITypingsGeneratorBaseOptions, TypingsGenerator } from '@rushstack/typings-generator'; + +import { + _addTsDocReleaseTagToExports, + _validateTsDocReleaseTag, + X_TSDOC_RELEASE_TAG_KEY +} from './TsDocReleaseTagHelpers'; + +interface IJsonSchemaTypingsGeneratorBaseOptions extends ITypingsGeneratorBaseOptions { + /** + * If true, format generated typings with prettier. Defaults to false. + * + * @remarks + * Enabling this requires the `prettier` package to be installed as a dependency. + */ + formatWithPrettier?: boolean; +} + +const SCHEMA_FILE_EXTENSION: '.schema.json' = '.schema.json'; + +type Json4Schema = Parameters[0]; +interface IExtendedJson4Schema extends Json4Schema { + [X_TSDOC_RELEASE_TAG_KEY]?: string; +} + +export class JsonSchemaTypingsGenerator extends TypingsGenerator { + public constructor(options: IJsonSchemaTypingsGeneratorBaseOptions) { + const { formatWithPrettier = false, ...otherOptions } = options; + super({ + ...otherOptions, + fileExtensions: [SCHEMA_FILE_EXTENSION], + // eslint-disable-next-line @typescript-eslint/naming-convention + parseAndGenerateTypings: async ( + fileContents: string, + filePath: string, + relativePath: string + ): Promise => { + const parsedFileContents: IExtendedJson4Schema = JSON.parse(fileContents); + const { [X_TSDOC_RELEASE_TAG_KEY]: tsdocReleaseTag, ...jsonSchemaWithoutReleaseTag } = + parsedFileContents; + + // Use the absolute directory of the schema file so that cross-file $ref + // (e.g. { "$ref": "./other.schema.json" }) resolves correctly. + const dirname: string = path.dirname(filePath); + const filenameWithoutExtension: string = filePath.slice( + dirname.length + 1, + -SCHEMA_FILE_EXTENSION.length + ); + let typings: string = await compile(jsonSchemaWithoutReleaseTag, filenameWithoutExtension, { + // The typings generator adds its own banner comment + bannerComment: '', + cwd: dirname, + format: formatWithPrettier + }); + + // Check for an "x-tsdoc-release-tag" property in the schema (e.g. "@public" or "@beta"). + // If present, inject the tag into JSDoc comments for all exported declarations. + if (tsdocReleaseTag) { + _validateTsDocReleaseTag(tsdocReleaseTag, relativePath); + typings = _addTsDocReleaseTagToExports(typings, tsdocReleaseTag); + } + + return typings; + } + }); + } +} diff --git a/heft-plugins/heft-json-schema-typings-plugin/src/JsonSchemaTypingsPlugin.ts b/heft-plugins/heft-json-schema-typings-plugin/src/JsonSchemaTypingsPlugin.ts new file mode 100644 index 00000000000..877cd041b26 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/src/JsonSchemaTypingsPlugin.ts @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { + HeftConfiguration, + IHeftTaskSession, + IHeftTaskPlugin, + IHeftTaskRunIncrementalHookOptions, + IWatchedFileState +} from '@rushstack/heft'; +import type { ITerminal } from '@rushstack/terminal'; + +import { JsonSchemaTypingsGenerator } from './JsonSchemaTypingsGenerator'; + +const PLUGIN_NAME: 'json-schema-typings-plugin' = 'json-schema-typings-plugin'; + +// TODO: Replace this with usage of this plugin after this plugin is published +export interface IJsonSchemaTypingsPluginOptions { + srcFolder?: string; + generatedTsFolders?: string[]; + formatWithPrettier?: boolean; +} + +export default class JsonSchemaTypingsPlugin implements IHeftTaskPlugin { + /** + * Generate typings for JSON Schemas. + */ + public apply( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + options: IJsonSchemaTypingsPluginOptions + ): void { + const { + logger: { terminal }, + hooks: { run, runIncremental } + } = taskSession; + const { buildFolderPath } = heftConfiguration; + const { srcFolder = 'src', generatedTsFolders = ['temp/schemas-ts'], formatWithPrettier } = options; + + const resolvedTsFolders: string[] = []; + for (const generatedTsFolder of generatedTsFolders) { + resolvedTsFolders.push(`${buildFolderPath}/${generatedTsFolder}`); + } + + const [generatedTsFolder, ...secondaryGeneratedTsFolders] = resolvedTsFolders; + + const typingsGenerator: JsonSchemaTypingsGenerator = new JsonSchemaTypingsGenerator({ + srcFolder: `${buildFolderPath}/${srcFolder}`, + generatedTsFolder, + secondaryGeneratedTsFolders, + terminal, + formatWithPrettier + }); + + run.tapPromise(PLUGIN_NAME, async () => { + await this._runTypingsGeneratorAsync(typingsGenerator, terminal, undefined); + }); + + runIncremental.tapPromise( + PLUGIN_NAME, + async (runIncrementalOptions: IHeftTaskRunIncrementalHookOptions) => { + await this._runTypingsGeneratorAsync(typingsGenerator, terminal, runIncrementalOptions); + } + ); + } + + private async _runTypingsGeneratorAsync( + typingsGenerator: JsonSchemaTypingsGenerator, + terminal: ITerminal, + runIncrementalOptions: IHeftTaskRunIncrementalHookOptions | undefined + ): Promise { + // If we have the incremental options, use them to determine which files to process. + // Otherwise, process all files. The typings generator also provides the file paths + // as relative paths from the sourceFolderPath. + let changedRelativeFilePaths: string[] | undefined; + if (runIncrementalOptions) { + changedRelativeFilePaths = []; + const relativeFilePaths: Map = await runIncrementalOptions.watchGlobAsync( + typingsGenerator.inputFileGlob, + { + cwd: typingsGenerator.sourceFolderPath, + ignore: Array.from(typingsGenerator.ignoredFileGlobs), + absolute: false + } + ); + for (const [relativeFilePath, { changed }] of relativeFilePaths) { + if (changed) { + changedRelativeFilePaths.push(relativeFilePath); + } + } + if (changedRelativeFilePaths.length === 0) { + return; + } + } + + terminal.writeLine('Generating typings for JSON schemas...'); + await typingsGenerator.generateTypingsAsync(changedRelativeFilePaths); + } +} diff --git a/heft-plugins/heft-json-schema-typings-plugin/src/TsDocReleaseTagHelpers.ts b/heft-plugins/heft-json-schema-typings-plugin/src/TsDocReleaseTagHelpers.ts new file mode 100644 index 00000000000..3f8b1d8af48 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/src/TsDocReleaseTagHelpers.ts @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export const X_TSDOC_RELEASE_TAG_KEY: 'x-tsdoc-release-tag' = 'x-tsdoc-release-tag'; +const RELEASE_TAG_PATTERN: RegExp = /^@[a-z]+$/; + +/** + * Validates that a string looks like a TSDoc release tag - a single lowercase + * word starting with `@` (e.g. `@public`, `@beta`, `@internal`). + */ +export function _validateTsDocReleaseTag(value: string, schemaPath: string): void { + if (!RELEASE_TAG_PATTERN.test(value)) { + throw new Error( + `Invalid ${X_TSDOC_RELEASE_TAG_KEY} value ${JSON.stringify(value)} in ${schemaPath}. ` + + 'Expected a single lowercase word starting with "@" (e.g. "@public", "@beta").' + ); + } +} + +/** + * Adds a TSDoc release tag (e.g. `@public`, `@beta`) to all exported declarations + * in generated typings. + * + * `json-schema-to-typescript` does not emit release tags, so this function + * post-processes the output to ensure API Extractor treats these types with the + * correct release tag when they are re-exported from package entry points. + */ +export function _addTsDocReleaseTagToExports(typingsData: string, tag: string): string { + // Normalize line endings for consistent regex matching. + // The TypingsGenerator base class applies NewlineKind.OsDefault when writing. + const normalized: string = typingsData.replace(/\r\n/g, '\n'); + + // Pass 1: For exports preceded by an existing JSDoc comment, insert + // the tag before the closing "*/". + let result: string = normalized.replace(/ \*\/\n(export )/g, ` *\n * ${tag}\n */\n$1`); + + // Pass 2: For exports NOT preceded by a JSDoc comment, insert a new + // JSDoc block. The negative lookbehind ensures Pass 1 + // results are not double-matched. + result = result.replace(/(? { + const outputPath: string = `${outputFolder}/${schemaRelativePath}.d.ts`; + return await FileSystem.readFileAsync(outputPath); +} + +describe('JsonSchemaTypingsGenerator', () => { + beforeEach(async () => { + await FileSystem.ensureEmptyFolderAsync(outputFolder); + }); + + it('generates typings for a basic object schema', async () => { + const generator = new JsonSchemaTypingsGenerator({ + srcFolder: schemasFolder, + generatedTsFolder: outputFolder + }); + + await generator.generateTypingsAsync(['basic.schema.json']); + const typings: string = await readGeneratedTypings('basic.schema.json'); + expect(typings).toMatchSnapshot(); + }); + + it('injects x-tsdoc-release-tag into exported declarations', async () => { + const generator = new JsonSchemaTypingsGenerator({ + srcFolder: schemasFolder, + generatedTsFolder: outputFolder + }); + + await generator.generateTypingsAsync(['with-tsdoc-tag.schema.json']); + const typings: string = await readGeneratedTypings('with-tsdoc-tag.schema.json'); + expect(typings).toMatchSnapshot(); + expect(typings).toContain('@public'); + }); + + it('resolves cross-file $ref between schema files', async () => { + const generator = new JsonSchemaTypingsGenerator({ + srcFolder: schemasFolder, + generatedTsFolder: outputFolder + }); + + await generator.generateTypingsAsync(['child.schema.json', 'parent.schema.json']); + const [parentTypings, childTypings]: string[] = await Promise.all([ + readGeneratedTypings('parent.schema.json'), + readGeneratedTypings('child.schema.json') + ]); + + expect(childTypings).toMatchSnapshot('child output'); + expect(parentTypings).toMatchSnapshot('parent output'); + + // The parent typings should reference the child type + expect(parentTypings).toContain('ChildType'); + }); +}); diff --git a/heft-plugins/heft-json-schema-typings-plugin/src/test/TsDocReleaseTagHelpers.test.ts b/heft-plugins/heft-json-schema-typings-plugin/src/test/TsDocReleaseTagHelpers.test.ts new file mode 100644 index 00000000000..ee9a7e55d14 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/src/test/TsDocReleaseTagHelpers.test.ts @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { _addTsDocReleaseTagToExports, _validateTsDocReleaseTag } from '../TsDocReleaseTagHelpers'; + +describe(_addTsDocReleaseTagToExports.name, () => { + test('injects tag into an existing JSDoc comment before an export', () => { + const input: string = ['/**', ' * A description.', ' */', 'export type Foo = {};'].join('\n'); + + expect(_addTsDocReleaseTagToExports(input, '@beta')).toMatchSnapshot(); + }); + + test('creates a new JSDoc block for an export without a preceding comment', () => { + const input: string = 'export type Foo = {};'; + + expect(_addTsDocReleaseTagToExports(input, '@public')).toMatchSnapshot(); + }); + + test('handles multiple exports with and without JSDoc comments', () => { + const input: string = [ + '/**', + ' * First type.', + ' */', + 'export type Foo = {};', + '', + 'export type Bar = {};' + ].join('\n'); + + expect(_addTsDocReleaseTagToExports(input, '@beta')).toMatchSnapshot(); + }); + + test('normalizes CRLF line endings to LF', () => { + const input: string = '/**\r\n * A description.\r\n */\r\nexport type Foo = {};'; + + expect(_addTsDocReleaseTagToExports(input, '@beta')).toMatchSnapshot(); + }); + + test('does not double-tag an export that already has a JSDoc block', () => { + const input: string = [ + '/**', + ' * Already documented.', + ' */', + 'export interface IConfig {', + ' name: string;', + '}' + ].join('\n'); + + const result: string = _addTsDocReleaseTagToExports(input, '@public'); + + // The tag should appear exactly once + const tagOccurrences: number = (result.match(/@public/g) || []).length; + expect(tagOccurrences).toBe(1); + expect(result).toMatchSnapshot(); + }); + + test('does not modify non-export lines', () => { + const input: string = ['// A leading comment', 'const internal = 1;', '', 'export type Foo = {};'].join( + '\n' + ); + + expect(_addTsDocReleaseTagToExports(input, '@beta')).toMatchSnapshot(); + }); +}); + +describe(_validateTsDocReleaseTag.name, () => { + test('accepts valid release tags', () => { + expect(() => _validateTsDocReleaseTag('@public', 'test.schema.json')).not.toThrow(); + expect(() => _validateTsDocReleaseTag('@beta', 'test.schema.json')).not.toThrow(); + expect(() => _validateTsDocReleaseTag('@alpha', 'test.schema.json')).not.toThrow(); + expect(() => _validateTsDocReleaseTag('@internal', 'test.schema.json')).not.toThrow(); + }); + + test('rejects invalid release tags', () => { + expect(() => _validateTsDocReleaseTag('public', 'test.schema.json')).toThrow( + /Invalid x-tsdoc-release-tag/ + ); + expect(() => _validateTsDocReleaseTag('@Public', 'test.schema.json')).toThrow( + /Invalid x-tsdoc-release-tag/ + ); + expect(() => _validateTsDocReleaseTag('@two words', 'test.schema.json')).toThrow( + /Invalid x-tsdoc-release-tag/ + ); + expect(() => _validateTsDocReleaseTag('', 'test.schema.json')).toThrow(/Invalid x-tsdoc-release-tag/); + }); +}); diff --git a/heft-plugins/heft-json-schema-typings-plugin/src/test/__snapshots__/JsonSchemaTypingsGenerator.test.ts.snap b/heft-plugins/heft-json-schema-typings-plugin/src/test/__snapshots__/JsonSchemaTypingsGenerator.test.ts.snap new file mode 100644 index 00000000000..6c14d0958bf --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/src/test/__snapshots__/JsonSchemaTypingsGenerator.test.ts.snap @@ -0,0 +1,85 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`JsonSchemaTypingsGenerator generates typings for a basic object schema 1`] = ` +"// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface BasicConfig { +/** + * The name of the item. + */ +name: string +/** + * The number of items. + */ +count?: number +/** + * Whether the feature is enabled. + */ +enabled?: boolean +} +" +`; + +exports[`JsonSchemaTypingsGenerator injects x-tsdoc-release-tag into exported declarations 1`] = ` +"// This file was generated by a tool. Modifying it will produce unexpected behavior + +/** + * @public + */ +export interface PublicConfig { +/** + * A value. + */ +value?: string +} +" +`; + +exports[`JsonSchemaTypingsGenerator resolves cross-file $ref between schema files: child output 1`] = ` +"// This file was generated by a tool. Modifying it will produce unexpected behavior + +/** + * A reusable child type. + */ +export interface ChildType { +/** + * The name of the child. + */ +childName: string +/** + * The value of the child. + */ +childValue?: number +} +" +`; + +exports[`JsonSchemaTypingsGenerator resolves cross-file $ref between schema files: parent output 1`] = ` +"// This file was generated by a tool. Modifying it will produce unexpected behavior + +export interface ParentConfig { +/** + * A label for the parent. + */ +label: string +child: ChildType +/** + * A list of children. + */ +children?: ChildType[] +} +/** + * A reusable child type. + */ +export interface ChildType { +/** + * The name of the child. + */ +childName: string +/** + * The value of the child. + */ +childValue?: number +} +" +`; diff --git a/heft-plugins/heft-json-schema-typings-plugin/src/test/__snapshots__/TsDocReleaseTagHelpers.test.ts.snap b/heft-plugins/heft-json-schema-typings-plugin/src/test/__snapshots__/TsDocReleaseTagHelpers.test.ts.snap new file mode 100644 index 00000000000..145d803cf1f --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/src/test/__snapshots__/TsDocReleaseTagHelpers.test.ts.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`_addTsDocReleaseTagToExports creates a new JSDoc block for an export without a preceding comment 1`] = ` +"/** + * @public + */ +export type Foo = {};" +`; + +exports[`_addTsDocReleaseTagToExports does not double-tag an export that already has a JSDoc block 1`] = ` +"/** + * Already documented. + * + * @public + */ +export interface IConfig { + name: string; +}" +`; + +exports[`_addTsDocReleaseTagToExports does not modify non-export lines 1`] = ` +"// A leading comment +const internal = 1; + +/** + * @beta + */ +export type Foo = {};" +`; + +exports[`_addTsDocReleaseTagToExports handles multiple exports with and without JSDoc comments 1`] = ` +"/** + * First type. + * + * @beta + */ +export type Foo = {}; + +/** + * @beta + */ +export type Bar = {};" +`; + +exports[`_addTsDocReleaseTagToExports injects tag into an existing JSDoc comment before an export 1`] = ` +"/** + * A description. + * + * @beta + */ +export type Foo = {};" +`; + +exports[`_addTsDocReleaseTagToExports normalizes CRLF line endings to LF 1`] = ` +"/** + * A description. + * + * @beta + */ +export type Foo = {};" +`; diff --git a/heft-plugins/heft-json-schema-typings-plugin/src/test/schemas/basic.schema.json b/heft-plugins/heft-json-schema-typings-plugin/src/test/schemas/basic.schema.json new file mode 100644 index 00000000000..05db112bce5 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/src/test/schemas/basic.schema.json @@ -0,0 +1,20 @@ +{ + "title": "Basic Config", + "type": "object", + "properties": { + "name": { + "description": "The name of the item.", + "type": "string" + }, + "count": { + "description": "The number of items.", + "type": "integer" + }, + "enabled": { + "description": "Whether the feature is enabled.", + "type": "boolean" + } + }, + "additionalProperties": false, + "required": ["name"] +} diff --git a/heft-plugins/heft-json-schema-typings-plugin/src/test/schemas/child.schema.json b/heft-plugins/heft-json-schema-typings-plugin/src/test/schemas/child.schema.json new file mode 100644 index 00000000000..dedcfaafabf --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/src/test/schemas/child.schema.json @@ -0,0 +1,17 @@ +{ + "title": "Child Type", + "description": "A reusable child type.", + "type": "object", + "properties": { + "childName": { + "description": "The name of the child.", + "type": "string" + }, + "childValue": { + "description": "The value of the child.", + "type": "number" + } + }, + "additionalProperties": false, + "required": ["childName"] +} diff --git a/heft-plugins/heft-json-schema-typings-plugin/src/test/schemas/parent.schema.json b/heft-plugins/heft-json-schema-typings-plugin/src/test/schemas/parent.schema.json new file mode 100644 index 00000000000..63cd9884e19 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/src/test/schemas/parent.schema.json @@ -0,0 +1,22 @@ +{ + "title": "Parent Config", + "type": "object", + "properties": { + "label": { + "description": "A label for the parent.", + "type": "string" + }, + "child": { + "$ref": "./child.schema.json" + }, + "children": { + "description": "A list of children.", + "type": "array", + "items": { + "$ref": "./child.schema.json" + } + } + }, + "additionalProperties": false, + "required": ["label", "child"] +} diff --git a/heft-plugins/heft-json-schema-typings-plugin/src/test/schemas/with-tsdoc-tag.schema.json b/heft-plugins/heft-json-schema-typings-plugin/src/test/schemas/with-tsdoc-tag.schema.json new file mode 100644 index 00000000000..14511d830b7 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/src/test/schemas/with-tsdoc-tag.schema.json @@ -0,0 +1,12 @@ +{ + "x-tsdoc-release-tag": "@public", + "title": "Public Config", + "type": "object", + "properties": { + "value": { + "description": "A value.", + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/heft-plugins/heft-json-schema-typings-plugin/tsconfig.json b/heft-plugins/heft-json-schema-typings-plugin/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/heft-plugins/heft-json-schema-typings-plugin/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/heft-plugins/heft-lint-plugin/.eslintrc.js b/heft-plugins/heft-lint-plugin/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/heft-plugins/heft-lint-plugin/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/heft-plugins/heft-lint-plugin/.npmignore b/heft-plugins/heft-lint-plugin/.npmignore index 869bde13daf..f7a40e10213 100644 --- a/heft-plugins/heft-lint-plugin/.npmignore +++ b/heft-plugins/heft-lint-plugin/.npmignore @@ -8,24 +8,29 @@ !/lib/** !/lib-*/** !/dist/** -!ThirdPartyNotice.txt +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json !heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt # Ignore certain patterns that should not get published. /dist/*.stats.* /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-lint-plugin/CHANGELOG.json b/heft-plugins/heft-lint-plugin/CHANGELOG.json index 7d0e0850693..d973b4b2e5b 100644 --- a/heft-plugins/heft-lint-plugin/CHANGELOG.json +++ b/heft-plugins/heft-lint-plugin/CHANGELOG.json @@ -1,6 +1,3677 @@ { "name": "@rushstack/heft-lint-plugin", "entries": [ + { + "version": "1.2.22", + "tag": "@rushstack/heft-lint-plugin_v1.2.22", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "1.2.21", + "tag": "@rushstack/heft-lint-plugin_v1.2.21", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "1.2.20", + "tag": "@rushstack/heft-lint-plugin_v1.2.20", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "1.2.19", + "tag": "@rushstack/heft-lint-plugin_v1.2.19", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "1.2.18", + "tag": "@rushstack/heft-lint-plugin_v1.2.18", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "1.2.17", + "tag": "@rushstack/heft-lint-plugin_v1.2.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "1.2.16", + "tag": "@rushstack/heft-lint-plugin_v1.2.16", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "1.2.15", + "tag": "@rushstack/heft-lint-plugin_v1.2.15", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "1.2.14", + "tag": "@rushstack/heft-lint-plugin_v1.2.14", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "patch": [ + { + "comment": "Bump semver." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "1.2.13", + "tag": "@rushstack/heft-lint-plugin_v1.2.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "1.2.12", + "tag": "@rushstack/heft-lint-plugin_v1.2.12", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "1.2.11", + "tag": "@rushstack/heft-lint-plugin_v1.2.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "1.2.10", + "tag": "@rushstack/heft-lint-plugin_v1.2.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "1.2.9", + "tag": "@rushstack/heft-lint-plugin_v1.2.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "1.2.8", + "tag": "@rushstack/heft-lint-plugin_v1.2.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "1.2.7", + "tag": "@rushstack/heft-lint-plugin_v1.2.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "1.2.6", + "tag": "@rushstack/heft-lint-plugin_v1.2.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "1.2.5", + "tag": "@rushstack/heft-lint-plugin_v1.2.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "1.2.4", + "tag": "@rushstack/heft-lint-plugin_v1.2.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "1.2.3", + "tag": "@rushstack/heft-lint-plugin_v1.2.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "1.2.2", + "tag": "@rushstack/heft-lint-plugin_v1.2.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "1.2.1", + "tag": "@rushstack/heft-lint-plugin_v1.2.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "1.2.0", + "tag": "@rushstack/heft-lint-plugin_v1.2.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "1.1.17", + "tag": "@rushstack/heft-lint-plugin_v1.1.17", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "1.1.16", + "tag": "@rushstack/heft-lint-plugin_v1.1.16", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "1.1.15", + "tag": "@rushstack/heft-lint-plugin_v1.1.15", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "1.1.14", + "tag": "@rushstack/heft-lint-plugin_v1.1.14", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "1.1.13", + "tag": "@rushstack/heft-lint-plugin_v1.1.13", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "1.1.12", + "tag": "@rushstack/heft-lint-plugin_v1.1.12", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "1.1.11", + "tag": "@rushstack/heft-lint-plugin_v1.1.11", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "1.1.10", + "tag": "@rushstack/heft-lint-plugin_v1.1.10", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "1.1.9", + "tag": "@rushstack/heft-lint-plugin_v1.1.9", + "date": "Wed, 03 Dec 2025 01:12:28 GMT", + "comments": { + "patch": [ + { + "comment": "Stabilize the hash suffix in the linter cache file by using tsconfig path hash instead of file list hash" + } + ] + } + }, + { + "version": "1.1.8", + "tag": "@rushstack/heft-lint-plugin_v1.1.8", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.5` to `1.1.6`" + } + ] + } + }, + { + "version": "1.1.7", + "tag": "@rushstack/heft-lint-plugin_v1.1.7", + "date": "Wed, 12 Nov 2025 01:57:54 GMT", + "comments": { + "patch": [ + { + "comment": "Forward suppressed messages to formatters." + } + ] + } + }, + { + "version": "1.1.6", + "tag": "@rushstack/heft-lint-plugin_v1.1.6", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.4` to `1.1.5`" + } + ] + } + }, + { + "version": "1.1.5", + "tag": "@rushstack/heft-lint-plugin_v1.1.5", + "date": "Tue, 11 Nov 2025 16:13:26 GMT", + "comments": { + "patch": [ + { + "comment": "Ensure that `parserOptions.tsconfigRootDir` is set for use by custom lint rules." + } + ] + } + }, + { + "version": "1.1.4", + "tag": "@rushstack/heft-lint-plugin_v1.1.4", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "patch": [ + { + "comment": "Fix bug where TypeScript program is not reused in ESLint 9." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.3` to `1.1.4`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft-lint-plugin_v1.1.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.2` to `1.1.3`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft-lint-plugin_v1.1.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.1` to `1.1.2`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft-lint-plugin_v1.1.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.0` to `1.1.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-lint-plugin_v1.1.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.0.0` to `1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-lint-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.75.0` to `1.0.0`" + } + ] + } + }, + { + "version": "0.7.7", + "tag": "@rushstack/heft-lint-plugin_v0.7.7", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.5` to `0.75.0`" + } + ] + } + }, + { + "version": "0.7.6", + "tag": "@rushstack/heft-lint-plugin_v0.7.6", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.4` to `0.74.5`" + } + ] + } + }, + { + "version": "0.7.5", + "tag": "@rushstack/heft-lint-plugin_v0.7.5", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.3` to `0.74.4`" + } + ] + } + }, + { + "version": "0.7.4", + "tag": "@rushstack/heft-lint-plugin_v0.7.4", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.2` to `0.74.3`" + } + ] + } + }, + { + "version": "0.7.3", + "tag": "@rushstack/heft-lint-plugin_v0.7.3", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.1` to `0.74.2`" + } + ] + } + }, + { + "version": "0.7.2", + "tag": "@rushstack/heft-lint-plugin_v0.7.2", + "date": "Mon, 28 Jul 2025 15:11:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.10`" + } + ] + } + }, + { + "version": "0.7.1", + "tag": "@rushstack/heft-lint-plugin_v0.7.1", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.0` to `0.74.1`" + } + ] + } + }, + { + "version": "0.7.0", + "tag": "@rushstack/heft-lint-plugin_v0.7.0", + "date": "Thu, 26 Jun 2025 18:57:04 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for ESLint 9. When using ESLint 9, the configuration will be loaded from `eslint.config.js`, and flat configs will be required by the Heft plugin" + } + ] + } + }, + { + "version": "0.6.1", + "tag": "@rushstack/heft-lint-plugin_v0.6.1", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.6` to `0.74.0`" + } + ] + } + }, + { + "version": "0.6.0", + "tag": "@rushstack/heft-lint-plugin_v0.6.0", + "date": "Fri, 06 Jun 2025 00:11:09 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for using heft-lint-plugin standalone without a typescript phase" + } + ] + } + }, + { + "version": "0.5.38", + "tag": "@rushstack/heft-lint-plugin_v0.5.38", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.5` to `0.73.6`" + } + ] + } + }, + { + "version": "0.5.37", + "tag": "@rushstack/heft-lint-plugin_v0.5.37", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.4` to `0.73.5`" + } + ] + } + }, + { + "version": "0.5.36", + "tag": "@rushstack/heft-lint-plugin_v0.5.36", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.3` to `0.73.4`" + } + ] + } + }, + { + "version": "0.5.35", + "tag": "@rushstack/heft-lint-plugin_v0.5.35", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.2` to `0.73.3`" + } + ] + } + }, + { + "version": "0.5.34", + "tag": "@rushstack/heft-lint-plugin_v0.5.34", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.1` to `0.73.2`" + } + ] + } + }, + { + "version": "0.5.33", + "tag": "@rushstack/heft-lint-plugin_v0.5.33", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.0` to `0.73.1`" + } + ] + } + }, + { + "version": "0.5.32", + "tag": "@rushstack/heft-lint-plugin_v0.5.32", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.72.0` to `0.73.0`" + } + ] + } + }, + { + "version": "0.5.31", + "tag": "@rushstack/heft-lint-plugin_v0.5.31", + "date": "Wed, 09 Apr 2025 00:11:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.71.2` to `0.72.0`" + } + ] + } + }, + { + "version": "0.5.30", + "tag": "@rushstack/heft-lint-plugin_v0.5.30", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.8.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.71.1` to `0.71.2`" + } + ] + } + }, + { + "version": "0.5.29", + "tag": "@rushstack/heft-lint-plugin_v0.5.29", + "date": "Tue, 25 Mar 2025 15:11:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.71.0` to `0.71.1`" + } + ] + } + }, + { + "version": "0.5.28", + "tag": "@rushstack/heft-lint-plugin_v0.5.28", + "date": "Tue, 25 Mar 2025 00:12:04 GMT", + "comments": { + "patch": [ + { + "comment": "Fix the `--fix` argument when the file only contains fixable issues." + } + ] + } + }, + { + "version": "0.5.27", + "tag": "@rushstack/heft-lint-plugin_v0.5.27", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.70.1` to `0.71.0`" + } + ] + } + }, + { + "version": "0.5.26", + "tag": "@rushstack/heft-lint-plugin_v0.5.26", + "date": "Wed, 12 Mar 2025 00:11:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.7.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.70.0` to `0.70.1`" + } + ] + } + }, + { + "version": "0.5.25", + "tag": "@rushstack/heft-lint-plugin_v0.5.25", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.69.3` to `0.70.0`" + } + ] + } + }, + { + "version": "0.5.24", + "tag": "@rushstack/heft-lint-plugin_v0.5.24", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.69.2` to `0.69.3`" + } + ] + } + }, + { + "version": "0.5.23", + "tag": "@rushstack/heft-lint-plugin_v0.5.23", + "date": "Thu, 06 Mar 2025 01:10:42 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where the cache is only populated for incremental TypeScript builds." + } + ] + } + }, + { + "version": "0.5.22", + "tag": "@rushstack/heft-lint-plugin_v0.5.22", + "date": "Sat, 01 Mar 2025 07:23:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.15`" + } + ] + } + }, + { + "version": "0.5.21", + "tag": "@rushstack/heft-lint-plugin_v0.5.21", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.69.1` to `0.69.2`" + } + ] + } + }, + { + "version": "0.5.20", + "tag": "@rushstack/heft-lint-plugin_v0.5.20", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.69.0` to `0.69.1`" + } + ] + } + }, + { + "version": "0.5.19", + "tag": "@rushstack/heft-lint-plugin_v0.5.19", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.18` to `0.69.0`" + } + ] + } + }, + { + "version": "0.5.18", + "tag": "@rushstack/heft-lint-plugin_v0.5.18", + "date": "Tue, 25 Feb 2025 01:11:55 GMT", + "comments": { + "patch": [ + { + "comment": "Add verbose logging around finding the lint config file." + } + ] + } + }, + { + "version": "0.5.17", + "tag": "@rushstack/heft-lint-plugin_v0.5.17", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.17` to `0.68.18`" + } + ] + } + }, + { + "version": "0.5.16", + "tag": "@rushstack/heft-lint-plugin_v0.5.16", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.16` to `0.68.17`" + } + ] + } + }, + { + "version": "0.5.15", + "tag": "@rushstack/heft-lint-plugin_v0.5.15", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.9`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.15` to `0.68.16`" + } + ] + } + }, + { + "version": "0.5.14", + "tag": "@rushstack/heft-lint-plugin_v0.5.14", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.14` to `0.68.15`" + } + ] + } + }, + { + "version": "0.5.13", + "tag": "@rushstack/heft-lint-plugin_v0.5.13", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.13` to `0.68.14`" + } + ] + } + }, + { + "version": "0.5.12", + "tag": "@rushstack/heft-lint-plugin_v0.5.12", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.12` to `0.68.13`" + } + ] + } + }, + { + "version": "0.5.11", + "tag": "@rushstack/heft-lint-plugin_v0.5.11", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.11` to `0.68.12`" + } + ] + } + }, + { + "version": "0.5.10", + "tag": "@rushstack/heft-lint-plugin_v0.5.10", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.10` to `0.68.11`" + } + ] + } + }, + { + "version": "0.5.9", + "tag": "@rushstack/heft-lint-plugin_v0.5.9", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.9` to `0.68.10`" + } + ] + } + }, + { + "version": "0.5.8", + "tag": "@rushstack/heft-lint-plugin_v0.5.8", + "date": "Tue, 03 Dec 2024 16:11:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.8` to `0.68.9`" + } + ] + } + }, + { + "version": "0.5.7", + "tag": "@rushstack/heft-lint-plugin_v0.5.7", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.7` to `0.68.8`" + } + ] + } + }, + { + "version": "0.5.6", + "tag": "@rushstack/heft-lint-plugin_v0.5.6", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.6` to `0.68.7`" + } + ] + } + }, + { + "version": "0.5.5", + "tag": "@rushstack/heft-lint-plugin_v0.5.5", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.5` to `0.68.6`" + } + ] + } + }, + { + "version": "0.5.4", + "tag": "@rushstack/heft-lint-plugin_v0.5.4", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.4` to `0.68.5`" + } + ] + } + }, + { + "version": "0.5.3", + "tag": "@rushstack/heft-lint-plugin_v0.5.3", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.3` to `0.68.4`" + } + ] + } + }, + { + "version": "0.5.2", + "tag": "@rushstack/heft-lint-plugin_v0.5.2", + "date": "Wed, 16 Oct 2024 00:11:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.32`" + } + ] + } + }, + { + "version": "0.5.1", + "tag": "@rushstack/heft-lint-plugin_v0.5.1", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.2` to `0.68.3`" + } + ] + } + }, + { + "version": "0.5.0", + "tag": "@rushstack/heft-lint-plugin_v0.5.0", + "date": "Thu, 10 Oct 2024 00:11:51 GMT", + "comments": { + "minor": [ + { + "comment": "Add an option `sarifLogPath` that, when specified, will emit logs in the SARIF format: https://sarifweb.azurewebsites.net/. Note that this is only supported by ESLint." + } + ] + } + }, + { + "version": "0.4.6", + "tag": "@rushstack/heft-lint-plugin_v0.4.6", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.1` to `0.68.2`" + } + ] + } + }, + { + "version": "0.4.5", + "tag": "@rushstack/heft-lint-plugin_v0.4.5", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.0` to `0.68.1`" + } + ] + } + }, + { + "version": "0.4.4", + "tag": "@rushstack/heft-lint-plugin_v0.4.4", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.67.2` to `0.68.0`" + } + ] + } + }, + { + "version": "0.4.3", + "tag": "@rushstack/heft-lint-plugin_v0.4.3", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.27`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.67.1` to `0.67.2`" + } + ] + } + }, + { + "version": "0.4.2", + "tag": "@rushstack/heft-lint-plugin_v0.4.2", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.26`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.67.0` to `0.67.1`" + } + ] + } + }, + { + "version": "0.4.1", + "tag": "@rushstack/heft-lint-plugin_v0.4.1", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.25`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.26` to `0.67.0`" + } + ] + } + }, + { + "version": "0.4.0", + "tag": "@rushstack/heft-lint-plugin_v0.4.0", + "date": "Wed, 14 Aug 2024 22:37:32 GMT", + "comments": { + "minor": [ + { + "comment": "Add autofix functionality for ESLint and TSLint. Fixes can now be applied by providing the \"--fix\" command-line argument, or setting the \"alwaysFix\" plugin option to \"true\"" + } + ], + "patch": [ + { + "comment": "Unintrusively disable \"--fix\" mode when running in \"--production\" mode" + } + ] + } + }, + { + "version": "0.3.48", + "tag": "@rushstack/heft-lint-plugin_v0.3.48", + "date": "Tue, 13 Aug 2024 18:17:05 GMT", + "comments": { + "patch": [ + { + "comment": "Supported linters (ESLint, TSLint) are now loaded asynchronously" + } + ] + } + }, + { + "version": "0.3.47", + "tag": "@rushstack/heft-lint-plugin_v0.3.47", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.24`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.25` to `0.66.26`" + } + ] + } + }, + { + "version": "0.3.46", + "tag": "@rushstack/heft-lint-plugin_v0.3.46", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.24` to `0.66.25`" + } + ] + } + }, + { + "version": "0.3.45", + "tag": "@rushstack/heft-lint-plugin_v0.3.45", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.22`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.23` to `0.66.24`" + } + ] + } + }, + { + "version": "0.3.44", + "tag": "@rushstack/heft-lint-plugin_v0.3.44", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.22` to `0.66.23`" + } + ] + } + }, + { + "version": "0.3.43", + "tag": "@rushstack/heft-lint-plugin_v0.3.43", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.20`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.21` to `0.66.22`" + } + ] + } + }, + { + "version": "0.3.42", + "tag": "@rushstack/heft-lint-plugin_v0.3.42", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.20` to `0.66.21`" + } + ] + } + }, + { + "version": "0.3.41", + "tag": "@rushstack/heft-lint-plugin_v0.3.41", + "date": "Tue, 16 Jul 2024 00:36:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.18`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.19` to `0.66.20`" + } + ] + } + }, + { + "version": "0.3.40", + "tag": "@rushstack/heft-lint-plugin_v0.3.40", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.18` to `0.66.19`" + } + ] + } + }, + { + "version": "0.3.39", + "tag": "@rushstack/heft-lint-plugin_v0.3.39", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.17` to `0.66.18`" + } + ] + } + }, + { + "version": "0.3.38", + "tag": "@rushstack/heft-lint-plugin_v0.3.38", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.15`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.16` to `0.66.17`" + } + ] + } + }, + { + "version": "0.3.37", + "tag": "@rushstack/heft-lint-plugin_v0.3.37", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.15` to `0.66.16`" + } + ] + } + }, + { + "version": "0.3.36", + "tag": "@rushstack/heft-lint-plugin_v0.3.36", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.14` to `0.66.15`" + } + ] + } + }, + { + "version": "0.3.35", + "tag": "@rushstack/heft-lint-plugin_v0.3.35", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.12`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.13` to `0.66.14`" + } + ] + } + }, + { + "version": "0.3.34", + "tag": "@rushstack/heft-lint-plugin_v0.3.34", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.11`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.12` to `0.66.13`" + } + ] + } + }, + { + "version": "0.3.33", + "tag": "@rushstack/heft-lint-plugin_v0.3.33", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.10`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.11` to `0.66.12`" + } + ] + } + }, + { + "version": "0.3.32", + "tag": "@rushstack/heft-lint-plugin_v0.3.32", + "date": "Fri, 24 May 2024 00:15:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.10` to `0.66.11`" + } + ] + } + }, + { + "version": "0.3.31", + "tag": "@rushstack/heft-lint-plugin_v0.3.31", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.9` to `0.66.10`" + } + ] + } + }, + { + "version": "0.3.30", + "tag": "@rushstack/heft-lint-plugin_v0.3.30", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.8` to `0.66.9`" + } + ] + } + }, + { + "version": "0.3.29", + "tag": "@rushstack/heft-lint-plugin_v0.3.29", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.7` to `0.66.8`" + } + ] + } + }, + { + "version": "0.3.28", + "tag": "@rushstack/heft-lint-plugin_v0.3.28", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.6` to `0.66.7`" + } + ] + } + }, + { + "version": "0.3.27", + "tag": "@rushstack/heft-lint-plugin_v0.3.27", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.5` to `0.66.6`" + } + ] + } + }, + { + "version": "0.3.26", + "tag": "@rushstack/heft-lint-plugin_v0.3.26", + "date": "Wed, 08 May 2024 22:23:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.4` to `0.66.5`" + } + ] + } + }, + { + "version": "0.3.25", + "tag": "@rushstack/heft-lint-plugin_v0.3.25", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.3` to `0.66.4`" + } + ] + } + }, + { + "version": "0.3.24", + "tag": "@rushstack/heft-lint-plugin_v0.3.24", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.2` to `0.66.3`" + } + ] + } + }, + { + "version": "0.3.23", + "tag": "@rushstack/heft-lint-plugin_v0.3.23", + "date": "Thu, 28 Mar 2024 22:42:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.5.0`" + } + ] + } + }, + { + "version": "0.3.22", + "tag": "@rushstack/heft-lint-plugin_v0.3.22", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.1` to `0.66.2`" + } + ] + } + }, + { + "version": "0.3.21", + "tag": "@rushstack/heft-lint-plugin_v0.3.21", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.0` to `0.66.1`" + } + ] + } + }, + { + "version": "0.3.20", + "tag": "@rushstack/heft-lint-plugin_v0.3.20", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.10` to `0.66.0`" + } + ] + } + }, + { + "version": "0.3.19", + "tag": "@rushstack/heft-lint-plugin_v0.3.19", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.9` to `0.65.10`" + } + ] + } + }, + { + "version": "0.3.18", + "tag": "@rushstack/heft-lint-plugin_v0.3.18", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.8` to `0.65.9`" + } + ] + } + }, + { + "version": "0.3.17", + "tag": "@rushstack/heft-lint-plugin_v0.3.17", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.7` to `0.65.8`" + } + ] + } + }, + { + "version": "0.3.16", + "tag": "@rushstack/heft-lint-plugin_v0.3.16", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.6` to `0.65.7`" + } + ] + } + }, + { + "version": "0.3.15", + "tag": "@rushstack/heft-lint-plugin_v0.3.15", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.5` to `0.65.6`" + } + ] + } + }, + { + "version": "0.3.14", + "tag": "@rushstack/heft-lint-plugin_v0.3.14", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.4` to `0.65.5`" + } + ] + } + }, + { + "version": "0.3.13", + "tag": "@rushstack/heft-lint-plugin_v0.3.13", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.3` to `0.65.4`" + } + ] + } + }, + { + "version": "0.3.12", + "tag": "@rushstack/heft-lint-plugin_v0.3.12", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.12`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.2` to `0.65.3`" + } + ] + } + }, + { + "version": "0.3.11", + "tag": "@rushstack/heft-lint-plugin_v0.3.11", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.1` to `0.65.2`" + } + ] + } + }, + { + "version": "0.3.10", + "tag": "@rushstack/heft-lint-plugin_v0.3.10", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.10`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.0` to `0.65.1`" + } + ] + } + }, + { + "version": "0.3.9", + "tag": "@rushstack/heft-lint-plugin_v0.3.9", + "date": "Tue, 20 Feb 2024 16:10:53 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.8` to `0.65.0`" + } + ] + } + }, + { + "version": "0.3.8", + "tag": "@rushstack/heft-lint-plugin_v0.3.8", + "date": "Mon, 19 Feb 2024 21:54:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.7` to `0.64.8`" + } + ] + } + }, + { + "version": "0.3.7", + "tag": "@rushstack/heft-lint-plugin_v0.3.7", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.6` to `0.64.7`" + } + ] + } + }, + { + "version": "0.3.6", + "tag": "@rushstack/heft-lint-plugin_v0.3.6", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.5` to `0.64.6`" + } + ] + } + }, + { + "version": "0.3.5", + "tag": "@rushstack/heft-lint-plugin_v0.3.5", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.4` to `0.64.5`" + } + ] + } + }, + { + "version": "0.3.4", + "tag": "@rushstack/heft-lint-plugin_v0.3.4", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.3` to `0.64.4`" + } + ] + } + }, + { + "version": "0.3.3", + "tag": "@rushstack/heft-lint-plugin_v0.3.3", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.2` to `0.64.3`" + } + ] + } + }, + { + "version": "0.3.2", + "tag": "@rushstack/heft-lint-plugin_v0.3.2", + "date": "Tue, 23 Jan 2024 20:12:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.1` to `0.64.2`" + } + ] + } + }, + { + "version": "0.3.1", + "tag": "@rushstack/heft-lint-plugin_v0.3.1", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.0` to `0.64.1`" + } + ] + } + }, + { + "version": "0.3.0", + "tag": "@rushstack/heft-lint-plugin_v0.3.0", + "date": "Tue, 16 Jan 2024 18:30:10 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for TypeScript 5.3" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.6` to `0.64.0`" + } + ] + } + }, + { + "version": "0.2.16", + "tag": "@rushstack/heft-lint-plugin_v0.2.16", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.5` to `0.63.6`" + } + ] + } + }, + { + "version": "0.2.15", + "tag": "@rushstack/heft-lint-plugin_v0.2.15", + "date": "Wed, 20 Dec 2023 01:09:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.4` to `0.63.5`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/heft-lint-plugin_v0.2.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.3` to `0.63.4`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/heft-lint-plugin_v0.2.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.2` to `0.63.3`" + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/heft-lint-plugin_v0.2.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.1` to `0.63.2`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/heft-lint-plugin_v0.2.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.0` to `0.63.1`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/heft-lint-plugin_v0.2.10", + "date": "Mon, 30 Oct 2023 23:36:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.62.3` to `0.63.0`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/heft-lint-plugin_v0.2.9", + "date": "Sun, 01 Oct 2023 02:56:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.62.2` to `0.62.3`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/heft-lint-plugin_v0.2.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.62.1` to `0.62.2`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/heft-lint-plugin_v0.2.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.62.0` to `0.62.1`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/heft-lint-plugin_v0.2.6", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.61.3` to `0.62.0`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/heft-lint-plugin_v0.2.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.61.2` to `0.61.3`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/heft-lint-plugin_v0.2.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.61.1` to `0.61.2`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/heft-lint-plugin_v0.2.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.61.0` to `0.61.1`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/heft-lint-plugin_v0.2.2", + "date": "Fri, 22 Sep 2023 00:05:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.60.0` to `0.61.0`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/heft-lint-plugin_v0.2.1", + "date": "Tue, 19 Sep 2023 15:21:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.59.0` to `0.60.0`" + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/heft-lint-plugin_v0.2.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "patch": [ + { + "comment": "Reduce verbosity by only printing the \"not supported in watch mode\" warning during the initial build." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.58.2` to `0.59.0`" + } + ] + } + }, + { + "version": "0.1.22", + "tag": "@rushstack/heft-lint-plugin_v0.1.22", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.58.1` to `0.58.2`" + } + ] + } + }, + { + "version": "0.1.21", + "tag": "@rushstack/heft-lint-plugin_v0.1.21", + "date": "Sat, 29 Jul 2023 00:22:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.58.0` to `0.58.1`" + } + ] + } + }, + { + "version": "0.1.20", + "tag": "@rushstack/heft-lint-plugin_v0.1.20", + "date": "Thu, 20 Jul 2023 20:47:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.57.1` to `0.58.0`" + } + ] + } + }, + { + "version": "0.1.19", + "tag": "@rushstack/heft-lint-plugin_v0.1.19", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "patch": [ + { + "comment": "Updated semver dependency" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.57.0` to `0.57.1`" + } + ] + } + }, + { + "version": "0.1.18", + "tag": "@rushstack/heft-lint-plugin_v0.1.18", + "date": "Fri, 14 Jul 2023 15:20:45 GMT", + "comments": { + "patch": [ + { + "comment": "Treat a malformed cache file the same as no cache file (i.e. recheck everything) instead of throwing an error.." + } + ] + } + }, + { + "version": "0.1.17", + "tag": "@rushstack/heft-lint-plugin_v0.1.17", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.56.3` to `0.57.0`" + } + ] + } + }, + { + "version": "0.1.16", + "tag": "@rushstack/heft-lint-plugin_v0.1.16", + "date": "Wed, 12 Jul 2023 15:20:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.56.2` to `0.56.3`" + } + ] + } + }, + { + "version": "0.1.15", + "tag": "@rushstack/heft-lint-plugin_v0.1.15", + "date": "Wed, 12 Jul 2023 00:23:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.15`" + } + ] + } + }, + { + "version": "0.1.14", + "tag": "@rushstack/heft-lint-plugin_v0.1.14", + "date": "Fri, 07 Jul 2023 00:19:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.56.1` to `0.56.2`" + } + ] + } + }, + { + "version": "0.1.13", + "tag": "@rushstack/heft-lint-plugin_v0.1.13", + "date": "Thu, 06 Jul 2023 00:16:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.56.0` to `0.56.1`" + } + ] + } + }, + { + "version": "0.1.12", + "tag": "@rushstack/heft-lint-plugin_v0.1.12", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.12`" + } + ] + } + }, + { + "version": "0.1.11", + "tag": "@rushstack/heft-lint-plugin_v0.1.11", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.55.2` to `0.56.0`" + } + ] + } + }, + { + "version": "0.1.10", + "tag": "@rushstack/heft-lint-plugin_v0.1.10", + "date": "Thu, 15 Jun 2023 00:21:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.55.1` to `0.55.2`" + } + ] + } + }, + { + "version": "0.1.9", + "tag": "@rushstack/heft-lint-plugin_v0.1.9", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.55.0` to `0.55.1`" + } + ] + } + }, + { + "version": "0.1.8", + "tag": "@rushstack/heft-lint-plugin_v0.1.8", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.54.0` to `0.55.0`" + } + ] + } + }, + { + "version": "0.1.7", + "tag": "@rushstack/heft-lint-plugin_v0.1.7", + "date": "Tue, 13 Jun 2023 01:49:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.53.1` to `0.54.0`" + } + ] + } + }, + { + "version": "0.1.6", + "tag": "@rushstack/heft-lint-plugin_v0.1.6", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.53.0` to `0.53.1`" + } + ] + } + }, + { + "version": "0.1.5", + "tag": "@rushstack/heft-lint-plugin_v0.1.5", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.52.2` to `0.53.0`" + } + ] + } + }, + { + "version": "0.1.4", + "tag": "@rushstack/heft-lint-plugin_v0.1.4", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.52.1` to `0.52.2`" + } + ] + } + }, + { + "version": "0.1.3", + "tag": "@rushstack/heft-lint-plugin_v0.1.3", + "date": "Thu, 08 Jun 2023 00:20:02 GMT", + "comments": { + "patch": [ + { + "comment": "Use the temp folder instead of the cache folder." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.52.0` to `0.52.1`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/heft-lint-plugin_v0.1.2", + "date": "Wed, 07 Jun 2023 22:45:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.51.0` to `0.52.0`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/heft-lint-plugin_v0.1.1", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "patch": [ + { + "comment": "Fix a regression that caused this error: \"[build:lint] The create() function for rule ___ did not return an object.\"" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-typescript-plugin\" to `0.1.1`" + } + ] + } + }, { "version": "0.1.0", "tag": "@rushstack/heft-lint-plugin_v0.1.0", diff --git a/heft-plugins/heft-lint-plugin/CHANGELOG.md b/heft-plugins/heft-lint-plugin/CHANGELOG.md index 1c6aee2d14c..1cf84a28913 100644 --- a/heft-plugins/heft-lint-plugin/CHANGELOG.md +++ b/heft-plugins/heft-lint-plugin/CHANGELOG.md @@ -1,6 +1,996 @@ # Change Log - @rushstack/heft-lint-plugin -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 1.2.22 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 1.2.21 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 1.2.20 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.2.19 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 1.2.18 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 1.2.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.2.16 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.2.15 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 1.2.14 +Sat, 18 Apr 2026 00:15:16 GMT + +### Patches + +- Bump semver. + +## 1.2.13 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.2.12 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.2.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.2.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.2.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.2.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.2.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.2.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.2.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 1.2.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.2.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.2.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.2.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.2.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.1.17 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.1.16 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.1.15 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.1.14 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.1.13 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.1.12 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 1.1.11 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 1.1.10 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.1.9 +Wed, 03 Dec 2025 01:12:28 GMT + +### Patches + +- Stabilize the hash suffix in the linter cache file by using tsconfig path hash instead of file list hash + +## 1.1.8 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.1.7 +Wed, 12 Nov 2025 01:57:54 GMT + +### Patches + +- Forward suppressed messages to formatters. + +## 1.1.6 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.1.5 +Tue, 11 Nov 2025 16:13:26 GMT + +### Patches + +- Ensure that `parserOptions.tsconfigRootDir` is set for use by custom lint rules. + +## 1.1.4 +Tue, 04 Nov 2025 08:15:14 GMT + +### Patches + +- Fix bug where TypeScript program is not reused in ESLint 9. + +## 1.1.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.1.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.1.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 1.1.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.7.7 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.7.6 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.7.5 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.7.4 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.7.3 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 0.7.2 +Mon, 28 Jul 2025 15:11:56 GMT + +_Version update only_ + +## 0.7.1 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.7.0 +Thu, 26 Jun 2025 18:57:04 GMT + +### Minor changes + +- Add support for ESLint 9. When using ESLint 9, the configuration will be loaded from `eslint.config.js`, and flat configs will be required by the Heft plugin + +## 0.6.1 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.6.0 +Fri, 06 Jun 2025 00:11:09 GMT + +### Minor changes + +- Add support for using heft-lint-plugin standalone without a typescript phase + +## 0.5.38 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.5.37 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.5.36 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.5.35 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.5.34 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.5.33 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.5.32 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 0.5.31 +Wed, 09 Apr 2025 00:11:02 GMT + +_Version update only_ + +## 0.5.30 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.5.29 +Tue, 25 Mar 2025 15:11:16 GMT + +_Version update only_ + +## 0.5.28 +Tue, 25 Mar 2025 00:12:04 GMT + +### Patches + +- Fix the `--fix` argument when the file only contains fixable issues. + +## 0.5.27 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.5.26 +Wed, 12 Mar 2025 00:11:31 GMT + +_Version update only_ + +## 0.5.25 +Tue, 11 Mar 2025 02:12:33 GMT + +_Version update only_ + +## 0.5.24 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.5.23 +Thu, 06 Mar 2025 01:10:42 GMT + +### Patches + +- Fix an issue where the cache is only populated for incremental TypeScript builds. + +## 0.5.22 +Sat, 01 Mar 2025 07:23:16 GMT + +_Version update only_ + +## 0.5.21 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.5.20 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.5.19 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 0.5.18 +Tue, 25 Feb 2025 01:11:55 GMT + +### Patches + +- Add verbose logging around finding the lint config file. + +## 0.5.17 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.5.16 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.5.15 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.5.14 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 0.5.13 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.5.12 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.5.11 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.5.10 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.5.9 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.5.8 +Tue, 03 Dec 2024 16:11:08 GMT + +_Version update only_ + +## 0.5.7 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.5.6 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.5.5 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.5.4 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.5.3 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.5.2 +Wed, 16 Oct 2024 00:11:20 GMT + +_Version update only_ + +## 0.5.1 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.5.0 +Thu, 10 Oct 2024 00:11:51 GMT + +### Minor changes + +- Add an option `sarifLogPath` that, when specified, will emit logs in the SARIF format: https://sarifweb.azurewebsites.net/. Note that this is only supported by ESLint. + +## 0.4.6 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.4.5 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.4.4 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.4.3 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.4.2 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.4.1 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.4.0 +Wed, 14 Aug 2024 22:37:32 GMT + +### Minor changes + +- Add autofix functionality for ESLint and TSLint. Fixes can now be applied by providing the "--fix" command-line argument, or setting the "alwaysFix" plugin option to "true" + +### Patches + +- Unintrusively disable "--fix" mode when running in "--production" mode + +## 0.3.48 +Tue, 13 Aug 2024 18:17:05 GMT + +### Patches + +- Supported linters (ESLint, TSLint) are now loaded asynchronously + +## 0.3.47 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.3.46 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.3.45 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.3.44 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 0.3.43 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 0.3.42 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.3.41 +Tue, 16 Jul 2024 00:36:22 GMT + +_Version update only_ + +## 0.3.40 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.3.39 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.3.38 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.3.37 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 0.3.36 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.3.35 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.3.34 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.3.33 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.3.32 +Fri, 24 May 2024 00:15:09 GMT + +_Version update only_ + +## 0.3.31 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 0.3.30 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.3.29 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.3.28 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.3.27 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.3.26 +Wed, 08 May 2024 22:23:51 GMT + +_Version update only_ + +## 0.3.25 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 0.3.24 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.3.23 +Thu, 28 Mar 2024 22:42:23 GMT + +_Version update only_ + +## 0.3.22 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 0.3.21 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 0.3.20 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 0.3.19 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 0.3.18 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 0.3.17 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 0.3.16 +Thu, 29 Feb 2024 07:11:45 GMT + +_Version update only_ + +## 0.3.15 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.3.14 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.3.13 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.3.12 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.3.11 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.3.10 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.3.9 +Tue, 20 Feb 2024 16:10:53 GMT + +_Version update only_ + +## 0.3.8 +Mon, 19 Feb 2024 21:54:26 GMT + +_Version update only_ + +## 0.3.7 +Sat, 17 Feb 2024 06:24:35 GMT + +_Version update only_ + +## 0.3.6 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 0.3.5 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.3.4 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.3.3 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.3.2 +Tue, 23 Jan 2024 20:12:58 GMT + +_Version update only_ + +## 0.3.1 +Tue, 23 Jan 2024 16:15:05 GMT + +_Version update only_ + +## 0.3.0 +Tue, 16 Jan 2024 18:30:10 GMT + +### Minor changes + +- Add support for TypeScript 5.3 + +## 0.2.16 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.2.15 +Wed, 20 Dec 2023 01:09:46 GMT + +_Version update only_ + +## 0.2.14 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.2.13 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.2.12 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 0.2.11 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 0.2.10 +Mon, 30 Oct 2023 23:36:38 GMT + +_Version update only_ + +## 0.2.9 +Sun, 01 Oct 2023 02:56:30 GMT + +_Version update only_ + +## 0.2.8 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 0.2.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.2.6 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 0.2.5 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 0.2.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.2.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 0.2.2 +Fri, 22 Sep 2023 00:05:51 GMT + +_Version update only_ + +## 0.2.1 +Tue, 19 Sep 2023 15:21:51 GMT + +_Version update only_ + +## 0.2.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +### Patches + +- Reduce verbosity by only printing the "not supported in watch mode" warning during the initial build. + +## 0.1.22 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 0.1.21 +Sat, 29 Jul 2023 00:22:51 GMT + +_Version update only_ + +## 0.1.20 +Thu, 20 Jul 2023 20:47:29 GMT + +_Version update only_ + +## 0.1.19 +Wed, 19 Jul 2023 00:20:31 GMT + +### Patches + +- Updated semver dependency + +## 0.1.18 +Fri, 14 Jul 2023 15:20:45 GMT + +### Patches + +- Treat a malformed cache file the same as no cache file (i.e. recheck everything) instead of throwing an error.. + +## 0.1.17 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 0.1.16 +Wed, 12 Jul 2023 15:20:40 GMT + +_Version update only_ + +## 0.1.15 +Wed, 12 Jul 2023 00:23:29 GMT + +_Version update only_ + +## 0.1.14 +Fri, 07 Jul 2023 00:19:33 GMT + +_Version update only_ + +## 0.1.13 +Thu, 06 Jul 2023 00:16:20 GMT + +_Version update only_ + +## 0.1.12 +Tue, 04 Jul 2023 00:18:47 GMT + +_Version update only_ + +## 0.1.11 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 0.1.10 +Thu, 15 Jun 2023 00:21:02 GMT + +_Version update only_ + +## 0.1.9 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 0.1.8 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 0.1.7 +Tue, 13 Jun 2023 01:49:01 GMT + +_Version update only_ + +## 0.1.6 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 0.1.5 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 0.1.4 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 0.1.3 +Thu, 08 Jun 2023 00:20:02 GMT + +### Patches + +- Use the temp folder instead of the cache folder. + +## 0.1.2 +Wed, 07 Jun 2023 22:45:16 GMT + +_Version update only_ + +## 0.1.1 +Mon, 05 Jun 2023 21:45:21 GMT + +### Patches + +- Fix a regression that caused this error: "[build:lint] The create() function for rule ___ did not return an object." ## 0.1.0 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/heft-plugins/heft-lint-plugin/config/heft.json b/heft-plugins/heft-lint-plugin/config/heft.json new file mode 100644 index 00000000000..8d1359f022f --- /dev/null +++ b/heft-plugins/heft-lint-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-lint-plugin/config/rig.json b/heft-plugins/heft-lint-plugin/config/rig.json index 6ac88a96368..cc98dea43dd 100644 --- a/heft-plugins/heft-lint-plugin/config/rig.json +++ b/heft-plugins/heft-lint-plugin/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "decoupled-local-node-rig" } diff --git a/heft-plugins/heft-lint-plugin/eslint.config.js b/heft-plugins/heft-lint-plugin/eslint.config.js new file mode 100644 index 00000000000..e54effd122a --- /dev/null +++ b/heft-plugins/heft-lint-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-lint-plugin/heft-plugin.json b/heft-plugins/heft-lint-plugin/heft-plugin.json index e5f8bbd62e7..13c0b3f3656 100644 --- a/heft-plugins/heft-lint-plugin/heft-plugin.json +++ b/heft-plugins/heft-lint-plugin/heft-plugin.json @@ -1,10 +1,20 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-plugin.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", "taskPlugins": [ { "pluginName": "lint-plugin", - "entryPoint": "./lib/LintPlugin" + "entryPoint": "./lib-commonjs/LintPlugin", + "optionsSchema": "./lib-commonjs/schemas/heft-lint-plugin.schema.json", + + "parameterScope": "lint", + "parameters": [ + { + "longName": "--fix", + "parameterKind": "flag", + "description": "Fix all encountered rule violations where the violated rule provides a fixer. When running in production mode, fixes will be disabled regardless of this parameter." + } + ] } ] } diff --git a/heft-plugins/heft-lint-plugin/package.json b/heft-plugins/heft-lint-plugin/package.json index 671eabbd1bd..e4c2a7c3e41 100644 --- a/heft-plugins/heft-lint-plugin/package.json +++ b/heft-plugins/heft-lint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/heft-lint-plugin", - "version": "0.1.0", + "version": "1.2.22", "description": "A Heft plugin for using ESLint or TSLint. Intended for use with @rushstack/heft-typescript-plugin", "repository": { "type": "git", @@ -10,30 +10,50 @@ "homepage": "https://rushstack.io/pages/heft/overview/", "license": "MIT", "scripts": { - "build": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged build --clean", - "start": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged test --clean --watch", - "_phase:build": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged build --clean", - "_phase:test": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged test --no-build" + "build": "heft build --clean", + "start": "heft build-watch", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" }, "peerDependencies": { - "@rushstack/heft": "0.51.0" + "@rushstack/heft": "1.2.22" }, "dependencies": { "@rushstack/node-core-library": "workspace:*", - "semver": "~7.3.0" + "json-stable-stringify-without-jsonify": "1.0.1", + "semver": "~7.7.4" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", - "@rushstack/heft-legacy": "npm:@rushstack/heft@0.50.6", - "@rushstack/heft-node-rig": "1.13.0", - "@types/eslint": "8.2.0", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "@types/semver": "7.3.5", - "eslint": "~8.7.0", - "tslint": "~5.20.1", - "typescript": "~5.0.4" - } + "@rushstack/heft": "workspace:*", + "@rushstack/terminal": "workspace:*", + "@types/eslint": "9.6.1", + "@types/eslint-8": "npm:@types/eslint@8.56.10", + "@types/json-stable-stringify-without-jsonify": "1.0.2", + "@types/semver": "7.7.1", + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0", + "eslint-8": "npm:eslint@~8.57.0", + "typescript": "~5.8.2", + "tslint": "~5.20.1" + }, + "exports": { + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "sideEffects": false } diff --git a/heft-plugins/heft-lint-plugin/src/Eslint.ts b/heft-plugins/heft-lint-plugin/src/Eslint.ts index 824a1c1de9d..aea83cbee8d 100644 --- a/heft-plugins/heft-lint-plugin/src/Eslint.ts +++ b/heft-plugins/heft-lint-plugin/src/Eslint.ts @@ -1,18 +1,26 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as crypto from 'crypto'; -import * as semver from 'semver'; +import path from 'node:path'; +import { createHash, type Hash } from 'node:crypto'; +import { performance } from 'node:perf_hooks'; + import type * as TTypescript from 'typescript'; import type * as TEslint from 'eslint'; -import { performance } from 'perf_hooks'; -import { FileError } from '@rushstack/node-core-library'; +import type * as TEslintLegacy from 'eslint-8'; +import * as semver from 'semver'; +import stableStringify from 'json-stable-stringify-without-jsonify'; + +import { FileError, FileSystem } from '@rushstack/node-core-library'; +import type { HeftConfiguration } from '@rushstack/heft'; import { LinterBase, type ILinterBaseOptions } from './LinterBase'; +import type { IExtendedSourceFile } from './internalTypings/TypeScriptInternals'; +import { name as pluginName, version as pluginVersion } from '../package.json'; interface IEslintOptions extends ILinterBaseOptions { - eslintPackagePath: string; + eslintPackage: typeof TEslint | typeof TEslintLegacy; + eslintTimings: Map; } interface IEslintTiming { @@ -20,91 +28,274 @@ interface IEslintTiming { time: (key: string, fn: (...args: unknown[]) => void) => (...args: unknown[]) => void; } -const enum EslintMessageSeverity { +enum EslintMessageSeverity { warning = 1, error = 2 } -export class Eslint extends LinterBase { - private readonly _eslintPackagePath: string; - private readonly _eslintTimings: Map = new Map(); +// Patch the timer used to track rule execution time. This allows us to get access to the detailed information +// about how long each rule took to execute, which we provide on the CLI when running in verbose mode. +async function patchTimerAsync(eslintPackagePath: string, timingsMap: Map): Promise { + const timingModulePath: string = `${eslintPackagePath}/lib/linter/timing`; + const timing: IEslintTiming = (await import(timingModulePath)).default; + timing.enabled = true; + const patchedTime: (key: string, fn: (...args: unknown[]) => unknown) => (...args: unknown[]) => unknown = ( + key: string, + fn: (...args: unknown[]) => unknown + ) => { + return (...args: unknown[]) => { + const startTime: number = performance.now(); + const result: unknown = fn(...args); + const endTime: number = performance.now(); + const existingTiming: number = timingsMap.get(key) || 0; + timingsMap.set(key, existingTiming + endTime - startTime); + return result; + }; + }; + timing.time = patchedTime; +} - private _eslintPackage: typeof TEslint; - private _eslint!: TEslint.ESLint; +function getFormattedErrorMessage( + lintMessage: TEslint.Linter.LintMessage | TEslintLegacy.Linter.LintMessage +): string { + // https://eslint.org/docs/developer-guide/nodejs-api#◆-lintmessage-type + return lintMessage.ruleId ? `(${lintMessage.ruleId}) ${lintMessage.message}` : lintMessage.message; +} - public constructor(options: IEslintOptions) { - super('eslint', options); - this._eslintPackagePath = options.eslintPackagePath; +function parserOptionsToJson(this: TEslint.Linter.LanguageOptions['parserOptions']): object { + const serializableParserOptions: TEslint.Linter.LanguageOptions['parserOptions'] = { + ...this, + // Remove the programs to avoid circular references and non-serializable data + programs: undefined + }; + return serializableParserOptions; +} - // This must happen before the rest of the linter package is loaded - this._patchTimer(options.eslintPackagePath); - this._eslintPackage = require(options.eslintPackagePath); - } +const ESLINT_CONFIG_JS_FILENAME: string = 'eslint.config.js'; +const ESLINT_CONFIG_CJS_FILENAME: string = 'eslint.config.cjs'; +const ESLINT_CONFIG_MJS_FILENAME: string = 'eslint.config.mjs'; +const LEGACY_ESLINTRC_JS_FILENAME: string = '.eslintrc.js'; +const LEGACY_ESLINTRC_CJS_FILENAME: string = '.eslintrc.cjs'; - public printVersionHeader(): void { - this._terminal.writeLine(`Using ESLint version ${this._eslintPackage.Linter.version}`); +const ESLINT_LEGACY_CONFIG_FILENAMES: Set = new Set([ + LEGACY_ESLINTRC_JS_FILENAME, + LEGACY_ESLINTRC_CJS_FILENAME +]); - const majorVersion: number = semver.major(this._eslintPackage.Linter.version); - if (majorVersion < 7) { +export class Eslint extends LinterBase { + private readonly _eslintPackage: typeof TEslint | typeof TEslintLegacy; + private readonly _eslintPackageVersion: semver.SemVer; + private readonly _linter: TEslint.ESLint | TEslintLegacy.ESLint; + private readonly _eslintTimings: Map = new Map(); + private readonly _currentFixMessages: (TEslint.Linter.LintMessage | TEslintLegacy.Linter.LintMessage)[] = + []; + private readonly _fixMessagesByResult: Map< + TEslint.ESLint.LintResult | TEslintLegacy.ESLint.LintResult, + (TEslint.Linter.LintMessage | TEslintLegacy.Linter.LintMessage)[] + > = new Map(); + private readonly _sarifLogPath: string | undefined; + private readonly _configHashMap: WeakMap = new WeakMap(); + + protected constructor(options: IEslintOptions) { + super('eslint', options); + + const { + buildFolderPath, + eslintPackage, + linterConfigFilePath, + tsProgram, + eslintTimings, + fix, + sarifLogPath + } = options; + this._eslintPackage = eslintPackage; + this._eslintPackageVersion = new semver.SemVer(eslintPackage.ESLint.version); + const linterConfigFileName: string = path.basename(linterConfigFilePath); + if (this._eslintPackageVersion.major < 9 && !ESLINT_LEGACY_CONFIG_FILENAMES.has(linterConfigFileName)) { + throw new Error( + `You must use a ${LEGACY_ESLINTRC_JS_FILENAME} or a ${LEGACY_ESLINTRC_CJS_FILENAME} file with ESLint ` + + `8 or older. The provided config file is "${linterConfigFilePath}".` + ); + } else if ( + this._eslintPackageVersion.major >= 9 && + ESLINT_LEGACY_CONFIG_FILENAMES.has(linterConfigFileName) + ) { throw new Error( - 'Heft requires ESLint 7 or newer. Your ESLint version is too old:\n' + this._eslintPackagePath + `You must use an ${ESLINT_CONFIG_JS_FILENAME}, ${ESLINT_CONFIG_CJS_FILENAME}, or an ` + + `${ESLINT_CONFIG_MJS_FILENAME} file with ESLint 9 or newer. The provided config file is ` + + `"${linterConfigFilePath}".` ); } - if (majorVersion > 8) { + + this._sarifLogPath = sarifLogPath; + + let overrideConfig: TEslint.Linter.Config | TEslintLegacy.Linter.Config | undefined; + let fixFn: Exclude; + if (fix) { + // We do not recieve the messages for the issues that were fixed, so we need to track them ourselves + // so that we can log them after the fix is applied. This array will be populated by the fix function, + // and subsequently mapped to the results in the ESLint.lintFileAsync method below. After the messages + // are mapped, the array will be cleared so that it is ready for the next fix operation. + fixFn = (message: TEslint.Linter.LintMessage | TEslintLegacy.Linter.LintMessage) => { + this._currentFixMessages.push(message); + return true; + }; + } else if (this._eslintPackageVersion.major <= 8) { + // The @typescript-eslint/parser package allows providing an existing TypeScript program to avoid needing + // to reparse. However, fixers in ESLint run in multiple passes against the underlying code until the + // fix fully succeeds. This conflicts with providing an existing program as the code no longer maps to + // the provided program, producing garbage fix output. To avoid this, only provide the existing program + // if we're not fixing. + const legacyEslintOverrideConfig: TEslintLegacy.Linter.Config = { + parserOptions: { + programs: [tsProgram], + toJSON: parserOptionsToJson + } + }; + overrideConfig = legacyEslintOverrideConfig; + } else { + let overrideParserOptions: TEslint.Linter.ParserOptions = { + programs: [tsProgram], + // Used by stableStringify and ESLint > 9.28.0 + toJSON: parserOptionsToJson, + // ESlint's merge logic for parserOptions is a "replace", so we need to set this again + tsconfigRootDir: buildFolderPath + }; + if (this._eslintPackageVersion.minor < 28) { + overrideParserOptions = Object.defineProperties(overrideParserOptions, { + // Support for `toJSON` within languageOptions was added in ESLint 9.28.0 + // This hack tells ESLint's `languageOptionsToJSON` function to replace the entire `parserOptions` object with `@rushstack/heft-lint-plugin@${version}` + meta: { + value: { + name: pluginName, + version: pluginVersion + } + } + }); + } + // The @typescript-eslint/parser package allows providing an existing TypeScript program to avoid needing + // to reparse. However, fixers in ESLint run in multiple passes against the underlying code until the + // fix fully succeeds. This conflicts with providing an existing program as the code no longer maps to + // the provided program, producing garbage fix output. To avoid this, only provide the existing program + // if we're not fixing. + const eslintOverrideConfig: TEslint.Linter.Config = { + languageOptions: { + parserOptions: overrideParserOptions + } + }; + overrideConfig = eslintOverrideConfig; + } + + this._linter = new eslintPackage.ESLint({ + cwd: buildFolderPath, + overrideConfigFile: linterConfigFilePath, + // Override config takes precedence over overrideConfigFile + // eslint-disable-next-line @typescript-eslint/no-explicit-any + overrideConfig: overrideConfig as any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fix: fixFn as any + }); + this._eslintTimings = eslintTimings; + } + + public static async resolveEslintConfigFilePathAsync( + heftConfiguration: HeftConfiguration + ): Promise { + // When project is configured with "type": "module" in package.json, the config file must have a .cjs extension + // so use it if it exists + const configPathCandidates: string[] = [ + `${heftConfiguration.buildFolderPath}/${ESLINT_CONFIG_JS_FILENAME}`, + `${heftConfiguration.buildFolderPath}/${ESLINT_CONFIG_CJS_FILENAME}`, + `${heftConfiguration.buildFolderPath}/${ESLINT_CONFIG_MJS_FILENAME}`, + `${heftConfiguration.buildFolderPath}/${LEGACY_ESLINTRC_JS_FILENAME}`, + `${heftConfiguration.buildFolderPath}/${LEGACY_ESLINTRC_CJS_FILENAME}` + ]; + const foundConfigs: string[] = ( + await Promise.all(configPathCandidates.map(async (p: string) => (await FileSystem.existsAsync(p)) && p)) + ).filter((p) => p !== false); + + if (foundConfigs.length > 1) { + throw new Error(`Project contains multiple ESLint configuration files: "${foundConfigs.join('", "')}"`); + } + + return foundConfigs[0]; + } + + public static async initializeAsync(options: ILinterBaseOptions): Promise { + const { linterToolPath } = options; + const eslintTimings: Map = new Map(); + // This must happen before the rest of the linter package is loaded + await patchTimerAsync(linterToolPath, eslintTimings); + + const eslintPackage: typeof TEslint = await import(linterToolPath); + return new Eslint({ + ...options, + eslintPackage, + eslintTimings + }); + } + + public override printVersionHeader(): void { + const { version, major } = this._eslintPackageVersion; + this._terminal.writeLine(`Using ESLint version ${version}`); + + if (major < 7) { + throw new Error('Heft requires ESLint 7 or newer. Your ESLint version is too old'); + } else if (major > 9) { // We don't use writeWarningLine() here because, if the person wants to take their chances with // a newer ESLint release, their build should be allowed to succeed. this._terminal.writeLine( - 'The ESLint version is newer than the latest version that was tested with Heft; it may not work correctly:' + 'The ESLint version is newer than the latest version that was tested with Heft, so it may not work correctly.' ); - this._terminal.writeLine(this._eslintPackagePath); } } - protected async getCacheVersionAsync(): Promise { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const eslintBaseConfiguration: any = await this._eslint.calculateConfigForFile( - this._linterConfigFilePath - ); - const eslintConfigHash: crypto.Hash = crypto - .createHash('sha1') - .update(JSON.stringify(eslintBaseConfiguration)); - const eslintConfigVersion: string = `${this._eslintPackage.Linter.version}_${eslintConfigHash.digest( - 'hex' - )}`; - - return eslintConfigVersion; + protected override async getCacheVersionAsync(): Promise { + return `${this._eslintPackageVersion.version}_${process.version}`; } - protected async initializeAsync(tsProgram: TTypescript.Program): Promise { - // Override config takes precedence over overrideConfigFile, which allows us to provide - // the source TypeScript program. - this._eslint = new this._eslintPackage.ESLint({ - cwd: this._buildFolderPath, - overrideConfigFile: this._linterConfigFilePath, - overrideConfig: { - parserOptions: { - programs: [tsProgram] - } - } - }); + protected override async getSourceFileHashAsync(sourceFile: IExtendedSourceFile): Promise { + const sourceFileEslintConfiguration: TEslint.Linter.Config = await this._linter.calculateConfigForFile( + sourceFile.fileName + ); + + const hash: Hash = createHash('sha1'); + // Use a stable stringifier to ensure that the hash is always the same, even if the order of the properties + // changes. This is also done in ESLint + // https://github.com/eslint/eslint/blob/8bbabc4691d97733a422180c71eba6c097b35475/lib/cli-engine/lint-result-cache.js#L50 + hash.update(stableStringify(sourceFileEslintConfiguration)); + + // Since the original hash can either come from TypeScript or from manually hashing the file, we can just + // append the config hash to the original hash to avoid reducing the hash space + const originalSourceFileHash: string = await super.getSourceFileHashAsync(sourceFile); + return `${originalSourceFileHash}_${hash.digest('base64')}`; } - protected async lintFileAsync(sourceFile: TTypescript.SourceFile): Promise { - const lintResults: TEslint.ESLint.LintResult[] = await this._eslint.lintText(sourceFile.text, { - filePath: sourceFile.fileName - }); + protected override async lintFileAsync( + sourceFile: TTypescript.SourceFile + ): Promise { + const lintResults: TEslint.ESLint.LintResult[] | TEslintLegacy.ESLint.LintResult[] = + await this._linter.lintText(sourceFile.text, { filePath: sourceFile.fileName }); - const failures: TEslint.ESLint.LintResult[] = []; - for (const lintResult of lintResults) { - if (lintResult.messages.length > 0) { - failures.push(lintResult); - } + // Map the fix messages to the results. This API should only return one result per file, so we can be sure + // that the fix messages belong to the returned result. If we somehow receive multiple results, we will + // drop the messages on the floor, but since they are only used for logging, this should not be a problem. + const fixMessages: (TEslint.Linter.LintMessage | TEslintLegacy.Linter.LintMessage)[] = + this._currentFixMessages.splice(0); + if (lintResults.length === 1) { + this._fixMessagesByResult.set(lintResults[0], fixMessages); } - return failures; + this._fixesPossible ||= + !this._fix && + lintResults.some((lintResult: TEslint.ESLint.LintResult | TEslintLegacy.ESLint.LintResult) => { + return lintResult.fixableErrorCount + lintResult.fixableWarningCount > 0; + }); + + return lintResults; } - protected lintingFinished(lintFailures: TEslint.ESLint.LintResult[]): void { + protected override async lintingFinishedAsync(lintResults: TEslint.ESLint.LintResult[]): Promise { let omittedRuleCount: number = 0; const timings: [string, number][] = Array.from(this._eslintTimings).sort( (x: [string, number], y: [string, number]) => { @@ -123,63 +314,86 @@ export class Eslint extends LinterBase { this._terminal.writeVerboseLine(`${omittedRuleCount} rules took 0ms`); } - const errors: Error[] = []; - const warnings: Error[] = []; - - for (const eslintFailure of lintFailures) { - for (const message of eslintFailure.messages) { - // https://eslint.org/docs/developer-guide/nodejs-api#◆-lintmessage-type - const formattedMessage: string = message.ruleId - ? `(${message.ruleId}) ${message.message}` - : message.message; - const errorObject: FileError = new FileError(formattedMessage, { - absolutePath: eslintFailure.filePath, - projectFolder: this._buildFolderPath, - line: message.line, - column: message.column - }); - switch (message.severity) { + if (this._fix && this._fixMessagesByResult.size > 0) { + await this._eslintPackage.ESLint.outputFixes(lintResults); + } + + for (const lintResult of lintResults) { + // Report linter fixes to the logger. These will only be returned when the underlying failure was fixed + const fixMessages: TEslint.Linter.LintMessage[] | TEslintLegacy.Linter.LintMessage[] | undefined = + this._fixMessagesByResult.get(lintResult); + if (fixMessages) { + for (const fixMessage of fixMessages) { + const formattedMessage: string = `[FIXED] ${getFormattedErrorMessage(fixMessage)}`; + const errorObject: FileError = this._getLintFileError(lintResult, fixMessage, formattedMessage); + this._scopedLogger.emitWarning(errorObject); + } + } + + // Report linter errors and warnings to the logger + for (const lintMessage of lintResult.messages) { + const errorObject: FileError = this._getLintFileError(lintResult, lintMessage); + switch (lintMessage.severity) { case EslintMessageSeverity.error: { - errors.push(errorObject); + this._scopedLogger.emitError(errorObject); break; } case EslintMessageSeverity.warning: { - warnings.push(errorObject); + this._scopedLogger.emitWarning(errorObject); break; } } } } - for (const error of errors) { - this._scopedLogger.emitError(error); - } + const sarifLogPath: string | undefined = this._sarifLogPath; + if (sarifLogPath) { + const rulesMeta: TEslint.ESLint.LintResultData['rulesMeta'] = + this._linter.getRulesMetaForResults(lintResults); + const { formatEslintResultsAsSARIF } = await import('./SarifFormatter'); + const sarifString: string = JSON.stringify( + formatEslintResultsAsSARIF(lintResults, rulesMeta, { + ignoreSuppressed: false, + eslintVersion: this._eslintPackage.ESLint.version, + buildFolderPath: this._buildFolderPath + }), + undefined, + 2 + ); - for (const warning of warnings) { - this._scopedLogger.emitWarning(warning); + await FileSystem.writeFileAsync(sarifLogPath, sarifString, { ensureFolderExists: true }); } } - protected async isFileExcludedAsync(filePath: string): Promise { - return await this._eslint.isPathIgnored(filePath); + protected override async isFileExcludedAsync(filePath: string): Promise { + return await this._linter.isPathIgnored(filePath); } - private _patchTimer(eslintPackagePath: string): void { - const timing: IEslintTiming = require(path.join(eslintPackagePath, 'lib', 'linter', 'timing')); - timing.enabled = true; - const patchedTime: (key: string, fn: (...args: unknown[]) => void) => (...args: unknown[]) => void = ( - key: string, - fn: (...args: unknown[]) => void - ) => { - return (...args: unknown[]) => { - const startTime: number = performance.now(); - fn(...args); - const endTime: number = performance.now(); - const existingTiming: number = this._eslintTimings.get(key) || 0; - this._eslintTimings.set(key, existingTiming + endTime - startTime); - }; - }; - timing.time = patchedTime; + protected override hasLintFailures( + lintResults: (TEslint.ESLint.LintResult | TEslintLegacy.ESLint.LintResult)[] + ): boolean { + return lintResults.some((lintResult: TEslint.ESLint.LintResult | TEslintLegacy.ESLint.LintResult) => { + return ( + !lintResult.suppressedMessages?.length && (lintResult.errorCount > 0 || lintResult.warningCount > 0) + ); + }); + } + + private _getLintFileError( + lintResult: TEslint.ESLint.LintResult | TEslintLegacy.ESLint.LintResult, + lintMessage: TEslint.Linter.LintMessage | TEslintLegacy.Linter.LintMessage, + message?: string + ): FileError { + if (!message) { + message = getFormattedErrorMessage(lintMessage); + } + + return new FileError(message, { + absolutePath: lintResult.filePath, + projectFolder: this._buildFolderPath, + line: lintMessage.line, + column: lintMessage.column + }); } } diff --git a/heft-plugins/heft-lint-plugin/src/LintPlugin.ts b/heft-plugins/heft-lint-plugin/src/LintPlugin.ts index 24ebe720b59..74a4e384d97 100644 --- a/heft-plugins/heft-lint-plugin/src/LintPlugin.ts +++ b/heft-plugins/heft-lint-plugin/src/LintPlugin.ts @@ -1,6 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { FileSystem } from '@rushstack/node-core-library'; + +import path from 'node:path'; + +import type * as TTypescript from 'typescript'; + import type { HeftConfiguration, IHeftTaskSession, @@ -13,19 +17,58 @@ import type { IChangedFilesHookOptions, ITypeScriptPluginAccessor } from '@rushstack/heft-typescript-plugin'; +import { AlreadyReportedError } from '@rushstack/node-core-library'; +import type { LinterBase } from './LinterBase'; import { Eslint } from './Eslint'; import { Tslint } from './Tslint'; import type { IExtendedProgram, IExtendedSourceFile } from './internalTypings/TypeScriptInternals'; const PLUGIN_NAME: 'lint-plugin' = 'lint-plugin'; +const TYPESCRIPT_PLUGIN_PACKAGE_NAME: '@rushstack/heft-typescript-plugin' = + '@rushstack/heft-typescript-plugin'; const TYPESCRIPT_PLUGIN_NAME: typeof TypeScriptPluginName = 'typescript-plugin'; -const ESLINTRC_JS_FILENAME: string = '.eslintrc.js'; -const ESLINTRC_CJS_FILENAME: string = '.eslintrc.cjs'; +const FIX_PARAMETER_NAME: string = '--fix'; + +interface ILintPluginOptions { + alwaysFix?: boolean; + sarifLogPath?: string; +} -export default class LintPlugin implements IHeftTaskPlugin { - private readonly _lintingPromises: Promise[] = []; +interface ILintOptions { + taskSession: IHeftTaskSession; + heftConfiguration: HeftConfiguration; + tsProgram: IExtendedProgram; + fix?: boolean; + sarifLogPath?: string; + changedFiles?: ReadonlySet; +} +function checkFix(taskSession: IHeftTaskSession, pluginOptions?: ILintPluginOptions): boolean { + let fix: boolean = + pluginOptions?.alwaysFix || taskSession.parameters.getFlagParameter(FIX_PARAMETER_NAME).value; + if (fix && taskSession.parameters.production) { + // Write this as a standard output message since we don't want to throw errors when running in + // production mode and "alwaysFix" is specified in the plugin options + taskSession.logger.terminal.writeLine( + 'Fix mode has been disabled since Heft is running in production mode' + ); + fix = false; + } + return fix; +} + +function getSarifLogPath( + heftConfiguration: HeftConfiguration, + pluginOptions?: ILintPluginOptions +): string | undefined { + const relativeSarifLogPath: string | undefined = pluginOptions?.sarifLogPath; + const sarifLogPath: string | undefined = + relativeSarifLogPath && path.resolve(heftConfiguration.buildFolderPath, relativeSarifLogPath); + return sarifLogPath; +} + +export default class LintPlugin implements IHeftTaskPlugin { // These are initliazed by _initAsync private _initPromise!: Promise; private _eslintToolPath: string | undefined; @@ -33,47 +76,117 @@ export default class LintPlugin implements IHeftTaskPlugin { private _tslintToolPath: string | undefined; private _tslintConfigFilePath: string | undefined; - public apply(taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration): void { + public apply( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + pluginOptions?: ILintPluginOptions + ): void { // Disable linting in watch mode. Some lint rules require the context of multiple files, which // may not be available in watch mode. - if (!taskSession.parameters.watch) { - // Use the changed files hook to kick off linting asynchronously - taskSession.requestAccessToPluginByName( - '@rushstack/heft-typescript-plugin', - TYPESCRIPT_PLUGIN_NAME, - (accessor: ITypeScriptPluginAccessor) => { - // Hook into the changed files hook to kick off linting, which will be awaited in the run hook - accessor.onChangedFilesHook.tap( - PLUGIN_NAME, - (changedFilesHookOptions: IChangedFilesHookOptions) => { - const lintingPromise: Promise = this._lintAsync( - taskSession, - heftConfiguration, - changedFilesHookOptions.program as IExtendedProgram, - changedFilesHookOptions.changedFiles as ReadonlySet - ); - lintingPromise.catch(() => { - // Suppress unhandled promise rejection error - }); - // Hold on to the original promise, which will throw in the run hook if it unexpectedly fails - this._lintingPromises.push(lintingPromise); - } - ); + if (taskSession.parameters.watch) { + let warningPrinted: boolean = false; + taskSession.hooks.run.tapPromise(PLUGIN_NAME, async () => { + if (warningPrinted) { + return; } - ); + + // Warn since don't run the linters when in watch mode. + taskSession.logger.terminal.writeWarningLine("Linting isn't currently supported in watch mode"); + warningPrinted = true; + }); + return; } + const fix: boolean = checkFix(taskSession, pluginOptions); + const sarifLogPath: string | undefined = getSarifLogPath(heftConfiguration, pluginOptions); + + // To support standalone linting, track if we have hooked to the typescript plugin + let inTypescriptPhase: boolean = false; + + // Use the changed files hook to collect the files and programs from TypeScript + let typescriptChangedFiles: [IExtendedProgram, ReadonlySet][] = []; + taskSession.requestAccessToPluginByName( + TYPESCRIPT_PLUGIN_PACKAGE_NAME, + TYPESCRIPT_PLUGIN_NAME, + (accessor: ITypeScriptPluginAccessor) => { + // Set the flag to indicate that we are in the typescript phase + inTypescriptPhase = true; + + // Hook into the changed files hook to collect the changed files and their programs + accessor.onChangedFilesHook.tap(PLUGIN_NAME, (changedFilesHookOptions: IChangedFilesHookOptions) => { + typescriptChangedFiles.push([ + changedFilesHookOptions.program as IExtendedProgram, + changedFilesHookOptions.changedFiles as ReadonlySet + ]); + }); + } + ); + taskSession.hooks.run.tapPromise(PLUGIN_NAME, async (options: IHeftTaskRunHookOptions) => { + // If we are not in the typescript phase, we need to create a typescript program + // from the tsconfig file + if (!inTypescriptPhase) { + const tsProgram: IExtendedProgram = await this._createTypescriptProgramAsync( + heftConfiguration, + taskSession + ); + typescriptChangedFiles.push([tsProgram, new Set(tsProgram.getSourceFiles())]); + } + // Run the linters to completion. Linters emit errors and warnings to the logger. - if (taskSession.parameters.watch) { - // Warn since don't run the linters when in watch mode. - taskSession.logger.terminal.writeWarningLine("Linting isn't currently supported in watch mode."); - } else { - await Promise.all(this._lintingPromises); + for (const [tsProgram, changedFiles] of typescriptChangedFiles) { + try { + await this._lintAsync({ + taskSession, + heftConfiguration, + tsProgram, + changedFiles, + fix, + sarifLogPath + }); + } catch (error) { + if (!(error instanceof AlreadyReportedError)) { + taskSession.logger.emitError(error as Error); + } + } + } + + // Clear the changed files so that we don't lint them again if the task is executed again + typescriptChangedFiles = []; + + // We rely on the linters to emit errors and warnings to the logger. If they do, we throw an + // AlreadyReportedError to indicate that the task failed, but we don't want to throw an error + // if the linter has already reported it. + if (taskSession.logger.hasErrors) { + throw new AlreadyReportedError(); } }); } + private async _createTypescriptProgramAsync( + heftConfiguration: HeftConfiguration, + taskSession: IHeftTaskSession + ): Promise { + const typescriptPath: string = await heftConfiguration.rigPackageResolver.resolvePackageAsync( + 'typescript', + taskSession.logger.terminal + ); + const ts: typeof TTypescript = await import(typescriptPath); + // Create a typescript program from the tsconfig file + const tsconfigPath: string = path.resolve(heftConfiguration.buildFolderPath, 'tsconfig.json'); + const parsed: TTypescript.ParsedCommandLine = ts.parseJsonConfigFileContent( + ts.readConfigFile(tsconfigPath, ts.sys.readFile).config, + ts.sys, + path.dirname(tsconfigPath) + ); + const program: IExtendedProgram = ts.createProgram({ + rootNames: parsed.fileNames, + options: parsed.options + }) as IExtendedProgram; + + return program; + } + private async _ensureInitializedAsync( taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration @@ -87,7 +200,7 @@ export default class LintPlugin implements IHeftTaskPlugin { private async _initInnerAsync(heftConfiguration: HeftConfiguration, logger: IScopedLogger): Promise { // Locate the tslint linter if enabled - this._tslintConfigFilePath = await this._resolveTslintConfigFilePathAsync(heftConfiguration); + this._tslintConfigFilePath = await Tslint.resolveTslintConfigFilePathAsync(heftConfiguration); if (this._tslintConfigFilePath) { this._tslintToolPath = await heftConfiguration.rigPackageResolver.resolvePackageAsync( 'tslint', @@ -96,138 +209,69 @@ export default class LintPlugin implements IHeftTaskPlugin { } // Locate the eslint linter if enabled - this._eslintConfigFilePath = await this._resolveEslintConfigFilePathAsync(heftConfiguration); + this._eslintConfigFilePath = await Eslint.resolveEslintConfigFilePathAsync(heftConfiguration); if (this._eslintConfigFilePath) { + logger.terminal.writeVerboseLine(`ESLint config file path: ${this._eslintConfigFilePath}`); this._eslintToolPath = await heftConfiguration.rigPackageResolver.resolvePackageAsync( 'eslint', logger.terminal ); + } else { + logger.terminal.writeVerboseLine('No ESLint config file found'); } } - private async _lintAsync( - taskSession: IHeftTaskSession, - heftConfiguration: HeftConfiguration, - tsProgram: IExtendedProgram, - changedFiles?: ReadonlySet - ): Promise { + private async _lintAsync(options: ILintOptions): Promise { + const { taskSession, heftConfiguration, tsProgram, changedFiles, fix, sarifLogPath } = options; + // Ensure that we have initialized. This promise is cached, so calling init // multiple times will only init once. await this._ensureInitializedAsync(taskSession, heftConfiguration); - // Now that we know we have initialized properly, run the linter(s) - const lintingPromises: Promise[] = []; - if (this._eslintToolPath) { - lintingPromises.push( - this._runEslintAsync( - taskSession, - heftConfiguration, - this._eslintToolPath, - this._eslintConfigFilePath!, - tsProgram, - changedFiles - ) - ); + const linters: LinterBase[] = []; + if (this._eslintConfigFilePath && this._eslintToolPath) { + const eslintLinter: Eslint = await Eslint.initializeAsync({ + tsProgram, + fix, + sarifLogPath, + scopedLogger: taskSession.logger, + linterToolPath: this._eslintToolPath, + linterConfigFilePath: this._eslintConfigFilePath, + buildFolderPath: heftConfiguration.buildFolderPath, + buildMetadataFolderPath: taskSession.tempFolderPath + }); + linters.push(eslintLinter); } - if (this._tslintToolPath) { - lintingPromises.push( - this._runTslintAsync( - taskSession, - heftConfiguration, - this._tslintToolPath, - this._tslintConfigFilePath!, - tsProgram, - changedFiles - ) - ); - } - - await Promise.all(lintingPromises); - } - private async _runEslintAsync( - taskSession: IHeftTaskSession, - heftConfiguration: HeftConfiguration, - eslintToolPath: string, - eslintConfigFilePath: string, - tsProgram: IExtendedProgram, - changedFiles?: ReadonlySet | undefined - ): Promise { - const eslint: Eslint = new Eslint({ - scopedLogger: taskSession.logger, - eslintPackagePath: eslintToolPath, - linterConfigFilePath: eslintConfigFilePath, - buildFolderPath: heftConfiguration.buildFolderPath, - buildMetadataFolderPath: taskSession.cacheFolderPath - }); - - eslint.printVersionHeader(); + if (this._tslintConfigFilePath && this._tslintToolPath) { + const tslintLinter: Tslint = await Tslint.initializeAsync({ + tsProgram, + fix, + scopedLogger: taskSession.logger, + linterToolPath: this._tslintToolPath, + linterConfigFilePath: this._tslintConfigFilePath, + buildFolderPath: heftConfiguration.buildFolderPath, + buildMetadataFolderPath: taskSession.tempFolderPath + }); + linters.push(tslintLinter); + } - const typeScriptFilenames: Set = new Set(tsProgram.getRootFileNames()); - await eslint.performLintingAsync({ - tsProgram, - typeScriptFilenames, - changedFiles: changedFiles || new Set(tsProgram.getSourceFiles()) - }); + // Now that we know we have initialized properly, run the linter(s) + await Promise.all(linters.map((linter) => this._runLinterAsync(linter, tsProgram, changedFiles))); } - private async _runTslintAsync( - taskSession: IHeftTaskSession, - heftConfiguration: HeftConfiguration, - tslintToolPath: string, - tslintConfigFilePath: string, + private async _runLinterAsync( + linter: LinterBase, tsProgram: IExtendedProgram, changedFiles?: ReadonlySet | undefined ): Promise { - const tslint: Tslint = new Tslint({ - scopedLogger: taskSession.logger, - tslintPackagePath: tslintToolPath, - linterConfigFilePath: tslintConfigFilePath, - buildFolderPath: heftConfiguration.buildFolderPath, - buildMetadataFolderPath: taskSession.cacheFolderPath - }); - - tslint.printVersionHeader(); + linter.printVersionHeader(); const typeScriptFilenames: Set = new Set(tsProgram.getRootFileNames()); - await tslint.performLintingAsync({ + await linter.performLintingAsync({ tsProgram, typeScriptFilenames, changedFiles: changedFiles || new Set(tsProgram.getSourceFiles()) }); } - - private async _resolveTslintConfigFilePathAsync( - heftConfiguration: HeftConfiguration - ): Promise { - const tslintConfigFilePath: string = `${heftConfiguration.buildFolderPath}/tslint.json`; - const tslintConfigFileExists: boolean = await FileSystem.existsAsync(tslintConfigFilePath); - return tslintConfigFileExists ? tslintConfigFilePath : undefined; - } - - private async _resolveEslintConfigFilePathAsync( - heftConfiguration: HeftConfiguration - ): Promise { - // When project is configured with "type": "module" in package.json, the config file must have a .cjs extension - // so use it if it exists - const defaultPath: string = `${heftConfiguration.buildFolderPath}/${ESLINTRC_JS_FILENAME}`; - const alternativePath: string = `${heftConfiguration.buildFolderPath}/${ESLINTRC_CJS_FILENAME}`; - const [alternativePathExists, defaultPathExists] = await Promise.all([ - FileSystem.existsAsync(alternativePath), - FileSystem.existsAsync(defaultPath) - ]); - - if (alternativePathExists && defaultPathExists) { - throw new Error( - `Project contains both "${ESLINTRC_JS_FILENAME}" and "${ESLINTRC_CJS_FILENAME}". Ensure that only ` + - 'one of these files is present in the project.' - ); - } else if (alternativePathExists) { - return alternativePath; - } else if (defaultPathExists) { - return defaultPath; - } else { - return undefined; - } - } } diff --git a/heft-plugins/heft-lint-plugin/src/LinterBase.ts b/heft-plugins/heft-lint-plugin/src/LinterBase.ts index 84515527ce6..f6c473754c7 100644 --- a/heft-plugins/heft-lint-plugin/src/LinterBase.ts +++ b/heft-plugins/heft-lint-plugin/src/LinterBase.ts @@ -1,10 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { performance } from 'perf_hooks'; -import { createHash, Hash } from 'crypto'; -import { type ITerminal, FileSystem, JsonFile, Path } from '@rushstack/node-core-library'; +import * as path from 'node:path'; +import { performance } from 'node:perf_hooks'; +import { createHash, type Hash } from 'node:crypto'; + +import type * as TTypescript from 'typescript'; + +import { FileSystem, JsonFile, Path } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; import type { IScopedLogger } from '@rushstack/heft'; import type { IExtendedProgram, IExtendedSourceFile } from './internalTypings/TypeScriptInternals'; @@ -16,7 +20,11 @@ export interface ILinterBaseOptions { * The path where the linter state will be written to. */ buildMetadataFolderPath: string; + linterToolPath: string; linterConfigFilePath: string; + tsProgram: IExtendedProgram; + fix?: boolean; + sarifLogPath?: string; } export interface IRunLinterOptions { @@ -45,6 +53,12 @@ interface ILinterCacheData { * each array item is the file's path and the second element is the file's hash. */ fileVersions: [string, string][]; + + /** + * A hash of the list of filenames that were linted. This is used to verify that + * the cache was run with the same files. + */ + filesHash?: string; } export abstract class LinterBase { @@ -53,6 +67,9 @@ export abstract class LinterBase { protected readonly _buildFolderPath: string; protected readonly _buildMetadataFolderPath: string; protected readonly _linterConfigFilePath: string; + protected readonly _fix: boolean; + + protected _fixesPossible: boolean = false; private readonly _linterName: string; @@ -63,6 +80,7 @@ export abstract class LinterBase { this._buildMetadataFolderPath = options.buildMetadataFolderPath; this._linterConfigFilePath = options.linterConfigFilePath; this._linterName = linterName; + this._fix = options.fix || false; } public abstract printVersionHeader(): void; @@ -71,20 +89,44 @@ export abstract class LinterBase { const startTime: number = performance.now(); let fileCount: number = 0; - await this.initializeAsync(options.tsProgram); - const commonDirectory: string = options.tsProgram.getCommonSourceDirectory(); const relativePaths: Map = new Map(); - const fileHash: Hash = createHash('md5'); + // Collect and sort file paths for stable hashing + const relativePathsArray: string[] = []; for (const file of options.typeScriptFilenames) { // Need to use relative paths to ensure portability. const relative: string = Path.convertToSlashes(path.relative(commonDirectory, file)); relativePaths.set(file, relative); - fileHash.update(relative); + relativePathsArray.push(relative); + } + relativePathsArray.sort(); + + // Calculate the hash of the list of filenames for verification purposes + const filesHash: Hash = createHash('md5'); + for (const relative of relativePathsArray) { + filesHash.update(relative); + } + const filesHashString: string = filesHash.digest('base64url'); + + // Calculate the hash suffix based on the project-relative path of the tsconfig file + // Extract the config file path from the program's compiler options + const compilerOptions: TTypescript.CompilerOptions = options.tsProgram.getCompilerOptions(); + const tsconfigFilePath: string | undefined = compilerOptions.configFilePath as string | undefined; + + let hashSuffix: string; + if (tsconfigFilePath) { + const relativeTsconfigPath: string = Path.convertToSlashes( + path.relative(this._buildFolderPath, tsconfigFilePath) + ); + const tsconfigHash: Hash = createHash('md5'); + tsconfigHash.update(relativeTsconfigPath); + hashSuffix = tsconfigHash.digest('base64url').slice(0, 8); + } else { + // Fallback to a default hash if configFilePath is not available + hashSuffix = 'default'; } - const hashSuffix: string = fileHash.digest('base64').replace(/\+/g, '-').replace(/\//g, '_').slice(0, 8); const linterCacheVersion: string = await this.getCacheVersionAsync(); const linterCacheFilePath: string = path.resolve( @@ -94,17 +136,28 @@ export abstract class LinterBase { let linterCacheData: ILinterCacheData | undefined; try { - linterCacheData = await JsonFile.loadAsync(linterCacheFilePath); + const cacheFileContent: string = await FileSystem.readFileAsync(linterCacheFilePath); + if (cacheFileContent) { + // Using JSON.parse instead of JsonFile because it is faster for plain JSON + // This is safe because it is a machine-generated file that will not be edited by a human. + // Also so that we can check for empty file first. + linterCacheData = JSON.parse(cacheFileContent); + } } catch (e) { if (FileSystem.isNotExistError(e as Error)) { linterCacheData = undefined; + } else if (e instanceof SyntaxError) { + this._terminal.writeVerboseLine(`Error parsing ${linterCacheFilePath}: ${e}; ignoring cached data.`); + linterCacheData = undefined; } else { throw e; } } const cachedNoFailureFileVersions: Map = new Map( - linterCacheData?.cacheVersion === linterCacheVersion ? linterCacheData.fileVersions : [] + linterCacheData?.cacheVersion === linterCacheVersion && linterCacheData?.filesHash === filesHashString + ? linterCacheData.fileVersions + : [] ); const newNoFailureFileVersions: Map = new Map(); @@ -113,7 +166,7 @@ export abstract class LinterBase { // Some of this code comes from here: // https://github.com/palantir/tslint/blob/24d29e421828348f616bf761adb3892bcdf51662/src/linter.ts#L161-L179 // Modified to only lint files that have changed and that we care about - const lintFailures: TLintResult[] = []; + const lintResults: TLintResult[] = []; for (const sourceFile of options.tsProgram.getSourceFiles()) { const filePath: string = sourceFile.fileName; const relative: string | undefined = relativePaths.get(filePath); @@ -122,8 +175,7 @@ export abstract class LinterBase { continue; } - // Compute the version from the source file content - const version: string = sourceFile.version || ''; + const version: string = await this.getSourceFileHashAsync(sourceFile); const cachedVersion: string = cachedNoFailureFileVersions.get(relative) || ''; if ( cachedVersion === '' || @@ -132,13 +184,14 @@ export abstract class LinterBase { options.changedFiles.has(sourceFile) ) { fileCount++; - const failures: TLintResult[] = await this.lintFileAsync(sourceFile); - if (failures.length === 0) { + const results: TLintResult[] = await this.lintFileAsync(sourceFile); + // Always forward the results, since they might be suppressed. + for (const result of results) { + lintResults.push(result); + } + + if (!this.hasLintFailures(results)) { newNoFailureFileVersions.set(relative, version); - } else { - for (const failure of failures) { - lintFailures.push(failure); - } } } else { newNoFailureFileVersions.set(relative, version); @@ -146,11 +199,18 @@ export abstract class LinterBase { } //#endregion - this.lintingFinished(lintFailures); + await this.lintingFinishedAsync(lintResults); + + if (!this._fix && this._fixesPossible) { + this._terminal.writeWarningLine( + 'The linter reported that fixes are possible. To apply fixes, run Heft with the "--fix" option.' + ); + } const updatedTslintCacheData: ILinterCacheData = { cacheVersion: linterCacheVersion, - fileVersions: Array.from(newNoFailureFileVersions) + fileVersions: Array.from(newNoFailureFileVersions), + filesHash: filesHashString }; await JsonFile.saveAsync(updatedTslintCacheData, linterCacheFilePath, { ensureFolderExists: true }); @@ -159,13 +219,26 @@ export abstract class LinterBase { this._terminal.writeVerboseLine(`Lint: ${duration}ms (${fileCount} files)`); } - protected abstract getCacheVersionAsync(): Promise; + protected async getSourceFileHashAsync(sourceFile: IExtendedSourceFile): Promise { + // TypeScript only computes the version during an incremental build. + let version: string = sourceFile.version; + if (!version) { + // Compute the version from the source file content + const sourceFileHash: Hash = createHash('sha1'); + sourceFileHash.update(sourceFile.text); + version = sourceFileHash.digest('base64'); + } + + return version; + } - protected abstract initializeAsync(tsProgram: IExtendedProgram): Promise; + protected abstract getCacheVersionAsync(): Promise; protected abstract lintFileAsync(sourceFile: IExtendedSourceFile): Promise; - protected abstract lintingFinished(lintFailures: TLintResult[]): void; + protected abstract lintingFinishedAsync(lintResults: TLintResult[]): Promise; + + protected abstract hasLintFailures(lintResults: TLintResult[]): boolean; protected abstract isFileExcludedAsync(filePath: string): Promise; } diff --git a/heft-plugins/heft-lint-plugin/src/SarifFormatter.ts b/heft-plugins/heft-lint-plugin/src/SarifFormatter.ts new file mode 100644 index 00000000000..1a55bfa2cc8 --- /dev/null +++ b/heft-plugins/heft-lint-plugin/src/SarifFormatter.ts @@ -0,0 +1,368 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; + +import type * as TEslint from 'eslint'; +import type * as TEslintLegacy from 'eslint-8'; + +import { Path, Text } from '@rushstack/node-core-library'; + +export interface ISerifFormatterOptions { + ignoreSuppressed: boolean; + eslintVersion?: string; + buildFolderPath: string; +} + +export interface ISarifRun { + tool: { + driver: { + name: string; + informationUri: string; + version?: string; + rules: IStaticAnalysisRules[]; + }; + }; + artifacts?: ISarifFile[]; + results?: ISarifRepresentation[]; + invocations?: { + toolConfigurationNotifications: ISarifRepresentation[]; + executionSuccessful: boolean; + }[]; +} + +export interface ISarifRepresentation { + level: string; + message: { + text: string; + }; + locations: ISarifLocation[]; + ruleId?: string; + ruleIndex?: number; + descriptor?: { + id: string; + }; + suppressions?: ISuppressedAnalysis[]; +} + +// Interface for the SARIF log structure +export interface ISarifLog { + version: string; + $schema: string; + runs: ISarifRun[]; +} + +export interface IRegion { + startLine?: number; + startColumn?: number; + endLine?: number; + endColumn?: number; + snippet?: { + text: string; + }; +} + +export interface IStaticAnalysisRules { + id: string; + name?: string; + shortDescription?: { + text: string; + }; + fullDescription?: { + text: string; + }; + defaultConfiguration?: { + level: 'note' | 'warning' | 'error'; + }; + helpUri?: string; + properties?: { + category?: string; + precision?: 'very-high' | 'high' | 'medium' | 'low'; + tags?: string[]; + problem?: { + severity?: 'recommendation' | 'warning' | 'error'; + securitySeverity?: number; + }; + }; +} + +export interface ISarifFile { + location: { + uri: string; + }; +} + +export interface ISuppressedAnalysis { + kind: string; + justification: string; +} + +export interface ISarifLocation { + physicalLocation: ISarifPhysicalLocation; +} + +export interface ISarifArtifactLocation { + uri: string; + index?: number; +} + +export interface ISarifPhysicalLocation { + artifactLocation: ISarifArtifactLocation; + region?: IRegion; +} + +export interface ISarifRule { + id: string; + helpUri?: string; + shortDescription?: { + text: string; + }; + properties?: { + category?: string; + }; +} + +type IExtendedLintMessage = (TEslint.Linter.LintMessage | TEslintLegacy.Linter.LintMessage) & { + suppressions?: ISuppressedAnalysis[]; +}; + +const INTERNAL_ERROR_ID: 'ESL0999' = 'ESL0999'; +const SARIF_VERSION: '2.1.0' = '2.1.0'; +const SARIF_INFORMATION_URI: 'http://json.schemastore.org/sarif-2.1.0-rtm.5' = + 'http://json.schemastore.org/sarif-2.1.0-rtm.5'; +/** + * Converts ESLint results into a SARIF (Static Analysis Results Interchange Format) log. + * + * This function takes in a list of ESLint lint results, processes them to extract + * relevant information such as errors, warnings, and suppressed messages, and + * outputs a SARIF log which conforms to the SARIF v2.1.0 specification. + * + * @param results - An array of lint results from ESLint that contains linting information, + * such as file paths, messages, and suppression details. + * @param rulesMeta - An object containing metadata about the ESLint rules that were applied during the linting session. + * The keys are the rule names, and the values are rule metadata objects + * that describe each rule. This metadata typically includes: + * - `docs`: Documentation about the rule. + * - `fixable`: Indicates whether the rule is fixable. + * - `messages`: Custom messages that the rule might output when triggered. + * - `schema`: The configuration schema for the rule. + * This metadata helps in providing more context about the rules when generating the SARIF log. + * @param options - An object containing options for formatting: + * - `ignoreSuppressed`: Boolean flag to decide whether to ignore suppressed messages. + * - `eslintVersion`: Optional string to include the version of ESLint in the SARIF log. + * @returns The SARIF log containing information about the linting results in SARIF format. + */ + +export function formatEslintResultsAsSARIF( + results: (TEslint.ESLint.LintResult | TEslintLegacy.ESLint.LintResult)[], + rulesMeta: (TEslint.ESLint.LintResultData | TEslintLegacy.ESLint.LintResultData)['rulesMeta'], + options: ISerifFormatterOptions +): ISarifLog { + const { ignoreSuppressed, eslintVersion, buildFolderPath } = options; + const toolConfigurationNotifications: ISarifRepresentation[] = []; + const sarifFiles: ISarifFile[] = []; + const sarifResults: ISarifRepresentation[] = []; + const sarifArtifactIndices: Map = new Map(); + const sarifRules: ISarifRule[] = []; + const sarifRuleIndices: Map = new Map(); + + const sarifRun: ISarifRun = { + tool: { + driver: { + name: 'ESLint', + informationUri: 'https://eslint.org', + version: eslintVersion, + rules: [] + } + } + }; + + const sarifLog: ISarifLog = { + version: SARIF_VERSION, + $schema: SARIF_INFORMATION_URI, + runs: [sarifRun] + }; + + let executionSuccessful: boolean = true; + let currentArtifactIndex: number = 0; + let currentRuleIndex: number = 0; + + for (const result of results) { + const { filePath, source } = result; + const fileUrl: string = Path.convertToSlashes(path.relative(buildFolderPath, filePath)); + let sarifFileIndex: number | undefined = sarifArtifactIndices.get(fileUrl); + + if (sarifFileIndex === undefined) { + sarifFileIndex = currentArtifactIndex++; + sarifArtifactIndices.set(fileUrl, sarifFileIndex); + sarifFiles.push({ + location: { + uri: fileUrl + } + }); + } + + const artifactLocation: ISarifArtifactLocation = { + uri: fileUrl, + index: sarifFileIndex + }; + + const containsSuppressedMessages: boolean = + result.suppressedMessages && result.suppressedMessages.length > 0; + const messages: IExtendedLintMessage[] = + containsSuppressedMessages && !ignoreSuppressed + ? [...result.messages, ...result.suppressedMessages] + : result.messages; + + const sourceLines: string[] | undefined = Text.splitByNewLines(source); + + for (const message of messages) { + const level: string = message.fatal || message.severity === 2 ? 'error' : 'warning'; + const physicalLocation: ISarifPhysicalLocation = { + artifactLocation + }; + + const sarifRepresentation: ISarifRepresentation = { + level, + message: { + text: message.message + }, + locations: [ + { + physicalLocation + } + ] + }; + + if (message.ruleId) { + sarifRepresentation.ruleId = message.ruleId; + + if (rulesMeta && sarifRuleIndices.get(message.ruleId) === undefined) { + const meta: TEslint.Rule.RuleMetaData | TEslintLegacy.Rule.RuleMetaData = rulesMeta[message.ruleId]; + + // An unknown ruleId will return null. This check prevents unit test failure. + if (meta) { + sarifRuleIndices.set(message.ruleId, currentRuleIndex++); + + if (meta.docs) { + // Create a new entry in the rules dictionary. + const shortDescription: string = meta.docs.description ?? ''; + + const sarifRule: ISarifRule = { + id: message.ruleId, + helpUri: meta.docs.url, + properties: { + category: meta.docs.category + }, + shortDescription: { + text: shortDescription + } + }; + sarifRules.push(sarifRule); + // Some rulesMetas do not have docs property + } else { + sarifRules.push({ + id: message.ruleId, + properties: { + category: 'No category provided' + }, + shortDescription: { + text: 'Please see details in message' + } + }); + } + } + } + + if (sarifRuleIndices.has(message.ruleId)) { + sarifRepresentation.ruleIndex = sarifRuleIndices.get(message.ruleId); + } + + if (containsSuppressedMessages && !ignoreSuppressed) { + sarifRepresentation.suppressions = message.suppressions + ? message.suppressions.map((suppression: ISuppressedAnalysis) => { + return { + kind: suppression.kind === 'directive' ? 'inSource' : 'external', + justification: suppression.justification + }; + }) + : []; + } + } else { + sarifRepresentation.descriptor = { + id: INTERNAL_ERROR_ID + }; + + if (sarifRepresentation.level === 'error') { + executionSuccessful = false; + } + } + + if (message.line !== undefined || message.column !== undefined) { + const { line: startLine, column: startColumn, endLine, endColumn } = message; + const region: IRegion = { + startLine, + startColumn, + endLine, + endColumn + }; + physicalLocation.region = region; + } + + if (sourceLines) { + // Build the snippet from the source lines + const startLine: number = message.line - 1; + const endLine: number = message.endLine !== undefined ? message.endLine - 1 : startLine; + const startLineColumn: number = message.column - 1; + const endLineColumn: number | undefined = + message.endColumn !== undefined ? message.endColumn - 1 : undefined; + const snippetLines: string[] = sourceLines.slice(startLine, endLine + 1); + const snippetText: string = snippetLines + .map((line, index) => { + let startColumn: number = 0; + let endColumn: number | undefined = undefined; + if (index === 0) { + startColumn = startLineColumn; + } + if (index === snippetLines.length - 1 && endLineColumn !== undefined) { + endColumn = endLineColumn; + } + return line.slice(startColumn, endColumn); + }) + .join('\n'); + + physicalLocation.region ??= {}; + physicalLocation.region.snippet = { + text: snippetText + }; + } + + if (message.ruleId) { + sarifResults.push(sarifRepresentation); + } else { + toolConfigurationNotifications.push(sarifRepresentation); + } + } + } + + if (sarifRules.length > 0) { + sarifRun.tool.driver.rules = sarifRules; + } + + if (sarifFiles.length > 0) { + sarifRun.artifacts = sarifFiles; + } + + sarifRun.results = sarifResults; + + if (toolConfigurationNotifications.length > 0) { + sarifRun.invocations = [ + { + toolConfigurationNotifications, + executionSuccessful + } + ]; + } + + return sarifLog; +} diff --git a/heft-plugins/heft-lint-plugin/src/Tslint.ts b/heft-plugins/heft-lint-plugin/src/Tslint.ts index b3dfb97f7f4..52a15677ad7 100644 --- a/heft-plugins/heft-lint-plugin/src/Tslint.ts +++ b/heft-plugins/heft-lint-plugin/src/Tslint.ts @@ -1,29 +1,81 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as crypto from 'crypto'; +import * as path from 'node:path'; +import * as crypto from 'node:crypto'; + import type * as TTslint from 'tslint'; import type * as TTypescript from 'typescript'; -import { Import, JsonFile, FileError, FileSystem, type ITerminal } from '@rushstack/node-core-library'; + +import { Import, JsonFile, FileError, FileSystem } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; +import type { HeftConfiguration } from '@rushstack/heft'; import { LinterBase, type ILinterBaseOptions } from './LinterBase'; import type { IExtendedLinter } from './internalTypings/TslintInternals'; interface ITslintOptions extends ILinterBaseOptions { - tslintPackagePath: string; + tslintPackage: typeof TTslint; + tslintConfiguration: TTslint.Configuration.IConfigurationFile; +} + +function getFormattedErrorMessage(tslintFailure: TTslint.RuleFailure): string { + return `(${tslintFailure.getRuleName()}) ${tslintFailure.getFailure()}`; } +const TSLINT_CONFIG_FILE_NAME: string = 'tslint.json'; + export class Tslint extends LinterBase { - private _tslint: typeof TTslint; - private _tslintConfiguration!: TTslint.Configuration.IConfigurationFile; - private _linter!: IExtendedLinter; - private _enabledRules!: TTslint.IRule[]; - private _ruleSeverityMap!: Map; + private readonly _tslintPackage: typeof TTslint; + private readonly _tslintConfiguration: TTslint.Configuration.IConfigurationFile; + private readonly _linter: IExtendedLinter; + private readonly _enabledRules: TTslint.IRule[]; + private readonly _ruleSeverityMap: Map; public constructor(options: ITslintOptions) { super('tslint', options); - this._tslint = require(options.tslintPackagePath); + + const { tslintPackage, tsProgram } = options; + this._tslintPackage = tslintPackage; + this._tslintConfiguration = options.tslintConfiguration; + this._linter = new tslintPackage.Linter( + { + // This is not handled by the linter in the way that we use it, so we will manually apply + // fixes later + fix: false, + rulesDirectory: this._tslintConfiguration.rulesDirectory + }, + tsProgram + ) as unknown as IExtendedLinter; + + this._enabledRules = this._linter.getEnabledRules(this._tslintConfiguration, false); + + this._ruleSeverityMap = new Map( + this._enabledRules.map((rule): [string, TTslint.RuleSeverity] => [ + rule.getOptions().ruleName, + rule.getOptions().ruleSeverity + ]) + ); + } + + public static async initializeAsync(options: ILinterBaseOptions): Promise { + const { linterToolPath, linterConfigFilePath } = options; + const tslintPackage: typeof TTslint = await import(linterToolPath); + const tslintConfiguration: TTslint.Configuration.IConfigurationFile = + tslintPackage.Configuration.loadConfigurationFromPath(linterConfigFilePath); + return new Tslint({ + ...options, + tslintPackage, + tslintConfiguration + }); + } + + public static async resolveTslintConfigFilePathAsync( + heftConfiguration: HeftConfiguration + ): Promise { + const tslintConfigFilePath: string = `${heftConfiguration.buildFolderPath}/${TSLINT_CONFIG_FILE_NAME}`; + const tslintConfigFileExists: boolean = await FileSystem.existsAsync(tslintConfigFilePath); + return tslintConfigFileExists ? tslintConfigFilePath : undefined; } /** @@ -79,7 +131,7 @@ export class Tslint extends LinterBase { } public printVersionHeader(): void { - this._terminal.writeLine(`Using TSLint version ${this._tslint.Linter.VERSION}`); + this._terminal.writeLine(`Using TSLint version ${this._tslintPackage.Linter.VERSION}`); } protected async getCacheVersionAsync(): Promise { @@ -87,38 +139,26 @@ export class Tslint extends LinterBase { this._linterConfigFilePath, this._terminal ); - const tslintConfigVersion: string = `${this._tslint.Linter.VERSION}_${tslintConfigHash.digest('hex')}`; + const tslintConfigVersion: string = `${this._tslintPackage.Linter.VERSION}_${tslintConfigHash.digest( + 'hex' + )}`; return tslintConfigVersion; } - protected async initializeAsync(tsProgram: TTypescript.Program): Promise { - this._tslintConfiguration = this._tslint.Configuration.loadConfigurationFromPath( - this._linterConfigFilePath - ); - this._linter = new this._tslint.Linter( - { - fix: false, - rulesDirectory: this._tslintConfiguration.rulesDirectory - }, - tsProgram - ) as unknown as IExtendedLinter; - - this._enabledRules = this._linter.getEnabledRules(this._tslintConfiguration, false); - - this._ruleSeverityMap = new Map( - this._enabledRules.map((rule): [string, TTslint.RuleSeverity] => [ - rule.getOptions().ruleName, - rule.getOptions().ruleSeverity - ]) - ); - } - protected async lintFileAsync(sourceFile: TTypescript.SourceFile): Promise { // Some of this code comes from here: // https://github.com/palantir/tslint/blob/24d29e421828348f616bf761adb3892bcdf51662/src/linter.ts#L161-L179 // Modified to only lint files that have changed and that we care about - const failures: TTslint.RuleFailure[] = this._linter.getAllFailures(sourceFile, this._enabledRules); + let failures: TTslint.RuleFailure[] = this._linter.getAllFailures(sourceFile, this._enabledRules); + const hasFixableIssue: boolean = failures.some((f) => f.hasFix()); + if (hasFixableIssue) { + if (this._fix) { + failures = this._linter.applyAllFixes(this._enabledRules, failures, sourceFile, sourceFile.fileName); + } else { + this._fixesPossible = true; + } + } for (const failure of failures) { const severity: TTslint.RuleSeverity | undefined = this._ruleSeverityMap.get(failure.getRuleName()); @@ -132,37 +172,55 @@ export class Tslint extends LinterBase { return failures; } - protected lintingFinished(failures: TTslint.RuleFailure[]): void { + protected async lintingFinishedAsync(failures: TTslint.RuleFailure[]): Promise { this._linter.failures = failures; const lintResult: TTslint.LintResult = this._linter.getResult(); + // Report linter fixes to the logger. These will only be returned when the underlying failure was fixed + if (lintResult.fixes?.length) { + for (const fixedTslintFailure of lintResult.fixes) { + const formattedMessage: string = `[FIXED] ${getFormattedErrorMessage(fixedTslintFailure)}`; + const errorObject: FileError = this._getLintFileError(fixedTslintFailure, formattedMessage); + this._scopedLogger.emitWarning(errorObject); + } + } + // Report linter errors and warnings to the logger - if (lintResult.failures.length) { - for (const tslintFailure of lintResult.failures) { - const { line, character } = tslintFailure.getStartPosition().getLineAndCharacter(); - const formattedFailure: string = `(${tslintFailure.getRuleName()}) ${tslintFailure.getFailure()}`; - const errorObject: FileError = new FileError(formattedFailure, { - absolutePath: tslintFailure.getFileName(), - projectFolder: this._buildFolderPath, - line: line + 1, - column: character + 1 - }); - switch (tslintFailure.getRuleSeverity()) { - case 'error': { - this._scopedLogger.emitError(errorObject); - break; - } - - case 'warning': { - this._scopedLogger.emitWarning(errorObject); - break; - } + for (const tslintFailure of lintResult.failures) { + const errorObject: FileError = this._getLintFileError(tslintFailure); + switch (tslintFailure.getRuleSeverity()) { + case 'error': { + this._scopedLogger.emitError(errorObject); + break; + } + + case 'warning': { + this._scopedLogger.emitWarning(errorObject); + break; } } } } protected async isFileExcludedAsync(filePath: string): Promise { - return this._tslint.Configuration.isFileExcluded(filePath, this._tslintConfiguration); + return this._tslintPackage.Configuration.isFileExcluded(filePath, this._tslintConfiguration); + } + + protected hasLintFailures(lintResults: TTslint.RuleFailure[]): boolean { + return lintResults.length > 0; + } + + private _getLintFileError(tslintFailure: TTslint.RuleFailure, message?: string): FileError { + if (!message) { + message = getFormattedErrorMessage(tslintFailure); + } + + const { line, character } = tslintFailure.getStartPosition().getLineAndCharacter(); + return new FileError(message, { + absolutePath: tslintFailure.getFileName(), + projectFolder: this._buildFolderPath, + line: line + 1, + column: character + 1 + }); } } diff --git a/heft-plugins/heft-lint-plugin/src/internalTypings/TslintInternals.ts b/heft-plugins/heft-lint-plugin/src/internalTypings/TslintInternals.ts index e99f518f605..a886be74f73 100644 --- a/heft-plugins/heft-lint-plugin/src/internalTypings/TslintInternals.ts +++ b/heft-plugins/heft-lint-plugin/src/internalTypings/TslintInternals.ts @@ -4,7 +4,10 @@ import type * as TTslint from 'tslint'; import type * as TTypescript from 'typescript'; -type TrimmedLinter = Omit; +type TrimmedLinter = Omit< + TTslint.Linter, + 'getAllFailures' | 'applyAllFixes' | 'getEnabledRules' | 'failures' +>; export interface IExtendedLinter extends TrimmedLinter { /** * https://github.com/palantir/tslint/blob/24d29e421828348f616bf761adb3892bcdf51662/src/linter.ts#L117 @@ -20,4 +23,14 @@ export interface IExtendedLinter extends TrimmedLinter { * https://github.com/palantir/tslint/blob/24d29e421828348f616bf761adb3892bcdf51662/src/linter.ts#L303-L306 */ getEnabledRules(configuration: TTslint.Configuration.IConfigurationFile, isJs: boolean): TTslint.IRule[]; + + /** + * https://github.com/palantir/tslint/blob/24d29e421828348f616bf761adb3892bcdf51662/src/linter.ts#L212-L241 + */ + applyAllFixes( + enabledRules: TTslint.IRule[], + fileFailures: TTslint.RuleFailure[], + sourceFile: TTypescript.SourceFile, + sourceFileName: string + ): TTslint.RuleFailure[]; } diff --git a/heft-plugins/heft-lint-plugin/src/schemas/heft-lint-plugin.schema.json b/heft-plugins/heft-lint-plugin/src/schemas/heft-lint-plugin.schema.json new file mode 100644 index 00000000000..072d2c70df6 --- /dev/null +++ b/heft-plugins/heft-lint-plugin/src/schemas/heft-lint-plugin.schema.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Heft Lint Plugin Options Configuration", + "description": "This schema describes the \"options\" field that can be specified in heft.json when loading \"@rushstack/heft-lint-plugin\".", + "type": "object", + + "additionalProperties": false, + + "properties": { + "alwaysFix": { + "title": "Always Fix", + "description": "If set to true, fix all encountered rule violations where the violated rule provides a fixer, regardless of if the \"--fix\" command-line argument is provided. When running in production mode, fixes will be disabled regardless of this setting.", + "type": "boolean" + }, + + "sarifLogPath": { + "title": "SARIF Log Path", + "description": "If specified and using ESLint, a log describing the lint configuration and all messages (suppressed or not) will be emitted in the Static Analysis Results Interchange Format (https://sarifweb.azurewebsites.net/) at the provided path, relative to the project root.", + "type": "string" + } + } +} diff --git a/heft-plugins/heft-lint-plugin/src/test/SarifFormatter.test.ts b/heft-plugins/heft-lint-plugin/src/test/SarifFormatter.test.ts new file mode 100644 index 00000000000..abae66cd8cd --- /dev/null +++ b/heft-plugins/heft-lint-plugin/src/test/SarifFormatter.test.ts @@ -0,0 +1,649 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { formatEslintResultsAsSARIF } from '../SarifFormatter'; +import type { ISerifFormatterOptions } from '../SarifFormatter'; +import type { ESLint } from 'eslint'; + +describe('formatEslintResultsAsSARIF', () => { + test('should correctly format ESLint results into SARIF log', () => { + const mockLintResults: ESLint.LintResult[] = [ + { + filePath: '/src/file1.ts', + source: 'const x = 1;', + messages: [ + { + ruleId: 'no-unused-vars', + severity: 2, + message: "'x' is defined but never used.", + line: 1, + column: 7, + nodeType: 'Identifier', + endLine: 1, + endColumn: 8 + } + ], + suppressedMessages: [], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + usedDeprecatedRules: [], + fatalErrorCount: 0 + } + ]; + + const mockRulesMeta: ESLint.LintResultData['rulesMeta'] = { + 'no-unused-vars': { + type: 'suggestion', + docs: { + description: "'x' is defined but never used.", + recommended: false, + url: 'https://eslint.org/docs/latest/rules/no-unused-vars' + }, + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { + type: 'string' + }, + minItems: 1, + uniqueItems: true + } + }, + additionalProperties: false + } + ], + hasSuggestions: true, + messages: { + unexpected: "'x' is defined but never used." + } + } + }; + + const options: ISerifFormatterOptions = { + ignoreSuppressed: false, + eslintVersion: '7.32.0', + buildFolderPath: '/' + }; + + const sarifLog = formatEslintResultsAsSARIF(mockLintResults, mockRulesMeta, options); + + expect(sarifLog).toMatchSnapshot(); + }); + + test('case with no files', () => { + const mockLintResults: ESLint.LintResult[] = []; + + const mockRulesMeta: ESLint.LintResultData['rulesMeta'] = {}; + + const options: ISerifFormatterOptions = { + ignoreSuppressed: false, + eslintVersion: '7.32.0', + buildFolderPath: '/' + }; + + const sarifLog = formatEslintResultsAsSARIF(mockLintResults, mockRulesMeta, options); + + expect(sarifLog).toMatchSnapshot(); + }); + + test('case with single issues in the same file', () => { + const mockLintResults: ESLint.LintResult[] = [ + { + filePath: '/src/file1.ts', + source: 'const x = 1;', + messages: [ + { + ruleId: 'no-unused-vars', + severity: 2, + message: "'x' is defined but never used.", + line: 1, + column: 7, + nodeType: 'Identifier', + endLine: 1, + endColumn: 8 + } + ], + suppressedMessages: [], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + usedDeprecatedRules: [], + fatalErrorCount: 0 + } + ]; + + const mockRulesMeta: ESLint.LintResultData['rulesMeta'] = { + 'no-unused-vars': { + type: 'suggestion', + docs: { + description: "'x' is defined but never used.", + recommended: false, + url: 'https://eslint.org/docs/latest/rules/no-unused-vars' + }, + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { + type: 'string' + }, + minItems: 1, + uniqueItems: true + } + }, + additionalProperties: false + } + ], + hasSuggestions: true, + messages: { + unexpected: "'x' is defined but never used." + } + } + }; + + const options: ISerifFormatterOptions = { + ignoreSuppressed: false, + eslintVersion: '7.32.0', + buildFolderPath: '/' + }; + + const sarifLog = formatEslintResultsAsSARIF(mockLintResults, mockRulesMeta, options); + + expect(sarifLog).toMatchSnapshot(); + }); + + test('should handle multiple issues in the same file', async () => { + const mockLintResults: ESLint.LintResult[] = [ + { + filePath: '/src/file2.ts', + source: 'let x;\nconsole.log("test");', + messages: [ + { + ruleId: 'no-unused-vars', + severity: 2, + message: "'x' is defined but never used.", + line: 1, + column: 5, + nodeType: 'Identifier', + endLine: 1, + endColumn: 6 + }, + { + ruleId: 'no-console', + severity: 1, + message: 'Unexpected console statement.', + line: 2, + column: 1, + nodeType: 'MemberExpression', + endLine: 2, + endColumn: 12 + } + ], + suppressedMessages: [], + errorCount: 1, + warningCount: 1, + fixableErrorCount: 0, + fixableWarningCount: 0, + usedDeprecatedRules: [], + fatalErrorCount: 0 + } + ]; + + const mockRulesMeta: ESLint.LintResultData['rulesMeta'] = { + 'no-console': { + type: 'suggestion', + docs: { + description: 'Disallow the use of `console`', + recommended: false, + url: 'https://eslint.org/docs/latest/rules/no-console' + }, + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { + type: 'string' + }, + minItems: 1, + uniqueItems: true + } + }, + additionalProperties: false + } + ], + hasSuggestions: true, + messages: { + unexpected: 'Unexpected console statement.', + removeConsole: 'Remove the console.{{ propertyName }}().' + } + }, + 'no-unused-vars': { + type: 'suggestion', + docs: { + description: "'x' is defined but never used.", + recommended: false, + url: 'https://eslint.org/docs/latest/rules/no-unused-vars' + }, + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { + type: 'string' + }, + minItems: 1, + uniqueItems: true + } + }, + additionalProperties: false + } + ], + hasSuggestions: true, + messages: { + unexpected: "'x' is defined but never used." + } + } + }; + + const options: ISerifFormatterOptions = { + ignoreSuppressed: false, + eslintVersion: '7.32.0', + buildFolderPath: '/' + }; + + const sarifLog = await formatEslintResultsAsSARIF(mockLintResults, mockRulesMeta, options); + + expect(sarifLog).toMatchSnapshot(); + }); + + test('should handle a file with no messages', async () => { + const mockLintResults: ESLint.LintResult[] = [ + { + filePath: '/src/file3.ts', + messages: [], + suppressedMessages: [], + errorCount: 0, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + usedDeprecatedRules: [], + fatalErrorCount: 0 + } + ]; + + const mockRulesMeta: ESLint.LintResultData['rulesMeta'] = {}; + + const options: ISerifFormatterOptions = { + ignoreSuppressed: false, + eslintVersion: '7.32.0', + buildFolderPath: '/' + }; + + const sarifLog = await formatEslintResultsAsSARIF(mockLintResults, mockRulesMeta, options); + + expect(sarifLog).toMatchSnapshot(); + }); + + test('should handle multiple files', async () => { + const mockLintResults: ESLint.LintResult[] = [ + { + filePath: '/src/file1.ts', + source: 'const x = 1;', + messages: [ + { + ruleId: 'no-unused-vars', + severity: 2, + message: "'x' is defined but never used.", + line: 1, + column: 7, + nodeType: 'Identifier', + endLine: 1, + endColumn: 8 + } + ], + suppressedMessages: [], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + usedDeprecatedRules: [], + fatalErrorCount: 0 + }, + { + filePath: '/src/file2.ts', + source: 'let y = z == 2;', + messages: [ + { + ruleId: 'eqeqeq', + severity: 2, + message: "Expected '===' and instead saw '=='.", + line: 1, + column: 9, + nodeType: 'BinaryExpression', + endLine: 1, + endColumn: 15 + } + ], + suppressedMessages: [], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + usedDeprecatedRules: [], + fatalErrorCount: 0 + } + ]; + + const mockRulesMeta: ESLint.LintResultData['rulesMeta'] = { + 'no-console': { + type: 'suggestion', + docs: { + description: 'Disallow the use of `console`', + recommended: false, + url: 'https://eslint.org/docs/latest/rules/no-console' + }, + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { + type: 'string' + }, + minItems: 1, + uniqueItems: true + } + }, + additionalProperties: false + } + ], + hasSuggestions: true, + messages: { + unexpected: 'Unexpected console statement.', + removeConsole: 'Remove the console.{{ propertyName }}().' + } + }, + eqeqeq: { + type: 'problem', + docs: { + description: 'Require the use of === and !==', + recommended: false, + url: 'https://eslint.org/docs/latest/rules/eqeqeq' + }, + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { + type: 'string' + }, + minItems: 1, + uniqueItems: true + } + }, + additionalProperties: false + } + ], + hasSuggestions: true, + messages: { + unexpected: "Expected '===' and instead saw '=='." + } + } + }; + + const options: ISerifFormatterOptions = { + ignoreSuppressed: false, + eslintVersion: '7.32.0', + buildFolderPath: '/' + }; + + const sarifLog = await formatEslintResultsAsSARIF(mockLintResults, mockRulesMeta, options); + + expect(sarifLog).toMatchSnapshot(); + }); + + test('should handle ignoreSuppressed: true with suppressed messages', async () => { + const mockLintResults: ESLint.LintResult[] = [ + { + filePath: '/src/file4.ts', + source: 'debugger;\nconsole.log("test");', + messages: [ + { + ruleId: 'no-debugger', + severity: 2, + message: "Unexpected 'debugger' statement.", + line: 1, + column: 1, + nodeType: 'DebuggerStatement', + endLine: 1, + endColumn: 10 + } + ], + suppressedMessages: [ + { + ruleId: 'no-console', + severity: 1, + message: 'Unexpected console statement.', + line: 2, + column: 1, + nodeType: 'MemberExpression', + endLine: 2, + endColumn: 12, + suppressions: [ + { + kind: 'inSource', + justification: 'rejected' + } + ] + } + ], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + usedDeprecatedRules: [], + fatalErrorCount: 0 + } + ]; + + const mockRulesMeta: ESLint.LintResultData['rulesMeta'] = { + 'no-console': { + type: 'suggestion', + docs: { + description: 'Disallow the use of `console`', + recommended: false, + url: 'https://eslint.org/docs/latest/rules/no-console' + }, + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { + type: 'string' + }, + minItems: 1, + uniqueItems: true + } + }, + additionalProperties: false + } + ], + hasSuggestions: true, + messages: { + unexpected: 'Unexpected console statement.', + removeConsole: 'Remove the console.{{ propertyName }}().' + } + }, + 'no-debugger': { + type: 'suggestion', + docs: { + description: 'Disallow the use of debugger', + recommended: false, + url: 'https://eslint.org/docs/latest/rules/no-debugger' + }, + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { + type: 'string' + }, + minItems: 1, + uniqueItems: true + } + }, + additionalProperties: false + } + ], + hasSuggestions: true, + messages: { + unexpected: "Unexpected 'debugger' statement." + } + } + }; + + const options: ISerifFormatterOptions = { + ignoreSuppressed: true, + eslintVersion: '7.32.0', + buildFolderPath: '/' + }; + + const sarifLog = await formatEslintResultsAsSARIF(mockLintResults, mockRulesMeta, options); + + expect(sarifLog).toMatchSnapshot(); + }); + + test('should handle ignoreSuppressed: false with suppressed messages', async () => { + const mockLintResults: ESLint.LintResult[] = [ + { + filePath: '/src/file4.ts', + source: 'debugger;\nconsole.log("test");', + messages: [ + { + ruleId: 'no-debugger', + severity: 2, + message: "Unexpected 'debugger' statement.", + line: 1, + column: 1, + nodeType: 'DebuggerStatement', + endLine: 1, + endColumn: 10 + } + ], + suppressedMessages: [ + { + ruleId: 'no-console', + severity: 1, + message: 'Unexpected console statement.', + line: 2, + column: 1, + nodeType: 'MemberExpression', + endLine: 2, + endColumn: 12, + suppressions: [ + { + kind: 'inSource', + justification: 'rejected' + } + ] + } + ], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + usedDeprecatedRules: [], + fatalErrorCount: 0 + } + ]; + + const mockRulesMeta: ESLint.LintResultData['rulesMeta'] = { + 'no-console': { + type: 'suggestion', + docs: { + description: 'Disallow the use of `console`', + recommended: false, + url: 'https://eslint.org/docs/latest/rules/no-console' + }, + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { + type: 'string' + }, + minItems: 1, + uniqueItems: true + } + }, + additionalProperties: false + } + ], + hasSuggestions: true, + messages: { + unexpected: 'Unexpected console statement.', + removeConsole: 'Remove the console.{{ propertyName }}().' + } + }, + 'no-debugger': { + type: 'suggestion', + docs: { + description: 'Disallow the use of debugger', + recommended: false, + url: 'https://eslint.org/docs/latest/rules/no-debugger' + }, + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { + type: 'string' + }, + minItems: 1, + uniqueItems: true + } + }, + additionalProperties: false + } + ], + hasSuggestions: true, + messages: { + unexpected: "Unexpected 'debugger' statement." + } + } + }; + + const options: ISerifFormatterOptions = { + ignoreSuppressed: false, + eslintVersion: '7.32.0', + buildFolderPath: '/' + }; + + const sarifLog = await formatEslintResultsAsSARIF(mockLintResults, mockRulesMeta, options); + + expect(sarifLog).toMatchSnapshot(); + }); +}); diff --git a/heft-plugins/heft-lint-plugin/src/test/__snapshots__/SarifFormatter.test.ts.snap b/heft-plugins/heft-lint-plugin/src/test/__snapshots__/SarifFormatter.test.ts.snap new file mode 100644 index 00000000000..684817c2038 --- /dev/null +++ b/heft-plugins/heft-lint-plugin/src/test/__snapshots__/SarifFormatter.test.ts.snap @@ -0,0 +1,556 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`formatEslintResultsAsSARIF case with no files 1`] = ` +Object { + "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", + "runs": Array [ + Object { + "results": Array [], + "tool": Object { + "driver": Object { + "informationUri": "https://eslint.org", + "name": "ESLint", + "rules": Array [], + "version": "7.32.0", + }, + }, + }, + ], + "version": "2.1.0", +} +`; + +exports[`formatEslintResultsAsSARIF case with single issues in the same file 1`] = ` +Object { + "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", + "runs": Array [ + Object { + "artifacts": Array [ + Object { + "location": Object { + "uri": "src/file1.ts", + }, + }, + ], + "results": Array [ + Object { + "level": "error", + "locations": Array [ + Object { + "physicalLocation": Object { + "artifactLocation": Object { + "index": 0, + "uri": "src/file1.ts", + }, + "region": Object { + "endColumn": 8, + "endLine": 1, + "snippet": Object { + "text": "x", + }, + "startColumn": 7, + "startLine": 1, + }, + }, + }, + ], + "message": Object { + "text": "'x' is defined but never used.", + }, + "ruleId": "no-unused-vars", + "ruleIndex": 0, + }, + ], + "tool": Object { + "driver": Object { + "informationUri": "https://eslint.org", + "name": "ESLint", + "rules": Array [ + Object { + "helpUri": "https://eslint.org/docs/latest/rules/no-unused-vars", + "id": "no-unused-vars", + "properties": Object { + "category": undefined, + }, + "shortDescription": Object { + "text": "'x' is defined but never used.", + }, + }, + ], + "version": "7.32.0", + }, + }, + }, + ], + "version": "2.1.0", +} +`; + +exports[`formatEslintResultsAsSARIF should correctly format ESLint results into SARIF log 1`] = ` +Object { + "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", + "runs": Array [ + Object { + "artifacts": Array [ + Object { + "location": Object { + "uri": "src/file1.ts", + }, + }, + ], + "results": Array [ + Object { + "level": "error", + "locations": Array [ + Object { + "physicalLocation": Object { + "artifactLocation": Object { + "index": 0, + "uri": "src/file1.ts", + }, + "region": Object { + "endColumn": 8, + "endLine": 1, + "snippet": Object { + "text": "x", + }, + "startColumn": 7, + "startLine": 1, + }, + }, + }, + ], + "message": Object { + "text": "'x' is defined but never used.", + }, + "ruleId": "no-unused-vars", + "ruleIndex": 0, + }, + ], + "tool": Object { + "driver": Object { + "informationUri": "https://eslint.org", + "name": "ESLint", + "rules": Array [ + Object { + "helpUri": "https://eslint.org/docs/latest/rules/no-unused-vars", + "id": "no-unused-vars", + "properties": Object { + "category": undefined, + }, + "shortDescription": Object { + "text": "'x' is defined but never used.", + }, + }, + ], + "version": "7.32.0", + }, + }, + }, + ], + "version": "2.1.0", +} +`; + +exports[`formatEslintResultsAsSARIF should handle a file with no messages 1`] = ` +Object { + "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", + "runs": Array [ + Object { + "artifacts": Array [ + Object { + "location": Object { + "uri": "src/file3.ts", + }, + }, + ], + "results": Array [], + "tool": Object { + "driver": Object { + "informationUri": "https://eslint.org", + "name": "ESLint", + "rules": Array [], + "version": "7.32.0", + }, + }, + }, + ], + "version": "2.1.0", +} +`; + +exports[`formatEslintResultsAsSARIF should handle ignoreSuppressed: false with suppressed messages 1`] = ` +Object { + "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", + "runs": Array [ + Object { + "artifacts": Array [ + Object { + "location": Object { + "uri": "src/file4.ts", + }, + }, + ], + "results": Array [ + Object { + "level": "error", + "locations": Array [ + Object { + "physicalLocation": Object { + "artifactLocation": Object { + "index": 0, + "uri": "src/file4.ts", + }, + "region": Object { + "endColumn": 10, + "endLine": 1, + "snippet": Object { + "text": "debugger;", + }, + "startColumn": 1, + "startLine": 1, + }, + }, + }, + ], + "message": Object { + "text": "Unexpected 'debugger' statement.", + }, + "ruleId": "no-debugger", + "ruleIndex": 0, + "suppressions": Array [], + }, + Object { + "level": "warning", + "locations": Array [ + Object { + "physicalLocation": Object { + "artifactLocation": Object { + "index": 0, + "uri": "src/file4.ts", + }, + "region": Object { + "endColumn": 12, + "endLine": 2, + "snippet": Object { + "text": "console.log", + }, + "startColumn": 1, + "startLine": 2, + }, + }, + }, + ], + "message": Object { + "text": "Unexpected console statement.", + }, + "ruleId": "no-console", + "ruleIndex": 1, + "suppressions": Array [ + Object { + "justification": "rejected", + "kind": "external", + }, + ], + }, + ], + "tool": Object { + "driver": Object { + "informationUri": "https://eslint.org", + "name": "ESLint", + "rules": Array [ + Object { + "helpUri": "https://eslint.org/docs/latest/rules/no-debugger", + "id": "no-debugger", + "properties": Object { + "category": undefined, + }, + "shortDescription": Object { + "text": "Disallow the use of debugger", + }, + }, + Object { + "helpUri": "https://eslint.org/docs/latest/rules/no-console", + "id": "no-console", + "properties": Object { + "category": undefined, + }, + "shortDescription": Object { + "text": "Disallow the use of \`console\`", + }, + }, + ], + "version": "7.32.0", + }, + }, + }, + ], + "version": "2.1.0", +} +`; + +exports[`formatEslintResultsAsSARIF should handle ignoreSuppressed: true with suppressed messages 1`] = ` +Object { + "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", + "runs": Array [ + Object { + "artifacts": Array [ + Object { + "location": Object { + "uri": "src/file4.ts", + }, + }, + ], + "results": Array [ + Object { + "level": "error", + "locations": Array [ + Object { + "physicalLocation": Object { + "artifactLocation": Object { + "index": 0, + "uri": "src/file4.ts", + }, + "region": Object { + "endColumn": 10, + "endLine": 1, + "snippet": Object { + "text": "debugger;", + }, + "startColumn": 1, + "startLine": 1, + }, + }, + }, + ], + "message": Object { + "text": "Unexpected 'debugger' statement.", + }, + "ruleId": "no-debugger", + "ruleIndex": 0, + }, + ], + "tool": Object { + "driver": Object { + "informationUri": "https://eslint.org", + "name": "ESLint", + "rules": Array [ + Object { + "helpUri": "https://eslint.org/docs/latest/rules/no-debugger", + "id": "no-debugger", + "properties": Object { + "category": undefined, + }, + "shortDescription": Object { + "text": "Disallow the use of debugger", + }, + }, + ], + "version": "7.32.0", + }, + }, + }, + ], + "version": "2.1.0", +} +`; + +exports[`formatEslintResultsAsSARIF should handle multiple files 1`] = ` +Object { + "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", + "runs": Array [ + Object { + "artifacts": Array [ + Object { + "location": Object { + "uri": "src/file1.ts", + }, + }, + Object { + "location": Object { + "uri": "src/file2.ts", + }, + }, + ], + "results": Array [ + Object { + "level": "error", + "locations": Array [ + Object { + "physicalLocation": Object { + "artifactLocation": Object { + "index": 0, + "uri": "src/file1.ts", + }, + "region": Object { + "endColumn": 8, + "endLine": 1, + "snippet": Object { + "text": "x", + }, + "startColumn": 7, + "startLine": 1, + }, + }, + }, + ], + "message": Object { + "text": "'x' is defined but never used.", + }, + "ruleId": "no-unused-vars", + }, + Object { + "level": "error", + "locations": Array [ + Object { + "physicalLocation": Object { + "artifactLocation": Object { + "index": 1, + "uri": "src/file2.ts", + }, + "region": Object { + "endColumn": 15, + "endLine": 1, + "snippet": Object { + "text": "z == 2", + }, + "startColumn": 9, + "startLine": 1, + }, + }, + }, + ], + "message": Object { + "text": "Expected '===' and instead saw '=='.", + }, + "ruleId": "eqeqeq", + "ruleIndex": 0, + }, + ], + "tool": Object { + "driver": Object { + "informationUri": "https://eslint.org", + "name": "ESLint", + "rules": Array [ + Object { + "helpUri": "https://eslint.org/docs/latest/rules/eqeqeq", + "id": "eqeqeq", + "properties": Object { + "category": undefined, + }, + "shortDescription": Object { + "text": "Require the use of === and !==", + }, + }, + ], + "version": "7.32.0", + }, + }, + }, + ], + "version": "2.1.0", +} +`; + +exports[`formatEslintResultsAsSARIF should handle multiple issues in the same file 1`] = ` +Object { + "$schema": "http://json.schemastore.org/sarif-2.1.0-rtm.5", + "runs": Array [ + Object { + "artifacts": Array [ + Object { + "location": Object { + "uri": "src/file2.ts", + }, + }, + ], + "results": Array [ + Object { + "level": "error", + "locations": Array [ + Object { + "physicalLocation": Object { + "artifactLocation": Object { + "index": 0, + "uri": "src/file2.ts", + }, + "region": Object { + "endColumn": 6, + "endLine": 1, + "snippet": Object { + "text": "x", + }, + "startColumn": 5, + "startLine": 1, + }, + }, + }, + ], + "message": Object { + "text": "'x' is defined but never used.", + }, + "ruleId": "no-unused-vars", + "ruleIndex": 0, + }, + Object { + "level": "warning", + "locations": Array [ + Object { + "physicalLocation": Object { + "artifactLocation": Object { + "index": 0, + "uri": "src/file2.ts", + }, + "region": Object { + "endColumn": 12, + "endLine": 2, + "snippet": Object { + "text": "console.log", + }, + "startColumn": 1, + "startLine": 2, + }, + }, + }, + ], + "message": Object { + "text": "Unexpected console statement.", + }, + "ruleId": "no-console", + "ruleIndex": 1, + }, + ], + "tool": Object { + "driver": Object { + "informationUri": "https://eslint.org", + "name": "ESLint", + "rules": Array [ + Object { + "helpUri": "https://eslint.org/docs/latest/rules/no-unused-vars", + "id": "no-unused-vars", + "properties": Object { + "category": undefined, + }, + "shortDescription": Object { + "text": "'x' is defined but never used.", + }, + }, + Object { + "helpUri": "https://eslint.org/docs/latest/rules/no-console", + "id": "no-console", + "properties": Object { + "category": undefined, + }, + "shortDescription": Object { + "text": "Disallow the use of \`console\`", + }, + }, + ], + "version": "7.32.0", + }, + }, + }, + ], + "version": "2.1.0", +} +`; diff --git a/heft-plugins/heft-lint-plugin/tsconfig.json b/heft-plugins/heft-lint-plugin/tsconfig.json index 7512871fdbf..1a33d17b873 100644 --- a/heft-plugins/heft-lint-plugin/tsconfig.json +++ b/heft-plugins/heft-lint-plugin/tsconfig.json @@ -1,7 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["node"] - } + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/heft-plugins/heft-localization-typings-plugin/.npmignore b/heft-plugins/heft-localization-typings-plugin/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/heft-plugins/heft-localization-typings-plugin/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-localization-typings-plugin/CHANGELOG.json b/heft-plugins/heft-localization-typings-plugin/CHANGELOG.json new file mode 100644 index 00000000000..7c8d8425366 --- /dev/null +++ b/heft-plugins/heft-localization-typings-plugin/CHANGELOG.json @@ -0,0 +1,1665 @@ +{ + "name": "@rushstack/heft-localization-typings-plugin", + "entries": [ + { + "version": "1.2.0", + "tag": "@rushstack/heft-localization-typings-plugin_v1.2.0", + "date": "Tue, 04 Aug 2026 00:17:24 GMT", + "comments": { + "minor": [ + { + "comment": "Add opt-in declaration source map generation so editors can resolve go-to-definition from generated typings to the original source file." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.16.0`" + } + ] + } + }, + { + "version": "1.1.23", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.23", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "1.1.22", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.22", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "1.1.21", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.21", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "1.1.20", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.20", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "1.1.19", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.19", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "1.1.18", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.18", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "1.1.17", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.17", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "1.1.16", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.16", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "1.1.15", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.15", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "1.1.14", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.14", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "1.1.13", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.13", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "1.1.12", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.12", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "1.1.11", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.11", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "1.1.10", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.10", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "1.1.9", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.9", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "1.1.8", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.8", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "1.1.7", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.7", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "1.1.6", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.6", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "1.1.5", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.5", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "1.1.4", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.4", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.3", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.2", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.1", + "date": "Thu, 19 Feb 2026 01:30:06 GMT", + "comments": { + "patch": [ + { + "comment": "Add missing README and LICENSE files to package." + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-localization-typings-plugin_v1.1.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "1.0.15", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.15", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "1.0.14", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.14", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "1.0.13", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.13", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "1.0.12", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.12", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "1.0.11", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.11", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "1.0.10", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.10", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "1.0.9", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.9", + "date": "Mon, 05 Jan 2026 16:12:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "1.0.8", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.8", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "1.0.7", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.7", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.5` to `1.1.6`" + } + ] + } + }, + { + "version": "1.0.6", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.6", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.4` to `1.1.5`" + } + ] + } + }, + { + "version": "1.0.5", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.5", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.3` to `1.1.4`" + } + ] + } + }, + { + "version": "1.0.4", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.4", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.2` to `1.1.3`" + } + ] + } + }, + { + "version": "1.0.3", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.3", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.1` to `1.1.2`" + } + ] + } + }, + { + "version": "1.0.2", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.2", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.0` to `1.1.1`" + } + ] + } + }, + { + "version": "1.0.1", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.1", + "date": "Fri, 03 Oct 2025 20:10:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.0.0` to `1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-localization-typings-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.75.0` to `1.0.0`" + } + ] + } + }, + { + "version": "0.3.22", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.22", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.5` to `0.75.0`" + } + ] + } + }, + { + "version": "0.3.21", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.21", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.4` to `0.74.5`" + } + ] + } + }, + { + "version": "0.3.20", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.20", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.3` to `0.74.4`" + } + ] + } + }, + { + "version": "0.3.19", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.19", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.2` to `0.74.3`" + } + ] + } + }, + { + "version": "0.3.18", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.18", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.1` to `0.74.2`" + } + ] + } + }, + { + "version": "0.3.17", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.17", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.0` to `0.74.1`" + } + ] + } + }, + { + "version": "0.3.16", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.16", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.6` to `0.74.0`" + } + ] + } + }, + { + "version": "0.3.15", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.15", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.5` to `0.73.6`" + } + ] + } + }, + { + "version": "0.3.14", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.14", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.4` to `0.73.5`" + } + ] + } + }, + { + "version": "0.3.13", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.13", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.3` to `0.73.4`" + } + ] + } + }, + { + "version": "0.3.12", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.12", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.2` to `0.73.3`" + } + ] + } + }, + { + "version": "0.3.11", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.11", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.1` to `0.73.2`" + } + ] + } + }, + { + "version": "0.3.10", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.10", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.0` to `0.73.1`" + } + ] + } + }, + { + "version": "0.3.9", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.9", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.72.0` to `0.73.0`" + } + ] + } + }, + { + "version": "0.3.8", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.8", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.2` to `0.72.0`" + } + ] + } + }, + { + "version": "0.3.7", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.7", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.1` to `0.71.2`" + } + ] + } + }, + { + "version": "0.3.6", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.6", + "date": "Tue, 25 Mar 2025 15:11:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.0` to `0.71.1`" + } + ] + } + }, + { + "version": "0.3.5", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.5", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.1` to `0.71.0`" + } + ] + } + }, + { + "version": "0.3.4", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.4", + "date": "Wed, 12 Mar 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.0` to `0.70.1`" + } + ] + } + }, + { + "version": "0.3.3", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.3", + "date": "Tue, 11 Mar 2025 02:12:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.3` to `0.70.0`" + } + ] + } + }, + { + "version": "0.3.2", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.2", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.2` to `0.69.3`" + } + ] + } + }, + { + "version": "0.3.1", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.1", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.1` to `0.69.2`" + } + ] + } + }, + { + "version": "0.3.0", + "tag": "@rushstack/heft-localization-typings-plugin_v0.3.0", + "date": "Thu, 27 Feb 2025 16:10:47 GMT", + "comments": { + "minor": [ + { + "comment": "Add option \"trimmedJsonOutputFolders\" to allow the plugin to output an object mapping the string names to untranslated strings as JSON in the specified folders. This allows bundlers and unit tests to operate on those strings without special handling." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.13.0`" + } + ] + } + }, + { + "version": "0.2.24", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.24", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.0` to `0.69.1`" + } + ] + } + }, + { + "version": "0.2.23", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.23", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.18` to `0.69.0`" + } + ] + } + }, + { + "version": "0.2.22", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.22", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.17` to `0.68.18`" + } + ] + } + }, + { + "version": "0.2.21", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.21", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.16` to `0.68.17`" + } + ] + } + }, + { + "version": "0.2.20", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.20", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.15` to `0.68.16`" + } + ] + } + }, + { + "version": "0.2.19", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.19", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.14` to `0.68.15`" + } + ] + } + }, + { + "version": "0.2.18", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.18", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.13` to `0.68.14`" + } + ] + } + }, + { + "version": "0.2.17", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.17", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.12` to `0.68.13`" + } + ] + } + }, + { + "version": "0.2.16", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.16", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.11` to `0.68.12`" + } + ] + } + }, + { + "version": "0.2.15", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.15", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.10` to `0.68.11`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.14", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.9` to `0.68.10`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.13", + "date": "Tue, 03 Dec 2024 16:11:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.8` to `0.68.9`" + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.12", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.7` to `0.68.8`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.11", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.6` to `0.68.7`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.10", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.5` to `0.68.6`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.9", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.4` to `0.68.5`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.8", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.3` to `0.68.4`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.7", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.2` to `0.68.3`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.6", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.1` to `0.68.2`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.5", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.0` to `0.68.1`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.4", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.2` to `0.68.0`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.3", + "date": "Sat, 28 Sep 2024 00:11:41 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.3`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.2", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.1` to `0.67.2`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.1", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.0` to `0.67.1`" + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/heft-localization-typings-plugin_v0.2.0", + "date": "Mon, 26 Aug 2024 02:00:11 GMT", + "comments": { + "minor": [ + { + "comment": "Add a `valueDocumentationComment` option to `exportAsDefault` that allows a documentation comment to be generated for the exported value." + }, + { + "comment": "Rename the `documentationComment` property in the `exportAsDefault` value to `interfaceDocumentationComment`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.12.0`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/heft-localization-typings-plugin_v0.1.2", + "date": "Wed, 21 Aug 2024 16:24:51 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where the `stringNamesToIgnore` option was ignored." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.11.1`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/heft-localization-typings-plugin_v0.1.1", + "date": "Wed, 21 Aug 2024 06:52:07 GMT", + "comments": { + "patch": [ + { + "comment": "Fix a misnamed property in the options schema." + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/heft-localization-typings-plugin_v0.1.0", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "minor": [ + { + "comment": "Initial release." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/localization-utilities\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.26` to `0.67.0`" + } + ] + } + } + ] +} diff --git a/heft-plugins/heft-localization-typings-plugin/CHANGELOG.md b/heft-plugins/heft-localization-typings-plugin/CHANGELOG.md new file mode 100644 index 00000000000..6c18ff42a46 --- /dev/null +++ b/heft-plugins/heft-localization-typings-plugin/CHANGELOG.md @@ -0,0 +1,485 @@ +# Change Log - @rushstack/heft-localization-typings-plugin + +This log was last generated on Tue, 04 Aug 2026 00:17:24 GMT and should not be manually modified. + +## 1.2.0 +Tue, 04 Aug 2026 00:17:24 GMT + +### Minor changes + +- Add opt-in declaration source map generation so editors can resolve go-to-definition from generated typings to the original source file. + +## 1.1.23 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 1.1.22 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 1.1.21 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.1.20 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 1.1.19 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 1.1.18 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.1.17 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.1.16 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 1.1.15 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 1.1.14 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.1.13 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.1.12 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.1.11 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.1.10 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.1.9 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.1.8 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.1.7 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.1.6 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 1.1.5 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.1.4 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.1.3 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.1.2 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.1.1 +Thu, 19 Feb 2026 01:30:06 GMT + +### Patches + +- Add missing README and LICENSE files to package. + +## 1.1.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.0.15 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.0.14 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.0.13 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.0.12 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.0.11 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.0.10 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 1.0.9 +Mon, 05 Jan 2026 16:12:50 GMT + +_Version update only_ + +## 1.0.8 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.0.7 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.0.6 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.0.5 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 1.0.4 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.0.3 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.0.2 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 1.0.1 +Fri, 03 Oct 2025 20:10:00 GMT + +_Version update only_ + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.3.22 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.3.21 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.3.20 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.3.19 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.3.18 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 0.3.17 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.3.16 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.3.15 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.3.14 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.3.13 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.3.12 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.3.11 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.3.10 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.3.9 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 0.3.8 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 0.3.7 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.3.6 +Tue, 25 Mar 2025 15:11:16 GMT + +_Version update only_ + +## 0.3.5 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.3.4 +Wed, 12 Mar 2025 00:11:32 GMT + +_Version update only_ + +## 0.3.3 +Tue, 11 Mar 2025 02:12:34 GMT + +_Version update only_ + +## 0.3.2 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.3.1 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.3.0 +Thu, 27 Feb 2025 16:10:47 GMT + +### Minor changes + +- Add option "trimmedJsonOutputFolders" to allow the plugin to output an object mapping the string names to untranslated strings as JSON in the specified folders. This allows bundlers and unit tests to operate on those strings without special handling. + +## 0.2.24 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.2.23 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 0.2.22 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.2.21 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.2.20 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.2.19 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 0.2.18 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.2.17 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.2.16 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.2.15 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.2.14 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.2.13 +Tue, 03 Dec 2024 16:11:08 GMT + +_Version update only_ + +## 0.2.12 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.2.11 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.2.10 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.2.9 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.2.8 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.2.7 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.2.6 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.2.5 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.2.4 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.2.3 +Sat, 28 Sep 2024 00:11:41 GMT + +_Version update only_ + +## 0.2.2 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.2.1 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.2.0 +Mon, 26 Aug 2024 02:00:11 GMT + +### Minor changes + +- Add a `valueDocumentationComment` option to `exportAsDefault` that allows a documentation comment to be generated for the exported value. +- Rename the `documentationComment` property in the `exportAsDefault` value to `interfaceDocumentationComment`. + +## 0.1.2 +Wed, 21 Aug 2024 16:24:51 GMT + +### Patches + +- Fix an issue where the `stringNamesToIgnore` option was ignored. + +## 0.1.1 +Wed, 21 Aug 2024 06:52:07 GMT + +### Patches + +- Fix a misnamed property in the options schema. + +## 0.1.0 +Wed, 21 Aug 2024 05:43:04 GMT + +### Minor changes + +- Initial release. + diff --git a/heft-plugins/heft-localization-typings-plugin/LICENSE b/heft-plugins/heft-localization-typings-plugin/LICENSE new file mode 100644 index 00000000000..2c4ae6d8c13 --- /dev/null +++ b/heft-plugins/heft-localization-typings-plugin/LICENSE @@ -0,0 +1,24 @@ +@rushstack/heft-localization-typings-plugin + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/heft-plugins/heft-localization-typings-plugin/README.md b/heft-plugins/heft-localization-typings-plugin/README.md new file mode 100644 index 00000000000..e0527c2dfbe --- /dev/null +++ b/heft-plugins/heft-localization-typings-plugin/README.md @@ -0,0 +1,11 @@ +# @rushstack/heft-localization-typings-plugin + +Heft plugin for generating types for localization files. + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/heft-plugins/heft-localization-typings-plugin/CHANGELOG.md) - Find + out what's new in the latest version + +Heft is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/heft-plugins/heft-localization-typings-plugin/config/heft.json b/heft-plugins/heft-localization-typings-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-localization-typings-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-localization-typings-plugin/config/rig.json b/heft-plugins/heft-localization-typings-plugin/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/heft-plugins/heft-localization-typings-plugin/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/heft-plugins/heft-localization-typings-plugin/eslint.config.js b/heft-plugins/heft-localization-typings-plugin/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/heft-plugins/heft-localization-typings-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-localization-typings-plugin/heft-plugin.json b/heft-plugins/heft-localization-typings-plugin/heft-plugin.json new file mode 100644 index 00000000000..cae29929771 --- /dev/null +++ b/heft-plugins/heft-localization-typings-plugin/heft-plugin.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", + + "taskPlugins": [ + { + "pluginName": "localization-typings-plugin", + "entryPoint": "./lib-commonjs/LocalizationTypingsPlugin", + "optionsSchema": "./lib-commonjs/schemas/heft-localization-typings-plugin.schema.json" + } + ] +} diff --git a/heft-plugins/heft-localization-typings-plugin/package.json b/heft-plugins/heft-localization-typings-plugin/package.json new file mode 100644 index 00000000000..e047c9ccc16 --- /dev/null +++ b/heft-plugins/heft-localization-typings-plugin/package.json @@ -0,0 +1,48 @@ +{ + "name": "@rushstack/heft-localization-typings-plugin", + "version": "1.2.0", + "description": "Heft plugin for generating types for localization files.", + "repository": { + "type": "git", + "url": "https://github.com/microsoft/rushstack.git", + "directory": "heft-plugins/heft-localization-typings-plugin" + }, + "homepage": "https://rushstack.io/pages/heft/overview/", + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "start": "heft test --clean --watch", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "peerDependencies": { + "@rushstack/heft": "^1.2.22" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "dependencies": { + "@rushstack/localization-utilities": "workspace:*" + }, + "exports": { + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "sideEffects": false +} diff --git a/heft-plugins/heft-localization-typings-plugin/src/LocalizationTypingsPlugin.ts b/heft-plugins/heft-localization-typings-plugin/src/LocalizationTypingsPlugin.ts new file mode 100644 index 00000000000..c93d7afca5c --- /dev/null +++ b/heft-plugins/heft-localization-typings-plugin/src/LocalizationTypingsPlugin.ts @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { + HeftConfiguration, + IHeftTaskPlugin, + IHeftTaskRunIncrementalHookOptions, + IHeftTaskSession, + IScopedLogger, + IWatchedFileState +} from '@rushstack/heft'; +import { type ITypingsGeneratorOptions, TypingsGenerator } from '@rushstack/localization-utilities'; + +export interface ILocalizationTypingsPluginOptions { + /** + * Source code root directory. + * Defaults to "src/". + */ + srcFolder?: string; + + /** + * Output directory for generated typings. + * Defaults to "temp/loc-ts/". + */ + generatedTsFolder?: string; + + /** + * Folders, relative to the project root, where JSON files containing only the key/string pairs should be emitted to. + * These files will be emitted as `.resx.json`, `.loc.json`, or `.resjson`, depending on the input file extension. + * The intent is that bundlers can find these files and load them to receive the original untranslated strings. + */ + trimmedJsonOutputFolders?: string[]; + + /** + * Additional folders, relative to the project root, where the generated typings should be emitted to. + */ + secondaryGeneratedTsFolders?: string[]; + + exportAsDefault?: ITypingsGeneratorOptions['exportAsDefault']; + + /** + * An array of string names to ignore when generating typings. + */ + stringNamesToIgnore?: string[]; + + /** + * If true, a `.d.ts.map` file is emitted next to each generated `.d.ts` file. This allows editors + * to resolve "go to definition" on a localized string to its declaration in the source + * localization file, instead of to the generated typings. + * + * @defaultValue false + */ + generateDeclarationMaps?: boolean; +} + +const PLUGIN_NAME: 'localization-typings-plugin' = 'localization-typings-plugin'; + +export default class LocalizationTypingsPlugin implements IHeftTaskPlugin { + public apply( + taskSession: IHeftTaskSession, + { slashNormalizedBuildFolderPath }: HeftConfiguration, + options?: ILocalizationTypingsPluginOptions + ): void { + const { + srcFolder, + generatedTsFolder, + stringNamesToIgnore, + secondaryGeneratedTsFolders: secondaryGeneratedTsFoldersFromOptions, + trimmedJsonOutputFolders: trimmedJsonOutputFoldersFromOptions + } = options ?? {}; + + let secondaryGeneratedTsFolders: string[] | undefined; + if (secondaryGeneratedTsFoldersFromOptions) { + secondaryGeneratedTsFolders = []; + for (const secondaryGeneratedTsFolder of secondaryGeneratedTsFoldersFromOptions) { + secondaryGeneratedTsFolders.push(`${slashNormalizedBuildFolderPath}/${secondaryGeneratedTsFolder}`); + } + } + + let trimmedJsonOutputFolders: string[] | undefined; + if (trimmedJsonOutputFoldersFromOptions) { + trimmedJsonOutputFolders = []; + for (const trimmedJsonOutputFolder of trimmedJsonOutputFoldersFromOptions) { + trimmedJsonOutputFolders.push(`${slashNormalizedBuildFolderPath}/${trimmedJsonOutputFolder}`); + } + } + + const logger: IScopedLogger = taskSession.logger; + const stringNamesToIgnoreSet: Set | undefined = stringNamesToIgnore + ? new Set(stringNamesToIgnore) + : undefined; + + const typingsGenerator: TypingsGenerator = new TypingsGenerator({ + ...options, + srcFolder: `${slashNormalizedBuildFolderPath}/${srcFolder ?? 'src'}`, + generatedTsFolder: `${slashNormalizedBuildFolderPath}/${generatedTsFolder ?? 'temp/loc-ts'}`, + terminal: logger.terminal, + ignoreString: stringNamesToIgnoreSet + ? (filePath: string, stringName: string) => stringNamesToIgnoreSet.has(stringName) + : undefined, + secondaryGeneratedTsFolders + }); + + taskSession.hooks.run.tapPromise(PLUGIN_NAME, async () => { + await this._runLocalizationTypingsGeneratorAsync(typingsGenerator, logger, undefined); + }); + + taskSession.hooks.runIncremental.tapPromise( + PLUGIN_NAME, + async (runIncrementalOptions: IHeftTaskRunIncrementalHookOptions) => { + await this._runLocalizationTypingsGeneratorAsync(typingsGenerator, logger, runIncrementalOptions); + } + ); + } + + private async _runLocalizationTypingsGeneratorAsync( + typingsGenerator: TypingsGenerator, + { terminal }: IScopedLogger, + runIncrementalOptions: IHeftTaskRunIncrementalHookOptions | undefined + ): Promise { + // If we have the incremental options, use them to determine which files to process. + // Otherwise, process all files. The typings generator also provides the file paths + // as relative paths from the sourceFolderPath. + let changedRelativeFilePaths: string[] | undefined; + if (runIncrementalOptions) { + changedRelativeFilePaths = []; + const relativeFilePaths: Map = await runIncrementalOptions.watchGlobAsync( + typingsGenerator.inputFileGlob, + { + cwd: typingsGenerator.sourceFolderPath, + ignore: Array.from(typingsGenerator.ignoredFileGlobs), + absolute: false + } + ); + for (const [relativeFilePath, { changed }] of relativeFilePaths) { + if (changed) { + changedRelativeFilePaths.push(relativeFilePath); + } + } + if (changedRelativeFilePaths.length === 0) { + return; + } + } + + terminal.writeLine('Generating localization typings...'); + await typingsGenerator.generateTypingsAsync(changedRelativeFilePaths); + } +} diff --git a/heft-plugins/heft-localization-typings-plugin/src/schemas/heft-localization-typings-plugin.schema.json b/heft-plugins/heft-localization-typings-plugin/src/schemas/heft-localization-typings-plugin.schema.json new file mode 100644 index 00000000000..2d8570602cd --- /dev/null +++ b/heft-plugins/heft-localization-typings-plugin/src/schemas/heft-localization-typings-plugin.schema.json @@ -0,0 +1,90 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "type": "object", + "additionalProperties": false, + "properties": { + "exportAsDefault": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "interfaceDocumentationComment": { + "type": "string", + "description": "This value is placed in a documentation comment for the exported default interface." + }, + "valueDocumentationComment": { + "type": "string", + "description": "This value is placed in a documentation comment for the exported value." + }, + "interfaceName": { + "type": "string", + "description": "The interface name for the default wrapped export. Defaults to \"IExport\"" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "interfaceDocumentationComment": { + "type": "string", + "description": "This value is placed in a documentation comment for the exported default interface." + }, + "valueDocumentationComment": { + "type": "string", + "description": "This value is placed in a documentation comment for the exported value." + }, + "inferInterfaceNameFromFilename": { + "type": "boolean", + "description": "When set to true, the default export interface name will be inferred from the filename. This takes precedence over the interfaceName option." + } + } + } + ] + }, + + "srcFolder": { + "type": "string", + "description": "Source code root directory. Defaults to \"src/\"." + }, + + "trimmedJsonOutputFolders": { + "type": "array", + "description": "Output folders, relative to the project root, where JSON files that have had comments and any ignored strings discarded should be emitted to.", + "items": { + "type": "string" + } + }, + + "generatedTsFolder": { + "type": "string", + "description": "Output directory for generated typings. Defaults to \"temp/loc-ts/\"." + }, + + "secondaryGeneratedTsFolders": { + "type": "array", + "description": "Additional folders, relative to the project root, where the generated typings should be emitted to.", + "items": { + "type": "string" + } + }, + + "stringNamesToIgnore": { + "type": "array", + "description": "An array of string names to ignore when generating typings.", + "items": { + "type": "string" + } + }, + + "generateDeclarationMaps": { + "type": "boolean", + "description": "If true, a \".d.ts.map\" file is emitted next to each generated \".d.ts\" file, allowing editors to resolve \"go to definition\" to the source localization file instead of the generated typings." + } + } +} diff --git a/heft-plugins/heft-localization-typings-plugin/tsconfig.json b/heft-plugins/heft-localization-typings-plugin/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/heft-plugins/heft-localization-typings-plugin/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/heft-plugins/heft-rspack-plugin/.npmignore b/heft-plugins/heft-rspack-plugin/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-rspack-plugin/CHANGELOG.json b/heft-plugins/heft-rspack-plugin/CHANGELOG.json new file mode 100644 index 00000000000..f7ed3ec57d0 --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/CHANGELOG.json @@ -0,0 +1,740 @@ +{ + "name": "@rushstack/heft-rspack-plugin", + "entries": [ + { + "version": "0.3.23", + "tag": "@rushstack/heft-rspack-plugin_v0.3.23", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "0.3.22", + "tag": "@rushstack/heft-rspack-plugin_v0.3.22", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.22`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "0.3.21", + "tag": "@rushstack/heft-rspack-plugin_v0.3.21", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.21`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "0.3.20", + "tag": "@rushstack/heft-rspack-plugin_v0.3.20", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "0.3.19", + "tag": "@rushstack/heft-rspack-plugin_v0.3.19", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "0.3.18", + "tag": "@rushstack/heft-rspack-plugin_v0.3.18", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "0.3.17", + "tag": "@rushstack/heft-rspack-plugin_v0.3.17", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "0.3.16", + "tag": "@rushstack/heft-rspack-plugin_v0.3.16", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "0.3.15", + "tag": "@rushstack/heft-rspack-plugin_v0.3.15", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.15`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "0.3.14", + "tag": "@rushstack/heft-rspack-plugin_v0.3.14", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.14`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "0.3.13", + "tag": "@rushstack/heft-rspack-plugin_v0.3.13", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "0.3.12", + "tag": "@rushstack/heft-rspack-plugin_v0.3.12", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "0.3.11", + "tag": "@rushstack/heft-rspack-plugin_v0.3.11", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "0.3.10", + "tag": "@rushstack/heft-rspack-plugin_v0.3.10", + "date": "Thu, 02 Apr 2026 00:14:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.10`" + } + ] + } + }, + { + "version": "0.3.9", + "tag": "@rushstack/heft-rspack-plugin_v0.3.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "0.3.8", + "tag": "@rushstack/heft-rspack-plugin_v0.3.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "0.3.7", + "tag": "@rushstack/heft-rspack-plugin_v0.3.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "0.3.6", + "tag": "@rushstack/heft-rspack-plugin_v0.3.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "0.3.5", + "tag": "@rushstack/heft-rspack-plugin_v0.3.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "0.3.4", + "tag": "@rushstack/heft-rspack-plugin_v0.3.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "0.3.3", + "tag": "@rushstack/heft-rspack-plugin_v0.3.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "0.3.2", + "tag": "@rushstack/heft-rspack-plugin_v0.3.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "0.3.1", + "tag": "@rushstack/heft-rspack-plugin_v0.3.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "0.3.0", + "tag": "@rushstack/heft-rspack-plugin_v0.3.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/heft-rspack-plugin_v0.2.9", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/heft-rspack-plugin_v0.2.8", + "date": "Thu, 05 Feb 2026 01:54:04 GMT", + "comments": { + "patch": [ + { + "comment": "Bump `webpack` dependency version to `~5.105.0`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/heft-rspack-plugin_v0.2.7", + "date": "Thu, 05 Feb 2026 00:23:59 GMT", + "comments": { + "patch": [ + { + "comment": "Bump `webpack` dependency version to `~5.104.1`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/heft-rspack-plugin_v0.2.6", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/heft-rspack-plugin_v0.2.5", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/heft-rspack-plugin_v0.2.4", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/heft-rspack-plugin_v0.2.3", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/heft-rspack-plugin_v0.2.2", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/heft-rspack-plugin_v0.2.1", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/heft-rspack-plugin_v0.2.0", + "date": "Thu, 18 Dec 2025 01:13:04 GMT", + "comments": { + "minor": [ + { + "comment": "Update Webpack dependency to `~5.103.0`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/heft-rspack-plugin_v0.1.2", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/heft-rspack-plugin_v0.1.1", + "date": "Tue, 25 Nov 2025 17:03:49 GMT", + "comments": { + "patch": [ + { + "comment": "Fix issue where ignoring ERR_MODULE_NOT_FOUND errors when importing the rspack config masks legitimate import issues." + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/heft-rspack-plugin_v0.1.0", + "date": "Fri, 21 Nov 2025 16:13:55 GMT", + "comments": { + "minor": [ + { + "comment": "Initial package release." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.5` to `1.1.6`" + } + ] + } + } + ] +} diff --git a/heft-plugins/heft-rspack-plugin/CHANGELOG.md b/heft-plugins/heft-rspack-plugin/CHANGELOG.md new file mode 100644 index 00000000000..4a05e1e1249 --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/CHANGELOG.md @@ -0,0 +1,203 @@ +# Change Log - @rushstack/heft-rspack-plugin + +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 0.3.23 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 0.3.22 +Fri, 17 Jul 2026 00:15:59 GMT + +_Version update only_ + +## 0.3.21 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.3.20 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.3.19 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 0.3.18 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.3.17 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 0.3.16 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 0.3.15 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 0.3.14 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.3.13 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 0.3.12 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.3.11 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 0.3.10 +Thu, 02 Apr 2026 00:14:38 GMT + +_Version update only_ + +## 0.3.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 0.3.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.3.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 0.3.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.3.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 0.3.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.3.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 0.3.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 0.3.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.3.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.2.9 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 0.2.8 +Thu, 05 Feb 2026 01:54:04 GMT + +### Patches + +- Bump `webpack` dependency version to `~5.105.0` + +## 0.2.7 +Thu, 05 Feb 2026 00:23:59 GMT + +### Patches + +- Bump `webpack` dependency version to `~5.104.1` + +## 0.2.6 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 0.2.5 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 0.2.4 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 0.2.3 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 0.2.2 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 0.2.1 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 0.2.0 +Thu, 18 Dec 2025 01:13:04 GMT + +### Minor changes + +- Update Webpack dependency to `~5.103.0` + +## 0.1.2 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 0.1.1 +Tue, 25 Nov 2025 17:03:49 GMT + +### Patches + +- Fix issue where ignoring ERR_MODULE_NOT_FOUND errors when importing the rspack config masks legitimate import issues. + +## 0.1.0 +Fri, 21 Nov 2025 16:13:55 GMT + +### Minor changes + +- Initial package release. + diff --git a/heft-plugins/heft-rspack-plugin/LICENSE b/heft-plugins/heft-rspack-plugin/LICENSE new file mode 100644 index 00000000000..53dca6cd336 --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/LICENSE @@ -0,0 +1,24 @@ +@rushstack/heft-rspack-plugin + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/heft-plugins/heft-rspack-plugin/README.md b/heft-plugins/heft-rspack-plugin/README.md new file mode 100644 index 00000000000..91a8874cfa1 --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/README.md @@ -0,0 +1,12 @@ +# @rushstack/heft-rspack-plugin + +This is a Heft plugin for using Rspack. + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/heft-plugins/heft-rspack-plugin/CHANGELOG.md) - Find + out what's new in the latest version +- [@rushstack/heft](https://www.npmjs.com/package/@rushstack/heft) - Heft is a config-driven toolchain that invokes popular tools such as TypeScript, ESLint, Jest, Webpack, and API Extractor. + +Heft is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/heft-plugins/heft-rspack-plugin/config/api-extractor.json b/heft-plugins/heft-rspack-plugin/config/api-extractor.json new file mode 100644 index 00000000000..5f6b2655ac8 --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/config/api-extractor.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json", + + "docModel": { + "enabled": false + }, + + "dtsRollup": { + "enabled": true, + "betaTrimmedFilePath": "/dist/.d.ts" + } +} diff --git a/heft-plugins/heft-rspack-plugin/config/heft.json b/heft-plugins/heft-rspack-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-rspack-plugin/config/jest.config.json b/heft-plugins/heft-rspack-plugin/config/jest.config.json new file mode 100644 index 00000000000..d1749681d90 --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/config/jest.config.json @@ -0,0 +1,3 @@ +{ + "extends": "local-node-rig/profiles/default/config/jest.config.json" +} diff --git a/heft-plugins/heft-rspack-plugin/config/rig.json b/heft-plugins/heft-rspack-plugin/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/heft-plugins/heft-rspack-plugin/eslint.config.js b/heft-plugins/heft-rspack-plugin/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-rspack-plugin/heft-plugin.json b/heft-plugins/heft-rspack-plugin/heft-plugin.json new file mode 100644 index 00000000000..cec21c0dfc3 --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/heft-plugin.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", + + "taskPlugins": [ + { + "pluginName": "rspack-plugin", + "entryPoint": "./lib-commonjs/RspackPlugin", + "optionsSchema": "./lib-commonjs/schemas/heft-rspack-plugin.schema.json", + + "parameterScope": "rspack", + "parameters": [ + { + "longName": "--serve", + "parameterKind": "flag", + "description": "Start a local web server for testing purposes using @rspack/dev-server. This parameter is only available when running in watch mode." + } + ] + } + ] +} diff --git a/heft-plugins/heft-rspack-plugin/package.json b/heft-plugins/heft-rspack-plugin/package.json new file mode 100644 index 00000000000..1391cef83af --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/package.json @@ -0,0 +1,66 @@ +{ + "name": "@rushstack/heft-rspack-plugin", + "version": "0.3.23", + "description": "Heft plugin for Rspack", + "repository": { + "type": "git", + "url": "https://github.com/microsoft/rushstack.git", + "directory": "heft-plugins/heft-rspack-plugin" + }, + "homepage": "https://rushstack.io/pages/heft/overview/", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/heft-rspack-plugin.d.ts", + "exports": { + ".": { + "types": "./dist/heft-rspack-plugin.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "start": "heft test --clean --watch", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "peerDependencies": { + "@rushstack/heft": "^1.2.22", + "@rspack/core": "^1.6.0-beta.0" + }, + "dependencies": { + "@rushstack/debug-certificate-manager": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "tapable": "2.3.0", + "@rspack/dev-server": "^1.1.4", + "watchpack": "2.4.0", + "webpack": "~5.105.2" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "@rushstack/terminal": "workspace:*", + "@types/watchpack": "2.4.0", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*", + "@rspack/core": "~1.6.0-beta.0" + }, + "sideEffects": false +} diff --git a/heft-plugins/heft-rspack-plugin/src/DeferredWatchFileSystem.ts b/heft-plugins/heft-rspack-plugin/src/DeferredWatchFileSystem.ts new file mode 100644 index 00000000000..1b7a437c21d --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/src/DeferredWatchFileSystem.ts @@ -0,0 +1,235 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import Watchpack, { type WatchOptions } from 'watchpack'; +import type { Compiler, RspackPluginInstance, WatchFileSystem } from '@rspack/core'; + +// InputFileSystem type is defined inline since it's not exported from @rspack/core +// missing re-export here: https://github.com/web-infra-dev/rspack/blob/9542b49ad43f91ecbcb37ff277e0445e67b99967/packages/rspack/src/exports.ts#L133 +// type definition here: https://github.com/web-infra-dev/rspack/blob/9542b49ad43f91ecbcb37ff277e0445e67b99967/packages/rspack/src/util/fs.ts#L496 +// eslint-disable-next-line @typescript-eslint/naming-convention +export interface InputFileSystem { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + readFile: (...args: any[]) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + readlink: (...args: any[]) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + readdir: (...args: any[]) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + stat: (...args: any[]) => void; + purge?: (files?: string | string[] | Set) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any; +} + +export type WatchCallback = Parameters[5]; +export type WatchUndelayedCallback = Parameters[6]; +export type Watcher = ReturnType; +export type WatcherInfo = ReturnType['getInfo']>; +type FileSystemMap = ReturnType>; + +interface IWatchState { + changes: Set; + removals: Set; + + callback: WatchCallback; +} + +interface ITimeEntry { + timestamp: number; + safeTime: number; +} + +type IRawFileSystemMap = Map; + +interface ITimeInfoEntries { + fileTimeInfoEntries: FileSystemMap; + contextTimeInfoEntries: FileSystemMap; +} + +export class DeferredWatchFileSystem implements WatchFileSystem { + public readonly inputFileSystem: InputFileSystem; + public readonly watcherOptions: WatchOptions; + public watcher: Watchpack | undefined; + + private readonly _onChange: () => void; + private _state: IWatchState | undefined; + + public constructor(inputFileSystem: InputFileSystem, onChange: () => void) { + this.inputFileSystem = inputFileSystem; + this.watcherOptions = { + aggregateTimeout: 0 + }; + this.watcher = new Watchpack(this.watcherOptions); + this._onChange = onChange; + } + + public flush(): boolean { + const state: IWatchState | undefined = this._state; + + if (!state) { + return false; + } + + const { changes, removals, callback } = state; + + // Force flush the aggregation callback + const { changes: newChanges, removals: newRemovals } = this.watcher!.getAggregated(); + + // Rspack (like Webpack 5) treats changes and removals as separate things + if (newRemovals) { + for (const removal of newRemovals) { + changes.delete(removal); + removals.add(removal); + } + } + if (newChanges) { + for (const change of newChanges) { + removals.delete(change); + changes.add(change); + } + } + + if (changes.size > 0 || removals.size > 0) { + this._purge(removals, changes); + + const { fileTimeInfoEntries, contextTimeInfoEntries } = this._fetchTimeInfo(); + + callback(null, fileTimeInfoEntries, contextTimeInfoEntries, changes, removals); + + changes.clear(); + removals.clear(); + + return true; + } + + return false; + } + + public watch( + files: Iterable, + directories: Iterable, + missing: Iterable, + startTime: number, + options: WatchOptions, + callback: WatchCallback, + callbackUndelayed: WatchUndelayedCallback + ): Watcher { + const oldWatcher: Watchpack | undefined = this.watcher; + this.watcher = new Watchpack(options); + + const changes: Set = new Set(); + const removals: Set = new Set(); + + this._state = { + changes, + removals, + + callback + }; + + this.watcher.on('aggregated', (newChanges: Set, newRemovals: Set) => { + for (const change of newChanges) { + removals.delete(change); + changes.add(change); + } + for (const removal of newRemovals) { + changes.delete(removal); + removals.add(removal); + } + + this._onChange(); + }); + + this.watcher.watch({ + files, + directories, + missing, + startTime + }); + + if (oldWatcher) { + oldWatcher.close(); + } + + return { + close: () => { + if (this.watcher) { + this.watcher.close(); + this.watcher = undefined; + } + }, + pause: () => { + if (this.watcher) { + this.watcher.pause(); + } + }, + getInfo: () => { + const newRemovals: Set | undefined = this.watcher?.aggregatedRemovals; + const newChanges: Set | undefined = this.watcher?.aggregatedChanges; + this._purge(newRemovals, newChanges); + const { fileTimeInfoEntries, contextTimeInfoEntries } = this._fetchTimeInfo(); + return { + changes: newChanges!, + removals: newRemovals!, + fileTimeInfoEntries, + contextTimeInfoEntries + }; + }, + getContextTimeInfoEntries: () => { + const { contextTimeInfoEntries } = this._fetchTimeInfo(); + return contextTimeInfoEntries; + }, + getFileTimeInfoEntries: () => { + const { fileTimeInfoEntries } = this._fetchTimeInfo(); + return fileTimeInfoEntries; + } + }; + } + + private _fetchTimeInfo(): ITimeInfoEntries { + const fileTimeInfoEntries: IRawFileSystemMap = new Map(); + const contextTimeInfoEntries: IRawFileSystemMap = new Map(); + this.watcher?.collectTimeInfoEntries(fileTimeInfoEntries, contextTimeInfoEntries); + return { fileTimeInfoEntries, contextTimeInfoEntries }; + } + + private _purge(removals: Set | undefined, changes: Set | undefined): void { + const fs: InputFileSystem = this.inputFileSystem; + if (fs.purge) { + if (removals) { + for (const removal of removals) { + fs.purge(removal); + } + } + if (changes) { + for (const change of changes) { + fs.purge(change); + } + } + } + } +} + +export class OverrideNodeWatchFSPlugin implements RspackPluginInstance { + public readonly fileSystems: Set = new Set(); + private readonly _onChange: () => void; + + public constructor(onChange: () => void) { + this._onChange = onChange; + } + + public apply(compiler: Compiler): void { + const { inputFileSystem } = compiler; + if (!inputFileSystem) { + throw new Error(`compiler.inputFileSystem is not defined`); + } + + const watchFileSystem: DeferredWatchFileSystem = new DeferredWatchFileSystem( + inputFileSystem, + this._onChange + ); + this.fileSystems.add(watchFileSystem); + compiler.watchFileSystem = watchFileSystem; + } +} diff --git a/heft-plugins/heft-rspack-plugin/src/RspackConfigurationLoader.ts b/heft-plugins/heft-rspack-plugin/src/RspackConfigurationLoader.ts new file mode 100644 index 00000000000..98a6d37fdcf --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/src/RspackConfigurationLoader.ts @@ -0,0 +1,185 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import { pathToFileURL } from 'node:url'; + +import type * as TRspack from '@rspack/core'; + +import type { HeftConfiguration, IHeftTaskSession } from '@rushstack/heft'; +import { FileSystem } from '@rushstack/node-core-library'; + +import type { IRspackPluginOptions } from './RspackPlugin'; +import { + type IRspackConfiguration, + type IRspackConfigurationFnEnvironment, + type IRspackPluginAccessorHooks, + PLUGIN_NAME, + STAGE_LOAD_LOCAL_CONFIG +} from './shared'; + +type IRspackConfigJsExport = + | TRspack.Configuration + | TRspack.Configuration[] + | Promise + | Promise + | ((env: IRspackConfigurationFnEnvironment) => TRspack.Configuration | TRspack.Configuration[]) + | ((env: IRspackConfigurationFnEnvironment) => Promise); +type IRspackConfigJs = IRspackConfigJsExport | { default: IRspackConfigJsExport }; + +/** + * @internal + */ +export interface ILoadRspackConfigurationOptions { + taskSession: IHeftTaskSession; + heftConfiguration: HeftConfiguration; + serveMode: boolean; + loadRspackAsyncFn: () => Promise; + hooks: Pick; + + _tryLoadConfigFileAsync?: typeof tryLoadRspackConfigurationFileAsync; +} + +const DEFAULT_RSPACK_CONFIG_PATH: './rspack.config.mjs' = './rspack.config.mjs'; +const DEFAULT_RSPACK_DEV_CONFIG_PATH: './rspack.dev.config.js' = './rspack.dev.config.js'; + +/** + * @internal + */ +export async function tryLoadRspackConfigurationAsync( + options: ILoadRspackConfigurationOptions, + pluginOptions: IRspackPluginOptions +): Promise { + const { taskSession, hooks, _tryLoadConfigFileAsync = tryLoadRspackConfigurationFileAsync } = options; + const { logger } = taskSession; + const { terminal } = logger; + + // Apply default behavior. Due to the state of `this._rspackConfiguration`, this code + // will execute exactly once. + hooks.onLoadConfiguration.tapPromise( + { + name: PLUGIN_NAME, + stage: STAGE_LOAD_LOCAL_CONFIG + }, + async () => { + terminal.writeVerboseLine(`Attempting to load Rspack configuration from local file`); + const rspackConfiguration: IRspackConfiguration | undefined = await _tryLoadConfigFileAsync( + options, + pluginOptions + ); + + if (rspackConfiguration) { + terminal.writeVerboseLine(`Loaded Rspack configuration from local file.`); + } + + return rspackConfiguration; + } + ); + + // Obtain the Rspack configuration by calling into the hook. + // The local configuration is loaded at STAGE_LOAD_LOCAL_CONFIG + terminal.writeVerboseLine('Attempting to load Rspack configuration'); + let rspackConfiguration: IRspackConfiguration | false | undefined = + await hooks.onLoadConfiguration.promise(); + + if (rspackConfiguration === false) { + terminal.writeLine('Rspack disabled by external plugin'); + rspackConfiguration = undefined; + } else if ( + rspackConfiguration === undefined || + (Array.isArray(rspackConfiguration) && rspackConfiguration.length === 0) + ) { + terminal.writeLine('No Rspack configuration found'); + rspackConfiguration = undefined; + } else { + if (hooks.onConfigure.isUsed()) { + // Allow for plugins to customize the configuration + await hooks.onConfigure.promise(rspackConfiguration); + } + if (hooks.onAfterConfigure.isUsed()) { + // Provide the finalized configuration + await hooks.onAfterConfigure.promise(rspackConfiguration); + } + } + return rspackConfiguration as IRspackConfiguration | undefined; +} + +/** + * @internal + */ +export async function tryLoadRspackConfigurationFileAsync( + options: ILoadRspackConfigurationOptions, + pluginOptions: IRspackPluginOptions +): Promise { + const { taskSession, heftConfiguration, loadRspackAsyncFn, serveMode } = options; + const { + logger, + parameters: { production } + } = taskSession; + const { terminal } = logger; + const { configurationPath, devConfigurationPath } = pluginOptions; + let rspackConfigJs: IRspackConfigJs | undefined; + + try { + const buildFolderPath: string = heftConfiguration.buildFolderPath; + if (serveMode) { + const devConfigPath: string = path.resolve( + buildFolderPath, + devConfigurationPath || DEFAULT_RSPACK_DEV_CONFIG_PATH + ); + terminal.writeVerboseLine(`Attempting to load rspack configuration from "${devConfigPath}".`); + rspackConfigJs = await _tryLoadRspackConfigurationFileInnerAsync(devConfigPath); + } + + if (!rspackConfigJs) { + const configPath: string = path.resolve( + buildFolderPath, + configurationPath || DEFAULT_RSPACK_CONFIG_PATH + ); + terminal.writeVerboseLine(`Attempting to load rspack configuration from "${configPath}".`); + rspackConfigJs = await _tryLoadRspackConfigurationFileInnerAsync(configPath); + } + } catch (error) { + logger.emitError(error as Error); + } + + if (rspackConfigJs) { + const rspackConfig: IRspackConfigJsExport = + (rspackConfigJs as { default: IRspackConfigJsExport }).default || + (rspackConfigJs as IRspackConfigJsExport); + + if (typeof rspackConfig === 'function') { + // Defer loading of rspack until we know for sure that we will need it + return rspackConfig({ + prod: production, + production, + taskSession, + heftConfiguration, + rspack: await loadRspackAsyncFn() + }); + } else { + return rspackConfig; + } + } else { + return undefined; + } +} + +/** + * @internal + */ +export async function _tryLoadRspackConfigurationFileInnerAsync( + configurationPath: string +): Promise { + const configExists: boolean = await FileSystem.existsAsync(configurationPath); + if (configExists) { + try { + const configurationUri: string = pathToFileURL(configurationPath).href; + return await import(configurationUri); + } catch (e) { + throw new Error(`Error loading Rspack configuration at "${configurationPath}": ${e}`); + } + } else { + return undefined; + } +} diff --git a/heft-plugins/heft-rspack-plugin/src/RspackPlugin.ts b/heft-plugins/heft-rspack-plugin/src/RspackPlugin.ts new file mode 100644 index 00000000000..67546a3e345 --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/src/RspackPlugin.ts @@ -0,0 +1,538 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { AddressInfo } from 'node:net'; + +import type * as TRspack from '@rspack/core'; +import type * as TRspackDevServer from '@rspack/dev-server'; +import { AsyncParallelHook, AsyncSeriesBailHook, AsyncSeriesHook, AsyncSeriesWaterfallHook } from 'tapable'; + +import { CertificateManager, type ICertificate } from '@rushstack/debug-certificate-manager'; +import { FileError, InternalError, LegacyAdapters } from '@rushstack/node-core-library'; +import type { + HeftConfiguration, + IHeftTaskSession, + IHeftTaskPlugin, + IHeftTaskRunHookOptions, + IScopedLogger, + IHeftTaskRunIncrementalHookOptions +} from '@rushstack/heft'; + +import { + type IRspackConfiguration, + type IRspackPluginAccessor, + PLUGIN_NAME, + type IRspackPluginAccessorHooks, + type RspackCoreImport +} from './shared'; +import { tryLoadRspackConfigurationAsync } from './RspackConfigurationLoader'; +import { type DeferredWatchFileSystem, OverrideNodeWatchFSPlugin } from './DeferredWatchFileSystem'; + +export interface IRspackPluginOptions { + devConfigurationPath?: string | undefined; + configurationPath?: string | undefined; +} +const SERVE_PARAMETER_LONG_NAME: '--serve' = '--serve'; +const RSPACK_PACKAGE_NAME: '@rspack/core' = '@rspack/core'; +const RSPACK_DEV_SERVER_PACKAGE_NAME: '@rspack/dev-server' = '@rspack/dev-server'; +const RSPACK_DEV_SERVER_ENV_VAR_NAME: 'RSPACK_DEV_SERVER' = 'RSPACK_DEV_SERVER'; +const WEBPACK_DEV_MIDDLEWARE_PACKAGE_NAME: 'webpack-dev-middleware' = 'webpack-dev-middleware'; + +/** + * @internal + */ +export default class RspackPlugin implements IHeftTaskPlugin { + private _accessor: IRspackPluginAccessor | undefined; + private _isServeMode: boolean = false; + private _rspack: RspackCoreImport | undefined; + private _rspackCompiler: TRspack.Compiler | TRspack.MultiCompiler | undefined; + private _rspackConfiguration: IRspackConfiguration | undefined | false = false; + private _rspackCompilationDonePromise: Promise | undefined; + private _rspackCompilationDonePromiseResolveFn: (() => void) | undefined; + private _watchFileSystems: Set | undefined; + + private _warnings: Error[] = []; + private _errors: Error[] = []; + + public get accessor(): IRspackPluginAccessor { + if (!this._accessor) { + this._accessor = { + hooks: _createAccessorHooks(), + parameters: { + isServeMode: this._isServeMode + } + }; + } + return this._accessor; + } + + public apply( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + options: IRspackPluginOptions = {} + ): void { + this._isServeMode = taskSession.parameters.getFlagParameter(SERVE_PARAMETER_LONG_NAME).value; + if (this._isServeMode && !taskSession.parameters.watch) { + throw new Error( + `The ${JSON.stringify( + SERVE_PARAMETER_LONG_NAME + )} parameter is only available when running in watch mode.` + + ` Try replacing "${taskSession.parsedCommandLine?.unaliasedCommandName}" with` + + ` "${taskSession.parsedCommandLine?.unaliasedCommandName}-watch" in your Heft command line.` + ); + } + + taskSession.hooks.run.tapPromise(PLUGIN_NAME, async (runOptions: IHeftTaskRunHookOptions) => { + await this._runRspackAsync(taskSession, heftConfiguration, options); + }); + + taskSession.hooks.runIncremental.tapPromise( + PLUGIN_NAME, + async (runOptions: IHeftTaskRunIncrementalHookOptions) => { + await this._runRspackWatchAsync(taskSession, heftConfiguration, options, runOptions.requestRun); + } + ); + } + + private async _getRspackConfigurationAsync( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + options: IRspackPluginOptions, + requestRun?: () => void + ): Promise { + if (this._rspackConfiguration === false) { + const rspackConfiguration: IRspackConfiguration | undefined = await tryLoadRspackConfigurationAsync( + { + taskSession, + heftConfiguration, + hooks: this.accessor.hooks, + serveMode: this._isServeMode, + loadRspackAsyncFn: this._loadRspackAsync.bind(this, taskSession, heftConfiguration) + }, + options + ); + + if (rspackConfiguration && requestRun) { + const overrideWatchFSPlugin: OverrideNodeWatchFSPlugin = new OverrideNodeWatchFSPlugin(requestRun); + this._watchFileSystems = overrideWatchFSPlugin.fileSystems; + for (const config of Array.isArray(rspackConfiguration) + ? rspackConfiguration + : [rspackConfiguration]) { + if (!config.plugins) { + config.plugins = [overrideWatchFSPlugin]; + } else { + config.plugins.unshift(overrideWatchFSPlugin); + } + } + } + + this._rspackConfiguration = rspackConfiguration; + } + + return this._rspackConfiguration; + } + + private async _loadRspackAsync( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration + ): Promise { + if (!this._rspack) { + try { + const rspackPackagePath: string = await heftConfiguration.rigPackageResolver.resolvePackageAsync( + RSPACK_PACKAGE_NAME, + taskSession.logger.terminal + ); + this._rspack = await import(rspackPackagePath); + taskSession.logger.terminal.writeDebugLine(`Using Rspack from rig package at "${rspackPackagePath}"`); + } catch (e) { + // Fallback to bundled version if not found in rig. + this._rspack = await import(RSPACK_PACKAGE_NAME); + taskSession.logger.terminal.writeDebugLine(`Using Rspack from built-in "${RSPACK_PACKAGE_NAME}"`); + } + } + return this._rspack!; + } + + private async _getRspackCompilerAsync( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + rspackConfiguration: IRspackConfiguration + ): Promise { + if (!this._rspackCompiler) { + const rspack: RspackCoreImport = await this._loadRspackAsync(taskSession, heftConfiguration); + taskSession.logger.terminal.writeLine(`Using Rspack version ${rspack.version}`); + this._rspackCompiler = Array.isArray(rspackConfiguration) + ? rspack.default(rspackConfiguration) /* (rspack.Compilation[]) => MultiCompiler */ + : rspack.default(rspackConfiguration); /* (rspack.Compilation) => Compiler */ + } + return this._rspackCompiler; + } + + private async _runRspackAsync( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + options: IRspackPluginOptions + ): Promise { + this._validateEnvironmentVariable(taskSession); + if (taskSession.parameters.watch || this._isServeMode) { + // Should never happen, but just in case + throw new InternalError('Cannot run Rspack in compilation mode when watch mode is enabled'); + } + + // Load the config and compiler, and return if there is no config found + const rspackConfiguration: IRspackConfiguration | undefined = await this._getRspackConfigurationAsync( + taskSession, + heftConfiguration, + options + ); + if (!rspackConfiguration) { + return; + } + const compiler: TRspack.Compiler | TRspack.MultiCompiler = await this._getRspackCompilerAsync( + taskSession, + heftConfiguration, + rspackConfiguration + ); + taskSession.logger.terminal.writeLine('Running Rspack compilation'); + + // Run the rspack compiler + let stats: TRspack.Stats | TRspack.MultiStats | undefined; + try { + stats = await LegacyAdapters.convertCallbackToPromise( + (compiler as TRspack.Compiler).run.bind(compiler) + ); + await LegacyAdapters.convertCallbackToPromise(compiler.close.bind(compiler)); + } catch (e) { + taskSession.logger.emitError(e as Error); + } + + // Emit the errors from the stats object, if present + if (stats) { + this._recordErrors(stats, heftConfiguration.buildFolderPath); + this._emitErrors(taskSession.logger); + if (this.accessor.hooks.onEmitStats.isUsed()) { + await this.accessor.hooks.onEmitStats.promise(stats); + } + } + } + + private async _runRspackWatchAsync( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + options: IRspackPluginOptions, + requestRun: () => void + ): Promise { + // Save a handle to the original promise, since the this-scoped promise will be replaced whenever + // the compilation completes. + let rspackCompilationDonePromise: Promise | undefined = this._rspackCompilationDonePromise; + + let isInitial: boolean = false; + + if (!this._rspackCompiler) { + isInitial = true; + this._validateEnvironmentVariable(taskSession); + if (!taskSession.parameters.watch) { + // Should never happen, but just in case + throw new InternalError('Cannot run Rspack in watch mode when watch mode is not enabled'); + } + + // Load the config and compiler, and return if there is no config found + const rspackConfiguration: IRspackConfiguration | undefined = await this._getRspackConfigurationAsync( + taskSession, + heftConfiguration, + options, + requestRun + ); + if (!rspackConfiguration) { + return; + } + + // Get the compiler which will be used for both serve and watch mode + const compiler: TRspack.Compiler | TRspack.MultiCompiler = await this._getRspackCompilerAsync( + taskSession, + heftConfiguration, + rspackConfiguration + ); + + // Set up the hook to detect when the watcher completes the watcher compilation. We will also log out + // errors from the compilation if present from the output stats object. + this._rspackCompilationDonePromise = new Promise((resolve: () => void) => { + this._rspackCompilationDonePromiseResolveFn = resolve; + }); + rspackCompilationDonePromise = this._rspackCompilationDonePromise; + compiler.hooks.done.tap(PLUGIN_NAME, (stats?: TRspack.Stats | TRspack.MultiStats) => { + this._rspackCompilationDonePromiseResolveFn!(); + this._rspackCompilationDonePromise = new Promise((resolve: () => void) => { + this._rspackCompilationDonePromiseResolveFn = resolve; + }); + + if (stats) { + this._recordErrors(stats, heftConfiguration.buildFolderPath); + } + }); + + // Determine how we will run the compiler. When serving, we will run the compiler + // via the @rspack/dev-server. Otherwise, we will run the compiler directly. + if (this._isServeMode) { + const defaultDevServerOptions: TRspackDevServer.Configuration = { + host: 'localhost', + devMiddleware: { + publicPath: '/', + stats: { + cached: false, + cachedAssets: false, + colors: heftConfiguration.terminalProvider.supportsColor + } + }, + client: { + logging: 'info', + webSocketURL: { + port: 8080 + } + }, + watchFiles: [], + static: [], + port: 8080, + onListening: (server: TRspackDevServer.RspackDevServer) => { + const addressInfo: AddressInfo | string | undefined = server.server?.address() as AddressInfo; + if (addressInfo) { + let url: string; + if (typeof addressInfo === 'string') { + url = addressInfo; + } else { + const address: string = + addressInfo.family === 'IPv6' + ? `[${addressInfo.address}]:${addressInfo.port}` + : `${addressInfo.address}:${addressInfo.port}`; + url = `https://${address}/`; + } + taskSession.logger.terminal.writeLine(`Started Rspack Dev Server at ${url}`); + } + } + }; + + // Obtain the devServerOptions from the rspack configuration, and combine with the default options + let devServerOptions: TRspackDevServer.Configuration; + if (Array.isArray(rspackConfiguration)) { + const filteredDevServerOptions: TRspackDevServer.Configuration[] = rspackConfiguration + .map((configuration) => configuration.devServer) + .filter((devServer): devServer is TRspackDevServer.Configuration => !!devServer); + if (filteredDevServerOptions.length > 1) { + taskSession.logger.emitWarning( + new Error(`Detected multiple rspack devServer configurations, using the first one.`) + ); + } + devServerOptions = { ...defaultDevServerOptions, ...filteredDevServerOptions[0] }; + } else { + devServerOptions = { ...defaultDevServerOptions, ...rspackConfiguration.devServer }; + } + + // Add the certificate and key to the devServerOptions if these fields don't already have values + if (!devServerOptions.server) { + const certificateManager: CertificateManager = new CertificateManager(); + const certificate: ICertificate = await certificateManager.ensureCertificateAsync( + true, + taskSession.logger.terminal + ); + + // Update the web socket URL to use the hostname provided by the certificate + const clientConfiguration: TRspackDevServer.Configuration['client'] = devServerOptions.client; + const hostname: string | undefined = certificate.subjectAltNames?.[0]; + if (hostname && typeof clientConfiguration === 'object') { + const { webSocketURL } = clientConfiguration; + if (typeof webSocketURL === 'object') { + clientConfiguration.webSocketURL = { + ...webSocketURL, + hostname + }; + } + } + + devServerOptions = { + ...devServerOptions, + server: { + type: 'https', + options: { + minVersion: 'TLSv1.3', + key: certificate.pemKey, + cert: certificate.pemCertificate, + ca: certificate.pemCaCertificate + } + } + }; + } + + // Since the webpack-dev-server does not return infrastructure errors via a callback like + // compiler.watch(...), we will need to intercept them and log them ourselves. + // note: @rspack/dev-server extends webpack-dev-server and also has this behavior + compiler.hooks.infrastructureLog.tap( + PLUGIN_NAME, + (name: string, type: string, args: unknown[] | undefined) => { + if (name === WEBPACK_DEV_MIDDLEWARE_PACKAGE_NAME && type === 'error') { + const error: Error | undefined = args?.[0] as Error | undefined; + if (error) { + taskSession.logger.emitError(error); + } + } + } + ); + + // The webpack-dev-server package has a design flaw, where merely loading its package will set the + // WEBPACK_DEV_SERVER environment variable -- even if no APIs are accessed. This environment variable + // causes incorrect behavior if Heft is not running in serve mode. Thus, we need to be careful to call + // require() only if Heft is in serve mode. + // note: @rspack/dev-server extends webpack-dev-server and also has this behavior + taskSession.logger.terminal.writeLine('Starting rspack-dev-server'); + const RspackDevServer: typeof TRspackDevServer.RspackDevServer = ( + await import(RSPACK_DEV_SERVER_PACKAGE_NAME) + ).RspackDevServer; + const rspackDevServer: TRspackDevServer.RspackDevServer = new RspackDevServer( + devServerOptions, + compiler + ); + await rspackDevServer.start(); + } else { + // Create the watcher. Compilation will start immediately after invoking watch(). + taskSession.logger.terminal.writeLine('Starting Rspack watcher'); + + const { onGetWatchOptions } = this.accessor.hooks; + + const watchOptions: + | Parameters[0] + | Parameters[0] = onGetWatchOptions.isUsed() + ? await onGetWatchOptions.promise({}, rspackConfiguration) + : {}; + + (compiler as TRspack.Compiler).watch(watchOptions, (error?: Error | null) => { + if (error) { + taskSession.logger.emitError(error); + } + }); + } + } + + let hasChanges: boolean = true; + if (!isInitial && this._watchFileSystems) { + hasChanges = false; + for (const watchFileSystem of this._watchFileSystems) { + hasChanges = watchFileSystem.flush() || hasChanges; + } + } + + // Resume the compilation, wait for the compilation to complete, then suspend the watchers until the + // next iteration. Even if there are no changes, the promise should resolve since resuming from a + // suspended state invalidates the state of the watcher. + if (hasChanges) { + taskSession.logger.terminal.writeLine('Running incremental Rspack compilation'); + await rspackCompilationDonePromise; + } else { + taskSession.logger.terminal.writeLine( + 'Rspack has not detected changes. Listing previous diagnostics.' + ); + } + + this._emitErrors(taskSession.logger); + } + + private _validateEnvironmentVariable(taskSession: IHeftTaskSession): void { + if (!this._isServeMode && process.env[RSPACK_DEV_SERVER_ENV_VAR_NAME]) { + taskSession.logger.emitWarning( + new Error( + `The "${RSPACK_DEV_SERVER_ENV_VAR_NAME}" environment variable is set, ` + + 'which will cause problems when rspack is not running in serve mode. ' + + `(Did a dependency inadvertently load the "${RSPACK_DEV_SERVER_PACKAGE_NAME}" package?)` + ) + ); + } + } + + private _emitErrors(logger: IScopedLogger): void { + for (const warning of this._warnings) { + logger.emitWarning(warning); + } + for (const error of this._errors) { + logger.emitError(error); + } + } + + private _recordErrors(stats: TRspack.Stats | TRspack.MultiStats, buildFolderPath: string): void { + const errors: Error[] = this._errors; + const warnings: Error[] = this._warnings; + + errors.length = 0; + warnings.length = 0; + + if (stats.hasErrors() || stats.hasWarnings()) { + const serializedStats: TRspack.StatsCompilation[] = [stats.toJson('errors-warnings')]; + + for (const compilationStats of serializedStats) { + if (compilationStats.warnings) { + for (const warning of compilationStats.warnings) { + warnings.push(this._normalizeError(buildFolderPath, warning)); + } + } + + if (compilationStats.errors) { + for (const error of compilationStats.errors) { + errors.push(this._normalizeError(buildFolderPath, error)); + } + } + + if (compilationStats.children) { + for (const child of compilationStats.children) { + serializedStats.push(child); + } + } + } + } + } + + private _normalizeError(buildFolderPath: string, error: TRspack.StatsError): Error { + if (error instanceof Error) { + return error; + } else if (error.moduleIdentifier) { + let lineNumber: number | undefined; + let columnNumber: number | undefined; + if (error.loc) { + // Format of ":-" + // https://webpack.js.org/api/stats/#errors-and-warnings + const [lineNumberRaw, columnRangeRaw] = error.loc.split(':'); + const [startColumnRaw] = columnRangeRaw.split('-'); + if (lineNumberRaw) { + lineNumber = parseInt(lineNumberRaw, 10); + if (Number.isNaN(lineNumber)) { + lineNumber = undefined; + } + } + if (startColumnRaw) { + columnNumber = parseInt(startColumnRaw, 10); + if (Number.isNaN(columnNumber)) { + columnNumber = undefined; + } + } + } + + return new FileError(error.message, { + absolutePath: error.moduleIdentifier, + projectFolder: buildFolderPath, + line: lineNumber, + column: columnNumber + }); + } else { + return new Error(error.message); + } + } +} + +/** + * @internal + */ +export function _createAccessorHooks(): IRspackPluginAccessorHooks { + return { + onLoadConfiguration: new AsyncSeriesBailHook(), + onConfigure: new AsyncSeriesHook(['rspackConfiguration']), + onAfterConfigure: new AsyncParallelHook(['rspackConfiguration']), + onEmitStats: new AsyncParallelHook(['rspackStats']), + onGetWatchOptions: new AsyncSeriesWaterfallHook(['watchOptions', 'rspackConfiguration']) + }; +} diff --git a/heft-plugins/heft-rspack-plugin/src/index.ts b/heft-plugins/heft-rspack-plugin/src/index.ts new file mode 100644 index 00000000000..0246643b10d --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/src/index.ts @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * HeftRspackPlugin is a Heft plugin that integrates the Rspack bundler into the Heft build process. + * + * @packageDocumentation + */ + +export { PLUGIN_NAME as PluginName, STAGE_LOAD_LOCAL_CONFIG } from './shared'; + +export type { + IRspackConfigurationWithDevServer, + IRspackConfiguration, + IRspackConfigurationFnEnvironment, + IRspackPluginAccessor, + IRspackPluginAccessorHooks, + IRspackPluginAccessorParameters, + RspackCoreImport +} from './shared'; diff --git a/heft-plugins/heft-rspack-plugin/src/schemas/heft-rspack-plugin.schema.json b/heft-plugins/heft-rspack-plugin/src/schemas/heft-rspack-plugin.schema.json new file mode 100644 index 00000000000..3a488443547 --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/src/schemas/heft-rspack-plugin.schema.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Rspack Plugin Configuration", + "description": "Defines options for Rspack plugin execution.", + "type": "object", + + "additionalProperties": false, + + "properties": { + "$schema": { + "description": "Part of the JSON Schema standard, this optional keyword declares the URL of the schema that the file conforms to. Editors may download the schema and use it to perform syntax highlighting.", + "type": "string" + }, + + "devConfigurationPath": { + "description": "Specifies a relative path to the Rspack dev configuration, which is used in \"serve\" mode. The default value is \"./rspack.dev.config.js\".", + "type": "string" + }, + + "configurationPath": { + "description": "Specifies a relative path to the Rspack configuration. The default value is \"./rspack.config.js\".", + "type": "string" + } + } +} diff --git a/heft-plugins/heft-rspack-plugin/src/shared.ts b/heft-plugins/heft-rspack-plugin/src/shared.ts new file mode 100644 index 00000000000..dde089b2141 --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/src/shared.ts @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type * as TRspack from '@rspack/core'; +import type * as TRspackDevServer from '@rspack/dev-server'; +import type { + AsyncParallelHook, + AsyncSeriesBailHook, + AsyncSeriesHook, + AsyncSeriesWaterfallHook +} from 'tapable'; + +import type { HeftConfiguration, IHeftTaskSession } from '@rushstack/heft'; + +/** + * @beta + */ +export type RspackCoreImport = typeof import('@rspack/core'); + +/** + * The environment passed into the Rspack configuration function. Loosely based + * on the default Rspack environment options, specified here: + * https://rspack.rs/plugins/webpack/environment-plugin#options + * + * @beta + */ +export interface IRspackConfigurationFnEnvironment { + /** + * Whether or not the run is in production mode. Synonym of + * {@link IRspackConfigurationFnEnvironment.production}. + */ + prod: boolean; + /** + * Whether or not the run is in production mode. Synonym of + * {@link IRspackConfigurationFnEnvironment.prod}. + */ + production: boolean; + + // Non-standard environment options + /** + * The task session provided to the plugin. + */ + taskSession: IHeftTaskSession; + /** + * The Heft configuration provided to the plugin. + */ + heftConfiguration: HeftConfiguration; + /** + * The resolved Rspack package. + */ + rspack: RspackCoreImport; +} + +/** + * @beta + */ +export interface IRspackConfigurationWithDevServer extends TRspack.Configuration { + devServer?: TRspackDevServer.Configuration; +} + +/** + * @beta + */ +export type IRspackConfiguration = TRspack.Configuration | TRspack.Configuration[]; + +/** + * @beta + */ +export interface IRspackPluginAccessorHooks { + /** + * A hook that allows for loading custom configurations used by the Rspack + * plugin. If a tap returns a value other than `undefined` before stage {@link STAGE_LOAD_LOCAL_CONFIG}, + * it will suppress loading from the Rspack config file. To provide a fallback behavior in the + * absence of a local config file, tap this hook with a `stage` value greater than {@link STAGE_LOAD_LOCAL_CONFIG}. + * + * @remarks + * Tapable event handlers can return `false` instead of `undefined` to suppress + * other handlers from creating a configuration object, and prevent Rspack from running. + */ + readonly onLoadConfiguration: AsyncSeriesBailHook<[], IRspackConfiguration | undefined | false>; + /** + * A hook that allows for modification of the loaded configuration used by the Rspack + * plugin. If no configuration was loaded, this hook will not be called. + */ + readonly onConfigure: AsyncSeriesHook<[IRspackConfiguration], never>; + /** + * A hook that provides the finalized configuration that will be used by Rspack. + * If no configuration was loaded, this hook will not be called. + */ + readonly onAfterConfigure: AsyncParallelHook<[IRspackConfiguration], never>; + /** + * A hook that provides the stats output from Rspack. If no configuration is loaded, + * this hook will not be called. + */ + readonly onEmitStats: AsyncParallelHook<[TRspack.Stats | TRspack.MultiStats], never>; + /** + * A hook that allows for customization of the file watcher options. If not running in watch mode, this hook will not be called. + */ + readonly onGetWatchOptions: AsyncSeriesWaterfallHook< + [Parameters[0], Readonly], + never + >; +} + +/** + * @beta + */ +export interface IRspackPluginAccessorParameters { + /** + * Whether or not serve mode was enabled by passing the `--serve` flag. + */ + readonly isServeMode: boolean; +} + +/** + * @beta + */ +export interface IRspackPluginAccessor { + /** + * Hooks that are called at various points in the Rspack plugin lifecycle. + */ + readonly hooks: IRspackPluginAccessorHooks; + /** + * Parameters that are provided by the Rspack plugin. + */ + readonly parameters: IRspackPluginAccessorParameters; +} + +/** + * The stage in the `onLoadConfiguration` hook at which the config will be loaded from the local + * rspack config file. + * @beta + */ +export const STAGE_LOAD_LOCAL_CONFIG: 1000 = 1000; + +/** + * @beta + */ +export const PLUGIN_NAME: 'rspack-plugin' = 'rspack-plugin'; diff --git a/heft-plugins/heft-rspack-plugin/tsconfig.json b/heft-plugins/heft-rspack-plugin/tsconfig.json new file mode 100644 index 00000000000..e64ab1d2405 --- /dev/null +++ b/heft-plugins/heft-rspack-plugin/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", + "compilerOptions": { + "lib": [ + "DOM" + ], + "module": "nodenext", + "moduleResolution": "nodenext" + } +} \ No newline at end of file diff --git a/heft-plugins/heft-sass-load-themed-styles-plugin/.npmignore b/heft-plugins/heft-sass-load-themed-styles-plugin/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/heft-plugins/heft-sass-load-themed-styles-plugin/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-sass-load-themed-styles-plugin/CHANGELOG.json b/heft-plugins/heft-sass-load-themed-styles-plugin/CHANGELOG.json new file mode 100644 index 00000000000..7b4518c76bb --- /dev/null +++ b/heft-plugins/heft-sass-load-themed-styles-plugin/CHANGELOG.json @@ -0,0 +1,1227 @@ +{ + "name": "@rushstack/heft-sass-load-themed-styles-plugin", + "entries": [ + { + "version": "1.2.27", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.27", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.4.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "1.2.26", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.26", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.4.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "1.2.25", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.25", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.4.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "1.2.24", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.24", + "date": "Sat, 13 Jun 2026 00:16:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.4.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "1.2.23", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.23", + "date": "Mon, 08 Jun 2026 15:15:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.4.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "1.2.22", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.22", + "date": "Tue, 02 Jun 2026 07:30:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.4.1`" + } + ] + } + }, + { + "version": "1.2.21", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.21", + "date": "Fri, 29 May 2026 18:25:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.4.0`" + } + ] + } + }, + { + "version": "1.2.20", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.20", + "date": "Thu, 30 Apr 2026 00:15:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.3.8`" + } + ] + } + }, + { + "version": "1.2.19", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.19", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "1.2.18", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.18", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "1.2.17", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.17", + "date": "Sat, 18 Apr 2026 03:47:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "1.2.16", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.16", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "1.2.15", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.15", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "1.2.14", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.14", + "date": "Sat, 11 Apr 2026 00:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.3.2`" + } + ] + } + }, + { + "version": "1.2.13", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.13", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "1.2.12", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.12", + "date": "Thu, 09 Apr 2026 23:00:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.3.0`" + } + ] + } + }, + { + "version": "1.2.11", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "1.2.10", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "1.2.9", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "1.2.8", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "1.2.7", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "1.2.6", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "1.2.5", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "1.2.4", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "1.2.3", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "1.2.2", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "1.2.1", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "1.2.0", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.2.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "1.1.14", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "1.1.13", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "1.1.12", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "1.1.11", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "1.1.10", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.10", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "1.1.9", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.9", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "1.1.8", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.8", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "1.1.7", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "1.1.6", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.6", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.5` to `1.1.6`" + } + ] + } + }, + { + "version": "1.1.5", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.4` to `1.1.5`" + } + ] + } + }, + { + "version": "1.1.4", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.4", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.3` to `1.1.4`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.2` to `1.1.3`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.1` to `1.1.2`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.0` to `1.1.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.1.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.0.0` to `1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.75.0` to `1.0.0`" + } + ] + } + }, + { + "version": "0.1.15", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.15", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.5` to `0.75.0`" + } + ] + } + }, + { + "version": "0.1.14", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.14", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.4` to `0.74.5`" + } + ] + } + }, + { + "version": "0.1.13", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.13", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.3` to `0.74.4`" + } + ] + } + }, + { + "version": "0.1.12", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.12", + "date": "Sun, 31 Aug 2025 02:24:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.12`" + } + ] + } + }, + { + "version": "0.1.11", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.11", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.2` to `0.74.3`" + } + ] + } + }, + { + "version": "0.1.10", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.10", + "date": "Fri, 01 Aug 2025 00:12:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.1` to `0.74.2`" + } + ] + } + }, + { + "version": "0.1.9", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.9", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.0` to `0.74.1`" + } + ] + } + }, + { + "version": "0.1.8", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.8", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.6` to `0.74.0`" + } + ] + } + }, + { + "version": "0.1.7", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.7", + "date": "Thu, 15 May 2025 00:11:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.7`" + } + ] + } + }, + { + "version": "0.1.6", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.6", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.5` to `0.73.6`" + } + ] + } + }, + { + "version": "0.1.5", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.5", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.4` to `0.73.5`" + } + ] + } + }, + { + "version": "0.1.4", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.4", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.3` to `0.73.4`" + } + ] + } + }, + { + "version": "0.1.3", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.3", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.2` to `0.73.3`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.2", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.1` to `0.73.2`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.1", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.0` to `0.73.1`" + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/heft-sass-load-themed-styles-plugin_v0.1.0", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "minor": [ + { + "comment": "Add new plugin for converting load-themed-styles theme tokens to CSS variable references during heft-sass-plugin." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/load-themed-styles\" to `2.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-sass-plugin\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.1` to `0.73.0`" + } + ] + } + } + ] +} diff --git a/heft-plugins/heft-sass-load-themed-styles-plugin/CHANGELOG.md b/heft-plugins/heft-sass-load-themed-styles-plugin/CHANGELOG.md new file mode 100644 index 00000000000..9eb86f427b2 --- /dev/null +++ b/heft-plugins/heft-sass-load-themed-styles-plugin/CHANGELOG.md @@ -0,0 +1,314 @@ +# Change Log - @rushstack/heft-sass-load-themed-styles-plugin + +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 1.2.27 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 1.2.26 +Fri, 17 Jul 2026 00:15:59 GMT + +_Version update only_ + +## 1.2.25 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.2.24 +Sat, 13 Jun 2026 00:16:18 GMT + +_Version update only_ + +## 1.2.23 +Mon, 08 Jun 2026 15:15:49 GMT + +_Version update only_ + +## 1.2.22 +Tue, 02 Jun 2026 07:30:20 GMT + +_Version update only_ + +## 1.2.21 +Fri, 29 May 2026 18:25:51 GMT + +_Version update only_ + +## 1.2.20 +Thu, 30 Apr 2026 00:15:22 GMT + +_Version update only_ + +## 1.2.19 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.2.18 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.2.17 +Sat, 18 Apr 2026 03:47:09 GMT + +_Version update only_ + +## 1.2.16 +Sat, 18 Apr 2026 00:15:16 GMT + +_Version update only_ + +## 1.2.15 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.2.14 +Sat, 11 Apr 2026 00:31:13 GMT + +_Version update only_ + +## 1.2.13 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.2.12 +Thu, 09 Apr 2026 23:00:20 GMT + +_Version update only_ + +## 1.2.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.2.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.2.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.2.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.2.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.2.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.2.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 1.2.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.2.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.2.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.2.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.2.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.1.14 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.1.13 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.1.12 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.1.11 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.1.10 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.1.9 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 1.1.8 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 1.1.7 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.1.6 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.1.5 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.1.4 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 1.1.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.1.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.1.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 1.1.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.1.15 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.1.14 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.1.13 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.1.12 +Sun, 31 Aug 2025 02:24:40 GMT + +_Version update only_ + +## 0.1.11 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.1.10 +Fri, 01 Aug 2025 00:12:48 GMT + +_Version update only_ + +## 0.1.9 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.1.8 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.1.7 +Thu, 15 May 2025 00:11:49 GMT + +_Version update only_ + +## 0.1.6 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.1.5 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.1.4 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.1.3 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.1.2 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.1.1 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.1.0 +Tue, 15 Apr 2025 15:11:57 GMT + +### Minor changes + +- Add new plugin for converting load-themed-styles theme tokens to CSS variable references during heft-sass-plugin. + diff --git a/heft-plugins/heft-sass-load-themed-styles-plugin/LICENSE b/heft-plugins/heft-sass-load-themed-styles-plugin/LICENSE new file mode 100644 index 00000000000..553367172fe --- /dev/null +++ b/heft-plugins/heft-sass-load-themed-styles-plugin/LICENSE @@ -0,0 +1,24 @@ +@rushstack/heft-sass-load-themed-styles-plugin + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/heft-plugins/heft-sass-load-themed-styles-plugin/README.md b/heft-plugins/heft-sass-load-themed-styles-plugin/README.md new file mode 100644 index 00000000000..65c41b9e4e5 --- /dev/null +++ b/heft-plugins/heft-sass-load-themed-styles-plugin/README.md @@ -0,0 +1,28 @@ +# @rushstack/heft-sass-load-themed-styles-plugin + +This is a Heft plugin to augment SASS processing with functionality to replace load-themed-styles theme expressions of the form +``` +[theme:, default:] +``` +e.g. +``` +[theme:someColor, default:#fc0] +``` + +With css variable references of the form: +```css +var(--, ) +``` +e.g. +```css +var(--someColor, #fc0>) +``` + + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/heft-plugins/heft-sass-load-themed-styles-plugin/CHANGELOG.md) - Find + out what's new in the latest version + +Heft is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/heft-plugins/heft-sass-load-themed-styles-plugin/config/rig.json b/heft-plugins/heft-sass-load-themed-styles-plugin/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/heft-plugins/heft-sass-load-themed-styles-plugin/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/heft-plugins/heft-sass-load-themed-styles-plugin/eslint.config.js b/heft-plugins/heft-sass-load-themed-styles-plugin/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/heft-plugins/heft-sass-load-themed-styles-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-sass-load-themed-styles-plugin/heft-plugin.json b/heft-plugins/heft-sass-load-themed-styles-plugin/heft-plugin.json new file mode 100644 index 00000000000..ad7de05949d --- /dev/null +++ b/heft-plugins/heft-sass-load-themed-styles-plugin/heft-plugin.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", + + "taskPlugins": [ + { + "pluginName": "sass-load-themed-styles-plugin", + "entryPoint": "./lib-commonjs/SassLoadThemedStylesPlugin.js" + } + ] +} diff --git a/heft-plugins/heft-sass-load-themed-styles-plugin/package.json b/heft-plugins/heft-sass-load-themed-styles-plugin/package.json new file mode 100644 index 00000000000..b4594494fb6 --- /dev/null +++ b/heft-plugins/heft-sass-load-themed-styles-plugin/package.json @@ -0,0 +1,48 @@ +{ + "name": "@rushstack/heft-sass-load-themed-styles-plugin", + "version": "1.2.27", + "description": "Heft plugin that connects to heft-sass-plugin and replaces load-themed-styles theme expressions with standard CSS variables", + "repository": { + "type": "git", + "url": "https://github.com/microsoft/rushstack.git", + "directory": "heft-plugins/heft-sass-load-themed-styles-plugin" + }, + "homepage": "https://rushstack.io/pages/heft/overview/", + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "start": "heft test --clean --watch", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "peerDependencies": { + "@rushstack/heft": "^1.2.22" + }, + "dependencies": { + "@microsoft/load-themed-styles": "workspace:*" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "@rushstack/heft-sass-plugin": "workspace:*", + "local-node-rig": "workspace:*", + "eslint": "~9.37.0" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "sideEffects": false +} diff --git a/heft-plugins/heft-sass-load-themed-styles-plugin/src/SassLoadThemedStylesPlugin.ts b/heft-plugins/heft-sass-load-themed-styles-plugin/src/SassLoadThemedStylesPlugin.ts new file mode 100644 index 00000000000..0111e2580fa --- /dev/null +++ b/heft-plugins/heft-sass-load-themed-styles-plugin/src/SassLoadThemedStylesPlugin.ts @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { HeftConfiguration, IHeftTaskPlugin, IHeftTaskSession } from '@rushstack/heft'; +import type { SassPluginName, ISassPluginAccessor } from '@rushstack/heft-sass-plugin'; +import { replaceTokensWithVariables } from '@microsoft/load-themed-styles'; + +const PLUGIN_NAME: 'sass-load-themed-styles-plugin' = 'sass-load-themed-styles-plugin'; +const SASS_PLUGIN_NAME: typeof SassPluginName = 'sass-plugin'; + +export default class SassLoadThemedStylesPlugin implements IHeftTaskPlugin { + public apply(heftSession: IHeftTaskSession, heftConfiguration: HeftConfiguration): void { + heftSession.requestAccessToPluginByName( + '@rushstack/heft-sass-plugin', + SASS_PLUGIN_NAME, + (accessor: ISassPluginAccessor) => { + accessor.hooks.postProcessCss.tap(PLUGIN_NAME, (cssText: string) => { + return replaceTokensWithVariables(cssText); + }); + } + ); + } +} diff --git a/heft-plugins/heft-sass-load-themed-styles-plugin/tsconfig.json b/heft-plugins/heft-sass-load-themed-styles-plugin/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/heft-plugins/heft-sass-load-themed-styles-plugin/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/heft-plugins/heft-sass-plugin/.eslintrc.js b/heft-plugins/heft-sass-plugin/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/heft-plugins/heft-sass-plugin/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/heft-plugins/heft-sass-plugin/.npmignore b/heft-plugins/heft-sass-plugin/.npmignore index 768cd348769..f7a40e10213 100644 --- a/heft-plugins/heft-sass-plugin/.npmignore +++ b/heft-plugins/heft-sass-plugin/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,17 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) -!heft-plugin.json \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-sass-plugin/CHANGELOG.json b/heft-plugins/heft-sass-plugin/CHANGELOG.json index 88f1a2d7c9c..87f621b411a 100644 --- a/heft-plugins/heft-sass-plugin/CHANGELOG.json +++ b/heft-plugins/heft-sass-plugin/CHANGELOG.json @@ -1,6 +1,4015 @@ { "name": "@rushstack/heft-sass-plugin", "entries": [ + { + "version": "1.4.6", + "tag": "@rushstack/heft-sass-plugin_v1.4.6", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "1.4.5", + "tag": "@rushstack/heft-sass-plugin_v1.4.5", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "1.4.4", + "tag": "@rushstack/heft-sass-plugin_v1.4.4", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "1.4.3", + "tag": "@rushstack/heft-sass-plugin_v1.4.3", + "date": "Sat, 13 Jun 2026 00:16:18 GMT", + "comments": { + "patch": [ + { + "comment": "Fix a regression where plain `.css` files referenced from another stylesheet via `@use`/`@import` could not be resolved by the importer." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "1.4.2", + "tag": "@rushstack/heft-sass-plugin_v1.4.2", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "1.4.1", + "tag": "@rushstack/heft-sass-plugin_v1.4.1", + "date": "Tue, 02 Jun 2026 07:30:20 GMT", + "comments": { + "patch": [ + { + "comment": "Fix sourceMap: true crashing on Linux/macOS when compiled .scss files use @use or @import" + } + ] + } + }, + { + "version": "1.4.0", + "tag": "@rushstack/heft-sass-plugin_v1.4.0", + "date": "Fri, 29 May 2026 18:25:51 GMT", + "comments": { + "minor": [ + { + "comment": "Add opt-in sourceMap option to emit `.css.map` files and `sourceMappingURL` comments alongside compiled CSS." + } + ] + } + }, + { + "version": "1.3.8", + "tag": "@rushstack/heft-sass-plugin_v1.3.8", + "date": "Thu, 30 Apr 2026 00:15:22 GMT", + "comments": { + "patch": [ + { + "comment": "Bump postcss@~8.5.10 to address CVE GHSA-qx2v-qp2m-jg93" + } + ] + } + }, + { + "version": "1.3.7", + "tag": "@rushstack/heft-sass-plugin_v1.3.7", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "1.3.6", + "tag": "@rushstack/heft-sass-plugin_v1.3.6", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "1.3.5", + "tag": "@rushstack/heft-sass-plugin_v1.3.5", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "1.3.4", + "tag": "@rushstack/heft-sass-plugin_v1.3.4", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "1.3.3", + "tag": "@rushstack/heft-sass-plugin_v1.3.3", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "1.3.2", + "tag": "@rushstack/heft-sass-plugin_v1.3.2", + "date": "Sat, 11 Apr 2026 00:31:13 GMT", + "comments": { + "patch": [ + { + "comment": "Fix generated JS shims and `.d.ts` for `.module.scss` files that contain only `:global` styles and have no local CSS class exports" + }, + { + "comment": "Improve project README." + } + ] + } + }, + { + "version": "1.3.1", + "tag": "@rushstack/heft-sass-plugin_v1.3.1", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "1.3.0", + "tag": "@rushstack/heft-sass-plugin_v1.3.0", + "date": "Thu, 09 Apr 2026 23:00:20 GMT", + "comments": { + "minor": [ + { + "comment": "Add `preserveIcssExports` option to keep the ICSS `:export` block in emitted CSS output, required when downstream webpack loaders (e.g. `css-loader` icssParser) need to extract `:export` values at bundle time." + }, + { + "comment": "Add a `doNotTrimOriginalFileExtension` option. When enabled, the original file extension is preserved in the CSS output filename (e.g. `styles.scss` emits `styles.scss.css` instead of `styles.css`)" + } + ] + } + }, + { + "version": "1.2.11", + "tag": "@rushstack/heft-sass-plugin_v1.2.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "1.2.10", + "tag": "@rushstack/heft-sass-plugin_v1.2.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "1.2.9", + "tag": "@rushstack/heft-sass-plugin_v1.2.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "1.2.8", + "tag": "@rushstack/heft-sass-plugin_v1.2.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "1.2.7", + "tag": "@rushstack/heft-sass-plugin_v1.2.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "1.2.6", + "tag": "@rushstack/heft-sass-plugin_v1.2.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "1.2.5", + "tag": "@rushstack/heft-sass-plugin_v1.2.5", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "1.2.4", + "tag": "@rushstack/heft-sass-plugin_v1.2.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "1.2.3", + "tag": "@rushstack/heft-sass-plugin_v1.2.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "1.2.2", + "tag": "@rushstack/heft-sass-plugin_v1.2.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "1.2.1", + "tag": "@rushstack/heft-sass-plugin_v1.2.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "1.2.0", + "tag": "@rushstack/heft-sass-plugin_v1.2.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "1.1.14", + "tag": "@rushstack/heft-sass-plugin_v1.1.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "1.1.13", + "tag": "@rushstack/heft-sass-plugin_v1.1.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "1.1.12", + "tag": "@rushstack/heft-sass-plugin_v1.1.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "1.1.11", + "tag": "@rushstack/heft-sass-plugin_v1.1.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "1.1.10", + "tag": "@rushstack/heft-sass-plugin_v1.1.10", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "1.1.9", + "tag": "@rushstack/heft-sass-plugin_v1.1.9", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "1.1.8", + "tag": "@rushstack/heft-sass-plugin_v1.1.8", + "date": "Mon, 05 Jan 2026 16:12:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "1.1.7", + "tag": "@rushstack/heft-sass-plugin_v1.1.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "1.1.6", + "tag": "@rushstack/heft-sass-plugin_v1.1.6", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.5` to `1.1.6`" + } + ] + } + }, + { + "version": "1.1.5", + "tag": "@rushstack/heft-sass-plugin_v1.1.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.4` to `1.1.5`" + } + ] + } + }, + { + "version": "1.1.4", + "tag": "@rushstack/heft-sass-plugin_v1.1.4", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.3` to `1.1.4`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft-sass-plugin_v1.1.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.2` to `1.1.3`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft-sass-plugin_v1.1.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.1` to `1.1.2`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft-sass-plugin_v1.1.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.0` to `1.1.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-sass-plugin_v1.1.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.0.0` to `1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-sass-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.75.0` to `1.0.0`" + } + ] + } + }, + { + "version": "0.17.15", + "tag": "@rushstack/heft-sass-plugin_v0.17.15", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.5` to `0.75.0`" + } + ] + } + }, + { + "version": "0.17.14", + "tag": "@rushstack/heft-sass-plugin_v0.17.14", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.4` to `0.74.5`" + } + ] + } + }, + { + "version": "0.17.13", + "tag": "@rushstack/heft-sass-plugin_v0.17.13", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.3` to `0.74.4`" + } + ] + } + }, + { + "version": "0.17.12", + "tag": "@rushstack/heft-sass-plugin_v0.17.12", + "date": "Sun, 31 Aug 2025 02:24:40 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where generated `.scss.js` files can contain an incorrect path to the `.css` file." + } + ] + } + }, + { + "version": "0.17.11", + "tag": "@rushstack/heft-sass-plugin_v0.17.11", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.2` to `0.74.3`" + } + ] + } + }, + { + "version": "0.17.10", + "tag": "@rushstack/heft-sass-plugin_v0.17.10", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.1` to `0.74.2`" + } + ] + } + }, + { + "version": "0.17.9", + "tag": "@rushstack/heft-sass-plugin_v0.17.9", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.0` to `0.74.1`" + } + ] + } + }, + { + "version": "0.17.8", + "tag": "@rushstack/heft-sass-plugin_v0.17.8", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.6` to `0.74.0`" + } + ] + } + }, + { + "version": "0.17.7", + "tag": "@rushstack/heft-sass-plugin_v0.17.7", + "date": "Thu, 15 May 2025 00:11:49 GMT", + "comments": { + "patch": [ + { + "comment": "Quote classnames in .d.ts files to handle non-identifier characters." + } + ] + } + }, + { + "version": "0.17.6", + "tag": "@rushstack/heft-sass-plugin_v0.17.6", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where the SCSS module classifier evaluated SCSS partials instead of ignoring them (since they aren't directly importable)." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.5` to `0.73.6`" + } + ] + } + }, + { + "version": "0.17.5", + "tag": "@rushstack/heft-sass-plugin_v0.17.5", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.4` to `0.73.5`" + } + ] + } + }, + { + "version": "0.17.4", + "tag": "@rushstack/heft-sass-plugin_v0.17.4", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.3` to `0.73.4`" + } + ] + } + }, + { + "version": "0.17.3", + "tag": "@rushstack/heft-sass-plugin_v0.17.3", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.2` to `0.73.3`" + } + ] + } + }, + { + "version": "0.17.2", + "tag": "@rushstack/heft-sass-plugin_v0.17.2", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.1` to `0.73.2`" + } + ] + } + }, + { + "version": "0.17.1", + "tag": "@rushstack/heft-sass-plugin_v0.17.1", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "patch": [ + { + "comment": "Update documentation for `extends`" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.0` to `0.73.1`" + } + ] + } + }, + { + "version": "0.17.0", + "tag": "@rushstack/heft-sass-plugin_v0.17.0", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "minor": [ + { + "comment": "(BREAKING CHANGE) Remove `preserveSCSSExtension`. Change input type of `cssOutputFolders` to allow specifying JavaScript shim module format. Add accessor with hook to allow other plugins to customize final CSS." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.72.0` to `0.73.0`" + } + ] + } + }, + { + "version": "0.16.0", + "tag": "@rushstack/heft-sass-plugin_v0.16.0", + "date": "Wed, 09 Apr 2025 00:11:02 GMT", + "comments": { + "minor": [ + { + "comment": "Use `tryLoadProjectConfigurationFileAsync` API to remove direct dependency on `@rushstack/heft-config-file`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.2` to `0.72.0`" + } + ] + } + }, + { + "version": "0.15.25", + "tag": "@rushstack/heft-sass-plugin_v0.15.25", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.31`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.1` to `0.71.2`" + } + ] + } + }, + { + "version": "0.15.24", + "tag": "@rushstack/heft-sass-plugin_v0.15.24", + "date": "Tue, 25 Mar 2025 15:11:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.30`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.0` to `0.71.1`" + } + ] + } + }, + { + "version": "0.15.23", + "tag": "@rushstack/heft-sass-plugin_v0.15.23", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.1` to `0.71.0`" + } + ] + } + }, + { + "version": "0.15.22", + "tag": "@rushstack/heft-sass-plugin_v0.15.22", + "date": "Wed, 12 Mar 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.0` to `0.70.1`" + } + ] + } + }, + { + "version": "0.15.21", + "tag": "@rushstack/heft-sass-plugin_v0.15.21", + "date": "Tue, 11 Mar 2025 02:12:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.27`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.3` to `0.70.0`" + } + ] + } + }, + { + "version": "0.15.20", + "tag": "@rushstack/heft-sass-plugin_v0.15.20", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.26`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.2` to `0.69.3`" + } + ] + } + }, + { + "version": "0.15.19", + "tag": "@rushstack/heft-sass-plugin_v0.15.19", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.25`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.51.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.1` to `0.69.2`" + } + ] + } + }, + { + "version": "0.15.18", + "tag": "@rushstack/heft-sass-plugin_v0.15.18", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.24`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.51.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.0` to `0.69.1`" + } + ] + } + }, + { + "version": "0.15.17", + "tag": "@rushstack/heft-sass-plugin_v0.15.17", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.18` to `0.69.0`" + } + ] + } + }, + { + "version": "0.15.16", + "tag": "@rushstack/heft-sass-plugin_v0.15.16", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.22`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.50.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.17` to `0.68.18`" + } + ] + } + }, + { + "version": "0.15.15", + "tag": "@rushstack/heft-sass-plugin_v0.15.15", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.6`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.16` to `0.68.17`" + } + ] + } + }, + { + "version": "0.15.14", + "tag": "@rushstack/heft-sass-plugin_v0.15.14", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.5`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.20`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.50.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.15` to `0.68.16`" + } + ] + } + }, + { + "version": "0.15.13", + "tag": "@rushstack/heft-sass-plugin_v0.15.13", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.14` to `0.68.15`" + } + ] + } + }, + { + "version": "0.15.12", + "tag": "@rushstack/heft-sass-plugin_v0.15.12", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.18`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.49.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.13` to `0.68.14`" + } + ] + } + }, + { + "version": "0.15.11", + "tag": "@rushstack/heft-sass-plugin_v0.15.11", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.17`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.49.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.12` to `0.68.13`" + } + ] + } + }, + { + "version": "0.15.10", + "tag": "@rushstack/heft-sass-plugin_v0.15.10", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.16`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.49.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.11` to `0.68.12`" + } + ] + } + }, + { + "version": "0.15.9", + "tag": "@rushstack/heft-sass-plugin_v0.15.9", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.15`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.48.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.10` to `0.68.11`" + } + ] + } + }, + { + "version": "0.15.8", + "tag": "@rushstack/heft-sass-plugin_v0.15.8", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.9` to `0.68.10`" + } + ] + } + }, + { + "version": "0.15.7", + "tag": "@rushstack/heft-sass-plugin_v0.15.7", + "date": "Tue, 03 Dec 2024 16:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.8` to `0.68.9`" + } + ] + } + }, + { + "version": "0.15.6", + "tag": "@rushstack/heft-sass-plugin_v0.15.6", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.12`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.48.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.7` to `0.68.8`" + } + ] + } + }, + { + "version": "0.15.5", + "tag": "@rushstack/heft-sass-plugin_v0.15.5", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.11`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.6` to `0.68.7`" + } + ] + } + }, + { + "version": "0.15.4", + "tag": "@rushstack/heft-sass-plugin_v0.15.4", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.8`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.5` to `0.68.6`" + } + ] + } + }, + { + "version": "0.15.3", + "tag": "@rushstack/heft-sass-plugin_v0.15.3", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.4` to `0.68.5`" + } + ] + } + }, + { + "version": "0.15.2", + "tag": "@rushstack/heft-sass-plugin_v0.15.2", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.8`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.3` to `0.68.4`" + } + ] + } + }, + { + "version": "0.15.1", + "tag": "@rushstack/heft-sass-plugin_v0.15.1", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.7`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.2` to `0.68.3`" + } + ] + } + }, + { + "version": "0.15.0", + "tag": "@rushstack/heft-sass-plugin_v0.15.0", + "date": "Thu, 03 Oct 2024 19:46:23 GMT", + "comments": { + "minor": [ + { + "comment": "Add \"suppressDeprecations\" option to suppress specific SASS deprecation IDs. Add \"ignoreDeprecationsInDependencies\" option to ignore deprecation warnings from external SASS." + } + ] + } + }, + { + "version": "0.14.24", + "tag": "@rushstack/heft-sass-plugin_v0.14.24", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.1` to `0.68.2`" + } + ] + } + }, + { + "version": "0.14.23", + "tag": "@rushstack/heft-sass-plugin_v0.14.23", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.0` to `0.68.1`" + } + ] + } + }, + { + "version": "0.14.22", + "tag": "@rushstack/heft-sass-plugin_v0.14.22", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.2` to `0.68.0`" + } + ] + } + }, + { + "version": "0.14.21", + "tag": "@rushstack/heft-sass-plugin_v0.14.21", + "date": "Sat, 28 Sep 2024 00:11:41 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.3`" + } + ] + } + }, + { + "version": "0.14.20", + "tag": "@rushstack/heft-sass-plugin_v0.14.20", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.1` to `0.67.2`" + } + ] + } + }, + { + "version": "0.14.19", + "tag": "@rushstack/heft-sass-plugin_v0.14.19", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.0` to `0.67.1`" + } + ] + } + }, + { + "version": "0.14.18", + "tag": "@rushstack/heft-sass-plugin_v0.14.18", + "date": "Mon, 26 Aug 2024 02:00:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.0`" + } + ] + } + }, + { + "version": "0.14.17", + "tag": "@rushstack/heft-sass-plugin_v0.14.17", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.26` to `0.67.0`" + } + ] + } + }, + { + "version": "0.14.16", + "tag": "@rushstack/heft-sass-plugin_v0.14.16", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.63`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.25` to `0.66.26`" + } + ] + } + }, + { + "version": "0.14.15", + "tag": "@rushstack/heft-sass-plugin_v0.14.15", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.62`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.24` to `0.66.25`" + } + ] + } + }, + { + "version": "0.14.14", + "tag": "@rushstack/heft-sass-plugin_v0.14.14", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.61`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.23` to `0.66.24`" + } + ] + } + }, + { + "version": "0.14.13", + "tag": "@rushstack/heft-sass-plugin_v0.14.13", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.60`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.22` to `0.66.23`" + } + ] + } + }, + { + "version": "0.14.12", + "tag": "@rushstack/heft-sass-plugin_v0.14.12", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.59`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.21` to `0.66.22`" + } + ] + } + }, + { + "version": "0.14.11", + "tag": "@rushstack/heft-sass-plugin_v0.14.11", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.58`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.20` to `0.66.21`" + } + ] + } + }, + { + "version": "0.14.10", + "tag": "@rushstack/heft-sass-plugin_v0.14.10", + "date": "Tue, 16 Jul 2024 00:36:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.57`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.19` to `0.66.20`" + } + ] + } + }, + { + "version": "0.14.9", + "tag": "@rushstack/heft-sass-plugin_v0.14.9", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.56`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.18` to `0.66.19`" + } + ] + } + }, + { + "version": "0.14.8", + "tag": "@rushstack/heft-sass-plugin_v0.14.8", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.55`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.47.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.17` to `0.66.18`" + } + ] + } + }, + { + "version": "0.14.7", + "tag": "@rushstack/heft-sass-plugin_v0.14.7", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.25`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.54`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.46.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.16` to `0.66.17`" + } + ] + } + }, + { + "version": "0.14.6", + "tag": "@rushstack/heft-sass-plugin_v0.14.6", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.24`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.53`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.46.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.15` to `0.66.16`" + } + ] + } + }, + { + "version": "0.14.5", + "tag": "@rushstack/heft-sass-plugin_v0.14.5", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.52`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.46.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.14` to `0.66.15`" + } + ] + } + }, + { + "version": "0.14.4", + "tag": "@rushstack/heft-sass-plugin_v0.14.4", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.23`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.51`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.45.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.13` to `0.66.14`" + } + ] + } + }, + { + "version": "0.14.3", + "tag": "@rushstack/heft-sass-plugin_v0.14.3", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.22`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.50`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.45.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.12` to `0.66.13`" + } + ] + } + }, + { + "version": "0.14.2", + "tag": "@rushstack/heft-sass-plugin_v0.14.2", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.21`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.49`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.44.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.11` to `0.66.12`" + } + ] + } + }, + { + "version": "0.14.1", + "tag": "@rushstack/heft-sass-plugin_v0.14.1", + "date": "Fri, 24 May 2024 00:15:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.48`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.44.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.10` to `0.66.11`" + } + ] + } + }, + { + "version": "0.14.0", + "tag": "@rushstack/heft-sass-plugin_v0.14.0", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "minor": [ + { + "comment": "Bump `sass-embedded` to 1.77." + }, + { + "comment": "Fix an issue where `@import` and `@use` rules that referenced dependency packages that are not direct dependencies of the project being built were not correctly resolved." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.20`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.47`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.9` to `0.66.10`" + } + ] + } + }, + { + "version": "0.13.32", + "tag": "@rushstack/heft-sass-plugin_v0.13.32", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.46`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.8` to `0.66.9`" + } + ] + } + }, + { + "version": "0.13.31", + "tag": "@rushstack/heft-sass-plugin_v0.13.31", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.19`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.45`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.7` to `0.66.8`" + } + ] + } + }, + { + "version": "0.13.30", + "tag": "@rushstack/heft-sass-plugin_v0.13.30", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.18`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.44`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.6` to `0.66.7`" + } + ] + } + }, + { + "version": "0.13.29", + "tag": "@rushstack/heft-sass-plugin_v0.13.29", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.17`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.43`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.5` to `0.66.6`" + } + ] + } + }, + { + "version": "0.13.28", + "tag": "@rushstack/heft-sass-plugin_v0.13.28", + "date": "Wed, 08 May 2024 22:23:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.42`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.4` to `0.66.5`" + } + ] + } + }, + { + "version": "0.13.27", + "tag": "@rushstack/heft-sass-plugin_v0.13.27", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.16`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.41`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.3` to `0.66.4`" + } + ] + } + }, + { + "version": "0.13.26", + "tag": "@rushstack/heft-sass-plugin_v0.13.26", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.15`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.40`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.2` to `0.66.3`" + } + ] + } + }, + { + "version": "0.13.25", + "tag": "@rushstack/heft-sass-plugin_v0.13.25", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.39`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.43.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.1` to `0.66.2`" + } + ] + } + }, + { + "version": "0.13.24", + "tag": "@rushstack/heft-sass-plugin_v0.13.24", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.0` to `0.66.1`" + } + ] + } + }, + { + "version": "0.13.23", + "tag": "@rushstack/heft-sass-plugin_v0.13.23", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.10` to `0.66.0`" + } + ] + } + }, + { + "version": "0.13.22", + "tag": "@rushstack/heft-sass-plugin_v0.13.22", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.36`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.9` to `0.65.10`" + } + ] + } + }, + { + "version": "0.13.21", + "tag": "@rushstack/heft-sass-plugin_v0.13.21", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.35`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.8` to `0.65.9`" + } + ] + } + }, + { + "version": "0.13.20", + "tag": "@rushstack/heft-sass-plugin_v0.13.20", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.34`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.7` to `0.65.8`" + } + ] + } + }, + { + "version": "0.13.19", + "tag": "@rushstack/heft-sass-plugin_v0.13.19", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.33`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.42.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.6` to `0.65.7`" + } + ] + } + }, + { + "version": "0.13.18", + "tag": "@rushstack/heft-sass-plugin_v0.13.18", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.32`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.41.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.5` to `0.65.6`" + } + ] + } + }, + { + "version": "0.13.17", + "tag": "@rushstack/heft-sass-plugin_v0.13.17", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.14`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.31`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.41.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.4` to `^0.65.5`" + } + ] + } + }, + { + "version": "0.13.16", + "tag": "@rushstack/heft-sass-plugin_v0.13.16", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.3` to `^0.65.4`" + } + ] + } + }, + { + "version": "0.13.15", + "tag": "@rushstack/heft-sass-plugin_v0.13.15", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.13`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.29`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.2` to `^0.65.3`" + } + ] + } + }, + { + "version": "0.13.14", + "tag": "@rushstack/heft-sass-plugin_v0.13.14", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.28`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.1` to `^0.65.2`" + } + ] + } + }, + { + "version": "0.13.13", + "tag": "@rushstack/heft-sass-plugin_v0.13.13", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.27`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.0` to `^0.65.1`" + } + ] + } + }, + { + "version": "0.13.12", + "tag": "@rushstack/heft-sass-plugin_v0.13.12", + "date": "Tue, 20 Feb 2024 16:10:53 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.8` to `^0.65.0`" + } + ] + } + }, + { + "version": "0.13.11", + "tag": "@rushstack/heft-sass-plugin_v0.13.11", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.11`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.25`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.7` to `^0.64.8`" + } + ] + } + }, + { + "version": "0.13.10", + "tag": "@rushstack/heft-sass-plugin_v0.13.10", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.10`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.24`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.6` to `^0.64.7`" + } + ] + } + }, + { + "version": "0.13.9", + "tag": "@rushstack/heft-sass-plugin_v0.13.9", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.23`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.5` to `^0.64.6`" + } + ] + } + }, + { + "version": "0.13.8", + "tag": "@rushstack/heft-sass-plugin_v0.13.8", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.22`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.40.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.4` to `^0.64.5`" + } + ] + } + }, + { + "version": "0.13.7", + "tag": "@rushstack/heft-sass-plugin_v0.13.7", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.21`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.3` to `^0.64.4`" + } + ] + } + }, + { + "version": "0.13.6", + "tag": "@rushstack/heft-sass-plugin_v0.13.6", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.20`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.2` to `^0.64.3`" + } + ] + } + }, + { + "version": "0.13.5", + "tag": "@rushstack/heft-sass-plugin_v0.13.5", + "date": "Tue, 23 Jan 2024 20:12:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.19`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.1` to `^0.64.2`" + } + ] + } + }, + { + "version": "0.13.4", + "tag": "@rushstack/heft-sass-plugin_v0.13.4", + "date": "Tue, 23 Jan 2024 16:15:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.18`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.0` to `^0.64.1`" + } + ] + } + }, + { + "version": "0.13.3", + "tag": "@rushstack/heft-sass-plugin_v0.13.3", + "date": "Tue, 16 Jan 2024 18:30:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.6` to `^0.64.0`" + } + ] + } + }, + { + "version": "0.13.2", + "tag": "@rushstack/heft-sass-plugin_v0.13.2", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.16`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.5` to `^0.63.6`" + } + ] + } + }, + { + "version": "0.13.1", + "tag": "@rushstack/heft-sass-plugin_v0.13.1", + "date": "Wed, 20 Dec 2023 01:09:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.15`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.39.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.4` to `^0.63.5`" + } + ] + } + }, + { + "version": "0.13.0", + "tag": "@rushstack/heft-sass-plugin_v0.13.0", + "date": "Fri, 08 Dec 2023 20:48:44 GMT", + "comments": { + "minor": [ + { + "comment": "Upgrade postcss-modules from v1.5.0 to v6.0.0" + } + ] + } + }, + { + "version": "0.12.14", + "tag": "@rushstack/heft-sass-plugin_v0.12.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.14`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.3` to `^0.63.4`" + } + ] + } + }, + { + "version": "0.12.13", + "tag": "@rushstack/heft-sass-plugin_v0.12.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.13`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.2` to `^0.63.3`" + } + ] + } + }, + { + "version": "0.12.12", + "tag": "@rushstack/heft-sass-plugin_v0.12.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.12`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.1` to `^0.63.2`" + } + ] + } + }, + { + "version": "0.12.11", + "tag": "@rushstack/heft-sass-plugin_v0.12.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.11`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.0` to `^0.63.1`" + } + ] + } + }, + { + "version": "0.12.10", + "tag": "@rushstack/heft-sass-plugin_v0.12.10", + "date": "Mon, 30 Oct 2023 23:36:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.10`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.3` to `^0.63.0`" + } + ] + } + }, + { + "version": "0.12.9", + "tag": "@rushstack/heft-sass-plugin_v0.12.9", + "date": "Sun, 01 Oct 2023 02:56:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.9`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.38.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.2` to `^0.62.3`" + } + ] + } + }, + { + "version": "0.12.8", + "tag": "@rushstack/heft-sass-plugin_v0.12.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.8`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.1` to `^0.62.2`" + } + ] + } + }, + { + "version": "0.12.7", + "tag": "@rushstack/heft-sass-plugin_v0.12.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.7`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.0` to `^0.62.1`" + } + ] + } + }, + { + "version": "0.12.6", + "tag": "@rushstack/heft-sass-plugin_v0.12.6", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.3` to `^0.62.0`" + } + ] + } + }, + { + "version": "0.12.5", + "tag": "@rushstack/heft-sass-plugin_v0.12.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.2` to `^0.61.3`" + } + ] + } + }, + { + "version": "0.12.4", + "tag": "@rushstack/heft-sass-plugin_v0.12.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.4`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.1` to `^0.61.2`" + } + ] + } + }, + { + "version": "0.12.3", + "tag": "@rushstack/heft-sass-plugin_v0.12.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.0` to `^0.61.1`" + } + ] + } + }, + { + "version": "0.12.2", + "tag": "@rushstack/heft-sass-plugin_v0.12.2", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.60.0` to `^0.61.0`" + } + ] + } + }, + { + "version": "0.12.1", + "tag": "@rushstack/heft-sass-plugin_v0.12.1", + "date": "Tue, 19 Sep 2023 15:21:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.59.0` to `^0.60.0`" + } + ] + } + }, + { + "version": "0.12.0", + "tag": "@rushstack/heft-sass-plugin_v0.12.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.37.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.2` to `^0.59.0`" + } + ] + } + }, + { + "version": "0.11.28", + "tag": "@rushstack/heft-sass-plugin_v0.11.28", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.1` to `^0.58.2`" + } + ] + } + }, + { + "version": "0.11.27", + "tag": "@rushstack/heft-sass-plugin_v0.11.27", + "date": "Sat, 05 Aug 2023 00:20:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.11.0`" + } + ] + } + }, + { + "version": "0.11.26", + "tag": "@rushstack/heft-sass-plugin_v0.11.26", + "date": "Fri, 04 Aug 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.37`" + } + ] + } + }, + { + "version": "0.11.25", + "tag": "@rushstack/heft-sass-plugin_v0.11.25", + "date": "Mon, 31 Jul 2023 15:19:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.21`" + } + ] + } + }, + { + "version": "0.11.24", + "tag": "@rushstack/heft-sass-plugin_v0.11.24", + "date": "Sat, 29 Jul 2023 00:22:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.0` to `^0.58.1`" + } + ] + } + }, + { + "version": "0.11.23", + "tag": "@rushstack/heft-sass-plugin_v0.11.23", + "date": "Thu, 20 Jul 2023 20:47:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.57.1` to `^0.58.0`" + } + ] + } + }, + { + "version": "0.11.22", + "tag": "@rushstack/heft-sass-plugin_v0.11.22", + "date": "Wed, 19 Jul 2023 00:20:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.33`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.57.0` to `^0.57.1`" + } + ] + } + }, + { + "version": "0.11.21", + "tag": "@rushstack/heft-sass-plugin_v0.11.21", + "date": "Mon, 17 Jul 2023 15:20:25 GMT", + "comments": { + "patch": [ + { + "comment": "Fix the \"excludeFiles\" configuration option." + } + ] + } + }, + { + "version": "0.11.20", + "tag": "@rushstack/heft-sass-plugin_v0.11.20", + "date": "Fri, 14 Jul 2023 15:20:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.17`" + } + ] + } + }, + { + "version": "0.11.19", + "tag": "@rushstack/heft-sass-plugin_v0.11.19", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.3` to `^0.57.0`" + } + ] + } + }, + { + "version": "0.11.18", + "tag": "@rushstack/heft-sass-plugin_v0.11.18", + "date": "Wed, 12 Jul 2023 15:20:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.30`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.2` to `^0.56.3`" + } + ] + } + }, + { + "version": "0.11.17", + "tag": "@rushstack/heft-sass-plugin_v0.11.17", + "date": "Wed, 12 Jul 2023 00:23:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.14`" + } + ] + } + }, + { + "version": "0.11.16", + "tag": "@rushstack/heft-sass-plugin_v0.11.16", + "date": "Fri, 07 Jul 2023 00:19:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.1` to `^0.56.2`" + } + ] + } + }, + { + "version": "0.11.15", + "tag": "@rushstack/heft-sass-plugin_v0.11.15", + "date": "Thu, 06 Jul 2023 00:16:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.27`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.0` to `^0.56.1`" + } + ] + } + }, + { + "version": "0.11.14", + "tag": "@rushstack/heft-sass-plugin_v0.11.14", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.11`" + } + ] + } + }, + { + "version": "0.11.13", + "tag": "@rushstack/heft-sass-plugin_v0.11.13", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.25`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.36.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.2` to `^0.56.0`" + } + ] + } + }, + { + "version": "0.11.12", + "tag": "@rushstack/heft-sass-plugin_v0.11.12", + "date": "Thu, 15 Jun 2023 00:21:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.12.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.24`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.35.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.1` to `^0.55.2`" + } + ] + } + }, + { + "version": "0.11.11", + "tag": "@rushstack/heft-sass-plugin_v0.11.11", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.0` to `^0.55.1`" + } + ] + } + }, + { + "version": "0.11.10", + "tag": "@rushstack/heft-sass-plugin_v0.11.10", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.54.0` to `^0.55.0`" + } + ] + } + }, + { + "version": "0.11.9", + "tag": "@rushstack/heft-sass-plugin_v0.11.9", + "date": "Tue, 13 Jun 2023 01:49:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.21`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.35.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.53.1` to `^0.54.0`" + } + ] + } + }, + { + "version": "0.11.8", + "tag": "@rushstack/heft-sass-plugin_v0.11.8", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.53.0` to `^0.53.1`" + } + ] + } + }, + { + "version": "0.11.7", + "tag": "@rushstack/heft-sass-plugin_v0.11.7", + "date": "Fri, 09 Jun 2023 15:23:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.4`" + } + ] + } + }, + { + "version": "0.11.6", + "tag": "@rushstack/heft-sass-plugin_v0.11.6", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.2` to `^0.53.0`" + } + ] + } + }, + { + "version": "0.11.5", + "tag": "@rushstack/heft-sass-plugin_v0.11.5", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.1` to `^0.52.2`" + } + ] + } + }, + { + "version": "0.11.4", + "tag": "@rushstack/heft-sass-plugin_v0.11.4", + "date": "Thu, 08 Jun 2023 00:20:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.0` to `^0.52.1`" + } + ] + } + }, + { + "version": "0.11.3", + "tag": "@rushstack/heft-sass-plugin_v0.11.3", + "date": "Wed, 07 Jun 2023 22:45:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.12.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.15`" + }, + { + "comment": "Updating dependency \"@microsoft/api-extractor\" to `7.35.2`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.51.0` to `^0.52.0`" + } + ] + } + }, + { + "version": "0.11.2", + "tag": "@rushstack/heft-sass-plugin_v0.11.2", + "date": "Tue, 06 Jun 2023 02:52:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.1.0`" + } + ] + } + }, + { + "version": "0.11.1", + "tag": "@rushstack/heft-sass-plugin_v0.11.1", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.0.1`" + } + ] + } + }, { "version": "0.11.0", "tag": "@rushstack/heft-sass-plugin_v0.11.0", diff --git a/heft-plugins/heft-sass-plugin/CHANGELOG.md b/heft-plugins/heft-sass-plugin/CHANGELOG.md index d18a14da39e..325bbe0099f 100644 --- a/heft-plugins/heft-sass-plugin/CHANGELOG.md +++ b/heft-plugins/heft-sass-plugin/CHANGELOG.md @@ -1,6 +1,997 @@ # Change Log - @rushstack/heft-sass-plugin -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 1.4.6 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 1.4.5 +Fri, 17 Jul 2026 00:15:59 GMT + +_Version update only_ + +## 1.4.4 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.4.3 +Sat, 13 Jun 2026 00:16:18 GMT + +### Patches + +- Fix a regression where plain `.css` files referenced from another stylesheet via `@use`/`@import` could not be resolved by the importer. + +## 1.4.2 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 1.4.1 +Tue, 02 Jun 2026 07:30:20 GMT + +### Patches + +- Fix sourceMap: true crashing on Linux/macOS when compiled .scss files use @use or @import + +## 1.4.0 +Fri, 29 May 2026 18:25:51 GMT + +### Minor changes + +- Add opt-in sourceMap option to emit `.css.map` files and `sourceMappingURL` comments alongside compiled CSS. + +## 1.3.8 +Thu, 30 Apr 2026 00:15:22 GMT + +### Patches + +- Bump postcss@~8.5.10 to address CVE GHSA-qx2v-qp2m-jg93 + +## 1.3.7 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.3.6 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.3.5 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 1.3.4 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 1.3.3 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.3.2 +Sat, 11 Apr 2026 00:31:13 GMT + +### Patches + +- Fix generated JS shims and `.d.ts` for `.module.scss` files that contain only `:global` styles and have no local CSS class exports +- Improve project README. + +## 1.3.1 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.3.0 +Thu, 09 Apr 2026 23:00:20 GMT + +### Minor changes + +- Add `preserveIcssExports` option to keep the ICSS `:export` block in emitted CSS output, required when downstream webpack loaders (e.g. `css-loader` icssParser) need to extract `:export` values at bundle time. +- Add a `doNotTrimOriginalFileExtension` option. When enabled, the original file extension is preserved in the CSS output filename (e.g. `styles.scss` emits `styles.scss.css` instead of `styles.css`) + +## 1.2.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.2.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.2.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.2.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.2.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.2.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.2.5 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 1.2.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.2.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.2.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.2.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.2.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.1.14 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.1.13 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.1.12 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.1.11 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.1.10 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.1.9 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 1.1.8 +Mon, 05 Jan 2026 16:12:50 GMT + +_Version update only_ + +## 1.1.7 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.1.6 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.1.5 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.1.4 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 1.1.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.1.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.1.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 1.1.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.17.15 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.17.14 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.17.13 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.17.12 +Sun, 31 Aug 2025 02:24:40 GMT + +### Patches + +- Fix an issue where generated `.scss.js` files can contain an incorrect path to the `.css` file. + +## 0.17.11 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.17.10 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 0.17.9 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.17.8 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.17.7 +Thu, 15 May 2025 00:11:49 GMT + +### Patches + +- Quote classnames in .d.ts files to handle non-identifier characters. + +## 0.17.6 +Tue, 13 May 2025 02:09:20 GMT + +### Patches + +- Fix an issue where the SCSS module classifier evaluated SCSS partials instead of ignoring them (since they aren't directly importable). + +## 0.17.5 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.17.4 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.17.3 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.17.2 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.17.1 +Thu, 17 Apr 2025 00:11:21 GMT + +### Patches + +- Update documentation for `extends` + +## 0.17.0 +Tue, 15 Apr 2025 15:11:57 GMT + +### Minor changes + +- (BREAKING CHANGE) Remove `preserveSCSSExtension`. Change input type of `cssOutputFolders` to allow specifying JavaScript shim module format. Add accessor with hook to allow other plugins to customize final CSS. + +## 0.16.0 +Wed, 09 Apr 2025 00:11:02 GMT + +### Minor changes + +- Use `tryLoadProjectConfigurationFileAsync` API to remove direct dependency on `@rushstack/heft-config-file`. + +## 0.15.25 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.15.24 +Tue, 25 Mar 2025 15:11:16 GMT + +_Version update only_ + +## 0.15.23 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.15.22 +Wed, 12 Mar 2025 00:11:32 GMT + +_Version update only_ + +## 0.15.21 +Tue, 11 Mar 2025 02:12:34 GMT + +_Version update only_ + +## 0.15.20 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.15.19 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.15.18 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.15.17 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 0.15.16 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.15.15 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.15.14 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.15.13 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 0.15.12 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.15.11 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.15.10 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.15.9 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.15.8 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.15.7 +Tue, 03 Dec 2024 16:11:07 GMT + +_Version update only_ + +## 0.15.6 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.15.5 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.15.4 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.15.3 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.15.2 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.15.1 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.15.0 +Thu, 03 Oct 2024 19:46:23 GMT + +### Minor changes + +- Add "suppressDeprecations" option to suppress specific SASS deprecation IDs. Add "ignoreDeprecationsInDependencies" option to ignore deprecation warnings from external SASS. + +## 0.14.24 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.14.23 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.14.22 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.14.21 +Sat, 28 Sep 2024 00:11:41 GMT + +_Version update only_ + +## 0.14.20 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.14.19 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.14.18 +Mon, 26 Aug 2024 02:00:11 GMT + +_Version update only_ + +## 0.14.17 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.14.16 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.14.15 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.14.14 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.14.13 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 0.14.12 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 0.14.11 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.14.10 +Tue, 16 Jul 2024 00:36:22 GMT + +_Version update only_ + +## 0.14.9 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.14.8 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.14.7 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.14.6 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 0.14.5 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.14.4 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.14.3 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.14.2 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.14.1 +Fri, 24 May 2024 00:15:09 GMT + +_Version update only_ + +## 0.14.0 +Thu, 23 May 2024 02:26:56 GMT + +### Minor changes + +- Bump `sass-embedded` to 1.77. +- Fix an issue where `@import` and `@use` rules that referenced dependency packages that are not direct dependencies of the project being built were not correctly resolved. + +## 0.13.32 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.13.31 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.13.30 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.13.29 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.13.28 +Wed, 08 May 2024 22:23:51 GMT + +_Version update only_ + +## 0.13.27 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 0.13.26 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.13.25 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 0.13.24 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 0.13.23 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 0.13.22 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 0.13.21 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 0.13.20 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 0.13.19 +Thu, 29 Feb 2024 07:11:45 GMT + +_Version update only_ + +## 0.13.18 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.13.17 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.13.16 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.13.15 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.13.14 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.13.13 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.13.12 +Tue, 20 Feb 2024 16:10:53 GMT + +_Version update only_ + +## 0.13.11 +Mon, 19 Feb 2024 21:54:27 GMT + +_Version update only_ + +## 0.13.10 +Sat, 17 Feb 2024 06:24:35 GMT + +_Version update only_ + +## 0.13.9 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 0.13.8 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.13.7 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.13.6 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.13.5 +Tue, 23 Jan 2024 20:12:58 GMT + +_Version update only_ + +## 0.13.4 +Tue, 23 Jan 2024 16:15:06 GMT + +_Version update only_ + +## 0.13.3 +Tue, 16 Jan 2024 18:30:11 GMT + +_Version update only_ + +## 0.13.2 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.13.1 +Wed, 20 Dec 2023 01:09:46 GMT + +_Version update only_ + +## 0.13.0 +Fri, 08 Dec 2023 20:48:44 GMT + +### Minor changes + +- Upgrade postcss-modules from v1.5.0 to v6.0.0 + +## 0.12.14 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.12.13 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.12.12 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 0.12.11 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 0.12.10 +Mon, 30 Oct 2023 23:36:38 GMT + +_Version update only_ + +## 0.12.9 +Sun, 01 Oct 2023 02:56:30 GMT + +_Version update only_ + +## 0.12.8 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 0.12.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.12.6 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 0.12.5 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 0.12.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.12.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 0.12.2 +Fri, 22 Sep 2023 00:05:50 GMT + +_Version update only_ + +## 0.12.1 +Tue, 19 Sep 2023 15:21:51 GMT + +_Version update only_ + +## 0.12.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 0.11.28 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 0.11.27 +Sat, 05 Aug 2023 00:20:19 GMT + +_Version update only_ + +## 0.11.26 +Fri, 04 Aug 2023 00:22:37 GMT + +_Version update only_ + +## 0.11.25 +Mon, 31 Jul 2023 15:19:06 GMT + +_Version update only_ + +## 0.11.24 +Sat, 29 Jul 2023 00:22:51 GMT + +_Version update only_ + +## 0.11.23 +Thu, 20 Jul 2023 20:47:28 GMT + +_Version update only_ + +## 0.11.22 +Wed, 19 Jul 2023 00:20:32 GMT + +_Version update only_ + +## 0.11.21 +Mon, 17 Jul 2023 15:20:25 GMT + +### Patches + +- Fix the "excludeFiles" configuration option. + +## 0.11.20 +Fri, 14 Jul 2023 15:20:45 GMT + +_Version update only_ + +## 0.11.19 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 0.11.18 +Wed, 12 Jul 2023 15:20:40 GMT + +_Version update only_ + +## 0.11.17 +Wed, 12 Jul 2023 00:23:30 GMT + +_Version update only_ + +## 0.11.16 +Fri, 07 Jul 2023 00:19:33 GMT + +_Version update only_ + +## 0.11.15 +Thu, 06 Jul 2023 00:16:20 GMT + +_Version update only_ + +## 0.11.14 +Tue, 04 Jul 2023 00:18:47 GMT + +_Version update only_ + +## 0.11.13 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 0.11.12 +Thu, 15 Jun 2023 00:21:02 GMT + +_Version update only_ + +## 0.11.11 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 0.11.10 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 0.11.9 +Tue, 13 Jun 2023 01:49:02 GMT + +_Version update only_ + +## 0.11.8 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 0.11.7 +Fri, 09 Jun 2023 15:23:15 GMT + +_Version update only_ + +## 0.11.6 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 0.11.5 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 0.11.4 +Thu, 08 Jun 2023 00:20:03 GMT + +_Version update only_ + +## 0.11.3 +Wed, 07 Jun 2023 22:45:17 GMT + +_Version update only_ + +## 0.11.2 +Tue, 06 Jun 2023 02:52:51 GMT + +_Version update only_ + +## 0.11.1 +Mon, 05 Jun 2023 21:45:21 GMT + +_Version update only_ ## 0.11.0 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/heft-plugins/heft-sass-plugin/README.md b/heft-plugins/heft-sass-plugin/README.md index 1d6602caf7e..a55f5b462cd 100644 --- a/heft-plugins/heft-sass-plugin/README.md +++ b/heft-plugins/heft-sass-plugin/README.md @@ -1,12 +1,244 @@ # @rushstack/heft-sass-plugin -This is a Heft plugin for using sass-embedded during the "build" stage. -If `sass-embedded` is not supported on your platform, you can override the dependency via npm alias to use the `sass` package instead. +A [Heft](https://heft.rushstack.io/) plugin that compiles SCSS/Sass files during the build phase. It uses [`sass-embedded`](https://www.npmjs.com/package/sass-embedded) under the hood and produces: + +- **TypeScript type definitions** (`.d.ts`) for CSS modules, giving you typed access to class names and `:export` values +- **Compiled CSS files** (optional) in one or more output folders +- **JavaScript shims** (optional) that re-export the CSS for consumption in CommonJS or ESM environments + +> If `sass-embedded` is not supported on your platform, you can substitute it with the [`sass`](https://www.npmjs.com/package/sass) package using an npm alias. ## Links -- [CHANGELOG.md]( - https://github.com/microsoft/rushstack/blob/main/heft-plugins/heft-sass-plugin/CHANGELOG.md) - Find - out what's new in the latest version +- [CHANGELOG.md](https://github.com/microsoft/rushstack/blob/main/heft-plugins/heft-sass-plugin/CHANGELOG.md) - Find out what's new in the latest version Heft is part of the [Rush Stack](https://rushstack.io/) family of projects. + +--- + +## Setup + +### 1. Add the plugin to your project + +In your project's `package.json`: + +```json +{ + "devDependencies": { + "@rushstack/heft": "...", + "@rushstack/heft-sass-plugin": "..." + } +} +``` + +### 2. Register the plugin in `config/heft.json` + +The `sass` task must run before `typescript` so that the generated `.d.ts` files are available when TypeScript compiles your project. + +```json +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "phasesByName": { + "build": { + "tasksByName": { + "sass": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-sass-plugin" + } + }, + + "typescript": { + "taskDependencies": ["sass"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-typescript-plugin" + } + } + } + } + } +} +``` + +### 3. Create `config/sass.json` + +A minimal config uses all defaults: + +```json +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-sass-plugin.schema.json" +} +``` + +A more complete setup that emits CSS and shims for both ESM and CommonJS: + +```json +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-sass-plugin.schema.json", + "cssOutputFolders": [ + { "folder": "lib-esm", "shimModuleFormat": "esnext" }, + { "folder": "lib-commonjs", "shimModuleFormat": "commonjs" } + ], + "fileExtensions": [".module.scss", ".module.sass"], + "nonModuleFileExtensions": [".global.scss", ".global.sass"], + "silenceDeprecations": ["mixed-decls", "import", "global-builtin", "color-functions"] +} +``` + +### 4. Add generated files to `tsconfig.json` + +Point TypeScript at the generated type definitions by including the `generatedTsFolder` in your `tsconfig.json`: + +```json +{ + "compilerOptions": { + "paths": {} + }, + "include": ["src", "temp/sass-ts"] +} +``` + +## CSS Modules vs. global stylesheets + +The plugin distinguishes between two kinds of files based on their extension: + +**CSS modules** (extensions listed in `fileExtensions`, default: `.sass`, `.scss`, `.css`): +- Processed with [`postcss-modules`](https://www.npmjs.com/package/postcss-modules) +- Class names and `:export` values become properties in a generated TypeScript interface +- The generated `.d.ts` exports a typed `styles` object as its default export + +**Global stylesheets** (extensions listed in `nonModuleFileExtensions`, default: `.global.sass`, `.global.scss`, `.global.css`): +- Compiled to plain CSS with no module scoping +- The generated `.d.ts` is a side-effect-only module (`export {}`) +- Useful for resets, themes, and base styles + +**Partials** (filenames starting with `_`): +- Never compiled to output files; they are only meant to be `@use`d or `@forward`ed by other files + +### Example: CSS module + +```scss +// src/Button.module.scss +.root { + background: blue; +} +.label { + font-size: 14px; +} +:export { + brandColor: #0078d4; +} +``` + +Generated `temp/sass-ts/Button.module.scss.d.ts`: + +```typescript +interface IStyles { + root: string; + label: string; + brandColor: string; +} +declare const styles: IStyles; +export default styles; +``` + +In your TypeScript source: + +```typescript +import styles from './Button.module.scss'; +// styles.root, styles.label, styles.brandColor are all typed strings +``` + +## Configuration reference + +All options are set in `config/sass.json`. Every option is optional. + +| Option | Default | Description | +|---|---|---| +| `srcFolder` | `"src/"` | Root directory that is scanned for SCSS files | +| `generatedTsFolder` | `"temp/sass-ts/"` | Output directory for generated `.d.ts` files | +| `secondaryGeneratedTsFolders` | `[]` | Additional directories to also write `.d.ts` files to (e.g. `"lib-esm"` when publishing typings alongside compiled output) | +| `exportAsDefault` | `true` | When `true`, wraps exports in a typed default interface. When `false`, generates individual named exports (`export const className: string`). Note: `false` is incompatible with `cssOutputFolders`. | +| `cssOutputFolders` | _(none)_ | Folders where compiled `.css` files are written. Each entry is either a plain folder path string, or an object with `folder` and optional `shimModuleFormat` (see below). | +| `fileExtensions` | `[".sass", ".scss", ".css"]` | File extensions to treat as CSS modules | +| `nonModuleFileExtensions` | `[".global.sass", ".global.scss", ".global.css"]` | File extensions to treat as global (non-module) stylesheets | +| `excludeFiles` | `[]` | Paths relative to `srcFolder` to skip entirely | +| `doNotTrimOriginalFileExtension` | `false` | When `true`, preserves the original extension in the CSS output filename. E.g. `styles.scss` → `styles.scss.css` instead of `styles.css`. Useful when downstream tooling needs to distinguish the source format. | +| `preserveIcssExports` | `false` | When `true`, keeps the `:export { }` block in the emitted CSS. This is needed when a webpack loader (e.g. `css-loader`'s `icssParser`) must extract `:export` values at bundle time. Has no effect on the generated `.d.ts`. | +| `silenceDeprecations` | `[]` | List of Sass deprecation codes to suppress (e.g. `"mixed-decls"`, `"import"`, `"global-builtin"`, `"color-functions"`) | +| `ignoreDeprecationsInDependencies` | `false` | Suppresses deprecation warnings that originate from `node_modules` dependencies | +| `extends` | _(none)_ | Path to another `sass.json` config file to inherit settings from | + +### CSS output folders and JS shims + +Each entry in `cssOutputFolders` can be a plain string (folder path only) or an object: + +```json +{ + "folder": "lib-esm", + "shimModuleFormat": "esnext" +} +``` + +When `shimModuleFormat` is set, the plugin writes a `.js` shim alongside each `.css` file. For a CSS module, the shim re-exports the CSS: + +```js +// ESM shim (shimModuleFormat: "esnext") +export { default } from "./Button.module.css"; + +// CommonJS shim (shimModuleFormat: "commonjs") +module.exports = require("./Button.module.css"); +module.exports.default = module.exports; +``` + +For a global stylesheet, the shim is a side-effect-only import: + +```js +// ESM shim +import "./global.global.css"; +export {}; + +// CommonJS shim +require("./global.global.css"); +``` + +## Sass import resolution + +The plugin supports the modern `pkg:` protocol for importing from npm packages: + +```scss +@use "pkg:@fluentui/react/dist/sass/variables"; +``` + +The legacy `~` prefix is automatically converted to `pkg:` for compatibility with older stylesheets: + +```scss +// These are equivalent: +@use "~@fluentui/react/dist/sass/variables"; +@use "pkg:@fluentui/react/dist/sass/variables"; +``` + +## Incremental builds + +The plugin tracks inter-file dependencies (via `@use`, `@forward`, and `@import`) and only recompiles files that changed or whose dependencies changed. This makes `heft build --watch` fast even in large projects. + +## Plugin accessor API + +Other Heft plugins can hook into the Sass compilation pipeline via the `ISassPluginAccessor` interface: + +```typescript +import { ISassPluginAccessor } from '@rushstack/heft-sass-plugin'; + +// In your plugin's apply() method: +const sassAccessor = session.requestAccessToPlugin( + '@rushstack/heft-sass-plugin', + 'sass-plugin', + '@rushstack/heft-sass-plugin' +); + +sassAccessor.hooks.postProcessCss.tapPromise('my-plugin', async (css, filePath) => { + // Transform CSS after Sass compilation but before it is written to cssOutputFolders + return transformedCss; +}); +``` + +The `postProcessCss` hook is an `AsyncSeriesWaterfallHook` that passes the compiled CSS string and source file path through each tap in sequence. diff --git a/heft-plugins/heft-sass-plugin/config/heft.json b/heft-plugins/heft-sass-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-sass-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-sass-plugin/config/jest.config.json b/heft-plugins/heft-sass-plugin/config/jest.config.json new file mode 100644 index 00000000000..d1749681d90 --- /dev/null +++ b/heft-plugins/heft-sass-plugin/config/jest.config.json @@ -0,0 +1,3 @@ +{ + "extends": "local-node-rig/profiles/default/config/jest.config.json" +} diff --git a/heft-plugins/heft-sass-plugin/config/rig.json b/heft-plugins/heft-sass-plugin/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/heft-plugins/heft-sass-plugin/config/rig.json +++ b/heft-plugins/heft-sass-plugin/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/heft-plugins/heft-sass-plugin/custom-typings/postcss-modules/index.d.ts b/heft-plugins/heft-sass-plugin/custom-typings/postcss-modules/index.d.ts deleted file mode 100644 index 265287e74d0..00000000000 --- a/heft-plugins/heft-sass-plugin/custom-typings/postcss-modules/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -// Add missing Definitely Typed typings -declare module 'postcss-modules'; diff --git a/heft-plugins/heft-sass-plugin/eslint.config.js b/heft-plugins/heft-sass-plugin/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/heft-plugins/heft-sass-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-sass-plugin/heft-plugin.json b/heft-plugins/heft-sass-plugin/heft-plugin.json index b4ac256a969..f077231c0d2 100644 --- a/heft-plugins/heft-sass-plugin/heft-plugin.json +++ b/heft-plugins/heft-sass-plugin/heft-plugin.json @@ -1,10 +1,10 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-plugin.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", "taskPlugins": [ { "pluginName": "sass-plugin", - "entryPoint": "./lib/SassPlugin" + "entryPoint": "./lib-commonjs/SassPlugin.js" } ] } diff --git a/heft-plugins/heft-sass-plugin/package.json b/heft-plugins/heft-sass-plugin/package.json index 7882f6e4868..09b3b76d1e2 100644 --- a/heft-plugins/heft-sass-plugin/package.json +++ b/heft-plugins/heft-sass-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/heft-sass-plugin", - "version": "0.11.0", + "version": "1.4.6", "description": "Heft plugin for SASS", "repository": { "type": "git", @@ -15,24 +15,50 @@ "_phase:build": "heft run --only build -- --clean", "_phase:test": "heft run --only test -- --clean" }, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "peerDependencies": { - "@rushstack/heft": "^0.51.0" + "@rushstack/heft": "^1.2.22" }, "dependencies": { - "@rushstack/heft-config-file": "workspace:*", "@rushstack/node-core-library": "workspace:*", - "@rushstack/typings-generator": "workspace:*", - "sass-embedded": "~1.62.0", - "postcss": "~8.4.6", - "postcss-modules": "~1.5.0" + "@types/tapable": "1.0.6", + "postcss": "~8.5.10", + "postcss-modules": "~6.0.0", + "sass-embedded": "~1.85.1", + "tapable": "1.1.3" }, "devDependencies": { "@microsoft/api-extractor": "workspace:*", - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "eslint": "~8.7.0" - } + "@rushstack/terminal": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "sideEffects": false } diff --git a/heft-plugins/heft-sass-plugin/src/SassPlugin.ts b/heft-plugins/heft-sass-plugin/src/SassPlugin.ts index 8494fff9b54..32d1ef5a746 100644 --- a/heft-plugins/heft-sass-plugin/src/SassPlugin.ts +++ b/heft-plugins/heft-sass-plugin/src/SassPlugin.ts @@ -1,174 +1,208 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { Path } from '@rushstack/node-core-library'; +import path from 'node:path'; + +import { AsyncSeriesWaterfallHook } from 'tapable'; + import type { HeftConfiguration, IHeftTaskSession, IHeftPlugin, - IScopedLogger, IHeftTaskRunHookOptions, IHeftTaskRunIncrementalHookOptions, - IWatchedFileState + IWatchedFileState, + ConfigurationFile } from '@rushstack/heft'; -import { ConfigurationFile } from '@rushstack/heft-config-file'; - -import { ISassConfiguration, SassProcessor } from './SassProcessor'; -export interface ISassConfigurationJson extends Partial {} +import { PLUGIN_NAME } from './constants'; +import { type ICssOutputFolder, type ISassProcessorOptions, SassProcessor } from './SassProcessor'; +import sassConfigSchema from './schemas/heft-sass-plugin.schema.json'; + +export interface ISassConfigurationJson { + srcFolder?: string; + generatedTsFolder?: string; + cssOutputFolders?: (string | ICssOutputFolder)[]; + secondaryGeneratedTsFolders?: string[]; + exportAsDefault?: boolean; + fileExtensions?: string[]; + nonModuleFileExtensions?: string[]; + silenceDeprecations?: string[]; + excludeFiles?: string[]; + doNotTrimOriginalFileExtension?: boolean; + preserveIcssExports?: boolean; + sourceMap?: boolean; +} -const PLUGIN_NAME: 'sass-plugin' = 'sass-plugin'; -const PLUGIN_SCHEMA_PATH: string = `${__dirname}/schemas/heft-sass-plugin.schema.json`; const SASS_CONFIGURATION_LOCATION: string = 'config/sass.json'; +const SASS_CONFIGURATION_FILE_SPECIFICATION: ConfigurationFile.IProjectConfigurationFileSpecification = + { + projectRelativeFilePath: SASS_CONFIGURATION_LOCATION, + jsonSchemaObject: sassConfigSchema + }; + +/** + * @public + */ +export interface ISassPluginAccessor { + readonly hooks: ISassPluginAccessorHooks; +} + +/** + * @public + */ +export interface ISassPluginAccessorHooks { + /** + * Hook that will be invoked after the CSS is generated but before it is written to a file. + */ + readonly postProcessCss: AsyncSeriesWaterfallHook; +} + export default class SassPlugin implements IHeftPlugin { - private static _sassConfigurationLoader: ConfigurationFile | undefined; - private _sassConfiguration: ISassConfiguration | undefined; - private _sassProcessor: SassProcessor | undefined; + public accessor: ISassPluginAccessor = { + hooks: { + postProcessCss: new AsyncSeriesWaterfallHook(['cssText']) + } + }; /** * Generate typings for Sass files before TypeScript compilation. */ public apply(taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration): void { - const slashNormalizedBuildFolderPath: string = Path.convertToSlashes(heftConfiguration.buildFolderPath); - - taskSession.hooks.run.tapPromise(PLUGIN_NAME, async (runOptions: IHeftTaskRunHookOptions) => { - await this._runSassTypingsGeneratorAsync( - taskSession, - heftConfiguration, - slashNormalizedBuildFolderPath - ); - }); - - taskSession.hooks.runIncremental.tapPromise( - PLUGIN_NAME, - async (runIncrementalOptions: IHeftTaskRunIncrementalHookOptions) => { - await this._runSassTypingsGeneratorAsync( - taskSession, - heftConfiguration, - slashNormalizedBuildFolderPath, - runIncrementalOptions - ); + const { numberOfCores, slashNormalizedBuildFolderPath } = heftConfiguration; + const { logger, tempFolderPath } = taskSession; + const { terminal } = logger; + const { + accessor: { hooks } + } = this; + + let sassProcessorPromise: Promise | undefined; + function initializeSassProcessorAsync(): Promise { + if (sassProcessorPromise) { + return sassProcessorPromise; } - ); - } - private async _runSassTypingsGeneratorAsync( - taskSession: IHeftTaskSession, - heftConfiguration: HeftConfiguration, - slashNormalizedBuildFolderPath: string, - runIncrementalOptions?: IHeftTaskRunIncrementalHookOptions - ): Promise { - taskSession.logger.terminal.writeVerboseLine('Starting sass typings generation...'); - const sassProcessor: SassProcessor = await this._loadSassProcessorAsync( - heftConfiguration, - slashNormalizedBuildFolderPath, - taskSession.logger - ); - // If we have the incremental options, use them to determine which files to process. - // Otherwise, process all files. The typings generator also provides the file paths - // as relative paths from the sourceFolderPath. - let changedRelativeFilePaths: string[] | undefined; - if (runIncrementalOptions) { - changedRelativeFilePaths = []; - const relativeFilePaths: Map = await runIncrementalOptions.watchGlobAsync( - sassProcessor.inputFileGlob, - { - cwd: sassProcessor.sourceFolderPath, - ignore: Array.from(sassProcessor.ignoredFileGlobs), - absolute: false + return (sassProcessorPromise = (async (): Promise => { + const sassConfigurationJson: ISassConfigurationJson | undefined = + await heftConfiguration.tryLoadProjectConfigurationFileAsync( + SASS_CONFIGURATION_FILE_SPECIFICATION, + terminal + ); + + const { + generatedTsFolder = 'temp/sass-ts', + srcFolder = 'src', + cssOutputFolders, + secondaryGeneratedTsFolders, + exportAsDefault = true, + fileExtensions, + nonModuleFileExtensions, + silenceDeprecations, + excludeFiles, + doNotTrimOriginalFileExtension, + preserveIcssExports, + sourceMap + } = sassConfigurationJson || {}; + + function resolveFolder(folder: string): string { + return path.resolve(slashNormalizedBuildFolderPath, folder); } - ); - for (const [relativePath, { changed }] of relativeFilePaths) { - if (changed) { - changedRelativeFilePaths.push(relativePath); - } - } + const sassProcessorOptions: ISassProcessorOptions = { + buildFolder: slashNormalizedBuildFolderPath, + concurrency: numberOfCores, + dtsOutputFolders: [generatedTsFolder, ...(secondaryGeneratedTsFolders || [])].map(resolveFolder), + logger, + exportAsDefault, + srcFolder: resolveFolder(srcFolder), + excludeFiles, + fileExtensions, + nonModuleFileExtensions, + cssOutputFolders: cssOutputFolders?.map((folder: string | ICssOutputFolder) => { + const folderPath: string = typeof folder === 'string' ? folder : folder.folder; + const shimModuleFormat: 'commonjs' | 'esnext' | undefined = + typeof folder === 'string' ? undefined : folder.shimModuleFormat; + return { + folder: resolveFolder(folderPath), + shimModuleFormat + }; + }), + silenceDeprecations, + doNotTrimOriginalFileExtension, + preserveIcssExports, + sourceMap, + postProcessCssAsync: hooks.postProcessCss.isUsed() + ? async (cssText: string) => hooks.postProcessCss.promise(cssText) + : undefined + }; + + const sassProcessor: SassProcessor = new SassProcessor(sassProcessorOptions); + await sassProcessor.loadCacheAsync(tempFolderPath); + + return sassProcessor; + })()); + } - if (changedRelativeFilePaths.length === 0) { + const compileFilesAsync = async ( + sassProcessor: SassProcessor, + files: Set, + changed: boolean + ): Promise => { + if (files.size === 0) { + terminal.writeLine(`No SCSS files to process.`); return; } - } - taskSession.logger.terminal.writeLine('Generating sass typings...'); - await sassProcessor.generateTypingsAsync(changedRelativeFilePaths); - taskSession.logger.terminal.writeLine('Generated sass typings'); - } + await sassProcessor.compileFilesAsync(files); + terminal.writeLine(`Finished compiling.`); + }; - private async _loadSassProcessorAsync( - heftConfiguration: HeftConfiguration, - slashNormalizedBuildFolderPath: string, - logger: IScopedLogger - ): Promise { - if (!this._sassProcessor) { - const sassConfiguration: ISassConfiguration = await this._loadSassConfigurationAsync( - heftConfiguration, - slashNormalizedBuildFolderPath, - logger - ); - this._sassProcessor = new SassProcessor({ - sassConfiguration, - buildFolder: slashNormalizedBuildFolderPath + taskSession.hooks.run.tapPromise(PLUGIN_NAME, async (runOptions: IHeftTaskRunHookOptions) => { + terminal.writeLine(`Starting...`); + const sassProcessor: SassProcessor = await initializeSassProcessorAsync(); + + terminal.writeVerboseLine(`Scanning for SCSS files...`); + const files: string[] = await runOptions.globAsync(sassProcessor.inputFileGlob, { + absolute: true, + ignore: sassProcessor.ignoredFileGlobs, + cwd: sassProcessor.sourceFolderPath }); - } - return this._sassProcessor; - } - private async _loadSassConfigurationAsync( - heftConfiguration: HeftConfiguration, - slashNormalizedBuildFolderPath: string, - logger: IScopedLogger - ): Promise { - if (!this._sassConfiguration) { - if (!SassPlugin._sassConfigurationLoader) { - SassPlugin._sassConfigurationLoader = new ConfigurationFile({ - projectRelativeFilePath: SASS_CONFIGURATION_LOCATION, - jsonSchemaPath: PLUGIN_SCHEMA_PATH - }); + const fileSet: Set = new Set(); + for (const file of files) { + // Using path.resolve to normalize slashes + fileSet.add(path.resolve(file)); } - const sassConfigurationJson: ISassConfigurationJson | undefined = - await SassPlugin._sassConfigurationLoader.tryLoadConfigurationFileForProjectAsync( - logger.terminal, - slashNormalizedBuildFolderPath, - heftConfiguration.rigConfig - ); - if (sassConfigurationJson) { - if (sassConfigurationJson.srcFolder) { - sassConfigurationJson.srcFolder = `${slashNormalizedBuildFolderPath}/${sassConfigurationJson.srcFolder}`; - } + await compileFilesAsync(sassProcessor, fileSet, false); + }); - if (sassConfigurationJson.generatedTsFolder) { - sassConfigurationJson.generatedTsFolder = `${slashNormalizedBuildFolderPath}/${sassConfigurationJson.generatedTsFolder}`; - } + taskSession.hooks.runIncremental.tapPromise( + PLUGIN_NAME, + async (runOptions: IHeftTaskRunIncrementalHookOptions) => { + terminal.writeLine(`Starting...`); + const sassProcessor: SassProcessor = await initializeSassProcessorAsync(); + + terminal.writeVerboseLine(`Scanning for changed SCSS files...`); + const changedFiles: Map = await runOptions.watchGlobAsync( + sassProcessor.inputFileGlob, + { + absolute: true, + cwd: sassProcessor.sourceFolderPath, + ignore: sassProcessor.ignoredFileGlobs + } + ); - function resolveFolderArray(folders: string[] | undefined): void { - if (folders) { - for (let i: number = 0; i < folders.length; i++) { - folders[i] = `${slashNormalizedBuildFolderPath}/${folders[i]}`; - } + const modifiedFiles: Set = new Set(); + for (const [file, { changed }] of changedFiles) { + if (changed) { + modifiedFiles.add(file); } } - resolveFolderArray(sassConfigurationJson.cssOutputFolders); - resolveFolderArray(sassConfigurationJson.secondaryGeneratedTsFolders); + await compileFilesAsync(sassProcessor, modifiedFiles, true); } - - // Set defaults if no configuration file or option was found - this._sassConfiguration = { - srcFolder: `${slashNormalizedBuildFolderPath}/src`, - generatedTsFolder: `${slashNormalizedBuildFolderPath}/temp/sass-ts`, - exportAsDefault: true, - fileExtensions: ['.sass', '.scss', '.css'], - importIncludePaths: [ - `${slashNormalizedBuildFolderPath}/node_modules`, - `${slashNormalizedBuildFolderPath}/src` - ], - ...sassConfigurationJson - }; - } - - return this._sassConfiguration; + ); } } diff --git a/heft-plugins/heft-sass-plugin/src/SassProcessor.ts b/heft-plugins/heft-sass-plugin/src/SassProcessor.ts index 676aa6eb54e..76a22befe69 100644 --- a/heft-plugins/heft-sass-plugin/src/SassProcessor.ts +++ b/heft-plugins/heft-sass-plugin/src/SassProcessor.ts @@ -1,51 +1,89 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -/// -import * as path from 'path'; -import { URL, pathToFileURL, fileURLToPath } from 'url'; -import { CompileResult, Syntax, Exception, compileStringAsync } from 'sass-embedded'; +import * as crypto from 'node:crypto'; +import * as path from 'node:path'; +import { URL, pathToFileURL, fileURLToPath } from 'node:url'; + +import { + type CompileResult, + type Syntax, + type Exception, + type CanonicalizeContext, + deprecations, + type Deprecations, + type DeprecationOrId, + type ImporterResult, + type AsyncCompiler, + type Options, + initAsyncCompiler +} from 'sass-embedded'; import * as postcss from 'postcss'; import cssModules from 'postcss-modules'; -import { FileSystem, Sort } from '@rushstack/node-core-library'; -import { IStringValueTypings, StringValuesTypingsGenerator } from '@rushstack/typings-generator'; + +import type { IScopedLogger } from '@rushstack/heft'; +import { + Async, + FileError, + FileSystem, + type IFileSystemWriteFileOptions, + Import, + type JsonObject, + Path, + RealNodeModulePathResolver, + Sort +} from '@rushstack/node-core-library'; + +const SIMPLE_IDENTIFIER_REGEX: RegExp = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/; /** * @public */ -export interface ISassConfiguration { +export interface ICssOutputFolder { + folder: string; + shimModuleFormat?: 'commonjs' | 'esnext'; +} + +/** + * @public + */ +export interface ISassProcessorOptions { /** - * Source code root directory. - * Defaults to "src/". + * The logger for this processor. */ - srcFolder?: string; + logger: IScopedLogger; /** - * Output directory for generated Sass typings. - * Defaults to "temp/sass-ts/". + * The project root folder. + */ + buildFolder: string; + + /** + * How many SASS compiler processes to run in parallel. */ - generatedTsFolder?: string; + concurrency: number; /** - * Optional additional folders to which Sass typings should be output. + * Source code root directory. + * Defaults to "src/". */ - secondaryGeneratedTsFolders?: string[]; + srcFolder: string; /** - * Output directories for compiled CSS + * Output directory for generated Sass typings. + * Defaults to "temp/sass-ts/". */ - cssOutputFolders?: string[] | undefined; + dtsOutputFolders: string[]; /** - * If `true`, when emitting compiled CSS from a file with a ".scss" extension, the emitted CSS will have the extension ".scss" instead of ".scss.css" + * Output kinds for generated JS stubs and CSS files. */ - preserveSCSSExtension?: boolean | undefined; + cssOutputFolders?: ICssOutputFolder[]; /** * Determines whether export values are wrapped in a default property, or not. - * Defaults to true. */ - exportAsDefault?: boolean; + exportAsDefault: boolean; /** * Files with these extensions will pass through the Sass transpiler for typings generation. @@ -62,16 +100,46 @@ export interface ISassConfiguration { nonModuleFileExtensions?: string[]; /** - * A list of paths used when resolving Sass `@imports` and `@use`. - * The paths should be relative to the project root. - * Defaults to ["node_modules", "src"] + * A list of file paths relative to the "src" folder that should be excluded from typings generation. */ - importIncludePaths?: string[]; + excludeFiles?: string[]; /** - * A list of file paths relative to the "src" folder that should be excluded from typings generation. + * If set, deprecation warnings from dependencies will be suppressed. */ - excludeFiles?: string[]; + ignoreDeprecationsInDependencies?: boolean; + + /** + * A list of deprecation codes to silence. This is useful for suppressing warnings from deprecated Sass features that are used in the project and known not to be a problem. + */ + silenceDeprecations?: readonly string[]; + + /** + * If true, the original file extension will not be trimmed when generating the output CSS file. The generated CSS + * file will retain its original extension. For example, "styles.scss" will generate "styles.scss.css" + * instead of "styles.css". + */ + doNotTrimOriginalFileExtension?: boolean; + + /** + * If true, the ICSS `:export` block will be preserved in the emitted CSS output. This is necessary + * when the CSS is consumed by a webpack loader (e.g. css-loader's icssParser) that extracts `:export` + * values at bundle time to generate JavaScript exports. + * + * Defaults to false. + */ + preserveIcssExports?: boolean; + + /** + * If true, a .css.map source map file will be written next to each emitted .css, and a + * sourceMappingURL comment will be appended to the .css. Defaults to false. + */ + sourceMap?: boolean; + + /** + * A callback to further modify the raw CSS text after it has been generated. Only relevant if emitting CSS files. + */ + postProcessCssAsync?: (cssText: string) => Promise; } /** @@ -79,185 +147,807 @@ export interface ISassConfiguration { */ export interface ISassTypingsGeneratorOptions { buildFolder: string; - sassConfiguration: ISassConfiguration; + sassConfiguration: ISassProcessorOptions; } -interface IClassMap { - [className: string]: string; +interface IFileRecord { + absolutePath: string; + url: URL; + index: number; + isPartial: boolean; + isModule: boolean; + relativePath: string; + version: string; + content: string | undefined; + cssVersion?: string; + consumers: Set; + dependencies: Set; +} + +interface ISerializedFileRecord { + relativePath: string; + version: string; + cssVersion?: string | undefined; + dependencies: number[]; } /** - * Generates type files (.d.ts) for Sass/SCSS/CSS files and optionally produces CSS files. + * Regexp to match legacy node_modules imports in SCSS files. + * Old syntax that this is matching is expressions like `@import '~@fluentui/react/dist/sass/blah.scss';` + * These should instead be written as `@import 'pkg:@fluentui/react/dist/sass/blah';` (note that `@import` is deprecated) + * Newest should prefer `@use` or `@forward` statements since those are designed to work with scss in a module fashion. + */ +const importTildeRegex: RegExp = /^(\s*@(?:import|use|forward)\s*)('~(?:[^']+)'|"~(?:[^"]+)")/gm; + +// eslint-disable-next-line @rushstack/no-new-null +type SyncResolution = URL | null; +type AsyncResolution = Promise; +type SyncOrAsyncResolution = SyncResolution | AsyncResolution; + +interface IFileContentAndVersion { + content: string; + version: string; +} + +/** + * Generates type files (.d.ts) for Sass/SCSS/CSS files and optionally produces CSS files and .scss.js redirector files. * * @public */ -export class SassProcessor extends StringValuesTypingsGenerator { - /** - * @param buildFolder - The project folder to search for Sass files and - * generate typings. - */ - public constructor(options: ISassTypingsGeneratorOptions) { - const { buildFolder, sassConfiguration } = options; - const srcFolder: string = sassConfiguration.srcFolder || `${buildFolder}/src`; - const generatedTsFolder: string = sassConfiguration.generatedTsFolder || `${buildFolder}/temp/sass-ts`; - const exportAsDefault: boolean = - sassConfiguration.exportAsDefault === undefined ? true : sassConfiguration.exportAsDefault; - const exportAsDefaultInterfaceName: string = 'IExportStyles'; - - const { allFileExtensions, isFileModule } = buildExtensionClassifier(sassConfiguration); - - const { cssOutputFolders, preserveSCSSExtension = false } = sassConfiguration; - - const getCssPaths: ((relativePath: string) => string[]) | undefined = cssOutputFolders - ? (relativePath: string): string[] => { - const lastDot: number = relativePath.lastIndexOf('.'); - const oldExtension: string = relativePath.slice(lastDot); - const cssRelativePath: string = - oldExtension === '.css' || (oldExtension === '.scss' && preserveSCSSExtension) - ? relativePath - : `${relativePath}.css`; - - const cssPaths: string[] = []; - for (const outputFolder of cssOutputFolders) { - cssPaths.push(`${outputFolder}/${cssRelativePath}`); +export class SassProcessor { + public readonly ignoredFileGlobs: string[] | undefined; + public readonly inputFileGlob: string; + public readonly sourceFolderPath: string; + + // Map of input file path -> record + private readonly _fileInfo: Map; + private readonly _resolutions: Map; + + private readonly _isFileModule: (filePath: string) => boolean; + private readonly _options: ISassProcessorOptions; + private readonly _realpathSync: (path: string) => string; + private readonly _scssOptions: Options<'async'>; + + private _configFilePath: string | undefined; + + public constructor(options: ISassProcessorOptions) { + const { silenceDeprecations, excludeFiles } = options; + + const { isFileModule, allFileExtensions } = buildExtensionClassifier(options); + + const deprecationsToSilence: DeprecationOrId[] | undefined = silenceDeprecations + ? Array.from(silenceDeprecations, (deprecation) => { + if (!Object.prototype.hasOwnProperty.call(deprecations, deprecation)) { + throw new Error(`Unknown deprecation code: ${deprecation}`); } - return cssPaths; - } + return deprecation as keyof Deprecations; + }) : undefined; - super({ - srcFolder, - generatedTsFolder, - exportAsDefault, - exportAsDefaultInterfaceName, - fileExtensions: allFileExtensions, - filesToIgnore: sassConfiguration.excludeFiles, - secondaryGeneratedTsFolders: sassConfiguration.secondaryGeneratedTsFolders, - - getAdditionalOutputFiles: getCssPaths, - - // Generate typings function - parseAndGenerateTypings: async (fileContents: string, filePath: string, relativePath: string) => { - if (this._isSassPartial(filePath)) { - // Do not generate typings for Sass partials. - return; - } + const canonicalizeAsync: (url: string, context: CanonicalizeContext) => AsyncResolution = async ( + url, + context + ) => { + return await this._canonicalizeAsync(url, context); + }; - const isModule: boolean = isFileModule(relativePath); + const loadAsync: (url: URL) => Promise = async (url) => { + const absolutePath: string = heftUrlToPath(url.href); + const record: IFileRecord = this._getOrCreateRecord(absolutePath); + if (record.content === undefined) { + const { content, version } = await this._readFileContentAsync(absolutePath); + record.version = version; + record.content = content; + } - const css: string = await this._transpileSassAsync( - fileContents, - filePath, - buildFolder, - sassConfiguration.importIncludePaths - ); + return { + contents: record.content, + syntax: determineSyntaxFromFilePath(absolutePath), + // Without sourceMapUrl, sass-embedded falls back to a data: URL for this file in the + // source map. data: URLs crash heftUrlToPath on Linux/macOS (non-empty URL host). + sourceMapUrl: url + }; + }; - let classMap: IClassMap = {}; - - if (isModule) { - // Not all input files are SCSS modules - const cssModulesClassMapPlugin: postcss.Plugin = cssModules({ - getJSON: (cssFileName: string, json: IClassMap) => { - // This callback will be invoked during the promise evaluation of the postcss process() function. - classMap = json; - }, - // Avoid unnecessary name hashing. - generateScopedName: (name: string) => name - }); + this.ignoredFileGlobs = excludeFiles?.map((excludedFile) => + excludedFile.startsWith('./') ? excludedFile.slice(2) : excludedFile + ); + this.inputFileGlob = `**/*+(${allFileExtensions.join('|')})`; + this.sourceFolderPath = options.srcFolder; - await postcss.default([cssModulesClassMapPlugin]).process(css, { from: filePath }); + this._configFilePath = undefined; + this._fileInfo = new Map(); + this._isFileModule = isFileModule; + this._resolutions = new Map(); + this._options = options; + this._realpathSync = new RealNodeModulePathResolver().realNodeModulePath; + this._scssOptions = { + style: 'expanded', // leave minification to clean-css + importers: [ + { + nonCanonicalScheme: 'pkg', + canonicalize: canonicalizeAsync, + load: loadAsync } + ], + silenceDeprecations: deprecationsToSilence, + ...(options.sourceMap && { sourceMap: true, sourceMapIncludeSources: true }) + }; + } - if (getCssPaths) { - await Promise.all( - getCssPaths(relativePath).map(async (cssFile: string) => { - // The typings generator processes files serially and the number of output folders is expected to be small, - // thus throttling here is not currently a concern. - await FileSystem.writeFileAsync(cssFile, css, { - ensureFolderExists: true - }); - }) - ); + public async loadCacheAsync(tempFolderPath: string): Promise { + const configHash: string = getContentsHash('sass.json', JSON.stringify(this._options)).slice(0, 8); + + this._configFilePath = path.join(tempFolderPath, `sass_${configHash}.json`); + + try { + const serializedConfig: string = await FileSystem.readFileAsync(this._configFilePath); + this._cache = serializedConfig; + } catch (err) { + if (!FileSystem.isNotExistError(err)) { + this._options.logger.terminal.writeVerboseLine(`Error reading cache file: ${err}`); + } + } + } + + public async compileFilesAsync(filepaths: Set): Promise { + // Incremental resolve is complicated, so just clear it for now + this._resolutions.clear(); + + // Expand affected files using dependency graph + // If this is the initial compilation, the graph will be empty, so this will no-op' + const affectedRecords: Set = new Set(); + + for (const file of filepaths) { + const record: IFileRecord = this._getOrCreateRecord(file); + affectedRecords.add(record); + } + + const { + concurrency, + logger: { terminal } + } = this._options; + + terminal.writeVerboseLine(`Checking for changes to ${filepaths.size} files...`); + for (const record of affectedRecords) { + for (const dependency of record.dependencies) { + affectedRecords.add(dependency); + } + } + + // Check the versions of all requested files and their dependencies + await Async.forEachAsync( + affectedRecords, + async (record: IFileRecord) => { + const contentAndVersion: IFileContentAndVersion = await this._readFileContentAsync( + record.absolutePath + ); + const { version } = contentAndVersion; + if (version !== record.version) { + record.content = contentAndVersion.content; + record.version = version; + } else { + // If the record was just hydrated from disk, content won't be present + record.content ??= contentAndVersion.content; + affectedRecords.delete(record); } + }, + { + concurrency + } + ); + + for (const record of affectedRecords) { + const consumers: Set = record.consumers; + for (const consumer of consumers) { + // Adding to the set while we are iterating it acts as a deduped queue + affectedRecords.add(consumer); + } + } + + for (const record of affectedRecords) { + if (record.isPartial) { + // Filter out partials before compilation so we don't pay async overhead when skipping them + affectedRecords.delete(record); + } + } + + terminal.writeLine(`Compiling ${affectedRecords.size} files...`); + + // Compile the files. Allow parallelism + if (affectedRecords.size) { + // Using `>>2` instead of `/4` because it also ensures that the result is an integer + const compilerCount: number = Math.min(affectedRecords.size >> 2, concurrency, 8) || 1; + const compilers: AsyncCompiler[] = await Promise.all( + Array.from({ length: compilerCount }, () => initAsyncCompiler()) + ); + + try { + await Async.forEachAsync( + affectedRecords, + async (record, i) => { + try { + await this._compileFileAsync(compilers[i % compilerCount], record, this._scssOptions); + } catch (err) { + this._options.logger.emitError(err); + } + }, + { + concurrency: compilerCount * 4 + } + ); + } finally { + await Promise.all(compilers.map((compiler) => compiler.dispose())); + } + } + + // Find all newly-referenced files and update the incremental build state data. + const newRecords: Set = new Set(); + for (const record of this._fileInfo.values()) { + if (!record.version) { + newRecords.add(record); + } + } + + await Async.forEachAsync( + newRecords, + async (record: IFileRecord) => { + const { content, version } = await this._readFileContentAsync(record.absolutePath); + // eslint-disable-next-line require-atomic-updates + record.content = content; + // eslint-disable-next-line require-atomic-updates + record.version = version; + }, + { + concurrency + } + ); + + if (this._configFilePath) { + const serializedConfig: string = this._cache; + try { + await FileSystem.writeFileAsync(this._configFilePath, serializedConfig, { + ensureFolderExists: true + }); + } catch (err) { + terminal.writeVerboseLine(`Error writing cache file: ${err}`); + } + } + } + + /** + * Resolves a `heft:` URL to a physical file path. + * @param url - The URL to canonicalize. Will only do the exact URL or the corresponding partial. + * @param context - The context in which the canonicalization is being performed + * @returns The canonical URL of the target file, or null if it does not resolve + */ + private async _canonicalizeFileAsync(url: string, context: CanonicalizeContext): AsyncResolution { + // The logic between `this._resolutions.get()` and `this._resolutions.set()` must be 100% synchronous + // Otherwise we could end up with multiple promises for the same URL + let resolution: SyncOrAsyncResolution | undefined = this._resolutions.get(url); + if (resolution === undefined) { + resolution = this._canonicalizeFileInnerAsync(url, context); + this._resolutions.set(url, resolution); + } + return await resolution; + } + + private async _canonicalizeFileInnerAsync(url: string, context: CanonicalizeContext): AsyncResolution { + const absolutePath: string = heftUrlToPath(url); + const lastSlash: number = url.lastIndexOf('/'); + const basename: string = url.slice(lastSlash + 1); + + // Does this file exist? + try { + const contentAndVersion: IFileContentAndVersion = await this._readFileContentAsync(absolutePath); + const record: IFileRecord = this._getOrCreateRecord(absolutePath); + const { version } = contentAndVersion; + if (version !== record.version) { + record.content = contentAndVersion.content; + record.version = version; + } else { + record.content ??= contentAndVersion.content; + } + return record.url; + } catch (err) { + if (!FileSystem.isNotExistError(err)) { + throw err; + } + } + + // Exact file didn't exist, was this a partial? + if (basename.startsWith('_')) { + // Was already a partial, so fail resolution. + return null; + } + + // Try again with the partial + const dirname: string = url.slice(0, lastSlash); + const partialUrl: string = `${dirname}/_${basename}`; + const result: SyncResolution = await this._canonicalizeFileAsync(partialUrl, context); + return result; + } + + /** + * Resolves a `pkg:` URL to a physical file path. + * @param url - The URL to canonicalize. The URL must be a deep import to a SCSS file in a separate package. + * @param context - The context in which the canonicalization is being performed + * @returns The canonical URL of the target file, or null if it does not resolve + */ + private async _canonicalizePackageAsync(url: string, context: CanonicalizeContext): AsyncResolution { + // We rewrite any of the old form `~` imports to `pkg:` + const { containingUrl } = context; + if (!containingUrl) { + throw new Error(`Cannot resolve ${url} without a containing URL`); + } + + const cacheKey: string = `${containingUrl.href}\0${url}`; + // The logic between `this._resolutions.get()` and `this._resolutions.set()` must be 100% synchronous + // Otherwise we could end up with multiple promises for the same URL + let resolution: SyncOrAsyncResolution | undefined = this._resolutions.get(cacheKey); + if (resolution === undefined) { + // Since the cache doesn't have an entry, get the promise for the resolution + // and inject it into the cache before other callers have a chance to try + resolution = this._canonicalizePackageInnerAsync(url, context); + this._resolutions.set(cacheKey, resolution); + } + return await resolution; + } - const sortedClassNames: string[] = Object.keys(classMap).sort(); + /** + * Resolves a `pkg:` URL to a physical file path, without caching. + * @param url - The URL to canonicalize. The URL must be a deep import to a SCSS file in a separate package. + * @param context - The context in which the canonicalization is being performed + * @returns The canonical URL of the target file, or null if it does not resolve + */ + private async _canonicalizePackageInnerAsync(url: string, context: CanonicalizeContext): AsyncResolution { + const containingUrl: string | undefined = context.containingUrl?.href; + if (containingUrl === undefined) { + throw new Error(`Cannot resolve ${url} without a containing URL`); + } + + const nodeModulesQuery: string = url.slice(4); + const isScoped: boolean = nodeModulesQuery.startsWith('@'); + let linkEnd: number = nodeModulesQuery.indexOf('/'); + if (isScoped) { + linkEnd = nodeModulesQuery.indexOf('/', linkEnd + 1); + } + if (linkEnd < 0) { + linkEnd = nodeModulesQuery.length; + } + + const packageName: string = nodeModulesQuery.slice(0, linkEnd); + const baseFolderPath: string = heftUrlToPath(containingUrl); + + const resolvedPackagePath: string = await Import.resolvePackageAsync({ + packageName, + baseFolderPath, + getRealPath: this._realpathSync + }); + const modulePath: string = nodeModulesQuery.slice(linkEnd); + const resolvedPath: string = `${resolvedPackagePath}${modulePath}`; + const heftUrl: string = pathToHeftUrl(resolvedPath).href; + return await this._canonicalizeHeftUrlAsync(heftUrl, context); + } + + /** + * Resolves a `heft:` URL to a physical file path. + * @param url - The URL to canonicalize. The URL must be a deep import to a candidate SCSS file. + * @param context - The context in which the canonicalization is being performed + * @returns The canonical URL of the target file, or null if it does not resolve + */ + private async _canonicalizeHeftUrlAsync(url: string, context: CanonicalizeContext): AsyncResolution { + // The logic between `this._resolutions.get()` and `this._resolutions.set()` must be 100% synchronous + let resolution: SyncOrAsyncResolution | undefined = this._resolutions.get(url); + if (resolution === undefined) { + // Since the cache doesn't have an entry, get the promise for the resolution + // and inject it into the cache before other callers have a chance to try + resolution = this._canonicalizeHeftInnerAsync(url, context); + this._resolutions.set(url, resolution); + } + + return await resolution; + } + + /** + * Resolves a sass request to a physical path + * @param url - The URL to canonicalize. The URL may be relative or absolute. + * This API supports the `heft:` and `pkg:` protocols. + * @param context - The context in which the canonicalization is being performed + * @returns The canonical URL of the target file, or null if it does not resolve + */ + private async _canonicalizeAsync(url: string, context: CanonicalizeContext): AsyncResolution { + if (url.startsWith('~')) { + throw new Error(`Unexpected tilde in URL: ${url} in context: ${context.containingUrl?.href}`); + } - const sassTypings: IStringValueTypings = { - typings: sortedClassNames.map((exportName: string) => { - return { - exportName - }; - }) - }; + if (url.startsWith('pkg:')) { + return await this._canonicalizePackageAsync(url, context); + } - return sassTypings; + // Check the cache first, and exit early if previously resolved + if (url.startsWith('heft:')) { + return await this._canonicalizeHeftUrlAsync(url, context); + } + + const { containingUrl } = context; + if (!containingUrl) { + throw new Error(`Cannot resolve ${url} without a containing URL`); + } + + const resolvedUrl: string = new URL(url, containingUrl.toString()).toString(); + return await this._canonicalizeHeftUrlAsync(resolvedUrl, context); + } + + /** + * Resolves a `heft:` URL to a physical file path, without caching. + * @param url - The URL to canonicalize. The URL must be a deep import to a candidate SCSS file. + * @param context - The context in which the canonicalization is being performed + * @returns The canonical URL of the target file, or null if it does not resolve + */ + private async _canonicalizeHeftInnerAsync(url: string, context: CanonicalizeContext): AsyncResolution { + if (url.endsWith('.sass') || url.endsWith('.scss') || url.endsWith('.css')) { + // Extension is already present, so only try the exact URL or the corresponding partial + return await this._canonicalizeFileAsync(url, context); + } + + // Spec says prefer .sass, but we don't use that extension. + // Plain `.css` is tried last, matching dart-sass's resolution order. + for (const candidate of [ + `${url}.scss`, + `${url}.sass`, + `${url}.css`, + `${url}/index.scss`, + `${url}/index.sass`, + `${url}/index.css` + ]) { + const result: SyncResolution = await this._canonicalizeFileAsync(candidate, context); + if (result) { + return result; } + } + + return null; + } + + private get _cache(): string { + const serializedRecords: ISerializedFileRecord[] = Array.from(this._fileInfo.values(), (record) => { + return { + relativePath: record.relativePath, + version: record.version, + cssVersion: record.cssVersion, + dependencies: Array.from(record.dependencies, (dependency) => dependency.index) + }; }); + + return JSON.stringify(serializedRecords); } /** - * Sass partial files are snippets of CSS meant to be included in other Sass files. - * Partial filenames always begin with a leading underscore and do not produce a CSS output file. + * Configures the state of this processor using the specified cache file content. + * @param cacheFileContent - The contents of the cache file */ - private _isSassPartial(filePath: string): boolean { - return path.basename(filePath)[0] === '_'; + private set _cache(cacheFileContent: string) { + this._fileInfo.clear(); + + const serializedRecords: ISerializedFileRecord[] = JSON.parse(cacheFileContent); + const records: IFileRecord[] = []; + const buildFolder: string = this._options.buildFolder; + for (const record of serializedRecords) { + const { relativePath, version, cssVersion } = record; + // relativePath may start with `../` or similar, so need to use a library join function. + const absolutePath: string = path.resolve(buildFolder, relativePath); + const url: URL = pathToHeftUrl(absolutePath); + + const isPartial: boolean = isSassPartial(absolutePath); + // SCSS partials are not modules, insofar as they cannot be imported directly. + const isModule: boolean = isPartial ? false : this._isFileModule(absolutePath); + + const fileRecord: IFileRecord = { + absolutePath, + url, + isPartial, + isModule, + index: records.length, + relativePath, + version, + content: undefined, + cssVersion, + consumers: new Set(), + dependencies: new Set() + }; + records.push(fileRecord); + this._fileInfo.set(absolutePath, fileRecord); + this._resolutions.set(absolutePath, url); + } + + for (let i: number = 0, len: number = serializedRecords.length; i < len; i++) { + const serializedRecord: ISerializedFileRecord = serializedRecords[i]; + const record: IFileRecord = records[i]; + + for (const dependencyIndex of serializedRecord.dependencies) { + const dependency: IFileRecord = records[dependencyIndex]; + record.dependencies.add(dependency); + dependency.consumers.add(record); + } + } + } + + /** + * Reads the contents of a file and returns an object that can be used to access the text and hash of the file. + * @param absolutePath - The absolute path to the file + * @returns A promise for an object that can be used to access the text and hash of the file. + */ + private async _readFileContentAsync(absolutePath: string): Promise { + const content: Buffer = await FileSystem.readFileToBufferAsync(absolutePath); + let version: string | undefined; + let contentString: string | undefined; + return { + get version() { + version ??= crypto.createHash('sha1').update(content).digest('base64'); + return version; + }, + get content() { + contentString ??= preprocessScss(content); + return contentString; + } + }; } - private async _transpileSassAsync( - fileContents: string, - filePath: string, - buildFolder: string, - importIncludePaths: string[] | undefined - ): Promise { + /** + * Gets a record for a SCSS file, creating it if necessary. + * @param filePath - The file path to get or create a record for + * @returns The tracking record for the specified file + */ + private _getOrCreateRecord(filePath: string): IFileRecord { + filePath = path.resolve(filePath); + let record: IFileRecord | undefined = this._fileInfo.get(filePath); + if (!record) { + const isPartial: boolean = isSassPartial(filePath); + const isModule: boolean = isPartial ? false : this._isFileModule(filePath); + const url: URL = pathToHeftUrl(filePath); + record = { + absolutePath: filePath, + url, + isPartial, + isModule, + index: this._fileInfo.size, + relativePath: Path.convertToSlashes(path.relative(this._options.buildFolder, filePath)), + version: '', + content: undefined, + cssVersion: undefined, + consumers: new Set(), + dependencies: new Set() + }; + this._resolutions.set(filePath, record.url); + this._fileInfo.set(filePath, record); + } + return record; + } + + private async _compileFileAsync( + compiler: Pick, + record: IFileRecord, + scssOptions: Options<'async'> + ): Promise { + const sourceFilePath: string = record.absolutePath; + const content: string | undefined = record.content; + if (content === undefined) { + throw new Error(`Content not loaded for ${sourceFilePath}`); + } + let result: CompileResult; - const nodeModulesUrl: URL = pathToFileURL(`${buildFolder}/node_modules/`); try { - result = await compileStringAsync(fileContents, { - importers: [ - { - findFileUrl: (url: string): URL | null => { - return this._patchSassUrl(url, nodeModulesUrl); - } - } - ], - url: pathToFileURL(filePath), - loadPaths: importIncludePaths - ? importIncludePaths - : [`${buildFolder}/node_modules`, `${buildFolder}/src`], - syntax: determineSyntaxFromFilePath(filePath) + result = await compiler.compileStringAsync(content, { + ...scssOptions, + url: record.url, + syntax: determineSyntaxFromFilePath(sourceFilePath) }); } catch (err) { const typedError: Exception = err; const { span } = typedError; - // Extract location information and format into the error message until we have a concept - // of location-aware diagnostics in Heft. - throw new Error(`${typedError}(${span.start.column},${span.start.line}): ${typedError.message}`); + throw new FileError(`${typedError.sassMessage}\n${span.context ?? span.text}${typedError.sassStack}`, { + absolutePath: span.url ? heftUrlToPath(span.url.href ?? span.url) : 'unknown', // This property should always be present + line: span.start.line, + column: span.start.column, + projectFolder: this._options.buildFolder + }); } - // Register any @import, @use files as dependencies. + // Register any @import files as dependencies. + record.dependencies.clear(); for (const dependency of result.loadedUrls) { - this.registerDependency(filePath, fileURLToPath(dependency as URL)); + const dependencyPath: string = heftUrlToPath(dependency.href); + const dependencyRecord: IFileRecord = this._getOrCreateRecord(dependencyPath); + record.dependencies.add(dependencyRecord); + dependencyRecord.consumers.add(record); + } + + let css: string = result.css.toString(); + const contentHash: string = getContentsHash(sourceFilePath, css); + if (record.cssVersion === contentHash) { + // The CSS has not changed, so don't reprocess this and downstream files. + return; + } + + record.cssVersion = contentHash; + const { + cssOutputFolders, + dtsOutputFolders, + srcFolder, + exportAsDefault, + doNotTrimOriginalFileExtension, + postProcessCssAsync, + preserveIcssExports, + sourceMap + } = this._options; + + // Handle CSS modules + let moduleMap: JsonObject | undefined; + if (record.isModule) { + const postCssModules: postcss.Plugin = cssModules({ + getJSON: (cssFileName: string, json: JsonObject) => { + // This callback will be invoked during the promise evaluation of the postcss process() function. + moduleMap = json; + }, + // Avoid unnecessary name hashing. + generateScopedName: (name: string) => name + }); + + const postCssResult: postcss.Result = await postcss + .default([postCssModules]) + .process(css, { from: sourceFilePath }); + + if (!preserveIcssExports) { + // Default behavior: use the transformed CSS output, which has the :export block stripped. + css = postCssResult.css; + } + // If preserveIcssExports is true, we discard the transformed CSS and keep the original so + // that the :export block remains in the output for downstream webpack loaders (e.g. + // css-loader's icssParser) that extract :export values at bundle time. + } + + if (postProcessCssAsync) { + css = await postProcessCssAsync(css); } - return result.css.toString(); + const relativeFilePath: string = path.relative(srcFolder, sourceFilePath); + + // A module file with no local class exports (e.g. only :global styles) has no + // default export at runtime, so treat it as a side-effect-only import just like + // a non-module file. + const hasModuleExports: boolean | undefined = moduleMap && Object.keys(moduleMap).length > 0; + const dtsContent: string = createDTS(moduleMap, exportAsDefault, hasModuleExports); + + const writeFileOptions: IFileSystemWriteFileOptions = { + ensureFolderExists: true + }; + + for (const dtsOutputFolder of dtsOutputFolders) { + await FileSystem.writeFileAsync( + path.resolve(dtsOutputFolder, `${relativeFilePath}.d.ts`), + dtsContent, + writeFileOptions + ); + } + + if (cssOutputFolders && cssOutputFolders.length > 0) { + if (!exportAsDefault) { + throw new Error(`The "cssOutputFolders" option is not supported when "exportAsDefault" is false.`); + } + + const filename: string = path.basename(relativeFilePath); + let cssFilename: string; + let relativeCssPath: string; + if (doNotTrimOriginalFileExtension) { + cssFilename = `${filename}.css`; + relativeCssPath = `${relativeFilePath}.css`; + } else { + const extensionStart: number = filename.lastIndexOf('.'); + cssFilename = `${filename.slice(0, extensionStart)}.css`; + + const relativeFilePathStart: number = relativeFilePath.lastIndexOf('.'); + relativeCssPath = `${relativeFilePath.slice(0, relativeFilePathStart)}.css`; + } + + const cssPathFromJs: string = `./${cssFilename}`; + + // When sourceMap is enabled, prepare the annotated CSS and map basename once — + // cssFilename is identical across all output folders. + const cssMapBasename: string | undefined = + sourceMap && result.sourceMap ? `${cssFilename}.map` : undefined; + const finalCss: string = cssMapBasename ? `${css}\n/*# sourceMappingURL=${cssMapBasename} */\n` : css; + + for (const cssOutputFolder of cssOutputFolders) { + const { folder, shimModuleFormat } = cssOutputFolder; + + const cssFilePath: string = path.resolve(folder, relativeCssPath); + await FileSystem.writeFileAsync(cssFilePath, finalCss, writeFileOptions); + + if (cssMapBasename && result.sourceMap) { + const mapFilePath: string = `${cssFilePath}.map`; + const mapDir: string = path.dirname(cssFilePath); + // Rewrite heft: URL sources to paths relative to the map file's directory + // so that source-map-loader can resolve them back to the original .scss. + const rewrittenSources: string[] = result.sourceMap.sources.map((source) => { + if (!source.startsWith('heft:')) return source; + const absoluteSourcePath: string = heftUrlToPath(source); + return Path.convertToSlashes(path.relative(mapDir, absoluteSourcePath)); + }); + await FileSystem.writeFileAsync( + mapFilePath, + JSON.stringify({ ...result.sourceMap, file: cssFilename, sources: rewrittenSources }), + writeFileOptions + ); + } + + if (shimModuleFormat && !filename.endsWith('.css')) { + const jsFilePath: string = path.resolve(folder, `${relativeFilePath}.js`); + const jsShimContent: string = generateJsShimContent( + shimModuleFormat, + cssPathFromJs, + hasModuleExports + ); + await FileSystem.writeFileAsync(jsFilePath, jsShimContent, writeFileOptions); + } + } + } } +} - private _patchSassUrl(url: string, nodeModulesUrl: URL): URL | null { - if (url[0] !== '~') { - return null; +function createDTS( + moduleMap: JsonObject | undefined, + exportAsDefault: boolean, + hasModuleExports: boolean | undefined +): string; +function createDTS(moduleMap: JsonObject, exportAsDefault: boolean, hasModuleExports: true): string; +function createDTS( + moduleMap: JsonObject | undefined, + exportAsDefault: boolean, + hasModuleExports: boolean | undefined +): string { + if (hasModuleExports) { + // Create a source file. + const source: string[] = []; + + if (exportAsDefault) { + source.push(`declare interface IStyles {`); + for (const className of Object.keys(moduleMap)) { + const safeClassName: string = SIMPLE_IDENTIFIER_REGEX.test(className) + ? className + : JSON.stringify(className); + // Quote and escape class names as needed. + source.push(` ${safeClassName}: string;`); + } + + source.push(`}`); + source.push(`declare const styles: IStyles;`); + source.push(`export default styles;`); + } else { + for (const className of Object.keys(moduleMap)) { + if (!SIMPLE_IDENTIFIER_REGEX.test(className)) { + throw new Error( + `Class name "${className}" is not a valid identifier and may only be exported using "exportAsDefault: true"` + ); + } + + source.push(`export const ${className}: string;`); + } } - return new URL(url.slice(1), nodeModulesUrl); + return source.join('\n'); + } else { + return `export {};`; } } -interface IExtensionClassifierResult { +interface IExtensionClassifier { allFileExtensions: string[]; isFileModule: (relativePath: string) => boolean; } -function buildExtensionClassifier(sassConfiguration: ISassConfiguration): IExtensionClassifierResult { +function buildExtensionClassifier(sassConfiguration: ISassProcessorOptions): IExtensionClassifier { const { fileExtensions: moduleFileExtensions = ['.sass', '.scss', '.css'], nonModuleFileExtensions = ['.global.sass', '.global.scss', '.global.css'] @@ -272,6 +962,7 @@ function buildExtensionClassifier(sassConfiguration: ISassConfiguration): IExten isFileModule: (relativePath: string) => false }; } + if (!hasNonModules) { return { allFileExtensions: moduleFileExtensions, @@ -318,8 +1009,63 @@ function buildExtensionClassifier(sassConfiguration: ISassConfiguration): IExten }; } -function determineSyntaxFromFilePath(path: string): Syntax { - switch (path.substring(path.lastIndexOf('.'))) { +/** + * A replacer function for preprocessing SCSS files that might contain a legacy tilde import. + * @param match - The matched `@import` or `@use` statement + * @param pre - The whitespace and `@import` or `@use` keyword + * @param specifier - The specifier containing the tilde + * @returns A replacement string with the tilde replaced by `pkg:` + */ +function replaceTilde(match: string, pre: string, specifier: string): string { + const quote: string = specifier[0]; + return `${pre}${quote}pkg:${specifier.slice(2, -1)}${quote}`; +} + +/** + * Preprocesses raw SCSS and replaces legacy `~@scope/pkg/...` imports with `pkg:@scope/pkg/...`. + * @param buffer - The buffer containing the SCSS file contents + * @returns The preprocessed SCSS file contents as a string + */ +function preprocessScss(buffer: Buffer): string { + return buffer.toString('utf8').replace(importTildeRegex, replaceTilde); +} + +/** + * Converts a `heft:` URL to a physical file path (platform normalized). + * The `heft:` protocol is used so that the SASS compiler will not try to load the resource itself. + * @param url - The URL to convert to an absolute file path + * @returns The platform-normalized absolute file path of the resource. + */ +function heftUrlToPath(url: string): string { + return fileURLToPath(`file://${url.slice(5)}`); +} + +/** + * Converts a physical file path (platform normalized) to a URL with a `heft:` protocol. + * The `heft:` protocol is used so that the SASS compiler will not try to load the resource itself. + * @param filePath - The platform-normalized absolute file path of the resource. + * @returns A URL with the `heft:` protocol representing the file path. + */ +function pathToHeftUrl(filePath: string): URL { + const url: URL = pathToFileURL(filePath); + const heftUrl: URL = new URL(`heft:${url.pathname}`); + return heftUrl; +} + +/** + * Sass partial files are snippets of CSS meant to be included in other Sass files. + * Partial filenames always begin with a leading underscore and do not produce a CSS output file. + */ +function isSassPartial(filePath: string): boolean { + return path.basename(filePath)[0] === '_'; +} + +function getContentsHash(fileName: string, fileContents: string): string { + return crypto.createHmac('sha1', fileName).update(fileContents).digest('base64'); +} + +function determineSyntaxFromFilePath(filePath: string): Syntax { + switch (filePath.slice(filePath.lastIndexOf('.'))) { case '.sass': return 'indented'; case '.scss': @@ -328,3 +1074,19 @@ function determineSyntaxFromFilePath(path: string): Syntax { return 'css'; } } + +function generateJsShimContent( + format: 'commonjs' | 'esnext', + relativePathToCss: string, + isModule: boolean | undefined +): string { + const pathString: string = JSON.stringify(relativePathToCss); + switch (format) { + case 'commonjs': + return isModule + ? `module.exports = require(${pathString});\nmodule.exports.default = module.exports;` + : `require(${pathString});`; + case 'esnext': + return isModule ? `export { default } from ${pathString};` : `import ${pathString};export {};`; + } +} diff --git a/heft-plugins/heft-sass-plugin/src/constants.ts b/heft-plugins/heft-sass-plugin/src/constants.ts new file mode 100644 index 00000000000..a515d9cc14a --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/constants.ts @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export const PLUGIN_NAME: 'sass-plugin' = 'sass-plugin'; diff --git a/heft-plugins/heft-sass-plugin/src/index.ts b/heft-plugins/heft-sass-plugin/src/index.ts new file mode 100644 index 00000000000..91ca269f566 --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/index.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export { PLUGIN_NAME as SassPluginName } from './constants'; +export type { ISassPluginAccessor } from './SassPlugin'; diff --git a/heft-plugins/heft-sass-plugin/src/schemas/heft-sass-plugin.schema.json b/heft-plugins/heft-sass-plugin/src/schemas/heft-sass-plugin.schema.json index fac89a9a617..71d163bff9d 100644 --- a/heft-plugins/heft-sass-plugin/src/schemas/heft-sass-plugin.schema.json +++ b/heft-plugins/heft-sass-plugin/src/schemas/heft-sass-plugin.schema.json @@ -13,7 +13,7 @@ }, "extends": { - "description": "Optionally specifies another JSON config file that this file extends from. This provides a way for standard settings to be shared across multiple projects.", + "description": "Optionally specifies another JSON config file that this file extends from. This provides a way for standard settings to be shared across multiple projects. To delete an inherited setting, set it to `null` in this file.", "type": "string" }, @@ -42,21 +42,35 @@ "cssOutputFolders": { "type": "array", - "description": "If specified, folders where compiled CSS files will be emitted to. They will be named by appending \".css\" to the source file name for ease of reference translation, unless \"preserveSCSSExtension\" is set.", + "description": "If specified, folders where compiled CSS files will be emitted to. They will be named by replacing \".scss\" or \".sass\" in the source file name with \".css\". If requested, JavaScript shims will be emitted to the same folder, named by appending \".js\" to the source file name.", "items": { - "type": "string", - "pattern": "[^\\\\]" + "oneOf": [ + { + "type": "string", + "pattern": "[^\\\\]" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "folder": { + "type": "string", + "pattern": "[^\\\\]" + }, + "shimModuleFormat": { + "type": "string", + "enum": ["commonjs", "esnext"] + } + }, + "required": ["folder"] + } + ] } }, - "preserveSCSSExtension": { - "type": "boolean", - "description": "If set, when emitting compiled CSS from a file with a \".scss\" extension, the emitted CSS will have the extension \".scss\" instead of \".scss.css\"." - }, - "fileExtensions": { "type": "array", - "description": "Files with these extensions will be treated as SCSS modules and pass through the Sass transpiler for typings generation.", + "description": "Files with these extensions will be treated as SCSS modules and pass through the Sass transpiler for typings generation and/or CSS emit.", "items": { "type": "string", "pattern": "^\\.[A-z0-9-_.]*[A-z0-9-_]+$" @@ -65,29 +79,48 @@ "nonModuleFileExtensions": { "type": "array", - "description": "Files with these extensions will be treated as non-module SCSS and pass through the Sass transpiler for typings generation.", + "description": "Files with these extensions will be treated as non-module SCSS and pass through the Sass transpiler for typings generation and/or CSS emit.", "items": { "type": "string", "pattern": "^\\.[A-z0-9-_.]*[A-z0-9-_]+$" } }, - "importIncludePaths": { + "excludeFiles": { "type": "array", - "description": "A list of paths used when resolving Sass imports.", + "description": "A list of file paths relative to the \"src\" folder that should be excluded from typings generation and/or CSS emit.", "items": { "type": "string", "pattern": "[^\\\\]" } }, - "excludeFiles": { + "ignoreDeprecationsInDependencies": { + "type": "boolean", + "description": "If set, deprecation warnings from dependencies will be suppressed." + }, + + "silenceDeprecations": { "type": "array", - "description": "A list of file paths relative to the \"src\" folder that should be excluded from typings generation.", + "description": "A list of deprecation codes to silence. This is useful for suppressing warnings from deprecated Sass features that are used in the project and known not to be a problem.", "items": { - "type": "string", - "pattern": "[^\\\\]" + "type": "string" } + }, + + "doNotTrimOriginalFileExtension": { + "type": "boolean", + "description": "If true, the original file extension will not be trimmed when generating the output CSS file. The generated CSS file will retain its original extension. For example, \"styles.scss\" will generate \"styles.scss.css\" instead of \"styles.css\"." + }, + + "preserveIcssExports": { + "type": "boolean", + "description": "If true, the ICSS `:export` block will be preserved in the emitted CSS output. This is necessary when the CSS is consumed by a webpack loader (e.g. css-loader's icssParser) that extracts `:export` values at bundle time to generate JavaScript exports. Defaults to false." + }, + + "sourceMap": { + "type": "boolean", + "description": "If true, a `.css.map` source map file will be written next to each emitted `.css` file, and a `sourceMappingURL` comment will be appended to the `.css`. Defaults to `false`." } } } diff --git a/heft-plugins/heft-sass-plugin/src/templates/sass.json b/heft-plugins/heft-sass-plugin/src/templates/sass.json index 05fc5a3a63a..2a874148f6e 100644 --- a/heft-plugins/heft-sass-plugin/src/templates/sass.json +++ b/heft-plugins/heft-sass-plugin/src/templates/sass.json @@ -2,13 +2,15 @@ * Configuration for @rushstack/heft-sass-plugin */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-sass-plugin.schema.json" + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-sass-plugin.schema.json" /** * Optionally specifies another JSON config file that this file extends from. This provides a way for standard * settings to be shared across multiple projects. + * + * To delete an inherited setting, set it to `null` in this file. */ - // "extends": "base-project/config/serve-command.json", + // "extends": "base-project/config/sass.json", /** * The root directory for project source code. @@ -25,51 +27,88 @@ // "generatedTsFolder": "temp/sass-ts/", /** - * Optional additional folders to which Sass typings should be output. + * Optional additional folders to which Sass typings should be output. Useful when publishing typings + * alongside compiled output (e.g. "lib-dts"). */ // "secondaryGeneratedTsFolders": [], /** - * Determines whether export values are wrapped in a default property, or not. + * Determines whether CSS module exports are wrapped in a typed default interface (true) or emitted as + * individual named exports (false). + * + * Note: setting this to false is incompatible with cssOutputFolders. * * Default value: true */ // "exportAsDefault": false, /** - * If specified, folders where compiled CSS files will be emitted to. They will be named by appending - * ".css" to the source file name for ease of reference translation, unless "preserveSCSSExtension" is set. + * If specified, folders where compiled CSS files will be emitted. Each entry is either a folder path string, + * or an object with a "folder" property and an optional "shimModuleFormat" property. When "shimModuleFormat" + * is set to "commonjs" or "esnext", a JavaScript shim file is emitted alongside each CSS file to re-export it + * in the specified module format. + * + * Default value: undefined + */ + // "cssOutputFolders": [ + // { "folder": "lib-esm", "shimModuleFormat": "esnext" }, + // { "folder": "lib-commonjs", "shimModuleFormat": "commonjs" } + // ], + + /** + * Files with these extensions will be treated as CSS modules and pass through the Sass transpiler for + * typings generation and/or CSS emit. + * + * Default value: [".sass", ".scss", ".css"] + */ + // "fileExtensions": [".module.scss", ".module.sass"], + + /** + * Files with these extensions will be treated as non-module (global) stylesheets and pass through the Sass + * transpiler for typings generation and/or CSS emit. The generated typings are side-effect-only (export {}). + * + * Default value: [".global.sass", ".global.scss", ".global.css"] + */ + // "nonModuleFileExtensions": [".global.scss", ".global.sass"], + + /** + * A list of file paths relative to the "src" folder that should be excluded from typings generation + * and/or CSS emit. * * Default value: undefined */ - // "cssOutputFolders": [], + // "excludeFiles": [], /** - * If set, when emitting compiled CSS from a file with a ".scss" extension, the emitted CSS will have - * the extension ".scss" instead of ".scss.css". + * If true, the original file extension will not be trimmed when generating the output CSS filename. + * For example, "styles.scss" will generate "styles.scss.css" instead of "styles.css". * * Default value: false */ - // "preserveSCSSExtension": true, + // "doNotTrimOriginalFileExtension": true, /** - * Files with these extensions will pass through the Sass transpiler for typings generation. + * If true, the ICSS ":export" block will be preserved in the emitted CSS output. This is necessary when + * the CSS is consumed by a webpack loader (e.g. css-loader's icssParser) that extracts ":export" values + * at bundle time. Has no effect on the generated ".d.ts" file. * - * Default value: [".sass", ".scss", ".css"] + * Default value: false */ - // "fileExtensions": [".sass", ".scss"], + // "preserveIcssExports": true, /** - * A list of paths used when resolving Sass imports. The paths should be relative to the project root. + * If set, deprecation warnings that originate from dependencies will be suppressed. * - * Default value: ["node_modules", "src"] + * Default value: false */ - // "importIncludePaths": ["node_modules", "src"], + // "ignoreDeprecationsInDependencies": true, /** - * A list of file paths relative to the "src" folder that should be excluded from typings generation. + * A list of Sass deprecation codes to silence. Useful for suppressing known warnings from deprecated + * features that are not yet actionable. Common values: "mixed-decls", "import", "global-builtin", + * "color-functions". * - * Default value: undefined + * Default value: [] */ - // "excludeFiles": [] + // "silenceDeprecations": ["mixed-decls"] } diff --git a/heft-plugins/heft-sass-plugin/src/test/SassProcessor.test.ts b/heft-plugins/heft-sass-plugin/src/test/SassProcessor.test.ts new file mode 100644 index 00000000000..4faa8027702 --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/SassProcessor.test.ts @@ -0,0 +1,829 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; +import nodeJsPath from 'node:path'; + +import { FileSystem, Path, Text } from '@rushstack/node-core-library'; +import { MockScopedLogger } from '@rushstack/heft/lib/pluginFramework/logging/MockScopedLogger'; +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; + +import { type ICssOutputFolder, type ISassProcessorOptions, SassProcessor } from '../SassProcessor'; + +const projectFolder: string = path.resolve(__dirname, '../..'); +const fixturesFolder: string = path.resolve(__dirname, '../../src/test/fixtures'); + +// Fake output folder paths - never actually written to disk because FileSystem.writeFileAsync is mocked. +const FAKE_OUTPUT_BASE_FOLDER: string = '/fake/output'; +const NORMALIZED_PLATFORM_FAKE_OUTPUT_BASE_FOLDER: string = Path.convertToSlashes( + nodeJsPath.resolve(FAKE_OUTPUT_BASE_FOLDER) +); +const CSS_OUTPUT_FOLDER: string = `${FAKE_OUTPUT_BASE_FOLDER}/css`; +const DTS_OUTPUT_FOLDER: string = `${FAKE_OUTPUT_BASE_FOLDER}/dts`; + +type ICreateProcessorOptions = Partial< + Pick< + ISassProcessorOptions, + | 'cssOutputFolders' + | 'doNotTrimOriginalFileExtension' + | 'dtsOutputFolders' + | 'exportAsDefault' + | 'fileExtensions' + | 'nonModuleFileExtensions' + | 'postProcessCssAsync' + | 'preserveIcssExports' + | 'silenceDeprecations' + | 'sourceMap' + | 'srcFolder' + > +>; + +function createProcessor( + terminalProvider: StringBufferTerminalProvider, + options: ICreateProcessorOptions = {} +): { + processor: SassProcessor; + logger: MockScopedLogger; +} { + const terminal: Terminal = new Terminal(terminalProvider); + const logger: MockScopedLogger = new MockScopedLogger(terminal); + + const processor: SassProcessor = new SassProcessor({ + logger, + buildFolder: projectFolder, + concurrency: 1, + srcFolder: fixturesFolder, + dtsOutputFolders: [DTS_OUTPUT_FOLDER], + cssOutputFolders: [{ folder: CSS_OUTPUT_FOLDER, shimModuleFormat: undefined }], + exportAsDefault: true, + ...options + }); + + return { processor, logger }; +} + +async function compileFixtureAsync(processor: SassProcessor, fixtureFilename: string): Promise { + await processor.compileFilesAsync(new Set([`${fixturesFolder}/${fixtureFilename}`])); +} + +/** + * Replaces OS-/checkout-dependent fields in a source map JSON string with stable placeholders so + * the test can snapshot the result on any machine. Specifically, rewrites `sources[]` entries that + * point at the fixtures folder to a `fixtures/` form, and normalizes `sourcesContent[]` + * line endings to LF. + */ +function normalizeSourceMapForSnapshot(json: string): string { + const map: { + sources?: string[]; + sourcesContent?: string[]; + [key: string]: unknown; + } = JSON.parse(json); + + if (map.sources) { + // sources[] are relative paths from the .css.map output file back to the source .scss. + // In real use both ends live on disk in the same project so this is stable; in this test + // the output folder is the mocked /fake/output/css/ while the source is the real fixture + // on disk, so the relative path traverses the entire checkout-specific path from /fake up + // to the real fixtures folder. Strip everything before the "/fixtures/" segment so the + // snapshot is checkout-independent. + map.sources = map.sources.map((source) => { + const normalized: string = Path.convertToSlashes(source); + const fixturesIndex: number = normalized.indexOf('/fixtures/'); + if (fixturesIndex >= 0) { + return normalized.slice(fixturesIndex + 1); + } + const lastSlash: number = normalized.lastIndexOf('/'); + return `fixtures/${lastSlash >= 0 ? normalized.slice(lastSlash + 1) : normalized}`; + }); + } + if (map.sourcesContent) { + map.sourcesContent = map.sourcesContent.map(Text.convertToLf); + } + + return JSON.stringify(map); +} + +describe(SassProcessor.name, () => { + let terminalProvider: StringBufferTerminalProvider; + /** Files captured by the mocked FileSystem.writeFileAsync, keyed by absolute path. */ + let writtenFiles: Map; + + /** Returns the content written to a path whose last segment matches the given filename. */ + function getWrittenFile(filename: string): string { + for (const [filePath, content] of writtenFiles) { + if (filePath.endsWith(`/${filename}`)) { + return content; + } + } + + throw new Error( + `No file written matching ".../${filename}". Written paths:\n${[...writtenFiles.keys()].join('\n')}` + ); + } + + /** Returns all paths written that end with the given suffix. */ + function getAllWrittenPathsMatching(suffix: string): string[] { + return [...writtenFiles.keys()].filter((p) => p.endsWith(suffix)); + } + + function getCssOutput(fixtureFilename: string): string { + // SassProcessor strips the last extension then appends .css + // export-only.module.scss → export-only.module.css + const withoutExt: string = fixtureFilename.slice(0, fixtureFilename.lastIndexOf('.')); + return getWrittenFile(`${withoutExt}.css`); + } + + function getDtsOutput(fixtureFilename: string): string { + return getWrittenFile(`${fixtureFilename}.d.ts`); + } + + function getJsShimOutput(fixtureFilename: string): string { + return getWrittenFile(`${fixtureFilename}.js`); + } + + beforeEach(() => { + terminalProvider = new StringBufferTerminalProvider(); + + writtenFiles = new Map(); + jest.spyOn(FileSystem, 'writeFileAsync').mockImplementation(async (filePath, content) => { + filePath = Path.convertToSlashes(filePath).replace( + NORMALIZED_PLATFORM_FAKE_OUTPUT_BASE_FOLDER, + FAKE_OUTPUT_BASE_FOLDER + ); + let serialized: string = String(content); + // Source map contents include the absolute-relative path back to the source file and the + // verbatim source file bytes. Both vary by checkout location and OS line endings, which makes + // raw snapshots non-portable. Normalize them to stable forms before storing. + if (filePath.endsWith('.css.map')) { + serialized = normalizeSourceMapForSnapshot(serialized); + } + writtenFiles.set(filePath, serialized); + }); + }); + + afterEach(() => { + jest.restoreAllMocks(); + + expect(writtenFiles).toMatchSnapshot('written-files'); + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot('terminal-output'); + }); + + describe('export-only.module.scss', () => { + it('strips the :export block from CSS when preserveIcssExports is false', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'export-only.module.scss'); + const css: string = getCssOutput('export-only.module.scss'); + expect(css).not.toContain(':export'); + }); + + it('preserves the :export block in CSS when preserveIcssExports is true', async () => { + const { processor } = createProcessor(terminalProvider, { preserveIcssExports: true }); + await compileFixtureAsync(processor, 'export-only.module.scss'); + const css: string = getCssOutput('export-only.module.scss'); + expect(css).toContain(':export'); + }); + + it('generates the same .d.ts regardless of preserveIcssExports', async () => { + const { processor: processorFalse } = createProcessor(terminalProvider); + await compileFixtureAsync(processorFalse, 'export-only.module.scss'); + const dtsFalse: string = getDtsOutput('export-only.module.scss'); + + writtenFiles.clear(); + + const { processor: processorTrue } = createProcessor(terminalProvider, { + preserveIcssExports: true + }); + await compileFixtureAsync(processorTrue, 'export-only.module.scss'); + const dtsTrue: string = getDtsOutput('export-only.module.scss'); + + expect(dtsFalse).toEqual(dtsTrue); + }); + }); + + describe('classes-and-exports.module.scss', () => { + it('strips the :export block from CSS when preserveIcssExports is false', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + const css: string = getCssOutput('classes-and-exports.module.scss'); + expect(css).not.toContain(':export'); + expect(css).toContain('.root'); + }); + + it('preserves the :export block in CSS when preserveIcssExports is true', async () => { + const { processor } = createProcessor(terminalProvider, { preserveIcssExports: true }); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + const css: string = getCssOutput('classes-and-exports.module.scss'); + expect(css).toContain(':export'); + expect(css).toContain('.root'); + }); + + it('generates correct .d.ts with both class names and :export values', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + const dts: string = getDtsOutput('classes-and-exports.module.scss'); + expect(dts).toContain('root'); + expect(dts).toContain('highlighted'); + expect(dts).toContain('themeColor'); + expect(dts).toContain('spacing'); + }); + + it('generates named exports in .d.ts when exportAsDefault is false', async () => { + // cssOutputFolders requires exportAsDefault: true, so omit it here + const { processor } = createProcessor(terminalProvider, { + exportAsDefault: false, + cssOutputFolders: [] + }); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + const dts: string = getDtsOutput('classes-and-exports.module.scss'); + // Named exports: "export const root: string;" instead of a default interface + expect(dts).toContain('export const root'); + expect(dts).toContain('export const highlighted'); + expect(dts).toContain('export const themeColor'); + expect(dts).not.toContain('export default'); + }); + }); + + describe('sass-variables-and-exports.module.scss (Sass variables, nesting, BEM)', () => { + it('resolves Sass variables and expands nested rules in CSS output', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'sass-variables-and-exports.module.scss'); + const css: string = getCssOutput('sass-variables-and-exports.module.scss'); + // Sass variables should be resolved to literal values + expect(css).toContain('#0078d4'); + expect(css).toContain('#106ebe'); + // Nested rules should be expanded + expect(css).toContain('.container:hover'); + expect(css).toContain('.container__title'); + // :export block should be stripped (preserveIcssExports: false) + expect(css).not.toContain(':export'); + }); + + it('resolves Sass variables inside the :export block when preserveIcssExports is true', async () => { + const { processor } = createProcessor(terminalProvider, { preserveIcssExports: true }); + await compileFixtureAsync(processor, 'sass-variables-and-exports.module.scss'); + const css: string = getCssOutput('sass-variables-and-exports.module.scss'); + // The :export block should contain resolved values, not Sass variable names + expect(css).toContain(':export'); + expect(css).toContain('#0078d4'); + expect(css).not.toContain('$primary-color'); + }); + + it('generates .d.ts with resolved :export keys as typed properties', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'sass-variables-and-exports.module.scss'); + const dts: string = getDtsOutput('sass-variables-and-exports.module.scss'); + expect(dts).toContain('container'); + expect(dts).toContain('primaryColor'); + expect(dts).toContain('secondaryColor'); + expect(dts).toContain('baseSpacing'); + }); + }); + + describe('mixin-with-exports.module.scss (Sass @mixin)', () => { + it('expands @mixin calls in CSS output', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'mixin-with-exports.module.scss'); + const css: string = getCssOutput('mixin-with-exports.module.scss'); + // Mixin output should be inlined - no @mixin or @include in the output + expect(css).not.toContain('@mixin'); + expect(css).not.toContain('@include'); + expect(css).toContain('display: flex'); + expect(css).toContain('.card'); + expect(css).toContain('.card--vertical'); + }); + + it('preserves :export alongside expanded @mixin output when preserveIcssExports is true', async () => { + const { processor } = createProcessor(terminalProvider, { preserveIcssExports: true }); + await compileFixtureAsync(processor, 'mixin-with-exports.module.scss'); + const css: string = getCssOutput('mixin-with-exports.module.scss'); + expect(css).toContain(':export'); + expect(css).toContain('display: flex'); + expect(css).not.toContain('@mixin'); + }); + + it('generates .d.ts with :export values and class names from @mixin-using file', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'mixin-with-exports.module.scss'); + const dts: string = getDtsOutput('mixin-with-exports.module.scss'); + expect(dts).toContain('card'); + expect(dts).toContain('cardRadius'); + expect(dts).toContain('animationDuration'); + }); + }); + + describe('extend-with-exports.module.scss (Sass @extend / placeholder selectors)', () => { + it('merges @extend selectors and strips :export when preserveIcssExports is false', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'extend-with-exports.module.scss'); + const css: string = getCssOutput('extend-with-exports.module.scss'); + // Placeholder %button-base should not appear literally; its rules should be merged + expect(css).not.toContain('%button-base'); + expect(css).toContain('.primaryButton'); + expect(css).toContain('.dangerButton'); + expect(css).not.toContain(':export'); + }); + + it('preserves :export alongside @extend-merged output when preserveIcssExports is true', async () => { + const { processor } = createProcessor(terminalProvider, { preserveIcssExports: true }); + await compileFixtureAsync(processor, 'extend-with-exports.module.scss'); + const css: string = getCssOutput('extend-with-exports.module.scss'); + expect(css).toContain(':export'); + expect(css).toContain('.primaryButton'); + expect(css).not.toContain('%button-base'); + }); + + it('generates .d.ts with class names and :export values for @extend file', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'extend-with-exports.module.scss'); + const dts: string = getDtsOutput('extend-with-exports.module.scss'); + expect(dts).toContain('primaryButton'); + expect(dts).toContain('dangerButton'); + expect(dts).toContain('colorPrimary'); + expect(dts).toContain('colorDanger'); + }); + }); + + describe('JS shim files', () => { + it('emits a CommonJS shim for a module file', async () => { + const { processor } = createProcessor(terminalProvider, { + cssOutputFolders: [{ folder: CSS_OUTPUT_FOLDER, shimModuleFormat: 'commonjs' }] + }); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + const shim: string = getJsShimOutput('classes-and-exports.module.scss'); + // CJS module shim re-exports from the CSS file and mirrors it as .default + expect(shim).toContain(`require("./classes-and-exports.module.css")`); + expect(shim).toContain('module.exports.default = module.exports'); + }); + + it('emits an ESM shim for a module file', async () => { + const { processor } = createProcessor(terminalProvider, { + cssOutputFolders: [{ folder: CSS_OUTPUT_FOLDER, shimModuleFormat: 'esnext' }] + }); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + const shim: string = getJsShimOutput('classes-and-exports.module.scss'); + // ESM module shim re-exports the default from the CSS file + expect(shim).toBe(`export { default } from "./classes-and-exports.module.css";`); + }); + + it('emits a CommonJS shim for a non-module (global) file', async () => { + const { processor } = createProcessor(terminalProvider, { + cssOutputFolders: [{ folder: CSS_OUTPUT_FOLDER, shimModuleFormat: 'commonjs' }], + // Register the .global.scss extension so the processor classifies it correctly + nonModuleFileExtensions: ['.global.scss'] + }); + await compileFixtureAsync(processor, 'global-styles.global.scss'); + const shim: string = getJsShimOutput('global-styles.global.scss'); + // CJS non-module shim: side-effect require only + expect(shim).toBe(`require("./global-styles.global.css");`); + }); + + it('emits an ESM shim for a non-module (global) file', async () => { + const { processor } = createProcessor(terminalProvider, { + cssOutputFolders: [{ folder: CSS_OUTPUT_FOLDER, shimModuleFormat: 'esnext' }], + nonModuleFileExtensions: ['.global.scss'] + }); + await compileFixtureAsync(processor, 'global-styles.global.scss'); + const shim: string = getJsShimOutput('global-styles.global.scss'); + // ESM non-module shim: side-effect import only + expect(shim).toBe(`import "./global-styles.global.css";export {};`); + }); + + it('does not emit a shim when shimModuleFormat is undefined', async () => { + const { processor } = createProcessor(terminalProvider, { + cssOutputFolders: [{ folder: CSS_OUTPUT_FOLDER, shimModuleFormat: undefined }] + }); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + // Only the CSS and DTS files should be written - no .js shim + const shimPaths: string[] = getAllWrittenPathsMatching('.module.scss.js'); + expect(shimPaths).toHaveLength(0); + }); + + it('writes shims to each configured cssOutputFolder independently', async () => { + const CSS_FOLDER_ESM: string = '/fake/output/css-esm'; + const CSS_FOLDER_CJS: string = '/fake/output/css-cjs'; + const cssOutputFolders: ICssOutputFolder[] = [ + { folder: CSS_FOLDER_ESM, shimModuleFormat: 'esnext' }, + { folder: CSS_FOLDER_CJS, shimModuleFormat: 'commonjs' } + ]; + const { processor } = createProcessor(terminalProvider, { cssOutputFolders }); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + + const esmShim: string = writtenFiles.get(`${CSS_FOLDER_ESM}/classes-and-exports.module.scss.js`)!; + const cjsShim: string = writtenFiles.get(`${CSS_FOLDER_CJS}/classes-and-exports.module.scss.js`)!; + + expect(esmShim).toBe(`export { default } from "./classes-and-exports.module.css";`); + expect(cjsShim).toContain('module.exports.default = module.exports'); + }); + }); + + describe('global-only.module.scss (module file with only :global styles)', () => { + it('emits export {}; in the .d.ts when all styles are :global', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'global-only.module.scss'); + const dts: string = getDtsOutput('global-only.module.scss'); + expect(dts).toBe('export {};'); + }); + + it('emits a side-effect ESM shim (no default re-export) when all styles are :global', async () => { + const { processor } = createProcessor(terminalProvider, { + cssOutputFolders: [{ folder: CSS_OUTPUT_FOLDER, shimModuleFormat: 'esnext' }] + }); + await compileFixtureAsync(processor, 'global-only.module.scss'); + const shim: string = getJsShimOutput('global-only.module.scss'); + expect(shim).toBe(`import "./global-only.module.css";export {};`); + }); + + it('emits a side-effect CJS shim (no module.exports assignment) when all styles are :global', async () => { + const { processor } = createProcessor(terminalProvider, { + cssOutputFolders: [{ folder: CSS_OUTPUT_FOLDER, shimModuleFormat: 'commonjs' }] + }); + await compileFixtureAsync(processor, 'global-only.module.scss'); + const shim: string = getJsShimOutput('global-only.module.scss'); + expect(shim).toBe(`require("./global-only.module.css");`); + }); + + it('emits compiled CSS with the :global styles applied', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'global-only.module.scss'); + const css: string = getCssOutput('global-only.module.scss'); + expect(css).toContain('.ms-Nav-group'); + expect(css).toContain('.ms-Nav-link'); + }); + }); + + describe('simple.module.sass (indented Sass syntax)', () => { + it('compiles indented Sass syntax to CSS correctly', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'simple.module.sass'); + const css: string = getCssOutput('simple.module.sass'); + expect(css).toContain('.exampleClass'); + expect(css).toContain('color: red'); + expect(css).toContain('.exampleHeading'); + expect(css).toContain('font-weight: bold'); + }); + + it('generates .d.ts with class names from indented Sass', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'simple.module.sass'); + const dts: string = getDtsOutput('simple.module.sass'); + expect(dts).toContain('exampleClass'); + expect(dts).toContain('exampleHeading'); + expect(dts).toContain('export default styles'); + }); + + it('emits an ESM shim for an indented Sass module file', async () => { + const { processor } = createProcessor(terminalProvider, { + cssOutputFolders: [{ folder: CSS_OUTPUT_FOLDER, shimModuleFormat: 'esnext' }] + }); + await compileFixtureAsync(processor, 'simple.module.sass'); + const shim: string = getJsShimOutput('simple.module.sass'); + expect(shim).toBe(`export { default } from "./simple.module.css";`); + }); + }); + + describe('use-with-partial.module.scss (@use with local partial)', () => { + it('resolves variables from a @use partial in CSS output', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'use-with-partial.module.scss'); + const css: string = getCssOutput('use-with-partial.module.scss'); + // $brand-color from the partial should be resolved to its value + expect(css).toContain('#0078d4'); + // $spacing-unit * 2 = 8px + expect(css).toContain('8px'); + expect(css).toContain('.container'); + expect(css).toContain('.header'); + }); + + it('generates .d.ts with class names from a file using @use', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'use-with-partial.module.scss'); + const dts: string = getDtsOutput('use-with-partial.module.scss'); + expect(dts).toContain('container'); + expect(dts).toContain('header'); + expect(dts).toContain('export default styles'); + }); + }); + + describe('use-plain-css.module.scss (@use of a plain .css file)', () => { + it('resolves a plain .css file referenced via @use with an explicit extension', async () => { + const { processor, logger } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'use-plain-css.module.scss'); + // The @use must resolve without "Can't find stylesheet to import." + expect(logger.errors).toHaveLength(0); + const css: string = getCssOutput('use-plain-css.module.scss'); + // Rules from the plain .css file should be inlined into the output + expect(css).toContain('.token-base'); + expect(css).toContain('#0078d4'); + expect(css).toContain('.container'); + }); + + it('resolves a plain .css file referenced via @use without an explicit extension', async () => { + const { processor, logger } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'use-plain-css-extensionless.module.scss'); + // Extensionless loads must fall back to the `.css` candidate, matching dart-sass + expect(logger.errors).toHaveLength(0); + const css: string = getCssOutput('use-plain-css-extensionless.module.scss'); + expect(css).toContain('.token-base'); + expect(css).toContain('#0078d4'); + expect(css).toContain('.container'); + }); + }); + + describe('global-styles.global.sass (.global.sass non-module file)', () => { + it('compiles indented Sass syntax to plain CSS for a .global.sass file', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'global-styles.global.sass'); + const css: string = getCssOutput('global-styles.global.sass'); + expect(css).toContain('body'); + expect(css).toContain('h1'); + // $heading-size should be resolved to its value + expect(css).toContain('24px'); + expect(css).toContain('#333'); + }); + + it('emits export {}; in the .d.ts for a .global.sass file', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'global-styles.global.sass'); + const dts: string = getDtsOutput('global-styles.global.sass'); + expect(dts).toBe('export {};'); + }); + + it('emits a side-effect ESM shim for a .global.sass file', async () => { + const { processor } = createProcessor(terminalProvider, { + cssOutputFolders: [{ folder: CSS_OUTPUT_FOLDER, shimModuleFormat: 'esnext' }] + }); + await compileFixtureAsync(processor, 'global-styles.global.sass'); + const shim: string = getJsShimOutput('global-styles.global.sass'); + expect(shim).toBe(`import "./global-styles.global.css";export {};`); + }); + }); + + describe('css-module.module.css (plain CSS as CSS module input)', () => { + it('generates .d.ts with class names from a .module.css input file', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'css-module.module.css'); + const dts: string = getDtsOutput('css-module.module.css'); + expect(dts).toContain('root'); + expect(dts).toContain('header'); + expect(dts).toContain('export default styles'); + }); + + it('passes CSS through the pipeline unchanged for a .module.css input file', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'css-module.module.css'); + const css: string = getCssOutput('css-module.module.css'); + expect(css).toContain('.root'); + expect(css).toContain('.header'); + expect(css).toContain('display: flex'); + }); + + it('does not emit a JS shim for a .module.css input file', async () => { + const { processor } = createProcessor(terminalProvider, { + cssOutputFolders: [{ folder: CSS_OUTPUT_FOLDER, shimModuleFormat: 'esnext' }] + }); + await compileFixtureAsync(processor, 'css-module.module.css'); + // .css inputs cannot have a shim (the shim would reference itself) + const shimPaths: string[] = getAllWrittenPathsMatching('.module.css.js'); + expect(shimPaths).toHaveLength(0); + }); + }); + + describe('silenceDeprecations option', () => { + it('throws at construction time when given an unknown deprecation code', () => { + expect(() => + createProcessor(terminalProvider, { silenceDeprecations: ['not-a-real-deprecation-code'] }) + ).toThrow('Unknown deprecation code: not-a-real-deprecation-code'); + }); + }); + + describe('non-module (global) files', () => { + it('emits plain compiled CSS for a .global.scss file', async () => { + const { processor } = createProcessor(terminalProvider, { + nonModuleFileExtensions: ['.global.scss'] + }); + await compileFixtureAsync(processor, 'global-styles.global.scss'); + const css: string = getCssOutput('global-styles.global.scss'); + // Variables should be resolved; selectors should be present + expect(css).toContain('body'); + expect(css).toContain('h1'); + expect(css).toContain('font-family'); + expect(css).not.toContain('$body-font'); + }); + + it('emits export {}; in the .d.ts for a non-module file', async () => { + const { processor } = createProcessor(terminalProvider, { + nonModuleFileExtensions: ['.global.scss'] + }); + await compileFixtureAsync(processor, 'global-styles.global.scss'); + const dts: string = getDtsOutput('global-styles.global.scss'); + expect(dts).toBe('export {};'); + }); + }); + + describe('multiple output folders', () => { + it('writes .d.ts to every configured dtsOutputFolder', async () => { + const DTS_FOLDER_A: string = '/fake/output/dts-a'; + const DTS_FOLDER_B: string = '/fake/output/dts-b'; + const { processor } = createProcessor(terminalProvider, { + dtsOutputFolders: [DTS_FOLDER_A, DTS_FOLDER_B] + }); + await compileFixtureAsync(processor, 'export-only.module.scss'); + + const dtsA: string = writtenFiles.get(`${DTS_FOLDER_A}/export-only.module.scss.d.ts`)!; + const dtsB: string = writtenFiles.get(`${DTS_FOLDER_B}/export-only.module.scss.d.ts`)!; + + expect(dtsA).toBeDefined(); + expect(dtsA).toEqual(dtsB); + }); + + it('writes CSS to every configured cssOutputFolder', async () => { + const CSS_FOLDER_A: string = '/fake/output/css-a'; + const CSS_FOLDER_B: string = '/fake/output/css-b'; + const { processor } = createProcessor(terminalProvider, { + cssOutputFolders: [ + { folder: CSS_FOLDER_A, shimModuleFormat: undefined }, + { folder: CSS_FOLDER_B, shimModuleFormat: undefined } + ] + }); + await compileFixtureAsync(processor, 'export-only.module.scss'); + + const cssA: string = writtenFiles.get(`${CSS_FOLDER_A}/export-only.module.css`)!; + const cssB: string = writtenFiles.get(`${CSS_FOLDER_B}/export-only.module.css`)!; + + expect(cssA).toBeDefined(); + expect(cssA).toEqual(cssB); + }); + }); + + describe('postProcessCssAsync', () => { + it('passes compiled CSS through the post-processor callback', async () => { + const postProcessed: string[] = []; + const { processor } = createProcessor(terminalProvider, { + postProcessCssAsync: async (css: string) => { + postProcessed.push(css); + return css.replace(/color:/g, 'color: /* post-processed */'); + } + }); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + + // The callback should have been called with the raw CSS + expect(postProcessed.length).toBe(1); + expect(postProcessed[0]).toContain('color:'); + + // The emitted CSS should reflect the transformation + const css: string = getCssOutput('classes-and-exports.module.scss'); + expect(css).toContain('color: /* post-processed */'); + }); + + it('post-processor runs after postcss-modules strips :export', async () => { + const seenCss: string[] = []; + const { processor } = createProcessor(terminalProvider, { + postProcessCssAsync: async (css: string) => { + seenCss.push(css); + return css; + } + }); + await compileFixtureAsync(processor, 'export-only.module.scss'); + + // With preserveIcssExports: false (default), the CSS seen by the callback + // should already have the :export block stripped + expect(seenCss[0]).not.toContain(':export'); + }); + + it('post-processor receives the original CSS including :export when preserveIcssExports is true', async () => { + const seenCss: string[] = []; + const { processor } = createProcessor(terminalProvider, { + preserveIcssExports: true, + postProcessCssAsync: async (css: string) => { + seenCss.push(css); + return css; + } + }); + await compileFixtureAsync(processor, 'export-only.module.scss'); + + expect(seenCss[0]).toContain(':export'); + }); + }); + + describe('doNotTrimOriginalFileExtension', () => { + it('strips the source extension by default (doNotTrimOriginalFileExtension: false)', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + // Default: "classes-and-exports.module.scss" → "classes-and-exports.module.css" + const css: string = writtenFiles.get(`${CSS_OUTPUT_FOLDER}/classes-and-exports.module.css`)!; + expect(css).toBeDefined(); + expect(writtenFiles.has(`${CSS_OUTPUT_FOLDER}/classes-and-exports.module.scss.css`)).toBe(false); + }); + + it('preserves the source extension when doNotTrimOriginalFileExtension is true', async () => { + const { processor } = createProcessor(terminalProvider, { doNotTrimOriginalFileExtension: true }); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + // "classes-and-exports.module.scss" → "classes-and-exports.module.scss.css" + const css: string = writtenFiles.get(`${CSS_OUTPUT_FOLDER}/classes-and-exports.module.scss.css`)!; + expect(css).toBeDefined(); + expect(writtenFiles.has(`${CSS_OUTPUT_FOLDER}/classes-and-exports.module.css`)).toBe(false); + }); + + it('uses the .scss.css filename in JS shims when doNotTrimOriginalFileExtension is true', async () => { + const { processor } = createProcessor(terminalProvider, { + doNotTrimOriginalFileExtension: true, + cssOutputFolders: [{ folder: CSS_OUTPUT_FOLDER, shimModuleFormat: 'commonjs' }] + }); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + const shim: string = writtenFiles.get(`${CSS_OUTPUT_FOLDER}/classes-and-exports.module.scss.js`)!; + expect(shim).toContain(`require("./classes-and-exports.module.scss.css")`); + }); + + it('the CSS content is the same regardless of doNotTrimOriginalFileExtension', async () => { + const { processor: processorDefault } = createProcessor(terminalProvider); + await compileFixtureAsync(processorDefault, 'classes-and-exports.module.scss'); + const cssDefault: string = writtenFiles.get(`${CSS_OUTPUT_FOLDER}/classes-and-exports.module.css`)!; + + writtenFiles.clear(); + + const { processor: processorPreserve } = createProcessor(terminalProvider, { + doNotTrimOriginalFileExtension: true + }); + await compileFixtureAsync(processorPreserve, 'classes-and-exports.module.scss'); + const cssPreserve: string = writtenFiles.get( + `${CSS_OUTPUT_FOLDER}/classes-and-exports.module.scss.css` + )!; + + expect(cssDefault).toEqual(cssPreserve); + }); + }); + + describe('error reporting', () => { + it('emits an error for invalid SCSS syntax', async () => { + const { processor, logger } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'invalid.module.scss'); + expect(logger.errors.length).toBeGreaterThan(0); + }); + }); + + describe('sourceMap option', () => { + it('emits .css.map and sourceMappingURL comment when sourceMap is true', async () => { + const { processor } = createProcessor(terminalProvider, { sourceMap: true }); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + + const mapPaths: string[] = getAllWrittenPathsMatching('.css.map'); + expect(mapPaths).toHaveLength(1); + + const css: string = getCssOutput('classes-and-exports.module.scss'); + expect(css).toMatch(/\/\*# sourceMappingURL=classes-and-exports\.module\.css\.map \*\//); + + const mapJson: string = getWrittenFile('classes-and-exports.module.css.map'); + const parsedMap: { + version: number; + mappings: string; + sources: string[]; + } = JSON.parse(mapJson); + expect(parsedMap.version).toBe(3); + expect(parsedMap.mappings).toBeTruthy(); + expect(parsedMap.sources).toHaveLength(1); + expect(parsedMap.sources[0]).toMatch(/classes-and-exports\.module\.scss$/); + }); + + it('emits a valid .css.map when the entry file @uses a partial (Linux/macOS regression)', async () => { + // use-with-partial.module.scss uses @use 'partial', which goes through loadAsync. + // Without sourceMapUrl on the ImporterResult, sass-embedded falls back to a data: URL + // for the partial in the source map; heftUrlToPath then crashes on Linux/macOS. + const { processor } = createProcessor(terminalProvider, { sourceMap: true }); + await compileFixtureAsync(processor, 'use-with-partial.module.scss'); + + const mapPaths: string[] = getAllWrittenPathsMatching('.css.map'); + expect(mapPaths).toHaveLength(1); + + const mapJson: string = getWrittenFile('use-with-partial.module.css.map'); + const parsedMap: { version: number; mappings: string; sources: string[] } = JSON.parse(mapJson); + expect(parsedMap.version).toBe(3); + expect(parsedMap.mappings).toBeTruthy(); + // Both the entry file and the partial must resolve to real paths, not data: URLs + expect(parsedMap.sources.every((s: string) => !s.startsWith('data:'))).toBe(true); + }); + + it('does not emit .css.map or sourceMappingURL comment by default', async () => { + const { processor } = createProcessor(terminalProvider); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + + expect(getAllWrittenPathsMatching('.css.map')).toHaveLength(0); + expect(getCssOutput('classes-and-exports.module.scss')).not.toContain('sourceMappingURL'); + }); + + it('uses the correct map filename when doNotTrimOriginalFileExtension is true', async () => { + const { processor } = createProcessor(terminalProvider, { + sourceMap: true, + doNotTrimOriginalFileExtension: true + }); + await compileFixtureAsync(processor, 'classes-and-exports.module.scss'); + + // With doNotTrimOriginalFileExtension the CSS file is foo.scss.css, so the map is foo.scss.css.map + const mapPaths: string[] = getAllWrittenPathsMatching('.css.map'); + expect(mapPaths).toHaveLength(1); + expect(mapPaths[0]).toMatch(/classes-and-exports\.module\.scss\.css\.map$/); + + const css: string = getWrittenFile('classes-and-exports.module.scss.css'); + expect(css).toMatch(/\/\*# sourceMappingURL=classes-and-exports\.module\.scss\.css\.map \*\//); + }); + }); +}); diff --git a/heft-plugins/heft-sass-plugin/src/test/__snapshots__/SassProcessor.test.ts.snap b/heft-plugins/heft-sass-plugin/src/test/__snapshots__/SassProcessor.test.ts.snap new file mode 100644 index 00000000000..d2fab19a07f --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/__snapshots__/SassProcessor.test.ts.snap @@ -0,0 +1,1498 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`SassProcessor JS shim files does not emit a shim when shimModuleFormat is undefined: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor JS shim files does not emit a shim when shimModuleFormat is undefined: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/classes-and-exports.module.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +}", +} +`; + +exports[`SassProcessor JS shim files emits a CommonJS shim for a module file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor JS shim files emits a CommonJS shim for a module file: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/classes-and-exports.module.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +}", + "/fake/output/css/classes-and-exports.module.scss.js" => "module.exports = require(\\"./classes-and-exports.module.css\\"); +module.exports.default = module.exports;", +} +`; + +exports[`SassProcessor JS shim files emits a CommonJS shim for a non-module (global) file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor JS shim files emits a CommonJS shim for a non-module (global) file: written-files 1`] = ` +Map { + "/fake/output/dts/global-styles.global.scss.d.ts" => "export {};", + "/fake/output/css/global-styles.global.css" => "body { + margin: 0; + padding: 0; + font-family: \\"Segoe UI\\", sans-serif; +} + +h1 { + font-size: 24px; + color: #333; +}", + "/fake/output/css/global-styles.global.scss.js" => "require(\\"./global-styles.global.css\\");", +} +`; + +exports[`SassProcessor JS shim files emits an ESM shim for a module file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor JS shim files emits an ESM shim for a module file: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/classes-and-exports.module.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +}", + "/fake/output/css/classes-and-exports.module.scss.js" => "export { default } from \\"./classes-and-exports.module.css\\";", +} +`; + +exports[`SassProcessor JS shim files emits an ESM shim for a non-module (global) file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor JS shim files emits an ESM shim for a non-module (global) file: written-files 1`] = ` +Map { + "/fake/output/dts/global-styles.global.scss.d.ts" => "export {};", + "/fake/output/css/global-styles.global.css" => "body { + margin: 0; + padding: 0; + font-family: \\"Segoe UI\\", sans-serif; +} + +h1 { + font-size: 24px; + color: #333; +}", + "/fake/output/css/global-styles.global.scss.js" => "import \\"./global-styles.global.css\\";export {};", +} +`; + +exports[`SassProcessor JS shim files writes shims to each configured cssOutputFolder independently: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor JS shim files writes shims to each configured cssOutputFolder independently: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css-esm/classes-and-exports.module.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +}", + "/fake/output/css-esm/classes-and-exports.module.scss.js" => "export { default } from \\"./classes-and-exports.module.css\\";", + "/fake/output/css-cjs/classes-and-exports.module.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +}", + "/fake/output/css-cjs/classes-and-exports.module.scss.js" => "module.exports = require(\\"./classes-and-exports.module.css\\"); +module.exports.default = module.exports;", +} +`; + +exports[`SassProcessor classes-and-exports.module.scss generates correct .d.ts with both class names and :export values: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor classes-and-exports.module.scss generates correct .d.ts with both class names and :export values: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/classes-and-exports.module.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +}", +} +`; + +exports[`SassProcessor classes-and-exports.module.scss generates named exports in .d.ts when exportAsDefault is false: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor classes-and-exports.module.scss generates named exports in .d.ts when exportAsDefault is false: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "export const themeColor: string; +export const spacing: string; +export const root: string; +export const highlighted: string;", +} +`; + +exports[`SassProcessor classes-and-exports.module.scss preserves the :export block in CSS when preserveIcssExports is true: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor classes-and-exports.module.scss preserves the :export block in CSS when preserveIcssExports is true: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/classes-and-exports.module.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +} + +:export { + themeColor: blue; + spacing: 8px; +}", +} +`; + +exports[`SassProcessor classes-and-exports.module.scss strips the :export block from CSS when preserveIcssExports is false: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor classes-and-exports.module.scss strips the :export block from CSS when preserveIcssExports is false: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/classes-and-exports.module.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +}", +} +`; + +exports[`SassProcessor css-module.module.css (plain CSS as CSS module input) does not emit a JS shim for a .module.css input file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor css-module.module.css (plain CSS as CSS module input) does not emit a JS shim for a .module.css input file: written-files 1`] = ` +Map { + "/fake/output/dts/css-module.module.css.d.ts" => "declare interface IStyles { + root: string; + header: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/css-module.module.css" => "/* Plain CSS file treated as a CSS module via fileExtensions: ['.module.css']. + Verifies that CSS input (not SCSS/Sass) is processed correctly. */ +.root { + display: flex; + flex-direction: column; +} + +.header { + font-size: 16px; + font-weight: bold; +}", +} +`; + +exports[`SassProcessor css-module.module.css (plain CSS as CSS module input) generates .d.ts with class names from a .module.css input file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor css-module.module.css (plain CSS as CSS module input) generates .d.ts with class names from a .module.css input file: written-files 1`] = ` +Map { + "/fake/output/dts/css-module.module.css.d.ts" => "declare interface IStyles { + root: string; + header: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/css-module.module.css" => "/* Plain CSS file treated as a CSS module via fileExtensions: ['.module.css']. + Verifies that CSS input (not SCSS/Sass) is processed correctly. */ +.root { + display: flex; + flex-direction: column; +} + +.header { + font-size: 16px; + font-weight: bold; +}", +} +`; + +exports[`SassProcessor css-module.module.css (plain CSS as CSS module input) passes CSS through the pipeline unchanged for a .module.css input file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor css-module.module.css (plain CSS as CSS module input) passes CSS through the pipeline unchanged for a .module.css input file: written-files 1`] = ` +Map { + "/fake/output/dts/css-module.module.css.d.ts" => "declare interface IStyles { + root: string; + header: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/css-module.module.css" => "/* Plain CSS file treated as a CSS module via fileExtensions: ['.module.css']. + Verifies that CSS input (not SCSS/Sass) is processed correctly. */ +.root { + display: flex; + flex-direction: column; +} + +.header { + font-size: 16px; + font-weight: bold; +}", +} +`; + +exports[`SassProcessor doNotTrimOriginalFileExtension preserves the source extension when doNotTrimOriginalFileExtension is true: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor doNotTrimOriginalFileExtension preserves the source extension when doNotTrimOriginalFileExtension is true: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/classes-and-exports.module.scss.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +}", +} +`; + +exports[`SassProcessor doNotTrimOriginalFileExtension strips the source extension by default (doNotTrimOriginalFileExtension: false): terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor doNotTrimOriginalFileExtension strips the source extension by default (doNotTrimOriginalFileExtension: false): written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/classes-and-exports.module.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +}", +} +`; + +exports[`SassProcessor doNotTrimOriginalFileExtension the CSS content is the same regardless of doNotTrimOriginalFileExtension: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor doNotTrimOriginalFileExtension the CSS content is the same regardless of doNotTrimOriginalFileExtension: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/classes-and-exports.module.scss.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +}", +} +`; + +exports[`SassProcessor doNotTrimOriginalFileExtension uses the .scss.css filename in JS shims when doNotTrimOriginalFileExtension is true: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor doNotTrimOriginalFileExtension uses the .scss.css filename in JS shims when doNotTrimOriginalFileExtension is true: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/classes-and-exports.module.scss.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +}", + "/fake/output/css/classes-and-exports.module.scss.js" => "module.exports = require(\\"./classes-and-exports.module.scss.css\\"); +module.exports.default = module.exports;", +} +`; + +exports[`SassProcessor error reporting emits an error for invalid SCSS syntax: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor error reporting emits an error for invalid SCSS syntax: written-files 1`] = `Map {}`; + +exports[`SassProcessor export-only.module.scss generates the same .d.ts regardless of preserveIcssExports: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor export-only.module.scss generates the same .d.ts regardless of preserveIcssExports: written-files 1`] = ` +Map { + "/fake/output/dts/export-only.module.scss.d.ts" => "declare interface IStyles { + primaryColor: string; + fontFamily: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/export-only.module.css" => ":export { + primaryColor: #0078d4; + fontFamily: \\"Segoe UI\\"; +}", +} +`; + +exports[`SassProcessor export-only.module.scss preserves the :export block in CSS when preserveIcssExports is true: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor export-only.module.scss preserves the :export block in CSS when preserveIcssExports is true: written-files 1`] = ` +Map { + "/fake/output/dts/export-only.module.scss.d.ts" => "declare interface IStyles { + primaryColor: string; + fontFamily: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/export-only.module.css" => ":export { + primaryColor: #0078d4; + fontFamily: \\"Segoe UI\\"; +}", +} +`; + +exports[`SassProcessor export-only.module.scss strips the :export block from CSS when preserveIcssExports is false: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor export-only.module.scss strips the :export block from CSS when preserveIcssExports is false: written-files 1`] = ` +Map { + "/fake/output/dts/export-only.module.scss.d.ts" => "declare interface IStyles { + primaryColor: string; + fontFamily: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/export-only.module.css" => "", +} +`; + +exports[`SassProcessor extend-with-exports.module.scss (Sass @extend / placeholder selectors) generates .d.ts with class names and :export values for @extend file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor extend-with-exports.module.scss (Sass @extend / placeholder selectors) generates .d.ts with class names and :export values for @extend file: written-files 1`] = ` +Map { + "/fake/output/dts/extend-with-exports.module.scss.d.ts" => "declare interface IStyles { + colorPrimary: string; + colorDanger: string; + dangerButton: string; + primaryButton: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/extend-with-exports.module.css" => ".dangerButton, .primaryButton { + display: inline-flex; + align-items: center; + cursor: pointer; + border: none; + border-radius: 2px; +} + +.primaryButton { + background-color: #0078d4; + color: white; +} + +.dangerButton { + background-color: #d13438; + color: white; +}", +} +`; + +exports[`SassProcessor extend-with-exports.module.scss (Sass @extend / placeholder selectors) merges @extend selectors and strips :export when preserveIcssExports is false: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor extend-with-exports.module.scss (Sass @extend / placeholder selectors) merges @extend selectors and strips :export when preserveIcssExports is false: written-files 1`] = ` +Map { + "/fake/output/dts/extend-with-exports.module.scss.d.ts" => "declare interface IStyles { + colorPrimary: string; + colorDanger: string; + dangerButton: string; + primaryButton: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/extend-with-exports.module.css" => ".dangerButton, .primaryButton { + display: inline-flex; + align-items: center; + cursor: pointer; + border: none; + border-radius: 2px; +} + +.primaryButton { + background-color: #0078d4; + color: white; +} + +.dangerButton { + background-color: #d13438; + color: white; +}", +} +`; + +exports[`SassProcessor extend-with-exports.module.scss (Sass @extend / placeholder selectors) preserves :export alongside @extend-merged output when preserveIcssExports is true: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor extend-with-exports.module.scss (Sass @extend / placeholder selectors) preserves :export alongside @extend-merged output when preserveIcssExports is true: written-files 1`] = ` +Map { + "/fake/output/dts/extend-with-exports.module.scss.d.ts" => "declare interface IStyles { + colorPrimary: string; + colorDanger: string; + dangerButton: string; + primaryButton: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/extend-with-exports.module.css" => ".dangerButton, .primaryButton { + display: inline-flex; + align-items: center; + cursor: pointer; + border: none; + border-radius: 2px; +} + +.primaryButton { + background-color: #0078d4; + color: white; +} + +.dangerButton { + background-color: #d13438; + color: white; +} + +:export { + colorPrimary: #0078d4; + colorDanger: #d13438; +}", +} +`; + +exports[`SassProcessor global-only.module.scss (module file with only :global styles) emits a side-effect CJS shim (no module.exports assignment) when all styles are :global: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor global-only.module.scss (module file with only :global styles) emits a side-effect CJS shim (no module.exports assignment) when all styles are :global: written-files 1`] = ` +Map { + "/fake/output/dts/global-only.module.scss.d.ts" => "export {};", + "/fake/output/css/global-only.module.css" => ".ms-Nav-group { + overflow: hidden; +} +.ms-Nav-link { + height: 30px; +}", + "/fake/output/css/global-only.module.scss.js" => "require(\\"./global-only.module.css\\");", +} +`; + +exports[`SassProcessor global-only.module.scss (module file with only :global styles) emits a side-effect ESM shim (no default re-export) when all styles are :global: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor global-only.module.scss (module file with only :global styles) emits a side-effect ESM shim (no default re-export) when all styles are :global: written-files 1`] = ` +Map { + "/fake/output/dts/global-only.module.scss.d.ts" => "export {};", + "/fake/output/css/global-only.module.css" => ".ms-Nav-group { + overflow: hidden; +} +.ms-Nav-link { + height: 30px; +}", + "/fake/output/css/global-only.module.scss.js" => "import \\"./global-only.module.css\\";export {};", +} +`; + +exports[`SassProcessor global-only.module.scss (module file with only :global styles) emits compiled CSS with the :global styles applied: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor global-only.module.scss (module file with only :global styles) emits compiled CSS with the :global styles applied: written-files 1`] = ` +Map { + "/fake/output/dts/global-only.module.scss.d.ts" => "export {};", + "/fake/output/css/global-only.module.css" => ".ms-Nav-group { + overflow: hidden; +} +.ms-Nav-link { + height: 30px; +}", +} +`; + +exports[`SassProcessor global-only.module.scss (module file with only :global styles) emits export {}; in the .d.ts when all styles are :global: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor global-only.module.scss (module file with only :global styles) emits export {}; in the .d.ts when all styles are :global: written-files 1`] = ` +Map { + "/fake/output/dts/global-only.module.scss.d.ts" => "export {};", + "/fake/output/css/global-only.module.css" => ".ms-Nav-group { + overflow: hidden; +} +.ms-Nav-link { + height: 30px; +}", +} +`; + +exports[`SassProcessor global-styles.global.sass (.global.sass non-module file) compiles indented Sass syntax to plain CSS for a .global.sass file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor global-styles.global.sass (.global.sass non-module file) compiles indented Sass syntax to plain CSS for a .global.sass file: written-files 1`] = ` +Map { + "/fake/output/dts/global-styles.global.sass.d.ts" => "export {};", + "/fake/output/css/global-styles.global.css" => "body { + margin: 0; + padding: 0; +} + +h1 { + font-size: 24px; + color: #333; +}", +} +`; + +exports[`SassProcessor global-styles.global.sass (.global.sass non-module file) emits a side-effect ESM shim for a .global.sass file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor global-styles.global.sass (.global.sass non-module file) emits a side-effect ESM shim for a .global.sass file: written-files 1`] = ` +Map { + "/fake/output/dts/global-styles.global.sass.d.ts" => "export {};", + "/fake/output/css/global-styles.global.css" => "body { + margin: 0; + padding: 0; +} + +h1 { + font-size: 24px; + color: #333; +}", + "/fake/output/css/global-styles.global.sass.js" => "import \\"./global-styles.global.css\\";export {};", +} +`; + +exports[`SassProcessor global-styles.global.sass (.global.sass non-module file) emits export {}; in the .d.ts for a .global.sass file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor global-styles.global.sass (.global.sass non-module file) emits export {}; in the .d.ts for a .global.sass file: written-files 1`] = ` +Map { + "/fake/output/dts/global-styles.global.sass.d.ts" => "export {};", + "/fake/output/css/global-styles.global.css" => "body { + margin: 0; + padding: 0; +} + +h1 { + font-size: 24px; + color: #333; +}", +} +`; + +exports[`SassProcessor mixin-with-exports.module.scss (Sass @mixin) expands @mixin calls in CSS output: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor mixin-with-exports.module.scss (Sass @mixin) expands @mixin calls in CSS output: written-files 1`] = ` +Map { + "/fake/output/dts/mixin-with-exports.module.scss.d.ts" => "declare interface IStyles { + cardRadius: string; + animationDuration: string; + card: string; + \\"card--vertical\\": string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/mixin-with-exports.module.css" => ".card { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + border-radius: 4px; +} + +.card--vertical { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +}", +} +`; + +exports[`SassProcessor mixin-with-exports.module.scss (Sass @mixin) generates .d.ts with :export values and class names from @mixin-using file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor mixin-with-exports.module.scss (Sass @mixin) generates .d.ts with :export values and class names from @mixin-using file: written-files 1`] = ` +Map { + "/fake/output/dts/mixin-with-exports.module.scss.d.ts" => "declare interface IStyles { + cardRadius: string; + animationDuration: string; + card: string; + \\"card--vertical\\": string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/mixin-with-exports.module.css" => ".card { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + border-radius: 4px; +} + +.card--vertical { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +}", +} +`; + +exports[`SassProcessor mixin-with-exports.module.scss (Sass @mixin) preserves :export alongside expanded @mixin output when preserveIcssExports is true: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor mixin-with-exports.module.scss (Sass @mixin) preserves :export alongside expanded @mixin output when preserveIcssExports is true: written-files 1`] = ` +Map { + "/fake/output/dts/mixin-with-exports.module.scss.d.ts" => "declare interface IStyles { + cardRadius: string; + animationDuration: string; + card: string; + \\"card--vertical\\": string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/mixin-with-exports.module.css" => ".card { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + border-radius: 4px; +} + +.card--vertical { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +:export { + cardRadius: 4px; + animationDuration: 200ms; +}", +} +`; + +exports[`SassProcessor multiple output folders writes .d.ts to every configured dtsOutputFolder: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor multiple output folders writes .d.ts to every configured dtsOutputFolder: written-files 1`] = ` +Map { + "/fake/output/dts-a/export-only.module.scss.d.ts" => "declare interface IStyles { + primaryColor: string; + fontFamily: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/dts-b/export-only.module.scss.d.ts" => "declare interface IStyles { + primaryColor: string; + fontFamily: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/export-only.module.css" => "", +} +`; + +exports[`SassProcessor multiple output folders writes CSS to every configured cssOutputFolder: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor multiple output folders writes CSS to every configured cssOutputFolder: written-files 1`] = ` +Map { + "/fake/output/dts/export-only.module.scss.d.ts" => "declare interface IStyles { + primaryColor: string; + fontFamily: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css-a/export-only.module.css" => "", + "/fake/output/css-b/export-only.module.css" => "", +} +`; + +exports[`SassProcessor non-module (global) files emits export {}; in the .d.ts for a non-module file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor non-module (global) files emits export {}; in the .d.ts for a non-module file: written-files 1`] = ` +Map { + "/fake/output/dts/global-styles.global.scss.d.ts" => "export {};", + "/fake/output/css/global-styles.global.css" => "body { + margin: 0; + padding: 0; + font-family: \\"Segoe UI\\", sans-serif; +} + +h1 { + font-size: 24px; + color: #333; +}", +} +`; + +exports[`SassProcessor non-module (global) files emits plain compiled CSS for a .global.scss file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor non-module (global) files emits plain compiled CSS for a .global.scss file: written-files 1`] = ` +Map { + "/fake/output/dts/global-styles.global.scss.d.ts" => "export {};", + "/fake/output/css/global-styles.global.css" => "body { + margin: 0; + padding: 0; + font-family: \\"Segoe UI\\", sans-serif; +} + +h1 { + font-size: 24px; + color: #333; +}", +} +`; + +exports[`SassProcessor postProcessCssAsync passes compiled CSS through the post-processor callback: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor postProcessCssAsync passes compiled CSS through the post-processor callback: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/classes-and-exports.module.css" => ".root { + color: /* post-processed */ red; + font-size: 14px; +} + +.highlighted { + background-color: /* post-processed */ yellow; +}", +} +`; + +exports[`SassProcessor postProcessCssAsync post-processor receives the original CSS including :export when preserveIcssExports is true: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor postProcessCssAsync post-processor receives the original CSS including :export when preserveIcssExports is true: written-files 1`] = ` +Map { + "/fake/output/dts/export-only.module.scss.d.ts" => "declare interface IStyles { + primaryColor: string; + fontFamily: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/export-only.module.css" => ":export { + primaryColor: #0078d4; + fontFamily: \\"Segoe UI\\"; +}", +} +`; + +exports[`SassProcessor postProcessCssAsync post-processor runs after postcss-modules strips :export: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor postProcessCssAsync post-processor runs after postcss-modules strips :export: written-files 1`] = ` +Map { + "/fake/output/dts/export-only.module.scss.d.ts" => "declare interface IStyles { + primaryColor: string; + fontFamily: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/export-only.module.css" => "", +} +`; + +exports[`SassProcessor sass-variables-and-exports.module.scss (Sass variables, nesting, BEM) generates .d.ts with resolved :export keys as typed properties: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor sass-variables-and-exports.module.scss (Sass variables, nesting, BEM) generates .d.ts with resolved :export keys as typed properties: written-files 1`] = ` +Map { + "/fake/output/dts/sass-variables-and-exports.module.scss.d.ts" => "declare interface IStyles { + primaryColor: string; + secondaryColor: string; + baseSpacing: string; + container: string; + container__title: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/sass-variables-and-exports.module.css" => ".container { + color: #0078d4; + padding: 8px; +} +.container:hover { + color: #106ebe; +} +.container__title { + font-size: 16px; + margin-bottom: 16px; +}", +} +`; + +exports[`SassProcessor sass-variables-and-exports.module.scss (Sass variables, nesting, BEM) resolves Sass variables and expands nested rules in CSS output: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor sass-variables-and-exports.module.scss (Sass variables, nesting, BEM) resolves Sass variables and expands nested rules in CSS output: written-files 1`] = ` +Map { + "/fake/output/dts/sass-variables-and-exports.module.scss.d.ts" => "declare interface IStyles { + primaryColor: string; + secondaryColor: string; + baseSpacing: string; + container: string; + container__title: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/sass-variables-and-exports.module.css" => ".container { + color: #0078d4; + padding: 8px; +} +.container:hover { + color: #106ebe; +} +.container__title { + font-size: 16px; + margin-bottom: 16px; +}", +} +`; + +exports[`SassProcessor sass-variables-and-exports.module.scss (Sass variables, nesting, BEM) resolves Sass variables inside the :export block when preserveIcssExports is true: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor sass-variables-and-exports.module.scss (Sass variables, nesting, BEM) resolves Sass variables inside the :export block when preserveIcssExports is true: written-files 1`] = ` +Map { + "/fake/output/dts/sass-variables-and-exports.module.scss.d.ts" => "declare interface IStyles { + primaryColor: string; + secondaryColor: string; + baseSpacing: string; + container: string; + container__title: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/sass-variables-and-exports.module.css" => ".container { + color: #0078d4; + padding: 8px; +} +.container:hover { + color: #106ebe; +} +.container__title { + font-size: 16px; + margin-bottom: 16px; +} + +:export { + primaryColor: #0078d4; + secondaryColor: #106ebe; + baseSpacing: 8px; +}", +} +`; + +exports[`SassProcessor silenceDeprecations option throws at construction time when given an unknown deprecation code: terminal-output 1`] = `Array []`; + +exports[`SassProcessor silenceDeprecations option throws at construction time when given an unknown deprecation code: written-files 1`] = `Map {}`; + +exports[`SassProcessor simple.module.sass (indented Sass syntax) compiles indented Sass syntax to CSS correctly: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor simple.module.sass (indented Sass syntax) compiles indented Sass syntax to CSS correctly: written-files 1`] = ` +Map { + "/fake/output/dts/simple.module.sass.d.ts" => "declare interface IStyles { + exampleClass: string; + exampleHeading: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/simple.module.css" => ".exampleClass { + color: red; + font-size: 14px; +} + +.exampleHeading { + font-weight: bold; + color: navy; +}", +} +`; + +exports[`SassProcessor simple.module.sass (indented Sass syntax) emits an ESM shim for an indented Sass module file: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor simple.module.sass (indented Sass syntax) emits an ESM shim for an indented Sass module file: written-files 1`] = ` +Map { + "/fake/output/dts/simple.module.sass.d.ts" => "declare interface IStyles { + exampleClass: string; + exampleHeading: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/simple.module.css" => ".exampleClass { + color: red; + font-size: 14px; +} + +.exampleHeading { + font-weight: bold; + color: navy; +}", + "/fake/output/css/simple.module.sass.js" => "export { default } from \\"./simple.module.css\\";", +} +`; + +exports[`SassProcessor simple.module.sass (indented Sass syntax) generates .d.ts with class names from indented Sass: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor simple.module.sass (indented Sass syntax) generates .d.ts with class names from indented Sass: written-files 1`] = ` +Map { + "/fake/output/dts/simple.module.sass.d.ts" => "declare interface IStyles { + exampleClass: string; + exampleHeading: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/simple.module.css" => ".exampleClass { + color: red; + font-size: 14px; +} + +.exampleHeading { + font-weight: bold; + color: navy; +}", +} +`; + +exports[`SassProcessor sourceMap option does not emit .css.map or sourceMappingURL comment by default: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor sourceMap option does not emit .css.map or sourceMappingURL comment by default: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/classes-and-exports.module.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +}", +} +`; + +exports[`SassProcessor sourceMap option emits .css.map and sourceMappingURL comment when sourceMap is true: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor sourceMap option emits .css.map and sourceMappingURL comment when sourceMap is true: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/classes-and-exports.module.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +} +/*# sourceMappingURL=classes-and-exports.module.css.map */ +", + "/fake/output/css/classes-and-exports.module.css.map" => "{\\"version\\":3,\\"sourceRoot\\":\\"\\",\\"sources\\":[\\"fixtures/classes-and-exports.module.scss\\"],\\"names\\":[],\\"mappings\\":\\"AACA;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA\\",\\"sourcesContent\\":[\\"// A CSS module that exports both class names and ICSS :export values.\\\\n.root {\\\\n color: red;\\\\n font-size: 14px;\\\\n}\\\\n\\\\n.highlighted {\\\\n background-color: yellow;\\\\n}\\\\n\\\\n:export {\\\\n themeColor: blue;\\\\n spacing: 8px;\\\\n}\\\\n\\"],\\"file\\":\\"classes-and-exports.module.css\\"}", +} +`; + +exports[`SassProcessor sourceMap option emits a valid .css.map when the entry file @uses a partial (Linux/macOS regression): terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor sourceMap option emits a valid .css.map when the entry file @uses a partial (Linux/macOS regression): written-files 1`] = ` +Map { + "/fake/output/dts/use-with-partial.module.scss.d.ts" => "declare interface IStyles { + container: string; + header: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/use-with-partial.module.css" => ".container { + color: #0078d4; + padding: 8px; +} + +.header { + border-bottom: 1px solid #0078d4; +} +/*# sourceMappingURL=use-with-partial.module.css.map */ +", + "/fake/output/css/use-with-partial.module.css.map" => "{\\"version\\":3,\\"sourceRoot\\":\\"\\",\\"sources\\":[\\"fixtures/use-with-partial.module.scss\\",\\"fixtures/_partial.scss\\"],\\"names\\":[],\\"mappings\\":\\"AAIA;EACE,OCHY;EDIZ;;;AAGF;EACE\\",\\"sourcesContent\\":[\\"// Uses the modern @use syntax to import variables from a local partial.\\\\n// Verifies that SassProcessor resolves _partial.scss when @use 'partial' is written.\\\\n@use 'partial' as tokens;\\\\n\\\\n.container {\\\\n color: tokens.$brand-color;\\\\n padding: tokens.$spacing-unit * 2;\\\\n}\\\\n\\\\n.header {\\\\n border-bottom: 1px solid tokens.$brand-color;\\\\n}\\\\n\\",\\"// Sass partial exposing shared design tokens.\\\\n// Imported via @use 'partial' in use-with-partial.module.scss.\\\\n$brand-color: #0078d4;\\\\n$spacing-unit: 4px;\\\\n\\"],\\"file\\":\\"use-with-partial.module.css\\"}", +} +`; + +exports[`SassProcessor sourceMap option uses the correct map filename when doNotTrimOriginalFileExtension is true: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor sourceMap option uses the correct map filename when doNotTrimOriginalFileExtension is true: written-files 1`] = ` +Map { + "/fake/output/dts/classes-and-exports.module.scss.d.ts" => "declare interface IStyles { + themeColor: string; + spacing: string; + root: string; + highlighted: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/classes-and-exports.module.scss.css" => ".root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +} +/*# sourceMappingURL=classes-and-exports.module.scss.css.map */ +", + "/fake/output/css/classes-and-exports.module.scss.css.map" => "{\\"version\\":3,\\"sourceRoot\\":\\"\\",\\"sources\\":[\\"fixtures/classes-and-exports.module.scss\\"],\\"names\\":[],\\"mappings\\":\\"AACA;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA\\",\\"sourcesContent\\":[\\"// A CSS module that exports both class names and ICSS :export values.\\\\n.root {\\\\n color: red;\\\\n font-size: 14px;\\\\n}\\\\n\\\\n.highlighted {\\\\n background-color: yellow;\\\\n}\\\\n\\\\n:export {\\\\n themeColor: blue;\\\\n spacing: 8px;\\\\n}\\\\n\\"],\\"file\\":\\"classes-and-exports.module.scss.css\\"}", +} +`; + +exports[`SassProcessor use-plain-css.module.scss (@use of a plain .css file) resolves a plain .css file referenced via @use with an explicit extension: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor use-plain-css.module.scss (@use of a plain .css file) resolves a plain .css file referenced via @use with an explicit extension: written-files 1`] = ` +Map { + "/fake/output/dts/use-plain-css.module.scss.d.ts" => "declare interface IStyles { + \\"token-base\\": string; + container: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/use-plain-css.module.css" => "/* A plain CSS file (no Sass syntax) imported via @use from another stylesheet. */ +.token-base { + color: #0078d4; + display: block; +} + +.container { + font-weight: bold; +}", +} +`; + +exports[`SassProcessor use-plain-css.module.scss (@use of a plain .css file) resolves a plain .css file referenced via @use without an explicit extension: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor use-plain-css.module.scss (@use of a plain .css file) resolves a plain .css file referenced via @use without an explicit extension: written-files 1`] = ` +Map { + "/fake/output/dts/use-plain-css-extensionless.module.scss.d.ts" => "declare interface IStyles { + \\"token-base\\": string; + container: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/use-plain-css-extensionless.module.css" => "/* A plain CSS file (no Sass syntax) imported via @use from another stylesheet. */ +.token-base { + color: #0078d4; + display: block; +} + +.container { + font-weight: bold; +}", +} +`; + +exports[`SassProcessor use-with-partial.module.scss (@use with local partial) generates .d.ts with class names from a file using @use: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor use-with-partial.module.scss (@use with local partial) generates .d.ts with class names from a file using @use: written-files 1`] = ` +Map { + "/fake/output/dts/use-with-partial.module.scss.d.ts" => "declare interface IStyles { + container: string; + header: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/use-with-partial.module.css" => ".container { + color: #0078d4; + padding: 8px; +} + +.header { + border-bottom: 1px solid #0078d4; +}", +} +`; + +exports[`SassProcessor use-with-partial.module.scss (@use with local partial) resolves variables from a @use partial in CSS output: terminal-output 1`] = ` +Array [ + "[verbose] Checking for changes to 1 files...[n]", + "[ log] Compiling 1 files...[n]", +] +`; + +exports[`SassProcessor use-with-partial.module.scss (@use with local partial) resolves variables from a @use partial in CSS output: written-files 1`] = ` +Map { + "/fake/output/dts/use-with-partial.module.scss.d.ts" => "declare interface IStyles { + container: string; + header: string; +} +declare const styles: IStyles; +export default styles;", + "/fake/output/css/use-with-partial.module.css" => ".container { + color: #0078d4; + padding: 8px; +} + +.header { + border-bottom: 1px solid #0078d4; +}", +} +`; diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/_partial.scss b/heft-plugins/heft-sass-plugin/src/test/fixtures/_partial.scss new file mode 100644 index 00000000000..8fad83f62a2 --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/_partial.scss @@ -0,0 +1,4 @@ +// Sass partial exposing shared design tokens. +// Imported via @use 'partial' in use-with-partial.module.scss. +$brand-color: #0078d4; +$spacing-unit: 4px; diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/classes-and-exports.module.scss b/heft-plugins/heft-sass-plugin/src/test/fixtures/classes-and-exports.module.scss new file mode 100644 index 00000000000..8bd91e97f7e --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/classes-and-exports.module.scss @@ -0,0 +1,14 @@ +// A CSS module that exports both class names and ICSS :export values. +.root { + color: red; + font-size: 14px; +} + +.highlighted { + background-color: yellow; +} + +:export { + themeColor: blue; + spacing: 8px; +} diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/css-module.module.css b/heft-plugins/heft-sass-plugin/src/test/fixtures/css-module.module.css new file mode 100644 index 00000000000..5967c09e336 --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/css-module.module.css @@ -0,0 +1,11 @@ +/* Plain CSS file treated as a CSS module via fileExtensions: ['.module.css']. + Verifies that CSS input (not SCSS/Sass) is processed correctly. */ +.root { + display: flex; + flex-direction: column; +} + +.header { + font-size: 16px; + font-weight: bold; +} diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/export-only.module.scss b/heft-plugins/heft-sass-plugin/src/test/fixtures/export-only.module.scss new file mode 100644 index 00000000000..9b6278b7406 --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/export-only.module.scss @@ -0,0 +1,6 @@ +// A CSS module that only exports ICSS values - no class names. +// Used to verify that the :export block is preserved or stripped based on preserveIcssExports. +:export { + primaryColor: #0078d4; + fontFamily: 'Segoe UI'; +} diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/extend-with-exports.module.scss b/heft-plugins/heft-sass-plugin/src/test/fixtures/extend-with-exports.module.scss new file mode 100644 index 00000000000..f36921fbba9 --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/extend-with-exports.module.scss @@ -0,0 +1,28 @@ +// Tests that @extend / placeholder selectors work alongside :export. +%button-base { + display: inline-flex; + align-items: center; + cursor: pointer; + border: none; + border-radius: 2px; +} + +$color-primary: #0078d4; +$color-danger: #d13438; + +.primaryButton { + @extend %button-base; + background-color: $color-primary; + color: white; +} + +.dangerButton { + @extend %button-base; + background-color: $color-danger; + color: white; +} + +:export { + colorPrimary: #{$color-primary}; + colorDanger: #{$color-danger}; +} diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/global-only.module.scss b/heft-plugins/heft-sass-plugin/src/test/fixtures/global-only.module.scss new file mode 100644 index 00000000000..529102ce04f --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/global-only.module.scss @@ -0,0 +1,11 @@ +// A module SCSS file that contains only :global styles and no local CSS module class exports. +// This pattern is used for applying global overrides from a file named .module.scss. +:global { + .ms-Nav-group { + overflow: hidden; + } + + .ms-Nav-link { + height: 30px; + } +} diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/global-styles.global.sass b/heft-plugins/heft-sass-plugin/src/test/fixtures/global-styles.global.sass new file mode 100644 index 00000000000..ea0f6d07b8a --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/global-styles.global.sass @@ -0,0 +1,12 @@ +// Non-module global stylesheet in indented Sass syntax (.global.sass). +// Used to verify that .global.sass files are treated as non-modules. +$heading-size: 24px +$body-color: #333 + +body + margin: 0 + padding: 0 + +h1 + font-size: $heading-size + color: $body-color diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/global-styles.global.scss b/heft-plugins/heft-sass-plugin/src/test/fixtures/global-styles.global.scss new file mode 100644 index 00000000000..a03043e40b4 --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/global-styles.global.scss @@ -0,0 +1,15 @@ +// Non-module global stylesheet - processed as plain CSS, not a CSS module. +// Used to verify that global files produce no class exports and generate correct shims. +$body-font: 'Segoe UI', sans-serif; +$heading-color: #333; + +body { + margin: 0; + padding: 0; + font-family: $body-font; +} + +h1 { + font-size: 24px; + color: $heading-color; +} diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/invalid.module.scss b/heft-plugins/heft-sass-plugin/src/test/fixtures/invalid.module.scss new file mode 100644 index 00000000000..b91bcd341fd --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/invalid.module.scss @@ -0,0 +1,4 @@ +// Intentionally invalid SCSS - used to verify that SassProcessor emits errors correctly. +.broken { + color:; +} diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/mixin-with-exports.module.scss b/heft-plugins/heft-sass-plugin/src/test/fixtures/mixin-with-exports.module.scss new file mode 100644 index 00000000000..28a8f98b9bb --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/mixin-with-exports.module.scss @@ -0,0 +1,24 @@ +// Tests that @mixin expansion works correctly alongside :export. +@mixin flex-center($direction: row) { + display: flex; + flex-direction: $direction; + align-items: center; + justify-content: center; +} + +$card-radius: 4px; +$animation-duration: 200ms; + +.card { + @include flex-center; + border-radius: $card-radius; +} + +.card--vertical { + @include flex-center(column); +} + +:export { + cardRadius: #{$card-radius}; + animationDuration: #{$animation-duration}; +} diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/plain-tokens.css b/heft-plugins/heft-sass-plugin/src/test/fixtures/plain-tokens.css new file mode 100644 index 00000000000..7c04e73117d --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/plain-tokens.css @@ -0,0 +1,5 @@ +/* A plain CSS file (no Sass syntax) imported via @use from another stylesheet. */ +.token-base { + color: #0078d4; + display: block; +} diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/sass-variables-and-exports.module.scss b/heft-plugins/heft-sass-plugin/src/test/fixtures/sass-variables-and-exports.module.scss new file mode 100644 index 00000000000..eda77f65c1e --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/sass-variables-and-exports.module.scss @@ -0,0 +1,24 @@ +// Tests that Sass variables are resolved in both class rules and the :export block. +$primary-color: #0078d4; +$secondary-color: #106ebe; +$base-spacing: 8px; + +.container { + color: $primary-color; + padding: $base-spacing; + + &:hover { + color: $secondary-color; + } + + &__title { + font-size: 16px; + margin-bottom: $base-spacing * 2; + } +} + +:export { + primaryColor: #{$primary-color}; + secondaryColor: #{$secondary-color}; + baseSpacing: #{$base-spacing}; +} diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/simple.module.sass b/heft-plugins/heft-sass-plugin/src/test/fixtures/simple.module.sass new file mode 100644 index 00000000000..e9194c59387 --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/simple.module.sass @@ -0,0 +1,9 @@ +// Indented Sass syntax (whitespace-significant, no braces). +// Used to verify that .sass files are parsed with the indented syntax. +.exampleClass + color: red + font-size: 14px + +.exampleHeading + font-weight: bold + color: navy diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/use-plain-css-extensionless.module.scss b/heft-plugins/heft-sass-plugin/src/test/fixtures/use-plain-css-extensionless.module.scss new file mode 100644 index 00000000000..aea851f22c6 --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/use-plain-css-extensionless.module.scss @@ -0,0 +1,8 @@ +// Uses the modern @use syntax to import a plain `.css` file without an explicit extension. +// dart-sass resolves extensionless loads against `.scss`, `.sass`, then `.css`, so SassProcessor +// must fall back to the `.css` candidate when no Sass file exists. +@use './plain-tokens'; + +.container { + font-weight: bold; +} diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/use-plain-css.module.scss b/heft-plugins/heft-sass-plugin/src/test/fixtures/use-plain-css.module.scss new file mode 100644 index 00000000000..8f70c8b9fed --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/use-plain-css.module.scss @@ -0,0 +1,8 @@ +// Uses the modern @use syntax to import a plain `.css` file with an explicit extension. +// dart-sass supports loading plain CSS this way, so SassProcessor must resolve the literal +// `.css` file rather than probing for `.css.scss` / `.css.sass` candidates. +@use './plain-tokens.css'; + +.container { + font-weight: bold; +} diff --git a/heft-plugins/heft-sass-plugin/src/test/fixtures/use-with-partial.module.scss b/heft-plugins/heft-sass-plugin/src/test/fixtures/use-with-partial.module.scss new file mode 100644 index 00000000000..9f8b9e74ea0 --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/fixtures/use-with-partial.module.scss @@ -0,0 +1,12 @@ +// Uses the modern @use syntax to import variables from a local partial. +// Verifies that SassProcessor resolves _partial.scss when @use 'partial' is written. +@use 'partial' as tokens; + +.container { + color: tokens.$brand-color; + padding: tokens.$spacing-unit * 2; +} + +.header { + border-bottom: 1px solid tokens.$brand-color; +} diff --git a/heft-plugins/heft-sass-plugin/src/test/tempate.test.ts b/heft-plugins/heft-sass-plugin/src/test/tempate.test.ts new file mode 100644 index 00000000000..afdab02292a --- /dev/null +++ b/heft-plugins/heft-sass-plugin/src/test/tempate.test.ts @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { FileSystem, JsonFile, JsonSchema } from '@rushstack/node-core-library'; + +import schema from '../schemas/heft-sass-plugin.schema.json'; +import type { ISassConfigurationJson } from '../SassPlugin'; + +describe('sass.json template', () => { + it('should match the schema', async () => { + const templateText: string = await FileSystem.readFileAsync(`${__dirname}/../templates/sass.json`); + let uncommentedTemplateText: string = templateText.replace(/$\s*\/\/\s*/gm, ''); + uncommentedTemplateText = uncommentedTemplateText.replace('"extends":', ',"extends":'); + const template: ISassConfigurationJson = JsonFile.parseString(uncommentedTemplateText); + const jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schema); + expect(() => jsonSchema.validateObject(template, `${__dirname}/../templates/sass.json`)).not.toThrow(); + }); +}); diff --git a/heft-plugins/heft-sass-plugin/tsconfig.json b/heft-plugins/heft-sass-plugin/tsconfig.json index ff8c38a3615..fbf5b4d1ea6 100644 --- a/heft-plugins/heft-sass-plugin/tsconfig.json +++ b/heft-plugins/heft-sass-plugin/tsconfig.json @@ -1,8 +1,4 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["node"] - }, + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "include": ["src/**/*.ts", "./custom-typings/**/*.ts"] } diff --git a/heft-plugins/heft-serverless-stack-plugin/.eslintrc.js b/heft-plugins/heft-serverless-stack-plugin/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/heft-plugins/heft-serverless-stack-plugin/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/heft-plugins/heft-serverless-stack-plugin/.npmignore b/heft-plugins/heft-serverless-stack-plugin/.npmignore index 8515ab19dc6..f7a40e10213 100644 --- a/heft-plugins/heft-serverless-stack-plugin/.npmignore +++ b/heft-plugins/heft-serverless-stack-plugin/.npmignore @@ -8,24 +8,29 @@ !/lib/** !/lib-*/** !/dist/** -!ThirdPartyNotice.txt +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json !heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt # Ignore certain patterns that should not get published. /dist/*.stats.* /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-serverless-stack-plugin/CHANGELOG.json b/heft-plugins/heft-serverless-stack-plugin/CHANGELOG.json index fa15a3e87b1..dec602fcffa 100644 --- a/heft-plugins/heft-serverless-stack-plugin/CHANGELOG.json +++ b/heft-plugins/heft-serverless-stack-plugin/CHANGELOG.json @@ -1,6 +1,4089 @@ { "name": "@rushstack/heft-serverless-stack-plugin", "entries": [ + { + "version": "1.2.24", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.24", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "1.2.23", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.23", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "1.2.22", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.22", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "1.2.21", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.21", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "1.2.20", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.20", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "1.2.19", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.19", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "1.2.18", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.18", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "1.2.17", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.17", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "1.2.16", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.16", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "1.2.15", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.15", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "1.2.14", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.14", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "1.2.13", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.13", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "1.2.12", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.12", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "1.2.11", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.11", + "date": "Thu, 02 Apr 2026 00:14:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.10`" + } + ] + } + }, + { + "version": "1.2.10", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.10", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "1.2.9", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.9", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "1.2.8", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.8", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "1.2.7", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.7", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "1.2.6", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.6", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "1.2.5", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.5", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "1.2.4", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.4", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "1.2.3", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.3", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "1.2.2", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.2", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "1.2.1", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.1", + "date": "Thu, 19 Feb 2026 01:30:06 GMT", + "comments": { + "patch": [ + { + "comment": "Add missing LICENSE file to package." + } + ] + } + }, + { + "version": "1.2.0", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.2.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "1.1.15", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.15", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "1.1.14", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.14", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "1.1.13", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.13", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "1.1.12", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.12", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "1.1.11", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.11", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "1.1.10", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.10", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "1.1.9", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.9", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "1.1.8", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.8", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "1.1.7", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.7", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.5` to `1.1.6`" + } + ] + } + }, + { + "version": "1.1.6", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.6", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.4` to `1.1.5`" + } + ] + } + }, + { + "version": "1.1.5", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.5", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.3` to `1.1.4`" + } + ] + } + }, + { + "version": "1.1.4", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.4", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.2` to `1.1.3`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.3", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.1` to `1.1.2`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.2", + "date": "Fri, 17 Oct 2025 23:22:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.1`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.1", + "date": "Wed, 08 Oct 2025 00:13:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.0` to `1.1.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.1.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.0.0` to `1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-serverless-stack-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.75.0` to `1.0.0`" + } + ] + } + }, + { + "version": "0.4.22", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.22", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.113`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.43`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.5` to `0.75.0`" + } + ] + } + }, + { + "version": "0.4.21", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.21", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.112`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.42`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.4` to `0.74.5`" + } + ] + } + }, + { + "version": "0.4.20", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.20", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.111`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.41`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.3` to `0.74.4`" + } + ] + } + }, + { + "version": "0.4.19", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.19", + "date": "Fri, 29 Aug 2025 00:08:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.110`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.40`" + } + ] + } + }, + { + "version": "0.4.18", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.18", + "date": "Tue, 26 Aug 2025 00:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.109`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.39`" + } + ] + } + }, + { + "version": "0.4.17", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.17", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.108`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.2` to `0.74.3`" + } + ] + } + }, + { + "version": "0.4.16", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.16", + "date": "Fri, 01 Aug 2025 00:12:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.107`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.1` to `0.74.2`" + } + ] + } + }, + { + "version": "0.4.15", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.15", + "date": "Sat, 26 Jul 2025 00:12:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.106`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.36`" + } + ] + } + }, + { + "version": "0.4.14", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.14", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.105`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.0` to `0.74.1`" + } + ] + } + }, + { + "version": "0.4.13", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.13", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.104`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.6` to `0.74.0`" + } + ] + } + }, + { + "version": "0.4.12", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.12", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.103`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.5` to `0.73.6`" + } + ] + } + }, + { + "version": "0.4.11", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.11", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.102`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.4` to `0.73.5`" + } + ] + } + }, + { + "version": "0.4.10", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.10", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.101`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.3` to `0.73.4`" + } + ] + } + }, + { + "version": "0.4.9", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.9", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.100`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.2` to `0.73.3`" + } + ] + } + }, + { + "version": "0.4.8", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.8", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.99`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.1` to `0.73.2`" + } + ] + } + }, + { + "version": "0.4.7", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.7", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.98`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.0` to `0.73.1`" + } + ] + } + }, + { + "version": "0.4.6", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.6", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.97`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.72.0` to `0.73.0`" + } + ] + } + }, + { + "version": "0.4.5", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.5", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.96`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.2` to `0.72.0`" + } + ] + } + }, + { + "version": "0.4.4", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.4", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.95`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.1` to `0.71.2`" + } + ] + } + }, + { + "version": "0.4.3", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.3", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.94`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.0` to `0.71.1`" + } + ] + } + }, + { + "version": "0.4.2", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.2", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.93`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.1` to `0.71.0`" + } + ] + } + }, + { + "version": "0.4.1", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.1", + "date": "Wed, 12 Mar 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.92`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.0` to `0.70.1`" + } + ] + } + }, + { + "version": "0.4.0", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.4.0", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "minor": [ + { + "comment": "Use `useNodeJSResolver: true` in `Import.resolvePackage` calls." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.91`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.3` to `0.70.0`" + } + ] + } + }, + { + "version": "0.3.91", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.91", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.90`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.2` to `0.69.3`" + } + ] + } + }, + { + "version": "0.3.90", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.90", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.89`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.1` to `0.69.2`" + } + ] + } + }, + { + "version": "0.3.89", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.89", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.88`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.0` to `0.69.1`" + } + ] + } + }, + { + "version": "0.3.88", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.88", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.87`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.18` to `0.69.0`" + } + ] + } + }, + { + "version": "0.3.87", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.87", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.86`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.17` to `0.68.18`" + } + ] + } + }, + { + "version": "0.3.86", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.86", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.85`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.16` to `0.68.17`" + } + ] + } + }, + { + "version": "0.3.85", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.85", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.84`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.15` to `0.68.16`" + } + ] + } + }, + { + "version": "0.3.84", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.84", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.83`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.14` to `0.68.15`" + } + ] + } + }, + { + "version": "0.3.83", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.83", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.82`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.13` to `0.68.14`" + } + ] + } + }, + { + "version": "0.3.82", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.82", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.81`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.12` to `0.68.13`" + } + ] + } + }, + { + "version": "0.3.81", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.81", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.80`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.11` to `0.68.12`" + } + ] + } + }, + { + "version": "0.3.80", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.80", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.79`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.10` to `0.68.11`" + } + ] + } + }, + { + "version": "0.3.79", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.79", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.78`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.9` to `0.68.10`" + } + ] + } + }, + { + "version": "0.3.78", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.78", + "date": "Tue, 03 Dec 2024 16:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.77`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.8` to `0.68.9`" + } + ] + } + }, + { + "version": "0.3.77", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.77", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.76`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.7` to `0.68.8`" + } + ] + } + }, + { + "version": "0.3.76", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.76", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.75`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.6` to `0.68.7`" + } + ] + } + }, + { + "version": "0.3.75", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.75", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.74`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.5` to `0.68.6`" + } + ] + } + }, + { + "version": "0.3.74", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.74", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.73`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.4` to `0.68.5`" + } + ] + } + }, + { + "version": "0.3.73", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.73", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.72`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.3` to `0.68.4`" + } + ] + } + }, + { + "version": "0.3.72", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.72", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.71`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.2` to `0.68.3`" + } + ] + } + }, + { + "version": "0.3.71", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.71", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.70`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.1` to `0.68.2`" + } + ] + } + }, + { + "version": "0.3.70", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.70", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.69`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.0` to `0.68.1`" + } + ] + } + }, + { + "version": "0.3.69", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.69", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.68`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.2` to `0.68.0`" + } + ] + } + }, + { + "version": "0.3.68", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.68", + "date": "Sat, 21 Sep 2024 00:10:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.67`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.12`" + } + ] + } + }, + { + "version": "0.3.67", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.67", + "date": "Fri, 13 Sep 2024 00:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.66`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.1` to `0.67.2`" + } + ] + } + }, + { + "version": "0.3.66", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.66", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.65`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.0` to `0.67.1`" + } + ] + } + }, + { + "version": "0.3.65", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.65", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.64`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.26` to `0.67.0`" + } + ] + } + }, + { + "version": "0.3.64", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.64", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.63`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.25` to `0.66.26`" + } + ] + } + }, + { + "version": "0.3.63", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.63", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.62`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.24` to `0.66.25`" + } + ] + } + }, + { + "version": "0.3.62", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.62", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.61`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.23` to `0.66.24`" + } + ] + } + }, + { + "version": "0.3.61", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.61", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.60`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.22` to `0.66.23`" + } + ] + } + }, + { + "version": "0.3.60", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.60", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.59`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.21` to `0.66.22`" + } + ] + } + }, + { + "version": "0.3.59", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.59", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.58`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.20` to `0.66.21`" + } + ] + } + }, + { + "version": "0.3.58", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.58", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.57`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.19` to `0.66.20`" + } + ] + } + }, + { + "version": "0.3.57", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.57", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.56`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.18` to `0.66.19`" + } + ] + } + }, + { + "version": "0.3.56", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.56", + "date": "Fri, 07 Jun 2024 15:10:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.0`" + } + ] + } + }, + { + "version": "0.3.55", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.55", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.55`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.55`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.17` to `0.66.18`" + } + ] + } + }, + { + "version": "0.3.54", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.54", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.54`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.54`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.16` to `0.66.17`" + } + ] + } + }, + { + "version": "0.3.53", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.53", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.53`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.53`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.15` to `0.66.16`" + } + ] + } + }, + { + "version": "0.3.52", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.52", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.52`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.52`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.14` to `0.66.15`" + } + ] + } + }, + { + "version": "0.3.51", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.51", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.51`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.51`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.13` to `0.66.14`" + } + ] + } + }, + { + "version": "0.3.50", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.50", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.50`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.50`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.12` to `0.66.13`" + } + ] + } + }, + { + "version": "0.3.49", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.49", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.49`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.49`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.11` to `0.66.12`" + } + ] + } + }, + { + "version": "0.3.48", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.48", + "date": "Fri, 24 May 2024 00:15:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.48`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.48`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.10` to `0.66.11`" + } + ] + } + }, + { + "version": "0.3.47", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.47", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.47`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.47`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.9` to `0.66.10`" + } + ] + } + }, + { + "version": "0.3.46", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.46", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.46`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.46`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.8` to `0.66.9`" + } + ] + } + }, + { + "version": "0.3.45", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.45", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.45`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.45`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.7` to `0.66.8`" + } + ] + } + }, + { + "version": "0.3.44", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.44", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.44`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.44`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.6` to `0.66.7`" + } + ] + } + }, + { + "version": "0.3.43", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.43", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.43`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.43`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.5` to `0.66.6`" + } + ] + } + }, + { + "version": "0.3.42", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.42", + "date": "Wed, 08 May 2024 22:23:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.42`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.42`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.4` to `0.66.5`" + } + ] + } + }, + { + "version": "0.3.41", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.41", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.41`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.41`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.3` to `0.66.4`" + } + ] + } + }, + { + "version": "0.3.40", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.40", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.40`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.40`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.2` to `0.66.3`" + } + ] + } + }, + { + "version": "0.3.39", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.39", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.39`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.39`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.1` to `0.66.2`" + } + ] + } + }, + { + "version": "0.3.38", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.38", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.0` to `0.66.1`" + } + ] + } + }, + { + "version": "0.3.37", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.37", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.10` to `0.66.0`" + } + ] + } + }, + { + "version": "0.3.36", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.36", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.9` to `0.65.10`" + } + ] + } + }, + { + "version": "0.3.35", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.35", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.8` to `0.65.9`" + } + ] + } + }, + { + "version": "0.3.34", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.34", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.7` to `0.65.8`" + } + ] + } + }, + { + "version": "0.3.33", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.33", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.6` to `0.65.7`" + } + ] + } + }, + { + "version": "0.3.32", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.32", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.5` to `0.65.6`" + } + ] + } + }, + { + "version": "0.3.31", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.31", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.4` to `^0.65.5`" + } + ] + } + }, + { + "version": "0.3.30", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.30", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.3` to `^0.65.4`" + } + ] + } + }, + { + "version": "0.3.29", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.29", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.2` to `^0.65.3`" + } + ] + } + }, + { + "version": "0.3.28", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.28", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.1` to `^0.65.2`" + } + ] + } + }, + { + "version": "0.3.27", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.27", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.0` to `^0.65.1`" + } + ] + } + }, + { + "version": "0.3.26", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.26", + "date": "Tue, 20 Feb 2024 16:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.8` to `^0.65.0`" + } + ] + } + }, + { + "version": "0.3.25", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.25", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.7` to `^0.64.8`" + } + ] + } + }, + { + "version": "0.3.24", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.24", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.6` to `^0.64.7`" + } + ] + } + }, + { + "version": "0.3.23", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.23", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.5` to `^0.64.6`" + } + ] + } + }, + { + "version": "0.3.22", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.22", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.4` to `^0.64.5`" + } + ] + } + }, + { + "version": "0.3.21", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.21", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.3` to `^0.64.4`" + } + ] + } + }, + { + "version": "0.3.20", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.20", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.2` to `^0.64.3`" + } + ] + } + }, + { + "version": "0.3.19", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.19", + "date": "Tue, 23 Jan 2024 20:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.1` to `^0.64.2`" + } + ] + } + }, + { + "version": "0.3.18", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.18", + "date": "Tue, 23 Jan 2024 16:15:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.0` to `^0.64.1`" + } + ] + } + }, + { + "version": "0.3.17", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.17", + "date": "Tue, 16 Jan 2024 18:30:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.6` to `^0.64.0`" + } + ] + } + }, + { + "version": "0.3.16", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.16", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.5` to `^0.63.6`" + } + ] + } + }, + { + "version": "0.3.15", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.15", + "date": "Wed, 20 Dec 2023 01:09:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.4` to `^0.63.5`" + } + ] + } + }, + { + "version": "0.3.14", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.3` to `^0.63.4`" + } + ] + } + }, + { + "version": "0.3.13", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.2` to `^0.63.3`" + } + ] + } + }, + { + "version": "0.3.12", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.1` to `^0.63.2`" + } + ] + } + }, + { + "version": "0.3.11", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.0` to `^0.63.1`" + } + ] + } + }, + { + "version": "0.3.10", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.10", + "date": "Mon, 30 Oct 2023 23:36:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.3` to `^0.63.0`" + } + ] + } + }, + { + "version": "0.3.9", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.9", + "date": "Sun, 01 Oct 2023 02:56:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.2` to `^0.62.3`" + } + ] + } + }, + { + "version": "0.3.8", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.1` to `^0.62.2`" + } + ] + } + }, + { + "version": "0.3.7", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.0` to `^0.62.1`" + } + ] + } + }, + { + "version": "0.3.6", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.6", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.3` to `^0.62.0`" + } + ] + } + }, + { + "version": "0.3.5", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.2` to `^0.61.3`" + } + ] + } + }, + { + "version": "0.3.4", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.1` to `^0.61.2`" + } + ] + } + }, + { + "version": "0.3.3", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.0` to `^0.61.1`" + } + ] + } + }, + { + "version": "0.3.2", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.2", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.60.0` to `^0.61.0`" + } + ] + } + }, + { + "version": "0.3.1", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.1", + "date": "Tue, 19 Sep 2023 15:21:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.59.0` to `^0.60.0`" + } + ] + } + }, + { + "version": "0.3.0", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.3.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.2` to `^0.59.0`" + } + ] + } + }, + { + "version": "0.2.28", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.28", + "date": "Wed, 13 Sep 2023 00:32:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.9.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.15`" + } + ] + } + }, + { + "version": "0.2.27", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.27", + "date": "Thu, 07 Sep 2023 03:35:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.9.0`" + } + ] + } + }, + { + "version": "0.2.26", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.26", + "date": "Sat, 12 Aug 2023 00:21:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.8.0`" + } + ] + } + }, + { + "version": "0.2.25", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.25", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.1` to `^0.58.2`" + } + ] + } + }, + { + "version": "0.2.24", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.24", + "date": "Mon, 31 Jul 2023 15:19:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.13`" + } + ] + } + }, + { + "version": "0.2.23", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.23", + "date": "Sat, 29 Jul 2023 00:22:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.0` to `^0.58.1`" + } + ] + } + }, + { + "version": "0.2.22", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.22", + "date": "Thu, 20 Jul 2023 20:47:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.57.1` to `^0.58.0`" + } + ] + } + }, + { + "version": "0.2.21", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.21", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.57.0` to `^0.57.1`" + } + ] + } + }, + { + "version": "0.2.20", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.20", + "date": "Fri, 14 Jul 2023 15:20:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.9`" + } + ] + } + }, + { + "version": "0.2.19", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.19", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.3` to `^0.57.0`" + } + ] + } + }, + { + "version": "0.2.18", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.18", + "date": "Wed, 12 Jul 2023 15:20:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.2` to `^0.56.3`" + } + ] + } + }, + { + "version": "0.2.17", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.17", + "date": "Wed, 12 Jul 2023 00:23:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.6`" + } + ] + } + }, + { + "version": "0.2.16", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.16", + "date": "Fri, 07 Jul 2023 00:19:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.1` to `^0.56.2`" + } + ] + } + }, + { + "version": "0.2.15", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.15", + "date": "Thu, 06 Jul 2023 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.0` to `^0.56.1`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.14", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.3`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.13", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.2` to `^0.56.0`" + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.12", + "date": "Thu, 15 Jun 2023 00:21:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.1` to `^0.55.2`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.11", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.0` to `^0.55.1`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.10", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.54.0` to `^0.55.0`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.9", + "date": "Tue, 13 Jun 2023 01:49:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.53.1` to `^0.54.0`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.8", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.53.0` to `^0.53.1`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.7", + "date": "Fri, 09 Jun 2023 15:23:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.7`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.6", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.2` to `^0.53.0`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.5", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.1` to `^0.52.2`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.4", + "date": "Thu, 08 Jun 2023 00:20:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.0` to `^0.52.1`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.3", + "date": "Wed, 07 Jun 2023 22:45:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.51.0` to `^0.52.0`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.2", + "date": "Tue, 06 Jun 2023 02:52:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.2`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.1", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.1`" + } + ] + } + }, { "version": "0.2.0", "tag": "@rushstack/heft-serverless-stack-plugin_v0.2.0", diff --git a/heft-plugins/heft-serverless-stack-plugin/CHANGELOG.md b/heft-plugins/heft-serverless-stack-plugin/CHANGELOG.md index 768f73e1332..fe2feadaea3 100644 --- a/heft-plugins/heft-serverless-stack-plugin/CHANGELOG.md +++ b/heft-plugins/heft-serverless-stack-plugin/CHANGELOG.md @@ -1,6 +1,951 @@ # Change Log - @rushstack/heft-serverless-stack-plugin -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 1.2.24 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 1.2.23 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 1.2.22 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.2.21 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 1.2.20 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 1.2.19 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.2.18 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.2.17 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 1.2.16 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 1.2.15 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.2.14 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.2.13 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.2.12 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.2.11 +Thu, 02 Apr 2026 00:14:38 GMT + +_Version update only_ + +## 1.2.10 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.2.9 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.2.8 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.2.7 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.2.6 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 1.2.5 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.2.4 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.2.3 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.2.2 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.2.1 +Thu, 19 Feb 2026 01:30:06 GMT + +### Patches + +- Add missing LICENSE file to package. + +## 1.2.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.1.15 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.1.14 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.1.13 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.1.12 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.1.11 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.1.10 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 1.1.9 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 1.1.8 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.1.7 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.1.6 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.1.5 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 1.1.4 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.1.3 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.1.2 +Fri, 17 Oct 2025 23:22:33 GMT + +_Version update only_ + +## 1.1.1 +Wed, 08 Oct 2025 00:13:28 GMT + +_Version update only_ + +## 1.1.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.4.22 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.4.21 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.4.20 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.4.19 +Fri, 29 Aug 2025 00:08:01 GMT + +_Version update only_ + +## 0.4.18 +Tue, 26 Aug 2025 00:12:57 GMT + +_Version update only_ + +## 0.4.17 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.4.16 +Fri, 01 Aug 2025 00:12:48 GMT + +_Version update only_ + +## 0.4.15 +Sat, 26 Jul 2025 00:12:22 GMT + +_Version update only_ + +## 0.4.14 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.4.13 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.4.12 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.4.11 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.4.10 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.4.9 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.4.8 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.4.7 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.4.6 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 0.4.5 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 0.4.4 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.4.3 +Tue, 25 Mar 2025 15:11:15 GMT + +_Version update only_ + +## 0.4.2 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.4.1 +Wed, 12 Mar 2025 00:11:32 GMT + +_Version update only_ + +## 0.4.0 +Tue, 11 Mar 2025 02:12:33 GMT + +### Minor changes + +- Use `useNodeJSResolver: true` in `Import.resolvePackage` calls. + +## 0.3.91 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.3.90 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.3.89 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.3.88 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 0.3.87 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.3.86 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.3.85 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.3.84 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 0.3.83 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.3.82 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.3.81 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.3.80 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.3.79 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.3.78 +Tue, 03 Dec 2024 16:11:07 GMT + +_Version update only_ + +## 0.3.77 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.3.76 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.3.75 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.3.74 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.3.73 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.3.72 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.3.71 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.3.70 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.3.69 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.3.68 +Sat, 21 Sep 2024 00:10:27 GMT + +_Version update only_ + +## 0.3.67 +Fri, 13 Sep 2024 00:11:42 GMT + +_Version update only_ + +## 0.3.66 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.3.65 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.3.64 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.3.63 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.3.62 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.3.61 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 0.3.60 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 0.3.59 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.3.58 +Tue, 16 Jul 2024 00:36:21 GMT + +_Version update only_ + +## 0.3.57 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.3.56 +Fri, 07 Jun 2024 15:10:25 GMT + +_Version update only_ + +## 0.3.55 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.3.54 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.3.53 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 0.3.52 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.3.51 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.3.50 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.3.49 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.3.48 +Fri, 24 May 2024 00:15:08 GMT + +_Version update only_ + +## 0.3.47 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 0.3.46 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.3.45 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.3.44 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.3.43 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.3.42 +Wed, 08 May 2024 22:23:50 GMT + +_Version update only_ + +## 0.3.41 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 0.3.40 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.3.39 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 0.3.38 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 0.3.37 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 0.3.36 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 0.3.35 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 0.3.34 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 0.3.33 +Thu, 29 Feb 2024 07:11:45 GMT + +_Version update only_ + +## 0.3.32 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.3.31 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.3.30 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.3.29 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.3.28 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.3.27 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.3.26 +Tue, 20 Feb 2024 16:10:52 GMT + +_Version update only_ + +## 0.3.25 +Mon, 19 Feb 2024 21:54:27 GMT + +_Version update only_ + +## 0.3.24 +Sat, 17 Feb 2024 06:24:35 GMT + +_Version update only_ + +## 0.3.23 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 0.3.22 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.3.21 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.3.20 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.3.19 +Tue, 23 Jan 2024 20:12:57 GMT + +_Version update only_ + +## 0.3.18 +Tue, 23 Jan 2024 16:15:06 GMT + +_Version update only_ + +## 0.3.17 +Tue, 16 Jan 2024 18:30:11 GMT + +_Version update only_ + +## 0.3.16 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.3.15 +Wed, 20 Dec 2023 01:09:45 GMT + +_Version update only_ + +## 0.3.14 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.3.13 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.3.12 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 0.3.11 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 0.3.10 +Mon, 30 Oct 2023 23:36:37 GMT + +_Version update only_ + +## 0.3.9 +Sun, 01 Oct 2023 02:56:29 GMT + +_Version update only_ + +## 0.3.8 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 0.3.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.3.6 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 0.3.5 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 0.3.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.3.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 0.3.2 +Fri, 22 Sep 2023 00:05:50 GMT + +_Version update only_ + +## 0.3.1 +Tue, 19 Sep 2023 15:21:52 GMT + +_Version update only_ + +## 0.3.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 0.2.28 +Wed, 13 Sep 2023 00:32:29 GMT + +_Version update only_ + +## 0.2.27 +Thu, 07 Sep 2023 03:35:43 GMT + +_Version update only_ + +## 0.2.26 +Sat, 12 Aug 2023 00:21:48 GMT + +_Version update only_ + +## 0.2.25 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 0.2.24 +Mon, 31 Jul 2023 15:19:05 GMT + +_Version update only_ + +## 0.2.23 +Sat, 29 Jul 2023 00:22:51 GMT + +_Version update only_ + +## 0.2.22 +Thu, 20 Jul 2023 20:47:28 GMT + +_Version update only_ + +## 0.2.21 +Wed, 19 Jul 2023 00:20:31 GMT + +_Version update only_ + +## 0.2.20 +Fri, 14 Jul 2023 15:20:45 GMT + +_Version update only_ + +## 0.2.19 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 0.2.18 +Wed, 12 Jul 2023 15:20:39 GMT + +_Version update only_ + +## 0.2.17 +Wed, 12 Jul 2023 00:23:29 GMT + +_Version update only_ + +## 0.2.16 +Fri, 07 Jul 2023 00:19:32 GMT + +_Version update only_ + +## 0.2.15 +Thu, 06 Jul 2023 00:16:19 GMT + +_Version update only_ + +## 0.2.14 +Tue, 04 Jul 2023 00:18:47 GMT + +_Version update only_ + +## 0.2.13 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 0.2.12 +Thu, 15 Jun 2023 00:21:02 GMT + +_Version update only_ + +## 0.2.11 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 0.2.10 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 0.2.9 +Tue, 13 Jun 2023 01:49:02 GMT + +_Version update only_ + +## 0.2.8 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 0.2.7 +Fri, 09 Jun 2023 15:23:15 GMT + +_Version update only_ + +## 0.2.6 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 0.2.5 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 0.2.4 +Thu, 08 Jun 2023 00:20:02 GMT + +_Version update only_ + +## 0.2.3 +Wed, 07 Jun 2023 22:45:17 GMT + +_Version update only_ + +## 0.2.2 +Tue, 06 Jun 2023 02:52:51 GMT + +_Version update only_ + +## 0.2.1 +Mon, 05 Jun 2023 21:45:21 GMT + +_Version update only_ ## 0.2.0 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/heft-plugins/heft-serverless-stack-plugin/LICENSE b/heft-plugins/heft-serverless-stack-plugin/LICENSE new file mode 100644 index 00000000000..da79b869ac5 --- /dev/null +++ b/heft-plugins/heft-serverless-stack-plugin/LICENSE @@ -0,0 +1,24 @@ +@rushstack/heft-serverless-stack-plugin + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/heft-plugins/heft-serverless-stack-plugin/config/rig.json b/heft-plugins/heft-serverless-stack-plugin/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/heft-plugins/heft-serverless-stack-plugin/config/rig.json +++ b/heft-plugins/heft-serverless-stack-plugin/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/heft-plugins/heft-serverless-stack-plugin/eslint.config.js b/heft-plugins/heft-serverless-stack-plugin/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/heft-plugins/heft-serverless-stack-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-serverless-stack-plugin/heft-plugin.json b/heft-plugins/heft-serverless-stack-plugin/heft-plugin.json index 383abc5217d..ff9ecc11ae8 100644 --- a/heft-plugins/heft-serverless-stack-plugin/heft-plugin.json +++ b/heft-plugins/heft-serverless-stack-plugin/heft-plugin.json @@ -1,10 +1,10 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-plugin.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", "taskPlugins": [ { "pluginName": "serverless-stack-plugin", - "entryPoint": "./lib/ServerlessStackPlugin", + "entryPoint": "./lib-commonjs/ServerlessStackPlugin", "parameterScope": "serverless-stack", "parameters": [ diff --git a/heft-plugins/heft-serverless-stack-plugin/package.json b/heft-plugins/heft-serverless-stack-plugin/package.json index 1531633c3ee..e24042f86a2 100644 --- a/heft-plugins/heft-serverless-stack-plugin/package.json +++ b/heft-plugins/heft-serverless-stack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/heft-serverless-stack-plugin", - "version": "0.2.0", + "version": "1.2.24", "description": "Heft plugin for building apps using the Serverless Stack (SST) framework", "repository": { "type": "git", @@ -15,17 +15,34 @@ "_phase:test": "heft run --only test -- --clean" }, "peerDependencies": { - "@rushstack/heft": "^0.51.0" + "@rushstack/heft": "^1.2.22" }, "dependencies": { "@rushstack/node-core-library": "workspace:*" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", "@rushstack/heft-webpack4-plugin": "workspace:*", "@rushstack/heft-webpack5-plugin": "workspace:*", - "@types/node": "14.18.36" - } + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "sideEffects": false } diff --git a/heft-plugins/heft-serverless-stack-plugin/src/ServerlessStackPlugin.ts b/heft-plugins/heft-serverless-stack-plugin/src/ServerlessStackPlugin.ts index c5717c4992c..3dd5368e47a 100644 --- a/heft-plugins/heft-serverless-stack-plugin/src/ServerlessStackPlugin.ts +++ b/heft-plugins/heft-serverless-stack-plugin/src/ServerlessStackPlugin.ts @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as process from 'process'; -import * as child_process from 'child_process'; +import * as path from 'node:path'; +import * as process from 'node:process'; +import * as child_process from 'node:child_process'; + import type { CommandLineFlagParameter, CommandLineStringParameter, @@ -84,7 +85,8 @@ export default class ServerlessStackPlugin implements IHeftTaskPlugin { try { sstCliPackagePath = Import.resolvePackage({ packageName: SST_CLI_PACKAGE_NAME, - baseFolderPath: options.heftConfiguration.buildFolderPath + baseFolderPath: options.heftConfiguration.buildFolderPath, + useNodeJSResolver: true }); } catch (e) { this._logger.emitError( diff --git a/heft-plugins/heft-serverless-stack-plugin/tsconfig.json b/heft-plugins/heft-serverless-stack-plugin/tsconfig.json index 7512871fdbf..dac21d04081 100644 --- a/heft-plugins/heft-serverless-stack-plugin/tsconfig.json +++ b/heft-plugins/heft-serverless-stack-plugin/tsconfig.json @@ -1,7 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["node"] - } + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/heft-plugins/heft-static-asset-typings-plugin/.npmignore b/heft-plugins/heft-static-asset-typings-plugin/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-static-asset-typings-plugin/CHANGELOG.json b/heft-plugins/heft-static-asset-typings-plugin/CHANGELOG.json new file mode 100644 index 00000000000..7fce250195e --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/CHANGELOG.json @@ -0,0 +1,431 @@ +{ + "name": "@rushstack/heft-static-asset-typings-plugin", + "entries": [ + { + "version": "0.1.19", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.19", + "date": "Tue, 04 Aug 2026 00:17:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.17.0`" + } + ] + } + }, + { + "version": "0.1.18", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.18", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "0.1.17", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.17", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "0.1.16", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.16", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.11`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "0.1.15", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.15", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "0.1.14", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.14", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "0.1.13", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.13", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.10`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "0.1.12", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.12", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "0.1.11", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.11", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.9`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "0.1.10", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.10", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "0.1.9", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.9", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "0.1.8", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.8", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.6`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "0.1.7", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.7", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "0.1.6", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.6", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "0.1.5", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.5", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "0.1.4", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.4", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "0.1.3", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.3", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.2", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.1", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.3` to `1.2.5`" + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/heft-static-asset-typings-plugin_v0.1.0", + "date": "Tue, 24 Feb 2026 02:08:44 GMT", + "comments": { + "minor": [ + { + "comment": "Initial release." + } + ] + } + } + ] +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/CHANGELOG.md b/heft-plugins/heft-static-asset-typings-plugin/CHANGELOG.md new file mode 100644 index 00000000000..65b4dec32cb --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/CHANGELOG.md @@ -0,0 +1,106 @@ +# Change Log - @rushstack/heft-static-asset-typings-plugin + +This log was last generated on Tue, 04 Aug 2026 00:17:24 GMT and should not be manually modified. + +## 0.1.19 +Tue, 04 Aug 2026 00:17:24 GMT + +_Version update only_ + +## 0.1.18 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 0.1.17 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 0.1.16 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.1.15 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.1.14 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 0.1.13 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.1.12 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 0.1.11 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 0.1.10 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 0.1.9 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.1.8 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 0.1.7 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.1.6 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 0.1.5 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 0.1.4 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.1.3 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 0.1.2 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.1.1 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 0.1.0 +Tue, 24 Feb 2026 02:08:44 GMT + +### Minor changes + +- Initial release. + diff --git a/heft-plugins/heft-static-asset-typings-plugin/LICENSE b/heft-plugins/heft-static-asset-typings-plugin/LICENSE new file mode 100644 index 00000000000..9570e2a1a6f --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/LICENSE @@ -0,0 +1,24 @@ +@rushstack/heft-static-asset-typings-plugin + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/heft-plugins/heft-static-asset-typings-plugin/README.md b/heft-plugins/heft-static-asset-typings-plugin/README.md new file mode 100644 index 00000000000..6fbaf7bf110 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/README.md @@ -0,0 +1,230 @@ +# @rushstack/heft-static-asset-typings-plugin + +This Heft plugin generates TypeScript `.d.ts` typings for static asset files, enabling type-safe +`import` statements for non-TypeScript files. It provides two task plugins: + +- **`resource-assets-plugin`** - Generates `.d.ts` typings for _resource_ files such as images (`.png`, + `.jpg`, `.svg`, etc.) and fonts. These are opaque binary blobs whose content is not meaningful to + JavaScript; the generated typing simply exports a default `string` representing the asset URL + (e.g. as resolved by a bundler's asset loader). + +- **`source-assets-plugin`** - Generates `.d.ts` typings _and_ JavaScript module output for _source_ + files (`.html`, `.css`, `.txt`, `.md`, etc.) whose textual content is consumed at runtime. The + generated JS modules read the file and re-export its content as a default `string`, making these + assets importable as ES modules. + +The terminology follows the [webpack convention](https://webpack.js.org/guides/asset-modules/) +where _resource_ assets are emitted as separate files referenced by URL, while _source_ assets are +inlined as strings. + +Both plugins support incremental and watch-mode builds. + +## Setup + +1. Add the plugin as a `devDependency` of your project: + + ```bash + rush add -p @rushstack/heft-static-asset-typings-plugin --dev + ``` + +2. Load the appropriate plugin(s) in your project's **config/heft.json**: + + ### Resource assets (images, fonts, etc.) + + **Inline configuration** - specify options directly in heft.json: + + ```jsonc + { + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "phasesByName": { + "build": { + "tasksByName": { + "image-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "resource-assets-plugin", + "options": { + "configType": "inline", + "config": { + "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], + "generatedTsFolders": ["temp/image-typings"] + } + } + } + }, + "typescript": { + "taskDependencies": ["image-typings"] + // ... + } + } + } + } + } + ``` + + **File configuration** - load settings from a riggable config file: + + ```jsonc + { + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "phasesByName": { + "build": { + "tasksByName": { + "image-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "resource-assets-plugin", + "options": { + "configType": "file", + "configFileName": "resource-assets.json" + } + } + }, + "typescript": { + "taskDependencies": ["image-typings"] + // ... + } + } + } + } + } + ``` + + And create a **config/resource-assets.json** file (which can be provided by a rig): + + ```jsonc + { + "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], + "generatedTsFolders": ["temp/image-typings"] + } + ``` + + ### Source assets + + **Inline configuration:** + + ```jsonc + { + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "phasesByName": { + "build": { + "tasksByName": { + "text-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "source-assets-plugin", + "options": { + "configType": "inline", + "config": { + "fileExtensions": [".html"], + "cjsOutputFolders": ["lib-commonjs"], + "esmOutputFolders": ["lib-esm"], + "generatedTsFolders": ["temp/text-typings"] + } + } + } + }, + "typescript": { + "taskDependencies": ["text-typings"] + // ... + } + } + } + } + } + ``` + + **File configuration:** + + ```jsonc + { + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "phasesByName": { + "build": { + "tasksByName": { + "text-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "source-assets-plugin", + "options": { + "configType": "file", + "configFileName": "source-assets.json" + } + } + }, + "typescript": { + "taskDependencies": ["text-typings"] + // ... + } + } + } + } + } + ``` + + And create a **config/source-assets.json** file (which can be provided by a rig): + + ```jsonc + { + "fileExtensions": [".html"], + "cjsOutputFolders": ["lib-commonjs"], + "esmOutputFolders": ["lib-esm"], + "generatedTsFolders": ["temp/text-typings"] + } + ``` + +3. Add the generated typings folder to your **tsconfig.json** `rootDirs` so that + TypeScript can resolve the declarations: + + ```jsonc + { + "compilerOptions": { + "rootDirs": ["src", "temp/image-typings"] + } + } + ``` + +## Plugin options + +Both plugins support two configuration modes via the `configType` option: + +### Inline mode (`configType: "inline"`) + +Provide configuration directly in heft.json under `options.config`: + +#### `resource-assets-plugin` inline config + +| Option | Type | Default | Description | +| ------------------- | ---------- | ------------------------ | ----------------------------------------------- | +| `fileExtensions` | `string[]` | - | **(required)** File extensions to generate typings for. | +| `generatedTsFolders`| `string[]` | `["temp/static-asset-ts"]` | Folders where generated `.d.ts` files are written. The first entry should be listed in `rootDirs` so TypeScript can resolve the asset imports during type-checking. Additional entries are typically your project's published typings folder(s). | +| `sourceFolderPath` | `string` | `"src"` | Source folder to scan for asset files. | + +#### `source-assets-plugin` inline config + +Includes all the above, plus: + +| Option | Type | Default | Description | +| ------------------- | ---------- | ------------------------ | ---------------------------------------------------- | +| `cjsOutputFolders` | `string[]` | - | **(required)** Output folders for generated CommonJS `.js` modules. | +| `esmOutputFolders` | `string[]` | `[]` | Output folders for generated ESM `.js` modules. | + +### File mode (`configType: "file"`) + +Load configuration from a riggable JSON config file in the project's `config/` folder: + +| Option | Type | Description | +| ---------------- | -------- | ---------------------------------------------------------------------- | +| `configFileName` | `string` | **(required)** Name of the JSON config file in the `config/` folder. | + +The config file supports the same properties as inline mode (see tables above). Config files +can be provided by a rig, making file mode ideal for shared build configurations. + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/heft-plugins/heft-static-asset-typings-plugin/CHANGELOG.md) - Find + out what's new in the latest version +- [@rushstack/heft](https://www.npmjs.com/package/@rushstack/heft) - Heft is a config-driven toolchain that invokes popular tools such as TypeScript, ESLint, Jest, Webpack, and API Extractor. + +Heft is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/heft-plugins/heft-static-asset-typings-plugin/config/heft.json b/heft-plugins/heft-static-asset-typings-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/config/jest.config.json b/heft-plugins/heft-static-asset-typings-plugin/config/jest.config.json new file mode 100644 index 00000000000..d1749681d90 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/config/jest.config.json @@ -0,0 +1,3 @@ +{ + "extends": "local-node-rig/profiles/default/config/jest.config.json" +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/config/rig.json b/heft-plugins/heft-static-asset-typings-plugin/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/eslint.config.js b/heft-plugins/heft-static-asset-typings-plugin/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-static-asset-typings-plugin/heft-plugin.json b/heft-plugins/heft-static-asset-typings-plugin/heft-plugin.json new file mode 100644 index 00000000000..73e8a17518f --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/heft-plugin.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", + + "taskPlugins": [ + { + "pluginName": "resource-assets-plugin", + "entryPoint": "./lib-commonjs/ResourceAssetsPlugin", + "optionsSchema": "./lib-commonjs/schemas/resource-assets-options.schema.json" + }, + { + "pluginName": "source-assets-plugin", + "entryPoint": "./lib-commonjs/SourceAssetsPlugin", + "optionsSchema": "./lib-commonjs/schemas/source-assets-options.schema.json" + } + ] +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/package.json b/heft-plugins/heft-static-asset-typings-plugin/package.json new file mode 100644 index 00000000000..072ea05c213 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/package.json @@ -0,0 +1,50 @@ +{ + "name": "@rushstack/heft-static-asset-typings-plugin", + "version": "0.1.19", + "description": "A Heft plugin that generates TypeScript typings for static asset files such as images and text files.", + "scripts": { + "build": "heft build --clean", + "start": "heft test --clean --watch", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "repository": { + "type": "git", + "url": "https://github.com/microsoft/rushstack.git", + "directory": "heft-plugins/heft-static-asset-typings-plugin" + }, + "homepage": "https://rushstack.io/pages/heft/overview/", + "exports": { + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "license": "MIT", + "peerDependencies": { + "@rushstack/heft": "^1.2.22" + }, + "dependencies": { + "@rushstack/heft-config-file": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "@rushstack/terminal": "workspace:*", + "@rushstack/typings-generator": "workspace:*" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + } +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/ResourceAssetsPlugin.ts b/heft-plugins/heft-static-asset-typings-plugin/src/ResourceAssetsPlugin.ts new file mode 100644 index 00000000000..253f625fea6 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/src/ResourceAssetsPlugin.ts @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { HeftConfiguration, IHeftTaskSession, IHeftTaskPlugin } from '@rushstack/heft'; + +import { + createTypingsGeneratorAsync, + tryGetConfigFromPluginOptionsAsync, + type IRunGeneratorOptions, + type IStaticAssetGeneratorOptions, + type IStaticAssetTypingsGenerator +} from './StaticAssetTypingsGenerator'; +import type { IAssetPluginOptions, IResourceStaticAssetTypingsConfigurationJson } from './types'; + +const PLUGIN_NAME: 'static-asset-typings-plugin' = 'static-asset-typings-plugin'; + +export default class ResourceAssetsPlugin + implements IHeftTaskPlugin> +{ + /** + * Generate typings for text files before TypeScript compilation. + */ + public apply( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + options: IAssetPluginOptions + ): void { + const { slashNormalizedBuildFolderPath, rigConfig } = heftConfiguration; + const staticAssetGeneratorOptions: IStaticAssetGeneratorOptions = { + tryGetConfigAsync: async (terminal) => { + return await tryGetConfigFromPluginOptionsAsync( + terminal, + slashNormalizedBuildFolderPath, + rigConfig, + options, + 'resource' + ); + }, + slashNormalizedBuildFolderPath, + getVersionAndEmitOutputFilesAsync: async () => 'versionless' + }; + + let generatorPromise: Promise | undefined; + + async function createAndRunGeneratorAsync(runOptions: IRunGeneratorOptions): Promise { + if (generatorPromise === undefined) { + generatorPromise = createTypingsGeneratorAsync(taskSession, staticAssetGeneratorOptions); + } + + const generator: IStaticAssetTypingsGenerator | false = await generatorPromise; + if (generator === false) { + return; + } + + await generator.runIncrementalAsync(runOptions); + } + + taskSession.hooks.run.tapPromise(PLUGIN_NAME, createAndRunGeneratorAsync); + taskSession.hooks.runIncremental.tapPromise(PLUGIN_NAME, createAndRunGeneratorAsync); + } +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/SourceAssetsPlugin.ts b/heft-plugins/heft-static-asset-typings-plugin/src/SourceAssetsPlugin.ts new file mode 100644 index 00000000000..edc1b09cf2a --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/src/SourceAssetsPlugin.ts @@ -0,0 +1,144 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { createHash } from 'node:crypto'; + +import type { HeftConfiguration, IHeftTaskSession, IHeftTaskPlugin } from '@rushstack/heft'; +import { Async, FileSystem } from '@rushstack/node-core-library'; + +import { + createTypingsGeneratorAsync, + tryGetConfigFromPluginOptionsAsync, + type IRunGeneratorOptions, + type IStaticAssetGeneratorOptions, + type IStaticAssetTypingsGenerator +} from './StaticAssetTypingsGenerator'; +import type { IAssetPluginOptions, ISourceStaticAssetTypingsConfigurationJson } from './types'; + +const PLUGIN_NAME: 'source-assets-plugin' = 'source-assets-plugin'; + +// Pre-allocated preamble/postamble buffers to avoid repeated allocations. +// Used with FileSystem.writeBuffersToFileAsync (writev) for efficient output. +const CJS_PREAMBLE: Buffer = Buffer.from( + '"use strict"\nObject.defineProperty(exports, "__esModule", { value: true });\nvar content = ' +); +const CJS_POSTAMBLE: Buffer = Buffer.from(';\nexports.default = content;\n'); +const ESM_PREAMBLE: Buffer = Buffer.from('const content = '); +const ESM_POSTAMBLE: Buffer = Buffer.from(';\nexport default content;\n'); + +export default class SourceAssetsPlugin + implements IHeftTaskPlugin> +{ + /** + * Generate typings for text files before TypeScript compilation. + */ + public apply( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + pluginOptions: IAssetPluginOptions + ): void { + let generatorPromise: Promise | undefined; + + async function initializeGeneratorAsync(): Promise { + const { slashNormalizedBuildFolderPath, rigConfig } = heftConfiguration; + + const options: ISourceStaticAssetTypingsConfigurationJson | undefined = + await tryGetConfigFromPluginOptionsAsync( + taskSession.logger.terminal, + slashNormalizedBuildFolderPath, + rigConfig, + pluginOptions, + 'source' + ); + + if (options) { + const { fileExtensions, sourceFolderPath, generatedTsFolders, cjsOutputFolders, esmOutputFolders } = + options; + + const resolvedCjsOutputFolders: string[] = cjsOutputFolders.map( + (jsPath) => `${slashNormalizedBuildFolderPath}/${jsPath}` + ); + const resolvedEsmOutputFolders: string[] = + esmOutputFolders?.map((jsPath) => `${slashNormalizedBuildFolderPath}/${jsPath}`) ?? []; + const jsOutputFolders: string[] = [...resolvedCjsOutputFolders, ...resolvedEsmOutputFolders]; + + function getAdditionalOutputFiles(relativePath: string): string[] { + return jsOutputFolders.map((folder) => `${folder}/${relativePath}.js`); + } + + async function getVersionAndEmitOutputFilesAsync( + filePath: string, + relativePath: string, + oldVersion: string | undefined + ): Promise { + const fileContents: Buffer = await FileSystem.readFileToBufferAsync(filePath); + const fileVersion: string = createHash('sha1').update(fileContents).digest('base64'); + if (fileVersion === oldVersion) { + return; + } + + const stringFileContents: string = fileContents.toString('utf8'); + + const contentBuffer: Buffer = Buffer.from(JSON.stringify(stringFileContents)); + + const outputs: { path: string; buffers: NodeJS.ArrayBufferView[] }[] = []; + for (const folder of resolvedCjsOutputFolders) { + outputs.push({ + path: `${folder}/${relativePath}.js`, + buffers: [CJS_PREAMBLE, contentBuffer, CJS_POSTAMBLE] + }); + } + for (const folder of resolvedEsmOutputFolders) { + outputs.push({ + path: `${folder}/${relativePath}.js`, + buffers: [ESM_PREAMBLE, contentBuffer, ESM_POSTAMBLE] + }); + } + + await Async.forEachAsync( + outputs, + async ({ path, buffers }) => { + await FileSystem.writeBuffersToFileAsync(path, buffers, { ensureFolderExists: true }); + }, + { concurrency: 10 } + ); + + return fileVersion; + } + + const staticAssetGeneratorOptions: IStaticAssetGeneratorOptions = { + tryGetConfigAsync: async () => { + return { + fileExtensions, + sourceFolderPath, + generatedTsFolders + }; + }, + slashNormalizedBuildFolderPath, + getAdditionalOutputFiles, + getVersionAndEmitOutputFilesAsync + }; + + return createTypingsGeneratorAsync(taskSession, staticAssetGeneratorOptions); + } else { + return false; + } + } + + async function createAndRunGeneratorAsync(runOptions: IRunGeneratorOptions): Promise { + if (generatorPromise === undefined) { + generatorPromise = initializeGeneratorAsync(); + } + + const generator: IStaticAssetTypingsGenerator | false = await generatorPromise; + if (generator === false) { + return; + } + + await generator.runIncrementalAsync(runOptions); + } + + taskSession.hooks.run.tapPromise(PLUGIN_NAME, createAndRunGeneratorAsync); + taskSession.hooks.runIncremental.tapPromise(PLUGIN_NAME, createAndRunGeneratorAsync); + } +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts new file mode 100644 index 00000000000..41fc438f00b --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts @@ -0,0 +1,304 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { createHash } from 'node:crypto'; + +import type { + HeftConfiguration, + IHeftTaskRunHookOptions, + IHeftTaskRunIncrementalHookOptions, + IHeftTaskSession, + IWatchedFileState +} from '@rushstack/heft'; +import { TypingsGenerator } from '@rushstack/typings-generator'; +import { FileSystem, Sort } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import type { + IAssetPluginOptions, + IResourceStaticAssetTypingsConfigurationJson, + ISourceStaticAssetTypingsConfigurationJson, + StaticAssetConfigurationFileLoader +} from './types'; + +// Use explicit \n to avoid platform-dependent line endings in template literals. +const DECLARATION: string = [ + '/**', + ' * @public', + ' */', + 'declare const content: string;', + 'export default content;', + '' +].join('\n'); + +// Include a hash of DECLARATION so the cache is invalidated if the declaration content changes. +const PLUGIN_VERSION: string = `1-${createHash('sha1').update(DECLARATION).digest('hex').slice(0, 8)}`; + +/** + * Options for constructing a static asset typings generator + */ +export interface IStaticAssetGeneratorOptions { + /** + * A getter for the loader for the riggable config file in the project. + */ + tryGetConfigAsync: StaticAssetConfigurationFileLoader; + + /** + * The path to the build folder, normalized to use forward slashes as the directory separator. + */ + slashNormalizedBuildFolderPath: string; + + /** + * @param relativePath - The relative path of the file to get additional output files for. + * @returns An array of output file names. + */ + getAdditionalOutputFiles?: (relativePath: string) => string[]; + + /** + * @param relativePath - The relative path of the file being processed. + * @param filePath - The absolute path of the file being processed. + * @param oldVersion - The old version of the file, if any. + * @returns The new version of the file, if emit should occur. + */ + getVersionAndEmitOutputFilesAsync: ( + relativePath: string, + filePath: string, + oldVersion: string | undefined + ) => Promise; +} + +export type IRunGeneratorOptions = IHeftTaskRunHookOptions & + Partial>; + +export interface IStaticAssetTypingsGenerator { + /** + * Runs this generator in incremental mode. + * + * @param runOptions - The task run hook options from Heft + * @returns A promise that resolves when the generator has finished processing. + */ + runIncrementalAsync: (runOptions: IRunGeneratorOptions) => Promise; +} + +interface IStaticAssetTypingsBuildInfoFile { + fileVersions: [string, string][]; + pluginVersion: string; +} + +export async function tryGetConfigFromPluginOptionsAsync( + terminal: ITerminal, + buildFolder: string, + rigConfig: HeftConfiguration['rigConfig'], + options: IAssetPluginOptions, + type: 'resource' +): Promise; +export async function tryGetConfigFromPluginOptionsAsync( + terminal: ITerminal, + buildFolder: string, + rigConfig: HeftConfiguration['rigConfig'], + options: IAssetPluginOptions, + type: 'source' +): Promise; +export async function tryGetConfigFromPluginOptionsAsync( + terminal: ITerminal, + buildFolder: string, + rigConfig: HeftConfiguration['rigConfig'], + options: IAssetPluginOptions< + IResourceStaticAssetTypingsConfigurationJson | ISourceStaticAssetTypingsConfigurationJson + >, + type: import('./getConfigFromConfigFileAsync').FileLoaderType +): Promise< + IResourceStaticAssetTypingsConfigurationJson | ISourceStaticAssetTypingsConfigurationJson | undefined +> { + if (options?.configType === 'inline') { + return options.config; + } else { + const { getConfigFromConfigFileAsync } = await import('./getConfigFromConfigFileAsync'); + const { configFileName } = options; + return getConfigFromConfigFileAsync(configFileName, type, terminal, buildFolder, rigConfig); + } +} + +/** + * Constructs a typings generator for processing static assets + * + * @param taskSession - The Heft task session + * @param rigConfig - The Heft configuration + * @param options - Options for the generator + * @returns + */ +export async function createTypingsGeneratorAsync( + taskSession: IHeftTaskSession, + options: IStaticAssetGeneratorOptions +): Promise { + const { tryGetConfigAsync, slashNormalizedBuildFolderPath } = options; + + const { terminal } = taskSession.logger; + + const configuration: IResourceStaticAssetTypingsConfigurationJson | undefined = + await tryGetConfigAsync(terminal); + + if (!configuration) { + return false; + } + + const { + generatedTsFolders = ['temp/static-asset-ts'], + sourceFolderPath = 'src', + fileExtensions + } = configuration; + const resolvedGeneratedTsFolders: string[] | undefined = generatedTsFolders.map( + (folder) => `${slashNormalizedBuildFolderPath}/${folder}` + ); + const [generatedTsFolder, ...secondaryGeneratedTsFolders] = resolvedGeneratedTsFolders; + + const { getAdditionalOutputFiles, getVersionAndEmitOutputFilesAsync } = options; + + const fileVersions: Map = new Map(); + + const typingsGenerator: TypingsGenerator = new TypingsGenerator({ + srcFolder: `${slashNormalizedBuildFolderPath}/${sourceFolderPath}`, + generatedTsFolder, + secondaryGeneratedTsFolders, + fileExtensions, + terminal, + // eslint-disable-next-line @typescript-eslint/naming-convention + parseAndGenerateTypings: async ( + fileContents: boolean, + filePath: string, + relativePath: string + ): Promise => { + const oldFileVersion: string | undefined = fileVersions.get(relativePath); + const fileVersion: string | undefined = await getVersionAndEmitOutputFilesAsync( + filePath, + relativePath, + oldFileVersion + ); + if (fileVersion === undefined) { + return; + } + + fileVersions.set(relativePath, fileVersion); + if (oldFileVersion) { + // Since DECLARATION is constant, no point re-emitting the declarations just because the input content changed. + return; + } + + return DECLARATION; + }, + readFile: (filePath: string, relativePath: string): boolean => { + return false; + }, + getAdditionalOutputFiles + }); + + // TODO: Heft has an internal incremental cache layer (IncrementalBuildInfo) used by built-in + // plugins like CopyFilesPlugin. It is not currently part of Heft's public API surface. If it + // becomes public, we should migrate to it instead of managing our own cache file. + const cacheFilePath: string = `${taskSession.tempFolderPath}/static-assets.json`; + try { + const cacheFileContent: string = await FileSystem.readFileAsync(cacheFilePath); + const oldCacheFile: IStaticAssetTypingsBuildInfoFile = JSON.parse(cacheFileContent); + if (oldCacheFile.pluginVersion === PLUGIN_VERSION) { + for (const [relativePath, version] of oldCacheFile.fileVersions) { + fileVersions.set(relativePath, version); + } + } + } catch (e) { + terminal.writeVerboseLine(`Failed to read cache file: ${e}`); + } + + return { + async runIncrementalAsync(runOptions: IRunGeneratorOptions): Promise { + await runTypingsGeneratorIncrementalAsync( + taskSession, + typingsGenerator, + cacheFilePath, + fileVersions, + runOptions + ); + } + }; +} + +/** + * Invokes the specified typings generator on any files changed since the last invocation. + * If the cache file has been deleted (e.g. via a `--clean` run), will process all files. + * + * @param taskSession - The Heft task session. + * @param typingsGenerator - The typings generator to invoke. + * @param cacheFilePath - The path to the file that will contain the last build file version metadata. + * @param fileVersions - The map of current file versions. + * @param heftRunOptions - The task options from Heft. + * @returns A promise that resolves when the typings generator has finished processing. + */ +async function runTypingsGeneratorIncrementalAsync( + taskSession: IHeftTaskSession, + typingsGenerator: TypingsGenerator, + cacheFilePath: string, + fileVersions: Map, + heftRunOptions: IRunGeneratorOptions +): Promise { + const { terminal } = taskSession.logger; + + const originalFileVersions: ReadonlyMap = new Map(fileVersions); + + // If we have the incremental options, use them to determine which files to process. + // Otherwise, process all files. The typings generator also provides the file paths + // as relative paths from the sourceFolderPath. + let changedRelativeFilePaths: string[] | undefined; + const { watchGlobAsync } = heftRunOptions as IHeftTaskRunIncrementalHookOptions; + if (watchGlobAsync) { + changedRelativeFilePaths = []; + const relativeFilePaths: Map = await watchGlobAsync( + typingsGenerator.inputFileGlob, + { + cwd: typingsGenerator.sourceFolderPath, + ignore: Array.from(typingsGenerator.ignoredFileGlobs), + absolute: false + } + ); + for (const [relativeFilePath, { changed }] of relativeFilePaths) { + if (changed) { + changedRelativeFilePaths.push(relativeFilePath); + } + } + + if (changedRelativeFilePaths.length === 0) { + return; + } + } + + terminal.writeLine('Processing static assets...'); + await typingsGenerator.generateTypingsAsync(changedRelativeFilePaths); + + if (hasChanges(fileVersions, originalFileVersions)) { + const fileVersionsArray: [string, string][] = Array.from(fileVersions); + Sort.sortBy(fileVersionsArray, ([relativePath]) => relativePath); + + const buildFile: IStaticAssetTypingsBuildInfoFile = { + fileVersions: fileVersionsArray, + pluginVersion: PLUGIN_VERSION + }; + await FileSystem.writeFileAsync(cacheFilePath, JSON.stringify(buildFile), { ensureFolderExists: true }); + } + terminal.writeLine('Finished processing static assets.'); +} + +/** + * @internal + * Returns true if the current map has different entries than the old map. + */ +export function hasChanges(current: ReadonlyMap, old: ReadonlyMap): boolean { + if (current.size !== old.size) { + return true; + } + + for (const [key, value] of current) { + if (old.get(key) !== value) { + return true; + } + } + + return false; +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts b/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts new file mode 100644 index 00000000000..c25341890c4 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { HeftConfiguration } from '@rushstack/heft'; +import { InheritanceType, ProjectConfigurationFile } from '@rushstack/heft-config-file'; +import type { ITerminal } from '@rushstack/terminal'; + +import type { + IResourceStaticAssetTypingsConfigurationJson, + ISourceStaticAssetTypingsConfigurationJson +} from './types'; +import resourceStaticAssetSchema from './schemas/resource-static-asset-typings.schema.json'; +import sourceStaticAssetSchema from './schemas/source-static-asset-typings.schema.json'; + +const configurationFileLoaderByFileName: Map< + string, + ProjectConfigurationFile< + IResourceStaticAssetTypingsConfigurationJson | ISourceStaticAssetTypingsConfigurationJson + > +> = new Map(); + +export type FileLoaderType = 'resource' | 'source'; + +function createConfigurationFileLoader( + configFileName: string, + fileLoaderType: FileLoaderType +): ProjectConfigurationFile< + IResourceStaticAssetTypingsConfigurationJson | ISourceStaticAssetTypingsConfigurationJson +> { + return new ProjectConfigurationFile< + IResourceStaticAssetTypingsConfigurationJson | ISourceStaticAssetTypingsConfigurationJson + >({ + jsonSchemaObject: fileLoaderType === 'resource' ? resourceStaticAssetSchema : sourceStaticAssetSchema, + projectRelativeFilePath: `config/${configFileName}`, + propertyInheritance: { + fileExtensions: { + inheritanceType: InheritanceType.append + } + } + }); +} + +export function getConfigFromConfigFileAsync( + configFileName: string, + fileLoaderType: FileLoaderType, + terminal: ITerminal, + slashNormalizedBuildFolderPath: string, + rigConfig: HeftConfiguration['rigConfig'] +): Promise< + IResourceStaticAssetTypingsConfigurationJson | ISourceStaticAssetTypingsConfigurationJson | undefined +> { + let configurationFileLoader: + | ProjectConfigurationFile< + IResourceStaticAssetTypingsConfigurationJson | ISourceStaticAssetTypingsConfigurationJson + > + | undefined = configurationFileLoaderByFileName.get(configFileName); + if (!configurationFileLoader) { + configurationFileLoader = createConfigurationFileLoader(configFileName, fileLoaderType); + configurationFileLoaderByFileName.set(configFileName, configurationFileLoader); + } + + return configurationFileLoader.tryLoadConfigurationFileForProjectAsync( + terminal, + slashNormalizedBuildFolderPath, + rigConfig + ); +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/resource-assets-options.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/resource-assets-options.schema.json new file mode 100644 index 00000000000..749715a9f63 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/resource-assets-options.schema.json @@ -0,0 +1,58 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "required": ["configType", "config"], + "properties": { + "configType": { + "type": "string", + "enum": ["inline"] + }, + "config": { + "required": ["fileExtensions"], + "type": "object", + "additionalProperties": false, + "properties": { + "fileExtensions": { + "type": "array", + "items": { + "type": "string", + "pattern": "\\.[^\\\\/]+$" + } + }, + "generatedTsFolders": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[^\\\\]+$" + } + }, + "sourceFolderPath": { + "type": "string", + "pattern": "^[^\\\\]+$" + } + } + } + } + }, + + { + "type": "object", + "additionalProperties": false, + "required": ["configType", "configFileName"], + "properties": { + "configType": { + "type": "string", + "enum": ["file"] + }, + "configFileName": { + "type": "string", + "pattern": "^[^\\\\\\/]+\\.json$" + } + } + } + ] +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/resource-static-asset-typings.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/resource-static-asset-typings.schema.json new file mode 100644 index 00000000000..640f68eb4dd --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/resource-static-asset-typings.schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "additionalProperties": false, + "required": ["fileExtensions"], + "properties": { + "$schema": { + "type": "string" + }, + "fileExtensions": { + "type": "array", + "items": { + "pattern": "\\.[^\\\\/]+$", + "type": "string" + } + }, + "generatedTsFolders": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[^\\\\]+$" + } + }, + "sourceFolderPath": { + "type": "string", + "pattern": "^[^\\\\]+$" + } + } +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/source-assets-options.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/source-assets-options.schema.json new file mode 100644 index 00000000000..6dd4d724767 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/source-assets-options.schema.json @@ -0,0 +1,72 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "required": ["configType", "config"], + "properties": { + "configType": { + "type": "string", + "enum": ["inline"] + }, + "config": { + "required": ["fileExtensions", "cjsOutputFolders"], + "type": "object", + "additionalProperties": false, + "properties": { + "fileExtensions": { + "type": "array", + "items": { + "type": "string", + "pattern": "\\.[^\\\\/]+$" + } + }, + "generatedTsFolders": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[^\\\\]+$" + } + }, + "sourceFolderPath": { + "type": "string", + "pattern": "^[^\\\\]+$" + }, + "cjsOutputFolders": { + "type": "array", + "items": { + "pattern": "^[^\\\\]+$", + "type": "string" + } + }, + "esmOutputFolders": { + "type": "array", + "items": { + "pattern": "^[^\\\\]+$", + "type": "string" + } + } + } + } + } + }, + + { + "type": "object", + "additionalProperties": false, + "required": ["configType", "configFileName"], + "properties": { + "configType": { + "type": "string", + "enum": ["file"] + }, + "configFileName": { + "type": "string", + "pattern": "^[^\\\\\\/]+\\.json$" + } + } + } + ] +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/source-static-asset-typings.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/source-static-asset-typings.schema.json new file mode 100644 index 00000000000..482c5181c87 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/source-static-asset-typings.schema.json @@ -0,0 +1,43 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "additionalProperties": false, + "required": ["fileExtensions", "cjsOutputFolders"], + "properties": { + "$schema": { + "type": "string" + }, + "fileExtensions": { + "type": "array", + "items": { + "pattern": "\\.[^\\\\/]+$", + "type": "string" + } + }, + "generatedTsFolders": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[^\\\\]+$" + } + }, + "sourceFolderPath": { + "type": "string", + "pattern": "^[^\\\\]+$" + }, + "cjsOutputFolders": { + "type": "array", + "items": { + "pattern": "^[^\\\\]+$", + "type": "string" + } + }, + "esmOutputFolders": { + "type": "array", + "items": { + "pattern": "^[^\\\\]+$", + "type": "string" + } + } + } +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/test/StaticAssetTypingsGenerator.test.ts b/heft-plugins/heft-static-asset-typings-plugin/src/test/StaticAssetTypingsGenerator.test.ts new file mode 100644 index 00000000000..bc550c55ce6 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/src/test/StaticAssetTypingsGenerator.test.ts @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { hasChanges } from '../StaticAssetTypingsGenerator'; + +describe('hasChanges', () => { + it('returns false for two empty maps', () => { + const current: Map = new Map(); + const old: Map = new Map(); + expect(hasChanges(current, old)).toBe(false); + }); + + it('returns false for identical maps', () => { + const current: Map = new Map([ + ['a.png', 'v1'], + ['b.png', 'v2'] + ]); + const old: Map = new Map([ + ['a.png', 'v1'], + ['b.png', 'v2'] + ]); + expect(hasChanges(current, old)).toBe(false); + }); + + it('returns true when current has more entries', () => { + const current: Map = new Map([ + ['a.png', 'v1'], + ['b.png', 'v2'] + ]); + const old: Map = new Map([['a.png', 'v1']]); + expect(hasChanges(current, old)).toBe(true); + }); + + it('returns true when old has more entries', () => { + const current: Map = new Map([['a.png', 'v1']]); + const old: Map = new Map([ + ['a.png', 'v1'], + ['b.png', 'v2'] + ]); + expect(hasChanges(current, old)).toBe(true); + }); + + it('returns true when a value differs', () => { + const current: Map = new Map([ + ['a.png', 'v1'], + ['b.png', 'v3'] + ]); + const old: Map = new Map([ + ['a.png', 'v1'], + ['b.png', 'v2'] + ]); + expect(hasChanges(current, old)).toBe(true); + }); + + it('returns true when a key differs', () => { + const current: Map = new Map([['a.png', 'v1']]); + const old: Map = new Map([['b.png', 'v1']]); + expect(hasChanges(current, old)).toBe(true); + }); + + it('returns true when current is empty and old has entries', () => { + const current: Map = new Map(); + const old: Map = new Map([['a.png', 'v1']]); + expect(hasChanges(current, old)).toBe(true); + }); + + it('returns true when current has entries and old is empty', () => { + const current: Map = new Map([['a.png', 'v1']]); + const old: Map = new Map(); + expect(hasChanges(current, old)).toBe(true); + }); +}); diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/types.ts b/heft-plugins/heft-static-asset-typings-plugin/src/types.ts new file mode 100644 index 00000000000..c5c9256ce02 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/src/types.ts @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { ITerminal } from '@rushstack/terminal'; + +export interface IAssetsInlineConfigPluginOptionsBase< + TConfig extends IResourceStaticAssetTypingsConfigurationJson +> { + configType: 'inline'; + /** + * The inline configuration object. + */ + config: TConfig; +} + +export interface IAssetsFileConfigPluginOptions { + configType: 'file'; + /** + * The name of the riggable config file in the config/ folder. + */ + configFileName: string; +} + +export type IAssetPluginOptions = + | IAssetsInlineConfigPluginOptionsBase + | IAssetsFileConfigPluginOptions; + +export interface IResourceStaticAssetTypingsConfigurationJson { + fileExtensions: string[]; + generatedTsFolders?: string[]; + sourceFolderPath?: string; +} + +export interface ISourceStaticAssetTypingsConfigurationJson + extends IResourceStaticAssetTypingsConfigurationJson { + cjsOutputFolders: string[]; + esmOutputFolders?: string[]; +} + +export type StaticAssetConfigurationFileLoader = ( + terminal: ITerminal +) => Promise; diff --git a/heft-plugins/heft-static-asset-typings-plugin/tsconfig.json b/heft-plugins/heft-static-asset-typings-plugin/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/heft-plugins/heft-storybook-plugin/.eslintrc.js b/heft-plugins/heft-storybook-plugin/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/heft-plugins/heft-storybook-plugin/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/heft-plugins/heft-storybook-plugin/.npmignore b/heft-plugins/heft-storybook-plugin/.npmignore index 47e24bddcbb..f7a40e10213 100644 --- a/heft-plugins/heft-storybook-plugin/.npmignore +++ b/heft-plugins/heft-storybook-plugin/.npmignore @@ -8,25 +8,29 @@ !/lib/** !/lib-*/** !/dist/** -!ThirdPartyNotice.txt +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json !heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt # Ignore certain patterns that should not get published. /dist/*.stats.* /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) -!/includes/** +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-storybook-plugin/CHANGELOG.json b/heft-plugins/heft-storybook-plugin/CHANGELOG.json index 0b75b34e32f..f37fce58118 100644 --- a/heft-plugins/heft-storybook-plugin/CHANGELOG.json +++ b/heft-plugins/heft-storybook-plugin/CHANGELOG.json @@ -1,6 +1,4461 @@ { "name": "@rushstack/heft-storybook-plugin", "entries": [ + { + "version": "1.6.4", + "tag": "@rushstack/heft-storybook-plugin_v1.6.4", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "1.6.3", + "tag": "@rushstack/heft-storybook-plugin_v1.6.3", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "1.6.2", + "tag": "@rushstack/heft-storybook-plugin_v1.6.2", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "1.6.1", + "tag": "@rushstack/heft-storybook-plugin_v1.6.1", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "1.6.0", + "tag": "@rushstack/heft-storybook-plugin_v1.6.0", + "date": "Mon, 08 Jun 2026 15:15:49 GMT", + "comments": { + "minor": [ + { + "comment": "Add `--port` flag to pass a specific dev server port through to the Storybook CLI in serve mode" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "1.5.0", + "tag": "@rushstack/heft-storybook-plugin_v1.5.0", + "date": "Mon, 25 May 2026 15:14:31 GMT", + "comments": { + "minor": [ + { + "comment": "Add `quiet` option to control whether --quiet is passed to the Storybook CLI, and add `--no-open` flag to suppress automatic browser launch in serve mode" + } + ] + } + }, + { + "version": "1.4.11", + "tag": "@rushstack/heft-storybook-plugin_v1.4.11", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "1.4.10", + "tag": "@rushstack/heft-storybook-plugin_v1.4.10", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "1.4.9", + "tag": "@rushstack/heft-storybook-plugin_v1.4.9", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "1.4.8", + "tag": "@rushstack/heft-storybook-plugin_v1.4.8", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "1.4.7", + "tag": "@rushstack/heft-storybook-plugin_v1.4.7", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "1.4.6", + "tag": "@rushstack/heft-storybook-plugin_v1.4.6", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "1.4.5", + "tag": "@rushstack/heft-storybook-plugin_v1.4.5", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "1.4.4", + "tag": "@rushstack/heft-storybook-plugin_v1.4.4", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "1.4.3", + "tag": "@rushstack/heft-storybook-plugin_v1.4.3", + "date": "Thu, 02 Apr 2026 00:14:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.10`" + } + ] + } + }, + { + "version": "1.4.2", + "tag": "@rushstack/heft-storybook-plugin_v1.4.2", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "1.4.1", + "tag": "@rushstack/heft-storybook-plugin_v1.4.1", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "1.4.0", + "tag": "@rushstack/heft-storybook-plugin_v1.4.0", + "date": "Sat, 14 Mar 2026 00:13:47 GMT", + "comments": { + "minor": [ + { + "comment": "Add `disableTelemetry` option to set STORYBOOK_DISABLE_TELEMETRY=1 when invoking Storybook; always set COREPACK_ENABLE_AUTO_PIN=0 in the subprocess environment" + } + ] + } + }, + { + "version": "1.3.7", + "tag": "@rushstack/heft-storybook-plugin_v1.3.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "1.3.6", + "tag": "@rushstack/heft-storybook-plugin_v1.3.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "1.3.5", + "tag": "@rushstack/heft-storybook-plugin_v1.3.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "1.3.4", + "tag": "@rushstack/heft-storybook-plugin_v1.3.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "1.3.3", + "tag": "@rushstack/heft-storybook-plugin_v1.3.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "1.3.2", + "tag": "@rushstack/heft-storybook-plugin_v1.3.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "1.3.1", + "tag": "@rushstack/heft-storybook-plugin_v1.3.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "1.3.0", + "tag": "@rushstack/heft-storybook-plugin_v1.3.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "1.2.9", + "tag": "@rushstack/heft-storybook-plugin_v1.2.9", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "1.2.8", + "tag": "@rushstack/heft-storybook-plugin_v1.2.8", + "date": "Thu, 05 Feb 2026 01:54:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.2.8`" + } + ] + } + }, + { + "version": "1.2.7", + "tag": "@rushstack/heft-storybook-plugin_v1.2.7", + "date": "Thu, 05 Feb 2026 00:23:59 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.2.7`" + } + ] + } + }, + { + "version": "1.2.6", + "tag": "@rushstack/heft-storybook-plugin_v1.2.6", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "1.2.5", + "tag": "@rushstack/heft-storybook-plugin_v1.2.5", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "1.2.4", + "tag": "@rushstack/heft-storybook-plugin_v1.2.4", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "1.2.3", + "tag": "@rushstack/heft-storybook-plugin_v1.2.3", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "1.2.2", + "tag": "@rushstack/heft-storybook-plugin_v1.2.2", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "1.2.1", + "tag": "@rushstack/heft-storybook-plugin_v1.2.1", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-rspack-plugin\" to `0.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "1.2.0", + "tag": "@rushstack/heft-storybook-plugin_v1.2.0", + "date": "Mon, 29 Dec 2025 16:12:51 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for Storybook v9" + }, + { + "comment": "Add support for serve mode with RSPack" + } + ] + } + }, + { + "version": "1.1.8", + "tag": "@rushstack/heft-storybook-plugin_v1.1.8", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "1.1.7", + "tag": "@rushstack/heft-storybook-plugin_v1.1.7", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.5` to `1.1.6`" + } + ] + } + }, + { + "version": "1.1.6", + "tag": "@rushstack/heft-storybook-plugin_v1.1.6", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.4` to `1.1.5`" + } + ] + } + }, + { + "version": "1.1.5", + "tag": "@rushstack/heft-storybook-plugin_v1.1.5", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.3` to `1.1.4`" + } + ] + } + }, + { + "version": "1.1.4", + "tag": "@rushstack/heft-storybook-plugin_v1.1.4", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.2` to `1.1.3`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft-storybook-plugin_v1.1.3", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.1` to `1.1.2`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft-storybook-plugin_v1.1.2", + "date": "Fri, 17 Oct 2025 23:22:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.1`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft-storybook-plugin_v1.1.1", + "date": "Wed, 08 Oct 2025 00:13:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.0` to `1.1.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-storybook-plugin_v1.1.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.0.0` to `1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-storybook-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.75.0` to `1.0.0`" + } + ] + } + }, + { + "version": "0.9.22", + "tag": "@rushstack/heft-storybook-plugin_v0.9.22", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.113`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.43`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.5` to `0.75.0`" + } + ] + } + }, + { + "version": "0.9.21", + "tag": "@rushstack/heft-storybook-plugin_v0.9.21", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.112`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.42`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.4` to `0.74.5`" + } + ] + } + }, + { + "version": "0.9.20", + "tag": "@rushstack/heft-storybook-plugin_v0.9.20", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.111`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.41`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.3` to `0.74.4`" + } + ] + } + }, + { + "version": "0.9.19", + "tag": "@rushstack/heft-storybook-plugin_v0.9.19", + "date": "Fri, 29 Aug 2025 00:08:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.110`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.40`" + } + ] + } + }, + { + "version": "0.9.18", + "tag": "@rushstack/heft-storybook-plugin_v0.9.18", + "date": "Tue, 26 Aug 2025 00:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.109`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.39`" + } + ] + } + }, + { + "version": "0.9.17", + "tag": "@rushstack/heft-storybook-plugin_v0.9.17", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.108`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.2` to `0.74.3`" + } + ] + } + }, + { + "version": "0.9.16", + "tag": "@rushstack/heft-storybook-plugin_v0.9.16", + "date": "Fri, 01 Aug 2025 00:12:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.107`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.1` to `0.74.2`" + } + ] + } + }, + { + "version": "0.9.15", + "tag": "@rushstack/heft-storybook-plugin_v0.9.15", + "date": "Sat, 26 Jul 2025 00:12:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.106`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.36`" + } + ] + } + }, + { + "version": "0.9.14", + "tag": "@rushstack/heft-storybook-plugin_v0.9.14", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.105`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.0` to `0.74.1`" + } + ] + } + }, + { + "version": "0.9.13", + "tag": "@rushstack/heft-storybook-plugin_v0.9.13", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.104`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.6` to `0.74.0`" + } + ] + } + }, + { + "version": "0.9.12", + "tag": "@rushstack/heft-storybook-plugin_v0.9.12", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.103`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.5` to `0.73.6`" + } + ] + } + }, + { + "version": "0.9.11", + "tag": "@rushstack/heft-storybook-plugin_v0.9.11", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.102`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.4` to `0.73.5`" + } + ] + } + }, + { + "version": "0.9.10", + "tag": "@rushstack/heft-storybook-plugin_v0.9.10", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.101`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.3` to `0.73.4`" + } + ] + } + }, + { + "version": "0.9.9", + "tag": "@rushstack/heft-storybook-plugin_v0.9.9", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.100`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.2` to `0.73.3`" + } + ] + } + }, + { + "version": "0.9.8", + "tag": "@rushstack/heft-storybook-plugin_v0.9.8", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.99`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.1` to `0.73.2`" + } + ] + } + }, + { + "version": "0.9.7", + "tag": "@rushstack/heft-storybook-plugin_v0.9.7", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.98`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.0` to `0.73.1`" + } + ] + } + }, + { + "version": "0.9.6", + "tag": "@rushstack/heft-storybook-plugin_v0.9.6", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.97`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.72.0` to `0.73.0`" + } + ] + } + }, + { + "version": "0.9.5", + "tag": "@rushstack/heft-storybook-plugin_v0.9.5", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.96`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.2` to `0.72.0`" + } + ] + } + }, + { + "version": "0.9.4", + "tag": "@rushstack/heft-storybook-plugin_v0.9.4", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.95`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.1` to `0.71.2`" + } + ] + } + }, + { + "version": "0.9.3", + "tag": "@rushstack/heft-storybook-plugin_v0.9.3", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.94`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.0` to `0.71.1`" + } + ] + } + }, + { + "version": "0.9.2", + "tag": "@rushstack/heft-storybook-plugin_v0.9.2", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.93`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.1` to `0.71.0`" + } + ] + } + }, + { + "version": "0.9.1", + "tag": "@rushstack/heft-storybook-plugin_v0.9.1", + "date": "Wed, 12 Mar 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.92`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.0` to `0.70.1`" + } + ] + } + }, + { + "version": "0.9.0", + "tag": "@rushstack/heft-storybook-plugin_v0.9.0", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "minor": [ + { + "comment": "Use `useNodeJSResolver: true` in `Import.resolvePackage` calls." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.91`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.3` to `0.70.0`" + } + ] + } + }, + { + "version": "0.8.9", + "tag": "@rushstack/heft-storybook-plugin_v0.8.9", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.90`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.2` to `0.69.3`" + } + ] + } + }, + { + "version": "0.8.8", + "tag": "@rushstack/heft-storybook-plugin_v0.8.8", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.89`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.1` to `0.69.2`" + } + ] + } + }, + { + "version": "0.8.7", + "tag": "@rushstack/heft-storybook-plugin_v0.8.7", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.88`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.0` to `0.69.1`" + } + ] + } + }, + { + "version": "0.8.6", + "tag": "@rushstack/heft-storybook-plugin_v0.8.6", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.87`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.18` to `0.69.0`" + } + ] + } + }, + { + "version": "0.8.5", + "tag": "@rushstack/heft-storybook-plugin_v0.8.5", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.86`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.17` to `0.68.18`" + } + ] + } + }, + { + "version": "0.8.4", + "tag": "@rushstack/heft-storybook-plugin_v0.8.4", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.85`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.16` to `0.68.17`" + } + ] + } + }, + { + "version": "0.8.3", + "tag": "@rushstack/heft-storybook-plugin_v0.8.3", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.84`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.15` to `0.68.16`" + } + ] + } + }, + { + "version": "0.8.2", + "tag": "@rushstack/heft-storybook-plugin_v0.8.2", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.83`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.14` to `0.68.15`" + } + ] + } + }, + { + "version": "0.8.1", + "tag": "@rushstack/heft-storybook-plugin_v0.8.1", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.82`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.13` to `0.68.14`" + } + ] + } + }, + { + "version": "0.8.0", + "tag": "@rushstack/heft-storybook-plugin_v0.8.0", + "date": "Thu, 16 Jan 2025 22:49:19 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for the `--docs` parameter." + } + ] + } + }, + { + "version": "0.7.7", + "tag": "@rushstack/heft-storybook-plugin_v0.7.7", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.81`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.12` to `0.68.13`" + } + ] + } + }, + { + "version": "0.7.6", + "tag": "@rushstack/heft-storybook-plugin_v0.7.6", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.80`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.11` to `0.68.12`" + } + ] + } + }, + { + "version": "0.7.5", + "tag": "@rushstack/heft-storybook-plugin_v0.7.5", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.79`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.10` to `0.68.11`" + } + ] + } + }, + { + "version": "0.7.4", + "tag": "@rushstack/heft-storybook-plugin_v0.7.4", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.78`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.9` to `0.68.10`" + } + ] + } + }, + { + "version": "0.7.3", + "tag": "@rushstack/heft-storybook-plugin_v0.7.3", + "date": "Tue, 03 Dec 2024 16:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.77`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.8` to `0.68.9`" + } + ] + } + }, + { + "version": "0.7.2", + "tag": "@rushstack/heft-storybook-plugin_v0.7.2", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.76`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.7` to `0.68.8`" + } + ] + } + }, + { + "version": "0.7.1", + "tag": "@rushstack/heft-storybook-plugin_v0.7.1", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.75`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.6` to `0.68.7`" + } + ] + } + }, + { + "version": "0.7.0", + "tag": "@rushstack/heft-storybook-plugin_v0.7.0", + "date": "Fri, 25 Oct 2024 00:10:38 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for Storybook v8" + } + ] + } + }, + { + "version": "0.6.52", + "tag": "@rushstack/heft-storybook-plugin_v0.6.52", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.74`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.5` to `0.68.6`" + } + ] + } + }, + { + "version": "0.6.51", + "tag": "@rushstack/heft-storybook-plugin_v0.6.51", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.73`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.4` to `0.68.5`" + } + ] + } + }, + { + "version": "0.6.50", + "tag": "@rushstack/heft-storybook-plugin_v0.6.50", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.72`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.3` to `0.68.4`" + } + ] + } + }, + { + "version": "0.6.49", + "tag": "@rushstack/heft-storybook-plugin_v0.6.49", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.71`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.2` to `0.68.3`" + } + ] + } + }, + { + "version": "0.6.48", + "tag": "@rushstack/heft-storybook-plugin_v0.6.48", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.70`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.1` to `0.68.2`" + } + ] + } + }, + { + "version": "0.6.47", + "tag": "@rushstack/heft-storybook-plugin_v0.6.47", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.69`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.0` to `0.68.1`" + } + ] + } + }, + { + "version": "0.6.46", + "tag": "@rushstack/heft-storybook-plugin_v0.6.46", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.68`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.2` to `0.68.0`" + } + ] + } + }, + { + "version": "0.6.45", + "tag": "@rushstack/heft-storybook-plugin_v0.6.45", + "date": "Sat, 21 Sep 2024 00:10:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.67`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.12`" + } + ] + } + }, + { + "version": "0.6.44", + "tag": "@rushstack/heft-storybook-plugin_v0.6.44", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.66`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.1` to `0.67.2`" + } + ] + } + }, + { + "version": "0.6.43", + "tag": "@rushstack/heft-storybook-plugin_v0.6.43", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.65`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.0` to `0.67.1`" + } + ] + } + }, + { + "version": "0.6.42", + "tag": "@rushstack/heft-storybook-plugin_v0.6.42", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.64`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.26` to `0.67.0`" + } + ] + } + }, + { + "version": "0.6.41", + "tag": "@rushstack/heft-storybook-plugin_v0.6.41", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.63`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.25` to `0.66.26`" + } + ] + } + }, + { + "version": "0.6.40", + "tag": "@rushstack/heft-storybook-plugin_v0.6.40", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.62`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.24` to `0.66.25`" + } + ] + } + }, + { + "version": "0.6.39", + "tag": "@rushstack/heft-storybook-plugin_v0.6.39", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.61`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.23` to `0.66.24`" + } + ] + } + }, + { + "version": "0.6.38", + "tag": "@rushstack/heft-storybook-plugin_v0.6.38", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.60`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.22` to `0.66.23`" + } + ] + } + }, + { + "version": "0.6.37", + "tag": "@rushstack/heft-storybook-plugin_v0.6.37", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.59`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.21` to `0.66.22`" + } + ] + } + }, + { + "version": "0.6.36", + "tag": "@rushstack/heft-storybook-plugin_v0.6.36", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.58`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.20` to `0.66.21`" + } + ] + } + }, + { + "version": "0.6.35", + "tag": "@rushstack/heft-storybook-plugin_v0.6.35", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.57`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.19` to `0.66.20`" + } + ] + } + }, + { + "version": "0.6.34", + "tag": "@rushstack/heft-storybook-plugin_v0.6.34", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.56`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.18` to `0.66.19`" + } + ] + } + }, + { + "version": "0.6.33", + "tag": "@rushstack/heft-storybook-plugin_v0.6.33", + "date": "Fri, 07 Jun 2024 15:10:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.0`" + } + ] + } + }, + { + "version": "0.6.32", + "tag": "@rushstack/heft-storybook-plugin_v0.6.32", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.55`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.55`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.17` to `0.66.18`" + } + ] + } + }, + { + "version": "0.6.31", + "tag": "@rushstack/heft-storybook-plugin_v0.6.31", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.54`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.54`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.16` to `0.66.17`" + } + ] + } + }, + { + "version": "0.6.30", + "tag": "@rushstack/heft-storybook-plugin_v0.6.30", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.53`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.53`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.15` to `0.66.16`" + } + ] + } + }, + { + "version": "0.6.29", + "tag": "@rushstack/heft-storybook-plugin_v0.6.29", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.52`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.52`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.14` to `0.66.15`" + } + ] + } + }, + { + "version": "0.6.28", + "tag": "@rushstack/heft-storybook-plugin_v0.6.28", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.51`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.51`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.13` to `0.66.14`" + } + ] + } + }, + { + "version": "0.6.27", + "tag": "@rushstack/heft-storybook-plugin_v0.6.27", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.50`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.50`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.12` to `0.66.13`" + } + ] + } + }, + { + "version": "0.6.26", + "tag": "@rushstack/heft-storybook-plugin_v0.6.26", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.49`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.49`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.11` to `0.66.12`" + } + ] + } + }, + { + "version": "0.6.25", + "tag": "@rushstack/heft-storybook-plugin_v0.6.25", + "date": "Fri, 24 May 2024 00:15:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.48`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.48`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.10` to `0.66.11`" + } + ] + } + }, + { + "version": "0.6.24", + "tag": "@rushstack/heft-storybook-plugin_v0.6.24", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an edge case where the Storybook STDOUT might not be flushed completely when an error occurs" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.47`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.47`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.9` to `0.66.10`" + } + ] + } + }, + { + "version": "0.6.23", + "tag": "@rushstack/heft-storybook-plugin_v0.6.23", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.46`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.46`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.8` to `0.66.9`" + } + ] + } + }, + { + "version": "0.6.22", + "tag": "@rushstack/heft-storybook-plugin_v0.6.22", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.45`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.45`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.7` to `0.66.8`" + } + ] + } + }, + { + "version": "0.6.21", + "tag": "@rushstack/heft-storybook-plugin_v0.6.21", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.44`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.44`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.6` to `0.66.7`" + } + ] + } + }, + { + "version": "0.6.20", + "tag": "@rushstack/heft-storybook-plugin_v0.6.20", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.43`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.43`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.5` to `0.66.6`" + } + ] + } + }, + { + "version": "0.6.19", + "tag": "@rushstack/heft-storybook-plugin_v0.6.19", + "date": "Wed, 08 May 2024 22:23:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.42`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.42`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.4` to `0.66.5`" + } + ] + } + }, + { + "version": "0.6.18", + "tag": "@rushstack/heft-storybook-plugin_v0.6.18", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.41`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.41`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.3` to `0.66.4`" + } + ] + } + }, + { + "version": "0.6.17", + "tag": "@rushstack/heft-storybook-plugin_v0.6.17", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.40`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.40`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.2` to `0.66.3`" + } + ] + } + }, + { + "version": "0.6.16", + "tag": "@rushstack/heft-storybook-plugin_v0.6.16", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.39`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.39`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.1` to `0.66.2`" + } + ] + } + }, + { + "version": "0.6.15", + "tag": "@rushstack/heft-storybook-plugin_v0.6.15", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.0` to `0.66.1`" + } + ] + } + }, + { + "version": "0.6.14", + "tag": "@rushstack/heft-storybook-plugin_v0.6.14", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.10` to `0.66.0`" + } + ] + } + }, + { + "version": "0.6.13", + "tag": "@rushstack/heft-storybook-plugin_v0.6.13", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.9` to `0.65.10`" + } + ] + } + }, + { + "version": "0.6.12", + "tag": "@rushstack/heft-storybook-plugin_v0.6.12", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.8` to `0.65.9`" + } + ] + } + }, + { + "version": "0.6.11", + "tag": "@rushstack/heft-storybook-plugin_v0.6.11", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.7` to `0.65.8`" + } + ] + } + }, + { + "version": "0.6.10", + "tag": "@rushstack/heft-storybook-plugin_v0.6.10", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.6` to `0.65.7`" + } + ] + } + }, + { + "version": "0.6.9", + "tag": "@rushstack/heft-storybook-plugin_v0.6.9", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.5` to `0.65.6`" + } + ] + } + }, + { + "version": "0.6.8", + "tag": "@rushstack/heft-storybook-plugin_v0.6.8", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.4` to `^0.65.5`" + } + ] + } + }, + { + "version": "0.6.7", + "tag": "@rushstack/heft-storybook-plugin_v0.6.7", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.3` to `^0.65.4`" + } + ] + } + }, + { + "version": "0.6.6", + "tag": "@rushstack/heft-storybook-plugin_v0.6.6", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.2` to `^0.65.3`" + } + ] + } + }, + { + "version": "0.6.5", + "tag": "@rushstack/heft-storybook-plugin_v0.6.5", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.1` to `^0.65.2`" + } + ] + } + }, + { + "version": "0.6.4", + "tag": "@rushstack/heft-storybook-plugin_v0.6.4", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.0` to `^0.65.1`" + } + ] + } + }, + { + "version": "0.6.3", + "tag": "@rushstack/heft-storybook-plugin_v0.6.3", + "date": "Tue, 20 Feb 2024 16:10:53 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.8` to `^0.65.0`" + } + ] + } + }, + { + "version": "0.6.2", + "tag": "@rushstack/heft-storybook-plugin_v0.6.2", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.7` to `^0.64.8`" + } + ] + } + }, + { + "version": "0.6.1", + "tag": "@rushstack/heft-storybook-plugin_v0.6.1", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.6` to `^0.64.7`" + } + ] + } + }, + { + "version": "0.6.0", + "tag": "@rushstack/heft-storybook-plugin_v0.6.0", + "date": "Mon, 12 Feb 2024 16:09:54 GMT", + "comments": { + "minor": [ + { + "comment": "Fix an issue where Webpack would run twice during static storybook builds." + }, + { + "comment": "Introduce a `captureWebpackStats` configuration option that, when enabled, will pass the `--webpack-stats-json` parameter to Storybook." + } + ] + } + }, + { + "version": "0.5.3", + "tag": "@rushstack/heft-storybook-plugin_v0.5.3", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.5` to `^0.64.6`" + } + ] + } + }, + { + "version": "0.5.2", + "tag": "@rushstack/heft-storybook-plugin_v0.5.2", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.4` to `^0.64.5`" + } + ] + } + }, + { + "version": "0.5.1", + "tag": "@rushstack/heft-storybook-plugin_v0.5.1", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.3` to `^0.64.4`" + } + ] + } + }, + { + "version": "0.5.0", + "tag": "@rushstack/heft-storybook-plugin_v0.5.0", + "date": "Thu, 25 Jan 2024 01:09:29 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for storybook 7, HMR, and breaking chages in the plugin configuration option. The \"startupModulePath\" and \"staticBuildModulePath\" have been removed in favour of \"cliCallingConvention\" and \"cliPackageName\". A new 'cwdPackageName' option provides the ability to set an alternative dependency name as (cwd) target for the storybook commands." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.2` to `^0.64.3`" + } + ] + } + }, + { + "version": "0.4.19", + "tag": "@rushstack/heft-storybook-plugin_v0.4.19", + "date": "Tue, 23 Jan 2024 20:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.1` to `^0.64.2`" + } + ] + } + }, + { + "version": "0.4.18", + "tag": "@rushstack/heft-storybook-plugin_v0.4.18", + "date": "Tue, 23 Jan 2024 16:15:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.0` to `^0.64.1`" + } + ] + } + }, + { + "version": "0.4.17", + "tag": "@rushstack/heft-storybook-plugin_v0.4.17", + "date": "Tue, 16 Jan 2024 18:30:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.6` to `^0.64.0`" + } + ] + } + }, + { + "version": "0.4.16", + "tag": "@rushstack/heft-storybook-plugin_v0.4.16", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.5` to `^0.63.6`" + } + ] + } + }, + { + "version": "0.4.15", + "tag": "@rushstack/heft-storybook-plugin_v0.4.15", + "date": "Wed, 20 Dec 2023 01:09:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.4` to `^0.63.5`" + } + ] + } + }, + { + "version": "0.4.14", + "tag": "@rushstack/heft-storybook-plugin_v0.4.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.3` to `^0.63.4`" + } + ] + } + }, + { + "version": "0.4.13", + "tag": "@rushstack/heft-storybook-plugin_v0.4.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.2` to `^0.63.3`" + } + ] + } + }, + { + "version": "0.4.12", + "tag": "@rushstack/heft-storybook-plugin_v0.4.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.1` to `^0.63.2`" + } + ] + } + }, + { + "version": "0.4.11", + "tag": "@rushstack/heft-storybook-plugin_v0.4.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.0` to `^0.63.1`" + } + ] + } + }, + { + "version": "0.4.10", + "tag": "@rushstack/heft-storybook-plugin_v0.4.10", + "date": "Mon, 30 Oct 2023 23:36:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.3` to `^0.63.0`" + } + ] + } + }, + { + "version": "0.4.9", + "tag": "@rushstack/heft-storybook-plugin_v0.4.9", + "date": "Sun, 01 Oct 2023 02:56:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.2` to `^0.62.3`" + } + ] + } + }, + { + "version": "0.4.8", + "tag": "@rushstack/heft-storybook-plugin_v0.4.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.1` to `^0.62.2`" + } + ] + } + }, + { + "version": "0.4.7", + "tag": "@rushstack/heft-storybook-plugin_v0.4.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.0` to `^0.62.1`" + } + ] + } + }, + { + "version": "0.4.6", + "tag": "@rushstack/heft-storybook-plugin_v0.4.6", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.3` to `^0.62.0`" + } + ] + } + }, + { + "version": "0.4.5", + "tag": "@rushstack/heft-storybook-plugin_v0.4.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.2` to `^0.61.3`" + } + ] + } + }, + { + "version": "0.4.4", + "tag": "@rushstack/heft-storybook-plugin_v0.4.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.1` to `^0.61.2`" + } + ] + } + }, + { + "version": "0.4.3", + "tag": "@rushstack/heft-storybook-plugin_v0.4.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.0` to `^0.61.1`" + } + ] + } + }, + { + "version": "0.4.2", + "tag": "@rushstack/heft-storybook-plugin_v0.4.2", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.60.0` to `^0.61.0`" + } + ] + } + }, + { + "version": "0.4.1", + "tag": "@rushstack/heft-storybook-plugin_v0.4.1", + "date": "Tue, 19 Sep 2023 15:21:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.59.0` to `^0.60.0`" + } + ] + } + }, + { + "version": "0.4.0", + "tag": "@rushstack/heft-storybook-plugin_v0.4.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.2` to `^0.59.0`" + } + ] + } + }, + { + "version": "0.3.29", + "tag": "@rushstack/heft-storybook-plugin_v0.3.29", + "date": "Wed, 13 Sep 2023 00:32:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.9.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.15`" + } + ] + } + }, + { + "version": "0.3.28", + "tag": "@rushstack/heft-storybook-plugin_v0.3.28", + "date": "Thu, 07 Sep 2023 03:35:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.9.0`" + } + ] + } + }, + { + "version": "0.3.27", + "tag": "@rushstack/heft-storybook-plugin_v0.3.27", + "date": "Sat, 12 Aug 2023 00:21:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.8.0`" + } + ] + } + }, + { + "version": "0.3.26", + "tag": "@rushstack/heft-storybook-plugin_v0.3.26", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.1` to `^0.58.2`" + } + ] + } + }, + { + "version": "0.3.25", + "tag": "@rushstack/heft-storybook-plugin_v0.3.25", + "date": "Mon, 31 Jul 2023 15:19:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.13`" + } + ] + } + }, + { + "version": "0.3.24", + "tag": "@rushstack/heft-storybook-plugin_v0.3.24", + "date": "Sat, 29 Jul 2023 00:22:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.0` to `^0.58.1`" + } + ] + } + }, + { + "version": "0.3.23", + "tag": "@rushstack/heft-storybook-plugin_v0.3.23", + "date": "Thu, 20 Jul 2023 20:47:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.57.1` to `^0.58.0`" + } + ] + } + }, + { + "version": "0.3.22", + "tag": "@rushstack/heft-storybook-plugin_v0.3.22", + "date": "Wed, 19 Jul 2023 18:46:59 GMT", + "comments": { + "patch": [ + { + "comment": "Run Storybook in a forked Node process, providing various advantages including isolation of the process and encapsulation of all console logging" + } + ] + } + }, + { + "version": "0.3.21", + "tag": "@rushstack/heft-storybook-plugin_v0.3.21", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.57.0` to `^0.57.1`" + } + ] + } + }, + { + "version": "0.3.20", + "tag": "@rushstack/heft-storybook-plugin_v0.3.20", + "date": "Fri, 14 Jul 2023 15:20:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.9`" + } + ] + } + }, + { + "version": "0.3.19", + "tag": "@rushstack/heft-storybook-plugin_v0.3.19", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.3` to `^0.57.0`" + } + ] + } + }, + { + "version": "0.3.18", + "tag": "@rushstack/heft-storybook-plugin_v0.3.18", + "date": "Wed, 12 Jul 2023 15:20:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.2` to `^0.56.3`" + } + ] + } + }, + { + "version": "0.3.17", + "tag": "@rushstack/heft-storybook-plugin_v0.3.17", + "date": "Wed, 12 Jul 2023 00:23:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.6`" + } + ] + } + }, + { + "version": "0.3.16", + "tag": "@rushstack/heft-storybook-plugin_v0.3.16", + "date": "Fri, 07 Jul 2023 00:19:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.1` to `^0.56.2`" + } + ] + } + }, + { + "version": "0.3.15", + "tag": "@rushstack/heft-storybook-plugin_v0.3.15", + "date": "Thu, 06 Jul 2023 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.0` to `^0.56.1`" + } + ] + } + }, + { + "version": "0.3.14", + "tag": "@rushstack/heft-storybook-plugin_v0.3.14", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.3`" + } + ] + } + }, + { + "version": "0.3.13", + "tag": "@rushstack/heft-storybook-plugin_v0.3.13", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.2` to `^0.56.0`" + } + ] + } + }, + { + "version": "0.3.12", + "tag": "@rushstack/heft-storybook-plugin_v0.3.12", + "date": "Thu, 15 Jun 2023 00:21:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.1` to `^0.55.2`" + } + ] + } + }, + { + "version": "0.3.11", + "tag": "@rushstack/heft-storybook-plugin_v0.3.11", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.0` to `^0.55.1`" + } + ] + } + }, + { + "version": "0.3.10", + "tag": "@rushstack/heft-storybook-plugin_v0.3.10", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.54.0` to `^0.55.0`" + } + ] + } + }, + { + "version": "0.3.9", + "tag": "@rushstack/heft-storybook-plugin_v0.3.9", + "date": "Tue, 13 Jun 2023 01:49:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.53.1` to `^0.54.0`" + } + ] + } + }, + { + "version": "0.3.8", + "tag": "@rushstack/heft-storybook-plugin_v0.3.8", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.53.0` to `^0.53.1`" + } + ] + } + }, + { + "version": "0.3.7", + "tag": "@rushstack/heft-storybook-plugin_v0.3.7", + "date": "Fri, 09 Jun 2023 15:23:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.7`" + } + ] + } + }, + { + "version": "0.3.6", + "tag": "@rushstack/heft-storybook-plugin_v0.3.6", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.2` to `^0.53.0`" + } + ] + } + }, + { + "version": "0.3.5", + "tag": "@rushstack/heft-storybook-plugin_v0.3.5", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.1` to `^0.52.2`" + } + ] + } + }, + { + "version": "0.3.4", + "tag": "@rushstack/heft-storybook-plugin_v0.3.4", + "date": "Thu, 08 Jun 2023 00:20:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.0` to `^0.52.1`" + } + ] + } + }, + { + "version": "0.3.3", + "tag": "@rushstack/heft-storybook-plugin_v0.3.3", + "date": "Wed, 07 Jun 2023 22:45:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.51.0` to `^0.52.0`" + } + ] + } + }, + { + "version": "0.3.2", + "tag": "@rushstack/heft-storybook-plugin_v0.3.2", + "date": "Tue, 06 Jun 2023 02:52:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.2`" + } + ] + } + }, + { + "version": "0.3.1", + "tag": "@rushstack/heft-storybook-plugin_v0.3.1", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack4-plugin\" to `0.6.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.1`" + } + ] + } + }, { "version": "0.3.0", "tag": "@rushstack/heft-storybook-plugin_v0.3.0", diff --git a/heft-plugins/heft-storybook-plugin/CHANGELOG.md b/heft-plugins/heft-storybook-plugin/CHANGELOG.md index 7e02633e886..e03c111021c 100644 --- a/heft-plugins/heft-storybook-plugin/CHANGELOG.md +++ b/heft-plugins/heft-storybook-plugin/CHANGELOG.md @@ -1,6 +1,1011 @@ # Change Log - @rushstack/heft-storybook-plugin -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 1.6.4 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 1.6.3 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 1.6.2 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.6.1 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 1.6.0 +Mon, 08 Jun 2026 15:15:49 GMT + +### Minor changes + +- Add `--port` flag to pass a specific dev server port through to the Storybook CLI in serve mode + +## 1.5.0 +Mon, 25 May 2026 15:14:31 GMT + +### Minor changes + +- Add `quiet` option to control whether --quiet is passed to the Storybook CLI, and add `--no-open` flag to suppress automatic browser launch in serve mode + +## 1.4.11 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.4.10 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.4.9 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 1.4.8 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 1.4.7 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.4.6 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.4.5 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.4.4 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.4.3 +Thu, 02 Apr 2026 00:14:38 GMT + +_Version update only_ + +## 1.4.2 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.4.1 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.4.0 +Sat, 14 Mar 2026 00:13:47 GMT + +### Minor changes + +- Add `disableTelemetry` option to set STORYBOOK_DISABLE_TELEMETRY=1 when invoking Storybook; always set COREPACK_ENABLE_AUTO_PIN=0 in the subprocess environment + +## 1.3.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.3.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.3.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 1.3.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.3.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.3.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.3.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.3.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.2.9 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.2.8 +Thu, 05 Feb 2026 01:54:04 GMT + +_Version update only_ + +## 1.2.7 +Thu, 05 Feb 2026 00:23:59 GMT + +_Version update only_ + +## 1.2.6 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.2.5 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.2.4 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.2.3 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.2.2 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 1.2.1 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 1.2.0 +Mon, 29 Dec 2025 16:12:51 GMT + +### Minor changes + +- Add support for Storybook v9 +- Add support for serve mode with RSPack + +## 1.1.8 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.1.7 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.1.6 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.1.5 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 1.1.4 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.1.3 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.1.2 +Fri, 17 Oct 2025 23:22:33 GMT + +_Version update only_ + +## 1.1.1 +Wed, 08 Oct 2025 00:13:28 GMT + +_Version update only_ + +## 1.1.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.9.22 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.9.21 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.9.20 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.9.19 +Fri, 29 Aug 2025 00:08:01 GMT + +_Version update only_ + +## 0.9.18 +Tue, 26 Aug 2025 00:12:57 GMT + +_Version update only_ + +## 0.9.17 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.9.16 +Fri, 01 Aug 2025 00:12:48 GMT + +_Version update only_ + +## 0.9.15 +Sat, 26 Jul 2025 00:12:22 GMT + +_Version update only_ + +## 0.9.14 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.9.13 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.9.12 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.9.11 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.9.10 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.9.9 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.9.8 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.9.7 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.9.6 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 0.9.5 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 0.9.4 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.9.3 +Tue, 25 Mar 2025 15:11:15 GMT + +_Version update only_ + +## 0.9.2 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.9.1 +Wed, 12 Mar 2025 00:11:32 GMT + +_Version update only_ + +## 0.9.0 +Tue, 11 Mar 2025 02:12:33 GMT + +### Minor changes + +- Use `useNodeJSResolver: true` in `Import.resolvePackage` calls. + +## 0.8.9 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.8.8 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.8.7 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.8.6 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 0.8.5 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.8.4 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.8.3 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.8.2 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 0.8.1 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.8.0 +Thu, 16 Jan 2025 22:49:19 GMT + +### Minor changes + +- Add support for the `--docs` parameter. + +## 0.7.7 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.7.6 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.7.5 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.7.4 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.7.3 +Tue, 03 Dec 2024 16:11:07 GMT + +_Version update only_ + +## 0.7.2 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.7.1 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.7.0 +Fri, 25 Oct 2024 00:10:38 GMT + +### Minor changes + +- Add support for Storybook v8 + +## 0.6.52 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.6.51 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.6.50 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.6.49 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.6.48 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.6.47 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.6.46 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.6.45 +Sat, 21 Sep 2024 00:10:27 GMT + +_Version update only_ + +## 0.6.44 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.6.43 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.6.42 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.6.41 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.6.40 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.6.39 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.6.38 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 0.6.37 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 0.6.36 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.6.35 +Tue, 16 Jul 2024 00:36:21 GMT + +_Version update only_ + +## 0.6.34 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.6.33 +Fri, 07 Jun 2024 15:10:25 GMT + +_Version update only_ + +## 0.6.32 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.6.31 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.6.30 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 0.6.29 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.6.28 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.6.27 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.6.26 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.6.25 +Fri, 24 May 2024 00:15:08 GMT + +_Version update only_ + +## 0.6.24 +Thu, 23 May 2024 02:26:56 GMT + +### Patches + +- Fix an edge case where the Storybook STDOUT might not be flushed completely when an error occurs + +## 0.6.23 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.6.22 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.6.21 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.6.20 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.6.19 +Wed, 08 May 2024 22:23:50 GMT + +_Version update only_ + +## 0.6.18 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 0.6.17 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.6.16 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 0.6.15 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 0.6.14 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 0.6.13 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 0.6.12 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 0.6.11 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 0.6.10 +Thu, 29 Feb 2024 07:11:45 GMT + +_Version update only_ + +## 0.6.9 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.6.8 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.6.7 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.6.6 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.6.5 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.6.4 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.6.3 +Tue, 20 Feb 2024 16:10:53 GMT + +_Version update only_ + +## 0.6.2 +Mon, 19 Feb 2024 21:54:27 GMT + +_Version update only_ + +## 0.6.1 +Sat, 17 Feb 2024 06:24:35 GMT + +_Version update only_ + +## 0.6.0 +Mon, 12 Feb 2024 16:09:54 GMT + +### Minor changes + +- Fix an issue where Webpack would run twice during static storybook builds. +- Introduce a `captureWebpackStats` configuration option that, when enabled, will pass the `--webpack-stats-json` parameter to Storybook. + +## 0.5.3 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 0.5.2 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.5.1 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.5.0 +Thu, 25 Jan 2024 01:09:29 GMT + +### Minor changes + +- Add support for storybook 7, HMR, and breaking chages in the plugin configuration option. The "startupModulePath" and "staticBuildModulePath" have been removed in favour of "cliCallingConvention" and "cliPackageName". A new 'cwdPackageName' option provides the ability to set an alternative dependency name as (cwd) target for the storybook commands. + +## 0.4.19 +Tue, 23 Jan 2024 20:12:57 GMT + +_Version update only_ + +## 0.4.18 +Tue, 23 Jan 2024 16:15:06 GMT + +_Version update only_ + +## 0.4.17 +Tue, 16 Jan 2024 18:30:11 GMT + +_Version update only_ + +## 0.4.16 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.4.15 +Wed, 20 Dec 2023 01:09:45 GMT + +_Version update only_ + +## 0.4.14 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.4.13 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.4.12 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 0.4.11 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 0.4.10 +Mon, 30 Oct 2023 23:36:37 GMT + +_Version update only_ + +## 0.4.9 +Sun, 01 Oct 2023 02:56:29 GMT + +_Version update only_ + +## 0.4.8 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 0.4.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.4.6 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 0.4.5 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 0.4.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.4.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 0.4.2 +Fri, 22 Sep 2023 00:05:50 GMT + +_Version update only_ + +## 0.4.1 +Tue, 19 Sep 2023 15:21:52 GMT + +_Version update only_ + +## 0.4.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 0.3.29 +Wed, 13 Sep 2023 00:32:29 GMT + +_Version update only_ + +## 0.3.28 +Thu, 07 Sep 2023 03:35:43 GMT + +_Version update only_ + +## 0.3.27 +Sat, 12 Aug 2023 00:21:48 GMT + +_Version update only_ + +## 0.3.26 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 0.3.25 +Mon, 31 Jul 2023 15:19:05 GMT + +_Version update only_ + +## 0.3.24 +Sat, 29 Jul 2023 00:22:51 GMT + +_Version update only_ + +## 0.3.23 +Thu, 20 Jul 2023 20:47:28 GMT + +_Version update only_ + +## 0.3.22 +Wed, 19 Jul 2023 18:46:59 GMT + +### Patches + +- Run Storybook in a forked Node process, providing various advantages including isolation of the process and encapsulation of all console logging + +## 0.3.21 +Wed, 19 Jul 2023 00:20:31 GMT + +_Version update only_ + +## 0.3.20 +Fri, 14 Jul 2023 15:20:45 GMT + +_Version update only_ + +## 0.3.19 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 0.3.18 +Wed, 12 Jul 2023 15:20:39 GMT + +_Version update only_ + +## 0.3.17 +Wed, 12 Jul 2023 00:23:29 GMT + +_Version update only_ + +## 0.3.16 +Fri, 07 Jul 2023 00:19:32 GMT + +_Version update only_ + +## 0.3.15 +Thu, 06 Jul 2023 00:16:19 GMT + +_Version update only_ + +## 0.3.14 +Tue, 04 Jul 2023 00:18:47 GMT + +_Version update only_ + +## 0.3.13 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 0.3.12 +Thu, 15 Jun 2023 00:21:02 GMT + +_Version update only_ + +## 0.3.11 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 0.3.10 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 0.3.9 +Tue, 13 Jun 2023 01:49:02 GMT + +_Version update only_ + +## 0.3.8 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 0.3.7 +Fri, 09 Jun 2023 15:23:15 GMT + +_Version update only_ + +## 0.3.6 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 0.3.5 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 0.3.4 +Thu, 08 Jun 2023 00:20:02 GMT + +_Version update only_ + +## 0.3.3 +Wed, 07 Jun 2023 22:45:17 GMT + +_Version update only_ + +## 0.3.2 +Tue, 06 Jun 2023 02:52:51 GMT + +_Version update only_ + +## 0.3.1 +Mon, 05 Jun 2023 21:45:21 GMT + +_Version update only_ ## 0.3.0 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/heft-plugins/heft-storybook-plugin/config/heft.json b/heft-plugins/heft-storybook-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-storybook-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-storybook-plugin/config/jest.config.json b/heft-plugins/heft-storybook-plugin/config/jest.config.json index 4bb17bde3ee..d1749681d90 100644 --- a/heft-plugins/heft-storybook-plugin/config/jest.config.json +++ b/heft-plugins/heft-storybook-plugin/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "local-node-rig/profiles/default/config/jest.config.json" } diff --git a/heft-plugins/heft-storybook-plugin/config/rig.json b/heft-plugins/heft-storybook-plugin/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/heft-plugins/heft-storybook-plugin/config/rig.json +++ b/heft-plugins/heft-storybook-plugin/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/heft-plugins/heft-storybook-plugin/eslint.config.js b/heft-plugins/heft-storybook-plugin/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/heft-plugins/heft-storybook-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-storybook-plugin/heft-plugin.json b/heft-plugins/heft-storybook-plugin/heft-plugin.json index 1ca782549cd..8c0b0185d2f 100644 --- a/heft-plugins/heft-storybook-plugin/heft-plugin.json +++ b/heft-plugins/heft-storybook-plugin/heft-plugin.json @@ -1,11 +1,11 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-plugin.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", "taskPlugins": [ { "pluginName": "storybook-plugin", - "entryPoint": "./lib/StorybookPlugin", - "optionsSchema": "./lib/schemas/storybook.schema.json", + "entryPoint": "./lib-commonjs/StorybookPlugin", + "optionsSchema": "./lib-commonjs/schemas/storybook.schema.json", "parameterScope": "storybook", "parameters": [ @@ -13,6 +13,27 @@ "longName": "--storybook", "description": "(EXPERIMENTAL) Used by the \"@rushstack/heft-storybook-plugin\" package to launch Storybook.", "parameterKind": "flag" + }, + { + "longName": "--storybook-test", + "description": "Executes a stripped down build-storybook for testing purposes.", + "parameterKind": "flag" + }, + { + "longName": "--docs", + "description": "Execute storybook in docs mode.", + "parameterKind": "flag" + }, + { + "longName": "--no-open", + "description": "Pass --no-open to the storybook CLI so it does not automatically open a browser window in serve mode.", + "parameterKind": "flag" + }, + { + "longName": "--port", + "description": "Pass --port to the storybook CLI to bind the dev server to a specific port in serve mode.", + "parameterKind": "string", + "argumentName": "PORT" } ] } diff --git a/heft-plugins/heft-storybook-plugin/package.json b/heft-plugins/heft-storybook-plugin/package.json index b93b6f7708e..51195cec56c 100644 --- a/heft-plugins/heft-storybook-plugin/package.json +++ b/heft-plugins/heft-storybook-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/heft-storybook-plugin", - "version": "0.3.0", + "version": "1.6.4", "description": "Heft plugin for supporting UI development using Storybook", "repository": { "type": "git", @@ -16,17 +16,37 @@ "_phase:test": "heft run --only test -- --clean" }, "peerDependencies": { - "@rushstack/heft": "^0.51.0" + "@rushstack/heft": "^1.2.22" }, "dependencies": { - "@rushstack/node-core-library": "workspace:*" + "@rushstack/node-core-library": "workspace:*", + "@rushstack/terminal": "workspace:*" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", "@rushstack/heft-webpack4-plugin": "workspace:*", "@rushstack/heft-webpack5-plugin": "workspace:*", - "@types/node": "14.18.36" - } + "eslint": "~9.37.0", + "local-node-rig": "workspace:*", + "@rushstack/heft-rspack-plugin": "workspace:*" + }, + "exports": { + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "sideEffects": false } diff --git a/heft-plugins/heft-storybook-plugin/src/StorybookPlugin.ts b/heft-plugins/heft-storybook-plugin/src/StorybookPlugin.ts index 56720c630f4..56d6476992c 100644 --- a/heft-plugins/heft-storybook-plugin/src/StorybookPlugin.ts +++ b/heft-plugins/heft-storybook-plugin/src/StorybookPlugin.ts @@ -1,19 +1,29 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import * as child_process from 'node:child_process'; +import * as path from 'node:path'; + import { AlreadyExistsBehavior, FileSystem, Import, - IParsedPackageNameOrError, - PackageName + type IParsedPackageNameOrError, + PackageName, + SubprocessTerminator, + FileConstants, + type IPackageJson, + InternalError, + JsonFile } from '@rushstack/node-core-library'; +import { TerminalStreamWritable, type ITerminal, TerminalProviderSeverity } from '@rushstack/terminal'; import type { HeftConfiguration, IHeftTaskSession, IScopedLogger, IHeftTaskPlugin, CommandLineFlagParameter, + CommandLineStringParameter, IHeftTaskRunHookOptions } from '@rushstack/heft'; import type { @@ -24,10 +34,44 @@ import type { PluginName as Webpack5PluginName, IWebpackPluginAccessor as IWebpack5PluginAccessor } from '@rushstack/heft-webpack5-plugin'; +import type { IRspackPluginAccessor, PluginName as RspackPluginName } from '@rushstack/heft-rspack-plugin'; const PLUGIN_NAME: 'storybook-plugin' = 'storybook-plugin'; const WEBPACK4_PLUGIN_NAME: typeof Webpack4PluginName = 'webpack4-plugin'; const WEBPACK5_PLUGIN_NAME: typeof Webpack5PluginName = 'webpack5-plugin'; +const RSPACK_PLUGIN_NAME: typeof RspackPluginName = 'rspack-plugin'; + +/** + * Storybook CLI build type targets + */ +enum StorybookBuildMode { + /** + * Invoke storybook in watch mode + */ + WATCH = 'watch', + /** + * Invoke storybook in build mode + */ + BUILD = 'build' +} + +/** + * Storybook CLI versions + */ +enum StorybookCliVersion { + STORYBOOK6 = 'storybook6', + STORYBOOK7 = 'storybook7', + STORYBOOK8 = 'storybook8', + STORYBOOK9 = 'storybook9' +} + +/** + * Configuration object holding default storybook cli package and command + */ +interface IStorybookCliCallingConfig { + command: Record; + packageName: string; +} /** * Options for `StorybookPlugin`. @@ -60,26 +104,31 @@ export interface IStorybookPluginOptions { storykitPackageName: string; /** - * The module entry point that Heft serve mode should use to launch the Storybook toolchain. - * Typically it is the path loaded the `start-storybook` shell script. + * Specify how the Storybook CLI should be invoked. Possible values: * - * @example - * If you are using `@storybook/react`, then the startup path would be: + * - "storybook6": For a static build, Heft will expect the cliPackageName package + * to define a binary command named "build-storybook". For the dev server mode, + * Heft will expect to find a binary command named "start-storybook". These commands + * must be declared in the "bin" section of package.json since Heft invokes the script directly. + * The output folder will be specified using the "--output-dir" CLI parameter. * - * `"startupModulePath": "@storybook/react/bin/index.js"` + * - "storybook7": Heft looks for a single binary command named "sb". It will be invoked as + * "sb build" for static builds, or "sb dev" for dev server mode. + * The output folder will be specified using the "--output-dir" CLI parameter. + * + * @defaultValue `storybook7` */ - startupModulePath?: string; + cliCallingConvention?: `${StorybookCliVersion}`; /** - * The module entry point that Heft non-serve mode should use to launch the Storybook toolchain. - * Typically it is the path loaded the `build-storybook` shell script. - * - * @example - * If you are using `@storybook/react`, then the static build path would be: + * Specify the NPM package that provides the CLI binary to run. + * It will be resolved from the folder of your storykit package. * - * `"staticBuildModulePath": "@storybook/react/bin/build.js"` + * @defaultValue + * The default is `@storybook/cli` when `cliCallingConvention` is `storybook7` + * and `@storybook/react` when `cliCallingConvention` is `storybook6` */ - staticBuildModulePath?: string; + cliPackageName?: string; /** * The customized output dir for storybook static build. @@ -91,18 +140,101 @@ export interface IStorybookPluginOptions { * `"staticBuildOutputFolder": "newStaticBuildDir"` */ staticBuildOutputFolder?: string; + + /** + * Specifies an NPM dependency name that is used as the (cwd) target for the storybook commands + * By default the plugin executes the storybook commands in the local package context, + * but for distribution purposes it can be useful to split the TS library and storybook exports into two packages. + * + * @example + * If you create a storybook app project "my-ui-storybook-library-app" for the storybook preview distribution, + * and your main UI component library is my-ui-storybook-library. + * + * Your 'app' project is able to compile the 'library' storybook preview using the CWD target: + * + * `"cwdPackageName": "my-storybook-ui-library"` + */ + cwdPackageName?: string; + /** + * Specifies whether to capture the webpack stats for the storybook build by adding the `--webpack-stats-json` CLI flag. + */ + captureWebpackStats?: boolean; + + /** + * If true, sets the `STORYBOOK_DISABLE_TELEMETRY=1` environment variable when invoking the Storybook subprocess, + * which disables Storybook's telemetry data collection. + */ + disableTelemetry?: boolean; + + /** + * Specifies whether to run storybook in quiet mode (--quiet). + * + * @defaultValue `true` + */ + quiet?: boolean; } -interface IRunStorybookOptions { +interface IRunStorybookOptions extends IPrepareStorybookOptions { + logger: IScopedLogger; + isServeMode: boolean; + workingDirectory: string; resolvedModulePath: string; outputFolder: string | undefined; + moduleDefaultArgs: string[]; + verbose: boolean; } +interface IPrepareStorybookOptions extends IStorybookPluginOptions { + logger: IScopedLogger; + taskSession: IHeftTaskSession; + heftConfiguration: HeftConfiguration; + isServeMode: boolean; + isTestMode: boolean; + isDocsMode: boolean; + isNoOpenMode: boolean; + port: string | undefined; +} + +const DEFAULT_STORYBOOK_VERSION: StorybookCliVersion = StorybookCliVersion.STORYBOOK7; +const DEFAULT_STORYBOOK_CLI_CONFIG: Record = { + [StorybookCliVersion.STORYBOOK6]: { + packageName: '@storybook/react', + command: { + watch: ['start-storybook'], + build: ['build-storybook'] + } + }, + [StorybookCliVersion.STORYBOOK7]: { + packageName: '@storybook/cli', + command: { + watch: ['sb', 'dev'], + build: ['sb', 'build'] + } + }, + [StorybookCliVersion.STORYBOOK8]: { + packageName: 'storybook', + command: { + watch: ['sb', 'dev'], + build: ['sb', 'build'] + } + }, + [StorybookCliVersion.STORYBOOK9]: { + packageName: 'storybook', + command: { + watch: ['sb', 'dev'], + build: ['sb', 'build'] + } + } +}; + +const STORYBOOK_FLAG_NAME: '--storybook' = '--storybook'; +const STORYBOOK_TEST_FLAG_NAME: '--storybook-test' = '--storybook-test'; +const DOCS_FLAG_NAME: '--docs' = '--docs'; +const NO_OPEN_FLAG_NAME: '--no-open' = '--no-open'; +const PORT_FLAG_NAME: '--port' = '--port'; + /** @public */ export default class StorybookPlugin implements IHeftTaskPlugin { - private _logger!: IScopedLogger; - private _isServeMode: boolean = false; - /** * Generate typings for Sass files before TypeScript compilation. */ @@ -111,9 +243,16 @@ export default class StorybookPlugin implements IHeftTaskPlugin Promise = async () => { - // Discard Webpack's configuration to prevent Webpack from running - this._logger.terminal.writeLine( - 'The command line includes "--storybook", redirecting Webpack to Storybook' - ); - return false; - }; + const configurePackagerTap: (packager: 'Webpack' | 'Rspack') => () => Promise = + (packager: string) => async () => { + // Discard Webpack's configuration to prevent Webpack from running + logger.terminal.writeLine( + 'The command line includes "--storybook", redirecting ' + packager + ' to Storybook' + ); + return false; + }; + let isServeMode: boolean = false; taskSession.requestAccessToPluginByName( '@rushstack/heft-webpack4-plugin', WEBPACK4_PLUGIN_NAME, (accessor: IWebpack4PluginAccessor) => { - // Discard Webpack's configuration to prevent Webpack from running only when starting a storybook server - if (accessor.parameters.isServeMode) { - this._isServeMode = true; - accessor.hooks.onLoadConfiguration.tapPromise(PLUGIN_NAME, configureWebpackTap); - } + isServeMode = accessor.parameters.isServeMode; + + // Discard Webpack's configuration to prevent Webpack from running only when performing Storybook build + accessor.hooks.onLoadConfiguration.tapPromise(PLUGIN_NAME, configurePackagerTap('Webpack')); } ); @@ -157,46 +290,125 @@ export default class StorybookPlugin implements IHeftTaskPlugin { - // Discard Webpack's configuration to prevent Webpack from running only when starting a storybook server - if (accessor.parameters.isServeMode) { - this._isServeMode = true; - accessor.hooks.onLoadConfiguration.tapPromise(PLUGIN_NAME, configureWebpackTap); - } + isServeMode = accessor.parameters.isServeMode; + + // Discard Webpack's configuration to prevent Webpack from running only when performing Storybook build + accessor.hooks.onLoadConfiguration.tapPromise(PLUGIN_NAME, configurePackagerTap('Webpack')); + } + ); + + taskSession.requestAccessToPluginByName( + '@rushstack/heft-rspack-plugin', + RSPACK_PLUGIN_NAME, + (accessor: IRspackPluginAccessor) => { + isServeMode = accessor.parameters.isServeMode; + + // Discard Rspack's configuration to prevent Rspack from running only when performing Storybook build + accessor.hooks.onLoadConfiguration.tapPromise(PLUGIN_NAME, configurePackagerTap('Rspack')); } ); taskSession.hooks.run.tapPromise(PLUGIN_NAME, async (runOptions: IHeftTaskRunHookOptions) => { - const runStorybookOptions: IRunStorybookOptions = await this._prepareStorybookAsync( + const runStorybookOptions: IRunStorybookOptions = await this._prepareStorybookAsync({ + logger, taskSession, heftConfiguration, - options - ); - await this._runStorybookAsync(runStorybookOptions); + isServeMode, + isTestMode: storybookTestParameter.value, + isDocsMode: docsParameter.value, + isNoOpenMode: noOpenParameter.value, + port: portParameter.value, + ...options + }); + await this._runStorybookAsync(runStorybookOptions, options); }); } } - private async _prepareStorybookAsync( - taskSession: IHeftTaskSession, - heftConfiguration: HeftConfiguration, - options: IStorybookPluginOptions - ): Promise { - const { storykitPackageName, startupModulePath, staticBuildModulePath, staticBuildOutputFolder } = - options; - this._logger.terminal.writeVerboseLine(`Probing for "${storykitPackageName}"`); + private async _prepareStorybookAsync(options: IPrepareStorybookOptions): Promise { + const { + logger, + taskSession, + heftConfiguration, + storykitPackageName, + staticBuildOutputFolder, + isTestMode + } = options; + const storybookCliVersion: `${StorybookCliVersion}` = this._getStorybookVersion(options); + const storyBookCliConfig: IStorybookCliCallingConfig = DEFAULT_STORYBOOK_CLI_CONFIG[storybookCliVersion]; + const cliPackageName: string = options.cliPackageName ?? storyBookCliConfig.packageName; + const buildMode: StorybookBuildMode = taskSession.parameters.watch + ? StorybookBuildMode.WATCH + : StorybookBuildMode.BUILD; + + if (buildMode === StorybookBuildMode.WATCH && isTestMode) { + throw new Error(`The ${STORYBOOK_TEST_FLAG_NAME} flag is not supported in watch mode`); + } + if ( + isTestMode && + (storybookCliVersion === StorybookCliVersion.STORYBOOK6 || + storybookCliVersion === StorybookCliVersion.STORYBOOK7) + ) { + throw new Error( + `The ${STORYBOOK_TEST_FLAG_NAME} flag is only supported in Storybook version 8 and above.` + ); + } + logger.terminal.writeVerboseLine(`Probing for "${storykitPackageName}"`); // Example: "/path/to/my-project/node_modules/my-storykit" let storykitFolderPath: string; try { storykitFolderPath = Import.resolvePackage({ packageName: storykitPackageName, - baseFolderPath: heftConfiguration.buildFolderPath + baseFolderPath: heftConfiguration.buildFolderPath, + useNodeJSResolver: true + }); + } catch (ex) { + throw new Error(`The ${taskSession.taskName} task cannot start: ` + (ex as Error).message); + } + + logger.terminal.writeVerboseLine(`Found "${storykitPackageName}" in ` + storykitFolderPath); + + logger.terminal.writeVerboseLine(`Probing for "${cliPackageName}" in "${storykitPackageName}"`); + // Example: "/path/to/my-project/node_modules/my-storykit/node_modules/@storybook/cli" + let storyBookCliPackage: string; + try { + storyBookCliPackage = Import.resolvePackage({ + packageName: cliPackageName, + baseFolderPath: storykitFolderPath, + useNodeJSResolver: true }); } catch (ex) { throw new Error(`The ${taskSession.taskName} task cannot start: ` + (ex as Error).message); } - this._logger.terminal.writeVerboseLine(`Found "${storykitPackageName}" in ` + storykitFolderPath); + logger.terminal.writeVerboseLine(`Found "${cliPackageName}" in ` + storyBookCliPackage); + + const storyBookPackagePackageJsonFile: string = path.join(storyBookCliPackage, FileConstants.PackageJson); + const packageJson: IPackageJson = await JsonFile.loadAsync(storyBookPackagePackageJsonFile); + if (!packageJson.bin) { + throw new Error( + `The cli package "${cliPackageName}" does not provide a 'bin' executables in the 'package.json'` + ); + } + + const [moduleExecutableName, ...moduleDefaultArgs] = storyBookCliConfig.command[buildMode]; + const modulePath: string | undefined = (() => { + if (storybookCliVersion === StorybookCliVersion.STORYBOOK9 && typeof packageJson.bin === 'string') { + return packageJson.bin; + } + + if (typeof packageJson.bin !== 'string') { + return packageJson.bin[moduleExecutableName]; + } else { + throw new Error( + `The cli package "${cliPackageName}" provides a 'bin' executables in the 'package.json' but it is a string` + ); + } + })(); + logger.terminal.writeVerboseLine( + `Found storybook "${modulePath}" for "${buildMode}" mode in "${cliPackageName}"` + ); // Example: "/path/to/my-project/node_modules/my-storykit/node_modules" const storykitModuleFolderPath: string = `${storykitFolderPath}/node_modules`; @@ -210,25 +422,26 @@ export default class StorybookPlugin implements IHeftTaskPlugin { - const { resolvedModulePath, outputFolder } = runStorybookOptions; - this._logger.terminal.writeLine('Starting Storybook...'); - this._logger.terminal.writeLine(`Launching "${resolvedModulePath}"`); + private async _runStorybookAsync( + runStorybookOptions: IRunStorybookOptions, + options: IStorybookPluginOptions + ): Promise { + const { logger, resolvedModulePath, verbose, isServeMode, isTestMode, isDocsMode, isNoOpenMode, port } = + runStorybookOptions; + let { workingDirectory, outputFolder } = runStorybookOptions; + logger.terminal.writeLine('Running Storybook compilation'); + logger.terminal.writeVerboseLine(`Loading Storybook module "${resolvedModulePath}"`); + const storybookCliVersion: `${StorybookCliVersion}` = this._getStorybookVersion(options); + + /** + * Support \'cwdPackageName\' option + * by changing the working directory of the storybook command + */ + if (options.cwdPackageName) { + // Map outputFolder to local context. + if (outputFolder) { + outputFolder = path.resolve(workingDirectory, outputFolder); + } + + // Update workingDirectory to target context. + workingDirectory = await Import.resolvePackageAsync({ + packageName: options.cwdPackageName, + baseFolderPath: workingDirectory + }); + + logger.terminal.writeVerboseLine(`Changing Storybook working directory to "${workingDirectory}"`); + } + + const storybookArgs: string[] = runStorybookOptions.moduleDefaultArgs ?? []; - // Internally, the storybook module uses commander to parse the argv, which contains commands for Heft. - // We will clear out the argv, and only add back the arguments that are relevant to Storybook. - const originalArgv: string[] = process.argv; - process.argv = [process.argv[0], resolvedModulePath]; if (outputFolder) { - process.argv.push(`--output-dir=${outputFolder}`); + storybookArgs.push('--output-dir', outputFolder); + } + + if (options.captureWebpackStats) { + storybookArgs.push('--webpack-stats-json'); + } + + if (options.quiet !== false && !verbose) { + storybookArgs.push('--quiet'); + } + + if (isTestMode) { + storybookArgs.push('--test'); + } + + if (isDocsMode) { + storybookArgs.push('--docs'); + } + + if (isServeMode && isNoOpenMode) { + storybookArgs.push('--no-open'); + } + + if (isServeMode && port) { + storybookArgs.push('--port', port); + } + + const storybookEnv: NodeJS.ProcessEnv = { + ...process.env, + // Prevent corepack from prompting to pin a package manager version + COREPACK_ENABLE_AUTO_PIN: '0' + }; + if (options.disableTelemetry) { + storybookEnv.STORYBOOK_DISABLE_TELEMETRY = '1'; + } + + if (isServeMode) { + // Instantiate storybook runner synchronously for incremental builds + // this ensure that the process is not killed when heft watcher detects file changes + this._invokeSync( + logger, + resolvedModulePath, + storybookArgs, + storybookEnv, + storybookCliVersion === StorybookCliVersion.STORYBOOK8 + ); + } else { + await this._invokeAsSubprocessAsync( + logger, + resolvedModulePath, + storybookArgs, + workingDirectory, + storybookEnv + ); } + } + + /** + * Invoke storybook cli in a forked subprocess + * @param command - storybook command + * @param args - storybook args + * @param cwd - working directory + * @returns + */ + private async _invokeAsSubprocessAsync( + logger: IScopedLogger, + command: string, + args: string[], + cwd: string, + env: NodeJS.ProcessEnv + ): Promise { + return await new Promise((resolve, reject) => { + const forkedProcess: child_process.ChildProcess = child_process.fork(command, args, { + execArgv: process.execArgv, + cwd, + stdio: ['ignore', 'pipe', 'pipe', 'ipc'], + env, + ...SubprocessTerminator.RECOMMENDED_OPTIONS + }); + + SubprocessTerminator.killProcessTreeOnExit(forkedProcess, SubprocessTerminator.RECOMMENDED_OPTIONS); - require(resolvedModulePath); + const childPid: number | undefined = forkedProcess.pid; + if (childPid === undefined) { + throw new InternalError(`Failed to spawn child process`); + } + logger.terminal.writeVerboseLine(`Started storybook process #${childPid}`); - // Reset the argv to the original set of args + // Apply the pipe here instead of doing it in the forked process args due to a bug in Node + // We will output stderr to the normal stdout stream since all output is piped through + // stdout. We have to rely on the exit code to determine if there was an error. + const terminal: ITerminal = logger.terminal; + const terminalOutStream: TerminalStreamWritable = new TerminalStreamWritable({ + terminal, + severity: TerminalProviderSeverity.log + }); + forkedProcess.stdout!.pipe(terminalOutStream); + forkedProcess.stderr!.pipe(terminalOutStream); + + let processFinished: boolean = false; + forkedProcess.on('error', (error: Error) => { + processFinished = true; + reject(new Error(`Storybook returned error: ${error}`)); + }); + + forkedProcess.on('close', (exitCode: number | null, signal: NodeJS.Signals | null) => { + if (processFinished) { + return; + } + processFinished = true; + if (exitCode) { + reject(new Error(`Storybook exited with code ${exitCode}`)); + } else if (signal) { + reject(new Error(`Storybook terminated by signal ${signal}`)); + } else { + resolve(); + } + }); + }); + } + + /** + * Invoke storybook cli synchronously within the current process + * @param command - storybook command + * @param args - storybook args + * @param cwd - working directory + */ + private _invokeSync( + logger: IScopedLogger, + command: string, + args: string[], + env: NodeJS.ProcessEnv, + patchNpmConfigUserAgent: boolean + ): void { + logger.terminal.writeLine('Launching ' + command); + + // simulate storybook cli command + const originalArgv: string[] = process.argv; + const node: string = originalArgv[0]; + process.argv = [node, command, ...args]; + // npm_config_user_agent is used by Storybook to determine the package manager + // in a Rush monorepo it can't determine it automatically so it raises a benign error + // Storybook failed to check addon compatibility Error: Unable to find a usable package manager within NPM, PNPM, Yarn and Yarn 2 + // hardcode it to NPM to suppress the error + // + // This only happens for dev server mode, not for build mode, so does not need to be in _invokeAsSubprocessAsync + // + // Storing the original env and restoring it like happens with argv does not seem to work + // At the time when storybook checks env.npm_config_user_agent it has been reset to undefined + if (patchNpmConfigUserAgent) { + process.env.npm_config_user_agent = 'npm'; + } + + // Apply custom environment variables + for (const [key, value] of Object.entries(env)) { + if (value !== undefined) { + process.env[key] = value; + } + } + + // invoke command synchronously + require(command); + + // restore original heft process argv process.argv = originalArgv; - this._logger.terminal.writeVerboseLine('Completed synchronous portion of launching startupModulePath'); + logger.terminal.writeVerboseLine('Completed synchronous portion of launching startupModulePath'); + } + + private _getStorybookVersion(options: IStorybookPluginOptions): `${StorybookCliVersion}` { + return options.cliCallingConvention ?? DEFAULT_STORYBOOK_VERSION; } } diff --git a/heft-plugins/heft-storybook-plugin/src/schemas/storybook.schema.json b/heft-plugins/heft-storybook-plugin/src/schemas/storybook.schema.json index 581e7165a29..f05e2b39658 100644 --- a/heft-plugins/heft-storybook-plugin/src/schemas/storybook.schema.json +++ b/heft-plugins/heft-storybook-plugin/src/schemas/storybook.schema.json @@ -4,6 +4,7 @@ "description": "This schema describes the \"options\" field that can be specified in heft.json when loading \"@rushstack/heft-storybook-plugin\".", "type": "object", "additionalProperties": false, + "required": ["storykitPackageName"], "properties": { "storykitPackageName": { @@ -11,23 +12,41 @@ "description": "Storybook's conventional approach is for your app project to have direct dependencies on NPM packages such as `@storybook/react` and `@storybook/addon-essentials`. These packages have heavyweight dependencies such as Babel, Webpack, and the associated loaders and plugins needed to build the Storybook app (which is bundled completely independently from Heft). Naively adding these dependencies to your app's package.json muddies the waters of two radically different toolchains, and is likely to lead to dependency conflicts, for example if Heft installs Webpack 5 but `@storybook/react` installs Webpack 4. To solve this problem, `heft-storybook-plugin` introduces the concept of a separate \"storykit package\". All of your Storybook NPM packages are moved to be dependencies of the storykit. Storybook's browser API unfortunately isn't separated into dedicated NPM packages, but instead is exported by the Node.js toolchain packages such as `@storybook/react`. For an even cleaner separation the storykit package can simply reexport such APIs.", "type": "string" }, - "startupModulePath": { - "title": "The module entry point that Heft serve mode should use to launch the Storybook toolchain.", - "description": "Typically it is the path loaded the `start-storybook` shell script. For example, If you are using `@storybook/react`, then the startup path would be: `\"startupModulePath\": \"@storybook/react/bin/index.js\"`", - "type": "string", - "pattern": "[^\\\\]" + "cliCallingConvention": { + "title": "Specifies the calling convention of the storybook CLI based on the storybook version.", + "description": "Specify how the Storybook CLI should be invoked. Possible values: \"storybook6\" or \"storybook7\", defaults to \"storybook7\".", + "enum": ["storybook6", "storybook7", "storybook8", "storybook9"] }, - "staticBuildModulePath": { - "title": "The module entry point that Heft non-serve mode should use to launch the Storybook toolchain.", - "description": "Typically it is the path loaded the `build-storybook` shell script. For example, If you are using `@storybook/react`, then the static build path would be: `\"staticBuildModulePath\": \"@storybook/react/bin/build.js\"`", - "type": "string", - "pattern": "[^\\\\]" + "cliPackageName": { + "title": "The NPM package that Heft should use to launch the Storybook toolchain.", + "description": "Specify the NPM package that provides the CLI binary to run. Defaults to `@storybook/cli` for storybook 7 and `@storybook/react` for storybook 6.", + "type": "string" }, "staticBuildOutputFolder": { "title": "The customized output dir for storybook static build.", "description": "If this is empty, then it will use the storybook default output dir. If you want to change the static build output dir to staticBuildDir, then the static build output dir would be: `\"staticBuildOutputFolder\": \"staticBuildDir\"`", "type": "string", "pattern": "[^\\\\]" + }, + "cwdPackageName": { + "title": "Specifies an NPM dependency name that is used as the CWD target for the storybook commands", + "description": "By default the plugin executes the storybook commands in the local package context, but for distribution purposes it can be useful to split the TS library and storybook exports into two packages. For example, If you create a storybook 'app' project \"my-ui-storybook-library-app\" for the storybook preview distribution, and your main UI component `library` is my-ui-storybook-library. Your 'app' project is able to compile the 'library' storybook app using the CWD target: `\"cwdPackageName\": \"my-ui-storybook-library\"`", + "type": "string" + }, + "captureWebpackStats": { + "title": "Specifies whether to capture the webpack stats for storybook build.", + "description": "If this is true, then it will capture the webpack stats for storybook build. Defaults to false.", + "type": "boolean" + }, + "disableTelemetry": { + "title": "Specifies whether to disable Storybook telemetry.", + "description": "If true, sets the STORYBOOK_DISABLE_TELEMETRY=1 environment variable when invoking the Storybook subprocess, which disables Storybook's telemetry data collection. Defaults to false.", + "type": "boolean" + }, + "quiet": { + "title": "Specifies whether to run storybook in quiet mode (--quiet).", + "description": "If this is true, then it will run storybook in quiet mode. Defaults to true.", + "type": "boolean" } } } diff --git a/heft-plugins/heft-storybook-plugin/tsconfig.json b/heft-plugins/heft-storybook-plugin/tsconfig.json index 7512871fdbf..f028a59436c 100644 --- a/heft-plugins/heft-storybook-plugin/tsconfig.json +++ b/heft-plugins/heft-storybook-plugin/tsconfig.json @@ -1,7 +1,7 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "types": ["node"] + // There are issues with the webpack-dev-server and @rspack/core typings + "skipLibCheck": true } } diff --git a/heft-plugins/heft-typescript-plugin/.eslintrc.js b/heft-plugins/heft-typescript-plugin/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/heft-plugins/heft-typescript-plugin/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/heft-plugins/heft-typescript-plugin/.npmignore b/heft-plugins/heft-typescript-plugin/.npmignore index 869bde13daf..f7a40e10213 100644 --- a/heft-plugins/heft-typescript-plugin/.npmignore +++ b/heft-plugins/heft-typescript-plugin/.npmignore @@ -8,24 +8,29 @@ !/lib/** !/lib-*/** !/dist/** -!ThirdPartyNotice.txt +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json !heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt # Ignore certain patterns that should not get published. /dist/*.stats.* /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-typescript-plugin/CHANGELOG.json b/heft-plugins/heft-typescript-plugin/CHANGELOG.json index 23ebae331ba..452b465bfa8 100644 --- a/heft-plugins/heft-typescript-plugin/CHANGELOG.json +++ b/heft-plugins/heft-typescript-plugin/CHANGELOG.json @@ -1,6 +1,3312 @@ { "name": "@rushstack/heft-typescript-plugin", "entries": [ + { + "version": "1.3.17", + "tag": "@rushstack/heft-typescript-plugin_v1.3.17", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "1.3.16", + "tag": "@rushstack/heft-typescript-plugin_v1.3.16", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "1.3.15", + "tag": "@rushstack/heft-typescript-plugin_v1.3.15", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "1.3.14", + "tag": "@rushstack/heft-typescript-plugin_v1.3.14", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "1.3.13", + "tag": "@rushstack/heft-typescript-plugin_v1.3.13", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "1.3.12", + "tag": "@rushstack/heft-typescript-plugin_v1.3.12", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "1.3.11", + "tag": "@rushstack/heft-typescript-plugin_v1.3.11", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "1.3.10", + "tag": "@rushstack/heft-typescript-plugin_v1.3.10", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "1.3.9", + "tag": "@rushstack/heft-typescript-plugin_v1.3.9", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "patch": [ + { + "comment": "Bump semver." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "1.3.8", + "tag": "@rushstack/heft-typescript-plugin_v1.3.8", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "1.3.7", + "tag": "@rushstack/heft-typescript-plugin_v1.3.7", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "1.3.6", + "tag": "@rushstack/heft-typescript-plugin_v1.3.6", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "1.3.5", + "tag": "@rushstack/heft-typescript-plugin_v1.3.5", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "1.3.4", + "tag": "@rushstack/heft-typescript-plugin_v1.3.4", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "1.3.3", + "tag": "@rushstack/heft-typescript-plugin_v1.3.3", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "1.3.2", + "tag": "@rushstack/heft-typescript-plugin_v1.3.2", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "1.3.1", + "tag": "@rushstack/heft-typescript-plugin_v1.3.1", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "1.3.0", + "tag": "@rushstack/heft-typescript-plugin_v1.3.0", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "minor": [ + { + "comment": "Add `emitModulePackageJson` option for `additionalModuleKindsToEmit` entries. When enabled, a `package.json` with the appropriate `\"type\"` field is written to the output folder after compilation, ensuring Node.js correctly interprets `.js` files regardless of the nearest ancestor package.json `\"type\"` setting." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "1.2.4", + "tag": "@rushstack/heft-typescript-plugin_v1.2.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "1.2.3", + "tag": "@rushstack/heft-typescript-plugin_v1.2.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "1.2.2", + "tag": "@rushstack/heft-typescript-plugin_v1.2.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "1.2.1", + "tag": "@rushstack/heft-typescript-plugin_v1.2.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "1.2.0", + "tag": "@rushstack/heft-typescript-plugin_v1.2.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "1.1.14", + "tag": "@rushstack/heft-typescript-plugin_v1.1.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "1.1.13", + "tag": "@rushstack/heft-typescript-plugin_v1.1.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "1.1.12", + "tag": "@rushstack/heft-typescript-plugin_v1.1.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "1.1.11", + "tag": "@rushstack/heft-typescript-plugin_v1.1.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "1.1.10", + "tag": "@rushstack/heft-typescript-plugin_v1.1.10", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "1.1.9", + "tag": "@rushstack/heft-typescript-plugin_v1.1.9", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "1.1.8", + "tag": "@rushstack/heft-typescript-plugin_v1.1.8", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "patch": [ + { + "comment": "Fix TypeScript build cache hash computation to use relative paths with normalized separators for portability across machines and platforms" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "1.1.7", + "tag": "@rushstack/heft-typescript-plugin_v1.1.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "1.1.6", + "tag": "@rushstack/heft-typescript-plugin_v1.1.6", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.5` to `1.1.6`" + } + ] + } + }, + { + "version": "1.1.5", + "tag": "@rushstack/heft-typescript-plugin_v1.1.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "patch": [ + { + "comment": "Support \"${configDir}\" token in tsconfig when using file copier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.4` to `1.1.5`" + } + ] + } + }, + { + "version": "1.1.4", + "tag": "@rushstack/heft-typescript-plugin_v1.1.4", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.3` to `1.1.4`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft-typescript-plugin_v1.1.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.2` to `1.1.3`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft-typescript-plugin_v1.1.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.1` to `1.1.2`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft-typescript-plugin_v1.1.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.1.0` to `1.1.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-typescript-plugin_v1.1.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `1.0.0` to `1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-typescript-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.75.0` to `1.0.0`" + } + ] + } + }, + { + "version": "0.9.15", + "tag": "@rushstack/heft-typescript-plugin_v0.9.15", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.5` to `0.75.0`" + } + ] + } + }, + { + "version": "0.9.14", + "tag": "@rushstack/heft-typescript-plugin_v0.9.14", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.4` to `0.74.5`" + } + ] + } + }, + { + "version": "0.9.13", + "tag": "@rushstack/heft-typescript-plugin_v0.9.13", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.3` to `0.74.4`" + } + ] + } + }, + { + "version": "0.9.12", + "tag": "@rushstack/heft-typescript-plugin_v0.9.12", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.2` to `0.74.3`" + } + ] + } + }, + { + "version": "0.9.11", + "tag": "@rushstack/heft-typescript-plugin_v0.9.11", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.1` to `0.74.2`" + } + ] + } + }, + { + "version": "0.9.10", + "tag": "@rushstack/heft-typescript-plugin_v0.9.10", + "date": "Mon, 28 Jul 2025 15:11:56 GMT", + "comments": { + "patch": [ + { + "comment": "Update internal typings." + } + ] + } + }, + { + "version": "0.9.9", + "tag": "@rushstack/heft-typescript-plugin_v0.9.9", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.74.0` to `0.74.1`" + } + ] + } + }, + { + "version": "0.9.8", + "tag": "@rushstack/heft-typescript-plugin_v0.9.8", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.6` to `0.74.0`" + } + ] + } + }, + { + "version": "0.9.7", + "tag": "@rushstack/heft-typescript-plugin_v0.9.7", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.5` to `0.73.6`" + } + ] + } + }, + { + "version": "0.9.6", + "tag": "@rushstack/heft-typescript-plugin_v0.9.6", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.4` to `0.73.5`" + } + ] + } + }, + { + "version": "0.9.5", + "tag": "@rushstack/heft-typescript-plugin_v0.9.5", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.3` to `0.73.4`" + } + ] + } + }, + { + "version": "0.9.4", + "tag": "@rushstack/heft-typescript-plugin_v0.9.4", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.2` to `0.73.3`" + } + ] + } + }, + { + "version": "0.9.3", + "tag": "@rushstack/heft-typescript-plugin_v0.9.3", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.1` to `0.73.2`" + } + ] + } + }, + { + "version": "0.9.2", + "tag": "@rushstack/heft-typescript-plugin_v0.9.2", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "patch": [ + { + "comment": "Update documentation for `extends`" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.73.0` to `0.73.1`" + } + ] + } + }, + { + "version": "0.9.1", + "tag": "@rushstack/heft-typescript-plugin_v0.9.1", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.72.0` to `0.73.0`" + } + ] + } + }, + { + "version": "0.9.0", + "tag": "@rushstack/heft-typescript-plugin_v0.9.0", + "date": "Wed, 09 Apr 2025 00:11:02 GMT", + "comments": { + "minor": [ + { + "comment": "Leverage Heft's new `tryLoadProjectConfigurationFileAsync` method." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.71.2` to `0.72.0`" + } + ] + } + }, + { + "version": "0.8.2", + "tag": "@rushstack/heft-typescript-plugin_v0.8.2", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.71.1` to `0.71.2`" + } + ] + } + }, + { + "version": "0.8.1", + "tag": "@rushstack/heft-typescript-plugin_v0.8.1", + "date": "Tue, 25 Mar 2025 15:11:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.71.0` to `0.71.1`" + } + ] + } + }, + { + "version": "0.8.0", + "tag": "@rushstack/heft-typescript-plugin_v0.8.0", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "minor": [ + { + "comment": "Expose some internal APIs to be used by `@rushstack/heft-isolated-typescript-transpile-plugin`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.70.1` to `0.71.0`" + } + ] + } + }, + { + "version": "0.7.1", + "tag": "@rushstack/heft-typescript-plugin_v0.7.1", + "date": "Wed, 12 Mar 2025 00:11:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.70.0` to `0.70.1`" + } + ] + } + }, + { + "version": "0.7.0", + "tag": "@rushstack/heft-typescript-plugin_v0.7.0", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for TypeScript 5.8." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.69.3` to `0.70.0`" + } + ] + } + }, + { + "version": "0.6.16", + "tag": "@rushstack/heft-typescript-plugin_v0.6.16", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.69.2` to `0.69.3`" + } + ] + } + }, + { + "version": "0.6.15", + "tag": "@rushstack/heft-typescript-plugin_v0.6.15", + "date": "Sat, 01 Mar 2025 07:23:16 GMT", + "comments": { + "patch": [ + { + "comment": "Add support for TypeScript 5.7." + } + ] + } + }, + { + "version": "0.6.14", + "tag": "@rushstack/heft-typescript-plugin_v0.6.14", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.69.1` to `0.69.2`" + } + ] + } + }, + { + "version": "0.6.13", + "tag": "@rushstack/heft-typescript-plugin_v0.6.13", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.69.0` to `0.69.1`" + } + ] + } + }, + { + "version": "0.6.12", + "tag": "@rushstack/heft-typescript-plugin_v0.6.12", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.18` to `0.69.0`" + } + ] + } + }, + { + "version": "0.6.11", + "tag": "@rushstack/heft-typescript-plugin_v0.6.11", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.17` to `0.68.18`" + } + ] + } + }, + { + "version": "0.6.10", + "tag": "@rushstack/heft-typescript-plugin_v0.6.10", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.16` to `0.68.17`" + } + ] + } + }, + { + "version": "0.6.9", + "tag": "@rushstack/heft-typescript-plugin_v0.6.9", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.15` to `0.68.16`" + } + ] + } + }, + { + "version": "0.6.8", + "tag": "@rushstack/heft-typescript-plugin_v0.6.8", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.14` to `0.68.15`" + } + ] + } + }, + { + "version": "0.6.7", + "tag": "@rushstack/heft-typescript-plugin_v0.6.7", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.13` to `0.68.14`" + } + ] + } + }, + { + "version": "0.6.6", + "tag": "@rushstack/heft-typescript-plugin_v0.6.6", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.12` to `0.68.13`" + } + ] + } + }, + { + "version": "0.6.5", + "tag": "@rushstack/heft-typescript-plugin_v0.6.5", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.11` to `0.68.12`" + } + ] + } + }, + { + "version": "0.6.4", + "tag": "@rushstack/heft-typescript-plugin_v0.6.4", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.10` to `0.68.11`" + } + ] + } + }, + { + "version": "0.6.3", + "tag": "@rushstack/heft-typescript-plugin_v0.6.3", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.9` to `0.68.10`" + } + ] + } + }, + { + "version": "0.6.2", + "tag": "@rushstack/heft-typescript-plugin_v0.6.2", + "date": "Tue, 03 Dec 2024 16:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.8` to `0.68.9`" + } + ] + } + }, + { + "version": "0.6.1", + "tag": "@rushstack/heft-typescript-plugin_v0.6.1", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.7` to `0.68.8`" + } + ] + } + }, + { + "version": "0.6.0", + "tag": "@rushstack/heft-typescript-plugin_v0.6.0", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "minor": [ + { + "comment": "Add \"onlyResolveSymlinksInNodeModules\" option to improve performance for typical repository layouts." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.6` to `0.68.7`" + } + ] + } + }, + { + "version": "0.5.35", + "tag": "@rushstack/heft-typescript-plugin_v0.5.35", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.5` to `0.68.6`" + } + ] + } + }, + { + "version": "0.5.34", + "tag": "@rushstack/heft-typescript-plugin_v0.5.34", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.4` to `0.68.5`" + } + ] + } + }, + { + "version": "0.5.33", + "tag": "@rushstack/heft-typescript-plugin_v0.5.33", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.3` to `0.68.4`" + } + ] + } + }, + { + "version": "0.5.32", + "tag": "@rushstack/heft-typescript-plugin_v0.5.32", + "date": "Wed, 16 Oct 2024 00:11:20 GMT", + "comments": { + "patch": [ + { + "comment": "Support typescript v5.6" + } + ] + } + }, + { + "version": "0.5.31", + "tag": "@rushstack/heft-typescript-plugin_v0.5.31", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.2` to `0.68.3`" + } + ] + } + }, + { + "version": "0.5.30", + "tag": "@rushstack/heft-typescript-plugin_v0.5.30", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.1` to `0.68.2`" + } + ] + } + }, + { + "version": "0.5.29", + "tag": "@rushstack/heft-typescript-plugin_v0.5.29", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.68.0` to `0.68.1`" + } + ] + } + }, + { + "version": "0.5.28", + "tag": "@rushstack/heft-typescript-plugin_v0.5.28", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.67.2` to `0.68.0`" + } + ] + } + }, + { + "version": "0.5.27", + "tag": "@rushstack/heft-typescript-plugin_v0.5.27", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.67.1` to `0.67.2`" + } + ] + } + }, + { + "version": "0.5.26", + "tag": "@rushstack/heft-typescript-plugin_v0.5.26", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.67.0` to `0.67.1`" + } + ] + } + }, + { + "version": "0.5.25", + "tag": "@rushstack/heft-typescript-plugin_v0.5.25", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.26` to `0.67.0`" + } + ] + } + }, + { + "version": "0.5.24", + "tag": "@rushstack/heft-typescript-plugin_v0.5.24", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.25` to `0.66.26`" + } + ] + } + }, + { + "version": "0.5.23", + "tag": "@rushstack/heft-typescript-plugin_v0.5.23", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.24` to `0.66.25`" + } + ] + } + }, + { + "version": "0.5.22", + "tag": "@rushstack/heft-typescript-plugin_v0.5.22", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.23` to `0.66.24`" + } + ] + } + }, + { + "version": "0.5.21", + "tag": "@rushstack/heft-typescript-plugin_v0.5.21", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.22` to `0.66.23`" + } + ] + } + }, + { + "version": "0.5.20", + "tag": "@rushstack/heft-typescript-plugin_v0.5.20", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.21` to `0.66.22`" + } + ] + } + }, + { + "version": "0.5.19", + "tag": "@rushstack/heft-typescript-plugin_v0.5.19", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.20` to `0.66.21`" + } + ] + } + }, + { + "version": "0.5.18", + "tag": "@rushstack/heft-typescript-plugin_v0.5.18", + "date": "Tue, 16 Jul 2024 00:36:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.19` to `0.66.20`" + } + ] + } + }, + { + "version": "0.5.17", + "tag": "@rushstack/heft-typescript-plugin_v0.5.17", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.18` to `0.66.19`" + } + ] + } + }, + { + "version": "0.5.16", + "tag": "@rushstack/heft-typescript-plugin_v0.5.16", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.17` to `0.66.18`" + } + ] + } + }, + { + "version": "0.5.15", + "tag": "@rushstack/heft-typescript-plugin_v0.5.15", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.16` to `0.66.17`" + } + ] + } + }, + { + "version": "0.5.14", + "tag": "@rushstack/heft-typescript-plugin_v0.5.14", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.15` to `0.66.16`" + } + ] + } + }, + { + "version": "0.5.13", + "tag": "@rushstack/heft-typescript-plugin_v0.5.13", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.14` to `0.66.15`" + } + ] + } + }, + { + "version": "0.5.12", + "tag": "@rushstack/heft-typescript-plugin_v0.5.12", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.13` to `0.66.14`" + } + ] + } + }, + { + "version": "0.5.11", + "tag": "@rushstack/heft-typescript-plugin_v0.5.11", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.12` to `0.66.13`" + } + ] + } + }, + { + "version": "0.5.10", + "tag": "@rushstack/heft-typescript-plugin_v0.5.10", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.11` to `0.66.12`" + } + ] + } + }, + { + "version": "0.5.9", + "tag": "@rushstack/heft-typescript-plugin_v0.5.9", + "date": "Fri, 24 May 2024 00:15:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.10` to `0.66.11`" + } + ] + } + }, + { + "version": "0.5.8", + "tag": "@rushstack/heft-typescript-plugin_v0.5.8", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.9` to `0.66.10`" + } + ] + } + }, + { + "version": "0.5.7", + "tag": "@rushstack/heft-typescript-plugin_v0.5.7", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.8` to `0.66.9`" + } + ] + } + }, + { + "version": "0.5.6", + "tag": "@rushstack/heft-typescript-plugin_v0.5.6", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.7` to `0.66.8`" + } + ] + } + }, + { + "version": "0.5.5", + "tag": "@rushstack/heft-typescript-plugin_v0.5.5", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.6` to `0.66.7`" + } + ] + } + }, + { + "version": "0.5.4", + "tag": "@rushstack/heft-typescript-plugin_v0.5.4", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.5` to `0.66.6`" + } + ] + } + }, + { + "version": "0.5.3", + "tag": "@rushstack/heft-typescript-plugin_v0.5.3", + "date": "Wed, 08 May 2024 22:23:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.4` to `0.66.5`" + } + ] + } + }, + { + "version": "0.5.2", + "tag": "@rushstack/heft-typescript-plugin_v0.5.2", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.3` to `0.66.4`" + } + ] + } + }, + { + "version": "0.5.1", + "tag": "@rushstack/heft-typescript-plugin_v0.5.1", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.2` to `0.66.3`" + } + ] + } + }, + { + "version": "0.5.0", + "tag": "@rushstack/heft-typescript-plugin_v0.5.0", + "date": "Thu, 28 Mar 2024 22:42:23 GMT", + "comments": { + "minor": [ + { + "comment": "Gracefully exit transpile worker instead of using `process.exit(0)`." + } + ] + } + }, + { + "version": "0.4.0", + "tag": "@rushstack/heft-typescript-plugin_v0.4.0", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "minor": [ + { + "comment": "Bump latest supported version of TypeScript to 5.4" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.1` to `0.66.2`" + } + ] + } + }, + { + "version": "0.3.21", + "tag": "@rushstack/heft-typescript-plugin_v0.3.21", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.66.0` to `0.66.1`" + } + ] + } + }, + { + "version": "0.3.20", + "tag": "@rushstack/heft-typescript-plugin_v0.3.20", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.10` to `0.66.0`" + } + ] + } + }, + { + "version": "0.3.19", + "tag": "@rushstack/heft-typescript-plugin_v0.3.19", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.9` to `0.65.10`" + } + ] + } + }, + { + "version": "0.3.18", + "tag": "@rushstack/heft-typescript-plugin_v0.3.18", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.8` to `0.65.9`" + } + ] + } + }, + { + "version": "0.3.17", + "tag": "@rushstack/heft-typescript-plugin_v0.3.17", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.7` to `0.65.8`" + } + ] + } + }, + { + "version": "0.3.16", + "tag": "@rushstack/heft-typescript-plugin_v0.3.16", + "date": "Thu, 29 Feb 2024 07:11:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.6` to `0.65.7`" + } + ] + } + }, + { + "version": "0.3.15", + "tag": "@rushstack/heft-typescript-plugin_v0.3.15", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.5` to `0.65.6`" + } + ] + } + }, + { + "version": "0.3.14", + "tag": "@rushstack/heft-typescript-plugin_v0.3.14", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.4` to `0.65.5`" + } + ] + } + }, + { + "version": "0.3.13", + "tag": "@rushstack/heft-typescript-plugin_v0.3.13", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.3` to `0.65.4`" + } + ] + } + }, + { + "version": "0.3.12", + "tag": "@rushstack/heft-typescript-plugin_v0.3.12", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.2` to `0.65.3`" + } + ] + } + }, + { + "version": "0.3.11", + "tag": "@rushstack/heft-typescript-plugin_v0.3.11", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.1` to `0.65.2`" + } + ] + } + }, + { + "version": "0.3.10", + "tag": "@rushstack/heft-typescript-plugin_v0.3.10", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.65.0` to `0.65.1`" + } + ] + } + }, + { + "version": "0.3.9", + "tag": "@rushstack/heft-typescript-plugin_v0.3.9", + "date": "Tue, 20 Feb 2024 16:10:53 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.8` to `0.65.0`" + } + ] + } + }, + { + "version": "0.3.8", + "tag": "@rushstack/heft-typescript-plugin_v0.3.8", + "date": "Mon, 19 Feb 2024 21:54:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.7` to `0.64.8`" + } + ] + } + }, + { + "version": "0.3.7", + "tag": "@rushstack/heft-typescript-plugin_v0.3.7", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.6` to `0.64.7`" + } + ] + } + }, + { + "version": "0.3.6", + "tag": "@rushstack/heft-typescript-plugin_v0.3.6", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.5` to `0.64.6`" + } + ] + } + }, + { + "version": "0.3.5", + "tag": "@rushstack/heft-typescript-plugin_v0.3.5", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.4` to `0.64.5`" + } + ] + } + }, + { + "version": "0.3.4", + "tag": "@rushstack/heft-typescript-plugin_v0.3.4", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.3` to `0.64.4`" + } + ] + } + }, + { + "version": "0.3.3", + "tag": "@rushstack/heft-typescript-plugin_v0.3.3", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.2` to `0.64.3`" + } + ] + } + }, + { + "version": "0.3.2", + "tag": "@rushstack/heft-typescript-plugin_v0.3.2", + "date": "Tue, 23 Jan 2024 20:12:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.1` to `0.64.2`" + } + ] + } + }, + { + "version": "0.3.1", + "tag": "@rushstack/heft-typescript-plugin_v0.3.1", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.64.0` to `0.64.1`" + } + ] + } + }, + { + "version": "0.3.0", + "tag": "@rushstack/heft-typescript-plugin_v0.3.0", + "date": "Tue, 16 Jan 2024 18:30:10 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for TypeScript 5.3" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.6` to `0.64.0`" + } + ] + } + }, + { + "version": "0.2.16", + "tag": "@rushstack/heft-typescript-plugin_v0.2.16", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "patch": [ + { + "comment": "Fix break in watch mode during updateShapeSignature call." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.5` to `0.63.6`" + } + ] + } + }, + { + "version": "0.2.15", + "tag": "@rushstack/heft-typescript-plugin_v0.2.15", + "date": "Wed, 20 Dec 2023 01:09:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.4` to `0.63.5`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/heft-typescript-plugin_v0.2.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.3` to `0.63.4`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/heft-typescript-plugin_v0.2.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.2` to `0.63.3`" + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/heft-typescript-plugin_v0.2.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.1` to `0.63.2`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/heft-typescript-plugin_v0.2.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.63.0` to `0.63.1`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/heft-typescript-plugin_v0.2.10", + "date": "Mon, 30 Oct 2023 23:36:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.62.3` to `0.63.0`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/heft-typescript-plugin_v0.2.9", + "date": "Sun, 01 Oct 2023 02:56:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.62.2` to `0.62.3`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/heft-typescript-plugin_v0.2.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.62.1` to `0.62.2`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/heft-typescript-plugin_v0.2.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.62.0` to `0.62.1`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/heft-typescript-plugin_v0.2.6", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.61.3` to `0.62.0`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/heft-typescript-plugin_v0.2.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.61.2` to `0.61.3`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/heft-typescript-plugin_v0.2.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.61.1` to `0.61.2`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/heft-typescript-plugin_v0.2.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.61.0` to `0.61.1`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/heft-typescript-plugin_v0.2.2", + "date": "Fri, 22 Sep 2023 00:05:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.60.0` to `0.61.0`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/heft-typescript-plugin_v0.2.1", + "date": "Tue, 19 Sep 2023 15:21:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.59.0` to `0.60.0`" + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/heft-typescript-plugin_v0.2.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "patch": [ + { + "comment": "Fix bugs related to tracking of the current working directory if the value changes." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.58.2` to `0.59.0`" + } + ] + } + }, + { + "version": "0.1.21", + "tag": "@rushstack/heft-typescript-plugin_v0.1.21", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.58.1` to `0.58.2`" + } + ] + } + }, + { + "version": "0.1.20", + "tag": "@rushstack/heft-typescript-plugin_v0.1.20", + "date": "Sat, 29 Jul 2023 00:22:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.58.0` to `0.58.1`" + } + ] + } + }, + { + "version": "0.1.19", + "tag": "@rushstack/heft-typescript-plugin_v0.1.19", + "date": "Thu, 20 Jul 2023 20:47:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.57.1` to `0.58.0`" + } + ] + } + }, + { + "version": "0.1.18", + "tag": "@rushstack/heft-typescript-plugin_v0.1.18", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "patch": [ + { + "comment": "Updated semver dependency" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.57.0` to `0.57.1`" + } + ] + } + }, + { + "version": "0.1.17", + "tag": "@rushstack/heft-typescript-plugin_v0.1.17", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.56.3` to `0.57.0`" + } + ] + } + }, + { + "version": "0.1.16", + "tag": "@rushstack/heft-typescript-plugin_v0.1.16", + "date": "Wed, 12 Jul 2023 15:20:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.56.2` to `0.56.3`" + } + ] + } + }, + { + "version": "0.1.15", + "tag": "@rushstack/heft-typescript-plugin_v0.1.15", + "date": "Wed, 12 Jul 2023 00:23:29 GMT", + "comments": { + "patch": [ + { + "comment": "Only make warnings terminal if \"buildProjectReferences\" is true." + } + ] + } + }, + { + "version": "0.1.14", + "tag": "@rushstack/heft-typescript-plugin_v0.1.14", + "date": "Fri, 07 Jul 2023 00:19:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.56.1` to `0.56.2`" + } + ] + } + }, + { + "version": "0.1.13", + "tag": "@rushstack/heft-typescript-plugin_v0.1.13", + "date": "Thu, 06 Jul 2023 00:16:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.56.0` to `0.56.1`" + } + ] + } + }, + { + "version": "0.1.12", + "tag": "@rushstack/heft-typescript-plugin_v0.1.12", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "patch": [ + { + "comment": "Fix evaluation of the \"project\" configuration option." + } + ] + } + }, + { + "version": "0.1.11", + "tag": "@rushstack/heft-typescript-plugin_v0.1.11", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.55.2` to `0.56.0`" + } + ] + } + }, + { + "version": "0.1.10", + "tag": "@rushstack/heft-typescript-plugin_v0.1.10", + "date": "Thu, 15 Jun 2023 00:21:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.12.5`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.55.1` to `0.55.2`" + } + ] + } + }, + { + "version": "0.1.9", + "tag": "@rushstack/heft-typescript-plugin_v0.1.9", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.55.0` to `0.55.1`" + } + ] + } + }, + { + "version": "0.1.8", + "tag": "@rushstack/heft-typescript-plugin_v0.1.8", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.54.0` to `0.55.0`" + } + ] + } + }, + { + "version": "0.1.7", + "tag": "@rushstack/heft-typescript-plugin_v0.1.7", + "date": "Tue, 13 Jun 2023 01:49:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.53.1` to `0.54.0`" + } + ] + } + }, + { + "version": "0.1.6", + "tag": "@rushstack/heft-typescript-plugin_v0.1.6", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.53.0` to `0.53.1`" + } + ] + } + }, + { + "version": "0.1.5", + "tag": "@rushstack/heft-typescript-plugin_v0.1.5", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "patch": [ + { + "comment": "Emit error if warnings are encountered when building in solution mode. This avoids confusion because the TypeScript compiler implicitly sets `noEmitOnError: true` when building in solution mode." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.52.2` to `0.53.0`" + } + ] + } + }, + { + "version": "0.1.4", + "tag": "@rushstack/heft-typescript-plugin_v0.1.4", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.52.1` to `0.52.2`" + } + ] + } + }, + { + "version": "0.1.3", + "tag": "@rushstack/heft-typescript-plugin_v0.1.3", + "date": "Thu, 08 Jun 2023 00:20:02 GMT", + "comments": { + "patch": [ + { + "comment": "Use the temp folder instead of the cache folder." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.52.0` to `0.52.1`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/heft-typescript-plugin_v0.1.2", + "date": "Wed, 07 Jun 2023 22:45:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-config-file\" to `0.12.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `0.51.0` to `0.52.0`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/heft-typescript-plugin_v0.1.1", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "patch": [ + { + "comment": "Fix resolution of relative tsconfig paths that start with './' or '../'." + } + ] + } + }, { "version": "0.1.0", "tag": "@rushstack/heft-typescript-plugin_v0.1.0", diff --git a/heft-plugins/heft-typescript-plugin/CHANGELOG.md b/heft-plugins/heft-typescript-plugin/CHANGELOG.md index 775cfd8b41b..649277e0201 100644 --- a/heft-plugins/heft-typescript-plugin/CHANGELOG.md +++ b/heft-plugins/heft-typescript-plugin/CHANGELOG.md @@ -1,6 +1,940 @@ # Change Log - @rushstack/heft-typescript-plugin -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 1.3.17 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 1.3.16 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 1.3.15 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.3.14 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 1.3.13 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 1.3.12 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.3.11 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.3.10 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 1.3.9 +Sat, 18 Apr 2026 00:15:16 GMT + +### Patches + +- Bump semver. + +## 1.3.8 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.3.7 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.3.6 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.3.5 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.3.4 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.3.3 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.3.2 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.3.1 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.3.0 +Wed, 25 Feb 2026 00:34:29 GMT + +### Minor changes + +- Add `emitModulePackageJson` option for `additionalModuleKindsToEmit` entries. When enabled, a `package.json` with the appropriate `"type"` field is written to the output folder after compilation, ensuring Node.js correctly interprets `.js` files regardless of the nearest ancestor package.json `"type"` setting. + +## 1.2.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.2.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.2.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.2.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.2.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.1.14 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.1.13 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.1.12 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.1.11 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.1.10 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.1.9 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 1.1.8 +Mon, 05 Jan 2026 16:12:49 GMT + +### Patches + +- Fix TypeScript build cache hash computation to use relative paths with normalized separators for portability across machines and platforms + +## 1.1.7 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.1.6 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.1.5 +Wed, 12 Nov 2025 01:12:56 GMT + +### Patches + +- Support "${configDir}" token in tsconfig when using file copier. + +## 1.1.4 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 1.1.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.1.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.1.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 1.1.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.9.15 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.9.14 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.9.13 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.9.12 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.9.11 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 0.9.10 +Mon, 28 Jul 2025 15:11:56 GMT + +### Patches + +- Update internal typings. + +## 0.9.9 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.9.8 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.9.7 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.9.6 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.9.5 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.9.4 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.9.3 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.9.2 +Thu, 17 Apr 2025 00:11:21 GMT + +### Patches + +- Update documentation for `extends` + +## 0.9.1 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 0.9.0 +Wed, 09 Apr 2025 00:11:02 GMT + +### Minor changes + +- Leverage Heft's new `tryLoadProjectConfigurationFileAsync` method. + +## 0.8.2 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.8.1 +Tue, 25 Mar 2025 15:11:16 GMT + +_Version update only_ + +## 0.8.0 +Wed, 12 Mar 2025 22:41:36 GMT + +### Minor changes + +- Expose some internal APIs to be used by `@rushstack/heft-isolated-typescript-transpile-plugin`. + +## 0.7.1 +Wed, 12 Mar 2025 00:11:31 GMT + +_Version update only_ + +## 0.7.0 +Tue, 11 Mar 2025 02:12:33 GMT + +### Minor changes + +- Add support for TypeScript 5.8. + +## 0.6.16 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.6.15 +Sat, 01 Mar 2025 07:23:16 GMT + +### Patches + +- Add support for TypeScript 5.7. + +## 0.6.14 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.6.13 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.6.12 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 0.6.11 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.6.10 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.6.9 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.6.8 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 0.6.7 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.6.6 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.6.5 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.6.4 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.6.3 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.6.2 +Tue, 03 Dec 2024 16:11:07 GMT + +_Version update only_ + +## 0.6.1 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.6.0 +Fri, 22 Nov 2024 01:10:43 GMT + +### Minor changes + +- Add "onlyResolveSymlinksInNodeModules" option to improve performance for typical repository layouts. + +## 0.5.35 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.5.34 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.5.33 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.5.32 +Wed, 16 Oct 2024 00:11:20 GMT + +### Patches + +- Support typescript v5.6 + +## 0.5.31 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.5.30 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.5.29 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.5.28 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.5.27 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.5.26 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.5.25 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.5.24 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.5.23 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.5.22 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.5.21 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 0.5.20 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 0.5.19 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.5.18 +Tue, 16 Jul 2024 00:36:22 GMT + +_Version update only_ + +## 0.5.17 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.5.16 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.5.15 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.5.14 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 0.5.13 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.5.12 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.5.11 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.5.10 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.5.9 +Fri, 24 May 2024 00:15:09 GMT + +_Version update only_ + +## 0.5.8 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 0.5.7 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.5.6 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.5.5 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.5.4 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.5.3 +Wed, 08 May 2024 22:23:51 GMT + +_Version update only_ + +## 0.5.2 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 0.5.1 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.5.0 +Thu, 28 Mar 2024 22:42:23 GMT + +### Minor changes + +- Gracefully exit transpile worker instead of using `process.exit(0)`. + +## 0.4.0 +Tue, 19 Mar 2024 15:10:18 GMT + +### Minor changes + +- Bump latest supported version of TypeScript to 5.4 + +## 0.3.21 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 0.3.20 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 0.3.19 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 0.3.18 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 0.3.17 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 0.3.16 +Thu, 29 Feb 2024 07:11:46 GMT + +_Version update only_ + +## 0.3.15 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.3.14 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.3.13 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.3.12 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.3.11 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.3.10 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.3.9 +Tue, 20 Feb 2024 16:10:53 GMT + +_Version update only_ + +## 0.3.8 +Mon, 19 Feb 2024 21:54:26 GMT + +_Version update only_ + +## 0.3.7 +Sat, 17 Feb 2024 06:24:35 GMT + +_Version update only_ + +## 0.3.6 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 0.3.5 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.3.4 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.3.3 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.3.2 +Tue, 23 Jan 2024 20:12:58 GMT + +_Version update only_ + +## 0.3.1 +Tue, 23 Jan 2024 16:15:05 GMT + +_Version update only_ + +## 0.3.0 +Tue, 16 Jan 2024 18:30:10 GMT + +### Minor changes + +- Add support for TypeScript 5.3 + +## 0.2.16 +Wed, 03 Jan 2024 00:31:18 GMT + +### Patches + +- Fix break in watch mode during updateShapeSignature call. + +## 0.2.15 +Wed, 20 Dec 2023 01:09:46 GMT + +_Version update only_ + +## 0.2.14 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.2.13 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.2.12 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 0.2.11 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 0.2.10 +Mon, 30 Oct 2023 23:36:38 GMT + +_Version update only_ + +## 0.2.9 +Sun, 01 Oct 2023 02:56:30 GMT + +_Version update only_ + +## 0.2.8 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 0.2.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.2.6 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 0.2.5 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 0.2.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.2.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 0.2.2 +Fri, 22 Sep 2023 00:05:51 GMT + +_Version update only_ + +## 0.2.1 +Tue, 19 Sep 2023 15:21:51 GMT + +_Version update only_ + +## 0.2.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +### Patches + +- Fix bugs related to tracking of the current working directory if the value changes. + +## 0.1.21 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 0.1.20 +Sat, 29 Jul 2023 00:22:51 GMT + +_Version update only_ + +## 0.1.19 +Thu, 20 Jul 2023 20:47:29 GMT + +_Version update only_ + +## 0.1.18 +Wed, 19 Jul 2023 00:20:31 GMT + +### Patches + +- Updated semver dependency + +## 0.1.17 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 0.1.16 +Wed, 12 Jul 2023 15:20:40 GMT + +_Version update only_ + +## 0.1.15 +Wed, 12 Jul 2023 00:23:29 GMT + +### Patches + +- Only make warnings terminal if "buildProjectReferences" is true. + +## 0.1.14 +Fri, 07 Jul 2023 00:19:33 GMT + +_Version update only_ + +## 0.1.13 +Thu, 06 Jul 2023 00:16:20 GMT + +_Version update only_ + +## 0.1.12 +Tue, 04 Jul 2023 00:18:47 GMT + +### Patches + +- Fix evaluation of the "project" configuration option. + +## 0.1.11 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 0.1.10 +Thu, 15 Jun 2023 00:21:02 GMT + +_Version update only_ + +## 0.1.9 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 0.1.8 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 0.1.7 +Tue, 13 Jun 2023 01:49:01 GMT + +_Version update only_ + +## 0.1.6 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 0.1.5 +Fri, 09 Jun 2023 00:19:49 GMT + +### Patches + +- Emit error if warnings are encountered when building in solution mode. This avoids confusion because the TypeScript compiler implicitly sets `noEmitOnError: true` when building in solution mode. + +## 0.1.4 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 0.1.3 +Thu, 08 Jun 2023 00:20:02 GMT + +### Patches + +- Use the temp folder instead of the cache folder. + +## 0.1.2 +Wed, 07 Jun 2023 22:45:16 GMT + +_Version update only_ + +## 0.1.1 +Mon, 05 Jun 2023 21:45:21 GMT + +### Patches + +- Fix resolution of relative tsconfig paths that start with './' or '../'. ## 0.1.0 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/heft-plugins/heft-typescript-plugin/config/api-extractor.json b/heft-plugins/heft-typescript-plugin/config/api-extractor.json index 74590d3c4f8..a29dfe9491e 100644 --- a/heft-plugins/heft-typescript-plugin/config/api-extractor.json +++ b/heft-plugins/heft-typescript-plugin/config/api-extractor.json @@ -1,14 +1,11 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/api-extractor-base.json", - "mainEntryPointFilePath": "/lib/index.d.ts", - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, "docModel": { "enabled": false }, + "dtsRollup": { "enabled": true, "betaTrimmedFilePath": "/dist/.d.ts" diff --git a/heft-plugins/heft-typescript-plugin/config/heft.json b/heft-plugins/heft-typescript-plugin/config/heft.json new file mode 100644 index 00000000000..8d1359f022f --- /dev/null +++ b/heft-plugins/heft-typescript-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-typescript-plugin/config/rig.json b/heft-plugins/heft-typescript-plugin/config/rig.json index 6ac88a96368..cc98dea43dd 100644 --- a/heft-plugins/heft-typescript-plugin/config/rig.json +++ b/heft-plugins/heft-typescript-plugin/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "decoupled-local-node-rig" } diff --git a/heft-plugins/heft-typescript-plugin/eslint.config.js b/heft-plugins/heft-typescript-plugin/eslint.config.js new file mode 100644 index 00000000000..e54effd122a --- /dev/null +++ b/heft-plugins/heft-typescript-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-typescript-plugin/heft-plugin.json b/heft-plugins/heft-typescript-plugin/heft-plugin.json index c569904fca9..f4603ba1068 100644 --- a/heft-plugins/heft-typescript-plugin/heft-plugin.json +++ b/heft-plugins/heft-typescript-plugin/heft-plugin.json @@ -1,10 +1,10 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-plugin.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", "taskPlugins": [ { "pluginName": "typescript-plugin", - "entryPoint": "./lib/TypeScriptPlugin" + "entryPoint": "./lib-commonjs/TypeScriptPlugin" } ] } diff --git a/heft-plugins/heft-typescript-plugin/package.json b/heft-plugins/heft-typescript-plugin/package.json index 3f1e5928c0b..eb9611c7959 100644 --- a/heft-plugins/heft-typescript-plugin/package.json +++ b/heft-plugins/heft-typescript-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/heft-typescript-plugin", - "version": "0.1.0", + "version": "1.3.17", "description": "Heft plugin for TypeScript", "repository": { "type": "git", @@ -8,32 +8,56 @@ "directory": "heft-plugins/heft-typescript-plugin" }, "homepage": "https://rushstack.io/pages/heft/overview/", - "main": "lib/index.js", - "types": "dist/heft-typescript-plugin.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/heft-typescript-plugin.d.ts", + "exports": { + ".": { + "types": "./dist/heft-typescript-plugin.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "license": "MIT", "scripts": { - "build": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged build --clean", - "start": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged test --clean --watch", - "_phase:build": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged build --clean", - "_phase:test": "node ./node_modules/@rushstack/heft-legacy/bin/heft --unmanaged test --no-build" + "build": "heft test --clean", + "start": "heft build-watch", + "_phase:build": "heft run --only build -- --clean" }, "peerDependencies": { - "@rushstack/heft": "0.51.0" + "@rushstack/heft": "1.2.22" }, "dependencies": { "@rushstack/node-core-library": "workspace:*", "@rushstack/heft-config-file": "workspace:*", "@types/tapable": "1.0.6", - "semver": "~7.3.0", + "semver": "~7.7.4", "tapable": "1.1.3" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-legacy": "npm:@rushstack/heft@0.50.6", - "@rushstack/heft-node-rig": "1.13.0", - "@types/node": "14.18.36", - "@types/semver": "7.3.5", - "typescript": "~5.0.4" - } + "@rushstack/terminal": "workspace:*", + "@types/semver": "7.7.1", + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0", + "typescript": "~5.8.2" + }, + "sideEffects": false } diff --git a/heft-plugins/heft-typescript-plugin/src/TranspilerWorker.ts b/heft-plugins/heft-typescript-plugin/src/TranspilerWorker.ts index 147b6654800..9021218e3c5 100644 --- a/heft-plugins/heft-typescript-plugin/src/TranspilerWorker.ts +++ b/heft-plugins/heft-typescript-plugin/src/TranspilerWorker.ts @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. + import { parentPort, workerData } from 'node:worker_threads'; import type * as TTypescript from 'typescript'; + import type { ITranspilationErrorMessage, ITranspilationRequestMessage, @@ -16,9 +18,13 @@ const typedWorkerData: ITypescriptWorkerData = workerData; const ts: ExtendedTypeScript = require(typedWorkerData.typeScriptToolPath); +process.exitCode = 1; + function handleMessage(message: ITranspilationRequestMessage | false): void { if (!message) { - process.exit(0); + parentPort!.off('message', handleMessage); + parentPort!.close(); + return; } try { @@ -115,4 +121,7 @@ function runTranspiler(message: ITranspilationRequestMessage): ITranspilationSuc return response; } +parentPort!.once('close', () => { + process.exitCode = 0; +}); parentPort!.on('message', handleMessage); diff --git a/heft-plugins/heft-typescript-plugin/src/TypeScriptBuilder.ts b/heft-plugins/heft-typescript-plugin/src/TypeScriptBuilder.ts index 665501666aa..b819d050c3b 100644 --- a/heft-plugins/heft-typescript-plugin/src/TypeScriptBuilder.ts +++ b/heft-plugins/heft-typescript-plugin/src/TypeScriptBuilder.ts @@ -1,17 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as crypto from 'crypto'; -import * as path from 'path'; -import { Worker } from 'worker_threads'; +import * as crypto from 'node:crypto'; +import * as path from 'node:path'; +import { Worker } from 'node:worker_threads'; -import * as semver from 'semver'; import type * as TTypescript from 'typescript'; -import { type ITerminal, JsonFile, type IPackageJson, Path, FileError } from '@rushstack/node-core-library'; -import type { IScopedLogger } from '@rushstack/heft'; -import type { ExtendedTypeScript, IExtendedSolutionBuilder } from './internalTypings/TypeScriptInternals'; -import type { ITypeScriptConfigurationJson } from './TypeScriptPlugin'; +import { Path, FileError } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; +import type { HeftConfiguration, IScopedLogger } from '@rushstack/heft'; + +import type { + ExtendedBuilderProgram, + ExtendedTypeScript, + IExtendedSolutionBuilder, + ITypeScriptNodeSystem +} from './internalTypings/TypeScriptInternals'; +import type { ITypeScriptConfigurationJson, IEmitModuleKind } from './TypeScriptPlugin'; import type { PerformanceMeasurer } from './Performance'; import type { ICachedEmitModuleKind, @@ -20,6 +26,8 @@ import type { ITypescriptWorkerData } from './types'; import { configureProgramForMultiEmit } from './configureProgramForMultiEmit'; +import { loadTsconfig } from './tsconfigLoader'; +import { loadTypeScriptToolAsync } from './loadTypeScriptTool'; export interface ITypeScriptBuilderConfiguration extends ITypeScriptConfigurationJson { /** @@ -35,7 +43,7 @@ export interface ITypeScriptBuilderConfiguration extends ITypeScriptConfiguratio /** * The path to the TypeScript tool. */ - typeScriptToolPath: string; + heftConfiguration: HeftConfiguration; // watchMode: boolean; @@ -66,20 +74,6 @@ type TWatchSolutionHost = type TWatchProgram = TTypescript.WatchOfFilesAndCompilerOptions; -interface ICompilerCapabilities { - /** - * Support for incremental compilation via `ts.createIncrementalProgram()`. - * Introduced with TypeScript 3.6. - */ - incrementalProgram: boolean; - - /** - * Support for composite projects via `ts.createSolutionBuilder()`. - * Introduced with TypeScript 3.0. - */ - solutionBuilder: boolean; -} - interface IFileToWrite { filePath: string; data: string; @@ -106,14 +100,16 @@ interface ITranspileSignal { reject: (error: Error) => void; } -const OLDEST_SUPPORTED_TS_MAJOR_VERSION: number = 2; -const OLDEST_SUPPORTED_TS_MINOR_VERSION: number = 9; - -const NEWEST_SUPPORTED_TS_MAJOR_VERSION: number = 5; -const NEWEST_SUPPORTED_TS_MINOR_VERSION: number = 0; - -interface ITypeScriptTool { +/** + * @internal + */ +export interface IBaseTypeScriptTool { + typeScriptToolPath: string; ts: ExtendedTypeScript; + system: TSystem; +} + +interface ITypeScriptTool extends IBaseTypeScriptTool { measureSync: PerformanceMeasurer; sourceFileCache: Map; @@ -132,8 +128,6 @@ interface ITypeScriptTool { pendingTranspileSignals: Map; reportDiagnostic: TTypescript.DiagnosticReporter; - clearTimeout: (timeout: IPendingWork) => void; - setTimeout: (timeout: (...args: T) => void, ms: number, ...args: T) => IPendingWork; } export class TypeScriptBuilder { @@ -141,10 +135,6 @@ export class TypeScriptBuilder { private readonly _typescriptLogger: IScopedLogger; private readonly _typescriptTerminal: ITerminal; - private _typescriptVersion!: string; - private _typescriptParsedVersion!: semver.SemVer; - - private _capabilities!: ICompilerCapabilities; private _useSolutionBuilder!: boolean; private _moduleKindsToEmit!: ICachedEmitModuleKind[]; @@ -162,12 +152,17 @@ export class TypeScriptBuilder { // We only need to hash our additional Heft configuration. const configHash: crypto.Hash = crypto.createHash('sha1'); - configHash.update(JSON.stringify(this._configuration.additionalModuleKindsToEmit || {})); - const serializedConfigHash: string = configHash - .digest('base64') - .slice(0, 8) - .replace(/\+/g, '-') - .replace(/\//g, '_'); + // Relativize the outFolderName paths before hashing to ensure portability across different machines + const normalizedConfig: IEmitModuleKind[] = + this._configuration.additionalModuleKindsToEmit?.map((emitKind) => ({ + ...emitKind, + outFolderName: Path.convertToSlashes( + path.relative(this._configuration.buildFolderPath, emitKind.outFolderName) + ) + })) || []; + + configHash.update(JSON.stringify(normalizedConfig)); + const serializedConfigHash: string = configHash.digest('base64url').slice(0, 8); // This conversion is theoretically redundant, but it is here to make absolutely sure that the path is formatted // using only '/' as the directory separator so that incremental builds don't break on Windows. @@ -189,70 +184,15 @@ export class TypeScriptBuilder { public async invokeAsync(onChangeDetected?: () => void): Promise { if (!this._tool) { - // Determine the compiler version - const compilerPackageJsonFilename: string = path.join( - this._configuration.typeScriptToolPath, - 'package.json' - ); - const packageJson: IPackageJson = await JsonFile.loadAsync(compilerPackageJsonFilename); - this._typescriptVersion = packageJson.version; - const parsedVersion: semver.SemVer | null = semver.parse(this._typescriptVersion); - if (!parsedVersion) { - throw new Error( - `Unable to parse version "${this._typescriptVersion}" for TypeScript compiler package in: ` + - compilerPackageJsonFilename - ); - } - this._typescriptParsedVersion = parsedVersion; - - // Detect what features this compiler supports. Note that manually comparing major/minor numbers - // loosens the matching to accept prereleases such as "3.6.0-dev.20190530" - this._capabilities = { - incrementalProgram: false, - solutionBuilder: this._typescriptParsedVersion.major >= 3 - }; - - if ( - this._typescriptParsedVersion.major > 3 || - (this._typescriptParsedVersion.major === 3 && this._typescriptParsedVersion.minor >= 6) - ) { - this._capabilities.incrementalProgram = true; - } - + const { + tool: { ts, system: baseSystem, typeScriptToolPath } + } = await loadTypeScriptToolAsync({ + terminal: this._typescriptTerminal, + heftConfiguration: this._configuration.heftConfiguration, + buildProjectReferences: this._configuration.buildProjectReferences, + onlyResolveSymlinksInNodeModules: this._configuration.onlyResolveSymlinksInNodeModules + }); this._useSolutionBuilder = !!this._configuration.buildProjectReferences; - if (this._useSolutionBuilder && !this._capabilities.solutionBuilder) { - throw new Error( - `Building project references requires TypeScript@>=3.0, but the current version is ${this._typescriptVersion}` - ); - } - - // Report a warning if the TypeScript version is too old/new. The current oldest supported version is - // TypeScript 2.9. Prior to that the "ts.getConfigFileParsingDiagnostics()" API is missing; more fixups - // would be required to deal with that. We won't do that work unless someone requests it. - if ( - this._typescriptParsedVersion.major < OLDEST_SUPPORTED_TS_MAJOR_VERSION || - (this._typescriptParsedVersion.major === OLDEST_SUPPORTED_TS_MAJOR_VERSION && - this._typescriptParsedVersion.minor < OLDEST_SUPPORTED_TS_MINOR_VERSION) - ) { - // We don't use writeWarningLine() here because, if the person wants to take their chances with - // a seemingly unsupported compiler, their build should be allowed to succeed. - this._typescriptTerminal.writeLine( - `The TypeScript compiler version ${this._typescriptVersion} is very old` + - ` and has not been tested with Heft; it may not work correctly.` - ); - } else if ( - this._typescriptParsedVersion.major > NEWEST_SUPPORTED_TS_MAJOR_VERSION || - (this._typescriptParsedVersion.major === NEWEST_SUPPORTED_TS_MAJOR_VERSION && - this._typescriptParsedVersion.minor > NEWEST_SUPPORTED_TS_MINOR_VERSION) - ) { - this._typescriptTerminal.writeLine( - `The TypeScript compiler version ${this._typescriptVersion} is newer` + - ' than the latest version that was tested with Heft ' + - `(${NEWEST_SUPPORTED_TS_MAJOR_VERSION}.${NEWEST_SUPPORTED_TS_MINOR_VERSION}); it may not work correctly.` - ); - } - - const ts: ExtendedTypeScript = require(this._configuration.typeScriptToolPath); ts.performance.enable(); @@ -291,8 +231,58 @@ export class TypeScriptBuilder { const pendingOperations: Set = new Set(); + const clearTimeout = (timeout: IPendingWork): void => { + pendingOperations.delete(timeout); + }; + + const setTimeout = ( + fn: (...args: T) => void, + ms: number, + ...args: T + ): IPendingWork => { + const timeout: IPendingWork = () => { + fn(...args); + }; + pendingOperations.add(timeout); + if (!this._tool?.executing && onChangeDetected) { + onChangeDetected(); + } + return timeout; + }; + + const getCurrentDirectory: () => string = () => this._configuration.buildFolderPath; + + // Need to also update watchFile and watchDirectory + const system: ITypeScriptNodeSystem = { + ...baseSystem, + getCurrentDirectory, + clearTimeout, + setTimeout + }; + const { realpath } = system; + + if (realpath && system.getAccessibleFileSystemEntries) { + const { getAccessibleFileSystemEntries } = system; + system.readDirectory = (folderPath, extensions, exclude, include, depth): string[] => { + return ts.matchFiles( + folderPath, + extensions, + exclude, + include, + ts.sys.useCaseSensitiveFileNames, + getCurrentDirectory(), + depth, + getAccessibleFileSystemEntries, + realpath, + ts.sys.directoryExists + ); + }; + } + this._tool = { + typeScriptToolPath, ts, + system, measureSync: measureTsPerformance, @@ -311,21 +301,6 @@ export class TypeScriptBuilder { rawDiagnostics.push(diagnostic); }, - clearTimeout(timeout: IPendingWork): void { - pendingOperations.delete(timeout); - }, - - setTimeout(fn: (...args: T) => void, ms: number, ...args: T): IPendingWork { - const timeout: IPendingWork = () => { - fn(...args); - }; - pendingOperations.add(timeout); - if (!this.executing && onChangeDetected) { - onChangeDetected(); - } - return timeout; - }, - worker: undefined, pendingTranspilePromises: new Map(), @@ -359,7 +334,11 @@ export class TypeScriptBuilder { if (!tool.solutionBuilder && !tool.watchProgram) { //#region CONFIGURE const { duration: configureDurationMs, tsconfig } = measureTsPerformance('Configure', () => { - const _tsconfig: TTypescript.ParsedCommandLine = this._loadTsconfig(ts); + const _tsconfig: TTypescript.ParsedCommandLine = loadTsconfig({ + tool, + tsconfigPath: this._configuration.tsconfigPath, + tsCacheFilePath: this._tsCacheFilePath + }); this._validateTsconfig(ts, _tsconfig); return { @@ -401,7 +380,7 @@ export class TypeScriptBuilder { // eslint-disable-next-line require-atomic-updates tool.executing = false; } - this._logDiagnostics(ts, rawDiagnostics); + this._logDiagnostics(ts, rawDiagnostics, this._useSolutionBuilder); } public async _runBuildAsync(tool: ITypeScriptTool): Promise { @@ -413,7 +392,11 @@ export class TypeScriptBuilder { tsconfig, compilerHost } = measureTsPerformance('Configure', () => { - const _tsconfig: TTypescript.ParsedCommandLine = this._loadTsconfig(ts); + const _tsconfig: TTypescript.ParsedCommandLine = loadTsconfig({ + tool, + tsconfigPath: this._configuration.tsconfigPath, + tsCacheFilePath: this._tsCacheFilePath + }); this._validateTsconfig(ts, _tsconfig); const _compilerHost: TTypescript.CompilerHost = this._buildIncrementalCompilerHost(tool, _tsconfig); @@ -507,6 +490,7 @@ export class TypeScriptBuilder { this._cleanupWorker(); //#endregion + this._emitModulePackageJsonFiles(ts); this._logEmitPerformance(ts); //#region FINAL_ANALYSIS @@ -540,7 +524,11 @@ export class TypeScriptBuilder { if (!tool.solutionBuilder) { //#region CONFIGURE const { duration: configureDurationMs, solutionBuilderHost } = measureSync('Configure', () => { - const _tsconfig: TTypescript.ParsedCommandLine = this._loadTsconfig(ts); + const _tsconfig: TTypescript.ParsedCommandLine = loadTsconfig({ + tool, + tsconfigPath: this._configuration.tsconfigPath, + tsCacheFilePath: this._tsCacheFilePath + }); this._validateTsconfig(ts, _tsconfig); const _solutionBuilderHost: TSolutionHost = this._buildSolutionBuilderHost(tool); @@ -570,6 +558,8 @@ export class TypeScriptBuilder { this._cleanupWorker(); //#endregion + this._emitModulePackageJsonFiles(ts); + if (pendingTranspilePromises.size) { const emitResults: TTypescript.EmitResult[] = await Promise.all(pendingTranspilePromises.values()); for (const { diagnostics } of emitResults) { @@ -579,13 +569,20 @@ export class TypeScriptBuilder { } } - this._logDiagnostics(ts, rawDiagnostics); + this._logDiagnostics(ts, rawDiagnostics, true); } - private _logDiagnostics(ts: ExtendedTypeScript, rawDiagnostics: TTypescript.Diagnostic[]): void { + private _logDiagnostics( + ts: ExtendedTypeScript, + rawDiagnostics: TTypescript.Diagnostic[], + isSolutionMode?: boolean + ): void { const diagnostics: readonly TTypescript.Diagnostic[] = ts.sortAndDeduplicateDiagnostics(rawDiagnostics); if (diagnostics.length > 0) { + let warningCount: number = 0; + let hasError: boolean = false; + this._typescriptTerminal.writeLine( `Encountered ${diagnostics.length} TypeScript issue${diagnostics.length > 1 ? 's' : ''}:` ); @@ -595,8 +592,23 @@ export class TypeScriptBuilder { ts ); + if (diagnosticCategory === ts.DiagnosticCategory.Warning) { + warningCount++; + } else if (diagnosticCategory === ts.DiagnosticCategory.Error) { + hasError = true; + } + this._printDiagnosticMessage(ts, diagnostic, diagnosticCategory); } + + if (isSolutionMode && warningCount > 0 && !hasError) { + this._typescriptLogger.emitError( + new Error( + `TypeScript encountered ${warningCount} warning${warningCount === 1 ? '' : 's'} ` + + `and is configured to build project references. As a result, no files were emitted. Please fix the reported warnings to proceed.` + ) + ); + } } } @@ -726,7 +738,8 @@ export class TypeScriptBuilder { ts.ModuleKind.CommonJS, tsconfig.options.outDir!, /* isPrimary */ tsconfig.options.module === ts.ModuleKind.CommonJS, - '.cjs' + '.cjs', + /* emitModulePackageJson */ false ); const cjsReason: IModuleKindReason = { @@ -745,7 +758,8 @@ export class TypeScriptBuilder { ts.ModuleKind.ESNext, tsconfig.options.outDir!, /* isPrimary */ tsconfig.options.module === ts.ModuleKind.ESNext, - '.mjs' + '.mjs', + /* emitModulePackageJson */ false ); const mjsReason: IModuleKindReason = { @@ -764,7 +778,8 @@ export class TypeScriptBuilder { tsconfig.options.module, tsconfig.options.outDir!, /* isPrimary */ true, - /* jsExtensionOverride */ undefined + /* jsExtensionOverride */ undefined, + /* emitModulePackageJson */ false ); const tsConfigReason: IModuleKindReason = { @@ -779,16 +794,14 @@ export class TypeScriptBuilder { } if (this._configuration.additionalModuleKindsToEmit) { - for (const additionalModuleKindToEmit of this._configuration.additionalModuleKindsToEmit) { - const moduleKind: TTypescript.ModuleKind = this._parseModuleKind( - ts, - additionalModuleKindToEmit.moduleKind - ); + for (const { moduleKind: moduleKindString, outFolderName, emitModulePackageJson = false } of this + ._configuration.additionalModuleKindsToEmit) { + const moduleKind: TTypescript.ModuleKind = this._parseModuleKind(ts, moduleKindString); - const outDirKey: string = `${additionalModuleKindToEmit.outFolderName}:.js`; + const outDirKey: string = `${outFolderName}:.js`; const moduleKindReason: IModuleKindReason = { kind: ts.ModuleKind[moduleKind] as keyof typeof TTypescript.ModuleKind, - outDir: additionalModuleKindToEmit.outFolderName, + outDir: outFolderName, extension: '.js', reason: `additionalModuleKindsToEmit` }; @@ -798,18 +811,19 @@ export class TypeScriptBuilder { if (existingKind) { throw new Error( - `Module kind "${additionalModuleKindToEmit.moduleKind}" is already emitted at ${existingKind.outDir} with extension '${existingKind.extension}' by option ${existingKind.reason}.` + `Module kind "${moduleKind}" is already emitted at ${existingKind.outDir} with extension '${existingKind.extension}' by option ${existingKind.reason}.` ); } else if (existingDir) { throw new Error( - `Output folder "${additionalModuleKindToEmit.outFolderName}" already contains module kind ${existingDir.kind} with extension '${existingDir.extension}', specified by option ${existingDir.reason}.` + `Output folder "${outFolderName}" already contains module kind ${existingDir.kind} with extension '${existingDir.extension}', specified by option ${existingDir.reason}.` ); } else { const outFolderKey: string | undefined = this._addModuleKindToEmit( moduleKind, - additionalModuleKindToEmit.outFolderName, + outFolderName, /* isPrimary */ false, - undefined + undefined, + emitModulePackageJson ); if (outFolderKey) { @@ -825,7 +839,8 @@ export class TypeScriptBuilder { moduleKind: TTypescript.ModuleKind, outFolderPath: string, isPrimary: boolean, - jsExtensionOverride: string | undefined + jsExtensionOverride: string | undefined, + emitModulePackageJson: boolean ): string | undefined { let outFolderName: string; if (path.isAbsolute(outFolderPath)) { @@ -876,40 +891,13 @@ export class TypeScriptBuilder { outFolderPath, moduleKind, jsExtensionOverride, - - isPrimary + isPrimary, + emitModulePackageJson }); return `${outFolderName}:${jsExtensionOverride || '.js'}`; } - private _loadTsconfig(ts: ExtendedTypeScript): TTypescript.ParsedCommandLine { - const parsedConfigFile: ReturnType = ts.readConfigFile( - this._configuration.tsconfigPath, - ts.sys.readFile - ); - - const currentFolder: string = path.dirname(this._configuration.tsconfigPath); - const tsconfig: TTypescript.ParsedCommandLine = ts.parseJsonConfigFileContent( - parsedConfigFile.config, - { - fileExists: ts.sys.fileExists, - readFile: ts.sys.readFile, - readDirectory: ts.sys.readDirectory, - useCaseSensitiveFileNames: true - }, - currentFolder, - /*existingOptions:*/ undefined, - this._configuration.tsconfigPath - ); - - if (tsconfig.options.incremental) { - tsconfig.options.tsBuildInfoFile = this._tsCacheFilePath; - } - - return tsconfig; - } - private _getCreateBuilderProgram( ts: ExtendedTypeScript ): TTypescript.CreateProgram { @@ -990,6 +978,7 @@ export class TypeScriptBuilder { `Emitting program "${innerCompilerOptions!.configFilePath}"` ); + this._emitModulePackageJsonFiles(ts); this._logEmitPerformance(ts); // Reset performance counters @@ -1015,11 +1004,11 @@ export class TypeScriptBuilder { // Do nothing }; - const { ts } = tool; + const { ts, system } = tool; const solutionBuilderHost: TTypescript.SolutionBuilderHost = ts.createSolutionBuilderHost( - ts.sys, + system, this._getCreateBuilderProgram(ts), tool.reportDiagnostic, reportSolutionBuilderStatus, @@ -1044,14 +1033,18 @@ export class TypeScriptBuilder { tool: ITypeScriptTool, tsconfig: TTypescript.ParsedCommandLine ): TTypescript.CompilerHost { - const { ts } = tool; + const { ts, system } = tool; let compilerHost: TTypescript.CompilerHost | undefined; if (tsconfig.options.incremental) { - compilerHost = ts.createIncrementalCompilerHost(tsconfig.options, ts.sys); + compilerHost = ts.createIncrementalCompilerHost(tsconfig.options, system); } else { - compilerHost = ts.createCompilerHost(tsconfig.options); + compilerHost = (ts.createCompilerHostWorker ?? ts.createCompilerHost)( + tsconfig.options, + undefined, + system + ); } this._changeCompilerHostToUseCache(compilerHost, tool); @@ -1063,7 +1056,7 @@ export class TypeScriptBuilder { tool: ITypeScriptTool, tsconfig: TTypescript.ParsedCommandLine ): TWatchCompilerHost { - const { ts } = tool; + const { ts, system } = tool; const reportWatchStatus: TTypescript.DiagnosticReporter = (diagnostic: TTypescript.Diagnostic): void => { this._printDiagnosticMessage(ts, diagnostic); @@ -1072,7 +1065,7 @@ export class TypeScriptBuilder { const compilerHost: TWatchCompilerHost = ts.createWatchCompilerHost( tsconfig.fileNames, tsconfig.options, - ts.sys, + system, this._getCreateBuilderProgram(ts), tool.reportDiagnostic, reportWatchStatus, @@ -1080,9 +1073,6 @@ export class TypeScriptBuilder { tsconfig.watchOptions ); - compilerHost.clearTimeout = tool.clearTimeout; - compilerHost.setTimeout = tool.setTimeout; - return compilerHost; } @@ -1094,6 +1084,8 @@ export class TypeScriptBuilder { return; } + compilerHost.getCurrentDirectory = () => this._configuration.buildFolderPath; + // Enable source file persistence const getSourceFile: typeof innerGetSourceFile & { cache?: typeof sourceFileCache; @@ -1130,22 +1122,70 @@ export class TypeScriptBuilder { } private _buildWatchSolutionBuilderHost(tool: ITypeScriptTool): TWatchSolutionHost { - const { reportDiagnostic, ts } = tool; + const { reportDiagnostic, ts, system } = tool; const host: TWatchSolutionHost = ts.createSolutionBuilderWithWatchHost( - ts.sys, + system, this._getCreateBuilderProgram(ts), reportDiagnostic, reportDiagnostic, reportDiagnostic ); - host.clearTimeout = tool.clearTimeout; - host.setTimeout = tool.setTimeout; - return host; } + /** + * For each module kind configured with `emitModulePackageJson: true`, writes a + * `package.json` with the appropriate `"type"` field to ensure Node.js correctly + * interprets `.js` files in the output folder. + */ + private _emitModulePackageJsonFiles(ts: ExtendedTypeScript): void { + for (const { emitModulePackageJson, moduleKind, outFolderPath } of this._moduleKindsToEmit) { + if (!emitModulePackageJson) { + continue; + } + + // "module" and "commonjs" are the only recognized values. See + // https://nodejs.org/api/packages.html#type + let moduleType: string | undefined; + switch (moduleKind) { + // UMD contains a CommonJS wrapper, so it should be treated as CommonJS for package.json generation purposes + case ts.ModuleKind.UMD: + case ts.ModuleKind.CommonJS: { + moduleType = 'commonjs'; + break; + } + + case ts.ModuleKind.AMD: + case ts.ModuleKind.None: + case ts.ModuleKind.Preserve: + case ts.ModuleKind.System: { + moduleType = undefined; + break; + } + + default: { + moduleType = 'module'; + break; + } + } + + if (moduleType) { + const packageJsonPath: string = `${outFolderPath}package.json`; + const packageJsonContent: string = `{\n "type": "${moduleType}"\n}\n`; + + ts.sys.writeFile(packageJsonPath, packageJsonContent); + this._typescriptTerminal.writeVerboseLine(`Wrote ${packageJsonPath} with "type": "${moduleType}"`); + } else { + throw new Error( + `Unsupported module kind ${ts.ModuleKind[moduleKind]} for package.json generation. ` + + `Remove the \`emitModulePackageJson\` option for this module kind.` + ); + } + } + } + private _parseModuleKind(ts: ExtendedTypeScript, moduleKindName: string): TTypescript.ModuleKind { switch (moduleKindName.toLowerCase()) { case 'commonjs': @@ -1176,11 +1216,11 @@ export class TypeScriptBuilder { compilerOptions: TTypescript.CompilerOptions, filesToTranspile: Map ): void { - const { pendingTranspilePromises, pendingTranspileSignals } = tool; + const { typeScriptToolPath, pendingTranspilePromises, pendingTranspileSignals } = tool; let maybeWorker: Worker | undefined = tool.worker; if (!maybeWorker) { const workerData: ITypescriptWorkerData = { - typeScriptToolPath: this._configuration.typeScriptToolPath + typeScriptToolPath }; tool.worker = maybeWorker = new Worker(require.resolve('./TranspilerWorker.js'), { workerData: workerData @@ -1269,9 +1309,10 @@ export class TypeScriptBuilder { function getFilesToTranspileFromBuilderProgram( builderProgram: TTypescript.BuilderProgram ): Map { - const changedFilesSet: Set = ( - builderProgram as unknown as { getState(): { changedFilesSet: Set } } - ).getState().changedFilesSet; + const program: ExtendedBuilderProgram = builderProgram as unknown as ExtendedBuilderProgram; + // getState was removed in Typescript 5.6, replaced with state + const changedFilesSet: Set = (program.state ?? program.getState()).changedFilesSet; + const filesToTranspile: Map = new Map(); for (const fileName of changedFilesSet) { const sourceFile: TTypescript.SourceFile | undefined = builderProgram.getSourceFile(fileName); diff --git a/heft-plugins/heft-typescript-plugin/src/TypeScriptPlugin.ts b/heft-plugins/heft-typescript-plugin/src/TypeScriptPlugin.ts index 81dbacc2b8f..6fb6eb72647 100644 --- a/heft-plugins/heft-typescript-plugin/src/TypeScriptPlugin.ts +++ b/heft-plugins/heft-typescript-plugin/src/TypeScriptPlugin.ts @@ -1,12 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; import type * as TTypescript from 'typescript'; import { SyncHook } from 'tapable'; -import { FileSystem, Path, type ITerminal } from '@rushstack/node-core-library'; -import { ConfigurationFile, InheritanceType, PathResolutionMethod } from '@rushstack/heft-config-file'; + +import { FileSystem } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; +import { ProjectConfigurationFile, InheritanceType, PathResolutionMethod } from '@rushstack/heft-config-file'; import type { HeftConfiguration, IHeftTaskSession, @@ -14,10 +16,14 @@ import type { IHeftTaskRunHookOptions, IHeftTaskRunIncrementalHookOptions, ICopyOperation, - IHeftTaskFileOperations + IHeftTaskFileOperations, + ConfigurationFile } from '@rushstack/heft'; -import { TypeScriptBuilder, ITypeScriptBuilderConfiguration } from './TypeScriptBuilder'; +import { TypeScriptBuilder, type ITypeScriptBuilderConfiguration } from './TypeScriptBuilder'; +import anythingSchema from './schemas/anything.schema.json'; +import typescriptConfigSchema from './schemas/typescript.schema.json'; +import { getTsconfigFilePath } from './tsconfigLoader'; /** * The name of the plugin, as specified in heft-plugin.json @@ -26,6 +32,12 @@ import { TypeScriptBuilder, ITypeScriptBuilderConfiguration } from './TypeScript */ export const PLUGIN_NAME: 'typescript-plugin' = 'typescript-plugin'; +/** + * The ${configDir} token supported in TypeScript 5.5 + * @see {@link https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html#the-configdir-template-variable-for-configuration-files} + */ +const CONFIG_DIR_TOKEN: '${configDir}' = '${configDir}'; + /** * @beta */ @@ -33,6 +45,7 @@ export interface IEmitModuleKind { moduleKind: 'commonjs' | 'amd' | 'umd' | 'system' | 'es2015' | 'esnext'; outFolderName: string; jsExtensionOverride?: string; + emitModulePackageJson?: boolean; } /** @@ -75,6 +88,12 @@ export interface ITypeScriptConfigurationJson { */ useTranspilerWorker?: boolean; + /** + * If true, the TypeScript compiler will only resolve symlinks to their targets if the links are in a node_modules folder. + * This significantly reduces file system operations in typical usage. + */ + onlyResolveSymlinksInNodeModules?: boolean; + /* * Specifies the tsconfig.json file that will be used for compilation. Equivalent to the "project" argument for the 'tsc' and 'tslint' command line tools. * @@ -118,11 +137,22 @@ export interface ITypeScriptPluginAccessor { readonly onChangedFilesHook: SyncHook; } -let _typeScriptConfigurationFileLoader: ConfigurationFile | undefined; -const _typeScriptConfigurationFilePromiseCache: Map< - string, - Promise -> = new Map(); +const TYPESCRIPT_LOADER_CONFIG: ConfigurationFile.IProjectConfigurationFileSpecification = + { + projectRelativeFilePath: 'config/typescript.json', + jsonSchemaObject: typescriptConfigSchema, + propertyInheritance: { + staticAssetsToCopy: { + // When merging objects, arrays will be automatically appended + inheritanceType: InheritanceType.merge + } + }, + jsonPathMetadata: { + '$.additionalModuleKindsToEmit.*.outFolderName': { + pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToProjectRoot + } + } + }; /** * @beta @@ -131,52 +161,15 @@ export async function loadTypeScriptConfigurationFileAsync( heftConfiguration: HeftConfiguration, terminal: ITerminal ): Promise { - const buildFolderPath: string = heftConfiguration.buildFolderPath; - - // Check the cache first - let typescriptConfigurationFilePromise: Promise | undefined = - _typeScriptConfigurationFilePromiseCache.get(buildFolderPath); - - if (!typescriptConfigurationFilePromise) { - // Ensure that the file loader has been initialized. - if (!_typeScriptConfigurationFileLoader) { - const schemaPath: string = `${__dirname}/schemas/typescript.schema.json`; - _typeScriptConfigurationFileLoader = new ConfigurationFile({ - projectRelativeFilePath: 'config/typescript.json', - jsonSchemaPath: schemaPath, - propertyInheritance: { - staticAssetsToCopy: { - // When merging objects, arrays will be automatically appended - inheritanceType: InheritanceType.merge - } - } - }); - } - - typescriptConfigurationFilePromise = - _typeScriptConfigurationFileLoader.tryLoadConfigurationFileForProjectAsync( - terminal, - buildFolderPath, - heftConfiguration.rigConfig - ); - _typeScriptConfigurationFilePromiseCache.set(buildFolderPath, typescriptConfigurationFilePromise); - } - - return await typescriptConfigurationFilePromise; + return await heftConfiguration.tryLoadProjectConfigurationFileAsync( + TYPESCRIPT_LOADER_CONFIG, + terminal + ); } -let _partialTsconfigFileLoader: ConfigurationFile | undefined; +let _partialTsconfigFileLoader: ProjectConfigurationFile | undefined; const _partialTsconfigFilePromiseCache: Map> = new Map(); -function getTsconfigFilePath( - heftConfiguration: HeftConfiguration, - typeScriptConfigurationJson?: ITypeScriptConfigurationJson -): string { - return Path.convertToSlashes( - `${heftConfiguration.buildFolderPath}/${typeScriptConfigurationJson?.project || './tsconfig.json'}` - ); -} - /** * @beta */ @@ -196,7 +189,10 @@ export async function loadPartialTsconfigFileAsync( // advantage of the extends functionality that ConfigurationFile provides. So we'll // check to see if the file exists and exit early if not. - const tsconfigFilePath: string = getTsconfigFilePath(heftConfiguration, typeScriptConfigurationJson); + const tsconfigFilePath: string = getTsconfigFilePath( + heftConfiguration, + typeScriptConfigurationJson?.project + ); terminal.writeVerboseLine(`Looking for tsconfig at ${tsconfigFilePath}`); const tsconfigExists: boolean = await FileSystem.existsAsync(tsconfigFilePath); if (!tsconfigExists) { @@ -204,10 +200,9 @@ export async function loadPartialTsconfigFileAsync( } else { // Ensure that the file loader has been initialized. if (!_partialTsconfigFileLoader) { - const schemaPath: string = `${__dirname}/schemas/anything.schema.json`; - _partialTsconfigFileLoader = new ConfigurationFile({ - projectRelativeFilePath: 'tsconfig.json', - jsonSchemaPath: schemaPath, + _partialTsconfigFileLoader = new ProjectConfigurationFile({ + projectRelativeFilePath: typeScriptConfigurationJson?.project || 'tsconfig.json', + jsonSchemaObject: anythingSchema, propertyInheritance: { compilerOptions: { inheritanceType: InheritanceType.merge @@ -215,7 +210,20 @@ export async function loadPartialTsconfigFileAsync( }, jsonPathMetadata: { '$.compilerOptions.outDir': { - pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToConfigurationFile + pathResolutionMethod: PathResolutionMethod.custom, + customResolver( + resolverOptions: ConfigurationFile.IJsonPathMetadataResolverOptions + ): string { + if (resolverOptions.propertyValue.includes(CONFIG_DIR_TOKEN)) { + // Typescript 5.5. introduced the `${configDir}` token to refer to the directory containing the root tsconfig + const configDir: string = path.dirname(tsconfigFilePath); + // The token is an absolute path, so it should occur at most once. + return path.resolve(resolverOptions.propertyValue.replace(CONFIG_DIR_TOKEN, configDir)); + } else { + const thisConfigDir: string = path.dirname(resolverOptions.configurationFilePath); + return path.resolve(thisConfigDir, resolverOptions.propertyValue); + } + } } } }); @@ -233,6 +241,11 @@ export async function loadPartialTsconfigFileAsync( return await partialTsconfigFilePromise; } +interface ITypeScriptConfigurationJsonAndPartialTsconfigFile { + typeScriptConfigurationJson: ITypeScriptConfigurationJson | undefined; + partialTsconfigFile: IPartialTsconfig | undefined; +} + export default class TypeScriptPlugin implements IHeftTaskPlugin { public accessor: ITypeScriptPluginAccessor = { onChangedFilesHook: new SyncHook(['changedFilesHookOptions']) @@ -246,7 +259,7 @@ export default class TypeScriptPlugin implements IHeftTaskPlugin { // all source files to this set of static assets. This would allow us to avoid // having to copy the static assets multiple times, increasing build times and // package size. - for (const copyOperation of await this._getStaticAssetCopyOperations( + for (const copyOperation of await this._getStaticAssetCopyOperationsAsync( taskSession, heftConfiguration )) { @@ -283,37 +296,39 @@ export default class TypeScriptPlugin implements IHeftTaskPlugin { ); } - private async _getStaticAssetCopyOperations( + private async _getStaticAssetCopyOperationsAsync( taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration ): Promise { - const typeScriptConfiguration: ITypeScriptConfigurationJson | undefined = - await loadTypeScriptConfigurationFileAsync(heftConfiguration, taskSession.logger.terminal); + const { typeScriptConfigurationJson, partialTsconfigFile } = await this._loadConfigAsync( + taskSession, + heftConfiguration + ); // We only care about the copy if static assets were specified. const copyOperations: ICopyOperation[] = []; + const staticAssetsConfig: IStaticAssetsCopyConfiguration | undefined = + typeScriptConfigurationJson?.staticAssetsToCopy; if ( - typeScriptConfiguration?.staticAssetsToCopy?.fileExtensions?.length || - typeScriptConfiguration?.staticAssetsToCopy?.includeGlobs?.length || - typeScriptConfiguration?.staticAssetsToCopy?.excludeGlobs?.length + staticAssetsConfig && + (staticAssetsConfig.fileExtensions?.length || + staticAssetsConfig.includeGlobs?.length || + staticAssetsConfig.excludeGlobs?.length) ) { const destinationFolderPaths: Set = new Set(); // Add the output folder and all additional module kind output folders as destinations - const tsconfigOutDir: string | undefined = await this._getTsconfigOutDirAsync( - taskSession, - heftConfiguration, - typeScriptConfiguration - ); + const tsconfigOutDir: string | undefined = partialTsconfigFile?.compilerOptions?.outDir; if (tsconfigOutDir) { destinationFolderPaths.add(tsconfigOutDir); } - for (const emitModule of typeScriptConfiguration?.additionalModuleKindsToEmit || []) { - destinationFolderPaths.add(`${heftConfiguration.buildFolderPath}/${emitModule.outFolderName}`); + + for (const emitModule of typeScriptConfigurationJson?.additionalModuleKindsToEmit || []) { + destinationFolderPaths.add(emitModule.outFolderName); } copyOperations.push({ - ...typeScriptConfiguration?.staticAssetsToCopy, + ...staticAssetsConfig, // For now - these may need to be revised later sourcePath: path.resolve(heftConfiguration.buildFolderPath, 'src'), @@ -322,6 +337,7 @@ export default class TypeScriptPlugin implements IHeftTaskPlugin { hardlink: false }); } + return copyOperations; } @@ -329,15 +345,9 @@ export default class TypeScriptPlugin implements IHeftTaskPlugin { taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration ): Promise { - const terminal: ITerminal = taskSession.logger.terminal; - - const typeScriptConfigurationJson: ITypeScriptConfigurationJson | undefined = - await loadTypeScriptConfigurationFileAsync(heftConfiguration, terminal); - - const partialTsconfigFile: IPartialTsconfig | undefined = await loadPartialTsconfigFileAsync( - heftConfiguration, - terminal, - typeScriptConfigurationJson + const { typeScriptConfigurationJson, partialTsconfigFile } = await this._loadConfigAsync( + taskSession, + heftConfiguration ); if (!partialTsconfigFile) { @@ -346,25 +356,21 @@ export default class TypeScriptPlugin implements IHeftTaskPlugin { return false; } - const typeScriptToolPath: string = await heftConfiguration.rigPackageResolver.resolvePackageAsync( - 'typescript', - terminal - ); - // Build out the configuration const typeScriptBuilderConfiguration: ITypeScriptBuilderConfiguration = { buildFolderPath: heftConfiguration.buildFolderPath, - // Use tempFolderPath instead of cacheFolderPath. Running a clean will delete build outputs - // which the metadata file will imply are unchanged, causing typescript to avoid building - // these files. Since cleaning will delete files in the temp folder path, place it there. + // Build metadata is just another build output, but we put it in the temp folder because it will + // usually be discarded when published. buildMetadataFolderPath: taskSession.tempFolderPath, - typeScriptToolPath: typeScriptToolPath, + heftConfiguration, buildProjectReferences: typeScriptConfigurationJson?.buildProjectReferences, useTranspilerWorker: typeScriptConfigurationJson?.useTranspilerWorker, - tsconfigPath: getTsconfigFilePath(heftConfiguration, typeScriptConfigurationJson), + onlyResolveSymlinksInNodeModules: typeScriptConfigurationJson?.onlyResolveSymlinksInNodeModules, + + tsconfigPath: getTsconfigFilePath(heftConfiguration, typeScriptConfigurationJson?.project), additionalModuleKindsToEmit: typeScriptConfigurationJson?.additionalModuleKindsToEmit, emitCjsExtensionForCommonJS: !!typeScriptConfigurationJson?.emitCjsExtensionForCommonJS, emitMjsExtensionForESModule: !!typeScriptConfigurationJson?.emitMjsExtensionForESModule, @@ -385,16 +391,24 @@ export default class TypeScriptPlugin implements IHeftTaskPlugin { return typeScriptBuilder; } - private async _getTsconfigOutDirAsync( + private async _loadConfigAsync( taskSession: IHeftTaskSession, - heftConfiguration: HeftConfiguration, - typeScriptConfiguration: ITypeScriptConfigurationJson | undefined - ): Promise { + heftConfiguration: HeftConfiguration + ): Promise { + const terminal: ITerminal = taskSession.logger.terminal; + + const typeScriptConfigurationJson: ITypeScriptConfigurationJson | undefined = + await loadTypeScriptConfigurationFileAsync(heftConfiguration, terminal); + const partialTsconfigFile: IPartialTsconfig | undefined = await loadPartialTsconfigFileAsync( heftConfiguration, - taskSession.logger.terminal, - typeScriptConfiguration + terminal, + typeScriptConfigurationJson ); - return partialTsconfigFile?.compilerOptions?.outDir; + + return { + typeScriptConfigurationJson, + partialTsconfigFile + }; } } diff --git a/heft-plugins/heft-typescript-plugin/src/configureProgramForMultiEmit.ts b/heft-plugins/heft-typescript-plugin/src/configureProgramForMultiEmit.ts index 6cfbd7cfe6b..a9fe703e7ec 100644 --- a/heft-plugins/heft-typescript-plugin/src/configureProgramForMultiEmit.ts +++ b/heft-plugins/heft-typescript-plugin/src/configureProgramForMultiEmit.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. + import type * as TTypescript from 'typescript'; + import { InternalError } from '@rushstack/node-core-library'; import type { ExtendedTypeScript } from './internalTypings/TypeScriptInternals'; @@ -50,7 +52,9 @@ export function configureProgramForMultiEmit( // Attach the originals to the Program instance to avoid modifying the same Program twice. // Don't use WeakMap because this Program could theoretically get a { ... } applied to it. [INNER_GET_COMPILER_OPTIONS_SYMBOL]?: TTypescript.Program['getCompilerOptions']; - [INNER_EMIT_SYMBOL]?: TTypescript.Program['emit']; + [INNER_EMIT_SYMBOL]?: // https://github.com/microsoft/TypeScript/blob/88cb76d314a93937ce8d9543114ccbad993be6d1/src/compiler/program.ts#L2697-L2698 + // There is a "forceDtsEmit" parameter that is not on the published types. + (...args: [...Parameters, boolean | undefined]) => TTypescript.EmitResult; } const program: IProgramWithMultiEmit = innerProgram; @@ -117,7 +121,8 @@ export function configureProgramForMultiEmit( writeFile?: TTypescript.WriteFileCallback, cancellationToken?: TTypescript.CancellationToken, emitOnlyDtsFiles?: boolean, - customTransformers?: TTypescript.CustomTransformers + customTransformers?: TTypescript.CustomTransformers, + forceDtsEmit?: boolean ) => { if (emitOnlyDtsFiles) { return program[INNER_EMIT_SYMBOL]!( @@ -125,7 +130,8 @@ export function configureProgramForMultiEmit( writeFile, cancellationToken, emitOnlyDtsFiles, - customTransformers + customTransformers, + forceDtsEmit ); } @@ -150,7 +156,8 @@ export function configureProgramForMultiEmit( writeFile && wrapWriteFile(writeFile, moduleKindToEmit.jsExtensionOverride), cancellationToken, emitOnlyDtsFiles, - customTransformers + customTransformers, + forceDtsEmit ); emitSkipped = emitSkipped || flavorResult.emitSkipped; diff --git a/heft-plugins/heft-typescript-plugin/src/fileSystem/TypeScriptCachedFileSystem.ts b/heft-plugins/heft-typescript-plugin/src/fileSystem/TypeScriptCachedFileSystem.ts index 3e0b21f6b19..ca76573d285 100644 --- a/heft-plugins/heft-typescript-plugin/src/fileSystem/TypeScriptCachedFileSystem.ts +++ b/heft-plugins/heft-typescript-plugin/src/fileSystem/TypeScriptCachedFileSystem.ts @@ -4,15 +4,15 @@ import { Encoding, Text, - IFileSystemWriteFileOptions, - IFileSystemReadFileOptions, - IFileSystemCopyFileOptions, - IFileSystemDeleteFileOptions, - IFileSystemCreateLinkOptions, + type IFileSystemWriteFileOptions, + type IFileSystemReadFileOptions, + type IFileSystemCopyFileOptions, + type IFileSystemDeleteFileOptions, + type IFileSystemCreateLinkOptions, FileSystem, - FileSystemStats, + type FileSystemStats, Sort, - FolderItem + type FolderItem } from '@rushstack/node-core-library'; export interface IReadFolderFilesAndDirectoriesResult { @@ -141,13 +141,13 @@ export class TypeScriptCachedFileSystem { public getRealPath: (linkPath: string) => string = (linkPath: string) => { return this._withCaching( linkPath, - (linkPath: string) => { + (path: string) => { try { - return FileSystem.getRealPath(linkPath); + return FileSystem.getRealPath(path); } catch (e) { if (FileSystem.isNotExistError(e as Error)) { // TypeScript's ts.sys.realpath returns the path it's provided if that path doesn't exist - return linkPath; + return path; } else { throw e; } diff --git a/heft-plugins/heft-typescript-plugin/src/index.ts b/heft-plugins/heft-typescript-plugin/src/index.ts index 96d3b207522..4b727d4baa1 100644 --- a/heft-plugins/heft-typescript-plugin/src/index.ts +++ b/heft-plugins/heft-typescript-plugin/src/index.ts @@ -22,3 +22,18 @@ export { loadTypeScriptConfigurationFileAsync, loadPartialTsconfigFileAsync } from './TypeScriptPlugin'; + +export type { IBaseTypeScriptTool as _IBaseTypeScriptTool } from './TypeScriptBuilder'; +export { + loadTypeScriptToolAsync as _loadTypeScriptToolAsync, + type ILoadedTypeScriptTool as _ILoadedTypeScriptTool, + type ICompilerCapabilities as _ICompilerCapabilities, + type ILoadTypeScriptToolOptions as _ILoadTypeScriptToolOptions +} from './loadTypeScriptTool'; +export { + loadTsconfig as _loadTsconfig, + getTsconfigFilePath as _getTsconfigFilePath, + type ILoadTsconfigOptions as _ILoadTsconfigOptions +} from './tsconfigLoader'; +import type * as TTypeScript from 'typescript'; +export { TTypeScript as _TTypeScript }; diff --git a/heft-plugins/heft-typescript-plugin/src/internalTypings/TypeScriptInternals.ts b/heft-plugins/heft-typescript-plugin/src/internalTypings/TypeScriptInternals.ts index 5701e0f9ddb..40be5463261 100644 --- a/heft-plugins/heft-typescript-plugin/src/internalTypings/TypeScriptInternals.ts +++ b/heft-plugins/heft-typescript-plugin/src/internalTypings/TypeScriptInternals.ts @@ -9,6 +9,22 @@ export interface IExtendedSolutionBuilder invalidateProject(configFilePath: string, mode: 0 | 1 | 2): void; } +/** + * @internal + */ +export interface ITypeScriptNodeSystem extends TTypescript.System { + /** + * https://github.com/microsoft/TypeScript/blob/d85767abfd83880cea17cea70f9913e9c4496dcc/src/compiler/sys.ts#L1438 + */ + getAccessibleFileSystemEntries?: (folderPath: string) => { + files: string[]; + directories: string[]; + }; +} + +/** + * @internal + */ export interface IExtendedTypeScript { /** * https://github.com/microsoft/TypeScript/blob/5f597e69b2e3b48d788cb548df40bcb703c8adb1/src/compiler/performance.ts#L3 @@ -53,6 +69,20 @@ export interface IExtendedTypeScript { getNewLineCharacter(compilerOptions: TTypescript.CompilerOptions): string; + createCompilerHost( + options: TTypescript.CompilerOptions, + setParentNodes?: boolean, + system?: TTypescript.System + ): TTypescript.CompilerHost; + + createCompilerHostWorker( + options: TTypescript.CompilerOptions, + setParentNodes?: boolean, + system?: TTypescript.System + ): TTypescript.CompilerHost; + + combinePaths(path1: string, path2: string): string; + /** * https://github.com/microsoft/TypeScript/blob/782c09d783e006a697b4ba6d1e7ec2f718ce8393/src/compiler/utilities.ts#L6540 */ @@ -92,3 +122,14 @@ export interface IExtendedTypeScript { } export type ExtendedTypeScript = typeof TTypescript & IExtendedTypeScript; + +export type ExtendedBuilderProgram = TTypescript.BuilderProgram & { + /** + * Typescript 5.6+ + */ + state?: { changedFilesSet: Set }; + /** + * Typescript < 5.6 + */ + getState(): { changedFilesSet: Set }; +}; diff --git a/heft-plugins/heft-typescript-plugin/src/loadTypeScriptTool.ts b/heft-plugins/heft-typescript-plugin/src/loadTypeScriptTool.ts new file mode 100644 index 00000000000..5fb9ad86fb8 --- /dev/null +++ b/heft-plugins/heft-typescript-plugin/src/loadTypeScriptTool.ts @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import semver from 'semver'; + +import type { HeftConfiguration } from '@rushstack/heft'; +import type { ITerminal } from '@rushstack/terminal'; +import { type IPackageJson, JsonFile, RealNodeModulePathResolver } from '@rushstack/node-core-library'; + +import type { ExtendedTypeScript } from './internalTypings/TypeScriptInternals'; +import type { IBaseTypeScriptTool } from './TypeScriptBuilder'; + +const OLDEST_SUPPORTED_TS_MAJOR_VERSION: number = 2; +const OLDEST_SUPPORTED_TS_MINOR_VERSION: number = 9; + +const NEWEST_SUPPORTED_TS_MAJOR_VERSION: number = 5; +const NEWEST_SUPPORTED_TS_MINOR_VERSION: number = 8; + +/** + * @internal + */ +export interface ILoadedTypeScriptTool { + tool: IBaseTypeScriptTool; + typescriptVersion: string; + typescriptParsedVersion: semver.SemVer; + capabilities: ICompilerCapabilities; +} + +/** + * @internal + */ +export interface ICompilerCapabilities { + /** + * Support for incremental compilation via `ts.createIncrementalProgram()`. + * Introduced with TypeScript 3.6. + */ + incrementalProgram: boolean; + + /** + * Support for composite projects via `ts.createSolutionBuilder()`. + * Introduced with TypeScript 3.0. + */ + solutionBuilder: boolean; +} + +/** + * @internal + */ +export interface ILoadTypeScriptToolOptions { + terminal: ITerminal; + heftConfiguration: HeftConfiguration; + onlyResolveSymlinksInNodeModules?: boolean; + buildProjectReferences?: boolean; +} + +/** + * @internal + */ +export async function loadTypeScriptToolAsync( + options: ILoadTypeScriptToolOptions +): Promise { + const { terminal, heftConfiguration, buildProjectReferences, onlyResolveSymlinksInNodeModules } = options; + + const typeScriptToolPath: string = await heftConfiguration.rigPackageResolver.resolvePackageAsync( + 'typescript', + terminal + ); + + // Determine the compiler version + const compilerPackageJsonFilename: string = `${typeScriptToolPath}/package.json`; + const packageJson: IPackageJson = await JsonFile.loadAsync(compilerPackageJsonFilename); + const typescriptVersion: string = packageJson.version; + const typescriptParsedVersion: semver.SemVer | null = semver.parse(typescriptVersion); + if (!typescriptParsedVersion) { + throw new Error( + `Unable to parse version "${typescriptVersion}" for TypeScript compiler package in: ` + + compilerPackageJsonFilename + ); + } + + // Detect what features this compiler supports. Note that manually comparing major/minor numbers + // loosens the matching to accept prereleases such as "3.6.0-dev.20190530" + const capabilities: ICompilerCapabilities = { + incrementalProgram: false, + solutionBuilder: typescriptParsedVersion.major >= 3 + }; + + if ( + typescriptParsedVersion.major > 3 || + (typescriptParsedVersion.major === 3 && typescriptParsedVersion.minor >= 6) + ) { + capabilities.incrementalProgram = true; + } + + if (buildProjectReferences && !capabilities.solutionBuilder) { + throw new Error( + `Building project references requires TypeScript@>=3.0, but the current version is ${typescriptVersion}` + ); + } + + // Report a warning if the TypeScript version is too old/new. The current oldest supported version is + // TypeScript 2.9. Prior to that the "ts.getConfigFileParsingDiagnostics()" API is missing; more fixups + // would be required to deal with that. We won't do that work unless someone requests it. + if ( + typescriptParsedVersion.major < OLDEST_SUPPORTED_TS_MAJOR_VERSION || + (typescriptParsedVersion.major === OLDEST_SUPPORTED_TS_MAJOR_VERSION && + typescriptParsedVersion.minor < OLDEST_SUPPORTED_TS_MINOR_VERSION) + ) { + // We don't use writeWarningLine() here because, if the person wants to take their chances with + // a seemingly unsupported compiler, their build should be allowed to succeed. + terminal.writeLine( + `The TypeScript compiler version ${typescriptVersion} is very old` + + ` and has not been tested with Heft; it may not work correctly.` + ); + } else if ( + typescriptParsedVersion.major > NEWEST_SUPPORTED_TS_MAJOR_VERSION || + (typescriptParsedVersion.major === NEWEST_SUPPORTED_TS_MAJOR_VERSION && + typescriptParsedVersion.minor > NEWEST_SUPPORTED_TS_MINOR_VERSION) + ) { + terminal.writeLine( + `The TypeScript compiler version ${typescriptVersion} is newer` + + ' than the latest version that was tested with Heft ' + + `(${NEWEST_SUPPORTED_TS_MAJOR_VERSION}.${NEWEST_SUPPORTED_TS_MINOR_VERSION}); it may not work correctly.` + ); + } + + const ts: ExtendedTypeScript = require(typeScriptToolPath); + + let realpath: typeof ts.sys.realpath = ts.sys.realpath; + if (onlyResolveSymlinksInNodeModules) { + const resolver: RealNodeModulePathResolver = new RealNodeModulePathResolver(); + realpath = resolver.realNodeModulePath; + } + + return { + tool: { + ts, + system: { + ...ts.sys, + realpath + }, + typeScriptToolPath + }, + typescriptVersion, + typescriptParsedVersion, + capabilities + }; +} diff --git a/heft-plugins/heft-typescript-plugin/src/schemas/typescript.schema.json b/heft-plugins/heft-typescript-plugin/src/schemas/typescript.schema.json index dd2e15d5bad..d068b3149c8 100644 --- a/heft-plugins/heft-typescript-plugin/src/schemas/typescript.schema.json +++ b/heft-plugins/heft-typescript-plugin/src/schemas/typescript.schema.json @@ -13,7 +13,7 @@ }, "extends": { - "description": "Optionally specifies another JSON config file that this file extends from. This provides a way for standard settings to be shared across multiple projects.", + "description": "Optionally specifies another JSON config file that this file extends from. This provides a way for standard settings to be shared across multiple projects. To delete an inherited setting, set it to `null` in this file.", "type": "string" }, @@ -31,12 +31,22 @@ "outFolderName": { "type": "string", "pattern": "[^\\\\\\/]" + }, + + "emitModulePackageJson": { + "description": "If true, a package.json file will be written to the output folder with the appropriate \"type\" field for the specified module kind. This ensures that Node.js correctly interprets .js files in the output folder regardless of the nearest ancestor package.json \"type\" setting. Only valid for CommonJS, UMD, and ES module kinds.", + "type": "boolean" } }, "required": ["moduleKind", "outFolderName"] } }, + "onlyResolveSymlinksInNodeModules": { + "description": "If true, the TypeScript compiler will only resolve symlinks to their targets if the links are in a node_modules folder. This significantly reduces file system operations in typical usage.", + "type": "boolean" + }, + "emitCjsExtensionForCommonJS": { "description": "If true, emit CommonJS module output to the folder specified in the tsconfig \"outDir\" compiler option with the .cjs extension alongside (or instead of, if TSConfig specifies CommonJS) the default compilation output.", "type": "boolean" diff --git a/heft-plugins/heft-typescript-plugin/src/tsconfigLoader.ts b/heft-plugins/heft-typescript-plugin/src/tsconfigLoader.ts new file mode 100644 index 00000000000..3e3e3f94d46 --- /dev/null +++ b/heft-plugins/heft-typescript-plugin/src/tsconfigLoader.ts @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; + +import type * as TTypescript from 'typescript'; + +import { Path } from '@rushstack/node-core-library'; +import type { HeftConfiguration } from '@rushstack/heft'; + +import type { IBaseTypeScriptTool } from './TypeScriptBuilder'; + +/** + * @internal + */ +export interface ILoadTsconfigOptions { + tool: IBaseTypeScriptTool; + tsconfigPath: string; + tsCacheFilePath?: string; +} + +/** + * @internal + */ +export function getTsconfigFilePath( + heftConfiguration: HeftConfiguration, + tsconfigRelativePath: string | undefined +): string { + return Path.convertToSlashes( + // Use path.resolve because the path can start with `./` or `../` + path.resolve(heftConfiguration.buildFolderPath, tsconfigRelativePath ?? './tsconfig.json') + ); +} + +/** + * @internal + */ +export function loadTsconfig(options: ILoadTsconfigOptions): TTypescript.ParsedCommandLine { + const { + tool: { ts, system }, + tsconfigPath, + tsCacheFilePath + } = options; + const parsedConfigFile: ReturnType = ts.readConfigFile( + tsconfigPath, + system.readFile + ); + + const currentFolder: string = path.dirname(tsconfigPath); + const tsconfig: TTypescript.ParsedCommandLine = ts.parseJsonConfigFileContent( + parsedConfigFile.config, + { + fileExists: system.fileExists, + readFile: system.readFile, + readDirectory: system.readDirectory, + realpath: system.realpath, + useCaseSensitiveFileNames: true + }, + currentFolder, + /*existingOptions:*/ undefined, + tsconfigPath + ); + + if (tsconfig.options.incremental) { + tsconfig.options.tsBuildInfoFile = tsCacheFilePath; + } + + return tsconfig; +} diff --git a/heft-plugins/heft-typescript-plugin/src/types.ts b/heft-plugins/heft-typescript-plugin/src/types.ts index d577627e329..f4704105bb0 100644 --- a/heft-plugins/heft-typescript-plugin/src/types.ts +++ b/heft-plugins/heft-typescript-plugin/src/types.ts @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. + import type * as TTypescript from 'typescript'; export interface ITypescriptWorkerData { @@ -61,4 +62,10 @@ export interface ICachedEmitModuleKind { * Declarations are only emitted for the primary module kind. */ isPrimary: boolean; + + /** + * If true, a package.json with the appropriate "type" field will be written + * to the output folder after emit. + */ + emitModulePackageJson: boolean; } diff --git a/heft-plugins/heft-typescript-plugin/tsconfig.json b/heft-plugins/heft-typescript-plugin/tsconfig.json index 14f45ad644e..c3c96cb3fb7 100644 --- a/heft-plugins/heft-typescript-plugin/tsconfig.json +++ b/heft-plugins/heft-typescript-plugin/tsconfig.json @@ -1,8 +1,8 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "types": ["node"], + // TODO: Remove when the repo is updated to ES2020 "lib": ["ES2019"] } } diff --git a/heft-plugins/heft-vscode-extension-plugin/.npmignore b/heft-plugins/heft-vscode-extension-plugin/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/heft-plugins/heft-vscode-extension-plugin/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-vscode-extension-plugin/CHANGELOG.json b/heft-plugins/heft-vscode-extension-plugin/CHANGELOG.json new file mode 100644 index 00000000000..5b7dbcd92a2 --- /dev/null +++ b/heft-plugins/heft-vscode-extension-plugin/CHANGELOG.json @@ -0,0 +1,1030 @@ +{ + "name": "@rushstack/heft-vscode-extension-plugin", + "entries": [ + { + "version": "1.1.22", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.22", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "1.1.21", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.21", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "1.1.20", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.20", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "1.1.19", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.19", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "1.1.18", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.18", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "1.1.17", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "1.1.16", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.16", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "1.1.15", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.15", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "1.1.14", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.14", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "1.1.13", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "1.1.12", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.12", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "1.1.11", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "1.1.10", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "1.1.9", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "1.1.8", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "1.1.7", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "1.1.6", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "1.1.5", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.5", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "1.1.4", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.1.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "1.0.22", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.22", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "1.0.21", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.21", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "1.0.20", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.20", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "1.0.19", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.19", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "1.0.18", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.18", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "1.0.17", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.17", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "1.0.16", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.16", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "1.0.15", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.15", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "1.0.14", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.14", + "date": "Wed, 03 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.11`" + } + ] + } + }, + { + "version": "1.0.13", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.13", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.5` to `1.1.6`" + } + ] + } + }, + { + "version": "1.0.12", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.12", + "date": "Wed, 12 Nov 2025 01:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.9`" + } + ] + } + }, + { + "version": "1.0.11", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.11", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.4` to `1.1.5`" + } + ] + } + }, + { + "version": "1.0.10", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.10", + "date": "Tue, 11 Nov 2025 16:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.7`" + } + ] + } + }, + { + "version": "1.0.9", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.9", + "date": "Mon, 10 Nov 2025 16:12:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.6`" + } + ] + } + }, + { + "version": "1.0.8", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.8", + "date": "Tue, 04 Nov 2025 08:15:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.3` to `1.1.4`" + } + ] + } + }, + { + "version": "1.0.7", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.7", + "date": "Fri, 24 Oct 2025 11:22:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.4`" + } + ] + } + }, + { + "version": "1.0.6", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.6", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.2` to `1.1.3`" + } + ] + } + }, + { + "version": "1.0.5", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.5", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.1` to `1.1.2`" + } + ] + } + }, + { + "version": "1.0.4", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.4", + "date": "Tue, 14 Oct 2025 15:13:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.1`" + } + ] + } + }, + { + "version": "1.0.3", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.3", + "date": "Mon, 13 Oct 2025 15:13:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.11.0`" + } + ] + } + }, + { + "version": "1.0.2", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.2", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.0` to `1.1.1`" + } + ] + } + }, + { + "version": "1.0.1", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.1", + "date": "Fri, 03 Oct 2025 20:10:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.0.0` to `1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-vscode-extension-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.75.0` to `1.0.0`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/heft-vscode-extension-plugin_v0.2.8", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.9.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.5` to `0.75.0`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/heft-vscode-extension-plugin_v0.2.7", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.9.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.4` to `0.74.5`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/heft-vscode-extension-plugin_v0.2.6", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.9.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.3` to `0.74.4`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/heft-vscode-extension-plugin_v0.2.5", + "date": "Fri, 29 Aug 2025 00:08:01 GMT", + "comments": { + "patch": [ + { + "comment": "add extraPackagingFlags plugin option" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/heft-vscode-extension-plugin_v0.2.4", + "date": "Thu, 21 Aug 2025 00:12:45 GMT", + "comments": { + "patch": [ + { + "comment": "Add support for verifying extension signature." + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/heft-vscode-extension-plugin_v0.2.3", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.9.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.2` to `0.74.3`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/heft-vscode-extension-plugin_v0.2.2", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.9.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.1` to `0.74.2`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/heft-vscode-extension-plugin_v0.2.1", + "date": "Mon, 28 Jul 2025 15:11:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.9.2`" + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/heft-vscode-extension-plugin_v0.2.0", + "date": "Sat, 26 Jul 2025 00:12:22 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for generating extension manifest." + }, + { + "comment": "Add VSIX publish plugin." + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/heft-vscode-extension-plugin_v0.1.0", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "minor": [ + { + "comment": "Add new Heft plugin to package files into a VSIX file" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.9.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.6` to `0.74.1`" + } + ] + } + } + ] +} diff --git a/heft-plugins/heft-vscode-extension-plugin/CHANGELOG.md b/heft-plugins/heft-vscode-extension-plugin/CHANGELOG.md new file mode 100644 index 00000000000..a4b41ef8a25 --- /dev/null +++ b/heft-plugins/heft-vscode-extension-plugin/CHANGELOG.md @@ -0,0 +1,299 @@ +# Change Log - @rushstack/heft-vscode-extension-plugin + +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 1.1.22 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 1.1.21 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 1.1.20 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.1.19 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 1.1.18 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 1.1.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.1.16 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.1.15 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 1.1.14 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 1.1.13 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.1.12 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.1.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.1.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.1.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.1.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.1.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.1.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.1.5 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 1.1.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.1.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.1.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.1.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.1.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.0.22 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.0.21 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.0.20 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.0.19 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.0.18 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.0.17 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 1.0.16 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 1.0.15 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.0.14 +Wed, 03 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.0.13 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.0.12 +Wed, 12 Nov 2025 01:57:54 GMT + +_Version update only_ + +## 1.0.11 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.0.10 +Tue, 11 Nov 2025 16:13:26 GMT + +_Version update only_ + +## 1.0.9 +Mon, 10 Nov 2025 16:12:32 GMT + +_Version update only_ + +## 1.0.8 +Tue, 04 Nov 2025 08:15:15 GMT + +_Version update only_ + +## 1.0.7 +Fri, 24 Oct 2025 11:22:09 GMT + +_Version update only_ + +## 1.0.6 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.0.5 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.0.4 +Tue, 14 Oct 2025 15:13:22 GMT + +_Version update only_ + +## 1.0.3 +Mon, 13 Oct 2025 15:13:02 GMT + +_Version update only_ + +## 1.0.2 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 1.0.1 +Fri, 03 Oct 2025 20:10:00 GMT + +_Version update only_ + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.2.8 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.2.7 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.2.6 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.2.5 +Fri, 29 Aug 2025 00:08:01 GMT + +### Patches + +- add extraPackagingFlags plugin option + +## 0.2.4 +Thu, 21 Aug 2025 00:12:45 GMT + +### Patches + +- Add support for verifying extension signature. + +## 0.2.3 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.2.2 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 0.2.1 +Mon, 28 Jul 2025 15:11:56 GMT + +_Version update only_ + +## 0.2.0 +Sat, 26 Jul 2025 00:12:22 GMT + +### Minor changes + +- Add support for generating extension manifest. +- Add VSIX publish plugin. + +## 0.1.0 +Wed, 23 Jul 2025 20:55:57 GMT + +### Minor changes + +- Add new Heft plugin to package files into a VSIX file + diff --git a/heft-plugins/heft-vscode-extension-plugin/LICENSE b/heft-plugins/heft-vscode-extension-plugin/LICENSE new file mode 100644 index 00000000000..80caec73dd4 --- /dev/null +++ b/heft-plugins/heft-vscode-extension-plugin/LICENSE @@ -0,0 +1,24 @@ +@rushstack/heft-vscode-extension-plugin + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/heft-plugins/heft-vscode-extension-plugin/README.md b/heft-plugins/heft-vscode-extension-plugin/README.md new file mode 100644 index 00000000000..70ece6d3ffe --- /dev/null +++ b/heft-plugins/heft-vscode-extension-plugin/README.md @@ -0,0 +1,12 @@ +# @rushstack/heft-vscode-extension-plugin + +This is a Heft plugin for packaging Visual Studio Code extensions using the `vsce` tool. + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/heft-plugins/heft-vscode-extension-plugin/CHANGELOG.md) - Find + out what's new in the latest version +- [@rushstack/heft](https://www.npmjs.com/package/@rushstack/heft) - Heft is a config-driven toolchain that invokes popular tools such as TypeScript, ESLint, Jest, Webpack, and API Extractor. + +Heft is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/heft-plugins/heft-vscode-extension-plugin/config/rig.json b/heft-plugins/heft-vscode-extension-plugin/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/heft-plugins/heft-vscode-extension-plugin/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/heft-plugins/heft-vscode-extension-plugin/eslint.config.js b/heft-plugins/heft-vscode-extension-plugin/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/heft-plugins/heft-vscode-extension-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-vscode-extension-plugin/heft-plugin.json b/heft-plugins/heft-vscode-extension-plugin/heft-plugin.json new file mode 100644 index 00000000000..a4df3eb5b8b --- /dev/null +++ b/heft-plugins/heft-vscode-extension-plugin/heft-plugin.json @@ -0,0 +1,72 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", + "taskPlugins": [ + { + "pluginName": "vscode-extension-package-plugin", + "entryPoint": "./lib-commonjs/VSCodeExtensionPackagePlugin.js", + "parameterScope": "package" + }, + { + "pluginName": "vscode-extension-verify-signature-plugin", + "entryPoint": "./lib-commonjs/VSCodeExtensionVerifySignaturePlugin.js", + "parameterScope": "verify-signature", + "parameters": [ + { + "longName": "--vsix-path", + "parameterKind": "string", + "argumentName": "RELATIVE_PATH", + "description": "Use this parameter to control which VSIX file will be used for verifying signature.", + "required": true + }, + { + "longName": "--manifest-path", + "parameterKind": "string", + "argumentName": "RELATIVE_PATH", + "description": "Use this parameter to control which manifest file will be used for verifying signature.", + "required": true + }, + { + "longName": "--signature-path", + "parameterKind": "string", + "argumentName": "RELATIVE_PATH", + "description": "Use this parameter to control which signature file will be used for verifying signature.", + "required": true + } + ] + }, + { + "pluginName": "vscode-extension-publish-plugin", + "entryPoint": "./lib-commonjs/VSCodeExtensionPublishPlugin.js", + "parameterScope": "publish-vsix", + "parameters": [ + { + "longName": "--vsix-path", + "parameterKind": "string", + "argumentName": "RELATIVE_PATH", + "description": "Use this parameter to control which VSIX file will be used for publishing.", + "required": true + }, + { + "longName": "--manifest-path", + "parameterKind": "string", + "argumentName": "RELATIVE_PATH", + "description": "Use this parameter to control which manifest file will be used for publishing.", + "required": false + }, + { + "longName": "--signature-path", + "parameterKind": "string", + "argumentName": "RELATIVE_PATH", + "description": "Use this parameter to control which signature file will be used for publishing.", + "required": false + }, + { + "longName": "--publish-unsigned", + "parameterKind": "flag", + "description": "Use this parameter to control whether to publish unsigned.", + "required": false + } + ] + } + ] +} diff --git a/heft-plugins/heft-vscode-extension-plugin/package.json b/heft-plugins/heft-vscode-extension-plugin/package.json new file mode 100644 index 00000000000..bea75e4c3cc --- /dev/null +++ b/heft-plugins/heft-vscode-extension-plugin/package.json @@ -0,0 +1,49 @@ +{ + "name": "@rushstack/heft-vscode-extension-plugin", + "version": "1.1.22", + "description": "Heft plugin for building vscode extensions.", + "repository": { + "type": "git", + "url": "https://github.com/microsoft/rushstack.git", + "directory": "heft-plugins/heft-vscode-extension-plugin" + }, + "homepage": "https://rushstack.io/pages/heft/overview/", + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "start": "heft test --clean --watch", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "exports": { + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "peerDependencies": { + "@rushstack/heft": "^1.2.22" + }, + "dependencies": { + "@rushstack/node-core-library": "workspace:*", + "@rushstack/terminal": "workspace:*", + "@vscode/vsce": "3.2.1" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "sideEffects": false +} diff --git a/heft-plugins/heft-vscode-extension-plugin/src/VSCodeExtensionPackagePlugin.ts b/heft-plugins/heft-vscode-extension-plugin/src/VSCodeExtensionPackagePlugin.ts new file mode 100644 index 00000000000..3a1dd05eb9d --- /dev/null +++ b/heft-plugins/heft-vscode-extension-plugin/src/VSCodeExtensionPackagePlugin.ts @@ -0,0 +1,103 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import type { + HeftConfiguration, + IHeftTaskPlugin, + IHeftTaskSession, + IHeftTaskRunHookOptions +} from '@rushstack/heft'; +import type { IWaitForExitResult } from '@rushstack/node-core-library'; + +import { executeAndWaitAsync, vsceScriptPath } from './util'; + +interface IVSCodeExtensionPackagePluginOptions { + /** + * The folder where the unpacked VSIX files are located. + * This is typically the output folder of the VSCode extension build. + */ + unpackedFolderPath: string; + /** + * The path where the packaged VSIX file will be saved. + * This can be a directory or a full vsix file path. + * If a directory is provided, the VSIX file will be named based on the extension's `package.json` name and version. + */ + vsixPath: string; + /** + * The path where the generated manifest file will be saved. + * This manifest is used for signing the VS Code extension. + */ + manifestPath: string; + /** + * Additional flags to pass to the VSCE packaging command. + */ + extraPackagingFlags?: string[]; +} + +const PLUGIN_NAME: 'vscode-extension-package-plugin' = 'vscode-extension-package-plugin'; + +export default class VSCodeExtensionPackagePlugin + implements IHeftTaskPlugin +{ + public apply( + heftTaskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + pluginOptions: IVSCodeExtensionPackagePluginOptions + ): void { + heftTaskSession.hooks.run.tapPromise(PLUGIN_NAME, async (runOptions: IHeftTaskRunHookOptions) => { + const { unpackedFolderPath, vsixPath, manifestPath, extraPackagingFlags = [] } = pluginOptions; + const { buildFolderPath } = heftConfiguration; + const { + logger: { terminal } + } = heftTaskSession; + + terminal.writeLine(`Using VSCE script: ${vsceScriptPath}`); + + terminal.writeLine(`Packaging VSIX from ${unpackedFolderPath} to ${vsixPath}`); + const packageResult: IWaitForExitResult = await executeAndWaitAsync( + terminal, + 'node', + [ + vsceScriptPath, + 'package', + '--no-dependencies', + '--out', + path.resolve(vsixPath), + ...extraPackagingFlags + ], + { + currentWorkingDirectory: path.resolve(buildFolderPath, unpackedFolderPath) + } + ); + if (packageResult.exitCode !== 0) { + throw new Error(`VSIX packaging failed with exit code ${packageResult.exitCode}`); + } + terminal.writeLine('VSIX successfully packaged.'); + + terminal.writeLine(`Generating manifest at ${manifestPath}`); + const manifestResult: IWaitForExitResult = await executeAndWaitAsync( + terminal, + 'node', + [ + vsceScriptPath, + 'generate-manifest', + '--packagePath', + path.resolve(vsixPath), + '--out', + path.resolve(manifestPath) + ], + { + currentWorkingDirectory: buildFolderPath + } + ); + if (manifestResult.exitCode !== 0) { + throw new Error(`Manifest generation failed with exit code ${manifestResult.exitCode}`); + } + terminal.writeLine('Manifest successfully generated.'); + + terminal.writeLine(`VSIX package and manifest generation completed successfully.`); + }); + } +} diff --git a/heft-plugins/heft-vscode-extension-plugin/src/VSCodeExtensionPublishPlugin.ts b/heft-plugins/heft-vscode-extension-plugin/src/VSCodeExtensionPublishPlugin.ts new file mode 100644 index 00000000000..3f41f3cf0b6 --- /dev/null +++ b/heft-plugins/heft-vscode-extension-plugin/src/VSCodeExtensionPublishPlugin.ts @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import type { + HeftConfiguration, + IHeftTaskPlugin, + IHeftTaskSession, + IHeftTaskRunHookOptions, + CommandLineStringParameter, + CommandLineFlagParameter +} from '@rushstack/heft'; +import type { IWaitForExitResult } from '@rushstack/node-core-library'; + +import { executeAndWaitAsync, vsceScriptPath } from './util'; + +interface IVSCodeExtensionPublishPluginOptions {} + +const PLUGIN_NAME: 'vscode-extension-publish-plugin' = 'vscode-extension-publish-plugin'; + +const VSIX_PATH_PARAMETER_NAME: string = '--vsix-path'; +const MANIFEST_PATH_PARAMETER_NAME: string = '--manifest-path'; +const SIGNATURE_PATH_PARAMETER_NAME: string = '--signature-path'; +const PUBLISH_UNSIGNED_PARAMETER_NAME: string = '--publish-unsigned'; + +export default class VSCodeExtensionPublishPlugin + implements IHeftTaskPlugin +{ + public apply( + heftTaskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + pluginOptions: IVSCodeExtensionPublishPluginOptions + ): void { + const vsixPathParameter: CommandLineStringParameter = + heftTaskSession.parameters.getStringParameter(VSIX_PATH_PARAMETER_NAME); + const manifestPathParameter: CommandLineStringParameter = heftTaskSession.parameters.getStringParameter( + MANIFEST_PATH_PARAMETER_NAME + ); + const signaturePathParameter: CommandLineStringParameter = heftTaskSession.parameters.getStringParameter( + SIGNATURE_PATH_PARAMETER_NAME + ); + const publishUnsignedParameter: CommandLineFlagParameter = heftTaskSession.parameters.getFlagParameter( + PUBLISH_UNSIGNED_PARAMETER_NAME + ); + + const { + logger: { terminal } + } = heftTaskSession; + + // required parameters defined in heft-plugin.json + const vsixPath: string = vsixPathParameter.value!; + + // manifestPath and signaturePath are required if publishUnsigned is unset + const manifestPath: string | undefined = manifestPathParameter.value; + const signaturePath: string | undefined = signaturePathParameter.value; + const publishUnsigned: boolean = publishUnsignedParameter.value; + if (publishUnsigned) { + terminal.writeLine(`Publishing unsigned VSIX ${vsixPath}`); + } else { + if (!manifestPath || !signaturePath) { + throw new Error( + `The parameters "${MANIFEST_PATH_PARAMETER_NAME}" and "${SIGNATURE_PATH_PARAMETER_NAME}" are required for the VSCodeExtensionPublishPlugin.` + ); + } + } + + heftTaskSession.hooks.run.tapPromise(PLUGIN_NAME, async (runOptions: IHeftTaskRunHookOptions) => { + const { buildFolderPath } = heftConfiguration; + + terminal.writeLine(`Using VSCE script: ${vsceScriptPath}`); + terminal.writeLine(`Publishing VSIX ${vsixPath}`); + + let publishResult: IWaitForExitResult; + + if (publishUnsigned) { + publishResult = await executeAndWaitAsync( + terminal, + 'node', + [ + vsceScriptPath, + 'publish', + '--no-dependencies', + '--azure-credential', + '--packagePath', + path.resolve(vsixPath) + ], + { + currentWorkingDirectory: path.resolve(buildFolderPath) + } + ); + } else { + if (!manifestPath) { + throw new Error(`Missing manifest path for the VSCodeExtensionPublishPlugin.`); + } + if (!signaturePath) { + throw new Error(`Missing signature path for the VSCodeExtensionPublishPlugin.`); + } + publishResult = await executeAndWaitAsync( + terminal, + 'node', + [ + vsceScriptPath, + 'publish', + '--no-dependencies', + '--azure-credential', + '--packagePath', + path.resolve(vsixPath), + '--manifestPath', + path.resolve(manifestPath), + '--signaturePath', + path.resolve(signaturePath) + ], + { + currentWorkingDirectory: path.resolve(buildFolderPath) + } + ); + } + if (publishResult.exitCode !== 0) { + throw new Error(`VSIX publishing failed with exit code ${publishResult.exitCode}`); + } + terminal.writeLine('VSIX successfully published.'); + }); + } +} diff --git a/heft-plugins/heft-vscode-extension-plugin/src/VSCodeExtensionVerifySignaturePlugin.ts b/heft-plugins/heft-vscode-extension-plugin/src/VSCodeExtensionVerifySignaturePlugin.ts new file mode 100644 index 00000000000..1d462e2f64d --- /dev/null +++ b/heft-plugins/heft-vscode-extension-plugin/src/VSCodeExtensionVerifySignaturePlugin.ts @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import type { + HeftConfiguration, + IHeftTaskPlugin, + IHeftTaskSession, + IHeftTaskRunHookOptions, + CommandLineStringParameter +} from '@rushstack/heft'; +import type { IWaitForExitResult } from '@rushstack/node-core-library'; + +import { executeAndWaitAsync, vsceScriptPath } from './util'; + +interface IVSCodeExtensionVerifySignaturePluginOptions {} + +const PLUGIN_NAME: 'vscode-extension-verify-signature-plugin' = 'vscode-extension-verify-signature-plugin'; + +const VSIX_PATH_PARAMETER_NAME: string = '--vsix-path'; +const MANIFEST_PATH_PARAMETER_NAME: string = '--manifest-path'; +const SIGNATURE_PATH_PARAMETER_NAME: string = '--signature-path'; + +export default class VSCodeExtensionVerifySignaturePlugin + implements IHeftTaskPlugin +{ + public apply( + heftTaskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + pluginOptions: IVSCodeExtensionVerifySignaturePluginOptions + ): void { + const vsixPathParameter: CommandLineStringParameter = + heftTaskSession.parameters.getStringParameter(VSIX_PATH_PARAMETER_NAME); + const manifestPathParameter: CommandLineStringParameter = heftTaskSession.parameters.getStringParameter( + MANIFEST_PATH_PARAMETER_NAME + ); + const signaturePathParameter: CommandLineStringParameter = heftTaskSession.parameters.getStringParameter( + SIGNATURE_PATH_PARAMETER_NAME + ); + + // required parameters defined in heft-plugin.json + const vsixPath: string = vsixPathParameter.value!; + const manifestPath: string = manifestPathParameter.value!; + const signaturePath: string = signaturePathParameter.value!; + + heftTaskSession.hooks.run.tapPromise(PLUGIN_NAME, async (runOptions: IHeftTaskRunHookOptions) => { + const { buildFolderPath } = heftConfiguration; + const { + logger: { terminal } + } = heftTaskSession; + + terminal.writeLine(`Using VSCE script: ${vsceScriptPath}`); + terminal.writeLine(`Verifying signature ${vsixPath}`); + + const verifySignatureResult: IWaitForExitResult = await executeAndWaitAsync( + terminal, + 'node', + [ + vsceScriptPath, + 'verify-signature', + '--packagePath', + path.resolve(vsixPath), + '--manifestPath', + path.resolve(manifestPath), + '--signaturePath', + path.resolve(signaturePath) + ], + { + currentWorkingDirectory: path.resolve(buildFolderPath) + } + ); + if (verifySignatureResult.exitCode !== 0) { + throw new Error( + `VSIX signature verification failed with exit code ${verifySignatureResult.exitCode}` + ); + } + terminal.writeLine('Successfully verified VSIX signature.'); + }); + } +} diff --git a/heft-plugins/heft-vscode-extension-plugin/src/util.ts b/heft-plugins/heft-vscode-extension-plugin/src/util.ts new file mode 100644 index 00000000000..5c32a56c949 --- /dev/null +++ b/heft-plugins/heft-vscode-extension-plugin/src/util.ts @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { ChildProcess } from 'node:child_process'; +import * as path from 'node:path'; + +import { + Executable, + type IExecutableSpawnOptions, + type IWaitForExitResult +} from '@rushstack/node-core-library'; +import { TerminalStreamWritable, TerminalProviderSeverity, type ITerminal } from '@rushstack/terminal'; + +export async function executeAndWaitAsync( + terminal: ITerminal, + command: string, + args: string[], + options: Omit = {} +): Promise> { + const childProcess: ChildProcess = Executable.spawn(command, args, { + ...options, + stdio: [ + 'ignore', // stdin + 'pipe', // stdout + 'pipe' // stderr + ] + }); + childProcess.stdout?.pipe( + new TerminalStreamWritable({ + terminal, + severity: TerminalProviderSeverity.log + }) + ); + childProcess.stderr?.pipe( + new TerminalStreamWritable({ + terminal, + severity: TerminalProviderSeverity.error + }) + ); + const result: IWaitForExitResult = await Executable.waitForExitAsync(childProcess, { + encoding: 'utf8' + }); + return result; +} + +const vsceBasePackagePath: string = require.resolve('@vscode/vsce/package.json'); +export const vsceScriptPath: string = path.resolve(vsceBasePackagePath, '../vsce'); diff --git a/heft-plugins/heft-vscode-extension-plugin/tsconfig.json b/heft-plugins/heft-vscode-extension-plugin/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/heft-plugins/heft-vscode-extension-plugin/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/heft-plugins/heft-webpack4-plugin/.eslintrc.js b/heft-plugins/heft-webpack4-plugin/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/heft-plugins/heft-webpack4-plugin/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/heft-plugins/heft-webpack4-plugin/.npmignore b/heft-plugins/heft-webpack4-plugin/.npmignore index d8609ea6c48..f7a40e10213 100644 --- a/heft-plugins/heft-webpack4-plugin/.npmignore +++ b/heft-plugins/heft-webpack4-plugin/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,18 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE +# README.md +# LICENSE -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- - -# (Add your project-specific overrides here) -!/includes/** -!heft-plugin.json \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-webpack4-plugin/CHANGELOG.json b/heft-plugins/heft-webpack4-plugin/CHANGELOG.json index 4d7958ae743..a29ca678837 100644 --- a/heft-plugins/heft-webpack4-plugin/CHANGELOG.json +++ b/heft-plugins/heft-webpack4-plugin/CHANGELOG.json @@ -1,6 +1,3675 @@ { "name": "@rushstack/heft-webpack4-plugin", "entries": [ + { + "version": "1.2.23", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.23", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "1.2.22", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.22", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.22`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "1.2.21", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.21", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.21`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "1.2.20", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.20", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "1.2.19", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.19", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "1.2.18", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.18", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "1.2.17", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.17", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "1.2.16", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.16", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "1.2.15", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.15", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.15`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "1.2.14", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.14", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.14`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "1.2.13", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.13", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "1.2.12", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.12", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "1.2.11", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.11", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "1.2.10", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.10", + "date": "Thu, 02 Apr 2026 00:14:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.10`" + } + ] + } + }, + { + "version": "1.2.9", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "1.2.8", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "1.2.7", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "1.2.6", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "1.2.5", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "1.2.4", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "1.2.3", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "1.2.2", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "1.2.1", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "1.2.0", + "tag": "@rushstack/heft-webpack4-plugin_v1.2.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "1.1.14", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "1.1.13", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "1.1.12", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "1.1.11", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "1.1.10", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.10", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "1.1.9", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.9", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "1.1.8", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.8", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "1.1.7", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "1.1.6", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.6", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.5` to `1.1.6`" + } + ] + } + }, + { + "version": "1.1.5", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.4` to `1.1.5`" + } + ] + } + }, + { + "version": "1.1.4", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.4", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.3` to `1.1.4`" + } + ] + } + }, + { + "version": "1.1.3", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.2` to `1.1.3`" + } + ] + } + }, + { + "version": "1.1.2", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.1` to `1.1.2`" + } + ] + } + }, + { + "version": "1.1.1", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.1", + "date": "Wed, 08 Oct 2025 00:13:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.0` to `1.1.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-webpack4-plugin_v1.1.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.0.0` to `1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-webpack4-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.75.0` to `1.0.0`" + } + ] + } + }, + { + "version": "0.10.113", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.113", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.5` to `0.75.0`" + } + ] + } + }, + { + "version": "0.10.112", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.112", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.4` to `0.74.5`" + } + ] + } + }, + { + "version": "0.10.111", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.111", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.3` to `0.74.4`" + } + ] + } + }, + { + "version": "0.10.110", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.110", + "date": "Fri, 29 Aug 2025 00:08:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.5`" + } + ] + } + }, + { + "version": "0.10.109", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.109", + "date": "Tue, 26 Aug 2025 00:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.4`" + } + ] + } + }, + { + "version": "0.10.108", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.108", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.2` to `0.74.3`" + } + ] + } + }, + { + "version": "0.10.107", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.107", + "date": "Fri, 01 Aug 2025 00:12:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.1` to `0.74.2`" + } + ] + } + }, + { + "version": "0.10.106", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.106", + "date": "Sat, 26 Jul 2025 00:12:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.1`" + } + ] + } + }, + { + "version": "0.10.105", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.105", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.0` to `0.74.1`" + } + ] + } + }, + { + "version": "0.10.104", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.104", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.6` to `0.74.0`" + } + ] + } + }, + { + "version": "0.10.103", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.103", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.5` to `0.73.6`" + } + ] + } + }, + { + "version": "0.10.102", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.102", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.4` to `0.73.5`" + } + ] + } + }, + { + "version": "0.10.101", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.101", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.34`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.3` to `0.73.4`" + } + ] + } + }, + { + "version": "0.10.100", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.100", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.2` to `0.73.3`" + } + ] + } + }, + { + "version": "0.10.99", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.99", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.1` to `0.73.2`" + } + ] + } + }, + { + "version": "0.10.98", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.98", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.0` to `0.73.1`" + } + ] + } + }, + { + "version": "0.10.97", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.97", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.72.0` to `0.73.0`" + } + ] + } + }, + { + "version": "0.10.96", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.96", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.2` to `0.72.0`" + } + ] + } + }, + { + "version": "0.10.95", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.95", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.1` to `0.71.2`" + } + ] + } + }, + { + "version": "0.10.94", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.94", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.27`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.0` to `0.71.1`" + } + ] + } + }, + { + "version": "0.10.93", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.93", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.1` to `0.71.0`" + } + ] + } + }, + { + "version": "0.10.92", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.92", + "date": "Wed, 12 Mar 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.0` to `0.70.1`" + } + ] + } + }, + { + "version": "0.10.91", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.91", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.24`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.3` to `0.70.0`" + } + ] + } + }, + { + "version": "0.10.90", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.90", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.2` to `0.69.3`" + } + ] + } + }, + { + "version": "0.10.89", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.89", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.1` to `0.69.2`" + } + ] + } + }, + { + "version": "0.10.88", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.88", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.0` to `0.69.1`" + } + ] + } + }, + { + "version": "0.10.87", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.87", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.18` to `0.69.0`" + } + ] + } + }, + { + "version": "0.10.86", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.86", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.17` to `0.68.18`" + } + ] + } + }, + { + "version": "0.10.85", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.85", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.16` to `0.68.17`" + } + ] + } + }, + { + "version": "0.10.84", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.84", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.15` to `0.68.16`" + } + ] + } + }, + { + "version": "0.10.83", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.83", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.14` to `0.68.15`" + } + ] + } + }, + { + "version": "0.10.82", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.82", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.15`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.13` to `0.68.14`" + } + ] + } + }, + { + "version": "0.10.81", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.81", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.14`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.12` to `0.68.13`" + } + ] + } + }, + { + "version": "0.10.80", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.80", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.11` to `0.68.12`" + } + ] + } + }, + { + "version": "0.10.79", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.79", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.10` to `0.68.11`" + } + ] + } + }, + { + "version": "0.10.78", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.78", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.9` to `0.68.10`" + } + ] + } + }, + { + "version": "0.10.77", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.77", + "date": "Tue, 03 Dec 2024 16:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.8` to `0.68.9`" + } + ] + } + }, + { + "version": "0.10.76", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.76", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.7` to `0.68.8`" + } + ] + } + }, + { + "version": "0.10.75", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.75", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.6` to `0.68.7`" + } + ] + } + }, + { + "version": "0.10.74", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.74", + "date": "Thu, 24 Oct 2024 00:15:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.5` to `0.68.6`" + } + ] + } + }, + { + "version": "0.10.73", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.73", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.4` to `0.68.5`" + } + ] + } + }, + { + "version": "0.10.72", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.72", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.3` to `0.68.4`" + } + ] + } + }, + { + "version": "0.10.71", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.71", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.2` to `0.68.3`" + } + ] + } + }, + { + "version": "0.10.70", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.70", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.1` to `0.68.2`" + } + ] + } + }, + { + "version": "0.10.69", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.69", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.0` to `0.68.1`" + } + ] + } + }, + { + "version": "0.10.68", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.68", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.2` to `0.68.0`" + } + ] + } + }, + { + "version": "0.10.67", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.67", + "date": "Sat, 21 Sep 2024 00:10:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.0`" + } + ] + } + }, + { + "version": "0.10.66", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.66", + "date": "Fri, 13 Sep 2024 00:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.66`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.1` to `0.67.2`" + } + ] + } + }, + { + "version": "0.10.65", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.65", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.65`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.0` to `0.67.1`" + } + ] + } + }, + { + "version": "0.10.64", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.64", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.64`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.26` to `0.67.0`" + } + ] + } + }, + { + "version": "0.10.63", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.63", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.63`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.25` to `0.66.26`" + } + ] + } + }, + { + "version": "0.10.62", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.62", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.62`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.24` to `0.66.25`" + } + ] + } + }, + { + "version": "0.10.61", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.61", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.61`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.23` to `0.66.24`" + } + ] + } + }, + { + "version": "0.10.60", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.60", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.60`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.22` to `0.66.23`" + } + ] + } + }, + { + "version": "0.10.59", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.59", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.59`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.21` to `0.66.22`" + } + ] + } + }, + { + "version": "0.10.58", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.58", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.58`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.20` to `0.66.21`" + } + ] + } + }, + { + "version": "0.10.57", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.57", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.57`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.19` to `0.66.20`" + } + ] + } + }, + { + "version": "0.10.56", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.56", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.56`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.18` to `0.66.19`" + } + ] + } + }, + { + "version": "0.10.55", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.55", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.55`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.17` to `0.66.18`" + } + ] + } + }, + { + "version": "0.10.54", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.54", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.54`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.16` to `0.66.17`" + } + ] + } + }, + { + "version": "0.10.53", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.53", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.53`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.15` to `0.66.16`" + } + ] + } + }, + { + "version": "0.10.52", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.52", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.52`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.14` to `0.66.15`" + } + ] + } + }, + { + "version": "0.10.51", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.51", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.51`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.13` to `0.66.14`" + } + ] + } + }, + { + "version": "0.10.50", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.50", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.50`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.12` to `0.66.13`" + } + ] + } + }, + { + "version": "0.10.49", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.49", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.49`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.11` to `0.66.12`" + } + ] + } + }, + { + "version": "0.10.48", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.48", + "date": "Fri, 24 May 2024 00:15:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.48`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.10` to `0.66.11`" + } + ] + } + }, + { + "version": "0.10.47", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.47", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.47`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.9` to `0.66.10`" + } + ] + } + }, + { + "version": "0.10.46", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.46", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.46`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.8` to `0.66.9`" + } + ] + } + }, + { + "version": "0.10.45", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.45", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.45`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.7` to `0.66.8`" + } + ] + } + }, + { + "version": "0.10.44", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.44", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.44`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.6` to `0.66.7`" + } + ] + } + }, + { + "version": "0.10.43", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.43", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.43`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.5` to `0.66.6`" + } + ] + } + }, + { + "version": "0.10.42", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.42", + "date": "Wed, 08 May 2024 22:23:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.42`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.4` to `0.66.5`" + } + ] + } + }, + { + "version": "0.10.41", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.41", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.41`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.3` to `0.66.4`" + } + ] + } + }, + { + "version": "0.10.40", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.40", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.40`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.2` to `0.66.3`" + } + ] + } + }, + { + "version": "0.10.39", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.39", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.39`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.1` to `0.66.2`" + } + ] + } + }, + { + "version": "0.10.38", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.38", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.0` to `0.66.1`" + } + ] + } + }, + { + "version": "0.10.37", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.37", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.10` to `0.66.0`" + } + ] + } + }, + { + "version": "0.10.36", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.36", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.9` to `0.65.10`" + } + ] + } + }, + { + "version": "0.10.35", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.35", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.8` to `0.65.9`" + } + ] + } + }, + { + "version": "0.10.34", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.34", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.7` to `0.65.8`" + } + ] + } + }, + { + "version": "0.10.33", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.33", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.6` to `0.65.7`" + } + ] + } + }, + { + "version": "0.10.32", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.32", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.5` to `0.65.6`" + } + ] + } + }, + { + "version": "0.10.31", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.31", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.4` to `^0.65.5`" + } + ] + } + }, + { + "version": "0.10.30", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.30", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.3` to `^0.65.4`" + } + ] + } + }, + { + "version": "0.10.29", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.29", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.29`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.2` to `^0.65.3`" + } + ] + } + }, + { + "version": "0.10.28", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.28", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.1` to `^0.65.2`" + } + ] + } + }, + { + "version": "0.10.27", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.27", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.27`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.0` to `^0.65.1`" + } + ] + } + }, + { + "version": "0.10.26", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.26", + "date": "Tue, 20 Feb 2024 16:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.8` to `^0.65.0`" + } + ] + } + }, + { + "version": "0.10.25", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.25", + "date": "Mon, 19 Feb 2024 21:54:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.25`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.7` to `^0.64.8`" + } + ] + } + }, + { + "version": "0.10.24", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.24", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.24`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.6` to `^0.64.7`" + } + ] + } + }, + { + "version": "0.10.23", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.23", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.23`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.5` to `^0.64.6`" + } + ] + } + }, + { + "version": "0.10.22", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.22", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.4` to `^0.64.5`" + } + ] + } + }, + { + "version": "0.10.21", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.21", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.21`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.3` to `^0.64.4`" + } + ] + } + }, + { + "version": "0.10.20", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.20", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.20`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.2` to `^0.64.3`" + } + ] + } + }, + { + "version": "0.10.19", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.19", + "date": "Tue, 23 Jan 2024 20:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.19`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.1` to `^0.64.2`" + } + ] + } + }, + { + "version": "0.10.18", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.18", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.18`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.0` to `^0.64.1`" + } + ] + } + }, + { + "version": "0.10.17", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.17", + "date": "Tue, 16 Jan 2024 18:30:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.6` to `^0.64.0`" + } + ] + } + }, + { + "version": "0.10.16", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.16", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.16`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.5` to `^0.63.6`" + } + ] + } + }, + { + "version": "0.10.15", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.15", + "date": "Wed, 20 Dec 2023 01:09:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.4` to `^0.63.5`" + } + ] + } + }, + { + "version": "0.10.14", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.14`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.3` to `^0.63.4`" + } + ] + } + }, + { + "version": "0.10.13", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.2` to `^0.63.3`" + } + ] + } + }, + { + "version": "0.10.12", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.1` to `^0.63.2`" + } + ] + } + }, + { + "version": "0.10.11", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.0` to `^0.63.1`" + } + ] + } + }, + { + "version": "0.10.10", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.10", + "date": "Mon, 30 Oct 2023 23:36:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.3` to `^0.63.0`" + } + ] + } + }, + { + "version": "0.10.9", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.9", + "date": "Sun, 01 Oct 2023 02:56:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.2` to `^0.62.3`" + } + ] + } + }, + { + "version": "0.10.8", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.1` to `^0.62.2`" + } + ] + } + }, + { + "version": "0.10.7", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.0` to `^0.62.1`" + } + ] + } + }, + { + "version": "0.10.6", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.6", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.3` to `^0.62.0`" + } + ] + } + }, + { + "version": "0.10.5", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.2` to `^0.61.3`" + } + ] + } + }, + { + "version": "0.10.4", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.1` to `^0.61.2`" + } + ] + } + }, + { + "version": "0.10.3", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.0` to `^0.61.1`" + } + ] + } + }, + { + "version": "0.10.2", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.2", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.60.0` to `^0.61.0`" + } + ] + } + }, + { + "version": "0.10.1", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.1", + "date": "Tue, 19 Sep 2023 15:21:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.59.0` to `^0.60.0`" + } + ] + } + }, + { + "version": "0.10.0", + "tag": "@rushstack/heft-webpack4-plugin_v0.10.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.2` to `^0.59.0`" + } + ] + } + }, + { + "version": "0.9.1", + "tag": "@rushstack/heft-webpack4-plugin_v0.9.1", + "date": "Wed, 13 Sep 2023 00:32:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.56`" + } + ] + } + }, + { + "version": "0.9.0", + "tag": "@rushstack/heft-webpack4-plugin_v0.9.0", + "date": "Thu, 07 Sep 2023 03:35:42 GMT", + "comments": { + "minor": [ + { + "comment": "Update Webpack peerDependency to ~4.47.0 and removes the warning about Node /lib/index.d.ts", - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, "docModel": { "enabled": false }, + "dtsRollup": { "enabled": true, "betaTrimmedFilePath": "/dist/.d.ts" diff --git a/heft-plugins/heft-webpack4-plugin/config/heft.json b/heft-plugins/heft-webpack4-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-webpack4-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-webpack4-plugin/config/jest.config.json b/heft-plugins/heft-webpack4-plugin/config/jest.config.json index 4bb17bde3ee..d1749681d90 100644 --- a/heft-plugins/heft-webpack4-plugin/config/jest.config.json +++ b/heft-plugins/heft-webpack4-plugin/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "local-node-rig/profiles/default/config/jest.config.json" } diff --git a/heft-plugins/heft-webpack4-plugin/config/rig.json b/heft-plugins/heft-webpack4-plugin/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/heft-plugins/heft-webpack4-plugin/config/rig.json +++ b/heft-plugins/heft-webpack4-plugin/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/heft-plugins/heft-webpack4-plugin/eslint.config.js b/heft-plugins/heft-webpack4-plugin/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/heft-plugins/heft-webpack4-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-webpack4-plugin/heft-plugin.json b/heft-plugins/heft-webpack4-plugin/heft-plugin.json index 32756eb9b98..32f5ce328d0 100644 --- a/heft-plugins/heft-webpack4-plugin/heft-plugin.json +++ b/heft-plugins/heft-webpack4-plugin/heft-plugin.json @@ -1,11 +1,11 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-plugin.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", "taskPlugins": [ { "pluginName": "webpack4-plugin", - "entryPoint": "./lib/Webpack4Plugin", - "optionsSchema": "./lib/schemas/heft-webpack4-plugin.schema.json", + "entryPoint": "./lib-commonjs/Webpack4Plugin", + "optionsSchema": "./lib-commonjs/schemas/heft-webpack4-plugin.schema.json", "parameterScope": "webpack4", "parameters": [ diff --git a/heft-plugins/heft-webpack4-plugin/package.json b/heft-plugins/heft-webpack4-plugin/package.json index 33223842ebb..bc0b321e3b7 100644 --- a/heft-plugins/heft-webpack4-plugin/package.json +++ b/heft-plugins/heft-webpack4-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/heft-webpack4-plugin", - "version": "0.6.0", + "version": "1.2.23", "description": "Heft plugin for Webpack 4", "repository": { "type": "git", @@ -8,8 +8,33 @@ "directory": "heft-plugins/heft-webpack4-plugin" }, "homepage": "https://rushstack.io/pages/heft/overview/", - "main": "lib/index.js", - "types": "lib/index.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "license": "MIT", "scripts": { "build": "heft build --clean", @@ -23,9 +48,9 @@ } }, "peerDependencies": { - "@rushstack/heft": "^0.51.0", + "@rushstack/heft": "^1.2.22", "@types/webpack": "^4", - "webpack": "~4.44.2" + "webpack": "~4.47.0" }, "dependencies": { "@rushstack/debug-certificate-manager": "workspace:*", @@ -36,12 +61,13 @@ "webpack-dev-server": "~4.9.3" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", "@rushstack/heft": "workspace:*", - "@types/node": "14.18.36", + "@rushstack/terminal": "workspace:*", "@types/watchpack": "2.4.0", "@types/webpack": "4.41.32", - "webpack": "~4.44.2" - } + "eslint": "~9.37.0", + "local-node-rig": "workspace:*", + "webpack": "~4.47.0" + }, + "sideEffects": false } diff --git a/heft-plugins/heft-webpack4-plugin/src/Webpack4Plugin.ts b/heft-plugins/heft-webpack4-plugin/src/Webpack4Plugin.ts index a946354348a..644f585c8e8 100644 --- a/heft-plugins/heft-webpack4-plugin/src/Webpack4Plugin.ts +++ b/heft-plugins/heft-webpack4-plugin/src/Webpack4Plugin.ts @@ -1,10 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { AddressInfo } from 'net'; +import type { AddressInfo } from 'node:net'; + import type * as TWebpack from 'webpack'; import type TWebpackDevServer from 'webpack-dev-server'; import { AsyncParallelHook, AsyncSeriesBailHook, AsyncSeriesHook, type SyncBailHook } from 'tapable'; + import { CertificateManager, type ICertificate } from '@rushstack/debug-certificate-manager'; import { InternalError, LegacyAdapters } from '@rushstack/node-core-library'; import type { @@ -16,10 +18,15 @@ import type { IHeftTaskRunIncrementalHookOptions } from '@rushstack/heft'; -import type { IWebpackConfiguration, IWebpackPluginAccessor } from './shared'; -import { WebpackConfigurationLoader } from './WebpackConfigurationLoader'; import { - DeferredWatchFileSystem, + PLUGIN_NAME, + type IWebpackConfiguration, + type IWebpackPluginAccessor, + type IWebpackPluginAccessorHooks +} from './shared'; +import { tryLoadWebpackConfigurationAsync } from './WebpackConfigurationLoader'; +import { + type DeferredWatchFileSystem, type IWatchFileSystem, OverrideNodeWatchFSPlugin } from './DeferredWatchFileSystem'; @@ -53,20 +60,15 @@ type ExtendedMultiCompiler = TWebpack.MultiCompiler & { }; export interface IWebpackPluginOptions { - devConfigurationPath: string | undefined; - configurationPath: string | undefined; + devConfigurationPath?: string | undefined; + configurationPath?: string | undefined; } -/** - * @public - */ -export const PLUGIN_NAME: 'webpack4-plugin' = 'webpack4-plugin'; const SERVE_PARAMETER_LONG_NAME: '--serve' = '--serve'; const WEBPACK_PACKAGE_NAME: 'webpack' = 'webpack'; const WEBPACK_DEV_SERVER_PACKAGE_NAME: 'webpack-dev-server' = 'webpack-dev-server'; const WEBPACK_DEV_SERVER_ENV_VAR_NAME: 'WEBPACK_DEV_SERVER' = 'WEBPACK_DEV_SERVER'; const WEBPACK_DEV_MIDDLEWARE_PACKAGE_NAME: 'webpack-dev-middleware' = 'webpack-dev-middleware'; -const UNINITIALIZED: 'UNINITIALIZED' = 'UNINITIALIZED'; /** * @internal @@ -76,7 +78,7 @@ export default class Webpack4Plugin implements IHeftTaskPlugin | undefined; private _webpackCompilationDonePromiseResolveFn: (() => void) | undefined; private _watchFileSystems: Set | undefined; @@ -87,12 +89,7 @@ export default class Webpack4Plugin implements IHeftTaskPlugin void ): Promise { - if (this._webpackConfiguration === UNINITIALIZED) { - // Obtain the webpack configuration by calling into the hook. If undefined - // is returned, load the default Webpack configuration. - taskSession.logger.terminal.writeVerboseLine( - 'Attempting to load Webpack configuration via external plugins' - ); - let webpackConfiguration: IWebpackConfiguration | false | undefined = - await this.accessor.hooks.onLoadConfiguration.promise(); - if (webpackConfiguration === undefined) { - taskSession.logger.terminal.writeVerboseLine('Attempt to load the default Webpack configuration'); - const configurationLoader: WebpackConfigurationLoader = new WebpackConfigurationLoader( - taskSession.logger, - taskSession.parameters.production, - taskSession.parameters.watch && this._isServeMode - ); - webpackConfiguration = await configurationLoader.tryLoadWebpackConfigurationAsync({ - ...options, + if (this._webpackConfiguration === false) { + const webpackConfiguration: IWebpackConfiguration | undefined = await tryLoadWebpackConfigurationAsync( + { taskSession, heftConfiguration, + hooks: this.accessor.hooks, + serveMode: this._isServeMode, loadWebpackAsyncFn: this._loadWebpackAsync.bind(this) - }); - } + }, + options + ); - if (webpackConfiguration === false) { - taskSession.logger.terminal.writeLine('Webpack disabled by external plugin'); - this._webpackConfiguration = undefined; - } else if ( - webpackConfiguration === undefined || - (Array.isArray(webpackConfiguration) && webpackConfiguration.length === 0) - ) { - taskSession.logger.terminal.writeLine('No Webpack configuration found'); - this._webpackConfiguration = undefined; - } else { - if (this.accessor.hooks.onConfigure.isUsed()) { - // Allow for plugins to customise the configuration - await this.accessor.hooks.onConfigure.promise(webpackConfiguration); - } - if (this.accessor.hooks.onAfterConfigure.isUsed()) { - // Provide the finalized configuration - await this.accessor.hooks.onAfterConfigure.promise(webpackConfiguration); - } - this._webpackConfiguration = webpackConfiguration; - - if (requestRun) { - const overrideWatchFSPlugin: OverrideNodeWatchFSPlugin = new OverrideNodeWatchFSPlugin(requestRun); - this._watchFileSystems = overrideWatchFSPlugin.fileSystems; - for (const config of Array.isArray(webpackConfiguration) - ? webpackConfiguration - : [webpackConfiguration]) { - if (!config.plugins) { - config.plugins = [overrideWatchFSPlugin]; - } else { - config.plugins.unshift(overrideWatchFSPlugin); - } + if (webpackConfiguration && requestRun) { + const overrideWatchFSPlugin: OverrideNodeWatchFSPlugin = new OverrideNodeWatchFSPlugin(requestRun); + this._watchFileSystems = overrideWatchFSPlugin.fileSystems; + for (const config of Array.isArray(webpackConfiguration) + ? webpackConfiguration + : [webpackConfiguration]) { + if (!config.plugins) { + config.plugins = [overrideWatchFSPlugin]; + } else { + config.plugins.unshift(overrideWatchFSPlugin); } } } + + this._webpackConfiguration = webpackConfiguration; } + return this._webpackConfiguration; } @@ -496,3 +464,15 @@ export default class Webpack4Plugin implements IHeftTaskPlugin { + function createOptions(production: boolean, serveMode: boolean): IMockLoadWebpackConfigurationOptions { + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(false); + const terminal: ITerminal = new Terminal(terminalProvider); + const logger: IScopedLogger = new MockScopedLogger(terminal); + const buildFolderPath: string = __dirname; + + const parameters: Partial = { + production + }; + + const taskSession: IHeftTaskSession = { + logger, + parameters: parameters as unknown as IHeftParameters, + // Other values unused during these tests + hooks: undefined!, + parsedCommandLine: undefined!, + requestAccessToPluginByName: undefined!, + taskName: 'webpack', + tempFolderPath: `${__dirname}/temp` + }; + + const heftConfiguration: Partial = { + buildFolderPath + }; + + return { + taskSession, + heftConfiguration: heftConfiguration as unknown as HeftConfiguration, + hooks: _createAccessorHooks(), + loadWebpackAsyncFn: jest.fn(), + serveMode, + + _terminalProvider: terminalProvider, + _tryLoadConfigFileAsync: jest.fn() + }; + } + + it(`onLoadConfiguration can return false`, async () => { + const options: IMockLoadWebpackConfigurationOptions = createOptions(false, false); + + const onLoadConfiguration: jest.Mock = jest.fn(); + const onConfigure: jest.Mock = jest.fn(); + const onAfterConfigure: jest.Mock = jest.fn(); + + options.hooks.onLoadConfiguration.tap('test', onLoadConfiguration); + options.hooks.onConfigure.tap('test', onConfigure); + options.hooks.onAfterConfigure.tap('test', onAfterConfigure); + + onLoadConfiguration.mockReturnValue(false); + + const config: IWebpackConfiguration | undefined = + await WebpackConfigurationLoader.tryLoadWebpackConfigurationAsync(options, {}); + expect(config).toBeUndefined(); + expect(onLoadConfiguration).toHaveBeenCalledTimes(1); + expect(options._tryLoadConfigFileAsync).toHaveBeenCalledTimes(0); + expect(onConfigure).toHaveBeenCalledTimes(0); + expect(onAfterConfigure).toHaveBeenCalledTimes(0); + }); + + it(`calls tryLoadWebpackConfigurationFileAsync`, async () => { + const options: IMockLoadWebpackConfigurationOptions = createOptions(false, false); + + const onConfigure: jest.Mock = jest.fn(); + const onAfterConfigure: jest.Mock = jest.fn(); + + options.hooks.onConfigure.tap('test', onConfigure); + options.hooks.onAfterConfigure.tap('test', onAfterConfigure); + + options._tryLoadConfigFileAsync.mockReturnValue(Promise.resolve(undefined)); + + const config: IWebpackConfiguration | undefined = + await WebpackConfigurationLoader.tryLoadWebpackConfigurationAsync(options, {}); + expect(config).toBeUndefined(); + expect(options._tryLoadConfigFileAsync).toHaveBeenCalledTimes(1); + expect(onConfigure).toHaveBeenCalledTimes(0); + expect(onAfterConfigure).toHaveBeenCalledTimes(0); + }); + + it(`can fall back`, async () => { + const options: IMockLoadWebpackConfigurationOptions = createOptions(false, false); + + const onLoadConfiguration: jest.Mock = jest.fn(); + const onConfigure: jest.Mock = jest.fn(); + const onAfterConfigure: jest.Mock = jest.fn(); + + options.hooks.onLoadConfiguration.tap( + { name: 'test', stage: STAGE_LOAD_LOCAL_CONFIG + 1 }, + onLoadConfiguration + ); + options.hooks.onConfigure.tap('test', onConfigure); + options.hooks.onAfterConfigure.tap('test', onAfterConfigure); + + options._tryLoadConfigFileAsync.mockReturnValue(Promise.resolve(undefined)); + + const mockConfig: IWebpackConfiguration = {}; + onLoadConfiguration.mockReturnValue(mockConfig); + + const config: IWebpackConfiguration | undefined = + await WebpackConfigurationLoader.tryLoadWebpackConfigurationAsync(options, {}); + expect(config).toBe(mockConfig); + + expect(options._tryLoadConfigFileAsync).toHaveBeenCalledTimes(1); + expect(onLoadConfiguration).toHaveBeenCalledTimes(1); + expect(onConfigure).toHaveBeenCalledTimes(1); + expect(onAfterConfigure).toHaveBeenCalledTimes(1); + + expect(onConfigure).toHaveBeenCalledWith(mockConfig); + expect(onAfterConfigure).toHaveBeenCalledWith(mockConfig); + }); + + it(`respects hook order`, async () => { + const options: IMockLoadWebpackConfigurationOptions = createOptions(false, false); + + const onConfigure: jest.Mock = jest.fn(); + const onAfterConfigure: jest.Mock = jest.fn(); + + options.hooks.onConfigure.tap('test', onConfigure); + options.hooks.onAfterConfigure.tap('test', onAfterConfigure); + + const mockConfig: IWebpackConfiguration = {}; + + options._tryLoadConfigFileAsync.mockReturnValue(Promise.resolve(mockConfig)); + + const config: IWebpackConfiguration | undefined = + await WebpackConfigurationLoader.tryLoadWebpackConfigurationAsync(options, {}); + expect(config).toBe(mockConfig); + expect(options._tryLoadConfigFileAsync).toHaveBeenCalledTimes(1); + expect(onConfigure).toHaveBeenCalledTimes(1); + expect(onConfigure).toHaveBeenCalledWith(mockConfig); + expect(onAfterConfigure).toHaveBeenCalledTimes(1); + expect(onAfterConfigure).toHaveBeenCalledWith(mockConfig); + }); +}); diff --git a/heft-plugins/heft-webpack4-plugin/src/WebpackConfigurationLoader.ts b/heft-plugins/heft-webpack4-plugin/src/WebpackConfigurationLoader.ts index cb79f3d7720..cd1b0c36052 100644 --- a/heft-plugins/heft-webpack4-plugin/src/WebpackConfigurationLoader.ts +++ b/heft-plugins/heft-webpack4-plugin/src/WebpackConfigurationLoader.ts @@ -1,13 +1,21 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import type * as TWebpack from 'webpack'; + import { FileSystem } from '@rushstack/node-core-library'; -import type { IScopedLogger, IHeftTaskSession, HeftConfiguration } from '@rushstack/heft'; +import type { IHeftTaskSession, HeftConfiguration } from '@rushstack/heft'; import type { IWebpackPluginOptions } from './Webpack4Plugin'; -import type { IWebpackConfiguration, IWebpackConfigurationFnEnvironment } from './shared'; +import { + PLUGIN_NAME, + STAGE_LOAD_LOCAL_CONFIG, + type IWebpackConfiguration, + type IWebpackConfigurationFnEnvironment, + type IWebpackPluginAccessorHooks +} from './shared'; type IWebpackConfigJsExport = | TWebpack.Configuration @@ -18,101 +26,165 @@ type IWebpackConfigJsExport = | ((env: IWebpackConfigurationFnEnvironment) => Promise); type IWebpackConfigJs = IWebpackConfigJsExport | { default: IWebpackConfigJsExport }; -interface ILoadWebpackConfigurationOptions extends IWebpackPluginOptions { +/** + * @internal + */ +export interface ILoadWebpackConfigurationOptions { taskSession: IHeftTaskSession; heftConfiguration: HeftConfiguration; + serveMode: boolean; loadWebpackAsyncFn: () => Promise; + hooks: Pick; + + _tryLoadConfigFileAsync?: typeof tryLoadWebpackConfigurationFileAsync; } const DEFAULT_WEBPACK_CONFIG_PATH: './webpack.config.js' = './webpack.config.js'; const DEFAULT_WEBPACK_DEV_CONFIG_PATH: './webpack.dev.config.js' = './webpack.dev.config.js'; -export class WebpackConfigurationLoader { - private readonly _logger: IScopedLogger; - private readonly _production: boolean; - private readonly _serveMode: boolean; +/** + * @internal + */ +export async function tryLoadWebpackConfigurationAsync( + options: ILoadWebpackConfigurationOptions, + pluginOptions: IWebpackPluginOptions +): Promise { + const { taskSession, hooks, _tryLoadConfigFileAsync = tryLoadWebpackConfigurationFileAsync } = options; + const { logger } = taskSession; + const { terminal } = logger; + + // Apply default behavior. Due to the state of `this._webpackConfiguration`, this code + // will execute exactly once. + hooks.onLoadConfiguration.tapPromise( + { + name: PLUGIN_NAME, + stage: STAGE_LOAD_LOCAL_CONFIG + }, + async () => { + terminal.writeVerboseLine(`Attempting to load Webpack configuration from local file`); + const webpackConfiguration: IWebpackConfiguration | undefined = await _tryLoadConfigFileAsync( + options, + pluginOptions + ); + + if (webpackConfiguration) { + terminal.writeVerboseLine(`Loaded Webpack configuration from local file.`); + } - public constructor(logger: IScopedLogger, production: boolean, serveMode: boolean) { - this._logger = logger; - this._production = production; - this._serveMode = serveMode; + return webpackConfiguration; + } + ); + + // Obtain the webpack configuration by calling into the hook. + // The local configuration is loaded at STAGE_LOAD_LOCAL_CONFIG + terminal.writeVerboseLine('Attempting to load Webpack configuration'); + let webpackConfiguration: IWebpackConfiguration | false | undefined = + await hooks.onLoadConfiguration.promise(); + + if (webpackConfiguration === false) { + terminal.writeLine('Webpack disabled by external plugin'); + webpackConfiguration = undefined; + } else if ( + webpackConfiguration === undefined || + (Array.isArray(webpackConfiguration) && webpackConfiguration.length === 0) + ) { + terminal.writeLine('No Webpack configuration found'); + webpackConfiguration = undefined; + } else { + if (hooks.onConfigure.isUsed()) { + // Allow for plugins to customise the configuration + await hooks.onConfigure.promise(webpackConfiguration); + } + if (hooks.onAfterConfigure.isUsed()) { + // Provide the finalized configuration + await hooks.onAfterConfigure.promise(webpackConfiguration); + } } + return webpackConfiguration; +} - public async tryLoadWebpackConfigurationAsync( - options: ILoadWebpackConfigurationOptions - ): Promise { - // TODO: Eventually replace this custom logic with a call to this utility in in webpack-cli: - // https://github.com/webpack/webpack-cli/blob/next/packages/webpack-cli/lib/groups/ConfigGroup.js +/** + * @internal + */ +export async function tryLoadWebpackConfigurationFileAsync( + options: ILoadWebpackConfigurationOptions, + pluginOptions: IWebpackPluginOptions +): Promise { + // TODO: Eventually replace this custom logic with a call to this utility in in webpack-cli: + // https://github.com/webpack/webpack-cli/blob/next/packages/webpack-cli/lib/groups/ConfigGroup.js - const { taskSession, heftConfiguration, configurationPath, devConfigurationPath, loadWebpackAsyncFn } = - options; - let webpackConfigJs: IWebpackConfigJs | undefined; + const { taskSession, heftConfiguration, loadWebpackAsyncFn, serveMode } = options; + const { + logger, + parameters: { production } + } = taskSession; + const { terminal } = logger; + const { configurationPath, devConfigurationPath } = pluginOptions; + let webpackConfigJs: IWebpackConfigJs | undefined; - try { - const buildFolderPath: string = heftConfiguration.buildFolderPath; - if (this._serveMode) { - const devConfigPath: string = path.resolve( - buildFolderPath, - devConfigurationPath || DEFAULT_WEBPACK_DEV_CONFIG_PATH - ); - this._logger.terminal.writeVerboseLine( - `Attempting to load webpack configuration from "${devConfigPath}".` - ); - webpackConfigJs = await this._tryLoadWebpackConfigurationInnerAsync(devConfigPath); - } + try { + const buildFolderPath: string = heftConfiguration.buildFolderPath; + if (serveMode) { + const devConfigPath: string = path.resolve( + buildFolderPath, + devConfigurationPath || DEFAULT_WEBPACK_DEV_CONFIG_PATH + ); + terminal.writeVerboseLine(`Attempting to load webpack configuration from "${devConfigPath}".`); + webpackConfigJs = await _tryLoadWebpackConfigurationFileInnerAsync(devConfigPath); + } - if (!webpackConfigJs) { - const configPath: string = path.resolve( - buildFolderPath, - configurationPath || DEFAULT_WEBPACK_CONFIG_PATH - ); - this._logger.terminal.writeVerboseLine( - `Attempting to load webpack configuration from "${configPath}".` - ); - webpackConfigJs = await this._tryLoadWebpackConfigurationInnerAsync(configPath); - } - } catch (error) { - this._logger.emitError(error as Error); + if (!webpackConfigJs) { + const configPath: string = path.resolve( + buildFolderPath, + configurationPath || DEFAULT_WEBPACK_CONFIG_PATH + ); + terminal.writeVerboseLine(`Attempting to load webpack configuration from "${configPath}".`); + webpackConfigJs = await _tryLoadWebpackConfigurationFileInnerAsync(configPath); } + } catch (error) { + logger.emitError(error as Error); + } - if (webpackConfigJs) { - const webpackConfig: IWebpackConfigJsExport = - (webpackConfigJs as { default: IWebpackConfigJsExport }).default || webpackConfigJs; - - if (typeof webpackConfig === 'function') { - // Defer loading of webpack until we know for sure that we will need it - return webpackConfig({ - prod: this._production, - production: this._production, - taskSession, - heftConfiguration, - webpack: await loadWebpackAsyncFn() - }); - } else { - return webpackConfig; - } + if (webpackConfigJs) { + const webpackConfig: IWebpackConfigJsExport = + (webpackConfigJs as { default: IWebpackConfigJsExport }).default || webpackConfigJs; + + if (typeof webpackConfig === 'function') { + // Defer loading of webpack until we know for sure that we will need it + return webpackConfig({ + prod: production, + production, + taskSession, + heftConfiguration, + webpack: await loadWebpackAsyncFn() + }); } else { - return undefined; + return webpackConfig; } + } else { + return undefined; } +} - private async _tryLoadWebpackConfigurationInnerAsync( - configurationPath: string - ): Promise { - const configExists: boolean = await FileSystem.existsAsync(configurationPath); - if (configExists) { - try { - return await import(configurationPath); - } catch (e) { - const error: NodeJS.ErrnoException = e as NodeJS.ErrnoException; - if (error.code === 'ERR_MODULE_NOT_FOUND') { - // No configuration found, return undefined. - return undefined; - } - throw new Error(`Error loading webpack configuration at "${configurationPath}": ${e}`); +/** + * @internal + */ +export async function _tryLoadWebpackConfigurationFileInnerAsync( + configurationPath: string +): Promise { + const configExists: boolean = await FileSystem.existsAsync(configurationPath); + if (configExists) { + try { + return await import(configurationPath); + } catch (e) { + const error: NodeJS.ErrnoException = e as NodeJS.ErrnoException; + if (error.code === 'ERR_MODULE_NOT_FOUND') { + // No configuration found, return undefined. + return undefined; } - } else { - return undefined; + throw new Error(`Error loading webpack configuration at "${configurationPath}": ${e}`); } + } else { + return undefined; } } diff --git a/heft-plugins/heft-webpack4-plugin/src/index.ts b/heft-plugins/heft-webpack4-plugin/src/index.ts index 1f4915b77ab..0ad82466ea3 100644 --- a/heft-plugins/heft-webpack4-plugin/src/index.ts +++ b/heft-plugins/heft-webpack4-plugin/src/index.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -export { PLUGIN_NAME as PluginName } from './Webpack4Plugin'; +export { PLUGIN_NAME as PluginName, STAGE_LOAD_LOCAL_CONFIG } from './shared'; export type { IWebpackConfigurationWithDevServer, diff --git a/heft-plugins/heft-webpack4-plugin/src/shared.ts b/heft-plugins/heft-webpack4-plugin/src/shared.ts index f95ca694514..22589b3d54e 100644 --- a/heft-plugins/heft-webpack4-plugin/src/shared.ts +++ b/heft-plugins/heft-webpack4-plugin/src/shared.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +// eslint-disable-next-line import/order import type * as TWebpack from 'webpack'; // Compensate for webpack-dev-server referencing constructs from webpack 5 declare module 'webpack' { @@ -15,6 +16,7 @@ declare module 'webpack' { } import type { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server'; import type { AsyncParallelHook, AsyncSeriesBailHook, AsyncSeriesHook } from 'tapable'; + import type { IHeftTaskSession, HeftConfiguration } from '@rushstack/heft'; /** @@ -69,9 +71,9 @@ export type IWebpackConfiguration = IWebpackConfigurationWithDevServer | IWebpac export interface IWebpackPluginAccessorHooks { /** * A hook that allows for loading custom configurations used by the Webpack - * plugin. If a webpack configuration is provided, this will be populated automatically - * with the exports of the config file. If a webpack configuration is not provided, - * one will be loaded by the Webpack plugin. + * plugin. If a tap returns a value other than `undefined` before stage {@link STAGE_LOAD_LOCAL_CONFIG}, + * it will suppress loading from the webpack config file. To provide a fallback behavior in the + * absence of a local config file, tap this hook with a `stage` value greater than {@link STAGE_LOAD_LOCAL_CONFIG}. * * @remarks * Tapable event handlers can return `false` instead of `undefined` to suppress @@ -118,3 +120,15 @@ export interface IWebpackPluginAccessor { */ readonly parameters: IWebpackPluginAccessorParameters; } + +/** + * The stage in the `onLoadConfiguration` hook at which the config will be loaded from the local + * webpack config file. + * @public + */ +export const STAGE_LOAD_LOCAL_CONFIG: 1000 = 1000; + +/** + * @public + */ +export const PLUGIN_NAME: 'webpack4-plugin' = 'webpack4-plugin'; diff --git a/heft-plugins/heft-webpack4-plugin/tsconfig.json b/heft-plugins/heft-webpack4-plugin/tsconfig.json index 7512871fdbf..dac21d04081 100644 --- a/heft-plugins/heft-webpack4-plugin/tsconfig.json +++ b/heft-plugins/heft-webpack4-plugin/tsconfig.json @@ -1,7 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["node"] - } + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/heft-plugins/heft-webpack5-plugin/.eslintrc.js b/heft-plugins/heft-webpack5-plugin/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/heft-plugins/heft-webpack5-plugin/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/heft-plugins/heft-webpack5-plugin/.npmignore b/heft-plugins/heft-webpack5-plugin/.npmignore index d8609ea6c48..f7a40e10213 100644 --- a/heft-plugins/heft-webpack5-plugin/.npmignore +++ b/heft-plugins/heft-webpack5-plugin/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,18 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE +# README.md +# LICENSE -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- - -# (Add your project-specific overrides here) -!/includes/** -!heft-plugin.json \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/heft-plugins/heft-webpack5-plugin/CHANGELOG.json b/heft-plugins/heft-webpack5-plugin/CHANGELOG.json index 8f765344581..c4b4f5689e6 100644 --- a/heft-plugins/heft-webpack5-plugin/CHANGELOG.json +++ b/heft-plugins/heft-webpack5-plugin/CHANGELOG.json @@ -1,6 +1,3701 @@ { "name": "@rushstack/heft-webpack5-plugin", "entries": [ + { + "version": "1.3.23", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.23", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.21` to `1.2.22`" + } + ] + } + }, + { + "version": "1.3.22", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.22", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.22`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.20` to `1.2.21`" + } + ] + } + }, + { + "version": "1.3.21", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.21", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.21`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.19` to `1.2.20`" + } + ] + } + }, + { + "version": "1.3.20", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.20", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.18` to `1.2.19`" + } + ] + } + }, + { + "version": "1.3.19", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.19", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.17` to `1.2.18`" + } + ] + } + }, + { + "version": "1.3.18", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.18", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.16` to `1.2.17`" + } + ] + } + }, + { + "version": "1.3.17", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.17", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.15` to `1.2.16`" + } + ] + } + }, + { + "version": "1.3.16", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.16", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.14` to `1.2.15`" + } + ] + } + }, + { + "version": "1.3.15", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.15", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.15`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.13` to `1.2.14`" + } + ] + } + }, + { + "version": "1.3.14", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.14", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.14`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.12` to `1.2.13`" + } + ] + } + }, + { + "version": "1.3.13", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.13", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.11` to `1.2.12`" + } + ] + } + }, + { + "version": "1.3.12", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.12", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.10` to `1.2.11`" + } + ] + } + }, + { + "version": "1.3.11", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.11", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.9` to `1.2.10`" + } + ] + } + }, + { + "version": "1.3.10", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.10", + "date": "Thu, 02 Apr 2026 00:14:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.10`" + } + ] + } + }, + { + "version": "1.3.9", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.8` to `1.2.9`" + } + ] + } + }, + { + "version": "1.3.8", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.7` to `1.2.8`" + } + ] + } + }, + { + "version": "1.3.7", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.6` to `1.2.7`" + } + ] + } + }, + { + "version": "1.3.6", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.5` to `1.2.6`" + } + ] + } + }, + { + "version": "1.3.5", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.5`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.4` to `1.2.5`" + } + ] + } + }, + { + "version": "1.3.4", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.3` to `1.2.4`" + } + ] + } + }, + { + "version": "1.3.3", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.2` to `1.2.3`" + } + ] + } + }, + { + "version": "1.3.2", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.1` to `1.2.2`" + } + ] + } + }, + { + "version": "1.3.1", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.2.0` to `1.2.1`" + } + ] + } + }, + { + "version": "1.3.0", + "tag": "@rushstack/heft-webpack5-plugin_v1.3.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.14` to `1.2.0`" + } + ] + } + }, + { + "version": "1.2.14", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.13` to `1.1.14`" + } + ] + } + }, + { + "version": "1.2.13", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.12` to `1.1.13`" + } + ] + } + }, + { + "version": "1.2.12", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.11` to `1.1.12`" + } + ] + } + }, + { + "version": "1.2.11", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.10` to `1.1.11`" + } + ] + } + }, + { + "version": "1.2.10", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.10", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.9` to `1.1.10`" + } + ] + } + }, + { + "version": "1.2.9", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.9", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.8` to `1.1.9`" + } + ] + } + }, + { + "version": "1.2.8", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.8", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.7` to `1.1.8`" + } + ] + } + }, + { + "version": "1.2.7", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.6` to `1.1.7`" + } + ] + } + }, + { + "version": "1.2.6", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.6", + "date": "Fri, 21 Nov 2025 16:13:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.5` to `1.1.6`" + } + ] + } + }, + { + "version": "1.2.5", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.4` to `1.1.5`" + } + ] + } + }, + { + "version": "1.2.4", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.4", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.3` to `1.1.4`" + } + ] + } + }, + { + "version": "1.2.3", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.3`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.2` to `1.1.3`" + } + ] + } + }, + { + "version": "1.2.2", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.1` to `1.1.2`" + } + ] + } + }, + { + "version": "1.2.1", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.1", + "date": "Fri, 17 Oct 2025 23:22:33 GMT", + "comments": { + "patch": [ + { + "comment": "dev-server: add ipv6 loopback address support in terminal output" + }, + { + "comment": "add debug log when Webpack is imported from the rig package" + } + ] + } + }, + { + "version": "1.2.0", + "tag": "@rushstack/heft-webpack5-plugin_v1.2.0", + "date": "Wed, 08 Oct 2025 00:13:28 GMT", + "comments": { + "minor": [ + { + "comment": "Allow infrastructure logs to be printed." + }, + { + "comment": "Use project-level webpack dependency when it's installed." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.1.0` to `1.1.1`" + } + ] + } + }, + { + "version": "1.1.0", + "tag": "@rushstack/heft-webpack5-plugin_v1.1.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^1.0.0` to `1.1.0`" + } + ] + } + }, + { + "version": "1.0.0", + "tag": "@rushstack/heft-webpack5-plugin_v1.0.0", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "major": [ + { + "comment": "Release Heft version 1.0.0" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.75.0` to `1.0.0`" + } + ] + } + }, + { + "version": "0.11.43", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.43", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.5` to `0.75.0`" + } + ] + } + }, + { + "version": "0.11.42", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.42", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.4` to `0.74.5`" + } + ] + } + }, + { + "version": "0.11.41", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.41", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.3` to `0.74.4`" + } + ] + } + }, + { + "version": "0.11.40", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.40", + "date": "Fri, 29 Aug 2025 00:08:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.5`" + } + ] + } + }, + { + "version": "0.11.39", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.39", + "date": "Tue, 26 Aug 2025 00:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.4`" + } + ] + } + }, + { + "version": "0.11.38", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.38", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.2` to `0.74.3`" + } + ] + } + }, + { + "version": "0.11.37", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.37", + "date": "Fri, 01 Aug 2025 00:12:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.1` to `0.74.2`" + } + ] + } + }, + { + "version": "0.11.36", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.36", + "date": "Sat, 26 Jul 2025 00:12:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.1`" + } + ] + } + }, + { + "version": "0.11.35", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.35", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.74.0` to `0.74.1`" + } + ] + } + }, + { + "version": "0.11.34", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.34", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.6` to `0.74.0`" + } + ] + } + }, + { + "version": "0.11.33", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.33", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.5` to `0.73.6`" + } + ] + } + }, + { + "version": "0.11.32", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.32", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.4` to `0.73.5`" + } + ] + } + }, + { + "version": "0.11.31", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.31", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.34`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.3` to `0.73.4`" + } + ] + } + }, + { + "version": "0.11.30", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.30", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.2` to `0.73.3`" + } + ] + } + }, + { + "version": "0.11.29", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.29", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.1` to `0.73.2`" + } + ] + } + }, + { + "version": "0.11.28", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.28", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.73.0` to `0.73.1`" + } + ] + } + }, + { + "version": "0.11.27", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.27", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.72.0` to `0.73.0`" + } + ] + } + }, + { + "version": "0.11.26", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.26", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.2` to `0.72.0`" + } + ] + } + }, + { + "version": "0.11.25", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.25", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.1` to `0.71.2`" + } + ] + } + }, + { + "version": "0.11.24", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.24", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.27`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.71.0` to `0.71.1`" + } + ] + } + }, + { + "version": "0.11.23", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.23", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.1` to `0.71.0`" + } + ] + } + }, + { + "version": "0.11.22", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.22", + "date": "Wed, 12 Mar 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.70.0` to `0.70.1`" + } + ] + } + }, + { + "version": "0.11.21", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.21", + "date": "Tue, 11 Mar 2025 02:12:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.24`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.3` to `0.70.0`" + } + ] + } + }, + { + "version": "0.11.20", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.20", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.2` to `0.69.3`" + } + ] + } + }, + { + "version": "0.11.19", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.19", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.1` to `0.69.2`" + } + ] + } + }, + { + "version": "0.11.18", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.18", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.69.0` to `0.69.1`" + } + ] + } + }, + { + "version": "0.11.17", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.17", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.18` to `0.69.0`" + } + ] + } + }, + { + "version": "0.11.16", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.16", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.17` to `0.68.18`" + } + ] + } + }, + { + "version": "0.11.15", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.15", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.16` to `0.68.17`" + } + ] + } + }, + { + "version": "0.11.14", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.14", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.15` to `0.68.16`" + } + ] + } + }, + { + "version": "0.11.13", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.13", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.14` to `0.68.15`" + } + ] + } + }, + { + "version": "0.11.12", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.12", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.15`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.13` to `0.68.14`" + } + ] + } + }, + { + "version": "0.11.11", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.11", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.14`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.12` to `0.68.13`" + } + ] + } + }, + { + "version": "0.11.10", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.10", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.11` to `0.68.12`" + } + ] + } + }, + { + "version": "0.11.9", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.9", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.10` to `0.68.11`" + } + ] + } + }, + { + "version": "0.11.8", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.8", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.9` to `0.68.10`" + } + ] + } + }, + { + "version": "0.11.7", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.7", + "date": "Tue, 03 Dec 2024 16:11:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.8` to `0.68.9`" + } + ] + } + }, + { + "version": "0.11.6", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.6", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.7` to `0.68.8`" + } + ] + } + }, + { + "version": "0.11.5", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.5", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.8`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.6` to `0.68.7`" + } + ] + } + }, + { + "version": "0.11.4", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.4", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.5` to `0.68.6`" + } + ] + } + }, + { + "version": "0.11.3", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.3", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.4` to `0.68.5`" + } + ] + } + }, + { + "version": "0.11.2", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.2", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.3` to `0.68.4`" + } + ] + } + }, + { + "version": "0.11.1", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.1", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.2` to `0.68.3`" + } + ] + } + }, + { + "version": "0.11.0", + "tag": "@rushstack/heft-webpack5-plugin_v0.11.0", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "minor": [ + { + "comment": "Update the `webpack` peer dependency to `^5.82.1` from `~5.82.1`. Also bump `webpack-dev-server` to `^5.1.0`. This drops support for Node 16 and includes some breaking configuration changes. See https://github.com/webpack/webpack-dev-server/blob/master/migration-v5.md." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.1` to `0.68.2`" + } + ] + } + }, + { + "version": "0.10.14", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.14", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.68.0` to `0.68.1`" + } + ] + } + }, + { + "version": "0.10.13", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.13", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.2` to `0.68.0`" + } + ] + } + }, + { + "version": "0.10.12", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.12", + "date": "Sat, 21 Sep 2024 00:10:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.4.0`" + } + ] + } + }, + { + "version": "0.10.11", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.11", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.66`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.1` to `0.67.2`" + } + ] + } + }, + { + "version": "0.10.10", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.10", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.65`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.67.0` to `0.67.1`" + } + ] + } + }, + { + "version": "0.10.9", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.9", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.64`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.26` to `0.67.0`" + } + ] + } + }, + { + "version": "0.10.8", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.8", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.63`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.25` to `0.66.26`" + } + ] + } + }, + { + "version": "0.10.7", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.7", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.62`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.24` to `0.66.25`" + } + ] + } + }, + { + "version": "0.10.6", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.6", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.61`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.23` to `0.66.24`" + } + ] + } + }, + { + "version": "0.10.5", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.5", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.60`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.22` to `0.66.23`" + } + ] + } + }, + { + "version": "0.10.4", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.4", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.59`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.21` to `0.66.22`" + } + ] + } + }, + { + "version": "0.10.3", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.3", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.58`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.20` to `0.66.21`" + } + ] + } + }, + { + "version": "0.10.2", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.2", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.57`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.19` to `0.66.20`" + } + ] + } + }, + { + "version": "0.10.1", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.1", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.56`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.18` to `0.66.19`" + } + ] + } + }, + { + "version": "0.10.0", + "tag": "@rushstack/heft-webpack5-plugin_v0.10.0", + "date": "Fri, 07 Jun 2024 15:10:25 GMT", + "comments": { + "minor": [ + { + "comment": "Add `onGetWatchOptions` accessor hook to allow cooperating plugins to, for example, configure a list of globs for the file watcher to ignore." + } + ] + } + }, + { + "version": "0.9.55", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.55", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.55`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.17` to `0.66.18`" + } + ] + } + }, + { + "version": "0.9.54", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.54", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.54`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.16` to `0.66.17`" + } + ] + } + }, + { + "version": "0.9.53", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.53", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.53`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.15` to `0.66.16`" + } + ] + } + }, + { + "version": "0.9.52", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.52", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.52`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.14` to `0.66.15`" + } + ] + } + }, + { + "version": "0.9.51", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.51", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.51`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.13` to `0.66.14`" + } + ] + } + }, + { + "version": "0.9.50", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.50", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.50`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.12` to `0.66.13`" + } + ] + } + }, + { + "version": "0.9.49", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.49", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.49`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.11` to `0.66.12`" + } + ] + } + }, + { + "version": "0.9.48", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.48", + "date": "Fri, 24 May 2024 00:15:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.48`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.10` to `0.66.11`" + } + ] + } + }, + { + "version": "0.9.47", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.47", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.47`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.9` to `0.66.10`" + } + ] + } + }, + { + "version": "0.9.46", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.46", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.46`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.8` to `0.66.9`" + } + ] + } + }, + { + "version": "0.9.45", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.45", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.45`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.7` to `0.66.8`" + } + ] + } + }, + { + "version": "0.9.44", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.44", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.44`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.6` to `0.66.7`" + } + ] + } + }, + { + "version": "0.9.43", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.43", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.43`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.5` to `0.66.6`" + } + ] + } + }, + { + "version": "0.9.42", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.42", + "date": "Wed, 08 May 2024 22:23:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.42`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.4` to `0.66.5`" + } + ] + } + }, + { + "version": "0.9.41", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.41", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.41`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.3` to `0.66.4`" + } + ] + } + }, + { + "version": "0.9.40", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.40", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.40`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.2` to `0.66.3`" + } + ] + } + }, + { + "version": "0.9.39", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.39", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.39`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.1` to `0.66.2`" + } + ] + } + }, + { + "version": "0.9.38", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.38", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.66.0` to `0.66.1`" + } + ] + } + }, + { + "version": "0.9.37", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.37", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.10` to `0.66.0`" + } + ] + } + }, + { + "version": "0.9.36", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.36", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.9` to `0.65.10`" + } + ] + } + }, + { + "version": "0.9.35", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.35", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.8` to `0.65.9`" + } + ] + } + }, + { + "version": "0.9.34", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.34", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.7` to `0.65.8`" + } + ] + } + }, + { + "version": "0.9.33", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.33", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.6` to `0.65.7`" + } + ] + } + }, + { + "version": "0.9.32", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.32", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.5` to `0.65.6`" + } + ] + } + }, + { + "version": "0.9.31", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.31", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.4` to `^0.65.5`" + } + ] + } + }, + { + "version": "0.9.30", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.30", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.3` to `^0.65.4`" + } + ] + } + }, + { + "version": "0.9.29", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.29", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.29`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.2` to `^0.65.3`" + } + ] + } + }, + { + "version": "0.9.28", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.28", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.1` to `^0.65.2`" + } + ] + } + }, + { + "version": "0.9.27", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.27", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.27`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.65.0` to `^0.65.1`" + } + ] + } + }, + { + "version": "0.9.26", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.26", + "date": "Tue, 20 Feb 2024 16:10:53 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.8` to `^0.65.0`" + } + ] + } + }, + { + "version": "0.9.25", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.25", + "date": "Mon, 19 Feb 2024 21:54:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.25`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.7` to `^0.64.8`" + } + ] + } + }, + { + "version": "0.9.24", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.24", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.24`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.6` to `^0.64.7`" + } + ] + } + }, + { + "version": "0.9.23", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.23", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.23`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.5` to `^0.64.6`" + } + ] + } + }, + { + "version": "0.9.22", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.22", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.4` to `^0.64.5`" + } + ] + } + }, + { + "version": "0.9.21", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.21", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.21`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.3` to `^0.64.4`" + } + ] + } + }, + { + "version": "0.9.20", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.20", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.20`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.2` to `^0.64.3`" + } + ] + } + }, + { + "version": "0.9.19", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.19", + "date": "Tue, 23 Jan 2024 20:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.19`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.1` to `^0.64.2`" + } + ] + } + }, + { + "version": "0.9.18", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.18", + "date": "Tue, 23 Jan 2024 16:15:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.18`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.64.0` to `^0.64.1`" + } + ] + } + }, + { + "version": "0.9.17", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.17", + "date": "Tue, 16 Jan 2024 18:30:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.6` to `^0.64.0`" + } + ] + } + }, + { + "version": "0.9.16", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.16", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.16`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.5` to `^0.63.6`" + } + ] + } + }, + { + "version": "0.9.15", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.15", + "date": "Wed, 20 Dec 2023 01:09:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.4` to `^0.63.5`" + } + ] + } + }, + { + "version": "0.9.14", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.14`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.3` to `^0.63.4`" + } + ] + } + }, + { + "version": "0.9.13", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.2` to `^0.63.3`" + } + ] + } + }, + { + "version": "0.9.12", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.1` to `^0.63.2`" + } + ] + } + }, + { + "version": "0.9.11", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.63.0` to `^0.63.1`" + } + ] + } + }, + { + "version": "0.9.10", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.10", + "date": "Mon, 30 Oct 2023 23:36:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.3` to `^0.63.0`" + } + ] + } + }, + { + "version": "0.9.9", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.9", + "date": "Sun, 01 Oct 2023 02:56:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.2` to `^0.62.3`" + } + ] + } + }, + { + "version": "0.9.8", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.1` to `^0.62.2`" + } + ] + } + }, + { + "version": "0.9.7", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.62.0` to `^0.62.1`" + } + ] + } + }, + { + "version": "0.9.6", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.6", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.3` to `^0.62.0`" + } + ] + } + }, + { + "version": "0.9.5", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.2` to `^0.61.3`" + } + ] + } + }, + { + "version": "0.9.4", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.1` to `^0.61.2`" + } + ] + } + }, + { + "version": "0.9.3", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.61.0` to `^0.61.1`" + } + ] + } + }, + { + "version": "0.9.2", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.2", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.60.0` to `^0.61.0`" + } + ] + } + }, + { + "version": "0.9.1", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.1", + "date": "Tue, 19 Sep 2023 15:21:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.59.0` to `^0.60.0`" + } + ] + } + }, + { + "version": "0.9.0", + "tag": "@rushstack/heft-webpack5-plugin_v0.9.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.2` to `^0.59.0`" + } + ] + } + }, + { + "version": "0.8.15", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.15", + "date": "Wed, 13 Sep 2023 00:32:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.56`" + } + ] + } + }, + { + "version": "0.8.14", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.14", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.55`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.1` to `^0.58.2`" + } + ] + } + }, + { + "version": "0.8.13", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.13", + "date": "Mon, 31 Jul 2023 15:19:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.54`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.21`" + } + ] + } + }, + { + "version": "0.8.12", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.12", + "date": "Sat, 29 Jul 2023 00:22:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.53`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.58.0` to `^0.58.1`" + } + ] + } + }, + { + "version": "0.8.11", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.11", + "date": "Thu, 20 Jul 2023 20:47:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.52`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.57.1` to `^0.58.0`" + } + ] + } + }, + { + "version": "0.8.10", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.10", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.51`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.57.0` to `^0.57.1`" + } + ] + } + }, + { + "version": "0.8.9", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.9", + "date": "Fri, 14 Jul 2023 15:20:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.50`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.17`" + } + ] + } + }, + { + "version": "0.8.8", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.8", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.49`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.3` to `^0.57.0`" + } + ] + } + }, + { + "version": "0.8.7", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.7", + "date": "Wed, 12 Jul 2023 15:20:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.48`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.2` to `^0.56.3`" + } + ] + } + }, + { + "version": "0.8.6", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.6", + "date": "Wed, 12 Jul 2023 00:23:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.47`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.14`" + } + ] + } + }, + { + "version": "0.8.5", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.5", + "date": "Fri, 07 Jul 2023 00:19:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.46`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.1` to `^0.56.2`" + } + ] + } + }, + { + "version": "0.8.4", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.4", + "date": "Thu, 06 Jul 2023 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.45`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.56.0` to `^0.56.1`" + } + ] + } + }, + { + "version": "0.8.3", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.3", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.44`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.11`" + } + ] + } + }, + { + "version": "0.8.2", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.2", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.43`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.2` to `^0.56.0`" + } + ] + } + }, + { + "version": "0.8.1", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.1", + "date": "Thu, 15 Jun 2023 00:21:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.42`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.1` to `^0.55.2`" + } + ] + } + }, + { + "version": "0.8.0", + "tag": "@rushstack/heft-webpack5-plugin_v0.8.0", + "date": "Wed, 14 Jun 2023 00:19:41 GMT", + "comments": { + "minor": [ + { + "comment": "Move loading of webpack config file into the `onLoadConfiguration` hook to allow other plugins to define fallback behavior, rather than only overriding the config file." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.41`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.55.0` to `^0.55.1`" + } + ] + } + }, + { + "version": "0.7.10", + "tag": "@rushstack/heft-webpack5-plugin_v0.7.10", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.40`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.54.0` to `^0.55.0`" + } + ] + } + }, + { + "version": "0.7.9", + "tag": "@rushstack/heft-webpack5-plugin_v0.7.9", + "date": "Tue, 13 Jun 2023 01:49:01 GMT", + "comments": { + "patch": [ + { + "comment": "Bump webpack to v5.82.1" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.39`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.53.1` to `^0.54.0`" + } + ] + } + }, + { + "version": "0.7.8", + "tag": "@rushstack/heft-webpack5-plugin_v0.7.8", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.53.0` to `^0.53.1`" + } + ] + } + }, + { + "version": "0.7.7", + "tag": "@rushstack/heft-webpack5-plugin_v0.7.7", + "date": "Fri, 09 Jun 2023 15:23:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.4`" + } + ] + } + }, + { + "version": "0.7.6", + "tag": "@rushstack/heft-webpack5-plugin_v0.7.6", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.2` to `^0.53.0`" + } + ] + } + }, + { + "version": "0.7.5", + "tag": "@rushstack/heft-webpack5-plugin_v0.7.5", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.1` to `^0.52.2`" + } + ] + } + }, + { + "version": "0.7.4", + "tag": "@rushstack/heft-webpack5-plugin_v0.7.4", + "date": "Thu, 08 Jun 2023 00:20:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.52.0` to `^0.52.1`" + } + ] + } + }, + { + "version": "0.7.3", + "tag": "@rushstack/heft-webpack5-plugin_v0.7.3", + "date": "Wed, 07 Jun 2023 22:45:16 GMT", + "comments": { + "patch": [ + { + "comment": "Improve the error message when the \"--serve\" is incorrectly specified" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.33`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" from `^0.51.0` to `^0.52.0`" + } + ] + } + }, + { + "version": "0.7.2", + "tag": "@rushstack/heft-webpack5-plugin_v0.7.2", + "date": "Tue, 06 Jun 2023 02:52:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.1.0`" + } + ] + } + }, + { + "version": "0.7.1", + "tag": "@rushstack/heft-webpack5-plugin_v0.7.1", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/debug-certificate-manager\" to `1.2.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.0.1`" + } + ] + } + }, { "version": "0.7.0", "tag": "@rushstack/heft-webpack5-plugin_v0.7.0", diff --git a/heft-plugins/heft-webpack5-plugin/CHANGELOG.md b/heft-plugins/heft-webpack5-plugin/CHANGELOG.md index 30454aedabf..2325cea31f3 100644 --- a/heft-plugins/heft-webpack5-plugin/CHANGELOG.md +++ b/heft-plugins/heft-webpack5-plugin/CHANGELOG.md @@ -1,6 +1,948 @@ # Change Log - @rushstack/heft-webpack5-plugin -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 1.3.23 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 1.3.22 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 1.3.21 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.3.20 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 1.3.19 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 1.3.18 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.3.17 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.3.16 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 1.3.15 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 1.3.14 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.3.13 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.3.12 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.3.11 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.3.10 +Thu, 02 Apr 2026 00:14:38 GMT + +_Version update only_ + +## 1.3.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.3.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.3.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.3.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.3.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 1.3.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.3.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.3.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.3.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.3.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.2.14 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.2.13 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.2.12 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.2.11 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.2.10 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.2.9 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 1.2.8 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 1.2.7 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.2.6 +Fri, 21 Nov 2025 16:13:55 GMT + +_Version update only_ + +## 1.2.5 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.2.4 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 1.2.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.2.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.2.1 +Fri, 17 Oct 2025 23:22:33 GMT + +### Patches + +- dev-server: add ipv6 loopback address support in terminal output +- add debug log when Webpack is imported from the rig package + +## 1.2.0 +Wed, 08 Oct 2025 00:13:28 GMT + +### Minor changes + +- Allow infrastructure logs to be printed. +- Use project-level webpack dependency when it's installed. + +## 1.1.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 1.0.0 +Tue, 30 Sep 2025 23:57:45 GMT + +### Breaking changes + +- Release Heft version 1.0.0 + +## 0.11.43 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.11.42 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.11.41 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.11.40 +Fri, 29 Aug 2025 00:08:01 GMT + +_Version update only_ + +## 0.11.39 +Tue, 26 Aug 2025 00:12:57 GMT + +_Version update only_ + +## 0.11.38 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.11.37 +Fri, 01 Aug 2025 00:12:48 GMT + +_Version update only_ + +## 0.11.36 +Sat, 26 Jul 2025 00:12:22 GMT + +_Version update only_ + +## 0.11.35 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.11.34 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.11.33 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.11.32 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.11.31 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.11.30 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.11.29 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.11.28 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.11.27 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 0.11.26 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 0.11.25 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.11.24 +Tue, 25 Mar 2025 15:11:15 GMT + +_Version update only_ + +## 0.11.23 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.11.22 +Wed, 12 Mar 2025 00:11:32 GMT + +_Version update only_ + +## 0.11.21 +Tue, 11 Mar 2025 02:12:34 GMT + +_Version update only_ + +## 0.11.20 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.11.19 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.11.18 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.11.17 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 0.11.16 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.11.15 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.11.14 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.11.13 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 0.11.12 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.11.11 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.11.10 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.11.9 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.11.8 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.11.7 +Tue, 03 Dec 2024 16:11:08 GMT + +_Version update only_ + +## 0.11.6 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.11.5 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.11.4 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.11.3 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.11.2 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.11.1 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.11.0 +Wed, 02 Oct 2024 00:11:19 GMT + +### Minor changes + +- Update the `webpack` peer dependency to `^5.82.1` from `~5.82.1`. Also bump `webpack-dev-server` to `^5.1.0`. This drops support for Node 16 and includes some breaking configuration changes. See https://github.com/webpack/webpack-dev-server/blob/master/migration-v5.md. + +## 0.10.14 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.10.13 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.10.12 +Sat, 21 Sep 2024 00:10:27 GMT + +_Version update only_ + +## 0.10.11 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.10.10 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.10.9 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.10.8 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.10.7 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.10.6 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.10.5 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 0.10.4 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 0.10.3 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.10.2 +Tue, 16 Jul 2024 00:36:21 GMT + +_Version update only_ + +## 0.10.1 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.10.0 +Fri, 07 Jun 2024 15:10:25 GMT + +### Minor changes + +- Add `onGetWatchOptions` accessor hook to allow cooperating plugins to, for example, configure a list of globs for the file watcher to ignore. + +## 0.9.55 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.9.54 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.9.53 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 0.9.52 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.9.51 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.9.50 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.9.49 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.9.48 +Fri, 24 May 2024 00:15:08 GMT + +_Version update only_ + +## 0.9.47 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 0.9.46 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.9.45 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.9.44 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.9.43 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.9.42 +Wed, 08 May 2024 22:23:50 GMT + +_Version update only_ + +## 0.9.41 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 0.9.40 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.9.39 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 0.9.38 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 0.9.37 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 0.9.36 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 0.9.35 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 0.9.34 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 0.9.33 +Thu, 29 Feb 2024 07:11:45 GMT + +_Version update only_ + +## 0.9.32 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.9.31 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.9.30 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.9.29 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.9.28 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.9.27 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.9.26 +Tue, 20 Feb 2024 16:10:53 GMT + +_Version update only_ + +## 0.9.25 +Mon, 19 Feb 2024 21:54:26 GMT + +_Version update only_ + +## 0.9.24 +Sat, 17 Feb 2024 06:24:35 GMT + +_Version update only_ + +## 0.9.23 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 0.9.22 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.9.21 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.9.20 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.9.19 +Tue, 23 Jan 2024 20:12:57 GMT + +_Version update only_ + +## 0.9.18 +Tue, 23 Jan 2024 16:15:06 GMT + +_Version update only_ + +## 0.9.17 +Tue, 16 Jan 2024 18:30:11 GMT + +_Version update only_ + +## 0.9.16 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.9.15 +Wed, 20 Dec 2023 01:09:45 GMT + +_Version update only_ + +## 0.9.14 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.9.13 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.9.12 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 0.9.11 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 0.9.10 +Mon, 30 Oct 2023 23:36:37 GMT + +_Version update only_ + +## 0.9.9 +Sun, 01 Oct 2023 02:56:30 GMT + +_Version update only_ + +## 0.9.8 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 0.9.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.9.6 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 0.9.5 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 0.9.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.9.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 0.9.2 +Fri, 22 Sep 2023 00:05:50 GMT + +_Version update only_ + +## 0.9.1 +Tue, 19 Sep 2023 15:21:52 GMT + +_Version update only_ + +## 0.9.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 0.8.15 +Wed, 13 Sep 2023 00:32:29 GMT + +_Version update only_ + +## 0.8.14 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 0.8.13 +Mon, 31 Jul 2023 15:19:05 GMT + +_Version update only_ + +## 0.8.12 +Sat, 29 Jul 2023 00:22:51 GMT + +_Version update only_ + +## 0.8.11 +Thu, 20 Jul 2023 20:47:28 GMT + +_Version update only_ + +## 0.8.10 +Wed, 19 Jul 2023 00:20:31 GMT + +_Version update only_ + +## 0.8.9 +Fri, 14 Jul 2023 15:20:45 GMT + +_Version update only_ + +## 0.8.8 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 0.8.7 +Wed, 12 Jul 2023 15:20:39 GMT + +_Version update only_ + +## 0.8.6 +Wed, 12 Jul 2023 00:23:29 GMT + +_Version update only_ + +## 0.8.5 +Fri, 07 Jul 2023 00:19:32 GMT + +_Version update only_ + +## 0.8.4 +Thu, 06 Jul 2023 00:16:19 GMT + +_Version update only_ + +## 0.8.3 +Tue, 04 Jul 2023 00:18:47 GMT + +_Version update only_ + +## 0.8.2 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 0.8.1 +Thu, 15 Jun 2023 00:21:02 GMT + +_Version update only_ + +## 0.8.0 +Wed, 14 Jun 2023 00:19:41 GMT + +### Minor changes + +- Move loading of webpack config file into the `onLoadConfiguration` hook to allow other plugins to define fallback behavior, rather than only overriding the config file. + +## 0.7.10 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 0.7.9 +Tue, 13 Jun 2023 01:49:01 GMT + +### Patches + +- Bump webpack to v5.82.1 + +## 0.7.8 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 0.7.7 +Fri, 09 Jun 2023 15:23:15 GMT + +_Version update only_ + +## 0.7.6 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 0.7.5 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 0.7.4 +Thu, 08 Jun 2023 00:20:02 GMT + +_Version update only_ + +## 0.7.3 +Wed, 07 Jun 2023 22:45:16 GMT + +### Patches + +- Improve the error message when the "--serve" is incorrectly specified + +## 0.7.2 +Tue, 06 Jun 2023 02:52:51 GMT + +_Version update only_ + +## 0.7.1 +Mon, 05 Jun 2023 21:45:21 GMT + +_Version update only_ ## 0.7.0 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/heft-plugins/heft-webpack5-plugin/config/api-extractor.json b/heft-plugins/heft-webpack5-plugin/config/api-extractor.json index 74590d3c4f8..5f6b2655ac8 100644 --- a/heft-plugins/heft-webpack5-plugin/config/api-extractor.json +++ b/heft-plugins/heft-webpack5-plugin/config/api-extractor.json @@ -1,14 +1,11 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json", - "mainEntryPointFilePath": "/lib/index.d.ts", - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, "docModel": { "enabled": false }, + "dtsRollup": { "enabled": true, "betaTrimmedFilePath": "/dist/.d.ts" diff --git a/heft-plugins/heft-webpack5-plugin/config/heft.json b/heft-plugins/heft-webpack5-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-webpack5-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-webpack5-plugin/config/jest.config.json b/heft-plugins/heft-webpack5-plugin/config/jest.config.json index 4bb17bde3ee..d1749681d90 100644 --- a/heft-plugins/heft-webpack5-plugin/config/jest.config.json +++ b/heft-plugins/heft-webpack5-plugin/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "local-node-rig/profiles/default/config/jest.config.json" } diff --git a/heft-plugins/heft-webpack5-plugin/config/rig.json b/heft-plugins/heft-webpack5-plugin/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/heft-plugins/heft-webpack5-plugin/config/rig.json +++ b/heft-plugins/heft-webpack5-plugin/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/heft-plugins/heft-webpack5-plugin/eslint.config.js b/heft-plugins/heft-webpack5-plugin/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/heft-plugins/heft-webpack5-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-webpack5-plugin/heft-plugin.json b/heft-plugins/heft-webpack5-plugin/heft-plugin.json index 600430966f8..165299e71d3 100644 --- a/heft-plugins/heft-webpack5-plugin/heft-plugin.json +++ b/heft-plugins/heft-webpack5-plugin/heft-plugin.json @@ -1,11 +1,11 @@ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft-plugin.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", "taskPlugins": [ { "pluginName": "webpack5-plugin", - "entryPoint": "./lib/Webpack5Plugin", - "optionsSchema": "./lib/schemas/heft-webpack5-plugin.schema.json", + "entryPoint": "./lib-commonjs/Webpack5Plugin", + "optionsSchema": "./lib-commonjs/schemas/heft-webpack5-plugin.schema.json", "parameterScope": "webpack5", "parameters": [ diff --git a/heft-plugins/heft-webpack5-plugin/package.json b/heft-plugins/heft-webpack5-plugin/package.json index f78e7000f4d..0fdd6ad1952 100644 --- a/heft-plugins/heft-webpack5-plugin/package.json +++ b/heft-plugins/heft-webpack5-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/heft-webpack5-plugin", - "version": "0.7.0", + "version": "1.3.23", "description": "Heft plugin for Webpack 5", "repository": { "type": "git", @@ -8,8 +8,33 @@ "directory": "heft-plugins/heft-webpack5-plugin" }, "homepage": "https://rushstack.io/pages/heft/overview/", - "main": "lib/index.js", - "types": "dist/heft-webpack5-plugin.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/heft-webpack5-plugin.d.ts", + "exports": { + ".": { + "types": "./dist/heft-webpack5-plugin.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "license": "MIT", "scripts": { "build": "heft build --clean", @@ -18,8 +43,8 @@ "_phase:test": "heft run --only test -- --clean" }, "peerDependencies": { - "@rushstack/heft": "^0.51.0", - "webpack": "~5.80.0" + "@rushstack/heft": "^1.2.22", + "webpack": "^5.82.1" }, "dependencies": { "@rushstack/debug-certificate-manager": "workspace:*", @@ -27,14 +52,15 @@ "@types/tapable": "1.0.6", "tapable": "1.1.3", "watchpack": "2.4.0", - "webpack-dev-server": "~4.9.3" + "webpack-dev-server": "^5.1.0" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", - "@types/node": "14.18.36", + "@rushstack/terminal": "workspace:*", "@types/watchpack": "2.4.0", - "webpack": "~5.80.0" - } + "eslint": "~9.37.0", + "local-node-rig": "workspace:*", + "webpack": "~5.105.2" + }, + "sideEffects": false } diff --git a/heft-plugins/heft-webpack5-plugin/src/DeferredWatchFileSystem.ts b/heft-plugins/heft-webpack5-plugin/src/DeferredWatchFileSystem.ts index 1d8a567acbf..302e367ed9b 100644 --- a/heft-plugins/heft-webpack5-plugin/src/DeferredWatchFileSystem.ts +++ b/heft-plugins/heft-webpack5-plugin/src/DeferredWatchFileSystem.ts @@ -2,10 +2,10 @@ // See LICENSE in the project root for license information. import Watchpack, { type WatchOptions } from 'watchpack'; -import type { Compiler, WebpackPluginInstance } from 'webpack'; +import type { Compiler, WebpackPluginInstance, InputFileSystem } from 'webpack'; -export type InputFileSystem = Compiler['inputFileSystem']; -export type WatchFileSystem = Compiler['watchFileSystem']; +export type { InputFileSystem }; +export type WatchFileSystem = NonNullable; export type WatchCallback = Parameters[5]; export type WatchUndelayedCallback = Parameters[6]; export type Watcher = ReturnType; @@ -79,7 +79,7 @@ export class DeferredWatchFileSystem implements WatchFileSystem { const { fileTimeInfoEntries, contextTimeInfoEntries } = this._fetchTimeInfo(); - callback(undefined, fileTimeInfoEntries, contextTimeInfoEntries, changes, removals); + callback(null, fileTimeInfoEntries, contextTimeInfoEntries, changes, removals); changes.clear(); removals.clear(); @@ -204,8 +204,13 @@ export class OverrideNodeWatchFSPlugin implements WebpackPluginInstance { } public apply(compiler: Compiler): void { + const { inputFileSystem } = compiler; + if (!inputFileSystem) { + throw new Error(`compiler.inputFileSystem is not defined`); + } + const watchFileSystem: DeferredWatchFileSystem = new DeferredWatchFileSystem( - compiler.inputFileSystem, + inputFileSystem, this._onChange ); this.fileSystems.add(watchFileSystem); diff --git a/heft-plugins/heft-webpack5-plugin/src/Webpack5Plugin.ts b/heft-plugins/heft-webpack5-plugin/src/Webpack5Plugin.ts index 77eaf1ab5fe..59bf0c4b725 100644 --- a/heft-plugins/heft-webpack5-plugin/src/Webpack5Plugin.ts +++ b/heft-plugins/heft-webpack5-plugin/src/Webpack5Plugin.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { AddressInfo } from 'net'; +import type { AddressInfo } from 'node:net'; import type * as TWebpack from 'webpack'; import type TWebpackDevServer from 'webpack-dev-server'; -import { AsyncParallelHook, AsyncSeriesBailHook, AsyncSeriesHook } from 'tapable'; +import { AsyncParallelHook, AsyncSeriesBailHook, AsyncSeriesHook, AsyncSeriesWaterfallHook } from 'tapable'; import { CertificateManager, type ICertificate } from '@rushstack/debug-certificate-manager'; import { FileError, InternalError, LegacyAdapters } from '@rushstack/node-core-library'; @@ -18,25 +18,24 @@ import type { IHeftTaskRunIncrementalHookOptions } from '@rushstack/heft'; -import type { IWebpackConfiguration, IWebpackPluginAccessor } from './shared'; -import { WebpackConfigurationLoader } from './WebpackConfigurationLoader'; -import { DeferredWatchFileSystem, OverrideNodeWatchFSPlugin } from './DeferredWatchFileSystem'; +import { + type IWebpackConfiguration, + type IWebpackPluginAccessor, + PLUGIN_NAME, + type IWebpackPluginAccessorHooks +} from './shared'; +import { tryLoadWebpackConfigurationAsync } from './WebpackConfigurationLoader'; +import { type DeferredWatchFileSystem, OverrideNodeWatchFSPlugin } from './DeferredWatchFileSystem'; export interface IWebpackPluginOptions { - devConfigurationPath: string | undefined; - configurationPath: string | undefined; + devConfigurationPath?: string | undefined; + configurationPath?: string | undefined; } - -/** - * @public - */ -export const PLUGIN_NAME: 'webpack5-plugin' = 'webpack5-plugin'; const SERVE_PARAMETER_LONG_NAME: '--serve' = '--serve'; const WEBPACK_PACKAGE_NAME: 'webpack' = 'webpack'; const WEBPACK_DEV_SERVER_PACKAGE_NAME: 'webpack-dev-server' = 'webpack-dev-server'; const WEBPACK_DEV_SERVER_ENV_VAR_NAME: 'WEBPACK_DEV_SERVER' = 'WEBPACK_DEV_SERVER'; const WEBPACK_DEV_MIDDLEWARE_PACKAGE_NAME: 'webpack-dev-middleware' = 'webpack-dev-middleware'; -const UNINITIALIZED: 'UNINITIALIZED' = 'UNINITIALIZED'; /** * @internal @@ -46,7 +45,7 @@ export default class Webpack5Plugin implements IHeftTaskPlugin | undefined; private _webpackCompilationDonePromiseResolveFn: (() => void) | undefined; private _watchFileSystems: Set | undefined; @@ -57,12 +56,7 @@ export default class Webpack5Plugin implements IHeftTaskPlugin void ): Promise { - if (this._webpackConfiguration === UNINITIALIZED) { - // Obtain the webpack configuration by calling into the hook. If undefined - // is returned, load the default Webpack configuration. - taskSession.logger.terminal.writeVerboseLine( - 'Attempting to load Webpack configuration via external plugins' - ); - let webpackConfiguration: IWebpackConfiguration | false | undefined = - await this.accessor.hooks.onLoadConfiguration.promise(); - if (webpackConfiguration === undefined) { - taskSession.logger.terminal.writeVerboseLine('Attempt to load the default Webpack configuration'); - const configurationLoader: WebpackConfigurationLoader = new WebpackConfigurationLoader( - taskSession.logger, - taskSession.parameters.production, - taskSession.parameters.watch && this._isServeMode - ); - webpackConfiguration = await configurationLoader.tryLoadWebpackConfigurationAsync({ - ...options, + if (this._webpackConfiguration === false) { + const webpackConfiguration: IWebpackConfiguration | undefined = await tryLoadWebpackConfigurationAsync( + { taskSession, heftConfiguration, - loadWebpackAsyncFn: this._loadWebpackAsync.bind(this) - }); - } + hooks: this.accessor.hooks, + serveMode: this._isServeMode, + loadWebpackAsyncFn: this._loadWebpackAsync.bind(this, taskSession, heftConfiguration) + }, + options + ); - if (webpackConfiguration === false) { - taskSession.logger.terminal.writeLine('Webpack disabled by external plugin'); - this._webpackConfiguration = undefined; - } else if ( - webpackConfiguration === undefined || - (Array.isArray(webpackConfiguration) && webpackConfiguration.length === 0) - ) { - taskSession.logger.terminal.writeLine('No Webpack configuration found'); - this._webpackConfiguration = undefined; - } else { - if (this.accessor.hooks.onConfigure.isUsed()) { - // Allow for plugins to customise the configuration - await this.accessor.hooks.onConfigure.promise(webpackConfiguration); - } - if (this.accessor.hooks.onAfterConfigure.isUsed()) { - // Provide the finalized configuration - await this.accessor.hooks.onAfterConfigure.promise(webpackConfiguration); - } - this._webpackConfiguration = webpackConfiguration; - - if (requestRun) { - const overrideWatchFSPlugin: OverrideNodeWatchFSPlugin = new OverrideNodeWatchFSPlugin(requestRun); - this._watchFileSystems = overrideWatchFSPlugin.fileSystems; - for (const config of Array.isArray(webpackConfiguration) - ? webpackConfiguration - : [webpackConfiguration]) { - if (!config.plugins) { - config.plugins = [overrideWatchFSPlugin]; - } else { - config.plugins.unshift(overrideWatchFSPlugin); - } + if (webpackConfiguration && requestRun) { + const overrideWatchFSPlugin: OverrideNodeWatchFSPlugin = new OverrideNodeWatchFSPlugin(requestRun); + this._watchFileSystems = overrideWatchFSPlugin.fileSystems; + for (const config of Array.isArray(webpackConfiguration) + ? webpackConfiguration + : [webpackConfiguration]) { + if (!config.plugins) { + config.plugins = [overrideWatchFSPlugin]; + } else { + config.plugins.unshift(overrideWatchFSPlugin); } } } + + this._webpackConfiguration = webpackConfiguration; } + return this._webpackConfiguration; } - private async _loadWebpackAsync(): Promise { + private async _loadWebpackAsync( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration + ): Promise { if (!this._webpack) { - // Allow this to fail if webpack is not installed - this._webpack = await import(WEBPACK_PACKAGE_NAME); + try { + const webpackPackagePath: string = await heftConfiguration.rigPackageResolver.resolvePackageAsync( + WEBPACK_PACKAGE_NAME, + taskSession.logger.terminal + ); + this._webpack = await import(webpackPackagePath); + taskSession.logger.terminal.writeDebugLine(`Using Webpack from rig package at "${webpackPackagePath}"`); + } catch (e) { + // Fallback to bundled version if not found in rig. + this._webpack = await import(WEBPACK_PACKAGE_NAME); + taskSession.logger.terminal.writeDebugLine(`Using Webpack from built-in "${WEBPACK_PACKAGE_NAME}"`); + } } return this._webpack!; } private async _getWebpackCompilerAsync( taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, webpackConfiguration: IWebpackConfiguration ): Promise { if (!this._webpackCompiler) { - const webpack: typeof TWebpack = await this._loadWebpackAsync(); + const webpack: typeof TWebpack = await this._loadWebpackAsync(taskSession, heftConfiguration); taskSession.logger.terminal.writeLine(`Using Webpack version ${webpack.version}`); this._webpackCompiler = Array.isArray(webpackConfiguration) ? webpack.default(webpackConfiguration) /* (webpack.Compilation[]) => MultiCompiler */ @@ -208,6 +189,7 @@ export default class Webpack5Plugin implements IHeftTaskPlugin { const addressInfo: AddressInfo | string | undefined = server.server?.address() as AddressInfo; if (addressInfo) { - const address: string = - typeof addressInfo === 'string' ? addressInfo : `${addressInfo.address}:${addressInfo.port}`; - taskSession.logger.terminal.writeLine(`Started Webpack Dev Server at https://${address}`); + let url: string; + if (typeof addressInfo === 'string') { + url = addressInfo; + } else { + const address: string = + addressInfo.family === 'IPv6' + ? `[${addressInfo.address}]:${addressInfo.port}` + : `${addressInfo.address}:${addressInfo.port}`; + url = `https://${address}/`; + } + taskSession.logger.terminal.writeLine(`Started Webpack Dev Server at ${url}`); } } }; @@ -366,16 +357,17 @@ export default class Webpack5Plugin implements IHeftTaskPlugin { - if (name === WEBPACK_DEV_MIDDLEWARE_PACKAGE_NAME && type === 'error') { - const error: Error | undefined = args[0]; - if (error) { - taskSession.logger.emitError(error); + compiler.hooks.infrastructureLog.tap( + PLUGIN_NAME, + (name: string, type: string, args: unknown[] | undefined) => { + if (name === WEBPACK_DEV_MIDDLEWARE_PACKAGE_NAME && type === 'error') { + const error: Error | undefined = args?.[0] as Error | undefined; + if (error) { + taskSession.logger.emitError(error); + } } } - return true; - }); + ); // The webpack-dev-server package has a design flaw, where merely loading its package will set the // WEBPACK_DEV_SERVER environment variable -- even if no APIs are accessed. This environment variable @@ -389,7 +381,14 @@ export default class Webpack5Plugin implements IHeftTaskPlugin { + + const { onGetWatchOptions } = this.accessor.hooks; + + const watchOptions: Parameters[0] = onGetWatchOptions.isUsed() + ? await onGetWatchOptions.promise({}, webpackConfiguration) + : {}; + + compiler.watch(watchOptions, (error?: Error | null) => { if (error) { taskSession.logger.emitError(error); } @@ -486,13 +485,13 @@ export default class Webpack5Plugin implements IHeftTaskPlugin Promise); type IWebpackConfigJs = IWebpackConfigJsExport | { default: IWebpackConfigJsExport }; -interface ILoadWebpackConfigurationOptions extends IWebpackPluginOptions { +/** + * @internal + */ +export interface ILoadWebpackConfigurationOptions { taskSession: IHeftTaskSession; heftConfiguration: HeftConfiguration; + serveMode: boolean; loadWebpackAsyncFn: () => Promise; + hooks: Pick; + + _tryLoadConfigFileAsync?: typeof tryLoadWebpackConfigurationFileAsync; } const DEFAULT_WEBPACK_CONFIG_PATH: './webpack.config.js' = './webpack.config.js'; const DEFAULT_WEBPACK_DEV_CONFIG_PATH: './webpack.dev.config.js' = './webpack.dev.config.js'; -export class WebpackConfigurationLoader { - private readonly _logger: IScopedLogger; - private readonly _production: boolean; - private readonly _serveMode: boolean; +/** + * @internal + */ +export async function tryLoadWebpackConfigurationAsync( + options: ILoadWebpackConfigurationOptions, + pluginOptions: IWebpackPluginOptions +): Promise { + const { taskSession, hooks, _tryLoadConfigFileAsync = tryLoadWebpackConfigurationFileAsync } = options; + const { logger } = taskSession; + const { terminal } = logger; + + // Apply default behavior. Due to the state of `this._webpackConfiguration`, this code + // will execute exactly once. + hooks.onLoadConfiguration.tapPromise( + { + name: PLUGIN_NAME, + stage: STAGE_LOAD_LOCAL_CONFIG + }, + async () => { + terminal.writeVerboseLine(`Attempting to load Webpack configuration from local file`); + const webpackConfiguration: IWebpackConfiguration | undefined = await _tryLoadConfigFileAsync( + options, + pluginOptions + ); + + if (webpackConfiguration) { + terminal.writeVerboseLine(`Loaded Webpack configuration from local file.`); + } - public constructor(logger: IScopedLogger, production: boolean, serveMode: boolean) { - this._logger = logger; - this._production = production; - this._serveMode = serveMode; + return webpackConfiguration; + } + ); + + // Obtain the webpack configuration by calling into the hook. + // The local configuration is loaded at STAGE_LOAD_LOCAL_CONFIG + terminal.writeVerboseLine('Attempting to load Webpack configuration'); + let webpackConfiguration: IWebpackConfiguration | false | undefined = + await hooks.onLoadConfiguration.promise(); + + if (webpackConfiguration === false) { + terminal.writeLine('Webpack disabled by external plugin'); + webpackConfiguration = undefined; + } else if ( + webpackConfiguration === undefined || + (Array.isArray(webpackConfiguration) && webpackConfiguration.length === 0) + ) { + terminal.writeLine('No Webpack configuration found'); + webpackConfiguration = undefined; + } else { + if (hooks.onConfigure.isUsed()) { + // Allow for plugins to customise the configuration + await hooks.onConfigure.promise(webpackConfiguration); + } + if (hooks.onAfterConfigure.isUsed()) { + // Provide the finalized configuration + await hooks.onAfterConfigure.promise(webpackConfiguration); + } } + return webpackConfiguration; +} - public async tryLoadWebpackConfigurationAsync( - options: ILoadWebpackConfigurationOptions - ): Promise { - // TODO: Eventually replace this custom logic with a call to this utility in in webpack-cli: - // https://github.com/webpack/webpack-cli/blob/next/packages/webpack-cli/lib/groups/ConfigGroup.js +/** + * @internal + */ +export async function tryLoadWebpackConfigurationFileAsync( + options: ILoadWebpackConfigurationOptions, + pluginOptions: IWebpackPluginOptions +): Promise { + // TODO: Eventually replace this custom logic with a call to this utility in in webpack-cli: + // https://github.com/webpack/webpack-cli/blob/next/packages/webpack-cli/lib/groups/ConfigGroup.js - const { taskSession, heftConfiguration, configurationPath, devConfigurationPath, loadWebpackAsyncFn } = - options; - let webpackConfigJs: IWebpackConfigJs | undefined; + const { taskSession, heftConfiguration, loadWebpackAsyncFn, serveMode } = options; + const { + logger, + parameters: { production } + } = taskSession; + const { terminal } = logger; + const { configurationPath, devConfigurationPath } = pluginOptions; + let webpackConfigJs: IWebpackConfigJs | undefined; - try { - const buildFolderPath: string = heftConfiguration.buildFolderPath; - if (this._serveMode) { - const devConfigPath: string = path.resolve( - buildFolderPath, - devConfigurationPath || DEFAULT_WEBPACK_DEV_CONFIG_PATH - ); - this._logger.terminal.writeVerboseLine( - `Attempting to load webpack configuration from "${devConfigPath}".` - ); - webpackConfigJs = await this._tryLoadWebpackConfigurationInnerAsync(devConfigPath); - } + try { + const buildFolderPath: string = heftConfiguration.buildFolderPath; + if (serveMode) { + const devConfigPath: string = path.resolve( + buildFolderPath, + devConfigurationPath || DEFAULT_WEBPACK_DEV_CONFIG_PATH + ); + terminal.writeVerboseLine(`Attempting to load webpack configuration from "${devConfigPath}".`); + webpackConfigJs = await _tryLoadWebpackConfigurationFileInnerAsync(devConfigPath); + } - if (!webpackConfigJs) { - const configPath: string = path.resolve( - buildFolderPath, - configurationPath || DEFAULT_WEBPACK_CONFIG_PATH - ); - this._logger.terminal.writeVerboseLine( - `Attempting to load webpack configuration from "${configPath}".` - ); - webpackConfigJs = await this._tryLoadWebpackConfigurationInnerAsync(configPath); - } - } catch (error) { - this._logger.emitError(error as Error); + if (!webpackConfigJs) { + const configPath: string = path.resolve( + buildFolderPath, + configurationPath || DEFAULT_WEBPACK_CONFIG_PATH + ); + terminal.writeVerboseLine(`Attempting to load webpack configuration from "${configPath}".`); + webpackConfigJs = await _tryLoadWebpackConfigurationFileInnerAsync(configPath); } + } catch (error) { + logger.emitError(error as Error); + } - if (webpackConfigJs) { - const webpackConfig: IWebpackConfigJsExport = - (webpackConfigJs as { default: IWebpackConfigJsExport }).default || webpackConfigJs; - - if (typeof webpackConfig === 'function') { - // Defer loading of webpack until we know for sure that we will need it - return webpackConfig({ - prod: this._production, - production: this._production, - taskSession, - heftConfiguration, - webpack: await loadWebpackAsyncFn() - }); - } else { - return webpackConfig; - } + if (webpackConfigJs) { + const webpackConfig: IWebpackConfigJsExport = + (webpackConfigJs as { default: IWebpackConfigJsExport }).default || webpackConfigJs; + + if (typeof webpackConfig === 'function') { + // Defer loading of webpack until we know for sure that we will need it + return webpackConfig({ + prod: production, + production, + taskSession, + heftConfiguration, + webpack: await loadWebpackAsyncFn() + }); } else { - return undefined; + return webpackConfig; } + } else { + return undefined; } +} - private async _tryLoadWebpackConfigurationInnerAsync( - configurationPath: string - ): Promise { - const configExists: boolean = await FileSystem.existsAsync(configurationPath); - if (configExists) { - try { - return await import(configurationPath); - } catch (e) { - const error: NodeJS.ErrnoException = e as NodeJS.ErrnoException; - if (error.code === 'ERR_MODULE_NOT_FOUND') { - // No configuration found, return undefined. - return undefined; - } - throw new Error(`Error loading webpack configuration at "${configurationPath}": ${e}`); +/** + * @internal + */ +export async function _tryLoadWebpackConfigurationFileInnerAsync( + configurationPath: string +): Promise { + const configExists: boolean = await FileSystem.existsAsync(configurationPath); + if (configExists) { + try { + return await import(configurationPath); + } catch (e) { + const error: NodeJS.ErrnoException = e as NodeJS.ErrnoException; + if (error.code === 'ERR_MODULE_NOT_FOUND') { + // No configuration found, return undefined. + return undefined; } - } else { - return undefined; + throw new Error(`Error loading webpack configuration at "${configurationPath}": ${e}`); } + } else { + return undefined; } } diff --git a/heft-plugins/heft-webpack5-plugin/src/index.ts b/heft-plugins/heft-webpack5-plugin/src/index.ts index d5d58c48f00..0ad82466ea3 100644 --- a/heft-plugins/heft-webpack5-plugin/src/index.ts +++ b/heft-plugins/heft-webpack5-plugin/src/index.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -export { PLUGIN_NAME as PluginName } from './Webpack5Plugin'; +export { PLUGIN_NAME as PluginName, STAGE_LOAD_LOCAL_CONFIG } from './shared'; export type { IWebpackConfigurationWithDevServer, diff --git a/heft-plugins/heft-webpack5-plugin/src/shared.ts b/heft-plugins/heft-webpack5-plugin/src/shared.ts index 20d7555cb3c..7975bcec0c0 100644 --- a/heft-plugins/heft-webpack5-plugin/src/shared.ts +++ b/heft-plugins/heft-webpack5-plugin/src/shared.ts @@ -3,7 +3,13 @@ import type * as TWebpack from 'webpack'; import type { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server'; -import type { AsyncParallelHook, AsyncSeriesBailHook, AsyncSeriesHook } from 'tapable'; +import type { + AsyncParallelHook, + AsyncSeriesBailHook, + AsyncSeriesHook, + AsyncSeriesWaterfallHook +} from 'tapable'; + import type { IHeftTaskSession, HeftConfiguration } from '@rushstack/heft'; /** @@ -16,12 +22,12 @@ import type { IHeftTaskSession, HeftConfiguration } from '@rushstack/heft'; export interface IWebpackConfigurationFnEnvironment { /** * Whether or not the run is in production mode. Synonym of - * IWebpackConfigurationFnEnvironment.production. + * {@link IWebpackConfigurationFnEnvironment.production}. */ prod: boolean; /** * Whether or not the run is in production mode. Synonym of - * IWebpackConfigurationFnEnvironment.prod. + * {@link IWebpackConfigurationFnEnvironment.prod}. */ production: boolean; @@ -58,9 +64,9 @@ export type IWebpackConfiguration = IWebpackConfigurationWithDevServer | IWebpac export interface IWebpackPluginAccessorHooks { /** * A hook that allows for loading custom configurations used by the Webpack - * plugin. If a webpack configuration is provided, this will be populated automatically - * with the exports of the config file. If a webpack configuration is not provided, - * one will be loaded by the Webpack plugin. + * plugin. If a tap returns a value other than `undefined` before stage {@link STAGE_LOAD_LOCAL_CONFIG}, + * it will suppress loading from the webpack config file. To provide a fallback behavior in the + * absence of a local config file, tap this hook with a `stage` value greater than {@link STAGE_LOAD_LOCAL_CONFIG}. * * @remarks * Tapable event handlers can return `false` instead of `undefined` to suppress @@ -82,6 +88,14 @@ export interface IWebpackPluginAccessorHooks { * this hook will not be called. */ readonly onEmitStats: AsyncParallelHook; + /** + * A hook that allows for customization of the file watcher options. If not running in watch mode, this hook will not be called. + */ + readonly onGetWatchOptions: AsyncSeriesWaterfallHook< + Parameters[0], + Readonly, + never + >; } /** @@ -107,3 +121,15 @@ export interface IWebpackPluginAccessor { */ readonly parameters: IWebpackPluginAccessorParameters; } + +/** + * The stage in the `onLoadConfiguration` hook at which the config will be loaded from the local + * webpack config file. + * @public + */ +export const STAGE_LOAD_LOCAL_CONFIG: 1000 = 1000; + +/** + * @public + */ +export const PLUGIN_NAME: 'webpack5-plugin' = 'webpack5-plugin'; diff --git a/heft-plugins/heft-webpack5-plugin/src/test/WebpackConfigurationLoader.test.ts b/heft-plugins/heft-webpack5-plugin/src/test/WebpackConfigurationLoader.test.ts new file mode 100644 index 00000000000..855acc231cf --- /dev/null +++ b/heft-plugins/heft-webpack5-plugin/src/test/WebpackConfigurationLoader.test.ts @@ -0,0 +1,152 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { HeftConfiguration, IHeftParameters, IHeftTaskSession, IScopedLogger } from '@rushstack/heft'; +import { MockScopedLogger } from '@rushstack/heft/lib/pluginFramework/logging/MockScopedLogger'; +import { type ITerminal, StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; + +import * as WebpackConfigurationLoader from '../WebpackConfigurationLoader'; +import { _createAccessorHooks } from '../Webpack5Plugin'; +import { type IWebpackConfiguration, STAGE_LOAD_LOCAL_CONFIG } from '../shared'; + +interface IMockLoadWebpackConfigurationOptions + extends WebpackConfigurationLoader.ILoadWebpackConfigurationOptions { + loadWebpackAsyncFn: jest.Mock; + _terminalProvider: StringBufferTerminalProvider; + _tryLoadConfigFileAsync: jest.Mock; +} + +describe(WebpackConfigurationLoader.tryLoadWebpackConfigurationAsync.name, () => { + function createOptions(production: boolean, serveMode: boolean): IMockLoadWebpackConfigurationOptions { + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(false); + const terminal: ITerminal = new Terminal(terminalProvider); + const logger: IScopedLogger = new MockScopedLogger(terminal); + const buildFolderPath: string = __dirname; + + const parameters: Partial = { + production + }; + + const taskSession: IHeftTaskSession = { + logger, + parameters: parameters as unknown as IHeftParameters, + // Other values unused during these tests + hooks: undefined!, + parsedCommandLine: undefined!, + requestAccessToPluginByName: undefined!, + taskName: 'webpack', + tempFolderPath: `${__dirname}/temp` + }; + + const heftConfiguration: Partial = { + buildFolderPath + }; + + return { + taskSession, + heftConfiguration: heftConfiguration as unknown as HeftConfiguration, + hooks: _createAccessorHooks(), + loadWebpackAsyncFn: jest.fn(), + serveMode, + + _terminalProvider: terminalProvider, + _tryLoadConfigFileAsync: jest.fn() + }; + } + + it(`onLoadConfiguration can return false`, async () => { + const options: IMockLoadWebpackConfigurationOptions = createOptions(false, false); + + const onLoadConfiguration: jest.Mock = jest.fn(); + const onConfigure: jest.Mock = jest.fn(); + const onAfterConfigure: jest.Mock = jest.fn(); + + options.hooks.onLoadConfiguration.tap('test', onLoadConfiguration); + options.hooks.onConfigure.tap('test', onConfigure); + options.hooks.onAfterConfigure.tap('test', onAfterConfigure); + + onLoadConfiguration.mockReturnValue(false); + + const config: IWebpackConfiguration | undefined = + await WebpackConfigurationLoader.tryLoadWebpackConfigurationAsync(options, {}); + expect(config).toBeUndefined(); + expect(onLoadConfiguration).toHaveBeenCalledTimes(1); + expect(options._tryLoadConfigFileAsync).toHaveBeenCalledTimes(0); + expect(onConfigure).toHaveBeenCalledTimes(0); + expect(onAfterConfigure).toHaveBeenCalledTimes(0); + }); + + it(`calls tryLoadWebpackConfigurationFileAsync`, async () => { + const options: IMockLoadWebpackConfigurationOptions = createOptions(false, false); + + const onConfigure: jest.Mock = jest.fn(); + const onAfterConfigure: jest.Mock = jest.fn(); + + options.hooks.onConfigure.tap('test', onConfigure); + options.hooks.onAfterConfigure.tap('test', onAfterConfigure); + + options._tryLoadConfigFileAsync.mockReturnValue(Promise.resolve(undefined)); + + const config: IWebpackConfiguration | undefined = + await WebpackConfigurationLoader.tryLoadWebpackConfigurationAsync(options, {}); + expect(config).toBeUndefined(); + expect(options._tryLoadConfigFileAsync).toHaveBeenCalledTimes(1); + expect(onConfigure).toHaveBeenCalledTimes(0); + expect(onAfterConfigure).toHaveBeenCalledTimes(0); + }); + + it(`can fall back`, async () => { + const options: IMockLoadWebpackConfigurationOptions = createOptions(false, false); + + const onLoadConfiguration: jest.Mock = jest.fn(); + const onConfigure: jest.Mock = jest.fn(); + const onAfterConfigure: jest.Mock = jest.fn(); + + options.hooks.onLoadConfiguration.tap( + { name: 'test', stage: STAGE_LOAD_LOCAL_CONFIG + 1 }, + onLoadConfiguration + ); + options.hooks.onConfigure.tap('test', onConfigure); + options.hooks.onAfterConfigure.tap('test', onAfterConfigure); + + options._tryLoadConfigFileAsync.mockReturnValue(Promise.resolve(undefined)); + + const mockConfig: IWebpackConfiguration = {}; + onLoadConfiguration.mockReturnValue(mockConfig); + + const config: IWebpackConfiguration | undefined = + await WebpackConfigurationLoader.tryLoadWebpackConfigurationAsync(options, {}); + expect(config).toBe(mockConfig); + + expect(options._tryLoadConfigFileAsync).toHaveBeenCalledTimes(1); + expect(onLoadConfiguration).toHaveBeenCalledTimes(1); + expect(onConfigure).toHaveBeenCalledTimes(1); + expect(onAfterConfigure).toHaveBeenCalledTimes(1); + + expect(onConfigure).toHaveBeenCalledWith(mockConfig); + expect(onAfterConfigure).toHaveBeenCalledWith(mockConfig); + }); + + it(`respects hook order`, async () => { + const options: IMockLoadWebpackConfigurationOptions = createOptions(false, false); + + const onConfigure: jest.Mock = jest.fn(); + const onAfterConfigure: jest.Mock = jest.fn(); + + options.hooks.onConfigure.tap('test', onConfigure); + options.hooks.onAfterConfigure.tap('test', onAfterConfigure); + + const mockConfig: IWebpackConfiguration = {}; + + options._tryLoadConfigFileAsync.mockReturnValue(Promise.resolve(mockConfig)); + + const config: IWebpackConfiguration | undefined = + await WebpackConfigurationLoader.tryLoadWebpackConfigurationAsync(options, {}); + expect(config).toBe(mockConfig); + expect(options._tryLoadConfigFileAsync).toHaveBeenCalledTimes(1); + expect(onConfigure).toHaveBeenCalledTimes(1); + expect(onConfigure).toHaveBeenCalledWith(mockConfig); + expect(onAfterConfigure).toHaveBeenCalledTimes(1); + expect(onAfterConfigure).toHaveBeenCalledWith(mockConfig); + }); +}); diff --git a/heft-plugins/heft-webpack5-plugin/tsconfig.json b/heft-plugins/heft-webpack5-plugin/tsconfig.json index 7512871fdbf..dac21d04081 100644 --- a/heft-plugins/heft-webpack5-plugin/tsconfig.json +++ b/heft-plugins/heft-webpack5-plugin/tsconfig.json @@ -1,7 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["node"] - } + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/libraries/api-extractor-model/.eslintrc.js b/libraries/api-extractor-model/.eslintrc.js deleted file mode 100644 index 640ff6db4e3..00000000000 --- a/libraries/api-extractor-model/.eslintrc.js +++ /dev/null @@ -1,12 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/node', '@rushstack/eslint-config/mixins/friendly-locals'], - parserOptions: { tsconfigRootDir: __dirname }, - - rules: { - // api-extractor-model uses namespaces to represent mixins - '@typescript-eslint/no-namespace': 'off' - } -}; diff --git a/libraries/api-extractor-model/.npmignore b/libraries/api-extractor-model/.npmignore index 302dbc5b019..f7a40e10213 100644 --- a/libraries/api-extractor-model/.npmignore +++ b/libraries/api-extractor-model/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,16 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/api-extractor-model/CHANGELOG.json b/libraries/api-extractor-model/CHANGELOG.json index 556b7afe205..32f136f7d2d 100644 --- a/libraries/api-extractor-model/CHANGELOG.json +++ b/libraries/api-extractor-model/CHANGELOG.json @@ -1,6 +1,851 @@ { "name": "@microsoft/api-extractor-model", "entries": [ + { + "version": "7.33.10", + "tag": "@microsoft/api-extractor-model_v7.33.10", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "patch": [ + { + "comment": "Replace `@override` with the `override` keyword." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + } + ] + } + }, + { + "version": "7.33.9", + "tag": "@microsoft/api-extractor-model_v7.33.9", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + } + ] + } + }, + { + "version": "7.33.8", + "tag": "@microsoft/api-extractor-model_v7.33.8", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + } + ] + } + }, + { + "version": "7.33.7", + "tag": "@microsoft/api-extractor-model_v7.33.7", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + } + ] + } + }, + { + "version": "7.33.6", + "tag": "@microsoft/api-extractor-model_v7.33.6", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + } + ] + } + }, + { + "version": "7.33.5", + "tag": "@microsoft/api-extractor-model_v7.33.5", + "date": "Tue, 31 Mar 2026 15:14:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + } + ] + } + }, + { + "version": "7.33.4", + "tag": "@microsoft/api-extractor-model_v7.33.4", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "patch": [ + { + "comment": "Bump `@microsoft/tsdoc-config` to `~0.18.1` to mitigate CVE-2025-69873." + } + ] + } + }, + { + "version": "7.33.3", + "tag": "@microsoft/api-extractor-model_v7.33.3", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + } + ] + } + }, + { + "version": "7.33.2", + "tag": "@microsoft/api-extractor-model_v7.33.2", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + } + ] + } + }, + { + "version": "7.33.1", + "tag": "@microsoft/api-extractor-model_v7.33.1", + "date": "Fri, 20 Feb 2026 00:15:03 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + } + ] + } + }, + { + "version": "7.33.0", + "tag": "@microsoft/api-extractor-model_v7.33.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + } + ] + } + }, + { + "version": "7.32.2", + "tag": "@microsoft/api-extractor-model_v7.32.2", + "date": "Sat, 06 Dec 2025 01:12:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + } + ] + } + }, + { + "version": "7.32.1", + "tag": "@microsoft/api-extractor-model_v7.32.1", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + } + ] + } + }, + { + "version": "7.32.0", + "tag": "@microsoft/api-extractor-model_v7.32.0", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "minor": [ + { + "comment": "Bump the `@microsoft/tsdoc` dependency to `~0.16.0`." + }, + { + "comment": "Bump the `@microsoft/tsdoc-config` dependency to `~0.18.0`." + } + ] + } + }, + { + "version": "7.31.3", + "tag": "@microsoft/api-extractor-model_v7.31.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + } + ] + } + }, + { + "version": "7.31.2", + "tag": "@microsoft/api-extractor-model_v7.31.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + } + ] + } + }, + { + "version": "7.31.1", + "tag": "@microsoft/api-extractor-model_v7.31.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + } + ] + } + }, + { + "version": "7.31.0", + "tag": "@microsoft/api-extractor-model_v7.31.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + } + ] + } + }, + { + "version": "7.30.9", + "tag": "@microsoft/api-extractor-model_v7.30.9", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + } + ] + } + }, + { + "version": "7.30.8", + "tag": "@microsoft/api-extractor-model_v7.30.8", + "date": "Tue, 30 Sep 2025 20:33:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + } + ] + } + }, + { + "version": "7.30.7", + "tag": "@microsoft/api-extractor-model_v7.30.7", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + } + ] + } + }, + { + "version": "7.30.6", + "tag": "@microsoft/api-extractor-model_v7.30.6", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + } + ] + } + }, + { + "version": "7.30.5", + "tag": "@microsoft/api-extractor-model_v7.30.5", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + } + ] + } + }, + { + "version": "7.30.4", + "tag": "@microsoft/api-extractor-model_v7.30.4", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + } + ] + } + }, + { + "version": "7.30.3", + "tag": "@microsoft/api-extractor-model_v7.30.3", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + } + ] + } + }, + { + "version": "7.30.2", + "tag": "@microsoft/api-extractor-model_v7.30.2", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + } + ] + } + }, + { + "version": "7.30.1", + "tag": "@microsoft/api-extractor-model_v7.30.1", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + } + ] + } + }, + { + "version": "7.30.0", + "tag": "@microsoft/api-extractor-model_v7.30.0", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "minor": [ + { + "comment": "Update TSDoc dependencies." + } + ] + } + }, + { + "version": "7.29.9", + "tag": "@microsoft/api-extractor-model_v7.29.9", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + } + ] + } + }, + { + "version": "7.29.8", + "tag": "@microsoft/api-extractor-model_v7.29.8", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + } + ] + } + }, + { + "version": "7.29.7", + "tag": "@microsoft/api-extractor-model_v7.29.7", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + } + ] + } + }, + { + "version": "7.29.6", + "tag": "@microsoft/api-extractor-model_v7.29.6", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + } + ] + } + }, + { + "version": "7.29.5", + "tag": "@microsoft/api-extractor-model_v7.29.5", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + } + ] + } + }, + { + "version": "7.29.4", + "tag": "@microsoft/api-extractor-model_v7.29.4", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + } + ] + } + }, + { + "version": "7.29.3", + "tag": "@microsoft/api-extractor-model_v7.29.3", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + } + ] + } + }, + { + "version": "7.29.2", + "tag": "@microsoft/api-extractor-model_v7.29.2", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "patch": [ + { + "comment": "Include missing `type` modifiers on type-only exports." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + } + ] + } + }, + { + "version": "7.29.1", + "tag": "@microsoft/api-extractor-model_v7.29.1", + "date": "Wed, 29 May 2024 02:03:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + } + ] + } + }, + { + "version": "7.29.0", + "tag": "@microsoft/api-extractor-model_v7.29.0", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "minor": [ + { + "comment": "Bump TSDoc dependencies." + } + ] + } + }, + { + "version": "7.28.21", + "tag": "@microsoft/api-extractor-model_v7.28.21", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + } + ] + } + }, + { + "version": "7.28.20", + "tag": "@microsoft/api-extractor-model_v7.28.20", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + } + ] + } + }, + { + "version": "7.28.19", + "tag": "@microsoft/api-extractor-model_v7.28.19", + "date": "Sat, 25 May 2024 04:54:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + } + ] + } + }, + { + "version": "7.28.18", + "tag": "@microsoft/api-extractor-model_v7.28.18", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + } + ] + } + }, + { + "version": "7.28.17", + "tag": "@microsoft/api-extractor-model_v7.28.17", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + } + ] + } + }, + { + "version": "7.28.16", + "tag": "@microsoft/api-extractor-model_v7.28.16", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + } + ] + } + }, + { + "version": "7.28.15", + "tag": "@microsoft/api-extractor-model_v7.28.15", + "date": "Mon, 06 May 2024 15:11:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + } + ] + } + }, + { + "version": "7.28.14", + "tag": "@microsoft/api-extractor-model_v7.28.14", + "date": "Wed, 10 Apr 2024 15:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + } + ] + } + }, + { + "version": "7.28.13", + "tag": "@microsoft/api-extractor-model_v7.28.13", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + } + ] + } + }, + { + "version": "7.28.12", + "tag": "@microsoft/api-extractor-model_v7.28.12", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + } + ] + } + }, + { + "version": "7.28.11", + "tag": "@microsoft/api-extractor-model_v7.28.11", + "date": "Mon, 19 Feb 2024 21:54:26 GMT", + "comments": { + "patch": [ + { + "comment": "Fix a formatting issue with the LICENSE." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + } + ] + } + }, + { + "version": "7.28.10", + "tag": "@microsoft/api-extractor-model_v7.28.10", + "date": "Sat, 17 Feb 2024 06:24:34 GMT", + "comments": { + "patch": [ + { + "comment": "Fix broken link to API documentation" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + } + ] + } + }, + { + "version": "7.28.9", + "tag": "@microsoft/api-extractor-model_v7.28.9", + "date": "Thu, 08 Feb 2024 01:09:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + } + ] + } + }, + { + "version": "7.28.8", + "tag": "@microsoft/api-extractor-model_v7.28.8", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + } + ] + } + }, + { + "version": "7.28.7", + "tag": "@microsoft/api-extractor-model_v7.28.7", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + } + ] + } + }, + { + "version": "7.28.6", + "tag": "@microsoft/api-extractor-model_v7.28.6", + "date": "Tue, 23 Jan 2024 20:12:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + } + ] + } + }, + { + "version": "7.28.5", + "tag": "@microsoft/api-extractor-model_v7.28.5", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + } + ] + } + }, + { + "version": "7.28.4", + "tag": "@microsoft/api-extractor-model_v7.28.4", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + } + ] + } + }, + { + "version": "7.28.3", + "tag": "@microsoft/api-extractor-model_v7.28.3", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + } + ] + } + }, + { + "version": "7.28.2", + "tag": "@microsoft/api-extractor-model_v7.28.2", + "date": "Thu, 28 Sep 2023 20:53:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + } + ] + } + }, + { + "version": "7.28.1", + "tag": "@microsoft/api-extractor-model_v7.28.1", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + } + ] + } + }, + { + "version": "7.28.0", + "tag": "@microsoft/api-extractor-model_v7.28.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + } + ] + } + }, + { + "version": "7.27.6", + "tag": "@microsoft/api-extractor-model_v7.27.6", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + } + ] + } + }, + { + "version": "7.27.5", + "tag": "@microsoft/api-extractor-model_v7.27.5", + "date": "Wed, 19 Jul 2023 00:20:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + } + ] + } + }, + { + "version": "7.27.4", + "tag": "@microsoft/api-extractor-model_v7.27.4", + "date": "Thu, 06 Jul 2023 00:16:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + } + ] + } + }, + { + "version": "7.27.3", + "tag": "@microsoft/api-extractor-model_v7.27.3", + "date": "Thu, 15 Jun 2023 00:21:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + } + ] + } + }, + { + "version": "7.27.2", + "tag": "@microsoft/api-extractor-model_v7.27.2", + "date": "Wed, 07 Jun 2023 22:45:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + } + ] + } + }, { "version": "7.27.1", "tag": "@microsoft/api-extractor-model_v7.27.1", diff --git a/libraries/api-extractor-model/CHANGELOG.md b/libraries/api-extractor-model/CHANGELOG.md index 41ddf5873a7..98a1e1f5fea 100644 --- a/libraries/api-extractor-model/CHANGELOG.md +++ b/libraries/api-extractor-model/CHANGELOG.md @@ -1,6 +1,360 @@ # Change Log - @microsoft/api-extractor-model -This log was last generated on Mon, 29 May 2023 15:21:15 GMT and should not be manually modified. +This log was last generated on Fri, 17 Jul 2026 00:15:59 GMT and should not be manually modified. + +## 7.33.10 +Fri, 17 Jul 2026 00:15:59 GMT + +### Patches + +- Replace `@override` with the `override` keyword. + +## 7.33.9 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 7.33.8 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 7.33.7 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 7.33.6 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 7.33.5 +Tue, 31 Mar 2026 15:14:14 GMT + +_Version update only_ + +## 7.33.4 +Wed, 25 Feb 2026 21:39:42 GMT + +### Patches + +- Bump `@microsoft/tsdoc-config` to `~0.18.1` to mitigate CVE-2025-69873. + +## 7.33.3 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 7.33.2 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 7.33.1 +Fri, 20 Feb 2026 00:15:03 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 7.33.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 7.32.2 +Sat, 06 Dec 2025 01:12:29 GMT + +_Version update only_ + +## 7.32.1 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 7.32.0 +Wed, 12 Nov 2025 01:12:56 GMT + +### Minor changes + +- Bump the `@microsoft/tsdoc` dependency to `~0.16.0`. +- Bump the `@microsoft/tsdoc-config` dependency to `~0.18.0`. + +## 7.31.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 7.31.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 7.31.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 7.31.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 7.30.9 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 7.30.8 +Tue, 30 Sep 2025 20:33:50 GMT + +_Version update only_ + +## 7.30.7 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 7.30.6 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 7.30.5 +Tue, 25 Mar 2025 15:11:15 GMT + +_Version update only_ + +## 7.30.4 +Tue, 11 Mar 2025 02:12:33 GMT + +_Version update only_ + +## 7.30.3 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 7.30.2 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 7.30.1 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 7.30.0 +Sat, 23 Nov 2024 01:18:55 GMT + +### Minor changes + +- Update TSDoc dependencies. + +## 7.29.9 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 7.29.8 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 7.29.7 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 7.29.6 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 7.29.5 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 7.29.4 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 7.29.3 +Tue, 16 Jul 2024 00:36:21 GMT + +_Version update only_ + +## 7.29.2 +Thu, 30 May 2024 00:13:05 GMT + +### Patches + +- Include missing `type` modifiers on type-only exports. + +## 7.29.1 +Wed, 29 May 2024 02:03:51 GMT + +_Version update only_ + +## 7.29.0 +Wed, 29 May 2024 00:10:52 GMT + +### Minor changes + +- Bump TSDoc dependencies. + +## 7.28.21 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 7.28.20 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 7.28.19 +Sat, 25 May 2024 04:54:08 GMT + +_Version update only_ + +## 7.28.18 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 7.28.17 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 7.28.16 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 7.28.15 +Mon, 06 May 2024 15:11:05 GMT + +_Version update only_ + +## 7.28.14 +Wed, 10 Apr 2024 15:10:08 GMT + +_Version update only_ + +## 7.28.13 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 7.28.12 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 7.28.11 +Mon, 19 Feb 2024 21:54:26 GMT + +### Patches + +- Fix a formatting issue with the LICENSE. + +## 7.28.10 +Sat, 17 Feb 2024 06:24:34 GMT + +### Patches + +- Fix broken link to API documentation + +## 7.28.9 +Thu, 08 Feb 2024 01:09:22 GMT + +_Version update only_ + +## 7.28.8 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 7.28.7 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 7.28.6 +Tue, 23 Jan 2024 20:12:58 GMT + +_Version update only_ + +## 7.28.5 +Tue, 23 Jan 2024 16:15:05 GMT + +_Version update only_ + +## 7.28.4 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 7.28.3 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 7.28.2 +Thu, 28 Sep 2023 20:53:16 GMT + +_Version update only_ + +## 7.28.1 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 7.28.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 7.27.6 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 7.27.5 +Wed, 19 Jul 2023 00:20:32 GMT + +_Version update only_ + +## 7.27.4 +Thu, 06 Jul 2023 00:16:20 GMT + +_Version update only_ + +## 7.27.3 +Thu, 15 Jun 2023 00:21:01 GMT + +_Version update only_ + +## 7.27.2 +Wed, 07 Jun 2023 22:45:16 GMT + +_Version update only_ ## 7.27.1 Mon, 29 May 2023 15:21:15 GMT diff --git a/libraries/api-extractor-model/LICENSE b/libraries/api-extractor-model/LICENSE index d9cfa3eb077..71a5411091a 100644 --- a/libraries/api-extractor-model/LICENSE +++ b/libraries/api-extractor-model/LICENSE @@ -1,4 +1,4 @@ -@microsoft/api-extractor +@microsoft/api-extractor-model Copyright (c) Microsoft Corporation. All rights reserved. diff --git a/libraries/api-extractor-model/README.md b/libraries/api-extractor-model/README.md index 9c2b5c243d4..d00064af4cd 100644 --- a/libraries/api-extractor-model/README.md +++ b/libraries/api-extractor-model/README.md @@ -4,7 +4,7 @@ Use this library to read and write *.api.json files as defined by the [API Extra These files are used to generate a documentation website for your TypeScript package. The files store the API signatures and doc comments that were extracted from your package. -API documentation for this package: https://rushstack.io/pages/api/api-extractor-model/ +API documentation for this package: https://api.rushstack.io/pages/api-extractor-model/ ## Example Usage @@ -63,6 +63,6 @@ a namespace containing static members of the class. - [CHANGELOG.md]( https://github.com/microsoft/rushstack/blob/main/libraries/api-extractor-model/CHANGELOG.md) - Find out what's new in the latest version -- [API Reference](https://rushstack.io/pages/api/api-extractor-model/) +- [API Reference](https://api.rushstack.io/pages/api-extractor-model/) API Extractor is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/libraries/api-extractor-model/config/api-extractor.json b/libraries/api-extractor-model/config/api-extractor.json index aa9d8f810fd..bf6aa143c60 100644 --- a/libraries/api-extractor-model/config/api-extractor.json +++ b/libraries/api-extractor-model/config/api-extractor.json @@ -1,17 +1,6 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - - "mainEntryPointFilePath": "/lib/index.d.ts", - - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, - - "docModel": { - "enabled": true, - "apiJsonFilePath": "../../../common/temp/api/.api.json" - }, + "extends": "decoupled-local-node-rig/profiles/default/config/api-extractor-base.json", "dtsRollup": { "enabled": true, diff --git a/libraries/api-extractor-model/config/jest.config.json b/libraries/api-extractor-model/config/jest.config.json index 4bb17bde3ee..7c0f9ccc9d6 100644 --- a/libraries/api-extractor-model/config/jest.config.json +++ b/libraries/api-extractor-model/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "decoupled-local-node-rig/profiles/default/config/jest.config.json" } diff --git a/libraries/api-extractor-model/config/rig.json b/libraries/api-extractor-model/config/rig.json index 6ac88a96368..cc98dea43dd 100644 --- a/libraries/api-extractor-model/config/rig.json +++ b/libraries/api-extractor-model/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "decoupled-local-node-rig" } diff --git a/libraries/api-extractor-model/eslint.config.js b/libraries/api-extractor-model/eslint.config.js new file mode 100644 index 00000000000..f0925d11b7d --- /dev/null +++ b/libraries/api-extractor-model/eslint.config.js @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + }, + rules: { + // api-extractor-model uses namespaces to represent mixins + '@typescript-eslint/no-namespace': 'off' + } + } +]; diff --git a/libraries/api-extractor-model/package.json b/libraries/api-extractor-model/package.json index 619964ef013..d203e042146 100644 --- a/libraries/api-extractor-model/package.json +++ b/libraries/api-extractor-model/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/api-extractor-model", - "version": "7.27.1", + "version": "7.33.10", "description": "A helper library for loading and saving the .api.json files created by API Extractor", "repository": { "type": "git", @@ -8,24 +8,46 @@ "directory": "libraries/api-extractor-model" }, "homepage": "https://api-extractor.com", - "main": "lib/index.js", - "typings": "dist/rollup.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/rollup.d.ts", + "exports": { + ".": { + "types": "./dist/rollup.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "license": "MIT", "scripts": { "build": "heft build --clean", - "_phase:build": "heft build --clean", - "_phase:test": "heft test --no-build" + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" }, "dependencies": { - "@microsoft/tsdoc": "0.14.2", - "@microsoft/tsdoc-config": "~0.16.1", + "@microsoft/tsdoc": "~0.16.0", + "@microsoft/tsdoc-config": "~0.18.1", "@rushstack/node-core-library": "workspace:*" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft": "0.50.6", - "@rushstack/heft-node-rig": "1.13.0", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36" - } + "@rushstack/heft": "1.2.22", + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0" + }, + "sideEffects": false } diff --git a/libraries/api-extractor-model/src/aedoc/AedocDefinitions.ts b/libraries/api-extractor-model/src/aedoc/AedocDefinitions.ts index a639404d66a..9d4ab635ffa 100644 --- a/libraries/api-extractor-model/src/aedoc/AedocDefinitions.ts +++ b/libraries/api-extractor-model/src/aedoc/AedocDefinitions.ts @@ -3,6 +3,8 @@ import { TSDocConfiguration, TSDocTagDefinition, TSDocTagSyntaxKind, StandardTags } from '@microsoft/tsdoc'; +let _tsdocConfiguration: TSDocConfiguration | undefined; + /** * @internal * @deprecated - tsdoc configuration is now constructed from tsdoc.json files associated with each package. @@ -24,7 +26,7 @@ export class AedocDefinitions { }); public static get tsdocConfiguration(): TSDocConfiguration { - if (!AedocDefinitions._tsdocConfiguration) { + if (!_tsdocConfiguration) { const configuration: TSDocConfiguration = new TSDocConfiguration(); configuration.addTagDefinitions( [ @@ -62,10 +64,8 @@ export class AedocDefinitions { true ); - AedocDefinitions._tsdocConfiguration = configuration; + _tsdocConfiguration = configuration; } - return AedocDefinitions._tsdocConfiguration; + return _tsdocConfiguration; } - - private static _tsdocConfiguration: TSDocConfiguration | undefined; } diff --git a/libraries/api-extractor-model/src/index.ts b/libraries/api-extractor-model/src/index.ts index be473904f9e..f902fa8dc91 100644 --- a/libraries/api-extractor-model/src/index.ts +++ b/libraries/api-extractor-model/src/index.ts @@ -14,64 +14,70 @@ export { AedocDefinitions } from './aedoc/AedocDefinitions'; export { ReleaseTag } from './aedoc/ReleaseTag'; // items -export { IApiDeclaredItemOptions, ApiDeclaredItem } from './items/ApiDeclaredItem'; -export { IApiDocumentedItemOptions, ApiDocumentedItem } from './items/ApiDocumentedItem'; -export { ApiItemKind, IApiItemOptions, ApiItem, IApiItemConstructor } from './items/ApiItem'; -export { IApiPropertyItemOptions, ApiPropertyItem } from './items/ApiPropertyItem'; +export { type IApiDeclaredItemOptions, ApiDeclaredItem } from './items/ApiDeclaredItem'; +export { type IApiDocumentedItemOptions, ApiDocumentedItem } from './items/ApiDocumentedItem'; +export { ApiItemKind, type IApiItemOptions, ApiItem, type IApiItemConstructor } from './items/ApiItem'; +export { type IApiPropertyItemOptions, ApiPropertyItem } from './items/ApiPropertyItem'; // mixins export { - IApiParameterListMixinOptions, - IApiParameterOptions, + type IApiParameterListMixinOptions, + type IApiParameterOptions, ApiParameterListMixin } from './mixins/ApiParameterListMixin'; export { - IApiTypeParameterOptions, - IApiTypeParameterListMixinOptions, + type IApiTypeParameterOptions, + type IApiTypeParameterListMixinOptions, ApiTypeParameterListMixin } from './mixins/ApiTypeParameterListMixin'; -export { IApiAbstractMixinOptions, ApiAbstractMixin } from './mixins/ApiAbstractMixin'; -export { IApiItemContainerMixinOptions, ApiItemContainerMixin } from './mixins/ApiItemContainerMixin'; -export { IApiProtectedMixinOptions, ApiProtectedMixin } from './mixins/ApiProtectedMixin'; -export { IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from './mixins/ApiReleaseTagMixin'; -export { IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from './mixins/ApiReturnTypeMixin'; -export { IApiStaticMixinOptions, ApiStaticMixin } from './mixins/ApiStaticMixin'; -export { IApiNameMixinOptions, ApiNameMixin } from './mixins/ApiNameMixin'; -export { IApiOptionalMixinOptions, ApiOptionalMixin } from './mixins/ApiOptionalMixin'; -export { IApiReadonlyMixinOptions, ApiReadonlyMixin } from './mixins/ApiReadonlyMixin'; -export { IApiInitializerMixinOptions, ApiInitializerMixin } from './mixins/ApiInitializerMixin'; -export { IApiExportedMixinOptions, ApiExportedMixin } from './mixins/ApiExportedMixin'; +export { type IApiAbstractMixinOptions, ApiAbstractMixin } from './mixins/ApiAbstractMixin'; +export { type IApiItemContainerMixinOptions, ApiItemContainerMixin } from './mixins/ApiItemContainerMixin'; +export { type IApiProtectedMixinOptions, ApiProtectedMixin } from './mixins/ApiProtectedMixin'; +export { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from './mixins/ApiReleaseTagMixin'; +export { type IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from './mixins/ApiReturnTypeMixin'; +export { type IApiStaticMixinOptions, ApiStaticMixin } from './mixins/ApiStaticMixin'; +export { type IApiNameMixinOptions, ApiNameMixin } from './mixins/ApiNameMixin'; +export { type IApiOptionalMixinOptions, ApiOptionalMixin } from './mixins/ApiOptionalMixin'; +export { type IApiReadonlyMixinOptions, ApiReadonlyMixin } from './mixins/ApiReadonlyMixin'; +export { type IApiInitializerMixinOptions, ApiInitializerMixin } from './mixins/ApiInitializerMixin'; +export { type IApiExportedMixinOptions, ApiExportedMixin } from './mixins/ApiExportedMixin'; export { - IFindApiItemsResult, - IFindApiItemsMessage, + type IFindApiItemsResult, + type IFindApiItemsMessage, FindApiItemsMessageId } from './mixins/IFindApiItemsResult'; -export { ExcerptTokenKind, IExcerptTokenRange, IExcerptToken, ExcerptToken, Excerpt } from './mixins/Excerpt'; -export { Constructor, PropertiesOf } from './mixins/Mixin'; +export { + ExcerptTokenKind, + type IExcerptTokenRange, + type IExcerptToken, + ExcerptToken, + Excerpt +} from './mixins/Excerpt'; +export type { Constructor, PropertiesOf } from './mixins/Mixin'; // model -export { IApiCallSignatureOptions, ApiCallSignature } from './model/ApiCallSignature'; -export { IApiClassOptions, ApiClass } from './model/ApiClass'; -export { IApiConstructorOptions, ApiConstructor } from './model/ApiConstructor'; -export { IApiConstructSignatureOptions, ApiConstructSignature } from './model/ApiConstructSignature'; -export { IApiEntryPointOptions, ApiEntryPoint } from './model/ApiEntryPoint'; -export { IApiEnumOptions, ApiEnum } from './model/ApiEnum'; -export { IApiEnumMemberOptions, ApiEnumMember, EnumMemberOrder } from './model/ApiEnumMember'; -export { IApiFunctionOptions, ApiFunction } from './model/ApiFunction'; -export { IApiIndexSignatureOptions, ApiIndexSignature } from './model/ApiIndexSignature'; -export { IApiInterfaceOptions, ApiInterface } from './model/ApiInterface'; -export { IApiMethodOptions, ApiMethod } from './model/ApiMethod'; -export { IApiMethodSignatureOptions, ApiMethodSignature } from './model/ApiMethodSignature'; +export { type IApiCallSignatureOptions, ApiCallSignature } from './model/ApiCallSignature'; +export { type IApiClassOptions, ApiClass } from './model/ApiClass'; +export { type IApiConstructorOptions, ApiConstructor } from './model/ApiConstructor'; +export { type IApiConstructSignatureOptions, ApiConstructSignature } from './model/ApiConstructSignature'; +export { type IApiEntryPointOptions, ApiEntryPoint } from './model/ApiEntryPoint'; +export { type IApiEnumOptions, ApiEnum } from './model/ApiEnum'; +export { type IApiEnumMemberOptions, ApiEnumMember, EnumMemberOrder } from './model/ApiEnumMember'; +export { type IApiFunctionOptions, ApiFunction } from './model/ApiFunction'; +export { type IApiIndexSignatureOptions, ApiIndexSignature } from './model/ApiIndexSignature'; +export { type IApiInterfaceOptions, ApiInterface } from './model/ApiInterface'; +export { type IApiMethodOptions, ApiMethod } from './model/ApiMethod'; +export { type IApiMethodSignatureOptions, ApiMethodSignature } from './model/ApiMethodSignature'; export { ApiModel } from './model/ApiModel'; -export { IApiNamespaceOptions, ApiNamespace } from './model/ApiNamespace'; -export { IApiPackageOptions, ApiPackage, IApiPackageSaveOptions } from './model/ApiPackage'; -export { IParameterOptions, Parameter } from './model/Parameter'; -export { IApiPropertyOptions, ApiProperty } from './model/ApiProperty'; -export { IApiPropertySignatureOptions, ApiPropertySignature } from './model/ApiPropertySignature'; -export { IApiTypeAliasOptions, ApiTypeAlias } from './model/ApiTypeAlias'; -export { ITypeParameterOptions, TypeParameter } from './model/TypeParameter'; -export { IApiVariableOptions, ApiVariable } from './model/ApiVariable'; -export { IResolveDeclarationReferenceResult } from './model/ModelReferenceResolver'; +export { type IApiNamespaceOptions, ApiNamespace } from './model/ApiNamespace'; +export { type IApiPackageOptions, ApiPackage, type IApiPackageSaveOptions } from './model/ApiPackage'; +export { type IParameterOptions, Parameter } from './model/Parameter'; +export { type IApiPropertyOptions, ApiProperty } from './model/ApiProperty'; +export { type IApiPropertySignatureOptions, ApiPropertySignature } from './model/ApiPropertySignature'; +export { type IApiTypeAliasOptions, ApiTypeAlias } from './model/ApiTypeAlias'; +export { type ITypeParameterOptions, TypeParameter } from './model/TypeParameter'; +export { type IApiVariableOptions, ApiVariable } from './model/ApiVariable'; +export { type IResolveDeclarationReferenceResult } from './model/ModelReferenceResolver'; export { HeritageType } from './model/HeritageType'; -export { ISourceLocationOptions, SourceLocation } from './model/SourceLocation'; +export { type ISourceLocationOptions, SourceLocation } from './model/SourceLocation'; diff --git a/libraries/api-extractor-model/src/items/ApiDeclaredItem.ts b/libraries/api-extractor-model/src/items/ApiDeclaredItem.ts index 8971dd40c43..aa4fba36e63 100644 --- a/libraries/api-extractor-model/src/items/ApiDeclaredItem.ts +++ b/libraries/api-extractor-model/src/items/ApiDeclaredItem.ts @@ -1,11 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information.s +// See LICENSE in the project root for license information. import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; -import { ApiDocumentedItem, IApiDocumentedItemJson, IApiDocumentedItemOptions } from './ApiDocumentedItem'; -import { ApiItem } from './ApiItem'; -import { Excerpt, ExcerptToken, IExcerptTokenRange, IExcerptToken } from '../mixins/Excerpt'; -import { DeserializerContext } from '../model/DeserializerContext'; + +import { + ApiDocumentedItem, + type IApiDocumentedItemJson, + type IApiDocumentedItemOptions +} from './ApiDocumentedItem'; +import type { ApiItem } from './ApiItem'; +import { Excerpt, ExcerptToken, type IExcerptTokenRange, type IExcerptToken } from '../mixins/Excerpt'; +import type { DeserializerContext } from '../model/DeserializerContext'; import { SourceLocation } from '../model/SourceLocation'; /** @@ -35,7 +40,6 @@ export interface IApiDeclaredItemJson extends IApiDocumentedItemJson { * * @public */ -// eslint-disable-next-line @typescript-eslint/naming-convention export class ApiDeclaredItem extends ApiDocumentedItem { private _excerptTokens: ExcerptToken[]; private _excerpt: Excerpt; @@ -56,8 +60,7 @@ export class ApiDeclaredItem extends ApiDocumentedItem { this._fileUrlPath = options.fileUrlPath; } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiDeclaredItemJson @@ -131,8 +134,7 @@ export class ApiDeclaredItem extends ApiDocumentedItem { return excerpt; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); jsonObject.excerptTokens = this.excerptTokens.map((x) => { const excerptToken: IExcerptToken = { kind: x.kind, text: x.text }; diff --git a/libraries/api-extractor-model/src/items/ApiDocumentedItem.ts b/libraries/api-extractor-model/src/items/ApiDocumentedItem.ts index a51b514e933..3fdce63f3a7 100644 --- a/libraries/api-extractor-model/src/items/ApiDocumentedItem.ts +++ b/libraries/api-extractor-model/src/items/ApiDocumentedItem.ts @@ -2,8 +2,9 @@ // See LICENSE in the project root for license information. import * as tsdoc from '@microsoft/tsdoc'; -import { ApiItem, IApiItemOptions, IApiItemJson } from './ApiItem'; -import { DeserializerContext } from '../model/DeserializerContext'; + +import { ApiItem, type IApiItemOptions, type IApiItemJson } from './ApiItem'; +import type { DeserializerContext } from '../model/DeserializerContext'; /** * Constructor options for {@link ApiDocumentedItem}. @@ -35,8 +36,7 @@ export class ApiDocumentedItem extends ApiItem { this._tsdocComment = options.docComment; } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiItemJson @@ -62,8 +62,7 @@ export class ApiDocumentedItem extends ApiItem { return this._tsdocComment; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); if (this.tsdocComment !== undefined) { jsonObject.docComment = this.tsdocComment.emitAsTsdoc(); diff --git a/libraries/api-extractor-model/src/items/ApiItem.ts b/libraries/api-extractor-model/src/items/ApiItem.ts index 56cb316f5c8..1eb7fb014c6 100644 --- a/libraries/api-extractor-model/src/items/ApiItem.ts +++ b/libraries/api-extractor-model/src/items/ApiItem.ts @@ -1,14 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; -import { Constructor, PropertiesOf } from '../mixins/Mixin'; -import { ApiPackage } from '../model/ApiPackage'; -import { ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; -import { DeserializerContext } from '../model/DeserializerContext'; +import type { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; import { InternalError } from '@rushstack/node-core-library'; + +import type { Constructor, PropertiesOf } from '../mixins/Mixin'; +import type { ApiPackage } from '../model/ApiPackage'; +import { ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; +import type { DeserializerContext } from '../model/DeserializerContext'; import { ApiItemContainerMixin } from '../mixins/ApiItemContainerMixin'; -import { ApiModel } from '../model/ApiModel'; +import type { ApiModel } from '../model/ApiModel'; /** * The type returned by the {@link ApiItem.kind} property, which can be used to easily distinguish subclasses of diff --git a/libraries/api-extractor-model/src/items/ApiPropertyItem.ts b/libraries/api-extractor-model/src/items/ApiPropertyItem.ts index 9a9cfefd650..0fcf373bc65 100644 --- a/libraries/api-extractor-model/src/items/ApiPropertyItem.ts +++ b/libraries/api-extractor-model/src/items/ApiPropertyItem.ts @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { Excerpt, IExcerptTokenRange } from '../mixins/Excerpt'; -import { IApiDeclaredItemOptions, ApiDeclaredItem, IApiDeclaredItemJson } from '../items/ApiDeclaredItem'; -import { ApiReleaseTagMixin, IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; -import { IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; -import { DeserializerContext } from '../model/DeserializerContext'; -import { ApiOptionalMixin, IApiOptionalMixinOptions } from '../mixins/ApiOptionalMixin'; -import { ApiReadonlyMixin, IApiReadonlyMixinOptions } from '../mixins/ApiReadonlyMixin'; +import type { Excerpt, IExcerptTokenRange } from '../mixins/Excerpt'; +import { type IApiDeclaredItemOptions, ApiDeclaredItem, type IApiDeclaredItemJson } from './ApiDeclaredItem'; +import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; +import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; +import type { DeserializerContext } from '../model/DeserializerContext'; +import { ApiOptionalMixin, type IApiOptionalMixinOptions } from '../mixins/ApiOptionalMixin'; +import { ApiReadonlyMixin, type IApiReadonlyMixinOptions } from '../mixins/ApiReadonlyMixin'; /** * Constructor options for {@link ApiPropertyItem}. @@ -45,8 +45,7 @@ export class ApiPropertyItem extends ApiNameMixin( this.propertyTypeExcerpt = this.buildExcerpt(options.propertyTypeTokenRange); } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiPropertyItemJson @@ -73,8 +72,7 @@ export class ApiPropertyItem extends ApiNameMixin( return false; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); jsonObject.propertyTypeTokenRange = this.propertyTypeExcerpt.tokenRange; diff --git a/libraries/api-extractor-model/src/mixins/ApiAbstractMixin.ts b/libraries/api-extractor-model/src/mixins/ApiAbstractMixin.ts index f73aa84cb50..65490537d9e 100644 --- a/libraries/api-extractor-model/src/mixins/ApiAbstractMixin.ts +++ b/libraries/api-extractor-model/src/mixins/ApiAbstractMixin.ts @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information.s +// See LICENSE in the project root for license information. -import { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; -import { DeserializerContext } from '../model/DeserializerContext'; +/* eslint-disable @typescript-eslint/no-redeclare */ + +import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; +import type { DeserializerContext } from '../model/DeserializerContext'; /** * Constructor options for {@link (ApiAbstractMixin:interface)}. @@ -67,8 +69,7 @@ export function ApiAbstractMixin( this[_isAbstract] = options.isAbstract; } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiAbstractMixinJson @@ -82,8 +83,7 @@ export function ApiAbstractMixin( return this[_isAbstract]; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); jsonObject.isAbstract = this.isAbstract; diff --git a/libraries/api-extractor-model/src/mixins/ApiExportedMixin.ts b/libraries/api-extractor-model/src/mixins/ApiExportedMixin.ts index de37c3bb80c..a91360a6b87 100644 --- a/libraries/api-extractor-model/src/mixins/ApiExportedMixin.ts +++ b/libraries/api-extractor-model/src/mixins/ApiExportedMixin.ts @@ -1,9 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information.s +// See LICENSE in the project root for license information. + +/* eslint-disable @typescript-eslint/no-redeclare */ import { DeclarationReference, Navigation } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; -import { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; -import { DeserializerContext } from '../model/DeserializerContext'; + +import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; +import type { DeserializerContext } from '../model/DeserializerContext'; /** * Constructor options for {@link (IApiExportedMixinOptions:interface)}. @@ -64,7 +67,6 @@ export interface ApiExportedMixin extends ApiItem { */ readonly isExported: boolean; - /** @override */ serializeInto(jsonObject: Partial): void; } @@ -91,8 +93,7 @@ export function ApiExportedMixin( this[_isExported] = options.isExported; } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiExportedMixinJson @@ -112,9 +113,8 @@ export function ApiExportedMixin( /** * The `isExported` property is intentionally not serialized because the information is already present * in the item's `canonicalReference`. - * @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); } } diff --git a/libraries/api-extractor-model/src/mixins/ApiInitializerMixin.ts b/libraries/api-extractor-model/src/mixins/ApiInitializerMixin.ts index ea2601fd6b1..fc3bfe876b8 100644 --- a/libraries/api-extractor-model/src/mixins/ApiInitializerMixin.ts +++ b/libraries/api-extractor-model/src/mixins/ApiInitializerMixin.ts @@ -1,11 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information.s +// See LICENSE in the project root for license information. + +/* eslint-disable @typescript-eslint/no-redeclare */ -import { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; -import { IExcerptTokenRange, Excerpt } from './Excerpt'; -import { ApiDeclaredItem } from '../items/ApiDeclaredItem'; import { InternalError } from '@rushstack/node-core-library'; -import { DeserializerContext } from '../model/DeserializerContext'; + +import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; +import type { IExcerptTokenRange, Excerpt } from './Excerpt'; +import { ApiDeclaredItem } from '../items/ApiDeclaredItem'; +import type { DeserializerContext } from '../model/DeserializerContext'; /** * Constructor options for {@link (IApiInitializerMixinOptions:interface)}. @@ -43,7 +46,6 @@ export interface ApiInitializerMixin extends ApiItem { */ readonly initializerExcerpt?: Excerpt; - /** @override */ serializeInto(jsonObject: Partial): void; } @@ -79,8 +81,7 @@ export function ApiInitializerMixin( } } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiInitializerMixinJson @@ -94,8 +95,7 @@ export function ApiInitializerMixin( return this[_initializerExcerpt]; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); // Note that JSON does not support the "undefined" value, so we simply omit the field entirely if it is undefined diff --git a/libraries/api-extractor-model/src/mixins/ApiItemContainerMixin.ts b/libraries/api-extractor-model/src/mixins/ApiItemContainerMixin.ts index 3e28b12c54c..99cde2f3da6 100644 --- a/libraries/api-extractor-model/src/mixins/ApiItemContainerMixin.ts +++ b/libraries/api-extractor-model/src/mixins/ApiItemContainerMixin.ts @@ -1,25 +1,32 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information.s +// See LICENSE in the project root for license information. + +/* eslint-disable @typescript-eslint/no-redeclare */ + +import { InternalError } from '@rushstack/node-core-library'; +import type { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; import { ApiItem, apiItem_onParentChanged, - IApiItemJson, - IApiItemOptions, - IApiItemConstructor, + type IApiItemJson, + type IApiItemOptions, + type IApiItemConstructor, ApiItemKind } from '../items/ApiItem'; import { ApiNameMixin } from './ApiNameMixin'; -import { DeserializerContext } from '../model/DeserializerContext'; -import { ApiModel } from '../model/ApiModel'; -import { ApiClass } from '../model/ApiClass'; -import { ApiInterface } from '../model/ApiInterface'; -import { ExcerptToken, ExcerptTokenKind } from './Excerpt'; -import { IFindApiItemsResult, IFindApiItemsMessage, FindApiItemsMessageId } from './IFindApiItemsResult'; -import { InternalError } from '@rushstack/node-core-library'; -import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; -import { HeritageType } from '../model/HeritageType'; -import { IResolveDeclarationReferenceResult } from '../model/ModelReferenceResolver'; +import type { DeserializerContext } from '../model/DeserializerContext'; +import type { ApiModel } from '../model/ApiModel'; +import type { ApiClass } from '../model/ApiClass'; +import type { ApiInterface } from '../model/ApiInterface'; +import { type ExcerptToken, ExcerptTokenKind } from './Excerpt'; +import { + type IFindApiItemsResult, + type IFindApiItemsMessage, + FindApiItemsMessageId +} from './IFindApiItemsResult'; +import type { HeritageType } from '../model/HeritageType'; +import type { IResolveDeclarationReferenceResult } from '../model/ModelReferenceResolver'; /** * Constructor options for {@link (ApiItemContainerMixin:interface)}. @@ -167,7 +174,6 @@ export interface ApiItemContainerMixin extends ApiItem { */ _getMergedSiblingsForMember(memberApiItem: ApiItem): ReadonlyArray; - /** @override */ serializeInto(jsonObject: Partial): void; } @@ -214,8 +220,7 @@ export function ApiItemContainerMixin( } } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiItemContainerJson @@ -228,8 +233,7 @@ export function ApiItemContainerMixin( } } - /** @override */ - public get members(): ReadonlyArray { + public override get members(): ReadonlyArray { if (!this[_membersSorted] && !this[_preserveMemberOrder]) { this[_members].sort((x, y) => x.getSortKey().localeCompare(y.getSortKey())); this[_membersSorted] = true; @@ -502,8 +506,7 @@ export function ApiItemContainerMixin( } } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); const memberObjects: IApiItemJson[] = []; diff --git a/libraries/api-extractor-model/src/mixins/ApiNameMixin.ts b/libraries/api-extractor-model/src/mixins/ApiNameMixin.ts index f04cd4cce02..61c6c33ab3d 100644 --- a/libraries/api-extractor-model/src/mixins/ApiNameMixin.ts +++ b/libraries/api-extractor-model/src/mixins/ApiNameMixin.ts @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information.s +// See LICENSE in the project root for license information. -import { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; -import { DeserializerContext } from '../model/DeserializerContext'; +/* eslint-disable @typescript-eslint/no-redeclare */ + +import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; +import type { DeserializerContext } from '../model/DeserializerContext'; /** * Constructor options for {@link (IApiNameMixinOptions:interface)}. @@ -44,7 +46,6 @@ export interface ApiNameMixin extends ApiItem { */ readonly name: string; - /** @override */ serializeInto(jsonObject: Partial): void; } @@ -71,8 +72,7 @@ export function ApiNameMixin( this[_name] = options.name; } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiNameMixinJson @@ -86,13 +86,11 @@ export function ApiNameMixin( return this[_name]; } - /** @override */ - public get displayName(): string { + public override get displayName(): string { return this[_name]; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); jsonObject.name = this.name; diff --git a/libraries/api-extractor-model/src/mixins/ApiOptionalMixin.ts b/libraries/api-extractor-model/src/mixins/ApiOptionalMixin.ts index e952404c381..e74f9d884e1 100644 --- a/libraries/api-extractor-model/src/mixins/ApiOptionalMixin.ts +++ b/libraries/api-extractor-model/src/mixins/ApiOptionalMixin.ts @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information.s +// See LICENSE in the project root for license information. -import { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; -import { DeserializerContext } from '../model/DeserializerContext'; +/* eslint-disable @typescript-eslint/no-redeclare */ + +import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; +import type { DeserializerContext } from '../model/DeserializerContext'; /** * Constructor options for {@link (IApiOptionalMixinOptions:interface)}. @@ -49,7 +51,6 @@ export interface ApiOptionalMixin extends ApiItem { */ readonly isOptional: boolean; - /** @override */ serializeInto(jsonObject: Partial): void; } @@ -76,8 +77,7 @@ export function ApiOptionalMixin( this[_isOptional] = !!options.isOptional; } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiOptionalMixinJson @@ -91,8 +91,7 @@ export function ApiOptionalMixin( return this[_isOptional]; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); jsonObject.isOptional = this.isOptional; diff --git a/libraries/api-extractor-model/src/mixins/ApiParameterListMixin.ts b/libraries/api-extractor-model/src/mixins/ApiParameterListMixin.ts index db1ba6970dd..49e651eb792 100644 --- a/libraries/api-extractor-model/src/mixins/ApiParameterListMixin.ts +++ b/libraries/api-extractor-model/src/mixins/ApiParameterListMixin.ts @@ -1,12 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information.s +// See LICENSE in the project root for license information. -import { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; +/* eslint-disable @typescript-eslint/no-redeclare */ + +import { InternalError } from '@rushstack/node-core-library'; + +import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; import { Parameter } from '../model/Parameter'; import { ApiDeclaredItem } from '../items/ApiDeclaredItem'; -import { IExcerptTokenRange } from './Excerpt'; -import { InternalError } from '@rushstack/node-core-library'; -import { DeserializerContext } from '../model/DeserializerContext'; +import type { IExcerptTokenRange } from './Excerpt'; +import type { DeserializerContext } from '../model/DeserializerContext'; /** * Represents parameter information that is part of {@link IApiParameterListMixinOptions} @@ -133,8 +136,7 @@ export function ApiParameterListMixin( } } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiParameterListJson @@ -153,8 +155,7 @@ export function ApiParameterListMixin( return this[_parameters]; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); jsonObject.overloadIndex = this.overloadIndex; diff --git a/libraries/api-extractor-model/src/mixins/ApiProtectedMixin.ts b/libraries/api-extractor-model/src/mixins/ApiProtectedMixin.ts index 78de89e0315..598529784aa 100644 --- a/libraries/api-extractor-model/src/mixins/ApiProtectedMixin.ts +++ b/libraries/api-extractor-model/src/mixins/ApiProtectedMixin.ts @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information.s +// See LICENSE in the project root for license information. -import { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; -import { DeserializerContext } from '../model/DeserializerContext'; +/* eslint-disable @typescript-eslint/no-redeclare */ + +import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; +import type { DeserializerContext } from '../model/DeserializerContext'; /** * Constructor options for {@link (IApiProtectedMixinOptions:interface)}. @@ -40,7 +42,6 @@ export interface ApiProtectedMixin extends ApiItem { */ readonly isProtected: boolean; - /** @override */ serializeInto(jsonObject: Partial): void; } @@ -67,8 +68,7 @@ export function ApiProtectedMixin( this[_isProtected] = options.isProtected; } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiProtectedMixinJson @@ -82,8 +82,7 @@ export function ApiProtectedMixin( return this[_isProtected]; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); jsonObject.isProtected = this.isProtected; diff --git a/libraries/api-extractor-model/src/mixins/ApiReadonlyMixin.ts b/libraries/api-extractor-model/src/mixins/ApiReadonlyMixin.ts index 45de9a603bd..859a3ffcd82 100644 --- a/libraries/api-extractor-model/src/mixins/ApiReadonlyMixin.ts +++ b/libraries/api-extractor-model/src/mixins/ApiReadonlyMixin.ts @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information.s +// See LICENSE in the project root for license information. -import { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; -import { DeserializerContext } from '../model/DeserializerContext'; +/* eslint-disable @typescript-eslint/no-redeclare */ + +import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; +import type { DeserializerContext } from '../model/DeserializerContext'; /** * Constructor options for {@link (ApiReadonlyMixin:interface)}. @@ -89,8 +91,7 @@ export function ApiReadonlyMixin( this[_isReadonly] = options.isReadonly; } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiReadonlyMixinJson @@ -104,8 +105,7 @@ export function ApiReadonlyMixin( return this[_isReadonly]; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); jsonObject.isReadonly = this.isReadonly; diff --git a/libraries/api-extractor-model/src/mixins/ApiReleaseTagMixin.ts b/libraries/api-extractor-model/src/mixins/ApiReleaseTagMixin.ts index 10098489521..3865f6579cd 100644 --- a/libraries/api-extractor-model/src/mixins/ApiReleaseTagMixin.ts +++ b/libraries/api-extractor-model/src/mixins/ApiReleaseTagMixin.ts @@ -1,11 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information.s +// See LICENSE in the project root for license information. + +/* eslint-disable @typescript-eslint/no-redeclare */ import { Enum } from '@rushstack/node-core-library'; -import { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; +import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; import { ReleaseTag } from '../aedoc/ReleaseTag'; -import { DeserializerContext } from '../model/DeserializerContext'; +import type { DeserializerContext } from '../model/DeserializerContext'; /** * Constructor options for {@link (ApiReleaseTagMixin:interface)}. @@ -48,7 +50,6 @@ export interface ApiReleaseTagMixin extends ApiItem { */ readonly releaseTag: ReleaseTag; - /** @override */ serializeInto(jsonObject: Partial): void; } @@ -75,8 +76,7 @@ export function ApiReleaseTagMixin( this[_releaseTag] = options.releaseTag; } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiReleaseTagMixinJson @@ -98,8 +98,7 @@ export function ApiReleaseTagMixin( return this[_releaseTag]; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); jsonObject.releaseTag = ReleaseTag[this.releaseTag]; diff --git a/libraries/api-extractor-model/src/mixins/ApiReturnTypeMixin.ts b/libraries/api-extractor-model/src/mixins/ApiReturnTypeMixin.ts index 0db1c1f5831..493b8e0bfbe 100644 --- a/libraries/api-extractor-model/src/mixins/ApiReturnTypeMixin.ts +++ b/libraries/api-extractor-model/src/mixins/ApiReturnTypeMixin.ts @@ -1,11 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information.s +// See LICENSE in the project root for license information. + +/* eslint-disable @typescript-eslint/no-redeclare */ -import { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; -import { IExcerptTokenRange, Excerpt } from './Excerpt'; -import { ApiDeclaredItem } from '../items/ApiDeclaredItem'; import { InternalError } from '@rushstack/node-core-library'; -import { DeserializerContext } from '../model/DeserializerContext'; + +import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; +import type { IExcerptTokenRange, Excerpt } from './Excerpt'; +import { ApiDeclaredItem } from '../items/ApiDeclaredItem'; +import type { DeserializerContext } from '../model/DeserializerContext'; /** * Constructor options for {@link (ApiReturnTypeMixin:interface)}. @@ -43,7 +46,6 @@ export interface ApiReturnTypeMixin extends ApiItem { */ readonly returnTypeExcerpt: Excerpt; - /** @override */ serializeInto(jsonObject: Partial): void; } @@ -75,8 +77,7 @@ export function ApiReturnTypeMixin( } } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiReturnTypeMixinJson @@ -90,8 +91,7 @@ export function ApiReturnTypeMixin( return this[_returnTypeExcerpt]; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); jsonObject.returnTypeTokenRange = this.returnTypeExcerpt.tokenRange; diff --git a/libraries/api-extractor-model/src/mixins/ApiStaticMixin.ts b/libraries/api-extractor-model/src/mixins/ApiStaticMixin.ts index de2d2b7b60f..cb57f6c0542 100644 --- a/libraries/api-extractor-model/src/mixins/ApiStaticMixin.ts +++ b/libraries/api-extractor-model/src/mixins/ApiStaticMixin.ts @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information.s +// See LICENSE in the project root for license information. -import { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; -import { DeserializerContext } from '../model/DeserializerContext'; +/* eslint-disable @typescript-eslint/no-redeclare */ + +import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; +import type { DeserializerContext } from '../model/DeserializerContext'; /** * Constructor options for {@link (IApiStaticMixinOptions:interface)}. @@ -40,7 +42,6 @@ export interface ApiStaticMixin extends ApiItem { */ readonly isStatic: boolean; - /** @override */ serializeInto(jsonObject: Partial): void; } @@ -67,8 +68,7 @@ export function ApiStaticMixin( this[_isStatic] = options.isStatic; } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiStaticMixinJson @@ -82,8 +82,7 @@ export function ApiStaticMixin( return this[_isStatic]; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); jsonObject.isStatic = this.isStatic; diff --git a/libraries/api-extractor-model/src/mixins/ApiTypeParameterListMixin.ts b/libraries/api-extractor-model/src/mixins/ApiTypeParameterListMixin.ts index 2272af1a9f8..9d938598af1 100644 --- a/libraries/api-extractor-model/src/mixins/ApiTypeParameterListMixin.ts +++ b/libraries/api-extractor-model/src/mixins/ApiTypeParameterListMixin.ts @@ -1,12 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information.s +// See LICENSE in the project root for license information. + +/* eslint-disable @typescript-eslint/no-redeclare */ -import { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; -import { Excerpt, IExcerptTokenRange } from './Excerpt'; -import { TypeParameter } from '../model/TypeParameter'; import { InternalError } from '@rushstack/node-core-library'; + +import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem'; +import type { Excerpt, IExcerptTokenRange } from './Excerpt'; +import { TypeParameter } from '../model/TypeParameter'; import { ApiDeclaredItem } from '../items/ApiDeclaredItem'; -import { DeserializerContext } from '../model/DeserializerContext'; +import type { DeserializerContext } from '../model/DeserializerContext'; /** * Represents parameter information that is part of {@link IApiTypeParameterListMixinOptions} @@ -103,8 +106,7 @@ export function ApiTypeParameterListMixin, context: DeserializerContext, jsonObject: IApiTypeParameterListMixinJson @@ -118,8 +120,7 @@ export function ApiTypeParameterListMixin): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); const typeParameterObjects: IApiTypeParameterOptions[] = []; diff --git a/libraries/api-extractor-model/src/mixins/Excerpt.ts b/libraries/api-extractor-model/src/mixins/Excerpt.ts index 8a02b2fad43..c6060a8448b 100644 --- a/libraries/api-extractor-model/src/mixins/Excerpt.ts +++ b/libraries/api-extractor-model/src/mixins/Excerpt.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; +import type { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; import { Text } from '@rushstack/node-core-library'; /** @public */ diff --git a/libraries/api-extractor-model/src/mixins/IFindApiItemsResult.ts b/libraries/api-extractor-model/src/mixins/IFindApiItemsResult.ts index df720b24925..f5a6df5da2c 100644 --- a/libraries/api-extractor-model/src/mixins/IFindApiItemsResult.ts +++ b/libraries/api-extractor-model/src/mixins/IFindApiItemsResult.ts @@ -1,4 +1,7 @@ -import { ApiItem } from '../items/ApiItem'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { ApiItem } from '../items/ApiItem'; /** * Generic result object for finding API items used by different kinds of find operations. diff --git a/libraries/api-extractor-model/src/model/ApiCallSignature.ts b/libraries/api-extractor-model/src/model/ApiCallSignature.ts index f1edd511333..565cf8530a0 100644 --- a/libraries/api-extractor-model/src/model/ApiCallSignature.ts +++ b/libraries/api-extractor-model/src/model/ApiCallSignature.ts @@ -6,13 +6,14 @@ import { Meaning, Navigation } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; -import { IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem'; -import { IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; -import { IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin'; -import { IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin'; +import { type IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem'; +import { type IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; +import { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin'; +import { type IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin'; import { - IApiTypeParameterListMixinOptions, + type IApiTypeParameterListMixinOptions, ApiTypeParameterListMixin } from '../mixins/ApiTypeParameterListMixin'; @@ -67,18 +68,16 @@ export class ApiCallSignature extends ApiTypeParameterListMixin( return `|${ApiItemKind.CallSignature}|${overloadIndex}`; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.CallSignature; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiCallSignature.getContainerKey(this.overloadIndex); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const parent: DeclarationReference = this.parent ? this.parent.canonicalReference : // .withMeaning() requires some kind of component diff --git a/libraries/api-extractor-model/src/model/ApiClass.ts b/libraries/api-extractor-model/src/model/ApiClass.ts index 6fc51b4bc83..8b260e7d052 100644 --- a/libraries/api-extractor-model/src/model/ApiClass.ts +++ b/libraries/api-extractor-model/src/model/ApiClass.ts @@ -5,30 +5,35 @@ import { DeclarationReference, Meaning, Navigation, - Component + type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; -import { ApiDeclaredItem, IApiDeclaredItemOptions, IApiDeclaredItemJson } from '../items/ApiDeclaredItem'; -import { ApiItemContainerMixin, IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin'; -import { ApiReleaseTagMixin, IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; -import { IExcerptTokenRange } from '../mixins/Excerpt'; +import { + ApiDeclaredItem, + type IApiDeclaredItemOptions, + type IApiDeclaredItemJson +} from '../items/ApiDeclaredItem'; +import { ApiItemContainerMixin, type IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin'; +import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; +import type { IExcerptTokenRange } from '../mixins/Excerpt'; import { HeritageType } from './HeritageType'; -import { IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; +import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; import { ApiTypeParameterListMixin, - IApiTypeParameterListMixinOptions, - IApiTypeParameterListMixinJson + type IApiTypeParameterListMixinOptions, + type IApiTypeParameterListMixinJson } from '../mixins/ApiTypeParameterListMixin'; -import { DeserializerContext } from './DeserializerContext'; +import type { DeserializerContext } from './DeserializerContext'; import { - IApiExportedMixinJson, - IApiExportedMixinOptions, + type IApiExportedMixinJson, + type IApiExportedMixinOptions, ApiExportedMixin } from '../mixins/ApiExportedMixin'; import { ApiAbstractMixin, - IApiAbstractMixinJson, - IApiAbstractMixinOptions + type IApiAbstractMixinJson, + type IApiAbstractMixinOptions } from '../mixins/ApiAbstractMixin'; /** @@ -102,8 +107,7 @@ export class ApiClass extends ApiItemContainerMixin( return `${name}|${ApiItemKind.Class}`; } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiClassJson @@ -114,13 +118,11 @@ export class ApiClass extends ApiItemContainerMixin( options.implementsTokenRanges = jsonObject.implementsTokenRanges; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.Class; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiClass.getContainerKey(this.name); } @@ -131,8 +133,7 @@ export class ApiClass extends ApiItemContainerMixin( return this._implementsTypes; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); // Note that JSON does not support the "undefined" value, so we simply omit the field entirely if it is undefined @@ -143,8 +144,8 @@ export class ApiClass extends ApiItemContainerMixin( jsonObject.implementsTokenRanges = this.implementsTypes.map((x) => x.excerpt.tokenRange); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const nameComponent: Component = DeclarationReference.parseComponent(this.name); const navigation: Navigation = this.isExported ? Navigation.Exports : Navigation.Locals; return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty()) diff --git a/libraries/api-extractor-model/src/model/ApiConstructSignature.ts b/libraries/api-extractor-model/src/model/ApiConstructSignature.ts index bb806487387..6d432a9830f 100644 --- a/libraries/api-extractor-model/src/model/ApiConstructSignature.ts +++ b/libraries/api-extractor-model/src/model/ApiConstructSignature.ts @@ -6,14 +6,15 @@ import { Meaning, Navigation } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; -import { IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem'; -import { IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; -import { IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin'; -import { IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin'; +import { type IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem'; +import { type IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; +import { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin'; +import { type IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin'; import { ApiTypeParameterListMixin, - IApiTypeParameterListMixinOptions + type IApiTypeParameterListMixinOptions } from '../mixins/ApiTypeParameterListMixin'; /** @@ -80,18 +81,16 @@ export class ApiConstructSignature extends ApiTypeParameterListMixin( return `|${ApiItemKind.ConstructSignature}|${overloadIndex}`; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.ConstructSignature; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiConstructSignature.getContainerKey(this.overloadIndex); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const parent: DeclarationReference = this.parent ? this.parent.canonicalReference : // .withMeaning() requires some kind of component diff --git a/libraries/api-extractor-model/src/model/ApiConstructor.ts b/libraries/api-extractor-model/src/model/ApiConstructor.ts index c25a7e1e4a5..2c03fcf8470 100644 --- a/libraries/api-extractor-model/src/model/ApiConstructor.ts +++ b/libraries/api-extractor-model/src/model/ApiConstructor.ts @@ -6,11 +6,12 @@ import { Meaning, Navigation } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; -import { IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem'; -import { IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; -import { ApiProtectedMixin, IApiProtectedMixinOptions } from '../mixins/ApiProtectedMixin'; -import { IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin'; +import { type IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem'; +import { type IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; +import { ApiProtectedMixin, type IApiProtectedMixinOptions } from '../mixins/ApiProtectedMixin'; +import { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin'; /** * Constructor options for {@link ApiConstructor}. @@ -60,18 +61,16 @@ export class ApiConstructor extends ApiParameterListMixin( return `|${ApiItemKind.Constructor}|${overloadIndex}`; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.Constructor; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiConstructor.getContainerKey(this.overloadIndex); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const parent: DeclarationReference = this.parent ? this.parent.canonicalReference : // .withMeaning() requires some kind of component diff --git a/libraries/api-extractor-model/src/model/ApiEntryPoint.ts b/libraries/api-extractor-model/src/model/ApiEntryPoint.ts index 8fa2c4fc311..4e70d282a64 100644 --- a/libraries/api-extractor-model/src/model/ApiEntryPoint.ts +++ b/libraries/api-extractor-model/src/model/ApiEntryPoint.ts @@ -2,9 +2,10 @@ // See LICENSE in the project root for license information. import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItem, ApiItemKind } from '../items/ApiItem'; -import { ApiItemContainerMixin, IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin'; -import { IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; +import { ApiItemContainerMixin, type IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin'; +import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; import { ApiPackage } from './ApiPackage'; /** @@ -45,13 +46,11 @@ export class ApiEntryPoint extends ApiItemContainerMixin(ApiNameMixin(ApiItem)) super(options); } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.EntryPoint; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { // No prefix needed, because ApiEntryPoint is the only possible member of an ApiPackage return this.name; } @@ -72,8 +71,8 @@ export class ApiEntryPoint extends ApiItemContainerMixin(ApiNameMixin(ApiItem)) return this.name; } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { if (this.parent instanceof ApiPackage) { return DeclarationReference.package(this.parent.name, this.importPath); } diff --git a/libraries/api-extractor-model/src/model/ApiEnum.ts b/libraries/api-extractor-model/src/model/ApiEnum.ts index f58a6d91272..923c1869463 100644 --- a/libraries/api-extractor-model/src/model/ApiEnum.ts +++ b/libraries/api-extractor-model/src/model/ApiEnum.ts @@ -5,15 +5,16 @@ import { DeclarationReference, Meaning, Navigation, - Component + type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; -import { ApiDeclaredItem, IApiDeclaredItemOptions } from '../items/ApiDeclaredItem'; -import { ApiReleaseTagMixin, IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; -import { ApiItemContainerMixin, IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin'; -import { ApiEnumMember } from './ApiEnumMember'; -import { IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; -import { IApiExportedMixinOptions, ApiExportedMixin } from '../mixins/ApiExportedMixin'; +import { ApiDeclaredItem, type IApiDeclaredItemOptions } from '../items/ApiDeclaredItem'; +import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; +import { ApiItemContainerMixin, type IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin'; +import type { ApiEnumMember } from './ApiEnumMember'; +import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; +import { type IApiExportedMixinOptions, ApiExportedMixin } from '../mixins/ApiExportedMixin'; /** * Constructor options for {@link ApiEnum}. @@ -57,31 +58,27 @@ export class ApiEnum extends ApiItemContainerMixin( return `${name}|${ApiItemKind.Enum}`; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.Enum; } - /** @override */ - public get members(): ReadonlyArray { + public override get members(): ReadonlyArray { return super.members as ReadonlyArray; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiEnum.getContainerKey(this.name); } - /** @override */ - public addMember(member: ApiEnumMember): void { + public override addMember(member: ApiEnumMember): void { if (member.kind !== ApiItemKind.EnumMember) { throw new Error('Only ApiEnumMember objects can be added to an ApiEnum'); } super.addMember(member); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const nameComponent: Component = DeclarationReference.parseComponent(this.name); const navigation: Navigation = this.isExported ? Navigation.Exports : Navigation.Locals; return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty()) diff --git a/libraries/api-extractor-model/src/model/ApiEnumMember.ts b/libraries/api-extractor-model/src/model/ApiEnumMember.ts index 703f1a7b4dc..a57f5b2d1d3 100644 --- a/libraries/api-extractor-model/src/model/ApiEnumMember.ts +++ b/libraries/api-extractor-model/src/model/ApiEnumMember.ts @@ -5,13 +5,14 @@ import { DeclarationReference, Meaning, Navigation, - Component + type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; -import { ApiDeclaredItem, IApiDeclaredItemOptions } from '../items/ApiDeclaredItem'; -import { ApiReleaseTagMixin, IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; -import { IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; -import { ApiInitializerMixin, IApiInitializerMixinOptions } from '../mixins/ApiInitializerMixin'; +import { ApiDeclaredItem, type IApiDeclaredItemOptions } from '../items/ApiDeclaredItem'; +import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; +import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; +import { ApiInitializerMixin, type IApiInitializerMixinOptions } from '../mixins/ApiInitializerMixin'; /** * Constructor options for {@link ApiEnumMember}. @@ -81,18 +82,16 @@ export class ApiEnumMember extends ApiNameMixin(ApiReleaseTagMixin(ApiInitialize return name; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.EnumMember; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiEnumMember.getContainerKey(this.name); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const nameComponent: Component = DeclarationReference.parseComponent(this.name); return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty()) .addNavigationStep(Navigation.Exports, nameComponent) diff --git a/libraries/api-extractor-model/src/model/ApiFunction.ts b/libraries/api-extractor-model/src/model/ApiFunction.ts index 9bcba681ca4..413522360ea 100644 --- a/libraries/api-extractor-model/src/model/ApiFunction.ts +++ b/libraries/api-extractor-model/src/model/ApiFunction.ts @@ -5,19 +5,20 @@ import { DeclarationReference, Meaning, Navigation, - Component + type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; -import { IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem'; -import { IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; -import { IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin'; -import { IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin'; -import { IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; +import { type IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem'; +import { type IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; +import { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin'; +import { type IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin'; +import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; import { - IApiTypeParameterListMixinOptions, + type IApiTypeParameterListMixinOptions, ApiTypeParameterListMixin } from '../mixins/ApiTypeParameterListMixin'; -import { IApiExportedMixinOptions, ApiExportedMixin } from '../mixins/ApiExportedMixin'; +import { type IApiExportedMixinOptions, ApiExportedMixin } from '../mixins/ApiExportedMixin'; /** * Constructor options for {@link ApiFunction}. @@ -66,18 +67,16 @@ export class ApiFunction extends ApiNameMixin( return `${name}|${ApiItemKind.Function}|${overloadIndex}`; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.Function; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiFunction.getContainerKey(this.name, this.overloadIndex); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const nameComponent: Component = DeclarationReference.parseComponent(this.name); const navigation: Navigation = this.isExported ? Navigation.Exports : Navigation.Locals; return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty()) diff --git a/libraries/api-extractor-model/src/model/ApiIndexSignature.ts b/libraries/api-extractor-model/src/model/ApiIndexSignature.ts index e30925d43d0..dbae05672fb 100644 --- a/libraries/api-extractor-model/src/model/ApiIndexSignature.ts +++ b/libraries/api-extractor-model/src/model/ApiIndexSignature.ts @@ -6,12 +6,13 @@ import { Meaning, Navigation } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; -import { IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem'; -import { IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; -import { IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin'; -import { IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin'; -import { IApiReadonlyMixinOptions, ApiReadonlyMixin } from '../mixins/ApiReadonlyMixin'; +import { type IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem'; +import { type IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; +import { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin'; +import { type IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin'; +import { type IApiReadonlyMixinOptions, ApiReadonlyMixin } from '../mixins/ApiReadonlyMixin'; /** * Constructor options for {@link ApiIndexSignature}. @@ -57,18 +58,16 @@ export class ApiIndexSignature extends ApiParameterListMixin( return `|${ApiItemKind.IndexSignature}|${overloadIndex}`; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.IndexSignature; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiIndexSignature.getContainerKey(this.overloadIndex); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const parent: DeclarationReference = this.parent ? this.parent.canonicalReference : // .withMeaning() requires some kind of component diff --git a/libraries/api-extractor-model/src/model/ApiInterface.ts b/libraries/api-extractor-model/src/model/ApiInterface.ts index 21bb42b806b..d327c00e3d8 100644 --- a/libraries/api-extractor-model/src/model/ApiInterface.ts +++ b/libraries/api-extractor-model/src/model/ApiInterface.ts @@ -5,32 +5,37 @@ import { DeclarationReference, Meaning, Navigation, - Component + type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; import { ApiItemContainerMixin, - IApiItemContainerMixinOptions, - IApiItemContainerJson + type IApiItemContainerMixinOptions, + type IApiItemContainerJson } from '../mixins/ApiItemContainerMixin'; -import { ApiDeclaredItem, IApiDeclaredItemOptions, IApiDeclaredItemJson } from '../items/ApiDeclaredItem'; import { - IApiReleaseTagMixinOptions, + ApiDeclaredItem, + type IApiDeclaredItemOptions, + type IApiDeclaredItemJson +} from '../items/ApiDeclaredItem'; +import { + type IApiReleaseTagMixinOptions, ApiReleaseTagMixin, - IApiReleaseTagMixinJson + type IApiReleaseTagMixinJson } from '../mixins/ApiReleaseTagMixin'; -import { IExcerptTokenRange } from '../mixins/Excerpt'; +import type { IExcerptTokenRange } from '../mixins/Excerpt'; import { HeritageType } from './HeritageType'; -import { IApiNameMixinOptions, ApiNameMixin, IApiNameMixinJson } from '../mixins/ApiNameMixin'; +import { type IApiNameMixinOptions, ApiNameMixin, type IApiNameMixinJson } from '../mixins/ApiNameMixin'; import { - IApiTypeParameterListMixinOptions, - IApiTypeParameterListMixinJson, + type IApiTypeParameterListMixinOptions, + type IApiTypeParameterListMixinJson, ApiTypeParameterListMixin } from '../mixins/ApiTypeParameterListMixin'; -import { DeserializerContext } from './DeserializerContext'; +import type { DeserializerContext } from './DeserializerContext'; import { - IApiExportedMixinJson, - IApiExportedMixinOptions, + type IApiExportedMixinJson, + type IApiExportedMixinOptions, ApiExportedMixin } from '../mixins/ApiExportedMixin'; @@ -92,8 +97,7 @@ export class ApiInterface extends ApiItemContainerMixin( return `${name}|${ApiItemKind.Interface}`; } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiInterfaceJson @@ -103,13 +107,11 @@ export class ApiInterface extends ApiItemContainerMixin( options.extendsTokenRanges = jsonObject.extendsTokenRanges; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.Interface; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiInterface.getContainerKey(this.name); } @@ -120,15 +122,14 @@ export class ApiInterface extends ApiItemContainerMixin( return this._extendsTypes; } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); jsonObject.extendsTokenRanges = this.extendsTypes.map((x) => x.excerpt.tokenRange); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const nameComponent: Component = DeclarationReference.parseComponent(this.name); const navigation: Navigation = this.isExported ? Navigation.Exports : Navigation.Locals; return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty()) diff --git a/libraries/api-extractor-model/src/model/ApiMethod.ts b/libraries/api-extractor-model/src/model/ApiMethod.ts index ea3a5fd576d..3937330fb8c 100644 --- a/libraries/api-extractor-model/src/model/ApiMethod.ts +++ b/libraries/api-extractor-model/src/model/ApiMethod.ts @@ -5,22 +5,23 @@ import { DeclarationReference, Meaning, Navigation, - Component + type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; -import { ApiProtectedMixin, IApiProtectedMixinOptions } from '../mixins/ApiProtectedMixin'; -import { ApiStaticMixin, IApiStaticMixinOptions } from '../mixins/ApiStaticMixin'; -import { IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem'; -import { IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; -import { IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin'; -import { ApiReturnTypeMixin, IApiReturnTypeMixinOptions } from '../mixins/ApiReturnTypeMixin'; -import { IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; -import { IApiAbstractMixinOptions, ApiAbstractMixin } from '../mixins/ApiAbstractMixin'; +import { ApiProtectedMixin, type IApiProtectedMixinOptions } from '../mixins/ApiProtectedMixin'; +import { ApiStaticMixin, type IApiStaticMixinOptions } from '../mixins/ApiStaticMixin'; +import { type IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem'; +import { type IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; +import { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin'; +import { ApiReturnTypeMixin, type IApiReturnTypeMixinOptions } from '../mixins/ApiReturnTypeMixin'; +import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; +import { type IApiAbstractMixinOptions, ApiAbstractMixin } from '../mixins/ApiAbstractMixin'; import { ApiTypeParameterListMixin, - IApiTypeParameterListMixinOptions + type IApiTypeParameterListMixinOptions } from '../mixins/ApiTypeParameterListMixin'; -import { ApiOptionalMixin, IApiOptionalMixinOptions } from '../mixins/ApiOptionalMixin'; +import { ApiOptionalMixin, type IApiOptionalMixinOptions } from '../mixins/ApiOptionalMixin'; /** * Constructor options for {@link ApiMethod}. @@ -82,18 +83,16 @@ export class ApiMethod extends ApiNameMixin( } } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.Method; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiMethod.getContainerKey(this.name, this.isStatic, this.overloadIndex); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const nameComponent: Component = DeclarationReference.parseComponent(this.name); return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty()) .addNavigationStep(this.isStatic ? Navigation.Exports : Navigation.Members, nameComponent) diff --git a/libraries/api-extractor-model/src/model/ApiMethodSignature.ts b/libraries/api-extractor-model/src/model/ApiMethodSignature.ts index caf8541b028..4c31cf1d041 100644 --- a/libraries/api-extractor-model/src/model/ApiMethodSignature.ts +++ b/libraries/api-extractor-model/src/model/ApiMethodSignature.ts @@ -5,19 +5,20 @@ import { DeclarationReference, Meaning, Navigation, - Component + type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; -import { ApiDeclaredItem, IApiDeclaredItemOptions } from '../items/ApiDeclaredItem'; -import { ApiParameterListMixin, IApiParameterListMixinOptions } from '../mixins/ApiParameterListMixin'; -import { ApiReleaseTagMixin, IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; -import { IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin'; -import { IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; +import { ApiDeclaredItem, type IApiDeclaredItemOptions } from '../items/ApiDeclaredItem'; +import { ApiParameterListMixin, type IApiParameterListMixinOptions } from '../mixins/ApiParameterListMixin'; +import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; +import { type IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin'; +import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; import { - IApiTypeParameterListMixinOptions, + type IApiTypeParameterListMixinOptions, ApiTypeParameterListMixin } from '../mixins/ApiTypeParameterListMixin'; -import { ApiOptionalMixin, IApiOptionalMixinOptions } from '../mixins/ApiOptionalMixin'; +import { ApiOptionalMixin, type IApiOptionalMixinOptions } from '../mixins/ApiOptionalMixin'; /** @public */ export interface IApiMethodSignatureOptions @@ -63,18 +64,16 @@ export class ApiMethodSignature extends ApiNameMixin( return `${name}|${ApiItemKind.MethodSignature}|${overloadIndex}`; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.MethodSignature; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiMethodSignature.getContainerKey(this.name, this.overloadIndex); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const nameComponent: Component = DeclarationReference.parseComponent(this.name); return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty()) .addNavigationStep(Navigation.Members, nameComponent) diff --git a/libraries/api-extractor-model/src/model/ApiModel.ts b/libraries/api-extractor-model/src/model/ApiModel.ts index 272aa038e79..2e7da02319a 100644 --- a/libraries/api-extractor-model/src/model/ApiModel.ts +++ b/libraries/api-extractor-model/src/model/ApiModel.ts @@ -2,12 +2,13 @@ // See LICENSE in the project root for license information. import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; +import { PackageName } from '@rushstack/node-core-library'; +import { DocDeclarationReference } from '@microsoft/tsdoc'; + import { ApiItem, ApiItemKind } from '../items/ApiItem'; import { ApiItemContainerMixin } from '../mixins/ApiItemContainerMixin'; import { ApiPackage } from './ApiPackage'; -import { PackageName } from '@rushstack/node-core-library'; -import { ModelReferenceResolver, IResolveDeclarationReferenceResult } from './ModelReferenceResolver'; -import { DocDeclarationReference } from '@microsoft/tsdoc'; +import { ModelReferenceResolver, type IResolveDeclarationReferenceResult } from './ModelReferenceResolver'; /** * A serializable representation of a collection of API declarations. @@ -66,13 +67,11 @@ export class ApiModel extends ApiItemContainerMixin(ApiItem) { return apiPackage; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.Model; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ''; } @@ -80,8 +79,7 @@ export class ApiModel extends ApiItemContainerMixin(ApiItem) { return this.members as ReadonlyArray; } - /** @override */ - public addMember(member: ApiPackage): void { + public override addMember(member: ApiPackage): void { if (member.kind !== ApiItemKind.Package) { throw new Error('Only items of type ApiPackage may be added to an ApiModel'); } @@ -193,8 +191,8 @@ export class ApiModel extends ApiItemContainerMixin(ApiItem) { } } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { return DeclarationReference.empty(); } } diff --git a/libraries/api-extractor-model/src/model/ApiNamespace.ts b/libraries/api-extractor-model/src/model/ApiNamespace.ts index 49281089332..81ce412c7e3 100644 --- a/libraries/api-extractor-model/src/model/ApiNamespace.ts +++ b/libraries/api-extractor-model/src/model/ApiNamespace.ts @@ -5,14 +5,15 @@ import { DeclarationReference, Meaning, Navigation, - Component + type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; -import { ApiItemContainerMixin, IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin'; -import { IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem'; -import { ApiReleaseTagMixin, IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; -import { IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; -import { IApiExportedMixinOptions, ApiExportedMixin } from '../mixins/ApiExportedMixin'; +import { ApiItemContainerMixin, type IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin'; +import { type IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem'; +import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; +import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; +import { type IApiExportedMixinOptions, ApiExportedMixin } from '../mixins/ApiExportedMixin'; /** * Constructor options for {@link ApiClass}. @@ -58,18 +59,16 @@ export class ApiNamespace extends ApiItemContainerMixin( return `${name}|${ApiItemKind.Namespace}`; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.Namespace; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiNamespace.getContainerKey(this.name); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const nameComponent: Component = DeclarationReference.parseComponent(this.name); const navigation: Navigation = this.isExported ? Navigation.Exports : Navigation.Locals; return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty()) diff --git a/libraries/api-extractor-model/src/model/ApiPackage.ts b/libraries/api-extractor-model/src/model/ApiPackage.ts index 01ea7a86af7..aa9dc373cd6 100644 --- a/libraries/api-extractor-model/src/model/ApiPackage.ts +++ b/libraries/api-extractor-model/src/model/ApiPackage.ts @@ -2,22 +2,23 @@ // See LICENSE in the project root for license information. import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; -import { ApiItem, ApiItemKind, IApiItemJson } from '../items/ApiItem'; -import { ApiItemContainerMixin, IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin'; import { JsonFile, - IJsonFileSaveOptions, + type IJsonFileSaveOptions, PackageJsonLookup, - IPackageJson, - JsonObject + type IPackageJson, + type JsonObject } from '@rushstack/node-core-library'; -import { ApiDocumentedItem, IApiDocumentedItemOptions } from '../items/ApiDocumentedItem'; -import { ApiEntryPoint } from './ApiEntryPoint'; -import { IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; -import { DeserializerContext, ApiJsonSchemaVersion } from './DeserializerContext'; import { TSDocConfiguration } from '@microsoft/tsdoc'; import { TSDocConfigFile } from '@microsoft/tsdoc-config'; +import { ApiItem, ApiItemKind, type IApiItemJson } from '../items/ApiItem'; +import { ApiItemContainerMixin, type IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin'; +import { ApiDocumentedItem, type IApiDocumentedItemOptions } from '../items/ApiDocumentedItem'; +import type { ApiEntryPoint } from './ApiEntryPoint'; +import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; +import { DeserializerContext, ApiJsonSchemaVersion } from './DeserializerContext'; + /** * Constructor options for {@link ApiPackage}. * @public @@ -139,8 +140,7 @@ export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumented this._projectFolderUrl = options.projectFolderUrl; } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiPackageJson @@ -222,13 +222,11 @@ export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumented return ApiItem.deserialize(jsonObject, context) as ApiPackage; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.Package; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { // No prefix needed, because ApiPackage is the only possible member of an ApiModel return this.name; } @@ -253,8 +251,7 @@ export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumented return this._projectFolderUrl; } - /** @override */ - public addMember(member: ApiEntryPoint): void { + public override addMember(member: ApiEntryPoint): void { if (member.kind !== ApiItemKind.EntryPoint) { throw new Error('Only items of type ApiEntryPoint may be added to an ApiPackage'); } @@ -295,8 +292,8 @@ export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumented JsonFile.save(jsonObject, apiJsonFilename, options); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { return DeclarationReference.package(this.name); } } diff --git a/libraries/api-extractor-model/src/model/ApiProperty.ts b/libraries/api-extractor-model/src/model/ApiProperty.ts index f198a96dd5b..e113cb05e08 100644 --- a/libraries/api-extractor-model/src/model/ApiProperty.ts +++ b/libraries/api-extractor-model/src/model/ApiProperty.ts @@ -5,14 +5,15 @@ import { DeclarationReference, Meaning, Navigation, - Component + type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; -import { ApiAbstractMixin, IApiAbstractMixinOptions } from '../mixins/ApiAbstractMixin'; -import { ApiProtectedMixin, IApiProtectedMixinOptions } from '../mixins/ApiProtectedMixin'; -import { ApiStaticMixin, IApiStaticMixinOptions } from '../mixins/ApiStaticMixin'; -import { ApiInitializerMixin, IApiInitializerMixinOptions } from '../mixins/ApiInitializerMixin'; -import { ApiPropertyItem, IApiPropertyItemOptions } from '../items/ApiPropertyItem'; +import { ApiAbstractMixin, type IApiAbstractMixinOptions } from '../mixins/ApiAbstractMixin'; +import { ApiProtectedMixin, type IApiProtectedMixinOptions } from '../mixins/ApiProtectedMixin'; +import { ApiStaticMixin, type IApiStaticMixinOptions } from '../mixins/ApiStaticMixin'; +import { ApiInitializerMixin, type IApiInitializerMixinOptions } from '../mixins/ApiInitializerMixin'; +import { ApiPropertyItem, type IApiPropertyItemOptions } from '../items/ApiPropertyItem'; /** * Constructor options for {@link ApiProperty}. @@ -74,18 +75,16 @@ export class ApiProperty extends ApiAbstractMixin( } } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.Property; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiProperty.getContainerKey(this.name, this.isStatic); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const nameComponent: Component = DeclarationReference.parseComponent(this.name); return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty()) .addNavigationStep(this.isStatic ? Navigation.Exports : Navigation.Members, nameComponent) diff --git a/libraries/api-extractor-model/src/model/ApiPropertySignature.ts b/libraries/api-extractor-model/src/model/ApiPropertySignature.ts index 6a5c561f840..47d82884337 100644 --- a/libraries/api-extractor-model/src/model/ApiPropertySignature.ts +++ b/libraries/api-extractor-model/src/model/ApiPropertySignature.ts @@ -5,10 +5,11 @@ import { DeclarationReference, Meaning, Navigation, - Component + type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; -import { ApiPropertyItem, IApiPropertyItemOptions } from '../items/ApiPropertyItem'; +import { ApiPropertyItem, type IApiPropertyItemOptions } from '../items/ApiPropertyItem'; /** * Constructor options for {@link ApiPropertySignature}. @@ -47,18 +48,16 @@ export class ApiPropertySignature extends ApiPropertyItem { return `${name}|${ApiItemKind.PropertySignature}`; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.PropertySignature; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiPropertySignature.getContainerKey(this.name); } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const nameComponent: Component = DeclarationReference.parseComponent(this.name); return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty()) .addNavigationStep(Navigation.Members, nameComponent) diff --git a/libraries/api-extractor-model/src/model/ApiTypeAlias.ts b/libraries/api-extractor-model/src/model/ApiTypeAlias.ts index 71f018ef259..511d2bc0266 100644 --- a/libraries/api-extractor-model/src/model/ApiTypeAlias.ts +++ b/libraries/api-extractor-model/src/model/ApiTypeAlias.ts @@ -5,22 +5,27 @@ import { DeclarationReference, Meaning, Navigation, - Component + type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; -import { Excerpt, IExcerptTokenRange } from '../mixins/Excerpt'; + +import type { Excerpt, IExcerptTokenRange } from '../mixins/Excerpt'; import { ApiItemKind } from '../items/ApiItem'; -import { ApiDeclaredItem, IApiDeclaredItemOptions, IApiDeclaredItemJson } from '../items/ApiDeclaredItem'; -import { ApiReleaseTagMixin, IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; -import { IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; +import { + ApiDeclaredItem, + type IApiDeclaredItemOptions, + type IApiDeclaredItemJson +} from '../items/ApiDeclaredItem'; +import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; +import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; import { ApiTypeParameterListMixin, - IApiTypeParameterListMixinOptions, - IApiTypeParameterListMixinJson + type IApiTypeParameterListMixinOptions, + type IApiTypeParameterListMixinJson } from '../mixins/ApiTypeParameterListMixin'; -import { DeserializerContext } from './DeserializerContext'; +import type { DeserializerContext } from './DeserializerContext'; import { - IApiExportedMixinJson, - IApiExportedMixinOptions, + type IApiExportedMixinJson, + type IApiExportedMixinOptions, ApiExportedMixin } from '../mixins/ApiExportedMixin'; @@ -92,8 +97,7 @@ export class ApiTypeAlias extends ApiTypeParameterListMixin( this.typeExcerpt = this.buildExcerpt(options.typeTokenRange); } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiTypeAliasJson @@ -107,25 +111,22 @@ export class ApiTypeAlias extends ApiTypeParameterListMixin( return `${name}|${ApiItemKind.TypeAlias}`; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.TypeAlias; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiTypeAlias.getContainerKey(this.name); } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); jsonObject.typeTokenRange = this.typeExcerpt.tokenRange; } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const nameComponent: Component = DeclarationReference.parseComponent(this.name); const navigation: Navigation = this.isExported ? Navigation.Exports : Navigation.Locals; return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty()) diff --git a/libraries/api-extractor-model/src/model/ApiVariable.ts b/libraries/api-extractor-model/src/model/ApiVariable.ts index a3ac4b9614e..a12369080e9 100644 --- a/libraries/api-extractor-model/src/model/ApiVariable.ts +++ b/libraries/api-extractor-model/src/model/ApiVariable.ts @@ -5,19 +5,24 @@ import { DeclarationReference, Meaning, Navigation, - Component + type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; + import { ApiItemKind } from '../items/ApiItem'; -import { ApiDeclaredItem, IApiDeclaredItemOptions, IApiDeclaredItemJson } from '../items/ApiDeclaredItem'; -import { ApiReleaseTagMixin, IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; -import { ApiReadonlyMixin, IApiReadonlyMixinOptions } from '../mixins/ApiReadonlyMixin'; -import { IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; -import { ApiInitializerMixin, IApiInitializerMixinOptions } from '../mixins/ApiInitializerMixin'; -import { IExcerptTokenRange, Excerpt } from '../mixins/Excerpt'; -import { DeserializerContext } from './DeserializerContext'; import { - IApiExportedMixinJson, - IApiExportedMixinOptions, + ApiDeclaredItem, + type IApiDeclaredItemOptions, + type IApiDeclaredItemJson +} from '../items/ApiDeclaredItem'; +import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin'; +import { ApiReadonlyMixin, type IApiReadonlyMixinOptions } from '../mixins/ApiReadonlyMixin'; +import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin'; +import { ApiInitializerMixin, type IApiInitializerMixinOptions } from '../mixins/ApiInitializerMixin'; +import type { IExcerptTokenRange, Excerpt } from '../mixins/Excerpt'; +import type { DeserializerContext } from './DeserializerContext'; +import { + type IApiExportedMixinJson, + type IApiExportedMixinOptions, ApiExportedMixin } from '../mixins/ApiExportedMixin'; @@ -73,8 +78,7 @@ export class ApiVariable extends ApiNameMixin( this.variableTypeExcerpt = this.buildExcerpt(options.variableTypeTokenRange); } - /** @override */ - public static onDeserializeInto( + public static override onDeserializeInto( options: Partial, context: DeserializerContext, jsonObject: IApiVariableJson @@ -88,25 +92,22 @@ export class ApiVariable extends ApiNameMixin( return `${name}|${ApiItemKind.Variable}`; } - /** @override */ - public get kind(): ApiItemKind { + public override get kind(): ApiItemKind { return ApiItemKind.Variable; } - /** @override */ - public get containerKey(): string { + public override get containerKey(): string { return ApiVariable.getContainerKey(this.name); } - /** @override */ - public serializeInto(jsonObject: Partial): void { + public override serializeInto(jsonObject: Partial): void { super.serializeInto(jsonObject); jsonObject.variableTypeTokenRange = this.variableTypeExcerpt.tokenRange; } - /** @beta @override */ - public buildCanonicalReference(): DeclarationReference { + /** @beta */ + public override buildCanonicalReference(): DeclarationReference { const nameComponent: Component = DeclarationReference.parseComponent(this.name); const navigation: Navigation = this.isExported ? Navigation.Exports : Navigation.Locals; return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty()) diff --git a/libraries/api-extractor-model/src/model/Deserializer.ts b/libraries/api-extractor-model/src/model/Deserializer.ts index 459e80319b1..c82955286f8 100644 --- a/libraries/api-extractor-model/src/model/Deserializer.ts +++ b/libraries/api-extractor-model/src/model/Deserializer.ts @@ -1,29 +1,29 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IApiItemJson, IApiItemOptions, ApiItem, ApiItemKind } from '../items/ApiItem'; -import { ApiClass, IApiClassOptions, IApiClassJson } from './ApiClass'; -import { ApiEntryPoint, IApiEntryPointOptions } from './ApiEntryPoint'; -import { ApiMethod, IApiMethodOptions } from './ApiMethod'; +import { type IApiItemJson, type IApiItemOptions, type ApiItem, ApiItemKind } from '../items/ApiItem'; +import { ApiClass, type IApiClassOptions, type IApiClassJson } from './ApiClass'; +import { ApiEntryPoint, type IApiEntryPointOptions } from './ApiEntryPoint'; +import { ApiMethod, type IApiMethodOptions } from './ApiMethod'; import { ApiModel } from './ApiModel'; -import { ApiNamespace, IApiNamespaceOptions } from './ApiNamespace'; -import { ApiPackage, IApiPackageOptions, IApiPackageJson } from './ApiPackage'; -import { ApiInterface, IApiInterfaceOptions, IApiInterfaceJson } from './ApiInterface'; -import { ApiPropertySignature, IApiPropertySignatureOptions } from './ApiPropertySignature'; -import { ApiMethodSignature, IApiMethodSignatureOptions } from './ApiMethodSignature'; -import { ApiProperty, IApiPropertyOptions } from './ApiProperty'; -import { ApiEnumMember, IApiEnumMemberOptions } from './ApiEnumMember'; -import { ApiEnum, IApiEnumOptions } from './ApiEnum'; -import { IApiPropertyItemJson } from '../items/ApiPropertyItem'; -import { ApiConstructor, IApiConstructorOptions } from './ApiConstructor'; -import { ApiConstructSignature, IApiConstructSignatureOptions } from './ApiConstructSignature'; -import { ApiFunction, IApiFunctionOptions } from './ApiFunction'; -import { ApiCallSignature, IApiCallSignatureOptions } from './ApiCallSignature'; -import { ApiIndexSignature, IApiIndexSignatureOptions } from './ApiIndexSignature'; -import { ApiTypeAlias, IApiTypeAliasOptions, IApiTypeAliasJson } from './ApiTypeAlias'; -import { ApiVariable, IApiVariableOptions, IApiVariableJson } from './ApiVariable'; -import { IApiDeclaredItemJson } from '../items/ApiDeclaredItem'; -import { DeserializerContext } from './DeserializerContext'; +import { ApiNamespace, type IApiNamespaceOptions } from './ApiNamespace'; +import { ApiPackage, type IApiPackageOptions, type IApiPackageJson } from './ApiPackage'; +import { ApiInterface, type IApiInterfaceOptions, type IApiInterfaceJson } from './ApiInterface'; +import { ApiPropertySignature, type IApiPropertySignatureOptions } from './ApiPropertySignature'; +import { ApiMethodSignature, type IApiMethodSignatureOptions } from './ApiMethodSignature'; +import { ApiProperty, type IApiPropertyOptions } from './ApiProperty'; +import { ApiEnumMember, type IApiEnumMemberOptions } from './ApiEnumMember'; +import { ApiEnum, type IApiEnumOptions } from './ApiEnum'; +import type { IApiPropertyItemJson } from '../items/ApiPropertyItem'; +import { ApiConstructor, type IApiConstructorOptions } from './ApiConstructor'; +import { ApiConstructSignature, type IApiConstructSignatureOptions } from './ApiConstructSignature'; +import { ApiFunction, type IApiFunctionOptions } from './ApiFunction'; +import { ApiCallSignature, type IApiCallSignatureOptions } from './ApiCallSignature'; +import { ApiIndexSignature, type IApiIndexSignatureOptions } from './ApiIndexSignature'; +import { ApiTypeAlias, type IApiTypeAliasOptions, type IApiTypeAliasJson } from './ApiTypeAlias'; +import { ApiVariable, type IApiVariableOptions, type IApiVariableJson } from './ApiVariable'; +import type { IApiDeclaredItemJson } from '../items/ApiDeclaredItem'; +import type { DeserializerContext } from './DeserializerContext'; export class Deserializer { public static deserialize(context: DeserializerContext, jsonObject: IApiItemJson): ApiItem { diff --git a/libraries/api-extractor-model/src/model/DeserializerContext.ts b/libraries/api-extractor-model/src/model/DeserializerContext.ts index b50aa8b1a68..ec2ed94c18b 100644 --- a/libraries/api-extractor-model/src/model/DeserializerContext.ts +++ b/libraries/api-extractor-model/src/model/DeserializerContext.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { TSDocConfiguration } from '@microsoft/tsdoc'; +import type { TSDocConfiguration } from '@microsoft/tsdoc'; export enum ApiJsonSchemaVersion { /** @@ -143,10 +143,11 @@ export class DeserializerContext { public readonly tsdocConfiguration: TSDocConfiguration; public constructor(options: DeserializerContext) { - this.apiJsonFilename = options.apiJsonFilename; - this.toolPackage = options.toolPackage; - this.toolVersion = options.toolVersion; - this.versionToDeserialize = options.versionToDeserialize; - this.tsdocConfiguration = options.tsdocConfiguration; + const { apiJsonFilename, toolPackage, toolVersion, versionToDeserialize, tsdocConfiguration } = options; + this.apiJsonFilename = apiJsonFilename; + this.toolPackage = toolPackage; + this.toolVersion = toolVersion; + this.versionToDeserialize = versionToDeserialize; + this.tsdocConfiguration = tsdocConfiguration; } } diff --git a/libraries/api-extractor-model/src/model/HeritageType.ts b/libraries/api-extractor-model/src/model/HeritageType.ts index 67f113c11a5..c07448c3d63 100644 --- a/libraries/api-extractor-model/src/model/HeritageType.ts +++ b/libraries/api-extractor-model/src/model/HeritageType.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { Excerpt } from '../mixins/Excerpt'; +import type { Excerpt } from '../mixins/Excerpt'; /** * Represents a type referenced via an "extends" or "implements" heritage clause for a TypeScript class diff --git a/libraries/api-extractor-model/src/model/ModelReferenceResolver.ts b/libraries/api-extractor-model/src/model/ModelReferenceResolver.ts index ea4c274f9e8..e8282dedaab 100644 --- a/libraries/api-extractor-model/src/model/ModelReferenceResolver.ts +++ b/libraries/api-extractor-model/src/model/ModelReferenceResolver.ts @@ -1,11 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { DocDeclarationReference, DocMemberSelector, SelectorKind } from '@microsoft/tsdoc'; -import { ApiItem, ApiItemKind } from '../items/ApiItem'; -import { ApiModel } from './ApiModel'; -import { ApiPackage } from './ApiPackage'; -import { ApiEntryPoint } from './ApiEntryPoint'; +import { type DocDeclarationReference, type DocMemberSelector, SelectorKind } from '@microsoft/tsdoc'; + +import { type ApiItem, ApiItemKind } from '../items/ApiItem'; +import type { ApiModel } from './ApiModel'; +import type { ApiPackage } from './ApiPackage'; +import type { ApiEntryPoint } from './ApiEntryPoint'; import { ApiItemContainerMixin } from '../mixins/ApiItemContainerMixin'; import { ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; @@ -208,7 +209,7 @@ export class ModelReferenceResolver { const selectedMembers: ApiItem[] = []; - const selectorOverloadIndex: number = parseInt(memberSelector.selector); + const selectorOverloadIndex: number = parseInt(memberSelector.selector, 10); for (const foundMember of foundMembers) { if (ApiParameterListMixin.isBaseClassOf(foundMember)) { if (foundMember.overloadIndex === selectorOverloadIndex) { diff --git a/libraries/api-extractor-model/src/model/Parameter.ts b/libraries/api-extractor-model/src/model/Parameter.ts index 8560eeabae6..d802380c443 100644 --- a/libraries/api-extractor-model/src/model/Parameter.ts +++ b/libraries/api-extractor-model/src/model/Parameter.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as tsdoc from '@microsoft/tsdoc'; +import type * as tsdoc from '@microsoft/tsdoc'; import { ApiDocumentedItem } from '../items/ApiDocumentedItem'; -import { Excerpt } from '../mixins/Excerpt'; -import { ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; +import type { Excerpt } from '../mixins/Excerpt'; +import type { ApiParameterListMixin } from '../mixins/ApiParameterListMixin'; /** * Constructor options for {@link Parameter}. diff --git a/libraries/api-extractor-model/src/model/SourceLocation.ts b/libraries/api-extractor-model/src/model/SourceLocation.ts index c6e9f641a37..4d92a07c66c 100644 --- a/libraries/api-extractor-model/src/model/SourceLocation.ts +++ b/libraries/api-extractor-model/src/model/SourceLocation.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { URL } from 'url'; +import { URL } from 'node:url'; /** * Constructor options for `SourceLocation`. diff --git a/libraries/api-extractor-model/src/model/TypeParameter.ts b/libraries/api-extractor-model/src/model/TypeParameter.ts index 618a2703029..55fd019fdca 100644 --- a/libraries/api-extractor-model/src/model/TypeParameter.ts +++ b/libraries/api-extractor-model/src/model/TypeParameter.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as tsdoc from '@microsoft/tsdoc'; +import type * as tsdoc from '@microsoft/tsdoc'; import { ApiDocumentedItem } from '../items/ApiDocumentedItem'; -import { Excerpt } from '../mixins/Excerpt'; -import { ApiTypeParameterListMixin } from '../mixins/ApiTypeParameterListMixin'; +import type { Excerpt } from '../mixins/Excerpt'; +import type { ApiTypeParameterListMixin } from '../mixins/ApiTypeParameterListMixin'; /** * Constructor options for {@link TypeParameter}. @@ -87,11 +87,12 @@ export class TypeParameter { private _parent: ApiTypeParameterListMixin; public constructor(options: ITypeParameterOptions) { - this.name = options.name; - this.constraintExcerpt = options.constraintExcerpt; - this.defaultTypeExcerpt = options.defaultTypeExcerpt; - this.isOptional = options.isOptional; - this._parent = options.parent; + const { name, constraintExcerpt, defaultTypeExcerpt, isOptional, parent } = options; + this.name = name; + this.constraintExcerpt = constraintExcerpt; + this.defaultTypeExcerpt = defaultTypeExcerpt; + this.isOptional = isOptional; + this._parent = parent; } /** diff --git a/libraries/api-extractor-model/tsconfig.json b/libraries/api-extractor-model/tsconfig.json index fbc2f5c0a6c..1a33d17b873 100644 --- a/libraries/api-extractor-model/tsconfig.json +++ b/libraries/api-extractor-model/tsconfig.json @@ -1,7 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["heft-jest", "node"] - } + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/libraries/credential-cache/.npmignore b/libraries/credential-cache/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/libraries/credential-cache/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/credential-cache/CHANGELOG.json b/libraries/credential-cache/CHANGELOG.json new file mode 100644 index 00000000000..8cf185c75a7 --- /dev/null +++ b/libraries/credential-cache/CHANGELOG.json @@ -0,0 +1,479 @@ +{ + "name": "@rushstack/credential-cache", + "entries": [ + { + "version": "0.2.22", + "tag": "@rushstack/credential-cache_v0.2.22", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "0.2.21", + "tag": "@rushstack/credential-cache_v0.2.21", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "0.2.20", + "tag": "@rushstack/credential-cache_v0.2.20", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "0.2.19", + "tag": "@rushstack/credential-cache_v0.2.19", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "0.2.18", + "tag": "@rushstack/credential-cache_v0.2.18", + "date": "Mon, 08 Jun 2026 15:15:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "0.2.17", + "tag": "@rushstack/credential-cache_v0.2.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "0.2.16", + "tag": "@rushstack/credential-cache_v0.2.16", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "0.2.15", + "tag": "@rushstack/credential-cache_v0.2.15", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/credential-cache_v0.2.14", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/credential-cache_v0.2.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/credential-cache_v0.2.12", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/credential-cache_v0.2.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/credential-cache_v0.2.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/credential-cache_v0.2.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/credential-cache_v0.2.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/credential-cache_v0.2.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/credential-cache_v0.2.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/credential-cache_v0.2.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/credential-cache_v0.2.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/credential-cache_v0.2.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/credential-cache_v0.2.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/credential-cache_v0.2.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/credential-cache_v0.2.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "0.1.11", + "tag": "@rushstack/credential-cache_v0.1.11", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "0.1.10", + "tag": "@rushstack/credential-cache_v0.1.10", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "0.1.9", + "tag": "@rushstack/credential-cache_v0.1.9", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "0.1.8", + "tag": "@rushstack/credential-cache_v0.1.8", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "0.1.7", + "tag": "@rushstack/credential-cache_v0.1.7", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "0.1.6", + "tag": "@rushstack/credential-cache_v0.1.6", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "0.1.5", + "tag": "@rushstack/credential-cache_v0.1.5", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "0.1.4", + "tag": "@rushstack/credential-cache_v0.1.4", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + } + ] + } + }, + { + "version": "0.1.3", + "tag": "@rushstack/credential-cache_v0.1.3", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/credential-cache_v0.1.2", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/credential-cache_v0.1.1", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/credential-cache_v0.1.0", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "minor": [ + { + "comment": "Create dedicated package for the Rush \"CredentialCache\" API. This API manages credential persistence on the local machine." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + } + ] + } + } + ] +} diff --git a/libraries/credential-cache/CHANGELOG.md b/libraries/credential-cache/CHANGELOG.md new file mode 100644 index 00000000000..953a8b0c000 --- /dev/null +++ b/libraries/credential-cache/CHANGELOG.md @@ -0,0 +1,185 @@ +# Change Log - @rushstack/credential-cache + +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 0.2.22 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 0.2.21 +Fri, 17 Jul 2026 00:15:59 GMT + +_Version update only_ + +## 0.2.20 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.2.19 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.2.18 +Mon, 08 Jun 2026 15:15:49 GMT + +_Version update only_ + +## 0.2.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.2.16 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 0.2.15 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 0.2.14 +Sat, 18 Apr 2026 00:15:16 GMT + +_Version update only_ + +## 0.2.13 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.2.12 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 0.2.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.2.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 0.2.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 0.2.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.2.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 0.2.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.2.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 0.2.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.2.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 0.2.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 0.2.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.2.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.1.11 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 0.1.10 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 0.1.9 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 0.1.8 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 0.1.7 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 0.1.6 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 0.1.5 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 0.1.4 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 0.1.3 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 0.1.2 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 0.1.1 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 0.1.0 +Fri, 24 Oct 2025 00:13:38 GMT + +### Minor changes + +- Create dedicated package for the Rush "CredentialCache" API. This API manages credential persistence on the local machine. + diff --git a/libraries/credential-cache/LICENSE b/libraries/credential-cache/LICENSE new file mode 100644 index 00000000000..a8687a8508f --- /dev/null +++ b/libraries/credential-cache/LICENSE @@ -0,0 +1,24 @@ +@rushstack/credential-cache + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/libraries/credential-cache/README.md b/libraries/credential-cache/README.md new file mode 100644 index 00000000000..72a9d9393cb --- /dev/null +++ b/libraries/credential-cache/README.md @@ -0,0 +1,18 @@ +# @rushstack/credential-cache + +## Installation + +`npm install @rushstack/credential-cache --save-dev` + +## Overview + +This package manages persistent credentials on the local machine. Since these credentials are stored unencrypted, do not use for secure credentials. + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/libraries/credential-cache/CHANGELOG.md) - Find + out what's new in the latest version +- [API Reference](https://api.rushstack.io/pages/credential-cache/) + +**@rushstack/credential-cache** is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/libraries/credential-cache/config/api-extractor.json b/libraries/credential-cache/config/api-extractor.json new file mode 100644 index 00000000000..3dbb76c0e6f --- /dev/null +++ b/libraries/credential-cache/config/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json" +} diff --git a/libraries/credential-cache/config/heft.json b/libraries/credential-cache/config/heft.json new file mode 100644 index 00000000000..0922361a39e --- /dev/null +++ b/libraries/credential-cache/config/heft.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/rush/v5"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/libraries/credential-cache/config/rig.json b/libraries/credential-cache/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/libraries/credential-cache/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/libraries/credential-cache/eslint.config.js b/libraries/credential-cache/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/libraries/credential-cache/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/credential-cache/package.json b/libraries/credential-cache/package.json new file mode 100644 index 00000000000..65d7242cd03 --- /dev/null +++ b/libraries/credential-cache/package.json @@ -0,0 +1,51 @@ +{ + "name": "@rushstack/credential-cache", + "version": "0.2.22", + "description": "Cross-platform functionality to manage cached credentials.", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/credential-cache.d.ts", + "exports": { + ".": { + "types": "./dist/credential-cache.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "license": "MIT", + "repository": { + "url": "https://github.com/microsoft/rushstack.git", + "type": "git", + "directory": "libraries/credential-cache" + }, + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "dependencies": { + "@rushstack/node-core-library": "workspace:*" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "sideEffects": false +} diff --git a/libraries/rush-lib/src/logic/CredentialCache.ts b/libraries/credential-cache/src/CredentialCache.ts similarity index 75% rename from libraries/rush-lib/src/logic/CredentialCache.ts rename to libraries/credential-cache/src/CredentialCache.ts index 75cdfaa2c2b..bac9378e7a9 100644 --- a/libraries/rush-lib/src/logic/CredentialCache.ts +++ b/libraries/credential-cache/src/CredentialCache.ts @@ -1,15 +1,30 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { FileSystem, JsonFile, JsonSchema, LockFile, Import } from '@rushstack/node-core-library'; +import * as path from 'node:path'; -import { Utilities } from '../utilities/Utilities'; -import { RushUserConfiguration } from '../api/RushUserConfiguration'; -import schemaJson from '../schemas/credentials.schema.json'; +import { + Disposables, + FileSystem, + JsonFile, + JsonSchema, + LockFile, + User, + Objects +} from '@rushstack/node-core-library'; -const lodash: typeof import('lodash') = Import.lazy('lodash', require); +import schemaJson from './schemas/credentials.schema.json'; -const CACHE_FILENAME: string = 'credentials.json'; +// Polyfill for node 18 +Disposables.polyfillDisposeSymbols(); + +/** + * The name of the default folder in the user's home directory where Rush stores user-specific data. + * @public + */ +export const RUSH_USER_FOLDER_NAME: '.rush-user' = '.rush-user'; + +const DEFAULT_CACHE_FILENAME: 'credentials.json' = 'credentials.json'; const LATEST_CREDENTIALS_JSON_VERSION: string = '0.1.0'; interface ICredentialCacheJson { @@ -26,7 +41,7 @@ interface ICacheEntryJson { } /** - * @beta + * @public */ export interface ICredentialCacheEntry { expires?: Date; @@ -35,21 +50,22 @@ export interface ICredentialCacheEntry { } /** - * @beta + * @public */ export interface ICredentialCacheOptions { supportEditing: boolean; + cacheFilePath?: string; } /** - * @beta + * @public */ -export class CredentialCache /* implements IDisposable */ { +export class CredentialCache implements Disposable { private readonly _cacheFilePath: string; private readonly _cacheEntries: Map; private _modified: boolean = false; private _disposed: boolean = false; - private _supportsEditing: boolean; + private readonly _supportsEditing: boolean; private readonly _lockfile: LockFile | undefined; private constructor( @@ -58,7 +74,7 @@ export class CredentialCache /* implements IDisposable */ { lockfile: LockFile | undefined ) { if (loadedJson && loadedJson.version !== LATEST_CREDENTIALS_JSON_VERSION) { - throw new Error(`Unexpected credentials.json file version: ${loadedJson.version}`); + throw new Error(`Unexpected ${cacheFilePath} file version: ${loadedJson.version}`); } this._cacheFilePath = cacheFilePath; @@ -68,8 +84,17 @@ export class CredentialCache /* implements IDisposable */ { } public static async initializeAsync(options: ICredentialCacheOptions): Promise { - const rushUserFolderPath: string = RushUserConfiguration.getRushUserFolderPath(); - const cacheFilePath: string = `${rushUserFolderPath}/${CACHE_FILENAME}`; + let cacheDirectory: string; + let cacheFileName: string; + if (options.cacheFilePath) { + cacheDirectory = path.dirname(options.cacheFilePath); + cacheFileName = options.cacheFilePath.slice(cacheDirectory.length + 1); + } else { + cacheDirectory = `${User.getHomeFolder()}/${RUSH_USER_FOLDER_NAME}`; + cacheFileName = DEFAULT_CACHE_FILENAME; + } + const cacheFilePath: string = `${cacheDirectory}/${cacheFileName}`; + const jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); let loadedJson: ICredentialCacheJson | undefined; @@ -83,7 +108,7 @@ export class CredentialCache /* implements IDisposable */ { let lockfile: LockFile | undefined; if (options.supportEditing) { - lockfile = await LockFile.acquire(rushUserFolderPath, `${CACHE_FILENAME}.lock`); + lockfile = await LockFile.acquireAsync(cacheDirectory, `${cacheFileName}.lock`); } const credentialCache: CredentialCache = new CredentialCache(cacheFilePath, loadedJson, lockfile); @@ -94,7 +119,12 @@ export class CredentialCache /* implements IDisposable */ { options: ICredentialCacheOptions, doActionAsync: (credentialCache: CredentialCache) => Promise | void ): Promise { - await Utilities.usingAsync(async () => await CredentialCache.initializeAsync(options), doActionAsync); + const cache: CredentialCache = await CredentialCache.initializeAsync(options); + try { + await doActionAsync(cache); + } finally { + cache.dispose(); + } } public setCacheEntry(cacheId: string, entry: ICredentialCacheEntry): void { @@ -106,7 +136,7 @@ export class CredentialCache /* implements IDisposable */ { if ( existingCacheEntry?.credential !== credential || existingCacheEntry?.expires !== expiresMilliseconds || - !lodash.isEqual(existingCacheEntry?.credentialMetadata, credentialMetadata) + !Objects.areDeepEqual(existingCacheEntry?.credentialMetadata, credentialMetadata) ) { this._modified = true; this._cacheEntries.set(cacheId, { @@ -178,6 +208,10 @@ export class CredentialCache /* implements IDisposable */ { } } + public [Symbol.dispose](): void { + this.dispose(); + } + public dispose(): void { this._lockfile?.release(); this._disposed = true; diff --git a/libraries/credential-cache/src/index.ts b/libraries/credential-cache/src/index.ts new file mode 100644 index 00000000000..00ce0172b53 --- /dev/null +++ b/libraries/credential-cache/src/index.ts @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * This package is used to manage persistent, per-user cached credentials. + * + * @packageDocumentation + */ + +export { + CredentialCache, + type ICredentialCacheEntry, + type ICredentialCacheOptions, + RUSH_USER_FOLDER_NAME +} from './CredentialCache'; diff --git a/libraries/rush-lib/src/schemas/credentials.schema.json b/libraries/credential-cache/src/schemas/credentials.schema.json similarity index 100% rename from libraries/rush-lib/src/schemas/credentials.schema.json rename to libraries/credential-cache/src/schemas/credentials.schema.json diff --git a/libraries/credential-cache/src/test/CredentialCache.mock.ts b/libraries/credential-cache/src/test/CredentialCache.mock.ts new file mode 100644 index 00000000000..5a4bcc29188 --- /dev/null +++ b/libraries/credential-cache/src/test/CredentialCache.mock.ts @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type * as GetHomeFolderModule from '@rushstack/node-core-library/lib/user/getHomeFolder'; + +export const mockGetHomeFolder: jest.MockedFunction = jest.fn(); +jest.mock('@rushstack/node-core-library/lib/user/getHomeFolder', (): typeof GetHomeFolderModule => ({ + getHomeFolder: mockGetHomeFolder +})); diff --git a/libraries/credential-cache/src/test/CredentialCache.test.ts b/libraries/credential-cache/src/test/CredentialCache.test.ts new file mode 100644 index 00000000000..5bdb13c49e6 --- /dev/null +++ b/libraries/credential-cache/src/test/CredentialCache.test.ts @@ -0,0 +1,395 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { mockGetHomeFolder } from './CredentialCache.mock'; +import { LockFile, Async, FileSystem } from '@rushstack/node-core-library'; +import { CredentialCache, type ICredentialCacheOptions, RUSH_USER_FOLDER_NAME } from '../CredentialCache'; + +const FAKE_HOME_FOLDER: string = 'temp'; +const FAKE_RUSH_USER_FOLDER: string = `${FAKE_HOME_FOLDER}/${RUSH_USER_FOLDER_NAME}`; + +interface IPathsTestCase extends Required> { + testCaseName: string; +} + +describe(CredentialCache.name, () => { + let fakeFilesystem: { [key: string]: string }; + let filesystemLocks: { [key: string]: Promise }; + let unresolvedLockfiles: Set; + + beforeEach(() => { + fakeFilesystem = {}; + filesystemLocks = {}; + unresolvedLockfiles = new Set(); + }); + + beforeEach(() => { + mockGetHomeFolder.mockReturnValue(FAKE_HOME_FOLDER); + + // TODO: Consider expanding these mocks and moving them to node-core-library + jest + .spyOn(LockFile, 'acquire') + .mockImplementation(async (folderPath: string, lockFilePath: string, maxWaitMs?: number) => { + const fullPath: string = `${folderPath}/${lockFilePath}`; + const existingLock: Promise | undefined = filesystemLocks[fullPath]; + if (existingLock) { + if (maxWaitMs === undefined) { + await existingLock; + } else { + await Promise.race([existingLock, Async.sleepAsync(maxWaitMs)]); + } + } + + let release: () => void; + const lockPromise: Promise = new Promise((resolve: () => void) => { + release = resolve; + }); + + // eslint-disable-next-line require-atomic-updates + filesystemLocks[fullPath] = lockPromise; + const result: LockFile = { + release: () => { + release(); + unresolvedLockfiles.delete(result); + } + } as LockFile; + unresolvedLockfiles.add(result); + return result; + }); + + jest + .spyOn(FileSystem, 'writeFileAsync') + .mockImplementation(async (filePath: string, data: Buffer | string) => { + fakeFilesystem[filePath] = data.toString(); + }); + + jest.spyOn(FileSystem, 'readFileAsync').mockImplementation(async (filePath: string) => { + if (filePath in fakeFilesystem) { + return fakeFilesystem[filePath]; + } else { + const notExistError: NodeJS.ErrnoException = new Error( + `ENOENT: no such file or directory, open '${filePath}'` + ); + notExistError.code = 'ENOENT'; + notExistError.errno = -2; + notExistError.syscall = 'open'; + notExistError.path = filePath; + throw notExistError; + } + }); + }); + + afterEach(() => { + for (const lockfile of unresolvedLockfiles) { + lockfile.release(); + } + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + describe.each([ + { + testCaseName: 'default cache path', + cacheFilePath: `${FAKE_RUSH_USER_FOLDER}/credentials.json` + }, + { + testCaseName: 'custom cache path with no suffix', + cacheFilePath: `${FAKE_RUSH_USER_FOLDER}/my-cache-name` + }, + { + testCaseName: 'custom cache path with json suffix', + cacheFilePath: `${FAKE_RUSH_USER_FOLDER}/my-cache-name.json` + } + ])('cache paths [$testCaseName]', ({ cacheFilePath }) => { + it("initializes a credential cache correctly when one doesn't exist on disk", async () => { + const credentialCache: CredentialCache = await CredentialCache.initializeAsync({ + supportEditing: false + }); + expect(credentialCache).toBeDefined(); + credentialCache.dispose(); + }); + + it('initializes a credential cache correctly when one exists on disk', async () => { + const credentialId: string = 'test-credential'; + const credentialValue: string = 'test-value'; + fakeFilesystem[cacheFilePath] = JSON.stringify({ + version: '0.1.0', + cacheEntries: { + [credentialId]: { + expires: 0, + credential: credentialValue + } + } + }); + + const credentialCache: CredentialCache = await CredentialCache.initializeAsync({ + cacheFilePath: cacheFilePath, + supportEditing: false + }); + expect(credentialCache.tryGetCacheEntry(credentialId)?.credential).toEqual(credentialValue); + expect(credentialCache.tryGetCacheEntry(credentialId)?.expires).toBeUndefined(); + credentialCache.dispose(); + }); + + it('initializes a credential cache correctly when one exists on disk with a expired credential', async () => { + const credentialId: string = 'test-credential'; + const credentialValue: string = 'test-value'; + fakeFilesystem[cacheFilePath] = JSON.stringify({ + version: '0.1.0', + cacheEntries: { + [credentialId]: { + expires: 100, // Expired + credential: credentialValue + } + } + }); + + const credentialCache: CredentialCache = await CredentialCache.initializeAsync({ + cacheFilePath: cacheFilePath, + supportEditing: false + }); + expect(credentialCache.tryGetCacheEntry(credentialId)?.credential).toEqual(credentialValue); + expect(credentialCache.tryGetCacheEntry(credentialId)?.expires).toMatchSnapshot('expiration'); + credentialCache.dispose(); + }); + + it('correctly trims expired credentials', async () => { + const credentialId: string = 'test-credential'; + const credentialValue: string = 'test-value'; + fakeFilesystem[cacheFilePath] = JSON.stringify({ + version: '0.1.0', + cacheEntries: { + [credentialId]: { + expires: 100, // Expired + credential: credentialValue + } + } + }); + + const credentialCache: CredentialCache = await CredentialCache.initializeAsync({ + cacheFilePath: cacheFilePath, + supportEditing: true + }); + credentialCache.trimExpiredEntries(); + expect(credentialCache.tryGetCacheEntry(credentialId)).toBeUndefined(); + await credentialCache.saveIfModifiedAsync(); + credentialCache.dispose(); + + expect(fakeFilesystem[cacheFilePath]).toMatchSnapshot('credential cache file'); + }); + + it('correctly adds a new credential', async () => { + const credentialId: string = 'test-credential'; + const credentialValue: string = 'test-value'; + + const credentialCache1: CredentialCache = await CredentialCache.initializeAsync({ + cacheFilePath: cacheFilePath, + supportEditing: true + }); + credentialCache1.setCacheEntry(credentialId, { credential: credentialValue }); + expect(credentialCache1.tryGetCacheEntry(credentialId)?.credential).toEqual(credentialValue); + expect(credentialCache1.tryGetCacheEntry(credentialId)?.expires).toBeUndefined(); + await credentialCache1.saveIfModifiedAsync(); + credentialCache1.dispose(); + + expect(fakeFilesystem[cacheFilePath]).toMatchSnapshot('credential cache file'); + + const credentialCache2: CredentialCache = await CredentialCache.initializeAsync({ + cacheFilePath: cacheFilePath, + supportEditing: false + }); + expect(credentialCache2.tryGetCacheEntry(credentialId)?.credential).toEqual(credentialValue); + expect(credentialCache2.tryGetCacheEntry(credentialId)?.expires).toBeUndefined(); + credentialCache2.dispose(); + }); + + it('correctly updates an existing credential', async () => { + const credentialId: string = 'test-credential'; + const credentialValue: string = 'test-value'; + const newCredentialValue: string = 'new-test-value'; + fakeFilesystem[cacheFilePath] = JSON.stringify({ + version: '0.1.0', + cacheEntries: { + [credentialId]: { + expires: 0, + credential: credentialValue + } + } + }); + + const credentialCache1: CredentialCache = await CredentialCache.initializeAsync({ + cacheFilePath: cacheFilePath, + supportEditing: true + }); + credentialCache1.setCacheEntry(credentialId, { credential: newCredentialValue }); + expect(credentialCache1.tryGetCacheEntry(credentialId)?.credential).toEqual(newCredentialValue); + expect(credentialCache1.tryGetCacheEntry(credentialId)?.expires).toBeUndefined(); + await credentialCache1.saveIfModifiedAsync(); + credentialCache1.dispose(); + + expect(fakeFilesystem[cacheFilePath]).toMatchSnapshot('credential cache file'); + + const credentialCache2: CredentialCache = await CredentialCache.initializeAsync({ + cacheFilePath: cacheFilePath, + supportEditing: false + }); + expect(credentialCache2.tryGetCacheEntry(credentialId)?.credential).toEqual(newCredentialValue); + expect(credentialCache2.tryGetCacheEntry(credentialId)?.expires).toBeUndefined(); + credentialCache2.dispose(); + }); + + it('correctly deletes an existing credential', async () => { + const credentialId: string = 'test-credential'; + fakeFilesystem[cacheFilePath] = JSON.stringify({ + version: '0.1.0', + cacheEntries: { + [credentialId]: { + expires: 0, + credential: 'test-value' + } + } + }); + + const credentialCache1: CredentialCache = await CredentialCache.initializeAsync({ + cacheFilePath: cacheFilePath, + supportEditing: true + }); + credentialCache1.deleteCacheEntry(credentialId); + expect(credentialCache1.tryGetCacheEntry(credentialId)).toBeUndefined(); + await credentialCache1.saveIfModifiedAsync(); + credentialCache1.dispose(); + + expect(fakeFilesystem[cacheFilePath]).toMatchSnapshot('credential cache file'); + + const credentialCache2: CredentialCache = await CredentialCache.initializeAsync({ + cacheFilePath: cacheFilePath, + supportEditing: false + }); + expect(credentialCache2.tryGetCacheEntry(credentialId)).toBeUndefined(); + credentialCache2.dispose(); + }); + + it('correctly sets credentialMetadata', async () => { + const credentialId: string = 'test-credential'; + const credentialValue: string = 'test-value'; + const credentialMetadata: object = { + a: 1, + b: true + }; + + const credentialCache1: CredentialCache = await CredentialCache.initializeAsync({ + cacheFilePath: cacheFilePath, + supportEditing: true + }); + credentialCache1.setCacheEntry(credentialId, { credential: credentialValue, credentialMetadata }); + expect(credentialCache1.tryGetCacheEntry(credentialId)).toEqual({ + credential: credentialValue, + credentialMetadata + }); + await credentialCache1.saveIfModifiedAsync(); + credentialCache1.dispose(); + + expect(fakeFilesystem[cacheFilePath]).toMatchSnapshot('credential cache file'); + + const credentialCache2: CredentialCache = await CredentialCache.initializeAsync({ + cacheFilePath: cacheFilePath, + supportEditing: false + }); + expect(credentialCache2.tryGetCacheEntry(credentialId)).toEqual({ + credential: credentialValue, + credentialMetadata + }); + credentialCache2.dispose(); + }); + + it('correctly updates credentialMetadata', async () => { + const credentialId: string = 'test-credential'; + const credentialValue: string = 'test-value'; + const oldCredentialMetadata: object = { + a: 1, + b: true + }; + const newCredentialMetadata: object = { + c: ['a', 'b', 'c'] + }; + + fakeFilesystem[cacheFilePath] = JSON.stringify({ + version: '0.1.0', + cacheEntries: { + [credentialId]: { + expires: 0, + credential: 'test-value', + credentialMetadata: oldCredentialMetadata + } + } + }); + + const credentialCache1: CredentialCache = await CredentialCache.initializeAsync({ + cacheFilePath: cacheFilePath, + supportEditing: true + }); + credentialCache1.setCacheEntry(credentialId, { + credential: credentialValue, + credentialMetadata: newCredentialMetadata + }); + expect(credentialCache1.tryGetCacheEntry(credentialId)).toEqual({ + credential: credentialValue, + credentialMetadata: newCredentialMetadata + }); + await credentialCache1.saveIfModifiedAsync(); + credentialCache1.dispose(); + + expect(fakeFilesystem[cacheFilePath]).toMatchSnapshot('credential cache file'); + + const credentialCache2: CredentialCache = await CredentialCache.initializeAsync({ + cacheFilePath: cacheFilePath, + supportEditing: false + }); + expect(credentialCache2.tryGetCacheEntry(credentialId)).toEqual({ + credential: credentialValue, + credentialMetadata: newCredentialMetadata + }); + credentialCache2.dispose(); + }); + }); + + it('does not allow interaction if already disposed', async () => { + const credentialCache: CredentialCache = await CredentialCache.initializeAsync({ supportEditing: true }); + credentialCache.dispose(); + + expect(() => credentialCache.deleteCacheEntry('test')).toThrowErrorMatchingInlineSnapshot( + `"This instance of CredentialCache has been disposed."` + ); + await expect(() => credentialCache.saveIfModifiedAsync()).rejects.toThrowErrorMatchingInlineSnapshot( + `"This instance of CredentialCache has been disposed."` + ); + expect(() => + credentialCache.setCacheEntry('test', { credential: 'test' }) + ).toThrowErrorMatchingInlineSnapshot(`"This instance of CredentialCache has been disposed."`); + expect(() => credentialCache.trimExpiredEntries()).toThrowErrorMatchingInlineSnapshot( + `"This instance of CredentialCache has been disposed."` + ); + expect(() => credentialCache.tryGetCacheEntry('test')).toThrowErrorMatchingInlineSnapshot( + `"This instance of CredentialCache has been disposed."` + ); + }); + + it("does not allow modification if initialized with 'supportEditing': false", async () => { + const credentialCache: CredentialCache = await CredentialCache.initializeAsync({ supportEditing: false }); + + expect(() => credentialCache.deleteCacheEntry('test')).toThrowErrorMatchingInlineSnapshot( + `"This instance of CredentialCache does not support editing."` + ); + await expect(() => credentialCache.saveIfModifiedAsync()).rejects.toThrowErrorMatchingInlineSnapshot( + `"This instance of CredentialCache does not support editing."` + ); + expect(() => + credentialCache.setCacheEntry('test', { credential: 'test' }) + ).toThrowErrorMatchingInlineSnapshot(`"This instance of CredentialCache does not support editing."`); + expect(() => credentialCache.trimExpiredEntries()).toThrowErrorMatchingInlineSnapshot( + `"This instance of CredentialCache does not support editing."` + ); + }); +}); diff --git a/libraries/credential-cache/src/test/__snapshots__/CredentialCache.test.ts.snap b/libraries/credential-cache/src/test/__snapshots__/CredentialCache.test.ts.snap new file mode 100644 index 00000000000..8930f68c982 --- /dev/null +++ b/libraries/credential-cache/src/test/__snapshots__/CredentialCache.test.ts.snap @@ -0,0 +1,244 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`CredentialCache cache paths [custom cache path with json suffix] correctly adds a new credential: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": { + \\"test-credential\\": { + \\"expires\\": 0, + \\"credential\\": \\"test-value\\" + } + } +} +" +`; + +exports[`CredentialCache cache paths [custom cache path with json suffix] correctly deletes an existing credential: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": {} +} +" +`; + +exports[`CredentialCache cache paths [custom cache path with json suffix] correctly sets credentialMetadata: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": { + \\"test-credential\\": { + \\"expires\\": 0, + \\"credential\\": \\"test-value\\", + \\"credentialMetadata\\": { + \\"a\\": 1, + \\"b\\": true + } + } + } +} +" +`; + +exports[`CredentialCache cache paths [custom cache path with json suffix] correctly trims expired credentials: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": {} +} +" +`; + +exports[`CredentialCache cache paths [custom cache path with json suffix] correctly updates an existing credential: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": { + \\"test-credential\\": { + \\"expires\\": 0, + \\"credential\\": \\"new-test-value\\" + } + } +} +" +`; + +exports[`CredentialCache cache paths [custom cache path with json suffix] correctly updates credentialMetadata: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": { + \\"test-credential\\": { + \\"expires\\": 0, + \\"credential\\": \\"test-value\\", + \\"credentialMetadata\\": { + \\"c\\": [ + \\"a\\", + \\"b\\", + \\"c\\" + ] + } + } + } +} +" +`; + +exports[`CredentialCache cache paths [custom cache path with json suffix] initializes a credential cache correctly when one exists on disk with a expired credential: expiration 1`] = `1970-01-01T00:00:00.100Z`; + +exports[`CredentialCache cache paths [custom cache path with no suffix] correctly adds a new credential: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": { + \\"test-credential\\": { + \\"expires\\": 0, + \\"credential\\": \\"test-value\\" + } + } +} +" +`; + +exports[`CredentialCache cache paths [custom cache path with no suffix] correctly deletes an existing credential: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": {} +} +" +`; + +exports[`CredentialCache cache paths [custom cache path with no suffix] correctly sets credentialMetadata: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": { + \\"test-credential\\": { + \\"expires\\": 0, + \\"credential\\": \\"test-value\\", + \\"credentialMetadata\\": { + \\"a\\": 1, + \\"b\\": true + } + } + } +} +" +`; + +exports[`CredentialCache cache paths [custom cache path with no suffix] correctly trims expired credentials: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": {} +} +" +`; + +exports[`CredentialCache cache paths [custom cache path with no suffix] correctly updates an existing credential: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": { + \\"test-credential\\": { + \\"expires\\": 0, + \\"credential\\": \\"new-test-value\\" + } + } +} +" +`; + +exports[`CredentialCache cache paths [custom cache path with no suffix] correctly updates credentialMetadata: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": { + \\"test-credential\\": { + \\"expires\\": 0, + \\"credential\\": \\"test-value\\", + \\"credentialMetadata\\": { + \\"c\\": [ + \\"a\\", + \\"b\\", + \\"c\\" + ] + } + } + } +} +" +`; + +exports[`CredentialCache cache paths [custom cache path with no suffix] initializes a credential cache correctly when one exists on disk with a expired credential: expiration 1`] = `1970-01-01T00:00:00.100Z`; + +exports[`CredentialCache cache paths [default cache path] correctly adds a new credential: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": { + \\"test-credential\\": { + \\"expires\\": 0, + \\"credential\\": \\"test-value\\" + } + } +} +" +`; + +exports[`CredentialCache cache paths [default cache path] correctly deletes an existing credential: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": {} +} +" +`; + +exports[`CredentialCache cache paths [default cache path] correctly sets credentialMetadata: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": { + \\"test-credential\\": { + \\"expires\\": 0, + \\"credential\\": \\"test-value\\", + \\"credentialMetadata\\": { + \\"a\\": 1, + \\"b\\": true + } + } + } +} +" +`; + +exports[`CredentialCache cache paths [default cache path] correctly trims expired credentials: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": {} +} +" +`; + +exports[`CredentialCache cache paths [default cache path] correctly updates an existing credential: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": { + \\"test-credential\\": { + \\"expires\\": 0, + \\"credential\\": \\"new-test-value\\" + } + } +} +" +`; + +exports[`CredentialCache cache paths [default cache path] correctly updates credentialMetadata: credential cache file 1`] = ` +"{ + \\"version\\": \\"0.1.0\\", + \\"cacheEntries\\": { + \\"test-credential\\": { + \\"expires\\": 0, + \\"credential\\": \\"test-value\\", + \\"credentialMetadata\\": { + \\"c\\": [ + \\"a\\", + \\"b\\", + \\"c\\" + ] + } + } + } +} +" +`; + +exports[`CredentialCache cache paths [default cache path] initializes a credential cache correctly when one exists on disk with a expired credential: expiration 1`] = `1970-01-01T00:00:00.100Z`; diff --git a/libraries/credential-cache/tsconfig.json b/libraries/credential-cache/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/libraries/credential-cache/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/libraries/debug-certificate-manager/.eslintrc.js b/libraries/debug-certificate-manager/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/libraries/debug-certificate-manager/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/libraries/debug-certificate-manager/.npmignore b/libraries/debug-certificate-manager/.npmignore index 302dbc5b019..f7a40e10213 100644 --- a/libraries/debug-certificate-manager/.npmignore +++ b/libraries/debug-certificate-manager/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,16 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/debug-certificate-manager/CHANGELOG.json b/libraries/debug-certificate-manager/CHANGELOG.json index bd00a7b618a..3d4cf1e71b5 100644 --- a/libraries/debug-certificate-manager/CHANGELOG.json +++ b/libraries/debug-certificate-manager/CHANGELOG.json @@ -1,6 +1,2657 @@ { "name": "@rushstack/debug-certificate-manager", "entries": [ + { + "version": "1.7.23", + "tag": "@rushstack/debug-certificate-manager_v1.7.23", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "1.7.22", + "tag": "@rushstack/debug-certificate-manager_v1.7.22", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "1.7.21", + "tag": "@rushstack/debug-certificate-manager_v1.7.21", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "1.7.20", + "tag": "@rushstack/debug-certificate-manager_v1.7.20", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "1.7.19", + "tag": "@rushstack/debug-certificate-manager_v1.7.19", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "1.7.18", + "tag": "@rushstack/debug-certificate-manager_v1.7.18", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "1.7.17", + "tag": "@rushstack/debug-certificate-manager_v1.7.17", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "1.7.16", + "tag": "@rushstack/debug-certificate-manager_v1.7.16", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "1.7.15", + "tag": "@rushstack/debug-certificate-manager_v1.7.15", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "1.7.14", + "tag": "@rushstack/debug-certificate-manager_v1.7.14", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "1.7.13", + "tag": "@rushstack/debug-certificate-manager_v1.7.13", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "1.7.12", + "tag": "@rushstack/debug-certificate-manager_v1.7.12", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "1.7.11", + "tag": "@rushstack/debug-certificate-manager_v1.7.11", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "1.7.10", + "tag": "@rushstack/debug-certificate-manager_v1.7.10", + "date": "Thu, 02 Apr 2026 00:14:38 GMT", + "comments": { + "patch": [ + { + "comment": "Bump node-forge to 1.4.0 to address CVEs GHSA-2328-f5f3-gj25, GHSA-q67f-28xg-22rw, GHSA-5m6q-g25r-mvwx, GHSA-ppp5-5v6c-4jwp\"" + } + ] + } + }, + { + "version": "1.7.9", + "tag": "@rushstack/debug-certificate-manager_v1.7.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "1.7.8", + "tag": "@rushstack/debug-certificate-manager_v1.7.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "1.7.7", + "tag": "@rushstack/debug-certificate-manager_v1.7.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "1.7.6", + "tag": "@rushstack/debug-certificate-manager_v1.7.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "1.7.5", + "tag": "@rushstack/debug-certificate-manager_v1.7.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "1.7.4", + "tag": "@rushstack/debug-certificate-manager_v1.7.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "1.7.3", + "tag": "@rushstack/debug-certificate-manager_v1.7.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "1.7.2", + "tag": "@rushstack/debug-certificate-manager_v1.7.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "1.7.1", + "tag": "@rushstack/debug-certificate-manager_v1.7.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "1.7.0", + "tag": "@rushstack/debug-certificate-manager_v1.7.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "1.6.14", + "tag": "@rushstack/debug-certificate-manager_v1.6.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "1.6.13", + "tag": "@rushstack/debug-certificate-manager_v1.6.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "1.6.12", + "tag": "@rushstack/debug-certificate-manager_v1.6.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "1.6.11", + "tag": "@rushstack/debug-certificate-manager_v1.6.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "1.6.10", + "tag": "@rushstack/debug-certificate-manager_v1.6.10", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "1.6.9", + "tag": "@rushstack/debug-certificate-manager_v1.6.9", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "1.6.8", + "tag": "@rushstack/debug-certificate-manager_v1.6.8", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "1.6.7", + "tag": "@rushstack/debug-certificate-manager_v1.6.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + } + ] + } + }, + { + "version": "1.6.6", + "tag": "@rushstack/debug-certificate-manager_v1.6.6", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + } + ] + } + }, + { + "version": "1.6.5", + "tag": "@rushstack/debug-certificate-manager_v1.6.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + } + ] + } + }, + { + "version": "1.6.4", + "tag": "@rushstack/debug-certificate-manager_v1.6.4", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + } + ] + } + }, + { + "version": "1.6.3", + "tag": "@rushstack/debug-certificate-manager_v1.6.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + } + ] + } + }, + { + "version": "1.6.2", + "tag": "@rushstack/debug-certificate-manager_v1.6.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + } + ] + } + }, + { + "version": "1.6.1", + "tag": "@rushstack/debug-certificate-manager_v1.6.1", + "date": "Wed, 08 Oct 2025 00:13:28 GMT", + "comments": { + "patch": [ + { + "comment": "Add support for the IPv6 localhost address (`::1`)." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + } + ] + } + }, + { + "version": "1.6.0", + "tag": "@rushstack/debug-certificate-manager_v1.6.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + } + ] + } + }, + { + "version": "1.5.9", + "tag": "@rushstack/debug-certificate-manager_v1.5.9", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + } + ] + } + }, + { + "version": "1.5.8", + "tag": "@rushstack/debug-certificate-manager_v1.5.8", + "date": "Tue, 30 Sep 2025 20:33:50 GMT", + "comments": { + "patch": [ + { + "comment": "Add message to use VS Code extension to errors." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + } + ] + } + }, + { + "version": "1.5.7", + "tag": "@rushstack/debug-certificate-manager_v1.5.7", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + } + ] + } + }, + { + "version": "1.5.6", + "tag": "@rushstack/debug-certificate-manager_v1.5.6", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + } + ] + } + }, + { + "version": "1.5.5", + "tag": "@rushstack/debug-certificate-manager_v1.5.5", + "date": "Fri, 29 Aug 2025 00:08:01 GMT", + "comments": { + "patch": [ + { + "comment": "Fix homedir resolution in CertificateStore" + } + ] + } + }, + { + "version": "1.5.4", + "tag": "@rushstack/debug-certificate-manager_v1.5.4", + "date": "Tue, 26 Aug 2025 00:12:57 GMT", + "comments": { + "patch": [ + { + "comment": "Fix handling of home directory paths when reading debug-certificate-manager.json config file." + } + ] + } + }, + { + "version": "1.5.3", + "tag": "@rushstack/debug-certificate-manager_v1.5.3", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + } + ] + } + }, + { + "version": "1.5.2", + "tag": "@rushstack/debug-certificate-manager_v1.5.2", + "date": "Fri, 01 Aug 2025 00:12:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + } + ] + } + }, + { + "version": "1.5.1", + "tag": "@rushstack/debug-certificate-manager_v1.5.1", + "date": "Sat, 26 Jul 2025 00:12:22 GMT", + "comments": { + "patch": [ + { + "comment": "Read CertificateStore configuration from .vscode/debug-certificate-manager.json" + } + ] + } + }, + { + "version": "1.5.0", + "tag": "@rushstack/debug-certificate-manager_v1.5.0", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "minor": [ + { + "comment": "CertificateStore - Add params to support custom paths and filenames" + }, + { + "comment": "CertificateManager - Update `untrustCertificateAsync` to clear `caCertificateData`" + }, + { + "comment": "CertificateManager - Use osascript (applescript) to run elevated command on macOS instead of sudo package." + }, + { + "comment": "CertificateManager - Expose `getCertificateExpirationAsync` method to retrieve certificate expiration date" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + } + ] + } + }, + { + "version": "1.4.37", + "tag": "@rushstack/debug-certificate-manager_v1.4.37", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + } + ] + } + }, + { + "version": "1.4.36", + "tag": "@rushstack/debug-certificate-manager_v1.4.36", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + } + ] + } + }, + { + "version": "1.4.35", + "tag": "@rushstack/debug-certificate-manager_v1.4.35", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + } + ] + } + }, + { + "version": "1.4.34", + "tag": "@rushstack/debug-certificate-manager_v1.4.34", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + } + ] + } + }, + { + "version": "1.4.33", + "tag": "@rushstack/debug-certificate-manager_v1.4.33", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + } + ] + } + }, + { + "version": "1.4.32", + "tag": "@rushstack/debug-certificate-manager_v1.4.32", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + } + ] + } + }, + { + "version": "1.4.31", + "tag": "@rushstack/debug-certificate-manager_v1.4.31", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + } + ] + } + }, + { + "version": "1.4.30", + "tag": "@rushstack/debug-certificate-manager_v1.4.30", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + } + ] + } + }, + { + "version": "1.4.29", + "tag": "@rushstack/debug-certificate-manager_v1.4.29", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + } + ] + } + }, + { + "version": "1.4.28", + "tag": "@rushstack/debug-certificate-manager_v1.4.28", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + } + ] + } + }, + { + "version": "1.4.27", + "tag": "@rushstack/debug-certificate-manager_v1.4.27", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + } + ] + } + }, + { + "version": "1.4.26", + "tag": "@rushstack/debug-certificate-manager_v1.4.26", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + } + ] + } + }, + { + "version": "1.4.25", + "tag": "@rushstack/debug-certificate-manager_v1.4.25", + "date": "Wed, 12 Mar 2025 00:11:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + } + ] + } + }, + { + "version": "1.4.24", + "tag": "@rushstack/debug-certificate-manager_v1.4.24", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + } + ] + } + }, + { + "version": "1.4.23", + "tag": "@rushstack/debug-certificate-manager_v1.4.23", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + } + ] + } + }, + { + "version": "1.4.22", + "tag": "@rushstack/debug-certificate-manager_v1.4.22", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + } + ] + } + }, + { + "version": "1.4.21", + "tag": "@rushstack/debug-certificate-manager_v1.4.21", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + } + ] + } + }, + { + "version": "1.4.20", + "tag": "@rushstack/debug-certificate-manager_v1.4.20", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + } + ] + } + }, + { + "version": "1.4.19", + "tag": "@rushstack/debug-certificate-manager_v1.4.19", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + } + ] + } + }, + { + "version": "1.4.18", + "tag": "@rushstack/debug-certificate-manager_v1.4.18", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + } + ] + } + }, + { + "version": "1.4.17", + "tag": "@rushstack/debug-certificate-manager_v1.4.17", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + } + ] + } + }, + { + "version": "1.4.16", + "tag": "@rushstack/debug-certificate-manager_v1.4.16", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + } + ] + } + }, + { + "version": "1.4.15", + "tag": "@rushstack/debug-certificate-manager_v1.4.15", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + } + ] + } + }, + { + "version": "1.4.14", + "tag": "@rushstack/debug-certificate-manager_v1.4.14", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + } + ] + } + }, + { + "version": "1.4.13", + "tag": "@rushstack/debug-certificate-manager_v1.4.13", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + } + ] + } + }, + { + "version": "1.4.12", + "tag": "@rushstack/debug-certificate-manager_v1.4.12", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + } + ] + } + }, + { + "version": "1.4.11", + "tag": "@rushstack/debug-certificate-manager_v1.4.11", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + } + ] + } + }, + { + "version": "1.4.10", + "tag": "@rushstack/debug-certificate-manager_v1.4.10", + "date": "Tue, 03 Dec 2024 16:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + } + ] + } + }, + { + "version": "1.4.9", + "tag": "@rushstack/debug-certificate-manager_v1.4.9", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + } + ] + } + }, + { + "version": "1.4.8", + "tag": "@rushstack/debug-certificate-manager_v1.4.8", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + } + ] + } + }, + { + "version": "1.4.7", + "tag": "@rushstack/debug-certificate-manager_v1.4.7", + "date": "Thu, 24 Oct 2024 00:15:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + } + ] + } + }, + { + "version": "1.4.6", + "tag": "@rushstack/debug-certificate-manager_v1.4.6", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + } + ] + } + }, + { + "version": "1.4.5", + "tag": "@rushstack/debug-certificate-manager_v1.4.5", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + } + ] + } + }, + { + "version": "1.4.4", + "tag": "@rushstack/debug-certificate-manager_v1.4.4", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + } + ] + } + }, + { + "version": "1.4.3", + "tag": "@rushstack/debug-certificate-manager_v1.4.3", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + } + ] + } + }, + { + "version": "1.4.2", + "tag": "@rushstack/debug-certificate-manager_v1.4.2", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + } + ] + } + }, + { + "version": "1.4.1", + "tag": "@rushstack/debug-certificate-manager_v1.4.1", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + } + ] + } + }, + { + "version": "1.4.0", + "tag": "@rushstack/debug-certificate-manager_v1.4.0", + "date": "Sat, 21 Sep 2024 00:10:27 GMT", + "comments": { + "minor": [ + { + "comment": "Add a `skipCertificateTrust` option to `CertificateManager.ensureCertificateAsync` that skips automatically trusting the generated certificate and untrusting an existing certificate with issues." + } + ] + } + }, + { + "version": "1.3.66", + "tag": "@rushstack/debug-certificate-manager_v1.3.66", + "date": "Fri, 13 Sep 2024 00:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + } + ] + } + }, + { + "version": "1.3.65", + "tag": "@rushstack/debug-certificate-manager_v1.3.65", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + } + ] + } + }, + { + "version": "1.3.64", + "tag": "@rushstack/debug-certificate-manager_v1.3.64", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + } + ] + } + }, + { + "version": "1.3.63", + "tag": "@rushstack/debug-certificate-manager_v1.3.63", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + } + ] + } + }, + { + "version": "1.3.62", + "tag": "@rushstack/debug-certificate-manager_v1.3.62", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + } + ] + } + }, + { + "version": "1.3.61", + "tag": "@rushstack/debug-certificate-manager_v1.3.61", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + } + ] + } + }, + { + "version": "1.3.60", + "tag": "@rushstack/debug-certificate-manager_v1.3.60", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + } + ] + } + }, + { + "version": "1.3.59", + "tag": "@rushstack/debug-certificate-manager_v1.3.59", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + } + ] + } + }, + { + "version": "1.3.58", + "tag": "@rushstack/debug-certificate-manager_v1.3.58", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + } + ] + } + }, + { + "version": "1.3.57", + "tag": "@rushstack/debug-certificate-manager_v1.3.57", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + } + ] + } + }, + { + "version": "1.3.56", + "tag": "@rushstack/debug-certificate-manager_v1.3.56", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + } + ] + } + }, + { + "version": "1.3.55", + "tag": "@rushstack/debug-certificate-manager_v1.3.55", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + } + ] + } + }, + { + "version": "1.3.54", + "tag": "@rushstack/debug-certificate-manager_v1.3.54", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "patch": [ + { + "comment": "Include missing `type` modifiers on type-only exports." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + } + ] + } + }, + { + "version": "1.3.53", + "tag": "@rushstack/debug-certificate-manager_v1.3.53", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + } + ] + } + }, + { + "version": "1.3.52", + "tag": "@rushstack/debug-certificate-manager_v1.3.52", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + } + ] + } + }, + { + "version": "1.3.51", + "tag": "@rushstack/debug-certificate-manager_v1.3.51", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + } + ] + } + }, + { + "version": "1.3.50", + "tag": "@rushstack/debug-certificate-manager_v1.3.50", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + } + ] + } + }, + { + "version": "1.3.49", + "tag": "@rushstack/debug-certificate-manager_v1.3.49", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + } + ] + } + }, + { + "version": "1.3.48", + "tag": "@rushstack/debug-certificate-manager_v1.3.48", + "date": "Fri, 24 May 2024 00:15:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + } + ] + } + }, + { + "version": "1.3.47", + "tag": "@rushstack/debug-certificate-manager_v1.3.47", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where the task could report success if the subprocess was terminated by a signal" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + } + ] + } + }, + { + "version": "1.3.46", + "tag": "@rushstack/debug-certificate-manager_v1.3.46", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + } + ] + } + }, + { + "version": "1.3.45", + "tag": "@rushstack/debug-certificate-manager_v1.3.45", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + } + ] + } + }, + { + "version": "1.3.44", + "tag": "@rushstack/debug-certificate-manager_v1.3.44", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + } + ] + } + }, + { + "version": "1.3.43", + "tag": "@rushstack/debug-certificate-manager_v1.3.43", + "date": "Fri, 10 May 2024 05:33:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + } + ] + } + }, + { + "version": "1.3.42", + "tag": "@rushstack/debug-certificate-manager_v1.3.42", + "date": "Wed, 08 May 2024 22:23:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + } + ] + } + }, + { + "version": "1.3.41", + "tag": "@rushstack/debug-certificate-manager_v1.3.41", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + } + ] + } + }, + { + "version": "1.3.40", + "tag": "@rushstack/debug-certificate-manager_v1.3.40", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + } + ] + } + }, + { + "version": "1.3.39", + "tag": "@rushstack/debug-certificate-manager_v1.3.39", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + } + ] + } + }, + { + "version": "1.3.38", + "tag": "@rushstack/debug-certificate-manager_v1.3.38", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + } + ] + } + }, + { + "version": "1.3.37", + "tag": "@rushstack/debug-certificate-manager_v1.3.37", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + } + ] + } + }, + { + "version": "1.3.36", + "tag": "@rushstack/debug-certificate-manager_v1.3.36", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + } + ] + } + }, + { + "version": "1.3.35", + "tag": "@rushstack/debug-certificate-manager_v1.3.35", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + } + ] + } + }, + { + "version": "1.3.34", + "tag": "@rushstack/debug-certificate-manager_v1.3.34", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + } + ] + } + }, + { + "version": "1.3.33", + "tag": "@rushstack/debug-certificate-manager_v1.3.33", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + } + ] + } + }, + { + "version": "1.3.32", + "tag": "@rushstack/debug-certificate-manager_v1.3.32", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + } + ] + } + }, + { + "version": "1.3.31", + "tag": "@rushstack/debug-certificate-manager_v1.3.31", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + } + ] + } + }, + { + "version": "1.3.30", + "tag": "@rushstack/debug-certificate-manager_v1.3.30", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + } + ] + } + }, + { + "version": "1.3.29", + "tag": "@rushstack/debug-certificate-manager_v1.3.29", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + } + ] + } + }, + { + "version": "1.3.28", + "tag": "@rushstack/debug-certificate-manager_v1.3.28", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + } + ] + } + }, + { + "version": "1.3.27", + "tag": "@rushstack/debug-certificate-manager_v1.3.27", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + } + ] + } + }, + { + "version": "1.3.26", + "tag": "@rushstack/debug-certificate-manager_v1.3.26", + "date": "Tue, 20 Feb 2024 16:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + } + ] + } + }, + { + "version": "1.3.25", + "tag": "@rushstack/debug-certificate-manager_v1.3.25", + "date": "Mon, 19 Feb 2024 21:54:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + } + ] + } + }, + { + "version": "1.3.24", + "tag": "@rushstack/debug-certificate-manager_v1.3.24", + "date": "Sat, 17 Feb 2024 06:24:34 GMT", + "comments": { + "patch": [ + { + "comment": "Fix broken link to API documentation" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + } + ] + } + }, + { + "version": "1.3.23", + "tag": "@rushstack/debug-certificate-manager_v1.3.23", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + } + ] + } + }, + { + "version": "1.3.22", + "tag": "@rushstack/debug-certificate-manager_v1.3.22", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + } + ] + } + }, + { + "version": "1.3.21", + "tag": "@rushstack/debug-certificate-manager_v1.3.21", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + } + ] + } + }, + { + "version": "1.3.20", + "tag": "@rushstack/debug-certificate-manager_v1.3.20", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + } + ] + } + }, + { + "version": "1.3.19", + "tag": "@rushstack/debug-certificate-manager_v1.3.19", + "date": "Tue, 23 Jan 2024 20:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + } + ] + } + }, + { + "version": "1.3.18", + "tag": "@rushstack/debug-certificate-manager_v1.3.18", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + } + ] + } + }, + { + "version": "1.3.17", + "tag": "@rushstack/debug-certificate-manager_v1.3.17", + "date": "Tue, 16 Jan 2024 18:30:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + } + ] + } + }, + { + "version": "1.3.16", + "tag": "@rushstack/debug-certificate-manager_v1.3.16", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + } + ] + } + }, + { + "version": "1.3.15", + "tag": "@rushstack/debug-certificate-manager_v1.3.15", + "date": "Wed, 20 Dec 2023 01:09:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + } + ] + } + }, + { + "version": "1.3.14", + "tag": "@rushstack/debug-certificate-manager_v1.3.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + } + ] + } + }, + { + "version": "1.3.13", + "tag": "@rushstack/debug-certificate-manager_v1.3.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + } + ] + } + }, + { + "version": "1.3.12", + "tag": "@rushstack/debug-certificate-manager_v1.3.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + } + ] + } + }, + { + "version": "1.3.11", + "tag": "@rushstack/debug-certificate-manager_v1.3.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + } + ] + } + }, + { + "version": "1.3.10", + "tag": "@rushstack/debug-certificate-manager_v1.3.10", + "date": "Mon, 30 Oct 2023 23:36:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + } + ] + } + }, + { + "version": "1.3.9", + "tag": "@rushstack/debug-certificate-manager_v1.3.9", + "date": "Sun, 01 Oct 2023 02:56:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + } + ] + } + }, + { + "version": "1.3.8", + "tag": "@rushstack/debug-certificate-manager_v1.3.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + } + ] + } + }, + { + "version": "1.3.7", + "tag": "@rushstack/debug-certificate-manager_v1.3.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + } + ] + } + }, + { + "version": "1.3.6", + "tag": "@rushstack/debug-certificate-manager_v1.3.6", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + } + ] + } + }, + { + "version": "1.3.5", + "tag": "@rushstack/debug-certificate-manager_v1.3.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + } + ] + } + }, + { + "version": "1.3.4", + "tag": "@rushstack/debug-certificate-manager_v1.3.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + } + ] + } + }, + { + "version": "1.3.3", + "tag": "@rushstack/debug-certificate-manager_v1.3.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + } + ] + } + }, + { + "version": "1.3.2", + "tag": "@rushstack/debug-certificate-manager_v1.3.2", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + } + ] + } + }, + { + "version": "1.3.1", + "tag": "@rushstack/debug-certificate-manager_v1.3.1", + "date": "Tue, 19 Sep 2023 15:21:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.24`" + } + ] + } + }, + { + "version": "1.3.0", + "tag": "@rushstack/debug-certificate-manager_v1.3.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.23`" + } + ] + } + }, + { + "version": "1.2.56", + "tag": "@rushstack/debug-certificate-manager_v1.2.56", + "date": "Wed, 13 Sep 2023 00:32:29 GMT", + "comments": { + "patch": [ + { + "comment": "Fixes issues with CertificateManager when setting the certificate friendly name fails." + } + ] + } + }, + { + "version": "1.2.55", + "tag": "@rushstack/debug-certificate-manager_v1.2.55", + "date": "Tue, 08 Aug 2023 07:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.22`" + } + ] + } + }, + { + "version": "1.2.54", + "tag": "@rushstack/debug-certificate-manager_v1.2.54", + "date": "Mon, 31 Jul 2023 15:19:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.21`" + } + ] + } + }, + { + "version": "1.2.53", + "tag": "@rushstack/debug-certificate-manager_v1.2.53", + "date": "Sat, 29 Jul 2023 00:22:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.20`" + } + ] + } + }, + { + "version": "1.2.52", + "tag": "@rushstack/debug-certificate-manager_v1.2.52", + "date": "Thu, 20 Jul 2023 20:47:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.19`" + } + ] + } + }, + { + "version": "1.2.51", + "tag": "@rushstack/debug-certificate-manager_v1.2.51", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.18`" + } + ] + } + }, + { + "version": "1.2.50", + "tag": "@rushstack/debug-certificate-manager_v1.2.50", + "date": "Fri, 14 Jul 2023 15:20:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.17`" + } + ] + } + }, + { + "version": "1.2.49", + "tag": "@rushstack/debug-certificate-manager_v1.2.49", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.16`" + } + ] + } + }, + { + "version": "1.2.48", + "tag": "@rushstack/debug-certificate-manager_v1.2.48", + "date": "Wed, 12 Jul 2023 15:20:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.15`" + } + ] + } + }, + { + "version": "1.2.47", + "tag": "@rushstack/debug-certificate-manager_v1.2.47", + "date": "Wed, 12 Jul 2023 00:23:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.14`" + } + ] + } + }, + { + "version": "1.2.46", + "tag": "@rushstack/debug-certificate-manager_v1.2.46", + "date": "Fri, 07 Jul 2023 00:19:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.13`" + } + ] + } + }, + { + "version": "1.2.45", + "tag": "@rushstack/debug-certificate-manager_v1.2.45", + "date": "Thu, 06 Jul 2023 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.12`" + } + ] + } + }, + { + "version": "1.2.44", + "tag": "@rushstack/debug-certificate-manager_v1.2.44", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.11`" + } + ] + } + }, + { + "version": "1.2.43", + "tag": "@rushstack/debug-certificate-manager_v1.2.43", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.10`" + } + ] + } + }, + { + "version": "1.2.42", + "tag": "@rushstack/debug-certificate-manager_v1.2.42", + "date": "Thu, 15 Jun 2023 00:21:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.9`" + } + ] + } + }, + { + "version": "1.2.41", + "tag": "@rushstack/debug-certificate-manager_v1.2.41", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.8`" + } + ] + } + }, + { + "version": "1.2.40", + "tag": "@rushstack/debug-certificate-manager_v1.2.40", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.7`" + } + ] + } + }, + { + "version": "1.2.39", + "tag": "@rushstack/debug-certificate-manager_v1.2.39", + "date": "Tue, 13 Jun 2023 01:49:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.6`" + } + ] + } + }, + { + "version": "1.2.38", + "tag": "@rushstack/debug-certificate-manager_v1.2.38", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.5`" + } + ] + } + }, + { + "version": "1.2.37", + "tag": "@rushstack/debug-certificate-manager_v1.2.37", + "date": "Fri, 09 Jun 2023 15:23:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.4`" + } + ] + } + }, + { + "version": "1.2.36", + "tag": "@rushstack/debug-certificate-manager_v1.2.36", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.3`" + } + ] + } + }, + { + "version": "1.2.35", + "tag": "@rushstack/debug-certificate-manager_v1.2.35", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.2`" + } + ] + } + }, + { + "version": "1.2.34", + "tag": "@rushstack/debug-certificate-manager_v1.2.34", + "date": "Thu, 08 Jun 2023 00:20:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.1`" + } + ] + } + }, + { + "version": "1.2.33", + "tag": "@rushstack/debug-certificate-manager_v1.2.33", + "date": "Wed, 07 Jun 2023 22:45:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.0`" + } + ] + } + }, + { + "version": "1.2.32", + "tag": "@rushstack/debug-certificate-manager_v1.2.32", + "date": "Tue, 06 Jun 2023 02:52:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.1.0`" + } + ] + } + }, + { + "version": "1.2.31", + "tag": "@rushstack/debug-certificate-manager_v1.2.31", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.0.1`" + } + ] + } + }, { "version": "1.2.30", "tag": "@rushstack/debug-certificate-manager_v1.2.30", diff --git a/libraries/debug-certificate-manager/CHANGELOG.md b/libraries/debug-certificate-manager/CHANGELOG.md index 2855056b269..38e357b97b3 100644 --- a/libraries/debug-certificate-manager/CHANGELOG.md +++ b/libraries/debug-certificate-manager/CHANGELOG.md @@ -1,6 +1,947 @@ # Change Log - @rushstack/debug-certificate-manager -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 1.7.23 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 1.7.22 +Fri, 17 Jul 2026 00:15:59 GMT + +_Version update only_ + +## 1.7.21 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 1.7.20 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 1.7.19 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 1.7.18 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 1.7.17 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 1.7.16 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 1.7.15 +Sat, 18 Apr 2026 00:15:16 GMT + +_Version update only_ + +## 1.7.14 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 1.7.13 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 1.7.12 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 1.7.11 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 1.7.10 +Thu, 02 Apr 2026 00:14:38 GMT + +### Patches + +- Bump node-forge to 1.4.0 to address CVEs GHSA-2328-f5f3-gj25, GHSA-q67f-28xg-22rw, GHSA-5m6q-g25r-mvwx, GHSA-ppp5-5v6c-4jwp" + +## 1.7.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 1.7.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 1.7.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 1.7.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 1.7.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 1.7.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 1.7.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 1.7.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 1.7.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 1.7.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 1.6.14 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 1.6.13 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 1.6.12 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 1.6.11 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 1.6.10 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 1.6.9 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 1.6.8 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 1.6.7 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 1.6.6 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 1.6.5 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 1.6.4 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 1.6.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 1.6.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 1.6.1 +Wed, 08 Oct 2025 00:13:28 GMT + +### Patches + +- Add support for the IPv6 localhost address (`::1`). + +## 1.6.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 1.5.9 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 1.5.8 +Tue, 30 Sep 2025 20:33:50 GMT + +### Patches + +- Add message to use VS Code extension to errors. + +## 1.5.7 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 1.5.6 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 1.5.5 +Fri, 29 Aug 2025 00:08:01 GMT + +### Patches + +- Fix homedir resolution in CertificateStore + +## 1.5.4 +Tue, 26 Aug 2025 00:12:57 GMT + +### Patches + +- Fix handling of home directory paths when reading debug-certificate-manager.json config file. + +## 1.5.3 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 1.5.2 +Fri, 01 Aug 2025 00:12:48 GMT + +_Version update only_ + +## 1.5.1 +Sat, 26 Jul 2025 00:12:22 GMT + +### Patches + +- Read CertificateStore configuration from .vscode/debug-certificate-manager.json + +## 1.5.0 +Wed, 23 Jul 2025 20:55:57 GMT + +### Minor changes + +- CertificateStore - Add params to support custom paths and filenames +- CertificateManager - Update `untrustCertificateAsync` to clear `caCertificateData` +- CertificateManager - Use osascript (applescript) to run elevated command on macOS instead of sudo package. +- CertificateManager - Expose `getCertificateExpirationAsync` method to retrieve certificate expiration date + +## 1.4.37 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 1.4.36 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 1.4.35 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 1.4.34 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 1.4.33 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 1.4.32 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 1.4.31 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 1.4.30 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 1.4.29 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 1.4.28 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 1.4.27 +Tue, 25 Mar 2025 15:11:15 GMT + +_Version update only_ + +## 1.4.26 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 1.4.25 +Wed, 12 Mar 2025 00:11:31 GMT + +_Version update only_ + +## 1.4.24 +Tue, 11 Mar 2025 02:12:33 GMT + +_Version update only_ + +## 1.4.23 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 1.4.22 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 1.4.21 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 1.4.20 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 1.4.19 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 1.4.18 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 1.4.17 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 1.4.16 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 1.4.15 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 1.4.14 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 1.4.13 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 1.4.12 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 1.4.11 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 1.4.10 +Tue, 03 Dec 2024 16:11:07 GMT + +_Version update only_ + +## 1.4.9 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 1.4.8 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 1.4.7 +Thu, 24 Oct 2024 00:15:47 GMT + +_Version update only_ + +## 1.4.6 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 1.4.5 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 1.4.4 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 1.4.3 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 1.4.2 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 1.4.1 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 1.4.0 +Sat, 21 Sep 2024 00:10:27 GMT + +### Minor changes + +- Add a `skipCertificateTrust` option to `CertificateManager.ensureCertificateAsync` that skips automatically trusting the generated certificate and untrusting an existing certificate with issues. + +## 1.3.66 +Fri, 13 Sep 2024 00:11:42 GMT + +_Version update only_ + +## 1.3.65 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 1.3.64 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 1.3.63 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 1.3.62 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 1.3.61 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 1.3.60 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 1.3.59 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 1.3.58 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 1.3.57 +Tue, 16 Jul 2024 00:36:21 GMT + +_Version update only_ + +## 1.3.56 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 1.3.55 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 1.3.54 +Thu, 30 May 2024 00:13:05 GMT + +### Patches + +- Include missing `type` modifiers on type-only exports. + +## 1.3.53 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 1.3.52 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 1.3.51 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 1.3.50 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 1.3.49 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 1.3.48 +Fri, 24 May 2024 00:15:08 GMT + +_Version update only_ + +## 1.3.47 +Thu, 23 May 2024 02:26:56 GMT + +### Patches + +- Fix an issue where the task could report success if the subprocess was terminated by a signal + +## 1.3.46 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 1.3.45 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 1.3.44 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 1.3.43 +Fri, 10 May 2024 05:33:33 GMT + +_Version update only_ + +## 1.3.42 +Wed, 08 May 2024 22:23:50 GMT + +_Version update only_ + +## 1.3.41 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 1.3.40 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 1.3.39 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 1.3.38 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 1.3.37 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 1.3.36 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 1.3.35 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 1.3.34 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 1.3.33 +Thu, 29 Feb 2024 07:11:45 GMT + +_Version update only_ + +## 1.3.32 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 1.3.31 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 1.3.30 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 1.3.29 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 1.3.28 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 1.3.27 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 1.3.26 +Tue, 20 Feb 2024 16:10:52 GMT + +_Version update only_ + +## 1.3.25 +Mon, 19 Feb 2024 21:54:26 GMT + +_Version update only_ + +## 1.3.24 +Sat, 17 Feb 2024 06:24:34 GMT + +### Patches + +- Fix broken link to API documentation + +## 1.3.23 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 1.3.22 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 1.3.21 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 1.3.20 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 1.3.19 +Tue, 23 Jan 2024 20:12:57 GMT + +_Version update only_ + +## 1.3.18 +Tue, 23 Jan 2024 16:15:05 GMT + +_Version update only_ + +## 1.3.17 +Tue, 16 Jan 2024 18:30:11 GMT + +_Version update only_ + +## 1.3.16 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 1.3.15 +Wed, 20 Dec 2023 01:09:45 GMT + +_Version update only_ + +## 1.3.14 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 1.3.13 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 1.3.12 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 1.3.11 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 1.3.10 +Mon, 30 Oct 2023 23:36:37 GMT + +_Version update only_ + +## 1.3.9 +Sun, 01 Oct 2023 02:56:29 GMT + +_Version update only_ + +## 1.3.8 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 1.3.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 1.3.6 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 1.3.5 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 1.3.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 1.3.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 1.3.2 +Fri, 22 Sep 2023 00:05:50 GMT + +_Version update only_ + +## 1.3.1 +Tue, 19 Sep 2023 15:21:52 GMT + +_Version update only_ + +## 1.3.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 1.2.56 +Wed, 13 Sep 2023 00:32:29 GMT + +### Patches + +- Fixes issues with CertificateManager when setting the certificate friendly name fails. + +## 1.2.55 +Tue, 08 Aug 2023 07:10:39 GMT + +_Version update only_ + +## 1.2.54 +Mon, 31 Jul 2023 15:19:05 GMT + +_Version update only_ + +## 1.2.53 +Sat, 29 Jul 2023 00:22:50 GMT + +_Version update only_ + +## 1.2.52 +Thu, 20 Jul 2023 20:47:28 GMT + +_Version update only_ + +## 1.2.51 +Wed, 19 Jul 2023 00:20:31 GMT + +_Version update only_ + +## 1.2.50 +Fri, 14 Jul 2023 15:20:45 GMT + +_Version update only_ + +## 1.2.49 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 1.2.48 +Wed, 12 Jul 2023 15:20:39 GMT + +_Version update only_ + +## 1.2.47 +Wed, 12 Jul 2023 00:23:29 GMT + +_Version update only_ + +## 1.2.46 +Fri, 07 Jul 2023 00:19:32 GMT + +_Version update only_ + +## 1.2.45 +Thu, 06 Jul 2023 00:16:19 GMT + +_Version update only_ + +## 1.2.44 +Tue, 04 Jul 2023 00:18:47 GMT + +_Version update only_ + +## 1.2.43 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 1.2.42 +Thu, 15 Jun 2023 00:21:01 GMT + +_Version update only_ + +## 1.2.41 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 1.2.40 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 1.2.39 +Tue, 13 Jun 2023 01:49:02 GMT + +_Version update only_ + +## 1.2.38 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 1.2.37 +Fri, 09 Jun 2023 15:23:15 GMT + +_Version update only_ + +## 1.2.36 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 1.2.35 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 1.2.34 +Thu, 08 Jun 2023 00:20:02 GMT + +_Version update only_ + +## 1.2.33 +Wed, 07 Jun 2023 22:45:17 GMT + +_Version update only_ + +## 1.2.32 +Tue, 06 Jun 2023 02:52:51 GMT + +_Version update only_ + +## 1.2.31 +Mon, 05 Jun 2023 21:45:21 GMT + +_Version update only_ ## 1.2.30 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/libraries/debug-certificate-manager/README.md b/libraries/debug-certificate-manager/README.md index 601e4e24298..59ecac4eb78 100644 --- a/libraries/debug-certificate-manager/README.md +++ b/libraries/debug-certificate-manager/README.md @@ -45,6 +45,6 @@ Attempts to locate a previously generated debug certificate and untrust it. Retu - [CHANGELOG.md]( https://github.com/microsoft/rushstack/blob/main/libraries/debug-certificate-manager/CHANGELOG.md) - Find out what's new in the latest version -- [API Reference](https://rushstack.io/pages/api/debug-certificate-manager/) +- [API Reference](https://api.rushstack.io/pages/debug-certificate-manager/) **@rushstack/debug-certificate-manager** is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/libraries/debug-certificate-manager/config/api-extractor.json b/libraries/debug-certificate-manager/config/api-extractor.json index 996e271d3dd..3dbb76c0e6f 100644 --- a/libraries/debug-certificate-manager/config/api-extractor.json +++ b/libraries/debug-certificate-manager/config/api-extractor.json @@ -1,19 +1,4 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - - "mainEntryPointFilePath": "/lib/index.d.ts", - - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, - - "docModel": { - "enabled": true, - "apiJsonFilePath": "../../../common/temp/api/.api.json" - }, - - "dtsRollup": { - "enabled": true - } + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json" } diff --git a/libraries/debug-certificate-manager/config/rig.json b/libraries/debug-certificate-manager/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/libraries/debug-certificate-manager/config/rig.json +++ b/libraries/debug-certificate-manager/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/libraries/debug-certificate-manager/eslint.config.js b/libraries/debug-certificate-manager/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/libraries/debug-certificate-manager/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/debug-certificate-manager/package.json b/libraries/debug-certificate-manager/package.json index 21ba40a22e7..6435bc3034d 100644 --- a/libraries/debug-certificate-manager/package.json +++ b/libraries/debug-certificate-manager/package.json @@ -1,9 +1,32 @@ { "name": "@rushstack/debug-certificate-manager", - "version": "1.2.30", + "version": "1.7.23", "description": "Cross-platform functionality to create debug ssl certificates.", - "main": "lib/index.js", - "typings": "dist/debug-certificate-manager.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/debug-certificate-manager.d.ts", + "exports": { + ".": { + "types": "./dist/debug-certificate-manager.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "license": "MIT", "repository": { "url": "https://github.com/microsoft/rushstack.git", @@ -16,15 +39,14 @@ }, "dependencies": { "@rushstack/node-core-library": "workspace:*", - "node-forge": "~1.3.1", - "sudo": "~1.0.3" + "@rushstack/terminal": "workspace:*", + "node-forge": "~1.4.0" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "@types/node-forge": "1.0.4" - } + "@types/node-forge": "1.3.14", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "sideEffects": false } diff --git a/libraries/debug-certificate-manager/src/CertificateManager.ts b/libraries/debug-certificate-manager/src/CertificateManager.ts index d57c6852a15..98456579fb9 100644 --- a/libraries/debug-certificate-manager/src/CertificateManager.ts +++ b/libraries/debug-certificate-manager/src/CertificateManager.ts @@ -1,13 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import * as path from 'node:path'; +import { EOL } from 'node:os'; + import type { pki } from 'node-forge'; -import * as path from 'path'; -import { EOL } from 'os'; -import { FileSystem, ITerminal } from '@rushstack/node-core-library'; -import { runSudoAsync, IRunResult, runAsync } from './runCommand'; -import { CertificateStore } from './CertificateStore'; +import { FileSystem } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import { darwinRunSudoAsync, type IRunResult, randomTmpPath, runAsync } from './runCommand'; +import { CertificateStore, type ICertificateStoreOptions } from './CertificateStore'; const CA_SERIAL_NUMBER: string = '731c321744e34650a202e3ef91c3c1b0'; const TLS_SERIAL_NUMBER: string = '731c321744e34650a202e3ef00000001'; @@ -27,7 +30,7 @@ export const DEFAULT_CERTIFICATE_SUBJECT_NAMES: ReadonlyArray = ['localh * The set of ip addresses the certificate should be generated for, by default. * @public */ -export const DEFAULT_CERTIFICATE_SUBJECT_IP_ADDRESSES: ReadonlyArray = ['127.0.0.1']; +export const DEFAULT_CERTIFICATE_SUBJECT_IP_ADDRESSES: ReadonlyArray = ['127.0.0.1', '::1']; const DISABLE_CERT_GENERATION_VARIABLE_NAME: 'RUSHSTACK_DISABLE_DEV_CERT_GENERATION' = 'RUSHSTACK_DISABLE_DEV_CERT_GENERATION'; @@ -59,6 +62,27 @@ export interface ICertificate { subjectAltNames: readonly string[] | undefined; } +/** + * Information about certificate validation results + * @public + */ +export interface ICertificateValidationResult { + /** + * Whether valid certificates exist and are usable + */ + isValid: boolean; + + /** + * List of validation messages/issues found + */ + validationMessages: string[]; + + /** + * The existing certificate if it exists and is valid + */ + certificate?: ICertificate; +} + interface ICaCertificate { /** * Certificate @@ -109,20 +133,38 @@ export interface ICertificateGenerationOptions { * How many days the certificate should be valid for. */ validityInDays?: number; + /** + * Skip trusting a certificate. Defaults to false. + */ + skipCertificateTrust?: boolean; } +/** + * Options for configuring the `CertificateManager`. + * @public + */ +export interface ICertificateManagerOptions extends ICertificateStoreOptions {} + const MAX_CERTIFICATE_VALIDITY_DAYS: 365 = 365; +const VS_CODE_EXTENSION_FIX_MESSAGE: string = + 'Use the "Debug Certificate Manager" Extension for VS Code (ms-RushStack.debug-certificate-manager) and run the ' + + '"Debug Certificate Manager: Ensure and Sync TLS Certificates" command to fix certificate issues. '; + /** * A utility class to handle generating, trusting, and untrustring a debug certificate. * Contains two public methods to `ensureCertificate` and `untrustCertificate`. * @public */ export class CertificateManager { - private _certificateStore: CertificateStore; + /** + * Get the certificate store used by this manager. + * @public + */ + public readonly certificateStore: CertificateStore; - public constructor() { - this._certificateStore = new CertificateStore(); + public constructor(options: ICertificateManagerOptions = {}) { + this.certificateStore = new CertificateStore(options); } /** @@ -134,122 +176,56 @@ export class CertificateManager { public async ensureCertificateAsync( canGenerateNewCertificate: boolean, terminal: ITerminal, - generationOptions?: ICertificateGenerationOptions + options?: ICertificateGenerationOptions ): Promise { - const optionsWithDefaults: Required = - applyDefaultOptions(generationOptions); - - const { certificateData: existingCert, keyData: existingKey } = this._certificateStore; + const optionsWithDefaults: Required = applyDefaultOptions(options); if (process.env[DISABLE_CERT_GENERATION_VARIABLE_NAME] === '1') { // Allow the environment (e.g. GitHub codespaces) to forcibly disable dev cert generation terminal.writeLine( - `Found environment variable ${DISABLE_CERT_GENERATION_VARIABLE_NAME}=1, disabling certificate generation.` + `Found environment variable ${DISABLE_CERT_GENERATION_VARIABLE_NAME}=1, disabling certificate generation. ` + + VS_CODE_EXTENSION_FIX_MESSAGE ); canGenerateNewCertificate = false; } - if (existingCert && existingKey) { - const messages: string[] = []; - - const forge: typeof import('node-forge') = await import('node-forge'); - const certificate: pki.Certificate = forge.pki.certificateFromPem(existingCert); - const altNamesExtension: ISubjectAltNameExtension | undefined = certificate.getExtension( - 'subjectAltName' - ) as ISubjectAltNameExtension; - if (!altNamesExtension) { - messages.push( - 'The existing development certificate is missing the subjectAltName ' + - 'property and will not work with the latest versions of some browsers.' - ); - } else { - const missingSubjectNames: Set = new Set(optionsWithDefaults.subjectAltNames); - for (const altName of altNamesExtension.altNames) { - missingSubjectNames.delete(isIPAddress(altName) ? altName.ip : altName.value); - } - if (missingSubjectNames.size) { - messages.push( - `The existing development certificate does not include the following expected subjectAltName values: ` + - Array.from(missingSubjectNames, (name: string) => `"${name}"`).join(', ') - ); - } - } - - const { notBefore, notAfter } = certificate.validity; - const now: Date = new Date(); - if (now < notBefore) { - messages.push( - `The existing development certificate's validity period does not start until ${notBefore}. It is currently ${now}.` - ); - } - - if (now > notAfter) { - messages.push( - `The existing development certificate's validity period ended ${notAfter}. It is currently ${now}.` - ); - } - - now.setUTCDate(now.getUTCDate() + optionsWithDefaults.validityInDays); - if (notAfter > now) { - messages.push( - `The existing development certificate's expiration date ${notAfter} exceeds the allowed limit ${now}. ` + - `This will be rejected by many browsers.` - ); - } - - if ( - notBefore.getTime() - notAfter.getTime() > - optionsWithDefaults.validityInDays * ONE_DAY_IN_MILLISECONDS - ) { - messages.push( - "The existing development certificate's validity period is longer " + - `than ${optionsWithDefaults.validityInDays} days.` - ); - } + // Validate existing certificates + const validationResult: ICertificateValidationResult = await this.validateCertificateAsync( + terminal, + options + ); - const { caCertificateData } = this._certificateStore; + if (validationResult.isValid && validationResult.certificate) { + // Existing certificate is valid, return it + return validationResult.certificate; + } - if (!caCertificateData) { - messages.push( - 'The existing development certificate is missing a separate CA cert as the root ' + - 'of trust and will not work with the latest versions of some browsers.' + // Certificate is invalid or doesn't exist + if (validationResult.validationMessages.length > 0) { + if (canGenerateNewCertificate) { + validationResult.validationMessages.push( + 'Attempting to untrust the certificate and generate a new one.' ); - } - - const isTrusted: boolean = await this._detectIfCertificateIsTrustedAsync(terminal); - if (!isTrusted) { - messages.push('The existing development certificate is not currently trusted by your system.'); - } - - if (messages.length > 0) { - if (canGenerateNewCertificate) { - messages.push('Attempting to untrust the certificate and generate a new one.'); - terminal.writeWarningLine(messages.join(' ')); + terminal.writeWarningLine(validationResult.validationMessages.join(' ')); + if (!options?.skipCertificateTrust) { await this.untrustCertificateAsync(terminal); - return await this._ensureCertificateInternalAsync(optionsWithDefaults, terminal); - } else { - messages.push( - 'Untrust the certificate and generate a new one, or set the ' + - '`canGenerateNewCertificate` parameter to `true` when calling `ensureCertificateAsync`.' - ); - throw new Error(messages.join(' ')); } + return await this._ensureCertificateInternalAsync(optionsWithDefaults, terminal); } else { - return { - pemCaCertificate: caCertificateData, - pemCertificate: existingCert, - pemKey: existingKey, - subjectAltNames: altNamesExtension.altNames.map((entry) => - isIPAddress(entry) ? entry.ip : entry.value - ) - }; + validationResult.validationMessages.push( + 'Untrust the certificate and generate a new one, or set the ' + + '`canGenerateNewCertificate` parameter to `true` when calling `ensureCertificateAsync`. ' + + VS_CODE_EXTENSION_FIX_MESSAGE + ); + throw new Error(validationResult.validationMessages.join(' ')); } } else if (canGenerateNewCertificate) { return await this._ensureCertificateInternalAsync(optionsWithDefaults, terminal); } else { throw new Error( 'No development certificate found. Generate a new certificate manually, or set the ' + - '`canGenerateNewCertificate` parameter to `true` when calling `ensureCertificateAsync`.' + '`canGenerateNewCertificate` parameter to `true` when calling `ensureCertificateAsync`. ' + + VS_CODE_EXTENSION_FIX_MESSAGE ); } } @@ -260,8 +236,9 @@ export class CertificateManager { * @public */ public async untrustCertificateAsync(terminal: ITerminal): Promise { - this._certificateStore.certificateData = undefined; - this._certificateStore.keyData = undefined; + this.certificateStore.certificateData = undefined; + this.certificateStore.keyData = undefined; + this.certificateStore.caCertificateData = undefined; switch (process.platform) { case 'win32': @@ -272,7 +249,7 @@ export class CertificateManager { CA_SERIAL_NUMBER ]); - if (winUntrustResult.code !== 0) { + if (winUntrustResult.exitCode !== 0) { terminal.writeErrorLine(`Error: ${winUntrustResult.stderr.join(' ')}`); return false; } else { @@ -286,12 +263,12 @@ export class CertificateManager { const macFindCertificateResult: IRunResult = await runAsync('security', [ 'find-certificate', '-c', - 'localhost', + CA_ALT_NAME, '-a', '-Z', MAC_KEYCHAIN ]); - if (macFindCertificateResult.code !== 0) { + if (macFindCertificateResult.exitCode !== 0) { terminal.writeErrorLine( `Error finding the development certificate: ${macFindCertificateResult.stderr.join(' ')}` ); @@ -309,14 +286,14 @@ export class CertificateManager { terminal.writeVerboseLine(`Found the development certificate. SHA is ${shaHash}`); } - const macUntrustResult: IRunResult = await runSudoAsync('security', [ + const macUntrustResult: IRunResult = await darwinRunSudoAsync(terminal, 'security', [ 'delete-certificate', '-Z', shaHash, MAC_KEYCHAIN ]); - if (macUntrustResult.code === 0) { + if (macUntrustResult.exitCode === 0) { terminal.writeVerboseLine('Successfully untrusted development certificate.'); return true; } else { @@ -329,7 +306,7 @@ export class CertificateManager { terminal.writeLine( 'Automatic certificate untrust is only implemented for debug-certificate-manager on Windows ' + 'and macOS. To untrust the development certificate, remove this certificate from your trusted ' + - `root certification authorities: "${this._certificateStore.certificatePath}". The ` + + `root certification authorities: "${this.certificateStore.certificatePath}". The ` + `certificate has serial number "${CA_SERIAL_NUMBER}".` ); return false; @@ -403,7 +380,7 @@ export class CertificateManager { ]); // self-sign certificate - certificate.sign(keys.privateKey, forge.md.sha256.create()); + certificate.sign(keys.privateKey as pki.rsa.PrivateKey, forge.md.sha256.create()); return { certificate, @@ -498,7 +475,7 @@ export class CertificateManager { ]); // Sign certificate with CA - certificate.sign(caPrivateKey, forge.md.sha256.create()); + certificate.sign(caPrivateKey as pki.rsa.PrivateKey, forge.md.sha256.create()); // convert a Forge certificate to PEM const caPem: string = forge.pki.certificateToPem(caCertificate); @@ -529,7 +506,7 @@ export class CertificateManager { certificatePath ]); - if (winTrustResult.code !== 0) { + if (winTrustResult.exitCode !== 0) { terminal.writeErrorLine(`Error: ${winTrustResult.stdout.toString()}`); const errorLines: string[] = winTrustResult.stdout @@ -539,7 +516,7 @@ export class CertificateManager { // Not sure if this is always the status code for "cancelled" - should confirm. if ( - winTrustResult.code === 2147943623 || + winTrustResult.exitCode === 2147943623 || errorLines[errorLines.length - 1].indexOf('The operation was canceled by the user.') > 0 ) { terminal.writeLine('Certificate trust cancelled.'); @@ -562,7 +539,7 @@ export class CertificateManager { 'root password in the prompt.' ); - const result: IRunResult = await runSudoAsync('security', [ + const result: IRunResult = await darwinRunSudoAsync(terminal, 'security', [ 'add-trusted-cert', '-d', '-r', @@ -572,7 +549,7 @@ export class CertificateManager { certificatePath ]); - if (result.code === 0) { + if (result.exitCode === 0) { terminal.writeVerboseLine('Successfully trusted development certificate.'); return true; } else { @@ -585,7 +562,7 @@ export class CertificateManager { return false; } else { terminal.writeErrorLine( - `Certificate trust failed with an unknown error. Exit code: ${result.code}. ` + + `Certificate trust failed with an unknown error. Exit code: ${result.exitCode}. ` + `Error: ${result.stderr.join(' ')}` ); return false; @@ -613,7 +590,7 @@ export class CertificateManager { CA_SERIAL_NUMBER ]); - if (winVerifyStoreResult.code !== 0) { + if (winVerifyStoreResult.exitCode !== 0) { terminal.writeVerboseLine( 'The development certificate was not found in the store. CertUtil error: ', winVerifyStoreResult.stderr.join(' ') @@ -633,13 +610,13 @@ export class CertificateManager { const macFindCertificateResult: IRunResult = await runAsync('security', [ 'find-certificate', '-c', - 'localhost', + CA_ALT_NAME, '-a', '-Z', MAC_KEYCHAIN ]); - if (macFindCertificateResult.code !== 0) { + if (macFindCertificateResult.exitCode !== 0) { terminal.writeVerboseLine( 'The development certificate was not found in keychain. Find certificate error: ', macFindCertificateResult.stderr.join(' ') @@ -667,7 +644,7 @@ export class CertificateManager { terminal.writeVerboseLine( 'Automatic certificate trust validation is only implemented for debug-certificate-manager on Windows ' + 'and macOS. Manually verify this development certificate is present in your trusted ' + - `root certification authorities: "${this._certificateStore.certificatePath}". ` + + `root certification authorities: "${this.certificateStore.certificatePath}". ` + `The certificate has serial number "${CA_SERIAL_NUMBER}".` ); // Always return true on Linux to prevent breaking flow. @@ -699,8 +676,9 @@ export class CertificateManager { friendlyNamePath ]); - if (repairStoreResult.code !== 0) { - terminal.writeErrorLine(`CertUtil Error: ${repairStoreResult.stderr.join('')}`); + if (repairStoreResult.exitCode !== 0) { + terminal.writeVerboseLine(`CertUtil Error: ${repairStoreResult.stderr.join('')}`); + terminal.writeVerboseLine(`CertUtil: ${repairStoreResult.stdout.join('')}`); return false; } else { terminal.writeVerboseLine('Successfully set certificate name.'); @@ -716,11 +694,12 @@ export class CertificateManager { options: Required, terminal: ITerminal ): Promise { - const certificateStore: CertificateStore = this._certificateStore; + const certificateStore: CertificateStore = this.certificateStore; const generatedCertificate: ICertificate = await this._createDevelopmentCertificateAsync(options); const certificateName: string = Date.now().toString(); - const tempDirName: string = path.join(__dirname, '..', 'temp'); + const tempDirName: string = randomTmpPath('rushstack', 'temp'); + await FileSystem.ensureFolderAsync(tempDirName); const tempCertificatePath: string = path.join(tempDirName, `${certificateName}.pem`); const pemFileContents: string | undefined = generatedCertificate.pemCaCertificate; @@ -730,10 +709,9 @@ export class CertificateManager { }); } - const trustCertificateResult: boolean = await this._tryTrustCertificateAsync( - tempCertificatePath, - terminal - ); + const trustCertificateResult: boolean = options.skipCertificateTrust + ? true + : await this._tryTrustCertificateAsync(tempCertificatePath, terminal); let subjectAltNames: readonly string[] | undefined; if (trustCertificateResult) { @@ -743,7 +721,7 @@ export class CertificateManager { subjectAltNames = generatedCertificate.subjectAltNames; // Try to set the friendly name, and warn if we can't - if (!this._trySetFriendlyNameAsync(tempCertificatePath, terminal)) { + if (!(await this._trySetFriendlyNameAsync(tempCertificatePath, terminal))) { terminal.writeWarningLine("Unable to set the certificate's friendly name."); } } else { @@ -763,6 +741,116 @@ export class CertificateManager { }; } + /** + * Validate existing certificates to check if they are usable. + * + * @public + */ + public async validateCertificateAsync( + terminal: ITerminal, + options?: ICertificateGenerationOptions + ): Promise { + const optionsWithDefaults: Required = applyDefaultOptions(options); + const { certificateData: existingCert, keyData: existingKey } = this.certificateStore; + + if (!existingCert || !existingKey) { + return { + isValid: false, + validationMessages: ['No development certificate found.'] + }; + } + + const messages: string[] = []; + + const forge: typeof import('node-forge') = await import('node-forge'); + const parsedCertificate: pki.Certificate = forge.pki.certificateFromPem(existingCert); + const altNamesExtension: ISubjectAltNameExtension | undefined = parsedCertificate.getExtension( + 'subjectAltName' + ) as ISubjectAltNameExtension; + + if (!altNamesExtension) { + messages.push( + 'The existing development certificate is missing the subjectAltName ' + + 'property and will not work with the latest versions of some browsers.' + ); + } else { + const missingSubjectNames: Set = new Set(optionsWithDefaults.subjectAltNames); + for (const altName of altNamesExtension.altNames) { + missingSubjectNames.delete(isIPAddress(altName) ? altName.ip : altName.value); + } + if (missingSubjectNames.size) { + messages.push( + `The existing development certificate does not include the following expected subjectAltName values: ` + + Array.from(missingSubjectNames, (name: string) => `"${name}"`).join(', ') + ); + } + } + + const { notBefore, notAfter } = parsedCertificate.validity; + const now: Date = new Date(); + if (now < notBefore) { + messages.push( + `The existing development certificate's validity period does not start until ${notBefore}. It is currently ${now}.` + ); + } + + if (now > notAfter) { + messages.push( + `The existing development certificate's validity period ended ${notAfter}. It is currently ${now}.` + ); + } + + now.setUTCDate(now.getUTCDate() + optionsWithDefaults.validityInDays); + if (notAfter > now) { + messages.push( + `The existing development certificate's expiration date ${notAfter} exceeds the allowed limit ${now}. ` + + `This will be rejected by many browsers.` + ); + } + + if ( + notBefore.getTime() - notAfter.getTime() > + optionsWithDefaults.validityInDays * ONE_DAY_IN_MILLISECONDS + ) { + messages.push( + "The existing development certificate's validity period is longer " + + `than ${optionsWithDefaults.validityInDays} days.` + ); + } + + const { caCertificateData } = this.certificateStore; + + if (!caCertificateData) { + messages.push( + 'The existing development certificate is missing a separate CA cert as the root ' + + 'of trust and will not work with the latest versions of some browsers.' + ); + } + + const isTrusted: boolean = await this._detectIfCertificateIsTrustedAsync(terminal); + if (!isTrusted) { + messages.push('The existing development certificate is not currently trusted by your system.'); + } + + const isValid: boolean = messages.length === 0; + const validCertificate: ICertificate | undefined = isValid + ? { + pemCaCertificate: caCertificateData, + pemCertificate: existingCert, + pemKey: existingKey, + subjectAltNames: altNamesExtension?.altNames.map((entry) => + isIPAddress(entry) ? entry.ip : entry.value + ) + } + : undefined; + + return { + isValid, + validationMessages: messages, + certificate: validCertificate + }; + } + private _parseMacOsMatchingCertificateHash(findCertificateOuput: string): string | undefined { let shaHash: string | undefined = undefined; for (const line of findCertificateOuput.split(EOL)) { @@ -785,6 +873,7 @@ function applyDefaultOptions( ): Required { const subjectNames: ReadonlyArray | undefined = options?.subjectAltNames; const subjectIpAddresses: ReadonlyArray | undefined = options?.subjectIPAddresses; + const skipCertificateTrust: boolean | undefined = options?.skipCertificateTrust || false; return { subjectAltNames: subjectNames?.length ? subjectNames : DEFAULT_CERTIFICATE_SUBJECT_NAMES, subjectIPAddresses: subjectIpAddresses?.length @@ -793,7 +882,8 @@ function applyDefaultOptions( validityInDays: Math.min( MAX_CERTIFICATE_VALIDITY_DAYS, options?.validityInDays ?? MAX_CERTIFICATE_VALIDITY_DAYS - ) + ), + skipCertificateTrust: skipCertificateTrust }; } diff --git a/libraries/debug-certificate-manager/src/CertificateStore.ts b/libraries/debug-certificate-manager/src/CertificateStore.ts index 999268e0dda..4f3ff73d1fa 100644 --- a/libraries/debug-certificate-manager/src/CertificateStore.ts +++ b/libraries/debug-certificate-manager/src/CertificateStore.ts @@ -1,11 +1,38 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { homedir } from 'os'; +import * as path from 'node:path'; +import { homedir } from 'node:os'; import { FileSystem } from '@rushstack/node-core-library'; +/** + * Options for configuring paths and filenames used by the `CertificateStore`. + * @public + */ +export interface ICertificateStoreOptions { + /** + * Path to the directory where the certificate store will be created. + * If not provided, it defaults to `/.rushstack`. + */ + storePath?: string; + /** + * Filename of the CA certificate file within the store directory. + * If not provided, it defaults to `rushstack-ca.pem`. + */ + caCertificateFilename?: string; + /** + * Filename of the TLS certificate file within the store directory. + * If not provided, it defaults to `rushstack-serve.pem`. + */ + certificateFilename?: string; + /** + * Filename of the TLS key file within the store directory. + * If not provided, it defaults to `rushstack-serve.key`. + */ + keyFilename?: string; +} + /** * Store to retrieve and save debug certificate data. * @public @@ -14,24 +41,89 @@ export class CertificateStore { private readonly _caCertificatePath: string; private readonly _certificatePath: string; private readonly _keyPath: string; + private readonly _storePath: string; private _caCertificateData: string | undefined; private _certificateData: string | undefined; private _keyData: string | undefined; - public constructor() { - const unresolvedUserFolder: string = homedir(); - const userProfilePath: string = path.resolve(unresolvedUserFolder); - if (!FileSystem.exists(userProfilePath)) { - throw new Error("Unable to determine the current user's home directory"); - } + public constructor(options: ICertificateStoreOptions = {}) { + const requestedStorePath: string | undefined = options.storePath; + + let storePath: string | undefined; + let debugCertificateManagerConfig: ICertificateStoreOptions | undefined = undefined; + + if (requestedStorePath) { + storePath = path.resolve(requestedStorePath); + } else { + // TLS Sync extension configuration lives in `.vscode/debug-certificate-manager.json` + let currentDir: string | undefined = process.cwd(); + while (currentDir) { + const debugCertificateManagerConfigFile: string = path.join( + currentDir, + '.vscode', + 'debug-certificate-manager.json' + ); + if (FileSystem.exists(debugCertificateManagerConfigFile)) { + const configContent: string = FileSystem.readFile(debugCertificateManagerConfigFile); + debugCertificateManagerConfig = JSON.parse(configContent) as ICertificateStoreOptions; + if (debugCertificateManagerConfig.storePath) { + storePath = debugCertificateManagerConfig.storePath; + if (storePath.startsWith('~')) { + storePath = path.join(homedir(), storePath.slice(2)); + } else { + storePath = path.resolve(currentDir, debugCertificateManagerConfig.storePath); + } + } + break; // found the config file, stop searching + } + const parentDir: string | undefined = path.dirname(currentDir); + if (parentDir === currentDir) { + break; // reached the root directory + } + currentDir = parentDir; + } - const serveDataPath: string = path.join(userProfilePath, '.rushstack'); - FileSystem.ensureFolder(serveDataPath); + if (!storePath) { + // Fallback to the user's home directory under `.rushstack` + const unresolvedUserFolder: string = homedir(); + const userProfilePath: string = path.resolve(unresolvedUserFolder); + if (!FileSystem.exists(userProfilePath)) { + throw new Error("Unable to determine the current user's home directory"); + } + storePath = path.join(userProfilePath, '.rushstack'); + } + } + FileSystem.ensureFolder(storePath); + + const caCertificatePath: string = path.join( + storePath, + options.caCertificateFilename ?? + debugCertificateManagerConfig?.caCertificateFilename ?? + 'rushstack-ca.pem' + ); + const certificatePath: string = path.join( + storePath, + options.certificateFilename ?? + debugCertificateManagerConfig?.certificateFilename ?? + 'rushstack-serve.pem' + ); + const keyPath: string = path.join( + storePath, + options.keyFilename ?? debugCertificateManagerConfig?.keyFilename ?? 'rushstack-serve.key' + ); + + this._storePath = storePath; + this._caCertificatePath = caCertificatePath; + this._certificatePath = certificatePath; + this._keyPath = keyPath; + } - this._caCertificatePath = path.join(serveDataPath, 'rushstack-ca.pem'); - this._certificatePath = path.join(serveDataPath, 'rushstack-serve.pem'); - this._keyPath = path.join(serveDataPath, 'rushstack-serve.key'); + /** + * Path to the directory where the debug certificates are stored. + */ + public get storePath(): string { + return this._storePath; } /** @@ -48,6 +140,13 @@ export class CertificateStore { return this._certificatePath; } + /** + * Path to the saved debug TLS key + */ + public get keyPath(): string { + return this._keyPath; + } + /** * Debug Certificate Authority certificate pem file contents. */ diff --git a/libraries/debug-certificate-manager/src/index.ts b/libraries/debug-certificate-manager/src/index.ts index c29f25d1bcc..02562f4baea 100644 --- a/libraries/debug-certificate-manager/src/index.ts +++ b/libraries/debug-certificate-manager/src/index.ts @@ -18,9 +18,11 @@ */ export { - ICertificate, + type ICertificate, CertificateManager, - ICertificateGenerationOptions, + type ICertificateGenerationOptions, + type ICertificateManagerOptions, + type ICertificateValidationResult, DEFAULT_CERTIFICATE_SUBJECT_NAMES } from './CertificateManager'; -export { CertificateStore } from './CertificateStore'; +export { CertificateStore, type ICertificateStoreOptions } from './CertificateStore'; diff --git a/libraries/debug-certificate-manager/src/runCommand.ts b/libraries/debug-certificate-manager/src/runCommand.ts index d9a14179897..e2febdb653a 100644 --- a/libraries/debug-certificate-manager/src/runCommand.ts +++ b/libraries/debug-certificate-manager/src/runCommand.ts @@ -1,28 +1,95 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { Executable } from '@rushstack/node-core-library'; -import * as child_process from 'child_process'; +import type * as child_process from 'node:child_process'; +import * as path from 'node:path'; +import * as os from 'node:os'; + +import type { ITerminal } from '@rushstack/terminal'; +import { Executable, FileSystem, Text } from '@rushstack/node-core-library'; export interface IRunResult { stdout: string[]; stderr: string[]; - code: number; + /** + * The exit code, or -1 if the child process was terminated by a signal + */ + exitCode: number; } -export interface ISudoOptions { - cachePassword?: boolean; - prompt?: string; - spawnOptions?: object; +export function randomTmpPath(prefix?: string, suffix?: string): string { + return path.join(os.tmpdir(), `${prefix || 'tmp-'}${Math.random().toString(36).slice(2)}${suffix || ''}`); } -export async function runSudoAsync(command: string, params: string[]): Promise { - const sudo: (args: string[], options: ISudoOptions) => child_process.ChildProcess = require('sudo'); - const result: child_process.ChildProcess = sudo([command, ...params], { - cachePassword: false, - prompt: 'Enter your password: ' - }); - return await _handleChildProcess(result); +export async function darwinRunSudoAsync( + terminal: ITerminal, + command: string, + params: string[] +): Promise { + if (process.platform !== 'darwin') { + throw new Error('This function is only supported on macOS.'); + } + + const basename: string = randomTmpPath('sudo-runner-'); + const stdoutFile: string = `${basename}.stdout`; + const stderrFile: string = `${basename}.stderr`; + const exitFile: string = `${basename}.exit`; + const scriptFile: string = `${basename}.script`; + + const commandStr: string = `${command} ${params.join(' ')}`; + terminal.writeLine(`Running command with elevated privileges: ${commandStr}`); + + // Wrap the shell command in a bash command and capture stdout, stderr, and exit code + const shellScript: string = `#!/bin/bash +set -v +echo "\\n\\nRunning command with elevated privileges: ${commandStr}"; +sudo ${commandStr} > >(tee -a ${stdoutFile}) 2> >(tee -a ${stderrFile} >&2) +echo $? > "${exitFile}" +`; + + FileSystem.writeFile(scriptFile, shellScript); + + // This AppleScript opens a new Terminal window, runs the shell script, waits for it to finish and then closes the Terminal window. + const appleScript: string = ` + tell application "Terminal" + activate + set win to do script "bash '${scriptFile}'" + repeat + delay 0.5 + if not busy of window 1 then exit repeat + end repeat + close window 1 + end tell + `; + + terminal.writeLine(`Running AppleScript: ${appleScript}`); + + const child: child_process.ChildProcess = Executable.spawn('osascript', ['-e', appleScript]); + + await Executable.waitForExitAsync(child); + + const [stdoutContent, stderrContent, exitCodeStr] = await Promise.all([ + FileSystem.readFileAsync(stdoutFile), + FileSystem.readFileAsync(stderrFile), + FileSystem.readFileAsync(exitFile) + ]); + + const stdout: string[] = Text.splitByNewLines(stdoutContent); + const stderr: string[] = Text.splitByNewLines(stderrContent); + const exitCode: number = exitCodeStr ? Number(exitCodeStr) : -1; + + await Promise.all([ + FileSystem.deleteFileAsync(stdoutFile), + FileSystem.deleteFileAsync(stderrFile), + FileSystem.deleteFileAsync(exitFile), + FileSystem.deleteFileAsync(scriptFile) + ]); + + return { + stdout, + stderr, + exitCode + }; } export async function runAsync(command: string, params: string[]): Promise { @@ -42,8 +109,9 @@ async function _handleChildProcess(childProcess: child_process.ChildProcess): Pr stdout.push(data.toString()); }); - childProcess.on('close', (code: number) => { - resolve({ code, stdout, stderr }); + childProcess.on('close', (exitCode: number | null, signal: NodeJS.Signals | null) => { + const normalizedExitCode: number = typeof exitCode === 'number' ? exitCode : signal ? -1 : 0; + resolve({ exitCode: normalizedExitCode, stdout, stderr }); }); }); } diff --git a/libraries/debug-certificate-manager/tsconfig.json b/libraries/debug-certificate-manager/tsconfig.json index 22f94ca28b5..dac21d04081 100644 --- a/libraries/debug-certificate-manager/tsconfig.json +++ b/libraries/debug-certificate-manager/tsconfig.json @@ -1,6 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - "compilerOptions": { - "types": ["heft-jest", "node"] - } + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/libraries/heft-config-file/.eslintrc.js b/libraries/heft-config-file/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/libraries/heft-config-file/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/libraries/heft-config-file/.npmignore b/libraries/heft-config-file/.npmignore index ad6bcd960e8..f7a40e10213 100644 --- a/libraries/heft-config-file/.npmignore +++ b/libraries/heft-config-file/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,17 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE +# README.md +# LICENSE -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- - -# (Add your project-specific overrides here) -!/includes/** +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/heft-config-file/CHANGELOG.json b/libraries/heft-config-file/CHANGELOG.json index a966c988d2a..c71a8c1af0a 100644 --- a/libraries/heft-config-file/CHANGELOG.json +++ b/libraries/heft-config-file/CHANGELOG.json @@ -1,6 +1,1208 @@ { "name": "@rushstack/heft-config-file", "entries": [ + { + "version": "0.20.12", + "tag": "@rushstack/heft-config-file_v0.20.12", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + } + ] + } + }, + { + "version": "0.20.11", + "tag": "@rushstack/heft-config-file_v0.20.11", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + } + ] + } + }, + { + "version": "0.20.10", + "tag": "@rushstack/heft-config-file_v0.20.10", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + } + ] + } + }, + { + "version": "0.20.9", + "tag": "@rushstack/heft-config-file_v0.20.9", + "date": "Sat, 18 Apr 2026 03:47:09 GMT", + "comments": { + "patch": [ + { + "comment": "Remove `@ungap/structured-clone` polyfill; use native `structuredClone` (Node 18+)." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + } + ] + } + }, + { + "version": "0.20.8", + "tag": "@rushstack/heft-config-file_v0.20.8", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + } + ] + } + }, + { + "version": "0.20.7", + "tag": "@rushstack/heft-config-file_v0.20.7", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + } + ] + } + }, + { + "version": "0.20.6", + "tag": "@rushstack/heft-config-file_v0.20.6", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "patch": [ + { + "comment": "Improve documentation in README." + } + ] + } + }, + { + "version": "0.20.5", + "tag": "@rushstack/heft-config-file_v0.20.5", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + } + ] + } + }, + { + "version": "0.20.4", + "tag": "@rushstack/heft-config-file_v0.20.4", + "date": "Tue, 31 Mar 2026 15:14:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + } + ] + } + }, + { + "version": "0.20.3", + "tag": "@rushstack/heft-config-file_v0.20.3", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.7.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + } + ] + } + }, + { + "version": "0.20.2", + "tag": "@rushstack/heft-config-file_v0.20.2", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + } + ] + } + }, + { + "version": "0.20.1", + "tag": "@rushstack/heft-config-file_v0.20.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.7.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + } + ] + } + }, + { + "version": "0.20.0", + "tag": "@rushstack/heft-config-file_v0.20.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + } + ] + } + }, + { + "version": "0.19.7", + "tag": "@rushstack/heft-config-file_v0.19.7", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + } + ] + } + }, + { + "version": "0.19.6", + "tag": "@rushstack/heft-config-file_v0.19.6", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + } + ] + } + }, + { + "version": "0.19.5", + "tag": "@rushstack/heft-config-file_v0.19.5", + "date": "Sat, 06 Dec 2025 01:12:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + } + ] + } + }, + { + "version": "0.19.4", + "tag": "@rushstack/heft-config-file_v0.19.4", + "date": "Fri, 21 Nov 2025 16:13:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + } + ] + } + }, + { + "version": "0.19.3", + "tag": "@rushstack/heft-config-file_v0.19.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + } + ] + } + }, + { + "version": "0.19.2", + "tag": "@rushstack/heft-config-file_v0.19.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + } + ] + } + }, + { + "version": "0.19.1", + "tag": "@rushstack/heft-config-file_v0.19.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + } + ] + } + }, + { + "version": "0.19.0", + "tag": "@rushstack/heft-config-file_v0.19.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + }, + { + "comment": "Add the ability to get the original value of the `$schema` property." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + } + ] + } + }, + { + "version": "0.18.6", + "tag": "@rushstack/heft-config-file_v0.18.6", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + } + ] + } + }, + { + "version": "0.18.5", + "tag": "@rushstack/heft-config-file_v0.18.5", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + } + ] + } + }, + { + "version": "0.18.4", + "tag": "@rushstack/heft-config-file_v0.18.4", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + } + ] + } + }, + { + "version": "0.18.3", + "tag": "@rushstack/heft-config-file_v0.18.3", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + } + ] + } + }, + { + "version": "0.18.2", + "tag": "@rushstack/heft-config-file_v0.18.2", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + } + ] + } + }, + { + "version": "0.18.1", + "tag": "@rushstack/heft-config-file_v0.18.1", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "patch": [ + { + "comment": "Fix Node 16 compatibility by using non-built-in structuredClone" + } + ] + } + }, + { + "version": "0.18.0", + "tag": "@rushstack/heft-config-file_v0.18.0", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "minor": [ + { + "comment": "Allow use of the value `null` to discard any value set for the property from a parent config file.." + } + ] + } + }, + { + "version": "0.17.0", + "tag": "@rushstack/heft-config-file_v0.17.0", + "date": "Wed, 09 Apr 2025 00:11:02 GMT", + "comments": { + "minor": [ + { + "comment": "Fix an issue with `PathResolutionMethod.resolvePathRelativeToProjectRoot` when extending files across packages." + }, + { + "comment": "Add a new `customValidationFunction` option for custom validation logic on loaded configuration files." + } + ] + } + }, + { + "version": "0.16.8", + "tag": "@rushstack/heft-config-file_v0.16.8", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + } + ] + } + }, + { + "version": "0.16.7", + "tag": "@rushstack/heft-config-file_v0.16.7", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + } + ] + } + }, + { + "version": "0.16.6", + "tag": "@rushstack/heft-config-file_v0.16.6", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "patch": [ + { + "comment": "Bump `jsonpath-plus` to `~10.3.0`." + } + ] + } + }, + { + "version": "0.16.5", + "tag": "@rushstack/heft-config-file_v0.16.5", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + } + ] + } + }, + { + "version": "0.16.4", + "tag": "@rushstack/heft-config-file_v0.16.4", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + } + ] + } + }, + { + "version": "0.16.3", + "tag": "@rushstack/heft-config-file_v0.16.3", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + } + ] + } + }, + { + "version": "0.16.2", + "tag": "@rushstack/heft-config-file_v0.16.2", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + } + ] + } + }, + { + "version": "0.16.1", + "tag": "@rushstack/heft-config-file_v0.16.1", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "patch": [ + { + "comment": "Bump `jsonpath-plus` to `~10.2.0`." + } + ] + } + }, + { + "version": "0.16.0", + "tag": "@rushstack/heft-config-file_v0.16.0", + "date": "Tue, 03 Dec 2024 16:11:07 GMT", + "comments": { + "minor": [ + { + "comment": "Add a new `NonProjectConfigurationFile` class that is designed to load absolute-pathed configuration files without rig support." + }, + { + "comment": "Rename `ConfigurationFile` to `ProjectConfigurationFile` and mark `ConfigurationFile` as `@deprecated`." + } + ] + } + }, + { + "version": "0.15.9", + "tag": "@rushstack/heft-config-file_v0.15.9", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + } + ] + } + }, + { + "version": "0.15.8", + "tag": "@rushstack/heft-config-file_v0.15.8", + "date": "Thu, 24 Oct 2024 00:15:47 GMT", + "comments": { + "patch": [ + { + "comment": "Update the `jsonpath-plus` dependency to mitigate CVE-2024-21534.\"" + } + ] + } + }, + { + "version": "0.15.7", + "tag": "@rushstack/heft-config-file_v0.15.7", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + } + ] + } + }, + { + "version": "0.15.6", + "tag": "@rushstack/heft-config-file_v0.15.6", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + } + ] + } + }, + { + "version": "0.15.5", + "tag": "@rushstack/heft-config-file_v0.15.5", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + } + ] + } + }, + { + "version": "0.15.4", + "tag": "@rushstack/heft-config-file_v0.15.4", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + } + ] + } + }, + { + "version": "0.15.3", + "tag": "@rushstack/heft-config-file_v0.15.3", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.5.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + } + ] + } + }, + { + "version": "0.15.2", + "tag": "@rushstack/heft-config-file_v0.15.2", + "date": "Wed, 17 Jul 2024 06:55:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + } + ] + } + }, + { + "version": "0.15.1", + "tag": "@rushstack/heft-config-file_v0.15.1", + "date": "Tue, 16 Jul 2024 00:36:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + } + ] + } + }, + { + "version": "0.15.0", + "tag": "@rushstack/heft-config-file_v0.15.0", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "minor": [ + { + "comment": "Add `ConfigurationFile.loadConfigurationFileForProject` and `ConfigurationFile.tryLoadConfigurationFileForProject` APIs to allow for synchronously loading Heft configuration files" + } + ] + } + }, + { + "version": "0.14.25", + "tag": "@rushstack/heft-config-file_v0.14.25", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "patch": [ + { + "comment": "Include missing `type` modifiers on type-only exports." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + } + ] + } + }, + { + "version": "0.14.24", + "tag": "@rushstack/heft-config-file_v0.14.24", + "date": "Wed, 29 May 2024 02:03:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + } + ] + } + }, + { + "version": "0.14.23", + "tag": "@rushstack/heft-config-file_v0.14.23", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + } + ] + } + }, + { + "version": "0.14.22", + "tag": "@rushstack/heft-config-file_v0.14.22", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + } + ] + } + }, + { + "version": "0.14.21", + "tag": "@rushstack/heft-config-file_v0.14.21", + "date": "Sat, 25 May 2024 04:54:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + } + ] + } + }, + { + "version": "0.14.20", + "tag": "@rushstack/heft-config-file_v0.14.20", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + } + ] + } + }, + { + "version": "0.14.19", + "tag": "@rushstack/heft-config-file_v0.14.19", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + } + ] + } + }, + { + "version": "0.14.18", + "tag": "@rushstack/heft-config-file_v0.14.18", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + } + ] + } + }, + { + "version": "0.14.17", + "tag": "@rushstack/heft-config-file_v0.14.17", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + } + ] + } + }, + { + "version": "0.14.16", + "tag": "@rushstack/heft-config-file_v0.14.16", + "date": "Mon, 06 May 2024 15:11:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + } + ] + } + }, + { + "version": "0.14.15", + "tag": "@rushstack/heft-config-file_v0.14.15", + "date": "Wed, 10 Apr 2024 15:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + } + ] + } + }, + { + "version": "0.14.14", + "tag": "@rushstack/heft-config-file_v0.14.14", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + } + ] + } + }, + { + "version": "0.14.13", + "tag": "@rushstack/heft-config-file_v0.14.13", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + } + ] + } + }, + { + "version": "0.14.12", + "tag": "@rushstack/heft-config-file_v0.14.12", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.1`" + } + ] + } + }, + { + "version": "0.14.11", + "tag": "@rushstack/heft-config-file_v0.14.11", + "date": "Mon, 19 Feb 2024 21:54:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.0`" + } + ] + } + }, + { + "version": "0.14.10", + "tag": "@rushstack/heft-config-file_v0.14.10", + "date": "Sat, 17 Feb 2024 06:24:34 GMT", + "comments": { + "patch": [ + { + "comment": "Fix broken link to API documentation" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.5.2`" + } + ] + } + }, + { + "version": "0.14.9", + "tag": "@rushstack/heft-config-file_v0.14.9", + "date": "Thu, 08 Feb 2024 01:09:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + } + ] + } + }, + { + "version": "0.14.8", + "tag": "@rushstack/heft-config-file_v0.14.8", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + } + ] + } + }, + { + "version": "0.14.7", + "tag": "@rushstack/heft-config-file_v0.14.7", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + } + ] + } + }, + { + "version": "0.14.6", + "tag": "@rushstack/heft-config-file_v0.14.6", + "date": "Tue, 23 Jan 2024 20:12:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + } + ] + } + }, + { + "version": "0.14.5", + "tag": "@rushstack/heft-config-file_v0.14.5", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + } + ] + } + }, + { + "version": "0.14.4", + "tag": "@rushstack/heft-config-file_v0.14.4", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + } + ] + } + }, + { + "version": "0.14.3", + "tag": "@rushstack/heft-config-file_v0.14.3", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + } + ] + } + }, + { + "version": "0.14.2", + "tag": "@rushstack/heft-config-file_v0.14.2", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + } + ] + } + }, + { + "version": "0.14.1", + "tag": "@rushstack/heft-config-file_v0.14.1", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.5.1`" + } + ] + } + }, + { + "version": "0.14.0", + "tag": "@rushstack/heft-config-file_v0.14.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + } + ] + } + }, + { + "version": "0.13.3", + "tag": "@rushstack/heft-config-file_v0.13.3", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + } + ] + } + }, + { + "version": "0.13.2", + "tag": "@rushstack/heft-config-file_v0.13.2", + "date": "Wed, 19 Jul 2023 00:20:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + } + ] + } + }, + { + "version": "0.13.1", + "tag": "@rushstack/heft-config-file_v0.13.1", + "date": "Thu, 06 Jul 2023 00:16:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + } + ] + } + }, + { + "version": "0.13.0", + "tag": "@rushstack/heft-config-file_v0.13.0", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "minor": [ + { + "comment": " Use the `IRigConfig` interface insteacd of the `RigConfig` class in the API." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.4.0`" + } + ] + } + }, + { + "version": "0.12.5", + "tag": "@rushstack/heft-config-file_v0.12.5", + "date": "Thu, 15 Jun 2023 00:21:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.3.21`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + } + ] + } + }, + { + "version": "0.12.4", + "tag": "@rushstack/heft-config-file_v0.12.4", + "date": "Wed, 07 Jun 2023 22:45:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/rig-package\" to `0.3.20`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + } + ] + } + }, { "version": "0.12.3", "tag": "@rushstack/heft-config-file_v0.12.3", diff --git a/libraries/heft-config-file/CHANGELOG.md b/libraries/heft-config-file/CHANGELOG.md index e1ccf6ea4ae..51856178329 100644 --- a/libraries/heft-config-file/CHANGELOG.md +++ b/libraries/heft-config-file/CHANGELOG.md @@ -1,6 +1,447 @@ # Change Log - @rushstack/heft-config-file -This log was last generated on Mon, 29 May 2023 15:21:15 GMT and should not be manually modified. +This log was last generated on Fri, 17 Jul 2026 00:15:59 GMT and should not be manually modified. + +## 0.20.12 +Fri, 17 Jul 2026 00:15:59 GMT + +_Version update only_ + +## 0.20.11 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.20.10 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.20.9 +Sat, 18 Apr 2026 03:47:09 GMT + +### Patches + +- Remove `@ungap/structured-clone` polyfill; use native `structuredClone` (Node 18+). + +## 0.20.8 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 0.20.7 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.20.6 +Fri, 10 Apr 2026 22:46:34 GMT + +### Patches + +- Improve documentation in README. + +## 0.20.5 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.20.4 +Tue, 31 Mar 2026 15:14:14 GMT + +_Version update only_ + +## 0.20.3 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 0.20.2 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.20.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.20.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.19.7 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 0.19.6 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 0.19.5 +Sat, 06 Dec 2025 01:12:29 GMT + +_Version update only_ + +## 0.19.4 +Fri, 21 Nov 2025 16:13:55 GMT + +_Version update only_ + +## 0.19.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 0.19.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 0.19.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 0.19.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. +- Add the ability to get the original value of the `$schema` property. + +## 0.18.6 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 0.18.5 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.18.4 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.18.3 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.18.2 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.18.1 +Fri, 25 Apr 2025 00:11:32 GMT + +### Patches + +- Fix Node 16 compatibility by using non-built-in structuredClone + +## 0.18.0 +Thu, 17 Apr 2025 00:11:21 GMT + +### Minor changes + +- Allow use of the value `null` to discard any value set for the property from a parent config file.. + +## 0.17.0 +Wed, 09 Apr 2025 00:11:02 GMT + +### Minor changes + +- Fix an issue with `PathResolutionMethod.resolvePathRelativeToProjectRoot` when extending files across packages. +- Add a new `customValidationFunction` option for custom validation logic on loaded configuration files. + +## 0.16.8 +Tue, 25 Mar 2025 15:11:15 GMT + +_Version update only_ + +## 0.16.7 +Tue, 11 Mar 2025 02:12:33 GMT + +_Version update only_ + +## 0.16.6 +Wed, 19 Feb 2025 18:53:48 GMT + +### Patches + +- Bump `jsonpath-plus` to `~10.3.0`. + +## 0.16.5 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.16.4 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.16.3 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.16.2 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.16.1 +Mon, 09 Dec 2024 20:31:43 GMT + +### Patches + +- Bump `jsonpath-plus` to `~10.2.0`. + +## 0.16.0 +Tue, 03 Dec 2024 16:11:07 GMT + +### Minor changes + +- Add a new `NonProjectConfigurationFile` class that is designed to load absolute-pathed configuration files without rig support. +- Rename `ConfigurationFile` to `ProjectConfigurationFile` and mark `ConfigurationFile` as `@deprecated`. + +## 0.15.9 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.15.8 +Thu, 24 Oct 2024 00:15:47 GMT + +### Patches + +- Update the `jsonpath-plus` dependency to mitigate CVE-2024-21534." + +## 0.15.7 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.15.6 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.15.5 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.15.4 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.15.3 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.15.2 +Wed, 17 Jul 2024 06:55:10 GMT + +_Version update only_ + +## 0.15.1 +Tue, 16 Jul 2024 00:36:22 GMT + +_Version update only_ + +## 0.15.0 +Thu, 27 Jun 2024 21:01:36 GMT + +### Minor changes + +- Add `ConfigurationFile.loadConfigurationFileForProject` and `ConfigurationFile.tryLoadConfigurationFileForProject` APIs to allow for synchronously loading Heft configuration files + +## 0.14.25 +Thu, 30 May 2024 00:13:05 GMT + +### Patches + +- Include missing `type` modifiers on type-only exports. + +## 0.14.24 +Wed, 29 May 2024 02:03:51 GMT + +_Version update only_ + +## 0.14.23 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.14.22 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.14.21 +Sat, 25 May 2024 04:54:08 GMT + +_Version update only_ + +## 0.14.20 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 0.14.19 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.14.18 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.14.17 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.14.16 +Mon, 06 May 2024 15:11:05 GMT + +_Version update only_ + +## 0.14.15 +Wed, 10 Apr 2024 15:10:08 GMT + +_Version update only_ + +## 0.14.14 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.14.13 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.14.12 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.14.11 +Mon, 19 Feb 2024 21:54:26 GMT + +_Version update only_ + +## 0.14.10 +Sat, 17 Feb 2024 06:24:34 GMT + +### Patches + +- Fix broken link to API documentation + +## 0.14.9 +Thu, 08 Feb 2024 01:09:22 GMT + +_Version update only_ + +## 0.14.8 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.14.7 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.14.6 +Tue, 23 Jan 2024 20:12:58 GMT + +_Version update only_ + +## 0.14.5 +Tue, 23 Jan 2024 16:15:05 GMT + +_Version update only_ + +## 0.14.4 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.14.3 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.14.2 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.14.1 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.14.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 0.13.3 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 0.13.2 +Wed, 19 Jul 2023 00:20:32 GMT + +_Version update only_ + +## 0.13.1 +Thu, 06 Jul 2023 00:16:20 GMT + +_Version update only_ + +## 0.13.0 +Mon, 19 Jun 2023 22:40:21 GMT + +### Minor changes + +- Use the `IRigConfig` interface insteacd of the `RigConfig` class in the API. + +## 0.12.5 +Thu, 15 Jun 2023 00:21:01 GMT + +_Version update only_ + +## 0.12.4 +Wed, 07 Jun 2023 22:45:16 GMT + +_Version update only_ ## 0.12.3 Mon, 29 May 2023 15:21:15 GMT diff --git a/libraries/heft-config-file/README.md b/libraries/heft-config-file/README.md index 7019b68ca75..c51a808d73a 100644 --- a/libraries/heft-config-file/README.md +++ b/libraries/heft-config-file/README.md @@ -1,12 +1,345 @@ # @rushstack/heft-config-file -A library for loading config files for use with the [Heft](https://rushstack.io/pages/heft/overview/) build system. +A library for loading JSON configuration files in the [Heft](https://rushstack.io/pages/heft/overview/) build +system. It supports `extends`-based inheritance between config files, configurable property merge strategies, +automatic path resolution, and JSON schema validation. ## Links - [CHANGELOG.md]( https://github.com/microsoft/rushstack/blob/main/libraries/heft-config-file/CHANGELOG.md) - Find out what's new in the latest version -- [API Reference](https://rushstack.io/pages/api/heft-config-file/) +- [API Reference](https://api.rushstack.io/pages/heft-config-file/) Heft is part of the [Rush Stack](https://rushstack.io/) family of projects. + +--- + +## Overview + +`@rushstack/heft-config-file` provides a structured way to load JSON config files that: + +- **Extend** a parent config file via an `"extends"` field (including across packages) +- **Merge** parent and child properties with configurable inheritance strategies (append, merge, replace, or custom) +- **Resolve paths** in property values relative to the config file, project root, or via Node.js module resolution +- **Validate** the merged result against a JSON schema +- **Support rigs** by falling back to a [rig package](https://rushstack.io/pages/heft/rig_packages/) profile if the project doesn't have its own config file + +--- + +## For config file authors (Heft users) + +If you're writing or customizing a config file that uses this system (e.g. `heft.json`, `typescript.json`, or +a plugin's config file), here's what you need to know. + +### The `extends` field + +Config files can inherit from a parent file using `"extends"`: + +```json +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "@my-company/build-config/config/heft.json" +} +``` + +The `"extends"` value is resolved using Node.js module resolution, so it can be: + +- A relative path: `"extends": "../shared/base.json"` +- A package reference: `"extends": "@my-company/rig/profiles/default/config/heft.json"` + +Circular `extends` chains are detected and will throw an error. + +### How property inheritance works + +When a child config extends a parent, each top-level property is merged according to its **inheritance type**. +The inheritance type is configured by the package that defines the config file schema (not by the user). + +The built-in inheritance types are: + +| Type | Applies to | Behavior | +|------|-----------|---------| +| `replace` | any | Child value completely replaces the parent value (default for objects) | +| `append` | arrays only | Child array elements are appended after parent array elements (default for arrays) | +| `merge` | objects only | Shallow merge: child properties override parent properties, parent-only properties are kept | +| `custom` | any | A custom merge function defined by the loader | + +**Setting a property to `null`** always removes the parent's value, regardless of inheritance type. + +### Per-property inline override: `$propertyName.inheritanceType` + +If the schema allows it, you can override the inheritance type for an individual property directly in your +config file using the `"$.inheritanceType"` annotation: + +```json +{ + "extends": "./base.json", + + "$plugins.inheritanceType": "append", + "plugins": [ + { "pluginName": "my-plugin" } + ], + + "$settings.inheritanceType": "merge", + "settings": { + "strict": true + } +} +``` + +These annotations work at any nesting level - you can annotate a nested property the same way: + +```json +{ + "extends": "./base.json", + + "$d.inheritanceType": "merge", + "d": { + "$g.inheritanceType": "append", + "g": [{ "h": "B" }], + + "$i.inheritanceType": "replace", + "i": [{ "j": "B" }] + } +} +``` + +The inline annotation takes precedence over any default set by the loader. + +**Note:** `$propertyName.inheritanceType` is a loader-level annotation and is stripped from the final config +object; it will not appear in the merged result or be validated by the schema. + +### Path resolution + +Properties that represent file system paths may be automatically resolved by the loader. The resolution +method is determined by the loader's configuration, not the config file author. The original (unresolved) +value is preserved and can be retrieved via the API. + +--- + +## For API consumers (plugin/loader authors) + +If you're writing a Heft plugin that needs to load a config file, use `ProjectConfigurationFile` or +`NonProjectConfigurationFile`. + +### `ProjectConfigurationFile` + +Use this for config files stored at a known path relative to the project root, with optional rig support. + +```typescript +import { ProjectConfigurationFile, InheritanceType, PathResolutionMethod } from '@rushstack/heft-config-file'; + +interface IMyPluginConfig { + outputFolder: string; + plugins: string[]; + settings?: { + strict: boolean; + }; + extends?: string; +} + +const loader = new ProjectConfigurationFile({ + // Path relative to the project root + projectRelativeFilePath: 'config/my-plugin.json', + + // Provide either jsonSchemaPath or jsonSchemaObject + jsonSchemaPath: require.resolve('./schemas/my-plugin.schema.json'), + + // Configure how properties merge when a config file uses "extends" + propertyInheritance: { + plugins: { inheritanceType: InheritanceType.append }, + settings: { inheritanceType: InheritanceType.merge } + // Properties not listed here use the default for their type + }, + + // Optionally override the default inheritance for all arrays or all objects + propertyInheritanceDefaults: { + array: { inheritanceType: InheritanceType.append }, // built-in default + object: { inheritanceType: InheritanceType.replace } // built-in default + }, + + // Automatically resolve path properties to absolute paths + jsonPathMetadata: { + '$.outputFolder': { + pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToConfigurationFile + } + } +}); + +// Load config for a project (throws if not found) +const config = loader.loadConfigurationFileForProject(terminal, projectPath); + +// Load config with rig fallback +const config = loader.loadConfigurationFileForProject(terminal, projectPath, rigConfig); + +// Returns undefined instead of throwing if the file doesn't exist +const config = loader.tryLoadConfigurationFileForProject(terminal, projectPath, rigConfig); + +// Async variants are also available: +// loader.loadConfigurationFileForProjectAsync(...) +// loader.tryLoadConfigurationFileForProjectAsync(...) +``` + +When a `rigConfig` is provided and the project does not have its own config file, the loader falls back to +the same relative path inside the rig's profile folder. + +### `NonProjectConfigurationFile` + +Use this for config files at arbitrary absolute paths (not bound to a project root): + +```typescript +import { NonProjectConfigurationFile } from '@rushstack/heft-config-file'; + +const loader = new NonProjectConfigurationFile({ + jsonSchemaPath: '/path/to/schema.json' +}); + +const config = loader.loadConfigurationFile(terminal, '/absolute/path/to/config.json'); +// Also: tryLoadConfigurationFile, loadConfigurationFileAsync, tryLoadConfigurationFileAsync +``` + +### JSON schema + +Supply either a file path or an inline object: + +```typescript +// From a file path +{ jsonSchemaPath: require.resolve('./schemas/my-plugin.schema.json') } + +// Inline +{ jsonSchemaObject: { type: 'object', properties: { ... } } } +``` + +Schema validation runs **after** all inheritance merging, so the schema describes the shape of the final +merged result. + +### Custom validation + +For validation logic that JSON schema cannot express, supply a `customValidationFunction`: + +```typescript +const loader = new ProjectConfigurationFile({ + projectRelativeFilePath: 'config/my-plugin.json', + jsonSchemaPath: require.resolve('./schemas/my-plugin.schema.json'), + + customValidationFunction: (configFile, configFilePath, terminal) => { + if (configFile.outputFolder === configFile.inputFolder) { + terminal.writeErrorLine('outputFolder and inputFolder must be different'); + return false; + } + return true; + } +}); +``` + +The function is called after schema validation. If it returns anything other than `true`, an error is thrown. +The function may also throw its own error to provide a custom message. + +### Path resolution + +Use `jsonPathMetadata` to automatically resolve string properties that represent file system paths. Keys are +[JSONPath](https://jsonpath.com/) expressions, so wildcards work for arrays and nested objects: + +```typescript +jsonPathMetadata: { + // Resolve a specific property + '$.outputFolder': { + pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToConfigurationFile + }, + + // Resolve all "path" properties inside an array of objects + '$.plugins.*.path': { + pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToConfigurationFile + }, + + // Node.js module resolution (like require.resolve) + '$.loaderPackage': { + pathResolutionMethod: PathResolutionMethod.nodeResolve + }, + + // Custom resolver + '$.specialPath': { + pathResolutionMethod: PathResolutionMethod.custom, + customResolver: ({ propertyValue, configurationFilePath }) => { + return myCustomResolve(propertyValue, configurationFilePath); + } + } +} +``` + +Available resolution methods: + +| Method | Behavior | +|--------|---------| +| `resolvePathRelativeToConfigurationFile` | `path.resolve(configFileDir, value)` | +| `resolvePathRelativeToProjectRoot` | `path.resolve(projectRoot, value)` | +| `nodeResolve` | Node.js `require.resolve`-style resolution | +| `custom` | Call your own resolver function | + +### Inspecting source file and original values + +After loading, you can query where objects came from and what their pre-resolution values were: + +```typescript +const config = loader.loadConfigurationFileForProject(terminal, projectPath); + +// Which config file did this object come from? +const sourceFile = loader.getObjectSourceFilePath(config); +// e.g. "/my-project/config/my-plugin.json" + +// What was the raw value of a property before path resolution? +const originalValue = loader.getPropertyOriginalValue({ + parentObject: config, + propertyName: 'outputFolder' +}); +// e.g. "./dist" (before being resolved to an absolute path) +``` + +These methods work on any object that was loaded as part of the config file (including nested objects). + +### Custom inheritance functions + +For cases where the built-in merge strategies aren't enough: + +```typescript +import { InheritanceType } from '@rushstack/heft-config-file'; + +const loader = new ProjectConfigurationFile({ + projectRelativeFilePath: 'config/my-plugin.json', + jsonSchemaPath: require.resolve('./schemas/my-plugin.schema.json'), + propertyInheritance: { + myProperty: { + inheritanceType: InheritanceType.custom, + inheritanceFunction: (childValue, parentValue) => { + // Merge logic here; return the combined result + return { ...parentValue, ...childValue, extra: 'added' }; + } + } + } +}); +``` + +The function receives `(childValue, parentValue)` and must return the merged result. It is not called if the +child sets the property to `null` - in that case the property is simply deleted. + +### Inheritance precedence + +When a child config file is merged with its parent, the inheritance type for each property is resolved in +this order (highest precedence first): + +1. **Inline annotation** in the config file: `"$myProp.inheritanceType": "append"` +2. **`propertyInheritance`** option passed to the loader constructor +3. **`propertyInheritanceDefaults`** option (per type: `array` or `object`) +4. **Built-in defaults**: `append` for arrays, `replace` for objects + +### Testing + +`TestUtilities.stripAnnotations` removes the internal tracking metadata from a loaded config object, +which is useful when writing snapshot tests: + +```typescript +import { TestUtilities } from '@rushstack/heft-config-file'; + +const config = loader.loadConfigurationFileForProject(terminal, projectPath); +expect(TestUtilities.stripAnnotations(config)).toMatchSnapshot(); +``` diff --git a/libraries/heft-config-file/config/api-extractor.json b/libraries/heft-config-file/config/api-extractor.json index 34fb7776c9d..005e818a08b 100644 --- a/libraries/heft-config-file/config/api-extractor.json +++ b/libraries/heft-config-file/config/api-extractor.json @@ -1,15 +1,7 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/api-extractor-base.json", - "mainEntryPointFilePath": "/lib/index.d.ts", - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, - "docModel": { - "enabled": true, - "apiJsonFilePath": "../../../common/temp/api/.api.json" - }, "dtsRollup": { "enabled": true, "betaTrimmedFilePath": "/dist/.d.ts" diff --git a/libraries/heft-config-file/config/jest.config.json b/libraries/heft-config-file/config/jest.config.json index 4bb17bde3ee..7c0f9ccc9d6 100644 --- a/libraries/heft-config-file/config/jest.config.json +++ b/libraries/heft-config-file/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "decoupled-local-node-rig/profiles/default/config/jest.config.json" } diff --git a/libraries/heft-config-file/config/rig.json b/libraries/heft-config-file/config/rig.json index 6ac88a96368..cc98dea43dd 100644 --- a/libraries/heft-config-file/config/rig.json +++ b/libraries/heft-config-file/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "decoupled-local-node-rig" } diff --git a/libraries/heft-config-file/eslint.config.js b/libraries/heft-config-file/eslint.config.js new file mode 100644 index 00000000000..e54effd122a --- /dev/null +++ b/libraries/heft-config-file/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/heft-config-file/package.json b/libraries/heft-config-file/package.json index 673a3bb42af..e0b23bdc6d8 100644 --- a/libraries/heft-config-file/package.json +++ b/libraries/heft-config-file/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/heft-config-file", - "version": "0.12.3", + "version": "0.20.12", "description": "Configuration file loader for @rushstack/heft", "repository": { "type": "git", @@ -11,25 +11,48 @@ "node": ">=10.13.0" }, "homepage": "https://rushstack.io/pages/heft/overview/", - "main": "lib/index.js", - "types": "dist/heft-config-file.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/heft-config-file.d.ts", + "exports": { + ".": { + "types": "./dist/heft-config-file.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "license": "MIT", "scripts": { "build": "heft build --clean", "start": "heft test --clean --watch", - "_phase:build": "heft build --clean", - "_phase:test": "heft test --no-build" + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" }, "dependencies": { "@rushstack/node-core-library": "workspace:*", "@rushstack/rig-package": "workspace:*", - "jsonpath-plus": "~4.0.0" + "@rushstack/terminal": "workspace:*", + "jsonpath-plus": "~10.3.0" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft": "0.50.6", - "@rushstack/heft-node-rig": "1.13.0", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36" - } + "@rushstack/heft": "1.2.22", + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0" + }, + "sideEffects": false } diff --git a/libraries/heft-config-file/src/ConfigurationFile.ts b/libraries/heft-config-file/src/ConfigurationFile.ts deleted file mode 100644 index 0268c90f5d1..00000000000 --- a/libraries/heft-config-file/src/ConfigurationFile.ts +++ /dev/null @@ -1,927 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import * as nodeJsPath from 'path'; -import { JSONPath } from 'jsonpath-plus'; -import { - JsonSchema, - JsonFile, - PackageJsonLookup, - Import, - FileSystem, - ITerminal -} from '@rushstack/node-core-library'; -import { RigConfig } from '@rushstack/rig-package'; - -interface IConfigurationJson { - extends?: string; -} - -/** - * @beta - */ -export enum InheritanceType { - /** - * Append additional elements after elements from the parent file's property. Only applicable - * for arrays. - */ - append = 'append', - - /** - * Perform a shallow merge of additional elements after elements from the parent file's property. - * Only applicable for objects. - */ - merge = 'merge', - - /** - * Discard elements from the parent file's property - */ - replace = 'replace', - - /** - * Custom inheritance functionality - */ - custom = 'custom' -} - -/** - * @beta - */ -export enum PathResolutionMethod { - /** - * Resolve a path relative to the configuration file - */ - resolvePathRelativeToConfigurationFile = 'resolvePathRelativeToConfigurationFile', - - /** - * Resolve a path relative to the root of the project containing the configuration file - */ - resolvePathRelativeToProjectRoot = 'resolvePathRelativeToProjectRoot', - - /** - * Treat the property as a NodeJS-style require/import reference and resolve using standard - * NodeJS filesystem resolution - * - * @deprecated - * Use {@link PathResolutionMethod.nodeResolve} instead - */ - NodeResolve = 'NodeResolve', - - /** - * Treat the property as a NodeJS-style require/import reference and resolve using standard - * NodeJS filesystem resolution - */ - nodeResolve = 'nodeResolve', - - /** - * Resolve the property using a custom resolver. - */ - custom = 'custom' -} - -const CONFIGURATION_FILE_MERGE_BEHAVIOR_FIELD_REGEX: RegExp = /^\$([^\.]+)\.inheritanceType$/; -const CONFIGURATION_FILE_FIELD_ANNOTATION: unique symbol = Symbol('configuration-file-field-annotation'); - -interface IAnnotatedField { - [CONFIGURATION_FILE_FIELD_ANNOTATION]: IConfigurationFileFieldAnnotation; -} - -interface IConfigurationFileFieldAnnotation { - configurationFilePath: string | undefined; - originalValues: { [propertyName in keyof TField]: unknown }; -} - -/** - * Options provided to the custom resolver specified in {@link ICustomJsonPathMetadata}. - * - * @beta - */ -export interface IJsonPathMetadataResolverOptions { - /** - * The name of the property being resolved. - */ - propertyName: string; - /** - * The value of the path property being resolved. - */ - propertyValue: string; - /** - * The path to the configuration file the property was obtained from. - */ - configurationFilePath: string; - /** - * The configuration file the property was obtained from. - */ - configurationFile: Partial; -} - -/** - * Used to specify how node(s) in a JSON object should be processed after being loaded. - * - * @beta - */ -export interface ICustomJsonPathMetadata { - /** - * If `ICustomJsonPathMetadata.pathResolutionMethod` is set to `PathResolutionMethod.custom`, - * this property be used to resolve the path. - */ - customResolver?: (resolverOptions: IJsonPathMetadataResolverOptions) => string; - - /** - * If this property describes a filesystem path, use this property to describe - * how the path should be resolved. - */ - pathResolutionMethod?: PathResolutionMethod.custom; -} - -/** - * Used to specify how node(s) in a JSON object should be processed after being loaded. - * - * @beta - */ -export interface INonCustomJsonPathMetadata { - /** - * If this property describes a filesystem path, use this property to describe - * how the path should be resolved. - */ - pathResolutionMethod?: - | PathResolutionMethod.NodeResolve // TODO: Remove - | PathResolutionMethod.nodeResolve - | PathResolutionMethod.resolvePathRelativeToConfigurationFile - | PathResolutionMethod.resolvePathRelativeToProjectRoot; -} - -/** - * @beta - */ -export type PropertyInheritanceCustomFunction = ( - currentObject: TObject, - parentObject: TObject -) => TObject; - -/** - * @beta - */ -export interface IPropertyInheritance { - inheritanceType: TInheritanceType; -} - -/** - * @beta - */ -export interface ICustomPropertyInheritance extends IPropertyInheritance { - /** - * Provides a custom inheritance function. This function takes two arguments: the first is the - * child file's object, and the second is the parent file's object. The function should return - * the resulting combined object. - */ - inheritanceFunction: PropertyInheritanceCustomFunction; -} - -/** - * @beta - */ -export type IPropertiesInheritance = { - [propertyName in keyof TConfigurationFile]?: - | IPropertyInheritance - | ICustomPropertyInheritance; -}; - -/** - * @beta - */ -export interface IPropertyInheritanceDefaults { - array?: IPropertyInheritance; - object?: IPropertyInheritance; -} - -/** - * @beta - */ -export type IJsonPathMetadata = ICustomJsonPathMetadata | INonCustomJsonPathMetadata; - -/** - * Keys in this object are JSONPaths {@link https://jsonpath.com/}, and values are objects - * that describe how node(s) selected by the JSONPath are processed after loading. - * - * @beta - */ -export interface IJsonPathsMetadata { - [jsonPath: string]: IJsonPathMetadata; -} - -/** - * @beta - */ -export interface IConfigurationFileOptionsBase { - /** - * A project root-relative path to the configuration file that should be loaded. - */ - projectRelativeFilePath: string; - - /** - * Use this property to specify how JSON nodes are postprocessed. - */ - jsonPathMetadata?: IJsonPathsMetadata; - - /** - * Use this property to control how root-level properties are handled between parent and child - * configuration files. - */ - propertyInheritance?: IPropertiesInheritance; - - /** - * Use this property to control how specific property types are handled between parent and child - * configuration files. - */ - propertyInheritanceDefaults?: IPropertyInheritanceDefaults; -} - -/** - * @beta - */ -export interface IConfigurationFileOptionsWithJsonSchemaFilePath - extends IConfigurationFileOptionsBase { - /** - * The path to the schema for the configuration file. - */ - jsonSchemaPath: string; - jsonSchemaObject?: never; -} - -/** - * @beta - */ -export interface IConfigurationFileOptionsWithJsonSchemaObject - extends IConfigurationFileOptionsBase { - /** - * The schema for the configuration file. - */ - jsonSchemaObject: object; - jsonSchemaPath?: never; -} - -/** - * @beta - */ -export type IConfigurationFileOptions = - | IConfigurationFileOptionsWithJsonSchemaFilePath - | IConfigurationFileOptionsWithJsonSchemaObject; - -interface IJsonPathCallbackObject { - path: string; - parent: object; - parentProperty: string; - value: string; -} - -/** - * @beta - */ -export interface IOriginalValueOptions { - parentObject: Partial; - propertyName: keyof TParentProperty; -} - -/** - * @beta - */ -export class ConfigurationFile { - private readonly _getSchema: () => JsonSchema; - - /** {@inheritDoc IConfigurationFileOptionsBase.projectRelativeFilePath} */ - public readonly projectRelativeFilePath: string; - - private readonly _jsonPathMetadata: IJsonPathsMetadata; - private readonly _propertyInheritanceTypes: IPropertiesInheritance; - private readonly _defaultPropertyInheritance: IPropertyInheritanceDefaults; - private __schema: JsonSchema | undefined; - private get _schema(): JsonSchema { - if (!this.__schema) { - this.__schema = this._getSchema(); - } - - return this.__schema; - } - - private readonly _configPromiseCache: Map> = new Map(); - private readonly _packageJsonLookup: PackageJsonLookup = new PackageJsonLookup(); - - public constructor(options: IConfigurationFileOptions) { - this.projectRelativeFilePath = options.projectRelativeFilePath; - - if (options.jsonSchemaObject) { - this._getSchema = () => JsonSchema.fromLoadedObject(options.jsonSchemaObject); - } else { - this._getSchema = () => JsonSchema.fromFile(options.jsonSchemaPath); - } - - this._jsonPathMetadata = options.jsonPathMetadata || {}; - this._propertyInheritanceTypes = options.propertyInheritance || {}; - this._defaultPropertyInheritance = options.propertyInheritanceDefaults || {}; - } - - /** - * Find and return a configuration file for the specified project, automatically resolving - * `extends` properties and handling rigged configuration files. Will throw an error if a configuration - * file cannot be found in the rig or project config folder. - */ - public async loadConfigurationFileForProjectAsync( - terminal: ITerminal, - projectPath: string, - rigConfig?: RigConfig - ): Promise { - const projectConfigurationFilePath: string = this._getConfigurationFilePathForProject(projectPath); - return await this._loadConfigurationFileInnerWithCacheAsync( - terminal, - projectConfigurationFilePath, - new Set(), - rigConfig - ); - } - - /** - * This function is identical to {@link ConfigurationFile.loadConfigurationFileForProjectAsync}, except - * that it returns `undefined` instead of throwing an error if the configuration file cannot be found. - */ - public async tryLoadConfigurationFileForProjectAsync( - terminal: ITerminal, - projectPath: string, - rigConfig?: RigConfig - ): Promise { - try { - return await this.loadConfigurationFileForProjectAsync(terminal, projectPath, rigConfig); - } catch (e) { - if (FileSystem.isNotExistError(e as Error)) { - return undefined; - } - throw e; - } - } - - /** - * @internal - */ - public static _formatPathForLogging: (path: string) => string = (path: string) => path; - - /** - * Get the path to the source file that the referenced property was originally - * loaded from. - */ - public getObjectSourceFilePath(obj: TObject): string | undefined { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const annotation: IConfigurationFileFieldAnnotation | undefined = (obj as any)[ - CONFIGURATION_FILE_FIELD_ANNOTATION - ]; - if (annotation) { - return annotation.configurationFilePath; - } - - return undefined; - } - - /** - * Get the value of the specified property on the specified object that was originally - * loaded from a configuration file. - */ - public getPropertyOriginalValue( - options: IOriginalValueOptions - ): TValue | undefined { - const annotation: IConfigurationFileFieldAnnotation | undefined = - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (options.parentObject as any)[CONFIGURATION_FILE_FIELD_ANNOTATION]; - if (annotation && annotation.originalValues.hasOwnProperty(options.propertyName)) { - return annotation.originalValues[options.propertyName] as TValue; - } else { - return undefined; - } - } - - private async _loadConfigurationFileInnerWithCacheAsync( - terminal: ITerminal, - resolvedConfigurationFilePath: string, - visitedConfigurationFilePaths: Set, - rigConfig: RigConfig | undefined - ): Promise { - let cacheEntryPromise: Promise | undefined = this._configPromiseCache.get( - resolvedConfigurationFilePath - ); - if (!cacheEntryPromise) { - cacheEntryPromise = this._loadConfigurationFileInnerAsync( - terminal, - resolvedConfigurationFilePath, - visitedConfigurationFilePaths, - rigConfig - ); - this._configPromiseCache.set(resolvedConfigurationFilePath, cacheEntryPromise); - } - - // We check for loops after caching a promise for this config file, but before attempting - // to resolve the promise. We can't handle loop detection in the `InnerAsync` function, because - // we could end up waiting for a cached promise (like A -> B -> A) that never resolves. - if (visitedConfigurationFilePaths.has(resolvedConfigurationFilePath)) { - const resolvedConfigurationFilePathForLogging: string = ConfigurationFile._formatPathForLogging( - resolvedConfigurationFilePath - ); - throw new Error( - 'A loop has been detected in the "extends" properties of configuration file at ' + - `"${resolvedConfigurationFilePathForLogging}".` - ); - } - visitedConfigurationFilePaths.add(resolvedConfigurationFilePath); - - return await cacheEntryPromise; - } - - // NOTE: Internal calls to load a configuration file should use `_loadConfigurationFileInnerWithCacheAsync`. - // Don't call this function directly, as it does not provide config file loop detection, - // and you won't get the advantage of queueing up for a config file that is already loading. - private async _loadConfigurationFileInnerAsync( - terminal: ITerminal, - resolvedConfigurationFilePath: string, - visitedConfigurationFilePaths: Set, - rigConfig: RigConfig | undefined - ): Promise { - const resolvedConfigurationFilePathForLogging: string = ConfigurationFile._formatPathForLogging( - resolvedConfigurationFilePath - ); - - let fileText: string; - try { - fileText = await FileSystem.readFileAsync(resolvedConfigurationFilePath); - } catch (e) { - if (FileSystem.isNotExistError(e as Error)) { - if (rigConfig) { - terminal.writeDebugLine( - `Config file "${resolvedConfigurationFilePathForLogging}" does not exist. Attempting to load via rig.` - ); - const rigResult: TConfigurationFile | undefined = await this._tryLoadConfigurationFileInRigAsync( - terminal, - rigConfig, - visitedConfigurationFilePaths - ); - if (rigResult) { - return rigResult; - } - } else { - terminal.writeDebugLine( - `Configuration file "${resolvedConfigurationFilePathForLogging}" not found.` - ); - } - - (e as Error).message = `File does not exist: ${resolvedConfigurationFilePathForLogging}`; - } - - throw e; - } - - let configurationJson: IConfigurationJson & TConfigurationFile; - try { - configurationJson = await JsonFile.parseString(fileText); - } catch (e) { - throw new Error(`In config file "${resolvedConfigurationFilePathForLogging}": ${e}`); - } - - this._annotateProperties(resolvedConfigurationFilePath, configurationJson); - - for (const [jsonPath, metadata] of Object.entries(this._jsonPathMetadata)) { - JSONPath({ - path: jsonPath, - json: configurationJson, - callback: (payload: unknown, payloadType: string, fullPayload: IJsonPathCallbackObject) => { - const resolvedPath: string = this._resolvePathProperty( - { - propertyName: fullPayload.path, - propertyValue: fullPayload.value, - configurationFilePath: resolvedConfigurationFilePath, - configurationFile: configurationJson - }, - metadata - ); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (fullPayload.parent as any)[fullPayload.parentProperty] = resolvedPath; - }, - otherTypeCallback: () => { - throw new Error('@other() tags are not supported'); - } - }); - } - - let parentConfiguration: TConfigurationFile | undefined; - if (configurationJson.extends) { - try { - const resolvedParentConfigPath: string = Import.resolveModule({ - modulePath: configurationJson.extends, - baseFolderPath: nodeJsPath.dirname(resolvedConfigurationFilePath) - }); - parentConfiguration = await this._loadConfigurationFileInnerWithCacheAsync( - terminal, - resolvedParentConfigPath, - visitedConfigurationFilePaths, - undefined - ); - } catch (e) { - if (FileSystem.isNotExistError(e as Error)) { - throw new Error( - `In file "${resolvedConfigurationFilePathForLogging}", file referenced in "extends" property ` + - `("${configurationJson.extends}") cannot be resolved.` - ); - } else { - throw e; - } - } - } - - const result: Partial = this._mergeConfigurationFiles( - parentConfiguration || {}, - configurationJson, - resolvedConfigurationFilePath - ); - try { - this._schema.validateObject(result, resolvedConfigurationFilePathForLogging); - } catch (e) { - throw new Error(`Resolved configuration object does not match schema: ${e}`); - } - - // If the schema validates, we can assume that the configuration file is complete. - return result as TConfigurationFile; - } - - private async _tryLoadConfigurationFileInRigAsync( - terminal: ITerminal, - rigConfig: RigConfig, - visitedConfigurationFilePaths: Set - ): Promise { - if (rigConfig.rigFound) { - const rigProfileFolder: string = await rigConfig.getResolvedProfileFolderAsync(); - try { - return await this._loadConfigurationFileInnerWithCacheAsync( - terminal, - nodeJsPath.resolve(rigProfileFolder, this.projectRelativeFilePath), - visitedConfigurationFilePaths, - undefined - ); - } catch (e) { - // Ignore cases where a configuration file doesn't exist in a rig - if (!FileSystem.isNotExistError(e as Error)) { - throw e; - } else { - terminal.writeDebugLine( - `Configuration file "${ - this.projectRelativeFilePath - }" not found in rig ("${ConfigurationFile._formatPathForLogging(rigProfileFolder)}")` - ); - } - } - } else { - terminal.writeDebugLine( - `No rig found for "${ConfigurationFile._formatPathForLogging(rigConfig.projectFolderPath)}"` - ); - } - - return undefined; - } - - private _annotateProperties(resolvedConfigurationFilePath: string, obj: TObject): void { - if (!obj) { - return; - } - - if (typeof obj === 'object') { - this._annotateProperty(resolvedConfigurationFilePath, obj); - - for (const objValue of Object.values(obj)) { - this._annotateProperties(resolvedConfigurationFilePath, objValue); - } - } - } - - private _annotateProperty(resolvedConfigurationFilePath: string, obj: TObject): void { - if (!obj) { - return; - } - - if (typeof obj === 'object') { - (obj as unknown as IAnnotatedField)[CONFIGURATION_FILE_FIELD_ANNOTATION] = { - configurationFilePath: resolvedConfigurationFilePath, - originalValues: { ...obj } - }; - } - } - - private _resolvePathProperty( - resolverOptions: IJsonPathMetadataResolverOptions, - metadata: IJsonPathMetadata - ): string { - const { propertyValue, configurationFilePath } = resolverOptions; - const resolutionMethod: PathResolutionMethod | undefined = metadata.pathResolutionMethod; - if (resolutionMethod === undefined) { - return propertyValue; - } - - switch (metadata.pathResolutionMethod) { - case PathResolutionMethod.resolvePathRelativeToConfigurationFile: { - return nodeJsPath.resolve(nodeJsPath.dirname(configurationFilePath), propertyValue); - } - - case PathResolutionMethod.resolvePathRelativeToProjectRoot: { - const packageRoot: string | undefined = - this._packageJsonLookup.tryGetPackageFolderFor(configurationFilePath); - if (!packageRoot) { - throw new Error( - `Could not find a package root for path "${ConfigurationFile._formatPathForLogging( - configurationFilePath - )}"` - ); - } - - return nodeJsPath.resolve(packageRoot, propertyValue); - } - - case PathResolutionMethod.NodeResolve: // TODO: Remove - case PathResolutionMethod.nodeResolve: { - return Import.resolveModule({ - modulePath: propertyValue, - baseFolderPath: nodeJsPath.dirname(configurationFilePath) - }); - } - - case PathResolutionMethod.custom: { - if (!metadata.customResolver) { - throw new Error( - `The pathResolutionMethod was set to "${PathResolutionMethod[resolutionMethod]}", but a custom ` + - 'resolver was not provided.' - ); - } - - return metadata.customResolver(resolverOptions); - } - - default: { - throw new Error( - `Unsupported PathResolutionMethod: ${PathResolutionMethod[resolutionMethod]} (${resolutionMethod})` - ); - } - } - } - - private _mergeConfigurationFiles( - parentConfiguration: Partial, - configurationJson: Partial, - resolvedConfigurationFilePath: string - ): Partial { - const ignoreProperties: Set = new Set(['extends', '$schema']); - - // Need to do a dance with the casting here because while we know that JSON keys are always - // strings, TypeScript doesn't. - return this._mergeObjects( - parentConfiguration as { [key: string]: unknown }, - configurationJson as { [key: string]: unknown }, - resolvedConfigurationFilePath, - this._defaultPropertyInheritance, - this._propertyInheritanceTypes as IPropertiesInheritance<{ [key: string]: unknown }>, - ignoreProperties - ) as Partial; - } - - private _mergeObjects( - parentObject: Partial, - currentObject: Partial, - resolvedConfigurationFilePath: string, - defaultPropertyInheritance: IPropertyInheritanceDefaults, - configuredPropertyInheritance?: IPropertiesInheritance, - ignoreProperties?: Set - ): Partial { - const resultAnnotation: IConfigurationFileFieldAnnotation> = { - configurationFilePath: resolvedConfigurationFilePath, - originalValues: {} as Partial - }; - const result: Partial = { - [CONFIGURATION_FILE_FIELD_ANNOTATION]: resultAnnotation - } as unknown as Partial; - - // An array of property names that are on the merging object. Typed as Set since it may - // contain inheritance type annotation keys, or other built-in properties that we ignore - // (eg. "extends", "$schema"). - const currentObjectPropertyNames: Set = new Set(Object.keys(currentObject)); - // An array of property names that should be included in the resulting object. - const filteredObjectPropertyNames: (keyof TField)[] = []; - // A map of property names to their inheritance type. - const inheritanceTypeMap: Map> = new Map(); - - // Do a first pass to gather and strip the inheritance type annotations from the merging object. - for (const propertyName of currentObjectPropertyNames) { - if (ignoreProperties && ignoreProperties.has(propertyName)) { - continue; - } - - // Try to get the inheritance type annotation from the merging object using the regex. - // Note: since this regex matches a specific style of property name, we should not need to - // allow for any escaping of $-prefixed properties. If this ever changes (eg. to allow for - // `"$propertyName": { ... }` options), then we'll likely need to handle that error case, - // as well as allow escaping $-prefixed properties that developers want to be serialized, - // possibly by using the form `$$propertyName` to escape `$propertyName`. - const inheritanceTypeMatches: RegExpMatchArray | null = propertyName.match( - CONFIGURATION_FILE_MERGE_BEHAVIOR_FIELD_REGEX - ); - if (inheritanceTypeMatches) { - // Should always be of length 2, since the first match is the entire string and the second - // match is the capture group. - const mergeTargetPropertyName: string = inheritanceTypeMatches[1]; - const inheritanceTypeRaw: unknown | undefined = currentObject[propertyName]; - if (!currentObjectPropertyNames.has(mergeTargetPropertyName)) { - throw new Error( - `Issue in processing configuration file property "${propertyName}". ` + - `An inheritance type was provided but no matching property was found in the parent.` - ); - } else if (typeof inheritanceTypeRaw !== 'string') { - throw new Error( - `Issue in processing configuration file property "${propertyName}". ` + - `An unsupported inheritance type was provided: ${JSON.stringify(inheritanceTypeRaw)}` - ); - } else if (typeof currentObject[mergeTargetPropertyName] !== 'object') { - throw new Error( - `Issue in processing configuration file property "${propertyName}". ` + - `An inheritance type was provided for a property that is not a keyed object or array.` - ); - } - switch (inheritanceTypeRaw.toLowerCase()) { - case 'append': - inheritanceTypeMap.set(mergeTargetPropertyName, { inheritanceType: InheritanceType.append }); - break; - case 'merge': - inheritanceTypeMap.set(mergeTargetPropertyName, { inheritanceType: InheritanceType.merge }); - break; - case 'replace': - inheritanceTypeMap.set(mergeTargetPropertyName, { inheritanceType: InheritanceType.replace }); - break; - default: - throw new Error( - `Issue in processing configuration file property "${propertyName}". ` + - `An unsupported inheritance type was provided: "${inheritanceTypeRaw}"` - ); - } - } else { - filteredObjectPropertyNames.push(propertyName); - } - } - - // We only filter the currentObject because the parent object should already be filtered - const propertyNames: Set = new Set([ - ...Object.keys(parentObject), - ...filteredObjectPropertyNames - ]); - - // Cycle through properties and merge them - for (const propertyName of propertyNames) { - const propertyValue: TField[keyof TField] | undefined = currentObject[propertyName]; - const parentPropertyValue: TField[keyof TField] | undefined = parentObject[propertyName]; - - let newValue: TField[keyof TField] | undefined; - const usePropertyValue: () => void = () => { - resultAnnotation.originalValues[propertyName] = this.getPropertyOriginalValue({ - parentObject: currentObject, - propertyName: propertyName - }); - newValue = propertyValue; - }; - const useParentPropertyValue: () => void = () => { - resultAnnotation.originalValues[propertyName] = this.getPropertyOriginalValue({ - parentObject: parentObject, - propertyName: propertyName - }); - newValue = parentPropertyValue; - }; - - if (propertyValue !== undefined && parentPropertyValue === undefined) { - usePropertyValue(); - } else if (parentPropertyValue !== undefined && propertyValue === undefined) { - useParentPropertyValue(); - } else if (propertyValue !== undefined && parentPropertyValue !== undefined) { - // If the property is an inheritance type annotation, use it, otherwise fallback to the configured - // top-level property inheritance, if one is specified. - let propertyInheritance: IPropertyInheritance | undefined = - inheritanceTypeMap.get(propertyName) ?? configuredPropertyInheritance?.[propertyName]; - if (!propertyInheritance) { - const bothAreArrays: boolean = Array.isArray(propertyValue) && Array.isArray(parentPropertyValue); - if (bothAreArrays) { - // If both are arrays, use the configured default array inheritance and fallback to appending - // if one is not specified - propertyInheritance = defaultPropertyInheritance.array ?? { - inheritanceType: InheritanceType.append - }; - } else { - const bothAreObjects: boolean = - propertyValue && - parentPropertyValue && - typeof propertyValue === 'object' && - typeof parentPropertyValue === 'object'; - if (bothAreObjects) { - // If both are objects, use the configured default object inheritance and fallback to replacing - // if one is not specified - propertyInheritance = defaultPropertyInheritance.object ?? { - inheritanceType: InheritanceType.replace - }; - } else { - // Fall back to replacing if they are of different types, since we don't know how to merge these - propertyInheritance = { inheritanceType: InheritanceType.replace }; - } - } - } - - switch (propertyInheritance.inheritanceType) { - case InheritanceType.replace: { - usePropertyValue(); - - break; - } - - case InheritanceType.append: { - if (!Array.isArray(propertyValue) || !Array.isArray(parentPropertyValue)) { - throw new Error( - `Issue in processing configuration file property "${propertyName.toString()}". ` + - `Property is not an array, but the inheritance type is set as "${InheritanceType.append}"` - ); - } - - newValue = [...parentPropertyValue, ...propertyValue] as TField[keyof TField]; - (newValue as unknown as IAnnotatedField)[CONFIGURATION_FILE_FIELD_ANNOTATION] = { - configurationFilePath: undefined, - originalValues: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ...(parentPropertyValue as any)[CONFIGURATION_FILE_FIELD_ANNOTATION].originalValues, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ...(propertyValue as any)[CONFIGURATION_FILE_FIELD_ANNOTATION].originalValues - } - }; - - break; - } - - case InheritanceType.merge: { - if (parentPropertyValue === null || propertyValue === null) { - throw new Error( - `Issue in processing configuration file property "${propertyName.toString()}". ` + - `Null values cannot be used when the inheritance type is set as "${InheritanceType.merge}"` - ); - } else if ( - (propertyValue && typeof propertyValue !== 'object') || - (parentPropertyValue && typeof parentPropertyValue !== 'object') - ) { - throw new Error( - `Issue in processing configuration file property "${propertyName.toString()}". ` + - `Primitive types cannot be provided when the inheritance type is set as "${InheritanceType.merge}"` - ); - } else if (Array.isArray(propertyValue) || Array.isArray(parentPropertyValue)) { - throw new Error( - `Issue in processing configuration file property "${propertyName.toString()}". ` + - `Property is not a keyed object, but the inheritance type is set as "${InheritanceType.merge}"` - ); - } - - // Recursively merge the parent and child objects. Don't pass the configuredPropertyInheritance or - // ignoreProperties because we are no longer at the top level of the configuration file. We also know - // that it must be a string-keyed object, since the JSON spec requires it. - newValue = this._mergeObjects( - parentPropertyValue as { [key: string]: unknown }, - propertyValue as { [key: string]: unknown }, - resolvedConfigurationFilePath, - defaultPropertyInheritance - ) as TField[keyof TField]; - - break; - } - - case InheritanceType.custom: { - const customInheritance: ICustomPropertyInheritance = - propertyInheritance as ICustomPropertyInheritance; - if ( - !customInheritance.inheritanceFunction || - typeof customInheritance.inheritanceFunction !== 'function' - ) { - throw new Error( - 'For property inheritance type "InheritanceType.custom", an inheritanceFunction must be provided.' - ); - } - - newValue = customInheritance.inheritanceFunction(propertyValue, parentPropertyValue); - - break; - } - - default: { - throw new Error(`Unknown inheritance type "${propertyInheritance}"`); - } - } - } - - result[propertyName] = newValue; - } - - return result; - } - - private _getConfigurationFilePathForProject(projectPath: string): string { - return nodeJsPath.resolve(projectPath, this.projectRelativeFilePath); - } -} diff --git a/libraries/heft-config-file/src/ConfigurationFileBase.ts b/libraries/heft-config-file/src/ConfigurationFileBase.ts new file mode 100644 index 00000000000..6f484f6fded --- /dev/null +++ b/libraries/heft-config-file/src/ConfigurationFileBase.ts @@ -0,0 +1,1265 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as nodeJsPath from 'node:path'; + +import { JSONPath } from 'jsonpath-plus'; + +import { JsonSchema, JsonFile, Import, FileSystem } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +interface IConfigurationJson { + extends?: string; +} + +/* eslint-disable @typescript-eslint/typedef,@typescript-eslint/no-redeclare,@typescript-eslint/no-namespace,@typescript-eslint/naming-convention */ +// This structure is used so that consumers can pass raw string literals and have it typecheck, without breaking existing callers. +/** + * @beta + * + * The set of possible mechanisms for merging properties from parent configuration files. + * If a child configuration file sets a property value to `null`, that will always delete the value + * specified in the parent configuration file, regardless of the inheritance type. + */ +const InheritanceType = { + /** + * Append additional elements after elements from the parent file's property. Only applicable + * for arrays. + */ + append: 'append', + + /** + * Perform a shallow merge of additional elements after elements from the parent file's property. + * Only applicable for objects. + */ + merge: 'merge', + + /** + * Discard elements from the parent file's property + */ + replace: 'replace', + + /** + * Custom inheritance functionality + */ + custom: 'custom' +} as const; +/** + * @beta + */ +type InheritanceType = (typeof InheritanceType)[keyof typeof InheritanceType]; +/** + * @beta + */ +declare namespace InheritanceType { + /** + * Append additional elements after elements from the parent file's property. Only applicable + * for arrays. + */ + export type append = typeof InheritanceType.append; + + /** + * Perform a shallow merge of additional elements after elements from the parent file's property. + * Only applicable for objects. + */ + export type merge = typeof InheritanceType.merge; + + /** + * Discard elements from the parent file's property + */ + export type replace = typeof InheritanceType.replace; + + /** + * Custom inheritance functionality + */ + export type custom = typeof InheritanceType.custom; +} +export { InheritanceType }; + +/** + * @beta + * + * The set of possible resolution methods for fields that refer to paths. + */ +const PathResolutionMethod = { + /** + * Resolve a path relative to the configuration file + */ + resolvePathRelativeToConfigurationFile: 'resolvePathRelativeToConfigurationFile', + + /** + * Resolve a path relative to the root of the project containing the configuration file + */ + resolvePathRelativeToProjectRoot: 'resolvePathRelativeToProjectRoot', + + /** + * Treat the property as a NodeJS-style require/import reference and resolve using standard + * NodeJS filesystem resolution + * + * @deprecated + * Use {@link (PathResolutionMethod:variable).nodeResolve} instead + */ + NodeResolve: 'NodeResolve', + + /** + * Treat the property as a NodeJS-style require/import reference and resolve using standard + * NodeJS filesystem resolution + */ + nodeResolve: 'nodeResolve', + + /** + * Resolve the property using a custom resolver. + */ + custom: 'custom' +} as const; +/** + * @beta + */ +type PathResolutionMethod = (typeof PathResolutionMethod)[keyof typeof PathResolutionMethod]; +/** + * @beta + */ +declare namespace PathResolutionMethod { + /** + * Resolve a path relative to the configuration file + */ + export type resolvePathRelativeToConfigurationFile = + typeof PathResolutionMethod.resolvePathRelativeToConfigurationFile; + + /** + * Resolve a path relative to the root of the project containing the configuration file + */ + export type resolvePathRelativeToProjectRoot = typeof PathResolutionMethod.resolvePathRelativeToProjectRoot; + + /** + * Treat the property as a NodeJS-style require/import reference and resolve using standard + * NodeJS filesystem resolution + * + * @deprecated + * Use {@link (PathResolutionMethod:namespace).nodeResolve} instead + */ + export type NodeResolve = typeof PathResolutionMethod.NodeResolve; + + /** + * Treat the property as a NodeJS-style require/import reference and resolve using standard + * NodeJS filesystem resolution + */ + export type nodeResolve = typeof PathResolutionMethod.nodeResolve; + + /** + * Resolve the property using a custom resolver. + */ + export type custom = typeof PathResolutionMethod.custom; +} +export { PathResolutionMethod }; +/* eslint-enable @typescript-eslint/typedef,@typescript-eslint/no-redeclare,@typescript-eslint/no-namespace,@typescript-eslint/naming-convention */ + +const CONFIGURATION_FILE_MERGE_BEHAVIOR_FIELD_REGEX: RegExp = /^\$([^\.]+)\.inheritanceType$/; +export const CONFIGURATION_FILE_FIELD_ANNOTATION: unique symbol = Symbol( + 'configuration-file-field-annotation' +); + +export interface IAnnotatedField< + TField, + TConfigurationFileFieldAnnotation extends + IConfigurationFileFieldAnnotation = IConfigurationFileFieldAnnotation +> { + [CONFIGURATION_FILE_FIELD_ANNOTATION]: TConfigurationFileFieldAnnotation; +} + +type IAnnotatedObject = Partial>; +type IRootAnnotatedObject = Partial< + IAnnotatedField> +>; + +interface IConfigurationFileFieldAnnotation { + configurationFilePath: string | undefined; + originalValues: { [propertyName in keyof TField]: unknown }; +} + +interface IRootConfigurationFileFieldAnnotation extends IConfigurationFileFieldAnnotation { + schemaPropertyOriginalValue?: string; +} + +interface IObjectWithSchema { + $schema?: string; +} + +/** + * Options provided to the custom resolver specified in {@link ICustomJsonPathMetadata}. + * + * @beta + */ +export interface IJsonPathMetadataResolverOptions { + /** + * The name of the property being resolved. + */ + propertyName: string; + /** + * The value of the path property being resolved. + */ + propertyValue: string; + /** + * The path to the configuration file the property was obtained from. + */ + configurationFilePath: string; + /** + * The configuration file the property was obtained from. + */ + configurationFile: Partial; + /** + * If this is a project configuration file, the root folder of the project. + */ + projectFolderPath?: string; +} + +/** + * Used to specify how node(s) in a JSON object should be processed after being loaded. + * + * @beta + */ +export interface ICustomJsonPathMetadata { + /** + * If `ICustomJsonPathMetadata.pathResolutionMethod` is set to `PathResolutionMethod.custom`, + * this property be used to resolve the path. + */ + customResolver?: (resolverOptions: IJsonPathMetadataResolverOptions) => string; + + /** + * If this property describes a filesystem path, use this property to describe + * how the path should be resolved. + */ + pathResolutionMethod?: PathResolutionMethod.custom; +} + +/** + * Used to specify how node(s) in a JSON object should be processed after being loaded. + * + * @beta + */ +export interface INonCustomJsonPathMetadata { + /** + * If this property describes a filesystem path, use this property to describe + * how the path should be resolved. + */ + pathResolutionMethod?: + | PathResolutionMethod.NodeResolve // TODO: Remove + | PathResolutionMethod.nodeResolve + | PathResolutionMethod.resolvePathRelativeToConfigurationFile + | PathResolutionMethod.resolvePathRelativeToProjectRoot; +} + +/** + * @beta + */ +export type PropertyInheritanceCustomFunction = ( + currentObject: TObject, + parentObject: TObject +) => TObject; + +/** + * @beta + */ +export interface IPropertyInheritance { + inheritanceType: TInheritanceType; +} + +/** + * @beta + */ +export interface ICustomPropertyInheritance extends IPropertyInheritance { + /** + * Provides a custom inheritance function. This function takes two arguments: the first is the + * child file's object, and the second is the parent file's object. The function should return + * the resulting combined object. + * This function will not be invoked if the current value is `null`, the property will simply be deleted. + */ + inheritanceFunction: PropertyInheritanceCustomFunction; +} + +/** + * @beta + */ +export type IPropertiesInheritance = { + [propertyName in keyof TConfigurationFile]?: + | IPropertyInheritance + | ICustomPropertyInheritance; +}; + +/** + * @beta + */ +export interface IPropertyInheritanceDefaults { + array?: IPropertyInheritance; + object?: IPropertyInheritance; +} + +/** + * @beta + */ +export type IJsonPathMetadata = ICustomJsonPathMetadata | INonCustomJsonPathMetadata; + +/** + * Keys in this object are JSONPaths {@link https://jsonpath.com/}, and values are objects + * that describe how node(s) selected by the JSONPath are processed after loading. + * + * @beta + */ +export interface IJsonPathsMetadata { + [jsonPath: string]: IJsonPathMetadata; +} + +/** + * A function to invoke after schema validation to validate the configuration file. + * If this function returns any value other than `true`, the configuration file API + * will throw an error indicating that custom validation failed. If the function wishes + * to provide its own error message, it may use any combination of the terminal and throwing + * its own error. + * @beta + */ +export type CustomValidationFunction = ( + configurationFile: TConfigurationFile, + resolvedConfigurationFilePathForLogging: string, + terminal: ITerminal +) => boolean; + +/** + * @beta + */ +export interface IConfigurationFileOptionsBase { + /** + * Use this property to specify how JSON nodes are postprocessed. + */ + jsonPathMetadata?: IJsonPathsMetadata; + + /** + * Use this property to control how root-level properties are handled between parent and child + * configuration files. + */ + propertyInheritance?: IPropertiesInheritance; + + /** + * Use this property to control how specific property types are handled between parent and child + * configuration files. + */ + propertyInheritanceDefaults?: IPropertyInheritanceDefaults; + + /** + * Use this property if you need to validate the configuration file in ways beyond what JSON schema can handle. + * This function will be invoked after JSON schema validation. + * + * If the file is valid, this function should return `true`, otherwise `ConfigurationFile` will throw an error + * indicating that custom validation failed. To suppress this error, the function may itself choose to throw. + */ + customValidationFunction?: CustomValidationFunction; +} + +/** + * @beta + */ +export type IConfigurationFileOptionsWithJsonSchemaFilePath< + TConfigurationFile, + TExtraOptions extends {} +> = IConfigurationFileOptionsBase & + TExtraOptions & { + /** + * The path to the schema for the configuration file. + */ + jsonSchemaPath: string; + jsonSchemaObject?: never; + }; + +/** + * @beta + */ +export type IConfigurationFileOptionsWithJsonSchemaObject< + TConfigurationFile, + TExtraOptions extends {} +> = IConfigurationFileOptionsBase & + TExtraOptions & { + /** + * The schema for the configuration file. + */ + jsonSchemaObject: object; + jsonSchemaPath?: never; + }; + +/** + * @beta + */ +export type IConfigurationFileOptions = + | IConfigurationFileOptionsWithJsonSchemaFilePath + | IConfigurationFileOptionsWithJsonSchemaObject; + +interface IJsonPathCallbackObject { + path: string; + parent: object; + parentProperty: string; + value: string; +} + +/** + * @beta + */ +export interface IOriginalValueOptions { + parentObject: Partial; + propertyName: keyof TParentProperty; +} + +interface IConfigurationFileCacheEntry { + resolvedConfigurationFilePath: string; + resolvedConfigurationFilePathForLogging: string; + parent?: IConfigurationFileCacheEntry; + configurationFile: TConfigFile & IConfigurationJson; +} + +/** + * Callback that returns a fallback configuration file path if the original configuration file was not found. + * @beta + */ +export type IOnConfigurationFileNotFoundCallback = ( + resolvedConfigurationFilePathForLogging: string +) => string | undefined; + +/** + * @beta + */ +export abstract class ConfigurationFileBase { + private readonly _getSchema: () => JsonSchema; + + private readonly _jsonPathMetadata: readonly [string, IJsonPathMetadata][]; + private readonly _propertyInheritanceTypes: IPropertiesInheritance; + private readonly _defaultPropertyInheritance: IPropertyInheritanceDefaults; + private readonly _customValidationFunction: CustomValidationFunction | undefined; + private __schema: JsonSchema | undefined; + private get _schema(): JsonSchema { + if (!this.__schema) { + this.__schema = this._getSchema(); + } + + return this.__schema; + } + + private readonly _configCache: Map> = new Map(); + private readonly _configPromiseCache: Map< + string, + Promise> + > = new Map(); + + public constructor(options: IConfigurationFileOptions) { + const { + jsonSchemaObject, + jsonSchemaPath, + jsonPathMetadata = {}, + propertyInheritance = {}, + propertyInheritanceDefaults = {}, + customValidationFunction + } = options; + if (jsonSchemaObject) { + this._getSchema = () => JsonSchema.fromLoadedObject(jsonSchemaObject); + } else { + this._getSchema = () => JsonSchema.fromFile(jsonSchemaPath); + } + + this._jsonPathMetadata = Object.entries(jsonPathMetadata); + this._propertyInheritanceTypes = propertyInheritance; + this._defaultPropertyInheritance = propertyInheritanceDefaults; + this._customValidationFunction = customValidationFunction; + } + + /** + * @internal + */ + public static _formatPathForLogging: (path: string) => string = (path: string) => path; + + /** + * Get the path to the source file that the referenced property was originally + * loaded from. + */ + public getObjectSourceFilePath(obj: TObject): string | undefined { + const { [CONFIGURATION_FILE_FIELD_ANNOTATION]: annotation }: IAnnotatedObject = obj; + return annotation?.configurationFilePath; + } + + /** + * Get the value of the specified property on the specified object that was originally + * loaded from a configuration file. + */ + public getPropertyOriginalValue( + options: IOriginalValueOptions + ): TValue | undefined { + const { parentObject, propertyName } = options; + const { [CONFIGURATION_FILE_FIELD_ANNOTATION]: annotation }: IAnnotatedObject = + parentObject; + if (annotation?.originalValues.hasOwnProperty(propertyName)) { + return annotation.originalValues[propertyName] as TValue; + } + } + + /** + * Get the original value of the `$schema` property from the original configuration file, it one was present. + */ + public getSchemaPropertyOriginalValue(obj: TObject): string | undefined { + const { [CONFIGURATION_FILE_FIELD_ANNOTATION]: annotation }: IRootAnnotatedObject = obj; + return annotation?.schemaPropertyOriginalValue; + } + + protected _loadConfigurationFileInnerWithCache( + terminal: ITerminal, + resolvedConfigurationFilePath: string, + projectFolderPath: string | undefined, + onConfigurationFileNotFound?: IOnConfigurationFileNotFoundCallback + ): TConfigurationFile { + const visitedConfigurationFilePaths: Set = new Set(); + const cacheEntry: IConfigurationFileCacheEntry = + this._loadConfigurationFileEntryWithCache( + terminal, + resolvedConfigurationFilePath, + visitedConfigurationFilePaths, + onConfigurationFileNotFound + ); + + const result: TConfigurationFile = this._finalizeConfigurationFile( + cacheEntry, + projectFolderPath, + terminal + ); + + return result; + } + + protected async _loadConfigurationFileInnerWithCacheAsync( + terminal: ITerminal, + resolvedConfigurationFilePath: string, + projectFolderPath: string | undefined, + onFileNotFound?: IOnConfigurationFileNotFoundCallback + ): Promise { + const visitedConfigurationFilePaths: Set = new Set(); + const cacheEntry: IConfigurationFileCacheEntry = + await this._loadConfigurationFileEntryWithCacheAsync( + terminal, + resolvedConfigurationFilePath, + visitedConfigurationFilePaths, + onFileNotFound + ); + + const result: TConfigurationFile = this._finalizeConfigurationFile( + cacheEntry, + projectFolderPath, + terminal + ); + + return result; + } + + private _loadConfigurationFileEntryWithCache( + terminal: ITerminal, + resolvedConfigurationFilePath: string, + visitedConfigurationFilePaths: Set, + onFileNotFound?: IOnConfigurationFileNotFoundCallback + ): IConfigurationFileCacheEntry { + if (visitedConfigurationFilePaths.has(resolvedConfigurationFilePath)) { + const resolvedConfigurationFilePathForLogging: string = ConfigurationFileBase._formatPathForLogging( + resolvedConfigurationFilePath + ); + throw new Error( + 'A loop has been detected in the "extends" properties of configuration file at ' + + `"${resolvedConfigurationFilePathForLogging}".` + ); + } + visitedConfigurationFilePaths.add(resolvedConfigurationFilePath); + + let cacheEntry: IConfigurationFileCacheEntry | undefined = this._configCache.get( + resolvedConfigurationFilePath + ); + if (!cacheEntry) { + cacheEntry = this._loadConfigurationFileEntry( + terminal, + resolvedConfigurationFilePath, + visitedConfigurationFilePaths, + onFileNotFound + ); + this._configCache.set(resolvedConfigurationFilePath, cacheEntry); + } + + return cacheEntry; + } + + private async _loadConfigurationFileEntryWithCacheAsync( + terminal: ITerminal, + resolvedConfigurationFilePath: string, + visitedConfigurationFilePaths: Set, + onConfigurationFileNotFound?: IOnConfigurationFileNotFoundCallback + ): Promise> { + if (visitedConfigurationFilePaths.has(resolvedConfigurationFilePath)) { + const resolvedConfigurationFilePathForLogging: string = ConfigurationFileBase._formatPathForLogging( + resolvedConfigurationFilePath + ); + throw new Error( + 'A loop has been detected in the "extends" properties of configuration file at ' + + `"${resolvedConfigurationFilePathForLogging}".` + ); + } + visitedConfigurationFilePaths.add(resolvedConfigurationFilePath); + + let cacheEntryPromise: Promise> | undefined = + this._configPromiseCache.get(resolvedConfigurationFilePath); + if (!cacheEntryPromise) { + cacheEntryPromise = this._loadConfigurationFileEntryAsync( + terminal, + resolvedConfigurationFilePath, + visitedConfigurationFilePaths, + onConfigurationFileNotFound + ).then((value: IConfigurationFileCacheEntry) => { + this._configCache.set(resolvedConfigurationFilePath, value); + return value; + }); + this._configPromiseCache.set(resolvedConfigurationFilePath, cacheEntryPromise); + } + + return await cacheEntryPromise; + } + + /** + * Parses the raw JSON-with-comments text of the configuration file. + * @param fileText - The text of the configuration file + * @param resolvedConfigurationFilePathForLogging - The path to the configuration file, formatted for logs + * @returns The parsed configuration file + */ + private _parseConfigurationFile( + fileText: string, + resolvedConfigurationFilePathForLogging: string + ): IConfigurationJson & TConfigurationFile { + let configurationJson: IConfigurationJson & TConfigurationFile; + try { + configurationJson = JsonFile.parseString(fileText); + } catch (e) { + throw new Error(`In configuration file "${resolvedConfigurationFilePathForLogging}": ${e}`); + } + + return configurationJson; + } + + /** + * Resolves all path properties and annotates properties with their original values. + * @param entry - The cache entry for the loaded configuration file + * @param projectFolderPath - The project folder path, if applicable + * @returns The configuration file with all path properties resolved + */ + private _contextualizeConfigurationFile( + entry: IConfigurationFileCacheEntry, + projectFolderPath: string | undefined + ): IConfigurationJson & TConfigurationFile { + // Deep copy the configuration file because different callers might contextualize properties differently. + const result: IConfigurationJson & TConfigurationFile = structuredClone< + IConfigurationJson & TConfigurationFile + >(entry.configurationFile); + + const { resolvedConfigurationFilePath } = entry; + + this._annotateProperties(resolvedConfigurationFilePath, result); + + for (const [jsonPath, metadata] of this._jsonPathMetadata) { + JSONPath({ + path: jsonPath, + json: result, + callback: (payload: unknown, payloadType: string, fullPayload: IJsonPathCallbackObject) => { + const resolvedPath: string = this._resolvePathProperty( + { + propertyName: fullPayload.path, + propertyValue: fullPayload.value, + configurationFilePath: resolvedConfigurationFilePath, + configurationFile: result, + projectFolderPath + }, + metadata + ); + (fullPayload.parent as Record)[fullPayload.parentProperty] = resolvedPath; + }, + otherTypeCallback: () => { + throw new Error('@other() tags are not supported'); + } + }); + } + + return result; + } + + /** + * Resolves all path properties and merges parent properties. + * @param entry - The cache entry for the loaded configuration file + * @param projectFolderPath - The project folder path, if applicable + * @returns The flattened, unvalidated configuration file, with path properties resolved + */ + private _contextualizeAndFlattenConfigurationFile( + entry: IConfigurationFileCacheEntry, + projectFolderPath: string | undefined + ): Partial { + const { parent, resolvedConfigurationFilePath } = entry; + const parentConfig: TConfigurationFile | {} = parent + ? this._contextualizeAndFlattenConfigurationFile(parent, projectFolderPath) + : {}; + + const currentConfig: IConfigurationJson & TConfigurationFile = this._contextualizeConfigurationFile( + entry, + projectFolderPath + ); + + const result: Partial = this._mergeConfigurationFiles( + parentConfig, + currentConfig, + resolvedConfigurationFilePath + ); + + return result; + } + + /** + * Resolves all path properties, merges parent properties, and validates the configuration file. + * @param entry - The cache entry for the loaded configuration file + * @param projectFolderPath - The project folder path, if applicable + * @param terminal - The terminal to log validation messages to + * @returns The finalized configuration file + */ + private _finalizeConfigurationFile( + entry: IConfigurationFileCacheEntry, + projectFolderPath: string | undefined, + terminal: ITerminal + ): TConfigurationFile { + const { resolvedConfigurationFilePathForLogging } = entry; + + const result: Partial = this._contextualizeAndFlattenConfigurationFile( + entry, + projectFolderPath + ); + + try { + this._schema.validateObject(result, resolvedConfigurationFilePathForLogging); + } catch (e) { + throw new Error(`Resolved configuration object does not match schema: ${e}`); + } + + if ( + this._customValidationFunction && + !this._customValidationFunction( + result as TConfigurationFile, + resolvedConfigurationFilePathForLogging, + terminal + ) + ) { + // To suppress this error, the function may throw its own error, such as an AlreadyReportedError if it already + // logged to the terminal. + throw new Error( + `Resolved configuration file at "${resolvedConfigurationFilePathForLogging}" failed custom validation.` + ); + } + + // If the schema validates, we can assume that the configuration file is complete. + return result as TConfigurationFile; + } + + // NOTE: Internal calls to load a configuration file should use `_loadConfigurationFileInnerWithCache`. + // Don't call this function directly, as it does not provide config file loop detection, + // and you won't get the advantage of queueing up for a config file that is already loading. + private _loadConfigurationFileEntry( + terminal: ITerminal, + resolvedConfigurationFilePath: string, + visitedConfigurationFilePaths: Set, + fileNotFoundFallback?: IOnConfigurationFileNotFoundCallback + ): IConfigurationFileCacheEntry { + const resolvedConfigurationFilePathForLogging: string = ConfigurationFileBase._formatPathForLogging( + resolvedConfigurationFilePath + ); + + let fileText: string; + try { + fileText = FileSystem.readFile(resolvedConfigurationFilePath); + } catch (e) { + if (FileSystem.isNotExistError(e as Error)) { + const fallbackPath: string | undefined = fileNotFoundFallback?.( + resolvedConfigurationFilePathForLogging + ); + if (fallbackPath) { + try { + return this._loadConfigurationFileEntryWithCache( + terminal, + fallbackPath, + visitedConfigurationFilePaths + ); + } catch (fallbackError) { + if (!FileSystem.isNotExistError(fallbackError as Error)) { + throw fallbackError; + } + // Otherwise report the missing original file. + } + } + + terminal.writeDebugLine(`Configuration file "${resolvedConfigurationFilePathForLogging}" not found.`); + (e as Error).message = `File does not exist: ${resolvedConfigurationFilePathForLogging}`; + } + + throw e; + } + const configurationJson: IConfigurationJson & TConfigurationFile = this._parseConfigurationFile( + fileText, + resolvedConfigurationFilePathForLogging + ); + + let parentConfiguration: IConfigurationFileCacheEntry | undefined; + if (configurationJson.extends) { + try { + const resolvedParentConfigPath: string = Import.resolveModule({ + modulePath: configurationJson.extends, + baseFolderPath: nodeJsPath.dirname(resolvedConfigurationFilePath) + }); + parentConfiguration = this._loadConfigurationFileEntryWithCache( + terminal, + resolvedParentConfigPath, + visitedConfigurationFilePaths + ); + } catch (e) { + if (FileSystem.isNotExistError(e as Error)) { + throw new Error( + `In file "${resolvedConfigurationFilePathForLogging}", file referenced in "extends" property ` + + `("${configurationJson.extends}") cannot be resolved.` + ); + } else { + throw e; + } + } + } + + const result: IConfigurationFileCacheEntry = { + configurationFile: configurationJson, + resolvedConfigurationFilePath, + resolvedConfigurationFilePathForLogging, + parent: parentConfiguration + }; + return result; + } + + // NOTE: Internal calls to load a configuration file should use `_loadConfigurationFileInnerWithCacheAsync`. + // Don't call this function directly, as it does not provide config file loop detection, + // and you won't get the advantage of queueing up for a config file that is already loading. + private async _loadConfigurationFileEntryAsync( + terminal: ITerminal, + resolvedConfigurationFilePath: string, + visitedConfigurationFilePaths: Set, + fileNotFoundFallback?: IOnConfigurationFileNotFoundCallback + ): Promise> { + const resolvedConfigurationFilePathForLogging: string = ConfigurationFileBase._formatPathForLogging( + resolvedConfigurationFilePath + ); + + let fileText: string; + try { + fileText = await FileSystem.readFileAsync(resolvedConfigurationFilePath); + } catch (e) { + if (FileSystem.isNotExistError(e as Error)) { + const fallbackPath: string | undefined = fileNotFoundFallback?.( + resolvedConfigurationFilePathForLogging + ); + if (fallbackPath) { + try { + return await this._loadConfigurationFileEntryWithCacheAsync( + terminal, + fallbackPath, + visitedConfigurationFilePaths + ); + } catch (fallbackError) { + if (!FileSystem.isNotExistError(fallbackError as Error)) { + throw fallbackError; + } + // Otherwise report the missing original file. + } + } + + terminal.writeDebugLine(`Configuration file "${resolvedConfigurationFilePathForLogging}" not found.`); + (e as Error).message = `File does not exist: ${resolvedConfigurationFilePathForLogging}`; + } + + throw e; + } + const configurationJson: IConfigurationJson & TConfigurationFile = this._parseConfigurationFile( + fileText, + resolvedConfigurationFilePathForLogging + ); + + let parentConfiguration: IConfigurationFileCacheEntry | undefined; + if (configurationJson.extends) { + try { + const resolvedParentConfigPath: string = await Import.resolveModuleAsync({ + modulePath: configurationJson.extends, + baseFolderPath: nodeJsPath.dirname(resolvedConfigurationFilePath) + }); + parentConfiguration = await this._loadConfigurationFileEntryWithCacheAsync( + terminal, + resolvedParentConfigPath, + visitedConfigurationFilePaths + ); + } catch (e) { + if (FileSystem.isNotExistError(e as Error)) { + throw new Error( + `In file "${resolvedConfigurationFilePathForLogging}", file referenced in "extends" property ` + + `("${configurationJson.extends}") cannot be resolved.` + ); + } else { + throw e; + } + } + } + + const result: IConfigurationFileCacheEntry = { + configurationFile: configurationJson, + resolvedConfigurationFilePath, + resolvedConfigurationFilePathForLogging, + parent: parentConfiguration + }; + return result; + } + + private _annotateProperties(resolvedConfigurationFilePath: string, root: TObject): void { + if (!root) { + return; + } + + const queue: Set = new Set([root]); + for (const obj of queue) { + if (obj && typeof obj === 'object') { + (obj as unknown as IAnnotatedField)[CONFIGURATION_FILE_FIELD_ANNOTATION] = { + configurationFilePath: resolvedConfigurationFilePath, + originalValues: { ...obj } + }; + + for (const objValue of Object.values(obj)) { + queue.add(objValue as TObject); + } + } + } + } + + private _resolvePathProperty( + resolverOptions: IJsonPathMetadataResolverOptions, + metadata: IJsonPathMetadata + ): string { + const { propertyValue, configurationFilePath, projectFolderPath } = resolverOptions; + const resolutionMethod: PathResolutionMethod | undefined = metadata.pathResolutionMethod; + if (resolutionMethod === undefined) { + return propertyValue; + } + + switch (metadata.pathResolutionMethod) { + case PathResolutionMethod.resolvePathRelativeToConfigurationFile: { + return nodeJsPath.resolve(nodeJsPath.dirname(configurationFilePath), propertyValue); + } + + case PathResolutionMethod.resolvePathRelativeToProjectRoot: { + const packageRoot: string | undefined = projectFolderPath; + if (!packageRoot) { + throw new Error( + `Project-relative resolution was requested in "${ConfigurationFileBase._formatPathForLogging( + configurationFilePath + )}" but no project root was provided to the configuration file loader.` + ); + } + + return nodeJsPath.resolve(packageRoot, propertyValue); + } + + case PathResolutionMethod.NodeResolve: // TODO: Remove + case PathResolutionMethod.nodeResolve: { + return Import.resolveModule({ + modulePath: propertyValue, + baseFolderPath: nodeJsPath.dirname(configurationFilePath) + }); + } + + case PathResolutionMethod.custom: { + if (!metadata.customResolver) { + throw new Error( + `The pathResolutionMethod was set to "${PathResolutionMethod[resolutionMethod]}", but a custom ` + + 'resolver was not provided.' + ); + } + + return metadata.customResolver(resolverOptions); + } + + default: { + throw new Error( + `Unsupported PathResolutionMethod: ${PathResolutionMethod[resolutionMethod]} (${resolutionMethod})` + ); + } + } + } + + private _mergeConfigurationFiles( + parentConfiguration: Partial, + configurationJson: Partial, + resolvedConfigurationFilePath: string + ): Partial { + const ignoreProperties: Set = new Set(['extends', '$schema']); + + // Need to do a dance with the casting here because while we know that JSON keys are always + // strings, TypeScript doesn't. + const result: Partial = this._mergeObjects( + parentConfiguration as { [key: string]: unknown }, + configurationJson as { [key: string]: unknown }, + resolvedConfigurationFilePath, + this._defaultPropertyInheritance, + this._propertyInheritanceTypes as IPropertiesInheritance<{ [key: string]: unknown }>, + ignoreProperties + ) as Partial; + + const schemaPropertyOriginalValue: string | undefined = (configurationJson as IObjectWithSchema).$schema; + (result as unknown as Required>)[ + CONFIGURATION_FILE_FIELD_ANNOTATION + ].schemaPropertyOriginalValue = schemaPropertyOriginalValue; + + return result; + } + + private _mergeObjects( + parentObject: Partial, + currentObject: Partial, + resolvedConfigurationFilePath: string, + defaultPropertyInheritance: IPropertyInheritanceDefaults, + configuredPropertyInheritance?: IPropertiesInheritance, + ignoreProperties?: Set + ): Partial { + const resultAnnotation: IConfigurationFileFieldAnnotation> = { + configurationFilePath: resolvedConfigurationFilePath, + originalValues: {} as Partial + }; + const result: Partial = { + [CONFIGURATION_FILE_FIELD_ANNOTATION]: resultAnnotation + } as unknown as Partial; + + // An array of property names that are on the merging object. Typed as Set since it may + // contain inheritance type annotation keys, or other built-in properties that we ignore + // (eg. "extends", "$schema"). + const currentObjectPropertyNames: Set = new Set(Object.keys(currentObject)); + // A map of property names to their inheritance type. + const inheritanceTypeMap: Map> = new Map(); + + // The set of property names that should be included in the resulting object + // All names from the parent are assumed to already be filtered. + const mergedPropertyNames: Set = new Set(Object.keys(parentObject)); + + // Do a first pass to gather and strip the inheritance type annotations from the merging object. + for (const propertyName of currentObjectPropertyNames) { + if (ignoreProperties && ignoreProperties.has(propertyName)) { + continue; + } + + // Try to get the inheritance type annotation from the merging object using the regex. + // Note: since this regex matches a specific style of property name, we should not need to + // allow for any escaping of $-prefixed properties. If this ever changes (eg. to allow for + // `"$propertyName": { ... }` options), then we'll likely need to handle that error case, + // as well as allow escaping $-prefixed properties that developers want to be serialized, + // possibly by using the form `$$propertyName` to escape `$propertyName`. + const inheritanceTypeMatches: RegExpMatchArray | null = propertyName.match( + CONFIGURATION_FILE_MERGE_BEHAVIOR_FIELD_REGEX + ); + if (inheritanceTypeMatches) { + // Should always be of length 2, since the first match is the entire string and the second + // match is the capture group. + const mergeTargetPropertyName: string = inheritanceTypeMatches[1]; + const inheritanceTypeRaw: unknown | undefined = currentObject[propertyName]; + if (!currentObjectPropertyNames.has(mergeTargetPropertyName)) { + throw new Error( + `Issue in processing configuration file property "${propertyName}". ` + + `An inheritance type was provided but no matching property was found in the parent.` + ); + } else if (typeof inheritanceTypeRaw !== 'string') { + throw new Error( + `Issue in processing configuration file property "${propertyName}". ` + + `An unsupported inheritance type was provided: ${JSON.stringify(inheritanceTypeRaw)}` + ); + } else if (typeof currentObject[mergeTargetPropertyName] !== 'object') { + throw new Error( + `Issue in processing configuration file property "${propertyName}". ` + + `An inheritance type was provided for a property that is not a keyed object or array.` + ); + } + switch (inheritanceTypeRaw.toLowerCase()) { + case 'append': + inheritanceTypeMap.set(mergeTargetPropertyName, { inheritanceType: InheritanceType.append }); + break; + case 'merge': + inheritanceTypeMap.set(mergeTargetPropertyName, { inheritanceType: InheritanceType.merge }); + break; + case 'replace': + inheritanceTypeMap.set(mergeTargetPropertyName, { inheritanceType: InheritanceType.replace }); + break; + default: + throw new Error( + `Issue in processing configuration file property "${propertyName}". ` + + `An unsupported inheritance type was provided: "${inheritanceTypeRaw}"` + ); + } + } else { + mergedPropertyNames.add(propertyName); + } + } + + // Cycle through properties and merge them + for (const propertyName of mergedPropertyNames) { + const propertyValue: TField[keyof TField] | undefined = currentObject[propertyName]; + const parentPropertyValue: TField[keyof TField] | undefined = parentObject[propertyName]; + + let newValue: TField[keyof TField] | undefined; + const usePropertyValue: () => void = () => { + resultAnnotation.originalValues[propertyName] = this.getPropertyOriginalValue({ + parentObject: currentObject, + propertyName: propertyName + }); + newValue = propertyValue; + }; + const useParentPropertyValue: () => void = () => { + resultAnnotation.originalValues[propertyName] = this.getPropertyOriginalValue({ + parentObject: parentObject, + propertyName: propertyName + }); + newValue = parentPropertyValue; + }; + + if (propertyValue === null) { + if (parentPropertyValue !== undefined) { + resultAnnotation.originalValues[propertyName] = this.getPropertyOriginalValue({ + parentObject: parentObject, + propertyName: propertyName + }); + } + newValue = undefined; + } else if (propertyValue !== undefined && parentPropertyValue === undefined) { + usePropertyValue(); + } else if (parentPropertyValue !== undefined && propertyValue === undefined) { + useParentPropertyValue(); + } else if (propertyValue !== undefined && parentPropertyValue !== undefined) { + // If the property is an inheritance type annotation, use it, otherwise fallback to the configured + // top-level property inheritance, if one is specified. + let propertyInheritance: IPropertyInheritance | undefined = + inheritanceTypeMap.get(propertyName) ?? configuredPropertyInheritance?.[propertyName]; + if (!propertyInheritance) { + const bothAreArrays: boolean = Array.isArray(propertyValue) && Array.isArray(parentPropertyValue); + if (bothAreArrays) { + // If both are arrays, use the configured default array inheritance and fallback to appending + // if one is not specified + propertyInheritance = defaultPropertyInheritance.array ?? { + inheritanceType: InheritanceType.append + }; + } else { + const bothAreObjects: boolean = + propertyValue && + parentPropertyValue && + typeof propertyValue === 'object' && + typeof parentPropertyValue === 'object'; + if (bothAreObjects) { + // If both are objects, use the configured default object inheritance and fallback to replacing + // if one is not specified + propertyInheritance = defaultPropertyInheritance.object ?? { + inheritanceType: InheritanceType.replace + }; + } else { + // Fall back to replacing if they are of different types, since we don't know how to merge these + propertyInheritance = { inheritanceType: InheritanceType.replace }; + } + } + } + + switch (propertyInheritance.inheritanceType) { + case InheritanceType.replace: { + usePropertyValue(); + + break; + } + + case InheritanceType.append: { + if (!Array.isArray(propertyValue) || !Array.isArray(parentPropertyValue)) { + throw new Error( + `Issue in processing configuration file property "${propertyName.toString()}". ` + + `Property is not an array, but the inheritance type is set as "${InheritanceType.append}"` + ); + } + + newValue = [...parentPropertyValue, ...propertyValue] as TField[keyof TField]; + (newValue as unknown as IAnnotatedField)[CONFIGURATION_FILE_FIELD_ANNOTATION] = { + configurationFilePath: undefined, + originalValues: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...(parentPropertyValue as any)[CONFIGURATION_FILE_FIELD_ANNOTATION].originalValues, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...(propertyValue as any)[CONFIGURATION_FILE_FIELD_ANNOTATION].originalValues + } + }; + + break; + } + + case InheritanceType.merge: { + if (parentPropertyValue === null || propertyValue === null) { + throw new Error( + `Issue in processing configuration file property "${propertyName.toString()}". ` + + `Null values cannot be used when the inheritance type is set as "${InheritanceType.merge}"` + ); + } else if ( + (propertyValue && typeof propertyValue !== 'object') || + (parentPropertyValue && typeof parentPropertyValue !== 'object') + ) { + throw new Error( + `Issue in processing configuration file property "${propertyName.toString()}". ` + + `Primitive types cannot be provided when the inheritance type is set as "${InheritanceType.merge}"` + ); + } else if (Array.isArray(propertyValue) || Array.isArray(parentPropertyValue)) { + throw new Error( + `Issue in processing configuration file property "${propertyName.toString()}". ` + + `Property is not a keyed object, but the inheritance type is set as "${InheritanceType.merge}"` + ); + } + + // Recursively merge the parent and child objects. Don't pass the configuredPropertyInheritance or + // ignoreProperties because we are no longer at the top level of the configuration file. We also know + // that it must be a string-keyed object, since the JSON spec requires it. + newValue = this._mergeObjects( + parentPropertyValue as { [key: string]: unknown }, + propertyValue as { [key: string]: unknown }, + resolvedConfigurationFilePath, + defaultPropertyInheritance + ) as TField[keyof TField]; + + break; + } + + case InheritanceType.custom: { + const customInheritance: ICustomPropertyInheritance = + propertyInheritance as ICustomPropertyInheritance; + if ( + !customInheritance.inheritanceFunction || + typeof customInheritance.inheritanceFunction !== 'function' + ) { + throw new Error( + 'For property inheritance type "InheritanceType.custom", an inheritanceFunction must be provided.' + ); + } + + newValue = customInheritance.inheritanceFunction(propertyValue, parentPropertyValue); + + break; + } + + default: { + throw new Error(`Unknown inheritance type "${propertyInheritance}"`); + } + } + } + + if (newValue !== undefined) { + // Don't attach the key for undefined values so that they don't enumerate. + result[propertyName] = newValue; + } + } + + return result; + } +} diff --git a/libraries/heft-config-file/src/NonProjectConfigurationFile.ts b/libraries/heft-config-file/src/NonProjectConfigurationFile.ts new file mode 100644 index 00000000000..20929de4d07 --- /dev/null +++ b/libraries/heft-config-file/src/NonProjectConfigurationFile.ts @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { FileSystem, PackageJsonLookup } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import { ConfigurationFileBase } from './ConfigurationFileBase'; + +/** + * @beta + */ +export class NonProjectConfigurationFile extends ConfigurationFileBase< + TConfigurationFile, + {} +> { + /** + * Load the configuration file at the specified absolute path, automatically resolving + * `extends` properties. Will throw an error if the file cannot be found. + */ + public loadConfigurationFile(terminal: ITerminal, filePath: string): TConfigurationFile { + return this._loadConfigurationFileInnerWithCache( + terminal, + filePath, + PackageJsonLookup.instance.tryGetPackageFolderFor(filePath) + ); + } + + /** + * Load the configuration file at the specified absolute path, automatically resolving + * `extends` properties. Will throw an error if the file cannot be found. + */ + public async loadConfigurationFileAsync( + terminal: ITerminal, + filePath: string + ): Promise { + return await this._loadConfigurationFileInnerWithCacheAsync( + terminal, + filePath, + PackageJsonLookup.instance.tryGetPackageFolderFor(filePath) + ); + } + + /** + * This function is identical to {@link NonProjectConfigurationFile.loadConfigurationFile}, except + * that it returns `undefined` instead of throwing an error if the configuration file cannot be found. + */ + public tryLoadConfigurationFile(terminal: ITerminal, filePath: string): TConfigurationFile | undefined { + try { + return this.loadConfigurationFile(terminal, filePath); + } catch (e) { + if (FileSystem.isNotExistError(e as Error)) { + return undefined; + } + throw e; + } + } + + /** + * This function is identical to {@link NonProjectConfigurationFile.loadConfigurationFileAsync}, except + * that it returns `undefined` instead of throwing an error if the configuration file cannot be found. + */ + public async tryLoadConfigurationFileAsync( + terminal: ITerminal, + filePath: string + ): Promise { + try { + return await this.loadConfigurationFileAsync(terminal, filePath); + } catch (e) { + if (FileSystem.isNotExistError(e as Error)) { + return undefined; + } + throw e; + } + } +} diff --git a/libraries/heft-config-file/src/ProjectConfigurationFile.ts b/libraries/heft-config-file/src/ProjectConfigurationFile.ts new file mode 100644 index 00000000000..4ccbb659db7 --- /dev/null +++ b/libraries/heft-config-file/src/ProjectConfigurationFile.ts @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as nodeJsPath from 'node:path'; + +import { FileSystem, PackageJsonLookup } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; +import type { IRigConfig } from '@rushstack/rig-package'; + +import { + ConfigurationFileBase, + type IOnConfigurationFileNotFoundCallback, + type IConfigurationFileOptions +} from './ConfigurationFileBase'; + +/** + * @beta + */ +export interface IProjectConfigurationFileOptions { + /** + * A project root-relative path to the configuration file that should be loaded. + */ + projectRelativeFilePath: string; +} + +/** + * Alias for the constructor type for {@link ProjectConfigurationFile}. + * @beta + */ +export type IProjectConfigurationFileSpecification = IConfigurationFileOptions< + TConfigFile, + IProjectConfigurationFileOptions +>; + +/** + * @beta + */ +export class ProjectConfigurationFile extends ConfigurationFileBase< + TConfigurationFile, + IProjectConfigurationFileOptions +> { + /** {@inheritDoc IProjectConfigurationFileOptions.projectRelativeFilePath} */ + public readonly projectRelativeFilePath: string; + + public constructor(options: IProjectConfigurationFileSpecification) { + super(options); + this.projectRelativeFilePath = options.projectRelativeFilePath; + } + + /** + * Find and return a configuration file for the specified project, automatically resolving + * `extends` properties and handling rigged configuration files. Will throw an error if a configuration + * file cannot be found in the rig or project config folder. + */ + public loadConfigurationFileForProject( + terminal: ITerminal, + projectPath: string, + rigConfig?: IRigConfig + ): TConfigurationFile { + const projectConfigurationFilePath: string = this._getConfigurationFilePathForProject(projectPath); + return this._loadConfigurationFileInnerWithCache( + terminal, + projectConfigurationFilePath, + PackageJsonLookup.instance.tryGetPackageFolderFor(projectPath), + this._getRigConfigFallback(terminal, rigConfig) + ); + } + + /** + * Find and return a configuration file for the specified project, automatically resolving + * `extends` properties and handling rigged configuration files. Will throw an error if a configuration + * file cannot be found in the rig or project config folder. + */ + public async loadConfigurationFileForProjectAsync( + terminal: ITerminal, + projectPath: string, + rigConfig?: IRigConfig + ): Promise { + const projectConfigurationFilePath: string = this._getConfigurationFilePathForProject(projectPath); + return await this._loadConfigurationFileInnerWithCacheAsync( + terminal, + projectConfigurationFilePath, + PackageJsonLookup.instance.tryGetPackageFolderFor(projectPath), + this._getRigConfigFallback(terminal, rigConfig) + ); + } + + /** + * This function is identical to {@link ProjectConfigurationFile.loadConfigurationFileForProject}, except + * that it returns `undefined` instead of throwing an error if the configuration file cannot be found. + */ + public tryLoadConfigurationFileForProject( + terminal: ITerminal, + projectPath: string, + rigConfig?: IRigConfig + ): TConfigurationFile | undefined { + try { + return this.loadConfigurationFileForProject(terminal, projectPath, rigConfig); + } catch (e) { + if (FileSystem.isNotExistError(e as Error)) { + return undefined; + } + throw e; + } + } + + /** + * This function is identical to {@link ProjectConfigurationFile.loadConfigurationFileForProjectAsync}, except + * that it returns `undefined` instead of throwing an error if the configuration file cannot be found. + */ + public async tryLoadConfigurationFileForProjectAsync( + terminal: ITerminal, + projectPath: string, + rigConfig?: IRigConfig + ): Promise { + try { + return await this.loadConfigurationFileForProjectAsync(terminal, projectPath, rigConfig); + } catch (e) { + if (FileSystem.isNotExistError(e as Error)) { + return undefined; + } + throw e; + } + } + + private _getConfigurationFilePathForProject(projectPath: string): string { + return nodeJsPath.resolve(projectPath, this.projectRelativeFilePath); + } + + private _getRigConfigFallback( + terminal: ITerminal, + rigConfig: IRigConfig | undefined + ): IOnConfigurationFileNotFoundCallback | undefined { + return rigConfig + ? (resolvedConfigurationFilePathForLogging: string) => { + if (rigConfig.rigFound) { + const rigProfileFolder: string = rigConfig.getResolvedProfileFolder(); + terminal.writeDebugLine( + `Configuration file "${resolvedConfigurationFilePathForLogging}" does not exist. Attempting to load via rig ("${ConfigurationFileBase._formatPathForLogging(rigProfileFolder)}").` + ); + return nodeJsPath.resolve(rigProfileFolder, this.projectRelativeFilePath); + } else { + terminal.writeDebugLine( + `No rig found for "${ConfigurationFileBase._formatPathForLogging(rigConfig.projectFolderPath)}"` + ); + } + } + : undefined; + } +} diff --git a/libraries/heft-config-file/src/TestUtilities.ts b/libraries/heft-config-file/src/TestUtilities.ts new file mode 100644 index 00000000000..b51f3f455c0 --- /dev/null +++ b/libraries/heft-config-file/src/TestUtilities.ts @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { CONFIGURATION_FILE_FIELD_ANNOTATION, type IAnnotatedField } from './ConfigurationFileBase'; + +/** + * Returns an object with investigative annotations stripped, useful for snapshot testing. + * + * @beta + */ +export function stripAnnotations(obj: TObject): TObject { + if (typeof obj !== 'object' || obj === null) { + return obj; + } else if (Array.isArray(obj)) { + const result: unknown[] = []; + for (const value of obj) { + result.push(stripAnnotations(value)); + } + + return result as TObject; + } else { + const clonedObj: TObject = { ...obj } as TObject; + delete (clonedObj as Partial>)[CONFIGURATION_FILE_FIELD_ANNOTATION]; + for (const [name, value] of Object.entries(clonedObj as object)) { + clonedObj[name as keyof TObject] = stripAnnotations( + value as TObject[keyof TObject] + ); + } + + return clonedObj; + } +} diff --git a/libraries/heft-config-file/src/index.ts b/libraries/heft-config-file/src/index.ts index 7650ea5fc68..bd912a1ea15 100644 --- a/libraries/heft-config-file/src/index.ts +++ b/libraries/heft-config-file/src/index.ts @@ -9,22 +9,48 @@ */ export { - ConfigurationFile, - IConfigurationFileOptionsBase, - IConfigurationFileOptionsWithJsonSchemaFilePath, - IConfigurationFileOptionsWithJsonSchemaObject, - IConfigurationFileOptions, - ICustomJsonPathMetadata, - ICustomPropertyInheritance, - IJsonPathMetadataResolverOptions, - IJsonPathMetadata, - IJsonPathsMetadata, + ConfigurationFileBase, + type CustomValidationFunction, + type IConfigurationFileOptionsBase, + type IConfigurationFileOptionsWithJsonSchemaFilePath, + type IConfigurationFileOptionsWithJsonSchemaObject, + type IConfigurationFileOptions, + type ICustomJsonPathMetadata, + type ICustomPropertyInheritance, + type IJsonPathMetadataResolverOptions, + type IJsonPathMetadata, + type IJsonPathsMetadata, InheritanceType, - INonCustomJsonPathMetadata, - IOriginalValueOptions, - IPropertiesInheritance, - IPropertyInheritance, - IPropertyInheritanceDefaults, + type INonCustomJsonPathMetadata, + type IOnConfigurationFileNotFoundCallback, + type IOriginalValueOptions, + type IPropertiesInheritance, + type IPropertyInheritance, + type IPropertyInheritanceDefaults, PathResolutionMethod, - PropertyInheritanceCustomFunction -} from './ConfigurationFile'; + type PropertyInheritanceCustomFunction +} from './ConfigurationFileBase'; + +import { ProjectConfigurationFile } from './ProjectConfigurationFile'; + +/** + * @deprecated Use {@link ProjectConfigurationFile} instead. + * @beta + */ +export const ConfigurationFile: typeof ProjectConfigurationFile = ProjectConfigurationFile; + +/** + * @deprecated Use {@link ProjectConfigurationFile} instead. + * @beta + */ +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type ConfigurationFile = ProjectConfigurationFile; + +export { + ProjectConfigurationFile, + type IProjectConfigurationFileOptions, + type IProjectConfigurationFileSpecification +} from './ProjectConfigurationFile'; +export { NonProjectConfigurationFile } from './NonProjectConfigurationFile'; + +export * as TestUtilities from './TestUtilities'; diff --git a/libraries/heft-config-file/src/test/ConfigurationFile.test.ts b/libraries/heft-config-file/src/test/ConfigurationFile.test.ts index 49e6893fff1..86a8efcd3a8 100644 --- a/libraries/heft-config-file/src/test/ConfigurationFile.test.ts +++ b/libraries/heft-config-file/src/test/ConfigurationFile.test.ts @@ -1,29 +1,24 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as nodeJsPath from 'path'; - -import { ConfigurationFile, PathResolutionMethod, InheritanceType } from '../ConfigurationFile'; -import { - FileSystem, - JsonFile, - Path, - StringBufferTerminalProvider, - Terminal, - Text -} from '@rushstack/node-core-library'; +import nodeJsPath from 'node:path'; +import { FileSystem, JsonFile, Path, Text } from '@rushstack/node-core-library'; +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; import { RigConfig } from '@rushstack/rig-package'; -describe(ConfigurationFile.name, () => { - const projectRoot: string = nodeJsPath.resolve(__dirname, '..', '..'); +import { ProjectConfigurationFile } from '../ProjectConfigurationFile'; +import { PathResolutionMethod, InheritanceType, ConfigurationFileBase } from '../ConfigurationFileBase'; +import { NonProjectConfigurationFile } from '../NonProjectConfigurationFile'; + +describe('ConfigurationFile', () => { + const projectRoot: string = nodeJsPath.resolve(__dirname, '../..'); let terminalProvider: StringBufferTerminalProvider; let terminal: Terminal; beforeEach(() => { - const projectRoot: string = nodeJsPath.resolve(__dirname, '..', '..'); const formatPathForLogging: (path: string) => string = (path: string) => `/${Path.convertToSlashes(nodeJsPath.relative(projectRoot, path))}`; - jest.spyOn(ConfigurationFile, '_formatPathForLogging').mockImplementation(formatPathForLogging); + jest.spyOn(ConfigurationFileBase, '_formatPathForLogging').mockImplementation(formatPathForLogging); jest.spyOn(JsonFile, '_formatPathForError').mockImplementation(formatPathForLogging); terminalProvider = new StringBufferTerminalProvider(false); @@ -31,13 +26,7 @@ describe(ConfigurationFile.name, () => { }); afterEach(() => { - expect({ - log: terminalProvider.getOutput(), - warning: terminalProvider.getWarningOutput(), - error: terminalProvider.getErrorOutput(), - verbose: terminalProvider.getVerbose(), - debug: terminalProvider.getDebugOutput() - }).toMatchSnapshot(); + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); }); describe('A simple config file', () => { @@ -49,9 +38,30 @@ describe(ConfigurationFile.name, () => { thing: string; } - it('Correctly loads the config file', async () => { - const configFileLoader: ConfigurationFile = - new ConfigurationFile({ + it('Correctly loads the config file', () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath: projectRelativeFilePath, + ...partialOptions + }); + const loadedConfigFile: ISimplestConfigFile = configFileLoader.loadConfigurationFileForProject( + terminal, + __dirname + ); + const expectedConfigFile: ISimplestConfigFile = { thing: 'A' }; + + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile)).toEqual( + nodeJsPath.resolve(__dirname, projectRelativeFilePath) + ); + expect( + configFileLoader.getPropertyOriginalValue({ parentObject: loadedConfigFile, propertyName: 'thing' }) + ).toEqual('A'); + }); + + it('Correctly loads the config file async', async () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath: projectRelativeFilePath, ...partialOptions }); @@ -68,9 +78,36 @@ describe(ConfigurationFile.name, () => { ).toEqual('A'); }); - it('Correctly resolves paths relative to the config file', async () => { - const configFileLoader: ConfigurationFile = - new ConfigurationFile({ + it('Correctly resolves paths relative to the config file', () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath: projectRelativeFilePath, + ...partialOptions, + jsonPathMetadata: { + '$.thing': { + pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToConfigurationFile + } + } + }); + const loadedConfigFile: ISimplestConfigFile = configFileLoader.loadConfigurationFileForProject( + terminal, + __dirname + ); + const expectedConfigFile: ISimplestConfigFile = { + thing: nodeJsPath.resolve(__dirname, configFileFolderName, 'A') + }; + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile)).toEqual( + nodeJsPath.resolve(__dirname, projectRelativeFilePath) + ); + expect( + configFileLoader.getPropertyOriginalValue({ parentObject: loadedConfigFile, propertyName: 'thing' }) + ).toEqual('A'); + }); + + it('Correctly resolves paths relative to the config file async', async () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath: projectRelativeFilePath, ...partialOptions, jsonPathMetadata: { @@ -93,9 +130,36 @@ describe(ConfigurationFile.name, () => { ).toEqual('A'); }); - it('Correctly resolves paths relative to the project root', async () => { - const configFileLoader: ConfigurationFile = - new ConfigurationFile({ + it('Correctly resolves paths relative to the project root', () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath: projectRelativeFilePath, + ...partialOptions, + jsonPathMetadata: { + '$.thing': { + pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToProjectRoot + } + } + }); + const loadedConfigFile: ISimplestConfigFile = configFileLoader.loadConfigurationFileForProject( + terminal, + __dirname + ); + const expectedConfigFile: ISimplestConfigFile = { + thing: nodeJsPath.resolve(projectRoot, 'A') + }; + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile)).toEqual( + nodeJsPath.resolve(__dirname, projectRelativeFilePath) + ); + expect( + configFileLoader.getPropertyOriginalValue({ parentObject: loadedConfigFile, propertyName: 'thing' }) + ).toEqual('A'); + }); + + it('Correctly resolves paths relative to the project root async', async () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath: projectRelativeFilePath, ...partialOptions, jsonPathMetadata: { @@ -117,6 +181,42 @@ describe(ConfigurationFile.name, () => { configFileLoader.getPropertyOriginalValue({ parentObject: loadedConfigFile, propertyName: 'thing' }) ).toEqual('A'); }); + + it(`The ${NonProjectConfigurationFile.name} version works correctly`, () => { + const configFileLoader: NonProjectConfigurationFile = + new NonProjectConfigurationFile(partialOptions); + const loadedConfigFile: ISimplestConfigFile = configFileLoader.loadConfigurationFile( + terminal, + `${__dirname}/${projectRelativeFilePath}` + ); + const expectedConfigFile: ISimplestConfigFile = { thing: 'A' }; + + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile)).toEqual( + `${__dirname}/${projectRelativeFilePath}` + ); + expect( + configFileLoader.getPropertyOriginalValue({ parentObject: loadedConfigFile, propertyName: 'thing' }) + ).toEqual('A'); + }); + + it(`The ${NonProjectConfigurationFile.name} version works correctly async`, async () => { + const configFileLoader: NonProjectConfigurationFile = + new NonProjectConfigurationFile(partialOptions); + const loadedConfigFile: ISimplestConfigFile = await configFileLoader.loadConfigurationFileAsync( + terminal, + `${__dirname}/${projectRelativeFilePath}` + ); + const expectedConfigFile: ISimplestConfigFile = { thing: 'A' }; + + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile)).toEqual( + `${__dirname}/${projectRelativeFilePath}` + ); + expect( + configFileLoader.getPropertyOriginalValue({ parentObject: loadedConfigFile, propertyName: 'thing' }) + ).toEqual('A'); + }); } describe('with a JSON schema path', () => { @@ -133,25 +233,40 @@ describe(ConfigurationFile.name, () => { describe('A simple config file containing an array and an object', () => { const configFileFolderName: string = 'simpleConfigFile'; const projectRelativeFilePath: string = `${configFileFolderName}/simpleConfigFile.json`; - const schemaPath: string = nodeJsPath.resolve( - __dirname, - configFileFolderName, - 'simpleConfigFile.schema.json' - ); + const schemaPath: string = `${__dirname}/${configFileFolderName}/simpleConfigFile.schema.json`; interface ISimpleConfigFile { things: string[]; thingsObj: { A: { B: string }; D: { E: string } }; booleanProp: boolean; + stringProp?: string; } - it('Correctly loads the config file', async () => { - const configFileLoader: ConfigurationFile = new ConfigurationFile( - { + it('Correctly loads the config file', () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath, jsonSchemaPath: schemaPath - } + }); + const loadedConfigFile: ISimpleConfigFile = configFileLoader.loadConfigurationFileForProject( + terminal, + __dirname ); + const expectedConfigFile: ISimpleConfigFile = { + things: ['A', 'B', 'C'], + thingsObj: { A: { B: 'C' }, D: { E: 'F' } }, + booleanProp: true, + stringProp: 'someValue' + }; + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + }); + + it('Correctly loads the config file async', async () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath, + jsonSchemaPath: schemaPath + }); const loadedConfigFile: ISimpleConfigFile = await configFileLoader.loadConfigurationFileForProjectAsync( terminal, __dirname @@ -159,14 +274,15 @@ describe(ConfigurationFile.name, () => { const expectedConfigFile: ISimpleConfigFile = { things: ['A', 'B', 'C'], thingsObj: { A: { B: 'C' }, D: { E: 'F' } }, - booleanProp: true + booleanProp: true, + stringProp: 'someValue' }; expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); }); - it('Correctly resolves paths relative to the config file', async () => { - const configFileLoader: ConfigurationFile = new ConfigurationFile( - { + it('Correctly resolves paths relative to the config file', () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath, jsonSchemaPath: schemaPath, jsonPathMetadata: { @@ -177,8 +293,41 @@ describe(ConfigurationFile.name, () => { pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToConfigurationFile } } - } + }); + const loadedConfigFile: ISimpleConfigFile = configFileLoader.loadConfigurationFileForProject( + terminal, + __dirname ); + const expectedConfigFile: ISimpleConfigFile = { + things: [ + nodeJsPath.resolve(__dirname, configFileFolderName, 'A'), + nodeJsPath.resolve(__dirname, configFileFolderName, 'B'), + nodeJsPath.resolve(__dirname, configFileFolderName, 'C') + ], + thingsObj: { + A: { B: nodeJsPath.resolve(__dirname, configFileFolderName, 'C') }, + D: { E: nodeJsPath.resolve(__dirname, configFileFolderName, 'F') } + }, + booleanProp: true, + stringProp: 'someValue' + }; + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + }); + + it('Correctly resolves paths relative to the config file async', async () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath, + jsonSchemaPath: schemaPath, + jsonPathMetadata: { + '$.things.*': { + pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToConfigurationFile + }, + '$.thingsObj.*.*': { + pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToConfigurationFile + } + } + }); const loadedConfigFile: ISimpleConfigFile = await configFileLoader.loadConfigurationFileForProjectAsync( terminal, __dirname @@ -193,14 +342,15 @@ describe(ConfigurationFile.name, () => { A: { B: nodeJsPath.resolve(__dirname, configFileFolderName, 'C') }, D: { E: nodeJsPath.resolve(__dirname, configFileFolderName, 'F') } }, - booleanProp: true + booleanProp: true, + stringProp: 'someValue' }; expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); }); - it('Correctly resolves paths relative to the project root', async () => { - const configFileLoader: ConfigurationFile = new ConfigurationFile( - { + it('Correctly resolves paths relative to the project root', () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath, jsonSchemaPath: schemaPath, jsonPathMetadata: { @@ -211,8 +361,41 @@ describe(ConfigurationFile.name, () => { pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToProjectRoot } } - } + }); + const loadedConfigFile: ISimpleConfigFile = configFileLoader.loadConfigurationFileForProject( + terminal, + __dirname ); + const expectedConfigFile: ISimpleConfigFile = { + things: [ + nodeJsPath.resolve(projectRoot, 'A'), + nodeJsPath.resolve(projectRoot, 'B'), + nodeJsPath.resolve(projectRoot, 'C') + ], + thingsObj: { + A: { B: nodeJsPath.resolve(projectRoot, 'C') }, + D: { E: nodeJsPath.resolve(projectRoot, 'F') } + }, + booleanProp: true, + stringProp: 'someValue' + }; + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + }); + + it('Correctly resolves paths relative to the project root async', async () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath, + jsonSchemaPath: schemaPath, + jsonPathMetadata: { + '$.things.*': { + pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToProjectRoot + }, + '$.thingsObj.*.*': { + pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToProjectRoot + } + } + }); const loadedConfigFile: ISimpleConfigFile = await configFileLoader.loadConfigurationFileForProjectAsync( terminal, __dirname @@ -227,7 +410,8 @@ describe(ConfigurationFile.name, () => { A: { B: nodeJsPath.resolve(projectRoot, 'C') }, D: { E: nodeJsPath.resolve(projectRoot, 'F') } }, - booleanProp: true + booleanProp: true, + stringProp: 'someValue' }; expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); }); @@ -236,40 +420,78 @@ describe(ConfigurationFile.name, () => { describe('A simple config file with "extends"', () => { const configFileFolderName: string = 'simpleConfigFileWithExtends'; const projectRelativeFilePath: string = `${configFileFolderName}/simpleConfigFileWithExtends.json`; - const schemaPath: string = nodeJsPath.resolve( - __dirname, - configFileFolderName, - 'simpleConfigFileWithExtends.schema.json' - ); + const schemaPath: string = `${__dirname}/${configFileFolderName}/simpleConfigFileWithExtends.schema.json`; interface ISimpleConfigFile { things: string[]; thingsObj: { A: { B?: string; D?: string }; D?: { E: string }; F?: { G: string } }; booleanProp: boolean; + stringProp?: string; } - it('Correctly loads the config file with default config meta', async () => { - const configFileLoader: ConfigurationFile = new ConfigurationFile( - { + it('Correctly loads the config file with default config meta', () => { + const expectedConfigFile: ISimpleConfigFile = { + things: ['A', 'B', 'C', 'D', 'E'], + thingsObj: { A: { D: 'E' }, F: { G: 'H' } }, + booleanProp: false + }; + + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath, jsonSchemaPath: schemaPath - } - ); - const loadedConfigFile: ISimpleConfigFile = await configFileLoader.loadConfigurationFileForProjectAsync( + }); + const loadedConfigFile: ISimpleConfigFile = configFileLoader.loadConfigurationFileForProject( terminal, __dirname ); + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + + const nonProjectConfigFileLoader: NonProjectConfigurationFile = + new NonProjectConfigurationFile({ + jsonSchemaPath: schemaPath + }); + const nonProjectLoadedConfigFile: ISimpleConfigFile = nonProjectConfigFileLoader.loadConfigurationFile( + terminal, + `${__dirname}/${projectRelativeFilePath}` + ); + expect(JSON.stringify(nonProjectLoadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + }); + + it('Correctly loads the config file with default config meta async', async () => { const expectedConfigFile: ISimpleConfigFile = { things: ['A', 'B', 'C', 'D', 'E'], thingsObj: { A: { D: 'E' }, F: { G: 'H' } }, booleanProp: false }; + + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath, + jsonSchemaPath: schemaPath + }); + const loadedConfigFile: ISimpleConfigFile = await configFileLoader.loadConfigurationFileForProjectAsync( + terminal, + __dirname + ); + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + + const nonProjectConfigFileLoader: NonProjectConfigurationFile = + new NonProjectConfigurationFile({ + jsonSchemaPath: schemaPath + }); + const nonProjectLoadedConfigFile: ISimpleConfigFile = + await nonProjectConfigFileLoader.loadConfigurationFileAsync( + terminal, + `${__dirname}/${projectRelativeFilePath}` + ); + expect(JSON.stringify(nonProjectLoadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); }); - it('Correctly loads the config file with "append" and "merge" in config meta', async () => { - const configFileLoader: ConfigurationFile = new ConfigurationFile( - { + it('Correctly loads the config file with "append" and "merge" in config meta', () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath, jsonSchemaPath: schemaPath, propertyInheritance: { @@ -280,8 +502,33 @@ describe(ConfigurationFile.name, () => { inheritanceType: InheritanceType.merge } } - } + }); + const loadedConfigFile: ISimpleConfigFile = configFileLoader.loadConfigurationFileForProject( + terminal, + __dirname ); + const expectedConfigFile: ISimpleConfigFile = { + things: ['A', 'B', 'C', 'D', 'E'], + thingsObj: { A: { D: 'E' }, D: { E: 'F' }, F: { G: 'H' } }, + booleanProp: false + }; + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + }); + + it('Correctly loads the config file with "append" and "merge" in config meta async', async () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath, + jsonSchemaPath: schemaPath, + propertyInheritance: { + things: { + inheritanceType: InheritanceType.append + }, + thingsObj: { + inheritanceType: InheritanceType.merge + } + } + }); const loadedConfigFile: ISimpleConfigFile = await configFileLoader.loadConfigurationFileForProjectAsync( terminal, __dirname @@ -294,9 +541,9 @@ describe(ConfigurationFile.name, () => { expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); }); - it('Correctly loads the config file with "replace" in config meta', async () => { - const configFileLoader: ConfigurationFile = new ConfigurationFile( - { + it('Correctly loads the config file with "replace" in config meta', () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath, jsonSchemaPath: schemaPath, propertyInheritance: { @@ -307,8 +554,33 @@ describe(ConfigurationFile.name, () => { inheritanceType: InheritanceType.replace } } - } + }); + const loadedConfigFile: ISimpleConfigFile = configFileLoader.loadConfigurationFileForProject( + terminal, + __dirname ); + const expectedConfigFile: ISimpleConfigFile = { + things: ['D', 'E'], + thingsObj: { A: { D: 'E' }, F: { G: 'H' } }, + booleanProp: false + }; + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + }); + + it('Correctly loads the config file with "replace" in config meta async', async () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath, + jsonSchemaPath: schemaPath, + propertyInheritance: { + things: { + inheritanceType: InheritanceType.replace + }, + thingsObj: { + inheritanceType: InheritanceType.replace + } + } + }); const loadedConfigFile: ISimpleConfigFile = await configFileLoader.loadConfigurationFileForProjectAsync( terminal, __dirname @@ -321,17 +593,38 @@ describe(ConfigurationFile.name, () => { expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); }); - it('Correctly loads the config file with modified merge behaviors for arrays and objects', async () => { - const configFileLoader: ConfigurationFile = new ConfigurationFile( - { + it('Correctly loads the config file with modified merge behaviors for arrays and objects', () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath, jsonSchemaPath: schemaPath, propertyInheritanceDefaults: { array: { inheritanceType: InheritanceType.replace }, object: { inheritanceType: InheritanceType.merge } } - } + }); + const loadedConfigFile: ISimpleConfigFile = configFileLoader.loadConfigurationFileForProject( + terminal, + __dirname ); + const expectedConfigFile: ISimpleConfigFile = { + things: ['D', 'E'], + thingsObj: { A: { B: 'C', D: 'E' }, D: { E: 'F' }, F: { G: 'H' } }, + booleanProp: false + }; + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + }); + + it('Correctly loads the config file with modified merge behaviors for arrays and objects async', async () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath, + jsonSchemaPath: schemaPath, + propertyInheritanceDefaults: { + array: { inheritanceType: InheritanceType.replace }, + object: { inheritanceType: InheritanceType.merge } + } + }); const loadedConfigFile: ISimpleConfigFile = await configFileLoader.loadConfigurationFileForProjectAsync( terminal, __dirname @@ -344,9 +637,9 @@ describe(ConfigurationFile.name, () => { expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); }); - it('Correctly loads the config file with "custom" in config meta', async () => { - const configFileLoader: ConfigurationFile = new ConfigurationFile( - { + it('Correctly loads the config file with "custom" in config meta', () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath, jsonSchemaPath: schemaPath, propertyInheritance: { @@ -366,9 +659,8 @@ describe(ConfigurationFile.name, () => { } } } - } - ); - const loadedConfigFile: ISimpleConfigFile = await configFileLoader.loadConfigurationFileForProjectAsync( + }); + const loadedConfigFile: ISimpleConfigFile = configFileLoader.loadConfigurationFileForProject( terminal, __dirname ); @@ -380,21 +672,97 @@ describe(ConfigurationFile.name, () => { expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); }); - it('Correctly resolves paths relative to the config file', async () => { - const configFileLoader: ConfigurationFile = new ConfigurationFile( - { + it('Correctly loads the config file with "custom" in config meta async', async () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath, jsonSchemaPath: schemaPath, - jsonPathMetadata: { - '$.things.*': { - pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToConfigurationFile - }, + propertyInheritance: { + things: { + inheritanceType: InheritanceType.custom, + inheritanceFunction: (current: string[], parent: string[]) => ['X', 'Y', 'Z'] + }, + thingsObj: { + inheritanceType: InheritanceType.custom, + inheritanceFunction: ( + current: { A: { B?: string; D?: string }; D?: { E: string }; F?: { G: string } }, + parent: { A: { B?: string; D?: string }; D?: { E: string }; F?: { G: string } } + ) => { + return { + A: { B: 'Y', D: 'Z' } + }; + } + } + } + }); + const loadedConfigFile: ISimpleConfigFile = await configFileLoader.loadConfigurationFileForProjectAsync( + terminal, + __dirname + ); + const expectedConfigFile: ISimpleConfigFile = { + things: ['X', 'Y', 'Z'], + thingsObj: { A: { B: 'Y', D: 'Z' } }, + booleanProp: false + }; + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + }); + + it('Correctly resolves paths relative to the config file', () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath, + jsonSchemaPath: schemaPath, + jsonPathMetadata: { + '$.things.*': { + pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToConfigurationFile + }, '$.thingsObj.*.*': { pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToConfigurationFile } } - } + }); + const loadedConfigFile: ISimpleConfigFile = configFileLoader.loadConfigurationFileForProject( + terminal, + __dirname + ); + const parentConfigFileFolder: string = nodeJsPath.resolve( + __dirname, + configFileFolderName, + '..', + 'simpleConfigFile' ); + + const expectedConfigFile: ISimpleConfigFile = { + things: [ + nodeJsPath.resolve(parentConfigFileFolder, 'A'), + nodeJsPath.resolve(parentConfigFileFolder, 'B'), + nodeJsPath.resolve(parentConfigFileFolder, 'C'), + nodeJsPath.resolve(__dirname, configFileFolderName, 'D'), + nodeJsPath.resolve(__dirname, configFileFolderName, 'E') + ], + thingsObj: { + A: { D: nodeJsPath.resolve(__dirname, configFileFolderName, 'E') }, + F: { G: nodeJsPath.resolve(__dirname, configFileFolderName, 'H') } + }, + booleanProp: false + }; + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + }); + + it('Correctly resolves paths relative to the config file async', async () => { + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath, + jsonSchemaPath: schemaPath, + jsonPathMetadata: { + '$.things.*': { + pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToConfigurationFile + }, + '$.thingsObj.*.*': { + pathResolutionMethod: PathResolutionMethod.resolvePathRelativeToConfigurationFile + } + } + }); const loadedConfigFile: ISimpleConfigFile = await configFileLoader.loadConfigurationFileForProjectAsync( terminal, __dirname @@ -429,7 +797,7 @@ describe(ConfigurationFile.name, () => { plugins: { plugin: string }[]; } - it('Correctly loads a complex config file (Deprecated PathResolutionMethod.NodeResolve)', async () => { + it('Correctly loads a complex config file (Deprecated PathResolutionMethod.NodeResolve)', () => { const projectRelativeFilePath: string = 'complexConfigFile/pluginsD.json'; const rootConfigFilePath: string = nodeJsPath.resolve(__dirname, 'complexConfigFile', 'pluginsA.json'); const secondConfigFilePath: string = nodeJsPath.resolve( @@ -437,10 +805,100 @@ describe(ConfigurationFile.name, () => { 'complexConfigFile', 'pluginsB.json' ); - const schemaPath: string = nodeJsPath.resolve(__dirname, 'complexConfigFile', 'plugins.schema.json'); + const schemaPath: string = `${__dirname}/complexConfigFile/plugins.schema.json`; - const configFileLoader: ConfigurationFile = - new ConfigurationFile({ + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath: projectRelativeFilePath, + jsonSchemaPath: schemaPath, + jsonPathMetadata: { + '$.plugins.*.plugin': { + pathResolutionMethod: PathResolutionMethod.NodeResolve + } + } + }); + const loadedConfigFile: IComplexConfigFile = configFileLoader.loadConfigurationFileForProject( + terminal, + __dirname + ); + const expectedConfigFile: IComplexConfigFile = { + plugins: [ + { + plugin: FileSystem.getRealPath( + nodeJsPath.resolve( + projectRoot, + 'node_modules', + '@rushstack', + 'node-core-library', + 'lib-commonjs', + 'index.js' + ) + ) + }, + { + plugin: FileSystem.getRealPath( + nodeJsPath.resolve( + projectRoot, + 'node_modules', + '@rushstack', + 'heft', + 'lib-commonjs', + 'index.js' + ) + ) + }, + { + plugin: FileSystem.getRealPath( + nodeJsPath.resolve(projectRoot, 'node_modules', 'jsonpath-plus', 'dist', 'index-node-cjs.cjs') + ) + } + ] + }; + + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + + expect( + configFileLoader.getPropertyOriginalValue({ + parentObject: loadedConfigFile.plugins[0], + propertyName: 'plugin' + }) + ).toEqual('@rushstack/node-core-library'); + expect( + configFileLoader.getPropertyOriginalValue({ + parentObject: loadedConfigFile.plugins[1], + propertyName: 'plugin' + }) + ).toEqual('@rushstack/heft'); + expect( + configFileLoader.getPropertyOriginalValue({ + parentObject: loadedConfigFile.plugins[2], + propertyName: 'plugin' + }) + ).toEqual('jsonpath-plus'); + + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.plugins[0])).toEqual( + rootConfigFilePath + ); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.plugins[1])).toEqual( + nodeJsPath.resolve(__dirname, secondConfigFilePath) + ); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.plugins[2])).toEqual( + nodeJsPath.resolve(__dirname, secondConfigFilePath) + ); + }); + + it('Correctly loads a complex config file async (Deprecated PathResolutionMethod.NodeResolve)', async () => { + const projectRelativeFilePath: string = 'complexConfigFile/pluginsD.json'; + const rootConfigFilePath: string = nodeJsPath.resolve(__dirname, 'complexConfigFile', 'pluginsA.json'); + const secondConfigFilePath: string = nodeJsPath.resolve( + __dirname, + 'complexConfigFile', + 'pluginsB.json' + ); + const schemaPath: string = `${__dirname}/complexConfigFile/plugins.schema.json`; + + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath: projectRelativeFilePath, jsonSchemaPath: schemaPath, jsonPathMetadata: { @@ -460,19 +918,26 @@ describe(ConfigurationFile.name, () => { 'node_modules', '@rushstack', 'node-core-library', - 'lib', + 'lib-commonjs', 'index.js' ) ) }, { plugin: await FileSystem.getRealPathAsync( - nodeJsPath.resolve(projectRoot, 'node_modules', '@rushstack', 'heft', 'lib', 'index.js') + nodeJsPath.resolve( + projectRoot, + 'node_modules', + '@rushstack', + 'heft', + 'lib-commonjs', + 'index.js' + ) ) }, { plugin: await FileSystem.getRealPathAsync( - nodeJsPath.resolve(projectRoot, 'node_modules', '@rushstack', 'eslint-config', 'index.js') + nodeJsPath.resolve(projectRoot, 'node_modules', 'jsonpath-plus', 'dist', 'index-node-cjs.cjs') ) } ] @@ -497,7 +962,7 @@ describe(ConfigurationFile.name, () => { parentObject: loadedConfigFile.plugins[2], propertyName: 'plugin' }) - ).toEqual('@rushstack/eslint-config'); + ).toEqual('jsonpath-plus'); expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.plugins[0])).toEqual( rootConfigFilePath @@ -510,7 +975,7 @@ describe(ConfigurationFile.name, () => { ); }); - it('Correctly loads a complex config file', async () => { + it('Correctly loads a complex config file', () => { const projectRelativeFilePath: string = 'complexConfigFile/pluginsD.json'; const rootConfigFilePath: string = nodeJsPath.resolve(__dirname, 'complexConfigFile', 'pluginsA.json'); const secondConfigFilePath: string = nodeJsPath.resolve( @@ -518,10 +983,100 @@ describe(ConfigurationFile.name, () => { 'complexConfigFile', 'pluginsB.json' ); - const schemaPath: string = nodeJsPath.resolve(__dirname, 'complexConfigFile', 'plugins.schema.json'); + const schemaPath: string = `${__dirname}/complexConfigFile/plugins.schema.json`; - const configFileLoader: ConfigurationFile = - new ConfigurationFile({ + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath: projectRelativeFilePath, + jsonSchemaPath: schemaPath, + jsonPathMetadata: { + '$.plugins.*.plugin': { + pathResolutionMethod: PathResolutionMethod.nodeResolve + } + } + }); + const loadedConfigFile: IComplexConfigFile = configFileLoader.loadConfigurationFileForProject( + terminal, + __dirname + ); + const expectedConfigFile: IComplexConfigFile = { + plugins: [ + { + plugin: FileSystem.getRealPath( + nodeJsPath.resolve( + projectRoot, + 'node_modules', + '@rushstack', + 'node-core-library', + 'lib-commonjs', + 'index.js' + ) + ) + }, + { + plugin: FileSystem.getRealPath( + nodeJsPath.resolve( + projectRoot, + 'node_modules', + '@rushstack', + 'heft', + 'lib-commonjs', + 'index.js' + ) + ) + }, + { + plugin: FileSystem.getRealPath( + nodeJsPath.resolve(projectRoot, 'node_modules', 'jsonpath-plus', 'dist', 'index-node-cjs.cjs') + ) + } + ] + }; + + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + + expect( + configFileLoader.getPropertyOriginalValue({ + parentObject: loadedConfigFile.plugins[0], + propertyName: 'plugin' + }) + ).toEqual('@rushstack/node-core-library'); + expect( + configFileLoader.getPropertyOriginalValue({ + parentObject: loadedConfigFile.plugins[1], + propertyName: 'plugin' + }) + ).toEqual('@rushstack/heft'); + expect( + configFileLoader.getPropertyOriginalValue({ + parentObject: loadedConfigFile.plugins[2], + propertyName: 'plugin' + }) + ).toEqual('jsonpath-plus'); + + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.plugins[0])).toEqual( + rootConfigFilePath + ); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.plugins[1])).toEqual( + nodeJsPath.resolve(__dirname, secondConfigFilePath) + ); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.plugins[2])).toEqual( + nodeJsPath.resolve(__dirname, secondConfigFilePath) + ); + }); + + it('Correctly loads a complex config file async', async () => { + const projectRelativeFilePath: string = 'complexConfigFile/pluginsD.json'; + const rootConfigFilePath: string = nodeJsPath.resolve(__dirname, 'complexConfigFile', 'pluginsA.json'); + const secondConfigFilePath: string = nodeJsPath.resolve( + __dirname, + 'complexConfigFile', + 'pluginsB.json' + ); + const schemaPath: string = `${__dirname}/complexConfigFile/plugins.schema.json`; + + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath: projectRelativeFilePath, jsonSchemaPath: schemaPath, jsonPathMetadata: { @@ -541,19 +1096,26 @@ describe(ConfigurationFile.name, () => { 'node_modules', '@rushstack', 'node-core-library', - 'lib', + 'lib-commonjs', 'index.js' ) ) }, { plugin: await FileSystem.getRealPathAsync( - nodeJsPath.resolve(projectRoot, 'node_modules', '@rushstack', 'heft', 'lib', 'index.js') + nodeJsPath.resolve( + projectRoot, + 'node_modules', + '@rushstack', + 'heft', + 'lib-commonjs', + 'index.js' + ) ) }, { plugin: await FileSystem.getRealPathAsync( - nodeJsPath.resolve(projectRoot, 'node_modules', '@rushstack', 'eslint-config', 'index.js') + nodeJsPath.resolve(projectRoot, 'node_modules', 'jsonpath-plus', 'dist', 'index-node-cjs.cjs') ) } ] @@ -578,64 +1140,177 @@ describe(ConfigurationFile.name, () => { parentObject: loadedConfigFile.plugins[2], propertyName: 'plugin' }) - ).toEqual('@rushstack/eslint-config'); + ).toEqual('jsonpath-plus'); + + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.plugins[0])).toEqual( + rootConfigFilePath + ); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.plugins[1])).toEqual( + nodeJsPath.resolve(__dirname, secondConfigFilePath) + ); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.plugins[2])).toEqual( + nodeJsPath.resolve(__dirname, secondConfigFilePath) + ); + }); + + it('Can get the original $schema property value', async () => { + async function testForFilename(filename: string, expectedSchema: string): Promise { + const projectRelativeFilePath: string = `complexConfigFile/${filename}`; + const jsonSchemaPath: string = nodeJsPath.resolve( + __dirname, + 'complexConfigFile', + 'plugins.schema.json' + ); + + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath, + jsonSchemaPath + }); + const loadedConfigFile: IComplexConfigFile = + await configFileLoader.loadConfigurationFileForProjectAsync(terminal, __dirname); + expect(configFileLoader.getSchemaPropertyOriginalValue(loadedConfigFile)).toEqual(expectedSchema); + } + + await testForFilename('pluginsA.json', 'http://schema.net/A'); + await testForFilename('pluginsB.json', 'http://schema.net/B'); + await testForFilename('pluginsC.json', 'http://schema.net/C'); + await testForFilename('pluginsD.json', 'http://schema.net/D'); + }); + }); + + describe('a complex file with inheritance type annotations', () => { + interface IInheritanceTypeConfigFile { + a: string; + b: { c: string }[]; + d: { + e: string; + f: string; + g: { h: string }[]; + i: { j: string }[]; + k: { + l: string; + m: { n: string }[]; + z?: string; + }; + o: { + p: { q: string }[]; + }; + r: { + s: string; + }; + y?: { + z: string; + }; + }; + y?: { + z: string; + }; + } + + interface ISimpleInheritanceTypeConfigFile { + a: { b: string }[]; + c: { + d: { e: string }[]; + }; + f: { + g: { h: string }[]; + i: { + j: { k: string }[]; + }; + }; + l: string; + } + + it('Correctly loads a complex config file with inheritance type annotations', () => { + const projectRelativeFilePath: string = 'inheritanceTypeConfigFile/inheritanceTypeConfigFileB.json'; + const rootConfigFilePath: string = nodeJsPath.resolve( + __dirname, + 'inheritanceTypeConfigFile', + 'inheritanceTypeConfigFileA.json' + ); + const secondConfigFilePath: string = nodeJsPath.resolve( + __dirname, + 'inheritanceTypeConfigFile', + 'inheritanceTypeConfigFileB.json' + ); + const schemaPath: string = `${__dirname}/inheritanceTypeConfigFile/inheritanceTypeConfigFile.schema.json`; + + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath: projectRelativeFilePath, + jsonSchemaPath: schemaPath + }); + const loadedConfigFile: IInheritanceTypeConfigFile = configFileLoader.loadConfigurationFileForProject( + terminal, + __dirname + ); + const expectedConfigFile: IInheritanceTypeConfigFile = { + a: 'A', + // "$b.inheritanceType": "append" + b: [{ c: 'A' }, { c: 'B' }], + // "$d.inheritanceType": "merge" + d: { + e: 'A', + f: 'B', + // "$g.inheritanceType": "append" + g: [{ h: 'A' }, { h: 'B' }], + // "$i.inheritanceType": "replace" + i: [{ j: 'B' }], + // "$k.inheritanceType": "merge" + k: { + l: 'A', + m: [{ n: 'A' }, { n: 'B' }], + z: 'B' + }, + // "$o.inheritanceType": "replace" + o: { + p: [{ q: 'B' }] + }, + r: { + s: 'A' + }, + y: { + z: 'B' + } + }, + y: { + z: 'B' + } + }; + + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.b[0])).toEqual(rootConfigFilePath); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.b[1])).toEqual(secondConfigFilePath); - expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.plugins[0])).toEqual( - rootConfigFilePath - ); - expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.plugins[1])).toEqual( - nodeJsPath.resolve(__dirname, secondConfigFilePath) + // loadedConfigFile.d source path is the second config file since it was merged into the first + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.d)).toEqual(secondConfigFilePath); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.d.g[0])).toEqual(rootConfigFilePath); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.d.g[1])).toEqual(secondConfigFilePath); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.d.i[0])).toEqual(secondConfigFilePath); + + // loadedConfigFile.d.k source path is the second config file since it was merged into the first + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.d.k)).toEqual(secondConfigFilePath); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.d.k.m[0])).toEqual(rootConfigFilePath); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.d.k.m[1])).toEqual( + secondConfigFilePath ); - expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.plugins[2])).toEqual( - nodeJsPath.resolve(__dirname, secondConfigFilePath) + + // loadedConfigFile.d.o source path is the second config file since it replaced the first + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.d.o)).toEqual(secondConfigFilePath); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.d.o.p[0])).toEqual( + secondConfigFilePath ); - }); - }); - describe('a complex file with inheritance type annotations', () => { - interface IInheritanceTypeConfigFile { - a: string; - b: { c: string }[]; - d: { - e: string; - f: string; - g: { h: string }[]; - i: { j: string }[]; - k: { - l: string; - m: { n: string }[]; - z?: string; - }; - o: { - p: { q: string }[]; - }; - r: { - s: string; - }; - y?: { - z: string; - }; - }; - y?: { - z: string; - }; - } + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.d.r)).toEqual(rootConfigFilePath); - interface ISimpleInheritanceTypeConfigFile { - a: { b: string }[]; - c: { - d: { e: string }[]; - }; - f: { - g: { h: string }[]; - i: { - j: { k: string }[]; - }; - }; - l: string; - } + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.d.y!)).toEqual(secondConfigFilePath); + + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile.y!)).toEqual(secondConfigFilePath); + }); - it('Correctly loads a complex config file with inheritance type annotations', async () => { + it('Correctly loads a complex config file with inheritance type annotations async', async () => { const projectRelativeFilePath: string = 'inheritanceTypeConfigFile/inheritanceTypeConfigFileB.json'; const rootConfigFilePath: string = nodeJsPath.resolve( __dirname, @@ -647,14 +1322,10 @@ describe(ConfigurationFile.name, () => { 'inheritanceTypeConfigFile', 'inheritanceTypeConfigFileB.json' ); - const schemaPath: string = nodeJsPath.resolve( - __dirname, - 'inheritanceTypeConfigFile', - 'inheritanceTypeConfigFile.schema.json' - ); + const schemaPath: string = `${__dirname}/inheritanceTypeConfigFile/inheritanceTypeConfigFile.schema.json`; - const configFileLoader: ConfigurationFile = - new ConfigurationFile({ + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath: projectRelativeFilePath, jsonSchemaPath: schemaPath }); @@ -738,14 +1409,10 @@ describe(ConfigurationFile.name, () => { 'simpleInheritanceTypeConfigFile', 'simpleInheritanceTypeConfigFileB.json' ); - const schemaPath: string = nodeJsPath.resolve( - __dirname, - 'simpleInheritanceTypeConfigFile', - 'simpleInheritanceTypeConfigFile.schema.json' - ); + const schemaPath: string = `${__dirname}/simpleInheritanceTypeConfigFile/simpleInheritanceTypeConfigFile.schema.json`; - const configFileLoader: ConfigurationFile = - new ConfigurationFile({ + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath: projectRelativeFilePath, jsonSchemaPath: schemaPath }); @@ -784,13 +1451,21 @@ describe(ConfigurationFile.name, () => { ); }); - it("throws an error when an array uses the 'merge' inheritance type", async () => { - const schemaPath: string = nodeJsPath.resolve( - __dirname, - 'simpleInheritanceTypeConfigFile', - 'simpleInheritanceTypeConfigFile.schema.json' - ); - const configFileLoader: ConfigurationFile = new ConfigurationFile({ + it("throws an error when an array uses the 'merge' inheritance type", () => { + const schemaPath: string = `${__dirname}/simpleInheritanceTypeConfigFile/simpleInheritanceTypeConfigFile.schema.json`; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ + projectRelativeFilePath: 'simpleInheritanceTypeConfigFile/badInheritanceTypeConfigFileA.json', + jsonSchemaPath: schemaPath + }); + + expect(() => + configFileLoader.loadConfigurationFileForProject(terminal, __dirname) + ).toThrowErrorMatchingSnapshot(); + }); + + it("throws an error when an array uses the 'merge' inheritance type async", async () => { + const schemaPath: string = `${__dirname}/simpleInheritanceTypeConfigFile/simpleInheritanceTypeConfigFile.schema.json`; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ projectRelativeFilePath: 'simpleInheritanceTypeConfigFile/badInheritanceTypeConfigFileA.json', jsonSchemaPath: schemaPath }); @@ -800,13 +1475,21 @@ describe(ConfigurationFile.name, () => { ).rejects.toThrowErrorMatchingSnapshot(); }); - it("throws an error when a keyed object uses the 'append' inheritance type", async () => { - const schemaPath: string = nodeJsPath.resolve( - __dirname, - 'simpleInheritanceTypeConfigFile', - 'simpleInheritanceTypeConfigFile.schema.json' - ); - const configFileLoader: ConfigurationFile = new ConfigurationFile({ + it("throws an error when a keyed object uses the 'append' inheritance type", () => { + const schemaPath: string = `${__dirname}/simpleInheritanceTypeConfigFile/simpleInheritanceTypeConfigFile.schema.json`; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ + projectRelativeFilePath: 'simpleInheritanceTypeConfigFile/badInheritanceTypeConfigFileB.json', + jsonSchemaPath: schemaPath + }); + + expect(() => + configFileLoader.loadConfigurationFileForProject(terminal, __dirname) + ).toThrowErrorMatchingSnapshot(); + }); + + it("throws an error when a keyed object uses the 'append' inheritance type async", async () => { + const schemaPath: string = `${__dirname}/simpleInheritanceTypeConfigFile/simpleInheritanceTypeConfigFile.schema.json`; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ projectRelativeFilePath: 'simpleInheritanceTypeConfigFile/badInheritanceTypeConfigFileB.json', jsonSchemaPath: schemaPath }); @@ -816,13 +1499,21 @@ describe(ConfigurationFile.name, () => { ).rejects.toThrowErrorMatchingSnapshot(); }); - it('throws an error when a non-object property uses an inheritance type', async () => { - const schemaPath: string = nodeJsPath.resolve( - __dirname, - 'simpleInheritanceTypeConfigFile', - 'simpleInheritanceTypeConfigFile.schema.json' - ); - const configFileLoader: ConfigurationFile = new ConfigurationFile({ + it('throws an error when a non-object property uses an inheritance type', () => { + const schemaPath: string = `${__dirname}/simpleInheritanceTypeConfigFile/simpleInheritanceTypeConfigFile.schema.json`; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ + projectRelativeFilePath: 'simpleInheritanceTypeConfigFile/badInheritanceTypeConfigFileC.json', + jsonSchemaPath: schemaPath + }); + + expect(() => + configFileLoader.loadConfigurationFileForProject(terminal, __dirname) + ).toThrowErrorMatchingSnapshot(); + }); + + it('throws an error when a non-object property uses an inheritance type async', async () => { + const schemaPath: string = `${__dirname}/simpleInheritanceTypeConfigFile/simpleInheritanceTypeConfigFile.schema.json`; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ projectRelativeFilePath: 'simpleInheritanceTypeConfigFile/badInheritanceTypeConfigFileC.json', jsonSchemaPath: schemaPath }); @@ -832,13 +1523,21 @@ describe(ConfigurationFile.name, () => { ).rejects.toThrowErrorMatchingSnapshot(); }); - it('throws an error when an inheritance type is specified for an unspecified property', async () => { - const schemaPath: string = nodeJsPath.resolve( - __dirname, - 'simpleInheritanceTypeConfigFile', - 'simpleInheritanceTypeConfigFile.schema.json' - ); - const configFileLoader: ConfigurationFile = new ConfigurationFile({ + it('throws an error when an inheritance type is specified for an unspecified property', () => { + const schemaPath: string = `${__dirname}/simpleInheritanceTypeConfigFile/simpleInheritanceTypeConfigFile.schema.json`; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ + projectRelativeFilePath: 'simpleInheritanceTypeConfigFile/badInheritanceTypeConfigFileD.json', + jsonSchemaPath: schemaPath + }); + + expect(() => + configFileLoader.loadConfigurationFileForProject(terminal, __dirname) + ).toThrowErrorMatchingSnapshot(); + }); + + it('throws an error when an inheritance type is specified for an unspecified property async', async () => { + const schemaPath: string = `${__dirname}/simpleInheritanceTypeConfigFile/simpleInheritanceTypeConfigFile.schema.json`; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ projectRelativeFilePath: 'simpleInheritanceTypeConfigFile/badInheritanceTypeConfigFileD.json', jsonSchemaPath: schemaPath }); @@ -848,13 +1547,21 @@ describe(ConfigurationFile.name, () => { ).rejects.toThrowErrorMatchingSnapshot(); }); - it('throws an error when an unsupported inheritance type is specified', async () => { - const schemaPath: string = nodeJsPath.resolve( - __dirname, - 'simpleInheritanceTypeConfigFile', - 'simpleInheritanceTypeConfigFile.schema.json' - ); - const configFileLoader: ConfigurationFile = new ConfigurationFile({ + it('throws an error when an unsupported inheritance type is specified', () => { + const schemaPath: string = `${__dirname}/simpleInheritanceTypeConfigFile/simpleInheritanceTypeConfigFile.schema.json`; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ + projectRelativeFilePath: 'simpleInheritanceTypeConfigFile/badInheritanceTypeConfigFileE.json', + jsonSchemaPath: schemaPath + }); + + expect(() => + configFileLoader.loadConfigurationFileForProject(terminal, __dirname) + ).toThrowErrorMatchingSnapshot(); + }); + + it('throws an error when an unsupported inheritance type is specified async', async () => { + const schemaPath: string = `${__dirname}/simpleInheritanceTypeConfigFile/simpleInheritanceTypeConfigFile.schema.json`; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ projectRelativeFilePath: 'simpleInheritanceTypeConfigFile/badInheritanceTypeConfigFileE.json', jsonSchemaPath: schemaPath }); @@ -866,23 +1573,49 @@ describe(ConfigurationFile.name, () => { }); describe('loading a rig', () => { - const projectFolder: string = nodeJsPath.resolve(__dirname, 'project-referencing-rig'); + const projectFolder: string = `${__dirname}/project-referencing-rig`; const rigConfig: RigConfig = RigConfig.loadForProjectFolder({ projectFolderPath: projectFolder }); - const schemaPath: string = nodeJsPath.resolve( - __dirname, - 'simplestConfigFile', - 'simplestConfigFile.schema.json' - ); + const schemaPath: string = `${__dirname}/simplestConfigFile/simplestConfigFile.schema.json`; interface ISimplestConfigFile { thing: string; } - it('correctly loads a config file inside a rig', async () => { + it('correctly loads a config file inside a rig', () => { + const projectRelativeFilePath: string = 'config/simplestConfigFile.json'; + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath: projectRelativeFilePath, + jsonSchemaPath: schemaPath + }); + const loadedConfigFile: ISimplestConfigFile = configFileLoader.loadConfigurationFileForProject( + terminal, + projectFolder, + rigConfig + ); + const expectedConfigFile: ISimplestConfigFile = { thing: 'A' }; + + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile)).toEqual( + nodeJsPath.resolve( + projectFolder, + 'node_modules', + 'test-rig', + 'profiles', + 'default', + projectRelativeFilePath + ) + ); + expect( + configFileLoader.getPropertyOriginalValue({ parentObject: loadedConfigFile, propertyName: 'thing' }) + ).toEqual('A'); + }); + + it('correctly loads a config file inside a rig async', async () => { const projectRelativeFilePath: string = 'config/simplestConfigFile.json'; - const configFileLoader: ConfigurationFile = - new ConfigurationFile({ + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath: projectRelativeFilePath, jsonSchemaPath: schemaPath }); @@ -906,10 +1639,38 @@ describe(ConfigurationFile.name, () => { ).toEqual('A'); }); + it('correctly loads a config file inside a rig via tryLoadConfigurationFileForProject', () => { + const projectRelativeFilePath: string = 'config/simplestConfigFile.json'; + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ + projectRelativeFilePath: projectRelativeFilePath, + jsonSchemaPath: schemaPath + }); + const loadedConfigFile: ISimplestConfigFile | undefined = + configFileLoader.tryLoadConfigurationFileForProject(terminal, projectFolder, rigConfig); + const expectedConfigFile: ISimplestConfigFile = { thing: 'A' }; + + expect(loadedConfigFile).not.toBeUndefined(); + expect(JSON.stringify(loadedConfigFile)).toEqual(JSON.stringify(expectedConfigFile)); + expect(configFileLoader.getObjectSourceFilePath(loadedConfigFile!)).toEqual( + nodeJsPath.resolve( + projectFolder, + 'node_modules', + 'test-rig', + 'profiles', + 'default', + projectRelativeFilePath + ) + ); + expect( + configFileLoader.getPropertyOriginalValue({ parentObject: loadedConfigFile!, propertyName: 'thing' }) + ).toEqual('A'); + }); + it('correctly loads a config file inside a rig via tryLoadConfigurationFileForProjectAsync', async () => { const projectRelativeFilePath: string = 'config/simplestConfigFile.json'; - const configFileLoader: ConfigurationFile = - new ConfigurationFile({ + const configFileLoader: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath: projectRelativeFilePath, jsonSchemaPath: schemaPath }); @@ -934,8 +1695,19 @@ describe(ConfigurationFile.name, () => { ).toEqual('A'); }); - it("throws an error when a config file doesn't exist in a project referencing a rig, which also doesn't have the file", async () => { - const configFileLoader: ConfigurationFile = new ConfigurationFile({ + it("throws an error when a config file doesn't exist in a project referencing a rig, which also doesn't have the file", () => { + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ + projectRelativeFilePath: 'config/notExist.json', + jsonSchemaPath: schemaPath + }); + + expect(() => + configFileLoader.loadConfigurationFileForProject(terminal, projectFolder, rigConfig) + ).toThrowErrorMatchingSnapshot(); + }); + + it("throws an error when a config file doesn't exist in a project referencing a rig, which also doesn't have the file async", async () => { + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ projectRelativeFilePath: 'config/notExist.json', jsonSchemaPath: schemaPath }); @@ -949,16 +1721,23 @@ describe(ConfigurationFile.name, () => { describe('error cases', () => { const errorCasesFolderName: string = 'errorCases'; - it("throws an error when the file doesn't exist", async () => { + it("throws an error when the file doesn't exist", () => { const errorCaseFolderName: string = 'invalidType'; - const configFileLoader: ConfigurationFile = new ConfigurationFile({ + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ projectRelativeFilePath: `${errorCasesFolderName}/${errorCaseFolderName}/notExist.json`, - jsonSchemaPath: nodeJsPath.resolve( - __dirname, - errorCasesFolderName, - errorCaseFolderName, - 'config.schema.json' - ) + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/${errorCaseFolderName}/config.schema.json` + }); + + expect(() => + configFileLoader.loadConfigurationFileForProject(terminal, __dirname) + ).toThrowErrorMatchingSnapshot(); + }); + + it("throws an error when the file doesn't exist async", async () => { + const errorCaseFolderName: string = 'invalidType'; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ + projectRelativeFilePath: `${errorCasesFolderName}/${errorCaseFolderName}/notExist.json`, + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/${errorCaseFolderName}/config.schema.json` }); await expect( @@ -966,16 +1745,21 @@ describe(ConfigurationFile.name, () => { ).rejects.toThrowErrorMatchingSnapshot(); }); + it("returns undefined when the file doesn't exist for tryLoadConfigurationFileForProject", () => { + const errorCaseFolderName: string = 'invalidType'; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ + projectRelativeFilePath: `${errorCasesFolderName}/${errorCaseFolderName}/notExist.json`, + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/${errorCaseFolderName}/config.schema.json` + }); + + expect(configFileLoader.tryLoadConfigurationFileForProject(terminal, __dirname)).toBeUndefined(); + }); + it("returns undefined when the file doesn't exist for tryLoadConfigurationFileForProjectAsync", async () => { const errorCaseFolderName: string = 'invalidType'; - const configFileLoader: ConfigurationFile = new ConfigurationFile({ + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ projectRelativeFilePath: `${errorCasesFolderName}/${errorCaseFolderName}/notExist.json`, - jsonSchemaPath: nodeJsPath.resolve( - __dirname, - errorCasesFolderName, - errorCaseFolderName, - 'config.schema.json' - ) + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/${errorCaseFolderName}/config.schema.json` }); await expect( @@ -983,7 +1767,36 @@ describe(ConfigurationFile.name, () => { ).resolves.toBeUndefined(); }); - it("Throws an error when the file isn't valid JSON", async () => { + it("Throws an error when the file isn't valid JSON", () => { + const errorCaseFolderName: string = 'invalidJson'; + const configFilePath: string = `${errorCasesFolderName}/${errorCaseFolderName}/config.json`; + const fullConfigFilePath: string = `${__dirname}/${configFilePath}`; + // Normalize newlines to make the error message consistent across platforms + const normalizedRawConfigFile: string = Text.convertToLf(FileSystem.readFile(fullConfigFilePath)); + jest + .spyOn(FileSystem, 'readFileAsync') + .mockImplementation((filePath: string) => + Path.convertToSlashes(filePath) === Path.convertToSlashes(fullConfigFilePath) + ? Promise.resolve(normalizedRawConfigFile) + : Promise.reject(new Error('File not found')) + ); + + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ + projectRelativeFilePath: configFilePath, + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/${errorCaseFolderName}/config.schema.json` + }); + + // The synchronous code path on Windows somehow determines that the unexpected character is + // a newline on Windows, and a curly brace on other platforms, even though the location is + // accurate in both cases. Use a regex to match either. + expect(() => configFileLoader.loadConfigurationFileForProject(terminal, __dirname)).toThrow( + /In configuration file "\/lib-commonjs\/test\/errorCases\/invalidJson\/config.json": SyntaxError: Unexpected token '(}|\\n)' at 2:19/ + ); + + jest.restoreAllMocks(); + }); + + it("Throws an error when the file isn't valid JSON async", async () => { const errorCaseFolderName: string = 'invalidJson'; const configFilePath: string = `${errorCasesFolderName}/${errorCaseFolderName}/config.json`; const fullConfigFilePath: string = `${__dirname}/${configFilePath}`; @@ -999,33 +1812,37 @@ describe(ConfigurationFile.name, () => { : Promise.reject(new Error('File not found')) ); - const configFileLoader: ConfigurationFile = new ConfigurationFile({ + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ projectRelativeFilePath: configFilePath, - jsonSchemaPath: nodeJsPath.resolve( - __dirname, - errorCasesFolderName, - errorCaseFolderName, - 'config.schema.json' - ) + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/${errorCaseFolderName}/config.schema.json` }); await expect( configFileLoader.loadConfigurationFileForProjectAsync(terminal, __dirname) - ).rejects.toThrowErrorMatchingSnapshot(); + ).rejects.toThrow( + /In configuration file "\/lib-commonjs\/test\/errorCases\/invalidJson\/config.json": SyntaxError: Unexpected token '(}|\\n)' at 2:19/ + ); jest.restoreAllMocks(); }); - it("Throws an error for a file that doesn't match its schema", async () => { + it("Throws an error for a file that doesn't match its schema", () => { const errorCaseFolderName: string = 'invalidType'; - const configFileLoader: ConfigurationFile = new ConfigurationFile({ + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ projectRelativeFilePath: `${errorCasesFolderName}/${errorCaseFolderName}/config.json`, - jsonSchemaPath: nodeJsPath.resolve( - __dirname, - errorCasesFolderName, - errorCaseFolderName, - 'config.schema.json' - ) + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/${errorCaseFolderName}/config.schema.json` + }); + + expect(() => + configFileLoader.loadConfigurationFileForProject(terminal, __dirname) + ).toThrowErrorMatchingSnapshot(); + }); + + it("Throws an error for a file that doesn't match its schema async", async () => { + const errorCaseFolderName: string = 'invalidType'; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ + projectRelativeFilePath: `${errorCasesFolderName}/${errorCaseFolderName}/config.json`, + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/${errorCaseFolderName}/config.schema.json` }); await expect( @@ -1033,16 +1850,23 @@ describe(ConfigurationFile.name, () => { ).rejects.toThrowErrorMatchingSnapshot(); }); - it('Throws an error when there is a circular reference in "extends" properties', async () => { + it('Throws an error when there is a circular reference in "extends" properties', () => { const errorCaseFolderName: string = 'circularReference'; - const configFileLoader: ConfigurationFile = new ConfigurationFile({ + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ projectRelativeFilePath: `${errorCasesFolderName}/${errorCaseFolderName}/config1.json`, - jsonSchemaPath: nodeJsPath.resolve( - __dirname, - errorCasesFolderName, - errorCaseFolderName, - 'config.schema.json' - ) + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/${errorCaseFolderName}/config.schema.json` + }); + + expect(() => + configFileLoader.loadConfigurationFileForProject(terminal, __dirname) + ).toThrowErrorMatchingSnapshot(); + }); + + it('Throws an error when there is a circular reference in "extends" properties async', async () => { + const errorCaseFolderName: string = 'circularReference'; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ + projectRelativeFilePath: `${errorCasesFolderName}/${errorCaseFolderName}/config1.json`, + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/${errorCaseFolderName}/config.schema.json` }); await expect( @@ -1050,16 +1874,23 @@ describe(ConfigurationFile.name, () => { ).rejects.toThrowErrorMatchingSnapshot(); }); - it('Throws an error when an "extends" property points to a file that cannot be resolved', async () => { + it('Throws an error when an "extends" property points to a file that cannot be resolved', () => { const errorCaseFolderName: string = 'extendsNotExist'; - const configFileLoader: ConfigurationFile = new ConfigurationFile({ + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ projectRelativeFilePath: `${errorCasesFolderName}/${errorCaseFolderName}/config.json`, - jsonSchemaPath: nodeJsPath.resolve( - __dirname, - errorCasesFolderName, - errorCaseFolderName, - 'config.schema.json' - ) + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/${errorCaseFolderName}/config.schema.json` + }); + + expect(() => + configFileLoader.loadConfigurationFileForProject(terminal, __dirname) + ).toThrowErrorMatchingSnapshot(); + }); + + it('Throws an error when an "extends" property points to a file that cannot be resolved async', async () => { + const errorCaseFolderName: string = 'extendsNotExist'; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ + projectRelativeFilePath: `${errorCasesFolderName}/${errorCaseFolderName}/config.json`, + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/${errorCaseFolderName}/config.schema.json` }); await expect( @@ -1067,16 +1898,23 @@ describe(ConfigurationFile.name, () => { ).rejects.toThrowErrorMatchingSnapshot(); }); - it("Throws an error when a combined config file doesn't match the schema", async () => { + it("Throws an error when a combined config file doesn't match the schema", () => { const errorCaseFolderName: string = 'invalidCombinedFile'; - const configFileLoader: ConfigurationFile = new ConfigurationFile({ + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ projectRelativeFilePath: `${errorCasesFolderName}/${errorCaseFolderName}/config1.json`, - jsonSchemaPath: nodeJsPath.resolve( - __dirname, - errorCasesFolderName, - errorCaseFolderName, - 'config.schema.json' - ) + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/${errorCaseFolderName}/config.schema.json` + }); + + expect(() => + configFileLoader.loadConfigurationFileForProject(terminal, __dirname) + ).toThrowErrorMatchingSnapshot(); + }); + + it("Throws an error when a combined config file doesn't match the schema async", async () => { + const errorCaseFolderName: string = 'invalidCombinedFile'; + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ + projectRelativeFilePath: `${errorCasesFolderName}/${errorCaseFolderName}/config1.json`, + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/${errorCaseFolderName}/config.schema.json` }); await expect( @@ -1084,15 +1922,21 @@ describe(ConfigurationFile.name, () => { ).rejects.toThrowErrorMatchingSnapshot(); }); - it("Throws an error when a requested file doesn't exist", async () => { - const configFileLoader: ConfigurationFile = new ConfigurationFile({ + it("Throws an error when a requested file doesn't exist", () => { + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ projectRelativeFilePath: `${errorCasesFolderName}/folderThatDoesntExist/config.json`, - jsonSchemaPath: nodeJsPath.resolve( - __dirname, - errorCasesFolderName, - 'invalidCombinedFile', - 'config.schema.json' - ) + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/invalidCombinedFile/config.schema.json` + }); + + expect(() => + configFileLoader.loadConfigurationFileForProject(terminal, __dirname) + ).toThrowErrorMatchingSnapshot(); + }); + + it("Throws an error when a requested file doesn't exist async", async () => { + const configFileLoader: ProjectConfigurationFile = new ProjectConfigurationFile({ + projectRelativeFilePath: `${errorCasesFolderName}/folderThatDoesntExist/config.json`, + jsonSchemaPath: `${__dirname}/${errorCasesFolderName}/invalidCombinedFile/config.schema.json` }); await expect( diff --git a/libraries/heft-config-file/src/test/__snapshots__/ConfigurationFile.test.ts.snap b/libraries/heft-config-file/src/test/__snapshots__/ConfigurationFile.test.ts.snap index 8bbe6a65e6d..f66eff9f917 100644 --- a/libraries/heft-config-file/src/test/__snapshots__/ConfigurationFile.test.ts.snap +++ b/libraries/heft-config-file/src/test/__snapshots__/ConfigurationFile.test.ts.snap @@ -1,397 +1,289 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ConfigurationFile A complex config file Correctly loads a complex config file (Deprecated PathResolutionMethod.NodeResolve) 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`ConfigurationFile A complex config file Correctly loads a complex config file 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A complex config file Can get the original $schema property value 1`] = `Array []`; -exports[`ConfigurationFile A simple config file containing an array and an object Correctly loads the config file 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A complex config file Correctly loads a complex config file (Deprecated PathResolutionMethod.NodeResolve) 1`] = `Array []`; -exports[`ConfigurationFile A simple config file containing an array and an object Correctly resolves paths relative to the config file 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A complex config file Correctly loads a complex config file 1`] = `Array []`; -exports[`ConfigurationFile A simple config file containing an array and an object Correctly resolves paths relative to the project root 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A complex config file Correctly loads a complex config file async (Deprecated PathResolutionMethod.NodeResolve) 1`] = `Array []`; -exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with "append" and "merge" in config meta 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A complex config file Correctly loads a complex config file async 1`] = `Array []`; -exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with "custom" in config meta 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A simple config file containing an array and an object Correctly loads the config file 1`] = `Array []`; -exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with "replace" in config meta 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A simple config file containing an array and an object Correctly loads the config file async 1`] = `Array []`; -exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with default config meta 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A simple config file containing an array and an object Correctly resolves paths relative to the config file 1`] = `Array []`; -exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with modified merge behaviors for arrays and objects 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A simple config file containing an array and an object Correctly resolves paths relative to the config file async 1`] = `Array []`; -exports[`ConfigurationFile A simple config file with "extends" Correctly resolves paths relative to the config file 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A simple config file containing an array and an object Correctly resolves paths relative to the project root 1`] = `Array []`; -exports[`ConfigurationFile A simple config file with a JSON schema object Correctly loads the config file 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A simple config file containing an array and an object Correctly resolves paths relative to the project root async 1`] = `Array []`; -exports[`ConfigurationFile A simple config file with a JSON schema object Correctly resolves paths relative to the config file 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with "append" and "merge" in config meta 1`] = `Array []`; -exports[`ConfigurationFile A simple config file with a JSON schema object Correctly resolves paths relative to the project root 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with "append" and "merge" in config meta async 1`] = `Array []`; -exports[`ConfigurationFile A simple config file with a JSON schema path Correctly loads the config file 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with "custom" in config meta 1`] = `Array []`; -exports[`ConfigurationFile A simple config file with a JSON schema path Correctly resolves paths relative to the config file 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with "custom" in config meta async 1`] = `Array []`; -exports[`ConfigurationFile A simple config file with a JSON schema path Correctly resolves paths relative to the project root 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with "replace" in config meta 1`] = `Array []`; -exports[`ConfigurationFile a complex file with inheritance type annotations Correctly loads a complex config file with a single inheritance type annotation 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with "replace" in config meta async 1`] = `Array []`; -exports[`ConfigurationFile a complex file with inheritance type annotations Correctly loads a complex config file with inheritance type annotations 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with default config meta 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with default config meta async 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with modified merge behaviors for arrays and objects 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with "extends" Correctly loads the config file with modified merge behaviors for arrays and objects async 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with "extends" Correctly resolves paths relative to the config file 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with "extends" Correctly resolves paths relative to the config file async 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema object Correctly loads the config file 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema object Correctly loads the config file async 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema object Correctly resolves paths relative to the config file 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema object Correctly resolves paths relative to the config file async 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema object Correctly resolves paths relative to the project root 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema object Correctly resolves paths relative to the project root async 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema object The NonProjectConfigurationFile version works correctly 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema object The NonProjectConfigurationFile version works correctly async 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema path Correctly loads the config file 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema path Correctly loads the config file async 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema path Correctly resolves paths relative to the config file 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema path Correctly resolves paths relative to the config file async 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema path Correctly resolves paths relative to the project root 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema path Correctly resolves paths relative to the project root async 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema path The NonProjectConfigurationFile version works correctly 1`] = `Array []`; + +exports[`ConfigurationFile A simple config file with a JSON schema path The NonProjectConfigurationFile version works correctly async 1`] = `Array []`; + +exports[`ConfigurationFile a complex file with inheritance type annotations Correctly loads a complex config file with a single inheritance type annotation 1`] = `Array []`; + +exports[`ConfigurationFile a complex file with inheritance type annotations Correctly loads a complex config file with inheritance type annotations 1`] = `Array []`; + +exports[`ConfigurationFile a complex file with inheritance type annotations Correctly loads a complex config file with inheritance type annotations async 1`] = `Array []`; exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when a keyed object uses the 'append' inheritance type 1`] = `"Issue in processing configuration file property \\"c\\". Property is not an array, but the inheritance type is set as \\"append\\""`; -exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when a keyed object uses the 'append' inheritance type 2`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when a keyed object uses the 'append' inheritance type 2`] = `Array []`; + +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when a keyed object uses the 'append' inheritance type async 1`] = `"Issue in processing configuration file property \\"c\\". Property is not an array, but the inheritance type is set as \\"append\\""`; + +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when a keyed object uses the 'append' inheritance type async 2`] = `Array []`; exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when a non-object property uses an inheritance type 1`] = `"Issue in processing configuration file property \\"$l.inheritanceType\\". An inheritance type was provided for a property that is not a keyed object or array."`; -exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when a non-object property uses an inheritance type 2`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when a non-object property uses an inheritance type 2`] = `Array []`; + +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when a non-object property uses an inheritance type async 1`] = `"Issue in processing configuration file property \\"$l.inheritanceType\\". An inheritance type was provided for a property that is not a keyed object or array."`; + +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when a non-object property uses an inheritance type async 2`] = `Array []`; exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an array uses the 'merge' inheritance type 1`] = `"Issue in processing configuration file property \\"a\\". Property is not a keyed object, but the inheritance type is set as \\"merge\\""`; -exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an array uses the 'merge' inheritance type 2`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an array uses the 'merge' inheritance type 2`] = `Array []`; + +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an array uses the 'merge' inheritance type async 1`] = `"Issue in processing configuration file property \\"a\\". Property is not a keyed object, but the inheritance type is set as \\"merge\\""`; + +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an array uses the 'merge' inheritance type async 2`] = `Array []`; exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an inheritance type is specified for an unspecified property 1`] = `"Issue in processing configuration file property \\"$c.inheritanceType\\". An inheritance type was provided but no matching property was found in the parent."`; -exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an inheritance type is specified for an unspecified property 2`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an inheritance type is specified for an unspecified property 2`] = `Array []`; + +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an inheritance type is specified for an unspecified property async 1`] = `"Issue in processing configuration file property \\"$c.inheritanceType\\". An inheritance type was provided but no matching property was found in the parent."`; + +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an inheritance type is specified for an unspecified property async 2`] = `Array []`; exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an unsupported inheritance type is specified 1`] = `"Issue in processing configuration file property \\"$a.inheritanceType\\". An unsupported inheritance type was provided: \\"custom\\""`; -exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an unsupported inheritance type is specified 2`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an unsupported inheritance type is specified 2`] = `Array []`; + +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an unsupported inheritance type is specified async 1`] = `"Issue in processing configuration file property \\"$a.inheritanceType\\". An unsupported inheritance type was provided: \\"custom\\""`; + +exports[`ConfigurationFile a complex file with inheritance type annotations throws an error when an unsupported inheritance type is specified async 2`] = `Array []`; exports[`ConfigurationFile error cases Throws an error for a file that doesn't match its schema 1`] = ` "Resolved configuration object does not match schema: Error: JSON validation failed: -/src/test/errorCases/invalidType/config.json +/lib-commonjs/test/errorCases/invalidType/config.json Error: #/filePaths - Expected type array but found type string" + must be array" `; -exports[`ConfigurationFile error cases Throws an error for a file that doesn't match its schema 2`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} +exports[`ConfigurationFile error cases Throws an error for a file that doesn't match its schema 2`] = `Array []`; + +exports[`ConfigurationFile error cases Throws an error for a file that doesn't match its schema async 1`] = ` +"Resolved configuration object does not match schema: Error: JSON validation failed: +/lib-commonjs/test/errorCases/invalidType/config.json + +Error: #/filePaths + must be array" `; +exports[`ConfigurationFile error cases Throws an error for a file that doesn't match its schema async 2`] = `Array []`; + exports[`ConfigurationFile error cases Throws an error when a combined config file doesn't match the schema 1`] = ` "Resolved configuration object does not match schema: Error: JSON validation failed: -/src/test/errorCases/invalidCombinedFile/config1.json - -Error: #/ - Data does not match any schemas from 'oneOf' - Error: #/ - Additional properties not allowed: folderPaths - Error: #/ - Additional properties not allowed: filePaths" +/lib-commonjs/test/errorCases/invalidCombinedFile/config1.json + +Error: # + must NOT have additional properties: folderPaths +Error: # + must NOT have additional properties: filePaths +Error: # + must match exactly one schema in oneOf" `; -exports[`ConfigurationFile error cases Throws an error when a combined config file doesn't match the schema 2`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} +exports[`ConfigurationFile error cases Throws an error when a combined config file doesn't match the schema 2`] = `Array []`; + +exports[`ConfigurationFile error cases Throws an error when a combined config file doesn't match the schema async 1`] = ` +"Resolved configuration object does not match schema: Error: JSON validation failed: +/lib-commonjs/test/errorCases/invalidCombinedFile/config1.json + +Error: # + must NOT have additional properties: folderPaths +Error: # + must NOT have additional properties: filePaths +Error: # + must match exactly one schema in oneOf" `; -exports[`ConfigurationFile error cases Throws an error when a requested file doesn't exist 1`] = `"File does not exist: /src/test/errorCases/folderThatDoesntExist/config.json"`; +exports[`ConfigurationFile error cases Throws an error when a combined config file doesn't match the schema async 2`] = `Array []`; + +exports[`ConfigurationFile error cases Throws an error when a requested file doesn't exist 1`] = `"File does not exist: /lib-commonjs/test/errorCases/folderThatDoesntExist/config.json"`; exports[`ConfigurationFile error cases Throws an error when a requested file doesn't exist 2`] = ` -Object { - "debug": "Configuration file \\"/src/test/errorCases/folderThatDoesntExist/config.json\\" not found.[n]", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} +Array [ + "[ debug] Configuration file \\"/lib-commonjs/test/errorCases/folderThatDoesntExist/config.json\\" not found.[n]", +] `; -exports[`ConfigurationFile error cases Throws an error when an "extends" property points to a file that cannot be resolved 1`] = `"In file \\"/src/test/errorCases/extendsNotExist/config.json\\", file referenced in \\"extends\\" property (\\"./config2.json\\") cannot be resolved."`; +exports[`ConfigurationFile error cases Throws an error when a requested file doesn't exist async 1`] = `"File does not exist: /lib-commonjs/test/errorCases/folderThatDoesntExist/config.json"`; -exports[`ConfigurationFile error cases Throws an error when an "extends" property points to a file that cannot be resolved 2`] = ` -Object { - "debug": "Configuration file \\"/src/test/errorCases/extendsNotExist/config2.json\\" not found.[n]", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} +exports[`ConfigurationFile error cases Throws an error when a requested file doesn't exist async 2`] = ` +Array [ + "[ debug] Configuration file \\"/lib-commonjs/test/errorCases/folderThatDoesntExist/config.json\\" not found.[n]", +] `; -exports[`ConfigurationFile error cases Throws an error when the file isn't valid JSON 1`] = ` -"In config file \\"/src/test/errorCases/invalidJson/config.json\\": SyntaxError: Unexpected token '}' at 2:19 - \\"filePaths\\": \\"A - ^" +exports[`ConfigurationFile error cases Throws an error when an "extends" property points to a file that cannot be resolved 1`] = `"In file \\"/lib-commonjs/test/errorCases/extendsNotExist/config.json\\", file referenced in \\"extends\\" property (\\"./config2.json\\") cannot be resolved."`; + +exports[`ConfigurationFile error cases Throws an error when an "extends" property points to a file that cannot be resolved 2`] = ` +Array [ + "[ debug] Configuration file \\"/lib-commonjs/test/errorCases/extendsNotExist/config2.json\\" not found.[n]", +] `; -exports[`ConfigurationFile error cases Throws an error when the file isn't valid JSON 2`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} +exports[`ConfigurationFile error cases Throws an error when an "extends" property points to a file that cannot be resolved async 1`] = `"In file \\"/lib-commonjs/test/errorCases/extendsNotExist/config.json\\", file referenced in \\"extends\\" property (\\"./config2.json\\") cannot be resolved."`; + +exports[`ConfigurationFile error cases Throws an error when an "extends" property points to a file that cannot be resolved async 2`] = ` +Array [ + "[ debug] Configuration file \\"/lib-commonjs/test/errorCases/extendsNotExist/config2.json\\" not found.[n]", +] `; -exports[`ConfigurationFile error cases Throws an error when there is a circular reference in "extends" properties 1`] = `"A loop has been detected in the \\"extends\\" properties of configuration file at \\"/src/test/errorCases/circularReference/config1.json\\"."`; +exports[`ConfigurationFile error cases Throws an error when the file isn't valid JSON 1`] = `Array []`; + +exports[`ConfigurationFile error cases Throws an error when the file isn't valid JSON async 1`] = `Array []`; + +exports[`ConfigurationFile error cases Throws an error when there is a circular reference in "extends" properties 1`] = `"A loop has been detected in the \\"extends\\" properties of configuration file at \\"/lib-commonjs/test/errorCases/circularReference/config1.json\\"."`; + +exports[`ConfigurationFile error cases Throws an error when there is a circular reference in "extends" properties 2`] = `Array []`; + +exports[`ConfigurationFile error cases Throws an error when there is a circular reference in "extends" properties async 1`] = `"A loop has been detected in the \\"extends\\" properties of configuration file at \\"/lib-commonjs/test/errorCases/circularReference/config1.json\\"."`; + +exports[`ConfigurationFile error cases Throws an error when there is a circular reference in "extends" properties async 2`] = `Array []`; -exports[`ConfigurationFile error cases Throws an error when there is a circular reference in "extends" properties 2`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} +exports[`ConfigurationFile error cases returns undefined when the file doesn't exist for tryLoadConfigurationFileForProject 1`] = ` +Array [ + "[ debug] Configuration file \\"/lib-commonjs/test/errorCases/invalidType/notExist.json\\" not found.[n]", +] `; exports[`ConfigurationFile error cases returns undefined when the file doesn't exist for tryLoadConfigurationFileForProjectAsync 1`] = ` -Object { - "debug": "Configuration file \\"/src/test/errorCases/invalidType/notExist.json\\" not found.[n]", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} +Array [ + "[ debug] Configuration file \\"/lib-commonjs/test/errorCases/invalidType/notExist.json\\" not found.[n]", +] `; -exports[`ConfigurationFile error cases throws an error when the file doesn't exist 1`] = `"File does not exist: /src/test/errorCases/invalidType/notExist.json"`; +exports[`ConfigurationFile error cases throws an error when the file doesn't exist 1`] = `"File does not exist: /lib-commonjs/test/errorCases/invalidType/notExist.json"`; exports[`ConfigurationFile error cases throws an error when the file doesn't exist 2`] = ` -Object { - "debug": "Configuration file \\"/src/test/errorCases/invalidType/notExist.json\\" not found.[n]", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} +Array [ + "[ debug] Configuration file \\"/lib-commonjs/test/errorCases/invalidType/notExist.json\\" not found.[n]", +] +`; + +exports[`ConfigurationFile error cases throws an error when the file doesn't exist async 1`] = `"File does not exist: /lib-commonjs/test/errorCases/invalidType/notExist.json"`; + +exports[`ConfigurationFile error cases throws an error when the file doesn't exist async 2`] = ` +Array [ + "[ debug] Configuration file \\"/lib-commonjs/test/errorCases/invalidType/notExist.json\\" not found.[n]", +] `; exports[`ConfigurationFile loading a rig correctly loads a config file inside a rig 1`] = ` -Object { - "debug": "Config file \\"/src/test/project-referencing-rig/config/simplestConfigFile.json\\" does not exist. Attempting to load via rig.[n]", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} +Array [ + "[ debug] Configuration file \\"/lib-commonjs/test/project-referencing-rig/config/simplestConfigFile.json\\" does not exist. Attempting to load via rig (\\"/lib-commonjs/test/project-referencing-rig/node_modules/test-rig/profiles/default\\").[n]", +] +`; + +exports[`ConfigurationFile loading a rig correctly loads a config file inside a rig async 1`] = ` +Array [ + "[ debug] Configuration file \\"/lib-commonjs/test/project-referencing-rig/config/simplestConfigFile.json\\" does not exist. Attempting to load via rig (\\"/lib-commonjs/test/project-referencing-rig/node_modules/test-rig/profiles/default\\").[n]", +] +`; + +exports[`ConfigurationFile loading a rig correctly loads a config file inside a rig via tryLoadConfigurationFileForProject 1`] = ` +Array [ + "[ debug] Configuration file \\"/lib-commonjs/test/project-referencing-rig/config/simplestConfigFile.json\\" does not exist. Attempting to load via rig (\\"/lib-commonjs/test/project-referencing-rig/node_modules/test-rig/profiles/default\\").[n]", +] `; exports[`ConfigurationFile loading a rig correctly loads a config file inside a rig via tryLoadConfigurationFileForProjectAsync 1`] = ` -Object { - "debug": "Config file \\"/src/test/project-referencing-rig/config/simplestConfigFile.json\\" does not exist. Attempting to load via rig.[n]", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} +Array [ + "[ debug] Configuration file \\"/lib-commonjs/test/project-referencing-rig/config/simplestConfigFile.json\\" does not exist. Attempting to load via rig (\\"/lib-commonjs/test/project-referencing-rig/node_modules/test-rig/profiles/default\\").[n]", +] `; -exports[`ConfigurationFile loading a rig throws an error when a config file doesn't exist in a project referencing a rig, which also doesn't have the file 1`] = `"File does not exist: /src/test/project-referencing-rig/config/notExist.json"`; +exports[`ConfigurationFile loading a rig throws an error when a config file doesn't exist in a project referencing a rig, which also doesn't have the file 1`] = `"File does not exist: /lib-commonjs/test/project-referencing-rig/config/notExist.json"`; exports[`ConfigurationFile loading a rig throws an error when a config file doesn't exist in a project referencing a rig, which also doesn't have the file 2`] = ` -Object { - "debug": "Config file \\"/src/test/project-referencing-rig/config/notExist.json\\" does not exist. Attempting to load via rig.[n]Configuration file \\"/src/test/project-referencing-rig/node_modules/test-rig/profiles/default/config/notExist.json\\" not found.[n]Configuration file \\"config/notExist.json\\" not found in rig (\\"/src/test/project-referencing-rig/node_modules/test-rig/profiles/default\\")[n]", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} +Array [ + "[ debug] Configuration file \\"/lib-commonjs/test/project-referencing-rig/config/notExist.json\\" does not exist. Attempting to load via rig (\\"/lib-commonjs/test/project-referencing-rig/node_modules/test-rig/profiles/default\\").[n]", + "[ debug] Configuration file \\"/lib-commonjs/test/project-referencing-rig/node_modules/test-rig/profiles/default/config/notExist.json\\" not found.[n]", + "[ debug] Configuration file \\"/lib-commonjs/test/project-referencing-rig/config/notExist.json\\" not found.[n]", +] +`; + +exports[`ConfigurationFile loading a rig throws an error when a config file doesn't exist in a project referencing a rig, which also doesn't have the file async 1`] = `"File does not exist: /lib-commonjs/test/project-referencing-rig/config/notExist.json"`; + +exports[`ConfigurationFile loading a rig throws an error when a config file doesn't exist in a project referencing a rig, which also doesn't have the file async 2`] = ` +Array [ + "[ debug] Configuration file \\"/lib-commonjs/test/project-referencing-rig/config/notExist.json\\" does not exist. Attempting to load via rig (\\"/lib-commonjs/test/project-referencing-rig/node_modules/test-rig/profiles/default\\").[n]", + "[ debug] Configuration file \\"/lib-commonjs/test/project-referencing-rig/node_modules/test-rig/profiles/default/config/notExist.json\\" not found.[n]", + "[ debug] Configuration file \\"/lib-commonjs/test/project-referencing-rig/config/notExist.json\\" not found.[n]", +] `; diff --git a/libraries/heft-config-file/src/test/complexConfigFile/pluginsA.json b/libraries/heft-config-file/src/test/complexConfigFile/pluginsA.json index c18a3595864..37a1be91f2e 100644 --- a/libraries/heft-config-file/src/test/complexConfigFile/pluginsA.json +++ b/libraries/heft-config-file/src/test/complexConfigFile/pluginsA.json @@ -1,5 +1,5 @@ { - "$schema": "http://schema.net/", + "$schema": "http://schema.net/A", "plugins": [ { diff --git a/libraries/heft-config-file/src/test/complexConfigFile/pluginsB.json b/libraries/heft-config-file/src/test/complexConfigFile/pluginsB.json index 37e7c7c4256..24cc40fa261 100644 --- a/libraries/heft-config-file/src/test/complexConfigFile/pluginsB.json +++ b/libraries/heft-config-file/src/test/complexConfigFile/pluginsB.json @@ -1,5 +1,5 @@ { - "$schema": "http://schema.net/", + "$schema": "http://schema.net/B", "extends": "./pluginsA.json", @@ -8,7 +8,7 @@ "plugin": "@rushstack/heft" }, { - "plugin": "@rushstack/eslint-config" + "plugin": "jsonpath-plus" } ] } diff --git a/libraries/heft-config-file/src/test/complexConfigFile/pluginsC.json b/libraries/heft-config-file/src/test/complexConfigFile/pluginsC.json index 1319215f110..9d24ea71f15 100644 --- a/libraries/heft-config-file/src/test/complexConfigFile/pluginsC.json +++ b/libraries/heft-config-file/src/test/complexConfigFile/pluginsC.json @@ -1,5 +1,5 @@ { - "$schema": "http://schema.net/", + "$schema": "http://schema.net/C", "extends": "./pluginsB.json" } diff --git a/libraries/heft-config-file/src/test/complexConfigFile/pluginsD.json b/libraries/heft-config-file/src/test/complexConfigFile/pluginsD.json index ae25f280294..08840447fef 100644 --- a/libraries/heft-config-file/src/test/complexConfigFile/pluginsD.json +++ b/libraries/heft-config-file/src/test/complexConfigFile/pluginsD.json @@ -1,5 +1,5 @@ { - "$schema": "http://schema.net/", + "$schema": "http://schema.net/D", "extends": "./pluginsC.json" } diff --git a/libraries/heft-config-file/src/test/simpleConfigFile/simpleConfigFile.json b/libraries/heft-config-file/src/test/simpleConfigFile/simpleConfigFile.json index 37dfdc3f199..4db0a32d630 100644 --- a/libraries/heft-config-file/src/test/simpleConfigFile/simpleConfigFile.json +++ b/libraries/heft-config-file/src/test/simpleConfigFile/simpleConfigFile.json @@ -9,5 +9,7 @@ "E": "F" } }, - "booleanProp": true + "booleanProp": true, + + "stringProp": "someValue" } diff --git a/libraries/heft-config-file/src/test/simpleConfigFile/simpleConfigFile.schema.json b/libraries/heft-config-file/src/test/simpleConfigFile/simpleConfigFile.schema.json index 3d118ed35a1..0c2c53a91b8 100644 --- a/libraries/heft-config-file/src/test/simpleConfigFile/simpleConfigFile.schema.json +++ b/libraries/heft-config-file/src/test/simpleConfigFile/simpleConfigFile.schema.json @@ -24,6 +24,10 @@ "booleanProp": { "type": "boolean" + }, + + "stringProp": { + "type": "string" } } } diff --git a/libraries/heft-config-file/src/test/simpleConfigFileWithExtends/simpleConfigFileWithExtends.json b/libraries/heft-config-file/src/test/simpleConfigFileWithExtends/simpleConfigFileWithExtends.json index ce8b0e3e2b4..5a4b4c5ce9e 100644 --- a/libraries/heft-config-file/src/test/simpleConfigFileWithExtends/simpleConfigFileWithExtends.json +++ b/libraries/heft-config-file/src/test/simpleConfigFileWithExtends/simpleConfigFileWithExtends.json @@ -13,5 +13,7 @@ } }, - "booleanProp": false + "booleanProp": false, + + "stringProp": null } diff --git a/libraries/heft-config-file/tsconfig.json b/libraries/heft-config-file/tsconfig.json index fbc2f5c0a6c..1a33d17b873 100644 --- a/libraries/heft-config-file/tsconfig.json +++ b/libraries/heft-config-file/tsconfig.json @@ -1,7 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["heft-jest", "node"] - } + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/libraries/load-themed-styles/.eslintrc.js b/libraries/load-themed-styles/.eslintrc.js deleted file mode 100644 index f612b415715..00000000000 --- a/libraries/load-themed-styles/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/web-app', '@rushstack/eslint-config/mixins/friendly-locals'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/libraries/load-themed-styles/.npmignore b/libraries/load-themed-styles/.npmignore index 302dbc5b019..f7a40e10213 100644 --- a/libraries/load-themed-styles/.npmignore +++ b/libraries/load-themed-styles/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,16 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/load-themed-styles/.vscode/tasks.json b/libraries/load-themed-styles/.vscode/tasks.json index 3567ff9ad0c..225802b60e0 100644 --- a/libraries/load-themed-styles/.vscode/tasks.json +++ b/libraries/load-themed-styles/.vscode/tasks.json @@ -1,23 +1,24 @@ { - "version": "0.1.0", + "version": "2.0.0", "command": "gulp", - "isShellCommand": true, "tasks": [ { - "taskName": "build", - - "echoCommand": true, - "args": [ - ], - "isBuildCommand": true, - "showOutput": "always", - "isWatching": true + "label": "build", + "type": "gulp", + "task": "build", + "isBackground": true, + "problemMatcher": [], + "group": { + "_id": "build", + "isDefault": false + } }, { - "taskName": "watch", - "isBuildCommand": false, - "showOutput": "always", - "isWatching": true + "label": "watch", + "type": "gulp", + "task": "watch", + "isBackground": true, + "problemMatcher": [] } ] } diff --git a/libraries/load-themed-styles/CHANGELOG.json b/libraries/load-themed-styles/CHANGELOG.json index 5a168cef02f..299b732bbc3 100644 --- a/libraries/load-themed-styles/CHANGELOG.json +++ b/libraries/load-themed-styles/CHANGELOG.json @@ -1,6 +1,2264 @@ { "name": "@microsoft/load-themed-styles", "entries": [ + { + "version": "2.2.22", + "tag": "@microsoft/load-themed-styles_v2.2.22", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "2.2.21", + "tag": "@microsoft/load-themed-styles_v2.2.21", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "2.2.20", + "tag": "@microsoft/load-themed-styles_v2.2.20", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "2.2.19", + "tag": "@microsoft/load-themed-styles_v2.2.19", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "2.2.18", + "tag": "@microsoft/load-themed-styles_v2.2.18", + "date": "Mon, 08 Jun 2026 15:15:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "2.2.17", + "tag": "@microsoft/load-themed-styles_v2.2.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "2.2.16", + "tag": "@microsoft/load-themed-styles_v2.2.16", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "2.2.15", + "tag": "@microsoft/load-themed-styles_v2.2.15", + "date": "Sat, 18 Apr 2026 03:47:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "2.2.14", + "tag": "@microsoft/load-themed-styles_v2.2.14", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "2.2.13", + "tag": "@microsoft/load-themed-styles_v2.2.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "2.2.12", + "tag": "@microsoft/load-themed-styles_v2.2.12", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "2.2.11", + "tag": "@microsoft/load-themed-styles_v2.2.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "2.2.10", + "tag": "@microsoft/load-themed-styles_v2.2.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "2.2.9", + "tag": "@microsoft/load-themed-styles_v2.2.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "2.2.8", + "tag": "@microsoft/load-themed-styles_v2.2.8", + "date": "Tue, 31 Mar 2026 15:14:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "2.2.7", + "tag": "@microsoft/load-themed-styles_v2.2.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "2.2.6", + "tag": "@microsoft/load-themed-styles_v2.2.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "patch": [ + { + "comment": "Include missing `lib-*` folders." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "2.2.5", + "tag": "@microsoft/load-themed-styles_v2.2.5", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "2.2.4", + "tag": "@microsoft/load-themed-styles_v2.2.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "2.2.3", + "tag": "@microsoft/load-themed-styles_v2.2.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "2.2.2", + "tag": "@microsoft/load-themed-styles_v2.2.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "2.2.1", + "tag": "@microsoft/load-themed-styles_v2.2.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "2.2.0", + "tag": "@microsoft/load-themed-styles_v2.2.0", + "date": "Thu, 19 Feb 2026 00:04:52 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "2.1.29", + "tag": "@microsoft/load-themed-styles_v2.1.29", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "2.1.28", + "tag": "@microsoft/load-themed-styles_v2.1.28", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "2.1.27", + "tag": "@microsoft/load-themed-styles_v2.1.27", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "2.1.26", + "tag": "@microsoft/load-themed-styles_v2.1.26", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "2.1.25", + "tag": "@microsoft/load-themed-styles_v2.1.25", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "2.1.24", + "tag": "@microsoft/load-themed-styles_v2.1.24", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "2.1.23", + "tag": "@microsoft/load-themed-styles_v2.1.23", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "2.1.22", + "tag": "@microsoft/load-themed-styles_v2.1.22", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + } + ] + } + }, + { + "version": "2.1.21", + "tag": "@microsoft/load-themed-styles_v2.1.21", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + } + ] + } + }, + { + "version": "2.1.20", + "tag": "@microsoft/load-themed-styles_v2.1.20", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + } + ] + } + }, + { + "version": "2.1.19", + "tag": "@microsoft/load-themed-styles_v2.1.19", + "date": "Tue, 04 Nov 2025 08:15:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + } + ] + } + }, + { + "version": "2.1.18", + "tag": "@microsoft/load-themed-styles_v2.1.18", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + } + ] + } + }, + { + "version": "2.1.17", + "tag": "@microsoft/load-themed-styles_v2.1.17", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + } + ] + } + }, + { + "version": "2.1.16", + "tag": "@microsoft/load-themed-styles_v2.1.16", + "date": "Wed, 08 Oct 2025 00:13:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + } + ] + } + }, + { + "version": "2.1.15", + "tag": "@microsoft/load-themed-styles_v2.1.15", + "date": "Fri, 03 Oct 2025 20:10:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + } + ] + } + }, + { + "version": "2.1.14", + "tag": "@microsoft/load-themed-styles_v2.1.14", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + } + ] + } + }, + { + "version": "2.1.13", + "tag": "@microsoft/load-themed-styles_v2.1.13", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + } + ] + } + }, + { + "version": "2.1.12", + "tag": "@microsoft/load-themed-styles_v2.1.12", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + } + ] + } + }, + { + "version": "2.1.11", + "tag": "@microsoft/load-themed-styles_v2.1.11", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + } + ] + } + }, + { + "version": "2.1.10", + "tag": "@microsoft/load-themed-styles_v2.1.10", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + } + ] + } + }, + { + "version": "2.1.9", + "tag": "@microsoft/load-themed-styles_v2.1.9", + "date": "Fri, 01 Aug 2025 00:12:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + } + ] + } + }, + { + "version": "2.1.8", + "tag": "@microsoft/load-themed-styles_v2.1.8", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + } + ] + } + }, + { + "version": "2.1.7", + "tag": "@microsoft/load-themed-styles_v2.1.7", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + } + ] + } + }, + { + "version": "2.1.6", + "tag": "@microsoft/load-themed-styles_v2.1.6", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + } + ] + } + }, + { + "version": "2.1.5", + "tag": "@microsoft/load-themed-styles_v2.1.5", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + } + ] + } + }, + { + "version": "2.1.4", + "tag": "@microsoft/load-themed-styles_v2.1.4", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + } + ] + } + }, + { + "version": "2.1.3", + "tag": "@microsoft/load-themed-styles_v2.1.3", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + } + ] + } + }, + { + "version": "2.1.2", + "tag": "@microsoft/load-themed-styles_v2.1.2", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + } + ] + } + }, + { + "version": "2.1.1", + "tag": "@microsoft/load-themed-styles_v2.1.1", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + } + ] + } + }, + { + "version": "2.1.0", + "tag": "@microsoft/load-themed-styles_v2.1.0", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "minor": [ + { + "comment": "Set css variables on `body` corresponding to all theme tokens in `loadTheme`. Add a new function `replaceTokensWithVariables` that will convert theme tokens in CSS to CSS variable references. Combined these allow callers to completely stop using `loadStyles` and export their CSS as external stylesheets." + }, + { + "comment": "Update package folder layout to be explicit about module types." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + } + ] + } + }, + { + "version": "2.0.171", + "tag": "@microsoft/load-themed-styles_v2.0.171", + "date": "Wed, 09 Apr 2025 00:11:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + } + ] + } + }, + { + "version": "2.0.170", + "tag": "@microsoft/load-themed-styles_v2.0.170", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + } + ] + } + }, + { + "version": "2.0.169", + "tag": "@microsoft/load-themed-styles_v2.0.169", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + } + ] + } + }, + { + "version": "2.0.168", + "tag": "@microsoft/load-themed-styles_v2.0.168", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + } + ] + } + }, + { + "version": "2.0.167", + "tag": "@microsoft/load-themed-styles_v2.0.167", + "date": "Wed, 12 Mar 2025 00:11:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + } + ] + } + }, + { + "version": "2.0.166", + "tag": "@microsoft/load-themed-styles_v2.0.166", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + } + ] + } + }, + { + "version": "2.0.165", + "tag": "@microsoft/load-themed-styles_v2.0.165", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + } + ] + } + }, + { + "version": "2.0.164", + "tag": "@microsoft/load-themed-styles_v2.0.164", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + } + ] + } + }, + { + "version": "2.0.163", + "tag": "@microsoft/load-themed-styles_v2.0.163", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + } + ] + } + }, + { + "version": "2.0.162", + "tag": "@microsoft/load-themed-styles_v2.0.162", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + } + ] + } + }, + { + "version": "2.0.161", + "tag": "@microsoft/load-themed-styles_v2.0.161", + "date": "Sat, 22 Feb 2025 01:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + } + ] + } + }, + { + "version": "2.0.160", + "tag": "@microsoft/load-themed-styles_v2.0.160", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + } + ] + } + }, + { + "version": "2.0.159", + "tag": "@microsoft/load-themed-styles_v2.0.159", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + } + ] + } + }, + { + "version": "2.0.158", + "tag": "@microsoft/load-themed-styles_v2.0.158", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + } + ] + } + }, + { + "version": "2.0.157", + "tag": "@microsoft/load-themed-styles_v2.0.157", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + } + ] + } + }, + { + "version": "2.0.156", + "tag": "@microsoft/load-themed-styles_v2.0.156", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + } + ] + } + }, + { + "version": "2.0.155", + "tag": "@microsoft/load-themed-styles_v2.0.155", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + } + ] + } + }, + { + "version": "2.0.154", + "tag": "@microsoft/load-themed-styles_v2.0.154", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + } + ] + } + }, + { + "version": "2.0.153", + "tag": "@microsoft/load-themed-styles_v2.0.153", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + } + ] + } + }, + { + "version": "2.0.152", + "tag": "@microsoft/load-themed-styles_v2.0.152", + "date": "Tue, 03 Dec 2024 16:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + } + ] + } + }, + { + "version": "2.0.151", + "tag": "@microsoft/load-themed-styles_v2.0.151", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + } + ] + } + }, + { + "version": "2.0.150", + "tag": "@microsoft/load-themed-styles_v2.0.150", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + } + ] + } + }, + { + "version": "2.0.149", + "tag": "@microsoft/load-themed-styles_v2.0.149", + "date": "Thu, 24 Oct 2024 00:15:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + } + ] + } + }, + { + "version": "2.0.148", + "tag": "@microsoft/load-themed-styles_v2.0.148", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + } + ] + } + }, + { + "version": "2.0.147", + "tag": "@microsoft/load-themed-styles_v2.0.147", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + } + ] + } + }, + { + "version": "2.0.146", + "tag": "@microsoft/load-themed-styles_v2.0.146", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + } + ] + } + }, + { + "version": "2.0.145", + "tag": "@microsoft/load-themed-styles_v2.0.145", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + } + ] + } + }, + { + "version": "2.0.144", + "tag": "@microsoft/load-themed-styles_v2.0.144", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + } + ] + } + }, + { + "version": "2.0.143", + "tag": "@microsoft/load-themed-styles_v2.0.143", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + } + ] + } + }, + { + "version": "2.0.142", + "tag": "@microsoft/load-themed-styles_v2.0.142", + "date": "Fri, 13 Sep 2024 00:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + } + ] + } + }, + { + "version": "2.0.141", + "tag": "@microsoft/load-themed-styles_v2.0.141", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + } + ] + } + }, + { + "version": "2.0.140", + "tag": "@microsoft/load-themed-styles_v2.0.140", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + } + ] + } + }, + { + "version": "2.0.139", + "tag": "@microsoft/load-themed-styles_v2.0.139", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + } + ] + } + }, + { + "version": "2.0.138", + "tag": "@microsoft/load-themed-styles_v2.0.138", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + } + ] + } + }, + { + "version": "2.0.137", + "tag": "@microsoft/load-themed-styles_v2.0.137", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + } + ] + } + }, + { + "version": "2.0.136", + "tag": "@microsoft/load-themed-styles_v2.0.136", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + } + ] + } + }, + { + "version": "2.0.135", + "tag": "@microsoft/load-themed-styles_v2.0.135", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + } + ] + } + }, + { + "version": "2.0.134", + "tag": "@microsoft/load-themed-styles_v2.0.134", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + } + ] + } + }, + { + "version": "2.0.133", + "tag": "@microsoft/load-themed-styles_v2.0.133", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + } + ] + } + }, + { + "version": "2.0.132", + "tag": "@microsoft/load-themed-styles_v2.0.132", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + } + ] + } + }, + { + "version": "2.0.131", + "tag": "@microsoft/load-themed-styles_v2.0.131", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + } + ] + } + }, + { + "version": "2.0.130", + "tag": "@microsoft/load-themed-styles_v2.0.130", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + } + ] + } + }, + { + "version": "2.0.129", + "tag": "@microsoft/load-themed-styles_v2.0.129", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + } + ] + } + }, + { + "version": "2.0.128", + "tag": "@microsoft/load-themed-styles_v2.0.128", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + } + ] + } + }, + { + "version": "2.0.127", + "tag": "@microsoft/load-themed-styles_v2.0.127", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + } + ] + } + }, + { + "version": "2.0.126", + "tag": "@microsoft/load-themed-styles_v2.0.126", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + } + ] + } + }, + { + "version": "2.0.125", + "tag": "@microsoft/load-themed-styles_v2.0.125", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + } + ] + } + }, + { + "version": "2.0.124", + "tag": "@microsoft/load-themed-styles_v2.0.124", + "date": "Fri, 24 May 2024 00:15:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + } + ] + } + }, + { + "version": "2.0.123", + "tag": "@microsoft/load-themed-styles_v2.0.123", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + } + ] + } + }, + { + "version": "2.0.122", + "tag": "@microsoft/load-themed-styles_v2.0.122", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + } + ] + } + }, + { + "version": "2.0.121", + "tag": "@microsoft/load-themed-styles_v2.0.121", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + } + ] + } + }, + { + "version": "2.0.120", + "tag": "@microsoft/load-themed-styles_v2.0.120", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + } + ] + } + }, + { + "version": "2.0.119", + "tag": "@microsoft/load-themed-styles_v2.0.119", + "date": "Fri, 10 May 2024 05:33:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + } + ] + } + }, + { + "version": "2.0.118", + "tag": "@microsoft/load-themed-styles_v2.0.118", + "date": "Wed, 08 May 2024 22:23:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + } + ] + } + }, + { + "version": "2.0.117", + "tag": "@microsoft/load-themed-styles_v2.0.117", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + } + ] + } + }, + { + "version": "2.0.116", + "tag": "@microsoft/load-themed-styles_v2.0.116", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + } + ] + } + }, + { + "version": "2.0.115", + "tag": "@microsoft/load-themed-styles_v2.0.115", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + } + ] + } + }, + { + "version": "2.0.114", + "tag": "@microsoft/load-themed-styles_v2.0.114", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + } + ] + } + }, + { + "version": "2.0.113", + "tag": "@microsoft/load-themed-styles_v2.0.113", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + } + ] + } + }, + { + "version": "2.0.112", + "tag": "@microsoft/load-themed-styles_v2.0.112", + "date": "Sun, 03 Mar 2024 20:58:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + } + ] + } + }, + { + "version": "2.0.111", + "tag": "@microsoft/load-themed-styles_v2.0.111", + "date": "Sat, 02 Mar 2024 02:22:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + } + ] + } + }, + { + "version": "2.0.110", + "tag": "@microsoft/load-themed-styles_v2.0.110", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + } + ] + } + }, + { + "version": "2.0.109", + "tag": "@microsoft/load-themed-styles_v2.0.109", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + } + ] + } + }, + { + "version": "2.0.108", + "tag": "@microsoft/load-themed-styles_v2.0.108", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + } + ] + } + }, + { + "version": "2.0.107", + "tag": "@microsoft/load-themed-styles_v2.0.107", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + } + ] + } + }, + { + "version": "2.0.106", + "tag": "@microsoft/load-themed-styles_v2.0.106", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + } + ] + } + }, + { + "version": "2.0.105", + "tag": "@microsoft/load-themed-styles_v2.0.105", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + } + ] + } + }, + { + "version": "2.0.104", + "tag": "@microsoft/load-themed-styles_v2.0.104", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + } + ] + } + }, + { + "version": "2.0.103", + "tag": "@microsoft/load-themed-styles_v2.0.103", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + } + ] + } + }, + { + "version": "2.0.102", + "tag": "@microsoft/load-themed-styles_v2.0.102", + "date": "Tue, 20 Feb 2024 16:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + } + ] + } + }, + { + "version": "2.0.101", + "tag": "@microsoft/load-themed-styles_v2.0.101", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + } + ] + } + }, + { + "version": "2.0.100", + "tag": "@microsoft/load-themed-styles_v2.0.100", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + } + ] + } + }, + { + "version": "2.0.99", + "tag": "@microsoft/load-themed-styles_v2.0.99", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + } + ] + } + }, + { + "version": "2.0.98", + "tag": "@microsoft/load-themed-styles_v2.0.98", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + } + ] + } + }, + { + "version": "2.0.97", + "tag": "@microsoft/load-themed-styles_v2.0.97", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + } + ] + } + }, + { + "version": "2.0.96", + "tag": "@microsoft/load-themed-styles_v2.0.96", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + } + ] + } + }, + { + "version": "2.0.95", + "tag": "@microsoft/load-themed-styles_v2.0.95", + "date": "Tue, 23 Jan 2024 20:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + } + ] + } + }, + { + "version": "2.0.94", + "tag": "@microsoft/load-themed-styles_v2.0.94", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + } + ] + } + }, + { + "version": "2.0.93", + "tag": "@microsoft/load-themed-styles_v2.0.93", + "date": "Tue, 16 Jan 2024 18:30:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + } + ] + } + }, + { + "version": "2.0.92", + "tag": "@microsoft/load-themed-styles_v2.0.92", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + } + ] + } + }, + { + "version": "2.0.91", + "tag": "@microsoft/load-themed-styles_v2.0.91", + "date": "Wed, 20 Dec 2023 01:09:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + } + ] + } + }, + { + "version": "2.0.90", + "tag": "@microsoft/load-themed-styles_v2.0.90", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + } + ] + } + }, + { + "version": "2.0.89", + "tag": "@microsoft/load-themed-styles_v2.0.89", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + } + ] + } + }, + { + "version": "2.0.88", + "tag": "@microsoft/load-themed-styles_v2.0.88", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + } + ] + } + }, + { + "version": "2.0.87", + "tag": "@microsoft/load-themed-styles_v2.0.87", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + } + ] + } + }, + { + "version": "2.0.86", + "tag": "@microsoft/load-themed-styles_v2.0.86", + "date": "Mon, 30 Oct 2023 23:36:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + } + ] + } + }, + { + "version": "2.0.85", + "tag": "@microsoft/load-themed-styles_v2.0.85", + "date": "Sun, 01 Oct 2023 02:56:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + } + ] + } + }, + { + "version": "2.0.84", + "tag": "@microsoft/load-themed-styles_v2.0.84", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + } + ] + } + }, + { + "version": "2.0.83", + "tag": "@microsoft/load-themed-styles_v2.0.83", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + } + ] + } + }, + { + "version": "2.0.82", + "tag": "@microsoft/load-themed-styles_v2.0.82", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + } + ] + } + }, + { + "version": "2.0.81", + "tag": "@microsoft/load-themed-styles_v2.0.81", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + } + ] + } + }, + { + "version": "2.0.80", + "tag": "@microsoft/load-themed-styles_v2.0.80", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + } + ] + } + }, + { + "version": "2.0.79", + "tag": "@microsoft/load-themed-styles_v2.0.79", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + } + ] + } + }, + { + "version": "2.0.78", + "tag": "@microsoft/load-themed-styles_v2.0.78", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + } + ] + } + }, + { + "version": "2.0.77", + "tag": "@microsoft/load-themed-styles_v2.0.77", + "date": "Tue, 19 Sep 2023 15:21:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.28`" + } + ] + } + }, + { + "version": "2.0.76", + "tag": "@microsoft/load-themed-styles_v2.0.76", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.27`" + } + ] + } + }, + { + "version": "2.0.75", + "tag": "@microsoft/load-themed-styles_v2.0.75", + "date": "Wed, 13 Sep 2023 00:32:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.26`" + } + ] + } + }, + { + "version": "2.0.74", + "tag": "@microsoft/load-themed-styles_v2.0.74", + "date": "Fri, 01 Sep 2023 04:53:58 GMT", + "comments": { + "patch": [ + { + "comment": "Use self.setTimeout() instead of setTimeout() to work around a Jest regression" + } + ] + } + }, + { + "version": "2.0.73", + "tag": "@microsoft/load-themed-styles_v2.0.73", + "date": "Tue, 08 Aug 2023 07:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.25`" + } + ] + } + }, + { + "version": "2.0.72", + "tag": "@microsoft/load-themed-styles_v2.0.72", + "date": "Sat, 05 Aug 2023 00:20:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.24`" + } + ] + } + }, + { + "version": "2.0.71", + "tag": "@microsoft/load-themed-styles_v2.0.71", + "date": "Fri, 04 Aug 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.23`" + } + ] + } + }, + { + "version": "2.0.70", + "tag": "@microsoft/load-themed-styles_v2.0.70", + "date": "Mon, 31 Jul 2023 15:19:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.22`" + } + ] + } + }, + { + "version": "2.0.69", + "tag": "@microsoft/load-themed-styles_v2.0.69", + "date": "Sat, 29 Jul 2023 00:22:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.21`" + } + ] + } + }, + { + "version": "2.0.68", + "tag": "@microsoft/load-themed-styles_v2.0.68", + "date": "Thu, 20 Jul 2023 20:47:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.20`" + } + ] + } + }, + { + "version": "2.0.67", + "tag": "@microsoft/load-themed-styles_v2.0.67", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.19`" + } + ] + } + }, + { + "version": "2.0.66", + "tag": "@microsoft/load-themed-styles_v2.0.66", + "date": "Mon, 17 Jul 2023 15:20:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.18`" + } + ] + } + }, + { + "version": "2.0.65", + "tag": "@microsoft/load-themed-styles_v2.0.65", + "date": "Fri, 14 Jul 2023 15:20:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.17`" + } + ] + } + }, + { + "version": "2.0.64", + "tag": "@microsoft/load-themed-styles_v2.0.64", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.16`" + } + ] + } + }, + { + "version": "2.0.63", + "tag": "@microsoft/load-themed-styles_v2.0.63", + "date": "Wed, 12 Jul 2023 15:20:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.15`" + } + ] + } + }, + { + "version": "2.0.62", + "tag": "@microsoft/load-themed-styles_v2.0.62", + "date": "Wed, 12 Jul 2023 00:23:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.14`" + } + ] + } + }, + { + "version": "2.0.61", + "tag": "@microsoft/load-themed-styles_v2.0.61", + "date": "Fri, 07 Jul 2023 00:19:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.13`" + } + ] + } + }, + { + "version": "2.0.60", + "tag": "@microsoft/load-themed-styles_v2.0.60", + "date": "Thu, 06 Jul 2023 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.12`" + } + ] + } + }, + { + "version": "2.0.59", + "tag": "@microsoft/load-themed-styles_v2.0.59", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.11`" + } + ] + } + }, + { + "version": "2.0.58", + "tag": "@microsoft/load-themed-styles_v2.0.58", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.10`" + } + ] + } + }, + { + "version": "2.0.57", + "tag": "@microsoft/load-themed-styles_v2.0.57", + "date": "Thu, 15 Jun 2023 00:21:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.9`" + } + ] + } + }, + { + "version": "2.0.56", + "tag": "@microsoft/load-themed-styles_v2.0.56", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.8`" + } + ] + } + }, + { + "version": "2.0.55", + "tag": "@microsoft/load-themed-styles_v2.0.55", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.7`" + } + ] + } + }, + { + "version": "2.0.54", + "tag": "@microsoft/load-themed-styles_v2.0.54", + "date": "Tue, 13 Jun 2023 01:49:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.6`" + } + ] + } + }, + { + "version": "2.0.53", + "tag": "@microsoft/load-themed-styles_v2.0.53", + "date": "Fri, 09 Jun 2023 18:05:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.5`" + } + ] + } + }, + { + "version": "2.0.52", + "tag": "@microsoft/load-themed-styles_v2.0.52", + "date": "Fri, 09 Jun 2023 15:23:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.4`" + } + ] + } + }, + { + "version": "2.0.51", + "tag": "@microsoft/load-themed-styles_v2.0.51", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.3`" + } + ] + } + }, + { + "version": "2.0.50", + "tag": "@microsoft/load-themed-styles_v2.0.50", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.2`" + } + ] + } + }, + { + "version": "2.0.49", + "tag": "@microsoft/load-themed-styles_v2.0.49", + "date": "Thu, 08 Jun 2023 00:20:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.1`" + } + ] + } + }, + { + "version": "2.0.48", + "tag": "@microsoft/load-themed-styles_v2.0.48", + "date": "Wed, 07 Jun 2023 22:45:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.18.0`" + } + ] + } + }, + { + "version": "2.0.47", + "tag": "@microsoft/load-themed-styles_v2.0.47", + "date": "Tue, 06 Jun 2023 02:52:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.17.0`" + } + ] + } + }, + { + "version": "2.0.46", + "tag": "@microsoft/load-themed-styles_v2.0.46", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-web-rig\" to `0.16.1`" + } + ] + } + }, { "version": "2.0.45", "tag": "@microsoft/load-themed-styles_v2.0.45", diff --git a/libraries/load-themed-styles/CHANGELOG.md b/libraries/load-themed-styles/CHANGELOG.md index a91ace578d5..17185ce0e45 100644 --- a/libraries/load-themed-styles/CHANGELOG.md +++ b/libraries/load-themed-styles/CHANGELOG.md @@ -1,6 +1,918 @@ # Change Log - @microsoft/load-themed-styles -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 2.2.22 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 2.2.21 +Fri, 17 Jul 2026 00:15:59 GMT + +_Version update only_ + +## 2.2.20 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 2.2.19 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 2.2.18 +Mon, 08 Jun 2026 15:15:49 GMT + +_Version update only_ + +## 2.2.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 2.2.16 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 2.2.15 +Sat, 18 Apr 2026 03:47:09 GMT + +_Version update only_ + +## 2.2.14 +Sat, 18 Apr 2026 00:15:16 GMT + +_Version update only_ + +## 2.2.13 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 2.2.12 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 2.2.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 2.2.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 2.2.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 2.2.8 +Tue, 31 Mar 2026 15:14:14 GMT + +_Version update only_ + +## 2.2.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 2.2.6 +Wed, 25 Feb 2026 21:39:42 GMT + +### Patches + +- Include missing `lib-*` folders. + +## 2.2.5 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 2.2.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 2.2.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 2.2.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 2.2.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 2.2.0 +Thu, 19 Feb 2026 00:04:52 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 2.1.29 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 2.1.28 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 2.1.27 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 2.1.26 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 2.1.25 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 2.1.24 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 2.1.23 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 2.1.22 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 2.1.21 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 2.1.20 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 2.1.19 +Tue, 04 Nov 2025 08:15:14 GMT + +_Version update only_ + +## 2.1.18 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 2.1.17 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 2.1.16 +Wed, 08 Oct 2025 00:13:28 GMT + +_Version update only_ + +## 2.1.15 +Fri, 03 Oct 2025 20:10:00 GMT + +_Version update only_ + +## 2.1.14 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 2.1.13 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 2.1.12 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 2.1.11 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 2.1.10 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 2.1.9 +Fri, 01 Aug 2025 00:12:48 GMT + +_Version update only_ + +## 2.1.8 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 2.1.7 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 2.1.6 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 2.1.5 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 2.1.4 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 2.1.3 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 2.1.2 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 2.1.1 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 2.1.0 +Tue, 15 Apr 2025 15:11:57 GMT + +### Minor changes + +- Set css variables on `body` corresponding to all theme tokens in `loadTheme`. Add a new function `replaceTokensWithVariables` that will convert theme tokens in CSS to CSS variable references. Combined these allow callers to completely stop using `loadStyles` and export their CSS as external stylesheets. +- Update package folder layout to be explicit about module types. + +## 2.0.171 +Wed, 09 Apr 2025 00:11:02 GMT + +_Version update only_ + +## 2.0.170 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 2.0.169 +Tue, 25 Mar 2025 15:11:15 GMT + +_Version update only_ + +## 2.0.168 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 2.0.167 +Wed, 12 Mar 2025 00:11:31 GMT + +_Version update only_ + +## 2.0.166 +Tue, 11 Mar 2025 02:12:33 GMT + +_Version update only_ + +## 2.0.165 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 2.0.164 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 2.0.163 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 2.0.162 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 2.0.161 +Sat, 22 Feb 2025 01:11:11 GMT + +_Version update only_ + +## 2.0.160 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 2.0.159 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 2.0.158 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 2.0.157 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 2.0.156 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 2.0.155 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 2.0.154 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 2.0.153 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 2.0.152 +Tue, 03 Dec 2024 16:11:07 GMT + +_Version update only_ + +## 2.0.151 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 2.0.150 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 2.0.149 +Thu, 24 Oct 2024 00:15:47 GMT + +_Version update only_ + +## 2.0.148 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 2.0.147 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 2.0.146 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 2.0.145 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 2.0.144 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 2.0.143 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 2.0.142 +Fri, 13 Sep 2024 00:11:42 GMT + +_Version update only_ + +## 2.0.141 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 2.0.140 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 2.0.139 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 2.0.138 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 2.0.137 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 2.0.136 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 2.0.135 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 2.0.134 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 2.0.133 +Tue, 16 Jul 2024 00:36:21 GMT + +_Version update only_ + +## 2.0.132 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 2.0.131 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 2.0.130 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 2.0.129 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 2.0.128 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 2.0.127 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 2.0.126 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 2.0.125 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 2.0.124 +Fri, 24 May 2024 00:15:08 GMT + +_Version update only_ + +## 2.0.123 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 2.0.122 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 2.0.121 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 2.0.120 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 2.0.119 +Fri, 10 May 2024 05:33:33 GMT + +_Version update only_ + +## 2.0.118 +Wed, 08 May 2024 22:23:50 GMT + +_Version update only_ + +## 2.0.117 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 2.0.116 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 2.0.115 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 2.0.114 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 2.0.113 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 2.0.112 +Sun, 03 Mar 2024 20:58:12 GMT + +_Version update only_ + +## 2.0.111 +Sat, 02 Mar 2024 02:22:23 GMT + +_Version update only_ + +## 2.0.110 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 2.0.109 +Thu, 29 Feb 2024 07:11:45 GMT + +_Version update only_ + +## 2.0.108 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 2.0.107 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 2.0.106 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 2.0.105 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 2.0.104 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 2.0.103 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 2.0.102 +Tue, 20 Feb 2024 16:10:52 GMT + +_Version update only_ + +## 2.0.101 +Mon, 19 Feb 2024 21:54:27 GMT + +_Version update only_ + +## 2.0.100 +Sat, 17 Feb 2024 06:24:35 GMT + +_Version update only_ + +## 2.0.99 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 2.0.98 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 2.0.97 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 2.0.96 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 2.0.95 +Tue, 23 Jan 2024 20:12:57 GMT + +_Version update only_ + +## 2.0.94 +Tue, 23 Jan 2024 16:15:05 GMT + +_Version update only_ + +## 2.0.93 +Tue, 16 Jan 2024 18:30:11 GMT + +_Version update only_ + +## 2.0.92 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 2.0.91 +Wed, 20 Dec 2023 01:09:45 GMT + +_Version update only_ + +## 2.0.90 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 2.0.89 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 2.0.88 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 2.0.87 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 2.0.86 +Mon, 30 Oct 2023 23:36:37 GMT + +_Version update only_ + +## 2.0.85 +Sun, 01 Oct 2023 02:56:29 GMT + +_Version update only_ + +## 2.0.84 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 2.0.83 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 2.0.82 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 2.0.81 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 2.0.80 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 2.0.79 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 2.0.78 +Fri, 22 Sep 2023 00:05:50 GMT + +_Version update only_ + +## 2.0.77 +Tue, 19 Sep 2023 15:21:51 GMT + +_Version update only_ + +## 2.0.76 +Fri, 15 Sep 2023 00:36:58 GMT + +_Version update only_ + +## 2.0.75 +Wed, 13 Sep 2023 00:32:29 GMT + +_Version update only_ + +## 2.0.74 +Fri, 01 Sep 2023 04:53:58 GMT + +### Patches + +- Use self.setTimeout() instead of setTimeout() to work around a Jest regression + +## 2.0.73 +Tue, 08 Aug 2023 07:10:39 GMT + +_Version update only_ + +## 2.0.72 +Sat, 05 Aug 2023 00:20:19 GMT + +_Version update only_ + +## 2.0.71 +Fri, 04 Aug 2023 00:22:37 GMT + +_Version update only_ + +## 2.0.70 +Mon, 31 Jul 2023 15:19:05 GMT + +_Version update only_ + +## 2.0.69 +Sat, 29 Jul 2023 00:22:50 GMT + +_Version update only_ + +## 2.0.68 +Thu, 20 Jul 2023 20:47:28 GMT + +_Version update only_ + +## 2.0.67 +Wed, 19 Jul 2023 00:20:31 GMT + +_Version update only_ + +## 2.0.66 +Mon, 17 Jul 2023 15:20:25 GMT + +_Version update only_ + +## 2.0.65 +Fri, 14 Jul 2023 15:20:45 GMT + +_Version update only_ + +## 2.0.64 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 2.0.63 +Wed, 12 Jul 2023 15:20:39 GMT + +_Version update only_ + +## 2.0.62 +Wed, 12 Jul 2023 00:23:29 GMT + +_Version update only_ + +## 2.0.61 +Fri, 07 Jul 2023 00:19:32 GMT + +_Version update only_ + +## 2.0.60 +Thu, 06 Jul 2023 00:16:19 GMT + +_Version update only_ + +## 2.0.59 +Tue, 04 Jul 2023 00:18:47 GMT + +_Version update only_ + +## 2.0.58 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 2.0.57 +Thu, 15 Jun 2023 00:21:01 GMT + +_Version update only_ + +## 2.0.56 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 2.0.55 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 2.0.54 +Tue, 13 Jun 2023 01:49:02 GMT + +_Version update only_ + +## 2.0.53 +Fri, 09 Jun 2023 18:05:34 GMT + +_Version update only_ + +## 2.0.52 +Fri, 09 Jun 2023 15:23:15 GMT + +_Version update only_ + +## 2.0.51 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 2.0.50 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 2.0.49 +Thu, 08 Jun 2023 00:20:02 GMT + +_Version update only_ + +## 2.0.48 +Wed, 07 Jun 2023 22:45:16 GMT + +_Version update only_ + +## 2.0.47 +Tue, 06 Jun 2023 02:52:51 GMT + +_Version update only_ + +## 2.0.46 +Mon, 05 Jun 2023 21:45:21 GMT + +_Version update only_ ## 2.0.45 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/libraries/load-themed-styles/config/jest.config.json b/libraries/load-themed-styles/config/jest.config.json index 600ba9ea39a..a6a75a1a029 100644 --- a/libraries/load-themed-styles/config/jest.config.json +++ b/libraries/load-themed-styles/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-web-rig/profiles/library/config/jest.config.json" + "extends": "local-web-rig/profiles/library/config/jest.config.json" } diff --git a/libraries/load-themed-styles/config/rig.json b/libraries/load-themed-styles/config/rig.json index a75d7748109..659f339663a 100644 --- a/libraries/load-themed-styles/config/rig.json +++ b/libraries/load-themed-styles/config/rig.json @@ -3,6 +3,6 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-web-rig", + "rigPackageName": "local-web-rig", "rigProfile": "library" } diff --git a/libraries/load-themed-styles/config/typescript.json b/libraries/load-themed-styles/config/typescript.json index 528ac4c485d..757aae0da54 100644 --- a/libraries/load-themed-styles/config/typescript.json +++ b/libraries/load-themed-styles/config/typescript.json @@ -2,7 +2,9 @@ * Configures the TypeScript plugin for Heft. This plugin also manages linting. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/typescript.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", + + "extends": "local-web-rig/profiles/library/config/typescript.json", /** * If provided, emit these module kinds in addition to the modules specified in the tsconfig. @@ -23,11 +25,6 @@ { "moduleKind": "amd", "outFolderName": "lib-amd" - }, - - { - "moduleKind": "esnext", - "outFolderName": "lib-es6" } ] } diff --git a/libraries/load-themed-styles/eslint.config.js b/libraries/load-themed-styles/eslint.config.js new file mode 100644 index 00000000000..8a61a653f26 --- /dev/null +++ b/libraries/load-themed-styles/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-web-rig/profiles/library/includes/eslint/flat/profile/web-app'); +const friendlyLocalsMixin = require('local-web-rig/profiles/library/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...webAppProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/load-themed-styles/package.json b/libraries/load-themed-styles/package.json index c4971d88b6b..66fed016bd7 100644 --- a/libraries/load-themed-styles/package.json +++ b/libraries/load-themed-styles/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/load-themed-styles", - "version": "2.0.45", + "version": "2.2.22", "description": "Loads themed styles.", "license": "MIT", "repository": { @@ -8,19 +8,41 @@ "type": "git", "directory": "libraries/load-themed-styles" }, + "keywords": [], "scripts": { "build": "heft build --clean", - "_phase:build": "heft run --only build -- --clean" + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*", + "node": "./lib-commonjs/*", + "import": "./lib-esm/*", + "require": "./lib-commonjs/*" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } }, - "main": "lib/index.js", - "module": "lib-es6/index.js", - "typings": "lib/index.d.ts", - "keywords": [], "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-web-rig": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/webpack-env": "1.18.0" - } + "eslint": "~9.37.0", + "local-web-rig": "workspace:*" + }, + "sideEffects": false } diff --git a/libraries/load-themed-styles/src/index.ts b/libraries/load-themed-styles/src/index.ts index 3bd083f2de6..ba6296d77b6 100644 --- a/libraries/load-themed-styles/src/index.ts +++ b/libraries/load-themed-styles/src/index.ts @@ -1,17 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -/** - * An IThemingInstruction can specify a rawString to be preserved or a theme slot and a default value - * to use if that slot is not specified by the theme. - */ - -/* eslint-disable @typescript-eslint/no-use-before-define */ - // Declaring a global here in case that the execution environment is Node.js (without importing the // entire node.js d.ts for now) +/// declare let global: any; // eslint-disable-line @typescript-eslint/no-explicit-any +declare const DEBUG: boolean | undefined; +/** + * An IThemingInstruction can specify a rawString to be preserved or a theme slot and a default value + * to use if that slot is not specified by the theme. + */ export interface IThemingInstruction { theme?: string; defaultValue?: string; @@ -221,7 +220,9 @@ export function flush(): void { * register async loadStyles */ function asyncLoadStyles(): number { - return setTimeout(() => { + // Use "self" to distinguish conflicting global typings for setTimeout() from lib.dom.d.ts vs Jest's @types/node + // https://github.com/jestjs/jest/issues/14418 + return self.setTimeout(() => { _themeState.runState.flushTimer = 0; flush(); }, 0); @@ -249,10 +250,28 @@ function applyThemableStyles(stylesArray: ThemableArray, styleRecord?: IStyleRec export function loadTheme(theme: ITheme | undefined): void { _themeState.theme = theme; + const { style } = document.body; + for (const key in theme) { + if (theme.hasOwnProperty(key)) { + style.setProperty(`--${key}`, theme[key]); + } + } + // reload styles. reloadStyles(); } +/** + * Replaces theme tokens with CSS variable references. + * @param styles - Raw css text with theme tokens + * @returns A css string with theme tokens replaced with css variable references + */ +export function replaceTokensWithVariables(styles: string): string { + return styles.replace(_themeTokenRegex, (match: string, themeSlot: string, defaultValue: string) => { + return typeof defaultValue === 'string' ? `var(--${themeSlot}, ${defaultValue})` : `var(--${themeSlot})`; + }); +} + /** * Clear already registered style elements and style records in theme_State object * @param option - specify which group of registered styles should be cleared. @@ -334,6 +353,7 @@ function resolveThemableArray(splitStyleArray: ThemableArray): IThemableArrayRes typeof DEBUG !== 'undefined' && DEBUG ) { + // eslint-disable-next-line no-console console.warn(`Theming value not provided for "${themeSlot}". Falling back to "${defaultValue}".`); } diff --git a/libraries/load-themed-styles/src/test/index.test.ts b/libraries/load-themed-styles/src/test/index.test.ts index cfe14387ab1..df4aa339704 100644 --- a/libraries/load-themed-styles/src/test/index.test.ts +++ b/libraries/load-themed-styles/src/test/index.test.ts @@ -7,10 +7,11 @@ import { splitStyles, loadStyles, configureLoadStyles, - IThemingInstruction -} from './../index'; + replaceTokensWithVariables, + type IThemingInstruction +} from '../index'; -describe('detokenize', () => { +describe(detokenize.name, () => { it('handles colors', () => { expect(detokenize('"[theme:name, default: #FFF]"')).toEqual('#FFF'); expect(detokenize('"[theme: name, default: #FFF]"')).toEqual('#FFF'); @@ -46,7 +47,40 @@ describe('detokenize', () => { it('translates missing themes', () => { expect(detokenize('"[theme:name]"')).toEqual('inherit'); }); +}); + +describe(replaceTokensWithVariables.name, () => { + it('handles colors', () => { + expect(replaceTokensWithVariables('"[theme:name, default: #FFF]"')).toEqual('var(--name, #FFF)'); + expect(replaceTokensWithVariables('"[theme: name, default: #FFF]"')).toEqual('var(--name, #FFF)'); + expect(replaceTokensWithVariables('"[theme: name , default: #FFF ]"')).toEqual('var(--name, #FFF)'); + }); + + it('handles rgba', () => { + expect(replaceTokensWithVariables('"[theme:name, default: rgba(255,255,255,.5)]"')).toEqual( + 'var(--name, rgba(255,255,255,.5))' + ); + }); + + it('handles fonts', () => { + expect(replaceTokensWithVariables('"[theme:name, default: "Segoe UI"]"')).toEqual( + 'var(--name, "Segoe UI")' + ); + }); + + it('ignores malformed themes', () => { + expect(replaceTokensWithVariables('"[theme:name, default: "Segoe UI"]')).toEqual( + '"[theme:name, default: "Segoe UI"]' + ); + expect(replaceTokensWithVariables('"[theme:]"')).toEqual('"[theme:]"'); + }); + it('translates missing defaults', () => { + expect(replaceTokensWithVariables('"[theme:name]"')).toEqual('var(--name)'); + }); +}); + +describe(splitStyles.name, () => { it('splits non-themable CSS', () => { const cssString: string = '.sampleClass\n{\n color: #FF0000;\n}\n'; const arr: IThemingInstruction[] = splitStyles(cssString); @@ -70,7 +104,9 @@ describe('detokenize', () => { } } }); +}); +describe(loadStyles.name, () => { it('passes the styles to loadStyles override callback', () => { const expected: string = 'xxx.foo { color: #FFF }xxx'; let subject: string | undefined = undefined; diff --git a/libraries/load-themed-styles/tsconfig.json b/libraries/load-themed-styles/tsconfig.json index 00ea21b7d61..d6a12420aa1 100644 --- a/libraries/load-themed-styles/tsconfig.json +++ b/libraries/load-themed-styles/tsconfig.json @@ -1,9 +1,9 @@ { - "extends": "./node_modules/@rushstack/heft-web-rig/profiles/library/tsconfig-base.json", + "extends": "./node_modules/local-web-rig/profiles/library/tsconfig-base.json", "compilerOptions": { "importHelpers": false, - "module": "commonjs", - "target": "ES2017", - "types": ["heft-jest", "webpack-env"] + "outDir": "lib-esm", + "declarationDir": "lib-dts", + "lib": ["ES2015"] } } diff --git a/libraries/localization-utilities/.eslintrc.js b/libraries/localization-utilities/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/libraries/localization-utilities/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/libraries/localization-utilities/.npmignore b/libraries/localization-utilities/.npmignore index 302dbc5b019..f7a40e10213 100644 --- a/libraries/localization-utilities/.npmignore +++ b/libraries/localization-utilities/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,16 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/localization-utilities/CHANGELOG.json b/libraries/localization-utilities/CHANGELOG.json index 111a4f5a136..3925e8c7780 100644 --- a/libraries/localization-utilities/CHANGELOG.json +++ b/libraries/localization-utilities/CHANGELOG.json @@ -1,6 +1,3180 @@ { "name": "@rushstack/localization-utilities", "entries": [ + { + "version": "0.16.0", + "tag": "@rushstack/localization-utilities_v0.16.0", + "date": "Tue, 04 Aug 2026 00:17:24 GMT", + "comments": { + "minor": [ + { + "comment": "Add opt-in declaration source map generation so editors can resolve go-to-definition from generated typings to the original source file." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.17.0`" + } + ] + } + }, + { + "version": "0.15.22", + "tag": "@rushstack/localization-utilities_v0.15.22", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "0.15.21", + "tag": "@rushstack/localization-utilities_v0.15.21", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "0.15.20", + "tag": "@rushstack/localization-utilities_v0.15.20", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "0.15.19", + "tag": "@rushstack/localization-utilities_v0.15.19", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "0.15.18", + "tag": "@rushstack/localization-utilities_v0.15.18", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "0.15.17", + "tag": "@rushstack/localization-utilities_v0.15.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "0.15.16", + "tag": "@rushstack/localization-utilities_v0.15.16", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "0.15.15", + "tag": "@rushstack/localization-utilities_v0.15.15", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "0.15.14", + "tag": "@rushstack/localization-utilities_v0.15.14", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "0.15.13", + "tag": "@rushstack/localization-utilities_v0.15.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "0.15.12", + "tag": "@rushstack/localization-utilities_v0.15.12", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "0.15.11", + "tag": "@rushstack/localization-utilities_v0.15.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "0.15.10", + "tag": "@rushstack/localization-utilities_v0.15.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "0.15.9", + "tag": "@rushstack/localization-utilities_v0.15.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "0.15.8", + "tag": "@rushstack/localization-utilities_v0.15.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "0.15.7", + "tag": "@rushstack/localization-utilities_v0.15.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "0.15.6", + "tag": "@rushstack/localization-utilities_v0.15.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "0.15.5", + "tag": "@rushstack/localization-utilities_v0.15.5", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "0.15.4", + "tag": "@rushstack/localization-utilities_v0.15.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "0.15.3", + "tag": "@rushstack/localization-utilities_v0.15.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "0.15.2", + "tag": "@rushstack/localization-utilities_v0.15.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "0.15.1", + "tag": "@rushstack/localization-utilities_v0.15.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "0.15.0", + "tag": "@rushstack/localization-utilities_v0.15.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "0.14.14", + "tag": "@rushstack/localization-utilities_v0.14.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "0.14.13", + "tag": "@rushstack/localization-utilities_v0.14.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "0.14.12", + "tag": "@rushstack/localization-utilities_v0.14.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "0.14.11", + "tag": "@rushstack/localization-utilities_v0.14.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "0.14.10", + "tag": "@rushstack/localization-utilities_v0.14.10", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "0.14.9", + "tag": "@rushstack/localization-utilities_v0.14.9", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "0.14.8", + "tag": "@rushstack/localization-utilities_v0.14.8", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "0.14.7", + "tag": "@rushstack/localization-utilities_v0.14.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + } + ] + } + }, + { + "version": "0.14.6", + "tag": "@rushstack/localization-utilities_v0.14.6", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + } + ] + } + }, + { + "version": "0.14.5", + "tag": "@rushstack/localization-utilities_v0.14.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + } + ] + } + }, + { + "version": "0.14.4", + "tag": "@rushstack/localization-utilities_v0.14.4", + "date": "Tue, 04 Nov 2025 08:15:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + } + ] + } + }, + { + "version": "0.14.3", + "tag": "@rushstack/localization-utilities_v0.14.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + } + ] + } + }, + { + "version": "0.14.2", + "tag": "@rushstack/localization-utilities_v0.14.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + } + ] + } + }, + { + "version": "0.14.1", + "tag": "@rushstack/localization-utilities_v0.14.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + } + ] + } + }, + { + "version": "0.14.0", + "tag": "@rushstack/localization-utilities_v0.14.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + } + ] + } + }, + { + "version": "0.13.23", + "tag": "@rushstack/localization-utilities_v0.13.23", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.47`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + } + ] + } + }, + { + "version": "0.13.22", + "tag": "@rushstack/localization-utilities_v0.13.22", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.46`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + } + ] + } + }, + { + "version": "0.13.21", + "tag": "@rushstack/localization-utilities_v0.13.21", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.45`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + } + ] + } + }, + { + "version": "0.13.20", + "tag": "@rushstack/localization-utilities_v0.13.20", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.44`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + } + ] + } + }, + { + "version": "0.13.19", + "tag": "@rushstack/localization-utilities_v0.13.19", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.43`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + } + ] + } + }, + { + "version": "0.13.18", + "tag": "@rushstack/localization-utilities_v0.13.18", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.42`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + } + ] + } + }, + { + "version": "0.13.17", + "tag": "@rushstack/localization-utilities_v0.13.17", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.41`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + } + ] + } + }, + { + "version": "0.13.16", + "tag": "@rushstack/localization-utilities_v0.13.16", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.40`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + } + ] + } + }, + { + "version": "0.13.15", + "tag": "@rushstack/localization-utilities_v0.13.15", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.39`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + } + ] + } + }, + { + "version": "0.13.14", + "tag": "@rushstack/localization-utilities_v0.13.14", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + } + ] + } + }, + { + "version": "0.13.13", + "tag": "@rushstack/localization-utilities_v0.13.13", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + } + ] + } + }, + { + "version": "0.13.12", + "tag": "@rushstack/localization-utilities_v0.13.12", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + } + ] + } + }, + { + "version": "0.13.11", + "tag": "@rushstack/localization-utilities_v0.13.11", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + } + ] + } + }, + { + "version": "0.13.10", + "tag": "@rushstack/localization-utilities_v0.13.10", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + } + ] + } + }, + { + "version": "0.13.9", + "tag": "@rushstack/localization-utilities_v0.13.9", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + } + ] + } + }, + { + "version": "0.13.8", + "tag": "@rushstack/localization-utilities_v0.13.8", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + } + ] + } + }, + { + "version": "0.13.7", + "tag": "@rushstack/localization-utilities_v0.13.7", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + } + ] + } + }, + { + "version": "0.13.6", + "tag": "@rushstack/localization-utilities_v0.13.6", + "date": "Tue, 25 Mar 2025 15:11:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + } + ] + } + }, + { + "version": "0.13.5", + "tag": "@rushstack/localization-utilities_v0.13.5", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + } + ] + } + }, + { + "version": "0.13.4", + "tag": "@rushstack/localization-utilities_v0.13.4", + "date": "Wed, 12 Mar 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + } + ] + } + }, + { + "version": "0.13.3", + "tag": "@rushstack/localization-utilities_v0.13.3", + "date": "Tue, 11 Mar 2025 02:12:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + } + ] + } + }, + { + "version": "0.13.2", + "tag": "@rushstack/localization-utilities_v0.13.2", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + } + ] + } + }, + { + "version": "0.13.1", + "tag": "@rushstack/localization-utilities_v0.13.1", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + } + ] + } + }, + { + "version": "0.13.0", + "tag": "@rushstack/localization-utilities_v0.13.0", + "date": "Thu, 27 Feb 2025 16:10:47 GMT", + "comments": { + "minor": [ + { + "comment": "Update `loc.json` format to allow keys to be mapped to raw strings. This is useful so that the file name can be preserved for a strings file that can be directly imported at runtime." + } + ] + } + }, + { + "version": "0.12.24", + "tag": "@rushstack/localization-utilities_v0.12.24", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + } + ] + } + }, + { + "version": "0.12.23", + "tag": "@rushstack/localization-utilities_v0.12.23", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + } + ] + } + }, + { + "version": "0.12.22", + "tag": "@rushstack/localization-utilities_v0.12.22", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + } + ] + } + }, + { + "version": "0.12.21", + "tag": "@rushstack/localization-utilities_v0.12.21", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + } + ] + } + }, + { + "version": "0.12.20", + "tag": "@rushstack/localization-utilities_v0.12.20", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + } + ] + } + }, + { + "version": "0.12.19", + "tag": "@rushstack/localization-utilities_v0.12.19", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + } + ] + } + }, + { + "version": "0.12.18", + "tag": "@rushstack/localization-utilities_v0.12.18", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + } + ] + } + }, + { + "version": "0.12.17", + "tag": "@rushstack/localization-utilities_v0.12.17", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + } + ] + } + }, + { + "version": "0.12.16", + "tag": "@rushstack/localization-utilities_v0.12.16", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + } + ] + } + }, + { + "version": "0.12.15", + "tag": "@rushstack/localization-utilities_v0.12.15", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + } + ] + } + }, + { + "version": "0.12.14", + "tag": "@rushstack/localization-utilities_v0.12.14", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + } + ] + } + }, + { + "version": "0.12.13", + "tag": "@rushstack/localization-utilities_v0.12.13", + "date": "Tue, 03 Dec 2024 16:11:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + } + ] + } + }, + { + "version": "0.12.12", + "tag": "@rushstack/localization-utilities_v0.12.12", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + } + ] + } + }, + { + "version": "0.12.11", + "tag": "@rushstack/localization-utilities_v0.12.11", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + } + ] + } + }, + { + "version": "0.12.10", + "tag": "@rushstack/localization-utilities_v0.12.10", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + } + ] + } + }, + { + "version": "0.12.9", + "tag": "@rushstack/localization-utilities_v0.12.9", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + } + ] + } + }, + { + "version": "0.12.8", + "tag": "@rushstack/localization-utilities_v0.12.8", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + } + ] + } + }, + { + "version": "0.12.7", + "tag": "@rushstack/localization-utilities_v0.12.7", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + } + ] + } + }, + { + "version": "0.12.6", + "tag": "@rushstack/localization-utilities_v0.12.6", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + } + ] + } + }, + { + "version": "0.12.5", + "tag": "@rushstack/localization-utilities_v0.12.5", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + } + ] + } + }, + { + "version": "0.12.4", + "tag": "@rushstack/localization-utilities_v0.12.4", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + } + ] + } + }, + { + "version": "0.12.3", + "tag": "@rushstack/localization-utilities_v0.12.3", + "date": "Sat, 28 Sep 2024 00:11:41 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.3`" + } + ] + } + }, + { + "version": "0.12.2", + "tag": "@rushstack/localization-utilities_v0.12.2", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + } + ] + } + }, + { + "version": "0.12.1", + "tag": "@rushstack/localization-utilities_v0.12.1", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + } + ] + } + }, + { + "version": "0.12.0", + "tag": "@rushstack/localization-utilities_v0.12.0", + "date": "Mon, 26 Aug 2024 02:00:11 GMT", + "comments": { + "minor": [ + { + "comment": "Add a `valueDocumentationComment` option to `exportAsDefault` that allows a documentation comment to be generated for the exported value." + }, + { + "comment": "Rename the `documentationComment` property in the `exportAsDefault` value to `interfaceDocumentationComment`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.14.0`" + } + ] + } + }, + { + "version": "0.11.1", + "tag": "@rushstack/localization-utilities_v0.11.1", + "date": "Wed, 21 Aug 2024 16:24:51 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where `inferDefaultExportInterfaceNameFromFilename` did not apply." + } + ] + } + }, + { + "version": "0.11.0", + "tag": "@rushstack/localization-utilities_v0.11.0", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "minor": [ + { + "comment": "Expand the typings generator to take a richer set of options for default exports. See `exportAsDefault` in @rushstack/typings-generator's `StringValuesTypingsGenerator`. Also included is another property in `exportAsDefault`: `inferInterfaceNameFromFilename`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + } + ] + } + }, + { + "version": "0.10.0", + "tag": "@rushstack/localization-utilities_v0.10.0", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "minor": [ + { + "comment": "Update the schema for `.loc.json` files to allow string names that include the `$` character." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.63`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + } + ] + } + }, + { + "version": "0.9.62", + "tag": "@rushstack/localization-utilities_v0.9.62", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.62`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + } + ] + } + }, + { + "version": "0.9.61", + "tag": "@rushstack/localization-utilities_v0.9.61", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.61`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + } + ] + } + }, + { + "version": "0.9.60", + "tag": "@rushstack/localization-utilities_v0.9.60", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.60`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + } + ] + } + }, + { + "version": "0.9.59", + "tag": "@rushstack/localization-utilities_v0.9.59", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.59`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + } + ] + } + }, + { + "version": "0.9.58", + "tag": "@rushstack/localization-utilities_v0.9.58", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.58`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + } + ] + } + }, + { + "version": "0.9.57", + "tag": "@rushstack/localization-utilities_v0.9.57", + "date": "Tue, 16 Jul 2024 00:36:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.57`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + } + ] + } + }, + { + "version": "0.9.56", + "tag": "@rushstack/localization-utilities_v0.9.56", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.56`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + } + ] + } + }, + { + "version": "0.9.55", + "tag": "@rushstack/localization-utilities_v0.9.55", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.55`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + } + ] + } + }, + { + "version": "0.9.54", + "tag": "@rushstack/localization-utilities_v0.9.54", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "patch": [ + { + "comment": "Include missing `type` modifiers on type-only exports." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.54`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + } + ] + } + }, + { + "version": "0.9.53", + "tag": "@rushstack/localization-utilities_v0.9.53", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.53`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + } + ] + } + }, + { + "version": "0.9.52", + "tag": "@rushstack/localization-utilities_v0.9.52", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.52`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + } + ] + } + }, + { + "version": "0.9.51", + "tag": "@rushstack/localization-utilities_v0.9.51", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.51`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + } + ] + } + }, + { + "version": "0.9.50", + "tag": "@rushstack/localization-utilities_v0.9.50", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.50`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + } + ] + } + }, + { + "version": "0.9.49", + "tag": "@rushstack/localization-utilities_v0.9.49", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.49`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + } + ] + } + }, + { + "version": "0.9.48", + "tag": "@rushstack/localization-utilities_v0.9.48", + "date": "Fri, 24 May 2024 00:15:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.48`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + } + ] + } + }, + { + "version": "0.9.47", + "tag": "@rushstack/localization-utilities_v0.9.47", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.47`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + } + ] + } + }, + { + "version": "0.9.46", + "tag": "@rushstack/localization-utilities_v0.9.46", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.46`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + } + ] + } + }, + { + "version": "0.9.45", + "tag": "@rushstack/localization-utilities_v0.9.45", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.45`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + } + ] + } + }, + { + "version": "0.9.44", + "tag": "@rushstack/localization-utilities_v0.9.44", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.44`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + } + ] + } + }, + { + "version": "0.9.43", + "tag": "@rushstack/localization-utilities_v0.9.43", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.43`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + } + ] + } + }, + { + "version": "0.9.42", + "tag": "@rushstack/localization-utilities_v0.9.42", + "date": "Wed, 08 May 2024 22:23:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.42`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + } + ] + } + }, + { + "version": "0.9.41", + "tag": "@rushstack/localization-utilities_v0.9.41", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.41`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + } + ] + } + }, + { + "version": "0.9.40", + "tag": "@rushstack/localization-utilities_v0.9.40", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.40`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + } + ] + } + }, + { + "version": "0.9.39", + "tag": "@rushstack/localization-utilities_v0.9.39", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.39`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + } + ] + } + }, + { + "version": "0.9.38", + "tag": "@rushstack/localization-utilities_v0.9.38", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + } + ] + } + }, + { + "version": "0.9.37", + "tag": "@rushstack/localization-utilities_v0.9.37", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + } + ] + } + }, + { + "version": "0.9.36", + "tag": "@rushstack/localization-utilities_v0.9.36", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + } + ] + } + }, + { + "version": "0.9.35", + "tag": "@rushstack/localization-utilities_v0.9.35", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + } + ] + } + }, + { + "version": "0.9.34", + "tag": "@rushstack/localization-utilities_v0.9.34", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + } + ] + } + }, + { + "version": "0.9.33", + "tag": "@rushstack/localization-utilities_v0.9.33", + "date": "Thu, 29 Feb 2024 07:11:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + } + ] + } + }, + { + "version": "0.9.32", + "tag": "@rushstack/localization-utilities_v0.9.32", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + } + ] + } + }, + { + "version": "0.9.31", + "tag": "@rushstack/localization-utilities_v0.9.31", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + } + ] + } + }, + { + "version": "0.9.30", + "tag": "@rushstack/localization-utilities_v0.9.30", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + } + ] + } + }, + { + "version": "0.9.29", + "tag": "@rushstack/localization-utilities_v0.9.29", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + } + ] + } + }, + { + "version": "0.9.28", + "tag": "@rushstack/localization-utilities_v0.9.28", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + } + ] + } + }, + { + "version": "0.9.27", + "tag": "@rushstack/localization-utilities_v0.9.27", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + } + ] + } + }, + { + "version": "0.9.26", + "tag": "@rushstack/localization-utilities_v0.9.26", + "date": "Tue, 20 Feb 2024 16:10:53 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + } + ] + } + }, + { + "version": "0.9.25", + "tag": "@rushstack/localization-utilities_v0.9.25", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + } + ] + } + }, + { + "version": "0.9.24", + "tag": "@rushstack/localization-utilities_v0.9.24", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + } + ] + } + }, + { + "version": "0.9.23", + "tag": "@rushstack/localization-utilities_v0.9.23", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + } + ] + } + }, + { + "version": "0.9.22", + "tag": "@rushstack/localization-utilities_v0.9.22", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + } + ] + } + }, + { + "version": "0.9.21", + "tag": "@rushstack/localization-utilities_v0.9.21", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + } + ] + } + }, + { + "version": "0.9.20", + "tag": "@rushstack/localization-utilities_v0.9.20", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + } + ] + } + }, + { + "version": "0.9.19", + "tag": "@rushstack/localization-utilities_v0.9.19", + "date": "Tue, 23 Jan 2024 20:12:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + } + ] + } + }, + { + "version": "0.9.18", + "tag": "@rushstack/localization-utilities_v0.9.18", + "date": "Tue, 23 Jan 2024 16:15:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + } + ] + } + }, + { + "version": "0.9.17", + "tag": "@rushstack/localization-utilities_v0.9.17", + "date": "Tue, 16 Jan 2024 18:30:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + } + ] + } + }, + { + "version": "0.9.16", + "tag": "@rushstack/localization-utilities_v0.9.16", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + } + ] + } + }, + { + "version": "0.9.15", + "tag": "@rushstack/localization-utilities_v0.9.15", + "date": "Wed, 20 Dec 2023 01:09:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + } + ] + } + }, + { + "version": "0.9.14", + "tag": "@rushstack/localization-utilities_v0.9.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + } + ] + } + }, + { + "version": "0.9.13", + "tag": "@rushstack/localization-utilities_v0.9.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + } + ] + } + }, + { + "version": "0.9.12", + "tag": "@rushstack/localization-utilities_v0.9.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + } + ] + } + }, + { + "version": "0.9.11", + "tag": "@rushstack/localization-utilities_v0.9.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + } + ] + } + }, + { + "version": "0.9.10", + "tag": "@rushstack/localization-utilities_v0.9.10", + "date": "Mon, 30 Oct 2023 23:36:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + } + ] + } + }, + { + "version": "0.9.9", + "tag": "@rushstack/localization-utilities_v0.9.9", + "date": "Sun, 01 Oct 2023 02:56:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + } + ] + } + }, + { + "version": "0.9.8", + "tag": "@rushstack/localization-utilities_v0.9.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + } + ] + } + }, + { + "version": "0.9.7", + "tag": "@rushstack/localization-utilities_v0.9.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + } + ] + } + }, + { + "version": "0.9.6", + "tag": "@rushstack/localization-utilities_v0.9.6", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + } + ] + } + }, + { + "version": "0.9.5", + "tag": "@rushstack/localization-utilities_v0.9.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + } + ] + } + }, + { + "version": "0.9.4", + "tag": "@rushstack/localization-utilities_v0.9.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + } + ] + } + }, + { + "version": "0.9.3", + "tag": "@rushstack/localization-utilities_v0.9.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + } + ] + } + }, + { + "version": "0.9.2", + "tag": "@rushstack/localization-utilities_v0.9.2", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + } + ] + } + }, + { + "version": "0.9.1", + "tag": "@rushstack/localization-utilities_v0.9.1", + "date": "Tue, 19 Sep 2023 15:21:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.24`" + } + ] + } + }, + { + "version": "0.9.0", + "tag": "@rushstack/localization-utilities_v0.9.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.23`" + } + ] + } + }, + { + "version": "0.8.83", + "tag": "@rushstack/localization-utilities_v0.8.83", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.22`" + } + ] + } + }, + { + "version": "0.8.82", + "tag": "@rushstack/localization-utilities_v0.8.82", + "date": "Sat, 05 Aug 2023 00:20:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.11.0`" + } + ] + } + }, + { + "version": "0.8.81", + "tag": "@rushstack/localization-utilities_v0.8.81", + "date": "Fri, 04 Aug 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.37`" + } + ] + } + }, + { + "version": "0.8.80", + "tag": "@rushstack/localization-utilities_v0.8.80", + "date": "Mon, 31 Jul 2023 15:19:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.21`" + } + ] + } + }, + { + "version": "0.8.79", + "tag": "@rushstack/localization-utilities_v0.8.79", + "date": "Sat, 29 Jul 2023 00:22:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.20`" + } + ] + } + }, + { + "version": "0.8.78", + "tag": "@rushstack/localization-utilities_v0.8.78", + "date": "Thu, 20 Jul 2023 20:47:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.19`" + } + ] + } + }, + { + "version": "0.8.77", + "tag": "@rushstack/localization-utilities_v0.8.77", + "date": "Wed, 19 Jul 2023 00:20:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.18`" + } + ] + } + }, + { + "version": "0.8.76", + "tag": "@rushstack/localization-utilities_v0.8.76", + "date": "Fri, 14 Jul 2023 15:20:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.17`" + } + ] + } + }, + { + "version": "0.8.75", + "tag": "@rushstack/localization-utilities_v0.8.75", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.16`" + } + ] + } + }, + { + "version": "0.8.74", + "tag": "@rushstack/localization-utilities_v0.8.74", + "date": "Wed, 12 Jul 2023 15:20:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.15`" + } + ] + } + }, + { + "version": "0.8.73", + "tag": "@rushstack/localization-utilities_v0.8.73", + "date": "Wed, 12 Jul 2023 00:23:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.14`" + } + ] + } + }, + { + "version": "0.8.72", + "tag": "@rushstack/localization-utilities_v0.8.72", + "date": "Fri, 07 Jul 2023 00:19:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.13`" + } + ] + } + }, + { + "version": "0.8.71", + "tag": "@rushstack/localization-utilities_v0.8.71", + "date": "Thu, 06 Jul 2023 00:16:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.12`" + } + ] + } + }, + { + "version": "0.8.70", + "tag": "@rushstack/localization-utilities_v0.8.70", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.11`" + } + ] + } + }, + { + "version": "0.8.69", + "tag": "@rushstack/localization-utilities_v0.8.69", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.10`" + } + ] + } + }, + { + "version": "0.8.68", + "tag": "@rushstack/localization-utilities_v0.8.68", + "date": "Thu, 15 Jun 2023 00:21:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.24`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.9`" + } + ] + } + }, + { + "version": "0.8.67", + "tag": "@rushstack/localization-utilities_v0.8.67", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.8`" + } + ] + } + }, + { + "version": "0.8.66", + "tag": "@rushstack/localization-utilities_v0.8.66", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.7`" + } + ] + } + }, + { + "version": "0.8.65", + "tag": "@rushstack/localization-utilities_v0.8.65", + "date": "Tue, 13 Jun 2023 01:49:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.6`" + } + ] + } + }, + { + "version": "0.8.64", + "tag": "@rushstack/localization-utilities_v0.8.64", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.5`" + } + ] + } + }, + { + "version": "0.8.63", + "tag": "@rushstack/localization-utilities_v0.8.63", + "date": "Fri, 09 Jun 2023 15:23:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.4`" + } + ] + } + }, + { + "version": "0.8.62", + "tag": "@rushstack/localization-utilities_v0.8.62", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.3`" + } + ] + } + }, + { + "version": "0.8.61", + "tag": "@rushstack/localization-utilities_v0.8.61", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.2`" + } + ] + } + }, + { + "version": "0.8.60", + "tag": "@rushstack/localization-utilities_v0.8.60", + "date": "Thu, 08 Jun 2023 00:20:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.1`" + } + ] + } + }, + { + "version": "0.8.59", + "tag": "@rushstack/localization-utilities_v0.8.59", + "date": "Wed, 07 Jun 2023 22:45:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.15`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.0`" + } + ] + } + }, + { + "version": "0.8.58", + "tag": "@rushstack/localization-utilities_v0.8.58", + "date": "Tue, 06 Jun 2023 02:52:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.1.0`" + } + ] + } + }, + { + "version": "0.8.57", + "tag": "@rushstack/localization-utilities_v0.8.57", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/typings-generator\" to `0.10.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.0.1`" + } + ] + } + }, { "version": "0.8.56", "tag": "@rushstack/localization-utilities_v0.8.56", diff --git a/libraries/localization-utilities/CHANGELOG.md b/libraries/localization-utilities/CHANGELOG.md index b3052f07afa..f1606b282dc 100644 --- a/libraries/localization-utilities/CHANGELOG.md +++ b/libraries/localization-utilities/CHANGELOG.md @@ -1,6 +1,940 @@ # Change Log - @rushstack/localization-utilities -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 04 Aug 2026 00:17:24 GMT and should not be manually modified. + +## 0.16.0 +Tue, 04 Aug 2026 00:17:24 GMT + +### Minor changes + +- Add opt-in declaration source map generation so editors can resolve go-to-definition from generated typings to the original source file. + +## 0.15.22 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 0.15.21 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 0.15.20 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.15.19 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.15.18 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 0.15.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.15.16 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 0.15.15 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 0.15.14 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 0.15.13 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.15.12 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 0.15.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.15.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 0.15.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 0.15.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.15.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 0.15.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.15.5 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 0.15.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.15.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 0.15.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 0.15.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.15.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.14.14 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 0.14.13 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 0.14.12 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 0.14.11 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 0.14.10 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 0.14.9 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 0.14.8 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 0.14.7 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 0.14.6 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 0.14.5 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 0.14.4 +Tue, 04 Nov 2025 08:15:15 GMT + +_Version update only_ + +## 0.14.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 0.14.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 0.14.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 0.14.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 0.13.23 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 0.13.22 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.13.21 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.13.20 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.13.19 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.13.18 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 0.13.17 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.13.16 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.13.15 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.13.14 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.13.13 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.13.12 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.13.11 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.13.10 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.13.9 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 0.13.8 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 0.13.7 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.13.6 +Tue, 25 Mar 2025 15:11:16 GMT + +_Version update only_ + +## 0.13.5 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.13.4 +Wed, 12 Mar 2025 00:11:32 GMT + +_Version update only_ + +## 0.13.3 +Tue, 11 Mar 2025 02:12:34 GMT + +_Version update only_ + +## 0.13.2 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.13.1 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.13.0 +Thu, 27 Feb 2025 16:10:47 GMT + +### Minor changes + +- Update `loc.json` format to allow keys to be mapped to raw strings. This is useful so that the file name can be preserved for a strings file that can be directly imported at runtime. + +## 0.12.24 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.12.23 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 0.12.22 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.12.21 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.12.20 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.12.19 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 0.12.18 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.12.17 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.12.16 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.12.15 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.12.14 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.12.13 +Tue, 03 Dec 2024 16:11:08 GMT + +_Version update only_ + +## 0.12.12 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.12.11 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.12.10 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.12.9 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.12.8 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.12.7 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.12.6 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.12.5 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.12.4 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.12.3 +Sat, 28 Sep 2024 00:11:41 GMT + +_Version update only_ + +## 0.12.2 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.12.1 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.12.0 +Mon, 26 Aug 2024 02:00:11 GMT + +### Minor changes + +- Add a `valueDocumentationComment` option to `exportAsDefault` that allows a documentation comment to be generated for the exported value. +- Rename the `documentationComment` property in the `exportAsDefault` value to `interfaceDocumentationComment`. + +## 0.11.1 +Wed, 21 Aug 2024 16:24:51 GMT + +### Patches + +- Fix an issue where `inferDefaultExportInterfaceNameFromFilename` did not apply. + +## 0.11.0 +Wed, 21 Aug 2024 05:43:04 GMT + +### Minor changes + +- Expand the typings generator to take a richer set of options for default exports. See `exportAsDefault` in @rushstack/typings-generator's `StringValuesTypingsGenerator`. Also included is another property in `exportAsDefault`: `inferInterfaceNameFromFilename`. + +## 0.10.0 +Mon, 12 Aug 2024 22:16:04 GMT + +### Minor changes + +- Update the schema for `.loc.json` files to allow string names that include the `$` character. + +## 0.9.62 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.9.61 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.9.60 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 0.9.59 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 0.9.58 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.9.57 +Tue, 16 Jul 2024 00:36:22 GMT + +_Version update only_ + +## 0.9.56 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.9.55 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.9.54 +Thu, 30 May 2024 00:13:05 GMT + +### Patches + +- Include missing `type` modifiers on type-only exports. + +## 0.9.53 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 0.9.52 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.9.51 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.9.50 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.9.49 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.9.48 +Fri, 24 May 2024 00:15:09 GMT + +_Version update only_ + +## 0.9.47 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 0.9.46 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.9.45 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.9.44 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.9.43 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.9.42 +Wed, 08 May 2024 22:23:51 GMT + +_Version update only_ + +## 0.9.41 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 0.9.40 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.9.39 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 0.9.38 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 0.9.37 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 0.9.36 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 0.9.35 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 0.9.34 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 0.9.33 +Thu, 29 Feb 2024 07:11:46 GMT + +_Version update only_ + +## 0.9.32 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.9.31 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.9.30 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.9.29 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.9.28 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.9.27 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.9.26 +Tue, 20 Feb 2024 16:10:53 GMT + +_Version update only_ + +## 0.9.25 +Mon, 19 Feb 2024 21:54:27 GMT + +_Version update only_ + +## 0.9.24 +Sat, 17 Feb 2024 06:24:35 GMT + +_Version update only_ + +## 0.9.23 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 0.9.22 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.9.21 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.9.20 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.9.19 +Tue, 23 Jan 2024 20:12:58 GMT + +_Version update only_ + +## 0.9.18 +Tue, 23 Jan 2024 16:15:06 GMT + +_Version update only_ + +## 0.9.17 +Tue, 16 Jan 2024 18:30:11 GMT + +_Version update only_ + +## 0.9.16 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.9.15 +Wed, 20 Dec 2023 01:09:46 GMT + +_Version update only_ + +## 0.9.14 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.9.13 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.9.12 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 0.9.11 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 0.9.10 +Mon, 30 Oct 2023 23:36:38 GMT + +_Version update only_ + +## 0.9.9 +Sun, 01 Oct 2023 02:56:30 GMT + +_Version update only_ + +## 0.9.8 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 0.9.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.9.6 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 0.9.5 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 0.9.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.9.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 0.9.2 +Fri, 22 Sep 2023 00:05:50 GMT + +_Version update only_ + +## 0.9.1 +Tue, 19 Sep 2023 15:21:51 GMT + +_Version update only_ + +## 0.9.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 0.8.83 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 0.8.82 +Sat, 05 Aug 2023 00:20:19 GMT + +_Version update only_ + +## 0.8.81 +Fri, 04 Aug 2023 00:22:37 GMT + +_Version update only_ + +## 0.8.80 +Mon, 31 Jul 2023 15:19:06 GMT + +_Version update only_ + +## 0.8.79 +Sat, 29 Jul 2023 00:22:51 GMT + +_Version update only_ + +## 0.8.78 +Thu, 20 Jul 2023 20:47:28 GMT + +_Version update only_ + +## 0.8.77 +Wed, 19 Jul 2023 00:20:32 GMT + +_Version update only_ + +## 0.8.76 +Fri, 14 Jul 2023 15:20:45 GMT + +_Version update only_ + +## 0.8.75 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 0.8.74 +Wed, 12 Jul 2023 15:20:40 GMT + +_Version update only_ + +## 0.8.73 +Wed, 12 Jul 2023 00:23:30 GMT + +_Version update only_ + +## 0.8.72 +Fri, 07 Jul 2023 00:19:33 GMT + +_Version update only_ + +## 0.8.71 +Thu, 06 Jul 2023 00:16:20 GMT + +_Version update only_ + +## 0.8.70 +Tue, 04 Jul 2023 00:18:47 GMT + +_Version update only_ + +## 0.8.69 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 0.8.68 +Thu, 15 Jun 2023 00:21:02 GMT + +_Version update only_ + +## 0.8.67 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 0.8.66 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 0.8.65 +Tue, 13 Jun 2023 01:49:02 GMT + +_Version update only_ + +## 0.8.64 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 0.8.63 +Fri, 09 Jun 2023 15:23:15 GMT + +_Version update only_ + +## 0.8.62 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 0.8.61 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 0.8.60 +Thu, 08 Jun 2023 00:20:03 GMT + +_Version update only_ + +## 0.8.59 +Wed, 07 Jun 2023 22:45:17 GMT + +_Version update only_ + +## 0.8.58 +Tue, 06 Jun 2023 02:52:51 GMT + +_Version update only_ + +## 0.8.57 +Mon, 05 Jun 2023 21:45:21 GMT + +_Version update only_ ## 0.8.56 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/libraries/localization-utilities/config/api-extractor.json b/libraries/localization-utilities/config/api-extractor.json index 996e271d3dd..3dbb76c0e6f 100644 --- a/libraries/localization-utilities/config/api-extractor.json +++ b/libraries/localization-utilities/config/api-extractor.json @@ -1,19 +1,4 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - - "mainEntryPointFilePath": "/lib/index.d.ts", - - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, - - "docModel": { - "enabled": true, - "apiJsonFilePath": "../../../common/temp/api/.api.json" - }, - - "dtsRollup": { - "enabled": true - } + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json" } diff --git a/libraries/localization-utilities/config/rig.json b/libraries/localization-utilities/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/libraries/localization-utilities/config/rig.json +++ b/libraries/localization-utilities/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/libraries/localization-utilities/config/typescript.json b/libraries/localization-utilities/config/typescript.json index d3c2eb61d60..5680fc18ddc 100644 --- a/libraries/localization-utilities/config/typescript.json +++ b/libraries/localization-utilities/config/typescript.json @@ -1,5 +1,5 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/typescript.json", + "extends": "local-node-rig/profiles/default/config/typescript.json", /** * Configures additional file types that should be copied into the TypeScript compiler's emit folders, for example diff --git a/libraries/localization-utilities/eslint.config.js b/libraries/localization-utilities/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/libraries/localization-utilities/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/localization-utilities/package.json b/libraries/localization-utilities/package.json index 50f07619200..48ca54e1830 100644 --- a/libraries/localization-utilities/package.json +++ b/libraries/localization-utilities/package.json @@ -1,9 +1,33 @@ { "name": "@rushstack/localization-utilities", - "version": "0.8.56", + "version": "0.16.0", "description": "This plugin contains some useful functions for localization.", - "main": "lib/index.js", - "typings": "dist/localization-utilities.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/localization-utilities.d.ts", + "exports": { + ".": { + "types": "./dist/localization-utilities.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "license": "MIT", "repository": { "type": "git", @@ -17,16 +41,16 @@ }, "dependencies": { "@rushstack/node-core-library": "workspace:*", + "@rushstack/terminal": "workspace:*", "@rushstack/typings-generator": "workspace:*", "pseudolocale": "~1.1.0", "xmldoc": "~1.1.2" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "@types/xmldoc": "1.1.4" - } + "@types/xmldoc": "1.1.4", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "sideEffects": false } diff --git a/libraries/localization-utilities/src/LocFileParser.ts b/libraries/localization-utilities/src/LocFileParser.ts index 67c7dc8ed56..a15c3adce17 100644 --- a/libraries/localization-utilities/src/LocFileParser.ts +++ b/libraries/localization-utilities/src/LocFileParser.ts @@ -4,7 +4,7 @@ import type { IgnoreStringFunction, ILocalizationFile, IParseFileOptions } from './interfaces'; import { parseLocJson } from './parsers/parseLocJson'; import { parseResJson } from './parsers/parseResJson'; -import { IParseResxOptionsBase, parseResx } from './parsers/parseResx'; +import { type IParseResxOptionsBase, parseResx } from './parsers/parseResx'; /** * @public diff --git a/libraries/localization-utilities/src/Pseudolocalization.ts b/libraries/localization-utilities/src/Pseudolocalization.ts index e1afdae689f..af6cc69161a 100644 --- a/libraries/localization-utilities/src/Pseudolocalization.ts +++ b/libraries/localization-utilities/src/Pseudolocalization.ts @@ -1,10 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import vm from 'vm'; +import vm from 'node:vm'; + import { FileSystem } from '@rushstack/node-core-library'; -import { IPseudolocaleOptions } from './interfaces'; +import type { IPseudolocaleOptions } from './interfaces'; const pseudolocalePath: string = require.resolve('pseudolocale/pseudolocale.min.js'); diff --git a/libraries/localization-utilities/src/TypingsGenerator.ts b/libraries/localization-utilities/src/TypingsGenerator.ts index d7f6ab26a4f..40e8c72d027 100644 --- a/libraries/localization-utilities/src/TypingsGenerator.ts +++ b/libraries/localization-utilities/src/TypingsGenerator.ts @@ -3,25 +3,71 @@ import { StringValuesTypingsGenerator, - IStringValueTyping, - ITypingsGeneratorBaseOptions + type IStringValueTypings, + type IExportAsDefaultOptions, + type IStringValueTyping, + type ITypingsGeneratorBaseOptions } from '@rushstack/typings-generator'; -import { NewlineKind } from '@rushstack/node-core-library'; +import { FileSystem, type NewlineKind } from '@rushstack/node-core-library'; import type { IgnoreStringFunction, ILocalizationFile } from './interfaces'; import { parseLocFile } from './LocFileParser'; +/** + * @public + */ +export interface IInferInterfaceNameExportAsDefaultOptions + extends Omit { + /** + * When `exportAsDefault` is true and this option is true, the default export interface name will be inferred + * from the filename. + */ + inferInterfaceNameFromFilename?: boolean; +} + /** * @public */ export interface ITypingsGeneratorOptions extends ITypingsGeneratorBaseOptions { - exportAsDefault?: boolean; + /** + * Options for configuring the default export. + */ + exportAsDefault?: boolean | IExportAsDefaultOptions | IInferInterfaceNameExportAsDefaultOptions; + + /** + * Normalizes the line endings in .resx files to the specified kind. + */ resxNewlineNormalization?: NewlineKind | undefined; + + /** + * If specified, the generator will write trimmed .json files to the specified folders. + * The .json files will be written to the same relative path as the source file. + * For example, if the source file is "<root>/foo/bar.resx", and the output folder is "dist", + * the trimmed .json file will be written to "dist/foo/bar.resx.json". + */ + trimmedJsonOutputFolders?: string[] | undefined; + + /** + * If true, .resx files will not throw errors if comments are missing. + */ ignoreMissingResxComments?: boolean | undefined; + + /** + * Optionally, provide a function that will be called for each string. If the function returns `true` + * the string will not be included. + */ ignoreString?: IgnoreStringFunction; + + /** + * Processes the raw text of a comment. + * @param comment - The original text of the comment to process + * @param relativeFilePath - The relative file path + * @param stringName - The name of the string that the comment is for + * @returns The processed comment + */ processComment?: ( comment: string | undefined, - resxFilePath: string, + relativeFilePath: string, stringName: string ) => string | undefined; } @@ -33,36 +79,111 @@ export interface ITypingsGeneratorOptions extends ITypingsGeneratorBaseOptions { */ export class TypingsGenerator extends StringValuesTypingsGenerator { public constructor(options: ITypingsGeneratorOptions) { - const { ignoreString, processComment } = options; + const { + ignoreString, + processComment, + resxNewlineNormalization, + ignoreMissingResxComments, + trimmedJsonOutputFolders, + exportAsDefault + } = options; + const inferDefaultExportInterfaceNameFromFilename: boolean | undefined = + typeof exportAsDefault === 'object' + ? (exportAsDefault as IInferInterfaceNameExportAsDefaultOptions).inferInterfaceNameFromFilename + : undefined; + + const getJsonPaths: ((relativePath: string) => string[]) | undefined = + trimmedJsonOutputFolders && trimmedJsonOutputFolders.length > 0 + ? (relativePath: string): string[] => { + const jsonRelativePath: string = + relativePath.endsWith('.json') || relativePath.endsWith('.resjson') + ? relativePath + : `${relativePath}.json`; + + const jsonPaths: string[] = []; + for (const outputFolder of trimmedJsonOutputFolders) { + jsonPaths.push(`${outputFolder}/${jsonRelativePath}`); + } + return jsonPaths; + } + : undefined; + super({ ...options, fileExtensions: ['.resx', '.resx.json', '.loc.json', '.resjson'], - parseAndGenerateTypings: (fileContents: string, filePath: string, resxFilePath: string) => { + getAdditionalOutputFiles: getJsonPaths, + // eslint-disable-next-line @typescript-eslint/naming-convention + parseAndGenerateTypings: async ( + content: string, + filePath: string, + relativeFilePath: string + ): Promise => { const locFileData: ILocalizationFile = parseLocFile({ - filePath: filePath, - content: fileContents, - terminal: this._options.terminal!, - resxNewlineNormalization: options.resxNewlineNormalization, - ignoreMissingResxComments: options.ignoreMissingResxComments, + filePath, + content, + terminal: this.terminal, + resxNewlineNormalization, + ignoreMissingResxComments, ignoreString }); const typings: IStringValueTyping[] = []; - // eslint-disable-next-line guard-for-in - for (const stringName in locFileData) { - let comment: string | undefined = locFileData[stringName].comment; + const json: Record | undefined = trimmedJsonOutputFolders ? {} : undefined; + + for (const [stringName, value] of Object.entries(locFileData)) { + let comment: string | undefined = value.comment; if (processComment) { - comment = processComment(comment, resxFilePath, stringName); + comment = processComment(comment, relativeFilePath, stringName); + } + + if (json) { + json[stringName] = value.value; } typings.push({ exportName: stringName, - comment + comment, + sourcePosition: value.sourcePosition }); } - return { typings }; + if (getJsonPaths) { + const jsonBuffer: Buffer = Buffer.from(JSON.stringify(json), 'utf8'); + for (const jsonFile of getJsonPaths(relativeFilePath)) { + await FileSystem.writeFileAsync(jsonFile, jsonBuffer, { + ensureFolderExists: true + }); + } + } + + if (inferDefaultExportInterfaceNameFromFilename) { + const lastSlashIndex: number = Math.max(filePath.lastIndexOf('/'), filePath.lastIndexOf('\\')); + let extensionIndex: number = filePath.lastIndexOf('.'); + if (filePath.slice(extensionIndex).toLowerCase() === '.json') { + extensionIndex = filePath.lastIndexOf('.', extensionIndex - 1); + } + + const fileNameWithoutExtension: string = filePath.substring(lastSlashIndex + 1, extensionIndex); + const normalizedFileName: string = fileNameWithoutExtension.replace(/[^a-zA-Z0-9$_]/g, ''); + const firstCharUpperCased: string = normalizedFileName.charAt(0).toUpperCase(); + let interfaceName: string | undefined = `I${firstCharUpperCased}${normalizedFileName.slice(1)}`; + + if (!interfaceName.endsWith('strings') && !interfaceName.endsWith('Strings')) { + interfaceName += 'Strings'; + } + + return { + typings, + exportAsDefault: { + interfaceName + } + }; + } else { + return { + typings + }; + } } }); } diff --git a/libraries/localization-utilities/src/index.ts b/libraries/localization-utilities/src/index.ts index b8401bcae44..b72ad1bc3ee 100644 --- a/libraries/localization-utilities/src/index.ts +++ b/libraries/localization-utilities/src/index.ts @@ -16,7 +16,11 @@ export type { } from './interfaces'; export { parseLocJson } from './parsers/parseLocJson'; export { parseResJson } from './parsers/parseResJson'; -export { parseResx, IParseResxOptions, IParseResxOptionsBase } from './parsers/parseResx'; -export { parseLocFile, IParseLocFileOptions, ParserKind } from './LocFileParser'; -export { ITypingsGeneratorOptions, TypingsGenerator } from './TypingsGenerator'; +export { parseResx, type IParseResxOptions, type IParseResxOptionsBase } from './parsers/parseResx'; +export { parseLocFile, type IParseLocFileOptions, type ParserKind } from './LocFileParser'; +export { + type ITypingsGeneratorOptions, + type IInferInterfaceNameExportAsDefaultOptions, + TypingsGenerator +} from './TypingsGenerator'; export { getPseudolocalizer } from './Pseudolocalization'; diff --git a/libraries/localization-utilities/src/interfaces.ts b/libraries/localization-utilities/src/interfaces.ts index 94fecf12a22..4236dcd84b8 100644 --- a/libraries/localization-utilities/src/interfaces.ts +++ b/libraries/localization-utilities/src/interfaces.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import type { ISourcePosition } from '@rushstack/typings-generator'; + /** * Options for the pseudolocale library. * @@ -32,6 +34,13 @@ export interface ILocalizationFile { export interface ILocalizedString { value: string; comment?: string; + + /** + * The zero-based position of this string's declaration in the source file, when the parser is + * able to determine it. This is used to emit declaration source maps so that editors can + * navigate from generated typings back to the string declaration. + */ + sourcePosition?: ISourcePosition; } /** diff --git a/libraries/localization-utilities/src/parsers/parseLocJson.ts b/libraries/localization-utilities/src/parsers/parseLocJson.ts index 49edd7d1ba4..6070ca6d8f4 100644 --- a/libraries/localization-utilities/src/parsers/parseLocJson.ts +++ b/libraries/localization-utilities/src/parsers/parseLocJson.ts @@ -3,10 +3,10 @@ import { JsonFile, JsonSchema } from '@rushstack/node-core-library'; -import { ILocalizationFile, IParseFileOptions } from '../interfaces'; +import type { ILocalizationFile, IParseFileOptions } from '../interfaces'; +import locJsonSchema from '../schemas/locJson.schema.json'; -// Use `require` here to allow this package to be bundled with Webpack. -const LOC_JSON_SCHEMA: JsonSchema = JsonSchema.fromLoadedObject(require('../schemas/locJson.schema.json')); +const LOC_JSON_SCHEMA: JsonSchema = JsonSchema.fromLoadedObject(locJsonSchema); /** * @public @@ -14,21 +14,20 @@ const LOC_JSON_SCHEMA: JsonSchema = JsonSchema.fromLoadedObject(require('../sche export function parseLocJson({ content, filePath, ignoreString }: IParseFileOptions): ILocalizationFile { const parsedFile: ILocalizationFile = JsonFile.parseString(content); try { - LOC_JSON_SCHEMA.validateObject(parsedFile, filePath); + LOC_JSON_SCHEMA.validateObject(parsedFile, filePath, { ignoreSchemaField: true }); } catch (e) { throw new Error(`The loc file is invalid. Error: ${e}`); } - if (ignoreString) { - const newParsedFile: ILocalizationFile = {}; - for (const [key, stringData] of Object.entries(parsedFile)) { - if (!ignoreString(filePath, key)) { - newParsedFile[key] = stringData; - } + // Normalize file shape and possibly filter + const newParsedFile: ILocalizationFile = {}; + for (const [key, stringData] of Object.entries(parsedFile)) { + if (!ignoreString?.(filePath, key)) { + // Normalize entry shape. We allow the values to be plain strings as a format that can be handed + // off to webpack builds that don't understand the comment syntax. + newParsedFile[key] = typeof stringData === 'string' ? { value: stringData } : stringData; } - - return newParsedFile; - } else { - return parsedFile; } + + return newParsedFile; } diff --git a/libraries/localization-utilities/src/parsers/parseResJson.ts b/libraries/localization-utilities/src/parsers/parseResJson.ts index 289c7e2b3f7..a8752a222c2 100644 --- a/libraries/localization-utilities/src/parsers/parseResJson.ts +++ b/libraries/localization-utilities/src/parsers/parseResJson.ts @@ -3,7 +3,7 @@ import { JsonFile } from '@rushstack/node-core-library'; -import { ILocalizationFile, IParseFileOptions } from '../interfaces'; +import type { ILocalizationFile, IParseFileOptions } from '../interfaces'; /** * @public diff --git a/libraries/localization-utilities/src/parsers/parseResx.ts b/libraries/localization-utilities/src/parsers/parseResx.ts index fc7ff0ccf68..0ac353c4901 100644 --- a/libraries/localization-utilities/src/parsers/parseResx.ts +++ b/libraries/localization-utilities/src/parsers/parseResx.ts @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ITerminal, Text, NewlineKind } from '@rushstack/node-core-library'; -import { XmlDocument, XmlElement } from 'xmldoc'; +import { XmlDocument, type XmlElement } from 'xmldoc'; + +import { Text, type NewlineKind } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; import type { ILocalizedString, ILocalizationFile, IParseFileOptions } from '../interfaces'; @@ -202,7 +204,10 @@ function _readDataElement( return { value: value || '', - comment + comment, + // xmldoc's `column` refers to the end of the element's open tag rather than its start, so the + // beginning of the line is used instead. That reliably lands an editor on the element. + sourcePosition: { line: dataElement.line, column: 0 } }; } } diff --git a/libraries/localization-utilities/src/parsers/test/__snapshots__/parseLocJson.test.ts.snap b/libraries/localization-utilities/src/parsers/test/__snapshots__/parseLocJson.test.ts.snap index 8062e77c9f8..e55f88c34ad 100644 --- a/libraries/localization-utilities/src/parsers/test/__snapshots__/parseLocJson.test.ts.snap +++ b/libraries/localization-utilities/src/parsers/test/__snapshots__/parseLocJson.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`parseLocJson correctly ignores a string: Loc file 1`] = ` Object { @@ -22,6 +22,17 @@ Array [ ] `; +exports[`parseLocJson parses a file with raw strings 1`] = ` +Object { + "bar": Object { + "value": "Bar", + }, + "foo": Object { + "value": "Foo", + }, +} +`; + exports[`parseLocJson parses a valid file 1`] = ` Object { "bar": Object { @@ -40,5 +51,9 @@ exports[`parseLocJson throws on invalid file 1`] = ` test.loc.json Error: #/foo - Additional properties not allowed: baz" + must NOT have additional properties: baz +Error: #/foo + must be string +Error: #/foo + must match exactly one schema in oneOf" `; diff --git a/libraries/localization-utilities/src/parsers/test/__snapshots__/parseResJson.test.ts.snap b/libraries/localization-utilities/src/parsers/test/__snapshots__/parseResJson.test.ts.snap index 525d98680ef..e9489240f58 100644 --- a/libraries/localization-utilities/src/parsers/test/__snapshots__/parseResJson.test.ts.snap +++ b/libraries/localization-utilities/src/parsers/test/__snapshots__/parseResJson.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`parseResJson correctly ignores a string: Loc file 1`] = ` Object { diff --git a/libraries/localization-utilities/src/parsers/test/__snapshots__/parseResx.test.ts.snap b/libraries/localization-utilities/src/parsers/test/__snapshots__/parseResx.test.ts.snap index 477b13c81b5..96065d30ee9 100644 --- a/libraries/localization-utilities/src/parsers/test/__snapshots__/parseResx.test.ts.snap +++ b/libraries/localization-utilities/src/parsers/test/__snapshots__/parseResx.test.ts.snap @@ -1,9 +1,13 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`parseResx correctly ignores a string: Loc file 1`] = ` Object { "foo": Object { "comment": "Foo", + "sourcePosition": Object { + "column": 0, + "line": 57, + }, "value": "foo", }, } @@ -22,105 +26,141 @@ Array [ ] `; -exports[`parseResx correctly ignores a string: terminal output 1`] = `Object {}`; +exports[`parseResx correctly ignores a string: terminal output 1`] = `Array []`; exports[`parseResx fails to parse a RESX file with a duplicate string: Loc file 1`] = ` Object { "stringA": Object { "comment": undefined, + "sourcePosition": Object { + "column": 0, + "line": 5, + }, "value": "Another string", }, } `; exports[`parseResx fails to parse a RESX file with a duplicate string: terminal output 1`] = ` -Object { - "errorOutput": "test.resx(6,45): Duplicate string value \\"stringA\\"[n]", -} +Array [ + "[ error] test.resx(6,45): Duplicate string value \\"stringA\\"[n]", +] `; exports[`parseResx ignoreMissingResxComments when set to false, warns on a missing comment: Loc file 1`] = ` Object { "stringWithoutAComment": Object { "comment": undefined, + "sourcePosition": Object { + "column": 0, + "line": 2, + }, "value": "String without a comment", }, } `; exports[`parseResx ignoreMissingResxComments when set to false, warns on a missing comment: terminal output 1`] = ` -Object { - "warningOutput": "test.resx(3,59): Missing string comment in element[n]", -} +Array [ + "[warning] test.resx(3,59): Missing string comment in element[n]", +] `; exports[`parseResx ignoreMissingResxComments when set to true, ignores a missing comment: Loc file 1`] = ` Object { "stringWithoutAComment": Object { "comment": undefined, + "sourcePosition": Object { + "column": 0, + "line": 2, + }, "value": "String without a comment", }, } `; -exports[`parseResx ignoreMissingResxComments when set to true, ignores a missing comment: terminal output 1`] = `Object {}`; +exports[`parseResx ignoreMissingResxComments when set to true, ignores a missing comment: terminal output 1`] = `Array []`; exports[`parseResx ignoreMissingResxComments when set to undefined, warns on a missing comment: Loc file 1`] = ` Object { "stringWithoutAComment": Object { "comment": undefined, + "sourcePosition": Object { + "column": 0, + "line": 2, + }, "value": "String without a comment", }, } `; -exports[`parseResx ignoreMissingResxComments when set to undefined, warns on a missing comment: terminal output 1`] = `Object {}`; +exports[`parseResx ignoreMissingResxComments when set to undefined, warns on a missing comment: terminal output 1`] = `Array []`; exports[`parseResx parses a valid file with a schema: Loc file 1`] = ` Object { "bar": Object { "comment": "Bar", + "sourcePosition": Object { + "column": 0, + "line": 61, + }, "value": "bar", }, "foo": Object { "comment": "Foo", + "sourcePosition": Object { + "column": 0, + "line": 57, + }, "value": "foo", }, } `; -exports[`parseResx parses a valid file with a schema: terminal output 1`] = `Object {}`; +exports[`parseResx parses a valid file with a schema: terminal output 1`] = `Array []`; exports[`parseResx parses a valid file with quotemarks: Loc file 1`] = ` Object { "stringWithQuotes": Object { "comment": "RESX string with quotemarks", + "sourcePosition": Object { + "column": 0, + "line": 2, + }, "value": "\\"RESX string with quotemarks\\"", }, } `; -exports[`parseResx parses a valid file with quotemarks: terminal output 1`] = `Object {}`; +exports[`parseResx parses a valid file with quotemarks: terminal output 1`] = `Array []`; exports[`parseResx prints an error on invalid XML: Loc file 1`] = ` Object { "foo": Object { "comment": "Foo", + "sourcePosition": Object { + "column": 0, + "line": 2, + }, "value": "foo", }, } `; exports[`parseResx prints an error on invalid XML: terminal output 1`] = ` -Object { - "errorOutput": "test.resx(3,41): Found unexpected non-empty text node in RESX element[n]", -} +Array [ + "[ error] test.resx(3,41): Found unexpected non-empty text node in RESX element[n]", +] `; exports[`parseResx resxNewlineNormalization when set to CrLf, normalizes to CrLf: Loc file 1`] = ` Object { "stringWithTabsAndNewlines": Object { "comment": "RESX string with newlines and tabs", + "sourcePosition": Object { + "column": 0, + "line": 2, + }, "value": " RESX string with newlines and tabs ", @@ -128,12 +168,16 @@ Object { } `; -exports[`parseResx resxNewlineNormalization when set to CrLf, normalizes to CrLf: terminal output 1`] = `Object {}`; +exports[`parseResx resxNewlineNormalization when set to CrLf, normalizes to CrLf: terminal output 1`] = `Array []`; exports[`parseResx resxNewlineNormalization when set to Lf, normalizes to Lf: Loc file 1`] = ` Object { "stringWithTabsAndNewlines": Object { "comment": "RESX string with newlines and tabs", + "sourcePosition": Object { + "column": 0, + "line": 2, + }, "value": " RESX string with newlines and tabs ", @@ -141,4 +185,4 @@ Object { } `; -exports[`parseResx resxNewlineNormalization when set to Lf, normalizes to Lf: terminal output 1`] = `Object {}`; +exports[`parseResx resxNewlineNormalization when set to Lf, normalizes to Lf: terminal output 1`] = `Array []`; diff --git a/libraries/localization-utilities/src/parsers/test/parseLocJson.test.ts b/libraries/localization-utilities/src/parsers/test/parseLocJson.test.ts index 8687a53665a..f1308b303f9 100644 --- a/libraries/localization-utilities/src/parsers/test/parseLocJson.test.ts +++ b/libraries/localization-utilities/src/parsers/test/parseLocJson.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IgnoreStringFunction } from '../../interfaces'; +import type { IgnoreStringFunction } from '../../interfaces'; import { parseLocJson } from '../parseLocJson'; describe(parseLocJson.name, () => { @@ -25,6 +25,20 @@ describe(parseLocJson.name, () => { ).toMatchSnapshot(); }); + it('parses a file with raw strings', () => { + const content: string = JSON.stringify({ + foo: 'Foo', + bar: 'Bar' + }); + + expect( + parseLocJson({ + content, + filePath: 'test.loc.json' + }) + ).toMatchSnapshot(); + }); + it('throws on invalid file', () => { const content: string = JSON.stringify({ foo: { diff --git a/libraries/localization-utilities/src/parsers/test/parseResJson.test.ts b/libraries/localization-utilities/src/parsers/test/parseResJson.test.ts index ab5dffdaad3..a59a3951df0 100644 --- a/libraries/localization-utilities/src/parsers/test/parseResJson.test.ts +++ b/libraries/localization-utilities/src/parsers/test/parseResJson.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IgnoreStringFunction } from '../../interfaces'; +import type { IgnoreStringFunction } from '../../interfaces'; import { parseResJson } from '../parseResJson'; describe(parseResJson.name, () => { diff --git a/libraries/localization-utilities/src/parsers/test/parseResx.test.ts b/libraries/localization-utilities/src/parsers/test/parseResx.test.ts index d236bc1b105..7a241d54cc5 100644 --- a/libraries/localization-utilities/src/parsers/test/parseResx.test.ts +++ b/libraries/localization-utilities/src/parsers/test/parseResx.test.ts @@ -1,14 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { - FileSystem, - NewlineKind, - StringBufferTerminalProvider, - Terminal -} from '@rushstack/node-core-library'; -import { IgnoreStringFunction } from '../../interfaces'; -import { IParseResxOptions, parseResx } from '../parseResx'; +import { FileSystem, NewlineKind } from '@rushstack/node-core-library'; +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; +import type { IgnoreStringFunction } from '../../interfaces'; +import { type IParseResxOptions, parseResx } from '../parseResx'; describe(parseResx.name, () => { let terminalProvider: StringBufferTerminalProvider; @@ -20,34 +16,7 @@ describe(parseResx.name, () => { }); afterEach(() => { - const outputObject: Record = {}; - - const output: string = terminalProvider.getOutput(); - if (output) { - outputObject.output = output; - } - - const verboseOutput: string = terminalProvider.getVerbose(); - if (verboseOutput) { - outputObject.verboseOutput = verboseOutput; - } - - const errorOutput: string = terminalProvider.getErrorOutput(); - if (errorOutput) { - outputObject.errorOutput = errorOutput; - } - - const warningOutput: string = terminalProvider.getWarningOutput(); - if (warningOutput) { - outputObject.warningOutput = warningOutput; - } - - const debugOutput: string = terminalProvider.getDebugOutput(); - if (debugOutput) { - outputObject.debugOutput = debugOutput; - } - - expect(outputObject).toMatchSnapshot('terminal output'); + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot('terminal output'); }); async function testResxAsync( diff --git a/libraries/localization-utilities/src/schemas/locJson.schema.json b/libraries/localization-utilities/src/schemas/locJson.schema.json index a407d34bf33..8c60e7573cb 100644 --- a/libraries/localization-utilities/src/schemas/locJson.schema.json +++ b/libraries/localization-utilities/src/schemas/locJson.schema.json @@ -2,25 +2,26 @@ "$schema": "http://json-schema.org/draft-04/schema#", "title": "Localizable JSON file", - "properties": { - "$schema": { - "description": "Part of the JSON Schema standard, this optional keyword declares the URL of the schema that the file conforms to. Editors may download the schema and use it to perform syntax highlighting.", - "type": "string" - } - }, "patternProperties": { - "^[A-Za-z_][0-9A-Za-z_]*$": { - "type": "object", - "properties": { - "value": { - "type": "string" + "^[A-Za-z_$][0-9A-Za-z_$]*$": { + "oneOf": [ + { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "comment": { + "type": "string" + } + }, + "additionalProperties": false, + "required": ["value"] }, - "comment": { + { "type": "string" } - }, - "additionalProperties": false, - "required": ["value"] + ] } }, "additionalProperties": false, diff --git a/libraries/localization-utilities/src/test/__snapshots__/Pseudolocalization.test.ts.snap b/libraries/localization-utilities/src/test/__snapshots__/Pseudolocalization.test.ts.snap index 66a2c2a53a8..d086c6c0f9d 100644 --- a/libraries/localization-utilities/src/test/__snapshots__/Pseudolocalization.test.ts.snap +++ b/libraries/localization-utilities/src/test/__snapshots__/Pseudolocalization.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`getPseudolocalizer gets distinct pseudolocalizers: bar 1`] = `"-Bar-ţēxţ-Bar-"`; diff --git a/libraries/localization-utilities/tsconfig.json b/libraries/localization-utilities/tsconfig.json index c7be49eac49..7902d0431ea 100644 --- a/libraries/localization-utilities/tsconfig.json +++ b/libraries/localization-utilities/tsconfig.json @@ -1,7 +1,6 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "target": "ES2019", - "types": ["heft-jest", "node"] + "target": "ES2019" } } diff --git a/libraries/lookup-by-path/.npmignore b/libraries/lookup-by-path/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/libraries/lookup-by-path/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/lookup-by-path/CHANGELOG.json b/libraries/lookup-by-path/CHANGELOG.json new file mode 100644 index 00000000000..d5333092da5 --- /dev/null +++ b/libraries/lookup-by-path/CHANGELOG.json @@ -0,0 +1,1144 @@ +{ + "name": "@rushstack/lookup-by-path", + "entries": [ + { + "version": "0.10.11", + "tag": "@rushstack/lookup-by-path_v0.10.11", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "0.10.10", + "tag": "@rushstack/lookup-by-path_v0.10.10", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "0.10.9", + "tag": "@rushstack/lookup-by-path_v0.10.9", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "0.10.8", + "tag": "@rushstack/lookup-by-path_v0.10.8", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "0.10.7", + "tag": "@rushstack/lookup-by-path_v0.10.7", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "0.10.6", + "tag": "@rushstack/lookup-by-path_v0.10.6", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "0.10.5", + "tag": "@rushstack/lookup-by-path_v0.10.5", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "0.10.4", + "tag": "@rushstack/lookup-by-path_v0.10.4", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "0.10.3", + "tag": "@rushstack/lookup-by-path_v0.10.3", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "0.10.2", + "tag": "@rushstack/lookup-by-path_v0.10.2", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "0.10.1", + "tag": "@rushstack/lookup-by-path_v0.10.1", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "0.10.0", + "tag": "@rushstack/lookup-by-path_v0.10.0", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "minor": [ + { + "comment": "Add `toJson`/`fromJson` methods to `LookupByPath` for serialization/deserialization" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "0.9.10", + "tag": "@rushstack/lookup-by-path_v0.9.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "0.9.9", + "tag": "@rushstack/lookup-by-path_v0.9.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "0.9.8", + "tag": "@rushstack/lookup-by-path_v0.9.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "0.9.7", + "tag": "@rushstack/lookup-by-path_v0.9.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "0.9.6", + "tag": "@rushstack/lookup-by-path_v0.9.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "0.9.5", + "tag": "@rushstack/lookup-by-path_v0.9.5", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "0.9.4", + "tag": "@rushstack/lookup-by-path_v0.9.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "0.9.3", + "tag": "@rushstack/lookup-by-path_v0.9.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "0.9.2", + "tag": "@rushstack/lookup-by-path_v0.9.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "0.9.1", + "tag": "@rushstack/lookup-by-path_v0.9.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "0.9.0", + "tag": "@rushstack/lookup-by-path_v0.9.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "0.8.16", + "tag": "@rushstack/lookup-by-path_v0.8.16", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "0.8.15", + "tag": "@rushstack/lookup-by-path_v0.8.15", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "0.8.14", + "tag": "@rushstack/lookup-by-path_v0.8.14", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "0.8.13", + "tag": "@rushstack/lookup-by-path_v0.8.13", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "0.8.12", + "tag": "@rushstack/lookup-by-path_v0.8.12", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "0.8.11", + "tag": "@rushstack/lookup-by-path_v0.8.11", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "0.8.10", + "tag": "@rushstack/lookup-by-path_v0.8.10", + "date": "Mon, 05 Jan 2026 16:12:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "0.8.9", + "tag": "@rushstack/lookup-by-path_v0.8.9", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + } + ] + } + }, + { + "version": "0.8.8", + "tag": "@rushstack/lookup-by-path_v0.8.8", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + } + ] + } + }, + { + "version": "0.8.7", + "tag": "@rushstack/lookup-by-path_v0.8.7", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + } + ] + } + }, + { + "version": "0.8.6", + "tag": "@rushstack/lookup-by-path_v0.8.6", + "date": "Tue, 04 Nov 2025 08:15:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + } + ] + } + }, + { + "version": "0.8.5", + "tag": "@rushstack/lookup-by-path_v0.8.5", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + } + ] + } + }, + { + "version": "0.8.4", + "tag": "@rushstack/lookup-by-path_v0.8.4", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + } + ] + } + }, + { + "version": "0.8.3", + "tag": "@rushstack/lookup-by-path_v0.8.3", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + } + ] + } + }, + { + "version": "0.8.2", + "tag": "@rushstack/lookup-by-path_v0.8.2", + "date": "Fri, 03 Oct 2025 20:10:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + } + ] + } + }, + { + "version": "0.8.1", + "tag": "@rushstack/lookup-by-path_v0.8.1", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + } + ] + } + }, + { + "version": "0.8.0", + "tag": "@rushstack/lookup-by-path_v0.8.0", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "minor": [ + { + "comment": "Expose `getNodeAtPrefix` API to allow getting nodes with undefined values." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + } + ] + } + }, + { + "version": "0.7.6", + "tag": "@rushstack/lookup-by-path_v0.7.6", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + } + ] + } + }, + { + "version": "0.7.5", + "tag": "@rushstack/lookup-by-path_v0.7.5", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + } + ] + } + }, + { + "version": "0.7.4", + "tag": "@rushstack/lookup-by-path_v0.7.4", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + } + ] + } + }, + { + "version": "0.7.3", + "tag": "@rushstack/lookup-by-path_v0.7.3", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + } + ] + } + }, + { + "version": "0.7.2", + "tag": "@rushstack/lookup-by-path_v0.7.2", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + } + ] + } + }, + { + "version": "0.7.1", + "tag": "@rushstack/lookup-by-path_v0.7.1", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + } + ] + } + }, + { + "version": "0.7.0", + "tag": "@rushstack/lookup-by-path_v0.7.0", + "date": "Tue, 13 May 2025 20:32:55 GMT", + "comments": { + "minor": [ + { + "comment": "Add `deleteSubtree` method." + } + ] + } + }, + { + "version": "0.6.1", + "tag": "@rushstack/lookup-by-path_v0.6.1", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + } + ] + } + }, + { + "version": "0.6.0", + "tag": "@rushstack/lookup-by-path_v0.6.0", + "date": "Thu, 08 May 2025 00:11:15 GMT", + "comments": { + "minor": [ + { + "comment": "Add `getFirstDifferenceInCommonNodes` API." + }, + { + "comment": "Expose `tree` accessor on `IReadonlyLookupByPath` for a readonly view of the raw tree." + } + ] + } + }, + { + "version": "0.5.23", + "tag": "@rushstack/lookup-by-path_v0.5.23", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + } + ] + } + }, + { + "version": "0.5.22", + "tag": "@rushstack/lookup-by-path_v0.5.22", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + } + ] + } + }, + { + "version": "0.5.21", + "tag": "@rushstack/lookup-by-path_v0.5.21", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + } + ] + } + }, + { + "version": "0.5.20", + "tag": "@rushstack/lookup-by-path_v0.5.20", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + } + ] + } + }, + { + "version": "0.5.19", + "tag": "@rushstack/lookup-by-path_v0.5.19", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + } + ] + } + }, + { + "version": "0.5.18", + "tag": "@rushstack/lookup-by-path_v0.5.18", + "date": "Tue, 15 Apr 2025 15:11:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + } + ] + } + }, + { + "version": "0.5.17", + "tag": "@rushstack/lookup-by-path_v0.5.17", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + } + ] + } + }, + { + "version": "0.5.16", + "tag": "@rushstack/lookup-by-path_v0.5.16", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + } + ] + } + }, + { + "version": "0.5.15", + "tag": "@rushstack/lookup-by-path_v0.5.15", + "date": "Tue, 25 Mar 2025 15:11:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + } + ] + } + }, + { + "version": "0.5.14", + "tag": "@rushstack/lookup-by-path_v0.5.14", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + } + ] + } + }, + { + "version": "0.5.13", + "tag": "@rushstack/lookup-by-path_v0.5.13", + "date": "Wed, 12 Mar 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + } + ] + } + }, + { + "version": "0.5.12", + "tag": "@rushstack/lookup-by-path_v0.5.12", + "date": "Tue, 11 Mar 2025 02:12:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + } + ] + } + }, + { + "version": "0.5.11", + "tag": "@rushstack/lookup-by-path_v0.5.11", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + } + ] + } + }, + { + "version": "0.5.10", + "tag": "@rushstack/lookup-by-path_v0.5.10", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + } + ] + } + }, + { + "version": "0.5.9", + "tag": "@rushstack/lookup-by-path_v0.5.9", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + } + ] + } + }, + { + "version": "0.5.8", + "tag": "@rushstack/lookup-by-path_v0.5.8", + "date": "Wed, 26 Feb 2025 16:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + } + ] + } + }, + { + "version": "0.5.7", + "tag": "@rushstack/lookup-by-path_v0.5.7", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + } + ] + } + }, + { + "version": "0.5.6", + "tag": "@rushstack/lookup-by-path_v0.5.6", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + } + ] + } + }, + { + "version": "0.5.5", + "tag": "@rushstack/lookup-by-path_v0.5.5", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + } + ] + } + }, + { + "version": "0.5.4", + "tag": "@rushstack/lookup-by-path_v0.5.4", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + } + ] + } + }, + { + "version": "0.5.3", + "tag": "@rushstack/lookup-by-path_v0.5.3", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + } + ] + } + }, + { + "version": "0.5.2", + "tag": "@rushstack/lookup-by-path_v0.5.2", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + } + ] + } + }, + { + "version": "0.5.1", + "tag": "@rushstack/lookup-by-path_v0.5.1", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + } + ] + } + }, + { + "version": "0.5.0", + "tag": "@rushstack/lookup-by-path_v0.5.0", + "date": "Wed, 18 Dec 2024 01:11:33 GMT", + "comments": { + "minor": [ + { + "comment": "Update all methods to accept optional override delimiters. Add `size`, `entries(), `get()`, `has()`, `removeItem()`. Make class iterable.\nExplicitly exclude `undefined` and `null` from the allowed types for the type parameter `TItem`." + } + ] + } + }, + { + "version": "0.4.7", + "tag": "@rushstack/lookup-by-path_v0.4.7", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + } + ] + } + }, + { + "version": "0.4.6", + "tag": "@rushstack/lookup-by-path_v0.4.6", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + } + ] + } + }, + { + "version": "0.4.5", + "tag": "@rushstack/lookup-by-path_v0.4.5", + "date": "Tue, 03 Dec 2024 16:11:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + } + ] + } + }, + { + "version": "0.4.4", + "tag": "@rushstack/lookup-by-path_v0.4.4", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + } + ] + } + }, + { + "version": "0.4.3", + "tag": "@rushstack/lookup-by-path_v0.4.3", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + } + ] + } + }, + { + "version": "0.4.2", + "tag": "@rushstack/lookup-by-path_v0.4.2", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + } + ] + } + }, + { + "version": "0.4.1", + "tag": "@rushstack/lookup-by-path_v0.4.1", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + } + ] + } + }, + { + "version": "0.4.0", + "tag": "@rushstack/lookup-by-path_v0.4.0", + "date": "Thu, 17 Oct 2024 20:25:42 GMT", + "comments": { + "minor": [ + { + "comment": "Add `IReadonlyLookupByPath` interface to help unit tests for functions that consume `LookupByPath`." + } + ] + } + }, + { + "version": "0.3.2", + "tag": "@rushstack/lookup-by-path_v0.3.2", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + } + ] + } + }, + { + "version": "0.3.1", + "tag": "@rushstack/lookup-by-path_v0.3.1", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + } + ] + } + }, + { + "version": "0.3.0", + "tag": "@rushstack/lookup-by-path_v0.3.0", + "date": "Thu, 03 Oct 2024 15:11:00 GMT", + "comments": { + "minor": [ + { + "comment": "Allow for a map of file paths to arbitrary info to be grouped by the nearest entry in the LookupByPath trie" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/lookup-by-path_v0.2.5", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/lookup-by-path_v0.2.4", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/lookup-by-path_v0.2.3", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/lookup-by-path_v0.2.2", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/lookup-by-path_v0.2.1", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/lookup-by-path_v0.2.0", + "date": "Tue, 27 Aug 2024 15:12:33 GMT", + "comments": { + "minor": [ + { + "comment": "Return a linked list of matches in `findLongestPrefixMatch` in the event that multiple prefixes match. The head of the list is the most specific match." + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/lookup-by-path_v0.1.2", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/lookup-by-path_v0.1.1", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/lookup-by-path_v0.1.0", + "date": "Thu, 08 Aug 2024 22:08:25 GMT", + "comments": { + "minor": [ + { + "comment": "Extract LookupByPath from @rushstack/rush-lib." + } + ] + } + } + ] +} diff --git a/libraries/lookup-by-path/CHANGELOG.md b/libraries/lookup-by-path/CHANGELOG.md new file mode 100644 index 00000000000..f8da68507c7 --- /dev/null +++ b/libraries/lookup-by-path/CHANGELOG.md @@ -0,0 +1,493 @@ +# Change Log - @rushstack/lookup-by-path + +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 0.10.11 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 0.10.10 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 0.10.9 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.10.8 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.10.7 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 0.10.6 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.10.5 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 0.10.4 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 0.10.3 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 0.10.2 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.10.1 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 0.10.0 +Thu, 09 Apr 2026 00:15:07 GMT + +### Minor changes + +- Add `toJson`/`fromJson` methods to `LookupByPath` for serialization/deserialization + +## 0.9.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 0.9.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 0.9.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.9.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 0.9.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.9.5 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 0.9.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.9.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 0.9.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 0.9.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.9.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.8.16 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 0.8.15 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 0.8.14 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 0.8.13 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 0.8.12 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 0.8.11 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 0.8.10 +Mon, 05 Jan 2026 16:12:50 GMT + +_Version update only_ + +## 0.8.9 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 0.8.8 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 0.8.7 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 0.8.6 +Tue, 04 Nov 2025 08:15:15 GMT + +_Version update only_ + +## 0.8.5 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 0.8.4 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 0.8.3 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 0.8.2 +Fri, 03 Oct 2025 20:10:00 GMT + +_Version update only_ + +## 0.8.1 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 0.8.0 +Tue, 30 Sep 2025 20:33:51 GMT + +### Minor changes + +- Expose `getNodeAtPrefix` API to allow getting nodes with undefined values. + +## 0.7.6 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.7.5 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.7.4 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.7.3 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 0.7.2 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.7.1 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.7.0 +Tue, 13 May 2025 20:32:55 GMT + +### Minor changes + +- Add `deleteSubtree` method. + +## 0.6.1 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.6.0 +Thu, 08 May 2025 00:11:15 GMT + +### Minor changes + +- Add `getFirstDifferenceInCommonNodes` API. +- Expose `tree` accessor on `IReadonlyLookupByPath` for a readonly view of the raw tree. + +## 0.5.23 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.5.22 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.5.21 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.5.20 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.5.19 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.5.18 +Tue, 15 Apr 2025 15:11:58 GMT + +_Version update only_ + +## 0.5.17 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 0.5.16 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.5.15 +Tue, 25 Mar 2025 15:11:16 GMT + +_Version update only_ + +## 0.5.14 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.5.13 +Wed, 12 Mar 2025 00:11:32 GMT + +_Version update only_ + +## 0.5.12 +Tue, 11 Mar 2025 02:12:34 GMT + +_Version update only_ + +## 0.5.11 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.5.10 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.5.9 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.5.8 +Wed, 26 Feb 2025 16:11:12 GMT + +_Version update only_ + +## 0.5.7 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.5.6 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.5.5 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.5.4 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 0.5.3 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.5.2 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.5.1 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.5.0 +Wed, 18 Dec 2024 01:11:33 GMT + +### Minor changes + +- Update all methods to accept optional override delimiters. Add `size`, `entries(), `get()`, `has()`, `removeItem()`. Make class iterable. +Explicitly exclude `undefined` and `null` from the allowed types for the type parameter `TItem`. + +## 0.4.7 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.4.6 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.4.5 +Tue, 03 Dec 2024 16:11:08 GMT + +_Version update only_ + +## 0.4.4 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.4.3 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.4.2 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.4.1 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.4.0 +Thu, 17 Oct 2024 20:25:42 GMT + +### Minor changes + +- Add `IReadonlyLookupByPath` interface to help unit tests for functions that consume `LookupByPath`. + +## 0.3.2 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.3.1 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.3.0 +Thu, 03 Oct 2024 15:11:00 GMT + +### Minor changes + +- Allow for a map of file paths to arbitrary info to be grouped by the nearest entry in the LookupByPath trie + +## 0.2.5 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.2.4 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.2.3 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.2.2 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.2.1 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.2.0 +Tue, 27 Aug 2024 15:12:33 GMT + +### Minor changes + +- Return a linked list of matches in `findLongestPrefixMatch` in the event that multiple prefixes match. The head of the list is the most specific match. + +## 0.1.2 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.1.1 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.1.0 +Thu, 08 Aug 2024 22:08:25 GMT + +### Minor changes + +- Extract LookupByPath from @rushstack/rush-lib. + diff --git a/libraries/lookup-by-path/LICENSE b/libraries/lookup-by-path/LICENSE new file mode 100644 index 00000000000..ad73d857028 --- /dev/null +++ b/libraries/lookup-by-path/LICENSE @@ -0,0 +1,24 @@ +@rushstack/lookup-by-path + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/libraries/lookup-by-path/README.md b/libraries/lookup-by-path/README.md new file mode 100644 index 00000000000..d450300ed73 --- /dev/null +++ b/libraries/lookup-by-path/README.md @@ -0,0 +1,14 @@ +# @rushstack/lookup-by-path + +This library contains a strongly-typed implementation of of a [Trie](https://en.wikipedia.org/wiki/Trie) (a.k.a. prefix tree) data structure optimized for file paths and URLs. + +This package is used by Rush to associate Git hashes with their nearest ancestor Rush project, for example. + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/libraries/lookup-by-path/CHANGELOG.md) - Find + out what's new in the latest version +- [API Reference](https://api.rushstack.io/pages/lookup-by-path/) + +`@rushstack/lookup-by-path` is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/libraries/lookup-by-path/config/api-extractor.json b/libraries/lookup-by-path/config/api-extractor.json new file mode 100644 index 00000000000..3dbb76c0e6f --- /dev/null +++ b/libraries/lookup-by-path/config/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json" +} diff --git a/libraries/lookup-by-path/config/jest.config.json b/libraries/lookup-by-path/config/jest.config.json new file mode 100644 index 00000000000..d1749681d90 --- /dev/null +++ b/libraries/lookup-by-path/config/jest.config.json @@ -0,0 +1,3 @@ +{ + "extends": "local-node-rig/profiles/default/config/jest.config.json" +} diff --git a/libraries/lookup-by-path/config/rig.json b/libraries/lookup-by-path/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/libraries/lookup-by-path/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/libraries/lookup-by-path/eslint.config.js b/libraries/lookup-by-path/eslint.config.js new file mode 100644 index 00000000000..87132f43292 --- /dev/null +++ b/libraries/lookup-by-path/eslint.config.js @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); +const tsdocMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/tsdoc'); + +module.exports = [ + ...nodeProfile, + ...friendlyLocalsMixin, + ...tsdocMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/lookup-by-path/package.json b/libraries/lookup-by-path/package.json new file mode 100644 index 00000000000..65133623e5b --- /dev/null +++ b/libraries/lookup-by-path/package.json @@ -0,0 +1,62 @@ +{ + "name": "@rushstack/lookup-by-path", + "version": "0.10.11", + "description": "Strongly typed trie data structure for path and URL-like strings.", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/lookup-by-path.d.ts", + "exports": { + ".": { + "types": "./dist/lookup-by-path.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "keywords": [ + "trie", + "path", + "url", + "radix tree", + "prefix tree" + ], + "license": "MIT", + "repository": { + "url": "https://github.com/microsoft/rushstack.git", + "type": "git", + "directory": "libraries/lookup-by-path" + }, + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + }, + "sideEffects": false +} diff --git a/libraries/lookup-by-path/src/LookupByPath.ts b/libraries/lookup-by-path/src/LookupByPath.ts new file mode 100644 index 00000000000..638cf7274e2 --- /dev/null +++ b/libraries/lookup-by-path/src/LookupByPath.ts @@ -0,0 +1,763 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * A node in the path trie used in LookupByPath + */ +interface IPathTrieNode { + /** + * The value that exactly matches the current relative path + */ + value: TItem | undefined; + + /** + * Child nodes by subfolder + */ + children: Map> | undefined; +} + +/** + * Readonly view of a node in the path trie used in LookupByPath + * + * @remarks + * This interface is used to facilitate parallel traversals for comparing two `LookupByPath` instances. + * + * @beta + */ +export interface IReadonlyPathTrieNode { + /** + * The value that exactly matches the current relative path + */ + readonly value: TItem | undefined; + + /** + * Child nodes by subfolder + */ + readonly children: ReadonlyMap> | undefined; +} + +/** + * JSON-serializable representation of a node in a {@link LookupByPath} trie. + * + * @beta + */ +export interface ISerializedPathTrieNode { + /** + * Index into the `values` array of the containing {@link ILookupByPathJson}. + * If `undefined`, this node has no associated value. + */ + valueIndex?: number; + + /** + * Child nodes keyed by path segment. + */ + children?: Record; +} + +/** + * JSON-serializable representation of a {@link LookupByPath} instance. + * + * @remarks + * The `values` array stores each unique value exactly once (by reference identity). + * Nodes in the tree reference values by their index in this array, which ensures that + * reference equality is preserved across serialization and deserialization. + * + * @beta + */ +export interface ILookupByPathJson { + /** + * The path delimiter used by the serialized trie. + */ + delimiter: string; + + /** + * Array of serialized values. Nodes in the tree reference values by their index in this array. + * Using an array with index-based references preserves reference equality: if multiple nodes + * share the same value (by reference), they will reference the same index. + */ + values: TSerialized[]; + + /** + * The serialized tree structure. + */ + tree: ISerializedPathTrieNode; +} + +interface IPrefixEntry { + /** + * The prefix that was matched + */ + prefix: string; + + /** + * The index of the first character after the matched prefix + */ + index: number; +} + +/** + * Object containing both the matched item and the start index of the remainder of the query. + * + * @beta + */ +export interface IPrefixMatch { + /** + * The item that matched the prefix + */ + value: TItem; + + /** + * The index of the first character after the matched prefix + */ + index: number; + + /** + * The last match found (with a shorter prefix), if any + */ + lastMatch?: IPrefixMatch; +} + +/** + * The readonly component of `LookupByPath`, to simplify unit testing. + * + * @beta + */ +export interface IReadonlyLookupByPath extends Iterable<[string, TItem]> { + /** + * Searches for the item associated with `childPath`, or the nearest ancestor of that path that + * has an associated item. + * + * @returns the found item, or `undefined` if no item was found + * + * @example + * ```ts + * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]); + * trie.findChildPath('foo/baz'); // returns 1 + * trie.findChildPath('foo/bar/baz'); // returns 2 + * ``` + */ + findChildPath(childPath: string, delimiter?: string): TItem | undefined; + + /** + * Searches for the item for which the recorded prefix is the longest matching prefix of `query`. + * Obtains both the item and the length of the matched prefix, so that the remainder of the path can be + * extracted. + * + * @returns the found item and the length of the matched prefix, or `undefined` if no item was found + * + * @example + * ```ts + * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]); + * trie.findLongestPrefixMatch('foo/baz'); // returns { item: 1, index: 3 } + * trie.findLongestPrefixMatch('foo/bar/baz'); // returns { item: 2, index: 7 } + * ``` + */ + findLongestPrefixMatch(query: string, delimiter?: string): IPrefixMatch | undefined; + + /** + * Searches for the item associated with `childPathSegments`, or the nearest ancestor of that path that + * has an associated item. + * + * @returns the found item, or `undefined` if no item was found + * + * @example + * ```ts + * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]); + * trie.findChildPathFromSegments(['foo', 'baz']); // returns 1 + * trie.findChildPathFromSegments(['foo','bar', 'baz']); // returns 2 + * ``` + */ + findChildPathFromSegments(childPathSegments: Iterable): TItem | undefined; + + /** + * Determines if an entry exists exactly at the specified path. + * + * @returns `true` if an entry exists at the specified path, `false` otherwise + */ + has(query: string, delimiter?: string): boolean; + + /** + * Retrieves the entry that exists exactly at the specified path, if any. + * + * @returns The entry that exists exactly at the specified path, or `undefined` if no entry exists. + */ + get(query: string, delimiter?: string): TItem | undefined; + + /** + * Gets the number of entries in this trie. + * + * @returns The number of entries in this trie. + */ + get size(): number; + + /** + * @returns The root node of the trie, corresponding to the path '' + */ + get tree(): IReadonlyPathTrieNode; + + /** + * Iterates over the entries in this trie. + * + * @param query - An optional query. If specified only entries that start with the query will be returned. + * + * @returns An iterator over the entries under the specified query (or the root if no query is specified). + * @remarks + * Keys in the returned iterator use the provided delimiter to join segments. + * Iteration order is not specified. + * @example + * ```ts + * const trie = new LookupByPath([['foo', 1], ['foo/bar', 2]]); + * [...trie.entries(undefined, ',')); // returns [['foo', 1], ['foo,bar', 2]] + * ``` + */ + entries(query?: string, delimiter?: string): IterableIterator<[string, TItem]>; + + /** + * Iterates over the entries in this trie. + * + * @param query - An optional query. If specified only entries that start with the query will be returned. + * + * @returns An iterator over the entries under the specified query (or the root if no query is specified). + * @remarks + * Keys in the returned iterator use the provided delimiter to join segments. + * Iteration order is not specified. + */ + [Symbol.iterator](query?: string, delimiter?: string): IterableIterator<[string, TItem]>; + + /** + * Groups the provided map of info by the nearest entry in the trie that contains the path. If the path + * is not found in the trie, the info is ignored. + * + * @returns The grouped info, grouped by the nearest entry in the trie that contains the path + * + * @param infoByPath - The info to be grouped, keyed by path + */ + groupByChild(infoByPath: Map, delimiter?: string): Map>; + + /** + * Retrieves the trie node at the specified prefix, if it exists. + * + * @param query - The prefix to check for + * @param delimiter - The path delimiter + * @returns The trie node at the specified prefix, or `undefined` if no node was found + */ + getNodeAtPrefix(query: string, delimiter?: string): IReadonlyPathTrieNode | undefined; + + /** + * Serializes this `LookupByPath` instance to a JSON-compatible representation. + * + * @param serializeValue - A function that converts a value of type `TItem` to a JSON-serializable form. + * @returns A JSON-serializable representation of this trie. + * + * @remarks + * Values that are reference-equal will be serialized once and referenced by index, ensuring + * that reference equality is preserved when deserialized via {@link LookupByPath.fromJson}. + */ + toJson(serializeValue: (value: TItem) => TSerialized): ILookupByPathJson; +} + +/** + * This class is used to associate path-like-strings, such as those returned by `git` commands, + * with entities that correspond with ancestor folders, such as Rush Projects or npm packages. + * + * It is optimized for efficiently locating the nearest ancestor path with an associated value. + * + * It is implemented as a Trie (https://en.wikipedia.org/wiki/Trie) data structure, with each edge + * being a path segment. + * + * @example + * ```ts + * const trie = new LookupByPath([['foo', 1], ['bar', 2], ['foo/bar', 3]]); + * trie.findChildPath('foo'); // returns 1 + * trie.findChildPath('foo/baz'); // returns 1 + * trie.findChildPath('baz'); // returns undefined + * trie.findChildPath('foo/bar/baz'); returns 3 + * trie.findChildPath('bar/foo/bar'); returns 2 + * ``` + * @beta + */ +export class LookupByPath implements IReadonlyLookupByPath { + /** + * The delimiter used to split paths + */ + public readonly delimiter: string; + + /** + * The root node of the trie, corresponding to the path '' + */ + private readonly _root: IPathTrieNode; + + /** + * The number of entries in this trie. + */ + private _size: number; + + /** + * Constructs a new `LookupByPath` + * + * @param entries - Initial path-value pairs to populate the trie. + */ + public constructor(entries?: Iterable<[string, TItem]>, delimiter?: string) { + this._root = { + value: undefined, + children: undefined + }; + + this.delimiter = delimiter ?? '/'; + this._size = 0; + + if (entries) { + for (const [path, item] of entries) { + this.setItem(path, item); + } + } + } + + /** + * Iterates over the segments of a serialized path. + * + * @example + * + * `LookupByPath.iteratePathSegments('foo/bar/baz')` yields 'foo', 'bar', 'baz' + * + * `LookupByPath.iteratePathSegments('foo\\bar\\baz', '\\')` yields 'foo', 'bar', 'baz' + */ + public static *iteratePathSegments(serializedPath: string, delimiter: string = '/'): Iterable { + for (const prefixMatch of _iteratePrefixes(serializedPath, delimiter)) { + yield prefixMatch.prefix; + } + } + + /** + * {@inheritdoc IReadonlyLookupByPath.size} + */ + public get size(): number { + return this._size; + } + + /** + * {@inheritdoc IReadonlyLookupByPath.tree} + */ + public get tree(): IReadonlyPathTrieNode { + return this._root; + } + + /** + * Deletes all entries from this `LookupByPath` instance. + * + * @returns this, for chained calls + */ + public clear(): this { + this._root.value = undefined; + this._root.children = undefined; + this._size = 0; + return this; + } + + /** + * Associates the value with the specified serialized path. + * If a value is already associated, will overwrite. + * + * @returns this, for chained calls + */ + public setItem(serializedPath: string, value: TItem, delimiter: string = this.delimiter): this { + return this.setItemFromSegments(LookupByPath.iteratePathSegments(serializedPath, delimiter), value); + } + + /** + * Deletes an item if it exists. + * @param query - The path to the item to delete + * @param delimeter - Optional override delimeter for parsing the query + * @returns `true` if the item was found and deleted, `false` otherwise + * @remarks + * If the node has children with values, they will be retained. + */ + public deleteItem(query: string, delimeter: string = this.delimiter): boolean { + const node: IPathTrieNode | undefined = this._findNodeAtPrefix(query, delimeter); + if (node?.value !== undefined) { + node.value = undefined; + this._size--; + return true; + } + + return false; + } + + /** + * Deletes an item and all its children. + * @param query - The path to the item to delete + * @param delimeter - Optional override delimeter for parsing the query + * @returns `true` if any nodes were deleted, `false` otherwise + */ + public deleteSubtree(query: string, delimeter: string = this.delimiter): boolean { + const queryNode: IPathTrieNode | undefined = this._findNodeAtPrefix(query, delimeter); + if (!queryNode) { + return false; + } + + const queue: IPathTrieNode[] = [queryNode]; + let removed: number = 0; + while (queue.length > 0) { + const node: IPathTrieNode = queue.pop()!; + if (node.value !== undefined) { + node.value = undefined; + removed++; + } + if (node.children) { + for (const child of node.children.values()) { + queue.push(child); + } + node.children.clear(); + } + } + + this._size -= removed; + return removed > 0; + } + + /** + * Associates the value with the specified path. + * If a value is already associated, will overwrite. + * + * @returns this, for chained calls + */ + public setItemFromSegments(pathSegments: Iterable, value: TItem): this { + let node: IPathTrieNode = this._root; + for (const segment of pathSegments) { + if (!node.children) { + node.children = new Map(); + } + let child: IPathTrieNode | undefined = node.children.get(segment); + if (!child) { + node.children.set( + segment, + (child = { + value: undefined, + children: undefined + }) + ); + } + node = child; + } + if (node.value === undefined) { + this._size++; + } + node.value = value; + + return this; + } + + /** + * {@inheritdoc IReadonlyLookupByPath} + */ + public findChildPath(childPath: string, delimiter: string = this.delimiter): TItem | undefined { + return this.findChildPathFromSegments(LookupByPath.iteratePathSegments(childPath, delimiter)); + } + + /** + * {@inheritdoc IReadonlyLookupByPath} + */ + public findLongestPrefixMatch( + query: string, + delimiter: string = this.delimiter + ): IPrefixMatch | undefined { + return this._findLongestPrefixMatch(_iteratePrefixes(query, delimiter)); + } + + /** + * {@inheritdoc IReadonlyLookupByPath} + */ + public findChildPathFromSegments(childPathSegments: Iterable): TItem | undefined { + let node: IPathTrieNode = this._root; + let best: TItem | undefined = node.value; + // Trivial cases + if (node.children) { + for (const segment of childPathSegments) { + const child: IPathTrieNode | undefined = node.children.get(segment); + if (!child) { + break; + } + node = child; + best = node.value ?? best; + if (!node.children) { + break; + } + } + } + + return best; + } + + /** + * {@inheritdoc IReadonlyLookupByPath} + */ + public has(key: string, delimiter: string = this.delimiter): boolean { + const match: IPrefixMatch | undefined = this.findLongestPrefixMatch(key, delimiter); + return match?.index === key.length; + } + + /** + * {@inheritdoc IReadonlyLookupByPath} + */ + public get(key: string, delimiter: string = this.delimiter): TItem | undefined { + const match: IPrefixMatch | undefined = this.findLongestPrefixMatch(key, delimiter); + return match?.index === key.length ? match.value : undefined; + } + + /** + * {@inheritdoc IReadonlyLookupByPath} + */ + public groupByChild( + infoByPath: Map, + delimiter: string = this.delimiter + ): Map> { + const groupedInfoByChild: Map> = new Map(); + + for (const [path, info] of infoByPath) { + const child: TItem | undefined = this.findChildPath(path, delimiter); + if (child === undefined) { + continue; + } + let groupedInfo: Map | undefined = groupedInfoByChild.get(child); + if (!groupedInfo) { + groupedInfo = new Map(); + groupedInfoByChild.set(child, groupedInfo); + } + groupedInfo.set(path, info); + } + + return groupedInfoByChild; + } + + /** + * {@inheritdoc IReadonlyLookupByPath} + */ + public *entries(query?: string, delimiter: string = this.delimiter): IterableIterator<[string, TItem]> { + let root: IPathTrieNode | undefined; + if (query) { + root = this._findNodeAtPrefix(query, delimiter); + if (!root) { + return; + } + } else { + root = this._root; + } + + const stack: [string, IPathTrieNode][] = [[query ?? '', root]]; + while (stack.length > 0) { + const [prefix, node] = stack.pop()!; + if (node.value !== undefined) { + yield [prefix, node.value]; + } + if (node.children) { + for (const [segment, child] of node.children) { + stack.push([prefix ? `${prefix}${delimiter}${segment}` : segment, child]); + } + } + } + } + + /** + * {@inheritdoc IReadonlyLookupByPath} + */ + public [Symbol.iterator]( + query?: string, + delimiter: string = this.delimiter + ): IterableIterator<[string, TItem]> { + return this.entries(query, delimiter); + } + + /** + * {@inheritdoc IReadonlyLookupByPath} + */ + public getNodeAtPrefix( + query: string, + delimiter: string = this.delimiter + ): IReadonlyPathTrieNode | undefined { + return this._findNodeAtPrefix(query, delimiter); + } + + /** + * {@inheritdoc IReadonlyLookupByPath.toJson} + */ + public toJson(serializeValue: (value: TItem) => TSerialized): ILookupByPathJson { + const valueToIndex: Map = new Map(); + const values: TSerialized[] = []; + + const getOrAddValueIndex: (value: TItem) => number = (value: TItem) => { + let index: number | undefined = valueToIndex.get(value); + if (index === undefined) { + index = values.length; + valueToIndex.set(value, index); + values.push(serializeValue(value)); + } + return index; + }; + + const serializeNode: (node: IPathTrieNode) => ISerializedPathTrieNode = ( + node: IPathTrieNode + ) => { + const result: ISerializedPathTrieNode = {}; + + if (node.value !== undefined) { + result.valueIndex = getOrAddValueIndex(node.value); + } + + if (node.children && node.children.size > 0) { + const children: Record = Object.create(null); + for (const [segment, child] of node.children) { + children[segment] = serializeNode(child); + } + result.children = children; + } + + return result; + }; + + return { + delimiter: this.delimiter, + values, + tree: serializeNode(this._root) + }; + } + + /** + * Deserializes a `LookupByPath` instance from a JSON representation previously + * created by {@link LookupByPath.toJson}. + * + * @param json - The JSON representation to deserialize. + * @param deserializeValue - A function that converts a serialized value back to its original type. + * @returns A new `LookupByPath` instance. + * + * @remarks + * Reference equality is preserved: if multiple nodes in the serialized trie pointed at the same + * value (i.e., the same index in the `values` array), the deserialized nodes will share the same + * object reference. + */ + public static fromJson( + json: ILookupByPathJson, + deserializeValue: (serialized: TSerialized) => TItem + ): LookupByPath { + const deserializedValues: TItem[] = json.values.map(deserializeValue); + + const result: LookupByPath = new LookupByPath(undefined, json.delimiter); + + const deserializeNode: (jsonNode: ISerializedPathTrieNode, targetNode: IPathTrieNode) => void = ( + jsonNode: ISerializedPathTrieNode, + targetNode: IPathTrieNode + ) => { + if (jsonNode.valueIndex !== undefined) { + targetNode.value = deserializedValues[jsonNode.valueIndex]; + result._size++; + } + + if (jsonNode.children) { + targetNode.children = new Map(); + for (const [segment, childJson] of Object.entries(jsonNode.children)) { + const childNode: IPathTrieNode = { + value: undefined, + children: undefined + }; + targetNode.children.set(segment, childNode); + deserializeNode(childJson, childNode); + } + } + }; + + deserializeNode(json.tree, result._root); + + return result; + } + + /** + * Iterates through progressively longer prefixes of a given string and returns as soon + * as the number of candidate items that match the prefix are 1 or 0. + * + * If a match is present, returns the matched itme and the length of the matched prefix. + * + * @returns the found item, or `undefined` if no item was found + */ + private _findLongestPrefixMatch(prefixes: Iterable): IPrefixMatch | undefined { + let node: IPathTrieNode = this._root; + let best: IPrefixMatch | undefined = node.value + ? { + value: node.value, + index: 0, + lastMatch: undefined + } + : undefined; + // Trivial cases + if (node.children) { + for (const { prefix: hash, index } of prefixes) { + const child: IPathTrieNode | undefined = node.children.get(hash); + if (!child) { + break; + } + node = child; + if (node.value !== undefined) { + best = { + value: node.value, + index, + lastMatch: best + }; + } + if (!node.children) { + break; + } + } + } + + return best; + } + + /** + * Finds the node at the specified path, or `undefined` if no node was found. + * + * @param query - The path to the node to search for + * @returns The trie node at the specified path, or `undefined` if no node was found + */ + private _findNodeAtPrefix( + query: string, + delimiter: string = this.delimiter + ): IPathTrieNode | undefined { + let node: IPathTrieNode = this._root; + for (const { prefix } of _iteratePrefixes(query, delimiter)) { + if (!node.children) { + return undefined; + } + const child: IPathTrieNode | undefined = node.children.get(prefix); + if (!child) { + return undefined; + } + node = child; + } + return node; + } +} + +function* _iteratePrefixes(input: string, delimiter: string = '/'): Iterable { + if (!input) { + return; + } + + let previousIndex: number = 0; + let nextIndex: number = input.indexOf(delimiter); + + // Leading segments + while (nextIndex >= 0) { + yield { + prefix: input.slice(previousIndex, nextIndex), + index: nextIndex + }; + previousIndex = nextIndex + 1; + nextIndex = input.indexOf(delimiter, previousIndex); + } + + // Last segment + if (previousIndex < input.length) { + yield { + prefix: input.slice(previousIndex, input.length), + index: input.length + }; + } +} diff --git a/libraries/lookup-by-path/src/getFirstDifferenceInCommonNodes.ts b/libraries/lookup-by-path/src/getFirstDifferenceInCommonNodes.ts new file mode 100644 index 00000000000..760e00413d2 --- /dev/null +++ b/libraries/lookup-by-path/src/getFirstDifferenceInCommonNodes.ts @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IReadonlyPathTrieNode } from './LookupByPath'; + +/** + * Options for the getFirstDifferenceInCommonNodes function. + * @beta + */ +export interface IGetFirstDifferenceInCommonNodesOptions { + /** + * The first node to compare. + */ + first: IReadonlyPathTrieNode; + /** + * The second node to compare. + */ + second: IReadonlyPathTrieNode; + /** + * The path prefix to the current node. + * @defaultValue '' + */ + prefix?: string; + /** + * The delimiter used to join path segments. + * @defaultValue '/' + */ + delimiter?: string; + /** + * A function to compare the values of the nodes. + * If not provided, strict equality (===) is used. + */ + equals?: (a: TItem, b: TItem) => boolean; +} + +/** + * Recursively compares two path tries to find the first shared node with a different value. + * + * @param options - The options for the comparison + * @returns The path to the first differing node, or undefined if they are identical + * + * @remarks + * Ignores any nodes that are not shared between the two tries. + * + * @beta + */ +export function getFirstDifferenceInCommonNodes( + options: IGetFirstDifferenceInCommonNodesOptions +): string | undefined { + const { first, second, prefix = '', delimiter = '/', equals = defaultEquals } = options; + + return getFirstDifferenceInCommonNodesInternal({ + first, + second, + prefix, + delimiter, + equals + }); +} + +/** + * Recursively compares two path tries to find the first shared node with a different value. + * + * @param options - The options for the comparison + * @returns The path to the first differing node, or undefined if they are identical + * + * @remarks + * Ignores any nodes that are not shared between the two tries. + * Separated out to avoid redundant parameter defaulting in the recursive calls. + */ +function getFirstDifferenceInCommonNodesInternal( + options: Required> +): string | undefined { + const { first, second, prefix, delimiter, equals } = options; + + const firstItem: TItem | undefined = first.value; + const secondItem: TItem | undefined = second.value; + + if (firstItem !== undefined && secondItem !== undefined && !equals(firstItem, secondItem)) { + // If this value was present in both tries with different values, return the prefix for this node. + return prefix; + } + + const { children: firstChildren } = first; + const { children: secondChildren } = second; + + if (firstChildren && secondChildren) { + for (const [key, firstChild] of firstChildren) { + const secondChild: IReadonlyPathTrieNode | undefined = secondChildren.get(key); + if (!secondChild) { + continue; + } + const result: string | undefined = getFirstDifferenceInCommonNodesInternal({ + first: firstChild, + second: secondChild, + prefix: key, + delimiter, + equals + }); + + if (result !== undefined) { + return prefix ? `${prefix}${delimiter}${result}` : result; + } + } + } + + return; +} + +/** + * Default equality function for comparing two items, using strict equality. + * @param a - The first item to compare + * @param b - The second item to compare + * @returns True if the items are reference equal, false otherwise + */ +function defaultEquals(a: TItem, b: TItem): boolean { + return a === b; +} diff --git a/libraries/lookup-by-path/src/index.ts b/libraries/lookup-by-path/src/index.ts new file mode 100644 index 00000000000..25fbc6b065f --- /dev/null +++ b/libraries/lookup-by-path/src/index.ts @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * Strongly typed trie data structure for path and URL-like strings. + * + * @packageDocumentation + */ + +export type { + ILookupByPathJson, + IPrefixMatch, + IReadonlyLookupByPath, + IReadonlyPathTrieNode, + ISerializedPathTrieNode +} from './LookupByPath'; +export { LookupByPath } from './LookupByPath'; +export type { IGetFirstDifferenceInCommonNodesOptions } from './getFirstDifferenceInCommonNodes'; +export { getFirstDifferenceInCommonNodes } from './getFirstDifferenceInCommonNodes'; diff --git a/libraries/lookup-by-path/src/test/LookupByPath.test.ts b/libraries/lookup-by-path/src/test/LookupByPath.test.ts new file mode 100644 index 00000000000..2b8e8beeb65 --- /dev/null +++ b/libraries/lookup-by-path/src/test/LookupByPath.test.ts @@ -0,0 +1,915 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { LookupByPath } from '../LookupByPath'; +import type { ILookupByPathJson } from '../LookupByPath'; + +describe(LookupByPath.iteratePathSegments.name, () => { + it('returns empty for an empty string', () => { + const result = [...LookupByPath.iteratePathSegments('')]; + expect(result.length).toEqual(0); + }); + it('returns the only segment of a trival string', () => { + const result = [...LookupByPath.iteratePathSegments('foo')]; + expect(result).toEqual(['foo']); + }); + it('treats backslashes as ordinary characters, per POSIX', () => { + const result = [...LookupByPath.iteratePathSegments('foo\\bar\\baz')]; + expect(result).toEqual(['foo\\bar\\baz']); + }); + it('iterates segments', () => { + const result = [...LookupByPath.iteratePathSegments('foo/bar/baz')]; + expect(result).toEqual(['foo', 'bar', 'baz']); + }); + it('returns correct last single character segment', () => { + const result = [...LookupByPath.iteratePathSegments('foo/a')]; + expect(result).toEqual(['foo', 'a']); + }); +}); + +describe('size', () => { + it('returns 0 for an empty tree', () => { + expect(new LookupByPath().size).toEqual(0); + }); + + it('returns the number of nodes for a non-empty tree', () => { + const lookup: LookupByPath = new LookupByPath([['foo', 1]]); + expect(lookup.size).toEqual(1); + lookup.setItem('bar', 2); + expect(lookup.size).toEqual(2); + lookup.setItem('bar', 4); + expect(lookup.size).toEqual(2); + lookup.setItem('bar/baz', 1); + expect(lookup.size).toEqual(3); + lookup.setItem('foo/bar/qux/quux', 1); + expect(lookup.size).toEqual(4); + }); +}); + +describe(LookupByPath.prototype.get.name, () => { + it('returns undefined for an empty tree', () => { + expect(new LookupByPath().get('foo')).toEqual(undefined); + }); + + it('returns the matching node for a trivial tree', () => { + expect(new LookupByPath([['foo', 1]]).get('foo')).toEqual(1); + }); + + it('returns undefined for non-matching paths in a single-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['bar', 2], + ['baz', 3] + ]); + + expect(tree.get('buzz')).toEqual(undefined); + expect(tree.get('foo/bar')).toEqual(undefined); + }); + + it('returns the matching node for a multi-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['foo/bar/baz', 3] + ]); + + expect(tree.get('foo')).toEqual(1); + expect(tree.get('foo/bar')).toEqual(2); + expect(tree.get('foo/bar/baz')).toEqual(3); + + expect(tree.get('foo')).toEqual(1); + expect(tree.get('foo,bar', ',')).toEqual(2); + expect(tree.get('foo\0bar\0baz', '\0')).toEqual(3); + }); + + it('returns undefined for non-matching paths in a multi-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['foo/bar/baz', 3] + ]); + + expect(tree.get('foo/baz')).toEqual(undefined); + expect(tree.get('foo/bar/baz/qux')).toEqual(undefined); + }); +}); + +describe(LookupByPath.prototype.has.name, () => { + it('returns false for an empty tree', () => { + expect(new LookupByPath().has('foo')).toEqual(false); + }); + + it('returns true for the matching node in a trivial tree', () => { + expect(new LookupByPath([['foo', 1]]).has('foo')).toEqual(true); + }); + + it('returns false for non-matching paths in a single-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['bar', 2], + ['baz', 3] + ]); + + expect(tree.has('buzz')).toEqual(false); + expect(tree.has('foo/bar')).toEqual(false); + }); + + it('returns true for the matching node in a multi-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['foo/bar/baz', 3] + ]); + + expect(tree.has('foo')).toEqual(true); + expect(tree.has('foo/bar')).toEqual(true); + expect(tree.has('foo/bar/baz')).toEqual(true); + + expect(tree.has('foo')).toEqual(true); + expect(tree.has('foo,bar', ',')).toEqual(true); + expect(tree.has('foo\0bar\0baz', '\0')).toEqual(true); + }); + + it('returns false for non-matching paths in a multi-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['foo/bar/baz', 3] + ]); + + expect(tree.has('foo/baz')).toEqual(false); + expect(tree.has('foo/bar/baz/qux')).toEqual(false); + }); +}); + +describe(LookupByPath.prototype.clear.name, () => { + it('clears an empty tree', () => { + const tree = new LookupByPath(); + tree.clear(); + expect(tree.size).toEqual(0); + }); + + it('clears a single-layer tree', () => { + const tree = new LookupByPath([['foo', 1]]); + expect(tree.size).toEqual(1); + tree.clear(); + expect(tree.size).toEqual(0); + }); + + it('clears a multi-layer tree', () => { + const tree = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['foo/bar/baz', 3] + ]); + expect(tree.size).toEqual(3); + tree.clear(); + expect(tree.size).toEqual(0); + }); + + it('clears a tree with custom delimiters', () => { + const tree = new LookupByPath( + [ + ['foo,bar', 1], + ['foo,bar,baz', 2] + ], + ',' + ); + expect(tree.size).toEqual(2); + tree.clear(); + expect(tree.size).toEqual(0); + }); +}); + +describe(LookupByPath.prototype.entries.name, () => { + it('returns an empty iterator for an empty tree', () => { + const tree = new LookupByPath(); + const result = [...tree]; + expect(result).toEqual([]); + }); + + it('returns an iterator for a single-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['bar', 2], + ['baz', 3] + ]); + + const result = [...tree]; + expect(result.length).toEqual(tree.size); + expect(Object.fromEntries(result)).toEqual({ + foo: 1, + bar: 2, + baz: 3 + }); + }); + + it('returns an iterator for a multi-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['foo/bar/baz', 3] + ]); + + const result = [...tree]; + expect(result.length).toEqual(tree.size); + expect(Object.fromEntries(result)).toEqual({ + foo: 1, + 'foo/bar': 2, + 'foo/bar/baz': 3 + }); + }); + + it('only includes non-empty nodes', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo/bar/baz', 1], + ['foo/bar/baz/qux/quux', 2] + ]); + + const result = [...tree]; + expect(result.length).toEqual(tree.size); + expect(Object.fromEntries(result)).toEqual({ + 'foo/bar/baz': 1, + 'foo/bar/baz/qux/quux': 2 + }); + }); + + it('returns an iterator for a tree with custom delimiters', () => { + const tree: LookupByPath = new LookupByPath( + [ + ['foo,bar', 1], + ['foo,bar,baz', 2] + ], + ',' + ); + + const result = [...tree]; + expect(result.length).toEqual(tree.size); + expect(Object.fromEntries(result)).toEqual({ + 'foo,bar': 1, + 'foo,bar,baz': 2 + }); + }); + + it('returns an iterator for a subtree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['foo/bar/baz', 3], + ['bar', 4], + ['bar/baz', 5] + ]); + + const result = [...tree.entries('foo')]; + expect(result.length).toEqual(3); + expect(Object.fromEntries(result)).toEqual({ + foo: 1, + 'foo/bar': 2, + 'foo/bar/baz': 3 + }); + }); + + it('returns an iterator for a subtree with custom delimiters', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo/bar', 1], + ['foo/bar/baz', 2], + ['bar/baz', 3] + ]); + + const result = [...tree.entries('foo', ',')]; + expect(result.length).toEqual(2); + expect(Object.fromEntries(result)).toEqual({ + 'foo,bar': 1, + 'foo,bar,baz': 2 + }); + }); +}); + +describe(LookupByPath.prototype.deleteItem.name, () => { + it('returns false for an empty tree', () => { + expect(new LookupByPath().deleteItem('foo')).toEqual(false); + }); + + it('deletes the matching node in a trivial tree', () => { + const tree = new LookupByPath([['foo', 1]]); + expect(tree.deleteItem('foo')).toEqual(true); + expect(tree.size).toEqual(0); + expect(tree.get('foo')).toEqual(undefined); + }); + + it('returns false for non-matching paths in a single-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['bar', 2], + ['baz', 3] + ]); + + expect(tree.deleteItem('buzz')).toEqual(false); + expect(tree.size).toEqual(3); + }); + + it('deletes the matching node in a single-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['bar', 2], + ['baz', 3] + ]); + + expect(tree.deleteItem('bar')).toEqual(true); + expect(tree.size).toEqual(2); + expect(tree.get('bar')).toEqual(undefined); + }); + + it('deletes the matching node in a multi-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['foo/bar/baz', 3] + ]); + + expect(tree.deleteItem('foo/bar')).toEqual(true); + expect(tree.size).toEqual(2); + expect(tree.get('foo/bar')).toEqual(undefined); + expect(tree.get('foo/bar/baz')).toEqual(3); // child nodes are retained + }); + + it('returns false for non-matching paths in a multi-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['foo/bar/baz', 3] + ]); + + expect(tree.deleteItem('foo/baz')).toEqual(false); + expect(tree.size).toEqual(3); + }); + + it('handles custom delimiters', () => { + const tree: LookupByPath = new LookupByPath( + [ + ['foo,bar', 1], + ['foo,bar,baz', 2] + ], + ',' + ); + + expect(tree.deleteItem('foo\0bar', '\0')).toEqual(true); + expect(tree.size).toEqual(1); + expect(tree.get('foo\0bar', '\0')).toEqual(undefined); + expect(tree.get('foo\0bar\0baz', '\0')).toEqual(2); // child nodes are retained + }); +}); + +describe(LookupByPath.prototype.deleteSubtree.name, () => { + it('returns false for an empty tree', () => { + expect(new LookupByPath().deleteSubtree('foo')).toEqual(false); + }); + + it('deletes the matching node in a trivial tree', () => { + const tree = new LookupByPath([['foo', 1]]); + expect(tree.deleteSubtree('foo')).toEqual(true); + expect(tree.size).toEqual(0); + expect(tree.get('foo')).toEqual(undefined); + }); + + it('returns false for non-matching paths in a single-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['bar', 2], + ['baz', 3] + ]); + + expect(tree.deleteSubtree('buzz')).toEqual(false); + expect(tree.size).toEqual(3); + }); + + it('deletes the matching node in a single-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['bar', 2], + ['baz', 3] + ]); + + expect(tree.deleteSubtree('bar')).toEqual(true); + expect(tree.size).toEqual(2); + expect(tree.get('bar')).toEqual(undefined); + }); + + it('deletes the matching subtree in a multi-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['foo/bar/baz', 3] + ]); + + expect(tree.deleteSubtree('foo/bar')).toEqual(true); + expect(tree.size).toEqual(1); + expect(tree.get('foo/bar')).toEqual(undefined); + expect(tree.get('foo/bar/baz')).toEqual(undefined); // child nodes are deleted + }); + + it('returns false for non-matching paths in a multi-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['foo/bar/baz', 3] + ]); + + expect(tree.deleteSubtree('foo/baz')).toEqual(false); + expect(tree.size).toEqual(3); + }); + + it('handles custom delimiters', () => { + const tree: LookupByPath = new LookupByPath( + [ + ['foo,bar', 1], + ['foo,bar,baz', 2] + ], + ',' + ); + + expect(tree.deleteSubtree('foo\0bar', '\0')).toEqual(true); + expect(tree.size).toEqual(0); + expect(tree.get('foo\0bar', '\0')).toEqual(undefined); + expect(tree.get('foo\0bar\0baz', '\0')).toEqual(undefined); // child nodes are deleted + }); +}); + +describe(LookupByPath.prototype.findChildPath.name, () => { + it('returns empty for an empty tree', () => { + expect(new LookupByPath().findChildPath('foo')).toEqual(undefined); + }); + it('returns the matching node for a trivial tree', () => { + expect(new LookupByPath([['foo', 1]]).findChildPath('foo')).toEqual(1); + }); + it('returns the matching node for a single-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['bar', 2], + ['baz', 3] + ]); + + expect(tree.findChildPath('foo')).toEqual(1); + expect(tree.findChildPath('bar')).toEqual(2); + expect(tree.findChildPath('baz')).toEqual(3); + expect(tree.findChildPath('buzz')).toEqual(undefined); + }); + it('returns the matching parent for multi-layer queries', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['bar', 2], + ['baz', 3] + ]); + + expect(tree.findChildPath('foo/bar')).toEqual(1); + expect(tree.findChildPath('bar/baz')).toEqual(2); + expect(tree.findChildPath('baz/foo')).toEqual(3); + expect(tree.findChildPath('foo/foo')).toEqual(1); + }); + it('returns the matching parent for multi-layer queries in multi-layer trees', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['bar', 2], + ['baz', 3], + ['foo/bar', 4], + ['foo/bar/baz', 5], + ['baz/foo', 6], + ['baz/baz/baz/baz', 7] + ]); + + expect(tree.findChildPath('foo/foo')).toEqual(1); + expect(tree.findChildPath('foo/bar\\baz')).toEqual(1); + + expect(tree.findChildPath('bar/baz')).toEqual(2); + + expect(tree.findChildPath('baz/bar')).toEqual(3); + expect(tree.findChildPath('baz/baz')).toEqual(3); + expect(tree.findChildPath('baz/baz/baz')).toEqual(3); + + expect(tree.findChildPath('foo/bar')).toEqual(4); + expect(tree.findChildPath('foo/bar/foo')).toEqual(4); + + expect(tree.findChildPath('foo/bar/baz')).toEqual(5); + expect(tree.findChildPath('foo/bar/baz/baz/baz/baz/baz')).toEqual(5); + + expect(tree.findChildPath('baz/foo/')).toEqual(6); + + expect(tree.findChildPath('baz/baz/baz/baz')).toEqual(7); + + expect(tree.findChildPath('')).toEqual(undefined); + expect(tree.findChildPath('foofoo')).toEqual(undefined); + expect(tree.findChildPath('foo\\bar\\baz')).toEqual(undefined); + }); + it('handles custom delimiters', () => { + const tree: LookupByPath = new LookupByPath( + [ + ['foo,bar', 1], + ['foo/bar', 2] + ], + ',' + ); + + expect(tree.findChildPath('foo/bar,baz')).toEqual(2); + expect(tree.findChildPath('foo,bar,baz', ',')).toEqual(1); + expect(tree.findChildPath('foo\0bar\0baz', '\0')).toEqual(1); + expect(tree.findChildPath('foo,bar/baz')).toEqual(undefined); + expect(tree.findChildPathFromSegments(['foo', 'bar', 'baz'])).toEqual(1); + }); +}); + +describe(LookupByPath.prototype.findLongestPrefixMatch.name, () => { + it('returns empty for an empty tree', () => { + expect(new LookupByPath().findLongestPrefixMatch('foo')).toEqual(undefined); + }); + it('returns the matching node for a trivial tree', () => { + expect(new LookupByPath([['foo', 1]]).findLongestPrefixMatch('foo')).toEqual({ value: 1, index: 3 }); + }); + it('returns the matching node for a single-layer tree', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['barbar', 2], + ['baz', 3] + ]); + + expect(tree.findLongestPrefixMatch('foo')).toEqual({ value: 1, index: 3 }); + expect(tree.findLongestPrefixMatch('barbar')).toEqual({ value: 2, index: 6 }); + expect(tree.findLongestPrefixMatch('baz')).toEqual({ value: 3, index: 3 }); + expect(tree.findLongestPrefixMatch('buzz')).toEqual(undefined); + }); + it('returns the matching parent for multi-layer queries', () => { + const tree: LookupByPath = new LookupByPath([ + ['foo', 1], + ['barbar', 2], + ['baz', 3], + ['foo/bar', 4] + ]); + + expect(tree.findLongestPrefixMatch('foo/bar')).toEqual({ + value: 4, + index: 7, + lastMatch: { value: 1, index: 3 } + }); + expect(tree.findLongestPrefixMatch('barbar/baz')).toEqual({ value: 2, index: 6 }); + expect(tree.findLongestPrefixMatch('baz/foo')).toEqual({ value: 3, index: 3 }); + expect(tree.findLongestPrefixMatch('foo/foo')).toEqual({ value: 1, index: 3 }); + }); +}); + +describe(LookupByPath.prototype.groupByChild.name, () => { + const lookup: LookupByPath = new LookupByPath([ + ['foo', 'foo'], + ['foo/bar', 'bar'], + ['foo/bar/baz', 'baz'] + ]); + + it('returns empty map for empty input', () => { + expect(lookup.groupByChild(new Map())).toEqual(new Map()); + }); + + it('groups items by the closest group that contains the file path', () => { + const infoByPath: Map = new Map([ + ['foo', 'foo'], + ['foo/bar', 'bar'], + ['foo/bar/baz', 'baz'], + ['foo/bar/baz/qux', 'qux'], + ['foo/bar/baz/qux/quux', 'quux'] + ]); + + const expected: Map> = new Map([ + ['foo', new Map([['foo', 'foo']])], + ['bar', new Map([['foo/bar', 'bar']])], + [ + 'baz', + new Map([ + ['foo/bar/baz', 'baz'], + ['foo/bar/baz/qux', 'qux'], + ['foo/bar/baz/qux/quux', 'quux'] + ]) + ] + ]); + + expect(lookup.groupByChild(infoByPath)).toEqual(expected); + }); + + it('groups items by the closest group that contains the file path with custom delimiter', () => { + const customLookup: LookupByPath = new LookupByPath( + [ + ['foo,bar', 'bar'], + ['foo,bar,baz', 'baz'] + ], + ',' + ); + + const infoByPath: Map = new Map([ + ['foo\0bar', 'bar'], + ['foo\0bar\0baz', 'baz'], + ['foo\0bar\0baz\0qux', 'qux'], + ['foo\0bar\0baz\0qux\0quux', 'quux'] + ]); + + const expected: Map> = new Map([ + ['bar', new Map([['foo\0bar', 'bar']])], + [ + 'baz', + new Map([ + ['foo\0bar\0baz', 'baz'], + ['foo\0bar\0baz\0qux', 'qux'], + ['foo\0bar\0baz\0qux\0quux', 'quux'] + ]) + ] + ]); + + expect(customLookup.groupByChild(infoByPath, '\0')).toEqual(expected); + }); + + it('ignores items that do not exist in the lookup', () => { + const infoByPath: Map = new Map([ + ['foo', 'foo'], + ['foo/qux', 'qux'], + ['bar', 'bar'], + ['baz', 'baz'] + ]); + + const expected: Map> = new Map([ + [ + 'foo', + new Map([ + ['foo', 'foo'], + ['foo/qux', 'qux'] + ]) + ] + ]); + + expect(lookup.groupByChild(infoByPath)).toEqual(expected); + }); + + it('ignores items that do not exist in the lookup when the lookup children are possibly falsy', () => { + const falsyLookup: LookupByPath = new LookupByPath([ + ['foo', 'foo'], + ['foo/bar', 'bar'], + ['foo/bar/baz', ''] + ]); + + const infoByPath: Map = new Map([ + ['foo', 'foo'], + ['foo/bar', 'bar'], + ['foo/bar/baz', 'baz'], + ['foo/bar/baz/qux', 'qux'], + ['foo/bar/baz/qux/quux', 'quux'] + ]); + + const expected: Map> = new Map([ + ['foo', new Map([['foo', 'foo']])], + ['bar', new Map([['foo/bar', 'bar']])], + [ + '', + new Map([ + ['foo/bar/baz', 'baz'], + ['foo/bar/baz/qux', 'qux'], + ['foo/bar/baz/qux/quux', 'quux'] + ]) + ] + ]); + + expect(falsyLookup.groupByChild(infoByPath)).toEqual(expected); + }); +}); + +describe(`${LookupByPath.prototype.toJson.name} and ${LookupByPath.fromJson.name}`, () => { + it('round-trips an empty trie', () => { + const original = new LookupByPath(); + const json: ILookupByPathJson = original.toJson((v) => v); + const restored: LookupByPath = LookupByPath.fromJson(json, (v) => v); + + expect(restored.size).toEqual(0); + expect([...restored]).toEqual([]); + }); + + it('round-trips with number values', () => { + const original = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['baz', 3] + ]); + + const json: ILookupByPathJson = original.toJson((v) => v); + const restored: LookupByPath = LookupByPath.fromJson(json, (v) => v); + + expect(restored.size).toEqual(3); + expect(restored.get('foo')).toEqual(1); + expect(restored.get('foo/bar')).toEqual(2); + expect(restored.get('baz')).toEqual(3); + expect(restored.get('missing')).toEqual(undefined); + }); + + it('snapshot: serialized JSON for a simple tree', () => { + const original = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['baz', 3] + ]); + + const json: ILookupByPathJson = original.toJson((v) => v); + expect(json).toMatchSnapshot(); + }); + + it('snapshot: serialized JSON with intermediate nodes and custom delimiter', () => { + const original = new LookupByPath( + [ + ['a,b,c', 'deep'], + ['a,b', 'mid'], + ['x', 'top'] + ], + ',' + ); + + const json: ILookupByPathJson = original.toJson((v) => v); + expect(json).toMatchSnapshot(); + }); + + it('round-trips with string values', () => { + const original = new LookupByPath([ + ['a', 'alpha'], + ['a/b', 'bravo'], + ['c', 'charlie'] + ]); + + const json: ILookupByPathJson = original.toJson((v) => v); + const restored: LookupByPath = LookupByPath.fromJson(json, (v) => v); + + expect(restored.size).toEqual(3); + expect(restored.get('a')).toEqual('alpha'); + expect(restored.get('a/b')).toEqual('bravo'); + expect(restored.get('c')).toEqual('charlie'); + }); + + it('preserves reference equality for shared values', () => { + const sharedObj = { name: 'shared' }; + const original = new LookupByPath<{ name: string }>([ + ['foo', sharedObj], + ['bar', sharedObj], + ['baz/qux', sharedObj] + ]); + + const json: ILookupByPathJson<{ name: string }> = original.toJson((v) => ({ ...v })); + // All three entries should point at the same index + expect(json.values.length).toEqual(1); + expect(json.values[0]).toEqual({ name: 'shared' }); + + const restored: LookupByPath<{ name: string }> = LookupByPath.fromJson(json, (v) => ({ ...v })); + + expect(restored.size).toEqual(3); + const fooVal = restored.get('foo'); + const barVal = restored.get('bar'); + const bazQuxVal = restored.get('baz/qux'); + + // All deserialized values should be the same reference + expect(fooVal).toBe(barVal); + expect(barVal).toBe(bazQuxVal); + expect(fooVal).toEqual({ name: 'shared' }); + }); + + it('keeps non-reference-equal objects with same JSON as separate entries', () => { + const obj1 = { name: 'same' }; + const obj2 = { name: 'same' }; + // Verify they are not reference-equal + expect(obj1).not.toBe(obj2); + + const original = new LookupByPath<{ name: string }>([ + ['foo', obj1], + ['bar', obj2] + ]); + + const json: ILookupByPathJson<{ name: string }> = original.toJson((v) => ({ ...v })); + // Should have two separate entries even though the JSON is the same + expect(json.values.length).toEqual(2); + + const restored: LookupByPath<{ name: string }> = LookupByPath.fromJson(json, (v) => ({ ...v })); + + expect(restored.size).toEqual(2); + const fooVal = restored.get('foo'); + const barVal = restored.get('bar'); + + // Values should be structurally equal + expect(fooVal).toEqual({ name: 'same' }); + expect(barVal).toEqual({ name: 'same' }); + + // But NOT reference-equal + expect(fooVal).not.toBe(barVal); + }); + + it('round-trips a complex multi-level tree', () => { + const original = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['foo/bar/baz', 3], + ['foo/bar/baz/qux', 4], + ['bar', 5], + ['bar/baz', 6] + ]); + + const json: ILookupByPathJson = original.toJson((v) => v); + const restored: LookupByPath = LookupByPath.fromJson(json, (v) => v); + + expect(restored.size).toEqual(original.size); + for (const [path, value] of original) { + expect(restored.get(path)).toEqual(value); + } + }); + + it('round-trips with a custom delimiter', () => { + const original = new LookupByPath( + [ + ['foo,bar', 1], + ['foo,bar,baz', 2], + ['qux', 3] + ], + ',' + ); + + const json: ILookupByPathJson = original.toJson((v) => v); + expect(json.delimiter).toEqual(','); + + const restored: LookupByPath = LookupByPath.fromJson(json, (v) => v); + + expect(restored.delimiter).toEqual(','); + expect(restored.size).toEqual(3); + expect(restored.get('foo,bar')).toEqual(1); + expect(restored.get('foo,bar,baz')).toEqual(2); + expect(restored.get('qux')).toEqual(3); + }); + + it('uses custom serializer and deserializer', () => { + const original = new LookupByPath<{ id: number; label: string }>([ + ['a', { id: 1, label: 'one' }], + ['b', { id: 2, label: 'two' }] + ]); + + const json: ILookupByPathJson = original.toJson((v) => JSON.stringify(v)); + expect(json.values).toEqual(['{"id":1,"label":"one"}', '{"id":2,"label":"two"}']); + + const restored: LookupByPath<{ id: number; label: string }> = LookupByPath.fromJson( + json, + (v) => JSON.parse(v) as { id: number; label: string } + ); + + expect(restored.size).toEqual(2); + expect(restored.get('a')).toEqual({ id: 1, label: 'one' }); + expect(restored.get('b')).toEqual({ id: 2, label: 'two' }); + }); + + it('produces valid JSON for the serialized form', () => { + const original = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2] + ]); + + const json: ILookupByPathJson = original.toJson((v) => v); + const jsonString: string = JSON.stringify(json); + const parsed: ILookupByPathJson = JSON.parse(jsonString) as ILookupByPathJson; + + const restored: LookupByPath = LookupByPath.fromJson(parsed, (v) => v); + expect(restored.size).toEqual(2); + expect(restored.get('foo')).toEqual(1); + expect(restored.get('foo/bar')).toEqual(2); + }); + + it('preserves findChildPath behavior after round-trip', () => { + const original = new LookupByPath([ + ['foo', 1], + ['foo/bar', 2], + ['baz', 3] + ]); + + const json: ILookupByPathJson = original.toJson((v) => v); + const restored: LookupByPath = LookupByPath.fromJson(json, (v) => v); + + expect(restored.findChildPath('foo/baz')).toEqual(1); + expect(restored.findChildPath('foo/bar/baz')).toEqual(2); + expect(restored.findChildPath('baz/anything')).toEqual(3); + expect(restored.findChildPath('missing')).toEqual(undefined); + }); + + it('handles nodes with children but no value', () => { + const original = new LookupByPath([ + ['foo/bar/baz', 1], + ['foo/bar/qux', 2] + ]); + + const json: ILookupByPathJson = original.toJson((v) => v); + // The intermediate nodes 'foo' and 'foo/bar' should exist in the tree but have no valueIndex + const fooNode = json.tree.children!.foo; + const barNode = fooNode.children!.bar; + expect(fooNode.valueIndex).toBeUndefined(); + expect(barNode.valueIndex).toBeUndefined(); + expect(barNode.children!.baz.valueIndex).toEqual(0); + expect(barNode.children!.qux.valueIndex).toEqual(1); + + const restored: LookupByPath = LookupByPath.fromJson(json, (v) => v); + expect(restored.size).toEqual(2); + expect(restored.get('foo')).toEqual(undefined); + expect(restored.get('foo/bar')).toEqual(undefined); + expect(restored.get('foo/bar/baz')).toEqual(1); + expect(restored.get('foo/bar/qux')).toEqual(2); + }); +}); diff --git a/libraries/lookup-by-path/src/test/__snapshots__/LookupByPath.test.ts.snap b/libraries/lookup-by-path/src/test/__snapshots__/LookupByPath.test.ts.snap new file mode 100644 index 00000000000..66dd3c8d07b --- /dev/null +++ b/libraries/lookup-by-path/src/test/__snapshots__/LookupByPath.test.ts.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`toJson and fromJson snapshot: serialized JSON for a simple tree 1`] = ` +Object { + "delimiter": "/", + "tree": Object { + "children": Object { + "baz": Object { + "valueIndex": 2, + }, + "foo": Object { + "children": Object { + "bar": Object { + "valueIndex": 1, + }, + }, + "valueIndex": 0, + }, + }, + }, + "values": Array [ + 1, + 2, + 3, + ], +} +`; + +exports[`toJson and fromJson snapshot: serialized JSON with intermediate nodes and custom delimiter 1`] = ` +Object { + "delimiter": ",", + "tree": Object { + "children": Object { + "a": Object { + "children": Object { + "b": Object { + "children": Object { + "c": Object { + "valueIndex": 1, + }, + }, + "valueIndex": 0, + }, + }, + }, + "x": Object { + "valueIndex": 2, + }, + }, + }, + "values": Array [ + "mid", + "deep", + "top", + ], +} +`; diff --git a/libraries/lookup-by-path/src/test/getFirstDifferenceInCommonNodes.test.ts b/libraries/lookup-by-path/src/test/getFirstDifferenceInCommonNodes.test.ts new file mode 100644 index 00000000000..2a85dec9f4f --- /dev/null +++ b/libraries/lookup-by-path/src/test/getFirstDifferenceInCommonNodes.test.ts @@ -0,0 +1,236 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { getFirstDifferenceInCommonNodes } from '../getFirstDifferenceInCommonNodes'; +import type { IReadonlyPathTrieNode } from '../LookupByPath'; + +describe(getFirstDifferenceInCommonNodes.name, () => { + it('detects a changed file at the current node', () => { + const last: IReadonlyPathTrieNode = { + children: undefined, + value: 'old' + }; + const current: IReadonlyPathTrieNode = { + children: undefined, + value: 'new' + }; + + expect( + getFirstDifferenceInCommonNodes({ + first: last, + second: current + }) + ).toBe(''); + expect( + getFirstDifferenceInCommonNodes({ + first: current, + second: last + }) + ).toBe(''); + + const prefix: string = 'some/prefix'; + + expect( + getFirstDifferenceInCommonNodes({ + first: last, + second: current, + prefix + }) + ).toBe(prefix); + expect( + getFirstDifferenceInCommonNodes({ + first: current, + second: last, + prefix + }) + ).toBe(prefix); + }); + + it('detects no changes when both nodes are identical', () => { + const last: IReadonlyPathTrieNode = { + children: new Map([ + [ + 'same', + { + children: undefined, + value: 'same' + } + ] + ]), + value: undefined + }; + const current: IReadonlyPathTrieNode = { + children: new Map([ + [ + 'same', + { + children: undefined, + value: 'same' + } + ] + ]), + value: undefined + }; + + expect( + getFirstDifferenceInCommonNodes({ + first: last, + second: current + }) + ).toBeUndefined(); + expect( + getFirstDifferenceInCommonNodes({ + first: current, + second: last + }) + ).toBeUndefined(); + }); + + it('detects no changes when both nodes are identical based on a custom equals', () => { + const last: IReadonlyPathTrieNode = { + children: new Map([ + [ + 'same', + { + children: undefined, + value: 'same' + } + ] + ]), + value: undefined + }; + const current: IReadonlyPathTrieNode = { + children: new Map([ + [ + 'same', + { + children: undefined, + value: 'other' + } + ] + ]), + value: undefined + }; + + function customEquals(a: string, b: string): boolean { + return a === b || (a === 'same' && b === 'other') || (a === 'other' && b === 'same'); + } + + expect( + getFirstDifferenceInCommonNodes({ + first: last, + second: current, + equals: customEquals + }) + ).toBeUndefined(); + expect( + getFirstDifferenceInCommonNodes({ + first: current, + second: last, + equals: customEquals + }) + ).toBeUndefined(); + }); + + it('detects no changes for extra children', () => { + const last: IReadonlyPathTrieNode = { + children: undefined, + value: undefined + }; + const current: IReadonlyPathTrieNode = { + children: new Map([ + [ + 'same', + { + children: undefined, + value: 'same' + } + ] + ]), + value: undefined + }; + + expect( + getFirstDifferenceInCommonNodes({ + first: last, + second: current + }) + ).toBeUndefined(); + expect( + getFirstDifferenceInCommonNodes({ + first: current, + second: last + }) + ).toBeUndefined(); + }); + + it('detects no changes if the set of common nodes differs', () => { + const last: IReadonlyPathTrieNode = { + children: undefined, + value: undefined + }; + const current: IReadonlyPathTrieNode = { + children: undefined, + value: 'new' + }; + + expect( + getFirstDifferenceInCommonNodes({ + first: last, + second: current + }) + ).toBeUndefined(); + expect( + getFirstDifferenceInCommonNodes({ + first: current, + second: last + }) + ).toBeUndefined(); + }); + + it('detects a nested change', () => { + const last: IReadonlyPathTrieNode = { + children: new Map([ + [ + 'child', + { + children: undefined, + value: 'old' + } + ] + ]), + value: undefined + }; + const current: IReadonlyPathTrieNode = { + children: new Map([ + [ + 'child', + { + children: undefined, + value: 'new' + } + ] + ]), + value: undefined + }; + + const prefix: string = 'some/prefix'; + + expect( + getFirstDifferenceInCommonNodes({ + first: last, + second: current, + prefix, + delimiter: '@' + }) + ).toBe('some/prefix@child'); + expect( + getFirstDifferenceInCommonNodes({ + first: current, + second: last, + prefix, + delimiter: '@' + }) + ).toBe('some/prefix@child'); + }); +}); diff --git a/libraries/lookup-by-path/tsconfig.json b/libraries/lookup-by-path/tsconfig.json new file mode 100644 index 00000000000..9a79fa4af11 --- /dev/null +++ b/libraries/lookup-by-path/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", + + "compilerOptions": { + "target": "ES2019" + } +} diff --git a/libraries/module-minifier/.eslintrc.js b/libraries/module-minifier/.eslintrc.js deleted file mode 100644 index f7ee2a5d364..00000000000 --- a/libraries/module-minifier/.eslintrc.js +++ /dev/null @@ -1,11 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node', - '@rushstack/eslint-config/mixins/friendly-locals', - '@rushstack/eslint-config/mixins/tsdoc' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/libraries/module-minifier/.npmignore b/libraries/module-minifier/.npmignore index 302dbc5b019..f7a40e10213 100644 --- a/libraries/module-minifier/.npmignore +++ b/libraries/module-minifier/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,16 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/module-minifier/CHANGELOG.json b/libraries/module-minifier/CHANGELOG.json index a3557e2fcab..00dcd4362c3 100644 --- a/libraries/module-minifier/CHANGELOG.json +++ b/libraries/module-minifier/CHANGELOG.json @@ -1,6 +1,2813 @@ { "name": "@rushstack/module-minifier", "entries": [ + { + "version": "0.9.24", + "tag": "@rushstack/module-minifier_v0.9.24", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "0.9.23", + "tag": "@rushstack/module-minifier_v0.9.23", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "0.9.22", + "tag": "@rushstack/module-minifier_v0.9.22", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "0.9.21", + "tag": "@rushstack/module-minifier_v0.9.21", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "0.9.20", + "tag": "@rushstack/module-minifier_v0.9.20", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "0.9.19", + "tag": "@rushstack/module-minifier_v0.9.19", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "0.9.18", + "tag": "@rushstack/module-minifier_v0.9.18", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "0.9.17", + "tag": "@rushstack/module-minifier_v0.9.17", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "0.9.16", + "tag": "@rushstack/module-minifier_v0.9.16", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "0.9.15", + "tag": "@rushstack/module-minifier_v0.9.15", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "0.9.14", + "tag": "@rushstack/module-minifier_v0.9.14", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "0.9.13", + "tag": "@rushstack/module-minifier_v0.9.13", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "0.9.12", + "tag": "@rushstack/module-minifier_v0.9.12", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "0.9.11", + "tag": "@rushstack/module-minifier_v0.9.11", + "date": "Thu, 02 Apr 2026 00:14:38 GMT", + "comments": { + "patch": [ + { + "comment": "Bump serialize-javascript 7.0.5 to address CVE GHSA-qj8w-gfj5-8c6v" + } + ] + } + }, + { + "version": "0.9.10", + "tag": "@rushstack/module-minifier_v0.9.10", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "0.9.9", + "tag": "@rushstack/module-minifier_v0.9.9", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "0.9.8", + "tag": "@rushstack/module-minifier_v0.9.8", + "date": "Tue, 10 Mar 2026 15:13:12 GMT", + "comments": { + "patch": [ + { + "comment": "Bump `serialize-javascript` to partially mitigate CVE-2020-7660." + } + ] + } + }, + { + "version": "0.9.7", + "tag": "@rushstack/module-minifier_v0.9.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "0.9.6", + "tag": "@rushstack/module-minifier_v0.9.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "0.9.5", + "tag": "@rushstack/module-minifier_v0.9.5", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "0.9.4", + "tag": "@rushstack/module-minifier_v0.9.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "0.9.3", + "tag": "@rushstack/module-minifier_v0.9.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "0.9.2", + "tag": "@rushstack/module-minifier_v0.9.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "0.9.1", + "tag": "@rushstack/module-minifier_v0.9.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "0.9.0", + "tag": "@rushstack/module-minifier_v0.9.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "0.8.14", + "tag": "@rushstack/module-minifier_v0.8.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "0.8.13", + "tag": "@rushstack/module-minifier_v0.8.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "0.8.12", + "tag": "@rushstack/module-minifier_v0.8.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "0.8.11", + "tag": "@rushstack/module-minifier_v0.8.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "0.8.10", + "tag": "@rushstack/module-minifier_v0.8.10", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "0.8.9", + "tag": "@rushstack/module-minifier_v0.8.9", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "0.8.8", + "tag": "@rushstack/module-minifier_v0.8.8", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "0.8.7", + "tag": "@rushstack/module-minifier_v0.8.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + } + ] + } + }, + { + "version": "0.8.6", + "tag": "@rushstack/module-minifier_v0.8.6", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + } + ] + } + }, + { + "version": "0.8.5", + "tag": "@rushstack/module-minifier_v0.8.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + } + ] + } + }, + { + "version": "0.8.4", + "tag": "@rushstack/module-minifier_v0.8.4", + "date": "Tue, 04 Nov 2025 08:15:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + } + ] + } + }, + { + "version": "0.8.3", + "tag": "@rushstack/module-minifier_v0.8.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + } + ] + } + }, + { + "version": "0.8.2", + "tag": "@rushstack/module-minifier_v0.8.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + } + ] + } + }, + { + "version": "0.8.1", + "tag": "@rushstack/module-minifier_v0.8.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + } + ] + } + }, + { + "version": "0.8.0", + "tag": "@rushstack/module-minifier_v0.8.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + } + ] + } + }, + { + "version": "0.7.30", + "tag": "@rushstack/module-minifier_v0.7.30", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + } + ] + } + }, + { + "version": "0.7.29", + "tag": "@rushstack/module-minifier_v0.7.29", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + } + ] + } + }, + { + "version": "0.7.28", + "tag": "@rushstack/module-minifier_v0.7.28", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + } + ] + } + }, + { + "version": "0.7.27", + "tag": "@rushstack/module-minifier_v0.7.27", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + } + ] + } + }, + { + "version": "0.7.26", + "tag": "@rushstack/module-minifier_v0.7.26", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + } + ] + } + }, + { + "version": "0.7.25", + "tag": "@rushstack/module-minifier_v0.7.25", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + } + ] + } + }, + { + "version": "0.7.24", + "tag": "@rushstack/module-minifier_v0.7.24", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + } + ] + } + }, + { + "version": "0.7.23", + "tag": "@rushstack/module-minifier_v0.7.23", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + } + ] + } + }, + { + "version": "0.7.22", + "tag": "@rushstack/module-minifier_v0.7.22", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + } + ] + } + }, + { + "version": "0.7.21", + "tag": "@rushstack/module-minifier_v0.7.21", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + } + ] + } + }, + { + "version": "0.7.20", + "tag": "@rushstack/module-minifier_v0.7.20", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + } + ] + } + }, + { + "version": "0.7.19", + "tag": "@rushstack/module-minifier_v0.7.19", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + } + ] + } + }, + { + "version": "0.7.18", + "tag": "@rushstack/module-minifier_v0.7.18", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + } + ] + } + }, + { + "version": "0.7.17", + "tag": "@rushstack/module-minifier_v0.7.17", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + } + ] + } + }, + { + "version": "0.7.16", + "tag": "@rushstack/module-minifier_v0.7.16", + "date": "Tue, 15 Apr 2025 15:11:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + } + ] + } + }, + { + "version": "0.7.15", + "tag": "@rushstack/module-minifier_v0.7.15", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + } + ] + } + }, + { + "version": "0.7.14", + "tag": "@rushstack/module-minifier_v0.7.14", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + } + ] + } + }, + { + "version": "0.7.13", + "tag": "@rushstack/module-minifier_v0.7.13", + "date": "Tue, 25 Mar 2025 15:11:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + } + ] + } + }, + { + "version": "0.7.12", + "tag": "@rushstack/module-minifier_v0.7.12", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + } + ] + } + }, + { + "version": "0.7.11", + "tag": "@rushstack/module-minifier_v0.7.11", + "date": "Wed, 12 Mar 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + } + ] + } + }, + { + "version": "0.7.10", + "tag": "@rushstack/module-minifier_v0.7.10", + "date": "Tue, 11 Mar 2025 02:12:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + } + ] + } + }, + { + "version": "0.7.9", + "tag": "@rushstack/module-minifier_v0.7.9", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + } + ] + } + }, + { + "version": "0.7.8", + "tag": "@rushstack/module-minifier_v0.7.8", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + } + ] + } + }, + { + "version": "0.7.7", + "tag": "@rushstack/module-minifier_v0.7.7", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + } + ] + } + }, + { + "version": "0.7.6", + "tag": "@rushstack/module-minifier_v0.7.6", + "date": "Wed, 26 Feb 2025 16:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + } + ] + } + }, + { + "version": "0.7.5", + "tag": "@rushstack/module-minifier_v0.7.5", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + } + ] + } + }, + { + "version": "0.7.4", + "tag": "@rushstack/module-minifier_v0.7.4", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + } + ] + } + }, + { + "version": "0.7.3", + "tag": "@rushstack/module-minifier_v0.7.3", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "patch": [ + { + "comment": "Bump the `serialize-javascript` dependency." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + } + ] + } + }, + { + "version": "0.7.2", + "tag": "@rushstack/module-minifier_v0.7.2", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "patch": [ + { + "comment": "Prefer `os.availableParallelism()` to `os.cpus().length`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + } + ] + } + }, + { + "version": "0.7.1", + "tag": "@rushstack/module-minifier_v0.7.1", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + } + ] + } + }, + { + "version": "0.7.0", + "tag": "@rushstack/module-minifier_v0.7.0", + "date": "Wed, 22 Jan 2025 03:03:47 GMT", + "comments": { + "minor": [ + { + "comment": "Add a `workerResourceLimits` option to the `WorkerPoolMinifier` constructor to control the available resources to the workers." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.5.0`" + } + ] + } + }, + { + "version": "0.6.36", + "tag": "@rushstack/module-minifier_v0.6.36", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.81`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + } + ] + } + }, + { + "version": "0.6.35", + "tag": "@rushstack/module-minifier_v0.6.35", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.80`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + } + ] + } + }, + { + "version": "0.6.34", + "tag": "@rushstack/module-minifier_v0.6.34", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.79`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + } + ] + } + }, + { + "version": "0.6.33", + "tag": "@rushstack/module-minifier_v0.6.33", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.78`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + } + ] + } + }, + { + "version": "0.6.32", + "tag": "@rushstack/module-minifier_v0.6.32", + "date": "Tue, 03 Dec 2024 16:11:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.77`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + } + ] + } + }, + { + "version": "0.6.31", + "tag": "@rushstack/module-minifier_v0.6.31", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.76`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + } + ] + } + }, + { + "version": "0.6.30", + "tag": "@rushstack/module-minifier_v0.6.30", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.75`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + } + ] + } + }, + { + "version": "0.6.29", + "tag": "@rushstack/module-minifier_v0.6.29", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.74`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + } + ] + } + }, + { + "version": "0.6.28", + "tag": "@rushstack/module-minifier_v0.6.28", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.73`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + } + ] + } + }, + { + "version": "0.6.27", + "tag": "@rushstack/module-minifier_v0.6.27", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.72`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + } + ] + } + }, + { + "version": "0.6.26", + "tag": "@rushstack/module-minifier_v0.6.26", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.71`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + } + ] + } + }, + { + "version": "0.6.25", + "tag": "@rushstack/module-minifier_v0.6.25", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.70`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + } + ] + } + }, + { + "version": "0.6.24", + "tag": "@rushstack/module-minifier_v0.6.24", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.69`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + } + ] + } + }, + { + "version": "0.6.23", + "tag": "@rushstack/module-minifier_v0.6.23", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.68`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + } + ] + } + }, + { + "version": "0.6.22", + "tag": "@rushstack/module-minifier_v0.6.22", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.67`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + } + ] + } + }, + { + "version": "0.6.21", + "tag": "@rushstack/module-minifier_v0.6.21", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.66`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + } + ] + } + }, + { + "version": "0.6.20", + "tag": "@rushstack/module-minifier_v0.6.20", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.65`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + } + ] + } + }, + { + "version": "0.6.19", + "tag": "@rushstack/module-minifier_v0.6.19", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.64`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + } + ] + } + }, + { + "version": "0.6.18", + "tag": "@rushstack/module-minifier_v0.6.18", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.63`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + } + ] + } + }, + { + "version": "0.6.17", + "tag": "@rushstack/module-minifier_v0.6.17", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.62`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + } + ] + } + }, + { + "version": "0.6.16", + "tag": "@rushstack/module-minifier_v0.6.16", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.61`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + } + ] + } + }, + { + "version": "0.6.15", + "tag": "@rushstack/module-minifier_v0.6.15", + "date": "Wed, 17 Jul 2024 06:55:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.60`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + } + ] + } + }, + { + "version": "0.6.14", + "tag": "@rushstack/module-minifier_v0.6.14", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.59`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + } + ] + } + }, + { + "version": "0.6.13", + "tag": "@rushstack/module-minifier_v0.6.13", + "date": "Tue, 16 Jul 2024 00:36:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.58`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + } + ] + } + }, + { + "version": "0.6.12", + "tag": "@rushstack/module-minifier_v0.6.12", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.57`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + } + ] + } + }, + { + "version": "0.6.11", + "tag": "@rushstack/module-minifier_v0.6.11", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.56`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + } + ] + } + }, + { + "version": "0.6.10", + "tag": "@rushstack/module-minifier_v0.6.10", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.55`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + } + ] + } + }, + { + "version": "0.6.9", + "tag": "@rushstack/module-minifier_v0.6.9", + "date": "Wed, 29 May 2024 02:03:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.54`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + } + ] + } + }, + { + "version": "0.6.8", + "tag": "@rushstack/module-minifier_v0.6.8", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.53`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + } + ] + } + }, + { + "version": "0.6.7", + "tag": "@rushstack/module-minifier_v0.6.7", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.52`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + } + ] + } + }, + { + "version": "0.6.6", + "tag": "@rushstack/module-minifier_v0.6.6", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.51`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + } + ] + } + }, + { + "version": "0.6.5", + "tag": "@rushstack/module-minifier_v0.6.5", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.50`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + } + ] + } + }, + { + "version": "0.6.4", + "tag": "@rushstack/module-minifier_v0.6.4", + "date": "Fri, 24 May 2024 00:15:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.49`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + } + ] + } + }, + { + "version": "0.6.3", + "tag": "@rushstack/module-minifier_v0.6.3", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.48`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + } + ] + } + }, + { + "version": "0.6.2", + "tag": "@rushstack/module-minifier_v0.6.2", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.47`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + } + ] + } + }, + { + "version": "0.6.1", + "tag": "@rushstack/module-minifier_v0.6.1", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.46`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + } + ] + } + }, + { + "version": "0.6.0", + "tag": "@rushstack/module-minifier_v0.6.0", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "minor": [ + { + "comment": "Rename `IMinifierConnection.disconnect` to `IMinifierConnection.disconnectAsync` and `IModuleMinifier.connect` to `IModuleMinifier.connectAsync`. The old functions are marked as `@deprecated`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.45`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + } + ] + } + }, + { + "version": "0.5.4", + "tag": "@rushstack/module-minifier_v0.5.4", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.44`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + } + ] + } + }, + { + "version": "0.5.3", + "tag": "@rushstack/module-minifier_v0.5.3", + "date": "Wed, 08 May 2024 22:23:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.43`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + } + ] + } + }, + { + "version": "0.5.2", + "tag": "@rushstack/module-minifier_v0.5.2", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.42`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + } + ] + } + }, + { + "version": "0.5.1", + "tag": "@rushstack/module-minifier_v0.5.1", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.41`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + } + ] + } + }, + { + "version": "0.5.0", + "tag": "@rushstack/module-minifier_v0.5.0", + "date": "Thu, 28 Mar 2024 22:42:23 GMT", + "comments": { + "minor": [ + { + "comment": "Gracefully exit minifier worker instead of using `process.exit(0)`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.40`" + } + ] + } + }, + { + "version": "0.4.40", + "tag": "@rushstack/module-minifier_v0.4.40", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.39`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + } + ] + } + }, + { + "version": "0.4.39", + "tag": "@rushstack/module-minifier_v0.4.39", + "date": "Sat, 16 Mar 2024 00:11:37 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where the assets listed in sourcemaps were incomplete or missing." + } + ] + } + }, + { + "version": "0.4.38", + "tag": "@rushstack/module-minifier_v0.4.38", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + } + ] + } + }, + { + "version": "0.4.37", + "tag": "@rushstack/module-minifier_v0.4.37", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + } + ] + } + }, + { + "version": "0.4.36", + "tag": "@rushstack/module-minifier_v0.4.36", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + } + ] + } + }, + { + "version": "0.4.35", + "tag": "@rushstack/module-minifier_v0.4.35", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + } + ] + } + }, + { + "version": "0.4.34", + "tag": "@rushstack/module-minifier_v0.4.34", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + } + ] + } + }, + { + "version": "0.4.33", + "tag": "@rushstack/module-minifier_v0.4.33", + "date": "Thu, 29 Feb 2024 07:11:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + } + ] + } + }, + { + "version": "0.4.32", + "tag": "@rushstack/module-minifier_v0.4.32", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + } + ] + } + }, + { + "version": "0.4.31", + "tag": "@rushstack/module-minifier_v0.4.31", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + } + ] + } + }, + { + "version": "0.4.30", + "tag": "@rushstack/module-minifier_v0.4.30", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + } + ] + } + }, + { + "version": "0.4.29", + "tag": "@rushstack/module-minifier_v0.4.29", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + } + ] + } + }, + { + "version": "0.4.28", + "tag": "@rushstack/module-minifier_v0.4.28", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + } + ] + } + }, + { + "version": "0.4.27", + "tag": "@rushstack/module-minifier_v0.4.27", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + } + ] + } + }, + { + "version": "0.4.26", + "tag": "@rushstack/module-minifier_v0.4.26", + "date": "Tue, 20 Feb 2024 16:10:53 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + } + ] + } + }, + { + "version": "0.4.25", + "tag": "@rushstack/module-minifier_v0.4.25", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + } + ] + } + }, + { + "version": "0.4.24", + "tag": "@rushstack/module-minifier_v0.4.24", + "date": "Sat, 17 Feb 2024 06:24:34 GMT", + "comments": { + "patch": [ + { + "comment": "Fix broken link to API documentation" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + } + ] + } + }, + { + "version": "0.4.23", + "tag": "@rushstack/module-minifier_v0.4.23", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + } + ] + } + }, + { + "version": "0.4.22", + "tag": "@rushstack/module-minifier_v0.4.22", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + } + ] + } + }, + { + "version": "0.4.21", + "tag": "@rushstack/module-minifier_v0.4.21", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + } + ] + } + }, + { + "version": "0.4.20", + "tag": "@rushstack/module-minifier_v0.4.20", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + } + ] + } + }, + { + "version": "0.4.19", + "tag": "@rushstack/module-minifier_v0.4.19", + "date": "Tue, 23 Jan 2024 20:12:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + } + ] + } + }, + { + "version": "0.4.18", + "tag": "@rushstack/module-minifier_v0.4.18", + "date": "Tue, 23 Jan 2024 16:15:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + } + ] + } + }, + { + "version": "0.4.17", + "tag": "@rushstack/module-minifier_v0.4.17", + "date": "Tue, 16 Jan 2024 18:30:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + } + ] + } + }, + { + "version": "0.4.16", + "tag": "@rushstack/module-minifier_v0.4.16", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + } + ] + } + }, + { + "version": "0.4.15", + "tag": "@rushstack/module-minifier_v0.4.15", + "date": "Wed, 20 Dec 2023 01:09:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + } + ] + } + }, + { + "version": "0.4.14", + "tag": "@rushstack/module-minifier_v0.4.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + } + ] + } + }, + { + "version": "0.4.13", + "tag": "@rushstack/module-minifier_v0.4.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + } + ] + } + }, + { + "version": "0.4.12", + "tag": "@rushstack/module-minifier_v0.4.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + } + ] + } + }, + { + "version": "0.4.11", + "tag": "@rushstack/module-minifier_v0.4.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + } + ] + } + }, + { + "version": "0.4.10", + "tag": "@rushstack/module-minifier_v0.4.10", + "date": "Mon, 30 Oct 2023 23:36:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + } + ] + } + }, + { + "version": "0.4.9", + "tag": "@rushstack/module-minifier_v0.4.9", + "date": "Sun, 01 Oct 2023 02:56:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + } + ] + } + }, + { + "version": "0.4.8", + "tag": "@rushstack/module-minifier_v0.4.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + } + ] + } + }, + { + "version": "0.4.7", + "tag": "@rushstack/module-minifier_v0.4.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + } + ] + } + }, + { + "version": "0.4.6", + "tag": "@rushstack/module-minifier_v0.4.6", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + } + ] + } + }, + { + "version": "0.4.5", + "tag": "@rushstack/module-minifier_v0.4.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + } + ] + } + }, + { + "version": "0.4.4", + "tag": "@rushstack/module-minifier_v0.4.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + } + ] + } + }, + { + "version": "0.4.3", + "tag": "@rushstack/module-minifier_v0.4.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + } + ] + } + }, + { + "version": "0.4.2", + "tag": "@rushstack/module-minifier_v0.4.2", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + } + ] + } + }, + { + "version": "0.4.1", + "tag": "@rushstack/module-minifier_v0.4.1", + "date": "Tue, 19 Sep 2023 15:21:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.24`" + } + ] + } + }, + { + "version": "0.4.0", + "tag": "@rushstack/module-minifier_v0.4.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.23`" + } + ] + } + }, + { + "version": "0.3.38", + "tag": "@rushstack/module-minifier_v0.3.38", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.37`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.22`" + } + ] + } + }, + { + "version": "0.3.37", + "tag": "@rushstack/module-minifier_v0.3.37", + "date": "Mon, 31 Jul 2023 15:19:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.21`" + } + ] + } + }, + { + "version": "0.3.36", + "tag": "@rushstack/module-minifier_v0.3.36", + "date": "Sat, 29 Jul 2023 00:22:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.20`" + } + ] + } + }, + { + "version": "0.3.35", + "tag": "@rushstack/module-minifier_v0.3.35", + "date": "Thu, 20 Jul 2023 20:47:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.19`" + } + ] + } + }, + { + "version": "0.3.34", + "tag": "@rushstack/module-minifier_v0.3.34", + "date": "Wed, 19 Jul 2023 00:20:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.18`" + } + ] + } + }, + { + "version": "0.3.33", + "tag": "@rushstack/module-minifier_v0.3.33", + "date": "Fri, 14 Jul 2023 15:20:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.17`" + } + ] + } + }, + { + "version": "0.3.32", + "tag": "@rushstack/module-minifier_v0.3.32", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.16`" + } + ] + } + }, + { + "version": "0.3.31", + "tag": "@rushstack/module-minifier_v0.3.31", + "date": "Wed, 12 Jul 2023 15:20:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.15`" + } + ] + } + }, + { + "version": "0.3.30", + "tag": "@rushstack/module-minifier_v0.3.30", + "date": "Wed, 12 Jul 2023 00:23:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.14`" + } + ] + } + }, + { + "version": "0.3.29", + "tag": "@rushstack/module-minifier_v0.3.29", + "date": "Fri, 07 Jul 2023 00:19:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.13`" + } + ] + } + }, + { + "version": "0.3.28", + "tag": "@rushstack/module-minifier_v0.3.28", + "date": "Thu, 06 Jul 2023 00:16:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.12`" + } + ] + } + }, + { + "version": "0.3.27", + "tag": "@rushstack/module-minifier_v0.3.27", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.11`" + } + ] + } + }, + { + "version": "0.3.26", + "tag": "@rushstack/module-minifier_v0.3.26", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.10`" + } + ] + } + }, + { + "version": "0.3.25", + "tag": "@rushstack/module-minifier_v0.3.25", + "date": "Thu, 15 Jun 2023 00:21:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.24`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.9`" + } + ] + } + }, + { + "version": "0.3.24", + "tag": "@rushstack/module-minifier_v0.3.24", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.8`" + } + ] + } + }, + { + "version": "0.3.23", + "tag": "@rushstack/module-minifier_v0.3.23", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.7`" + } + ] + } + }, + { + "version": "0.3.22", + "tag": "@rushstack/module-minifier_v0.3.22", + "date": "Tue, 13 Jun 2023 01:49:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.6`" + } + ] + } + }, + { + "version": "0.3.21", + "tag": "@rushstack/module-minifier_v0.3.21", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.5`" + } + ] + } + }, + { + "version": "0.3.20", + "tag": "@rushstack/module-minifier_v0.3.20", + "date": "Fri, 09 Jun 2023 15:23:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.4`" + } + ] + } + }, + { + "version": "0.3.19", + "tag": "@rushstack/module-minifier_v0.3.19", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.3`" + } + ] + } + }, + { + "version": "0.3.18", + "tag": "@rushstack/module-minifier_v0.3.18", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.2`" + } + ] + } + }, + { + "version": "0.3.17", + "tag": "@rushstack/module-minifier_v0.3.17", + "date": "Thu, 08 Jun 2023 00:20:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.1`" + } + ] + } + }, + { + "version": "0.3.16", + "tag": "@rushstack/module-minifier_v0.3.16", + "date": "Wed, 07 Jun 2023 22:45:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.15`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.0`" + } + ] + } + }, + { + "version": "0.3.15", + "tag": "@rushstack/module-minifier_v0.3.15", + "date": "Tue, 06 Jun 2023 02:52:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.1.0`" + } + ] + } + }, + { + "version": "0.3.14", + "tag": "@rushstack/module-minifier_v0.3.14", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/worker-pool\" to `0.3.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.0.1`" + } + ] + } + }, { "version": "0.3.13", "tag": "@rushstack/module-minifier_v0.3.13", diff --git a/libraries/module-minifier/CHANGELOG.md b/libraries/module-minifier/CHANGELOG.md index cea5dab8085..0d253ea641d 100644 --- a/libraries/module-minifier/CHANGELOG.md +++ b/libraries/module-minifier/CHANGELOG.md @@ -1,6 +1,933 @@ # Change Log - @rushstack/module-minifier -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 0.9.24 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 0.9.23 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 0.9.22 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.9.21 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.9.20 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 0.9.19 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.9.18 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 0.9.17 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 0.9.16 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 0.9.15 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.9.14 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 0.9.13 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.9.12 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 0.9.11 +Thu, 02 Apr 2026 00:14:38 GMT + +### Patches + +- Bump serialize-javascript 7.0.5 to address CVE GHSA-qj8w-gfj5-8c6v + +## 0.9.10 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 0.9.9 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.9.8 +Tue, 10 Mar 2026 15:13:12 GMT + +### Patches + +- Bump `serialize-javascript` to partially mitigate CVE-2020-7660. + +## 0.9.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 0.9.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.9.5 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 0.9.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.9.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 0.9.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 0.9.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.9.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.8.14 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 0.8.13 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 0.8.12 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 0.8.11 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 0.8.10 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 0.8.9 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 0.8.8 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 0.8.7 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 0.8.6 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 0.8.5 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 0.8.4 +Tue, 04 Nov 2025 08:15:15 GMT + +_Version update only_ + +## 0.8.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 0.8.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 0.8.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 0.8.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 0.7.30 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 0.7.29 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.7.28 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.7.27 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.7.26 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.7.25 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 0.7.24 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.7.23 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.7.22 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.7.21 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.7.20 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.7.19 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.7.18 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.7.17 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.7.16 +Tue, 15 Apr 2025 15:11:58 GMT + +_Version update only_ + +## 0.7.15 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 0.7.14 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.7.13 +Tue, 25 Mar 2025 15:11:16 GMT + +_Version update only_ + +## 0.7.12 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.7.11 +Wed, 12 Mar 2025 00:11:32 GMT + +_Version update only_ + +## 0.7.10 +Tue, 11 Mar 2025 02:12:34 GMT + +_Version update only_ + +## 0.7.9 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.7.8 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.7.7 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.7.6 +Wed, 26 Feb 2025 16:11:12 GMT + +_Version update only_ + +## 0.7.5 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.7.4 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.7.3 +Wed, 12 Feb 2025 01:10:52 GMT + +### Patches + +- Bump the `serialize-javascript` dependency. + +## 0.7.2 +Thu, 30 Jan 2025 16:10:36 GMT + +### Patches + +- Prefer `os.availableParallelism()` to `os.cpus().length`. + +## 0.7.1 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.7.0 +Wed, 22 Jan 2025 03:03:47 GMT + +### Minor changes + +- Add a `workerResourceLimits` option to the `WorkerPoolMinifier` constructor to control the available resources to the workers. + +## 0.6.36 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.6.35 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.6.34 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.6.33 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.6.32 +Tue, 03 Dec 2024 16:11:08 GMT + +_Version update only_ + +## 0.6.31 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.6.30 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.6.29 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.6.28 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.6.27 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.6.26 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.6.25 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.6.24 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.6.23 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.6.22 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.6.21 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.6.20 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.6.19 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.6.18 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.6.17 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.6.16 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 0.6.15 +Wed, 17 Jul 2024 06:55:10 GMT + +_Version update only_ + +## 0.6.14 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.6.13 +Tue, 16 Jul 2024 00:36:22 GMT + +_Version update only_ + +## 0.6.12 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.6.11 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.6.10 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.6.9 +Wed, 29 May 2024 02:03:51 GMT + +_Version update only_ + +## 0.6.8 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.6.7 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.6.6 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.6.5 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.6.4 +Fri, 24 May 2024 00:15:09 GMT + +_Version update only_ + +## 0.6.3 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 0.6.2 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.6.1 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.6.0 +Wed, 15 May 2024 06:04:17 GMT + +### Minor changes + +- Rename `IMinifierConnection.disconnect` to `IMinifierConnection.disconnectAsync` and `IModuleMinifier.connect` to `IModuleMinifier.connectAsync`. The old functions are marked as `@deprecated`. + +## 0.5.4 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.5.3 +Wed, 08 May 2024 22:23:51 GMT + +_Version update only_ + +## 0.5.2 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 0.5.1 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.5.0 +Thu, 28 Mar 2024 22:42:23 GMT + +### Minor changes + +- Gracefully exit minifier worker instead of using `process.exit(0)`. + +## 0.4.40 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 0.4.39 +Sat, 16 Mar 2024 00:11:37 GMT + +### Patches + +- Fix an issue where the assets listed in sourcemaps were incomplete or missing. + +## 0.4.38 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 0.4.37 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 0.4.36 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 0.4.35 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 0.4.34 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 0.4.33 +Thu, 29 Feb 2024 07:11:46 GMT + +_Version update only_ + +## 0.4.32 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.4.31 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.4.30 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.4.29 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.4.28 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.4.27 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.4.26 +Tue, 20 Feb 2024 16:10:53 GMT + +_Version update only_ + +## 0.4.25 +Mon, 19 Feb 2024 21:54:27 GMT + +_Version update only_ + +## 0.4.24 +Sat, 17 Feb 2024 06:24:34 GMT + +### Patches + +- Fix broken link to API documentation + +## 0.4.23 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 0.4.22 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.4.21 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.4.20 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.4.19 +Tue, 23 Jan 2024 20:12:58 GMT + +_Version update only_ + +## 0.4.18 +Tue, 23 Jan 2024 16:15:06 GMT + +_Version update only_ + +## 0.4.17 +Tue, 16 Jan 2024 18:30:11 GMT + +_Version update only_ + +## 0.4.16 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.4.15 +Wed, 20 Dec 2023 01:09:46 GMT + +_Version update only_ + +## 0.4.14 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.4.13 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.4.12 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 0.4.11 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 0.4.10 +Mon, 30 Oct 2023 23:36:38 GMT + +_Version update only_ + +## 0.4.9 +Sun, 01 Oct 2023 02:56:30 GMT + +_Version update only_ + +## 0.4.8 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 0.4.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.4.6 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 0.4.5 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 0.4.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.4.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 0.4.2 +Fri, 22 Sep 2023 00:05:50 GMT + +_Version update only_ + +## 0.4.1 +Tue, 19 Sep 2023 15:21:52 GMT + +_Version update only_ + +## 0.4.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 0.3.38 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 0.3.37 +Mon, 31 Jul 2023 15:19:06 GMT + +_Version update only_ + +## 0.3.36 +Sat, 29 Jul 2023 00:22:51 GMT + +_Version update only_ + +## 0.3.35 +Thu, 20 Jul 2023 20:47:28 GMT + +_Version update only_ + +## 0.3.34 +Wed, 19 Jul 2023 00:20:32 GMT + +_Version update only_ + +## 0.3.33 +Fri, 14 Jul 2023 15:20:45 GMT + +_Version update only_ + +## 0.3.32 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 0.3.31 +Wed, 12 Jul 2023 15:20:40 GMT + +_Version update only_ + +## 0.3.30 +Wed, 12 Jul 2023 00:23:30 GMT + +_Version update only_ + +## 0.3.29 +Fri, 07 Jul 2023 00:19:33 GMT + +_Version update only_ + +## 0.3.28 +Thu, 06 Jul 2023 00:16:20 GMT + +_Version update only_ + +## 0.3.27 +Tue, 04 Jul 2023 00:18:47 GMT + +_Version update only_ + +## 0.3.26 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 0.3.25 +Thu, 15 Jun 2023 00:21:02 GMT + +_Version update only_ + +## 0.3.24 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 0.3.23 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 0.3.22 +Tue, 13 Jun 2023 01:49:02 GMT + +_Version update only_ + +## 0.3.21 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 0.3.20 +Fri, 09 Jun 2023 15:23:15 GMT + +_Version update only_ + +## 0.3.19 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 0.3.18 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 0.3.17 +Thu, 08 Jun 2023 00:20:03 GMT + +_Version update only_ + +## 0.3.16 +Wed, 07 Jun 2023 22:45:17 GMT + +_Version update only_ + +## 0.3.15 +Tue, 06 Jun 2023 02:52:51 GMT + +_Version update only_ + +## 0.3.14 +Mon, 05 Jun 2023 21:45:21 GMT + +_Version update only_ ## 0.3.13 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/libraries/module-minifier/README.md b/libraries/module-minifier/README.md index bf2b247f59b..f532520ef55 100644 --- a/libraries/module-minifier/README.md +++ b/libraries/module-minifier/README.md @@ -7,6 +7,6 @@ This library wraps terser in convenient handles for parallelization. It powers @ - [CHANGELOG.md]( https://github.com/microsoft/rushstack/blob/main/libraries/module-minifier/CHANGELOG.md) - Find out what's new in the latest version -- [API Reference](https://rushstack.io/pages/api/module-minifier/) +- [API Reference](https://api.rushstack.io/pages/module-minifier/) `@rushstack/module-minifier` is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/libraries/module-minifier/config/api-extractor.json b/libraries/module-minifier/config/api-extractor.json index 996e271d3dd..3dbb76c0e6f 100644 --- a/libraries/module-minifier/config/api-extractor.json +++ b/libraries/module-minifier/config/api-extractor.json @@ -1,19 +1,4 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - - "mainEntryPointFilePath": "/lib/index.d.ts", - - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, - - "docModel": { - "enabled": true, - "apiJsonFilePath": "../../../common/temp/api/.api.json" - }, - - "dtsRollup": { - "enabled": true - } + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json" } diff --git a/libraries/module-minifier/config/jest.config.json b/libraries/module-minifier/config/jest.config.json index 4bb17bde3ee..d1749681d90 100644 --- a/libraries/module-minifier/config/jest.config.json +++ b/libraries/module-minifier/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "local-node-rig/profiles/default/config/jest.config.json" } diff --git a/libraries/module-minifier/config/rig.json b/libraries/module-minifier/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/libraries/module-minifier/config/rig.json +++ b/libraries/module-minifier/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/libraries/module-minifier/eslint.config.js b/libraries/module-minifier/eslint.config.js new file mode 100644 index 00000000000..87132f43292 --- /dev/null +++ b/libraries/module-minifier/eslint.config.js @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); +const tsdocMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/tsdoc'); + +module.exports = [ + ...nodeProfile, + ...friendlyLocalsMixin, + ...tsdocMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/module-minifier/package.json b/libraries/module-minifier/package.json index d2da3360395..62290a4c892 100644 --- a/libraries/module-minifier/package.json +++ b/libraries/module-minifier/package.json @@ -1,9 +1,32 @@ { "name": "@rushstack/module-minifier", - "version": "0.3.13", + "version": "0.9.24", "description": "Wrapper for terser to support bulk parallel minification.", - "main": "lib/index.js", - "typings": "dist/module-minifier.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/module-minifier.d.ts", + "exports": { + ".": { + "types": "./dist/module-minifier.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "license": "MIT", "repository": { "url": "https://github.com/microsoft/rushstack.git", @@ -17,17 +40,16 @@ }, "dependencies": { "@rushstack/worker-pool": "workspace:*", - "serialize-javascript": "6.0.0", + "serialize-javascript": "7.0.5", "source-map": "~0.7.3", "terser": "^5.9.0" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", - "@types/serialize-javascript": "5.0.2" + "@types/node": "20.17.19", + "@types/serialize-javascript": "5.0.4", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" }, "peerDependencies": { "@types/node": "*" @@ -36,5 +58,6 @@ "@types/node": { "optional": true } - } + }, + "sideEffects": false } diff --git a/libraries/module-minifier/src/LocalMinifier.ts b/libraries/module-minifier/src/LocalMinifier.ts index 231b8e3ea9b..984098fb76c 100644 --- a/libraries/module-minifier/src/LocalMinifier.ts +++ b/libraries/module-minifier/src/LocalMinifier.ts @@ -1,7 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { createHash } from 'crypto'; +import { createHash } from 'node:crypto'; + +import './cryptoPolyfill'; import serialize from 'serialize-javascript'; import type { MinifyOptions } from 'terser'; @@ -84,12 +86,25 @@ export class LocalMinifier implements IModuleMinifier { }); } - public async connect(): Promise { + /** + * {@inheritdoc IModuleMinifier.connectAsync} + */ + public async connectAsync(): Promise { + const disconnectAsync: IMinifierConnection['disconnectAsync'] = async () => { + // Do nothing. + }; return { configHash: this._configHash, - disconnect: async () => { - // Do nothing. - } + disconnectAsync, + disconnect: disconnectAsync }; } + + /** + * @deprecated Use {@link LocalMinifier.connectAsync} instead. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + public async connect(): Promise { + return await this.connectAsync(); + } } diff --git a/libraries/module-minifier/src/MessagePortMinifier.ts b/libraries/module-minifier/src/MessagePortMinifier.ts index 5274b2b6510..45259e90665 100644 --- a/libraries/module-minifier/src/MessagePortMinifier.ts +++ b/libraries/module-minifier/src/MessagePortMinifier.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { once } from 'events'; -import { MessagePort } from 'worker_threads'; +import { once } from 'node:events'; +import type * as WorkerThreads from 'node:worker_threads'; import type { IMinifierConnection, @@ -17,11 +17,11 @@ import type { * @public */ export class MessagePortMinifier implements IModuleMinifier { - public readonly port: MessagePort; + public readonly port: WorkerThreads.MessagePort; private readonly _callbacks: Map; - public constructor(port: MessagePort) { + public constructor(port: WorkerThreads.MessagePort) { this.port = port; this._callbacks = new Map(); } @@ -45,7 +45,10 @@ export class MessagePortMinifier implements IModuleMinifier { this.port.postMessage(request); } - public async connect(): Promise { + /** + * {@inheritdoc IModuleMinifier.connectAsync} + */ + public async connectAsync(): Promise { const configHashPromise: Promise = once(this.port, 'message') as unknown as Promise; this.port.postMessage('initialize'); const configHash: string = await configHashPromise; @@ -63,12 +66,22 @@ export class MessagePortMinifier implements IModuleMinifier { } this.port.on('message', handler); + const disconnectAsync: IMinifierConnection['disconnectAsync'] = async () => { + this.port.off('message', handler); + this.port.close(); + }; return { configHash, - disconnect: async () => { - this.port.off('message', handler); - this.port.close(); - } + disconnectAsync, + disconnect: disconnectAsync }; } + + /** + * @deprecated Use {@link MessagePortMinifier.connectAsync} instead + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + public async connect(): Promise { + return await this.connectAsync(); + } } diff --git a/libraries/module-minifier/src/MinifiedIdentifier.ts b/libraries/module-minifier/src/MinifiedIdentifier.ts index f408b7dfc6b..c46488e540d 100644 --- a/libraries/module-minifier/src/MinifiedIdentifier.ts +++ b/libraries/module-minifier/src/MinifiedIdentifier.ts @@ -79,11 +79,13 @@ const RESERVED_KEYWORDS: string[] = [ export function getIdentifierInternal(ordinal: number): string { let ret: string = IDENTIFIER_LEADING_DIGITS[ordinal % 54]; - ordinal = (ordinal / 54) | 0; // eslint-disable-line no-bitwise + // eslint-disable-next-line no-bitwise + ordinal = (ordinal / 54) | 0; while (ordinal > 0) { --ordinal; - ret += IDENTIFIER_TRAILING_DIGITS[ordinal & 0x3f]; // eslint-disable-line no-bitwise - ordinal >>>= 6; // eslint-disable-line no-bitwise + // eslint-disable-next-line no-bitwise + ret += IDENTIFIER_TRAILING_DIGITS[ordinal & 0x3f]; + ordinal >>>= 6; } return ret; @@ -112,7 +114,7 @@ export function getOrdinalFromIdentifierInternal(identifier: string): number { return NaN; } - ordinal <<= 6; // eslint-disable-line no-bitwise + ordinal <<= 6; ordinal += trailingCharIndex.get(identifier.charCodeAt(i))! + 1; } diff --git a/libraries/module-minifier/src/MinifierWorker.ts b/libraries/module-minifier/src/MinifierWorker.ts index d919c05a004..6eea649283f 100644 --- a/libraries/module-minifier/src/MinifierWorker.ts +++ b/libraries/module-minifier/src/MinifierWorker.ts @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import { parentPort, workerData } from 'node:worker_threads'; + import type { MinifyOptions } from 'terser'; -import { parentPort, workerData } from 'worker_threads'; import { minifySingleFileAsync } from './MinifySingleFile'; import type { IModuleMinificationRequest, IModuleMinificationResult } from './types'; @@ -12,12 +13,19 @@ const terserOptions: MinifyOptions = workerData; // Set to non-zero to help debug unexpected graceful exit process.exitCode = 2; -parentPort!.on('message', async (message: IModuleMinificationRequest) => { +async function handler(message: IModuleMinificationRequest): Promise { if (!message) { - process.exit(0); + parentPort!.off('postMessage', handler); + parentPort!.close(); + return; } const result: IModuleMinificationResult = await minifySingleFileAsync(message, terserOptions); parentPort!.postMessage(result); +} + +parentPort!.once('close', () => { + process.exitCode = 0; }); +parentPort!.on('message', handler); diff --git a/libraries/module-minifier/src/MinifySingleFile.ts b/libraries/module-minifier/src/MinifySingleFile.ts index 66736ec753b..64bd9b04731 100644 --- a/libraries/module-minifier/src/MinifySingleFile.ts +++ b/libraries/module-minifier/src/MinifySingleFile.ts @@ -1,16 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { minify, MinifyOptions, MinifyOutput, SimpleIdentifierMangler } from 'terser'; +import { minify, type MinifyOptions, type MinifyOutput, type SimpleIdentifierMangler } from 'terser'; import type { RawSourceMap } from 'source-map'; -declare module 'terser' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface SourceMapOptions { - asObject?: boolean; - } -} - import { getIdentifier } from './MinifiedIdentifier'; import type { IModuleMinificationRequest, IModuleMinificationResult } from './types'; @@ -56,11 +49,15 @@ export async function minifySingleFileAsync( mangle.reserved = mangle.reserved ? externals.concat(mangle.reserved) : externals; } - finalOptions.sourceMap = nameForMap - ? { - asObject: true - } - : false; + // SourceMap is only generated if nameForMap is provided- overrides terserOptions.sourceMap + if (nameForMap) { + finalOptions.sourceMap = { + includeSources: true, + asObject: true + }; + } else { + finalOptions.sourceMap = false; + } const minified: MinifyOutput = await minify( { @@ -76,6 +73,7 @@ export async function minifySingleFileAsync( hash }; } catch (error) { + // eslint-disable-next-line no-console console.error(error); return { error: error as Error, diff --git a/libraries/module-minifier/src/NoopMinifier.ts b/libraries/module-minifier/src/NoopMinifier.ts index e969ea53f19..c20c4fe593a 100644 --- a/libraries/module-minifier/src/NoopMinifier.ts +++ b/libraries/module-minifier/src/NoopMinifier.ts @@ -40,13 +40,26 @@ export class NoopMinifier implements IModuleMinifier { }); } - public async connect(): Promise { + /** + * {@inheritdoc IModuleMinifier.connectAsync} + */ + public async connectAsync(): Promise { + const disconnectAsync: IMinifierConnection['disconnectAsync'] = async () => { + // Do nothing. + }; + return { configHash: NoopMinifier.name, - - disconnect: async () => { - // Do nothing. - } + disconnectAsync, + disconnect: disconnectAsync }; } + + /** + * @deprecated Use {@link NoopMinifier.connectAsync} instead + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + public async connect(): Promise { + return await this.connectAsync(); + } } diff --git a/libraries/module-minifier/src/WorkerPoolMinifier.ts b/libraries/module-minifier/src/WorkerPoolMinifier.ts index c369f520839..e9b6b9c0bc0 100644 --- a/libraries/module-minifier/src/WorkerPoolMinifier.ts +++ b/libraries/module-minifier/src/WorkerPoolMinifier.ts @@ -1,11 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { createHash } from 'crypto'; -import { cpus } from 'os'; +import { createHash } from 'node:crypto'; +import os from 'node:os'; +import type { ResourceLimits } from 'node:worker_threads'; +import './cryptoPolyfill'; import serialize from 'serialize-javascript'; import type { MinifyOptions } from 'terser'; + import { WorkerPool } from '@rushstack/worker-pool'; import type { @@ -23,7 +26,7 @@ import type { export interface IWorkerPoolMinifierOptions { /** * Maximum number of worker threads to use. Will never use more than there are modules to process. - * Defaults to os.cpus().length + * Defaults to os.availableParallelism() */ maxThreads?: number; /** @@ -36,6 +39,11 @@ export interface IWorkerPoolMinifierOptions { * If true, log to the console about the minification results. */ verbose?: boolean; + + /** + * Optional resource limits for the workers. + */ + workerResourceLimits?: ResourceLimits; } /** @@ -55,7 +63,12 @@ export class WorkerPoolMinifier implements IModuleMinifier { private readonly _activeRequests: Map; public constructor(options: IWorkerPoolMinifierOptions) { - const { maxThreads = cpus().length, terserOptions = {}, verbose = false } = options || {}; + const { + maxThreads = os.availableParallelism?.() ?? os.cpus().length, + terserOptions = {}, + verbose = false, + workerResourceLimits + } = options || {}; const activeRequests: Map = new Map(); const resultCache: Map = new Map(); @@ -63,7 +76,8 @@ export class WorkerPoolMinifier implements IModuleMinifier { id: 'Minifier', maxWorkers: maxThreads, workerData: terserOptions, - workerScriptPath: require.resolve('./MinifierWorker') + workerScriptPath: require.resolve('./MinifierWorker'), + workerResourceLimits }); const { version: terserVersion } = require('terser/package.json'); @@ -124,11 +138,13 @@ export class WorkerPoolMinifier implements IModuleMinifier { message: IModuleMinificationResult ): void => { worker.off('message', cb); - const callbacks: IModuleMinificationCallback[] | undefined = activeRequests.get(message.hash)!; + const workerCallbacks: IModuleMinificationCallback[] | undefined = activeRequests.get( + message.hash + )!; activeRequests.delete(message.hash); this._resultCache.set(message.hash, message); - for (const callback of callbacks) { - callback(message); + for (const workerCallback of workerCallbacks) { + workerCallback(message); } // This should always be the last thing done with the worker this._pool.checkinWorker(worker); @@ -150,29 +166,45 @@ export class WorkerPoolMinifier implements IModuleMinifier { }); } - public async connect(): Promise { + /** + * {@inheritdoc IModuleMinifier.connectAsync} + */ + public async connectAsync(): Promise { if (++this._refCount === 1) { this._pool.reset(); } + const disconnectAsync: IMinifierConnection['disconnectAsync'] = async () => { + if (--this._refCount === 0) { + if (this._verbose) { + // eslint-disable-next-line no-console + console.log(`Shutting down minifier worker pool`); + } + await this._pool.finishAsync(); + this._resultCache.clear(); + this._activeRequests.clear(); + if (this._verbose) { + // eslint-disable-next-line no-console + console.log(`Module minification: ${this._deduped} Deduped, ${this._minified} Processed`); + } + } + this._deduped = 0; + this._minified = 0; + }; + return { configHash: this._configHash, - disconnect: async () => { - if (--this._refCount === 0) { - if (this._verbose) { - console.log(`Shutting down minifier worker pool`); - } - await this._pool.finishAsync(); - this._resultCache.clear(); - this._activeRequests.clear(); - if (this._verbose) { - console.log(`Module minification: ${this._deduped} Deduped, ${this._minified} Processed`); - } - } - this._deduped = 0; - this._minified = 0; - } + disconnectAsync, + disconnect: disconnectAsync }; } + + /** + * @deprecated Use {@link WorkerPoolMinifier.connectAsync} instead + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + public async connect(): Promise { + return await this.connectAsync(); + } } diff --git a/libraries/module-minifier/src/cryptoPolyfill.ts b/libraries/module-minifier/src/cryptoPolyfill.ts new file mode 100644 index 00000000000..fbd539ab2ba --- /dev/null +++ b/libraries/module-minifier/src/cryptoPolyfill.ts @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// Polyfill globalThis.crypto for Node 18, which doesn't expose it as a global. +// serialize-javascript accesses crypto.randomBytes() at module load time, +// which requires globalThis.crypto to be the Node.js crypto module. +// Remove this when deprecating nodev18 support. +import { webcrypto } from 'node:crypto'; + +if (!globalThis.crypto) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (globalThis as any).crypto = webcrypto; +} diff --git a/libraries/module-minifier/src/index.ts b/libraries/module-minifier/src/index.ts index ca82f966b67..b8b6d53f8de 100644 --- a/libraries/module-minifier/src/index.ts +++ b/libraries/module-minifier/src/index.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +/// + /** * This library wraps terser in convenient handles for parallelization. * It powers `@rushstack/webpack4-module-minifier-plugin` and `@rushstack/webpack5-module-minifier-plugin` diff --git a/libraries/module-minifier/src/test/LocalMinifier.test.ts b/libraries/module-minifier/src/test/LocalMinifier.test.ts index a135eb51272..1be9cfa8be8 100644 --- a/libraries/module-minifier/src/test/LocalMinifier.test.ts +++ b/libraries/module-minifier/src/test/LocalMinifier.test.ts @@ -15,6 +15,7 @@ jest.mock('terser/package.json', () => { describe('LocalMinifier', () => { it('Includes terserOptions in config hash', async () => { const { LocalMinifier } = await import('../LocalMinifier'); + // eslint-disable-next-line @typescript-eslint/no-redeclare type LocalMinifier = typeof LocalMinifier.prototype; const minifier1: LocalMinifier = new LocalMinifier({ @@ -28,10 +29,10 @@ describe('LocalMinifier', () => { } }); - const connection1: IMinifierConnection = await minifier1.connect(); - await connection1.disconnect(); - const connection2: IMinifierConnection = await minifier2.connect(); - await connection2.disconnect(); + const connection1: IMinifierConnection = await minifier1.connectAsync(); + await connection1.disconnectAsync(); + const connection2: IMinifierConnection = await minifier2.connectAsync(); + await connection2.disconnectAsync(); expect(connection1.configHash).toMatchSnapshot('ecma5'); expect(connection2.configHash).toMatchSnapshot('ecma2015'); @@ -40,6 +41,7 @@ describe('LocalMinifier', () => { it('Includes terser package version in config hash', async () => { const { LocalMinifier } = await import('../LocalMinifier'); + // eslint-disable-next-line @typescript-eslint/no-redeclare type LocalMinifier = typeof LocalMinifier.prototype; terserVersion = '5.9.1'; @@ -47,10 +49,10 @@ describe('LocalMinifier', () => { terserVersion = '5.16.2'; const minifier2: LocalMinifier = new LocalMinifier({}); - const connection1: IMinifierConnection = await minifier1.connect(); - await connection1.disconnect(); - const connection2: IMinifierConnection = await minifier2.connect(); - await connection2.disconnect(); + const connection1: IMinifierConnection = await minifier1.connectAsync(); + await connection1.disconnectAsync(); + const connection2: IMinifierConnection = await minifier2.connectAsync(); + await connection2.disconnectAsync(); expect(connection1.configHash).toMatchSnapshot('terser-5.9.1'); expect(connection2.configHash).toMatchSnapshot('terser-5.16.1'); diff --git a/libraries/module-minifier/src/test/MinifiedIdentifier.test.ts b/libraries/module-minifier/src/test/MinifiedIdentifier.test.ts index a6caa1765b8..3999b90b4af 100644 --- a/libraries/module-minifier/src/test/MinifiedIdentifier.test.ts +++ b/libraries/module-minifier/src/test/MinifiedIdentifier.test.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import { getIdentifierInternal, getOrdinalFromIdentifierInternal, diff --git a/libraries/module-minifier/src/test/WorkerPoolMinifier.test.ts b/libraries/module-minifier/src/test/WorkerPoolMinifier.test.ts index 89fd95cd326..f0ea7e27383 100644 --- a/libraries/module-minifier/src/test/WorkerPoolMinifier.test.ts +++ b/libraries/module-minifier/src/test/WorkerPoolMinifier.test.ts @@ -15,6 +15,7 @@ jest.mock('terser/package.json', () => { describe('WorkerPoolMinifier', () => { it('Includes terserOptions in config hash', async () => { const { WorkerPoolMinifier } = await import('../WorkerPoolMinifier'); + // eslint-disable-next-line @typescript-eslint/no-redeclare type WorkerPoolMinifier = typeof WorkerPoolMinifier.prototype; const minifier1: WorkerPoolMinifier = new WorkerPoolMinifier({ @@ -28,10 +29,10 @@ describe('WorkerPoolMinifier', () => { } }); - const connection1: IMinifierConnection = await minifier1.connect(); - await connection1.disconnect(); - const connection2: IMinifierConnection = await minifier2.connect(); - await connection2.disconnect(); + const connection1: IMinifierConnection = await minifier1.connectAsync(); + await connection1.disconnectAsync(); + const connection2: IMinifierConnection = await minifier2.connectAsync(); + await connection2.disconnectAsync(); expect(connection1.configHash).toMatchSnapshot('ecma5'); expect(connection2.configHash).toMatchSnapshot('ecma2015'); @@ -40,6 +41,7 @@ describe('WorkerPoolMinifier', () => { it('Includes terser package version in config hash', async () => { const { WorkerPoolMinifier } = await import('../WorkerPoolMinifier'); + // eslint-disable-next-line @typescript-eslint/no-redeclare type WorkerPoolMinifier = typeof WorkerPoolMinifier.prototype; terserVersion = '5.9.1'; @@ -47,10 +49,10 @@ describe('WorkerPoolMinifier', () => { terserVersion = '5.16.2'; const minifier2: WorkerPoolMinifier = new WorkerPoolMinifier({}); - const connection1: IMinifierConnection = await minifier1.connect(); - await connection1.disconnect(); - const connection2: IMinifierConnection = await minifier2.connect(); - await connection2.disconnect(); + const connection1: IMinifierConnection = await minifier1.connectAsync(); + await connection1.disconnectAsync(); + const connection2: IMinifierConnection = await minifier2.connectAsync(); + await connection2.disconnectAsync(); expect(connection1.configHash).toMatchSnapshot('terser-5.9.1'); expect(connection2.configHash).toMatchSnapshot('terser-5.16.1'); diff --git a/libraries/module-minifier/src/test/__snapshots__/LocalMinifier.test.ts.snap b/libraries/module-minifier/src/test/__snapshots__/LocalMinifier.test.ts.snap index a310d9b8a69..7449707c9c2 100644 --- a/libraries/module-minifier/src/test/__snapshots__/LocalMinifier.test.ts.snap +++ b/libraries/module-minifier/src/test/__snapshots__/LocalMinifier.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`LocalMinifier Includes terser package version in config hash: terser-5.9.1 1`] = `"4EVkVBAkdvF45JdRt44fWqn3HDW5t4dHo0zmzpQrWtc="`; diff --git a/libraries/module-minifier/src/test/__snapshots__/MinifySingleFile.test.ts.snap b/libraries/module-minifier/src/test/__snapshots__/MinifySingleFile.test.ts.snap index a66aee94e2f..a3d83437cb0 100644 --- a/libraries/module-minifier/src/test/__snapshots__/MinifySingleFile.test.ts.snap +++ b/libraries/module-minifier/src/test/__snapshots__/MinifySingleFile.test.ts.snap @@ -1,8 +1,8 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`minifySingleFileAsync uses consistent identifiers for webpack vars 1`] = ` Object { - "code": "__MINIFY_MODULE__((function(e,t,n){}));", + "code": "__MINIFY_MODULE__(function(e,t,n){});", "error": undefined, "hash": "foo", "map": undefined, diff --git a/libraries/module-minifier/src/test/__snapshots__/WorkerPoolMinifier.test.ts.snap b/libraries/module-minifier/src/test/__snapshots__/WorkerPoolMinifier.test.ts.snap index 78c3f7dafef..e37e584947a 100644 --- a/libraries/module-minifier/src/test/__snapshots__/WorkerPoolMinifier.test.ts.snap +++ b/libraries/module-minifier/src/test/__snapshots__/WorkerPoolMinifier.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`WorkerPoolMinifier Includes terser package version in config hash: terser-5.9.1 1`] = `"0cjBnphY49XdxkmLxJLfNoRQbl3R6HdeT8WNl56oZWk="`; diff --git a/libraries/module-minifier/src/types.ts b/libraries/module-minifier/src/types.ts index 463b083ddc9..5340bb06b9b 100644 --- a/libraries/module-minifier/src/types.ts +++ b/libraries/module-minifier/src/types.ts @@ -103,10 +103,16 @@ export interface IMinifierConnection { * Hash of the configuration of this minifier, for cache busting. */ configHash: string; + /** - * Callback to be invoked when done with the minifier + * @deprecated Use {@link IMinifierConnection.disconnectAsync} instead. */ disconnect(): Promise; + + /** + * Callback to be invoked when done with the minifier + */ + disconnectAsync(): Promise; } /** @@ -119,10 +125,15 @@ export interface IModuleMinifier { */ minify: IModuleMinifierFunction; + /** + * @deprecated Use {@link IModuleMinifier.connectAsync} instead. + */ + connect(): Promise; + /** * Prevents the minifier from shutting down until the returned `disconnect()` callback is invoked. * The callback may be used to surface errors encountered by the minifier that may not be relevant to a specific file. * It should be called to allow the minifier to cleanup */ - connect(): Promise; + connectAsync(): Promise; } diff --git a/libraries/module-minifier/tsconfig.json b/libraries/module-minifier/tsconfig.json index 02e0a631784..f471d671b3f 100644 --- a/libraries/module-minifier/tsconfig.json +++ b/libraries/module-minifier/tsconfig.json @@ -1,9 +1,8 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { "allowSyntheticDefaultImports": true, - "target": "ES2019", - "types": ["heft-jest", "node"] + "target": "ES2019" } } diff --git a/libraries/node-core-library/.eslintrc.js b/libraries/node-core-library/.eslintrc.js deleted file mode 100644 index f7ee2a5d364..00000000000 --- a/libraries/node-core-library/.eslintrc.js +++ /dev/null @@ -1,11 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node', - '@rushstack/eslint-config/mixins/friendly-locals', - '@rushstack/eslint-config/mixins/tsdoc' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/libraries/node-core-library/.npmignore b/libraries/node-core-library/.npmignore index 302dbc5b019..f7a40e10213 100644 --- a/libraries/node-core-library/.npmignore +++ b/libraries/node-core-library/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,16 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/node-core-library/CHANGELOG.json b/libraries/node-core-library/CHANGELOG.json index c42da54bc6e..3144dc6573f 100644 --- a/libraries/node-core-library/CHANGELOG.json +++ b/libraries/node-core-library/CHANGELOG.json @@ -1,6 +1,830 @@ { "name": "@rushstack/node-core-library", "entries": [ + { + "version": "5.23.3", + "tag": "@rushstack/node-core-library_v5.23.3", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "patch": [ + { + "comment": "Replace `@override` with the `override` keyword." + } + ] + } + }, + { + "version": "5.23.2", + "tag": "@rushstack/node-core-library_v5.23.2", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "patch": [ + { + "comment": "Update `ajv` dependency to `~8.20.0` to resolve vulnerable transitive `fast-uri` versions." + } + ] + } + }, + { + "version": "5.23.1", + "tag": "@rushstack/node-core-library_v5.23.1", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "patch": [ + { + "comment": "Bump semver." + } + ] + } + }, + { + "version": "5.23.0", + "tag": "@rushstack/node-core-library_v5.23.0", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "minor": [ + { + "comment": "Add two new APIs: `Object.isRecord` asserts if an object is a `Record` object and `Object.mergeWith` is a customizable deep object merge." + } + ] + } + }, + { + "version": "5.22.0", + "tag": "@rushstack/node-core-library_v5.22.0", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "minor": [ + { + "comment": "Add `FileSystem.createReadStream`, `FileSystem.createWriteStream`, and `FileSystem.createWriteStreamAsync` APIs for creating read and write filesystem streams." + } + ] + } + }, + { + "version": "5.21.0", + "tag": "@rushstack/node-core-library_v5.21.0", + "date": "Tue, 31 Mar 2026 15:14:14 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where the LockFile API sometimes incorrectly reported a dirty acquisition, causing Rush autoinstaller failures (GitHub #5684)" + } + ], + "minor": [ + { + "comment": "Regression risk: This narrows when a lock is considered \"dirty\". Although the previous behavior was incorrect, the fix could break consumers that implicitly relied on those false positives." + } + ] + } + }, + { + "version": "5.20.3", + "tag": "@rushstack/node-core-library_v5.20.3", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "patch": [ + { + "comment": "Update `ajv` dependency to `~8.18.0` to mitigate CVE-2025-69873." + } + ] + } + }, + { + "version": "5.20.2", + "tag": "@rushstack/node-core-library_v5.20.2", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "patch": [ + { + "comment": "Fix race condition in FileSystem.create*Link helpers: EEXIST errors that occur after ensureFolder/ensureFolderAsync are now handled consistently with the initial EEXIST handling." + } + ] + } + }, + { + "version": "5.20.1", + "tag": "@rushstack/node-core-library_v5.20.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/problem-matcher\" to `0.2.1`" + } + ] + } + }, + { + "version": "5.20.0", + "tag": "@rushstack/node-core-library_v5.20.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + }, + { + "comment": "Add a property to the `JsonSchema` validator to control the handling of vendor extension keywords. By default, vendor extension keywords matching the `x--` pattern are accepted. Set the new `rejectVendorExtensionKeywords` option to `true` to restore the previous strict behavior." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/problem-matcher\" to `0.2.0`" + } + ] + } + }, + { + "version": "5.19.1", + "tag": "@rushstack/node-core-library_v5.19.1", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "patch": [ + { + "comment": "Replace fs-extra with node:fs in FileWriter" + } + ] + } + }, + { + "version": "5.19.0", + "tag": "@rushstack/node-core-library_v5.19.0", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "minor": [ + { + "comment": "Update `Executable.getProcessInfoBy*` APIs to use PowerShell on Windows to support latest Windows 11 versions." + } + ] + } + }, + { + "version": "5.18.0", + "tag": "@rushstack/node-core-library_v5.18.0", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "minor": [ + { + "comment": "Add \"Objects.areDeepEqual\" and \"User.getHomeFolder\" APIs." + } + ] + } + }, + { + "version": "5.17.1", + "tag": "@rushstack/node-core-library_v5.17.1", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "patch": [ + { + "comment": "Update the return type of `Executable.waitForExitAsync` to omit `stdout` and `stderr` if an `encoding` parameter isn't passed to the options object." + } + ] + } + }, + { + "version": "5.17.0", + "tag": "@rushstack/node-core-library_v5.17.0", + "date": "Wed, 08 Oct 2025 00:13:28 GMT", + "comments": { + "minor": [ + { + "comment": "Add an `allowOversubscription` option to the `Async` API functions which prevents running tasks from exceeding concurrency. Change its default to `false`." + } + ] + } + }, + { + "version": "5.16.0", + "tag": "@rushstack/node-core-library_v5.16.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ] + } + }, + { + "version": "5.15.1", + "tag": "@rushstack/node-core-library_v5.15.1", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/problem-matcher\" to `0.1.1`" + } + ] + } + }, + { + "version": "5.15.0", + "tag": "@rushstack/node-core-library_v5.15.0", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "minor": [ + { + "comment": "Add `FileError.getProblemMatcher()` which returns the problem matcher compatible with `IOperationExecutionResult.problemCollector` as well as VS Code, GitHub Actions" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/problem-matcher\" to `0.1.0`" + } + ] + } + }, + { + "version": "5.14.0", + "tag": "@rushstack/node-core-library_v5.14.0", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "minor": [ + { + "comment": "Add a `Async.runWithTimeoutAsync` function that executes an async function, resolving if the specified timeout elapses first." + } + ] + } + }, + { + "version": "5.13.1", + "tag": "@rushstack/node-core-library_v5.13.1", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "patch": [ + { + "comment": "Fix a bug in `FileSystem.isErrnoException` that failed to identify errors if the underlying method was invoked using only a file descriptor, e.g. for `fs.readSync`." + } + ] + } + }, + { + "version": "5.13.0", + "tag": "@rushstack/node-core-library_v5.13.0", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "minor": [ + { + "comment": "Expand `FileSystem.writeBuffersToFile` and `FileSystem.writeBuffersToFileAsync` to take more kinds of buffers." + } + ] + } + }, + { + "version": "5.12.0", + "tag": "@rushstack/node-core-library_v5.12.0", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "minor": [ + { + "comment": "Add `useNodeJSResolver` option to `Import.resolvePackage` to rely on the built-in `require.resolve` and share its cache." + }, + { + "comment": "In `RealNodeModulePathResolver`, add the option to configure to throw or not throw for non-existent paths." + } + ], + "patch": [ + { + "comment": "In `RealNodeModulePathResolver`, add negative caching when a path segment that might be a symbolic link is not." + } + ] + } + }, + { + "version": "5.11.0", + "tag": "@rushstack/node-core-library_v5.11.0", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "minor": [ + { + "comment": "Update fs-extra to 11.3.0." + } + ] + } + }, + { + "version": "5.10.2", + "tag": "@rushstack/node-core-library_v5.10.2", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "patch": [ + { + "comment": "Provide the `retryCount` parameter to actions executed using `Async.runWithRetriesAsync`" + } + ] + } + }, + { + "version": "5.10.1", + "tag": "@rushstack/node-core-library_v5.10.1", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "patch": [ + { + "comment": "Fix handling of trailing slashes and relative paths in RealNodeModulePath to match semantics of `fs.realpathSync.native`." + } + ] + } + }, + { + "version": "5.10.0", + "tag": "@rushstack/node-core-library_v5.10.0", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "minor": [ + { + "comment": "Add `RealNodeModulePathResolver` class to get equivalent behavior to `realpath` with fewer system calls (and therefore higher performance) in the typical scenario where the only symlinks in the repository are inside of `node_modules` folders and are links to package folders." + } + ] + } + }, + { + "version": "5.9.0", + "tag": "@rushstack/node-core-library_v5.9.0", + "date": "Fri, 13 Sep 2024 00:11:42 GMT", + "comments": { + "minor": [ + { + "comment": "Add a `Sort.sortKeys` function for sorting keys in an object" + }, + { + "comment": "Rename `LockFile.acquire` to `Lockfile.acquireAsync`." + } + ], + "patch": [ + { + "comment": "Fix an issue where attempting to acquire multiple `LockFile`s at the same time on POSIX would cause the second to immediately be acquired without releasing the first." + } + ] + } + }, + { + "version": "5.8.0", + "tag": "@rushstack/node-core-library_v5.8.0", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "minor": [ + { + "comment": "Add a `customFormats` option to `JsonSchema`." + } + ] + } + }, + { + "version": "5.7.0", + "tag": "@rushstack/node-core-library_v5.7.0", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "minor": [ + { + "comment": "Introduce a `Text.splitByNewLines` function." + } + ] + } + }, + { + "version": "5.6.0", + "tag": "@rushstack/node-core-library_v5.6.0", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "minor": [ + { + "comment": "Add a `ignoreSchemaField` option to the `JsonSchema.validateObject` options to ignore `$schema` properties and add an options object argument to `JsonSchema.validateObjectWithCallback` with the same `ignoreSchemaField` option." + } + ] + } + }, + { + "version": "5.5.1", + "tag": "@rushstack/node-core-library_v5.5.1", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ] + } + }, + { + "version": "5.5.0", + "tag": "@rushstack/node-core-library_v5.5.0", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for the `jsonSyntax` option to the `JsonFile.save`, `JsonFile.saveAsync`, and `JsonFile.stringify` functions." + } + ] + } + }, + { + "version": "5.4.1", + "tag": "@rushstack/node-core-library_v5.4.1", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "patch": [ + { + "comment": "Include missing `type` modifiers on type-only exports." + } + ] + } + }, + { + "version": "5.4.0", + "tag": "@rushstack/node-core-library_v5.4.0", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "minor": [ + { + "comment": "Add a `throwOnSignal` option to the `Executable.waitForExitAsync` to control if that function should throw if the process is terminated with a signal." + }, + { + "comment": "Add a `signal` property to the result of `Executable.waitForExitAsync` that includes a signal if the process was termianted by a signal." + } + ] + } + }, + { + "version": "5.3.0", + "tag": "@rushstack/node-core-library_v5.3.0", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "minor": [ + { + "comment": "Include typings for the the `\"files\"` field in `IPackageJson`." + } + ] + } + }, + { + "version": "5.2.0", + "tag": "@rushstack/node-core-library_v5.2.0", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "minor": [ + { + "comment": "Include typings for the `\"exports\"` and `\"typesVersions\"` fields in `IPackageJson`." + } + ] + } + }, + { + "version": "5.1.0", + "tag": "@rushstack/node-core-library_v5.1.0", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "minor": [ + { + "comment": "Update `JsonFile` to support loading JSON files that include object keys that are members of `Object.prototype`." + } + ], + "patch": [ + { + "comment": "Fix an issue with `JsonSchema` where `\"uniqueItems\": true` would throw an error if the `\"item\"` type in the schema has `\"type\": \"object\"`." + } + ] + } + }, + { + "version": "5.0.0", + "tag": "@rushstack/node-core-library_v5.0.0", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "major": [ + { + "comment": "Replace z-schema with ajv for schema validation and add support for json-schema draft-07." + }, + { + "comment": "Remove the deprecated `Async.sleep` function." + }, + { + "comment": "Convert `FileConstants` and `FolderConstants` from enums to const objects." + } + ], + "patch": [ + { + "comment": "Fix an issue where waitForExitAsync() might reject before all output was collected" + } + ] + } + }, + { + "version": "4.3.0", + "tag": "@rushstack/node-core-library_v4.3.0", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "minor": [ + { + "comment": "Rename `Async.sleep` to `Async.sleepAsync`. The old function is marked as `@deprecated`." + } + ] + } + }, + { + "version": "4.2.1", + "tag": "@rushstack/node-core-library_v4.2.1", + "date": "Fri, 10 May 2024 05:33:33 GMT", + "comments": { + "patch": [ + { + "comment": "Fix a bug in `Async.forEachAsync` where weight wasn't respected." + } + ] + } + }, + { + "version": "4.2.0", + "tag": "@rushstack/node-core-library_v4.2.0", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "minor": [ + { + "comment": "Add a new `weighted: true` option to the `Async.forEachAsync` method that allows each element to specify how much of the allowed parallelism the callback uses." + } + ], + "patch": [ + { + "comment": "Add a new `weighted: true` option to the `Async.mapAsync` method that allows each element to specify how much of the allowed parallelism the callback uses." + } + ] + } + }, + { + "version": "4.1.0", + "tag": "@rushstack/node-core-library_v4.1.0", + "date": "Wed, 10 Apr 2024 15:10:08 GMT", + "comments": { + "minor": [ + { + "comment": "Add `writeBuffersToFile` and `writeBuffersToFileAsync` methods to `FileSystem` for efficient writing of concatenated files." + } + ] + } + }, + { + "version": "4.0.2", + "tag": "@rushstack/node-core-library_v4.0.2", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "patch": [ + { + "comment": "Replace the dependency on the `colors` package with `Colorize` from `@rushstack/terminal`." + } + ] + } + }, + { + "version": "4.0.1", + "tag": "@rushstack/node-core-library_v4.0.1", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "patch": [ + { + "comment": "Remove a no longer needed dependency on the `colors` package" + } + ] + } + }, + { + "version": "4.0.0", + "tag": "@rushstack/node-core-library_v4.0.0", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "major": [ + { + "comment": "(BREAKING CHANGE) Remove the Terminal and related APIs (Colors, AsciEscape, etc). These have been moved into the @rushstack/terminal package. See https://github.com/microsoft/rushstack/pull/3176 for details." + }, + { + "comment": "Remove deprecated `FileSystem.readFolder`, `FileSystem.readFolderAsync`, and `LegacyAdapters.sortStable` APIs." + } + ], + "minor": [ + { + "comment": "Graduate `Async` and `MinimumHeap` APIs from beta to public." + } + ] + } + }, + { + "version": "3.66.1", + "tag": "@rushstack/node-core-library_v3.66.1", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix broken link to API documentation" + } + ] + } + }, + { + "version": "3.66.0", + "tag": "@rushstack/node-core-library_v3.66.0", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "patch": [ + { + "comment": "LockFile: prevent accidentaly deleting freshly created lockfile when multiple processes try to acquire the same lock on macOS/Linux" + } + ], + "minor": [ + { + "comment": "Add getStatistics() method to FileWriter instances" + } + ] + } + }, + { + "version": "3.65.0", + "tag": "@rushstack/node-core-library_v3.65.0", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "minor": [ + { + "comment": "Inclue a `Text.reverse` API for reversing a string." + } + ] + } + }, + { + "version": "3.64.2", + "tag": "@rushstack/node-core-library_v3.64.2", + "date": "Thu, 25 Jan 2024 01:09:29 GMT", + "comments": { + "patch": [ + { + "comment": "Improve 'bin' definition in `IPackageJson` type" + } + ] + } + }, + { + "version": "3.64.1", + "tag": "@rushstack/node-core-library_v3.64.1", + "date": "Tue, 23 Jan 2024 20:12:57 GMT", + "comments": { + "patch": [ + { + "comment": "Fix Executable.getProcessInfoBy* methods truncating the process name on MacOS" + } + ] + } + }, + { + "version": "3.64.0", + "tag": "@rushstack/node-core-library_v3.64.0", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "minor": [ + { + "comment": "Add the `dependenciesMeta` property to the `INodePackageJson` interface." + } + ] + } + }, + { + "version": "3.63.0", + "tag": "@rushstack/node-core-library_v3.63.0", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "minor": [ + { + "comment": "Updates the `JsonFile` API to format JSON as JSON5 if an existing string is being updated to preserve the style of the existing JSON." + } + ] + } + }, + { + "version": "3.62.0", + "tag": "@rushstack/node-core-library_v3.62.0", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "minor": [ + { + "comment": "Add functions inside the `Executable` API to list all process trees (`getProcessInfoById`, `getProcessInfoByIdAsync`, `getProcessInfoByName`, and `getProcessInfoByNameAsync`)." + }, + { + "comment": "Add functions inside the `Text` API to split iterables (or async iterables) that produce strings or buffers on newlines (`readLinesFromIterable` and `readLinesFromIterableAsync`)." + }, + { + "comment": "Add the `waitForExitAsync` method inside the `Executable` API used to wait for a provided child process to exit." + } + ] + } + }, + { + "version": "3.61.0", + "tag": "@rushstack/node-core-library_v3.61.0", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "minor": [ + { + "comment": "Add Async.getSignal for promise-based signaling. Add MinimumHeap for use as a priority queue." + } + ] + } + }, + { + "version": "3.60.1", + "tag": "@rushstack/node-core-library_v3.60.1", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ] + } + }, + { + "version": "3.60.0", + "tag": "@rushstack/node-core-library_v3.60.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + } + ] + } + }, + { + "version": "3.59.7", + "tag": "@rushstack/node-core-library_v3.59.7", + "date": "Tue, 08 Aug 2023 07:10:39 GMT", + "comments": { + "none": [ + { + "comment": "Update error messages when modules or packages cannot be found using the \"Import.resolve*\" methods." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + } + ] + } + }, + { + "version": "3.59.6", + "tag": "@rushstack/node-core-library_v3.59.6", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "patch": [ + { + "comment": "Updated semver dependency" + } + ] + } + }, + { + "version": "3.59.5", + "tag": "@rushstack/node-core-library_v3.59.5", + "date": "Thu, 06 Jul 2023 00:16:19 GMT", + "comments": { + "patch": [ + { + "comment": "Fix Import.resolveModule* and Import.resolvePackage* methods to return real-paths when resolving self-referencing specs" + } + ] + } + }, + { + "version": "3.59.4", + "tag": "@rushstack/node-core-library_v3.59.4", + "date": "Thu, 15 Jun 2023 00:21:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + } + ] + } + }, + { + "version": "3.59.3", + "tag": "@rushstack/node-core-library_v3.59.3", + "date": "Wed, 07 Jun 2023 22:45:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + } + ] + } + }, { "version": "3.59.2", "tag": "@rushstack/node-core-library_v3.59.2", diff --git a/libraries/node-core-library/CHANGELOG.md b/libraries/node-core-library/CHANGELOG.md index 3670d6abaae..a71294af2b3 100644 --- a/libraries/node-core-library/CHANGELOG.md +++ b/libraries/node-core-library/CHANGELOG.md @@ -1,6 +1,466 @@ # Change Log - @rushstack/node-core-library -This log was last generated on Mon, 29 May 2023 15:21:15 GMT and should not be manually modified. +This log was last generated on Fri, 17 Jul 2026 00:15:59 GMT and should not be manually modified. + +## 5.23.3 +Fri, 17 Jul 2026 00:15:59 GMT + +### Patches + +- Replace `@override` with the `override` keyword. + +## 5.23.2 +Thu, 16 Jul 2026 00:16:13 GMT + +### Patches + +- Update `ajv` dependency to `~8.20.0` to resolve vulnerable transitive `fast-uri` versions. + +## 5.23.1 +Sat, 18 Apr 2026 00:15:16 GMT + +### Patches + +- Bump semver. + +## 5.23.0 +Fri, 17 Apr 2026 15:14:57 GMT + +### Minor changes + +- Add two new APIs: `Object.isRecord` asserts if an object is a `Record` object and `Object.mergeWith` is a customizable deep object merge. + +## 5.22.0 +Thu, 09 Apr 2026 00:15:07 GMT + +### Minor changes + +- Add `FileSystem.createReadStream`, `FileSystem.createWriteStream`, and `FileSystem.createWriteStreamAsync` APIs for creating read and write filesystem streams. + +## 5.21.0 +Tue, 31 Mar 2026 15:14:14 GMT + +### Minor changes + +- Regression risk: This narrows when a lock is considered "dirty". Although the previous behavior was incorrect, the fix could break consumers that implicitly relied on those false positives. + +### Patches + +- Fix an issue where the LockFile API sometimes incorrectly reported a dirty acquisition, causing Rush autoinstaller failures (GitHub #5684) + +## 5.20.3 +Wed, 25 Feb 2026 00:34:29 GMT + +### Patches + +- Update `ajv` dependency to `~8.18.0` to mitigate CVE-2025-69873. + +## 5.20.2 +Tue, 24 Feb 2026 01:13:27 GMT + +### Patches + +- Fix race condition in FileSystem.create*Link helpers: EEXIST errors that occur after ensureFolder/ensureFolderAsync are now handled consistently with the initial EEXIST handling. + +## 5.20.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 5.20.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. +- Add a property to the `JsonSchema` validator to control the handling of vendor extension keywords. By default, vendor extension keywords matching the `x--` pattern are accepted. Set the new `rejectVendorExtensionKeywords` option to `true` to restore the previous strict behavior. + +## 5.19.1 +Sat, 06 Dec 2025 01:12:28 GMT + +### Patches + +- Replace fs-extra with node:fs in FileWriter + +## 5.19.0 +Fri, 21 Nov 2025 16:13:56 GMT + +### Minor changes + +- Update `Executable.getProcessInfoBy*` APIs to use PowerShell on Windows to support latest Windows 11 versions. + +## 5.18.0 +Fri, 24 Oct 2025 00:13:38 GMT + +### Minor changes + +- Add "Objects.areDeepEqual" and "User.getHomeFolder" APIs. + +## 5.17.1 +Wed, 22 Oct 2025 00:57:54 GMT + +### Patches + +- Update the return type of `Executable.waitForExitAsync` to omit `stdout` and `stderr` if an `encoding` parameter isn't passed to the options object. + +## 5.17.0 +Wed, 08 Oct 2025 00:13:28 GMT + +### Minor changes + +- Add an `allowOversubscription` option to the `Async` API functions which prevents running tasks from exceeding concurrency. Change its default to `false`. + +## 5.16.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 5.15.1 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 5.15.0 +Tue, 30 Sep 2025 20:33:51 GMT + +### Minor changes + +- Add `FileError.getProblemMatcher()` which returns the problem matcher compatible with `IOperationExecutionResult.problemCollector` as well as VS Code, GitHub Actions + +## 5.14.0 +Wed, 23 Jul 2025 20:55:57 GMT + +### Minor changes + +- Add a `Async.runWithTimeoutAsync` function that executes an async function, resolving if the specified timeout elapses first. + +## 5.13.1 +Thu, 01 May 2025 00:11:12 GMT + +### Patches + +- Fix a bug in `FileSystem.isErrnoException` that failed to identify errors if the underlying method was invoked using only a file descriptor, e.g. for `fs.readSync`. + +## 5.13.0 +Tue, 25 Mar 2025 15:11:15 GMT + +### Minor changes + +- Expand `FileSystem.writeBuffersToFile` and `FileSystem.writeBuffersToFileAsync` to take more kinds of buffers. + +## 5.12.0 +Tue, 11 Mar 2025 02:12:33 GMT + +### Minor changes + +- Add `useNodeJSResolver` option to `Import.resolvePackage` to rely on the built-in `require.resolve` and share its cache. +- In `RealNodeModulePathResolver`, add the option to configure to throw or not throw for non-existent paths. + +### Patches + +- In `RealNodeModulePathResolver`, add negative caching when a path segment that might be a symbolic link is not. + +## 5.11.0 +Thu, 30 Jan 2025 01:11:42 GMT + +### Minor changes + +- Update fs-extra to 11.3.0. + +## 5.10.2 +Thu, 09 Jan 2025 01:10:10 GMT + +### Patches + +- Provide the `retryCount` parameter to actions executed using `Async.runWithRetriesAsync` + +## 5.10.1 +Sat, 14 Dec 2024 01:11:07 GMT + +### Patches + +- Fix handling of trailing slashes and relative paths in RealNodeModulePath to match semantics of `fs.realpathSync.native`. + +## 5.10.0 +Fri, 22 Nov 2024 01:10:43 GMT + +### Minor changes + +- Add `RealNodeModulePathResolver` class to get equivalent behavior to `realpath` with fewer system calls (and therefore higher performance) in the typical scenario where the only symlinks in the repository are inside of `node_modules` folders and are links to package folders. + +## 5.9.0 +Fri, 13 Sep 2024 00:11:42 GMT + +### Minor changes + +- Add a `Sort.sortKeys` function for sorting keys in an object +- Rename `LockFile.acquire` to `Lockfile.acquireAsync`. + +### Patches + +- Fix an issue where attempting to acquire multiple `LockFile`s at the same time on POSIX would cause the second to immediately be acquired without releasing the first. + +## 5.8.0 +Tue, 10 Sep 2024 20:08:11 GMT + +### Minor changes + +- Add a `customFormats` option to `JsonSchema`. + +## 5.7.0 +Wed, 21 Aug 2024 05:43:04 GMT + +### Minor changes + +- Introduce a `Text.splitByNewLines` function. + +## 5.6.0 +Mon, 12 Aug 2024 22:16:04 GMT + +### Minor changes + +- Add a `ignoreSchemaField` option to the `JsonSchema.validateObject` options to ignore `$schema` properties and add an options object argument to `JsonSchema.validateObjectWithCallback` with the same `ignoreSchemaField` option. + +## 5.5.1 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 5.5.0 +Tue, 16 Jul 2024 00:36:21 GMT + +### Minor changes + +- Add support for the `jsonSyntax` option to the `JsonFile.save`, `JsonFile.saveAsync`, and `JsonFile.stringify` functions. + +## 5.4.1 +Thu, 30 May 2024 00:13:05 GMT + +### Patches + +- Include missing `type` modifiers on type-only exports. + +## 5.4.0 +Wed, 29 May 2024 02:03:50 GMT + +### Minor changes + +- Add a `throwOnSignal` option to the `Executable.waitForExitAsync` to control if that function should throw if the process is terminated with a signal. +- Add a `signal` property to the result of `Executable.waitForExitAsync` that includes a signal if the process was termianted by a signal. + +## 5.3.0 +Tue, 28 May 2024 15:10:09 GMT + +### Minor changes + +- Include typings for the the `"files"` field in `IPackageJson`. + +## 5.2.0 +Tue, 28 May 2024 00:09:47 GMT + +### Minor changes + +- Include typings for the `"exports"` and `"typesVersions"` fields in `IPackageJson`. + +## 5.1.0 +Sat, 25 May 2024 04:54:07 GMT + +### Minor changes + +- Update `JsonFile` to support loading JSON files that include object keys that are members of `Object.prototype`. + +### Patches + +- Fix an issue with `JsonSchema` where `"uniqueItems": true` would throw an error if the `"item"` type in the schema has `"type": "object"`. + +## 5.0.0 +Thu, 23 May 2024 02:26:56 GMT + +### Breaking changes + +- Replace z-schema with ajv for schema validation and add support for json-schema draft-07. +- Remove the deprecated `Async.sleep` function. +- Convert `FileConstants` and `FolderConstants` from enums to const objects. + +### Patches + +- Fix an issue where waitForExitAsync() might reject before all output was collected + +## 4.3.0 +Wed, 15 May 2024 06:04:17 GMT + +### Minor changes + +- Rename `Async.sleep` to `Async.sleepAsync`. The old function is marked as `@deprecated`. + +## 4.2.1 +Fri, 10 May 2024 05:33:33 GMT + +### Patches + +- Fix a bug in `Async.forEachAsync` where weight wasn't respected. + +## 4.2.0 +Mon, 06 May 2024 15:11:04 GMT + +### Minor changes + +- Add a new `weighted: true` option to the `Async.forEachAsync` method that allows each element to specify how much of the allowed parallelism the callback uses. + +### Patches + +- Add a new `weighted: true` option to the `Async.mapAsync` method that allows each element to specify how much of the allowed parallelism the callback uses. + +## 4.1.0 +Wed, 10 Apr 2024 15:10:08 GMT + +### Minor changes + +- Add `writeBuffersToFile` and `writeBuffersToFileAsync` methods to `FileSystem` for efficient writing of concatenated files. + +## 4.0.2 +Wed, 21 Feb 2024 21:45:28 GMT + +### Patches + +- Replace the dependency on the `colors` package with `Colorize` from `@rushstack/terminal`. + +## 4.0.1 +Tue, 20 Feb 2024 21:45:10 GMT + +### Patches + +- Remove a no longer needed dependency on the `colors` package + +## 4.0.0 +Mon, 19 Feb 2024 21:54:27 GMT + +### Breaking changes + +- (BREAKING CHANGE) Remove the Terminal and related APIs (Colors, AsciEscape, etc). These have been moved into the @rushstack/terminal package. See https://github.com/microsoft/rushstack/pull/3176 for details. +- Remove deprecated `FileSystem.readFolder`, `FileSystem.readFolderAsync`, and `LegacyAdapters.sortStable` APIs. + +### Minor changes + +- Graduate `Async` and `MinimumHeap` APIs from beta to public. + +## 3.66.1 +Sat, 17 Feb 2024 06:24:35 GMT + +### Patches + +- Fix broken link to API documentation + +## 3.66.0 +Thu, 08 Feb 2024 01:09:21 GMT + +### Minor changes + +- Add getStatistics() method to FileWriter instances + +### Patches + +- LockFile: prevent accidentaly deleting freshly created lockfile when multiple processes try to acquire the same lock on macOS/Linux + +## 3.65.0 +Mon, 05 Feb 2024 23:46:52 GMT + +### Minor changes + +- Inclue a `Text.reverse` API for reversing a string. + +## 3.64.2 +Thu, 25 Jan 2024 01:09:29 GMT + +### Patches + +- Improve 'bin' definition in `IPackageJson` type + +## 3.64.1 +Tue, 23 Jan 2024 20:12:57 GMT + +### Patches + +- Fix Executable.getProcessInfoBy* methods truncating the process name on MacOS + +## 3.64.0 +Tue, 23 Jan 2024 16:15:05 GMT + +### Minor changes + +- Add the `dependenciesMeta` property to the `INodePackageJson` interface. + +## 3.63.0 +Wed, 03 Jan 2024 00:31:18 GMT + +### Minor changes + +- Updates the `JsonFile` API to format JSON as JSON5 if an existing string is being updated to preserve the style of the existing JSON. + +## 3.62.0 +Thu, 07 Dec 2023 03:44:13 GMT + +### Minor changes + +- Add functions inside the `Executable` API to list all process trees (`getProcessInfoById`, `getProcessInfoByIdAsync`, `getProcessInfoByName`, and `getProcessInfoByNameAsync`). +- Add functions inside the `Text` API to split iterables (or async iterables) that produce strings or buffers on newlines (`readLinesFromIterable` and `readLinesFromIterableAsync`). +- Add the `waitForExitAsync` method inside the `Executable` API used to wait for a provided child process to exit. + +## 3.61.0 +Thu, 28 Sep 2023 20:53:17 GMT + +### Minor changes + +- Add Async.getSignal for promise-based signaling. Add MinimumHeap for use as a priority queue. + +## 3.60.1 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 3.60.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 3.59.7 +Tue, 08 Aug 2023 07:10:39 GMT + +_Version update only_ + +## 3.59.6 +Wed, 19 Jul 2023 00:20:31 GMT + +### Patches + +- Updated semver dependency + +## 3.59.5 +Thu, 06 Jul 2023 00:16:19 GMT + +### Patches + +- Fix Import.resolveModule* and Import.resolvePackage* methods to return real-paths when resolving self-referencing specs + +## 3.59.4 +Thu, 15 Jun 2023 00:21:01 GMT + +_Version update only_ + +## 3.59.3 +Wed, 07 Jun 2023 22:45:16 GMT + +_Version update only_ ## 3.59.2 Mon, 29 May 2023 15:21:15 GMT diff --git a/libraries/node-core-library/README.md b/libraries/node-core-library/README.md index ecdfac76aca..73e7901f785 100644 --- a/libraries/node-core-library/README.md +++ b/libraries/node-core-library/README.md @@ -33,6 +33,6 @@ demonstrated. If in doubt, create your own NPM package. - [CHANGELOG.md]( https://github.com/microsoft/rushstack/blob/main/libraries/node-core-library/CHANGELOG.md) - Find out what's new in the latest version -- [API Reference](https://rushstack.io/pages/api/node-core-library/) +- [API Reference](https://api.rushstack.io/pages/node-core-library/) `@rushstack/node-core-library` is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/libraries/node-core-library/config/api-extractor.json b/libraries/node-core-library/config/api-extractor.json index 996e271d3dd..7c99207b503 100644 --- a/libraries/node-core-library/config/api-extractor.json +++ b/libraries/node-core-library/config/api-extractor.json @@ -1,19 +1,6 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/api-extractor-base.json", - "mainEntryPointFilePath": "/lib/index.d.ts", - - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, - - "docModel": { - "enabled": true, - "apiJsonFilePath": "../../../common/temp/api/.api.json" - }, - - "dtsRollup": { - "enabled": true - } + "bundledPackages": ["@rushstack/problem-matcher"] } diff --git a/libraries/node-core-library/config/heft.json b/libraries/node-core-library/config/heft.json deleted file mode 100644 index b7b21396eeb..00000000000 --- a/libraries/node-core-library/config/heft.json +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Defines configuration used by core Heft. - */ -{ - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", - - /** - * Optionally specifies another JSON config file that this file extends from. This provides a way for standard - * settings to be shared across multiple projects. - */ - "extends": "@rushstack/heft-node-rig/profiles/default/config/heft.json", - - "eventActions": [ - // { - // /** - // * (Required) The kind of built-in operation that should be performed. - // * The "deleteGlobs" action deletes files or folders that match the specified glob patterns. - // */ - // "actionKind": "deleteGlobs", - // - // /** - // * (Required) The Heft stage when this action should be performed. Note that heft.json event actions - // * are scheduled after any plugin tasks have processed the event. For example, a "compile" event action - // * will be performed after the TypeScript compiler has been invoked. - // * - // * Options: "clean", "pre-compile", "compile", "bundle", "post-build" - // */ - // "heftEvent": "clean", - // - // /** - // * (Required) A user-defined tag whose purpose is to allow configs to replace/delete handlers that - // * were added by other configs. - // */ - // "actionId": "my-example-action", - // - // /** - // * (Required) Glob patterns to be deleted. The paths are resolved relative to the project folder. - // * Documentation for supported glob syntaxes: https://www.npmjs.com/package/fast-glob - // */ - // "globsToDelete": [ - // "dist", - // "lib", - // "lib-esnext", - // "temp" - // ] - // }, - // - // { - // /** - // * (Required) The kind of built-in operation that should be performed. - // * The "copyFiles" action copies files that match the specified patterns. - // */ - // "actionKind": "copyFiles", - // - // /** - // * (Required) The Heft stage when this action should be performed. Note that heft.json event actions - // * are scheduled after any plugin tasks have processed the event. For example, a "compile" event action - // * will be performed after the TypeScript compiler has been invoked. - // * - // * Options: "pre-compile", "compile", "bundle", "post-build" - // */ - // "heftEvent": "pre-compile", - // - // /** - // * (Required) A user-defined tag whose purpose is to allow configs to replace/delete handlers that - // * were added by other configs. - // */ - // "actionId": "my-example-action", - // - // /** - // * (Required) An array of copy operations to run perform during the specified Heft event. - // */ - // "copyOperations": [ - // { - // /** - // * (Required) The base folder that files will be copied from, relative to the project root. - // * Settings such as "includeGlobs" and "excludeGlobs" will be resolved relative - // * to this folder. - // * NOTE: Assigning "sourceFolder" does not by itself select any files to be copied. - // */ - // "sourceFolder": "src", - // - // /** - // * (Required) One or more folders that files will be copied into, relative to the project root. - // * If you specify more than one destination folder, Heft will read the input files only once, using - // * streams to efficiently write multiple outputs. - // */ - // "destinationFolders": ["dist/assets"], - // - // /** - // * If specified, this option recursively scans all folders under "sourceFolder" and includes any files - // * that match the specified extensions. (If "fileExtensions" and "includeGlobs" are both - // * specified, their selections are added together.) - // */ - // "fileExtensions": [".jpg", ".png"], - // - // /** - // * A list of glob patterns that select files to be copied. The paths are resolved relative - // * to "sourceFolder". - // * Documentation for supported glob syntaxes: https://www.npmjs.com/package/fast-glob - // */ - // "includeGlobs": ["assets/*.md"], - // - // /** - // * A list of glob patterns that exclude files/folders from being copied. The paths are resolved relative - // * to "sourceFolder". These exclusions eliminate items that were selected by the "includeGlobs" - // * or "fileExtensions" setting. - // */ - // "excludeGlobs": [], - // - // /** - // * Normally, when files are selected under a child folder, a corresponding folder will be created in - // * the destination folder. Specify flatten=true to discard the source path and copy all matching files - // * to the same folder. If two files have the same name an error will be reported. - // * The default value is false. - // */ - // "flatten": false, - // - // /** - // * If true, filesystem hard links will be created instead of copying the file. Depending on the - // * operating system, this may be faster. (But note that it may cause unexpected behavior if a tool - // * modifies the link.) The default value is false. - // */ - // "hardlink": false - // } - // ] - // } - - { - "actionKind": "copyFiles", - "heftEvent": "pre-compile", - "actionId": "copy-test-assets", - - "copyOperations": [ - { - "sourceFolder": "src", - "destinationFolders": ["lib"], - "fileExtensions": [".lock"] - } - ] - } - ], - - /** - * The list of Heft plugins to be loaded. - */ - "heftPlugins": [ - // { - // /** - // * The path to the plugin package. - // */ - // "plugin": "path/to/my-plugin", - // - // /** - // * An optional object that provides additional settings that may be defined by the plugin. - // */ - // // "options": { } - // } - ] -} diff --git a/libraries/node-core-library/config/jest.config.json b/libraries/node-core-library/config/jest.config.json index 4bb17bde3ee..7c0f9ccc9d6 100644 --- a/libraries/node-core-library/config/jest.config.json +++ b/libraries/node-core-library/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "decoupled-local-node-rig/profiles/default/config/jest.config.json" } diff --git a/libraries/node-core-library/config/rig.json b/libraries/node-core-library/config/rig.json index 6ac88a96368..cc98dea43dd 100644 --- a/libraries/node-core-library/config/rig.json +++ b/libraries/node-core-library/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "decoupled-local-node-rig" } diff --git a/libraries/node-core-library/eslint.config.js b/libraries/node-core-library/eslint.config.js new file mode 100644 index 00000000000..f83aea7d1b7 --- /dev/null +++ b/libraries/node-core-library/eslint.config.js @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); +const tsdocMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/tsdoc'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + ...tsdocMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/node-core-library/package.json b/libraries/node-core-library/package.json index a7d07ad7363..86fb72be3fd 100644 --- a/libraries/node-core-library/package.json +++ b/libraries/node-core-library/package.json @@ -1,9 +1,32 @@ { "name": "@rushstack/node-core-library", - "version": "3.59.2", + "version": "5.23.3", "description": "Core libraries that every NodeJS toolchain project should use", - "main": "lib/index.js", - "typings": "dist/node-core-library.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/node-core-library.d.ts", + "exports": { + ".": { + "types": "./dist/node-core-library.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "license": "MIT", "repository": { "url": "https://github.com/microsoft/rushstack.git", @@ -12,28 +35,28 @@ }, "scripts": { "build": "heft build --clean", - "_phase:build": "heft build --clean", - "_phase:test": "heft test --no-build" + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" }, "dependencies": { - "colors": "~1.2.1", - "fs-extra": "~7.0.1", + "fs-extra": "~11.3.0", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", - "semver": "~7.3.0", - "z-schema": "~5.0.2" + "semver": "~7.7.4", + "ajv": "~8.20.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft": "0.50.6", - "@rushstack/heft-node-rig": "1.13.0", + "@rushstack/heft": "1.2.22", + "@rushstack/problem-matcher": "workspace:*", "@types/fs-extra": "7.0.0", - "@types/heft-jest": "1.0.1", "@types/jju": "1.4.1", - "@types/node": "14.18.36", "@types/resolve": "1.20.2", - "@types/semver": "7.3.5" + "@types/semver": "7.7.1", + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0" }, "peerDependencies": { "@types/node": "*" @@ -42,5 +65,6 @@ "@types/node": { "optional": true } - } + }, + "sideEffects": false } diff --git a/libraries/node-core-library/src/Async.ts b/libraries/node-core-library/src/Async.ts index 5f960409cc0..29f8669badb 100644 --- a/libraries/node-core-library/src/Async.ts +++ b/libraries/node-core-library/src/Async.ts @@ -5,34 +5,130 @@ * Options for controlling the parallelism of asynchronous operations. * * @remarks - * Used with {@link Async.mapAsync} and {@link Async.forEachAsync}. + * Used with {@link (Async:class).(mapAsync:1)}, {@link (Async:class).(mapAsync:2)} and + * {@link (Async:class).(forEachAsync:1)}, and {@link (Async:class).(forEachAsync:2)}. * - * @beta + * @public */ export interface IAsyncParallelismOptions { /** - * Optionally used with the {@link Async.mapAsync} and {@link Async.forEachAsync} - * to limit the maximum number of concurrent promises to the specified number. + * Optionally used with the {@link (Async:class).(mapAsync:1)}, {@link (Async:class).(mapAsync:2)} and + * {@link (Async:class).(forEachAsync:1)}, and {@link (Async:class).(forEachAsync:2)} to limit the maximum + * number of concurrent promises to the specified number. */ concurrency?: number; + + /** + * Optionally used with the {@link (Async:class).(forEachAsync:2)} to enable weighted operations where an + * operation can take up more or less than one concurrency unit. + */ + weighted?: boolean; + + /** + * This option affects the handling of task weights, applying a softer policy that favors maximizing parallelism + * instead of avoiding overload. + * + * @remarks + * By default, a new task cannot start executing if doing so would push the total weight above the concurrency limit. + * Set `allowOversubscription` to true to relax this rule, allowing a new task to start as long as the current + * total weight is below the concurrency limit. Either way, a task cannot start if the total weight already equals + * the concurrency limit; therefore, `allowOversubscription` has no effect when all tasks have weight 1. + * + * Example: Suppose the concurrency limit is 8, and seven tasks are running whose weights are 1, so the current + * total weight is 7. If an available task has weight 2, that would push the total weight to 9, exceeding + * the limit. This task can start only if `allowOversubscription` is true. + * + * @defaultValue false + */ + allowOversubscription?: boolean; } /** * @remarks * Used with {@link Async.runWithRetriesAsync}. * - * @beta + * @public */ export interface IRunWithRetriesOptions { - action: () => Promise | TResult; + /** + * The action to be performed. The action is repeatedly executed until it completes without throwing or the + * maximum number of retries is reached. + * + * @param retryCount - The number of times the action has been retried. + */ + action: (retryCount: number) => Promise | TResult; + /** + * The maximum number of times the action should be retried. + */ maxRetries: number; + /** + * The delay in milliseconds between retries. + */ retryDelayMs?: number; } +/** + * @remarks + * Used with {@link Async.runWithTimeoutAsync}. + * + * @public + */ +export interface IRunWithTimeoutOptions { + /** + * The action to be performed. The action is executed with a timeout. + */ + action: () => Promise | TResult; + /** + * The timeout in milliseconds. + */ + timeoutMs: number; + /** + * The message to use for the error if the timeout is reached. + */ + timeoutMessage?: string; +} + +/** + * @remarks + * Used with {@link (Async:class).(forEachAsync:2)} and {@link (Async:class).(mapAsync:2)}. + * + * @public + */ +export interface IWeighted { + /** + * The weight of the element, used to determine the concurrency units that it will take up. + * Must be a whole number greater than or equal to 0. + */ + weight: number; +} + +function toWeightedIterator( + iterable: Iterable | AsyncIterable, + useWeights?: boolean +): AsyncIterable<{ element: TEntry; weight: number }> { + const iterator: Iterator | AsyncIterator = ( + (iterable as Iterable)[Symbol.iterator] || + (iterable as AsyncIterable)[Symbol.asyncIterator] + ).call(iterable); + return { + [Symbol.asyncIterator]: () => ({ + // eslint-disable-next-line @typescript-eslint/naming-convention + next: async () => { + // The await is necessary here, but TS will complain - it's a false positive. + const { value, done } = await iterator.next(); + return { + value: { element: value, weight: useWeights ? value?.weight : 1 }, + done: !!done + }; + } + }) + }; +} + /** * Utilities for parallel asynchronous operations, for use with the system `Promise` APIs. * - * @beta + * @public */ export class Async { /** @@ -55,6 +151,38 @@ export class Async { * @returns an array containing the result for each callback, in the same order * as the original input `array` */ + public static async mapAsync( + iterable: Iterable | AsyncIterable, + callback: (entry: TEntry, arrayIndex: number) => Promise, + options?: (IAsyncParallelismOptions & { weighted?: false }) | undefined + ): Promise; + + /** + * Given an input array and a `callback` function, invoke the callback to start a + * promise for each element in the array. Returns an array containing the results. + * + * @remarks + * This API is similar to the system `Array#map`, except that the loop is asynchronous, + * and the maximum number of concurrent units can be throttled + * using {@link IAsyncParallelismOptions.concurrency}. Using the {@link IAsyncParallelismOptions.weighted} + * option, the weight of each operation can be specified, which determines how many concurrent units it takes up. + * + * If `callback` throws a synchronous exception, or if it returns a promise that rejects, + * then the loop stops immediately. Any remaining array items will be skipped, and + * overall operation will reject with the first error that was encountered. + * + * @param iterable - the array of inputs for the callback function + * @param callback - a function that starts an asynchronous promise for an element + * from the array + * @param options - options for customizing the control flow + * @returns an array containing the result for each callback, in the same order + * as the original input `array` + */ + public static async mapAsync( + iterable: Iterable | AsyncIterable, + callback: (entry: TEntry, arrayIndex: number) => Promise, + options: IAsyncParallelismOptions & { weighted: true } + ): Promise; public static async mapAsync( iterable: Iterable | AsyncIterable, callback: (entry: TEntry, arrayIndex: number) => Promise, @@ -62,6 +190,7 @@ export class Async { ): Promise { const result: TRetVal[] = []; + // @ts-expect-error https://github.com/microsoft/TypeScript/issues/22609, it succeeds against the implementation but fails against the overloads await Async.forEachAsync( iterable, async (item: TEntry, arrayIndex: number): Promise => { @@ -94,74 +223,52 @@ export class Async { public static async forEachAsync( iterable: Iterable | AsyncIterable, callback: (entry: TEntry, arrayIndex: number) => Promise, - options?: IAsyncParallelismOptions | undefined - ): Promise { - await new Promise((resolve: () => void, reject: (error: Error) => void) => { - const concurrency: number = - options?.concurrency && options.concurrency > 0 ? options.concurrency : Infinity; - let operationsInProgress: number = 0; - - const iterator: Iterator | AsyncIterator = ( - (iterable as Iterable)[Symbol.iterator] || - (iterable as AsyncIterable)[Symbol.asyncIterator] - ).call(iterable); - - let arrayIndex: number = 0; - let iteratorIsComplete: boolean = false; - let promiseHasResolvedOrRejected: boolean = false; - - async function queueOperationsAsync(): Promise { - while (operationsInProgress < concurrency && !iteratorIsComplete && !promiseHasResolvedOrRejected) { - // Increment the concurrency while waiting for the iterator. - // This function is reentrant, so this ensures that at most `concurrency` executions are waiting - operationsInProgress++; - const currentIteratorResult: IteratorResult = await iterator.next(); - // eslint-disable-next-line require-atomic-updates - iteratorIsComplete = !!currentIteratorResult.done; - - if (!iteratorIsComplete) { - Promise.resolve(callback(currentIteratorResult.value, arrayIndex++)) - .then(async () => { - operationsInProgress--; - await onOperationCompletionAsync(); - }) - .catch((error) => { - promiseHasResolvedOrRejected = true; - reject(error); - }); - } else { - // The iterator is complete and there wasn't a value, so untrack the waiting state. - operationsInProgress--; - } - } + options?: (IAsyncParallelismOptions & { weighted?: false }) | undefined + ): Promise; - if (iteratorIsComplete) { - await onOperationCompletionAsync(); - } - } - - async function onOperationCompletionAsync(): Promise { - if (!promiseHasResolvedOrRejected) { - if (operationsInProgress === 0 && iteratorIsComplete) { - promiseHasResolvedOrRejected = true; - resolve(); - } else if (!iteratorIsComplete) { - await queueOperationsAsync(); - } - } - } - - queueOperationsAsync().catch((error) => { - promiseHasResolvedOrRejected = true; - reject(error); - }); - }); + /** + * Given an input array and a `callback` function, invoke the callback to start a + * promise for each element in the array. + * + * @remarks + * This API is similar to the other `Array#forEachAsync`, except that each item can have + * a weight that determines how many concurrent operations are allowed. The unweighted + * `Array#forEachAsync` is a special case of this method where weight = 1 for all items. + * + * The maximum number of concurrent operations can still be throttled using + * {@link IAsyncParallelismOptions.concurrency}, however it no longer determines the + * maximum number of operations that can be in progress at once. Instead, it determines the + * number of concurrency units that can be in progress at once. The weight of each operation + * determines how many concurrency units it takes up. For example, if the concurrency is 2 + * and the first operation has a weight of 2, then only one more operation can be in progress. + * Operations may exceed the concurrency limit based on the `allowOversubscription` option. + * + * If `callback` throws a synchronous exception, or if it returns a promise that rejects, + * then the loop stops immediately. Any remaining array items will be skipped, and + * overall operation will reject with the first error that was encountered. + * + * @param iterable - the array of inputs for the callback function + * @param callback - a function that starts an asynchronous promise for an element + * from the array + * @param options - options for customizing the control flow + */ + public static async forEachAsync( + iterable: Iterable | AsyncIterable, + callback: (entry: TEntry, arrayIndex: number) => Promise, + options: IAsyncParallelismOptions & { weighted: true } + ): Promise; + public static async forEachAsync( + iterable: Iterable | AsyncIterable, + callback: (entry: TEntry, arrayIndex: number) => Promise, + options?: IAsyncParallelismOptions + ): Promise { + await _forEachWeightedAsync(toWeightedIterator(iterable, options?.weighted), callback, options); } /** * Return a promise that resolves after the specified number of milliseconds. */ - public static async sleep(ms: number): Promise { + public static async sleepAsync(ms: number): Promise { await new Promise((resolve) => { setTimeout(resolve, ms); }); @@ -175,28 +282,174 @@ export class Async { maxRetries, retryDelayMs = 0 }: IRunWithRetriesOptions): Promise { - let retryCounter: number = 0; - // eslint-disable-next-line no-constant-condition + let retryCount: number = 0; while (true) { try { - return await action(); + return await action(retryCount); } catch (e) { - if (++retryCounter > maxRetries) { + if (++retryCount > maxRetries) { throw e; } else if (retryDelayMs > 0) { - await Async.sleep(retryDelayMs); + await Async.sleepAsync(retryDelayMs); } } } } + + /** + * Ensures that the argument is a valid {@link IWeighted}, with a `weight` argument that + * is a positive integer or 0. + */ + public static validateWeightedIterable(operation: IWeighted): void { + if (operation.weight < 0) { + throw new Error('Weight must be a whole number greater than or equal to 0'); + } + if (operation.weight % 1 !== 0) { + throw new Error('Weight must be a whole number greater than or equal to 0'); + } + } + + /** + * Returns a Signal, a.k.a. a "deferred promise". + */ + public static getSignal(): [Promise, () => void, (err: Error) => void] { + return getSignal(); + } + + /** + * Runs a promise with a timeout. If the promise does not resolve within the specified timeout, + * it will reject with an error. + * @remarks If the action is completely synchronous, runWithTimeoutAsync doesn't do anything meaningful. + */ + public static async runWithTimeoutAsync({ + action, + timeoutMs, + timeoutMessage = 'Operation timed out' + }: IRunWithTimeoutOptions): Promise { + let timeoutHandle: NodeJS.Timeout | undefined; + const promise: Promise = Promise.resolve(action()); + const timeoutPromise: Promise = new Promise((resolve, reject) => { + timeoutHandle = setTimeout(() => reject(new Error(timeoutMessage)), timeoutMs); + }); + + try { + return Promise.race([promise, timeoutPromise]); + } finally { + if (timeoutHandle) { + clearTimeout(timeoutHandle); + } + } + } } -function getSignal(): [Promise, () => void] { +async function _forEachWeightedAsync( + iterable: AsyncIterable, + callback: (entry: TReturn, arrayIndex: number) => Promise, + options?: IAsyncParallelismOptions | undefined +): Promise { + await new Promise((resolve: () => void, reject: (error: Error) => void) => { + const concurrency: number = + options?.concurrency && options.concurrency > 0 ? options.concurrency : Infinity; + let concurrentUnitsInProgress: number = 0; + + const iterator: Iterator | AsyncIterator = (iterable as AsyncIterable)[ + Symbol.asyncIterator + ].call(iterable); + + let arrayIndex: number = 0; + let iteratorIsComplete: boolean = false; + let promiseHasResolvedOrRejected: boolean = false; + // iterator that is stored when the loop exits early due to not enough concurrency + let nextIterator: IteratorResult | undefined = undefined; + + async function queueOperationsAsync(): Promise { + while ( + concurrentUnitsInProgress < concurrency && + !iteratorIsComplete && + !promiseHasResolvedOrRejected + ) { + // Increment the current concurrency units in progress by the concurrency limit before fetching the iterator weight. + // This function is reentrant, so this if concurrency is finite, at most 1 operation will be waiting. If it's infinite, + // there will be effectively no cap on the number of operations waiting. + const limitedConcurrency: number = !Number.isFinite(concurrency) ? 1 : concurrency; + concurrentUnitsInProgress += limitedConcurrency; + const currentIteratorResult: IteratorResult = nextIterator ?? (await iterator.next()); + // eslint-disable-next-line require-atomic-updates + iteratorIsComplete = !!currentIteratorResult.done; + + if (!iteratorIsComplete) { + const currentIteratorValue: TEntry = currentIteratorResult.value; + Async.validateWeightedIterable(currentIteratorValue); + // Cap the weight to concurrency, this allows 0 weight items to execute despite the concurrency limit. + const weight: number = Math.min(currentIteratorValue.weight, concurrency); + + // Remove the "lock" from the concurrency check and only apply the current weight. + // This should allow other operations to execute. + concurrentUnitsInProgress -= limitedConcurrency; + + // Wait until there's enough capacity to run this job, this function will be re-entered as tasks call `onOperationCompletionAsync` + const wouldExceedConcurrency: boolean = concurrentUnitsInProgress + weight > concurrency; + const allowOversubscription: boolean = options?.allowOversubscription ?? false; + if (!allowOversubscription && wouldExceedConcurrency) { + // eslint-disable-next-line require-atomic-updates + nextIterator = currentIteratorResult; + break; + } + + // eslint-disable-next-line require-atomic-updates + nextIterator = undefined; + concurrentUnitsInProgress += weight; + + Promise.resolve(callback(currentIteratorValue.element, arrayIndex++)) + .then(async () => { + // Remove the operation completely from the in progress units. + concurrentUnitsInProgress -= weight; + await onOperationCompletionAsync(); + }) + .catch((error) => { + promiseHasResolvedOrRejected = true; + reject(error); + }); + } else { + // The iterator is complete and there wasn't a value, so untrack the waiting state. + concurrentUnitsInProgress -= limitedConcurrency; + } + } + + if (iteratorIsComplete) { + await onOperationCompletionAsync(); + } + } + + async function onOperationCompletionAsync(): Promise { + if (!promiseHasResolvedOrRejected) { + if (concurrentUnitsInProgress === 0 && iteratorIsComplete) { + promiseHasResolvedOrRejected = true; + resolve(); + } else if (!iteratorIsComplete) { + await queueOperationsAsync(); + } + } + } + + queueOperationsAsync().catch((error) => { + promiseHasResolvedOrRejected = true; + reject(error); + }); + }); +} + +/** + * Returns an unwrapped promise. + */ +function getSignal(): [Promise, () => void, (err: Error) => void] { let resolver: () => void; - const promise: Promise = new Promise((resolve) => { + let rejecter: (err: Error) => void; + const promise: Promise = new Promise((resolve, reject) => { resolver = resolve; + rejecter = reject; }); - return [promise, resolver!]; + return [promise, resolver!, rejecter!]; } /** diff --git a/libraries/node-core-library/src/Constants.ts b/libraries/node-core-library/src/Constants.ts index 85f4834c0d7..d02b31ed1ea 100644 --- a/libraries/node-core-library/src/Constants.ts +++ b/libraries/node-core-library/src/Constants.ts @@ -6,26 +6,28 @@ * * @public */ -export enum FileConstants { +// eslint-disable-next-line @typescript-eslint/typedef +export const FileConstants = { /** * "package.json" - the configuration file that defines an NPM package */ - PackageJson = 'package.json' -} + PackageJson: 'package.json' +} as const; /** * String constants for common folder names. * * @public */ -export enum FolderConstants { +// eslint-disable-next-line @typescript-eslint/typedef +export const FolderConstants = { /** * ".git" - the data storage for a Git working folder */ - Git = '.git', + Git: '.git', /** * "node_modules" - the folder where package managers install their files */ - NodeModules = 'node_modules' -} + NodeModules: 'node_modules' +} as const; diff --git a/libraries/node-core-library/src/Disposables.ts b/libraries/node-core-library/src/Disposables.ts new file mode 100644 index 00000000000..8edc3e0e794 --- /dev/null +++ b/libraries/node-core-library/src/Disposables.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as Disposables from './disposables/index'; + +export { Disposables }; diff --git a/libraries/node-core-library/src/EnvironmentMap.ts b/libraries/node-core-library/src/EnvironmentMap.ts index 64af99d0d70..c672f1be36b 100644 --- a/libraries/node-core-library/src/EnvironmentMap.ts +++ b/libraries/node-core-library/src/EnvironmentMap.ts @@ -1,4 +1,8 @@ -import process from 'process'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import process from 'node:process'; + import { InternalError } from './InternalError'; /** diff --git a/libraries/node-core-library/src/Executable.ts b/libraries/node-core-library/src/Executable.ts index 049274bf666..bf7931a3221 100644 --- a/libraries/node-core-library/src/Executable.ts +++ b/libraries/node-core-library/src/Executable.ts @@ -1,13 +1,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as child_process from 'child_process'; -import * as os from 'os'; -import * as path from 'path'; -import { EnvironmentMap } from './EnvironmentMap'; +import * as os from 'node:os'; +import * as child_process from 'node:child_process'; +import * as path from 'node:path'; +import { EnvironmentMap } from './EnvironmentMap'; import { FileSystem } from './FileSystem'; import { PosixModeBits } from './PosixModeBits'; +import { Text } from './Text'; +import { InternalError } from './InternalError'; + +const OS_PLATFORM: NodeJS.Platform = os.platform(); /** * Typings for one of the streams inside IExecutableSpawnSyncOptions.stdio. @@ -105,6 +109,95 @@ export interface IExecutableSpawnOptions extends IExecutableResolveOptions { stdio?: ExecutableStdioMapping; } +/** + * The options for running a process to completion using {@link Executable.(waitForExitAsync:3)}. + * + * @public + */ +export interface IWaitForExitOptions { + /** + * Whether or not to throw when the process completes with a non-zero exit code. Defaults to false. + * + * @defaultValue false + */ + throwOnNonZeroExitCode?: boolean; + + /** + * Whether or not to throw when the process is terminated by a signal. Defaults to false. + * + * @defaultValue false + */ + throwOnSignal?: boolean; + + /** + * The encoding of the output. If not provided, the output will not be collected. + */ + encoding?: BufferEncoding | 'buffer'; +} + +/** + * {@inheritDoc IWaitForExitOptions} + * + * @public + */ +export interface IWaitForExitWithStringOptions extends IWaitForExitOptions { + /** + * {@inheritDoc IWaitForExitOptions.encoding} + */ + encoding: BufferEncoding; +} + +/** + * {@inheritDoc IWaitForExitOptions} + * + * @public + */ +export interface IWaitForExitWithBufferOptions extends IWaitForExitOptions { + /** + * {@inheritDoc IWaitForExitOptions.encoding} + */ + encoding: 'buffer'; +} + +/** + * The result of running a process to completion using {@link Executable.(waitForExitAsync:3)}. This + * interface does not include stdout or stderr output because an {@link IWaitForExitOptions.encoding} was not specified. + * + * @public + */ +export interface IWaitForExitResultWithoutOutput { + /** + * The process exit code. If the process was terminated, this will be null. + */ + // eslint-disable-next-line @rushstack/no-new-null + exitCode: number | null; + + /** + * The process signal that terminated the process. If the process exited normally, this will be null. + */ + // eslint-disable-next-line @rushstack/no-new-null + signal: string | null; +} + +/** + * The result of running a process to completion using {@link Executable.(waitForExitAsync:1)}, + * or {@link Executable.(waitForExitAsync:2)}. + * + * @public + */ +export interface IWaitForExitResult + extends IWaitForExitResultWithoutOutput { + /** + * The process stdout output, if encoding was specified. + */ + stdout: T; + + /** + * The process stderr output, if encoding was specified. + */ + stderr: T; +} + // Common environmental state used by Executable members interface IExecutableContext { currentWorkingDirectory: string; @@ -113,11 +206,187 @@ interface IExecutableContext { windowsExecutableExtensions: string[]; } -interface ICommandLineFixup { +interface ICommandLineOptions { path: string; args: string[]; } +/** + * Process information sourced from the system. This process info is sourced differently depending + * on the operating system: + * - On Windows, this uses `powershell.exe` and a scriptlet to retrieve process information. + * The wmic utility that was previously used is no longer present on the latest Windows versions. + * - On Unix, this uses the `ps` utility. + * + * @public + */ +export interface IProcessInfo { + /** + * The name of the process. + * + * @remarks On Windows, the process name will be empty if the process is a kernel process. + * On Unix, the process name will be empty if the process is the root process. + */ + processName: string; + + /** + * The process ID. + */ + processId: number; + + /** + * The parent process info. + * + * @remarks On Windows, the parent process info will be undefined if the process is a kernel process. + * On Unix, the parent process info will be undefined if the process is the root process. + */ + parentProcessInfo: IProcessInfo | undefined; + + /** + * The child process infos. + */ + childProcessInfos: IProcessInfo[]; +} + +export async function parseProcessListOutputAsync( + stream: NodeJS.ReadableStream, + platform: NodeJS.Platform = OS_PLATFORM +): Promise> { + const processInfoById: Map = new Map(); + let seenHeaders: boolean = false; + for await (const line of Text.readLinesFromIterableAsync(stream, { ignoreEmptyLines: true })) { + if (!seenHeaders) { + seenHeaders = true; + } else { + parseProcessInfoEntry(line, processInfoById, platform); + } + } + return processInfoById; +} + +export function parseProcessListOutput( + // eslint-disable-next-line @rushstack/no-new-null + output: Iterable, + platform: NodeJS.Platform = OS_PLATFORM +): Map { + const processInfoById: Map = new Map(); + let seenHeaders: boolean = false; + for (const line of Text.readLinesFromIterable(output, { ignoreEmptyLines: true })) { + if (!seenHeaders) { + seenHeaders = true; + } else { + parseProcessInfoEntry(line, processInfoById, platform); + } + } + return processInfoById; +} + +// win32 format: +// PPID PID NAME +// 51234 56784 process name +// unix format: +// PPID PID COMMAND +// 51234 56784 process name +const NAME_GROUP: 'name' = 'name'; +const PROCESS_ID_GROUP: 'pid' = 'pid'; +const PARENT_PROCESS_ID_GROUP: 'ppid' = 'ppid'; +const PROCESS_LIST_ENTRY_REGEX: RegExp = new RegExp( + `^\\s*(?<${PARENT_PROCESS_ID_GROUP}>\\d+)\\s+(?<${PROCESS_ID_GROUP}>\\d+)\\s+(?<${NAME_GROUP}>.+?)\\s*$` +); + +function parseProcessInfoEntry( + line: string, + existingProcessInfoById: Map, + platform: NodeJS.Platform +): void { + const processListEntryRegex: RegExp = PROCESS_LIST_ENTRY_REGEX; + const match: RegExpMatchArray | null = line.match(processListEntryRegex); + if (!match?.groups) { + throw new InternalError(`Invalid process list entry: ${line}`); + } + + const processName: string = match.groups[NAME_GROUP]; + const processId: number = parseInt(match.groups[PROCESS_ID_GROUP], 10); + const parentProcessId: number = parseInt(match.groups[PARENT_PROCESS_ID_GROUP], 10); + + // Only care about the parent process if it is not the same as the current process. + let parentProcessInfo: IProcessInfo | undefined; + if (parentProcessId !== processId) { + parentProcessInfo = existingProcessInfoById.get(parentProcessId); + if (!parentProcessInfo) { + // Create a new placeholder entry for the parent with the information we have so far + parentProcessInfo = { + processName: '', + processId: parentProcessId, + parentProcessInfo: undefined, + childProcessInfos: [] + }; + existingProcessInfoById.set(parentProcessId, parentProcessInfo); + } + } + + let processInfo: IProcessInfo | undefined = existingProcessInfoById.get(processId); + if (!processInfo) { + // Create a new entry + processInfo = { + processName, + processId, + parentProcessInfo, + childProcessInfos: [] + }; + existingProcessInfoById.set(processId, processInfo); + } else { + // Update placeholder entry + processInfo.processName = processName; + processInfo.parentProcessInfo = parentProcessInfo; + } + + // Add the process as a child of the parent process + parentProcessInfo?.childProcessInfos.push(processInfo); +} + +function convertToProcessInfoByNameMap( + processInfoById: Map +): Map { + const processInfoByNameMap: Map = new Map(); + for (const processInfo of processInfoById.values()) { + let processInfoNameEntries: IProcessInfo[] | undefined = processInfoByNameMap.get( + processInfo.processName + ); + if (!processInfoNameEntries) { + processInfoNameEntries = []; + processInfoByNameMap.set(processInfo.processName, processInfoNameEntries); + } + processInfoNameEntries.push(processInfo); + } + return processInfoByNameMap; +} + +function getProcessListProcessOptions(): ICommandLineOptions { + let command: string; + let args: string[]; + if (OS_PLATFORM === 'win32') { + command = 'powershell.exe'; + // Order of declared properties sets the order of the output. + // Put name last to simplify parsing, since it can contain spaces. + args = [ + '-NoProfile', + '-Command', + `'PPID PID Name'; Get-CimInstance Win32_Process | % { '{0} {1} {2}' -f $_.ParentProcessId, $_.ProcessId, $_.Name }` + ]; + } else { + command = 'ps'; + // -A: Select all processes + // -w: Wide format + // -o: User-defined format + // Order of declared properties sets the order of the output. We will + // need to request the "comm" property last in order to ensure that the + // process names are not truncated on certain platforms + args = ['-Awo', 'ppid,pid,comm']; + } + return { path: command, args }; +} + /** * The Executable class provides a safe, portable, recommended solution for tools that need * to launch child processes. @@ -187,9 +456,9 @@ export class Executable { options = {}; } - const context: IExecutableContext = Executable._getExecutableContext(options); + const context: IExecutableContext = _getExecutableContext(options); - const resolvedPath: string | undefined = Executable._tryResolve(filename, options, context); + const resolvedPath: string | undefined = _tryResolve(filename, options, context); if (!resolvedPath) { throw new Error(`The executable file was not found: "${filename}"`); } @@ -210,11 +479,7 @@ export class Executable { shell: false }; - const normalizedCommandLine: ICommandLineFixup = Executable._buildCommandLineFixup( - resolvedPath, - args, - context - ); + const normalizedCommandLine: ICommandLineOptions = _buildCommandLineFixup(resolvedPath, args, context); return child_process.spawnSync(normalizedCommandLine.path, normalizedCommandLine.args, spawnOptions); } @@ -251,9 +516,9 @@ export class Executable { options = {}; } - const context: IExecutableContext = Executable._getExecutableContext(options); + const context: IExecutableContext = _getExecutableContext(options); - const resolvedPath: string | undefined = Executable._tryResolve(filename, options, context); + const resolvedPath: string | undefined = _tryResolve(filename, options, context); if (!resolvedPath) { throw new Error(`The executable file was not found: "${filename}"`); } @@ -267,86 +532,175 @@ export class Executable { shell: false }; - const normalizedCommandLine: ICommandLineFixup = Executable._buildCommandLineFixup( - resolvedPath, - args, - context - ); + const normalizedCommandLine: ICommandLineOptions = _buildCommandLineFixup(resolvedPath, args, context); return child_process.spawn(normalizedCommandLine.path, normalizedCommandLine.args, spawnOptions); } - // PROBLEM: Given an "args" array of strings that may contain special characters (e.g. spaces, - // backslashes, quotes), ensure that these strings pass through to the child process's ARGV array - // without anything getting corrupted along the way. - // - // On Unix you just pass the array to spawnSync(). But on Windows, this is a very complex problem: - // - The Win32 CreateProcess() API expects the args to be encoded as a single text string - // - The decoding of this string is up to the application (not the OS), and there are 3 different - // algorithms in common usage: the cmd.exe shell, the Microsoft CRT library init code, and - // the Win32 CommandLineToArgvW() - // - The encodings are counterintuitive and have lots of special cases - // - NodeJS spawnSync() tries do the encoding without knowing which decoder will be used - // - // See these articles for a full analysis: - // http://www.windowsinspired.com/understanding-the-command-line-string-and-arguments-received-by-a-windows-program/ - // http://www.windowsinspired.com/how-a-windows-programs-splits-its-command-line-into-individual-arguments/ - private static _buildCommandLineFixup( - resolvedPath: string, - args: string[], - context: IExecutableContext - ): ICommandLineFixup { - const fileExtension: string = path.extname(resolvedPath); - - if (os.platform() === 'win32') { - // Do we need a custom handler for this file type? - switch (fileExtension.toUpperCase()) { - case '.EXE': - case '.COM': - // okay to execute directly - break; - case '.BAT': - case '.CMD': { - Executable._validateArgsForWindowsShell(args); - - // These file types must be invoked via the Windows shell - let shellPath: string | undefined = context.environmentMap.get('COMSPEC'); - if (!shellPath || !Executable._canExecute(shellPath, context)) { - shellPath = Executable.tryResolve('cmd.exe'); - } - if (!shellPath) { - throw new Error( - `Unable to execute "${path.basename(resolvedPath)}" ` + - `because CMD.exe was not found in the PATH` - ); - } + /** {@inheritDoc Executable.(waitForExitAsync:3)} */ + public static async waitForExitAsync( + childProcess: child_process.ChildProcess, + options: IWaitForExitWithStringOptions + ): Promise>; + + /** {@inheritDoc Executable.(waitForExitAsync:3)} */ + public static async waitForExitAsync( + childProcess: child_process.ChildProcess, + options: IWaitForExitWithBufferOptions + ): Promise>; + + /** + * Wait for a child process to exit and return the result. + * + * @param childProcess - The child process to wait for. + * @param options - Options for waiting for the process to exit. + */ + public static async waitForExitAsync( + childProcess: child_process.ChildProcess, + options?: IWaitForExitOptions + ): Promise; + + public static async waitForExitAsync( + childProcess: child_process.ChildProcess, + options: IWaitForExitOptions = {} + ): Promise | IWaitForExitResultWithoutOutput> { + const { throwOnNonZeroExitCode, throwOnSignal, encoding } = options; + if (encoding && (!childProcess.stdout || !childProcess.stderr)) { + throw new Error( + 'An encoding was specified, but stdout and/or stderr on the child process are not defined' + ); + } - const shellArgs: string[] = []; - // /D: Disable execution of AutoRun commands when starting the new shell context - shellArgs.push('/d'); - // /S: Disable Cmd.exe's parsing of double-quote characters inside the command-line - shellArgs.push('/s'); - // /C: Execute the following command and then exit immediately - shellArgs.push('/c'); + const collectedStdout: T[] = []; + const collectedStderr: T[] = []; + const useBufferEncoding: boolean = encoding === 'buffer'; - // If the path contains special charactrers (e.g. spaces), escape them so that - // they don't get interpreted by the shell - shellArgs.push(Executable._getEscapedForWindowsShell(resolvedPath)); - shellArgs.push(...args); + function normalizeChunk(chunk: Buffer | string): TChunk { + if (typeof chunk === 'string') { + return (useBufferEncoding ? Buffer.from(chunk) : chunk) as TChunk; + } else { + return (useBufferEncoding ? chunk : chunk.toString(encoding as BufferEncoding)) as TChunk; + } + } - return { path: shellPath, args: shellArgs }; + type ISignalAndExitCode = Pick, 'exitCode' | 'signal'>; + + let errorThrown: Error | undefined = undefined; + const { exitCode, signal } = await new Promise( + (resolve: (result: ISignalAndExitCode) => void, reject: (error: Error) => void) => { + if (encoding) { + childProcess.stdout!.on('data', (chunk: Buffer | string) => { + collectedStdout.push(normalizeChunk(chunk)); + }); + childProcess.stderr!.on('data', (chunk: Buffer | string) => { + collectedStderr.push(normalizeChunk(chunk)); + }); } - default: - throw new Error( - `Cannot execute "${path.basename(resolvedPath)}" because the file type is not supported` - ); + childProcess.on('error', (error: Error) => { + // Wait to call reject() until any output is collected + errorThrown = error; + }); + childProcess.on('close', (closeExitCode: number | null, closeSignal: NodeJS.Signals | null) => { + if (errorThrown) { + reject(errorThrown); + } + if (closeSignal && throwOnSignal) { + reject(new Error(`Process terminated by ${closeSignal}`)); + } else if (closeExitCode !== 0 && throwOnNonZeroExitCode) { + reject(new Error(`Process exited with code ${closeExitCode}`)); + } else { + resolve({ exitCode: closeExitCode, signal: closeSignal }); + } + }); + } + ); + + let result: IWaitForExitResult | IWaitForExitResultWithoutOutput; + if (encoding) { + let stdout: T | undefined; + let stderr: T | undefined; + + if (encoding === 'buffer') { + stdout = Buffer.concat(collectedStdout as Buffer[]) as T; + stderr = Buffer.concat(collectedStderr as Buffer[]) as T; + } else if (encoding !== undefined) { + stdout = collectedStdout.join('') as T; + stderr = collectedStderr.join('') as T; } + + result = { + stdout: stdout as T, + stderr: stderr as T, + exitCode, + signal + }; + } else { + result = { + exitCode, + signal + }; } - return { - path: resolvedPath, - args: args - }; + return result; + } + + /** + * Get the list of processes currently running on the system, keyed by the process ID. + * + * @remarks The underlying implementation depends on the operating system: + * - On Windows, this uses `powershell.exe` and the `Get-CimInstance` cmdlet. + * - On Unix, this uses the `ps` utility. + */ + public static async getProcessInfoByIdAsync(): Promise> { + const { path: command, args } = getProcessListProcessOptions(); + const process: child_process.ChildProcess = Executable.spawn(command, args, { + stdio: ['ignore', 'pipe', 'ignore'] + }); + if (process.stdout === null) { + throw new InternalError('Child process did not provide stdout'); + } + const [processInfoByIdMap] = await Promise.all([ + parseProcessListOutputAsync(process.stdout), + // Don't collect output in the result since we process it directly + Executable.waitForExitAsync(process, { throwOnNonZeroExitCode: true, throwOnSignal: true }) + ]); + return processInfoByIdMap; + } + + /** + * {@inheritDoc Executable.getProcessInfoByIdAsync} + */ + public static getProcessInfoById(): Map { + const { path: command, args } = getProcessListProcessOptions(); + const processOutput: child_process.SpawnSyncReturns = Executable.spawnSync(command, args); + if (processOutput.error) { + throw new Error(`Unable to list processes: ${command} failed with error ${processOutput.error}`); + } + if (processOutput.status !== 0) { + throw new Error(`Unable to list processes: ${command} exited with code ${processOutput.status}`); + } + return parseProcessListOutput(processOutput.output); + } + + /** + * Get the list of processes currently running on the system, keyed by the process name. All processes + * with the same name will be grouped. + * + * @remarks The underlying implementation depends on the operating system: + * - On Windows, this uses `powershell.exe` and the `Get-CimInstance` cmdlet. + * - On Unix, this uses the `ps` utility. + */ + public static async getProcessInfoByNameAsync(): Promise> { + const processInfoById: Map = await Executable.getProcessInfoByIdAsync(); + return convertToProcessInfoByNameMap(processInfoById); + } + + /** + * {@inheritDoc Executable.getProcessInfoByNameAsync} + */ + public static getProcessInfoByName(): Map { + const processInfoByIdMap: Map = Executable.getProcessInfoById(); + return convertToProcessInfoByNameMap(processInfoByIdMap); } /** @@ -366,230 +720,300 @@ export class Executable { * @returns the absolute path of the executable, or undefined if it was not found */ public static tryResolve(filename: string, options?: IExecutableResolveOptions): string | undefined { - return Executable._tryResolve(filename, options || {}, Executable._getExecutableContext(options)); + return _tryResolve(filename, options || {}, _getExecutableContext(options)); } +} - private static _tryResolve( - filename: string, - options: IExecutableResolveOptions, - context: IExecutableContext - ): string | undefined { - // NOTE: Since "filename" cannot contain command-line arguments, the "/" here - // must be interpreted as a path delimiter - const hasPathSeparators: boolean = - filename.indexOf('/') >= 0 || (os.platform() === 'win32' && filename.indexOf('\\') >= 0); - - // Are there any path separators? - if (hasPathSeparators) { - // If so, then don't search the PATH. Just resolve relative to the current working directory - const resolvedPath: string = path.resolve(context.currentWorkingDirectory, filename); - return Executable._tryResolveFileExtension(resolvedPath, context); - } else { - // Otherwise if it's a bare name, then try everything in the shell PATH - const pathsToSearch: string[] = Executable._getSearchFolders(context); - - for (const pathToSearch of pathsToSearch) { - const resolvedPath: string = path.join(pathToSearch, filename); - const result: string | undefined = Executable._tryResolveFileExtension(resolvedPath, context); - if (result) { - return result; - } +function _tryResolve( + filename: string, + options: IExecutableResolveOptions, + context: IExecutableContext +): string | undefined { + // NOTE: Since "filename" cannot contain command-line arguments, the "/" here + // must be interpreted as a path delimiter + const hasPathSeparators: boolean = + filename.indexOf('/') >= 0 || (OS_PLATFORM === 'win32' && filename.indexOf('\\') >= 0); + + // Are there any path separators? + if (hasPathSeparators) { + // If so, then don't search the PATH. Just resolve relative to the current working directory + const resolvedPath: string = path.resolve(context.currentWorkingDirectory, filename); + return _tryResolveFileExtension(resolvedPath, context); + } else { + // Otherwise if it's a bare name, then try everything in the shell PATH + const pathsToSearch: string[] = _getSearchFolders(context); + + for (const pathToSearch of pathsToSearch) { + const resolvedPath: string = path.join(pathToSearch, filename); + const result: string | undefined = _tryResolveFileExtension(resolvedPath, context); + if (result) { + return result; } - - // No match was found - return undefined; } + + // No match was found + return undefined; } +} - private static _tryResolveFileExtension( - resolvedPath: string, - context: IExecutableContext - ): string | undefined { - if (Executable._canExecute(resolvedPath, context)) { - return resolvedPath; - } +function _tryResolveFileExtension(resolvedPath: string, context: IExecutableContext): string | undefined { + if (_canExecute(resolvedPath, context)) { + return resolvedPath; + } - // Try the default file extensions - for (const shellExtension of context.windowsExecutableExtensions) { - const resolvedNameWithExtension: string = resolvedPath + shellExtension; + // Try the default file extensions + for (const shellExtension of context.windowsExecutableExtensions) { + const resolvedNameWithExtension: string = resolvedPath + shellExtension; - if (Executable._canExecute(resolvedNameWithExtension, context)) { - return resolvedNameWithExtension; - } + if (_canExecute(resolvedNameWithExtension, context)) { + return resolvedNameWithExtension; } + } - return undefined; + return undefined; +} + +function _buildEnvironmentMap(options: IExecutableResolveOptions): EnvironmentMap { + const environmentMap: EnvironmentMap = new EnvironmentMap(); + if (options.environment !== undefined && options.environmentMap !== undefined) { + throw new Error( + 'IExecutableResolveOptions.environment and IExecutableResolveOptions.environmentMap' + + ' cannot both be specified' + ); } + if (options.environment !== undefined) { + environmentMap.mergeFromObject(options.environment); + } else if (options.environmentMap !== undefined) { + environmentMap.mergeFrom(options.environmentMap); + } else { + environmentMap.mergeFromObject(process.env); + } + return environmentMap; +} - private static _buildEnvironmentMap(options: IExecutableResolveOptions): EnvironmentMap { - const environmentMap: EnvironmentMap = new EnvironmentMap(); - if (options.environment !== undefined && options.environmentMap !== undefined) { - throw new Error( - 'IExecutableResolveOptions.environment and IExecutableResolveOptions.environmentMap' + - ' cannot both be specified' - ); - } - if (options.environment !== undefined) { - environmentMap.mergeFromObject(options.environment); - } else if (options.environmentMap !== undefined) { - environmentMap.mergeFrom(options.environmentMap); - } else { - environmentMap.mergeFromObject(process.env); - } - return environmentMap; +/** + * This is used when searching the shell PATH for an executable, to determine + * whether a match should be skipped or not. If it returns true, this does not + * guarantee that the file can be successfully executed. + */ +function _canExecute(filePath: string, context: IExecutableContext): boolean { + if (!FileSystem.exists(filePath)) { + return false; } - /** - * This is used when searching the shell PATH for an executable, to determine - * whether a match should be skipped or not. If it returns true, this does not - * guarantee that the file can be successfully executed. - */ - private static _canExecute(filePath: string, context: IExecutableContext): boolean { - if (!FileSystem.exists(filePath)) { + if (OS_PLATFORM === 'win32') { + // NOTE: For Windows, we don't validate that the file extension appears in PATHEXT. + // That environment variable determines which extensions can be appended if the + // extension is missing, but it does not affect whether a file may be executed or not. + // Windows does have a (seldom used) ACL that can be used to deny execution permissions + // for a file, but NodeJS doesn't expose that API, so we don't bother checking it. + + // However, Windows *does* require that the file has some kind of file extension + if (path.extname(filePath) === '') { return false; } + } else { + // For Unix, check whether any of the POSIX execute bits are set + try { + // eslint-disable-next-line no-bitwise + if ((FileSystem.getPosixModeBits(filePath) & PosixModeBits.AllExecute) === 0) { + return false; // not executable + } + } catch (error) { + // If we have trouble accessing the file, ignore the error and consider it "not executable" + // since that's what a shell would do + } + } - if (os.platform() === 'win32') { - // NOTE: For Windows, we don't validate that the file extension appears in PATHEXT. - // That environment variable determines which extensions can be appended if the - // extension is missing, but it does not affect whether a file may be executed or not. - // Windows does have a (seldom used) ACL that can be used to deny execution permissions - // for a file, but NodeJS doesn't expose that API, so we don't bother checking it. + return true; +} - // However, Windows *does* require that the file has some kind of file extension - if (path.extname(filePath) === '') { - return false; - } - } else { - // For Unix, check whether any of the POSIX execute bits are set - try { - // eslint-disable-next-line no-bitwise - if ((FileSystem.getPosixModeBits(filePath) & PosixModeBits.AllExecute) === 0) { - return false; // not executable +/** + * Returns the list of folders where we will search for an executable, + * based on the PATH environment variable. + */ +function _getSearchFolders(context: IExecutableContext): string[] { + const pathList: string = context.environmentMap.get('PATH') || ''; + + const folders: string[] = []; + + // Avoid processing duplicates + const seenPaths: Set = new Set(); + + // NOTE: Cmd.exe on Windows always searches the current working directory first. + // PowerShell and Unix shells do NOT do that, because it's a security concern. + // We follow their behavior. + + for (const splitPath of pathList.split(path.delimiter)) { + const trimmedPath: string = splitPath.trim(); + if (trimmedPath !== '') { + if (!seenPaths.has(trimmedPath)) { + // Fun fact: If you put relative paths in your PATH environment variable, + // all shells will dynamically match them against the current working directory. + // This is a terrible design, and in practice nobody does that, but it is supported... + // so we allow it here. + const resolvedPath: string = path.resolve(context.currentWorkingDirectory, trimmedPath); + + if (!seenPaths.has(resolvedPath)) { + if (FileSystem.exists(resolvedPath)) { + folders.push(resolvedPath); + } + + seenPaths.add(resolvedPath); } - } catch (error) { - // If we have trouble accessing the file, ignore the error and consider it "not executable" - // since that's what a shell would do + + seenPaths.add(trimmedPath); } } + } - return true; + return folders; +} + +function _getExecutableContext(options: IExecutableResolveOptions | undefined): IExecutableContext { + if (!options) { + options = {}; } - /** - * Returns the list of folders where we will search for an executable, - * based on the PATH environment variable. - */ - private static _getSearchFolders(context: IExecutableContext): string[] { - const pathList: string = context.environmentMap.get('PATH') || ''; - - const folders: string[] = []; - - // Avoid processing duplicates - const seenPaths: Set = new Set(); - - // NOTE: Cmd.exe on Windows always searches the current working directory first. - // PowerShell and Unix shells do NOT do that, because it's a security concern. - // We follow their behavior. - - for (const splitPath of pathList.split(path.delimiter)) { - const trimmedPath: string = splitPath.trim(); - if (trimmedPath !== '') { - if (!seenPaths.has(trimmedPath)) { - // Fun fact: If you put relative paths in your PATH environment variable, - // all shells will dynamically match them against the current working directory. - // This is a terrible design, and in practice nobody does that, but it is supported... - // so we allow it here. - const resolvedPath: string = path.resolve(context.currentWorkingDirectory, trimmedPath); - - if (!seenPaths.has(resolvedPath)) { - if (FileSystem.exists(resolvedPath)) { - folders.push(resolvedPath); - } - - seenPaths.add(resolvedPath); - } + const environment: EnvironmentMap = _buildEnvironmentMap(options); + + let currentWorkingDirectory: string; + if (options.currentWorkingDirectory) { + currentWorkingDirectory = path.resolve(options.currentWorkingDirectory); + } else { + currentWorkingDirectory = process.cwd(); + } - seenPaths.add(trimmedPath); + const windowsExecutableExtensions: string[] = []; + + if (OS_PLATFORM === 'win32') { + const pathExtVariable: string = environment.get('PATHEXT') || ''; + for (const splitValue of pathExtVariable.split(';')) { + const trimmed: string = splitValue.trim().toLowerCase(); + // Ignore malformed extensions + if (/^\.[a-z0-9\.]*[a-z0-9]$/i.test(trimmed)) { + // Don't add the same extension twice + if (windowsExecutableExtensions.indexOf(trimmed) < 0) { + windowsExecutableExtensions.push(trimmed); } } } - - return folders; } - private static _getExecutableContext(options: IExecutableResolveOptions | undefined): IExecutableContext { - if (!options) { - options = {}; - } + return { + environmentMap: environment, + currentWorkingDirectory, + windowsExecutableExtensions + }; +} - const environment: EnvironmentMap = Executable._buildEnvironmentMap(options); +/** + * Given an input string containing special symbol characters, this inserts the "^" escape + * character to ensure the symbols are interpreted literally by the Windows shell. + */ +function _getEscapedForWindowsShell(text: string): string { + const escapableCharRegExp: RegExp = /[%\^&|<> ]/g; + return text.replace(escapableCharRegExp, (value) => '^' + value); +} - let currentWorkingDirectory: string; - if (options.currentWorkingDirectory) { - currentWorkingDirectory = path.resolve(options.currentWorkingDirectory); - } else { - currentWorkingDirectory = process.cwd(); +/** + * Checks for characters that are unsafe to pass to a Windows batch file + * due to the way that cmd.exe implements escaping. + */ +function _validateArgsForWindowsShell(args: string[]): void { + const specialCharRegExp: RegExp = /[%\^&|<>\r\n]/g; + + for (const arg of args) { + const match: RegExpMatchArray | null = arg.match(specialCharRegExp); + if (match) { + // NOTE: It is possible to escape some of these characters by prefixing them + // with a caret (^), which allows these characters to be successfully passed + // through to the batch file %1 variables. But they will be expanded again + // whenever they are used. For example, NPM's binary wrapper batch files + // use "%*" to pass their arguments to Node.exe, which causes them to be expanded + // again. Unfortunately the Cmd.exe batch language provides native escaping + // function (that could be used to insert the carets again). + // + // We could work around that by adding double carets, but in general there + // is no way to predict how many times the variable will get expanded. + // Thus, there is no generally reliable way to pass these characters. + throw new Error( + `The command line argument ${JSON.stringify(arg)} contains a` + + ` special character ${JSON.stringify(match[0])} that cannot be escaped for the Windows shell` + ); } + } +} - const windowsExecutableExtensions: string[] = []; - - if (os.platform() === 'win32') { - const pathExtVariable: string = environment.get('PATHEXT') || ''; - for (const splitValue of pathExtVariable.split(';')) { - const trimmed: string = splitValue.trim().toLowerCase(); - // Ignore malformed extensions - if (/^\.[a-z0-9\.]*[a-z0-9]$/i.test(trimmed)) { - // Don't add the same extension twice - if (windowsExecutableExtensions.indexOf(trimmed) < 0) { - windowsExecutableExtensions.push(trimmed); - } +// PROBLEM: Given an "args" array of strings that may contain special characters (e.g. spaces, +// backslashes, quotes), ensure that these strings pass through to the child process's ARGV array +// without anything getting corrupted along the way. +// +// On Unix you just pass the array to spawnSync(). But on Windows, this is a very complex problem: +// - The Win32 CreateProcess() API expects the args to be encoded as a single text string +// - The decoding of this string is up to the application (not the OS), and there are 3 different +// algorithms in common usage: the cmd.exe shell, the Microsoft CRT library init code, and +// the Win32 CommandLineToArgvW() +// - The encodings are counterintuitive and have lots of special cases +// - NodeJS spawnSync() tries do the encoding without knowing which decoder will be used +// +// See these articles for a full analysis: +// http://www.windowsinspired.com/understanding-the-command-line-string-and-arguments-received-by-a-windows-program/ +// http://www.windowsinspired.com/how-a-windows-programs-splits-its-command-line-into-individual-arguments/ +function _buildCommandLineFixup( + resolvedPath: string, + args: string[], + context: IExecutableContext +): ICommandLineOptions { + const fileExtension: string = path.extname(resolvedPath); + + if (OS_PLATFORM === 'win32') { + // Do we need a custom handler for this file type? + switch (fileExtension.toUpperCase()) { + case '.EXE': + case '.COM': + // okay to execute directly + break; + case '.BAT': + case '.CMD': { + _validateArgsForWindowsShell(args); + + // These file types must be invoked via the Windows shell + let shellPath: string | undefined = context.environmentMap.get('COMSPEC'); + if (!shellPath || !_canExecute(shellPath, context)) { + shellPath = Executable.tryResolve('cmd.exe'); + } + if (!shellPath) { + throw new Error( + `Unable to execute "${path.basename(resolvedPath)}" ` + + `because CMD.exe was not found in the PATH` + ); } - } - } - return { - environmentMap: environment, - currentWorkingDirectory, - windowsExecutableExtensions - }; - } + const shellArgs: string[] = []; + // /D: Disable execution of AutoRun commands when starting the new shell context + shellArgs.push('/d'); + // /S: Disable Cmd.exe's parsing of double-quote characters inside the command-line + shellArgs.push('/s'); + // /C: Execute the following command and then exit immediately + shellArgs.push('/c'); - /** - * Given an input string containing special symbol characters, this inserts the "^" escape - * character to ensure the symbols are interpreted literally by the Windows shell. - */ - private static _getEscapedForWindowsShell(text: string): string { - const escapableCharRegExp: RegExp = /[%\^&|<> ]/g; - return text.replace(escapableCharRegExp, (value) => '^' + value); - } + // If the path contains special charactrers (e.g. spaces), escape them so that + // they don't get interpreted by the shell + shellArgs.push(_getEscapedForWindowsShell(resolvedPath)); + shellArgs.push(...args); - /** - * Checks for characters that are unsafe to pass to a Windows batch file - * due to the way that cmd.exe implements escaping. - */ - private static _validateArgsForWindowsShell(args: string[]): void { - const specialCharRegExp: RegExp = /[%\^&|<>\r\n]/g; - - for (const arg of args) { - const match: RegExpMatchArray | null = arg.match(specialCharRegExp); - if (match) { - // NOTE: It is possible to escape some of these characters by prefixing them - // with a caret (^), which allows these characters to be successfully passed - // through to the batch file %1 variables. But they will be expanded again - // whenever they are used. For example, NPM's binary wrapper batch files - // use "%*" to pass their arguments to Node.exe, which causes them to be expanded - // again. Unfortunately the Cmd.exe batch language provides native escaping - // function (that could be used to insert the carets again). - // - // We could work around that by adding double carets, but in general there - // is no way to predict how many times the variable will get expanded. - // Thus, there is no generally reliable way to pass these characters. + return { path: shellPath, args: shellArgs }; + } + default: throw new Error( - `The command line argument ${JSON.stringify(arg)} contains a` + - ` special character ${JSON.stringify(match[0])} that cannot be escaped for the Windows shell` + `Cannot execute "${path.basename(resolvedPath)}" because the file type is not supported` ); - } } } + + return { + path: resolvedPath, + args: args + }; } diff --git a/libraries/node-core-library/src/FileError.ts b/libraries/node-core-library/src/FileError.ts index 00426da5d20..4de451cd7ab 100644 --- a/libraries/node-core-library/src/FileError.ts +++ b/libraries/node-core-library/src/FileError.ts @@ -1,6 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { FileLocationStyle, Path } from './Path'; + +import type { IProblemPattern } from '@rushstack/problem-matcher'; + +import { type FileLocationStyle, Path } from './Path'; import { TypeUuid } from './TypeUuid'; /** @@ -46,6 +49,36 @@ const uuidFileError: string = '37a4c772-2dc8-4c66-89ae-262f8cc1f0c1'; const baseFolderEnvVar: string = 'RUSHSTACK_FILE_ERROR_BASE_FOLDER'; +const unixProblemMatcherPattern: IProblemPattern = { + regexp: '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^:]+):(\\d+):(\\d+)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', + severity: 1, + file: 2, + line: 3, + column: 4, + code: 5, + message: 6 +}; + +const vsProblemMatcherPattern: IProblemPattern = { + regexp: + '^\\[[^\\]]+\\]\\s+(Error|Warning):\\s+([^\\(]+)\\((\\d+),(\\d+)\\)\\s+-\\s+(?:\\(([^)]+)\\)\\s+)?(.*)$', + severity: 1, + file: 2, + line: 3, + column: 4, + code: 5, + message: 6 +}; + +const _environmentVariableBasePathFnMap: ReadonlyMap< + string | undefined, + (fileError: FileError) => string | undefined +> = new Map([ + [undefined, (fileError: FileError) => fileError.projectFolder], + ['{PROJECT_FOLDER}', (fileError: FileError) => fileError.projectFolder], + ['{ABSOLUTE_PATH}', (fileError: FileError) => undefined as string | undefined] +]); + /** * An `Error` subclass that should be thrown to report an unexpected state that specifically references * a location in a file. @@ -61,15 +94,6 @@ export class FileError extends Error { /** @internal */ public static _environmentVariableIsAbsolutePath: boolean = false; - private static _environmentVariableBasePathFnMap: ReadonlyMap< - string | undefined, - (fileError: FileError) => string | undefined - > = new Map([ - [undefined, (fileError: FileError) => fileError.projectFolder], - ['{PROJECT_FOLDER}', (fileError: FileError) => fileError.projectFolder], - ['{ABSOLUTE_PATH}', (fileError: FileError) => undefined as string | undefined] - ]); - /** {@inheritdoc IFileErrorOptions.absolutePath} */ public readonly absolutePath: string; /** {@inheritdoc IFileErrorOptions.projectFolder} */ @@ -102,10 +126,8 @@ export class FileError extends Error { /** * Get the Unix-formatted the error message. - * - * @override */ - public toString(): string { + public override toString(): string { // Default to formatting in 'Unix' format, for consistency. return this.getFormattedErrorMessage(); } @@ -126,6 +148,24 @@ export class FileError extends Error { }); } + /** + * Get the problem matcher pattern for parsing error messages. + * + * @param options - Options for the error message format. + * @returns The problem matcher pattern. + */ + public static getProblemMatcher(options?: Pick): IProblemPattern { + const format: FileLocationStyle = options?.format || 'Unix'; + switch (format) { + case 'Unix': + return unixProblemMatcherPattern; + case 'VisualStudio': + return vsProblemMatcherPattern; + default: + throw new Error(`The FileError format "${format}" is not supported for problem matchers.`); + } + } + private _evaluateBaseFolder(): string | undefined { // Cache the sanitized environment variable. This means that we don't support changing // the environment variable mid-execution. This is a reasonable tradeoff for the benefit @@ -143,7 +183,7 @@ export class FileError extends Error { // undefined environment variable has a mapping to the project folder const baseFolderFn: ((fileError: FileError) => string | undefined) | undefined = - FileError._environmentVariableBasePathFnMap.get(FileError._sanitizedEnvironmentVariable); + _environmentVariableBasePathFnMap.get(FileError._sanitizedEnvironmentVariable); if (baseFolderFn) { return baseFolderFn(this); } diff --git a/libraries/node-core-library/src/FileSystem.ts b/libraries/node-core-library/src/FileSystem.ts index 4911b684ef4..7eff1995b8b 100644 --- a/libraries/node-core-library/src/FileSystem.ts +++ b/libraries/node-core-library/src/FileSystem.ts @@ -1,13 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as nodeJsPath from 'path'; -import * as fs from 'fs'; +import * as nodeJsPath from 'node:path'; +import * as fs from 'node:fs'; +import * as fsPromises from 'node:fs/promises'; + import * as fsx from 'fs-extra'; -import { Text, NewlineKind, Encoding } from './Text'; +import { Text, type NewlineKind, Encoding } from './Text'; import { PosixModeBits } from './PosixModeBits'; -import { LegacyAdapters } from './LegacyAdapters'; /** * An alias for the Node.js `fs.Stats` object. @@ -27,11 +28,29 @@ export type FileSystemStats = fs.Stats; */ export type FolderItem = fs.Dirent; +/** + * An alias for the Node.js `fs.ReadStream` object. + * + * @remarks + * This avoids the need to import the `fs` package when using the {@link FileSystem} API. + * @public + */ +export type FileSystemReadStream = fs.ReadStream; + +/** + * An alias for the Node.js `fs.WriteStream` object. + * + * @remarks + * This avoids the need to import the `fs` package when using the {@link FileSystem} API. + * @public + */ +export type FileSystemWriteStream = fs.WriteStream; + // The PosixModeBits are intended to be used with bitwise operations. /* eslint-disable no-bitwise */ /** - * The options for {@link FileSystem.readFolder} + * The options for {@link FileSystem.readFolderItems} and {@link FileSystem.readFolderItemNames}. * @public */ export interface IFileSystemReadFolderOptions { @@ -43,16 +62,27 @@ export interface IFileSystemReadFolderOptions { } /** - * The options for {@link FileSystem.writeFile} * @public */ -export interface IFileSystemWriteFileOptions { +export interface IFileSystemWriteFileOptionsBase { /** * If true, will ensure the folder is created before writing the file. * @defaultValue false */ ensureFolderExists?: boolean; +} + +/** + * The options for {@link FileSystem.writeBuffersToFile} + * @public + */ +export interface IFileSystemWriteBinaryFileOptions extends IFileSystemWriteFileOptionsBase {} +/** + * The options for {@link FileSystem.writeFile} + * @public + */ +export interface IFileSystemWriteFileOptions extends IFileSystemWriteBinaryFileOptions { /** * If specified, will normalize line endings to the specified style of newline. * @defaultValue `undefined` which means no conversion will be performed @@ -88,7 +118,7 @@ export interface IFileSystemReadFileOptions { * The options for {@link FileSystem.move} * @public */ -export interface IFileSystemMoveOptions { +export interface IFileSystemMoveOptions extends IFileSystemWriteFileOptionsBase { /** * The path of the existing object to be moved. * The path may be absolute or relative. @@ -106,12 +136,6 @@ export interface IFileSystemMoveOptions { * @defaultValue true */ overwrite?: boolean; - - /** - * If true, will ensure the folder is created before writing the file. - * @defaultValue false - */ - ensureFolderExists?: boolean; } /** @@ -251,6 +275,12 @@ export interface IFileSystemCopyFilesOptions extends IFileSystemCopyFilesAsyncOp filter?: FileSystemCopyFilesFilter; // narrow the type to exclude FileSystemCopyFilesAsyncFilter } +/** + * The options for {@link FileSystem.createWriteStream} + * @public + */ +export interface IFileSystemCreateWriteStreamOptions extends IFileSystemWriteFileOptionsBase {} + /** * The options for {@link FileSystem.deleteFile} * @public @@ -380,7 +410,7 @@ export class FileSystem { * @param path - The absolute or relative path to the filesystem object. */ public static exists(path: string): boolean { - return FileSystem._wrapException(() => { + return _wrapException(() => { return fsx.existsSync(path); }); } @@ -389,7 +419,7 @@ export class FileSystem { * An async version of {@link FileSystem.exists}. */ public static async existsAsync(path: string): Promise { - return await FileSystem._wrapExceptionAsync(() => { + return await _wrapExceptionAsync(() => { return new Promise((resolve: (result: boolean) => void) => { fsx.exists(path, resolve); }); @@ -403,7 +433,7 @@ export class FileSystem { * @param path - The absolute or relative path to the filesystem object. */ public static getStatistics(path: string): FileSystemStats { - return FileSystem._wrapException(() => { + return _wrapException(() => { return fsx.statSync(path); }); } @@ -412,7 +442,7 @@ export class FileSystem { * An async version of {@link FileSystem.getStatistics}. */ public static async getStatisticsAsync(path: string): Promise { - return await FileSystem._wrapExceptionAsync(() => { + return await _wrapExceptionAsync(() => { return fsx.stat(path); }); } @@ -425,7 +455,7 @@ export class FileSystem { * @param times - The times that the object should be updated to reflect. */ public static updateTimes(path: string, times: IFileSystemUpdateTimeParameters): void { - return FileSystem._wrapException(() => { + return _wrapException(() => { fsx.utimesSync(path, times.accessedTime, times.modifiedTime); }); } @@ -434,7 +464,7 @@ export class FileSystem { * An async version of {@link FileSystem.updateTimes}. */ public static async updateTimesAsync(path: string, times: IFileSystemUpdateTimeParameters): Promise { - await FileSystem._wrapExceptionAsync(() => { + await _wrapExceptionAsync(() => { // This cast is needed because the fs-extra typings require both parameters // to have the same type (number or Date), whereas Node.js does not require that. return fsx.utimes(path, times.accessedTime as number, times.modifiedTime as number); @@ -447,9 +477,9 @@ export class FileSystem { * @param path - The absolute or relative path to the object that should be updated. * @param modeBits - POSIX-style file mode bits specified using the {@link PosixModeBits} enum */ - public static changePosixModeBits(path: string, mode: PosixModeBits): void { - FileSystem._wrapException(() => { - fs.chmodSync(path, mode); + public static changePosixModeBits(path: string, modeBits: PosixModeBits): void { + _wrapException(() => { + fs.chmodSync(path, modeBits); }); } @@ -457,7 +487,7 @@ export class FileSystem { * An async version of {@link FileSystem.changePosixModeBits}. */ public static async changePosixModeBitsAsync(path: string, mode: PosixModeBits): Promise { - await FileSystem._wrapExceptionAsync(() => { + await _wrapExceptionAsync(() => { return fsx.chmod(path, mode); }); } @@ -473,7 +503,7 @@ export class FileSystem { * to call {@link FileSystem.getStatistics} directly instead. */ public static getPosixModeBits(path: string): PosixModeBits { - return FileSystem._wrapException(() => { + return _wrapException(() => { return FileSystem.getStatistics(path).mode; }); } @@ -482,7 +512,7 @@ export class FileSystem { * An async version of {@link FileSystem.getPosixModeBits}. */ public static async getPosixModeBitsAsync(path: string): Promise { - return await FileSystem._wrapExceptionAsync(async () => { + return await _wrapExceptionAsync(async () => { return (await FileSystem.getStatisticsAsync(path)).mode; }); } @@ -517,7 +547,7 @@ export class FileSystem { * Behind the scenes it uses `fs-extra.moveSync()` */ public static move(options: IFileSystemMoveOptions): void { - FileSystem._wrapException(() => { + _wrapException(() => { options = { ...MOVE_DEFAULT_OPTIONS, ...options @@ -545,7 +575,7 @@ export class FileSystem { * An async version of {@link FileSystem.move}. */ public static async moveAsync(options: IFileSystemMoveOptions): Promise { - await FileSystem._wrapExceptionAsync(async () => { + await _wrapExceptionAsync(async () => { options = { ...MOVE_DEFAULT_OPTIONS, ...options @@ -581,7 +611,7 @@ export class FileSystem { * @param folderPath - The absolute or relative path of the folder which should be created. */ public static ensureFolder(folderPath: string): void { - FileSystem._wrapException(() => { + _wrapException(() => { fsx.ensureDirSync(folderPath); }); } @@ -590,30 +620,11 @@ export class FileSystem { * An async version of {@link FileSystem.ensureFolder}. */ public static async ensureFolderAsync(folderPath: string): Promise { - await FileSystem._wrapExceptionAsync(() => { + await _wrapExceptionAsync(() => { return fsx.ensureDir(folderPath); }); } - /** - * @deprecated - * Use {@link FileSystem.readFolderItemNames} instead. - */ - public static readFolder(folderPath: string, options?: IFileSystemReadFolderOptions): string[] { - return FileSystem.readFolderItemNames(folderPath, options); - } - - /** - * @deprecated - * Use {@link FileSystem.readFolderItemNamesAsync} instead. - */ - public static async readFolderAsync( - folderPath: string, - options?: IFileSystemReadFolderOptions - ): Promise { - return await FileSystem.readFolderItemNamesAsync(folderPath, options); - } - /** * Reads the names of folder entries, not including "." or "..". * Behind the scenes it uses `fs.readdirSync()`. @@ -621,7 +632,7 @@ export class FileSystem { * @param options - Optional settings that can change the behavior. Type: `IReadFolderOptions` */ public static readFolderItemNames(folderPath: string, options?: IFileSystemReadFolderOptions): string[] { - return FileSystem._wrapException(() => { + return _wrapException(() => { options = { ...READ_FOLDER_DEFAULT_OPTIONS, ...options @@ -643,7 +654,7 @@ export class FileSystem { folderPath: string, options?: IFileSystemReadFolderOptions ): Promise { - return await FileSystem._wrapExceptionAsync(async () => { + return await _wrapExceptionAsync(async () => { options = { ...READ_FOLDER_DEFAULT_OPTIONS, ...options @@ -666,7 +677,7 @@ export class FileSystem { * @param options - Optional settings that can change the behavior. Type: `IReadFolderOptions` */ public static readFolderItems(folderPath: string, options?: IFileSystemReadFolderOptions): FolderItem[] { - return FileSystem._wrapException(() => { + return _wrapException(() => { options = { ...READ_FOLDER_DEFAULT_OPTIONS, ...options @@ -691,17 +702,13 @@ export class FileSystem { folderPath: string, options?: IFileSystemReadFolderOptions ): Promise { - return await FileSystem._wrapExceptionAsync(async () => { + return await _wrapExceptionAsync(async () => { options = { ...READ_FOLDER_DEFAULT_OPTIONS, ...options }; - const folderEntries: FolderItem[] = await LegacyAdapters.convertCallbackToPromise( - fs.readdir, - folderPath, - { withFileTypes: true } - ); + const folderEntries: FolderItem[] = await fsPromises.readdir(folderPath, { withFileTypes: true }); if (options.absolutePaths) { return folderEntries.map((folderEntry) => { folderEntry.name = nodeJsPath.resolve(folderPath, folderEntry.name); @@ -721,7 +728,7 @@ export class FileSystem { * @param folderPath - The absolute or relative path to the folder which should be deleted. */ public static deleteFolder(folderPath: string): void { - FileSystem._wrapException(() => { + _wrapException(() => { fsx.removeSync(folderPath); }); } @@ -730,7 +737,7 @@ export class FileSystem { * An async version of {@link FileSystem.deleteFolder}. */ public static async deleteFolderAsync(folderPath: string): Promise { - await FileSystem._wrapExceptionAsync(() => { + await _wrapExceptionAsync(() => { return fsx.remove(folderPath); }); } @@ -744,7 +751,7 @@ export class FileSystem { * @param folderPath - The absolute or relative path to the folder which should have its contents deleted. */ public static ensureEmptyFolder(folderPath: string): void { - FileSystem._wrapException(() => { + _wrapException(() => { fsx.emptyDirSync(folderPath); }); } @@ -753,7 +760,7 @@ export class FileSystem { * An async version of {@link FileSystem.ensureEmptyFolder}. */ public static async ensureEmptyFolderAsync(folderPath: string): Promise { - await FileSystem._wrapExceptionAsync(() => { + await _wrapExceptionAsync(() => { return fsx.emptyDir(folderPath); }); } @@ -766,17 +773,18 @@ export class FileSystem { * Writes a text string to a file on disk, overwriting the file if it already exists. * Behind the scenes it uses `fs.writeFileSync()`. * @remarks - * Throws an error if the folder doesn't exist, unless ensureFolder=true. + * Throws an error if the folder doesn't exist, unless {@link IFileSystemWriteFileOptionsBase.ensureFolderExists} + * is set to `true`. * @param filePath - The absolute or relative path of the file. * @param contents - The text that should be written to the file. - * @param options - Optional settings that can change the behavior. Type: `IWriteFileOptions` + * @param options - Optional settings that can change the behavior. */ public static writeFile( filePath: string, contents: string | Buffer, options?: IFileSystemWriteFileOptions ): void { - FileSystem._wrapException(() => { + _wrapException(() => { options = { ...WRITE_FILE_DEFAULT_OPTIONS, ...options @@ -804,6 +812,76 @@ export class FileSystem { }); } + /** + * Writes the contents of multiple Uint8Arrays to a file on disk, overwriting the file if it already exists. + * Behind the scenes it uses `fs.writevSync()`. + * + * This API is useful for writing large files efficiently, especially if the input is being concatenated from + * multiple sources. + * + * @remarks + * Throws an error if the folder doesn't exist, unless {@link IFileSystemWriteFileOptionsBase.ensureFolderExists} + * is set to `true`. + * @param filePath - The absolute or relative path of the file. + * @param contents - The content that should be written to the file. + * @param options - Optional settings that can change the behavior. + */ + public static writeBuffersToFile( + filePath: string, + contents: ReadonlyArray, + options?: IFileSystemWriteBinaryFileOptions + ): void { + _wrapException(() => { + // Need a mutable copy of the iterable to handle incomplete writes, + // since writev() doesn't take an argument for where to start writing. + const toCopy: NodeJS.ArrayBufferView[] = [...contents]; + + let fd: number | undefined; + try { + fd = fsx.openSync(filePath, 'w'); + } catch (error) { + if (!options?.ensureFolderExists || !FileSystem.isNotExistError(error as Error)) { + throw error; + } + + const folderPath: string = nodeJsPath.dirname(filePath); + FileSystem.ensureFolder(folderPath); + fd = fsx.openSync(filePath, 'w'); + } + + try { + // In practice this loop will have exactly 1 iteration, but the spec allows + // for a writev call to write fewer bytes than requested + while (toCopy.length) { + let bytesWritten: number = fsx.writevSync(fd, toCopy); + let buffersWritten: number = 0; + while (buffersWritten < toCopy.length) { + const bytesInCurrentBuffer: number = toCopy[buffersWritten].byteLength; + if (bytesWritten < bytesInCurrentBuffer) { + // This buffer was partially written. + const currentToCopy: NodeJS.ArrayBufferView = toCopy[buffersWritten]; + toCopy[buffersWritten] = new Uint8Array( + currentToCopy.buffer, + currentToCopy.byteOffset + bytesWritten, + currentToCopy.byteLength - bytesWritten + ); + break; + } + bytesWritten -= bytesInCurrentBuffer; + buffersWritten++; + } + + if (buffersWritten > 0) { + // Avoid cost of shifting the array more than needed. + toCopy.splice(0, buffersWritten); + } + } + } finally { + fsx.closeSync(fd); + } + }); + } + /** * An async version of {@link FileSystem.writeFile}. */ @@ -812,7 +890,7 @@ export class FileSystem { contents: string | Buffer, options?: IFileSystemWriteFileOptions ): Promise { - await FileSystem._wrapExceptionAsync(async () => { + await _wrapExceptionAsync(async () => { options = { ...WRITE_FILE_DEFAULT_OPTIONS, ...options @@ -840,21 +918,81 @@ export class FileSystem { }); } + /** + * An async version of {@link FileSystem.writeBuffersToFile}. + */ + public static async writeBuffersToFileAsync( + filePath: string, + contents: ReadonlyArray, + options?: IFileSystemWriteBinaryFileOptions + ): Promise { + await _wrapExceptionAsync(async () => { + // Need a mutable copy of the iterable to handle incomplete writes, + // since writev() doesn't take an argument for where to start writing. + const toCopy: NodeJS.ArrayBufferView[] = [...contents]; + + let handle: fsPromises.FileHandle | undefined; + try { + handle = await fsPromises.open(filePath, 'w'); + } catch (error) { + if (!options?.ensureFolderExists || !FileSystem.isNotExistError(error as Error)) { + throw error; + } + + const folderPath: string = nodeJsPath.dirname(filePath); + await FileSystem.ensureFolderAsync(folderPath); + handle = await fsPromises.open(filePath, 'w'); + } + + try { + // In practice this loop will have exactly 1 iteration, but the spec allows + // for a writev call to write fewer bytes than requested + while (toCopy.length) { + let bytesWritten: number = (await handle.writev(toCopy)).bytesWritten; + let buffersWritten: number = 0; + while (buffersWritten < toCopy.length) { + const bytesInCurrentBuffer: number = toCopy[buffersWritten].byteLength; + if (bytesWritten < bytesInCurrentBuffer) { + // This buffer was partially written. + const currentToCopy: NodeJS.ArrayBufferView = toCopy[buffersWritten]; + toCopy[buffersWritten] = new Uint8Array( + currentToCopy.buffer, + currentToCopy.byteOffset + bytesWritten, + currentToCopy.byteLength - bytesWritten + ); + break; + } + bytesWritten -= bytesInCurrentBuffer; + buffersWritten++; + } + + if (buffersWritten > 0) { + // Avoid cost of shifting the array more than needed. + toCopy.splice(0, buffersWritten); + } + } + } finally { + await handle.close(); + } + }); + } + /** * Writes a text string to a file on disk, appending to the file if it already exists. * Behind the scenes it uses `fs.appendFileSync()`. * @remarks - * Throws an error if the folder doesn't exist, unless ensureFolder=true. + * Throws an error if the folder doesn't exist, unless {@link IFileSystemWriteFileOptionsBase.ensureFolderExists} + * is set to `true`. * @param filePath - The absolute or relative path of the file. * @param contents - The text that should be written to the file. - * @param options - Optional settings that can change the behavior. Type: `IWriteFileOptions` + * @param options - Optional settings that can change the behavior. */ public static appendToFile( filePath: string, contents: string | Buffer, options?: IFileSystemWriteFileOptions ): void { - FileSystem._wrapException(() => { + _wrapException(() => { options = { ...APPEND_TO_FILE_DEFAULT_OPTIONS, ...options @@ -890,7 +1028,7 @@ export class FileSystem { contents: string | Buffer, options?: IFileSystemWriteFileOptions ): Promise { - await FileSystem._wrapExceptionAsync(async () => { + await _wrapExceptionAsync(async () => { options = { ...APPEND_TO_FILE_DEFAULT_OPTIONS, ...options @@ -925,7 +1063,7 @@ export class FileSystem { * @param options - Optional settings that can change the behavior. Type: `IReadFileOptions` */ public static readFile(filePath: string, options?: IFileSystemReadFileOptions): string { - return FileSystem._wrapException(() => { + return _wrapException(() => { options = { ...READ_FILE_DEFAULT_OPTIONS, ...options @@ -944,7 +1082,7 @@ export class FileSystem { * An async version of {@link FileSystem.readFile}. */ public static async readFileAsync(filePath: string, options?: IFileSystemReadFileOptions): Promise { - return await FileSystem._wrapExceptionAsync(async () => { + return await _wrapExceptionAsync(async () => { options = { ...READ_FILE_DEFAULT_OPTIONS, ...options @@ -965,7 +1103,7 @@ export class FileSystem { * @param filePath - The relative or absolute path to the file whose contents should be read. */ public static readFileToBuffer(filePath: string): Buffer { - return FileSystem._wrapException(() => { + return _wrapException(() => { return fsx.readFileSync(filePath); }); } @@ -974,7 +1112,7 @@ export class FileSystem { * An async version of {@link FileSystem.readFileToBuffer}. */ public static async readFileToBufferAsync(filePath: string): Promise { - return await FileSystem._wrapExceptionAsync(() => { + return await _wrapExceptionAsync(() => { return fsx.readFile(filePath); }); } @@ -1001,7 +1139,7 @@ export class FileSystem { ); } - FileSystem._wrapException(() => { + _wrapException(() => { fsx.copySync(options.sourcePath, options.destinationPath, { errorOnExist: options.alreadyExistsBehavior === AlreadyExistsBehavior.Error, overwrite: options.alreadyExistsBehavior === AlreadyExistsBehavior.Overwrite @@ -1024,7 +1162,7 @@ export class FileSystem { ); } - await FileSystem._wrapExceptionAsync(() => { + await _wrapExceptionAsync(() => { return fsx.copy(options.sourcePath, options.destinationPath, { errorOnExist: options.alreadyExistsBehavior === AlreadyExistsBehavior.Error, overwrite: options.alreadyExistsBehavior === AlreadyExistsBehavior.Overwrite @@ -1048,7 +1186,7 @@ export class FileSystem { ...options }; - FileSystem._wrapException(() => { + _wrapException(() => { fsx.copySync(options.sourcePath, options.destinationPath, { dereference: !!options.dereferenceSymlinks, errorOnExist: options.alreadyExistsBehavior === AlreadyExistsBehavior.Error, @@ -1068,7 +1206,7 @@ export class FileSystem { ...options }; - await FileSystem._wrapExceptionAsync(async () => { + await _wrapExceptionAsync(async () => { await fsx.copy(options.sourcePath, options.destinationPath, { dereference: !!options.dereferenceSymlinks, errorOnExist: options.alreadyExistsBehavior === AlreadyExistsBehavior.Error, @@ -1086,7 +1224,7 @@ export class FileSystem { * @param options - Optional settings that can change the behavior. Type: `IDeleteFileOptions` */ public static deleteFile(filePath: string, options?: IFileSystemDeleteFileOptions): void { - FileSystem._wrapException(() => { + _wrapException(() => { options = { ...DELETE_FILE_DEFAULT_OPTIONS, ...options @@ -1109,7 +1247,7 @@ export class FileSystem { filePath: string, options?: IFileSystemDeleteFileOptions ): Promise { - await FileSystem._wrapExceptionAsync(async () => { + await _wrapExceptionAsync(async () => { options = { ...DELETE_FILE_DEFAULT_OPTIONS, ...options @@ -1125,6 +1263,61 @@ export class FileSystem { }); } + /** + * Creates a readable stream for an existing file. + * Behind the scenes it uses `fs.createReadStream()`. + * + * @param filePath - The path to the file. The path may be absolute or relative. + * @returns A new readable stream for the file. + */ + public static createReadStream(filePath: string): FileSystemReadStream { + return _wrapException(() => { + return fs.createReadStream(filePath); + }); + } + + /** + * Creates a writable stream for writing to a file. + * Behind the scenes it uses `fs.createWriteStream()`. + * + * @remarks + * Throws an error if the folder doesn't exist, unless {@link IFileSystemWriteFileOptionsBase.ensureFolderExists} + * is set to `true`. + * @param filePath - The path to the file. The path may be absolute or relative. + * @param options - Optional settings that can change the behavior. + * @returns A new writable stream for the file. + */ + public static createWriteStream( + filePath: string, + options?: IFileSystemCreateWriteStreamOptions + ): FileSystemWriteStream { + return _wrapException(() => { + if (options?.ensureFolderExists) { + const folderPath: string = nodeJsPath.dirname(filePath); + FileSystem.ensureFolder(folderPath); + } + + return fs.createWriteStream(filePath); + }); + } + + /** + * An async version of {@link FileSystem.createWriteStream}. + */ + public static async createWriteStreamAsync( + filePath: string, + options?: IFileSystemCreateWriteStreamOptions + ): Promise { + return await _wrapExceptionAsync(async () => { + if (options?.ensureFolderExists) { + const folderPath: string = nodeJsPath.dirname(filePath); + await FileSystem.ensureFolderAsync(folderPath); + } + + return fs.createWriteStream(filePath); + }); + } + // =============== // LINK OPERATIONS // =============== @@ -1135,7 +1328,7 @@ export class FileSystem { * @param path - The absolute or relative path to the filesystem object. */ public static getLinkStatistics(path: string): FileSystemStats { - return FileSystem._wrapException(() => { + return _wrapException(() => { return fsx.lstatSync(path); }); } @@ -1144,7 +1337,7 @@ export class FileSystem { * An async version of {@link FileSystem.getLinkStatistics}. */ public static async getLinkStatisticsAsync(path: string): Promise { - return await FileSystem._wrapExceptionAsync(() => { + return await _wrapExceptionAsync(() => { return fsx.lstat(path); }); } @@ -1161,7 +1354,7 @@ export class FileSystem { * @returns the path of the link target */ public static readLink(path: string): string { - return FileSystem._wrapException(() => { + return _wrapException(() => { return fsx.readlinkSync(path); }); } @@ -1170,7 +1363,7 @@ export class FileSystem { * An async version of {@link FileSystem.readLink}. */ public static async readLinkAsync(path: string): Promise { - return await FileSystem._wrapExceptionAsync(() => { + return await _wrapExceptionAsync(() => { return fsx.readlink(path); }); } @@ -1193,8 +1386,8 @@ export class FileSystem { * if not, use a symbolic link instead. */ public static createSymbolicLinkJunction(options: IFileSystemCreateLinkOptions): void { - FileSystem._wrapException(() => { - return FileSystem._handleLink(() => { + _wrapException(() => { + return _handleLink(() => { // For directories, we use a Windows "junction". On POSIX operating systems, this produces a regular symlink. return fsx.symlinkSync(options.linkTargetPath, options.newLinkPath, 'junction'); }, options); @@ -1205,8 +1398,8 @@ export class FileSystem { * An async version of {@link FileSystem.createSymbolicLinkJunction}. */ public static async createSymbolicLinkJunctionAsync(options: IFileSystemCreateLinkOptions): Promise { - await FileSystem._wrapExceptionAsync(() => { - return FileSystem._handleLinkAsync(() => { + await _wrapExceptionAsync(() => { + return _handleLinkAsync(() => { // For directories, we use a Windows "junction". On POSIX operating systems, this produces a regular symlink. return fsx.symlink(options.linkTargetPath, options.newLinkPath, 'junction'); }, options); @@ -1227,8 +1420,8 @@ export class FileSystem { * tool incompatible with Windows. */ public static createSymbolicLinkFile(options: IFileSystemCreateLinkOptions): void { - FileSystem._wrapException(() => { - return FileSystem._handleLink(() => { + _wrapException(() => { + return _handleLink(() => { return fsx.symlinkSync(options.linkTargetPath, options.newLinkPath, 'file'); }, options); }); @@ -1238,8 +1431,8 @@ export class FileSystem { * An async version of {@link FileSystem.createSymbolicLinkFile}. */ public static async createSymbolicLinkFileAsync(options: IFileSystemCreateLinkOptions): Promise { - await FileSystem._wrapExceptionAsync(() => { - return FileSystem._handleLinkAsync(() => { + await _wrapExceptionAsync(() => { + return _handleLinkAsync(() => { return fsx.symlink(options.linkTargetPath, options.newLinkPath, 'file'); }, options); }); @@ -1259,8 +1452,8 @@ export class FileSystem { * tool incompatible with Windows. */ public static createSymbolicLinkFolder(options: IFileSystemCreateLinkOptions): void { - FileSystem._wrapException(() => { - return FileSystem._handleLink(() => { + _wrapException(() => { + return _handleLink(() => { return fsx.symlinkSync(options.linkTargetPath, options.newLinkPath, 'dir'); }, options); }); @@ -1270,8 +1463,8 @@ export class FileSystem { * An async version of {@link FileSystem.createSymbolicLinkFolder}. */ public static async createSymbolicLinkFolderAsync(options: IFileSystemCreateLinkOptions): Promise { - await FileSystem._wrapExceptionAsync(() => { - return FileSystem._handleLinkAsync(() => { + await _wrapExceptionAsync(() => { + return _handleLinkAsync(() => { return fsx.symlink(options.linkTargetPath, options.newLinkPath, 'dir'); }, options); }); @@ -1294,8 +1487,8 @@ export class FileSystem { * if not, use a symbolic link instead. */ public static createHardLink(options: IFileSystemCreateLinkOptions): void { - FileSystem._wrapException(() => { - return FileSystem._handleLink( + _wrapException(() => { + return _handleLink( () => { return fsx.linkSync(options.linkTargetPath, options.newLinkPath); }, @@ -1308,8 +1501,8 @@ export class FileSystem { * An async version of {@link FileSystem.createHardLink}. */ public static async createHardLinkAsync(options: IFileSystemCreateLinkOptions): Promise { - await FileSystem._wrapExceptionAsync(() => { - return FileSystem._handleLinkAsync( + await _wrapExceptionAsync(() => { + return _handleLinkAsync( () => { return fsx.link(options.linkTargetPath, options.newLinkPath); }, @@ -1324,7 +1517,7 @@ export class FileSystem { * @param linkPath - The path to the link. */ public static getRealPath(linkPath: string): string { - return FileSystem._wrapException(() => { + return _wrapException(() => { return fsx.realpathSync(linkPath); }); } @@ -1333,7 +1526,7 @@ export class FileSystem { * An async version of {@link FileSystem.getRealPath}. */ public static async getRealPathAsync(linkPath: string): Promise { - return await FileSystem._wrapExceptionAsync(() => { + return await _wrapExceptionAsync(() => { return fsx.realpath(linkPath); }); } @@ -1397,133 +1590,164 @@ export class FileSystem { */ public static isErrnoException(error: Error): error is NodeJS.ErrnoException { const typedError: NodeJS.ErrnoException = error; + // Don't check for `path` because the syscall may not have a path. + // For example, when invoked with a file descriptor. return ( typeof typedError.code === 'string' && typeof typedError.errno === 'number' && - typeof typedError.path === 'string' && typeof typedError.syscall === 'string' ); } +} - private static _handleLink(linkFn: () => void, options: IInternalFileSystemCreateLinkOptions): void { - try { +function _handleLinkExistError( + linkFn: () => void, + options: IInternalFileSystemCreateLinkOptions, + error: Error +): void { + switch (options.alreadyExistsBehavior) { + case AlreadyExistsBehavior.Ignore: + break; + case AlreadyExistsBehavior.Overwrite: + // fsx.linkSync does not allow overwriting so we must manually delete. If it's + // a folder, it will throw an error. + FileSystem.deleteFile(options.newLinkPath); linkFn(); - } catch (error) { - if (FileSystem.isExistError(error as Error)) { - // Link exists, handle it - switch (options.alreadyExistsBehavior) { - case AlreadyExistsBehavior.Ignore: - break; - case AlreadyExistsBehavior.Overwrite: - // fsx.linkSync does not allow overwriting so we must manually delete. If it's - // a folder, it will throw an error. - this.deleteFile(options.newLinkPath); - linkFn(); - break; - case AlreadyExistsBehavior.Error: - default: - throw error; - } - } else { - // When attempting to create a link in a directory that does not exist, an ENOENT - // or ENOTDIR error is thrown, so we should ensure the directory exists before - // retrying. There are also cases where the target file must exist, so validate in - // those cases to avoid confusing the missing directory with the missing target file. - if ( - FileSystem.isNotExistError(error as Error) && - (!options.linkTargetMustExist || FileSystem.exists(options.linkTargetPath)) - ) { - this.ensureFolder(nodeJsPath.dirname(options.newLinkPath)); + break; + case AlreadyExistsBehavior.Error: + default: + throw error; + } +} + +function _handleLink(linkFn: () => void, options: IInternalFileSystemCreateLinkOptions): void { + try { + linkFn(); + } catch (error) { + if (FileSystem.isExistError(error as Error)) { + // Link exists, handle it + _handleLinkExistError(linkFn, options, error as Error); + } else { + // When attempting to create a link in a directory that does not exist, an ENOENT + // or ENOTDIR error is thrown, so we should ensure the directory exists before + // retrying. There are also cases where the target file must exist, so validate in + // those cases to avoid confusing the missing directory with the missing target file. + if ( + FileSystem.isNotExistError(error as Error) && + (!options.linkTargetMustExist || FileSystem.exists(options.linkTargetPath)) + ) { + FileSystem.ensureFolder(nodeJsPath.dirname(options.newLinkPath)); + try { linkFn(); - } else { - throw error; + } catch (retryError) { + if (FileSystem.isExistError(retryError as Error)) { + // Another concurrent process may have created the link between the ensureFolder + // call and the retry; handle it the same way as the initial exist error. + _handleLinkExistError(linkFn, options, retryError as Error); + } else { + throw retryError; + } } + } else { + throw error; } } } +} - private static async _handleLinkAsync( - linkFn: () => Promise, - options: IInternalFileSystemCreateLinkOptions - ): Promise { - try { +async function _handleLinkExistErrorAsync( + linkFn: () => Promise, + options: IInternalFileSystemCreateLinkOptions, + error: Error +): Promise { + switch (options.alreadyExistsBehavior) { + case AlreadyExistsBehavior.Ignore: + break; + case AlreadyExistsBehavior.Overwrite: + // fsx.linkSync does not allow overwriting so we must manually delete. If it's + // a folder, it will throw an error. + await FileSystem.deleteFileAsync(options.newLinkPath); await linkFn(); - } catch (error) { - if (FileSystem.isExistError(error as Error)) { - // Link exists, handle it - switch (options.alreadyExistsBehavior) { - case AlreadyExistsBehavior.Ignore: - break; - case AlreadyExistsBehavior.Overwrite: - // fsx.linkSync does not allow overwriting so we must manually delete. If it's - // a folder, it will throw an error. - await this.deleteFileAsync(options.newLinkPath); - await linkFn(); - break; - case AlreadyExistsBehavior.Error: - default: - throw error; - } - } else { - // When attempting to create a link in a directory that does not exist, an ENOENT - // or ENOTDIR error is thrown, so we should ensure the directory exists before - // retrying. There are also cases where the target file must exist, so validate in - // those cases to avoid confusing the missing directory with the missing target file. - if ( - FileSystem.isNotExistError(error as Error) && - (!options.linkTargetMustExist || (await FileSystem.existsAsync(options.linkTargetPath))) - ) { - await this.ensureFolderAsync(nodeJsPath.dirname(options.newLinkPath)); + break; + case AlreadyExistsBehavior.Error: + default: + throw error; + } +} + +async function _handleLinkAsync( + linkFn: () => Promise, + options: IInternalFileSystemCreateLinkOptions +): Promise { + try { + await linkFn(); + } catch (error) { + if (FileSystem.isExistError(error as Error)) { + // Link exists, handle it + await _handleLinkExistErrorAsync(linkFn, options, error as Error); + } else { + // When attempting to create a link in a directory that does not exist, an ENOENT + // or ENOTDIR error is thrown, so we should ensure the directory exists before + // retrying. There are also cases where the target file must exist, so validate in + // those cases to avoid confusing the missing directory with the missing target file. + if ( + FileSystem.isNotExistError(error as Error) && + (!options.linkTargetMustExist || (await FileSystem.existsAsync(options.linkTargetPath))) + ) { + await FileSystem.ensureFolderAsync(nodeJsPath.dirname(options.newLinkPath)); + try { await linkFn(); - } else { - throw error; + } catch (retryError) { + if (FileSystem.isExistError(retryError as Error)) { + // Another concurrent process may have created the link between the ensureFolderAsync + // call and the retry; handle it the same way as the initial exist error. + await _handleLinkExistErrorAsync(linkFn, options, retryError as Error); + } else { + throw retryError; + } } + } else { + throw error; } } } +} - private static _wrapException(fn: () => TResult): TResult { - try { - return fn(); - } catch (error) { - FileSystem._updateErrorMessage(error as Error); - throw error; - } +function _wrapException(fn: () => TResult): TResult { + try { + return fn(); + } catch (error) { + _updateErrorMessage(error as Error); + throw error; } +} - private static async _wrapExceptionAsync(fn: () => Promise): Promise { - try { - return await fn(); - } catch (error) { - FileSystem._updateErrorMessage(error as Error); - throw error; - } +async function _wrapExceptionAsync(fn: () => Promise): Promise { + try { + return await fn(); + } catch (error) { + _updateErrorMessage(error as Error); + throw error; } +} - private static _updateErrorMessage(error: Error): void { - if (FileSystem.isErrnoException(error)) { - if (FileSystem.isFileDoesNotExistError(error)) { - // eslint-disable-line @typescript-eslint/no-use-before-define - error.message = `File does not exist: ${error.path}\n${error.message}`; - } else if (FileSystem.isFolderDoesNotExistError(error)) { - // eslint-disable-line @typescript-eslint/no-use-before-define - error.message = `Folder does not exist: ${error.path}\n${error.message}`; - } else if (FileSystem.isExistError(error)) { - // Oddly, the typing does not include the `dest` property even though the documentation - // indicates it is there: https://nodejs.org/docs/latest-v10.x/api/errors.html#errors_error_dest - const extendedError: NodeJS.ErrnoException & { dest?: string } = error; - // eslint-disable-line @typescript-eslint/no-use-before-define - error.message = `File or folder already exists: ${extendedError.dest}\n${error.message}`; - } else if (FileSystem.isUnlinkNotPermittedError(error)) { - // eslint-disable-line @typescript-eslint/no-use-before-define - error.message = `File or folder could not be deleted: ${error.path}\n${error.message}`; - } else if (FileSystem.isDirectoryError(error)) { - // eslint-disable-line @typescript-eslint/no-use-before-define - error.message = `Target is a folder, not a file: ${error.path}\n${error.message}`; - } else if (FileSystem.isNotDirectoryError(error)) { - // eslint-disable-line @typescript-eslint/no-use-before-define - error.message = `Target is not a folder: ${error.path}\n${error.message}`; - } +function _updateErrorMessage(error: Error): void { + if (FileSystem.isErrnoException(error)) { + if (FileSystem.isFileDoesNotExistError(error)) { + error.message = `File does not exist: ${error.path}\n${error.message}`; + } else if (FileSystem.isFolderDoesNotExistError(error)) { + error.message = `Folder does not exist: ${error.path}\n${error.message}`; + } else if (FileSystem.isExistError(error)) { + // Oddly, the typing does not include the `dest` property even though the documentation + // indicates it is there: https://nodejs.org/docs/latest-v10.x/api/errors.html#errors_error_dest + const extendedError: NodeJS.ErrnoException & { dest?: string } = error; + error.message = `File or folder already exists: ${extendedError.dest}\n${error.message}`; + } else if (FileSystem.isUnlinkNotPermittedError(error)) { + error.message = `File or folder could not be deleted: ${error.path}\n${error.message}`; + } else if (FileSystem.isDirectoryError(error)) { + error.message = `Target is a folder, not a file: ${error.path}\n${error.message}`; + } else if (FileSystem.isNotDirectoryError(error)) { + error.message = `Target is not a folder: ${error.path}\n${error.message}`; } } } diff --git a/libraries/node-core-library/src/FileWriter.ts b/libraries/node-core-library/src/FileWriter.ts index 988c4c7a480..70db42a79d4 100644 --- a/libraries/node-core-library/src/FileWriter.ts +++ b/libraries/node-core-library/src/FileWriter.ts @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { Import } from './Import'; +import * as fs from 'node:fs'; -const fsx: typeof import('fs-extra') = Import.lazy('fs-extra', require); +import type { FileSystemStats } from './FileSystem'; /** * Available file handle opening flags. @@ -11,6 +11,20 @@ const fsx: typeof import('fs-extra') = Import.lazy('fs-extra', require); */ type NodeFileFlags = 'r' | 'r+' | 'rs+' | 'w' | 'wx' | 'w+' | 'wx+' | 'a' | 'ax' | 'a+' | 'ax+'; +/** + * Helper function to convert the file writer array to a Node.js style string (e.g. "wx" or "a"). + * @param flags - The flags that should be converted. + */ +function convertFlagsForNode(flags: IFileWriterFlags | undefined): NodeFileFlags { + flags = { + append: false, + exclusive: false, + ...flags + }; + const result: NodeFileFlags = `${flags.append ? 'a' : 'w'}${flags.exclusive ? 'x' : ''}` as NodeFileFlags; + return result; +} + /** * Interface which represents the flags about which mode the file should be opened in. * @public @@ -59,20 +73,7 @@ export class FileWriter { * @param flags - The flags for opening the handle */ public static open(filePath: string, flags?: IFileWriterFlags): FileWriter { - return new FileWriter(fsx.openSync(filePath, FileWriter._convertFlagsForNode(flags)), filePath); - } - - /** - * Helper function to convert the file writer array to a Node.js style string (e.g. "wx" or "a"). - * @param flags - The flags that should be converted. - */ - private static _convertFlagsForNode(flags: IFileWriterFlags | undefined): NodeFileFlags { - flags = { - append: false, - exclusive: false, - ...flags - }; - return [flags.append ? 'a' : 'w', flags.exclusive ? 'x' : ''].join('') as NodeFileFlags; + return new FileWriter(fs.openSync(filePath, convertFlagsForNode(flags)), filePath); } /** @@ -85,7 +86,7 @@ export class FileWriter { throw new Error(`Cannot write to file, file descriptor has already been released.`); } - fsx.writeSync(this._fileDescriptor, text); + fs.writeSync(this._fileDescriptor, text); } /** @@ -99,7 +100,19 @@ export class FileWriter { const fd: number | undefined = this._fileDescriptor; if (fd) { this._fileDescriptor = undefined; - fsx.closeSync(fd); + fs.closeSync(fd); } } + + /** + * Gets the statistics for the given file handle. Throws if the file handle has been closed. + * Behind the scenes it uses `fs.statSync()`. + */ + public getStatistics(): FileSystemStats { + if (!this._fileDescriptor) { + throw new Error(`Cannot get file statistics, file descriptor has already been released.`); + } + + return fs.fstatSync(this._fileDescriptor); + } } diff --git a/libraries/node-core-library/src/IPackageJson.ts b/libraries/node-core-library/src/IPackageJson.ts index ab33d9a6f6f..74d2c062439 100644 --- a/libraries/node-core-library/src/IPackageJson.ts +++ b/libraries/node-core-library/src/IPackageJson.ts @@ -60,6 +60,80 @@ export interface IPeerDependenciesMetaTable { }; } +/** + * This interface is part of the {@link IPackageJson} file format. It is used for the + * "dependenciesMeta" field. + * @public + */ +export interface IDependenciesMetaTable { + [dependencyName: string]: { + injected?: boolean; + }; +} + +/** + * This interface is part of the {@link IPackageJson} file format. It is used for the values + * of the "exports" field. + * + * See {@link https://nodejs.org/api/packages.html#conditional-exports | Node.js documentation on Conditional Exports} and + * {@link https://nodejs.org/api/packages.html#community-conditions-definitions | Node.js documentation on Community Conditional Exports}. + * + * @public + */ +export interface IPackageJsonExports { + /** + * This export is like {@link IPackageJsonExports.node} in that it matches for any NodeJS environment. + * This export is specifically for native C++ addons. + */ + 'node-addons'?: string | IPackageJsonExports; + + /** + * This export matches for any NodeJS environment. + */ + node?: string | IPackageJsonExports; + + /** + * This export matches when loaded via ESM syntax (i.e. - `import '...'` or `import('...')`). + * This is always mutually exclusive with {@link IPackageJsonExports.require}. + */ + import?: string | IPackageJsonExports; + + /** + * This export matches when loaded via `require()`. + * This is always mutually exclusive with {@link IPackageJsonExports.import}. + */ + require?: string | IPackageJsonExports; + + /** + * This export matches as a fallback when no other conditions match. Because exports are evaluated in + * the order that they are specified in the `package.json` file, this condition should always come last + * as no later exports will match if this one does. + */ + default?: string | IPackageJsonExports; + + /** + * This export matches when loaded by the typing system (i.e. - the TypeScript compiler). + */ + types?: string | IPackageJsonExports; + + /** + * Any web browser environment. + */ + browser?: string | IPackageJsonExports; + + /** + * This export matches in development-only environments. + * This is always mutually exclusive with {@link IPackageJsonExports.production}. + */ + development?: string | IPackageJsonExports; + + /** + * This export matches in production-only environments. + * This is always mutually exclusive with {@link IPackageJsonExports.development}. + */ + production?: string | IPackageJsonExports; +} + /** * An interface for accessing common fields from a package.json file whose version field may be missing. * @@ -142,7 +216,7 @@ export interface INodePackageJson { /** * The main entry point for the package. */ - bin?: string; + bin?: string | Record; /** * An array of dependencies that must always be installed for this package. @@ -166,6 +240,12 @@ export interface INodePackageJson { */ peerDependencies?: IPackageJsonDependencyTable; + /** + * An array of metadata for dependencies declared inside dependencies, optionalDependencies, and devDependencies. + * https://pnpm.io/package_json#dependenciesmeta + */ + dependenciesMeta?: IDependenciesMetaTable; + /** * An array of metadata about peer dependencies. */ @@ -184,6 +264,57 @@ export interface INodePackageJson { * | 0000-selective-versions-resolutions.md RFC} for details. */ resolutions?: Record; + + /** + * A table of TypeScript *.d.ts file paths that are compatible with specific TypeScript version + * selectors. This data take a form similar to that of the {@link INodePackageJson.exports} field, + * with fallbacks listed in order in the value array for example: + * + * ```JSON + * "typesVersions": { + * ">=3.1": { + * "*": ["./types-3.1/*", "./types-3.1-fallback/*"] + * }, + * ">=3.0": { + * "*": ["./types-legacy/*"] + * } + * } + * ``` + * + * or + * + * ```JSON + * "typesVersions": { + * ">=3.1": { + * "app/*": ["./app/types-3.1/*"], + * "lib/*": ["./lib/types-3.1/*"] + * }, + * ">=3.0": { + * "app/*": ["./app/types-legacy/*"], + * "lib/*": ["./lib/types-legacy/*"] + * } + * } + * ``` + * + * See the + * {@link https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#version-selection-with-typesversions + * | TypeScript documentation} for details. + */ + typesVersions?: Record>; + + /** + * The "exports" field is used to specify the entry points for a package. + * See {@link https://nodejs.org/api/packages.html#exports | Node.js documentation} + */ + // eslint-disable-next-line @rushstack/no-new-null + exports?: string | string[] | Record; + + /** + * The "files" field is an array of file globs that should be included in the package during publishing. + * + * See the {@link https://docs.npmjs.com/cli/v6/configuring-npm/package-json#files | NPM documentation}. + */ + files?: string[]; } /** diff --git a/libraries/node-core-library/src/Import.ts b/libraries/node-core-library/src/Import.ts index a67e86c5e58..029fbe104e4 100644 --- a/libraries/node-core-library/src/Import.ts +++ b/libraries/node-core-library/src/Import.ts @@ -1,14 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; +import nodeModule = require('module'); + import importLazy = require('import-lazy'); import * as Resolve from 'resolve'; -import nodeModule = require('module'); import { PackageJsonLookup } from './PackageJsonLookup'; import { FileSystem } from './FileSystem'; -import { IPackageJson } from './IPackageJson'; +import type { IPackageJson } from './IPackageJson'; import { PackageName } from './PackageName'; type RealpathFnType = Parameters[1]['realpath']; @@ -121,6 +122,15 @@ export interface IImportResolvePackageOptions extends IImportResolveOptions { * The package name to resolve. For example "\@rushstack/node-core-library" */ packageName: string; + + /** + * If true, then the module path will be resolved using Node.js's built-in resolution algorithm. + * + * @remarks + * This allows reusing Node's built-in resolver cache. + * This implies `allowSelfReference: true`. The passed `getRealPath` will only be used on `baseFolderPath`. + */ + useNodeJSResolver?: boolean; } /** @@ -139,20 +149,20 @@ interface IPackageDescriptor { packageName: string; } +let _builtInModules: Set | undefined; +function _getBuiltInModules(): Set { + if (!_builtInModules) { + _builtInModules = new Set(nodeModule.builtinModules); + } + + return _builtInModules; +} + /** * Helpers for resolving and importing Node.js modules. * @public */ export class Import { - private static __builtInModules: Set | undefined; - private static get _builtInModules(): Set { - if (!Import.__builtInModules) { - Import.__builtInModules = new Set(nodeModule.builtinModules); - } - - return Import.__builtInModules; - } - /** * Provides a way to improve process startup times by lazy-loading imported modules. * @@ -271,12 +281,12 @@ export class Import { // against the first path segment const slashIndex: number = modulePath.indexOf('/'); const moduleName: string = slashIndex === -1 ? modulePath : modulePath.slice(0, slashIndex); - if (!includeSystemModules && Import._builtInModules.has(moduleName)) { + if (!includeSystemModules && _getBuiltInModules().has(moduleName)) { throw new Error(`Cannot find module "${modulePath}" from "${options.baseFolderPath}".`); } if (allowSelfReference === true) { - const ownPackage: IPackageDescriptor | undefined = Import._getPackageName(baseFolderPath); + const ownPackage: IPackageDescriptor | undefined = _getPackageName(normalizedRootPath); if ( ownPackage && (modulePath === ownPackage.packageName || modulePath.startsWith(`${ownPackage.packageName}/`)) @@ -292,8 +302,8 @@ export class Import { preserveSymlinks: false, realpathSync: getRealPath }); - } catch (e) { - throw new Error(`Cannot find module "${modulePath}" from "${options.baseFolderPath}".`); + } catch (e: unknown) { + throw new Error(`Cannot find module "${modulePath}" from "${options.baseFolderPath}": ${e}`); } } @@ -327,12 +337,12 @@ export class Import { // against the first path segment const slashIndex: number = modulePath.indexOf('/'); const moduleName: string = slashIndex === -1 ? modulePath : modulePath.slice(0, slashIndex); - if (!includeSystemModules && Import._builtInModules.has(moduleName)) { + if (!includeSystemModules && _getBuiltInModules().has(moduleName)) { throw new Error(`Cannot find module "${modulePath}" from "${options.baseFolderPath}".`); } if (allowSelfReference === true) { - const ownPackage: IPackageDescriptor | undefined = Import._getPackageName(baseFolderPath); + const ownPackage: IPackageDescriptor | undefined = _getPackageName(normalizedRootPath); if ( ownPackage && (modulePath === ownPackage.packageName || modulePath.startsWith(`${ownPackage.packageName}/`)) @@ -383,8 +393,8 @@ export class Import { } ); return await resolvePromise; - } catch (e) { - throw new Error(`Cannot find module "${modulePath}" from "${options.baseFolderPath}".`); + } catch (e: unknown) { + throw new Error(`Cannot find module "${modulePath}" from "${options.baseFolderPath}": ${e}`); } } @@ -409,16 +419,23 @@ export class Import { * and a system module is found, then its name is returned without any file path. */ public static resolvePackage(options: IImportResolvePackageOptions): string { - const { packageName, includeSystemModules, baseFolderPath, allowSelfReference, getRealPath } = options; + const { + packageName, + includeSystemModules, + baseFolderPath, + allowSelfReference, + getRealPath, + useNodeJSResolver + } = options; - if (includeSystemModules && Import._builtInModules.has(packageName)) { + if (includeSystemModules && _getBuiltInModules().has(packageName)) { return packageName; } const normalizedRootPath: string = (getRealPath || FileSystem.getRealPath)(baseFolderPath); if (allowSelfReference) { - const ownPackage: IPackageDescriptor | undefined = Import._getPackageName(baseFolderPath); + const ownPackage: IPackageDescriptor | undefined = _getPackageName(normalizedRootPath); if (ownPackage && ownPackage.packageName === packageName) { return ownPackage.packageRootPath; } @@ -427,25 +444,22 @@ export class Import { PackageName.parse(packageName); // Ensure the package name is valid and doesn't contain a path try { - // Append a slash to the package name to ensure `resolve.sync` doesn't attempt to return a system package - const resolvedPath: string = Resolve.sync(`${packageName}/`, { - basedir: normalizedRootPath, - preserveSymlinks: false, - packageFilter: (pkg: Resolve.PackageJSON, pkgFile: string, dir: string): Resolve.PackageJSON => { - // Hardwire "main" to point to a file that is guaranteed to exist. - // This helps resolve packages such as @types/node that have no entry point. - // And then we can use path.dirname() below to locate the package folder, - // even if the real entry point was in an subfolder with arbitrary nesting. - pkg.main = 'package.json'; - return pkg; - }, - realpathSync: getRealPath - }); + const resolvedPath: string = useNodeJSResolver + ? require.resolve(`${packageName}/package.json`, { + paths: [normalizedRootPath] + }) + : // Append `/package.json` to ensure `resolve.sync` doesn't attempt to return a system package, and to avoid + // having to mess with the `packageFilter` option. + Resolve.sync(`${packageName}/package.json`, { + basedir: normalizedRootPath, + preserveSymlinks: false, + realpathSync: getRealPath + }); const packagePath: string = path.dirname(resolvedPath); return packagePath; - } catch { - throw new Error(`Cannot find package "${packageName}" from "${baseFolderPath}".`); + } catch (e: unknown) { + throw new Error(`Cannot find package "${packageName}" from "${baseFolderPath}": ${e}.`); } } @@ -462,7 +476,7 @@ export class Import { getRealPathAsync } = options; - if (includeSystemModules && Import._builtInModules.has(packageName)) { + if (includeSystemModules && _getBuiltInModules().has(packageName)) { return packageName; } @@ -471,7 +485,7 @@ export class Import { ); if (allowSelfReference) { - const ownPackage: IPackageDescriptor | undefined = Import._getPackageName(baseFolderPath); + const ownPackage: IPackageDescriptor | undefined = _getPackageName(normalizedRootPath); if (ownPackage && ownPackage.packageName === packageName) { return ownPackage.packageRootPath; } @@ -501,23 +515,12 @@ export class Import { : undefined; Resolve.default( - // Append a slash to the package name to ensure `resolve` doesn't attempt to return a system package - `${packageName}/`, + // Append `/package.json` to ensure `resolve` doesn't attempt to return a system package, and to avoid + // having to mess with the `packageFilter` option. + `${packageName}/package.json`, { basedir: normalizedRootPath, preserveSymlinks: false, - packageFilter: ( - pkg: Resolve.PackageJSON, - pkgFile: string, - dir: string - ): Resolve.PackageJSON => { - // Hardwire "main" to point to a file that is guaranteed to exist. - // This helps resolve packages such as @types/node that have no entry point. - // And then we can use path.dirname() below to locate the package folder, - // even if the real entry point was in an subfolder with arbitrary nesting. - pkg.main = 'package.json'; - return pkg; - }, realpath: realPathFn }, (error: Error | null, resolvedPath?: string) => { @@ -536,22 +539,22 @@ export class Import { const packagePath: string = path.dirname(resolvedPath); return packagePath; - } catch { - throw new Error(`Cannot find package "${packageName}" from "${baseFolderPath}".`); + } catch (e: unknown) { + throw new Error(`Cannot find package "${packageName}" from "${baseFolderPath}": ${e}`); } } +} - private static _getPackageName(rootPath: string): IPackageDescriptor | undefined { - const packageJsonPath: string | undefined = - PackageJsonLookup.instance.tryGetPackageJsonFilePathFor(rootPath); - if (packageJsonPath) { - const packageJson: IPackageJson = PackageJsonLookup.instance.loadPackageJson(packageJsonPath); - return { - packageRootPath: path.dirname(packageJsonPath), - packageName: packageJson.name - }; - } else { - return undefined; - } +function _getPackageName(rootPath: string): IPackageDescriptor | undefined { + const packageJsonPath: string | undefined = + PackageJsonLookup.instance.tryGetPackageJsonFilePathFor(rootPath); + if (packageJsonPath) { + const packageJson: IPackageJson = PackageJsonLookup.instance.loadPackageJson(packageJsonPath); + return { + packageRootPath: path.dirname(packageJsonPath), + packageName: packageJson.name + }; + } else { + return undefined; } } diff --git a/libraries/node-core-library/src/InternalError.ts b/libraries/node-core-library/src/InternalError.ts index 8fb67df1979..84ba4d03621 100644 --- a/libraries/node-core-library/src/InternalError.ts +++ b/libraries/node-core-library/src/InternalError.ts @@ -34,7 +34,7 @@ export class InternalError extends Error { * explaining that the user has encountered a software defect. */ public constructor(message: string) { - super(InternalError._formatMessage(message)); + super(_formatMessage(message)); // Manually set the prototype, as we can no longer extend built-in classes like Error, Array, Map, etc. // https://github.com/microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work @@ -50,15 +50,14 @@ export class InternalError extends Error { } } - private static _formatMessage(unformattedMessage: string): string { - return ( - `Internal Error: ${unformattedMessage}\n\nYou have encountered a software defect. Please consider` + - ` reporting the issue to the maintainers of this application.` - ); - } - - /** @override */ - public toString(): string { + public override toString(): string { return this.message; // Avoid adding the "Error:" prefix } } + +function _formatMessage(unformattedMessage: string): string { + return ( + `Internal Error: ${unformattedMessage}\n\nYou have encountered a software defect. Please consider` + + ` reporting the issue to the maintainers of this application.` + ); +} diff --git a/libraries/node-core-library/src/JsonFile.ts b/libraries/node-core-library/src/JsonFile.ts index 4c294147c70..151edd6d366 100644 --- a/libraries/node-core-library/src/JsonFile.ts +++ b/libraries/node-core-library/src/JsonFile.ts @@ -1,11 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as os from 'os'; +import * as os from 'node:os'; + import * as jju from 'jju'; -import { JsonSchema, IJsonSchemaErrorInfo, IJsonSchemaValidateOptions } from './JsonSchema'; -import { Text, NewlineKind } from './Text'; +import type { JsonSchema, IJsonSchemaErrorInfo, IJsonSchemaValidateOptions } from './JsonSchema'; +import { Text, type NewlineKind } from './Text'; import { FileSystem } from './FileSystem'; /** @@ -90,7 +91,7 @@ export enum JsonSyntax { * Files using this format should use the `.json5` file extension instead of `.json`. * * JSON5 has substantial differences from JSON: object keys may be unquoted, trailing commas - * are allowed, and strings may span multiple lines. Whereas `JsonSyntax.JsonWithComments` can + * are allowed, and strings may span multiple lines. Whereas {@link JsonSyntax.JsonWithComments} can * be cheaply converted to standard JSON by stripping comments, parsing JSON5 requires a * nontrivial algorithm that may not be easily available in some contexts or programming languages. * @@ -109,7 +110,7 @@ export interface IJsonFileParseOptions { * Specifies the variant of JSON syntax to be used. * * @defaultValue - * `JsonSyntax.Json5` + * {@link JsonSyntax.Json5} * * NOTE: This default will be changed to `JsonSyntax.JsonWithComments` in a future release. */ @@ -128,15 +129,16 @@ export interface IJsonFileLoadAndValidateOptions extends IJsonFileParseOptions, * * @public */ -export interface IJsonFileStringifyOptions { +export interface IJsonFileStringifyOptions extends IJsonFileParseOptions { /** * If provided, the specified newline type will be used instead of the default `\r\n`. */ newlineConversion?: NewlineKind; /** - * By default, `JsonFile.stringify()` validates that the object does not contain any - * keys whose value is `undefined`. To disable this validation, set `ignoreUndefinedValues=true` + * By default, {@link JsonFile.stringify} validates that the object does not contain any + * keys whose value is `undefined`. To disable this validation, set + * {@link IJsonFileStringifyOptions.ignoreUndefinedValues} to `true` * which causes such keys to be silently discarded, consistent with the system `JSON.stringify()`. * * @remarks @@ -146,8 +148,9 @@ export interface IJsonFileStringifyOptions { * as `undefined`, because it is the default value of missing/uninitialized variables. * (In practice, distinguishing "null" versus "uninitialized" has more drawbacks than benefits.) * This poses a problem when serializing ECMAScript objects that contain `undefined` members. - * As a safeguard, `JsonFile` will report an error if any `undefined` values are encountered - * during serialization. Set `ignoreUndefinedValues=true` to disable this safeguard. + * As a safeguard, {@link JsonFile} will report an error if any `undefined` values are encountered + * during serialization. Set {@link IJsonFileStringifyOptions.ignoreUndefinedValues} to `true` + * to disable this safeguard. */ ignoreUndefinedValues?: boolean; @@ -212,7 +215,7 @@ export class JsonFile { public static load(jsonFilename: string, options?: IJsonFileParseOptions): JsonObject { try { const contents: string = FileSystem.readFile(jsonFilename); - const parseOptions: jju.ParseOptions = JsonFile._buildJjuParseOptions(options); + const parseOptions: jju.ParseOptions = _buildJjuParseOptions(options); return jju.parse(contents, parseOptions); } catch (error) { if (FileSystem.isNotExistError(error as Error)) { @@ -233,7 +236,7 @@ export class JsonFile { public static async loadAsync(jsonFilename: string, options?: IJsonFileParseOptions): Promise { try { const contents: string = await FileSystem.readFileAsync(jsonFilename); - const parseOptions: jju.ParseOptions = JsonFile._buildJjuParseOptions(options); + const parseOptions: jju.ParseOptions = _buildJjuParseOptions(options); return jju.parse(contents, parseOptions); } catch (error) { if (FileSystem.isNotExistError(error as Error)) { @@ -252,7 +255,7 @@ export class JsonFile { * Parses a JSON file's contents. */ public static parseString(jsonContents: string, options?: IJsonFileParseOptions): JsonObject { - const parseOptions: jju.ParseOptions = JsonFile._buildJjuParseOptions(options); + const parseOptions: jju.ParseOptions = _buildJjuParseOptions(options); return jju.parse(jsonContents, parseOptions); } @@ -328,53 +331,63 @@ export class JsonFile { /** * Serializes the specified JSON object to a string buffer. - * @param jsonObject - the object to be serialized + * @param previousJson - the previous JSON string, which will be updated + * @param newJsonObject - the object to be serialized * @param options - other settings that control serialization * @returns a JSON string, with newlines, and indented with two spaces */ public static updateString( previousJson: string, newJsonObject: JsonObject, - options?: IJsonFileStringifyOptions + options: IJsonFileStringifyOptions = {} ): string { - if (!options) { - options = {}; - } - if (!options.ignoreUndefinedValues) { // Standard handling of `undefined` in JSON stringification is to discard the key. JsonFile.validateNoUndefinedMembers(newJsonObject); } + let explicitMode: 'json5' | 'json' | 'cjson' | undefined = undefined; + switch (options.jsonSyntax) { + case JsonSyntax.Strict: + explicitMode = 'json'; + break; + case JsonSyntax.JsonWithComments: + explicitMode = 'cjson'; + break; + case JsonSyntax.Json5: + explicitMode = 'json5'; + break; + } + let stringified: string; if (previousJson !== '') { // NOTE: We don't use mode=json here because comments aren't allowed by strict JSON stringified = jju.update(previousJson, newJsonObject, { - mode: 'cjson', + mode: explicitMode ?? JsonSyntax.Json5, indent: 2 }); } else if (options.prettyFormatting) { stringified = jju.stringify(newJsonObject, { - mode: 'json', + mode: explicitMode ?? 'json', indent: 2 }); if (options.headerComment !== undefined) { - stringified = JsonFile._formatJsonHeaderComment(options.headerComment) + stringified; + stringified = _formatJsonHeaderComment(options.headerComment) + stringified; } } else { stringified = JSON.stringify(newJsonObject, undefined, 2); if (options.headerComment !== undefined) { - stringified = JsonFile._formatJsonHeaderComment(options.headerComment) + stringified; + stringified = _formatJsonHeaderComment(options.headerComment) + stringified; } } // Add the trailing newline stringified = Text.ensureTrailingNewline(stringified); - if (options && options.newlineConversion) { + if (options.newlineConversion) { stringified = Text.convertTo(stringified, options.newlineConversion); } @@ -388,11 +401,11 @@ export class JsonFile { * @param options - other settings that control how the file is saved * @returns false if ISaveJsonFileOptions.onlyIfChanged didn't save anything; true otherwise */ - public static save(jsonObject: JsonObject, jsonFilename: string, options?: IJsonFileSaveOptions): boolean { - if (!options) { - options = {}; - } - + public static save( + jsonObject: JsonObject, + jsonFilename: string, + options: IJsonFileSaveOptions = {} + ): boolean { // Do we need to read the previous file contents? let oldBuffer: Buffer | undefined = undefined; if (options.updateExistingFile || options.onlyIfChanged) { @@ -445,12 +458,8 @@ export class JsonFile { public static async saveAsync( jsonObject: JsonObject, jsonFilename: string, - options?: IJsonFileSaveOptions + options: IJsonFileSaveOptions = {} ): Promise { - if (!options) { - options = {}; - } - // Do we need to read the previous file contents? let oldBuffer: Buffer | undefined = undefined; if (options.updateExistingFile || options.onlyIfChanged) { @@ -502,97 +511,97 @@ export class JsonFile { * are any undefined members. */ public static validateNoUndefinedMembers(jsonObject: JsonObject): void { - return JsonFile._validateNoUndefinedMembers(jsonObject, []); + return _validateNoUndefinedMembers(jsonObject, []); } +} - // Private implementation of validateNoUndefinedMembers() - private static _validateNoUndefinedMembers(jsonObject: JsonObject, keyPath: string[]): void { - if (!jsonObject) { - return; - } - if (typeof jsonObject === 'object') { - for (const key of Object.keys(jsonObject)) { - keyPath.push(key); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const value: any = jsonObject[key]; - if (value === undefined) { - const fullPath: string = JsonFile._formatKeyPath(keyPath); - throw new Error(`The value for ${fullPath} is "undefined" and cannot be serialized as JSON`); - } - - JsonFile._validateNoUndefinedMembers(value, keyPath); - keyPath.pop(); - } - } +// Private implementation of validateNoUndefinedMembers() +function _validateNoUndefinedMembers(jsonObject: JsonObject, keyPath: string[]): void { + if (!jsonObject) { + return; } - - // Given this input: ['items', '4', 'syntax', 'parameters', 'string "with" symbols", 'type'] - // Return this string: items[4].syntax.parameters["string \"with\" symbols"].type - private static _formatKeyPath(keyPath: string[]): string { - let result: string = ''; - - for (const key of keyPath) { - if (/^[0-9]+$/.test(key)) { - // It's an integer, so display like this: parent[123] - result += `[${key}]`; - } else if (/^[a-z_][a-z_0-9]*$/i.test(key)) { - // It's an alphanumeric identifier, so display like this: parent.name - if (result) { - result += '.'; - } - result += `${key}`; - } else { - // It's a freeform string, so display like this: parent["A path: \"C:\\file\""] - - // Convert this: A path: "C:\file" - // To this: A path: \"C:\\file\" - const escapedKey: string = key - .replace(/[\\]/g, '\\\\') // escape backslashes - .replace(/["]/g, '\\'); // escape quotes - result += `["${escapedKey}"]`; + if (typeof jsonObject === 'object') { + for (const key of Object.keys(jsonObject)) { + keyPath.push(key); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const value: any = jsonObject[key]; + if (value === undefined) { + const fullPath: string = _formatKeyPath(keyPath); + throw new Error(`The value for ${fullPath} is "undefined" and cannot be serialized as JSON`); } + + _validateNoUndefinedMembers(value, keyPath); + keyPath.pop(); } - return result; } +} - private static _formatJsonHeaderComment(headerComment: string): string { - if (headerComment === '') { - return ''; - } - const lines: string[] = headerComment.split('\n'); - const result: string[] = []; - for (const line of lines) { - if (!/^\s*$/.test(line) && !/^\s*\/\//.test(line)) { - throw new Error( - 'The headerComment lines must be blank or start with the "//" prefix.\n' + - 'Invalid line' + - JSON.stringify(line) - ); +// Given this input: ['items', '4', 'syntax', 'parameters', 'string "with" symbols", 'type'] +// Return this string: items[4].syntax.parameters["string \"with\" symbols"].type +function _formatKeyPath(keyPath: string[]): string { + let result: string = ''; + + for (const key of keyPath) { + if (/^[0-9]+$/.test(key)) { + // It's an integer, so display like this: parent[123] + result += `[${key}]`; + } else if (/^[a-z_][a-z_0-9]*$/i.test(key)) { + // It's an alphanumeric identifier, so display like this: parent.name + if (result) { + result += '.'; } - result.push(Text.replaceAll(line, '\r', '')); + result += `${key}`; + } else { + // It's a freeform string, so display like this: parent["A path: \"C:\\file\""] + + // Convert this: A path: "C:\file" + // To this: A path: \"C:\\file\" + const escapedKey: string = key + .replace(/[\\]/g, '\\\\') // escape backslashes + .replace(/["]/g, '\\'); // escape quotes + result += `["${escapedKey}"]`; } - return lines.join('\n') + '\n'; } + return result; +} - private static _buildJjuParseOptions(options: IJsonFileParseOptions | undefined): jju.ParseOptions { - if (!options) { - options = {}; - } - const parseOptions: jju.ParseOptions = {}; - switch (options.jsonSyntax) { - case JsonSyntax.Strict: - parseOptions.mode = 'json'; - break; - case JsonSyntax.JsonWithComments: - parseOptions.mode = 'cjson'; - break; - case JsonSyntax.Json5: - default: - parseOptions.mode = 'json5'; - break; +function _formatJsonHeaderComment(headerComment: string): string { + if (headerComment === '') { + return ''; + } + const lines: string[] = headerComment.split('\n'); + const result: string[] = []; + for (const line of lines) { + if (!/^\s*$/.test(line) && !/^\s*\/\//.test(line)) { + throw new Error( + 'The headerComment lines must be blank or start with the "//" prefix.\n' + + 'Invalid line' + + JSON.stringify(line) + ); } + result.push(Text.replaceAll(line, '\r', '')); + } + return lines.join('\n') + '\n'; +} - return parseOptions; +function _buildJjuParseOptions(options: IJsonFileParseOptions = {}): jju.ParseOptions { + const parseOptions: jju.ParseOptions = { + reserved_keys: 'replace' + }; + + switch (options.jsonSyntax) { + case JsonSyntax.Strict: + parseOptions.mode = 'json'; + break; + case JsonSyntax.JsonWithComments: + parseOptions.mode = 'cjson'; + break; + case JsonSyntax.Json5: + default: + parseOptions.mode = 'json5'; + break; } + + return parseOptions; } diff --git a/libraries/node-core-library/src/JsonSchema.ts b/libraries/node-core-library/src/JsonSchema.ts index d7683bcb1f4..8864740ef41 100644 --- a/libraries/node-core-library/src/JsonSchema.ts +++ b/libraries/node-core-library/src/JsonSchema.ts @@ -1,40 +1,102 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as os from 'os'; -import * as path from 'path'; +import * as os from 'node:os'; +import * as path from 'node:path'; -import { JsonFile, JsonObject } from './JsonFile'; +import Ajv, { type Options as AjvOptions, type ErrorObject, type ValidateFunction } from 'ajv'; +import AjvDraft04 from 'ajv-draft-04'; +import addFormats from 'ajv-formats'; + +import { JsonFile, type JsonObject } from './JsonFile'; import { FileSystem } from './FileSystem'; -import type ValidatorType from 'z-schema'; -const Validator: typeof import('z-schema') = require('z-schema/dist/ZSchema-browser-min'); +/** + * Pattern matching JSON Schema vendor extension keywords in the form `x--`, + * where `` is alphanumeric and `` is kebab-case alphanumeric. + * @example `x-tsdoc-release-tag`, `x-myvendor-description` + */ +const VENDOR_EXTENSION_KEY_PATTERN: RegExp = /^x-[a-z0-9]+-[a-z0-9]+(-[a-z0-9]+)*$/; + +/** + * Collects top-level property keys from a JSON object that match the vendor extension + * pattern `x--`. Only root-level keys are inspected for performance. + */ +function _collectVendorExtensionKeywords(obj: unknown, keywords: Set): void { + if (typeof obj === 'object' && obj !== null && !Array.isArray(obj)) { + for (const key of Object.keys(obj as Record)) { + if (VENDOR_EXTENSION_KEY_PATTERN.test(key)) { + keywords.add(key); + } + } + } +} interface ISchemaWithId { + // draft-04 uses "id" id: string | undefined; + // draft-06 and higher uses "$id" + $id: string | undefined; } /** - * Callback function arguments for JsonSchema.validateObjectWithCallback(); + * Specifies the version of json-schema to be validated against. + * https://json-schema.org/specification + * @public + */ +export type JsonSchemaVersion = 'draft-04' | 'draft-07'; + +/** + * A definition for a custom format to consider during validation. + * @public + */ +export interface IJsonSchemaCustomFormat { + /** + * The base JSON type. + */ + type: T extends string ? 'string' : T extends number ? 'number' : never; + + /** + * A validation function for the format. + * @param data - The raw field data to validate. + * @returns whether the data is valid according to the format. + */ + validate: (data: T) => boolean; +} + +/** + * Callback function arguments for {@link JsonSchema.validateObjectWithCallback} * @public */ export interface IJsonSchemaErrorInfo { /** - * The z-schema error tree, formatted as an indented text string. + * The ajv error list, formatted as an indented text string. */ details: string; } /** - * Options for JsonSchema.validateObject() + * Options for {@link JsonSchema.validateObjectWithCallback} * @public */ -export interface IJsonSchemaValidateOptions { +export interface IJsonSchemaValidateObjectWithOptions { + /** + * If true, the root-level `$schema` property in a JSON object being validated will be ignored during validation. + * If this is set to `true` and the schema requires a `$schema` property, validation will fail. + */ + ignoreSchemaField?: boolean; +} + +/** + * Options for {@link JsonSchema.validateObject} + * @public + */ +export interface IJsonSchemaValidateOptions extends IJsonSchemaValidateObjectWithOptions { /** * A custom header that will be used to report schema errors. * @remarks * If omitted, the default header is "JSON validation failed:". The error message starts with - * the header, followed by the full input filename, followed by the z-schema error tree. + * the header, followed by the full input filename, followed by the ajv error list. * If you wish to customize all aspects of the error message, use JsonFile.loadAndValidateWithCallback() * or JsonSchema.validateObjectWithCallback(). */ @@ -42,15 +104,15 @@ export interface IJsonSchemaValidateOptions { } /** - * Options for JsonSchema.fromFile() + * Options for {@link JsonSchema.fromFile} and {@link JsonSchema.fromLoadedObject} * @public */ -export interface IJsonSchemaFromFileOptions { +export interface IJsonSchemaLoadOptions { /** * Other schemas that this schema references, e.g. via the "$ref" directive. * @remarks * The tree of dependent schemas may reference the same schema more than once. - * However, if the same schema "id" is used by two different JsonSchema instances, + * However, if the same schema "$id" is used by two different JsonSchema instances, * an error will be reported. This means you cannot load the same filename twice * and use them both together, and you cannot have diamond dependencies on different * versions of the same schema. Although technically this would be possible to support, @@ -59,6 +121,74 @@ export interface IJsonSchemaFromFileOptions { * JsonSchema also does not allow circular references between schema dependencies. */ dependentSchemas?: JsonSchema[]; + + /** + * The json-schema version to target for validation. + * + * @defaultValue draft-07 + * + * @remarks + * If the a version is not explicitly set, the schema object's `$schema` property + * will be inspected to determine the version. If a `$schema` property is not found + * or does not match an expected URL, the default version will be used. + */ + schemaVersion?: JsonSchemaVersion; + + /** + * Any custom formats to consider during validation. Some standard formats are supported + * out-of-the-box (e.g. emails, uris), but additional formats can be defined here. You could + * for example define generic numeric formats (e.g. uint8) or domain-specific formats. + */ + customFormats?: Record | IJsonSchemaCustomFormat>; + + /** + * If true, the AJV validator will reject JSON Schema vendor extension keywords + * matching the pattern `x--` as unknown keywords. + * + * @remarks + * The JSON Schema specification allows vendor-specific extensions using the `x-` prefix. + * For example, `x-tsdoc-release-tag` is used by `@rushstack/heft-json-schema-typings-plugin`. + * Other tools may define their own extensions such as `x-myvendor-html-description`. + * + * By default, the schema tree is scanned for any keys matching the `x--` + * pattern, and those keys are registered as custom AJV keywords so that strict mode validation + * succeeds. Set this option to `true` to disable this behavior and treat vendor extension + * keywords as unknown (which causes AJV strict mode to reject them). + * + * @defaultValue false + * @beta + */ + rejectVendorExtensionKeywords?: boolean; +} + +/** + * Options for {@link JsonSchema.fromFile} + * @public + */ +export type IJsonSchemaFromFileOptions = IJsonSchemaLoadOptions; + +/** + * Options for {@link JsonSchema.fromLoadedObject} + * @public + */ +export type IJsonSchemaFromObjectOptions = IJsonSchemaLoadOptions; + +const JSON_SCHEMA_URL_PREFIX_BY_JSON_SCHEMA_VERSION: Map = new Map([ + ['draft-04', 'http://json-schema.org/draft-04/schema'], + ['draft-07', 'http://json-schema.org/draft-07/schema'] +]); + +/** + * Helper function to determine the json-schema version to target for validation. + */ +function _inferJsonSchemaVersion({ $schema }: JsonObject): JsonSchemaVersion | undefined { + if ($schema) { + for (const [jsonSchemaVersion, urlPrefix] of JSON_SCHEMA_URL_PREFIX_BY_JSON_SCHEMA_VERSION) { + if ($schema.startsWith(urlPrefix)) { + return jsonSchemaVersion; + } + } + } } /** @@ -73,8 +203,13 @@ export interface IJsonSchemaFromFileOptions { export class JsonSchema { private _dependentSchemas: JsonSchema[] = []; private _filename: string = ''; - private _validator: ValidatorType | undefined = undefined; + private _validator: ValidateFunction | undefined = undefined; private _schemaObject: JsonObject | undefined = undefined; + private _schemaVersion: JsonSchemaVersion | undefined = undefined; + private _customFormats: + | Record | IJsonSchemaCustomFormat> + | undefined = undefined; + private _rejectVendorExtensionKeywords: boolean = false; private constructor() {} @@ -96,104 +231,38 @@ export class JsonSchema { if (options) { schema._dependentSchemas = options.dependentSchemas || []; + schema._schemaVersion = options.schemaVersion; + schema._customFormats = options.customFormats; + schema._rejectVendorExtensionKeywords = options.rejectVendorExtensionKeywords ?? false; } return schema; } /** - * Registers a JsonSchema that will be loaded from a file on disk. - * @remarks - * NOTE: An error occurs if the file does not exist; however, the file itself is not loaded or validated - * until it the schema is actually used. + * Registers a JsonSchema that will be loaded from an object. */ - public static fromLoadedObject(schemaObject: JsonObject): JsonSchema { + public static fromLoadedObject( + schemaObject: JsonObject, + options?: IJsonSchemaFromObjectOptions + ): JsonSchema { const schema: JsonSchema = new JsonSchema(); schema._schemaObject = schemaObject; - return schema; - } - - private static _collectDependentSchemas( - collectedSchemas: JsonSchema[], - dependentSchemas: JsonSchema[], - seenObjects: Set, - seenIds: Set - ): void { - for (const dependentSchema of dependentSchemas) { - // It's okay for the same schema to appear multiple times in the tree, but we only process it once - if (seenObjects.has(dependentSchema)) { - continue; - } - seenObjects.add(dependentSchema); - - const schemaId: string = dependentSchema._ensureLoaded(); - if (schemaId === '') { - throw new Error( - `This schema ${dependentSchema.shortName} cannot be referenced` + - ' because is missing the "id" field' - ); - } - if (seenIds.has(schemaId)) { - throw new Error( - `This schema ${dependentSchema.shortName} has the same "id" as another schema in this set` - ); - } - - seenIds.add(schemaId); - - collectedSchemas.push(dependentSchema); - JsonSchema._collectDependentSchemas( - collectedSchemas, - dependentSchema._dependentSchemas, - seenObjects, - seenIds - ); - } - } - - /** - * Used to nicely format the ZSchema error tree. - */ - private static _formatErrorDetails(errorDetails: ValidatorType.SchemaErrorDetail[]): string { - return JsonSchema._formatErrorDetailsHelper(errorDetails, '', ''); - } - - /** - * Used by _formatErrorDetails. - */ - private static _formatErrorDetailsHelper( - errorDetails: ValidatorType.SchemaErrorDetail[], - indent: string, - buffer: string - ): string { - for (const errorDetail of errorDetails) { - buffer += os.EOL + indent + `Error: ${errorDetail.path}`; - - if (errorDetail.description) { - const MAX_LENGTH: number = 40; - let truncatedDescription: string = errorDetail.description.trim(); - if (truncatedDescription.length > MAX_LENGTH) { - truncatedDescription = truncatedDescription.substr(0, MAX_LENGTH - 3) + '...'; - } - - buffer += ` (${truncatedDescription})`; - } - - buffer += os.EOL + indent + ` ${errorDetail.message}`; - - if (errorDetail.inner) { - buffer = JsonSchema._formatErrorDetailsHelper(errorDetail.inner, indent + ' ', buffer); - } + if (options) { + schema._dependentSchemas = options.dependentSchemas || []; + schema._schemaVersion = options.schemaVersion; + schema._customFormats = options.customFormats; + schema._rejectVendorExtensionKeywords = options.rejectVendorExtensionKeywords ?? false; } - return buffer; + return schema; } /** * Returns a short name for this schema, for use in error messages. * @remarks - * If the schema was loaded from a file, then the base filename is used. Otherwise, the "id" + * If the schema was loaded from a file, then the base filename is used. Otherwise, the "$id" * field is used if available. */ public get shortName(): string { @@ -202,6 +271,8 @@ export class JsonSchema { const schemaWithId: ISchemaWithId = this._schemaObject as ISchemaWithId; if (schemaWithId.id) { return schemaWithId.id; + } else if (schemaWithId.$id) { + return schemaWithId.$id; } } return '(anonymous schema)'; @@ -219,39 +290,72 @@ export class JsonSchema { this._ensureLoaded(); if (!this._validator) { - // Don't assign this to _validator until we're sure everything was successful - const newValidator: ValidatorType = new Validator({ - breakOnFirstError: false, - noTypeless: true, - noExtraKeywords: true - }); - - const anythingSchema: JsonObject = { - type: ['array', 'boolean', 'integer', 'number', 'object', 'string'] + const targetSchemaVersion: JsonSchemaVersion | undefined = + this._schemaVersion ?? _inferJsonSchemaVersion(this._schemaObject); + const validatorOptions: AjvOptions = { + strictSchema: true, + allowUnionTypes: true }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (newValidator as any).setRemoteReference('http://json-schema.org/draft-04/schema', anythingSchema); + let validator: Ajv; + // Keep legacy support for older draft-04 schema + switch (targetSchemaVersion) { + case 'draft-04': { + validator = new AjvDraft04(validatorOptions); + break; + } + + case 'draft-07': + default: { + validator = new Ajv(validatorOptions); + break; + } + } + + // Enable json-schema format validation + // https://ajv.js.org/packages/ajv-formats.html + addFormats(validator); + if (this._customFormats) { + for (const [name, format] of Object.entries(this._customFormats)) { + validator.addFormat(name, { ...format, async: false }); + } + } const collectedSchemas: JsonSchema[] = []; const seenObjects: Set = new Set(); const seenIds: Set = new Set(); - JsonSchema._collectDependentSchemas(collectedSchemas, this._dependentSchemas, seenObjects, seenIds); + this._collectDependentSchemas(collectedSchemas, this._dependentSchemas, seenObjects, seenIds); + + // Unless explicitly rejected, scan the top-level keys of each schema for vendor + // extension keys matching the x-- pattern and register them with + // AJV so that strict mode does not reject them as unknown keywords. + if (!this._rejectVendorExtensionKeywords) { + const vendorKeywords: Set = new Set(); + _collectVendorExtensionKeywords(this._schemaObject, vendorKeywords); + for (const collectedSchema of collectedSchemas) { + _collectVendorExtensionKeywords(collectedSchema._schemaObject, vendorKeywords); + } + for (const keyword of vendorKeywords) { + validator.addKeyword(keyword); + } + } // Validate each schema in order. We specifically do not supply them all together, because we want // to make sure that circular references will fail to validate. for (const collectedSchema of collectedSchemas) { - if (!newValidator.validateSchema(collectedSchema._schemaObject)) { + validator.validateSchema(collectedSchema._schemaObject) as boolean; + if (validator.errors && validator.errors.length > 0) { throw new Error( `Failed to validate schema "${collectedSchema.shortName}":` + os.EOL + - JsonSchema._formatErrorDetails(newValidator.getLastErrors()) + _formatErrorDetails(validator.errors) ); } + validator.addSchema(collectedSchema._schemaObject); } - this._validator = newValidator; + this._validator = validator.compile(this._schemaObject); } } @@ -268,12 +372,15 @@ export class JsonSchema { filenameForErrors: string, options?: IJsonSchemaValidateOptions ): void { - this.validateObjectWithCallback(jsonObject, (errorInfo: IJsonSchemaErrorInfo) => { - const prefix: string = - options && options.customErrorHeader ? options.customErrorHeader : 'JSON validation failed:'; - - throw new Error(prefix + os.EOL + filenameForErrors + os.EOL + errorInfo.details); - }); + this.validateObjectWithCallback( + jsonObject, + (errorInfo: IJsonSchemaErrorInfo) => { + const prefix: string = options?.customErrorHeader ?? 'JSON validation failed:'; + + throw new Error(prefix + os.EOL + filenameForErrors + os.EOL + errorInfo.details); + }, + options + ); } /** @@ -282,12 +389,22 @@ export class JsonSchema { */ public validateObjectWithCallback( jsonObject: JsonObject, - errorCallback: (errorInfo: IJsonSchemaErrorInfo) => void + errorCallback: (errorInfo: IJsonSchemaErrorInfo) => void, + options?: IJsonSchemaValidateObjectWithOptions ): void { this.ensureCompiled(); - if (!this._validator!.validate(jsonObject, this._schemaObject)) { - const errorDetails: string = JsonSchema._formatErrorDetails(this._validator!.getLastErrors()); + if (options?.ignoreSchemaField) { + const { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + $schema, + ...remainder + } = jsonObject; + jsonObject = remainder; + } + + if (this._validator && !this._validator(jsonObject)) { + const errorDetails: string = _formatErrorDetails(this._validator.errors!); const args: IJsonSchemaErrorInfo = { details: errorDetails @@ -300,6 +417,68 @@ export class JsonSchema { if (!this._schemaObject) { this._schemaObject = JsonFile.load(this._filename); } - return (this._schemaObject as ISchemaWithId).id || ''; + return (this._schemaObject as ISchemaWithId).id || (this._schemaObject as ISchemaWithId).$id || ''; + } + + private _collectDependentSchemas( + collectedSchemas: JsonSchema[], + dependentSchemas: JsonSchema[], + seenObjects: Set, + seenIds: Set + ): void { + for (const dependentSchema of dependentSchemas) { + // It's okay for the same schema to appear multiple times in the tree, but we only process it once + if (seenObjects.has(dependentSchema)) { + continue; + } + seenObjects.add(dependentSchema); + + const schemaId: string = dependentSchema._ensureLoaded(); + if (schemaId === '') { + throw new Error( + `This schema ${dependentSchema.shortName} cannot be referenced` + + ' because is missing the "id" (draft-04) or "$id" field' + ); + } + if (seenIds.has(schemaId)) { + throw new Error( + `This schema ${dependentSchema.shortName} has the same "id" (draft-04) or "$id" as another schema in this set` + ); + } + + seenIds.add(schemaId); + + collectedSchemas.push(dependentSchema); + + this._collectDependentSchemas( + collectedSchemas, + dependentSchema._dependentSchemas, + seenObjects, + seenIds + ); + } + } +} + +/** + * Used to nicely format the ZSchema error tree. + */ +function _formatErrorDetails(errorDetails: ErrorObject[]): string { + return _formatErrorDetailsHelper(errorDetails, '', ''); +} + +/** + * Used by _formatErrorDetails. + */ +function _formatErrorDetailsHelper(errorDetails: ErrorObject[], indent: string, buffer: string): string { + for (const errorDetail of errorDetails) { + buffer += os.EOL + indent + `Error: #${errorDetail.instancePath}`; + + buffer += os.EOL + indent + ` ${errorDetail.message}`; + if (errorDetail.params?.additionalProperty) { + buffer += `: ${errorDetail.params?.additionalProperty}`; + } } + + return buffer; } diff --git a/libraries/node-core-library/src/LegacyAdapters.ts b/libraries/node-core-library/src/LegacyAdapters.ts index d5edf1a3cec..ab9a4cd276b 100644 --- a/libraries/node-core-library/src/LegacyAdapters.ts +++ b/libraries/node-core-library/src/LegacyAdapters.ts @@ -96,17 +96,4 @@ export class LegacyAdapters { return errorObject; } } - - /** - * Prior to Node 11.x, the `Array.sort()` algorithm is not guaranteed to be stable. - * If you need a stable sort, you can use `sortStable()` as a workaround. - * - * @deprecated - * Use native Array.sort(), since Node < 14 is no longer supported - * @remarks - * On NodeJS 11.x and later, this method simply calls the native `Array.sort()`. - */ - public static sortStable(array: T[], compare?: (a: T, b: T) => number): void { - Array.prototype.sort.call(array, compare); - } } diff --git a/libraries/node-core-library/src/LockFile.ts b/libraries/node-core-library/src/LockFile.ts index 6684f945893..511a5e86490 100644 --- a/libraries/node-core-library/src/LockFile.ts +++ b/libraries/node-core-library/src/LockFile.ts @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as child_process from 'child_process'; +import * as path from 'node:path'; +import * as child_process from 'node:child_process'; + import { FileSystem } from './FileSystem'; import { FileWriter } from './FileWriter'; import { Async } from './Async'; @@ -58,7 +59,7 @@ export function getProcessStartTimeFromProcStat(stat: string): string | undefine // In theory, the representations of start time returned by `cat /proc/[pid]/stat` and `ps -o lstart` can change // while the system is running, but we assume this does not happen. // So the caller can safely use this value as part of a unique process id (on the machine, without comparing - // accross reboots). + // across reboots). return startTimeJiffies; } @@ -121,7 +122,7 @@ export function getProcessStartTime(pid: number): string | undefined { const psSplit: string[] = psStdout.split('\n'); - // successfuly able to run "ps", but no process was found + // successfully able to run "ps", but no process was found if (psSplit[1] === '') { return undefined; } @@ -136,6 +137,27 @@ export function getProcessStartTime(pid: number): string | undefined { throw new Error(`Unexpected output from the "ps" command`); } +// A set of locks that currently exist in the current process, to be used when +// multiple locks are acquired in the same process. +const IN_PROC_LOCKS: Set = new Set(); + +// The function used to determine a process's start time. Overridable for unit testing. +let _getStartTime: (pid: number) => string | undefined = getProcessStartTime; + +/** + * For unit testing only: overrides the function used to determine a process's start time. + * @internal + */ +export function _setLockFileGetProcessStartTime(fn: (pid: number) => string | undefined): void { + _getStartTime = fn; +} + +interface ITryAcquireResult { + fileWriter: FileWriter | undefined; + filePath: string; + dirtyWhenAcquired: boolean; +} + /** * The `LockFile` implements a file-based mutex for synchronizing access to a shared resource * between multiple Node.js processes. It is not recommended for synchronization solely within @@ -147,8 +169,6 @@ export function getProcessStartTime(pid: number): string | undefined { * @public */ export class LockFile { - private static _getStartTime: (pid: number) => string | undefined = getProcessStartTime; - private _fileWriter: FileWriter | undefined; private _filePath: string; private _dirtyWhenAcquired: boolean; @@ -157,6 +177,8 @@ export class LockFile { this._fileWriter = fileWriter; this._filePath = filePath; this._dirtyWhenAcquired = dirtyWhenAcquired; + + IN_PROC_LOCKS.add(filePath); } /** @@ -174,17 +196,24 @@ export class LockFile { if (!resourceName.match(/^[a-zA-Z0-9][a-zA-Z0-9-.]+[a-zA-Z0-9]$/)) { throw new Error( `The resource name "${resourceName}" is invalid.` + - ` It must be an alphanumberic string with only "-" or "." It must start with an alphanumeric character.` + ` It must be an alphanumeric string with only "-" or "." It must start and end with an alphanumeric character.` ); } - if (process.platform === 'win32') { - return path.join(path.resolve(resourceFolder), `${resourceName}.lock`); - } else if (process.platform === 'linux' || process.platform === 'darwin') { - return path.join(path.resolve(resourceFolder), `${resourceName}#${pid}.lock`); - } + switch (process.platform) { + case 'win32': { + return path.resolve(resourceFolder, `${resourceName}.lock`); + } - throw new Error(`File locking not implemented for platform: "${process.platform}"`); + case 'linux': + case 'darwin': { + return path.resolve(resourceFolder, `${resourceName}#${pid}.lock`); + } + + default: { + throw new Error(`File locking not implemented for platform: "${process.platform}"`); + } + } } /** @@ -196,12 +225,20 @@ export class LockFile { */ public static tryAcquire(resourceFolder: string, resourceName: string): LockFile | undefined { FileSystem.ensureFolder(resourceFolder); - if (process.platform === 'win32') { - return LockFile._tryAcquireWindows(resourceFolder, resourceName); - } else if (process.platform === 'linux' || process.platform === 'darwin') { - return LockFile._tryAcquireMacOrLinux(resourceFolder, resourceName); - } - throw new Error(`File locking not implemented for platform: "${process.platform}"`); + const lockFilePath: string = LockFile.getLockFilePath(resourceFolder, resourceName); + const result: ITryAcquireResult | undefined = _tryAcquireInner( + resourceFolder, + resourceName, + lockFilePath + ); + return result && new LockFile(result.fileWriter, result.filePath, result.dirtyWhenAcquired); + } + + /** + * @deprecated Use {@link LockFile.acquireAsync} instead. + */ + public static acquire(resourceFolder: string, resourceName: string, maxWaitMs?: number): Promise { + return LockFile.acquireAsync(resourceFolder, resourceName, maxWaitMs); } /** @@ -218,206 +255,34 @@ export class LockFile { * the filename of the temporary file created to manage the lock. * @param maxWaitMs - The maximum number of milliseconds to wait for the lock before reporting an error */ - public static acquire(resourceFolder: string, resourceName: string, maxWaitMs?: number): Promise { + public static async acquireAsync( + resourceFolder: string, + resourceName: string, + maxWaitMs?: number + ): Promise { const interval: number = 100; const startTime: number = Date.now(); + const timeoutTime: number | undefined = maxWaitMs ? startTime + maxWaitMs : undefined; - const retryLoop: () => Promise = async () => { - const lock: LockFile | undefined = LockFile.tryAcquire(resourceFolder, resourceName); - if (lock) { - return lock; - } - if (maxWaitMs && Date.now() > startTime + maxWaitMs) { - throw new Error(`Exceeded maximum wait time to acquire lock for resource "${resourceName}"`); - } - - await Async.sleep(interval); - return retryLoop(); - }; - - return retryLoop(); - } - - /** - * Attempts to acquire the lock on a Linux or OSX machine - */ - private static _tryAcquireMacOrLinux(resourceFolder: string, resourceName: string): LockFile | undefined { - let dirtyWhenAcquired: boolean = false; - - // get the current process' pid - const pid: number = process.pid; - const startTime: string | undefined = LockFile._getStartTime(pid); - - if (!startTime) { - throw new Error(`Unable to calculate start time for current process.`); - } - - const pidLockFilePath: string = LockFile.getLockFilePath(resourceFolder, resourceName); - let lockFileHandle: FileWriter | undefined; - - let lockFile: LockFile; - - try { - // open in write mode since if this file exists, it cannot be from the current process - // TODO: This will malfunction if the same process tries to acquire two locks on the same file. - // We should ideally maintain a dictionary of normalized acquired filenames - lockFileHandle = FileWriter.open(pidLockFilePath); - lockFileHandle.write(startTime); - - const currentBirthTimeMs: number = FileSystem.getStatistics(pidLockFilePath).birthtime.getTime(); - - let smallestBirthTimeMs: number = currentBirthTimeMs; - let smallestBirthTimePid: string = pid.toString(); - - // now, scan the directory for all lockfiles - const files: string[] = FileSystem.readFolderItemNames(resourceFolder); - - // look for anything ending with # then numbers and ".lock" - const lockFileRegExp: RegExp = /^(.+)#([0-9]+)\.lock$/; - - let match: RegExpMatchArray | null; - let otherPid: string; - for (const fileInFolder of files) { - if ( - (match = fileInFolder.match(lockFileRegExp)) && - match[1] === resourceName && - (otherPid = match[2]) !== pid.toString() - ) { - // we found at least one lockfile hanging around that isn't ours - const fileInFolderPath: string = path.join(resourceFolder, fileInFolder); - dirtyWhenAcquired = true; - - // console.log(`FOUND OTHER LOCKFILE: ${otherPid}`); - - const otherPidCurrentStartTime: string | undefined = LockFile._getStartTime(parseInt(otherPid, 10)); - - let otherPidOldStartTime: string | undefined; - let otherBirthtimeMs: number | undefined; - try { - otherPidOldStartTime = FileSystem.readFile(fileInFolderPath); - // check the timestamp of the file - otherBirthtimeMs = FileSystem.getStatistics(fileInFolderPath).birthtime.getTime(); - } catch (err) { - // this means the file is probably deleted already - } - - // if the otherPidOldStartTime is invalid, then we should look at the timestamp, - // if this file was created after us, ignore it - // if it was created within 1 second before us, then it could be good, so we - // will conservatively fail - // otherwise it is an old lock file and will be deleted - if (otherPidOldStartTime === '' && otherBirthtimeMs !== undefined) { - if (otherBirthtimeMs > currentBirthTimeMs) { - // ignore this file, he will be unable to get the lock since this process - // will hold it - // console.log(`Ignoring lock for pid ${otherPid} because its lockfile is newer than ours.`); - continue; - } else if ( - otherBirthtimeMs - currentBirthTimeMs < 0 && // it was created before us AND - otherBirthtimeMs - currentBirthTimeMs > -1000 - ) { - // it was created less than a second before - - // conservatively be unable to keep the lock - return undefined; - } - } - - // console.log(`Other pid ${otherPid} lockfile has start time: "${otherPidOldStartTime}"`); - // console.log(`Other pid ${otherPid} actually has start time: "${otherPidCurrentStartTime}"`); - - // this means the process is no longer executing, delete the file - if (!otherPidCurrentStartTime || otherPidOldStartTime !== otherPidCurrentStartTime) { - // console.log(`Other pid ${otherPid} is no longer executing!`); - FileSystem.deleteFile(fileInFolderPath); - continue; - } + await FileSystem.ensureFolderAsync(resourceFolder); - // console.log(`Pid ${otherPid} lockfile has birth time: ${otherBirthtimeMs}`); - // console.log(`Pid ${pid} lockfile has birth time: ${currentBirthTimeMs}`); - // this is a lockfile pointing at something valid - if (otherBirthtimeMs !== undefined) { - // the other lock file was created before the current earliest lock file - // or the other lock file was created at the same exact time, but has earlier pid - - // note that it is acceptable to do a direct comparison of the PIDs in this case - // since we are establishing a consistent order to apply to the lock files in all - // execution instances. - - // it doesn't matter that the PIDs roll over, we've already - // established that these processes all started at the same time, so we just - // need to get all instances of the lock test to agree which one won. - if ( - otherBirthtimeMs < smallestBirthTimeMs || - (otherBirthtimeMs === smallestBirthTimeMs && otherPid < smallestBirthTimePid) - ) { - smallestBirthTimeMs = otherBirthtimeMs; - smallestBirthTimePid = otherPid; - } - } - } - } - - if (smallestBirthTimePid !== pid.toString()) { - // we do not have the lock - return undefined; - } - - // we have the lock! - lockFile = new LockFile(lockFileHandle, pidLockFilePath, dirtyWhenAcquired); - lockFileHandle = undefined; // we have handed the descriptor off to the instance - } finally { - if (lockFileHandle) { - // ensure our lock is closed - lockFileHandle.close(); - FileSystem.deleteFile(pidLockFilePath); - } - } - return lockFile; - } - - /** - * Attempts to acquire the lock using Windows - * This algorithm is much simpler since we can rely on the operating system - */ - private static _tryAcquireWindows(resourceFolder: string, resourceName: string): LockFile | undefined { const lockFilePath: string = LockFile.getLockFilePath(resourceFolder, resourceName); - let dirtyWhenAcquired: boolean = false; - - let fileHandle: FileWriter | undefined; - let lockFile: LockFile; - - try { - if (FileSystem.exists(lockFilePath)) { - dirtyWhenAcquired = true; - // If the lockfile is held by an process with an exclusive lock, then removing it will - // silently fail. OpenSync() below will then fail and we will be unable to create a lock. - - // Otherwise, the lockfile is sitting on disk, but nothing is holding it, implying that - // the last process to hold it died. - FileSystem.deleteFile(lockFilePath); - } - - try { - // Attempt to open an exclusive lockfile - fileHandle = FileWriter.open(lockFilePath, { exclusive: true }); - } catch (error) { - // we tried to delete the lock, but something else is holding it, - // (probably an active process), therefore we are unable to create a lock - return undefined; + // eslint-disable-next-line no-unmodified-loop-condition + while (!timeoutTime || Date.now() <= timeoutTime) { + const result: ITryAcquireResult | undefined = _tryAcquireInner( + resourceFolder, + resourceName, + lockFilePath + ); + if (result) { + return new LockFile(result.fileWriter, result.filePath, result.dirtyWhenAcquired); } - // Ensure we can hand off the file descriptor to the lockfile - lockFile = new LockFile(fileHandle, lockFilePath, dirtyWhenAcquired); - fileHandle = undefined; - } finally { - if (fileHandle) { - fileHandle.close(); - } + await Async.sleepAsync(interval); } - return lockFile; + throw new Error(`Exceeded maximum wait time to acquire lock for resource "${resourceName}"`); } /** @@ -431,10 +296,13 @@ export class LockFile { throw new Error(`The lock for file "${path.basename(this._filePath)}" has already been released.`); } + IN_PROC_LOCKS.delete(this._filePath); + this._fileWriter!.close(); if (deleteFile) { FileSystem.deleteFile(this._filePath); } + this._fileWriter = undefined; } @@ -460,3 +328,243 @@ export class LockFile { return this._fileWriter === undefined; } } + +function _tryAcquireInner( + resourceFolder: string, + resourceName: string, + lockFilePath: string +): ITryAcquireResult | undefined { + if (!IN_PROC_LOCKS.has(lockFilePath)) { + switch (process.platform) { + case 'win32': { + return _tryAcquireWindows(lockFilePath); + } + + case 'linux': + case 'darwin': { + return _tryAcquireMacOrLinux(resourceFolder, resourceName, lockFilePath); + } + + default: { + throw new Error(`File locking not implemented for platform: "${process.platform}"`); + } + } + } +} + +/** + * Attempts to acquire the lock on a Linux or OSX machine + */ +function _tryAcquireMacOrLinux( + resourceFolder: string, + resourceName: string, + pidLockFilePath: string +): ITryAcquireResult | undefined { + // get the current process identifier (PID) + const pid: number = process.pid; + + // Suppose that a process terminates unexpectedly without deleting its PID-based lockfile, + // then we check to see if the process is still alive. The OS may have given the same PID + // to a new process, how to detect that? We will rely on getProcessStartTime() which + // is stored in the file itself for comparison. + const startTime: string | undefined = _getStartTime(pid); + + if (!startTime) { + throw new Error(`Unable to calculate start time for current process.`); + } + + let lockFileHandle: FileWriter | undefined; + + let result: ITryAcquireResult | undefined; + + try { + // open in write mode since if this file exists, it cannot be from the current process + // TODO: This will malfunction if the same process tries to acquire two locks on the same file. + // We should ideally maintain a dictionary of normalized acquired filenames + lockFileHandle = FileWriter.open(pidLockFilePath); + lockFileHandle.write(startTime); + const currentBirthTimeMs: number = lockFileHandle.getStatistics().birthtime.getTime(); + + let smallestBirthTimeMs: number = currentBirthTimeMs; + let smallestBirthTimePid: string = pid.toString(); + + // now, scan the directory for all lockfiles + const files: string[] = FileSystem.readFolderItemNames(resourceFolder); + + // look for anything ending with # then numbers and ".lock" + const lockFileRegExp: RegExp = /^(.+)#([0-9]+)\.lock$/; + + // If we are the process to acquire the lock, it becomes our responsibility to clean up these + // stale files. If there is at least 1 stale file, then the resource is assumed to be "dirty" + // (for example, the previous process was interrupted before releasing or while acquiring). + const staleFilesToDelete: string[] = []; + + let match: RegExpMatchArray | null; + let otherPid: string; + for (const fileInFolder of files) { + if ( + (match = fileInFolder.match(lockFileRegExp)) && + match[1] === resourceName && + (otherPid = match[2]) !== pid.toString() + ) { + // We found at least one lockfile hanging around that isn't ours + const fileInFolderPath: string = `${resourceFolder}/${fileInFolder}`; + + // console.log(`FOUND OTHER LOCKFILE: ${otherPid}`); + + // Actual start time of the other PID + const otherPidCurrentStartTime: string | undefined = _getStartTime(parseInt(otherPid, 10)); + + // The start time from the file, which we will compare with otherPidCurrentStartTime + // to determine whether the PID got reused by a new process. + let otherPidOldStartTime: string | undefined; + let otherBirthtimeMs: number | undefined; + try { + otherPidOldStartTime = FileSystem.readFile(fileInFolderPath); + // check the timestamp of the file + otherBirthtimeMs = FileSystem.getStatistics(fileInFolderPath).birthtime.getTime(); + } catch (error) { + if (FileSystem.isNotExistError(error)) { + // ==> Properly closed lockfile, safe to ignore: + // The other process deleted the file, which we assume means it completed successfully, + // so the state is not dirty. This is equivalent to if readFolderItemNames() never saw + // the file in the firstplace. + continue; + } + } + + // What the other process's file exists, but it is an empty file? + // Either they were terminated while acquiring, or else they haven't finished writing it yet. + if (otherBirthtimeMs !== undefined && otherPidOldStartTime === '') { + if (otherBirthtimeMs > currentBirthTimeMs) { + // ==> Safe to ignore + // If the other process was terminated, it happened before they finished acquiring. + // If the other process is alive, their file is newer, so we will acquire instead of them. + + // console.log(`Ignoring lock for pid ${otherPid} because its lockfile is newer than ours.`); + continue; + } else if ( + otherBirthtimeMs - currentBirthTimeMs < 0 && + otherBirthtimeMs - currentBirthTimeMs > -1000 + ) { + // ==> Race condition + // The other process created their file first, so they will probably acquire the lock + // after they finish writing the contents. But what if their process is actually dead + // and replaced by a new process with the same PID? Normally the otherPidOldStartTime + // gives the answer, but in this edge case we are missing that information. + // So we conservatively assume that it should not take them more than 1000ms to + // open a file, write a PID, and close the file. + return undefined; // fail to acquire and retry later + } + } + + // console.log(`Other pid ${otherPid} lockfile has start time: "${otherPidOldStartTime}"`); + // console.log(`Other pid ${otherPid} actually has start time: "${otherPidCurrentStartTime}"`); + + // Time to compare + if (!otherPidCurrentStartTime || otherPidOldStartTime !== otherPidCurrentStartTime) { + // ==> Stale lockfile + // This file doesn't prevent us from acquiring the lock, but it does indicate that + // the resource was left in a dirty state. (If we delete the file right now, that + // information would be lost, so we clean up later when we acquire successfully.) + + // console.log(`Other pid ${otherPid} is no longer executing!`); + staleFilesToDelete.push(fileInFolderPath); + continue; + } + + // console.log(`Pid ${otherPid} lockfile has birth time: ${otherBirthtimeMs}`); + // console.log(`Pid ${pid} lockfile has birth time: ${currentBirthTimeMs}`); + + if (otherBirthtimeMs !== undefined) { + // ==> We found a valid file belonging to another process. + // With multiple parties trying to acquire, the winner is the smallestBirthTime, + // so we need to sort. + + // the other lock file was created before the current earliest lock file + // or the other lock file was created at the same exact time, but has earlier pid + + // note that it is acceptable to do a direct comparison of the PIDs in this case + // since we are establishing a consistent order to apply to the lock files in all + // execution instances. + + // it doesn't matter that the PIDs roll over, we've already + // established that these processes all started at the same time, so we just + // need to get all instances of the lock test to agree which one won. + if ( + otherBirthtimeMs < smallestBirthTimeMs || + (otherBirthtimeMs === smallestBirthTimeMs && otherPid < smallestBirthTimePid) + ) { + smallestBirthTimeMs = otherBirthtimeMs; + smallestBirthTimePid = otherPid; + } + } + } + } + + if (smallestBirthTimePid !== pid.toString()) { + // we do not have the lock + return undefined; + } + + let dirtyWhenAcquired: boolean = false; + for (const staleFileToDelete of staleFilesToDelete) { + FileSystem.deleteFile(staleFileToDelete, { throwIfNotExists: false }); + dirtyWhenAcquired = true; + } + + // We have the lock! + result = { fileWriter: lockFileHandle, filePath: pidLockFilePath, dirtyWhenAcquired }; + lockFileHandle = undefined; // The returned result has taken ownership of our handle + } finally { + if (lockFileHandle) { + // ensure our lock is closed + lockFileHandle.close(); + FileSystem.deleteFile(pidLockFilePath); + } + } + return result; +} + +/** + * Attempts to acquire the lock using Windows + * This algorithm is much simpler since we can rely on the operating system + */ +function _tryAcquireWindows(lockFilePath: string): ITryAcquireResult | undefined { + let dirtyWhenAcquired: boolean = false; + + let fileHandle: FileWriter | undefined; + let result: ITryAcquireResult | undefined; + + try { + if (FileSystem.exists(lockFilePath)) { + dirtyWhenAcquired = true; + + // If the lockfile is held by an process with an exclusive lock, then removing it will + // silently fail. OpenSync() below will then fail and we will be unable to create a lock. + + // Otherwise, the lockfile is sitting on disk, but nothing is holding it, implying that + // the last process to hold it died. + FileSystem.deleteFile(lockFilePath); + } + + try { + // Attempt to open an exclusive lockfile + fileHandle = FileWriter.open(lockFilePath, { exclusive: true }); + } catch (error) { + // we tried to delete the lock, but something else is holding it, + // (probably an active process), therefore we are unable to create a lock + return undefined; + } + + // Ensure we can hand off the file descriptor to the lockfile + result = { fileWriter: fileHandle, filePath: lockFilePath, dirtyWhenAcquired }; + fileHandle = undefined; + } finally { + if (fileHandle) { + fileHandle.close(); + } + } + + return result; +} diff --git a/libraries/node-core-library/src/MinimumHeap.ts b/libraries/node-core-library/src/MinimumHeap.ts new file mode 100644 index 00000000000..e7406619d65 --- /dev/null +++ b/libraries/node-core-library/src/MinimumHeap.ts @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * Implements a standard heap data structure for items of type T and a custom comparator. + * The root will always be the minimum value as determined by the comparator. + * + * @public + */ +export class MinimumHeap { + private readonly _items: T[] = []; + private readonly _comparator: (a: T, b: T) => number; + + /** + * Constructs a new MinimumHeap instance. + * @param comparator - a comparator function that determines the order of the items in the heap. + * If the comparator returns a value less than zero, then `a` will be considered less than `b`. + * If the comparator returns zero, then `a` and `b` are considered equal. + * Otherwise, `a` will be considered greater than `b`. + */ + public constructor(comparator: (a: T, b: T) => number) { + this._comparator = comparator; + } + + /** + * Returns the number of items in the heap. + * @returns the number of items in the heap. + */ + public get size(): number { + return this._items.length; + } + + /** + * Retrieves the root item from the heap without removing it. + * @returns the root item, or `undefined` if the heap is empty + */ + public peek(): T | undefined { + return this._items[0]; + } + + /** + * Retrieves and removes the root item from the heap. The next smallest item will become the new root. + * @returns the root item, or `undefined` if the heap is empty + */ + public poll(): T | undefined { + if (this.size > 0) { + const result: T = this._items[0]; + const item: T = this._items.pop()!; + + const size: number = this.size; + if (size === 0) { + // Short circuit in the trivial case + return result; + } + + let index: number = 0; + + let smallerChildIndex: number = 1; + + while (smallerChildIndex < size) { + let smallerChild: T = this._items[smallerChildIndex]; + + const rightChildIndex: number = smallerChildIndex + 1; + + if (rightChildIndex < size) { + const rightChild: T = this._items[rightChildIndex]; + if (this._comparator(rightChild, smallerChild) < 0) { + smallerChildIndex = rightChildIndex; + smallerChild = rightChild; + } + } + + if (this._comparator(smallerChild, item) < 0) { + this._items[index] = smallerChild; + index = smallerChildIndex; + smallerChildIndex = index * 2 + 1; + } else { + break; + } + } + + // Place the item in its final location satisfying the heap property + this._items[index] = item; + + return result; + } + } + + /** + * Pushes an item into the heap. + * @param item - the item to push + */ + public push(item: T): void { + let index: number = this.size; + while (index > 0) { + // Due to zero-based indexing the parent is not exactly a bit shift + const parentIndex: number = ((index + 1) >> 1) - 1; + const parent: T = this._items[parentIndex]; + if (this._comparator(item, parent) < 0) { + this._items[index] = parent; + index = parentIndex; + } else { + break; + } + } + this._items[index] = item; + } +} diff --git a/libraries/node-core-library/src/Objects.ts b/libraries/node-core-library/src/Objects.ts new file mode 100644 index 00000000000..ac7923db5f7 --- /dev/null +++ b/libraries/node-core-library/src/Objects.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as Objects from './objects/index'; + +export { Objects }; diff --git a/libraries/node-core-library/src/PackageJsonLookup.ts b/libraries/node-core-library/src/PackageJsonLookup.ts index 27c6de93086..59efc981569 100644 --- a/libraries/node-core-library/src/PackageJsonLookup.ts +++ b/libraries/node-core-library/src/PackageJsonLookup.ts @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import { JsonFile } from './JsonFile'; -import { IPackageJson, INodePackageJson } from './IPackageJson'; +import type { IPackageJson, INodePackageJson } from './IPackageJson'; import { FileConstants } from './Constants'; import { FileSystem } from './FileSystem'; @@ -50,6 +51,8 @@ type ITryLoadPackageJsonInternalResult = | ITryLoadPackageJsonInternalKnownFailureResult | ITryLoadPackageJsonInternalUnknownFailureResult; +let _instance: PackageJsonLookup | undefined; + /** * This class provides methods for finding the nearest "package.json" for a folder * and retrieving the name of the package. The results are cached. @@ -57,8 +60,6 @@ type ITryLoadPackageJsonInternalResult = * @public */ export class PackageJsonLookup { - private static _instance: PackageJsonLookup | undefined; - /** * A singleton instance of `PackageJsonLookup`, which is useful for short-lived processes * that can reasonably assume that the file system will not be modified after the cache @@ -70,11 +71,11 @@ export class PackageJsonLookup { * of relying on this instance. */ public static get instance(): PackageJsonLookup { - if (!PackageJsonLookup._instance) { - PackageJsonLookup._instance = new PackageJsonLookup({ loadExtraFields: true }); + if (!_instance) { + _instance = new PackageJsonLookup({ loadExtraFields: true }); } - return PackageJsonLookup._instance; + return _instance; } private _loadExtraFields: boolean = false; @@ -346,6 +347,7 @@ export class PackageJsonLookup { packageJson.dependencies = loadedPackageJson.dependencies; packageJson.description = loadedPackageJson.description; packageJson.devDependencies = loadedPackageJson.devDependencies; + packageJson.exports = loadedPackageJson.exports; packageJson.homepage = loadedPackageJson.homepage; packageJson.license = loadedPackageJson.license; packageJson.main = loadedPackageJson.main; @@ -354,8 +356,8 @@ export class PackageJsonLookup { packageJson.peerDependencies = loadedPackageJson.peerDependencies; packageJson.private = loadedPackageJson.private; packageJson.scripts = loadedPackageJson.scripts; - packageJson.typings = loadedPackageJson.typings || loadedPackageJson.types; packageJson.tsdocMetadata = loadedPackageJson.tsdocMetadata; + packageJson.typings = loadedPackageJson.typings || loadedPackageJson.types; packageJson.version = loadedPackageJson.version; } diff --git a/libraries/node-core-library/src/PackageName.ts b/libraries/node-core-library/src/PackageName.ts index d96d2793759..bbcc0fef328 100644 --- a/libraries/node-core-library/src/PackageName.ts +++ b/libraries/node-core-library/src/PackageName.ts @@ -1,6 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +// encodeURIComponent() escapes all characters except: A-Z a-z 0-9 - _ . ! ~ * ' ( ) +// However, these are disallowed because they are shell characters: ! ~ * ' ( ) +const _invalidNameCharactersRegExp: RegExp = /[^A-Za-z0-9\-_\.]/; + /** * A package name that has been separated into its scope and unscoped name. * @@ -72,10 +76,6 @@ export interface IPackageNameParserOptions { * @public */ export class PackageNameParser { - // encodeURIComponent() escapes all characters except: A-Z a-z 0-9 - _ . ! ~ * ' ( ) - // However, these are disallowed because they are shell characters: ! ~ * ' ( ) - private static readonly _invalidNameCharactersRegExp: RegExp = /[^A-Za-z0-9\-_\.]/; - private readonly _options: IPackageNameParserOptions; public constructor(options: IPackageNameParserOptions = {}) { @@ -163,9 +163,7 @@ export class PackageNameParser { // "The name ends up being part of a URL, an argument on the command line, and a folder name. // Therefore, the name can't contain any non-URL-safe characters" - const match: RegExpMatchArray | null = nameWithoutScopeSymbols.match( - PackageNameParser._invalidNameCharactersRegExp - ); + const match: RegExpMatchArray | null = nameWithoutScopeSymbols.match(_invalidNameCharactersRegExp); if (match) { result.error = `The package name "${packageName}" contains an invalid character: "${match[0]}"`; return result; @@ -257,6 +255,8 @@ export class PackageNameParser { } } +const _parser: PackageNameParser = new PackageNameParser(); + /** * Provides basic operations for validating and manipulating NPM package names such as `my-package` * or `@scope/my-package`. @@ -268,40 +268,38 @@ export class PackageNameParser { * @public */ export class PackageName { - private static readonly _parser: PackageNameParser = new PackageNameParser(); - /** {@inheritDoc PackageNameParser.tryParse} */ public static tryParse(packageName: string): IParsedPackageNameOrError { - return PackageName._parser.tryParse(packageName); + return _parser.tryParse(packageName); } /** {@inheritDoc PackageNameParser.parse} */ public static parse(packageName: string): IParsedPackageName { - return this._parser.parse(packageName); + return _parser.parse(packageName); } /** {@inheritDoc PackageNameParser.getScope} */ public static getScope(packageName: string): string { - return this._parser.getScope(packageName); + return _parser.getScope(packageName); } /** {@inheritDoc PackageNameParser.getUnscopedName} */ public static getUnscopedName(packageName: string): string { - return this._parser.getUnscopedName(packageName); + return _parser.getUnscopedName(packageName); } /** {@inheritDoc PackageNameParser.isValidName} */ public static isValidName(packageName: string): boolean { - return this._parser.isValidName(packageName); + return _parser.isValidName(packageName); } /** {@inheritDoc PackageNameParser.validate} */ public static validate(packageName: string): void { - return this._parser.validate(packageName); + return _parser.validate(packageName); } /** {@inheritDoc PackageNameParser.combineParts} */ public static combineParts(scope: string, unscopedName: string): string { - return this._parser.combineParts(scope, unscopedName); + return _parser.combineParts(scope, unscopedName); } } diff --git a/libraries/node-core-library/src/Path.ts b/libraries/node-core-library/src/Path.ts index 6ebcb907795..a00484fe97b 100644 --- a/libraries/node-core-library/src/Path.ts +++ b/libraries/node-core-library/src/Path.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; /** * The format that the FileError message should conform to. The supported formats are: @@ -67,6 +67,14 @@ export interface IPathFormatConciselyOptions { trimLeadingDotSlash?: boolean; } +// Matches a relative path consisting entirely of periods and slashes +// Example: ".", "..", "../..", etc +const _relativePathRegex: RegExp = /^[.\/\\]+$/; + +// Matches a relative path segment that traverses upwards +// Example: "a/../b" +const _upwardPathSegmentRegex: RegExp = /([\/\\]|^)\.\.([\/\\]|$)/; + /** * Common operations for manipulating file and directory paths. * @remarks @@ -74,14 +82,6 @@ export interface IPathFormatConciselyOptions { * @public */ export class Path { - // Matches a relative path consisting entirely of periods and slashes - // Example: ".", "..", "../..", etc - private static _relativePathRegex: RegExp = /^[.\/\\]+$/; - - // Matches a relative path segment that traverses upwards - // Example: "a/../b" - private static _upwardPathSegmentRegex: RegExp = /([\/\\]|^)\.\.([\/\\]|$)/; - /** * Returns true if "childPath" is located inside the "parentFolderPath" folder * or one of its child folders. Note that "parentFolderPath" is not considered to be @@ -97,7 +97,7 @@ export class Path { // "../.." or "..\\..", which consists entirely of periods and slashes. // (Note that something like "....t" is actually a valid filename, but "...." is not.) const relativePath: string = path.relative(childPath, parentFolderPath); - return Path._relativePathRegex.test(relativePath); + return _relativePathRegex.test(relativePath); } /** @@ -111,7 +111,7 @@ export class Path { */ public static isUnderOrEqual(childPath: string, parentFolderPath: string): boolean { const relativePath: string = path.relative(childPath, parentFolderPath); - return relativePath === '' || Path._relativePathRegex.test(relativePath); + return relativePath === '' || _relativePathRegex.test(relativePath); } /** @@ -137,7 +137,7 @@ export class Path { public static formatConcisely(options: IPathFormatConciselyOptions): string { // Same logic as Path.isUnderOrEqual() const relativePath: string = path.relative(options.pathToConvert, options.baseFolder); - const isUnderOrEqual: boolean = relativePath === '' || Path._relativePathRegex.test(relativePath); + const isUnderOrEqual: boolean = relativePath === '' || _relativePathRegex.test(relativePath); if (isUnderOrEqual) { // Note that isUnderOrEqual()'s relativePath is the reverse direction @@ -259,7 +259,7 @@ export class Path { return false; } // Does it contain ".." - if (Path._upwardPathSegmentRegex.test(inputPath)) { + if (_upwardPathSegmentRegex.test(inputPath)) { return false; } return true; diff --git a/libraries/node-core-library/src/ProtectableMapView.ts b/libraries/node-core-library/src/ProtectableMapView.ts index f7a0babc1da..02a60e5bafb 100644 --- a/libraries/node-core-library/src/ProtectableMapView.ts +++ b/libraries/node-core-library/src/ProtectableMapView.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ProtectableMap, IProtectableMapParameters } from './ProtectableMap'; +import type { ProtectableMap, IProtectableMapParameters } from './ProtectableMap'; /** * The internal wrapper used by ProtectableMap. It extends the real `Map` base class, @@ -21,24 +21,21 @@ export class ProtectableMapView extends Map { this._parameters = parameters; } - public clear(): void { - // override + public override clear(): void { if (this._parameters.onClear) { this._parameters.onClear(this._owner); } super.clear(); } - public delete(key: K): boolean { - // override + public override delete(key: K): boolean { if (this._parameters.onDelete) { this._parameters.onDelete(this._owner, key); } return super.delete(key); } - public set(key: K, value: V): this { - // override + public override set(key: K, value: V): this { let modifiedValue: V = value; if (this._parameters.onSet) { modifiedValue = this._parameters.onSet(this._owner, key, modifiedValue); diff --git a/libraries/node-core-library/src/RealNodeModulePath.ts b/libraries/node-core-library/src/RealNodeModulePath.ts new file mode 100644 index 00000000000..dc4c74ac345 --- /dev/null +++ b/libraries/node-core-library/src/RealNodeModulePath.ts @@ -0,0 +1,199 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as nodeFs from 'node:fs'; +import * as nodePath from 'node:path'; + +/** + * Arguments used to create a function that resolves symlinked node_modules in a path + * @public + */ +export interface IRealNodeModulePathResolverOptions { + fs?: Partial>; + path?: Partial>; + /** + * If set to true, the resolver will not throw if part of the path does not exist. + * @defaultValue false + */ + ignoreMissingPaths?: boolean; +} + +/** + * This class encapsulates a caching resolver for symlinks in node_modules directories. + * It assumes that the only symlinks that exist in input paths are those that correspond to + * npm packages. + * + * @remarks + * In a repository with a symlinked node_modules installation, some symbolic links need to be mapped for + * node module resolution to produce correct results. However, calling `fs.realpathSync.native` on every path, + * as is commonly done by most resolvers, involves an enormous number of file system operations (for reference, + * each invocation of `fs.realpathSync.native` involves a series of `fs.readlinkSync` calls, up to one for each + * path segment in the input). + * + * @public + */ +export class RealNodeModulePathResolver { + /** + * Similar in function to `fs.realpathSync.native`, but assumes the only symlinks present are npm packages. + * + * @param input - A path to a file or directory, where the path separator is `${require('node:path').sep}` + * @returns The real path to the input, resolving the node_modules symlinks in the path + * @public + */ + public readonly realNodeModulePath: (input: string) => string; + + private readonly _cache: Map; + private readonly _errorCache: Map; + private readonly _fs: Required>; + private readonly _path: Required>; + private readonly _lstatOptions: Pick; + + public constructor(options: IRealNodeModulePathResolverOptions = {}) { + const { + fs: { lstatSync = nodeFs.lstatSync, readlinkSync = nodeFs.readlinkSync } = nodeFs, + path: { + isAbsolute = nodePath.isAbsolute, + join = nodePath.join, + resolve = nodePath.resolve, + sep = nodePath.sep + } = nodePath, + ignoreMissingPaths = false + } = options; + const cache: Map = (this._cache = new Map()); + this._errorCache = new Map(); + this._fs = { + lstatSync, + readlinkSync + }; + this._path = { + isAbsolute, + join, + resolve, + sep + }; + this._lstatOptions = { + throwIfNoEntry: !ignoreMissingPaths + }; + + const nodeModulesToken: string = `${sep}node_modules${sep}`; + const self: this = this; + + function realNodeModulePathInternal(input: string): string { + // Find the last node_modules path segment + const nodeModulesIndex: number = input.lastIndexOf(nodeModulesToken); + if (nodeModulesIndex < 0) { + // No node_modules in path, so we assume it is already the real path + return input; + } + + // First assume that the next path segment after node_modules is a symlink + let linkStart: number = nodeModulesIndex + nodeModulesToken.length - 1; + let linkEnd: number = input.indexOf(sep, linkStart + 1); + // If the path segment starts with a '@', then it is a scoped package + const isScoped: boolean = input.charAt(linkStart + 1) === '@'; + if (isScoped) { + // For a scoped package, the scope is an ordinary directory, so we need to find the next path segment + if (linkEnd < 0) { + // Symlink missing, so see if anything before the last node_modules needs resolving, + // and preserve the rest of the path + return join( + realNodeModulePathInternal(input.slice(0, nodeModulesIndex)), + input.slice(nodeModulesIndex + 1), + // Joining to `.` will clean up any extraneous trailing slashes + '.' + ); + } + + linkStart = linkEnd; + linkEnd = input.indexOf(sep, linkStart + 1); + } + + // No trailing separator, so the link is the last path segment + if (linkEnd < 0) { + linkEnd = input.length; + } + + const linkCandidate: string = input.slice(0, linkEnd); + // Check if the link is a symlink + const linkTarget: string | undefined = self._tryReadLink(linkCandidate); + if (linkTarget && isAbsolute(linkTarget)) { + // Absolute path, combine the link target with any remaining path segments + // Cache the resolution to avoid the readlink call in subsequent calls + cache.set(linkCandidate, linkTarget); + cache.set(linkTarget, linkTarget); + // Joining to `.` will clean up any extraneous trailing slashes + return join(linkTarget, input.slice(linkEnd + 1), '.'); + } + + // Relative path or does not exist + // Either way, the path before the last node_modules could itself be in a node_modules folder + // So resolve the base path to find out what paths are relative to + const realpathBeforeNodeModules: string = realNodeModulePathInternal(input.slice(0, nodeModulesIndex)); + if (linkTarget) { + // Relative path in symbolic link. Should be resolved relative to real path of base path. + const resolvedTarget: string = resolve( + realpathBeforeNodeModules, + input.slice(nodeModulesIndex + 1, linkStart), + linkTarget + ); + // Cache the result of the combined resolution to avoid the readlink call in subsequent calls + cache.set(linkCandidate, resolvedTarget); + cache.set(resolvedTarget, resolvedTarget); + // Joining to `.` will clean up any extraneous trailing slashes + return join(resolvedTarget, input.slice(linkEnd + 1), '.'); + } + + // No symlink, so just return the real path before the last node_modules combined with the + // subsequent path segments + // Joining to `.` will clean up any extraneous trailing slashes + return join(realpathBeforeNodeModules, input.slice(nodeModulesIndex + 1), '.'); + } + + this.realNodeModulePath = (input: string) => { + return realNodeModulePathInternal(resolve(input)); + }; + } + + /** + * Clears the cache of resolved symlinks. + * @public + */ + public clearCache(): void { + this._cache.clear(); + } + + /** + * Tries to read a symbolic link at the specified path. + * If the input is not a symbolic link, returns undefined. + * @param link - The link to try to read + * @returns The target of the symbolic link, or undefined if the input is not a symbolic link + */ + private _tryReadLink(link: string): string | undefined { + const cached: string | false | undefined = this._cache.get(link); + if (cached !== undefined) { + return cached || undefined; + } + + const cachedError: Error | undefined = this._errorCache.get(link); + if (cachedError) { + // Fill the properties but fix the stack trace. + throw Object.assign(new Error(cachedError.message), cachedError); + } + + // On Windows, calling `readlink` on a directory throws an EUNKOWN, not EINVAL, so just pay the cost + // of an lstat call. + try { + const stat: nodeFs.Stats | undefined = this._fs.lstatSync(link, this._lstatOptions); + if (stat?.isSymbolicLink()) { + // path.join(x, '.') will trim trailing slashes, if applicable + const result: string = this._path.join(this._fs.readlinkSync(link, 'utf8'), '.'); + return result; + } + + // Ensure we cache that this was not a symbolic link. + this._cache.set(link, false); + } catch (err) { + this._errorCache.set(link, err as Error); + } + } +} diff --git a/libraries/node-core-library/src/Sort.ts b/libraries/node-core-library/src/Sort.ts index 77ff843c67a..2a719d7ffae 100644 --- a/libraries/node-core-library/src/Sort.ts +++ b/libraries/node-core-library/src/Sort.ts @@ -148,7 +148,6 @@ export class Sort { * console.log(JSON.stringify(Array.from(map.keys()))); // ["aardvark","goose","zebra"] * ``` */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any public static sortMapKeys( map: Map, keyComparer: (x: K, y: K) => number = Sort.compareByValue @@ -216,7 +215,6 @@ export class Sort { * console.log(Array.from(set)); // ['aardvark', 'goose', 'zebra'] * ``` */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any public static sortSet(set: Set, comparer: (x: T, y: T) => number = Sort.compareByValue): void { // Sorting a set is expensive, so first check whether it's already sorted. if (Sort.isSorted(set, comparer)) { @@ -231,4 +229,60 @@ export class Sort { set.add(item); } } + + /** + * Sort the keys deeply given an object or an array. + * + * Doesn't handle cyclic reference. + * + * @param object - The object to be sorted + * + * @example + * + * ```ts + * console.log(Sort.sortKeys({ c: 3, b: 2, a: 1 })); // { a: 1, b: 2, c: 3} + * ``` + */ + public static sortKeys> | unknown[]>(object: T): T { + if (!isPlainObject(object) && !Array.isArray(object)) { + throw new TypeError(`Expected object or array`); + } + + return Array.isArray(object) ? (innerSortArray(object) as T) : (innerSortKeys(object) as T); + } +} + +function isPlainObject(obj: unknown): obj is object { + return obj !== null && typeof obj === 'object'; +} + +function innerSortArray(arr: unknown[]): unknown[] { + const result: unknown[] = []; + for (const entry of arr) { + if (Array.isArray(entry)) { + result.push(innerSortArray(entry)); + } else if (isPlainObject(entry)) { + result.push(innerSortKeys(entry)); + } else { + result.push(entry); + } + } + return result; +} + +function innerSortKeys(obj: Partial>): Partial> { + const result: Partial> = {}; + const keys: string[] = Object.keys(obj).sort(); + for (const key of keys) { + const value: unknown = obj[key]; + if (Array.isArray(value)) { + result[key] = innerSortArray(value); + } else if (isPlainObject(value)) { + result[key] = innerSortKeys(value); + } else { + result[key] = value; + } + } + + return result; } diff --git a/libraries/node-core-library/src/SubprocessTerminator.ts b/libraries/node-core-library/src/SubprocessTerminator.ts index ef7c8edb492..1c2b8f45b1a 100644 --- a/libraries/node-core-library/src/SubprocessTerminator.ts +++ b/libraries/node-core-library/src/SubprocessTerminator.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as child_process from 'child_process'; -import process from 'process'; +import type * as child_process from 'node:child_process'; +import process from 'node:process'; import { Executable } from './Executable'; @@ -30,6 +30,19 @@ interface ITrackedSubprocess { subprocessOptions: ISubprocessOptions; } +/** + * Whether the hooks are installed + */ +let _initialized: boolean = false; + +/** + * The list of registered child processes. Processes are removed from this set if they + * terminate on their own. + */ +const _subprocessesByPid: Map = new Map(); + +const _isWindows: boolean = process.platform === 'win32'; + /** * When a child process is created, registering it with the SubprocessTerminator will ensure * that the child gets terminated when the current process terminates. @@ -44,19 +57,6 @@ interface ITrackedSubprocess { * @beta */ export class SubprocessTerminator { - /** - * Whether the hooks are installed - */ - private static _initialized: boolean = false; - - /** - * The list of registered child processes. Processes are removed from this set if they - * terminate on their own. - */ - private static _subprocessesByPid: Map = new Map(); - - private static readonly _isWindows: boolean = process.platform === 'win32'; - /** * The recommended options when creating a child process. */ @@ -77,25 +77,28 @@ export class SubprocessTerminator { return; } - SubprocessTerminator._validateSubprocessOptions(subprocessOptions); + _validateSubprocessOptions(subprocessOptions); - SubprocessTerminator._ensureInitialized(); + _ensureInitialized(); // Closure variable - const pid: number = subprocess.pid; + const pid: number | undefined = subprocess.pid; + if (pid === undefined) { + // The process failed to spawn. + return; + } - subprocess.on('close', (code: number, signal: string): void => { - if (SubprocessTerminator._subprocessesByPid.has(pid)) { - SubprocessTerminator._logDebug(`untracking #${pid}`); - SubprocessTerminator._subprocessesByPid.delete(pid); + subprocess.on('close', (exitCode: number | null, signal: NodeJS.Signals | null): void => { + if (_subprocessesByPid.delete(pid)) { + _logDebug(`untracking #${pid}`); } }); - SubprocessTerminator._subprocessesByPid.set(pid, { + _subprocessesByPid.set(pid, { subprocess, subprocessOptions }); - SubprocessTerminator._logDebug(`tracking #${pid}`); + _logDebug(`tracking #${pid}`); } /** @@ -105,24 +108,27 @@ export class SubprocessTerminator { subprocess: child_process.ChildProcess, subprocessOptions: ISubprocessOptions ): void { - const pid: number = subprocess.pid; + const pid: number | undefined = subprocess.pid; + if (pid === undefined) { + // The process failed to spawn. + return; + } // Don't attempt to kill the same process twice - if (SubprocessTerminator._subprocessesByPid.has(pid)) { - SubprocessTerminator._logDebug(`untracking #${pid} via killProcessTree()`); - this._subprocessesByPid.delete(subprocess.pid); + if (_subprocessesByPid.delete(pid)) { + _logDebug(`untracking #${pid} via killProcessTree()`); } - SubprocessTerminator._validateSubprocessOptions(subprocessOptions); + _validateSubprocessOptions(subprocessOptions); if (typeof subprocess.exitCode === 'number') { // Process has already been killed return; } - SubprocessTerminator._logDebug(`terminating #${subprocess.pid}`); + _logDebug(`terminating #${pid}`); - if (SubprocessTerminator._isWindows) { + if (_isWindows) { // On Windows we have a problem that CMD.exe launches child processes, but when CMD.exe is killed // the child processes may continue running. Also if we send signals to CMD.exe the child processes // will not receive them. The safest solution is not to attempt a graceful shutdown, but simply @@ -131,7 +137,7 @@ export class SubprocessTerminator { '/T', // "Terminates the specified process and any child processes which were started by it." '/F', // Without this, TaskKill will try to use WM_CLOSE which doesn't work with CLI tools '/PID', - subprocess.pid.toString() + pid.toString() ]); if (result.status) { @@ -147,95 +153,94 @@ export class SubprocessTerminator { } } else { // Passing a negative PID terminates the entire group instead of just the one process - process.kill(-subprocess.pid, 'SIGKILL'); + process.kill(-pid, 'SIGKILL'); } } +} - // Install the hooks - private static _ensureInitialized(): void { - if (!SubprocessTerminator._initialized) { - SubprocessTerminator._initialized = true; +function _ensureInitialized(): void { + if (!_initialized) { + _initialized = true; - SubprocessTerminator._logDebug('initialize'); + _logDebug('initialize'); - process.prependListener('SIGTERM', SubprocessTerminator._onTerminateSignal); - process.prependListener('SIGINT', SubprocessTerminator._onTerminateSignal); + process.prependListener('SIGTERM', _onTerminateSignal); + process.prependListener('SIGINT', _onTerminateSignal); - process.prependListener('exit', SubprocessTerminator._onExit); - } + process.prependListener('exit', _onExit); } +} - // Uninstall the hooks and perform cleanup - private static _cleanupChildProcesses(): void { - if (SubprocessTerminator._initialized) { - SubprocessTerminator._initialized = false; +// Uninstall the hooks and perform cleanup +function _cleanupChildProcesses(): void { + if (_initialized) { + _initialized = false; - process.removeListener('SIGTERM', SubprocessTerminator._onTerminateSignal); - process.removeListener('SIGINT', SubprocessTerminator._onTerminateSignal); + process.removeListener('SIGTERM', _onTerminateSignal); + process.removeListener('SIGINT', _onTerminateSignal); - const trackedSubprocesses: ITrackedSubprocess[] = Array.from( - SubprocessTerminator._subprocessesByPid.values() - ); + const trackedSubprocesses: ITrackedSubprocess[] = Array.from(_subprocessesByPid.values()); - let firstError: Error | undefined = undefined; + let firstError: Error | undefined = undefined; - for (const trackedSubprocess of trackedSubprocesses) { - try { - SubprocessTerminator.killProcessTree(trackedSubprocess.subprocess, { detached: true }); - } catch (error) { - if (firstError === undefined) { - firstError = error as Error; - } + for (const trackedSubprocess of trackedSubprocesses) { + try { + SubprocessTerminator.killProcessTree(trackedSubprocess.subprocess, { detached: true }); + } catch (error) { + if (firstError === undefined) { + firstError = error as Error; } } + } - if (firstError !== undefined) { - // This is generally an unexpected error such as the TaskKill.exe command not being found, - // not a trivial issue such as a nonexistent PID. Since this occurs during process shutdown, - // we should not interfere with control flow by throwing an exception or calling process.exit(). - // So simply write to STDERR and ensure our exit code indicates the problem. - console.error('\nAn unexpected error was encountered while attempting to clean up child processes:'); - console.error(firstError.toString()); - if (!process.exitCode) { - process.exitCode = 1; - } + if (firstError !== undefined) { + // This is generally an unexpected error such as the TaskKill.exe command not being found, + // not a trivial issue such as a nonexistent PID. Since this occurs during process shutdown, + // we should not interfere with control flow by throwing an exception or calling process.exit(). + // So simply write to STDERR and ensure our exit code indicates the problem. + // eslint-disable-next-line no-console + console.error('\nAn unexpected error was encountered while attempting to clean up child processes:'); + // eslint-disable-next-line no-console + console.error(firstError.toString()); + if (!process.exitCode) { + process.exitCode = 1; } } } +} - private static _validateSubprocessOptions(subprocessOptions: ISubprocessOptions): void { - if (!SubprocessTerminator._isWindows) { - if (!subprocessOptions.detached) { - // Setting detached=true is what creates the process group that we use to kill the children - throw new Error('killProcessTree() requires detached=true on this operating system'); - } +function _validateSubprocessOptions(subprocessOptions: ISubprocessOptions): void { + if (!_isWindows) { + if (!subprocessOptions.detached) { + // Setting detached=true is what creates the process group that we use to kill the children + throw new Error('killProcessTree() requires detached=true on this operating system'); } } +} - private static _onExit(exitCode: number): void { - SubprocessTerminator._logDebug(`received exit(${exitCode})`); +function _onExit(exitCode: number): void { + _logDebug(`received exit(${exitCode})`); - SubprocessTerminator._cleanupChildProcesses(); + _cleanupChildProcesses(); - SubprocessTerminator._logDebug(`finished exit()`); - } + _logDebug(`finished exit()`); +} - private static _onTerminateSignal(signal: string): void { - SubprocessTerminator._logDebug(`received signal ${signal}`); +function _onTerminateSignal(signal: string): void { + _logDebug(`received signal ${signal}`); - SubprocessTerminator._cleanupChildProcesses(); + _cleanupChildProcesses(); - // When a listener is added to SIGTERM, Node.js strangely provides no way to reference - // the original handler. But we can invoke it by removing our listener and then resending - // the signal to our own process. - SubprocessTerminator._logDebug(`relaying ${signal}`); - process.kill(process.pid, signal); - } + // When a listener is added to SIGTERM, Node.js strangely provides no way to reference + // the original handler. But we can invoke it by removing our listener and then resending + // the signal to our own process. + _logDebug(`relaying ${signal}`); + process.kill(process.pid, signal); +} - // For debugging - private static _logDebug(message: string): void { - //const logLine: string = `SubprocessTerminator: [${process.pid}] ${message}`; - // fs.writeFileSync('trace.log', logLine + '\n', { flag: 'a' }); - //console.log(logLine); - } +// For debugging +function _logDebug(message: string): void { + //const logLine: string = `SubprocessTerminator: [${process.pid}] ${message}`; + // fs.writeFileSync('trace.log', logLine + '\n', { flag: 'a' }); + //console.log(logLine); } diff --git a/libraries/node-core-library/src/Terminal/AnsiEscape.ts b/libraries/node-core-library/src/Terminal/AnsiEscape.ts deleted file mode 100644 index 8cb03d0c3f4..00000000000 --- a/libraries/node-core-library/src/Terminal/AnsiEscape.ts +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { ConsoleColorCodes } from './Colors'; - -/** - * Options for {@link AnsiEscape.formatForTests}. - * @public - */ -export interface IAnsiEscapeConvertForTestsOptions { - /** - * If true then `\n` will be replaced by `[n]`, and `\r` will be replaced by `[r]`. - */ - encodeNewlines?: boolean; -} - -/** - * Operations for working with text strings that contain - * {@link https://en.wikipedia.org/wiki/ANSI_escape_code | ANSI escape codes}. - * The most commonly used escape codes set the foreground/background color for console output. - * @public - */ -export class AnsiEscape { - // For now, we only care about the Control Sequence Introducer (CSI) commands which always start with "[". - // eslint-disable-next-line no-control-regex - private static readonly _csiRegExp: RegExp = /\x1b\[([\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e])/gu; - - // Text coloring is performed using Select Graphic Rendition (SGR) codes, which come after the - // CSI introducer "ESC [". The SGR sequence is a number followed by "m". - private static readonly _sgrRegExp: RegExp = /([0-9]+)m/u; - - private static readonly _backslashNRegExp: RegExp = /\n/g; - private static readonly _backslashRRegExp: RegExp = /\r/g; - - /** - * Returns the input text with all ANSI escape codes removed. For example, this is useful when saving - * colorized console output to a log file. - */ - public static removeCodes(text: string): string { - // eslint-disable-next-line no-control-regex - return text.replace(AnsiEscape._csiRegExp, ''); - } - - /** - * Replaces ANSI escape codes with human-readable tokens. This is useful for unit tests - * that compare text strings in test assertions or snapshot files. - */ - public static formatForTests(text: string, options?: IAnsiEscapeConvertForTestsOptions): string { - if (!options) { - options = {}; - } - - let result: string = text.replace(AnsiEscape._csiRegExp, (capture: string, csiCode: string) => { - // If it is an SGR code, then try to show a friendly token - const match: RegExpMatchArray | null = csiCode.match(AnsiEscape._sgrRegExp); - if (match) { - const sgrParameter: number = parseInt(match[1]); - const sgrParameterName: string | undefined = AnsiEscape._tryGetSgrFriendlyName(sgrParameter); - if (sgrParameterName) { - // Example: "[black-bg]" - return `[${sgrParameterName}]`; - } - } - - // Otherwise show the raw code, but without the "[" from the CSI prefix - // Example: "[31m]" - return `[${csiCode}]`; - }); - - if (options.encodeNewlines) { - result = result - .replace(AnsiEscape._backslashNRegExp, '[n]') - .replace(AnsiEscape._backslashRRegExp, `[r]`); - } - return result; - } - - // Returns a human-readable token representing an SGR parameter, or undefined for parameter that is not well-known. - // The SGR parameter numbers are documented in this table: - // https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters - private static _tryGetSgrFriendlyName(sgiParameter: number): string | undefined { - switch (sgiParameter) { - case ConsoleColorCodes.BlackForeground: - return 'black'; - case ConsoleColorCodes.RedForeground: - return 'red'; - case ConsoleColorCodes.GreenForeground: - return 'green'; - case ConsoleColorCodes.YellowForeground: - return 'yellow'; - case ConsoleColorCodes.BlueForeground: - return 'blue'; - case ConsoleColorCodes.MagentaForeground: - return 'magenta'; - case ConsoleColorCodes.CyanForeground: - return 'cyan'; - case ConsoleColorCodes.WhiteForeground: - return 'white'; - case ConsoleColorCodes.GrayForeground: - return 'gray'; - case ConsoleColorCodes.DefaultForeground: - return 'default'; - - case ConsoleColorCodes.BlackBackground: - return 'black-bg'; - case ConsoleColorCodes.RedBackground: - return 'red-bg'; - case ConsoleColorCodes.GreenBackground: - return 'green-bg'; - case ConsoleColorCodes.YellowBackground: - return 'yellow-bg'; - case ConsoleColorCodes.BlueBackground: - return 'blue-bg'; - case ConsoleColorCodes.MagentaBackground: - return 'magenta-bg'; - case ConsoleColorCodes.CyanBackground: - return 'cyan-bg'; - case ConsoleColorCodes.WhiteBackground: - return 'white-bg'; - case ConsoleColorCodes.GrayBackground: - return 'gray-bg'; - case ConsoleColorCodes.DefaultBackground: - return 'default-bg'; - - case ConsoleColorCodes.Bold: - return 'bold'; - case ConsoleColorCodes.Dim: - return 'dim'; - case ConsoleColorCodes.NormalColorOrIntensity: - return 'normal'; - case ConsoleColorCodes.Underline: - return 'underline'; - case ConsoleColorCodes.UnderlineOff: - return 'underline-off'; - case ConsoleColorCodes.Blink: - return 'blink'; - case ConsoleColorCodes.BlinkOff: - return 'blink-off'; - case ConsoleColorCodes.InvertColor: - return 'invert'; - case ConsoleColorCodes.InvertColorOff: - return 'invert-off'; - case ConsoleColorCodes.Hidden: - return 'hidden'; - case ConsoleColorCodes.HiddenOff: - return 'hidden-off'; - default: - return undefined; - } - } -} diff --git a/libraries/node-core-library/src/Terminal/Colors.ts b/libraries/node-core-library/src/Terminal/Colors.ts deleted file mode 100644 index a1a10856b64..00000000000 --- a/libraries/node-core-library/src/Terminal/Colors.ts +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -/** - * @beta - */ -export interface IColorableSequence { - text: string; - isEol?: boolean; - foregroundColor?: ColorValue; - backgroundColor?: ColorValue; - textAttributes?: TextAttribute[]; -} - -export const eolSequence: IColorableSequence = { - isEol: true -} as IColorableSequence; - -/** - * Colors used with {@link IColorableSequence}. - * @beta - */ -export enum ColorValue { - Black, - Red, - Green, - Yellow, - Blue, - Magenta, - Cyan, - White, - Gray -} - -/** - * Text styles used with {@link IColorableSequence}. - * @beta - */ -export enum TextAttribute { - Bold, - Dim, - Underline, - Blink, - InvertColor, - Hidden -} - -export enum ConsoleColorCodes { - BlackForeground = 30, - RedForeground = 31, - GreenForeground = 32, - YellowForeground = 33, - BlueForeground = 34, - MagentaForeground = 35, - CyanForeground = 36, - WhiteForeground = 37, - GrayForeground = 90, - DefaultForeground = 39, - - BlackBackground = 40, - RedBackground = 41, - GreenBackground = 42, - YellowBackground = 43, - BlueBackground = 44, - MagentaBackground = 45, - CyanBackground = 46, - WhiteBackground = 47, - GrayBackground = 100, - DefaultBackground = 49, - - Bold = 1, - - // On Linux, the "BoldOff" code instead causes the text to be double-underlined: - // https://en.wikipedia.org/wiki/Talk:ANSI_escape_code#SGR_21%E2%80%94%60Bold_off%60_not_widely_supported - // Use "NormalColorOrIntensity" instead - // BoldOff = 21, - - Dim = 2, - NormalColorOrIntensity = 22, - Underline = 4, - UnderlineOff = 24, - Blink = 5, - BlinkOff = 25, - InvertColor = 7, - InvertColorOff = 27, - Hidden = 8, - HiddenOff = 28 -} - -/** - * The static functions on this class are used to produce colored text - * for use with the node-core-library terminal. - * - * @example - * terminal.writeLine(Colors.green('Green Text!'), ' ', Colors.blue('Blue Text!')); - * - * @beta - */ -export class Colors { - public static black(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - foregroundColor: ColorValue.Black - }; - } - - public static red(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - foregroundColor: ColorValue.Red - }; - } - - public static green(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - foregroundColor: ColorValue.Green - }; - } - - public static yellow(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - foregroundColor: ColorValue.Yellow - }; - } - - public static blue(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - foregroundColor: ColorValue.Blue - }; - } - - public static magenta(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - foregroundColor: ColorValue.Magenta - }; - } - - public static cyan(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - foregroundColor: ColorValue.Cyan - }; - } - - public static white(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - foregroundColor: ColorValue.White - }; - } - - public static gray(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - foregroundColor: ColorValue.Gray - }; - } - - public static blackBackground(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - backgroundColor: ColorValue.Black - }; - } - - public static redBackground(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - backgroundColor: ColorValue.Red - }; - } - - public static greenBackground(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - backgroundColor: ColorValue.Green - }; - } - - public static yellowBackground(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - backgroundColor: ColorValue.Yellow - }; - } - - public static blueBackground(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - backgroundColor: ColorValue.Blue - }; - } - - public static magentaBackground(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - backgroundColor: ColorValue.Magenta - }; - } - - public static cyanBackground(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - backgroundColor: ColorValue.Cyan - }; - } - - public static whiteBackground(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - backgroundColor: ColorValue.White - }; - } - - public static grayBackground(text: string | IColorableSequence): IColorableSequence { - return { - ...Colors._normalizeStringOrColorableSequence(text), - backgroundColor: ColorValue.Gray - }; - } - - public static bold(text: string | IColorableSequence): IColorableSequence { - return Colors._applyTextAttribute(text, TextAttribute.Bold); - } - - public static dim(text: string | IColorableSequence): IColorableSequence { - return Colors._applyTextAttribute(text, TextAttribute.Dim); - } - - public static underline(text: string | IColorableSequence): IColorableSequence { - return Colors._applyTextAttribute(text, TextAttribute.Underline); - } - - public static blink(text: string | IColorableSequence): IColorableSequence { - return Colors._applyTextAttribute(text, TextAttribute.Blink); - } - - public static invertColor(text: string | IColorableSequence): IColorableSequence { - return Colors._applyTextAttribute(text, TextAttribute.InvertColor); - } - - public static hidden(text: string | IColorableSequence): IColorableSequence { - return Colors._applyTextAttribute(text, TextAttribute.Hidden); - } - - /** - * If called with a string, returns the string wrapped in a {@link IColorableSequence}. - * If called with a {@link IColorableSequence}, returns the {@link IColorableSequence}. - * - * @internal - */ - public static _normalizeStringOrColorableSequence(value: string | IColorableSequence): IColorableSequence { - if (typeof value === 'string') { - return { - text: value - }; - } else { - return value; - } - } - - private static _applyTextAttribute( - text: string | IColorableSequence, - attribute: TextAttribute - ): IColorableSequence { - const sequence: IColorableSequence = Colors._normalizeStringOrColorableSequence(text); - if (!sequence.textAttributes) { - sequence.textAttributes = []; - } - - sequence.textAttributes.push(attribute); - return sequence; - } -} diff --git a/libraries/node-core-library/src/Terminal/ITerminal.ts b/libraries/node-core-library/src/Terminal/ITerminal.ts deleted file mode 100644 index 11cb245fb33..00000000000 --- a/libraries/node-core-library/src/Terminal/ITerminal.ts +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { ITerminalProvider } from './ITerminalProvider'; -import { IColorableSequence } from './Colors'; - -/** - * @beta - */ -export interface ITerminal { - /** - * Subscribe a new terminal provider. - */ - registerProvider(provider: ITerminalProvider): void; - - /** - * Unsubscribe a terminal provider. If the provider isn't subscribed, this function does nothing. - */ - unregisterProvider(provider: ITerminalProvider): void; - - /** - * Write a generic message to the terminal - */ - write(...messageParts: (string | IColorableSequence)[]): void; - - /** - * Write a generic message to the terminal, followed by a newline - */ - writeLine(...messageParts: (string | IColorableSequence)[]): void; - - /** - * Write a warning message to the console with yellow text. - * - * @remarks - * The yellow color takes precedence over any other foreground colors set. - */ - writeWarning(...messageParts: (string | IColorableSequence)[]): void; - - /** - * Write a warning message to the console with yellow text, followed by a newline. - * - * @remarks - * The yellow color takes precedence over any other foreground colors set. - */ - writeWarningLine(...messageParts: (string | IColorableSequence)[]): void; - - /** - * Write an error message to the console with red text. - * - * @remarks - * The red color takes precedence over any other foreground colors set. - */ - writeError(...messageParts: (string | IColorableSequence)[]): void; - - /** - * Write an error message to the console with red text, followed by a newline. - * - * @remarks - * The red color takes precedence over any other foreground colors set. - */ - writeErrorLine(...messageParts: (string | IColorableSequence)[]): void; - - /** - * Write a verbose-level message. - */ - writeVerbose(...messageParts: (string | IColorableSequence)[]): void; - - /** - * Write a verbose-level message followed by a newline. - */ - writeVerboseLine(...messageParts: (string | IColorableSequence)[]): void; - - /** - * Write a debug-level message. - */ - writeDebug(...messageParts: (string | IColorableSequence)[]): void; - - /** - * Write a debug-level message followed by a newline. - */ - writeDebugLine(...messageParts: (string | IColorableSequence)[]): void; -} diff --git a/libraries/node-core-library/src/Terminal/StringBufferTerminalProvider.ts b/libraries/node-core-library/src/Terminal/StringBufferTerminalProvider.ts deleted file mode 100644 index ff2359c5a96..00000000000 --- a/libraries/node-core-library/src/Terminal/StringBufferTerminalProvider.ts +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { ITerminalProvider, TerminalProviderSeverity } from './ITerminalProvider'; -import { StringBuilder } from '../StringBuilder'; -import { Text } from '../Text'; -import { AnsiEscape } from './AnsiEscape'; - -/** - * @beta - */ -export interface IStringBufferOutputOptions { - /** - * If set to true, special characters like \\n, \\r, and the \\u001b character - * in color control tokens will get normalized to [-n-], [-r-], and [-x-] respectively - * - * This option defaults to `true` - */ - normalizeSpecialCharacters: boolean; -} - -/** - * Terminal provider that stores written data in buffers separated by severity. - * This terminal provider is designed to be used when code that prints to a terminal - * is being unit tested. - * - * @beta - */ -export class StringBufferTerminalProvider implements ITerminalProvider { - private _standardBuffer: StringBuilder = new StringBuilder(); - private _verboseBuffer: StringBuilder = new StringBuilder(); - private _debugBuffer: StringBuilder = new StringBuilder(); - private _warningBuffer: StringBuilder = new StringBuilder(); - private _errorBuffer: StringBuilder = new StringBuilder(); - - private _supportsColor: boolean; - - public constructor(supportsColor: boolean = false) { - this._supportsColor = supportsColor; - } - - /** - * {@inheritDoc ITerminalProvider.write} - */ - public write(data: string, severity: TerminalProviderSeverity): void { - switch (severity) { - case TerminalProviderSeverity.warning: { - this._warningBuffer.append(data); - break; - } - - case TerminalProviderSeverity.error: { - this._errorBuffer.append(data); - break; - } - - case TerminalProviderSeverity.verbose: { - this._verboseBuffer.append(data); - break; - } - - case TerminalProviderSeverity.debug: { - this._debugBuffer.append(data); - break; - } - - case TerminalProviderSeverity.log: - default: { - this._standardBuffer.append(data); - break; - } - } - } - - /** - * {@inheritDoc ITerminalProvider.eolCharacter} - */ - public get eolCharacter(): string { - return '[n]'; - } - - /** - * {@inheritDoc ITerminalProvider.supportsColor} - */ - public get supportsColor(): boolean { - return this._supportsColor; - } - - /** - * Get everything that has been written at log-level severity. - */ - public getOutput(options?: IStringBufferOutputOptions): string { - return this._normalizeOutput(this._standardBuffer.toString(), options); - } - - /** - * Get everything that has been written at verbose-level severity. - */ - public getVerbose(options?: IStringBufferOutputOptions): string { - return this._normalizeOutput(this._verboseBuffer.toString(), options); - } - - /** - * Get everything that has been written at debug-level severity. - */ - public getDebugOutput(options?: IStringBufferOutputOptions): string { - return this._normalizeOutput(this._debugBuffer.toString(), options); - } - - /** - * Get everything that has been written at error-level severity. - */ - public getErrorOutput(options?: IStringBufferOutputOptions): string { - return this._normalizeOutput(this._errorBuffer.toString(), options); - } - - /** - * Get everything that has been written at warning-level severity. - */ - public getWarningOutput(options?: IStringBufferOutputOptions): string { - return this._normalizeOutput(this._warningBuffer.toString(), options); - } - - private _normalizeOutput(s: string, options: IStringBufferOutputOptions | undefined): string { - options = { - normalizeSpecialCharacters: true, - - ...(options || {}) - }; - - s = Text.convertToLf(s); - - if (options.normalizeSpecialCharacters) { - return AnsiEscape.formatForTests(s, { encodeNewlines: true }); - } else { - return s; - } - } -} diff --git a/libraries/node-core-library/src/Terminal/Terminal.ts b/libraries/node-core-library/src/Terminal/Terminal.ts deleted file mode 100644 index d0e88ad6d82..00000000000 --- a/libraries/node-core-library/src/Terminal/Terminal.ts +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { ITerminalProvider, TerminalProviderSeverity } from './ITerminalProvider'; -import { - IColorableSequence, - ColorValue, - Colors, - eolSequence, - TextAttribute, - ConsoleColorCodes -} from './Colors'; -import { ITerminal } from './ITerminal'; - -/** - * This class facilitates writing to a console. - * - * @beta - */ -export class Terminal implements ITerminal { - private _providers: Set; - - public constructor(provider: ITerminalProvider) { - this._providers = new Set(); - this._providers.add(provider); - } - - /** - * {@inheritdoc ITerminal.registerProvider} - */ - public registerProvider(provider: ITerminalProvider): void { - this._providers.add(provider); - } - - /** - * {@inheritdoc ITerminal.unregisterProvider} - */ - public unregisterProvider(provider: ITerminalProvider): void { - if (this._providers.has(provider)) { - this._providers.delete(provider); - } - } - - /** - * {@inheritdoc ITerminal.write} - */ - public write(...messageParts: (string | IColorableSequence)[]): void { - this._writeSegmentsToProviders(messageParts, TerminalProviderSeverity.log); - } - - /** - * {@inheritdoc ITerminal.writeLine} - */ - public writeLine(...messageParts: (string | IColorableSequence)[]): void { - this.write(...messageParts, eolSequence); - } - - /** - * {@inheritdoc ITerminal.writeWarning} - */ - public writeWarning(...messageParts: (string | IColorableSequence)[]): void { - this._writeSegmentsToProviders( - messageParts.map( - (part): IColorableSequence => ({ - ...Colors._normalizeStringOrColorableSequence(part), - foregroundColor: ColorValue.Yellow - }) - ), - TerminalProviderSeverity.warning - ); - } - - /** - * {@inheritdoc ITerminal.writeWarningLine} - */ - public writeWarningLine(...messageParts: (string | IColorableSequence)[]): void { - this._writeSegmentsToProviders( - [ - ...messageParts.map( - (part): IColorableSequence => ({ - ...Colors._normalizeStringOrColorableSequence(part), - foregroundColor: ColorValue.Yellow - }) - ), - eolSequence - ], - TerminalProviderSeverity.warning - ); - } - - /** - * {@inheritdoc ITerminal.writeError} - */ - public writeError(...messageParts: (string | IColorableSequence)[]): void { - this._writeSegmentsToProviders( - messageParts.map( - (part): IColorableSequence => ({ - ...Colors._normalizeStringOrColorableSequence(part), - foregroundColor: ColorValue.Red - }) - ), - TerminalProviderSeverity.error - ); - } - - /** - * {@inheritdoc ITerminal.writeErrorLine} - */ - public writeErrorLine(...messageParts: (string | IColorableSequence)[]): void { - this._writeSegmentsToProviders( - [ - ...messageParts.map( - (part): IColorableSequence => ({ - ...Colors._normalizeStringOrColorableSequence(part), - foregroundColor: ColorValue.Red - }) - ), - eolSequence - ], - TerminalProviderSeverity.error - ); - } - - /** - * {@inheritdoc ITerminal.writeVerbose} - */ - public writeVerbose(...messageParts: (string | IColorableSequence)[]): void { - this._writeSegmentsToProviders(messageParts, TerminalProviderSeverity.verbose); - } - - /** - * {@inheritdoc ITerminal.writeVerboseLine} - */ - public writeVerboseLine(...messageParts: (string | IColorableSequence)[]): void { - this.writeVerbose(...messageParts, eolSequence); - } - - /** - * {@inheritdoc ITerminal.writeDebug} - */ - public writeDebug(...messageParts: (string | IColorableSequence)[]): void { - this._writeSegmentsToProviders(messageParts, TerminalProviderSeverity.debug); - } - - /** - * {@inheritdoc ITerminal.writeDebugLine} - */ - public writeDebugLine(...messageParts: (string | IColorableSequence)[]): void { - this.writeDebug(...messageParts, eolSequence); - } - - private _writeSegmentsToProviders( - segments: (string | IColorableSequence)[], - severity: TerminalProviderSeverity - ): void { - const withColorText: { [eolChar: string]: string } = {}; - const withoutColorText: { [eolChar: string]: string } = {}; - let withColorLines: string[] | undefined; - let withoutColorLines: string[] | undefined; - - this._providers.forEach((provider) => { - const eol: string = provider.eolCharacter; - let textToWrite: string; - if (provider.supportsColor) { - if (!withColorLines) { - withColorLines = this._serializeFormattableTextSegments(segments, true); - } - - if (!withColorText[eol]) { - withColorText[eol] = withColorLines.join(eol); - } - - textToWrite = withColorText[eol]; - } else { - if (!withoutColorLines) { - withoutColorLines = this._serializeFormattableTextSegments(segments, false); - } - - if (!withoutColorText[eol]) { - withoutColorText[eol] = withoutColorLines.join(eol); - } - - textToWrite = withoutColorText[eol]; - } - - provider.write(textToWrite, severity); - }); - } - - private _serializeFormattableTextSegments( - segments: (string | IColorableSequence)[], - withColor: boolean - ): string[] { - const lines: string[] = []; - let segmentsToJoin: string[] = []; - let lastSegmentWasEol: boolean = false; - for (let i: number = 0; i < segments.length; i++) { - const segment: IColorableSequence = Colors._normalizeStringOrColorableSequence(segments[i]); - lastSegmentWasEol = !!segment.isEol; - if (lastSegmentWasEol) { - lines.push(segmentsToJoin.join('')); - segmentsToJoin = []; - } else { - if (withColor) { - const startColorCodes: number[] = []; - const endColorCodes: number[] = []; - switch (segment.foregroundColor) { - case ColorValue.Black: { - startColorCodes.push(ConsoleColorCodes.BlackForeground); - endColorCodes.push(ConsoleColorCodes.DefaultForeground); - break; - } - - case ColorValue.Red: { - startColorCodes.push(ConsoleColorCodes.RedForeground); - endColorCodes.push(ConsoleColorCodes.DefaultForeground); - break; - } - - case ColorValue.Green: { - startColorCodes.push(ConsoleColorCodes.GreenForeground); - endColorCodes.push(ConsoleColorCodes.DefaultForeground); - break; - } - - case ColorValue.Yellow: { - startColorCodes.push(ConsoleColorCodes.YellowForeground); - endColorCodes.push(ConsoleColorCodes.DefaultForeground); - break; - } - - case ColorValue.Blue: { - startColorCodes.push(ConsoleColorCodes.BlueForeground); - endColorCodes.push(ConsoleColorCodes.DefaultForeground); - break; - } - - case ColorValue.Magenta: { - startColorCodes.push(ConsoleColorCodes.MagentaForeground); - endColorCodes.push(ConsoleColorCodes.DefaultForeground); - break; - } - - case ColorValue.Cyan: { - startColorCodes.push(ConsoleColorCodes.CyanForeground); - endColorCodes.push(ConsoleColorCodes.DefaultForeground); - break; - } - - case ColorValue.White: { - startColorCodes.push(ConsoleColorCodes.WhiteForeground); - endColorCodes.push(ConsoleColorCodes.DefaultForeground); - break; - } - - case ColorValue.Gray: { - startColorCodes.push(ConsoleColorCodes.GrayForeground); - endColorCodes.push(ConsoleColorCodes.DefaultForeground); - break; - } - } - - switch (segment.backgroundColor) { - case ColorValue.Black: { - startColorCodes.push(ConsoleColorCodes.BlackBackground); - endColorCodes.push(ConsoleColorCodes.DefaultBackground); - break; - } - - case ColorValue.Red: { - startColorCodes.push(ConsoleColorCodes.RedBackground); - endColorCodes.push(ConsoleColorCodes.DefaultBackground); - break; - } - - case ColorValue.Green: { - startColorCodes.push(ConsoleColorCodes.GreenBackground); - endColorCodes.push(ConsoleColorCodes.DefaultBackground); - break; - } - - case ColorValue.Yellow: { - startColorCodes.push(ConsoleColorCodes.YellowBackground); - endColorCodes.push(ConsoleColorCodes.DefaultBackground); - break; - } - - case ColorValue.Blue: { - startColorCodes.push(ConsoleColorCodes.BlueBackground); - endColorCodes.push(ConsoleColorCodes.DefaultBackground); - break; - } - - case ColorValue.Magenta: { - startColorCodes.push(ConsoleColorCodes.MagentaBackground); - endColorCodes.push(ConsoleColorCodes.DefaultBackground); - break; - } - - case ColorValue.Cyan: { - startColorCodes.push(ConsoleColorCodes.CyanBackground); - endColorCodes.push(ConsoleColorCodes.DefaultBackground); - break; - } - - case ColorValue.White: { - startColorCodes.push(ConsoleColorCodes.WhiteBackground); - endColorCodes.push(ConsoleColorCodes.DefaultBackground); - break; - } - - case ColorValue.Gray: { - startColorCodes.push(ConsoleColorCodes.GrayBackground); - endColorCodes.push(49); - break; - } - } - - if (segment.textAttributes) { - for (const textAttribute of segment.textAttributes) { - switch (textAttribute) { - case TextAttribute.Bold: { - startColorCodes.push(ConsoleColorCodes.Bold); - endColorCodes.push(ConsoleColorCodes.NormalColorOrIntensity); - break; - } - - case TextAttribute.Dim: { - startColorCodes.push(ConsoleColorCodes.Dim); - endColorCodes.push(ConsoleColorCodes.NormalColorOrIntensity); - break; - } - - case TextAttribute.Underline: { - startColorCodes.push(ConsoleColorCodes.Underline); - endColorCodes.push(ConsoleColorCodes.UnderlineOff); - break; - } - - case TextAttribute.Blink: { - startColorCodes.push(ConsoleColorCodes.Blink); - endColorCodes.push(ConsoleColorCodes.BlinkOff); - break; - } - - case TextAttribute.InvertColor: { - startColorCodes.push(ConsoleColorCodes.InvertColor); - endColorCodes.push(ConsoleColorCodes.InvertColorOff); - break; - } - - case TextAttribute.Hidden: { - startColorCodes.push(ConsoleColorCodes.Hidden); - endColorCodes.push(ConsoleColorCodes.HiddenOff); - break; - } - } - } - } - - for (let j: number = 0; j < startColorCodes.length; j++) { - const code: number = startColorCodes[j]; - segmentsToJoin.push(...['\u001b[', code.toString(), 'm']); - } - - segmentsToJoin.push(segment.text); - - for (let j: number = endColorCodes.length - 1; j >= 0; j--) { - const code: number = endColorCodes[j]; - segmentsToJoin.push(...['\u001b[', code.toString(), 'm']); - } - } else { - segmentsToJoin.push(segment.text); - } - } - } - - if (segmentsToJoin.length > 0) { - lines.push(segmentsToJoin.join('')); - } - - if (lastSegmentWasEol) { - lines.push(''); - } - - return lines; - } -} diff --git a/libraries/node-core-library/src/Terminal/TerminalWritable.ts b/libraries/node-core-library/src/Terminal/TerminalWritable.ts deleted file mode 100644 index 8681ac5af29..00000000000 --- a/libraries/node-core-library/src/Terminal/TerminalWritable.ts +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import type { ITerminal } from './ITerminal'; -import { TerminalProviderSeverity } from './ITerminalProvider'; -import { Writable, type WritableOptions } from 'stream'; - -/** - * Options for {@link TerminalWritable}. - * - * @beta - */ -export interface ITerminalWritableOptions { - /** - * The {@link ITerminal} that the Writable will write to. - */ - terminal: ITerminal; - /** - * The severity of the messages that will be written to the {@link ITerminal}. - */ - severity: TerminalProviderSeverity; - /** - * Options for the underlying Writable. - */ - writableOptions?: WritableOptions; -} - -/** - * A adapter to allow writing to a provided terminal using Writable streams. - * - * @beta - */ -export class TerminalWritable extends Writable { - private _writeMethod: (data: string) => void; - - public constructor(options: ITerminalWritableOptions) { - const { terminal, severity, writableOptions } = options; - super(writableOptions); - - this._writev = undefined; - switch (severity) { - case TerminalProviderSeverity.log: - this._writeMethod = terminal.write.bind(terminal); - break; - case TerminalProviderSeverity.verbose: - this._writeMethod = terminal.writeVerbose.bind(terminal); - break; - case TerminalProviderSeverity.debug: - this._writeMethod = terminal.writeDebug.bind(terminal); - break; - case TerminalProviderSeverity.warning: - this._writeMethod = terminal.writeWarning.bind(terminal); - break; - case TerminalProviderSeverity.error: - this._writeMethod = terminal.writeError.bind(terminal); - break; - default: - throw new Error(`Unknown severity: ${severity}`); - } - } - - public _write( - chunk: string | Buffer | Uint8Array, - encoding: string, - // eslint-disable-next-line @rushstack/no-new-null - callback: (error?: Error | null) => void - ): void { - try { - const chunkData: string | Buffer = typeof chunk === 'string' ? chunk : Buffer.from(chunk); - this._writeMethod(chunkData.toString()); - } catch (e: unknown) { - callback(e as Error); - return; - } - callback(); - } -} diff --git a/libraries/node-core-library/src/Terminal/test/AnsiEscape.test.ts b/libraries/node-core-library/src/Terminal/test/AnsiEscape.test.ts deleted file mode 100644 index 00a926fa60a..00000000000 --- a/libraries/node-core-library/src/Terminal/test/AnsiEscape.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import * as colors from 'colors'; -import { AnsiEscape } from '../AnsiEscape'; - -describe(AnsiEscape.name, () => { - let initialColorsEnabled: boolean; - - beforeAll(() => { - initialColorsEnabled = colors.enabled; - colors.enable(); - }); - - afterAll(() => { - if (!initialColorsEnabled) { - colors.disable(); - } - }); - - test('calls removeCodes() successfully', () => { - const coloredInput: string = colors.rainbow('Hello, world!'); - const decoloredInput: string = AnsiEscape.removeCodes(coloredInput); - expect(coloredInput).not.toBe(decoloredInput); - expect(decoloredInput).toBe('Hello, world!'); - }); -}); diff --git a/libraries/node-core-library/src/Terminal/test/Colors.test.ts b/libraries/node-core-library/src/Terminal/test/Colors.test.ts deleted file mode 100644 index db9c79f92d3..00000000000 --- a/libraries/node-core-library/src/Terminal/test/Colors.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { Terminal } from '../Terminal'; -import { StringBufferTerminalProvider } from '../StringBufferTerminalProvider'; -import { createColorGrid } from './createColorGrid'; -import { AnsiEscape } from '../AnsiEscape'; -import { Colors } from '../Colors'; - -describe(Colors.name, () => { - let terminal: Terminal; - let provider: StringBufferTerminalProvider; - - beforeEach(() => { - provider = new StringBufferTerminalProvider(true); - terminal = new Terminal(provider); - }); - - test('writes color grid correctly', () => { - for (const line of createColorGrid()) { - terminal.writeLine(...line); - } - - expect(provider.getOutput()).toMatchSnapshot(); - }); - - test('correctly normalizes color codes for tests', () => { - for (const line of createColorGrid()) { - terminal.writeLine(...line); - } - - expect( - AnsiEscape.formatForTests(provider.getOutput({ normalizeSpecialCharacters: false })) - ).toMatchSnapshot(); - }); -}); diff --git a/libraries/node-core-library/src/Terminal/test/Terminal.test.ts b/libraries/node-core-library/src/Terminal/test/Terminal.test.ts deleted file mode 100644 index e59e52f4d77..00000000000 --- a/libraries/node-core-library/src/Terminal/test/Terminal.test.ts +++ /dev/null @@ -1,629 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { Terminal } from '../Terminal'; -import { StringBufferTerminalProvider } from '../StringBufferTerminalProvider'; -import { Colors } from '../Colors'; - -let terminal: Terminal; -let provider: StringBufferTerminalProvider; - -function verifyProvider(): void { - expect({ - log: provider.getOutput(), - warning: provider.getWarningOutput(), - error: provider.getErrorOutput(), - verbose: provider.getVerbose(), - debug: provider.getDebugOutput() - }).toMatchSnapshot(); -} - -describe('01 color enabled', () => { - beforeEach(() => { - provider = new StringBufferTerminalProvider(true); - terminal = new Terminal(provider); - }); - - describe('01 basic terminal functions', () => { - describe('01 write', () => { - test('01 writes a single message', () => { - terminal.write('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.write('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.write(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.write(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.write('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - verifyProvider(); - }); - }); - - describe('02 writeLine', () => { - test('01 writes a single message', () => { - terminal.writeLine('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeLine('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeLine(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeLine(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeLine('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - verifyProvider(); - }); - }); - - describe('03 writeWarning', () => { - test('01 writes a single message', () => { - terminal.writeWarning('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeWarning('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeWarning(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeWarning(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeWarning('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - verifyProvider(); - }); - }); - - describe('04 writeWarningLine', () => { - test('01 writes a single message', () => { - terminal.writeWarningLine('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeWarningLine('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeWarningLine(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeWarningLine(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeWarningLine( - 'message 1', - Colors.green('message 2'), - 'message 3', - Colors.red('message 4') - ); - verifyProvider(); - }); - }); - - describe('05 writeError', () => { - test('01 writes a single message', () => { - terminal.writeError('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeError('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeError(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeError(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeError('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - verifyProvider(); - }); - }); - - describe('06 writeErrorLine', () => { - test('01 writes a single message', () => { - terminal.writeErrorLine('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeErrorLine('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeErrorLine(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeErrorLine(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeErrorLine('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - verifyProvider(); - }); - }); - - describe('07 writeVerbose', () => { - test('01 writes a single message', () => { - terminal.writeVerbose('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeVerbose('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeVerbose(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeVerbose(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeVerbose('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - verifyProvider(); - }); - }); - - describe('08 writeVerboseLine', () => { - test('01 writes a single message', () => { - terminal.writeVerboseLine('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeVerboseLine('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeVerboseLine(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeVerboseLine(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeVerboseLine( - 'message 1', - Colors.green('message 2'), - 'message 3', - Colors.red('message 4') - ); - verifyProvider(); - }); - }); - }); - - test('05 writes to multiple streams', () => { - terminal.write('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.writeWarningLine('message 1', 'message 2'); - terminal.writeVerbose('test message'); - terminal.writeVerbose(Colors.green('message 1')); - terminal.writeLine(Colors.green('message 1')); - terminal.writeError('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.writeErrorLine('test message'); - terminal.writeVerboseLine('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.writeVerboseLine('test message'); - terminal.writeWarning(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeWarning('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.writeError('message 1', 'message 2'); - terminal.write(Colors.green('message 1')); - terminal.writeVerbose('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.writeErrorLine('message 1', 'message 2'); - terminal.write(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeVerbose('message 1', 'message 2'); - terminal.writeVerboseLine(Colors.green('message 1')); - terminal.writeLine(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeError(Colors.green('message 1')); - terminal.writeWarningLine('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.write('test message'); - terminal.writeWarningLine('test message'); - terminal.writeVerboseLine(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeVerboseLine('message 1', 'message 2'); - terminal.writeErrorLine('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.writeLine('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.writeWarning('message 1', 'message 2'); - terminal.writeErrorLine(Colors.green('message 1')); - terminal.write('message 1', 'message 2'); - terminal.writeVerbose(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeWarning(Colors.green('message 1')); - terminal.writeLine('test message'); - terminal.writeError('test message'); - terminal.writeLine('message 1', 'message 2'); - terminal.writeErrorLine(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeError(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeWarningLine(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeWarningLine(Colors.green('message 1')); - verifyProvider(); - }); -}); - -describe('02 color disabled', () => { - beforeEach(() => { - provider = new StringBufferTerminalProvider(false); - terminal = new Terminal(provider); - }); - - describe('01 basic terminal functions', () => { - describe('01 write', () => { - test('01 writes a single message', () => { - terminal.write('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.write('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.write(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.write(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.write('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - verifyProvider(); - }); - }); - - describe('02 writeLine', () => { - test('01 writes a single message', () => { - terminal.writeLine('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeLine('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeLine(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeLine(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeLine('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - verifyProvider(); - }); - }); - - describe('03 writeWarning', () => { - test('01 writes a single message', () => { - terminal.writeWarning('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeWarning('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeWarning(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeWarning(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeWarning('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - verifyProvider(); - }); - }); - - describe('04 writeWarningLine', () => { - test('01 writes a single message', () => { - terminal.writeWarningLine('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeWarningLine('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeWarningLine(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeWarningLine(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeWarningLine( - 'message 1', - Colors.green('message 2'), - 'message 3', - Colors.red('message 4') - ); - verifyProvider(); - }); - }); - - describe('05 writeError', () => { - test('01 writes a single message', () => { - terminal.writeError('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeError('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeError(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeError(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeError('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - verifyProvider(); - }); - }); - - describe('06 writeErrorLine', () => { - test('01 writes a single message', () => { - terminal.writeErrorLine('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeErrorLine('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeErrorLine(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeErrorLine(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeErrorLine('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - verifyProvider(); - }); - }); - - describe('07 writeVerbose', () => { - test('01 writes a single message', () => { - terminal.writeVerbose('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeVerbose('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeVerbose(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeVerbose(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeVerbose('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - verifyProvider(); - }); - }); - - describe('08 writeVerboseLine', () => { - test('01 writes a single message', () => { - terminal.writeVerboseLine('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeVerboseLine('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeVerboseLine(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeVerboseLine(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeVerboseLine( - 'message 1', - Colors.green('message 2'), - 'message 3', - Colors.red('message 4') - ); - verifyProvider(); - }); - }); - - describe('09 writeDebug', () => { - test('01 writes a single message', () => { - terminal.writeDebug('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeDebug('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeDebug(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeDebug(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeDebug('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - verifyProvider(); - }); - }); - - describe('10 writeDebugLine', () => { - test('01 writes a single message', () => { - terminal.writeDebugLine('test message'); - verifyProvider(); - }); - - test('02 writes multiple messages', () => { - terminal.writeDebugLine('message 1', 'message 2'); - verifyProvider(); - }); - - test('03 writes a message with colors', () => { - terminal.writeDebugLine(Colors.green('message 1')); - verifyProvider(); - }); - - test('04 writes a multiple messages with colors', () => { - terminal.writeDebugLine(Colors.green('message 1'), Colors.red('message 2')); - verifyProvider(); - }); - - test('05 writes a messages with colors interspersed with non-colored messages', () => { - terminal.writeDebugLine('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - verifyProvider(); - }); - }); - }); - - test('05 writes to multiple streams', () => { - terminal.write('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.writeWarningLine('message 1', 'message 2'); - terminal.writeVerbose('test message'); - terminal.writeVerbose(Colors.green('message 1')); - terminal.writeLine(Colors.green('message 1')); - terminal.writeError('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.writeErrorLine('test message'); - terminal.writeVerboseLine('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.writeVerboseLine('test message'); - terminal.writeWarning(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeWarning('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.writeError('message 1', 'message 2'); - terminal.write(Colors.green('message 1')); - terminal.writeVerbose('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.writeErrorLine('message 1', 'message 2'); - terminal.write(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeVerbose('message 1', 'message 2'); - terminal.writeVerboseLine(Colors.green('message 1')); - terminal.writeLine(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeError(Colors.green('message 1')); - terminal.writeWarningLine('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.write('test message'); - terminal.writeWarningLine('test message'); - terminal.writeVerboseLine(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeVerboseLine('message 1', 'message 2'); - terminal.writeErrorLine('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.writeLine('message 1', Colors.green('message 2'), 'message 3', Colors.red('message 4')); - terminal.writeWarning('message 1', 'message 2'); - terminal.writeErrorLine(Colors.green('message 1')); - terminal.write('message 1', 'message 2'); - terminal.writeVerbose(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeWarning(Colors.green('message 1')); - terminal.writeLine('test message'); - terminal.writeError('test message'); - terminal.writeLine('message 1', 'message 2'); - terminal.writeErrorLine(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeError(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeWarningLine(Colors.green('message 1'), Colors.red('message 2')); - terminal.writeWarningLine(Colors.green('message 1')); - verifyProvider(); - }); -}); diff --git a/libraries/node-core-library/src/Terminal/test/TerminalWritable.test.ts b/libraries/node-core-library/src/Terminal/test/TerminalWritable.test.ts deleted file mode 100644 index 1bed1228e7c..00000000000 --- a/libraries/node-core-library/src/Terminal/test/TerminalWritable.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { Terminal } from '../Terminal'; -import { StringBufferTerminalProvider } from '../StringBufferTerminalProvider'; -import { TerminalWritable } from '../TerminalWritable'; -import { TerminalProviderSeverity } from '../ITerminalProvider'; -import { Writable } from 'stream'; - -let terminal: Terminal; -let provider: StringBufferTerminalProvider; - -function verifyProvider(): void { - expect({ - log: provider.getOutput(), - warning: provider.getWarningOutput(), - error: provider.getErrorOutput(), - verbose: provider.getVerbose(), - debug: provider.getDebugOutput() - }).toMatchSnapshot(); -} - -async function writeAsync(writable: Writable, data: string): Promise { - await new Promise((resolve: () => void, reject: (error: Error) => void) => { - // eslint-disable-next-line @rushstack/no-new-null - writable.write(data, (error?: Error | null) => { - if (error) { - reject(error); - } else { - resolve(); - } - }); - }); -} - -describe(TerminalWritable.name, () => { - beforeEach(() => { - provider = new StringBufferTerminalProvider(true); - terminal = new Terminal(provider); - }); - - test('writes a message', async () => { - const writable: TerminalWritable = new TerminalWritable({ - terminal, - severity: TerminalProviderSeverity.log - }); - - await writeAsync(writable, 'test message'); - verifyProvider(); - }); - - test('writes a verbose message', async () => { - const writable: TerminalWritable = new TerminalWritable({ - terminal, - severity: TerminalProviderSeverity.verbose - }); - - await writeAsync(writable, 'test message'); - verifyProvider(); - }); - - test('writes a debug message', async () => { - const writable: TerminalWritable = new TerminalWritable({ - terminal, - severity: TerminalProviderSeverity.debug - }); - - await writeAsync(writable, 'test message'); - verifyProvider(); - }); - - test('writes a warning message', async () => { - const writable: TerminalWritable = new TerminalWritable({ - terminal, - severity: TerminalProviderSeverity.warning - }); - - await writeAsync(writable, 'test message'); - verifyProvider(); - }); - - test('writes an error message', async () => { - const writable: TerminalWritable = new TerminalWritable({ - terminal, - severity: TerminalProviderSeverity.error - }); - - await writeAsync(writable, 'test message'); - verifyProvider(); - }); -}); diff --git a/libraries/node-core-library/src/Terminal/test/__snapshots__/Colors.test.ts.snap b/libraries/node-core-library/src/Terminal/test/__snapshots__/Colors.test.ts.snap deleted file mode 100644 index cdda0ea5d10..00000000000 --- a/libraries/node-core-library/src/Terminal/test/__snapshots__/Colors.test.ts.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Colors correctly normalizes color codes for tests 1`] = `"X[black]X[default][white]X[default][gray]X[default][magenta]X[default][red]X[default][yellow]X[default][green]X[default][cyan]X[default][blue]X[default][n][black-bg]X[default-bg][black][black-bg]X[default-bg][default][white][black-bg]X[default-bg][default][gray][black-bg]X[default-bg][default][magenta][black-bg]X[default-bg][default][red][black-bg]X[default-bg][default][yellow][black-bg]X[default-bg][default][green][black-bg]X[default-bg][default][cyan][black-bg]X[default-bg][default][blue][black-bg]X[default-bg][default][n][white-bg]X[default-bg][black][white-bg]X[default-bg][default][white][white-bg]X[default-bg][default][gray][white-bg]X[default-bg][default][magenta][white-bg]X[default-bg][default][red][white-bg]X[default-bg][default][yellow][white-bg]X[default-bg][default][green][white-bg]X[default-bg][default][cyan][white-bg]X[default-bg][default][blue][white-bg]X[default-bg][default][n][gray-bg]X[default-bg][black][gray-bg]X[default-bg][default][white][gray-bg]X[default-bg][default][gray][gray-bg]X[default-bg][default][magenta][gray-bg]X[default-bg][default][red][gray-bg]X[default-bg][default][yellow][gray-bg]X[default-bg][default][green][gray-bg]X[default-bg][default][cyan][gray-bg]X[default-bg][default][blue][gray-bg]X[default-bg][default][n][magenta-bg]X[default-bg][black][magenta-bg]X[default-bg][default][white][magenta-bg]X[default-bg][default][gray][magenta-bg]X[default-bg][default][magenta][magenta-bg]X[default-bg][default][red][magenta-bg]X[default-bg][default][yellow][magenta-bg]X[default-bg][default][green][magenta-bg]X[default-bg][default][cyan][magenta-bg]X[default-bg][default][blue][magenta-bg]X[default-bg][default][n][red-bg]X[default-bg][black][red-bg]X[default-bg][default][white][red-bg]X[default-bg][default][gray][red-bg]X[default-bg][default][magenta][red-bg]X[default-bg][default][red][red-bg]X[default-bg][default][yellow][red-bg]X[default-bg][default][green][red-bg]X[default-bg][default][cyan][red-bg]X[default-bg][default][blue][red-bg]X[default-bg][default][n][yellow-bg]X[default-bg][black][yellow-bg]X[default-bg][default][white][yellow-bg]X[default-bg][default][gray][yellow-bg]X[default-bg][default][magenta][yellow-bg]X[default-bg][default][red][yellow-bg]X[default-bg][default][yellow][yellow-bg]X[default-bg][default][green][yellow-bg]X[default-bg][default][cyan][yellow-bg]X[default-bg][default][blue][yellow-bg]X[default-bg][default][n][green-bg]X[default-bg][black][green-bg]X[default-bg][default][white][green-bg]X[default-bg][default][gray][green-bg]X[default-bg][default][magenta][green-bg]X[default-bg][default][red][green-bg]X[default-bg][default][yellow][green-bg]X[default-bg][default][green][green-bg]X[default-bg][default][cyan][green-bg]X[default-bg][default][blue][green-bg]X[default-bg][default][n][cyan-bg]X[default-bg][black][cyan-bg]X[default-bg][default][white][cyan-bg]X[default-bg][default][gray][cyan-bg]X[default-bg][default][magenta][cyan-bg]X[default-bg][default][red][cyan-bg]X[default-bg][default][yellow][cyan-bg]X[default-bg][default][green][cyan-bg]X[default-bg][default][cyan][cyan-bg]X[default-bg][default][blue][cyan-bg]X[default-bg][default][n][blue-bg]X[default-bg][black][blue-bg]X[default-bg][default][white][blue-bg]X[default-bg][default][gray][blue-bg]X[default-bg][default][magenta][blue-bg]X[default-bg][default][red][blue-bg]X[default-bg][default][yellow][blue-bg]X[default-bg][default][green][blue-bg]X[default-bg][default][cyan][blue-bg]X[default-bg][default][blue][blue-bg]X[default-bg][default][n]"`; - -exports[`Colors writes color grid correctly 1`] = `"X[black]X[default][white]X[default][gray]X[default][magenta]X[default][red]X[default][yellow]X[default][green]X[default][cyan]X[default][blue]X[default][n][black-bg]X[default-bg][black][black-bg]X[default-bg][default][white][black-bg]X[default-bg][default][gray][black-bg]X[default-bg][default][magenta][black-bg]X[default-bg][default][red][black-bg]X[default-bg][default][yellow][black-bg]X[default-bg][default][green][black-bg]X[default-bg][default][cyan][black-bg]X[default-bg][default][blue][black-bg]X[default-bg][default][n][white-bg]X[default-bg][black][white-bg]X[default-bg][default][white][white-bg]X[default-bg][default][gray][white-bg]X[default-bg][default][magenta][white-bg]X[default-bg][default][red][white-bg]X[default-bg][default][yellow][white-bg]X[default-bg][default][green][white-bg]X[default-bg][default][cyan][white-bg]X[default-bg][default][blue][white-bg]X[default-bg][default][n][gray-bg]X[default-bg][black][gray-bg]X[default-bg][default][white][gray-bg]X[default-bg][default][gray][gray-bg]X[default-bg][default][magenta][gray-bg]X[default-bg][default][red][gray-bg]X[default-bg][default][yellow][gray-bg]X[default-bg][default][green][gray-bg]X[default-bg][default][cyan][gray-bg]X[default-bg][default][blue][gray-bg]X[default-bg][default][n][magenta-bg]X[default-bg][black][magenta-bg]X[default-bg][default][white][magenta-bg]X[default-bg][default][gray][magenta-bg]X[default-bg][default][magenta][magenta-bg]X[default-bg][default][red][magenta-bg]X[default-bg][default][yellow][magenta-bg]X[default-bg][default][green][magenta-bg]X[default-bg][default][cyan][magenta-bg]X[default-bg][default][blue][magenta-bg]X[default-bg][default][n][red-bg]X[default-bg][black][red-bg]X[default-bg][default][white][red-bg]X[default-bg][default][gray][red-bg]X[default-bg][default][magenta][red-bg]X[default-bg][default][red][red-bg]X[default-bg][default][yellow][red-bg]X[default-bg][default][green][red-bg]X[default-bg][default][cyan][red-bg]X[default-bg][default][blue][red-bg]X[default-bg][default][n][yellow-bg]X[default-bg][black][yellow-bg]X[default-bg][default][white][yellow-bg]X[default-bg][default][gray][yellow-bg]X[default-bg][default][magenta][yellow-bg]X[default-bg][default][red][yellow-bg]X[default-bg][default][yellow][yellow-bg]X[default-bg][default][green][yellow-bg]X[default-bg][default][cyan][yellow-bg]X[default-bg][default][blue][yellow-bg]X[default-bg][default][n][green-bg]X[default-bg][black][green-bg]X[default-bg][default][white][green-bg]X[default-bg][default][gray][green-bg]X[default-bg][default][magenta][green-bg]X[default-bg][default][red][green-bg]X[default-bg][default][yellow][green-bg]X[default-bg][default][green][green-bg]X[default-bg][default][cyan][green-bg]X[default-bg][default][blue][green-bg]X[default-bg][default][n][cyan-bg]X[default-bg][black][cyan-bg]X[default-bg][default][white][cyan-bg]X[default-bg][default][gray][cyan-bg]X[default-bg][default][magenta][cyan-bg]X[default-bg][default][red][cyan-bg]X[default-bg][default][yellow][cyan-bg]X[default-bg][default][green][cyan-bg]X[default-bg][default][cyan][cyan-bg]X[default-bg][default][blue][cyan-bg]X[default-bg][default][n][blue-bg]X[default-bg][black][blue-bg]X[default-bg][default][white][blue-bg]X[default-bg][default][gray][blue-bg]X[default-bg][default][magenta][blue-bg]X[default-bg][default][red][blue-bg]X[default-bg][default][yellow][blue-bg]X[default-bg][default][green][blue-bg]X[default-bg][default][cyan][blue-bg]X[default-bg][default][blue][blue-bg]X[default-bg][default][n]"`; diff --git a/libraries/node-core-library/src/Terminal/test/__snapshots__/PrefixProxyTerminalProvider.test.ts.snap b/libraries/node-core-library/src/Terminal/test/__snapshots__/PrefixProxyTerminalProvider.test.ts.snap deleted file mode 100644 index 561059efad4..00000000000 --- a/libraries/node-core-library/src/Terminal/test/__snapshots__/PrefixProxyTerminalProvider.test.ts.snap +++ /dev/null @@ -1,181 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`PrefixProxyTerminalProvider With a dynamic prefix write writes a message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix (0)] test message", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a dynamic prefix write writes a message with newlines 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix (0)] message 1[n][prefix (1)] message 2[n][prefix (2)] message 3", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a dynamic prefix write writes a message with provider newlines 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix (0)] message 1[n][prefix (1)] message 2[n][prefix (2)] message 3", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a dynamic prefix write writes a mix of messages with and without newlines 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix (0)] message 1message 2[n][prefix (1)] message 3[n][prefix (2)] message 4message 5[n][prefix (3)] message 6", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a dynamic prefix write writes messages without newlines 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix (0)] message 1message 2message 3", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a dynamic prefix writeLine writes a message line 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix (0)] test message[n]", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a dynamic prefix writeLine writes a message line with newlines 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix (0)] message 1[n][prefix (1)] message 2[n][prefix (2)] message 3[n]", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a dynamic prefix writeLine writes a message line with provider newlines 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix (0)] message 1[n][prefix (1)] message 2[n][prefix (2)] message 3[n]", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a dynamic prefix writeLine writes a mix of message lines with and without newlines 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix (0)] message 1[n][prefix (1)] message 2[n][prefix (2)] message 3[n][prefix (3)] [n][prefix (4)] message 4[n][prefix (5)] message 5[n][prefix (6)] message 6[n]", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a static prefix write writes a message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix] test message", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a static prefix write writes a message with newlines 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix] message 1[n][prefix] message 2[n][prefix] message 3", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a static prefix write writes a message with provider newlines 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix] message 1[n][prefix] message 2[n][prefix] message 3", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a static prefix write writes a mix of messages with and without newlines 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix] message 1message 2[n][prefix] message 3[n][prefix] message 4message 5[n][prefix] message 6", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a static prefix write writes messages without newlines 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix] message 1message 2message 3", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a static prefix writeLine writes a message line 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix] test message[n]", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a static prefix writeLine writes a message line with newlines 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix] message 1[n][prefix] message 2[n][prefix] message 3[n]", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a static prefix writeLine writes a message line with provider newlines 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix] message 1[n][prefix] message 2[n][prefix] message 3[n]", - "verbose": "", - "warning": "", -} -`; - -exports[`PrefixProxyTerminalProvider With a static prefix writeLine writes a mix of message lines with and without newlines 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[prefix] message 1[n][prefix] message 2[n][prefix] message 3[n][prefix] [n][prefix] message 4[n][prefix] message 5[n][prefix] message 6[n]", - "verbose": "", - "warning": "", -} -`; diff --git a/libraries/node-core-library/src/Terminal/test/__snapshots__/Terminal.test.ts.snap b/libraries/node-core-library/src/Terminal/test/__snapshots__/Terminal.test.ts.snap deleted file mode 100644 index f22a28ef5ff..00000000000 --- a/libraries/node-core-library/src/Terminal/test/__snapshots__/Terminal.test.ts.snap +++ /dev/null @@ -1,921 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`01 color enabled 01 basic terminal functions 01 write 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "test message", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 01 write 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "message 1message 2", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 01 write 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[green]message 1[default]", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 01 write 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[green]message 1[default][red]message 2[default]", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 01 write 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "message 1[green]message 2[default]message 3[red]message 4[default]", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 02 writeLine 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "test message[n]", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 02 writeLine 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "message 1message 2[n]", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 02 writeLine 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[green]message 1[default][n]", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 02 writeLine 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "[green]message 1[default][red]message 2[default][n]", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 02 writeLine 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "message 1[green]message 2[default]message 3[red]message 4[default][n]", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 03 writeWarning 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "[yellow]test message[default]", -} -`; - -exports[`01 color enabled 01 basic terminal functions 03 writeWarning 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "[yellow]message 1[default][yellow]message 2[default]", -} -`; - -exports[`01 color enabled 01 basic terminal functions 03 writeWarning 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "[yellow]message 1[default]", -} -`; - -exports[`01 color enabled 01 basic terminal functions 03 writeWarning 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "[yellow]message 1[default][yellow]message 2[default]", -} -`; - -exports[`01 color enabled 01 basic terminal functions 03 writeWarning 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "[yellow]message 1[default][yellow]message 2[default][yellow]message 3[default][yellow]message 4[default]", -} -`; - -exports[`01 color enabled 01 basic terminal functions 04 writeWarningLine 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "[yellow]test message[default][n]", -} -`; - -exports[`01 color enabled 01 basic terminal functions 04 writeWarningLine 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "[yellow]message 1[default][yellow]message 2[default][n]", -} -`; - -exports[`01 color enabled 01 basic terminal functions 04 writeWarningLine 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "[yellow]message 1[default][n]", -} -`; - -exports[`01 color enabled 01 basic terminal functions 04 writeWarningLine 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "[yellow]message 1[default][yellow]message 2[default][n]", -} -`; - -exports[`01 color enabled 01 basic terminal functions 04 writeWarningLine 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "[yellow]message 1[default][yellow]message 2[default][yellow]message 3[default][yellow]message 4[default][n]", -} -`; - -exports[`01 color enabled 01 basic terminal functions 05 writeError 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "[red]test message[default]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 05 writeError 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "[red]message 1[default][red]message 2[default]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 05 writeError 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "[red]message 1[default]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 05 writeError 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "[red]message 1[default][red]message 2[default]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 05 writeError 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "[red]message 1[default][red]message 2[default][red]message 3[default][red]message 4[default]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 06 writeErrorLine 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "[red]test message[default][n]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 06 writeErrorLine 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "[red]message 1[default][red]message 2[default][n]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 06 writeErrorLine 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "[red]message 1[default][n]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 06 writeErrorLine 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "[red]message 1[default][red]message 2[default][n]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 06 writeErrorLine 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "[red]message 1[default][red]message 2[default][red]message 3[default][red]message 4[default][n]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 07 writeVerbose 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "test message", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 07 writeVerbose 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "message 1message 2", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 07 writeVerbose 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "[green]message 1[default]", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 07 writeVerbose 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "[green]message 1[default][red]message 2[default]", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 07 writeVerbose 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "message 1[green]message 2[default]message 3[red]message 4[default]", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 08 writeVerboseLine 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "test message[n]", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 08 writeVerboseLine 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "message 1message 2[n]", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 08 writeVerboseLine 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "[green]message 1[default][n]", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 08 writeVerboseLine 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "[green]message 1[default][red]message 2[default][n]", - "warning": "", -} -`; - -exports[`01 color enabled 01 basic terminal functions 08 writeVerboseLine 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "message 1[green]message 2[default]message 3[red]message 4[default][n]", - "warning": "", -} -`; - -exports[`01 color enabled 05 writes to multiple streams 1`] = ` -Object { - "debug": "", - "error": "[red]message 1[default][red]message 2[default][red]message 3[default][red]message 4[default][red]test message[default][n][red]message 1[default][red]message 2[default][red]message 1[default][red]message 2[default][n][red]message 1[default][red]message 1[default][red]message 2[default][red]message 3[default][red]message 4[default][n][red]message 1[default][n][red]test message[default][red]message 1[default][red]message 2[default][n][red]message 1[default][red]message 2[default]", - "log": "message 1[green]message 2[default]message 3[red]message 4[default][green]message 1[default][n][green]message 1[default][green]message 1[default][red]message 2[default][green]message 1[default][red]message 2[default][n]test messagemessage 1[green]message 2[default]message 3[red]message 4[default][n]message 1message 2test message[n]message 1message 2[n]", - "verbose": "test message[green]message 1[default]message 1[green]message 2[default]message 3[red]message 4[default][n]test message[n]message 1[green]message 2[default]message 3[red]message 4[default]message 1message 2[green]message 1[default][n][green]message 1[default][red]message 2[default][n]message 1message 2[n][green]message 1[default][red]message 2[default]", - "warning": "[yellow]message 1[default][yellow]message 2[default][n][yellow]message 1[default][yellow]message 2[default][yellow]message 1[default][yellow]message 2[default][yellow]message 3[default][yellow]message 4[default][yellow]message 1[default][yellow]message 2[default][yellow]message 3[default][yellow]message 4[default][n][yellow]test message[default][n][yellow]message 1[default][yellow]message 2[default][yellow]message 1[default][yellow]message 1[default][yellow]message 2[default][n][yellow]message 1[default][n]", -} -`; - -exports[`02 color disabled 01 basic terminal functions 01 write 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "test message", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 01 write 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "message 1message 2", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 01 write 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "message 1", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 01 write 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "message 1message 2", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 01 write 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "message 1message 2message 3message 4", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 02 writeLine 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "test message[n]", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 02 writeLine 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "message 1message 2[n]", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 02 writeLine 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "message 1[n]", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 02 writeLine 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "message 1message 2[n]", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 02 writeLine 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "message 1message 2message 3message 4[n]", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 03 writeWarning 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "test message", -} -`; - -exports[`02 color disabled 01 basic terminal functions 03 writeWarning 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "message 1message 2", -} -`; - -exports[`02 color disabled 01 basic terminal functions 03 writeWarning 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "message 1", -} -`; - -exports[`02 color disabled 01 basic terminal functions 03 writeWarning 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "message 1message 2", -} -`; - -exports[`02 color disabled 01 basic terminal functions 03 writeWarning 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "message 1message 2message 3message 4", -} -`; - -exports[`02 color disabled 01 basic terminal functions 04 writeWarningLine 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "test message[n]", -} -`; - -exports[`02 color disabled 01 basic terminal functions 04 writeWarningLine 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "message 1message 2[n]", -} -`; - -exports[`02 color disabled 01 basic terminal functions 04 writeWarningLine 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "message 1[n]", -} -`; - -exports[`02 color disabled 01 basic terminal functions 04 writeWarningLine 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "message 1message 2[n]", -} -`; - -exports[`02 color disabled 01 basic terminal functions 04 writeWarningLine 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "message 1message 2message 3message 4[n]", -} -`; - -exports[`02 color disabled 01 basic terminal functions 05 writeError 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "test message", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 05 writeError 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "message 1message 2", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 05 writeError 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "message 1", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 05 writeError 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "message 1message 2", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 05 writeError 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "message 1message 2message 3message 4", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 06 writeErrorLine 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "test message[n]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 06 writeErrorLine 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "message 1message 2[n]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 06 writeErrorLine 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "message 1[n]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 06 writeErrorLine 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "message 1message 2[n]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 06 writeErrorLine 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "message 1message 2message 3message 4[n]", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 07 writeVerbose 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "test message", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 07 writeVerbose 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "message 1message 2", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 07 writeVerbose 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "message 1", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 07 writeVerbose 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "message 1message 2", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 07 writeVerbose 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "message 1message 2message 3message 4", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 08 writeVerboseLine 01 writes a single message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "test message[n]", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 08 writeVerboseLine 02 writes multiple messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "message 1message 2[n]", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 08 writeVerboseLine 03 writes a message with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "message 1[n]", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 08 writeVerboseLine 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "message 1message 2[n]", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 08 writeVerboseLine 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "message 1message 2message 3message 4[n]", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 09 writeDebug 01 writes a single message 1`] = ` -Object { - "debug": "test message", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 09 writeDebug 02 writes multiple messages 1`] = ` -Object { - "debug": "message 1message 2", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 09 writeDebug 03 writes a message with colors 1`] = ` -Object { - "debug": "message 1", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 09 writeDebug 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "message 1message 2", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 09 writeDebug 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "message 1message 2message 3message 4", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 10 writeDebugLine 01 writes a single message 1`] = ` -Object { - "debug": "test message[n]", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 10 writeDebugLine 02 writes multiple messages 1`] = ` -Object { - "debug": "message 1message 2[n]", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 10 writeDebugLine 03 writes a message with colors 1`] = ` -Object { - "debug": "message 1[n]", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 10 writeDebugLine 04 writes a multiple messages with colors 1`] = ` -Object { - "debug": "message 1message 2[n]", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 01 basic terminal functions 10 writeDebugLine 05 writes a messages with colors interspersed with non-colored messages 1`] = ` -Object { - "debug": "message 1message 2message 3message 4[n]", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`02 color disabled 05 writes to multiple streams 1`] = ` -Object { - "debug": "", - "error": "message 1message 2message 3message 4test message[n]message 1message 2message 1message 2[n]message 1message 1message 2message 3message 4[n]message 1[n]test messagemessage 1message 2[n]message 1message 2", - "log": "message 1message 2message 3message 4message 1[n]message 1message 1message 2message 1message 2[n]test messagemessage 1message 2message 3message 4[n]message 1message 2test message[n]message 1message 2[n]", - "verbose": "test messagemessage 1message 1message 2message 3message 4[n]test message[n]message 1message 2message 3message 4message 1message 2message 1[n]message 1message 2[n]message 1message 2[n]message 1message 2", - "warning": "message 1message 2[n]message 1message 2message 1message 2message 3message 4message 1message 2message 3message 4[n]test message[n]message 1message 2message 1message 1message 2[n]message 1[n]", -} -`; diff --git a/libraries/node-core-library/src/Terminal/test/__snapshots__/TerminalWritable.test.ts.snap b/libraries/node-core-library/src/Terminal/test/__snapshots__/TerminalWritable.test.ts.snap deleted file mode 100644 index 1f17c94e56f..00000000000 --- a/libraries/node-core-library/src/Terminal/test/__snapshots__/TerminalWritable.test.ts.snap +++ /dev/null @@ -1,51 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TerminalWritable writes a debug message 1`] = ` -Object { - "debug": "test message", - "error": "", - "log": "", - "verbose": "", - "warning": "", -} -`; - -exports[`TerminalWritable writes a message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "test message", - "verbose": "", - "warning": "", -} -`; - -exports[`TerminalWritable writes a verbose message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "test message", - "warning": "", -} -`; - -exports[`TerminalWritable writes a warning message 1`] = ` -Object { - "debug": "", - "error": "", - "log": "", - "verbose": "", - "warning": "[yellow]test message[default]", -} -`; - -exports[`TerminalWritable writes an error message 1`] = ` -Object { - "debug": "", - "error": "[red]test message[default]", - "log": "", - "verbose": "", - "warning": "", -} -`; diff --git a/libraries/node-core-library/src/Terminal/test/createColorGrid.ts b/libraries/node-core-library/src/Terminal/test/createColorGrid.ts deleted file mode 100644 index 8bd761134ae..00000000000 --- a/libraries/node-core-library/src/Terminal/test/createColorGrid.ts +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -/** - * This file is a little program that prints all of the colors to the console - */ - -import { Colors, IColorableSequence } from '../../index'; - -export function createColorGrid( - attributeFunction?: (text: string | IColorableSequence) => IColorableSequence -): IColorableSequence[][] { - const foregroundFunctions: ((text: string | IColorableSequence) => IColorableSequence)[] = [ - (text) => Colors._normalizeStringOrColorableSequence(text), - Colors.black, - Colors.white, - Colors.gray, - Colors.magenta, - Colors.red, - Colors.yellow, - Colors.green, - Colors.cyan, - Colors.blue - ]; - - const backgroundFunctions: ((text: string | IColorableSequence) => IColorableSequence)[] = [ - (text) => Colors._normalizeStringOrColorableSequence(text), - Colors.blackBackground, - Colors.whiteBackground, - Colors.grayBackground, - Colors.magentaBackground, - Colors.redBackground, - Colors.yellowBackground, - Colors.greenBackground, - Colors.cyanBackground, - Colors.blueBackground - ]; - - const lines: IColorableSequence[][] = []; - - for (const backgroundFunction of backgroundFunctions) { - const sequences: IColorableSequence[] = []; - - for (const foregroundFunction of foregroundFunctions) { - let sequence: IColorableSequence = backgroundFunction(foregroundFunction('X')); - if (attributeFunction) { - sequence = attributeFunction(sequence); - } - - sequences.push(sequence); - } - - lines.push(sequences); - } - - return lines; -} diff --git a/libraries/node-core-library/src/Terminal/test/write-colors.ts b/libraries/node-core-library/src/Terminal/test/write-colors.ts deleted file mode 100644 index 74a6b86ae5a..00000000000 --- a/libraries/node-core-library/src/Terminal/test/write-colors.ts +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -/** - * This file is a little program that prints all of the colors to the console. - * - * Run this program with `node write-colors.js` - */ - -import { Terminal, ConsoleTerminalProvider } from '../../index'; -import { createColorGrid } from './createColorGrid'; -import { Colors, IColorableSequence } from '../Colors'; - -const terminal: Terminal = new Terminal(new ConsoleTerminalProvider()); -function writeColorGrid(colorGridSequences: IColorableSequence[][]): void { - for (const line of colorGridSequences) { - terminal.writeLine(...line); - } -} - -writeColorGrid(createColorGrid()); -terminal.writeLine(); -writeColorGrid(createColorGrid(Colors.bold)); -terminal.writeLine(); -writeColorGrid(createColorGrid(Colors.dim)); -terminal.writeLine(); -writeColorGrid(createColorGrid(Colors.underline)); -terminal.writeLine(); -writeColorGrid(createColorGrid(Colors.blink)); -terminal.writeLine(); -writeColorGrid(createColorGrid(Colors.invertColor)); -terminal.writeLine(); -writeColorGrid(createColorGrid(Colors.hidden)); -terminal.writeLine(); - -terminal.write('Normal text...'); -terminal.writeLine(Colors.green('done')); - -terminal.writeError('Error...'); -terminal.writeErrorLine(Colors.green('done')); - -terminal.writeWarning('Warning...'); -terminal.writeWarningLine(Colors.green('done')); diff --git a/libraries/node-core-library/src/Text.ts b/libraries/node-core-library/src/Text.ts index b7ab11139d4..465c240c5c0 100644 --- a/libraries/node-core-library/src/Text.ts +++ b/libraries/node-core-library/src/Text.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as os from 'os'; +import * as os from 'node:os'; /** * The allowed types of encodings, as supported by Node.js @@ -35,6 +35,57 @@ export enum NewlineKind { OsDefault = 'os' } +/** + * Options used when calling the {@link Text.readLinesFromIterable} or + * {@link Text.readLinesFromIterableAsync} methods. + * + * @public + */ +export interface IReadLinesFromIterableOptions { + /** + * The encoding of the input iterable. The default is utf8. + */ + encoding?: Encoding; + + /** + * If true, empty lines will not be returned. The default is false. + */ + ignoreEmptyLines?: boolean; +} + +interface IReadLinesFromIterableState { + remaining: string; +} + +const NEWLINE_REGEX: RegExp = /\r\n|\n\r|\r|\n/g; +const NEWLINE_AT_END_REGEX: RegExp = /(\r\n|\n\r|\r|\n)$/; + +const _newLineRegEx: RegExp = NEWLINE_REGEX; +const _newLineAtEndRegEx: RegExp = NEWLINE_AT_END_REGEX; + +function* readLinesFromChunk( + // eslint-disable-next-line @rushstack/no-new-null + chunk: string | Buffer | null, + encoding: Encoding, + ignoreEmptyLines: boolean, + state: IReadLinesFromIterableState +): Generator { + if (!chunk) { + return; + } + const remaining: string = state.remaining + (typeof chunk === 'string' ? chunk : chunk.toString(encoding)); + let startIndex: number = 0; + const matches: IterableIterator = remaining.matchAll(NEWLINE_REGEX); + for (const match of matches) { + const endIndex: number = match.index!; + if (startIndex !== endIndex || !ignoreEmptyLines) { + yield remaining.substring(startIndex, endIndex); + } + startIndex = endIndex + match[0].length; + } + state.remaining = remaining.substring(startIndex); +} + /** * Operations for working with strings that contain text. * @@ -45,9 +96,6 @@ export enum NewlineKind { * @public */ export class Text { - private static readonly _newLineRegEx: RegExp = /\r\n|\n\r|\r|\n/g; - private static readonly _newLineAtEndRegEx: RegExp = /(\r\n|\n\r|\r|\n)$/; - /** * Returns the same thing as targetString.replace(searchValue, replaceValue), except that * all matches are replaced, rather than just the first match. @@ -63,7 +111,7 @@ export class Text { * Converts all newlines in the provided string to use Windows-style CRLF end of line characters. */ public static convertToCrLf(input: string): string { - return input.replace(Text._newLineRegEx, '\r\n'); + return input.replace(_newLineRegEx, '\r\n'); } /** @@ -72,14 +120,14 @@ export class Text { * POSIX is a registered trademark of the Institute of Electrical and Electronic Engineers, Inc. */ public static convertToLf(input: string): string { - return input.replace(Text._newLineRegEx, '\n'); + return input.replace(_newLineRegEx, '\n'); } /** * Converts all newlines in the provided string to use the specified newline type. */ public static convertTo(input: string, newlineKind: NewlineKind): string { - return input.replace(Text._newLineRegEx, Text.getNewline(newlineKind)); + return input.replace(_newLineRegEx, Text.getNewline(newlineKind)); } /** @@ -166,7 +214,7 @@ export class Text { */ public static ensureTrailingNewline(s: string, newlineKind: NewlineKind = NewlineKind.Lf): string { // Is there already a newline? - if (Text._newLineAtEndRegEx.test(s)) { + if (_newLineAtEndRegEx.test(s)) { return s; // yes, no change } return s + newlineKind; // no, add it @@ -178,4 +226,68 @@ export class Text { public static escapeRegExp(literal: string): string { return literal.replace(/[^A-Za-z0-9_]/g, '\\$&'); } + + /** + * Read lines from an iterable object that returns strings or buffers, and return a generator that + * produces the lines as strings. The lines will not include the newline characters. + * + * @param iterable - An iterable object that returns strings or buffers + * @param options - Options used when reading the lines from the provided iterable + */ + public static async *readLinesFromIterableAsync( + iterable: AsyncIterable, + options: IReadLinesFromIterableOptions = {} + ): AsyncGenerator { + const { encoding = Encoding.Utf8, ignoreEmptyLines = false } = options; + const state: IReadLinesFromIterableState = { remaining: '' }; + for await (const chunk of iterable) { + yield* readLinesFromChunk(chunk, encoding, ignoreEmptyLines, state); + } + const remaining: string = state.remaining; + if (remaining.length) { + yield remaining; + } + } + + /** + * Read lines from an iterable object that returns strings or buffers, and return a generator that + * produces the lines as strings. The lines will not include the newline characters. + * + * @param iterable - An iterable object that returns strings or buffers + * @param options - Options used when reading the lines from the provided iterable + */ + public static *readLinesFromIterable( + // eslint-disable-next-line @rushstack/no-new-null + iterable: Iterable, + options: IReadLinesFromIterableOptions = {} + ): Generator { + const { encoding = Encoding.Utf8, ignoreEmptyLines = false } = options; + const state: IReadLinesFromIterableState = { remaining: '' }; + for (const chunk of iterable) { + yield* readLinesFromChunk(chunk, encoding, ignoreEmptyLines, state); + } + const remaining: string = state.remaining; + if (remaining.length) { + yield remaining; + } + } + + /** + * Returns a new string that is the input string with the order of characters reversed. + */ + public static reverse(s: string): string { + // Benchmarks of several algorithms: https://jsbench.me/4bkfflcm2z + return s.split('').reduce((newString, char) => char + newString, ''); + } + + /** + * Splits the provided string by newlines. Note that leading and trailing newlines will produce + * leading or trailing empty string array entries. + */ + public static splitByNewLines(s: undefined): undefined; + public static splitByNewLines(s: string): string[]; + public static splitByNewLines(s: string | undefined): string[] | undefined; + public static splitByNewLines(s: string | undefined): string[] | undefined { + return s?.split(/\r?\n/); + } } diff --git a/libraries/node-core-library/src/TypeUuid.ts b/libraries/node-core-library/src/TypeUuid.ts index 7637e3f7f95..a28d0f7ff96 100644 --- a/libraries/node-core-library/src/TypeUuid.ts +++ b/libraries/node-core-library/src/TypeUuid.ts @@ -5,6 +5,8 @@ import { InternalError } from './InternalError'; const classPrototypeUuidSymbol: symbol = Symbol.for('TypeUuid.classPrototypeUuid'); +const _uuidRegExp: RegExp = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/; + /** * Provides a version-independent implementation of the JavaScript `instanceof` operator. * @@ -39,8 +41,6 @@ const classPrototypeUuidSymbol: symbol = Symbol.for('TypeUuid.classPrototypeUuid * @public */ export class TypeUuid { - private static _uuidRegExp: RegExp = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/; - /** * Registers a JavaScript class as having a type identified by the specified UUID. * @privateRemarks @@ -52,7 +52,7 @@ export class TypeUuid { throw new Error('The targetClass parameter must be a JavaScript class'); } - if (!TypeUuid._uuidRegExp.test(typeUuid)) { + if (!_uuidRegExp.test(typeUuid)) { throw new Error(`The type UUID must be specified as lowercase hexadecimal with dashes: "${typeUuid}"`); } diff --git a/libraries/node-core-library/src/User.ts b/libraries/node-core-library/src/User.ts new file mode 100644 index 00000000000..3107da72bf6 --- /dev/null +++ b/libraries/node-core-library/src/User.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as User from './user/index'; + +export { User }; diff --git a/libraries/node-core-library/src/disposables/index.ts b/libraries/node-core-library/src/disposables/index.ts new file mode 100644 index 00000000000..f3aa7768ad1 --- /dev/null +++ b/libraries/node-core-library/src/disposables/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export { polyfillDisposeSymbols } from './polyfillDisposeSymbols'; diff --git a/libraries/node-core-library/src/disposables/polyfillDisposeSymbols.ts b/libraries/node-core-library/src/disposables/polyfillDisposeSymbols.ts new file mode 100644 index 00000000000..621807aa12f --- /dev/null +++ b/libraries/node-core-library/src/disposables/polyfillDisposeSymbols.ts @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * @public + * Polyfill for `Symbol.dispose` and `Symbol.asyncDispose` for Node.js versions prior to 20 + */ +export function polyfillDisposeSymbols(): void { + (Symbol as { dispose?: typeof Symbol.dispose }).dispose ??= Symbol.for( + 'Symbol.dispose' + ) as typeof Symbol.dispose; + (Symbol as { asyncDispose?: typeof Symbol.asyncDispose }).asyncDispose ??= Symbol.for( + 'Symbol.asyncDispose' + ) as typeof Symbol.asyncDispose; +} diff --git a/libraries/node-core-library/src/index.ts b/libraries/node-core-library/src/index.ts index 7f2563b47f6..3f4592cf6b1 100644 --- a/libraries/node-core-library/src/index.ts +++ b/libraries/node-core-library/src/index.ts @@ -1,115 +1,168 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +/// + /** * Core libraries that every NodeJS toolchain project should use. * * @packageDocumentation */ +export type { IProblemPattern } from '@rushstack/problem-matcher'; + export { AlreadyReportedError } from './AlreadyReportedError'; -export { AnsiEscape, IAnsiEscapeConvertForTestsOptions } from './Terminal/AnsiEscape'; -export { Async, AsyncQueue, IAsyncParallelismOptions, IRunWithRetriesOptions } from './Async'; -export { Brand } from './PrimitiveTypes'; + +export { + Async, + AsyncQueue, + type IAsyncParallelismOptions, + type IRunWithRetriesOptions, + type IRunWithTimeoutOptions, + type IWeighted +} from './Async'; + export { FileConstants, FolderConstants } from './Constants'; + +export { Disposables } from './Disposables'; + export { Enum } from './Enum'; -export { EnvironmentMap, IEnvironmentEntry } from './EnvironmentMap'; + +export { EnvironmentMap, type IEnvironmentEntry } from './EnvironmentMap'; + export { - ExecutableStdioStreamMapping, - ExecutableStdioMapping, - IExecutableResolveOptions, - IExecutableSpawnSyncOptions, - IExecutableSpawnOptions, + type ExecutableStdioStreamMapping, + type ExecutableStdioMapping, + type IExecutableResolveOptions, + type IExecutableSpawnSyncOptions, + type IExecutableSpawnOptions, + type IWaitForExitOptions, + type IWaitForExitWithBufferOptions, + type IWaitForExitWithStringOptions, + type IWaitForExitResult, + type IWaitForExitResultWithoutOutput, + type IProcessInfo, Executable } from './Executable'; -export { IFileErrorOptions, IFileErrorFormattingOptions, FileError } from './FileError'; + +export { type IFileErrorOptions, type IFileErrorFormattingOptions, FileError } from './FileError'; + +export { + AlreadyExistsBehavior, + FileSystem, + type IFileSystemWriteFileOptionsBase, + type FileSystemCopyFilesAsyncFilter, + type FileSystemCopyFilesFilter, + type FileSystemReadStream, + type FileSystemWriteStream, + type FolderItem, + type FileSystemStats, + type IFileSystemCopyFileBaseOptions, + type IFileSystemCopyFileOptions, + type IFileSystemCopyFilesAsyncOptions, + type IFileSystemCopyFilesOptions, + type IFileSystemCreateLinkOptions, + type IFileSystemCreateWriteStreamOptions, + type IFileSystemDeleteFileOptions, + type IFileSystemMoveOptions, + type IFileSystemReadFileOptions, + type IFileSystemReadFolderOptions, + type IFileSystemUpdateTimeParameters, + type IFileSystemWriteBinaryFileOptions, + type IFileSystemWriteFileOptions +} from './FileSystem'; + +export { FileWriter, type IFileWriterFlags } from './FileWriter'; + export { + Import, + type IImportResolveOptions, + type IImportResolveAsyncOptions, + type IImportResolveModuleOptions, + type IImportResolveModuleAsyncOptions, + type IImportResolvePackageOptions, + type IImportResolvePackageAsyncOptions +} from './Import'; + +export { InternalError } from './InternalError'; + +export type { INodePackageJson, IPackageJson, IPackageJsonDependencyTable, IPackageJsonScriptTable, IPackageJsonRepository, - IPeerDependenciesMetaTable + IPeerDependenciesMetaTable, + IDependenciesMetaTable, + IPackageJsonExports } from './IPackageJson'; + export { - Import, - IImportResolveOptions, - IImportResolveAsyncOptions, - IImportResolveModuleOptions, - IImportResolveModuleAsyncOptions, - IImportResolvePackageOptions, - IImportResolvePackageAsyncOptions -} from './Import'; -export { InternalError } from './InternalError'; -export { - JsonObject, - JsonNull, + type JsonObject, + type JsonNull, JsonSyntax, - IJsonFileParseOptions, - IJsonFileLoadAndValidateOptions, - IJsonFileStringifyOptions, - IJsonFileSaveOptions, + type IJsonFileParseOptions, + type IJsonFileLoadAndValidateOptions, + type IJsonFileStringifyOptions, + type IJsonFileSaveOptions, JsonFile } from './JsonFile'; + export { + type IJsonSchemaErrorInfo, + type IJsonSchemaCustomFormat, + type IJsonSchemaFromFileOptions, + type IJsonSchemaFromObjectOptions, + type IJsonSchemaLoadOptions, + type IJsonSchemaValidateOptions, + type IJsonSchemaValidateObjectWithOptions, JsonSchema, - IJsonSchemaErrorInfo, - IJsonSchemaValidateOptions, - IJsonSchemaFromFileOptions + type JsonSchemaVersion } from './JsonSchema'; + +export { LegacyAdapters, type LegacyCallback } from './LegacyAdapters'; + export { LockFile } from './LockFile'; + export { MapExtensions } from './MapExtensions'; -export { PosixModeBits } from './PosixModeBits'; -export { ProtectableMap, IProtectableMapParameters } from './ProtectableMap'; -export { IPackageJsonLookupParameters, PackageJsonLookup } from './PackageJsonLookup'; + +export { MinimumHeap } from './MinimumHeap'; + +export { Objects } from './Objects'; + +export { type IPackageJsonLookupParameters, PackageJsonLookup } from './PackageJsonLookup'; + export { PackageName, PackageNameParser, - IPackageNameParserOptions, - IParsedPackageName, - IParsedPackageNameOrError + type IPackageNameParserOptions, + type IParsedPackageName, + type IParsedPackageNameOrError } from './PackageName'; -export { Path, FileLocationStyle, IPathFormatFileLocationOptions, IPathFormatConciselyOptions } from './Path'; -export { Encoding, Text, NewlineKind } from './Text'; -export { Sort } from './Sort'; -export { - AlreadyExistsBehavior, - FileSystem, - FileSystemCopyFilesAsyncFilter, - FileSystemCopyFilesFilter, - FolderItem, - FileSystemStats, - IFileSystemCopyFileBaseOptions, - IFileSystemCopyFileOptions, - IFileSystemCopyFilesAsyncOptions, - IFileSystemCopyFilesOptions, - IFileSystemCreateLinkOptions, - IFileSystemDeleteFileOptions, - IFileSystemMoveOptions, - IFileSystemReadFileOptions, - IFileSystemReadFolderOptions, - IFileSystemUpdateTimeParameters, - IFileSystemWriteFileOptions -} from './FileSystem'; -export { FileWriter, IFileWriterFlags } from './FileWriter'; -export { LegacyAdapters, LegacyCallback } from './LegacyAdapters'; -export { StringBuilder, IStringBuilder } from './StringBuilder'; -export { ISubprocessOptions, SubprocessTerminator } from './SubprocessTerminator'; -export { ITerminal } from './Terminal/ITerminal'; -export { Terminal } from './Terminal/Terminal'; -export { Colors, IColorableSequence, ColorValue, TextAttribute } from './Terminal/Colors'; -export { ITerminalProvider, TerminalProviderSeverity } from './Terminal/ITerminalProvider'; -export { ConsoleTerminalProvider, IConsoleTerminalProviderOptions } from './Terminal/ConsoleTerminalProvider'; -export { - StringBufferTerminalProvider, - IStringBufferOutputOptions -} from './Terminal/StringBufferTerminalProvider'; + export { - PrefixProxyTerminalProvider, - IPrefixProxyTerminalProviderOptions, - IDynamicPrefixProxyTerminalProviderOptions, - IPrefixProxyTerminalProviderOptionsBase, - IStaticPrefixProxyTerminalProviderOptions -} from './Terminal/PrefixProxyTerminalProvider'; -export { TerminalWritable, ITerminalWritableOptions } from './Terminal/TerminalWritable'; + Path, + type FileLocationStyle, + type IPathFormatFileLocationOptions, + type IPathFormatConciselyOptions +} from './Path'; + +export { PosixModeBits } from './PosixModeBits'; + +export type { Brand } from './PrimitiveTypes'; + +export { ProtectableMap, type IProtectableMapParameters } from './ProtectableMap'; + +export { RealNodeModulePathResolver, type IRealNodeModulePathResolverOptions } from './RealNodeModulePath'; + +export { Sort } from './Sort'; + +export { StringBuilder, type IStringBuilder } from './StringBuilder'; + +export { type ISubprocessOptions, SubprocessTerminator } from './SubprocessTerminator'; + +export { Encoding, Text, NewlineKind, type IReadLinesFromIterableOptions } from './Text'; + export { TypeUuid } from './TypeUuid'; + +export { User } from './User'; diff --git a/libraries/node-core-library/src/objects/areDeepEqual.ts b/libraries/node-core-library/src/objects/areDeepEqual.ts new file mode 100644 index 00000000000..09d618e588a --- /dev/null +++ b/libraries/node-core-library/src/objects/areDeepEqual.ts @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * Determines if two objects are deeply equal. + * @public + */ +export function areDeepEqual(a: TObject, b: TObject): boolean { + if (a === b) { + return true; + } else { + const aType: string = typeof a; + const bType: string = typeof b; + + if (aType !== bType) { + return false; + } else { + if (aType === 'object') { + if (a === null || b === null) { + // We already handled the case where a === b, so if either is null, they are not equal + return false; + } else if (Array.isArray(a)) { + if (!Array.isArray(b) || a.length !== b.length) { + return false; + } else { + for (let i: number = 0; i < a.length; ++i) { + if (!areDeepEqual(a[i], b[i])) { + return false; + } + } + + return true; + } + } else { + const aObjectProperties: Set = new Set(Object.getOwnPropertyNames(a)); + const bObjectProperties: Set = new Set(Object.getOwnPropertyNames(b)); + if (aObjectProperties.size !== bObjectProperties.size) { + return false; + } else { + for (const property of aObjectProperties) { + if (bObjectProperties.delete(property)) { + if ( + !areDeepEqual( + (a as Record)[property], + (b as Record)[property] + ) + ) { + return false; + } + } else { + return false; + } + } + + return bObjectProperties.size === 0; + } + } + } else { + return false; + } + } + } +} diff --git a/libraries/node-core-library/src/objects/index.ts b/libraries/node-core-library/src/objects/index.ts new file mode 100644 index 00000000000..6dbd95ce592 --- /dev/null +++ b/libraries/node-core-library/src/objects/index.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export { areDeepEqual } from './areDeepEqual'; +export { isRecord } from './isRecord'; +export { type MergeWithCustomizer, mergeWith } from './mergeWith'; diff --git a/libraries/node-core-library/src/objects/isRecord.ts b/libraries/node-core-library/src/objects/isRecord.ts new file mode 100644 index 00000000000..62f82851a86 --- /dev/null +++ b/libraries/node-core-library/src/objects/isRecord.ts @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * Returns `true` if `value` is a non-null, non-array plain object (i.e. assignable to + * `Record`), narrowing the type accordingly. + * + * @public + */ +export function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} diff --git a/libraries/node-core-library/src/objects/mergeWith.ts b/libraries/node-core-library/src/objects/mergeWith.ts new file mode 100644 index 00000000000..c31d8fcddee --- /dev/null +++ b/libraries/node-core-library/src/objects/mergeWith.ts @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { isRecord } from './isRecord'; + +/** + * Customizer function for use with `mergeWith`. + * Return `undefined` to fall back to the default deep-merge behavior for that property. + * @public + */ +export type MergeWithCustomizer = (objValue: unknown, srcValue: unknown, key: string) => unknown; + +/** + * Recursively merges own enumerable string-keyed properties of `source` into `target`, invoking + * `customizer` for each property. Mutates and returns `target`. + * + * @remarks + * For each property in `source`, `customizer` is called with `(targetValue, sourceValue, key)`. + * If the customizer returns a value other than `undefined`, that value is assigned directly. + * Otherwise the default behavior applies: plain objects are merged recursively; all other values + * (arrays, primitives, `null`) overwrite the corresponding target property. + * + * @public + */ +export function mergeWith( + target: TTarget, + source: TSource, + customizer?: MergeWithCustomizer +): TTarget { + const targetRecord: Record = target as unknown as Record; + const sourceRecord: Record = source as unknown as Record; + for (const [key, srcValue] of Object.entries(sourceRecord)) { + const objValue: unknown = targetRecord[key]; + const customized: unknown = customizer?.(objValue, srcValue, key); + if (customized !== undefined) { + targetRecord[key] = customized; + } else if (isRecord(srcValue) && isRecord(objValue)) { + mergeWith(objValue, srcValue, customizer); + } else { + targetRecord[key] = srcValue; + } + } + + return target; +} diff --git a/libraries/node-core-library/src/objects/test/areDeepEqual.test.ts b/libraries/node-core-library/src/objects/test/areDeepEqual.test.ts new file mode 100644 index 00000000000..28f29790685 --- /dev/null +++ b/libraries/node-core-library/src/objects/test/areDeepEqual.test.ts @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { areDeepEqual } from '../areDeepEqual'; + +describe(areDeepEqual.name, () => { + it('can compare primitives', () => { + expect(areDeepEqual(1, 1)).toEqual(true); + expect(areDeepEqual(1, undefined)).toEqual(false); + expect(areDeepEqual(1, null)).toEqual(false); + expect(areDeepEqual(undefined, 1)).toEqual(false); + expect(areDeepEqual(null, 1)).toEqual(false); + expect(areDeepEqual(1, 2)).toEqual(false); + + expect(areDeepEqual('a', 'a')).toEqual(true); + expect(areDeepEqual('a', undefined)).toEqual(false); + expect(areDeepEqual('a', null)).toEqual(false); + expect(areDeepEqual(undefined, 'a')).toEqual(false); + expect(areDeepEqual(null, 'a')).toEqual(false); + expect(areDeepEqual('a', 'b')).toEqual(false); + + expect(areDeepEqual(true, true)).toEqual(true); + expect(areDeepEqual(true, undefined)).toEqual(false); + expect(areDeepEqual(true, null)).toEqual(false); + expect(areDeepEqual(undefined, true)).toEqual(false); + expect(areDeepEqual(null, true)).toEqual(false); + expect(areDeepEqual(true, false)).toEqual(false); + + expect(areDeepEqual(undefined, undefined)).toEqual(true); + expect(areDeepEqual(undefined, null)).toEqual(false); + expect(areDeepEqual(null, null)).toEqual(true); + }); + + it('can compare arrays', () => { + expect(areDeepEqual([], [])).toEqual(true); + expect(areDeepEqual([], undefined)).toEqual(false); + expect(areDeepEqual([], null)).toEqual(false); + expect(areDeepEqual(undefined, [])).toEqual(false); + expect(areDeepEqual(null, [])).toEqual(false); + + expect(areDeepEqual([1], [1])).toEqual(true); + expect(areDeepEqual([1], [2])).toEqual(false); + + expect(areDeepEqual([1, 2], [1, 2])).toEqual(true); + expect(areDeepEqual([1, 2], [2, 1])).toEqual(false); + + expect(areDeepEqual([1, 2, 3], [1, 2, 3])).toEqual(true); + expect(areDeepEqual([1, 2, 3], [1, 2, 4])).toEqual(false); + }); + + it('can compare objects', () => { + expect(areDeepEqual({}, {})).toEqual(true); + expect(areDeepEqual({}, undefined)).toEqual(false); + expect(areDeepEqual({}, null)).toEqual(false); + expect(areDeepEqual(undefined, {})).toEqual(false); + expect(areDeepEqual(null, {})).toEqual(false); + + expect(areDeepEqual({ a: 1 }, { a: 1 })).toEqual(true); + expect(areDeepEqual({ a: 1 }, { a: 2 })).toEqual(false); + expect(areDeepEqual({ a: 1 }, {})).toEqual(false); + expect(areDeepEqual({}, { a: 1 })).toEqual(false); + expect(areDeepEqual({ a: 1 }, { b: 1 })).toEqual(false); + + expect(areDeepEqual({ a: 1, b: 2 }, { a: 1, b: 2 })).toEqual(true); + expect(areDeepEqual({ a: 1, b: 2 }, { a: 1, b: 3 })).toEqual(false); + expect(areDeepEqual({ a: 1, b: 2 }, { a: 1, c: 2 })).toEqual(false); + expect(areDeepEqual({ a: 1, b: 2 }, { b: 2, a: 1 })).toEqual(true); + }); + + it('can compare nested objects', () => { + expect(areDeepEqual({ a: { b: 1 } }, { a: { b: 1 } })).toEqual(true); + expect(areDeepEqual({ a: { b: 1 } }, { a: { b: 2 } })).toEqual(false); + expect(areDeepEqual({ a: { b: 1 } }, { a: { c: 1 } })).toEqual(false); + expect(areDeepEqual({ a: { b: 1 } }, { a: { b: 1, c: 2 } })).toEqual(false); + expect(areDeepEqual({ a: { b: 1 } }, { a: { b: 1 }, c: 2 })).toEqual(false); + }); +}); diff --git a/libraries/node-core-library/src/objects/test/mergeWith.test.ts b/libraries/node-core-library/src/objects/test/mergeWith.test.ts new file mode 100644 index 00000000000..333e2a46150 --- /dev/null +++ b/libraries/node-core-library/src/objects/test/mergeWith.test.ts @@ -0,0 +1,157 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { mergeWith } from '../mergeWith'; + +describe(mergeWith.name, () => { + describe('default behavior (no customizer)', () => { + it('returns the target object', () => { + const target: { a: number } = { a: 1 }; + const result: { a: number } = mergeWith(target, { a: 2 }); + expect(result).toBe(target); + }); + + it('copies source properties onto target', () => { + const target: Record = { a: 1 }; + mergeWith(target, { b: 2 }); + expect(target).toEqual({ a: 1, b: 2 }); + }); + + it('overwrites target primitive with source primitive', () => { + const target: Record = { a: 1 }; + mergeWith(target, { a: 2 }); + expect(target.a).toBe(2); + }); + + it('recursively merges nested plain objects', () => { + const target: Record = { nested: { a: 1, b: 2 } }; + mergeWith(target, { nested: { b: 99, c: 3 } }); + expect(target).toEqual({ nested: { a: 1, b: 99, c: 3 } }); + }); + + it('overwrites target array with source array (does not merge by index)', () => { + const target: Record = { arr: [1, 2, 3] }; + mergeWith(target, { arr: [10, 20] }); + expect(target.arr).toEqual([10, 20]); + }); + + it('overwrites nested array with source array', () => { + const target: Record = { nested: { arr: ['a', 'b', 'c'] } }; + mergeWith(target, { nested: { arr: ['x'] } }); + expect((target.nested as Record).arr).toEqual(['x']); + }); + + it('overwrites target object with source null', () => { + const target: Record = { a: { b: 1 } }; + mergeWith(target, { a: null }); + expect(target.a).toBeNull(); + }); + + it('overwrites target null with source object', () => { + const target: Record = { a: null }; + mergeWith(target, { a: { b: 1 } }); + expect(target.a).toEqual({ b: 1 }); + }); + + it('mutates the target object in place', () => { + const nested: Record = { x: 1 }; + const target: Record = { nested }; + mergeWith(target, { nested: { y: 2 } }); + expect(target.nested).toBe(nested); + expect(nested).toEqual({ x: 1, y: 2 }); + }); + + it('handles empty source', () => { + const target: Record = { a: 1 }; + mergeWith(target, {}); + expect(target).toEqual({ a: 1 }); + }); + + it('handles empty target', () => { + const target: Record = {}; + mergeWith(target, { a: 1 }); + expect(target).toEqual({ a: 1 }); + }); + }); + + describe('customizer behavior', () => { + it('uses customizer return value when it is not undefined', () => { + const target: Record = { a: 1 }; + mergeWith(target, { a: 2 }, () => 'custom'); + expect(target.a).toBe('custom'); + }); + + it('falls back to default merge when customizer returns undefined', () => { + const target: Record = { a: 1 }; + mergeWith(target, { a: 2 }, () => undefined); + expect(target.a).toBe(2); + }); + + it('customizer receives (targetValue, sourceValue, key)', () => { + const calls: [unknown, unknown, string][] = []; + const target: Record = { a: 1, b: 2 }; + mergeWith(target, { a: 10, b: 20 }, (obj, src, key) => { + calls.push([obj, src, key]); + return undefined; + }); + expect(calls).toEqual([ + [1, 10, 'a'], + [2, 20, 'b'] + ]); + }); + + it('customizer can overwrite arrays instead of concatenating', () => { + const target: Record = { arr: [1, 2] }; + mergeWith(target, { arr: [3, 4] }, (currentValue, srcValue) => { + void currentValue; + if (Array.isArray(srcValue)) return srcValue; + return undefined; + }); + expect(target.arr).toEqual([3, 4]); + }); + + it('customizer can concatenate arrays', () => { + const target: Record = { arr: [1, 2] }; + mergeWith(target, { arr: [3, 4] }, (objValue, srcValue) => { + if (Array.isArray(objValue) && Array.isArray(srcValue)) return [...objValue, ...srcValue]; + return undefined; + }); + expect(target.arr).toEqual([1, 2, 3, 4]); + }); + + it('customizer is not called for keys not in source', () => { + const calls: string[] = []; + const target: Record = { a: 1, b: 2 }; + mergeWith(target, { a: 10 }, (objValue, srcValue, key) => { + void objValue; + void srcValue; + calls.push(key); + return undefined; + }); + expect(calls).toEqual(['a']); + expect(target.b).toBe(2); + }); + + it('customizer receives undefined targetValue for keys only in source', () => { + let receivedObjValue: unknown = 'sentinel'; + const target: Record = {}; + mergeWith(target, { newKey: 42 }, (objValue) => { + receivedObjValue = objValue; + return undefined; + }); + expect(receivedObjValue).toBeUndefined(); + expect(target.newKey).toBe(42); + }); + + it('deep merge falls back to default when customizer returns undefined for nested objects', () => { + const target: Record = { nested: { a: 1, b: 2 } }; + mergeWith(target, { nested: { b: 99, c: 3 } }, (objValue, srcValue) => { + void objValue; + // Only intercept arrays, let objects deep-merge + if (Array.isArray(srcValue)) return srcValue; + return undefined; + }); + expect(target).toEqual({ nested: { a: 1, b: 99, c: 3 } }); + }); + }); +}); diff --git a/libraries/node-core-library/src/test/2/test#999999999.lock b/libraries/node-core-library/src/test/2/test#999999999.lock deleted file mode 100644 index da4eb94a088..00000000000 --- a/libraries/node-core-library/src/test/2/test#999999999.lock +++ /dev/null @@ -1 +0,0 @@ -2012-01-02 12:53:12 \ No newline at end of file diff --git a/libraries/node-core-library/src/test/3/test#1.lock b/libraries/node-core-library/src/test/3/test#1.lock deleted file mode 100644 index da4eb94a088..00000000000 --- a/libraries/node-core-library/src/test/3/test#1.lock +++ /dev/null @@ -1 +0,0 @@ -2012-01-02 12:53:12 \ No newline at end of file diff --git a/libraries/node-core-library/src/test/Async.test.ts b/libraries/node-core-library/src/test/Async.test.ts index 14cd54a6fb4..fb55c7c0822 100644 --- a/libraries/node-core-library/src/test/Async.test.ts +++ b/libraries/node-core-library/src/test/Async.test.ts @@ -3,6 +3,11 @@ import { Async, AsyncQueue } from '../Async'; +interface INumberWithWeight { + n: number; + weight: number; +} + describe(Async.name, () => { describe(Async.mapAsync.name, () => { it('handles an empty array correctly', async () => { @@ -27,13 +32,6 @@ describe(Async.name, () => { expect(fn).toHaveBeenNthCalledWith(3, 3, 2); }); - it('returns the same result as built-in Promise.all', async () => { - const array: number[] = [1, 2, 3, 4, 5, 6, 7, 8]; - const fn: (item: number) => Promise = async (item) => `result ${item}`; - - expect(await Async.mapAsync(array, fn)).toEqual(await Promise.all(array.map(fn))); - }); - it('if concurrency is set, ensures no more than N operations occur in parallel', async () => { let running: number = 0; let maxRunning: number = 0; @@ -42,7 +40,7 @@ describe(Async.name, () => { const fn: (item: number) => Promise = async (item) => { running++; - await Async.sleep(1); + await Async.sleepAsync(0); maxRunning = Math.max(maxRunning, running); running--; return `result ${item}`; @@ -61,6 +59,31 @@ describe(Async.name, () => { expect(maxRunning).toEqual(3); }); + it('respects concurrency limit with allowOversubscription=false in mapAsync', async () => { + const array: INumberWithWeight[] = [ + { n: 1, weight: 2 }, + { n: 2, weight: 2 } + ]; + + let running = 0; + let maxRunning = 0; + + const result = await Async.mapAsync( + array, + async (item) => { + running++; + maxRunning = Math.max(maxRunning, running); + await Async.sleepAsync(0); + running--; + return `result-${item.n}`; + }, + { concurrency: 3, weighted: true, allowOversubscription: false } + ); + + expect(result).toEqual(['result-1', 'result-2']); + expect(maxRunning).toEqual(1); + }); + it('rejects if a sync iterator throws an error', async () => { const expectedError: Error = new Error('iterator error'); let iteratorIndex: number = 0; @@ -137,7 +160,7 @@ describe(Async.name, () => { const fn: (item: number) => Promise = jest.fn(async (item) => { running++; - await Async.sleep(1); + await Async.sleepAsync(0); maxRunning = Math.max(maxRunning, running); running--; }); @@ -155,7 +178,7 @@ describe(Async.name, () => { const fn: (item: number) => Promise = jest.fn(async (item) => { running++; - await Async.sleep(1); + await Async.sleepAsync(0); maxRunning = Math.max(maxRunning, running); running--; }); @@ -171,18 +194,18 @@ describe(Async.name, () => { array.push(i); } - await Async.forEachAsync(array, async () => await Async.sleep(1), { concurrency: 3 }); + await Async.forEachAsync(array, async () => await Async.sleepAsync(0), { concurrency: 3 }); }); it('rejects if any operation rejects', async () => { const array: number[] = [1, 2, 3]; const fn: (item: number) => Promise = jest.fn(async (item) => { - await Async.sleep(1); + await Async.sleepAsync(0); if (item === 3) throw new Error('Something broke'); }); - await expect(() => Async.forEachAsync(array, fn, { concurrency: 3 })).rejects.toThrowError( + await expect(() => Async.forEachAsync(array, fn, { concurrency: 3 })).rejects.toThrow( 'Something broke' ); expect(fn).toHaveBeenCalledTimes(3); @@ -199,7 +222,7 @@ describe(Async.name, () => { if (item === 3) throw new Error('Something broke'); }) as unknown as (item: number) => Promise; - await expect(() => Async.forEachAsync(array, fn, { concurrency: 3 })).rejects.toThrowError( + await expect(() => Async.forEachAsync(array, fn, { concurrency: 3 })).rejects.toThrow( 'Something broke' ); expect(fn).toHaveBeenCalledTimes(3); @@ -223,7 +246,7 @@ describe(Async.name, () => { }; await expect(() => - Async.forEachAsync(syncIterable, async (item) => await Async.sleep(1)) + Async.forEachAsync(syncIterable, async (item) => await Async.sleepAsync(0)) ).rejects.toThrow(expectedError); }); @@ -245,7 +268,7 @@ describe(Async.name, () => { }; await expect(() => - Async.forEachAsync(syncIterable, async (item) => await Async.sleep(1)) + Async.forEachAsync(syncIterable, async (item) => await Async.sleepAsync(0)) ).rejects.toThrow(expectedError); }); @@ -273,20 +296,21 @@ describe(Async.name, () => { [Symbol.asyncIterator]: () => asyncIterator }; - const expectedConcurrency: 4 = 4; const finalPromise: Promise = Async.forEachAsync( asyncIterable, async (item) => { // Do nothing }, { - concurrency: expectedConcurrency + concurrency: 4 } ); // Wait for all the instant resolutions to be done - await Async.sleep(1); - expect(waitingIterators).toEqual(expectedConcurrency); + await Async.sleepAsync(0); + + // The final iteration cycle is locked, so only 1 iterator is waiting. + expect(waitingIterators).toEqual(1); resolve2({ done: true, value: undefined }); await finalPromise; }); @@ -309,12 +333,237 @@ describe(Async.name, () => { }; await expect(() => - Async.forEachAsync(syncIterable, async (item) => await Async.sleep(1)) + Async.forEachAsync(syncIterable, async (item) => await Async.sleepAsync(0)) ).rejects.toThrow(expectedError); }); + + it('handles an empty array correctly', async () => { + let running: number = 0; + let maxRunning: number = 0; + + const array: INumberWithWeight[] = []; + + const fn: (item: INumberWithWeight) => Promise = jest.fn(async (item) => { + running++; + await Async.sleepAsync(0); + maxRunning = Math.max(maxRunning, running); + running--; + }); + + await Async.forEachAsync(array, fn, { concurrency: 3, weighted: true }); + expect(fn).toHaveBeenCalledTimes(0); + expect(maxRunning).toEqual(0); + }); + + it('if concurrency is set, ensures no more than N operations occur in parallel', async () => { + let running: number = 0; + let maxRunning: number = 0; + + const array: INumberWithWeight[] = [1, 2, 3, 4, 5, 6, 7, 8].map((n) => ({ weight: 1, n })); + + const fn: (item: INumberWithWeight) => Promise = jest.fn(async (item) => { + running++; + await Async.sleepAsync(0); + maxRunning = Math.max(maxRunning, running); + running--; + }); + + await Async.forEachAsync(array, fn, { concurrency: 3, weighted: true }); + expect(fn).toHaveBeenCalledTimes(8); + expect(maxRunning).toEqual(3); + }); + + it('if concurrency is set but weighted is not, ensures no more than N operations occur in parallel and ignores operation weight', async () => { + let running: number = 0; + let maxRunning: number = 0; + + const array: INumberWithWeight[] = [1, 2, 3, 4, 5, 6, 7, 8].map((n) => ({ weight: 2, n })); + + const fn: (item: INumberWithWeight) => Promise = jest.fn(async (item) => { + running++; + await Async.sleepAsync(0); + maxRunning = Math.max(maxRunning, running); + running--; + }); + + await Async.forEachAsync(array, fn, { concurrency: 3 }); + expect(fn).toHaveBeenCalledTimes(8); + expect(maxRunning).toEqual(3); + }); + + it.each([ + { + concurrency: 4, + weight: 4, + expectedConcurrency: 1 + }, + { + concurrency: 4, + weight: 1, + expectedConcurrency: 4 + }, + { + concurrency: 3, + weight: 1, + expectedConcurrency: 3 + }, + { + concurrency: 6, + weight: 2, + expectedConcurrency: 3 + }, + { + concurrency: 12, + weight: 3, + expectedConcurrency: 4 + } + ])( + 'if concurrency is set to $concurrency with operation weight $weight, ensures no more than $expectedConcurrency operations occur in parallel', + async ({ concurrency, weight, expectedConcurrency }) => { + let running: number = 0; + let maxRunning: number = 0; + + const array: INumberWithWeight[] = [1, 2, 3, 4, 5, 6, 7, 8].map((n) => ({ n, weight })); + + const fn: (item: INumberWithWeight) => Promise = jest.fn(async (item) => { + running++; + await Async.sleepAsync(0); + maxRunning = Math.max(maxRunning, running); + running--; + }); + + await Async.forEachAsync(array, fn, { concurrency, weighted: true }); + expect(fn).toHaveBeenCalledTimes(8); + expect(maxRunning).toEqual(expectedConcurrency); + } + ); + + it('ensures that a large operation cannot be scheduled around', async () => { + let running: number = 0; + let maxRunning: number = 0; + + const array: INumberWithWeight[] = [ + { n: 1, weight: 1 }, + { n: 2, weight: 1 }, + { n: 3, weight: 1 }, + { n: 4, weight: 10 }, + { n: 5, weight: 1 }, + { n: 6, weight: 1 }, + { n: 7, weight: 5 }, + { n: 8, weight: 1 } + ]; + + const fn: (item: INumberWithWeight) => Promise = jest.fn(async (item) => { + running++; + await Async.sleepAsync(0); + maxRunning = Math.max(maxRunning, running); + running--; + }); + + await Async.forEachAsync(array, fn, { concurrency: 3, weighted: true }); + expect(fn).toHaveBeenCalledTimes(8); + expect(maxRunning).toEqual(3); + }); + + it('waits for a large operation to finish before scheduling more', async () => { + let running: number = 0; + let maxRunning: number = 0; + + const array: INumberWithWeight[] = [ + { n: 1, weight: 1 }, + { n: 2, weight: 10 }, + { n: 3, weight: 1 }, + { n: 4, weight: 10 }, + { n: 5, weight: 1 }, + { n: 6, weight: 10 }, + { n: 7, weight: 1 }, + { n: 8, weight: 10 } + ]; + + const fn: (item: INumberWithWeight) => Promise = jest.fn(async (item) => { + running++; + await Async.sleepAsync(0); + maxRunning = Math.max(maxRunning, running); + running--; + }); + + await Async.forEachAsync(array, fn, { concurrency: 3, weighted: true, allowOversubscription: true }); + expect(fn).toHaveBeenCalledTimes(8); + expect(maxRunning).toEqual(2); + }); + + it('allows operations with a weight of 0 and schedules them accordingly', async () => { + let running: number = 0; + let maxRunning: number = 0; + + const array: INumberWithWeight[] = [1, 2, 3, 4, 5, 6, 7, 8].map((n) => ({ n, weight: 0 })); + + array.unshift({ n: 9, weight: 3 }); + + array.push({ n: 10, weight: 3 }); + + const fn: (item: INumberWithWeight) => Promise = jest.fn(async (item) => { + running++; + await Async.sleepAsync(0); + maxRunning = Math.max(maxRunning, running); + running--; + }); + + await Async.forEachAsync(array, fn, { concurrency: 3, weighted: true }); + expect(fn).toHaveBeenCalledTimes(10); + expect(maxRunning).toEqual(9); + }); + + it('does not exceed the maxiumum concurrency for an async iterator when weighted', async () => { + let waitingIterators: number = 0; + + let resolve2!: (value: { done: true; value: undefined }) => void; + const signal2: Promise<{ done: true; value: undefined }> = new Promise((resolve, reject) => { + resolve2 = resolve; + }); + + let iteratorIndex: number = 0; + const asyncIterator: AsyncIterator<{ element: number; weight: number }> = { + next: () => { + iteratorIndex++; + if (iteratorIndex < 20) { + return Promise.resolve({ done: false, value: { element: iteratorIndex, weight: 2 } }); + } else { + ++waitingIterators; + return signal2; + } + } + }; + const asyncIterable: AsyncIterable<{ element: number; weight: number }> = { + [Symbol.asyncIterator]: () => asyncIterator + }; + + const finalPromise: Promise = Async.forEachAsync( + asyncIterable, + async (item) => { + // Do nothing + }, + { + concurrency: 4, + weighted: true + } + ); + + // Wait for all the instant resolutions to be done + await Async.sleepAsync(0); + + // The final iteration cycle is locked, so only 1 iterator is waiting. + expect(waitingIterators).toEqual(1); + resolve2({ done: true, value: undefined }); + await finalPromise; + }); }); describe(Async.runWithRetriesAsync.name, () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + it('Correctly handles a sync function that succeeds the first time', async () => { const expectedResult: string = 'RESULT'; const result: string = await Async.runWithRetriesAsync({ action: () => expectedResult, maxRetries: 0 }); @@ -324,6 +573,7 @@ describe(Async.name, () => { it('Correctly handles an async function that succeeds the first time', async () => { const expectedResult: string = 'RESULT'; const result: string = await Async.runWithRetriesAsync({ + // eslint-disable-next-line @typescript-eslint/naming-convention action: async () => expectedResult, maxRetries: 0 }); @@ -346,6 +596,7 @@ describe(Async.name, () => { await expect( async () => await Async.runWithRetriesAsync({ + // eslint-disable-next-line @typescript-eslint/naming-convention action: async () => { throw new Error('error'); }, @@ -370,6 +621,7 @@ describe(Async.name, () => { await expect( async () => await Async.runWithRetriesAsync({ + // eslint-disable-next-line @typescript-eslint/naming-convention action: async () => { throw new Error('error'); }, @@ -414,7 +666,7 @@ describe(Async.name, () => { const expectedResult: string = 'RESULT'; let callCount: number = 0; const sleepSpy: jest.SpyInstance = jest - .spyOn(Async, 'sleep') + .spyOn(Async, 'sleepAsync') .mockImplementation(() => Promise.resolve()); const resultPromise: Promise = Async.runWithRetriesAsync({ @@ -438,10 +690,11 @@ describe(Async.name, () => { const expectedResult: string = 'RESULT'; let callCount: number = 0; const sleepSpy: jest.SpyInstance = jest - .spyOn(Async, 'sleep') + .spyOn(Async, 'sleepAsync') .mockImplementation(() => Promise.resolve()); const resultPromise: Promise = Async.runWithRetriesAsync({ + // eslint-disable-next-line @typescript-eslint/naming-convention action: async () => { if (callCount++ === 0) { throw new Error('error'); @@ -457,6 +710,142 @@ describe(Async.name, () => { expect(sleepSpy).toHaveBeenCalledTimes(1); expect(sleepSpy).toHaveBeenLastCalledWith(5); }); + + describe('allowOversubscription=false operations', () => { + it.each([ + { + concurrency: 4, + weight: 4, + expectedConcurrency: 1, + numberOfTasks: 4 + }, + { + concurrency: 4, + weight: 1, + expectedConcurrency: 4, + numberOfTasks: 4 + }, + { + concurrency: 4, + weight: 5, + expectedConcurrency: 1, + numberOfTasks: 2 + } + ])( + 'enforces strict concurrency limits when allowOversubscription=false: concurrency=$concurrency, weight=$weight, expects max $expectedConcurrency concurrent operations', + async ({ concurrency, weight, expectedConcurrency, numberOfTasks }) => { + let running: number = 0; + let maxRunning: number = 0; + + const array: INumberWithWeight[] = Array.from({ length: numberOfTasks }, (v, i) => i).map((n) => ({ + n, + weight + })); + + const fn: (item: INumberWithWeight) => Promise = jest.fn(async () => { + running++; + await Async.sleepAsync(0); + maxRunning = Math.max(maxRunning, running); + running--; + }); + + await Async.forEachAsync(array, fn, { concurrency, weighted: true, allowOversubscription: false }); + expect(fn).toHaveBeenCalledTimes(numberOfTasks); + expect(maxRunning).toEqual(expectedConcurrency); + } + ); + + it('waits for a small and large operation to finish before scheduling more', async () => { + let running: number = 0; + let maxRunning: number = 0; + + const array: INumberWithWeight[] = [ + { n: 1, weight: 1 }, + { n: 2, weight: 10 }, + { n: 3, weight: 1 }, + { n: 4, weight: 10 }, + { n: 5, weight: 1 }, + { n: 6, weight: 10 }, + { n: 7, weight: 1 }, + { n: 8, weight: 10 } + ]; + + const fn: (item: INumberWithWeight) => Promise = jest.fn(async (item) => { + running++; + await Async.sleepAsync(0); + maxRunning = Math.max(maxRunning, running); + running--; + }); + + await Async.forEachAsync(array, fn, { concurrency: 3, weighted: true, allowOversubscription: false }); + expect(fn).toHaveBeenCalledTimes(8); + expect(maxRunning).toEqual(1); + }); + + it('handles operation with mixed weights', async () => { + const concurrency: number = 3; + let running: number = 0; + let maxRunning: number = 0; + const taskToMaxConcurrency: Record = {}; + + const array: INumberWithWeight[] = [ + { n: 1, weight: 1 }, + { n: 2, weight: 2 }, + { n: 3, weight: concurrency }, + { n: 4, weight: 1 }, + { n: 5, weight: 1 } + ]; + + const fn: (item: INumberWithWeight) => Promise = jest.fn(async (item) => { + running++; + taskToMaxConcurrency[item.n] = running; + await Async.sleepAsync(0); + maxRunning = Math.max(maxRunning, running); + running--; + }); + + await Async.forEachAsync(array, fn, { concurrency, weighted: true, allowOversubscription: false }); + expect(fn).toHaveBeenCalledTimes(5); + expect(maxRunning).toEqual(2); + + expect(taskToMaxConcurrency[1]).toEqual(1); // task 1 + expect(taskToMaxConcurrency[2]).toEqual(2); // task 1 + 2 + expect(taskToMaxConcurrency[3]).toEqual(1); // task 3 + expect(taskToMaxConcurrency[4]).toEqual(1); // task 4 + expect(taskToMaxConcurrency[5]).toEqual(2); // task 4 + 5 + }); + + it('allows operations with weight 0 to be picked up when system is at max concurrency', async () => { + let running: number = 0; + let maxRunning: number = 0; + const taskToMaxConcurrency: Record = {}; + + const array: INumberWithWeight[] = [ + { n: 1, weight: 1 }, + { n: 2, weight: 0 }, + { n: 3, weight: 3 }, + { n: 4, weight: 1 } + ]; + + const fn: (item: INumberWithWeight) => Promise = jest.fn(async (item) => { + running++; + taskToMaxConcurrency[item.n] = running; + maxRunning = Math.max(maxRunning, running); + await Async.sleepAsync(0); + running--; + }); + + await Async.forEachAsync(array, fn, { concurrency: 3, weighted: true, allowOversubscription: false }); + + expect(fn).toHaveBeenCalledTimes(4); + expect(maxRunning).toEqual(2); + + expect(taskToMaxConcurrency[1]).toEqual(1); // task 1 + expect(taskToMaxConcurrency[2]).toEqual(2); // task 1 + 2 + expect(taskToMaxConcurrency[3]).toEqual(2); // task 2 + 3 + expect(taskToMaxConcurrency[4]).toEqual(1); // task 4 + }); + }); }); }); @@ -516,7 +905,7 @@ describe(AsyncQueue.name, () => { queue, async ([item, callback]) => { // Add an async tick to ensure that the queue is actually running concurrently - await Async.sleep(1); + await Async.sleepAsync(0); seenItems++; expect(expectedItems.has(item)).toBe(true); expectedItems.delete(item); @@ -543,7 +932,7 @@ describe(AsyncQueue.name, () => { queue, async ([item, callback]) => { // Add an async tick to ensure that the queue is actually running concurrently - await Async.sleep(1); + await Async.sleepAsync(0); seenItems++; if (item < 4) { expect(expectedItems.has(item)).toBe(true); @@ -577,7 +966,7 @@ describe(AsyncQueue.name, () => { queue, async ([item, callback]) => { // Add an async tick to ensure that the queue is actually running concurrently - await Async.sleep(1); + await Async.sleepAsync(0); seenItems++; if (item < 4) { expect(expectedItems.has(item)).toBe(true); diff --git a/libraries/node-core-library/src/test/EnvironmentMap.test.ts b/libraries/node-core-library/src/test/EnvironmentMap.test.ts index 3e8999400e4..be51e8163b4 100644 --- a/libraries/node-core-library/src/test/EnvironmentMap.test.ts +++ b/libraries/node-core-library/src/test/EnvironmentMap.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import process from 'process'; +import process from 'node:process'; import { EnvironmentMap } from '../EnvironmentMap'; diff --git a/libraries/node-core-library/src/test/Executable.test.ts b/libraries/node-core-library/src/test/Executable.test.ts index 2450024b642..df8e3d9003f 100644 --- a/libraries/node-core-library/src/test/Executable.test.ts +++ b/libraries/node-core-library/src/test/Executable.test.ts @@ -1,221 +1,502 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as os from 'os'; -import * as path from 'path'; -import * as child_process from 'child_process'; - -import { Executable, IExecutableSpawnSyncOptions } from '../Executable'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import type * as child_process from 'node:child_process'; +import { once } from 'node:events'; + +import { + Executable, + parseProcessListOutput, + parseProcessListOutputAsync, + type IProcessInfo, + type IExecutableSpawnSyncOptions, + type IWaitForExitResult, + type IWaitForExitResultWithoutOutput +} from '../Executable'; import { FileSystem } from '../FileSystem'; import { PosixModeBits } from '../PosixModeBits'; import { Text } from '../Text'; +import { Readable } from 'node:stream'; -// The PosixModeBits are intended to be used with bitwise operations. -/* eslint-disable no-bitwise */ - -// Use src/test/test-data instead of lib/test/test-data -const executableFolder: string = path.join(__dirname, '..', '..', 'src', 'test', 'test-data', 'executable'); - -let environment: NodeJS.ProcessEnv; - -if (os.platform() === 'win32') { - environment = { - PATH: [ - path.join(executableFolder, 'skipped'), - path.join(executableFolder, 'success'), - path.join(executableFolder, 'fail'), - path.dirname(process.execPath) // the folder where node.exe can be found - ].join(path.delimiter), - - PATHEXT: '.COM;.EXE;.BAT;.CMD;.VBS', - - TEST_VAR: '123' - }; -} else { - environment = { - PATH: [ - path.join(executableFolder, 'skipped'), - path.join(executableFolder, 'success'), - path.join(executableFolder, 'fail'), - path.dirname(process.execPath), // the folder where node.exe can be found - // These are needed because our example script needs to find bash - '/usr/local/bin', - '/usr/bin', - '/bin' - ].join(path.delimiter), - - TEST_VAR: '123' - }; -} - -const options: IExecutableSpawnSyncOptions = { - environment: environment, - currentWorkingDirectory: executableFolder, - stdio: 'pipe' -}; - -beforeAll(() => { - // Make sure the test folder exists where we expect it - expect(FileSystem.exists(executableFolder)).toEqual(true); - - // Git's core.filemode setting wrongly defaults to true on Windows. This design flaw makes - // it completely impractical to store POSIX file permissions in a cross-platform Git repo. - // So instead we set them before the test runs, and then revert them after the test completes. - if (os.platform() !== 'win32') { - FileSystem.changePosixModeBits( - path.join(executableFolder, 'success', 'npm-binary-wrapper'), - PosixModeBits.AllRead | PosixModeBits.AllWrite | PosixModeBits.AllExecute - ); - FileSystem.changePosixModeBits( - path.join(executableFolder, 'success', 'bash-script.sh'), - PosixModeBits.AllRead | PosixModeBits.AllWrite | PosixModeBits.AllExecute - ); - } -}); +describe('Executable process tests', () => { + // The PosixModeBits are intended to be used with bitwise operations. + /* eslint-disable no-bitwise */ -afterAll(() => { - // Revert the permissions to the defaults - if (os.platform() !== 'win32') { - FileSystem.changePosixModeBits( - path.join(executableFolder, 'success', 'npm-binary-wrapper'), - PosixModeBits.AllRead | PosixModeBits.AllWrite - ); - FileSystem.changePosixModeBits( - path.join(executableFolder, 'success', 'bash-script.sh'), - PosixModeBits.AllRead | PosixModeBits.AllWrite - ); - } -}); + // Use src/test/test-data instead of lib/test/test-data + const executableFolder: string = path.join(__dirname, '..', '..', 'src', 'test', 'test-data', 'executable'); -test('Executable.tryResolve() pathless', () => { - const resolved: string | undefined = Executable.tryResolve('npm-binary-wrapper', options); - expect(resolved).toBeDefined(); - const resolvedRelative: string = Text.replaceAll(path.relative(executableFolder, resolved!), '\\', '/'); + let environment: NodeJS.ProcessEnv; if (os.platform() === 'win32') { - // On Windows, we should find npm-binary-wrapper.cmd instead of npm-binary-wrapper - expect(resolvedRelative).toEqual('success/npm-binary-wrapper.cmd'); + environment = { + PATH: [ + path.join(executableFolder, 'skipped'), + path.join(executableFolder, 'success'), + path.join(executableFolder, 'fail'), + path.dirname(process.execPath) // the folder where node.exe can be found + ].join(path.delimiter), + + PATHEXT: '.COM;.EXE;.BAT;.CMD;.VBS', + + TEST_VAR: '123' + }; } else { - expect(resolvedRelative).toEqual('success/npm-binary-wrapper'); + environment = { + PATH: [ + path.join(executableFolder, 'skipped'), + path.join(executableFolder, 'success'), + path.join(executableFolder, 'fail'), + path.dirname(process.execPath), // the folder where node.exe can be found + // These are needed because our example script needs to find bash + '/usr/local/bin', + '/usr/bin', + '/bin' + ].join(path.delimiter), + + TEST_VAR: '123' + }; } - // We should not find the "missing-extension" at all, because its file extension - // is not executable on Windows (and the execute bit is missing on Unix) - expect(Executable.tryResolve('missing-extension', options)).toBeUndefined(); -}); + const options: IExecutableSpawnSyncOptions = { + environment: environment, + currentWorkingDirectory: executableFolder, + stdio: 'pipe' + }; -test('Executable.tryResolve() with path', () => { - const resolved: string | undefined = Executable.tryResolve('./npm-binary-wrapper', options); - expect(resolved).toBeUndefined(); -}); + beforeAll(() => { + // Make sure the test folder exists where we expect it + expect(FileSystem.exists(executableFolder)).toEqual(true); + + // Git's core.filemode setting wrongly defaults to true on Windows. This design flaw makes + // it completely impractical to store POSIX file permissions in a cross-platform Git repo. + // So instead we set them before the test runs, and then revert them after the test completes. + if (os.platform() !== 'win32') { + FileSystem.changePosixModeBits( + path.join(executableFolder, 'success', 'npm-binary-wrapper'), + PosixModeBits.AllRead | PosixModeBits.AllWrite | PosixModeBits.AllExecute + ); + FileSystem.changePosixModeBits( + path.join(executableFolder, 'fail', 'npm-binary-wrapper'), + PosixModeBits.AllRead | PosixModeBits.AllWrite | PosixModeBits.AllExecute + ); + } + }); + + afterAll(() => { + // Revert the permissions to the defaults + if (os.platform() !== 'win32') { + FileSystem.changePosixModeBits( + path.join(executableFolder, 'success', 'npm-binary-wrapper'), + PosixModeBits.AllRead | PosixModeBits.AllWrite + ); + FileSystem.changePosixModeBits( + path.join(executableFolder, 'fail', 'npm-binary-wrapper'), + PosixModeBits.AllRead | PosixModeBits.AllWrite + ); + } + }); + + test('Executable.tryResolve() pathless', () => { + const resolved: string | undefined = Executable.tryResolve('npm-binary-wrapper', options); + expect(resolved).toBeDefined(); + const resolvedRelative: string = Text.replaceAll(path.relative(executableFolder, resolved!), '\\', '/'); + + if (os.platform() === 'win32') { + // On Windows, we should find npm-binary-wrapper.cmd instead of npm-binary-wrapper + expect(resolvedRelative).toEqual('success/npm-binary-wrapper.cmd'); + } else { + expect(resolvedRelative).toEqual('success/npm-binary-wrapper'); + } + + // We should not find the "missing-extension" at all, because its file extension + // is not executable on Windows (and the execute bit is missing on Unix) + expect(Executable.tryResolve('missing-extension', options)).toBeUndefined(); + }); + + test('Executable.tryResolve() with path', () => { + const resolved: string | undefined = Executable.tryResolve('./npm-binary-wrapper', options); + expect(resolved).toBeUndefined(); + }); + + function executeNpmBinaryWrapper(args: string[]): string[] { + const result: child_process.SpawnSyncReturns = Executable.spawnSync( + 'npm-binary-wrapper', + args, + options + ); + expect(result.error).toBeUndefined(); -function executeNpmBinaryWrapper(args: string[]): string[] { - const result: child_process.SpawnSyncReturns = Executable.spawnSync( - 'npm-binary-wrapper', - args, - options - ); - expect(result.error).toBeUndefined(); + expect(result.stderr).toBeDefined(); + expect(result.stderr.toString()).toEqual(''); - expect(result.stderr).toBeDefined(); - expect(result.stderr.toString()).toEqual(''); + expect(result.stdout).toBeDefined(); + const outputLines: string[] = result.stdout + .toString() + .split(/[\r\n]+/g) + .map((x) => x.trim()); - expect(result.stdout).toBeDefined(); - const outputLines: string[] = result.stdout - .toString() - .split(/[\r\n]+/g) - .map((x) => x.trim()); + let lineIndex: number = 0; + if (os.platform() === 'win32') { + expect(outputLines[lineIndex++]).toEqual('Executing npm-binary-wrapper.cmd with args:'); + } else { + expect(outputLines[lineIndex++]).toEqual('Executing npm-binary-wrapper with args:'); + } + // console.log('npm-binary-wrapper.cmd ARGS: ' + outputLines[lineIndex]); + ++lineIndex; // skip npm-binary-wrapper's args - let lineIndex: number = 0; - if (os.platform() === 'win32') { - expect(outputLines[lineIndex++]).toEqual('Executing npm-binary-wrapper.cmd with args:'); - } else { - expect(outputLines[lineIndex++]).toEqual('Executing npm-binary-wrapper with args:'); - } - // console.log('npm-binary-wrapper.cmd ARGS: ' + outputLines[lineIndex]); - ++lineIndex; // skip npm-binary-wrapper's args + expect(outputLines[lineIndex++]).toEqual('Executing javascript-file.js with args:'); - expect(outputLines[lineIndex++]).toEqual('Executing javascript-file.js with args:'); + const stringifiedArgv: string = outputLines[lineIndex++]; + expect(stringifiedArgv.substr(0, 2)).toEqual('["'); - const stringifiedArgv: string = outputLines[lineIndex++]; - expect(stringifiedArgv.substr(0, 2)).toEqual('["'); + const argv: string[] = JSON.parse(stringifiedArgv); + // Discard the first two array entries whose path is nondeterministic + argv.shift(); // the path to node.exe + argv.shift(); // the path to javascript-file.js - const argv: string[] = JSON.parse(stringifiedArgv); - // Discard the first two array entries whose path is nondeterministic - argv.shift(); // the path to node.exe - argv.shift(); // the path to javascript-file.js + return argv; + } - return argv; -} + test('Executable.spawnSync("npm-binary-wrapper") simple', () => { + const args: string[] = ['arg1', 'arg2', 'arg3']; + expect(executeNpmBinaryWrapper(args)).toEqual(args); + }); + + test('Executable.spawnSync("npm-binary-wrapper") edge cases 1', () => { + // Characters that confuse the CreateProcess() WIN32 API's encoding + const args: string[] = ['', '/', ' \t ', '"a', 'b"', '"c"', '\\"\\d', '!', '!TEST_VAR!']; + expect(executeNpmBinaryWrapper(args)).toEqual(args); + }); + + test('Executable.spawnSync("npm-binary-wrapper") edge cases 2', () => { + // All ASCII punctuation + const args: string[] = [ + // Characters that are impossible to escape for cmd.exe: + // %^&|<> newline + '~!@#$*()_+`={}[]:";\'?,./', + '~!@#$*()_+`={}[]:";\'?,./' + ]; + expect(executeNpmBinaryWrapper(args)).toEqual(args); + }); + + test('Executable.spawnSync("npm-binary-wrapper") edge cases 2', () => { + // All ASCII punctuation + const args: string[] = [ + // Characters that are impossible to escape for cmd.exe: + // %^&|<> newline + '~!@#$*()_+`={}[]:";\'?,./', + '~!@#$*()_+`={}[]:";\'?,./' + ]; + expect(executeNpmBinaryWrapper(args)).toEqual(args); + }); + + test('Executable.spawnSync("npm-binary-wrapper") bad characters', () => { + if (os.platform() === 'win32') { + expect(() => { + executeNpmBinaryWrapper(['abc%123']); + }).toThrow( + 'The command line argument "abc%123" contains a special character "%"' + + ' that cannot be escaped for the Windows shell' + ); + expect(() => { + executeNpmBinaryWrapper(['abc<>123']); + }).toThrow( + 'The command line argument "abc<>123" contains a special character "<"' + + ' that cannot be escaped for the Windows shell' + ); + } + }); + + test('Executable.spawn("npm-binary-wrapper")', async () => { + const executablePath: string = path.join(executableFolder, 'success', 'npm-binary-wrapper'); + + await expect( + (() => { + const childProcess: child_process.ChildProcess = Executable.spawn(executablePath, ['1', '2', '3'], { + environment, + currentWorkingDirectory: executableFolder + }); -test('Executable.spawnSync("npm-binary-wrapper") simple', () => { - const args: string[] = ['arg1', 'arg2', 'arg3']; - expect(executeNpmBinaryWrapper(args)).toEqual(args); -}); + return new Promise((resolve, reject) => { + childProcess.on('exit', (code: number) => { + resolve(`Exit with code=${code}`); + }); + childProcess.on('error', (error: Error) => { + reject(`Failed with error: ${error.message}`); + }); + }); + })() + ).resolves.toBe('Exit with code=0'); + }); + + test('Executable.runToCompletion(Executable.spawn("npm-binary-wrapper")) without output', async () => { + const executablePath: string = path.join(executableFolder, 'success', 'npm-binary-wrapper'); + const childProcess: child_process.ChildProcess = Executable.spawn(executablePath, ['1', '2', '3'], { + environment, + currentWorkingDirectory: executableFolder + }); + const result: IWaitForExitResultWithoutOutput = await Executable.waitForExitAsync(childProcess); + expect(result.exitCode).toEqual(0); + expect(result.signal).toBeNull(); + expect('stdout' in result).toBe(false); + expect('stderr' in result).toBe(false); + }); + + test('Executable.runToCompletion(Executable.spawn("npm-binary-wrapper")) with buffer output', async () => { + const executablePath: string = path.join(executableFolder, 'success', 'npm-binary-wrapper'); + const childProcess: child_process.ChildProcess = Executable.spawn(executablePath, ['1', '2', '3'], { + environment, + currentWorkingDirectory: executableFolder + }); + const result: IWaitForExitResult = await Executable.waitForExitAsync(childProcess, { + encoding: 'buffer' + }); + expect(result.exitCode).toEqual(0); + expect(result.signal).toBeNull(); + expect(Buffer.isBuffer(result.stdout)).toEqual(true); + expect(Buffer.isBuffer(result.stderr)).toEqual(true); + expect(result.stdout.toString('utf8').includes('Executing javascript-file.js with args:')).toBe(true); + expect(result.stderr.toString('utf8')).toEqual(''); + }); + + test('Executable.runToCompletion(Executable.spawn("npm-binary-wrapper")) with string output', async () => { + const executablePath: string = path.join(executableFolder, 'success', 'npm-binary-wrapper'); + const childProcess: child_process.ChildProcess = Executable.spawn(executablePath, ['1', '2', '3'], { + environment, + currentWorkingDirectory: executableFolder + }); + const result: IWaitForExitResult = await Executable.waitForExitAsync(childProcess, { + encoding: 'utf8' + }); + expect(result.exitCode).toEqual(0); + expect(result.signal).toBeNull(); + expect(typeof result.stdout).toEqual('string'); + expect(typeof result.stderr).toEqual('string'); + expect(result.stdout.indexOf('Executing javascript-file.js with args:')).toBeGreaterThanOrEqual(0); + expect(result.stderr).toEqual(''); + }); + + test('Executable.runToCompletion(Executable.spawn("npm-binary-wrapper")) failure', async () => { + const executablePath: string = path.join(executableFolder, 'fail', 'npm-binary-wrapper'); + const childProcess: child_process.ChildProcess = Executable.spawn(executablePath, ['1', '2', '3'], { + environment, + currentWorkingDirectory: executableFolder + }); + const result: IWaitForExitResult = await Executable.waitForExitAsync(childProcess, { + encoding: 'utf8' + }); + expect(result.exitCode).toEqual(1); + expect(result.signal).toBeNull(); + expect(typeof result.stdout).toEqual('string'); + expect(typeof result.stderr).toEqual('string'); + expect(result.stdout).toMatch(/^Executing npm-binary-wrapper(\.cmd)? with args:/); + expect(result.stderr.endsWith('This is a failure')); + }); + + test('Executable.runToCompletion(Executable.spawn("no-terminate")) killed', async () => { + const executablePath: string = path.join(executableFolder, 'no-terminate', 'javascript-file.js'); + const childProcess: child_process.ChildProcess = Executable.spawn( + process.argv0, + [executablePath, '1', '2', '3'], + { + environment, + currentWorkingDirectory: executableFolder + } + ); -test('Executable.spawnSync("npm-binary-wrapper") edge cases 1', () => { - // Characters that confuse the CreateProcess() WIN32 API's encoding - const args: string[] = ['', '/', ' \t ', '"a', 'b"', '"c"', '\\"\\d', '!', '!TEST_VAR!']; - expect(executeNpmBinaryWrapper(args)).toEqual(args); + // Wait for the process to print the error line + expect(childProcess.stderr).toBeDefined(); + const [stderrPre] = await once(childProcess.stderr!, 'data'); + + const killResult: boolean = childProcess.kill('SIGTERM'); + const result: IWaitForExitResult = await Executable.waitForExitAsync(childProcess, { + encoding: 'utf8' + }); + + expect(killResult).toBe(true); + expect(result.signal).toBe('SIGTERM'); + expect(result.exitCode).toBeNull(); + expect(typeof result.stdout).toEqual('string'); + expect(typeof result.stderr).toEqual('string'); + expect(result.stdout).toMatch(/^Executing no-terminate with args:/); + expect((stderrPre.toString('utf8') + result.stderr).includes('This process never terminates')).toBe(true); + }); + + test('Executable.runToCompletion(Executable.spawn("npm-binary-wrapper")) failure with throw on non-zero exit code', async () => { + const executablePath: string = path.join(executableFolder, 'fail', 'npm-binary-wrapper'); + const childProcess: child_process.ChildProcess = Executable.spawn(executablePath, ['1', '2', '3'], { + environment, + currentWorkingDirectory: executableFolder + }); + await expect( + Executable.waitForExitAsync(childProcess, { encoding: 'utf8', throwOnNonZeroExitCode: true }) + ).rejects.toThrow(/exited with code 1/); + }); + + test('Executable.runToCompletion(Executable.spawn("no-terminate")) failure with throw on signal', async () => { + const executablePath: string = path.join(executableFolder, 'no-terminate', 'javascript-file.js'); + const childProcess: child_process.ChildProcess = Executable.spawn( + process.argv0, + [executablePath, '1', '2', '3'], + { + environment, + currentWorkingDirectory: executableFolder + } + ); + childProcess.kill('SIGTERM'); + await expect( + Executable.waitForExitAsync(childProcess, { encoding: 'utf8', throwOnSignal: true }) + ).rejects.toThrow(/Process terminated by SIGTERM/); + }); }); -test('Executable.spawnSync("npm-binary-wrapper") edge cases 2', () => { - // All ASCII punctuation - const args: string[] = [ - // Characters that are impossible to escape for cmd.exe: - // %^&|<> newline - '~!@#$*()_+`={}[]:";\'?,./', - '~!@#$*()_+`={}[]:";\'?,./' +describe('Executable process list', () => { + const WIN32_PROCESS_LIST_OUTPUT: (string | null)[] = [ + 'PPID PID NAME\r\n', + // Test that the parser can handle referencing a parent that is the same as the current process + // Test that the parser can handle multiple return characters + '0 0 System Idle Process\r\n', + '0 1 System\r\n', + // Test that the parser can handle an entry referencing a parent that hasn't been seen yet + '2 4 executable2.exe\r\n', + '1 2 executable0.exe\r\n', + // Test children handling when multiple entries reference the same parent + '1 3 executable1.exe\r\n', + // Test that the parser can handle empty strings + '', + // Test that the parser can handle referencing a parent that doesn't exist + '6 5 executable3.exe\r\n' ]; - expect(executeNpmBinaryWrapper(args)).toEqual(args); -}); -test('Executable.spawnSync("npm-binary-wrapper") edge cases 2', () => { - // All ASCII punctuation - const args: string[] = [ - // Characters that are impossible to escape for cmd.exe: - // %^&|<> newline - '~!@#$*()_+`={}[]:";\'?,./', - '~!@#$*()_+`={}[]:";\'?,./' + const UNIX_PROCESS_LIST_OUTPUT: (string | null)[] = [ + 'PPID PID COMMAND\n', + // Test that the parser can handle referencing a parent that doesn't exist + ' 0 1 init\n', + // Test that the parser can handle a line that is truncated in the middle of a field + // Test that the parser can handle an entry referencing a parent that hasn't been seen yet + // Test that the parser can handle whitespace at the end of the process name. + ' 2 4', + ' process2 \n', + ' 1 2 process0\n', + // Test that the parser can handle empty strings + '', + // Test children handling when multiple entries reference the same parent + ' 1 3 process1\n' ]; - expect(executeNpmBinaryWrapper(args)).toEqual(args); -}); -test('Executable.spawnSync("npm-binary-wrapper") bad characters', () => { - if (os.platform() === 'win32') { - expect(() => { - executeNpmBinaryWrapper(['abc%123']); - }).toThrowError( - 'The command line argument "abc%123" contains a special character "%"' + - ' that cannot be escaped for the Windows shell' + test('contains the current pid (sync)', () => { + const results: ReadonlyMap = Executable.getProcessInfoById(); + const currentProcessInfo: IProcessInfo | undefined = results.get(process.pid); + expect(currentProcessInfo).toBeDefined(); + expect(currentProcessInfo?.parentProcessInfo?.processId).toEqual(process.ppid); + // TODO: Fix parsing of process name as "MainThread" for Node 24 + expect(currentProcessInfo?.processName).toMatch(/(node(\.exe)|MainThread)?$/i); + }); + + test('contains the current pid (async)', async () => { + const results: ReadonlyMap = await Executable.getProcessInfoByIdAsync(); + const currentProcessInfo: IProcessInfo | undefined = results.get(process.pid); + expect(currentProcessInfo).toBeDefined(); + expect(currentProcessInfo?.parentProcessInfo?.processId).toEqual(process.ppid); + // TODO: Fix parsing of process name as "MainThread" for Node 24 + expect(currentProcessInfo?.processName).toMatch(/(node(\.exe)|MainThread)?$/i); + }); + + test('parses win32 output', () => { + const processListMap: Map = parseProcessListOutput( + WIN32_PROCESS_LIST_OUTPUT, + 'win32' ); - expect(() => { - executeNpmBinaryWrapper(['abc<>123']); - }).toThrowError( - 'The command line argument "abc<>123" contains a special character "<"' + - ' that cannot be escaped for the Windows shell' + const results: IProcessInfo[] = [...processListMap.values()].sort(); + + // Expect 7 because we reference a parent that doesn't exist + expect(results.length).toEqual(7); + + // Since snapshot validation of circular entries is difficult to parse by humans, manually validate + // that the parent/child relationships are correct + expect(processListMap.get(0)!.parentProcessInfo).toBeUndefined(); + expect(processListMap.get(1)!.parentProcessInfo).toBe(processListMap.get(0)); + expect(processListMap.get(2)!.parentProcessInfo).toBe(processListMap.get(1)); + expect(processListMap.get(3)!.parentProcessInfo).toBe(processListMap.get(1)); + expect(processListMap.get(4)!.parentProcessInfo).toBe(processListMap.get(2)); + expect(processListMap.get(5)!.parentProcessInfo).toBe(processListMap.get(6)); + expect(processListMap.get(6)!.parentProcessInfo).toBeUndefined(); + + for (const processInfo of results) { + expect(processInfo).toMatchSnapshot(); + } + }); + + test('parses win32 stream output', async () => { + const processListMap: Map = await parseProcessListOutputAsync( + Readable.from(WIN32_PROCESS_LIST_OUTPUT), + 'win32' ); - } -}); - -test('Executable.spawn("npm-binary-wrapper")', async () => { - const executablePath: string = path.join(executableFolder, 'success', 'npm-binary-wrapper'); - - await expect( - (() => { - const childProcess: child_process.ChildProcess = Executable.spawn(executablePath, ['1', '2', '3'], { - environment, - currentWorkingDirectory: executableFolder - }); - - return new Promise((resolve, reject) => { - childProcess.on('exit', (code: number) => { - resolve(`Exit with code=${code}`); - }); - childProcess.on('error', (error: Error) => { - reject(`Failed with error: ${error.message}`); - }); - }); - })() - ).resolves.toBe('Exit with code=0'); + const results: IProcessInfo[] = [...processListMap.values()].sort(); + + // Expect 7 because we reference a parent that doesn't exist + expect(results.length).toEqual(7); + + // Since snapshot validation of circular entries is difficult to parse by humans, manually validate + // that the parent/child relationships are correct + expect(processListMap.get(0)!.parentProcessInfo).toBeUndefined(); + expect(processListMap.get(1)!.parentProcessInfo).toBe(processListMap.get(0)); + expect(processListMap.get(2)!.parentProcessInfo).toBe(processListMap.get(1)); + expect(processListMap.get(3)!.parentProcessInfo).toBe(processListMap.get(1)); + expect(processListMap.get(4)!.parentProcessInfo).toBe(processListMap.get(2)); + expect(processListMap.get(5)!.parentProcessInfo).toBe(processListMap.get(6)); + expect(processListMap.get(6)!.parentProcessInfo).toBeUndefined(); + + for (const processInfo of results) { + expect(processInfo).toMatchSnapshot(); + } + }); + + test('parses unix output', () => { + const processListMap: Map = parseProcessListOutput( + UNIX_PROCESS_LIST_OUTPUT, + 'linux' + ); + const results: IProcessInfo[] = [...processListMap.values()].sort(); + + // Expect 5 because we reference a parent that doesn't exist + expect(results.length).toEqual(5); + + // Since snapshot validation of circular entries is difficult to parse by humans, manually validate + // that the parent/child relationships are correct + expect(processListMap.get(0)!.parentProcessInfo).toBeUndefined(); + expect(processListMap.get(1)!.parentProcessInfo).toBe(processListMap.get(0)); + expect(processListMap.get(2)!.parentProcessInfo).toBe(processListMap.get(1)); + expect(processListMap.get(3)!.parentProcessInfo).toBe(processListMap.get(1)); + expect(processListMap.get(4)!.parentProcessInfo).toBe(processListMap.get(2)); + + for (const processInfo of results) { + expect(processInfo).toMatchSnapshot(); + } + }); + + test('parses unix stream output', async () => { + const processListMap: Map = await parseProcessListOutputAsync( + Readable.from(UNIX_PROCESS_LIST_OUTPUT), + 'linux' + ); + const results: IProcessInfo[] = [...processListMap.values()].sort(); + + // Expect 5 because we reference a parent that doesn't exist + expect(results.length).toEqual(5); + + // Since snapshot validation of circular entries is difficult to parse by humans, manually validate + // that the parent/child relationships are correct + expect(processListMap.get(0)!.parentProcessInfo).toBeUndefined(); + expect(processListMap.get(1)!.parentProcessInfo).toBe(processListMap.get(0)); + expect(processListMap.get(2)!.parentProcessInfo).toBe(processListMap.get(1)); + expect(processListMap.get(3)!.parentProcessInfo).toBe(processListMap.get(1)); + expect(processListMap.get(4)!.parentProcessInfo).toBe(processListMap.get(2)); + + for (const processInfo of results) { + expect(processInfo).toMatchSnapshot(); + } + }); }); diff --git a/libraries/node-core-library/src/test/FileError.test.ts b/libraries/node-core-library/src/test/FileError.test.ts index 5604b70fc74..27e8f728f8e 100644 --- a/libraries/node-core-library/src/test/FileError.test.ts +++ b/libraries/node-core-library/src/test/FileError.test.ts @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; import { FileError } from '../FileError'; +import type { FileLocationStyle } from '../Path'; describe(FileError.name, () => { let originalValue: string | undefined; @@ -333,6 +334,83 @@ describe(`${FileError.name} using unsupported base folder token`, () => { line: 5, column: 12 }); - expect(() => error1.getFormattedErrorMessage({ format: 'Unix' })).toThrowError(); + expect(() => error1.getFormattedErrorMessage({ format: 'Unix' })).toThrow(); + }); +}); + +describe(`${FileError.name} problem matcher patterns`, () => { + let originalValue: string | undefined; + + beforeEach(() => { + originalValue = process.env.RUSHSTACK_FILE_ERROR_BASE_FOLDER; + delete process.env.RUSHSTACK_FILE_ERROR_BASE_FOLDER; + FileError._sanitizedEnvironmentVariable = undefined; + FileError._environmentVariableIsAbsolutePath = false; + }); + + afterEach(() => { + if (originalValue) { + process.env.RUSHSTACK_FILE_ERROR_BASE_FOLDER = originalValue; + } else { + delete process.env.RUSHSTACK_FILE_ERROR_BASE_FOLDER; + } + }); + + const errorStringFormats = ['Unix', 'VisualStudio'] satisfies FileLocationStyle[]; + errorStringFormats.forEach((format) => { + it(`${format} format - message without code`, () => { + const projectFolder = '/path/to/project'; + const relativePathToFile = 'path/to/file'; + const absolutePathToFile = `${projectFolder}/${relativePathToFile}`; + const lineNumber = 5; + const columnNumber = 12; + + const error1 = new FileError('message', { + absolutePath: absolutePathToFile, + projectFolder: projectFolder, + line: lineNumber, + column: columnNumber + }); + const errorMessage = error1.getFormattedErrorMessage({ format }); + const pattern = FileError.getProblemMatcher({ format }); + + const regexp = new RegExp(pattern.regexp); + const matches = regexp.exec(errorMessage); + expect(matches).toBeDefined(); + if (matches) { + expect(matches[pattern.file!]).toEqual(relativePathToFile); + expect(parseInt(matches[pattern.line!], 10)).toEqual(lineNumber); + expect(parseInt(matches[pattern.column!], 10)).toEqual(columnNumber); + expect(matches[pattern.message]).toEqual('message'); + } + }); + + it(`${format} format - message with code`, () => { + const projectFolder = '/path/to/project'; + const relativePathToFile = 'path/to/file'; + const absolutePathToFile = `${projectFolder}/${relativePathToFile}`; + const lineNumber = 5; + const columnNumber = 12; + + const error1 = new FileError('(code) message', { + absolutePath: absolutePathToFile, + projectFolder: projectFolder, + line: lineNumber, + column: columnNumber + }); + const errorMessage = error1.getFormattedErrorMessage({ format }); + const pattern = FileError.getProblemMatcher({ format }); + + const regexp = new RegExp(pattern.regexp); + const matches = regexp.exec(errorMessage); + expect(matches).toBeDefined(); + if (matches) { + expect(matches[pattern.file!]).toEqual(relativePathToFile); + expect(parseInt(matches[pattern.line!], 10)).toEqual(lineNumber); + expect(parseInt(matches[pattern.column!], 10)).toEqual(columnNumber); + expect(matches[pattern.message]).toEqual('message'); + expect(matches[pattern.code!]).toEqual('code'); + } + }); }); }); diff --git a/libraries/node-core-library/src/test/FileSystem.test.ts b/libraries/node-core-library/src/test/FileSystem.test.ts index 9789d3d3a6d..8aa3432f6c3 100644 --- a/libraries/node-core-library/src/test/FileSystem.test.ts +++ b/libraries/node-core-library/src/test/FileSystem.test.ts @@ -1,25 +1,193 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import fs from 'node:fs'; + import { FileSystem } from '../FileSystem'; import { PosixModeBits } from '../PosixModeBits'; -// The PosixModeBits are intended to be used with bitwise operations. -/* eslint-disable no-bitwise */ +// Use a deterministic path inside the project's output folder for temp files +const testTempFolder: string = `${__dirname}/temp`; + +describe(FileSystem.name, () => { + test(FileSystem.formatPosixModeBits.name, () => { + // The PosixModeBits are intended to be used with bitwise operations. + /* eslint-disable no-bitwise */ + let modeBits: number = PosixModeBits.AllRead | PosixModeBits.AllWrite; + + expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rw-rw-'); + + modeBits |= PosixModeBits.GroupExecute; + expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rwxrw-'); + + // Add the group execute bit + modeBits |= PosixModeBits.OthersExecute; + expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rwxrwx'); + + // Add the group execute bit + modeBits &= ~PosixModeBits.AllWrite; + expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-r--r-xr-x'); + /* eslint-enable no-bitwise */ + }); + + describe(FileSystem.isErrnoException.name, () => { + test('Should return false for a non-ErrnoException', () => { + const error: Error = new Error('Test error'); + expect(FileSystem.isErrnoException(error)).toBe(false); + }); + + test('Should return true for an error on a path call', () => { + expect.assertions(1); + try { + fs.openSync(`${__dirname}/nonexistent.txt`, 'r'); + } catch (error) { + expect(FileSystem.isErrnoException(error)).toBe(true); + } + }); + + test('Should return true for an error on a file descriptor call', () => { + expect.assertions(1); + try { + fs.readFileSync(`${__dirname}/nonexistent.txt`); + } catch (error) { + expect(FileSystem.isErrnoException(error)).toBe(true); + } + }); + }); + + describe(FileSystem.createReadStream.name, () => { + const tempDir: string = `${testTempFolder}/createReadStream`; + + beforeEach(async () => { + await FileSystem.ensureFolderAsync(tempDir); + }); + + afterEach(async () => { + await FileSystem.deleteFolderAsync(tempDir); + }); + + test('returns a readable stream for an existing file', async () => { + const filePath: string = `${tempDir}/test.txt`; + await FileSystem.writeFileAsync(filePath, 'hello world'); + + const stream: fs.ReadStream = FileSystem.createReadStream(filePath); + const chunks: Buffer[] = []; + + for await (const chunk of stream) { + chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk); + } + + const result: string = Buffer.concat(chunks).toString(); + expect(result).toBe('hello world'); + }); + + test('stream emits an error for a nonexistent file', async () => { + const filePath: string = `${tempDir}/nonexistent.txt`; + const stream: fs.ReadStream = FileSystem.createReadStream(filePath); + + await expect(async () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + for await (const _chunk of stream) { + fail(); + } + }).rejects.toThrow(/ENOENT/); + }); + }); + + describe(FileSystem.createWriteStream.name, () => { + const tempDir: string = `${testTempFolder}/createWriteStream`; + + beforeEach(async () => { + await FileSystem.ensureFolderAsync(tempDir); + }); + + afterEach(async () => { + await FileSystem.deleteFolderAsync(tempDir); + }); + + test('creates a writable stream that writes data to a file', async () => { + const filePath: string = `${tempDir}/output.txt`; + const stream: fs.WriteStream = FileSystem.createWriteStream(filePath); + + await new Promise((resolve, reject) => { + stream.on('error', reject); + stream.write('hello '); + stream.write('world'); + stream.end(() => resolve()); + }); + + const result: string = await FileSystem.readFileAsync(filePath); + expect(result).toBe('hello world'); + }); + + test('emits an error when the parent folder does not exist and ensureFolderExists is not set', async () => { + const filePath: string = `${tempDir}/nonexistent-folder/output.txt`; + const stream: fs.WriteStream = FileSystem.createWriteStream(filePath); + + await expect( + new Promise((resolve, reject) => { + stream.on('error', reject); + stream.on('open', () => resolve()); + }) + ).rejects.toThrow(/ENOENT/); + }); + + test('creates the parent folder when ensureFolderExists is true', async () => { + const filePath: string = `${tempDir}/new-folder/output.txt`; + const stream: fs.WriteStream = FileSystem.createWriteStream(filePath, { + ensureFolderExists: true + }); + + await new Promise((resolve, reject) => { + stream.on('error', reject); + stream.write('test data'); + stream.end(() => resolve()); + }); + + const result: string = await FileSystem.readFileAsync(filePath); + expect(result).toBe('test data'); + }); + }); + + describe(FileSystem.createWriteStreamAsync.name, () => { + const tempDir: string = `${testTempFolder}/createWriteStreamAsync`; + + beforeEach(async () => { + await FileSystem.ensureFolderAsync(tempDir); + }); + + afterEach(async () => { + await FileSystem.deleteFolderAsync(tempDir); + }); + + test('creates a writable stream that writes data to a file', async () => { + const filePath: string = `${tempDir}/output.txt`; + const stream: fs.WriteStream = await FileSystem.createWriteStreamAsync(filePath); -test('PosixModeBits tests', () => { - let modeBits: number = PosixModeBits.AllRead | PosixModeBits.AllWrite; + await new Promise((resolve, reject) => { + stream.on('error', reject); + stream.write('async hello'); + stream.end(() => resolve()); + }); - expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rw-rw-'); + const result: string = await FileSystem.readFileAsync(filePath); + expect(result).toBe('async hello'); + }); - modeBits |= PosixModeBits.GroupExecute; - expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rwxrw-'); + test('creates the parent folder when ensureFolderExists is true', async () => { + const filePath: string = `${tempDir}/new-folder/output.txt`; + const stream: fs.WriteStream = await FileSystem.createWriteStreamAsync(filePath, { + ensureFolderExists: true + }); - // Add the group execute bit - modeBits |= PosixModeBits.OthersExecute; - expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-rw-rwxrwx'); + await new Promise((resolve, reject) => { + stream.on('error', reject); + stream.write('async test data'); + stream.end(() => resolve()); + }); - // Add the group execute bit - modeBits &= ~PosixModeBits.AllWrite; - expect(FileSystem.formatPosixModeBits(modeBits)).toEqual('-r--r-xr-x'); + const result: string = await FileSystem.readFileAsync(filePath); + expect(result).toBe('async test data'); + }); + }); }); diff --git a/libraries/node-core-library/src/test/Import.test.ts b/libraries/node-core-library/src/test/Import.test.ts index e33eb07dcf0..30732b7ae14 100644 --- a/libraries/node-core-library/src/test/Import.test.ts +++ b/libraries/node-core-library/src/test/Import.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as nodeJsPath from 'path'; +import * as nodeJsPath from 'node:path'; import { Import } from '../Import'; import { PackageJsonLookup } from '../PackageJsonLookup'; import { Path } from '../Path'; @@ -53,7 +53,7 @@ describe(Import.name, () => { Path.convertToSlashes( Import.resolveModule({ modulePath: '@rushstack/heft', baseFolderPath: __dirname }) ) - ).toMatch(/node_modules\/@rushstack\/heft\/lib\/index.js$/); + ).toMatch(/node_modules\/@rushstack\/heft\/lib-commonjs\/index.js$/); }); it('resolves a path inside a dependency', () => { @@ -86,29 +86,29 @@ describe(Import.name, () => { baseFolderPath: nodeJsPath.join(packageRoot, 'node_modules', '@rushstack', 'heft') }) ) - ).toMatch(/node_modules\/@rushstack\/ts-command-line\/lib\/index\.js$/); + ).toMatch(/node_modules\/@rushstack\/ts-command-line\/lib-commonjs\/index\.js$/); }); it('resolves a path inside a dependency of a dependency', () => { expect( Path.convertToSlashes( Import.resolveModule({ - modulePath: '@rushstack/ts-command-line/lib/Constants.js', + modulePath: '@rushstack/ts-command-line/lib-commonjs/Constants.js', baseFolderPath: nodeJsPath.join(packageRoot, 'node_modules', '@rushstack', 'heft') }) ) - ).toMatch(/node_modules\/@rushstack\/ts-command-line\/lib\/Constants\.js$/); + ).toMatch(/node_modules\/@rushstack\/ts-command-line\/lib-commonjs\/Constants\.js$/); }); it('resolves a path inside a dependency of a dependency without an extension', () => { expect( Path.convertToSlashes( Import.resolveModule({ - modulePath: '@rushstack/ts-command-line/lib/Constants', + modulePath: '@rushstack/ts-command-line/lib-commonjs/Constants', baseFolderPath: nodeJsPath.join(packageRoot, 'node_modules', '@rushstack', 'heft') }) ) - ).toMatch(/node_modules\/@rushstack\/ts-command-line\/lib\/Constants\.js$/); + ).toMatch(/node_modules\/@rushstack\/ts-command-line\/lib-commonjs\/Constants\.js$/); }); describe('allowSelfReference', () => { @@ -122,11 +122,23 @@ describe(Import.name, () => { ).toEqual(packageRoot); expect( Import.resolveModule({ - modulePath: '@rushstack/node-core-library/lib/Constants.js', + modulePath: '@rushstack/node-core-library/lib-commonjs/Constants.js', baseFolderPath: __dirname, allowSelfReference: true }) - ).toEqual(nodeJsPath.join(packageRoot, 'lib', 'Constants.js')); + ).toEqual(nodeJsPath.join(packageRoot, 'lib-commonjs', 'Constants.js')); + }); + + it('resolves the real path inside a package with allowSelfReference turned on', () => { + const heftPackageRoot: string = nodeJsPath.join(packageRoot, 'node_modules', '@rushstack', 'heft'); + const heftPackageJsonRealPath: string = require.resolve('@rushstack/heft/package.json'); + expect( + Import.resolveModule({ + modulePath: '@rushstack/heft/package.json', + baseFolderPath: heftPackageRoot, + allowSelfReference: true + }) + ).toEqual(heftPackageJsonRealPath); }); it('throws on an attempt to reference this package without allowSelfReference turned on', () => { @@ -138,7 +150,7 @@ describe(Import.name, () => { ); expectToThrowNormalizedErrorMatchingSnapshot(() => Import.resolveModule({ - modulePath: '@rushstack/node-core-library/lib/Constants.js', + modulePath: '@rushstack/node-core-library/lib-commonjs/Constants.js', baseFolderPath: __dirname }) ); @@ -194,7 +206,7 @@ describe(Import.name, () => { expectToThrowNormalizedErrorMatchingSnapshot(() => Path.convertToSlashes( Import.resolvePackage({ - packageName: '@rushstack/heft/lib/start.js', + packageName: '@rushstack/heft/lib-commonjs/start.js', baseFolderPath: __dirname }) ) @@ -216,7 +228,7 @@ describe(Import.name, () => { expectToThrowNormalizedErrorMatchingSnapshot(() => Path.convertToSlashes( Import.resolvePackage({ - packageName: '@rushstack/ts-command-line/lib/Constants.js', + packageName: '@rushstack/ts-command-line/lib-commonjs/Constants.js', baseFolderPath: nodeJsPath.join(packageRoot, 'node_modules', '@rushstack', 'heft') }) ) @@ -234,10 +246,24 @@ describe(Import.name, () => { ).toEqual(packageRoot); }); + it('resolves the real path of a package with allowSelfReference turned on', () => { + const heftPackageRoot: string = nodeJsPath.join(packageRoot, 'node_modules', '@rushstack', 'heft'); + const resolvedHeftPackageRoot: string = nodeJsPath.dirname( + require.resolve('@rushstack/heft/package.json') + ); + expect( + Import.resolvePackage({ + packageName: '@rushstack/heft', + baseFolderPath: heftPackageRoot, + allowSelfReference: true + }) + ).toEqual(resolvedHeftPackageRoot); + }); + it('fails to resolve a path inside this package with allowSelfReference turned on', () => { expectToThrowNormalizedErrorMatchingSnapshot(() => Import.resolvePackage({ - packageName: '@rushstack/node-core-library/lib/Constants.js', + packageName: '@rushstack/node-core-library/lib-commonjs/Constants.js', baseFolderPath: __dirname, allowSelfReference: true }) diff --git a/libraries/node-core-library/src/test/JsonFile.test.ts b/libraries/node-core-library/src/test/JsonFile.test.ts index fac4e54dea0..f33d0a0877e 100644 --- a/libraries/node-core-library/src/test/JsonFile.test.ts +++ b/libraries/node-core-library/src/test/JsonFile.test.ts @@ -4,7 +4,6 @@ import { JsonFile } from '../JsonFile'; // The PosixModeBits are intended to be used with bitwise operations. -/* eslint-disable no-bitwise */ describe(JsonFile.name, () => { it('adds a header comment', () => { @@ -17,6 +16,7 @@ describe(JsonFile.name, () => { ) ).toMatchSnapshot(); }); + it('adds an empty header comment', () => { expect( JsonFile.stringify( @@ -27,6 +27,7 @@ describe(JsonFile.name, () => { ) ).toMatchSnapshot(); }); + it('allows undefined values when asked', () => { expect( JsonFile.stringify( @@ -47,4 +48,33 @@ describe(JsonFile.name, () => { ) ).toMatchSnapshot(); }); + + it('supports updating a simple file', () => { + expect(JsonFile.updateString('{"a": 1}', { a: 1, b: 2 })).toMatchSnapshot(); + }); + + it('supports updating a simple file with a comment', () => { + expect(JsonFile.updateString(`{\n // comment\n "a": 1\n}`, { a: 1, b: 2 })).toMatchSnapshot(); + }); + + it('supports updating a simple file with a comment and a trailing comma', () => { + expect(JsonFile.updateString(`{\n // comment\n "a": 1,\n}`, { a: 1, b: 2 })).toMatchSnapshot(); + }); + + it('supports updating a simple file with an unquoted property', () => { + expect( + JsonFile.updateString(`{\n // comment\n a: 1,\n}`, { a: 1, b: 2, 'c-123': 3 }) + ).toMatchSnapshot(); + }); + + it('supports parsing keys that map to `Object` properties', () => { + const propertyStrings: string[] = []; + for (const objectKey of Object.getOwnPropertyNames(Object.prototype).sort()) { + propertyStrings.push(`"${objectKey}": 1`); + } + + const jsonString: string = `{\n ${propertyStrings.join(',\n ')}\n}`; + expect(jsonString).toMatchSnapshot('JSON String'); + expect(JsonFile.parseString(jsonString)).toMatchSnapshot('Parsed JSON Object'); + }); }); diff --git a/libraries/node-core-library/src/test/JsonSchema.test.ts b/libraries/node-core-library/src/test/JsonSchema.test.ts index e6b2d252185..44a482d9398 100644 --- a/libraries/node-core-library/src/test/JsonSchema.test.ts +++ b/libraries/node-core-library/src/test/JsonSchema.test.ts @@ -1,32 +1,238 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { JsonFile, JsonObject } from '../JsonFile'; -import { JsonSchema, IJsonSchemaErrorInfo } from '../JsonSchema'; +import { JsonFile, type JsonObject } from '../JsonFile'; +import { JsonSchema, type IJsonSchemaErrorInfo } from '../JsonSchema'; + +const SCHEMA_PATH: string = `${__dirname}/test-data/test-schemas/test-schema.schema.json`; +const DRAFT_04_SCHEMA_PATH: string = `${__dirname}/test-data/test-schemas/test-schema-draft-04.schema.json`; +const DRAFT_07_SCHEMA_PATH: string = `${__dirname}/test-data/test-schemas/test-schema-draft-07.schema.json`; describe(JsonSchema.name, () => { - const schemaPath: string = `${__dirname}/test-data/test-schema.json`; - const schema: JsonSchema = JsonSchema.fromFile(schemaPath); + const schema: JsonSchema = JsonSchema.fromFile(SCHEMA_PATH, { + schemaVersion: 'draft-07' + }); + + describe(JsonFile.loadAndValidate.name, () => { + test('successfully validates a JSON file', () => { + const jsonPath: string = `${__dirname}/test-data/test-schemas/test-valid.schema.json`; + const jsonObject: JsonObject = JsonFile.loadAndValidate(jsonPath, schema); + + expect(jsonObject).toMatchObject({ + exampleString: 'This is a string', + exampleArray: ['apple', 'banana', 'coconut'] + }); + }); - test('loadAndValidate successfully validates a JSON file', () => { - const jsonPath: string = `${__dirname}/test-data/test.json`; - const jsonObject: JsonObject = JsonFile.loadAndValidate(jsonPath, schema); + test('successfully validates a JSON file against a draft-04 schema', () => { + const schemaDraft04: JsonSchema = JsonSchema.fromFile(DRAFT_04_SCHEMA_PATH); - expect(jsonObject).toMatchObject({ - exampleString: 'This is a string', - exampleArray: ['apple', 'banana', 'coconut'] + const jsonPath: string = `${__dirname}/test-data/test-schemas/test-valid.schema.json`; + const jsonObject: JsonObject = JsonFile.loadAndValidate(jsonPath, schemaDraft04); + + expect(jsonObject).toMatchObject({ + exampleString: 'This is a string', + exampleArray: ['apple', 'banana', 'coconut'] + }); + }); + + test('throws an error if the wrong schema version is explicitly specified for an incompatible schema object', () => { + const schemaDraft04: JsonSchema = JsonSchema.fromFile(DRAFT_04_SCHEMA_PATH, { + schemaVersion: 'draft-07' + }); + + const jsonPath: string = `${__dirname}/test-data/test-schemas/test-valid.schema.json`; + expect(() => JsonFile.loadAndValidate(jsonPath, schemaDraft04)).toThrowErrorMatchingSnapshot(); + }); + + test('validates a JSON file against a draft-07 schema', () => { + const schemaDraft07: JsonSchema = JsonSchema.fromFile(DRAFT_07_SCHEMA_PATH); + + const jsonPath: string = `${__dirname}/test-data/test-schemas/test-valid.schema.json`; + const jsonObject: JsonObject = JsonFile.loadAndValidate(jsonPath, schemaDraft07); + + expect(jsonObject).toMatchObject({ + exampleString: 'This is a string', + exampleArray: ['apple', 'banana', 'coconut'] + }); + }); + + test('validates a JSON file using nested schemas', () => { + const schemaPathChild: string = `${__dirname}/test-data/test-schemas/test-schema-nested-child.schema.json`; + const schemaChild: JsonSchema = JsonSchema.fromFile(schemaPathChild); + + const schemaPathNested: string = `${__dirname}/test-data/test-schemas/test-schema-nested.schema.json`; + const schemaNested: JsonSchema = JsonSchema.fromFile(schemaPathNested, { + dependentSchemas: [schemaChild] + }); + + const jsonPath: string = `${__dirname}/test-data/test-schemas/test-valid.schema.json`; + const jsonObject: JsonObject = JsonFile.loadAndValidate(jsonPath, schemaNested); + + expect(jsonObject).toMatchObject({ + exampleString: 'This is a string', + exampleArray: ['apple', 'banana', 'coconut'] + }); + }); + + test('throws an error for an invalid nested schema', () => { + const schemaPathChild: string = `${__dirname}/test-data/test-schemas/test-schema-invalid.schema.json`; + const schemaInvalidChild: JsonSchema = JsonSchema.fromFile(schemaPathChild); + + const schemaPathNested: string = `${__dirname}/test-data/test-schemas/test-schema-nested.schema.json`; + const schemaNested: JsonSchema = JsonSchema.fromFile(schemaPathNested, { + dependentSchemas: [schemaInvalidChild] + }); + + const jsonPath: string = `${__dirname}/test-data/test-schemas/test-valid.schema.json`; + + expect.assertions(1); + try { + JsonFile.loadAndValidate(jsonPath, schemaNested); + } catch (err) { + expect(err.message).toMatchSnapshot(); + } }); }); - test('validateObjectWithCallback successfully reports a compound validation error', () => { - const jsonPath2: string = `${__dirname}/test-data/test2.json`; - const jsonObject2: JsonObject = JsonFile.load(jsonPath2); + describe(JsonSchema.prototype.validateObjectWithCallback.name, () => { + test('successfully reports a compound validation error schema errors', () => { + const jsonPath: string = `${__dirname}/test-data/test-schemas/test-invalid-additional.schema.json`; + const jsonObject: JsonObject = JsonFile.load(jsonPath); + + const errorDetails: string[] = []; + schema.validateObjectWithCallback(jsonObject, (errorInfo: IJsonSchemaErrorInfo) => { + errorDetails.push(errorInfo.details); + }); - const errorDetails: string[] = []; - schema.validateObjectWithCallback(jsonObject2, (errorInfo: IJsonSchemaErrorInfo) => { - errorDetails.push(errorInfo.details); + expect(errorDetails).toMatchSnapshot(); }); + test('successfully reports a compound validation error for format errors', () => { + const jsonPath: string = `${__dirname}/test-data/test-schemas/test-invalid-format.schema.json`; + const jsonObject: JsonObject = JsonFile.load(jsonPath); + + const errorDetails: string[] = []; + schema.validateObjectWithCallback(jsonObject, (errorInfo: IJsonSchemaErrorInfo) => { + errorDetails.push(errorInfo.details); + }); + + expect(errorDetails).toMatchSnapshot(); + }); + }); + + test('accepts vendor extension keywords by default', () => { + const schemaWithVendorExtensions: JsonSchema = JsonSchema.fromLoadedObject( + { + title: 'Test vendor extensions', + 'x-tsdoc-release-tag': '@beta', + 'x-myvendor-html-description': 'bold', + type: 'object', + properties: { + name: { type: 'string' } + }, + additionalProperties: false, + required: ['name'] + }, + { schemaVersion: 'draft-07' } + ); + expect(() => schemaWithVendorExtensions.validateObject({ name: 'hello' }, '')).not.toThrow(); + }); + + test('rejects vendor extension keywords when rejectVendorExtensionKeywords is enabled', () => { + const schemaWithVendorExtensions: JsonSchema = JsonSchema.fromLoadedObject( + { + title: 'Test vendor extensions rejected', + 'x-tsdoc-release-tag': '@beta', + type: 'object', + properties: { + name: { type: 'string' } + }, + additionalProperties: false, + required: ['name'] + }, + { schemaVersion: 'draft-07', rejectVendorExtensionKeywords: true } + ); + expect(() => schemaWithVendorExtensions.validateObject({ name: 'hello' }, '')).toThrow(); + }); + + test('rejects vendor extension keywords that are not at the schema root level', () => { + const schemaWithNestedVendorExtension: JsonSchema = JsonSchema.fromLoadedObject( + { + title: 'Test nested vendor extension', + type: 'object', + properties: { + name: { + type: 'string', + 'x-myvendor-display-name': 'Name field' + } + }, + additionalProperties: false, + required: ['name'] + }, + { schemaVersion: 'draft-07' } + ); + expect(() => schemaWithNestedVendorExtension.validateObject({ name: 'hello' }, '')).toThrow(); + }); + + test('rejects malformed vendor extension keywords that do not match x--', () => { + // Missing vendor segment: "x-tag" has no second hyphen-separated part + const schemaWithMalformedTag: JsonSchema = JsonSchema.fromLoadedObject( + { + title: 'Test malformed vendor extension', + 'x-tag': '@beta', + type: 'object', + properties: { + name: { type: 'string' } + }, + additionalProperties: false, + required: ['name'] + }, + { schemaVersion: 'draft-07' } + ); + expect(() => schemaWithMalformedTag.validateObject({ name: 'hello' }, '')).toThrow(); + + // Uppercase characters in vendor segment + const schemaWithUppercaseTag: JsonSchema = JsonSchema.fromLoadedObject( + { + title: 'Test uppercase vendor extension', + 'x-MyVendor-tag': 'value', + type: 'object', + properties: { + name: { type: 'string' } + }, + additionalProperties: false, + required: ['name'] + }, + { schemaVersion: 'draft-07' } + ); + expect(() => schemaWithUppercaseTag.validateObject({ name: 'hello' }, '')).toThrow(); + }); - expect(errorDetails).toMatchSnapshot(); + test('successfully applies custom formats', () => { + const schemaWithCustomFormat = JsonSchema.fromLoadedObject( + { + title: 'Test Custom Format', + type: 'object', + properties: { + exampleNumber: { + type: 'number', + format: 'uint8' + } + }, + additionalProperties: false, + required: ['exampleNumber'] + }, + { + schemaVersion: 'draft-07', + customFormats: { + uint8: { + type: 'number', + validate: (data) => data >= 0 && data <= 255 + } + } + } + ); + expect(() => schemaWithCustomFormat.validateObject({ exampleNumber: 10 }, '')).not.toThrow(); + expect(() => schemaWithCustomFormat.validateObject({ exampleNumber: 1000 }, '')).toThrow(); }); }); diff --git a/libraries/node-core-library/src/test/LockFile.test.ts b/libraries/node-core-library/src/test/LockFile.test.ts index 34c5dd48736..9c335ecd8e2 100644 --- a/libraries/node-core-library/src/test/LockFile.test.ts +++ b/libraries/node-core-library/src/test/LockFile.test.ts @@ -1,21 +1,26 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { LockFile, getProcessStartTime, getProcessStartTimeFromProcStat } from '../LockFile'; +import * as path from 'node:path'; +import { + LockFile, + getProcessStartTime, + getProcessStartTimeFromProcStat, + _setLockFileGetProcessStartTime +} from '../LockFile'; import { FileSystem } from '../FileSystem'; import { FileWriter } from '../FileWriter'; function setLockFileGetProcessStartTime(fn: (process: number) => string | undefined): void { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (LockFile as any)._getStartTime = fn; + _setLockFileGetProcessStartTime(fn); } // lib/test -const libTestFolder: string = path.resolve(__dirname, '../../lib/test'); +const libTestFolder: string = path.resolve(__dirname, '../../lib-commonjs/test'); describe(LockFile.name, () => { afterEach(() => { + jest.restoreAllMocks(); setLockFileGetProcessStartTime(getProcessStartTime); }); @@ -98,6 +103,48 @@ describe(LockFile.name, () => { }); }); + it('supports two lockfiles in the same process', async () => { + const testFolder: string = `${libTestFolder}/6`; + await FileSystem.ensureEmptyFolderAsync(testFolder); + + const resourceName: string = 'test1'; + + const lock1: LockFile = await LockFile.acquireAsync(testFolder, resourceName); + const lock2Promise: Promise = LockFile.acquireAsync(testFolder, resourceName); + + let lock2Acquired: boolean = false; + lock2Promise + .then(() => { + lock2Acquired = true; + }) + .catch(() => { + fail(); + }); + + const lock1Exists: boolean = await FileSystem.existsAsync(lock1.filePath); + expect(lock1Exists).toEqual(true); + expect(lock1.isReleased).toEqual(false); + expect(lock2Acquired).toEqual(false); + + lock1.release(); + + expect(lock1.isReleased).toEqual(true); + + const lock2: LockFile = await lock2Promise; + + const lock2Exists: boolean = await FileSystem.existsAsync(lock2.filePath); + expect(lock2Exists).toEqual(true); + // The second lock should not be dirty since it is acquired after the first lock is released + expect(lock2.dirtyWhenAcquired).toEqual(false); + expect(lock2.isReleased).toEqual(false); + + expect(lock2Acquired).toEqual(true); + + lock2.release(); + + expect(lock2.isReleased).toEqual(true); + }); + if (process.platform === 'darwin' || process.platform === 'linux') { describe('Linux and Mac', () => { describe(LockFile.getLockFilePath.name, () => { @@ -170,6 +217,7 @@ describe(LockFile.name, () => { // this lock should be undefined since there is an existing lock expect(lock).toBeUndefined(); }); + test('cannot acquire a lock if another valid lock exists with the same start time', () => { // ensure test folder is clean const testFolder: string = path.join(libTestFolder, '3'); @@ -201,85 +249,181 @@ describe(LockFile.name, () => { // this lock should be undefined since there is an existing lock expect(lock).toBeUndefined(); }); - }); - } - if (process.platform === 'win32') { - describe(LockFile.getLockFilePath.name, () => { - test("returns a resolved path that doesn't contain", () => { - expect(path.join(process.cwd(), `test.lock`)).toEqual(LockFile.getLockFilePath('./', 'test')); + test('deletes other hanging lockfiles if corresponding processes are not running anymore and marks dirtyWhenAcquired', () => { + // ensure test folder is clean + const testFolder: string = path.join(libTestFolder, '4'); + FileSystem.ensureEmptyFolder(testFolder); + + const resourceName: string = 'test'; + + const otherPid: number = 999999999; + const otherPidInitialStartTime: string = '2012-01-02 12:53:12'; + + // simulate a hanging lockfile that was not cleaned by other process + const otherPidLockFileName: string = LockFile.getLockFilePath(testFolder, resourceName, otherPid); + const lockFileHandle: FileWriter = FileWriter.open(otherPidLockFileName); + lockFileHandle.write(otherPidInitialStartTime); + lockFileHandle.close(); + FileSystem.updateTimes(otherPidLockFileName, { + accessedTime: 10000, + modifiedTime: 10000 + }); + + // return undefined as if the process was not running anymore + setLockFileGetProcessStartTime((pid: number) => { + return pid === otherPid ? undefined : getProcessStartTime(pid); + }); + + const deleteFileSpy = jest.spyOn(FileSystem, 'deleteFile'); + + const lock: LockFile | undefined = LockFile.tryAcquire(testFolder, resourceName); + + expect(lock).toBeDefined(); + expect(lock!.dirtyWhenAcquired).toEqual(true); + expect(lock!.isReleased).toEqual(false); + + expect(deleteFileSpy).toHaveBeenCalledTimes(1); + expect(deleteFileSpy).toHaveBeenNthCalledWith(1, otherPidLockFileName, { + throwIfNotExists: false + }); + + lock!.release(); }); - test('ignores pid that is passed in', () => { - expect(path.join(process.cwd(), `test.lock`)).toEqual(LockFile.getLockFilePath('./', 'test', 99)); + test("doesn't attempt deleting other process lockfile if it is released in the middle of acquiring process", () => { + // ensure test folder is clean + const testFolder: string = path.join(libTestFolder, '5'); + FileSystem.ensureEmptyFolder(testFolder); + + const resourceName: string = 'test'; + + const otherPid: number = 999999999; + const otherPidStartTime: string = '2012-01-02 12:53:12'; + + const otherPidLockFileName: string = LockFile.getLockFilePath(testFolder, resourceName, otherPid); + + // create an open lockfile for other process + const lockFileHandle: FileWriter = FileWriter.open(otherPidLockFileName); + lockFileHandle.write(otherPidStartTime); + lockFileHandle.close(); + FileSystem.updateTimes(otherPidLockFileName, { + accessedTime: 10000, + modifiedTime: 10000 + }); + + // return other process start time as if it was still running + setLockFileGetProcessStartTime((pid: number) => { + return pid === otherPid ? otherPidStartTime : getProcessStartTime(pid); + }); + + const originalReadFile: typeof FileSystem.readFile = FileSystem.readFile; + jest.spyOn(FileSystem, 'readFile').mockImplementation((filePath: string) => { + if (filePath === otherPidLockFileName) { + // simulate other process lock release right before the current process reads + // other process lockfile to decide on next steps for acquiring the lock + FileSystem.deleteFile(filePath); + } + + return originalReadFile(filePath); + }); + + const deleteFileSpy = jest.spyOn(FileSystem, 'deleteFile'); + + const lock: LockFile | undefined = LockFile.tryAcquire(testFolder, resourceName); + + expect(lock).toBeDefined(); + expect(lock!.dirtyWhenAcquired).toEqual(false); + expect(lock!.isReleased).toEqual(false); + + // Ensure there were no other FileSystem.deleteFile calls after our lock release simulation. + // An extra attempt to delete the lockfile might lead to unexpectedly deleting a new lockfile + // created by another process right after releasing/deleting the previous lockfile + expect(deleteFileSpy).toHaveBeenCalledTimes(1); + expect(deleteFileSpy).toHaveBeenNthCalledWith(1, otherPidLockFileName); + + lock!.release(); }); }); + } - test('will not acquire if existing lock is there', () => { - // ensure test folder is clean - const testFolder: string = path.join(libTestFolder, '1'); - FileSystem.deleteFolder(testFolder); - FileSystem.ensureFolder(testFolder); + if (process.platform === 'win32') { + describe('Windows', () => { + describe(LockFile.getLockFilePath.name, () => { + test("returns a resolved path that doesn't contain", () => { + expect(path.join(process.cwd(), `test.lock`)).toEqual(LockFile.getLockFilePath('./', 'test')); + }); - // create an open lockfile - const resourceName: string = 'test'; - const lockFileName: string = LockFile.getLockFilePath(testFolder, resourceName); - const lockFileHandle: FileWriter = FileWriter.open(lockFileName, { exclusive: true }); + test('ignores pid that is passed in', () => { + expect(path.join(process.cwd(), `test.lock`)).toEqual(LockFile.getLockFilePath('./', 'test', 99)); + }); + }); - const lock: LockFile | undefined = LockFile.tryAcquire(testFolder, resourceName); + test('will not acquire if existing lock is there', () => { + // ensure test folder is clean + const testFolder: string = path.join(libTestFolder, '1'); + FileSystem.deleteFolder(testFolder); + FileSystem.ensureFolder(testFolder); - // this lock should be undefined since there is an existing lock - expect(lock).toBeUndefined(); - lockFileHandle.close(); - }); + // create an open lockfile + const resourceName: string = 'test'; + const lockFileHandle: LockFile | undefined = LockFile.tryAcquire(testFolder, resourceName); + expect(lockFileHandle).toBeDefined(); - test('can acquire and close a dirty lockfile', () => { - // ensure test folder is clean - const testFolder: string = path.join(libTestFolder, '1'); - FileSystem.ensureEmptyFolder(testFolder); + const lock: LockFile | undefined = LockFile.tryAcquire(testFolder, resourceName); + // this lock should be undefined since there is an existing lock + expect(lock).toBeUndefined(); + lockFileHandle!.release(); + }); - // Create a lockfile that is still hanging around on disk, - const resourceName: string = 'test'; - const lockFileName: string = LockFile.getLockFilePath(testFolder, resourceName); - FileWriter.open(lockFileName, { exclusive: true }).close(); + test('can acquire and close a dirty lockfile', () => { + // ensure test folder is clean + const testFolder: string = path.join(libTestFolder, '1'); + FileSystem.ensureEmptyFolder(testFolder); - const lock: LockFile | undefined = LockFile.tryAcquire(testFolder, resourceName); + // Create a lockfile that is still hanging around on disk, + const resourceName: string = 'test'; + const lockFileName: string = LockFile.getLockFilePath(testFolder, resourceName); + FileWriter.open(lockFileName, { exclusive: true }).close(); - expect(lock).toBeDefined(); - expect(lock!.dirtyWhenAcquired).toEqual(true); - expect(lock!.isReleased).toEqual(false); - expect(FileSystem.exists(lockFileName)).toEqual(true); + const lock: LockFile | undefined = LockFile.tryAcquire(testFolder, resourceName); - // Ensure that we can release the "dirty" lockfile - lock!.release(); - expect(FileSystem.exists(lockFileName)).toEqual(false); - expect(lock!.isReleased).toEqual(true); - }); + expect(lock).toBeDefined(); + expect(lock!.dirtyWhenAcquired).toEqual(true); + expect(lock!.isReleased).toEqual(false); + expect(FileSystem.exists(lockFileName)).toEqual(true); - test('can acquire and close a clean lockfile', () => { - // ensure test folder is clean - const testFolder: string = path.join(libTestFolder, '1'); - FileSystem.ensureEmptyFolder(testFolder); + // Ensure that we can release the "dirty" lockfile + lock!.release(); + expect(FileSystem.exists(lockFileName)).toEqual(false); + expect(lock!.isReleased).toEqual(true); + }); - const resourceName: string = 'test'; - const lockFileName: string = LockFile.getLockFilePath(testFolder, resourceName); - const lock: LockFile | undefined = LockFile.tryAcquire(testFolder, resourceName); + test('can acquire and close a clean lockfile', () => { + // ensure test folder is clean + const testFolder: string = path.join(libTestFolder, '1'); + FileSystem.ensureEmptyFolder(testFolder); - // The lockfile should exist and be in a clean state - expect(lock).toBeDefined(); - expect(lock!.dirtyWhenAcquired).toEqual(false); - expect(lock!.isReleased).toEqual(false); - expect(FileSystem.exists(lockFileName)).toEqual(true); + const resourceName: string = 'test'; + const lockFileName: string = LockFile.getLockFilePath(testFolder, resourceName); + const lock: LockFile | undefined = LockFile.tryAcquire(testFolder, resourceName); - // Ensure that we can release the "clean" lockfile - lock!.release(); - expect(FileSystem.exists(lockFileName)).toEqual(false); - expect(lock!.isReleased).toEqual(true); + // The lockfile should exist and be in a clean state + expect(lock).toBeDefined(); + expect(lock!.dirtyWhenAcquired).toEqual(false); + expect(lock!.isReleased).toEqual(false); + expect(FileSystem.exists(lockFileName)).toEqual(true); - // Ensure we cannot release the lockfile twice - expect(() => { + // Ensure that we can release the "clean" lockfile lock!.release(); - }).toThrow(); + expect(FileSystem.exists(lockFileName)).toEqual(false); + expect(lock!.isReleased).toEqual(true); + + // Ensure we cannot release the lockfile twice + expect(() => { + lock!.release(); + }).toThrow(); + }); }); } }); diff --git a/libraries/node-core-library/src/test/MinimumHeap.test.ts b/libraries/node-core-library/src/test/MinimumHeap.test.ts new file mode 100644 index 00000000000..367e5af1bf3 --- /dev/null +++ b/libraries/node-core-library/src/test/MinimumHeap.test.ts @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { MinimumHeap } from '../MinimumHeap'; + +describe(MinimumHeap.name, () => { + it('iterates in sorted order', () => { + const comparator: (a: number, b: number) => number = (a: number, b: number) => a - b; + + const inputs: number[] = []; + for (let heapSize: number = 1; heapSize < 100; heapSize++) { + const heap: MinimumHeap = new MinimumHeap(comparator); + inputs.length = 0; + for (let i = 0; i < heapSize; i++) { + const x: number = Math.random(); + inputs.push(x); + heap.push(x); + } + + const iterationResults: number[] = []; + while (heap.size > 0) { + iterationResults.push(heap.poll()!); + } + + expect(iterationResults).toEqual(inputs.sort(comparator)); + } + }); + + it('returns all input objects', () => { + const comparator: (a: {}, b: {}) => number = (a: {}, b: {}) => 0; + + const heap: MinimumHeap<{}> = new MinimumHeap<{}>(comparator); + const inputs: Set<{}> = new Set([{}, {}, {}, {}, {}, {}]); + for (const x of inputs) { + heap.push(x); + } + + const iterationResults: Set<{}> = new Set(); + while (heap.size > 0) { + iterationResults.add(heap.poll()!); + } + + expect(iterationResults.size).toEqual(inputs.size); + }); + + it('handles interleaved push and poll', () => { + const comparator: (a: {}, b: {}) => number = (a: {}, b: {}) => 0; + + const heap: MinimumHeap<{}> = new MinimumHeap<{}>(comparator); + const input1: Set<{}> = new Set(); + const input2: Set<{}> = new Set(); + for (let heapSize: number = 1; heapSize < 100; heapSize++) { + input1.add({}); + input2.add({}); + + const iterationResults: Set<{}> = new Set(); + + for (const x of input1) { + heap.push(x); + } + + for (const x of input2) { + iterationResults.add(heap.poll()!); + heap.push(x); + } + + while (heap.size > 0) { + iterationResults.add(heap.poll()!); + } + + expect(iterationResults.size).toEqual(input1.size + input2.size); + } + }); +}); diff --git a/libraries/node-core-library/src/test/PackageJsonLookup.test.ts b/libraries/node-core-library/src/test/PackageJsonLookup.test.ts index 7fb44a3d328..81ffc038dcd 100644 --- a/libraries/node-core-library/src/test/PackageJsonLookup.test.ts +++ b/libraries/node-core-library/src/test/PackageJsonLookup.test.ts @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; import { PackageJsonLookup } from '../PackageJsonLookup'; -import { IPackageJson, INodePackageJson } from '../IPackageJson'; +import type { IPackageJson, INodePackageJson } from '../IPackageJson'; import { FileConstants } from '../Constants'; describe(PackageJsonLookup.name, () => { diff --git a/libraries/node-core-library/src/test/PackageName.test.ts b/libraries/node-core-library/src/test/PackageName.test.ts index 3fee972193b..537d473fd45 100644 --- a/libraries/node-core-library/src/test/PackageName.test.ts +++ b/libraries/node-core-library/src/test/PackageName.test.ts @@ -73,7 +73,7 @@ describe(PackageName.name, () => { it(PackageName.parse.name, () => { expect(() => { PackageName.parse('@'); - }).toThrowError('The scope must be followed by a slash'); + }).toThrow('The scope must be followed by a slash'); }); it(PackageName.combineParts.name, () => { @@ -85,14 +85,14 @@ describe(PackageName.name, () => { it(`${PackageName.combineParts.name} errors`, () => { expect(() => { PackageName.combineParts('', '@microsoft/example-package'); - }).toThrowError('The unscopedName cannot start with an "@" character'); + }).toThrow('The unscopedName cannot start with an "@" character'); expect(() => { PackageName.combineParts('@micr!osoft', 'example-package'); - }).toThrowError('The package name "@micr!osoft/example-package" contains an invalid character: "!"'); + }).toThrow('The package name "@micr!osoft/example-package" contains an invalid character: "!"'); expect(() => { PackageName.combineParts('', ''); - }).toThrowError('The package name must not be empty'); + }).toThrow('The package name must not be empty'); }); }); diff --git a/libraries/node-core-library/src/test/Path.test.ts b/libraries/node-core-library/src/test/Path.test.ts index e3ac3587120..eb79424d6de 100644 --- a/libraries/node-core-library/src/test/Path.test.ts +++ b/libraries/node-core-library/src/test/Path.test.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as os from 'os'; -import * as path from 'path'; +import * as os from 'node:os'; +import * as path from 'node:path'; import { Path } from '../Path'; describe(Path.name, () => { diff --git a/libraries/node-core-library/src/test/ProtectableMap.test.ts b/libraries/node-core-library/src/test/ProtectableMap.test.ts index 6c7226e1c24..ec25a0f62f9 100644 --- a/libraries/node-core-library/src/test/ProtectableMap.test.ts +++ b/libraries/node-core-library/src/test/ProtectableMap.test.ts @@ -77,6 +77,6 @@ describe(ProtectableMap.name, () => { const exampleApi: ExampleApi = new ExampleApi(); expect(() => { exampleApi.studentAgesByName.set('Jane', 23); - }).toThrowError('The key must be all upper case: Jane'); + }).toThrow('The key must be all upper case: Jane'); }); }); diff --git a/libraries/node-core-library/src/test/RealNodeModulePath.test.ts b/libraries/node-core-library/src/test/RealNodeModulePath.test.ts new file mode 100644 index 00000000000..b7e60e77e8d --- /dev/null +++ b/libraries/node-core-library/src/test/RealNodeModulePath.test.ts @@ -0,0 +1,353 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type * as fs from 'node:fs'; +import * as path from 'node:path'; + +import { type IRealNodeModulePathResolverOptions, RealNodeModulePathResolver } from '../RealNodeModulePath'; + +const mocklstatSync: jest.Mock, Parameters> = jest.fn(); +const lstatSync: typeof fs.lstatSync = mocklstatSync as unknown as typeof fs.lstatSync; +const mockReadlinkSync: jest.Mock< + ReturnType, + Parameters +> = jest.fn(); +const readlinkSync: typeof fs.readlinkSync = mockReadlinkSync as unknown as typeof fs.readlinkSync; + +const mockFs: IRealNodeModulePathResolverOptions['fs'] = { + lstatSync, + readlinkSync +}; + +describe('realNodeModulePath', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + describe('POSIX paths', () => { + const resolver: RealNodeModulePathResolver = new RealNodeModulePathResolver({ + fs: mockFs, + path: path.posix + }); + const { realNodeModulePath } = resolver; + + beforeEach(() => { + resolver.clearCache(); + }); + + it('should return the input path if it is absolute and does not contain node_modules', () => { + for (const input of ['/foo/bar', '/']) { + expect(realNodeModulePath(input)).toBe(input); + + expect(mocklstatSync).not.toHaveBeenCalled(); + expect(mockReadlinkSync).not.toHaveBeenCalled(); + } + }); + + it('should return the input path if it is not a symbolic link', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => false } as unknown as fs.Stats); + + expect(realNodeModulePath('/foo/node_modules/foo')).toBe('/foo/node_modules/foo'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('/foo/node_modules/foo'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledTimes(0); + }); + + it('should trim a trailing slash from the input path if it is not a symbolic link', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => false } as unknown as fs.Stats); + + expect(realNodeModulePath('/foo/node_modules/foo/')).toBe('/foo/node_modules/foo'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('/foo/node_modules/foo'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledTimes(0); + }); + + it('Should handle absolute link targets', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('/link/target'); + + expect(realNodeModulePath('/foo/node_modules/link')).toBe('/link/target'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('/foo/node_modules/link'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledWith('/foo/node_modules/link', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledTimes(1); + }); + + it('Should trim trailing slash from absolute link targets', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('/link/target/'); + + expect(realNodeModulePath('/foo/node_modules/link/bar')).toBe('/link/target/bar'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('/foo/node_modules/link'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledWith('/foo/node_modules/link', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledTimes(1); + }); + + it('Caches resolved symlinks', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('/link/target'); + + expect(realNodeModulePath('/foo/node_modules/link')).toBe('/link/target'); + expect(realNodeModulePath('/foo/node_modules/link/bar')).toBe('/link/target/bar'); + expect(realNodeModulePath('/foo/node_modules/link/')).toBe('/link/target'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('/foo/node_modules/link'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledWith('/foo/node_modules/link', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledTimes(1); + }); + + it('Caches not-symlinks', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => false } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('/link/target'); + + expect(realNodeModulePath('/foo/node_modules/.pnpm')).toBe('/foo/node_modules/.pnpm'); + expect(realNodeModulePath('/foo/node_modules/.pnpm')).toBe('/foo/node_modules/.pnpm'); + expect(realNodeModulePath('/foo/node_modules/.pnpm')).toBe('/foo/node_modules/.pnpm'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('/foo/node_modules/.pnpm'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledTimes(0); + }); + + it('Should stop after a single absolute link target', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('/link/target'); + + expect(realNodeModulePath('/node_modules/foo/node_modules/link')).toBe('/link/target'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('/node_modules/foo/node_modules/link'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledWith('/node_modules/foo/node_modules/link', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledTimes(1); + }); + + it('Should handle relative link targets', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('../../link/target'); + + expect(realNodeModulePath('/foo/node_modules/link')).toBe('/link/target'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('/foo/node_modules/link'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledWith('/foo/node_modules/link', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledTimes(1); + }); + + it('Should recursively handle relative link targets', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('../../link'); + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('/other/root/bar'); + + expect(realNodeModulePath('/foo/1/2/3/node_modules/bar/node_modules/link/4/5/6')).toBe( + '/other/root/link/4/5/6' + ); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('/foo/1/2/3/node_modules/bar/node_modules/link'); + expect(mocklstatSync.mock.calls[1][0]).toEqual('/foo/1/2/3/node_modules/bar'); + expect(mocklstatSync).toHaveBeenCalledTimes(2); + expect(mockReadlinkSync).toHaveBeenCalledWith('/foo/1/2/3/node_modules/bar/node_modules/link', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledWith('/foo/1/2/3/node_modules/bar', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledTimes(2); + }); + + it('Caches multi-layer resolution', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('../../link'); + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('/other/root/bar'); + + expect(realNodeModulePath('/foo/1/2/3/node_modules/bar/node_modules/link/4/5/6')).toBe( + '/other/root/link/4/5/6' + ); + expect(realNodeModulePath('/foo/1/2/3/node_modules/bar/node_modules/link/a/b')).toBe( + '/other/root/link/a/b' + ); + expect(realNodeModulePath('/foo/1/2/3/node_modules/bar/a/b')).toBe('/other/root/bar/a/b'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('/foo/1/2/3/node_modules/bar/node_modules/link'); + expect(mocklstatSync.mock.calls[1][0]).toEqual('/foo/1/2/3/node_modules/bar'); + expect(mocklstatSync).toHaveBeenCalledTimes(2); + expect(mockReadlinkSync).toHaveBeenCalledWith('/foo/1/2/3/node_modules/bar/node_modules/link', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledWith('/foo/1/2/3/node_modules/bar', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledTimes(2); + }); + }); + + describe('Windows paths', () => { + const resolver: RealNodeModulePathResolver = new RealNodeModulePathResolver({ + fs: mockFs, + path: path.win32 + }); + const { realNodeModulePath } = resolver; + + beforeEach(() => { + resolver.clearCache(); + }); + + it('should return the input path if it is absolute and does not contain node_modules', () => { + for (const input of ['C:\\foo\\bar', 'C:\\']) { + expect(realNodeModulePath(input)).toBe(input); + + expect(mocklstatSync).not.toHaveBeenCalled(); + expect(mockReadlinkSync).not.toHaveBeenCalled(); + } + }); + + it('should trim extra trailing separators from the root', () => { + expect(realNodeModulePath('C:////')).toBe('C:\\'); + + expect(mocklstatSync).not.toHaveBeenCalled(); + expect(mockReadlinkSync).not.toHaveBeenCalled(); + }); + + it('should return the resolved input path if it is absolute and does not contain node_modules', () => { + for (const input of ['C:/foo/bar', 'C:/', 'ab', '../b/c/d']) { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + + expect(realNodeModulePath(input)).toBe(path.win32.resolve(input)); + + expect(mocklstatSync).not.toHaveBeenCalled(); + expect(mockReadlinkSync).not.toHaveBeenCalled(); + } + }); + + it('Should return the input path if the target is not a symbolic link', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => false } as unknown as fs.Stats); + + expect(realNodeModulePath('C:\\foo\\node_modules\\foo')).toBe('C:\\foo\\node_modules\\foo'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('C:\\foo\\node_modules\\foo'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledTimes(0); + }); + + it('Should trim a trailing path separator if the target is not a symbolic link', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => false } as unknown as fs.Stats); + + expect(realNodeModulePath('C:\\foo\\node_modules\\foo\\')).toBe('C:\\foo\\node_modules\\foo'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('C:\\foo\\node_modules\\foo'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledTimes(0); + }); + + it('Should handle absolute link targets', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('C:\\link\\target'); + + expect(realNodeModulePath('C:\\foo\\node_modules\\link\\relative')).toBe('C:\\link\\target\\relative'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('C:\\foo\\node_modules\\link'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledWith('C:\\foo\\node_modules\\link', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledTimes(1); + }); + + it('Should trim a trailing path separator from an absolute link target', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('C:\\link\\target\\'); + + expect(realNodeModulePath('C:\\foo\\node_modules\\link\\relative')).toBe('C:\\link\\target\\relative'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('C:\\foo\\node_modules\\link'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledWith('C:\\foo\\node_modules\\link', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledTimes(1); + }); + + it('Should normalize input', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('C:\\link\\target'); + + expect(realNodeModulePath('C:\\foo\\node_modules\\link')).toBe('C:\\link\\target'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('C:\\foo\\node_modules\\link'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledWith('C:\\foo\\node_modules\\link', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledTimes(1); + }); + + it('Should stop after a single absolute link target', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('D:\\link\\target'); + + expect(realNodeModulePath('C:\\node_modules\\foo\\node_modules\\link')).toBe('D:\\link\\target'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('C:\\node_modules\\foo\\node_modules\\link'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledWith('C:\\node_modules\\foo\\node_modules\\link', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledTimes(1); + }); + + it('Should handle relative link targets', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('..\\..\\link\\target'); + + expect(realNodeModulePath('C:\\foo\\node_modules\\link')).toBe('C:\\link\\target'); + + expect(mocklstatSync.mock.calls[0][0]).toEqual('C:\\foo\\node_modules\\link'); + expect(mocklstatSync).toHaveBeenCalledTimes(1); + expect(mockReadlinkSync).toHaveBeenCalledWith('C:\\foo\\node_modules\\link', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledTimes(1); + }); + + it('Should recursively handle relative link targets', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('..\\..\\link'); + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('D:\\other\\root\\bar'); + + expect(realNodeModulePath('C:\\foo\\1\\2\\3\\node_modules\\bar\\node_modules\\link\\4\\5\\6')).toBe( + 'D:\\other\\root\\link\\4\\5\\6' + ); + + expect(mocklstatSync.mock.calls[0][0]).toEqual( + 'C:\\foo\\1\\2\\3\\node_modules\\bar\\node_modules\\link' + ); + expect(mocklstatSync.mock.calls[1][0]).toEqual('C:\\foo\\1\\2\\3\\node_modules\\bar'); + expect(mocklstatSync).toHaveBeenCalledTimes(2); + expect(mockReadlinkSync).toHaveBeenCalledWith( + 'C:\\foo\\1\\2\\3\\node_modules\\bar\\node_modules\\link', + 'utf8' + ); + expect(mockReadlinkSync).toHaveBeenCalledWith('C:\\foo\\1\\2\\3\\node_modules\\bar', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledTimes(2); + }); + + it('Caches multi-layer resolution', () => { + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('..\\..\\link'); + mocklstatSync.mockReturnValueOnce({ isSymbolicLink: () => true } as unknown as fs.Stats); + mockReadlinkSync.mockReturnValueOnce('D:\\other\\root\\bar'); + + expect(realNodeModulePath('C:\\foo\\1\\2\\3\\node_modules\\bar\\node_modules\\link\\4\\5\\6')).toBe( + 'D:\\other\\root\\link\\4\\5\\6' + ); + expect(realNodeModulePath('C:\\foo\\1\\2\\3\\node_modules\\bar\\node_modules\\link\\a\\b')).toBe( + 'D:\\other\\root\\link\\a\\b' + ); + expect(realNodeModulePath('C:\\foo\\1\\2\\3\\node_modules\\bar\\a\\b')).toBe( + 'D:\\other\\root\\bar\\a\\b' + ); + + expect(mocklstatSync.mock.calls[0][0]).toEqual( + 'C:\\foo\\1\\2\\3\\node_modules\\bar\\node_modules\\link' + ); + expect(mocklstatSync.mock.calls[1][0]).toEqual('C:\\foo\\1\\2\\3\\node_modules\\bar'); + expect(mocklstatSync).toHaveBeenCalledTimes(2); + expect(mockReadlinkSync).toHaveBeenCalledWith( + 'C:\\foo\\1\\2\\3\\node_modules\\bar\\node_modules\\link', + 'utf8' + ); + expect(mockReadlinkSync).toHaveBeenCalledWith('C:\\foo\\1\\2\\3\\node_modules\\bar', 'utf8'); + expect(mockReadlinkSync).toHaveBeenCalledTimes(2); + }); + }); +}); diff --git a/libraries/node-core-library/src/test/Sort.test.ts b/libraries/node-core-library/src/test/Sort.test.ts index 997e5f344c1..b87eef16a37 100644 --- a/libraries/node-core-library/src/test/Sort.test.ts +++ b/libraries/node-core-library/src/test/Sort.test.ts @@ -69,3 +69,50 @@ test('Sort.sortSet', () => { Sort.sortSet(set); expect(Array.from(set)).toEqual(['aardvark', 'goose', 'zebra']); }); + +describe('Sort.sortKeys', () => { + test('Simple object', () => { + const unsortedObj = { q: 0, p: 0, r: 0 }; + const sortedObj = Sort.sortKeys(unsortedObj); + + // Assert that it's not sorted in-place + expect(sortedObj).not.toBe(unsortedObj); + + expect(Object.keys(unsortedObj)).toEqual(['q', 'p', 'r']); + expect(Object.keys(sortedObj)).toEqual(['p', 'q', 'r']); + }); + test('Simple array with objects', () => { + const unsortedArr = [ + { b: 1, a: 0 }, + { y: 0, z: 1, x: 2 } + ]; + const sortedArr = Sort.sortKeys(unsortedArr); + + // Assert that it's not sorted in-place + expect(sortedArr).not.toBe(unsortedArr); + + expect(Object.keys(unsortedArr[0])).toEqual(['b', 'a']); + expect(Object.keys(sortedArr[0])).toEqual(['a', 'b']); + + expect(Object.keys(unsortedArr[1])).toEqual(['y', 'z', 'x']); + expect(Object.keys(sortedArr[1])).toEqual(['x', 'y', 'z']); + }); + test('Nested objects', () => { + const unsortedDeepObj = { c: { q: 0, r: { a: 42 }, p: 2 }, b: { y: 0, z: 1, x: 2 }, a: 2 }; + const sortedDeepObj = Sort.sortKeys(unsortedDeepObj); + + expect(sortedDeepObj).not.toBe(unsortedDeepObj); + + expect(Object.keys(unsortedDeepObj)).toEqual(['c', 'b', 'a']); + expect(Object.keys(sortedDeepObj)).toEqual(['a', 'b', 'c']); + + expect(Object.keys(unsortedDeepObj.b)).toEqual(['y', 'z', 'x']); + expect(Object.keys(sortedDeepObj.b)).toEqual(['x', 'y', 'z']); + + expect(Object.keys(unsortedDeepObj.c)).toEqual(['q', 'r', 'p']); + expect(Object.keys(sortedDeepObj.c)).toEqual(['p', 'q', 'r']); + + expect(Object.keys(unsortedDeepObj.c.r)).toEqual(['a']); + expect(Object.keys(sortedDeepObj.c.r)).toEqual(['a']); + }); +}); diff --git a/libraries/node-core-library/src/test/Text.test.ts b/libraries/node-core-library/src/test/Text.test.ts index 5e6ce48f51c..a4567b25f98 100644 --- a/libraries/node-core-library/src/test/Text.test.ts +++ b/libraries/node-core-library/src/test/Text.test.ts @@ -119,4 +119,17 @@ describe(Text.name, () => { expect(Text.escapeRegExp('a\\c')).toEqual('a\\\\c'); }); }); + + describe(Text.splitByNewLines.name, () => { + it('splits a string by newlines', () => { + expect(Text.splitByNewLines(undefined)).toEqual(undefined); + expect(Text.splitByNewLines('')).toEqual(['']); + expect(Text.splitByNewLines('abc')).toEqual(['abc']); + expect(Text.splitByNewLines('a\nb\nc')).toEqual(['a', 'b', 'c']); + expect(Text.splitByNewLines('a\nb\nc\n')).toEqual(['a', 'b', 'c', '']); + expect(Text.splitByNewLines('a\nb\nc\n\n')).toEqual(['a', 'b', 'c', '', '']); + expect(Text.splitByNewLines('\n\na\nb\nc\n\n')).toEqual(['', '', 'a', 'b', 'c', '', '']); + expect(Text.splitByNewLines('a\r\nb\nc')).toEqual(['a', 'b', 'c']); + }); + }); }); diff --git a/libraries/node-core-library/src/test/__snapshots__/Async.test.ts.snap b/libraries/node-core-library/src/test/__snapshots__/Async.test.ts.snap index 0cd07a6a9b4..aa491186744 100644 --- a/libraries/node-core-library/src/test/__snapshots__/Async.test.ts.snap +++ b/libraries/node-core-library/src/test/__snapshots__/Async.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`Async runWithRetriesAsync Correctly handles a sync function that always throws and allows several retries 1`] = `"error"`; diff --git a/libraries/node-core-library/src/test/__snapshots__/Executable.test.ts.snap b/libraries/node-core-library/src/test/__snapshots__/Executable.test.ts.snap new file mode 100644 index 00000000000..3fff3bff543 --- /dev/null +++ b/libraries/node-core-library/src/test/__snapshots__/Executable.test.ts.snap @@ -0,0 +1,785 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`Executable process list parses unix output 1`] = ` +Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "process2", + }, + ], + "parentProcessInfo": [Circular], + "processId": 2, + "processName": "process0", + }, + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "process1", + }, + ], + "parentProcessInfo": [Circular], + "processId": 1, + "processName": "init", + }, + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "", +} +`; + +exports[`Executable process list parses unix output 2`] = ` +Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "process2", + }, + ], + "parentProcessInfo": [Circular], + "processId": 2, + "processName": "process0", + }, + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "process1", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "", + }, + "processId": 1, + "processName": "init", +} +`; + +exports[`Executable process list parses unix output 3`] = ` +Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "process2", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "process1", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "", + }, + "processId": 1, + "processName": "init", + }, + "processId": 2, + "processName": "process0", +} +`; + +exports[`Executable process list parses unix output 4`] = ` +Object { + "childProcessInfos": Array [], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "process1", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "", + }, + "processId": 1, + "processName": "init", + }, + "processId": 2, + "processName": "process0", + }, + "processId": 4, + "processName": "process2", +} +`; + +exports[`Executable process list parses unix output 5`] = ` +Object { + "childProcessInfos": Array [], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "process2", + }, + ], + "parentProcessInfo": [Circular], + "processId": 2, + "processName": "process0", + }, + [Circular], + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "", + }, + "processId": 1, + "processName": "init", + }, + "processId": 3, + "processName": "process1", +} +`; + +exports[`Executable process list parses unix stream output 1`] = ` +Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "process2", + }, + ], + "parentProcessInfo": [Circular], + "processId": 2, + "processName": "process0", + }, + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "process1", + }, + ], + "parentProcessInfo": [Circular], + "processId": 1, + "processName": "init", + }, + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "", +} +`; + +exports[`Executable process list parses unix stream output 2`] = ` +Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "process2", + }, + ], + "parentProcessInfo": [Circular], + "processId": 2, + "processName": "process0", + }, + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "process1", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "", + }, + "processId": 1, + "processName": "init", +} +`; + +exports[`Executable process list parses unix stream output 3`] = ` +Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "process2", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "process1", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "", + }, + "processId": 1, + "processName": "init", + }, + "processId": 2, + "processName": "process0", +} +`; + +exports[`Executable process list parses unix stream output 4`] = ` +Object { + "childProcessInfos": Array [], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "process1", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "", + }, + "processId": 1, + "processName": "init", + }, + "processId": 2, + "processName": "process0", + }, + "processId": 4, + "processName": "process2", +} +`; + +exports[`Executable process list parses unix stream output 5`] = ` +Object { + "childProcessInfos": Array [], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "process2", + }, + ], + "parentProcessInfo": [Circular], + "processId": 2, + "processName": "process0", + }, + [Circular], + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "", + }, + "processId": 1, + "processName": "init", + }, + "processId": 3, + "processName": "process1", +} +`; + +exports[`Executable process list parses win32 output 1`] = ` +Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "executable2.exe", + }, + ], + "parentProcessInfo": [Circular], + "processId": 2, + "processName": "executable0.exe", + }, + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "executable1.exe", + }, + ], + "parentProcessInfo": [Circular], + "processId": 1, + "processName": "System", + }, + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "System Idle Process", +} +`; + +exports[`Executable process list parses win32 output 2`] = ` +Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "executable2.exe", + }, + ], + "parentProcessInfo": [Circular], + "processId": 2, + "processName": "executable0.exe", + }, + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "executable1.exe", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "System Idle Process", + }, + "processId": 1, + "processName": "System", +} +`; + +exports[`Executable process list parses win32 output 3`] = ` +Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "executable2.exe", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "executable1.exe", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "System Idle Process", + }, + "processId": 1, + "processName": "System", + }, + "processId": 2, + "processName": "executable0.exe", +} +`; + +exports[`Executable process list parses win32 output 4`] = ` +Object { + "childProcessInfos": Array [], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "executable1.exe", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "System Idle Process", + }, + "processId": 1, + "processName": "System", + }, + "processId": 2, + "processName": "executable0.exe", + }, + "processId": 4, + "processName": "executable2.exe", +} +`; + +exports[`Executable process list parses win32 output 5`] = ` +Object { + "childProcessInfos": Array [], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "executable2.exe", + }, + ], + "parentProcessInfo": [Circular], + "processId": 2, + "processName": "executable0.exe", + }, + [Circular], + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "System Idle Process", + }, + "processId": 1, + "processName": "System", + }, + "processId": 3, + "processName": "executable1.exe", +} +`; + +exports[`Executable process list parses win32 output 6`] = ` +Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 5, + "processName": "executable3.exe", + }, + ], + "parentProcessInfo": undefined, + "processId": 6, + "processName": "", +} +`; + +exports[`Executable process list parses win32 output 7`] = ` +Object { + "childProcessInfos": Array [], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 6, + "processName": "", + }, + "processId": 5, + "processName": "executable3.exe", +} +`; + +exports[`Executable process list parses win32 stream output 1`] = ` +Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "executable2.exe", + }, + ], + "parentProcessInfo": [Circular], + "processId": 2, + "processName": "executable0.exe", + }, + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "executable1.exe", + }, + ], + "parentProcessInfo": [Circular], + "processId": 1, + "processName": "System", + }, + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "System Idle Process", +} +`; + +exports[`Executable process list parses win32 stream output 2`] = ` +Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "executable2.exe", + }, + ], + "parentProcessInfo": [Circular], + "processId": 2, + "processName": "executable0.exe", + }, + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "executable1.exe", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "System Idle Process", + }, + "processId": 1, + "processName": "System", +} +`; + +exports[`Executable process list parses win32 stream output 3`] = ` +Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "executable2.exe", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "executable1.exe", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "System Idle Process", + }, + "processId": 1, + "processName": "System", + }, + "processId": 2, + "processName": "executable0.exe", +} +`; + +exports[`Executable process list parses win32 stream output 4`] = ` +Object { + "childProcessInfos": Array [], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 3, + "processName": "executable1.exe", + }, + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "System Idle Process", + }, + "processId": 1, + "processName": "System", + }, + "processId": 2, + "processName": "executable0.exe", + }, + "processId": 4, + "processName": "executable2.exe", +} +`; + +exports[`Executable process list parses win32 stream output 5`] = ` +Object { + "childProcessInfos": Array [], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 4, + "processName": "executable2.exe", + }, + ], + "parentProcessInfo": [Circular], + "processId": 2, + "processName": "executable0.exe", + }, + [Circular], + ], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 0, + "processName": "System Idle Process", + }, + "processId": 1, + "processName": "System", + }, + "processId": 3, + "processName": "executable1.exe", +} +`; + +exports[`Executable process list parses win32 stream output 6`] = ` +Object { + "childProcessInfos": Array [ + Object { + "childProcessInfos": Array [], + "parentProcessInfo": [Circular], + "processId": 5, + "processName": "executable3.exe", + }, + ], + "parentProcessInfo": undefined, + "processId": 6, + "processName": "", +} +`; + +exports[`Executable process list parses win32 stream output 7`] = ` +Object { + "childProcessInfos": Array [], + "parentProcessInfo": Object { + "childProcessInfos": Array [ + [Circular], + ], + "parentProcessInfo": undefined, + "processId": 6, + "processName": "", + }, + "processId": 5, + "processName": "executable3.exe", +} +`; diff --git a/libraries/node-core-library/src/test/__snapshots__/Import.test.ts.snap b/libraries/node-core-library/src/test/__snapshots__/Import.test.ts.snap index 3dcf2195665..eca617cfc1d 100644 --- a/libraries/node-core-library/src/test/__snapshots__/Import.test.ts.snap +++ b/libraries/node-core-library/src/test/__snapshots__/Import.test.ts.snap @@ -1,21 +1,21 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`Import resolveModule allowSelfReference throws on an attempt to reference this package without allowSelfReference turned on 1`] = `"Cannot find module \\"@rushstack/node-core-library\\" from \\"/src/test\\"."`; +exports[`Import resolveModule allowSelfReference throws on an attempt to reference this package without allowSelfReference turned on 1`] = `"Cannot find module \\"@rushstack/node-core-library\\" from \\"/lib-commonjs/test\\": Error: Cannot find module '@rushstack/node-core-library' from ''"`; -exports[`Import resolveModule allowSelfReference throws on an attempt to reference this package without allowSelfReference turned on 2`] = `"Cannot find module \\"@rushstack/node-core-library/lib/Constants.js\\" from \\"/src/test\\"."`; +exports[`Import resolveModule allowSelfReference throws on an attempt to reference this package without allowSelfReference turned on 2`] = `"Cannot find module \\"@rushstack/node-core-library/lib-commonjs/Constants.js\\" from \\"/lib-commonjs/test\\": Error: Cannot find module '@rushstack/node-core-library/lib-commonjs/Constants.js' from ''"`; -exports[`Import resolveModule includeSystemModules throws on an attempt to resolve a non-existing path inside a system module with includeSystemModules turned on 1`] = `"Cannot find module \\"fs/foo/bar\\" from \\"/src/test\\"."`; +exports[`Import resolveModule includeSystemModules throws on an attempt to resolve a non-existing path inside a system module with includeSystemModules turned on 1`] = `"Cannot find module \\"fs/foo/bar\\" from \\"/lib-commonjs/test\\": Error: Cannot find module 'fs/foo/bar' from ''"`; -exports[`Import resolveModule includeSystemModules throws on an attempt to resolve a system module without includeSystemModules turned on 1`] = `"Cannot find module \\"fs\\" from \\"/src/test\\"."`; +exports[`Import resolveModule includeSystemModules throws on an attempt to resolve a system module without includeSystemModules turned on 1`] = `"Cannot find module \\"fs\\" from \\"/lib-commonjs/test\\"."`; -exports[`Import resolvePackage allowSelfReference fails to resolve a path inside this package with allowSelfReference turned on 1`] = `"The package name \\"@rushstack/node-core-library/lib/Constants.js\\" contains an invalid character: \\"/\\""`; +exports[`Import resolvePackage allowSelfReference fails to resolve a path inside this package with allowSelfReference turned on 1`] = `"The package name \\"@rushstack/node-core-library/lib-commonjs/Constants.js\\" contains an invalid character: \\"/\\""`; -exports[`Import resolvePackage allowSelfReference throws on an attempt to reference this package without allowSelfReference turned on 1`] = `"Cannot find package \\"@rushstack/node-core-library\\" from \\"/src/test\\"."`; +exports[`Import resolvePackage allowSelfReference throws on an attempt to reference this package without allowSelfReference turned on 1`] = `"Cannot find package \\"@rushstack/node-core-library\\" from \\"/lib-commonjs/test\\": Error: Cannot find module '@rushstack/node-core-library/package.json' from ''."`; -exports[`Import resolvePackage fails to resolve a path inside a dependency 1`] = `"The package name \\"@rushstack/heft/lib/start.js\\" contains an invalid character: \\"/\\""`; +exports[`Import resolvePackage fails to resolve a path inside a dependency 1`] = `"The package name \\"@rushstack/heft/lib-commonjs/start.js\\" contains an invalid character: \\"/\\""`; -exports[`Import resolvePackage fails to resolve a path inside a dependency of a dependency 1`] = `"The package name \\"@rushstack/ts-command-line/lib/Constants.js\\" contains an invalid character: \\"/\\""`; +exports[`Import resolvePackage fails to resolve a path inside a dependency of a dependency 1`] = `"The package name \\"@rushstack/ts-command-line/lib-commonjs/Constants.js\\" contains an invalid character: \\"/\\""`; exports[`Import resolvePackage includeSystemModules throws on an attempt to resolve a non-existing path inside a system module with includeSystemModules turned on 1`] = `"The package name \\"fs/foo/bar\\" contains an invalid character: \\"/\\""`; -exports[`Import resolvePackage includeSystemModules throws on an attempt to resolve a system module without includeSystemModules turned on 1`] = `"Cannot find package \\"fs\\" from \\"/src/test\\"."`; +exports[`Import resolvePackage includeSystemModules throws on an attempt to resolve a system module without includeSystemModules turned on 1`] = `"Cannot find package \\"fs\\" from \\"/lib-commonjs/test\\": Error: Cannot find module 'fs/package.json' from ''."`; diff --git a/libraries/node-core-library/src/test/__snapshots__/JsonFile.test.ts.snap b/libraries/node-core-library/src/test/__snapshots__/JsonFile.test.ts.snap index 3c290e3aa9a..3a299d76b43 100644 --- a/libraries/node-core-library/src/test/__snapshots__/JsonFile.test.ts.snap +++ b/libraries/node-core-library/src/test/__snapshots__/JsonFile.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`JsonFile adds a header comment 1`] = ` "// header @@ -25,3 +25,70 @@ exports[`JsonFile allows undefined values when asked 2`] = ` "{} " `; + +exports[`JsonFile supports parsing keys that map to \`Object\` properties: JSON String 1`] = ` +"{ + \\"__defineGetter__\\": 1, + \\"__defineSetter__\\": 1, + \\"__lookupGetter__\\": 1, + \\"__lookupSetter__\\": 1, + \\"__proto__\\": 1, + \\"constructor\\": 1, + \\"hasOwnProperty\\": 1, + \\"isPrototypeOf\\": 1, + \\"propertyIsEnumerable\\": 1, + \\"toLocaleString\\": 1, + \\"toString\\": 1, + \\"valueOf\\": 1 +}" +`; + +exports[`JsonFile supports parsing keys that map to \`Object\` properties: Parsed JSON Object 1`] = ` +Object { + "__defineGetter__": 1, + "__defineSetter__": 1, + "__lookupGetter__": 1, + "__lookupSetter__": 1, + "__proto__": 1, + "constructor": 1, + "hasOwnProperty": 1, + "isPrototypeOf": 1, + "propertyIsEnumerable": 1, + "toLocaleString": 1, + "toString": 1, + "valueOf": 1, +} +`; + +exports[`JsonFile supports updating a simple file 1`] = ` +"{\\"a\\": 1,\\"b\\": 2} +" +`; + +exports[`JsonFile supports updating a simple file with a comment 1`] = ` +"{ + // comment + \\"a\\": 1, + \\"b\\": 2 +} +" +`; + +exports[`JsonFile supports updating a simple file with a comment and a trailing comma 1`] = ` +"{ + // comment + \\"a\\": 1, + \\"b\\": 2, +} +" +`; + +exports[`JsonFile supports updating a simple file with an unquoted property 1`] = ` +"{ + // comment + a: 1, + b: 2, + \\"c-123\\": 3, +} +" +`; diff --git a/libraries/node-core-library/src/test/__snapshots__/JsonSchema.test.ts.snap b/libraries/node-core-library/src/test/__snapshots__/JsonSchema.test.ts.snap index 5ad93205398..d683bf859b3 100644 --- a/libraries/node-core-library/src/test/__snapshots__/JsonSchema.test.ts.snap +++ b/libraries/node-core-library/src/test/__snapshots__/JsonSchema.test.ts.snap @@ -1,13 +1,34 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`JsonSchema validateObjectWithCallback successfully reports a compound validation error 1`] = ` +exports[`JsonSchema loadAndValidate throws an error for an invalid nested schema 1`] = ` +"Failed to validate schema \\"test-schema-invalid.schema.json\\": + +Error: #/type + must be equal to one of the allowed values +Error: #/type + must be array +Error: #/type + must match a schema in anyOf" +`; + +exports[`JsonSchema loadAndValidate throws an error if the wrong schema version is explicitly specified for an incompatible schema object 1`] = `"no schema with key or ref \\"http://json-schema.org/draft-04/schema#\\""`; + +exports[`JsonSchema validateObjectWithCallback successfully reports a compound validation error for format errors 1`] = ` +Array [ + " +Error: #/exampleLink + must match format \\"uri\\"", +] +`; + +exports[`JsonSchema validateObjectWithCallback successfully reports a compound validation error schema errors 1`] = ` Array [ " -Error: #/exampleOneOf (Description for exampleOneOf - this i...) - Data does not match any schemas from 'oneOf' - Error: #/exampleOneOf (Description for type1) - Additional properties not allowed: field2 - Error: #/exampleOneOf (Description for type2) - Missing required property: field3", +Error: #/exampleOneOf + must have required property 'field1' +Error: #/exampleOneOf + must have required property 'field3' +Error: #/exampleOneOf + must match exactly one schema in oneOf", ] `; diff --git a/libraries/node-core-library/src/test/__snapshots__/Sort.test.ts.snap b/libraries/node-core-library/src/test/__snapshots__/Sort.test.ts.snap index fcacb7be2f6..15557620941 100644 --- a/libraries/node-core-library/src/test/__snapshots__/Sort.test.ts.snap +++ b/libraries/node-core-library/src/test/__snapshots__/Sort.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`Sort.compareByValue cases 1`] = ` Array [ diff --git a/libraries/node-core-library/src/test/test-data/executable/fail/javascript-file.js b/libraries/node-core-library/src/test/test-data/executable/fail/javascript-file.js new file mode 100644 index 00000000000..039b34c716a --- /dev/null +++ b/libraries/node-core-library/src/test/test-data/executable/fail/javascript-file.js @@ -0,0 +1,2 @@ +console.error('This is a failure'); +process.exit(1); diff --git a/libraries/node-core-library/src/test/test-data/executable/fail/npm-binary-wrapper b/libraries/node-core-library/src/test/test-data/executable/fail/npm-binary-wrapper new file mode 100644 index 00000000000..70bdd7e4277 --- /dev/null +++ b/libraries/node-core-library/src/test/test-data/executable/fail/npm-binary-wrapper @@ -0,0 +1,16 @@ +#!/bin/sh +# This script follows the same pattern as an NPM binary wrapper for non-Windows + +echo "Executing npm-binary-wrapper with args:" +echo "$@" + +basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") + +case `uname` in + *CYGWIN*) basedir=`cygpath -w "$basedir"`;; +esac + +node "$basedir/javascript-file.js" "$@" +ret=$? + +exit $ret diff --git a/libraries/node-core-library/src/test/test-data/executable/fail/npm-binary-wrapper.cmd b/libraries/node-core-library/src/test/test-data/executable/fail/npm-binary-wrapper.cmd new file mode 100644 index 00000000000..c5edb625609 --- /dev/null +++ b/libraries/node-core-library/src/test/test-data/executable/fail/npm-binary-wrapper.cmd @@ -0,0 +1,10 @@ +@ECHO OFF + +REM This script follows the same pattern as an NPM binary wrapper batch file on Windows + +echo Executing npm-binary-wrapper.cmd with args: +echo "%*" + +SETLOCAL +SET PATHEXT=%PATHEXT:;.JS;=;% +node "%~dp0\javascript-file.js" %* diff --git a/libraries/node-core-library/src/test/test-data/executable/no-terminate/javascript-file.js b/libraries/node-core-library/src/test/test-data/executable/no-terminate/javascript-file.js new file mode 100644 index 00000000000..750177a495b --- /dev/null +++ b/libraries/node-core-library/src/test/test-data/executable/no-terminate/javascript-file.js @@ -0,0 +1,19 @@ +const [node, script, ...args] = process.argv; +console.log(`Executing no-terminate with args: ${args.join(' ')}`); + +console.error('This process never terminates'); + +const readline = require('readline'); +const readlineInterface = readline.createInterface({ + input: process.stdin, + output: process.stdout, + terminal: false +}); + +async function runAsync() { + for await (const line of readlineInterface) { + console.log(line); + } +} + +runAsync(); diff --git a/libraries/node-core-library/src/test/test-data/executable/skipped/bash-script.sh b/libraries/node-core-library/src/test/test-data/executable/skipped/bash-script.sh deleted file mode 100644 index f04c038fae6..00000000000 --- a/libraries/node-core-library/src/test/test-data/executable/skipped/bash-script.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -echo THIS SHOULD NOT RUN diff --git a/libraries/node-core-library/src/test/test-data/executable/success/bash-script.sh b/libraries/node-core-library/src/test/test-data/executable/success/bash-script.sh deleted file mode 100644 index 52115b0404b..00000000000 --- a/libraries/node-core-library/src/test/test-data/executable/success/bash-script.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -echo "Executing bash-script.sh with args:" -echo "$@" - -# Print the command-line arguments with [] around each one -for a in $@ ; do - echo -n "[$a] " -done -echo diff --git a/libraries/node-core-library/src/test/test-data/test-schema.json b/libraries/node-core-library/src/test/test-data/test-schema.json deleted file mode 100644 index 9bcce6a4888..00000000000 --- a/libraries/node-core-library/src/test/test-data/test-schema.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "title": "Test Schema File", - "type": "object", - - "definitions": { - "type1": { - "description": "Description for type1", - "type": "object", - "properties": { - "field1": { - "description": "Description for field1", - "type": "string" - } - }, - "additionalProperties": false, - "required": ["field1"] - }, - "type2": { - "description": "Description for type2", - "type": "object", - "properties": { - "field2": { - "description": "Description for field2", - "type": "string" - }, - "field3": { - "description": "Description for field3", - "type": "string" - } - }, - "additionalProperties": false, - "required": ["field2", "field3"] - } - }, - - "properties": { - "exampleString": { - "type": "string" - }, - "exampleArray": { - "type": "array", - "items": { - "type": "string" - } - }, - "exampleOneOf": { - "description": "Description for exampleOneOf - this is a very long description to show in an error message", - "type": "object", - "oneOf": [{ "$ref": "#/definitions/type1" }, { "$ref": "#/definitions/type2" }] - } - }, - "additionalProperties": false, - "required": ["exampleString", "exampleArray"] -} diff --git a/libraries/node-core-library/src/test/test-data/test-schemas/test-invalid-additional.schema.json b/libraries/node-core-library/src/test/test-data/test-schemas/test-invalid-additional.schema.json new file mode 100644 index 00000000000..0618076245a --- /dev/null +++ b/libraries/node-core-library/src/test/test-data/test-schemas/test-invalid-additional.schema.json @@ -0,0 +1,8 @@ +{ + "exampleString": "This is a string", + "exampleLink": "http://example.com", + "exampleArray": ["apple", "banana", "coconut"], + "exampleOneOf": { + "field2": "blah" + } +} diff --git a/libraries/node-core-library/src/test/test-data/test-schemas/test-invalid-format.schema.json b/libraries/node-core-library/src/test/test-data/test-schemas/test-invalid-format.schema.json new file mode 100644 index 00000000000..8c5693588f3 --- /dev/null +++ b/libraries/node-core-library/src/test/test-data/test-schemas/test-invalid-format.schema.json @@ -0,0 +1,5 @@ +{ + "exampleString": "This is a string", + "exampleLink": "//example", + "exampleArray": ["apple", "banana", "coconut"] +} diff --git a/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-draft-04.schema.json b/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-draft-04.schema.json new file mode 100644 index 00000000000..2f1f8bec465 --- /dev/null +++ b/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-draft-04.schema.json @@ -0,0 +1,75 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test Schema File", + "type": "object", + + "definitions": { + "type1": { + "description": "Description for type1", + "type": "object", + "properties": { + "field1": { + "description": "Description for field1", + "type": "string" + } + }, + "additionalProperties": false, + "required": ["field1"] + }, + "type2": { + "description": "Description for type2", + "type": "object", + "properties": { + "field2": { + "description": "Description for field2", + "type": "string" + }, + "field3": { + "description": "Description for field3", + "type": "string" + } + }, + "additionalProperties": false, + "required": ["field2", "field3"] + } + }, + + "properties": { + "exampleString": { + "type": "string" + }, + "exampleLink": { + "type": "string", + "format": "uri" + }, + "exampleArray": { + "type": "array", + "items": { + "type": "string" + } + }, + "exampleOneOf": { + "description": "Description for exampleOneOf - this is a very long description to show in an error message", + "type": "object", + "oneOf": [{ "$ref": "#/definitions/type1" }, { "$ref": "#/definitions/type2" }] + }, + "exampleUniqueObjectArray": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "field2": { + "type": "string" + }, + "field3": { + "type": "string" + } + } + } + } + }, + "additionalProperties": false, + "required": ["exampleString", "exampleArray"] +} diff --git a/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-draft-07.schema.json b/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-draft-07.schema.json new file mode 100644 index 00000000000..40a5c41fc0c --- /dev/null +++ b/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-draft-07.schema.json @@ -0,0 +1,75 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Test Schema File", + "type": "object", + + "definitions": { + "type1": { + "description": "Description for type1", + "type": "object", + "properties": { + "field1": { + "description": "Description for field1", + "type": "string" + } + }, + "additionalProperties": false, + "required": ["field1"] + }, + "type2": { + "description": "Description for type2", + "type": "object", + "properties": { + "field2": { + "description": "Description for field2", + "type": "string" + }, + "field3": { + "description": "Description for field3", + "type": "string" + } + }, + "additionalProperties": false, + "required": ["field2", "field3"] + } + }, + + "properties": { + "exampleString": { + "type": "string" + }, + "exampleLink": { + "type": "string", + "format": "uri" + }, + "exampleArray": { + "type": "array", + "items": { + "type": "string" + } + }, + "exampleOneOf": { + "description": "Description for exampleOneOf - this is a very long description to show in an error message", + "type": "object", + "oneOf": [{ "$ref": "#/definitions/type1" }, { "$ref": "#/definitions/type2" }] + }, + "exampleUniqueObjectArray": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "field2": { + "type": "string" + }, + "field3": { + "type": "string" + } + } + } + } + }, + "additionalProperties": false, + "required": ["exampleString", "exampleArray"] +} diff --git a/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-invalid.schema.json b/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-invalid.schema.json new file mode 100644 index 00000000000..4825f561184 --- /dev/null +++ b/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-invalid.schema.json @@ -0,0 +1,5 @@ +{ + "$id": "http://example.com/schemas/test-schema-nested-child.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "wrong_type" +} diff --git a/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-nested-child.schema.json b/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-nested-child.schema.json new file mode 100644 index 00000000000..ef1836cb7cc --- /dev/null +++ b/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-nested-child.schema.json @@ -0,0 +1,33 @@ +{ + "$id": "http://example.com/schemas/test-schema-nested-child.schema.json", + "definitions": { + "type1": { + "description": "Description for type1", + "type": "object", + "properties": { + "field1": { + "description": "Description for field1", + "type": "string" + } + }, + "additionalProperties": false, + "required": ["field1"] + }, + "type2": { + "description": "Description for type2", + "type": "object", + "properties": { + "field2": { + "description": "Description for field2", + "type": "string" + }, + "field3": { + "description": "Description for field3", + "type": "string" + } + }, + "additionalProperties": false, + "required": ["field2", "field3"] + } + } +} diff --git a/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-nested.schema.json b/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-nested.schema.json new file mode 100644 index 00000000000..7e25cf3db3c --- /dev/null +++ b/libraries/node-core-library/src/test/test-data/test-schemas/test-schema-nested.schema.json @@ -0,0 +1,36 @@ +{ + "$id": "http://example.com/schemas/test-schema-nested.schema.json", + "title": "Test Schema File", + "type": "object", + + "properties": { + "exampleString": { + "type": "string" + }, + "exampleLink": { + "type": "string", + "format": "uri" + }, + "exampleArray": { + "type": "array", + "items": { + "type": "string" + } + }, + "exampleOneOf": { + "description": "Description for exampleOneOf - this is a very long description to show in an error message", + "type": "object", + "oneOf": [ + { "$ref": "test-schema-nested-child.schema.json#/definitions/type1" }, + { "$ref": "test-schema-nested-child.schema.json#/definitions/type2" } + ] + }, + "exampleUniqueObjectArray": { + "type": "array", + "uniqueItems": true, + "items": { "$ref": "test-schema-nested-child.schema.json#/definitions/type2" } + } + }, + "additionalProperties": false, + "required": ["exampleString", "exampleArray"] +} diff --git a/libraries/node-core-library/src/test/test-data/test-schemas/test-schema.schema.json b/libraries/node-core-library/src/test/test-data/test-schemas/test-schema.schema.json new file mode 100644 index 00000000000..b49f3165f38 --- /dev/null +++ b/libraries/node-core-library/src/test/test-data/test-schemas/test-schema.schema.json @@ -0,0 +1,74 @@ +{ + "title": "Test Schema File", + "type": "object", + + "definitions": { + "type1": { + "description": "Description for type1", + "type": "object", + "properties": { + "field1": { + "description": "Description for field1", + "type": "string" + } + }, + "additionalProperties": false, + "required": ["field1"] + }, + "type2": { + "description": "Description for type2", + "type": "object", + "properties": { + "field2": { + "description": "Description for field2", + "type": "string" + }, + "field3": { + "description": "Description for field3", + "type": "string" + } + }, + "additionalProperties": false, + "required": ["field2", "field3"] + } + }, + + "properties": { + "exampleString": { + "type": "string" + }, + "exampleLink": { + "type": "string", + "format": "uri" + }, + "exampleArray": { + "type": "array", + "items": { + "type": "string" + } + }, + "exampleOneOf": { + "description": "Description for exampleOneOf - this is a very long description to show in an error message", + "type": "object", + "oneOf": [{ "$ref": "#/definitions/type1" }, { "$ref": "#/definitions/type2" }] + }, + "exampleUniqueObjectArray": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "field2": { + "type": "string" + }, + "field3": { + "type": "string" + } + } + } + } + }, + "additionalProperties": false, + "required": ["exampleString", "exampleArray"] +} diff --git a/libraries/node-core-library/src/test/test-data/test-schemas/test-valid.schema.json b/libraries/node-core-library/src/test/test-data/test-schemas/test-valid.schema.json new file mode 100644 index 00000000000..879ef2a0f36 --- /dev/null +++ b/libraries/node-core-library/src/test/test-data/test-schemas/test-valid.schema.json @@ -0,0 +1,15 @@ +{ + "exampleString": "This is a string", + "exampleLink": "http://example.com", + "exampleArray": ["apple", "banana", "coconut"], + "exampleUniqueObjectArray": [ + { + "field2": "a", + "field3": "b" + }, + { + "field2": "c", + "field3": "d" + } + ] +} diff --git a/libraries/node-core-library/src/test/test-data/test.json b/libraries/node-core-library/src/test/test-data/test.json deleted file mode 100644 index 0d0cafa9b9b..00000000000 --- a/libraries/node-core-library/src/test/test-data/test.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "exampleString": "This is a string", - "exampleArray": ["apple", "banana", "coconut"] -} diff --git a/libraries/node-core-library/src/test/test-data/test2.json b/libraries/node-core-library/src/test/test-data/test2.json deleted file mode 100644 index 3f5d37c1c31..00000000000 --- a/libraries/node-core-library/src/test/test-data/test2.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "exampleString": "This is a string", - "exampleArray": ["apple", "banana", "coconut"], - "exampleOneOf": { - "field2": "blah" - } -} diff --git a/libraries/node-core-library/src/test/writeBuffersToFile.test.ts b/libraries/node-core-library/src/test/writeBuffersToFile.test.ts new file mode 100644 index 00000000000..9e3c8bf086a --- /dev/null +++ b/libraries/node-core-library/src/test/writeBuffersToFile.test.ts @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const openHandle: jest.Mock<{}> = jest.fn(); + +const closeSync: jest.Mock<{}> = jest.fn(); +const ensureDir: jest.Mock<{}> = jest.fn(); +const ensureDirSync: jest.Mock<{}> = jest.fn(); +const openSync: jest.Mock<{}> = jest.fn(); +const writevSync: jest.Mock<{}> = jest.fn(); + +jest.mock('fs-extra', () => { + return { + closeSync, + ensureDir, + ensureDirSync, + openSync, + writevSync + }; +}); +jest.mock('node:fs/promises', () => { + return { + open: openHandle + }; +}); +jest.mock('../Text', () => { + return { + Encoding: { + Utf8: 'utf8' + } + }; +}); + +describe('FileSystem', () => { + const content: Uint8Array[] = []; + let totalBytes: number = 0; + let FileSystem: typeof import('../FileSystem').FileSystem; + + beforeAll(async () => { + FileSystem = (await import('../FileSystem')).FileSystem; + totalBytes = 0; + let nextValue = 37; + for (let i = 0; i < 10; i++) { + const arr: Uint8Array = new Uint8Array(i + 1); + content[i] = arr; + for (let j = 0; j < arr.length; j++) { + arr[j] = nextValue; + // 256 and 11 are coprime, so this sequence will cover all 256 values. + // These are deliberately not the ordinal index just to ensure that an index isn't accidentally being written to the file. + // eslint-disable-next-line no-bitwise + nextValue = (nextValue + 11) & 0xff; + } + totalBytes += arr.length; + } + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('writeBuffersToFile', () => { + it('handles a single-shot write', () => { + const sampleFd: number = 42; + openSync.mockReturnValue(sampleFd); + writevSync.mockImplementation((fd: number, buffers: Uint8Array[]) => { + expect(fd).toEqual(sampleFd); + expect(buffers).toEqual(content); + return totalBytes; + }); + + FileSystem.writeBuffersToFile('/fake/path', content); + expect(openSync).toHaveBeenCalledWith('/fake/path', 'w'); + expect(closeSync).toHaveBeenCalledWith(sampleFd); + expect(writevSync).toHaveBeenCalledTimes(1); + }); + + for (let i = 0; i < totalBytes; i++) { + const increment: number = i; + const expectedCallCount = Math.ceil(totalBytes / increment); + const expectedData = Buffer.concat(content); + const sampleFd: number = 42; + + it(`handles a multi-shot write writing ${increment} bytes at a time`, () => { + const actual = Buffer.alloc(totalBytes); + let written: number = 0; + openSync.mockReturnValue(sampleFd); + writevSync.mockImplementation((fd: number, buffers: Uint8Array[]) => { + expect(fd).toEqual(sampleFd); + const writtenThisTime: number = Math.min(increment, totalBytes - written); + let bufIndex: number = 0; + let bufOffset: number = 0; + for (let j = 0; j < writtenThisTime; j++) { + actual[written] = buffers[bufIndex][bufOffset]; + bufOffset++; + written++; + if (bufOffset === buffers[bufIndex].length) { + bufIndex++; + bufOffset = 0; + } + } + return writtenThisTime; + }); + + FileSystem.writeBuffersToFile('/fake/path', content); + expect(openSync).toHaveBeenCalledWith('/fake/path', 'w'); + expect(closeSync).toHaveBeenCalledWith(sampleFd); + expect(writevSync).toHaveBeenCalledTimes(expectedCallCount); + expect(actual.equals(expectedData)).toBeTruthy(); + }); + } + }); + + describe('writeBuffersToFileAsync', () => { + it('handles a single-shot write', async () => { + const sampleHandle = { + close: jest.fn(), + writev: jest.fn() + }; + openHandle.mockReturnValue(sampleHandle); + sampleHandle.writev.mockImplementation((buffers: Uint8Array[]) => { + expect(buffers).toEqual(content); + return { bytesWritten: totalBytes }; + }); + + await FileSystem.writeBuffersToFileAsync('/fake/path', content); + expect(openHandle).toHaveBeenCalledWith('/fake/path', 'w'); + expect(sampleHandle.close).toHaveBeenCalledTimes(1); + expect(sampleHandle.writev).toHaveBeenCalledTimes(1); + }); + + for (let i = 0; i < totalBytes; i++) { + const increment: number = i; + const expectedCallCount = Math.ceil(totalBytes / increment); + const expectedData = Buffer.concat(content); + it(`handles a multi-shot write writing ${increment} bytes at a time`, async () => { + const sampleHandle = { + close: jest.fn(), + writev: jest.fn() + }; + const actual = Buffer.alloc(totalBytes); + let written: number = 0; + openHandle.mockReturnValue(sampleHandle); + sampleHandle.writev.mockImplementation((buffers: Uint8Array[]) => { + const writtenThisTime: number = Math.min(increment, totalBytes - written); + let bufIndex: number = 0; + let bufOffset: number = 0; + for (let j = 0; j < writtenThisTime; j++) { + actual[written] = buffers[bufIndex][bufOffset]; + bufOffset++; + written++; + if (bufOffset === buffers[bufIndex].length) { + bufIndex++; + bufOffset = 0; + } + } + return { bytesWritten: writtenThisTime }; + }); + + await FileSystem.writeBuffersToFileAsync('/fake/path', content); + expect(openHandle).toHaveBeenCalledWith('/fake/path', 'w'); + expect(sampleHandle.close).toHaveBeenCalledTimes(1); + expect(sampleHandle.writev).toHaveBeenCalledTimes(expectedCallCount); + expect(actual.equals(expectedData)).toBeTruthy(); + }); + } + }); +}); diff --git a/libraries/node-core-library/src/user/getHomeFolder.ts b/libraries/node-core-library/src/user/getHomeFolder.ts new file mode 100644 index 00000000000..61db1b38714 --- /dev/null +++ b/libraries/node-core-library/src/user/getHomeFolder.ts @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { FileSystem } from '../FileSystem'; + +let _cachedHomeFolder: string | undefined; + +/** + * Returns the current user's home folder path. + * Throws if it cannot be determined. Successful results are cached. + * @public + */ +export function getHomeFolder(): string { + if (_cachedHomeFolder !== undefined) { + return _cachedHomeFolder; + } + + const unresolvedUserFolder: string | undefined = + process.env[process.platform === 'win32' ? 'USERPROFILE' : 'HOME']; + const dirError: string = "Unable to determine the current user's home directory"; + if (unresolvedUserFolder === undefined) { + throw new Error(dirError); + } + + const homeFolder: string = path.resolve(unresolvedUserFolder); + if (!FileSystem.exists(homeFolder)) { + throw new Error(dirError); + } + + _cachedHomeFolder = homeFolder; + + return homeFolder; +} diff --git a/libraries/node-core-library/src/user/index.ts b/libraries/node-core-library/src/user/index.ts new file mode 100644 index 00000000000..9e4ef36360e --- /dev/null +++ b/libraries/node-core-library/src/user/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export { getHomeFolder } from './getHomeFolder'; diff --git a/libraries/node-core-library/tsconfig.json b/libraries/node-core-library/tsconfig.json index fbc2f5c0a6c..1a33d17b873 100644 --- a/libraries/node-core-library/tsconfig.json +++ b/libraries/node-core-library/tsconfig.json @@ -1,7 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["heft-jest", "node"] - } + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/libraries/npm-check-fork/.npmignore b/libraries/npm-check-fork/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/libraries/npm-check-fork/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/npm-check-fork/CHANGELOG.json b/libraries/npm-check-fork/CHANGELOG.json new file mode 100644 index 00000000000..13b1061ced4 --- /dev/null +++ b/libraries/npm-check-fork/CHANGELOG.json @@ -0,0 +1,526 @@ +{ + "name": "@rushstack/npm-check-fork", + "entries": [ + { + "version": "0.2.22", + "tag": "@rushstack/npm-check-fork_v0.2.22", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "0.2.21", + "tag": "@rushstack/npm-check-fork_v0.2.21", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "0.2.20", + "tag": "@rushstack/npm-check-fork_v0.2.20", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "0.2.19", + "tag": "@rushstack/npm-check-fork_v0.2.19", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "0.2.18", + "tag": "@rushstack/npm-check-fork_v0.2.18", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "0.2.17", + "tag": "@rushstack/npm-check-fork_v0.2.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "0.2.16", + "tag": "@rushstack/npm-check-fork_v0.2.16", + "date": "Mon, 20 Apr 2026 15:15:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "0.2.15", + "tag": "@rushstack/npm-check-fork_v0.2.15", + "date": "Sat, 18 Apr 2026 03:47:09 GMT", + "comments": { + "patch": [ + { + "comment": "Remove giturl dependency; replace with original toHttpsUrl() implementation using the native URL class." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/npm-check-fork_v0.2.14", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "patch": [ + { + "comment": "Bump semver." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/npm-check-fork_v0.2.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "patch": [ + { + "comment": "Remove dependecy on `lodash`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/npm-check-fork_v0.2.12", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/npm-check-fork_v0.2.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/npm-check-fork_v0.2.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "patch": [ + { + "comment": "Bump lodash 4.18.1 to address CVEs GHSA-r5fr-rjxr-66jc, GHSA-f23m-r3pf-42rh" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/npm-check-fork_v0.2.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/npm-check-fork_v0.2.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/npm-check-fork_v0.2.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/npm-check-fork_v0.2.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/npm-check-fork_v0.2.5", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/npm-check-fork_v0.2.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/npm-check-fork_v0.2.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/npm-check-fork_v0.2.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/npm-check-fork_v0.2.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/npm-check-fork_v0.2.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "0.1.14", + "tag": "@rushstack/npm-check-fork_v0.1.14", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "patch": [ + { + "comment": "Upgrade `lodash` dependency from `~4.17.15` to `~4.17.23`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "0.1.13", + "tag": "@rushstack/npm-check-fork_v0.1.13", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "0.1.12", + "tag": "@rushstack/npm-check-fork_v0.1.12", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "0.1.11", + "tag": "@rushstack/npm-check-fork_v0.1.11", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "0.1.10", + "tag": "@rushstack/npm-check-fork_v0.1.10", + "date": "Wed, 28 Jan 2026 01:15:23 GMT", + "comments": { + "patch": [ + { + "comment": "Remove dependencies on throat and package-json" + } + ] + } + }, + { + "version": "0.1.9", + "tag": "@rushstack/npm-check-fork_v0.1.9", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "0.1.8", + "tag": "@rushstack/npm-check-fork_v0.1.8", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "0.1.7", + "tag": "@rushstack/npm-check-fork_v0.1.7", + "date": "Mon, 05 Jan 2026 16:12:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "0.1.6", + "tag": "@rushstack/npm-check-fork_v0.1.6", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + } + ] + } + }, + { + "version": "0.1.5", + "tag": "@rushstack/npm-check-fork_v0.1.5", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + } + ] + } + }, + { + "version": "0.1.4", + "tag": "@rushstack/npm-check-fork_v0.1.4", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + } + ] + } + }, + { + "version": "0.1.3", + "tag": "@rushstack/npm-check-fork_v0.1.3", + "date": "Tue, 04 Nov 2025 08:15:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/npm-check-fork_v0.1.2", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/npm-check-fork_v0.1.1", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/npm-check-fork_v0.1.0", + "date": "Sat, 18 Oct 2025 00:06:19 GMT", + "comments": { + "minor": [ + { + "comment": "Initial fork of npm-check" + } + ] + } + } + ] +} diff --git a/libraries/npm-check-fork/CHANGELOG.md b/libraries/npm-check-fork/CHANGELOG.md new file mode 100644 index 00000000000..9c57277dd5b --- /dev/null +++ b/libraries/npm-check-fork/CHANGELOG.md @@ -0,0 +1,212 @@ +# Change Log - @rushstack/npm-check-fork + +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 0.2.22 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 0.2.21 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 0.2.20 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.2.19 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.2.18 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 0.2.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.2.16 +Mon, 20 Apr 2026 15:15:25 GMT + +_Version update only_ + +## 0.2.15 +Sat, 18 Apr 2026 03:47:09 GMT + +### Patches + +- Remove giturl dependency; replace with original toHttpsUrl() implementation using the native URL class. + +## 0.2.14 +Sat, 18 Apr 2026 00:15:16 GMT + +### Patches + +- Bump semver. + +## 0.2.13 +Fri, 17 Apr 2026 15:14:57 GMT + +### Patches + +- Remove dependecy on `lodash`. + +## 0.2.12 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 0.2.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.2.10 +Sat, 04 Apr 2026 00:14:00 GMT + +### Patches + +- Bump lodash 4.18.1 to address CVEs GHSA-r5fr-rjxr-66jc, GHSA-f23m-r3pf-42rh + +## 0.2.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 0.2.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.2.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 0.2.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.2.5 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 0.2.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.2.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 0.2.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 0.2.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.2.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.1.14 +Sat, 07 Feb 2026 01:13:26 GMT + +### Patches + +- Upgrade `lodash` dependency from `~4.17.15` to `~4.17.23`. + +## 0.1.13 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 0.1.12 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 0.1.11 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 0.1.10 +Wed, 28 Jan 2026 01:15:23 GMT + +### Patches + +- Remove dependencies on throat and package-json + +## 0.1.9 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 0.1.8 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 0.1.7 +Mon, 05 Jan 2026 16:12:50 GMT + +_Version update only_ + +## 0.1.6 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 0.1.5 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 0.1.4 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 0.1.3 +Tue, 04 Nov 2025 08:15:15 GMT + +_Version update only_ + +## 0.1.2 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 0.1.1 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 0.1.0 +Sat, 18 Oct 2025 00:06:19 GMT + +### Minor changes + +- Initial fork of npm-check + diff --git a/libraries/npm-check-fork/LICENSE b/libraries/npm-check-fork/LICENSE new file mode 100644 index 00000000000..b7b33fb69c7 --- /dev/null +++ b/libraries/npm-check-fork/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Dylan Greene + +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. \ No newline at end of file diff --git a/libraries/npm-check-fork/README.md b/libraries/npm-check-fork/README.md new file mode 100644 index 00000000000..c4a6296956d --- /dev/null +++ b/libraries/npm-check-fork/README.md @@ -0,0 +1,31 @@ +# @rushstack/npm-check-fork + +This package is a temporary rushstack maintained fork of [`npm-check`](https://github.com/dylang/npm-check), used internally by `rush upgrade-interactive`. It exists to address security vulnerabilities and compatibility issues present in the latest upstream version. + +**Origin:** +- Forked from [`npm-check`](https://github.com/dylang/npm-check) +- Original copyright: + ``` + Copyright (c) 2015 Dylan Greene + Licensed under the MIT license. + ``` + +**Purpose:** +This fork is expected to be temporary and will be removed once upstream issues are resolved. + +## Changes from Upstream + +- **Removed unused state properties:** + Properties from the state object that were never set or used have been removed (see `INpmCheckState`). +- **Removed `peerDependencies` from `INpmCheckPackageSummary`:** + This property was deprecated in `npm-check` and was never set. +- **Removed emoji support:** + Emoji output was never used in rushstack/rush-lib and has been stripped out. +- **Downgraded `path-exists` dependency:** + The latest version of `path-exists` is ESM-only; this fork uses a compatible CommonJS version. +- **Removed `semverDiff` dependency:** + This was deprecated and its functionality has been replaced by direct usage of `semver`. + +## License + +This fork retains the original MIT license from `npm-check`. diff --git a/libraries/npm-check-fork/config/rig.json b/libraries/npm-check-fork/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/libraries/npm-check-fork/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/libraries/npm-check-fork/eslint.config.js b/libraries/npm-check-fork/eslint.config.js new file mode 100644 index 00000000000..9175fbaa4cd --- /dev/null +++ b/libraries/npm-check-fork/eslint.config.js @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + }, + rules: { + // This package is a fork, so it carries the original copyright. + 'headers/header-format': 'off' + } + } +]; diff --git a/libraries/npm-check-fork/package.json b/libraries/npm-check-fork/package.json new file mode 100644 index 00000000000..92d73ba1e53 --- /dev/null +++ b/libraries/npm-check-fork/package.json @@ -0,0 +1,54 @@ +{ + "name": "@rushstack/npm-check-fork", + "version": "0.2.22", + "description": "A fork of npm-check.", + "repository": { + "type": "git", + "url": "https://github.com/microsoft/rushstack.git", + "directory": "libraries/npm-check-fork" + }, + "homepage": "https://github.com/dylang/npm-check", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./lib-dts/index.d.ts", + "exports": { + ".": { + "types": "./lib-dts/index.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "license": "MIT", + "scripts": { + "build": "heft build --clean", + "test": "heft test --clean", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "dependencies": { + "semver": "~7.7.4", + "@rushstack/node-core-library": "workspace:*" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "@types/semver": "7.7.1", + "local-node-rig": "workspace:*", + "eslint": "~9.37.0" + }, + "sideEffects": false +} diff --git a/libraries/npm-check-fork/src/BestGuessHomepage.ts b/libraries/npm-check-fork/src/BestGuessHomepage.ts new file mode 100644 index 00000000000..57bbe4a0d5a --- /dev/null +++ b/libraries/npm-check-fork/src/BestGuessHomepage.ts @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { INpmCheckPackageVersion, INpmCheckRegistryData } from './interfaces/INpmCheckRegistry'; +import { toHttpsUrl } from './toHttpsUrl'; + +export default function bestGuessHomepage(data: INpmCheckRegistryData | undefined): string | false { + if (!data) { + return false; + } + const packageDataForLatest: INpmCheckPackageVersion = data.versions[data['dist-tags'].latest]; + + return packageDataForLatest + ? packageDataForLatest.homepage || + (packageDataForLatest.bugs && + packageDataForLatest.bugs.url && + toHttpsUrl(packageDataForLatest.bugs.url.trim())) || + (packageDataForLatest.repository && + packageDataForLatest.repository.url && + toHttpsUrl(packageDataForLatest.repository.url.trim())) || + false + : false; +} diff --git a/libraries/npm-check-fork/src/CreatePackageSummary.ts b/libraries/npm-check-fork/src/CreatePackageSummary.ts new file mode 100644 index 00000000000..f539e2ba0dc --- /dev/null +++ b/libraries/npm-check-fork/src/CreatePackageSummary.ts @@ -0,0 +1,91 @@ +import { existsSync } from 'node:fs'; +import path from 'node:path'; + +import semver, { type ReleaseType } from 'semver'; + +import type { INpmCheckState, INpmCheckPackageJson } from './interfaces/INpmCheck.ts'; +import type { INpmCheckPackageSummary, INpmCheckVersionBumpType } from './interfaces/INpmCheckPackageSummary'; +import type { INpmRegistryInfo } from './interfaces/INpmCheckRegistry'; +import findModulePath from './FindModulePath'; +import getLatestFromRegistry from './GetLatestFromRegistry'; +import readPackageJson from './ReadPackageJson'; + +export default async function createPackageSummary( + moduleName: string, + state: INpmCheckState +): Promise { + const cwdPackageJson: INpmCheckPackageJson | undefined = state.cwdPackageJson; + + const modulePath: string = findModulePath(moduleName, state); + const packageIsInstalled: boolean = existsSync(modulePath); + const modulePackageJson: INpmCheckPackageJson = readPackageJson(path.join(modulePath, 'package.json')); + + // Ignore private packages + const isPrivate: boolean = Boolean(modulePackageJson.private); + if (isPrivate) { + return false; + } + + // Ignore packages that are using github or file urls + const packageJsonVersion: string | undefined = + cwdPackageJson?.dependencies[moduleName] || cwdPackageJson?.devDependencies[moduleName]; + if (packageJsonVersion && !semver.validRange(packageJsonVersion)) { + return false; + } + + return getLatestFromRegistry(moduleName).then((fromRegistry: INpmRegistryInfo) => { + const installedVersion: string | undefined = modulePackageJson.version; + const latest: string | undefined = + installedVersion && + fromRegistry.latest && + fromRegistry.next && + semver.gt(installedVersion, fromRegistry.latest) + ? fromRegistry.next + : fromRegistry.latest; + const versions: string[] = fromRegistry.versions || []; + let versionWanted: string | null = null; + if (packageJsonVersion) { + versionWanted = semver.maxSatisfying(versions, packageJsonVersion); + } + + const versionToUse: string | undefined | null = installedVersion || versionWanted; + let bump: INpmCheckVersionBumpType; + if (versionToUse && latest && semver.valid(latest) && semver.valid(versionToUse)) { + const diff: ReleaseType | null = semver.diff(versionToUse, latest); + if (diff) { + const usingNonSemver: boolean = semver.lt(latest, '1.0.0-pre'); + if (usingNonSemver) { + bump = 'nonSemver'; + } else { + bump = diff; + } + } + } + + return { + // info + moduleName: moduleName, + homepage: fromRegistry.homepage ?? '', + regError: new Error(fromRegistry.error), + pkgError: modulePackageJson.error, + + // versions + latest: latest ?? '', + installed: versionToUse === null ? '' : versionToUse, + notInstalled: !packageIsInstalled, + packageJson: packageJsonVersion ?? '', + + // meta + // TODO: Replace with Object.hasOwn() when the TypeScript target library is upgraded to es2022+ + devDependency: Object.prototype.hasOwnProperty.call(cwdPackageJson?.devDependencies, moduleName), + mismatch: + packageJsonVersion !== undefined && + versionToUse !== null && + semver.validRange(packageJsonVersion) && + semver.valid(versionToUse) + ? !semver.satisfies(versionToUse, packageJsonVersion) + : false, + bump: bump + }; + }); +} diff --git a/libraries/npm-check-fork/src/FindModulePath.ts b/libraries/npm-check-fork/src/FindModulePath.ts new file mode 100644 index 00000000000..80aec8044d2 --- /dev/null +++ b/libraries/npm-check-fork/src/FindModulePath.ts @@ -0,0 +1,24 @@ +import { existsSync } from 'node:fs'; +import Module from 'node:module'; +import path from 'node:path'; + +import type { INpmCheckState } from './interfaces/INpmCheck.ts'; + +/** + * Searches the directory hierarchy to return the path to the requested node module. + * If the module can't be found, returns the initial (deepest) tried path. + */ +export default function findModulePath(moduleName: string, currentState: INpmCheckState): string { + const cwd: string = currentState.cwd; + + // Module._nodeModulePaths does not include some places the node module resolver searches, such as + // the global prefix or other special directories. This is desirable because if a module is missing + // in the project directory we want to be sure to report it as missing. + // We can't use require.resolve because it fails if the module doesn't have an entry point. + // @ts-ignore + const nodeModulesPaths: string[] = Module._nodeModulePaths(cwd); + const possibleModulePaths: string[] = nodeModulesPaths.map((x) => path.join(x, moduleName)); + const modulePath: string | undefined = possibleModulePaths.find((p) => existsSync(p)); + // if no existing path was found, return the first tried path anyway + return modulePath || path.join(cwd, moduleName); +} diff --git a/libraries/npm-check-fork/src/GetLatestFromRegistry.ts b/libraries/npm-check-fork/src/GetLatestFromRegistry.ts new file mode 100644 index 00000000000..43b0725e825 --- /dev/null +++ b/libraries/npm-check-fork/src/GetLatestFromRegistry.ts @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import os from 'node:os'; + +import semver from 'semver'; + +import { Async } from '@rushstack/node-core-library'; + +import bestGuessHomepage from './BestGuessHomepage'; +import { NpmRegistryClient, type INpmRegistryClientResult } from './NpmRegistryClient'; +import type { + INpmRegistryInfo, + INpmCheckRegistryData, + INpmRegistryPackageResponse +} from './interfaces/INpmCheckRegistry'; + +// Module-level registry client instance (lazy initialized) +let _registryClient: NpmRegistryClient | undefined; + +/** + * Gets or creates the shared registry client instance. + */ +function getRegistryClient(): NpmRegistryClient { + if (!_registryClient) { + _registryClient = new NpmRegistryClient(); + } + return _registryClient; +} + +/** + * Fetches package information from the npm registry. + * + * @param packageName - The name of the package to fetch + * @returns A promise that resolves to the package registry info + */ +export default async function getNpmInfo(packageName: string): Promise { + const client: NpmRegistryClient = getRegistryClient(); + const result: INpmRegistryClientResult = await client.fetchPackageMetadataAsync(packageName); + + if (result.error) { + return { + error: `Registry error ${result.error}` + }; + } + + const rawData: INpmRegistryPackageResponse = result.data!; + const CRAZY_HIGH_SEMVER: string = '8000.0.0'; + const sortedVersions: string[] = Object.keys(rawData.versions) + .filter((version: string) => semver.gt(CRAZY_HIGH_SEMVER, version)) + .sort(semver.compare); + + const latest: string = rawData['dist-tags'].latest; + const next: string = rawData['dist-tags'].next; + const latestStableRelease: string | undefined = semver.satisfies(latest, '*') + ? latest + : semver.maxSatisfying(sortedVersions, '*') || ''; + + // Cast to INpmCheckRegistryData for bestGuessHomepage compatibility + // INpmRegistryPackageResponse is a superset of INpmCheckRegistryData + const registryData: INpmCheckRegistryData = rawData as unknown as INpmCheckRegistryData; + + return { + latest: latestStableRelease, + next: next, + versions: sortedVersions, + homepage: bestGuessHomepage(registryData) || '' + }; +} + +/** + * Fetches package information for multiple packages concurrently. + * + * @param packageNames - Array of package names to fetch + * @param concurrency - Maximum number of concurrent requests (defaults to CPU count) + * @returns A promise that resolves to a Map of package name to registry info + */ +export async function getNpmInfoBatch( + packageNames: string[], + concurrency: number = os.cpus().length +): Promise> { + const results: Map = new Map(); + + // TODO: Refactor createPackageSummary to use this batch function to reduce registry requests + await Async.forEachAsync( + packageNames, + async (packageName: string) => { + results.set(packageName, await getNpmInfo(packageName)); + }, + { concurrency } + ); + + return results; +} diff --git a/libraries/npm-check-fork/src/NpmCheck.ts b/libraries/npm-check-fork/src/NpmCheck.ts new file mode 100644 index 00000000000..2a833ba75de --- /dev/null +++ b/libraries/npm-check-fork/src/NpmCheck.ts @@ -0,0 +1,32 @@ +import type { INpmCheckPackageJson, INpmCheckState } from './interfaces/INpmCheck.ts'; +import type { INpmCheckPackageSummary } from './interfaces/INpmCheckPackageSummary'; +import createPackageSummary from './CreatePackageSummary'; +import initializeState from './NpmCheckState'; + +export default async function NpmCheck(initialOptions?: INpmCheckState): Promise { + const state: INpmCheckState = await initializeState(initialOptions); + const cwdPackageJson: INpmCheckPackageJson | undefined = state.cwdPackageJson; + const allDependencies: Record | undefined = getDependencies(cwdPackageJson); + + let packages: INpmCheckPackageSummary[] = []; + if (allDependencies) { + const packageSummaryPromises: Promise[] = Object.keys( + allDependencies + ).map((moduleName: string) => createPackageSummary(moduleName, state)); + packages = await Promise.all(packageSummaryPromises).then( + (results: (INpmCheckPackageSummary | false)[]) => { + return results.filter((pkg): pkg is INpmCheckPackageSummary => pkg !== false); + } + ); + } + + return { ...state, packages }; +} + +function getDependencies(pkg: INpmCheckPackageJson | undefined): Record | undefined { + if (!pkg) { + return undefined; + } + + return Object.assign(pkg.dependencies, pkg.devDependencies); +} diff --git a/libraries/npm-check-fork/src/NpmCheckState.ts b/libraries/npm-check-fork/src/NpmCheckState.ts new file mode 100644 index 00000000000..ebcbdb7de86 --- /dev/null +++ b/libraries/npm-check-fork/src/NpmCheckState.ts @@ -0,0 +1,25 @@ +import path from 'node:path'; + +import { + DefaultNpmCheckOptions, + type INpmCheckPackageJson, + type INpmCheckState +} from './interfaces/INpmCheck'; +import readPackageJson from './ReadPackageJson'; + +export default async function initializeState(initialOptions?: INpmCheckState): Promise { + const state: INpmCheckState = Object.assign(DefaultNpmCheckOptions, initialOptions ?? {}); + + if (state.cwd) { + const cwd: string = path.resolve(state.cwd); + const pkg: INpmCheckPackageJson = readPackageJson(path.join(cwd, 'package.json')); + state.cwdPackageJson = pkg; + state.cwd = cwd; + } + + if (state.cwdPackageJson?.error) { + return Promise.reject(state.cwdPackageJson.error); + } + + return Promise.resolve(state); +} diff --git a/libraries/npm-check-fork/src/NpmRegistryClient.ts b/libraries/npm-check-fork/src/NpmRegistryClient.ts new file mode 100644 index 00000000000..b8a000b2e1f --- /dev/null +++ b/libraries/npm-check-fork/src/NpmRegistryClient.ts @@ -0,0 +1,200 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as https from 'node:https'; +import * as http from 'node:http'; +import * as os from 'node:os'; +import * as process from 'node:process'; +import * as zlib from 'node:zlib'; + +import type { INpmRegistryPackageResponse } from './interfaces/INpmCheckRegistry'; + +/** + * Options for configuring the NpmRegistryClient. + * @public + */ +export interface INpmRegistryClientOptions { + /** + * The base URL of the npm registry. + * @defaultValue 'https://registry.npmjs.org' + */ + registryUrl?: string; + + /** + * The User-Agent header to send with requests. + * @defaultValue A string containing npm-check-fork version and platform info + */ + userAgent?: string; + + /** + * Request timeout in milliseconds. + * @defaultValue 30000 + */ + timeoutMs?: number; +} + +/** + * Result from fetching package metadata from the npm registry. + * @public + */ +export interface INpmRegistryClientResult { + /** + * The package metadata if the request was successful. + */ + data?: INpmRegistryPackageResponse; + + /** + * Error message if the request failed. + */ + error?: string; +} + +const DEFAULT_REGISTRY_URL: string = 'https://registry.npmjs.org'; +const DEFAULT_TIMEOUT_MS: number = 30000; + +/** + * A client for fetching package metadata from the npm registry. + * + * @remarks + * This client provides a simple interface for fetching package metadata + * without external dependencies like `package-json`. + * + * @public + */ +export class NpmRegistryClient { + private readonly _registryUrl: string; + private readonly _userAgent: string; + private readonly _timeoutMs: number; + + public constructor(options?: INpmRegistryClientOptions) { + // trim trailing slash if one was provided + this._registryUrl = (options?.registryUrl ?? DEFAULT_REGISTRY_URL).replace(/\/$/, ''); + this._userAgent = + options?.userAgent ?? `npm-check-fork node/${process.version} ${os.platform()} ${os.arch()}`; + this._timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS; + } + + /** + * Builds the URL for fetching package metadata. + * + * @remarks + * Handles scoped packages by URL-encoding the package name. + * For example, `@scope/name` becomes `@scope%2Fname`. + * + * @param packageName - The name of the package + * @returns The full URL for fetching the package metadata + */ + private _buildPackageUrl(packageName: string): string { + // Scoped packages need the slash encoded + // @scope/name -> @scope%2Fname + const encodedName: string = packageName.replace(/\//g, '%2F'); + return `${this._registryUrl}/${encodedName}`; + } + + /** + * Fetches package metadata from the npm registry. + * + * @param packageName - The name of the package to fetch + * @returns A promise that resolves to the result containing either data or an error + * + * @example + * ```ts + * const client = new NpmRegistryClient(); + * const result = await client.fetchPackageMetadataAsync('lodash'); + * if (result.error) { + * console.error(result.error); + * } else { + * console.log(result.data?.['dist-tags'].latest); + * } + * ``` + */ + public async fetchPackageMetadataAsync(packageName: string): Promise { + const url: string = this._buildPackageUrl(packageName); + + return new Promise((resolve) => { + const parsedUrl: URL = new URL(url); + const isHttps: boolean = parsedUrl.protocol === 'https:'; + const requestModule: typeof https | typeof http = isHttps ? https : http; + + const requestOptions: https.RequestOptions = { + hostname: parsedUrl.hostname, + port: parsedUrl.port || (isHttps ? 443 : 80), + path: parsedUrl.pathname + parsedUrl.search, + method: 'GET', + timeout: this._timeoutMs, + headers: { + Accept: 'application/json', + 'Accept-Encoding': 'gzip, deflate', + 'User-Agent': this._userAgent + } + }; + + // TODO: Extract WebClient from rush-lib so that we can use it here + // instead of this reimplementation of HTTP request logic. + const request: http.ClientRequest = requestModule.request( + requestOptions, + (response: http.IncomingMessage) => { + const chunks: Buffer[] = []; + + response.on('data', (chunk: Buffer) => { + chunks.push(chunk); + }); + + response.on('end', () => { + const statusCode: number = response.statusCode ?? 0; + + // Handle 404 - Package not found + if (statusCode === 404) { + resolve({ error: 'Package not found' }); + return; + } + + // Handle other HTTP errors + if (statusCode < 200 || statusCode >= 300) { + resolve({ error: `HTTP error ${statusCode}: ${response.statusMessage}` }); + return; + } + + try { + let buffer: Buffer = Buffer.concat(chunks); + + // Decompress if needed + const contentEncoding: string | undefined = response.headers['content-encoding']; + if (contentEncoding === 'gzip') { + buffer = zlib.gunzipSync(buffer); + } else if (contentEncoding === 'deflate') { + buffer = zlib.inflateSync(buffer); + } + + const data: INpmRegistryPackageResponse = JSON.parse(buffer.toString('utf8')); + + // Successfully retrieved and parsed data + resolve({ data }); + } catch (parseError) { + resolve({ + error: `Failed to parse response: ${ + parseError instanceof Error ? parseError.message : String(parseError) + }` + }); + } + }); + + response.on('error', (error: Error) => { + resolve({ error: `Response error: ${error.message}` }); + }); + } + ); + + request.on('error', (error: Error) => { + resolve({ error: `Network error: ${error.message}` }); + }); + + request.on('timeout', () => { + request.destroy(); + resolve({ error: `Request timed out after ${this._timeoutMs}ms` }); + }); + + request.end(); + }); + } +} diff --git a/libraries/npm-check-fork/src/ReadPackageJson.ts b/libraries/npm-check-fork/src/ReadPackageJson.ts new file mode 100644 index 00000000000..68363c579df --- /dev/null +++ b/libraries/npm-check-fork/src/ReadPackageJson.ts @@ -0,0 +1,16 @@ +import type { INpmCheckPackageJson } from './interfaces/INpmCheck.ts'; + +export default function readPackageJson(filename: string): INpmCheckPackageJson { + let pkg: INpmCheckPackageJson | undefined = undefined; + let error: Error | undefined = undefined; + try { + pkg = require(filename); + } catch (e: unknown) { + if (e && typeof e === 'object' && 'code' in e && e.code === 'MODULE_NOT_FOUND') { + error = new Error(`A package.json was not found at ${filename}`); + } else { + error = new Error(`A package.json was found at ${filename}, but it is not valid.`); + } + } + return { devDependencies: {}, dependencies: {}, error, ...pkg } as INpmCheckPackageJson; +} diff --git a/libraries/npm-check-fork/src/index.ts b/libraries/npm-check-fork/src/index.ts new file mode 100644 index 00000000000..df216bda88a --- /dev/null +++ b/libraries/npm-check-fork/src/index.ts @@ -0,0 +1,14 @@ +export { default as NpmCheck } from './NpmCheck'; +export type { INpmCheckPackageSummary } from './interfaces/INpmCheckPackageSummary'; +export type { INpmCheckState } from './interfaces/INpmCheck'; +export { + NpmRegistryClient, + type INpmRegistryClientOptions, + type INpmRegistryClientResult +} from './NpmRegistryClient'; +export type { + INpmRegistryInfo, + INpmRegistryPackageResponse, + INpmRegistryVersionMetadata +} from './interfaces/INpmCheckRegistry'; +export { getNpmInfoBatch } from './GetLatestFromRegistry'; diff --git a/libraries/npm-check-fork/src/interfaces/INpmCheck.ts b/libraries/npm-check-fork/src/interfaces/INpmCheck.ts new file mode 100644 index 00000000000..77f2d2172f7 --- /dev/null +++ b/libraries/npm-check-fork/src/interfaces/INpmCheck.ts @@ -0,0 +1,24 @@ +import type { INpmCheckPackageSummary } from './INpmCheckPackageSummary'; + +export interface INpmCheckPackageJson { + name?: string; + version?: string; + devDependencies: Record; + dependencies: Record; + error?: Error; + scripts?: Record; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any; +} + +export interface INpmCheckState { + cwd: string; + cwdPackageJson?: INpmCheckPackageJson; + packages?: INpmCheckPackageSummary[]; +} + +export const DefaultNpmCheckOptions: INpmCheckState = { + cwd: process.cwd(), + cwdPackageJson: { devDependencies: {}, dependencies: {} }, + packages: undefined +}; diff --git a/libraries/npm-check-fork/src/interfaces/INpmCheckPackageSummary.ts b/libraries/npm-check-fork/src/interfaces/INpmCheckPackageSummary.ts new file mode 100644 index 00000000000..2184a41c227 --- /dev/null +++ b/libraries/npm-check-fork/src/interfaces/INpmCheckPackageSummary.ts @@ -0,0 +1,29 @@ +export type INpmCheckVersionBumpType = + | '' + | 'build' + | 'major' + | 'premajor' + | 'minor' + | 'preminor' + | 'patch' + | 'prepatch' + | 'prerelease' + | 'release' + | 'nonSemver' + | undefined + // eslint-disable-next-line @rushstack/no-new-null + | null; + +export interface INpmCheckPackageSummary { + moduleName: string; // name of the module. + homepage: string; // url to the home page. + regError?: Error; // error communicating with the registry + pkgError?: Error; // error reading the package.json + latest: string; // latest according to the registry. + installed: string; // version installed + notInstalled: boolean; // Is it installed? + packageJson: string; // Version or range requested in the parent package.json. + devDependency: boolean; // Is this a devDependency? + mismatch: boolean; // Does the version installed not match the range in package.json? + bump?: INpmCheckVersionBumpType; // What kind of bump is required to get the latest +} diff --git a/libraries/npm-check-fork/src/interfaces/INpmCheckRegistry.ts b/libraries/npm-check-fork/src/interfaces/INpmCheckRegistry.ts new file mode 100644 index 00000000000..67c5e7a9639 --- /dev/null +++ b/libraries/npm-check-fork/src/interfaces/INpmCheckRegistry.ts @@ -0,0 +1,67 @@ +/** + * The result returned by getNpmInfo for a single package. + */ +export interface INpmRegistryInfo { + latest?: string; + next?: string; + versions?: string[]; + homepage?: string; + error?: string; +} + +interface INpmCheckRegistryInfoBugs { + url?: string; +} +interface INpmCheckRepository { + url?: string; +} +export interface INpmCheckPackageVersion { + homepage?: string; + bugs?: INpmCheckRegistryInfoBugs; + repository?: INpmCheckRepository; +} +export interface INpmCheckRegistryData { + versions: Record; + ['dist-tags']: { latest: string }; +} + +/** + * Metadata for a specific package version from the npm registry. + * + * @remarks + * This interface extends the existing INpmCheckPackageVersion with additional + * fields that are present in the npm registry response. + * + * @see https://github.com/npm/registry/blob/main/docs/responses/package-metadata.md + */ +export interface INpmRegistryVersionMetadata extends INpmCheckPackageVersion { + /** Package name */ + name: string; + + /** Version string */ + version: string; +} + +/** + * Response structure from npm registry API for full metadata. + * + * @remarks + * This interface represents the full response from the npm registry when + * fetching package metadata. It is structurally compatible with INpmCheckRegistryData + * to maintain compatibility with existing code like bestGuessHomepage. + * + * @see https://github.com/npm/registry/blob/main/docs/responses/package-metadata.md + */ +export interface INpmRegistryPackageResponse { + /** Package name */ + name: string; + + /** Distribution tags (latest, next, etc.) */ + 'dist-tags': Record; + + /** All published versions with their metadata */ + versions: Record; + + /** Modification timestamps for each version */ + time?: Record; +} diff --git a/libraries/npm-check-fork/src/tests/BestGuessHomepage.test.ts b/libraries/npm-check-fork/src/tests/BestGuessHomepage.test.ts new file mode 100644 index 00000000000..57242e394b1 --- /dev/null +++ b/libraries/npm-check-fork/src/tests/BestGuessHomepage.test.ts @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import bestGuessHomepage from '../BestGuessHomepage'; +import type { INpmCheckRegistryData } from '../interfaces/INpmCheckRegistry'; + +describe('bestGuessHomepage', () => { + it('returns false if data is undefined', () => { + expect(bestGuessHomepage(undefined)).toBe(false); + }); + + it('returns homepage if present', () => { + const data: INpmCheckRegistryData = { + versions: { + latest: { + homepage: 'https://homepage.com' + } + }, + 'dist-tags': { latest: 'latest' } + }; + expect(bestGuessHomepage(data)).toBe('https://homepage.com'); + }); + + it('returns bugs.url if homepage is missing', () => { + const data: INpmCheckRegistryData = { + versions: { + latest: { + bugs: { url: 'https://bugs.com/issues' } + } + }, + 'dist-tags': { latest: 'latest' } + }; + expect(bestGuessHomepage(data)).toBe('https://bugs.com/issues'); + }); + + it('returns repository.url if homepage and bugs.url are missing', () => { + const data: INpmCheckRegistryData = { + versions: { + latest: { + repository: { url: 'https://repo.com/user/proj' } + } + }, + 'dist-tags': { latest: 'latest' } + }; + expect(bestGuessHomepage(data)).toBe('https://repo.com/user/proj'); + }); + + it('converts git@ SCP-style repository URL to https', () => { + const data: INpmCheckRegistryData = { + versions: { + latest: { + repository: { url: 'git@github.com:user/repo.git' } + } + }, + 'dist-tags': { latest: 'latest' } + }; + expect(bestGuessHomepage(data)).toBe('https://github.com/user/repo'); + }); + + it('converts git:// repository URL to https', () => { + const data: INpmCheckRegistryData = { + versions: { + latest: { + repository: { url: 'git://github.com/user/repo.git' } + } + }, + 'dist-tags': { latest: 'latest' } + }; + expect(bestGuessHomepage(data)).toBe('https://github.com/user/repo'); + }); + + it('converts git+https:// repository URL to https', () => { + const data: INpmCheckRegistryData = { + versions: { + latest: { + repository: { url: 'git+https://github.com/user/repo.git' } + } + }, + 'dist-tags': { latest: 'latest' } + }; + expect(bestGuessHomepage(data)).toBe('https://github.com/user/repo'); + }); + + it('returns false if no homepage, bugs.url, or repository.url', () => { + const data: INpmCheckRegistryData = { + versions: { + latest: {} + }, + 'dist-tags': { latest: 'latest' } + }; + expect(bestGuessHomepage(data)).toBe(false); + }); +}); diff --git a/libraries/npm-check-fork/src/tests/CreatePackageSummary.test.ts b/libraries/npm-check-fork/src/tests/CreatePackageSummary.test.ts new file mode 100644 index 00000000000..9dce1cc11e1 --- /dev/null +++ b/libraries/npm-check-fork/src/tests/CreatePackageSummary.test.ts @@ -0,0 +1,79 @@ +jest.mock('../GetLatestFromRegistry'); +jest.mock('../ReadPackageJson'); +jest.mock('../FindModulePath'); + +import createPackageSummary from '../CreatePackageSummary'; +import getLatestFromRegistry from '../GetLatestFromRegistry'; +import readPackageJson from '../ReadPackageJson'; +import findModulePath from '../FindModulePath'; +import type { INpmCheckState, INpmCheckPackageJson } from '../interfaces/INpmCheck'; +import type { INpmRegistryInfo } from '../interfaces/INpmCheckRegistry'; +import type { INpmCheckPackageSummary } from '../interfaces/INpmCheckPackageSummary'; + +const mockGetLatestFromRegistry = getLatestFromRegistry as jest.MockedFunction; +const mockReadPackageJson = readPackageJson as jest.MockedFunction; +const mockFindModulePath = findModulePath as jest.MockedFunction; + +describe('createPackageSummary', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('returns false for private package', async () => { + mockFindModulePath.mockReturnValue('/mock/path/private-pkg'); + mockReadPackageJson.mockReturnValue({ + dependencies: {}, + devDependencies: {}, + private: true + } as INpmCheckPackageJson); + const state: INpmCheckState = { + cwd: process.cwd(), + cwdPackageJson: { dependencies: {}, devDependencies: {} } + }; + const result: INpmCheckPackageSummary | boolean = await createPackageSummary('private-pkg', state); + expect(result).toBe(false); + }); + + it('returns false for invalid semver range', async () => { + mockFindModulePath.mockReturnValue('/mock/path'); + mockReadPackageJson.mockReturnValue({ + dependencies: {}, + devDependencies: {} + } as INpmCheckPackageJson); + const state: INpmCheckState = { + cwd: process.cwd(), + cwdPackageJson: { + dependencies: { 'bad-pkg': 'github:foo/bar' }, + devDependencies: {} + } + }; + const result: INpmCheckPackageSummary | boolean = await createPackageSummary('bad-pkg', state); + expect(result).toBe(false); + }); + + it('returns summary for valid package', async () => { + mockFindModulePath.mockReturnValue('/mock/path'); + mockReadPackageJson.mockReturnValue({ + dependencies: {}, + devDependencies: {} + } as INpmCheckPackageJson); + mockGetLatestFromRegistry.mockResolvedValue({ + latest: '2.0.0', + next: '3.0.0', + versions: ['1.0.0', '2.0.0', '3.0.0'], + homepage: 'https://homepage.com' + } as INpmRegistryInfo); + const state: INpmCheckState = { + cwd: process.cwd(), + cwdPackageJson: { dependencies: { 'good-pkg': '1.0.0' }, devDependencies: {} }, + unusedDependencies: ['good-pkg'], + missingFromPackageJson: {} + } as INpmCheckState; + const result: INpmCheckPackageSummary | boolean = await createPackageSummary('good-pkg', state); + expect(result).toBeTruthy(); + expect(result).toHaveProperty('moduleName', 'good-pkg'); + expect(result).toHaveProperty('homepage', 'https://homepage.com'); + expect(result).toHaveProperty('latest', '2.0.0'); + expect(result).toHaveProperty('installed', '1.0.0'); + }); +}); diff --git a/libraries/npm-check-fork/src/tests/FindModulePath.test.ts b/libraries/npm-check-fork/src/tests/FindModulePath.test.ts new file mode 100644 index 00000000000..bf9ddeca78a --- /dev/null +++ b/libraries/npm-check-fork/src/tests/FindModulePath.test.ts @@ -0,0 +1,34 @@ +jest.mock('path', () => ({ + join: jest.fn((...args) => args.join('/')) + // Add other path methods as needed +})); + +import findModulePath from '../FindModulePath'; +import type { INpmCheckState } from '../interfaces/INpmCheck'; +import path from 'node:path'; + +const Module = require('node:module'); + +describe('findModulePath', () => { + beforeAll(() => { + jest + .spyOn(Module, '_nodeModulePaths') + .mockImplementation(() => ['/mock/path/node_modules', '/another/mock/path/node_modules']); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + it('returns found path', () => { + const state: INpmCheckState = { cwd: '/test/cwd', global: false } as INpmCheckState; + const result = findModulePath('my-module', state); + expect(result).toBe(path.join('/test/cwd', 'my-module')); + }); + + it('returns first tried path', () => { + const state: INpmCheckState = { cwd: '/test/cwd', global: false } as INpmCheckState; + const result = findModulePath('missing-module', state); + expect(result).toBe(path.join('/test/cwd', 'missing-module')); + }); +}); diff --git a/libraries/npm-check-fork/src/tests/GetLatestFromRegistry.test.ts b/libraries/npm-check-fork/src/tests/GetLatestFromRegistry.test.ts new file mode 100644 index 00000000000..17aa23581ec --- /dev/null +++ b/libraries/npm-check-fork/src/tests/GetLatestFromRegistry.test.ts @@ -0,0 +1,109 @@ +// Mock the NpmRegistryClient before imports +jest.mock('../NpmRegistryClient'); + +import type { INpmRegistryInfo, INpmRegistryPackageResponse } from '../interfaces/INpmCheckRegistry'; + +describe('getNpmInfo', () => { + let getNpmInfo: (packageName: string) => Promise; + let mockFetchPackageMetadataAsync: jest.Mock; + + beforeEach(() => { + jest.resetModules(); + jest.clearAllMocks(); + + // Re-require to get fresh module instances + mockFetchPackageMetadataAsync = jest.fn(); + + // Set up the mock implementation before importing getNpmInfo + const mockNpmRegistryClient = jest.requireMock('../NpmRegistryClient'); + mockNpmRegistryClient.NpmRegistryClient.mockImplementation(() => ({ + fetchPackageMetadataAsync: mockFetchPackageMetadataAsync + })); + + // Import the module under test + const module = jest.requireActual('../GetLatestFromRegistry'); + getNpmInfo = module.default; + }); + + it('returns registry info with homepage', async () => { + const mockData: INpmRegistryPackageResponse = { + name: 'test-package', + versions: { + '1.0.0': { + name: 'test-package', + version: '1.0.0', + homepage: 'https://homepage.com' + }, + '2.0.0': { + name: 'test-package', + version: '2.0.0', + bugs: { url: 'https://bugs.com' } + } + }, + 'dist-tags': { latest: '1.0.0', next: '2.0.0' } + }; + mockFetchPackageMetadataAsync.mockResolvedValue({ data: mockData }); + + const result: INpmRegistryInfo = await getNpmInfo('test-package'); + expect(result).toHaveProperty('latest', '1.0.0'); + expect(result).toHaveProperty('next', '2.0.0'); + expect(result).toHaveProperty('versions', ['1.0.0', '2.0.0']); + expect(result).toHaveProperty('homepage', 'https://homepage.com'); + }); + + it('returns error if fetch fails', async () => { + mockFetchPackageMetadataAsync.mockResolvedValue({ error: 'Registry down' }); + + const result: INpmRegistryInfo = await getNpmInfo('test-package'); + expect(result).toHaveProperty('error'); + expect(result.error).toBe('Registry error Registry down'); + }); + + it('returns "" homepage if not present', async () => { + const mockData: INpmRegistryPackageResponse = { + name: 'test-package', + versions: { + '1.0.0': { + name: 'test-package', + version: '1.0.0' + }, + '2.0.0': { + name: 'test-package', + version: '2.0.0' + } + }, + 'dist-tags': { latest: '1.0.0', next: '2.0.0' } + }; + mockFetchPackageMetadataAsync.mockResolvedValue({ data: mockData }); + + const result: INpmRegistryInfo = await getNpmInfo('test-package'); + expect(result).toHaveProperty('homepage', ''); + }); + + it('filters out versions exceeding CRAZY_HIGH_SEMVER threshold', async () => { + const mockData: INpmRegistryPackageResponse = { + name: 'test-package', + versions: { + '1.0.0': { + name: 'test-package', + version: '1.0.0' + }, + '2.0.0': { + name: 'test-package', + version: '2.0.0' + }, + '9000.0.0': { + name: 'test-package', + version: '9000.0.0' + } + }, + 'dist-tags': { latest: '2.0.0', next: '2.0.0' } + }; + mockFetchPackageMetadataAsync.mockResolvedValue({ data: mockData }); + + const result: INpmRegistryInfo = await getNpmInfo('test-package'); + // Versions exceeding 8000.0.0 should be filtered out + expect(result.versions).toEqual(['1.0.0', '2.0.0']); + expect(result.versions).not.toContain('9000.0.0'); + }); +}); diff --git a/libraries/npm-check-fork/src/tests/NpmCheck.test.ts b/libraries/npm-check-fork/src/tests/NpmCheck.test.ts new file mode 100644 index 00000000000..6fea389f076 --- /dev/null +++ b/libraries/npm-check-fork/src/tests/NpmCheck.test.ts @@ -0,0 +1,36 @@ +jest.mock('../CreatePackageSummary', () => ({ + __esModule: true, + default: jest.fn(async () => ({})) +})); + +import createPackageSummary from '../CreatePackageSummary'; +const mockCreatePackageSummary = createPackageSummary as jest.MockedFunction; + +import type { INpmCheckState } from '../interfaces/INpmCheck'; +import NpmCheck from '../NpmCheck'; + +describe('NpmCheck', () => { + it('should mimic rush initial options', async () => { + mockCreatePackageSummary.mockImplementation(async (moduleName) => ({ + moduleName, + homepage: '', + latest: '', + installed: '', + notInstalled: true, + packageWanted: '', + packageJson: '', + notInPackageJson: undefined, + devDependency: false, + peerDependency: false, + mismatch: false, + bump: undefined + })); + const result: INpmCheckState = await NpmCheck({ + cwd: process.cwd() + }); + expect(result.packages).toBeDefined(); + if (result.packages && result.packages.length > 0) { + expect(result.packages[0]).toHaveProperty('moduleName'); + } + }); +}); diff --git a/libraries/npm-check-fork/src/tests/NpmCheckState.test.ts b/libraries/npm-check-fork/src/tests/NpmCheckState.test.ts new file mode 100644 index 00000000000..68ad0d0ff3f --- /dev/null +++ b/libraries/npm-check-fork/src/tests/NpmCheckState.test.ts @@ -0,0 +1,12 @@ +import type { INpmCheckState } from '../interfaces/INpmCheck'; +import initializeState from '../NpmCheckState'; + +describe('NpmCheckState', () => { + it('should create with default options', async () => { + const state: INpmCheckState = await initializeState(); + expect(state).toBeDefined(); + expect(state.cwd).toBe(process.cwd()); + expect(state.cwdPackageJson).toHaveProperty('name'); + expect(state.cwdPackageJson).toHaveProperty('version'); + }); +}); diff --git a/libraries/npm-check-fork/src/tests/NpmRegistryClient.test.ts b/libraries/npm-check-fork/src/tests/NpmRegistryClient.test.ts new file mode 100644 index 00000000000..00ab716f72f --- /dev/null +++ b/libraries/npm-check-fork/src/tests/NpmRegistryClient.test.ts @@ -0,0 +1,455 @@ +// Mock modules +jest.mock('node:https'); +jest.mock('node:http'); + +import type * as http from 'node:http'; +import type * as https from 'node:https'; +import { EventEmitter } from 'node:events'; +import * as zlib from 'node:zlib'; + +import { NpmRegistryClient, type INpmRegistryClientOptions } from '../NpmRegistryClient'; +import type { INpmRegistryPackageResponse } from '../interfaces/INpmCheckRegistry'; + +describe('NpmRegistryClient', () => { + let mockHttpsRequest: jest.Mock; + let mockHttpRequest: jest.Mock; + + beforeEach(() => { + jest.clearAllMocks(); + + // Get the mocked modules + const httpsModule = jest.requireMock('node:https'); + const httpModule = jest.requireMock('node:http'); + + mockHttpsRequest = httpsModule.request = jest.fn(); + mockHttpRequest = httpModule.request = jest.fn(); + }); + + describe('constructor', () => { + it('uses default registry URL when not provided', () => { + const client = new NpmRegistryClient(); + expect(client).toBeDefined(); + }); + + it('accepts custom options', () => { + const options: INpmRegistryClientOptions = { + registryUrl: 'https://custom.registry.com', + userAgent: 'custom-agent', + timeoutMs: 10000 + }; + const client = new NpmRegistryClient(options); + expect(client).toBeDefined(); + }); + + it('removes trailing slash from registry URL', () => { + const options: INpmRegistryClientOptions = { + registryUrl: 'https://registry.example.com/' + }; + const client = new NpmRegistryClient(options); + expect(client).toBeDefined(); + }); + }); + + describe('fetchPackageMetadataAsync', () => { + interface IMockRequest extends EventEmitter { + destroy: jest.Mock; + end: jest.Mock; + } + + interface IMockResponse extends EventEmitter { + statusCode?: number; + statusMessage?: string; + headers: Record; + } + + function createMockRequest(): { + request: IMockRequest; + response: IMockResponse; + } { + const request = new EventEmitter() as IMockRequest; + const response = new EventEmitter() as IMockResponse; + + request.destroy = jest.fn(); + request.end = jest.fn(); + response.headers = {}; + + return { request, response }; + } + + it('successfully fetches package metadata with https', async () => { + const client = new NpmRegistryClient(); + const { request, response } = createMockRequest(); + + const mockData: INpmRegistryPackageResponse = { + name: 'test-package', + versions: { + '1.0.0': { + name: 'test-package', + version: '1.0.0' + } + }, + 'dist-tags': { latest: '1.0.0' } + }; + + mockHttpsRequest.mockImplementation( + (options: https.RequestOptions, callback: (res: http.IncomingMessage) => void) => { + // Verify request options + expect(options.hostname).toBe('registry.npmjs.org'); + expect(options.method).toBe('GET'); + expect(options.headers).toMatchObject({ + Accept: 'application/json', + 'Accept-Encoding': 'gzip, deflate', + 'User-Agent': expect.stringContaining('npm-check-fork') + }); + + // Trigger callback with response + setImmediate(() => callback(response as http.IncomingMessage)); + return request; + } + ); + + const fetchPromise = client.fetchPackageMetadataAsync('test-package'); + + // Simulate response + response.statusCode = 200; + response.statusMessage = 'OK'; + setImmediate(() => { + response.emit('data', Buffer.from(JSON.stringify(mockData))); + response.emit('end'); + }); + + const result = await fetchPromise; + expect(result.data).toEqual(mockData); + expect(result.error).toBeUndefined(); + }); + + it('builds correct URL for scoped packages', async () => { + const client = new NpmRegistryClient(); + const { request, response } = createMockRequest(); + + mockHttpsRequest.mockImplementation( + (options: https.RequestOptions, callback: (res: http.IncomingMessage) => void) => { + // Verify that scoped package name is URL-encoded + expect(options.path).toBe('/@scope%2Fpackage-name'); + + setImmediate(() => callback(response as http.IncomingMessage)); + return request; + } + ); + + const fetchPromise = client.fetchPackageMetadataAsync('@scope/package-name'); + + response.statusCode = 200; + setImmediate(() => { + response.emit( + 'data', + Buffer.from(JSON.stringify({ name: '@scope/package-name', versions: {}, 'dist-tags': {} })) + ); + response.emit('end'); + }); + + await fetchPromise; + }); + + it('uses custom registry URL', async () => { + const client = new NpmRegistryClient({ registryUrl: 'https://custom.registry.com' }); + const { request, response } = createMockRequest(); + + mockHttpsRequest.mockImplementation( + (options: https.RequestOptions, callback: (res: http.IncomingMessage) => void) => { + expect(options.hostname).toBe('custom.registry.com'); + + setImmediate(() => callback(response as http.IncomingMessage)); + return request; + } + ); + + const fetchPromise = client.fetchPackageMetadataAsync('test-package'); + + response.statusCode = 200; + setImmediate(() => { + response.emit('data', Buffer.from(JSON.stringify({ name: 'test', versions: {}, 'dist-tags': {} }))); + response.emit('end'); + }); + + await fetchPromise; + }); + + it('uses http for http URLs', async () => { + const client = new NpmRegistryClient({ registryUrl: 'http://custom.registry.com' }); + const { request, response } = createMockRequest(); + + mockHttpRequest.mockImplementation( + (options: http.RequestOptions, callback: (res: http.IncomingMessage) => void) => { + expect(options.hostname).toBe('custom.registry.com'); + expect(options.port).toBe(80); + + setImmediate(() => callback(response as http.IncomingMessage)); + return request; + } + ); + + const fetchPromise = client.fetchPackageMetadataAsync('test-package'); + + response.statusCode = 200; + setImmediate(() => { + response.emit('data', Buffer.from(JSON.stringify({ name: 'test', versions: {}, 'dist-tags': {} }))); + response.emit('end'); + }); + + await fetchPromise; + expect(mockHttpRequest).toHaveBeenCalled(); + expect(mockHttpsRequest).not.toHaveBeenCalled(); + }); + + it('handles gzip-encoded responses', async () => { + const client = new NpmRegistryClient(); + const { request, response } = createMockRequest(); + + const mockData: INpmRegistryPackageResponse = { + name: 'test-package', + versions: {}, + 'dist-tags': { latest: '1.0.0' } + }; + + mockHttpsRequest.mockImplementation( + (options: https.RequestOptions, callback: (res: http.IncomingMessage) => void) => { + setImmediate(() => callback(response as http.IncomingMessage)); + return request; + } + ); + + const fetchPromise = client.fetchPackageMetadataAsync('test-package'); + + response.statusCode = 200; + response.headers['content-encoding'] = 'gzip'; + + setImmediate(() => { + const compressed = zlib.gzipSync(Buffer.from(JSON.stringify(mockData))); + response.emit('data', compressed); + response.emit('end'); + }); + + const result = await fetchPromise; + expect(result.data).toEqual(mockData); + expect(result.error).toBeUndefined(); + }); + + it('handles deflate-encoded responses', async () => { + const client = new NpmRegistryClient(); + const { request, response } = createMockRequest(); + + const mockData: INpmRegistryPackageResponse = { + name: 'test-package', + versions: {}, + 'dist-tags': { latest: '1.0.0' } + }; + + mockHttpsRequest.mockImplementation( + (options: https.RequestOptions, callback: (res: http.IncomingMessage) => void) => { + setImmediate(() => callback(response as http.IncomingMessage)); + return request; + } + ); + + const fetchPromise = client.fetchPackageMetadataAsync('test-package'); + + response.statusCode = 200; + response.headers['content-encoding'] = 'deflate'; + + setImmediate(() => { + const compressed = zlib.deflateSync(Buffer.from(JSON.stringify(mockData))); + response.emit('data', compressed); + response.emit('end'); + }); + + const result = await fetchPromise; + expect(result.data).toEqual(mockData); + expect(result.error).toBeUndefined(); + }); + + it('handles 404 status code', async () => { + const client = new NpmRegistryClient(); + const { request, response } = createMockRequest(); + + mockHttpsRequest.mockImplementation( + (options: https.RequestOptions, callback: (res: http.IncomingMessage) => void) => { + setImmediate(() => callback(response as http.IncomingMessage)); + return request; + } + ); + + const fetchPromise = client.fetchPackageMetadataAsync('nonexistent-package'); + + response.statusCode = 404; + response.statusMessage = 'Not Found'; + setImmediate(() => { + response.emit('data', Buffer.from('Not found')); + response.emit('end'); + }); + + const result = await fetchPromise; + expect(result.data).toBeUndefined(); + expect(result.error).toBe('Package not found'); + }); + + it('handles non-2xx status codes', async () => { + const client = new NpmRegistryClient(); + const { request, response } = createMockRequest(); + + mockHttpsRequest.mockImplementation( + (options: https.RequestOptions, callback: (res: http.IncomingMessage) => void) => { + setImmediate(() => callback(response as http.IncomingMessage)); + return request; + } + ); + + const fetchPromise = client.fetchPackageMetadataAsync('test-package'); + + response.statusCode = 500; + response.statusMessage = 'Internal Server Error'; + setImmediate(() => { + response.emit('data', Buffer.from('Error')); + response.emit('end'); + }); + + const result = await fetchPromise; + expect(result.data).toBeUndefined(); + expect(result.error).toBe('HTTP error 500: Internal Server Error'); + }); + + it('handles network errors', async () => { + const client = new NpmRegistryClient(); + const { request } = createMockRequest(); + + mockHttpsRequest.mockImplementation(() => { + return request; + }); + + const fetchPromise = client.fetchPackageMetadataAsync('test-package'); + + setImmediate(() => { + request.emit('error', new Error('Network connection failed')); + }); + + const result = await fetchPromise; + expect(result.data).toBeUndefined(); + expect(result.error).toBe('Network error: Network connection failed'); + }); + + it('handles response errors', async () => { + const client = new NpmRegistryClient(); + const { request, response } = createMockRequest(); + + mockHttpsRequest.mockImplementation( + (options: https.RequestOptions, callback: (res: http.IncomingMessage) => void) => { + setImmediate(() => callback(response as http.IncomingMessage)); + return request; + } + ); + + const fetchPromise = client.fetchPackageMetadataAsync('test-package'); + + response.statusCode = 200; + setImmediate(() => { + response.emit('error', new Error('Stream error')); + }); + + const result = await fetchPromise; + expect(result.data).toBeUndefined(); + expect(result.error).toBe('Response error: Stream error'); + }); + + it('handles timeout', async () => { + const client = new NpmRegistryClient({ timeoutMs: 1000 }); + const { request } = createMockRequest(); + + mockHttpsRequest.mockImplementation(() => { + return request; + }); + + const fetchPromise = client.fetchPackageMetadataAsync('test-package'); + + setImmediate(() => { + request.emit('timeout'); + }); + + const result = await fetchPromise; + expect(result.data).toBeUndefined(); + expect(result.error).toBe('Request timed out after 1000ms'); + expect(request.destroy).toHaveBeenCalled(); + }); + + it('handles JSON parse errors', async () => { + const client = new NpmRegistryClient(); + const { request, response } = createMockRequest(); + + mockHttpsRequest.mockImplementation( + (options: https.RequestOptions, callback: (res: http.IncomingMessage) => void) => { + setImmediate(() => callback(response as http.IncomingMessage)); + return request; + } + ); + + const fetchPromise = client.fetchPackageMetadataAsync('test-package'); + + response.statusCode = 200; + setImmediate(() => { + response.emit('data', Buffer.from('invalid json')); + response.emit('end'); + }); + + const result = await fetchPromise; + expect(result.data).toBeUndefined(); + expect(result.error).toContain('Failed to parse response'); + }); + + it('uses custom User-Agent header', async () => { + const client = new NpmRegistryClient({ userAgent: 'custom-agent/1.0' }); + const { request, response } = createMockRequest(); + + mockHttpsRequest.mockImplementation( + (options: https.RequestOptions, callback: (res: http.IncomingMessage) => void) => { + expect(options.headers?.['User-Agent']).toBe('custom-agent/1.0'); + + setImmediate(() => callback(response as http.IncomingMessage)); + return request; + } + ); + + const fetchPromise = client.fetchPackageMetadataAsync('test-package'); + + response.statusCode = 200; + setImmediate(() => { + response.emit('data', Buffer.from(JSON.stringify({ name: 'test', versions: {}, 'dist-tags': {} }))); + response.emit('end'); + }); + + await fetchPromise; + }); + + it('uses custom timeout value', async () => { + const client = new NpmRegistryClient({ timeoutMs: 5000 }); + const { request, response } = createMockRequest(); + + mockHttpsRequest.mockImplementation( + (options: https.RequestOptions, callback: (res: http.IncomingMessage) => void) => { + expect(options.timeout).toBe(5000); + + setImmediate(() => callback(response as http.IncomingMessage)); + return request; + } + ); + + const fetchPromise = client.fetchPackageMetadataAsync('test-package'); + + response.statusCode = 200; + setImmediate(() => { + response.emit('data', Buffer.from(JSON.stringify({ name: 'test', versions: {}, 'dist-tags': {} }))); + response.emit('end'); + }); + + await fetchPromise; + }); + }); +}); diff --git a/libraries/npm-check-fork/src/tests/ReadPackageJson.test.ts b/libraries/npm-check-fork/src/tests/ReadPackageJson.test.ts new file mode 100644 index 00000000000..28e75e35cdd --- /dev/null +++ b/libraries/npm-check-fork/src/tests/ReadPackageJson.test.ts @@ -0,0 +1,14 @@ +import path from 'node:path'; + +import readPackageJson from '../ReadPackageJson'; +import type { INpmCheckPackageJson } from '../interfaces/INpmCheck'; + +describe('readPackageJson', () => { + it('should return valid packageJson if it exists', async () => { + const fileName: string = path.join(process.cwd(), 'package.json'); + const result: INpmCheckPackageJson = await readPackageJson(fileName); + + expect(result).toBeDefined(); + expect(result).toHaveProperty('name'); + }); +}); diff --git a/libraries/npm-check-fork/src/tests/toHttpsUrl.test.ts b/libraries/npm-check-fork/src/tests/toHttpsUrl.test.ts new file mode 100644 index 00000000000..40d1ec386cd --- /dev/null +++ b/libraries/npm-check-fork/src/tests/toHttpsUrl.test.ts @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { toHttpsUrl } from '../toHttpsUrl'; + +describe(toHttpsUrl.name, () => { + it('returns empty string for empty input', () => { + expect(toHttpsUrl('')).toBe(''); + }); + + it('passes through an already-valid https URL unchanged', () => { + expect(toHttpsUrl('https://github.com/user/repo')).toBe('https://github.com/user/repo'); + }); + + it('strips .git suffix from an https URL', () => { + expect(toHttpsUrl('https://github.com/user/repo.git')).toBe('https://github.com/user/repo'); + }); + + it('converts SCP-style git@ URL to https', () => { + expect(toHttpsUrl('git@github.com:user/repo.git')).toBe('https://github.com/user/repo'); + }); + + it('converts git:// URL to https', () => { + expect(toHttpsUrl('git://github.com/user/repo.git')).toBe('https://github.com/user/repo'); + }); + + it('converts git+https:// URL to https', () => { + expect(toHttpsUrl('git+https://github.com/user/repo.git')).toBe('https://github.com/user/repo'); + }); + + it('converts git+http:// URL to http', () => { + expect(toHttpsUrl('git+http://example.com/user/repo.git')).toBe('http://example.com/user/repo'); + }); + + it('returns the original string for an unparseable input', () => { + expect(toHttpsUrl('not-a-url-at-all')).toBe('not-a-url-at-all'); + }); +}); diff --git a/libraries/npm-check-fork/src/toHttpsUrl.ts b/libraries/npm-check-fork/src/toHttpsUrl.ts new file mode 100644 index 00000000000..a18206daa53 --- /dev/null +++ b/libraries/npm-check-fork/src/toHttpsUrl.ts @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * Converts a git-protocol URL into a browseable HTTPS URL, using the native URL class. + * + * Handles the common formats found in npm package metadata: + * git@github.com:user/repo.git -> https://github.com/user/repo + * git://github.com/user/repo -> https://github.com/user/repo + * git+https://github.com/... -> https://github.com/... + * + * Returns the original string unchanged if it cannot be parsed. + */ +export function toHttpsUrl(sourceUrl: string): string { + if (!sourceUrl) { + return ''; + } + + let url: string = sourceUrl; + + // Convert SCP-like syntax (git@host:path) to a standard URL + url = url.replace(/^[^@]*@([^:]+):(.+)$/, 'https://$1/$2'); + + // Strip the "git+" compound prefix and normalize git:// to https:// + url = url.replace(/^git\+/, '').replace(/^git:\/\//, 'https://'); + + try { + const parsed: URL = new URL(url); + parsed.pathname = parsed.pathname.replace(/\.git$/i, ''); + return parsed.toString(); + } catch { + return sourceUrl; + } +} diff --git a/libraries/npm-check-fork/tsconfig.json b/libraries/npm-check-fork/tsconfig.json new file mode 100644 index 00000000000..859b2901d5e --- /dev/null +++ b/libraries/npm-check-fork/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": false, + "outDir": "./lib-commonjs", + "declarationDir": "./lib-dts" + } +} diff --git a/libraries/operation-graph/.npmignore b/libraries/operation-graph/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/libraries/operation-graph/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/operation-graph/CHANGELOG.json b/libraries/operation-graph/CHANGELOG.json new file mode 100644 index 00000000000..7fd1741574b --- /dev/null +++ b/libraries/operation-graph/CHANGELOG.json @@ -0,0 +1,1023 @@ +{ + "name": "@rushstack/operation-graph", + "entries": [ + { + "version": "0.6.11", + "tag": "@rushstack/operation-graph_v0.6.11", + "date": "Fri, 17 Jul 2026 00:15:59 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + } + ] + } + }, + { + "version": "0.6.10", + "tag": "@rushstack/operation-graph_v0.6.10", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + } + ] + } + }, + { + "version": "0.6.9", + "tag": "@rushstack/operation-graph_v0.6.9", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + } + ] + } + }, + { + "version": "0.6.8", + "tag": "@rushstack/operation-graph_v0.6.8", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + } + ] + } + }, + { + "version": "0.6.7", + "tag": "@rushstack/operation-graph_v0.6.7", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + } + ] + } + }, + { + "version": "0.6.6", + "tag": "@rushstack/operation-graph_v0.6.6", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + } + ] + } + }, + { + "version": "0.6.5", + "tag": "@rushstack/operation-graph_v0.6.5", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + } + ] + } + }, + { + "version": "0.6.4", + "tag": "@rushstack/operation-graph_v0.6.4", + "date": "Tue, 31 Mar 2026 15:14:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + } + ] + } + }, + { + "version": "0.6.3", + "tag": "@rushstack/operation-graph_v0.6.3", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + } + ] + } + }, + { + "version": "0.6.2", + "tag": "@rushstack/operation-graph_v0.6.2", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + } + ] + } + }, + { + "version": "0.6.1", + "tag": "@rushstack/operation-graph_v0.6.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + } + ] + } + }, + { + "version": "0.6.0", + "tag": "@rushstack/operation-graph_v0.6.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + } + ] + } + }, + { + "version": "0.5.7", + "tag": "@rushstack/operation-graph_v0.5.7", + "date": "Wed, 07 Jan 2026 01:12:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + } + ] + } + }, + { + "version": "0.5.6", + "tag": "@rushstack/operation-graph_v0.5.6", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + } + ] + } + }, + { + "version": "0.5.5", + "tag": "@rushstack/operation-graph_v0.5.5", + "date": "Sat, 06 Dec 2025 01:12:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + } + ] + } + }, + { + "version": "0.5.4", + "tag": "@rushstack/operation-graph_v0.5.4", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + } + ] + } + }, + { + "version": "0.5.3", + "tag": "@rushstack/operation-graph_v0.5.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + } + ] + } + }, + { + "version": "0.5.2", + "tag": "@rushstack/operation-graph_v0.5.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + } + ] + } + }, + { + "version": "0.5.1", + "tag": "@rushstack/operation-graph_v0.5.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + } + ] + } + }, + { + "version": "0.5.0", + "tag": "@rushstack/operation-graph_v0.5.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + } + ] + } + }, + { + "version": "0.4.1", + "tag": "@rushstack/operation-graph_v0.4.1", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + } + ] + } + }, + { + "version": "0.4.0", + "tag": "@rushstack/operation-graph_v0.4.0", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "minor": [ + { + "comment": "Require the \"requestor\" parameter and add a new \"detail\" parameter for watch-mode rerun requests. Make \"name\" a required field for operations." + }, + { + "comment": "(BREAKING CHANGE) Revert the extensibility points for `(before/after)ExecuteOperation(Group)?Async` to be synchronous to signify that they are only meant for logging, not for expensive work." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + } + ] + } + }, + { + "version": "0.3.2", + "tag": "@rushstack/operation-graph_v0.3.2", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + } + ] + } + }, + { + "version": "0.3.1", + "tag": "@rushstack/operation-graph_v0.3.1", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + } + ] + } + }, + { + "version": "0.3.0", + "tag": "@rushstack/operation-graph_v0.3.0", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "minor": [ + { + "comment": "(BREAKING CHANGE) The OperationExecutionManager `beforeExecute` and `afterExecute` hooks have been made async and renamed to `beforeExecuteAsync` and `afterExecuteAsync`. Operations now have an optional `metadata` field that can be used to store arbitrary data." + } + ] + } + }, + { + "version": "0.2.41", + "tag": "@rushstack/operation-graph_v0.2.41", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + } + ] + } + }, + { + "version": "0.2.40", + "tag": "@rushstack/operation-graph_v0.2.40", + "date": "Tue, 25 Mar 2025 15:11:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + } + ] + } + }, + { + "version": "0.2.39", + "tag": "@rushstack/operation-graph_v0.2.39", + "date": "Tue, 11 Mar 2025 02:12:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + } + ] + } + }, + { + "version": "0.2.38", + "tag": "@rushstack/operation-graph_v0.2.38", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + } + ] + } + }, + { + "version": "0.2.37", + "tag": "@rushstack/operation-graph_v0.2.37", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + } + ] + } + }, + { + "version": "0.2.36", + "tag": "@rushstack/operation-graph_v0.2.36", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + } + ] + } + }, + { + "version": "0.2.35", + "tag": "@rushstack/operation-graph_v0.2.35", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + } + ] + } + }, + { + "version": "0.2.34", + "tag": "@rushstack/operation-graph_v0.2.34", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + } + ] + } + }, + { + "version": "0.2.33", + "tag": "@rushstack/operation-graph_v0.2.33", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + } + ] + } + }, + { + "version": "0.2.32", + "tag": "@rushstack/operation-graph_v0.2.32", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + } + ] + } + }, + { + "version": "0.2.31", + "tag": "@rushstack/operation-graph_v0.2.31", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + } + ] + } + }, + { + "version": "0.2.30", + "tag": "@rushstack/operation-graph_v0.2.30", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + } + ] + } + }, + { + "version": "0.2.29", + "tag": "@rushstack/operation-graph_v0.2.29", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + } + ] + } + }, + { + "version": "0.2.28", + "tag": "@rushstack/operation-graph_v0.2.28", + "date": "Wed, 17 Jul 2024 06:55:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + } + ] + } + }, + { + "version": "0.2.27", + "tag": "@rushstack/operation-graph_v0.2.27", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "patch": [ + { + "comment": "Handle errors when sending IPC messages to host." + } + ] + } + }, + { + "version": "0.2.26", + "tag": "@rushstack/operation-graph_v0.2.26", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + } + ] + } + }, + { + "version": "0.2.25", + "tag": "@rushstack/operation-graph_v0.2.25", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + } + ] + } + }, + { + "version": "0.2.24", + "tag": "@rushstack/operation-graph_v0.2.24", + "date": "Wed, 29 May 2024 02:03:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + } + ] + } + }, + { + "version": "0.2.23", + "tag": "@rushstack/operation-graph_v0.2.23", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + } + ] + } + }, + { + "version": "0.2.22", + "tag": "@rushstack/operation-graph_v0.2.22", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + } + ] + } + }, + { + "version": "0.2.21", + "tag": "@rushstack/operation-graph_v0.2.21", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + } + ] + } + }, + { + "version": "0.2.20", + "tag": "@rushstack/operation-graph_v0.2.20", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + } + ] + } + }, + { + "version": "0.2.19", + "tag": "@rushstack/operation-graph_v0.2.19", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + } + ] + } + }, + { + "version": "0.2.18", + "tag": "@rushstack/operation-graph_v0.2.18", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + } + ] + } + }, + { + "version": "0.2.17", + "tag": "@rushstack/operation-graph_v0.2.17", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + } + ] + } + }, + { + "version": "0.2.16", + "tag": "@rushstack/operation-graph_v0.2.16", + "date": "Mon, 06 May 2024 15:11:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + } + ] + } + }, + { + "version": "0.2.15", + "tag": "@rushstack/operation-graph_v0.2.15", + "date": "Wed, 10 Apr 2024 15:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/operation-graph_v0.2.14", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/operation-graph_v0.2.13", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "patch": [ + { + "comment": "Fix memory leaks on abort controllers." + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/operation-graph_v0.2.12", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/operation-graph_v0.2.11", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.1`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/operation-graph_v0.2.10", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.0`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/operation-graph_v0.2.9", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix broken link to API documentation" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/operation-graph_v0.2.8", + "date": "Thu, 08 Feb 2024 01:09:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/operation-graph_v0.2.7", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/operation-graph_v0.2.6", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/operation-graph_v0.2.5", + "date": "Tue, 23 Jan 2024 20:12:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/operation-graph_v0.2.4", + "date": "Tue, 23 Jan 2024 16:15:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/operation-graph_v0.2.3", + "date": "Tue, 16 Jan 2024 18:30:10 GMT", + "comments": { + "patch": [ + { + "comment": "Upgrade build dependencies" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/operation-graph_v0.2.2", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/operation-graph_v0.2.1", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/operation-graph_v0.2.0", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "minor": [ + { + "comment": "Enforce task concurrency limits and respect priority for sequencing." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/operation-graph_v0.1.2", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/operation-graph_v0.1.1", + "date": "Mon, 25 Sep 2023 23:38:27 GMT", + "comments": { + "patch": [ + { + "comment": "Add OperationStatus.Waiting to possible states in watcher loop, add exhaustiveness check." + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/operation-graph_v0.1.0", + "date": "Tue, 19 Sep 2023 15:21:51 GMT", + "comments": { + "minor": [ + { + "comment": "Initial commit. Includes IPC support and watch loop." + } + ] + } + } + ] +} diff --git a/libraries/operation-graph/CHANGELOG.md b/libraries/operation-graph/CHANGELOG.md new file mode 100644 index 00000000000..79ffe85aa75 --- /dev/null +++ b/libraries/operation-graph/CHANGELOG.md @@ -0,0 +1,383 @@ +# Change Log - @rushstack/operation-graph + +This log was last generated on Fri, 17 Jul 2026 00:15:59 GMT and should not be manually modified. + +## 0.6.11 +Fri, 17 Jul 2026 00:15:59 GMT + +_Version update only_ + +## 0.6.10 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.6.9 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.6.8 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 0.6.7 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 0.6.6 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.6.5 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.6.4 +Tue, 31 Mar 2026 15:14:14 GMT + +_Version update only_ + +## 0.6.3 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 0.6.2 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.6.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.6.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.5.7 +Wed, 07 Jan 2026 01:12:24 GMT + +_Version update only_ + +## 0.5.6 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 0.5.5 +Sat, 06 Dec 2025 01:12:29 GMT + +_Version update only_ + +## 0.5.4 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 0.5.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 0.5.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 0.5.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 0.5.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 0.4.1 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 0.4.0 +Tue, 30 Sep 2025 20:33:51 GMT + +### Minor changes + +- Require the "requestor" parameter and add a new "detail" parameter for watch-mode rerun requests. Make "name" a required field for operations. +- (BREAKING CHANGE) Revert the extensibility points for `(before/after)ExecuteOperation(Group)?Async` to be synchronous to signify that they are only meant for logging, not for expensive work. + +## 0.3.2 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.3.1 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.3.0 +Sat, 21 Jun 2025 00:13:15 GMT + +### Minor changes + +- (BREAKING CHANGE) The OperationExecutionManager `beforeExecute` and `afterExecute` hooks have been made async and renamed to `beforeExecuteAsync` and `afterExecuteAsync`. Operations now have an optional `metadata` field that can be used to store arbitrary data. + +## 0.2.41 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.2.40 +Tue, 25 Mar 2025 15:11:15 GMT + +_Version update only_ + +## 0.2.39 +Tue, 11 Mar 2025 02:12:33 GMT + +_Version update only_ + +## 0.2.38 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.2.37 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.2.36 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.2.35 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.2.34 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.2.33 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.2.32 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.2.31 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.2.30 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.2.29 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.2.28 +Wed, 17 Jul 2024 06:55:10 GMT + +_Version update only_ + +## 0.2.27 +Wed, 17 Jul 2024 00:11:19 GMT + +### Patches + +- Handle errors when sending IPC messages to host. + +## 0.2.26 +Tue, 16 Jul 2024 00:36:21 GMT + +_Version update only_ + +## 0.2.25 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.2.24 +Wed, 29 May 2024 02:03:51 GMT + +_Version update only_ + +## 0.2.23 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.2.22 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.2.21 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.2.20 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 0.2.19 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.2.18 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.2.17 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.2.16 +Mon, 06 May 2024 15:11:05 GMT + +_Version update only_ + +## 0.2.15 +Wed, 10 Apr 2024 15:10:08 GMT + +_Version update only_ + +## 0.2.14 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.2.13 +Thu, 22 Feb 2024 01:36:09 GMT + +### Patches + +- Fix memory leaks on abort controllers. + +## 0.2.12 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.2.11 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.2.10 +Mon, 19 Feb 2024 21:54:27 GMT + +_Version update only_ + +## 0.2.9 +Sat, 17 Feb 2024 06:24:35 GMT + +### Patches + +- Fix broken link to API documentation + +## 0.2.8 +Thu, 08 Feb 2024 01:09:22 GMT + +_Version update only_ + +## 0.2.7 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.2.6 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.2.5 +Tue, 23 Jan 2024 20:12:58 GMT + +_Version update only_ + +## 0.2.4 +Tue, 23 Jan 2024 16:15:05 GMT + +_Version update only_ + +## 0.2.3 +Tue, 16 Jan 2024 18:30:10 GMT + +### Patches + +- Upgrade build dependencies + +## 0.2.2 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.2.1 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.2.0 +Thu, 28 Sep 2023 20:53:17 GMT + +### Minor changes + +- Enforce task concurrency limits and respect priority for sequencing. + +## 0.1.2 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.1.1 +Mon, 25 Sep 2023 23:38:27 GMT + +### Patches + +- Add OperationStatus.Waiting to possible states in watcher loop, add exhaustiveness check. + +## 0.1.0 +Tue, 19 Sep 2023 15:21:51 GMT + +### Minor changes + +- Initial commit. Includes IPC support and watch loop. + diff --git a/libraries/operation-graph/LICENSE b/libraries/operation-graph/LICENSE new file mode 100644 index 00000000000..bd4533ad992 --- /dev/null +++ b/libraries/operation-graph/LICENSE @@ -0,0 +1,24 @@ +@rushstack/operation-graph + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/libraries/operation-graph/README.md b/libraries/operation-graph/README.md new file mode 100644 index 00000000000..f04f7c25777 --- /dev/null +++ b/libraries/operation-graph/README.md @@ -0,0 +1,12 @@ +# @rushstack/operation-graph + +This library contains logic for managing and executing tasks in a directed acyclic graph. It supports single execution or executing in a loop. + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/libraries/operation-graph/CHANGELOG.md) - Find + out what's new in the latest version +- [API Reference](https://api.rushstack.io/pages/operation-graph/) + +`@rushstack/operation-graph` is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/libraries/operation-graph/config/api-extractor.json b/libraries/operation-graph/config/api-extractor.json new file mode 100644 index 00000000000..b53db1d0910 --- /dev/null +++ b/libraries/operation-graph/config/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/api-extractor-base.json" +} diff --git a/libraries/operation-graph/config/jest.config.json b/libraries/operation-graph/config/jest.config.json new file mode 100644 index 00000000000..7c0f9ccc9d6 --- /dev/null +++ b/libraries/operation-graph/config/jest.config.json @@ -0,0 +1,3 @@ +{ + "extends": "decoupled-local-node-rig/profiles/default/config/jest.config.json" +} diff --git a/libraries/operation-graph/config/rig.json b/libraries/operation-graph/config/rig.json new file mode 100644 index 00000000000..cc98dea43dd --- /dev/null +++ b/libraries/operation-graph/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "decoupled-local-node-rig" +} diff --git a/libraries/operation-graph/eslint.config.js b/libraries/operation-graph/eslint.config.js new file mode 100644 index 00000000000..f83aea7d1b7 --- /dev/null +++ b/libraries/operation-graph/eslint.config.js @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); +const tsdocMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/tsdoc'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + ...tsdocMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/operation-graph/package.json b/libraries/operation-graph/package.json new file mode 100644 index 00000000000..d152ef76570 --- /dev/null +++ b/libraries/operation-graph/package.json @@ -0,0 +1,59 @@ +{ + "name": "@rushstack/operation-graph", + "version": "0.6.11", + "description": "Library for managing and executing operations in a directed acyclic graph.", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/operation-graph.d.ts", + "exports": { + ".": { + "types": "./dist/operation-graph.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "license": "MIT", + "repository": { + "url": "https://github.com/microsoft/rushstack.git", + "type": "git", + "directory": "libraries/operation-graph" + }, + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "dependencies": { + "@rushstack/node-core-library": "workspace:*", + "@rushstack/terminal": "workspace:*" + }, + "devDependencies": { + "@rushstack/heft": "1.2.22", + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + }, + "sideEffects": false +} diff --git a/libraries/operation-graph/src/IOperationRunner.ts b/libraries/operation-graph/src/IOperationRunner.ts new file mode 100644 index 00000000000..4f66f332d1b --- /dev/null +++ b/libraries/operation-graph/src/IOperationRunner.ts @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { OperationStatus } from './OperationStatus'; +import type { OperationError } from './OperationError'; +import type { Stopwatch } from './Stopwatch'; + +/** + * Information passed to the executing `IOperationRunner` + * + * @beta + */ +export interface IOperationRunnerContext { + /** + * An abort signal for the overarching execution. Runners should do their best to gracefully abort + * as soon as possible if the signal is aborted. + */ + abortSignal: AbortSignal; + + /** + * If this is the first time this operation has been executed. + */ + isFirstRun: boolean; + + /** + * A callback to the overarching orchestrator to request that the operation be invoked again. + * Used in watch mode to signal that inputs have changed. + * + * @param detail - Optional detail about why the rerun is requested, e.g. the name of a changed file. + */ + requestRun?: (detail?: string) => void; +} + +/** + * Interface contract for a single state of an operation. + * + * @beta + */ +export interface IOperationState { + /** + * The status code for the operation. + */ + status: OperationStatus; + /** + * Whether the operation has been run at least once. + */ + hasBeenRun: boolean; + /** + * The error, if the status is `OperationStatus.Failure`. + */ + error: OperationError | undefined; + /** + * Timing information for the operation. + */ + stopwatch: Stopwatch; +} + +/** + * Interface contract for the current and past state of an operation. + * + * @beta + */ +export interface IOperationStates { + /** + * The current state of the operation. + */ + readonly state: Readonly | undefined; + /** + * The previous state of the operation. + */ + readonly lastState: Readonly | undefined; +} + +/** + * The `Operation` class is a node in the dependency graph of work that needs to be scheduled by the + * `OperationExecutionManager`. Each `Operation` has a `runner` member of type `IOperationRunner`, whose + * implementation manages the actual process for running a single operation. + * + * @beta + */ +export interface IOperationRunner { + /** + * Name of the operation, for logging. + */ + readonly name: string; + + /** + * Indicates that this runner is architectural and should not be reported on. + */ + silent: boolean; + + /** + * Method to be executed for the operation. + */ + executeAsync(context: IOperationRunnerContext): Promise; +} diff --git a/libraries/operation-graph/src/Operation.ts b/libraries/operation-graph/src/Operation.ts new file mode 100644 index 00000000000..2094be60e7c --- /dev/null +++ b/libraries/operation-graph/src/Operation.ts @@ -0,0 +1,376 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { InternalError } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import { Stopwatch } from './Stopwatch'; +import type { + IOperationRunner, + IOperationRunnerContext, + IOperationState, + IOperationStates +} from './IOperationRunner'; +import type { OperationError } from './OperationError'; +import { OperationStatus } from './OperationStatus'; +import type { OperationGroupRecord } from './OperationGroupRecord'; + +/** + * Options for constructing a new Operation. + * @beta + */ +export interface IOperationOptions { + /** + * The name of this operation, for logging. + */ + name: string; + + /** + * The group that this operation belongs to. Will be used for logging and duration tracking. + */ + group?: OperationGroupRecord | undefined; + + /** + * When the scheduler is ready to process this `Operation`, the `runner` implements the actual work of + * running the operation. + */ + runner?: IOperationRunner | undefined; + + /** + * The weight used by the scheduler to determine order of execution. + */ + weight?: number | undefined; + + /** + * The metadata for this operation. + */ + metadata?: TMetadata | undefined; +} + +/** + * Type for the `requestRun` callback. + * @beta + */ +export type OperationRequestRunCallback = (requestor: string, detail?: string) => void; + +/** + * Information provided to `executeAsync` by the `OperationExecutionManager`. + * + * @beta + */ +export interface IExecuteOperationContext extends Omit { + /** + * Function to invoke before execution of an operation, for logging. + */ + beforeExecute(operation: Operation, state: IOperationState): void; + + /** + * Function to invoke after execution of an operation, for logging. + */ + afterExecute(operation: Operation, state: IOperationState): void; + + /** + * Function used to schedule the concurrency-limited execution of an operation. + * + * Will return OperationStatus.Aborted if execution is aborted before the task executes. + */ + queueWork(workFn: () => Promise, priority: number): Promise; + + /** + * A callback to the overarching orchestrator to request that the operation be invoked again. + * Used in watch mode to signal that inputs have changed. + * + * @param requestor - The name of the operation requesting a rerun. + * @param detail - Optional detail about why the rerun is requested, e.g. the name of a changed file. + */ + requestRun?: OperationRequestRunCallback; + + /** + * Terminal to write output to. + */ + terminal: ITerminal; +} + +/** + * The `Operation` class is a node in the dependency graph of work that needs to be scheduled by the + * `OperationExecutionManager`. Each `Operation` has a `runner` member of type `IOperationRunner`, whose + * implementation manages the actual process of running a single operation. + * + * The graph of `Operation` instances will be cloned into a separate execution graph after processing. + * + * @beta + */ +export class Operation + implements IOperationStates +{ + /** + * A set of all dependencies which must be executed before this operation is complete. + */ + public readonly dependencies: Set> = new Set< + Operation + >(); + /** + * A set of all operations that wait for this operation. + */ + public readonly consumers: Set> = new Set< + Operation + >(); + /** + * If specified, the name of a grouping to which this Operation belongs, for logging start and end times. + */ + public readonly group: OperationGroupRecord | undefined; + /** + * The name of this operation, for logging. + */ + public readonly name: string; + + /** + * When the scheduler is ready to process this `Operation`, the `runner` implements the actual work of + * running the operation. + */ + public runner: IOperationRunner | undefined = undefined; + + /** + * This number represents how far away this Operation is from the furthest "root" operation (i.e. + * an operation with no consumers). This helps us to calculate the critical path (i.e. the + * longest chain of projects which must be executed in order, thereby limiting execution speed + * of the entire operation tree. + * + * This number is calculated via a memoized depth-first search, and when choosing the next + * operation to execute, the operation with the highest criticalPathLength is chosen. + * + * Example: + * (0) A + * \\ + * (1) B C (0) (applications) + * \\ /|\\ + * \\ / | \\ + * (2) D | X (1) (utilities) + * | / \\ + * |/ \\ + * (2) Y Z (2) (other utilities) + * + * All roots (A & C) have a criticalPathLength of 0. + * B has a score of 1, since A depends on it. + * D has a score of 2, since we look at the longest chain (e.g D-\>B-\>A is longer than D-\>C) + * X has a score of 1, since the only package which depends on it is A + * Z has a score of 2, since only X depends on it, and X has a score of 1 + * Y has a score of 2, since the chain Y-\>X-\>C is longer than Y-\>C + * + * The algorithm is implemented in AsyncOperationQueue.ts as calculateCriticalPathLength() + */ + public criticalPathLength: number | undefined = undefined; + + /** + * The weight for this operation. This scalar is the contribution of this operation to the + * `criticalPathLength` calculation above. Modify to indicate the following: + * - `weight` === 1: indicates that this operation has an average duration + * - `weight` > 1: indicates that this operation takes longer than average and so the scheduler + * should try to favor starting it over other, shorter operations. An example might be an operation that + * bundles an entire application and runs whole-program optimization. + * - `weight` < 1: indicates that this operation takes less time than average and so the scheduler + * should favor other, longer operations over it. An example might be an operation to unpack a cached + * output, or an operation using NullOperationRunner, which might use a value of 0. + */ + public weight: number; + + /** + * The state of this operation the previous time a manager was invoked. + */ + public lastState: IOperationState | undefined = undefined; + + /** + * The current state of this operation + */ + public state: IOperationState | undefined = undefined; + + /** + * A cached execution promise for the current OperationExecutionManager invocation of this operation. + */ + private _promise: Promise | undefined = undefined; + + /** + * If true, then a run of this operation is currently wanted. + * This is used to track state from the `requestRun` callback passed to the runner. + */ + private _runPending: boolean = true; + + public readonly metadata: TMetadata; + + public constructor(options: IOperationOptions) { + const { group, runner, weight = 1, name, metadata = {} as TMetadata } = options; + this.group = group; + this.runner = runner; + this.weight = weight; + this.name = name; + this.metadata = metadata; + + if (this.group) { + this.group.addOperation(this); + } + } + + public addDependency(dependency: Operation): void { + this.dependencies.add(dependency); + dependency.consumers.add(this); + } + + public deleteDependency(dependency: Operation): void { + this.dependencies.delete(dependency); + dependency.consumers.delete(this); + } + + public reset(): void { + // Reset operation state + this.lastState = this.state; + + this.state = { + status: this.dependencies.size > 0 ? OperationStatus.Waiting : OperationStatus.Ready, + hasBeenRun: this.lastState?.hasBeenRun ?? false, + error: undefined, + stopwatch: new Stopwatch() + }; + + this._promise = undefined; + this._runPending = true; + } + + /** + * @internal + */ + public async _executeAsync(context: IExecuteOperationContext): Promise { + const { state } = this; + if (!state) { + throw new Error(`Operation state has not been initialized.`); + } + + if (!this._promise) { + this._promise = this._executeInnerAsync(context, state); + } + + return this._promise; + } + + private async _executeInnerAsync( + context: IExecuteOperationContext, + rawState: IOperationState + ): Promise { + const state: IOperationState = rawState; + const { runner } = this; + + const dependencyResults: PromiseSettledResult[] = await Promise.allSettled( + Array.from(this.dependencies, (dependency: Operation) => dependency._executeAsync(context)) + ); + + const { abortSignal, requestRun, queueWork } = context; + + if (abortSignal.aborted) { + state.status = OperationStatus.Aborted; + return state.status; + } + + for (const result of dependencyResults) { + if ( + result.status === 'rejected' || + result.value === OperationStatus.Blocked || + result.value === OperationStatus.Failure + ) { + state.status = OperationStatus.Blocked; + return state.status; + } + } + + state.status = OperationStatus.Ready; + + const innerContext: IOperationRunnerContext = { + abortSignal, + isFirstRun: !state.hasBeenRun, + requestRun: requestRun + ? (detail?: string) => { + switch (this.state?.status) { + case OperationStatus.Waiting: + case OperationStatus.Ready: + case OperationStatus.Executing: + // If current status has not yet resolved to a fixed value, + // re-executing this operation does not require a full rerun + // of the operation graph. Simply mark that a run is requested. + + // This variable is on the Operation instead of the + // containing closure to deal with scenarios in which + // the runner hangs on to an old copy of the callback. + this._runPending = true; + return; + + case OperationStatus.Blocked: + case OperationStatus.Aborted: + case OperationStatus.Failure: + case OperationStatus.NoOp: + case OperationStatus.Success: + // The requestRun callback is assumed to remain constant + // throughout the lifetime of the process, so it is safe + // to capture here. + return requestRun(this.name, detail); + default: + // This line is here to enforce exhaustiveness + const currentStatus: undefined = this.state?.status; + throw new InternalError(`Unexpected status: ${currentStatus}`); + } + } + : undefined + }; + + // eslint-disable-next-line require-atomic-updates + state.status = await queueWork(async (): Promise => { + // Redundant variable to satisfy require-atomic-updates + const innerState: IOperationState = state; + + if (abortSignal.aborted) { + innerState.status = OperationStatus.Aborted; + return innerState.status; + } + + context.beforeExecute(this, innerState); + + innerState.stopwatch.start(); + innerState.status = OperationStatus.Executing; + // Mark that the operation has been started at least once. + innerState.hasBeenRun = true; + + while (this._runPending) { + this._runPending = false; + try { + // We don't support aborting in the middle of a runner's execution. + innerState.status = runner ? await runner.executeAsync(innerContext) : OperationStatus.NoOp; + } catch (error) { + innerState.status = OperationStatus.Failure; + innerState.error = error as OperationError; + } + + // Since runner.executeAsync is async, a change could have occurred that requires re-execution + // This operation is still active, so can re-execute immediately, rather than forcing a whole + // new execution pass. + + // As currently written, this does mean that if a job is scheduled with higher priority while + // this operation is still executing, it will still wait for this retry. This may not be desired + // and if it becomes a problem, the retry loop will need to be moved outside of the `queueWork` call. + // This introduces complexity regarding tracking of timing and start/end logging, however. + + if (this._runPending) { + if (abortSignal.aborted) { + innerState.status = OperationStatus.Aborted; + break; + } else { + context.terminal.writeLine(`Immediate rerun requested. Executing.`); + } + } + } + + state.stopwatch.stop(); + context.afterExecute(this, state); + + return state.status; + }, /* priority */ this.criticalPathLength ?? 0); + + return state.status; + } +} diff --git a/apps/heft/src/operations/OperationError.ts b/libraries/operation-graph/src/OperationError.ts similarity index 88% rename from apps/heft/src/operations/OperationError.ts rename to libraries/operation-graph/src/OperationError.ts index 56219b02bbc..85f392a34be 100644 --- a/apps/heft/src/operations/OperationError.ts +++ b/libraries/operation-graph/src/OperationError.ts @@ -3,6 +3,8 @@ /** * Encapsulates information about an error + * + * @beta */ export class OperationError extends Error { protected _type: string; @@ -14,7 +16,7 @@ export class OperationError extends Error { // https://github.com/microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work // // Note: the prototype must also be set on any classes which extend this one - (this as any).__proto__ = OperationError.prototype; // eslint-disable-line @typescript-eslint/no-explicit-any + Object.setPrototypeOf(this, OperationError.prototype); this._type = type; } diff --git a/libraries/operation-graph/src/OperationExecutionManager.ts b/libraries/operation-graph/src/OperationExecutionManager.ts new file mode 100644 index 00000000000..7132e35af97 --- /dev/null +++ b/libraries/operation-graph/src/OperationExecutionManager.ts @@ -0,0 +1,216 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { Async } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import type { IOperationState } from './IOperationRunner'; +import type { IExecuteOperationContext, Operation, OperationRequestRunCallback } from './Operation'; +import type { OperationGroupRecord } from './OperationGroupRecord'; +import { OperationStatus } from './OperationStatus'; +import { calculateCriticalPathLengths } from './calculateCriticalPath'; +import { WorkQueue } from './WorkQueue'; + +/** + * Options for the current run. + * + * @beta + */ +export interface IOperationExecutionOptions< + TOperationMetadata extends {} = {}, + TGroupMetadata extends {} = {} +> { + abortSignal: AbortSignal; + parallelism: number; + terminal: ITerminal; + + requestRun?: OperationRequestRunCallback; + + beforeExecuteOperation?: (operation: Operation) => void; + afterExecuteOperation?: (operation: Operation) => void; + beforeExecuteOperationGroup?: (operationGroup: OperationGroupRecord) => void; + afterExecuteOperationGroup?: (operationGroup: OperationGroupRecord) => void; +} + +/** + * A class which manages the execution of a set of tasks with interdependencies. + * Initially, and at the end of each task execution, all unblocked tasks + * are added to a ready queue which is then executed. This is done continually until all + * tasks are complete, or prematurely fails if any of the tasks fail. + * + * @beta + */ +export class OperationExecutionManager { + /** + * The set of operations that will be executed + */ + private readonly _operations: Operation[]; + /** + * The total number of non-silent operations in the graph. + * Silent operations are generally used to simplify the construction of the graph. + */ + private readonly _trackedOperationCount: number; + + private readonly _groupRecords: Set>; + + public constructor(operations: ReadonlySet>) { + let trackedOperationCount: number = 0; + for (const operation of operations) { + if (!operation.runner?.silent) { + // Only count non-silent operations + trackedOperationCount++; + } + } + + this._trackedOperationCount = trackedOperationCount; + + this._operations = calculateCriticalPathLengths(operations); + + this._groupRecords = new Set(Array.from(this._operations, (e) => e.group).filter((e) => e !== undefined)); + + for (const consumer of operations) { + for (const dependency of consumer.dependencies) { + if (!operations.has(dependency)) { + throw new Error( + `Operation ${JSON.stringify(consumer.name)} declares a dependency on operation ` + + `${JSON.stringify(dependency.name)} that is not in the set of operations to execute.` + ); + } + } + } + } + + /** + * Executes all operations which have been registered, returning a promise which is resolved when all the + * operations are completed successfully, or rejects when any operation fails. + */ + public async executeAsync( + executionOptions: IOperationExecutionOptions + ): Promise { + let hasReportedFailures: boolean = false; + + const { abortSignal, parallelism, terminal, requestRun } = executionOptions; + + if (abortSignal.aborted) { + return OperationStatus.Aborted; + } + + const startedGroups: Set = new Set(); + const finishedGroups: Set = new Set(); + + const maxParallelism: number = Math.min(this._operations.length, parallelism); + + for (const groupRecord of this._groupRecords) { + groupRecord.reset(); + } + + for (const operation of this._operations) { + operation.reset(); + } + + terminal.writeVerboseLine(`Executing a maximum of ${maxParallelism} simultaneous tasks...`); + + const workQueueAbortController: AbortController = new AbortController(); + const abortHandler: () => void = () => workQueueAbortController.abort(); + abortSignal.addEventListener('abort', abortHandler, { once: true }); + try { + const workQueue: WorkQueue = new WorkQueue(workQueueAbortController.signal); + + const executionContext: IExecuteOperationContext = { + terminal, + abortSignal, + + requestRun, + + queueWork: (workFn: () => Promise, priority: number): Promise => { + return workQueue.pushAsync(workFn, priority); + }, + + beforeExecute: (operation: Operation): void => { + // Initialize group if uninitialized and log the group name + const { group, runner } = operation; + if (group) { + if (!startedGroups.has(group)) { + startedGroups.add(group); + group.startTimer(); + terminal.writeLine(` ---- ${group.name} started ---- `); + executionOptions.beforeExecuteOperationGroup?.(group); + } + } + if (!runner?.silent) { + executionOptions.beforeExecuteOperation?.(operation); + } + }, + + afterExecute: ( + operation: Operation, + state: IOperationState + ): void => { + const { group, runner } = operation; + if (group) { + group.setOperationAsComplete(operation, state); + } + + if (state.status === OperationStatus.Failure) { + // This operation failed. Mark it as such and all reachable dependents as blocked. + // Failed operations get reported, even if silent. + // Generally speaking, silent operations shouldn't be able to fail, so this is a safety measure. + const message: string | undefined = state.error?.message; + if (message) { + terminal.writeErrorLine(message); + } + hasReportedFailures = true; + } + + if (!runner?.silent) { + executionOptions.afterExecuteOperation?.(operation); + } + + if (group) { + // Log out the group name and duration if it is the last operation in the group + if (group?.finished && !finishedGroups.has(group)) { + finishedGroups.add(group); + const finishedLoggingWord: string = group.hasFailures + ? 'encountered an error' + : group.hasCancellations + ? 'cancelled' + : 'finished'; + terminal.writeLine( + ` ---- ${group.name} ${finishedLoggingWord} (${group.duration.toFixed(3)}s) ---- ` + ); + executionOptions.afterExecuteOperationGroup?.(group); + } + } + } + }; + + const workQueuePromise: Promise = Async.forEachAsync( + workQueue, + (workFn: () => Promise) => workFn(), + { + concurrency: maxParallelism + } + ); + + await Promise.all(this._operations.map((record: Operation) => record._executeAsync(executionContext))); + + // Terminate queue execution. + workQueueAbortController.abort(); + await workQueuePromise; + } finally { + // Cleanup resources + abortSignal.removeEventListener('abort', abortHandler); + } + + const finalStatus: OperationStatus = + this._trackedOperationCount === 0 + ? OperationStatus.NoOp + : abortSignal.aborted + ? OperationStatus.Aborted + : hasReportedFailures + ? OperationStatus.Failure + : OperationStatus.Success; + + return finalStatus; + } +} diff --git a/apps/heft/src/operations/OperationGroupRecord.ts b/libraries/operation-graph/src/OperationGroupRecord.ts similarity index 83% rename from apps/heft/src/operations/OperationGroupRecord.ts rename to libraries/operation-graph/src/OperationGroupRecord.ts index 3effd6c4f07..d6d21106253 100644 --- a/apps/heft/src/operations/OperationGroupRecord.ts +++ b/libraries/operation-graph/src/OperationGroupRecord.ts @@ -1,13 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { IOperationState } from './IOperationRunner'; -import { OperationStatus } from './OperationStatus'; import { InternalError } from '@rushstack/node-core-library'; -import { Stopwatch } from '../utilities/Stopwatch'; + +import type { IOperationState } from './IOperationRunner'; import type { Operation } from './Operation'; +import { OperationStatus } from './OperationStatus'; +import { Stopwatch } from './Stopwatch'; -export class OperationGroupRecord { +/** + * Meta-entity that tracks information about a group of related operations. + * + * @beta + */ +export class OperationGroupRecord { private readonly _operations: Set = new Set(); private _remainingOperations: Set = new Set(); @@ -16,6 +22,7 @@ export class OperationGroupRecord { private _hasFailures: boolean = false; public readonly name: string; + public readonly metadata: TMetadata; public get duration(): number { return this._groupStopwatch ? this._groupStopwatch.duration : 0; @@ -33,8 +40,9 @@ export class OperationGroupRecord { return this._hasFailures; } - public constructor(name: string) { + public constructor(name: string, metadata: TMetadata = {} as TMetadata) { this.name = name; + this.metadata = metadata; } public addOperation(operation: Operation): void { @@ -51,7 +59,7 @@ export class OperationGroupRecord { throw new InternalError(`Operation ${operation.name} is not in the group ${this.name}`); } - if (state.status === OperationStatus.Cancelled) { + if (state.status === OperationStatus.Aborted) { this._hasCancellations = true; } else if (state.status === OperationStatus.Failure) { this._hasFailures = true; diff --git a/apps/heft/src/operations/OperationStatus.ts b/libraries/operation-graph/src/OperationStatus.ts similarity index 78% rename from apps/heft/src/operations/OperationStatus.ts rename to libraries/operation-graph/src/OperationStatus.ts index 30422bcc3f2..176108455c4 100644 --- a/apps/heft/src/operations/OperationStatus.ts +++ b/libraries/operation-graph/src/OperationStatus.ts @@ -7,9 +7,13 @@ */ export enum OperationStatus { /** - * The Operation is on the queue, ready to execute (but may be waiting for dependencies) + * The Operation is on the queue, ready to execute */ Ready = 'READY', + /** + * The Operation is on the queue, waiting for one or more depencies + */ + Waiting = 'WAITING', /** * The Operation is currently executing */ @@ -23,9 +27,9 @@ export enum OperationStatus { */ Failure = 'FAILURE', /** - * The operation was cancelled + * The operation was aborted */ - Cancelled = 'CANCELLED', + Aborted = 'ABORTED', /** * The Operation could not be executed because one or more of its dependencies failed */ diff --git a/apps/heft/src/utilities/Stopwatch.ts b/libraries/operation-graph/src/Stopwatch.ts similarity index 83% rename from apps/heft/src/utilities/Stopwatch.ts rename to libraries/operation-graph/src/Stopwatch.ts index c298a6e6616..ed1591d731a 100644 --- a/apps/heft/src/utilities/Stopwatch.ts +++ b/libraries/operation-graph/src/Stopwatch.ts @@ -1,29 +1,21 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { performance } from 'perf_hooks'; - -/** - * Used with the Stopwatch class. - */ -export enum StopwatchState { - Stopped = 1, - Started = 2 -} - /** * Represents a typical timer/stopwatch which keeps track * of elapsed time in between two events. + * + * @public */ export class Stopwatch { private _startTime: number | undefined; private _endTime: number | undefined; - private _state: StopwatchState; + private _running: boolean; public constructor() { this._startTime = undefined; this._endTime = undefined; - this._state = StopwatchState.Stopped; + this._running = false; } /** @@ -33,8 +25,8 @@ export class Stopwatch { return new Stopwatch().start(); } - public get state(): StopwatchState { - return this._state; + public get isRunning(): boolean { + return this._running; } /** @@ -47,7 +39,7 @@ export class Stopwatch { } this._startTime = performance.now(); this._endTime = undefined; - this._state = StopwatchState.Started; + this._running = true; return this; } @@ -56,7 +48,7 @@ export class Stopwatch { */ public stop(): Stopwatch { this._endTime = this._startTime !== undefined ? performance.now() : undefined; - this._state = StopwatchState.Stopped; + this._running = false; return this; } @@ -65,7 +57,7 @@ export class Stopwatch { */ public reset(): Stopwatch { this._endTime = this._startTime = undefined; - this._state = StopwatchState.Stopped; + this._running = false; return this; } @@ -73,7 +65,7 @@ export class Stopwatch { * Displays how long the stopwatch has been executing in a human readable format. */ public toString(): string { - if (this._state === StopwatchState.Stopped && this._startTime === undefined) { + if (!this._running && this._startTime === undefined) { return '0.00 seconds (stopped)'; } const totalSeconds: number = this.duration; diff --git a/libraries/operation-graph/src/WatchLoop.ts b/libraries/operation-graph/src/WatchLoop.ts new file mode 100644 index 00000000000..43ff6b32c7c --- /dev/null +++ b/libraries/operation-graph/src/WatchLoop.ts @@ -0,0 +1,299 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { once } from 'node:events'; + +import { AlreadyReportedError } from '@rushstack/node-core-library'; + +import type { OperationRequestRunCallback } from './Operation'; +import { OperationStatus } from './OperationStatus'; +import type { + IAfterExecuteEventMessage, + IPCHost, + CommandMessageFromHost, + ISyncEventMessage, + IRequestRunEventMessage +} from './protocol.types'; + +/** + * Callbacks for the watch loop. + * + * @beta + */ +export interface IWatchLoopOptions { + /** + * Callback that performs the core work of a single iteration. + */ + executeAsync: (state: IWatchLoopState) => Promise; + /** + * Logging callback immediately before execution occurs. + */ + onBeforeExecute: () => void; + /** + * Logging callback when a run is requested (and hasn't already been). + * + * @param requestor - The name of the operation requesting a rerun. + * @param detail - Optional detail about why the rerun is requested, e.g. the name of a changed file. + */ + onRequestRun: OperationRequestRunCallback; + /** + * Logging callback when a run is aborted. + */ + onAbort: () => void; +} + +/** + * The public API surface of the watch loop, for use in the `executeAsync` callback. + * + * @beta + */ +export interface IWatchLoopState { + get abortSignal(): AbortSignal; + requestRun: OperationRequestRunCallback; +} + +/** + * This class implements a watch loop. + * + * @beta + */ +export class WatchLoop implements IWatchLoopState { + private readonly _options: Readonly; + + private _abortController: AbortController; + private _isRunning: boolean; + private _runRequested: boolean; + private _requestRunPromise: Promise<[string, string?]>; + private _resolveRequestRun!: (value: [string, string?]) => void; + + public constructor(options: IWatchLoopOptions) { + this._options = options; + + this._abortController = new AbortController(); + this._isRunning = false; + // Always start as true, so that any requests prior to first run are silenced. + this._runRequested = true; + this._requestRunPromise = new Promise<[string, string?]>((resolve) => { + this._resolveRequestRun = resolve; + }); + } + + /** + * Runs the inner loop until the abort signal is cancelled or a run completes without a new run being requested. + */ + public async runUntilStableAsync(abortSignal: AbortSignal): Promise { + if (abortSignal.aborted) { + return OperationStatus.Aborted; + } + + abortSignal.addEventListener('abort', this._abortCurrent, { once: true }); + + try { + let result: OperationStatus = OperationStatus.Ready; + + do { + // Always check the abort signal first, in case it was aborted in the async tick since the last executeAsync() call. + if (abortSignal.aborted) { + return OperationStatus.Aborted; + } + + result = await this._runIterationAsync(); + } while (this._runRequested); + + // Even if the run has finished, if the abort signal was aborted, we should return `Aborted` just in case. + return abortSignal.aborted ? OperationStatus.Aborted : result; + } finally { + abortSignal.removeEventListener('abort', this._abortCurrent); + } + } + + /** + * Runs the inner loop until the abort signal is aborted. Will otherwise wait indefinitely for a new run to be requested. + */ + public async runUntilAbortedAsync(abortSignal: AbortSignal, onWaiting: () => void): Promise { + if (abortSignal.aborted) { + return; + } + + const abortPromise: Promise = once(abortSignal, 'abort'); + + while (!abortSignal.aborted) { + await this.runUntilStableAsync(abortSignal); + + onWaiting(); + await Promise.race([this._requestRunPromise, abortPromise]); + } + } + + /** + * Sets up an IPC handler that will run the inner loop when it receives a "run" message from the host. + * Runs until receiving an "exit" message from the host, or aborts early if an unhandled error is thrown. + */ + public async runIPCAsync(host: IPCHost = process): Promise { + await new Promise((resolve, reject) => { + let abortController: AbortController = new AbortController(); + + let runRequestedFromHost: boolean = true; + let status: OperationStatus = OperationStatus.Ready; + + function tryMessageHost( + message: ISyncEventMessage | IRequestRunEventMessage | IAfterExecuteEventMessage + ): void { + if (!host.send) { + return reject(new Error('Host does not support IPC')); + } + + try { + host.send(message); + } catch (err) { + reject(new Error(`Unable to communicate with host: ${err}`)); + } + } + + function requestRunFromHost(requestor: string, detail?: string): void { + if (runRequestedFromHost) { + return; + } + + runRequestedFromHost = true; + + const requestRunMessage: IRequestRunEventMessage = { + event: 'requestRun', + requestor, + detail + }; + + tryMessageHost(requestRunMessage); + } + + function sendSync(): void { + const syncMessage: ISyncEventMessage = { + event: 'sync', + status + }; + tryMessageHost(syncMessage); + } + + host.on('message', async (message: CommandMessageFromHost) => { + switch (message.command) { + case 'exit': { + return resolve(); + } + + case 'cancel': { + if (this._isRunning) { + abortController.abort(); + abortController = new AbortController(); + // This will terminate the currently executing `runUntilStableAsync` call. + } + return; + } + + case 'run': { + runRequestedFromHost = false; + + status = OperationStatus.Executing; + + try { + status = await this.runUntilStableAsync(abortController.signal); + // ESLINT: "Promises must be awaited, end with a call to .catch, end with a call to .then ..." + this._requestRunPromise.then( + ([requestor, detail]) => requestRunFromHost(requestor, detail), + (error: Error) => { + // Unreachable code. The promise will never be rejected. + } + ); + } catch (err) { + status = OperationStatus.Failure; + return reject(err); + } finally { + const afterExecuteMessage: IAfterExecuteEventMessage = { + event: 'after-execute', + status + }; + tryMessageHost(afterExecuteMessage); + } + return; + } + + case 'sync': { + return sendSync(); + } + + default: { + return reject(new Error(`Unexpected command from host: ${message}`)); + } + } + }); + + sendSync(); + }); + } + + /** + * Requests that a new run occur. + */ + public requestRun: OperationRequestRunCallback = (requestor: string, detail?: string) => { + if (!this._runRequested) { + this._options.onRequestRun(requestor, detail); + this._runRequested = true; + if (this._isRunning) { + this._options.onAbort(); + this._abortCurrent(); + } + } + this._resolveRequestRun([requestor, detail]); + }; + + /** + * The abort signal for the current iteration. + */ + public get abortSignal(): AbortSignal { + return this._abortController.signal; + } + + /** + * Cancels the current iteration (if possible). + */ + private _abortCurrent = (): void => { + this._abortController.abort(); + }; + + /** + * Resets the abort signal and run request state. + */ + private _reset(): void { + if (this._abortController.signal.aborted) { + this._abortController = new AbortController(); + } + + if (this._runRequested) { + this._runRequested = false; + this._requestRunPromise = new Promise<[string, string?]>((resolve) => { + this._resolveRequestRun = resolve; + }); + } + } + + /** + * Runs a single iteration of the loop. + * @returns The status of the iteration. + */ + private async _runIterationAsync(): Promise { + this._reset(); + + this._options.onBeforeExecute(); + try { + this._isRunning = true; + return await this._options.executeAsync(this); + } catch (err) { + if (!(err instanceof AlreadyReportedError)) { + throw err; + } else { + return OperationStatus.Failure; + } + } finally { + this._isRunning = false; + } + } +} diff --git a/libraries/operation-graph/src/WorkQueue.ts b/libraries/operation-graph/src/WorkQueue.ts new file mode 100644 index 00000000000..114eae665e8 --- /dev/null +++ b/libraries/operation-graph/src/WorkQueue.ts @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { Async, MinimumHeap } from '@rushstack/node-core-library'; + +import { OperationStatus } from './OperationStatus'; + +interface IQueueItem { + task: () => Promise; + priority: number; +} + +export class WorkQueue { + private readonly _queue: MinimumHeap; + private readonly _abortSignal: AbortSignal; + private readonly _abortPromise: Promise; + + private _pushPromise: Promise; + private _resolvePush: () => void; + private _resolvePushTimeout: NodeJS.Timeout | undefined; + + public constructor(abortSignal: AbortSignal) { + // Sort by priority descending. Thus the comparator returns a negative number if a has higher priority than b. + this._queue = new MinimumHeap((a: IQueueItem, b: IQueueItem) => b.priority - a.priority); + this._abortSignal = abortSignal; + this._abortPromise = abortSignal.aborted + ? Promise.resolve() + : new Promise((resolve) => { + abortSignal.addEventListener('abort', () => resolve(), { once: true }); + }); + + [this._pushPromise, this._resolvePush] = Async.getSignal(); + this._resolvePushTimeout = undefined; + } + + public async *[Symbol.asyncIterator](): AsyncIterableIterator<() => Promise> { + while (!this._abortSignal.aborted) { + while (this._queue.size > 0) { + const item: IQueueItem = this._queue.poll()!; + yield item.task; + } + + await Promise.race([this._pushPromise, this._abortPromise]); + } + } + + public pushAsync(task: () => Promise, priority: number): Promise { + return new Promise((resolve, reject) => { + this._queue.push({ + task: () => task().then(resolve, reject), + priority + }); + + // ESLINT: "Promises must be awaited, end with a call to .catch, end with a call to .then ..." + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this._abortPromise.finally(() => resolve(OperationStatus.Aborted)); + + this._resolvePushDebounced(); + }); + } + + private _resolvePushDebounced(): void { + if (!this._resolvePushTimeout) { + this._resolvePushTimeout = setTimeout(() => { + this._resolvePushTimeout = undefined; + this._resolvePush(); + + [this._pushPromise, this._resolvePush] = Async.getSignal(); + }); + } + } +} diff --git a/apps/heft/src/operations/calculateCriticalPath.ts b/libraries/operation-graph/src/calculateCriticalPath.ts similarity index 100% rename from apps/heft/src/operations/calculateCriticalPath.ts rename to libraries/operation-graph/src/calculateCriticalPath.ts diff --git a/libraries/operation-graph/src/index.ts b/libraries/operation-graph/src/index.ts new file mode 100644 index 00000000000..3debdfb5bb7 --- /dev/null +++ b/libraries/operation-graph/src/index.ts @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/// + +export type { + IOperationRunner, + IOperationRunnerContext, + IOperationState, + IOperationStates +} from './IOperationRunner'; + +export type { + IAfterExecuteEventMessage, + ISyncEventMessage, + IRequestRunEventMessage, + EventMessageFromClient, + ICancelCommandMessage, + IExitCommandMessage, + IRunCommandMessage, + ISyncCommandMessage, + CommandMessageFromHost, + IPCHost +} from './protocol.types'; + +export { + type IExecuteOperationContext, + type IOperationOptions, + Operation, + type OperationRequestRunCallback +} from './Operation'; + +export { OperationError } from './OperationError'; + +export { type IOperationExecutionOptions, OperationExecutionManager } from './OperationExecutionManager'; + +export { OperationGroupRecord } from './OperationGroupRecord'; + +export { OperationStatus } from './OperationStatus'; + +export { Stopwatch } from './Stopwatch'; + +export { type IWatchLoopOptions, type IWatchLoopState, WatchLoop } from './WatchLoop'; diff --git a/libraries/operation-graph/src/protocol.types.ts b/libraries/operation-graph/src/protocol.types.ts new file mode 100644 index 00000000000..f178ea84eb9 --- /dev/null +++ b/libraries/operation-graph/src/protocol.types.ts @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { OperationStatus } from './OperationStatus'; + +/** + * A message sent to the host to ask it to run this task. + * + * @beta + */ +export interface IRequestRunEventMessage { + event: 'requestRun'; + /** + * The name of the operation requesting a rerun. + */ + requestor: string; + /** + * Optional detail about why the rerun is requested, e.g. the name of a changed file. + */ + detail?: string; +} + +/** + * A message sent to the host upon completion of a run of this task. + * + * @beta + */ +export interface IAfterExecuteEventMessage { + event: 'after-execute'; + status: OperationStatus; +} + +/** + * A message sent to the host upon connection of the channel, to indicate + * to the host that this task supports the protocol and to provide baseline status information. + * + * @beta + */ +export interface ISyncEventMessage { + event: 'sync'; + status: OperationStatus; +} + +/** + * A message sent by the host to tell the watch loop to cancel the current run. + * + * @beta + */ +export interface ICancelCommandMessage { + command: 'cancel'; +} + +/** + * A message sent by the host to tell the watch loop to shutdown gracefully. + * + * @beta + */ +export interface IExitCommandMessage { + command: 'exit'; +} + +/** + * A message sent by the host to tell the watch loop to perform a single run. + * + * @beta + */ +export interface IRunCommandMessage { + command: 'run'; +} + +/** + * A message sent by the host to ask for to resync status information. + * + * @beta + */ +export interface ISyncCommandMessage { + command: 'sync'; +} + +/** + * The set of known messages from the host to the watch loop. + * @beta + */ +export type CommandMessageFromHost = + | ICancelCommandMessage + | IExitCommandMessage + | IRunCommandMessage + | ISyncCommandMessage; + +/** + * The set of known messages from the watch loop to the host. + * @beta + */ +export type EventMessageFromClient = IRequestRunEventMessage | IAfterExecuteEventMessage | ISyncEventMessage; + +/** + * The interface contract for IPC send/receive, to support alternate channels and unit tests. + * + * @beta + */ +export type IPCHost = Pick; diff --git a/libraries/operation-graph/src/test/OperationExecutionManager.test.ts b/libraries/operation-graph/src/test/OperationExecutionManager.test.ts new file mode 100644 index 00000000000..bdd635afde4 --- /dev/null +++ b/libraries/operation-graph/src/test/OperationExecutionManager.test.ts @@ -0,0 +1,439 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { type ITerminal, StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; +import { Operation } from '../Operation'; +import { OperationExecutionManager } from '../OperationExecutionManager'; +import { OperationStatus } from '../OperationStatus'; +import type { IOperationRunner, IOperationRunnerContext } from '../IOperationRunner'; +import { Async } from '@rushstack/node-core-library'; + +type ExecuteAsyncMock = jest.Mock< + ReturnType, + Parameters +>; + +describe(OperationExecutionManager.name, () => { + describe('constructor', () => { + it('handles empty input', () => { + const manager: OperationExecutionManager = new OperationExecutionManager(new Set()); + + expect(manager).toBeDefined(); + }); + + it('throws if a dependency is not in the set', () => { + const alpha: Operation = new Operation({ + name: 'alpha' + }); + const beta: Operation = new Operation({ + name: 'beta' + }); + + alpha.addDependency(beta); + + expect(() => { + return new OperationExecutionManager(new Set([alpha])); + }).toThrowErrorMatchingSnapshot(); + }); + + it('sets critical path lengths', () => { + const alpha: Operation = new Operation({ + name: 'alpha' + }); + const beta: Operation = new Operation({ + name: 'beta' + }); + + alpha.addDependency(beta); + + new OperationExecutionManager(new Set([alpha, beta])); + + expect(alpha.criticalPathLength).toBe(1); + expect(beta.criticalPathLength).toBe(2); + }); + }); + + describe(OperationExecutionManager.prototype.executeAsync.name, () => { + describe('single pass', () => { + it('handles empty input', async () => { + const manager: OperationExecutionManager = new OperationExecutionManager(new Set()); + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(false); + const terminal: ITerminal = new Terminal(terminalProvider); + + const result: OperationStatus = await manager.executeAsync({ + abortSignal: new AbortController().signal, + parallelism: 1, + terminal + }); + + expect(result).toBe(OperationStatus.NoOp); + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); + }); + + it('handles trivial input', async () => { + const operation: Operation = new Operation({ + name: 'alpha' + }); + const manager: OperationExecutionManager = new OperationExecutionManager(new Set([operation])); + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(false); + const terminal: ITerminal = new Terminal(terminalProvider); + + const result: OperationStatus = await manager.executeAsync({ + abortSignal: new AbortController().signal, + parallelism: 1, + terminal + }); + + expect(result).toBe(OperationStatus.Success); + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); + + expect(operation.state?.status).toBe(OperationStatus.NoOp); + }); + + it('executes in order', async () => { + const runAlpha: ExecuteAsyncMock = jest.fn(); + const runBeta: ExecuteAsyncMock = jest.fn(); + + const alpha: Operation = new Operation({ + name: 'alpha', + runner: { + name: 'alpha', + executeAsync: runAlpha, + silent: false + } + }); + const beta: Operation = new Operation({ + name: 'beta', + runner: { + name: 'beta', + executeAsync: runBeta, + silent: false + } + }); + beta.addDependency(alpha); + const manager: OperationExecutionManager = new OperationExecutionManager(new Set([alpha, beta])); + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(false); + const terminal: ITerminal = new Terminal(terminalProvider); + + runAlpha.mockImplementationOnce(async () => { + expect(runBeta).not.toHaveBeenCalled(); + return OperationStatus.Success; + }); + + runBeta.mockImplementationOnce(async () => { + expect(runAlpha).toHaveBeenCalledTimes(1); + return OperationStatus.Success; + }); + + const result: OperationStatus = await manager.executeAsync({ + abortSignal: new AbortController().signal, + parallelism: 1, + terminal + }); + + expect(result).toBe(OperationStatus.Success); + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); + + expect(runAlpha).toHaveBeenCalledTimes(1); + expect(runBeta).toHaveBeenCalledTimes(1); + + expect(alpha.state?.status).toBe(OperationStatus.Success); + expect(beta.state?.status).toBe(OperationStatus.Success); + }); + + it('blocks on failure', async () => { + const runAlpha: ExecuteAsyncMock = jest.fn(); + const runBeta: ExecuteAsyncMock = jest.fn(); + + const alpha: Operation = new Operation({ + name: 'alpha', + runner: { + name: 'alpha', + executeAsync: runAlpha, + silent: false + } + }); + const beta: Operation = new Operation({ + name: 'beta', + runner: { + name: 'beta', + executeAsync: runBeta, + silent: false + } + }); + beta.addDependency(alpha); + const manager: OperationExecutionManager = new OperationExecutionManager(new Set([alpha, beta])); + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(false); + const terminal: ITerminal = new Terminal(terminalProvider); + + runAlpha.mockImplementationOnce(async () => { + expect(runBeta).not.toHaveBeenCalled(); + return OperationStatus.Failure; + }); + + runBeta.mockImplementationOnce(async () => { + expect(runAlpha).toHaveBeenCalledTimes(1); + return OperationStatus.Success; + }); + + const result: OperationStatus = await manager.executeAsync({ + abortSignal: new AbortController().signal, + parallelism: 1, + terminal + }); + + expect(result).toBe(OperationStatus.Failure); + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); + expect(runAlpha).toHaveBeenCalledTimes(1); + expect(runBeta).toHaveBeenCalledTimes(0); + + expect(alpha.state?.status).toBe(OperationStatus.Failure); + expect(beta.state?.status).toBe(OperationStatus.Blocked); + }); + + it('does not track noops', async () => { + const operation: Operation = new Operation({ + name: 'alpha', + runner: { + name: 'alpha', + executeAsync(): Promise { + return Promise.resolve(OperationStatus.NoOp); + }, + silent: true + } + }); + const manager: OperationExecutionManager = new OperationExecutionManager(new Set([operation])); + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(false); + const terminal: ITerminal = new Terminal(terminalProvider); + + const result: OperationStatus = await manager.executeAsync({ + abortSignal: new AbortController().signal, + parallelism: 1, + terminal + }); + + expect(result).toBe(OperationStatus.NoOp); + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); + }); + + it('respects priority order', async () => { + const runAlpha: ExecuteAsyncMock = jest.fn(); + const runBeta: ExecuteAsyncMock = jest.fn(); + + const alpha: Operation = new Operation({ + name: 'alpha', + runner: { + name: 'alpha', + executeAsync: runAlpha, + silent: false + } + }); + const beta: Operation = new Operation({ + name: 'beta', + runner: { + name: 'beta', + executeAsync: runBeta, + silent: false + } + }); + const manager: OperationExecutionManager = new OperationExecutionManager(new Set([alpha, beta])); + + // Override default sort order. + alpha.criticalPathLength = 1; + beta.criticalPathLength = 2; + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(false); + const terminal: ITerminal = new Terminal(terminalProvider); + + const executed: Operation[] = []; + + runAlpha.mockImplementationOnce(async () => { + executed.push(alpha); + return OperationStatus.Success; + }); + + runBeta.mockImplementationOnce(async () => { + executed.push(beta); + return OperationStatus.Success; + }); + + const result: OperationStatus = await manager.executeAsync({ + abortSignal: new AbortController().signal, + parallelism: 1, + terminal + }); + + expect(executed).toEqual([beta, alpha]); + + expect(result).toBe(OperationStatus.Success); + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); + + expect(runAlpha).toHaveBeenCalledTimes(1); + expect(runBeta).toHaveBeenCalledTimes(1); + + expect(alpha.state?.status).toBe(OperationStatus.Success); + expect(beta.state?.status).toBe(OperationStatus.Success); + }); + + it('respects concurrency', async () => { + let concurrency: number = 0; + let maxConcurrency: number = 0; + + const run: ExecuteAsyncMock = jest.fn( + async (context: IOperationRunnerContext): Promise => { + ++concurrency; + await Async.sleepAsync(0); + if (concurrency > maxConcurrency) { + maxConcurrency = concurrency; + } + --concurrency; + return OperationStatus.Success; + } + ); + + const alpha: Operation = new Operation({ + name: 'alpha', + runner: { + name: 'alpha', + executeAsync: run, + silent: false + } + }); + const beta: Operation = new Operation({ + name: 'beta', + runner: { + name: 'beta', + executeAsync: run, + silent: false + } + }); + const manager: OperationExecutionManager = new OperationExecutionManager(new Set([alpha, beta])); + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(false); + const terminal: ITerminal = new Terminal(terminalProvider); + + const result: OperationStatus = await manager.executeAsync({ + abortSignal: new AbortController().signal, + parallelism: 2, + terminal + }); + + expect(result).toBe(OperationStatus.Success); + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); + + expect(run).toHaveBeenCalledTimes(2); + + expect(maxConcurrency).toBe(2); + + expect(alpha.state?.status).toBe(OperationStatus.Success); + expect(beta.state?.status).toBe(OperationStatus.Success); + }); + }); + + describe('watch mode', () => { + it('executes in order', async () => { + const runAlpha: ExecuteAsyncMock = jest.fn(); + const runBeta: ExecuteAsyncMock = jest.fn(); + + const requestRun: jest.Mock = jest.fn(); + + const alpha: Operation = new Operation({ + name: 'alpha', + runner: { + name: 'alpha', + executeAsync: runAlpha, + silent: false + } + }); + const beta: Operation = new Operation({ + name: 'beta', + runner: { + name: 'beta', + executeAsync: runBeta, + silent: false + } + }); + const executed: Operation[] = []; + beta.addDependency(alpha); + const manager: OperationExecutionManager = new OperationExecutionManager(new Set([alpha, beta])); + + const terminalProvider1: StringBufferTerminalProvider = new StringBufferTerminalProvider(false); + const terminal1: ITerminal = new Terminal(terminalProvider1); + + let betaRequestRun: IOperationRunnerContext['requestRun']; + + runAlpha.mockImplementationOnce(async () => { + executed.push(alpha); + return OperationStatus.Success; + }); + + runBeta.mockImplementationOnce(async (options) => { + executed.push(beta); + betaRequestRun = options.requestRun; + return OperationStatus.Success; + }); + + const result1: OperationStatus = await manager.executeAsync({ + abortSignal: new AbortController().signal, + parallelism: 1, + terminal: terminal1, + requestRun + }); + + expect(executed).toEqual([alpha, beta]); + + expect(requestRun).not.toHaveBeenCalled(); + expect(betaRequestRun).toBeDefined(); + + expect(result1).toBe(OperationStatus.Success); + expect(terminalProvider1.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot('first'); + + expect(runAlpha).toHaveBeenCalledTimes(1); + expect(runBeta).toHaveBeenCalledTimes(1); + + expect(alpha.state?.status).toBe(OperationStatus.Success); + expect(beta.state?.status).toBe(OperationStatus.Success); + + betaRequestRun!('why'); + + expect(requestRun).toHaveBeenCalledTimes(1); + expect(requestRun).toHaveBeenLastCalledWith(beta.name, 'why'); + + const terminalProvider2: StringBufferTerminalProvider = new StringBufferTerminalProvider(false); + const terminal2: ITerminal = new Terminal(terminalProvider2); + + runAlpha.mockImplementationOnce(async () => { + return OperationStatus.NoOp; + }); + + runBeta.mockImplementationOnce(async () => { + return OperationStatus.Success; + }); + + const result2: OperationStatus = await manager.executeAsync({ + abortSignal: new AbortController().signal, + parallelism: 1, + terminal: terminal2, + requestRun + }); + + expect(result2).toBe(OperationStatus.Success); + expect(terminalProvider2.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot('second'); + + expect(runAlpha).toHaveBeenCalledTimes(2); + expect(runBeta).toHaveBeenCalledTimes(2); + + expect(alpha.lastState?.status).toBe(OperationStatus.Success); + expect(beta.lastState?.status).toBe(OperationStatus.Success); + + expect(alpha.state?.status).toBe(OperationStatus.NoOp); + expect(beta.state?.status).toBe(OperationStatus.Success); + }); + }); + }); +}); diff --git a/libraries/operation-graph/src/test/WatchLoop.test.ts b/libraries/operation-graph/src/test/WatchLoop.test.ts new file mode 100644 index 00000000000..2a8c44b3ce3 --- /dev/null +++ b/libraries/operation-graph/src/test/WatchLoop.test.ts @@ -0,0 +1,319 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { AlreadyReportedError } from '@rushstack/node-core-library'; +import { OperationStatus } from '../OperationStatus'; +import { type IWatchLoopOptions, type IWatchLoopState, WatchLoop } from '../WatchLoop'; +import type { + CommandMessageFromHost, + EventMessageFromClient, + IAfterExecuteEventMessage, + IPCHost, + ISyncEventMessage +} from '../protocol.types'; + +type IMockOptions = { + [K in keyof IWatchLoopOptions]: jest.Mock< + ReturnType, + Parameters + >; +}; + +interface IMockOptionsAndWatchLoop { + watchLoop: WatchLoop; + mocks: IMockOptions; +} + +function createWatchLoop(): IMockOptionsAndWatchLoop { + const mocks = { + executeAsync: jest.fn(), + onBeforeExecute: jest.fn(), + onRequestRun: jest.fn(), + onAbort: jest.fn() + }; + + return { + watchLoop: new WatchLoop(mocks), + mocks + }; +} + +describe(WatchLoop.name, () => { + describe(WatchLoop.prototype.runUntilStableAsync.name, () => { + it('executes once when no run is requested', async () => { + const { + watchLoop, + mocks: { executeAsync, onBeforeExecute, onRequestRun, onAbort } + } = createWatchLoop(); + + const outerAbortController: AbortController = new AbortController(); + + await watchLoop.runUntilStableAsync(outerAbortController.signal); + + expect(onBeforeExecute).toHaveBeenCalledTimes(1); + expect(executeAsync).toHaveBeenCalledTimes(1); + expect(onRequestRun).toHaveBeenCalledTimes(0); + expect(onAbort).toHaveBeenCalledTimes(0); + }); + + it('will abort and re-execute if a run is requested while executing', async () => { + const { + watchLoop, + mocks: { executeAsync, onBeforeExecute, onRequestRun, onAbort } + } = createWatchLoop(); + + let iteration: number = 0; + const maxIterations: number = 5; + + const outerAbortController: AbortController = new AbortController(); + + executeAsync.mockImplementation(async (state: IWatchLoopState) => { + iteration++; + if (iteration < maxIterations) { + state.requestRun('test'); + return OperationStatus.Success; + } + return OperationStatus.NoOp; + }); + + expect(await watchLoop.runUntilStableAsync(outerAbortController.signal)).toEqual(OperationStatus.NoOp); + + expect(onBeforeExecute).toHaveBeenCalledTimes(maxIterations); + expect(executeAsync).toHaveBeenCalledTimes(maxIterations); + expect(onRequestRun).toHaveBeenCalledTimes(maxIterations - 1); + expect(onAbort).toHaveBeenCalledTimes(maxIterations - 1); + }); + + it('will abort if the outer signal is aborted', async () => { + const { + watchLoop, + mocks: { executeAsync, onBeforeExecute, onRequestRun, onAbort } + } = createWatchLoop(); + + let iteration: number = 0; + const cancelIterations: number = 3; + const maxIterations: number = 5; + + const outerAbortController: AbortController = new AbortController(); + + executeAsync.mockImplementation(async (state: IWatchLoopState) => { + iteration++; + if (iteration < maxIterations) { + state.requestRun('test', 'some detail'); + } + if (iteration === cancelIterations) { + outerAbortController.abort(); + } + return OperationStatus.Failure; + }); + + expect(await watchLoop.runUntilStableAsync(outerAbortController.signal)).toEqual( + OperationStatus.Aborted + ); + + expect(onBeforeExecute).toHaveBeenCalledTimes(cancelIterations); + expect(executeAsync).toHaveBeenCalledTimes(cancelIterations); + expect(onRequestRun).toHaveBeenCalledTimes(cancelIterations); + expect(onRequestRun).toHaveBeenLastCalledWith('test', 'some detail'); + expect(onAbort).toHaveBeenCalledTimes(cancelIterations); + }); + + it('will abort if an unhandled exception arises', async () => { + const { + watchLoop, + mocks: { executeAsync, onBeforeExecute, onRequestRun, onAbort } + } = createWatchLoop(); + + let iteration: number = 0; + const exceptionIterations: number = 3; + const maxIterations: number = 5; + + const outerAbortController: AbortController = new AbortController(); + + executeAsync.mockImplementation(async (state: IWatchLoopState) => { + iteration++; + if (iteration < maxIterations) { + state.requestRun('test', 'reason'); + } + if (iteration === exceptionIterations) { + throw new Error('fnord'); + } + return OperationStatus.Success; + }); + + await expect(() => watchLoop.runUntilStableAsync(outerAbortController.signal)).rejects.toThrow('fnord'); + + expect(onBeforeExecute).toHaveBeenCalledTimes(exceptionIterations); + expect(executeAsync).toHaveBeenCalledTimes(exceptionIterations); + expect(onRequestRun).toHaveBeenCalledTimes(exceptionIterations); + expect(onRequestRun).toHaveBeenLastCalledWith('test', 'reason'); + expect(onAbort).toHaveBeenCalledTimes(exceptionIterations); + }); + }); + + it('treats AlreadyReportedError as generic failure', async () => { + const { + watchLoop, + mocks: { executeAsync, onBeforeExecute, onRequestRun, onAbort } + } = createWatchLoop(); + + const outerAbortController: AbortController = new AbortController(); + + executeAsync.mockImplementation(async (state: IWatchLoopState) => { + throw new AlreadyReportedError(); + }); + + expect(await watchLoop.runUntilStableAsync(outerAbortController.signal)).toEqual(OperationStatus.Failure); + + expect(onBeforeExecute).toHaveBeenCalledTimes(1); + expect(executeAsync).toHaveBeenCalledTimes(1); + expect(onRequestRun).toHaveBeenCalledTimes(0); + expect(onAbort).toHaveBeenCalledTimes(0); + }); + + describe(WatchLoop.prototype.runUntilAbortedAsync.name, () => { + it('will abort if an unhandled exception arises', async () => { + const { + watchLoop, + mocks: { executeAsync, onBeforeExecute, onRequestRun, onAbort } + } = createWatchLoop(); + + let iteration: number = 0; + const exceptionIterations: number = 3; + const maxIterations: number = 5; + + const onWaiting: jest.Mock = jest.fn(); + + const outerAbortController: AbortController = new AbortController(); + + executeAsync.mockImplementation(async (state: IWatchLoopState) => { + iteration++; + if (iteration < maxIterations) { + state.requestRun('test', 'why'); + } + if (iteration === exceptionIterations) { + throw new Error('fnord'); + } + return OperationStatus.Success; + }); + + await expect(() => + watchLoop.runUntilAbortedAsync(outerAbortController.signal, onWaiting) + ).rejects.toThrow('fnord'); + + expect(onBeforeExecute).toHaveBeenCalledTimes(exceptionIterations); + expect(executeAsync).toHaveBeenCalledTimes(exceptionIterations); + expect(onRequestRun).toHaveBeenCalledTimes(exceptionIterations); + expect(onRequestRun).toHaveBeenLastCalledWith('test', 'why'); + expect(onAbort).toHaveBeenCalledTimes(exceptionIterations); + expect(onWaiting).toHaveBeenCalledTimes(0); + }); + + it('will wait if not immediately ready', async () => { + const { + watchLoop, + mocks: { executeAsync, onBeforeExecute, onRequestRun, onAbort } + } = createWatchLoop(); + + let iteration: number = 0; + const cancelIterations: number = 3; + const maxIterations: number = 5; + + const onWaiting: jest.Mock = jest.fn(); + const promises: Promise[] = []; + + const outerAbortController: AbortController = new AbortController(); + + executeAsync.mockImplementation(async (state: IWatchLoopState) => { + iteration++; + if (iteration < maxIterations) { + promises.push( + new Promise((resolve) => setTimeout(resolve, 0)).then(() => state.requestRun('test')) + ); + } + if (iteration === cancelIterations) { + outerAbortController.abort(); + } + return OperationStatus.Success; + }); + + await watchLoop.runUntilAbortedAsync(outerAbortController.signal, onWaiting); + + expect(onBeforeExecute).toHaveBeenCalledTimes(cancelIterations); + expect(executeAsync).toHaveBeenCalledTimes(cancelIterations); + expect(onRequestRun).toHaveBeenLastCalledWith('test', undefined); + + // Since the run finishes, no cancellation should occur + expect(onAbort).toHaveBeenCalledTimes(0); + expect(onWaiting).toHaveBeenCalledTimes(cancelIterations); + + // Canceling of the outer signal happens before requestRun on the final iteration + expect(onRequestRun).toHaveBeenCalledTimes(cancelIterations - 1); + + await Promise.all(promises); + // Final iteration async + expect(onRequestRun).toHaveBeenCalledTimes(cancelIterations); + }); + }); + + describe(WatchLoop.prototype.runIPCAsync.name, () => { + it('messsages the host with finished state', async () => { + const { + watchLoop, + mocks: { executeAsync, onBeforeExecute, onRequestRun, onAbort } + } = createWatchLoop(); + + const onMock: jest.Mock = jest.fn(); + const sendMock: jest.Mock = jest.fn(); + + let messageHandler: ((message: CommandMessageFromHost) => void) | undefined; + + onMock.mockImplementationOnce((event: string, handler: (message: CommandMessageFromHost) => void) => { + if (event !== 'message') { + throw new Error(`Unexpected event type: ${event}`); + } + messageHandler = handler; + }); + + sendMock.mockImplementation((message: EventMessageFromClient) => { + if (message.event === 'sync') { + process.nextTick(() => messageHandler!({ command: 'run' })); + } else { + process.nextTick(() => messageHandler!({ command: 'exit' })); + } + }); + + const ipcHost: IPCHost = { + on: onMock, + send: sendMock + }; + + executeAsync.mockImplementation(async (state: IWatchLoopState) => { + return OperationStatus.Success; + }); + + await watchLoop.runIPCAsync(ipcHost); + + expect(onBeforeExecute).toHaveBeenCalledTimes(1); + expect(executeAsync).toHaveBeenCalledTimes(1); + expect(onRequestRun).toHaveBeenCalledTimes(0); + expect(onAbort).toHaveBeenCalledTimes(0); + + expect(onMock).toHaveBeenCalledTimes(1); + expect(sendMock).toHaveBeenCalledTimes(2); + + const syncMessage: ISyncEventMessage = { + event: 'sync', + status: OperationStatus.Ready + }; + + const successMessage: IAfterExecuteEventMessage = { + event: 'after-execute', + status: OperationStatus.Success + }; + + expect(sendMock).toHaveBeenCalledWith(syncMessage); + expect(sendMock).toHaveBeenLastCalledWith(successMessage); + }); + }); +}); diff --git a/libraries/operation-graph/src/test/WorkQueue.test.ts b/libraries/operation-graph/src/test/WorkQueue.test.ts new file mode 100644 index 00000000000..745f71ff509 --- /dev/null +++ b/libraries/operation-graph/src/test/WorkQueue.test.ts @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { Async } from '@rushstack/node-core-library'; +import { OperationStatus } from '../OperationStatus'; +import { WorkQueue } from '../WorkQueue'; + +describe(WorkQueue.name, () => { + it('Executes in dependency order', async () => { + const abortController: AbortController = new AbortController(); + + const queue: WorkQueue = new WorkQueue(abortController.signal); + + const executed: Set = new Set(); + + const outerPromises: Promise[] = []; + + for (let i: number = 0; i < 10; i++) { + outerPromises.push( + queue.pushAsync(async () => { + executed.add(i); + return OperationStatus.Success; + }, i) + ); + } + + let expectedCount: number = 0; + const queuePromise: Promise = (async () => { + for await (const task of queue) { + expect(executed.size).toBe(expectedCount); + await task(); + ++expectedCount; + expect(executed.has(outerPromises.length - expectedCount)).toBe(true); + } + })(); + + expect((await Promise.all(outerPromises)).every((status) => status === OperationStatus.Success)).toBe( + true + ); + abortController.abort(); + await queuePromise; + }); + + it('Aborts any tasks left on the queue when aborted', async () => { + const abortController: AbortController = new AbortController(); + + const queue: WorkQueue = new WorkQueue(abortController.signal); + + const executed: Set = new Set(); + + const outerPromises: Promise[] = []; + + for (let i: number = 0; i < 10; i++) { + outerPromises.push( + queue.pushAsync(async () => { + executed.add(i); + return OperationStatus.Success; + }, i) + ); + } + + let expectedCount: number = 0; + for await (const task of queue) { + expect(executed.size).toBe(expectedCount); + await task(); + ++expectedCount; + expect(executed.has(outerPromises.length - expectedCount)).toBe(true); + + if (expectedCount === 1) { + abortController.abort(); + } + } + + const results: OperationStatus[] = await Promise.all(outerPromises); + // The last pushed operation had the highest priority, so is the only one executed before the abort call + expect(results.pop()).toBe(OperationStatus.Success); + for (const result of results) { + expect(result).toBe(OperationStatus.Aborted); + } + }); + + it('works with Async.forEachAsync', async () => { + const abortController: AbortController = new AbortController(); + + const queue: WorkQueue = new WorkQueue(abortController.signal); + + const executed: Set = new Set(); + + const outerPromises: Promise[] = []; + + for (let i: number = 0; i < 10; i++) { + outerPromises.push( + queue.pushAsync(async () => { + executed.add(i); + return OperationStatus.Success; + }, i) + ); + } + + let expectedCount: number = 0; + const queuePromise: Promise = Async.forEachAsync( + queue, + async (task) => { + expect(executed.size).toBe(expectedCount); + await task(); + ++expectedCount; + expect(executed.has(outerPromises.length - expectedCount)).toBe(true); + }, + { concurrency: 1 } + ); + + expect((await Promise.all(outerPromises)).every((status) => status === OperationStatus.Success)).toBe( + true + ); + abortController.abort(); + await queuePromise; + }); + + it('works concurrently with Async.forEachAsync', async () => { + const abortController: AbortController = new AbortController(); + + const queue: WorkQueue = new WorkQueue(abortController.signal); + + let running: number = 0; + let maxRunning: number = 0; + + const array: number[] = [1, 2, 3, 4, 5, 6, 7, 8]; + + const fn: () => Promise = jest.fn(async () => { + running++; + await Async.sleepAsync(0); + maxRunning = Math.max(maxRunning, running); + running--; + return OperationStatus.Success; + }); + const outerPromises: Promise[] = array.map((index) => queue.pushAsync(fn, 0)); + + const queuePromise: Promise = Async.forEachAsync(queue, (task) => task(), { concurrency: 3 }); + expect((await Promise.all(outerPromises)).every((status) => status === OperationStatus.Success)).toBe( + true + ); + + abortController.abort(); + await queuePromise; + + expect(fn).toHaveBeenCalledTimes(8); + expect(maxRunning).toEqual(3); + }); +}); diff --git a/libraries/operation-graph/src/test/__snapshots__/OperationExecutionManager.test.ts.snap b/libraries/operation-graph/src/test/__snapshots__/OperationExecutionManager.test.ts.snap new file mode 100644 index 00000000000..d9b554f70a7 --- /dev/null +++ b/libraries/operation-graph/src/test/__snapshots__/OperationExecutionManager.test.ts.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`OperationExecutionManager constructor throws if a dependency is not in the set 1`] = `"Operation \\"alpha\\" declares a dependency on operation \\"beta\\" that is not in the set of operations to execute."`; + +exports[`OperationExecutionManager executeAsync single pass blocks on failure 1`] = ` +Array [ + "[verbose] Executing a maximum of 1 simultaneous tasks...[n]", +] +`; + +exports[`OperationExecutionManager executeAsync single pass does not track noops 1`] = ` +Array [ + "[verbose] Executing a maximum of 1 simultaneous tasks...[n]", +] +`; + +exports[`OperationExecutionManager executeAsync single pass executes in order 1`] = ` +Array [ + "[verbose] Executing a maximum of 1 simultaneous tasks...[n]", +] +`; + +exports[`OperationExecutionManager executeAsync single pass handles empty input 1`] = ` +Array [ + "[verbose] Executing a maximum of 0 simultaneous tasks...[n]", +] +`; + +exports[`OperationExecutionManager executeAsync single pass handles trivial input 1`] = ` +Array [ + "[verbose] Executing a maximum of 1 simultaneous tasks...[n]", +] +`; + +exports[`OperationExecutionManager executeAsync single pass respects concurrency 1`] = ` +Array [ + "[verbose] Executing a maximum of 2 simultaneous tasks...[n]", +] +`; + +exports[`OperationExecutionManager executeAsync single pass respects priority order 1`] = ` +Array [ + "[verbose] Executing a maximum of 1 simultaneous tasks...[n]", +] +`; + +exports[`OperationExecutionManager executeAsync watch mode executes in order: first 1`] = ` +Array [ + "[verbose] Executing a maximum of 1 simultaneous tasks...[n]", +] +`; + +exports[`OperationExecutionManager executeAsync watch mode executes in order: second 1`] = ` +Array [ + "[verbose] Executing a maximum of 1 simultaneous tasks...[n]", +] +`; diff --git a/apps/heft/src/operations/__snapshots__/calculateCriticalPath.test.ts.snap b/libraries/operation-graph/src/test/__snapshots__/calculateCriticalPath.test.ts.snap similarity index 94% rename from apps/heft/src/operations/__snapshots__/calculateCriticalPath.test.ts.snap rename to libraries/operation-graph/src/test/__snapshots__/calculateCriticalPath.test.ts.snap index 8b2f076390a..333973daec9 100644 --- a/apps/heft/src/operations/__snapshots__/calculateCriticalPath.test.ts.snap +++ b/libraries/operation-graph/src/test/__snapshots__/calculateCriticalPath.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`calculateCriticalPathLength reports circularities 1`] = ` "A cyclic dependency was encountered: diff --git a/apps/heft/src/operations/calculateCriticalPath.test.ts b/libraries/operation-graph/src/test/calculateCriticalPath.test.ts similarity index 99% rename from apps/heft/src/operations/calculateCriticalPath.test.ts rename to libraries/operation-graph/src/test/calculateCriticalPath.test.ts index 4431d4d082d..bc45ffb6cea 100644 --- a/apps/heft/src/operations/calculateCriticalPath.test.ts +++ b/libraries/operation-graph/src/test/calculateCriticalPath.test.ts @@ -6,7 +6,7 @@ import { calculateCriticalPathLength, calculateCriticalPathLengths, type ISortableOperation -} from './calculateCriticalPath'; +} from '../calculateCriticalPath'; interface ITestOperation extends ISortableOperation { // Nothing added, just need an interface to solve the infinite expansion. diff --git a/libraries/operation-graph/tsconfig.json b/libraries/operation-graph/tsconfig.json new file mode 100644 index 00000000000..9f4aaf6de01 --- /dev/null +++ b/libraries/operation-graph/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json", + + "compilerOptions": { + // TODO: Consider turning this on for all projects, or removing it here + "allowSyntheticDefaultImports": true, + // TODO: update the rest of the repo to use ES2020 + "target": "ES2020", + "lib": ["ES2020"] + } +} diff --git a/libraries/package-deps-hash/.eslintrc.js b/libraries/package-deps-hash/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/libraries/package-deps-hash/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/libraries/package-deps-hash/.npmignore b/libraries/package-deps-hash/.npmignore index 302dbc5b019..f7a40e10213 100644 --- a/libraries/package-deps-hash/.npmignore +++ b/libraries/package-deps-hash/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,16 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/package-deps-hash/CHANGELOG.json b/libraries/package-deps-hash/CHANGELOG.json index cccc95230dc..8f77f2715a6 100644 --- a/libraries/package-deps-hash/CHANGELOG.json +++ b/libraries/package-deps-hash/CHANGELOG.json @@ -1,6 +1,2542 @@ { "name": "@rushstack/package-deps-hash", "entries": [ + { + "version": "4.7.24", + "tag": "@rushstack/package-deps-hash_v4.7.24", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "4.7.23", + "tag": "@rushstack/package-deps-hash_v4.7.23", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "4.7.22", + "tag": "@rushstack/package-deps-hash_v4.7.22", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "4.7.21", + "tag": "@rushstack/package-deps-hash_v4.7.21", + "date": "Mon, 15 Jun 2026 15:15:33 GMT", + "comments": { + "patch": [ + { + "comment": "Strip GIT_DIR and GIT_WORK_TREE Node env variables to fix issues with miscalculating the git repo root when working in a linked worktree" + } + ] + } + }, + { + "version": "4.7.20", + "tag": "@rushstack/package-deps-hash_v4.7.20", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "4.7.19", + "tag": "@rushstack/package-deps-hash_v4.7.19", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "4.7.18", + "tag": "@rushstack/package-deps-hash_v4.7.18", + "date": "Mon, 25 May 2026 15:14:32 GMT", + "comments": { + "patch": [ + { + "comment": "Skip untracked files whose basename is a Windows reserved device name (e.g. `nul`, `con`, `aux`, `com1`-`com9`, `lpt1`-`lpt9`) when computing repo state on Windows. `git hash-object` cannot open such paths and otherwise aborts the entire repo-state calculation." + } + ] + } + }, + { + "version": "4.7.17", + "tag": "@rushstack/package-deps-hash_v4.7.17", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "4.7.16", + "tag": "@rushstack/package-deps-hash_v4.7.16", + "date": "Mon, 20 Apr 2026 15:15:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "4.7.15", + "tag": "@rushstack/package-deps-hash_v4.7.15", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "4.7.14", + "tag": "@rushstack/package-deps-hash_v4.7.14", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "4.7.13", + "tag": "@rushstack/package-deps-hash_v4.7.13", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "4.7.12", + "tag": "@rushstack/package-deps-hash_v4.7.12", + "date": "Fri, 10 Apr 2026 22:46:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "4.7.11", + "tag": "@rushstack/package-deps-hash_v4.7.11", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "4.7.10", + "tag": "@rushstack/package-deps-hash_v4.7.10", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "4.7.9", + "tag": "@rushstack/package-deps-hash_v4.7.9", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "4.7.8", + "tag": "@rushstack/package-deps-hash_v4.7.8", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "4.7.7", + "tag": "@rushstack/package-deps-hash_v4.7.7", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "4.7.6", + "tag": "@rushstack/package-deps-hash_v4.7.6", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "4.7.5", + "tag": "@rushstack/package-deps-hash_v4.7.5", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "4.7.4", + "tag": "@rushstack/package-deps-hash_v4.7.4", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "4.7.3", + "tag": "@rushstack/package-deps-hash_v4.7.3", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "4.7.2", + "tag": "@rushstack/package-deps-hash_v4.7.2", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "4.7.1", + "tag": "@rushstack/package-deps-hash_v4.7.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "4.7.0", + "tag": "@rushstack/package-deps-hash_v4.7.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "4.6.8", + "tag": "@rushstack/package-deps-hash_v4.6.8", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "4.6.7", + "tag": "@rushstack/package-deps-hash_v4.6.7", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "4.6.6", + "tag": "@rushstack/package-deps-hash_v4.6.6", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "4.6.5", + "tag": "@rushstack/package-deps-hash_v4.6.5", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "4.6.4", + "tag": "@rushstack/package-deps-hash_v4.6.4", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "4.6.3", + "tag": "@rushstack/package-deps-hash_v4.6.3", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "4.6.2", + "tag": "@rushstack/package-deps-hash_v4.6.2", + "date": "Mon, 05 Jan 2026 16:12:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "4.6.1", + "tag": "@rushstack/package-deps-hash_v4.6.1", + "date": "Mon, 29 Dec 2025 22:42:58 GMT", + "comments": { + "patch": [ + { + "comment": "Update MINIMUM_GIT_VERSION to 2.35.0 to account for usage of the --format argument with git ls-files" + } + ] + } + }, + { + "version": "4.6.0", + "tag": "@rushstack/package-deps-hash_v4.6.0", + "date": "Fri, 12 Dec 2025 01:12:05 GMT", + "comments": { + "minor": [ + { + "comment": "Replace \"git ls-tree\" with \"git ls-files\" to improve performance. Identify symbolic links and return them separately in \"getDetailedRepoStateAsync\". Symbolic links will be omitted from the result returned by \"getRepoStateAsync\", as they are not \"files\"." + } + ] + } + }, + { + "version": "4.5.7", + "tag": "@rushstack/package-deps-hash_v4.5.7", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + } + ] + } + }, + { + "version": "4.5.6", + "tag": "@rushstack/package-deps-hash_v4.5.6", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + } + ] + } + }, + { + "version": "4.5.5", + "tag": "@rushstack/package-deps-hash_v4.5.5", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + } + ] + } + }, + { + "version": "4.5.4", + "tag": "@rushstack/package-deps-hash_v4.5.4", + "date": "Tue, 04 Nov 2025 08:15:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + } + ] + } + }, + { + "version": "4.5.3", + "tag": "@rushstack/package-deps-hash_v4.5.3", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + } + ] + } + }, + { + "version": "4.5.2", + "tag": "@rushstack/package-deps-hash_v4.5.2", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + } + ] + } + }, + { + "version": "4.5.1", + "tag": "@rushstack/package-deps-hash_v4.5.1", + "date": "Wed, 08 Oct 2025 00:13:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + } + ] + } + }, + { + "version": "4.5.0", + "tag": "@rushstack/package-deps-hash_v4.5.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + } + ] + } + }, + { + "version": "4.4.9", + "tag": "@rushstack/package-deps-hash_v4.4.9", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + } + ] + } + }, + { + "version": "4.4.8", + "tag": "@rushstack/package-deps-hash_v4.4.8", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + } + ] + } + }, + { + "version": "4.4.7", + "tag": "@rushstack/package-deps-hash_v4.4.7", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + } + ] + } + }, + { + "version": "4.4.6", + "tag": "@rushstack/package-deps-hash_v4.4.6", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + } + ] + } + }, + { + "version": "4.4.5", + "tag": "@rushstack/package-deps-hash_v4.4.5", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + } + ] + } + }, + { + "version": "4.4.4", + "tag": "@rushstack/package-deps-hash_v4.4.4", + "date": "Fri, 01 Aug 2025 00:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + } + ] + } + }, + { + "version": "4.4.3", + "tag": "@rushstack/package-deps-hash_v4.4.3", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + } + ] + } + }, + { + "version": "4.4.2", + "tag": "@rushstack/package-deps-hash_v4.4.2", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + } + ] + } + }, + { + "version": "4.4.1", + "tag": "@rushstack/package-deps-hash_v4.4.1", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + } + ] + } + }, + { + "version": "4.4.0", + "tag": "@rushstack/package-deps-hash_v4.4.0", + "date": "Thu, 08 May 2025 00:11:15 GMT", + "comments": { + "minor": [ + { + "comment": "Add `getDetailedRepoState` API to expose `hasSubmodules` and `hasUncommittedChanges` in addition to the results returned by `getRepoState`." + } + ] + } + }, + { + "version": "4.3.24", + "tag": "@rushstack/package-deps-hash_v4.3.24", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + } + ] + } + }, + { + "version": "4.3.23", + "tag": "@rushstack/package-deps-hash_v4.3.23", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + } + ] + } + }, + { + "version": "4.3.22", + "tag": "@rushstack/package-deps-hash_v4.3.22", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + } + ] + } + }, + { + "version": "4.3.21", + "tag": "@rushstack/package-deps-hash_v4.3.21", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + } + ] + } + }, + { + "version": "4.3.20", + "tag": "@rushstack/package-deps-hash_v4.3.20", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + } + ] + } + }, + { + "version": "4.3.19", + "tag": "@rushstack/package-deps-hash_v4.3.19", + "date": "Tue, 15 Apr 2025 15:11:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + } + ] + } + }, + { + "version": "4.3.18", + "tag": "@rushstack/package-deps-hash_v4.3.18", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + } + ] + } + }, + { + "version": "4.3.17", + "tag": "@rushstack/package-deps-hash_v4.3.17", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + } + ] + } + }, + { + "version": "4.3.16", + "tag": "@rushstack/package-deps-hash_v4.3.16", + "date": "Tue, 25 Mar 2025 15:11:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + } + ] + } + }, + { + "version": "4.3.15", + "tag": "@rushstack/package-deps-hash_v4.3.15", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + } + ] + } + }, + { + "version": "4.3.14", + "tag": "@rushstack/package-deps-hash_v4.3.14", + "date": "Wed, 12 Mar 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + } + ] + } + }, + { + "version": "4.3.13", + "tag": "@rushstack/package-deps-hash_v4.3.13", + "date": "Tue, 11 Mar 2025 02:12:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + } + ] + } + }, + { + "version": "4.3.12", + "tag": "@rushstack/package-deps-hash_v4.3.12", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + } + ] + } + }, + { + "version": "4.3.11", + "tag": "@rushstack/package-deps-hash_v4.3.11", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + } + ] + } + }, + { + "version": "4.3.10", + "tag": "@rushstack/package-deps-hash_v4.3.10", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + } + ] + } + }, + { + "version": "4.3.9", + "tag": "@rushstack/package-deps-hash_v4.3.9", + "date": "Wed, 26 Feb 2025 16:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + } + ] + } + }, + { + "version": "4.3.8", + "tag": "@rushstack/package-deps-hash_v4.3.8", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + } + ] + } + }, + { + "version": "4.3.7", + "tag": "@rushstack/package-deps-hash_v4.3.7", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + } + ] + } + }, + { + "version": "4.3.6", + "tag": "@rushstack/package-deps-hash_v4.3.6", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + } + ] + } + }, + { + "version": "4.3.5", + "tag": "@rushstack/package-deps-hash_v4.3.5", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + } + ] + } + }, + { + "version": "4.3.4", + "tag": "@rushstack/package-deps-hash_v4.3.4", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + } + ] + } + }, + { + "version": "4.3.3", + "tag": "@rushstack/package-deps-hash_v4.3.3", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + } + ] + } + }, + { + "version": "4.3.2", + "tag": "@rushstack/package-deps-hash_v4.3.2", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + } + ] + } + }, + { + "version": "4.3.1", + "tag": "@rushstack/package-deps-hash_v4.3.1", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + } + ] + } + }, + { + "version": "4.3.0", + "tag": "@rushstack/package-deps-hash_v4.3.0", + "date": "Thu, 12 Dec 2024 01:37:09 GMT", + "comments": { + "minor": [ + { + "comment": "Add a new optional parameter `filterPath` to `getRepoStateAsync` that limits the scope of the git query to only the specified subpaths. This can significantly improve the performance of the function when only part of the full repo data is necessary." + } + ] + } + }, + { + "version": "4.2.11", + "tag": "@rushstack/package-deps-hash_v4.2.11", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + } + ] + } + }, + { + "version": "4.2.10", + "tag": "@rushstack/package-deps-hash_v4.2.10", + "date": "Tue, 03 Dec 2024 16:11:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + } + ] + } + }, + { + "version": "4.2.9", + "tag": "@rushstack/package-deps-hash_v4.2.9", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + } + ] + } + }, + { + "version": "4.2.8", + "tag": "@rushstack/package-deps-hash_v4.2.8", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + } + ] + } + }, + { + "version": "4.2.7", + "tag": "@rushstack/package-deps-hash_v4.2.7", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + } + ] + } + }, + { + "version": "4.2.6", + "tag": "@rushstack/package-deps-hash_v4.2.6", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + } + ] + } + }, + { + "version": "4.2.5", + "tag": "@rushstack/package-deps-hash_v4.2.5", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + } + ] + } + }, + { + "version": "4.2.4", + "tag": "@rushstack/package-deps-hash_v4.2.4", + "date": "Tue, 15 Oct 2024 00:12:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + } + ] + } + }, + { + "version": "4.2.3", + "tag": "@rushstack/package-deps-hash_v4.2.3", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + } + ] + } + }, + { + "version": "4.2.2", + "tag": "@rushstack/package-deps-hash_v4.2.2", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + } + ] + } + }, + { + "version": "4.2.1", + "tag": "@rushstack/package-deps-hash_v4.2.1", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + } + ] + } + }, + { + "version": "4.2.0", + "tag": "@rushstack/package-deps-hash_v4.2.0", + "date": "Sat, 21 Sep 2024 00:10:27 GMT", + "comments": { + "minor": [ + { + "comment": "Expose `hashFilesAsync` API. This serves a similar role as `getGitHashForFiles` but is asynchronous and allows for the file names to be provided as an async iterable." + } + ] + } + }, + { + "version": "4.1.68", + "tag": "@rushstack/package-deps-hash_v4.1.68", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + } + ] + } + }, + { + "version": "4.1.67", + "tag": "@rushstack/package-deps-hash_v4.1.67", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + } + ] + } + }, + { + "version": "4.1.66", + "tag": "@rushstack/package-deps-hash_v4.1.66", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + } + ] + } + }, + { + "version": "4.1.65", + "tag": "@rushstack/package-deps-hash_v4.1.65", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + } + ] + } + }, + { + "version": "4.1.64", + "tag": "@rushstack/package-deps-hash_v4.1.64", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + } + ] + } + }, + { + "version": "4.1.63", + "tag": "@rushstack/package-deps-hash_v4.1.63", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + } + ] + } + }, + { + "version": "4.1.62", + "tag": "@rushstack/package-deps-hash_v4.1.62", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + } + ] + } + }, + { + "version": "4.1.61", + "tag": "@rushstack/package-deps-hash_v4.1.61", + "date": "Wed, 17 Jul 2024 06:55:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + } + ] + } + }, + { + "version": "4.1.60", + "tag": "@rushstack/package-deps-hash_v4.1.60", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + } + ] + } + }, + { + "version": "4.1.59", + "tag": "@rushstack/package-deps-hash_v4.1.59", + "date": "Tue, 16 Jul 2024 00:36:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + } + ] + } + }, + { + "version": "4.1.58", + "tag": "@rushstack/package-deps-hash_v4.1.58", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + } + ] + } + }, + { + "version": "4.1.57", + "tag": "@rushstack/package-deps-hash_v4.1.57", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + } + ] + } + }, + { + "version": "4.1.56", + "tag": "@rushstack/package-deps-hash_v4.1.56", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "patch": [ + { + "comment": "Include missing `type` modifiers on type-only exports." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + } + ] + } + }, + { + "version": "4.1.55", + "tag": "@rushstack/package-deps-hash_v4.1.55", + "date": "Wed, 29 May 2024 02:03:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + } + ] + } + }, + { + "version": "4.1.54", + "tag": "@rushstack/package-deps-hash_v4.1.54", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + } + ] + } + }, + { + "version": "4.1.53", + "tag": "@rushstack/package-deps-hash_v4.1.53", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + } + ] + } + }, + { + "version": "4.1.52", + "tag": "@rushstack/package-deps-hash_v4.1.52", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + } + ] + } + }, + { + "version": "4.1.51", + "tag": "@rushstack/package-deps-hash_v4.1.51", + "date": "Sat, 25 May 2024 04:54:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + } + ] + } + }, + { + "version": "4.1.50", + "tag": "@rushstack/package-deps-hash_v4.1.50", + "date": "Fri, 24 May 2024 00:15:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + } + ] + } + }, + { + "version": "4.1.49", + "tag": "@rushstack/package-deps-hash_v4.1.49", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "patch": [ + { + "comment": "Add a newline to an error message" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + } + ] + } + }, + { + "version": "4.1.48", + "tag": "@rushstack/package-deps-hash_v4.1.48", + "date": "Fri, 17 May 2024 00:10:40 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where an incomplete repo state analysis was sometimes returned, especially on WSL. See https://github.com/microsoft/rushstack/pull/4711 for details." + } + ] + } + }, + { + "version": "4.1.47", + "tag": "@rushstack/package-deps-hash_v4.1.47", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + } + ] + } + }, + { + "version": "4.1.46", + "tag": "@rushstack/package-deps-hash_v4.1.46", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + } + ] + } + }, + { + "version": "4.1.45", + "tag": "@rushstack/package-deps-hash_v4.1.45", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + } + ] + } + }, + { + "version": "4.1.44", + "tag": "@rushstack/package-deps-hash_v4.1.44", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + } + ] + } + }, + { + "version": "4.1.43", + "tag": "@rushstack/package-deps-hash_v4.1.43", + "date": "Wed, 08 May 2024 22:23:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + } + ] + } + }, + { + "version": "4.1.42", + "tag": "@rushstack/package-deps-hash_v4.1.42", + "date": "Mon, 06 May 2024 15:11:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + } + ] + } + }, + { + "version": "4.1.41", + "tag": "@rushstack/package-deps-hash_v4.1.41", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + } + ] + } + }, + { + "version": "4.1.40", + "tag": "@rushstack/package-deps-hash_v4.1.40", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + } + ] + } + }, + { + "version": "4.1.39", + "tag": "@rushstack/package-deps-hash_v4.1.39", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + } + ] + } + }, + { + "version": "4.1.38", + "tag": "@rushstack/package-deps-hash_v4.1.38", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + } + ] + } + }, + { + "version": "4.1.37", + "tag": "@rushstack/package-deps-hash_v4.1.37", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + } + ] + } + }, + { + "version": "4.1.36", + "tag": "@rushstack/package-deps-hash_v4.1.36", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + } + ] + } + }, + { + "version": "4.1.35", + "tag": "@rushstack/package-deps-hash_v4.1.35", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + } + ] + } + }, + { + "version": "4.1.34", + "tag": "@rushstack/package-deps-hash_v4.1.34", + "date": "Thu, 29 Feb 2024 07:11:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + } + ] + } + }, + { + "version": "4.1.33", + "tag": "@rushstack/package-deps-hash_v4.1.33", + "date": "Wed, 28 Feb 2024 16:09:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + } + ] + } + }, + { + "version": "4.1.32", + "tag": "@rushstack/package-deps-hash_v4.1.32", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + } + ] + } + }, + { + "version": "4.1.31", + "tag": "@rushstack/package-deps-hash_v4.1.31", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + } + ] + } + }, + { + "version": "4.1.30", + "tag": "@rushstack/package-deps-hash_v4.1.30", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + } + ] + } + }, + { + "version": "4.1.29", + "tag": "@rushstack/package-deps-hash_v4.1.29", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + } + ] + } + }, + { + "version": "4.1.28", + "tag": "@rushstack/package-deps-hash_v4.1.28", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + } + ] + } + }, + { + "version": "4.1.27", + "tag": "@rushstack/package-deps-hash_v4.1.27", + "date": "Tue, 20 Feb 2024 16:10:53 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + } + ] + } + }, + { + "version": "4.1.26", + "tag": "@rushstack/package-deps-hash_v4.1.26", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "patch": [ + { + "comment": "Fix a formatting issue with the LICENSE." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + } + ] + } + }, + { + "version": "4.1.25", + "tag": "@rushstack/package-deps-hash_v4.1.25", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix broken link to API documentation" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + } + ] + } + }, + { + "version": "4.1.24", + "tag": "@rushstack/package-deps-hash_v4.1.24", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + } + ] + } + }, + { + "version": "4.1.23", + "tag": "@rushstack/package-deps-hash_v4.1.23", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + } + ] + } + }, + { + "version": "4.1.22", + "tag": "@rushstack/package-deps-hash_v4.1.22", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + } + ] + } + }, + { + "version": "4.1.21", + "tag": "@rushstack/package-deps-hash_v4.1.21", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + } + ] + } + }, + { + "version": "4.1.20", + "tag": "@rushstack/package-deps-hash_v4.1.20", + "date": "Tue, 23 Jan 2024 20:12:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + } + ] + } + }, + { + "version": "4.1.19", + "tag": "@rushstack/package-deps-hash_v4.1.19", + "date": "Tue, 23 Jan 2024 16:15:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + } + ] + } + }, + { + "version": "4.1.18", + "tag": "@rushstack/package-deps-hash_v4.1.18", + "date": "Thu, 18 Jan 2024 01:08:53 GMT", + "comments": { + "patch": [ + { + "comment": "Handle an edge case in `getRepoState` wherein it tries to asynchronously pipe data to `git hash-object` but the subprocess has already exited." + } + ] + } + }, + { + "version": "4.1.17", + "tag": "@rushstack/package-deps-hash_v4.1.17", + "date": "Tue, 16 Jan 2024 18:30:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + } + ] + } + }, + { + "version": "4.1.16", + "tag": "@rushstack/package-deps-hash_v4.1.16", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + } + ] + } + }, + { + "version": "4.1.15", + "tag": "@rushstack/package-deps-hash_v4.1.15", + "date": "Wed, 20 Dec 2023 01:09:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + } + ] + } + }, + { + "version": "4.1.14", + "tag": "@rushstack/package-deps-hash_v4.1.14", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + } + ] + } + }, + { + "version": "4.1.13", + "tag": "@rushstack/package-deps-hash_v4.1.13", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + } + ] + } + }, + { + "version": "4.1.12", + "tag": "@rushstack/package-deps-hash_v4.1.12", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + } + ] + } + }, + { + "version": "4.1.11", + "tag": "@rushstack/package-deps-hash_v4.1.11", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + } + ] + } + }, + { + "version": "4.1.10", + "tag": "@rushstack/package-deps-hash_v4.1.10", + "date": "Mon, 30 Oct 2023 23:36:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + } + ] + } + }, + { + "version": "4.1.9", + "tag": "@rushstack/package-deps-hash_v4.1.9", + "date": "Sun, 01 Oct 2023 02:56:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + } + ] + } + }, + { + "version": "4.1.8", + "tag": "@rushstack/package-deps-hash_v4.1.8", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + } + ] + } + }, + { + "version": "4.1.7", + "tag": "@rushstack/package-deps-hash_v4.1.7", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + } + ] + } + }, + { + "version": "4.1.6", + "tag": "@rushstack/package-deps-hash_v4.1.6", + "date": "Wed, 27 Sep 2023 00:21:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + } + ] + } + }, + { + "version": "4.1.5", + "tag": "@rushstack/package-deps-hash_v4.1.5", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + } + ] + } + }, + { + "version": "4.1.4", + "tag": "@rushstack/package-deps-hash_v4.1.4", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + } + ] + } + }, + { + "version": "4.1.3", + "tag": "@rushstack/package-deps-hash_v4.1.3", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + } + ] + } + }, + { + "version": "4.1.2", + "tag": "@rushstack/package-deps-hash_v4.1.2", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + } + ] + } + }, + { + "version": "4.1.1", + "tag": "@rushstack/package-deps-hash_v4.1.1", + "date": "Tue, 19 Sep 2023 15:21:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.24`" + } + ] + } + }, + { + "version": "4.1.0", + "tag": "@rushstack/package-deps-hash_v4.1.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.23`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + } + ] + } + }, + { + "version": "4.0.44", + "tag": "@rushstack/package-deps-hash_v4.0.44", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + } + ] + } + }, + { + "version": "4.0.43", + "tag": "@rushstack/package-deps-hash_v4.0.43", + "date": "Mon, 31 Jul 2023 15:19:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.21`" + } + ] + } + }, + { + "version": "4.0.42", + "tag": "@rushstack/package-deps-hash_v4.0.42", + "date": "Sat, 29 Jul 2023 00:22:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.20`" + } + ] + } + }, + { + "version": "4.0.41", + "tag": "@rushstack/package-deps-hash_v4.0.41", + "date": "Thu, 20 Jul 2023 20:47:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.19`" + } + ] + } + }, + { + "version": "4.0.40", + "tag": "@rushstack/package-deps-hash_v4.0.40", + "date": "Wed, 19 Jul 2023 00:20:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + } + ] + } + }, + { + "version": "4.0.39", + "tag": "@rushstack/package-deps-hash_v4.0.39", + "date": "Fri, 14 Jul 2023 15:20:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.17`" + } + ] + } + }, + { + "version": "4.0.38", + "tag": "@rushstack/package-deps-hash_v4.0.38", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.16`" + } + ] + } + }, + { + "version": "4.0.37", + "tag": "@rushstack/package-deps-hash_v4.0.37", + "date": "Wed, 12 Jul 2023 15:20:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.15`" + } + ] + } + }, + { + "version": "4.0.36", + "tag": "@rushstack/package-deps-hash_v4.0.36", + "date": "Wed, 12 Jul 2023 00:23:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.14`" + } + ] + } + }, + { + "version": "4.0.35", + "tag": "@rushstack/package-deps-hash_v4.0.35", + "date": "Fri, 07 Jul 2023 00:19:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.13`" + } + ] + } + }, + { + "version": "4.0.34", + "tag": "@rushstack/package-deps-hash_v4.0.34", + "date": "Thu, 06 Jul 2023 00:16:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + } + ] + } + }, + { + "version": "4.0.33", + "tag": "@rushstack/package-deps-hash_v4.0.33", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.11`" + } + ] + } + }, + { + "version": "4.0.32", + "tag": "@rushstack/package-deps-hash_v4.0.32", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.10`" + } + ] + } + }, + { + "version": "4.0.31", + "tag": "@rushstack/package-deps-hash_v4.0.31", + "date": "Thu, 15 Jun 2023 00:21:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + } + ] + } + }, + { + "version": "4.0.30", + "tag": "@rushstack/package-deps-hash_v4.0.30", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.8`" + } + ] + } + }, + { + "version": "4.0.29", + "tag": "@rushstack/package-deps-hash_v4.0.29", + "date": "Tue, 13 Jun 2023 15:17:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.7`" + } + ] + } + }, + { + "version": "4.0.28", + "tag": "@rushstack/package-deps-hash_v4.0.28", + "date": "Tue, 13 Jun 2023 01:49:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.6`" + } + ] + } + }, + { + "version": "4.0.27", + "tag": "@rushstack/package-deps-hash_v4.0.27", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.5`" + } + ] + } + }, + { + "version": "4.0.26", + "tag": "@rushstack/package-deps-hash_v4.0.26", + "date": "Fri, 09 Jun 2023 15:23:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.4`" + } + ] + } + }, + { + "version": "4.0.25", + "tag": "@rushstack/package-deps-hash_v4.0.25", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.3`" + } + ] + } + }, + { + "version": "4.0.24", + "tag": "@rushstack/package-deps-hash_v4.0.24", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.2`" + } + ] + } + }, + { + "version": "4.0.23", + "tag": "@rushstack/package-deps-hash_v4.0.23", + "date": "Thu, 08 Jun 2023 00:20:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.1`" + } + ] + } + }, + { + "version": "4.0.22", + "tag": "@rushstack/package-deps-hash_v4.0.22", + "date": "Wed, 07 Jun 2023 22:45:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + } + ] + } + }, + { + "version": "4.0.21", + "tag": "@rushstack/package-deps-hash_v4.0.21", + "date": "Tue, 06 Jun 2023 02:52:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.1.0`" + } + ] + } + }, + { + "version": "4.0.20", + "tag": "@rushstack/package-deps-hash_v4.0.20", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.0.1`" + } + ] + } + }, { "version": "4.0.19", "tag": "@rushstack/package-deps-hash_v4.0.19", diff --git a/libraries/package-deps-hash/CHANGELOG.md b/libraries/package-deps-hash/CHANGELOG.md index f10cef68f7d..6ebfe7eaad5 100644 --- a/libraries/package-deps-hash/CHANGELOG.md +++ b/libraries/package-deps-hash/CHANGELOG.md @@ -1,6 +1,961 @@ # Change Log - @rushstack/package-deps-hash -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 4.7.24 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 4.7.23 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 4.7.22 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 4.7.21 +Mon, 15 Jun 2026 15:15:33 GMT + +### Patches + +- Strip GIT_DIR and GIT_WORK_TREE Node env variables to fix issues with miscalculating the git repo root when working in a linked worktree + +## 4.7.20 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 4.7.19 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 4.7.18 +Mon, 25 May 2026 15:14:32 GMT + +### Patches + +- Skip untracked files whose basename is a Windows reserved device name (e.g. `nul`, `con`, `aux`, `com1`-`com9`, `lpt1`-`lpt9`) when computing repo state on Windows. `git hash-object` cannot open such paths and otherwise aborts the entire repo-state calculation. + +## 4.7.17 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 4.7.16 +Mon, 20 Apr 2026 15:15:25 GMT + +_Version update only_ + +## 4.7.15 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 4.7.14 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 4.7.13 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 4.7.12 +Fri, 10 Apr 2026 22:46:35 GMT + +_Version update only_ + +## 4.7.11 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 4.7.10 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 4.7.9 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 4.7.8 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 4.7.7 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 4.7.6 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 4.7.5 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 4.7.4 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 4.7.3 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 4.7.2 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 4.7.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 4.7.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 4.6.8 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 4.6.7 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 4.6.6 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 4.6.5 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 4.6.4 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 4.6.3 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 4.6.2 +Mon, 05 Jan 2026 16:12:50 GMT + +_Version update only_ + +## 4.6.1 +Mon, 29 Dec 2025 22:42:58 GMT + +### Patches + +- Update MINIMUM_GIT_VERSION to 2.35.0 to account for usage of the --format argument with git ls-files + +## 4.6.0 +Fri, 12 Dec 2025 01:12:05 GMT + +### Minor changes + +- Replace "git ls-tree" with "git ls-files" to improve performance. Identify symbolic links and return them separately in "getDetailedRepoStateAsync". Symbolic links will be omitted from the result returned by "getRepoStateAsync", as they are not "files". + +## 4.5.7 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 4.5.6 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 4.5.5 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 4.5.4 +Tue, 04 Nov 2025 08:15:15 GMT + +_Version update only_ + +## 4.5.3 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 4.5.2 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 4.5.1 +Wed, 08 Oct 2025 00:13:29 GMT + +_Version update only_ + +## 4.5.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 4.4.9 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 4.4.8 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 4.4.7 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 4.4.6 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 4.4.5 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 4.4.4 +Fri, 01 Aug 2025 00:12:49 GMT + +_Version update only_ + +## 4.4.3 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 4.4.2 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 4.4.1 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 4.4.0 +Thu, 08 May 2025 00:11:15 GMT + +### Minor changes + +- Add `getDetailedRepoState` API to expose `hasSubmodules` and `hasUncommittedChanges` in addition to the results returned by `getRepoState`. + +## 4.3.24 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 4.3.23 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 4.3.22 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 4.3.21 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 4.3.20 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 4.3.19 +Tue, 15 Apr 2025 15:11:58 GMT + +_Version update only_ + +## 4.3.18 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 4.3.17 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 4.3.16 +Tue, 25 Mar 2025 15:11:16 GMT + +_Version update only_ + +## 4.3.15 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 4.3.14 +Wed, 12 Mar 2025 00:11:32 GMT + +_Version update only_ + +## 4.3.13 +Tue, 11 Mar 2025 02:12:34 GMT + +_Version update only_ + +## 4.3.12 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 4.3.11 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 4.3.10 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 4.3.9 +Wed, 26 Feb 2025 16:11:12 GMT + +_Version update only_ + +## 4.3.8 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 4.3.7 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 4.3.6 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 4.3.5 +Thu, 30 Jan 2025 16:10:36 GMT + +_Version update only_ + +## 4.3.4 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 4.3.3 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 4.3.2 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 4.3.1 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 4.3.0 +Thu, 12 Dec 2024 01:37:09 GMT + +### Minor changes + +- Add a new optional parameter `filterPath` to `getRepoStateAsync` that limits the scope of the git query to only the specified subpaths. This can significantly improve the performance of the function when only part of the full repo data is necessary. + +## 4.2.11 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 4.2.10 +Tue, 03 Dec 2024 16:11:08 GMT + +_Version update only_ + +## 4.2.9 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 4.2.8 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 4.2.7 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 4.2.6 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 4.2.5 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 4.2.4 +Tue, 15 Oct 2024 00:12:32 GMT + +_Version update only_ + +## 4.2.3 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 4.2.2 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 4.2.1 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 4.2.0 +Sat, 21 Sep 2024 00:10:27 GMT + +### Minor changes + +- Expose `hashFilesAsync` API. This serves a similar role as `getGitHashForFiles` but is asynchronous and allows for the file names to be provided as an async iterable. + +## 4.1.68 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 4.1.67 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 4.1.66 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 4.1.65 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 4.1.64 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 4.1.63 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 4.1.62 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 4.1.61 +Wed, 17 Jul 2024 06:55:10 GMT + +_Version update only_ + +## 4.1.60 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 4.1.59 +Tue, 16 Jul 2024 00:36:22 GMT + +_Version update only_ + +## 4.1.58 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 4.1.57 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 4.1.56 +Thu, 30 May 2024 00:13:05 GMT + +### Patches + +- Include missing `type` modifiers on type-only exports. + +## 4.1.55 +Wed, 29 May 2024 02:03:51 GMT + +_Version update only_ + +## 4.1.54 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 4.1.53 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 4.1.52 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 4.1.51 +Sat, 25 May 2024 04:54:08 GMT + +_Version update only_ + +## 4.1.50 +Fri, 24 May 2024 00:15:09 GMT + +_Version update only_ + +## 4.1.49 +Thu, 23 May 2024 02:26:56 GMT + +### Patches + +- Add a newline to an error message + +## 4.1.48 +Fri, 17 May 2024 00:10:40 GMT + +### Patches + +- Fix an issue where an incomplete repo state analysis was sometimes returned, especially on WSL. See https://github.com/microsoft/rushstack/pull/4711 for details. + +## 4.1.47 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 4.1.46 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 4.1.45 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 4.1.44 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 4.1.43 +Wed, 08 May 2024 22:23:51 GMT + +_Version update only_ + +## 4.1.42 +Mon, 06 May 2024 15:11:05 GMT + +_Version update only_ + +## 4.1.41 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 4.1.40 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 4.1.39 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 4.1.38 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 4.1.37 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 4.1.36 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 4.1.35 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 4.1.34 +Thu, 29 Feb 2024 07:11:46 GMT + +_Version update only_ + +## 4.1.33 +Wed, 28 Feb 2024 16:09:28 GMT + +_Version update only_ + +## 4.1.32 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 4.1.31 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 4.1.30 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 4.1.29 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 4.1.28 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 4.1.27 +Tue, 20 Feb 2024 16:10:53 GMT + +_Version update only_ + +## 4.1.26 +Mon, 19 Feb 2024 21:54:27 GMT + +### Patches + +- Fix a formatting issue with the LICENSE. + +## 4.1.25 +Sat, 17 Feb 2024 06:24:35 GMT + +### Patches + +- Fix broken link to API documentation + +## 4.1.24 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 4.1.23 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 4.1.22 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 4.1.21 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 4.1.20 +Tue, 23 Jan 2024 20:12:58 GMT + +_Version update only_ + +## 4.1.19 +Tue, 23 Jan 2024 16:15:06 GMT + +_Version update only_ + +## 4.1.18 +Thu, 18 Jan 2024 01:08:53 GMT + +### Patches + +- Handle an edge case in `getRepoState` wherein it tries to asynchronously pipe data to `git hash-object` but the subprocess has already exited. + +## 4.1.17 +Tue, 16 Jan 2024 18:30:11 GMT + +_Version update only_ + +## 4.1.16 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 4.1.15 +Wed, 20 Dec 2023 01:09:46 GMT + +_Version update only_ + +## 4.1.14 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 4.1.13 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 4.1.12 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 4.1.11 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 4.1.10 +Mon, 30 Oct 2023 23:36:38 GMT + +_Version update only_ + +## 4.1.9 +Sun, 01 Oct 2023 02:56:30 GMT + +_Version update only_ + +## 4.1.8 +Sat, 30 Sep 2023 00:20:51 GMT + +_Version update only_ + +## 4.1.7 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 4.1.6 +Wed, 27 Sep 2023 00:21:39 GMT + +_Version update only_ + +## 4.1.5 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 4.1.4 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 4.1.3 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 4.1.2 +Fri, 22 Sep 2023 00:05:50 GMT + +_Version update only_ + +## 4.1.1 +Tue, 19 Sep 2023 15:21:52 GMT + +_Version update only_ + +## 4.1.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 4.0.44 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 4.0.43 +Mon, 31 Jul 2023 15:19:06 GMT + +_Version update only_ + +## 4.0.42 +Sat, 29 Jul 2023 00:22:51 GMT + +_Version update only_ + +## 4.0.41 +Thu, 20 Jul 2023 20:47:28 GMT + +_Version update only_ + +## 4.0.40 +Wed, 19 Jul 2023 00:20:32 GMT + +_Version update only_ + +## 4.0.39 +Fri, 14 Jul 2023 15:20:45 GMT + +_Version update only_ + +## 4.0.38 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 4.0.37 +Wed, 12 Jul 2023 15:20:40 GMT + +_Version update only_ + +## 4.0.36 +Wed, 12 Jul 2023 00:23:30 GMT + +_Version update only_ + +## 4.0.35 +Fri, 07 Jul 2023 00:19:33 GMT + +_Version update only_ + +## 4.0.34 +Thu, 06 Jul 2023 00:16:20 GMT + +_Version update only_ + +## 4.0.33 +Tue, 04 Jul 2023 00:18:47 GMT + +_Version update only_ + +## 4.0.32 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 4.0.31 +Thu, 15 Jun 2023 00:21:02 GMT + +_Version update only_ + +## 4.0.30 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 4.0.29 +Tue, 13 Jun 2023 15:17:21 GMT + +_Version update only_ + +## 4.0.28 +Tue, 13 Jun 2023 01:49:02 GMT + +_Version update only_ + +## 4.0.27 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 4.0.26 +Fri, 09 Jun 2023 15:23:15 GMT + +_Version update only_ + +## 4.0.25 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 4.0.24 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 4.0.23 +Thu, 08 Jun 2023 00:20:03 GMT + +_Version update only_ + +## 4.0.22 +Wed, 07 Jun 2023 22:45:17 GMT + +_Version update only_ + +## 4.0.21 +Tue, 06 Jun 2023 02:52:51 GMT + +_Version update only_ + +## 4.0.20 +Mon, 05 Jun 2023 21:45:21 GMT + +_Version update only_ ## 4.0.19 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/libraries/package-deps-hash/LICENSE b/libraries/package-deps-hash/LICENSE index 93142aec687..c47fca6439f 100644 --- a/libraries/package-deps-hash/LICENSE +++ b/libraries/package-deps-hash/LICENSE @@ -1,24 +1,24 @@ -@rushstack/package-deps-hash - -Copyright (c) Microsoft Corporation. All rights reserved. - -MIT License - -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 +@rushstack/package-deps-hash + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. \ No newline at end of file diff --git a/libraries/package-deps-hash/README.md b/libraries/package-deps-hash/README.md index 9f573086e63..5386749d7a5 100644 --- a/libraries/package-deps-hash/README.md +++ b/libraries/package-deps-hash/README.md @@ -34,6 +34,6 @@ if (_.isEqual(deps, existingDeps)) { - [CHANGELOG.md]( https://github.com/microsoft/rushstack/blob/main/libraries/package-deps-hash/CHANGELOG.md) - Find out what's new in the latest version -- [API Reference](https://rushstack.io/pages/api/package-deps-hash/) +- [API Reference](https://api.rushstack.io/pages/package-deps-hash/) `@rushstack/package-deps-hash` is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/libraries/package-deps-hash/config/api-extractor.json b/libraries/package-deps-hash/config/api-extractor.json index 996e271d3dd..3dbb76c0e6f 100644 --- a/libraries/package-deps-hash/config/api-extractor.json +++ b/libraries/package-deps-hash/config/api-extractor.json @@ -1,19 +1,4 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - - "mainEntryPointFilePath": "/lib/index.d.ts", - - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, - - "docModel": { - "enabled": true, - "apiJsonFilePath": "../../../common/temp/api/.api.json" - }, - - "dtsRollup": { - "enabled": true - } + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json" } diff --git a/libraries/package-deps-hash/config/jest.config.json b/libraries/package-deps-hash/config/jest.config.json index c400ccf6037..dc083f44326 100644 --- a/libraries/package-deps-hash/config/jest.config.json +++ b/libraries/package-deps-hash/config/jest.config.json @@ -1,5 +1,5 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json", + "extends": "local-node-rig/profiles/default/config/jest.config.json", // Tests in this package break isolation and so must run serially "maxWorkers": 1 } diff --git a/libraries/package-deps-hash/config/rig.json b/libraries/package-deps-hash/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/libraries/package-deps-hash/config/rig.json +++ b/libraries/package-deps-hash/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/libraries/package-deps-hash/eslint.config.js b/libraries/package-deps-hash/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/libraries/package-deps-hash/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/package-deps-hash/package.json b/libraries/package-deps-hash/package.json index ace0ee46c06..4649a689caf 100644 --- a/libraries/package-deps-hash/package.json +++ b/libraries/package-deps-hash/package.json @@ -1,9 +1,32 @@ { "name": "@rushstack/package-deps-hash", - "version": "4.0.19", + "version": "4.7.24", "description": "", - "main": "lib/index.js", - "typings": "dist/package-deps-hash.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/package-deps-hash.d.ts", + "exports": { + ".": { + "types": "./dist/package-deps-hash.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "license": "MIT", "repository": { "type": "git", @@ -16,14 +39,12 @@ "_phase:test": "heft run --only test -- --clean" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", - "@rushstack/node-core-library": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36" + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" }, "dependencies": { "@rushstack/node-core-library": "workspace:*" - } + }, + "sideEffects": false } diff --git a/libraries/package-deps-hash/src/getPackageDeps.ts b/libraries/package-deps-hash/src/getPackageDeps.ts index 6eaa65a532e..a284af5600a 100644 --- a/libraries/package-deps-hash/src/getPackageDeps.ts +++ b/libraries/package-deps-hash/src/getPackageDeps.ts @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as child_process from 'child_process'; -import * as path from 'path'; +import type * as child_process from 'node:child_process'; +import * as path from 'node:path'; + import { Executable } from '@rushstack/node-core-library'; import { ensureGitMinimumVersion } from './getRepoState'; @@ -184,12 +185,12 @@ export function getGitHashForFiles( /** * Executes "git ls-tree" in a folder */ -export function gitLsTree(path: string, gitPath?: string): string { +export function gitLsTree(cwdPath: string, gitPath?: string): string { const result: child_process.SpawnSyncReturns = Executable.spawnSync( gitPath || 'git', ['ls-tree', 'HEAD', '-r'], { - currentWorkingDirectory: path + currentWorkingDirectory: cwdPath } ); @@ -205,7 +206,7 @@ export function gitLsTree(path: string, gitPath?: string): string { /** * Executes "git status" in a folder */ -export function gitStatus(path: string, gitPath?: string): string { +export function gitStatus(cwdPath: string, gitPath?: string): string { /** * -s - Short format. Will be printed as 'XY PATH' or 'XY ORIG_PATH -> PATH'. Paths with non-standard * characters will be escaped using double-quotes, and non-standard characters will be backslash @@ -218,7 +219,7 @@ export function gitStatus(path: string, gitPath?: string): string { gitPath || 'git', ['status', '-s', '-u', '.'], { - currentWorkingDirectory: path + currentWorkingDirectory: cwdPath } ); diff --git a/libraries/package-deps-hash/src/getRepoState.ts b/libraries/package-deps-hash/src/getRepoState.ts index 6016a4b1dde..45cb07a8dd7 100644 --- a/libraries/package-deps-hash/src/getRepoState.ts +++ b/libraries/package-deps-hash/src/getRepoState.ts @@ -1,11 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type * as child_process from 'child_process'; -import { once } from 'events'; -import { Readable } from 'stream'; +import type * as child_process from 'node:child_process'; +import { once } from 'node:events'; +import { Readable, pipeline } from 'node:stream'; -import { Executable, FileSystem, IExecutableSpawnOptions } from '@rushstack/node-core-library'; +import { Executable, type IExecutableSpawnOptions } from '@rushstack/node-core-library/lib/Executable'; +import { FileSystem } from '@rushstack/node-core-library/lib/FileSystem'; export interface IGitVersion { major: number; @@ -15,7 +16,7 @@ export interface IGitVersion { const MINIMUM_GIT_VERSION: IGitVersion = { major: 2, - minor: 20, + minor: 35, patch: 0 }; @@ -27,17 +28,70 @@ const STANDARD_GIT_OPTIONS: readonly string[] = [ 'maintenance.auto=false' ]; +// Windows reserved device names (case-insensitive). A file whose final path segment matches one +// of these (with or without an extension) cannot be opened by name on Windows, so passing it to +// `git hash-object` aborts the process. Such files are typically untracked artifacts left behind +// by tooling (e.g. stray `nul` from a shell redirect). +const WINDOWS_RESERVED_BASENAMES: ReadonlySet = new Set([ + 'CON', + 'PRN', + 'AUX', + 'NUL', + 'COM1', + 'COM2', + 'COM3', + 'COM4', + 'COM5', + 'COM6', + 'COM7', + 'COM8', + 'COM9', + 'LPT1', + 'LPT2', + 'LPT3', + 'LPT4', + 'LPT5', + 'LPT6', + 'LPT7', + 'LPT8', + 'LPT9' +]); + +/** + * Returns `true` if `filePath`'s final path segment is a Windows reserved device name + * (with or without an extension), case-insensitively. Exported for tests. + * @internal + */ +export function isWindowsReservedPath(filePath: string): boolean { + const lastSlash: number = Math.max(filePath.lastIndexOf('/'), filePath.lastIndexOf('\\')); + const basename: string = lastSlash >= 0 ? filePath.slice(lastSlash + 1) : filePath; + const dot: number = basename.indexOf('.'); + const stem: string = (dot >= 0 ? basename.slice(0, dot) : basename).toUpperCase(); + return WINDOWS_RESERVED_BASENAMES.has(stem); +} + +const OBJECTMODE_SUBMODULE: '160000' = '160000'; +const OBJECTMODE_SYMLINK: '120000' = '120000'; +const OBJECTMODE_FILE_NONEXECUTABLE: '100644' = '100644'; +const OBJECTMODE_FILE_EXECUTABLE: '100755' = '100755'; + +// Note that `type` is a stub that is being ignored by the parser in favor of using `mode` to infer, since `%(objecttype)` requires git 2.51.0+ +// e.g. 10644 blob \t +const GIT_LSTREE_FORMAT: string = '%(objectmode) type %(objectname)%x09%(path)'; + interface IGitTreeState { files: Map; // type "blob" + symlinks: Map; // type "link" submodules: Map; // type "commit" } /** - * Parses the output of the "git ls-tree -r -z" command + * Parses the output of the "git ls-tree -r -z" command or of other commands that have been coerced to match its format. * @internal */ export function parseGitLsTree(output: string): IGitTreeState { const files: Map = new Map(); + const symlinks: Map = new Map(); const submodules: Map = new Map(); // Parse the output @@ -57,16 +111,21 @@ export function parseGitLsTree(output: string): IGitTreeState { // The newHash will be all zeros if the file is deleted, or a hash if it exists const hash: string = item.slice(tabIndex - 40, tabIndex); - const spaceIndex: number = item.lastIndexOf(' ', tabIndex - 42); + const mode: string = item.slice(0, item.indexOf(' ')); - const type: string = item.slice(spaceIndex + 1, tabIndex - 41); - - switch (type) { - case 'commit': { + switch (mode) { + case OBJECTMODE_SUBMODULE: { + // This is a submodule submodules.set(filePath, hash); break; } - case 'blob': + case OBJECTMODE_SYMLINK: { + // This is a symbolic link + symlinks.set(filePath, hash); + break; + } + case OBJECTMODE_FILE_NONEXECUTABLE: + case OBJECTMODE_FILE_EXECUTABLE: default: { files.set(filePath, hash); break; @@ -79,6 +138,7 @@ export function parseGitLsTree(output: string): IGitTreeState { return { files, + symlinks, submodules }; } @@ -213,6 +273,13 @@ export function parseGitStatus(output: string): Map { const repoRootCache: Map = new Map(); +// Strip GIT_DIR/GIT_WORK_TREE: git hooks in linked worktrees set GIT_DIR to the per-worktree metadata dir, causing rev-parse --show-toplevel to return CWD instead of the worktree root. +function getCleanGitEnvironment(): NodeJS.ProcessEnv { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { GIT_DIR, GIT_WORK_TREE, ...trimmedEnv } = process.env; + return trimmedEnv; +} + /** * Finds the root of the current Git repository * @@ -229,7 +296,8 @@ export function getRepoRoot(currentWorkingDirectory: string, gitPath?: string): gitPath || 'git', ['--no-optional-locks', 'rev-parse', '--show-toplevel'], { - currentWorkingDirectory + currentWorkingDirectory, + environment: getCleanGitEnvironment() } ); @@ -264,7 +332,8 @@ async function spawnGitAsync( ): Promise { const spawnOptions: IExecutableSpawnOptions = { currentWorkingDirectory, - stdio: ['pipe', 'pipe', 'pipe'] + stdio: ['pipe', 'pipe', 'pipe'], + environment: getCleanGitEnvironment() }; let stdout: string = ''; @@ -282,17 +351,79 @@ async function spawnGitAsync( }); if (stdin) { - stdin.pipe(proc.stdin!); + /** + * For `git hash-object` data is piped in asynchronously. In the event that one of the + * passed filenames cannot be hashed, subsequent writes to `proc.stdin` will error. + * Silence this error since it will be handled by the non-zero exit code of the process. + */ + pipeline(stdin, proc.stdin!, (err) => {}); } - const [status] = await once(proc, 'exit'); + const [status] = await once(proc, 'close'); if (status !== 0) { - throw new Error(`git ${args[0]} exited with code ${status}: ${stderr}`); + ensureGitMinimumVersion(gitPath); + + throw new Error(`git ${args[0]} exited with code ${status}:\n${stderr}`); } return stdout; } +function isIterable(value: Iterable | AsyncIterable): value is Iterable { + return Symbol.iterator in value; +} + +/** + * Uses `git hash-object` to hash the provided files. Unlike `getGitHashForFiles`, this API is asynchronous, and also allows for + * the input file paths to be specified as an async iterable. + * + * @param rootDirectory - The root directory to which paths are specified relative. Must be the root of the Git repository. + * @param filesToHash - The file paths to hash using `git hash-object` + * @param gitPath - The path to the Git executable + * @returns An iterable of [filePath, hash] pairs + * + * @remarks + * The input file paths must be specified relative to the Git repository root, or else be absolute paths. + * @beta + */ +export async function hashFilesAsync( + rootDirectory: string, + filesToHash: Iterable | AsyncIterable, + gitPath?: string +): Promise> { + const hashPaths: string[] = []; + + const input: Readable = Readable.from( + isIterable(filesToHash) + ? (function* (): IterableIterator { + for (const file of filesToHash) { + hashPaths.push(file); + yield `${file}\n`; + } + })() + : (async function* (): AsyncIterableIterator { + for await (const file of filesToHash) { + hashPaths.push(file); + yield `${file}\n`; + } + })(), + { + encoding: 'utf-8', + objectMode: false, + autoDestroy: true + } + ); + + const hashObjectResult: string = await spawnGitAsync( + gitPath, + STANDARD_GIT_OPTIONS.concat(['hash-object', '--stdin-paths']), + rootDirectory, + input + ); + + return parseGitHashObject(hashObjectResult, hashPaths); +} + /** * Gets the object hashes for all files in the Git repo, combining the current commit with working tree state. * Uses async operations and runs all primary Git calls in parallel. @@ -304,21 +435,70 @@ async function spawnGitAsync( export async function getRepoStateAsync( rootDirectory: string, additionalRelativePathsToHash?: string[], - gitPath?: string + gitPath?: string, + filterPath?: string[] ): Promise> { + const { files } = await getDetailedRepoStateAsync( + rootDirectory, + additionalRelativePathsToHash, + gitPath, + filterPath + ); + + return files; +} + +/** + * Information about the detailed state of the Git repository. + * @beta + */ +export interface IDetailedRepoState { + /** + * The Git file hashes for all files in the repository, including uncommitted changes. + */ + files: Map; + /** + * The Git file hashes for all symbolic links in the repository, including uncommitted changes. + */ + symlinks: Map; + /** + * A boolean indicating whether the repository has submodules. + */ + hasSubmodules: boolean; + /** + * A boolean indicating whether the repository has uncommitted changes. + */ + hasUncommittedChanges: boolean; +} + +/** + * Gets the object hashes for all files in the Git repo, combining the current commit with working tree state. + * Uses async operations and runs all primary Git calls in parallel. + * @param rootDirectory - The root directory of the Git repository + * @param additionalRelativePathsToHash - Root-relative file paths to have Git hash and include in the results + * @param gitPath - The path to the Git executable + * @beta + */ +export async function getDetailedRepoStateAsync( + rootDirectory: string, + additionalRelativePathsToHash?: string[], + gitPath?: string, + filterPath?: string[] +): Promise { const statePromise: Promise = spawnGitAsync( gitPath, STANDARD_GIT_OPTIONS.concat([ - 'ls-tree', - // Recursively expand trees - '-r', + 'ls-files', + // Read from the index only + '--cached', // Use NUL as the separator '-z', // Specify the full path to files relative to the root '--full-name', - // As of last commit - 'HEAD', - '--' + // Match the format of "git ls-tree". The %(objecttype) placeholder requires git 2.51.0+, so not using yet. + `--format=${GIT_LSTREE_FORMAT}`, + '--', + ...(filterPath ?? []) ]), rootDirectory ).then(parseGitLsTree); @@ -336,51 +516,51 @@ export async function getRepoStateAsync( '--ignore-submodules', // Don't compare against the remote '--no-ahead-behind', - '--' + '--', + ...(filterPath ?? []) ]), rootDirectory ).then(parseGitStatus); - const hashPaths: string[] = []; async function* getFilesToHash(): AsyncIterableIterator { if (additionalRelativePathsToHash) { for (const file of additionalRelativePathsToHash) { - hashPaths.push(file); - yield `${file}\n`; + yield file; } } - const [{ files }, locallyModified] = await Promise.all([statePromise, locallyModifiedPromise]); + const [{ files, symlinks }, locallyModified] = await Promise.all([statePromise, locallyModifiedPromise]); + const isWindows: boolean = process.platform === 'win32'; for (const [filePath, exists] of locallyModified) { - if (exists) { - hashPaths.push(filePath); - yield `${filePath}\n`; + if (exists && !symlinks.has(filePath)) { + // Skip Windows reserved device names. `git hash-object` cannot open them and would abort + // the entire repo-state computation. These are almost always stray artifacts (e.g. a `nul` + // file produced by a misdirected shell redirect) rather than meaningful inputs. + if (isWindows && isWindowsReservedPath(filePath)) { + continue; + } + yield filePath; } else { files.delete(filePath); + symlinks.delete(filePath); } } } - const hashObjectPromise: Promise = spawnGitAsync( - gitPath, - STANDARD_GIT_OPTIONS.concat(['hash-object', '--stdin-paths']), + const hashObjectPromise: Promise> = hashFilesAsync( rootDirectory, - Readable.from(getFilesToHash(), { - encoding: 'utf-8', - objectMode: false, - autoDestroy: true - }) + getFilesToHash(), + gitPath ); - const [{ files, submodules }, hashObject] = await Promise.all([ + const [{ files, symlinks, submodules }, locallyModifiedFiles] = await Promise.all([ statePromise, - hashObjectPromise, locallyModifiedPromise ]); // The result of "git hash-object" will be a list of file hashes delimited by newlines - for (const [filePath, hash] of parseGitHashObject(hashObject, hashPaths)) { + for (const [filePath, hash] of await hashObjectPromise) { files.set(filePath, hash); } @@ -402,7 +582,12 @@ export async function getRepoStateAsync( } } - return files; + return { + hasSubmodules, + hasUncommittedChanges: locallyModifiedFiles.size > 0, + files, + symlinks + }; } /** @@ -434,7 +619,8 @@ export function getRepoChanges( '--' ]), { - currentWorkingDirectory: rootDirectory + currentWorkingDirectory: rootDirectory, + environment: getCleanGitEnvironment() } ); diff --git a/libraries/package-deps-hash/src/index.ts b/libraries/package-deps-hash/src/index.ts index 2135572b5b7..8558d210a4d 100644 --- a/libraries/package-deps-hash/src/index.ts +++ b/libraries/package-deps-hash/src/index.ts @@ -15,9 +15,12 @@ export { getPackageDeps, getGitHashForFiles } from './getPackageDeps'; export { - IFileDiffStatus, + type IFileDiffStatus, + type IDetailedRepoState, + getDetailedRepoStateAsync, getRepoChanges, getRepoRoot, getRepoStateAsync, - ensureGitMinimumVersion + ensureGitMinimumVersion, + hashFilesAsync } from './getRepoState'; diff --git a/libraries/package-deps-hash/src/test/__snapshots__/getRepoDeps.test.ts.snap b/libraries/package-deps-hash/src/test/__snapshots__/getRepoDeps.test.ts.snap index add7d8be46f..c5025ca14fb 100644 --- a/libraries/package-deps-hash/src/test/__snapshots__/getRepoDeps.test.ts.snap +++ b/libraries/package-deps-hash/src/test/__snapshots__/getRepoDeps.test.ts.snap @@ -1,85 +1,113 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`getRepoStateAsync can handle adding one file 1`] = ` +exports[`getDetailedRepoStateAsync can handle adding one file 1`] = ` Object { - "nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", - "nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", - "testProject/a.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e", - "testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb", - "testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", - "testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba", - "testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + "files": Object { + "nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + "nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", + "testProject/a.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e", + "testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb", + "testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", + "testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba", + "testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + }, + "hasSubmodules": false, + "hasUncommittedChanges": true, } `; -exports[`getRepoStateAsync can handle adding two files 1`] = ` +exports[`getDetailedRepoStateAsync can handle adding two files 1`] = ` Object { - "nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", - "nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", - "testProject/a.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e", - "testProject/b.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e", - "testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb", - "testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", - "testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba", - "testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + "files": Object { + "nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + "nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", + "testProject/a.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e", + "testProject/b.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e", + "testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb", + "testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", + "testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba", + "testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + }, + "hasSubmodules": false, + "hasUncommittedChanges": true, } `; -exports[`getRepoStateAsync can handle changing one file 1`] = ` +exports[`getDetailedRepoStateAsync can handle changing one file 1`] = ` Object { - "nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", - "nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", - "testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb", - "testProject/file1.txt": "f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f", - "testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba", - "testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + "files": Object { + "nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + "nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", + "testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb", + "testProject/file1.txt": "f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f", + "testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba", + "testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + }, + "hasSubmodules": false, + "hasUncommittedChanges": true, } `; -exports[`getRepoStateAsync can handle removing one file 1`] = ` +exports[`getDetailedRepoStateAsync can handle removing one file 1`] = ` Object { - "nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", - "nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", - "testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb", - "testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba", - "testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + "files": Object { + "nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + "nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", + "testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb", + "testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba", + "testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + }, + "hasSubmodules": false, + "hasUncommittedChanges": true, } `; -exports[`getRepoStateAsync can handle uncommitted filenames with spaces and non-ASCII characters 1`] = ` +exports[`getDetailedRepoStateAsync can handle uncommitted filenames with spaces and non-ASCII characters 1`] = ` Object { - "nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", - "nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", - "testProject/a file name.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e", - "testProject/a file.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e", - "testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb", - "testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", - "testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba", - "testProject/newFile批把.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e", - "testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + "files": Object { + "nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + "nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", + "testProject/a file name.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e", + "testProject/a file.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e", + "testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb", + "testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", + "testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba", + "testProject/newFile批把.txt": "2e65efe2a145dda7ee51d1741299f848e5bf752e", + "testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + }, + "hasSubmodules": false, + "hasUncommittedChanges": true, } `; -exports[`getRepoStateAsync can parse committed files 1`] = ` +exports[`getDetailedRepoStateAsync can parse committed files 1`] = ` Object { - "nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", - "nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", - "testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb", - "testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", - "testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba", - "testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + "files": Object { + "nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + "nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", + "testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb", + "testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", + "testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba", + "testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + }, + "hasSubmodules": false, + "hasUncommittedChanges": false, } `; -exports[`getRepoStateAsync handles requests for additional files 1`] = ` +exports[`getDetailedRepoStateAsync handles requests for additional files 1`] = ` Object { - "nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", - "nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", - "testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb", - "testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", - "testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba", - "testProject/log.log": "2e65efe2a145dda7ee51d1741299f848e5bf752e", - "testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + "files": Object { + "nestedTestProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + "nestedTestProject/src/file 1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", + "testProject/file 2.txt": "a385f754ec4fede884a4864d090064d9aeef8ccb", + "testProject/file1.txt": "c7b2f707ac99ca522f965210a7b6b0b109863f34", + "testProject/file蝴蝶.txt": "ae814af81e16cb2ae8c57503c77e2cab6b5462ba", + "testProject/log.log": "2e65efe2a145dda7ee51d1741299f848e5bf752e", + "testProject/package.json": "18a1e415e56220fa5122428a4ef8eb8874756576", + }, + "hasSubmodules": false, + "hasUncommittedChanges": false, } `; diff --git a/libraries/package-deps-hash/src/test/getPackageDeps.test.ts b/libraries/package-deps-hash/src/test/getPackageDeps.test.ts index 264131a0fc4..0c57ea6f3cb 100644 --- a/libraries/package-deps-hash/src/test/getPackageDeps.test.ts +++ b/libraries/package-deps-hash/src/test/getPackageDeps.test.ts @@ -1,14 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { execSync } from 'child_process'; +import * as path from 'node:path'; +import { execSync } from 'node:child_process'; import { getPackageDeps, parseGitLsTree, parseGitFilename } from '../getPackageDeps'; import { FileSystem, FileConstants } from '@rushstack/node-core-library'; -const SOURCE_PATH: string = path.join(__dirname).replace(path.join('lib', 'test'), path.join('src', 'test')); +const SOURCE_PATH: string = path + .join(__dirname) + .replace(path.join('lib-commonjs', 'test'), path.join('src', 'test')); const TEST_PROJECT_PATH: string = path.join(SOURCE_PATH, 'testProject'); const NESTED_TEST_PROJECT_PATH: string = path.join(SOURCE_PATH, 'nestedTestProject'); diff --git a/libraries/package-deps-hash/src/test/getRepoDeps.test.ts b/libraries/package-deps-hash/src/test/getRepoDeps.test.ts index 1e16ff3ac5f..c78e034386b 100644 --- a/libraries/package-deps-hash/src/test/getRepoDeps.test.ts +++ b/libraries/package-deps-hash/src/test/getRepoDeps.test.ts @@ -1,34 +1,42 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { execSync } from 'child_process'; +import * as path from 'node:path'; +import { execSync, type SpawnSyncReturns } from 'node:child_process'; -import { getRepoStateAsync, parseGitLsTree, getRepoRoot, parseGitHashObject } from '../getRepoState'; +import { + getDetailedRepoStateAsync, + type IDetailedRepoState, + parseGitLsTree, + getRepoRoot, + parseGitHashObject +} from '../getRepoState'; -import { FileSystem } from '@rushstack/node-core-library'; +import { Executable, FileSystem } from '@rushstack/node-core-library'; -const SOURCE_PATH: string = path.join(__dirname).replace(path.join('lib', 'test'), path.join('src', 'test')); +const SOURCE_PATH: string = path + .join(__dirname) + .replace(path.join('lib-commonjs', 'test'), path.join('src', 'test')); const TEST_PREFIX: string = `libraries/package-deps-hash/src/test/`; const TEST_PROJECT_PATH: string = path.join(SOURCE_PATH, 'testProject'); const FILTERS: string[] = [`testProject/`, `nestedTestProject/`]; -function checkSnapshot(results: Map): void { +function checkSnapshot(results: IDetailedRepoState): void { const relevantResults: Record = {}; - for (const [key, hash] of results) { + for (const [key, hash] of results.files) { if (key.startsWith(TEST_PREFIX)) { const partialKey: string = key.slice(TEST_PREFIX.length); - for (const filter of FILTERS) { - if (partialKey.startsWith(filter)) { - relevantResults[partialKey] = hash; - } - } + relevantResults[partialKey] = hash; } } - expect(relevantResults).toMatchSnapshot(); + expect({ + hasSubmodules: results.hasSubmodules, + hasUncommittedChanges: results.hasUncommittedChanges, + files: relevantResults + }).toMatchSnapshot(); } describe(getRepoRoot.name, () => { @@ -37,6 +45,59 @@ describe(getRepoRoot.name, () => { const expectedRoot: string = path.resolve(__dirname, '../../../..').replace(/\\/g, '/'); expect(root).toEqual(expectedRoot); }); + + it(`strips GIT_DIR and GIT_WORK_TREE before invoking git`, () => { + // Regression test for the linked-worktree bug. When git runs a hook it injects GIT_DIR (and + // sometimes GIT_WORK_TREE) into the environment; in a linked worktree GIT_DIR points at the + // per-worktree metadata directory, which makes `git rev-parse --show-toplevel` resolve against + // the current directory instead of the true repository root. getRepoRoot must therefore invoke + // git with those variables removed, so the root is derived solely from currentWorkingDirectory. + // + // This is asserted at the spawn boundary rather than by mutating process.env and shelling out for + // real: the Jest environment does not propagate in-process process.env writes to child processes, + // so an end-to-end variant would pass whether or not the stripping actually happens. + const fakeRoot: string = '/fake/repo/root'; + const mockResult: SpawnSyncReturns = { + pid: 0, + output: [], + stdout: fakeRoot, + stderr: '', + status: 0, + signal: null + }; + const spawnSyncSpy: jest.SpyInstance = jest.spyOn(Executable, 'spawnSync').mockReturnValue(mockResult); + + const originalGitDir: string | undefined = process.env.GIT_DIR; + const originalGitWorkTree: string | undefined = process.env.GIT_WORK_TREE; + try { + process.env.GIT_DIR = '/repo/.git/worktrees/feature'; + process.env.GIT_WORK_TREE = '/repo/work/tree'; + + // A unique cwd that no other test resolves, so getRepoRoot's module-level cache can't satisfy + // this from a previous call and skip the spawn. + getRepoRoot('/nonexistent/getRepoRoot-strips-git-env'); + + expect(spawnSyncSpy).toHaveBeenCalledTimes(1); + const passedEnvironment: NodeJS.ProcessEnv | undefined = spawnSyncSpy.mock.calls[0][2]?.environment; + // The fix passes an explicit environment (pre-fix code passed none) that omits both variables, + // while leaving the rest of process.env intact. + expect(passedEnvironment).toBeDefined(); + expect(passedEnvironment).not.toHaveProperty('GIT_DIR'); + expect(passedEnvironment).not.toHaveProperty('GIT_WORK_TREE'); + } finally { + spawnSyncSpy.mockRestore(); + if (originalGitDir === undefined) { + delete process.env.GIT_DIR; + } else { + process.env.GIT_DIR = originalGitDir; + } + if (originalGitWorkTree === undefined) { + delete process.env.GIT_WORK_TREE; + } else { + process.env.GIT_WORK_TREE = originalGitWorkTree; + } + } + }); }); describe(parseGitLsTree.name, () => { @@ -45,12 +106,29 @@ describe(parseGitLsTree.name, () => { const hash: string = '3451bccdc831cb43d7a70ed8e628dcf9c7f888c8'; const output: string = `100644 blob ${hash}\t${filename}\x00`; - const { files } = parseGitLsTree(output); + const { files, symlinks, submodules } = parseGitLsTree(output); + + expect(symlinks.size).toEqual(0); // Expect there to be exactly 0 symlinks + expect(submodules.size).toEqual(0); // Expect there to be exactly 0 submodules expect(files.size).toEqual(1); // Expect there to be exactly 1 change expect(files.get(filename)).toEqual(hash); // Expect the hash to be ${hash} }); + it('can handle a symlink', () => { + const filename: string = 'src/symlink'; + const hash: string = '3451bccdc831cb43d7a70ed8e628dcf9c7f888c8'; + + const output: string = `120000 link ${hash}\t${filename}\x00`; + const { files, symlinks, submodules } = parseGitLsTree(output); + + expect(files.size).toEqual(0); // Expect there to be exactly 0 files + expect(submodules.size).toEqual(0); // Expect there to be exactly 0 submodules + + expect(symlinks.size).toEqual(1); // Expect there to be exactly 1 symlink + expect(symlinks.get(filename)).toEqual(hash); // Expect the hash to be ${hash} + }); + it('can handle a submodule', () => { const filename: string = 'rushstack'; const hash: string = 'c5880bf5b0c6c1f2e2c43c95beeb8f0a808e8bac'; @@ -72,14 +150,16 @@ describe(parseGitLsTree.name, () => { const filename3: string = 'submodule/src/index.ts'; const hash3: string = 'fedcba9876543210fedcba9876543210fedcba98'; - const output: string = `100644 blob ${hash1}\t${filename1}\x00100666 blob ${hash2}\t${filename2}\x00106666 commit ${hash3}\t${filename3}\0`; - const { files, submodules } = parseGitLsTree(output); + const output: string = `100644 blob ${hash1}\t${filename1}\x00100666 blob ${hash2}\t${filename2}\x00160000 commit ${hash3}\t${filename3}\0`; + const { files, symlinks, submodules } = parseGitLsTree(output); - expect(files.size).toEqual(2); // Expect there to be exactly 2 changes + expect(files.size).toEqual(2); // Expect there to be exactly 2 files expect(files.get(filename1)).toEqual(hash1); // Expect the hash to be ${hash1} expect(files.get(filename2)).toEqual(hash2); // Expect the hash to be ${hash2} - expect(submodules.size).toEqual(1); // Expect there to be exactly 1 submodule changes + expect(symlinks.size).toEqual(0); // Expect there to be exactly 0 symlink changes + + expect(submodules.size).toEqual(1); // Expect there to be exactly 1 submodule expect(submodules.get(filename3)).toEqual(hash3); // Expect the hash to be ${hash3} }); }); @@ -113,9 +193,14 @@ describe(parseGitHashObject.name, () => { }); }); -describe(getRepoStateAsync.name, () => { +describe(getDetailedRepoStateAsync.name, () => { it('can parse committed files', async () => { - const results: Map = await getRepoStateAsync(SOURCE_PATH); + const results: IDetailedRepoState = await getDetailedRepoStateAsync( + SOURCE_PATH, + undefined, + undefined, + FILTERS + ); checkSnapshot(results); }); @@ -125,7 +210,12 @@ describe(getRepoStateAsync.name, () => { FileSystem.writeFile(tempFilePath, 'a'); try { - const results: Map = await getRepoStateAsync(SOURCE_PATH); + const results: IDetailedRepoState = await getDetailedRepoStateAsync( + SOURCE_PATH, + undefined, + undefined, + FILTERS + ); checkSnapshot(results); } finally { FileSystem.deleteFile(tempFilePath); @@ -140,7 +230,12 @@ describe(getRepoStateAsync.name, () => { FileSystem.writeFile(tempFilePath2, 'a'); try { - const results: Map = await getRepoStateAsync(SOURCE_PATH); + const results: IDetailedRepoState = await getDetailedRepoStateAsync( + SOURCE_PATH, + undefined, + undefined, + FILTERS + ); checkSnapshot(results); } finally { FileSystem.deleteFile(tempFilePath1); @@ -154,7 +249,12 @@ describe(getRepoStateAsync.name, () => { FileSystem.deleteFile(testFilePath); try { - const results: Map = await getRepoStateAsync(SOURCE_PATH); + const results: IDetailedRepoState = await getDetailedRepoStateAsync( + SOURCE_PATH, + undefined, + undefined, + FILTERS + ); checkSnapshot(results); } finally { execSync(`git checkout --force HEAD -- ${TEST_PREFIX}testProject/file1.txt`, { @@ -170,7 +270,12 @@ describe(getRepoStateAsync.name, () => { FileSystem.writeFile(testFilePath, 'abc'); try { - const results: Map = await getRepoStateAsync(SOURCE_PATH); + const results: IDetailedRepoState = await getDetailedRepoStateAsync( + SOURCE_PATH, + undefined, + undefined, + FILTERS + ); checkSnapshot(results); } finally { execSync(`git checkout --force HEAD -- ${TEST_PREFIX}testProject/file1.txt`, { @@ -190,7 +295,12 @@ describe(getRepoStateAsync.name, () => { FileSystem.writeFile(tempFilePath3, 'a'); try { - const results: Map = await getRepoStateAsync(SOURCE_PATH); + const results: IDetailedRepoState = await getDetailedRepoStateAsync( + SOURCE_PATH, + undefined, + undefined, + FILTERS + ); checkSnapshot(results); } finally { FileSystem.deleteFile(tempFilePath1); @@ -205,9 +315,12 @@ describe(getRepoStateAsync.name, () => { FileSystem.writeFile(tempFilePath1, 'a'); try { - const results: Map = await getRepoStateAsync(SOURCE_PATH, [ - `${TEST_PREFIX}testProject/log.log` - ]); + const results: IDetailedRepoState = await getDetailedRepoStateAsync( + SOURCE_PATH, + [`${TEST_PREFIX}testProject/log.log`], + undefined, + FILTERS + ); checkSnapshot(results); } finally { FileSystem.deleteFile(tempFilePath1); diff --git a/libraries/package-deps-hash/src/test/getRepoState.test.ts b/libraries/package-deps-hash/src/test/getRepoState.test.ts index c1422367b0f..573657feb36 100644 --- a/libraries/package-deps-hash/src/test/getRepoState.test.ts +++ b/libraries/package-deps-hash/src/test/getRepoState.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { parseGitStatus, parseGitVersion } from '../getRepoState'; +import { isWindowsReservedPath, parseGitStatus, parseGitVersion } from '../getRepoState'; describe(parseGitVersion.name, () => { it('Can parse valid git version responses', () => { @@ -87,3 +87,32 @@ describe(parseGitStatus.name, () => { expect(result.get(files[2])).toEqual(true); }); }); + +describe(isWindowsReservedPath.name, () => { + it('detects bare reserved basenames', () => { + expect(isWindowsReservedPath('nul')).toBe(true); + expect(isWindowsReservedPath('NUL')).toBe(true); + expect(isWindowsReservedPath('Con')).toBe(true); + expect(isWindowsReservedPath('com1')).toBe(true); + expect(isWindowsReservedPath('LPT9')).toBe(true); + }); + + it('detects reserved basenames with an extension', () => { + expect(isWindowsReservedPath('nul.txt')).toBe(true); + expect(isWindowsReservedPath('aux.log.bak')).toBe(true); + }); + + it('matches the final segment of nested paths with either slash style', () => { + expect(isWindowsReservedPath('apps/admin/nul')).toBe(true); + expect(isWindowsReservedPath('apps\\admin\\nul')).toBe(true); + expect(isWindowsReservedPath('apps/admin/sub/CON.tmp')).toBe(true); + }); + + it('does not match non-reserved names', () => { + expect(isWindowsReservedPath('null')).toBe(false); + expect(isWindowsReservedPath('console.ts')).toBe(false); + expect(isWindowsReservedPath('com.ts')).toBe(false); + expect(isWindowsReservedPath('lpt10')).toBe(false); + expect(isWindowsReservedPath('packages/nul-suffix/index.ts')).toBe(false); + }); +}); diff --git a/libraries/package-deps-hash/tsconfig.json b/libraries/package-deps-hash/tsconfig.json index fbc2f5c0a6c..dac21d04081 100644 --- a/libraries/package-deps-hash/tsconfig.json +++ b/libraries/package-deps-hash/tsconfig.json @@ -1,7 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["heft-jest", "node"] - } + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/libraries/package-extractor/.eslintrc.js b/libraries/package-extractor/.eslintrc.js deleted file mode 100644 index f7ee2a5d364..00000000000 --- a/libraries/package-extractor/.eslintrc.js +++ /dev/null @@ -1,11 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node', - '@rushstack/eslint-config/mixins/friendly-locals', - '@rushstack/eslint-config/mixins/tsdoc' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/libraries/package-extractor/.gitignore b/libraries/package-extractor/.gitignore index 618684c2905..e93dfd32bfd 100644 --- a/libraries/package-extractor/.gitignore +++ b/libraries/package-extractor/.gitignore @@ -1,2 +1 @@ -# Keep temp folders in mocked 'repos' that are used for unit tests -!**/test/**/temp +test-output diff --git a/libraries/package-extractor/.npmignore b/libraries/package-extractor/.npmignore index e42f0c370b6..f7a40e10213 100644 --- a/libraries/package-extractor/.npmignore +++ b/libraries/package-extractor/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,18 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) -!/assets/** -/lib-*/** +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/package-extractor/CHANGELOG.json b/libraries/package-extractor/CHANGELOG.json index 448188397af..a061e449eac 100644 --- a/libraries/package-extractor/CHANGELOG.json +++ b/libraries/package-extractor/CHANGELOG.json @@ -1,6 +1,4207 @@ { "name": "@rushstack/package-extractor", "entries": [ + { + "version": "0.13.10", + "tag": "@rushstack/package-extractor_v0.13.10", + "date": "Tue, 21 Jul 2026 02:53:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.23`" + } + ] + } + }, + { + "version": "0.13.9", + "tag": "@rushstack/package-extractor_v0.13.9", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.22`" + } + ] + } + }, + { + "version": "0.13.8", + "tag": "@rushstack/package-extractor_v0.13.8", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.21`" + } + ] + } + }, + { + "version": "0.13.7", + "tag": "@rushstack/package-extractor_v0.13.7", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.20`" + } + ] + } + }, + { + "version": "0.13.6", + "tag": "@rushstack/package-extractor_v0.13.6", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.19`" + } + ] + } + }, + { + "version": "0.13.5", + "tag": "@rushstack/package-extractor_v0.13.5", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.24.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.18`" + } + ] + } + }, + { + "version": "0.13.4", + "tag": "@rushstack/package-extractor_v0.13.4", + "date": "Mon, 20 Apr 2026 15:15:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.17`" + } + ] + } + }, + { + "version": "0.13.3", + "tag": "@rushstack/package-extractor_v0.13.3", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.16`" + } + ] + } + }, + { + "version": "0.13.2", + "tag": "@rushstack/package-extractor_v0.13.2", + "date": "Sat, 18 Apr 2026 00:15:16 GMT", + "comments": { + "patch": [ + { + "comment": "Bump semver." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.7`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.15`" + } + ] + } + }, + { + "version": "0.13.1", + "tag": "@rushstack/package-extractor_v0.13.1", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.23.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.6`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.14`" + } + ] + } + }, + { + "version": "0.13.0", + "tag": "@rushstack/package-extractor_v0.13.0", + "date": "Tue, 14 Apr 2026 15:19:52 GMT", + "comments": { + "minor": [ + { + "comment": "Update `_collectFoldersAsync` to process all starting folders in a single shared queue instead of serially. Add `pnpmNodeModulesHoistingEnabled` option to `IExtractorSubspace` to skip virtual store hoisting lookup when hoisting is disabled." + } + ] + } + }, + { + "version": "0.12.15", + "tag": "@rushstack/package-extractor_v0.12.15", + "date": "Fri, 10 Apr 2026 22:46:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.13`" + } + ] + } + }, + { + "version": "0.12.14", + "tag": "@rushstack/package-extractor_v0.12.14", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.12`" + } + ] + } + }, + { + "version": "0.12.13", + "tag": "@rushstack/package-extractor_v0.12.13", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.11`" + } + ] + } + }, + { + "version": "0.12.12", + "tag": "@rushstack/package-extractor_v0.12.12", + "date": "Thu, 02 Apr 2026 00:14:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.10`" + } + ] + } + }, + { + "version": "0.12.11", + "tag": "@rushstack/package-extractor_v0.12.11", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.10`" + } + ] + } + }, + { + "version": "0.12.10", + "tag": "@rushstack/package-extractor_v0.12.10", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.9`" + } + ] + } + }, + { + "version": "0.12.9", + "tag": "@rushstack/package-extractor_v0.12.9", + "date": "Wed, 25 Mar 2026 01:00:26 GMT", + "comments": { + "patch": [ + { + "comment": "preventing duplicate-copy conflicts across npm-packlist versions" + } + ] + } + }, + { + "version": "0.12.8", + "tag": "@rushstack/package-extractor_v0.12.8", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "patch": [ + { + "comment": "Bump `minimatch` version from `10.2.1` to `10.2.3` to address CVE-2026-27903." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.8`" + } + ] + } + }, + { + "version": "0.12.7", + "tag": "@rushstack/package-extractor_v0.12.7", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.7`" + } + ] + } + }, + { + "version": "0.12.6", + "tag": "@rushstack/package-extractor_v0.12.6", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.6`" + } + ] + } + }, + { + "version": "0.12.5", + "tag": "@rushstack/package-extractor_v0.12.5", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.5`" + } + ] + } + }, + { + "version": "0.12.4", + "tag": "@rushstack/package-extractor_v0.12.4", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.4`" + } + ] + } + }, + { + "version": "0.12.3", + "tag": "@rushstack/package-extractor_v0.12.3", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "patch": [ + { + "comment": "Bump minimatch from 10.1.2 to 10.2.1" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.3`" + } + ] + } + }, + { + "version": "0.12.2", + "tag": "@rushstack/package-extractor_v0.12.2", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.2`" + } + ] + } + }, + { + "version": "0.12.1", + "tag": "@rushstack/package-extractor_v0.12.1", + "date": "Thu, 19 Feb 2026 01:30:06 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where `lib-` folders were excluded from publish." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.1`" + } + ] + } + }, + { + "version": "0.12.0", + "tag": "@rushstack/package-extractor_v0.12.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.22.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.12.0`" + } + ] + } + }, + { + "version": "0.11.16", + "tag": "@rushstack/package-extractor_v0.11.16", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.125`" + } + ] + } + }, + { + "version": "0.11.15", + "tag": "@rushstack/package-extractor_v0.11.15", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "patch": [ + { + "comment": "Update minimatch dependency from 10.0.3 to 10.1.2" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.124`" + } + ] + } + }, + { + "version": "0.11.14", + "tag": "@rushstack/package-extractor_v0.11.14", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.123`" + } + ] + } + }, + { + "version": "0.11.13", + "tag": "@rushstack/package-extractor_v0.11.13", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.122`" + } + ] + } + }, + { + "version": "0.11.12", + "tag": "@rushstack/package-extractor_v0.11.12", + "date": "Tue, 27 Jan 2026 16:13:30 GMT", + "comments": { + "patch": [ + { + "comment": "Upgrade npm-packlist from ~2.1.2 to ~5.1.3 to remove deprecated glob@7 and inflight dependencies" + } + ] + } + }, + { + "version": "0.11.11", + "tag": "@rushstack/package-extractor_v0.11.11", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.121`" + } + ] + } + }, + { + "version": "0.11.10", + "tag": "@rushstack/package-extractor_v0.11.10", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.21.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.120`" + } + ] + } + }, + { + "version": "0.11.9", + "tag": "@rushstack/package-extractor_v0.11.9", + "date": "Mon, 05 Jan 2026 16:12:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.20.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.119`" + } + ] + } + }, + { + "version": "0.11.8", + "tag": "@rushstack/package-extractor_v0.11.8", + "date": "Sat, 06 Dec 2025 01:12:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.7`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.118`" + } + ] + } + }, + { + "version": "0.11.7", + "tag": "@rushstack/package-extractor_v0.11.7", + "date": "Fri, 21 Nov 2025 16:13:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.6`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.117`" + } + ] + } + }, + { + "version": "0.11.6", + "tag": "@rushstack/package-extractor_v0.11.6", + "date": "Wed, 12 Nov 2025 01:12:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.5`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.116`" + } + ] + } + }, + { + "version": "0.11.5", + "tag": "@rushstack/package-extractor_v0.11.5", + "date": "Tue, 04 Nov 2025 08:15:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.4`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.115`" + } + ] + } + }, + { + "version": "0.11.4", + "tag": "@rushstack/package-extractor_v0.11.4", + "date": "Fri, 24 Oct 2025 00:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.3`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.114`" + } + ] + } + }, + { + "version": "0.11.3", + "tag": "@rushstack/package-extractor_v0.11.3", + "date": "Wed, 22 Oct 2025 00:57:54 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.113`" + } + ] + } + }, + { + "version": "0.11.2", + "tag": "@rushstack/package-extractor_v0.11.2", + "date": "Fri, 17 Oct 2025 23:22:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.1`" + } + ] + } + }, + { + "version": "0.11.1", + "tag": "@rushstack/package-extractor_v0.11.1", + "date": "Wed, 08 Oct 2025 00:13:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.112`" + } + ] + } + }, + { + "version": "0.11.0", + "tag": "@rushstack/package-extractor_v0.11.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.19.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.111`" + } + ] + } + }, + { + "version": "0.10.40", + "tag": "@rushstack/package-extractor_v0.10.40", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.18.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.110`" + } + ] + } + }, + { + "version": "0.10.39", + "tag": "@rushstack/package-extractor_v0.10.39", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.17.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.43`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.75.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.109`" + } + ] + } + }, + { + "version": "0.10.38", + "tag": "@rushstack/package-extractor_v0.10.38", + "date": "Fri, 12 Sep 2025 15:13:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.42`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.5`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.108`" + } + ] + } + }, + { + "version": "0.10.37", + "tag": "@rushstack/package-extractor_v0.10.37", + "date": "Thu, 11 Sep 2025 00:22:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.16.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.41`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.4`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.107`" + } + ] + } + }, + { + "version": "0.10.36", + "tag": "@rushstack/package-extractor_v0.10.36", + "date": "Fri, 29 Aug 2025 00:08:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.40`" + } + ] + } + }, + { + "version": "0.10.35", + "tag": "@rushstack/package-extractor_v0.10.35", + "date": "Tue, 26 Aug 2025 00:12:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.39`" + } + ] + } + }, + { + "version": "0.10.34", + "tag": "@rushstack/package-extractor_v0.10.34", + "date": "Tue, 19 Aug 2025 20:45:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.3`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.106`" + } + ] + } + }, + { + "version": "0.10.33", + "tag": "@rushstack/package-extractor_v0.10.33", + "date": "Fri, 01 Aug 2025 00:12:48 GMT", + "comments": { + "patch": [ + { + "comment": "Upgrades the minimatch dependency from ~3.0.3 to 10.0.3 across the entire Rush monorepo to address a Regular Expression Denial of Service (ReDoS) vulnerability in the underlying brace-expansion dependency." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.105`" + } + ] + } + }, + { + "version": "0.10.32", + "tag": "@rushstack/package-extractor_v0.10.32", + "date": "Sat, 26 Jul 2025 00:12:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.36`" + } + ] + } + }, + { + "version": "0.10.31", + "tag": "@rushstack/package-extractor_v0.10.31", + "date": "Wed, 23 Jul 2025 20:55:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.104`" + } + ] + } + }, + { + "version": "0.10.30", + "tag": "@rushstack/package-extractor_v0.10.30", + "date": "Sat, 21 Jun 2025 00:13:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.74.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.103`" + } + ] + } + }, + { + "version": "0.10.29", + "tag": "@rushstack/package-extractor_v0.10.29", + "date": "Tue, 13 May 2025 02:09:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.6`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.102`" + } + ] + } + }, + { + "version": "0.10.28", + "tag": "@rushstack/package-extractor_v0.10.28", + "date": "Thu, 01 May 2025 15:11:33 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.5`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.101`" + } + ] + } + }, + { + "version": "0.10.27", + "tag": "@rushstack/package-extractor_v0.10.27", + "date": "Thu, 01 May 2025 00:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.4`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.100`" + } + ] + } + }, + { + "version": "0.10.26", + "tag": "@rushstack/package-extractor_v0.10.26", + "date": "Fri, 25 Apr 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.3`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.99`" + } + ] + } + }, + { + "version": "0.10.25", + "tag": "@rushstack/package-extractor_v0.10.25", + "date": "Mon, 21 Apr 2025 22:24:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.98`" + } + ] + } + }, + { + "version": "0.10.24", + "tag": "@rushstack/package-extractor_v0.10.24", + "date": "Thu, 17 Apr 2025 00:11:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.97`" + } + ] + } + }, + { + "version": "0.10.23", + "tag": "@rushstack/package-extractor_v0.10.23", + "date": "Tue, 15 Apr 2025 15:11:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.73.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.96`" + } + ] + } + }, + { + "version": "0.10.22", + "tag": "@rushstack/package-extractor_v0.10.22", + "date": "Wed, 09 Apr 2025 00:11:03 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.72.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.95`" + } + ] + } + }, + { + "version": "0.10.21", + "tag": "@rushstack/package-extractor_v0.10.21", + "date": "Fri, 04 Apr 2025 18:34:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.94`" + } + ] + } + }, + { + "version": "0.10.20", + "tag": "@rushstack/package-extractor_v0.10.20", + "date": "Tue, 25 Mar 2025 15:11:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.2`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.93`" + } + ] + } + }, + { + "version": "0.10.19", + "tag": "@rushstack/package-extractor_v0.10.19", + "date": "Wed, 12 Mar 2025 22:41:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.71.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.92`" + } + ] + } + }, + { + "version": "0.10.18", + "tag": "@rushstack/package-extractor_v0.10.18", + "date": "Wed, 12 Mar 2025 00:11:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.91`" + } + ] + } + }, + { + "version": "0.10.17", + "tag": "@rushstack/package-extractor_v0.10.17", + "date": "Tue, 11 Mar 2025 02:12:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.1`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.70.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.90`" + } + ] + } + }, + { + "version": "0.10.16", + "tag": "@rushstack/package-extractor_v0.10.16", + "date": "Tue, 11 Mar 2025 00:11:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.3`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.89`" + } + ] + } + }, + { + "version": "0.10.15", + "tag": "@rushstack/package-extractor_v0.10.15", + "date": "Sat, 01 Mar 2025 05:00:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.88`" + } + ] + } + }, + { + "version": "0.10.14", + "tag": "@rushstack/package-extractor_v0.10.14", + "date": "Thu, 27 Feb 2025 01:10:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.87`" + } + ] + } + }, + { + "version": "0.10.13", + "tag": "@rushstack/package-extractor_v0.10.13", + "date": "Wed, 26 Feb 2025 16:11:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.69.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.86`" + } + ] + } + }, + { + "version": "0.10.12", + "tag": "@rushstack/package-extractor_v0.10.12", + "date": "Sat, 22 Feb 2025 01:11:12 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.18`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.85`" + } + ] + } + }, + { + "version": "0.10.11", + "tag": "@rushstack/package-extractor_v0.10.11", + "date": "Wed, 19 Feb 2025 18:53:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.17`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.84`" + } + ] + } + }, + { + "version": "0.10.10", + "tag": "@rushstack/package-extractor_v0.10.10", + "date": "Wed, 12 Feb 2025 01:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.15.0`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.16`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.83`" + } + ] + } + }, + { + "version": "0.10.9", + "tag": "@rushstack/package-extractor_v0.10.9", + "date": "Thu, 30 Jan 2025 16:10:36 GMT", + "comments": { + "patch": [ + { + "comment": "Prefer `os.availableParallelism()` to `os.cpus().length`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.15`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.82`" + } + ] + } + }, + { + "version": "0.10.8", + "tag": "@rushstack/package-extractor_v0.10.8", + "date": "Thu, 30 Jan 2025 01:11:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.6`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.14`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.81`" + } + ] + } + }, + { + "version": "0.10.7", + "tag": "@rushstack/package-extractor_v0.10.7", + "date": "Thu, 09 Jan 2025 01:10:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.5`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.13`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.80`" + } + ] + } + }, + { + "version": "0.10.6", + "tag": "@rushstack/package-extractor_v0.10.6", + "date": "Tue, 07 Jan 2025 22:17:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.12`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.79`" + } + ] + } + }, + { + "version": "0.10.5", + "tag": "@rushstack/package-extractor_v0.10.5", + "date": "Sat, 14 Dec 2024 01:11:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.4`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.11`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.78`" + } + ] + } + }, + { + "version": "0.10.4", + "tag": "@rushstack/package-extractor_v0.10.4", + "date": "Mon, 09 Dec 2024 20:31:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.10`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.77`" + } + ] + } + }, + { + "version": "0.10.3", + "tag": "@rushstack/package-extractor_v0.10.3", + "date": "Tue, 03 Dec 2024 16:11:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.9`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.76`" + } + ] + } + }, + { + "version": "0.10.2", + "tag": "@rushstack/package-extractor_v0.10.2", + "date": "Sat, 23 Nov 2024 01:18:55 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.8`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.75`" + } + ] + } + }, + { + "version": "0.10.1", + "tag": "@rushstack/package-extractor_v0.10.1", + "date": "Fri, 22 Nov 2024 01:10:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.3`" + }, + { + "comment": "Updating dependency \"@rushstack/ts-command-line\" to `4.23.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.7`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.74`" + } + ] + } + }, + { + "version": "0.10.0", + "tag": "@rushstack/package-extractor_v0.10.0", + "date": "Thu, 24 Oct 2024 15:11:19 GMT", + "comments": { + "minor": [ + { + "comment": "Add bin linking support when calling the create-links.js script with the \"--link-bins\" parameter" + } + ] + } + }, + { + "version": "0.9.9", + "tag": "@rushstack/package-extractor_v0.9.9", + "date": "Thu, 24 Oct 2024 00:15:48 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.6`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.73`" + } + ] + } + }, + { + "version": "0.9.8", + "tag": "@rushstack/package-extractor_v0.9.8", + "date": "Tue, 22 Oct 2024 22:12:40 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where the `node_modules/.bin` folder symlinks were not created for extracted packages when using the \"default\" link creation mode" + } + ] + } + }, + { + "version": "0.9.7", + "tag": "@rushstack/package-extractor_v0.9.7", + "date": "Mon, 21 Oct 2024 18:50:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.5`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.72`" + } + ] + } + }, + { + "version": "0.9.6", + "tag": "@rushstack/package-extractor_v0.9.6", + "date": "Thu, 17 Oct 2024 08:35:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.4`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.71`" + } + ] + } + }, + { + "version": "0.9.5", + "tag": "@rushstack/package-extractor_v0.9.5", + "date": "Tue, 15 Oct 2024 00:12:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.3`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.70`" + } + ] + } + }, + { + "version": "0.9.4", + "tag": "@rushstack/package-extractor_v0.9.4", + "date": "Wed, 02 Oct 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.69`" + } + ] + } + }, + { + "version": "0.9.3", + "tag": "@rushstack/package-extractor_v0.9.3", + "date": "Tue, 01 Oct 2024 00:11:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.68`" + } + ] + } + }, + { + "version": "0.9.2", + "tag": "@rushstack/package-extractor_v0.9.2", + "date": "Mon, 30 Sep 2024 15:12:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.68.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.67`" + } + ] + } + }, + { + "version": "0.9.1", + "tag": "@rushstack/package-extractor_v0.9.1", + "date": "Sat, 21 Sep 2024 00:10:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.12`" + } + ] + } + }, + { + "version": "0.9.0", + "tag": "@rushstack/package-extractor_v0.9.0", + "date": "Mon, 16 Sep 2024 02:09:00 GMT", + "comments": { + "minor": [ + { + "comment": "Add a `files` field to the `extractor-metadata.json` file" + } + ] + } + }, + { + "version": "0.8.1", + "tag": "@rushstack/package-extractor_v0.8.1", + "date": "Fri, 13 Sep 2024 00:11:43 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.66`" + } + ] + } + }, + { + "version": "0.8.0", + "tag": "@rushstack/package-extractor_v0.8.0", + "date": "Wed, 11 Sep 2024 19:54:47 GMT", + "comments": { + "minor": [ + { + "comment": "Add the ability to change where the \"create-links.js\" file and the associated metadata file are generated when running in the \"script\" linkCreation mode" + } + ] + } + }, + { + "version": "0.7.26", + "tag": "@rushstack/package-extractor_v0.7.26", + "date": "Tue, 10 Sep 2024 20:08:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.65`" + } + ] + } + }, + { + "version": "0.7.25", + "tag": "@rushstack/package-extractor_v0.7.25", + "date": "Wed, 21 Aug 2024 05:43:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.7.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.14.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.67.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.64`" + } + ] + } + }, + { + "version": "0.7.24", + "tag": "@rushstack/package-extractor_v0.7.24", + "date": "Mon, 12 Aug 2024 22:16:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.26`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.63`" + } + ] + } + }, + { + "version": "0.7.23", + "tag": "@rushstack/package-extractor_v0.7.23", + "date": "Fri, 02 Aug 2024 17:26:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.25`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.62`" + } + ] + } + }, + { + "version": "0.7.22", + "tag": "@rushstack/package-extractor_v0.7.22", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.24`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.61`" + } + ] + } + }, + { + "version": "0.7.21", + "tag": "@rushstack/package-extractor_v0.7.21", + "date": "Wed, 24 Jul 2024 00:12:14 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.23`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.60`" + } + ] + } + }, + { + "version": "0.7.20", + "tag": "@rushstack/package-extractor_v0.7.20", + "date": "Wed, 17 Jul 2024 06:55:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.22`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.59`" + } + ] + } + }, + { + "version": "0.7.19", + "tag": "@rushstack/package-extractor_v0.7.19", + "date": "Wed, 17 Jul 2024 00:11:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.21`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.58`" + } + ] + } + }, + { + "version": "0.7.18", + "tag": "@rushstack/package-extractor_v0.7.18", + "date": "Tue, 16 Jul 2024 00:36:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.5.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.20`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.57`" + } + ] + } + }, + { + "version": "0.7.17", + "tag": "@rushstack/package-extractor_v0.7.17", + "date": "Thu, 27 Jun 2024 21:01:36 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.19`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.56`" + } + ] + } + }, + { + "version": "0.7.16", + "tag": "@rushstack/package-extractor_v0.7.16", + "date": "Fri, 07 Jun 2024 15:10:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.10.0`" + } + ] + } + }, + { + "version": "0.7.15", + "tag": "@rushstack/package-extractor_v0.7.15", + "date": "Mon, 03 Jun 2024 23:43:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.55`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.18`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.55`" + } + ] + } + }, + { + "version": "0.7.14", + "tag": "@rushstack/package-extractor_v0.7.14", + "date": "Thu, 30 May 2024 00:13:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.13.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.54`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.17`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.54`" + } + ] + } + }, + { + "version": "0.7.13", + "tag": "@rushstack/package-extractor_v0.7.13", + "date": "Wed, 29 May 2024 02:03:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.4.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.53`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.16`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.53`" + } + ] + } + }, + { + "version": "0.7.12", + "tag": "@rushstack/package-extractor_v0.7.12", + "date": "Wed, 29 May 2024 00:10:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.52`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.15`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.52`" + } + ] + } + }, + { + "version": "0.7.11", + "tag": "@rushstack/package-extractor_v0.7.11", + "date": "Tue, 28 May 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.51`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.14`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.51`" + } + ] + } + }, + { + "version": "0.7.10", + "tag": "@rushstack/package-extractor_v0.7.10", + "date": "Tue, 28 May 2024 00:09:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.50`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.13`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.50`" + } + ] + } + }, + { + "version": "0.7.9", + "tag": "@rushstack/package-extractor_v0.7.9", + "date": "Sat, 25 May 2024 04:54:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.12.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.49`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.12`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.49`" + } + ] + } + }, + { + "version": "0.7.8", + "tag": "@rushstack/package-extractor_v0.7.8", + "date": "Fri, 24 May 2024 00:15:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.48`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.11`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.48`" + } + ] + } + }, + { + "version": "0.7.7", + "tag": "@rushstack/package-extractor_v0.7.7", + "date": "Thu, 23 May 2024 02:26:56 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `5.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.47`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.10`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.47`" + } + ] + } + }, + { + "version": "0.7.6", + "tag": "@rushstack/package-extractor_v0.7.6", + "date": "Thu, 16 May 2024 15:10:22 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.46`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.9`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.46`" + } + ] + } + }, + { + "version": "0.7.5", + "tag": "@rushstack/package-extractor_v0.7.5", + "date": "Wed, 15 May 2024 23:42:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.11.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.45`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.8`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.45`" + } + ] + } + }, + { + "version": "0.7.4", + "tag": "@rushstack/package-extractor_v0.7.4", + "date": "Wed, 15 May 2024 06:04:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.3.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.44`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.7`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.44`" + } + ] + } + }, + { + "version": "0.7.3", + "tag": "@rushstack/package-extractor_v0.7.3", + "date": "Fri, 10 May 2024 05:33:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.43`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.6`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.43`" + } + ] + } + }, + { + "version": "0.7.2", + "tag": "@rushstack/package-extractor_v0.7.2", + "date": "Wed, 08 May 2024 22:23:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.42`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.5`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.42`" + } + ] + } + }, + { + "version": "0.7.1", + "tag": "@rushstack/package-extractor_v0.7.1", + "date": "Mon, 06 May 2024 15:11:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.41`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.4`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.41`" + } + ] + } + }, + { + "version": "0.7.0", + "tag": "@rushstack/package-extractor_v0.7.0", + "date": "Thu, 18 Apr 2024 23:19:43 GMT", + "comments": { + "minor": [ + { + "comment": "Add support for Rush subspaces" + } + ] + } + }, + { + "version": "0.6.43", + "tag": "@rushstack/package-extractor_v0.6.43", + "date": "Wed, 10 Apr 2024 15:10:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.40`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.3`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.40`" + } + ] + } + }, + { + "version": "0.6.42", + "tag": "@rushstack/package-extractor_v0.6.42", + "date": "Tue, 19 Mar 2024 15:10:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.39`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.39`" + } + ] + } + }, + { + "version": "0.6.41", + "tag": "@rushstack/package-extractor_v0.6.41", + "date": "Fri, 15 Mar 2024 00:12:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.38`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.38`" + } + ] + } + }, + { + "version": "0.6.40", + "tag": "@rushstack/package-extractor_v0.6.40", + "date": "Tue, 05 Mar 2024 01:19:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.37`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.37`" + } + ] + } + }, + { + "version": "0.6.39", + "tag": "@rushstack/package-extractor_v0.6.39", + "date": "Sun, 03 Mar 2024 20:58:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.10`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.36`" + } + ] + } + }, + { + "version": "0.6.38", + "tag": "@rushstack/package-extractor_v0.6.38", + "date": "Sat, 02 Mar 2024 02:22:24 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.9`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.35`" + } + ] + } + }, + { + "version": "0.6.37", + "tag": "@rushstack/package-extractor_v0.6.37", + "date": "Fri, 01 Mar 2024 01:10:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.8`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.34`" + } + ] + } + }, + { + "version": "0.6.36", + "tag": "@rushstack/package-extractor_v0.6.36", + "date": "Thu, 29 Feb 2024 07:11:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.7`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.33`" + } + ] + } + }, + { + "version": "0.6.35", + "tag": "@rushstack/package-extractor_v0.6.35", + "date": "Wed, 28 Feb 2024 16:09:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.6`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.32`" + } + ] + } + }, + { + "version": "0.6.34", + "tag": "@rushstack/package-extractor_v0.6.34", + "date": "Sat, 24 Feb 2024 23:02:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.10.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.5`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.31`" + } + ] + } + }, + { + "version": "0.6.33", + "tag": "@rushstack/package-extractor_v0.6.33", + "date": "Thu, 22 Feb 2024 01:36:09 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.4`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.30`" + } + ] + } + }, + { + "version": "0.6.32", + "tag": "@rushstack/package-extractor_v0.6.32", + "date": "Wed, 21 Feb 2024 21:45:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.3`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.29`" + } + ] + } + }, + { + "version": "0.6.31", + "tag": "@rushstack/package-extractor_v0.6.31", + "date": "Wed, 21 Feb 2024 08:55:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.28`" + } + ] + } + }, + { + "version": "0.6.30", + "tag": "@rushstack/package-extractor_v0.6.30", + "date": "Tue, 20 Feb 2024 21:45:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.27`" + } + ] + } + }, + { + "version": "0.6.29", + "tag": "@rushstack/package-extractor_v0.6.29", + "date": "Tue, 20 Feb 2024 16:10:53 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.26`" + } + ] + } + }, + { + "version": "0.6.28", + "tag": "@rushstack/package-extractor_v0.6.28", + "date": "Mon, 19 Feb 2024 21:54:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `4.0.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.8`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.25`" + } + ] + } + }, + { + "version": "0.6.27", + "tag": "@rushstack/package-extractor_v0.6.27", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix broken link to API documentation" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.7`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.24`" + } + ] + } + }, + { + "version": "0.6.26", + "tag": "@rushstack/package-extractor_v0.6.26", + "date": "Thu, 08 Feb 2024 01:09:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.66.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.6`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.23`" + } + ] + } + }, + { + "version": "0.6.25", + "tag": "@rushstack/package-extractor_v0.6.25", + "date": "Wed, 07 Feb 2024 01:11:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.5`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.22`" + } + ] + } + }, + { + "version": "0.6.24", + "tag": "@rushstack/package-extractor_v0.6.24", + "date": "Mon, 05 Feb 2024 23:46:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.65.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.4`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.21`" + } + ] + } + }, + { + "version": "0.6.23", + "tag": "@rushstack/package-extractor_v0.6.23", + "date": "Thu, 25 Jan 2024 01:09:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.3`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.20`" + } + ] + } + }, + { + "version": "0.6.22", + "tag": "@rushstack/package-extractor_v0.6.22", + "date": "Tue, 23 Jan 2024 20:12:58 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.19`" + } + ] + } + }, + { + "version": "0.6.21", + "tag": "@rushstack/package-extractor_v0.6.21", + "date": "Tue, 23 Jan 2024 16:15:06 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.18`" + } + ] + } + }, + { + "version": "0.6.20", + "tag": "@rushstack/package-extractor_v0.6.20", + "date": "Tue, 16 Jan 2024 18:30:11 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.64.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.17`" + } + ] + } + }, + { + "version": "0.6.19", + "tag": "@rushstack/package-extractor_v0.6.19", + "date": "Wed, 03 Jan 2024 00:31:18 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.6`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.16`" + } + ] + } + }, + { + "version": "0.6.18", + "tag": "@rushstack/package-extractor_v0.6.18", + "date": "Wed, 20 Dec 2023 01:09:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.5`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.15`" + } + ] + } + }, + { + "version": "0.6.17", + "tag": "@rushstack/package-extractor_v0.6.17", + "date": "Tue, 12 Dec 2023 00:20:33 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue with the `folderToCopy` option, where the folder contents would be copied into a subfolder instead of into the target folder root." + } + ] + } + }, + { + "version": "0.6.16", + "tag": "@rushstack/package-extractor_v0.6.16", + "date": "Thu, 07 Dec 2023 03:44:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.4`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.14`" + } + ] + } + }, + { + "version": "0.6.15", + "tag": "@rushstack/package-extractor_v0.6.15", + "date": "Tue, 05 Dec 2023 01:10:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.3`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.13`" + } + ] + } + }, + { + "version": "0.6.14", + "tag": "@rushstack/package-extractor_v0.6.14", + "date": "Thu, 16 Nov 2023 01:09:56 GMT", + "comments": { + "patch": [ + { + "comment": "Links that target a path outside of the source directory can now be ignored using \"patternsToInclude\" and \"patternsToExclude\" options" + } + ] + } + }, + { + "version": "0.6.13", + "tag": "@rushstack/package-extractor_v0.6.13", + "date": "Fri, 10 Nov 2023 18:02:04 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.12`" + } + ] + } + }, + { + "version": "0.6.12", + "tag": "@rushstack/package-extractor_v0.6.12", + "date": "Wed, 01 Nov 2023 23:11:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix line endings in published package." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.11`" + } + ] + } + }, + { + "version": "0.6.11", + "tag": "@rushstack/package-extractor_v0.6.11", + "date": "Mon, 30 Oct 2023 23:36:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.63.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.10`" + } + ] + } + }, + { + "version": "0.6.10", + "tag": "@rushstack/package-extractor_v0.6.10", + "date": "Sun, 01 Oct 2023 02:56:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.3`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.9`" + } + ] + } + }, + { + "version": "0.6.9", + "tag": "@rushstack/package-extractor_v0.6.9", + "date": "Sat, 30 Sep 2023 00:20:51 GMT", + "comments": { + "patch": [ + { + "comment": "Ensure the \"folderToCopy\" field is included in generated archives" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.8`" + } + ] + } + }, + { + "version": "0.6.8", + "tag": "@rushstack/package-extractor_v0.6.8", + "date": "Thu, 28 Sep 2023 20:53:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.7`" + } + ] + } + }, + { + "version": "0.6.7", + "tag": "@rushstack/package-extractor_v0.6.7", + "date": "Wed, 27 Sep 2023 00:21:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.62.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.6`" + } + ] + } + }, + { + "version": "0.6.6", + "tag": "@rushstack/package-extractor_v0.6.6", + "date": "Tue, 26 Sep 2023 21:02:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.3`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.5`" + } + ] + } + }, + { + "version": "0.6.5", + "tag": "@rushstack/package-extractor_v0.6.5", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.1`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.4`" + } + ] + } + }, + { + "version": "0.6.4", + "tag": "@rushstack/package-extractor_v0.6.4", + "date": "Mon, 25 Sep 2023 23:38:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.3`" + } + ] + } + }, + { + "version": "0.6.3", + "tag": "@rushstack/package-extractor_v0.6.3", + "date": "Fri, 22 Sep 2023 00:05:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.61.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.2`" + } + ] + } + }, + { + "version": "0.6.2", + "tag": "@rushstack/package-extractor_v0.6.2", + "date": "Tue, 19 Sep 2023 15:21:52 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.24`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.1`" + } + ] + } + }, + { + "version": "0.6.1", + "tag": "@rushstack/package-extractor_v0.6.1", + "date": "Tue, 19 Sep 2023 00:36:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.7.0`" + } + ] + } + }, + { + "version": "0.6.0", + "tag": "@rushstack/package-extractor_v0.6.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.60.0`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.6.0`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.9.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.59.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.11.0`" + } + ] + } + }, + { + "version": "0.5.3", + "tag": "@rushstack/package-extractor_v0.5.3", + "date": "Wed, 13 Sep 2023 00:32:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.15`" + } + ] + } + }, + { + "version": "0.5.2", + "tag": "@rushstack/package-extractor_v0.5.2", + "date": "Wed, 06 Sep 2023 19:00:39 GMT", + "comments": { + "patch": [ + { + "comment": "Fix an issue where subdirectory inclusion patterns (ex. \"src/subdir/**/*\") would get ignored during extraction" + } + ] + } + }, + { + "version": "0.5.1", + "tag": "@rushstack/package-extractor_v0.5.1", + "date": "Thu, 24 Aug 2023 15:20:46 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.38`" + } + ] + } + }, + { + "version": "0.5.0", + "tag": "@rushstack/package-extractor_v0.5.0", + "date": "Wed, 23 Aug 2023 00:20:45 GMT", + "comments": { + "minor": [ + { + "comment": "Add option field dependenciesConfigurations in PackageExtractor to filter files for third party dependencies" + } + ] + } + }, + { + "version": "0.4.1", + "tag": "@rushstack/package-extractor_v0.4.1", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.7`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.37`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.40`" + } + ] + } + }, + { + "version": "0.4.0", + "tag": "@rushstack/package-extractor_v0.4.0", + "date": "Fri, 04 Aug 2023 15:22:44 GMT", + "comments": { + "minor": [ + { + "comment": "Include an API for getting files that are included in a npm package." + } + ] + } + }, + { + "version": "0.3.13", + "tag": "@rushstack/package-extractor_v0.3.13", + "date": "Mon, 31 Jul 2023 15:19:05 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.36`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.13`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.39`" + } + ] + } + }, + { + "version": "0.3.12", + "tag": "@rushstack/package-extractor_v0.3.12", + "date": "Sat, 29 Jul 2023 00:22:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.35`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.38`" + } + ] + } + }, + { + "version": "0.3.11", + "tag": "@rushstack/package-extractor_v0.3.11", + "date": "Thu, 20 Jul 2023 20:47:28 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.34`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.58.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.37`" + } + ] + } + }, + { + "version": "0.3.10", + "tag": "@rushstack/package-extractor_v0.3.10", + "date": "Wed, 19 Jul 2023 00:20:31 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.6`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.33`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.36`" + } + ] + } + }, + { + "version": "0.3.9", + "tag": "@rushstack/package-extractor_v0.3.9", + "date": "Fri, 14 Jul 2023 15:20:45 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.32`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.9`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.35`" + } + ] + } + }, + { + "version": "0.3.8", + "tag": "@rushstack/package-extractor_v0.3.8", + "date": "Thu, 13 Jul 2023 00:22:37 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.31`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.57.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.34`" + } + ] + } + }, + { + "version": "0.3.7", + "tag": "@rushstack/package-extractor_v0.3.7", + "date": "Wed, 12 Jul 2023 15:20:39 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.30`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.15`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.3`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.33`" + } + ] + } + }, + { + "version": "0.3.6", + "tag": "@rushstack/package-extractor_v0.3.6", + "date": "Wed, 12 Jul 2023 00:23:29 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.29`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.6`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.32`" + } + ] + } + }, + { + "version": "0.3.5", + "tag": "@rushstack/package-extractor_v0.3.5", + "date": "Fri, 07 Jul 2023 00:19:32 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.28`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.31`" + } + ] + } + }, + { + "version": "0.3.4", + "tag": "@rushstack/package-extractor_v0.3.4", + "date": "Thu, 06 Jul 2023 00:16:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.5`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.27`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.12`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.30`" + } + ] + } + }, + { + "version": "0.3.3", + "tag": "@rushstack/package-extractor_v0.3.3", + "date": "Tue, 04 Jul 2023 00:18:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.26`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.11`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.3`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.29`" + } + ] + } + }, + { + "version": "0.3.2", + "tag": "@rushstack/package-extractor_v0.3.2", + "date": "Mon, 26 Jun 2023 23:45:21 GMT", + "comments": { + "patch": [ + { + "comment": "Fix patternsToInclude and patternsToExclude filters when provided patterns target subdirectories of folders that do not match the provided patterns" + } + ] + } + }, + { + "version": "0.3.1", + "tag": "@rushstack/package-extractor_v0.3.1", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.25`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.56.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.28`" + } + ] + } + }, + { + "version": "0.3.0", + "tag": "@rushstack/package-extractor_v0.3.0", + "date": "Sat, 17 Jun 2023 00:21:54 GMT", + "comments": { + "minor": [ + { + "comment": "Allow for include and exclude filters to be provided for projects. This allows for an additional layer of filtering when extracting a package." + } + ] + } + }, + { + "version": "0.2.18", + "tag": "@rushstack/package-extractor_v0.2.18", + "date": "Thu, 15 Jun 2023 00:21:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.4`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.24`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.27`" + } + ] + } + }, + { + "version": "0.2.17", + "tag": "@rushstack/package-extractor_v0.2.17", + "date": "Wed, 14 Jun 2023 00:19:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.23`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.8.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.26`" + } + ] + } + }, + { + "version": "0.2.16", + "tag": "@rushstack/package-extractor_v0.2.16", + "date": "Tue, 13 Jun 2023 15:17:20 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.22`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.7`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.10`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.55.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.25`" + } + ] + } + }, + { + "version": "0.2.15", + "tag": "@rushstack/package-extractor_v0.2.15", + "date": "Tue, 13 Jun 2023 01:49:01 GMT", + "comments": { + "patch": [ + { + "comment": "Bump webpack to v5.82.1" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.21`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.9`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.54.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.24`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/package-extractor_v0.2.14", + "date": "Fri, 09 Jun 2023 18:05:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.20`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.8`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.23`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/package-extractor_v0.2.13", + "date": "Fri, 09 Jun 2023 15:23:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.19`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.7`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.22`" + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/package-extractor_v0.2.12", + "date": "Fri, 09 Jun 2023 00:19:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.18`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.6`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.53.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.21`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/package-extractor_v0.2.11", + "date": "Thu, 08 Jun 2023 15:21:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.17`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.2`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.5`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.20`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/package-extractor_v0.2.10", + "date": "Thu, 08 Jun 2023 00:20:02 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.16`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.4`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.19`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/package-extractor_v0.2.9", + "date": "Wed, 07 Jun 2023 22:45:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/node-core-library\" to `3.59.3`" + }, + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.15`" + }, + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.2.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.3`" + }, + { + "comment": "Updating dependency \"@rushstack/heft\" to `0.52.0`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.18`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/package-extractor_v0.2.8", + "date": "Tue, 06 Jun 2023 02:52:51 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.14`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.1.0`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.2`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.17`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/package-extractor_v0.2.7", + "date": "Mon, 05 Jun 2023 21:45:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/terminal\" to `0.5.13`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-node-rig\" to `2.0.1`" + }, + { + "comment": "Updating dependency \"@rushstack/heft-webpack5-plugin\" to `0.7.1`" + }, + { + "comment": "Updating dependency \"@rushstack/webpack-preserve-dynamic-require-plugin\" to `0.10.16`" + } + ] + } + }, { "version": "0.2.6", "tag": "@rushstack/package-extractor_v0.2.6", diff --git a/libraries/package-extractor/CHANGELOG.md b/libraries/package-extractor/CHANGELOG.md index 7d3a1ec71cf..0c95d490ed0 100644 --- a/libraries/package-extractor/CHANGELOG.md +++ b/libraries/package-extractor/CHANGELOG.md @@ -1,6 +1,1070 @@ # Change Log - @rushstack/package-extractor -This log was last generated on Fri, 02 Jun 2023 02:01:12 GMT and should not be manually modified. +This log was last generated on Tue, 21 Jul 2026 02:53:22 GMT and should not be manually modified. + +## 0.13.10 +Tue, 21 Jul 2026 02:53:22 GMT + +_Version update only_ + +## 0.13.9 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 0.13.8 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.13.7 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.13.6 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 0.13.5 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.13.4 +Mon, 20 Apr 2026 15:15:24 GMT + +_Version update only_ + +## 0.13.3 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 0.13.2 +Sat, 18 Apr 2026 00:15:16 GMT + +### Patches + +- Bump semver. + +## 0.13.1 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.13.0 +Tue, 14 Apr 2026 15:19:52 GMT + +### Minor changes + +- Update `_collectFoldersAsync` to process all starting folders in a single shared queue instead of serially. Add `pnpmNodeModulesHoistingEnabled` option to `IExtractorSubspace` to skip virtual store hoisting lookup when hoisting is disabled. + +## 0.12.15 +Fri, 10 Apr 2026 22:46:34 GMT + +_Version update only_ + +## 0.12.14 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.12.13 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 0.12.12 +Thu, 02 Apr 2026 00:14:38 GMT + +_Version update only_ + +## 0.12.11 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 0.12.10 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.12.9 +Wed, 25 Mar 2026 01:00:26 GMT + +### Patches + +- preventing duplicate-copy conflicts across npm-packlist versions + +## 0.12.8 +Mon, 09 Mar 2026 15:14:08 GMT + +### Patches + +- Bump `minimatch` version from `10.2.1` to `10.2.3` to address CVE-2026-27903. + +## 0.12.7 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.12.6 +Wed, 25 Feb 2026 00:34:29 GMT + +_Version update only_ + +## 0.12.5 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.12.4 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 0.12.3 +Fri, 20 Feb 2026 16:14:49 GMT + +### Patches + +- Bump minimatch from 10.1.2 to 10.2.1 + +## 0.12.2 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.12.1 +Thu, 19 Feb 2026 01:30:06 GMT + +### Patches + +- Fix an issue where `lib-` folders were excluded from publish. + +## 0.12.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.11.16 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 0.11.15 +Wed, 04 Feb 2026 20:42:47 GMT + +### Patches + +- Update minimatch dependency from 10.0.3 to 10.1.2 + +## 0.11.14 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 0.11.13 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 0.11.12 +Tue, 27 Jan 2026 16:13:30 GMT + +### Patches + +- Upgrade npm-packlist from ~2.1.2 to ~5.1.3 to remove deprecated glob@7 and inflight dependencies + +## 0.11.11 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 0.11.10 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 0.11.9 +Mon, 05 Jan 2026 16:12:49 GMT + +_Version update only_ + +## 0.11.8 +Sat, 06 Dec 2025 01:12:28 GMT + +_Version update only_ + +## 0.11.7 +Fri, 21 Nov 2025 16:13:56 GMT + +_Version update only_ + +## 0.11.6 +Wed, 12 Nov 2025 01:12:56 GMT + +_Version update only_ + +## 0.11.5 +Tue, 04 Nov 2025 08:15:15 GMT + +_Version update only_ + +## 0.11.4 +Fri, 24 Oct 2025 00:13:38 GMT + +_Version update only_ + +## 0.11.3 +Wed, 22 Oct 2025 00:57:54 GMT + +_Version update only_ + +## 0.11.2 +Fri, 17 Oct 2025 23:22:33 GMT + +_Version update only_ + +## 0.11.1 +Wed, 08 Oct 2025 00:13:28 GMT + +_Version update only_ + +## 0.11.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 0.10.40 +Tue, 30 Sep 2025 23:57:45 GMT + +_Version update only_ + +## 0.10.39 +Tue, 30 Sep 2025 20:33:51 GMT + +_Version update only_ + +## 0.10.38 +Fri, 12 Sep 2025 15:13:07 GMT + +_Version update only_ + +## 0.10.37 +Thu, 11 Sep 2025 00:22:31 GMT + +_Version update only_ + +## 0.10.36 +Fri, 29 Aug 2025 00:08:01 GMT + +_Version update only_ + +## 0.10.35 +Tue, 26 Aug 2025 00:12:57 GMT + +_Version update only_ + +## 0.10.34 +Tue, 19 Aug 2025 20:45:02 GMT + +_Version update only_ + +## 0.10.33 +Fri, 01 Aug 2025 00:12:48 GMT + +### Patches + +- Upgrades the minimatch dependency from ~3.0.3 to 10.0.3 across the entire Rush monorepo to address a Regular Expression Denial of Service (ReDoS) vulnerability in the underlying brace-expansion dependency. + +## 0.10.32 +Sat, 26 Jul 2025 00:12:22 GMT + +_Version update only_ + +## 0.10.31 +Wed, 23 Jul 2025 20:55:57 GMT + +_Version update only_ + +## 0.10.30 +Sat, 21 Jun 2025 00:13:15 GMT + +_Version update only_ + +## 0.10.29 +Tue, 13 May 2025 02:09:20 GMT + +_Version update only_ + +## 0.10.28 +Thu, 01 May 2025 15:11:33 GMT + +_Version update only_ + +## 0.10.27 +Thu, 01 May 2025 00:11:12 GMT + +_Version update only_ + +## 0.10.26 +Fri, 25 Apr 2025 00:11:32 GMT + +_Version update only_ + +## 0.10.25 +Mon, 21 Apr 2025 22:24:25 GMT + +_Version update only_ + +## 0.10.24 +Thu, 17 Apr 2025 00:11:21 GMT + +_Version update only_ + +## 0.10.23 +Tue, 15 Apr 2025 15:11:57 GMT + +_Version update only_ + +## 0.10.22 +Wed, 09 Apr 2025 00:11:03 GMT + +_Version update only_ + +## 0.10.21 +Fri, 04 Apr 2025 18:34:35 GMT + +_Version update only_ + +## 0.10.20 +Tue, 25 Mar 2025 15:11:16 GMT + +_Version update only_ + +## 0.10.19 +Wed, 12 Mar 2025 22:41:36 GMT + +_Version update only_ + +## 0.10.18 +Wed, 12 Mar 2025 00:11:32 GMT + +_Version update only_ + +## 0.10.17 +Tue, 11 Mar 2025 02:12:34 GMT + +_Version update only_ + +## 0.10.16 +Tue, 11 Mar 2025 00:11:25 GMT + +_Version update only_ + +## 0.10.15 +Sat, 01 Mar 2025 05:00:09 GMT + +_Version update only_ + +## 0.10.14 +Thu, 27 Feb 2025 01:10:39 GMT + +_Version update only_ + +## 0.10.13 +Wed, 26 Feb 2025 16:11:11 GMT + +_Version update only_ + +## 0.10.12 +Sat, 22 Feb 2025 01:11:12 GMT + +_Version update only_ + +## 0.10.11 +Wed, 19 Feb 2025 18:53:48 GMT + +_Version update only_ + +## 0.10.10 +Wed, 12 Feb 2025 01:10:52 GMT + +_Version update only_ + +## 0.10.9 +Thu, 30 Jan 2025 16:10:36 GMT + +### Patches + +- Prefer `os.availableParallelism()` to `os.cpus().length`. + +## 0.10.8 +Thu, 30 Jan 2025 01:11:42 GMT + +_Version update only_ + +## 0.10.7 +Thu, 09 Jan 2025 01:10:10 GMT + +_Version update only_ + +## 0.10.6 +Tue, 07 Jan 2025 22:17:32 GMT + +_Version update only_ + +## 0.10.5 +Sat, 14 Dec 2024 01:11:07 GMT + +_Version update only_ + +## 0.10.4 +Mon, 09 Dec 2024 20:31:43 GMT + +_Version update only_ + +## 0.10.3 +Tue, 03 Dec 2024 16:11:08 GMT + +_Version update only_ + +## 0.10.2 +Sat, 23 Nov 2024 01:18:55 GMT + +_Version update only_ + +## 0.10.1 +Fri, 22 Nov 2024 01:10:43 GMT + +_Version update only_ + +## 0.10.0 +Thu, 24 Oct 2024 15:11:19 GMT + +### Minor changes + +- Add bin linking support when calling the create-links.js script with the "--link-bins" parameter + +## 0.9.9 +Thu, 24 Oct 2024 00:15:48 GMT + +_Version update only_ + +## 0.9.8 +Tue, 22 Oct 2024 22:12:40 GMT + +### Patches + +- Fix an issue where the `node_modules/.bin` folder symlinks were not created for extracted packages when using the "default" link creation mode + +## 0.9.7 +Mon, 21 Oct 2024 18:50:10 GMT + +_Version update only_ + +## 0.9.6 +Thu, 17 Oct 2024 08:35:06 GMT + +_Version update only_ + +## 0.9.5 +Tue, 15 Oct 2024 00:12:31 GMT + +_Version update only_ + +## 0.9.4 +Wed, 02 Oct 2024 00:11:19 GMT + +_Version update only_ + +## 0.9.3 +Tue, 01 Oct 2024 00:11:28 GMT + +_Version update only_ + +## 0.9.2 +Mon, 30 Sep 2024 15:12:19 GMT + +_Version update only_ + +## 0.9.1 +Sat, 21 Sep 2024 00:10:27 GMT + +_Version update only_ + +## 0.9.0 +Mon, 16 Sep 2024 02:09:00 GMT + +### Minor changes + +- Add a `files` field to the `extractor-metadata.json` file + +## 0.8.1 +Fri, 13 Sep 2024 00:11:43 GMT + +_Version update only_ + +## 0.8.0 +Wed, 11 Sep 2024 19:54:47 GMT + +### Minor changes + +- Add the ability to change where the "create-links.js" file and the associated metadata file are generated when running in the "script" linkCreation mode + +## 0.7.26 +Tue, 10 Sep 2024 20:08:11 GMT + +_Version update only_ + +## 0.7.25 +Wed, 21 Aug 2024 05:43:04 GMT + +_Version update only_ + +## 0.7.24 +Mon, 12 Aug 2024 22:16:04 GMT + +_Version update only_ + +## 0.7.23 +Fri, 02 Aug 2024 17:26:42 GMT + +_Version update only_ + +## 0.7.22 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.7.21 +Wed, 24 Jul 2024 00:12:14 GMT + +_Version update only_ + +## 0.7.20 +Wed, 17 Jul 2024 06:55:09 GMT + +_Version update only_ + +## 0.7.19 +Wed, 17 Jul 2024 00:11:19 GMT + +_Version update only_ + +## 0.7.18 +Tue, 16 Jul 2024 00:36:21 GMT + +_Version update only_ + +## 0.7.17 +Thu, 27 Jun 2024 21:01:36 GMT + +_Version update only_ + +## 0.7.16 +Fri, 07 Jun 2024 15:10:25 GMT + +_Version update only_ + +## 0.7.15 +Mon, 03 Jun 2024 23:43:15 GMT + +_Version update only_ + +## 0.7.14 +Thu, 30 May 2024 00:13:05 GMT + +_Version update only_ + +## 0.7.13 +Wed, 29 May 2024 02:03:50 GMT + +_Version update only_ + +## 0.7.12 +Wed, 29 May 2024 00:10:52 GMT + +_Version update only_ + +## 0.7.11 +Tue, 28 May 2024 15:10:09 GMT + +_Version update only_ + +## 0.7.10 +Tue, 28 May 2024 00:09:47 GMT + +_Version update only_ + +## 0.7.9 +Sat, 25 May 2024 04:54:07 GMT + +_Version update only_ + +## 0.7.8 +Fri, 24 May 2024 00:15:08 GMT + +_Version update only_ + +## 0.7.7 +Thu, 23 May 2024 02:26:56 GMT + +_Version update only_ + +## 0.7.6 +Thu, 16 May 2024 15:10:22 GMT + +_Version update only_ + +## 0.7.5 +Wed, 15 May 2024 23:42:58 GMT + +_Version update only_ + +## 0.7.4 +Wed, 15 May 2024 06:04:17 GMT + +_Version update only_ + +## 0.7.3 +Fri, 10 May 2024 05:33:34 GMT + +_Version update only_ + +## 0.7.2 +Wed, 08 May 2024 22:23:50 GMT + +_Version update only_ + +## 0.7.1 +Mon, 06 May 2024 15:11:04 GMT + +_Version update only_ + +## 0.7.0 +Thu, 18 Apr 2024 23:19:43 GMT + +### Minor changes + +- Add support for Rush subspaces + +## 0.6.43 +Wed, 10 Apr 2024 15:10:09 GMT + +_Version update only_ + +## 0.6.42 +Tue, 19 Mar 2024 15:10:18 GMT + +_Version update only_ + +## 0.6.41 +Fri, 15 Mar 2024 00:12:40 GMT + +_Version update only_ + +## 0.6.40 +Tue, 05 Mar 2024 01:19:24 GMT + +_Version update only_ + +## 0.6.39 +Sun, 03 Mar 2024 20:58:13 GMT + +_Version update only_ + +## 0.6.38 +Sat, 02 Mar 2024 02:22:24 GMT + +_Version update only_ + +## 0.6.37 +Fri, 01 Mar 2024 01:10:08 GMT + +_Version update only_ + +## 0.6.36 +Thu, 29 Feb 2024 07:11:45 GMT + +_Version update only_ + +## 0.6.35 +Wed, 28 Feb 2024 16:09:27 GMT + +_Version update only_ + +## 0.6.34 +Sat, 24 Feb 2024 23:02:51 GMT + +_Version update only_ + +## 0.6.33 +Thu, 22 Feb 2024 01:36:09 GMT + +_Version update only_ + +## 0.6.32 +Wed, 21 Feb 2024 21:45:28 GMT + +_Version update only_ + +## 0.6.31 +Wed, 21 Feb 2024 08:55:47 GMT + +_Version update only_ + +## 0.6.30 +Tue, 20 Feb 2024 21:45:10 GMT + +_Version update only_ + +## 0.6.29 +Tue, 20 Feb 2024 16:10:53 GMT + +_Version update only_ + +## 0.6.28 +Mon, 19 Feb 2024 21:54:27 GMT + +_Version update only_ + +## 0.6.27 +Sat, 17 Feb 2024 06:24:35 GMT + +### Patches + +- Fix broken link to API documentation + +## 0.6.26 +Thu, 08 Feb 2024 01:09:21 GMT + +_Version update only_ + +## 0.6.25 +Wed, 07 Feb 2024 01:11:18 GMT + +_Version update only_ + +## 0.6.24 +Mon, 05 Feb 2024 23:46:52 GMT + +_Version update only_ + +## 0.6.23 +Thu, 25 Jan 2024 01:09:30 GMT + +_Version update only_ + +## 0.6.22 +Tue, 23 Jan 2024 20:12:58 GMT + +_Version update only_ + +## 0.6.21 +Tue, 23 Jan 2024 16:15:06 GMT + +_Version update only_ + +## 0.6.20 +Tue, 16 Jan 2024 18:30:11 GMT + +_Version update only_ + +## 0.6.19 +Wed, 03 Jan 2024 00:31:18 GMT + +_Version update only_ + +## 0.6.18 +Wed, 20 Dec 2023 01:09:45 GMT + +_Version update only_ + +## 0.6.17 +Tue, 12 Dec 2023 00:20:33 GMT + +### Patches + +- Fix an issue with the `folderToCopy` option, where the folder contents would be copied into a subfolder instead of into the target folder root. + +## 0.6.16 +Thu, 07 Dec 2023 03:44:13 GMT + +_Version update only_ + +## 0.6.15 +Tue, 05 Dec 2023 01:10:16 GMT + +_Version update only_ + +## 0.6.14 +Thu, 16 Nov 2023 01:09:56 GMT + +### Patches + +- Links that target a path outside of the source directory can now be ignored using "patternsToInclude" and "patternsToExclude" options + +## 0.6.13 +Fri, 10 Nov 2023 18:02:04 GMT + +_Version update only_ + +## 0.6.12 +Wed, 01 Nov 2023 23:11:35 GMT + +### Patches + +- Fix line endings in published package. + +## 0.6.11 +Mon, 30 Oct 2023 23:36:37 GMT + +_Version update only_ + +## 0.6.10 +Sun, 01 Oct 2023 02:56:30 GMT + +_Version update only_ + +## 0.6.9 +Sat, 30 Sep 2023 00:20:51 GMT + +### Patches + +- Ensure the "folderToCopy" field is included in generated archives + +## 0.6.8 +Thu, 28 Sep 2023 20:53:17 GMT + +_Version update only_ + +## 0.6.7 +Wed, 27 Sep 2023 00:21:38 GMT + +_Version update only_ + +## 0.6.6 +Tue, 26 Sep 2023 21:02:30 GMT + +_Version update only_ + +## 0.6.5 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.6.4 +Mon, 25 Sep 2023 23:38:28 GMT + +_Version update only_ + +## 0.6.3 +Fri, 22 Sep 2023 00:05:50 GMT + +_Version update only_ + +## 0.6.2 +Tue, 19 Sep 2023 15:21:52 GMT + +_Version update only_ + +## 0.6.1 +Tue, 19 Sep 2023 00:36:30 GMT + +_Version update only_ + +## 0.6.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 0.5.3 +Wed, 13 Sep 2023 00:32:29 GMT + +_Version update only_ + +## 0.5.2 +Wed, 06 Sep 2023 19:00:39 GMT + +### Patches + +- Fix an issue where subdirectory inclusion patterns (ex. "src/subdir/**/*") would get ignored during extraction + +## 0.5.1 +Thu, 24 Aug 2023 15:20:46 GMT + +_Version update only_ + +## 0.5.0 +Wed, 23 Aug 2023 00:20:45 GMT + +### Minor changes + +- Add option field dependenciesConfigurations in PackageExtractor to filter files for third party dependencies + +## 0.4.1 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 0.4.0 +Fri, 04 Aug 2023 15:22:44 GMT + +### Minor changes + +- Include an API for getting files that are included in a npm package. + +## 0.3.13 +Mon, 31 Jul 2023 15:19:05 GMT + +_Version update only_ + +## 0.3.12 +Sat, 29 Jul 2023 00:22:51 GMT + +_Version update only_ + +## 0.3.11 +Thu, 20 Jul 2023 20:47:28 GMT + +_Version update only_ + +## 0.3.10 +Wed, 19 Jul 2023 00:20:31 GMT + +_Version update only_ + +## 0.3.9 +Fri, 14 Jul 2023 15:20:45 GMT + +_Version update only_ + +## 0.3.8 +Thu, 13 Jul 2023 00:22:37 GMT + +_Version update only_ + +## 0.3.7 +Wed, 12 Jul 2023 15:20:39 GMT + +_Version update only_ + +## 0.3.6 +Wed, 12 Jul 2023 00:23:29 GMT + +_Version update only_ + +## 0.3.5 +Fri, 07 Jul 2023 00:19:32 GMT + +_Version update only_ + +## 0.3.4 +Thu, 06 Jul 2023 00:16:20 GMT + +_Version update only_ + +## 0.3.3 +Tue, 04 Jul 2023 00:18:47 GMT + +_Version update only_ + +## 0.3.2 +Mon, 26 Jun 2023 23:45:21 GMT + +### Patches + +- Fix patternsToInclude and patternsToExclude filters when provided patterns target subdirectories of folders that do not match the provided patterns + +## 0.3.1 +Mon, 19 Jun 2023 22:40:21 GMT + +_Version update only_ + +## 0.3.0 +Sat, 17 Jun 2023 00:21:54 GMT + +### Minor changes + +- Allow for include and exclude filters to be provided for projects. This allows for an additional layer of filtering when extracting a package. + +## 0.2.18 +Thu, 15 Jun 2023 00:21:02 GMT + +_Version update only_ + +## 0.2.17 +Wed, 14 Jun 2023 00:19:42 GMT + +_Version update only_ + +## 0.2.16 +Tue, 13 Jun 2023 15:17:20 GMT + +_Version update only_ + +## 0.2.15 +Tue, 13 Jun 2023 01:49:01 GMT + +### Patches + +- Bump webpack to v5.82.1 + +## 0.2.14 +Fri, 09 Jun 2023 18:05:35 GMT + +_Version update only_ + +## 0.2.13 +Fri, 09 Jun 2023 15:23:15 GMT + +_Version update only_ + +## 0.2.12 +Fri, 09 Jun 2023 00:19:49 GMT + +_Version update only_ + +## 0.2.11 +Thu, 08 Jun 2023 15:21:17 GMT + +_Version update only_ + +## 0.2.10 +Thu, 08 Jun 2023 00:20:02 GMT + +_Version update only_ + +## 0.2.9 +Wed, 07 Jun 2023 22:45:17 GMT + +_Version update only_ + +## 0.2.8 +Tue, 06 Jun 2023 02:52:51 GMT + +_Version update only_ + +## 0.2.7 +Mon, 05 Jun 2023 21:45:21 GMT + +_Version update only_ ## 0.2.6 Fri, 02 Jun 2023 02:01:12 GMT diff --git a/libraries/package-extractor/README.md b/libraries/package-extractor/README.md index dd1dc9b4c43..6bdf6b3af4a 100644 --- a/libraries/package-extractor/README.md +++ b/libraries/package-extractor/README.md @@ -7,6 +7,6 @@ A library used for creating an isolated copy of a package and, optionally, bundl - [CHANGELOG.md]( https://github.com/microsoft/rushstack/blob/main/libraries/deploy-manager/CHANGELOG.md) - Find out what's new in the latest version -- [API Reference](https://rushstack.io/pages/api/deploy-manager/) +- [API Reference](https://api.rushstack.io/pages/deploy-manager/) `@rushstack/package-extractor` is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/libraries/package-extractor/config/api-extractor.json b/libraries/package-extractor/config/api-extractor.json index 996e271d3dd..3dbb76c0e6f 100644 --- a/libraries/package-extractor/config/api-extractor.json +++ b/libraries/package-extractor/config/api-extractor.json @@ -1,19 +1,4 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - - "mainEntryPointFilePath": "/lib/index.d.ts", - - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, - - "docModel": { - "enabled": true, - "apiJsonFilePath": "../../../common/temp/api/.api.json" - }, - - "dtsRollup": { - "enabled": true - } + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json" } diff --git a/libraries/package-extractor/config/heft.json b/libraries/package-extractor/config/heft.json index 877bea32245..e345f0f7b15 100644 --- a/libraries/package-extractor/config/heft.json +++ b/libraries/package-extractor/config/heft.json @@ -2,9 +2,9 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", - "extends": "@rushstack/heft-node-rig/profiles/default/config/heft.json", + "extends": "local-node-rig/profiles/default/config/heft.json", "phasesByName": { "build": { @@ -16,6 +16,10 @@ } } } + }, + + "test": { + "cleanFiles": [{ "sourcePath": "test-output" }] } } } diff --git a/libraries/package-extractor/config/jest.config.json b/libraries/package-extractor/config/jest.config.json index 4bb17bde3ee..d1749681d90 100644 --- a/libraries/package-extractor/config/jest.config.json +++ b/libraries/package-extractor/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "local-node-rig/profiles/default/config/jest.config.json" } diff --git a/libraries/package-extractor/config/rig.json b/libraries/package-extractor/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/libraries/package-extractor/config/rig.json +++ b/libraries/package-extractor/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/libraries/package-extractor/config/typescript.json b/libraries/package-extractor/config/typescript.json deleted file mode 100644 index be5269a1834..00000000000 --- a/libraries/package-extractor/config/typescript.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@rushstack/heft-node-rig/profiles/default/config/typescript.json", - - "additionalModuleKindsToEmit": [ - { - "moduleKind": "esnext", - "outFolderName": "lib-esnext" - } - ] -} diff --git a/libraries/package-extractor/eslint.config.js b/libraries/package-extractor/eslint.config.js new file mode 100644 index 00000000000..87132f43292 --- /dev/null +++ b/libraries/package-extractor/eslint.config.js @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); +const tsdocMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/tsdoc'); + +module.exports = [ + ...nodeProfile, + ...friendlyLocalsMixin, + ...tsdocMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/package-extractor/package.json b/libraries/package-extractor/package.json index df78552a5f9..a0c1936d369 100644 --- a/libraries/package-extractor/package.json +++ b/libraries/package-extractor/package.json @@ -1,9 +1,32 @@ { "name": "@rushstack/package-extractor", - "version": "0.2.6", + "version": "0.13.10", "description": "A library for bundling selected files and dependencies into a deployable package.", - "main": "lib/index.js", - "typings": "dist/package-extractor.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/package-extractor.d.ts", + "exports": { + ".": { + "types": "./dist/package-extractor.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "repository": { "type": "git", "url": "https://github.com/microsoft/rushstack.git", @@ -20,21 +43,23 @@ "@pnpm/link-bins": "~5.3.7", "@rushstack/node-core-library": "workspace:*", "@rushstack/terminal": "workspace:*", + "@rushstack/ts-command-line": "workspace:*", "ignore": "~5.1.6", "jszip": "~3.8.0", - "npm-packlist": "~2.1.2" + "minimatch": "10.2.3", + "npm-packlist": "~5.1.3", + "semver": "~7.7.4" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", + "local-node-rig": "workspace:*", "@rushstack/heft-webpack5-plugin": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/webpack-preserve-dynamic-require-plugin": "workspace:*", "@types/glob": "7.1.1", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", "@types/npm-packlist": "~1.1.1", - "eslint": "~8.7.0", - "webpack": "~5.80.0" - } + "eslint": "~9.37.0", + "webpack": "~5.105.2", + "@types/semver": "7.7.1" + }, + "sideEffects": false } diff --git a/libraries/package-extractor/src/ArchiveManager.ts b/libraries/package-extractor/src/ArchiveManager.ts index 8c3cabdc4a0..a463fbe45af 100644 --- a/libraries/package-extractor/src/ArchiveManager.ts +++ b/libraries/package-extractor/src/ArchiveManager.ts @@ -2,7 +2,8 @@ // See LICENSE in the project root for license information. import JSZip from 'jszip'; -import { FileSystem, FileSystemStats, Path } from '@rushstack/node-core-library'; + +import { FileSystem, type FileSystemStats, Path } from '@rushstack/node-core-library'; // 755 are default permissions to allow read/write/execute for owner and read/execute for group and others. const DEFAULT_FILE_PERMISSIONS: number = 0o755; diff --git a/libraries/package-extractor/src/AssetHandler.ts b/libraries/package-extractor/src/AssetHandler.ts new file mode 100644 index 00000000000..e177c009067 --- /dev/null +++ b/libraries/package-extractor/src/AssetHandler.ts @@ -0,0 +1,234 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; +import fs from 'node:fs'; + +import { Async, FileSystem, Path, type FileSystemStats } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import { ArchiveManager } from './ArchiveManager'; +import type { IExtractorOptions, LinkCreationMode } from './PackageExtractor'; +import type { ILinkInfo, SymlinkAnalyzer } from './SymlinkAnalyzer'; +import { remapSourcePathForTargetFolder } from './Utils'; + +export interface IIncludeAssetOptions { + sourceFilePath?: string; + sourceFileStats?: FileSystemStats; + sourceFileContent?: string | Buffer; + targetFilePath: string; + ignoreIfExisting?: boolean; +} + +export interface IIncludeAssetPathOptions extends IIncludeAssetOptions { + sourceFilePath: string; + sourceFileContent?: never; +} + +export interface IIncludeExistingAssetPathOptions extends IIncludeAssetOptions { + sourceFilePath?: never; + sourceFileContent?: never; +} + +export interface IIncludeAssetContentOptions extends IIncludeAssetOptions { + sourceFileContent: string | Buffer; + sourceFilePath?: never; + sourceFileStats?: never; +} + +export interface IAssetHandlerOptions extends IExtractorOptions { + symlinkAnalyzer: SymlinkAnalyzer; +} + +export interface IFinalizeOptions { + onAfterExtractSymlinksAsync: () => Promise; +} + +export class AssetHandler { + private readonly _terminal: ITerminal; + private readonly _sourceRootFolder: string; + private readonly _targetRootFolder: string; + private readonly _createArchiveOnly: boolean; + private readonly _symlinkAnalyzer: SymlinkAnalyzer; + private readonly _archiveManager: ArchiveManager | undefined; + private readonly _archiveFilePath: string | undefined; + private readonly _linkCreationMode: LinkCreationMode; + private readonly _includedAssetPaths: Set = new Set(); + private _isFinalized: boolean = false; + + public constructor(options: IAssetHandlerOptions) { + const { + terminal, + sourceRootFolder, + targetRootFolder, + linkCreation, + symlinkAnalyzer, + createArchiveFilePath, + createArchiveOnly = false + } = options; + this._terminal = terminal; + this._sourceRootFolder = sourceRootFolder; + this._targetRootFolder = targetRootFolder; + this._symlinkAnalyzer = symlinkAnalyzer; + if (createArchiveFilePath) { + if (path.extname(createArchiveFilePath) !== '.zip') { + throw new Error('Only archives with the .zip file extension are currently supported.'); + } + this._archiveFilePath = path.resolve(targetRootFolder, createArchiveFilePath); + this._archiveManager = new ArchiveManager(); + } + if (createArchiveOnly && !this._archiveManager) { + throw new Error('createArchiveOnly cannot be true if createArchiveFilePath is not provided'); + } + this._createArchiveOnly = createArchiveOnly; + this._linkCreationMode = linkCreation || 'default'; + } + + public async includeAssetAsync(options: IIncludeAssetPathOptions): Promise; + public async includeAssetAsync(options: IIncludeExistingAssetPathOptions): Promise; + public async includeAssetAsync(options: IIncludeAssetContentOptions): Promise; + public async includeAssetAsync(options: IIncludeAssetOptions): Promise { + const { sourceFileContent, targetFilePath, ignoreIfExisting = false } = options; + let { sourceFilePath } = options; + + if (this._isFinalized) { + throw new Error('includeAssetAsync() cannot be called after finalizeAsync()'); + } + if (!sourceFilePath && !sourceFileContent) { + if (!Path.isUnder(targetFilePath, this._targetRootFolder)) { + throw new Error('The existing asset path must be under the target root folder'); + } + sourceFilePath = targetFilePath; + } + if (sourceFilePath && sourceFileContent) { + throw new Error('Either sourceFilePath or sourceFileContent must be provided, but not both'); + } + if (this._includedAssetPaths.has(targetFilePath)) { + if (ignoreIfExisting) { + return; + } + throw new Error(`The asset at path "${targetFilePath}" has already been included`); + } + + if (!this._createArchiveOnly) { + // Ignore when the source file is the same as the target file, as it's a no-op + if (sourceFilePath && sourceFilePath !== targetFilePath) { + // Use the fs.copyFile API instead of FileSystem.copyFileAsync() since copyFileAsync performs + // a needless stat() call to determine if it's a file or folder, and we already know it's a file. + try { + await fs.promises.copyFile(sourceFilePath, targetFilePath, fs.constants.COPYFILE_EXCL); + } catch (e: unknown) { + if (!FileSystem.isNotExistError(e as Error)) { + throw e; + } + // The parent folder may not exist, so ensure it exists before trying to copy again + await FileSystem.ensureFolderAsync(path.dirname(targetFilePath)); + await fs.promises.copyFile(sourceFilePath, targetFilePath, fs.constants.COPYFILE_EXCL); + } + } else if (sourceFileContent) { + await FileSystem.writeFileAsync(targetFilePath, sourceFileContent, { + ensureFolderExists: true + }); + } + } + + if (this._archiveManager) { + const targetRelativeFilePath: string = path.relative(this._targetRootFolder, targetFilePath); + if (sourceFilePath) { + await this._archiveManager.addToArchiveAsync({ + filePath: sourceFilePath, + archivePath: targetRelativeFilePath + }); + } else if (sourceFileContent) { + await this._archiveManager.addToArchiveAsync({ + fileData: sourceFileContent, + archivePath: targetRelativeFilePath + }); + } + } + + this._includedAssetPaths.add(targetFilePath); + } + + public get assetPaths(): string[] { + return [...this._includedAssetPaths]; + } + + public async finalizeAsync(options?: IFinalizeOptions): Promise { + const { onAfterExtractSymlinksAsync } = options ?? {}; + + if (this._isFinalized) { + throw new Error('finalizeAsync() has already been called'); + } + + if (this._linkCreationMode === 'default') { + this._terminal.writeLine('Creating symlinks'); + const linksToCopy: ILinkInfo[] = this._symlinkAnalyzer.reportSymlinks(); + await Async.forEachAsync(linksToCopy, async (linkToCopy: ILinkInfo) => { + await this._extractSymlinkAsync(linkToCopy); + }); + } + + await onAfterExtractSymlinksAsync?.(); + + if (this._archiveManager && this._archiveFilePath) { + this._terminal.writeLine(`Creating archive at "${this._archiveFilePath}"`); + await this._archiveManager.createArchiveAsync(this._archiveFilePath); + } + + this._isFinalized = true; + } + + /** + * Create a symlink as described by the ILinkInfo object. + */ + private async _extractSymlinkAsync(linkInfo: ILinkInfo): Promise { + const { kind, linkPath, targetPath } = { + ...linkInfo, + linkPath: remapSourcePathForTargetFolder({ + sourceRootFolder: this._sourceRootFolder, + targetRootFolder: this._targetRootFolder, + sourcePath: linkInfo.linkPath + }), + targetPath: remapSourcePathForTargetFolder({ + sourceRootFolder: this._sourceRootFolder, + targetRootFolder: this._targetRootFolder, + sourcePath: linkInfo.targetPath + }) + }; + + const newLinkFolder: string = path.dirname(linkPath); + await FileSystem.ensureFolderAsync(newLinkFolder); + + // Link to the relative path for symlinks + const relativeTargetPath: string = path.relative(newLinkFolder, targetPath); + + // NOTE: This logic is based on NpmLinkManager._createSymlink() + if (kind === 'fileLink') { + // For files, we use a Windows "hard link", because creating a symbolic link requires + // administrator permission. However hard links seem to cause build failures on Mac, + // so for all other operating systems we use symbolic links for this case. + if (process.platform === 'win32') { + await FileSystem.createHardLinkAsync({ + linkTargetPath: relativeTargetPath, + newLinkPath: linkPath + }); + } else { + await FileSystem.createSymbolicLinkFileAsync({ + linkTargetPath: relativeTargetPath, + newLinkPath: linkPath + }); + } + } else { + // Junctions are only supported on Windows. This will create a symbolic link on other platforms. + await FileSystem.createSymbolicLinkJunctionAsync({ + linkTargetPath: relativeTargetPath, + newLinkPath: linkPath + }); + } + + // Since the created symlinks have the required relative paths, they can be added directly to + // the archive. + await this.includeAssetAsync({ targetFilePath: linkPath }); + } +} diff --git a/libraries/package-extractor/src/PackageExtractor.ts b/libraries/package-extractor/src/PackageExtractor.ts index c3264c64fcd..c9e381e818f 100644 --- a/libraries/package-extractor/src/PackageExtractor.ts +++ b/libraries/package-extractor/src/PackageExtractor.ts @@ -1,28 +1,38 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as fs from 'fs'; +import * as path from 'node:path'; + +import { Minimatch } from 'minimatch'; +import semver from 'semver'; import npmPacklist from 'npm-packlist'; -import pnpmLinkBins from '@pnpm/link-bins'; -import ignore, { Ignore } from 'ignore'; +import ignore, { type Ignore } from 'ignore'; + import { Async, AsyncQueue, Path, FileSystem, Import, - Colors, JsonFile, - AlreadyExistsBehavior, - type IPackageJson, - type ITerminal + type IPackageJson } from '@rushstack/node-core-library'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; -import { ArchiveManager } from './ArchiveManager'; import { SymlinkAnalyzer, type ILinkInfo, type PathNode } from './SymlinkAnalyzer'; -import { matchesWithStar } from './Utils'; -import { createLinksScriptFilename, scriptsFolderPath } from './PathConstants'; +import { AssetHandler } from './AssetHandler'; +import { + matchesWithStar, + remapSourcePathForTargetFolder, + remapPathForExtractorMetadata, + makeBinLinksAsync +} from './Utils'; +import { + CREATE_LINKS_SCRIPT_FILENAME, + EXTRACTOR_METADATA_FILENAME, + SCRIPTS_FOLDER_PATH +} from './PathConstants'; +import { MAX_CONCURRENCY } from './scripts/createLinks/utilities/constants'; // (@types/npm-packlist is missing this API) declare module 'npm-packlist' { @@ -35,6 +45,9 @@ declare module 'npm-packlist' { } } +export const TARGET_ROOT_SCRIPT_RELATIVE_PATH_TEMPLATE_STRING: '{TARGET_ROOT_SCRIPT_RELATIVE_PATH}' = + '{TARGET_ROOT_SCRIPT_RELATIVE_PATH}'; + /** * Part of the extractor-matadata.json file format. Represents an extracted project. * @@ -69,14 +82,48 @@ export interface IExtractorMetadataJson { * A list of all links that are part of the extracted project. */ links: ILinkInfo[]; + /** + * A list of all files that are part of the extracted project. + */ + files: string[]; +} + +/** + * The extractor subspace configurations + * + * @public + */ +export interface IExtractorSubspace { + /** + * The subspace name + */ + subspaceName: string; + /** + * The folder where the PNPM "node_modules" folder is located. This is used to resolve packages linked + * to the PNPM virtual store. + */ + pnpmInstallFolder?: string; + /** + * The pnpmfile configuration if using PNPM, otherwise undefined. The configuration will be used to + * transform the package.json prior to extraction. + */ + transformPackageJson?: (packageJson: IPackageJson) => IPackageJson; + /** + * Whether PNPM hoisting is enabled for this subspace. When set to `false`, + * the extractor will skip looking for hoisted packages in the PNPM virtual store, since no + * hoisting symlinks will exist. Default is `true`. + */ + pnpmNodeModulesHoistingEnabled?: boolean; } interface IExtractorState { foldersToCopy: Set; + packageJsonByPath: Map; projectConfigurationsByPath: Map; projectConfigurationsByName: Map; + dependencyConfigurationsByName: Map; symlinkAnalyzer: SymlinkAnalyzer; - archiver?: ArchiveManager; + assetHandler: AssetHandler; } /** @@ -93,6 +140,18 @@ export interface IExtractorProjectConfiguration { * The absolute path to the project. */ projectFolder: string; + /** + * A list of glob patterns to include when extracting this project. If a path is + * matched by both "patternsToInclude" and "patternsToExclude", the path will be + * excluded. If undefined, all paths will be included. + */ + patternsToInclude?: string[]; + /** + * A list of glob patterns to exclude when extracting this project. If a path is + * matched by both "patternsToInclude" and "patternsToExclude", the path will be + * excluded. If undefined, no paths will be excluded. + */ + patternsToExclude?: string[]; /** * The names of additional projects to include when extracting this project. */ @@ -107,6 +166,41 @@ export interface IExtractorProjectConfiguration { dependenciesToExclude?: string[]; } +/** + * The extractor configuration for individual dependencies. + * + * @public + */ +export interface IExtractorDependencyConfiguration { + /** + * The name of dependency + */ + dependencyName: string; + /** + * The semver version range of dependency + */ + dependencyVersionRange: string; + /** + * A list of glob patterns to exclude when extracting this dependency. If a path is + * matched by both "patternsToInclude" and "patternsToExclude", the path will be + * excluded. If undefined, no paths will be excluded. + */ + patternsToExclude?: string[]; + /** + * A list of glob patterns to include when extracting this dependency. If a path is + * matched by both "patternsToInclude" and "patternsToExclude", the path will be + * excluded. If undefined, all paths will be included. + */ + patternsToInclude?: string[]; +} + +/** + * The mode to use for link creation. + * + * @public + */ +export type LinkCreationMode = 'default' | 'script' | 'none'; + /** * Options that can be provided to the extractor. * @@ -145,15 +239,20 @@ export interface IExtractorOptions { /** * Whether to skip copying files to the extraction target directory, and only create an extraction - * archive. This is only supported when linkCreation is 'script' or 'none'. + * archive. This is only supported when {@link IExtractorOptions.linkCreation} is 'script' or 'none'. */ createArchiveOnly?: boolean; /** - * The pnpmfile configuration if using PNPM, otherwise undefined. The configuration will be used to + * The pnpmfile configuration if using PNPM, otherwise `undefined`. The configuration will be used to * transform the package.json prior to extraction. + * + * @remarks + * When Rush subspaces are enabled, this setting applies to `default` subspace only. To configure + * each subspace, use the {@link IExtractorOptions.subspaces} array instead. The two approaches + * cannot be combined. */ - transformPackageJson?: (packageJson: IPackageJson) => IPackageJson | undefined; + transformPackageJson?: (packageJson: IPackageJson) => IPackageJson; /** * If dependencies from the "devDependencies" package.json field should be included in the extraction. @@ -168,6 +267,11 @@ export interface IExtractorOptions { /** * The folder where the PNPM "node_modules" folder is located. This is used to resolve packages linked * to the PNPM virtual store. + * + * @remarks + * When Rush subspaces are enabled, this setting applies to `default` subspace only. To configure + * each subspace, use the {@link IExtractorOptions.subspaces} array instead. The two approaches + * cannot be combined. */ pnpmInstallFolder?: string; @@ -179,7 +283,13 @@ export interface IExtractorOptions { * to create links on the server machine, after the files have been uploaded. * "none": Do nothing; some other tool may create the links later, based on the extractor-metadata.json file. */ - linkCreation?: 'default' | 'script' | 'none'; + linkCreation?: LinkCreationMode; + + /** + * The path to the generated link creation script. This is only used when {@link IExtractorOptions.linkCreation} + * is 'script'. + */ + linkCreationScriptPath?: string; /** * An additional folder containing files which will be copied into the root of the extraction. @@ -190,6 +300,21 @@ export interface IExtractorOptions { * Configurations for individual projects, keyed by the project path relative to the sourceRootFolder. */ projectConfigurations: IExtractorProjectConfiguration[]; + + /** + * Configurations for individual dependencies. + */ + dependencyConfigurations?: IExtractorDependencyConfiguration[]; + + /** + * When using Rush subspaces, this setting can be used to provide configuration information for each + * individual subspace. + * + * @remarks + * To avoid confusion, if this setting is used, then the {@link IExtractorOptions.transformPackageJson} and + * {@link IExtractorOptions.pnpmInstallFolder} settings must not be used. + */ + subspaces?: IExtractorSubspace[]; } /** @@ -198,10 +323,44 @@ export interface IExtractorOptions { * @public */ export class PackageExtractor { + /** + * Get a list of files that would be included in a package created from the provided package root path. + * + * @beta + */ + public static async getPackageIncludedFilesAsync(packageRootPath: string): Promise { + // Use npm-packlist to filter the files. Using the Walker class (instead of the default API) ensures + // that "bundledDependencies" are not included. + const walkerPromise: Promise = new Promise( + (resolve: (result: string[]) => void, reject: (error: Error) => void) => { + const walker: npmPacklist.Walker = new npmPacklist.Walker({ + path: packageRootPath + }); + walker.on('done', resolve).on('error', reject).start(); + } + ); + const npmPackFiles: string[] = await walkerPromise; + + // npm-packlist@v5 (uses glob@v8) returns "./dist/index.js" for pattern: "./dist/index.js", + // whereas npm-packlist@v2 (uses glob@v7) returns an empty array. + // This may cause duplicate files in the result list, leading to copying conflicts + // in the AssetHandler.ts includeAssetAsync method. + // + // Temporary fix: normalize the file paths to be relative to the package root, and remove duplicates. + // + // TODO: A better long-term fix is to replace "npm-packlist" with "@pnpm/fs.packlist", notes here: + // https://github.com/microsoft/rushstack/pull/5720/changes#r2984873283 + const normalizedFiles: string[] = Array.from( + new Set(npmPackFiles.map((file) => path.posix.normalize(file))) + ); + return normalizedFiles; + } + /** * Extract a package using the provided options */ public async extractAsync(options: IExtractorOptions): Promise { + options = _normalizeOptions(options); const { terminal, projectConfigurations, @@ -209,69 +368,67 @@ export class PackageExtractor { targetRootFolder, mainProjectName, overwriteExisting, - createArchiveFilePath, - createArchiveOnly + dependencyConfigurations, + linkCreation } = options; - if (createArchiveOnly) { - if (options.linkCreation !== 'script' && options.linkCreation !== 'none') { - throw new Error('createArchiveOnly is only supported when linkCreation is "script" or "none"'); - } - if (!createArchiveFilePath) { - throw new Error('createArchiveOnly is only supported when createArchiveFilePath is specified'); - } - } - - let archiver: ArchiveManager | undefined; - let archiveFilePath: string | undefined; - if (createArchiveFilePath) { - if (path.extname(createArchiveFilePath) !== '.zip') { - throw new Error('Only archives with the .zip file extension are currently supported.'); - } - - archiveFilePath = path.resolve(targetRootFolder, createArchiveFilePath); - archiver = new ArchiveManager(); - } + terminal.writeLine(Colorize.cyan(`Extracting to target folder: ${targetRootFolder}`)); + terminal.writeLine(Colorize.cyan(`Main project for extraction: ${mainProjectName}`)); await FileSystem.ensureFolderAsync(targetRootFolder); - - terminal.writeLine(Colors.cyan(`Extracting to target folder: ${targetRootFolder}`)); - terminal.writeLine(Colors.cyan(`Main project for extraction: ${mainProjectName}`)); - - try { - const existingExtraction: boolean = - (await FileSystem.readFolderItemNamesAsync(targetRootFolder)).length > 0; - if (existingExtraction) { - if (!overwriteExisting) { - throw new Error( - 'The extraction target folder is not empty. Overwrite must be explicitly requested' - ); - } else { - terminal.writeLine('Deleting target folder contents...'); - terminal.writeLine(''); - await FileSystem.ensureEmptyFolderAsync(targetRootFolder); - } - } - } catch (error: unknown) { - if (!FileSystem.isFolderDoesNotExistError(error as Error)) { - throw error; + const existingExtraction: boolean = + (await FileSystem.readFolderItemNamesAsync(targetRootFolder)).length > 0; + if (existingExtraction) { + if (!overwriteExisting) { + throw new Error('The extraction target folder is not empty. Overwrite must be explicitly requested'); } + terminal.writeLine('Deleting target folder contents...'); + terminal.writeLine(''); + await FileSystem.ensureEmptyFolderAsync(targetRootFolder); } // Create a new state for each run + const symlinkAnalyzer: SymlinkAnalyzer = new SymlinkAnalyzer({ + requiredSourceParentPath: sourceRootFolder + }); const state: IExtractorState = { + symlinkAnalyzer, + assetHandler: new AssetHandler({ ...options, symlinkAnalyzer }), foldersToCopy: new Set(), + packageJsonByPath: new Map(), projectConfigurationsByName: new Map(projectConfigurations.map((p) => [p.projectName, p])), projectConfigurationsByPath: new Map(projectConfigurations.map((p) => [p.projectFolder, p])), - symlinkAnalyzer: new SymlinkAnalyzer({ requiredSourceParentPath: sourceRootFolder }), - archiver + dependencyConfigurationsByName: new Map() }; - await this._performExtractionAsync(options, state); - if (archiver && archiveFilePath) { - terminal.writeLine(`Creating archive at "${archiveFilePath}"`); - await archiver.createArchiveAsync(archiveFilePath); + // set state dependencyConfigurationsByName + for (const dependencyConfiguration of dependencyConfigurations || []) { + const { dependencyName } = dependencyConfiguration; + let existingDependencyConfigurations: IExtractorDependencyConfiguration[] | undefined = + state.dependencyConfigurationsByName.get(dependencyName); + if (!existingDependencyConfigurations) { + existingDependencyConfigurations = []; + state.dependencyConfigurationsByName.set(dependencyName, existingDependencyConfigurations); + } + existingDependencyConfigurations.push(dependencyConfiguration); } + + await this._performExtractionAsync(options, state); + await state.assetHandler.finalizeAsync({ + onAfterExtractSymlinksAsync: async () => { + // We need the symlinks to be created before attempting to create the bin links, since it requires + // the node_modules folder to be realized. While we're here, we may as well perform some specific + // link creation tasks and write the extractor-metadata.json file before the asset handler finalizes. + if (linkCreation === 'default') { + await this._makeBinLinksAsync(options, state); + } else if (linkCreation === 'script') { + await this._writeCreateLinksScriptAsync(options, state); + } + + terminal.writeLine('Creating extractor-metadata.json'); + await this._writeExtractorMetadataAsync(options, state); + } + }); } private async _performExtractionAsync(options: IExtractorOptions, state: IExtractorState): Promise { @@ -280,10 +437,10 @@ export class PackageExtractor { mainProjectName, sourceRootFolder, targetRootFolder, - folderToCopy: addditionalFolderToCopy, - linkCreation + folderToCopy: additionalFolderToCopy, + createArchiveOnly } = options; - const { projectConfigurationsByName, foldersToCopy, symlinkAnalyzer } = state; + const { projectConfigurationsByName, foldersToCopy } = state; const mainProjectConfiguration: IExtractorProjectConfiguration | undefined = projectConfigurationsByName.get(mainProjectName); @@ -308,12 +465,14 @@ export class PackageExtractor { } } + const startingFolders: string[] = []; for (const { projectName, projectFolder } of includedProjectsSet) { - terminal.writeLine(Colors.cyan(`Analyzing project: ${projectName}`)); - await this._collectFoldersAsync(projectFolder, options, state); + terminal.writeLine(Colorize.cyan(`Analyzing project: ${projectName}`)); + startingFolders.push(projectFolder); } + await this._collectFoldersAsync(startingFolders, options, state); - if (!options.createArchiveOnly) { + if (!createArchiveOnly) { terminal.writeLine(`Copying folders to target folder "${targetRootFolder}"`); } await Async.forEachAsync( @@ -322,51 +481,20 @@ export class PackageExtractor { await this._extractFolderAsync(folderToCopy, options, state); }, { - concurrency: 10 + concurrency: MAX_CONCURRENCY } ); - switch (linkCreation) { - case 'script': { - const sourceFilePath: string = path.join(scriptsFolderPath, createLinksScriptFilename); - if (!options.createArchiveOnly) { - terminal.writeLine(`Creating ${createLinksScriptFilename}`); - await FileSystem.copyFileAsync({ - sourcePath: sourceFilePath, - destinationPath: path.join(targetRootFolder, createLinksScriptFilename), - alreadyExistsBehavior: AlreadyExistsBehavior.Error - }); - } - await state.archiver?.addToArchiveAsync({ - filePath: sourceFilePath, - archivePath: createLinksScriptFilename - }); - break; - } - case 'default': { - terminal.writeLine('Creating symlinks'); - const linksToCopy: ILinkInfo[] = symlinkAnalyzer.reportSymlinks(); - await Async.forEachAsync(linksToCopy, async (linkToCopy: ILinkInfo) => { - await this._extractSymlinkAsync(linkToCopy, options, state); - }); - await this._makeBinLinksAsync(options, state); - break; - } - default: { - break; - } - } - - terminal.writeLine('Creating extractor-metadata.json'); - await this._writeExtractorMetadataAsync(options, state); - - if (addditionalFolderToCopy) { - const sourceFolderPath: string = path.resolve(sourceRootFolder, addditionalFolderToCopy); - await FileSystem.copyFilesAsync({ - sourcePath: sourceFolderPath, - destinationPath: targetRootFolder, - alreadyExistsBehavior: AlreadyExistsBehavior.Error - }); + if (additionalFolderToCopy) { + // Copy the additional folder directly into the root of the target folder by setting the sourceRootFolder + // to the root of the folderToCopy + const additionalFolderPath: string = path.resolve(sourceRootFolder, additionalFolderToCopy); + const additionalFolderExtractorOptions: IExtractorOptions = { + ...options, + sourceRootFolder: additionalFolderPath, + targetRootFolder + }; + await this._extractFolderAsync(additionalFolderPath, additionalFolderExtractorOptions, state); } } @@ -374,14 +502,14 @@ export class PackageExtractor { * Recursively crawl the node_modules dependencies and collect the result in IExtractorState.foldersToCopy. */ private async _collectFoldersAsync( - packageJsonFolder: string, + packageJsonFolders: string[], options: IExtractorOptions, state: IExtractorState ): Promise { - const { terminal, pnpmInstallFolder, transformPackageJson } = options; + const { terminal, subspaces } = options; const { projectConfigurationsByPath } = state; - const packageJsonFolderPathQueue: AsyncQueue = new AsyncQueue([packageJsonFolder]); + const packageJsonFolderPathQueue: AsyncQueue = new AsyncQueue(packageJsonFolders); await Async.forEachAsync( packageJsonFolderPathQueue, @@ -398,9 +526,16 @@ export class PackageExtractor { path.join(packageJsonRealFolderPath, 'package.json') ); + const targetSubspace: IExtractorSubspace | undefined = subspaces?.find( + (subspace) => + subspace.pnpmInstallFolder && Path.isUnder(packageJsonFolderPath, subspace.pnpmInstallFolder) + ); + // Transform packageJson using the provided transformer, if requested - const packageJson: IPackageJson = transformPackageJson?.(originalPackageJson) ?? originalPackageJson; + const packageJson: IPackageJson = + targetSubspace?.transformPackageJson?.(originalPackageJson) ?? originalPackageJson; + state.packageJsonByPath.set(packageJsonRealFolderPath, packageJson); // Union of keys from regular dependencies, peerDependencies, optionalDependencies // (and possibly devDependencies if includeDevDependencies=true) const dependencyNamesToProcess: Set = new Set(); @@ -446,7 +581,7 @@ export class PackageExtractor { baseFolderPath: packageJsonRealFolderPath, getRealPathAsync: async (filePath: string) => { try { - return (await state.symlinkAnalyzer.analyzePathAsync(filePath)).nodePath; + return (await state.symlinkAnalyzer.analyzePathAsync({ inputPath: filePath })).nodePath; } catch (error: unknown) { if (FileSystem.isFileDoesNotExistError(error as Error)) { return filePath; @@ -467,13 +602,20 @@ export class PackageExtractor { // Replicate the links to the virtual store. Note that if the package has not been hoisted by // PNPM, the package will not be resolvable from here. - // Only apply this logic for packages that were actually installed under the common/temp folder. - if (pnpmInstallFolder && Path.isUnder(packageJsonFolderPath, pnpmInstallFolder)) { + // Only apply this logic for packages that were actually installed under the common/temp folder, + // and only when hoisting is enabled for the subspace. + const realPnpmInstallFolder: string | undefined = targetSubspace?.pnpmInstallFolder; + const hoistingEnabled: boolean = targetSubspace?.pnpmNodeModulesHoistingEnabled !== false; + if ( + hoistingEnabled && + realPnpmInstallFolder && + Path.isUnder(packageJsonFolderPath, realPnpmInstallFolder) + ) { try { // The PNPM virtual store links are created in this folder. We will resolve the current package // from that location and collect any additional links encountered along the way. // TODO: This can be configured via NPMRC. We should support that. - const pnpmDotFolderPath: string = path.join(pnpmInstallFolder, 'node_modules', '.pnpm'); + const pnpmDotFolderPath: string = path.join(realPnpmInstallFolder, 'node_modules', '.pnpm'); // TODO: Investigate how package aliases are handled by PNPM in this case. For example: // @@ -485,7 +627,7 @@ export class PackageExtractor { baseFolderPath: pnpmDotFolderPath, getRealPathAsync: async (filePath: string) => { try { - return (await state.symlinkAnalyzer.analyzePathAsync(filePath)).nodePath; + return (await state.symlinkAnalyzer.analyzePathAsync({ inputPath: filePath })).nodePath; } catch (error: unknown) { if (FileSystem.isFileDoesNotExistError(error as Error)) { return filePath; @@ -498,6 +640,7 @@ export class PackageExtractor { } catch (resolveErr) { // The virtual store link isn't guaranteed to exist, so ignore if it's missing // NOTE: If you encounter this warning a lot, please report it to the Rush maintainers. + // eslint-disable-next-line no-console console.log('Ignoring missing PNPM virtual store link for ' + packageJsonFolderPath); } } @@ -505,7 +648,7 @@ export class PackageExtractor { callback(); }, { - concurrency: 10 + concurrency: MAX_CONCURRENCY } ); } @@ -550,43 +693,6 @@ export class PackageExtractor { return allDependencyNames; } - /** - * Maps a file path from IExtractorOptions.sourceRootFolder to IExtractorOptions.targetRootFolder - * - * Example input: "C:\\MyRepo\\libraries\\my-lib" - * Example output: "C:\\MyRepo\\common\\deploy\\libraries\\my-lib" - */ - private _remapPathForExtractorFolder( - absolutePathInSourceFolder: string, - options: IExtractorOptions - ): string { - const { sourceRootFolder, targetRootFolder } = options; - const relativePath: string = path.relative(sourceRootFolder, absolutePathInSourceFolder); - if (relativePath.startsWith('..')) { - throw new Error(`Source path "${absolutePathInSourceFolder}" is not under "${sourceRootFolder}"`); - } - const absolutePathInTargetFolder: string = path.join(targetRootFolder, relativePath); - return absolutePathInTargetFolder; - } - - /** - * Maps a file path from IExtractorOptions.sourceRootFolder to relative path - * - * Example input: "C:\\MyRepo\\libraries\\my-lib" - * Example output: "libraries/my-lib" - */ - private _remapPathForExtractorMetadata( - absolutePathInSourceFolder: string, - options: IExtractorOptions - ): string { - const { sourceRootFolder } = options; - const relativePath: string = path.relative(sourceRootFolder, absolutePathInSourceFolder); - if (relativePath.startsWith('..')) { - throw new Error(`Source path "${absolutePathInSourceFolder}" is not under "${sourceRootFolder}"`); - } - return Path.convertToSlashes(relativePath); - } - /** * Copy one package folder to the extractor target folder. */ @@ -595,36 +701,83 @@ export class PackageExtractor { options: IExtractorOptions, state: IExtractorState ): Promise { - const { includeNpmIgnoreFiles, targetRootFolder } = options; - const { projectConfigurationsByPath, archiver } = state; + const { includeNpmIgnoreFiles } = options; + const { projectConfigurationsByPath, packageJsonByPath, dependencyConfigurationsByName, assetHandler } = + state; let useNpmIgnoreFilter: boolean = false; - if (!includeNpmIgnoreFiles) { - const sourceFolderRealPath: string = await FileSystem.getRealPathAsync(sourceFolderPath); - const sourceProjectConfiguration: IExtractorProjectConfiguration | undefined = - projectConfigurationsByPath.get(sourceFolderRealPath); - if (sourceProjectConfiguration) { - useNpmIgnoreFilter = true; - } - } + const sourceFolderRealPath: string = await FileSystem.getRealPathAsync(sourceFolderPath); + const sourceProjectConfiguration: IExtractorProjectConfiguration | undefined = + projectConfigurationsByPath.get(sourceFolderRealPath); + + const packagesJson: IPackageJson | undefined = packageJsonByPath.get(sourceFolderRealPath); + // As this function will be used to copy folder for both project inside monorepo and third party + // dependencies insides node_modules. Third party dependencies won't have project configurations + const isLocalProject: boolean = !!sourceProjectConfiguration; + + // Function to filter files inside local project or third party dependencies. + const isFileExcluded = (filePath: string): boolean => { + // Encapsulate exclude logic into a function, so it can be reused. + const excludeFileByPatterns = ( + patternsToInclude: string[] | undefined, + patternsToExclude: string[] | undefined + ): boolean => { + let includeFilters: Minimatch[] | undefined; + let excludeFilters: Minimatch[] | undefined; + if (patternsToInclude?.length) { + includeFilters = patternsToInclude?.map((p) => new Minimatch(p, { dot: true })); + } + if (patternsToExclude?.length) { + excludeFilters = patternsToExclude?.map((p) => new Minimatch(p, { dot: true })); + } + // If there are no filters, then we can't exclude anything. + if (!includeFilters && !excludeFilters) { + return false; + } - const targetFolderPath: string = this._remapPathForExtractorFolder(sourceFolderPath, options); + const isIncluded: boolean = !includeFilters || includeFilters.some((m) => m.match(filePath)); - if (useNpmIgnoreFilter) { - // Use npm-packlist to filter the files. Using the Walker class (instead of the default API) ensures - // that "bundledDependencies" are not included. - const walkerPromise: Promise = new Promise( - (resolve: (result: string[]) => void, reject: (error: Error) => void) => { - const walker: npmPacklist.Walker = new npmPacklist.Walker({ - path: sourceFolderPath - }); - walker.on('done', resolve).on('error', reject).start(); + // If the file is not included, then we don't need to check the excludeFilter. If it is included + // and there is no exclude filter, then we know that the file is not excluded. If it is included + // and there is an exclude filter, then we need to check for a match. + return !isIncluded || !!excludeFilters?.some((m) => m.match(filePath)); + }; + + if (isLocalProject) { + return excludeFileByPatterns( + sourceProjectConfiguration?.patternsToInclude, + sourceProjectConfiguration?.patternsToExclude + ); + } else { + if (!packagesJson) { + return false; } - ); - const npmPackFiles: string[] = await walkerPromise; + const dependenciesConfigurations: IExtractorDependencyConfiguration[] | undefined = + dependencyConfigurationsByName.get(packagesJson.name); + if (!dependenciesConfigurations) { + return false; + } + const matchedDependenciesConfigurations: IExtractorDependencyConfiguration[] = + dependenciesConfigurations.filter((d) => + semver.satisfies(packagesJson.version, d.dependencyVersionRange) + ); + return matchedDependenciesConfigurations.some((d) => + excludeFileByPatterns(d.patternsToInclude, d.patternsToExclude) + ); + } + }; - const alreadyCopiedSourcePaths: Set = new Set(); + if (sourceProjectConfiguration && !includeNpmIgnoreFiles) { + // Only use the npmignore filter if the project configuration explicitly asks for it + useNpmIgnoreFilter = true; + } + const targetFolderPath: string = remapSourcePathForTargetFolder({ + ...options, + sourcePath: sourceFolderPath + }); + if (useNpmIgnoreFilter) { + const npmPackFiles: string[] = await PackageExtractor.getPackageIncludedFilesAsync(sourceFolderPath); await Async.forEachAsync( npmPackFiles, async (npmPackFile: string) => { @@ -634,35 +787,27 @@ export class PackageExtractor { // 'dist//index.js' // 'dist/index.js' // - // We can detect the duplicates by comparing the path.resolve() result. - const copySourcePath: string = path.resolve(sourceFolderPath, npmPackFile); - if (alreadyCopiedSourcePaths.has(copySourcePath)) { + + // Filter out files that are excluded by the project configuration or dependency configuration. + if (isFileExcluded(npmPackFile)) { return; } - alreadyCopiedSourcePaths.add(copySourcePath); - - const copyDestinationPath: string = path.join(targetFolderPath, npmPackFile); - const copySourcePathNode: PathNode = await state.symlinkAnalyzer.analyzePathAsync(copySourcePath); - if (copySourcePathNode.kind !== 'link') { - if (!options.createArchiveOnly) { - await FileSystem.ensureFolderAsync(path.dirname(copyDestinationPath)); - // Use the fs.copyFile API instead of FileSystem.copyFileAsync() since copyFileAsync performs - // a needless stat() call to determine if it's a file or folder, and we already know it's a file. - await fs.promises.copyFile(copySourcePath, copyDestinationPath, fs.constants.COPYFILE_EXCL); - } - if (archiver) { - const archivePath: string = path.relative(targetRootFolder, copyDestinationPath); - await archiver.addToArchiveAsync({ - filePath: copySourcePath, - archivePath, - stats: copySourcePathNode.linkStats - }); - } + const sourceFilePath: string = path.resolve(sourceFolderPath, npmPackFile); + const { kind, linkStats: sourceFileStats } = await state.symlinkAnalyzer.analyzePathAsync({ + inputPath: sourceFilePath + }); + if (kind === 'file') { + const targetFilePath: string = path.resolve(targetFolderPath, npmPackFile); + await assetHandler.includeAssetAsync({ + sourceFilePath, + sourceFileStats, + targetFilePath + }); } }, { - concurrency: 10 + concurrency: MAX_CONCURRENCY } ); } else { @@ -689,28 +834,38 @@ export class PackageExtractor { return; } - const sourcePathNode: PathNode = await state.symlinkAnalyzer.analyzePathAsync(sourcePath); - if (sourcePathNode.kind === 'file') { - const targetPath: string = path.join(targetFolderPath, relativeSourcePath); - if (!options.createArchiveOnly) { - // Manually call fs.copyFile to avoid unnecessary stat calls. - await fs.promises.copyFile(sourcePath, targetPath, fs.constants.COPYFILE_EXCL); + const sourcePathNode: PathNode | undefined = await state.symlinkAnalyzer.analyzePathAsync({ + inputPath: sourcePath, + // Treat all links to external paths as if they are files for this scenario. In the future, we may + // want to explore the target of the external link to see if all files within the target are + // excluded, and throw if they are not. + shouldIgnoreExternalLink: (linkSourcePath: string) => { + // Ignore the provided linkSourcePath since it may not be the first link in the chain. Instead, + // we will consider only the relativeSourcePath, since that would be our entrypoint into the + // link chain. + return isFileExcluded(relativeSourcePath); } + }); - // Add the file to the archive. Only need to add files since directories will be auto-created - if (archiver) { - const archivePath: string = path.relative(targetRootFolder, targetPath); - await archiver.addToArchiveAsync({ - filePath: sourcePath, - archivePath: archivePath, - stats: sourcePathNode.linkStats - }); + if (sourcePathNode === undefined) { + // The target was a symlink that is excluded. We don't need to do anything. + callback(); + return; + } else if (sourcePathNode.kind === 'file') { + // Only ignore files and not folders to ensure that we traverse the contents of all folders. This is + // done so that we can match against subfolder patterns, ex. "src/subfolder/**/*" + if (relativeSourcePath !== '' && isFileExcluded(relativeSourcePath)) { + callback(); + return; } + + const targetFilePath: string = path.resolve(targetFolderPath, relativeSourcePath); + await assetHandler.includeAssetAsync({ + sourceFilePath: sourcePath, + sourceFileStats: sourcePathNode.linkStats, + targetFilePath + }); } else if (sourcePathNode.kind === 'folder') { - if (!options.createArchiveOnly) { - const targetPath: string = path.join(targetFolderPath, relativeSourcePath); - await FileSystem.ensureFolderAsync(targetPath); - } const children: string[] = await FileSystem.readFolderItemNamesAsync(sourcePath); for (const child of children) { queue.push(path.join(sourcePath, child)); @@ -720,64 +875,12 @@ export class PackageExtractor { callback(); }, { - concurrency: 10 + concurrency: MAX_CONCURRENCY } ); } } - /** - * Create a symlink as described by the ILinkInfo object. - */ - private async _extractSymlinkAsync( - originalLinkInfo: ILinkInfo, - options: IExtractorOptions, - state: IExtractorState - ): Promise { - const linkInfo: ILinkInfo = { - kind: originalLinkInfo.kind, - linkPath: this._remapPathForExtractorFolder(originalLinkInfo.linkPath, options), - targetPath: this._remapPathForExtractorFolder(originalLinkInfo.targetPath, options) - }; - - const newLinkFolder: string = path.dirname(linkInfo.linkPath); - await FileSystem.ensureFolderAsync(newLinkFolder); - - // Link to the relative path for symlinks - const relativeTargetPath: string = path.relative(newLinkFolder, linkInfo.targetPath); - - // NOTE: This logic is based on NpmLinkManager._createSymlink() - if (linkInfo.kind === 'fileLink') { - // For files, we use a Windows "hard link", because creating a symbolic link requires - // administrator permission. However hard links seem to cause build failures on Mac, - // so for all other operating systems we use symbolic links for this case. - if (process.platform === 'win32') { - await FileSystem.createHardLinkAsync({ - linkTargetPath: relativeTargetPath, - newLinkPath: linkInfo.linkPath - }); - } else { - await FileSystem.createSymbolicLinkFileAsync({ - linkTargetPath: relativeTargetPath, - newLinkPath: linkInfo.linkPath - }); - } - } else { - // Junctions are only supported on Windows. This will create a symbolic link on other platforms. - await FileSystem.createSymbolicLinkJunctionAsync({ - linkTargetPath: relativeTargetPath, - newLinkPath: linkInfo.linkPath - }); - } - - // Since the created symlinks have the required relative paths, they can be added directly to - // the archive. - await state.archiver?.addToArchiveAsync({ - filePath: linkInfo.linkPath, - archivePath: path.relative(options.targetRootFolder, linkInfo.linkPath) - }); - } - /** * Write the common/deploy/deploy-metadata.json file. */ @@ -785,84 +888,127 @@ export class PackageExtractor { options: IExtractorOptions, state: IExtractorState ): Promise { - const { mainProjectName, targetRootFolder } = options; + const { mainProjectName, sourceRootFolder, targetRootFolder, linkCreation, linkCreationScriptPath } = + options; const { projectConfigurationsByPath } = state; - const extractorMetadataFileName: string = 'extractor-metadata.json'; - const extractorMetadataFilePath: string = path.join(targetRootFolder, extractorMetadataFileName); + const extractorMetadataFolderPath: string = + linkCreation === 'script' && linkCreationScriptPath + ? path.dirname(path.resolve(targetRootFolder, linkCreationScriptPath)) + : targetRootFolder; + const extractorMetadataFilePath: string = path.join( + extractorMetadataFolderPath, + EXTRACTOR_METADATA_FILENAME + ); const extractorMetadataJson: IExtractorMetadataJson = { mainProjectName, projects: [], - links: [] + links: [], + files: [] }; for (const { projectFolder, projectName } of projectConfigurationsByPath.values()) { if (state.foldersToCopy.has(projectFolder)) { extractorMetadataJson.projects.push({ projectName, - path: this._remapPathForExtractorMetadata(projectFolder, options) + path: remapPathForExtractorMetadata(sourceRootFolder, projectFolder) }); } } // Remap the links to be relative to target folder - for (const absoluteLinkInfo of state.symlinkAnalyzer.reportSymlinks()) { - const relativeInfo: ILinkInfo = { - kind: absoluteLinkInfo.kind, - linkPath: this._remapPathForExtractorMetadata(absoluteLinkInfo.linkPath, options), - targetPath: this._remapPathForExtractorMetadata(absoluteLinkInfo.targetPath, options) - }; - extractorMetadataJson.links.push(relativeInfo); + for (const { kind, linkPath, targetPath } of state.symlinkAnalyzer.reportSymlinks()) { + extractorMetadataJson.links.push({ + kind, + linkPath: remapPathForExtractorMetadata(sourceRootFolder, linkPath), + targetPath: remapPathForExtractorMetadata(sourceRootFolder, targetPath) + }); } - const extractorMetadataFileContent: string = JSON.stringify(extractorMetadataJson, undefined, 0); - if (!options.createArchiveOnly) { - await FileSystem.writeFileAsync(extractorMetadataFilePath, extractorMetadataFileContent); + for (const assetPath of state.assetHandler.assetPaths) { + extractorMetadataJson.files.push(remapPathForExtractorMetadata(targetRootFolder, assetPath)); } - await state.archiver?.addToArchiveAsync({ - fileData: extractorMetadataFileContent, - archivePath: extractorMetadataFileName + + const extractorMetadataFileContent: string = JSON.stringify(extractorMetadataJson, undefined, 0); + await state.assetHandler.includeAssetAsync({ + sourceFileContent: extractorMetadataFileContent, + targetFilePath: extractorMetadataFilePath }); } private async _makeBinLinksAsync(options: IExtractorOptions, state: IExtractorState): Promise { const { terminal } = options; - const extractedProjectFolders: string[] = Array.from(state.projectConfigurationsByPath.keys()).filter( - (folderPath: string) => state.foldersToCopy.has(folderPath) - ); - - await Async.forEachAsync( - extractedProjectFolders, - async (projectFolder: string) => { - const extractedProjectFolder: string = this._remapPathForExtractorFolder(projectFolder, options); - const extractedProjectNodeModulesFolder: string = path.join(extractedProjectFolder, 'node_modules'); - const extractedProjectBinFolder: string = path.join(extractedProjectNodeModulesFolder, '.bin'); - - const linkedBinPackageNames: string[] = await pnpmLinkBins( - extractedProjectNodeModulesFolder, - extractedProjectBinFolder, - { - warn: (msg: string) => terminal.writeLine(Colors.yellow(msg)) - } + const extractedProjectFolderPaths: string[] = []; + for (const folderPath of state.projectConfigurationsByPath.keys()) { + if (state.foldersToCopy.has(folderPath)) { + extractedProjectFolderPaths.push( + remapSourcePathForTargetFolder({ ...options, sourcePath: folderPath }) ); + } + } - if (linkedBinPackageNames.length && state.archiver) { - const binFolderItems: string[] = await FileSystem.readFolderItemNamesAsync( - extractedProjectBinFolder - ); - for (const binFolderItem of binFolderItems) { - const binFilePath: string = path.join(extractedProjectBinFolder, binFolderItem); - await state.archiver.addToArchiveAsync({ - filePath: binFilePath, - archivePath: path.relative(options.targetRootFolder, binFilePath) - }); - } - } - }, + const binFilePaths: string[] = await makeBinLinksAsync(terminal, extractedProjectFolderPaths); + await Async.forEachAsync( + binFilePaths, + (targetFilePath: string) => state.assetHandler.includeAssetAsync({ targetFilePath }), { - concurrency: 10 + concurrency: MAX_CONCURRENCY } ); } + + private async _writeCreateLinksScriptAsync( + options: IExtractorOptions, + state: IExtractorState + ): Promise { + const { terminal, targetRootFolder, linkCreationScriptPath } = options; + const { assetHandler } = state; + + terminal.writeLine(`Creating ${CREATE_LINKS_SCRIPT_FILENAME}`); + const createLinksSourceFilePath: string = `${SCRIPTS_FOLDER_PATH}/${CREATE_LINKS_SCRIPT_FILENAME}`; + const createLinksTargetFilePath: string = path.resolve( + targetRootFolder, + linkCreationScriptPath || CREATE_LINKS_SCRIPT_FILENAME + ); + let createLinksScriptContent: string = await FileSystem.readFileAsync(createLinksSourceFilePath); + createLinksScriptContent = createLinksScriptContent.replace( + TARGET_ROOT_SCRIPT_RELATIVE_PATH_TEMPLATE_STRING, + Path.convertToSlashes(path.relative(path.dirname(createLinksTargetFilePath), targetRootFolder)) + ); + await assetHandler.includeAssetAsync({ + sourceFileContent: createLinksScriptContent, + targetFilePath: createLinksTargetFilePath + }); + } +} + +function _normalizeOptions(options: IExtractorOptions): IExtractorOptions { + if (options.subspaces) { + if (options.pnpmInstallFolder !== undefined) { + throw new Error( + 'IExtractorOptions.pnpmInstallFolder cannot be combined with IExtractorOptions.subspaces' + ); + } + if (options.transformPackageJson !== undefined) { + throw new Error( + 'IExtractorOptions.transformPackageJson cannot be combined with IExtractorOptions.subspaces' + ); + } + return options; + } + + const normalizedOptions: IExtractorOptions = { ...options }; + delete normalizedOptions.pnpmInstallFolder; + delete normalizedOptions.transformPackageJson; + + normalizedOptions.subspaces = [ + { + subspaceName: 'default', + pnpmInstallFolder: options.pnpmInstallFolder, + transformPackageJson: options.transformPackageJson + } + ]; + + return normalizedOptions; } diff --git a/libraries/package-extractor/src/PathConstants.ts b/libraries/package-extractor/src/PathConstants.ts index d2cff5814a9..51239dabb66 100644 --- a/libraries/package-extractor/src/PathConstants.ts +++ b/libraries/package-extractor/src/PathConstants.ts @@ -1,8 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See the @microsoft/rush package's LICENSE file for license information. +// See LICENSE in the project root for license information. import { PackageJsonLookup } from '@rushstack/node-core-library'; +export const CREATE_LINKS_SCRIPT_FILENAME: 'create-links.js' = 'create-links.js'; + +export const EXTRACTOR_METADATA_FILENAME: 'extractor-metadata.json' = 'extractor-metadata.json'; + const packageExtractorFolderRootPath: string = PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname)!; -export const createLinksScriptFilename: 'create-links.js' = 'create-links.js'; -export const scriptsFolderPath: string = `${packageExtractorFolderRootPath}/dist/scripts`; +export const SCRIPTS_FOLDER_PATH: string = `${packageExtractorFolderRootPath}/dist/scripts`; diff --git a/libraries/package-extractor/src/SymlinkAnalyzer.ts b/libraries/package-extractor/src/SymlinkAnalyzer.ts index cf87975eb9e..36eaff95481 100644 --- a/libraries/package-extractor/src/SymlinkAnalyzer.ts +++ b/libraries/package-extractor/src/SymlinkAnalyzer.ts @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { FileSystem, FileSystemStats, Sort } from '@rushstack/node-core-library'; +import * as path from 'node:path'; -import * as path from 'path'; +import { FileSystem, type FileSystemStats, Sort } from '@rushstack/node-core-library'; export interface IPathNodeBase { kind: 'file' | 'folder' | 'link'; @@ -65,6 +65,12 @@ export interface ISymlinkAnalyzerOptions { requiredSourceParentPath?: string; } +export interface IAnalyzePathOptions { + inputPath: string; + preserveLinks?: boolean; + shouldIgnoreExternalLink?: (path: string) => boolean; +} + export class SymlinkAnalyzer { private readonly _requiredSourceParentPath: string | undefined; @@ -78,7 +84,15 @@ export class SymlinkAnalyzer { this._requiredSourceParentPath = options.requiredSourceParentPath; } - public async analyzePathAsync(inputPath: string, preserveLinks: boolean = false): Promise { + public async analyzePathAsync( + options: IAnalyzePathOptions & { shouldIgnoreExternalLink: (path: string) => boolean } + ): Promise; + public async analyzePathAsync( + options: IAnalyzePathOptions & { shouldIgnoreExternalLink?: never } + ): Promise; + public async analyzePathAsync(options: IAnalyzePathOptions): Promise { + const { inputPath, preserveLinks = false, shouldIgnoreExternalLink } = options; + // First, try to short-circuit the analysis if we've already analyzed this path const resolvedPath: string = path.resolve(inputPath); const existingNode: PathNode | undefined = this._nodesByPath.get(resolvedPath); @@ -114,6 +128,11 @@ export class SymlinkAnalyzer { resolvedLinkTargetPath ); if (relativeLinkTargetPath.startsWith('..')) { + // Symlinks that link outside of the source folder may be ignored. Check to see if we + // can ignore this one and if so, return undefined. + if (shouldIgnoreExternalLink?.(currentPath)) { + return undefined; + } throw new Error( `Symlink targets not under folder "${this._requiredSourceParentPath}": ` + `${currentPath} -> ${resolvedLinkTargetPath}` @@ -147,7 +166,10 @@ export class SymlinkAnalyzer { if (!preserveLinks) { while (currentNode?.kind === 'link') { - const targetNode: PathNode = await this.analyzePathAsync(currentNode.linkTarget, true); + const targetNode: PathNode = await this.analyzePathAsync({ + inputPath: currentNode.linkTarget, + preserveLinks: true + }); // Have we created an ILinkInfo for this link yet? if (!this._linkInfosByPath.has(currentNode.nodePath)) { diff --git a/libraries/package-extractor/src/Utils.ts b/libraries/package-extractor/src/Utils.ts index 4ac440c4511..0bf49be52ab 100644 --- a/libraries/package-extractor/src/Utils.ts +++ b/libraries/package-extractor/src/Utils.ts @@ -1,4 +1,14 @@ -import { Text } from '@rushstack/node-core-library'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; + +import pnpmLinkBins from '@pnpm/link-bins'; + +import { Async, FileSystem, Path, Text } from '@rushstack/node-core-library'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; + +import { MAX_CONCURRENCY } from './scripts/createLinks/utilities/constants'; export function matchesWithStar(patternWithStar: string, input: string): boolean { // Map "@types/*" --> "^\@types\/.*$" @@ -13,3 +23,84 @@ export function matchesWithStar(patternWithStar: string, input: string): boolean const regExp: RegExp = new RegExp(pattern); return regExp.test(input); } + +export interface IRemapPathForTargetFolder { + sourcePath: string; + sourceRootFolder: string; + targetRootFolder: string; +} + +/** + * Maps a file path under the provided {@link IRemapPathForTargetFolder.sourceRootFolder} to the provided + * {@link IExtractorOptions.targetRootFolder}. + * + * Example input: "C:\\MyRepo\\libraries\\my-lib" + * Example output: "C:\\MyRepo\\common\\deploy\\libraries\\my-lib" + */ +export function remapSourcePathForTargetFolder(options: IRemapPathForTargetFolder): string { + const { sourcePath, sourceRootFolder, targetRootFolder } = options; + const relativePath: string = path.relative(sourceRootFolder, sourcePath); + if (relativePath.startsWith('..')) { + throw new Error(`Source path "${sourcePath}" is not under "${sourceRootFolder}"`); + } + const absolutePathInTargetFolder: string = path.join(targetRootFolder, relativePath); + return absolutePathInTargetFolder; +} + +/** + * Maps a file path under the provided folder path to the expected path format for the extractor metadata. + * + * Example input: "C:\\MyRepo\\libraries\\my-lib" + * Example output: "common/deploy/libraries/my-lib" + */ +export function remapPathForExtractorMetadata(folderPath: string, filePath: string): string { + const relativePath: string = path.relative(folderPath, filePath); + if (relativePath.startsWith('..')) { + throw new Error(`Path "${filePath}" is not under "${folderPath}"`); + } + return Path.convertToSlashes(relativePath); +} + +/** + * Creates the .bin files for the extracted projects and returns the paths to the created .bin files. + * + * @param terminal - The terminal to write to + * @param extractedProjectFolderPaths - The paths to the extracted projects + */ +export async function makeBinLinksAsync( + terminal: ITerminal, + extractedProjectFolderPaths: string[] +): Promise { + const binFilePaths: string[] = []; + await Async.forEachAsync( + extractedProjectFolderPaths, + async (extractedProjectFolderPath: string) => { + const extractedProjectNodeModulesFolderPath: string = `${extractedProjectFolderPath}/node_modules`; + const extractedProjectBinFolderPath: string = `${extractedProjectNodeModulesFolderPath}/.bin`; + + const linkedBinPackageNames: string[] = await pnpmLinkBins( + extractedProjectNodeModulesFolderPath, + extractedProjectBinFolderPath, + { + warn: (msg: string) => terminal.writeLine(Colorize.yellow(msg)) + } + ); + + if (linkedBinPackageNames.length) { + const binFolderItems: string[] = await FileSystem.readFolderItemNamesAsync( + extractedProjectBinFolderPath + ); + for (const binFolderItem of binFolderItems) { + const binFilePath: string = `${extractedProjectBinFolderPath}/${binFolderItem}`; + terminal.writeVerboseLine(`Created .bin file: ${binFilePath}`); + binFilePaths.push(binFilePath); + } + } + }, + { + concurrency: MAX_CONCURRENCY + } + ); + + return binFilePaths; +} diff --git a/libraries/package-extractor/src/index.ts b/libraries/package-extractor/src/index.ts index 4d24f155d1c..564800a2f3c 100644 --- a/libraries/package-extractor/src/index.ts +++ b/libraries/package-extractor/src/index.ts @@ -3,10 +3,13 @@ export { PackageExtractor, + type LinkCreationMode, type IExtractorOptions, type IExtractorProjectConfiguration, + type IExtractorDependencyConfiguration, type IExtractorMetadataJson, - type IProjectInfoJson + type IProjectInfoJson, + type IExtractorSubspace } from './PackageExtractor'; export type { ILinkInfo } from './SymlinkAnalyzer'; diff --git a/libraries/package-extractor/src/scripts/create-links.ts b/libraries/package-extractor/src/scripts/create-links.ts deleted file mode 100644 index 42815a8c005..00000000000 --- a/libraries/package-extractor/src/scripts/create-links.ts +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See the @microsoft/rush package's LICENSE file for license information. - -// THIS SCRIPT IS GENERATED BY THE "rush deploy" COMMAND. - -import * as fs from 'fs'; -import * as path from 'path'; -import type { IExtractorMetadataJson } from '../PackageExtractor'; -import type { IFileSystemCreateLinkOptions } from '@rushstack/node-core-library'; - -// API borrowed from @rushstack/node-core-library, since this script avoids using any -// NPM dependencies. -class FileSystem { - public static createSymbolicLinkJunction(options: IFileSystemCreateLinkOptions): void { - fs.symlinkSync(options.linkTargetPath, options.newLinkPath, 'junction'); - } - - public static createSymbolicLinkFile(options: IFileSystemCreateLinkOptions): void { - fs.symlinkSync(options.linkTargetPath, options.newLinkPath, 'file'); - } - - public static createSymbolicLinkFolder(options: IFileSystemCreateLinkOptions): void { - fs.symlinkSync(options.linkTargetPath, options.newLinkPath, 'dir'); - } - - public static createHardLink(options: IFileSystemCreateLinkOptions): void { - fs.linkSync(options.linkTargetPath, options.newLinkPath); - } -} - -function ensureFolder(folderPath: string): void { - if (!folderPath) { - return; - } - if (fs.existsSync(folderPath)) { - return; - } - const parentPath: string = path.dirname(folderPath); - if (parentPath && parentPath !== folderPath) { - ensureFolder(parentPath); - } - fs.mkdirSync(folderPath); -} - -function removeLinks(targetRootFolder: string, extractorMetadataObject: IExtractorMetadataJson): void { - for (const linkInfo of extractorMetadataObject.links) { - // Link to the relative path for symlinks - const newLinkPath: string = path.join(targetRootFolder, linkInfo.linkPath); - if (fs.existsSync(newLinkPath)) { - fs.unlinkSync(newLinkPath); - } - } -} - -function createLinks(targetRootFolder: string, extractorMetadataObject: IExtractorMetadataJson): void { - for (const linkInfo of extractorMetadataObject.links) { - // Link to the relative path for symlinks - const newLinkPath: string = path.join(targetRootFolder, linkInfo.linkPath); - const linkTargetPath: string = path.join(targetRootFolder, linkInfo.targetPath); - - // Make sure the containing folder exists - ensureFolder(path.dirname(newLinkPath)); - - // NOTE: This logic is based on NpmLinkManager._createSymlink() - if (process.platform === 'win32') { - if (linkInfo.kind === 'folderLink') { - // For directories, we use a Windows "junction". On Unix, this produces a regular symlink. - FileSystem.createSymbolicLinkJunction({ newLinkPath, linkTargetPath }); - } else { - // For files, we use a Windows "hard link", because creating a symbolic link requires - // administrator permission. - - // NOTE: We cannot use the relative path for hard links - FileSystem.createHardLink({ newLinkPath, linkTargetPath }); - } - } else { - // However hard links seem to cause build failures on Mac, so for all other operating systems - // we use symbolic links for this case. - if (linkInfo.kind === 'folderLink') { - FileSystem.createSymbolicLinkFolder({ newLinkPath, linkTargetPath }); - } else { - FileSystem.createSymbolicLinkFile({ newLinkPath, linkTargetPath }); - } - } - } -} - -function showUsage(): void { - console.log('Usage:'); - console.log(' node create-links.js create'); - console.log(' node create-links.js remove'); - - console.log('\nCreates or removes the symlinks for the output folder created by "rush deploy".'); - console.log('The link information is read from "extractor-metadata.json" in the same folder.'); -} - -function main(): boolean { - // Example: [ "node.exe", "create-links.js", ""create" ] - const args: string[] = process.argv.slice(2); - - if (args.length !== 1 || (args[0] !== 'create' && args[0] !== 'remove')) { - showUsage(); - return false; - } - - const targetRootFolder: string = __dirname; - const extractorMetadataPath: string = path.join(targetRootFolder, 'extractor-metadata.json'); - - if (!fs.existsSync(extractorMetadataPath)) { - throw new Error('Input file not found: ' + extractorMetadataPath); - } - - const extractorMetadataJson: string = fs.readFileSync(extractorMetadataPath).toString(); - const extractorMetadataObject: IExtractorMetadataJson = JSON.parse(extractorMetadataJson); - - if (args[0] === 'create') { - console.log(`\nCreating links for extraction at path "${targetRootFolder}"`); - removeLinks(targetRootFolder, extractorMetadataObject); - createLinks(targetRootFolder, extractorMetadataObject); - } else { - console.log(`\nRemoving links for extraction at path "${targetRootFolder}"`); - removeLinks(targetRootFolder, extractorMetadataObject); - } - - console.log('The operation completed successfully.'); - return true; -} - -try { - process.exitCode = 1; - if (main()) { - process.exitCode = 0; - } -} catch (error) { - console.log('ERROR: ' + error); -} diff --git a/libraries/package-extractor/src/scripts/createLinks/cli/CreateLinksCommandLineParser.ts b/libraries/package-extractor/src/scripts/createLinks/cli/CreateLinksCommandLineParser.ts new file mode 100644 index 00000000000..10b64a2ed7f --- /dev/null +++ b/libraries/package-extractor/src/scripts/createLinks/cli/CreateLinksCommandLineParser.ts @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { CommandLineParser } from '@rushstack/ts-command-line'; +import { AlreadyReportedError } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import { CreateLinksAction } from './actions/CreateLinksAction'; +import { RemoveLinksAction } from './actions/RemoveLinksAction'; + +export class CreateLinksCommandLineParser extends CommandLineParser { + private readonly _terminal: ITerminal; + + public constructor(terminal: ITerminal) { + super({ + toolFilename: 'create-links', + toolDescription: 'Create or remove symlinks for the extracted packages' + }); + + this._terminal = terminal; + + this.addAction(new CreateLinksAction(this._terminal)); + this.addAction(new RemoveLinksAction(this._terminal)); + } + + protected override async onExecuteAsync(): Promise { + process.exitCode = 1; + + try { + await super.onExecuteAsync(); + process.exitCode = 0; + } catch (error) { + if (!(error instanceof AlreadyReportedError)) { + this._terminal.writeErrorLine(); + this._terminal.writeErrorLine('ERROR: ' + error.message.trim()); + } + } + } +} diff --git a/libraries/package-extractor/src/scripts/createLinks/cli/actions/CreateLinksAction.ts b/libraries/package-extractor/src/scripts/createLinks/cli/actions/CreateLinksAction.ts new file mode 100644 index 00000000000..146bb76b46c --- /dev/null +++ b/libraries/package-extractor/src/scripts/createLinks/cli/actions/CreateLinksAction.ts @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; + +import { Async, FileSystem, Path } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; +import { CommandLineAction, type CommandLineFlagParameter } from '@rushstack/ts-command-line'; + +import type { IExtractorMetadataJson, IProjectInfoJson } from '../../../../PackageExtractor'; +import { makeBinLinksAsync } from '../../../../Utils'; +import { getExtractorMetadataAsync } from '../../utilities/CreateLinksUtilities'; +import { + TARGET_ROOT_FOLDER, + REALIZE_FILES_PARAMETER_NAME, + LINK_BINS_PARAMETER_NAME, + MAX_CONCURRENCY +} from '../../utilities/constants'; +import { removeLinksAsync } from './RemoveLinksAction'; + +async function createLinksAsync( + terminal: ITerminal, + targetRootFolder: string, + extractorMetadataObject: IExtractorMetadataJson +): Promise { + await Async.forEachAsync( + extractorMetadataObject.links, + async (linkInfo) => { + // Link to the relative path for symlinks + const newLinkPath: string = path.join(targetRootFolder, linkInfo.linkPath); + const linkTargetPath: string = path.join(targetRootFolder, linkInfo.targetPath); + + // Make sure the containing folder exists + await FileSystem.ensureFolderAsync(path.dirname(newLinkPath)); + + // NOTE: This logic is based on NpmLinkManager._createSymlink() + if (linkInfo.kind === 'folderLink') { + terminal.writeVerboseLine(`Creating linked folder at path "${newLinkPath}"`); + await FileSystem.createSymbolicLinkJunctionAsync({ newLinkPath, linkTargetPath }); + } else if (linkInfo.kind === 'fileLink') { + // Use hardlinks for Windows and symlinks for other platforms since creating a symbolic link + // requires administrator permission on Windows. This may cause unexpected behaviour for consumers + // of the hardlinked files. If this becomes an issue, we may need to revisit this. + terminal.writeVerboseLine(`Creating linked file at path "${newLinkPath}"`); + if (process.platform === 'win32') { + await FileSystem.createHardLinkAsync({ newLinkPath, linkTargetPath }); + } else { + await FileSystem.createSymbolicLinkFileAsync({ newLinkPath, linkTargetPath }); + } + } + }, + { concurrency: MAX_CONCURRENCY } + ); +} + +async function realizeFilesAsync( + terminal: ITerminal, + targetRootFolder: string, + extractorMetadataObject: IExtractorMetadataJson +): Promise { + await Async.forEachAsync( + extractorMetadataObject.files, + async (relativeFilePath) => { + const filePath: string = `${targetRootFolder}/${relativeFilePath}`; + const realFilePath: string = await FileSystem.getRealPathAsync(filePath); + if (!Path.isEqual(realFilePath, filePath)) { + // Delete the existing symlink and create a hardlink to the real file, since creating hardlinks + // is less overhead than copying the file. + terminal.writeVerboseLine(`Realizing file at path "${filePath}"`); + await FileSystem.deleteFileAsync(filePath); + await FileSystem.createHardLinkAsync({ newLinkPath: filePath, linkTargetPath: realFilePath }); + } + }, + { concurrency: MAX_CONCURRENCY } + ); +} + +export class CreateLinksAction extends CommandLineAction { + private _terminal: ITerminal; + private _realizeFilesParameter: CommandLineFlagParameter; + private _linkBinsParameter: CommandLineFlagParameter; + + public constructor(terminal: ITerminal) { + super({ + actionName: 'create', + summary: 'Create symlinks for extraction', + documentation: 'This action creates symlinks for the extraction process.' + }); + + this._terminal = terminal; + + this._realizeFilesParameter = this.defineFlagParameter({ + parameterLongName: REALIZE_FILES_PARAMETER_NAME, + description: 'Realize files instead of creating symlinks' + }); + + this._linkBinsParameter = this.defineFlagParameter({ + parameterLongName: LINK_BINS_PARAMETER_NAME, + description: 'Create the .bin files for extracted packages' + }); + } + + protected override async onExecuteAsync(): Promise { + const extractorMetadataObject: IExtractorMetadataJson = await getExtractorMetadataAsync(); + const realizeFiles: boolean = this._realizeFilesParameter.value; + const linkBins: boolean = this._linkBinsParameter.value; + + this._terminal.writeLine(`Creating links for extraction at path "${TARGET_ROOT_FOLDER}"`); + await removeLinksAsync(this._terminal, TARGET_ROOT_FOLDER, extractorMetadataObject); + await createLinksAsync(this._terminal, TARGET_ROOT_FOLDER, extractorMetadataObject); + + if (realizeFiles) { + this._terminal.writeLine(`Realizing files for extraction at path "${TARGET_ROOT_FOLDER}"`); + await realizeFilesAsync(this._terminal, TARGET_ROOT_FOLDER, extractorMetadataObject); + } + + if (linkBins) { + this._terminal.writeLine(`Linking bins for extraction at path "${TARGET_ROOT_FOLDER}"`); + const extractedProjectFolderPaths: string[] = extractorMetadataObject.projects.map( + (project: IProjectInfoJson) => path.join(TARGET_ROOT_FOLDER, project.path) + ); + await makeBinLinksAsync(this._terminal, extractedProjectFolderPaths); + } + } +} diff --git a/libraries/package-extractor/src/scripts/createLinks/cli/actions/RemoveLinksAction.ts b/libraries/package-extractor/src/scripts/createLinks/cli/actions/RemoveLinksAction.ts new file mode 100644 index 00000000000..bea0e60f9e4 --- /dev/null +++ b/libraries/package-extractor/src/scripts/createLinks/cli/actions/RemoveLinksAction.ts @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; + +import { Async, FileSystem } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; +import { CommandLineAction } from '@rushstack/ts-command-line'; + +import type { IExtractorMetadataJson } from '../../../../PackageExtractor'; +import { getExtractorMetadataAsync } from '../../utilities/CreateLinksUtilities'; +import { TARGET_ROOT_FOLDER, MAX_CONCURRENCY } from '../../utilities/constants'; + +export async function removeLinksAsync( + terminal: ITerminal, + targetRootFolder: string, + extractorMetadataObject: IExtractorMetadataJson +): Promise { + await Async.forEachAsync( + extractorMetadataObject.links, + async ({ linkPath }) => { + const newLinkPath: string = path.join(targetRootFolder, linkPath); + terminal.writeVerboseLine(`Removing link at path "${newLinkPath}"`); + await FileSystem.deleteFileAsync(newLinkPath, { throwIfNotExists: false }); + }, + { concurrency: MAX_CONCURRENCY } + ); +} + +export class RemoveLinksAction extends CommandLineAction { + private _terminal: ITerminal; + + public constructor(terminal: ITerminal) { + super({ + actionName: 'remove', + summary: 'Remove symlinks created by the "create" action', + documentation: 'This action removes the symlinks created by the "create" action.' + }); + + this._terminal = terminal; + } + + protected override async onExecuteAsync(): Promise { + const extractorMetadataObject: IExtractorMetadataJson = await getExtractorMetadataAsync(); + + this._terminal.writeLine(`Removing links for extraction at path "${TARGET_ROOT_FOLDER}"`); + await removeLinksAsync(this._terminal, TARGET_ROOT_FOLDER, extractorMetadataObject); + } +} diff --git a/libraries/package-extractor/src/scripts/createLinks/start.ts b/libraries/package-extractor/src/scripts/createLinks/start.ts new file mode 100644 index 00000000000..d03d9f6e5c9 --- /dev/null +++ b/libraries/package-extractor/src/scripts/createLinks/start.ts @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { Terminal, ConsoleTerminalProvider } from '@rushstack/terminal'; + +import { CreateLinksCommandLineParser } from './cli/CreateLinksCommandLineParser'; + +const terminal: Terminal = new Terminal(new ConsoleTerminalProvider({ verboseEnabled: true })); + +const parser: CreateLinksCommandLineParser = new CreateLinksCommandLineParser(terminal); +parser.executeAsync().catch(terminal.writeErrorLine); diff --git a/libraries/package-extractor/src/scripts/createLinks/utilities/CreateLinksUtilities.ts b/libraries/package-extractor/src/scripts/createLinks/utilities/CreateLinksUtilities.ts new file mode 100644 index 00000000000..69f4443e241 --- /dev/null +++ b/libraries/package-extractor/src/scripts/createLinks/utilities/CreateLinksUtilities.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { FileSystem } from '@rushstack/node-core-library'; + +import type { IExtractorMetadataJson } from '../../../PackageExtractor'; +import { EXTRACTOR_METADATA_FILENAME } from '../../../PathConstants'; + +export async function getExtractorMetadataAsync(): Promise { + const extractorMetadataPath: string = `${__dirname}/${EXTRACTOR_METADATA_FILENAME}`; + const extractorMetadataJson: string = await FileSystem.readFileAsync(extractorMetadataPath); + const extractorMetadataObject: IExtractorMetadataJson = JSON.parse(extractorMetadataJson); + return extractorMetadataObject; +} diff --git a/libraries/package-extractor/src/scripts/createLinks/utilities/constants.ts b/libraries/package-extractor/src/scripts/createLinks/utilities/constants.ts new file mode 100644 index 00000000000..707a3f08e41 --- /dev/null +++ b/libraries/package-extractor/src/scripts/createLinks/utilities/constants.ts @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import os from 'node:os'; +import path from 'node:path'; + +import type { TARGET_ROOT_SCRIPT_RELATIVE_PATH_TEMPLATE_STRING as TargetRootScriptRelativePathTemplateString } from '../../../PackageExtractor'; + +/** + * The maximum number of concurrent operations to perform. + */ +export const MAX_CONCURRENCY: number = (os.availableParallelism?.() ?? os.cpus().length) * 2; + +/** + * The name of the action to create symlinks. + */ +export const CREATE_ACTION_NAME: 'create' = 'create'; + +/** + * The name of the action to remove symlinks. + */ +export const REMOVE_ACTION_NAME: 'remove' = 'remove'; + +/** + * The name of the parameter to realize files when creating symlinks. + */ +export const REALIZE_FILES_PARAMETER_NAME: '--realize-files' = '--realize-files'; + +/** + * The name of the parameter to link bins when creating symlinks. + */ +export const LINK_BINS_PARAMETER_NAME: '--link-bins' = '--link-bins'; + +/** + * The name of the parameter to link packages when creating symlinks. The actual value of this + * export is modified after bundling the script to ensure that the extracted version of the script + * contains the relative path from the extraction target folder to the script. Generally, this + * value should not be used directly, but rather the `TARGET_ROOT_FOLDER` export should be used + * instead. + */ +export const TARGET_ROOT_SCRIPT_RELATIVE_PATH: typeof TargetRootScriptRelativePathTemplateString = + '{TARGET_ROOT_SCRIPT_RELATIVE_PATH}'; + +/** + * The path to the root folder where symlinks are created. + */ +export const TARGET_ROOT_FOLDER: string = path.resolve(__dirname, TARGET_ROOT_SCRIPT_RELATIVE_PATH); diff --git a/libraries/package-extractor/src/test/PackageExtractor.test.ts b/libraries/package-extractor/src/test/PackageExtractor.test.ts new file mode 100644 index 00000000000..e16677fd277 --- /dev/null +++ b/libraries/package-extractor/src/test/PackageExtractor.test.ts @@ -0,0 +1,634 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; +import type { ChildProcess } from 'node:child_process'; + +import { Executable, FileSystem, Sort } from '@rushstack/node-core-library'; +import { Terminal, StringBufferTerminalProvider } from '@rushstack/terminal'; +import { + PackageExtractor, + type IExtractorProjectConfiguration, + type IExtractorMetadataJson +} from '../PackageExtractor'; + +// Do this work in the "temp/test.jest" directory since it gets cleaned on clean runs +const extractorTargetFolder: string = path.resolve(__dirname, '..', '..', 'test-output'); +const repoRoot: string = path.resolve(__dirname, '..', '..', '..', '..'); +const project1PackageName: string = 'package-extractor-test-01'; +const project2PackageName: string = 'package-extractor-test-02'; +const project3PackageName: string = 'package-extractor-test-03'; +const project4PackageName: string = 'package-extractor-test-04'; +const project5PackageName: string = 'package-extractor-test-05'; +const project1RelativePath: string = path.join('build-tests', project1PackageName); +const project2RelativePath: string = path.join('build-tests', project2PackageName); +const project3RelativePath: string = path.join('build-tests', project3PackageName); +const project4RelativePath: string = path.join('build-tests', project4PackageName); +const project5RelativePath: string = path.join('build-tests', project5PackageName); +const project1Path: string = path.join(repoRoot, project1RelativePath); +const project2Path: string = path.resolve(repoRoot, project2RelativePath); +const project3Path: string = path.resolve(repoRoot, project3RelativePath); +const project4Path: string = path.resolve(repoRoot, project4RelativePath); +const project5Path: string = path.resolve(repoRoot, project5RelativePath); + +function getDefaultProjectConfigurations(): IExtractorProjectConfiguration[] { + return [ + { + projectName: project1PackageName, + projectFolder: project1Path + }, + { + projectName: project2PackageName, + projectFolder: project2Path + }, + { + projectName: project3PackageName, + projectFolder: project3Path + } + ]; +} + +describe(PackageExtractor.name, () => { + const terminal = new Terminal(new StringBufferTerminalProvider()); + const packageExtractor = new PackageExtractor(); + + it('should extract project', async () => { + const targetFolder: string = path.join(extractorTargetFolder, 'extractor-output-01'); + + await expect( + packageExtractor.extractAsync({ + mainProjectName: project1PackageName, + sourceRootFolder: repoRoot, + targetRootFolder: targetFolder, + overwriteExisting: true, + projectConfigurations: getDefaultProjectConfigurations(), + terminal, + createArchiveOnly: false, + includeNpmIgnoreFiles: true, + linkCreation: 'default', + includeDevDependencies: true + }) + ).resolves.not.toThrow(); + + // Validate project 1 files + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'src', 'index.js')) + ).resolves.toBe(true); + + // Validate project 2 is linked through node_modules + const project1NodeModulesPath: string = path.join(targetFolder, project1RelativePath, 'node_modules'); + await expect( + FileSystem.getRealPathAsync(path.join(project1NodeModulesPath, project2PackageName, 'src', 'index.js')) + ).resolves.toEqual(path.join(targetFolder, project2RelativePath, 'src', 'index.js')); + + // Validate project 3 is linked through node_modules + await expect( + FileSystem.getRealPathAsync(path.join(project1NodeModulesPath, project3PackageName, 'src', 'index.js')) + ).resolves.toEqual(path.join(targetFolder, project3RelativePath, 'src', 'index.js')); + await expect( + FileSystem.getRealPathAsync( + path.join( + project1NodeModulesPath, + project2PackageName, + 'node_modules', + project3PackageName, + 'src', + 'index.js' + ) + ) + ).resolves.toEqual(path.join(targetFolder, project3RelativePath, 'src', 'index.js')); + }); + + it('should extract project with dependencies only', async () => { + const targetFolder: string = path.join(extractorTargetFolder, 'extractor-output-02'); + + await expect( + packageExtractor.extractAsync({ + mainProjectName: project1PackageName, + sourceRootFolder: repoRoot, + targetRootFolder: targetFolder, + overwriteExisting: true, + projectConfigurations: getDefaultProjectConfigurations(), + terminal, + createArchiveOnly: false, + includeNpmIgnoreFiles: true, + linkCreation: 'default', + includeDevDependencies: false + }) + ).resolves.not.toThrow(); + + // Validate project 1 files + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'src', 'index.js')) + ).resolves.toBe(true); + + // Validate project 2 is linked through node_modules + const project1NodeModulesPath: string = path.join(targetFolder, project1RelativePath, 'node_modules'); + await expect( + FileSystem.getRealPathAsync(path.join(project1NodeModulesPath, project2PackageName, 'src', 'index.js')) + ).resolves.toEqual(path.join(targetFolder, project2RelativePath, 'src', 'index.js')); + + // Validate project 3 is not linked through node_modules on project 1 but is linked through node_modules on project 2 + await expect( + FileSystem.existsAsync(path.join(project1NodeModulesPath, project3PackageName)) + ).resolves.toBe(false); + await expect( + FileSystem.getRealPathAsync( + path.join( + project1NodeModulesPath, + project2PackageName, + 'node_modules', + project3PackageName, + 'src', + 'index.js' + ) + ) + ).resolves.toEqual(path.join(targetFolder, project3RelativePath, 'src', 'index.js')); + }); + + it('should throw error if main project does not exist', async () => { + const targetFolder: string = path.join(extractorTargetFolder, 'extractor-output-03'); + + await expect( + packageExtractor.extractAsync({ + mainProjectName: 'project-that-not-exist', + sourceRootFolder: repoRoot, + targetRootFolder: targetFolder, + overwriteExisting: true, + terminal, + projectConfigurations: [], + linkCreation: 'default' + }) + ).rejects.toThrow('Main project "project-that-not-exist" was not found in the list of projects'); + }); + + it('should throw error if contains symlink outsides targetRootFolder', async () => { + const sourceFolder: string = path.join(repoRoot, 'build-tests'); + const targetFolder: string = path.join(extractorTargetFolder, 'extractor-output-04'); + + await expect( + packageExtractor.extractAsync({ + mainProjectName: project4PackageName, + sourceRootFolder: sourceFolder, + targetRootFolder: targetFolder, + overwriteExisting: true, + projectConfigurations: [ + { + projectName: project4PackageName, + projectFolder: project4Path + } + ], + terminal, + createArchiveOnly: false, + includeNpmIgnoreFiles: true + }) + ).rejects.toThrow(/Symlink targets not under folder/); + }); + + it('should exclude specified dependencies', async () => { + const targetFolder: string = path.join(extractorTargetFolder, 'extractor-output-05'); + + await expect( + packageExtractor.extractAsync({ + mainProjectName: project1PackageName, + sourceRootFolder: repoRoot, + targetRootFolder: targetFolder, + overwriteExisting: true, + projectConfigurations: [ + { + projectName: project1PackageName, + projectFolder: project1Path, + patternsToExclude: ['src/**'] + }, + { + projectName: project2PackageName, + projectFolder: project2Path + }, + { + projectName: project3PackageName, + projectFolder: project3Path + } + ], + terminal, + createArchiveOnly: false, + includeNpmIgnoreFiles: true, + linkCreation: 'default', + includeDevDependencies: true + }) + ).resolves.not.toThrow(); + + // Validate project 1 files + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'package.json')) + ).resolves.toBe(true); + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'src', 'index.js')) + ).resolves.toBe(false); + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'src', 'subdir')) + ).resolves.toBe(false); + + // Validate project 2 files + await expect( + FileSystem.existsAsync(path.join(targetFolder, project2RelativePath, 'package.json')) + ).resolves.toBe(true); + await expect( + FileSystem.existsAsync(path.join(targetFolder, project2RelativePath, 'src', 'index.js')) + ).resolves.toBe(true); + }); + + it('should include specified dependencies', async () => { + const targetFolder: string = path.join(extractorTargetFolder, 'extractor-output-05'); + + await expect( + packageExtractor.extractAsync({ + mainProjectName: project1PackageName, + sourceRootFolder: repoRoot, + targetRootFolder: targetFolder, + overwriteExisting: true, + projectConfigurations: [ + { + projectName: project1PackageName, + projectFolder: project1Path, + patternsToInclude: ['src/subdir/**'] + } + ], + terminal, + createArchiveOnly: false, + includeNpmIgnoreFiles: true, + linkCreation: 'default', + includeDevDependencies: true + }) + ).resolves.not.toThrow(); + + // Validate project 1 files + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'package.json')) + ).resolves.toBe(false); + await expect(FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'src'))).resolves.toBe( + true + ); + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'src', 'index.js')) + ).resolves.toBe(false); + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'src', 'subdir')) + ).resolves.toBe(true); + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'src', 'subdir', 'file.js')) + ).resolves.toBe(true); + }); + + it('should exclude specified dependencies on local dependencies', async () => { + const targetFolder: string = path.join(extractorTargetFolder, 'extractor-output-06'); + + await expect( + packageExtractor.extractAsync({ + mainProjectName: project1PackageName, + sourceRootFolder: repoRoot, + targetRootFolder: targetFolder, + overwriteExisting: true, + projectConfigurations: [ + { + projectName: project1PackageName, + projectFolder: project1Path + }, + { + projectName: project2PackageName, + projectFolder: project2Path, + patternsToExclude: ['src/**'] + }, + { + projectName: project3PackageName, + projectFolder: project3Path + } + ], + terminal, + createArchiveOnly: false, + includeNpmIgnoreFiles: true, + linkCreation: 'default', + includeDevDependencies: true + }) + ).resolves.not.toThrow(); + + // Validate project 1 files + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'package.json')) + ).resolves.toBe(true); + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'src', 'index.js')) + ).resolves.toBe(true); + + // Validate project 2 files + await expect( + FileSystem.existsAsync(path.join(targetFolder, project2RelativePath, 'package.json')) + ).resolves.toBe(true); + await expect( + FileSystem.existsAsync(path.join(targetFolder, project2RelativePath, 'src', 'index.js')) + ).resolves.toBe(false); + }); + + it('should exclude specified files on third party dependencies with semver version', async () => { + const targetFolder: string = path.join(extractorTargetFolder, 'extractor-output-07'); + + await expect( + packageExtractor.extractAsync({ + mainProjectName: project1PackageName, + sourceRootFolder: repoRoot, + targetRootFolder: targetFolder, + overwriteExisting: true, + projectConfigurations: [ + { + projectName: project1PackageName, + projectFolder: project1Path + }, + { + projectName: project2PackageName, + projectFolder: project2Path + }, + { + projectName: project3PackageName, + projectFolder: project3Path + } + ], + dependencyConfigurations: [ + { + dependencyName: '@types/node', + dependencyVersionRange: '^18', + patternsToExclude: ['fs/**'] + } + ], + terminal, + createArchiveOnly: false, + includeNpmIgnoreFiles: true, + linkCreation: 'default', + includeDevDependencies: true + }) + ).resolves.not.toThrow(); + // Validate project 1 files + await expect( + FileSystem.existsAsync( + path.join(targetFolder, project1RelativePath, 'node_modules/@types/node/fs-promises.d.ts') + ) + ).resolves.toBe(false); + await expect( + FileSystem.existsAsync( + path.join(targetFolder, project1RelativePath, 'node_modules/@types/node/path.d.ts') + ) + ).resolves.toBe(true); + + // Validate project 3 files + await expect( + FileSystem.existsAsync( + path.join(targetFolder, project3RelativePath, 'node_modules/@types/node/fs/promises.d.ts') + ) + ).resolves.toBe(true); + }); + it('should not exclude specified files on third party dependencies if semver version not match', async () => { + const targetFolder: string = path.join(extractorTargetFolder, 'extractor-output-08'); + + await expect( + packageExtractor.extractAsync({ + mainProjectName: project1PackageName, + sourceRootFolder: repoRoot, + targetRootFolder: targetFolder, + overwriteExisting: true, + projectConfigurations: [ + { + projectName: project1PackageName, + projectFolder: project1Path + }, + { + projectName: project2PackageName, + projectFolder: project2Path + }, + { + projectName: project3PackageName, + projectFolder: project3Path + } + ], + dependencyConfigurations: [ + { + dependencyName: '@types/node', + dependencyVersionRange: '^16.20.0', + patternsToExclude: ['fs/**'] + } + ], + terminal, + createArchiveOnly: false, + includeNpmIgnoreFiles: true, + linkCreation: 'default', + includeDevDependencies: true + }) + ).resolves.not.toThrow(); + // Validate project 1 files + await expect( + FileSystem.existsAsync( + path.join(targetFolder, project1RelativePath, 'node_modules/@types/node/fs/promises.d.ts') + ) + ).resolves.toBe(true); + + // Validate project file that shouldn't be exclude + await expect( + FileSystem.existsAsync( + path.join(targetFolder, project3RelativePath, 'node_modules/@types/node/fs/promises.d.ts') + ) + ).resolves.toBe(true); + }); + + it('should include folderToCopy', async () => { + const targetFolder: string = path.join(extractorTargetFolder, 'extractor-output-09'); + + await expect( + packageExtractor.extractAsync({ + mainProjectName: project1PackageName, + sourceRootFolder: repoRoot, + targetRootFolder: targetFolder, + overwriteExisting: true, + projectConfigurations: [ + { + projectName: project1PackageName, + projectFolder: project1Path + } + ], + folderToCopy: project2Path, + terminal, + createArchiveOnly: false, + includeNpmIgnoreFiles: true, + linkCreation: 'default', + includeDevDependencies: true + }) + ).resolves.not.toThrow(); + + // Validate project 1 files + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'package.json')) + ).resolves.toBe(true); + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'src', 'index.js')) + ).resolves.toBe(true); + + // Validate project 2 files + await expect(FileSystem.existsAsync(path.join(targetFolder, 'package.json'))).resolves.toBe(true); + await expect(FileSystem.existsAsync(path.join(targetFolder, 'src', 'index.js'))).resolves.toBe(true); + }); + + it('should extract project with script linkCreation', async () => { + const targetFolder: string = path.join(extractorTargetFolder, 'extractor-output-10'); + + await expect( + packageExtractor.extractAsync({ + mainProjectName: project1PackageName, + sourceRootFolder: repoRoot, + targetRootFolder: targetFolder, + overwriteExisting: true, + projectConfigurations: getDefaultProjectConfigurations(), + terminal, + includeNpmIgnoreFiles: true, + linkCreation: 'script', + includeDevDependencies: true + }) + ).resolves.not.toThrow(); + + // Validate project 1 files + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'src', 'index.js')) + ).resolves.toBe(true); + + // Validate project 2 is not linked through node_modules + const project1NodeModulesPath: string = path.join(targetFolder, project1RelativePath, 'node_modules'); + await expect( + FileSystem.existsAsync(path.join(project1NodeModulesPath, project2PackageName, 'src', 'index.js')) + ).resolves.toEqual(false); + + // Validate project 3 is not linked through node_modules + await expect( + FileSystem.existsAsync(path.join(project1NodeModulesPath, project3PackageName, 'src', 'index.js')) + ).resolves.toEqual(false); + + // Run the linkCreation script + const createLinksProcess: ChildProcess = Executable.spawn(process.argv0, [ + path.join(targetFolder, 'create-links.js'), + 'create' + ]); + await expect( + Executable.waitForExitAsync(createLinksProcess, { throwOnNonZeroExitCode: true }) + ).resolves.not.toThrow(); + + // Validate project 2 is linked through node_modules + await expect( + FileSystem.getRealPathAsync(path.join(project1NodeModulesPath, project2PackageName, 'src', 'index.js')) + ).resolves.toEqual(path.join(targetFolder, project2RelativePath, 'src', 'index.js')); + + // Validate project 3 is linked through node_modules + await expect( + FileSystem.getRealPathAsync(path.join(project1NodeModulesPath, project3PackageName, 'src', 'index.js')) + ).resolves.toEqual(path.join(targetFolder, project3RelativePath, 'src', 'index.js')); + await expect( + FileSystem.getRealPathAsync( + path.join( + project1NodeModulesPath, + project2PackageName, + 'node_modules', + project3PackageName, + 'src', + 'index.js' + ) + ) + ).resolves.toEqual(path.join(targetFolder, project3RelativePath, 'src', 'index.js')); + + const metadataFileContent: string = await FileSystem.readFileAsync( + `${targetFolder}/extractor-metadata.json` + ); + const metadata: IExtractorMetadataJson = JSON.parse(metadataFileContent); + Sort.sortBy(metadata.files, (x) => x); + Sort.sortBy(metadata.links, (x) => x.linkPath); + Sort.sortBy(metadata.projects, (x) => x.path); + expect(metadata).toMatchSnapshot(); + }); + + it('should extract project with script linkCreation and custom linkCreationScriptPath', async () => { + const targetFolder: string = path.join(extractorTargetFolder, 'extractor-output-11'); + const linkCreationScriptPath: string = path.join(targetFolder, 'foo', 'bar', 'baz.js'); + + await expect( + packageExtractor.extractAsync({ + mainProjectName: project1PackageName, + sourceRootFolder: repoRoot, + targetRootFolder: targetFolder, + overwriteExisting: true, + projectConfigurations: getDefaultProjectConfigurations(), + terminal, + includeNpmIgnoreFiles: true, + linkCreation: 'script', + linkCreationScriptPath, + includeDevDependencies: true + }) + ).resolves.not.toThrow(); + + // Validate project 1 files + await expect( + FileSystem.existsAsync(path.join(targetFolder, project1RelativePath, 'src', 'index.js')) + ).resolves.toBe(true); + + // Validate project 2 is not linked through node_modules + const project1NodeModulesPath: string = path.join(targetFolder, project1RelativePath, 'node_modules'); + await expect( + FileSystem.existsAsync(path.join(project1NodeModulesPath, project2PackageName, 'src', 'index.js')) + ).resolves.toEqual(false); + + // Validate project 3 is not linked through node_modules + await expect( + FileSystem.existsAsync(path.join(project1NodeModulesPath, project3PackageName, 'src', 'index.js')) + ).resolves.toEqual(false); + + // Run the linkCreation script + const createLinksProcess: ChildProcess = Executable.spawn(process.argv0, [ + linkCreationScriptPath, + 'create' + ]); + await expect( + Executable.waitForExitAsync(createLinksProcess, { throwOnNonZeroExitCode: true }) + ).resolves.not.toThrow(); + + // Validate project 2 is linked through node_modules + await expect( + FileSystem.getRealPathAsync(path.join(project1NodeModulesPath, project2PackageName, 'src', 'index.js')) + ).resolves.toEqual(path.join(targetFolder, project2RelativePath, 'src', 'index.js')); + + // Validate project 3 is linked through node_modules + await expect( + FileSystem.getRealPathAsync(path.join(project1NodeModulesPath, project3PackageName, 'src', 'index.js')) + ).resolves.toEqual(path.join(targetFolder, project3RelativePath, 'src', 'index.js')); + await expect( + FileSystem.getRealPathAsync( + path.join( + project1NodeModulesPath, + project2PackageName, + 'node_modules', + project3PackageName, + 'src', + 'index.js' + ) + ) + ).resolves.toEqual(path.join(targetFolder, project3RelativePath, 'src', 'index.js')); + + const metadataFileContent: string = await FileSystem.readFileAsync( + `${path.dirname(linkCreationScriptPath)}/extractor-metadata.json` + ); + const metadata: IExtractorMetadataJson = JSON.parse(metadataFileContent); + Sort.sortBy(metadata.files, (x) => x); + Sort.sortBy(metadata.links, (x) => x.linkPath); + Sort.sortBy(metadata.projects, (x) => x.path); + expect(metadata).toMatchSnapshot(); + }); + + it('should normalize and remove duplicate file paths', async () => { + await FileSystem.writeFileAsync(path.join(project5Path, 'dist', 'index.js'), '', { + ensureFolderExists: true + }); + const result = await PackageExtractor.getPackageIncludedFilesAsync(project5Path); + // To make the test work on both Windows and *nix, need to normalize the paths to posix style + expect(result.map(path.posix.normalize)).toEqual(['dist/index.js', 'package.json']); + }); +}); diff --git a/libraries/package-extractor/src/test/__snapshots__/PackageExtractor.test.ts.snap b/libraries/package-extractor/src/test/__snapshots__/PackageExtractor.test.ts.snap new file mode 100644 index 00000000000..cf593067402 --- /dev/null +++ b/libraries/package-extractor/src/test/__snapshots__/PackageExtractor.test.ts.snap @@ -0,0 +1,453 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`PackageExtractor should extract project with script linkCreation 1`] = ` +Object { + "files": Array [ + "build-tests/package-extractor-test-01/.rush/temp/shrinkwrap-deps.json", + "build-tests/package-extractor-test-01/package.json", + "build-tests/package-extractor-test-01/src/index.js", + "build-tests/package-extractor-test-01/src/subdir/file.js", + "build-tests/package-extractor-test-02/.rush/temp/shrinkwrap-deps.json", + "build-tests/package-extractor-test-02/package.json", + "build-tests/package-extractor-test-02/src/index.js", + "build-tests/package-extractor-test-03/.rush/temp/shrinkwrap-deps.json", + "build-tests/package-extractor-test-03/package.json", + "build-tests/package-extractor-test-03/src/index.js", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/LICENSE", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/README.md", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/assert.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/assert/strict.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/async_hooks.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/buffer.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/child_process.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/cluster.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/console.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/constants.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/crypto.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/dgram.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/diagnostics_channel.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/dns.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/dns/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/domain.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/events.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/fs.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/fs/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/globals.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/globals.global.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/http.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/http2.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/https.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/index.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/inspector.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/module.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/net.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/os.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/package.json", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/path.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/perf_hooks.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/process.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/punycode.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/querystring.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/readline.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/repl.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/stream.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/stream/consumers.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/stream/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/stream/web.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/string_decoder.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/timers.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/timers/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/tls.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/trace_events.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/tty.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/url.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/util.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/v8.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/vm.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/wasi.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/worker_threads.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/zlib.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/LICENSE", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/README.md", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/assert.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/assert/strict.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/async_hooks.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/buffer.buffer.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/buffer.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/child_process.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/cluster.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/compatibility/disposable.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/compatibility/index.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/compatibility/indexable.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/compatibility/iterators.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/console.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/constants.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/crypto.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/dgram.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/diagnostics_channel.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/dns.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/dns/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/dom-events.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/domain.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/events.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/fs.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/fs/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/globals.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/globals.typedarray.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/http.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/http2.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/https.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/index.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/inspector.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/module.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/net.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/os.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/package.json", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/path.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/perf_hooks.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/process.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/punycode.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/querystring.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/readline.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/readline/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/repl.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/sea.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/stream.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/stream/consumers.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/stream/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/stream/web.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/string_decoder.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/test.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/timers.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/timers/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/tls.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/trace_events.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/ts5.6/buffer.buffer.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/ts5.6/globals.typedarray.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/ts5.6/index.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/tty.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/url.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/util.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/v8.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/vm.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/wasi.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/worker_threads.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/zlib.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/LICENSE", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/README.md", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/agent.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/api.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/balanced-pool.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/cache.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/client.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/connector.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/content-type.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/cookies.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/diagnostics-channel.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/dispatcher.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/env-http-proxy-agent.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/errors.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/eventsource.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/fetch.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/file.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/filereader.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/formdata.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/global-dispatcher.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/global-origin.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/handlers.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/header.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/index.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/interceptors.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/mock-agent.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/mock-client.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/mock-errors.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/mock-interceptor.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/mock-pool.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/package.json", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/patch.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/pool-stats.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/pool.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/proxy-agent.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/readable.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/retry-agent.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/retry-handler.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/util.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/webidl.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/websocket.d.ts", + "create-links.js", + ], + "links": Array [ + Object { + "kind": "folderLink", + "linkPath": "build-tests/package-extractor-test-01/node_modules/@types/node", + "targetPath": "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node", + }, + Object { + "kind": "folderLink", + "linkPath": "build-tests/package-extractor-test-01/node_modules/package-extractor-test-02", + "targetPath": "build-tests/package-extractor-test-02", + }, + Object { + "kind": "folderLink", + "linkPath": "build-tests/package-extractor-test-01/node_modules/package-extractor-test-03", + "targetPath": "build-tests/package-extractor-test-03", + }, + Object { + "kind": "folderLink", + "linkPath": "build-tests/package-extractor-test-02/node_modules/package-extractor-test-03", + "targetPath": "build-tests/package-extractor-test-03", + }, + Object { + "kind": "folderLink", + "linkPath": "build-tests/package-extractor-test-03/node_modules/@types/node", + "targetPath": "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node", + }, + Object { + "kind": "folderLink", + "linkPath": "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/undici-types", + "targetPath": "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types", + }, + ], + "mainProjectName": "package-extractor-test-01", + "projects": Array [ + Object { + "path": "build-tests/package-extractor-test-01", + "projectName": "package-extractor-test-01", + }, + Object { + "path": "build-tests/package-extractor-test-02", + "projectName": "package-extractor-test-02", + }, + Object { + "path": "build-tests/package-extractor-test-03", + "projectName": "package-extractor-test-03", + }, + ], +} +`; + +exports[`PackageExtractor should extract project with script linkCreation and custom linkCreationScriptPath 1`] = ` +Object { + "files": Array [ + "build-tests/package-extractor-test-01/.rush/temp/shrinkwrap-deps.json", + "build-tests/package-extractor-test-01/package.json", + "build-tests/package-extractor-test-01/src/index.js", + "build-tests/package-extractor-test-01/src/subdir/file.js", + "build-tests/package-extractor-test-02/.rush/temp/shrinkwrap-deps.json", + "build-tests/package-extractor-test-02/package.json", + "build-tests/package-extractor-test-02/src/index.js", + "build-tests/package-extractor-test-03/.rush/temp/shrinkwrap-deps.json", + "build-tests/package-extractor-test-03/package.json", + "build-tests/package-extractor-test-03/src/index.js", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/LICENSE", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/README.md", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/assert.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/assert/strict.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/async_hooks.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/buffer.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/child_process.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/cluster.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/console.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/constants.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/crypto.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/dgram.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/diagnostics_channel.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/dns.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/dns/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/domain.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/events.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/fs.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/fs/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/globals.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/globals.global.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/http.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/http2.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/https.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/index.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/inspector.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/module.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/net.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/os.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/package.json", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/path.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/perf_hooks.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/process.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/punycode.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/querystring.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/readline.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/repl.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/stream.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/stream/consumers.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/stream/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/stream/web.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/string_decoder.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/timers.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/timers/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/tls.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/trace_events.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/tty.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/url.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/util.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/v8.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/vm.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/wasi.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/worker_threads.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node/zlib.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/LICENSE", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/README.md", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/assert.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/assert/strict.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/async_hooks.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/buffer.buffer.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/buffer.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/child_process.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/cluster.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/compatibility/disposable.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/compatibility/index.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/compatibility/indexable.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/compatibility/iterators.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/console.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/constants.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/crypto.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/dgram.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/diagnostics_channel.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/dns.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/dns/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/dom-events.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/domain.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/events.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/fs.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/fs/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/globals.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/globals.typedarray.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/http.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/http2.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/https.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/index.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/inspector.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/module.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/net.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/os.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/package.json", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/path.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/perf_hooks.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/process.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/punycode.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/querystring.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/readline.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/readline/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/repl.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/sea.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/stream.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/stream/consumers.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/stream/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/stream/web.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/string_decoder.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/test.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/timers.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/timers/promises.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/tls.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/trace_events.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/ts5.6/buffer.buffer.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/ts5.6/globals.typedarray.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/ts5.6/index.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/tty.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/url.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/util.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/v8.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/vm.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/wasi.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/worker_threads.d.ts", + "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node/zlib.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/LICENSE", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/README.md", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/agent.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/api.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/balanced-pool.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/cache.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/client.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/connector.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/content-type.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/cookies.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/diagnostics-channel.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/dispatcher.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/env-http-proxy-agent.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/errors.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/eventsource.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/fetch.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/file.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/filereader.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/formdata.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/global-dispatcher.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/global-origin.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/handlers.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/header.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/index.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/interceptors.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/mock-agent.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/mock-client.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/mock-errors.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/mock-interceptor.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/mock-pool.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/package.json", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/patch.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/pool-stats.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/pool.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/proxy-agent.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/readable.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/retry-agent.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/retry-handler.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/util.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/webidl.d.ts", + "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types/websocket.d.ts", + "foo/bar/baz.js", + ], + "links": Array [ + Object { + "kind": "folderLink", + "linkPath": "build-tests/package-extractor-test-01/node_modules/@types/node", + "targetPath": "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/@types/node", + }, + Object { + "kind": "folderLink", + "linkPath": "build-tests/package-extractor-test-01/node_modules/package-extractor-test-02", + "targetPath": "build-tests/package-extractor-test-02", + }, + Object { + "kind": "folderLink", + "linkPath": "build-tests/package-extractor-test-01/node_modules/package-extractor-test-03", + "targetPath": "build-tests/package-extractor-test-03", + }, + Object { + "kind": "folderLink", + "linkPath": "build-tests/package-extractor-test-02/node_modules/package-extractor-test-03", + "targetPath": "build-tests/package-extractor-test-03", + }, + Object { + "kind": "folderLink", + "linkPath": "build-tests/package-extractor-test-03/node_modules/@types/node", + "targetPath": "common/temp/default/node_modules/.pnpm/@types+node@17.0.41/node_modules/@types/node", + }, + Object { + "kind": "folderLink", + "linkPath": "common/temp/default/node_modules/.pnpm/@types+node@20.17.19/node_modules/undici-types", + "targetPath": "common/temp/default/node_modules/.pnpm/undici-types@6.19.8/node_modules/undici-types", + }, + ], + "mainProjectName": "package-extractor-test-01", + "projects": Array [ + Object { + "path": "build-tests/package-extractor-test-01", + "projectName": "package-extractor-test-01", + }, + Object { + "path": "build-tests/package-extractor-test-02", + "projectName": "package-extractor-test-02", + }, + Object { + "path": "build-tests/package-extractor-test-03", + "projectName": "package-extractor-test-03", + }, + ], +} +`; diff --git a/libraries/package-extractor/tsconfig.json b/libraries/package-extractor/tsconfig.json index bb839f30422..54fdeba4b2f 100644 --- a/libraries/package-extractor/tsconfig.json +++ b/libraries/package-extractor/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { // Needed because JSZip is missing a typing, and attempting to re-add the typing conflicts with diff --git a/libraries/package-extractor/webpack.config.js b/libraries/package-extractor/webpack.config.js index a3aa1698dd3..d0a48573569 100644 --- a/libraries/package-extractor/webpack.config.js +++ b/libraries/package-extractor/webpack.config.js @@ -2,7 +2,7 @@ const webpack = require('webpack'); const { PreserveDynamicRequireWebpackPlugin } = require('@rushstack/webpack-preserve-dynamic-require-plugin'); -const PathConstants = require('./lib/PathConstants'); +const { CREATE_LINKS_SCRIPT_FILENAME, SCRIPTS_FOLDER_PATH } = require('./lib-commonjs/PathConstants'); module.exports = () => { return { @@ -10,13 +10,13 @@ module.exports = () => { mode: 'development', // So the output isn't minified devtool: 'source-map', entry: { - [PathConstants.createLinksScriptFilename]: { - import: `${__dirname}/lib-esnext/scripts/create-links.js`, + [CREATE_LINKS_SCRIPT_FILENAME]: { + import: `${__dirname}/lib-esm/scripts/createLinks/start.js`, filename: `[name]` } }, output: { - path: PathConstants.scriptsFolderPath, + path: SCRIPTS_FOLDER_PATH, filename: '[name].js', chunkFilename: 'chunks/[name].js', // TODO: Don't allow any chunks to be created library: { @@ -29,6 +29,10 @@ module.exports = () => { new webpack.ids.DeterministicModuleIdsPlugin({ maxLength: 6 }) + ], + ignoreWarnings: [ + // This is included by the 'mz' package which is a dependency of '@pnpm/link-bins' but is unused + /Module not found: Error: Can't resolve 'graceful-fs'/ ] }; }; diff --git a/libraries/problem-matcher/.npmignore b/libraries/problem-matcher/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/libraries/problem-matcher/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/problem-matcher/CHANGELOG.json b/libraries/problem-matcher/CHANGELOG.json new file mode 100644 index 00000000000..678cde44a00 --- /dev/null +++ b/libraries/problem-matcher/CHANGELOG.json @@ -0,0 +1,53 @@ +{ + "name": "@rushstack/problem-matcher", + "entries": [ + { + "version": "0.2.1", + "tag": "@rushstack/problem-matcher_v0.2.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/problem-matcher_v0.2.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/problem-matcher_v0.1.1", + "date": "Tue, 30 Sep 2025 23:57:45 GMT", + "comments": { + "patch": [ + { + "comment": "Fix multi-line looping problem matcher message parsing" + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/problem-matcher_v0.1.0", + "date": "Tue, 30 Sep 2025 20:33:51 GMT", + "comments": { + "minor": [ + { + "comment": "Add @rushstack/problem-matcher library to parse and use VS Code style problem matchers" + } + ] + } + } + ] +} diff --git a/libraries/problem-matcher/CHANGELOG.md b/libraries/problem-matcher/CHANGELOG.md new file mode 100644 index 00000000000..340fd290df5 --- /dev/null +++ b/libraries/problem-matcher/CHANGELOG.md @@ -0,0 +1,32 @@ +# Change Log - @rushstack/problem-matcher + +This log was last generated on Fri, 20 Feb 2026 00:15:04 GMT and should not be manually modified. + +## 0.2.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.2.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.1.1 +Tue, 30 Sep 2025 23:57:45 GMT + +### Patches + +- Fix multi-line looping problem matcher message parsing + +## 0.1.0 +Tue, 30 Sep 2025 20:33:51 GMT + +### Minor changes + +- Add @rushstack/problem-matcher library to parse and use VS Code style problem matchers + diff --git a/libraries/problem-matcher/LICENSE b/libraries/problem-matcher/LICENSE new file mode 100644 index 00000000000..878f9710d96 --- /dev/null +++ b/libraries/problem-matcher/LICENSE @@ -0,0 +1,24 @@ +@rushstack/problem-matcher + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/libraries/problem-matcher/README.md b/libraries/problem-matcher/README.md new file mode 100644 index 00000000000..78d048be722 --- /dev/null +++ b/libraries/problem-matcher/README.md @@ -0,0 +1,12 @@ +# @rushstack/problem-matcher + +Parse VS Code style problem matcher definitions and extract structured problem reports (errors, warnings, info) from strings. + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/libraries/problem-matcher/CHANGELOG.md) - Find + out what's new in the latest version +- [API Reference](https://api.rushstack.io/pages/problem-matcher/) + +`@rushstack/problem-matcher` is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/libraries/problem-matcher/config/api-extractor.json b/libraries/problem-matcher/config/api-extractor.json new file mode 100644 index 00000000000..b53db1d0910 --- /dev/null +++ b/libraries/problem-matcher/config/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "decoupled-local-node-rig/profiles/default/config/api-extractor-base.json" +} diff --git a/libraries/problem-matcher/config/jest.config.json b/libraries/problem-matcher/config/jest.config.json new file mode 100644 index 00000000000..7c0f9ccc9d6 --- /dev/null +++ b/libraries/problem-matcher/config/jest.config.json @@ -0,0 +1,3 @@ +{ + "extends": "decoupled-local-node-rig/profiles/default/config/jest.config.json" +} diff --git a/libraries/problem-matcher/config/rig.json b/libraries/problem-matcher/config/rig.json new file mode 100644 index 00000000000..cc98dea43dd --- /dev/null +++ b/libraries/problem-matcher/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "decoupled-local-node-rig" +} diff --git a/libraries/problem-matcher/eslint.config.js b/libraries/problem-matcher/eslint.config.js new file mode 100644 index 00000000000..f83aea7d1b7 --- /dev/null +++ b/libraries/problem-matcher/eslint.config.js @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); +const tsdocMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/tsdoc'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + ...tsdocMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/problem-matcher/package.json b/libraries/problem-matcher/package.json new file mode 100644 index 00000000000..092a5fc6ab7 --- /dev/null +++ b/libraries/problem-matcher/package.json @@ -0,0 +1,55 @@ +{ + "name": "@rushstack/problem-matcher", + "version": "0.2.1", + "description": "A library for parsing VS Code style problem matchers", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/problem-matcher.d.ts", + "exports": { + ".": { + "types": "./dist/problem-matcher.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "license": "MIT", + "repository": { + "url": "https://github.com/microsoft/rushstack.git", + "type": "git", + "directory": "libraries/problem-matcher" + }, + "scripts": { + "build": "heft build --clean", + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" + }, + "devDependencies": { + "@rushstack/heft": "1.2.22", + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + }, + "sideEffects": false +} diff --git a/libraries/problem-matcher/src/ProblemMatcher.ts b/libraries/problem-matcher/src/ProblemMatcher.ts new file mode 100644 index 00000000000..14f111a5c96 --- /dev/null +++ b/libraries/problem-matcher/src/ProblemMatcher.ts @@ -0,0 +1,391 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * Represents the severity level of a problem. + * + * @public + */ +export type ProblemSeverity = 'error' | 'warning' | 'info'; + +/** + * Represents a problem (generally an error or warning) detected in the console output. + * + * @public + */ +export interface IProblem { + /** The name of the matcher that detected the problem. */ + readonly matcherName: string; + /** Parsed message from the problem matcher */ + readonly message: string; + /** Parsed severity level from the problem matcher */ + readonly severity?: ProblemSeverity; + /** Parsed file path from the problem matcher */ + readonly file?: string; + /** Parsed line number from the problem matcher */ + readonly line?: number; + /** Parsed column number from the problem matcher */ + readonly column?: number; + /** Parsed ending line number from the problem matcher */ + readonly endLine?: number; + /** Parsed ending column number from the problem matcher */ + readonly endColumn?: number; + /** Parsed error or warning code from the problem matcher */ + readonly code?: string; +} + +/** + * A problem matcher processes one line at a time and returns an {@link IProblem} if a match occurs. + * + * @remarks + * Multi-line matchers may keep internal state and emit on a later line; they can also optionally + * implement `flush()` to emit any buffered problems when the stream closes. + * + * @public + */ +export interface IProblemMatcher { + /** A friendly (and stable) name identifying the matcher. */ + readonly name: string; + /** + * Attempt to match a problem for the provided line of console output. + * + * @param line - A single line of text, always terminated with a newline character (\\n). + * @returns A problem if recognized, otherwise `false`. + */ + exec(line: string): IProblem | false; + /** + * Flush any buffered state and return additional problems. Optional. + */ + flush?(): IProblem[]; +} + +/** + * VS Code style problem matcher pattern definition. + * + * @remarks + * This mirrors the shape used in VS Code's `problemMatcher.pattern` entries. + * Reference: https://code.visualstudio.com/docs/editor/tasks#_defining-a-problem-matcher + * + * @public + */ +export interface IProblemPattern { + /** A regular expression used to match the problem. */ + regexp: string; + /** Match index for the file path. */ + file?: number; + /** Match index for the location. */ + location?: number; + /** Match index for the starting line number. */ + line?: number; + /** Match index for the starting column number. */ + column?: number; + /** Match index for the ending line number. */ + endLine?: number; + /** Match index for the ending column number. */ + endColumn?: number; + /** Match index for the severity level. */ + severity?: number; + /** Match index for the problem code. */ + code?: number; + /** Match index for the problem message. */ + message: number; + /** If true, the last pattern in a multi-line matcher may repeat (loop) producing multiple problems */ + loop?: boolean; +} + +/** + * Minimal VS Code problem matcher definition. + * + * @public + */ +export interface IProblemMatcherJson { + /** A friendly (and stable) name identifying the matcher. */ + name: string; + /** An optional default severity to apply if the pattern does not capture one. */ + severity?: ProblemSeverity; + /** A single pattern or an array of patterns to match. */ + pattern: IProblemPattern | IProblemPattern[]; +} + +/** + * Parse VS Code problem matcher JSON definitions into {@link IProblemMatcher} objects. + * + * @public + */ +export function parseProblemMatchersJson(problemMatchers: IProblemMatcherJson[]): IProblemMatcher[] { + const result: IProblemMatcher[] = []; + + for (const matcher of problemMatchers) { + const problemPatterns: IProblemPattern[] = Array.isArray(matcher.pattern) + ? matcher.pattern + : [matcher.pattern]; + if (problemPatterns.length === 0) { + continue; + } + + const name: string = matcher.name; + const defaultSeverity: ProblemSeverity | undefined = matcher.severity; + const compiled: ICompiledProblemPattern[] = compileProblemPatterns(problemPatterns); + + if (compiled.length === 1) { + result.push(createSingleLineMatcher(name, compiled[0], defaultSeverity)); + } else { + result.push(createMultiLineMatcher(name, compiled, defaultSeverity)); + } + } + + return result; +} + +function toNumber(text: string | undefined): number | undefined { + if (!text) { + return undefined; + } + const n: number = parseInt(text, 10); + return isNaN(n) ? undefined : n; +} + +function normalizeSeverity(raw: string | undefined): ProblemSeverity | undefined { + if (!raw) { + return undefined; + } + const lowered: string = raw.toLowerCase(); + // Support full words as well as common abbreviations (e.g. single-letter tokens) + if (lowered.indexOf('err') === 0) return 'error'; + if (lowered.indexOf('warn') === 0) return 'warning'; + if (lowered.indexOf('info') === 0) return 'info'; + return undefined; +} + +interface ICompiledProblemPattern { + re: RegExp; + spec: IProblemPattern; +} + +function compileProblemPatterns(problemPatterns: IProblemPattern[]): ICompiledProblemPattern[] { + return problemPatterns.map((problemPattern) => { + let reStr: string = problemPattern.regexp; + if (/\\r?\\n\$/.test(reStr) || /\\n\$/.test(reStr)) { + // already newline aware + } else if (reStr.length > 0 && reStr.charAt(reStr.length - 1) === '$') { + reStr = reStr.slice(0, -1) + '\\r?\\n$'; + } else { + reStr = reStr + '(?:\\r?\\n)'; + } + const re: RegExp = new RegExp(reStr); + return { re, spec: problemPattern }; + }); +} + +/** + * Shared capture structure used by both single-line and multi-line implementations. + */ +interface ICapturesMutable { + file?: string; + line?: number; + column?: number; + endLine?: number; + endColumn?: number; + severity?: ProblemSeverity; + code?: string; + messageParts: string[]; +} + +function createEmptyCaptures(): ICapturesMutable { + return { messageParts: [] }; +} + +/** + * Apply one pattern's regex match to the (possibly accumulating) captures. + */ +function applyPatternCaptures( + spec: IProblemPattern, + reMatch: RegExpExecArray, + captures: ICapturesMutable, + defaultSeverity: ProblemSeverity | undefined +): void { + if (spec.file && reMatch[spec.file]) { + captures.file = reMatch[spec.file]; + } + + if (spec.location && reMatch[spec.location]) { + const loc: string = reMatch[spec.location]; + const parts: string[] = loc.split(/[,.:]/).filter((s) => s.length > 0); + if (parts.length === 1) { + captures.line = toNumber(parts[0]); + } else if (parts.length === 2) { + captures.line = toNumber(parts[0]); + captures.column = toNumber(parts[1]); + } else if (parts.length === 4) { + captures.line = toNumber(parts[0]); + captures.column = toNumber(parts[1]); + captures.endLine = toNumber(parts[2]); + captures.endColumn = toNumber(parts[3]); + } + } else { + if (spec.line && reMatch[spec.line]) { + captures.line = toNumber(reMatch[spec.line]); + } + if (spec.column && reMatch[spec.column]) { + captures.column = toNumber(reMatch[spec.column]); + } + } + + if (spec.endLine && reMatch[spec.endLine]) { + captures.endLine = toNumber(reMatch[spec.endLine]); + } + if (spec.endColumn && reMatch[spec.endColumn]) { + captures.endColumn = toNumber(reMatch[spec.endColumn]); + } + + if (spec.severity && reMatch[spec.severity]) { + captures.severity = normalizeSeverity(reMatch[spec.severity]) || defaultSeverity; + } else if (!captures.severity && defaultSeverity) { + captures.severity = defaultSeverity; + } + + if (spec.code && reMatch[spec.code]) { + captures.code = reMatch[spec.code]; + } + + if (spec.message && reMatch[spec.message]) { + captures.messageParts.push(reMatch[spec.message]); + } +} + +function finalizeProblem( + matcherName: string, + captures: ICapturesMutable, + defaultSeverity: ProblemSeverity | undefined +): IProblem { + // For multi-line patterns, use only the last non-empty message part + const message: string = + captures.messageParts.length > 0 ? captures.messageParts[captures.messageParts.length - 1] : ''; + + return { + matcherName, + file: captures.file, + line: captures.line, + column: captures.column, + endLine: captures.endLine, + endColumn: captures.endColumn, + severity: captures.severity || defaultSeverity, + code: captures.code, + message: message + }; +} + +function createSingleLineMatcher( + name: string, + compiled: ICompiledProblemPattern, + defaultSeverity: ProblemSeverity | undefined +): IProblemMatcher { + const { re, spec } = compiled; + return { + name, + exec(line: string): IProblem | false { + const match: RegExpExecArray | null = re.exec(line); + if (!match) { + return false; + } + const captures: ICapturesMutable = createEmptyCaptures(); + applyPatternCaptures(spec, match, captures, defaultSeverity); + return finalizeProblem(name, captures, defaultSeverity); + } + }; +} + +function createMultiLineMatcher( + name: string, + compiled: ICompiledProblemPattern[], + defaultSeverity: ProblemSeverity | undefined +): IProblemMatcher { + // currentIndex points to the next pattern we expect to match. When it equals compiled.length + // and the last pattern is a loop, we are in a special "loop state" where additional lines + // should be attempted against only the last pattern to emit more problems. + let currentIndex: number = 0; + const lastSpec: IProblemPattern = compiled[compiled.length - 1].spec; + const lastIsLoop: boolean = !!lastSpec.loop; + + let captures: ICapturesMutable = createEmptyCaptures(); + + return { + name, + exec(line: string): IProblem | false { + let effectiveMatch: RegExpExecArray | null = null; + let effectiveSpec: IProblemPattern | undefined; + + // Determine matching behavior based on current state + if (currentIndex === compiled.length && lastIsLoop) { + // Loop state: only try to match the last pattern + const lastPattern: ICompiledProblemPattern = compiled[compiled.length - 1]; + effectiveMatch = lastPattern.re.exec(line); + if (!effectiveMatch) { + // Exit loop state and reset for a potential new sequence + currentIndex = 0; + captures = createEmptyCaptures(); + // Attempt to treat this line as a fresh start (pattern 0) + const first: ICompiledProblemPattern = compiled[0]; + const fresh: RegExpExecArray | null = first.re.exec(line); + if (!fresh) { + return false; + } + effectiveMatch = fresh; + effectiveSpec = first.spec; + currentIndex = compiled.length > 1 ? 1 : compiled.length; + } else { + effectiveSpec = lastPattern.spec; + // currentIndex remains compiled.length (loop state) until we decide to emit + } + } else { + // Normal multi-line progression state + const active: ICompiledProblemPattern = compiled[currentIndex]; + const reMatch: RegExpExecArray | null = active.re.exec(line); + if (!reMatch) { + // Reset and maybe attempt new start + currentIndex = 0; + captures = createEmptyCaptures(); + const { re: re0, spec: spec0 } = compiled[0]; + const restartMatch: RegExpExecArray | null = re0.exec(line); + if (!restartMatch) { + return false; + } + effectiveMatch = restartMatch; + effectiveSpec = spec0; + currentIndex = compiled.length > 1 ? 1 : compiled.length; + } else { + effectiveMatch = reMatch; + effectiveSpec = active.spec; + currentIndex++; + } + } + + applyPatternCaptures( + effectiveSpec as IProblemPattern, + effectiveMatch as RegExpExecArray, + captures, + defaultSeverity + ); + + // If we haven't matched all patterns yet (and not in loop state), wait for more lines + if (currentIndex < compiled.length) { + return false; + } + + // We have matched the full sequence (either first completion or a loop iteration) + const problem: IProblem = finalizeProblem(name, captures, defaultSeverity); + + if (lastIsLoop) { + // Stay in loop state; reset fields that accumulate per problem but retain other context (e.g., file if first pattern captured it?) + // For safety, if the last pattern provided the file each iteration we keep overwriting anyway. + captures.messageParts = []; + // Do not clear entire captures to allow preceding pattern data (e.g., summary) to persist if desirable. + } else { + currentIndex = 0; + captures = createEmptyCaptures(); + } + + return problem; + } + }; +} diff --git a/libraries/problem-matcher/src/index.ts b/libraries/problem-matcher/src/index.ts new file mode 100644 index 00000000000..22e215252c7 --- /dev/null +++ b/libraries/problem-matcher/src/index.ts @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** + * Parse VS Code style problem matcher definitions and use them to extract + * structured problem reports from strings. + * + * @packageDocumentation + */ + +export type { + ProblemSeverity, + IProblemMatcher, + IProblemMatcherJson, + IProblemPattern, + IProblem +} from './ProblemMatcher'; +export { parseProblemMatchersJson } from './ProblemMatcher'; diff --git a/libraries/problem-matcher/src/test/ProblemMatcher.test.ts b/libraries/problem-matcher/src/test/ProblemMatcher.test.ts new file mode 100644 index 00000000000..07a6b910c98 --- /dev/null +++ b/libraries/problem-matcher/src/test/ProblemMatcher.test.ts @@ -0,0 +1,180 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { parseProblemMatchersJson, type IProblemMatcherJson } from '../ProblemMatcher'; + +describe('parseProblemMatchersJson - single line', () => { + it('matches a tsc style line', () => { + const matcher: IProblemMatcherJson = { + name: 'tsc-single', + pattern: { + regexp: '^(.*)\\((\\d+),(\\d+)\\): (error|warning) (TS\\d+): (.*)$', + file: 1, + line: 2, + column: 3, + severity: 4, + code: 5, + message: 6 + } + }; + + const [compiled] = parseProblemMatchersJson([matcher]); + const line = 'src/example.ts(12,34): error TS1000: Something bad happened\n'; + const prob = compiled.exec(line); + expect(prob).toBeTruthy(); + if (prob !== false) { + expect(prob.file).toBe('src/example.ts'); + expect(prob.line).toBe(12); + expect(prob.column).toBe(34); + expect(prob.code).toBe('TS1000'); + expect(prob.severity).toBe('error'); + expect(prob.message).toBe('Something bad happened'); + } + }); + + it('returns false for non-matching line', () => { + const matcher: IProblemMatcherJson = { + name: 'simple', + pattern: { + regexp: '^(.*)\\((\\d+),(\\d+)\\): error (E\\d+): (.*)$', + file: 1, + line: 2, + column: 3, + code: 4, + message: 5 + } + }; + const [compiled] = parseProblemMatchersJson([matcher]); + const notMatched = compiled.exec('This will not match\n'); + expect(notMatched).toBe(false); + }); +}); + +describe('parseProblemMatchersJson - default severity', () => { + it('applies default severity when group absent', () => { + const matcher: IProblemMatcherJson = { + name: 'default-sev', + severity: 'warning', + pattern: { + regexp: '^(.*):(\\d+):(\\d+): (W\\d+): (.*)$', + file: 1, + line: 2, + column: 3, + code: 4, + message: 5 + } + }; + const [compiled] = parseProblemMatchersJson([matcher]); + const prob = compiled.exec('lib/z.c:5:7: W123: Be careful\n'); + if (prob === false) throw new Error('Expected match'); + expect(prob.severity).toBe('warning'); + expect(prob.code).toBe('W123'); + }); +}); + +describe('parseProblemMatchersJson - multi line', () => { + it('accumulates message parts and resets after emit', () => { + const matcher: IProblemMatcherJson = { + name: 'multi-basic', + pattern: [ + { regexp: '^File: (.*)$', file: 1, message: 0 }, + { regexp: '^Pos: (\\d+),(\\d+)$', line: 1, column: 2, message: 0 }, + { regexp: '^Severity: (error|warning)$', severity: 1, message: 0 }, + { regexp: '^Msg: (.*)$', message: 1 } + ] + }; + const [compiled] = parseProblemMatchersJson([matcher]); + // Feed lines + expect(compiled.exec('File: src/a.c\n')).toBe(false); + expect(compiled.exec('Pos: 10,20\n')).toBe(false); + expect(compiled.exec('Severity: error\n')).toBe(false); + const final = compiled.exec('Msg: Something broke\n'); + if (final === false) throw new Error('Expected final match'); + expect(final.file).toBe('src/a.c'); + expect(final.line).toBe(10); + expect(final.column).toBe(20); + expect(final.severity).toBe('error'); + // Ensure message assembled (empty placeholders filtered, only last part meaningful) + expect(final.message).toBe('Something broke'); + + // Next unrelated line should not erroneously reuse old state + const no = compiled.exec('Msg: stray\n'); + expect(no).toBe(false); + }); +}); + +describe('parseProblemMatchersJson - looping last pattern', () => { + it('emits multiple problems after first sequence completion', () => { + const matcher: IProblemMatcherJson = { + name: 'looping', + severity: 'error', + pattern: [ + { regexp: '^Summary with (\\d+) issues$', message: 1 }, + { + regexp: '^(.*)\\((\\d+),(\\d+)\\): (E\\d+): (.*)$', + file: 1, + line: 2, + column: 3, + code: 4, + message: 5, + loop: true + } + ] + }; + const [compiled] = parseProblemMatchersJson([matcher]); + // Start sequence + expect(compiled.exec('Summary with 2 issues\n')).toBe(false); + const first = compiled.exec('src/a.c(1,2): E001: First\n'); + const second = compiled.exec('src/b.c(3,4): E002: Second\n'); + if (first === false || second === false) throw new Error('Expected loop matches'); + expect(first.file).toBe('src/a.c'); + expect(second.file).toBe('src/b.c'); + expect(first.code).toBe('E001'); + expect(second.code).toBe('E002'); + expect(first.severity).toBe('error'); + expect(second.severity).toBe('error'); + // Exiting loop with unrelated line resets state + expect(compiled.exec('Unrelated line\n')).toBe(false); + }); +}); + +describe('parseProblemMatchersJson - location parsing variants', () => { + it('parses (line,column) location group', () => { + const matcher: IProblemMatcherJson = { + name: 'loc-group', + pattern: { + regexp: '^(.*)\\((\\d+),(\\d+)\\): (.*)$', + file: 1, + line: 2, + column: 3, + message: 4 + } + }; + const [compiled] = parseProblemMatchersJson([matcher]); + const prob = compiled.exec('path/file.c(10,5): details here\n'); + if (prob === false) throw new Error('Expected match'); + expect(prob.file).toBe('path/file.c'); + expect(prob.line).toBe(10); + expect(prob.column).toBe(5); + }); + + it('parses explicit endLine/endColumn groups', () => { + const matcher: IProblemMatcherJson = { + name: 'end-range', + pattern: { + regexp: '^(.*)\\((\\d+),(\\d+),(\\d+),(\\d+)\\): (.*)$', + file: 1, + line: 2, + column: 3, + endLine: 4, + endColumn: 5, + message: 6 + } + }; + const [compiled] = parseProblemMatchersJson([matcher]); + const prob = compiled.exec('lib/x.c(1,2,3,4): thing\n'); + if (prob === false) throw new Error('Expected match'); + expect(prob.endLine).toBe(3); + expect(prob.endColumn).toBe(4); + }); +}); diff --git a/libraries/problem-matcher/tsconfig.json b/libraries/problem-matcher/tsconfig.json new file mode 100644 index 00000000000..1a33d17b873 --- /dev/null +++ b/libraries/problem-matcher/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/libraries/rig-package/.eslintrc.js b/libraries/rig-package/.eslintrc.js deleted file mode 100644 index f7ee2a5d364..00000000000 --- a/libraries/rig-package/.eslintrc.js +++ /dev/null @@ -1,11 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node', - '@rushstack/eslint-config/mixins/friendly-locals', - '@rushstack/eslint-config/mixins/tsdoc' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/libraries/rig-package/.npmignore b/libraries/rig-package/.npmignore index 302dbc5b019..f7a40e10213 100644 --- a/libraries/rig-package/.npmignore +++ b/libraries/rig-package/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,16 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/rig-package/CHANGELOG.json b/libraries/rig-package/CHANGELOG.json index cc8be9f8945..d711fdc26c4 100644 --- a/libraries/rig-package/CHANGELOG.json +++ b/libraries/rig-package/CHANGELOG.json @@ -1,6 +1,167 @@ { "name": "@rushstack/rig-package", "entries": [ + { + "version": "0.7.3", + "tag": "@rushstack/rig-package_v0.7.3", + "date": "Sat, 18 Apr 2026 03:47:09 GMT", + "comments": { + "patch": [ + { + "comment": "Replace `strip-json-comments` with `jju.parse()` for JSONC parsing." + } + ] + } + }, + { + "version": "0.7.2", + "tag": "@rushstack/rig-package_v0.7.2", + "date": "Wed, 25 Feb 2026 00:34:29 GMT", + "comments": { + "patch": [ + { + "comment": "Update `ajv` dependency to `~8.18.0` to mitigate CVE-2025-69873." + } + ] + } + }, + { + "version": "0.7.1", + "tag": "@rushstack/rig-package_v0.7.1", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ] + } + }, + { + "version": "0.7.0", + "tag": "@rushstack/rig-package_v0.7.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ] + } + }, + { + "version": "0.6.0", + "tag": "@rushstack/rig-package_v0.6.0", + "date": "Fri, 03 Oct 2025 20:09:59 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize import of builtin modules to use the `node:` protocol." + } + ] + } + }, + { + "version": "0.5.3", + "tag": "@rushstack/rig-package_v0.5.3", + "date": "Sat, 27 Jul 2024 00:10:27 GMT", + "comments": { + "patch": [ + { + "comment": "Include CHANGELOG.md in published releases again" + } + ] + } + }, + { + "version": "0.5.2", + "tag": "@rushstack/rig-package_v0.5.2", + "date": "Sat, 17 Feb 2024 06:24:35 GMT", + "comments": { + "patch": [ + { + "comment": "Fix broken link to API documentation" + } + ] + } + }, + { + "version": "0.5.1", + "tag": "@rushstack/rig-package_v0.5.1", + "date": "Tue, 26 Sep 2023 09:30:33 GMT", + "comments": { + "patch": [ + { + "comment": "Update type-only imports to include the type modifier." + } + ] + } + }, + { + "version": "0.5.0", + "tag": "@rushstack/rig-package_v0.5.0", + "date": "Fri, 15 Sep 2023 00:36:58 GMT", + "comments": { + "minor": [ + { + "comment": "Update @types/node from 14 to 18" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.4`" + } + ] + } + }, + { + "version": "0.4.1", + "tag": "@rushstack/rig-package_v0.4.1", + "date": "Tue, 08 Aug 2023 07:10:40 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.3`" + } + ] + } + }, + { + "version": "0.4.0", + "tag": "@rushstack/rig-package_v0.4.0", + "date": "Mon, 19 Jun 2023 22:40:21 GMT", + "comments": { + "minor": [ + { + "comment": "Expose an `IRigConfig` interface that `RigConfig` implements." + } + ] + } + }, + { + "version": "0.3.21", + "tag": "@rushstack/rig-package_v0.3.21", + "date": "Thu, 15 Jun 2023 00:21:01 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.2`" + } + ] + } + }, + { + "version": "0.3.20", + "tag": "@rushstack/rig-package_v0.3.20", + "date": "Wed, 07 Jun 2023 22:45:16 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/eslint-config\" to `3.3.1`" + } + ] + } + }, { "version": "0.3.19", "tag": "@rushstack/rig-package_v0.3.19", diff --git a/libraries/rig-package/CHANGELOG.md b/libraries/rig-package/CHANGELOG.md index 8cc0bb318c9..9c90b04d263 100644 --- a/libraries/rig-package/CHANGELOG.md +++ b/libraries/rig-package/CHANGELOG.md @@ -1,6 +1,91 @@ # Change Log - @rushstack/rig-package -This log was last generated on Mon, 22 May 2023 06:34:33 GMT and should not be manually modified. +This log was last generated on Sat, 18 Apr 2026 03:47:09 GMT and should not be manually modified. + +## 0.7.3 +Sat, 18 Apr 2026 03:47:09 GMT + +### Patches + +- Replace `strip-json-comments` with `jju.parse()` for JSONC parsing. + +## 0.7.2 +Wed, 25 Feb 2026 00:34:29 GMT + +### Patches + +- Update `ajv` dependency to `~8.18.0` to mitigate CVE-2025-69873. + +## 0.7.1 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.7.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.6.0 +Fri, 03 Oct 2025 20:09:59 GMT + +### Minor changes + +- Normalize import of builtin modules to use the `node:` protocol. + +## 0.5.3 +Sat, 27 Jul 2024 00:10:27 GMT + +### Patches + +- Include CHANGELOG.md in published releases again + +## 0.5.2 +Sat, 17 Feb 2024 06:24:35 GMT + +### Patches + +- Fix broken link to API documentation + +## 0.5.1 +Tue, 26 Sep 2023 09:30:33 GMT + +### Patches + +- Update type-only imports to include the type modifier. + +## 0.5.0 +Fri, 15 Sep 2023 00:36:58 GMT + +### Minor changes + +- Update @types/node from 14 to 18 + +## 0.4.1 +Tue, 08 Aug 2023 07:10:40 GMT + +_Version update only_ + +## 0.4.0 +Mon, 19 Jun 2023 22:40:21 GMT + +### Minor changes + +- Expose an `IRigConfig` interface that `RigConfig` implements. + +## 0.3.21 +Thu, 15 Jun 2023 00:21:01 GMT + +_Version update only_ + +## 0.3.20 +Wed, 07 Jun 2023 22:45:16 GMT + +_Version update only_ ## 0.3.19 Mon, 22 May 2023 06:34:33 GMT diff --git a/libraries/rig-package/README.md b/libraries/rig-package/README.md index 7f4c4b6eee2..8ad3e006bda 100644 --- a/libraries/rig-package/README.md +++ b/libraries/rig-package/README.md @@ -224,6 +224,6 @@ Note that there are also async variants of the functions that access the filesys - [CHANGELOG.md]( https://github.com/microsoft/rushstack/blob/main/libraries/rig-package/CHANGELOG.md) - Find out what's new in the latest version -- [API Reference](https://rushstack.io/pages/api/rig-package/) +- [API Reference](https://api.rushstack.io/pages/rig-package/) `@rushstack/rig-package` is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/libraries/rig-package/config/api-extractor.json b/libraries/rig-package/config/api-extractor.json index 996e271d3dd..b53db1d0910 100644 --- a/libraries/rig-package/config/api-extractor.json +++ b/libraries/rig-package/config/api-extractor.json @@ -1,19 +1,4 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - - "mainEntryPointFilePath": "/lib/index.d.ts", - - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, - - "docModel": { - "enabled": true, - "apiJsonFilePath": "../../../common/temp/api/.api.json" - }, - - "dtsRollup": { - "enabled": true - } + "extends": "decoupled-local-node-rig/profiles/default/config/api-extractor-base.json" } diff --git a/libraries/rig-package/config/heft.json b/libraries/rig-package/config/heft.json new file mode 100644 index 00000000000..2fc245813e5 --- /dev/null +++ b/libraries/rig-package/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "extends": "decoupled-local-node-rig/profiles/default/config/heft.json", + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/rig-package"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/libraries/rig-package/config/jest.config.json b/libraries/rig-package/config/jest.config.json index 4bb17bde3ee..7c0f9ccc9d6 100644 --- a/libraries/rig-package/config/jest.config.json +++ b/libraries/rig-package/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json" + "extends": "decoupled-local-node-rig/profiles/default/config/jest.config.json" } diff --git a/libraries/rig-package/config/rig.json b/libraries/rig-package/config/rig.json index 6ac88a96368..cc98dea43dd 100644 --- a/libraries/rig-package/config/rig.json +++ b/libraries/rig-package/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "decoupled-local-node-rig" } diff --git a/libraries/rig-package/eslint.config.js b/libraries/rig-package/eslint.config.js new file mode 100644 index 00000000000..f83aea7d1b7 --- /dev/null +++ b/libraries/rig-package/eslint.config.js @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); +const tsdocMixin = require('decoupled-local-node-rig/profiles/default/includes/eslint/flat/mixins/tsdoc'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + ...tsdocMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/rig-package/package.json b/libraries/rig-package/package.json index 65f452ec069..81be1e6a4d1 100644 --- a/libraries/rig-package/package.json +++ b/libraries/rig-package/package.json @@ -1,9 +1,33 @@ { "name": "@rushstack/rig-package", - "version": "0.3.19", + "version": "0.7.3", "description": "A system for sharing tool configurations between projects without duplicating config files.", - "main": "lib/index.js", - "typings": "dist/rig-package.d.ts", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/rig-package.d.ts", + "exports": { + ".": { + "types": "./dist/rig-package.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "license": "MIT", "repository": { "url": "https://github.com/microsoft/rushstack.git", @@ -12,21 +36,21 @@ }, "scripts": { "build": "heft build --clean", - "_phase:build": "heft build --clean", - "_phase:test": "heft test --no-build" + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" }, "dependencies": { - "resolve": "~1.22.1", - "strip-json-comments": "~3.1.1" + "jju": "~1.4.0", + "resolve": "~1.22.1" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft-node-rig": "1.13.0", - "@rushstack/heft": "0.50.6", - "@types/heft-jest": "1.0.1", - "@types/node": "14.18.36", + "@rushstack/heft": "1.2.22", + "@types/jju": "1.4.1", "@types/resolve": "1.20.2", - "ajv": "~6.12.5", + "ajv": "~8.20.0", + "decoupled-local-node-rig": "workspace:*", + "eslint": "~9.37.0", "resolve": "~1.22.1" - } + }, + "sideEffects": false } diff --git a/libraries/rig-package/src/Helpers.ts b/libraries/rig-package/src/Helpers.ts index aa816c5772e..49457a4f7fd 100644 --- a/libraries/rig-package/src/Helpers.ts +++ b/libraries/rig-package/src/Helpers.ts @@ -1,15 +1,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as fs from 'fs'; +import * as path from 'node:path'; +import * as fs from 'node:fs'; + import nodeResolve from 'resolve'; // These helpers avoid taking dependencies on other NPM packages -export class Helpers { - // Based on Path.isDownwardRelative() from @rushstack/node-core-library - private static _upwardPathSegmentRegex: RegExp = /([\/\\]|^)\.\.([\/\\]|$)/; +// Based on Path.isDownwardRelative() from @rushstack/node-core-library +const _upwardPathSegmentRegex: RegExp = /([\/\\]|^)\.\.([\/\\]|$)/; + +export class Helpers { public static async nodeResolveAsync(id: string, opts: nodeResolve.AsyncOpts): Promise { return await new Promise((resolve: (result: string) => void, reject: (error: Error) => void) => { nodeResolve(id, opts, (error: Error | null, result: string | undefined) => { @@ -22,9 +24,9 @@ export class Helpers { }); } - public static async fsExistsAsync(path: fs.PathLike): Promise { + public static async fsExistsAsync(filesystemPath: fs.PathLike): Promise { return await new Promise((resolve: (result: boolean) => void) => { - fs.exists(path, (exists: boolean) => { + fs.exists(filesystemPath, (exists: boolean) => { resolve(exists); }); }); @@ -36,7 +38,7 @@ export class Helpers { return false; } // Does it contain ".." - if (Helpers._upwardPathSegmentRegex.test(inputPath)) { + if (_upwardPathSegmentRegex.test(inputPath)) { return false; } return true; diff --git a/libraries/rig-package/src/RigConfig.ts b/libraries/rig-package/src/RigConfig.ts index d022955492f..28cdf252338 100644 --- a/libraries/rig-package/src/RigConfig.ts +++ b/libraries/rig-package/src/RigConfig.ts @@ -1,10 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as fs from 'fs'; +import * as path from 'node:path'; +import * as fs from 'node:fs'; + import * as nodeResolve from 'resolve'; -import stripJsonComments from 'strip-json-comments'; +import * as jju from 'jju'; import { Helpers } from './Helpers'; @@ -72,32 +73,7 @@ export interface ILoadForProjectFolderOptions { * * @public */ -export class RigConfig { - // For syntax details, see PackageNameParser from @rushstack/node-core-library - private static readonly _packageNameRegExp: RegExp = /^(@[A-Za-z0-9\-_\.]+\/)?[A-Za-z0-9\-_\.]+$/; - - // Rig package names must have the "-rig" suffix. - // Also silently accept "-rig-test" for our build test projects. - private static readonly _rigNameRegExp: RegExp = /-rig(-test)?$/; - - // Profiles must be lowercase alphanumeric words separated by hyphens - private static readonly _profileNameRegExp: RegExp = /^[a-z0-9_\.]+(\-[a-z0-9_\.]+)*$/; - - /** - * Returns the absolute path of the `rig.schema.json` JSON schema file for `config/rig.json`, - * which is bundled with this NPM package. - * - * @remarks - * The `RigConfig` class already performs schema validation when loading `rig.json`; however - * this schema file may be useful for integration with other validation tools. - * - * @public - */ - public static jsonSchemaPath: string = path.resolve(__dirname, './schemas/rig.schema.json'); - private static _jsonSchemaObject: object | undefined = undefined; - - private static readonly _configCache: Map = new Map(); - +export interface IRigConfig { /** * The project folder path that was passed to {@link RigConfig.loadForProjectFolder}, * which maybe an absolute or relative path. @@ -105,7 +81,7 @@ export class RigConfig { * @remarks * Example: `.` */ - public readonly projectFolderOriginalPath: string; + readonly projectFolderOriginalPath: string; /** * The absolute path for the project folder path that was passed to {@link RigConfig.loadForProjectFolder}. @@ -113,12 +89,12 @@ export class RigConfig { * @remarks * Example: `/path/to/your-project` */ - public readonly projectFolderPath: string; + readonly projectFolderPath: string; /** * Returns `true` if `config/rig.json` was found, or `false` otherwise. */ - public readonly rigFound: boolean; + readonly rigFound: boolean; /** * The full path to the `rig.json` file that was found, or `""` if none was found. @@ -126,7 +102,7 @@ export class RigConfig { * @remarks * Example: `/path/to/your-project/config/rig.json` */ - public readonly filePath: string; + readonly filePath: string; /** * The `"rigPackageName"` field from `rig.json`, or `""` if the file was not found. @@ -136,7 +112,7 @@ export class RigConfig { * * Example: `example-rig` */ - public readonly rigPackageName: string; + readonly rigPackageName: string; /** * The `"rigProfile"` value that was loaded from `rig.json`, or `""` if the file was not found. @@ -148,7 +124,7 @@ export class RigConfig { * * Example: `example-profile` */ - public readonly rigProfile: string; + readonly rigProfile: string; /** * The relative path to the rig profile specified by `rig.json`, or `""` if the file was not found. @@ -156,6 +132,114 @@ export class RigConfig { * @remarks * Example: `profiles/example-profile` */ + readonly relativeProfileFolderPath: string; + + /** + * Performs Node.js module resolution to locate the rig package folder, then returns the absolute path + * of the rig profile folder specified by `rig.json`. + * + * @remarks + * If no `rig.json` file was found, then this method throws an error. The first time this method + * is called, the result is cached and will be returned by all subsequent calls. + * + * Example: `/path/to/your-project/node_modules/example-rig/profiles/example-profile` + */ + getResolvedProfileFolder(): string; + + /** + * An async variant of {@link IRigConfig.getResolvedProfileFolder} + */ + getResolvedProfileFolderAsync(): Promise; + + /** + * This lookup first checks for the specified relative path under `projectFolderPath`; if it does + * not exist there, then it checks in the resolved rig profile folder. If the file is found, + * its absolute path is returned. Otherwise, `undefined` is returned. + * + * @remarks + * For example, suppose the rig profile is: + * + * `/path/to/your-project/node_modules/example-rig/profiles/example-profile` + * + * And suppose `configFileRelativePath` is `folder/file.json`. Then the following locations will be checked: + * + * `/path/to/your-project/folder/file.json` + * + * `/path/to/your-project/node_modules/example-rig/profiles/example-profile/folder/file.json` + */ + tryResolveConfigFilePath(configFileRelativePath: string): string | undefined; + + /** + * An async variant of {@link IRigConfig.tryResolveConfigFilePath} + */ + tryResolveConfigFilePathAsync(configFileRelativePath: string): Promise; +} + +// For syntax details, see PackageNameParser from @rushstack/node-core-library +const _packageNameRegExp: RegExp = /^(@[A-Za-z0-9\-_\.]+\/)?[A-Za-z0-9\-_\.]+$/; + +// Rig package names must have the "-rig" suffix. +// Also silently accept "-rig-test" for our build test projects. +const _rigNameRegExp: RegExp = /-rig(-test)?$/; + +// Profiles must be lowercase alphanumeric words separated by hyphens +const _profileNameRegExp: RegExp = /^[a-z0-9_\.]+(\-[a-z0-9_\.]+)*$/; + +let _jsonSchemaObject: object | undefined = undefined; + +const _configCache: Map = new Map(); + +/** + * {@inheritdoc IRigConfig} + * + * @public + */ +export class RigConfig implements IRigConfig { + /** + * Returns the absolute path of the `rig.schema.json` JSON schema file for `config/rig.json`, + * which is bundled with this NPM package. + * + * @remarks + * The `RigConfig` class already performs schema validation when loading `rig.json`; however + * this schema file may be useful for integration with other validation tools. + * + * @public + */ + public static jsonSchemaPath: string = path.resolve(__dirname, './schemas/rig.schema.json'); + + /** + * {@inheritdoc IRigConfig.projectFolderOriginalPath} + */ + public readonly projectFolderOriginalPath: string; + + /** + * {@inheritdoc IRigConfig.projectFolderPath} + */ + public readonly projectFolderPath: string; + + /** + * {@inheritdoc IRigConfig.rigFound} + */ + public readonly rigFound: boolean; + + /** + * {@inheritdoc IRigConfig.filePath} + */ + public readonly filePath: string; + + /** + * {@inheritdoc IRigConfig.rigPackageName} + */ + public readonly rigPackageName: string; + + /** + * {@inheritdoc IRigConfig.rigProfile} + */ + public readonly rigProfile: string; + + /** + * {@inheritdoc IRigConfig.relativeProfileFolderPath} + */ public readonly relativeProfileFolderPath: string; // Example: /path/to/your-project/node_modules/example-rig/ @@ -193,11 +277,11 @@ export class RigConfig { * Accessing this property may make a synchronous filesystem call. */ public static get jsonSchemaObject(): object { - if (RigConfig._jsonSchemaObject === undefined) { + if (_jsonSchemaObject === undefined) { const jsonSchemaContent: string = fs.readFileSync(RigConfig.jsonSchemaPath).toString(); - RigConfig._jsonSchemaObject = JSON.parse(jsonSchemaContent); + _jsonSchemaObject = JSON.parse(jsonSchemaContent); } - return RigConfig._jsonSchemaObject!; + return _jsonSchemaObject!; } /** @@ -211,9 +295,7 @@ export class RigConfig { const { overrideRigJsonObject, projectFolderPath } = options; const fromCache: RigConfig | undefined = - !options.bypassCache && !overrideRigJsonObject - ? RigConfig._configCache.get(projectFolderPath) - : undefined; + !options.bypassCache && !overrideRigJsonObject ? _configCache.get(projectFolderPath) : undefined; if (fromCache) { return fromCache; @@ -226,11 +308,11 @@ export class RigConfig { try { if (!json) { const rigConfigFileContent: string = fs.readFileSync(rigConfigFilePath).toString(); - json = JSON.parse(stripJsonComments(rigConfigFileContent)) as IRigConfigJson; + json = jju.parse(rigConfigFileContent) as IRigConfigJson; } - RigConfig._validateSchema(json); + _validateSchema(json); } catch (error) { - config = RigConfig._handleConfigError(error as Error, projectFolderPath, rigConfigFilePath); + config = new RigConfig(_handleConfigError(error as Error, projectFolderPath, rigConfigFilePath)); } if (!config) { @@ -245,7 +327,7 @@ export class RigConfig { } if (!overrideRigJsonObject) { - RigConfig._configCache.set(projectFolderPath, config); + _configCache.set(projectFolderPath, config); } return config; } @@ -257,7 +339,7 @@ export class RigConfig { const { overrideRigJsonObject, projectFolderPath } = options; const fromCache: RigConfig | false | undefined = - !options.bypassCache && !overrideRigJsonObject && RigConfig._configCache.get(projectFolderPath); + !options.bypassCache && !overrideRigJsonObject && _configCache.get(projectFolderPath); if (fromCache) { return fromCache; @@ -270,12 +352,12 @@ export class RigConfig { try { if (!json) { const rigConfigFileContent: string = (await fs.promises.readFile(rigConfigFilePath)).toString(); - json = JSON.parse(stripJsonComments(rigConfigFileContent)) as IRigConfigJson; + json = jju.parse(rigConfigFileContent) as IRigConfigJson; } - RigConfig._validateSchema(json); + _validateSchema(json); } catch (error) { - config = RigConfig._handleConfigError(error as Error, projectFolderPath, rigConfigFilePath); + config = new RigConfig(_handleConfigError(error as Error, projectFolderPath, rigConfigFilePath)); } if (!config) { @@ -290,40 +372,13 @@ export class RigConfig { } if (!overrideRigJsonObject) { - RigConfig._configCache.set(projectFolderPath, config); + _configCache.set(projectFolderPath, config); } return config; } - private static _handleConfigError( - error: NodeJS.ErrnoException, - projectFolderPath: string, - rigConfigFilePath: string - ): RigConfig { - if (error.code !== 'ENOENT' && error.code !== 'ENOTDIR') { - throw new Error(error.message + '\nError loading config file: ' + rigConfigFilePath); - } - - // File not found, i.e. no rig config - return new RigConfig({ - projectFolderPath, - - rigFound: false, - filePath: '', - rigPackageName: '', - rigProfile: '' - }); - } - /** - * Performs Node.js module resolution to locate the rig package folder, then returns the absolute path - * of the rig profile folder specified by `rig.json`. - * - * @remarks - * If no `rig.json` file was found, then this method throws an error. The first time this method - * is called, the result is cached and will be returned by all subsequent calls. - * - * Example: `/path/to/your-project/node_modules/example-rig/profiles/example-profile` + * {@inheritdoc IRigConfig.getResolvedProfileFolder} */ public getResolvedProfileFolder(): string { if (this._resolvedRigPackageFolder === undefined) { @@ -356,7 +411,7 @@ export class RigConfig { } /** - * An async variant of {@link RigConfig.getResolvedProfileFolder} + * {@inheritdoc IRigConfig.getResolvedProfileFolderAsync} */ public async getResolvedProfileFolderAsync(): Promise { if (this._resolvedRigPackageFolder === undefined) { @@ -389,20 +444,7 @@ export class RigConfig { } /** - * This lookup first checks for the specified relative path under `projectFolderPath`; if it does - * not exist there, then it checks in the resolved rig profile folder. If the file is found, - * its absolute path is returned. Otherwise, `undefined` is returned. - * - * @remarks - * For example, suppose the rig profile is: - * - * `/path/to/your-project/node_modules/example-rig/profiles/example-profile` - * - * And suppose `configFileRelativePath` is `folder/file.json`. Then the following locations will be checked: - * - * `/path/to/your-project/folder/file.json` - * - * `/path/to/your-project/node_modules/example-rig/profiles/example-profile/folder/file.json` + * {@inheritdoc IRigConfig.tryResolveConfigFilePath} */ public tryResolveConfigFilePath(configFileRelativePath: string): string | undefined { if (!Helpers.isDownwardRelative(configFileRelativePath)) { @@ -423,7 +465,7 @@ export class RigConfig { } /** - * An async variant of {@link RigConfig.tryResolveConfigFilePath} + * {@inheritdoc IRigConfig.tryResolveConfigFilePathAsync} */ public async tryResolveConfigFilePathAsync(configFileRelativePath: string): Promise { if (!Helpers.isDownwardRelative(configFileRelativePath)) { @@ -445,41 +487,61 @@ export class RigConfig { } return undefined; } +} - private static _validateSchema(json: IRigConfigJson): void { - for (const key of Object.getOwnPropertyNames(json)) { - switch (key) { - case '$schema': - case 'rigPackageName': - case 'rigProfile': - break; - default: - throw new Error(`Unsupported field ${JSON.stringify(key)}`); - } - } - if (!json.rigPackageName) { - throw new Error('Missing required field "rigPackageName"'); - } +function _handleConfigError( + error: NodeJS.ErrnoException, + projectFolderPath: string, + rigConfigFilePath: string +): IRigConfigOptions { + if (error.code !== 'ENOENT' && error.code !== 'ENOTDIR') { + throw new Error(error.message + '\nError loading config file: ' + rigConfigFilePath); + } - if (!RigConfig._packageNameRegExp.test(json.rigPackageName)) { - throw new Error( - `The "rigPackageName" value is not a valid NPM package name: ${JSON.stringify(json.rigPackageName)}` - ); + // File not found, i.e. no rig config + return { + projectFolderPath, + + rigFound: false, + filePath: '', + rigPackageName: '', + rigProfile: '' + }; +} + +function _validateSchema(json: IRigConfigJson): void { + for (const key of Object.getOwnPropertyNames(json)) { + switch (key) { + case '$schema': + case 'rigPackageName': + case 'rigProfile': + break; + default: + throw new Error(`Unsupported field ${JSON.stringify(key)}`); } + } + if (!json.rigPackageName) { + throw new Error('Missing required field "rigPackageName"'); + } - if (!RigConfig._rigNameRegExp.test(json.rigPackageName)) { + if (!_packageNameRegExp.test(json.rigPackageName)) { + throw new Error( + `The "rigPackageName" value is not a valid NPM package name: ${JSON.stringify(json.rigPackageName)}` + ); + } + + if (!_rigNameRegExp.test(json.rigPackageName)) { + throw new Error( + `The "rigPackageName" value is missing the "-rig" suffix: ` + JSON.stringify(json.rigProfile) + ); + } + + if (json.rigProfile !== undefined) { + if (!_profileNameRegExp.test(json.rigProfile)) { throw new Error( - `The "rigPackageName" value is missing the "-rig" suffix: ` + JSON.stringify(json.rigProfile) + `The profile name must consist of lowercase alphanumeric words separated by hyphens: ` + + JSON.stringify(json.rigProfile) ); } - - if (json.rigProfile !== undefined) { - if (!RigConfig._profileNameRegExp.test(json.rigProfile)) { - throw new Error( - `The profile name must consist of lowercase alphanumeric words separated by hyphens: ` + - JSON.stringify(json.rigProfile) - ); - } - } } } diff --git a/libraries/rig-package/src/index.ts b/libraries/rig-package/src/index.ts index fdb64bef0e1..aea84ab6857 100644 --- a/libraries/rig-package/src/index.ts +++ b/libraries/rig-package/src/index.ts @@ -12,4 +12,9 @@ * @packageDocumentation */ -export { IRigConfigJson, RigConfig, ILoadForProjectFolderOptions } from './RigConfig'; +export { + type IRigConfigJson, + type IRigConfig, + RigConfig, + type ILoadForProjectFolderOptions +} from './RigConfig'; diff --git a/libraries/rig-package/src/test/RigConfig.test.ts b/libraries/rig-package/src/test/RigConfig.test.ts index 1e4b3727be7..0e1edd227f1 100644 --- a/libraries/rig-package/src/test/RigConfig.test.ts +++ b/libraries/rig-package/src/test/RigConfig.test.ts @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as fs from 'fs'; -import Ajv from 'ajv'; -import stripJsonComments from 'strip-json-comments'; +import Ajv, { type ValidateFunction } from 'ajv'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import * as jju from 'jju'; import { RigConfig } from '../RigConfig'; @@ -131,7 +131,7 @@ describe(RigConfig.name, () => { expect(rigConfig.rigFound).toBe(true); - expect(() => rigConfig.getResolvedProfileFolder()).toThrowError( + expect(() => rigConfig.getResolvedProfileFolder()).toThrow( 'The rig profile "missing-profile" is not defined by the rig package "example-rig"' ); }); @@ -145,7 +145,7 @@ describe(RigConfig.name, () => { } }); - await expect(rigConfig.getResolvedProfileFolderAsync()).rejects.toThrowError( + await expect(rigConfig.getResolvedProfileFolderAsync()).rejects.toThrow( 'The rig profile "missing-profile" is not defined by the rig package "example-rig"' ); }); @@ -177,9 +177,8 @@ describe(RigConfig.name, () => { expect(rigConfig.rigFound).toBe(true); - const resolvedPath: string | undefined = await rigConfig.tryResolveConfigFilePathAsync( - 'example-config.json' - ); + const resolvedPath: string | undefined = + await rigConfig.tryResolveConfigFilePathAsync('example-config.json'); expect(resolvedPath).toBeDefined(); expectEqualPaths( @@ -192,8 +191,7 @@ describe(RigConfig.name, () => { const rigConfigFilePath: string = path.join(testProjectFolder, 'config', 'rig.json'); const ajv = new Ajv({ - verbose: true, - strictKeywords: true + verbose: true }); // Delete our older "draft-04/schema" and use AJV's built-in schema @@ -201,11 +199,11 @@ describe(RigConfig.name, () => { delete (RigConfig.jsonSchemaObject as any)['$schema']; // Compile our schema - const validateRigFile: Ajv.ValidateFunction = ajv.compile(RigConfig.jsonSchemaObject); + const validateRigFile: ValidateFunction = ajv.compile(RigConfig.jsonSchemaObject); // Load the rig.json file const rigConfigFileContent: string = fs.readFileSync(rigConfigFilePath).toString(); - const rigConfigJsonObject: unknown = JSON.parse(stripJsonComments(rigConfigFileContent)); + const rigConfigJsonObject: unknown = jju.parse(rigConfigFileContent); // Validate it against our schema const valid: boolean = validateRigFile(rigConfigJsonObject) as boolean; diff --git a/libraries/rig-package/tsconfig.json b/libraries/rig-package/tsconfig.json index fbc2f5c0a6c..1a33d17b873 100644 --- a/libraries/rig-package/tsconfig.json +++ b/libraries/rig-package/tsconfig.json @@ -1,7 +1,3 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", - - "compilerOptions": { - "types": ["heft-jest", "node"] - } + "extends": "./node_modules/decoupled-local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/libraries/rush-lib/.eslintrc.js b/libraries/rush-lib/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/libraries/rush-lib/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/libraries/rush-lib/.gitignore b/libraries/rush-lib/.gitignore new file mode 100644 index 00000000000..4717fcdca27 --- /dev/null +++ b/libraries/rush-lib/.gitignore @@ -0,0 +1 @@ +lib-intermediate-*/ \ No newline at end of file diff --git a/libraries/rush-lib/.npmignore b/libraries/rush-lib/.npmignore index e42f0c370b6..b01dc3a04d8 100644 --- a/libraries/rush-lib/.npmignore +++ b/libraries/rush-lib/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,18 +21,27 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- + +# These are generated during build and then used by rush-sdk. They are not useful +# to external consumers. +*.exports.json -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# Exclude intermediate build outputs (not shipped) +/lib-intermediate-*/** +/lib-commonjs/**/*.exports.json -# (Add your project-specific overrides here) +# Include assets used by `rush init` !/assets/** -/lib-*/** diff --git a/libraries/rush-lib/assets/rush-init/[dot]gitignore b/libraries/rush-lib/assets/rush-init/[dot]gitignore index 41e850bba07..5c2c2991940 100644 --- a/libraries/rush-lib/assets/rush-init/[dot]gitignore +++ b/libraries/rush-lib/assets/rush-init/[dot]gitignore @@ -3,6 +3,10 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data *.pid @@ -10,35 +14,38 @@ yarn-error.log* *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover -lib-cov +lib-cov/ # Coverage directory used by tools like istanbul -coverage +coverage/ # nyc test coverage -.nyc_output +.nyc_output/ # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt +.grunt/ # Bower dependency directory (https://bower.io/) -bower_components +bower_components/ # node-waf configuration -.lock-wscript +.lock-wscript/ # Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release +build/Release/ # Dependency directories node_modules/ jspm_packages/ +# TypeScript cache +*.tsbuildinfo + # Optional npm cache directory -.npm +.npm/ # Optional eslint cache -.eslintcache +.eslintcache/ # Optional REPL history .node_repl_history @@ -51,9 +58,32 @@ jspm_packages/ # dotenv environment variables file .env +.env.development.local +.env.test.local +.env.production.local +.env.local # next.js build output -.next +.next/ + +# Docusaurus cache and generated files +.docusaurus/ + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# yarn v2 +.yarn/cache/ +.yarn/unplugged/ +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* # OS X temporary files .DS_Store @@ -63,11 +93,31 @@ jspm_packages/ .idea/ *.iml +# Visual Studio Code +.vscode/ +!.vscode/tasks.json +!.vscode/launch.json + # Rush temporary files common/deploy/ common/temp/ common/autoinstallers/*/.npmrc **/.rush/temp/ +*.lock + +# Common toolchain intermediate files +temp/ +lib/ +lib-amd/ +lib-es6/ +lib-esm/ +lib-esnext/ +lib-commonjs/ +lib-shim/ +dist/ +dist-storybook/ +*.tsbuildinfo # Heft temporary files -.heft +.cache/ +.heft/ diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/.pnpmfile.cjs b/libraries/rush-lib/assets/rush-init/common/config/rush/.pnpmfile.cjs index 3c557371b83..000015badc7 100644 --- a/libraries/rush-lib/assets/rush-init/common/config/rush/.pnpmfile.cjs +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/.pnpmfile.cjs @@ -6,7 +6,7 @@ * functionally similar to Yarn's "resolutions".) * * For details, see the PNPM documentation: - * https://pnpm.js.org/docs/en/hooks.html + * https://pnpm.io/pnpmfile#hooks * * IMPORTANT: SINCE THIS FILE CONTAINS EXECUTABLE CODE, MODIFYING IT IS LIKELY TO INVALIDATE * ANY CACHED DEPENDENCY ANALYSIS. After any modification to pnpmfile.js, it's recommended to run diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/[dot]npmrc b/libraries/rush-lib/assets/rush-init/common/config/rush/[dot]npmrc index b902e270ccd..43f783886e9 100644 --- a/libraries/rush-lib/assets/rush-init/common/config/rush/[dot]npmrc +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/[dot]npmrc @@ -4,19 +4,30 @@ # # NOTE: The "rush publish" command uses .npmrc-publish instead. # -# Before invoking the package manager, Rush will copy this file to the folder where installation -# is performed. The copied file will omit any config lines that reference environment variables +# Before invoking the package manager, Rush will generate an .npmrc in the folder where installation +# is performed. This generated file will omit any config lines that reference environment variables # that are undefined in that session; this avoids problems that would otherwise result due to # a missing variable being replaced by an empty string. # +# If "subspacesEnabled" is true in subspaces.json, the generated file will merge settings from +# "common/config/rush/.npmrc" and "common/config/subspaces//.npmrc", with the latter taking +# precedence. +# # * * * SECURITY WARNING * * * # # It is NOT recommended to store authentication tokens in a text file on a lab machine, because -# other unrelated processes may be able to read the file. Also, the file may persist indefinitely, +# other unrelated processes may be able to read that file. Also, the file may persist indefinitely, # for example if the machine loses power. A safer practice is to pass the token via an # environment variable, which can be referenced from .npmrc using ${} expansion. For example: # # //registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} # + +# Explicitly specify the NPM registry that "rush install" and "rush update" will use by default: registry=https://registry.npmjs.org/ + +# Optionally provide an authentication token for the above registry URL (if it is a private registry): +# //registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} + +# Change this to "true" if your registry requires authentication for read-only operations: always-auth=false diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/[dot]npmrc-publish b/libraries/rush-lib/assets/rush-init/common/config/rush/[dot]npmrc-publish index 7ab44c18d65..51acb920edc 100644 --- a/libraries/rush-lib/assets/rush-init/common/config/rush/[dot]npmrc-publish +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/[dot]npmrc-publish @@ -18,3 +18,9 @@ # # //registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} # + +# Explicitly specify the NPM registry that "rush publish" will use by default: +registry=https://registry.npmjs.org/ + +# Provide an authentication token for the above registry URL: +# //registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN} diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/build-cache.json b/libraries/rush-lib/assets/rush-init/common/config/rush/build-cache.json index 4e26cde258a..072e9f7d497 100644 --- a/libraries/rush-lib/assets/rush-init/common/config/rush/build-cache.json +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/build-cache.json @@ -24,14 +24,21 @@ * a [hash] token. * * Other available tokens: - * - [projectName] - * - [projectName:normalize] - * - [phaseName] - * - [phaseName:normalize] - * - [phaseName:trimPrefix] + * - [projectName] Example: "@my-scope/my-project" + * - [projectName:normalize] Example: "my-scope+my-project" + * - [phaseName] Example: "_phase:test/api" + * - [phaseName:normalize] Example: "_phase:test+api" + * - [phaseName:trimPrefix] Example: "test/api" + * - [os] Example: "win32" + * - [arch] Example: "x64" */ // "cacheEntryNamePattern": "[projectName:normalize]-[phaseName:normalize]-[hash]" + /** + * (Optional) Salt to inject during calculation of the cache key. This can be used to invalidate the cache for all projects when the salt changes. + */ + // "cacheHashSalt": "1", + /** * Use this configuration with "cacheProvider"="azure-blob-storage" */ @@ -61,7 +68,17 @@ /** * If set to true, allow writing to the cache. Defaults to false. */ - // "isCacheWriteAllowed": true + // "isCacheWriteAllowed": true, + + /** + * The Entra ID login flow to use. Defaults to 'AdoCodespacesAuth' on GitHub Codespaces, 'InteractiveBrowser' otherwise. + */ + // "loginFlow": "InteractiveBrowser", + + /** + * If set to true, reading the cache requires authentication. Defaults to false. + */ + // "readRequiresAuthentication": true }, /** diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/cobuild.json b/libraries/rush-lib/assets/rush-init/common/config/rush/cobuild.json new file mode 100644 index 00000000000..a47fad18d5e --- /dev/null +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/cobuild.json @@ -0,0 +1,22 @@ +/** + * This configuration file manages Rush's cobuild feature. + * More documentation is available on the Rush website: https://rushjs.io + */ + { + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/cobuild.schema.json", + + /** + * (Required) EXPERIMENTAL - Set this to true to enable the cobuild feature. + * RUSH_COBUILD_CONTEXT_ID should always be specified as an environment variable with an non-empty string, + * otherwise the cobuild feature will be disabled. + */ + "cobuildFeatureEnabled": false, + + /** + * (Required) Choose where cobuild lock will be acquired. + * + * The lock provider is registered by the rush plugins. + * For example, @rushstack/rush-redis-cobuild-plugin registers the "redis" lock provider. + */ + "cobuildLockProvider": "redis" +} diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/command-line.json b/libraries/rush-lib/assets/rush-init/common/config/rush/command-line.json index 8b972ba7734..3760029c00d 100644 --- a/libraries/rush-lib/assets/rush-init/common/config/rush/command-line.json +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/command-line.json @@ -77,6 +77,14 @@ */ "enableParallelism": false, + /** + * Controls whether weighted operations can start when the total weight would exceed the limit + * but is currently below the limit. This setting only applies when "enableParallelism" is true + * and operations have a "weight" property configured in their rush-project.json "operationSettings". + * Choose true (the default) to favor parallelism. Choose false to strictly stay under the limit. + */ + "allowOversubscription": false, + /** * Normally projects will be processed according to their dependency order: a given project will not start * processing the command until all of its dependencies have completed. This restriction doesn't apply for diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/common-versions.json b/libraries/rush-lib/assets/rush-init/common/config/rush/common-versions.json index 7c2719a5fb7..cc276075c76 100644 --- a/libraries/rush-lib/assets/rush-init/common/config/rush/common-versions.json +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/common-versions.json @@ -40,6 +40,21 @@ */ /*[LINE "HYPOTHETICAL"]*/ "implicitlyPreferredVersions": false, + /** + * If you would like the version specifiers for your dependencies to be consistent, then + * uncomment this line. This is effectively similar to running "rush check" before any + * of the following commands: + * + * rush install, rush update, rush link, rush version, rush publish + * + * In some cases you may want this turned on, but need to allow certain packages to use a different + * version. In those cases, you will need to add an entry to the "allowedAlternativeVersions" + * section of the common-versions.json. + * + * In the case that subspaces is enabled, this setting will take effect at a subspace level. + */ + /*[LINE "HYPOTHETICAL"]*/ "ensureConsistentVersions": true, + /** * The "rush check" command can be used to enforce that every project in the repo must specify * the same SemVer range for a given dependency. However, sometimes exceptions are needed. @@ -59,4 +74,4 @@ /*[LINE "HYPOTHETICAL"]*/ "~2.4.0" /*[LINE "HYPOTHETICAL"]*/ ] } -} +} \ No newline at end of file diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/custom-tips.json b/libraries/rush-lib/assets/rush-init/common/config/rush/custom-tips.json new file mode 100644 index 00000000000..aacc7cc42c6 --- /dev/null +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/custom-tips.json @@ -0,0 +1,31 @@ +/** + * This configuration file allows repo maintainers to configure extra details to be + * printed alongside certain Rush messages. More documentation is available on the + * Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/custom-tips.schema.json", + + /** + * Custom tips allow you to annotate Rush's console messages with advice tailored for + * your specific monorepo. + */ + "customTips": [ + /*[BEGIN "DEMO"]*/ + { + /** + * (REQUIRED) An identifier indicating a message that may be printed by Rush. + * If that message is printed, then this custom tip will be shown. + * The list of available tip identifiers can be found on this page: + * https://rushjs.io/pages/maintainer/custom_tips/ + */ + "tipId": "TIP_RUSH_INCONSISTENT_VERSIONS", + + /** + * (REQUIRED) The message text to be displayed for this tip. + */ + "message": "For additional troubleshooting information, refer this wiki article:\n\nhttps://intranet.contoso.com/docs/pnpm-mismatch" + } + /*[END "DEMO"]*/ + ] +} diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/experiments.json b/libraries/rush-lib/assets/rush-init/common/config/rush/experiments.json index de1549153a5..1bead4b1057 100644 --- a/libraries/rush-lib/assets/rush-init/common/config/rush/experiments.json +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/experiments.json @@ -17,6 +17,13 @@ */ /*[LINE "HYPOTHETICAL"]*/ "usePnpmPreferFrozenLockfileForRushUpdate": true, + /** + * By default, 'rush update' runs as a single operation. + * Set this option to true to instead update the lockfile with `--lockfile-only`, then perform a `--frozen-lockfile` install. + * Necessary when using the `afterAllResolved` hook in .pnpmfile.cjs. + */ + /*[LINE "HYPOTHETICAL"]*/ "usePnpmLockfileOnlyThenFrozenLockfileForRushUpdate": true, + /** * If using the 'preventManualShrinkwrapChanges' option, restricts the hash to only include the layout of external dependencies. * Used to allow links between workspace projects or the addition/removal of references to existing dependency versions to not @@ -37,10 +44,10 @@ /*[LINE "HYPOTHETICAL"]*/ "buildCacheWithAllowWarningsInSuccessfulBuild": true, /** - * If true, the phased commands feature is enabled. To use this feature, create a "phased" command - * in common/config/rush/command-line.json. + * If true, build skipping will respect the allowWarningsInSuccessfulBuild flag and skip builds with warnings. + * This will not replay warnings from the skipped build. */ - /*[LINE "HYPOTHETICAL"]*/ "phasedCommands": true, + /*[LINE "HYPOTHETICAL"]*/ "buildSkipWithAllowWarningsInSuccessfulBuild": true, /** * If true, perform a clean install after when running `rush install` or `rush update` if the @@ -56,5 +63,83 @@ /** * If true, Rush will not allow node_modules in the repo folder or in parent folders. */ - /*[LINE "HYPOTHETICAL"]*/ "forbidPhantomResolvableNodeModulesFolders": true + /*[LINE "HYPOTHETICAL"]*/ "forbidPhantomResolvableNodeModulesFolders": true, + + /** + * (UNDER DEVELOPMENT) For certain installation problems involving peer dependencies, PNPM cannot + * correctly satisfy versioning requirements without installing duplicate copies of a package inside the + * node_modules folder. This poses a problem for "workspace:*" dependencies, as they are normally + * installed by making a symlink to the local project source folder. PNPM's "injected dependencies" + * feature provides a model for copying the local project folder into node_modules, however copying + * must occur AFTER the dependency project is built and BEFORE the consuming project starts to build. + * The "pnpm-sync" tool manages this operation; see its documentation for details. + * Enable this experiment if you want "rush" and "rushx" commands to resync injected dependencies + * by invoking "pnpm-sync" during the build. + */ + /*[LINE "HYPOTHETICAL"]*/ "usePnpmSyncForInjectedDependencies": true, + + /** + * If set to true, Rush will generate a `project-impact-graph.yaml` file in the repository root during `rush update`. + */ + /*[LINE "HYPOTHETICAL"]*/ "generateProjectImpactGraphDuringRushUpdate": true, + + /** + * If true, when running in watch mode, Rush will check for phase scripts named `_phase::ipc` and run them instead + * of `_phase:` if they exist. The created child process will be provided with an IPC channel and expected to persist + * across invocations. + */ + /*[LINE "HYPOTHETICAL"]*/ "useIPCScriptsInWatchMode": true, + + /** + * (UNDER DEVELOPMENT) The Rush alerts feature provides a way to send announcements to engineers + * working in the monorepo, by printing directly in the user's shell window when they invoke Rush commands. + * This ensures that important notices will be seen by anyone doing active development, since people often + * ignore normal discussion group messages or don't know to subscribe. + */ + /*[LINE "HYPOTHETICAL"]*/ "rushAlerts": true, + + + /** + * When using cobuilds, this experiment allows uncacheable operations to benefit from cobuild orchestration without using the build cache. + */ + /*[LINE "HYPOTHETICAL"]*/ "allowCobuildWithoutCache": true, + + /** + * By default, rush perform a full scan of the entire repository. For example, Rush runs `git status` to check for local file changes. + * When this toggle is enabled, Rush will only scan specific paths, significantly speeding up Git operations. + */ + /*[LINE "HYPOTHETICAL"]*/ "enableSubpathScan": true, + + /** + * Rush has a policy that normally requires Rush projects to specify `workspace:*` in package.json when depending + * on other projects in the workspace, unless they are explicitly declared as `decoupledLocalDependencies` + * in rush.json. Enabling this experiment will remove that requirement for dependencies belonging to a different + * subspace. This is useful for large product groups who work in separate subspaces and generally prefer to consume + * each other's packages via the NPM registry. + */ + /*[LINE "HYPOTHETICAL"]*/ "exemptDecoupledDependenciesBetweenSubspaces": false, + + /** + * If true, when running on macOS, Rush will omit AppleDouble files (._*) from build cache archives + * when a companion file exists in the same directory. AppleDouble files are automatically created by + * macOS to store extended attributes on filesystems that don't support them, and should generally not + * be included in the shared build cache. + */ + /*[LINE "HYPOTHETICAL"]*/ "omitAppleDoubleFilesFromBuildCache": true, + + /** + * If true, "rush change --verify" will report errors if change files reference projects that do not + * exist in the Rush configuration, or if change files target a project that belongs to a lockstepped + * version policy but is not the policy's main project. + */ + /*[LINE "HYPOTHETICAL"]*/ "strictChangefileValidation": true, + + /** + * If true, the build cache will use file-based APIs to transfer cache entries to and from cloud storage. + * This avoids loading the entire cache entry into memory, which can prevent out-of-memory errors for large + * build outputs and allow cache entries to exceed the limit of a single Buffer. The cloud cache provider plugin + * must implement the optional file-based methods for this to take effect; otherwise it falls back to the + * buffer-based approach. + */ + /*[LINE "HYPOTHETICAL"]*/ "useDirectFileTransfersForBuildCache": true } diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json b/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json index 924e4ed6bb9..9d5f764bc67 100644 --- a/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json @@ -1,6 +1,11 @@ /** * This configuration file provides settings specific to the PNPM package manager. * More documentation is available on the Rush website: https://rushjs.io + * + * Rush normally looks for this file in `common/config/rush/pnpm-config.json`. However, + * if `subspacesEnabled` is true in subspaces.json, then Rush will instead first look + * for `common/config/subspaces//pnpm-config.json`. (If the file exists in both places, + * then the file under `common/config/rush` is ignored.) */ { "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/pnpm-config.schema.json", @@ -19,6 +24,133 @@ */ "useWorkspaces": true, + /** + * This setting determines how PNPM chooses version numbers during `rush update`. + * For example, suppose `lib-x@3.0.0` depends on `"lib-y": "^1.2.3"` whose latest major + * releases are `1.8.9` and `2.3.4`. The resolution mode `lowest-direct` might choose + * `lib-y@1.2.3`, wheres `highest` will choose 1.8.9, and `time-based` will pick the + * highest compatible version at the time when `lib-x@3.0.0` itself was published (ensuring + * that the version could have been tested by the maintainer of "lib-x"). For local workspace + * projects, `time-based` instead works like `lowest-direct`, avoiding upgrades unless + * they are explicitly requested. Although `time-based` is the most robust option, it may be + * slightly slower with registries such as npmjs.com that have not implemented an optimization. + * + * IMPORTANT: Be aware that PNPM 8.0.0 initially defaulted to `lowest-direct` instead of + * `highest`, but PNPM reverted this decision in 8.6.12 because it caused confusion for users. + * Rush version 5.106.0 and newer avoids this confusion by consistently defaulting to + * `highest` when `resolutionMode` is not explicitly set in pnpm-config.json or .npmrc, + * regardless of your PNPM version. + * + * PNPM documentation: https://pnpm.io/npmrc#resolution-mode + * + * Possible values are: `highest`, `time-based`, and `lowest-direct`. + * The default is `highest`. + */ + /*[LINE "DEMO"]*/ "resolutionMode": "time-based", + + /** + * This setting determines whether PNPM will automatically install (non-optional) + * missing peer dependencies instead of reporting an error. Doing so conveniently + * avoids the need to specify peer versions in package.json, but in a large monorepo + * this often creates worse problems. The reason is that peer dependency behavior + * is inherently complicated, and it is easier to troubleshoot consequences of an explicit + * version than an invisible heuristic. The original NPM RFC discussion pointed out + * some other problems with this feature: https://github.com/npm/rfcs/pull/43 + + * IMPORTANT: Without Rush, the setting defaults to true for PNPM 8 and newer; however, + * as of Rush version 5.109.0 the default is always false unless `autoInstallPeers` + * is specified in pnpm-config.json or .npmrc, regardless of your PNPM version. + + * PNPM documentation: https://pnpm.io/npmrc#auto-install-peers + + * The default value is false. + */ + /*[LINE "DEMO"]*/ "autoInstallPeers": false, + + /** + * The minimum number of minutes that must pass after a version is published before pnpm will install it. + * This setting helps reduce the risk of installing compromised packages, as malicious releases are typically + * discovered and removed within a short time frame. + * + * For example, the following setting ensures that only packages released at least one day ago can be installed: + * + * "minimumReleaseAgeMinutes": 1440 + * + * (SUPPORTED ONLY IN PNPM 10.16.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#minimumreleaseage + * + * The default value is 0 (disabled). + */ + /*[LINE "HYPOTHETICAL"]*/ "minimumReleaseAgeMinutes": 1440, + + /** + * An array of package names or patterns to exclude from the minimumReleaseAgeMinutes check. + * This allows certain trusted packages to be installed immediately after publication. + * Patterns are supported using glob syntax (e.g., "@myorg/*" to exclude all packages from an organization). + * + * For example: + * + * "minimumReleaseAgeExclude": ["webpack", "react", "@myorg/*"] + * + * (SUPPORTED ONLY IN PNPM 10.16.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#minimumreleaseageexclude + */ + /*[LINE "HYPOTHETICAL"]*/ "minimumReleaseAgeExclude": ["@myorg/*"], + + /** + * The trust policy controls whether pnpm should block installation of package versions where + * the trust level has decreased (e.g., a package previously published with provenance is now + * published without it). Setting this to `"no-downgrade"` enables the protection. + * + * (SUPPORTED ONLY IN PNPM 10.21.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#trustpolicy + * + * Possible values are: `off` and `no-downgrade`. + * The default is `off`. + */ + /*[LINE "HYPOTHETICAL"]*/ "trustPolicy": "no-downgrade", + + /** + * An array of package names or patterns to exclude from the trust policy check. + * These packages will be allowed to install even if their trust level has decreased. + * Patterns are supported using glob syntax (e.g., "@myorg/*" to exclude all packages + * from an organization). + * + * For example: + * + * "trustPolicyExclude": ["@babel/core@7.28.5", "chokidar@4.0.3", "@myorg/*"] + * + * (SUPPORTED ONLY IN PNPM 10.22.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#trustpolicyexclude + * + * The default value is []. + */ + /*[LINE "HYPOTHETICAL"]*/ "trustPolicyExclude": ["@myorg/*"], + + /** + * The number of minutes after which pnpm will ignore trust level downgrades. Packages + * published longer ago than this threshold will not be blocked even if their trust level + * has decreased. This is useful when enabling strict trust policies, as it allows older versions + * of packages (which may lack a process for publishing with signatures or provenance) to be + * installed without manual exclusion, assuming they are safe due to their age. + * + * For example, the following setting ignores trust level changes for packages published + * more than 14 days ago: + * + * "trustPolicyIgnoreAfterMinutes": 20160 + * + * (SUPPORTED ONLY IN PNPM 10.27.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#trustpolicyignoreafter + * + * The default value is undefined (no exclusion). + */ + /*[LINE "HYPOTHETICAL"]*/ "trustPolicyIgnoreAfterMinutes": 20160, + /** * If true, then Rush will add the `--strict-peer-dependencies` command-line parameter when * invoking PNPM. This causes `rush update` to fail if there are unsatisfied peer dependencies, @@ -78,6 +210,71 @@ */ /*[LINE "HYPOTHETICAL"]*/ "preventManualShrinkwrapChanges": true, + /** + * When a project uses `workspace:` to depend on another Rush project, PNPM normally installs + * it by creating a symlink under `node_modules`. This generally works well, but in certain + * cases such as differing `peerDependencies` versions, symlinking may cause trouble + * such as incorrectly satisfied versions. For such cases, the dependency can be declared + * as "injected", causing PNPM to copy its built output into `node_modules` like a real + * install from a registry. Details here: https://rushjs.io/pages/advanced/injected_deps/ + * + * When using Rush subspaces, these sorts of versioning problems are much more likely if + * `workspace:` refers to a project from a different subspace. This is because the symlink + * would point to a separate `node_modules` tree installed by a different PNPM lockfile. + * A comprehensive solution is to enable `alwaysInjectDependenciesFromOtherSubspaces`, + * which automatically treats all projects from other subspaces as injected dependencies + * without having to manually configure them. + * + * NOTE: Use carefully -- excessive file copying can slow down the `rush install` and + * `pnpm-sync` operations if too many dependencies become injected. + * + * The default value is false. + */ + /*[LINE "HYPOTHETICAL"]*/ "alwaysInjectDependenciesFromOtherSubspaces": false, + + /** + * Defines the policies to be checked for the `pnpm-lock.yaml` file. + */ + "pnpmLockfilePolicies": { + + /** + * This policy will cause "rush update" to report an error if `pnpm-lock.yaml` contains + * any SHA1 integrity hashes. + * + * For each NPM dependency, `pnpm-lock.yaml` normally stores an `integrity` hash. Although + * its main purpose is to detect corrupted or truncated network requests, this hash can also + * serve as a security fingerprint to protect against attacks that would substitute a + * malicious tarball, for example if a misconfigured .npmrc caused a machine to accidentally + * download a matching package name+version from npmjs.com instead of the private NPM registry. + * NPM originally used a SHA1 hash; this was insecure because an attacker can too easily craft + * a tarball with a matching fingerprint. For this reason, NPM later deprecated SHA1 and + * instead adopted a cryptographically strong SHA512 hash. Nonetheless, SHA1 hashes can + * occasionally reappear during "rush update", for example due to missing metadata fallbacks + * (https://github.com/orgs/pnpm/discussions/6194) or an incompletely migrated private registry. + * The `disallowInsecureSha1` policy prevents this, avoiding potential security/compliance alerts. + */ + /*[BEGIN "HYPOTHETICAL"]*/ + "disallowInsecureSha1": { + /** + * Enables the "disallowInsecureSha1" policy. The default value is false. + */ + "enabled": true, + + /** + * In rare cases, a private NPM registry may continue to serve SHA1 hashes for very old + * package versions, perhaps due to a caching issue or database migration glitch. To avoid + * having to disable the "disallowInsecureSha1" policy for the entire monorepo, the problematic + * package versions can be individually ignored. The "exemptPackageVersions" key is the + * package name, and the array value lists exact version numbers to be ignored. + */ + "exemptPackageVersions": { + "example1": ["1.0.0"], + "example2": ["2.0.0", "2.0.1"] + } + } + /*[END "HYPOTHETICAL"]*/ + }, + /** * The "globalOverrides" setting provides a simple mechanism for overriding version selections * for all dependencies of all projects in the monorepo workspace. The settings are copied @@ -161,6 +358,46 @@ /*[LINE "HYPOTHETICAL"]*/ "fsevents" ], + /** + * The `globalOnlyBuiltDependencies` setting specifies which dependencies are permitted to run + * build scripts (`preinstall`, `install`, and `postinstall` lifecycle events). This is the inverse + * of `globalNeverBuiltDependencies`. In PNPM 10.x, build scripts are disabled by default for + * security, so this setting is required to explicitly permit specific packages to run their + * build scripts. The settings are written to the `onlyBuiltDependencies` field of the + * `pnpm-workspace.yaml` file that is generated by Rush during installation. + * + * (SUPPORTED ONLY IN PNPM 10.1.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#onlybuiltdependencies + * + * Example: + * "globalOnlyBuiltDependencies": [ + * "esbuild", + * "playwright", + * "@swc/core" + * ] + */ + /*[BEGIN "HYPOTHETICAL"]*/ + "globalOnlyBuiltDependencies": [ + "esbuild" + ], + /*[END "HYPOTHETICAL"]*/ + + /** + * The `globalIgnoredOptionalDependencies` setting suppresses the installation of optional NPM + * dependencies specified in the list. This is useful when certain optional dependencies are + * not needed in your environment, such as platform-specific packages or dependencies that + * fail during installation but are not critical to your project. + * These settings are copied into the `pnpm.overrides` field of the `common/temp/package.json` + * file that is generated by Rush during installation, instructing PNPM to ignore the specified + * optional dependencies. + * + * PNPM documentation: https://pnpm.io/package_json#pnpmignoredoptionaldependencies + */ + "globalIgnoredOptionalDependencies": [ + /*[LINE "HYPOTHETICAL"]*/ "fsevents" + ], + /** * The `globalAllowedDeprecatedVersions` setting suppresses installation warnings for package * versions that the NPM registry reports as being deprecated. This is useful if the diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/rush-alerts.json b/libraries/rush-lib/assets/rush-init/common/config/rush/rush-alerts.json new file mode 100644 index 00000000000..0118d33ca69 --- /dev/null +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/rush-alerts.json @@ -0,0 +1,115 @@ +/** + * This configuration file manages the Rush alerts feature. + * More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-alerts.schema.json", + + /** + * Settings such as `startTime` and `endTime` will use this timezone. + * If omitted, the default timezone is UTC (`+00:00`). + */ + "timezone": "-08:00", + + /** + * An array of alert messages and conditions for triggering them. + */ + "alerts": [ + /*[BEGIN "DEMO"]*/ + { + /** + * The alertId is used to identify the alert. + */ + "alertId": "node-js", + + /** + * When the alert is displayed, this title will appear at the top of the message box. + * It should be a single line of text, as concise as possible. + */ + "title": "Node.js upgrade soon!", + + /** + * When the alert is displayed, this text appears in the message box. To make the + * JSON file more readable, if the text is longer than one line, you can instead provide + * an array of strings that will be concatenated. Your text may contain newline characters, + * but generally this is unnecessary because word-wrapping is automatically applied. + */ + "message": [ + "This Thursday, we will complete the Node.js version upgrade. Any pipelines that", + " still have not upgraded will be temporarily disabled." + ], + + /** + * (OPTIONAL) To avoid spamming users, the `title` and `message` settings should be kept + * as concise as possible. If you need to provide more detail, use this setting to + * print a hyperlink to a web page with further guidance. + */ + /*[LINE "HYPOTHETICAL"]*/ "detailsUrl": "https://contoso.com/team-wiki/2024-01-01-migration", + + /** + * (OPTIONAL) If `startTime` is specified, then this alert will not be shown prior to + * that time. + * + * Keep in mind that the alert is not guaranteed to be shown at this time, or at all: + * Alerts are only displayed after a Rush command has triggered fetching of the + * latest rush-alerts.json configuration. Also, display of alerts is throttled to + * avoid spamming the user with too many messages. If you need to test your alert, + * set the environment variable `RUSH_ALERTS_DEBUG=1` to disable throttling. + * + * The `startTime` should be specified as `YYYY-MM-DD HH:MM` using 24 hour time format, + * or else `YYYY-MM-DD` in which case the time part will be `00:00` (start of that day). + * The time zone is obtained from the `timezone` setting above. + */ + /*[LINE "HYPOTHETICAL"]*/ "startTime": "2024-01-01 15:00", + + /** + * (OPTIONAL) This alert will not be shown if the current time is later than `endTime`. + * The format is the same as `startTime`. + */ + /*[LINE "HYPOTHETICAL"]*/ "endTime": "2024-01-05", + + /** + * (OPTIONAL) Specifies the maximum frequency at which this alert can be displayed within a defined time period. + * Options are: + * "always" (default) - no limit on display frequency, + * "monthly" - display up to once per month + * "weekly" - display up to once per week + * "daily" - display up to once per day + * "hourly" - display up to once per hour + */ + /*[LINE "HYPOTHETICAL"]*/ "maximumDisplayInterval": "always", + + /** + * (OPTIONAL) Determines the order in which this alert is shown relative to other alerts, based on urgency. + * Options are: + * "high" - displayed first + * "normal" (default) - standard urgency + * "low" - least urgency + */ + /*[LINE "HYPOTHETICAL"]*/ "priority": "normal", + + /** + * (OPTIONAL) The filename of a script that determines whether this alert can be shown, + * found in the "common/config/rush/alert-scripts" folder. The script must define + * a CommonJS export named `canShowAlert` that returns a boolean value, for example: + * + * ``` + * module.exports.canShowAlert = function () { + * // (your logic goes here) + * return true; + * } + * ``` + * + * Rush will invoke this script with the working directory set to the monorepo root folder, + * with no guarantee that `rush install` has been run. To ensure up-to-date alerts, Rush + * may fetch and checkout the "common/config/rush-alerts" folder in an unpredictable temporary + * path. Therefore, your script should avoid importing dependencies from outside its folder, + * generally be kept as simple and reliable and quick as possible. For more complex conditions, + * we suggest to design some other process that prepares a data file or environment variable + * that can be cheaply checked by your condition script. + */ + /*[LINE "HYPOTHETICAL"]*/ "conditionScript": "rush-alert-node-upgrade.js" + } + /*[END "DEMO"]*/ + ] +} diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/subspaces.json b/libraries/rush-lib/assets/rush-init/common/config/rush/subspaces.json new file mode 100644 index 00000000000..d3c3ae8c516 --- /dev/null +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/subspaces.json @@ -0,0 +1,35 @@ +/** + * This configuration file manages the experimental "subspaces" feature for Rush, + * which allows multiple PNPM lockfiles to be used in a single Rush workspace. + * For full documentation, please see https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/subspaces.schema.json", + + /** + * Set this flag to "true" to enable usage of subspaces. + */ + "subspacesEnabled": false, + + /** + * (DEPRECATED) This is a temporary workaround for migrating from an earlier prototype + * of this feature: https://github.com/microsoft/rushstack/pull/3481 + * It allows subspaces with only one project to store their config files in the project folder. + */ + "splitWorkspaceCompatibility": false, + + /** + * When a command such as "rush update" is invoked without the "--subspace" or "--to" + * parameters, Rush will install all subspaces. In a huge monorepo with numerous subspaces, + * this would be extremely slow. Set "preventSelectingAllSubspaces" to true to avoid this + * mistake by always requiring selection parameters for commands such as "rush update". + */ + "preventSelectingAllSubspaces": false, + + /** + * The list of subspace names, which should be lowercase alphanumeric words separated by + * hyphens, for example "my-subspace". The corresponding config files will have paths + * such as "common/config/subspaces/my-subspace/package-lock.yaml". + */ + "subspaceNames": [] +} diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/version-policies.json b/libraries/rush-lib/assets/rush-init/common/config/rush/version-policies.json index 71979944462..fa53012f5b2 100644 --- a/libraries/rush-lib/assets/rush-init/common/config/rush/version-policies.json +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/version-policies.json @@ -41,7 +41,7 @@ * When creating a release branch in Git, this field should be updated according to the * type of release. * - * Valid values are: "prerelease", "release", "minor", "patch", "major" + * Valid values are: "prerelease", "preminor", "minor", "patch", "major" */ "nextBump": "prerelease", diff --git a/libraries/rush-lib/assets/rush-init/rush.json b/libraries/rush-lib/assets/rush-init/rush.json index 12012e5c9f5..a972877f6d0 100644 --- a/libraries/rush-lib/assets/rush-init/rush.json +++ b/libraries/rush-lib/assets/rush-init/rush.json @@ -26,7 +26,7 @@ * Specify one of: "pnpmVersion", "npmVersion", or "yarnVersion". See the Rush documentation * for details about these alternatives. */ - "pnpmVersion": "6.7.1", + "pnpmVersion": "9.15.9", /*[LINE "HYPOTHETICAL"]*/ "npmVersion": "6.14.15", /*[LINE "HYPOTHETICAL"]*/ "yarnVersion": "1.9.4", @@ -42,7 +42,7 @@ * LTS schedule: https://nodejs.org/en/about/releases/ * LTS versions: https://nodejs.org/en/download/releases/ */ - "nodeSupportedVersionRange": ">=14.15.0 <15.0.0 || >=16.13.0 <17.0.0 || >=18.15.0 <19.0.0", + "nodeSupportedVersionRange": ">=24.11.1 <25.0.0", /** * If the version check above fails, Rush will display a message showing the current @@ -66,17 +66,11 @@ /*[LINE "HYPOTHETICAL"]*/ "suppressNodeLtsWarning": false, /** - * If you would like the version specifiers for your dependencies to be consistent, then - * uncomment this line. This is effectively similar to running "rush check" before any - * of the following commands: - * - * rush install, rush update, rush link, rush version, rush publish - * - * In some cases you may want this turned on, but need to allow certain packages to use a different - * version. In those cases, you will need to add an entry to the "allowedAlternativeVersions" - * section of the common-versions.json. + * Rush normally prints a warning if it detects that the current version is not one published to the + * public npmjs.org registry. If you need to block calls to the npm registry, you can use this setting to disable + * Rush's check. */ - /*[LINE "HYPOTHETICAL"]*/ "ensureConsistentVersions": true, + /*[LINE "HYPOTHETICAL"]*/ "suppressRushIsPublicVersionCheck": false, /** * Large monorepos can become intimidating for newcomers if project folder paths don't follow @@ -204,7 +198,7 @@ /*[LINE "DEMO"]*/ "changeLogUpdateCommitMessage": "Update changelogs [skip ci]", /** - * The commit message to use when commiting changefiles during 'rush change --commit' + * The commit message to use when committing changefiles during 'rush change --commit' * * If no commit message is set it will default to 'Rush change' */ @@ -247,26 +241,36 @@ */ "eventHooks": { /** - * The list of shell commands to run before the Rush installation starts + * A list of shell commands to run before "rush install" or "rush update" starts installation */ "preRushInstall": [ /*[LINE "HYPOTHETICAL"]*/ "common/scripts/pre-rush-install.js" ], /** - * The list of shell commands to run after the Rush installation finishes + * A list of shell commands to run after "rush install" or "rush update" finishes installation */ "postRushInstall": [], /** - * The list of shell commands to run before the Rush build command starts + * A list of shell commands to run before "rush build" or "rush rebuild" starts building */ "preRushBuild": [], /** - * The list of shell commands to run after the Rush build command finishes + * A list of shell commands to run after "rush build" or "rush rebuild" finishes building + */ + "postRushBuild": [], + + /** + * A list of shell commands to run before the "rushx" command starts + */ + "preRushx": [], + + /** + * A list of shell commands to run after the "rushx" command finishes */ - "postRushBuild": [] + "postRushx": [] }, /** @@ -286,7 +290,7 @@ * * For more details and instructions, see this article: https://rushjs.io/pages/advanced/installation_variants/ */ - "variants": [ + "variants": [ /*[BEGIN "HYPOTHETICAL"]*/ { /** @@ -320,13 +324,13 @@ /*[LINE "HYPOTHETICAL"]*/ "hotfixChangeEnabled": false, /** - * This is an optional, but recommended, list of allowed tags that can be applied to Rush projects - * using the "tags" setting in this file. This list is useful for preventing mistakes such as misspelling, - * and it also provides a centralized place to document your tags. If "allowedProjectTags" list is - * not specified, then any valid tag is allowed. A tag name must be one or more words - * separated by hyphens or slashes, where a word may contain lowercase ASCII letters, digits, - * ".", and "@" characters. - */ + * This is an optional, but recommended, list of allowed tags that can be applied to Rush projects + * using the "tags" setting in this file. This list is useful for preventing mistakes such as misspelling, + * and it also provides a centralized place to document your tags. If "allowedProjectTags" list is + * not specified, then any valid tag is allowed. A tag name must be one or more words + * separated by hyphens or slashes, where a word may contain lowercase ASCII letters, digits, + * ".", and "@" characters. + */ /*[LINE "HYPOTHETICAL"]*/ "allowedProjectTags": [ "tools", "frontend-team", "1.0.0-release" ], /** @@ -350,6 +354,13 @@ */ "projectFolder": "apps/my-app", + /** + * This field is only used if "subspacesEnabled" is true in subspaces.json. + * It specifies the subspace that this project belongs to. If omitted, then the + * project belongs to the "default" subspace. + */ + /*[LINE "HYPOTHETICAL"]*/ "subspaceName": "my-subspace", + /** * An optional category for usage in the "browser-approved-packages.json" * and "nonbrowser-approved-packages.json" files. The value must be one of the diff --git a/libraries/rush-lib/assets/website-only/rush-project.json b/libraries/rush-lib/assets/website-only/rush-project.json index 819dd322050..1347fb59eea 100644 --- a/libraries/rush-lib/assets/website-only/rush-project.json +++ b/libraries/rush-lib/assets/website-only/rush-project.json @@ -8,6 +8,8 @@ /** * Optionally specifies another JSON config file that this file extends from. This provides a way for standard * settings to be shared across multiple projects. + * + * To delete an inherited setting, set it to `null` in this file. */ // "extends": "my-rig/profiles/default/config/rush-project.json", diff --git a/libraries/rush-lib/config/api-extractor.json b/libraries/rush-lib/config/api-extractor.json index 3386b9a947a..3dbb76c0e6f 100644 --- a/libraries/rush-lib/config/api-extractor.json +++ b/libraries/rush-lib/config/api-extractor.json @@ -1,19 +1,4 @@ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - - "mainEntryPointFilePath": "/lib-commonjs/index.d.ts", - - "apiReport": { - "enabled": true, - "reportFolder": "../../../common/reviews/api" - }, - - "docModel": { - "enabled": true, - "apiJsonFilePath": "../../../common/temp/api/.api.json" - }, - - "dtsRollup": { - "enabled": true - } + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json" } diff --git a/libraries/rush-lib/config/heft.json b/libraries/rush-lib/config/heft.json index 5a4f9b1b905..02a4934f2bf 100644 --- a/libraries/rush-lib/config/heft.json +++ b/libraries/rush-lib/config/heft.json @@ -2,32 +2,53 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", - "extends": "@rushstack/heft-node-rig/profiles/default/config/heft.json", + "extends": "local-node-rig/profiles/default/config/heft.json", // TODO: Add comments "phasesByName": { "build": { + "cleanFiles": [{ "includeGlobs": ["lib-intermediate-commonjs", "lib-intermediate-esm"] }], + "tasksByName": { "copy-mock-flush-telemetry-plugin": { "taskDependencies": ["typescript"], - "taskEvent": { - "eventKind": "copyFiles", + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", "options": { "copyOperations": [ { - "sourcePath": "lib-commonjs/cli/test/rush-mock-flush-telemetry-plugin", + "sourcePath": "lib-intermediate-commonjs/cli/test/rush-mock-flush-telemetry-plugin", "destinationFolders": [ - "lib-commonjs/cli/test/tapFlushTelemetryAndRunBuildActionRepo/common/autoinstallers/plugins/node_modules/rush-mock-flush-telemetry-plugin" + "lib-intermediate-commonjs/cli/test/tapFlushTelemetryAndRunBuildActionRepo/common/autoinstallers/plugins/node_modules/rush-mock-flush-telemetry-plugin" + ], + "fileExtensions": [".json", ".js", ".map"], + "hardlink": true + }, + { + "sourcePath": "lib-intermediate-commonjs/cli/test/rush-mock-clear-operations-plugin", + "destinationFolders": [ + "lib-intermediate-commonjs/cli/test/clearOperationsAndRunBuildActionRepo/common/autoinstallers/plugins/node_modules/rush-mock-clear-operations-plugin" ], "fileExtensions": [".json", ".js", ".map"], "hardlink": true }, { "sourcePath": "src/cli/test", - "destinationFolders": ["lib-commonjs/cli/test"], - "includeGlobs": ["**/*.js"] + "destinationFolders": ["lib-intermediate-commonjs/cli/test"], + "fileExtensions": [".js", ".yaml"] + }, + { + "sourcePath": "src/logic/pnpm/test", + "destinationFolders": ["lib-intermediate-commonjs/logic/pnpm/test"], + "fileExtensions": [".yaml"] + }, + { + "sourcePath": "src/logic/test", + "destinationFolders": ["lib-intermediate-commonjs/logic/test"], + "includeGlobs": ["**/.mergequeueignore"] } ] } @@ -36,19 +57,54 @@ "copy-empty-modules": { "taskDependencies": ["typescript"], - "taskEvent": { - "eventKind": "runScript", + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "run-script-plugin", "options": { "scriptPath": "./scripts/copyEmptyModules.js" } } }, + "copy-legacy-compatibility-index-js": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/legacy-compatibility", + "destinationFolders": ["lib"], + "includeGlobs": ["*"] + } + ] + } + } + }, + "webpack": { "taskDependencies": ["typescript"], "taskPlugin": { "pluginPackage": "@rushstack/heft-webpack5-plugin" } + }, + + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/rush/v5"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } } } } diff --git a/libraries/rush-lib/config/jest.config.json b/libraries/rush-lib/config/jest.config.json index fd094920638..38fb384805a 100644 --- a/libraries/rush-lib/config/jest.config.json +++ b/libraries/rush-lib/config/jest.config.json @@ -1,17 +1,19 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json", + "extends": "local-node-rig/profiles/default/config/jest.config.json", - "roots": ["/lib-commonjs"], + "roots": ["/lib-intermediate-commonjs"], - "testMatch": ["/lib-commonjs/**/*.test.js"], + "testMatch": ["/lib-intermediate-commonjs/**/*.test.js"], "collectCoverageFrom": [ - "lib-commonjs/**/*.js", - "!lib-commonjs/**/*.d.ts", - "!lib-commonjs/**/*.test.js", - "!lib-commonjs/**/test/**", - "!lib-commonjs/**/__tests__/**", - "!lib-commonjs/**/__fixtures__/**", - "!lib-commonjs/**/__mocks__/**" - ] + "lib-intermediate-commonjs/**/*.js", + "!lib-intermediate-commonjs/**/*.d.ts", + "!lib-intermediate-commonjs/**/*.test.js", + "!lib-intermediate-commonjs/**/test/**", + "!lib-intermediate-commonjs/**/__tests__/**", + "!lib-intermediate-commonjs/**/__fixtures__/**", + "!lib-intermediate-commonjs/**/__mocks__/**" + ], + + "globalTeardown": "/lib-intermediate-commonjs/utilities/test/global-teardown.js" } diff --git a/libraries/rush-lib/config/rig.json b/libraries/rush-lib/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/libraries/rush-lib/config/rig.json +++ b/libraries/rush-lib/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/libraries/rush-lib/config/rush-project.json b/libraries/rush-lib/config/rush-project.json new file mode 100644 index 00000000000..4e4a32b3f81 --- /dev/null +++ b/libraries/rush-lib/config/rush-project.json @@ -0,0 +1,10 @@ +{ + "extends": "local-node-rig/profiles/default/config/rush-project.json", + + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["lib-intermediate-commonjs", "lib-intermediate-esm"] + } + ] +} diff --git a/libraries/rush-lib/config/typescript.json b/libraries/rush-lib/config/typescript.json index be5269a1834..403432e06f5 100644 --- a/libraries/rush-lib/config/typescript.json +++ b/libraries/rush-lib/config/typescript.json @@ -1,10 +1,13 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/typescript.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", + "extends": "local-node-rig/profiles/default/config/typescript.json", + + "$additionalModuleKindsToEmit.inheritanceType": "replace", "additionalModuleKindsToEmit": [ { "moduleKind": "esnext", - "outFolderName": "lib-esnext" + "outFolderName": "lib-intermediate-esm" } ] } diff --git a/libraries/rush-lib/eslint.config.js b/libraries/rush-lib/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/libraries/rush-lib/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/rush-lib/package.json b/libraries/rush-lib/package.json index 8355219f7c6..a37b8ba9b73 100644 --- a/libraries/rush-lib/package.json +++ b/libraries/rush-lib/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/rush-lib", - "version": "5.98.0", + "version": "5.178.1", "description": "A library for writing scripts that interact with the Rush tool", "repository": { "type": "git", @@ -12,8 +12,27 @@ }, "engineStrict": true, "homepage": "https://rushjs.io", - "main": "lib/index.js", - "typings": "dist/rush-lib.d.ts", + "main": "./lib-commonjs/index.js", + "types": "./dist/rush-lib.d.ts", + "exports": { + ".": { + "types": "./dist/rush-lib.d.ts", + "require": "./lib-commonjs/index.js" + }, + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, "scripts": { "build": "heft build --clean", "test": "heft test --clean", @@ -23,63 +42,63 @@ "license": "MIT", "dependencies": { "@pnpm/link-bins": "~5.3.7", - "@rushstack/package-extractor": "workspace:*", + "@rushstack/credential-cache": "workspace:*", "@rushstack/heft-config-file": "workspace:*", + "@rushstack/lookup-by-path": "workspace:*", "@rushstack/node-core-library": "workspace:*", + "@rushstack/npm-check-fork": "workspace:*", "@rushstack/package-deps-hash": "workspace:*", + "@rushstack/package-extractor": "workspace:*", "@rushstack/rig-package": "workspace:*", + "@rushstack/rush-pnpm-kit-v10": "workspace:*", + "@rushstack/rush-pnpm-kit-v8": "workspace:*", + "@rushstack/rush-pnpm-kit-v9": "workspace:*", "@rushstack/stream-collator": "workspace:*", "@rushstack/terminal": "workspace:*", "@rushstack/ts-command-line": "workspace:*", - "@types/node-fetch": "2.6.2", "@yarnpkg/lockfile": "~1.0.2", - "builtin-modules": "~3.1.0", - "cli-table": "~0.3.1", - "colors": "~1.2.1", "dependency-path": "~9.2.8", - "figures": "3.0.0", + "dotenv": "~16.4.7", + "fast-glob": "~3.3.1", "git-repo-info": "~2.1.0", - "glob-escape": "~0.0.2", - "glob": "~7.0.5", "https-proxy-agent": "~5.0.0", "ignore": "~5.1.6", - "inquirer": "~7.3.3", - "js-yaml": "~3.13.1", - "lodash": "~4.17.15", - "node-fetch": "2.6.7", - "npm-check": "~6.0.1", + "@inquirer/checkbox": "~5.1.3", + "@inquirer/confirm": "~6.0.11", + "@inquirer/input": "~5.0.11", + "@inquirer/search": "~4.1.7", + "@inquirer/select": "~5.1.3", + "js-yaml": "~4.1.0", "npm-package-arg": "~6.1.0", + "object-hash": "3.0.0", + "pnpm-sync-lib": "0.3.4", "read-package-tree": "~5.1.5", "rxjs": "~6.6.7", - "semver": "~7.3.0", + "semver": "~7.7.4", "ssri": "~8.0.0", "strict-uri-encode": "~2.0.0", "tapable": "2.2.1", - "tar": "~6.1.11", + "tar": "~7.5.6", "true-case-path": "~2.2.1" }, "devDependencies": { - "@pnpm/logger": "4.0.0", - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", + "@pnpm/lockfile.types-900": "npm:@pnpm/lockfile.types@~900.0.0", "@rushstack/heft-webpack5-plugin": "workspace:*", "@rushstack/heft": "workspace:*", + "@rushstack/operation-graph": "workspace:*", "@rushstack/webpack-deep-imports-plugin": "workspace:*", "@rushstack/webpack-preserve-dynamic-require-plugin": "workspace:*", - "@types/cli-table": "0.3.0", - "@types/glob": "7.1.1", - "@types/inquirer": "7.3.1", - "@types/js-yaml": "3.12.1", - "@types/lodash": "4.14.116", - "@types/node": "14.18.36", + "@types/js-yaml": "4.0.9", "@types/npm-package-arg": "6.1.0", + "@types/object-hash": "~3.0.6", "@types/read-package-tree": "5.1.0", - "@types/semver": "7.3.5", + "@types/semver": "7.7.1", "@types/ssri": "~7.1.0", "@types/strict-uri-encode": "2.0.0", - "@types/tar": "6.1.1", - "@types/webpack-env": "1.18.0", - "webpack": "~5.80.0" + "@types/webpack-env": "1.18.8", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*", + "webpack": "~5.105.2" }, "publishOnlyDependencies": { "@rushstack/rush-amazon-s3-build-cache-plugin": "workspace:*", @@ -87,9 +106,13 @@ "@rushstack/rush-http-build-cache-plugin": "workspace:*" }, "sideEffects": [ - "lib-esnext/start-pnpm.js", - "lib-esnext/start.js", - "lib-esnext/startx.js", - "lib-esnext/utilities/SetRushLibPath.js" + "lib-esm/start-pnpm.js", + "lib-esm/start.js", + "lib-esm/startx.js", + "lib-esm/utilities/SetRushLibPath.js", + "lib-commonjs/start-pnpm.js", + "lib-commonjs/start.js", + "lib-commonjs/startx.js", + "lib-commonjs/utilities/SetRushLibPath.js" ] } diff --git a/libraries/rush-lib/scripts/copyEmptyModules.js b/libraries/rush-lib/scripts/copyEmptyModules.js index eed4451a80d..061a03d699e 100644 --- a/libraries/rush-lib/scripts/copyEmptyModules.js +++ b/libraries/rush-lib/scripts/copyEmptyModules.js @@ -1,6 +1,6 @@ -'ues strict'; +'use strict'; -const { FileSystem } = require('@rushstack/node-core-library'); +const { FileSystem, Async, AsyncQueue } = require('@rushstack/node-core-library'); const JS_FILE_EXTENSION = '.js'; const DTS_FILE_EXTENSION = '.d.ts'; @@ -12,7 +12,7 @@ module.exports = { }, heftConfiguration: { buildFolderPath } }) => { - // We're using a Webpack plugin called `@rushstack/webpack-preserve-dynamic-require-plugin` to + // We're using a Webpack plugin called `@rushstack/webpack-deep-imports-plugin` to // examine all of the modules that are imported by the entrypoints (index, and the start* scripts) // to `rush-lib` and generate stub JS files in the `lib` folder that reference the original modules // in the webpack bundle. The plugin also copies the `.d.ts` files for those modules to the `lib` folder. @@ -22,7 +22,7 @@ module.exports = { // creates a problem when a `.d.ts` file references a module that doesn't have runtime code (i.e. - // a `.d.ts` file that only contains types). // - // This script looks through the `lib-esnext` folder for `.js` files that were produced by the TypeScript + // This script looks through the `lib-intermediate-esm` folder for `.js` files that were produced by the TypeScript // compiler from `.ts` files that contain no runtime code and generates stub `.js` files for them in the // `lib` folder and copies the corresponding `.d.ts` files to the `lib`. This ensures that the `.d.ts` // files that end up in the `lib` folder don't have any unresolved imports. This is tested by the @@ -43,39 +43,48 @@ module.exports = { return resultLines.join('\n'); } - const jsInFolderPath = `${buildFolderPath}/lib-esnext`; - const dtsInFolderPath = `${buildFolderPath}/lib-commonjs`; - const outFolderPath = `${buildFolderPath}/lib`; - async function searchAsync(relativeFolderPath) { - const folderItems = await FileSystem.readFolderItemsAsync( - relativeFolderPath ? `${jsInFolderPath}/${relativeFolderPath}` : jsInFolderPath - ); - for (const folderItem of folderItems) { - const itemName = folderItem.name; - const relativeItemPath = relativeFolderPath ? `${relativeFolderPath}/${itemName}` : itemName; + const jsInFolderPath = `${buildFolderPath}/lib-intermediate-esm`; + const dtsInFolderPath = `${buildFolderPath}/lib-dts`; + const outCjsFolderPath = `${buildFolderPath}/lib-commonjs`; + const emptyModuleBuffer = Buffer.from('module.exports = {};', 'utf8'); + const folderPathQueue = new AsyncQueue([undefined]); - if (folderItem.isDirectory()) { - await searchAsync(relativeItemPath); - } else if (folderItem.isFile() && itemName.endsWith(JS_FILE_EXTENSION)) { - const jsInPath = `${jsInFolderPath}/${relativeItemPath}`; - const jsFileText = await FileSystem.readFileAsync(jsInPath); - const strippedJsFileText = stripCommentsFromJsFile(jsFileText); - if (strippedJsFileText === 'export {};') { - await FileSystem.ensureFolderAsync(`${outFolderPath}/${relativeFolderPath}`); - const outJsPath = `${outFolderPath}/${relativeItemPath}`; - terminal.writeVerboseLine(`Writing stub to ${outJsPath}`); - await FileSystem.writeFileAsync(outJsPath, 'module.exports = {};'); + await Async.forEachAsync( + folderPathQueue, + async ([relativeFolderPath, callback]) => { + const folderPath = relativeFolderPath ? `${jsInFolderPath}/${relativeFolderPath}` : jsInFolderPath; + const folderItems = await FileSystem.readFolderItemsAsync(folderPath); + for (const folderItem of folderItems) { + const itemName = folderItem.name; + const relativeItemPath = relativeFolderPath ? `${relativeFolderPath}/${itemName}` : itemName; - const relativeDtsPath = relativeItemPath.slice(0, -JS_FILE_EXTENSION.length) + DTS_FILE_EXTENSION; - const inDtsPath = `${dtsInFolderPath}/${relativeDtsPath}`; - const outDtsPath = `${outFolderPath}/${relativeDtsPath}`; - terminal.writeVerboseLine(`Copying ${inDtsPath} to ${outDtsPath}`); - await FileSystem.copyFileAsync({ sourcePath: inDtsPath, destinationPath: outDtsPath }); + if (folderItem.isDirectory()) { + folderPathQueue.push(relativeItemPath); + } else if (folderItem.isFile() && itemName.endsWith(JS_FILE_EXTENSION)) { + const jsInPath = `${jsInFolderPath}/${relativeItemPath}`; + const jsFileText = await FileSystem.readFileAsync(jsInPath); + const strippedJsFileText = stripCommentsFromJsFile(jsFileText); + if (strippedJsFileText === 'export {};') { + const outJsPath = `${outCjsFolderPath}/${relativeItemPath}`; + terminal.writeVerboseLine(`Writing stub to ${outJsPath}`); + await FileSystem.writeFileAsync(outJsPath, emptyModuleBuffer, { + ensureFolderExists: true + }); + + const relativeDtsPath = + relativeItemPath.slice(0, -JS_FILE_EXTENSION.length) + DTS_FILE_EXTENSION; + const inDtsPath = `${dtsInFolderPath}/${relativeDtsPath}`; + const outDtsPath = `${outCjsFolderPath}/${relativeDtsPath}`; + terminal.writeVerboseLine(`Copying ${inDtsPath} to ${outDtsPath}`); + // We know this is a file, don't need the redundant checks in FileSystem.copyFileAsync + const buffer = await FileSystem.readFileToBufferAsync(inDtsPath); + await FileSystem.writeFileAsync(outDtsPath, buffer, { ensureFolderExists: true }); + } } } - } - } - - await searchAsync(undefined); + callback(); + }, + { concurrency: 10 } + ); } }; diff --git a/libraries/rush-lib/scripts/plugins-prepublish.js b/libraries/rush-lib/scripts/plugins-prepublish.js index 4ab65b85d68..47501d16e68 100644 --- a/libraries/rush-lib/scripts/plugins-prepublish.js +++ b/libraries/rush-lib/scripts/plugins-prepublish.js @@ -7,5 +7,6 @@ const packageJson = JsonFile.load(packageJsonPath); delete packageJson['publishOnlyDependencies']; packageJson.dependencies['@rushstack/rush-amazon-s3-build-cache-plugin'] = packageJson.version; packageJson.dependencies['@rushstack/rush-azure-storage-build-cache-plugin'] = packageJson.version; +packageJson.dependencies['@rushstack/rush-http-build-cache-plugin'] = packageJson.version; JsonFile.save(packageJson, packageJsonPath, { updateExistingFile: true }); diff --git a/libraries/rush-lib/src/__mocks__/child_process.ts b/libraries/rush-lib/src/__mocks__/child_process.ts deleted file mode 100644 index f3ed28847e0..00000000000 --- a/libraries/rush-lib/src/__mocks__/child_process.ts +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -/* eslint-disable */ - -const EventEmitter = require('events'); - -const childProcess: any = jest.genMockFromModule('child_process'); -const childProcessActual = jest.requireActual('child_process'); -childProcess.spawn.mockImplementation(spawn); -childProcess.__setSpawnMockConfig = setSpawnMockConfig; - -let spawnMockConfig = normalizeSpawnMockConfig(); - -/** - * Helper to initialize how the `spawn` mock should behave. - */ -function normalizeSpawnMockConfig(maybeConfig?: any) { - const config = maybeConfig || {}; - return { - emitError: typeof config.emitError !== 'undefined' ? config.emitError : false, - returnCode: typeof config.returnCode !== 'undefined' ? config.returnCode : 0 - }; -} - -/** - * Initialize the `spawn` mock behavior. - * - * Not a pure function. - */ -function setSpawnMockConfig(spawnConfig: any) { - spawnMockConfig = normalizeSpawnMockConfig(spawnConfig); -} - -/** - * Mock of `spawn`. - */ -function spawn(file: string, args: string[], options: {}) { - const cpMock = new childProcess.ChildProcess(); - - // Add working event emitters ourselves since `genMockFromModule` does not add them because they - // are dynamically added by `spawn`. - const cpEmitter = new EventEmitter(); - const cp = Object.assign({}, cpMock, { - stdin: new EventEmitter(), - stdout: new EventEmitter(), - stderr: new EventEmitter(), - on: cpEmitter.on, - emit: cpEmitter.emit - }); - - setTimeout(() => { - cp.stdout.emit('data', `${file} ${args}: Mock task is spawned`); - - if (spawnMockConfig.emitError) { - cp.stderr.emit('data', `${file} ${args}: A mock error occurred in the task`); - } - - cp.emit('close', spawnMockConfig.returnCode); - }, 0); - - return cp; -} - -/** - * Ensure the real spawnSync function is used, otherwise LockFile breaks. - */ -childProcess.spawnSync = childProcessActual.spawnSync; - -module.exports = childProcess; diff --git a/libraries/rush-lib/src/api/ApprovedPackagesConfiguration.ts b/libraries/rush-lib/src/api/ApprovedPackagesConfiguration.ts index 503f0b9c7d7..6bbb3b7b412 100644 --- a/libraries/rush-lib/src/api/ApprovedPackagesConfiguration.ts +++ b/libraries/rush-lib/src/api/ApprovedPackagesConfiguration.ts @@ -1,11 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import { JsonFile, JsonSchema, FileSystem, NewlineKind, InternalError } from '@rushstack/node-core-library'; import { JsonSchemaUrls } from '../logic/JsonSchemaUrls'; import schemaJson from '../schemas/approved-packages.schema.json'; +import { RushConstants } from '../logic/RushConstants'; /** * Part of IApprovedPackagesJson. @@ -48,13 +50,13 @@ export class ApprovedPackagesItem { } } +const _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); + /** * This represents the JSON file specified via the "approvedPackagesFile" option in rush.json. * @public */ export class ApprovedPackagesConfiguration { - private static _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); - public items: ApprovedPackagesItem[] = []; private _itemsByName: Map = new Map(); @@ -112,9 +114,10 @@ export class ApprovedPackagesConfiguration { this.loadFromFile(); if (!approvedPackagesPolicyEnabled) { + // eslint-disable-next-line no-console console.log( `Warning: Ignoring "${path.basename(this._jsonFilename)}" because the` + - ` "approvedPackagesPolicy" setting was not specified in rush.json` + ` "approvedPackagesPolicy" setting was not specified in ${RushConstants.rushJsonFilename}` ); } @@ -127,7 +130,7 @@ export class ApprovedPackagesConfiguration { public loadFromFile(): void { const approvedPackagesJson: IApprovedPackagesJson = JsonFile.loadAndValidate( this._jsonFilename, - ApprovedPackagesConfiguration._jsonSchema + _jsonSchema ); this.clear(); diff --git a/libraries/rush-lib/src/api/ApprovedPackagesPolicy.ts b/libraries/rush-lib/src/api/ApprovedPackagesPolicy.ts index 352f63fd908..32038e636e4 100644 --- a/libraries/rush-lib/src/api/ApprovedPackagesPolicy.ts +++ b/libraries/rush-lib/src/api/ApprovedPackagesPolicy.ts @@ -1,11 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; import { ApprovedPackagesConfiguration } from './ApprovedPackagesConfiguration'; import { RushConstants } from '../logic/RushConstants'; -import { RushConfiguration, IRushConfigurationJson, IApprovedPackagesPolicyJson } from './RushConfiguration'; +import type { + RushConfiguration, + IRushConfigurationJson, + IApprovedPackagesPolicyJson +} from './RushConfiguration'; /** * This is a helper object for RushConfiguration. @@ -72,7 +76,7 @@ export class ApprovedPackagesPolicy { if (this.enabled) { if (!this.reviewCategories.size) { throw new Error( - `The "approvedPackagesPolicy" feature is enabled rush.json, but the reviewCategories` + + `The "approvedPackagesPolicy" feature is enabled ${RushConstants.rushJsonFilename}, but the reviewCategories` + ` list is not configured.` ); } diff --git a/libraries/rush-lib/src/api/BuildCacheConfiguration.ts b/libraries/rush-lib/src/api/BuildCacheConfiguration.ts index ef8eaddc649..f374397c552 100644 --- a/libraries/rush-lib/src/api/BuildCacheConfiguration.ts +++ b/libraries/rush-lib/src/api/BuildCacheConfiguration.ts @@ -1,23 +1,28 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import { createHash } from 'node:crypto'; + import { JsonFile, JsonSchema, FileSystem, - JsonObject, - AlreadyReportedError, - ITerminal + type JsonObject, + AlreadyReportedError } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; -import { RushConfiguration } from './RushConfiguration'; +import type { RushConfiguration } from './RushConfiguration'; import { FileSystemBuildCacheProvider } from '../logic/buildCache/FileSystemBuildCacheProvider'; import { RushConstants } from '../logic/RushConstants'; -import { ICloudBuildCacheProvider } from '../logic/buildCache/ICloudBuildCacheProvider'; +import type { ICloudBuildCacheProvider } from '../logic/buildCache/ICloudBuildCacheProvider'; import { RushUserConfiguration } from './RushUserConfiguration'; -import { EnvironmentConfiguration } from './EnvironmentConfiguration'; -import { CacheEntryId, GetCacheEntryIdFunction } from '../logic/buildCache/CacheEntryId'; +import { EnvironmentConfiguration, EnvironmentVariableNames } from './EnvironmentConfiguration'; +import { + CacheEntryId, + type IGenerateCacheEntryIdOptions, + type GetCacheEntryIdFunction +} from '../logic/buildCache/CacheEntryId'; import type { CloudBuildCacheProviderFactory, RushSession } from '../pluginFramework/RushSession'; import schemaJson from '../schemas/build-cache.schema.json'; @@ -27,7 +32,25 @@ import schemaJson from '../schemas/build-cache.schema.json'; export interface IBaseBuildCacheJson { buildCacheEnabled: boolean; cacheProvider: string; + /** + * Used to specify the cache entry ID format. If this property is set, it must + * contain a `[hash]` token. It may also contain one of the following tokens: + * - `[projectName]` + * - `[projectName:normalize]` + * - `[phaseName]` + * - `[phaseName:normalize]` + * - `[phaseName:trimPrefix]` + * - `[os]` + * - `[arch]` + * @privateRemarks + * NOTE: If you update this comment, make sure to update build-cache.json in the "rush init" template. + * The token parser is in CacheEntryId.ts + */ cacheEntryNamePattern?: string; + /** + * An optional salt to inject during calculation of the cache key. This can be used to invalidate the cache for all projects when the salt changes. + */ + cacheHashSalt?: string; } /** @@ -59,14 +82,14 @@ interface IBuildCacheConfigurationOptions { cloudCacheProvider: ICloudBuildCacheProvider | undefined; } +const BUILD_CACHE_JSON_SCHEMA: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); + /** * Use this class to load and save the "common/config/rush/build-cache.json" config file. * This file provides configuration options for cached project build output. * @beta */ export class BuildCacheConfiguration { - private static _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); - /** * Indicates whether the build cache feature is enabled. * Typically it is enabled in the build-cache.json config file. @@ -88,6 +111,10 @@ export class BuildCacheConfiguration { * The provider for interacting with the cloud build cache, if configured. */ public readonly cloudCacheProvider: ICloudBuildCacheProvider | undefined; + /** + * An optional salt to inject during calculation of the cache key. This can be used to invalidate the cache for all projects when the salt changes. + */ + public readonly cacheHashSalt: string | undefined; private constructor({ getCacheEntryId, @@ -106,6 +133,7 @@ export class BuildCacheConfiguration { rushConfiguration: rushConfiguration }); this.cloudCacheProvider = cloudCacheProvider; + this.cacheHashSalt = buildCacheJson.cacheHashSalt; } /** @@ -117,11 +145,8 @@ export class BuildCacheConfiguration { rushConfiguration: RushConfiguration, rushSession: RushSession ): Promise { - const jsonFilePath: string = BuildCacheConfiguration.getBuildCacheConfigFilePath(rushConfiguration); - if (!FileSystem.exists(jsonFilePath)) { - return undefined; - } - return await BuildCacheConfiguration._loadAsync(jsonFilePath, terminal, rushConfiguration, rushSession); + const { options } = await _tryLoadInternalAsync(terminal, rushConfiguration, rushSession); + return options ? new BuildCacheConfiguration(options) : undefined; } /** @@ -133,8 +158,12 @@ export class BuildCacheConfiguration { rushConfiguration: RushConfiguration, rushSession: RushSession ): Promise { - const jsonFilePath: string = BuildCacheConfiguration.getBuildCacheConfigFilePath(rushConfiguration); - if (!FileSystem.exists(jsonFilePath)) { + const { options, jsonFilePath } = await _tryLoadInternalAsync(terminal, rushConfiguration, rushSession); + const buildCacheConfiguration: BuildCacheConfiguration | undefined = options + ? new BuildCacheConfiguration(options) + : undefined; + + if (!buildCacheConfiguration) { terminal.writeErrorLine( `The build cache feature is not enabled. This config file is missing:\n` + jsonFilePath ); @@ -142,13 +171,6 @@ export class BuildCacheConfiguration { throw new AlreadyReportedError(); } - const buildCacheConfiguration: BuildCacheConfiguration = await BuildCacheConfiguration._loadAsync( - jsonFilePath, - terminal, - rushConfiguration, - rushSession - ); - if (!buildCacheConfiguration.buildCacheEnabled) { terminal.writeErrorLine( `The build cache feature is not enabled. You can enable it by editing this config file:\n` + @@ -156,6 +178,7 @@ export class BuildCacheConfiguration { ); throw new AlreadyReportedError(); } + return buildCacheConfiguration; } @@ -163,49 +186,105 @@ export class BuildCacheConfiguration { * Gets the absolute path to the build-cache.json file in the specified rush workspace. */ public static getBuildCacheConfigFilePath(rushConfiguration: RushConfiguration): string { - return path.resolve(rushConfiguration.commonRushConfigFolder, RushConstants.buildCacheFilename); + return `${rushConfiguration.commonRushConfigFolder}/${RushConstants.buildCacheFilename}`; } +} - private static async _loadAsync( - jsonFilePath: string, - terminal: ITerminal, - rushConfiguration: RushConfiguration, - rushSession: RushSession - ): Promise { - const buildCacheJson: IBuildCacheJson = await JsonFile.loadAndValidateAsync( - jsonFilePath, - BuildCacheConfiguration._jsonSchema - ); - const rushUserConfiguration: RushUserConfiguration = await RushUserConfiguration.initializeAsync(); +async function _tryLoadInternalAsync( + terminal: ITerminal, + rushConfiguration: RushConfiguration, + rushSession: RushSession +): Promise<{ options: IBuildCacheConfigurationOptions | undefined; jsonFilePath: string }> { + const jsonFilePath: string = BuildCacheConfiguration.getBuildCacheConfigFilePath(rushConfiguration); + const options: IBuildCacheConfigurationOptions | undefined = await _tryLoadAsync( + jsonFilePath, + terminal, + rushConfiguration, + rushSession + ); + return { options, jsonFilePath }; +} - let getCacheEntryId: GetCacheEntryIdFunction; - try { - getCacheEntryId = CacheEntryId.parsePattern(buildCacheJson.cacheEntryNamePattern); - } catch (e) { - terminal.writeErrorLine( - `Error parsing cache entry name pattern "${buildCacheJson.cacheEntryNamePattern}": ${e}` +async function _tryLoadAsync( + jsonFilePath: string, + terminal: ITerminal, + rushConfiguration: RushConfiguration, + rushSession: RushSession +): Promise { + let buildCacheJson: IBuildCacheJson; + const buildCacheOverrideJson: string | undefined = EnvironmentConfiguration.buildCacheOverrideJson; + if (buildCacheOverrideJson) { + buildCacheJson = JsonFile.parseString(buildCacheOverrideJson); + BUILD_CACHE_JSON_SCHEMA.validateObject( + buildCacheJson, + `${EnvironmentVariableNames.RUSH_BUILD_CACHE_OVERRIDE_JSON} environment variable` + ); + } else { + const buildCacheOverrideJsonFilePath: string | undefined = + EnvironmentConfiguration.buildCacheOverrideJsonFilePath; + if (buildCacheOverrideJsonFilePath) { + buildCacheJson = await JsonFile.loadAndValidateAsync( + buildCacheOverrideJsonFilePath, + BUILD_CACHE_JSON_SCHEMA ); - throw new AlreadyReportedError(); - } - - let cloudCacheProvider: ICloudBuildCacheProvider | undefined; - // Don't configure a cloud cache provider if local-only - if (buildCacheJson.cacheProvider !== 'local-only') { - const cloudCacheProviderFactory: CloudBuildCacheProviderFactory | undefined = - rushSession.getCloudBuildCacheProviderFactory(buildCacheJson.cacheProvider); - if (!cloudCacheProviderFactory) { - throw new Error(`Unexpected cache provider: ${buildCacheJson.cacheProvider}`); + } else { + try { + buildCacheJson = await JsonFile.loadAndValidateAsync(jsonFilePath, BUILD_CACHE_JSON_SCHEMA); + } catch (e) { + if (!FileSystem.isNotExistError(e)) { + throw e; + } else { + return undefined; + } } - cloudCacheProvider = await cloudCacheProviderFactory(buildCacheJson as ICloudBuildCacheJson); } + } - return new BuildCacheConfiguration({ - buildCacheJson, - getCacheEntryId, - rushConfiguration, - rushUserConfiguration, - rushSession, - cloudCacheProvider + const rushUserConfiguration: RushUserConfiguration = await RushUserConfiguration.initializeAsync(); + let innerGetCacheEntryId: GetCacheEntryIdFunction; + try { + innerGetCacheEntryId = CacheEntryId.parsePattern(buildCacheJson.cacheEntryNamePattern); + } catch (e) { + terminal.writeErrorLine( + `Error parsing cache entry name pattern "${buildCacheJson.cacheEntryNamePattern}": ${e}` + ); + throw new AlreadyReportedError(); + } + + const { cacheHashSalt = '', cacheProvider } = buildCacheJson; + const salt: string = `${RushConstants.buildCacheVersion}${ + cacheHashSalt ? `${RushConstants.hashDelimiter}${cacheHashSalt}` : '' + }`; + // Extend the cache entry id with to salt the hash + // This facilitates forcing cache invalidation either when the build cache version changes (new version of Rush) + // or when the user-side salt changes (need to purge bad cache entries, plugins including additional files) + const getCacheEntryId: GetCacheEntryIdFunction = (options: IGenerateCacheEntryIdOptions): string => { + const saltedHash: string = createHash('sha1').update(salt).update(options.projectStateHash).digest('hex'); + + return innerGetCacheEntryId({ + phaseName: options.phaseName, + projectName: options.projectName, + projectStateHash: saltedHash }); + }; + + let cloudCacheProvider: ICloudBuildCacheProvider | undefined; + // Don't configure a cloud cache provider if local-only + if (cacheProvider !== 'local-only') { + const cloudCacheProviderFactory: CloudBuildCacheProviderFactory | undefined = + rushSession.getCloudBuildCacheProviderFactory(cacheProvider); + if (!cloudCacheProviderFactory) { + throw new Error(`Unexpected cache provider: ${cacheProvider}`); + } + cloudCacheProvider = await cloudCacheProviderFactory(buildCacheJson as ICloudBuildCacheJson); } + + return { + buildCacheJson, + getCacheEntryId, + rushConfiguration, + rushUserConfiguration, + rushSession, + cloudCacheProvider + }; } diff --git a/libraries/rush-lib/src/api/ChangeFile.ts b/libraries/rush-lib/src/api/ChangeFile.ts index 77039fbd21d..1de74ec139a 100644 --- a/libraries/rush-lib/src/api/ChangeFile.ts +++ b/libraries/rush-lib/src/api/ChangeFile.ts @@ -1,14 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; -import gitInfo from 'git-repo-info'; +import type gitInfo from 'git-repo-info'; import { JsonFile } from '@rushstack/node-core-library'; -import { RushConfiguration } from './RushConfiguration'; -import { IChangeFile, IChangeInfo } from './ChangeManagement'; +import type { RushConfiguration } from './RushConfiguration'; +import type { IChangeFile, IChangeInfo } from './ChangeManagement'; import { Git } from '../logic/Git'; /** @@ -80,13 +80,19 @@ export class ChangeFile { const repoInfo: gitInfo.GitRepoInfo | undefined = git.getGitInfo(); branch = repoInfo && repoInfo.branch; if (!branch) { + // eslint-disable-next-line no-console console.log('Could not automatically detect git branch name, using timestamp instead.'); } - // example filename: yourbranchname_2017-05-01-20-20.json + // The timestamp includes seconds so that running "rush change" more than once + // in the same minute produces distinct filenames. Without this, the "--overwrite" + // flag rarely had any effect, and a second invocation would silently clobber the + // change file written by the first one. See GitHub issue #2195. + // example filename: yourbranchname_2017-05-01-20-20-30.json + const timestamp: string | undefined = this._getTimestamp(true); const filename: string = branch - ? this._escapeFilename(`${branch}_${this._getTimestamp()}.json`) - : `${this._getTimestamp()}.json`; + ? this._escapeFilename(`${branch}_${timestamp}.json`) + : `${timestamp}.json`; const filePath: string = path.join( this._rushConfiguration.changesFolder, ...this._changeFileData.packageName.split('/'), @@ -97,7 +103,7 @@ export class ChangeFile { /** * Gets the current time, formatted as YYYY-MM-DD-HH-MM - * Optionally will include seconds + * When useSeconds is true, the seconds are appended as well: YYYY-MM-DD-HH-MM-SS */ private _getTimestamp(useSeconds: boolean = false): string | undefined { // Create a date string with the current time @@ -119,7 +125,7 @@ export class ChangeFile { let formattedTime: string; if (useSeconds) { // formattedTime === "22-47-49" - formattedTime = matches[2].replace(':', '-'); + formattedTime = matches[2].replace(/:/g, '-'); } else { // formattedTime === "22-47" const timeParts: string[] = matches[2].split(':'); diff --git a/libraries/rush-lib/src/api/ChangeManager.ts b/libraries/rush-lib/src/api/ChangeManager.ts index 85e1ab5fa0b..e3e779fd5e1 100644 --- a/libraries/rush-lib/src/api/ChangeManager.ts +++ b/libraries/rush-lib/src/api/ChangeManager.ts @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { RushConfiguration } from './RushConfiguration'; -import { RushConfigurationProject } from './RushConfigurationProject'; +import type { RushConfiguration } from './RushConfiguration'; +import type { RushConfigurationProject } from './RushConfigurationProject'; import { ChangeFile } from './ChangeFile'; -import { IChangeFile } from './ChangeManagement'; +import type { IChangeFile } from './ChangeManagement'; /** * A class that helps with programmatically interacting with Rush's change files. @@ -26,7 +26,6 @@ export class ChangeManager { const projectInfo: RushConfigurationProject | undefined = rushConfiguration.getProjectByName(projectName); if (projectInfo && projectInfo.shouldPublish) { const changefile: IChangeFile = { - // eslint-disable-line @typescript-eslint/no-explicit-any changes: [ { comment: '', diff --git a/libraries/rush-lib/src/api/CobuildConfiguration.ts b/libraries/rush-lib/src/api/CobuildConfiguration.ts new file mode 100644 index 00000000000..e397018ca44 --- /dev/null +++ b/libraries/rush-lib/src/api/CobuildConfiguration.ts @@ -0,0 +1,184 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { randomUUID } from 'node:crypto'; + +import { FileSystem, JsonFile, JsonSchema } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import { EnvironmentConfiguration } from './EnvironmentConfiguration'; +import type { CobuildLockProviderFactory, RushSession } from '../pluginFramework/RushSession'; +import { RushConstants } from '../logic/RushConstants'; +import type { ICobuildLockProvider } from '../logic/cobuild/ICobuildLockProvider'; +import type { RushConfiguration } from './RushConfiguration'; +import schemaJson from '../schemas/cobuild.schema.json'; + +/** + * @beta + */ +export interface ICobuildJson { + cobuildFeatureEnabled: boolean; + cobuildLockProvider: string; +} + +/** + * @beta + */ +export interface ICobuildConfigurationOptions { + cobuildJson: ICobuildJson; + rushConfiguration: RushConfiguration; + rushSession: RushSession; + cobuildLockProviderFactory: CobuildLockProviderFactory; +} + +const _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); + +/** + * Use this class to load and save the "common/config/rush/cobuild.json" config file. + * This file provides configuration options for the Rush Cobuild feature. + * @beta + */ +export class CobuildConfiguration { + /** + * Indicates whether the cobuild feature is enabled. + * Typically it is enabled in the cobuild.json config file. + * + * Note: The orchestrator (or local users) should always have to opt into running with cobuilds by + * providing a cobuild context id. Even if cobuilds are "enabled" as a feature, they don't + * actually turn on for that particular build unless the cobuild context id is provided as an + * non-empty string. + */ + public readonly cobuildFeatureEnabled: boolean; + + /** + * Cobuild context id + * + * @remarks + * The cobuild feature won't be enabled until the context id is provided as an non-empty string. + */ + public readonly cobuildContextId: string | undefined; + + /** + * This is a name of the participating cobuild runner. It can be specified by the environment variable + * RUSH_COBUILD_RUNNER_ID. If it is not provided, a random id will be generated to identify the runner. + */ + public readonly cobuildRunnerId: string; + /** + * If true, Rush will automatically handle the leaf project with build cache "disabled" by writing + * to the cache in a special "log files only mode". This is useful when you want to use Cobuilds + * to improve the performance in CI validations and the leaf projects have not enabled cache. + */ + public readonly cobuildLeafProjectLogOnlyAllowed: boolean; + + /** + * If true, operations can opt into leveraging cobuilds without restoring from the build cache. + * Operations will need to us the allowCobuildWithoutCache flag to opt into this behavior per phase. + */ + public readonly cobuildWithoutCacheAllowed: boolean; + + private _cobuildLockProvider: ICobuildLockProvider | undefined; + private readonly _cobuildLockProviderFactory: CobuildLockProviderFactory; + private readonly _cobuildJson: ICobuildJson; + + private constructor(options: ICobuildConfigurationOptions) { + const { cobuildJson, cobuildLockProviderFactory, rushConfiguration } = options; + + this.cobuildContextId = EnvironmentConfiguration.cobuildContextId; + this.cobuildFeatureEnabled = this.cobuildContextId ? cobuildJson.cobuildFeatureEnabled : false; + this.cobuildRunnerId = EnvironmentConfiguration.cobuildRunnerId || randomUUID(); + this.cobuildLeafProjectLogOnlyAllowed = + EnvironmentConfiguration.cobuildLeafProjectLogOnlyAllowed ?? false; + this.cobuildWithoutCacheAllowed = + rushConfiguration.experimentsConfiguration.configuration.allowCobuildWithoutCache ?? false; + + this._cobuildLockProviderFactory = cobuildLockProviderFactory; + this._cobuildJson = cobuildJson; + } + + /** + * Attempts to load the cobuild.json data from the standard file path `common/config/rush/cobuild.json`. + * If the file has not been created yet, then undefined is returned. + */ + public static async tryLoadAsync( + terminal: ITerminal, + rushConfiguration: RushConfiguration, + rushSession: RushSession + ): Promise { + const jsonFilePath: string = CobuildConfiguration.getCobuildConfigFilePath(rushConfiguration); + try { + const options: ICobuildConfigurationOptions | undefined = await _loadAsync( + jsonFilePath, + terminal, + rushConfiguration, + rushSession + ); + return options ? new CobuildConfiguration(options) : undefined; + } catch (err) { + if (!FileSystem.isNotExistError(err)) { + throw err; + } + } + } + + public static getCobuildConfigFilePath(rushConfiguration: RushConfiguration): string { + return `${rushConfiguration.commonRushConfigFolder}/${RushConstants.cobuildFilename}`; + } + + public async createLockProviderAsync(terminal: ITerminal): Promise { + if (this.cobuildFeatureEnabled) { + terminal.writeLine(`Running cobuild (runner ${this.cobuildContextId}/${this.cobuildRunnerId})`); + const cobuildLockProvider: ICobuildLockProvider = await this._cobuildLockProviderFactory( + this._cobuildJson + ); + this._cobuildLockProvider = cobuildLockProvider; + await this._cobuildLockProvider.connectAsync(); + } + } + + public async destroyLockProviderAsync(): Promise { + if (this.cobuildFeatureEnabled) { + await this._cobuildLockProvider?.disconnectAsync(); + } + } + + public getCobuildLockProvider(): ICobuildLockProvider { + if (!this._cobuildLockProvider) { + throw new Error(`Cobuild lock provider has not been created`); + } + return this._cobuildLockProvider; + } +} + +async function _loadAsync( + jsonFilePath: string, + terminal: ITerminal, + rushConfiguration: RushConfiguration, + rushSession: RushSession +): Promise { + let cobuildJson: ICobuildJson | undefined; + try { + cobuildJson = await JsonFile.loadAndValidateAsync(jsonFilePath, _jsonSchema); + } catch (e) { + if (FileSystem.isNotExistError(e)) { + return undefined; + } + throw e; + } + + if (!cobuildJson?.cobuildFeatureEnabled) { + return undefined; + } + + const cobuildLockProviderFactory: CobuildLockProviderFactory | undefined = + rushSession.getCobuildLockProviderFactory(cobuildJson.cobuildLockProvider); + if (!cobuildLockProviderFactory) { + throw new Error(`Unexpected cobuild lock provider: ${cobuildJson.cobuildLockProvider}`); + } + + return { + cobuildJson, + rushConfiguration, + rushSession, + cobuildLockProviderFactory + }; +} diff --git a/libraries/rush-lib/src/api/CommandLineConfiguration.ts b/libraries/rush-lib/src/api/CommandLineConfiguration.ts index c49de0384d5..53b5678f4ff 100644 --- a/libraries/rush-lib/src/api/CommandLineConfiguration.ts +++ b/libraries/rush-lib/src/api/CommandLineConfiguration.ts @@ -119,6 +119,13 @@ export interface IPhasedCommandConfig extends IPhasedCommandWithoutPhasesJson, I * How many milliseconds to wait after receiving a file system notification before executing in watch mode. */ watchDebounceMs?: number; + /** + * If true, when running this command in watch mode the operation graph will include every project + * in the repository (respecting phase selection), but only the projects selected by the user's + * CLI project selection parameters will be initially enabled. Other projects will remain disabled + * unless they become required or are explicitly selected in a subsequent pass. + */ + includeAllProjectsInWatchGraph?: boolean; /** * If set to `true`, then this phased command will always perform an install before executing, regardless of CLI flags. * If set to `false`, then Rush will define a built-in "--install" CLI flag for this command. @@ -127,7 +134,14 @@ export interface IPhasedCommandConfig extends IPhasedCommandWithoutPhasesJson, I alwaysInstall: boolean | undefined; } -export interface IGlobalCommandConfig extends IGlobalCommandJson, ICommandWithParameters {} +export interface IGlobalCommandConfig extends IGlobalCommandJson, ICommandWithParameters { + /** + * If true, this command was declared with commandKind "globalPlugin" and its implementation + * is provided by a Rush plugin via the `runGlobalCustomCommand` hook. There is no shell + * command to execute. + */ + providedByPlugin: boolean; +} export type Command = IGlobalCommandConfig | IPhasedCommandConfig; @@ -169,7 +183,7 @@ const DEFAULT_REBUILD_COMMAND_JSON: IBulkCommandJson = { description: 'This command assumes that the package.json file for each project contains' + ' a "scripts" entry for "npm run build" that performs a full clean build.' + - ' Rush invokes this script to build each project that is registered in rush.json.' + + ` Rush invokes this script to build each project that is registered in ${RushConstants.rushJsonFilename}.` + ' Projects are built in parallel where possible, but always respecting the dependency' + ' graph for locally linked projects. The number of simultaneous processes will be' + ' based on the number of machine cores unless overridden by the --parallelism flag.' + @@ -186,12 +200,22 @@ interface ICommandLineConfigurationOptions { doNotIncludeDefaultBuildCommands?: boolean; } +/** + * This function replaces colons (":") with underscores ("_"). + * + * ts-command-line restricts command names to lowercase letters, numbers, underscores, and colons. + * Replacing colons with underscores produces a filesystem-safe name. + */ +function _normalizeNameForLogFilenameIdentifiers(name: string): string { + return name.replace(/:/g, '_'); // Replace colons with underscores to be filesystem-safe +} + +const _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); + /** * Custom Commands and Options for the Rush Command Line */ export class CommandLineConfiguration { - private static _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); - public readonly commands: Map = new Map(); public readonly phases: Map = new Map(); public readonly parameters: IParameterJson[] = []; @@ -256,7 +280,7 @@ export class CommandLineConfiguration { const processedPhase: IPhase = { name: phase.name, isSynthetic: false, - logFilenameIdentifier: this._normalizeNameForLogFilenameIdentifiers(phase.name), + logFilenameIdentifier: _normalizeNameForLogFilenameIdentifiers(phase.name), associatedParameters: new Set(), dependencies: { self: new Set(), @@ -373,6 +397,8 @@ export class CommandLineConfiguration { if (watchOptions) { normalizedCommand.alwaysWatch = watchOptions.alwaysWatch; normalizedCommand.watchDebounceMs = watchOptions.debounceMs; + normalizedCommand.includeAllProjectsInWatchGraph = + !!watchOptions.includeAllProjectsInWatchGraph; // No implicit phase dependency expansion for watch mode. for (const phaseName of watchOptions.watchPhases) { @@ -398,6 +424,20 @@ export class CommandLineConfiguration { case RushConstants.globalCommandKind: { normalizedCommand = { ...command, + providedByPlugin: false, + associatedParameters: new Set() + }; + break; + } + + case RushConstants.globalPluginCommandKind: { + // Normalize globalPlugin commands to global commands with an empty shellCommand, + // similar to how bulk commands are converted to phased commands. + normalizedCommand = { + ...command, + commandKind: RushConstants.globalCommandKind, + shellCommand: '', + providedByPlugin: true, associatedParameters: new Set() }; break; @@ -446,27 +486,28 @@ export class CommandLineConfiguration { buildCommandOriginalPhases = buildCommand.originalPhases; this.commands.set(buildCommand.name, buildCommand); } + } - if (!this.commands.has(RushConstants.rebuildCommandName)) { - // If a rebuild command was not specified in the config file, add the default rebuild command - if (!buildCommandPhases || !buildCommandOriginalPhases) { - throw new Error(`Phases for the "${RushConstants.buildCommandName}" were not found.`); - } - - const rebuildCommand: IPhasedCommandConfig = { - ...DEFAULT_REBUILD_COMMAND_JSON, - commandKind: RushConstants.phasedCommandKind, - isSynthetic: true, - phases: buildCommandPhases, - disableBuildCache: DEFAULT_REBUILD_COMMAND_JSON.disableBuildCache, - associatedParameters: buildCommand.associatedParameters, // rebuild should share build's parameters in this case, - originalPhases: buildCommandOriginalPhases, - watchPhases: new Set(), - alwaysWatch: false, - alwaysInstall: undefined - }; - this.commands.set(rebuildCommand.name, rebuildCommand); + const buildCommand: Command | undefined = this.commands.get(RushConstants.buildCommandName); + if (buildCommand && !this.commands.has(RushConstants.rebuildCommandName)) { + // If a rebuild command was not specified in the config file, add the default rebuild command + if (!buildCommandPhases || !buildCommandOriginalPhases) { + throw new Error(`Phases for the "${RushConstants.buildCommandName}" were not found.`); } + + const rebuildCommand: IPhasedCommandConfig = { + ...DEFAULT_REBUILD_COMMAND_JSON, + commandKind: RushConstants.phasedCommandKind, + isSynthetic: true, + phases: buildCommandPhases, + disableBuildCache: DEFAULT_REBUILD_COMMAND_JSON.disableBuildCache, + associatedParameters: buildCommand.associatedParameters, // rebuild should share build's parameters in this case, + originalPhases: buildCommandOriginalPhases, + watchPhases: new Set(), + alwaysWatch: false, + alwaysInstall: undefined + }; + this.commands.set(rebuildCommand.name, rebuildCommand); } const parametersJson: ICommandLineJson['parameters'] = commandLineJson?.parameters; @@ -515,8 +556,8 @@ export class CommandLineConfiguration { if (!associatedCommand) { throw new Error( `${RushConstants.commandLineFilename} defines a parameter "${normalizedParameter.longName}" ` + - `that is associated with a command "${associatedCommandName}" that does not exist or does ` + - 'not support custom parameters.' + `that is associated with a command "${associatedCommandName}" that is not defined in ` + + 'this file.' ); } else { associatedCommand.associatedParameters.add(normalizedParameter); @@ -573,7 +614,7 @@ export class CommandLineConfiguration { `In ${RushConstants.commandLineFilename}, there exists a cycle within the ` + `set of ${dependency.name} dependencies: ${Array.from( phasesInPath, - (phase: IPhase) => phase.name + (phaseInPath: IPhase) => phaseInPath.name ).join(', ')}` ); } else { @@ -598,7 +639,7 @@ export class CommandLineConfiguration { public static tryLoadFromFile(jsonFilePath: string): CommandLineConfiguration | undefined { let commandLineJson: ICommandLineJson | undefined; try { - commandLineJson = JsonFile.loadAndValidate(jsonFilePath, CommandLineConfiguration._jsonSchema); + commandLineJson = JsonFile.loadAndValidate(jsonFilePath, _jsonSchema); } catch (e) { if (!FileSystem.isNotExistError(e as Error)) { throw e; @@ -606,7 +647,17 @@ export class CommandLineConfiguration { } if (commandLineJson) { - return new CommandLineConfiguration(commandLineJson, { doNotIncludeDefaultBuildCommands: true }); + _applyBuildCommandDefaults(commandLineJson); + const hasBuildCommand: boolean = !!commandLineJson.commands?.some( + (command) => command.name === RushConstants.buildCommandName + ); + const hasRebuildCommand: boolean = !!commandLineJson.commands?.some( + (command) => command.name === RushConstants.rebuildCommandName + ); + + return new CommandLineConfiguration(commandLineJson, { + doNotIncludeDefaultBuildCommands: !(hasBuildCommand || hasRebuildCommand) + }); } else { return undefined; } @@ -617,7 +668,10 @@ export class CommandLineConfiguration { * settings. If the file does not exist, then a default instance is returned. * If the file contains errors, then an exception is thrown. */ - public static loadFromFileOrDefault(jsonFilePath?: string): CommandLineConfiguration { + public static loadFromFileOrDefault( + jsonFilePath?: string, + doNotIncludeDefaultBuildCommands?: boolean + ): CommandLineConfiguration { let commandLineJson: ICommandLineJson | undefined = undefined; if (jsonFilePath) { try { @@ -631,63 +685,36 @@ export class CommandLineConfiguration { // merge commands specified in command-line.json and default (re)build settings // Ensure both build commands are included and preserve any other commands specified if (commandLineJson?.commands) { - for (let i: number = 0; i < commandLineJson.commands.length; i++) { - const command: CommandJson = commandLineJson.commands[i]; - - // Determine if we have a set of default parameters - let commandDefaultDefinition: CommandJson | {} = {}; - switch (command.commandKind) { - case RushConstants.phasedCommandKind: - case RushConstants.bulkCommandKind: { - switch (command.name) { - case RushConstants.buildCommandName: { - commandDefaultDefinition = DEFAULT_BUILD_COMMAND_JSON; - break; - } + _applyBuildCommandDefaults(commandLineJson); - case RushConstants.rebuildCommandName: { - commandDefaultDefinition = DEFAULT_REBUILD_COMMAND_JSON; - break; - } - } - break; - } - } + _jsonSchema.validateObject(commandLineJson, jsonFilePath); - // Merge the default parameters into the repo-specified parameters - commandLineJson.commands[i] = { - ...commandDefaultDefinition, - ...command - }; + // Validate that globalPlugin commands are not used in the repo's command-line.json + for (const { commandKind, name } of commandLineJson.commands) { + if (commandKind === RushConstants.globalPluginCommandKind) { + throw new Error( + `${RushConstants.commandLineFilename} defines a command "${name}" using ` + + `the command kind "${RushConstants.globalPluginCommandKind}". This command kind can only ` + + `be used in command-line.json files provided by Rush plugins.` + ); + } } - - CommandLineConfiguration._jsonSchema.validateObject(commandLineJson, jsonFilePath); } } - return new CommandLineConfiguration(commandLineJson, { doNotIncludeDefaultBuildCommands: false }); + return new CommandLineConfiguration(commandLineJson, { doNotIncludeDefaultBuildCommands }); } public prependAdditionalPathFolder(pathFolder: string): void { (this.additionalPathFolders as string[]).unshift(pathFolder); } - /** - * This function replaces colons (":") with underscores ("_"). - * - * ts-command-line restricts command names to lowercase letters, numbers, underscores, and colons. - * Replacing colons with underscores produces a filesystem-safe name. - */ - private _normalizeNameForLogFilenameIdentifiers(name: string): string { - return name.replace(/:/g, '_'); // Replace colons with underscores to be filesystem-safe - } - private _translateBulkCommandToPhasedCommand(command: IBulkCommandJson): IPhasedCommandConfig { const phaseName: string = command.name; const phase: IPhase = { name: phaseName, isSynthetic: true, - logFilenameIdentifier: this._normalizeNameForLogFilenameIdentifiers(command.name), + logFilenameIdentifier: _normalizeNameForLogFilenameIdentifiers(command.name), associatedParameters: new Set(), dependencies: { self: new Set(), @@ -723,3 +750,39 @@ export class CommandLineConfiguration { return translatedCommand; } } + +function _applyBuildCommandDefaults(commandLineJson: ICommandLineJson): void { + // merge commands specified in command-line.json and default (re)build settings + // Ensure both build commands are included and preserve any other commands specified + if (commandLineJson?.commands) { + for (let i: number = 0; i < commandLineJson.commands.length; i++) { + const command: CommandJson = commandLineJson.commands[i]; + + // Determine if we have a set of default parameters + let commandDefaultDefinition: CommandJson | {} = {}; + switch (command.commandKind) { + case RushConstants.phasedCommandKind: + case RushConstants.bulkCommandKind: { + switch (command.name) { + case RushConstants.buildCommandName: { + commandDefaultDefinition = DEFAULT_BUILD_COMMAND_JSON; + break; + } + + case RushConstants.rebuildCommandName: { + commandDefaultDefinition = DEFAULT_REBUILD_COMMAND_JSON; + break; + } + } + break; + } + } + + // Merge the default parameters into the repo-specified parameters + commandLineJson.commands[i] = { + ...commandDefaultDefinition, + ...command + }; + } + } +} diff --git a/libraries/rush-lib/src/api/CommandLineJson.ts b/libraries/rush-lib/src/api/CommandLineJson.ts index ba4f412176f..6d07a609808 100644 --- a/libraries/rush-lib/src/api/CommandLineJson.ts +++ b/libraries/rush-lib/src/api/CommandLineJson.ts @@ -5,7 +5,7 @@ * "baseCommand" from command-line.schema.json */ export interface IBaseCommandJson { - commandKind: 'bulk' | 'global' | 'phased'; + commandKind: 'bulk' | 'global' | 'globalPlugin' | 'phased'; name: string; summary: string; /** @@ -23,6 +23,7 @@ export interface IBaseCommandJson { export interface IBulkCommandJson extends IBaseCommandJson { commandKind: 'bulk'; enableParallelism: boolean; + allowOversubscription?: boolean; ignoreDependencyOrder?: boolean; ignoreMissingScript?: boolean; incremental?: boolean; @@ -38,6 +39,7 @@ export interface IBulkCommandJson extends IBaseCommandJson { export interface IPhasedCommandWithoutPhasesJson extends IBaseCommandJson { commandKind: 'phased'; enableParallelism: boolean; + allowOversubscription?: boolean; incremental?: boolean; } @@ -50,6 +52,7 @@ export interface IPhasedCommandJson extends IPhasedCommandWithoutPhasesJson { alwaysWatch: boolean; debounceMs?: number; watchPhases: string[]; + includeAllProjectsInWatchGraph?: boolean; }; installOptions?: { alwaysInstall: boolean; @@ -64,7 +67,20 @@ export interface IGlobalCommandJson extends IBaseCommandJson { shellCommand: string; } -export type CommandJson = IBulkCommandJson | IGlobalCommandJson | IPhasedCommandJson; +/** + * "globalPluginCommand" from command-line.schema.json. + * A global command whose implementation is provided entirely by a Rush plugin. + * This command kind can only be used in command-line.json files provided by Rush plugins. + */ +export interface IGlobalPluginCommandJson extends IBaseCommandJson { + commandKind: 'globalPlugin'; +} + +export type CommandJson = + | IBulkCommandJson + | IGlobalCommandJson + | IGlobalPluginCommandJson + | IPhasedCommandJson; /** * The dependencies of a phase. diff --git a/libraries/rush-lib/src/api/CommonVersionsConfiguration.ts b/libraries/rush-lib/src/api/CommonVersionsConfiguration.ts index 50dead14870..42b74f7e961 100644 --- a/libraries/rush-lib/src/api/CommonVersionsConfiguration.ts +++ b/libraries/rush-lib/src/api/CommonVersionsConfiguration.ts @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import crypto from 'crypto'; -import * as path from 'path'; +import crypto from 'node:crypto'; +import * as path from 'node:path'; + import { JsonFile, JsonSchema, @@ -12,8 +13,11 @@ import { Sort } from '@rushstack/node-core-library'; +import type { OptionalToUndefined } from '../utilities/Utilities'; import { PackageNameParsers } from './PackageNameParsers'; import { JsonSchemaUrls } from '../logic/JsonSchemaUrls'; +import type { RushConfiguration } from './RushConfiguration'; +import { RushConstants } from '../logic/RushConstants'; import schemaJson from '../schemas/common-versions.schema.json'; /** @@ -49,19 +53,22 @@ interface ICommonVersionsJson { implicitlyPreferredVersions?: boolean; allowedAlternativeVersions?: ICommonVersionsJsonVersionsMap; + + ensureConsistentVersions?: boolean; } +const _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); + /** * Use this class to load and save the "common/config/rush/common-versions.json" config file. * This config file stores dependency version information that affects all projects in the repo. * @public */ export class CommonVersionsConfiguration { - private static _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); - private _preferredVersions: ProtectableMap; private _allowedAlternativeVersions: ProtectableMap; private _modified: boolean = false; + private _commonVersionsJsonHasEnsureConsistentVersionsProperty: boolean; /** * Get the absolute file path of the common-versions.json file. @@ -78,6 +85,12 @@ export class CommonVersionsConfiguration { */ public readonly implicitlyPreferredVersions: boolean | undefined; + /** + * If true, then consistent version specifiers for dependencies will be enforced. + * I.e. "rush check" is run before some commands. + */ + public readonly ensureConsistentVersions: boolean; + /** * A table that specifies a "preferred version" for a given NPM package. This feature is typically used * to hold back an indirect dependency to a specific older version, or to reduce duplication of indirect dependencies. @@ -108,7 +121,11 @@ export class CommonVersionsConfiguration { */ public readonly allowedAlternativeVersions: Map>; - private constructor(commonVersionsJson: ICommonVersionsJson | undefined, filePath: string) { + private constructor( + commonVersionsJson: ICommonVersionsJson | undefined, + filePath: string, + rushConfiguration: RushConfiguration | undefined + ) { this._preferredVersions = new ProtectableMap({ onSet: this._onSetPreferredVersions.bind(this) }); @@ -125,16 +142,36 @@ export class CommonVersionsConfiguration { }); this.allowedAlternativeVersions = this._allowedAlternativeVersions.protectedView; + const subspacesFeatureEnabled: boolean | undefined = rushConfiguration?.subspacesFeatureEnabled; + const rushJsonEnsureConsistentVersions: boolean | undefined = + rushConfiguration?._ensureConsistentVersionsJsonValue; + const commonVersionsEnsureConsistentVersions: boolean | undefined = + commonVersionsJson?.ensureConsistentVersions; + if (subspacesFeatureEnabled && rushJsonEnsureConsistentVersions !== undefined) { + throw new Error( + `When using subspaces, the ensureConsistentVersions config is now defined in the ${RushConstants.commonVersionsFilename} file, ` + + `you must remove the old setting "ensureConsistentVersions" from ${RushConstants.rushJsonFilename}` + ); + } else if ( + !subspacesFeatureEnabled && + rushJsonEnsureConsistentVersions !== undefined && + commonVersionsEnsureConsistentVersions !== undefined + ) { + throw new Error( + `When the ensureConsistentVersions config is defined in the ${RushConstants.rushJsonFilename} file, ` + + `it cannot also be defined in the ${RushConstants.commonVersionsFilename} file` + ); + } + + this.ensureConsistentVersions = + commonVersionsEnsureConsistentVersions ?? rushJsonEnsureConsistentVersions ?? false; + this._commonVersionsJsonHasEnsureConsistentVersionsProperty = + commonVersionsEnsureConsistentVersions !== undefined; + if (commonVersionsJson) { try { - CommonVersionsConfiguration._deserializeTable( - this.preferredVersions, - commonVersionsJson.preferredVersions - ); - CommonVersionsConfiguration._deserializeTable( - this.allowedAlternativeVersions, - commonVersionsJson.allowedAlternativeVersions - ); + _deserializeTable(this.preferredVersions, commonVersionsJson.preferredVersions); + _deserializeTable(this.allowedAlternativeVersions, commonVersionsJson.allowedAlternativeVersions); } catch (e) { throw new Error(`Error loading "${path.basename(filePath)}": ${(e as Error).message}`); } @@ -143,40 +180,42 @@ export class CommonVersionsConfiguration { } /** - * Loads the common-versions.json data from the specified file path. - * If the file has not been created yet, then an empty object is returned. + * @deprecated Use {@link CommonVersionsConfiguration.loadFromFileAsync} method instead. */ - public static loadFromFile(jsonFilename: string): CommonVersionsConfiguration { + public static loadFromFile( + jsonFilePath: string, + rushConfiguration?: RushConfiguration + ): CommonVersionsConfiguration { let commonVersionsJson: ICommonVersionsJson | undefined = undefined; - - if (FileSystem.exists(jsonFilename)) { - commonVersionsJson = JsonFile.loadAndValidate(jsonFilename, CommonVersionsConfiguration._jsonSchema); + try { + commonVersionsJson = JsonFile.loadAndValidate(jsonFilePath, _jsonSchema); + } catch (error) { + if (!FileSystem.isNotExistError(error)) { + throw error; + } } - return new CommonVersionsConfiguration(commonVersionsJson, jsonFilename); + return new CommonVersionsConfiguration(commonVersionsJson, jsonFilePath, rushConfiguration); } - private static _deserializeTable( - map: Map, - object: { [key: string]: TValue } | undefined - ): void { - if (object) { - for (const [key, value] of Object.entries(object)) { - map.set(key, value); + /** + * Loads the common-versions.json data from the specified file path. + * If the file has not been created yet, then an empty object is returned. + */ + public static async loadFromFileAsync( + jsonFilePath: string, + rushConfiguration?: RushConfiguration + ): Promise { + let commonVersionsJson: ICommonVersionsJson | undefined = undefined; + try { + commonVersionsJson = await JsonFile.loadAndValidateAsync(jsonFilePath, _jsonSchema); + } catch (error) { + if (!FileSystem.isNotExistError(error)) { + throw error; } } - } - - private static _serializeTable(map: Map): { [key: string]: TValue } { - const table: { [key: string]: TValue } = {}; - - const keys: string[] = [...map.keys()]; - keys.sort(); - for (const key of keys) { - table[key] = map.get(key)!; - } - return table; + return new CommonVersionsConfiguration(commonVersionsJson, jsonFilePath, rushConfiguration); } /** @@ -196,11 +235,30 @@ export class CommonVersionsConfiguration { } /** - * Writes the "common-versions.json" file to disk, using the filename that was passed to loadFromFile(). + * @deprecated Use {@link CommonVersionsConfiguration.saveAsync} method instead. */ public save(): boolean { if (this._modified) { - JsonFile.save(this._serialize(), this.filePath, { updateExistingFile: true }); + JsonFile.save(this._serialize(), this.filePath, { + updateExistingFile: true, + ignoreUndefinedValues: true + }); + this._modified = false; + return true; + } + + return false; + } + + /** + * Writes the "common-versions.json" file to disk, using the filename that was passed to loadFromFile(). + */ + public async saveAsync(): Promise { + if (this._modified) { + await JsonFile.saveAsync(this._serialize(), this.filePath, { + updateExistingFile: true, + ignoreUndefinedValues: true + }); this._modified = false; return true; } @@ -242,20 +300,50 @@ export class CommonVersionsConfiguration { } private _serialize(): ICommonVersionsJson { - const result: ICommonVersionsJson = { - $schema: JsonSchemaUrls.commonVersions - }; - + let preferredVersions: ICommonVersionsJsonVersionMap | undefined; if (this._preferredVersions.size) { - result.preferredVersions = CommonVersionsConfiguration._serializeTable(this.preferredVersions); + preferredVersions = _serializeTable(this.preferredVersions); } + let allowedAlternativeVersions: ICommonVersionsJsonVersionsMap | undefined; if (this._allowedAlternativeVersions.size) { - result.allowedAlternativeVersions = CommonVersionsConfiguration._serializeTable( + allowedAlternativeVersions = _serializeTable( this.allowedAlternativeVersions ) as ICommonVersionsJsonVersionsMap; } + const result: OptionalToUndefined = { + $schema: JsonSchemaUrls.commonVersions, + preferredVersions, + implicitlyPreferredVersions: this.implicitlyPreferredVersions, + allowedAlternativeVersions, + ensureConsistentVersions: this._commonVersionsJsonHasEnsureConsistentVersionsProperty + ? this.ensureConsistentVersions + : undefined + }; return result; } } + +function _deserializeTable( + map: Map, + object: { [key: string]: TValue } | undefined +): void { + if (object) { + for (const [key, value] of Object.entries(object)) { + map.set(key, value); + } + } +} + +function _serializeTable(map: Map): { [key: string]: TValue } { + const table: { [key: string]: TValue } = {}; + + const keys: string[] = [...map.keys()]; + keys.sort(); + for (const key of keys) { + table[key] = map.get(key)!; + } + + return table; +} diff --git a/libraries/rush-lib/src/api/CustomTipsConfiguration.ts b/libraries/rush-lib/src/api/CustomTipsConfiguration.ts new file mode 100644 index 00000000000..8bc12a439e8 --- /dev/null +++ b/libraries/rush-lib/src/api/CustomTipsConfiguration.ts @@ -0,0 +1,382 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { FileSystem, JsonFile, JsonSchema } from '@rushstack/node-core-library'; +import { type ITerminal, PrintUtilities, Colorize } from '@rushstack/terminal'; + +import schemaJson from '../schemas/custom-tips.schema.json'; + +/** + * This interface represents the raw custom-tips.json file which allows repo maintainers + * to configure extra details to be printed alongside certain Rush messages. + * @beta + */ +export interface ICustomTipsJson { + /** + * Specifies the custom tips to be displayed by Rush. + */ + customTips?: ICustomTipItemJson[]; +} + +/** + * An item from the {@link ICustomTipsJson.customTips} list. + * @beta + */ +export interface ICustomTipItemJson { + /** + * (REQUIRED) An identifier indicating a message that may be printed by Rush. + * If that message is printed, then this custom tip will be shown. + * Consult the Rush documentation for the current list of possible identifiers. + */ + tipId: CustomTipId; + + /** + * (REQUIRED) The message text to be displayed for this tip. + */ + message: string; +} + +/** + * An identifier representing a Rush message that can be customized by + * defining a custom tip in `common/config/rush/custom-tips.json`. + * @remarks + * Custom tip ids always start with the `TIP_` prefix. + * + * @privateRemarks + * Events from the Rush process should with "TIP_RUSH_". + * Events from a PNPM subprocess should start with "TIP_PNPM_". + * + * @beta + */ +export enum CustomTipId { + // Events from the Rush process should with "TIP_RUSH_". + TIP_RUSH_INCONSISTENT_VERSIONS = 'TIP_RUSH_INCONSISTENT_VERSIONS', + TIP_RUSH_DISALLOW_INSECURE_SHA1 = 'TIP_RUSH_DISALLOW_INSECURE_SHA1', + + // Events from a PNPM subprocess should start with "TIP_PNPM_". + TIP_PNPM_UNEXPECTED_STORE = 'TIP_PNPM_UNEXPECTED_STORE', + TIP_PNPM_NO_MATCHING_VERSION = 'TIP_PNPM_NO_MATCHING_VERSION', + TIP_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE = 'TIP_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE', + TIP_PNPM_PEER_DEP_ISSUES = 'TIP_PNPM_PEER_DEP_ISSUES', + TIP_PNPM_OUTDATED_LOCKFILE = 'TIP_PNPM_OUTDATED_LOCKFILE', + TIP_PNPM_TARBALL_INTEGRITY = 'TIP_PNPM_TARBALL_INTEGRITY', + TIP_PNPM_MISMATCHED_RELEASE_CHANNEL = 'TIP_PNPM_MISMATCHED_RELEASE_CHANNEL', + TIP_PNPM_INVALID_NODE_VERSION = 'TIP_PNPM_INVALID_NODE_VERSION' +} + +/** + * The severity of a custom tip. + * It determines the printing severity ("Error" = red, "Warning" = yellow, "Info" = normal). + * + * @beta + */ +export enum CustomTipSeverity { + Warning = 'Warning', + Error = 'Error', + Info = 'Info' +} + +/** + * The type of the custom tip. + * + * @remarks + * There might be types like `git` in the future. + * + * @beta + */ +export enum CustomTipType { + rush = 'rush', + pnpm = 'pnpm' +} + +/** + * Metadata for a custom tip. + * + * @remarks + * This differs from the {@link ICustomTipItemJson} interface in that these are not configurable by the user; + * it's the inherent state of a custom tip. For example, the custom tip for `ERR_PNPM_NO_MATCHING_VERSION` + * has a inherent severity of `Error`, and a inherent match function that rush maintainer defines. + * + * @beta + */ +export interface ICustomTipInfo { + tipId: CustomTipId; + /** + * The severity of the custom tip. It will determine the printing severity ("Error" = red, "Warning" = yellow, "Info" = normal). + * + * @remarks + * The severity should be consistent with the original message, unless there are strong reasons not to. + */ + severity: CustomTipSeverity; + + /** + * The type of the custom tip. + */ + type: CustomTipType; + + /** + * The function to determine how to match this tipId. + * + * @remarks + * This function might need to be updated if the depending package is updated. + * For example, if `pnpm` change the error logs for "ERR_PNPM_NO_MATCHING_VERSION", we will need to update the match function accordingly. + */ + isMatch?: (str: string) => boolean; +} + +export const RUSH_CUSTOM_TIPS: Readonly> = { + [CustomTipId.TIP_RUSH_DISALLOW_INSECURE_SHA1]: { + tipId: CustomTipId.TIP_RUSH_DISALLOW_INSECURE_SHA1, + severity: CustomTipSeverity.Error, + type: CustomTipType.pnpm, + isMatch: (str: string) => { + return str.includes('ERR_PNPM_DISALLOW_INSECURE_SHA1'); + } + }, + [CustomTipId.TIP_RUSH_INCONSISTENT_VERSIONS]: { + tipId: CustomTipId.TIP_RUSH_INCONSISTENT_VERSIONS, + severity: CustomTipSeverity.Error, + type: CustomTipType.rush + } +}; + +export const PNPM_CUSTOM_TIPS: Readonly> = { + [CustomTipId.TIP_PNPM_UNEXPECTED_STORE]: { + tipId: CustomTipId.TIP_PNPM_UNEXPECTED_STORE, + severity: CustomTipSeverity.Error, + type: CustomTipType.pnpm, + isMatch: (str: string) => { + return str.includes('ERR_PNPM_UNEXPECTED_STORE'); + } + }, + [CustomTipId.TIP_PNPM_NO_MATCHING_VERSION]: { + tipId: CustomTipId.TIP_PNPM_NO_MATCHING_VERSION, + severity: CustomTipSeverity.Error, + type: CustomTipType.pnpm, + isMatch: (str: string) => { + // Example message: (do notice the difference between this one and the TIP_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE) + + // Error Message: ERR_PNPM_NO_MATCHING_VERSION  No matching version found for @babel/types@^7.22.5 + // The latest release of @babel/types is "7.22.4". + // Other releases are: + // * esm: 7.21.4-esm.4 + + return str.includes('No matching version found for') && str.includes('The latest release of'); + } + }, + [CustomTipId.TIP_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE]: { + tipId: CustomTipId.TIP_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE, + severity: CustomTipSeverity.Error, + type: CustomTipType.pnpm, + isMatch: (str: string) => { + return str.includes('ERR_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE'); + } + }, + [CustomTipId.TIP_PNPM_PEER_DEP_ISSUES]: { + tipId: CustomTipId.TIP_PNPM_PEER_DEP_ISSUES, + severity: CustomTipSeverity.Error, + type: CustomTipType.pnpm, + isMatch: (str: string) => { + return str.includes('ERR_PNPM_PEER_DEP_ISSUES'); + } + }, + [CustomTipId.TIP_PNPM_OUTDATED_LOCKFILE]: { + tipId: CustomTipId.TIP_PNPM_OUTDATED_LOCKFILE, + severity: CustomTipSeverity.Error, + type: CustomTipType.pnpm, + isMatch: (str: string) => { + // Todo: verify this + return str.includes('ERR_PNPM_OUTDATED_LOCKFILE'); + } + }, + + [CustomTipId.TIP_PNPM_TARBALL_INTEGRITY]: { + tipId: CustomTipId.TIP_PNPM_TARBALL_INTEGRITY, + severity: CustomTipSeverity.Error, + type: CustomTipType.pnpm, + isMatch: (str: string) => { + // Todo: verify this + return str.includes('ERR_PNPM_TARBALL_INTEGRITY'); + } + }, + + [CustomTipId.TIP_PNPM_MISMATCHED_RELEASE_CHANNEL]: { + tipId: CustomTipId.TIP_PNPM_MISMATCHED_RELEASE_CHANNEL, + severity: CustomTipSeverity.Error, + type: CustomTipType.pnpm, + isMatch: (str: string) => { + // Todo: verify this + return str.includes('ERR_PNPM_MISMATCHED_RELEASE_CHANNEL'); + } + }, + + [CustomTipId.TIP_PNPM_INVALID_NODE_VERSION]: { + tipId: CustomTipId.TIP_PNPM_INVALID_NODE_VERSION, + severity: CustomTipSeverity.Error, + type: CustomTipType.pnpm, + isMatch: (str: string) => { + // Todo: verify this + return str.includes('ERR_PNPM_INVALID_NODE_VERSION'); + } + } +}; + +const _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); + +/** + * Used to access the `common/config/rush/custom-tips.json` config file, + * which allows repo maintainers to configure extra details to be printed alongside + * certain Rush messages. + * @beta + */ +export class CustomTipsConfiguration { + public readonly providedCustomTipsByTipId: ReadonlyMap; + + /** + * A registry mapping custom tip IDs to their corresponding metadata. + * + * @remarks + * This registry is used to look up metadata for custom tips based on their IDs. The metadata includes + * information such as the severity level, the type of tip, and an optional matching function. + * + * Each key in the registry corresponds to a `CustomTipIdEnum` value, and each value is an object + * implementing the `ICustomTipInfo` interface. + * + * @example + * ```typescript + * const tipInfo = CustomTipsConfiguration.customTipRegistry[CustomTipIdEnum.TIP_RUSH_INCONSISTENT_VERSIONS]; + * console.log(tipInfo.severity); // Output: CustomTipSeverity.Error + * ``` + * + * See {@link CustomTipId} for the list of custom tip IDs. + * See {@link ICustomTipInfo} for the structure of the metadata. + */ + public static customTipRegistry: Readonly> = { + ...RUSH_CUSTOM_TIPS, + ...PNPM_CUSTOM_TIPS + }; + + public constructor(configFilePath: string) { + const providedCustomTips: Map = new Map(); + + let configuration: ICustomTipsJson | undefined; + try { + configuration = JsonFile.loadAndValidate(configFilePath, _jsonSchema); + } catch (e) { + if (!FileSystem.isNotExistError(e)) { + throw e; + } + } + + const customTips: ICustomTipItemJson[] | undefined = configuration?.customTips; + if (customTips) { + for (const tipItem of customTips) { + if (!(tipItem.tipId in CustomTipId)) { + throw new Error( + `The ${path.basename(configFilePath)} configuration` + + ` references an unknown ID "${tipItem.tipId}"` + ); + } + + if (providedCustomTips.has(tipItem.tipId)) { + throw new Error( + `The ${path.basename(configFilePath)} configuration` + + ` specifies a duplicate definition for "${tipItem.tipId}"` + ); + } else { + providedCustomTips.set(tipItem.tipId, tipItem); + } + } + } + + this.providedCustomTipsByTipId = providedCustomTips; + } + + /** + * If custom-tips.json defines a tip for the specified tipId, display the tip on the terminal. + * + * @remarks + * The severity of the tip is defined in ${@link CustomTipsConfiguration.customTipRegistry}. + * If you want to change the severity specifically for this call, + * use other APIs such as {@link CustomTipsConfiguration._showErrorTip}. + * + * Custom tips by design do not replace Rush's standard messaging; instead, they annotate Rush's + * output with additional team-specific advice. + * + * @internal + */ + public _showTip(terminal: ITerminal, tipId: CustomTipId): void { + const severityOfOriginalMessage: CustomTipSeverity = + CustomTipsConfiguration.customTipRegistry[tipId].severity; + + this._writeMessageWithPipes(terminal, severityOfOriginalMessage, tipId); + } + + /** + * If custom-tips.json defines a tip for the specified tipId, display the tip on the terminal. + * @remarks + * Custom tips by design do not replace Rush's standard messaging; instead, they annotate Rush's + * output with additional team-specific advice. + * @internal + */ + public _showInfoTip(terminal: ITerminal, tipId: CustomTipId): void { + this._writeMessageWithPipes(terminal, CustomTipSeverity.Info, tipId); + } + + /** + * If custom-tips.json defines a tip for the specified tipId, display the tip on the terminal. + * @remarks + * Custom tips by design do not replace Rush's standard messaging; instead, they annotate Rush's + * output with additional team-specific advice. + * @internal + */ + public _showWarningTip(terminal: ITerminal, tipId: CustomTipId): void { + this._writeMessageWithPipes(terminal, CustomTipSeverity.Warning, tipId); + } + + /** + * If custom-tips.json defines a tip for the specified tipId, display the tip on the terminal. + * @remarks + * Custom tips by design do not replace Rush's standard messaging; instead, they annotate Rush's + * output with additional team-specific advice. + * @internal + */ + public _showErrorTip(terminal: ITerminal, tipId: CustomTipId): void { + this._writeMessageWithPipes(terminal, CustomTipSeverity.Error, tipId); + } + + private _writeMessageWithPipes(terminal: ITerminal, severity: CustomTipSeverity, tipId: CustomTipId): void { + const customTipJsonItem: ICustomTipItemJson | undefined = this.providedCustomTipsByTipId.get(tipId); + if (customTipJsonItem) { + let writeFunction: + | typeof terminal.writeErrorLine + | typeof terminal.writeWarningLine + | typeof terminal.writeLine; + let prefix: string; + switch (severity) { + case CustomTipSeverity.Error: + writeFunction = terminal.writeErrorLine.bind(terminal); + prefix = Colorize.red('| '); + break; + case CustomTipSeverity.Warning: + writeFunction = terminal.writeWarningLine.bind(terminal); + prefix = Colorize.yellow('| '); + break; + default: + writeFunction = terminal.writeLine.bind(terminal); + prefix = '| '; + break; + } + + writeFunction(`| Custom Tip (${tipId})`); + writeFunction('|'); + + const message: string = customTipJsonItem.message; + const wrappedAndIndentedMessage: string = PrintUtilities.wrapWords(message, undefined, prefix); + writeFunction(...wrappedAndIndentedMessage, { doNotOverrideSgrCodes: true }); + terminal.writeLine(); + } + } +} diff --git a/libraries/rush-lib/src/api/EnvironmentConfiguration.ts b/libraries/rush-lib/src/api/EnvironmentConfiguration.ts index 96bc34cac99..58cdea5a062 100644 --- a/libraries/rush-lib/src/api/EnvironmentConfiguration.ts +++ b/libraries/rush-lib/src/api/EnvironmentConfiguration.ts @@ -1,11 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as os from 'os'; -import * as path from 'path'; +import * as path from 'node:path'; + import { trueCasePathSync } from 'true-case-path'; -import { IEnvironment } from '../utilities/Utilities'; +import type { IEnvironment } from '../utilities/Utilities'; +import { IS_WINDOWS } from '../utilities/executionUtilities'; /** * @beta @@ -144,6 +145,64 @@ export const EnvironmentVariableNames = { */ RUSH_BUILD_CACHE_WRITE_ALLOWED: 'RUSH_BUILD_CACHE_WRITE_ALLOWED', + /** + * Set this environment variable to a JSON string to override the build cache configuration that normally lives + * at `common/config/rush/build-cache.json`. + * + * This is useful for testing purposes, or for OSS repos that are have a local-only cache, but can have + * a different cache configuration in CI/CD pipelines. + * + * @remarks + * This is similar to {@link EnvironmentVariableNames.RUSH_BUILD_CACHE_OVERRIDE_JSON_FILE_PATH}, but it allows you to specify + * a JSON string instead of a file path. The two environment variables are mutually exclusive, meaning you can + * only use one of them at a time. + */ + RUSH_BUILD_CACHE_OVERRIDE_JSON: 'RUSH_BUILD_CACHE_OVERRIDE_JSON', + + /** + * Set this environment variable to the path to a `build-cache.json` file to override the build cache configuration + * that normally lives at `common/config/rush/build-cache.json`. + * + * This is useful for testing purposes, or for OSS repos that are have a local-only cache, but can have + * a different cache configuration in CI/CD pipelines. + * + * @remarks + * This is similar to {@link EnvironmentVariableNames.RUSH_BUILD_CACHE_OVERRIDE_JSON}, but it allows you to specify + * a file path instead of a JSON string. The two environment variables are mutually exclusive, meaning you can + * only use one of them at a time. + */ + RUSH_BUILD_CACHE_OVERRIDE_JSON_FILE_PATH: 'RUSH_BUILD_CACHE_OVERRIDE_JSON_FILE_PATH', + + /** + * Setting this environment variable opts into running with cobuilds. The context id should be the same across + * multiple VMs, but changed when it is a new round of cobuilds. + * + * e.g. `Build.BuildNumber` in Azure DevOps Pipeline. + * + * @remarks + * If there is no cobuild configured, then this environment variable is ignored. + */ + RUSH_COBUILD_CONTEXT_ID: 'RUSH_COBUILD_CONTEXT_ID', + + /** + * Explicitly specifies a name for each participating cobuild runner. + * + * Setting this environment variable opts into running with cobuilds. + * + * @remarks + * This environment variable is optional, if it is not provided, a random id is used. + * + * If there is no cobuild configured, then this environment variable is ignored. + */ + RUSH_COBUILD_RUNNER_ID: 'RUSH_COBUILD_RUNNER_ID', + + /** + * If this variable is set to "1", When getting distributed builds, Rush will automatically handle the leaf project + * with build cache "disabled" by writing to the cache in a special "log files only mode". This is useful when you + * want to use Cobuilds to improve the performance in CI validations and the leaf projects have not enabled cache. + */ + RUSH_COBUILD_LEAF_PROJECT_LOG_ONLY_ALLOWED: 'RUSH_COBUILD_LEAF_PROJECT_LOG_ONLY_ALLOWED', + /** * Explicitly specifies the path for the Git binary that is invoked by certain Rush operations. */ @@ -154,11 +213,17 @@ export const EnvironmentVariableNames = { */ RUSH_TAR_BINARY_PATH: 'RUSH_TAR_BINARY_PATH', + /** + * Internal variable used by `rushx` when recursively invoking another `rushx` process, to avoid + * nesting event hooks. + */ + _RUSH_RECURSIVE_RUSHX_CALL: '_RUSH_RECURSIVE_RUSHX_CALL', + /** * Internal variable that explicitly specifies the path for the version of `@microsoft/rush-lib` being executed. * Will be set upon loading Rush. */ - RUSH_LIB_PATH: '_RUSH_LIB_PATH', + _RUSH_LIB_PATH: '_RUSH_LIB_PATH', /** * When Rush executes shell scripts, it sometimes changes the working directory to be a project folder or @@ -169,50 +234,87 @@ export const EnvironmentVariableNames = { * The `RUSH_INVOKED_FOLDER` variable is the same idea as the `INIT_CWD` variable that package managers * assign when they execute lifecycle scripts. */ - RUSH_INVOKED_FOLDER: 'RUSH_INVOKED_FOLDER' + RUSH_INVOKED_FOLDER: 'RUSH_INVOKED_FOLDER', + + /** + * When running a hook script, this environment variable communicates the original arguments + * passed to the `rush` or `rushx` command. + * + * @remarks + * Unlike `RUSH_INVOKED_FOLDER`, the `RUSH_INVOKED_ARGS` variable is only available for hook scripts. + * Other lifecycle scripts should not make assumptions about Rush's command line syntax + * if Rush did not explicitly pass along command-line parameters to their process. + */ + RUSH_INVOKED_ARGS: 'RUSH_INVOKED_ARGS', + + /** + * When set to `1` or `true`, this environment variable is equivalent to passing the `--quiet` flag + * to `rush`, `rushx`, and `install-run-rush.ts`. It suppresses informational startup messages + * while preserving error output. + */ + RUSH_QUIET_MODE: 'RUSH_QUIET_MODE' } as const; -/** - * Provides Rush-specific environment variable data. All Rush environment variables must start with "RUSH_". This class - * is designed to be used by RushConfiguration. - * @beta - * - * @remarks - * Initialize will throw if any unknown parameters are present. - */ -export class EnvironmentConfiguration { - private static _hasBeenValidated: boolean = false; +let _hasBeenValidated: boolean = false; + +let _rushTempFolderOverride: string | undefined; + +let _absoluteSymlinks: boolean = false; + +let _allowUnsupportedNodeVersion: boolean = false; + +let _allowWarningsInSuccessfulBuild: boolean = false; + +let _pnpmStorePathOverride: string | undefined; - private static _rushTempFolderOverride: string | undefined; +let _pnpmVerifyStoreIntegrity: boolean | undefined; - private static _absoluteSymlinks: boolean = false; +let _rushGlobalFolderOverride: string | undefined; - private static _allowUnsupportedNodeVersion: boolean = false; +let _buildCacheCredential: string | undefined; - private static _allowWarningsInSuccessfulBuild: boolean = false; +let _buildCacheEnabled: boolean | undefined; - private static _pnpmStorePathOverride: string | undefined; +let _buildCacheWriteAllowed: boolean | undefined; - private static _pnpmVerifyStoreIntegrity: boolean | undefined; +let _buildCacheOverrideJson: string | undefined; - private static _rushGlobalFolderOverride: string | undefined; +let _buildCacheOverrideJsonFilePath: string | undefined; - private static _buildCacheCredential: string | undefined; +let _cobuildContextId: string | undefined; - private static _buildCacheEnabled: boolean | undefined; +let _cobuildRunnerId: string | undefined; - private static _buildCacheWriteAllowed: boolean | undefined; +let _cobuildLeafProjectLogOnlyAllowed: boolean | undefined; - private static _gitBinaryPath: string | undefined; +let _gitBinaryPath: string | undefined; - private static _tarBinaryPath: string | undefined; +let _tarBinaryPath: string | undefined; + +let _quietMode: boolean = false; + +/** + * Provides Rush-specific environment variable data. All Rush environment variables must start with "RUSH_". This class + * is designed to be used by RushConfiguration. + * @beta + * + * @remarks + * Initialize will throw if any unknown parameters are present. + */ +export class EnvironmentConfiguration { + /** + * If true, the environment configuration has been validated and initialized. + */ + public static get hasBeenValidated(): boolean { + return _hasBeenValidated; + } /** * An override for the common/temp folder path. */ public static get rushTempFolderOverride(): string | undefined { - EnvironmentConfiguration._ensureValidated(); - return EnvironmentConfiguration._rushTempFolderOverride; + _ensureValidated(); + return _rushTempFolderOverride; } /** @@ -220,8 +322,8 @@ export class EnvironmentConfiguration { * See {@link EnvironmentVariableNames.RUSH_ABSOLUTE_SYMLINKS} */ public static get absoluteSymlinks(): boolean { - EnvironmentConfiguration._ensureValidated(); - return EnvironmentConfiguration._absoluteSymlinks; + _ensureValidated(); + return _absoluteSymlinks; } /** @@ -232,8 +334,8 @@ export class EnvironmentConfiguration { * See {@link EnvironmentVariableNames.RUSH_ALLOW_UNSUPPORTED_NODEJS}. */ public static get allowUnsupportedNodeVersion(): boolean { - EnvironmentConfiguration._ensureValidated(); - return EnvironmentConfiguration._allowUnsupportedNodeVersion; + _ensureValidated(); + return _allowUnsupportedNodeVersion; } /** @@ -242,8 +344,8 @@ export class EnvironmentConfiguration { * or `0` to disallow them. (See the comments in the command-line.json file for more information). */ public static get allowWarningsInSuccessfulBuild(): boolean { - EnvironmentConfiguration._ensureValidated(); - return EnvironmentConfiguration._allowWarningsInSuccessfulBuild; + _ensureValidated(); + return _allowWarningsInSuccessfulBuild; } /** @@ -251,8 +353,8 @@ export class EnvironmentConfiguration { * See {@link EnvironmentVariableNames.RUSH_PNPM_STORE_PATH} */ public static get pnpmStorePathOverride(): string | undefined { - EnvironmentConfiguration._ensureValidated(); - return EnvironmentConfiguration._pnpmStorePathOverride; + _ensureValidated(); + return _pnpmStorePathOverride; } /** @@ -260,8 +362,8 @@ export class EnvironmentConfiguration { * See {@link EnvironmentVariableNames.RUSH_PNPM_VERIFY_STORE_INTEGRITY} */ public static get pnpmVerifyStoreIntegrity(): boolean | undefined { - EnvironmentConfiguration._ensureValidated(); - return EnvironmentConfiguration._pnpmVerifyStoreIntegrity; + _ensureValidated(); + return _pnpmVerifyStoreIntegrity; } /** @@ -269,8 +371,8 @@ export class EnvironmentConfiguration { * See {@link EnvironmentVariableNames.RUSH_GLOBAL_FOLDER} */ public static get rushGlobalFolderOverride(): string | undefined { - EnvironmentConfiguration._ensureValidated(); - return EnvironmentConfiguration._rushGlobalFolderOverride; + _ensureValidated(); + return _rushGlobalFolderOverride; } /** @@ -278,8 +380,8 @@ export class EnvironmentConfiguration { * See {@link EnvironmentVariableNames.RUSH_BUILD_CACHE_CREDENTIAL} */ public static get buildCacheCredential(): string | undefined { - EnvironmentConfiguration._ensureValidated(); - return EnvironmentConfiguration._buildCacheCredential; + _ensureValidated(); + return _buildCacheCredential; } /** @@ -287,8 +389,8 @@ export class EnvironmentConfiguration { * See {@link EnvironmentVariableNames.RUSH_BUILD_CACHE_ENABLED} */ public static get buildCacheEnabled(): boolean | undefined { - EnvironmentConfiguration._ensureValidated(); - return EnvironmentConfiguration._buildCacheEnabled; + _ensureValidated(); + return _buildCacheEnabled; } /** @@ -296,8 +398,53 @@ export class EnvironmentConfiguration { * See {@link EnvironmentVariableNames.RUSH_BUILD_CACHE_WRITE_ALLOWED} */ public static get buildCacheWriteAllowed(): boolean | undefined { - EnvironmentConfiguration._ensureValidated(); - return EnvironmentConfiguration._buildCacheWriteAllowed; + _ensureValidated(); + return _buildCacheWriteAllowed; + } + + /** + * If set, overrides the build cache configuration that normally lives at `common/config/rush/build-cache.json`. + * See {@link EnvironmentVariableNames.RUSH_BUILD_CACHE_OVERRIDE_JSON} + */ + public static get buildCacheOverrideJson(): string | undefined { + _ensureValidated(); + return _buildCacheOverrideJson; + } + + /** + * If set, overrides the build cache configuration that normally lives at `common/config/rush/build-cache.json`. + * See {@link EnvironmentVariableNames.RUSH_BUILD_CACHE_OVERRIDE_JSON_FILE_PATH} + */ + public static get buildCacheOverrideJsonFilePath(): string | undefined { + _ensureValidated(); + return _buildCacheOverrideJsonFilePath; + } + + /** + * Provides a determined cobuild context id if configured + * See {@link EnvironmentVariableNames.RUSH_COBUILD_CONTEXT_ID} + */ + public static get cobuildContextId(): string | undefined { + _ensureValidated(); + return _cobuildContextId; + } + + /** + * Provides a determined cobuild runner id if configured + * See {@link EnvironmentVariableNames.RUSH_COBUILD_RUNNER_ID} + */ + public static get cobuildRunnerId(): string | undefined { + _ensureValidated(); + return _cobuildRunnerId; + } + + /** + * If set, enables or disables the cobuild leaf project log only feature. + * See {@link EnvironmentVariableNames.RUSH_COBUILD_LEAF_PROJECT_LOG_ONLY_ALLOWED} + */ + public static get cobuildLeafProjectLogOnlyAllowed(): boolean | undefined { + _ensureValidated(); + return _cobuildLeafProjectLogOnlyAllowed; } /** @@ -305,8 +452,8 @@ export class EnvironmentConfiguration { * See {@link EnvironmentVariableNames.RUSH_GIT_BINARY_PATH} */ public static get gitBinaryPath(): string | undefined { - EnvironmentConfiguration._ensureValidated(); - return EnvironmentConfiguration._gitBinaryPath; + _ensureValidated(); + return _gitBinaryPath; } /** @@ -314,8 +461,17 @@ export class EnvironmentConfiguration { * See {@link EnvironmentVariableNames.RUSH_TAR_BINARY_PATH} */ public static get tarBinaryPath(): string | undefined { - EnvironmentConfiguration._ensureValidated(); - return EnvironmentConfiguration._tarBinaryPath; + _ensureValidated(); + return _tarBinaryPath; + } + + /** + * If `true`, Rush will suppress informational startup messages, equivalent to passing `--quiet`. + * See {@link EnvironmentVariableNames.RUSH_QUIET_MODE} + */ + public static get quietMode(): boolean { + _ensureValidated(); + return _quietMode; } /** @@ -327,8 +483,7 @@ export class EnvironmentConfiguration { public static _getRushGlobalFolderOverride(processEnv: IEnvironment): string | undefined { const value: string | undefined = processEnv[EnvironmentVariableNames.RUSH_GLOBAL_FOLDER]; if (value) { - const normalizedValue: string | undefined = - EnvironmentConfiguration._normalizeDeepestParentFolderPath(value); + const normalizedValue: string | undefined = _normalizeDeepestParentFolderPath(value); return normalizedValue; } } @@ -344,19 +499,18 @@ export class EnvironmentConfiguration { if (process.env.hasOwnProperty(envVarName) && envVarName.match(/^RUSH_/i)) { const value: string | undefined = process.env[envVarName]; // Environment variables are only case-insensitive on Windows - const normalizedEnvVarName: string = - os.platform() === 'win32' ? envVarName.toUpperCase() : envVarName; + const normalizedEnvVarName: string = IS_WINDOWS ? envVarName.toUpperCase() : envVarName; switch (normalizedEnvVarName) { case EnvironmentVariableNames.RUSH_TEMP_FOLDER: { - EnvironmentConfiguration._rushTempFolderOverride = + _rushTempFolderOverride = value && !options.doNotNormalizePaths - ? EnvironmentConfiguration._normalizeDeepestParentFolderPath(value) || value + ? _normalizeDeepestParentFolderPath(value) || value : value; break; } case EnvironmentVariableNames.RUSH_ABSOLUTE_SYMLINKS: { - EnvironmentConfiguration._absoluteSymlinks = + _absoluteSymlinks = EnvironmentConfiguration.parseBooleanEnvironmentVariable( EnvironmentVariableNames.RUSH_ABSOLUTE_SYMLINKS, value @@ -368,9 +522,9 @@ export class EnvironmentConfiguration { if (value === 'true' || value === 'false') { // Small, undocumented acceptance of old "true" and "false" values for // users of RUSH_ALLOW_UNSUPPORTED_NODEJS in rush pre-v5.46. - EnvironmentConfiguration._allowUnsupportedNodeVersion = value === 'true'; + _allowUnsupportedNodeVersion = value === 'true'; } else { - EnvironmentConfiguration._allowUnsupportedNodeVersion = + _allowUnsupportedNodeVersion = EnvironmentConfiguration.parseBooleanEnvironmentVariable( EnvironmentVariableNames.RUSH_ALLOW_UNSUPPORTED_NODEJS, value @@ -380,7 +534,7 @@ export class EnvironmentConfiguration { } case EnvironmentVariableNames.RUSH_ALLOW_WARNINGS_IN_SUCCESSFUL_BUILD: { - EnvironmentConfiguration._allowWarningsInSuccessfulBuild = + _allowWarningsInSuccessfulBuild = EnvironmentConfiguration.parseBooleanEnvironmentVariable( EnvironmentVariableNames.RUSH_ALLOW_WARNINGS_IN_SUCCESSFUL_BUILD, value @@ -389,16 +543,15 @@ export class EnvironmentConfiguration { } case EnvironmentVariableNames.RUSH_PNPM_STORE_PATH: { - EnvironmentConfiguration._pnpmStorePathOverride = + _pnpmStorePathOverride = value && !options.doNotNormalizePaths - ? EnvironmentConfiguration._normalizeDeepestParentFolderPath(value) || value + ? _normalizeDeepestParentFolderPath(value) || value : value; break; } case EnvironmentVariableNames.RUSH_PNPM_VERIFY_STORE_INTEGRITY: { - EnvironmentConfiguration._pnpmVerifyStoreIntegrity = - value === '1' ? true : value === '0' ? false : undefined; + _pnpmVerifyStoreIntegrity = value === '1' ? true : value === '0' ? false : undefined; break; } @@ -408,35 +561,75 @@ export class EnvironmentConfiguration { } case EnvironmentVariableNames.RUSH_BUILD_CACHE_CREDENTIAL: { - EnvironmentConfiguration._buildCacheCredential = value; + _buildCacheCredential = value; break; } case EnvironmentVariableNames.RUSH_BUILD_CACHE_ENABLED: { - EnvironmentConfiguration._buildCacheEnabled = - EnvironmentConfiguration.parseBooleanEnvironmentVariable( - EnvironmentVariableNames.RUSH_BUILD_CACHE_ENABLED, - value - ); + _buildCacheEnabled = EnvironmentConfiguration.parseBooleanEnvironmentVariable( + EnvironmentVariableNames.RUSH_BUILD_CACHE_ENABLED, + value + ); break; } case EnvironmentVariableNames.RUSH_BUILD_CACHE_WRITE_ALLOWED: { - EnvironmentConfiguration._buildCacheWriteAllowed = - EnvironmentConfiguration.parseBooleanEnvironmentVariable( - EnvironmentVariableNames.RUSH_BUILD_CACHE_WRITE_ALLOWED, - value - ); + _buildCacheWriteAllowed = EnvironmentConfiguration.parseBooleanEnvironmentVariable( + EnvironmentVariableNames.RUSH_BUILD_CACHE_WRITE_ALLOWED, + value + ); + break; + } + + case EnvironmentVariableNames.RUSH_BUILD_CACHE_OVERRIDE_JSON: { + _buildCacheOverrideJson = value; + break; + } + + case EnvironmentVariableNames.RUSH_BUILD_CACHE_OVERRIDE_JSON_FILE_PATH: { + _buildCacheOverrideJsonFilePath = value; + break; + } + + case EnvironmentVariableNames.RUSH_COBUILD_CONTEXT_ID: { + _cobuildContextId = value; + break; + } + + case EnvironmentVariableNames.RUSH_COBUILD_RUNNER_ID: { + _cobuildRunnerId = value; + break; + } + + case EnvironmentVariableNames.RUSH_COBUILD_LEAF_PROJECT_LOG_ONLY_ALLOWED: { + _cobuildLeafProjectLogOnlyAllowed = EnvironmentConfiguration.parseBooleanEnvironmentVariable( + EnvironmentVariableNames.RUSH_COBUILD_LEAF_PROJECT_LOG_ONLY_ALLOWED, + value + ); break; } case EnvironmentVariableNames.RUSH_GIT_BINARY_PATH: { - EnvironmentConfiguration._gitBinaryPath = value; + _gitBinaryPath = value; break; } case EnvironmentVariableNames.RUSH_TAR_BINARY_PATH: { - EnvironmentConfiguration._tarBinaryPath = value; + _tarBinaryPath = value; + break; + } + + case EnvironmentVariableNames.RUSH_QUIET_MODE: { + // Accept both "true"/"false" string values and the standard "1"/"0" values + if (value === 'true' || value === 'false') { + _quietMode = value === 'true'; + } else { + _quietMode = + EnvironmentConfiguration.parseBooleanEnvironmentVariable( + EnvironmentVariableNames.RUSH_QUIET_MODE, + value + ) ?? false; + } break; } @@ -448,10 +641,15 @@ export class EnvironmentConfiguration { break; case EnvironmentVariableNames.RUSH_INVOKED_FOLDER: - case EnvironmentVariableNames.RUSH_LIB_PATH: + case EnvironmentVariableNames.RUSH_INVOKED_ARGS: + case EnvironmentVariableNames._RUSH_LIB_PATH: // Assigned by Rush itself break; + case EnvironmentVariableNames._RUSH_RECURSIVE_RUSHX_CALL: + // Assigned/read internally by RushXCommandLine + break; + default: unknownEnvVariables.push(envVarName); break; @@ -467,26 +665,30 @@ export class EnvironmentConfiguration { ); } + if (_buildCacheOverrideJsonFilePath && _buildCacheOverrideJson) { + throw new Error( + `Environment variable ${EnvironmentVariableNames.RUSH_BUILD_CACHE_OVERRIDE_JSON_FILE_PATH} and ` + + `${EnvironmentVariableNames.RUSH_BUILD_CACHE_OVERRIDE_JSON} are mutually exclusive. ` + + `Only one may be specified.` + ); + } + // See doc comment for EnvironmentConfiguration._getRushGlobalFolderOverride(). - EnvironmentConfiguration._rushGlobalFolderOverride = - EnvironmentConfiguration._getRushGlobalFolderOverride(process.env); + _rushGlobalFolderOverride = EnvironmentConfiguration._getRushGlobalFolderOverride(process.env); - EnvironmentConfiguration._hasBeenValidated = true; + _hasBeenValidated = true; } /** * Resets EnvironmentConfiguration into an un-initialized state. */ public static reset(): void { - EnvironmentConfiguration._rushTempFolderOverride = undefined; - - EnvironmentConfiguration._hasBeenValidated = false; - } - - private static _ensureValidated(): void { - if (!EnvironmentConfiguration._hasBeenValidated) { - EnvironmentConfiguration.validate(); - } + _rushTempFolderOverride = undefined; + _pnpmStorePathOverride = undefined; + _quietMode = false; + _gitBinaryPath = undefined; + _tarBinaryPath = undefined; + _hasBeenValidated = false; } public static parseBooleanEnvironmentVariable( @@ -505,49 +707,53 @@ export class EnvironmentConfiguration { ); } } +} - /** - * Given a path to a folder (that may or may not exist), normalize the path, including casing, - * to the first existing parent folder in the path. - * - * If no existing path can be found (for example, if the root is a volume that doesn't exist), - * this function returns undefined. - * - * @example - * If the following path exists on disk: `C:\Folder1\folder2\` - * _normalizeFirstExistingFolderPath('c:\\folder1\\folder2\\temp\\subfolder') - * returns 'C:\\Folder1\\folder2\\temp\\subfolder' - */ - private static _normalizeDeepestParentFolderPath(folderPath: string): string | undefined { - folderPath = path.normalize(folderPath); - const endsWithSlash: boolean = folderPath.charAt(folderPath.length - 1) === path.sep; - const parsedPath: path.ParsedPath = path.parse(folderPath); - const pathRoot: string = parsedPath.root; - const pathWithoutRoot: string = parsedPath.dir.substr(pathRoot.length); - const pathParts: string[] = [...pathWithoutRoot.split(path.sep), parsedPath.name].filter( - (part) => !!part - ); - - // Starting with all path sections, and eliminating one from the end during each loop iteration, - // run trueCasePathSync. If trueCasePathSync returns without exception, we've found a subset - // of the path that exists and we've now gotten the correct casing. - // - // Once we've found a parent folder that exists, append the path sections that didn't exist. - for (let i: number = pathParts.length; i >= 0; i--) { - const constructedPath: string = path.join(pathRoot, ...pathParts.slice(0, i)); - try { - const normalizedConstructedPath: string = trueCasePathSync(constructedPath); - const result: string = path.join(normalizedConstructedPath, ...pathParts.slice(i)); - if (endsWithSlash) { - return `${result}${path.sep}`; - } else { - return result; - } - } catch (e) { - // This path doesn't exist, continue to the next subpath +function _ensureValidated(): void { + if (!_hasBeenValidated) { + EnvironmentConfiguration.validate(); + } +} + +/** + * Given a path to a folder (that may or may not exist), normalize the path, including casing, + * to the first existing parent folder in the path. + * + * If no existing path can be found (for example, if the root is a volume that doesn't exist), + * this function returns undefined. + * + * @example + * If the following path exists on disk: `C:\Folder1\folder2\` + * _normalizeFirstExistingFolderPath('c:\\folder1\\folder2\\temp\\subfolder') + * returns 'C:\\Folder1\\folder2\\temp\\subfolder' + */ +function _normalizeDeepestParentFolderPath(folderPath: string): string | undefined { + folderPath = path.normalize(folderPath); + const endsWithSlash: boolean = folderPath.charAt(folderPath.length - 1) === path.sep; + const parsedPath: path.ParsedPath = path.parse(folderPath); + const pathRoot: string = parsedPath.root; + const pathWithoutRoot: string = parsedPath.dir.substr(pathRoot.length); + const pathParts: string[] = [...pathWithoutRoot.split(path.sep), parsedPath.name].filter((part) => !!part); + + // Starting with all path sections, and eliminating one from the end during each loop iteration, + // run trueCasePathSync. If trueCasePathSync returns without exception, we've found a subset + // of the path that exists and we've now gotten the correct casing. + // + // Once we've found a parent folder that exists, append the path sections that didn't exist. + for (let i: number = pathParts.length; i >= 0; i--) { + const constructedPath: string = path.join(pathRoot, ...pathParts.slice(0, i)); + try { + const normalizedConstructedPath: string = trueCasePathSync(constructedPath); + const result: string = path.join(normalizedConstructedPath, ...pathParts.slice(i)); + if (endsWithSlash) { + return `${result}${path.sep}`; + } else { + return result; } + } catch (e) { + // This path doesn't exist, continue to the next subpath } - - return undefined; } + + return undefined; } diff --git a/libraries/rush-lib/src/api/EventHooks.ts b/libraries/rush-lib/src/api/EventHooks.ts index 25a137094d4..d53fdd365ad 100644 --- a/libraries/rush-lib/src/api/EventHooks.ts +++ b/libraries/rush-lib/src/api/EventHooks.ts @@ -1,11 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IEventHooksJson } from './RushConfiguration'; import { Enum } from '@rushstack/node-core-library'; +import type { IEventHooksJson } from './RushConfiguration'; + /** - * Events happen during Rush runs. + * Events happen during Rush invocation. * @beta */ export enum Event { @@ -24,7 +25,15 @@ export enum Event { /** * Post Rush build event */ - postRushBuild = 4 + postRushBuild = 4, + /** + * Start of rushx execution event + */ + preRushx = 5, + /** + * End of rushx execution event + */ + postRushx = 6 } /** @@ -44,7 +53,7 @@ export class EventHooks { for (const [name, eventHooks] of Object.entries(eventHooksJson)) { const eventName: Event | undefined = Enum.tryGetValueByKey(Event, name); if (eventName) { - this._hooks.set(eventName, [...eventHooks] || []); + this._hooks.set(eventName, [...eventHooks]); } } } diff --git a/libraries/rush-lib/src/api/ExperimentsConfiguration.ts b/libraries/rush-lib/src/api/ExperimentsConfiguration.ts index 607b17a67b9..0f8db8e9d00 100644 --- a/libraries/rush-lib/src/api/ExperimentsConfiguration.ts +++ b/libraries/rush-lib/src/api/ExperimentsConfiguration.ts @@ -2,9 +2,12 @@ // See LICENSE in the project root for license information. import { JsonFile, JsonSchema, FileSystem } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; import schemaJson from '../schemas/experiments.schema.json'; +const GRADUATED_EXPERIMENTS: Set = new Set(['phasedCommands']); + /** * This interface represents the raw experiments.json file which allows repo * maintainers to enable and disable experimental Rush features. @@ -13,16 +16,23 @@ import schemaJson from '../schemas/experiments.schema.json'; export interface IExperimentsJson { /** * By default, 'rush install' passes --no-prefer-frozen-lockfile to 'pnpm install'. - * Set this option to true to pass '--frozen-lockfile' instead. + * Set this option to true to pass '--frozen-lockfile' instead for faster installs. */ usePnpmFrozenLockfileForRushInstall?: boolean; /** * By default, 'rush update' passes --no-prefer-frozen-lockfile to 'pnpm install'. - * Set this option to true to pass '--prefer-frozen-lockfile' instead. + * Set this option to true to pass '--prefer-frozen-lockfile' instead to minimize shrinkwrap changes. */ usePnpmPreferFrozenLockfileForRushUpdate?: boolean; + /** + * By default, 'rush update' runs as a single operation. + * Set this option to true to instead update the lockfile with `--lockfile-only`, then perform a `--frozen-lockfile` install. + * Necessary when using the `afterAllResolved` hook in .pnpmfile.cjs. + */ + usePnpmLockfileOnlyThenFrozenLockfileForRushUpdate?: boolean; + /** * If using the 'preventManualShrinkwrapChanges' option, restricts the hash to only include the layout of external dependencies. * Used to allow links between workspace projects or the addition/removal of references to existing dependency versions to not @@ -43,10 +53,10 @@ export interface IExperimentsJson { buildCacheWithAllowWarningsInSuccessfulBuild?: boolean; /** - * If true, the phased commands feature is enabled. To use this feature, create a "phased" command - * in common/config/rush/command-line.json. + * If true, build skipping will respect the allowWarningsInSuccessfulBuild flag and skip builds with warnings. + * This will not replay warnings from the skipped build. */ - phasedCommands?: boolean; + buildSkipWithAllowWarningsInSuccessfulBuild?: boolean; /** * If true, perform a clean install after when running `rush install` or `rush update` if the @@ -63,18 +73,96 @@ export interface IExperimentsJson { * If true, Rush will not allow node_modules in the repo folder or in parent folders. */ forbidPhantomResolvableNodeModulesFolders?: boolean; + + /** + * (UNDER DEVELOPMENT) For certain installation problems involving peer dependencies, PNPM cannot + * correctly satisfy versioning requirements without installing duplicate copies of a package inside the + * node_modules folder. This poses a problem for "workspace:*" dependencies, as they are normally + * installed by making a symlink to the local project source folder. PNPM's "injected dependencies" + * feature provides a model for copying the local project folder into node_modules, however copying + * must occur AFTER the dependency project is built and BEFORE the consuming project starts to build. + * The "pnpm-sync" tool manages this operation; see its documentation for details. + * Enable this experiment if you want "rush" and "rushx" commands to resync injected dependencies + * by invoking "pnpm-sync" during the build. + */ + usePnpmSyncForInjectedDependencies?: boolean; + + /** + * If set to true, Rush will generate a `project-impact-graph.yaml` file in the repository root during `rush update`. + */ + generateProjectImpactGraphDuringRushUpdate?: boolean; + + /** + * If true, when running in watch mode, Rush will check for phase scripts named `_phase::ipc` and run them instead + * of `_phase:` if they exist. The created child process will be provided with an IPC channel and expected to persist + * across invocations. + */ + useIPCScriptsInWatchMode?: boolean; + + /** + * (UNDER DEVELOPMENT) The Rush alerts feature provides a way to send announcements to engineers + * working in the monorepo, by printing directly in the user's shell window when they invoke Rush commands. + * This ensures that important notices will be seen by anyone doing active development, since people often + * ignore normal discussion group messages or don't know to subscribe. + */ + rushAlerts?: boolean; + + /** + * Allow cobuilds without using the build cache to store previous execution info. When setting up + * distributed builds, Rush will allow uncacheable projects to still leverage the cobuild feature. + * This is useful when you want to speed up operations that can't (or shouldn't) be cached. + */ + allowCobuildWithoutCache?: boolean; + + /** + * By default, rush perform a full scan of the entire repository. For example, Rush runs `git status` to check for local file changes. + * When this toggle is enabled, Rush will only scan specific paths, significantly speeding up Git operations. + */ + enableSubpathScan?: boolean; + + /** + * Rush has a policy that normally requires Rush projects to specify `workspace:*` in package.json when depending + * on other projects in the workspace, unless they are explicitly declared as `decoupledLocalDependencies` + * in rush.json. Enabling this experiment will remove that requirement for dependencies belonging to a different + * subspace. This is useful for large product groups who work in separate subspaces and generally prefer to consume + * each other's packages via the NPM registry. + */ + exemptDecoupledDependenciesBetweenSubspaces?: boolean; + + /** + * If true, when running on macOS, Rush will omit AppleDouble files (`._*`) from build cache archives + * when a companion file exists in the same directory. AppleDouble files are automatically created by + * macOS to store extended attributes on filesystems that don't support them, and should generally not + * be included in the shared build cache. + */ + omitAppleDoubleFilesFromBuildCache?: boolean; + + /** + * If true, `rush change --verify` will perform additional validation of change files. Specifically, + * it will report errors if change files reference projects that do not exist in the Rush configuration, + * or if change files target a project that belongs to a lockstepped version policy but is not the + * policy's main project. + */ + strictChangefileValidation?: boolean; + + /** + * If true, the build cache will use file-based APIs to transfer cache entries to and from cloud + * storage. This avoids loading the entire cache entry into memory, which can prevent out-of-memory + * errors for large build outputs and allow cache entries to exceed the limit of a single Buffer. + * The cloud cache provider plugin must implement the optional file-based methods for this to take + * effect; otherwise it falls back to the buffer-based approach. + */ + useDirectFileTransfersForBuildCache?: boolean; } +const _EXPERIMENTS_JSON_SCHEMA: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); + /** * Use this class to load the "common/config/rush/experiments.json" config file. * This file allows repo maintainers to enable and disable experimental Rush features. * @public */ export class ExperimentsConfiguration { - private static _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); - - private _jsonFileName: string; - /** * Get the experiments configuration. * @beta @@ -84,14 +172,27 @@ export class ExperimentsConfiguration { /** * @internal */ - public constructor(jsonFileName: string) { - this._jsonFileName = jsonFileName; - this.configuration = {}; + public constructor(jsonFilePath: string) { + try { + this.configuration = JsonFile.loadAndValidate(jsonFilePath, _EXPERIMENTS_JSON_SCHEMA); + } catch (e) { + if (FileSystem.isNotExistError(e)) { + this.configuration = {}; + } else { + throw e; + } + } - if (!FileSystem.exists(this._jsonFileName)) { - this.configuration = {}; - } else { - this.configuration = JsonFile.loadAndValidate(this._jsonFileName, ExperimentsConfiguration._jsonSchema); + for (const experimentName of Object.getOwnPropertyNames(this.configuration)) { + if (GRADUATED_EXPERIMENTS.has(experimentName)) { + // eslint-disable-next-line no-console + console.log( + Colorize.yellow( + `The experiment "${experimentName}" has graduated to a standard feature. Remove this experiment from ` + + `"${jsonFilePath}".` + ) + ); + } } } } diff --git a/libraries/rush-lib/src/api/FlagFile.ts b/libraries/rush-lib/src/api/FlagFile.ts new file mode 100644 index 00000000000..535029943c9 --- /dev/null +++ b/libraries/rush-lib/src/api/FlagFile.ts @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { FileSystem, JsonFile, type JsonObject, Objects } from '@rushstack/node-core-library'; + +/** + * A base class for flag file. + * @internal + */ +export class FlagFile { + /** + * Flag file path + */ + public readonly path: string; + + /** + * Content of the flag + */ + protected _state: TState; + + /** + * Creates a new flag file + * @param folderPath - the folder that this flag is managing + * @param state - optional, the state that should be managed or compared + */ + public constructor(folderPath: string, flagName: string, initialState: TState) { + this.path = `${folderPath}/${flagName}.flag`; + this._state = initialState; + } + + /** + * Returns true if the file exists and the contents match the current state. + */ + public async isValidAsync(): Promise { + let oldState: JsonObject | undefined; + try { + oldState = await JsonFile.loadAsync(this.path); + const newState: JsonObject = this._state; + return Objects.areDeepEqual(oldState, newState); + } catch (err) { + return false; + } + } + + /** + * Writes the flag file to disk with the current state + */ + public async createAsync(): Promise { + await JsonFile.saveAsync(this._state, this.path, { + ensureFolderExists: true + }); + } + + /** + * Removes the flag file + */ + public async clearAsync(): Promise { + await FileSystem.deleteFileAsync(this.path); + } +} diff --git a/libraries/rush-lib/src/api/LastInstallFlag.ts b/libraries/rush-lib/src/api/LastInstallFlag.ts index ab453f55041..a0af3df9dd8 100644 --- a/libraries/rush-lib/src/api/LastInstallFlag.ts +++ b/libraries/rush-lib/src/api/LastInstallFlag.ts @@ -1,22 +1,72 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import { pnpmSyncGetJsonVersion } from 'pnpm-sync-lib'; -import { FileSystem, JsonFile, JsonObject, Import, Path } from '@rushstack/node-core-library'; +import { JsonFile, type JsonObject, Path, type IPackageJson, Objects } from '@rushstack/node-core-library'; -import { PackageManagerName } from './packageManager/PackageManager'; -import { RushConfiguration } from './RushConfiguration'; +import type { PackageManagerName } from './packageManager/PackageManager'; +import type { RushConfiguration } from './RushConfiguration'; +import * as objectUtilities from '../utilities/objectUtilities'; +import type { Subspace } from './Subspace'; +import { Selection } from '../logic/Selection'; +import { FlagFile } from './FlagFile'; -const lodash: typeof import('lodash') = Import.lazy('lodash', require); - -export const LAST_INSTALL_FLAG_FILE_NAME: string = 'last-install.flag'; +const LAST_INSTALL_FLAG_FILE_NAME: string = 'last-install'; /** - * @internal + * This represents the JSON data structure for the "last-install.flag" file. */ -export interface ILockfileValidityCheckOptions { - statePropertiesToIgnore?: string[]; +export interface ILastInstallFlagJson { + /** + * Current node version + */ + node?: string; + /** + * Current package manager name + */ + packageManager?: PackageManagerName; + /** + * Current package manager version + */ + packageManagerVersion: string; + /** + * Current rush json folder + */ + rushJsonFolder: string; + /** + * The content of package.json, used in the flag file of autoinstaller + */ + packageJson?: IPackageJson; + /** + * Same with pnpmOptions.pnpmStorePath in rush.json + */ + storePath?: string; + /** + * An experimental flag used by cleanInstallAfterNpmrcChanges + */ + npmrcHash?: string; + /** + * True when "useWorkspaces" is true in rush.json + */ + workspaces?: boolean; + /** + * True when user explicitly specify "--ignore-scripts" CLI parameter or deferredInstallationScripts + */ + ignoreScripts?: boolean; + /** + * When specified, it is a list of selected projects during partial install + * It is undefined when full install + */ + selectedProjectNames?: string[]; + /** + * pnpm-sync-lib version + */ + pnpmSync?: string; +} + +interface ILockfileValidityCheckOptions { + statePropertiesToIgnore?: (keyof ILastInstallFlagJson)[]; rushVerb?: string; } @@ -25,31 +75,22 @@ export interface ILockfileValidityCheckOptions { * indicate that something installed in the folder was successfully completed. * It also compares state, so that if something like the Node.js version has changed, * it can invalidate the last install. - * @internal */ -export class LastInstallFlag { - private _state: JsonObject; - - /** - * Returns the full path to the flag file - */ - public readonly path: string; - +export class LastInstallFlag extends FlagFile> { /** * Creates a new LastInstall flag * @param folderPath - the folder that this flag is managing * @param state - optional, the state that should be managed or compared */ - public constructor(folderPath: string, state: JsonObject = {}) { - this.path = path.join(folderPath, this.flagName); - this._state = state; + public constructor(folderPath: string, state?: Partial) { + super(folderPath, LAST_INSTALL_FLAG_FILE_NAME, state || {}); } /** * Returns true if the file exists and the contents match the current state. */ - public isValid(options?: ILockfileValidityCheckOptions): boolean { - return this._isValid(false, options); + public override async isValidAsync(): Promise { + return await this._isValidAsync(false, {}); } /** @@ -58,30 +99,24 @@ export class LastInstallFlag { * * @internal */ - public checkValidAndReportStoreIssues( + public async checkValidAndReportStoreIssuesAsync( options: ILockfileValidityCheckOptions & { rushVerb: string } - ): boolean { - return this._isValid(true, options); + ): Promise { + return this._isValidAsync(true, options); } - private _isValid(checkValidAndReportStoreIssues: false, options?: ILockfileValidityCheckOptions): boolean; - private _isValid( - checkValidAndReportStoreIssues: true, - options: ILockfileValidityCheckOptions & { rushVerb: string } - ): boolean; - private _isValid( + private async _isValidAsync( checkValidAndReportStoreIssues: boolean, { rushVerb = 'update', statePropertiesToIgnore }: ILockfileValidityCheckOptions = {} - ): boolean { + ): Promise { let oldState: JsonObject; try { - oldState = JsonFile.load(this.path); + oldState = await JsonFile.loadAsync(this.path); } catch (err) { return false; } - const newState: JsonObject = { ...this._state }; - + const newState: ILastInstallFlagJson = { ...this._state } as ILastInstallFlagJson; if (statePropertiesToIgnore) { for (const optionToIgnore of statePropertiesToIgnore) { delete newState[optionToIgnore]; @@ -89,7 +124,7 @@ export class LastInstallFlag { } } - if (!lodash.isEqual(oldState, newState)) { + if (!Objects.areDeepEqual(oldState, newState)) { if (checkValidAndReportStoreIssues) { const pkgManager: PackageManagerName = newState.packageManager as PackageManagerName; if (pkgManager === 'pnpm') { @@ -115,6 +150,19 @@ export class LastInstallFlag { ); } } + // check whether new selected projects are installed + if (newState.selectedProjectNames) { + if (!oldState.selectedProjectNames) { + // used to be a full install + return true; + } else if ( + Selection.union(newState.selectedProjectNames, oldState.selectedProjectNames).size === + oldState.selectedProjectNames.length + ) { + // current selected projects are included in old selected projects + return true; + } + } } } return false; @@ -124,62 +172,45 @@ export class LastInstallFlag { } /** - * Writes the flag file to disk with the current state - */ - public create(): void { - JsonFile.save(this._state, this.path, { - ensureFolderExists: true - }); - } - - /** - * Removes the flag file + * Merge new data into current state by "merge" */ - public clear(): void { - FileSystem.deleteFile(this.path); - } - - /** - * Returns the name of the flag file - */ - protected get flagName(): string { - return LAST_INSTALL_FLAG_FILE_NAME; + public mergeFromObject(data: JsonObject): void { + if (objectUtilities.isMatch(this._state, data)) { + return; + } + objectUtilities.merge(this._state, data); } } /** - * A helper class for LastInstallFlag + * Gets the LastInstall flag and sets the current state. This state is used to compare + * against the last-known-good state tracked by the LastInstall flag. + * @param rushConfiguration - the configuration of the Rush repo to get the install + * state from * * @internal */ -export class LastInstallFlagFactory { - /** - * Gets the LastInstall flag and sets the current state. This state is used to compare - * against the last-known-good state tracked by the LastInstall flag. - * @param rushConfiguration - the configuration of the Rush repo to get the install - * state from - * - * @internal - */ - public static getCommonTempFlag( - rushConfiguration: RushConfiguration, - extraState: Record = {} - ): LastInstallFlag { - const currentState: JsonObject = { - node: process.versions.node, - packageManager: rushConfiguration.packageManager, - packageManagerVersion: rushConfiguration.packageManagerToolVersion, - rushJsonFolder: rushConfiguration.rushJsonFolder, - ...extraState - }; - - if (currentState.packageManager === 'pnpm' && rushConfiguration.pnpmOptions) { - currentState.storePath = rushConfiguration.pnpmOptions.pnpmStorePath; - if (rushConfiguration.pnpmOptions.useWorkspaces) { - currentState.workspaces = rushConfiguration.pnpmOptions.useWorkspaces; - } +export function getCommonTempFlag( + rushConfiguration: RushConfiguration, + subspace: Subspace, + extraState: Record = {} +): LastInstallFlag { + const currentState: ILastInstallFlagJson = { + node: process.versions.node, + packageManager: rushConfiguration.packageManager, + packageManagerVersion: rushConfiguration.packageManagerToolVersion, + rushJsonFolder: rushConfiguration.rushJsonFolder, + ignoreScripts: false, + pnpmSync: pnpmSyncGetJsonVersion(), + ...extraState + }; + + if (currentState.packageManager === 'pnpm' && rushConfiguration.pnpmOptions) { + currentState.storePath = rushConfiguration.pnpmOptions.pnpmStorePath; + if (rushConfiguration.pnpmOptions.useWorkspaces) { + currentState.workspaces = rushConfiguration.pnpmOptions.useWorkspaces; } - - return new LastInstallFlag(rushConfiguration.commonTempFolder, currentState); } + + return new LastInstallFlag(subspace.getSubspaceTempFolderPath(), currentState); } diff --git a/libraries/rush-lib/src/api/LastLinkFlag.ts b/libraries/rush-lib/src/api/LastLinkFlag.ts deleted file mode 100644 index bcac4008469..00000000000 --- a/libraries/rush-lib/src/api/LastLinkFlag.ts +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { LastInstallFlag } from './LastInstallFlag'; -import { JsonObject, JsonFile, InternalError } from '@rushstack/node-core-library'; -import { RushConfiguration } from './RushConfiguration'; - -export const LAST_LINK_FLAG_FILE_NAME: string = 'last-link.flag'; - -/** - * A helper class for managing the last-link flag, which is persistent and - * indicates that linking was completed successfully. - * @internal - */ -export class LastLinkFlag extends LastInstallFlag { - /** - * @override - */ - public isValid(): boolean { - let oldState: JsonObject | undefined; - try { - oldState = JsonFile.load(this.path); - } catch (err) { - // Swallow error - } - return !!oldState; - } - - /** - * @override - */ - public checkValidAndReportStoreIssues(): boolean { - throw new InternalError('Not implemented'); - } - - /** - * Returns the name of the flag file - * - * @override - */ - protected get flagName(): string { - return LAST_LINK_FLAG_FILE_NAME; - } -} - -/** - * A helper class for LastLinkFlag - * - * @internal - */ -export class LastLinkFlagFactory { - /** - * Gets the LastLink flag and sets the current state. This state is used to compare - * against the last-known-good state tracked by the LastLink flag. - * @param rushConfiguration - the configuration of the Rush repo to get the install - * state from - * - * @internal - */ - public static getCommonTempFlag(rushConfiguration: RushConfiguration): LastLinkFlag { - return new LastLinkFlag(rushConfiguration.commonTempFolder, {}); - } -} diff --git a/libraries/rush-lib/src/api/PackageJsonEditor.ts b/libraries/rush-lib/src/api/PackageJsonEditor.ts index 7a153ce6299..c9baeb73e0f 100644 --- a/libraries/rush-lib/src/api/PackageJsonEditor.ts +++ b/libraries/rush-lib/src/api/PackageJsonEditor.ts @@ -2,9 +2,10 @@ // See LICENSE in the project root for license information. import * as semver from 'semver'; -import { Import, InternalError, IPackageJson, JsonFile, Sort } from '@rushstack/node-core-library'; -const lodash: typeof import('lodash') = Import.lazy('lodash', require); +import { InternalError, type IPackageJson, JsonFile, Sort, JsonSyntax } from '@rushstack/node-core-library'; + +import { cloneDeep } from '../utilities/objectUtilities'; /** * @public @@ -47,6 +48,26 @@ export class PackageJsonDependency { } } +/** + * @public + */ +export class PackageJsonDependencyMeta { + private _injected: boolean; + private _onChange: () => void; + + public readonly name: string; + + public constructor(name: string, injected: boolean, onChange: () => void) { + this.name = name; + this._injected = injected; + this._onChange = onChange; + } + + public get injected(): boolean { + return this._injected; + } +} + /** * @public */ @@ -58,6 +79,8 @@ export class PackageJsonEditor { // and "peerDependencies" are mutually exclusive, but "devDependencies" is not. private readonly _devDependencies: Map; + private readonly _dependenciesMeta: Map; + // NOTE: The "resolutions" field is a yarn specific feature that controls package // resolution override within yarn. private readonly _resolutions: Map; @@ -74,87 +97,89 @@ export class PackageJsonEditor { this._sourceData = data; this._modified = false; - this._dependencies = new Map(); - this._devDependencies = new Map(); - this._resolutions = new Map(); - - const dependencies: { [key: string]: string } = data.dependencies || {}; - const optionalDependencies: { [key: string]: string } = data.optionalDependencies || {}; - const peerDependencies: { [key: string]: string } = data.peerDependencies || {}; - - const devDependencies: { [key: string]: string } = data.devDependencies || {}; - const resolutions: { [key: string]: string } = data.resolutions || {}; + const { + dependencies = {}, + optionalDependencies = {}, + peerDependencies = {}, + devDependencies = {}, + resolutions = {}, + dependenciesMeta = {} + } = data; const _onChange: () => void = this._onChange.bind(this); + const optionalDependenciesSet: Set = new Set(Object.keys(optionalDependencies)); + const peerDependenciesSet: Set = new Set(Object.keys(peerDependencies)); try { - Object.keys(dependencies || {}).forEach((packageName: string) => { - if (Object.prototype.hasOwnProperty.call(optionalDependencies, packageName)) { - throw new Error( - `The package "${packageName}" cannot be listed in both ` + - `"dependencies" and "optionalDependencies"` - ); - } - if (Object.prototype.hasOwnProperty.call(peerDependencies, packageName)) { - throw new Error( - `The package "${packageName}" cannot be listed in both "dependencies" and "peerDependencies"` - ); - } + const dependenciesMapEntries: [string, PackageJsonDependency][] = Object.entries(dependencies).map( + ([packageName, version]: [string, string]) => { + if (optionalDependenciesSet.has(packageName)) { + throw new Error( + `The package "${packageName}" cannot be listed in both ` + + `"dependencies" and "optionalDependencies"` + ); + } + if (peerDependenciesSet.has(packageName)) { + throw new Error( + `The package "${packageName}" cannot be listed in both "dependencies" and "peerDependencies"` + ); + } - this._dependencies.set( - packageName, - new PackageJsonDependency(packageName, dependencies[packageName], DependencyType.Regular, _onChange) - ); - }); + return [ + packageName, + new PackageJsonDependency(packageName, version, DependencyType.Regular, _onChange) + ]; + } + ); - Object.keys(optionalDependencies || {}).forEach((packageName: string) => { - if (Object.prototype.hasOwnProperty.call(peerDependencies, packageName)) { + const optionalDependenciesMapEntries: [string, PackageJsonDependency][] = Object.entries( + optionalDependencies + ).map(([packageName, version]) => { + if (peerDependenciesSet.has(packageName)) { throw new Error( `The package "${packageName}" cannot be listed in both ` + `"optionalDependencies" and "peerDependencies"` ); } - this._dependencies.set( + return [ packageName, - new PackageJsonDependency( - packageName, - optionalDependencies[packageName], - DependencyType.Optional, - _onChange - ) - ); + new PackageJsonDependency(packageName, version, DependencyType.Optional, _onChange) + ]; }); - Object.keys(peerDependencies || {}).forEach((packageName: string) => { - this._dependencies.set( + const peerDependenciesMapEntries: [string, PackageJsonDependency][] = Object.entries( + peerDependencies + ).map(([packageName, version]) => [ + packageName, + new PackageJsonDependency(packageName, version, DependencyType.Peer, _onChange) + ]); + + this._dependencies = new Map([ + ...dependenciesMapEntries, + ...optionalDependenciesMapEntries, + ...peerDependenciesMapEntries + ]); + + this._devDependencies = new Map( + Object.entries(devDependencies).map(([packageName, version]) => [ packageName, - new PackageJsonDependency( - packageName, - peerDependencies[packageName], - DependencyType.Peer, - _onChange - ) - ); - }); + new PackageJsonDependency(packageName, version, DependencyType.Dev, _onChange) + ]) + ); - Object.keys(devDependencies || {}).forEach((packageName: string) => { - this._devDependencies.set( + this._resolutions = new Map( + Object.entries(resolutions).map(([packageName, version]) => [ packageName, - new PackageJsonDependency(packageName, devDependencies[packageName], DependencyType.Dev, _onChange) - ); - }); + new PackageJsonDependency(packageName, version, DependencyType.YarnResolutions, _onChange) + ]) + ); - Object.keys(resolutions || {}).forEach((packageName: string) => { - this._resolutions.set( + this._dependenciesMeta = new Map( + Object.entries(dependenciesMeta).map(([packageName, { injected = false }]) => [ packageName, - new PackageJsonDependency( - packageName, - resolutions[packageName], - DependencyType.YarnResolutions, - _onChange - ) - ); - }); + new PackageJsonDependencyMeta(packageName, injected, _onChange) + ]) + ); // (Do not sort this._resolutions because order may be significant; the RFC is unclear about that.) Sort.sortMapKeys(this._dependencies); @@ -164,8 +189,17 @@ export class PackageJsonEditor { } } + /** + * @deprecated Use {@link PackageJsonEditor.loadAsync} method instead. + */ public static load(filePath: string): PackageJsonEditor { - return new PackageJsonEditor(filePath, JsonFile.load(filePath)); + const packageJson: IPackageJson = JsonFile.load(filePath); + return new PackageJsonEditor(filePath, packageJson); + } + + public static async loadAsync(filePath: string): Promise { + const packageJson: IPackageJson = await JsonFile.loadAsync(filePath); + return new PackageJsonEditor(filePath, packageJson); } public static fromObject(object: IPackageJson, filename: string): PackageJsonEditor { @@ -194,6 +228,13 @@ export class PackageJsonEditor { return [...this._devDependencies.values()]; } + /** + * The list of dependenciesMeta in package.json. + */ + public get dependencyMetaList(): ReadonlyArray { + return [...this._dependenciesMeta.values()]; + } + /** * This field is a Yarn-specific feature that allows overriding of package resolution. * @@ -230,17 +271,24 @@ export class PackageJsonEditor { switch (dependencyType) { case DependencyType.Regular: case DependencyType.Optional: - case DependencyType.Peer: + case DependencyType.Peer: { this._dependencies.set(packageName, dependency); break; - case DependencyType.Dev: + } + + case DependencyType.Dev: { this._devDependencies.set(packageName, dependency); break; - case DependencyType.YarnResolutions: + } + + case DependencyType.YarnResolutions: { this._resolutions.set(packageName, dependency); break; - default: + } + + default: { throw new InternalError('Unsupported DependencyType'); + } } this._modified = true; @@ -250,29 +298,57 @@ export class PackageJsonEditor { switch (dependencyType) { case DependencyType.Regular: case DependencyType.Optional: - case DependencyType.Peer: + case DependencyType.Peer: { this._dependencies.delete(packageName); break; - case DependencyType.Dev: + } + + case DependencyType.Dev: { this._devDependencies.delete(packageName); break; - case DependencyType.YarnResolutions: + } + + case DependencyType.YarnResolutions: { this._resolutions.delete(packageName); break; - default: + } + + default: { throw new InternalError('Unsupported DependencyType'); + } } this._modified = true; } + /** + * @deprecated Use {@link PackageJsonEditor.saveIfModifiedAsync} method instead. + */ public saveIfModified(): boolean { if (this._modified) { this._modified = false; this._sourceData = this._normalize(this._sourceData); - JsonFile.save(this._sourceData, this.filePath, { updateExistingFile: true }); + JsonFile.save(this._sourceData, this.filePath, { + updateExistingFile: true, + jsonSyntax: JsonSyntax.Strict + }); return true; } + + return false; + } + + public async saveIfModifiedAsync(): Promise { + if (this._modified) { + this._modified = false; + this._sourceData = this._normalize(this._sourceData); + await JsonFile.saveAsync(this._sourceData, this.filePath, { + updateExistingFile: true, + jsonSyntax: JsonSyntax.Strict + }); + return true; + } + return false; } @@ -286,7 +362,7 @@ export class PackageJsonEditor { // Only normalize if we need to const sourceData: IPackageJson = this._modified ? this._normalize(this._sourceData) : this._sourceData; // Provide a clone to avoid reference back to the original data object - return lodash.cloneDeep(sourceData); + return cloneDeep(sourceData); } private _onChange(): void { @@ -310,53 +386,64 @@ export class PackageJsonEditor { const keys: string[] = [...this._dependencies.keys()].sort(); for (const packageName of keys) { - const dependency: PackageJsonDependency = this._dependencies.get(packageName)!; + const { dependencyType, name, version }: PackageJsonDependency = this._dependencies.get(packageName)!; - switch (dependency.dependencyType) { - case DependencyType.Regular: + switch (dependencyType) { + case DependencyType.Regular: { if (!normalizedData.dependencies) { normalizedData.dependencies = {}; } - normalizedData.dependencies[dependency.name] = dependency.version; + + normalizedData.dependencies[name] = version; break; - case DependencyType.Optional: + } + + case DependencyType.Optional: { if (!normalizedData.optionalDependencies) { normalizedData.optionalDependencies = {}; } - normalizedData.optionalDependencies[dependency.name] = dependency.version; + + normalizedData.optionalDependencies[name] = version; break; - case DependencyType.Peer: + } + + case DependencyType.Peer: { if (!normalizedData.peerDependencies) { normalizedData.peerDependencies = {}; } - normalizedData.peerDependencies[dependency.name] = dependency.version; + + normalizedData.peerDependencies[name] = version; break; + } + case DependencyType.Dev: // uses this._devDependencies instead case DependencyType.YarnResolutions: // uses this._resolutions instead - default: + default: { throw new InternalError('Unsupported DependencyType'); + } } } const devDependenciesKeys: string[] = [...this._devDependencies.keys()].sort(); - for (const packageName of devDependenciesKeys) { - const dependency: PackageJsonDependency = this._devDependencies.get(packageName)!; + const { name, version }: PackageJsonDependency = this._devDependencies.get(packageName)!; if (!normalizedData.devDependencies) { normalizedData.devDependencies = {}; } - normalizedData.devDependencies[dependency.name] = dependency.version; + + normalizedData.devDependencies[name] = version; } // (Do not sort this._resolutions because order may be significant; the RFC is unclear about that.) for (const packageName of this._resolutions.keys()) { - const dependency: PackageJsonDependency = this._resolutions.get(packageName)!; + const { name, version }: PackageJsonDependency = this._resolutions.get(packageName)!; if (!normalizedData.resolutions) { normalizedData.resolutions = {}; } - normalizedData.resolutions[dependency.name] = dependency.version; + + normalizedData.resolutions[name] = version; } return normalizedData; diff --git a/libraries/rush-lib/src/api/Rush.ts b/libraries/rush-lib/src/api/Rush.ts index 3fcdc1cd221..64e06354047 100644 --- a/libraries/rush-lib/src/api/Rush.ts +++ b/libraries/rush-lib/src/api/Rush.ts @@ -1,14 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; -import { - InternalError, - IPackageJson, - ITerminalProvider, - PackageJsonLookup -} from '@rushstack/node-core-library'; +import { InternalError, type IPackageJson, PackageJsonLookup } from '@rushstack/node-core-library'; +import type { ITerminalProvider } from '@rushstack/terminal'; import '../utilities/SetRushLibPath'; @@ -17,8 +13,9 @@ import { RushStartupBanner } from '../cli/RushStartupBanner'; import { RushXCommandLine } from '../cli/RushXCommandLine'; import { CommandLineMigrationAdvisor } from '../cli/CommandLineMigrationAdvisor'; import { EnvironmentVariableNames } from './EnvironmentConfiguration'; -import { IBuiltInPluginConfiguration } from '../pluginFramework/PluginLoader/BuiltInPluginLoader'; +import type { IBuiltInPluginConfiguration } from '../pluginFramework/PluginLoader/BuiltInPluginLoader'; import { RushPnpmCommandLine } from '../cli/RushPnpmCommandLine'; +import { measureAsyncFn } from '../utilities/performance'; /** * Options to pass to the rush "launch" functions. @@ -40,27 +37,34 @@ export interface ILaunchOptions { alreadyReportedNodeTooNewError?: boolean; /** - * Used to specify Rush plugins that are dependencies of the "\@microsoft/rush" package. + * Pass along the terminal provider from the CLI version selector. * - * @internal + * @privateRemarks + * We should remove this. The version selector package can be very old. It's unwise for + * `rush-lib` to rely on a potentially ancient `ITerminalProvider` implementation. */ - builtInPluginConfigurations?: IBuiltInPluginConfiguration[]; + terminalProvider?: ITerminalProvider; /** - * Used to specify terminal how to write a message + * Used only by `@microsoft/rush/lib/start-dev.js` during development. + * Specifies Rush devDependencies of the `@microsoft/rush` to be manually loaded. + * + * @remarks + * Marked as `@internal` because `IBuiltInPluginConfiguration` is internal. + * @internal */ - terminalProvider?: ITerminalProvider; + builtInPluginConfigurations?: IBuiltInPluginConfiguration[]; } +let _rushLibPackageJsonCache: IPackageJson | undefined = undefined; +let _rushLibPackageFolderCache: string | undefined = undefined; + /** * General operations for the Rush engine. * * @public */ export class Rush { - private static __rushLibPackageJson: IPackageJson | undefined = undefined; - private static __rushLibPackageFolder: string | undefined = undefined; - /** * This API is used by the `@microsoft/rush` front end to launch the "rush" command-line. * Third-party tools should not use this API. Instead, they should execute the "rush" binary @@ -72,8 +76,8 @@ export class Rush { * * Even though this API isn't documented, it is still supported for legacy compatibility. */ - public static launch(launcherVersion: string, arg: ILaunchOptions): void { - const options: ILaunchOptions = Rush._normalizeLaunchOptions(arg); + public static launch(launcherVersion: string, options: ILaunchOptions): void { + options = _normalizeLaunchOptions(options); if (!RushCommandLineParser.shouldRestrictConsoleOutput()) { RushStartupBanner.logBanner(Rush.version, options.isManaged); @@ -85,12 +89,14 @@ export class Rush { return; } - Rush._assignRushInvokedFolder(); + _assignRushInvokedFolder(); const parser: RushCommandLineParser = new RushCommandLineParser({ alreadyReportedNodeTooNewError: options.alreadyReportedNodeTooNewError, builtInPluginConfigurations: options.builtInPluginConfigurations }); - parser.execute().catch(console.error); // CommandLineParser.execute() should never reject the promise + // CommandLineParser.executeAsync() should never reject the promise + // eslint-disable-next-line no-console + measureAsyncFn('rush:parser:executeAsync', () => parser.executeAsync()).catch(console.error); } /** @@ -99,10 +105,10 @@ export class Rush { * and start a new Node.js process. */ public static launchRushX(launcherVersion: string, options: ILaunchOptions): void { - options = Rush._normalizeLaunchOptions(options); - - Rush._assignRushInvokedFolder(); - RushXCommandLine._launchRushXInternal(launcherVersion, { ...options }); + options = _normalizeLaunchOptions(options); + _assignRushInvokedFolder(); + // eslint-disable-next-line no-console + RushXCommandLine.launchRushXAsync(launcherVersion, options).catch(console.error); // CommandLineParser.executeAsync() should never reject the promise } /** @@ -111,7 +117,7 @@ export class Rush { * and start a new Node.js process. */ public static launchRushPnpm(launcherVersion: string, options: ILaunchOptions): void { - Rush._assignRushInvokedFolder(); + _assignRushInvokedFolder(); RushPnpmCommandLine.launch(launcherVersion, { ...options }); } @@ -127,25 +133,13 @@ export class Rush { * @internal */ public static get _rushLibPackageJson(): IPackageJson { - Rush._ensureOwnPackageJsonIsLoaded(); - return Rush.__rushLibPackageJson!; + _ensureOwnPackageJsonIsLoaded(); + return _rushLibPackageJsonCache!; } public static get _rushLibPackageFolder(): string { - Rush._ensureOwnPackageJsonIsLoaded(); - return Rush.__rushLibPackageFolder!; - } - - private static _ensureOwnPackageJsonIsLoaded(): void { - if (!Rush.__rushLibPackageJson) { - const packageJsonFilePath: string | undefined = - PackageJsonLookup.instance.tryGetPackageJsonFilePathFor(__dirname); - if (!packageJsonFilePath) { - throw new InternalError('Unable to locate the package.json file for this module'); - } - Rush.__rushLibPackageFolder = path.dirname(packageJsonFilePath); - Rush.__rushLibPackageJson = PackageJsonLookup.instance.loadPackageJson(packageJsonFilePath); - } + _ensureOwnPackageJsonIsLoaded(); + return _rushLibPackageFolderCache!; } /** @@ -159,16 +153,29 @@ export class Rush { * The natural time to do that refactoring is when we rework `Utilities.executeCommand()` to use * `Executable.spawn()` or rushell. */ - private static _assignRushInvokedFolder(): void { - process.env[EnvironmentVariableNames.RUSH_INVOKED_FOLDER] = process.cwd(); - } +} - /** - * This function normalizes legacy options to the current {@link ILaunchOptions} object. - */ - private static _normalizeLaunchOptions(arg: ILaunchOptions): ILaunchOptions { - return typeof arg === 'boolean' - ? { isManaged: arg } // In older versions of Rush, this the `launch` functions took a boolean arg for "isManaged" - : arg; +function _ensureOwnPackageJsonIsLoaded(): void { + if (!_rushLibPackageJsonCache) { + const packageJsonFilePath: string | undefined = + PackageJsonLookup.instance.tryGetPackageJsonFilePathFor(__dirname); + if (!packageJsonFilePath) { + throw new InternalError('Unable to locate the package.json file for this module'); + } + _rushLibPackageFolderCache = path.dirname(packageJsonFilePath); + _rushLibPackageJsonCache = PackageJsonLookup.instance.loadPackageJson(packageJsonFilePath); } } + +function _assignRushInvokedFolder(): void { + process.env[EnvironmentVariableNames.RUSH_INVOKED_FOLDER] = process.cwd(); +} + +/** + * This function normalizes legacy options to the current {@link ILaunchOptions} object. + */ +function _normalizeLaunchOptions(arg: ILaunchOptions): ILaunchOptions { + return typeof arg === 'boolean' + ? { isManaged: arg } // In older versions of Rush, this the `launch` functions took a boolean arg for "isManaged" + : arg; +} diff --git a/libraries/rush-lib/src/api/RushCommandLine.ts b/libraries/rush-lib/src/api/RushCommandLine.ts new file mode 100644 index 00000000000..34de4d115ea --- /dev/null +++ b/libraries/rush-lib/src/api/RushCommandLine.ts @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { CommandLineParameterKind } from '@rushstack/ts-command-line'; + +import { RushCommandLineParser } from '../cli/RushCommandLineParser'; + +/** + * Information about the available parameters associated with a Rush action + * + * @beta + */ +export interface IRushCommandLineParameter { + /** + * The corresponding string representation of CliParameterKind + */ + readonly kind: keyof typeof CommandLineParameterKind; + + /** + * The long name of the flag including double dashes, e.g. "--do-something" + */ + readonly longName: string; + + /** + * An optional short name for the flag including the dash, e.g. "-d" + */ + readonly shortName?: string; + + /** + * Documentation for the parameter that will be shown when invoking the tool with "--help" + */ + readonly description: string; + + /** + * If true, then an error occurs if the parameter was not included on the command-line. + */ + readonly required?: boolean; + + /** + * If provided, this parameter can also be provided by an environment variable with the specified name. + */ + readonly environmentVariable?: string; +} + +/** + * The full spec of an available Rush command line action + * + * @beta + */ +export interface IRushCommandLineAction { + actionName: string; + parameters: IRushCommandLineParameter[]; +} + +/** + * The full spec of a Rush CLI + * + * @beta + */ +export interface IRushCommandLineSpec { + actions: IRushCommandLineAction[]; +} + +const _commandLineSpecByWorkspaceFolder: Map = new Map(); + +/** + * Information about the available CLI commands + * + * @beta + */ +export class RushCommandLine { + public static getCliSpec(rushJsonFolder: string): IRushCommandLineSpec { + let result: IRushCommandLineSpec | undefined = _commandLineSpecByWorkspaceFolder.get(rushJsonFolder); + + if (!result) { + const commandLineParser: RushCommandLineParser = new RushCommandLineParser({ cwd: rushJsonFolder }); + + // extract the set of command line elements from the command line parser + const actions: IRushCommandLineAction[] = []; + for (const { actionName, parameters: rawParameters } of commandLineParser.actions) { + const parameters: IRushCommandLineParameter[] = []; + for (const { + kind: rawKind, + longName, + shortName, + description, + required, + environmentVariable + } of rawParameters) { + parameters.push({ + kind: CommandLineParameterKind[rawKind] as keyof typeof CommandLineParameterKind, + longName, + shortName, + description, + required, + environmentVariable + }); + } + + actions.push({ + actionName, + parameters + }); + } + + result = { actions }; + _commandLineSpecByWorkspaceFolder.set(rushJsonFolder, result); + } + + return result; + } +} diff --git a/libraries/rush-lib/src/api/RushConfiguration.ts b/libraries/rush-lib/src/api/RushConfiguration.ts index ddec0bc65f2..527ec92be93 100644 --- a/libraries/rush-lib/src/api/RushConfiguration.ts +++ b/libraries/rush-lib/src/api/RushConfiguration.ts @@ -3,44 +3,49 @@ /* eslint max-lines: off */ -import * as path from 'path'; +import * as path from 'node:path'; + import * as semver from 'semver'; +import { trueCasePathSync } from 'true-case-path'; + import { JsonFile, JsonSchema, - JsonNull, Path, FileSystem, - PackageNameParser, - FileSystemStats + type PackageNameParser, + type FileSystemStats, + InternalError, + type JsonNull } from '@rushstack/node-core-library'; -import { trueCasePathSync } from 'true-case-path'; +import { LookupByPath } from '@rushstack/lookup-by-path'; -import { Rush } from '../api/Rush'; -import { RushConfigurationProject, IRushConfigurationProjectJson } from './RushConfigurationProject'; +import { Rush } from './Rush'; +import { RushConfigurationProject, type IRushConfigurationProjectJson } from './RushConfigurationProject'; import { RushConstants } from '../logic/RushConstants'; import { ApprovedPackagesPolicy } from './ApprovedPackagesPolicy'; import { EventHooks } from './EventHooks'; import { VersionPolicyConfiguration } from './VersionPolicyConfiguration'; import { EnvironmentConfiguration } from './EnvironmentConfiguration'; -import { CommonVersionsConfiguration } from './CommonVersionsConfiguration'; +import type { CommonVersionsConfiguration } from './CommonVersionsConfiguration'; import { Utilities } from '../utilities/Utilities'; -import { PackageManagerName, PackageManager } from './packageManager/PackageManager'; +import type { PackageManagerName, PackageManager } from './packageManager/PackageManager'; import { NpmPackageManager } from './packageManager/NpmPackageManager'; import { YarnPackageManager } from './packageManager/YarnPackageManager'; import { PnpmPackageManager } from './packageManager/PnpmPackageManager'; import { ExperimentsConfiguration } from './ExperimentsConfiguration'; import { PackageNameParsers } from './PackageNameParsers'; -import { RepoStateFile } from '../logic/RepoStateFile'; -import { LookupByPath } from '../logic/LookupByPath'; +import type { RepoStateFile } from '../logic/RepoStateFile'; import { RushPluginsConfiguration } from './RushPluginsConfiguration'; -import { IPnpmOptionsJson, PnpmOptionsConfiguration } from '../logic/pnpm/PnpmOptionsConfiguration'; -import { INpmOptionsJson, NpmOptionsConfiguration } from '../logic/npm/NpmOptionsConfiguration'; -import { IYarnOptionsJson, YarnOptionsConfiguration } from '../logic/yarn/YarnOptionsConfiguration'; +import { type IPnpmOptionsJson, PnpmOptionsConfiguration } from '../logic/pnpm/PnpmOptionsConfiguration'; +import { type INpmOptionsJson, NpmOptionsConfiguration } from '../logic/npm/NpmOptionsConfiguration'; +import { type IYarnOptionsJson, YarnOptionsConfiguration } from '../logic/yarn/YarnOptionsConfiguration'; import schemaJson from '../schemas/rush.schema.json'; - import type * as DependencyAnalyzerModuleType from '../logic/DependencyAnalyzer'; -import { PackageManagerOptionsConfigurationBase } from '../logic/base/BasePackageManagerOptionsConfiguration'; +import type { PackageManagerOptionsConfigurationBase } from '../logic/base/BasePackageManagerOptionsConfiguration'; +import { CustomTipsConfiguration } from './CustomTipsConfiguration'; +import { SubspacesConfiguration } from './SubspacesConfiguration'; +import { Subspace } from './Subspace'; const MINIMUM_SUPPORTED_RUSH_JSON_VERSION: string = '0.0.0'; const DEFAULT_BRANCH: string = 'main'; @@ -57,15 +62,19 @@ const knownRushConfigFilenames: string[] = [ RushConstants.artifactoryFilename, RushConstants.browserApprovedPackagesFilename, RushConstants.buildCacheFilename, + RushConstants.cobuildFilename, RushConstants.commandLineFilename, RushConstants.commonVersionsFilename, + RushConstants.customTipsFilename, RushConstants.experimentsFilename, RushConstants.nonbrowserApprovedPackagesFilename, RushConstants.pinnedVersionsFilename, RushConstants.repoStateFilename, RushConstants.versionPoliciesFilename, RushConstants.rushPluginsConfigFilename, - RushConstants.pnpmConfigFilename + RushConstants.pnpmConfigFilename, + RushConstants.subspacesConfigFilename, + RushConstants.rushAlertsConfigFilename ]; /** @@ -159,6 +168,7 @@ export interface IRushConfigurationJson { nodeSupportedVersionRange?: string; nodeSupportedVersionInstructions?: string; suppressNodeLtsWarning?: boolean; + suppressRushIsPublicVersionCheck?: boolean; projectFolderMinDepth?: number; projectFolderMaxDepth?: number; allowMostlyStandardPackageNames?: boolean; @@ -183,6 +193,16 @@ export interface ICurrentVariantJson { variant: string | JsonNull; } +/** + * The filter parameters to search from all projects + */ +export interface IRushConfigurationProjectsFilter { + /** + * A string representation of the subspace to filter for + */ + subspace: string; +} + /** * Options for `RushConfiguration.tryFindRushJsonLocation`. * @public @@ -194,22 +214,26 @@ export interface ITryFindRushJsonLocationOptions { showVerbose?: boolean; // Defaults to false (inverse of old `verbose` parameter) /** - * The folder path where the search will start. Defaults tot he current working directory. + * The folder path where the search will start. Defaults to the current working directory. */ startingFolder?: string; // Defaults to cwd } +const _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); + /** * This represents the Rush configuration for a repository, based on the "rush.json" * configuration file. * @public */ export class RushConfiguration { - private static _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); - - private _variants: Set; private readonly _pathTrees: Map>; + /** + * @internal + */ + public _currentVariantJsonLoadingPromise: Promise | undefined; + // Lazily loaded when the projects() getter is called. private _projects: RushConfigurationProject[] | undefined; @@ -219,14 +243,20 @@ export class RushConfiguration { // Lazily loaded when the projectsByTag() getter is called. private _projectsByTag: ReadonlyMap> | undefined; - // variant -> common-versions configuration - private _commonVersionsConfigurationsByVariant: Map | undefined; + // subspaceName -> subspace + private readonly _subspacesByName: Map; + private readonly _subspaces: Subspace[] = []; /** * The name of the package manager being used to install dependencies */ public readonly packageManager!: PackageManagerName; + /** + * If true, the repository is using PNPM as its package manager. + */ + public readonly isPnpm!: boolean; + /** * {@inheritdoc PackageManager} * @@ -326,41 +356,32 @@ export class RushConfiguration { public readonly shrinkwrapFilename: string; /** - * The full path of the temporary shrinkwrap file that is used during "rush install". - * This file may get rewritten by the package manager during installation. - * @remarks - * This property merely reports the filename; the file itself may not actually exist. - * Example: `C:\MyRepo\common\temp\npm-shrinkwrap.json` or `C:\MyRepo\common\temp\pnpm-lock.yaml` + * The object that specifies subspace configurations if they are provided in the rush workspace. + * @beta */ - public readonly tempShrinkwrapFilename: string; + public readonly subspacesConfiguration: SubspacesConfiguration | undefined; /** - * The full path of a backup copy of tempShrinkwrapFilename. This backup copy is made - * before installation begins, and can be compared to determine how the package manager - * modified tempShrinkwrapFilename. - * @remarks - * This property merely reports the filename; the file itself may not actually exist. - * Example: `C:\MyRepo\common\temp\npm-shrinkwrap-preinstall.json` - * or `C:\MyRepo\common\temp\pnpm-lock-preinstall.yaml` + * Returns true if subspaces.json is present with "subspacesEnabled=true". */ - public readonly tempShrinkwrapPreinstallFilename: string; + public readonly subspacesFeatureEnabled: boolean; /** * The filename of the variant dependency data file. By default this is - * called 'current-variant.json' resides in the Rush common folder. + * called 'current-variant.json' and resides in the Rush common folder. * Its data structure is defined by ICurrentVariantJson. * * Example: `C:\MyRepo\common\temp\current-variant.json` */ - public readonly currentVariantJsonFilename: string; + public readonly currentVariantJsonFilePath: string; /** - * The version of the locally installed NPM tool. (Example: "1.2.3") + * The version of the locally package manager tool. (Example: "1.2.3") */ public readonly packageManagerToolVersion: string; /** - * The absolute path to the locally installed NPM tool. If "rush install" has not + * The absolute path to the locally package manager tool. If "rush install" has not * been run, then this file may not exist yet. * Example: `C:\MyRepo\common\temp\npm-local\node_modules\.bin\npm` */ @@ -479,9 +500,20 @@ export class RushConfiguration { */ public readonly suppressNodeLtsWarning: boolean; + /** + * The raw value of `ensureConsistentVersions` from the `rush.json` file. + * + * @internal + */ + public readonly _ensureConsistentVersionsJsonValue: boolean | undefined; + /** * If true, then consistent version specifiers for dependencies will be enforced. * I.e. "rush check" is run before some commands. + * + * @deprecated + * This setting was moved from `rush.json` to `common-versions.json`. + * Read it using {@link Subspace.shouldEnsureConsistentVersions} instead. */ public readonly ensureConsistentVersions: boolean; @@ -535,6 +567,18 @@ export class RushConfiguration { */ public readonly versionPolicyConfigurationFilePath: string; + /** + * Accesses the custom-tips.json configuration. + * @beta + */ + public readonly customTipsConfiguration: CustomTipsConfiguration; + + /** + * The absolute path to the custom tips configuration file. + * @beta + */ + public readonly customTipsConfigurationFilePath: string; + /** * This configuration object contains settings repo maintainers have specified to enable * and disable experimental Rush features. @@ -548,6 +592,13 @@ export class RushConfiguration { */ public readonly _rushPluginsConfiguration: RushPluginsConfiguration; + /** + * The variants specified in the rush.json configuration file. + * + * @beta + */ + public readonly variants: ReadonlySet; + /** * Use RushConfiguration.loadFromConfigurationFile() or Use RushConfiguration.loadFromDefaultLocation() * instead. @@ -560,13 +611,13 @@ export class RushConfiguration { if (!semver.validRange(rushConfigurationJson.nodeSupportedVersionRange)) { throw new Error( 'Error parsing the node-semver expression in the "nodeSupportedVersionRange"' + - ` field from rush.json: "${rushConfigurationJson.nodeSupportedVersionRange}"` + ` field from ${RushConstants.rushJsonFilename}: "${rushConfigurationJson.nodeSupportedVersionRange}"` ); } if (!semver.satisfies(process.version, rushConfigurationJson.nodeSupportedVersionRange)) { let message: string = `Your dev environment is running Node.js version ${process.version} which does` + - ` not meet the requirements for building this repository. (The rush.json configuration` + + ` not meet the requirements for building this repository. (The ${RushConstants.rushJsonFilename} configuration` + ` requires nodeSupportedVersionRange="${rushConfigurationJson.nodeSupportedVersionRange}")`; if (rushConfigurationJson.nodeSupportedVersionInstructions) { @@ -574,6 +625,7 @@ export class RushConfiguration { } if (EnvironmentConfiguration.allowUnsupportedNodeVersion) { + // eslint-disable-next-line no-console console.warn(message); } else { throw new Error(message); @@ -600,12 +652,19 @@ export class RushConfiguration { this.changesFolder = path.join(this.commonFolder, RushConstants.changeFilesFolderName); - this.currentVariantJsonFilename = path.join(this.commonTempFolder, 'current-variant.json'); + this.currentVariantJsonFilePath = path.join(this.commonTempFolder, RushConstants.currentVariantsFilename); this.suppressNodeLtsWarning = !!rushConfigurationJson.suppressNodeLtsWarning; + this._ensureConsistentVersionsJsonValue = rushConfigurationJson.ensureConsistentVersions; this.ensureConsistentVersions = !!rushConfigurationJson.ensureConsistentVersions; + // Try getting a subspace configuration + this.subspacesConfiguration = SubspacesConfiguration.tryLoadFromDefaultLocation(this); + this.subspacesFeatureEnabled = !!this.subspacesConfiguration?.subspacesEnabled; + + this._subspacesByName = new Map(); + const experimentsConfigFile: string = path.join( this.commonRushConfigFolder, RushConstants.experimentsFilename @@ -628,7 +687,7 @@ export class RushConfiguration { if (rushConfigurationJson.pnpmOptions) { throw new Error( 'Because the new config file "common/config/rush/pnpm-config.json" is being used, ' + - 'you must remove the old setting "pnpmOptions" from rush.json' + `you must remove the old setting "pnpmOptions" from ${RushConstants.rushJsonFilename}` ); } } catch (error) { @@ -645,16 +704,20 @@ export class RushConfiguration { // TODO: Add an actual "packageManager" field in rush.json const packageManagerFields: string[] = []; + this.isPnpm = false; if (rushConfigurationJson.npmVersion) { this.packageManager = 'npm'; this.packageManagerOptions = this.npmOptions; packageManagerFields.push('npmVersion'); } + if (rushConfigurationJson.pnpmVersion) { this.packageManager = 'pnpm'; + this.isPnpm = true; this.packageManagerOptions = this.pnpmOptions; packageManagerFields.push('pnpmVersion'); } + if (rushConfigurationJson.yarnVersion) { this.packageManager = 'yarn'; this.packageManagerOptions = this.yarnOptions; @@ -663,13 +726,13 @@ export class RushConfiguration { if (packageManagerFields.length === 0) { throw new Error( - `The rush.json configuration must specify one of: npmVersion, pnpmVersion, or yarnVersion` + `The ${RushConstants.rushJsonFilename} configuration must specify one of: npmVersion, pnpmVersion, or yarnVersion` ); } if (packageManagerFields.length > 1) { throw new Error( - `The rush.json configuration cannot specify both ${packageManagerFields[0]}` + + `The ${RushConstants.rushJsonFilename} configuration cannot specify both ${packageManagerFields[0]}` + ` and ${packageManagerFields[1]} ` ); } @@ -687,7 +750,6 @@ export class RushConfiguration { this.shrinkwrapFilename = this.packageManagerWrapper.shrinkwrapFilename; - this.tempShrinkwrapFilename = path.join(this.commonTempFolder, this.shrinkwrapFilename); this.packageManagerToolFilename = path.resolve( path.join( this.commonTempFolder, @@ -698,17 +760,11 @@ export class RushConfiguration { ) ); - /// From "C:\repo\common\temp\pnpm-lock.yaml" --> "C:\repo\common\temp\pnpm-lock-preinstall.yaml" - const parsedPath: path.ParsedPath = path.parse(this.tempShrinkwrapFilename); - this.tempShrinkwrapPreinstallFilename = path.join( - parsedPath.dir, - parsedPath.name + '-preinstall' + parsedPath.ext - ); - - RushConfiguration._validateCommonRushConfigFolder( + _validateCommonRushConfigFolder( this.commonRushConfigFolder, this.packageManagerWrapper, - this.experimentsConfiguration + this.experimentsConfiguration, + this.subspacesFeatureEnabled ); this.projectFolderMinDepth = @@ -746,7 +802,7 @@ export class RushConfiguration { if (this.gitSampleEmail.trim().length < 1) { throw new Error( - 'The rush.json file is missing the "sampleEmail" option, ' + + `The ${RushConstants.rushJsonFilename} file is missing the "sampleEmail" option, ` + 'which is required when using "allowedEmailRegExps"' ); } @@ -805,28 +861,66 @@ export class RushConfiguration { ); this.versionPolicyConfiguration = new VersionPolicyConfiguration(this.versionPolicyConfigurationFilePath); - this._variants = new Set(); - - if (rushConfigurationJson.variants) { - for (const variantOptions of rushConfigurationJson.variants) { - const { variantName } = variantOptions; + this.customTipsConfigurationFilePath = path.join( + this.commonRushConfigFolder, + RushConstants.customTipsFilename + ); + this.customTipsConfiguration = new CustomTipsConfiguration(this.customTipsConfigurationFilePath); - if (this._variants.has(variantName)) { - throw new Error(`Duplicate variant named '${variantName}' specified in configuration.`); - } + const variants: Set = new Set(); + for (const variantOptions of rushConfigurationJson.variants ?? []) { + const { variantName } = variantOptions; - this._variants.add(variantName); + if (variants.has(variantName)) { + throw new Error(`Duplicate variant named '${variantName}' specified in configuration.`); } + + variants.add(variantName); } + this.variants = variants; + this._pathTrees = new Map(); } private _initializeAndValidateLocalProjects(): void { this._projects = []; this._projectsByName = new Map(); + this._subspacesByName.clear(); + this._subspaces.length = 0; + + // Build the subspaces map + const subspaceNames: string[] = []; + let splitWorkspaceCompatibility: boolean = false; + if (this.subspacesConfiguration?.subspacesEnabled) { + splitWorkspaceCompatibility = this.subspacesConfiguration.splitWorkspaceCompatibility; + + subspaceNames.push(...this.subspacesConfiguration.subspaceNames); + } + if (subspaceNames.indexOf(RushConstants.defaultSubspaceName) < 0) { + subspaceNames.push(RushConstants.defaultSubspaceName); + } + + // Sort the subspaces in alphabetical order. This ensures that they are processed + // in a deterministic order by the various Rush algorithms. + subspaceNames.sort(); + for (const subspaceName of subspaceNames) { + const subspace: Subspace = new Subspace({ + subspaceName, + rushConfiguration: this, + splitWorkspaceCompatibility + }); + this._subspacesByName.set(subspaceName, subspace); + this._subspaces.push(subspace); + } + const defaultSubspace: Subspace | undefined = this._subspacesByName.get( + RushConstants.defaultSubspaceName + ); + if (!defaultSubspace) { + throw new InternalError('The default subspace was not created'); + } - // We sort the projects array in alphabetical order. This ensures that the packages + // Sort the projects array in alphabetical order. This ensures that the packages // are processed in a deterministic order by the various Rush algorithms. const sortedProjectJsons: IRushConfigurationProjectJson[] = this.rushConfigurationJson.projects.slice(0); sortedProjectJsons.sort((a: IRushConfigurationProjectJson, b: IRushConfigurationProjectJson) => @@ -839,22 +933,38 @@ export class RushConfiguration { const usedTempNames: Set = new Set(); for (let i: number = 0, len: number = sortedProjectJsons.length; i < len; i++) { const projectJson: IRushConfigurationProjectJson = sortedProjectJsons[i]; - const tempProjectName: string | undefined = RushConfiguration._generateTempNameForProject( - projectJson, - usedTempNames - ); + const tempProjectName: string | undefined = _generateTempNameForProject(projectJson, usedTempNames); + + let subspace: Subspace | undefined = undefined; + if (this.subspacesFeatureEnabled) { + if (projectJson.subspaceName) { + subspace = this._subspacesByName.get(projectJson.subspaceName); + if (subspace === undefined) { + throw new Error( + `The project "${projectJson.packageName}" in ${RushConstants.rushJsonFilename} references` + + ` a nonexistent subspace "${projectJson.subspaceName}"` + ); + } + } + } + if (subspace === undefined) { + subspace = defaultSubspace; + } + const project: RushConfigurationProject = new RushConfigurationProject({ projectJson, rushConfiguration: this, tempProjectName, - allowedProjectTags + allowedProjectTags, + subspace }); + subspace._addProject(project); this._projects.push(project); if (this._projectsByName.has(project.packageName)) { throw new Error( `The project name "${project.packageName}" was specified more than once` + - ` in the rush.json configuration file.` + ` in the ${RushConstants.rushJsonFilename} configuration file.` ); } this._projectsByName.set(project.packageName, project); @@ -864,7 +974,7 @@ export class RushConfiguration { project.decoupledLocalDependencies.forEach((decoupledLocalDependency: string) => { if (!this.getProjectByName(decoupledLocalDependency)) { throw new Error( - `In rush.json, the "${decoupledLocalDependency}" project does not exist,` + + `In ${RushConstants.rushJsonFilename}, the "${decoupledLocalDependency}" project does not exist,` + ` but was referenced by the decoupledLocalDependencies (previously cyclicDependencyProjects) for ${project.packageName}` ); } @@ -930,7 +1040,7 @@ export class RushConfiguration { } } - RushConfiguration._jsonSchema.validateObject(rushConfigurationJson, resolvedRushJsonFilename); + _jsonSchema.validateObject(rushConfigurationJson, resolvedRushJsonFilename); return new RushConfiguration(rushConfigurationJson, resolvedRushJsonFilename); } @@ -965,133 +1075,33 @@ export class RushConfiguration { const optionsIn: ITryFindRushJsonLocationOptions = options || {}; const verbose: boolean = optionsIn.showVerbose || false; let currentFolder: string = optionsIn.startingFolder || process.cwd(); + let parentFolder: string = path.dirname(currentFolder); - // Look upwards at parent folders until we find a folder containing rush.json - for (let i: number = 0; i < 10; ++i) { - const rushJsonFilename: string = path.join(currentFolder, 'rush.json'); - + // look upwards at parent folders until we find a folder containing rush.json, + // or we reach the root directory without finding a rush.json file + while (parentFolder && parentFolder !== currentFolder) { + const rushJsonFilename: string = path.join(currentFolder, RushConstants.rushJsonFilename); if (FileSystem.exists(rushJsonFilename)) { - if (i > 0 && verbose) { + if (currentFolder !== optionsIn.startingFolder && verbose) { + // eslint-disable-next-line no-console console.log('Found configuration in ' + rushJsonFilename); } if (verbose) { + // eslint-disable-next-line no-console console.log(''); } return rushJsonFilename; } - - const parentFolder: string = path.dirname(currentFolder); - if (parentFolder === currentFolder) { - break; - } - currentFolder = parentFolder; + parentFolder = path.dirname(currentFolder); } + // no match return undefined; } - /** - * This generates the unique names that are used to create temporary projects - * in the Rush common folder. - * NOTE: sortedProjectJsons is sorted by the caller. - */ - private static _generateTempNameForProject( - projectJson: IRushConfigurationProjectJson, - usedTempNames: Set - ): string { - // If the name is "@ms/MyProject", extract the "MyProject" part - const unscopedName: string = PackageNameParsers.permissive.getUnscopedName(projectJson.packageName); - - // Generate a unique like name "@rush-temp/MyProject", or "@rush-temp/MyProject-2" if - // there is a naming conflict - let counter: number = 0; - let tempProjectName: string = `${RushConstants.rushTempNpmScope}/${unscopedName}`; - while (usedTempNames.has(tempProjectName)) { - ++counter; - tempProjectName = `${RushConstants.rushTempNpmScope}/${unscopedName}-${counter}`; - } - usedTempNames.add(tempProjectName); - - return tempProjectName; - } - - /** - * If someone adds a config file in the "common/rush/config" folder, it would be a bad - * experience for Rush to silently ignore their file simply because they misspelled the - * filename, or maybe it's an old format that's no longer supported. The - * _validateCommonRushConfigFolder() function makes sure that this folder only contains - * recognized config files. - */ - private static _validateCommonRushConfigFolder( - commonRushConfigFolder: string, - packageManagerWrapper: PackageManager, - experiments: ExperimentsConfiguration - ): void { - if (!FileSystem.exists(commonRushConfigFolder)) { - console.log(`Creating folder: ${commonRushConfigFolder}`); - FileSystem.ensureFolder(commonRushConfigFolder); - return; - } - - for (const filename of FileSystem.readFolderItemNames(commonRushConfigFolder)) { - // Ignore things that aren't actual files - const stat: FileSystemStats = FileSystem.getLinkStatistics(path.join(commonRushConfigFolder, filename)); - if (!stat.isFile() && !stat.isSymbolicLink()) { - continue; - } - - // Ignore harmless file extensions - const fileExtension: string = path.extname(filename); - if (['.bak', '.disabled', '.md', '.old', '.orig'].indexOf(fileExtension) >= 0) { - continue; - } - - // Ignore hidden files such as ".DS_Store" - if (filename.startsWith('.')) { - continue; - } - - if (filename.startsWith('deploy-') && fileExtension === '.json') { - // Ignore "rush deploy" files, which use the naming pattern "deploy-.json". - continue; - } - - const knownSet: Set = new Set(knownRushConfigFilenames.map((x) => x.toUpperCase())); - - // Add the shrinkwrap filename for the package manager to the known set. - knownSet.add(packageManagerWrapper.shrinkwrapFilename.toUpperCase()); - - // If the package manager is pnpm, then also add the pnpm file to the known set. - if (packageManagerWrapper.packageManager === 'pnpm') { - knownSet.add((packageManagerWrapper as PnpmPackageManager).pnpmfileFilename.toUpperCase()); - } - - // Is the filename something we know? If not, report an error. - if (!knownSet.has(filename.toUpperCase())) { - throw new Error( - `An unrecognized file "${filename}" was found in the Rush config folder:` + - ` ${commonRushConfigFolder}` - ); - } - } - - const pinnedVersionsFilename: string = path.join( - commonRushConfigFolder, - RushConstants.pinnedVersionsFilename - ); - if (FileSystem.exists(pinnedVersionsFilename)) { - throw new Error( - 'The "pinned-versions.json" config file is no longer supported;' + - ' please move your settings to the "preferredVersions" field of a "common-versions.json" config file.' + - ` (See the ${RushConstants.rushWebSiteUrl} documentation for details.)\n\n` + - pinnedVersionsFilename - ); - } - } - /** * The fully resolved path for the "autoinstallers" folder. * Example: `C:\MyRepo\common\autoinstallers` @@ -1109,17 +1119,42 @@ export class RushConfiguration { } /** - * The full path of the shrinkwrap file that is tracked by Git. (The "rush install" - * command uses a temporary copy, whose path is tempShrinkwrapFilename.) + * The full path of the temporary shrinkwrap file that is used during "rush install". + * This file may get rewritten by the package manager during installation. * @remarks * This property merely reports the filename; the file itself may not actually exist. - * Example: `C:\MyRepo\common\npm-shrinkwrap.json` or `C:\MyRepo\common\pnpm-lock.yaml` + * Example: `C:\MyRepo\common\temp\npm-shrinkwrap.json` or `C:\MyRepo\common\temp\pnpm-lock.yaml` * - * @deprecated Use `getCommittedShrinkwrapFilename` instead, which gets the correct common - * shrinkwrap file name for a given active variant. + * @deprecated Introduced with subspaces is subspace specific tempShrinkwrapFilename accessible from the Subspace class. */ - public get committedShrinkwrapFilename(): string { - return this.getCommittedShrinkwrapFilename(); + public get tempShrinkwrapFilename(): string { + if (this.subspacesFeatureEnabled) { + throw new Error( + 'tempShrinkwrapFilename() is not available when using subspaces. Use the subspace specific temp shrinkwrap filename.' + ); + } + return path.join(this.commonTempFolder, this.shrinkwrapFilename); + } + + /** + * The full path of a backup copy of tempShrinkwrapFilename. This backup copy is made + * before installation begins, and can be compared to determine how the package manager + * modified tempShrinkwrapFilename. + * @remarks + * This property merely reports the filename; the file itself may not actually exist. + * Example: `C:\MyRepo\common\temp\npm-shrinkwrap-preinstall.json` + * or `C:\MyRepo\common\temp\pnpm-lock-preinstall.yaml` + * + * @deprecated Introduced with subspaces is subspace specific tempShrinkwrapPreinstallFilename accessible from the Subspace class. + */ + public get tempShrinkwrapPreinstallFilename(): string { + if (this.subspacesFeatureEnabled) { + throw new Error( + 'tempShrinkwrapPreinstallFilename() is not available when using subspaces. Use the subspace specific temp shrinkwrap preinstall filename.' + ); + } + const parsedPath: path.ParsedPath = path.parse(this.tempShrinkwrapFilename); + return path.join(parsedPath.dir, parsedPath.name + '-preinstall' + parsedPath.ext); } /** @@ -1128,13 +1163,7 @@ export class RushConfiguration { * package manager. */ public get shrinkwrapFilePhrase(): string { - if (this.packageManager === 'yarn') { - // Eventually we'd like to be consistent with Yarn's terminology of calling this a "lock file", - // but a lot of Rush documentation uses "shrinkwrap" file and would all need to be updated. - return 'shrinkwrap file (yarn.lock)'; - } else { - return 'shrinkwrap file'; - } + return `shrinkwrap file (${this.shrinkwrapFilename})`; } /** @@ -1169,7 +1198,82 @@ export class RushConfiguration { return this._projects!; } - public get projectsByName(): Map { + /** + * @beta + */ + public get defaultSubspace(): Subspace { + // TODO: Enable the default subspace to be obtained without initializing the full set of all projects + if (!this._projects) { + this._initializeAndValidateLocalProjects(); + } + const defaultSubspace: Subspace | undefined = this.tryGetSubspace(RushConstants.defaultSubspaceName); + if (!defaultSubspace) { + throw new InternalError('Default subspace was not created'); + } + return defaultSubspace; + } + + /** + * A list of all the available subspaces in this workspace. + * @beta + */ + public get subspaces(): readonly Subspace[] { + if (!this._projects) { + this._initializeAndValidateLocalProjects(); + } + return this._subspaces; + } + + /** + * @beta + */ + public tryGetSubspace(subspaceName: string): Subspace | undefined { + if (!this._projects) { + this._initializeAndValidateLocalProjects(); + } + const subspace: Subspace | undefined = this._subspacesByName.get(subspaceName); + if (!subspace) { + // If the name is not even valid, that is more important information than if the subspace doesn't exist + SubspacesConfiguration.requireValidSubspaceName( + subspaceName, + this.subspacesConfiguration?.splitWorkspaceCompatibility + ); + } + return subspace; + } + + /** + * @beta + */ + public getSubspace(subspaceName: string): Subspace { + const subspace: Subspace | undefined = this.tryGetSubspace(subspaceName); + if (!subspace) { + throw new Error(`The specified subspace "${subspaceName}" does not exist`); + } + return subspace; + } + + /** + * Returns the set of subspaces that the given projects belong to + * @beta + */ + public getSubspacesForProjects(projects: Iterable): ReadonlySet { + if (!this._projects) { + this._initializeAndValidateLocalProjects(); + } + + const subspaceSet: Set = new Set(); + for (const project of projects) { + subspaceSet.add(project.subspace); + } + + return subspaceSet; + } + + /** + * @beta + */ + public get projectsByName(): ReadonlyMap { if (!this._projectsByName) { this._initializeAndValidateLocalProjects(); } @@ -1209,7 +1313,7 @@ export class RushConfiguration { * for a given active variant. */ public get commonVersions(): CommonVersionsConfiguration { - return this.getCommonVersions(); + return this.defaultSubspace.getCommonVersions(undefined); } /** @@ -1218,128 +1322,73 @@ export class RushConfiguration { * determines which variant, if any, was last specified when performing "rush install" * or "rush update". */ - public get currentInstalledVariant(): string | undefined { - let variant: string | undefined; - - if (FileSystem.exists(this.currentVariantJsonFilename)) { - const currentVariantJson: ICurrentVariantJson = JsonFile.load(this.currentVariantJsonFilename); - - variant = currentVariantJson.variant || undefined; + public async getCurrentlyInstalledVariantAsync(): Promise { + if (!this._currentVariantJsonLoadingPromise) { + this._currentVariantJsonLoadingPromise = this._loadCurrentVariantJsonAsync(); } - return variant; + return (await this._currentVariantJsonLoadingPromise)?.variant ?? undefined; } /** - * Gets the path to the common-versions.json config file for a specific variant. - * @param variant - The name of the current variant in use by the active command. + * @deprecated Use {@link Subspace.getCommonVersionsFilePath} instead */ - public getCommonVersionsFilePath(variant?: string | undefined): string { - const commonVersionsFilename: string = path.join( - this.commonRushConfigFolder, - ...(variant ? [RushConstants.rushVariantsFolderName, variant] : []), - RushConstants.commonVersionsFilename - ); - return commonVersionsFilename; + public getCommonVersionsFilePath(subspace?: Subspace, variant?: string): string { + return (subspace ?? this.defaultSubspace).getCommonVersionsFilePath(variant); } /** - * Gets the settings from the common-versions.json config file for a specific variant. - * @param variant - The name of the current variant in use by the active command. + * @deprecated Use {@link Subspace.getCommonVersions} instead */ - public getCommonVersions(variant?: string | undefined): CommonVersionsConfiguration { - if (!this._commonVersionsConfigurationsByVariant) { - this._commonVersionsConfigurationsByVariant = new Map(); - } - - // Use an empty string as the key when no variant provided. Anything else would possibly conflict - // with a variant created by the user - const variantKey: string = variant || ''; - let commonVersionsConfiguration: CommonVersionsConfiguration | undefined = - this._commonVersionsConfigurationsByVariant.get(variantKey); - if (!commonVersionsConfiguration) { - const commonVersionsFilename: string = this.getCommonVersionsFilePath(variant); - commonVersionsConfiguration = CommonVersionsConfiguration.loadFromFile(commonVersionsFilename); - this._commonVersionsConfigurationsByVariant.set(variantKey, commonVersionsConfiguration); - } - - return commonVersionsConfiguration; + public getCommonVersions(subspace?: Subspace, variant?: string): CommonVersionsConfiguration { + return (subspace ?? this.defaultSubspace).getCommonVersions(variant); } /** * Returns a map of all direct dependencies that only have a single semantic version specifier. + * + * @param subspace - The subspace to use * @param variant - The name of the current variant in use by the active command. * * @returns A map of dependency name --\> version specifier for implicitly preferred versions. */ - public getImplicitlyPreferredVersions(variant?: string | undefined): Map { + public getImplicitlyPreferredVersions(subspace?: Subspace, variant?: string): Map { // TODO: During the next major release of Rush, replace this `require` call with a dynamic import, and // change this function to be async. const DependencyAnalyzerModule: typeof DependencyAnalyzerModuleType = require('../logic/DependencyAnalyzer'); const dependencyAnalyzer: DependencyAnalyzerModuleType.DependencyAnalyzer = DependencyAnalyzerModule.DependencyAnalyzer.forRushConfiguration(this); const dependencyAnalysis: DependencyAnalyzerModuleType.IDependencyAnalysis = - dependencyAnalyzer.getAnalysis(variant); + dependencyAnalyzer.getAnalysis(subspace, variant, false); return dependencyAnalysis.implicitlyPreferredVersionByPackageName; } /** - * Gets the path to the repo-state.json file for a specific variant. - * @param variant - The name of the current variant in use by the active command. + * @deprecated Use {@link Subspace.getRepoStateFilePath} instead */ - public getRepoStateFilePath(variant?: string | undefined): string { - const repoStateFilename: string = path.join( - this.commonRushConfigFolder, - ...(variant ? [RushConstants.rushVariantsFolderName, variant] : []), - RushConstants.repoStateFilename - ); - return repoStateFilename; + public getRepoStateFilePath(subspace?: Subspace): string { + return (subspace ?? this.defaultSubspace).getRepoStateFilePath(); } /** - * Gets the contents from the repo-state.json file for a specific variant. - * @param variant - The name of the current variant in use by the active command. + * @deprecated Use {@link Subspace.getRepoState} instead */ - public getRepoState(variant?: string | undefined): RepoStateFile { - const repoStateFilename: string = this.getRepoStateFilePath(variant); - return RepoStateFile.loadFromFile(repoStateFilename, variant); + public getRepoState(subspace?: Subspace): RepoStateFile { + return (subspace ?? this.defaultSubspace).getRepoState(); } /** - * Gets the committed shrinkwrap file name for a specific variant. - * @param variant - The name of the current variant in use by the active command. + * @deprecated Use {@link Subspace.getCommittedShrinkwrapFilePath} instead */ - public getCommittedShrinkwrapFilename(variant?: string | undefined): string { - if (variant) { - if (!this._variants.has(variant)) { - throw new Error( - `Invalid variant name '${variant}'. The provided variant parameter needs to be ` + - `one of the following from rush.json: ` + - `${Array.from(this._variants.values()) - .map((name: string) => `"${name}"`) - .join(', ')}.` - ); - } - } - - const variantConfigFolderPath: string = this._getVariantConfigFolderPath(variant); - - return path.join(variantConfigFolderPath, this.shrinkwrapFilename); + public getCommittedShrinkwrapFilename(subspace?: Subspace, variant?: string): string { + return (subspace ?? this.defaultSubspace).getCommittedShrinkwrapFilePath(variant); } /** - * Gets the absolute path for "pnpmfile.js" for a specific variant. - * @param variant - The name of the current variant in use by the active command. - * @remarks - * The file path is returned even if PNPM is not configured as the package manager. + * @deprecated Use {@link Subspace.getPnpmfilePath} instead */ - public getPnpmfilePath(variant?: string | undefined): string { - const variantConfigFolderPath: string = this._getVariantConfigFolderPath(variant); - - return path.join( - variantConfigFolderPath, - (this.packageManagerWrapper as PnpmPackageManager).pnpmfileFilename - ); + public getPnpmfilePath(subspace?: Subspace, variant?: string): string { + return (subspace ?? this.defaultSubspace).getPnpmfilePath(variant); } /** @@ -1425,22 +1474,125 @@ export class RushConfiguration { return undefined; } - private _getVariantConfigFolderPath(variant?: string | undefined): string { - if (variant) { - if (!this._variants.has(variant)) { + private async _loadCurrentVariantJsonAsync(): Promise { + try { + return await JsonFile.loadAsync(this.currentVariantJsonFilePath); + } catch (e) { + if (!FileSystem.isNotExistError(e)) { + throw e; + } + } + } +} + +/** + * This generates the unique names that are used to create temporary projects + * in the Rush common folder. + * NOTE: sortedProjectJsons is sorted by the caller. + */ +function _generateTempNameForProject( + projectJson: IRushConfigurationProjectJson, + usedTempNames: Set +): string { + // If the name is "@ms/MyProject", extract the "MyProject" part + const unscopedName: string = PackageNameParsers.permissive.getUnscopedName(projectJson.packageName); + + // Generate a unique like name "@rush-temp/MyProject", or "@rush-temp/MyProject-2" if + // there is a naming conflict + let counter: number = 0; + let tempProjectName: string = `${RushConstants.rushTempNpmScope}/${unscopedName}`; + while (usedTempNames.has(tempProjectName)) { + ++counter; + tempProjectName = `${RushConstants.rushTempNpmScope}/${unscopedName}-${counter}`; + } + usedTempNames.add(tempProjectName); + + return tempProjectName; +} + +/** + * If someone adds a config file in the "common/rush/config" folder, it would be a bad + * experience for Rush to silently ignore their file simply because they misspelled the + * filename, or maybe it's an old format that's no longer supported. The + * _validateCommonRushConfigFolder() function makes sure that this folder only contains + * recognized config files. + */ +function _validateCommonRushConfigFolder( + commonRushConfigFolder: string, + packageManagerWrapper: PackageManager, + experiments: ExperimentsConfiguration, + subspacesFeatureEnabled: boolean +): void { + if (!FileSystem.exists(commonRushConfigFolder)) { + // eslint-disable-next-line no-console + console.log(`Creating folder: ${commonRushConfigFolder}`); + FileSystem.ensureFolder(commonRushConfigFolder); + return; + } + + for (const filename of FileSystem.readFolderItemNames(commonRushConfigFolder)) { + // Ignore things that aren't actual files + const stat: FileSystemStats = FileSystem.getLinkStatistics(path.join(commonRushConfigFolder, filename)); + if (!stat.isFile() && !stat.isSymbolicLink()) { + continue; + } + + // Ignore harmless file extensions + const fileExtension: string = path.extname(filename); + if (['.bak', '.disabled', '.md', '.old', '.orig'].indexOf(fileExtension) >= 0) { + continue; + } + + // Check if there are prohibited files when subspaces is enabled + if (subspacesFeatureEnabled) { + if (filename === RushConstants.pnpmfileV6Filename || filename === RushConstants.pnpmfileV1Filename) { throw new Error( - `Invalid variant name '${variant}'. The provided variant parameter needs to be ` + - `one of the following from rush.json: ` + - `${Array.from(this._variants.values()) - .map((name: string) => `"${name}"`) - .join(', ')}.` + 'When the subspaces feature is enabled, a separate lockfile is stored in each subspace folder. ' + + `To avoid confusion, remove this file: ${commonRushConfigFolder}/${filename}` ); } } - return path.join( - this.commonRushConfigFolder, - ...(variant ? [RushConstants.rushVariantsFolderName, variant] : []) + // Ignore hidden files such as ".DS_Store" + if (filename.startsWith('.')) { + continue; + } + + if (filename.startsWith('deploy-') && fileExtension === '.json') { + // Ignore "rush deploy" files, which use the naming pattern "deploy-.json". + continue; + } + + const knownSet: Set = new Set(knownRushConfigFilenames.map((x) => x.toUpperCase())); + + // Add the shrinkwrap filename for the package manager to the known set. + knownSet.add(packageManagerWrapper.shrinkwrapFilename.toUpperCase()); + + // If the package manager is pnpm, then also add the pnpm file to the known set. + if (packageManagerWrapper.packageManager === 'pnpm') { + const pnpmPackageManager: PnpmPackageManager = packageManagerWrapper as PnpmPackageManager; + knownSet.add(pnpmPackageManager.pnpmfileFilename.toUpperCase()); + } + + // Is the filename something we know? If not, report an error. + if (!knownSet.has(filename.toUpperCase())) { + throw new Error( + `An unrecognized file "${filename}" was found in the Rush config folder:` + + ` ${commonRushConfigFolder}` + ); + } + } + + const pinnedVersionsFilename: string = path.join( + commonRushConfigFolder, + RushConstants.pinnedVersionsFilename + ); + if (FileSystem.exists(pinnedVersionsFilename)) { + throw new Error( + 'The "pinned-versions.json" config file is no longer supported;' + + ' please move your settings to the "preferredVersions" field of a "common-versions.json" config file.' + + ` (See the ${RushConstants.rushWebSiteUrl} documentation for details.)\n\n` + + pinnedVersionsFilename ); } } diff --git a/libraries/rush-lib/src/api/RushConfigurationProject.ts b/libraries/rush-lib/src/api/RushConfigurationProject.ts index bb3ed09dbfe..e80dce4bbde 100644 --- a/libraries/rush-lib/src/api/RushConfigurationProject.ts +++ b/libraries/rush-lib/src/api/RushConfigurationProject.ts @@ -1,17 +1,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import * as semver from 'semver'; -import { IPackageJson, FileSystem, FileConstants } from '@rushstack/node-core-library'; -import { RushConfiguration } from '../api/RushConfiguration'; -import { VersionPolicy, LockStepVersionPolicy } from './VersionPolicy'; +import { type IPackageJson, FileSystem, FileConstants } from '@rushstack/node-core-library'; + +import type { RushConfiguration } from './RushConfiguration'; +import type { VersionPolicy, LockStepVersionPolicy } from './VersionPolicy'; import type { PackageJsonEditor } from './PackageJsonEditor'; import { RushConstants } from '../logic/RushConstants'; import { PackageNameParsers } from './PackageNameParsers'; import { DependencySpecifier, DependencySpecifierType } from '../logic/DependencySpecifier'; import { SaveCallbackPackageJsonEditor } from './SaveCallbackPackageJsonEditor'; +import type { Subspace } from './Subspace'; /** * This represents the JSON data object for a project entry in the rush.json configuration file. @@ -27,6 +30,7 @@ export interface IRushConfigurationProjectJson { skipRushCheck?: boolean; publishFolder?: string; tags?: string[]; + subspaceName?: string; } /** @@ -49,6 +53,11 @@ export interface IRushConfigurationProjectOptions { * If specified, validate project tags against this list. */ allowedProjectTags: Set | undefined; + + /** + * The containing subspace. + */ + subspace: Subspace; } /** @@ -105,6 +114,12 @@ export class RushConfigurationProject { */ public readonly rushConfiguration: RushConfiguration; + /** + * Returns the subspace name that a project belongs to. + * If subspaces is not enabled, returns the default subspace. + */ + public readonly subspace: Subspace; + /** * The review category name, or undefined if no category was assigned. * This name must be one of the valid choices listed in RushConfiguration.reviewCategories. @@ -184,32 +199,45 @@ export class RushConfigurationProject { */ public readonly tags: ReadonlySet; + /** + * Returns the subspace name specified in the `"subspaceName"` field in `rush.json`. + * Note that this field may be undefined, if the `default` subspace is being used, + * and this field may be ignored if the subspaces feature is disabled. + * + * @beta + */ + public readonly configuredSubspaceName: string | undefined; + /** @internal */ public constructor(options: IRushConfigurationProjectOptions) { const { projectJson, rushConfiguration, tempProjectName, allowedProjectTags } = options; + const { packageName, projectFolder: projectRelativeFolder } = projectJson; this.rushConfiguration = rushConfiguration; - this.packageName = projectJson.packageName; - this.projectRelativeFolder = projectJson.projectFolder; + this.packageName = packageName; + this.projectRelativeFolder = projectRelativeFolder; + + validateRelativePathField(projectRelativeFolder, 'projectFolder', rushConfiguration.rushJsonFile); // For example, the depth of "a/b/c" would be 3. The depth of "a" is 1. - const projectFolderDepth: number = projectJson.projectFolder.split('/').length; + const projectFolderDepth: number = projectRelativeFolder.split('/').length; if (projectFolderDepth < rushConfiguration.projectFolderMinDepth) { throw new Error( `To keep things organized, this repository has a projectFolderMinDepth policy` + ` requiring project folders to be at least ${rushConfiguration.projectFolderMinDepth} levels deep.` + - ` Problem folder: "${projectJson.projectFolder}"` + ` Problem folder: "${projectRelativeFolder}"` ); } if (projectFolderDepth > rushConfiguration.projectFolderMaxDepth) { throw new Error( `To keep things organized, this repository has a projectFolderMaxDepth policy` + ` preventing project folders from being deeper than ${rushConfiguration.projectFolderMaxDepth} levels.` + - ` Problem folder: "${projectJson.projectFolder}"` + ` Problem folder: "${projectRelativeFolder}"` ); } - this.projectFolder = path.join(rushConfiguration.rushJsonFolder, projectJson.projectFolder); - const packageJsonFilename: string = path.join(this.projectFolder, FileConstants.PackageJson); + const absoluteProjectFolder: string = path.join(rushConfiguration.rushJsonFolder, projectRelativeFolder); + this.projectFolder = absoluteProjectFolder; + const packageJsonFilename: string = path.join(absoluteProjectFolder, FileConstants.PackageJson); try { const packageJsonText: string = FileSystem.readFile(packageJsonFilename); @@ -217,16 +245,19 @@ export class RushConfigurationProject { this._packageJson = JSON.parse(packageJsonText); } catch (error) { if (FileSystem.isNotExistError(error as Error)) { - throw new Error( - `Could not find package.json for ${projectJson.packageName} at ${packageJsonFilename}` - ); + throw new Error(`Could not find package.json for ${packageName} at ${packageJsonFilename}`); + } + + // Encountered an error while loading the package.json file. Please append the error message with the corresponding file location. + if (error instanceof SyntaxError) { + error.message = `${error.message}\nFilename: ${packageJsonFilename}`; } throw error; } - this.projectRushConfigFolder = path.join(this.projectFolder, 'config', 'rush'); + this.projectRushConfigFolder = path.join(absoluteProjectFolder, 'config', 'rush'); this.projectRushTempFolder = path.join( - this.projectFolder, + absoluteProjectFolder, RushConstants.projectRushFolderName, RushConstants.rushTempFolderName ); @@ -237,13 +268,13 @@ export class RushConfigurationProject { // by the reviewCategories array. if (!projectJson.reviewCategory) { throw new Error( - `The "approvedPackagesPolicy" feature is enabled rush.json, but a reviewCategory` + - ` was not specified for the project "${projectJson.packageName}".` + `The "approvedPackagesPolicy" feature is enabled ${RushConstants.rushJsonFilename}, but a reviewCategory` + + ` was not specified for the project "${packageName}".` ); } if (!rushConfiguration.approvedPackagesPolicy.reviewCategories.has(projectJson.reviewCategory)) { throw new Error( - `The project "${projectJson.packageName}" specifies its reviewCategory as` + + `The project "${packageName}" specifies its reviewCategory as` + `"${projectJson.reviewCategory}" which is not one of the defined reviewCategories.` ); } @@ -252,7 +283,7 @@ export class RushConfigurationProject { if (this.packageJson.name !== this.packageName) { throw new Error( - `The package name "${this.packageName}" specified in rush.json does not` + + `The package name "${this.packageName}" specified in ${RushConstants.rushJsonFilename} does not` + ` match the name "${this.packageJson.name}" from package.json` ); } @@ -299,31 +330,37 @@ export class RushConfigurationProject { if (this._shouldPublish && this.packageJson.private) { throw new Error( - `The project "${projectJson.packageName}" specifies "shouldPublish": true, ` + + `The project "${packageName}" specifies "shouldPublish": true, ` + `but the package.json file specifies "private": true.` ); } - this.publishFolder = this.projectFolder; - if (projectJson.publishFolder) { - this.publishFolder = path.join(this.publishFolder, projectJson.publishFolder); + this.publishFolder = absoluteProjectFolder; + const { publishFolder } = projectJson; + if (publishFolder) { + validateRelativePathField(publishFolder, 'publishFolder', rushConfiguration.rushJsonFile); + this.publishFolder = path.join(this.publishFolder, publishFolder); } if (allowedProjectTags && projectJson.tags) { - this.tags = new Set(); + const tags: Set = new Set(); for (const tag of projectJson.tags) { if (!allowedProjectTags.has(tag)) { throw new Error( - `The tag "${tag}" specified for project "${this.packageName}" is not listed in the ` + - `allowedProjectTags field in rush.json.` + `The tag "${tag}" specified for project "${packageName}" is not listed in the ` + + `allowedProjectTags field in ${RushConstants.rushJsonFilename}.` ); } else { - (this.tags as Set).add(tag); + tags.add(tag); } } + this.tags = tags; } else { this.tags = new Set(projectJson.tags); } + + this.configuredSubspaceName = projectJson.subspaceName; + this.subspace = options.subspace; } /** @@ -376,12 +413,17 @@ export class RushConfigurationProject { ]) { if (dependencySet) { for (const [dependency, version] of Object.entries(dependencySet)) { + const dependencySpecifier: DependencySpecifier = DependencySpecifier.parseWithCache( + dependency, + version + ); + const dependencyName: string = + dependencySpecifier.aliasTarget?.packageName ?? dependencySpecifier.packageName; // Skip if we can't find the local project or it's a cyclic dependency const localProject: RushConfigurationProject | undefined = - this.rushConfiguration.getProjectByName(dependency); + this.rushConfiguration.getProjectByName(dependencyName); if (localProject && !this.decoupledLocalDependencies.has(dependency)) { // Set the value if it's a workspace project, or if we have a local project and the semver is satisfied - const dependencySpecifier: DependencySpecifier = new DependencySpecifier(dependency, version); switch (dependencySpecifier.specifierType) { case DependencySpecifierType.Version: case DependencySpecifierType.Range: @@ -478,3 +520,33 @@ export class RushConfigurationProject { return isMain; } } + +export function validateRelativePathField(relativePath: string, field: string, file: string): void { + // path.isAbsolute delegates depending on platform; however, path.posix.isAbsolute('C:/a') returns false, + // while path.win32.isAbsolute('C:/a') returns true. We want consistent validation across platforms. + if (path.posix.isAbsolute(relativePath) || path.win32.isAbsolute(relativePath)) { + throw new Error( + `The value "${relativePath}" in the "${field}" field in "${file}" must be a relative path.` + ); + } + + if (relativePath.includes('\\')) { + throw new Error( + `The value "${relativePath}" in the "${field}" field in "${file}" may not contain backslashes ('\\'), since they are interpreted differently` + + ` on POSIX and Windows. Paths must use '/' as the path separator.` + ); + } + + if (relativePath.endsWith('/')) { + throw new Error( + `The value "${relativePath}" in the "${field}" field in "${file}" may not end with a trailing '/' character.` + ); + } + + const normalized: string = path.posix.normalize(relativePath); + if (relativePath !== normalized) { + throw new Error( + `The value "${relativePath}" in the "${field}" field in "${file}" should be replaced with its normalized form "${normalized}".` + ); + } +} diff --git a/libraries/rush-lib/src/api/RushGlobalFolder.ts b/libraries/rush-lib/src/api/RushGlobalFolder.ts index 1d0541d05bd..18f77f7f938 100644 --- a/libraries/rush-lib/src/api/RushGlobalFolder.ts +++ b/libraries/rush-lib/src/api/RushGlobalFolder.ts @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { Utilities } from '../utilities/Utilities'; +import * as path from 'node:path'; + +import { User } from '@rushstack/node-core-library'; + import { EnvironmentConfiguration } from './EnvironmentConfiguration'; /** @@ -44,7 +46,7 @@ export class RushGlobalFolder { if (rushGlobalFolderOverride !== undefined) { this.path = rushGlobalFolderOverride; } else { - this.path = path.join(Utilities.getHomeFolder(), '.rush'); + this.path = path.join(User.getHomeFolder(), '.rush'); } const normalizedNodeVersion: string = process.version.match(/^[a-z0-9\-\.]+$/i) diff --git a/libraries/rush-lib/src/api/RushInternals.ts b/libraries/rush-lib/src/api/RushInternals.ts index 6abf2e7122e..714a5fd6a63 100644 --- a/libraries/rush-lib/src/api/RushInternals.ts +++ b/libraries/rush-lib/src/api/RushInternals.ts @@ -16,7 +16,7 @@ export class RushInternals { * @returns the module object as would be returned by `require()` */ public static loadModule(srcImportPath: string): unknown { - const libPath: string = `${Rush._rushLibPackageFolder}/lib/${srcImportPath}`; + const libPath: string = `${Rush._rushLibPackageFolder}/lib-commonjs/${srcImportPath}`; try { return require(libPath); } catch (e) { diff --git a/libraries/rush-lib/src/api/RushPluginsConfiguration.ts b/libraries/rush-lib/src/api/RushPluginsConfiguration.ts index f8d194d0ab1..9d3908fb601 100644 --- a/libraries/rush-lib/src/api/RushPluginsConfiguration.ts +++ b/libraries/rush-lib/src/api/RushPluginsConfiguration.ts @@ -21,9 +21,9 @@ interface IRushPluginsConfigurationJson { plugins: IRushPluginConfiguration[]; } -export class RushPluginsConfiguration { - private static _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); +const _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); +export class RushPluginsConfiguration { private _jsonFilename: string; public readonly configuration: Readonly; @@ -35,7 +35,7 @@ export class RushPluginsConfiguration { }; if (FileSystem.exists(this._jsonFilename)) { - this.configuration = JsonFile.loadAndValidate(this._jsonFilename, RushPluginsConfiguration._jsonSchema); + this.configuration = JsonFile.loadAndValidate(this._jsonFilename, _jsonSchema); } } } diff --git a/libraries/rush-lib/src/api/RushProjectConfiguration.ts b/libraries/rush-lib/src/api/RushProjectConfiguration.ts index 0245b8c53a9..50f71ce1785 100644 --- a/libraries/rush-lib/src/api/RushProjectConfiguration.ts +++ b/libraries/rush-lib/src/api/RushProjectConfiguration.ts @@ -1,19 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { AlreadyReportedError, ITerminal, Path } from '@rushstack/node-core-library'; -import { ConfigurationFile, InheritanceType } from '@rushstack/heft-config-file'; +import { AlreadyReportedError, Async, Path } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; +import { ProjectConfigurationFile, InheritanceType } from '@rushstack/heft-config-file'; import { RigConfig } from '@rushstack/rig-package'; -import { RushConfigurationProject } from './RushConfigurationProject'; +import type { RushConfigurationProject } from './RushConfigurationProject'; import { RushConstants } from '../logic/RushConstants'; import type { IPhase } from './CommandLineConfiguration'; import { OverlappingPathAnalyzer } from '../utilities/OverlappingPathAnalyzer'; import schemaJson from '../schemas/rush-project.schema.json'; -import anythingSchemaJson from '../schemas/rush-project.schema.json'; +import anythingSchemaJson from '../schemas/anything.schema.json'; +import { HotlinkManager } from '../utilities/HotlinkManager'; +import type { RushConfiguration } from './RushConfiguration'; /** - * Describes the file structure for the "/config/rush-project.json" config file. + * Describes the file structure for the `/config/rush-project.json` config file. + * @internal */ export interface IRushProjectJson { /** @@ -40,6 +44,48 @@ export interface IRushProjectJson { operationSettings?: IOperationSettings[]; } +/** @alpha */ +export interface IRushPhaseSharding { + /** + * The number of shards to create. + */ + count: number; + + /** + * The format of the argument to pass to the command to indicate the shard index and count. + * + * @defaultValue `--shard={shardIndex}/{shardCount}` + */ + shardArgumentFormat?: string; + + /** + * An optional argument to pass to the command to indicate the output folder for the shard. + * It must end with `{shardIndex}`. + * + * @defaultValue `--shard-output-folder=.rush/operations/{phaseName}/shards/{shardIndex}`. + */ + outputFolderArgumentFormat?: string; + + /** + * @deprecated Create a separate operation settings object for the shard operation settings with the name `{operationName}:shard`. + */ + shardOperationSettings?: unknown; +} + +/** + * The granularity at which the Node.js version is included in the build cache hash. + * + * - `"major"` - includes only the major version (e.g. `18`) + * - `"minor"` - includes the major and minor version (e.g. `18.17`) + * - `"patch"` - includes the full version (e.g. `18.17.1`) + * + * @alpha + */ +export type NodeVersionGranularity = 'major' | 'minor' | 'patch'; + +/** + * @alpha + */ export interface IOperationSettings { /** * The name of the operation. This should be a key in the `package.json`'s `scripts` object. @@ -77,6 +123,20 @@ export interface IOperationSettings { */ dependsOnEnvVars?: string[]; + /** + * Specifies whether and at what granularity the Node.js version should be included in the hash + * used for the build cache. When enabled, changing the Node.js version at the specified granularity + * will invalidate cached outputs and cause the operation to be re-executed. This is useful for + * projects that produce Node.js-version-specific outputs, such as native module builds. + * + * Allowed values: + * - `true` - alias for `"patch"`, includes the full version (e.g. `18.17.1`) + * - `"major"` - includes only the major version (e.g. `18`) + * - `"minor"` - includes the major and minor version (e.g. `18.17`) + * - `"patch"` - includes the full version (e.g. `18.17.1`) + */ + dependsOnNodeVersion?: boolean | NodeVersionGranularity; + /** * An optional list of glob (minimatch) patterns pointing to files that can affect this operation. * The hash values of the contents of these files will become part of the final hash when reading @@ -87,6 +147,38 @@ export interface IOperationSettings { * calculating final hash value when reading and writing the build cache */ dependsOnAdditionalFiles?: string[]; + + /** + * An optional config object for sharding the operation. If specified, the operation will be sharded + * into multiple invocations. The `count` property specifies the number of shards to create. The + * `shardArgumentFormat` property specifies the format of the argument to pass to the command to + * indicate the shard index and count. The default value is `--shard={shardIndex}/{shardCount}`. + */ + sharding?: IRushPhaseSharding; + + /** + * How many concurrency units this operation should take up during execution. The maximum concurrent units is + * determined by the -p flag. + */ + weight?: number | `${number}%`; + + /** + * If true, this operation can use cobuilds for orchestration without restoring build cache entries. + */ + allowCobuildWithoutCache?: boolean; + + /** + * If true, this operation will never be skipped by the `--changed-projects-only` flag. + */ + ignoreChangedProjectsOnlyFlag?: boolean; + + /** + * An optional list of custom command-line parameter names (their `parameterLongName` values from + * command-line.json) that should be ignored when invoking the command for this operation. + * This allows a project to opt out of parameters that don't affect its operation, preventing + * unnecessary cache invalidation for this operation and its consumers. + */ + parameterNamesToIgnore?: string[]; } interface IOldRushProjectJson { @@ -95,8 +187,8 @@ interface IOldRushProjectJson { buildCacheOptions?: unknown; } -const RUSH_PROJECT_CONFIGURATION_FILE: ConfigurationFile = - new ConfigurationFile({ +const RUSH_PROJECT_CONFIGURATION_FILE: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath: `config/${RushConstants.rushProjectConfigFilename}`, jsonSchemaObject: schemaJson, propertyInheritance: { @@ -178,31 +270,30 @@ const RUSH_PROJECT_CONFIGURATION_FILE: ConfigurationFile = } }); -const OLD_RUSH_PROJECT_CONFIGURATION_FILE: ConfigurationFile = - new ConfigurationFile({ +const OLD_RUSH_PROJECT_CONFIGURATION_FILE: ProjectConfigurationFile = + new ProjectConfigurationFile({ projectRelativeFilePath: RUSH_PROJECT_CONFIGURATION_FILE.projectRelativeFilePath, jsonSchemaObject: anythingSchemaJson }); +const _configCache: Map = new Map(); + /** * Use this class to load the "config/rush-project.json" config file. * * This file provides project-specific configuration options. - * @public + * @alpha */ export class RushProjectConfiguration { - private static readonly _configCache: Map = - new Map(); - public readonly project: RushConfigurationProject; /** - * {@inheritdoc IRushProjectJson.incrementalBuildIgnoredGlobs} + * {@inheritdoc _IRushProjectJson.incrementalBuildIgnoredGlobs} */ public readonly incrementalBuildIgnoredGlobs: ReadonlyArray; /** - * {@inheritdoc IRushProjectJson.disableBuildCacheForProject} + * {@inheritdoc _IRushProjectJson.disableBuildCacheForProject} */ public readonly disableBuildCacheForProject: boolean; @@ -247,10 +338,11 @@ export class RushProjectConfiguration { if (operationSettings) { if (operationSettings.outputFolderNames) { for (const outputFolderName of operationSettings.outputFolderNames) { - const overlappingOperationNames: string[] | undefined = + const otherOverlappingOperationNames: string[] | undefined = overlappingPathAnalyzer.addPathAndGetFirstEncounteredLabels(outputFolderName, operationName); - if (overlappingOperationNames) { - const overlapsWithOwnOperation: boolean = overlappingOperationNames?.includes(operationName); + if (otherOverlappingOperationNames) { + const overlapsWithOwnOperation: boolean = + otherOverlappingOperationNames?.includes(operationName); if (overlapsWithOwnOperation) { terminal.writeErrorLine( `The project "${project.packageName}" has a ` + @@ -263,10 +355,9 @@ export class RushProjectConfiguration { `The project "${project.packageName}" has a ` + `"${RUSH_PROJECT_CONFIGURATION_FILE.projectRelativeFilePath}" configuration that defines ` + 'two operations in the same command whose "outputFolderNames" would overlap. ' + - 'Operations outputs in the same command must be disjoint so that they can be independently cached.' + - `\n\n` + + 'Operations outputs in the same command must be disjoint so that they can be independently cached. ' + `The "${outputFolderName}" path overlaps between these operations: ` + - overlappingOperationNames.map((operationName) => `"${operationName}"`).join(', ') + `"${operationName}", "${otherOverlappingOperationNames.join('", "')}"` ); } @@ -274,6 +365,35 @@ export class RushProjectConfiguration { } } } + + // Validate that parameter names to ignore actually exist for this operation + if (operationSettings.parameterNamesToIgnore) { + // Build a set of valid parameter names for this phase + const validParameterNames: Set = new Set(); + for (const parameter of phase.associatedParameters) { + validParameterNames.add(parameter.longName); + } + + // Collect all invalid parameter names + const invalidParameterNames: string[] = []; + for (const parameterName of operationSettings.parameterNamesToIgnore) { + if (!validParameterNames.has(parameterName)) { + invalidParameterNames.push(parameterName); + } + } + + // Report all invalid parameters in a single message + if (invalidParameterNames.length > 0) { + terminal.writeErrorLine( + `The project "${project.packageName}" has a ` + + `"${RUSH_PROJECT_CONFIGURATION_FILE.projectRelativeFilePath}" configuration that specifies ` + + `invalid parameter(s) in "parameterNamesToIgnore" for operation "${operationName}": ` + + `${invalidParameterNames.join(', ')}. ` + + `Valid parameters for this operation are: ${Array.from(validParameterNames).sort().join(', ') || '(none)'}.` + ); + hasErrors = true; + } + } } } @@ -284,6 +404,94 @@ export class RushProjectConfiguration { } } + /** + * Examines the list of source files for the project and the target phase and returns a reason + * why the project cannot enable the build cache for that phase, or undefined if it is safe to so do. + */ + public getCacheDisabledReason( + trackedFileNames: Iterable, + phaseName: string, + isNoOp: boolean + ): string | undefined { + const rushConfiguration: RushConfiguration | undefined = this.project.rushConfiguration; + if (rushConfiguration) { + const hotlinkManager: HotlinkManager = HotlinkManager.loadFromRushConfiguration(rushConfiguration); + if (hotlinkManager.hasAnyHotlinksInSubspace(this.project.subspace.subspaceName)) { + return 'Caching has been disabled for this project because it is in a subspace with hotlinked dependencies.'; + } + } + + // Skip no-op operations as they won't have any output/cacheable things. + if (isNoOp) { + return undefined; + } + if (this.disableBuildCacheForProject) { + return 'Caching has been disabled for this project.'; + } + + const operationSettings: IOperationSettings | undefined = + this.operationSettingsByOperationName.get(phaseName); + if (!operationSettings) { + return `This project does not define the caching behavior of the "${phaseName}" command, so caching has been disabled.`; + } + + if (operationSettings.disableBuildCacheForOperation) { + return `Caching has been disabled for this project's "${phaseName}" command.`; + } + + const { outputFolderNames } = operationSettings; + if (!outputFolderNames) { + return; + } + const normalizedProjectRelativeFolder: string = Path.convertToSlashes(this.project.projectRelativeFolder); + + const normalizedOutputFolders: string[] = outputFolderNames.map( + (outputFolderName) => `${normalizedProjectRelativeFolder}/${outputFolderName}/` + ); + + const inputOutputFiles: string[] = []; + for (const file of trackedFileNames) { + for (const outputFolder of normalizedOutputFolders) { + if (file.startsWith(outputFolder)) { + inputOutputFiles.push(file); + } + } + } + + if (inputOutputFiles.length > 0) { + return ( + 'The following files are used to calculate project state ' + + `and are considered project output: ${inputOutputFiles.join(', ')}` + ); + } + } + + /** + * Source of truth for whether a project is unable to use the build cache for a given phase. + * As some operations may not have a rush-project.json file defined at all, but may be no-op operations + * we'll want to ignore those completely. + */ + public static getCacheDisabledReasonForProject(options: { + projectConfiguration: RushProjectConfiguration | undefined; + trackedFileNames: Iterable; + phaseName: string; + isNoOp: boolean; + }): string | undefined { + const { projectConfiguration, trackedFileNames, phaseName, isNoOp } = options; + if (isNoOp) { + return undefined; + } + + if (!projectConfiguration) { + return ( + `Project does not have a ${RushConstants.rushProjectConfigFilename} configuration file, ` + + 'or one provided by a rig, so it does not support caching.' + ); + } + + return projectConfiguration.getCacheDisabledReason(trackedFileNames, phaseName, isNoOp); + } + /** * Loads the rush-project.json data for the specified project. */ @@ -292,27 +500,28 @@ export class RushProjectConfiguration { terminal: ITerminal ): Promise { // false is a signal that the project config does not exist - const cacheEntry: RushProjectConfiguration | false | undefined = - RushProjectConfiguration._configCache.get(project); + const cacheEntry: RushProjectConfiguration | false | undefined = _configCache.get(project); if (cacheEntry !== undefined) { return cacheEntry || undefined; } - const rushProjectJson: IRushProjectJson | undefined = await this._tryLoadJsonForProjectAsync( + const rushProjectJson: IRushProjectJson | undefined = await _tryLoadJsonForProjectAsync( project, terminal ); if (rushProjectJson) { - const result: RushProjectConfiguration = RushProjectConfiguration._getRushProjectConfiguration( + const operationSettingsByOperationName: ReadonlyMap = + _getRushProjectConfiguration(project, rushProjectJson, terminal); + const result: RushProjectConfiguration = new RushProjectConfiguration( project, rushProjectJson, - terminal + operationSettingsByOperationName ); - RushProjectConfiguration._configCache.set(project, result); + _configCache.set(project, result); return result; } else { - RushProjectConfiguration._configCache.set(project, false); + _configCache.set(project, false); return undefined; } } @@ -329,7 +538,7 @@ export class RushProjectConfiguration { project: RushConfigurationProject, terminal: ITerminal ): Promise | undefined> { - const rushProjectJson: IRushProjectJson | undefined = await this._tryLoadJsonForProjectAsync( + const rushProjectJson: IRushProjectJson | undefined = await _tryLoadJsonForProjectAsync( project, terminal ); @@ -337,102 +546,139 @@ export class RushProjectConfiguration { return rushProjectJson?.incrementalBuildIgnoredGlobs; } - private static async _tryLoadJsonForProjectAsync( - project: RushConfigurationProject, + /** + * Load the rush-project.json data for all selected projects. + * Validate compatibility of output folders across all selected phases. + */ + public static async tryLoadForProjectsAsync( + projects: Iterable, terminal: ITerminal - ): Promise { - const rigConfig: RigConfig = await RigConfig.loadForProjectFolderAsync({ - projectFolderPath: project.projectFolder - }); + ): Promise> { + const result: Map = new Map(); + + await Async.forEachAsync( + projects, + async (project: RushConfigurationProject) => { + const projectConfig: RushProjectConfiguration | undefined = + await RushProjectConfiguration.tryLoadForProjectAsync(project, terminal); + if (projectConfig) { + result.set(project, projectConfig); + } + }, + { concurrency: 50 } + ); + return result; + } +} + +async function _tryLoadJsonForProjectAsync( + project: RushConfigurationProject, + terminal: ITerminal +): Promise { + const rigConfig: RigConfig = await RigConfig.loadForProjectFolderAsync({ + projectFolderPath: project.projectFolder + }); + + try { + return await RUSH_PROJECT_CONFIGURATION_FILE.tryLoadConfigurationFileForProjectAsync( + terminal, + project.projectFolder, + rigConfig + ); + } catch (e1) { + // Detect if the project is using the old rush-project.json schema + let oldRushProjectJson: IOldRushProjectJson | undefined; try { - return await RUSH_PROJECT_CONFIGURATION_FILE.tryLoadConfigurationFileForProjectAsync( + oldRushProjectJson = await OLD_RUSH_PROJECT_CONFIGURATION_FILE.tryLoadConfigurationFileForProjectAsync( terminal, project.projectFolder, rigConfig ); - } catch (e) { - // Detect if the project is using the old rush-project.json schema - let oldRushProjectJson: IOldRushProjectJson | undefined; - try { - oldRushProjectJson = - await OLD_RUSH_PROJECT_CONFIGURATION_FILE.tryLoadConfigurationFileForProjectAsync( - terminal, - project.projectFolder, - rigConfig - ); - } catch (e) { - // Ignore - } + } catch (e2) { + // Ignore + } - if ( - oldRushProjectJson?.projectOutputFolderNames || - oldRushProjectJson?.phaseOptions || - oldRushProjectJson?.buildCacheOptions - ) { - throw new Error( - `The ${RUSH_PROJECT_CONFIGURATION_FILE.projectRelativeFilePath} file appears to be ` + - 'in an outdated format. Please see the UPGRADING.md notes for details. ' + - 'Quick link: https://rushjs.io/link/upgrading' - ); - } else { - throw e; - } + if ( + oldRushProjectJson?.projectOutputFolderNames || + oldRushProjectJson?.phaseOptions || + oldRushProjectJson?.buildCacheOptions + ) { + throw new Error( + `The ${RUSH_PROJECT_CONFIGURATION_FILE.projectRelativeFilePath} file appears to be ` + + 'in an outdated format. Please see the UPGRADING.md notes for details. ' + + 'Quick link: https://rushjs.io/link/upgrading' + ); + } else { + throw e1; } } +} - private static _getRushProjectConfiguration( - project: RushConfigurationProject, - rushProjectJson: IRushProjectJson, - terminal: ITerminal - ): RushProjectConfiguration { - const operationSettingsByOperationName: Map = new Map< - string, - IOperationSettings - >(); - - let hasErrors: boolean = false; - - if (rushProjectJson.operationSettings) { - for (const operationSettings of rushProjectJson.operationSettings) { - const operationName: string = operationSettings.operationName; - const existingOperationSettings: IOperationSettings | undefined = - operationSettingsByOperationName.get(operationName); - if (existingOperationSettings) { - const existingOperationSettingsJsonPath: string | undefined = - RUSH_PROJECT_CONFIGURATION_FILE.getObjectSourceFilePath(existingOperationSettings); - const operationSettingsJsonPath: string | undefined = - RUSH_PROJECT_CONFIGURATION_FILE.getObjectSourceFilePath(operationSettings); - hasErrors = true; - let errorMessage: string = - `The operation "${operationName}" appears multiple times in the "${project.packageName}" project's ` + - `${RUSH_PROJECT_CONFIGURATION_FILE.projectRelativeFilePath} file's ` + - 'operationSettings property.'; - if (existingOperationSettingsJsonPath && operationSettingsJsonPath) { - if (existingOperationSettingsJsonPath !== operationSettingsJsonPath) { - errorMessage += - ` It first appears in "${existingOperationSettingsJsonPath}" and again ` + - `in "${operationSettingsJsonPath}".`; - } else if ( - !Path.convertToSlashes(existingOperationSettingsJsonPath).startsWith( - Path.convertToSlashes(project.projectFolder) - ) - ) { - errorMessage += ` It appears multiple times in "${operationSettingsJsonPath}".`; - } +/** + * Parses and validates the operation settings from the rush-project.json data. Returns the + * validated `operationSettingsByOperationName` map used to construct a {@link RushProjectConfiguration}. + * (The construction itself must remain in the class body because the constructor is private.) + */ +function _getRushProjectConfiguration( + project: RushConfigurationProject, + rushProjectJson: IRushProjectJson, + terminal: ITerminal +): ReadonlyMap { + const operationSettingsByOperationName: Map = new Map< + string, + IOperationSettings + >(); + + let hasErrors: boolean = false; + + if (rushProjectJson.operationSettings) { + for (const operationSettings of rushProjectJson.operationSettings) { + const operationName: string = operationSettings.operationName; + const existingOperationSettings: IOperationSettings | undefined = + operationSettingsByOperationName.get(operationName); + if (existingOperationSettings) { + const existingOperationSettingsJsonPath: string | undefined = + RUSH_PROJECT_CONFIGURATION_FILE.getObjectSourceFilePath(existingOperationSettings); + const operationSettingsJsonPath: string | undefined = + RUSH_PROJECT_CONFIGURATION_FILE.getObjectSourceFilePath(operationSettings); + hasErrors = true; + let errorMessage: string = + `The operation "${operationName}" appears multiple times in the "${project.packageName}" project's ` + + `${RUSH_PROJECT_CONFIGURATION_FILE.projectRelativeFilePath} file's ` + + 'operationSettings property.'; + if (existingOperationSettingsJsonPath && operationSettingsJsonPath) { + if (existingOperationSettingsJsonPath !== operationSettingsJsonPath) { + errorMessage += + ` It first appears in "${existingOperationSettingsJsonPath}" and again ` + + `in "${operationSettingsJsonPath}".`; + } else if ( + !Path.convertToSlashes(existingOperationSettingsJsonPath).startsWith( + Path.convertToSlashes(project.projectFolder) + ) + ) { + errorMessage += ` It appears multiple times in "${operationSettingsJsonPath}".`; } - - terminal.writeErrorLine(errorMessage); - } else { - operationSettingsByOperationName.set(operationName, operationSettings); } + + terminal.writeErrorLine(errorMessage); + } else { + operationSettingsByOperationName.set(operationName, operationSettings); } } - if (hasErrors) { - throw new AlreadyReportedError(); + for (const [operationName, operationSettings] of operationSettingsByOperationName) { + if (operationSettings.sharding?.shardOperationSettings) { + terminal.writeWarningLine( + `DEPRECATED: The "sharding.shardOperationSettings" field is deprecated. Please create a new operation, '${operationName}:shard' to track shard operation settings.` + ); + } } + } - return new RushProjectConfiguration(project, rushProjectJson, operationSettingsByOperationName); + if (hasErrors) { + throw new AlreadyReportedError(); } + + return operationSettingsByOperationName; } diff --git a/libraries/rush-lib/src/api/RushUserConfiguration.ts b/libraries/rush-lib/src/api/RushUserConfiguration.ts index a81c4080ed7..7ff410d9d5f 100644 --- a/libraries/rush-lib/src/api/RushUserConfiguration.ts +++ b/libraries/rush-lib/src/api/RushUserConfiguration.ts @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { FileSystem, JsonFile, JsonSchema } from '@rushstack/node-core-library'; -import * as path from 'path'; +import * as path from 'node:path'; + +import { FileSystem, JsonFile, JsonSchema, User } from '@rushstack/node-core-library'; -import { Utilities } from '../utilities/Utilities'; import { RushConstants } from '../logic/RushConstants'; import schemaJson from '../schemas/rush-user-settings.schema.json'; @@ -12,14 +12,14 @@ interface IRushUserSettingsJson { buildCacheFolder?: string; } +const _schema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); + /** * Rush per-user configuration data. * * @beta */ export class RushUserConfiguration { - private static _schema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); - /** * If provided, store build cache in the specified folder. Must be an absolute path. */ @@ -37,10 +37,7 @@ export class RushUserConfiguration { const rushUserSettingsFilePath: string = path.join(rushUserFolderPath, 'settings.json'); let rushUserSettingsJson: IRushUserSettingsJson | undefined; try { - rushUserSettingsJson = await JsonFile.loadAndValidateAsync( - rushUserSettingsFilePath, - RushUserConfiguration._schema - ); + rushUserSettingsJson = await JsonFile.loadAndValidateAsync(rushUserSettingsFilePath, _schema); } catch (e) { if (!FileSystem.isNotExistError(e as Error)) { throw e; @@ -51,11 +48,7 @@ export class RushUserConfiguration { } public static getRushUserFolderPath(): string { - const homeFolderPath: string = Utilities.getHomeFolder(); - const rushUserSettingsFilePath: string = path.join( - homeFolderPath, - RushConstants.rushUserConfigurationFolderName - ); - return rushUserSettingsFilePath; + const homeFolderPath: string = User.getHomeFolder(); + return `${homeFolderPath}/${RushConstants.rushUserConfigurationFolderName}`; } } diff --git a/libraries/rush-lib/src/api/SaveCallbackPackageJsonEditor.ts b/libraries/rush-lib/src/api/SaveCallbackPackageJsonEditor.ts index 3543310304d..cf4f5028b47 100644 --- a/libraries/rush-lib/src/api/SaveCallbackPackageJsonEditor.ts +++ b/libraries/rush-lib/src/api/SaveCallbackPackageJsonEditor.ts @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IPackageJson } from '@rushstack/node-core-library'; +import type { IPackageJson } from '@rushstack/node-core-library'; + import { PackageJsonEditor } from './PackageJsonEditor'; export interface IFromObjectOptions { @@ -23,8 +24,8 @@ export class SaveCallbackPackageJsonEditor extends PackageJsonEditor { return new SaveCallbackPackageJsonEditor(options); } - public saveIfModified(): boolean { - const modified: boolean = super.saveIfModified(); + public override async saveIfModifiedAsync(): Promise { + const modified: boolean = await super.saveIfModifiedAsync(); if (this._onSaved) { this._onSaved(this.saveToObject()); } diff --git a/libraries/rush-lib/src/api/Subspace.ts b/libraries/rush-lib/src/api/Subspace.ts new file mode 100644 index 00000000000..49f00faa943 --- /dev/null +++ b/libraries/rush-lib/src/api/Subspace.ts @@ -0,0 +1,524 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import * as crypto from 'node:crypto'; + +import { FileSystem } from '@rushstack/node-core-library'; +import type { IPackageJson } from '@rushstack/node-core-library'; + +import type { RushConfiguration } from './RushConfiguration'; +import type { RushConfigurationProject } from './RushConfigurationProject'; +import { EnvironmentConfiguration } from './EnvironmentConfiguration'; +import { RushConstants } from '../logic/RushConstants'; +import { CommonVersionsConfiguration } from './CommonVersionsConfiguration'; +import { RepoStateFile } from '../logic/RepoStateFile'; +import type { PnpmPackageManager } from './packageManager/PnpmPackageManager'; +import { PnpmOptionsConfiguration } from '../logic/pnpm/PnpmOptionsConfiguration'; +import { SubspacePnpmfileConfiguration } from '../logic/pnpm/SubspacePnpmfileConfiguration'; +import type { ISubspacePnpmfileShimSettings } from '../logic/pnpm/IPnpmfile'; + +/** + * @internal + */ +export interface ISubspaceOptions { + subspaceName: string; + rushConfiguration: RushConfiguration; + splitWorkspaceCompatibility: boolean; +} + +interface ISubspaceDetail { + subspaceConfigFolderPath: string; + subspacePnpmPatchesFolderPath: string; + subspaceTempFolderPath: string; + tempShrinkwrapFilePath: string; + tempShrinkwrapPreinstallFilePath: string; +} + +interface IPackageJsonLite extends Omit {} + +/** + * This represents the subspace configurations for a repository, based on the "subspaces.json" + * configuration file. + * @public + */ +export class Subspace { + public readonly subspaceName: string; + private readonly _rushConfiguration: RushConfiguration; + private readonly _projects: RushConfigurationProject[] = []; + private readonly _splitWorkspaceCompatibility: boolean; + private _commonVersionsConfiguration: CommonVersionsConfiguration | undefined = undefined; + + private _detail: ISubspaceDetail | undefined; + + private _cachedPnpmOptions: PnpmOptionsConfiguration | undefined = undefined; + // If true, then _cachedPnpmOptions has been initialized. + private _cachedPnpmOptionsInitialized: boolean = false; + + public constructor(options: ISubspaceOptions) { + this.subspaceName = options.subspaceName; + this._rushConfiguration = options.rushConfiguration; + this._splitWorkspaceCompatibility = options.splitWorkspaceCompatibility; + } + + /** + * Returns the list of projects belonging to this subspace. + * @beta + */ + public getProjects(): RushConfigurationProject[] { + return this._projects; + } + + /** + * Returns the parsed contents of the pnpm-config.json config file. + * @beta + */ + public getPnpmOptions(): PnpmOptionsConfiguration | undefined { + if (!this._cachedPnpmOptionsInitialized) { + // Calculate these outside the try/catch block since their error messages shouldn't be annotated: + const subspaceTempFolder: string = this.getSubspaceTempFolderPath(); + try { + this._cachedPnpmOptions = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + this.getPnpmConfigFilePath(), + subspaceTempFolder + ); + this._cachedPnpmOptionsInitialized = true; + } catch (e) { + if (FileSystem.isNotExistError(e as Error)) { + this._cachedPnpmOptions = undefined; + this._cachedPnpmOptionsInitialized = true; + } else { + throw new Error( + `The subspace "${this.subspaceName}" has an invalid pnpm-config.json file:\n` + e.message + ); + } + } + } + return this._cachedPnpmOptions; + } + + private _ensureDetail(): ISubspaceDetail { + if (!this._detail) { + const rushConfiguration: RushConfiguration = this._rushConfiguration; + let subspaceConfigFolderPath: string; + let subspacePnpmPatchesFolderPath: string; + + if (rushConfiguration.subspacesFeatureEnabled) { + if (!rushConfiguration.pnpmOptions.useWorkspaces) { + throw new Error( + `The Rush subspaces feature is enabled. You must set useWorkspaces=true in pnpm-config.json.` + ); + } + + // If this subspace doesn't have a configuration folder, check if it is in the project folder itself + // if the splitWorkspaceCompatibility option is enabled in the subspace configuration + + // Example: C:\MyRepo\common\config\subspaces\my-subspace + const standardSubspaceConfigFolder: string = `${rushConfiguration.commonFolder}/config/subspaces/${this.subspaceName}`; + + subspaceConfigFolderPath = standardSubspaceConfigFolder; + + if (this._splitWorkspaceCompatibility && this.subspaceName.startsWith('split_')) { + if (FileSystem.exists(standardSubspaceConfigFolder + '/pnpm-lock.yaml')) { + throw new Error( + `The split workspace subspace "${this.subspaceName}" cannot use a common/config folder: ` + + standardSubspaceConfigFolder + ); + } + + if (this._projects.length !== 1) { + throw new Error( + `The split workspace subspace "${this.subspaceName}" contains ${this._projects.length}` + + ` projects; there must be exactly one project.` + ); + } + const project: RushConfigurationProject = this._projects[0]; + + subspaceConfigFolderPath = `${project.projectFolder}/subspace/${this.subspaceName}`; + + // Ensure that this project does not have it's own pnpmfile.cjs or .npmrc file + if (FileSystem.exists(`${project.projectFolder}/.npmrc`)) { + throw new Error( + `The project level configuration file ${project.projectFolder}/.npmrc is no longer valid. Please use a ${subspaceConfigFolderPath}/.npmrc file instead.` + ); + } + if (FileSystem.exists(`${project.projectFolder}/.pnpmfile.cjs`)) { + throw new Error( + `The project level configuration file ${project.projectFolder}/.pnpmfile.cjs is no longer valid. Please use a ${subspaceConfigFolderPath}/.pnpmfile.cjs file instead.` + ); + } + } + + if (!FileSystem.exists(subspaceConfigFolderPath)) { + throw new Error( + `The configuration folder for the "${this.subspaceName}" subspace does not exist: ` + + subspaceConfigFolderPath + ); + } + + subspacePnpmPatchesFolderPath = `${subspaceConfigFolderPath}/${RushConstants.pnpmPatchesCommonFolderName}`; + } else { + // Example: C:\MyRepo\common\config\rush + subspaceConfigFolderPath = rushConfiguration.commonRushConfigFolder; + // Example: C:\MyRepo\common\pnpm-patches + subspacePnpmPatchesFolderPath = `${rushConfiguration.commonFolder}/${RushConstants.pnpmPatchesCommonFolderName}`; + } + + // Example: C:\MyRepo\common\temp + const commonTempFolder: string = + EnvironmentConfiguration.rushTempFolderOverride || rushConfiguration.commonTempFolder; + + let subspaceTempFolderPath: string; + if (rushConfiguration.subspacesFeatureEnabled) { + // Example: C:\MyRepo\common\temp\my-subspace + subspaceTempFolderPath = `${commonTempFolder}/${this.subspaceName}`; + } else { + // Example: C:\MyRepo\common\temp + subspaceTempFolderPath = commonTempFolder; + } + + // Example: C:\MyRepo\common\temp\my-subspace\pnpm-lock.yaml + const tempShrinkwrapFilePath: string = `${subspaceTempFolderPath}/${rushConfiguration.shrinkwrapFilename}`; + + /// From "C:\MyRepo\common\temp\pnpm-lock.yaml" --> "C:\MyRepo\common\temp\pnpm-lock-preinstall.yaml" + const parsedPath: path.ParsedPath = path.parse(tempShrinkwrapFilePath); + const tempShrinkwrapPreinstallFilePath: string = `${parsedPath.dir}/${parsedPath.name}-preinstall${parsedPath.ext}`; + + this._detail = { + subspaceConfigFolderPath, + subspacePnpmPatchesFolderPath, + subspaceTempFolderPath, + tempShrinkwrapFilePath, + tempShrinkwrapPreinstallFilePath + }; + } + return this._detail; + } + + /** + * Returns the full path of the folder containing this subspace's variant-dependent configuration files + * such as `pnpm-lock.yaml`. + * + * Example (variants): `C:\MyRepo\common\config\rush\variants\my-variant` + * Example (variants and subspaces): `C:\MyRepo\common\config\subspaces\my-subspace\variants\my-variant` + * Example (subspaces): `C:\MyRepo\common\config\subspaces\my-subspace` + * Example (neither): `C:\MyRepo\common\config\rush` + * @beta + * + * @remarks + * The following files may be variant-dependent: + * - Lockfiles: (i.e. - `pnpm-lock.yaml`, `npm-shrinkwrap.json`, `yarn.lock`, etc) + * - 'common-versions.json' + * - 'pnpmfile.js'/'.pnpmfile.cjs' + */ + public getVariantDependentSubspaceConfigFolderPath(variant: string | undefined): string { + const subspaceConfigFolderPath: string = this.getSubspaceConfigFolderPath(); + if (!variant) { + return subspaceConfigFolderPath; + } else { + return `${subspaceConfigFolderPath}/${RushConstants.rushVariantsFolderName}/${variant}`; + } + } + + /** + * Returns the full path of the folder containing this subspace's configuration files such as `pnpm-lock.yaml`. + * + * Example (subspaces feature enabled): `C:\MyRepo\common\config\subspaces\my-subspace` + * Example (subspaces feature disabled): `C:\MyRepo\common\config\rush` + * @beta + */ + public getSubspaceConfigFolderPath(): string { + return this._ensureDetail().subspaceConfigFolderPath; + } + + /** + * Returns the full path of the folder containing this subspace's configuration files such as `pnpm-lock.yaml`. + * + * Example (subspaces feature enabled): `C:\MyRepo\common\config\subspaces\my-subspace\pnpm-patches` + * Example (subspaces feature disabled): `C:\MyRepo\common\pnpm-patches` + * @beta + */ + public getSubspacePnpmPatchesFolderPath(): string { + return this._ensureDetail().subspacePnpmPatchesFolderPath; + } + + /** + * The full path of the folder where the subspace's node_modules and other temporary files will be stored. + * + * Example (subspaces feature enabled): `C:\MyRepo\common\temp\subspaces\my-subspace` + * Example (subspaces feature disabled): `C:\MyRepo\common\temp` + * @beta + */ + public getSubspaceTempFolderPath(): string { + return this._ensureDetail().subspaceTempFolderPath; + } + + /** + * Returns full path of the temporary shrinkwrap file for a specific subspace and returns the common workspace + * shrinkwrap if no subspaceName is provided. + * @remarks + * This function takes the subspace name, and returns the full path for the subspace's shrinkwrap file. + * This function also consults the deprecated option to allow for shrinkwraps to be stored under a package folder. + * This shrinkwrap file is used during "rush install", and may be rewritten by the package manager during installation + * This property merely reports the filename, the file itself may not actually exist. + * example: `C:\MyRepo\common\\pnpm-lock.yaml` + * @beta + */ + public getTempShrinkwrapFilename(): string { + return this._ensureDetail().tempShrinkwrapFilePath; + } + + /** + * @deprecated - Use {@link Subspace.getTempShrinkwrapPreinstallFilePath} instead. + */ + public getTempShrinkwrapPreinstallFilename(subspaceName?: string | undefined): string { + return this.getTempShrinkwrapPreinstallFilePath(); + } + + /** + * The full path of a backup copy of tempShrinkwrapFilename. This backup copy is made + * before installation begins, and can be compared to determine how the package manager + * modified tempShrinkwrapFilename. + * @remarks + * This property merely reports the filename; the file itself may not actually exist. + * Example: `C:\MyRepo\common\temp\npm-shrinkwrap-preinstall.json` + * or `C:\MyRepo\common\temp\pnpm-lock-preinstall.yaml` + * @beta + */ + public getTempShrinkwrapPreinstallFilePath(): string { + return this._ensureDetail().tempShrinkwrapPreinstallFilePath; + } + + /** + * Gets the full path to the common-versions.json config file for this subspace. + * + * Example (subspaces feature enabled): `C:\MyRepo\common\config\subspaces\my-subspace\common-versions.json` + * Example (subspaces feature disabled): `C:\MyRepo\common\config\rush\common-versions.json` + * @beta + */ + public getCommonVersionsFilePath(variant?: string): string { + return ( + this.getVariantDependentSubspaceConfigFolderPath(variant) + '/' + RushConstants.commonVersionsFilename + ); + } + + /** + * Gets the full path to the pnpm-config.json config file for this subspace. + * + * Example (subspaces feature enabled): `C:\MyRepo\common\config\subspaces\my-subspace\pnpm-config.json` + * Example (subspaces feature disabled): `C:\MyRepo\common\config\rush\pnpm-config.json` + * @beta + */ + public getPnpmConfigFilePath(): string { + return this.getSubspaceConfigFolderPath() + '/' + RushConstants.pnpmConfigFilename; + } + + /** + * Gets the settings from the common-versions.json config file. + * @beta + */ + public getCommonVersions(variant?: string): CommonVersionsConfiguration { + const commonVersionsFilePath: string = this.getCommonVersionsFilePath(variant); + if (!this._commonVersionsConfiguration) { + this._commonVersionsConfiguration = CommonVersionsConfiguration.loadFromFile( + commonVersionsFilePath, + this._rushConfiguration + ); + } + + return this._commonVersionsConfiguration; + } + + /** + * Gets the ensureConsistentVersions property from the common-versions.json config file, + * or from the rush.json file if it isn't defined in common-versions.json + * @beta + */ + public shouldEnsureConsistentVersions(variant?: string): boolean { + // If the ensureConsistentVersions field is defined, return the value of the field + const commonVersions: CommonVersionsConfiguration = this.getCommonVersions(variant); + if (commonVersions.ensureConsistentVersions !== undefined) { + return commonVersions.ensureConsistentVersions; + } + + // Fallback to ensureConsistentVersions in rush.json if the setting is not defined in + // the common-versions.json file + return this._rushConfiguration.ensureConsistentVersions; + } + + /** + * Gets the path to the repo-state.json file. + * @beta + */ + public getRepoStateFilePath(): string { + return this.getSubspaceConfigFolderPath() + '/' + RushConstants.repoStateFilename; + } + + /** + * Gets the contents from the repo-state.json file. + * @param subspaceName - The name of the subspace in use by the active command. + * @beta + */ + public getRepoState(): RepoStateFile { + const repoStateFilePath: string = this.getRepoStateFilePath(); + return RepoStateFile.loadFromFile(repoStateFilePath); + } + + /** + * @deprecated - Use {@link Subspace.getCommittedShrinkwrapFilePath} instead. + */ + public getCommittedShrinkwrapFilename(): string { + return this.getCommittedShrinkwrapFilePath(undefined); + } + + /** + * Gets the committed shrinkwrap file name for a specific variant. + * @param variant - The name of the current variant in use by the active command. + * @beta + */ + public getCommittedShrinkwrapFilePath(variant?: string): string { + const subspaceConfigFolderPath: string = this.getVariantDependentSubspaceConfigFolderPath(variant); + return `${subspaceConfigFolderPath}/${this._rushConfiguration.shrinkwrapFilename}`; + } + + /** + * Gets the absolute path for "pnpmfile.js" for a specific subspace. + * @param subspace - The name of the current subspace in use by the active command. + * @remarks + * The file path is returned even if PNPM is not configured as the package manager. + * @beta + */ + public getPnpmfilePath(variant?: string): string { + const subspaceConfigFolderPath: string = this.getVariantDependentSubspaceConfigFolderPath(variant); + + const pnpmFilename: string = (this._rushConfiguration.packageManagerWrapper as PnpmPackageManager) + .pnpmfileFilename; + + return `${subspaceConfigFolderPath}/${pnpmFilename}`; + } + + /** + * Returns true if the specified project belongs to this subspace. + * @beta + */ + public contains(project: RushConfigurationProject): boolean { + return project.subspace.subspaceName === this.subspaceName; + } + + /** @internal */ + public _addProject(project: RushConfigurationProject): void { + this._projects.push(project); + } + + /** + * Computes a hash of the PNPM catalog definitions for this subspace. + * Returns undefined if no catalogs are defined. + */ + public getPnpmCatalogsHash(): string | undefined { + const pnpmOptions: PnpmOptionsConfiguration | undefined = this.getPnpmOptions(); + if (!pnpmOptions) { + return undefined; + } + + const catalogData: Record = {}; + if (pnpmOptions.globalCatalogs && Object.keys(pnpmOptions.globalCatalogs).length !== 0) { + Object.assign(catalogData, pnpmOptions.globalCatalogs); + } + + // If no catalogs are defined, return undefined + if (Object.keys(catalogData).length === 0) { + return undefined; + } + + const hash: crypto.Hash = crypto.createHash('sha1'); + hash.update(JSON.stringify(catalogData)); + return hash.digest('hex'); + } + + /** + * Returns hash value of injected dependencies in related package.json. + * @beta + */ + public getPackageJsonInjectedDependenciesHash(variant?: string): string | undefined { + const allPackageJson: IPackageJsonLite[] = []; + + const relatedProjects: RushConfigurationProject[] = []; + const subspacePnpmfileShimSettings: ISubspacePnpmfileShimSettings = + SubspacePnpmfileConfiguration.getSubspacePnpmfileShimSettings(this._rushConfiguration, this, variant); + + for (const rushProject of this.getProjects()) { + const injectedDependencies: Array = + subspacePnpmfileShimSettings?.subspaceProjects[rushProject.packageName]?.injectedDependencies || []; + if (injectedDependencies.length === 0) { + continue; + } + + const injectedDependencySet: Set = new Set(injectedDependencies); + + for (const dependencyProject of rushProject.dependencyProjects) { + if (injectedDependencySet.has(dependencyProject.packageName)) { + relatedProjects.push(dependencyProject); + } + } + } + + // this means no injected dependencies found for current subspace + if (relatedProjects.length === 0) { + return undefined; + } + + const allWorkspaceProjectSet: Set = new Set( + this._rushConfiguration.projects.map((rushProject) => rushProject.packageName) + ); + + // get all related package.json + while (relatedProjects.length > 0) { + const rushProject: RushConfigurationProject = relatedProjects.pop()!; + // collect fields that could update the `pnpm-lock.yaml` + const { + name, + bin, + dependencies, + peerDependencies, + optionalDependencies, + dependenciesMeta, + peerDependenciesMeta, + resolutions + } = rushProject.packageJson; + + // special handing for peerDependencies + // for workspace packages, the version range is meaningless here. + if (peerDependencies) { + for (const packageName of Object.keys(peerDependencies)) { + if (allWorkspaceProjectSet.has(packageName)) { + peerDependencies[packageName] = 'workspace:*'; + } + } + } + + allPackageJson.push({ + name, + bin, + dependencies, + peerDependencies, + optionalDependencies, + dependenciesMeta, + peerDependenciesMeta, + resolutions + }); + + relatedProjects.push(...rushProject.dependencyProjects); + } + + const collator: Intl.Collator = new Intl.Collator('en'); + allPackageJson.sort((pa, pb) => collator.compare(pa.name, pb.name)); + const hash: crypto.Hash = crypto.createHash('sha1'); + for (const packageFile of allPackageJson) { + hash.update(JSON.stringify(packageFile)); + } + + const packageJsonInjectedDependenciesHash: string = hash.digest('hex'); + + return packageJsonInjectedDependenciesHash; + } +} diff --git a/libraries/rush-lib/src/api/SubspacesConfiguration.ts b/libraries/rush-lib/src/api/SubspacesConfiguration.ts new file mode 100644 index 00000000000..e1c6a1cd21d --- /dev/null +++ b/libraries/rush-lib/src/api/SubspacesConfiguration.ts @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { FileSystem, JsonFile, JsonSchema } from '@rushstack/node-core-library'; + +import type { RushConfiguration } from './RushConfiguration'; +import schemaJson from '../schemas/subspaces.schema.json'; +import { RushConstants } from '../logic/RushConstants'; + +/** + * The allowed naming convention for subspace names. + * Allows for names to be formed of identifiers separated by hyphens (-) + * + * Example: "my-subspace" + */ +export const SUBSPACE_NAME_REGEXP: RegExp = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/; +export const SPLIT_WORKSPACE_SUBSPACE_NAME_REGEXP: RegExp = /^[a-z0-9][+_\-a-z0-9]*$/; + +/** + * This represents the JSON data structure for the "subspaces.json" configuration file. + * See subspace.schema.json for documentation. + */ +export interface ISubspacesConfigurationJson { + subspacesEnabled: boolean; + splitWorkspaceCompatibility?: boolean; + preventSelectingAllSubspaces?: boolean; + subspaceNames: string[]; +} + +const _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); + +/** + * This represents the subspace configurations for a repository, based on the "subspaces.json" + * configuration file. + * @beta + */ +export class SubspacesConfiguration { + /** + * The absolute path to the "subspaces.json" configuration file that was loaded to construct this object. + */ + public readonly subspaceJsonFilePath: string; + + /* + * Determines whether the subspaces feature is enabled. + */ + public readonly subspacesEnabled: boolean; + + /** + * This determines if the subspaces feature supports adding configuration files under the project folder itself + */ + public readonly splitWorkspaceCompatibility: boolean; + + /** + * This determines if selectors are required when installing and building + */ + public readonly preventSelectingAllSubspaces: boolean; + + /** + * A set of the available subspaces + */ + public readonly subspaceNames: ReadonlySet; + + private constructor(configuration: Readonly, subspaceJsonFilePath: string) { + this.subspaceJsonFilePath = subspaceJsonFilePath; + this.subspacesEnabled = configuration.subspacesEnabled; + this.splitWorkspaceCompatibility = !!configuration.splitWorkspaceCompatibility; + this.preventSelectingAllSubspaces = !!configuration.preventSelectingAllSubspaces; + const subspaceNames: Set = new Set(); + for (const subspaceName of configuration.subspaceNames) { + SubspacesConfiguration.requireValidSubspaceName(subspaceName, this.splitWorkspaceCompatibility); + + subspaceNames.add(subspaceName); + } + // Add the default subspace if it wasn't explicitly declared + subspaceNames.add(RushConstants.defaultSubspaceName); + this.subspaceNames = subspaceNames; + } + + /** + * Checks whether the provided string could be used as a subspace name. + * Returns `undefined` if the name is valid; otherwise returns an error message. + * @remarks + * This is a syntax check only; it does not test whether the subspace is actually defined in the Rush configuration. + */ + public static explainIfInvalidSubspaceName( + subspaceName: string, + splitWorkspaceCompatibility: boolean = false + ): string | undefined { + if (subspaceName.length === 0) { + return `The subspace name cannot be empty`; + } + let regexToUse: RegExp; + if (splitWorkspaceCompatibility) { + regexToUse = SPLIT_WORKSPACE_SUBSPACE_NAME_REGEXP; + } else { + regexToUse = SUBSPACE_NAME_REGEXP; + } + if (!regexToUse.test(subspaceName)) { + if (splitWorkspaceCompatibility) { + return ( + `Invalid name "${subspaceName}". ` + + `Subspace names must consist of lowercase letters and numbers separated by hyphens, underscores, or plus signs.` + ); + } + return ( + `Invalid name "${subspaceName}". ` + + `Subspace names must consist of lowercase letters and numbers separated by hyphens.` + ); + } + + return undefined; // name is okay + } + + /** + * Checks whether the provided string could be used as a subspace name. + * If not, an exception is thrown. + * @remarks + * This is a syntax check only; it does not test whether the subspace is actually defined in the Rush configuration. + */ + public static requireValidSubspaceName( + subspaceName: string, + splitWorkspaceCompatibility: boolean = false + ): void { + const message: string | undefined = SubspacesConfiguration.explainIfInvalidSubspaceName( + subspaceName, + splitWorkspaceCompatibility + ); + if (message) { + throw new Error(message); + } + } + + public static tryLoadFromConfigurationFile( + subspaceJsonFilePath: string + ): SubspacesConfiguration | undefined { + let configuration: Readonly | undefined; + try { + configuration = JsonFile.loadAndValidate(subspaceJsonFilePath, _jsonSchema); + } catch (e) { + if (!FileSystem.isNotExistError(e)) { + throw e; + } + } + if (configuration) { + return new SubspacesConfiguration(configuration, subspaceJsonFilePath); + } + } + + public static tryLoadFromDefaultLocation( + rushConfiguration: RushConfiguration + ): SubspacesConfiguration | undefined { + const commonRushConfigFolder: string = rushConfiguration.commonRushConfigFolder; + const subspaceJsonLocation: string = `${commonRushConfigFolder}/${RushConstants.subspacesConfigFilename}`; + return SubspacesConfiguration.tryLoadFromConfigurationFile(subspaceJsonLocation); + } +} diff --git a/libraries/rush-lib/src/api/Variants.ts b/libraries/rush-lib/src/api/Variants.ts index 942ea055608..06825d73281 100644 --- a/libraries/rush-lib/src/api/Variants.ts +++ b/libraries/rush-lib/src/api/Variants.ts @@ -1,19 +1,35 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ICommandLineStringDefinition } from '@rushstack/ts-command-line'; +import type { CommandLineStringParameter, ICommandLineStringDefinition } from '@rushstack/ts-command-line'; + +import { EnvironmentVariableNames } from './EnvironmentConfiguration'; +import type { RushConfiguration } from './RushConfiguration'; +import { RushConstants } from '../logic/RushConstants'; /** - * Namespace for utilities relating to the Variants feature. + * Provides the parameter configuration for '--variant'. */ -export class Variants { - /** - * Provides the parameter configuration for '--variant'. - */ - public static readonly VARIANT_PARAMETER: ICommandLineStringDefinition = { - parameterLongName: '--variant', - argumentName: 'VARIANT', - description: 'Run command using a variant installation configuration', - environmentVariable: 'RUSH_VARIANT' - }; +export const VARIANT_PARAMETER: ICommandLineStringDefinition = { + parameterLongName: '--variant', + argumentName: 'VARIANT', + description: 'Run command using a variant installation configuration', + environmentVariable: EnvironmentVariableNames.RUSH_VARIANT +}; + +export async function getVariantAsync( + variantsParameter: CommandLineStringParameter | undefined, + rushConfiguration: RushConfiguration, + defaultToCurrentlyInstalledVariant: boolean +): Promise { + let variant: string | undefined = variantsParameter?.value; + if (variant && !rushConfiguration.variants.has(variant)) { + throw new Error(`The variant "${variant}" is not defined in ${RushConstants.rushJsonFilename}`); + } + + if (!variant && defaultToCurrentlyInstalledVariant) { + variant = await rushConfiguration.getCurrentlyInstalledVariantAsync(); + } + + return variant; } diff --git a/libraries/rush-lib/src/api/VersionPolicy.ts b/libraries/rush-lib/src/api/VersionPolicy.ts index 1c3b9613021..b738ba487d0 100644 --- a/libraries/rush-lib/src/api/VersionPolicy.ts +++ b/libraries/rush-lib/src/api/VersionPolicy.ts @@ -2,39 +2,45 @@ // See LICENSE in the project root for license information. import * as semver from 'semver'; -import { IPackageJson, Import, Enum } from '@rushstack/node-core-library'; -import { +import { type IPackageJson, Enum } from '@rushstack/node-core-library'; + +import type { IVersionPolicyJson, ILockStepVersionJson, IIndividualVersionJson, VersionFormatForCommit, - VersionFormatForPublish, - IVersionPolicyDependencyJson + VersionFormatForPublish } from './VersionPolicyConfiguration'; -import { PackageJsonEditor } from './PackageJsonEditor'; -import { RushConfiguration } from './RushConfiguration'; -import { RushConfigurationProject } from './RushConfigurationProject'; - -const lodash: typeof import('lodash') = Import.lazy('lodash', require); +import type { PackageJsonEditor } from './PackageJsonEditor'; +import type { RushConfiguration } from './RushConfiguration'; +import type { RushConfigurationProject } from './RushConfigurationProject'; +import { cloneDeep } from '../utilities/objectUtilities'; /** * Type of version bumps * @public + * + * @internalRemarks + * This is a copy of the semver ReleaseType enum, but with the `none` value added and + * the `premajor` and `prepatch` omitted. + * See {@link LockStepVersionPolicy._getReleaseType}. + * + * TODO: Consider supporting `premajor` and `prepatch` in the future. */ export enum BumpType { // No version bump - 'none', + 'none' = 0, // Prerelease version bump - 'prerelease', + 'prerelease' = 1, // Patch version bump - 'patch', + 'patch' = 2, // Preminor version bump - 'preminor', + 'preminor' = 3, // Minor version bump - 'minor', + 'minor' = 4, // Major version bump - 'major' + 'major' = 5 } /** @@ -46,47 +52,119 @@ export enum VersionPolicyDefinitionName { 'individualVersion' } +/** + * Updates the dependencies in the package json editor to values used for publishing, if needed. + * + * @returns the updated package json editor if the version format for publish is 'exact', otherwise undefined. + */ +function updateDependenciesBeforePublish( + packageName: string, + configuration: RushConfiguration, + versionFormatForPublish: VersionFormatForPublish +): PackageJsonEditor | undefined { + if (versionFormatForPublish === 'exact') { + const project: RushConfigurationProject = configuration.getProjectByName(packageName)!; + + const packageJsonEditor: PackageJsonEditor = project.packageJsonEditor; + + for (const dependency of packageJsonEditor.dependencyList) { + const rushDependencyProject: RushConfigurationProject | undefined = configuration.getProjectByName( + dependency.name + ); + + if (rushDependencyProject) { + const dependencyVersion: string = rushDependencyProject.packageJson.version; + + dependency.setVersion(dependencyVersion); + } + } + + return packageJsonEditor; + } +} + +/** + * Updates the dependencies in the package json editor to values used for checked-in source, if needed. + * + * @returns the updated package json editor if the version format for commit is 'wildcard', otherwise undefined. + */ +function updateDependenciesBeforeCommit( + packageName: string, + configuration: RushConfiguration, + versionFormatForCommit: VersionFormatForCommit +): PackageJsonEditor | undefined { + if (versionFormatForCommit === 'wildcard') { + const project: RushConfigurationProject = configuration.getProjectByName(packageName)!; + + const packageJsonEditor: PackageJsonEditor = project.packageJsonEditor; + + for (const dependency of packageJsonEditor.dependencyList) { + const rushDependencyProject: RushConfigurationProject | undefined = configuration.getProjectByName( + dependency.name + ); + + if (rushDependencyProject) { + dependency.setVersion('*'); + } + } + + return packageJsonEditor; + } +} + /** * This is the base class for version policy which controls how versions get bumped. * @public */ export abstract class VersionPolicy { - private _versionFormatForCommit: VersionFormatForCommit; - private _versionFormatForPublish: VersionFormatForPublish; + /** + * Serialized json for the policy + * + * @internal + */ + public readonly _json: IVersionPolicyJson; + + private get _versionFormatForCommit(): VersionFormatForCommit { + return this._json.dependencies?.versionFormatForCommit ?? 'original'; + } + + private get _versionFormatForPublish(): VersionFormatForPublish { + return this._json.dependencies?.versionFormatForPublish ?? 'original'; + } /** * Version policy name */ - public readonly policyName: string; + public get policyName(): string { + return this._json.policyName; + } /** * Version policy definition name */ - public readonly definitionName: VersionPolicyDefinitionName; + public get definitionName(): VersionPolicyDefinitionName { + return Enum.getValueByKey(VersionPolicyDefinitionName, this._json.definitionName); + } /** * Determines if a version policy wants to opt out of changelog files. */ - public readonly exemptFromRushChange: boolean; + public get exemptFromRushChange(): boolean { + return this._json.exemptFromRushChange ?? false; + } /** * Determines if a version policy wants to opt in to including email. */ - public readonly includeEmailInChangeFile: boolean; + public get includeEmailInChangeFile(): boolean { + return this._json.includeEmailInChangeFile ?? false; + } /** * @internal */ public constructor(versionPolicyJson: IVersionPolicyJson) { - this.policyName = versionPolicyJson.policyName; - this.definitionName = Enum.getValueByKey(VersionPolicyDefinitionName, versionPolicyJson.definitionName); - this.exemptFromRushChange = versionPolicyJson.exemptFromRushChange || false; - this.includeEmailInChangeFile = versionPolicyJson.includeEmailInChangeFile || false; - - const jsonDependencies: IVersionPolicyDependencyJson = versionPolicyJson.dependencies || {}; - this._versionFormatForCommit = jsonDependencies.versionFormatForCommit || VersionFormatForCommit.original; - this._versionFormatForPublish = - jsonDependencies.versionFormatForPublish || VersionFormatForPublish.original; + this._json = versionPolicyJson; } /** @@ -134,13 +212,6 @@ export abstract class VersionPolicy { */ public abstract bump(bumpType?: BumpType, identifier?: string): void; - /** - * Serialized json for the policy - * - * @internal - */ - public abstract get _json(): IVersionPolicyJson; - /** * Validates the specified version and throws if the version does not satisfy the policy. * @@ -150,53 +221,63 @@ export abstract class VersionPolicy { public abstract validate(versionString: string, packageName: string): void; /** - * Tells the version policy to modify any dependencies in the target package - * to values used for publishing. + * @deprecated Use {@link VersionPolicy.setDependenciesBeforePublishAsync} method instead. */ public setDependenciesBeforePublish(packageName: string, configuration: RushConfiguration): void { - if (this._versionFormatForPublish === VersionFormatForPublish.exact) { - const project: RushConfigurationProject = configuration.getProjectByName(packageName)!; + const packageJsonEditor: PackageJsonEditor | undefined = updateDependenciesBeforePublish( + packageName, + configuration, + this._versionFormatForPublish + ); - const packageJsonEditor: PackageJsonEditor = project.packageJsonEditor; + packageJsonEditor?.saveIfModified(); + } - for (const dependency of packageJsonEditor.dependencyList) { - const rushDependencyProject: RushConfigurationProject | undefined = configuration.getProjectByName( - dependency.name - ); + /** + * Tells the version policy to modify any dependencies in the target package + * to values used for publishing. + */ + public async setDependenciesBeforePublishAsync( + packageName: string, + configuration: RushConfiguration + ): Promise { + const packageJsonEditor: PackageJsonEditor | undefined = updateDependenciesBeforePublish( + packageName, + configuration, + this._versionFormatForPublish + ); - if (rushDependencyProject) { - const dependencyVersion: string = rushDependencyProject.packageJson.version; + await packageJsonEditor?.saveIfModifiedAsync(); + } - dependency.setVersion(dependencyVersion); - } - } + /** + * @deprecated Use {@link VersionPolicy.setDependenciesBeforeCommitAsync} method instead. + */ + public setDependenciesBeforeCommit(packageName: string, configuration: RushConfiguration): void { + const packageJsonEditor: PackageJsonEditor | undefined = updateDependenciesBeforeCommit( + packageName, + configuration, + this._versionFormatForCommit + ); - packageJsonEditor.saveIfModified(); - } + packageJsonEditor?.saveIfModified(); } /** * Tells the version policy to modify any dependencies in the target package * to values used for checked-in source. */ - public setDependenciesBeforeCommit(packageName: string, configuration: RushConfiguration): void { - if (this._versionFormatForCommit === VersionFormatForCommit.wildcard) { - const project: RushConfigurationProject = configuration.getProjectByName(packageName)!; - - const packageJsonEditor: PackageJsonEditor = project.packageJsonEditor; - - for (const dependency of packageJsonEditor.dependencyList) { - const rushDependencyProject: RushConfigurationProject | undefined = configuration.getProjectByName( - dependency.name - ); - - if (rushDependencyProject) { - dependency.setVersion('*'); - } - } + public async setDependenciesBeforeCommitAsync( + packageName: string, + configuration: RushConfiguration + ): Promise { + const packageJsonEditor: PackageJsonEditor | undefined = updateDependenciesBeforeCommit( + packageName, + configuration, + this._versionFormatForCommit + ); - packageJsonEditor.saveIfModified(); - } + await packageJsonEditor?.saveIfModifiedAsync(); } } @@ -205,6 +286,10 @@ export abstract class VersionPolicy { * @public */ export class LockStepVersionPolicy extends VersionPolicy { + /** + * @internal + */ + declare public readonly _json: ILockStepVersionJson; private _version: semver.SemVer; /** @@ -212,7 +297,9 @@ export class LockStepVersionPolicy extends VersionPolicy { */ // nextBump is probably not needed. It can be prerelease only. // Other types of bumps can be passed in as a parameter to bump method, so can identifier. - public readonly nextBump: BumpType | undefined; + public get nextBump(): BumpType | undefined { + return this._json.nextBump !== undefined ? Enum.getValueByKey(BumpType, this._json.nextBump) : undefined; + } /** * The main project for the version policy. @@ -220,7 +307,9 @@ export class LockStepVersionPolicy extends VersionPolicy { * If the value is provided, change logs will only be generated in that project. * If the value is not provided, change logs will be hosted in each project associated with the policy. */ - public readonly mainProject: string | undefined; + public get mainProject(): string | undefined { + return this._json.mainProject; + } /** * @internal @@ -228,11 +317,6 @@ export class LockStepVersionPolicy extends VersionPolicy { public constructor(versionPolicyJson: ILockStepVersionJson) { super(versionPolicyJson); this._version = new semver.SemVer(versionPolicyJson.version); - this.nextBump = - versionPolicyJson.nextBump !== undefined - ? Enum.getValueByKey(BumpType, versionPolicyJson.nextBump) - : undefined; - this.mainProject = versionPolicyJson.mainProject; } /** @@ -242,26 +326,6 @@ export class LockStepVersionPolicy extends VersionPolicy { return this._version.format(); } - /** - * Serialized json for this policy - * - * @internal - */ - public get _json(): ILockStepVersionJson { - const json: ILockStepVersionJson = { - policyName: this.policyName, - definitionName: VersionPolicyDefinitionName[this.definitionName], - version: this.version - }; - if (this.nextBump !== undefined) { - json.nextBump = BumpType[this.nextBump]; - } - if (this.mainProject !== undefined) { - json.mainProject = this.mainProject; - } - return json; - } - /** * Returns an updated package json that satisfies the version policy. * @@ -297,6 +361,7 @@ export class LockStepVersionPolicy extends VersionPolicy { } this._version.inc(this._getReleaseType(nextBump), identifier); + this._json.version = this.version; } /** @@ -309,6 +374,7 @@ export class LockStepVersionPolicy extends VersionPolicy { return false; } this._version = newVersion; + this._json.version = this.version; return true; } @@ -326,7 +392,7 @@ export class LockStepVersionPolicy extends VersionPolicy { } private _updatePackageVersion(project: IPackageJson, newVersion: semver.SemVer): IPackageJson { - const updatedProject: IPackageJson = lodash.cloneDeep(project); + const updatedProject: IPackageJson = cloneDeep(project); updatedProject.version = newVersion.format(); return updatedProject; } @@ -343,32 +409,22 @@ export class LockStepVersionPolicy extends VersionPolicy { */ export class IndividualVersionPolicy extends VersionPolicy { /** - * The major version that has been locked + * @internal */ - public readonly lockedMajor: number | undefined; + declare public readonly _json: IIndividualVersionJson; /** - * @internal + * The major version that has been locked */ - public constructor(versionPolicyJson: IIndividualVersionJson) { - super(versionPolicyJson); - this.lockedMajor = versionPolicyJson.lockedMajor; + public get lockedMajor(): number | undefined { + return this._json.lockedMajor; } /** - * Serialized json for this policy - * * @internal */ - public get _json(): IIndividualVersionJson { - const json: IIndividualVersionJson = { - policyName: this.policyName, - definitionName: VersionPolicyDefinitionName[this.definitionName] - }; - if (this.lockedMajor !== undefined) { - json.lockedMajor = this.lockedMajor; - } - return json; + public constructor(versionPolicyJson: IIndividualVersionJson) { + super(versionPolicyJson); } /** @@ -381,7 +437,7 @@ export class IndividualVersionPolicy extends VersionPolicy { if (this.lockedMajor) { const version: semver.SemVer = new semver.SemVer(project.version); if (version.major < this.lockedMajor) { - const updatedProject: IPackageJson = lodash.cloneDeep(project); + const updatedProject: IPackageJson = cloneDeep(project); updatedProject.version = `${this.lockedMajor}.0.0`; return updatedProject; } else if (version.major > this.lockedMajor) { diff --git a/libraries/rush-lib/src/api/VersionPolicyConfiguration.ts b/libraries/rush-lib/src/api/VersionPolicyConfiguration.ts index a4be12d280c..16d1804561c 100644 --- a/libraries/rush-lib/src/api/VersionPolicyConfiguration.ts +++ b/libraries/rush-lib/src/api/VersionPolicyConfiguration.ts @@ -3,10 +3,16 @@ import { JsonFile, JsonSchema, FileSystem } from '@rushstack/node-core-library'; -import { VersionPolicy, BumpType, LockStepVersionPolicy } from './VersionPolicy'; -import { RushConfigurationProject } from './RushConfigurationProject'; +import { VersionPolicy, type BumpType, type LockStepVersionPolicy } from './VersionPolicy'; +import type { RushConfigurationProject } from './RushConfigurationProject'; import schemaJson from '../schemas/version-policies.schema.json'; +/** + * This interface represents the raw version policy JSON object which allows repo + * maintainers to define how different groups of projects will be published by Rush, + * and how their version numbers will be determined. + * @public + */ export interface IVersionPolicyJson { policyName: string; definitionName: string; @@ -15,31 +21,49 @@ export interface IVersionPolicyJson { includeEmailInChangeFile?: boolean; } +/** + * This interface represents the raw lock-step version policy JSON object which extends the base version policy + * with additional fields specific to lock-step versioning. + * @public + */ export interface ILockStepVersionJson extends IVersionPolicyJson { version: string; nextBump?: string; mainProject?: string; } +/** + * This interface represents the raw individual version policy JSON object which extends the base version policy + * with additional fields specific to individual versioning. + * @public + */ export interface IIndividualVersionJson extends IVersionPolicyJson { lockedMajor?: number; } -export enum VersionFormatForPublish { - original = 'original', - exact = 'exact' -} +/** + * @public + */ +export type VersionFormatForPublish = 'original' | 'exact'; -export enum VersionFormatForCommit { - wildcard = 'wildcard', - original = 'original' -} +/** + * @public + */ +export type VersionFormatForCommit = 'wildcard' | 'original'; +/** + * This interface represents the `dependencies` field in a version policy JSON object, + * allowing repo maintainers to specify how dependencies' versions should be handled + * during publishing and committing. + * @public + */ export interface IVersionPolicyDependencyJson { versionFormatForPublish?: VersionFormatForPublish; versionFormatForCommit?: VersionFormatForCommit; } +const _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); + /** * Use this class to load and save the "common/config/rush/version-policies.json" config file. * This config file configures how different groups of projects will be published by Rush, @@ -47,8 +71,6 @@ export interface IVersionPolicyDependencyJson { * @public */ export class VersionPolicyConfiguration { - private static _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); - private _jsonFileName: string; /** @@ -68,7 +90,7 @@ export class VersionPolicyConfiguration { /** * Validate the version policy configuration against the rush config */ - public validate(projectsByName: Map): void { + public validate(projectsByName: ReadonlyMap): void { if (!this.versionPolicies) { return; } @@ -138,6 +160,7 @@ export class VersionPolicyConfiguration { const lockStepVersionPolicy: LockStepVersionPolicy = policy as LockStepVersionPolicy; const previousVersion: string = lockStepVersionPolicy.version; if (lockStepVersionPolicy.update(newVersion)) { + // eslint-disable-next-line no-console console.log(`\nUpdate version policy ${versionPolicyName} from ${previousVersion} to ${newVersion}`); this._saveFile(!!shouldCommit); } @@ -147,10 +170,7 @@ export class VersionPolicyConfiguration { if (!FileSystem.exists(this._jsonFileName)) { return; } - const versionPolicyJson: IVersionPolicyJson[] = JsonFile.loadAndValidate( - this._jsonFileName, - VersionPolicyConfiguration._jsonSchema - ); + const versionPolicyJson: IVersionPolicyJson[] = JsonFile.loadAndValidate(this._jsonFileName, _jsonSchema); versionPolicyJson.forEach((policyJson) => { const policy: VersionPolicy | undefined = VersionPolicy.load(policyJson); diff --git a/libraries/rush-lib/src/api/packageManager/PnpmPackageManager.ts b/libraries/rush-lib/src/api/packageManager/PnpmPackageManager.ts index a1ddd769278..d00a35c64a1 100644 --- a/libraries/rush-lib/src/api/packageManager/PnpmPackageManager.ts +++ b/libraries/rush-lib/src/api/packageManager/PnpmPackageManager.ts @@ -1,8 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import * as path from 'node:path'; + import * as semver from 'semver'; -import * as path from 'path'; import { RushConstants } from '../../logic/RushConstants'; import { PackageManager } from './PackageManager'; diff --git a/libraries/rush-lib/src/api/test/ChangeFile.test.ts b/libraries/rush-lib/src/api/test/ChangeFile.test.ts index bbe1b9797fe..8df693df8b0 100644 --- a/libraries/rush-lib/src/api/test/ChangeFile.test.ts +++ b/libraries/rush-lib/src/api/test/ChangeFile.test.ts @@ -1,11 +1,42 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import type { GitRepoInfo } from 'git-repo-info'; + import { ChangeFile } from '../ChangeFile'; import { RushConfiguration } from '../RushConfiguration'; import { ChangeType } from '../ChangeManagement'; +import { Git } from '../../logic/Git'; describe(ChangeFile.name, () => { + it('generates a path that includes seconds so repeated invocations do not collide', () => { + const rushFilename: string = `${__dirname}/repo/rush-npm.json`; + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile(rushFilename); + + // Pin the branch name so the generated filename is deterministic. + jest.spyOn(Git.prototype, 'getGitInfo').mockReturnValue({ branch: 'my-branch' } as Readonly); + + // Pin the clock to 2017-05-01 20:20:30 UTC so the timestamp is deterministic. + jest.useFakeTimers({ + now: new Date('2017-05-01T20:20:30.000Z').getTime() + }); + + const changeFile: ChangeFile = new ChangeFile( + { + packageName: 'a', + changes: [], + email: 'fake@example.com' + }, + rushConfiguration + ); + + const generatedPath: string = changeFile.generatePath(); + // The seconds must be present and the filename must be fully dash-separated + // (no leftover colons from the time portion). + // Check toContain on the forward-slash-normalised path so it works on Windows too. + expect(generatedPath.replace(/\\/g, '/').endsWith('my-branch_2017-05-01-20-20-30.json')).toBe(true); + }); + it('can add a change', () => { const rushFilename: string = `${__dirname}/repo/rush-npm.json`; const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile(rushFilename); diff --git a/libraries/rush-lib/src/api/test/CommandLineConfiguration.test.ts b/libraries/rush-lib/src/api/test/CommandLineConfiguration.test.ts index 7bb8fe2d795..6cd9360ed1b 100644 --- a/libraries/rush-lib/src/api/test/CommandLineConfiguration.test.ts +++ b/libraries/rush-lib/src/api/test/CommandLineConfiguration.test.ts @@ -3,11 +3,11 @@ import { RushConstants } from '../../logic/RushConstants'; import { - IPhasedCommandConfig, + type IPhasedCommandConfig, CommandLineConfiguration, - IParameterJson, - IPhase, - Command + type IParameterJson, + type IPhase, + type Command } from '../CommandLineConfiguration'; describe(CommandLineConfiguration.name, () => { diff --git a/libraries/rush-lib/src/api/test/CommonVersionsConfiguration.test.ts b/libraries/rush-lib/src/api/test/CommonVersionsConfiguration.test.ts index d9cb1d3b2ef..ec42d471194 100644 --- a/libraries/rush-lib/src/api/test/CommonVersionsConfiguration.test.ts +++ b/libraries/rush-lib/src/api/test/CommonVersionsConfiguration.test.ts @@ -2,13 +2,53 @@ // See LICENSE in the project root for license information. import { CommonVersionsConfiguration } from '../CommonVersionsConfiguration'; +import type { RushConfiguration } from '../RushConfiguration'; describe(CommonVersionsConfiguration.name, () => { - it('can load the file', () => { + it('can load the file', async () => { const filename: string = `${__dirname}/jsonFiles/common-versions.json`; - const configuration: CommonVersionsConfiguration = CommonVersionsConfiguration.loadFromFile(filename); + const configuration: CommonVersionsConfiguration = await CommonVersionsConfiguration.loadFromFileAsync( + filename, + {} as RushConfiguration + ); expect(configuration.preferredVersions.get('@scope/library-1')).toEqual('~3.2.1'); expect(configuration.allowedAlternativeVersions.get('library-3')).toEqual(['^1.2.3']); }); + + it('gets `ensureConsistentVersions` from the file if it provides that value', async () => { + const filename: string = `${__dirname}/jsonFiles/common-versions-with-ensureConsistentVersionsTrue.json`; + const configuration: CommonVersionsConfiguration = await CommonVersionsConfiguration.loadFromFileAsync( + filename, + { + _ensureConsistentVersionsJsonValue: undefined, + ensureConsistentVersions: false + } as RushConfiguration + ); + + expect(configuration.ensureConsistentVersions).toBe(true); + }); + + it("gets `ensureConsistentVersions` from the rush configuration if common-versions.json doesn't provide that value", async () => { + const filename: string = `${__dirname}/jsonFiles/common-versions.json`; + const configuration: CommonVersionsConfiguration = await CommonVersionsConfiguration.loadFromFileAsync( + filename, + { + _ensureConsistentVersionsJsonValue: false, + ensureConsistentVersions: false + } as RushConfiguration + ); + + expect(configuration.ensureConsistentVersions).toBe(false); + }); + + it('Does not allow `ensureConsistentVersions` to be set in both rush.json and common-versions.json', async () => { + const filename: string = `${__dirname}/jsonFiles/common-versions-with-ensureConsistentVersionsTrue.json`; + await expect(() => + CommonVersionsConfiguration.loadFromFileAsync(filename, { + _ensureConsistentVersionsJsonValue: false, + ensureConsistentVersions: false + } as RushConfiguration) + ).rejects.toThrowErrorMatchingSnapshot(); + }); }); diff --git a/libraries/rush-lib/src/api/test/CustomTipsConfiguration.test.ts b/libraries/rush-lib/src/api/test/CustomTipsConfiguration.test.ts new file mode 100644 index 00000000000..bc3d7fb1432 --- /dev/null +++ b/libraries/rush-lib/src/api/test/CustomTipsConfiguration.test.ts @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { JsonFile } from '@rushstack/node-core-library'; +import { + type IOutputChunk, + PrintUtilities, + StringBufferTerminalProvider, + Terminal +} from '@rushstack/terminal'; + +import { CustomTipId, CustomTipsConfiguration, type ICustomTipsJson } from '../CustomTipsConfiguration'; +import { RushConfiguration } from '../RushConfiguration'; + +const LOREM: string = + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'; + +describe(CustomTipsConfiguration.name, () => { + it('loads the config file (custom-tips.json)', () => { + const rushFilename: string = `${__dirname}/repo/rush-npm.json`; + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile(rushFilename); + expect(rushConfiguration.customTipsConfiguration.providedCustomTipsByTipId).toMatchSnapshot(); + }); + + it('reports an error for duplicate tips', () => { + expect(() => { + new CustomTipsConfiguration(`${__dirname}/jsonFiles/custom-tips.error.json`); + }).toThrow('TIP_RUSH_INCONSISTENT_VERSIONS'); + }); + + function runFormattingTests(testName: string, customTipText: string): void { + describe(`formatting (${testName})`, () => { + let customTipsConfiguration: CustomTipsConfiguration; + let terminalProvider: StringBufferTerminalProvider; + let terminal: Terminal; + + const CUSTOM_TIP_FOR_TESTING: CustomTipId = CustomTipId.TIP_PNPM_INVALID_NODE_VERSION; + + beforeEach(() => { + terminalProvider = new StringBufferTerminalProvider(true); + terminal = new Terminal(terminalProvider); + + const mockCustomTipsJson: ICustomTipsJson = { + customTips: [ + { + tipId: CUSTOM_TIP_FOR_TESTING, + message: customTipText + } + ] + }; + jest.spyOn(JsonFile, 'loadAndValidate').mockReturnValue(mockCustomTipsJson); + customTipsConfiguration = new CustomTipsConfiguration(''); + + jest.spyOn(PrintUtilities, 'getConsoleWidth').mockReturnValue(60); + }); + + afterEach(() => { + jest.restoreAllMocks(); + + const terminalProviderOutput: IOutputChunk[] = terminalProvider.getAllOutputAsChunks(); + const lineSplitTerminalProviderOutput: string[] = []; + for (const { text, severity } of terminalProviderOutput) { + const lines: string[] = text.split('[n]'); + for (const line of lines) { + lineSplitTerminalProviderOutput.push(`[${severity}] ${line}`); + } + } + + expect(lineSplitTerminalProviderOutput).toMatchSnapshot(); + }); + + const printFunctions = [ + CustomTipsConfiguration.prototype._showTip, + CustomTipsConfiguration.prototype._showInfoTip, + CustomTipsConfiguration.prototype._showWarningTip, + CustomTipsConfiguration.prototype._showErrorTip + ]; + + for (const printFunction of printFunctions) { + it(`${printFunction.name} prints an expected message`, () => { + printFunction.call(customTipsConfiguration, terminal, CUSTOM_TIP_FOR_TESTING); + }); + } + }); + } + + runFormattingTests('a short message', 'This is a test'); + runFormattingTests('a long message', LOREM); + runFormattingTests('a message with newlines', 'This is a test\nThis is a test'); + runFormattingTests('a message with an indented line', 'This is a test\n This is a test'); + runFormattingTests('a long message with an indented line', `${LOREM}\n ${LOREM}`); +}); diff --git a/libraries/rush-lib/src/api/test/EnvironmentConfiguration.test.ts b/libraries/rush-lib/src/api/test/EnvironmentConfiguration.test.ts index 83ebc055b5a..78f597c2a3d 100644 --- a/libraries/rush-lib/src/api/test/EnvironmentConfiguration.test.ts +++ b/libraries/rush-lib/src/api/test/EnvironmentConfiguration.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; import { EnvironmentConfiguration } from '../EnvironmentConfiguration'; describe(EnvironmentConfiguration.name, () => { diff --git a/libraries/rush-lib/src/api/test/FlagFile.test.ts b/libraries/rush-lib/src/api/test/FlagFile.test.ts new file mode 100644 index 00000000000..017c648aeb1 --- /dev/null +++ b/libraries/rush-lib/src/api/test/FlagFile.test.ts @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import { FileSystem } from '@rushstack/node-core-library'; + +import { FlagFile } from '../FlagFile'; +import { RushConstants } from '../../logic/RushConstants'; + +const TEMP_DIR_PATH: string = `${__dirname}/temp`; + +describe(FlagFile.name, () => { + beforeEach(() => { + FileSystem.ensureEmptyFolder(TEMP_DIR_PATH); + }); + + afterEach(() => { + FileSystem.ensureEmptyFolder(TEMP_DIR_PATH); + }); + + it('can get correct path', () => { + const flag: FlagFile = new FlagFile(TEMP_DIR_PATH, RushConstants.lastLinkFlagFilename, {}); + expect(path.basename(flag.path)).toEqual(RushConstants.lastLinkFlagFilename + '.flag'); + }); +}); diff --git a/libraries/rush-lib/src/api/test/LastInstallFlag.test.ts b/libraries/rush-lib/src/api/test/LastInstallFlag.test.ts index 3b953a7c58f..11aa23f09a5 100644 --- a/libraries/rush-lib/src/api/test/LastInstallFlag.test.ts +++ b/libraries/rush-lib/src/api/test/LastInstallFlag.test.ts @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; import { FileSystem } from '@rushstack/node-core-library'; -import { LastInstallFlag, LAST_INSTALL_FLAG_FILE_NAME } from '../LastInstallFlag'; +import { LastInstallFlag } from '../LastInstallFlag'; const TEMP_DIR_PATH: string = `${__dirname}/temp`; @@ -19,64 +19,64 @@ describe(LastInstallFlag.name, () => { it('can get correct path', () => { const flag: LastInstallFlag = new LastInstallFlag(TEMP_DIR_PATH); - expect(path.basename(flag.path)).toEqual(LAST_INSTALL_FLAG_FILE_NAME); + expect(path.basename(flag.path)).toMatchInlineSnapshot(`"last-install.flag"`); }); - it('can create and remove a flag in an empty directory', () => { + it('can create and remove a flag in an empty directory', async () => { // preparation const flag: LastInstallFlag = new LastInstallFlag(TEMP_DIR_PATH); FileSystem.deleteFile(flag.path); // test state, should be invalid since the file doesn't exist - expect(flag.isValid()).toEqual(false); + await expect(flag.isValidAsync()).resolves.toEqual(false); // test creation - flag.create(); + await flag.createAsync(); expect(FileSystem.exists(flag.path)).toEqual(true); - expect(flag.isValid()).toEqual(true); + await expect(flag.isValidAsync()).resolves.toEqual(true); // test deletion - flag.clear(); + await flag.clearAsync(); expect(FileSystem.exists(flag.path)).toEqual(false); - expect(flag.isValid()).toEqual(false); + await expect(flag.isValidAsync()).resolves.toEqual(false); }); - it('can detect if the last flag was in a different state', () => { + it('can detect if the last flag was in a different state', async () => { // preparation const flag1: LastInstallFlag = new LastInstallFlag(TEMP_DIR_PATH, { node: '5.0.0' }); const flag2: LastInstallFlag = new LastInstallFlag(TEMP_DIR_PATH, { node: '8.9.4' }); FileSystem.deleteFile(flag1.path); // test state, should be invalid since the file doesn't exist - expect(flag1.isValid()).toEqual(false); - expect(flag2.isValid()).toEqual(false); + await expect(flag1.isValidAsync()).resolves.toEqual(false); + await expect(flag2.isValidAsync()).resolves.toEqual(false); // test creation - flag1.create(); + await flag1.createAsync(); expect(FileSystem.exists(flag1.path)).toEqual(true); - expect(flag1.isValid()).toEqual(true); + await expect(flag1.isValidAsync()).resolves.toEqual(true); // the second flag has different state and should be invalid - expect(flag2.isValid()).toEqual(false); + await expect(flag2.isValidAsync()).resolves.toEqual(false); // test deletion - flag1.clear(); + await flag1.clearAsync(); expect(FileSystem.exists(flag1.path)).toEqual(false); - expect(flag1.isValid()).toEqual(false); - expect(flag2.isValid()).toEqual(false); + await expect(flag1.isValidAsync()).resolves.toEqual(false); + await expect(flag2.isValidAsync()).resolves.toEqual(false); }); - it('can detect if the last flag was in a corrupted state', () => { + it('can detect if the last flag was in a corrupted state', async () => { // preparation, write non-json into flag file const flag: LastInstallFlag = new LastInstallFlag(TEMP_DIR_PATH); FileSystem.writeFile(flag.path, 'sdfjkaklfjksldajgfkld'); // test state, should be invalid since the file is not JSON - expect(flag.isValid()).toEqual(false); + await expect(flag.isValidAsync()).resolves.toEqual(false); FileSystem.deleteFile(flag.path); }); - it("throws an error if new storePath doesn't match the old one", () => { + it("throws an error if new storePath doesn't match the old one", async () => { const flag1: LastInstallFlag = new LastInstallFlag(TEMP_DIR_PATH, { packageManager: 'pnpm', storePath: `${TEMP_DIR_PATH}/pnpm-store` @@ -86,13 +86,13 @@ describe(LastInstallFlag.name, () => { storePath: `${TEMP_DIR_PATH}/temp-store` }); - flag1.create(); - expect(() => { - flag2.checkValidAndReportStoreIssues({ rushVerb: 'install' }); - }).toThrowError(/PNPM store path/); + await flag1.createAsync(); + await expect(async () => { + await flag2.checkValidAndReportStoreIssuesAsync({ rushVerb: 'install' }); + }).rejects.toThrow(/PNPM store path/); }); - it("doesn't throw an error if conditions for error aren't met", () => { + it("doesn't throw an error if conditions for error aren't met", async () => { const flag1: LastInstallFlag = new LastInstallFlag(TEMP_DIR_PATH, { packageManager: 'pnpm', storePath: `${TEMP_DIR_PATH}/pnpm-store` @@ -101,27 +101,8 @@ describe(LastInstallFlag.name, () => { packageManager: 'npm' }); - flag1.create(); - expect(() => { - flag2.checkValidAndReportStoreIssues({ rushVerb: 'install' }); - }).not.toThrow(); - expect(flag2.checkValidAndReportStoreIssues({ rushVerb: 'install' })).toEqual(false); - }); - - it("ignores a specified option that doesn't match", () => { - const flag1: LastInstallFlag = new LastInstallFlag(TEMP_DIR_PATH, { - option1: 'a', - option2: 'b' - }); - const flag2: LastInstallFlag = new LastInstallFlag(TEMP_DIR_PATH, { - option1: 'a', - option2: 'c' - }); - - flag1.create(); - expect(() => { - flag2.isValid({ statePropertiesToIgnore: ['option2'] }); - }).not.toThrow(); - expect(flag2.isValid({ statePropertiesToIgnore: ['option2'] })).toEqual(true); + await flag1.createAsync(); + await expect(flag2.checkValidAndReportStoreIssuesAsync({ rushVerb: 'install' })).resolves.not.toThrow(); + await expect(flag2.checkValidAndReportStoreIssuesAsync({ rushVerb: 'install' })).resolves.toEqual(false); }); }); diff --git a/libraries/rush-lib/src/api/test/LastLinkFlag.test.ts b/libraries/rush-lib/src/api/test/LastLinkFlag.test.ts deleted file mode 100644 index 038854dc1db..00000000000 --- a/libraries/rush-lib/src/api/test/LastLinkFlag.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import * as path from 'path'; -import { FileSystem } from '@rushstack/node-core-library'; - -import { LastLinkFlag, LAST_LINK_FLAG_FILE_NAME } from '../LastLinkFlag'; - -const TEMP_DIR_PATH: string = `${__dirname}/temp`; - -describe(LastLinkFlag.name, () => { - beforeEach(() => { - FileSystem.ensureEmptyFolder(TEMP_DIR_PATH); - }); - - afterEach(() => { - FileSystem.ensureEmptyFolder(TEMP_DIR_PATH); - }); - - it('can get correct path', () => { - const flag: LastLinkFlag = new LastLinkFlag(TEMP_DIR_PATH); - expect(path.basename(flag.path)).toEqual(LAST_LINK_FLAG_FILE_NAME); - }); -}); diff --git a/libraries/rush-lib/src/api/test/RushCommandLine.test.ts b/libraries/rush-lib/src/api/test/RushCommandLine.test.ts new file mode 100644 index 00000000000..4f0c95f6022 --- /dev/null +++ b/libraries/rush-lib/src/api/test/RushCommandLine.test.ts @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; + +import { RushCommandLine } from '../RushCommandLine'; + +describe(RushCommandLine.name, () => { + it(`Returns a spec`, async () => { + const spec = RushCommandLine.getCliSpec(path.resolve(__dirname, '../../cli/test/repo/')); + expect(spec).toMatchSnapshot(); + }); +}); diff --git a/libraries/rush-lib/src/api/test/RushConfiguration.test.ts b/libraries/rush-lib/src/api/test/RushConfiguration.test.ts index a8cccb9b227..039dbb7df31 100644 --- a/libraries/rush-lib/src/api/test/RushConfiguration.test.ts +++ b/libraries/rush-lib/src/api/test/RushConfiguration.test.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; import { JsonFile, Path, Text } from '@rushstack/node-core-library'; import { RushConfiguration } from '../RushConfiguration'; -import { ApprovedPackagesPolicy } from '../ApprovedPackagesPolicy'; +import type { ApprovedPackagesPolicy } from '../ApprovedPackagesPolicy'; import { RushConfigurationProject } from '../RushConfigurationProject'; import { EnvironmentConfiguration } from '../EnvironmentConfiguration'; import { DependencyType } from '../PackageJsonEditor'; @@ -48,11 +48,6 @@ describe(RushConfiguration.name, () => { const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile(rushFilename); expect(rushConfiguration.packageManager).toEqual('npm'); - assertPathProperty( - 'committedShrinkwrapFilename', - rushConfiguration.committedShrinkwrapFilename, - './repo/common/config/rush/npm-shrinkwrap.json' - ); assertPathProperty('commonFolder', rushConfiguration.commonFolder, './repo/common'); assertPathProperty( 'commonRushConfigFolder', @@ -82,7 +77,7 @@ describe(RushConfiguration.name, () => { expect(rushConfiguration.projectFolderMinDepth).toEqual(1); expect(rushConfiguration.hotfixChangeEnabled).toEqual(true); - expect(rushConfiguration.projects).toHaveLength(3); + expect(rushConfiguration.projects).toHaveLength(5); // "approvedPackagesPolicy" feature const approvedPackagesPolicy: ApprovedPackagesPolicy = rushConfiguration.approvedPackagesPolicy; @@ -121,14 +116,9 @@ describe(RushConfiguration.name, () => { expect(rushConfiguration.packageManager).toEqual('pnpm'); expect(rushConfiguration.shrinkwrapFilename).toEqual('pnpm-lock.yaml'); - assertPathProperty( - 'committedShrinkwrapFilename', - rushConfiguration.getCommittedShrinkwrapFilename(), - './repo/common/config/rush/pnpm-lock.yaml' - ); assertPathProperty( 'getPnpmfilePath', - rushConfiguration.getPnpmfilePath(), + rushConfiguration.defaultSubspace.getPnpmfilePath(undefined), './repo/common/config/rush/.pnpmfile.cjs' ); assertPathProperty('commonFolder', rushConfiguration.commonFolder, './repo/common'); @@ -195,7 +185,7 @@ describe(RushConfiguration.name, () => { expect(rushConfiguration.shrinkwrapFilename).toEqual('pnpm-lock.yaml'); assertPathProperty( 'getPnpmfilePath', - rushConfiguration.getPnpmfilePath(), + rushConfiguration.defaultSubspace.getPnpmfilePath(undefined), './repo/common/config/rush/pnpmfile.js' ); expect(rushConfiguration.repositoryUrls).toEqual(['someFakeUrl', 'otherFakeUrl']); @@ -236,7 +226,7 @@ describe(RushConfiguration.name, () => { describe('PNPM Store Paths', () => { afterEach(() => { - EnvironmentConfiguration['_pnpmStorePathOverride'] = undefined; + EnvironmentConfiguration.reset(); }); const PNPM_STORE_PATH_ENV: string = 'RUSH_PNPM_STORE_PATH'; @@ -322,7 +312,7 @@ describe(RushConfiguration.name, () => { const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile( `${__dirname}/repo/rush-pnpm.json` ); - jest.spyOn(JsonFile, 'save').mockImplementation(() => { + jest.spyOn(JsonFile, 'saveAsync').mockImplementation(async () => { /* no-op*/ return true; }); @@ -334,7 +324,7 @@ describe(RushConfiguration.name, () => { 'dependencyProjects before' ); project.packageJsonEditor.addOrUpdateDependency('project2', '1.0.0', DependencyType.Dev); - project.packageJsonEditor.saveIfModified(); + await project.packageJsonEditor.saveIfModifiedAsync(); expect(project.packageJson.devDependencies).toMatchSnapshot('devDependencies after'); expect(Array.from(project.dependencyProjects.values()).map((x) => x.packageName)).toMatchSnapshot( 'dependencyProjects after' diff --git a/libraries/rush-lib/src/api/test/RushConfigurationProject.test.ts b/libraries/rush-lib/src/api/test/RushConfigurationProject.test.ts new file mode 100644 index 00000000000..c5da537283c --- /dev/null +++ b/libraries/rush-lib/src/api/test/RushConfigurationProject.test.ts @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { validateRelativePathField } from '../RushConfigurationProject'; + +describe(validateRelativePathField.name, () => { + it('accepts valid paths', () => { + validateRelativePathField('path/to/project', 'projectFolder', '/rush.json'); + validateRelativePathField('project', 'projectFolder', '/rush.json'); + validateRelativePathField('.', 'projectFolder', '/rush.json'); + validateRelativePathField('..', 'projectFolder', '/rush.json'); + validateRelativePathField('../path/to/project', 'projectFolder', '/rush.json'); + }); + + it('should throw an error if the path is not relative', () => { + expect(() => + validateRelativePathField('C:/path/to/project', 'projectFolder', '/rush.json') + ).toThrowErrorMatchingSnapshot(); + expect(() => + validateRelativePathField('/path/to/project', 'publishFolder', '/rush.json') + ).toThrowErrorMatchingSnapshot(); + }); + + it('should throw an error if the path ends in a trailing slash', () => { + expect(() => + validateRelativePathField('path/to/project/', 'someField', '/repo/rush.json') + ).toThrowErrorMatchingSnapshot(); + expect(() => + validateRelativePathField('p/', 'someField', '/repo/rush.json') + ).toThrowErrorMatchingSnapshot(); + }); + + it('should throw an error if the path contains backslashes', () => { + expect(() => + validateRelativePathField('path\\to\\project', 'someField', '/repo/rush.json') + ).toThrowErrorMatchingSnapshot(); + expect(() => + validateRelativePathField('path\\', 'someOtherField', '/repo/rush.json') + ).toThrowErrorMatchingSnapshot(); + }); + + it('should throw an error if the path is not normalized', () => { + expect(() => + validateRelativePathField('path/../to/project', 'someField', '/repo/rush.json') + ).toThrowErrorMatchingSnapshot(); + expect(() => + validateRelativePathField('path/./to/project', 'someField', '/repo/rush.json') + ).toThrowErrorMatchingSnapshot(); + expect(() => + validateRelativePathField('./path/to/project', 'someField', '/repo/rush.json') + ).toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/libraries/rush-lib/src/api/test/RushProjectConfiguration.test.ts b/libraries/rush-lib/src/api/test/RushProjectConfiguration.test.ts index 9d737bd9ac5..76ecd423cc3 100644 --- a/libraries/rush-lib/src/api/test/RushProjectConfiguration.test.ts +++ b/libraries/rush-lib/src/api/test/RushProjectConfiguration.test.ts @@ -1,6 +1,11 @@ -import { StringBufferTerminalProvider, Terminal } from '@rushstack/node-core-library'; -import { IPhase } from '../CommandLineConfiguration'; -import { RushConfigurationProject } from '../RushConfigurationProject'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; +import type { CommandLineParameter } from '@rushstack/ts-command-line'; + +import type { IPhase } from '../CommandLineConfiguration'; +import type { RushConfigurationProject } from '../RushConfigurationProject'; import { RushProjectConfiguration } from '../RushProjectConfiguration'; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -23,7 +28,8 @@ async function loadProjectConfigurationAsync( const testFolder: string = `${__dirname}/jsonFiles/${testProjectName}`; const rushProject: RushConfigurationProject = { packageName: testProjectName, - projectFolder: testFolder + projectFolder: testFolder, + projectRelativeFolder: testProjectName } as RushConfigurationProject; const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); try { @@ -52,15 +58,39 @@ function validateConfiguration(rushProjectConfiguration: RushProjectConfiguratio try { rushProjectConfiguration.validatePhaseConfiguration( Array.from(rushProjectConfiguration.operationSettingsByOperationName.keys()).map( - (phaseName) => ({ name: phaseName } as IPhase) + (phaseName) => ({ name: phaseName, associatedParameters: new Set() }) as IPhase ), terminal ); } finally { - expect(terminalProvider.getOutput()).toMatchSnapshot('validation: terminal output'); - expect(terminalProvider.getErrorOutput()).toMatchSnapshot('validation: terminal error'); - expect(terminalProvider.getWarningOutput()).toMatchSnapshot('validation: terminal warning'); - expect(terminalProvider.getVerbose()).toMatchSnapshot('validation: terminal verbose'); + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); + } + } +} + +function validateConfigurationWithParameters( + rushProjectConfiguration: RushProjectConfiguration | undefined, + parameterNames: string[] +): void { + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(); + const terminal: Terminal = new Terminal(terminalProvider); + + if (rushProjectConfiguration) { + try { + // Create mock parameters with the specified names + const mockParameters = new Set( + parameterNames.map((name) => ({ longName: name }) as CommandLineParameter) + ); + + rushProjectConfiguration.validatePhaseConfiguration( + Array.from( + rushProjectConfiguration.operationSettingsByOperationName.keys(), + (phaseName) => ({ name: phaseName, associatedParameters: mockParameters }) as IPhase + ), + terminal + ); + } finally { + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); } } } @@ -76,14 +106,9 @@ describe(RushProjectConfiguration.name, () => { }); it('throws an error when loading a rush-project.json config that lists an operation twice', async () => { - let errorMessage: string | undefined; - try { - await loadProjectConfigurationAsync('test-project-b'); - } catch (e) { - errorMessage = (e as Error).message; - } - - expect(errorMessage).toMatchSnapshot(); + await expect( + async () => await loadProjectConfigurationAsync('test-project-b') + ).rejects.toThrowErrorMatchingSnapshot(); }); it('allows outputFolderNames to be inside subfolders', async () => { @@ -98,14 +123,124 @@ describe(RushProjectConfiguration.name, () => { const rushProjectConfiguration: RushProjectConfiguration | undefined = await loadProjectConfigurationAsync('test-project-d'); - let errorWasThrown: boolean = false; - try { - validateConfiguration(rushProjectConfiguration); - } catch (e) { - errorWasThrown = true; + expect(() => validateConfiguration(rushProjectConfiguration)).toThrow(); + }); + + it('validates that parameters in parameterNamesToIgnore exist for the operation', async () => { + const rushProjectConfiguration: RushProjectConfiguration | undefined = + await loadProjectConfigurationAsync('test-project-e'); + + expect(() => validateConfiguration(rushProjectConfiguration)).toThrow(); + }); + + it('validates nonexistent parameters when operation has valid parameters', async () => { + const rushProjectConfiguration: RushProjectConfiguration | undefined = + await loadProjectConfigurationAsync('test-project-f'); + + // Provide some valid parameters for the operation + expect(() => + validateConfigurationWithParameters(rushProjectConfiguration, ['--production', '--verbose']) + ).toThrow(); + }); + + it('validates mix of existent and nonexistent parameters', async () => { + const rushProjectConfiguration: RushProjectConfiguration | undefined = + await loadProjectConfigurationAsync('test-project-g'); + + // Provide some valid parameters, test-project-g references both valid and invalid ones + expect(() => + validateConfigurationWithParameters(rushProjectConfiguration, ['--production', '--verbose']) + ).toThrow(); + }); + }); + + describe(RushProjectConfiguration.prototype.getCacheDisabledReason.name, () => { + it('Indicates if the build cache is completely disabled', async () => { + const config: RushProjectConfiguration | undefined = + await loadProjectConfigurationAsync('test-project-a'); + + if (!config) { + throw new Error('Failed to load config'); + } + + const reason: string | undefined = config.getCacheDisabledReason([], 'z', false); + expect(reason).toMatchSnapshot(); + }); + + it('Indicates if the phase behavior is not defined', async () => { + const config: RushProjectConfiguration | undefined = + await loadProjectConfigurationAsync('test-project-c'); + + if (!config) { + throw new Error('Failed to load config'); + } + + const reason: string | undefined = config.getCacheDisabledReason([], 'z', false); + expect(reason).toMatchSnapshot(); + }); + + it('Indicates if the phase has disabled the cache', async () => { + const config: RushProjectConfiguration | undefined = + await loadProjectConfigurationAsync('test-project-c'); + + if (!config) { + throw new Error('Failed to load config'); + } + + const reason: string | undefined = config.getCacheDisabledReason([], '_phase:a', false); + expect(reason).toMatchSnapshot(); + }); + + it('Indicates if tracked files are outputs of the phase', async () => { + const config: RushProjectConfiguration | undefined = + await loadProjectConfigurationAsync('test-project-c'); + + if (!config) { + throw new Error('Failed to load config'); + } + + const reason: string | undefined = config.getCacheDisabledReason( + ['test-project-c/.cache/b/foo'], + '_phase:b', + false + ); + expect(reason).toMatchSnapshot(); + }); + + it('returns undefined if the config is safe', async () => { + const config: RushProjectConfiguration | undefined = + await loadProjectConfigurationAsync('test-project-c'); + + if (!config) { + throw new Error('Failed to load config'); + } + + const reason: string | undefined = config.getCacheDisabledReason([''], '_phase:b', false); + expect(reason).toBeUndefined(); + }); + + it('returns undefined if the operation is a no-op', async () => { + const config: RushProjectConfiguration | undefined = + await loadProjectConfigurationAsync('test-project-c'); + + if (!config) { + throw new Error('Failed to load config'); + } + + const reason: string | undefined = config.getCacheDisabledReason([''], '_phase:b', true); + expect(reason).toBeUndefined(); + }); + + it('returns reason if the operation is runnable', async () => { + const config: RushProjectConfiguration | undefined = + await loadProjectConfigurationAsync('test-project-c'); + + if (!config) { + throw new Error('Failed to load config'); } - expect(errorWasThrown).toBe(true); + const reason: string | undefined = config.getCacheDisabledReason([], '_phase:a', false); + expect(reason).toMatchSnapshot(); }); }); }); diff --git a/libraries/rush-lib/src/api/test/Subspace.test.ts b/libraries/rush-lib/src/api/test/Subspace.test.ts new file mode 100644 index 00000000000..4087b2b9716 --- /dev/null +++ b/libraries/rush-lib/src/api/test/Subspace.test.ts @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { RushConfiguration } from '../RushConfiguration'; +import { Subspace } from '../Subspace'; + +describe(Subspace.name, () => { + describe('getPnpmCatalogsHash', () => { + it('returns undefined when no catalogs are defined', () => { + const rushJsonFilename: string = path.resolve(__dirname, 'repo', 'rush-pnpm.json'); + const rushConfiguration: RushConfiguration = + RushConfiguration.loadFromConfigurationFile(rushJsonFilename); + const defaultSubspace: Subspace = rushConfiguration.defaultSubspace; + + const catalogsHash: string | undefined = defaultSubspace.getPnpmCatalogsHash(); + expect(catalogsHash).toBeUndefined(); + }); + + it('returns undefined for non-pnpm package manager', () => { + const rushJsonFilename: string = path.resolve(__dirname, 'repo', 'rush-npm.json'); + const rushConfiguration: RushConfiguration = + RushConfiguration.loadFromConfigurationFile(rushJsonFilename); + const defaultSubspace: Subspace = rushConfiguration.defaultSubspace; + + const catalogsHash: string | undefined = defaultSubspace.getPnpmCatalogsHash(); + expect(catalogsHash).toBeUndefined(); + }); + + it('computes hash when catalogs are defined', () => { + const rushJsonFilename: string = path.resolve(__dirname, 'repoCatalogs', 'rush.json'); + const rushConfiguration: RushConfiguration = + RushConfiguration.loadFromConfigurationFile(rushJsonFilename); + const defaultSubspace: Subspace = rushConfiguration.defaultSubspace; + + const catalogsHash: string | undefined = defaultSubspace.getPnpmCatalogsHash(); + expect(catalogsHash).toBeDefined(); + expect(typeof catalogsHash).toBe('string'); + expect(catalogsHash).toHaveLength(40); // SHA1 hash is 40 characters + }); + + it('computes consistent hash for same catalog data', () => { + const rushJsonFilename: string = path.resolve(__dirname, 'repoCatalogs', 'rush.json'); + const rushConfiguration: RushConfiguration = + RushConfiguration.loadFromConfigurationFile(rushJsonFilename); + const defaultSubspace: Subspace = rushConfiguration.defaultSubspace; + + const hash1: string | undefined = defaultSubspace.getPnpmCatalogsHash(); + const hash2: string | undefined = defaultSubspace.getPnpmCatalogsHash(); + + expect(hash1).toBeDefined(); + expect(hash1).toBe(hash2); + }); + + it('computes different hashes for different catalog data', () => { + // Configuration without catalogs + const rushJsonWithoutCatalogs: string = path.resolve(__dirname, 'repo', 'rush-pnpm.json'); + const rushConfigWithoutCatalogs: RushConfiguration = + RushConfiguration.loadFromConfigurationFile(rushJsonWithoutCatalogs); + const subspaceWithoutCatalogs: Subspace = rushConfigWithoutCatalogs.defaultSubspace; + + // Configuration with catalogs + const rushJsonWithCatalogs: string = path.resolve(__dirname, 'repoCatalogs', 'rush.json'); + const rushConfigWithCatalogs: RushConfiguration = + RushConfiguration.loadFromConfigurationFile(rushJsonWithCatalogs); + const subspaceWithCatalogs: Subspace = rushConfigWithCatalogs.defaultSubspace; + + const hashWithoutCatalogs: string | undefined = subspaceWithoutCatalogs.getPnpmCatalogsHash(); + const hashWithCatalogs: string | undefined = subspaceWithCatalogs.getPnpmCatalogsHash(); + + // One should be undefined (no catalogs) and one should have a hash + expect(hashWithoutCatalogs).toBeUndefined(); + expect(hashWithCatalogs).toBeDefined(); + }); + }); + + describe(Subspace.prototype.getPackageJsonInjectedDependenciesHash.name, () => { + it('returns undefined when no injected dependencies exist', () => { + const rushJsonFilename: string = `${__dirname}/repo/rush-pnpm.json`; + const rushConfiguration: RushConfiguration = + RushConfiguration.loadFromConfigurationFile(rushJsonFilename); + const defaultSubspace: Subspace = rushConfiguration.defaultSubspace; + + const hash: string | undefined = defaultSubspace.getPackageJsonInjectedDependenciesHash(); + expect(hash).toBeUndefined(); + }); + + it('computes a hash when injected dependencies exist', () => { + const rushJsonFilename: string = `${__dirname}/repoInjectedDeps/rush.json`; + const rushConfiguration: RushConfiguration = + RushConfiguration.loadFromConfigurationFile(rushJsonFilename); + const defaultSubspace: Subspace = rushConfiguration.defaultSubspace; + + const hash: string | undefined = defaultSubspace.getPackageJsonInjectedDependenciesHash(); + expect(hash).toMatchSnapshot(); + }); + + it('does not change when devDependencies of the injected package change', () => { + const rushJsonFilename: string = `${__dirname}/repoInjectedDeps/rush.json`; + const rushConfiguration: RushConfiguration = + RushConfiguration.loadFromConfigurationFile(rushJsonFilename); + const defaultSubspace: Subspace = rushConfiguration.defaultSubspace; + + const hashBefore: string | undefined = defaultSubspace.getPackageJsonInjectedDependenciesHash(); + + // Mutate devDependencies of the injected provider package + const providerProject = rushConfiguration.getProjectByName('provider')!; + providerProject.packageJson.devDependencies = { + ...providerProject.packageJson.devDependencies, + jest: '^29.0.0' + }; + + const hashAfter: string | undefined = defaultSubspace.getPackageJsonInjectedDependenciesHash(); + + expect(hashBefore).toBeDefined(); + expect(hashAfter).toBeDefined(); + expect(hashBefore).toBe(hashAfter); + }); + + it('changes when production dependencies of the injected package change', () => { + const rushJsonFilename: string = `${__dirname}/repoInjectedDeps/rush.json`; + const rushConfiguration: RushConfiguration = + RushConfiguration.loadFromConfigurationFile(rushJsonFilename); + const defaultSubspace: Subspace = rushConfiguration.defaultSubspace; + + const hashBefore: string | undefined = defaultSubspace.getPackageJsonInjectedDependenciesHash(); + + // Mutate dependencies of the injected provider package + const providerProject = rushConfiguration.getProjectByName('provider')!; + providerProject.packageJson.dependencies = { + ...providerProject.packageJson.dependencies, + axios: '^1.6.0' + }; + + const hashAfter: string | undefined = defaultSubspace.getPackageJsonInjectedDependenciesHash(); + + expect(hashBefore).toBeDefined(); + expect(hashAfter).toBeDefined(); + expect(hashBefore).not.toBe(hashAfter); + }); + }); +}); diff --git a/libraries/rush-lib/src/api/test/VersionMismatchFinder.test.ts b/libraries/rush-lib/src/api/test/VersionMismatchFinder.test.ts index 1a929991606..a9ea220ec72 100644 --- a/libraries/rush-lib/src/api/test/VersionMismatchFinder.test.ts +++ b/libraries/rush-lib/src/api/test/VersionMismatchFinder.test.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { RushConfigurationProject } from '../RushConfigurationProject'; +import type { RushConfigurationProject } from '../RushConfigurationProject'; import { VersionMismatchFinder } from '../../logic/versionMismatch/VersionMismatchFinder'; import { PackageJsonEditor } from '../PackageJsonEditor'; import { CommonVersionsConfiguration } from '../CommonVersionsConfiguration'; -import { VersionMismatchFinderEntity } from '../../logic/versionMismatch/VersionMismatchFinderEntity'; +import type { VersionMismatchFinderEntity } from '../../logic/versionMismatch/VersionMismatchFinderEntity'; import { VersionMismatchFinderProject } from '../../logic/versionMismatch/VersionMismatchFinderProject'; import { VersionMismatchFinderCommonVersions } from '../../logic/versionMismatch/VersionMismatchFinderCommonVersions'; @@ -415,7 +415,7 @@ describe(VersionMismatchFinder.name, () => { expect(mismatchFinder.getMismatches()).toHaveLength(0); }); - it('handles the common-versions.json file correctly', () => { + it('handles the common-versions.json file correctly', async () => { const projectA: VersionMismatchFinderEntity = new VersionMismatchFinderProject({ packageName: 'A', packageJsonEditor: PackageJsonEditor.fromObject( @@ -429,8 +429,10 @@ describe(VersionMismatchFinder.name, () => { ), decoupledLocalDependencies: new Set() } as any as RushConfigurationProject); + const commonVersionsConfiguration: CommonVersionsConfiguration = + await CommonVersionsConfiguration.loadFromFileAsync(`${__dirname}/jsonFiles/common-versions.json`); const projectB: VersionMismatchFinderEntity = new VersionMismatchFinderCommonVersions( - CommonVersionsConfiguration.loadFromFile(`${__dirname}/jsonFiles/common-versions.json`) + commonVersionsConfiguration ); const mismatchFinder: VersionMismatchFinder = new VersionMismatchFinder([projectA, projectB]); diff --git a/libraries/rush-lib/src/api/test/VersionPolicy.test.ts b/libraries/rush-lib/src/api/test/VersionPolicy.test.ts index 49fd50f8698..738798be560 100644 --- a/libraries/rush-lib/src/api/test/VersionPolicy.test.ts +++ b/libraries/rush-lib/src/api/test/VersionPolicy.test.ts @@ -1,9 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IPackageJson } from '@rushstack/node-core-library'; +import type { IPackageJson } from '@rushstack/node-core-library'; -import { VersionPolicyConfiguration } from '../VersionPolicyConfiguration'; +import { + type ILockStepVersionJson, + VersionPolicyConfiguration, + type IIndividualVersionJson +} from '../VersionPolicyConfiguration'; import { VersionPolicy, LockStepVersionPolicy, IndividualVersionPolicy, BumpType } from '../VersionPolicy'; describe(VersionPolicy.name, () => { @@ -113,6 +117,25 @@ describe(VersionPolicy.name, () => { lockStepVersionPolicy.update(newVersion); expect(lockStepVersionPolicy.version).toEqual(newVersion); }); + + it('preserves fields', () => { + const originalJson: ILockStepVersionJson = { + definitionName: 'lockStepVersion', + policyName: 'test', + dependencies: { + versionFormatForCommit: 'original', + versionFormatForPublish: 'original' + }, + exemptFromRushChange: true, + includeEmailInChangeFile: true, + version: '1.1.0', + mainProject: 'main-project', + nextBump: 'major' + }; + + const nextJson: ILockStepVersionJson = new LockStepVersionPolicy(originalJson)._json; + expect(nextJson).toMatchObject(originalJson); + }); }); describe(IndividualVersionPolicy.name, () => { @@ -159,5 +182,22 @@ describe(VersionPolicy.name, () => { individualVersionPolicy.ensure(originalPackageJson); }).toThrow(); }); + + it('preserves fields', () => { + const originalJson: IIndividualVersionJson = { + definitionName: 'individualVersion', + policyName: 'test', + dependencies: { + versionFormatForCommit: 'wildcard', + versionFormatForPublish: 'exact' + }, + exemptFromRushChange: true, + includeEmailInChangeFile: true, + lockedMajor: 3 + }; + + const nextJson: IIndividualVersionJson = new IndividualVersionPolicy(originalJson)._json; + expect(nextJson).toMatchObject(originalJson); + }); }); }); diff --git a/libraries/rush-lib/src/api/test/__snapshots__/CommandLineConfiguration.test.ts.snap b/libraries/rush-lib/src/api/test/__snapshots__/CommandLineConfiguration.test.ts.snap index 55015a7af18..64975ca1448 100644 --- a/libraries/rush-lib/src/api/test/__snapshots__/CommandLineConfiguration.test.ts.snap +++ b/libraries/rush-lib/src/api/test/__snapshots__/CommandLineConfiguration.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`CommandLineConfiguration Detects a cycle among phases 1`] = `"In command-line.json, there exists a cycle within the set of _phase:b dependencies: _phase:b, _phase:c, _phase:a"`; diff --git a/libraries/rush-lib/src/api/test/__snapshots__/CommonVersionsConfiguration.test.ts.snap b/libraries/rush-lib/src/api/test/__snapshots__/CommonVersionsConfiguration.test.ts.snap new file mode 100644 index 00000000000..7a2f966c4cf --- /dev/null +++ b/libraries/rush-lib/src/api/test/__snapshots__/CommonVersionsConfiguration.test.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`CommonVersionsConfiguration Does not allow \`ensureConsistentVersions\` to be set in both rush.json and common-versions.json 1`] = `"When the ensureConsistentVersions config is defined in the rush.json file, it cannot also be defined in the common-versions.json file"`; diff --git a/libraries/rush-lib/src/api/test/__snapshots__/CustomTipsConfiguration.test.ts.snap b/libraries/rush-lib/src/api/test/__snapshots__/CustomTipsConfiguration.test.ts.snap new file mode 100644 index 00000000000..ec2c9038eb0 --- /dev/null +++ b/libraries/rush-lib/src/api/test/__snapshots__/CustomTipsConfiguration.test.ts.snap @@ -0,0 +1,339 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`CustomTipsConfiguration formatting (a long message with an indented line) _showErrorTip prints an expected message 1`] = ` +Array [ + "[error] [red]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[error] [red]|[default]", + "[error] [red]| [default]Lorem ipsum dolor sit amet, consectetur", + "[error] [red]| [default]adipiscing elit, sed do eiusmod tempor", + "[error] [red]| [default]incididunt ut labore et dolore magna aliqua. Ut", + "[error] [red]| [default]enim ad minim veniam, quis nostrud exercitation", + "[error] [red]| [default]ullamco laboris nisi ut aliquip ex ea commodo", + "[error] [red]| [default]consequat. Duis aute irure dolor in", + "[error] [red]| [default]reprehenderit in voluptate velit esse cillum", + "[error] [red]| [default]dolore eu fugiat nulla pariatur. Excepteur sint", + "[error] [red]| [default]occaecat cupidatat non proident, sunt in culpa", + "[error] [red]| [default]qui officia deserunt mollit anim id est laborum.", + "[error] [red]| [default] Lorem ipsum dolor sit amet, consectetur", + "[error] [red]| [default] adipiscing elit, sed do eiusmod tempor", + "[error] [red]| [default] incididunt ut labore et dolore magna aliqua. Ut", + "[error] [red]| [default] enim ad minim veniam, quis nostrud exercitation", + "[error] [red]| [default] ullamco laboris nisi ut aliquip ex ea commodo", + "[error] [red]| [default] consequat. Duis aute irure dolor in", + "[error] [red]| [default] reprehenderit in voluptate velit esse cillum", + "[error] [red]| [default] dolore eu fugiat nulla pariatur. Excepteur sint", + "[error] [red]| [default] occaecat cupidatat non proident, sunt in culpa", + "[error] [red]| [default] qui officia deserunt mollit anim id est laborum.", + "[error] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a long message with an indented line) _showInfoTip prints an expected message 1`] = ` +Array [ + "[log] | Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)", + "[log] |", + "[log] | Lorem ipsum dolor sit amet, consectetur adipiscing elit,", + "[log] | sed do eiusmod tempor incididunt ut labore et dolore magna", + "[log] | aliqua. Ut enim ad minim veniam, quis nostrud exercitation", + "[log] | ullamco laboris nisi ut aliquip ex ea commodo consequat.", + "[log] | Duis aute irure dolor in reprehenderit in voluptate velit", + "[log] | esse cillum dolore eu fugiat nulla pariatur. Excepteur", + "[log] | sint occaecat cupidatat non proident, sunt in culpa qui", + "[log] | officia deserunt mollit anim id est laborum.", + "[log] | Lorem ipsum dolor sit amet, consectetur adipiscing elit,", + "[log] | sed do eiusmod tempor incididunt ut labore et dolore magna", + "[log] | aliqua. Ut enim ad minim veniam, quis nostrud exercitation", + "[log] | ullamco laboris nisi ut aliquip ex ea commodo consequat.", + "[log] | Duis aute irure dolor in reprehenderit in voluptate velit", + "[log] | esse cillum dolore eu fugiat nulla pariatur. Excepteur", + "[log] | sint occaecat cupidatat non proident, sunt in culpa qui", + "[log] | officia deserunt mollit anim id est laborum.", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a long message with an indented line) _showTip prints an expected message 1`] = ` +Array [ + "[error] [red]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[error] [red]|[default]", + "[error] [red]| [default]Lorem ipsum dolor sit amet, consectetur", + "[error] [red]| [default]adipiscing elit, sed do eiusmod tempor", + "[error] [red]| [default]incididunt ut labore et dolore magna aliqua. Ut", + "[error] [red]| [default]enim ad minim veniam, quis nostrud exercitation", + "[error] [red]| [default]ullamco laboris nisi ut aliquip ex ea commodo", + "[error] [red]| [default]consequat. Duis aute irure dolor in", + "[error] [red]| [default]reprehenderit in voluptate velit esse cillum", + "[error] [red]| [default]dolore eu fugiat nulla pariatur. Excepteur sint", + "[error] [red]| [default]occaecat cupidatat non proident, sunt in culpa", + "[error] [red]| [default]qui officia deserunt mollit anim id est laborum.", + "[error] [red]| [default] Lorem ipsum dolor sit amet, consectetur", + "[error] [red]| [default] adipiscing elit, sed do eiusmod tempor", + "[error] [red]| [default] incididunt ut labore et dolore magna aliqua. Ut", + "[error] [red]| [default] enim ad minim veniam, quis nostrud exercitation", + "[error] [red]| [default] ullamco laboris nisi ut aliquip ex ea commodo", + "[error] [red]| [default] consequat. Duis aute irure dolor in", + "[error] [red]| [default] reprehenderit in voluptate velit esse cillum", + "[error] [red]| [default] dolore eu fugiat nulla pariatur. Excepteur sint", + "[error] [red]| [default] occaecat cupidatat non proident, sunt in culpa", + "[error] [red]| [default] qui officia deserunt mollit anim id est laborum.", + "[error] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a long message with an indented line) _showWarningTip prints an expected message 1`] = ` +Array [ + "[warning] [yellow]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[warning] [yellow]|[default]", + "[warning] [yellow]| [default]Lorem ipsum dolor sit amet, consectetur", + "[warning] [yellow]| [default]adipiscing elit, sed do eiusmod tempor", + "[warning] [yellow]| [default]incididunt ut labore et dolore magna aliqua. Ut", + "[warning] [yellow]| [default]enim ad minim veniam, quis nostrud exercitation", + "[warning] [yellow]| [default]ullamco laboris nisi ut aliquip ex ea commodo", + "[warning] [yellow]| [default]consequat. Duis aute irure dolor in", + "[warning] [yellow]| [default]reprehenderit in voluptate velit esse cillum", + "[warning] [yellow]| [default]dolore eu fugiat nulla pariatur. Excepteur sint", + "[warning] [yellow]| [default]occaecat cupidatat non proident, sunt in culpa", + "[warning] [yellow]| [default]qui officia deserunt mollit anim id est laborum.", + "[warning] [yellow]| [default] Lorem ipsum dolor sit amet, consectetur", + "[warning] [yellow]| [default] adipiscing elit, sed do eiusmod tempor", + "[warning] [yellow]| [default] incididunt ut labore et dolore magna aliqua. Ut", + "[warning] [yellow]| [default] enim ad minim veniam, quis nostrud exercitation", + "[warning] [yellow]| [default] ullamco laboris nisi ut aliquip ex ea commodo", + "[warning] [yellow]| [default] consequat. Duis aute irure dolor in", + "[warning] [yellow]| [default] reprehenderit in voluptate velit esse cillum", + "[warning] [yellow]| [default] dolore eu fugiat nulla pariatur. Excepteur sint", + "[warning] [yellow]| [default] occaecat cupidatat non proident, sunt in culpa", + "[warning] [yellow]| [default] qui officia deserunt mollit anim id est laborum.", + "[warning] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a long message) _showErrorTip prints an expected message 1`] = ` +Array [ + "[error] [red]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[error] [red]|[default]", + "[error] [red]| [default]Lorem ipsum dolor sit amet, consectetur", + "[error] [red]| [default]adipiscing elit, sed do eiusmod tempor", + "[error] [red]| [default]incididunt ut labore et dolore magna aliqua. Ut", + "[error] [red]| [default]enim ad minim veniam, quis nostrud exercitation", + "[error] [red]| [default]ullamco laboris nisi ut aliquip ex ea commodo", + "[error] [red]| [default]consequat. Duis aute irure dolor in", + "[error] [red]| [default]reprehenderit in voluptate velit esse cillum", + "[error] [red]| [default]dolore eu fugiat nulla pariatur. Excepteur sint", + "[error] [red]| [default]occaecat cupidatat non proident, sunt in culpa", + "[error] [red]| [default]qui officia deserunt mollit anim id est laborum.", + "[error] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a long message) _showInfoTip prints an expected message 1`] = ` +Array [ + "[log] | Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)", + "[log] |", + "[log] | Lorem ipsum dolor sit amet, consectetur adipiscing elit,", + "[log] | sed do eiusmod tempor incididunt ut labore et dolore magna", + "[log] | aliqua. Ut enim ad minim veniam, quis nostrud exercitation", + "[log] | ullamco laboris nisi ut aliquip ex ea commodo consequat.", + "[log] | Duis aute irure dolor in reprehenderit in voluptate velit", + "[log] | esse cillum dolore eu fugiat nulla pariatur. Excepteur", + "[log] | sint occaecat cupidatat non proident, sunt in culpa qui", + "[log] | officia deserunt mollit anim id est laborum.", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a long message) _showTip prints an expected message 1`] = ` +Array [ + "[error] [red]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[error] [red]|[default]", + "[error] [red]| [default]Lorem ipsum dolor sit amet, consectetur", + "[error] [red]| [default]adipiscing elit, sed do eiusmod tempor", + "[error] [red]| [default]incididunt ut labore et dolore magna aliqua. Ut", + "[error] [red]| [default]enim ad minim veniam, quis nostrud exercitation", + "[error] [red]| [default]ullamco laboris nisi ut aliquip ex ea commodo", + "[error] [red]| [default]consequat. Duis aute irure dolor in", + "[error] [red]| [default]reprehenderit in voluptate velit esse cillum", + "[error] [red]| [default]dolore eu fugiat nulla pariatur. Excepteur sint", + "[error] [red]| [default]occaecat cupidatat non proident, sunt in culpa", + "[error] [red]| [default]qui officia deserunt mollit anim id est laborum.", + "[error] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a long message) _showWarningTip prints an expected message 1`] = ` +Array [ + "[warning] [yellow]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[warning] [yellow]|[default]", + "[warning] [yellow]| [default]Lorem ipsum dolor sit amet, consectetur", + "[warning] [yellow]| [default]adipiscing elit, sed do eiusmod tempor", + "[warning] [yellow]| [default]incididunt ut labore et dolore magna aliqua. Ut", + "[warning] [yellow]| [default]enim ad minim veniam, quis nostrud exercitation", + "[warning] [yellow]| [default]ullamco laboris nisi ut aliquip ex ea commodo", + "[warning] [yellow]| [default]consequat. Duis aute irure dolor in", + "[warning] [yellow]| [default]reprehenderit in voluptate velit esse cillum", + "[warning] [yellow]| [default]dolore eu fugiat nulla pariatur. Excepteur sint", + "[warning] [yellow]| [default]occaecat cupidatat non proident, sunt in culpa", + "[warning] [yellow]| [default]qui officia deserunt mollit anim id est laborum.", + "[warning] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a message with an indented line) _showErrorTip prints an expected message 1`] = ` +Array [ + "[error] [red]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[error] [red]|[default]", + "[error] [red]| [default]This is a test", + "[error] [red]| [default] This is a test", + "[error] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a message with an indented line) _showInfoTip prints an expected message 1`] = ` +Array [ + "[log] | Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)", + "[log] |", + "[log] | This is a test", + "[log] | This is a test", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a message with an indented line) _showTip prints an expected message 1`] = ` +Array [ + "[error] [red]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[error] [red]|[default]", + "[error] [red]| [default]This is a test", + "[error] [red]| [default] This is a test", + "[error] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a message with an indented line) _showWarningTip prints an expected message 1`] = ` +Array [ + "[warning] [yellow]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[warning] [yellow]|[default]", + "[warning] [yellow]| [default]This is a test", + "[warning] [yellow]| [default] This is a test", + "[warning] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a message with newlines) _showErrorTip prints an expected message 1`] = ` +Array [ + "[error] [red]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[error] [red]|[default]", + "[error] [red]| [default]This is a test", + "[error] [red]| [default]This is a test", + "[error] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a message with newlines) _showInfoTip prints an expected message 1`] = ` +Array [ + "[log] | Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)", + "[log] |", + "[log] | This is a test", + "[log] | This is a test", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a message with newlines) _showTip prints an expected message 1`] = ` +Array [ + "[error] [red]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[error] [red]|[default]", + "[error] [red]| [default]This is a test", + "[error] [red]| [default]This is a test", + "[error] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a message with newlines) _showWarningTip prints an expected message 1`] = ` +Array [ + "[warning] [yellow]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[warning] [yellow]|[default]", + "[warning] [yellow]| [default]This is a test", + "[warning] [yellow]| [default]This is a test", + "[warning] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a short message) _showErrorTip prints an expected message 1`] = ` +Array [ + "[error] [red]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[error] [red]|[default]", + "[error] [red]| [default]This is a test", + "[error] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a short message) _showInfoTip prints an expected message 1`] = ` +Array [ + "[log] | Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)", + "[log] |", + "[log] | This is a test", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a short message) _showTip prints an expected message 1`] = ` +Array [ + "[error] [red]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[error] [red]|[default]", + "[error] [red]| [default]This is a test", + "[error] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration formatting (a short message) _showWarningTip prints an expected message 1`] = ` +Array [ + "[warning] [yellow]| Custom Tip (TIP_PNPM_INVALID_NODE_VERSION)[default]", + "[warning] [yellow]|[default]", + "[warning] [yellow]| [default]This is a test", + "[warning] ", + "[log] ", + "[log] ", +] +`; + +exports[`CustomTipsConfiguration loads the config file (custom-tips.json) 1`] = ` +Map { + "TIP_RUSH_INCONSISTENT_VERSIONS" => Object { + "message": "This is so wrong my friend. Please read this doc for more information: google.com", + "tipId": "TIP_RUSH_INCONSISTENT_VERSIONS", + }, +} +`; diff --git a/libraries/rush-lib/src/api/test/__snapshots__/RushCommandLine.test.ts.snap b/libraries/rush-lib/src/api/test/__snapshots__/RushCommandLine.test.ts.snap new file mode 100644 index 00000000000..d913bb774e3 --- /dev/null +++ b/libraries/rush-lib/src/api/test/__snapshots__/RushCommandLine.test.ts.snap @@ -0,0 +1,1660 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`RushCommandLine Returns a spec 1`] = ` +Object { + "actions": Array [ + Object { + "actionName": "add", + "parameters": Array [ + Object { + "description": "If specified, the \\"rush update\\" command will not be run after updating the package.json files.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--skip-update", + "required": false, + "shortName": "-s", + }, + Object { + "description": "The name of the package which should be added as a dependency. A SemVer version specifier can be appended after an \\"@\\" sign. WARNING: Symbol characters are usually interpreted by your shell, so it's recommended to use quotes. For example, write \\"rush add --package \\"example@^1.2.3\\"\\" instead of \\"rush add --package example@^1.2.3\\". To add multiple packages, write \\"rush add --package foo --package bar\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--package", + "required": true, + "shortName": "-p", + }, + Object { + "description": "If specified, the dependency will be added to all projects.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--all", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Run command using a variant installation configuration", + "environmentVariable": "RUSH_VARIANT", + "kind": "String", + "longName": "--variant", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If specified, the SemVer specifier added to the package.json will be an exact version (e.g. without tilde or caret).", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--exact", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If specified, the SemVer specifier added to the package.json will be a prepended with a \\"caret\\" specifier (\\"^\\").", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--caret", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If specified, the package will be added to the \\"devDependencies\\" section of the package.json", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--dev", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If specified, the package will be added to the \\"peerDependencies\\" section of the package.json", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--peer", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If specified, other packages with this dependency will have their package.json files updated to use the same version of the dependency.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--make-consistent", + "required": false, + "shortName": "-m", + }, + ], + }, + Object { + "actionName": "change", + "parameters": Array [ + Object { + "description": "Verify the change file has been generated and that it is a valid JSON file", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--verify", + "required": false, + "shortName": "-v", + }, + Object { + "description": "Validate all change files in the repository, not just those added in the current branch. Reports errors for change files that reference nonexistent projects or target non-main projects in a lockstepped version policy. Requires the \\"strictChangefileValidation\\" experiment to be enabled.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--verify-all", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Skips fetching the baseline branch before running \\"git diff\\" to detect changes.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--no-fetch", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If this parameter is specified, compare the checked out branch with the specified branch to determine which projects were changed. If this parameter is not specified, the checked out branch is compared against the \\"main\\" branch.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--target-branch", + "required": false, + "shortName": "-b", + }, + Object { + "description": "If a changefile already exists, overwrite without prompting (or erroring in --bulk mode).", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--overwrite", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If this flag is specified generated changefiles will be commited automatically.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--commit", + "required": false, + "shortName": "-c", + }, + Object { + "description": "If this parameter is specified generated changefiles will be commited automatically with the specified commit message.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--commit-message", + "required": false, + "shortName": undefined, + }, + Object { + "description": "The email address to use in changefiles. If this parameter is not provided, the email address will be detected or prompted for in interactive mode.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--email", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If this flag is specified, apply the same change message and bump type to all changed projects. The --message and the --bump-type parameters must be specified if the --bulk parameter is specified", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--bulk", + "required": false, + "shortName": undefined, + }, + Object { + "description": "The message to apply to all changed projects if the --bulk flag is provided.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--message", + "required": false, + "shortName": undefined, + }, + Object { + "description": "The bump type to apply to all changed projects if the --bulk flag is provided.", + "environmentVariable": undefined, + "kind": "Choice", + "longName": "--bump-type", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "check", + "parameters": Array [ + Object { + "description": "If this flag is specified, output will be in JSON format.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--json", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If this flag is specified, long lists of package names will not be truncated. This has no effect if the --json flag is also specified.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--verbose", + "required": false, + "shortName": undefined, + }, + Object { + "description": "(EXPERIMENTAL) Specifies an individual Rush subspace to check, requiring versions to be consistent only within that subspace (ignoring other subspaces). This parameter is required when the \\"subspacesEnabled\\" setting is set to true in subspaces.json.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--subspace", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Run command using a variant installation configuration", + "environmentVariable": "RUSH_VARIANT", + "kind": "String", + "longName": "--variant", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "deploy", + "parameters": Array [ + Object { + "description": "Specifies the name of the main Rush project to be deployed. It must appear in the \\"deploymentProjectNames\\" setting in the deployment config file.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--project", + "required": false, + "shortName": "-p", + }, + Object { + "description": "By default, the deployment configuration is specified in \\"common/config/rush/deploy.json\\". You can use \\"--scenario\\" to specify an alternate name. The name must be lowercase and separated by dashes. For example, if SCENARIO_NAME is \\"web\\", then the config file would be \\"common/config/rush/deploy-web.json\\".", + "environmentVariable": undefined, + "kind": "String", + "longName": "--scenario", + "required": false, + "shortName": "-s", + }, + Object { + "description": "By default, deployment will fail if the target folder is not empty. SPECIFYING THIS FLAG WILL RECURSIVELY DELETE EXISTING CONTENTS OF THE TARGET FOLDER.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--overwrite", + "required": false, + "shortName": undefined, + }, + Object { + "description": "By default, files are deployed to the \\"common/deploy\\" folder inside the Rush repo. Use this parameter to specify a different location. WARNING: USE CAUTION WHEN COMBINING WITH \\"--overwrite\\"", + "environmentVariable": "RUSH_DEPLOY_TARGET_FOLDER", + "kind": "String", + "longName": "--target-folder", + "required": false, + "shortName": "-t", + }, + Object { + "description": "If specified, after the deployment has been prepared, \\"rush deploy\\" will create an archive containing the contents of the target folder. The newly created archive file will be placed according to the designated path, relative to the target folder. Supported file extensions: .zip", + "environmentVariable": undefined, + "kind": "String", + "longName": "--create-archive", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If specified, \\"rush deploy\\" will only create an archive containing the contents of the target folder. The target folder will not be modified other than to create the archive file.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--create-archive-only", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "init", + "parameters": Array [ + Object { + "description": "By default \\"rush init\\" will not overwrite existing config files. Specify this switch to override that. This can be useful when upgrading your repo to a newer release of Rush. WARNING: USE WITH CARE!", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--overwrite-existing", + "required": false, + "shortName": undefined, + }, + Object { + "description": "When copying the template config files, this uncomments fragments that are used by the \\"rush-example\\" GitHub repo, which is a sample monorepo that illustrates many Rush features. This option is primarily intended for maintaining that example.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--rush-example-repo", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Include features that may not be complete features, useful for demoing specific future features or current work in progress features.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--include-experiments", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "init-autoinstaller", + "parameters": Array [ + Object { + "description": "Specifies the name of the autoinstaller folder, which must conform to the naming rules for NPM packages.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--name", + "required": true, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "init-deploy", + "parameters": Array [ + Object { + "description": "Specifies the name of the main Rush project to be deployed in this scenario. It will be added to the \\"deploymentProjectNames\\" setting.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--project", + "required": true, + "shortName": "-p", + }, + Object { + "description": "By default, the deployment configuration will be written to \\"common/config/rush/deploy.json\\". You can use \\"--scenario\\" to specify an alternate name. The name must be lowercase and separated by dashes. For example, if the name is \\"web\\", then the config file would be \\"common/config/rush/deploy-web.json\\".", + "environmentVariable": undefined, + "kind": "String", + "longName": "--scenario", + "required": false, + "shortName": "-s", + }, + ], + }, + Object { + "actionName": "init-subspace", + "parameters": Array [ + Object { + "description": "The name of the subspace that is being initialized.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--name", + "required": true, + "shortName": "-n", + }, + ], + }, + Object { + "actionName": "install", + "parameters": Array [ + Object { + "description": "Perform \\"rush purge\\" before starting the installation", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--purge", + "required": false, + "shortName": "-p", + }, + Object { + "description": "Overrides enforcement of the \\"gitPolicy\\" rules from rush.json (use honorably!)", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--bypass-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If \\"--no-link\\" is specified, then project symlinks will NOT be created after the installation completes. You will need to run \\"rush link\\" manually. This flag is useful for automated builds that want to report stages individually or perform extra operations in between the two stages. This flag is not supported when using workspaces.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--no-link", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If specified, limits the maximum number of concurrent network requests. This is useful when troubleshooting network failures.", + "environmentVariable": undefined, + "kind": "Integer", + "longName": "--network-concurrency", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Activates verbose logging for the package manager. You will probably want to pipe the output of Rush to a file when using this command.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--debug-package-manager", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Overrides the default maximum number of install attempts.", + "environmentVariable": undefined, + "kind": "Integer", + "longName": "--max-install-attempts", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Skips execution of the \\"eventHooks\\" scripts defined in rush.json. Make sure you know what you are skipping.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--ignore-hooks", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Enables installation to be performed without internet access. PNPM will instead report an error if the necessary NPM packages cannot be obtained from the local cache. For details, see the documentation for PNPM's \\"--offline\\" parameter.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--offline", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Run command using a variant installation configuration", + "environmentVariable": "RUSH_VARIANT", + "kind": "String", + "longName": "--variant", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--to\\" parameter expands this selection to include PROJECT and all its dependencies. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to", + "required": false, + "shortName": "-t", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--to-except\\" parameter expands this selection to include all dependencies of PROJECT, but not PROJECT itself. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to-except", + "required": false, + "shortName": "-T", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--from\\" parameter expands this selection to include PROJECT and all projects that depend on it, plus all dependencies of this set. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--from", + "required": false, + "shortName": "-f", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--only\\" parameter expands this selection to include PROJECT; its dependencies are not added. \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--only", + "required": false, + "shortName": "-o", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--impacted-by\\" parameter expands this selection to include PROJECT and any projects that depend on PROJECT (and thus might be broken by changes to PROJECT). \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--impacted-by", + "required": false, + "shortName": "-i", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--impacted-by-except\\" parameter works the same as \\"--impacted-by\\" except that PROJECT itself is not added to the selection. \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--impacted-by-except", + "required": false, + "shortName": "-I", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. The \\"--to-version-policy\\" parameter is equivalent to specifying \\"--to\\" for each of the projects belonging to VERSION_POLICY_NAME. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to-version-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. The \\"--from-version-policy\\" parameter is equivalent to specifying \\"--from\\" for each of the projects belonging to VERSION_POLICY_NAME. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--from-version-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "(EXPERIMENTAL) Specifies a Rush subspace to be installed. Requires the \\"subspacesEnabled\\" feature to be enabled in subspaces.json.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--subspace", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Only check the validity of the shrinkwrap file without performing an install.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--check-only", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Only perform dependency resolution, useful for ensuring peer dependendencies are up to date. Note that this flag is only supported when using the pnpm package manager.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--resolution-only", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "link", + "parameters": Array [ + Object { + "description": "Deletes and recreates all links, even if the filesystem state seems to indicate that this is unnecessary.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--force", + "required": false, + "shortName": "-f", + }, + ], + }, + Object { + "actionName": "list", + "parameters": Array [ + Object { + "description": "If this flag is specified, the project version will be displayed in a column along with the package name.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--version", + "required": false, + "shortName": "-v", + }, + Object { + "description": "If this flag is specified, the project path will be displayed in a column along with the package name.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--path", + "required": false, + "shortName": "-p", + }, + Object { + "description": "If this flag is specified, the project full path will be displayed in a column along with the package name.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--full-path", + "required": false, + "shortName": undefined, + }, + Object { + "description": "For the non --json view, if this flag is specified, include path (-p), version (-v) columns along with the project's applicable: versionPolicy, versionPolicyName, shouldPublish, reviewPolicy, and tags fields.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--detailed", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If this flag is specified, output will be in JSON format.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--json", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--to\\" parameter expands this selection to include PROJECT and all its dependencies. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to", + "required": false, + "shortName": "-t", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--to-except\\" parameter expands this selection to include all dependencies of PROJECT, but not PROJECT itself. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to-except", + "required": false, + "shortName": "-T", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--from\\" parameter expands this selection to include PROJECT and all projects that depend on it, plus all dependencies of this set. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--from", + "required": false, + "shortName": "-f", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--only\\" parameter expands this selection to include PROJECT; its dependencies are not added. \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--only", + "required": false, + "shortName": "-o", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--impacted-by\\" parameter expands this selection to include PROJECT and any projects that depend on PROJECT (and thus might be broken by changes to PROJECT). \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--impacted-by", + "required": false, + "shortName": "-i", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--impacted-by-except\\" parameter works the same as \\"--impacted-by\\" except that PROJECT itself is not added to the selection. \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--impacted-by-except", + "required": false, + "shortName": "-I", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. The \\"--to-version-policy\\" parameter is equivalent to specifying \\"--to\\" for each of the projects belonging to VERSION_POLICY_NAME. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to-version-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. The \\"--from-version-policy\\" parameter is equivalent to specifying \\"--from\\" for each of the projects belonging to VERSION_POLICY_NAME. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--from-version-policy", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "publish", + "parameters": Array [ + Object { + "description": "If this flag is specified, the change requests will be applied to package.json files.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--apply", + "required": false, + "shortName": "-a", + }, + Object { + "description": "If this flag is specified, applied changes and deleted change requests will be committed and merged into the target branch.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--target-branch", + "required": false, + "shortName": "-b", + }, + Object { + "description": "If this flag is specified, applied changes will be published to the NPM registry.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--publish", + "required": false, + "shortName": "-p", + }, + Object { + "description": "Adds commit author and hash to the changelog.json files for each change.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--add-commit-details", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Regenerates all changelog files based on the current JSON content.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--regenerate-changelogs", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Publishes to a specified NPM registry. If this is specified, it will prevent the current commit will not be tagged.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--registry", + "required": false, + "shortName": "-r", + }, + Object { + "description": "(DEPRECATED) Specifies the authentication token to use during publishing. This parameter is deprecated because command line parameters may be readable by unrelated processes on a lab machine. Instead, a safer practice is to pass the token via an environment variable and reference it from your common/config/rush/.npmrc-publish file.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--npm-auth-token", + "required": false, + "shortName": "-n", + }, + Object { + "description": "The tag option to pass to npm publish. By default NPM will publish using the 'latest' tag, even if the package is older than the current latest, so in publishing workflows for older releases, providing a tag is important. When hotfix changes are made, this parameter defaults to 'hotfix'.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--tag", + "required": false, + "shortName": "-t", + }, + Object { + "description": "By default, when Rush invokes \\"npm publish\\" it will publish scoped packages with an access level of \\"restricted\\". Scoped packages can be published with an access level of \\"public\\" by specifying that value for this flag with the initial publication. NPM always publishes unscoped packages with an access level of \\"public\\". For more information, see the NPM documentation for the \\"--access\\" option of \\"npm publish\\".", + "environmentVariable": undefined, + "kind": "Choice", + "longName": "--set-access-level", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Packs projects into tarballs instead of publishing to npm repository. It can only be used when --include-all is specified. If this flag is specified, NPM registry related parameters will be ignored.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--pack", + "required": false, + "shortName": undefined, + }, + Object { + "description": "This parameter is used with --pack parameter to provide customized location for the tarballs instead of the default value. ", + "environmentVariable": undefined, + "kind": "String", + "longName": "--release-folder", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If this flag is specified, all packages with shouldPublish=true in rush.json or with a specified version policy will be published if their version is newer than published version.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--include-all", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Version policy name. Only projects with this version policy will be published if used with --include-all.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--version-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Bump up to a prerelease version with the provided prerelease name. Cannot be used with --suffix", + "environmentVariable": undefined, + "kind": "String", + "longName": "--prerelease-name", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Used with --prerelease-name. Only bump packages to a prerelease version if they have changes.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--partial-prerelease", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Append a suffix to all changed versions. Cannot be used with --prerelease-name.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--suffix", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If this flag is specified with --publish, packages will be published with --force on npm", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--force", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If specified with --publish and --pack, git tags will be applied for packages as if a publish was being run without --pack.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--apply-git-tags-on-pack", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Used in conjunction with git tagging -- apply git tags at the commit hash specified. If not provided, the current HEAD will be tagged.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--commit", + "required": false, + "shortName": "-c", + }, + Object { + "description": "Skips execution of all git hooks. Make sure you know what you are skipping.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--ignore-git-hooks", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "purge", + "parameters": Array [ + Object { + "description": "(UNSAFE!) Also delete shared files such as the package manager instances stored in the \\".rush\\" folder in the user's home directory. This is a more aggressive fix that is NOT SAFE to run in a live environment because it will cause other concurrent Rush processes to fail.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--unsafe", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "remove", + "parameters": Array [ + Object { + "description": "If specified, the \\"rush update\\" command will not be run after updating the package.json files.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--skip-update", + "required": false, + "shortName": "-s", + }, + Object { + "description": "The name of the package which should be removed. To remove multiple packages, run \\"rush remove --package foo --package bar\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--package", + "required": true, + "shortName": "-p", + }, + Object { + "description": "If specified, the dependency will be removed from all projects that declare it.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--all", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Run command using a variant installation configuration", + "environmentVariable": "RUSH_VARIANT", + "kind": "String", + "longName": "--variant", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "scan", + "parameters": Array [ + Object { + "description": "If this flag is specified, output will be in JSON format.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--json", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If this flag is specified, output will list all detected dependencies.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--all", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "setup", + "parameters": Array [], + }, + Object { + "actionName": "unlink", + "parameters": Array [], + }, + Object { + "actionName": "update", + "parameters": Array [ + Object { + "description": "Perform \\"rush purge\\" before starting the installation", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--purge", + "required": false, + "shortName": "-p", + }, + Object { + "description": "Overrides enforcement of the \\"gitPolicy\\" rules from rush.json (use honorably!)", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--bypass-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If \\"--no-link\\" is specified, then project symlinks will NOT be created after the installation completes. You will need to run \\"rush link\\" manually. This flag is useful for automated builds that want to report stages individually or perform extra operations in between the two stages. This flag is not supported when using workspaces.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--no-link", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If specified, limits the maximum number of concurrent network requests. This is useful when troubleshooting network failures.", + "environmentVariable": undefined, + "kind": "Integer", + "longName": "--network-concurrency", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Activates verbose logging for the package manager. You will probably want to pipe the output of Rush to a file when using this command.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--debug-package-manager", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Overrides the default maximum number of install attempts.", + "environmentVariable": undefined, + "kind": "Integer", + "longName": "--max-install-attempts", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Skips execution of the \\"eventHooks\\" scripts defined in rush.json. Make sure you know what you are skipping.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--ignore-hooks", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Enables installation to be performed without internet access. PNPM will instead report an error if the necessary NPM packages cannot be obtained from the local cache. For details, see the documentation for PNPM's \\"--offline\\" parameter.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--offline", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Run command using a variant installation configuration", + "environmentVariable": "RUSH_VARIANT", + "kind": "String", + "longName": "--variant", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Normally \\"rush update\\" tries to preserve your existing installed versions and only makes the minimum updates needed to satisfy the package.json files. This conservative approach prevents your PR from getting involved with package updates that are unrelated to your work. Use \\"--full\\" when you really want to update all dependencies to the latest SemVer-compatible version. This should be done periodically by a person or robot whose role is to deal with potential upgrade regressions.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--full", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If the shrinkwrap file appears to already satisfy the package.json files, then \\"rush update\\" will skip invoking the package manager at all. In certain situations this heuristic may be inaccurate. Use the \\"--recheck\\" flag to force the package manager to process the shrinkwrap file. This will also update your shrinkwrap file with Rush's fixups. (To minimize shrinkwrap churn, these fixups are normally performed only in the temporary folder.)", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--recheck", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "install-autoinstaller", + "parameters": Array [ + Object { + "description": "The name of the autoinstaller, which must be one of the folders under common/autoinstallers.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--name", + "required": true, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "update-autoinstaller", + "parameters": Array [ + Object { + "description": "The name of the autoinstaller, which must be one of the folders under common/autoinstallers.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--name", + "required": true, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "update-cloud-credentials", + "parameters": Array [ + Object { + "description": "Run the credential update operation in interactive mode, if supported by the provider.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--interactive", + "required": false, + "shortName": "-i", + }, + Object { + "description": "A static credential, to be cached.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--credential", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If specified, delete stored credentials.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--delete", + "required": false, + "shortName": "-d", + }, + ], + }, + Object { + "actionName": "upgrade-interactive", + "parameters": Array [ + Object { + "description": "When upgrading dependencies from a single project, also upgrade dependencies from other projects.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--make-consistent", + "required": false, + "shortName": undefined, + }, + Object { + "description": "If specified, the \\"rush update\\" command will not be run after updating the package.json files.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--skip-update", + "required": false, + "shortName": "-s", + }, + Object { + "description": "Run command using a variant installation configuration", + "environmentVariable": "RUSH_VARIANT", + "kind": "String", + "longName": "--variant", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "version", + "parameters": Array [ + Object { + "description": "If this flag is specified, changes will be committed and merged into the target branch.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--target-branch", + "required": false, + "shortName": "-b", + }, + Object { + "description": "Updates package versions if needed to satisfy version policies.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--ensure-version-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Override the version in the specified --version-policy. This setting only works for lock-step version policy and when --ensure-version-policy is specified.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--override-version", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Bumps package version based on version policies.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--bump", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Overrides \\"gitPolicy\\" enforcement (use honorably!)", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--bypass-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "The name of the version policy", + "environmentVariable": undefined, + "kind": "String", + "longName": "--version-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Overrides the bump type in the version-policy.json for the specified version policy. Valid BUMPTYPE values include: prerelease, patch, preminor, minor, major. This setting only works for lock-step version policy in bump action.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--override-bump", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Overrides the prerelease identifier in the version value of version-policy.json for the specified version policy. This setting only works for lock-step version policy. This setting increases to new prerelease id when \\"--bump\\" is provided but only replaces the prerelease name when \\"--ensure-version-policy\\" is provided.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--override-prerelease-id", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Skips execution of all git hooks. Make sure you know what you are skipping.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--ignore-git-hooks", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "alert", + "parameters": Array [ + Object { + "description": "Temporarily suspend the specified alert for one week", + "environmentVariable": undefined, + "kind": "String", + "longName": "--snooze", + "required": false, + "shortName": "-s", + }, + Object { + "description": "Combined with \\"--snooze\\", causes that alert to be suspended permanently", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--forever", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "bridge-package", + "parameters": Array [ + Object { + "description": "The path of folder of a project outside of this Rush repo, whose installation will be simulated using node_modules symlinks (\\"hotlinks\\"). This folder is the symlink target.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--path", + "required": true, + "shortName": undefined, + }, + Object { + "description": "Specify which installed versions should be hotlinked.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--version", + "required": false, + "shortName": undefined, + }, + Object { + "description": "The name of the subspace to use for the hotlinked package.", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--subspace", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "link-package", + "parameters": Array [ + Object { + "description": "The path of folder of a project outside of this Rush repo, whose installation will be simulated using node_modules symlinks (\\"hotlinks\\"). This folder is the symlink target.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--path", + "required": true, + "shortName": undefined, + }, + Object { + "description": "A list of Rush project names that will be hotlinked to the \\"--path\\" folder. If not specified, the default is the project of the current working directory.", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--project", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "import-strings", + "parameters": Array [ + Object { + "description": "Specifies the maximum number of concurrent processes to launch during a build. The COUNT should be a positive integer, a percentage value (eg. \\"50%\\") or the word \\"max\\" to specify a count that is equal to the number of CPU cores. If this parameter is omitted, then the default value depends on the operating system and number of CPU cores.", + "environmentVariable": "RUSH_PARALLELISM", + "kind": "String", + "longName": "--parallelism", + "required": false, + "shortName": "-p", + }, + Object { + "description": "After the build is complete, print additional statistics and CPU usage information, including an ASCII chart of the start and stop times for each operation.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--timeline", + "required": false, + "shortName": undefined, + }, + Object { + "description": "(EXPERIMENTAL) Before the build starts, log information about the cobuild state. This will include information about clusters and the projects that are part of each cluster.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--log-cobuild-plan", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--to\\" parameter expands this selection to include PROJECT and all its dependencies. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to", + "required": false, + "shortName": "-t", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--to-except\\" parameter expands this selection to include all dependencies of PROJECT, but not PROJECT itself. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to-except", + "required": false, + "shortName": "-T", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--from\\" parameter expands this selection to include PROJECT and all projects that depend on it, plus all dependencies of this set. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--from", + "required": false, + "shortName": "-f", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--only\\" parameter expands this selection to include PROJECT; its dependencies are not added. \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--only", + "required": false, + "shortName": "-o", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--impacted-by\\" parameter expands this selection to include PROJECT and any projects that depend on PROJECT (and thus might be broken by changes to PROJECT). \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--impacted-by", + "required": false, + "shortName": "-i", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--impacted-by-except\\" parameter works the same as \\"--impacted-by\\" except that PROJECT itself is not added to the selection. \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--impacted-by-except", + "required": false, + "shortName": "-I", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. The \\"--to-version-policy\\" parameter is equivalent to specifying \\"--to\\" for each of the projects belonging to VERSION_POLICY_NAME. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to-version-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. The \\"--from-version-policy\\" parameter is equivalent to specifying \\"--from\\" for each of the projects belonging to VERSION_POLICY_NAME. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--from-version-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Display the logs during the build, rather than just displaying the build status summary", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--verbose", + "required": false, + "shortName": "-v", + }, + Object { + "description": "If the selected projects are \\"unsafe\\" (missing some dependencies), add the minimal set of phase dependencies. For example, \\"--from A\\" normally might include the \\"_phase:test\\" phase for A's dependencies, even though changes to A can't break those tests. Using \\"--impacted-by A --include-phase-deps\\" avoids that work by performing \\"_phase:test\\" only for downstream projects.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--include-phase-deps", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Skips execution of the \\"eventHooks\\" scripts defined in rush.json. Make sure you know what you are skipping.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--ignore-hooks", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Specifies the directory where Node.js diagnostic reports will be written. This directory will contain a subdirectory for each project and phase.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--node-diagnostic-dir", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Logs information about the components of the build cache ids for individual operations. This is useful for debugging the incremental build logic.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--debug-build-cache-ids", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Selects a single instead of the default locale (en-us) for non-ship builds or all locales for ship builds.", + "environmentVariable": undefined, + "kind": "Choice", + "longName": "--locale", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "upload", + "parameters": Array [ + Object { + "description": "Selects a single instead of the default locale (en-us) for non-ship builds or all locales for ship builds.", + "environmentVariable": undefined, + "kind": "Choice", + "longName": "--locale", + "required": false, + "shortName": undefined, + }, + ], + }, + Object { + "actionName": "build", + "parameters": Array [ + Object { + "description": "Specifies the maximum number of concurrent processes to launch during a build. The COUNT should be a positive integer, a percentage value (eg. \\"50%\\") or the word \\"max\\" to specify a count that is equal to the number of CPU cores. If this parameter is omitted, then the default value depends on the operating system and number of CPU cores.", + "environmentVariable": "RUSH_PARALLELISM", + "kind": "String", + "longName": "--parallelism", + "required": false, + "shortName": "-p", + }, + Object { + "description": "After the build is complete, print additional statistics and CPU usage information, including an ASCII chart of the start and stop times for each operation.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--timeline", + "required": false, + "shortName": undefined, + }, + Object { + "description": "(EXPERIMENTAL) Before the build starts, log information about the cobuild state. This will include information about clusters and the projects that are part of each cluster.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--log-cobuild-plan", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--to\\" parameter expands this selection to include PROJECT and all its dependencies. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to", + "required": false, + "shortName": "-t", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--to-except\\" parameter expands this selection to include all dependencies of PROJECT, but not PROJECT itself. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to-except", + "required": false, + "shortName": "-T", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--from\\" parameter expands this selection to include PROJECT and all projects that depend on it, plus all dependencies of this set. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--from", + "required": false, + "shortName": "-f", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--only\\" parameter expands this selection to include PROJECT; its dependencies are not added. \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--only", + "required": false, + "shortName": "-o", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--impacted-by\\" parameter expands this selection to include PROJECT and any projects that depend on PROJECT (and thus might be broken by changes to PROJECT). \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--impacted-by", + "required": false, + "shortName": "-i", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--impacted-by-except\\" parameter works the same as \\"--impacted-by\\" except that PROJECT itself is not added to the selection. \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--impacted-by-except", + "required": false, + "shortName": "-I", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. The \\"--to-version-policy\\" parameter is equivalent to specifying \\"--to\\" for each of the projects belonging to VERSION_POLICY_NAME. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to-version-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. The \\"--from-version-policy\\" parameter is equivalent to specifying \\"--from\\" for each of the projects belonging to VERSION_POLICY_NAME. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--from-version-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Display the logs during the build, rather than just displaying the build status summary", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--verbose", + "required": false, + "shortName": "-v", + }, + Object { + "description": "If the selected projects are \\"unsafe\\" (missing some dependencies), add the minimal set of phase dependencies. For example, \\"--from A\\" normally might include the \\"_phase:test\\" phase for A's dependencies, even though changes to A can't break those tests. Using \\"--impacted-by A --include-phase-deps\\" avoids that work by performing \\"_phase:test\\" only for downstream projects.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--include-phase-deps", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Normally the incremental build logic will rebuild changed projects as well as any projects that directly or indirectly depend on a changed project. Specify \\"--changed-projects-only\\" to ignore dependent projects, only rebuilding those projects whose files were changed. Note that this parameter is \\"unsafe\\"; it is up to the developer to ensure that the ignored projects are okay to ignore.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--changed-projects-only", + "required": false, + "shortName": "-c", + }, + Object { + "description": "Skips execution of the \\"eventHooks\\" scripts defined in rush.json. Make sure you know what you are skipping.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--ignore-hooks", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Specifies the directory where Node.js diagnostic reports will be written. This directory will contain a subdirectory for each project and phase.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--node-diagnostic-dir", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Logs information about the components of the build cache ids for individual operations. This is useful for debugging the incremental build logic.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--debug-build-cache-ids", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Perform a production build, including minification and localization steps", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--ship", + "required": false, + "shortName": "-s", + }, + Object { + "description": "Perform a fast build, which disables certain tasks such as unit tests and linting", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--minimal", + "required": false, + "shortName": "-m", + }, + ], + }, + Object { + "actionName": "rebuild", + "parameters": Array [ + Object { + "description": "Specifies the maximum number of concurrent processes to launch during a build. The COUNT should be a positive integer, a percentage value (eg. \\"50%\\") or the word \\"max\\" to specify a count that is equal to the number of CPU cores. If this parameter is omitted, then the default value depends on the operating system and number of CPU cores.", + "environmentVariable": "RUSH_PARALLELISM", + "kind": "String", + "longName": "--parallelism", + "required": false, + "shortName": "-p", + }, + Object { + "description": "After the build is complete, print additional statistics and CPU usage information, including an ASCII chart of the start and stop times for each operation.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--timeline", + "required": false, + "shortName": undefined, + }, + Object { + "description": "(EXPERIMENTAL) Before the build starts, log information about the cobuild state. This will include information about clusters and the projects that are part of each cluster.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--log-cobuild-plan", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--to\\" parameter expands this selection to include PROJECT and all its dependencies. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to", + "required": false, + "shortName": "-t", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--to-except\\" parameter expands this selection to include all dependencies of PROJECT, but not PROJECT itself. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to-except", + "required": false, + "shortName": "-T", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--from\\" parameter expands this selection to include PROJECT and all projects that depend on it, plus all dependencies of this set. \\".\\" can be used as shorthand for the project in the current working directory. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--from", + "required": false, + "shortName": "-f", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--only\\" parameter expands this selection to include PROJECT; its dependencies are not added. \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--only", + "required": false, + "shortName": "-o", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--impacted-by\\" parameter expands this selection to include PROJECT and any projects that depend on PROJECT (and thus might be broken by changes to PROJECT). \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--impacted-by", + "required": false, + "shortName": "-i", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. Each \\"--impacted-by-except\\" parameter works the same as \\"--impacted-by\\" except that PROJECT itself is not added to the selection. \\".\\" can be used as shorthand for the project in the current working directory. Note that this parameter is \\"unsafe\\" as it may produce a selection that excludes some dependencies. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--impacted-by-except", + "required": false, + "shortName": "-I", + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. The \\"--to-version-policy\\" parameter is equivalent to specifying \\"--to\\" for each of the projects belonging to VERSION_POLICY_NAME. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--to-version-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Normally all projects in the monorepo will be processed; adding this parameter will instead select a subset of projects. The \\"--from-version-policy\\" parameter is equivalent to specifying \\"--from\\" for each of the projects belonging to VERSION_POLICY_NAME. For details, refer to the website article \\"Selecting subsets of projects\\".", + "environmentVariable": undefined, + "kind": "StringList", + "longName": "--from-version-policy", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Display the logs during the build, rather than just displaying the build status summary", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--verbose", + "required": false, + "shortName": "-v", + }, + Object { + "description": "If the selected projects are \\"unsafe\\" (missing some dependencies), add the minimal set of phase dependencies. For example, \\"--from A\\" normally might include the \\"_phase:test\\" phase for A's dependencies, even though changes to A can't break those tests. Using \\"--impacted-by A --include-phase-deps\\" avoids that work by performing \\"_phase:test\\" only for downstream projects.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--include-phase-deps", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Skips execution of the \\"eventHooks\\" scripts defined in rush.json. Make sure you know what you are skipping.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--ignore-hooks", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Specifies the directory where Node.js diagnostic reports will be written. This directory will contain a subdirectory for each project and phase.", + "environmentVariable": undefined, + "kind": "String", + "longName": "--node-diagnostic-dir", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Logs information about the components of the build cache ids for individual operations. This is useful for debugging the incremental build logic.", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--debug-build-cache-ids", + "required": false, + "shortName": undefined, + }, + Object { + "description": "Perform a production build, including minification and localization steps", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--ship", + "required": false, + "shortName": "-s", + }, + Object { + "description": "Perform a fast build, which disables certain tasks such as unit tests and linting", + "environmentVariable": undefined, + "kind": "Flag", + "longName": "--minimal", + "required": false, + "shortName": "-m", + }, + ], + }, + ], +} +`; diff --git a/libraries/rush-lib/src/api/test/__snapshots__/RushConfiguration.test.ts.snap b/libraries/rush-lib/src/api/test/__snapshots__/RushConfiguration.test.ts.snap index 79352d781bb..7b0f0eef7fd 100644 --- a/libraries/rush-lib/src/api/test/__snapshots__/RushConfiguration.test.ts.snap +++ b/libraries/rush-lib/src/api/test/__snapshots__/RushConfiguration.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`RushConfiguration RushConfigurationProject correctly updates the packageJson property after the packageJson is edited by packageJsonEditor: dependencyProjects after 1`] = ` Array [ diff --git a/libraries/rush-lib/src/api/test/__snapshots__/RushConfigurationProject.test.ts.snap b/libraries/rush-lib/src/api/test/__snapshots__/RushConfigurationProject.test.ts.snap new file mode 100644 index 00000000000..e488c65ddca --- /dev/null +++ b/libraries/rush-lib/src/api/test/__snapshots__/RushConfigurationProject.test.ts.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`validateRelativePathField should throw an error if the path contains backslashes 1`] = `"The value \\"path\\\\to\\\\project\\" in the \\"someField\\" field in \\"/repo/rush.json\\" may not contain backslashes ('\\\\'), since they are interpreted differently on POSIX and Windows. Paths must use '/' as the path separator."`; + +exports[`validateRelativePathField should throw an error if the path contains backslashes 2`] = `"The value \\"path\\\\\\" in the \\"someOtherField\\" field in \\"/repo/rush.json\\" may not contain backslashes ('\\\\'), since they are interpreted differently on POSIX and Windows. Paths must use '/' as the path separator."`; + +exports[`validateRelativePathField should throw an error if the path ends in a trailing slash 1`] = `"The value \\"path/to/project/\\" in the \\"someField\\" field in \\"/repo/rush.json\\" may not end with a trailing '/' character."`; + +exports[`validateRelativePathField should throw an error if the path ends in a trailing slash 2`] = `"The value \\"p/\\" in the \\"someField\\" field in \\"/repo/rush.json\\" may not end with a trailing '/' character."`; + +exports[`validateRelativePathField should throw an error if the path is not normalized 1`] = `"The value \\"path/../to/project\\" in the \\"someField\\" field in \\"/repo/rush.json\\" should be replaced with its normalized form \\"to/project\\"."`; + +exports[`validateRelativePathField should throw an error if the path is not normalized 2`] = `"The value \\"path/./to/project\\" in the \\"someField\\" field in \\"/repo/rush.json\\" should be replaced with its normalized form \\"path/to/project\\"."`; + +exports[`validateRelativePathField should throw an error if the path is not normalized 3`] = `"The value \\"./path/to/project\\" in the \\"someField\\" field in \\"/repo/rush.json\\" should be replaced with its normalized form \\"path/to/project\\"."`; + +exports[`validateRelativePathField should throw an error if the path is not relative 1`] = `"The value \\"C:/path/to/project\\" in the \\"projectFolder\\" field in \\"/rush.json\\" must be a relative path."`; + +exports[`validateRelativePathField should throw an error if the path is not relative 2`] = `"The value \\"/path/to/project\\" in the \\"publishFolder\\" field in \\"/rush.json\\" must be a relative path."`; diff --git a/libraries/rush-lib/src/api/test/__snapshots__/RushProjectConfiguration.test.ts.snap b/libraries/rush-lib/src/api/test/__snapshots__/RushProjectConfiguration.test.ts.snap index c087d0f6ad5..8de9fe79d08 100644 --- a/libraries/rush-lib/src/api/test/__snapshots__/RushProjectConfiguration.test.ts.snap +++ b/libraries/rush-lib/src/api/test/__snapshots__/RushProjectConfiguration.test.ts.snap @@ -1,8 +1,21 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`RushProjectConfiguration operationSettingsByOperationName allows outputFolderNames to be inside subfolders 1`] = ` +exports[`RushProjectConfiguration getCacheDisabledReason Indicates if the build cache is completely disabled 1`] = `"Caching has been disabled for this project."`; + +exports[`RushProjectConfiguration getCacheDisabledReason Indicates if the phase behavior is not defined 1`] = `"This project does not define the caching behavior of the \\"z\\" command, so caching has been disabled."`; + +exports[`RushProjectConfiguration getCacheDisabledReason Indicates if the phase has disabled the cache 1`] = `"Caching has been disabled for this project's \\"_phase:a\\" command."`; + +exports[`RushProjectConfiguration getCacheDisabledReason Indicates if tracked files are outputs of the phase 1`] = `"The following files are used to calculate project state and are considered project output: test-project-c/.cache/b/foo"`; + +exports[`RushProjectConfiguration getCacheDisabledReason returns reason if the operation is runnable 1`] = `"Caching has been disabled for this project's \\"_phase:a\\" command."`; + +exports[`RushProjectConfiguration operationSettingsByOperationName allows outputFolderNames to be inside subfolders 1`] = `Array []`; + +exports[`RushProjectConfiguration operationSettingsByOperationName allows outputFolderNames to be inside subfolders 2`] = ` Map { "_phase:a" => Object { + "disableBuildCacheForOperation": true, "operationName": "_phase:a", "outputFolderNames": Array [ ".cache/a", @@ -17,23 +30,15 @@ Map { } `; -exports[`RushProjectConfiguration operationSettingsByOperationName allows outputFolderNames to be inside subfolders: validation: terminal error 1`] = `""`; - -exports[`RushProjectConfiguration operationSettingsByOperationName allows outputFolderNames to be inside subfolders: validation: terminal output 1`] = `""`; - -exports[`RushProjectConfiguration operationSettingsByOperationName allows outputFolderNames to be inside subfolders: validation: terminal verbose 1`] = `""`; - -exports[`RushProjectConfiguration operationSettingsByOperationName allows outputFolderNames to be inside subfolders: validation: terminal warning 1`] = `""`; - -exports[`RushProjectConfiguration operationSettingsByOperationName does not allow one outputFolderName to be under another: validation: terminal error 1`] = `"The project \\"test-project-d\\" has a \\"config/rush-project.json\\" configuration that defines two operations in the same command whose \\"outputFolderNames\\" would overlap. Operations outputs in the same command must be disjoint so that they can be independently cached.[n][n]The \\"a/b\\" path overlaps between these operations: \\"_phase:a\\"[n]"`; - -exports[`RushProjectConfiguration operationSettingsByOperationName does not allow one outputFolderName to be under another: validation: terminal output 1`] = `""`; - -exports[`RushProjectConfiguration operationSettingsByOperationName does not allow one outputFolderName to be under another: validation: terminal verbose 1`] = `""`; +exports[`RushProjectConfiguration operationSettingsByOperationName does not allow one outputFolderName to be under another 1`] = ` +Array [ + "[ error] The project \\"test-project-d\\" has a \\"config/rush-project.json\\" configuration that defines two operations in the same command whose \\"outputFolderNames\\" would overlap. Operations outputs in the same command must be disjoint so that they can be independently cached. The \\"a/b\\" path overlaps between these operations: \\"_phase:b\\", \\"_phase:a\\"[n]", +] +`; -exports[`RushProjectConfiguration operationSettingsByOperationName does not allow one outputFolderName to be under another: validation: terminal warning 1`] = `""`; +exports[`RushProjectConfiguration operationSettingsByOperationName loads a rush-project.json config that extends another config file 1`] = `Array []`; -exports[`RushProjectConfiguration operationSettingsByOperationName loads a rush-project.json config that extends another config file 1`] = ` +exports[`RushProjectConfiguration operationSettingsByOperationName loads a rush-project.json config that extends another config file 2`] = ` Map { "_phase:a" => Object { "operationName": "_phase:a", @@ -51,12 +56,22 @@ Map { } `; -exports[`RushProjectConfiguration operationSettingsByOperationName loads a rush-project.json config that extends another config file: validation: terminal error 1`] = `""`; - -exports[`RushProjectConfiguration operationSettingsByOperationName loads a rush-project.json config that extends another config file: validation: terminal output 1`] = `""`; +exports[`RushProjectConfiguration operationSettingsByOperationName throws an error when loading a rush-project.json config that lists an operation twice 1`] = `"The operation \\"_phase:a\\" occurs multiple times in the \\"operationSettings\\" array in \\"/config/rush-project.json\\"."`; -exports[`RushProjectConfiguration operationSettingsByOperationName loads a rush-project.json config that extends another config file: validation: terminal verbose 1`] = `""`; +exports[`RushProjectConfiguration operationSettingsByOperationName validates mix of existent and nonexistent parameters 1`] = ` +Array [ + "[ error] The project \\"test-project-g\\" has a \\"config/rush-project.json\\" configuration that specifies invalid parameter(s) in \\"parameterNamesToIgnore\\" for operation \\"_phase:build\\": --nonexistent-param. Valid parameters for this operation are: --production, --verbose.[n]", +] +`; -exports[`RushProjectConfiguration operationSettingsByOperationName loads a rush-project.json config that extends another config file: validation: terminal warning 1`] = `""`; +exports[`RushProjectConfiguration operationSettingsByOperationName validates nonexistent parameters when operation has valid parameters 1`] = ` +Array [ + "[ error] The project \\"test-project-f\\" has a \\"config/rush-project.json\\" configuration that specifies invalid parameter(s) in \\"parameterNamesToIgnore\\" for operation \\"_phase:build\\": --nonexistent-param, --another-nonexistent. Valid parameters for this operation are: --production, --verbose.[n]", +] +`; -exports[`RushProjectConfiguration operationSettingsByOperationName throws an error when loading a rush-project.json config that lists an operation twice 1`] = `"The operation \\"_phase:a\\" occurs multiple times in the \\"operationSettings\\" array in \\"/config/rush-project.json\\"."`; +exports[`RushProjectConfiguration operationSettingsByOperationName validates that parameters in parameterNamesToIgnore exist for the operation 1`] = ` +Array [ + "[ error] The project \\"test-project-e\\" has a \\"config/rush-project.json\\" configuration that specifies invalid parameter(s) in \\"parameterNamesToIgnore\\" for operation \\"_phase:build\\": --invalid-parameter, --another-invalid, -malformed-parameter. Valid parameters for this operation are: (none).[n]", +] +`; diff --git a/libraries/rush-lib/src/api/test/__snapshots__/Subspace.test.ts.snap b/libraries/rush-lib/src/api/test/__snapshots__/Subspace.test.ts.snap new file mode 100644 index 00000000000..f30c327a612 --- /dev/null +++ b/libraries/rush-lib/src/api/test/__snapshots__/Subspace.test.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`Subspace getPackageJsonInjectedDependenciesHash computes a hash when injected dependencies exist 1`] = `"d0931e89ec05817885cbf2623217f99c90ab3ab4"`; diff --git a/libraries/rush-lib/src/api/test/jsonFiles/common-versions-with-ensureConsistentVersionsTrue.json b/libraries/rush-lib/src/api/test/jsonFiles/common-versions-with-ensureConsistentVersionsTrue.json new file mode 100644 index 00000000000..91b891e85f8 --- /dev/null +++ b/libraries/rush-lib/src/api/test/jsonFiles/common-versions-with-ensureConsistentVersionsTrue.json @@ -0,0 +1,9 @@ +{ + "ensureConsistentVersions": true, + "preferredVersions": { + "@scope/library-1": "~3.2.1" + }, + "allowedAlternativeVersions": { + "library-3": ["^1.2.3"] + } +} diff --git a/libraries/rush-lib/src/api/test/jsonFiles/custom-tips.error.json b/libraries/rush-lib/src/api/test/jsonFiles/custom-tips.error.json new file mode 100644 index 00000000000..3764417bb58 --- /dev/null +++ b/libraries/rush-lib/src/api/test/jsonFiles/custom-tips.error.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/custom-tips.schema.json", + + "customTips": [ + { + "tipId": "TIP_RUSH_INCONSISTENT_VERSIONS", + "message": "duplicate 1" + }, + { + "tipId": "TIP_RUSH_INCONSISTENT_VERSIONS", + "message": "duplicate 2" + } + ] +} diff --git a/libraries/rush-lib/src/api/test/jsonFiles/test-project-a/config/rush-project.json b/libraries/rush-lib/src/api/test/jsonFiles/test-project-a/config/rush-project.json index c8743b4eb22..fe852bfeda4 100644 --- a/libraries/rush-lib/src/api/test/jsonFiles/test-project-a/config/rush-project.json +++ b/libraries/rush-lib/src/api/test/jsonFiles/test-project-a/config/rush-project.json @@ -1,6 +1,8 @@ { "extends": "../../rush-project-base.json", + "disableBuildCacheForProject": true, + "operationSettings": [ { "operationName": "_phase:a", diff --git a/libraries/rush-lib/src/api/test/jsonFiles/test-project-c/config/rush-project.json b/libraries/rush-lib/src/api/test/jsonFiles/test-project-c/config/rush-project.json index 3609b5a63b9..6b2625686a3 100644 --- a/libraries/rush-lib/src/api/test/jsonFiles/test-project-c/config/rush-project.json +++ b/libraries/rush-lib/src/api/test/jsonFiles/test-project-c/config/rush-project.json @@ -2,7 +2,8 @@ "operationSettings": [ { "operationName": "_phase:a", - "outputFolderNames": [".cache/a"] + "outputFolderNames": [".cache/a"], + "disableBuildCacheForOperation": true }, { "operationName": "_phase:b", diff --git a/libraries/rush-lib/src/api/test/jsonFiles/test-project-e/config/rush-project.json b/libraries/rush-lib/src/api/test/jsonFiles/test-project-e/config/rush-project.json new file mode 100644 index 00000000000..0dfe8b808e0 --- /dev/null +++ b/libraries/rush-lib/src/api/test/jsonFiles/test-project-e/config/rush-project.json @@ -0,0 +1,9 @@ +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["lib"], + "parameterNamesToIgnore": ["--invalid-parameter", "--another-invalid", "-malformed-parameter"] + } + ] +} diff --git a/libraries/rush-lib/src/api/test/jsonFiles/test-project-f/config/rush-project.json b/libraries/rush-lib/src/api/test/jsonFiles/test-project-f/config/rush-project.json new file mode 100644 index 00000000000..fe9cc4909ae --- /dev/null +++ b/libraries/rush-lib/src/api/test/jsonFiles/test-project-f/config/rush-project.json @@ -0,0 +1,9 @@ +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["lib"], + "parameterNamesToIgnore": ["--nonexistent-param", "--another-nonexistent"] + } + ] +} diff --git a/libraries/rush-lib/src/api/test/jsonFiles/test-project-g/config/rush-project.json b/libraries/rush-lib/src/api/test/jsonFiles/test-project-g/config/rush-project.json new file mode 100644 index 00000000000..ab34f9ea349 --- /dev/null +++ b/libraries/rush-lib/src/api/test/jsonFiles/test-project-g/config/rush-project.json @@ -0,0 +1,9 @@ +{ + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": ["lib"], + "parameterNamesToIgnore": ["--production", "--nonexistent-param", "--verbose"] + } + ] +} diff --git a/libraries/rush-lib/src/api/test/repo/apps/app1/package.json b/libraries/rush-lib/src/api/test/repo/apps/app1/package.json new file mode 100644 index 00000000000..571970cfa29 --- /dev/null +++ b/libraries/rush-lib/src/api/test/repo/apps/app1/package.json @@ -0,0 +1,5 @@ +{ + "name": "app1", + "version": "1.0.0", + "description": "Test app 1" +} diff --git a/libraries/rush-lib/src/api/test/repo/apps/app2/package.json b/libraries/rush-lib/src/api/test/repo/apps/app2/package.json new file mode 100644 index 00000000000..a030c5e5775 --- /dev/null +++ b/libraries/rush-lib/src/api/test/repo/apps/app2/package.json @@ -0,0 +1,5 @@ +{ + "name": "app2", + "version": "1.0.0", + "description": "Test app 2" +} diff --git a/libraries/rush-lib/src/api/test/repo/common/config/rush/custom-tips.json b/libraries/rush-lib/src/api/test/repo/common/config/rush/custom-tips.json new file mode 100644 index 00000000000..6b7a05c8129 --- /dev/null +++ b/libraries/rush-lib/src/api/test/repo/common/config/rush/custom-tips.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/custom-tips.schema.json", + + "customTips": [ + { + "tipId": "TIP_RUSH_INCONSISTENT_VERSIONS", + "message": "This is so wrong my friend. Please read this doc for more information: google.com" + } + ] +} diff --git a/libraries/rush-lib/src/api/test/repo/common/config/rush/version-policies.json b/libraries/rush-lib/src/api/test/repo/common/config/rush/version-policies.json new file mode 100644 index 00000000000..ad3bf99e03d --- /dev/null +++ b/libraries/rush-lib/src/api/test/repo/common/config/rush/version-policies.json @@ -0,0 +1,8 @@ +[ + { + "definitionName": "lockStepVersion", + "policyName": "testPolicy", + "version": "1.0.0", + "nextBump": "minor" + } +] diff --git a/libraries/rush-lib/src/api/test/repo/project1/package.json b/libraries/rush-lib/src/api/test/repo/project1/package.json index cef656a4e3a..4809d0e8e37 100644 --- a/libraries/rush-lib/src/api/test/repo/project1/package.json +++ b/libraries/rush-lib/src/api/test/repo/project1/package.json @@ -9,7 +9,7 @@ "author": "", "license": "ISC", "dependencies": { - "lodash": "^4.17.15", + "semver": "^7.5.4", "react": "^0.14.9" } } diff --git a/libraries/rush-lib/src/api/test/repo/project2/package.json b/libraries/rush-lib/src/api/test/repo/project2/package.json index 939deab00d7..2fa2757c5c0 100644 --- a/libraries/rush-lib/src/api/test/repo/project2/package.json +++ b/libraries/rush-lib/src/api/test/repo/project2/package.json @@ -9,7 +9,7 @@ "author": "", "license": "ISC", "dependencies": { - "lodash": "^4.17.15", + "semver": "^7.5.4", "react": "^15.5.4" } } diff --git a/libraries/rush-lib/src/api/test/repo/rush-npm.json b/libraries/rush-lib/src/api/test/repo/rush-npm.json index a1bead19121..5f448332dcd 100644 --- a/libraries/rush-lib/src/api/test/repo/rush-npm.json +++ b/libraries/rush-lib/src/api/test/repo/rush-npm.json @@ -27,20 +27,37 @@ { "packageName": "project1", "projectFolder": "project1", - "reviewCategory": "third-party" + "reviewCategory": "third-party", + "tags": ["frontend", "ui"], + "versionPolicyName": "testPolicy" }, { "packageName": "project2", "projectFolder": "project2", "reviewCategory": "third-party", - "skipRushCheck": true + "skipRushCheck": true, + "tags": ["backend"] }, { "packageName": "project3", "projectFolder": "project3", - "reviewCategory": "prototype" + "reviewCategory": "prototype", + "tags": ["frontend"], + "versionPolicyName": "testPolicy" + }, + + { + "packageName": "app1", + "projectFolder": "apps/app1", + "reviewCategory": "first-party" + }, + + { + "packageName": "app2", + "projectFolder": "apps/app2", + "reviewCategory": "first-party" } ] } diff --git a/libraries/rush-lib/src/api/test/repoCatalogs/common/config/rush/pnpm-config.json b/libraries/rush-lib/src/api/test/repoCatalogs/common/config/rush/pnpm-config.json new file mode 100644 index 00000000000..dc7c0f32d81 --- /dev/null +++ b/libraries/rush-lib/src/api/test/repoCatalogs/common/config/rush/pnpm-config.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/pnpm-config.schema.json", + "globalCatalogs": { + "default": { + "react": "^18.0.0", + "react-dom": "^18.0.0", + "typescript": "~5.3.0" + }, + "internal": { + "semver": "^7.5.4", + "axios": "^1.6.0" + } + } +} diff --git a/libraries/rush-lib/src/api/test/repoCatalogs/project1/package.json b/libraries/rush-lib/src/api/test/repoCatalogs/project1/package.json new file mode 100644 index 00000000000..31cf82c13cf --- /dev/null +++ b/libraries/rush-lib/src/api/test/repoCatalogs/project1/package.json @@ -0,0 +1,4 @@ +{ + "name": "project1", + "version": "1.0.0" +} diff --git a/libraries/rush-lib/src/api/test/repoCatalogs/rush.json b/libraries/rush-lib/src/api/test/repoCatalogs/rush.json new file mode 100644 index 00000000000..a045354bbc2 --- /dev/null +++ b/libraries/rush-lib/src/api/test/repoCatalogs/rush.json @@ -0,0 +1,13 @@ +{ + "pnpmVersion": "9.5.0", + "rushVersion": "5.46.1", + "projectFolderMinDepth": 1, + "projectFolderMaxDepth": 99, + + "projects": [ + { + "packageName": "project1", + "projectFolder": "project1" + } + ] +} diff --git a/libraries/rush-lib/src/api/test/repoInjectedDeps/consumer/package.json b/libraries/rush-lib/src/api/test/repoInjectedDeps/consumer/package.json new file mode 100644 index 00000000000..9fb49f623fd --- /dev/null +++ b/libraries/rush-lib/src/api/test/repoInjectedDeps/consumer/package.json @@ -0,0 +1,12 @@ +{ + "name": "consumer", + "version": "1.0.0", + "dependencies": { + "provider": "workspace:*" + }, + "dependenciesMeta": { + "provider": { + "injected": true + } + } +} diff --git a/libraries/rush-lib/src/api/test/repoInjectedDeps/provider/package.json b/libraries/rush-lib/src/api/test/repoInjectedDeps/provider/package.json new file mode 100644 index 00000000000..22b969cb820 --- /dev/null +++ b/libraries/rush-lib/src/api/test/repoInjectedDeps/provider/package.json @@ -0,0 +1,10 @@ +{ + "name": "provider", + "version": "1.0.0", + "dependencies": { + "lodash": "^4.17.21" + }, + "devDependencies": { + "typescript": "~5.3.0" + } +} diff --git a/libraries/rush-lib/src/api/test/repoInjectedDeps/rush.json b/libraries/rush-lib/src/api/test/repoInjectedDeps/rush.json new file mode 100644 index 00000000000..eebb706445e --- /dev/null +++ b/libraries/rush-lib/src/api/test/repoInjectedDeps/rush.json @@ -0,0 +1,17 @@ +{ + "pnpmVersion": "9.5.0", + "rushVersion": "5.46.1", + "projectFolderMinDepth": 1, + "projectFolderMaxDepth": 99, + + "projects": [ + { + "packageName": "consumer", + "projectFolder": "consumer" + }, + { + "packageName": "provider", + "projectFolder": "provider" + } + ] +} diff --git a/libraries/rush-lib/src/cli/CommandLineMigrationAdvisor.ts b/libraries/rush-lib/src/cli/CommandLineMigrationAdvisor.ts index 710d453fd2c..15a40c596f7 100644 --- a/libraries/rush-lib/src/cli/CommandLineMigrationAdvisor.ts +++ b/libraries/rush-lib/src/cli/CommandLineMigrationAdvisor.ts @@ -1,8 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import { PrintUtilities } from '@rushstack/terminal'; +import { Colorize, PrintUtilities } from '@rushstack/terminal'; import { RushConstants } from '../logic/RushConstants'; @@ -15,35 +14,25 @@ export class CommandLineMigrationAdvisor { if (args.length > 0) { if (args[0] === 'generate') { - CommandLineMigrationAdvisor._reportDeprecated( - 'Instead of "rush generate", use "rush update" or "rush update --full".' - ); + _reportDeprecated('Instead of "rush generate", use "rush update" or "rush update --full".'); return false; } if (args[0] === 'install') { if (args.indexOf('--full-clean') >= 0) { - CommandLineMigrationAdvisor._reportDeprecated( - 'Instead of "rush install --full-clean", use "rush purge --unsafe".' - ); + _reportDeprecated('Instead of "rush install --full-clean", use "rush purge --unsafe".'); return false; } if (args.indexOf('-C') >= 0) { - CommandLineMigrationAdvisor._reportDeprecated( - 'Instead of "rush install -C", use "rush purge --unsafe".' - ); + _reportDeprecated('Instead of "rush install -C", use "rush purge --unsafe".'); return false; } if (args.indexOf('--clean') >= 0) { - CommandLineMigrationAdvisor._reportDeprecated( - 'Instead of "rush install --clean", use "rush install --purge".' - ); + _reportDeprecated('Instead of "rush install --clean", use "rush install --purge".'); return false; } if (args.indexOf('-c') >= 0) { - CommandLineMigrationAdvisor._reportDeprecated( - 'Instead of "rush install -c", use "rush install --purge".' - ); + _reportDeprecated('Instead of "rush install -c", use "rush install --purge".'); return false; } } @@ -52,22 +41,26 @@ export class CommandLineMigrationAdvisor { // Everything is okay return true; } +} - private static _reportDeprecated(message: string): void { - console.error( - colors.red( - PrintUtilities.wrapWords( - 'ERROR: You specified an outdated command-line that is no longer supported by this version of Rush:' - ) - ) - ); - console.error(colors.yellow(PrintUtilities.wrapWords(message))); - console.error(); - console.error( +function _reportDeprecated(message: string): void { + // eslint-disable-next-line no-console + console.error( + Colorize.red( PrintUtilities.wrapWords( - `For command-line help, type "rush -h". For migration instructions,` + - ` please visit ${RushConstants.rushWebSiteUrl}` + 'ERROR: You specified an outdated command-line that is no longer supported by this version of Rush:' ) - ); - } + ) + ); + // eslint-disable-next-line no-console + console.error(Colorize.yellow(PrintUtilities.wrapWords(message))); + // eslint-disable-next-line no-console + console.error(); + // eslint-disable-next-line no-console + console.error( + PrintUtilities.wrapWords( + `For command-line help, type "rush -h". For migration instructions,` + + ` please visit ${RushConstants.rushWebSiteUrl}` + ) + ); } diff --git a/libraries/rush-lib/src/cli/RushCommandLineParser.ts b/libraries/rush-lib/src/cli/RushCommandLineParser.ts index 3f483e054f1..47f2b3a640b 100644 --- a/libraries/rush-lib/src/cli/RushCommandLineParser.ts +++ b/libraries/rush-lib/src/cli/RushCommandLineParser.ts @@ -1,28 +1,33 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; +import * as path from 'node:path'; -import { CommandLineParser, CommandLineFlagParameter, CommandLineHelper } from '@rushstack/ts-command-line'; import { - InternalError, - AlreadyReportedError, + CommandLineParser, + type CommandLineFlagParameter, + CommandLineHelper +} from '@rushstack/ts-command-line'; +import { InternalError, AlreadyReportedError, Text } from '@rushstack/node-core-library'; +import { ConsoleTerminalProvider, - Terminal -} from '@rushstack/node-core-library'; -import { PrintUtilities } from '@rushstack/terminal'; + Terminal, + PrintUtilities, + Colorize, + type ITerminal +} from '@rushstack/terminal'; import { RushConfiguration } from '../api/RushConfiguration'; import { RushConstants } from '../logic/RushConstants'; import { - Command, + type Command, CommandLineConfiguration, - IGlobalCommandConfig, - IPhasedCommandConfig + type IGlobalCommandConfig, + type IPhasedCommandConfig } from '../api/CommandLineConfiguration'; - import { AddAction } from './actions/AddAction'; +import { AlertAction } from './actions/AlertAction'; +import { BridgePackageAction } from './actions/BridgePackageAction'; import { ChangeAction } from './actions/ChangeAction'; import { CheckAction } from './actions/CheckAction'; import { DeployAction } from './actions/DeployAction'; @@ -30,7 +35,9 @@ import { InitAction } from './actions/InitAction'; import { InitAutoinstallerAction } from './actions/InitAutoinstallerAction'; import { InitDeployAction } from './actions/InitDeployAction'; import { InstallAction } from './actions/InstallAction'; +import { InstallAutoinstallerAction } from './actions/InstallAutoinstallerAction'; import { LinkAction } from './actions/LinkAction'; +import { LinkPackageAction } from './actions/LinkPackageAction'; import { ListAction } from './actions/ListAction'; import { PublishAction } from './actions/PublishAction'; import { PurgeAction } from './actions/PurgeAction'; @@ -39,21 +46,24 @@ import { ScanAction } from './actions/ScanAction'; import { UnlinkAction } from './actions/UnlinkAction'; import { UpdateAction } from './actions/UpdateAction'; import { UpdateAutoinstallerAction } from './actions/UpdateAutoinstallerAction'; -import { VersionAction } from './actions/VersionAction'; import { UpdateCloudCredentialsAction } from './actions/UpdateCloudCredentialsAction'; import { UpgradeInteractiveAction } from './actions/UpgradeInteractiveAction'; - +import { VersionAction } from './actions/VersionAction'; import { GlobalScriptAction } from './scriptActions/GlobalScriptAction'; -import { IBaseScriptActionOptions } from './scriptActions/BaseScriptAction'; - +import { PhasedScriptAction } from './scriptActions/PhasedScriptAction'; +import type { IBaseScriptActionOptions } from './scriptActions/BaseScriptAction'; import { Telemetry } from '../logic/Telemetry'; import { RushGlobalFolder } from '../api/RushGlobalFolder'; import { NodeJsCompatibility } from '../logic/NodeJsCompatibility'; import { SetupAction } from './actions/SetupAction'; -import { ICustomCommandLineConfigurationInfo, PluginManager } from '../pluginFramework/PluginManager'; +import { type ICustomCommandLineConfigurationInfo, PluginManager } from '../pluginFramework/PluginManager'; import { RushSession } from '../pluginFramework/RushSession'; -import { PhasedScriptAction } from './scriptActions/PhasedScriptAction'; -import { IBuiltInPluginConfiguration } from '../pluginFramework/PluginLoader/BuiltInPluginLoader'; +import type { IBuiltInPluginConfiguration } from '../pluginFramework/PluginLoader/BuiltInPluginLoader'; +import { InitSubspaceAction } from './actions/InitSubspaceAction'; +import { RushAlerts } from '../utilities/RushAlerts'; +import { initializeDotEnv } from '../logic/dotenv'; +import { measureAsyncFn } from '../utilities/performance'; +import { EnvironmentVariableNames } from '../api/EnvironmentConfiguration'; /** * Options for `RushCommandLineParser`. @@ -77,6 +87,14 @@ export class RushCommandLineParser extends CommandLineParser { private readonly _rushOptions: IRushCommandLineParserOptions; private readonly _terminalProvider: ConsoleTerminalProvider; private readonly _terminal: Terminal; + private readonly _autocreateBuildCommand: boolean; + + /** + * The current working directory that was used to find the Rush configuration. + */ + public get cwd(): string { + return this._rushOptions.cwd; + } public constructor(options?: Partial) { super({ @@ -105,17 +123,24 @@ export class RushCommandLineParser extends CommandLineParser { description: 'Hide rush startup information' }); - this._terminalProvider = new ConsoleTerminalProvider(); - this._terminal = new Terminal(this._terminalProvider); + const terminalProvider: ConsoleTerminalProvider = new ConsoleTerminalProvider(); + this._terminalProvider = terminalProvider; + const terminal: Terminal = new Terminal(this._terminalProvider); + this._terminal = terminal; this._rushOptions = this._normalizeOptions(options || {}); + const { cwd, alreadyReportedNodeTooNewError, builtInPluginConfigurations } = this._rushOptions; + let rushJsonFilePath: string | undefined; try { - const rushJsonFilename: string | undefined = RushConfiguration.tryFindRushJsonLocation({ - startingFolder: this._rushOptions.cwd, + rushJsonFilePath = RushConfiguration.tryFindRushJsonLocation({ + startingFolder: cwd, showVerbose: !this._restrictConsoleOutput }); - if (rushJsonFilename) { - this.rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFilename); + + initializeDotEnv(terminal, rushJsonFilePath); + + if (rushJsonFilePath) { + this.rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFilePath); } } catch (error) { this._reportErrorAndSetExitCode(error as Error); @@ -123,7 +148,7 @@ export class RushCommandLineParser extends CommandLineParser { NodeJsCompatibility.warnAboutCompatibilityIssues({ isRushLib: true, - alreadyReportedNodeTooNewError: this._rushOptions.alreadyReportedNodeTooNewError, + alreadyReportedNodeTooNewError, rushConfiguration: this.rushConfiguration }); @@ -131,29 +156,39 @@ export class RushCommandLineParser extends CommandLineParser { this.rushSession = new RushSession({ getIsDebugMode: () => this.isDebug, - terminalProvider: this._terminalProvider + terminalProvider }); this.pluginManager = new PluginManager({ rushSession: this.rushSession, rushConfiguration: this.rushConfiguration, - terminal: this._terminal, - builtInPluginConfigurations: this._rushOptions.builtInPluginConfigurations, + terminal, + builtInPluginConfigurations, restrictConsoleOutput: this._restrictConsoleOutput, rushGlobalFolder: this.rushGlobalFolder }); - this._populateActions(); - const pluginCommandLineConfigurations: ICustomCommandLineConfigurationInfo[] = this.pluginManager.tryGetCustomCommandLineConfigurationInfos(); + + const hasBuildCommandInPlugin: boolean = pluginCommandLineConfigurations.some((x) => + x.commandLineConfiguration.commands.has(RushConstants.buildCommandName) + ); + + // If the plugin has a build command, we don't need to autocreate the default build command. + this._autocreateBuildCommand = !hasBuildCommandInPlugin; + + this._populateActions(); + for (const { commandLineConfiguration, pluginLoader } of pluginCommandLineConfigurations) { try { this._addCommandLineConfigActions(commandLineConfiguration); } catch (e) { - this._terminal.writeErrorLine( - `Error from plugin ${pluginLoader.pluginName} by ${pluginLoader.packageName}: ${( - e as Error - ).toString()}` + this._reportErrorAndSetExitCode( + new Error( + `Error from plugin ${pluginLoader.pluginName} by ${pluginLoader.packageName}: ${( + e as Error + ).toString()}` + ) ); } } @@ -167,6 +202,10 @@ export class RushCommandLineParser extends CommandLineParser { return this._quietParameter.value; } + public get terminal(): ITerminal { + return this._terminal; + } + /** * Utility to determine if the app should restrict writing to the console. */ @@ -182,6 +221,11 @@ export class RushCommandLineParser extends CommandLineParser { } } + const quietModeValue: string | undefined = process.env[EnvironmentVariableNames.RUSH_QUIET_MODE]; + if (quietModeValue === '1' || quietModeValue === 'true') { + return true; + } + return false; } @@ -189,15 +233,19 @@ export class RushCommandLineParser extends CommandLineParser { this.telemetry?.flush(); } - public async execute(args?: string[]): Promise { - this._terminalProvider.verboseEnabled = this.isDebug; + public override async executeAsync(args?: string[]): Promise { + // debugParameter will be correctly parsed during super.executeAsync(), so manually parse here. + this._terminalProvider.verboseEnabled = this._terminalProvider.debugEnabled = + process.argv.indexOf('--debug') >= 0; - await this.pluginManager.tryInitializeUnassociatedPluginsAsync(); + await measureAsyncFn('rush:initializeUnassociatedPlugins', () => + this.pluginManager.tryInitializeUnassociatedPluginsAsync() + ); - return await super.execute(args); + return await super.executeAsync(args); } - protected async onExecute(): Promise { + protected override async onExecuteAsync(): Promise { // Defensively set the exit code to 1 so if Rush crashes for whatever reason, we'll have a nonzero exit code. // For example, Node.js currently has the inexcusable design of terminating with zero exit code when // there is an uncaught promise exception. This will supposedly be fixed in Node.js 9. @@ -211,12 +259,49 @@ export class RushCommandLineParser extends CommandLineParser { try { await this._wrapOnExecuteAsync(); + + // TODO: rushConfiguration is typed as "!: RushConfiguration" here, but can sometimes be undefined + if (this.rushConfiguration) { + try { + const { configuration: experiments } = this.rushConfiguration.experimentsConfiguration; + + if (experiments.rushAlerts) { + // TODO: Fix this + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const actionName: string = (this as any) + ._getArgumentParser() + .parseArgs(process.argv.slice(2)).action; + + // only display alerts when certain specific actions are triggered + if (RushAlerts.alertTriggerActions.includes(actionName)) { + this._terminal.writeDebugLine('Checking Rush alerts...'); + const rushAlerts: RushAlerts = await RushAlerts.loadFromConfigurationAsync( + this.rushConfiguration, + this._terminal + ); + // Print out alerts if have after each successful command actions + await rushAlerts.printAlertsAsync(); + } + } + } catch (error) { + if (error instanceof AlreadyReportedError) { + throw error; + } + // Generally the RushAlerts implementation should handle its own error reporting; if not, + // clarify the source, since the Rush Alerts behavior is nondeterministic and may not repro easily: + this._terminal.writeErrorLine(`\nAn unexpected error was encountered by the Rush alerts feature:`); + this._terminal.writeErrorLine(error.message); + throw new AlreadyReportedError(); + } + } + // If we make it here, everything went fine, so reset the exit code back to 0 process.exitCode = 0; } catch (error) { this._reportErrorAndSetExitCode(error as Error); } + // This only gets hit if the wrapped execution completes successfully await this.telemetry?.ensureFlushedAsync(); } @@ -233,9 +318,12 @@ export class RushCommandLineParser extends CommandLineParser { this.telemetry = new Telemetry(this.rushConfiguration, this.rushSession); } - await super.onExecute(); - if (this.telemetry) { - this.flushTelemetry(); + try { + await measureAsyncFn('rush:commandLineParser:onExecuteAsync', () => super.onExecuteAsync()); + } finally { + if (this.telemetry) { + this.flushTelemetry(); + } } } @@ -249,6 +337,7 @@ export class RushCommandLineParser extends CommandLineParser { this.addAction(new InitAction(this)); this.addAction(new InitAutoinstallerAction(this)); this.addAction(new InitDeployAction(this)); + this.addAction(new InitSubspaceAction(this)); this.addAction(new InstallAction(this)); this.addAction(new LinkAction(this)); this.addAction(new ListAction(this)); @@ -259,10 +348,14 @@ export class RushCommandLineParser extends CommandLineParser { this.addAction(new SetupAction(this)); this.addAction(new UnlinkAction(this)); this.addAction(new UpdateAction(this)); + this.addAction(new InstallAutoinstallerAction(this)); this.addAction(new UpdateAutoinstallerAction(this)); this.addAction(new UpdateCloudCredentialsAction(this)); this.addAction(new UpgradeInteractiveAction(this)); this.addAction(new VersionAction(this)); + this.addAction(new AlertAction(this)); + this.addAction(new BridgePackageAction(this)); + this.addAction(new LinkPackageAction(this)); this._populateScriptActions(); } catch (error) { @@ -281,8 +374,13 @@ export class RushCommandLineParser extends CommandLineParser { ); } - const commandLineConfiguration: CommandLineConfiguration = - CommandLineConfiguration.loadFromFileOrDefault(commandLineConfigFilePath); + // If a build action is already added by a plugin, we don't want to add a default "build" script + const doNotIncludeDefaultBuildCommands: boolean = !this._autocreateBuildCommand; + + const commandLineConfiguration: CommandLineConfiguration = CommandLineConfiguration.loadFromFileOrDefault( + commandLineConfigFilePath, + doNotIncludeDefaultBuildCommands + ); this._addCommandLineConfigActions(commandLineConfiguration); } @@ -311,18 +409,6 @@ export class RushCommandLineParser extends CommandLineParser { } case RushConstants.phasedCommandKind: { - if ( - !command.isSynthetic && // synthetic commands come from bulk commands - !this.rushConfiguration.experimentsConfiguration.configuration.phasedCommands - ) { - throw new Error( - `${RushConstants.commandLineFilename} defines a command "${command.name}" ` + - `that uses the "${RushConstants.phasedCommandKind}" command kind. To use this command kind, ` + - 'the "phasedCommands" experiment must be enabled. Note that this feature is not complete ' + - 'and will not work as expected.' - ); - } - this._addPhasedCommandLineConfigAction(commandLineConfiguration, command); break; } @@ -355,12 +441,11 @@ export class RushCommandLineParser extends CommandLineParser { commandLineConfiguration: CommandLineConfiguration, command: IGlobalCommandConfig ): void { - if ( - command.name === RushConstants.buildCommandName || - command.name === RushConstants.rebuildCommandName - ) { + const { name, shellCommand, autoinstallerName, providedByPlugin } = command; + + if (name === RushConstants.buildCommandName || name === RushConstants.rebuildCommandName) { throw new Error( - `${RushConstants.commandLineFilename} defines a command "${command.name}" using ` + + `${RushConstants.commandLineFilename} defines a command "${name}" using ` + `the command kind "${RushConstants.globalCommandKind}". This command can only be designated as a command ` + `kind "${RushConstants.bulkCommandKind}" or "${RushConstants.phasedCommandKind}".` ); @@ -373,8 +458,9 @@ export class RushCommandLineParser extends CommandLineParser { new GlobalScriptAction({ ...sharedCommandOptions, - shellCommand: command.shellCommand, - autoinstallerName: command.autoinstallerName + shellCommand, + autoinstallerName, + providedByPlugin }) ); } @@ -386,22 +472,41 @@ export class RushCommandLineParser extends CommandLineParser { const baseCommandOptions: IBaseScriptActionOptions = this._getSharedCommandActionOptions(commandLineConfiguration, command); + const { + enableParallelism, + incremental = false, + disableBuildCache = false, + allowOversubscription = true, + phases: initialPhases, + originalPhases, + watchPhases, + watchDebounceMs = RushConstants.defaultWatchDebounceMs, + alwaysWatch, + alwaysInstall, + includeAllProjectsInWatchGraph = false + } = command; this.addAction( new PhasedScriptAction({ ...baseCommandOptions, - enableParallelism: command.enableParallelism, - incremental: command.incremental || false, - disableBuildCache: command.disableBuildCache || false, + enableParallelism, + incremental, + disableBuildCache, + + // The Async.forEachAsync() API defaults allowOversubscription=false, whereas Rush historically + // defaults allowOversubscription=true to favor faster builds rather than strictly staying below + // the CPU limit. + allowOversubscription, - initialPhases: command.phases, - originalPhases: command.originalPhases, - watchPhases: command.watchPhases, - watchDebounceMs: command.watchDebounceMs ?? RushConstants.defaultWatchDebounceMs, + initialPhases, + originalPhases, + watchPhases, + watchDebounceMs, + includeAllProjectsInWatchGraph, phases: commandLineConfiguration.phases, - alwaysWatch: command.alwaysWatch, - alwaysInstall: command.alwaysInstall + alwaysWatch, + alwaysInstall }) ); } @@ -413,31 +518,40 @@ export class RushCommandLineParser extends CommandLineParser { // The colors package will eat multi-newlines, which could break formatting // in user-specified messages and instructions, so we prefer to color each // line individually. - const message: string = PrintUtilities.wrapWords(prefix + error.message) - .split(/\r?\n/) - .map((line) => colors.red(line)) + const message: string = Text.splitByNewLines(PrintUtilities.wrapWords(prefix + error.message)) + .map((line) => Colorize.red(line)) .join('\n'); + // eslint-disable-next-line no-console console.error(`\n${message}`); } if (this._debugParameter.value) { // If catchSyncErrors() called this, then show a call stack similar to what Node.js // would show for an uncaught error + // eslint-disable-next-line no-console console.error(`\n${error.stack}`); } this.flushTelemetry(); - // Ideally we want to eliminate all calls to process.exit() from our code, and replace them - // with normal control flow that properly cleans up its data structures. - // For this particular call, we have a problem that the RushCommandLineParser constructor - // performs nontrivial work that can throw an exception. Either the Rush class would need - // to handle reporting for those exceptions, or else _populateActions() should be moved - // to a RushCommandLineParser lifecycle stage that can handle it. - if (process.exitCode !== undefined) { - process.exit(process.exitCode); + const handleExit = (): never => { + // Ideally we want to eliminate all calls to process.exit() from our code, and replace them + // with normal control flow that properly cleans up its data structures. + // For this particular call, we have a problem that the RushCommandLineParser constructor + // performs nontrivial work that can throw an exception. Either the Rush class would need + // to handle reporting for those exceptions, or else _populateActions() should be moved + // to a RushCommandLineParser lifecycle stage that can handle it. + if (process.exitCode !== undefined) { + process.exit(process.exitCode); + } else { + process.exit(1); + } + }; + + if (this.telemetry && this.rushSession.hooks.flushTelemetry.isUsed()) { + this.telemetry.ensureFlushedAsync().then(handleExit).catch(handleExit); } else { - process.exit(1); + handleExit(); } } } diff --git a/libraries/rush-lib/src/cli/RushPnpmCommandLine.ts b/libraries/rush-lib/src/cli/RushPnpmCommandLine.ts index 6bd0ebb238e..5720ef8d547 100644 --- a/libraries/rush-lib/src/cli/RushPnpmCommandLine.ts +++ b/libraries/rush-lib/src/cli/RushPnpmCommandLine.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ILaunchOptions } from '../api/Rush'; +import type { ILaunchOptions } from '../api/Rush'; import { RushPnpmCommandLineParser } from './RushPnpmCommandLineParser'; export interface ILaunchRushPnpmInternalOptions extends ILaunchOptions {} @@ -11,6 +11,7 @@ export class RushPnpmCommandLine { RushPnpmCommandLineParser.initializeAsync(options) // RushPnpmCommandLineParser.executeAsync should never reject the promise .then((rushPnpmCommandLineParser) => rushPnpmCommandLineParser.executeAsync()) + // eslint-disable-next-line no-console .catch(console.error); } } diff --git a/libraries/rush-lib/src/cli/RushPnpmCommandLineParser.ts b/libraries/rush-lib/src/cli/RushPnpmCommandLineParser.ts index 25c2d7abd10..ecebe85de7b 100644 --- a/libraries/rush-lib/src/cli/RushPnpmCommandLineParser.ts +++ b/libraries/rush-lib/src/cli/RushPnpmCommandLineParser.ts @@ -1,34 +1,61 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { RushConfiguration } from '../api/RushConfiguration'; -import { NodeJsCompatibility } from '../logic/NodeJsCompatibility'; +import * as path from 'node:path'; + import { AlreadyReportedError, - Colors, - ConsoleTerminalProvider, EnvironmentMap, - Executable, FileConstants, FileSystem, - ITerminal, - ITerminalProvider, JsonFile, - JsonObject, - Terminal + type JsonObject, + Objects } from '@rushstack/node-core-library'; -import { PrintUtilities } from '@rushstack/terminal'; +import { + Colorize, + ConsoleTerminalProvider, + type ITerminal, + type ITerminalProvider, + Terminal, + PrintUtilities +} from '@rushstack/terminal'; + +import { RushConfiguration } from '../api/RushConfiguration'; +import { NodeJsCompatibility } from '../logic/NodeJsCompatibility'; import { RushConstants } from '../logic/RushConstants'; import { RushGlobalFolder } from '../api/RushGlobalFolder'; import { PurgeManager } from '../logic/PurgeManager'; - import type { IBuiltInPluginConfiguration } from '../pluginFramework/PluginLoader/BuiltInPluginLoader'; -import type { SpawnSyncReturns } from 'child_process'; import type { BaseInstallManager } from '../logic/base/BaseInstallManager'; import type { IInstallManagerOptions } from '../logic/base/BaseInstallManagerTypes'; +import { Utilities } from '../utilities/Utilities'; +import type { Subspace } from '../api/Subspace'; +import type { PnpmOptionsConfiguration } from '../logic/pnpm/PnpmOptionsConfiguration'; +import { PnpmWorkspaceFile } from '../logic/pnpm/PnpmWorkspaceFile'; +import { EnvironmentVariableNames } from '../api/EnvironmentConfiguration'; +import { initializeDotEnv } from '../logic/dotenv'; const RUSH_SKIP_CHECKS_PARAMETER: string = '--rush-skip-checks'; +const RUSH_PNPM_RECURSIVE_DEFAULT_COMMANDS: Set = new Set(['outdated', 'why']); + +function _hasRecursiveFlag(pnpmArgs: string[]): boolean { + return pnpmArgs.some((arg) => arg === '-r' || arg === '--recursive' || arg.startsWith('--recursive=')); +} + +function _hasGlobalFlag(pnpmArgs: string[]): boolean { + return pnpmArgs.some((arg) => arg === '-g' || arg === '--global' || arg.startsWith('--global=')); +} + +function _addDefaultRecursiveFlagIfNeeded(commandName: string, pnpmArgs: string[]): void { + if ( + RUSH_PNPM_RECURSIVE_DEFAULT_COMMANDS.has(commandName) && + !_hasRecursiveFlag(pnpmArgs) && + !_hasGlobalFlag(pnpmArgs) + ) { + pnpmArgs.splice(1, 0, '--recursive'); + } +} /** * Options for RushPnpmCommandLineParser @@ -60,6 +87,7 @@ export class RushPnpmCommandLineParser { private readonly _pnpmArgs: string[]; private _commandName: string | undefined; private readonly _debugEnabled: boolean; + private _subspace: Subspace; private constructor( options: IRushPnpmCommandLineParserOptions, @@ -71,10 +99,17 @@ export class RushPnpmCommandLineParser { this._terminal = terminal; // Are we in a Rush repo? - const rushConfiguration: RushConfiguration | undefined = RushConfiguration.tryLoadFromDefaultLocation({ + const rushJsonFilePath: string | undefined = RushConfiguration.tryFindRushJsonLocation({ // showVerbose is false because the logging message may break JSON output showVerbose: false }); + + initializeDotEnv(terminal, rushJsonFilePath); + + const rushConfiguration: RushConfiguration | undefined = rushJsonFilePath + ? RushConfiguration.loadFromConfigurationFile(rushJsonFilePath) + : undefined; + NodeJsCompatibility.warnAboutCompatibilityIssues({ isRushLib: true, alreadyReportedNodeTooNewError: !!options.alreadyReportedNodeTooNewError, @@ -90,38 +125,61 @@ export class RushPnpmCommandLineParser { if (rushConfiguration.packageManager !== 'pnpm') { throw new Error( - 'The "rush-pnpm" command requires your rush.json to be configured to use the PNPM package manager' + `The "rush-pnpm" command requires your ${RushConstants.rushJsonFilename} to be configured to use the PNPM package manager` ); } if (!rushConfiguration.pnpmOptions.useWorkspaces) { - const pnpmConfigFilename: string = rushConfiguration.pnpmOptions.jsonFilename || 'rush.json'; + const pnpmConfigFilename: string = + rushConfiguration.pnpmOptions.jsonFilename || RushConstants.rushJsonFilename; throw new Error( `The "rush-pnpm" command requires the "useWorkspaces" setting to be enabled in ${pnpmConfigFilename}` ); } - const workspaceFolder: string = rushConfiguration.commonTempFolder; - const workspaceFilePath: string = path.join(workspaceFolder, 'pnpm-workspace.yaml'); + let pnpmArgs: string[] = []; + let subspaceName: string = 'default'; + + if (process.argv.indexOf('--subspace') >= 0) { + if (process.argv[2] !== '--subspace') { + throw new Error( + 'If you want to specify a subspace, you should place "--subspace " immediately after the "rush-pnpm" command' + ); + } + + subspaceName = process.argv[3]; + + // 0 = node.exe + // 1 = rush-pnpm + // 2 = --subspace + // 3 = + pnpmArgs = process.argv.slice(4); + } else { + // 0 = node.exe + // 1 = rush-pnpm + pnpmArgs = process.argv.slice(2); + } + + this._pnpmArgs = pnpmArgs; + + const subspace: Subspace = rushConfiguration.getSubspace(subspaceName); + this._subspace = subspace; + + const workspaceFolder: string = subspace.getSubspaceTempFolderPath(); + const workspaceFilePath: string = `${workspaceFolder}/${RushConstants.pnpmWorkspaceFileName}`; if (!FileSystem.exists(workspaceFilePath)) { this._terminal.writeErrorLine('Error: The PNPM workspace file has not been generated:'); this._terminal.writeErrorLine(` ${workspaceFilePath}\n`); - this._terminal.writeLine(Colors.cyan(`Do you need to run "rush install" or "rush update"?`)); + this._terminal.writeLine(Colorize.cyan(`Do you need to run "rush install" or "rush update"?`)); throw new AlreadyReportedError(); } if (!FileSystem.exists(rushConfiguration.packageManagerToolFilename)) { this._terminal.writeErrorLine('Error: The PNPM local binary has not been installed yet.'); - this._terminal.writeLine('\n' + Colors.cyan(`Do you need to run "rush install" or "rush update"?`)); + this._terminal.writeLine('\n' + Colorize.cyan(`Do you need to run "rush install" or "rush update"?`)); throw new AlreadyReportedError(); } - - // 0 = node.exe - // 1 = rush-pnpm - const pnpmArgs: string[] = process.argv.slice(2); - - this._pnpmArgs = pnpmArgs; } public static async initializeAsync( @@ -155,7 +213,7 @@ export class RushPnpmCommandLineParser { // promise exception), so we start with the assumption that the exit code is 1 // and set it to 0 only on success. process.exitCode = 1; - this._execute(); + await this._executeAsync(); if (process.exitCode === 0) { await this._postExecuteAsync(); @@ -192,7 +250,7 @@ export class RushPnpmCommandLineParser { this._terminal.writeErrorLine( `Warning: The "rush-pnpm" wrapper expects a command verb before "${firstArg}"\n` ); - this._terminal.writeLine(Colors.cyan(BYPASS_NOTICE)); + this._terminal.writeLine(Colorize.cyan(BYPASS_NOTICE)); throw new AlreadyReportedError(); } else { const commandName: string = firstArg; @@ -215,6 +273,7 @@ export class RushPnpmCommandLineParser { } this._commandName = commandName; + _addDefaultRecursiveFlagIfNeeded(commandName, pnpmArgs); // Warn about commands known not to work /* eslint-disable no-fallthrough */ @@ -226,7 +285,7 @@ export class RushPnpmCommandLineParser { `Error: The "pnpm ${commandName}" command is known to be incompatible with Rush's environment.` ) + '\n' ); - this._terminal.writeLine(Colors.cyan(BYPASS_NOTICE)); + this._terminal.writeLine(Colorize.cyan(BYPASS_NOTICE)); throw new AlreadyReportedError(); } @@ -244,7 +303,7 @@ export class RushPnpmCommandLineParser { ` Use the "rush install" or "rush update" commands instead.` ) + '\n' ); - this._terminal.writeLine(Colors.cyan(BYPASS_NOTICE)); + this._terminal.writeLine(Colorize.cyan(BYPASS_NOTICE)); throw new AlreadyReportedError(); } @@ -282,7 +341,7 @@ export class RushPnpmCommandLineParser { this._terminal.writeErrorLine( PrintUtilities.wrapWords( `Error: The "pnpm patch" command is added after pnpm@7.4.0.` + - ` Please update "pnpmVersion" >= 7.4.0 in rush.json file and run "rush update" to use this command.` + ` Please update "pnpmVersion" >= 7.4.0 in ${RushConstants.rushJsonFilename} file and run "rush update" to use this command.` ) + '\n' ); throw new AlreadyReportedError(); @@ -297,8 +356,55 @@ export class RushPnpmCommandLineParser { if (this._rushConfiguration.rushConfigurationJson.pnpmOptions) { this._terminal.writeErrorLine( PrintUtilities.wrapWords( - `Error: The "pnpm patch-commit" command is incompatible with specifying "pnpmOptions" in rush.json file.` + - ` Please move the content of "pnpmOptions" in rush.json file to ${pnpmOptionsJsonFilename}` + `Error: The "pnpm patch-commit" command is incompatible with specifying "pnpmOptions" in ${RushConstants.rushJsonFilename} file.` + + ` Please move the content of "pnpmOptions" in ${RushConstants.rushJsonFilename} file to ${pnpmOptionsJsonFilename}` + ) + '\n' + ); + throw new AlreadyReportedError(); + } + break; + } + case 'patch-remove': { + const semver: typeof import('semver') = await import('semver'); + /** + * The "patch-remove" command was introduced in pnpm version 8.5.0 + */ + if (semver.lt(this._rushConfiguration.packageManagerToolVersion, '8.5.0')) { + this._terminal.writeErrorLine( + PrintUtilities.wrapWords( + `Error: The "pnpm patch-remove" command is added after pnpm@8.5.0.` + + ` Please update "pnpmVersion" >= 8.5.0 in ${RushConstants.rushJsonFilename} file and run "rush update" to use this command.` + ) + '\n' + ); + throw new AlreadyReportedError(); + } + break; + } + case 'approve-builds': { + const semver: typeof import('semver') = await import('semver'); + /** + * The "approve-builds" command was introduced in PNPM version 10.1.0 + * to approve packages for running build scripts when onlyBuiltDependencies is used. + * In PNPM 11.0.0, it was updated to use allowBuilds in pnpm-workspace.yaml. + */ + if (semver.lt(this._rushConfiguration.packageManagerToolVersion, '10.1.0')) { + this._terminal.writeErrorLine( + PrintUtilities.wrapWords( + `Error: The "pnpm approve-builds" command is added after pnpm@10.1.0.` + + ` Please update "pnpmVersion" >= 10.1.0 in ${RushConstants.rushJsonFilename} file and run "rush update" to use this command.` + ) + '\n' + ); + throw new AlreadyReportedError(); + } + const pnpmOptionsJsonFilename: string = path.join( + this._rushConfiguration.commonRushConfigFolder, + RushConstants.pnpmConfigFilename + ); + if (this._rushConfiguration.rushConfigurationJson.pnpmOptions) { + this._terminal.writeErrorLine( + PrintUtilities.wrapWords( + `Error: The "pnpm approve-builds" command is incompatible with specifying "pnpmOptions" in ${RushConstants.rushJsonFilename} file.` + + ` Please move the content of "pnpmOptions" in ${RushConstants.rushJsonFilename} file to ${pnpmOptionsJsonFilename}` ) + '\n' ); throw new AlreadyReportedError(); @@ -337,16 +443,16 @@ export class RushPnpmCommandLineParser { `Error: The "pnpm ${commandName}" command has not been tested with Rush's environment. It may be incompatible.` ) + '\n' ); - this._terminal.writeLine(Colors.cyan(BYPASS_NOTICE)); + this._terminal.writeLine(Colorize.cyan(BYPASS_NOTICE)); } } /* eslint-enable no-fallthrough */ } } - private _execute(): void { + private async _executeAsync(): Promise { const rushConfiguration: RushConfiguration = this._rushConfiguration; - const workspaceFolder: string = rushConfiguration.commonTempFolder; + const workspaceFolder: string = this._subspace.getSubspaceTempFolderPath(); const pnpmEnvironmentMap: EnvironmentMap = new EnvironmentMap(process.env); pnpmEnvironmentMap.set('NPM_CONFIG_WORKSPACE_DIR', workspaceFolder); @@ -370,21 +476,42 @@ export class RushPnpmCommandLineParser { } } - const result: SpawnSyncReturns = Executable.spawnSync( - rushConfiguration.packageManagerToolFilename, - this._pnpmArgs, - { - environmentMap: pnpmEnvironmentMap, - stdio: 'inherit' + let onStdoutStreamChunk: ((chunk: string) => string | void) | undefined; + switch (this._commandName) { + case 'patch': { + // Replace `pnpm patch-commit` with `rush-pnpm patch-commit` when running + // `pnpm patch` to avoid the `pnpm patch` command being suggested in the output + onStdoutStreamChunk = (stdoutChunk: string) => { + return stdoutChunk.replace( + /pnpm patch-commit/g, + `rush-pnpm --subspace ${this._subspace.subspaceName} patch-commit` + ); + }; + + break; } - ); - if (result.error) { - throw new Error('Failed to invoke PNPM: ' + result.error); } - if (result.status === null) { - throw new Error('Failed to invoke PNPM: Spawn completed without an exit code'); + + try { + const { exitCode } = await Utilities.executeCommandAsync({ + command: rushConfiguration.packageManagerToolFilename, + args: this._pnpmArgs, + workingDirectory: process.cwd(), + environment: pnpmEnvironmentMap.toObject(), + keepEnvironment: true, + onStdoutStreamChunk, + captureExitCodeAndSignal: true + }); + + if (typeof exitCode === 'number') { + process.exitCode = exitCode; + } else { + // If the exit code is not a number, the process was terminated by a signal + process.exitCode = 1; + } + } catch (e) { + this._terminal.writeDebugLine(`Error: ${e}`); } - process.exitCode = result.status; } private async _postExecuteAsync(): Promise { @@ -393,24 +520,56 @@ export class RushPnpmCommandLineParser { return; } + const subspaceTempFolder: string = this._subspace.getSubspaceTempFolderPath(); + switch (commandName) { + case 'patch-remove': case 'patch-commit': { - // Example: "C:\MyRepo\common\temp\package.json" - const commonPackageJsonFilename: string = `${this._rushConfiguration.commonTempFolder}/${FileConstants.PackageJson}`; - const commonPackageJson: JsonObject = JsonFile.load(commonPackageJsonFilename); - const newGlobalPatchedDependencies: Record | undefined = - commonPackageJson?.pnpm?.patchedDependencies; + // why need to throw error when pnpm-config.json not exists? + // 1. pnpm-config.json is required for `rush-pnpm patch-commit`. Rush writes the patched dependency to the pnpm-config.json when finishes. + // 2. we can not fallback to use Monorepo config folder (common/config/rush) due to that this command is intended to apply to input subspace only. + // It will produce unexpected behavior if we use the fallback. + if (this._subspace.getPnpmOptions() === undefined) { + const subspaceConfigFolder: string = this._subspace.getSubspaceConfigFolderPath(); + this._terminal.writeErrorLine( + `The "rush-pnpm patch-commit" command cannot proceed without a pnpm-config.json file.` + + ` Create one in this folder: ${subspaceConfigFolder}` + ); + break; + } + + const pnpmOptions: PnpmOptionsConfiguration | undefined = this._subspace.getPnpmOptions(); + const pnpmVersion: string = this._rushConfiguration.packageManagerToolVersion; + const semver: typeof import('semver') = await import('semver'); + + let newGlobalPatchedDependencies: Record | undefined; + if (semver.gte(pnpmVersion, '11.0.0')) { + // PNPM 11+ stores patchedDependencies in pnpm-workspace.yaml instead of the package.json "pnpm" field + const workspaceFile: PnpmWorkspaceFile | undefined = await PnpmWorkspaceFile.tryLoadAsync( + `${subspaceTempFolder}/${RushConstants.pnpmWorkspaceFileName}` + ); + newGlobalPatchedDependencies = workspaceFile?.patchedDependencies; + } else { + // PNPM 10.x and earlier store patchedDependencies in the package.json "pnpm" field + // Example: "C:\MyRepo\common\temp\package.json" + const commonPackageJsonFilename: string = `${subspaceTempFolder}/${FileConstants.PackageJson}`; + const commonPackageJson: JsonObject = JsonFile.load(commonPackageJsonFilename); + newGlobalPatchedDependencies = commonPackageJson?.pnpm?.patchedDependencies; + } + const currentGlobalPatchedDependencies: Record | undefined = - this._rushConfiguration.pnpmOptions.globalPatchedDependencies; + pnpmOptions?.globalPatchedDependencies; + + if (!Objects.areDeepEqual(currentGlobalPatchedDependencies, newGlobalPatchedDependencies)) { + const commonTempPnpmPatchesFolder: string = `${subspaceTempFolder}/${RushConstants.pnpmPatchesFolderName}`; + const rushPnpmPatchesFolder: string = this._subspace.getSubspacePnpmPatchesFolderPath(); - const { isEqual } = await import('lodash'); - if (!isEqual(currentGlobalPatchedDependencies, newGlobalPatchedDependencies)) { - const commonTempPnpmPatchesFolder: string = `${this._rushConfiguration.commonTempFolder}/${RushConstants.pnpmPatchesFolderName}`; - const rushPnpmPatchesFolder: string = `${this._rushConfiguration.commonFolder}/pnpm-${RushConstants.pnpmPatchesFolderName}`; - // Copy (or delete) common\temp\patches\ --> common\pnpm-patches\ + // Copy (or delete) common\temp\subspace\patches\ --> common\config\pnpm-patches\ OR common\config\rush\pnpm-patches\ if (FileSystem.exists(commonTempPnpmPatchesFolder)) { FileSystem.ensureEmptyFolder(rushPnpmPatchesFolder); + // eslint-disable-next-line no-console console.log(`Copying ${commonTempPnpmPatchesFolder}`); + // eslint-disable-next-line no-console console.log(` --> ${rushPnpmPatchesFolder}`); FileSystem.copyFiles({ sourcePath: commonTempPnpmPatchesFolder, @@ -418,19 +577,113 @@ export class RushPnpmCommandLineParser { }); } else { if (FileSystem.exists(rushPnpmPatchesFolder)) { + // eslint-disable-next-line no-console console.log(`Deleting ${rushPnpmPatchesFolder}`); FileSystem.deleteFolder(rushPnpmPatchesFolder); } } // Update patchedDependencies to pnpm configuration file - this._rushConfiguration.pnpmOptions.updateGlobalPatchedDependencies(newGlobalPatchedDependencies); + pnpmOptions?.updateGlobalPatchedDependencies(newGlobalPatchedDependencies); // Rerun installation to update await this._doRushUpdateAsync(); this._terminal.writeWarningLine( - `Rush refreshed the ${RushConstants.pnpmConfigFilename}, shrinkwrap file and patch files under the "common/pnpm/patches" folder.\n` + + `Rush refreshed the ${RushConstants.pnpmConfigFilename}, shrinkwrap file and patch files under the ` + + `"${commonTempPnpmPatchesFolder}" folder.\n` + + ' Please commit this change to Git.' + ); + } + break; + } + case 'approve-builds': { + if (this._subspace.getPnpmOptions() === undefined) { + const subspaceConfigFolder: string = this._subspace.getSubspaceConfigFolderPath(); + this._terminal.writeErrorLine( + `The "rush-pnpm approve-builds" command cannot proceed without a pnpm-config.json file.` + + ` Create one in this folder: ${subspaceConfigFolder}` + ); + break; + } + + const pnpmOptions: PnpmOptionsConfiguration | undefined = this._subspace.getPnpmOptions(); + const pnpmVersion: string = this._rushConfiguration.packageManagerToolVersion; + const semver: typeof import('semver') = await import('semver'); + + if (semver.gte(pnpmVersion, '11.0.0')) { + // PNPM 11+ uses allowBuilds in pnpm-workspace.yaml instead of onlyBuiltDependencies in package.json + const workspaceFile: PnpmWorkspaceFile | undefined = await PnpmWorkspaceFile.tryLoadAsync( + `${subspaceTempFolder}/${RushConstants.pnpmWorkspaceFileName}` + ); + const newGlobalAllowBuilds: Record | undefined = workspaceFile?.allowBuilds; + const currentGlobalAllowBuilds: Record | undefined = + pnpmOptions?.globalAllowBuilds; + + if (!Objects.areDeepEqual(currentGlobalAllowBuilds, newGlobalAllowBuilds)) { + // Update allowBuilds to pnpm configuration file + pnpmOptions?.updateGlobalAllowBuilds(newGlobalAllowBuilds); + + // Rerun installation to update + await this._doRushUpdateAsync(); + + this._terminal.writeWarningLine( + `Rush refreshed the ${RushConstants.pnpmConfigFilename} and shrinkwrap file.\n` + + ' Please commit this change to Git.' + ); + } + } else { + // PNPM 10.x uses onlyBuiltDependencies in package.json + // Example: "C:\MyRepo\common\temp\package.json" + const commonPackageJsonFilename: string = `${subspaceTempFolder}/${FileConstants.PackageJson}`; + const commonPackageJson: JsonObject = await JsonFile.loadAsync(commonPackageJsonFilename); + const newGlobalOnlyBuiltDependencies: string[] | undefined = + commonPackageJson?.pnpm?.onlyBuiltDependencies; + const currentGlobalOnlyBuiltDependencies: string[] | undefined = + pnpmOptions?.globalOnlyBuiltDependencies; + + if (!Objects.areDeepEqual(currentGlobalOnlyBuiltDependencies, newGlobalOnlyBuiltDependencies)) { + // Update onlyBuiltDependencies to pnpm configuration file + await pnpmOptions?.updateGlobalOnlyBuiltDependenciesAsync(newGlobalOnlyBuiltDependencies); + + // Rerun installation to update + await this._doRushUpdateAsync(); + + this._terminal.writeWarningLine( + `Rush refreshed the ${RushConstants.pnpmConfigFilename} and shrinkwrap file.\n` + + ' Please commit this change to Git.' + ); + } + } + break; + } + case 'update': + case 'up': { + // When "pnpm up" / "pnpm update" runs, PNPM writes any updated catalog versions to the + // generated "catalogs" section of common/temp//pnpm-workspace.yaml. That file is + // regenerated on every install, so the updated versions must be synced back to the + // "globalCatalogs" field of pnpm-config.json for the change to be persisted. + const pnpmOptions: PnpmOptionsConfiguration | undefined = this._subspace.getPnpmOptions(); + if (pnpmOptions === undefined) { + break; + } + + const workspaceYamlFilename: string = `${subspaceTempFolder}/pnpm-workspace.yaml`; + const yamlModule: typeof import('js-yaml') = await import('js-yaml'); + const workspaceYamlContent: string = await FileSystem.readFileAsync(workspaceYamlFilename); + const workspaceYaml: { catalogs?: Record> } = (yamlModule.load( + workspaceYamlContent + ) ?? {}) as { catalogs?: Record> }; + const newGlobalCatalogs: Record> | undefined = workspaceYaml?.catalogs; + const currentGlobalCatalogs: Record> | undefined = + pnpmOptions.globalCatalogs; + + if (!Objects.areDeepEqual(currentGlobalCatalogs, newGlobalCatalogs)) { + await pnpmOptions.updateGlobalCatalogsAsync(newGlobalCatalogs); + await this._doRushUpdateAsync(); + + this._terminal.writeWarningLine( + `Rush refreshed the ${RushConstants.pnpmConfigFilename} and shrinkwrap file.\n` + ' Please commit this change to Git.' ); } @@ -441,7 +694,7 @@ export class RushPnpmCommandLineParser { private async _doRushUpdateAsync(): Promise { this._terminal.writeLine(); - this._terminal.writeLine(Colors.green('Running "rush update"')); + this._terminal.writeLine(Colorize.green('Running "rush update"')); this._terminal.writeLine(); const rushGlobalFolder: RushGlobalFolder = new RushGlobalFolder(); @@ -454,11 +707,15 @@ export class RushPnpmCommandLineParser { fullUpgrade: false, recheckShrinkwrap: true, networkConcurrency: undefined, + offline: false, collectLogFile: false, - variant: undefined, + variant: process.env[EnvironmentVariableNames.RUSH_VARIANT], // For `rush-pnpm`, only use the env var maxInstallAttempts: RushConstants.defaultMaxInstallAttempts, - pnpmFilterArguments: [], - checkOnly: false + pnpmFilterArgumentValues: [], + selectedProjects: new Set(this._rushConfiguration.projects), + checkOnly: false, + subspace: this._subspace, + terminal: this._terminal }; const installManagerFactoryModule: typeof import('../logic/InstallManagerFactory') = await import( @@ -475,7 +732,7 @@ export class RushPnpmCommandLineParser { try { await installManager.doInstallAsync(); } finally { - purgeManager.deleteAll(); + await purgeManager.startDeleteAllAsync(); } } } diff --git a/libraries/rush-lib/src/cli/RushStartupBanner.ts b/libraries/rush-lib/src/cli/RushStartupBanner.ts index 06a631311f7..989d96d3d5a 100644 --- a/libraries/rush-lib/src/cli/RushStartupBanner.ts +++ b/libraries/rush-lib/src/cli/RushStartupBanner.ts @@ -1,42 +1,44 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; +import { Colorize } from '@rushstack/terminal'; import { RushConstants } from '../logic/RushConstants'; import { NodeJsCompatibility } from '../logic/NodeJsCompatibility'; export class RushStartupBanner { public static logBanner(rushVersion: string, isManaged: boolean): void { - const nodeVersion: string = this._formatNodeVersion(); - const versionSuffix: string = rushVersion ? ' ' + this._formatRushVersion(rushVersion, isManaged) : ''; + const nodeVersion: string = _formatNodeVersion(); + const versionSuffix: string = rushVersion ? ' ' + _formatRushVersion(rushVersion, isManaged) : ''; + // eslint-disable-next-line no-console console.log( '\n' + - colors.bold(`Rush Multi-Project Build Tool${versionSuffix}`) + - colors.cyan(` - ${RushConstants.rushWebSiteUrl}`) + + Colorize.bold(`Rush Multi-Project Build Tool${versionSuffix}`) + + Colorize.cyan(` - ${RushConstants.rushWebSiteUrl}`) + `\nNode.js version is ${nodeVersion}\n` ); } public static logStreamlinedBanner(rushVersion: string, isManaged: boolean): void { - const nodeVersion: string = this._formatNodeVersion(); - const versionSuffix: string = rushVersion ? ' ' + this._formatRushVersion(rushVersion, isManaged) : ''; + const nodeVersion: string = _formatNodeVersion(); + const versionSuffix: string = rushVersion ? ' ' + _formatRushVersion(rushVersion, isManaged) : ''; - console.log(colors.bold(`Rush Multi-Project Build Tool${versionSuffix}`) + ` - Node.js ${nodeVersion}`); + // eslint-disable-next-line no-console + console.log(Colorize.bold(`Rush Multi-Project Build Tool${versionSuffix}`) + ` - Node.js ${nodeVersion}`); } +} - private static _formatNodeVersion(): string { - const nodeVersion: string = process.versions.node; - const nodeReleaseLabel: string = NodeJsCompatibility.isOddNumberedVersion - ? 'unstable' - : NodeJsCompatibility.isLtsVersion +function _formatNodeVersion(): string { + const nodeVersion: string = process.versions.node; + const nodeReleaseLabel: string = NodeJsCompatibility.isOddNumberedVersion + ? 'unstable' + : NodeJsCompatibility.isLtsVersion ? 'LTS' : 'pre-LTS'; - return `${nodeVersion} (${nodeReleaseLabel})`; - } + return `${nodeVersion} (${nodeReleaseLabel})`; +} - private static _formatRushVersion(rushVersion: string, isManaged: boolean): string { - return rushVersion + colors.yellow(isManaged ? '' : ' (unmanaged)'); - } +function _formatRushVersion(rushVersion: string, isManaged: boolean): string { + return rushVersion + Colorize.yellow(isManaged ? '' : ' (unmanaged)'); } diff --git a/libraries/rush-lib/src/cli/RushXCommandLine.ts b/libraries/rush-lib/src/cli/RushXCommandLine.ts index 6c023f9c7cc..20f550d65b8 100644 --- a/libraries/rush-lib/src/cli/RushXCommandLine.ts +++ b/libraries/rush-lib/src/cli/RushXCommandLine.ts @@ -1,26 +1,34 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; -import { PackageJsonLookup, IPackageJson, Text } from '@rushstack/node-core-library'; -import { DEFAULT_CONSOLE_WIDTH, PrintUtilities } from '@rushstack/terminal'; +import * as path from 'node:path'; + +import { type ILogMessageCallbackOptions, pnpmSyncCopyAsync } from 'pnpm-sync-lib'; + +import { PackageJsonLookup, type IPackageJson, Text, FileSystem, Async } from '@rushstack/node-core-library'; +import { + Colorize, + ConsoleTerminalProvider, + DEFAULT_CONSOLE_WIDTH, + type ITerminalProvider, + PrintUtilities, + Terminal, + type ITerminal +} from '@rushstack/terminal'; import { Utilities } from '../utilities/Utilities'; import { ProjectCommandSet } from '../logic/ProjectCommandSet'; -import { Rush } from '../api/Rush'; +import { type ILaunchOptions, Rush } from '../api/Rush'; import { RushConfiguration } from '../api/RushConfiguration'; import { NodeJsCompatibility } from '../logic/NodeJsCompatibility'; import { RushStartupBanner } from './RushStartupBanner'; - -/** - * @internal - */ -export interface ILaunchRushXInternalOptions { - isManaged: boolean; - - alreadyReportedNodeTooNewError?: boolean; -} +import { EventHooksManager } from '../logic/EventHooksManager'; +import { Event } from '../api/EventHooks'; +import { EnvironmentVariableNames } from '../api/EnvironmentConfiguration'; +import { RushConstants } from '../logic/RushConstants'; +import { PnpmSyncUtilities } from '../utilities/PnpmSyncUtilities'; +import { initializeDotEnv } from '../logic/dotenv'; +import { escapeArgumentIfNeeded } from '../utilities/executionUtilities'; interface IRushXCommandLineArguments { /** @@ -33,6 +41,16 @@ interface IRushXCommandLineArguments { */ help: boolean; + /** + * Flag indicating whether the user has requested debug mode. + */ + isDebug: boolean; + + /** + * Flag indicating whether the user wants to not call hooks. + */ + ignoreHooks: boolean; + /** * The command to run (i.e., the target "script" in package.json.) */ @@ -44,230 +62,327 @@ interface IRushXCommandLineArguments { commandArgs: string[]; } -export class RushXCommandLine { - public static launchRushX(launcherVersion: string, isManaged: boolean): void { - RushXCommandLine._launchRushXInternal(launcherVersion, { isManaged }); - } - - /** - * @internal - */ - public static _launchRushXInternal(launcherVersion: string, options: ILaunchRushXInternalOptions): void { - // Node.js can sometimes accidentally terminate with a zero exit code (e.g. for an uncaught - // promise exception), so we start with the assumption that the exit code is 1 - // and set it to 0 only on success. - process.exitCode = 1; +class ProcessError extends Error { + public readonly exitCode: number; + public constructor(message: string, exitCode: number) { + super(message); - const args: IRushXCommandLineArguments = this._getCommandLineArguments(); + // Manually set the prototype, as we can no longer extend built-in classes like Error, Array, Map, etc. + // https://github.com/microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + // + // Note: the prototype must also be set on any classes which extend this one + (this as any).__proto__ = ProcessError.prototype; // eslint-disable-line @typescript-eslint/no-explicit-any - if (!args.quiet) { - RushStartupBanner.logStreamlinedBanner(Rush.version, options.isManaged); - } + this.exitCode = exitCode; + } +} +export class RushXCommandLine { + public static async launchRushXAsync(launcherVersion: string, options: ILaunchOptions): Promise { try { - // Are we in a Rush repo? - const rushConfiguration: RushConfiguration | undefined = RushConfiguration.tryLoadFromDefaultLocation({ + const rushxArguments: IRushXCommandLineArguments = _parseCommandLineArguments(); + const rushJsonFilePath: string | undefined = RushConfiguration.tryFindRushJsonLocation({ showVerbose: false }); - NodeJsCompatibility.warnAboutCompatibilityIssues({ - isRushLib: true, - alreadyReportedNodeTooNewError: !!options.alreadyReportedNodeTooNewError, - rushConfiguration - }); - - // Find the governing package.json for this folder: - const packageJsonLookup: PackageJsonLookup = new PackageJsonLookup(); + const { isDebug, help, ignoreHooks } = rushxArguments; - const packageJsonFilePath: string | undefined = packageJsonLookup.tryGetPackageJsonFilePathFor( - process.cwd() - ); - if (!packageJsonFilePath) { - console.log(colors.red('This command should be used inside a project folder.')); - console.log( - `Unable to find a package.json file in the current working directory or any of its parents.` - ); - return; + const terminalProvider: ITerminalProvider = new ConsoleTerminalProvider({ + debugEnabled: isDebug, + verboseEnabled: isDebug + }); + const terminal: ITerminal = new Terminal(terminalProvider); + + initializeDotEnv(terminal, rushJsonFilePath); + + const rushConfiguration: RushConfiguration | undefined = rushJsonFilePath + ? RushConfiguration.loadFromConfigurationFile(rushJsonFilePath) + : undefined; + const eventHooksManager: EventHooksManager | undefined = rushConfiguration + ? new EventHooksManager(rushConfiguration) + : undefined; + + const suppressHooks: boolean = process.env[EnvironmentVariableNames._RUSH_RECURSIVE_RUSHX_CALL] === '1'; + const attemptHooks: boolean = !suppressHooks && !help; + if (attemptHooks) { + try { + eventHooksManager?.handle(Event.preRushx, isDebug, ignoreHooks); + } catch (error) { + // eslint-disable-next-line no-console + console.error(Colorize.red('PreRushx hook error: ' + (error as Error).message)); + } + } + // Node.js can sometimes accidentally terminate with a zero exit code (e.g. for an uncaught + // promise exception), so we start with the assumption that the exit code is 1 + // and set it to 0 only on success. + process.exitCode = 1; + await _launchRushXInternalAsync(terminal, rushxArguments, rushConfiguration, options); + if (attemptHooks) { + try { + eventHooksManager?.handle(Event.postRushx, isDebug, ignoreHooks); + } catch (error) { + // eslint-disable-next-line no-console + console.error(Colorize.red('PostRushx hook error: ' + (error as Error).message)); + } } - if (rushConfiguration && !rushConfiguration.tryGetProjectForPath(process.cwd())) { - // GitHub #2713: Users reported confusion resulting from a situation where "rush install" - // did not install the project's dependencies, because the project was not registered. - console.log( - colors.yellow( - 'Warning: You are invoking "rushx" inside a Rush repository, but this project is not registered in rush.json.' - ) - ); + // Getting here means that we are all done with no major errors + process.exitCode = 0; + } catch (error) { + if (error instanceof ProcessError) { + process.exitCode = error.exitCode; + } else { + process.exitCode = 1; } + // eslint-disable-next-line no-console + console.error(Colorize.red('Error: ' + (error as Error).message)); + } + } +} - const packageJson: IPackageJson = packageJsonLookup.loadPackageJson(packageJsonFilePath); +async function _launchRushXInternalAsync( + terminal: ITerminal, + rushxArguments: IRushXCommandLineArguments, + rushConfiguration: RushConfiguration | undefined, + options: ILaunchOptions +): Promise { + const { quiet, help, commandName, commandArgs } = rushxArguments; - const projectCommandSet: ProjectCommandSet = new ProjectCommandSet(packageJson); + if (!quiet) { + RushStartupBanner.logStreamlinedBanner(Rush.version, options.isManaged); + } + // Are we in a Rush repo? + NodeJsCompatibility.warnAboutCompatibilityIssues({ + isRushLib: true, + alreadyReportedNodeTooNewError: options.alreadyReportedNodeTooNewError || false, + rushConfiguration + }); + + // Find the governing package.json for this folder: + const packageJsonLookup: PackageJsonLookup = new PackageJsonLookup(); + + const packageJsonFilePath: string | undefined = packageJsonLookup.tryGetPackageJsonFilePathFor( + process.cwd() + ); + if (!packageJsonFilePath) { + throw Error( + 'This command should be used inside a project folder. ' + + 'Unable to find a package.json file in the current working directory or any of its parents.' + ); + } - if (args.help) { - RushXCommandLine._showUsage(packageJson, projectCommandSet); - return; - } + if (rushConfiguration && !rushConfiguration.tryGetProjectForPath(process.cwd())) { + // GitHub #2713: Users reported confusion resulting from a situation where "rush install" + // did not install the project's dependencies, because the project was not registered. + // eslint-disable-next-line no-console + console.log( + Colorize.yellow( + 'Warning: You are invoking "rushx" inside a Rush repository, but this project is not registered in ' + + `${RushConstants.rushJsonFilename}.` + ) + ); + } - const scriptBody: string | undefined = projectCommandSet.tryGetScriptBody(args.commandName); + const packageJson: IPackageJson = packageJsonLookup.loadPackageJson(packageJsonFilePath); - if (scriptBody === undefined) { - console.log( - colors.red( - `Error: The command "${args.commandName}" is not defined in the` + - ` package.json file for this project.` - ) - ); + const projectCommandSet: ProjectCommandSet = new ProjectCommandSet(packageJson); - if (projectCommandSet.commandNames.length > 0) { - console.log( - '\nAvailable commands for this project are: ' + - projectCommandSet.commandNames.map((x) => `"${x}"`).join(', ') - ); - } + if (help) { + _showUsage(packageJson, projectCommandSet); + return; + } - console.log(`Use ${colors.yellow('"rushx --help"')} for more information.`); - return; - } + const scriptBody: string | undefined = projectCommandSet.tryGetScriptBody(commandName); - let commandWithArgs: string = scriptBody; - let commandWithArgsForDisplay: string = scriptBody; - if (args.commandArgs.length > 0) { - // This approach is based on what NPM 7 now does: - // https://github.com/npm/run-script/blob/47a4d539fb07220e7215cc0e482683b76407ef9b/lib/run-script-pkg.js#L34 - const escapedRemainingArgs: string[] = args.commandArgs.map((x) => Utilities.escapeShellParameter(x)); + if (scriptBody === undefined) { + let errorMessage: string = `The command "${commandName}" is not defined in the package.json file for this project.`; - commandWithArgs += ' ' + escapedRemainingArgs.join(' '); + if (projectCommandSet.commandNames.length > 0) { + errorMessage += + '\nAvailable commands for this project are: ' + + projectCommandSet.commandNames.map((x) => `"${x}"`).join(', '); + } - // Display it nicely without the extra quotes - commandWithArgsForDisplay += ' ' + args.commandArgs.join(' '); - } + throw Error(errorMessage); + } - if (!args.quiet) { - console.log(`> ${JSON.stringify(commandWithArgsForDisplay)}\n`); - } + let commandWithArgs: string = scriptBody; + let commandWithArgsForDisplay: string = scriptBody; + if (commandArgs.length > 0) { + const escapedRemainingArgs: string[] = commandArgs.map((x) => escapeArgumentIfNeeded(x)); + commandWithArgs += ' ' + escapedRemainingArgs.join(' '); - const packageFolder: string = path.dirname(packageJsonFilePath); - - const exitCode: number = Utilities.executeLifecycleCommand(commandWithArgs, { - rushConfiguration, - workingDirectory: packageFolder, - // If there is a rush.json then use its .npmrc from the temp folder. - // Otherwise look for npmrc in the project folder. - initCwd: rushConfiguration ? rushConfiguration.commonTempFolder : packageFolder, - handleOutput: false, - environmentPathOptions: { - includeProjectBin: true - } - }); + // Display it nicely without the extra quotes + commandWithArgsForDisplay += ' ' + commandArgs.join(' '); + } - if (exitCode > 0) { - console.log(colors.red(`The script failed with exit code ${exitCode}`)); - } + if (!quiet) { + // eslint-disable-next-line no-console + console.log(`> ${JSON.stringify(commandWithArgsForDisplay)}\n`); + } - process.exitCode = exitCode; - } catch (error) { - console.log(colors.red('Error: ' + (error as Error).message)); + const packageFolder: string = path.dirname(packageJsonFilePath); + + const exitCode: number = Utilities.executeLifecycleCommand(commandWithArgs, { + rushConfiguration, + workingDirectory: packageFolder, + // If there is a rush.json then use its .npmrc from the temp folder. + // Otherwise look for npmrc in the project folder. + initCwd: rushConfiguration ? rushConfiguration.commonTempFolder : packageFolder, + handleOutput: false, + environmentPathOptions: { + includeProjectBin: true } - } + }); - private static _getCommandLineArguments(): IRushXCommandLineArguments { - // 0 = node.exe - // 1 = rushx - const args: string[] = process.argv.slice(2); - const unknownArgs: string[] = []; - - let help: boolean = false; - let quiet: boolean = false; - let commandName: string = ''; - const commandArgs: string[] = []; - - for (let index: number = 0; index < args.length; index++) { - const argValue: string = args[index]; - - if (!commandName) { - if (argValue === '-q' || argValue === '--quiet') { - quiet = true; - } else if (argValue === '-h' || argValue === '--help') { - help = true; - } else if (argValue.startsWith('-')) { - unknownArgs.push(args[index]); - } else { - commandName = args[index]; - } - } else { - commandArgs.push(args[index]); + if (rushConfiguration?.isPnpm && rushConfiguration?.experimentsConfiguration) { + const { configuration: experiments } = rushConfiguration?.experimentsConfiguration; + + if (experiments?.usePnpmSyncForInjectedDependencies) { + const pnpmSyncJsonPath: string = `${packageFolder}/${RushConstants.nodeModulesFolderName}/${RushConstants.pnpmSyncFilename}`; + if (await FileSystem.existsAsync(pnpmSyncJsonPath)) { + const { PackageExtractor } = await import( + /* webpackChunkName: 'PackageExtractor' */ + '@rushstack/package-extractor' + ); + await pnpmSyncCopyAsync({ + pnpmSyncJsonPath, + ensureFolderAsync: FileSystem.ensureFolderAsync, + forEachAsyncWithConcurrency: Async.forEachAsync, + getPackageIncludedFiles: PackageExtractor.getPackageIncludedFilesAsync, + logMessageCallback: (logMessageOptions: ILogMessageCallbackOptions) => + PnpmSyncUtilities.processLogMessage(logMessageOptions, terminal) + }); } } + } + + if (exitCode > 0) { + throw new ProcessError(`Failed calling ${commandWithArgs}. Exit code: ${exitCode}`, exitCode); + } +} + +function _parseCommandLineArguments(): IRushXCommandLineArguments { + // 0 = node.exe + // 1 = rushx + const args: string[] = process.argv.slice(2); + const unknownArgs: string[] = []; + + let help: boolean = false; + let quiet: boolean = false; + let commandName: string = ''; + let isDebug: boolean = false; + let ignoreHooks: boolean = false; + const commandArgs: string[] = []; + + for (let index: number = 0; index < args.length; index++) { + const argValue: string = args[index]; if (!commandName) { - help = true; + if (argValue === '-q' || argValue === '--quiet') { + quiet = true; + } else if (argValue === '-h' || argValue === '--help') { + help = true; + } else if (argValue === '-d' || argValue === '--debug') { + isDebug = true; + } else if (argValue === '--ignore-hooks') { + ignoreHooks = true; + } else if (argValue.startsWith('-')) { + unknownArgs.push(args[index]); + } else { + commandName = args[index]; + } + } else { + commandArgs.push(args[index]); } + } - if (unknownArgs.length > 0) { - // Future TODO: Instead of just displaying usage info, we could display a - // specific error about the unknown flag the user tried to pass to rushx. - help = true; - } + const quietModeValue: string | undefined = process.env[EnvironmentVariableNames.RUSH_QUIET_MODE]; + if (quietModeValue === '1' || quietModeValue === 'true') { + quiet = true; + } - return { - help, - quiet, - commandName, - commandArgs - }; + if (!commandName) { + help = true; } - private static _showUsage(packageJson: IPackageJson, projectCommandSet: ProjectCommandSet): void { - console.log('usage: rushx [-h]'); - console.log(' rushx [-q/--quiet] ...\n'); + if (unknownArgs.length > 0) { + // Future TODO: Instead of just displaying usage info, we could display a + // specific error about the unknown flag the user tried to pass to rushx. + // eslint-disable-next-line no-console + console.log(Colorize.red(`Unknown arguments: ${unknownArgs.map((x) => JSON.stringify(x)).join(', ')}`)); + help = true; + } - console.log('Optional arguments:'); - console.log(' -h, --help Show this help message and exit.'); - console.log(' -q, --quiet Hide rushx startup information.\n'); + return { + help, + quiet, + isDebug, + ignoreHooks, + commandName, + commandArgs + }; +} - if (projectCommandSet.commandNames.length > 0) { - console.log(`Project commands for ${colors.cyan(packageJson.name)}:`); +function _showUsage(packageJson: IPackageJson, projectCommandSet: ProjectCommandSet): void { + // eslint-disable-next-line no-console + console.log('usage: rushx [-h]'); + // eslint-disable-next-line no-console + console.log(' rushx [-q/--quiet] [-d/--debug] [--ignore-hooks] ...\n'); + + // eslint-disable-next-line no-console + console.log('Optional arguments:'); + // eslint-disable-next-line no-console + console.log(' -h, --help Show this help message and exit.'); + // eslint-disable-next-line no-console + console.log(' -q, --quiet Hide rushx startup information.'); + // eslint-disable-next-line no-console + console.log(' -d, --debug Run in debug mode.\n'); + + if (projectCommandSet.commandNames.length > 0) { + // eslint-disable-next-line no-console + console.log(`Project commands for ${Colorize.cyan(packageJson.name)}:`); + + // Calculate the length of the longest script name, for formatting + let maxLength: number = 0; + for (const commandName of projectCommandSet.commandNames) { + maxLength = Math.max(maxLength, commandName.length); + } - // Calculate the length of the longest script name, for formatting - let maxLength: number = 0; - for (const commandName of projectCommandSet.commandNames) { - maxLength = Math.max(maxLength, commandName.length); - } + for (const commandName of projectCommandSet.commandNames) { + const escapedScriptBody: string = JSON.stringify(projectCommandSet.getScriptBody(commandName)); - for (const commandName of projectCommandSet.commandNames) { - const escapedScriptBody: string = JSON.stringify(projectCommandSet.getScriptBody(commandName)); - - // The length of the string e.g. " command: " - const firstPartLength: number = 2 + maxLength + 2; - // The length for truncating the escaped escapedScriptBody so it doesn't wrap - // to the next line - const consoleWidth: number = PrintUtilities.getConsoleWidth() || DEFAULT_CONSOLE_WIDTH; - const truncateLength: number = Math.max(0, consoleWidth - firstPartLength) - 1; - - console.log( - // Example: " command: " - ' ' + - colors.cyan(Text.padEnd(commandName + ':', maxLength + 2)) + - // Example: "do some thin..." - Text.truncateWithEllipsis(escapedScriptBody, truncateLength) - ); - } + // The length of the string e.g. " command: " + const firstPartLength: number = 2 + maxLength + 2; + // The length for truncating the escaped escapedScriptBody so it doesn't wrap + // to the next line + const consoleWidth: number = PrintUtilities.getConsoleWidth() || DEFAULT_CONSOLE_WIDTH; + const truncateLength: number = Math.max(0, consoleWidth - firstPartLength) - 1; - if (projectCommandSet.malformedScriptNames.length > 0) { - console.log( - '\n' + - colors.yellow( - 'Warning: Some "scripts" entries in the package.json file' + - ' have malformed names: ' + - projectCommandSet.malformedScriptNames.map((x) => `"${x}"`).join(', ') - ) - ); - } - } else { - console.log(colors.yellow('Warning: No commands are defined yet for this project.')); + // eslint-disable-next-line no-console console.log( - 'You can define a command by adding a "scripts" table to the project\'s package.json file.' + // Example: " command: " + ' ' + + Colorize.cyan(Text.padEnd(commandName + ':', maxLength + 2)) + + // Example: "do some thin..." + Text.truncateWithEllipsis(escapedScriptBody, truncateLength) + ); + } + + if (projectCommandSet.malformedScriptNames.length > 0) { + // eslint-disable-next-line no-console + console.log( + '\n' + + Colorize.yellow( + 'Warning: Some "scripts" entries in the package.json file' + + ' have malformed names: ' + + projectCommandSet.malformedScriptNames.map((x) => `"${x}"`).join(', ') + ) ); } + } else { + // eslint-disable-next-line no-console + console.log(Colorize.yellow('Warning: No commands are defined yet for this project.')); + // eslint-disable-next-line no-console + console.log('You can define a command by adding a "scripts" table to the project\'s package.json file.'); } } diff --git a/libraries/rush-lib/src/cli/actions/AddAction.ts b/libraries/rush-lib/src/cli/actions/AddAction.ts index 81c84f081b0..db67f19ea0c 100644 --- a/libraries/rush-lib/src/cli/actions/AddAction.ts +++ b/libraries/rush-lib/src/cli/actions/AddAction.ts @@ -2,24 +2,30 @@ // See LICENSE in the project root for license information. import * as semver from 'semver'; -import type { CommandLineFlagParameter, CommandLineStringListParameter } from '@rushstack/ts-command-line'; -import { BaseAddAndRemoveAction } from './BaseAddAndRemoveAction'; -import { RushCommandLineParser } from '../RushCommandLineParser'; +import type { CommandLineFlagParameter } from '@rushstack/ts-command-line'; + +import { BaseAddAndRemoveAction, PACKAGE_PARAMETER_NAME } from './BaseAddAndRemoveAction'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; import { DependencySpecifier } from '../../logic/DependencySpecifier'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import { type IPackageForRushAdd, type IPackageJsonUpdaterRushAddOptions, SemVerStyle } from '../../logic/PackageJsonUpdaterTypes'; +import { getVariantAsync } from '../../api/Variants'; + +const ADD_ACTION_NAME: 'add' = 'add'; +export const MAKE_CONSISTENT_FLAG_NAME: '--make-consistent' = '--make-consistent'; +const EXACT_FLAG_NAME: '--exact' = '--exact'; +const CARET_FLAG_NAME: '--caret' = '--caret'; export class AddAction extends BaseAddAndRemoveAction { - protected readonly _allFlag: CommandLineFlagParameter; - protected readonly _packageNameList: CommandLineStringListParameter; private readonly _exactFlag: CommandLineFlagParameter; private readonly _caretFlag: CommandLineFlagParameter; private readonly _devDependencyFlag: CommandLineFlagParameter; + private readonly _peerDependencyFlag: CommandLineFlagParameter; private readonly _makeConsistentFlag: CommandLineFlagParameter; public constructor(parser: RushCommandLineParser) { @@ -28,37 +34,32 @@ export class AddAction extends BaseAddAndRemoveAction { ' and then runs "rush update". If no version is specified, a version will be automatically detected (typically' + ' either the latest version or a version that won\'t break the "ensureConsistentVersions" policy). If a version' + ' range (or a workspace range) is specified, the latest version in the range will be used. The version will be' + - ' automatically prepended with a tilde, unless the "--exact" or "--caret" flags are used. The "--make-consistent"' + - ' flag can be used to update all packages with the dependency.' + ` automatically prepended with a tilde, unless the "${EXACT_FLAG_NAME}" or "${CARET_FLAG_NAME}" flags are used.` + + ` The "${MAKE_CONSISTENT_FLAG_NAME}" flag can be used to update all packages with the dependency.` ].join('\n'); super({ - actionName: 'add', + actionName: ADD_ACTION_NAME, summary: 'Adds one or more dependencies to the package.json and runs rush update.', documentation, safeForSimultaneousRushProcesses: false, - parser - }); - - this._packageNameList = this.defineStringListParameter({ - parameterLongName: '--package', - parameterShortName: '-p', - required: true, - argumentName: 'PACKAGE', - description: + parser, + allFlagDescription: 'If specified, the dependency will be added to all projects.', + packageNameListParameterDescription: 'The name of the package which should be added as a dependency.' + ' A SemVer version specifier can be appended after an "@" sign. WARNING: Symbol characters' + " are usually interpreted by your shell, so it's recommended to use quotes." + - ' For example, write "rush add --package "example@^1.2.3"" instead of "rush add --package example@^1.2.3".' + - ' To add multiple packages, write "rush add --package foo --package bar".' + ` For example, write "rush add ${PACKAGE_PARAMETER_NAME} "example@^1.2.3"" instead of "rush add ${PACKAGE_PARAMETER_NAME} example@^1.2.3".` + + ` To add multiple packages, write "rush add ${PACKAGE_PARAMETER_NAME} foo ${PACKAGE_PARAMETER_NAME} bar".` }); + this._exactFlag = this.defineFlagParameter({ - parameterLongName: '--exact', + parameterLongName: EXACT_FLAG_NAME, description: 'If specified, the SemVer specifier added to the' + ' package.json will be an exact version (e.g. without tilde or caret).' }); this._caretFlag = this.defineFlagParameter({ - parameterLongName: '--caret', + parameterLongName: CARET_FLAG_NAME, description: 'If specified, the SemVer specifier added to the' + ' package.json will be a prepended with a "caret" specifier ("^").' @@ -68,20 +69,21 @@ export class AddAction extends BaseAddAndRemoveAction { description: 'If specified, the package will be added to the "devDependencies" section of the package.json' }); + this._peerDependencyFlag = this.defineFlagParameter({ + parameterLongName: '--peer', + description: + 'If specified, the package will be added to the "peerDependencies" section of the package.json' + }); this._makeConsistentFlag = this.defineFlagParameter({ - parameterLongName: '--make-consistent', + parameterLongName: MAKE_CONSISTENT_FLAG_NAME, parameterShortName: '-m', description: 'If specified, other packages with this dependency will have their package.json' + ' files updated to use the same version of the dependency.' }); - this._allFlag = this.defineFlagParameter({ - parameterLongName: '--all', - description: 'If specified, the dependency will be added to all projects.' - }); } - public getUpdateOptions(): IPackageJsonUpdaterRushAddOptions { + public async getUpdateOptionsAsync(): Promise { const projects: RushConfigurationProject[] = super.getProjects(); if (this._caretFlag.value && this._exactFlag.value) { @@ -128,7 +130,7 @@ export class AddAction extends BaseAddAndRemoveAction { if (this._exactFlag.value || this._caretFlag.value) { throw new Error( `The "${this._caretFlag.longName}" and "${this._exactFlag.longName}" flags may not be specified if a ` + - `version is provided in the ${this._packageNameList.longName} specifier. In this case "${version}" was provided.` + `version is provided in the ${this._packageNameListParameter.longName} specifier. In this case "${version}" was provided.` ); } @@ -137,20 +139,29 @@ export class AddAction extends BaseAddAndRemoveAction { rangeStyle = this._caretFlag.value ? SemVerStyle.Caret : this._exactFlag.value - ? SemVerStyle.Exact - : SemVerStyle.Tilde; + ? SemVerStyle.Exact + : SemVerStyle.Tilde; } packagesToAdd.push({ packageName, version, rangeStyle }); } + + const variant: string | undefined = await getVariantAsync( + this._variantParameter, + this.rushConfiguration, + true + ); + return { - projects: projects, + projects, packagesToUpdate: packagesToAdd, devDependency: this._devDependencyFlag.value, + peerDependency: this._peerDependencyFlag.value, updateOtherPackages: this._makeConsistentFlag.value, skipUpdate: this._skipUpdateFlag.value, debugInstall: this.parser.isDebug, - actionName: this.actionName + actionName: this.actionName, + variant }; } } diff --git a/libraries/rush-lib/src/cli/actions/AlertAction.ts b/libraries/rush-lib/src/cli/actions/AlertAction.ts new file mode 100644 index 00000000000..052220c06b0 --- /dev/null +++ b/libraries/rush-lib/src/cli/actions/AlertAction.ts @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { CommandLineFlagParameter, CommandLineStringParameter } from '@rushstack/ts-command-line'; + +import type { RushCommandLineParser } from '../RushCommandLineParser'; +import { BaseRushAction } from './BaseRushAction'; +import { RushAlerts } from '../../utilities/RushAlerts'; + +export class AlertAction extends BaseRushAction { + private readonly _snoozeParameter: CommandLineStringParameter; + private readonly _snoozeTimeFlagParameter: CommandLineFlagParameter; + + public constructor(parser: RushCommandLineParser) { + super({ + actionName: 'alert', + summary: '(EXPERIMENTAL) View and manage Rush alerts for the repository', + documentation: + 'This command displays the Rush alerts for this repository. Rush alerts are customizable announcements' + + ' and reminders that Rush prints occasionally on the command line.' + + ' The alert definitions can be found in the rush-alerts.json config file.', + parser + }); + + this._snoozeParameter = this.defineStringParameter({ + parameterLongName: '--snooze', + parameterShortName: '-s', + argumentName: 'ALERT_ID', + description: 'Temporarily suspend the specified alert for one week' + }); + + this._snoozeTimeFlagParameter = this.defineFlagParameter({ + parameterLongName: '--forever', + description: 'Combined with "--snooze", causes that alert to be suspended permanently' + }); + } + + public async runAsync(): Promise { + const rushAlerts: RushAlerts = await RushAlerts.loadFromConfigurationAsync( + this.rushConfiguration, + this.terminal + ); + const snoozeAlertId: string | undefined = this._snoozeParameter.value; + if (snoozeAlertId) { + const snoozeTimeFlag: boolean = this._snoozeTimeFlagParameter.value; + await rushAlerts.snoozeAlertsByAlertIdAsync(snoozeAlertId, snoozeTimeFlag); + } + await rushAlerts.printAllAlertsAsync(); + } +} diff --git a/libraries/rush-lib/src/cli/actions/BaseAddAndRemoveAction.ts b/libraries/rush-lib/src/cli/actions/BaseAddAndRemoveAction.ts index 8f368afab56..9e0c4364548 100644 --- a/libraries/rush-lib/src/cli/actions/BaseAddAndRemoveAction.ts +++ b/libraries/rush-lib/src/cli/actions/BaseAddAndRemoveAction.ts @@ -1,15 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { CommandLineFlagParameter, CommandLineStringListParameter } from '@rushstack/ts-command-line'; +import type { + CommandLineFlagParameter, + CommandLineStringListParameter, + CommandLineStringParameter +} from '@rushstack/ts-command-line'; import { BaseRushAction, type IBaseRushActionOptions } from './BaseRushAction'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import type * as PackageJsonUpdaterType from '../../logic/PackageJsonUpdater'; import type { IPackageForRushUpdate, IPackageJsonUpdaterRushBaseUpdateOptions } from '../../logic/PackageJsonUpdaterTypes'; +import { RushConstants } from '../../logic/RushConstants'; +import { VARIANT_PARAMETER } from '../../api/Variants'; + +export const PACKAGE_PARAMETER_NAME: '--package' = '--package'; export interface IBasePackageJsonUpdaterRushOptions { /** @@ -30,30 +38,53 @@ export interface IBasePackageJsonUpdaterRushOptions { debugInstall: boolean; } +export interface IBaseAddAndRemoveActionOptions extends IBaseRushActionOptions { + allFlagDescription: string; + packageNameListParameterDescription: string; +} + /** * This is the common base class for AddAction and RemoveAction. */ export abstract class BaseAddAndRemoveAction extends BaseRushAction { - protected abstract readonly _allFlag: CommandLineFlagParameter; - protected readonly _skipUpdateFlag!: CommandLineFlagParameter; - protected abstract readonly _packageNameList: CommandLineStringListParameter; + protected readonly _skipUpdateFlag: CommandLineFlagParameter; + protected readonly _packageNameListParameter: CommandLineStringListParameter; + protected readonly _allFlag: CommandLineFlagParameter; + protected readonly _variantParameter: CommandLineStringParameter; protected get specifiedPackageNameList(): readonly string[] { - return this._packageNameList.values!; + return this._packageNameListParameter.values; } - public constructor(options: IBaseRushActionOptions) { + public constructor(options: IBaseAddAndRemoveActionOptions) { super(options); + const { packageNameListParameterDescription, allFlagDescription } = options; + this._skipUpdateFlag = this.defineFlagParameter({ parameterLongName: '--skip-update', parameterShortName: '-s', description: 'If specified, the "rush update" command will not be run after updating the package.json files.' }); + + this._packageNameListParameter = this.defineStringListParameter({ + parameterLongName: PACKAGE_PARAMETER_NAME, + parameterShortName: '-p', + required: true, + argumentName: 'PACKAGE', + description: packageNameListParameterDescription + }); + + this._allFlag = this.defineFlagParameter({ + parameterLongName: '--all', + description: allFlagDescription + }); + + this._variantParameter = this.defineStringParameter(VARIANT_PARAMETER); } - protected abstract getUpdateOptions(): IPackageJsonUpdaterRushBaseUpdateOptions; + protected abstract getUpdateOptionsAsync(): Promise; protected getProjects(): RushConfigurationProject[] { if (this._allFlag.value) { @@ -65,7 +96,7 @@ export abstract class BaseAddAndRemoveAction extends BaseRushAction { if (!currentProject) { throw new Error( `The rush "${this.actionName}" command must be invoked under a project` + - ` folder that is registered in rush.json unless the ${this._allFlag.longName} is used.` + ` folder that is registered in ${RushConstants.rushJsonFilename} unless the ${this._allFlag.longName} is used.` ); } @@ -78,10 +109,12 @@ export abstract class BaseAddAndRemoveAction extends BaseRushAction { /* webpackChunkName: 'PackageJsonUpdater' */ '../../logic/PackageJsonUpdater' ); const updater: PackageJsonUpdaterType.PackageJsonUpdater = new packageJsonUpdater.PackageJsonUpdater( + this.terminal, this.rushConfiguration, this.rushGlobalFolder ); - await updater.doRushUpdateAsync(this.getUpdateOptions()); + const updateOptions: IPackageJsonUpdaterRushBaseUpdateOptions = await this.getUpdateOptionsAsync(); + await updater.doRushUpdateAsync(updateOptions); } } diff --git a/libraries/rush-lib/src/cli/actions/BaseAutoinstallerAction.ts b/libraries/rush-lib/src/cli/actions/BaseAutoinstallerAction.ts new file mode 100644 index 00000000000..52c556f5c3b --- /dev/null +++ b/libraries/rush-lib/src/cli/actions/BaseAutoinstallerAction.ts @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IRequiredCommandLineStringParameter } from '@rushstack/ts-command-line'; + +import { BaseRushAction, type IBaseRushActionOptions } from './BaseRushAction'; +import { Autoinstaller } from '../../logic/Autoinstaller'; + +export abstract class BaseAutoinstallerAction extends BaseRushAction { + protected readonly _name: IRequiredCommandLineStringParameter; + + public constructor(options: IBaseRushActionOptions) { + super(options); + + this._name = this.defineStringParameter({ + parameterLongName: '--name', + argumentName: 'AUTOINSTALLER_NAME', + required: true, + description: + 'The name of the autoinstaller, which must be one of the folders under common/autoinstallers.' + }); + } + + protected abstract prepareAsync(autoinstaller: Autoinstaller): Promise; + + protected async runAsync(): Promise { + const autoinstallerName: string = this._name.value; + const autoinstaller: Autoinstaller = new Autoinstaller({ + autoinstallerName, + rushConfiguration: this.rushConfiguration, + rushGlobalFolder: this.rushGlobalFolder + }); + + await this.prepareAsync(autoinstaller); + + this.terminal.writeLine(); + this.terminal.writeLine('Success.'); + } +} diff --git a/libraries/rush-lib/src/cli/actions/BaseHotlinkPackageAction.ts b/libraries/rush-lib/src/cli/actions/BaseHotlinkPackageAction.ts new file mode 100644 index 00000000000..0d357d8f729 --- /dev/null +++ b/libraries/rush-lib/src/cli/actions/BaseHotlinkPackageAction.ts @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; + +import type { IRequiredCommandLineStringParameter } from '@rushstack/ts-command-line'; + +import { HotlinkManager } from '../../utilities/HotlinkManager'; +import { BaseRushAction, type IBaseRushActionOptions } from './BaseRushAction'; + +export abstract class BaseHotlinkPackageAction extends BaseRushAction { + protected readonly _pathParameter: IRequiredCommandLineStringParameter; + + protected constructor(options: IBaseRushActionOptions) { + super(options); + + this._pathParameter = this.defineStringParameter({ + parameterLongName: '--path', + argumentName: 'PATH', + required: true, + description: + 'The path of folder of a project outside of this Rush repo, whose installation will be simulated using' + + ' node_modules symlinks ("hotlinks"). This folder is the symlink target.' + }); + } + + protected abstract hotlinkPackageAsync( + linkedPackagePath: string, + hotlinkManager: HotlinkManager + ): Promise; + + protected async runAsync(): Promise { + const hotlinkManager: HotlinkManager = HotlinkManager.loadFromRushConfiguration(this.rushConfiguration); + const linkedPackagePath: string = path.resolve(process.cwd(), this._pathParameter.value); + await this.hotlinkPackageAsync(linkedPackagePath, hotlinkManager); + } +} diff --git a/libraries/rush-lib/src/cli/actions/BaseInstallAction.ts b/libraries/rush-lib/src/cli/actions/BaseInstallAction.ts index 151e7b80197..b13b34fe681 100644 --- a/libraries/rush-lib/src/cli/actions/BaseInstallAction.ts +++ b/libraries/rush-lib/src/cli/actions/BaseInstallAction.ts @@ -1,15 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; - import type { CommandLineFlagParameter, CommandLineIntegerParameter, - CommandLineStringParameter + CommandLineStringParameter, + IRequiredCommandLineIntegerParameter } from '@rushstack/ts-command-line'; +import { AlreadyReportedError } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; -import { BaseRushAction, IBaseRushActionOptions } from './BaseRushAction'; +import { BaseRushAction, type IBaseRushActionOptions } from './BaseRushAction'; import { Event } from '../../api/EventHooks'; import type { BaseInstallManager } from '../../logic/base/BaseInstallManager'; import type { IInstallManagerOptions } from '../../logic/base/BaseInstallManagerTypes'; @@ -18,22 +19,34 @@ import { SetupChecks } from '../../logic/SetupChecks'; import { StandardScriptUpdater } from '../../logic/StandardScriptUpdater'; import { Stopwatch } from '../../utilities/Stopwatch'; import { VersionMismatchFinder } from '../../logic/versionMismatch/VersionMismatchFinder'; -import { Variants } from '../../api/Variants'; import { RushConstants } from '../../logic/RushConstants'; -import { SelectionParameterSet } from '../parsing/SelectionParameterSet'; +import { SUBSPACE_LONG_ARG_NAME, type SelectionParameterSet } from '../parsing/SelectionParameterSet'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { Subspace } from '../../api/Subspace'; +import { getVariantAsync, VARIANT_PARAMETER } from '../../api/Variants'; +import { measureAsyncFn } from '../../utilities/performance'; + +/** + * Temporary data structure used by `BaseInstallAction.runAsync()` + */ +interface ISubspaceInstallationData { + selectedProjects: Set; + pnpmFilterArgumentValues: string[]; +} /** * This is the common base class for InstallAction and UpdateAction. */ export abstract class BaseInstallAction extends BaseRushAction { - protected readonly _variant: CommandLineStringParameter; + protected readonly _variantParameter: CommandLineStringParameter; protected readonly _purgeParameter: CommandLineFlagParameter; protected readonly _bypassPolicyParameter: CommandLineFlagParameter; protected readonly _noLinkParameter: CommandLineFlagParameter; protected readonly _networkConcurrencyParameter: CommandLineIntegerParameter; protected readonly _debugPackageManagerParameter: CommandLineFlagParameter; - protected readonly _maxInstallAttempts: CommandLineIntegerParameter; + protected readonly _maxInstallAttempts: IRequiredCommandLineIntegerParameter; protected readonly _ignoreHooksParameter: CommandLineFlagParameter; + protected readonly _offlineParameter: CommandLineFlagParameter; /* * Subclasses can initialize the _selectionParameters property in order for * the parameters to be written to the telemetry file @@ -50,7 +63,7 @@ export abstract class BaseInstallAction extends BaseRushAction { }); this._bypassPolicyParameter = this.defineFlagParameter({ parameterLongName: RushConstants.bypassPolicyFlagLongName, - description: 'Overrides enforcement of the "gitPolicy" rules from rush.json (use honorably!)' + description: `Overrides enforcement of the "gitPolicy" rules from ${RushConstants.rushJsonFilename} (use honorably!)` }); this._noLinkParameter = this.defineFlagParameter({ parameterLongName: '--no-link', @@ -82,17 +95,97 @@ export abstract class BaseInstallAction extends BaseRushAction { }); this._ignoreHooksParameter = this.defineFlagParameter({ parameterLongName: '--ignore-hooks', - description: `Skips execution of the "eventHooks" scripts defined in rush.json. Make sure you know what you are skipping.` + description: + `Skips execution of the "eventHooks" scripts defined in ${RushConstants.rushJsonFilename}. ` + + 'Make sure you know what you are skipping.' + }); + this._offlineParameter = this.defineFlagParameter({ + parameterLongName: '--offline', + description: + `Enables installation to be performed without internet access. PNPM will instead report an error` + + ` if the necessary NPM packages cannot be obtained from the local cache.` + + ` For details, see the documentation for PNPM's "--offline" parameter.` }); - this._variant = this.defineStringParameter(Variants.VARIANT_PARAMETER); + this._variantParameter = this.defineStringParameter(VARIANT_PARAMETER); } - protected abstract buildInstallOptionsAsync(): Promise; + protected abstract buildInstallOptionsAsync(): Promise>; protected async runAsync(): Promise { - VersionMismatchFinder.ensureConsistentVersions(this.rushConfiguration, { - variant: this._variant.value - }); + const installManagerOptions: Omit = + await this.buildInstallOptionsAsync(); + + // If we are doing a filtered install and subspaces is enabled, we need to find the affected subspaces and install for all of them. + let selectedSubspaces: ReadonlySet | undefined; + const subspaceInstallationDataBySubspace: Map = new Map(); + if (this.rushConfiguration.subspacesFeatureEnabled) { + // Selecting all subspaces if preventSelectingAllSubspaces is not enabled in subspaces.json + if ( + this.rushConfiguration.subspacesConfiguration?.preventSelectingAllSubspaces && + !this._selectionParameters?.didUserSelectAnything() + ) { + this.terminal.writeLine(); + this.terminal.writeLine( + Colorize.red( + `The subspaces preventSelectingAllSubspaces configuration is enabled, which enforces installation for a specified set of subspace,` + + ` passed by the "${SUBSPACE_LONG_ARG_NAME}" parameter or selected from targeted projects using any project selector.` + ) + ); + throw new AlreadyReportedError(); + } + + const { selectedProjects } = installManagerOptions; + + if (selectedProjects.size === this.rushConfiguration.projects.length) { + // Optimization for the common case, equivalent to the logic below + selectedSubspaces = new Set(this.rushConfiguration.subspaces); + } else { + selectedSubspaces = this.rushConfiguration.getSubspacesForProjects(selectedProjects); + for (const selectedSubspace of selectedSubspaces) { + let subspaceSelectedProjects: Set; + let pnpmFilterArgumentValues: string[]; + if (selectedSubspace.getPnpmOptions()?.alwaysFullInstall) { + subspaceSelectedProjects = new Set(selectedSubspace.getProjects()); + pnpmFilterArgumentValues = []; + } else { + // This may involve filtered installs. Go through each project, add its subspace's pnpm filter arguments + subspaceSelectedProjects = new Set(); + pnpmFilterArgumentValues = []; + for (const project of selectedSubspace.getProjects()) { + if (selectedProjects.has(project)) { + subspaceSelectedProjects.add(project); + pnpmFilterArgumentValues.push(project.packageName); + } + } + } + + subspaceInstallationDataBySubspace.set(selectedSubspace, { + selectedProjects: subspaceSelectedProjects, + pnpmFilterArgumentValues + }); + } + } + } + + const variant: string | undefined = await getVariantAsync( + this._variantParameter, + this.rushConfiguration, + false + ); + if (selectedSubspaces) { + // Check each subspace for version inconsistencies + for (const subspace of selectedSubspaces) { + VersionMismatchFinder.ensureConsistentVersions(this.rushConfiguration, this.terminal, { + subspace, + variant + }); + } + } else { + VersionMismatchFinder.ensureConsistentVersions(this.rushConfiguration, this.terminal, { + subspace: undefined, + variant + }); + } const stopwatch: Stopwatch = Stopwatch.start(); @@ -113,8 +206,10 @@ export abstract class BaseInstallAction extends BaseRushAction { const purgeManager: PurgeManager = new PurgeManager(this.rushConfiguration, this.rushGlobalFolder); if (this._purgeParameter.value!) { + // eslint-disable-next-line no-console console.log('The --purge flag was specified, so performing "rush purge"'); purgeManager.purgeNormal(); + // eslint-disable-next-line no-console console.log(''); } @@ -127,48 +222,73 @@ export abstract class BaseInstallAction extends BaseRushAction { } } - // Because the 'defaultValue' option on the _maxInstallAttempts parameter is set, - // it is safe to assume that the value is not null - if (this._maxInstallAttempts.value! < 1) { + if (this._maxInstallAttempts.value < 1) { throw new Error(`The value of "${this._maxInstallAttempts.longName}" must be positive and nonzero.`); } - const installManagerOptions: IInstallManagerOptions = await this.buildInstallOptionsAsync(); - const installManagerFactoryModule: typeof import('../../logic/InstallManagerFactory') = await import( /* webpackChunkName: 'InstallManagerFactory' */ '../../logic/InstallManagerFactory' ); - const installManager: BaseInstallManager = - await installManagerFactoryModule.InstallManagerFactory.getInstallManagerAsync( - this.rushConfiguration, - this.rushGlobalFolder, - purgeManager, - installManagerOptions - ); - let installSuccessful: boolean = true; + try { - await installManager.doInstallAsync(); - - if (warnAboutScriptUpdate) { - console.log( - '\n' + - colors.yellow( - 'Rush refreshed some files in the "common/scripts" folder.' + - ' Please commit this change to Git.' - ) - ); - } + if (selectedSubspaces) { + // Run the install for each affected subspace + for (const subspace of selectedSubspaces) { + const subspaceInstallationData: ISubspaceInstallationData | undefined = + subspaceInstallationDataBySubspace.get(subspace); + // eslint-disable-next-line no-console + console.log(Colorize.green(`Installing for subspace: ${subspace.subspaceName}`)); + let installManagerOptionsForInstall: IInstallManagerOptions; + if (subspaceInstallationData) { + // This will install the selected of projects in the subspace + const { selectedProjects, pnpmFilterArgumentValues } = subspaceInstallationData; + installManagerOptionsForInstall = { + ...installManagerOptions, + selectedProjects, + // IMPORTANT: SelectionParameterSet.getPnpmFilterArgumentValuesAsync() already calculated + // installManagerOptions.pnpmFilterArgumentValues using PNPM CLI operators such as "...my-app". + // But with subspaces, "pnpm install" can only see the subset of projects in subspace's temp workspace, + // therefore an operator like "--filter ...my-app" will malfunction. As a workaround, here we are + // overwriting installManagerOptions.pnpmFilterArgumentValues with a flat last of project names that + // were calculated by Rush. + // + // TODO: If the flat list produces too many "--filter" arguments, invoking "pnpm install" will exceed + // the maximum command length and fail on Windows OS. Once this is solved, we can eliminate the + // redundant logic from SelectionParameterSet.getPnpmFilterArgumentValuesAsync(). + pnpmFilterArgumentValues, + subspace + }; + } else { + // This will install all projects in the subspace + installManagerOptionsForInstall = { + ...installManagerOptions, + pnpmFilterArgumentValues: [], + subspace + }; + } - console.log( - '\n' + colors.green(`Rush ${this.actionName} finished successfully. (${stopwatch.toString()})`) - ); + await this._doInstallAsync( + installManagerFactoryModule, + purgeManager, + installManagerOptionsForInstall + ); + } + } else { + // Simple case when subspacesFeatureEnabled=false + await this._doInstallAsync(installManagerFactoryModule, purgeManager, { + ...installManagerOptions, + subspace: this.rushConfiguration.defaultSubspace + }); + } } catch (error) { installSuccessful = false; throw error; } finally { - purgeManager.deleteAll(); + await measureAsyncFn('rush:installManager:startDeleteAllAsync', () => + purgeManager.startDeleteAllAsync() + ); stopwatch.stop(); this._collectTelemetry(stopwatch, installManagerOptions, installSuccessful); @@ -179,11 +299,43 @@ export abstract class BaseInstallAction extends BaseRushAction { this._ignoreHooksParameter.value ); } + + if (warnAboutScriptUpdate) { + // eslint-disable-next-line no-console + console.log( + '\n' + + Colorize.yellow( + 'Rush refreshed some files in the "common/scripts" folder.' + + ' Please commit this change to Git.' + ) + ); + } + + // eslint-disable-next-line no-console + console.log( + '\n' + Colorize.green(`Rush ${this.actionName} finished successfully. (${stopwatch.toString()})`) + ); + } + + private async _doInstallAsync( + installManagerFactoryModule: typeof import('../../logic/InstallManagerFactory'), + purgeManager: PurgeManager, + installManagerOptions: IInstallManagerOptions + ): Promise { + const installManager: BaseInstallManager = + await installManagerFactoryModule.InstallManagerFactory.getInstallManagerAsync( + this.rushConfiguration, + this.rushGlobalFolder, + purgeManager, + installManagerOptions + ); + + await measureAsyncFn('rush:installManager:doInstallAsync', () => installManager.doInstallAsync()); } private _collectTelemetry( stopwatch: Stopwatch, - installManagerOptions: IInstallManagerOptions, + installManagerOptions: Omit, success: boolean ): void { if (this.parser.telemetry) { diff --git a/libraries/rush-lib/src/cli/actions/BaseRushAction.ts b/libraries/rush-lib/src/cli/actions/BaseRushAction.ts index 57e4c0ed0a5..45444525d12 100644 --- a/libraries/rush-lib/src/cli/actions/BaseRushAction.ts +++ b/libraries/rush-lib/src/cli/actions/BaseRushAction.ts @@ -1,19 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; +import * as path from 'node:path'; -import { CommandLineAction, ICommandLineActionOptions } from '@rushstack/ts-command-line'; +import { CommandLineAction, type ICommandLineActionOptions } from '@rushstack/ts-command-line'; import { LockFile } from '@rushstack/node-core-library'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; -import { RushConfiguration } from '../../api/RushConfiguration'; +import type { RushConfiguration } from '../../api/RushConfiguration'; import { EventHooksManager } from '../../logic/EventHooksManager'; -import { RushCommandLineParser } from './../RushCommandLineParser'; +import { RushCommandLineParser } from '../RushCommandLineParser'; import { Utilities } from '../../utilities/Utilities'; -import { RushGlobalFolder } from '../../api/RushGlobalFolder'; -import { RushSession } from '../../pluginFramework/RushSession'; +import type { RushGlobalFolder } from '../../api/RushGlobalFolder'; +import type { RushSession } from '../../pluginFramework/RushSession'; import type { IRushCommand } from '../../pluginFramework/RushLifeCycle'; +import { measureAsyncFn } from '../../utilities/performance'; + +const PERF_PREFIX: string = 'rush:action'; export interface IBaseRushActionOptions extends ICommandLineActionOptions { /** @@ -38,43 +41,44 @@ export interface IBaseRushActionOptions extends ICommandLineActionOptions { export abstract class BaseConfiglessRushAction extends CommandLineAction implements IRushCommand { private _safeForSimultaneousRushProcesses: boolean; - protected get rushConfiguration(): RushConfiguration | undefined { - return this.parser.rushConfiguration; - } - - protected get rushSession(): RushSession { - return this.parser.rushSession; - } - - protected get rushGlobalFolder(): RushGlobalFolder { - return this.parser.rushGlobalFolder; - } - + protected readonly rushConfiguration: RushConfiguration | undefined; + protected readonly terminal: ITerminal; + protected readonly rushSession: RushSession; + protected readonly rushGlobalFolder: RushGlobalFolder; protected readonly parser: RushCommandLineParser; public constructor(options: IBaseRushActionOptions) { super(options); - this.parser = options.parser; - this._safeForSimultaneousRushProcesses = !!options.safeForSimultaneousRushProcesses; + const { parser, safeForSimultaneousRushProcesses } = options; + this.parser = parser; + const { rushConfiguration, terminal, rushSession, rushGlobalFolder } = parser; + this._safeForSimultaneousRushProcesses = !!safeForSimultaneousRushProcesses; + this.rushConfiguration = rushConfiguration; + this.terminal = terminal; + this.rushSession = rushSession; + this.rushGlobalFolder = rushGlobalFolder; } - protected onExecute(): Promise { + protected override async onExecuteAsync(): Promise { this._ensureEnvironment(); if (this.rushConfiguration) { if (!this._safeForSimultaneousRushProcesses) { if (!LockFile.tryAcquire(this.rushConfiguration.commonTempFolder, 'rush')) { - console.log(colors.red(`Another Rush command is already running in this repository.`)); + this.terminal.writeLine( + Colorize.red(`Another Rush command is already running in this repository.`) + ); process.exit(1); } } } if (!RushCommandLineParser.shouldRestrictConsoleOutput()) { - console.log(`Starting "rush ${this.actionName}"\n`); + this.terminal.write(`Starting "rush ${this.actionName}"\n`); } - return this.runAsync(); + + await measureAsyncFn(`${PERF_PREFIX}:runAsync`, () => this.runAsync()); } /** @@ -111,28 +115,30 @@ export abstract class BaseRushAction extends BaseConfiglessRushAction { return this._eventHooksManager; } - protected get rushConfiguration(): RushConfiguration { - return super.rushConfiguration!; - } + protected override readonly rushConfiguration!: RushConfiguration; - protected async onExecute(): Promise { + protected override async onExecuteAsync(): Promise { if (!this.rushConfiguration) { throw Utilities.getRushConfigNotFoundError(); } this._throwPluginErrorIfNeed(); - await this.parser.pluginManager.tryInitializeAssociatedCommandPluginsAsync(this.actionName); + await measureAsyncFn(`${PERF_PREFIX}:initializePluginsAsync`, () => + this.parser.pluginManager.tryInitializeAssociatedCommandPluginsAsync(this.actionName) + ); this._throwPluginErrorIfNeed(); const { hooks: sessionHooks } = this.rushSession; - if (sessionHooks.initialize.isUsed()) { - // Avoid the cost of compiling the hook if it wasn't tapped. - await sessionHooks.initialize.promise(this); - } + await measureAsyncFn(`${PERF_PREFIX}:initializePlugins`, async () => { + if (sessionHooks.initialize.isUsed()) { + // Avoid the cost of compiling the hook if it wasn't tapped. + await sessionHooks.initialize.promise(this); + } + }); - return super.onExecute(); + return super.onExecuteAsync(); } /** diff --git a/libraries/rush-lib/src/cli/actions/BridgePackageAction.ts b/libraries/rush-lib/src/cli/actions/BridgePackageAction.ts new file mode 100644 index 00000000000..a380251c099 --- /dev/null +++ b/libraries/rush-lib/src/cli/actions/BridgePackageAction.ts @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { + CommandLineStringListParameter, + IRequiredCommandLineStringParameter +} from '@rushstack/ts-command-line'; +import { Async } from '@rushstack/node-core-library'; + +import type { RushCommandLineParser } from '../RushCommandLineParser'; +import { BaseHotlinkPackageAction } from './BaseHotlinkPackageAction'; +import type { HotlinkManager } from '../../utilities/HotlinkManager'; +import { BRIDGE_PACKAGE_ACTION_NAME, LINK_PACKAGE_ACTION_NAME } from '../../utilities/actionNameConstants'; +import { RushConstants } from '../../logic/RushConstants'; +import type { Subspace } from '../../api/Subspace'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; + +export class BridgePackageAction extends BaseHotlinkPackageAction { + private readonly _versionParameter: IRequiredCommandLineStringParameter; + private readonly _subspaceNamesParameter: CommandLineStringListParameter; + + public constructor(parser: RushCommandLineParser) { + super({ + actionName: BRIDGE_PACKAGE_ACTION_NAME, + summary: + '(EXPERIMENTAL) Use hotlinks to simulate upgrade of a dependency for all consumers across a lockfile.', + documentation: + 'This command enables you to test a locally built project by simulating its upgrade by updating' + + ' node_modules folders using hotlinks. Unlike "pnpm link" and "npm link", the hotlinks created by this' + + ' command affect all Rush projects across the lockfile, as well as their indirect dependencies. The' + + ' simulated installation is not reflected in pnpm-lock.yaml, does not install new package.json dependencies,' + + ' and simply updates the contents of existing node_modules folders of "rush install".' + + ' The hotlinks will be cleared when you next run "rush install" or "rush update".' + + ` Compare with the "rush ${LINK_PACKAGE_ACTION_NAME}" command, which affects only the consuming project.`, + parser + }); + + this._versionParameter = this.defineStringParameter({ + parameterLongName: '--version', + argumentName: 'SEMVER_RANGE', + defaultValue: '*', + description: 'Specify which installed versions should be hotlinked.' + }); + + this._subspaceNamesParameter = this.defineStringListParameter({ + parameterLongName: '--subspace', + argumentName: 'SUBSPACE_NAME', + description: 'The name of the subspace to use for the hotlinked package.' + }); + } + + private _getSubspacesToBridgeAsync(): Set { + const subspaceToBridge: Set = new Set(); + const subspaceNames: readonly string[] = this._subspaceNamesParameter.values; + + if (subspaceNames.length > 0) { + for (const subspaceName of subspaceNames) { + const subspace: Subspace | undefined = this.rushConfiguration.tryGetSubspace(subspaceName); + if (!subspace) { + throw new Error( + `The subspace "${subspaceName}" was not found in "${RushConstants.rushPackageName}"` + ); + } + subspaceToBridge.add(subspace); + } + } else { + const currentProject: RushConfigurationProject | undefined = + this.rushConfiguration.tryGetProjectForPath(process.cwd()); + if (!currentProject) { + throw new Error(`No Rush project was found in the current working directory`); + } + subspaceToBridge.add(currentProject.subspace); + } + + return subspaceToBridge; + } + + protected async hotlinkPackageAsync( + linkedPackagePath: string, + hotlinkManager: HotlinkManager + ): Promise { + const version: string = this._versionParameter.value; + const subspaces: Set = await this._getSubspacesToBridgeAsync(); + await Async.forEachAsync( + subspaces, + async (subspace) => { + await hotlinkManager.bridgePackageAsync(this.terminal, subspace, linkedPackagePath, version); + }, + { concurrency: 5 } + ); + } +} diff --git a/libraries/rush-lib/src/cli/actions/ChangeAction.ts b/libraries/rush-lib/src/cli/actions/ChangeAction.ts index 2e0c50e6b6a..fed5bb1f8f5 100644 --- a/libraries/rush-lib/src/cli/actions/ChangeAction.ts +++ b/libraries/rush-lib/src/cli/actions/ChangeAction.ts @@ -1,41 +1,35 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as child_process from 'child_process'; -import colors from 'colors/safe'; +import * as path from 'node:path'; +import * as child_process from 'node:child_process'; -import { + +import type { CommandLineFlagParameter, CommandLineStringParameter, CommandLineChoiceParameter } from '@rushstack/ts-command-line'; -import { - FileSystem, - AlreadyReportedError, - Terminal, - ITerminal, - ConsoleTerminalProvider -} from '@rushstack/node-core-library'; +import { FileSystem, JsonFile, AlreadyReportedError } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; import { getRepoRoot } from '@rushstack/package-deps-hash'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; -import { IChangeFile, IChangeInfo, ChangeType } from '../../api/ChangeManagement'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { IRushConfigurationJson } from '../../api/RushConfiguration'; +import { type IChangeFile, type IChangeInfo, ChangeType } from '../../api/ChangeManagement'; import { ChangeFile } from '../../api/ChangeFile'; import { BaseRushAction } from './BaseRushAction'; -import { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; import { ChangeFiles } from '../../logic/ChangeFiles'; import { - VersionPolicy, - IndividualVersionPolicy, - LockStepVersionPolicy, + type VersionPolicy, + type IndividualVersionPolicy, + type LockStepVersionPolicy, VersionPolicyDefinitionName } from '../../api/VersionPolicy'; import { ProjectChangeAnalyzer } from '../../logic/ProjectChangeAnalyzer'; import { Git } from '../../logic/Git'; - -import type * as InquirerType from 'inquirer'; -import { Utilities } from '../../utilities/Utilities'; +import { RushConstants } from '../../logic/RushConstants'; const BULK_LONG_NAME: string = '--bulk'; const BULK_MESSAGE_LONG_NAME: string = '--message'; @@ -43,8 +37,8 @@ const BULK_BUMP_TYPE_LONG_NAME: string = '--bump-type'; export class ChangeAction extends BaseRushAction { private readonly _git: Git; - private readonly _terminal: ITerminal; private readonly _verifyParameter: CommandLineFlagParameter; + private readonly _verifyAllParameter: CommandLineFlagParameter; private readonly _noFetchParameter: CommandLineFlagParameter; private readonly _targetBranchParameter: CommandLineStringParameter; private readonly _changeEmailParameter: CommandLineStringParameter; @@ -84,7 +78,7 @@ export class ChangeAction extends BaseRushAction { 'HOTFIX (EXPERIMENTAL) - these are changes that are hotfixes targeting a ' + 'specific older version of the package. When a hotfix change is added, ' + 'other changes will not be able to increment the version number. ' + - "Enable this feature by setting 'hotfixChangeEnabled' in your rush.json.", + `Enable this feature by setting 'hotfixChangeEnabled' in your ${RushConstants.rushJsonFilename}.`, '' ].join('\n'); super({ @@ -98,7 +92,6 @@ export class ChangeAction extends BaseRushAction { }); this._git = new Git(this.rushConfiguration); - this._terminal = new Terminal(new ConsoleTerminalProvider({ verboseEnabled: parser.isDebug })); this._verifyParameter = this.defineFlagParameter({ parameterLongName: '--verify', @@ -106,6 +99,14 @@ export class ChangeAction extends BaseRushAction { description: 'Verify the change file has been generated and that it is a valid JSON file' }); + this._verifyAllParameter = this.defineFlagParameter({ + parameterLongName: '--verify-all', + description: + 'Validate all change files in the repository, not just those added in the current branch. ' + + 'Reports errors for change files that reference nonexistent projects or target non-main projects ' + + 'in a lockstepped version policy. Requires the "strictChangefileValidation" experiment to be enabled.' + }); + this._noFetchParameter = this.defineFlagParameter({ parameterLongName: '--no-fetch', description: 'Skips fetching the baseline branch before running "git diff" to detect changes.' @@ -170,25 +171,64 @@ export class ChangeAction extends BaseRushAction { } public async runAsync(): Promise { - console.log(`The target branch is ${this._targetBranch}`); + if (this._verifyAllParameter.value) { + const incompatibleParameters: ( + | CommandLineFlagParameter + | CommandLineStringParameter + | CommandLineChoiceParameter + )[] = [ + this._verifyParameter, + this._bulkChangeParameter, + this._bulkChangeMessageParameter, + this._bulkChangeBumpTypeParameter, + this._overwriteFlagParameter, + this._commitChangesFlagParameter + ]; + const errors: string[] = incompatibleParameters + .filter((parameter) => parameter.value) + .map( + (parameter) => + `The ${parameter.longName} parameter cannot be provided with the ` + + `${this._verifyAllParameter.longName} parameter` + ); + if (errors.length > 0) { + errors.forEach((error) => { + this.terminal.writeErrorLine(error); + }); + throw new AlreadyReportedError(); + } + + await this._validateAllChangeFilesAsync(); + return; + } + + const targetBranch: string = await this._getTargetBranchAsync(); + this.terminal.writeLine(`The target branch is ${targetBranch}`); if (this._verifyParameter.value) { - const errors: string[] = [ + const incompatibleParameters: ( + | CommandLineFlagParameter + | CommandLineStringParameter + | CommandLineChoiceParameter + )[] = [ this._bulkChangeParameter, this._bulkChangeMessageParameter, this._bulkChangeBumpTypeParameter, this._overwriteFlagParameter, this._commitChangesFlagParameter - ] + ]; + const errors: string[] = incompatibleParameters .map((parameter) => { return parameter.value - ? `The {${this._bulkChangeParameter.longName} parameter cannot be provided with the ` + + ? `The ${parameter.longName} parameter cannot be provided with the ` + `${this._verifyParameter.longName} parameter` : ''; }) .filter((error) => error !== ''); if (errors.length > 0) { - errors.forEach((error) => console.error(error)); + errors.forEach((error) => { + this.terminal.writeErrorLine(error); + }); throw new AlreadyReportedError(); } @@ -199,14 +239,12 @@ export class ChangeAction extends BaseRushAction { const sortedProjectList: string[] = (await this._getChangedProjectNamesAsync()).sort(); if (sortedProjectList.length === 0) { this._logNoChangeFileRequired(); - this._warnUnstagedChanges(); + await this._warnUnstagedChangesAsync(); return; } - this._warnUnstagedChanges(); + await this._warnUnstagedChangesAsync(); - const inquirer: typeof InquirerType = await import('inquirer'); - const promptModule: InquirerType.PromptModule = inquirer.createPromptModule(); let changeFileData: Map = new Map(); let interactiveMode: boolean = false; if (this._bulkChangeParameter.value) { @@ -264,7 +302,7 @@ export class ChangeAction extends BaseRushAction { if (errors.length > 0) { for (const error of errors) { - console.error(error); + this.terminal.writeErrorLine(error); } throw new AlreadyReportedError(); @@ -278,10 +316,10 @@ export class ChangeAction extends BaseRushAction { interactiveMode = true; const existingChangeComments: Map = ChangeFiles.getChangeComments( - this._getChangeFiles() + this.terminal, + await this._getChangeFilesSinceBaseBranchAsync() ); - changeFileData = await this._promptForChangeFileData( - promptModule, + changeFileData = await this._promptForChangeFileDataAsync( sortedProjectList, existingChangeComments ); @@ -289,7 +327,7 @@ export class ChangeAction extends BaseRushAction { if (this._isEmailRequired(changeFileData)) { const email: string = this._changeEmailParameter.value ? this._changeEmailParameter.value - : await this._detectOrAskForEmail(promptModule); + : await this._detectOrAskForEmailAsync(); changeFileData.forEach((changeFile: IChangeFile) => { changeFile.email = this.rushConfiguration.getProjectByName(changeFile.packageName)?.versionPolicy ?.includeEmailInChangeFile @@ -300,8 +338,7 @@ export class ChangeAction extends BaseRushAction { } let changefiles: string[]; try { - changefiles = await this._writeChangeFiles( - promptModule, + changefiles = await this._writeChangeFilesAsync( changeFileData, this._overwriteFlagParameter.value, interactiveMode @@ -311,14 +348,15 @@ export class ChangeAction extends BaseRushAction { } if (this._commitChangesFlagParameter.value || this._commitChangesMessageStringParameter.value) { if (changefiles && changefiles.length !== 0) { - this._stageAndCommitGitChanges( + await this._git.stageAndCommitGitChangesAsync( changefiles, this._commitChangesMessageStringParameter.value || this.rushConfiguration.gitChangefilesCommitMessage || - 'Rush change' + 'Rush change', + this.terminal ); } else { - this._terminal.writeWarningLine('Warning: No change files generated, nothing to commit.'); + this.terminal.writeWarningLine('Warning: No change files generated, nothing to commit.'); } } } @@ -339,17 +377,40 @@ export class ChangeAction extends BaseRushAction { } private async _verifyAsync(): Promise { - const changedPackages: string[] = await this._getChangedProjectNamesAsync(); - if (changedPackages.length > 0) { - this._validateChangeFile(changedPackages); + const changedProjectNames: string[] = await this._getChangedProjectNamesAsync(); + const strictValidation: boolean | undefined = + this.rushConfiguration.experimentsConfiguration.configuration.strictChangefileValidation; + + // When strict validation is enabled, validate ALL change files to catch references to + // deleted or nonexistent projects. Otherwise, only validate change files added on this branch. + const changeFilesInstance: ChangeFiles = new ChangeFiles(this.rushConfiguration); + let filesToValidate: string[]; + if (strictValidation) { + filesToValidate = await changeFilesInstance.getAllChangeFilesAsync(); + } else { + filesToValidate = await this._getChangeFilesSinceBaseBranchAsync(); + } + + if (changedProjectNames.length > 0 || filesToValidate.length > 0) { + const deletedProjectNames: Set | undefined = strictValidation + ? await this._getDeletedProjectNamesAsync() + : undefined; + + await changeFilesInstance.validateAsync({ + terminal: this.terminal, + filesToValidate, + changedProjectNames, + deletedProjectNames + }); } else { this._logNoChangeFileRequired(); } } - private get _targetBranch(): string { + private async _getTargetBranchAsync(): Promise { if (!this._targetBranchName) { - this._targetBranchName = this._targetBranchParameter.value || this._git.getRemoteDefaultBranch(); + this._targetBranchName = + this._targetBranchParameter.value || (await this._git.getRemoteDefaultBranchAsync()); } return this._targetBranchName; @@ -359,14 +420,16 @@ export class ChangeAction extends BaseRushAction { const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(this.rushConfiguration); const changedProjects: Set = await projectChangeAnalyzer.getChangedProjectsAsync({ - targetBranchName: this._targetBranch, - terminal: this._terminal, + targetBranchName: await this._getTargetBranchAsync(), + terminal: this.terminal, shouldFetch: !this._noFetchParameter.value, // Lockfile evaluation will expand the set of projects that request change files // Not enabling, since this would be a breaking change includeExternalDependencies: false, // Since install may not have happened, cannot read rush-project.json - enableFiltering: false + enableFiltering: false, + // Exclude version-only changes to prevent 'rush version --bump' from triggering 'rush change --verify' + excludeVersionOnlyChanges: true }); const projectHostMap: Map = this._generateHostMap(); @@ -383,34 +446,90 @@ export class ChangeAction extends BaseRushAction { return Array.from(changedProjectNames); } - private _validateChangeFile(changedPackages: string[]): void { - const files: string[] = this._getChangeFiles(); - ChangeFiles.validate(files, changedPackages, this.rushConfiguration); + private async _validateAllChangeFilesAsync(): Promise { + if (!this.rushConfiguration.experimentsConfiguration.configuration.strictChangefileValidation) { + throw new Error( + `The ${this._verifyAllParameter.longName} parameter requires the ` + + '"strictChangefileValidation" experiment to be enabled.' + ); + } + + const changeFiles: ChangeFiles = new ChangeFiles(this.rushConfiguration); + const allChangeFiles: string[] = await changeFiles.getAllChangeFilesAsync(); + const deletedProjectNames: Set = await this._getDeletedProjectNamesAsync(); + await changeFiles.validateAsync({ + terminal: this.terminal, + filesToValidate: allChangeFiles, + changedProjectNames: [], + deletedProjectNames + }); + } + + /** + * Compares the current rush.json project list against the target branch to find + * projects that were removed. + */ + private async _getDeletedProjectNamesAsync(): Promise> { + const repoRoot: string = getRepoRoot(this.rushConfiguration.rushJsonFolder); + const targetBranch: string = await this._getTargetBranchAsync(); + const mergeBase: string = await this._git.getMergeBaseAsync(targetBranch, this.terminal); + + let oldRushJsonContent: string; + try { + const rushJsonRelativePath: string = path.relative(repoRoot, this.rushConfiguration.rushJsonFile); + oldRushJsonContent = await this._git.getBlobContentAsync({ + blobSpec: `${mergeBase}:${rushJsonRelativePath}`, + repositoryRoot: repoRoot + }); + } catch { + // If rush.json didn't exist on the target branch, no projects were deleted + return new Set(); + } + + const oldRushJson: IRushConfigurationJson = JsonFile.parseString(oldRushJsonContent); + const currentProjectsByName: ReadonlyMap = + this.rushConfiguration.projectsByName; + + const deletedProjectNames: Set = new Set(); + for (const { packageName } of oldRushJson.projects) { + if (!currentProjectsByName.has(packageName)) { + deletedProjectNames.add(packageName); + } + } + + return deletedProjectNames; } - private _getChangeFiles(): string[] { + private async _getChangeFilesSinceBaseBranchAsync(): Promise { const repoRoot: string = getRepoRoot(this.rushConfiguration.rushJsonFolder); const relativeChangesFolder: string = path.relative(repoRoot, this.rushConfiguration.changesFolder); - return this._git - .getChangedFiles(this._targetBranch, this._terminal, true, relativeChangesFolder) - .map((relativePath) => { - return path.join(repoRoot, relativePath); - }); + const targetBranch: string = await this._getTargetBranchAsync(); + const changedFiles: string[] = await this._git.getChangedFilesAsync( + targetBranch, + this.terminal, + true, + relativeChangesFolder + ); + + const result: string[] = []; + for (const changedFile of changedFiles) { + result.push(path.join(repoRoot, changedFile)); + } + + return result; } /** * The main loop which prompts the user for information on changed projects. */ - private async _promptForChangeFileData( - promptModule: InquirerType.PromptModule, + private async _promptForChangeFileDataAsync( sortedProjectList: string[], existingChangeComments: Map ): Promise> { const changedFileData: Map = new Map(); for (const projectName of sortedProjectList) { - const changeInfo: IChangeInfo | undefined = await this._askQuestions( - promptModule, + const changeInfo: IChangeInfo | undefined = await this._askQuestionsAsync( projectName, existingChangeComments ); @@ -436,23 +555,21 @@ export class ChangeAction extends BaseRushAction { /** * Asks all questions which are needed to generate changelist for a project. */ - private async _askQuestions( - promptModule: InquirerType.PromptModule, + private async _askQuestionsAsync( packageName: string, existingChangeComments: Map ): Promise { - console.log(`\n${packageName}`); + this.terminal.writeLine(`\n${packageName}`); const comments: string[] | undefined = existingChangeComments.get(packageName); if (comments) { - console.log(`Found existing comments:`); + this.terminal.writeLine(`Found existing comments:`); comments.forEach((comment) => { - console.log(` > ${comment}`); + this.terminal.writeLine(` > ${comment}`); }); - const { appendComment }: { appendComment: 'skip' | 'append' } = await promptModule({ - name: 'appendComment', - type: 'list', - default: 'skip', + const { default: select } = await import('@inquirer/select'); + const appendComment: 'skip' | 'append' = await select<'skip' | 'append'>({ message: 'Append to existing comments or skip?', + default: 'skip', choices: [ { name: 'Skip', @@ -468,23 +585,19 @@ export class ChangeAction extends BaseRushAction { if (appendComment === 'skip') { return undefined; } else { - return await this._promptForComments(promptModule, packageName); + return await this._promptForCommentsAsync(packageName); } } else { - return await this._promptForComments(promptModule, packageName); + return await this._promptForCommentsAsync(packageName); } } - private async _promptForComments( - promptModule: InquirerType.PromptModule, + private async _promptForCommentsAsync( packageName: string ): Promise { const bumpOptions: { [type: string]: string } = this._getBumpOptions(packageName); - const { comment }: { comment: string } = await promptModule({ - name: 'comment', - type: 'input', - message: `Describe changes, or ENTER if no changes:` - }); + const { default: input } = await import('@inquirer/input'); + const comment: string = await input({ message: `Describe changes, or ENTER if no changes:` }); if (Object.keys(bumpOptions).length === 0 || !comment) { return { @@ -493,7 +606,8 @@ export class ChangeAction extends BaseRushAction { type: ChangeType[ChangeType.none] } as IChangeInfo; } else { - const { bumpType }: { bumpType: string } = await promptModule({ + const { default: select } = await import('@inquirer/select'); + const bumpType: string = await select({ choices: Object.keys(bumpOptions).map((option) => { return { value: option, @@ -501,9 +615,7 @@ export class ChangeAction extends BaseRushAction { }; }), default: 'patch', - message: 'Select the type of change:', - name: 'bumpType', - type: 'list' + message: 'Select the type of change:' }); return { @@ -567,8 +679,11 @@ export class ChangeAction extends BaseRushAction { * Will determine a user's email by first detecting it from their Git config, * or will ask for it if it is not found or the Git config is wrong. */ - private async _detectOrAskForEmail(promptModule: InquirerType.PromptModule): Promise { - return (await this._detectAndConfirmEmail(promptModule)) || (await this._promptForEmail(promptModule)); + private async _detectOrAskForEmailAsync(): Promise { + return ( + (await this._detectAndConfirmEmailAsync()) || + (await this._promptForEmailAsync()) + ); } private _detectEmail(): string | undefined { @@ -578,7 +693,7 @@ export class ChangeAction extends BaseRushAction { .toString() .replace(/(\r\n|\n|\r)/gm, ''); } catch (err) { - console.log('There was an issue detecting your Git email...'); + this.terminal.writeLine('There was an issue detecting your Git email...'); return undefined; } } @@ -587,18 +702,15 @@ export class ChangeAction extends BaseRushAction { * Detects the user's email address from their Git configuration, prompts the user to approve the * detected email. It returns undefined if it cannot be detected. */ - private async _detectAndConfirmEmail(promptModule: InquirerType.PromptModule): Promise { + private async _detectAndConfirmEmailAsync(): Promise { const email: string | undefined = this._detectEmail(); if (email) { - const { isCorrectEmail }: { isCorrectEmail: boolean } = await promptModule([ - { - type: 'confirm', - name: 'isCorrectEmail', - default: 'Y', - message: `Is your email address ${email}?` - } - ]); + const { default: confirm } = await import('@inquirer/confirm'); + const isCorrectEmail: boolean = await confirm({ + message: `Is your email address ${email}?`, + default: true + }); return isCorrectEmail ? email : undefined; } else { return undefined; @@ -608,49 +720,44 @@ export class ChangeAction extends BaseRushAction { /** * Asks the user for their email address */ - private async _promptForEmail(promptModule: InquirerType.PromptModule): Promise { - const { email }: { email: string } = await promptModule([ - { - type: 'input', - name: 'email', - message: 'What is your email address?', - validate: (input: string) => { - return true; // @todo should be an email - } + private async _promptForEmailAsync(): Promise { + const { default: input } = await import('@inquirer/input'); + return await input({ + message: 'What is your email address?', + validate: (value: string) => { + return true; // @todo should be an email } - ]); - return email; + }); } - private _warnUnstagedChanges(): void { + private async _warnUnstagedChangesAsync(): Promise { try { - if (this._git.hasUnstagedChanges()) { - console.log( + const hasUnstagedChanges: boolean = await this._git.hasUnstagedChangesAsync(); + if (hasUnstagedChanges) { + this.terminal.writeLine( '\n' + - colors.yellow( + Colorize.yellow( 'Warning: You have unstaged changes, which do not trigger prompting for change ' + 'descriptions.' ) ); } } catch (error) { - console.log(`An error occurred when detecting unstaged changes: ${error}`); + this.terminal.writeLine(`An error occurred when detecting unstaged changes: ${error}`); } } /** * Writes change files to the common/changes folder. Will prompt for overwrite if file already exists. */ - private async _writeChangeFiles( - promptModule: InquirerType.PromptModule, + private async _writeChangeFilesAsync( changeFileData: Map, overwrite: boolean, interactiveMode: boolean ): Promise { const writtenFiles: string[] = []; await changeFileData.forEach(async (changeFile: IChangeFile) => { - const writtenFile: string | undefined = await this._writeChangeFile( - promptModule, + const writtenFile: string | undefined = await this._writeChangeFileAsync( changeFile, overwrite, interactiveMode @@ -662,8 +769,7 @@ export class ChangeAction extends BaseRushAction { return writtenFiles; } - private async _writeChangeFile( - promptModule: InquirerType.PromptModule, + private async _writeChangeFileAsync( changeFileData: IChangeFile, overwrite: boolean, interactiveMode: boolean @@ -676,7 +782,7 @@ export class ChangeAction extends BaseRushAction { const shouldWrite: boolean = !fileExists || overwrite || - (interactiveMode ? await this._promptForOverwrite(promptModule, filePath) : false); + (interactiveMode ? await this._promptForOverwriteAsync(filePath) : false); if (!interactiveMode && fileExists && !overwrite) { throw new Error(`Changefile ${filePath} already exists`); @@ -688,22 +794,18 @@ export class ChangeAction extends BaseRushAction { } } - private async _promptForOverwrite( - promptModule: InquirerType.PromptModule, + private async _promptForOverwriteAsync( filePath: string ): Promise { - const overwrite: boolean = await promptModule([ - { - name: 'overwrite', - type: 'confirm', - message: `Overwrite ${filePath}?` - } - ]); + const { default: confirm } = await import('@inquirer/confirm'); + const overwrite: boolean = await confirm({ + message: `Overwrite ${filePath}?` + }); if (overwrite) { return true; } else { - console.log(`Not overwriting ${filePath}`); + this.terminal.writeLine(`Not overwriting ${filePath}`); return false; } } @@ -714,30 +816,13 @@ export class ChangeAction extends BaseRushAction { private _writeFile(fileName: string, output: string, isOverwrite: boolean): void { FileSystem.writeFile(fileName, output, { ensureFolderExists: true }); if (isOverwrite) { - console.log(`Overwrote file: ${fileName}`); + this.terminal.writeLine(`Overwrote file: ${fileName}`); } else { - console.log(`Created file: ${fileName}`); + this.terminal.writeLine(`Created file: ${fileName}`); } } private _logNoChangeFileRequired(): void { - console.log('No changes were detected to relevant packages on this branch. Nothing to do.'); - } - - private _stageAndCommitGitChanges(pattern: string[], message: string): void { - try { - Utilities.executeCommand({ - command: 'git', - args: ['add', ...pattern], - workingDirectory: this.rushConfiguration.changesFolder - }); - Utilities.executeCommand({ - command: 'git', - args: ['commit', ...pattern, '-m', message], - workingDirectory: this.rushConfiguration.changesFolder - }); - } catch (error) { - this._terminal.writeErrorLine(`ERROR: Cannot stage and commit git changes ${(error as Error).message}`); - } + this.terminal.writeLine('No changes were detected to relevant packages on this branch. Nothing to do.'); } } diff --git a/libraries/rush-lib/src/cli/actions/CheckAction.ts b/libraries/rush-lib/src/cli/actions/CheckAction.ts index 3469a0bb413..fcf752b0657 100644 --- a/libraries/rush-lib/src/cli/actions/CheckAction.ts +++ b/libraries/rush-lib/src/cli/actions/CheckAction.ts @@ -1,18 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import { CommandLineStringParameter, CommandLineFlagParameter } from '@rushstack/ts-command-line'; +import type { CommandLineFlagParameter, CommandLineStringParameter } from '@rushstack/ts-command-line'; +import { Colorize } from '@rushstack/terminal'; -import { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; import { BaseRushAction } from './BaseRushAction'; import { VersionMismatchFinder } from '../../logic/versionMismatch/VersionMismatchFinder'; -import { Variants } from '../../api/Variants'; +import { getVariantAsync, VARIANT_PARAMETER } from '../../api/Variants'; export class CheckAction extends BaseRushAction { - private readonly _variant: CommandLineStringParameter; private readonly _jsonFlag: CommandLineFlagParameter; private readonly _verboseFlag: CommandLineFlagParameter; + private readonly _subspaceParameter: CommandLineStringParameter | undefined; + private readonly _variantParameter: CommandLineStringParameter; public constructor(parser: RushCommandLineParser) { super({ @@ -27,7 +28,6 @@ export class CheckAction extends BaseRushAction { parser }); - this._variant = this.defineStringParameter(Variants.VARIANT_PARAMETER); this._jsonFlag = this.defineFlagParameter({ parameterLongName: '--json', description: 'If this flag is specified, output will be in JSON format.' @@ -38,24 +38,47 @@ export class CheckAction extends BaseRushAction { 'If this flag is specified, long lists of package names will not be truncated. ' + `This has no effect if the ${this._jsonFlag.longName} flag is also specified.` }); + this._subspaceParameter = this.defineStringParameter({ + parameterLongName: '--subspace', + argumentName: 'SUBSPACE_NAME', + description: + '(EXPERIMENTAL) Specifies an individual Rush subspace to check, requiring versions to be ' + + 'consistent only within that subspace (ignoring other subspaces). This parameter is required when ' + + 'the "subspacesEnabled" setting is set to true in subspaces.json.' + }); + this._variantParameter = this.defineStringParameter(VARIANT_PARAMETER); } protected async runAsync(): Promise { - const variant: string | undefined = this.rushConfiguration.currentInstalledVariant; + if (this.rushConfiguration.subspacesFeatureEnabled && !this._subspaceParameter) { + throw new Error( + `The --subspace parameter must be specified with "rush check" when subspaces is enabled.` + ); + } - if (!this._variant.value && variant) { - console.log( - colors.yellow( - `Variant '${variant}' has been installed, but 'rush check' is currently checking the default variant. ` + - `Use 'rush check --variant '${variant}' to check the current installation.` + const currentlyInstalledVariant: string | undefined = + await this.rushConfiguration.getCurrentlyInstalledVariantAsync(); + const variant: string | undefined = await getVariantAsync( + this._variantParameter, + this.rushConfiguration, + true + ); + if (!variant && currentlyInstalledVariant) { + this.terminal.writeWarningLine( + Colorize.yellow( + `Variant '${currentlyInstalledVariant}' has been installed, but 'rush check' is currently checking the default variant. ` + + `Use 'rush ${this.actionName} ${this._variantParameter.longName} '${currentlyInstalledVariant}' to check the current installation.` ) ); } - VersionMismatchFinder.rushCheck(this.rushConfiguration, { - variant: this._variant.value, + VersionMismatchFinder.rushCheck(this.rushConfiguration, this.terminal, { + variant, printAsJson: this._jsonFlag.value, - truncateLongPackageNameLists: !this._verboseFlag.value + truncateLongPackageNameLists: !this._verboseFlag.value, + subspace: this._subspaceParameter?.value + ? this.rushConfiguration.getSubspace(this._subspaceParameter.value) + : this.rushConfiguration.defaultSubspace }); } } diff --git a/libraries/rush-lib/src/cli/actions/DeployAction.ts b/libraries/rush-lib/src/cli/actions/DeployAction.ts index e3bf8956252..ebbf57c7eeb 100644 --- a/libraries/rush-lib/src/cli/actions/DeployAction.ts +++ b/libraries/rush-lib/src/cli/actions/DeployAction.ts @@ -1,10 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import type { IPackageJson } from '@rushstack/node-core-library'; -import { CommandLineFlagParameter, CommandLineStringParameter } from '@rushstack/ts-command-line'; -import type { PackageExtractor, IExtractorProjectConfiguration } from '@rushstack/package-extractor'; +import * as path from 'node:path'; + +import type { CommandLineFlagParameter, CommandLineStringParameter } from '@rushstack/ts-command-line'; +import type { + PackageExtractor, + IExtractorProjectConfiguration, + IExtractorSubspace +} from '@rushstack/package-extractor'; import { BaseRushAction } from './BaseRushAction'; import type { RushCommandLineParser } from '../RushCommandLineParser'; @@ -14,6 +18,7 @@ import type { DeployScenarioConfiguration, IDeployScenarioProjectJson } from '../../logic/deploy/DeployScenarioConfiguration'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; export class DeployAction extends BaseRushAction { private readonly _logger: ILogger; @@ -142,21 +147,47 @@ export class DeployAction extends BaseRushAction { const createArchiveOnly: boolean = this._createArchiveOnly.value; - let transformPackageJson: ((packageJson: IPackageJson) => IPackageJson) | undefined; - let pnpmInstallFolder: string | undefined; - if (this.rushConfiguration.packageManager === 'pnpm') { - const pnpmfileConfiguration: PnpmfileConfiguration = await PnpmfileConfiguration.initializeAsync( - this.rushConfiguration - ); - transformPackageJson = pnpmfileConfiguration.transform.bind(pnpmfileConfiguration); - if (!scenarioConfiguration.json.omitPnpmWorkaroundLinks) { - pnpmInstallFolder = this.rushConfiguration.commonTempFolder; + /** + * Subspaces that will be involved in deploy process. + * Each subspace may have its own configurations + */ + const subspaces: Map = new Map(); + + const rushConfigurationProject: RushConfigurationProject | undefined = + this.rushConfiguration.getProjectByName(mainProjectName); + if (!rushConfigurationProject) { + throw new Error(`The specified deployment project "${mainProjectName}" was not found in rush.json`); + } + + const projects: RushConfigurationProject[] = this.rushConfiguration.projects; + if (this.rushConfiguration.isPnpm) { + const currentlyInstalledVariant: string | undefined = + await this.rushConfiguration.getCurrentlyInstalledVariantAsync(); + for (const project of projects) { + const pnpmfileConfiguration: PnpmfileConfiguration = await PnpmfileConfiguration.initializeAsync( + this.rushConfiguration, + project.subspace, + currentlyInstalledVariant + ); + const subspace: IExtractorSubspace = { + subspaceName: project.subspace.subspaceName, + transformPackageJson: pnpmfileConfiguration.transform.bind(pnpmfileConfiguration) + }; + + if (subspaces.has(subspace.subspaceName)) { + continue; + } + + if (!scenarioConfiguration.json.omitPnpmWorkaroundLinks) { + subspace.pnpmInstallFolder = project.subspace.getSubspaceTempFolderPath(); + } + subspaces.set(subspace.subspaceName, subspace); } } // Construct the project list for the deployer const projectConfigurations: IExtractorProjectConfiguration[] = []; - for (const project of this.rushConfiguration.projects) { + for (const project of projects) { const scenarioProjectJson: IDeployScenarioProjectJson | undefined = scenarioConfiguration.projectJsonsByName.get(project.packageName); projectConfigurations.push({ @@ -164,7 +195,9 @@ export class DeployAction extends BaseRushAction { projectFolder: project.projectFolder, additionalProjectsToInclude: scenarioProjectJson?.additionalProjectsToInclude, additionalDependenciesToInclude: scenarioProjectJson?.additionalDependenciesToInclude, - dependenciesToExclude: scenarioProjectJson?.dependenciesToExclude + dependenciesToExclude: scenarioProjectJson?.dependenciesToExclude, + patternsToInclude: scenarioProjectJson?.patternsToInclude, + patternsToExclude: scenarioProjectJson?.patternsToExclude }); } @@ -185,10 +218,10 @@ export class DeployAction extends BaseRushAction { targetRootFolder, mainProjectName, projectConfigurations, + dependencyConfigurations: scenarioConfiguration.json.dependencySettings, createArchiveFilePath, createArchiveOnly, - pnpmInstallFolder, - transformPackageJson + subspaces: Array.from(subspaces.values()) }); } } diff --git a/libraries/rush-lib/src/cli/actions/InitAction.ts b/libraries/rush-lib/src/cli/actions/InitAction.ts index 8bb6f914ec9..7eeb7480312 100644 --- a/libraries/rush-lib/src/cli/actions/InitAction.ts +++ b/libraries/rush-lib/src/cli/actions/InitAction.ts @@ -1,57 +1,26 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; +import * as path from 'node:path'; + import { FileSystem, - NewlineKind, InternalError, AlreadyReportedError, - FileSystemStats + type FileSystemStats } from '@rushstack/node-core-library'; -import { CommandLineFlagParameter } from '@rushstack/ts-command-line'; +import type { CommandLineFlagParameter } from '@rushstack/ts-command-line'; +import { Colorize } from '@rushstack/terminal'; -import { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; import { BaseConfiglessRushAction } from './BaseRushAction'; - -import { Rush } from '../../api/Rush'; import { assetsFolderPath } from '../../utilities/PathConstants'; - -// Matches a well-formed BEGIN macro starting a block section. -// Example: /*[BEGIN "DEMO"]*/ -// -// Group #1 is the indentation spaces before the macro -// Group #2 is the section name -const BEGIN_MARCO_REGEXP: RegExp = /^(\s*)\/\*\[BEGIN "([A-Z]+)"\]\s*\*\/\s*$/; - -// Matches a well-formed END macro ending a block section. -// Example: /*[END "DEMO"]*/ -// -// Group #1 is the indentation spaces before the macro -// Group #2 is the section name -const END_MACRO_REGEXP: RegExp = /^(\s*)\/\*\[END "([A-Z]+)"\]\s*\*\/\s*$/; - -// Matches a well-formed single-line section, including the space character after it -// if present. -// Example: /*[LINE "HYPOTHETICAL"]*/ -// -// Group #1 is the section name -const LINE_MACRO_REGEXP: RegExp = /\/\*\[LINE "([A-Z]+)"\]\s*\*\/\s?/; - -// Matches a variable expansion. -// Example: [%RUSH_VERSION%] -// -// Group #1 is the variable name including the dollar sign -const VARIABLE_MACRO_REGEXP: RegExp = /\[(%[A-Z0-9_]+%)\]/; - -// Matches anything that starts with "/*[" and ends with "]*/" -// Used to catch malformed macro expressions -const ANY_MACRO_REGEXP: RegExp = /\/\*\s*\[.*\]\s*\*\//; +import { copyTemplateFileAsync } from '../../utilities/templateUtilities'; export class InitAction extends BaseConfiglessRushAction { private readonly _overwriteParameter: CommandLineFlagParameter; private readonly _rushExampleParameter: CommandLineFlagParameter; + private readonly _experimentsParameter: CommandLineFlagParameter; // template section name --> whether it should be commented out private _commentedBySectionName: Map = new Map(); @@ -80,6 +49,12 @@ export class InitAction extends BaseConfiglessRushAction { ' by the "rush-example" GitHub repo, which is a sample monorepo that illustrates many Rush' + ' features. This option is primarily intended for maintaining that example.' }); + this._experimentsParameter = this.defineFlagParameter({ + parameterLongName: '--include-experiments', + description: + 'Include features that may not be complete features, useful for demoing specific future features' + + ' or current work in progress features.' + }); } protected async runAsync(): Promise { @@ -91,28 +66,17 @@ export class InitAction extends BaseConfiglessRushAction { } } - this._defineMacroSections(); - this._copyTemplateFiles(initFolder); - } - - private _defineMacroSections(): void { - this._commentedBySectionName.clear(); - - // The "HYPOTHETICAL" sections are always commented out by "rush init". - // They are uncommented in the "assets" source folder so that we can easily validate - // that they conform to their JSON schema. - this._commentedBySectionName.set('HYPOTHETICAL', true); - - // The "DEMO" sections are uncommented only when "--rush-example-repo" is specified. - this._commentedBySectionName.set('DEMO', !this._rushExampleParameter.value); + await this._copyTemplateFilesAsync(initFolder); } // Check whether it's safe to run "rush init" in the current working directory. private _validateFolderIsEmpty(initFolder: string): boolean { if (this.rushConfiguration !== undefined) { + // eslint-disable-next-line no-console console.error( - colors.red('ERROR: Found an existing configuration in: ' + this.rushConfiguration.rushJsonFile) + Colorize.red('ERROR: Found an existing configuration in: ' + this.rushConfiguration.rushJsonFile) ); + // eslint-disable-next-line no-console console.log( '\nThe "rush init" command must be run in a new folder without an existing Rush configuration.' ); @@ -131,12 +95,16 @@ export class InitAction extends BaseConfiglessRushAction { // Ignore any loose files in the current folder, e.g. "README.md" // or "CONTRIBUTING.md" if (stats.isDirectory()) { - console.error(colors.red(`ERROR: Found a subdirectory: "${itemName}"`)); + // eslint-disable-next-line no-console + console.error(Colorize.red(`ERROR: Found a subdirectory: "${itemName}"`)); + // eslint-disable-next-line no-console console.log('\nThe "rush init" command must be run in a new folder with no projects added yet.'); return false; } else { if (itemName.toLowerCase() === 'package.json') { - console.error(colors.red(`ERROR: Found a package.json file in this folder`)); + // eslint-disable-next-line no-console + console.error(Colorize.red(`ERROR: Found a package.json file in this folder`)); + // eslint-disable-next-line no-console console.log('\nThe "rush init" command must be run in a new folder with no projects added yet.'); return false; } @@ -145,7 +113,7 @@ export class InitAction extends BaseConfiglessRushAction { return true; } - private _copyTemplateFiles(initFolder: string): void { + private async _copyTemplateFilesAsync(initFolder: string): Promise { // The "[dot]" base name is used for hidden files to prevent various tools from interpreting them. // For example, "npm publish" will always exclude the filename ".gitignore" const templateFilePaths: string[] = [ @@ -156,11 +124,14 @@ export class InitAction extends BaseConfiglessRushAction { 'common/config/rush/[dot]npmrc-publish', 'common/config/rush/artifactory.json', 'common/config/rush/build-cache.json', + 'common/config/rush/cobuild.json', 'common/config/rush/command-line.json', 'common/config/rush/common-versions.json', + 'common/config/rush/custom-tips.json', 'common/config/rush/experiments.json', 'common/config/rush/pnpm-config.json', 'common/config/rush/rush-plugins.json', + 'common/config/rush/subspaces.json', 'common/config/rush/version-policies.json', 'common/git-hooks/commit-msg.sample', @@ -170,6 +141,12 @@ export class InitAction extends BaseConfiglessRushAction { 'rush.json' ]; + const experimentalTemplateFilePaths: string[] = ['common/config/rush/rush-alerts.json']; + + if (this._experimentsParameter.value) { + templateFilePaths.push(...experimentalTemplateFilePaths); + } + const assetsSubfolder: string = `${assetsFolderPath}/rush-init`; for (const templateFilePath of templateFilePaths) { @@ -182,189 +159,13 @@ export class InitAction extends BaseConfiglessRushAction { const destinationPath: string = path.join(initFolder, templateFilePath).replace('[dot]', '.'); - this._copyTemplateFile(sourcePath, destinationPath); - } - } - - // Copy the template from sourcePath, transform any macros, and write the output to destinationPath. - // - // We implement a simple template engine. "Single-line section" macros have this form: - // - // /*[LINE "NAME"]*/ (content goes here) - // - // ...and when commented out will look like this: - // - // // (content goes here) - // - // "Block section" macros have this form: - // - // /*[BEGIN "NAME"]*/ - // (content goes - // here) - // /*[END "NAME"]*/ - // - // ...and when commented out will look like this: - // - // // (content goes - // // here) - // - // Lastly, a variable expansion has this form: - // - // // The value is [%NAME%]. - // - // ...and when expanded with e.g. "123" will look like this: - // - // // The value is 123. - // - // The section names must be one of the predefined names used by "rush init". - // A single-line section may appear inside a block section, in which case it will get - // commented twice. - private _copyTemplateFile(sourcePath: string, destinationPath: string): void { - const destinationFileExists: boolean = FileSystem.exists(destinationPath); - - if (!this._overwriteParameter.value) { - if (destinationFileExists) { - console.log(colors.yellow('Not overwriting already existing file: ') + destinationPath); - return; - } - } - - if (destinationFileExists) { - console.log(colors.yellow(`Overwriting: ${destinationPath}`)); - } else { - console.log(`Generating: ${destinationPath}`); - } - - const outputLines: string[] = []; - const lines: string[] = FileSystem.readFile(sourcePath, { convertLineEndings: NewlineKind.Lf }).split( - '\n' - ); - - let activeBlockSectionName: string | undefined = undefined; - let activeBlockIndent: string = ''; - - for (const line of lines) { - let match: RegExpMatchArray | null; - - // Check for a block section start - // Example: /*[BEGIN "DEMO"]*/ - match = line.match(BEGIN_MARCO_REGEXP); - if (match) { - if (activeBlockSectionName) { - // If this happens, please report a Rush bug - throw new InternalError( - `The template contains an unmatched BEGIN macro for "${activeBlockSectionName}"` - ); - } - - activeBlockSectionName = match[2]; - activeBlockIndent = match[1]; - // Remove the entire line containing the macro - continue; - } - - // Check for a block section end - // Example: /*[END "DEMO"]*/ - match = line.match(END_MACRO_REGEXP); - if (match) { - if (activeBlockSectionName === undefined) { - // If this happens, please report a Rush bug - throw new InternalError( - `The template contains an unmatched END macro for "${activeBlockSectionName}"` - ); - } - - if (activeBlockSectionName !== match[2]) { - // If this happens, please report a Rush bug - throw new InternalError( - `The template contains an mismatched END macro for "${activeBlockSectionName}"` - ); - } - - if (activeBlockIndent !== match[1]) { - // If this happens, please report a Rush bug - throw new InternalError( - `The template contains an inconsistently indented section "${activeBlockSectionName}"` - ); - } - - activeBlockSectionName = undefined; - - // Remove the entire line containing the macro - continue; - } - - let transformedLine: string = line; - - // Check for a single-line section - // Example: /*[LINE "HYPOTHETICAL"]*/ - match = transformedLine.match(LINE_MACRO_REGEXP); - if (match) { - const sectionName: string = match[1]; - const replacement: string = this._isSectionCommented(sectionName) ? '// ' : ''; - transformedLine = transformedLine.replace(LINE_MACRO_REGEXP, replacement); - } - - // Check for variable expansions - // Example: [%RUSH_VERSION%] - while ((match = transformedLine.match(VARIABLE_MACRO_REGEXP))) { - const variableName: string = match[1]; - const replacement: string = this._expandMacroVariable(variableName); - transformedLine = transformedLine.replace(VARIABLE_MACRO_REGEXP, replacement); - } - - // Verify that all macros were handled - match = transformedLine.match(ANY_MACRO_REGEXP); - if (match) { - // If this happens, please report a Rush bug - throw new InternalError( - 'The template contains a malformed macro expression: ' + JSON.stringify(match[0]) - ); - } - - // If we are inside a block section that is commented out, then insert the "//" after indentation - if (activeBlockSectionName !== undefined) { - if (this._isSectionCommented(activeBlockSectionName)) { - // Is the line indented properly? - if (transformedLine.substr(0, activeBlockIndent.length).trim().length > 0) { - // If this happens, please report a Rush bug - throw new InternalError( - `The template contains inconsistently indented lines inside` + - ` the "${activeBlockSectionName}" section` - ); - } - - // Insert comment characters after the indentation - const contentAfterIndent: string = transformedLine.substr(activeBlockIndent.length); - transformedLine = activeBlockIndent + '// ' + contentAfterIndent; - } - } - - outputLines.push(transformedLine); - } - - // Write the output - FileSystem.writeFile(destinationPath, outputLines.join('\n'), { - ensureFolderExists: true - }); - } - - private _isSectionCommented(sectionName: string): boolean { - const value: boolean | undefined = this._commentedBySectionName.get(sectionName); - if (value === undefined) { - // If this happens, please report a Rush bug - throw new InternalError(`The template references an undefined section name ${sectionName}`); - } - - return value!; - } - - private _expandMacroVariable(variableName: string): string { - switch (variableName) { - case '%RUSH_VERSION%': - return Rush.version; - default: - throw new InternalError(`The template references an undefined variable "${variableName}"`); + // The "DEMO" sections are uncommented only when "--rush-example-repo" is specified. + await copyTemplateFileAsync( + sourcePath, + destinationPath, + this._overwriteParameter.value, + !this._rushExampleParameter.value + ); } } } diff --git a/libraries/rush-lib/src/cli/actions/InitAutoinstallerAction.ts b/libraries/rush-lib/src/cli/actions/InitAutoinstallerAction.ts index 83a30f7996d..aebc4408103 100644 --- a/libraries/rush-lib/src/cli/actions/InitAutoinstallerAction.ts +++ b/libraries/rush-lib/src/cli/actions/InitAutoinstallerAction.ts @@ -1,17 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; - -import { CommandLineStringParameter } from '@rushstack/ts-command-line'; -import { FileSystem, NewlineKind, IPackageJson, JsonFile } from '@rushstack/node-core-library'; +import type { IRequiredCommandLineStringParameter } from '@rushstack/ts-command-line'; +import { FileSystem, NewlineKind, type IPackageJson, JsonFile } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; import { BaseRushAction } from './BaseRushAction'; -import { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; import { Autoinstaller } from '../../logic/Autoinstaller'; export class InitAutoinstallerAction extends BaseRushAction { - private readonly _name: CommandLineStringParameter; + private readonly _name: IRequiredCommandLineStringParameter; public constructor(parser: RushCommandLineParser) { super({ @@ -34,7 +33,7 @@ export class InitAutoinstallerAction extends BaseRushAction { } protected async runAsync(): Promise { - const autoinstallerName: string = this._name.value!; + const autoinstallerName: string = this._name.value; const autoinstaller: Autoinstaller = new Autoinstaller({ autoinstallerName, @@ -56,13 +55,15 @@ export class InitAutoinstallerAction extends BaseRushAction { dependencies: {} }; - console.log(colors.green('Creating package: ') + autoinstaller.packageJsonPath); + // eslint-disable-next-line no-console + console.log(Colorize.green('Creating package: ') + autoinstaller.packageJsonPath); JsonFile.save(packageJson, autoinstaller.packageJsonPath, { ensureFolderExists: true, newlineConversion: NewlineKind.OsDefault }); + // eslint-disable-next-line no-console console.log('\nFile successfully written. Add your dependencies before committing.'); } } diff --git a/libraries/rush-lib/src/cli/actions/InitDeployAction.ts b/libraries/rush-lib/src/cli/actions/InitDeployAction.ts index 7143523c6e0..6f7d996d7e7 100644 --- a/libraries/rush-lib/src/cli/actions/InitDeployAction.ts +++ b/libraries/rush-lib/src/cli/actions/InitDeployAction.ts @@ -1,19 +1,24 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import { BaseRushAction } from './BaseRushAction'; -import { RushCommandLineParser } from '../RushCommandLineParser'; -import { CommandLineStringParameter } from '@rushstack/ts-command-line'; import { FileSystem, NewlineKind } from '@rushstack/node-core-library'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { + CommandLineStringParameter, + IRequiredCommandLineStringParameter +} from '@rushstack/ts-command-line'; +import { Colorize } from '@rushstack/terminal'; + +import { BaseRushAction } from './BaseRushAction'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import { DeployScenarioConfiguration } from '../../logic/deploy/DeployScenarioConfiguration'; import { assetsFolderPath } from '../../utilities/PathConstants'; +import { RushConstants } from '../../logic/RushConstants'; const CONFIG_TEMPLATE_PATH: string = `${assetsFolderPath}/rush-init-deploy/scenario-template.json`; export class InitDeployAction extends BaseRushAction { - private readonly _project: CommandLineStringParameter; + private readonly _project: IRequiredCommandLineStringParameter; private readonly _scenario: CommandLineStringParameter; public constructor(parser: RushCommandLineParser) { @@ -62,13 +67,16 @@ export class InitDeployAction extends BaseRushAction { ); } - console.log(colors.green('Creating scenario file: ') + scenarioFilePath); + // eslint-disable-next-line no-console + console.log(Colorize.green('Creating scenario file: ') + scenarioFilePath); - const shortProjectName: string = this._project.value!; + const shortProjectName: string = this._project.value; const rushProject: RushConfigurationProject | undefined = this.rushConfiguration.findProjectByShorthandName(shortProjectName); if (!rushProject) { - throw new Error(`The specified project was not found in rush.json: "${shortProjectName}"`); + throw new Error( + `The specified project was not found in ${RushConstants.rushJsonFilename}: "${shortProjectName}"` + ); } const templateContent: string = FileSystem.readFile(CONFIG_TEMPLATE_PATH); @@ -82,6 +90,7 @@ export class InitDeployAction extends BaseRushAction { convertLineEndings: NewlineKind.OsDefault }); + // eslint-disable-next-line no-console console.log('\nFile successfully written. Please review the file contents before committing.'); } } diff --git a/libraries/rush-lib/src/cli/actions/InitSubspaceAction.ts b/libraries/rush-lib/src/cli/actions/InitSubspaceAction.ts new file mode 100644 index 00000000000..6659ae9ac81 --- /dev/null +++ b/libraries/rush-lib/src/cli/actions/InitSubspaceAction.ts @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IRequiredCommandLineStringParameter } from '@rushstack/ts-command-line'; +import { Async, FileSystem, JsonFile } from '@rushstack/node-core-library'; +import { ConsoleTerminalProvider, Terminal } from '@rushstack/terminal'; + +import { assetsFolderPath } from '../../utilities/PathConstants'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; +import { BaseRushAction } from './BaseRushAction'; +import { type ISubspacesConfigurationJson, SubspacesConfiguration } from '../../api/SubspacesConfiguration'; +import { copyTemplateFileAsync } from '../../utilities/templateUtilities'; + +export class InitSubspaceAction extends BaseRushAction { + private readonly _subspaceNameParameter: IRequiredCommandLineStringParameter; + + public constructor(parser: RushCommandLineParser) { + super({ + actionName: 'init-subspace', + summary: 'Create a new subspace.', + documentation: + 'Use this command to create a new subspace with the default subspace configuration files.', + parser + }); + + this._subspaceNameParameter = this.defineStringParameter({ + parameterLongName: '--name', + parameterShortName: '-n', + argumentName: 'SUBSPACE_NAME', + description: 'The name of the subspace that is being initialized.', + required: true + }); + } + + protected async runAsync(): Promise { + const terminal: Terminal = new Terminal(new ConsoleTerminalProvider()); + + if (!this.rushConfiguration.subspacesFeatureEnabled) { + throw new Error('Unable to create a subspace because the subspaces feature is not enabled.'); + } + + const subspacesConfiguration: SubspacesConfiguration = this.rushConfiguration + .subspacesConfiguration as SubspacesConfiguration; + // Verify this subspace name does not already exist + const existingSubspaceNames: ReadonlySet = subspacesConfiguration.subspaceNames; + const newSubspaceName: string = this._subspaceNameParameter.value; + if (existingSubspaceNames.has(newSubspaceName)) { + throw new Error( + `The subspace name: ${this._subspaceNameParameter.value} already exists in the subspace.json file.` + ); + } + if ( + SubspacesConfiguration.explainIfInvalidSubspaceName( + newSubspaceName, + this.rushConfiguration.subspacesConfiguration?.splitWorkspaceCompatibility + ) + ) { + return; + } + + const subspaceConfigPath: string = `${this.rushConfiguration.commonFolder}/config/subspaces/${newSubspaceName}`; + const assetsSubfolder: string = `${assetsFolderPath}/rush-init`; + const templateFilePaths: string[] = [ + '[dot]npmrc', + '.pnpmfile.cjs', + 'common-versions.json', + 'pnpm-config.json' + ]; + + await FileSystem.ensureEmptyFolderAsync(subspaceConfigPath); + await Async.forEachAsync( + templateFilePaths, + async (templateFilePath) => { + const sourcePath: string = `${assetsSubfolder}/common/config/rush/${templateFilePath}`; + const destinationPath: string = `${subspaceConfigPath}/${templateFilePath.replace('[dot]', '.')}`; + await copyTemplateFileAsync(sourcePath, destinationPath, true); + }, + { concurrency: 10 } + ); + + // Add the subspace name to subspaces.json + const subspaceJson: ISubspacesConfigurationJson = await JsonFile.loadAsync( + subspacesConfiguration.subspaceJsonFilePath + ); + subspaceJson.subspaceNames.push(newSubspaceName); + await JsonFile.saveAsync(subspaceJson, subspacesConfiguration.subspaceJsonFilePath, { + updateExistingFile: true + }); + + terminal.writeLine( + '\nSubspace successfully created. Please review the subspace configuration files before committing.' + ); + } +} diff --git a/libraries/rush-lib/src/cli/actions/InstallAction.ts b/libraries/rush-lib/src/cli/actions/InstallAction.ts index 888c3901083..ebc828c33b1 100644 --- a/libraries/rush-lib/src/cli/actions/InstallAction.ts +++ b/libraries/rush-lib/src/cli/actions/InstallAction.ts @@ -1,16 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { CommandLineFlagParameter } from '@rushstack/ts-command-line'; -import { ConsoleTerminalProvider, Terminal } from '@rushstack/node-core-library'; +import type { CommandLineFlagParameter } from '@rushstack/ts-command-line'; import { BaseInstallAction } from './BaseInstallAction'; import type { IInstallManagerOptions } from '../../logic/base/BaseInstallManagerTypes'; -import { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; import { SelectionParameterSet } from '../parsing/SelectionParameterSet'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { Subspace } from '../../api/Subspace'; +import { getVariantAsync } from '../../api/Variants'; export class InstallAction extends BaseInstallAction { private readonly _checkOnlyParameter: CommandLineFlagParameter; + private readonly _resolutionOnlyParameter: CommandLineFlagParameter | undefined; public constructor(parser: RushCommandLineParser) { super({ @@ -31,21 +34,41 @@ export class InstallAction extends BaseInstallAction { }); this._selectionParameters = new SelectionParameterSet(this.rushConfiguration, this, { - // Include lockfile processing since this expands the selection, and we need to select - // at least the same projects selected with the same query to "rush build" - includeExternalDependencies: true, - // Disable filtering because rush-project.json is riggable and therefore may not be available - enableFiltering: false + gitOptions: { + // Include lockfile processing since this expands the selection, and we need to select + // at least the same projects selected with the same query to "rush build" + includeExternalDependencies: true, + // Disable filtering because rush-project.json is riggable and therefore may not be available + enableFiltering: false + }, + includeSubspaceSelector: true, + cwd: this.parser.cwd }); this._checkOnlyParameter = this.defineFlagParameter({ parameterLongName: '--check-only', description: `Only check the validity of the shrinkwrap file without performing an install.` }); + + if (this.rushConfiguration?.isPnpm) { + this._resolutionOnlyParameter = this.defineFlagParameter({ + parameterLongName: '--resolution-only', + description: `Only perform dependency resolution, useful for ensuring peer dependendencies are up to date. Note that this flag is only supported when using the pnpm package manager.` + }); + } } - protected async buildInstallOptionsAsync(): Promise { - const terminal: Terminal = new Terminal(new ConsoleTerminalProvider()); + protected async buildInstallOptionsAsync(): Promise> { + const selectedProjects: Set = + (await this._selectionParameters?.getSelectedProjectsAsync(this.terminal)) ?? + new Set(this.rushConfiguration.projects); + + const variant: string | undefined = await getVariantAsync( + this._variantParameter, + this.rushConfiguration, + false + ); + return { debug: this.parser.isDebug, allowShrinkwrapUpdates: false, @@ -54,17 +77,24 @@ export class InstallAction extends BaseInstallAction { noLink: this._noLinkParameter.value!, fullUpgrade: false, recheckShrinkwrap: false, + offline: this._offlineParameter.value!, networkConcurrency: this._networkConcurrencyParameter.value, collectLogFile: this._debugPackageManagerParameter.value!, - variant: this._variant.value, + variant, // Because the 'defaultValue' option on the _maxInstallAttempts parameter is set, // it is safe to assume that the value is not null maxInstallAttempts: this._maxInstallAttempts.value!, // These are derived independently of the selection for command line brevity - pnpmFilterArguments: await this._selectionParameters!.getPnpmFilterArgumentsAsync(terminal), + selectedProjects, + pnpmFilterArgumentValues: + (await this._selectionParameters?.getPnpmFilterArgumentValuesAsync(this.terminal)) ?? [], checkOnly: this._checkOnlyParameter.value, - - beforeInstallAsync: () => this.rushSession.hooks.beforeInstall.promise(this) + resolutionOnly: this._resolutionOnlyParameter?.value, + beforeInstallAsync: (subspace: Subspace) => + this.rushSession.hooks.beforeInstall.promise(this, subspace, variant), + afterInstallAsync: (subspace: Subspace) => + this.rushSession.hooks.afterInstall.promise(this, subspace, variant), + terminal: this.terminal }; } } diff --git a/libraries/rush-lib/src/cli/actions/InstallAutoinstallerAction.ts b/libraries/rush-lib/src/cli/actions/InstallAutoinstallerAction.ts new file mode 100644 index 00000000000..b8445383d75 --- /dev/null +++ b/libraries/rush-lib/src/cli/actions/InstallAutoinstallerAction.ts @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { Autoinstaller } from '../../logic/Autoinstaller'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; +import { BaseAutoinstallerAction } from './BaseAutoinstallerAction'; + +export class InstallAutoinstallerAction extends BaseAutoinstallerAction { + public constructor(parser: RushCommandLineParser) { + super({ + actionName: 'install-autoinstaller', + summary: 'Install autoinstaller package dependencies', + documentation: 'Use this command to install dependencies for an autoinstaller folder.', + parser + }); + } + + protected async prepareAsync(autoinstaller: Autoinstaller): Promise { + await autoinstaller.prepareAsync(); + } +} diff --git a/libraries/rush-lib/src/cli/actions/LinkAction.ts b/libraries/rush-lib/src/cli/actions/LinkAction.ts index 31559aa9d5e..eabf19c414c 100644 --- a/libraries/rush-lib/src/cli/actions/LinkAction.ts +++ b/libraries/rush-lib/src/cli/actions/LinkAction.ts @@ -1,11 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { CommandLineFlagParameter } from '@rushstack/ts-command-line'; +import type { CommandLineFlagParameter } from '@rushstack/ts-command-line'; -import { RushCommandLineParser } from '../RushCommandLineParser'; - -import { BaseLinkManager } from '../../logic/base/BaseLinkManager'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; +import type { BaseLinkManager } from '../../logic/base/BaseLinkManager'; import { BaseRushAction } from './BaseRushAction'; export class LinkAction extends BaseRushAction { @@ -40,6 +39,6 @@ export class LinkAction extends BaseRushAction { const linkManager: BaseLinkManager = linkManagerFactoryModule.LinkManagerFactory.getLinkManager( this.rushConfiguration ); - await linkManager.createSymlinksForProjects(this._force.value); + await linkManager.createSymlinksForProjectsAsync(this._force.value); } } diff --git a/libraries/rush-lib/src/cli/actions/LinkPackageAction.ts b/libraries/rush-lib/src/cli/actions/LinkPackageAction.ts new file mode 100644 index 00000000000..1d8352811f8 --- /dev/null +++ b/libraries/rush-lib/src/cli/actions/LinkPackageAction.ts @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { Async } from '@rushstack/node-core-library'; +import type { CommandLineStringListParameter } from '@rushstack/ts-command-line'; + +import type { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import { BaseHotlinkPackageAction } from './BaseHotlinkPackageAction'; +import type { HotlinkManager } from '../../utilities/HotlinkManager'; +import { BRIDGE_PACKAGE_ACTION_NAME, LINK_PACKAGE_ACTION_NAME } from '../../utilities/actionNameConstants'; +import { RushConstants } from '../../logic/RushConstants'; + +export class LinkPackageAction extends BaseHotlinkPackageAction { + protected readonly _projectListParameter: CommandLineStringListParameter; + + public constructor(parser: RushCommandLineParser) { + super({ + actionName: LINK_PACKAGE_ACTION_NAME, + summary: + '(EXPERIMENTAL) Use hotlinks to simulate installation of a locally built project folder as a dependency' + + ' of specific projects.', + documentation: + 'This command enables you to test a locally built project by creating a symlink under the specified' + + ' projects\' node_modules folders. The implementation is similar to "pnpm link" and "npm link", but' + + ' better integrated with Rush features. Like those commands, the symlink ("hotlink") is not reflected' + + ' in pnpm-lock.yaml, affects the consuming project only, and has the same limitations as "workspace:*".' + + ' The hotlinks will be cleared when you next run "rush install" or "rush update".' + + ` Compare with the "rush ${BRIDGE_PACKAGE_ACTION_NAME}" command, which affects the entire lockfile` + + ' including indirect dependencies.', + parser + }); + + this._projectListParameter = this.defineStringListParameter({ + parameterLongName: '--project', + argumentName: 'PROJECT_NAME', + required: false, + description: + 'A list of Rush project names that will be hotlinked to the "--path" folder. ' + + 'If not specified, the default is the project of the current working directory.' + }); + } + + private async _getProjectsToLinkAsync(): Promise> { + const projectsToLink: Set = new Set(); + const projectNames: readonly string[] = this._projectListParameter.values; + + if (projectNames.length > 0) { + for (const projectName of projectNames) { + const project: RushConfigurationProject | undefined = + this.rushConfiguration.getProjectByName(projectName); + if (!project) { + throw new Error(`The project "${projectName}" was not found in "${RushConstants.rushPackageName}"`); + } + projectsToLink.add(project); + } + } else { + const currentProject: RushConfigurationProject | undefined = + this.rushConfiguration.tryGetProjectForPath(process.cwd()); + if (!currentProject) { + throw new Error(`No Rush project was found in the current working directory`); + } + projectsToLink.add(currentProject); + } + + return projectsToLink; + } + + protected async hotlinkPackageAsync( + linkedPackagePath: string, + hotlinkManager: HotlinkManager + ): Promise { + const projectsToLink: Set = await this._getProjectsToLinkAsync(); + await Async.forEachAsync( + projectsToLink, + async (project) => { + await hotlinkManager.linkPackageAsync(this.terminal, project, linkedPackagePath); + }, + { concurrency: 5 } + ); + } +} diff --git a/libraries/rush-lib/src/cli/actions/ListAction.ts b/libraries/rush-lib/src/cli/actions/ListAction.ts index ec2eaf3851e..76583015535 100644 --- a/libraries/rush-lib/src/cli/actions/ListAction.ts +++ b/libraries/rush-lib/src/cli/actions/ListAction.ts @@ -1,12 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ConsoleTerminalProvider, Sort, Terminal } from '@rushstack/node-core-library'; -import { CommandLineFlagParameter } from '@rushstack/ts-command-line'; +import { Sort } from '@rushstack/node-core-library'; +import { ConsoleTerminalProvider, Terminal, TerminalTable } from '@rushstack/terminal'; +import type { CommandLineFlagParameter } from '@rushstack/ts-command-line'; import { BaseRushAction } from './BaseRushAction'; -import { RushCommandLineParser } from '../RushCommandLineParser'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import { VersionPolicyDefinitionName } from '../../api/VersionPolicy'; import { SelectionParameterSet } from '../parsing/SelectionParameterSet'; @@ -44,6 +45,10 @@ export interface IJsonEntry { * @see {@link ../../api/RushConfigurationProject#RushConfigurationProject.tags | RushConfigurationProject.tags} */ tags: string[]; + /** + * @see {@link ../../api/Subspace#Subspace.subspaceName | Subspace.subspaceName} + */ + subspaceName: string | undefined; } export interface IJsonOutput { @@ -108,19 +113,22 @@ export class ListAction extends BaseRushAction { }); this._selectionParameters = new SelectionParameterSet(this.rushConfiguration, this, { - // Include lockfile processing since this expands the selection, and we need to select - // at least the same projects selected with the same query to "rush build" - includeExternalDependencies: true, - // Disable filtering because rush-project.json is riggable and therefore may not be available - enableFiltering: false + gitOptions: { + // Include lockfile processing since this expands the selection, and we need to select + // at least the same projects selected with the same query to "rush build" + includeExternalDependencies: true, + // Disable filtering because rush-project.json is riggable and therefore may not be available + enableFiltering: false + }, + includeSubspaceSelector: false, + cwd: this.parser.cwd }); } protected async runAsync(): Promise { const terminal: Terminal = new Terminal(new ConsoleTerminalProvider()); - const selection: Set = await this._selectionParameters.getSelectedProjectsAsync( - terminal - ); + const selection: Set = + await this._selectionParameters.getSelectedProjectsAsync(terminal); Sort.sortSetBy(selection, (x: RushConfigurationProject) => x.packageName); if (this._jsonFlag.value && this._detailedFlag.value) { throw new Error(`The parameters "--json" and "--detailed" cannot be used together.`); @@ -141,6 +149,7 @@ export class ListAction extends BaseRushAction { let shouldPublish: undefined | boolean; let versionPolicy: undefined | string; let versionPolicyName: undefined | string; + let subspaceName: undefined | string; if (config.versionPolicy !== undefined) { const definitionName: string = VersionPolicyDefinitionName[config.versionPolicy.definitionName]; versionPolicy = `${definitionName}`; @@ -153,6 +162,10 @@ export class ListAction extends BaseRushAction { reviewCategory = config.reviewCategory; } + if (this.rushConfiguration.subspacesFeatureEnabled) { + subspaceName = config.subspace.subspaceName; + } + return { name: config.packageName, version: config.packageJson.version, @@ -162,24 +175,31 @@ export class ListAction extends BaseRushAction { versionPolicyName, shouldPublish, reviewCategory, - tags: Array.from(config.tags) + tags: Array.from(config.tags), + subspaceName }; }); const output: IJsonOutput = { projects }; + // eslint-disable-next-line no-console console.log(JSON.stringify(output, undefined, 2)); } private _printList(selection: Set): void { for (const project of selection) { + // eslint-disable-next-line no-console console.log(project.packageName); } } private async _printListTableAsync(selection: Set): Promise { const tableHeader: string[] = ['Project']; + if (this.rushConfiguration.subspacesFeatureEnabled) { + tableHeader.push('Subspace'); + } + if (this._version.value || this._detailedFlag.value) { tableHeader.push('Version'); } @@ -200,8 +220,7 @@ export class ListAction extends BaseRushAction { tableHeader.push('Tags'); } - const { default: CliTable } = await import('cli-table'); - const table: import('cli-table') = new CliTable({ + const table: TerminalTable = new TerminalTable({ head: tableHeader }); @@ -213,6 +232,10 @@ export class ListAction extends BaseRushAction { appendToPackageRow(project.packageName); + if (this.rushConfiguration.subspacesFeatureEnabled) { + appendToPackageRow(project.subspace.subspaceName); + } + if (this._version.value || this._detailedFlag.value) { appendToPackageRow(project.packageJson.version); } @@ -254,6 +277,6 @@ export class ListAction extends BaseRushAction { table.push(packageRow); } - console.log(table.toString()); + table.printToTerminal(this.terminal); } } diff --git a/libraries/rush-lib/src/cli/actions/PublishAction.ts b/libraries/rush-lib/src/cli/actions/PublishAction.ts index 496e41e35f5..4363f378cfe 100644 --- a/libraries/rush-lib/src/cli/actions/PublishAction.ts +++ b/libraries/rush-lib/src/cli/actions/PublishAction.ts @@ -1,20 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; +import * as path from 'node:path'; + import * as semver from 'semver'; -import { + +import type { CommandLineFlagParameter, CommandLineStringParameter, CommandLineChoiceParameter } from '@rushstack/ts-command-line'; -import { FileSystem } from '@rushstack/node-core-library'; +import { Async, FileSystem } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; -import { IChangeInfo, ChangeType } from '../../api/ChangeManagement'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import { type IChangeInfo, ChangeType } from '../../api/ChangeManagement'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import { Npm } from '../../utilities/Npm'; -import { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; import { PublishUtilities } from '../../logic/PublishUtilities'; import { ChangelogGenerator } from '../../logic/ChangelogGenerator'; import { PrereleaseToken } from '../../logic/PrereleaseToken'; @@ -22,10 +24,12 @@ import { ChangeManager } from '../../logic/ChangeManager'; import { BaseRushAction } from './BaseRushAction'; import { PublishGit } from '../../logic/PublishGit'; import * as PolicyValidator from '../../logic/policy/PolicyValidator'; -import { VersionPolicy } from '../../api/VersionPolicy'; import { DEFAULT_PACKAGE_UPDATE_MESSAGE } from './VersionAction'; import { Utilities } from '../../utilities/Utilities'; import { Git } from '../../logic/Git'; +import { RushConstants } from '../../logic/RushConstants'; +import { IS_WINDOWS } from '../../utilities/executionUtilities'; +import type { RushConfiguration } from '../../api/RushConfiguration'; export class PublishAction extends BaseRushAction { private readonly _addCommitDetails: CommandLineFlagParameter; @@ -154,7 +158,7 @@ export class PublishAction extends BaseRushAction { parameterLongName: '--include-all', parameterShortName: undefined, description: - 'If this flag is specified, all packages with shouldPublish=true in rush.json ' + + `If this flag is specified, all packages with shouldPublish=true in ${RushConstants.rushJsonFilename} ` + 'or with a specified version policy ' + 'will be published if their version is newer than published version.' }); @@ -211,7 +215,14 @@ export class PublishAction extends BaseRushAction { * Executes the publish action, which will read change request files, apply changes to package.jsons, */ protected async runAsync(): Promise { - await PolicyValidator.validatePolicyAsync(this.rushConfiguration, { bypassPolicy: false }); + const currentlyInstalledVariant: string | undefined = + await this.rushConfiguration.getCurrentlyInstalledVariantAsync(); + await PolicyValidator.validatePolicyAsync( + this.rushConfiguration, + this.rushConfiguration.defaultSubspace, + currentlyInstalledVariant, + { bypassPolicy: false } + ); // Example: "common\temp\publish-home" this._targetNpmrcPublishFolder = path.join(this.rushConfiguration.commonTempFolder, 'publish-home'); @@ -219,9 +230,10 @@ export class PublishAction extends BaseRushAction { // Example: "common\temp\publish-home\.npmrc" this._targetNpmrcPublishPath = path.join(this._targetNpmrcPublishFolder, '.npmrc'); - const allPackages: Map = this.rushConfiguration.projectsByName; + const allPackages: ReadonlyMap = this.rushConfiguration.projectsByName; if (this._regenerateChangelogs.value) { + // eslint-disable-next-line no-console console.log('Regenerating changelogs'); ChangelogGenerator.regenerateChangelogs(allPackages, this.rushConfiguration); return; @@ -229,12 +241,12 @@ export class PublishAction extends BaseRushAction { this._validate(); - this._addNpmPublishHome(); + this._addNpmPublishHome(this.rushConfiguration.isPnpm); const git: Git = new Git(this.rushConfiguration); const publishGit: PublishGit = new PublishGit(git, this._targetBranch.value); if (this._includeAll.value) { - this._publishAll(publishGit, allPackages); + await this._publishAllAsync(publishGit, allPackages); } else { this._prereleaseToken = new PrereleaseToken( this._prereleaseName.value, @@ -244,7 +256,8 @@ export class PublishAction extends BaseRushAction { await this._publishChangesAsync(git, publishGit, allPackages); } - console.log('\n' + colors.green('Rush publish finished successfully.')); + // eslint-disable-next-line no-console + console.log('\n' + Colorize.green('Rush publish finished successfully.')); } /** @@ -265,40 +278,36 @@ export class PublishAction extends BaseRushAction { private async _publishChangesAsync( git: Git, publishGit: PublishGit, - allPackages: Map + allPackages: ReadonlyMap ): Promise { const changeManager: ChangeManager = new ChangeManager(this.rushConfiguration); - await changeManager.loadAsync( - this.rushConfiguration.changesFolder, - this._prereleaseToken, - this._addCommitDetails.value - ); + await changeManager.loadAsync(this._prereleaseToken, this._addCommitDetails.value); if (changeManager.hasChanges()) { const orderedChanges: IChangeInfo[] = changeManager.packageChanges; const tempBranchName: string = `publish-${Date.now()}`; // Make changes in temp branch. - publishGit.checkout(tempBranchName, true); + await publishGit.checkoutAsync(tempBranchName, true); - this._setDependenciesBeforePublish(); + await this._setDependenciesBeforePublishAsync(); // Make changes to package.json and change logs. changeManager.apply(this._apply.value); - await changeManager.updateChangelogAsync(this._apply.value); + await changeManager.updateChangelogAsync(this.terminal, this._apply.value); - this._setDependenciesBeforeCommit(); + await this._setDependenciesBeforeCommitAsync(); - if (git.hasUncommittedChanges()) { + if (await git.hasUncommittedChangesAsync()) { // Stage, commit, and push the changes to remote temp branch. - publishGit.addChanges(':/*'); - publishGit.commit( + await publishGit.addChangesAsync(':/*'); + await publishGit.commitAsync( this.rushConfiguration.gitVersionBumpCommitMessage || DEFAULT_PACKAGE_UPDATE_MESSAGE, !this._ignoreGitHooksParameter.value ); - publishGit.push(tempBranchName, !this._ignoreGitHooksParameter.value); + await publishGit.pushAsync(tempBranchName, !this._ignoreGitHooksParameter.value); - this._setDependenciesBeforePublish(); + await this._setDependenciesBeforePublishAsync(); // Override tag parameter if there is a hotfix change. for (const change of orderedChanges) { @@ -313,47 +322,53 @@ export class PublishAction extends BaseRushAction { if (change.changeType && change.changeType > ChangeType.dependency) { const project: RushConfigurationProject | undefined = allPackages.get(change.packageName); if (project) { - if (!this._packageExists(project)) { - this._npmPublish(change.packageName, project.publishFolder); + if (!(await this._packageExistsAsync(project))) { + await this._npmPublishAsync(change.packageName, project.publishFolder); } else { + // eslint-disable-next-line no-console console.log(`Skip ${change.packageName}. Package exists.`); } } else { + // eslint-disable-next-line no-console console.log(`Skip ${change.packageName}. Failed to find its project.`); } } } - this._setDependenciesBeforeCommit(); + await this._setDependenciesBeforeCommitAsync(); // Create and push appropriate Git tags. - this._gitAddTags(publishGit, orderedChanges); - publishGit.push(tempBranchName, !this._ignoreGitHooksParameter.value); + await this._gitAddTagsAsync(publishGit, orderedChanges); + await publishGit.pushAsync(tempBranchName, !this._ignoreGitHooksParameter.value); // Now merge to target branch. - publishGit.checkout(this._targetBranch.value!); - publishGit.pull(!this._ignoreGitHooksParameter.value); - publishGit.merge(tempBranchName, !this._ignoreGitHooksParameter.value); - publishGit.push(this._targetBranch.value!, !this._ignoreGitHooksParameter.value); - publishGit.deleteBranch(tempBranchName, true, !this._ignoreGitHooksParameter.value); + await publishGit.checkoutAsync(this._targetBranch.value!); + await publishGit.pullAsync(!this._ignoreGitHooksParameter.value); + await publishGit.mergeAsync(tempBranchName, !this._ignoreGitHooksParameter.value); + await publishGit.pushAsync(this._targetBranch.value!, !this._ignoreGitHooksParameter.value); + await publishGit.deleteBranchAsync(tempBranchName, true, !this._ignoreGitHooksParameter.value); } else { - publishGit.checkout(this._targetBranch.value!); - publishGit.deleteBranch(tempBranchName, false, !this._ignoreGitHooksParameter.value); + await publishGit.checkoutAsync(this._targetBranch.value!); + await publishGit.deleteBranchAsync(tempBranchName, false, !this._ignoreGitHooksParameter.value); } } } - private _publishAll(git: PublishGit, allPackages: Map): void { + private async _publishAllAsync( + git: PublishGit, + allPackages: ReadonlyMap + ): Promise { + // eslint-disable-next-line no-console console.log(`Rush publish starts with includeAll and version policy ${this._versionPolicy.value}`); let updated: boolean = false; - allPackages.forEach((packageConfig, packageName) => { + for (const [packageName, packageConfig] of allPackages) { if ( packageConfig.shouldPublish && (!this._versionPolicy.value || this._versionPolicy.value === packageConfig.versionPolicyName) ) { - const applyTag: (apply: boolean) => void = (apply: boolean): void => { + const applyTagAsync: (apply: boolean) => Promise = async (apply: boolean): Promise => { if (!apply) { return; } @@ -361,14 +376,15 @@ export class PublishAction extends BaseRushAction { const packageVersion: string = packageConfig.packageJson.version; // Do not create a new tag if one already exists, this will result in a fatal error - if (git.hasTag(packageConfig)) { + if (await git.hasTagAsync(packageConfig)) { + // eslint-disable-next-line no-console console.log( `Not tagging ${packageName}@${packageVersion}. A tag already exists for this version.` ); return; } - git.addTag( + await git.addTagAsync( !!this._publish.value, packageName, packageVersion, @@ -380,31 +396,32 @@ export class PublishAction extends BaseRushAction { if (this._pack.value) { // packs to tarball instead of publishing to NPM repository - this._npmPack(packageName, packageConfig); - applyTag(this._applyGitTagsOnPack.value); - } else if (this._force.value || !this._packageExists(packageConfig)) { + await this._npmPackAsync(packageName, packageConfig); + await applyTagAsync(this._applyGitTagsOnPack.value); + } else if (this._force.value || !(await this._packageExistsAsync(packageConfig))) { // Publish to npm repository - this._npmPublish(packageName, packageConfig.publishFolder); - applyTag(true); + await this._npmPublishAsync(packageName, packageConfig.publishFolder); + await applyTagAsync(true); } else { + // eslint-disable-next-line no-console console.log(`Skip ${packageName}. Not updated.`); } } - }); + } if (updated) { - git.push(this._targetBranch.value!, !this._ignoreGitHooksParameter.value); + await git.pushAsync(this._targetBranch.value!, !this._ignoreGitHooksParameter.value); } } - private _gitAddTags(git: PublishGit, orderedChanges: IChangeInfo[]): void { + private async _gitAddTagsAsync(git: PublishGit, orderedChanges: IChangeInfo[]): Promise { for (const change of orderedChanges) { if ( change.changeType && change.changeType > ChangeType.dependency && this.rushConfiguration.projectsByName.get(change.packageName)!.shouldPublish ) { - git.addTag( + await git.addTagAsync( !!this._publish.value && !this._registryUrl.value, change.packageName, change.newVersion!, @@ -415,7 +432,7 @@ export class PublishAction extends BaseRushAction { } } - private _npmPublish(packageName: string, packagePath: string): void { + private async _npmPublishAsync(packageName: string, packagePath: string): Promise { const env: { [key: string]: string | undefined } = PublishUtilities.getEnvArgs(); const args: string[] = ['publish']; @@ -436,7 +453,7 @@ export class PublishAction extends BaseRushAction { args.push(`--access`, this._npmAccessLevel.value); } - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { // PNPM 4.11.0 introduced a feature that may interrupt publishing and prompt the user for input. // See this issue for details: https://github.com/microsoft/rushstack/issues/1940 args.push('--no-git-checks'); @@ -453,23 +470,23 @@ export class PublishAction extends BaseRushAction { // If the auth token was specified via the command line, avoid printing it on the console const secretSubstring: string | undefined = this._npmAuthToken.value; - PublishUtilities.execCommand( - !!this._publish.value, - packageManagerToolFilename, + await PublishUtilities.execCommandAsync({ + shouldExecute: this._publish.value, + command: packageManagerToolFilename, args, - packagePath, - env, + workingDirectory: packagePath, + environment: env, secretSubstring - ); + }); } } - private _packageExists(packageConfig: RushConfigurationProject): boolean { + private async _packageExistsAsync(packageConfig: RushConfigurationProject): Promise { const env: { [key: string]: string | undefined } = PublishUtilities.getEnvArgs(); const args: string[] = []; this._addSharedNpmConfig(env, args); - const publishedVersions: string[] = Npm.publishedVersions( + const publishedVersions: string[] = await Npm.getPublishedVersionsAsync( packageConfig.packageName, packageConfig.publishFolder, env, @@ -498,17 +515,17 @@ export class PublishAction extends BaseRushAction { return publishedVersions.indexOf(normalizedVersion) >= 0; } - private _npmPack(packageName: string, project: RushConfigurationProject): void { + private async _npmPackAsync(packageName: string, project: RushConfigurationProject): Promise { const args: string[] = ['pack']; const env: { [key: string]: string | undefined } = PublishUtilities.getEnvArgs(); - PublishUtilities.execCommand( - !!this._publish.value, - this.rushConfiguration.packageManagerToolFilename, + await PublishUtilities.execCommandAsync({ + shouldExecute: this._publish.value, + command: this.rushConfiguration.packageManagerToolFilename, args, - project.publishFolder, - env - ); + workingDirectory: project.publishFolder, + environment: env + }); if (this._publish.value) { // Copy the tarball the release folder @@ -539,40 +556,47 @@ export class PublishAction extends BaseRushAction { } } - private _setDependenciesBeforePublish(): void { - for (const project of this.rushConfiguration.projects) { - if (!this._versionPolicy.value || this._versionPolicy.value === project.versionPolicyName) { - const versionPolicy: VersionPolicy | undefined = project.versionPolicy; - - if (versionPolicy) { - versionPolicy.setDependenciesBeforePublish(project.packageName, this.rushConfiguration); + private async _setDependenciesBeforePublishAsync(): Promise { + const rushConfiguration: RushConfiguration = this.rushConfiguration; + await Async.forEachAsync( + rushConfiguration.projects, + async ({ versionPolicy, versionPolicyName, packageName }) => { + if (!this._versionPolicy.value || this._versionPolicy.value === versionPolicyName) { + await versionPolicy?.setDependenciesBeforePublishAsync(packageName, rushConfiguration); } - } - } + }, + { concurrency: 10 } + ); } - private _setDependenciesBeforeCommit(): void { - for (const project of this.rushConfiguration.projects) { - if (!this._versionPolicy.value || this._versionPolicy.value === project.versionPolicyName) { - const versionPolicy: VersionPolicy | undefined = project.versionPolicy; - - if (versionPolicy) { - versionPolicy.setDependenciesBeforePublish(project.packageName, this.rushConfiguration); + private async _setDependenciesBeforeCommitAsync(): Promise { + const rushConfiguration: RushConfiguration = this.rushConfiguration; + await Async.forEachAsync( + rushConfiguration.projects, + async ({ versionPolicy, versionPolicyName, packageName }) => { + if (!this._versionPolicy.value || this._versionPolicy.value === versionPolicyName) { + await versionPolicy?.setDependenciesBeforeCommitAsync(packageName, rushConfiguration); } - } - } + }, + { concurrency: 10 } + ); } - private _addNpmPublishHome(): void { + private _addNpmPublishHome(supportEnvVarFallbackSyntax: boolean): void { // Create "common\temp\publish-home" folder, if it doesn't exist Utilities.createFolderWithRetry(this._targetNpmrcPublishFolder); // Copy down the committed "common\config\rush\.npmrc-publish" file, if there is one - Utilities.syncNpmrc(this.rushConfiguration.commonRushConfigFolder, this._targetNpmrcPublishFolder, true); + Utilities.syncNpmrc({ + sourceNpmrcFolder: this.rushConfiguration.commonRushConfigFolder, + targetNpmrcFolder: this._targetNpmrcPublishFolder, + useNpmrcPublish: true, + supportEnvVarFallbackSyntax + }); } private _addSharedNpmConfig(env: { [key: string]: string | undefined }, args: string[]): void { - const userHomeEnvVariable: string = process.platform === 'win32' ? 'USERPROFILE' : 'HOME'; + const userHomeEnvVariable: string = IS_WINDOWS ? 'USERPROFILE' : 'HOME'; let registry: string = '//registry.npmjs.org/'; // Check if .npmrc file exists in "common\temp\publish-home" diff --git a/libraries/rush-lib/src/cli/actions/PurgeAction.ts b/libraries/rush-lib/src/cli/actions/PurgeAction.ts index da1c8544931..3f8e0d223ff 100644 --- a/libraries/rush-lib/src/cli/actions/PurgeAction.ts +++ b/libraries/rush-lib/src/cli/actions/PurgeAction.ts @@ -1,22 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; - -import { CommandLineFlagParameter } from '@rushstack/ts-command-line'; +import type { CommandLineFlagParameter } from '@rushstack/ts-command-line'; +import { Colorize } from '@rushstack/terminal'; import { BaseRushAction } from './BaseRushAction'; -import { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; import { Stopwatch } from '../../utilities/Stopwatch'; import { PurgeManager } from '../../logic/PurgeManager'; import { UnlinkManager } from '../../logic/UnlinkManager'; +import { PURGE_ACTION_NAME } from '../../utilities/actionNameConstants'; export class PurgeAction extends BaseRushAction { private readonly _unsafeParameter: CommandLineFlagParameter; public constructor(parser: RushCommandLineParser) { super({ - actionName: 'purge', + actionName: PURGE_ACTION_NAME, summary: 'For diagnostic purposes, use this command to delete caches and other temporary files used by Rush', documentation: @@ -40,7 +40,7 @@ export class PurgeAction extends BaseRushAction { const unlinkManager: UnlinkManager = new UnlinkManager(this.rushConfiguration); const purgeManager: PurgeManager = new PurgeManager(this.rushConfiguration, this.rushGlobalFolder); - unlinkManager.unlink(/*force:*/ true); + await unlinkManager.unlinkAsync(/*force:*/ true); if (this._unsafeParameter.value!) { purgeManager.purgeUnsafe(); @@ -48,11 +48,12 @@ export class PurgeAction extends BaseRushAction { purgeManager.purgeNormal(); } - purgeManager.deleteAll(); + await purgeManager.startDeleteAllAsync(); + // eslint-disable-next-line no-console console.log( '\n' + - colors.green( + Colorize.green( `Rush purge started successfully and will complete asynchronously. (${stopwatch.toString()})` ) ); diff --git a/libraries/rush-lib/src/cli/actions/RemoveAction.ts b/libraries/rush-lib/src/cli/actions/RemoveAction.ts index 87607369111..72c17f44d55 100644 --- a/libraries/rush-lib/src/cli/actions/RemoveAction.ts +++ b/libraries/rush-lib/src/cli/actions/RemoveAction.ts @@ -1,88 +1,74 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ConsoleTerminalProvider, Terminal, ITerminal } from '@rushstack/node-core-library'; -import type { CommandLineFlagParameter, CommandLineStringListParameter } from '@rushstack/ts-command-line'; - -import { BaseAddAndRemoveAction } from './BaseAddAndRemoveAction'; -import { RushCommandLineParser } from '../RushCommandLineParser'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import { BaseAddAndRemoveAction, PACKAGE_PARAMETER_NAME } from './BaseAddAndRemoveAction'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import type { IPackageForRushRemove, IPackageJsonUpdaterRushRemoveOptions } from '../../logic/PackageJsonUpdaterTypes'; +import { getVariantAsync } from '../../api/Variants'; -export class RemoveAction extends BaseAddAndRemoveAction { - protected readonly _allFlag: CommandLineFlagParameter; - protected readonly _packageNameList: CommandLineStringListParameter; - private _terminalProvider: ConsoleTerminalProvider; - private _terminal: ITerminal; +const REMOVE_ACTION_NAME: 'remove' = 'remove'; +export class RemoveAction extends BaseAddAndRemoveAction { public constructor(parser: RushCommandLineParser) { const documentation: string = [ 'Removes specified package(s) from the dependencies of the current project (as determined by the current working directory)' + ' and then runs "rush update".' ].join('\n'); super({ - actionName: 'remove', + actionName: REMOVE_ACTION_NAME, summary: 'Removes one or more dependencies from the package.json and runs rush update.', documentation, safeForSimultaneousRushProcesses: false, - parser - }); - this._terminalProvider = new ConsoleTerminalProvider(); - this._terminal = new Terminal(this._terminalProvider); + parser, - this._packageNameList = this.defineStringListParameter({ - parameterLongName: '--package', - parameterShortName: '-p', - required: true, - argumentName: 'PACKAGE', - description: + packageNameListParameterDescription: 'The name of the package which should be removed.' + - ' To remove multiple packages, run "rush remove --package foo --package bar".' - }); - this._allFlag = this.defineFlagParameter({ - parameterLongName: '--all', - description: 'If specified, the dependency will be removed from all projects that declare it.' + ` To remove multiple packages, run "rush ${REMOVE_ACTION_NAME} ${PACKAGE_PARAMETER_NAME} foo ${PACKAGE_PARAMETER_NAME} bar".`, + allFlagDescription: 'If specified, the dependency will be removed from all projects that declare it.' }); } - public getUpdateOptions(): IPackageJsonUpdaterRushRemoveOptions { + public async getUpdateOptionsAsync(): Promise { const projects: RushConfigurationProject[] = super.getProjects(); const packagesToRemove: IPackageForRushRemove[] = []; for (const specifiedPackageName of this.specifiedPackageNameList) { - /** - * Name - */ - const packageName: string = specifiedPackageName; - - if (!this.rushConfiguration.packageNameParser.isValidName(packageName)) { - throw new Error(`The package name "${packageName}" is not valid.`); + if (!this.rushConfiguration.packageNameParser.isValidName(specifiedPackageName)) { + throw new Error(`The package name "${specifiedPackageName}" is not valid.`); } for (const project of projects) { if ( - !project.packageJsonEditor.tryGetDependency(packageName) && - !project.packageJsonEditor.tryGetDevDependency(packageName) + !project.packageJsonEditor.tryGetDependency(specifiedPackageName) && + !project.packageJsonEditor.tryGetDevDependency(specifiedPackageName) ) { - this._terminal.writeLine( - `The project "${project.packageName}" do not have ${packageName} in package.json.` + this.terminal.writeLine( + `The project "${project.packageName}" does not have "${specifiedPackageName}" in package.json.` ); } } - packagesToRemove.push({ packageName }); + packagesToRemove.push({ packageName: specifiedPackageName }); } + const variant: string | undefined = await getVariantAsync( + this._variantParameter, + this.rushConfiguration, + true + ); + return { - projects: projects, + projects, packagesToUpdate: packagesToRemove, skipUpdate: this._skipUpdateFlag.value, debugInstall: this.parser.isDebug, - actionName: this.actionName + actionName: this.actionName, + variant }; } } diff --git a/libraries/rush-lib/src/cli/actions/ScanAction.ts b/libraries/rush-lib/src/cli/actions/ScanAction.ts index 3ae1305bda1..3fd04e7a12e 100644 --- a/libraries/rush-lib/src/cli/actions/ScanAction.ts +++ b/libraries/rush-lib/src/cli/actions/ScanAction.ts @@ -1,13 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; -import builtinPackageNames from 'builtin-modules'; +import * as path from 'node:path'; +import { isBuiltin as isBuiltinModule } from 'node:module'; -import { FileSystem, LegacyAdapters } from '@rushstack/node-core-library'; -import { RushCommandLineParser } from '../RushCommandLineParser'; -import { CommandLineFlagParameter } from '@rushstack/ts-command-line'; +import { Colorize } from '@rushstack/terminal'; +import type { CommandLineFlagParameter } from '@rushstack/ts-command-line'; +import { FileSystem } from '@rushstack/node-core-library'; + +import type { RushCommandLineParser } from '../RushCommandLineParser'; import { BaseConfiglessRushAction } from './BaseRushAction'; export interface IJsonOutput { @@ -67,20 +68,24 @@ export class ScanAction extends BaseConfiglessRushAction { const requireRegExps: RegExp[] = [ // Example: require('something') - /\brequire\s*\(\s*[']([^']+\s*)[']\)/, - /\brequire\s*\(\s*["]([^"]+)["]\s*\)/, + /\brequire\s*\(\s*[']([^']+\s*)[']\s*\)/, + /\brequire\s*\(\s*["]([^"]+\s*)["]\s*\)/, // Example: require.ensure('something') - /\brequire.ensure\s*\(\s*[']([^']+\s*)[']\)/, - /\brequire.ensure\s*\(\s*["]([^"]+)["]\s*\)/, + /\brequire\.ensure\s*\(\s*[']([^']+\s*)[']\s*\)/, + /\brequire\.ensure\s*\(\s*["]([^"]+\s*)["]\s*\)/, // Example: require.resolve('something') - /\brequire.resolve\s*\(\s*[']([^']+\s*)[']\)/, - /\brequire.resolve\s*\(\s*["]([^"]+)["]\s*\)/, + /\brequire\.resolve\s*\(\s*[']([^']+\s*)[']\s*\)/, + /\brequire\.resolve\s*\(\s*["]([^"]+\s*)["]\s*\)/, // Example: System.import('something') - /\bSystem.import\s*\(\s*[']([^']+\s*)[']\)/, - /\bSystem.import\s*\(\s*["]([^"]+)["]\s*\)/, + /\bSystem\.import\s*\(\s*[']([^']+\s*)[']\s*\)/, + /\bSystem\.import\s*\(\s*["]([^"]+\s*)["]\s*\)/, + + // Example: Import.lazy('something', require); + /\bImport\.lazy\s*\(\s*[']([^']+\s*)[']/, + /\bImport\.lazy\s*\(\s*["]([^"]+\s*)["]/, // Example: // @@ -94,6 +99,10 @@ export class ScanAction extends BaseConfiglessRushAction { /\bimport\s*[']([^']+)[']\s*\;/, /\bimport\s*["]([^"]+)["]\s*\;/, + // Example: await import('fast-glob') + /\bimport\s*\(\s*[']([^']+)[']\s*\)/, + /\bimport\s*\(\s*["]([^"]+)["]\s*\)/, + // Example: // /// /\/\/\/\s*<\s*reference\s+types\s*=\s*["]([^"]+)["]\s*\/>/ @@ -101,15 +110,13 @@ export class ScanAction extends BaseConfiglessRushAction { // Example: "my-package/lad/dee/dah" --> "my-package" // Example: "@ms/my-package" --> "@ms/my-package" - const packageRegExp: RegExp = /^((@[a-z\-0-9!_]+\/)?[a-z\-0-9!_]+)\/?/; + // Example: "lodash.get" --> "lodash.get" + const packageRegExp: RegExp = /^((@[a-z\-0-9!_]+\/)?[a-z\-0-9!_][a-z\-0-9!_.]*)\/?/; const requireMatches: Set = new Set(); - const { default: glob } = await import('glob'); - const scanResults: string[] = await LegacyAdapters.convertCallbackToPromise( - glob, - '{./*.{ts,js,tsx,jsx},./{src,lib}/**/*.{ts,js,tsx,jsx}}' - ); + const { default: glob } = await import('fast-glob'); + const scanResults: string[] = await glob(['./*.{ts,js,tsx,jsx}', './{src,lib}/**/*.{ts,js,tsx,jsx}']); for (const filename of scanResults) { try { const contents: string = FileSystem.readFile(filename); @@ -124,7 +131,8 @@ export class ScanAction extends BaseConfiglessRushAction { } } } catch (error) { - console.log(colors.bold('Skipping file due to error: ' + filename)); + // eslint-disable-next-line no-console + console.log(Colorize.bold('Skipping file due to error: ' + filename)); } } @@ -140,7 +148,7 @@ export class ScanAction extends BaseConfiglessRushAction { const detectedPackageNames: string[] = []; packageMatches.forEach((packageName: string) => { - if (builtinPackageNames.indexOf(packageName) < 0) { + if (!isBuiltinModule(packageName)) { detectedPackageNames.push(packageName); } }); @@ -168,6 +176,7 @@ export class ScanAction extends BaseConfiglessRushAction { } } } catch (e) { + // eslint-disable-next-line no-console console.error(`JSON.parse ${packageJsonFilename} error`); } @@ -199,25 +208,31 @@ export class ScanAction extends BaseConfiglessRushAction { }; if (this._jsonFlag.value) { + // eslint-disable-next-line no-console console.log(JSON.stringify(output, undefined, 2)); } else if (this._allFlag.value) { if (detectedPackageNames.length !== 0) { + // eslint-disable-next-line no-console console.log('Dependencies that seem to be imported by this project:'); for (const packageName of detectedPackageNames) { + // eslint-disable-next-line no-console console.log(' ' + packageName); } } else { + // eslint-disable-next-line no-console console.log('This project does not seem to import any NPM packages.'); } } else { let wroteAnything: boolean = false; if (missingDependencies.length > 0) { + // eslint-disable-next-line no-console console.log( - colors.yellow('Possible phantom dependencies') + + Colorize.yellow('Possible phantom dependencies') + " - these seem to be imported but aren't listed in package.json:" ); for (const packageName of missingDependencies) { + // eslint-disable-next-line no-console console.log(' ' + packageName); } wroteAnything = true; @@ -225,21 +240,25 @@ export class ScanAction extends BaseConfiglessRushAction { if (unusedDependencies.length > 0) { if (wroteAnything) { + // eslint-disable-next-line no-console console.log(''); } + // eslint-disable-next-line no-console console.log( - colors.yellow('Possible unused dependencies') + + Colorize.yellow('Possible unused dependencies') + " - these are listed in package.json but don't seem to be imported:" ); for (const packageName of unusedDependencies) { + // eslint-disable-next-line no-console console.log(' ' + packageName); } wroteAnything = true; } if (!wroteAnything) { + // eslint-disable-next-line no-console console.log( - colors.green('Everything looks good.') + ' No missing or unused dependencies were found.' + Colorize.green('Everything looks good.') + ' No missing or unused dependencies were found.' ); } } diff --git a/libraries/rush-lib/src/cli/actions/SetupAction.ts b/libraries/rush-lib/src/cli/actions/SetupAction.ts index 4db157127d7..04e91720507 100644 --- a/libraries/rush-lib/src/cli/actions/SetupAction.ts +++ b/libraries/rush-lib/src/cli/actions/SetupAction.ts @@ -2,7 +2,7 @@ // See LICENSE in the project root for license information. import { SetupPackageRegistry } from '../../logic/setup/SetupPackageRegistry'; -import { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; import { BaseRushAction } from './BaseRushAction'; export class SetupAction extends BaseRushAction { @@ -26,6 +26,6 @@ export class SetupAction extends BaseRushAction { isDebug: this.parser.isDebug, syncNpmrcAlreadyCalled: false }); - await setupPackageRegistry.checkAndSetup(); + await setupPackageRegistry.checkAndSetupAsync(); } } diff --git a/libraries/rush-lib/src/cli/actions/UnlinkAction.ts b/libraries/rush-lib/src/cli/actions/UnlinkAction.ts index b96c08b63e7..ef3c112c2b6 100644 --- a/libraries/rush-lib/src/cli/actions/UnlinkAction.ts +++ b/libraries/rush-lib/src/cli/actions/UnlinkAction.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; import { BaseRushAction } from './BaseRushAction'; import { UnlinkManager } from '../../logic/UnlinkManager'; @@ -21,9 +21,11 @@ export class UnlinkAction extends BaseRushAction { protected async runAsync(): Promise { const unlinkManager: UnlinkManager = new UnlinkManager(this.rushConfiguration); - if (!unlinkManager.unlink()) { + if (!(await unlinkManager.unlinkAsync())) { + // eslint-disable-next-line no-console console.log('Nothing to do.'); } else { + // eslint-disable-next-line no-console console.log('\nDone.'); } } diff --git a/libraries/rush-lib/src/cli/actions/UpdateAction.ts b/libraries/rush-lib/src/cli/actions/UpdateAction.ts index 27f61febcdc..5fc74900e09 100644 --- a/libraries/rush-lib/src/cli/actions/UpdateAction.ts +++ b/libraries/rush-lib/src/cli/actions/UpdateAction.ts @@ -1,11 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { CommandLineFlagParameter } from '@rushstack/ts-command-line'; +import type { CommandLineFlagParameter } from '@rushstack/ts-command-line'; import { BaseInstallAction } from './BaseInstallAction'; import type { IInstallManagerOptions } from '../../logic/base/BaseInstallManagerTypes'; -import { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; +import { SelectionParameterSet } from '../parsing/SelectionParameterSet'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { Subspace } from '../../api/Subspace'; +import { getVariantAsync } from '../../api/Variants'; export class UpdateAction extends BaseInstallAction { private readonly _fullParameter: CommandLineFlagParameter; @@ -31,6 +35,21 @@ export class UpdateAction extends BaseInstallAction { parser }); + if (this.rushConfiguration?.subspacesFeatureEnabled) { + // Partial update is supported only when subspaces is enabled. + this._selectionParameters = new SelectionParameterSet(this.rushConfiguration, this, { + gitOptions: { + // Include lockfile processing since this expands the selection, and we need to select + // at least the same projects selected with the same query to "rush build" + includeExternalDependencies: true, + // Disable filtering because rush-project.json is riggable and therefore may not be available + enableFiltering: false + }, + includeSubspaceSelector: true, + cwd: this.parser.cwd + }); + } + this._fullParameter = this.defineFlagParameter({ parameterLongName: '--full', description: @@ -52,7 +71,7 @@ export class UpdateAction extends BaseInstallAction { }); } - protected async runAsync(): Promise { + protected override async runAsync(): Promise { await this.parser.pluginManager.updateAsync(); if (this.parser.pluginManager.error) { @@ -62,7 +81,17 @@ export class UpdateAction extends BaseInstallAction { return super.runAsync(); } - protected async buildInstallOptionsAsync(): Promise { + protected async buildInstallOptionsAsync(): Promise> { + const selectedProjects: Set = + (await this._selectionParameters?.getSelectedProjectsAsync(this.terminal)) ?? + new Set(this.rushConfiguration.projects); + + const variant: string | undefined = await getVariantAsync( + this._variantParameter, + this.rushConfiguration, + false + ); + return { debug: this.parser.isDebug, allowShrinkwrapUpdates: true, @@ -71,16 +100,23 @@ export class UpdateAction extends BaseInstallAction { noLink: this._noLinkParameter.value!, fullUpgrade: this._fullParameter.value!, recheckShrinkwrap: this._recheckParameter.value!, + offline: this._offlineParameter.value!, networkConcurrency: this._networkConcurrencyParameter.value, collectLogFile: this._debugPackageManagerParameter.value!, - variant: this._variant.value, + variant, // Because the 'defaultValue' option on the _maxInstallAttempts parameter is set, // it is safe to assume that the value is not null maxInstallAttempts: this._maxInstallAttempts.value!, - pnpmFilterArguments: [], + // These are derived independently of the selection for command line brevity + selectedProjects, + pnpmFilterArgumentValues: + (await this._selectionParameters?.getPnpmFilterArgumentValuesAsync(this.terminal)) ?? [], checkOnly: false, - - beforeInstallAsync: () => this.rushSession.hooks.beforeInstall.promise(this) + beforeInstallAsync: (subspace: Subspace) => + this.rushSession.hooks.beforeInstall.promise(this, subspace, variant), + afterInstallAsync: (subspace: Subspace) => + this.rushSession.hooks.afterInstall.promise(this, subspace, variant), + terminal: this.terminal }; } } diff --git a/libraries/rush-lib/src/cli/actions/UpdateAutoinstallerAction.ts b/libraries/rush-lib/src/cli/actions/UpdateAutoinstallerAction.ts index 54aa48dab80..e8e2f7085b2 100644 --- a/libraries/rush-lib/src/cli/actions/UpdateAutoinstallerAction.ts +++ b/libraries/rush-lib/src/cli/actions/UpdateAutoinstallerAction.ts @@ -1,15 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { CommandLineStringParameter } from '@rushstack/ts-command-line'; - -import { BaseRushAction } from './BaseRushAction'; -import { RushCommandLineParser } from '../RushCommandLineParser'; -import { Autoinstaller } from '../../logic/Autoinstaller'; - -export class UpdateAutoinstallerAction extends BaseRushAction { - private readonly _name: CommandLineStringParameter; +import type { RushCommandLineParser } from '../RushCommandLineParser'; +import type { Autoinstaller } from '../../logic/Autoinstaller'; +import { BaseAutoinstallerAction } from './BaseAutoinstallerAction'; +export class UpdateAutoinstallerAction extends BaseAutoinstallerAction { public constructor(parser: RushCommandLineParser) { super({ actionName: 'update-autoinstaller', @@ -17,31 +13,12 @@ export class UpdateAutoinstallerAction extends BaseRushAction { documentation: 'Use this command to regenerate the shrinkwrap file for an autoinstaller folder.', parser }); - - this._name = this.defineStringParameter({ - parameterLongName: '--name', - argumentName: 'AUTOINSTALLER_NAME', - required: true, - description: - 'Specifies the name of the autoinstaller, which must be one of the folders under common/autoinstallers.' - }); } - protected async runAsync(): Promise { - const autoinstallerName: string = this._name.value!; - - const autoinstaller: Autoinstaller = new Autoinstaller({ - autoinstallerName, - rushConfiguration: this.rushConfiguration, - rushGlobalFolder: this.rushGlobalFolder - }); - + protected async prepareAsync(autoinstaller: Autoinstaller): Promise { // Do not run `autoinstaller.prepareAsync` here. It tries to install the autoinstaller with // --frozen-lockfile or equivalent, which will fail if the autoinstaller's dependencies // have been changed. - await autoinstaller.updateAsync(); - - console.log('\nSuccess.'); } } diff --git a/libraries/rush-lib/src/cli/actions/UpdateCloudCredentialsAction.ts b/libraries/rush-lib/src/cli/actions/UpdateCloudCredentialsAction.ts index a43635b1116..97a792c9916 100644 --- a/libraries/rush-lib/src/cli/actions/UpdateCloudCredentialsAction.ts +++ b/libraries/rush-lib/src/cli/actions/UpdateCloudCredentialsAction.ts @@ -1,10 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { CommandLineStringParameter, CommandLineFlagParameter } from '@rushstack/ts-command-line'; -import { AlreadyReportedError, ConsoleTerminalProvider, Terminal } from '@rushstack/node-core-library'; +import type { CommandLineStringParameter, CommandLineFlagParameter } from '@rushstack/ts-command-line'; +import { AlreadyReportedError } from '@rushstack/node-core-library'; +import { ConsoleTerminalProvider, Terminal } from '@rushstack/terminal'; -import { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; import { BaseRushAction } from './BaseRushAction'; import { BuildCacheConfiguration } from '../../api/BuildCacheConfiguration'; import { RushConstants } from '../../logic/RushConstants'; diff --git a/libraries/rush-lib/src/cli/actions/UpgradeInteractiveAction.ts b/libraries/rush-lib/src/cli/actions/UpgradeInteractiveAction.ts index 346a137cd72..273b67ba91b 100644 --- a/libraries/rush-lib/src/cli/actions/UpgradeInteractiveAction.ts +++ b/libraries/rush-lib/src/cli/actions/UpgradeInteractiveAction.ts @@ -1,16 +1,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { CommandLineFlagParameter } from '@rushstack/ts-command-line'; -import { RushCommandLineParser } from '../RushCommandLineParser'; -import { BaseRushAction } from './BaseRushAction'; +import type { CommandLineFlagParameter, CommandLineStringParameter } from '@rushstack/ts-command-line'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; +import { BaseRushAction } from './BaseRushAction'; import type * as PackageJsonUpdaterType from '../../logic/PackageJsonUpdater'; import type * as InteractiveUpgraderType from '../../logic/InteractiveUpgrader'; +import { getVariantAsync, VARIANT_PARAMETER } from '../../api/Variants'; export class UpgradeInteractiveAction extends BaseRushAction { private _makeConsistentFlag: CommandLineFlagParameter; private _skipUpdateFlag: CommandLineFlagParameter; + private readonly _variantParameter: CommandLineStringParameter; public constructor(parser: RushCommandLineParser) { const documentation: string[] = [ @@ -42,6 +44,8 @@ export class UpgradeInteractiveAction extends BaseRushAction { description: 'If specified, the "rush update" command will not be run after updating the package.json files.' }); + + this._variantParameter = this.defineStringParameter(VARIANT_PARAMETER); } public async runAsync(): Promise { @@ -51,6 +55,7 @@ export class UpgradeInteractiveAction extends BaseRushAction { ]); const packageJsonUpdater: PackageJsonUpdaterType.PackageJsonUpdater = new PackageJsonUpdater( + this.terminal, this.rushConfiguration, this.rushGlobalFolder ); @@ -58,17 +63,24 @@ export class UpgradeInteractiveAction extends BaseRushAction { this.rushConfiguration ); + const variant: string | undefined = await getVariantAsync( + this._variantParameter, + this.rushConfiguration, + true + ); const shouldMakeConsistent: boolean = - this.rushConfiguration.ensureConsistentVersions || this._makeConsistentFlag.value; + this.rushConfiguration.defaultSubspace.shouldEnsureConsistentVersions(variant) || + this._makeConsistentFlag.value; - const { projects, depsToUpgrade } = await interactiveUpgrader.upgrade(); + const { projects, depsToUpgrade } = await interactiveUpgrader.upgradeAsync(); await packageJsonUpdater.doRushUpgradeAsync({ - projects: projects, + projects, packagesToAdd: depsToUpgrade.packages, updateOtherPackages: shouldMakeConsistent, skipUpdate: this._skipUpdateFlag.value, - debugInstall: this.parser.isDebug + debugInstall: this.parser.isDebug, + variant }); } } diff --git a/libraries/rush-lib/src/cli/actions/VersionAction.ts b/libraries/rush-lib/src/cli/actions/VersionAction.ts index 0206e22aec5..6eff2c1176d 100644 --- a/libraries/rush-lib/src/cli/actions/VersionAction.ts +++ b/libraries/rush-lib/src/cli/actions/VersionAction.ts @@ -2,20 +2,20 @@ // See LICENSE in the project root for license information. import * as semver from 'semver'; -import { IPackageJson, FileConstants, Enum } from '@rushstack/node-core-library'; -import { CommandLineFlagParameter, CommandLineStringParameter } from '@rushstack/ts-command-line'; -import { BumpType, LockStepVersionPolicy } from '../../api/VersionPolicy'; -import { VersionPolicyConfiguration } from '../../api/VersionPolicyConfiguration'; +import { type IPackageJson, FileConstants, Enum } from '@rushstack/node-core-library'; +import type { CommandLineFlagParameter, CommandLineStringParameter } from '@rushstack/ts-command-line'; + +import { BumpType, type LockStepVersionPolicy } from '../../api/VersionPolicy'; +import type { VersionPolicyConfiguration } from '../../api/VersionPolicyConfiguration'; import { RushConfiguration } from '../../api/RushConfiguration'; import { VersionMismatchFinder } from '../../logic/versionMismatch/VersionMismatchFinder'; -import { RushCommandLineParser } from '../RushCommandLineParser'; +import type { RushCommandLineParser } from '../RushCommandLineParser'; import * as PolicyValidator from '../../logic/policy/PolicyValidator'; import { BaseRushAction } from './BaseRushAction'; import { PublishGit } from '../../logic/PublishGit'; import { Git } from '../../logic/Git'; import { RushConstants } from '../../logic/RushConstants'; - import type * as VersionManagerType from '../../logic/VersionManager'; export const DEFAULT_PACKAGE_UPDATE_MESSAGE: string = 'Bump versions [skip ci]'; @@ -95,12 +95,16 @@ export class VersionAction extends BaseRushAction { } protected async runAsync(): Promise { - await PolicyValidator.validatePolicyAsync(this.rushConfiguration, { - bypassPolicyAllowed: true, - bypassPolicy: this._bypassPolicy.value - }); + const currentlyInstalledVariant: string | undefined = + await this.rushConfiguration.getCurrentlyInstalledVariantAsync(); + for (const subspace of this.rushConfiguration.subspaces) { + await PolicyValidator.validatePolicyAsync(this.rushConfiguration, subspace, currentlyInstalledVariant, { + bypassPolicyAllowed: true, + bypassPolicy: this._bypassPolicy.value + }); + } const git: Git = new Git(this.rushConfiguration); - const userEmail: string = git.getGitEmail(); + const userEmail: string = await git.getGitEmailAsync(); this._validateInput(); const versionManagerModule: typeof VersionManagerType = await import( @@ -124,18 +128,20 @@ export class VersionAction extends BaseRushAction { const updatedPackages: Map = versionManager.updatedProjects; if (updatedPackages.size > 0) { + // eslint-disable-next-line no-console console.log(`${updatedPackages.size} packages are getting updated.`); - this._gitProcess(tempBranch, this._targetBranch.value); + await this._gitProcessAsync(tempBranch, this._targetBranch.value, currentlyInstalledVariant); } } else if (this._bumpVersion.value) { const tempBranch: string = 'version/bump-' + new Date().getTime(); await versionManager.bumpAsync( + this.terminal, this._versionPolicy.value, this._overwriteBump.value ? Enum.getValueByKey(BumpType, this._overwriteBump.value) : undefined, this._prereleaseIdentifier.value, true ); - this._gitProcess(tempBranch, this._targetBranch.value); + await this._gitProcessAsync(tempBranch, this._targetBranch.value, currentlyInstalledVariant); } } @@ -202,37 +208,47 @@ export class VersionAction extends BaseRushAction { } } - private _validateResult(): void { + private _validateResult(variant: string | undefined): void { // Load the config from file to avoid using inconsistent in-memory data. const rushConfig: RushConfiguration = RushConfiguration.loadFromConfigurationFile( this.rushConfiguration.rushJsonFile ); - // Respect the `ensureConsistentVersions` field in rush.json - if (!rushConfig.ensureConsistentVersions) { - return; - } + // Validate result of all subspaces + for (const subspace of rushConfig.subspaces) { + // Respect the `ensureConsistentVersions` field in rush.json + if (!subspace.shouldEnsureConsistentVersions(variant)) { + continue; + } - const mismatchFinder: VersionMismatchFinder = VersionMismatchFinder.getMismatches(rushConfig); - if (mismatchFinder.numberOfMismatches) { - throw new Error( - 'Unable to finish version bump because inconsistencies were encountered. ' + - 'Run "rush check" to find more details.' - ); + const mismatchFinder: VersionMismatchFinder = VersionMismatchFinder.getMismatches(rushConfig, { + subspace, + variant + }); + if (mismatchFinder.numberOfMismatches) { + throw new Error( + 'Unable to finish version bump because inconsistencies were encountered. ' + + 'Run "rush check" to find more details.' + ); + } } } - private _gitProcess(tempBranch: string, targetBranch: string | undefined): void { + private async _gitProcessAsync( + tempBranch: string, + targetBranch: string | undefined, + variant: string | undefined + ): Promise { // Validate the result before commit. - this._validateResult(); + this._validateResult(variant); const git: Git = new Git(this.rushConfiguration); const publishGit: PublishGit = new PublishGit(git, targetBranch); // Make changes in temp branch. - publishGit.checkout(tempBranch, true); + await publishGit.checkoutAsync(tempBranch, true); - const uncommittedChanges: ReadonlyArray = git.getUncommittedChanges(); + const uncommittedChanges: ReadonlyArray = await git.getUncommittedChangesAsync(); // Stage, commit, and push the changes to remote temp branch. // Need to commit the change log updates in its own commit @@ -241,10 +257,10 @@ export class VersionAction extends BaseRushAction { }); if (changeLogUpdated) { - publishGit.addChanges('.', this.rushConfiguration.changesFolder); - publishGit.addChanges(':/**/CHANGELOG.json'); - publishGit.addChanges(':/**/CHANGELOG.md'); - publishGit.commit( + await publishGit.addChangesAsync('.', this.rushConfiguration.changesFolder); + await publishGit.addChangesAsync(':/**/CHANGELOG.json'); + await publishGit.addChangesAsync(':/**/CHANGELOG.md'); + await publishGit.commitAsync( this.rushConfiguration.gitChangeLogUpdateCommitMessage || DEFAULT_CHANGELOG_UPDATE_MESSAGE, !this._ignoreGitHooksParameter.value ); @@ -256,29 +272,29 @@ export class VersionAction extends BaseRushAction { }); if (packageJsonUpdated) { - publishGit.addChanges(this.rushConfiguration.versionPolicyConfigurationFilePath); - publishGit.addChanges(':/**/package.json'); - publishGit.commit( + await publishGit.addChangesAsync(this.rushConfiguration.versionPolicyConfigurationFilePath); + await publishGit.addChangesAsync(':/**/package.json'); + await publishGit.commitAsync( this.rushConfiguration.gitVersionBumpCommitMessage || DEFAULT_PACKAGE_UPDATE_MESSAGE, !this._ignoreGitHooksParameter.value ); } if (changeLogUpdated || packageJsonUpdated) { - publishGit.push(tempBranch, !this._ignoreGitHooksParameter.value); + await publishGit.pushAsync(tempBranch, !this._ignoreGitHooksParameter.value, false); // Now merge to target branch. - publishGit.fetch(); - publishGit.checkout(targetBranch); - publishGit.pull(!this._ignoreGitHooksParameter.value); - publishGit.merge(tempBranch, !this._ignoreGitHooksParameter.value); - publishGit.push(targetBranch, !this._ignoreGitHooksParameter.value); - publishGit.deleteBranch(tempBranch, true, !this._ignoreGitHooksParameter.value); + await publishGit.fetchAsync(); + await publishGit.checkoutAsync(targetBranch); + await publishGit.pullAsync(!this._ignoreGitHooksParameter.value); + await publishGit.mergeAsync(tempBranch, !this._ignoreGitHooksParameter.value); + await publishGit.pushAsync(targetBranch, !this._ignoreGitHooksParameter.value, false); + await publishGit.deleteBranchAsync(tempBranch, true, !this._ignoreGitHooksParameter.value); } else { // skip commits - publishGit.fetch(); - publishGit.checkout(targetBranch); - publishGit.deleteBranch(tempBranch, false, !this._ignoreGitHooksParameter.value); + await publishGit.fetchAsync(); + await publishGit.checkoutAsync(targetBranch); + await publishGit.deleteBranchAsync(tempBranch, false, !this._ignoreGitHooksParameter.value); } } } diff --git a/libraries/rush-lib/src/cli/actions/test/AddAction.test.ts b/libraries/rush-lib/src/cli/actions/test/AddAction.test.ts index dd2d97b456c..e41934ed1f0 100644 --- a/libraries/rush-lib/src/cli/actions/test/AddAction.test.ts +++ b/libraries/rush-lib/src/cli/actions/test/AddAction.test.ts @@ -3,15 +3,18 @@ import '../../test/mockRushCommandLineParser'; +import { LockFile } from '@rushstack/node-core-library'; + import { PackageJsonUpdater } from '../../../logic/PackageJsonUpdater'; import type { IPackageJsonUpdaterRushAddOptions } from '../../../logic/PackageJsonUpdaterTypes'; import { RushCommandLineParser } from '../../RushCommandLineParser'; import { AddAction } from '../AddAction'; +import { EnvironmentConfiguration } from '../../../api/EnvironmentConfiguration'; describe(AddAction.name, () => { describe('basic "rush add" tests', () => { let doRushAddMock: jest.SpyInstance; - let oldExitCode: number | undefined; + let oldExitCode: number | string | undefined; let oldArgs: string[]; beforeEach(() => { @@ -19,6 +22,10 @@ describe(AddAction.name, () => { .spyOn(PackageJsonUpdater.prototype, 'doRushUpdateAsync') .mockImplementation(() => Promise.resolve()); jest.spyOn(process, 'exit').mockImplementation(); + + // Suppress "Another Rush command is already running" error + jest.spyOn(LockFile, 'tryAcquire').mockImplementation(() => ({}) as LockFile); + oldExitCode = process.exitCode; oldArgs = process.argv; }); @@ -27,6 +34,7 @@ describe(AddAction.name, () => { jest.clearAllMocks(); process.exitCode = oldExitCode; process.argv = oldArgs; + EnvironmentConfiguration.reset(); }); describe("'add' action", () => { @@ -46,7 +54,7 @@ describe(AddAction.name, () => { // Mock the command process.argv = ['pretend-this-is-node.exe', 'pretend-this-is-rush', 'add', '-p', 'assert']; - await expect(parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); expect(doRushAddMock).toHaveBeenCalledTimes(1); const doRushAddOptions: IPackageJsonUpdaterRushAddOptions = doRushAddMock.mock.calls[0][0]; expect(doRushAddOptions.projects).toHaveLength(1); @@ -80,7 +88,7 @@ describe(AddAction.name, () => { // Mock the command process.argv = ['pretend-this-is-node.exe', 'pretend-this-is-rush', 'add', '-p', 'assert', '--all']; - await expect(parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); expect(doRushAddMock).toHaveBeenCalledTimes(1); const doRushAddOptions: IPackageJsonUpdaterRushAddOptions = doRushAddMock.mock.calls[0][0]; expect(doRushAddOptions.projects).toHaveLength(2); diff --git a/libraries/rush-lib/src/cli/actions/test/RemoveAction.test.ts b/libraries/rush-lib/src/cli/actions/test/RemoveAction.test.ts index 850e7d1c186..f6a32ac87b4 100644 --- a/libraries/rush-lib/src/cli/actions/test/RemoveAction.test.ts +++ b/libraries/rush-lib/src/cli/actions/test/RemoveAction.test.ts @@ -3,18 +3,21 @@ import '../../test/mockRushCommandLineParser'; +import { LockFile } from '@rushstack/node-core-library'; + import { PackageJsonUpdater } from '../../../logic/PackageJsonUpdater'; import type { IPackageJsonUpdaterRushRemoveOptions } from '../../../logic/PackageJsonUpdaterTypes'; import { RushCommandLineParser } from '../../RushCommandLineParser'; import { RemoveAction } from '../RemoveAction'; import { VersionMismatchFinderProject } from '../../../logic/versionMismatch/VersionMismatchFinderProject'; import { DependencyType } from '../../../api/PackageJsonEditor'; +import { EnvironmentConfiguration } from '../../../api/EnvironmentConfiguration'; describe(RemoveAction.name, () => { describe('basic "rush remove" tests', () => { let doRushRemoveMock: jest.SpyInstance; let removeDependencyMock: jest.SpyInstance; - let oldExitCode: number | undefined; + let oldExitCode: number | string | undefined; let oldArgs: string[]; beforeEach(() => { @@ -23,6 +26,10 @@ describe(RemoveAction.name, () => { .mockImplementation(() => {}); jest.spyOn(process, 'exit').mockImplementation(); + + // Suppress "Another Rush command is already running" error + jest.spyOn(LockFile, 'tryAcquire').mockImplementation(() => ({}) as LockFile); + oldExitCode = process.exitCode; oldArgs = process.argv; }); @@ -31,6 +38,7 @@ describe(RemoveAction.name, () => { jest.clearAllMocks(); process.exitCode = oldExitCode; process.argv = oldArgs; + EnvironmentConfiguration.reset(); }); describe("'remove' action", () => { @@ -50,7 +58,7 @@ describe(RemoveAction.name, () => { // Mock the command process.argv = ['pretend-this-is-node.exe', 'pretend-this-is-rush', 'remove', '-p', 'assert', '-s']; - await expect(parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); expect(removeDependencyMock).toHaveBeenCalledTimes(2); const packageName: string = removeDependencyMock.mock.calls[0][0]; expect(packageName).toEqual('assert'); @@ -78,7 +86,7 @@ describe(RemoveAction.name, () => { // Mock the command process.argv = ['pretend-this-is-node.exe', 'pretend-this-is-rush', 'remove', '-p', 'assert']; - await expect(parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); expect(doRushRemoveMock).toHaveBeenCalledTimes(1); const doRushRemoveOptions: IPackageJsonUpdaterRushRemoveOptions = doRushRemoveMock.mock.calls[0][0]; expect(doRushRemoveOptions.projects).toHaveLength(1); @@ -121,8 +129,7 @@ describe(RemoveAction.name, () => { '--all' ]; - // const a = await parser.execute(); - await expect(parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); expect(doRushRemoveMock).toHaveBeenCalledTimes(1); const doRushRemoveOptions: IPackageJsonUpdaterRushRemoveOptions = doRushRemoveMock.mock.calls[0][0]; expect(doRushRemoveOptions.projects).toHaveLength(3); diff --git a/libraries/rush-lib/src/cli/parsing/ParseParallelism.ts b/libraries/rush-lib/src/cli/parsing/ParseParallelism.ts deleted file mode 100644 index f89216f8307..00000000000 --- a/libraries/rush-lib/src/cli/parsing/ParseParallelism.ts +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import * as os from 'os'; - -/** - * Parses a command line specification for desired parallelism. - * Factored out to enable unit tests - */ -export function parseParallelism( - rawParallelism: string | undefined, - numberOfCores: number = os.cpus().length -): number { - if (rawParallelism) { - if (rawParallelism === 'max') { - return numberOfCores; - } else { - const parallelismAsNumber: number = Number(rawParallelism); - - if (typeof rawParallelism === 'string' && rawParallelism.trim().endsWith('%')) { - const parsedPercentage: number = Number(rawParallelism.trim().replace(/\%$/, '')); - - if (parsedPercentage <= 0 || parsedPercentage > 100) { - throw new Error( - `Invalid percentage value of '${rawParallelism}', value cannot be less than '0%' or more than '100%'` - ); - } - - const workers: number = Math.floor((parsedPercentage / 100) * numberOfCores); - return Math.max(workers, 1); - } else if (!isNaN(parallelismAsNumber)) { - return Math.max(parallelismAsNumber, 1); - } else { - throw new Error( - `Invalid parallelism value of '${rawParallelism}', expected a number, a percentage, or 'max'` - ); - } - } - } else { - // If an explicit parallelism number wasn't provided, then choose a sensible - // default. - if (os.platform() === 'win32') { - // On desktop Windows, some people have complained that their system becomes - // sluggish if Rush is using all the CPU cores. Leave one thread for - // other operations. For CI environments, you can use the "max" argument to use all available cores. - return Math.max(numberOfCores - 1, 1); - } else { - // Unix-like operating systems have more balanced scheduling, so default - // to the number of CPU cores - return numberOfCores; - } - } -} diff --git a/libraries/rush-lib/src/cli/parsing/SelectionParameterSet.ts b/libraries/rush-lib/src/cli/parsing/SelectionParameterSet.ts index e20918dd0a9..758fbb5737b 100644 --- a/libraries/rush-lib/src/cli/parsing/SelectionParameterSet.ts +++ b/libraries/rush-lib/src/cli/parsing/SelectionParameterSet.ts @@ -1,25 +1,40 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { - AlreadyReportedError, - PackageJsonLookup, - IPackageJson, - ITerminal -} from '@rushstack/node-core-library'; -import { CommandLineParameterProvider, CommandLineStringListParameter } from '@rushstack/ts-command-line'; - -import { RushConfiguration } from '../../api/RushConfiguration'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import { AlreadyReportedError } from '@rushstack/node-core-library'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; +import type { + CommandLineParameterProvider, + CommandLineStringListParameter, + CommandLineStringParameter +} from '@rushstack/ts-command-line'; + +import type { RushConfiguration } from '../../api/RushConfiguration'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import { Selection } from '../../logic/Selection'; import type { ISelectorParser as ISelectorParser } from '../../logic/selectors/ISelectorParser'; import { GitChangedProjectSelectorParser, - IGitSelectorParserOptions + type IGitSelectorParserOptions } from '../../logic/selectors/GitChangedProjectSelectorParser'; import { NamedProjectSelectorParser } from '../../logic/selectors/NamedProjectSelectorParser'; import { TagProjectSelectorParser } from '../../logic/selectors/TagProjectSelectorParser'; import { VersionPolicyProjectSelectorParser } from '../../logic/selectors/VersionPolicyProjectSelectorParser'; +import { SubspaceSelectorParser } from '../../logic/selectors/SubspaceSelectorParser'; +import { PathProjectSelectorParser } from '../../logic/selectors/PathProjectSelectorParser'; +import type { Subspace } from '../../api/Subspace'; + +export const SUBSPACE_LONG_ARG_NAME: '--subspace' = '--subspace'; + +interface ISelectionParameterSetOptions { + gitOptions: IGitSelectorParserOptions; + includeSubspaceSelector: boolean; + /** + * The working directory used to resolve relative paths. + * This should be the same directory that was used to find the Rush configuration. + */ + cwd: string; +} /** * This class is provides the set of command line parameters used to select projects @@ -36,6 +51,7 @@ export class SelectionParameterSet { private readonly _onlyProject: CommandLineStringListParameter; private readonly _toProject: CommandLineStringListParameter; private readonly _toExceptProject: CommandLineStringListParameter; + private readonly _subspaceParameter: CommandLineStringParameter | undefined; private readonly _fromVersionPolicy: CommandLineStringListParameter; private readonly _toVersionPolicy: CommandLineStringListParameter; @@ -45,8 +61,9 @@ export class SelectionParameterSet { public constructor( rushConfiguration: RushConfiguration, action: CommandLineParameterProvider, - gitOptions: IGitSelectorParserOptions + options: ISelectionParameterSetOptions ) { + const { gitOptions, includeSubspaceSelector, cwd } = options; this._rushConfiguration = rushConfiguration; const selectorParsers: Map> = new Map< @@ -59,10 +76,12 @@ export class SelectionParameterSet { selectorParsers.set('git', new GitChangedProjectSelectorParser(rushConfiguration, gitOptions)); selectorParsers.set('tag', new TagProjectSelectorParser(rushConfiguration)); selectorParsers.set('version-policy', new VersionPolicyProjectSelectorParser(rushConfiguration)); + selectorParsers.set('subspace', new SubspaceSelectorParser(rushConfiguration)); + selectorParsers.set('path', new PathProjectSelectorParser(rushConfiguration, cwd)); this._selectorParserByScope = selectorParsers; - const getSpecifierCompletions: () => Promise = async (): Promise => { + const getCompletionsAsync: () => Promise = async (): Promise => { const completions: string[] = ['.']; for (const [prefix, selector] of selectorParsers) { for (const completion of selector.getCompletions()) { @@ -88,7 +107,7 @@ export class SelectionParameterSet { ' Each "--to" parameter expands this selection to include PROJECT and all its dependencies.' + ' "." can be used as shorthand for the project in the current working directory.' + ' For details, refer to the website article "Selecting subsets of projects".', - completions: getSpecifierCompletions + getCompletionsAsync }); this._toExceptProject = action.defineStringListParameter({ parameterLongName: '--to-except', @@ -101,7 +120,7 @@ export class SelectionParameterSet { ' but not PROJECT itself.' + ' "." can be used as shorthand for the project in the current working directory.' + ' For details, refer to the website article "Selecting subsets of projects".', - completions: getSpecifierCompletions + getCompletionsAsync }); this._fromProject = action.defineStringListParameter({ @@ -115,7 +134,7 @@ export class SelectionParameterSet { ' plus all dependencies of this set.' + ' "." can be used as shorthand for the project in the current working directory.' + ' For details, refer to the website article "Selecting subsets of projects".', - completions: getSpecifierCompletions + getCompletionsAsync }); this._onlyProject = action.defineStringListParameter({ parameterLongName: '--only', @@ -128,7 +147,7 @@ export class SelectionParameterSet { ' "." can be used as shorthand for the project in the current working directory.' + ' Note that this parameter is "unsafe" as it may produce a selection that excludes some dependencies.' + ' For details, refer to the website article "Selecting subsets of projects".', - completions: getSpecifierCompletions + getCompletionsAsync }); this._impactedByProject = action.defineStringListParameter({ @@ -143,7 +162,7 @@ export class SelectionParameterSet { ' "." can be used as shorthand for the project in the current working directory.' + ' Note that this parameter is "unsafe" as it may produce a selection that excludes some dependencies.' + ' For details, refer to the website article "Selecting subsets of projects".', - completions: getSpecifierCompletions + getCompletionsAsync }); this._impactedByExceptProject = action.defineStringListParameter({ @@ -158,7 +177,7 @@ export class SelectionParameterSet { ' "." can be used as shorthand for the project in the current working directory.' + ' Note that this parameter is "unsafe" as it may produce a selection that excludes some dependencies.' + ' For details, refer to the website article "Selecting subsets of projects".', - completions: getSpecifierCompletions + getCompletionsAsync }); this._toVersionPolicy = action.defineStringListParameter({ @@ -181,6 +200,40 @@ export class SelectionParameterSet { ' belonging to VERSION_POLICY_NAME.' + ' For details, refer to the website article "Selecting subsets of projects".' }); + + if (includeSubspaceSelector) { + this._subspaceParameter = action.defineStringParameter({ + parameterLongName: SUBSPACE_LONG_ARG_NAME, + argumentName: 'SUBSPACE_NAME', + description: + '(EXPERIMENTAL) Specifies a Rush subspace to be installed. Requires the "subspacesEnabled" feature to be enabled in subspaces.json.' + }); + } + } + + /** + * Used to implement the `preventSelectingAllSubspaces` policy which checks for commands that accidentally + * select everything. Return `true` if the CLI was invoked with selection parameters. + * + * @remarks + * It is still possible for a user to select everything, but they must do so using an explicit selection + * such as `rush install --from thing-that-everything-depends-on`. + */ + public didUserSelectAnything(): boolean { + if (this._subspaceParameter?.value) { + return true; + } + + return [ + this._impactedByProject, + this._impactedByExceptProject, + this._onlyProject, + this._toProject, + this._fromProject, + this._toExceptProject, + this._fromVersionPolicy, + this._toVersionPolicy + ].some((x) => x.values.length > 0); } /** @@ -188,7 +241,10 @@ export class SelectionParameterSet { * * If no parameters are specified, returns all projects in the Rush config file. */ - public async getSelectedProjectsAsync(terminal: ITerminal): Promise> { + public async getSelectedProjectsAsync( + terminal: ITerminal, + allowEmptySelection?: boolean + ): Promise> { // Hack out the old version-policy parameters for (const value of this._fromVersionPolicy.values) { (this._fromProject.values as string[]).push(`version-policy:${value}`); @@ -207,13 +263,13 @@ export class SelectionParameterSet { ]; // Check if any of the selection parameters have a value specified on the command line - const isSelectionSpecified: boolean = selectors.some( - (param: CommandLineStringListParameter) => param.values.length > 0 - ); + const isSelectionSpecified: boolean = + selectors.some((param: CommandLineStringListParameter) => param.values.length > 0) || + !!this._subspaceParameter?.value; // If no selection parameters are specified, return everything if (!isSelectionSpecified) { - return new Set(this._rushConfiguration.projects); + return allowEmptySelection ? new Set() : new Set(this._rushConfiguration.projects); } const [ @@ -235,6 +291,26 @@ export class SelectionParameterSet { }) ); + let subspaceProjects: Iterable = []; + + if (this._subspaceParameter?.value) { + if (!this._rushConfiguration.subspacesFeatureEnabled) { + // eslint-disable-next-line no-console + console.log(); + // eslint-disable-next-line no-console + console.log( + Colorize.red( + `The "${SUBSPACE_LONG_ARG_NAME}" parameter can only be passed if "subspacesEnabled" ` + + 'is set to true in subspaces.json.' + ) + ); + throw new AlreadyReportedError(); + } + + const subspace: Subspace = this._rushConfiguration.getSubspace(this._subspaceParameter.value); + subspaceProjects = subspace.getProjects(); + } + const selection: Set = Selection.union( // Safe command line options Selection.expandAllDependencies( @@ -245,6 +321,7 @@ export class SelectionParameterSet { Selection.expandAllConsumers(fromProjects) ) ), + subspaceProjects, // Unsafe command line option: --only onlyProjects, @@ -262,16 +339,21 @@ export class SelectionParameterSet { * Represents the selection as `--filter` parameters to pnpm. * * @remarks - * This is a separate from the selection to allow the filters to be represented more concisely. * - * @see https://pnpm.js.org/en/filtering + * IMPORTANT: This function produces PNPM CLI operators that select projects from PNPM's temp workspace. + * If Rush subspaces are enabled, PNPM cannot see the complete Rush workspace, and therefore these operators + * would malfunction. In the current implementation, we calculate them anyway, then `BaseInstallAction.runAsync()` + * will overwrite `pnpmFilterArgumentValues` with a flat list of project names. In the future, these + * two code paths will be combined into a single general solution. + * + * @see https://pnpm.io/filtering */ - public async getPnpmFilterArgumentsAsync(terminal: ITerminal): Promise { + public async getPnpmFilterArgumentValuesAsync(terminal: ITerminal): Promise { const args: string[] = []; // Include exactly these projects (--only) for (const project of await this._evaluateProjectParameterAsync(this._onlyProject, terminal)) { - args.push('--filter', project.packageName); + args.push(project.packageName); } // Include all projects that depend on these projects, and all dependencies thereof @@ -287,19 +369,19 @@ export class SelectionParameterSet { // --from / --from-version-policy Selection.expandAllConsumers(fromProjects) )) { - args.push('--filter', `${project.packageName}...`); + args.push(`${project.packageName}...`); } // --to-except // All projects that the project directly or indirectly declares as a dependency for (const project of await this._evaluateProjectParameterAsync(this._toExceptProject, terminal)) { - args.push('--filter', `${project.packageName}^...`); + args.push(`${project.packageName}^...`); } // --impacted-by // The project and all projects directly or indirectly declare it as a dependency for (const project of await this._evaluateProjectParameterAsync(this._impactedByProject, terminal)) { - args.push('--filter', `...${project.packageName}`); + args.push(`...${project.packageName}`); } // --impacted-by-except @@ -308,7 +390,7 @@ export class SelectionParameterSet { this._impactedByExceptProject, terminal )) { - args.push('--filter', `...^${project.packageName}`); + args.push(`...^${project.packageName}`); } return args; @@ -343,40 +425,36 @@ export class SelectionParameterSet { const selection: Set = new Set(); for (const rawSelector of listParameter.values) { - // Handle the special case of "current project" without a scope - if (rawSelector === '.') { - const packageJsonLookup: PackageJsonLookup = PackageJsonLookup.instance; - const packageJson: IPackageJson | undefined = packageJsonLookup.tryLoadPackageJsonFor(process.cwd()); - if (packageJson) { - const project: RushConfigurationProject | undefined = this._rushConfiguration.getProjectByName( - packageJson.name - ); - - if (project) { - selection.add(project); - } else { - terminal.writeErrorLine( - 'Rush is not currently running in a project directory specified in rush.json. ' + - `The "." value for the ${parameterName} parameter is not allowed.` - ); - throw new AlreadyReportedError(); - } + const scopeIndex: number = rawSelector.indexOf(':'); + + let scope: string; + let unscopedSelector: string; + + if (scopeIndex < 0) { + // No explicit scope - determine if this looks like a path + // Check for relative paths: '.', '..', or those followed by '/' and more + // Check for absolute POSIX paths: starting with '/' + const isRelativePath: boolean = + rawSelector === '.' || + rawSelector === '..' || + rawSelector.startsWith('./') || + rawSelector.startsWith('../'); + const isAbsolutePosixPath: boolean = rawSelector.startsWith('/'); + + if (isRelativePath || isAbsolutePosixPath) { + // Route to path: selector + scope = 'path'; + unscopedSelector = rawSelector; } else { - terminal.writeErrorLine( - 'Rush is not currently running in a project directory. ' + - `The "." value for the ${parameterName} parameter is not allowed.` - ); - throw new AlreadyReportedError(); + // Default to name: selector + scope = 'name'; + unscopedSelector = rawSelector; } - - continue; + } else { + scope = rawSelector.slice(0, scopeIndex); + unscopedSelector = rawSelector.slice(scopeIndex + 1); } - const scopeIndex: number = rawSelector.indexOf(':'); - - const scope: string = scopeIndex < 0 ? 'name' : rawSelector.slice(0, scopeIndex); - const unscopedSelector: string = scopeIndex < 0 ? rawSelector : rawSelector.slice(scopeIndex + 1); - const handler: ISelectorParser | undefined = this._selectorParserByScope.get(scope); if (!handler) { @@ -384,7 +462,7 @@ export class SelectionParameterSet { `Unsupported selector prefix "${scope}" passed to "${parameterName}": "${rawSelector}".` + ` Supported prefixes: ${Array.from( this._selectorParserByScope.keys(), - (scope: string) => `"${scope}:"` + (selectorParserScope: string) => `"${selectorParserScope}:"` ).join(', ')}` ); throw new AlreadyReportedError(); diff --git a/libraries/rush-lib/src/cli/parsing/associateParametersByPhase.ts b/libraries/rush-lib/src/cli/parsing/associateParametersByPhase.ts new file mode 100644 index 00000000000..408b1f27910 --- /dev/null +++ b/libraries/rush-lib/src/cli/parsing/associateParametersByPhase.ts @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { InternalError } from '@rushstack/node-core-library'; +import type { CommandLineParameter } from '@rushstack/ts-command-line'; + +import type { IParameterJson, IPhase } from '../../api/CommandLineConfiguration'; + +/** + * Associates command line parameters with their associated phases. + * This helper is used to populate the `associatedParameters` set on each phase + * based on the `associatedPhases` property of each parameter. + * + * @param customParameters - Map of parameter definitions to their CommandLineParameter instances + * @param knownPhases - Map of phase names to IPhase objects + */ +export function associateParametersByPhase( + customParameters: ReadonlyMap, + knownPhases: ReadonlyMap +): void { + for (const [parameterJson, tsCommandLineParameter] of customParameters) { + if (parameterJson.associatedPhases) { + for (const phaseName of parameterJson.associatedPhases) { + const phase: IPhase | undefined = knownPhases.get(phaseName); + if (!phase) { + throw new InternalError(`Could not find a phase matching ${phaseName}.`); + } + phase.associatedParameters.add(tsCommandLineParameter); + } + } + } +} diff --git a/libraries/rush-lib/src/cli/parsing/defineCustomParameters.ts b/libraries/rush-lib/src/cli/parsing/defineCustomParameters.ts new file mode 100644 index 00000000000..bd5b80758dc --- /dev/null +++ b/libraries/rush-lib/src/cli/parsing/defineCustomParameters.ts @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { CommandLineAction, CommandLineParameter } from '@rushstack/ts-command-line'; + +import type { IParameterJson } from '../../api/CommandLineConfiguration'; +import { RushConstants } from '../../logic/RushConstants'; +import type { ParameterJson } from '../../api/CommandLineJson'; + +/** + * Helper function to create CommandLineParameter instances from parameter definitions. + * This centralizes the logic for defining parameters based on their kind. + * + * @param action - The CommandLineAction to define the parameters on + * @param associatedParameters - The set of parameter definitions + * @param targetMap - The map to populate with parameter definitions to CommandLineParameter instances + */ +export function defineCustomParameters( + action: CommandLineAction, + associatedParameters: Iterable, + targetMap: Map +): void { + for (const parameter of associatedParameters) { + let tsCommandLineParameter: CommandLineParameter | undefined; + + switch (parameter.parameterKind) { + case 'flag': + tsCommandLineParameter = action.defineFlagParameter({ + parameterShortName: parameter.shortName, + parameterLongName: parameter.longName, + description: parameter.description, + required: parameter.required + }); + break; + case 'choice': + tsCommandLineParameter = action.defineChoiceParameter({ + parameterShortName: parameter.shortName, + parameterLongName: parameter.longName, + description: parameter.description, + required: parameter.required, + alternatives: parameter.alternatives.map((x) => x.name), + defaultValue: parameter.defaultValue + }); + break; + case 'string': + tsCommandLineParameter = action.defineStringParameter({ + parameterLongName: parameter.longName, + parameterShortName: parameter.shortName, + description: parameter.description, + required: parameter.required, + argumentName: parameter.argumentName + }); + break; + case 'integer': + tsCommandLineParameter = action.defineIntegerParameter({ + parameterLongName: parameter.longName, + parameterShortName: parameter.shortName, + description: parameter.description, + required: parameter.required, + argumentName: parameter.argumentName + }); + break; + case 'stringList': + tsCommandLineParameter = action.defineStringListParameter({ + parameterLongName: parameter.longName, + parameterShortName: parameter.shortName, + description: parameter.description, + required: parameter.required, + argumentName: parameter.argumentName + }); + break; + case 'integerList': + tsCommandLineParameter = action.defineIntegerListParameter({ + parameterLongName: parameter.longName, + parameterShortName: parameter.shortName, + description: parameter.description, + required: parameter.required, + argumentName: parameter.argumentName + }); + break; + case 'choiceList': + tsCommandLineParameter = action.defineChoiceListParameter({ + parameterShortName: parameter.shortName, + parameterLongName: parameter.longName, + description: parameter.description, + required: parameter.required, + alternatives: parameter.alternatives.map((x) => x.name) + }); + break; + default: + throw new Error( + `${RushConstants.commandLineFilename} defines a parameter "${ + (parameter as ParameterJson).longName + }" using an unsupported parameter kind "${(parameter as ParameterJson).parameterKind}"` + ); + } + + targetMap.set(parameter, tsCommandLineParameter); + } +} diff --git a/libraries/rush-lib/src/cli/parsing/test/ParseParallelism.test.ts b/libraries/rush-lib/src/cli/parsing/test/ParseParallelism.test.ts deleted file mode 100644 index 7d2315d3d8a..00000000000 --- a/libraries/rush-lib/src/cli/parsing/test/ParseParallelism.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { parseParallelism } from '../ParseParallelism'; - -describe(parseParallelism.name, () => { - it('throwsErrorOnInvalidParallelism', () => { - expect(() => parseParallelism('tequila')).toThrowErrorMatchingSnapshot(); - }); - - it('createsWithPercentageBasedParallelism', () => { - const value: number = parseParallelism('50%', 20); - expect(value).toEqual(10); - }); - - it('throwsErrorOnInvalidParallelismPercentage', () => { - expect(() => parseParallelism('200%')).toThrowErrorMatchingSnapshot(); - }); -}); diff --git a/libraries/rush-lib/src/cli/parsing/test/__snapshots__/ParseParallelism.test.ts.snap b/libraries/rush-lib/src/cli/parsing/test/__snapshots__/ParseParallelism.test.ts.snap deleted file mode 100644 index c6ee56e6310..00000000000 --- a/libraries/rush-lib/src/cli/parsing/test/__snapshots__/ParseParallelism.test.ts.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`parseParallelism throwsErrorOnInvalidParallelism 1`] = `"Invalid parallelism value of 'tequila', expected a number, a percentage, or 'max'"`; - -exports[`parseParallelism throwsErrorOnInvalidParallelismPercentage 1`] = `"Invalid percentage value of '200%', value cannot be less than '0%' or more than '100%'"`; diff --git a/libraries/rush-lib/src/cli/scriptActions/BaseScriptAction.ts b/libraries/rush-lib/src/cli/scriptActions/BaseScriptAction.ts index 0719cb7af72..3d22215e517 100644 --- a/libraries/rush-lib/src/cli/scriptActions/BaseScriptAction.ts +++ b/libraries/rush-lib/src/cli/scriptActions/BaseScriptAction.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { CommandLineParameter } from '@rushstack/ts-command-line'; -import { BaseRushAction, IBaseRushActionOptions } from '../actions/BaseRushAction'; -import { Command, CommandLineConfiguration, IParameterJson } from '../../api/CommandLineConfiguration'; -import { RushConstants } from '../../logic/RushConstants'; -import type { ParameterJson } from '../../api/CommandLineJson'; +import type { CommandLineParameter } from '@rushstack/ts-command-line'; + +import { BaseRushAction, type IBaseRushActionOptions } from '../actions/BaseRushAction'; +import type { Command, CommandLineConfiguration, IParameterJson } from '../../api/CommandLineConfiguration'; +import { defineCustomParameters } from '../parsing/defineCustomParameters'; /** * Constructor parameters for BaseScriptAction @@ -41,83 +41,7 @@ export abstract class BaseScriptAction extends BaseRus return; } - // Find any parameters that are associated with this command - for (const parameter of this.command.associatedParameters) { - let tsCommandLineParameter: CommandLineParameter | undefined; - - switch (parameter.parameterKind) { - case 'flag': - tsCommandLineParameter = this.defineFlagParameter({ - parameterShortName: parameter.shortName, - parameterLongName: parameter.longName, - description: parameter.description, - required: parameter.required - }); - break; - case 'choice': - tsCommandLineParameter = this.defineChoiceParameter({ - parameterShortName: parameter.shortName, - parameterLongName: parameter.longName, - description: parameter.description, - required: parameter.required, - alternatives: parameter.alternatives.map((x) => x.name), - defaultValue: parameter.defaultValue - }); - break; - case 'string': - tsCommandLineParameter = this.defineStringParameter({ - parameterLongName: parameter.longName, - parameterShortName: parameter.shortName, - description: parameter.description, - required: parameter.required, - argumentName: parameter.argumentName - }); - break; - case 'integer': - tsCommandLineParameter = this.defineIntegerParameter({ - parameterLongName: parameter.longName, - parameterShortName: parameter.shortName, - description: parameter.description, - required: parameter.required, - argumentName: parameter.argumentName - }); - break; - case 'stringList': - tsCommandLineParameter = this.defineStringListParameter({ - parameterLongName: parameter.longName, - parameterShortName: parameter.shortName, - description: parameter.description, - required: parameter.required, - argumentName: parameter.argumentName - }); - break; - case 'integerList': - tsCommandLineParameter = this.defineIntegerListParameter({ - parameterLongName: parameter.longName, - parameterShortName: parameter.shortName, - description: parameter.description, - required: parameter.required, - argumentName: parameter.argumentName - }); - break; - case 'choiceList': - tsCommandLineParameter = this.defineChoiceListParameter({ - parameterShortName: parameter.shortName, - parameterLongName: parameter.longName, - description: parameter.description, - required: parameter.required, - alternatives: parameter.alternatives.map((x) => x.name) - }); - break; - default: - throw new Error( - `${RushConstants.commandLineFilename} defines a parameter "${ - (parameter as ParameterJson).longName - }" using an unsupported parameter kind "${(parameter as ParameterJson).parameterKind}"` - ); - } - - this.customParameters.set(parameter, tsCommandLineParameter); - } + // Use the centralized helper to create CommandLineParameter instances + defineCustomParameters(this, this.command.associatedParameters, this.customParameters); } } diff --git a/libraries/rush-lib/src/cli/scriptActions/GlobalScriptAction.ts b/libraries/rush-lib/src/cli/scriptActions/GlobalScriptAction.ts index e2d0f393851..0f0c38923e7 100644 --- a/libraries/rush-lib/src/cli/scriptActions/GlobalScriptAction.ts +++ b/libraries/rush-lib/src/cli/scriptActions/GlobalScriptAction.ts @@ -1,18 +1,28 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import colors from 'colors/safe'; +import * as path from 'node:path'; + import type { AsyncSeriesHook } from 'tapable'; -import { FileSystem, IPackageJson, JsonFile, AlreadyReportedError, Text } from '@rushstack/node-core-library'; +import type { CommandLineParameter } from '@rushstack/ts-command-line'; +import { + FileSystem, + type IPackageJson, + JsonFile, + AlreadyReportedError, + Text +} from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; import type { IGlobalCommand } from '../../pluginFramework/RushLifeCycle'; -import { BaseScriptAction, IBaseScriptActionOptions } from './BaseScriptAction'; +import { BaseScriptAction, type IBaseScriptActionOptions } from './BaseScriptAction'; import { Utilities } from '../../utilities/Utilities'; import { Stopwatch } from '../../utilities/Stopwatch'; import { Autoinstaller } from '../../logic/Autoinstaller'; +import { RushConstants } from '../../logic/RushConstants'; import type { IGlobalCommandConfig, IShellCommandTokenContext } from '../../api/CommandLineConfiguration'; +import { measureAsyncFn } from '../../utilities/performance'; /** * Constructor parameters for GlobalScriptAction. @@ -20,6 +30,7 @@ import type { IGlobalCommandConfig, IShellCommandTokenContext } from '../../api/ export interface IGlobalScriptActionOptions extends IBaseScriptActionOptions { shellCommand: string; autoinstallerName: string | undefined; + providedByPlugin: boolean; } /** @@ -36,11 +47,17 @@ export class GlobalScriptAction extends BaseScriptAction { private readonly _shellCommand: string; private readonly _autoinstallerName: string; private readonly _autoinstallerFullPath: string; + private readonly _providedByPlugin: boolean; + + private _customParametersByLongName: ReadonlyMap | undefined; + private _isHandled: boolean = false; public constructor(options: IGlobalScriptActionOptions) { super(options); - this._shellCommand = options.shellCommand; - this._autoinstallerName = options.autoinstallerName || ''; + const { shellCommand, providedByPlugin, autoinstallerName = '' } = options; + this._shellCommand = shellCommand; + this._providedByPlugin = providedByPlugin; + this._autoinstallerName = autoinstallerName; if (this._autoinstallerName) { Autoinstaller.validateName(this._autoinstallerName); @@ -85,7 +102,38 @@ export class GlobalScriptAction extends BaseScriptAction { this.defineScriptParameters(); } - private async _prepareAutoinstallerName(): Promise { + /** + * {@inheritDoc IGlobalCommand.setHandled} + */ + public setHandled(): void { + this._isHandled = true; + } + + /** + * {@inheritDoc IGlobalCommand.getCustomParametersByLongName} + */ + public getCustomParametersByLongName( + longName: string + ): TParameter { + if (!this._customParametersByLongName) { + const map: Map = new Map(); + for (const [parameterJson, parameter] of this.customParameters) { + map.set(parameterJson.longName, parameter); + } + this._customParametersByLongName = map; + } + + const parameter: CommandLineParameter | undefined = this._customParametersByLongName.get(longName); + if (!parameter) { + throw new Error( + `The command "${this.actionName}" does not have a custom parameter with long name "${longName}".` + ); + } + + return parameter as TParameter; + } + + private async _prepareAutoinstallerNameAsync(): Promise { const autoInstaller: Autoinstaller = new Autoinstaller({ autoinstallerName: this._autoinstallerName, rushConfiguration: this.rushConfiguration, @@ -109,11 +157,36 @@ export class GlobalScriptAction extends BaseScriptAction { await hookForAction.promise(this); } + // If a plugin hook called setHandled(), the command has been fully handled. + // Skip the default shell command execution. + if (this._isHandled) { + return; + } + + if (this._providedByPlugin) { + throw new Error( + `The custom command "${this.actionName}" is a "${RushConstants.globalPluginCommandKind}" command, ` + + 'meaning its implementation must be provided entirely by a Rush plugin. However, no plugin ' + + 'called setHandled() for this command. Ensure that the plugin defining this command is ' + + 'properly installed and that it handles this command.' + ); + } + + if (this._shellCommand === '') { + throw new Error( + `The custom command "${this.actionName}" has an empty "shellCommand" value, but no plugin ` + + 'called setHandled() for this command. An empty "shellCommand" is intended for global ' + + 'commands whose implementation is provided entirely by a Rush plugin.' + ); + } + const additionalPathFolders: string[] = this.commandLineConfiguration?.additionalPathFolders.slice() || []; if (this._autoinstallerName) { - await this._prepareAutoinstallerName(); + await measureAsyncFn('rush:globalScriptAction:prepareAutoinstaller', () => + this._prepareAutoinstallerNameAsync() + ); const autoinstallerNameBinPath: string = path.join(this._autoinstallerFullPath, 'node_modules', '.bin'); additionalPathFolders.push(autoinstallerNameBinPath); @@ -179,7 +252,8 @@ export class GlobalScriptAction extends BaseScriptAction { } if (exitCode > 0) { - console.log('\n' + colors.red(`The script failed with exit code ${exitCode}`)); + // eslint-disable-next-line no-console + console.log('\n' + Colorize.red(`The script failed with exit code ${exitCode}`)); throw new AlreadyReportedError(); } } diff --git a/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts b/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts index ba83fe56090..7b79e36e081 100644 --- a/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts +++ b/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts @@ -1,55 +1,104 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; +import { once } from 'node:events'; + import type { AsyncSeriesHook } from 'tapable'; -import { AlreadyReportedError, InternalError, ITerminal, Terminal } from '@rushstack/node-core-library'; -import { +import { AlreadyReportedError } from '@rushstack/node-core-library'; +import { type ITerminal, Terminal, Colorize, StdioWritable } from '@rushstack/terminal'; +import type { CommandLineFlagParameter, CommandLineParameter, CommandLineStringParameter } from '@rushstack/ts-command-line'; +import type { Subspace } from '../../api/Subspace'; import type { IPhasedCommand } from '../../pluginFramework/RushLifeCycle'; -import { PhasedCommandHooks, ICreateOperationsContext } from '../../pluginFramework/PhasedCommandHooks'; -import { SetupChecks } from '../../logic/SetupChecks'; -import { Stopwatch, StopwatchState } from '../../utilities/Stopwatch'; -import { BaseScriptAction, IBaseScriptActionOptions } from './BaseScriptAction'; import { - IOperationExecutionManagerOptions, - OperationExecutionManager -} from '../../logic/operations/OperationExecutionManager'; + type IOperationGraphContext, + PhasedCommandHooks, + type ICreateOperationsContext +} from '../../pluginFramework/PhasedCommandHooks'; +import type { IOperationGraphIterationOptions } from '../../logic/operations/IOperationGraph'; +import { SetupChecks } from '../../logic/SetupChecks'; +import { Stopwatch } from '../../utilities/Stopwatch'; +import { BaseScriptAction, type IBaseScriptActionOptions } from './BaseScriptAction'; +import type { IOperationGraphOptions, IOperationGraphTelemetry } from '../../logic/operations/OperationGraph'; +import { OperationGraph } from '../../logic/operations/OperationGraph'; import { RushConstants } from '../../logic/RushConstants'; import { EnvironmentVariableNames } from '../../api/EnvironmentConfiguration'; -import { LastLinkFlag, LastLinkFlagFactory } from '../../api/LastLinkFlag'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import { BuildCacheConfiguration } from '../../api/BuildCacheConfiguration'; import { SelectionParameterSet } from '../parsing/SelectionParameterSet'; import type { IPhase, IPhasedCommandConfig } from '../../api/CommandLineConfiguration'; -import { Operation } from '../../logic/operations/Operation'; -import { OperationExecutionRecord } from '../../logic/operations/OperationExecutionRecord'; +import type { Operation } from '../../logic/operations/Operation'; +import { associateParametersByPhase } from '../parsing/associateParametersByPhase'; import { PhasedOperationPlugin } from '../../logic/operations/PhasedOperationPlugin'; import { ShellOperationRunnerPlugin } from '../../logic/operations/ShellOperationRunnerPlugin'; import { Event } from '../../api/EventHooks'; import { ProjectChangeAnalyzer } from '../../logic/ProjectChangeAnalyzer'; import { OperationStatus } from '../../logic/operations/OperationStatus'; -import { IExecutionResult } from '../../logic/operations/IOperationExecutionResult'; +import type { IExecutionResult } from '../../logic/operations/IOperationExecutionResult'; import { OperationResultSummarizerPlugin } from '../../logic/operations/OperationResultSummarizerPlugin'; -import type { ITelemetryData, ITelemetryOperationResult } from '../../logic/Telemetry'; -import { parseParallelism } from '../parsing/ParseParallelism'; +import type { ITelemetryData } from '../../logic/Telemetry'; +import { + getNumberOfCores, + parseParallelism, + type Parallelism +} from '../../logic/operations/ParseParallelism'; +import { CobuildConfiguration } from '../../api/CobuildConfiguration'; +import { CacheableOperationPlugin } from '../../logic/operations/CacheableOperationPlugin'; +import type { IInputsSnapshot, GetInputsSnapshotAsyncFn } from '../../logic/incremental/InputsSnapshot'; +import { RushProjectConfiguration } from '../../api/RushProjectConfiguration'; +import { LegacySkipPlugin } from '../../logic/operations/LegacySkipPlugin'; +import { ValidateOperationsPlugin } from '../../logic/operations/ValidateOperationsPlugin'; +import { ShardedPhasedOperationPlugin } from '../../logic/operations/ShardedPhaseOperationPlugin'; +import { FlagFile } from '../../api/FlagFile'; +import { getVariantAsync, VARIANT_PARAMETER } from '../../api/Variants'; +import { Selection } from '../../logic/Selection'; +import { NodeDiagnosticDirPlugin } from '../../logic/operations/NodeDiagnosticDirPlugin'; +import { IgnoredParametersPlugin } from '../../logic/operations/IgnoredParametersPlugin'; +import { DebugHashesPlugin } from '../../logic/operations/DebugHashesPlugin'; +import { measureAsyncFn, measureFn } from '../../utilities/performance'; + +const PERF_PREFIX: 'rush:phasedScriptAction' = 'rush:phasedScriptAction'; + +/** + * The set of overall execution statuses that mean the command did what was asked of it and should + * exit with code 0. + * + * - `NoOp` -- the iteration scheduled no non-silent operations. This happens when a plugin + * legitimately consumes the work itself, either by returning an empty operation set from + * `createOperationsAsync` or by disabling every operation during `configureIteration` (a disabled + * record is silent, so both routes converge on this status). + * - `Skipped` / `FromCache` -- a tap short-circuited the iteration with a successful bail status, + * for example the bridge-cache plugin performing a cache read/write out of band. + * + * `PhasedScriptAction` already treats an empty *project* selection as success, so treating an empty + * *operation* set as a failure would be inconsistent. `SuccessWithWarning` is deliberately excluded + * because non-allowed warnings are expected to fail the command. + */ +const SUCCESSFUL_EXECUTION_STATUSES: ReadonlySet = new Set([ + OperationStatus.Success, + OperationStatus.Skipped, + OperationStatus.FromCache, + OperationStatus.NoOp +]); /** * Constructor parameters for PhasedScriptAction. */ export interface IPhasedScriptActionOptions extends IBaseScriptActionOptions { enableParallelism: boolean; + allowOversubscription: boolean; incremental: boolean; disableBuildCache: boolean; originalPhases: Set; initialPhases: Set; watchPhases: Set; + includeAllProjectsInWatchGraph: boolean; phases: Map; alwaysWatch: boolean; @@ -58,37 +107,14 @@ export interface IPhasedScriptActionOptions extends IBaseScriptActionOptions; + isWatch: boolean; stopwatch: Stopwatch; terminal: ITerminal; } -interface IPhasedCommandTelemetry { - [key: string]: string | number | boolean; - isInitial: boolean; - isWatch: boolean; - - countAll: number; - countSuccess: number; - countSuccessWithWarnings: number; - countFailure: number; - countBlocked: number; - countFromCache: number; - countSkipped: number; - countNoOp: number; -} - /** * This class implements phased commands which are run individually for each project in the repo, * possibly in parallel, and which may define multiple phases. @@ -98,10 +124,16 @@ interface IPhasedCommandTelemetry { * and "rebuild" commands are also modeled as phased commands with a single phase that invokes the npm * "build" script for each project. */ -export class PhasedScriptAction extends BaseScriptAction { +export class PhasedScriptAction extends BaseScriptAction implements IPhasedCommand { + /** + * @internal + */ + public _runsBeforeInstall: boolean | undefined; public readonly hooks: PhasedCommandHooks; + public readonly sessionAbortController: AbortController; private readonly _enableParallelism: boolean; + private readonly _allowOversubscription: boolean; private readonly _isIncrementalBuildAllowed: boolean; private readonly _disableBuildCache: boolean; private readonly _originalPhases: ReadonlySet; @@ -110,67 +142,95 @@ export class PhasedScriptAction extends BaseScriptAction { private readonly _watchDebounceMs: number; private readonly _alwaysWatch: boolean; private readonly _alwaysInstall: boolean | undefined; - private readonly _knownPhases: ReadonlyMap; + private readonly _includeAllProjectsInWatchGraph: boolean; private readonly _terminal: ITerminal; - private readonly _changedProjectsOnly: CommandLineFlagParameter | undefined; + private readonly _changedProjectsOnlyParameter: CommandLineFlagParameter | undefined; private readonly _selectionParameters: SelectionParameterSet; private readonly _verboseParameter: CommandLineFlagParameter; private readonly _parallelismParameter: CommandLineStringParameter | undefined; private readonly _ignoreHooksParameter: CommandLineFlagParameter; private readonly _watchParameter: CommandLineFlagParameter | undefined; private readonly _timelineParameter: CommandLineFlagParameter | undefined; + private readonly _cobuildPlanParameter: CommandLineFlagParameter | undefined; private readonly _installParameter: CommandLineFlagParameter | undefined; + private readonly _variantParameter: CommandLineStringParameter | undefined; + private readonly _noIPCParameter: CommandLineFlagParameter | undefined; + private readonly _nodeDiagnosticDirParameter: CommandLineStringParameter; + private readonly _debugBuildCacheIdsParameter: CommandLineFlagParameter; + private readonly _includePhaseDeps: CommandLineFlagParameter | undefined; public constructor(options: IPhasedScriptActionOptions) { super(options); - this._enableParallelism = options.enableParallelism; - this._isIncrementalBuildAllowed = options.incremental; - this._disableBuildCache = options.disableBuildCache; - this._originalPhases = options.originalPhases; - this._initialPhases = options.initialPhases; - this._watchPhases = options.watchPhases; - this._watchDebounceMs = options.watchDebounceMs ?? RushConstants.defaultWatchDebounceMs; - this._alwaysWatch = options.alwaysWatch; - this._alwaysInstall = options.alwaysInstall; - this._knownPhases = options.phases; + const { + enableParallelism, + allowOversubscription, + incremental, + disableBuildCache, + originalPhases, + initialPhases, + watchPhases, + watchDebounceMs = RushConstants.defaultWatchDebounceMs, + alwaysWatch, + alwaysInstall, + includeAllProjectsInWatchGraph, + phases + } = options; + this._enableParallelism = enableParallelism; + this._allowOversubscription = allowOversubscription; + this._isIncrementalBuildAllowed = incremental; + this._disableBuildCache = disableBuildCache; + this._originalPhases = originalPhases; + this._initialPhases = initialPhases; + this._watchPhases = watchPhases; + this._watchDebounceMs = watchDebounceMs; + this._alwaysWatch = alwaysWatch; + this._alwaysInstall = alwaysInstall; + this._includeAllProjectsInWatchGraph = includeAllProjectsInWatchGraph; + this._runsBeforeInstall = false; + this.sessionAbortController = new AbortController(); this.hooks = new PhasedCommandHooks(); - const terminal: Terminal = new Terminal(this.rushSession.terminalProvider); - this._terminal = terminal; - - // Generates the default operation graph - new PhasedOperationPlugin().apply(this.hooks); - // Applies the Shell Operation Runner to selected operations - new ShellOperationRunnerPlugin().apply(this.hooks); - - if (this._enableParallelism) { - this._parallelismParameter = this.defineStringParameter({ - parameterLongName: '--parallelism', - parameterShortName: '-p', - argumentName: 'COUNT', - environmentVariable: EnvironmentVariableNames.RUSH_PARALLELISM, - description: - 'Specifies the maximum number of concurrent processes to launch during a build.' + - ' The COUNT should be a positive integer, a percentage value (eg. "50%%") or the word "max"' + - ' to specify a count that is equal to the number of CPU cores. If this parameter is omitted,' + - ' then the default value depends on the operating system and number of CPU cores.' - }); - this._timelineParameter = this.defineFlagParameter({ - parameterLongName: '--timeline', - description: - 'After the build is complete, print additional statistics and CPU usage information,' + - ' including an ASCII chart of the start and stop times for each operation.' - }); - } + this._terminal = new Terminal(this.rushSession.terminalProvider); + + this._parallelismParameter = this._enableParallelism + ? this.defineStringParameter({ + parameterLongName: '--parallelism', + parameterShortName: '-p', + argumentName: 'COUNT', + environmentVariable: EnvironmentVariableNames.RUSH_PARALLELISM, + description: + 'Specifies the maximum number of concurrent processes to launch during a build.' + + ' The COUNT should be a positive integer, a percentage value (eg. "50%") or the word "max"' + + ' to specify a count that is equal to the number of CPU cores. If this parameter is omitted,' + + ' then the default value depends on the operating system and number of CPU cores.' + }) + : undefined; + + this._timelineParameter = this.defineFlagParameter({ + parameterLongName: '--timeline', + description: + 'After the build is complete, print additional statistics and CPU usage information,' + + ' including an ASCII chart of the start and stop times for each operation.' + }); + this._cobuildPlanParameter = this.defineFlagParameter({ + parameterLongName: '--log-cobuild-plan', + description: + '(EXPERIMENTAL) Before the build starts, log information about the cobuild state. This will include information about ' + + 'clusters and the projects that are part of each cluster.' + }); this._selectionParameters = new SelectionParameterSet(this.rushConfiguration, this, { - // Include lockfile processing since this expands the selection, and we need to select - // at least the same projects selected with the same query to "rush build" - includeExternalDependencies: true, - // Enable filtering to reduce evaluation cost - enableFiltering: true + gitOptions: { + // Include lockfile processing since this expands the selection, and we need to select + // at least the same projects selected with the same query to "rush build" + includeExternalDependencies: true, + // Enable filtering to reduce evaluation cost + enableFiltering: true + }, + includeSubspaceSelector: false, + cwd: this.parser.cwd }); this._verboseParameter = this.defineFlagParameter({ @@ -179,114 +239,200 @@ export class PhasedScriptAction extends BaseScriptAction { description: 'Display the logs during the build, rather than just displaying the build status summary' }); - if (this._isIncrementalBuildAllowed) { - this._changedProjectsOnly = this.defineFlagParameter({ - parameterLongName: '--changed-projects-only', - parameterShortName: '-c', - description: - 'Normally the incremental build logic will rebuild changed projects as well as' + - ' any projects that directly or indirectly depend on a changed project. Specify "--changed-projects-only"' + - ' to ignore dependent projects, only rebuilding those projects whose files were changed.' + - ' Note that this parameter is "unsafe"; it is up to the developer to ensure that the ignored projects' + - ' are okay to ignore.' - }); - } + this._includePhaseDeps = this.defineFlagParameter({ + parameterLongName: '--include-phase-deps', + description: + 'If the selected projects are "unsafe" (missing some dependencies), add the minimal set of phase dependencies. For example, ' + + `"--from A" normally might include the "_phase:test" phase for A's dependencies, even though changes to A can't break those tests. ` + + `Using "--impacted-by A --include-phase-deps" avoids that work by performing "_phase:test" only for downstream projects.` + }); + + this._changedProjectsOnlyParameter = this._isIncrementalBuildAllowed + ? this.defineFlagParameter({ + parameterLongName: '--changed-projects-only', + parameterShortName: '-c', + description: + 'Normally the incremental build logic will rebuild changed projects as well as' + + ' any projects that directly or indirectly depend on a changed project. Specify "--changed-projects-only"' + + ' to ignore dependent projects, only rebuilding those projects whose files were changed.' + + ' Note that this parameter is "unsafe"; it is up to the developer to ensure that the ignored projects' + + ' are okay to ignore.' + }) + : undefined; this._ignoreHooksParameter = this.defineFlagParameter({ parameterLongName: '--ignore-hooks', - description: `Skips execution of the "eventHooks" scripts defined in rush.json. Make sure you know what you are skipping.` + description: + `Skips execution of the "eventHooks" scripts defined in ${RushConstants.rushJsonFilename}. ` + + 'Make sure you know what you are skipping.' }); - if (this._watchPhases.size > 0 && !this._alwaysWatch) { - // Only define the parameter if it has an effect. - this._watchParameter = this.defineFlagParameter({ - parameterLongName: '--watch', - description: `Starts a file watcher after initial execution finishes. Will run the following phases on affected projects: ${Array.from( - this._watchPhases, - (phase: IPhase) => phase.name - ).join(', ')}` - }); - } + // Only define the parameter if it has an effect. + this._watchParameter = + this._watchPhases.size > 0 && !this._alwaysWatch + ? this.defineFlagParameter({ + parameterLongName: '--watch', + description: `Starts a file watcher after initial execution finishes. Will run the following phases on affected projects: ${Array.from( + this._watchPhases, + (phase: IPhase) => phase.name + ).join(', ')}` + }) + : undefined; // If `this._alwaysInstall === undefined`, Rush does not define the parameter // but a repository may still define a custom parameter with the same name. - if (this._alwaysInstall === false) { - this._installParameter = this.defineFlagParameter({ - parameterLongName: '--install', - description: - 'Normally a phased command expects "rush install" to have been manually run first. If this flag is specified, ' + - 'Rush will automatically perform an install before processing the current command.' - }); - } + this._installParameter = + this._alwaysInstall === false + ? this.defineFlagParameter({ + parameterLongName: '--install', + description: + 'Normally a phased command expects "rush install" to have been manually run first. If this flag is specified, ' + + 'Rush will automatically perform an install before processing the current command.' + }) + : undefined; + + this._variantParameter = + this._alwaysInstall !== undefined ? this.defineStringParameter(VARIANT_PARAMETER) : undefined; + + const isIpcSupported: boolean = + this._watchPhases.size > 0 && + !!this.rushConfiguration.experimentsConfiguration.configuration.useIPCScriptsInWatchMode; + this._noIPCParameter = isIpcSupported + ? this.defineFlagParameter({ + parameterLongName: '--no-ipc', + description: + 'Disables the IPC feature for the current command (if applicable to selected operations). Operations will not look for a ":ipc" suffixed script.' + + 'This feature only applies in watch mode and is enabled by default.' + }) + : undefined; + + this._nodeDiagnosticDirParameter = this.defineStringParameter({ + parameterLongName: '--node-diagnostic-dir', + argumentName: 'DIRECTORY', + description: + 'Specifies the directory where Node.js diagnostic reports will be written. ' + + 'This directory will contain a subdirectory for each project and phase.' + }); + + this._debugBuildCacheIdsParameter = this.defineFlagParameter({ + parameterLongName: '--debug-build-cache-ids', + description: + 'Logs information about the components of the build cache ids for individual operations. This is useful for debugging the incremental build logic.' + }); this.defineScriptParameters(); - for (const [{ associatedPhases }, tsCommandLineParameter] of this.customParameters) { - if (associatedPhases) { - for (const phaseName of associatedPhases) { - const phase: IPhase | undefined = this._knownPhases.get(phaseName); - if (!phase) { - throw new InternalError(`Could not find a phase matching ${phaseName}.`); - } - phase.associatedParameters.add(tsCommandLineParameter); - } - } - } + // Associate parameters with their respective phases + associateParametersByPhase(this.customParameters, phases); } public async runAsync(): Promise { + // Initialize the stopwatch's start time at 0 (process startup). + const stopwatch: Stopwatch = Stopwatch.start(0); + + const { + defaultSubspace, + subspacesFeatureEnabled, + pnpmOptions: { useWorkspaces } + } = this.rushConfiguration; if (this._alwaysInstall || this._installParameter?.value) { - const { doBasicInstallAsync } = await import( - /* webpackChunkName: 'doBasicInstallAsync' */ - '../../logic/installManager/doBasicInstallAsync' - ); - - await doBasicInstallAsync({ - rushConfiguration: this.rushConfiguration, - rushGlobalFolder: this.rushGlobalFolder, - isDebug: this.parser.isDebug + await measureAsyncFn(`${PERF_PREFIX}:install`, async () => { + const { doBasicInstallAsync } = await import( + /* webpackChunkName: 'doBasicInstallAsync' */ + '../../logic/installManager/doBasicInstallAsync' + ); + + const variant: string | undefined = await getVariantAsync( + this._variantParameter, + this.rushConfiguration, + true + ); + await doBasicInstallAsync({ + terminal: this._terminal, + rushConfiguration: this.rushConfiguration, + rushGlobalFolder: this.rushGlobalFolder, + isDebug: this.parser.isDebug, + variant, + beforeInstallAsync: (subspace: Subspace) => + this.rushSession.hooks.beforeInstall.promise(this, subspace, variant), + afterInstallAsync: (subspace: Subspace) => + this.rushSession.hooks.afterInstall.promise(this, subspace, variant), + // Eventually we may want to allow a subspace to be selected here + subspace: defaultSubspace + }); }); } - // TODO: Replace with last-install.flag when "rush link" and "rush unlink" are deprecated - const lastLinkFlag: LastLinkFlag = LastLinkFlagFactory.getCommonTempFlag(this.rushConfiguration); - if (!lastLinkFlag.isValid()) { - const useWorkspaces: boolean = - this.rushConfiguration.pnpmOptions && this.rushConfiguration.pnpmOptions.useWorkspaces; - if (useWorkspaces) { - throw new Error('Link flag invalid.\nDid you run "rush install" or "rush update"?'); - } else { - throw new Error('Link flag invalid.\nDid you run "rush link"?'); - } + if (!this._runsBeforeInstall) { + await measureAsyncFn(`${PERF_PREFIX}:checkInstallFlag`, async () => { + // TODO: Replace with last-install.flag when "rush link" and "rush unlink" are removed + const lastLinkFlag: FlagFile = new FlagFile( + defaultSubspace.getSubspaceTempFolderPath(), + RushConstants.lastLinkFlagFilename, + {} + ); + // Only check for a valid link flag when subspaces is not enabled + if (!(await lastLinkFlag.isValidAsync()) && !subspacesFeatureEnabled) { + if (useWorkspaces) { + throw new Error('Link flag invalid.\nDid you run "rush install" or "rush update"?'); + } else { + throw new Error('Link flag invalid.\nDid you run "rush link"?'); + } + } + }); } - this._doBeforeTask(); + measureFn(`${PERF_PREFIX}:doBeforeTask`, () => this._doBeforeTask()); + + const hooks: PhasedCommandHooks = this.hooks; + const terminal: ITerminal = this._terminal; // if this is parallelizable, then use the value from the flag (undefined or a number), // if parallelism is not enabled, then restrict to 1 core - const parallelism: number = this._enableParallelism + const maxParallelism: number = getNumberOfCores(); + const parallelism: Parallelism = this._enableParallelism ? parseParallelism(this._parallelismParameter?.value) : 1; - const terminal: ITerminal = this._terminal; + await measureAsyncFn(`${PERF_PREFIX}:applyStandardPlugins`, async () => { + // Generates the default operation graph + new PhasedOperationPlugin().apply(hooks); + // Splices in sharded phases to the operation graph. + new ShardedPhasedOperationPlugin().apply(hooks); + // Applies the Shell Operation Runner to selected operations + new ShellOperationRunnerPlugin().apply(hooks); + // Verifies correctness of rush-project.json entries for the graph + new ValidateOperationsPlugin(terminal).apply(hooks); + + // Forward ignored parameters to child processes as an environment variable + new IgnoredParametersPlugin().apply(hooks); + + const showTimeline: boolean = this._timelineParameter?.value ?? false; + if (showTimeline) { + const { ConsoleTimelinePlugin } = await import( + /* webpackChunkName: 'ConsoleTimelinePlugin' */ + '../../logic/operations/ConsoleTimelinePlugin' + ); + new ConsoleTimelinePlugin(terminal).apply(this.hooks); + } - const stopwatch: Stopwatch = Stopwatch.start(); + const diagnosticDir: string | undefined = this._nodeDiagnosticDirParameter.value; + if (diagnosticDir) { + new NodeDiagnosticDirPlugin({ + diagnosticDir + }).apply(this.hooks); + } - const showTimeline: boolean = this._timelineParameter ? this._timelineParameter.value : false; - if (showTimeline) { - const { ConsoleTimelinePlugin } = await import( - /* webpackChunkName: 'ConsoleTimelinePlugin' */ - '../../logic/operations/ConsoleTimelinePlugin' - ); - new ConsoleTimelinePlugin(terminal).apply(this.hooks); - } - // Enable the standard summary - new OperationResultSummarizerPlugin(terminal).apply(this.hooks); + // Enable the standard summary + new OperationResultSummarizerPlugin(terminal).apply(this.hooks); + }); const { hooks: sessionHooks } = this.rushSession; if (sessionHooks.runAnyPhasedCommand.isUsed()) { - // Avoid the cost of compiling the hook if it wasn't tapped. - await sessionHooks.runAnyPhasedCommand.promise(this); + await measureAsyncFn(`${PERF_PREFIX}:runAnyPhasedCommand`, async () => { + // Avoid the cost of compiling the hook if it wasn't tapped. + await sessionHooks.runAnyPhasedCommand.promise(this); + }); } const hookForAction: AsyncSeriesHook | undefined = sessionHooks.runPhasedCommand.get( @@ -294,208 +440,287 @@ export class PhasedScriptAction extends BaseScriptAction { ); if (hookForAction) { - // Run the more specific hook for a command with this name after the general hook - await hookForAction.promise(this); + await measureAsyncFn(`${PERF_PREFIX}:runPhasedCommand`, async () => { + // Run the more specific hook for a command with this name after the general hook + await hookForAction.promise(this); + }); } const isQuietMode: boolean = !this._verboseParameter.value; - const changedProjectsOnly: boolean = !!this._changedProjectsOnly?.value; + const changedProjectsOnly: boolean = !!this._changedProjectsOnlyParameter?.value; let buildCacheConfiguration: BuildCacheConfiguration | undefined; + let cobuildConfiguration: CobuildConfiguration | undefined; if (!this._disableBuildCache) { - buildCacheConfiguration = await BuildCacheConfiguration.tryLoadAsync( - terminal, - this.rushConfiguration, - this.rushSession - ); - } - - const projectSelection: Set = - await this._selectionParameters.getSelectedProjectsAsync(terminal); - - if (!projectSelection.size) { - terminal.writeLine(colors.yellow(`The command line selection parameters did not match any projects.`)); - return; + await measureAsyncFn(`${PERF_PREFIX}:configureBuildCache`, async () => { + [buildCacheConfiguration, cobuildConfiguration] = await Promise.all([ + BuildCacheConfiguration.tryLoadAsync(terminal, this.rushConfiguration, this.rushSession), + CobuildConfiguration.tryLoadAsync(terminal, this.rushConfiguration, this.rushSession).then( + async (cobuildCfg: CobuildConfiguration | undefined) => { + if (cobuildCfg) { + await cobuildCfg.createLockProviderAsync(terminal); + } + return cobuildCfg; + } + ) + ]); + }); } const isWatch: boolean = this._watchParameter?.value || this._alwaysWatch; + const generateFullGraph: boolean = isWatch && this._includeAllProjectsInWatchGraph; - const customParametersByName: Map = new Map(); - for (const [configParameter, parserParameter] of this.customParameters) { - customParametersByName.set(configParameter.longName, parserParameter); - } + try { + const projectSelection: Set = await measureAsyncFn( + `${PERF_PREFIX}:getSelectedProjects`, + () => this._selectionParameters.getSelectedProjectsAsync(terminal, generateFullGraph) + ); - const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(this.rushConfiguration); - const initialCreateOperationsContext: ICreateOperationsContext = { - buildCacheConfiguration, - customParameters: customParametersByName, - isIncrementalBuildAllowed: this._isIncrementalBuildAllowed, - isInitial: true, - isWatch, - rushConfiguration: this.rushConfiguration, - phaseOriginal: new Set(this._originalPhases), - phaseSelection: new Set(this._initialPhases), - projectChangeAnalyzer, - projectSelection, - projectsInUnknownState: projectSelection - }; - - const executionManagerOptions: IOperationExecutionManagerOptions = { - quietMode: isQuietMode, - debugMode: this.parser.isDebug, - parallelism, - changedProjectsOnly, - beforeExecuteOperations: async (records: Map) => { - await this.hooks.beforeExecuteOperations.promise(records); - }, - onOperationStatusChanged: (record: OperationExecutionRecord) => { - this.hooks.onOperationStatusChanged.call(record); - } - }; - - const internalOptions: IRunPhasesOptions = { - initialCreateOperationsContext, - executionManagerOptions, - stopwatch, - terminal - }; - - terminal.write('Analyzing repo state... '); - const repoStateStopwatch: Stopwatch = new Stopwatch(); - repoStateStopwatch.start(); - await projectChangeAnalyzer._ensureInitializedAsync(terminal); - repoStateStopwatch.stop(); - terminal.writeLine(`DONE (${repoStateStopwatch.toString()})`); - terminal.writeLine(); - - await this._runInitialPhases(internalOptions); - - if (isWatch) { - if (buildCacheConfiguration) { - // Cache writes are not supported during watch mode, only reads. - buildCacheConfiguration.cacheWriteEnabled = false; + const customParametersByName: Map = new Map(); + for (const [configParameter, parserParameter] of this.customParameters) { + customParametersByName.set(configParameter.longName, parserParameter); } - await this._runWatchPhases(internalOptions); - } - } + if (!generateFullGraph && !projectSelection.size) { + terminal.writeLine( + Colorize.yellow(`The command line selection parameters did not match any projects.`) + ); + return; + } - private async _runInitialPhases(options: IRunPhasesOptions): Promise { - const { initialCreateOperationsContext, executionManagerOptions, stopwatch, terminal } = options; + await measureAsyncFn(`${PERF_PREFIX}:applySituationalPlugins`, async () => { + if (isWatch && this._noIPCParameter?.value === false) { + new ( + await import( + /* webpackChunkName: 'IPCOperationRunnerPlugin' */ '../../logic/operations/IPCOperationRunnerPlugin' + ) + ).IPCOperationRunnerPlugin().apply(this.hooks); + } - const operations: Set = await this.hooks.createOperations.promise( - new Set(), - initialCreateOperationsContext - ); + const { + experimentsConfiguration: { + configuration: { + buildCacheWithAllowWarningsInSuccessfulBuild = false, + buildSkipWithAllowWarningsInSuccessfulBuild, + omitAppleDoubleFilesFromBuildCache: excludeAppleDoubleFiles = false, + useDirectFileTransfersForBuildCache = false, + usePnpmSyncForInjectedDependencies + } + }, + isPnpm + } = this.rushConfiguration; + if (buildCacheConfiguration?.buildCacheEnabled) { + terminal.writeVerboseLine(`Incremental strategy: cache restoration`); + new CacheableOperationPlugin({ + allowWarningsInSuccessfulBuild: buildCacheWithAllowWarningsInSuccessfulBuild, + buildCacheConfiguration, + cobuildConfiguration, + terminal, + excludeAppleDoubleFiles, + useDirectFileTransfersForBuildCache + }).apply(this.hooks); + + if (this._debugBuildCacheIdsParameter.value) { + new DebugHashesPlugin(terminal).apply(this.hooks); + } + } else if (!this._disableBuildCache) { + terminal.writeVerboseLine(`Incremental strategy: output preservation`); + // Explicitly disabling the build cache also disables legacy skip detection. + new LegacySkipPlugin({ + allowWarningsInSuccessfulBuild: buildSkipWithAllowWarningsInSuccessfulBuild, + terminal, + changedProjectsOnly, + isIncrementalBuildAllowed: this._isIncrementalBuildAllowed + }).apply(this.hooks); + } else { + terminal.writeVerboseLine(`Incremental strategy: none (full rebuild)`); + } - const initialOptions: IExecutionOperationsOptions = { - createOperationsContext: initialCreateOperationsContext, - ignoreHooks: false, - operations, - stopwatch, - executionManagerOptions, - terminal - }; + const showBuildPlan: boolean = this._cobuildPlanParameter?.value ?? false; - await this._executeOperations(initialOptions); - } + if (showBuildPlan) { + if (!buildCacheConfiguration?.buildCacheEnabled) { + throw new Error('You must have build cache enabled to use this option.'); + } - /** - * Runs the command in watch mode. Fundamentally is a simple loop: - * 1) Wait for a change to one or more projects in the selection - * 2) Invoke the command on the changed projects, and, if applicable, impacted projects - * Uses the same algorithm as --impacted-by - * 3) Goto (1) - */ - private async _runWatchPhases(options: IRunPhasesOptions): Promise { - const { initialCreateOperationsContext, executionManagerOptions, stopwatch, terminal } = options; + const { BuildPlanPlugin } = await import('../../logic/operations/BuildPlanPlugin'); + new BuildPlanPlugin(terminal).apply(this.hooks); + } - const phaseOriginal: Set = new Set(this._watchPhases); - const phaseSelection: Set = new Set(this._watchPhases); + if (isPnpm && usePnpmSyncForInjectedDependencies) { + const { PnpmSyncCopyOperationPlugin } = await import( + '../../logic/operations/PnpmSyncCopyOperationPlugin' + ); + new PnpmSyncCopyOperationPlugin(terminal).apply(this.hooks); + } + }); - const { projectChangeAnalyzer: initialState, projectSelection: projectsToWatch } = - initialCreateOperationsContext; + const relevantProjects: Set = generateFullGraph + ? new Set(this.rushConfiguration.projects) + : Selection.expandAllDependencies(projectSelection); - // Use async import so that we don't pay the cost for sync builds - const { ProjectWatcher } = await import( - /* webpackChunkName: 'ProjectWatcher' */ - '../../logic/ProjectWatcher' - ); + const projectConfigurations: ReadonlyMap = this + ._runsBeforeInstall + ? new Map() + : await measureAsyncFn(`${PERF_PREFIX}:loadProjectConfigurations`, () => + RushProjectConfiguration.tryLoadForProjectsAsync(relevantProjects, terminal) + ); - const projectWatcher: typeof ProjectWatcher.prototype = new ProjectWatcher({ - debounceMs: this._watchDebounceMs, - rushConfiguration: this.rushConfiguration, - projectsToWatch, - terminal, - initialState - }); + const includePhaseDeps: boolean = this._includePhaseDeps?.value ?? false; - const onWaitingForChanges = (): void => { - // Allow plugins to display their own messages when waiting for changes. - this.hooks.waitingForChanges.call(); + const createOperationsContext: ICreateOperationsContext = { + buildCacheConfiguration, + cobuildConfiguration, + customParameters: customParametersByName, + changedProjectsOnly, + includePhaseDeps, + isIncrementalBuildAllowed: this._isIncrementalBuildAllowed, + isWatch, + rushConfiguration: this.rushConfiguration, + parallelism, + phaseSelection: isWatch + ? this._watchPhases + : includePhaseDeps + ? this._originalPhases + : this._initialPhases, + projectSelection, + generateFullGraph, + projectConfigurations + }; - // Report so that the developer can always see that it is in watch mode as the latest console line. - terminal.writeLine( - `Watching for changes to ${projectsToWatch.size} ${ - projectsToWatch.size === 1 ? 'project' : 'projects' - }. Press Ctrl+C to exit.` + const operations: Set = await measureAsyncFn(`${PERF_PREFIX}:createOperations`, () => + this.hooks.createOperationsAsync.promise(new Set(), createOperationsContext) ); - }; - - // Loop until Ctrl+C - // eslint-disable-next-line no-constant-condition - while (true) { - // On the initial invocation, this promise will return immediately with the full set of projects - const { changedProjects, state } = await projectWatcher.waitForChange(onWaitingForChanges); - - if (stopwatch.state === StopwatchState.Stopped) { - // Clear and reset the stopwatch so that we only report time from a single execution at a time - stopwatch.reset(); - stopwatch.start(); - } - terminal.writeLine( - `Detected changes in ${changedProjects.size} project${changedProjects.size === 1 ? '' : 's'}:` + const [getInputsSnapshotAsync, initialSnapshot] = await measureAsyncFn( + `${PERF_PREFIX}:analyzeRepoState`, + async () => { + terminal.write('Analyzing repo state... '); + const repoStateStopwatch: Stopwatch = new Stopwatch(); + repoStateStopwatch.start(); + + const analyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(this.rushConfiguration); + const innerGetInputsSnapshotAsync: GetInputsSnapshotAsyncFn | undefined = + await analyzer._tryGetSnapshotProviderAsync( + projectConfigurations, + terminal, + // We need to include all dependencies, otherwise build cache id calculation will be incorrect + relevantProjects + ); + const innerInitialSnapshot: IInputsSnapshot | undefined = innerGetInputsSnapshotAsync + ? await innerGetInputsSnapshotAsync() + : undefined; + + repoStateStopwatch.stop(); + terminal.writeLine(`DONE (${repoStateStopwatch.toString()})`); + terminal.writeLine(); + return [innerGetInputsSnapshotAsync, innerInitialSnapshot]; + } ); - const names: string[] = [...changedProjects].map((x) => x.packageName).sort(); - for (const name of names) { - terminal.writeLine(` ${colors.cyan(name)}`); + + let executionTelemetryHandler: IOperationGraphTelemetry | undefined; + const { telemetry: parserTelemetry } = this.parser; + if (parserTelemetry) { + const { _changedProjectsOnlyParameter: changedProjectsOnlyParameter } = this; + executionTelemetryHandler = { + changedProjectsOnlyKey: + changedProjectsOnlyParameter?.scopedLongName ?? changedProjectsOnlyParameter?.longName, + initialExtraData: { + // Fields preserved across the command invocation + ...this._selectionParameters.getTelemetry(), + ...this.getParameterStringMap() + }, + nameForLog: this.actionName, + log: (logEntry: ITelemetryData) => { + parserTelemetry.log(logEntry); + parserTelemetry.flush(); + } + }; } - // Account for consumer relationships - const createOperationsContext: ICreateOperationsContext = { - ...initialCreateOperationsContext, - isInitial: false, - projectChangeAnalyzer: state, - projectsInUnknownState: changedProjects, - phaseOriginal, - phaseSelection + const graphOptions: IOperationGraphOptions = { + quietMode: isQuietMode, + debugMode: this.parser.isDebug, + destinations: [StdioWritable.instance], + parallelism, + maxParallelism, + allowOversubscription: this._allowOversubscription, + isWatch, + pauseNextIteration: false, + getInputsSnapshotAsync, + abortController: this.sessionAbortController, + telemetry: executionTelemetryHandler }; - const operations: Set = await this.hooks.createOperations.promise( - new Set(), - createOperationsContext - ); + const graph: OperationGraph = new OperationGraph(operations, graphOptions); - const executeOptions: IExecutionOperationsOptions = { - createOperationsContext, - // For now, don't run pre-build or post-build in watch mode - ignoreHooks: true, - operations, + const graphContext: IOperationGraphContext = { + ...createOperationsContext, + initialSnapshot + }; + + const abortPromise: Promise = once(this.sessionAbortController.signal, 'abort').then(async () => { + terminal.writeLine(`Shutting down Rush...`); + return await graph.abortCurrentIterationAsync(); + }); + + await measureAsyncFn(`${PERF_PREFIX}:executionManager`, async () => { + await hooks.onGraphCreatedAsync.promise(graph, graphContext); + }); + + const executeOptions: IExecuteOperationsOptions = { + graph, + ignoreHooks: !!this._ignoreHooksParameter.value, + isWatch, stopwatch, - executionManagerOptions, terminal }; - try { - // Delegate the the underlying command, for only the projects that need reprocessing - await this._executeOperations(executeOptions); - } catch (err) { - // In watch mode, we want to rebuild even if the original build failed. - if (!(err instanceof AlreadyReportedError)) { - throw err; + const initialIterationOptions: IOperationGraphIterationOptions = { + inputsSnapshot: initialSnapshot + }; + if (isWatch) { + if (!initialSnapshot) { + terminal.writeErrorLine(`Unable to run in watch mode: could not analyze repository state`); + throw new AlreadyReportedError(); + } + + if (buildCacheConfiguration) { + // Cache writes are not supported during watch mode, only reads. + buildCacheConfiguration.cacheWriteEnabled = false; } + + const { ProjectWatcher } = await import( + /* webpackChunkName: 'ProjectWatcher' */ + '../../logic/ProjectWatcher' + ); + const watcher: typeof ProjectWatcher.prototype = new ProjectWatcher({ + rushConfiguration: this.rushConfiguration, + graph, + initialSnapshot, + terminal, + debounceMs: this._watchDebounceMs + }); + watcher.clearStatus(); + + await measureAsyncFn(`${PERF_PREFIX}:executeOperationsInner`, async () => { + return await graph.executeAsync(initialIterationOptions); + }); + + await abortPromise; + + terminal.writeLine(`Watch mode exited.`); + } else { + await measureAsyncFn(`${PERF_PREFIX}:runInitialPhases`, () => + measureAsyncFn(`${PERF_PREFIX}:executeOperations`, () => + this._executeOperationsAsync(executeOptions, initialIterationOptions) + ) + ); + } + } finally { + if (cobuildConfiguration) { + await cobuildConfiguration.destroyLockProviderAsync(); } } } @@ -503,30 +728,28 @@ export class PhasedScriptAction extends BaseScriptAction { /** * Runs a set of operations and reports the results. */ - private async _executeOperations(options: IExecutionOperationsOptions): Promise { - const { executionManagerOptions, ignoreHooks, operations, stopwatch, terminal } = options; - - const executionManager: OperationExecutionManager = new OperationExecutionManager( - operations, - executionManagerOptions - ); - - const { isInitial, isWatch } = options.createOperationsContext; + private async _executeOperationsAsync( + options: IExecuteOperationsOptions, + iterationOptions: IOperationGraphIterationOptions + ): Promise { + const { graph, ignoreHooks, stopwatch, terminal } = options; let success: boolean = false; - let result: IExecutionResult | undefined; try { - result = await executionManager.executeAsync(); - success = result.status === OperationStatus.Success; - - await this.hooks.afterExecuteOperations.promise(result, options.createOperationsContext); + const definiteResult: IExecutionResult = await measureAsyncFn( + `${PERF_PREFIX}:executeOperationsInner`, + async () => { + return await graph.executeAsync(iterationOptions); + } + ); + success = SUCCESSFUL_EXECUTION_STATUSES.has(definiteResult.status); stopwatch.stop(); const message: string = `rush ${this.actionName} (${stopwatch.toString()})`; - if (result.status === OperationStatus.Success) { - terminal.writeLine(colors.green(message)); + if (success) { + terminal.writeLine(Colorize.green(message)); } else { terminal.writeLine(message); } @@ -545,116 +768,15 @@ export class PhasedScriptAction extends BaseScriptAction { } } - terminal.writeErrorLine(colors.red(`rush ${this.actionName} - Errors! (${stopwatch.toString()})`)); + terminal.writeErrorLine(Colorize.red(`rush ${this.actionName} - Errors! (${stopwatch.toString()})`)); } } if (!ignoreHooks) { - this._doAfterTask(); - } - - if (this.parser.telemetry) { - const operationResults: Record = {}; - - const extraData: IPhasedCommandTelemetry = { - // Fields preserved across the command invocation - ...this._selectionParameters.getTelemetry(), - ...this.getParameterStringMap(), - isWatch, - // Fields specific to the current operation set - isInitial, - - countAll: 0, - countSuccess: 0, - countSuccessWithWarnings: 0, - countFailure: 0, - countBlocked: 0, - countFromCache: 0, - countSkipped: 0, - countNoOp: 0 - }; - - if (result) { - const nonSilentDependenciesByOperation: Map> = new Map(); - function getNonSilentDependencies(operation: Operation): ReadonlySet { - let realDependencies: Set | undefined = nonSilentDependenciesByOperation.get(operation); - if (!realDependencies) { - realDependencies = new Set(); - nonSilentDependenciesByOperation.set(operation, realDependencies); - for (const dependency of operation.dependencies) { - if (dependency.runner!.silent) { - for (const deepDependency of getNonSilentDependencies(dependency)) { - realDependencies.add(deepDependency); - } - } else { - realDependencies.add(dependency.name!); - } - } - } - return realDependencies; - } - - for (const [operation, operationResult] of result.operationResults) { - if (operation.runner?.silent) { - // Architectural operation. Ignore. - continue; - } - - const { startTime, endTime } = operationResult.stopwatch; - operationResults[operation.name!] = { - startTimestampMs: startTime, - endTimestampMs: endTime, - nonCachedDurationMs: operationResult.nonCachedDurationMs, - result: operationResult.status, - dependencies: Array.from(getNonSilentDependencies(operation)).sort() - }; - - extraData.countAll++; - switch (operationResult.status) { - case OperationStatus.Success: - extraData.countSuccess++; - break; - case OperationStatus.SuccessWithWarning: - extraData.countSuccessWithWarnings++; - break; - case OperationStatus.Failure: - extraData.countFailure++; - break; - case OperationStatus.Blocked: - extraData.countBlocked++; - break; - case OperationStatus.FromCache: - extraData.countFromCache++; - break; - case OperationStatus.Skipped: - extraData.countSkipped++; - break; - case OperationStatus.NoOp: - extraData.countNoOp++; - break; - default: - // Do nothing. - break; - } - } - } - - const logEntry: ITelemetryData = { - name: this.actionName, - durationInSeconds: stopwatch.duration, - result: success ? 'Succeeded' : 'Failed', - extraData, - operationResults - }; - - this.hooks.beforeLog.call(logEntry); - - this.parser.telemetry.log(logEntry); - - this.parser.flushTelemetry(); + measureFn(`${PERF_PREFIX}:doAfterTask`, () => this._doAfterTask()); } - if (!success && !isWatch) { + if (!success) { throw new AlreadyReportedError(); } } diff --git a/libraries/rush-lib/src/cli/test/Autoinstaller.test.ts b/libraries/rush-lib/src/cli/test/Autoinstaller.test.ts new file mode 100644 index 00000000000..f975427f50a --- /dev/null +++ b/libraries/rush-lib/src/cli/test/Autoinstaller.test.ts @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import './mockRushCommandLineParser'; + +import { FileSystem } from '@rushstack/node-core-library'; + +import { Autoinstaller } from '../../logic/Autoinstaller'; +import { InstallHelpers } from '../../logic/installManager/InstallHelpers'; +import { RushConstants } from '../../logic/RushConstants'; +import { Utilities } from '../../utilities/Utilities'; +import { + getCommandLineParserInstanceAsync, + isolateEnvironmentConfigurationForTests, + type IEnvironmentConfigIsolation +} from './TestUtils'; + +describe(Autoinstaller.name, () => { + let _envIsolation: IEnvironmentConfigIsolation; + + beforeEach(() => { + _envIsolation = isolateEnvironmentConfigurationForTests(); + }); + + afterEach(() => { + _envIsolation.restore(); + jest.restoreAllMocks(); + }); + + it('moves an existing node_modules folder into the Rush recycler before reinstalling', async () => { + const { parser, repoPath, spawnMock } = await getCommandLineParserInstanceAsync( + 'pluginWithBuildCommandRepo', + 'update' + ); + const autoinstallerPath: string = `${repoPath}/common/autoinstallers/plugins`; + const nodeModulesFolder: string = `${autoinstallerPath}/${RushConstants.nodeModulesFolderName}`; + const staleFilePath: string = `${nodeModulesFolder}/stale-package/index.js`; + const recyclerFolder: string = `${parser.rushConfiguration.commonTempFolder}/${RushConstants.rushRecyclerFolderName}`; + + await FileSystem.writeFileAsync(staleFilePath, 'stale', { + ensureFolderExists: true + }); + + let recyclerEntriesBefore: Set; + try { + recyclerEntriesBefore = new Set(await FileSystem.readFolderItemNamesAsync(recyclerFolder)); + } catch (error) { + if (FileSystem.isNotExistError(error)) { + recyclerEntriesBefore = new Set(); + } else { + throw error; + } + } + + jest.spyOn(InstallHelpers, 'ensureLocalPackageManagerAsync').mockResolvedValue(undefined); + jest.spyOn(Utilities, 'syncNpmrc').mockImplementation(() => undefined); + jest + .spyOn(Utilities, 'executeCommandAsync') + .mockImplementation(async (options: Parameters[0]) => { + await FileSystem.ensureFolderAsync( + `${options.workingDirectory}/${RushConstants.nodeModulesFolderName}` + ); + }); + + const autoinstaller: Autoinstaller = new Autoinstaller({ + autoinstallerName: 'plugins', + rushConfiguration: parser.rushConfiguration, + rushGlobalFolder: parser.rushGlobalFolder + }); + + await autoinstaller.prepareAsync(); + + const recyclerEntriesAfter: string[] = (await FileSystem.readFolderItemNamesAsync(recyclerFolder)).filter( + (entry: string) => !recyclerEntriesBefore.has(entry) + ); + + expect(recyclerEntriesAfter).toHaveLength(1); + await expect( + FileSystem.existsAsync(`${recyclerFolder}/${recyclerEntriesAfter[0]}/stale-package/index.js`) + ).resolves.toBe(true); + await expect(FileSystem.existsAsync(staleFilePath)).resolves.toBe(false); + await expect(FileSystem.existsAsync(`${nodeModulesFolder}/rush-autoinstaller.flag`)).resolves.toBe(true); + + if (process.platform === 'win32') { + expect(spawnMock).toHaveBeenCalledWith( + 'cmd.exe', + expect.arrayContaining(['/c']), + expect.objectContaining({ + detached: true, + stdio: 'ignore', + windowsVerbatimArguments: true + }) + ); + } else { + expect(spawnMock).toHaveBeenCalledWith( + 'rm', + expect.arrayContaining(['-rf']), + expect.objectContaining({ + detached: true, + stdio: 'ignore' + }) + ); + } + }); +}); diff --git a/libraries/rush-lib/src/cli/test/Cli.test.ts b/libraries/rush-lib/src/cli/test/Cli.test.ts index 5d6cd21a24f..7ef8255d977 100644 --- a/libraries/rush-lib/src/cli/test/Cli.test.ts +++ b/libraries/rush-lib/src/cli/test/Cli.test.ts @@ -1,35 +1,38 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; import { Utilities } from '../../utilities/Utilities'; +// Increase the timeout since this command spawns child processes +jest.setTimeout(10000); + describe('CLI', () => { - it('should not fail when there is no rush.json', () => { + it('should not fail when there is no rush.json', async () => { const workingDir: string = '/'; const startPath: string = path.resolve(__dirname, '../../../lib-commonjs/start.js'); - expect(() => { - Utilities.executeCommand({ + await expect( + Utilities.executeCommandAsync({ command: 'node', args: [startPath], workingDirectory: workingDir, suppressOutput: true - }); - }).not.toThrow(); + }) + ).resolves.not.toThrow(); }); - it('rushx should pass args to scripts', () => { + it('rushx should pass args to scripts', async () => { // Invoke "rushx" const startPath: string = path.resolve(__dirname, '../../../lib-commonjs/startx.js'); // Run "rushx show-args 1 2 -x" in the "repo/rushx-project" folder - const output: string = Utilities.executeCommandAndCaptureOutput( - 'node', - [startPath, 'show-args', '1', '2', '-x'], - `${__dirname}/repo/rushx-project` - ); + const output: string = await Utilities.executeCommandAndCaptureOutputAsync({ + command: 'node', + args: [startPath, 'show-args', '1', '2', '-x'], + workingDirectory: `${__dirname}/repo/rushx-project` + }); const lastLine: string = output .split(/\s*\n\s*/) @@ -38,17 +41,15 @@ describe('CLI', () => { expect(lastLine).toEqual('build.js: ARGS=["1","2","-x"]'); }); - it('rushx should fail in un-rush project', () => { + it('rushx should fail in un-rush project', async () => { // Invoke "rushx" const startPath: string = path.resolve(__dirname, '../../../lib-commonjs/startx.js'); - const output = Utilities.executeCommandAndCaptureOutput( - 'node', - [startPath, 'show-args', '1', '2', '-x'], - `${__dirname}/repo/rushx-not-in-rush-project` - ); - - console.log(output); + const output: string = await Utilities.executeCommandAndCaptureOutputAsync({ + command: 'node', + args: [startPath, 'show-args', '1', '2', '-x'], + workingDirectory: `${__dirname}/repo/rushx-not-in-rush-project` + }); expect(output).toEqual( expect.stringMatching( diff --git a/libraries/rush-lib/src/cli/test/CommandLineHelp.test.ts b/libraries/rush-lib/src/cli/test/CommandLineHelp.test.ts index c4362c9cc86..78ce4ee8557 100644 --- a/libraries/rush-lib/src/cli/test/CommandLineHelp.test.ts +++ b/libraries/rush-lib/src/cli/test/CommandLineHelp.test.ts @@ -1,20 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { AnsiEscape } from '@rushstack/node-core-library'; -import * as colorsPackage from 'colors'; +import { AnsiEscape } from '@rushstack/terminal'; import { RushCommandLineParser } from '../RushCommandLineParser'; +import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration'; describe('CommandLineHelp', () => { let oldCwd: string | undefined; - let colorsEnabled: boolean; let parser: RushCommandLineParser; beforeEach(() => { // ts-command-line calls process.exit() which interferes with Jest - jest.spyOn(process, 'exit').mockImplementation((code?: number) => { + jest.spyOn(process, 'exit').mockImplementation((code) => { throw new Error(`Test code called process.exit(${code})`); }); @@ -23,16 +22,12 @@ describe('CommandLineHelp', () => { process.chdir(localCwd); - colorsEnabled = colorsPackage.enabled; - if (!colorsEnabled) { - colorsPackage.enable(); - } - // This call may terminate the entire test run because it invokes process.exit() // if it encounters errors. // TODO Remove the calls to process.exit() or override them for testing. parser = new RushCommandLineParser(); - parser.execute().catch(console.error); + // eslint-disable-next-line no-console + parser.executeAsync().catch(console.error); }); afterEach(() => { @@ -40,9 +35,7 @@ describe('CommandLineHelp', () => { process.chdir(oldCwd); } - if (!colorsEnabled) { - colorsPackage.disable(); - } + EnvironmentConfiguration.reset(); }); it('prints the global help', () => { diff --git a/libraries/rush-lib/src/cli/test/RushCommandLineParser.test.ts b/libraries/rush-lib/src/cli/test/RushCommandLineParser.test.ts index 2ca62a05d0a..dcdbca339ff 100644 --- a/libraries/rush-lib/src/cli/test/RushCommandLineParser.test.ts +++ b/libraries/rush-lib/src/cli/test/RushCommandLineParser.test.ts @@ -6,191 +6,138 @@ jest.mock(`@rushstack/package-deps-hash`, () => { getRepoRoot(dir: string): string { return dir; }, - getRepoStateAsync(): ReadonlyMap { - return new Map(); + getDetailedRepoStateAsync(): IDetailedRepoState { + return { + hasSubmodules: false, + hasUncommittedChanges: false, + files: new Map([['common/config/rush/npm-shrinkwrap.json', 'hash']]), + symlinks: new Map() + }; }, getRepoChangesAsync(): ReadonlyMap { return new Map(); + }, + getGitHashForFiles(filePaths: Iterable): ReadonlyMap { + return new Map(Array.from(filePaths, (filePath: string) => [filePath, filePath])); + }, + hashFilesAsync(rootDirectory: string, filePaths: Iterable): Promise> { + return Promise.resolve(new Map(Array.from(filePaths, (filePath: string) => [filePath, filePath]))); } }; }); import './mockRushCommandLineParser'; -import * as path from 'path'; -import { FileSystem, JsonFile, Path, PackageJsonLookup } from '@rushstack/node-core-library'; -import type { RushCommandLineParser as RushCommandLineParserType } from '../RushCommandLineParser'; -import { LastLinkFlagFactory } from '../../api/LastLinkFlag'; +import type { SpawnOptions } from 'node:child_process'; +import { FileSystem, JsonFile, Path } from '@rushstack/node-core-library'; +import type { IDetailedRepoState } from '@rushstack/package-deps-hash'; import { Autoinstaller } from '../../logic/Autoinstaller'; -import { ITelemetryData } from '../../logic/Telemetry'; - -/** - * See `__mocks__/child_process.js`. - */ -interface ISpawnMockConfig { - emitError: boolean; - returnCode: number; -} - -interface IChildProcessModuleMock { - /** - * Initialize the `spawn` mock behavior. - */ - __setSpawnMockConfig(config?: ISpawnMockConfig): void; - - spawn: jest.Mock; -} - -/** - * Interface definition for a test instance for the RushCommandLineParser. - */ -interface IParserTestInstance { - parser: RushCommandLineParserType; - spawnMock: jest.Mock; -} +import type { ITelemetryData } from '../../logic/Telemetry'; +import { + getCommandLineParserInstanceAsync, + type SpawnMockArgs, + type SpawnMockCall, + isolateEnvironmentConfigurationForTests, + type IEnvironmentConfigIsolation +} from './TestUtils'; +import { IS_WINDOWS } from '../../utilities/executionUtilities'; + +// Ordinals into the `mock.calls` array referencing each of the arguments to `spawn`. Note that +// the exact structure of these arguments differs between Windows and non-Windows platforms, so +// we only reference the one that is common. +const SPAWN_ARG_OPTIONS: number = 2; -/** - * Configure the `child_process` `spawn` mock for these tests. This relies on the mock implementation - * in `__mocks__/child_process.js`. - */ -function setSpawnMock(options?: ISpawnMockConfig): jest.Mock { - const cpMocked: IChildProcessModuleMock = require('child_process'); - cpMocked.__setSpawnMockConfig(options); - - const spawnMock: jest.Mock = cpMocked.spawn; - spawnMock.mockName('spawn'); - return spawnMock; +function spawnOptionEquals( + spawnCall: SpawnMockCall, + optionName: TOption, + expected: TExepcted, + tweakActual: (actual: SpawnOptions[TOption]) => TExepcted = (x) => x as TExepcted +): void { + const spawnOptions: SpawnOptions = spawnCall[SPAWN_ARG_OPTIONS] as SpawnOptions; + expect(spawnOptions).toEqual(expect.any(Object)); + expect(tweakActual(spawnOptions[optionName])).toEqual(expected); } -function getDirnameInLib(): string { - // Run these tests in the /lib folder because some of them require compiled output - const projectRootFolder: string = PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname)!; - const projectRootRelativeDirnamePath: string = path.relative(projectRootFolder, __dirname); - const projectRootRelativeLibDirnamePath: string = projectRootRelativeDirnamePath.replace( - /^src/, - 'lib-commonjs' +function cwdOptionEquals(spawnCall: SpawnMockCall, expected: string): void { + spawnOptionEquals(spawnCall, 'cwd', Path.convertToSlashes(expected), (actual) => + Path.convertToSlashes(String(actual)) ); - const dirnameInLIb: string = `${projectRootFolder}/${projectRootRelativeLibDirnamePath}`; - return dirnameInLIb; } -// eslint-disable-next-line @typescript-eslint/naming-convention -const __dirnameInLib: string = getDirnameInLib(); - -/** - * Helper to set up a test instance for RushCommandLineParser. - */ -async function getCommandLineParserInstanceAsync( - repoName: string, - taskName: string -): Promise { - // Run these tests in the /lib folder because some of them require compiled output - // Point to the test repo folder - const startPath: string = `${__dirnameInLib}/${repoName}`; - - // The `build` task is hard-coded to be incremental. So delete the package-deps file folder in - // the test repo to guarantee the test actually runs. - FileSystem.deleteFolder(`${startPath}/a/.rush/temp`); - FileSystem.deleteFolder(`${startPath}/b/.rush/temp`); - - const { RushCommandLineParser } = await import('../RushCommandLineParser'); - - // Create a Rush CLI instance. This instance is heavy-weight and relies on setting process.exit - // to exit and clear the Rush file lock. So running multiple `it` or `describe` test blocks over the same test - // repo will fail due to contention over the same lock which is kept until the test runner process - // ends. - const parser: RushCommandLineParserType = new RushCommandLineParser({ cwd: startPath }); - - // Bulk tasks are hard-coded to expect install to have been completed. So, ensure the last-link.flag - // file exists and is valid - LastLinkFlagFactory.getCommonTempFlag(parser.rushConfiguration).create(); - - // Mock the command - process.argv = ['pretend-this-is-node.exe', 'pretend-this-is-rush', taskName]; - const spawnMock: jest.Mock = setSpawnMock(); - - return { - parser, - spawnMock - }; -} +jest.setTimeout(1000000); -function pathEquals(actual: string, expected: string): void { - expect(Path.convertToSlashes(actual)).toEqual(Path.convertToSlashes(expected)); +function expectSpawnToMatchRegexp(spawnCall: SpawnMockCall, expectedRegexp: RegExp): void { + if (IS_WINDOWS) { + // On Windows, the command is passed as a single string with the `shell: true` option + spawnOptionEquals(spawnCall, 'shell', true); + expect(spawnCall[0]).toMatch(expectedRegexp); + } else { + expect(spawnCall[1]).toEqual(expect.arrayContaining([expect.stringMatching(expectedRegexp)])); + } } -// Ordinals into the `mock.calls` array referencing each of the arguments to `spawn` -const SPAWN_ARG_ARGS: number = 1; -const SPAWN_ARG_OPTIONS: number = 2; - describe('RushCommandLineParser', () => { describe('execute', () => { + let _envIsolation: IEnvironmentConfigIsolation; + + beforeEach(() => { + _envIsolation = isolateEnvironmentConfigurationForTests(); + }); + afterEach(() => { jest.clearAllMocks(); + _envIsolation.restore(); }); describe('in basic repo', () => { describe("'build' action", () => { it(`executes the package's 'build' script`, async () => { const repoName: string = 'basicAndRunBuildActionRepo'; - const instance: IParserTestInstance = await getCommandLineParserInstanceAsync(repoName, 'build'); + const { parser, spawnMock, repoPath } = await getCommandLineParserInstanceAsync(repoName, 'build'); - await expect(instance.parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); // There should be 1 build per package - const packageCount: number = instance.spawnMock.mock.calls.length; + const packageCount: number = spawnMock.mock.calls.length; expect(packageCount).toEqual(2); // Use regex for task name in case spaces were prepended or appended to spawned command const expectedBuildTaskRegexp: RegExp = /fake_build_task_but_works_with_mock/; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const firstSpawn: any[] = instance.spawnMock.mock.calls[0]; - expect(firstSpawn[SPAWN_ARG_ARGS]).toEqual( - expect.arrayContaining([expect.stringMatching(expectedBuildTaskRegexp)]) - ); - expect(firstSpawn[SPAWN_ARG_OPTIONS]).toEqual(expect.any(Object)); - pathEquals(firstSpawn[SPAWN_ARG_OPTIONS].cwd, `${__dirnameInLib}/${repoName}/a`); + const firstSpawn: SpawnMockArgs = spawnMock.mock.calls[0]; + expectSpawnToMatchRegexp(firstSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(firstSpawn, `${repoPath}/a`); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const secondSpawn: any[] = instance.spawnMock.mock.calls[1]; - expect(secondSpawn[SPAWN_ARG_ARGS]).toEqual( - expect.arrayContaining([expect.stringMatching(expectedBuildTaskRegexp)]) - ); - expect(secondSpawn[SPAWN_ARG_OPTIONS]).toEqual(expect.any(Object)); - pathEquals(secondSpawn[SPAWN_ARG_OPTIONS].cwd, `${__dirnameInLib}/${repoName}/b`); + const secondSpawn: SpawnMockArgs = spawnMock.mock.calls[1]; + expectSpawnToMatchRegexp(secondSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(secondSpawn, `${repoPath}/b`); }); }); describe("'rebuild' action", () => { it(`executes the package's 'build' script`, async () => { const repoName: string = 'basicAndRunRebuildActionRepo'; - const instance: IParserTestInstance = await getCommandLineParserInstanceAsync(repoName, 'rebuild'); + const { parser, spawnMock, repoPath } = await getCommandLineParserInstanceAsync( + repoName, + 'rebuild' + ); - await expect(instance.parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); // There should be 1 build per package - const packageCount: number = instance.spawnMock.mock.calls.length; + const packageCount: number = spawnMock.mock.calls.length; expect(packageCount).toEqual(2); // Use regex for task name in case spaces were prepended or appended to spawned command const expectedBuildTaskRegexp: RegExp = /fake_build_task_but_works_with_mock/; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const firstSpawn: any[] = instance.spawnMock.mock.calls[0]; - expect(firstSpawn[SPAWN_ARG_ARGS]).toEqual( - expect.arrayContaining([expect.stringMatching(expectedBuildTaskRegexp)]) - ); - expect(firstSpawn[SPAWN_ARG_OPTIONS]).toEqual(expect.any(Object)); - pathEquals(firstSpawn[SPAWN_ARG_OPTIONS].cwd, `${__dirnameInLib}/${repoName}/a`); + const firstSpawn: SpawnMockCall = spawnMock.mock.calls[0]; + expectSpawnToMatchRegexp(firstSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(firstSpawn, `${repoPath}/a`); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const secondSpawn: any[] = instance.spawnMock.mock.calls[1]; - expect(secondSpawn[SPAWN_ARG_ARGS]).toEqual( - expect.arrayContaining([expect.stringMatching(expectedBuildTaskRegexp)]) - ); - expect(secondSpawn[SPAWN_ARG_OPTIONS]).toEqual(expect.any(Object)); - pathEquals(secondSpawn[SPAWN_ARG_OPTIONS].cwd, `${__dirnameInLib}/${repoName}/b`); + const secondSpawn: SpawnMockCall = spawnMock.mock.calls[1]; + expectSpawnToMatchRegexp(secondSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(secondSpawn, `${repoPath}/b`); }); }); }); @@ -199,64 +146,51 @@ describe('RushCommandLineParser', () => { describe("'build' action", () => { it(`executes the package's 'build' script`, async () => { const repoName: string = 'overrideRebuildAndRunBuildActionRepo'; - const instance: IParserTestInstance = await getCommandLineParserInstanceAsync(repoName, 'build'); + const { parser, spawnMock, repoPath } = await getCommandLineParserInstanceAsync(repoName, 'build'); - await expect(instance.parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); // There should be 1 build per package - const packageCount: number = instance.spawnMock.mock.calls.length; + const packageCount: number = spawnMock.mock.calls.length; expect(packageCount).toEqual(2); // Use regex for task name in case spaces were prepended or appended to spawned command const expectedBuildTaskRegexp: RegExp = /fake_build_task_but_works_with_mock/; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const firstSpawn: any[] = instance.spawnMock.mock.calls[0]; - expect(firstSpawn[SPAWN_ARG_ARGS]).toEqual( - expect.arrayContaining([expect.stringMatching(expectedBuildTaskRegexp)]) - ); - expect(firstSpawn[SPAWN_ARG_OPTIONS]).toEqual(expect.any(Object)); - pathEquals(firstSpawn[SPAWN_ARG_OPTIONS].cwd, `${__dirnameInLib}/${repoName}/a`); + const firstSpawn: SpawnMockCall = spawnMock.mock.calls[0]; + expectSpawnToMatchRegexp(firstSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(firstSpawn, `${repoPath}/a`); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const secondSpawn: any[] = instance.spawnMock.mock.calls[1]; - expect(secondSpawn[SPAWN_ARG_ARGS]).toEqual( - expect.arrayContaining([expect.stringMatching(expectedBuildTaskRegexp)]) - ); - expect(secondSpawn[SPAWN_ARG_OPTIONS]).toEqual(expect.any(Object)); - pathEquals(secondSpawn[SPAWN_ARG_OPTIONS].cwd, `${__dirnameInLib}/${repoName}/b`); + const secondSpawn: SpawnMockCall = spawnMock.mock.calls[1]; + expectSpawnToMatchRegexp(secondSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(secondSpawn, `${repoPath}/b`); }); }); describe("'rebuild' action", () => { it(`executes the package's 'rebuild' script`, async () => { const repoName: string = 'overrideRebuildAndRunRebuildActionRepo'; - const instance: IParserTestInstance = await getCommandLineParserInstanceAsync(repoName, 'rebuild'); + const { parser, spawnMock, repoPath } = await getCommandLineParserInstanceAsync( + repoName, + 'rebuild' + ); - await expect(instance.parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); // There should be 1 build per package - const packageCount: number = instance.spawnMock.mock.calls.length; + const packageCount: number = spawnMock.mock.calls.length; expect(packageCount).toEqual(2); // Use regex for task name in case spaces were prepended or appended to spawned command const expectedBuildTaskRegexp: RegExp = /fake_REbuild_task_but_works_with_mock/; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const firstSpawn: any[] = instance.spawnMock.mock.calls[0]; - expect(firstSpawn[SPAWN_ARG_ARGS]).toEqual( - expect.arrayContaining([expect.stringMatching(expectedBuildTaskRegexp)]) - ); - expect(firstSpawn[SPAWN_ARG_OPTIONS]).toEqual(expect.any(Object)); - pathEquals(firstSpawn[SPAWN_ARG_OPTIONS].cwd, `${__dirnameInLib}/${repoName}/a`); + const firstSpawn: SpawnMockCall = spawnMock.mock.calls[0]; + expectSpawnToMatchRegexp(firstSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(firstSpawn, `${repoPath}/a`); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const secondSpawn: any[] = instance.spawnMock.mock.calls[1]; - expect(secondSpawn[SPAWN_ARG_ARGS]).toEqual( - expect.arrayContaining([expect.stringMatching(expectedBuildTaskRegexp)]) - ); - expect(secondSpawn[SPAWN_ARG_OPTIONS]).toEqual(expect.any(Object)); - pathEquals(secondSpawn[SPAWN_ARG_OPTIONS].cwd, `${__dirnameInLib}/${repoName}/b`); + const secondSpawn: SpawnMockCall = spawnMock.mock.calls[1]; + expectSpawnToMatchRegexp(secondSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(secondSpawn, `${repoPath}/b`); }); }); }); @@ -265,31 +199,23 @@ describe('RushCommandLineParser', () => { describe("'build' action", () => { it(`executes the package's 'build' script`, async () => { const repoName: string = 'overrideAndDefaultBuildActionRepo'; - const instance: IParserTestInstance = await getCommandLineParserInstanceAsync(repoName, 'build'); - await expect(instance.parser.execute()).resolves.toEqual(true); + const { parser, spawnMock, repoPath } = await getCommandLineParserInstanceAsync(repoName, 'build'); + await expect(parser.executeAsync()).resolves.toEqual(true); // There should be 1 build per package - const packageCount: number = instance.spawnMock.mock.calls.length; + const packageCount: number = spawnMock.mock.calls.length; expect(packageCount).toEqual(2); // Use regex for task name in case spaces were prepended or appended to spawned command const expectedBuildTaskRegexp: RegExp = /fake_build_task_but_works_with_mock/; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const firstSpawn: any[] = instance.spawnMock.mock.calls[0]; - expect(firstSpawn[SPAWN_ARG_ARGS]).toEqual( - expect.arrayContaining([expect.stringMatching(expectedBuildTaskRegexp)]) - ); - expect(firstSpawn[SPAWN_ARG_OPTIONS]).toEqual(expect.any(Object)); - pathEquals(firstSpawn[SPAWN_ARG_OPTIONS].cwd, `${__dirnameInLib}/${repoName}/a`); + const firstSpawn: SpawnMockCall = spawnMock.mock.calls[0]; + expectSpawnToMatchRegexp(firstSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(firstSpawn, `${repoPath}/a`); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const secondSpawn: any[] = instance.spawnMock.mock.calls[1]; - expect(secondSpawn[SPAWN_ARG_ARGS]).toEqual( - expect.arrayContaining([expect.stringMatching(expectedBuildTaskRegexp)]) - ); - expect(secondSpawn[SPAWN_ARG_OPTIONS]).toEqual(expect.any(Object)); - pathEquals(secondSpawn[SPAWN_ARG_OPTIONS].cwd, `${__dirnameInLib}/${repoName}/b`); + const secondSpawn: SpawnMockCall = spawnMock.mock.calls[1]; + expectSpawnToMatchRegexp(secondSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(secondSpawn, `${repoPath}/b`); }); }); @@ -297,31 +223,26 @@ describe('RushCommandLineParser', () => { it(`executes the package's 'build' script`, async () => { // broken const repoName: string = 'overrideAndDefaultRebuildActionRepo'; - const instance: IParserTestInstance = await getCommandLineParserInstanceAsync(repoName, 'rebuild'); - await expect(instance.parser.execute()).resolves.toEqual(true); + const { parser, spawnMock, repoPath } = await getCommandLineParserInstanceAsync( + repoName, + 'rebuild' + ); + await expect(parser.executeAsync()).resolves.toEqual(true); // There should be 1 build per package - const packageCount: number = instance.spawnMock.mock.calls.length; + const packageCount: number = spawnMock.mock.calls.length; expect(packageCount).toEqual(2); // Use regex for task name in case spaces were prepended or appended to spawned command const expectedBuildTaskRegexp: RegExp = /fake_build_task_but_works_with_mock/; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const firstSpawn: any[] = instance.spawnMock.mock.calls[0]; - expect(firstSpawn[SPAWN_ARG_ARGS]).toEqual( - expect.arrayContaining([expect.stringMatching(expectedBuildTaskRegexp)]) - ); - expect(firstSpawn[SPAWN_ARG_OPTIONS]).toEqual(expect.any(Object)); - pathEquals(firstSpawn[SPAWN_ARG_OPTIONS].cwd, `${__dirnameInLib}/${repoName}/a`); + const firstSpawn: SpawnMockCall = spawnMock.mock.calls[0]; + expectSpawnToMatchRegexp(firstSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(firstSpawn, `${repoPath}/a`); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const secondSpawn: any[] = instance.spawnMock.mock.calls[1]; - expect(secondSpawn[SPAWN_ARG_ARGS]).toEqual( - expect.arrayContaining([expect.stringMatching(expectedBuildTaskRegexp)]) - ); - expect(secondSpawn[SPAWN_ARG_OPTIONS]).toEqual(expect.any(Object)); - pathEquals(secondSpawn[SPAWN_ARG_OPTIONS].cwd, `${__dirnameInLib}/${repoName}/b`); + const secondSpawn: SpawnMockCall = spawnMock.mock.calls[1]; + expectSpawnToMatchRegexp(secondSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(secondSpawn, `${repoPath}/b`); }); }); }); @@ -377,8 +298,8 @@ describe('RushCommandLineParser', () => { describe('in repo plugin custom flushTelemetry', () => { it('creates a custom telemetry file', async () => { const repoName: string = 'tapFlushTelemetryAndRunBuildActionRepo'; - const instance: IParserTestInstance = await getCommandLineParserInstanceAsync(repoName, 'build'); - const telemetryFilePath: string = `${instance.parser.rushConfiguration.commonTempFolder}/test-telemetry.json`; + const { parser } = await getCommandLineParserInstanceAsync(repoName, 'build'); + const telemetryFilePath: string = `${parser.rushConfiguration.commonTempFolder}/test-telemetry.json`; FileSystem.deleteFile(telemetryFilePath); /** @@ -386,15 +307,169 @@ describe('RushCommandLineParser', () => { */ jest.spyOn(Autoinstaller.prototype, 'prepareAsync').mockImplementation(async function () {}); - await expect(instance.parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); expect(FileSystem.exists(telemetryFilePath)).toEqual(true); - let telemetryStore: ITelemetryData[] = []; - expect(() => { - telemetryStore = JsonFile.load(telemetryFilePath); - }).not.toThrowError(); + const telemetryStore: ITelemetryData[] = await JsonFile.loadAsync(telemetryFilePath); expect(telemetryStore?.[0].name).toEqual('build'); + expect(telemetryStore?.[0].result).toEqual('Succeeded'); + }); + }); + + describe('in repo plugin that produces no operations', () => { + it('succeeds when a plugin returns an empty operation set', async () => { + // Regression test: `@rushstack/rush-buildxl-graph-plugin` writes the build graph to disk in + // response to `--drop-graph` and then returns an empty operation set, because there is + // nothing left for Rush to execute. An iteration with zero operations resolves to + // `OperationStatus.NoOp`, which must not be reported as a failure. + const repoName: string = 'clearOperationsAndRunBuildActionRepo'; + const { parser, spawnMock } = await getCommandLineParserInstanceAsync(repoName, 'build'); + + /** + * The plugin is copied into the autoinstaller folder using an option in /config/heft.json + */ + jest.spyOn(Autoinstaller.prototype, 'prepareAsync').mockImplementation(async function () {}); + + await expect(parser.executeAsync()).resolves.toEqual(true); + + // Nothing should have been executed, since the plugin removed every operation. + expect(spawnMock.mock.calls.length).toEqual(0); + }); + }); + + describe('in repo plugin with build command', () => { + describe("'build' action", () => { + it(`executes the package's 'build' script`, async () => { + const repoName: string = 'pluginWithBuildCommandRepo'; + const { parser, spawnMock, repoPath } = await getCommandLineParserInstanceAsync(repoName, 'build'); + + expect(parser.getAction('build').summary).toEqual('Override build command summary in plugin'); + expect(parser.getAction('rebuild').summary).toEqual(expect.any(String)); + + await expect(parser.executeAsync()).resolves.toEqual(true); + + // There should be 1 build per package + const packageCount: number = spawnMock.mock.calls.length; + expect(packageCount).toEqual(2); + + // Use regex for task name in case spaces were prepended or appended to spawned command + const expectedBuildTaskRegexp: RegExp = /fake_build_task_but_works_with_mock/; + + const firstSpawn: SpawnMockCall = spawnMock.mock.calls[0]; + expectSpawnToMatchRegexp(firstSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(firstSpawn, `${repoPath}/a`); + + const secondSpawn: SpawnMockCall = spawnMock.mock.calls[1]; + expectSpawnToMatchRegexp(secondSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(secondSpawn, `${repoPath}/b`); + }); + }); + + describe("'rebuild' action", () => { + it(`executes the package's 'rebuild' script`, async () => { + const repoName: string = 'pluginWithBuildCommandRepo'; + const { parser, spawnMock, repoPath } = await getCommandLineParserInstanceAsync( + repoName, + 'rebuild' + ); + + await expect(parser.executeAsync()).resolves.toEqual(true); + + // There should be 1 build per package + const packageCount: number = spawnMock.mock.calls.length; + expect(packageCount).toEqual(2); + + // Use regex for task name in case spaces were prepended or appended to spawned command + const expectedBuildTaskRegexp: RegExp = /fake_build_task_but_works_with_mock/; + + const firstSpawn: SpawnMockCall = spawnMock.mock.calls[0]; + expectSpawnToMatchRegexp(firstSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(firstSpawn, `${repoPath}/a`); + + const secondSpawn: SpawnMockCall = spawnMock.mock.calls[1]; + expectSpawnToMatchRegexp(secondSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(secondSpawn, `${repoPath}/b`); + }); + }); + }); + + describe('in repo plugin with rebuild command', () => { + describe("'build' action", () => { + it(`executes the package's 'build' script`, async () => { + const repoName: string = 'pluginWithRebuildCommandRepo'; + const { parser, spawnMock, repoPath } = await getCommandLineParserInstanceAsync(repoName, 'build'); + + expect(parser.getAction('rebuild').summary).toEqual('Override rebuild command summary in plugin'); + expect(parser.getAction('build').summary).toEqual(expect.any(String)); + await expect(parser.executeAsync()).resolves.toEqual(true); + + // There should be 1 build per package + const packageCount: number = spawnMock.mock.calls.length; + expect(packageCount).toEqual(2); + + // Use regex for task name in case spaces were prepended or appended to spawned command + const expectedBuildTaskRegexp: RegExp = /fake_build_task_but_works_with_mock/; + + const firstSpawn: SpawnMockCall = spawnMock.mock.calls[0]; + expectSpawnToMatchRegexp(firstSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(firstSpawn, `${repoPath}/a`); + + const secondSpawn: SpawnMockCall = spawnMock.mock.calls[1]; + expectSpawnToMatchRegexp(secondSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(secondSpawn, `${repoPath}/b`); + }); + }); + + describe("'rebuild' action", () => { + it(`executes the package's 'rebuild' script`, async () => { + const repoName: string = 'pluginWithRebuildCommandRepo'; + const { parser, spawnMock, repoPath } = await getCommandLineParserInstanceAsync( + repoName, + 'rebuild' + ); + + await expect(parser.executeAsync()).resolves.toEqual(true); + + // There should be 1 build per package + const packageCount: number = spawnMock.mock.calls.length; + expect(packageCount).toEqual(2); + + // Use regex for task name in case spaces were prepended or appended to spawned command + const expectedBuildTaskRegexp: RegExp = /fake_REbuild_task_but_works_with_mock/; + + const firstSpawn: SpawnMockCall = spawnMock.mock.calls[0]; + expectSpawnToMatchRegexp(firstSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(firstSpawn, `${repoPath}/a`); + + const secondSpawn: SpawnMockCall = spawnMock.mock.calls[1]; + expectSpawnToMatchRegexp(secondSpawn, expectedBuildTaskRegexp); + cwdOptionEquals(secondSpawn, `${repoPath}/b`); + }); + }); + }); + + describe('in repo plugin with conflict build command', () => { + it(`throws an error when starting Rush`, async () => { + const repoName: string = 'pluginWithConflictBuildCommandRepo'; + + await expect(async () => { + await getCommandLineParserInstanceAsync(repoName, 'doesnt-matter'); + }).rejects.toThrowErrorMatchingInlineSnapshot( + `"Error from plugin rush-build-command-plugin by rush-build-command-plugin: Error: command-line.json defines a command \\"build\\" using a name that already exists"` + ); + }); + }); + + describe("in repo plugin with conflict rebuild command'", () => { + it(`throws an error when starting Rush`, async () => { + const repoName: string = 'pluginWithConflictRebuildCommandRepo'; + + await expect(async () => { + await getCommandLineParserInstanceAsync(repoName, 'doesnt-matter'); + }).rejects.toThrowErrorMatchingInlineSnapshot( + `"command-line.json defines a parameter \\"--no-color\\" that is associated with a command \\"build\\" that is not defined in this file."` + ); }); }); }); diff --git a/libraries/rush-lib/src/cli/test/RushCommandLineParserFailureCases.test.ts b/libraries/rush-lib/src/cli/test/RushCommandLineParserFailureCases.test.ts new file mode 100644 index 00000000000..e4501830cb0 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/RushCommandLineParserFailureCases.test.ts @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// Mock child_process so we can verify tasks are (or are not) invoked as we expect +jest.mock('node:child_process', () => jest.requireActual('./mock_child_process')); +jest.mock('@rushstack/terminal'); +jest.mock(`@rushstack/package-deps-hash`, () => { + return { + getRepoRoot(dir: string): string { + return dir; + }, + getDetailedRepoStateAsync(): IDetailedRepoState { + return { + hasSubmodules: false, + hasUncommittedChanges: false, + files: new Map([['common/config/rush/npm-shrinkwrap.json', 'hash']]), + symlinks: new Map() + }; + }, + getRepoChangesAsync(): ReadonlyMap { + return new Map(); + }, + hashFilesAsync(rootDirectory: string, filePaths: Iterable): Promise> { + return Promise.resolve(new Map(Array.from(filePaths, (filePath: string) => [filePath, filePath]))); + } + }; +}); + +import { FileSystem, JsonFile } from '@rushstack/node-core-library'; +import type { IDetailedRepoState } from '@rushstack/package-deps-hash'; +import { Autoinstaller } from '../../logic/Autoinstaller'; +import type { ITelemetryData } from '../../logic/Telemetry'; +import { getCommandLineParserInstanceAsync, setSpawnMock } from './TestUtils'; +import { isolateEnvironmentConfigurationForTests, type IEnvironmentConfigIsolation } from './TestUtils'; + +describe('RushCommandLineParserFailureCases', () => { + describe('execute', () => { + let _envIsolation: IEnvironmentConfigIsolation; + + beforeEach(() => { + _envIsolation = isolateEnvironmentConfigurationForTests({ silenceStderrWrite: true }); + }); + + afterEach(() => { + _envIsolation.restore(); + jest.restoreAllMocks(); + }); + + describe('in repo plugin custom flushTelemetry', () => { + it('custom telemetry reports errors', async () => { + const repoName: string = 'tapFlushTelemetryAndRunBuildActionRepo'; + + // WARNING: This test case needs the real implementation of _reportErrorAndSetExitCode. + // As a result, process.exit needs to be explicitly mocked to prevent the test runner from exiting. + const procProm = new Promise((resolve, reject) => { + jest.spyOn(process, 'exit').mockImplementation((() => { + resolve(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }) as any); + }); + + const { parser } = await getCommandLineParserInstanceAsync(repoName, 'build'); + + const telemetryFilePath: string = `${parser.rushConfiguration.commonTempFolder}/test-telemetry.json`; + FileSystem.deleteFile(telemetryFilePath); + + jest.spyOn(Autoinstaller.prototype, 'prepareAsync').mockImplementation(async function () {}); + + setSpawnMock({ emitError: false, returnCode: 1 }); + await parser.executeAsync(); + await procProm; + expect(process.exit).toHaveBeenCalledWith(1); + + expect(FileSystem.exists(telemetryFilePath)).toEqual(true); + + const telemetryStore: ITelemetryData[] = JsonFile.load(telemetryFilePath); + expect(telemetryStore?.[0].name).toEqual('build'); + expect(telemetryStore?.[0].result).toEqual('Failed'); + }); + }); + }); +}); diff --git a/libraries/rush-lib/src/cli/test/RushPluginAutoinstallerUpdate.test.ts b/libraries/rush-lib/src/cli/test/RushPluginAutoinstallerUpdate.test.ts new file mode 100644 index 00000000000..1ea0bc95453 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/RushPluginAutoinstallerUpdate.test.ts @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +jest.mock(`@rushstack/package-deps-hash`, () => { + return { + getRepoRoot(dir: string): string { + return dir; + }, + getDetailedRepoStateAsync(): IDetailedRepoState { + return { + hasSubmodules: false, + hasUncommittedChanges: false, + files: new Map([['common/config/rush/npm-shrinkwrap.json', 'hash']]), + symlinks: new Map() + }; + }, + getRepoChangesAsync(): ReadonlyMap { + return new Map(); + }, + getGitHashForFiles(filePaths: Iterable): ReadonlyMap { + return new Map(Array.from(filePaths, (filePath: string) => [filePath, filePath])); + }, + hashFilesAsync(rootDirectory: string, filePaths: Iterable): Promise> { + return Promise.resolve(new Map(Array.from(filePaths, (filePath: string) => [filePath, filePath]))); + } + }; +}); + +import './mockRushCommandLineParser'; + +import { FileSystem, JsonFile } from '@rushstack/node-core-library'; +import type { IDetailedRepoState } from '@rushstack/package-deps-hash'; +import { Autoinstaller } from '../../logic/Autoinstaller'; +import type { IRushPluginManifestJson } from '../../pluginFramework/PluginLoader/PluginLoaderBase'; +import { BaseInstallAction } from '../actions/BaseInstallAction'; +import { + getCommandLineParserInstanceAsync, + isolateEnvironmentConfigurationForTests, + type IEnvironmentConfigIsolation +} from './TestUtils'; + +interface IPluginTestPaths { + autoinstallerStorePath: string; + sourceCommandLineJsonPath: string; + sourceManifestPath: string; + destinationCommandLineJsonPath: string; + destinationManifestPath: string; +} + +function convertToCrLf(content: string): string { + return content.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n'); +} + +function mockAutoinstallerPrepareAsync(): jest.SpyInstance, []> { + return jest.spyOn(Autoinstaller.prototype, 'prepareAsync').mockResolvedValue(undefined); +} + +function seedPluginFilesWithCrLf(repoPath: string, packageName: string): IPluginTestPaths { + const autoinstallerPath: string = `${repoPath}/common/autoinstallers/plugins`; + const autoinstallerStorePath: string = `${autoinstallerPath}/rush-plugins`; + const installedPluginPath: string = `${autoinstallerPath}/node_modules/${packageName}`; + const sourcePluginPath: string = `${repoPath}/${packageName}`; + const sourceManifestPath: string = `${installedPluginPath}/rush-plugin-manifest.json`; + const sourceCommandLineJsonPath: string = `${installedPluginPath}/command-line.json`; + const destinationManifestPath: string = `${autoinstallerStorePath}/${packageName}/rush-plugin-manifest.json`; + const destinationCommandLineJsonPath: string = `${autoinstallerStorePath}/${packageName}/${packageName}/command-line.json`; + + FileSystem.copyFiles({ + sourcePath: sourcePluginPath, + destinationPath: installedPluginPath + }); + + const manifestJson: IRushPluginManifestJson = JsonFile.load( + `${sourcePluginPath}/rush-plugin-manifest.json` + ); + manifestJson.plugins[0].commandLineJsonFilePath = 'command-line.json'; + FileSystem.writeFile(sourceManifestPath, convertToCrLf(`${JSON.stringify(manifestJson, undefined, 2)}\n`), { + ensureFolderExists: true + }); + + const commandLineJsonFixturePath: string = destinationCommandLineJsonPath; + const commandLineJsonContent: string = FileSystem.readFile(commandLineJsonFixturePath); + FileSystem.writeFile(sourceCommandLineJsonPath, convertToCrLf(commandLineJsonContent), { + ensureFolderExists: true + }); + + return { + autoinstallerStorePath, + sourceCommandLineJsonPath, + sourceManifestPath, + destinationCommandLineJsonPath, + destinationManifestPath + }; +} + +function expectFileToUseLfLineEndings(filePath: string): void { + const content: string = FileSystem.readFile(filePath); + expect(content).toContain('\n'); + expect(content).not.toContain('\r\n'); +} + +describe('RushPluginAutoinstallerUpdate', () => { + let _envIsolation: IEnvironmentConfigIsolation; + + beforeEach(() => { + _envIsolation = isolateEnvironmentConfigurationForTests(); + }); + + afterEach(() => { + _envIsolation.restore(); + jest.restoreAllMocks(); + }); + + it('update() creates destination folders that do not exist when writing plugin files', async () => { + const repoName: string = 'pluginWithBuildCommandRepo'; + const packageName: string = 'rush-build-command-plugin'; + const { parser, repoPath } = await getCommandLineParserInstanceAsync(repoName, 'update'); + const { + autoinstallerStorePath, + sourceCommandLineJsonPath, + sourceManifestPath, + destinationCommandLineJsonPath, + destinationManifestPath + } = seedPluginFilesWithCrLf(repoPath, packageName); + + FileSystem.ensureEmptyFolder(autoinstallerStorePath); + + expect(FileSystem.exists(destinationManifestPath)).toBe(false); + expect(FileSystem.exists(destinationCommandLineJsonPath)).toBe(false); + expect(FileSystem.readFile(sourceManifestPath)).toContain('\r\n'); + expect(FileSystem.readFile(sourceCommandLineJsonPath)).toContain('\r\n'); + + const prepareAsyncSpy = mockAutoinstallerPrepareAsync(); + const baseInstallActionPrototype: { runAsync(): Promise } = + BaseInstallAction.prototype as unknown as { runAsync(): Promise }; + const runAsyncSpy = jest.spyOn(baseInstallActionPrototype, 'runAsync').mockResolvedValue(undefined); + + try { + await expect(parser.executeAsync()).resolves.toEqual(true); + } finally { + runAsyncSpy.mockRestore(); + prepareAsyncSpy.mockRestore(); + } + + expect(FileSystem.exists(destinationManifestPath)).toBe(true); + expect(FileSystem.exists(destinationCommandLineJsonPath)).toBe(true); + expectFileToUseLfLineEndings(destinationManifestPath); + expectFileToUseLfLineEndings(destinationCommandLineJsonPath); + }); +}); diff --git a/libraries/rush-lib/src/cli/test/RushPluginCommandLineParameters.test.ts b/libraries/rush-lib/src/cli/test/RushPluginCommandLineParameters.test.ts index d92b0ece981..04c968f136d 100644 --- a/libraries/rush-lib/src/cli/test/RushPluginCommandLineParameters.test.ts +++ b/libraries/rush-lib/src/cli/test/RushPluginCommandLineParameters.test.ts @@ -1,11 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. + import './mockRushCommandLineParser'; -import path from 'path'; +import path from 'node:path'; import { FileSystem, LockFile } from '@rushstack/node-core-library'; import { RushCommandLineParser } from '../RushCommandLineParser'; import { Autoinstaller } from '../../logic/Autoinstaller'; +import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration'; describe('PluginCommandLineParameters', () => { let originCWD: string | undefined; @@ -45,7 +47,7 @@ describe('PluginCommandLineParameters', () => { beforeEach(() => { // ts-command-line calls process.exit() which interferes with Jest - jest.spyOn(process, 'exit').mockImplementation((code?: number) => { + jest.spyOn(process, 'exit').mockImplementation((code) => { throw new Error(`Test code called process.exit(${code})`); }); @@ -60,6 +62,8 @@ describe('PluginCommandLineParameters', () => { originCWD = undefined; process.argv = _argv; } + + EnvironmentConfiguration.reset(); }); afterAll(() => { @@ -72,7 +76,7 @@ describe('PluginCommandLineParameters', () => { mockProcessArgv(['fake-node', 'fake-rush', 'cmd-parameters-test', '--mystring', '123']); const parser = new RushCommandLineParser({ cwd: currentCWD }); - await expect(parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); const action = parser.actions.find((ac) => ac.actionName === 'cmd-parameters-test'); @@ -84,7 +88,7 @@ describe('PluginCommandLineParameters', () => { mockProcessArgv(['fake-node', 'fake-rush', 'cmd-parameters-test', '--myinteger', '1']); const parser = new RushCommandLineParser({ cwd: currentCWD }); - await expect(parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); const action = parser.actions.find((ac) => ac.actionName === 'cmd-parameters-test'); @@ -96,7 +100,7 @@ describe('PluginCommandLineParameters', () => { mockProcessArgv(['fake-node', 'fake-rush', 'cmd-parameters-test', '--myflag']); const parser = new RushCommandLineParser({ cwd: currentCWD }); - await expect(parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); const action = parser.actions.find((ac) => ac.actionName === 'cmd-parameters-test'); @@ -108,7 +112,7 @@ describe('PluginCommandLineParameters', () => { mockProcessArgv(['fake-node', 'fake-rush', 'cmd-parameters-test', '--mychoice', 'a']); const parser = new RushCommandLineParser({ cwd: currentCWD }); - await expect(parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); const action = parser.actions.find((ac) => ac.actionName === 'cmd-parameters-test'); @@ -128,7 +132,7 @@ describe('PluginCommandLineParameters', () => { ]); const parser = new RushCommandLineParser({ cwd: currentCWD }); - await expect(parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); const action = parser.actions.find((ac) => ac.actionName === 'cmd-parameters-test'); @@ -148,7 +152,7 @@ describe('PluginCommandLineParameters', () => { ]); const parser = new RushCommandLineParser({ cwd: currentCWD }); - await expect(parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); const action = parser.actions.find((ac) => ac.actionName === 'cmd-parameters-test'); expect(action?.getIntegerListParameter('--myintegerlist').values).toStrictEqual([1, 2]); @@ -167,7 +171,7 @@ describe('PluginCommandLineParameters', () => { ]); const parser = new RushCommandLineParser({ cwd: currentCWD }); - await expect(parser.execute()).resolves.toEqual(true); + await expect(parser.executeAsync()).resolves.toEqual(true); const action = parser.actions.find((ac) => ac.actionName === 'cmd-parameters-test'); expect(action?.getChoiceListParameter('--mychoicelist').values).toStrictEqual(['a', 'c']); diff --git a/libraries/rush-lib/src/cli/test/RushPnpmCommandLineParser.test.ts b/libraries/rush-lib/src/cli/test/RushPnpmCommandLineParser.test.ts new file mode 100644 index 00000000000..d067e8bb5ce --- /dev/null +++ b/libraries/rush-lib/src/cli/test/RushPnpmCommandLineParser.test.ts @@ -0,0 +1,224 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { FileSystem, JsonFile } from '@rushstack/node-core-library'; + +import { RushConfiguration } from '../../api/RushConfiguration'; +import type { Subspace } from '../../api/Subspace'; +import { RushPnpmCommandLineParser } from '../RushPnpmCommandLineParser'; + +interface IRushPnpmCommandLineParserInternals { + _validatePnpmUsageAsync(pnpmArgs: string[]): Promise; +} + +async function validatePnpmArgsAsync(pnpmArgs: string[]): Promise { + const parser: IRushPnpmCommandLineParserInternals = Object.create(RushPnpmCommandLineParser.prototype); + await parser._validatePnpmUsageAsync(pnpmArgs); + return pnpmArgs; +} + +const SUBSPACE_TEMP_FOLDER: string = '/repo/common/temp'; + +function createPostExecuteParser(options: { + commandName: string; + pnpmVersion: string; + globalPatchedDependencies: Record | undefined; + updateGlobalPatchedDependencies: jest.Mock; + doRushUpdateAsync: jest.Mock; +}): RushPnpmCommandLineParser { + const parser: RushPnpmCommandLineParser = Object.create(RushPnpmCommandLineParser.prototype); + Object.assign(parser, { + _commandName: options.commandName, + _rushConfiguration: { packageManagerToolVersion: options.pnpmVersion }, + _terminal: { writeWarningLine: jest.fn(), writeErrorLine: jest.fn() }, + _doRushUpdateAsync: options.doRushUpdateAsync, + _subspace: { + getSubspaceTempFolderPath: () => SUBSPACE_TEMP_FOLDER, + getSubspaceConfigFolderPath: () => '/repo/common/config/rush', + getSubspacePnpmPatchesFolderPath: () => '/repo/common/config/rush/pnpm-patches', + getPnpmOptions: () => ({ + globalPatchedDependencies: options.globalPatchedDependencies, + updateGlobalPatchedDependencies: options.updateGlobalPatchedDependencies + }) + } + }); + return parser; +} + +describe(RushPnpmCommandLineParser.name, () => { + it('adds recursive mode to workspace query commands by default', async () => { + await expect(validatePnpmArgsAsync(['outdated'])).resolves.toEqual(['outdated', '--recursive']); + await expect(validatePnpmArgsAsync(['why', '@rushstack/node-core-library'])).resolves.toEqual([ + 'why', + '--recursive', + '@rushstack/node-core-library' + ]); + }); + + it('does not duplicate explicit recursive flags', async () => { + await expect(validatePnpmArgsAsync(['outdated', '-r'])).resolves.toEqual(['outdated', '-r']); + await expect( + validatePnpmArgsAsync(['why', '--recursive', '@rushstack/node-core-library']) + ).resolves.toEqual(['why', '--recursive', '@rushstack/node-core-library']); + }); + + it('does not force recursive mode for global outdated checks', async () => { + await expect(validatePnpmArgsAsync(['outdated', '--global'])).resolves.toEqual(['outdated', '--global']); + }); +}); + +describe(`${RushPnpmCommandLineParser.name} catalog sync`, () => { + const PACKAGE_ROOT: string = path.resolve(__dirname, '../../..'); + const TEST_TEMP_FOLDER: string = `${PACKAGE_ROOT}/temp/rush-pnpm-catalog-sync-test`; + const FIXTURE_FOLDER: string = `${__dirname}/catalogSyncTestRepo`; + + interface IRushPnpmCommandLineParserCatalogInternals { + _commandName: string; + _subspace: Subspace; + _terminal: { writeWarningLine(message: string): void }; + _doRushUpdateAsync(): Promise; + _postExecuteAsync(): Promise; + } + + function createParserForCommand( + repoFolder: string, + commandName: string + ): { parser: IRushPnpmCommandLineParserCatalogInternals; pnpmConfigFilename: string } { + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile( + `${repoFolder}/rush.json` + ); + const subspace: Subspace = rushConfiguration.defaultSubspace; + + const parser: IRushPnpmCommandLineParserCatalogInternals = Object.create( + RushPnpmCommandLineParser.prototype + ); + parser._commandName = commandName; + parser._subspace = subspace; + parser._terminal = { writeWarningLine: () => {} }; + // Avoid triggering a real "rush update" + parser._doRushUpdateAsync = async () => {}; + + return { + parser, + pnpmConfigFilename: `${repoFolder}/common/config/rush/pnpm-config.json` + }; + } + + beforeEach(async () => { + await FileSystem.deleteFolderAsync(TEST_TEMP_FOLDER); + await FileSystem.copyFilesAsync({ + sourcePath: FIXTURE_FOLDER, + destinationPath: TEST_TEMP_FOLDER + }); + }); + + afterEach(async () => { + await FileSystem.deleteFolderAsync(TEST_TEMP_FOLDER); + }); + + it('writes updated catalog versions from pnpm-workspace.yaml back to pnpm-config.json', async () => { + // Simulate "pnpm up" having bumped a catalog entry in the generated workspace file + const workspaceYamlFilename: string = `${TEST_TEMP_FOLDER}/common/temp/pnpm-workspace.yaml`; + const bumpedWorkspaceYaml: string = [ + 'packages:', + " - '../../apps/*'", + 'catalogs:', + ' default:', + ' react: ^18.2.0', + ' react-dom: ^18.2.0', + '' + ].join('\n'); + await FileSystem.writeFileAsync(workspaceYamlFilename, bumpedWorkspaceYaml); + + const { parser, pnpmConfigFilename } = createParserForCommand(TEST_TEMP_FOLDER, 'up'); + await parser._postExecuteAsync(); + + const updatedConfig: { globalCatalogs?: Record> } = + await JsonFile.loadAsync(pnpmConfigFilename); + expect(updatedConfig.globalCatalogs).toEqual({ + default: { + react: '^18.2.0', + 'react-dom': '^18.2.0' + } + }); + }); + + it('does not modify pnpm-config.json when the catalog is unchanged', async () => { + const { parser, pnpmConfigFilename } = createParserForCommand(TEST_TEMP_FOLDER, 'up'); + + const originalContent: string = await FileSystem.readFileAsync(pnpmConfigFilename); + const doRushUpdateSpy: jest.SpyInstance = jest + .spyOn(parser, '_doRushUpdateAsync') + .mockResolvedValue(undefined); + + await parser._postExecuteAsync(); + + // The fixture's pnpm-workspace.yaml already matches pnpm-config.json, so nothing should change + expect(await FileSystem.readFileAsync(pnpmConfigFilename)).toEqual(originalContent); + expect(doRushUpdateSpy).not.toHaveBeenCalled(); + }); +}); + +describe(`${RushPnpmCommandLineParser.name} patch-commit patchedDependencies sync`, () => { + it('reads patchedDependencies from pnpm-workspace.yaml for pnpm >= 11', async () => { + const updateGlobalPatchedDependencies: jest.Mock = jest.fn(); + const doRushUpdateAsync: jest.Mock = jest.fn(); + const parser: RushPnpmCommandLineParser = createPostExecuteParser({ + commandName: 'patch-commit', + pnpmVersion: '11.7.0', + globalPatchedDependencies: { 'left-pad@1.0.0': 'patches/left-pad@1.0.0.patch' }, + updateGlobalPatchedDependencies, + doRushUpdateAsync + }); + + const workspaceYaml: string = + 'packages:\n' + + ' - ../../app\n' + + 'patchedDependencies:\n' + + ' lodash@4.17.21: patches/lodash@4.17.21.patch\n'; + const readFileAsyncSpy: jest.SpyInstance = jest + .spyOn(FileSystem, 'readFileAsync') + .mockResolvedValue(workspaceYaml); + // If the code incorrectly read package.json for pnpm 11, it would pick up this sentinel value. + const jsonLoadSpy: jest.SpyInstance = jest + .spyOn(JsonFile, 'load') + .mockReturnValue({ pnpm: { patchedDependencies: { 'should-not-be-used@1.0.0': 'x.patch' } } }); + + await parser['_postExecuteAsync'](); + + expect(readFileAsyncSpy).toHaveBeenCalledWith(`${SUBSPACE_TEMP_FOLDER}/pnpm-workspace.yaml`); + expect(jsonLoadSpy).not.toHaveBeenCalled(); + expect(updateGlobalPatchedDependencies).toHaveBeenCalledWith({ + 'lodash@4.17.21': 'patches/lodash@4.17.21.patch' + }); + expect(doRushUpdateAsync).toHaveBeenCalledTimes(1); + }); + + it('reads patchedDependencies from package.json for pnpm < 11', async () => { + const updateGlobalPatchedDependencies: jest.Mock = jest.fn(); + const doRushUpdateAsync: jest.Mock = jest.fn(); + const parser: RushPnpmCommandLineParser = createPostExecuteParser({ + commandName: 'patch-commit', + pnpmVersion: '10.27.0', + globalPatchedDependencies: { 'left-pad@1.0.0': 'patches/left-pad@1.0.0.patch' }, + updateGlobalPatchedDependencies, + doRushUpdateAsync + }); + + const readFileAsyncSpy: jest.SpyInstance = jest.spyOn(FileSystem, 'readFileAsync').mockResolvedValue(''); + const jsonLoadSpy: jest.SpyInstance = jest.spyOn(JsonFile, 'load').mockReturnValue({ + pnpm: { patchedDependencies: { 'lodash@4.17.21': 'patches/lodash@4.17.21.patch' } } + }); + + await parser['_postExecuteAsync'](); + + expect(jsonLoadSpy).toHaveBeenCalledWith(`${SUBSPACE_TEMP_FOLDER}/package.json`); + expect(readFileAsyncSpy).not.toHaveBeenCalled(); + expect(updateGlobalPatchedDependencies).toHaveBeenCalledWith({ + 'lodash@4.17.21': 'patches/lodash@4.17.21.patch' + }); + expect(doRushUpdateAsync).toHaveBeenCalledTimes(1); + }); +}); diff --git a/libraries/rush-lib/src/cli/test/RushXCommandLine.test.ts b/libraries/rush-lib/src/cli/test/RushXCommandLine.test.ts index 32d0cf7676c..0bb398c4698 100644 --- a/libraries/rush-lib/src/cli/test/RushXCommandLine.test.ts +++ b/libraries/rush-lib/src/cli/test/RushXCommandLine.test.ts @@ -1,13 +1,17 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +jest.mock('../../logic/dotenv', () => ({ + initializeDotEnv: () => {} +})); + import { PackageJsonLookup } from '@rushstack/node-core-library'; -import * as colorsPackage from 'colors'; import { Utilities } from '../../utilities/Utilities'; -import { Rush } from '../../api/Rush'; +import { Rush, type ILaunchOptions } from '../../api/Rush'; import { RushConfiguration } from '../../api/RushConfiguration'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import { NodeJsCompatibility } from '../../logic/NodeJsCompatibility'; import { RushXCommandLine } from '../RushXCommandLine'; @@ -17,14 +21,8 @@ describe(RushXCommandLine.name, () => { let executeLifecycleCommandMock: jest.SpyInstance | undefined; let logMock: jest.SpyInstance | undefined; let rushConfiguration: RushConfiguration | undefined; - let colorsEnabled: boolean; beforeEach(() => { - colorsEnabled = colorsPackage.enabled; - if (!colorsEnabled) { - colorsPackage.enable(); - } - // Mock process $argv = process.argv; process.argv = [...process.argv]; @@ -67,20 +65,19 @@ describe(RushXCommandLine.name, () => { return projects.find((project) => project.projectFolder === path); } } as RushConfiguration; - jest.spyOn(RushConfiguration, 'tryLoadFromDefaultLocation').mockReturnValue(rushConfiguration); + jest.spyOn(RushConfiguration, 'tryFindRushJsonLocation').mockReturnValue('/Users/jdoe/bigrepo'); + jest.spyOn(RushConfiguration, 'loadFromConfigurationFile').mockReturnValue(rushConfiguration); // Mock command execution executeLifecycleCommandMock = jest.spyOn(Utilities, 'executeLifecycleCommand'); // Mock console log logMock = jest.spyOn(console, 'log'); + + jest.spyOn(NodeJsCompatibility, 'isLtsVersion', 'get').mockReturnValue(true); }); afterEach(() => { - if (!colorsEnabled) { - colorsPackage.disable(); - } - process.argv = $argv; Object.defineProperty(process, 'versions', { value: $versions @@ -91,12 +88,12 @@ describe(RushXCommandLine.name, () => { jest.restoreAllMocks(); }); - describe(RushXCommandLine.launchRushX.name, () => { + describe(RushXCommandLine.launchRushXAsync.name, () => { it('prints usage info', () => { process.argv = ['node', 'startx.js', '--help']; executeLifecycleCommandMock!.mockReturnValue(0); - RushXCommandLine.launchRushX('', true); + Rush.launchRushX('0.0.0', true as unknown as ILaunchOptions); expect(executeLifecycleCommandMock).not.toHaveBeenCalled(); expect(logMock!.mock.calls).toMatchSnapshot(); @@ -106,7 +103,7 @@ describe(RushXCommandLine.name, () => { process.argv = ['node', 'startx.js', 'build']; executeLifecycleCommandMock!.mockReturnValue(0); - RushXCommandLine.launchRushX('', true); + Rush.launchRushX('0.0.0', true as unknown as ILaunchOptions); expect(executeLifecycleCommandMock).toHaveBeenCalledWith('an acme project build command', { rushConfiguration, @@ -124,7 +121,7 @@ describe(RushXCommandLine.name, () => { process.argv = ['node', 'startx.js', '--quiet', 'build']; executeLifecycleCommandMock!.mockReturnValue(0); - RushXCommandLine.launchRushX('', true); + Rush.launchRushX('0.0.0', { isManaged: true }); expect(executeLifecycleCommandMock).toHaveBeenCalledWith('an acme project build command', { rushConfiguration, @@ -142,7 +139,7 @@ describe(RushXCommandLine.name, () => { process.argv = ['node', 'startx.js', 'asdf']; executeLifecycleCommandMock!.mockReturnValue(0); - RushXCommandLine.launchRushX('', true); + Rush.launchRushX('0.0.0', { isManaged: true }); expect(executeLifecycleCommandMock).not.toHaveBeenCalled(); expect(logMock!.mock.calls).toMatchSnapshot(); diff --git a/libraries/rush-lib/src/cli/test/TestUtils.ts b/libraries/rush-lib/src/cli/test/TestUtils.ts new file mode 100644 index 00000000000..c8191358c2c --- /dev/null +++ b/libraries/rush-lib/src/cli/test/TestUtils.ts @@ -0,0 +1,200 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { AlreadyExistsBehavior, FileSystem, PackageJsonLookup } from '@rushstack/node-core-library'; + +import type { RushCommandLineParser as RushCommandLineParserType } from '../RushCommandLineParser'; +import { FlagFile } from '../../api/FlagFile'; +import { RushConstants } from '../../logic/RushConstants'; +import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration'; + +export type SpawnMockArgs = Parameters; +export type SpawnMock = jest.Mock, SpawnMockArgs>; +export type SpawnMockCall = SpawnMock['mock']['calls'][number]; + +/** + * Interface definition for a test instance for the RushCommandLineParser. + */ +export interface IParserTestInstance { + parser: RushCommandLineParserType; + spawnMock: SpawnMock; + repoPath: string; +} + +/** + * See `./mock_child_process`. + */ +export interface ISpawnMockConfig { + emitError: boolean; + returnCode: number; +} + +export interface IChildProcessModuleMock { + /** + * Initialize the `spawn` mock behavior. + */ + __setSpawnMockConfig(config?: ISpawnMockConfig): void; + + spawn: jest.Mock; +} + +const DEFAULT_RUSH_ENV_VARS_TO_CLEAR: ReadonlyArray = [ + 'RUSH_BUILD_CACHE_OVERRIDE_JSON', + 'RUSH_BUILD_CACHE_OVERRIDE_JSON_FILE_PATH', + 'RUSH_BUILD_CACHE_CREDENTIAL', + 'RUSH_BUILD_CACHE_ENABLED', + 'RUSH_BUILD_CACHE_WRITE_ALLOWED' +]; + +export interface IWithEnvironmentConfigIsolationOptions { + envVarNamesToClear?: ReadonlyArray; + silenceStderrWrite?: boolean; +} + +export interface IEnvironmentConfigIsolation { + restore(): void; +} + +/** + * Configure the `child_process` `spawn` mock for these tests. This relies on the mock implementation + * in `mock_child_process`. + */ +export function setSpawnMock(options?: ISpawnMockConfig): jest.Mock { + const cpMocked: IChildProcessModuleMock = require('node:child_process'); + cpMocked.__setSpawnMockConfig(options); + + const spawnMock: jest.Mock = cpMocked.spawn; + spawnMock.mockName('spawn'); + return spawnMock; +} + +const PROJECT_ROOT: string = PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname)!; +export const TEST_REPO_FOLDER_PATH: string = `${PROJECT_ROOT}/temp/test/unit-test-repos`; + +/** + * Helper to set up a test instance for RushCommandLineParser. + */ +export async function getCommandLineParserInstanceAsync( + repoName: string, + taskName: string +): Promise { + // Copy the test repo to a sandbox folder + const repoPath: string = `${TEST_REPO_FOLDER_PATH}/${repoName}-${performance.now()}`; + + await FileSystem.copyFilesAsync({ + sourcePath: `${__dirname}/${repoName}`, + destinationPath: repoPath, + alreadyExistsBehavior: AlreadyExistsBehavior.Error + }); + + // The `build` task is hard-coded to be incremental. So delete the package-deps file folder in + // the test repo to guarantee the test actually runs. + await Promise.all([ + FileSystem.deleteFolderAsync(`${repoPath}/a/.rush/temp`), + FileSystem.deleteFolderAsync(`${repoPath}/b/.rush/temp`) + ]); + + const { RushCommandLineParser } = await import('../RushCommandLineParser'); + + // Create a Rush CLI instance. This instance is heavy-weight and relies on setting process.exit + // to exit and clear the Rush file lock. So running multiple `it` or `describe` test blocks over the same test + // repo will fail due to contention over the same lock which is kept until the test runner process + // ends. + const parser: RushCommandLineParserType = new RushCommandLineParser({ cwd: repoPath }); + + // Bulk tasks are hard-coded to expect install to have been completed. So, ensure the last-link.flag + // file exists and is valid + await new FlagFile( + parser.rushConfiguration.defaultSubspace.getSubspaceTempFolderPath(), + RushConstants.lastLinkFlagFilename, + {} + ).createAsync(); + + // Mock the command + process.argv = ['pretend-this-is-node.exe', 'pretend-this-is-rush', taskName]; + const spawnMock: jest.Mock = setSpawnMock(); + + return { + parser, + spawnMock, + repoPath + }; +} + +/** + * Clears Rush-related environment variables and resets EnvironmentConfiguration for deterministic tests. + * + * Notes: + * - EnvironmentConfiguration caches some values, so we also stub the build-cache override getters. + * - Rush treats any stderr output during `rush test` as a warning, which fails the command; some + * tests intentionally simulate failures and may need stderr silenced. + */ +export function isolateEnvironmentConfigurationForTests( + options: IWithEnvironmentConfigIsolationOptions = {} +): IEnvironmentConfigIsolation { + const envVarNamesToClear: ReadonlyArray = + options.envVarNamesToClear ?? DEFAULT_RUSH_ENV_VARS_TO_CLEAR; + + const savedProcessEnv: Record = {}; + for (const envVarName of envVarNamesToClear) { + savedProcessEnv[envVarName] = process.env[envVarName]; + delete process.env[envVarName]; + } + + EnvironmentConfiguration.reset(); + + const restoreFns: Array<() => void> = []; + + restoreFns.push(() => { + for (const envVarName of envVarNamesToClear) { + const oldValue: string | undefined = savedProcessEnv[envVarName]; + if (oldValue === undefined) { + delete process.env[envVarName]; + } else { + process.env[envVarName] = oldValue; + } + } + }); + + if (options.silenceStderrWrite) { + type StderrWrite = typeof process.stderr.write; + const silentWrite: unknown = ( + chunk: string | Uint8Array, + encoding?: BufferEncoding | ((err?: Error | null) => void), + cb?: (err?: Error | null) => void + ): boolean => { + if (typeof encoding === 'function') { + encoding(null); + } else { + cb?.(null); + } + return true; + }; + + const writeSpy: jest.SpyInstance, Parameters> = jest + .spyOn(process.stderr, 'write') + .mockImplementation(silentWrite as StderrWrite); + + restoreFns.push(() => writeSpy.mockRestore()); + } + + // EnvironmentConfiguration.reset() does not clear cached values for these fields. + const overrideJsonFilePathSpy: jest.SpyInstance = jest + .spyOn(EnvironmentConfiguration, 'buildCacheOverrideJsonFilePath', 'get') + .mockReturnValue(undefined); + const overrideJsonSpy: jest.SpyInstance = jest + .spyOn(EnvironmentConfiguration, 'buildCacheOverrideJson', 'get') + .mockReturnValue(undefined); + + restoreFns.push(() => overrideJsonFilePathSpy.mockRestore()); + restoreFns.push(() => overrideJsonSpy.mockRestore()); + restoreFns.push(() => EnvironmentConfiguration.reset()); + + return { + restore: () => { + for (let i: number = restoreFns.length - 1; i >= 0; i--) { + restoreFns[i](); + } + } + }; +} diff --git a/libraries/rush-lib/src/cli/test/__snapshots__/CommandLineHelp.test.ts.snap b/libraries/rush-lib/src/cli/test/__snapshots__/CommandLineHelp.test.ts.snap index 6e54c87c5ed..efe3e717b7d 100644 --- a/libraries/rush-lib/src/cli/test/__snapshots__/CommandLineHelp.test.ts.snap +++ b/libraries/rush-lib/src/cli/test/__snapshots__/CommandLineHelp.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`CommandLineHelp prints the global help 1`] = ` "usage: rush [-h] [-d] [-q] ... @@ -29,6 +29,7 @@ Positional arguments: init-autoinstaller Initializes a new autoinstaller init-deploy Creates a deployment scenario config file for use with \\"rush deploy\\". + init-subspace Create a new subspace. install Install package dependencies for all projects in the repo according to the shrinkwrap file link Create node_modules symlinks for all projects @@ -50,6 +51,8 @@ Positional arguments: update Install package dependencies for all projects in the repo, and create or update the shrinkwrap file as needed + install-autoinstaller + Install autoinstaller package dependencies update-autoinstaller Updates autoinstaller package dependencies update-cloud-credentials @@ -59,6 +62,13 @@ Positional arguments: Provides interactive prompt for upgrading package dependencies per project version Manage package versions in the repo. + alert (EXPERIMENTAL) View and manage Rush alerts for the + repository + bridge-package (EXPERIMENTAL) Use hotlinks to simulate upgrade of a + dependency for all consumers across a lockfile. + link-package (EXPERIMENTAL) Use hotlinks to simulate installation + of a locally built project folder as a dependency of + specific projects. import-strings Imports translated strings into each project. upload Uploads the built files to the server build Build all projects that haven't been built, or have @@ -77,7 +87,9 @@ Optional arguments: `; exports[`CommandLineHelp prints the help for each action: add 1`] = ` -"usage: rush add [-h] [-s] -p PACKAGE [--exact] [--caret] [--dev] [-m] [--all] +"usage: rush add [-h] [-s] -p PACKAGE [--all] [--variant VARIANT] [--exact] + [--caret] [--dev] [--peer] [-m] + Adds specified package(s) to the dependencies of the current project (as determined by the current working directory) and then runs \\"rush update\\". If @@ -103,6 +115,11 @@ Optional arguments: \\"rush add --package example@^1.2.3\\". To add multiple packages, write \\"rush add --package foo --package bar\\". + --all If specified, the dependency will be added to all + projects. + --variant VARIANT Run command using a variant installation + configuration. This parameter may alternatively be + specified via the RUSH_VARIANT environment variable. --exact If specified, the SemVer specifier added to the package.json will be an exact version (e.g. without tilde or caret). @@ -111,21 +128,71 @@ Optional arguments: specifier (\\"^\\"). --dev If specified, the package will be added to the \\"devDependencies\\" section of the package.json + --peer If specified, the package will be added to the + \\"peerDependencies\\" section of the package.json -m, --make-consistent If specified, other packages with this dependency will have their package.json files updated to use the same version of the dependency. - --all If specified, the dependency will be added to all - projects. +" +`; + +exports[`CommandLineHelp prints the help for each action: alert 1`] = ` +"usage: rush alert [-h] [-s ALERT_ID] [--forever] + +This command displays the Rush alerts for this repository. Rush alerts are +customizable announcements and reminders that Rush prints occasionally on the +command line. The alert definitions can be found in the rush-alerts.json +config file. + +Optional arguments: + -h, --help Show this help message and exit. + -s ALERT_ID, --snooze ALERT_ID + Temporarily suspend the specified alert for one week + --forever Combined with \\"--snooze\\", causes that alert to be + suspended permanently +" +`; + +exports[`CommandLineHelp prints the help for each action: bridge-package 1`] = ` +"usage: rush bridge-package [-h] --path PATH [--version SEMVER_RANGE] + [--subspace SUBSPACE_NAME] + + +This command enables you to test a locally built project by simulating its +upgrade by updating node_modules folders using hotlinks. Unlike \\"pnpm link\\" +and \\"npm link\\", the hotlinks created by this command affect all Rush projects +across the lockfile, as well as their indirect dependencies. The simulated +installation is not reflected in pnpm-lock.yaml, does not install new package. +json dependencies, and simply updates the contents of existing node_modules +folders of \\"rush install\\". The hotlinks will be cleared when you next run +\\"rush install\\" or \\"rush update\\". Compare with the \\"rush link-package\\" command, + which affects only the consuming project. + +Optional arguments: + -h, --help Show this help message and exit. + --path PATH The path of folder of a project outside of this Rush + repo, whose installation will be simulated using + node_modules symlinks (\\"hotlinks\\"). This folder is + the symlink target. + --version SEMVER_RANGE + Specify which installed versions should be hotlinked. + The default value is \\"*\\". + --subspace SUBSPACE_NAME + The name of the subspace to use for the hotlinked + package. " `; exports[`CommandLineHelp prints the help for each action: build 1`] = ` -"usage: rush build [-h] [-p COUNT] [--timeline] [-t PROJECT] [-T PROJECT] - [-f PROJECT] [-o PROJECT] [-i PROJECT] [-I PROJECT] +"usage: rush build [-h] [-p COUNT] [--timeline] [--log-cobuild-plan] + [-t PROJECT] [-T PROJECT] [-f PROJECT] [-o PROJECT] + [-i PROJECT] [-I PROJECT] [--to-version-policy VERSION_POLICY_NAME] - [--from-version-policy VERSION_POLICY_NAME] [-v] [-c] - [--ignore-hooks] [-s] [-m] + [--from-version-policy VERSION_POLICY_NAME] [-v] + [--include-phase-deps] [-c] [--ignore-hooks] + [--node-diagnostic-dir DIRECTORY] [--debug-build-cache-ids] + [-s] [-m] This command is similar to \\"rush rebuild\\", except that \\"rush build\\" performs @@ -156,6 +223,10 @@ Optional arguments: statistics and CPU usage information, including an ASCII chart of the start and stop times for each operation. + --log-cobuild-plan (EXPERIMENTAL) Before the build starts, log + information about the cobuild state. This will + include information about clusters and the projects + that are part of each cluster. -t PROJECT, --to PROJECT Normally all projects in the monorepo will be processed; adding this parameter will instead select @@ -235,6 +306,14 @@ Optional arguments: subsets of projects\\". -v, --verbose Display the logs during the build, rather than just displaying the build status summary + --include-phase-deps If the selected projects are \\"unsafe\\" (missing some + dependencies), add the minimal set of phase + dependencies. For example, \\"--from A\\" normally might + include the \\"_phase:test\\" phase for A's dependencies, + even though changes to A can't break those tests. + Using \\"--impacted-by A --include-phase-deps\\" avoids + that work by performing \\"_phase:test\\" only for + downstream projects. -c, --changed-projects-only Normally the incremental build logic will rebuild changed projects as well as any projects that @@ -247,6 +326,14 @@ Optional arguments: --ignore-hooks Skips execution of the \\"eventHooks\\" scripts defined in rush.json. Make sure you know what you are skipping. + --node-diagnostic-dir DIRECTORY + Specifies the directory where Node.js diagnostic + reports will be written. This directory will contain + a subdirectory for each project and phase. + --debug-build-cache-ids + Logs information about the components of the build + cache ids for individual operations. This is useful + for debugging the incremental build logic. -s, --ship Perform a production build, including minification and localization steps -m, --minimal Perform a fast build, which disables certain tasks @@ -255,9 +342,10 @@ Optional arguments: `; exports[`CommandLineHelp prints the help for each action: change 1`] = ` -"usage: rush change [-h] [-v] [--no-fetch] [-b BRANCH] [--overwrite] [-c] - [--commit-message COMMIT_MESSAGE] [--email EMAIL] [--bulk] - [--message MESSAGE] [--bump-type {major,minor,patch,none}] +"usage: rush change [-h] [-v] [--verify-all] [--no-fetch] [-b BRANCH] + [--overwrite] [-c] [--commit-message COMMIT_MESSAGE] + [--email EMAIL] [--bulk] [--message MESSAGE] + [--bump-type {major,minor,patch,none}] Asks a series of questions and then generates a -.json @@ -283,6 +371,12 @@ Optional arguments: -h, --help Show this help message and exit. -v, --verify Verify the change file has been generated and that it is a valid JSON file + --verify-all Validate all change files in the repository, not just + those added in the current branch. Reports errors for + change files that reference nonexistent projects or + target non-main projects in a lockstepped version + policy. Requires the \\"strictChangefileValidation\\" + experiment to be enabled. --no-fetch Skips fetching the baseline branch before running \\"git diff\\" to detect changes. -b BRANCH, --target-branch BRANCH @@ -315,20 +409,29 @@ Optional arguments: `; exports[`CommandLineHelp prints the help for each action: check 1`] = ` -"usage: rush check [-h] [--variant VARIANT] [--json] [--verbose] +"usage: rush check [-h] [--json] [--verbose] [--subspace SUBSPACE_NAME] + [--variant VARIANT] + Checks each project's package.json files and ensures that all dependencies are of the same version throughout the repository. Optional arguments: - -h, --help Show this help message and exit. - --variant VARIANT Run command using a variant installation configuration. - This parameter may alternatively be specified via the - RUSH_VARIANT environment variable. - --json If this flag is specified, output will be in JSON format. - --verbose If this flag is specified, long lists of package names - will not be truncated. This has no effect if the --json - flag is also specified. + -h, --help Show this help message and exit. + --json If this flag is specified, output will be in JSON + format. + --verbose If this flag is specified, long lists of package + names will not be truncated. This has no effect if + the --json flag is also specified. + --subspace SUBSPACE_NAME + (EXPERIMENTAL) Specifies an individual Rush subspace + to check, requiring versions to be consistent only + within that subspace (ignoring other subspaces). This + parameter is required when the \\"subspacesEnabled\\" + setting is set to true in subspaces.json. + --variant VARIANT Run command using a variant installation + configuration. This parameter may alternatively be + specified via the RUSH_VARIANT environment variable. " `; @@ -384,12 +487,14 @@ Optional arguments: `; exports[`CommandLineHelp prints the help for each action: import-strings 1`] = ` -"usage: rush import-strings [-h] [-p COUNT] [--timeline] [-t PROJECT] - [-T PROJECT] [-f PROJECT] [-o PROJECT] [-i PROJECT] - [-I PROJECT] +"usage: rush import-strings [-h] [-p COUNT] [--timeline] [--log-cobuild-plan] + [-t PROJECT] [-T PROJECT] [-f PROJECT] [-o PROJECT] + [-i PROJECT] [-I PROJECT] [--to-version-policy VERSION_POLICY_NAME] [--from-version-policy VERSION_POLICY_NAME] [-v] - [--ignore-hooks] + [--include-phase-deps] [--ignore-hooks] + [--node-diagnostic-dir DIRECTORY] + [--debug-build-cache-ids] [--locale {en-us,fr-fr,es-es,zh-cn}] @@ -412,6 +517,10 @@ Optional arguments: statistics and CPU usage information, including an ASCII chart of the start and stop times for each operation. + --log-cobuild-plan (EXPERIMENTAL) Before the build starts, log + information about the cobuild state. This will + include information about clusters and the projects + that are part of each cluster. -t PROJECT, --to PROJECT Normally all projects in the monorepo will be processed; adding this parameter will instead select @@ -491,9 +600,25 @@ Optional arguments: subsets of projects\\". -v, --verbose Display the logs during the build, rather than just displaying the build status summary + --include-phase-deps If the selected projects are \\"unsafe\\" (missing some + dependencies), add the minimal set of phase + dependencies. For example, \\"--from A\\" normally might + include the \\"_phase:test\\" phase for A's dependencies, + even though changes to A can't break those tests. + Using \\"--impacted-by A --include-phase-deps\\" avoids + that work by performing \\"_phase:test\\" only for + downstream projects. --ignore-hooks Skips execution of the \\"eventHooks\\" scripts defined in rush.json. Make sure you know what you are skipping. + --node-diagnostic-dir DIRECTORY + Specifies the directory where Node.js diagnostic + reports will be written. This directory will contain + a subdirectory for each project and phase. + --debug-build-cache-ids + Logs information about the components of the build + cache ids for individual operations. This is useful + for debugging the incremental build logic. --locale {en-us,fr-fr,es-es,zh-cn} Selects a single instead of the default locale (en-us) for non-ship builds or all locales for ship @@ -503,6 +628,8 @@ Optional arguments: exports[`CommandLineHelp prints the help for each action: init 1`] = ` "usage: rush init [-h] [--overwrite-existing] [--rush-example-repo] + [--include-experiments] + When invoked in an empty folder, this command provisions a standard set of config file templates to start managing projects using Rush. @@ -519,6 +646,10 @@ Optional arguments: monorepo that illustrates many Rush features. This option is primarily intended for maintaining that example. + --include-experiments + Include features that may not be complete features, + useful for demoing specific future features or + current work in progress features. " `; @@ -562,14 +693,29 @@ Optional arguments: " `; +exports[`CommandLineHelp prints the help for each action: init-subspace 1`] = ` +"usage: rush init-subspace [-h] -n SUBSPACE_NAME + +Use this command to create a new subspace with the default subspace +configuration files. + +Optional arguments: + -h, --help Show this help message and exit. + -n SUBSPACE_NAME, --name SUBSPACE_NAME + The name of the subspace that is being initialized. +" +`; + exports[`CommandLineHelp prints the help for each action: install 1`] = ` "usage: rush install [-h] [-p] [--bypass-policy] [--no-link] [--network-concurrency COUNT] [--debug-package-manager] [--max-install-attempts NUMBER] [--ignore-hooks] - [--variant VARIANT] [-t PROJECT] [-T PROJECT] [-f PROJECT] - [-o PROJECT] [-i PROJECT] [-I PROJECT] + [--offline] [--variant VARIANT] [-t PROJECT] [-T PROJECT] + [-f PROJECT] [-o PROJECT] [-i PROJECT] [-I PROJECT] [--to-version-policy VERSION_POLICY_NAME] - [--from-version-policy VERSION_POLICY_NAME] [--check-only] + [--from-version-policy VERSION_POLICY_NAME] + [--subspace SUBSPACE_NAME] [--check-only] + [--resolution-only] The \\"rush install\\" command installs package dependencies for all your @@ -610,6 +756,11 @@ Optional arguments: --ignore-hooks Skips execution of the \\"eventHooks\\" scripts defined in rush.json. Make sure you know what you are skipping. + --offline Enables installation to be performed without internet + access. PNPM will instead report an error if the + necessary NPM packages cannot be obtained from the + local cache. For details, see the documentation for + PNPM's \\"--offline\\" parameter. --variant VARIANT Run command using a variant installation configuration. This parameter may alternatively be specified via the RUSH_VARIANT environment variable. @@ -690,8 +841,29 @@ Optional arguments: each of the projects belonging to VERSION_POLICY_NAME. For details, refer to the website article \\"Selecting subsets of projects\\". + --subspace SUBSPACE_NAME + (EXPERIMENTAL) Specifies a Rush subspace to be + installed. Requires the \\"subspacesEnabled\\" feature to + be enabled in subspaces.json. --check-only Only check the validity of the shrinkwrap file without performing an install. + --resolution-only Only perform dependency resolution, useful for + ensuring peer dependendencies are up to date. Note + that this flag is only supported when using the pnpm + package manager. +" +`; + +exports[`CommandLineHelp prints the help for each action: install-autoinstaller 1`] = ` +"usage: rush install-autoinstaller [-h] --name AUTOINSTALLER_NAME + +Use this command to install dependencies for an autoinstaller folder. + +Optional arguments: + -h, --help Show this help message and exit. + --name AUTOINSTALLER_NAME + The name of the autoinstaller, which must be one of + the folders under common/autoinstallers. " `; @@ -711,6 +883,32 @@ Optional arguments: " `; +exports[`CommandLineHelp prints the help for each action: link-package 1`] = ` +"usage: rush link-package [-h] --path PATH [--project PROJECT_NAME] + +This command enables you to test a locally built project by creating a +symlink under the specified projects' node_modules folders. The +implementation is similar to \\"pnpm link\\" and \\"npm link\\", but better +integrated with Rush features. Like those commands, the symlink (\\"hotlink\\") +is not reflected in pnpm-lock.yaml, affects the consuming project only, and +has the same limitations as \\"workspace:*\\". The hotlinks will be cleared when +you next run \\"rush install\\" or \\"rush update\\". Compare with the \\"rush +bridge-package\\" command, which affects the entire lockfile including indirect +dependencies. + +Optional arguments: + -h, --help Show this help message and exit. + --path PATH The path of folder of a project outside of this Rush + repo, whose installation will be simulated using + node_modules symlinks (\\"hotlinks\\"). This folder is + the symlink target. + --project PROJECT_NAME + A list of Rush project names that will be hotlinked + to the \\"--path\\" folder. If not specified, the default + is the project of the current working directory. +" +`; + exports[`CommandLineHelp prints the help for each action: list 1`] = ` "usage: rush list [-h] [-v] [-p] [--full-path] [--detailed] [--json] [-t PROJECT] [-T PROJECT] [-f PROJECT] [-o PROJECT] @@ -931,11 +1129,14 @@ Optional arguments: `; exports[`CommandLineHelp prints the help for each action: rebuild 1`] = ` -"usage: rush rebuild [-h] [-p COUNT] [--timeline] [-t PROJECT] [-T PROJECT] - [-f PROJECT] [-o PROJECT] [-i PROJECT] [-I PROJECT] +"usage: rush rebuild [-h] [-p COUNT] [--timeline] [--log-cobuild-plan] + [-t PROJECT] [-T PROJECT] [-f PROJECT] [-o PROJECT] + [-i PROJECT] [-I PROJECT] [--to-version-policy VERSION_POLICY_NAME] [--from-version-policy VERSION_POLICY_NAME] [-v] - [--ignore-hooks] [-s] [-m] + [--include-phase-deps] [--ignore-hooks] + [--node-diagnostic-dir DIRECTORY] + [--debug-build-cache-ids] [-s] [-m] This command assumes that the package.json file for each project contains a @@ -963,6 +1164,10 @@ Optional arguments: statistics and CPU usage information, including an ASCII chart of the start and stop times for each operation. + --log-cobuild-plan (EXPERIMENTAL) Before the build starts, log + information about the cobuild state. This will + include information about clusters and the projects + that are part of each cluster. -t PROJECT, --to PROJECT Normally all projects in the monorepo will be processed; adding this parameter will instead select @@ -1042,9 +1247,25 @@ Optional arguments: subsets of projects\\". -v, --verbose Display the logs during the build, rather than just displaying the build status summary + --include-phase-deps If the selected projects are \\"unsafe\\" (missing some + dependencies), add the minimal set of phase + dependencies. For example, \\"--from A\\" normally might + include the \\"_phase:test\\" phase for A's dependencies, + even though changes to A can't break those tests. + Using \\"--impacted-by A --include-phase-deps\\" avoids + that work by performing \\"_phase:test\\" only for + downstream projects. --ignore-hooks Skips execution of the \\"eventHooks\\" scripts defined in rush.json. Make sure you know what you are skipping. + --node-diagnostic-dir DIRECTORY + Specifies the directory where Node.js diagnostic + reports will be written. This directory will contain + a subdirectory for each project and phase. + --debug-build-cache-ids + Logs information about the components of the build + cache ids for individual operations. This is useful + for debugging the incremental build logic. -s, --ship Perform a production build, including minification and localization steps -m, --minimal Perform a fast build, which disables certain tasks @@ -1053,7 +1274,7 @@ Optional arguments: `; exports[`CommandLineHelp prints the help for each action: remove 1`] = ` -"usage: rush remove [-h] [-s] -p PACKAGE [--all] +"usage: rush remove [-h] [-s] -p PACKAGE [--all] [--variant VARIANT] Removes specified package(s) from the dependencies of the current project (as determined by the current working directory) and then runs \\"rush update\\". @@ -1068,6 +1289,9 @@ Optional arguments: foo --package bar\\". --all If specified, the dependency will be removed from all projects that declare it. + --variant VARIANT Run command using a variant installation + configuration. This parameter may alternatively be + specified via the RUSH_VARIANT environment variable. " `; @@ -1135,7 +1359,7 @@ exports[`CommandLineHelp prints the help for each action: update 1`] = ` "usage: rush update [-h] [-p] [--bypass-policy] [--no-link] [--network-concurrency COUNT] [--debug-package-manager] [--max-install-attempts NUMBER] [--ignore-hooks] - [--variant VARIANT] [--full] [--recheck] + [--offline] [--variant VARIANT] [--full] [--recheck] The \\"rush update\\" command installs the dependencies described in your package. @@ -1175,6 +1399,11 @@ Optional arguments: --ignore-hooks Skips execution of the \\"eventHooks\\" scripts defined in rush.json. Make sure you know what you are skipping. + --offline Enables installation to be performed without internet + access. PNPM will instead report an error if the + necessary NPM packages cannot be obtained from the + local cache. For details, see the documentation for + PNPM's \\"--offline\\" parameter. --variant VARIANT Run command using a variant installation configuration. This parameter may alternatively be specified via the RUSH_VARIANT environment variable. @@ -1209,8 +1438,8 @@ folder. Optional arguments: -h, --help Show this help message and exit. --name AUTOINSTALLER_NAME - Specifies the name of the autoinstaller, which must - be one of the folders under common/autoinstallers. + The name of the autoinstaller, which must be one of + the folders under common/autoinstallers. " `; @@ -1234,6 +1463,8 @@ Optional arguments: exports[`CommandLineHelp prints the help for each action: upgrade-interactive 1`] = ` "usage: rush upgrade-interactive [-h] [--make-consistent] [-s] + [--variant VARIANT] + Provide an interactive way to upgrade your dependencies. Running the command will open an interactive prompt that will ask you which projects and which @@ -1251,6 +1482,9 @@ Optional arguments: upgrade dependencies from other projects. -s, --skip-update If specified, the \\"rush update\\" command will not be run after updating the package.json files. + --variant VARIANT Run command using a variant installation configuration. + This parameter may alternatively be specified via the + RUSH_VARIANT environment variable. " `; diff --git a/libraries/rush-lib/src/cli/test/__snapshots__/RushXCommandLine.test.ts.snap b/libraries/rush-lib/src/cli/test/__snapshots__/RushXCommandLine.test.ts.snap index ec919b385e3..1d477eb956a 100644 --- a/libraries/rush-lib/src/cli/test/__snapshots__/RushXCommandLine.test.ts.snap +++ b/libraries/rush-lib/src/cli/test/__snapshots__/RushXCommandLine.test.ts.snap @@ -1,9 +1,9 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`RushXCommandLine launchRushX executes a valid package script 1`] = ` +exports[`RushXCommandLine launchRushXAsync executes a valid package script 1`] = ` Array [ Array [ - "Rush Multi-Project Build Tool 40.40.40 - Node.js 12.12.12 (LTS)", + "Rush Multi-Project Build Tool 40.40.40 - Node.js 12.12.12 (LTS)", ], Array [ "> \\"an acme project build command\\" @@ -12,36 +12,26 @@ Array [ ] `; -exports[`RushXCommandLine launchRushX executes a valid package script with no startup banner 1`] = `Array []`; +exports[`RushXCommandLine launchRushXAsync executes a valid package script with no startup banner 1`] = `Array []`; -exports[`RushXCommandLine launchRushX fails if the package does not contain a matching script 1`] = ` +exports[`RushXCommandLine launchRushXAsync fails if the package does not contain a matching script 1`] = ` Array [ Array [ - "Rush Multi-Project Build Tool 40.40.40 - Node.js 12.12.12 (LTS)", - ], - Array [ - "Error: The command \\"asdf\\" is not defined in the package.json file for this project.", - ], - Array [ - " -Available commands for this project are: \\"build\\", \\"test\\"", - ], - Array [ - "Use \\"rushx --help\\" for more information.", + "Rush Multi-Project Build Tool 40.40.40 - Node.js 12.12.12 (LTS)", ], ] `; -exports[`RushXCommandLine launchRushX prints usage info 1`] = ` +exports[`RushXCommandLine launchRushXAsync prints usage info 1`] = ` Array [ Array [ - "Rush Multi-Project Build Tool 40.40.40 - Node.js 12.12.12 (LTS)", + "Rush Multi-Project Build Tool 40.40.40 - Node.js 12.12.12 (LTS)", ], Array [ "usage: rushx [-h]", ], Array [ - " rushx [-q/--quiet] ... + " rushx [-q/--quiet] [-d/--debug] [--ignore-hooks] ... ", ], Array [ @@ -51,7 +41,10 @@ Array [ " -h, --help Show this help message and exit.", ], Array [ - " -q, --quiet Hide rushx startup information. + " -q, --quiet Hide rushx startup information.", + ], + Array [ + " -d, --debug Run in debug mode. ", ], Array [ diff --git a/libraries/rush-lib/src/cli/test/catalogSyncTestRepo/common/.gitignore b/libraries/rush-lib/src/cli/test/catalogSyncTestRepo/common/.gitignore new file mode 100644 index 00000000000..1eb00e19c6a --- /dev/null +++ b/libraries/rush-lib/src/cli/test/catalogSyncTestRepo/common/.gitignore @@ -0,0 +1 @@ +!temp diff --git a/libraries/rush-lib/src/cli/test/catalogSyncTestRepo/common/config/rush/pnpm-config.json b/libraries/rush-lib/src/cli/test/catalogSyncTestRepo/common/config/rush/pnpm-config.json new file mode 100644 index 00000000000..afa5c0bf4c8 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/catalogSyncTestRepo/common/config/rush/pnpm-config.json @@ -0,0 +1,8 @@ +{ + "globalCatalogs": { + "default": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + } +} diff --git a/libraries/rush-lib/src/cli/test/catalogSyncTestRepo/common/temp/pnpm-workspace.yaml b/libraries/rush-lib/src/cli/test/catalogSyncTestRepo/common/temp/pnpm-workspace.yaml new file mode 100644 index 00000000000..c807b81d31e --- /dev/null +++ b/libraries/rush-lib/src/cli/test/catalogSyncTestRepo/common/temp/pnpm-workspace.yaml @@ -0,0 +1,6 @@ +packages: + - '../../apps/*' +catalogs: + default: + react: ^18.0.0 + react-dom: ^18.0.0 diff --git a/libraries/rush-lib/src/cli/test/catalogSyncTestRepo/rush.json b/libraries/rush-lib/src/cli/test/catalogSyncTestRepo/rush.json new file mode 100644 index 00000000000..90cd8a844c4 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/catalogSyncTestRepo/rush.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush.schema.json", + "rushVersion": "5.166.0", + "pnpmVersion": "10.28.1", + "nodeSupportedVersionRange": ">=18.0.0", + "projects": [] +} diff --git a/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/a/package.json b/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/a/package.json new file mode 100644 index 00000000000..f00575e3099 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/a/package.json @@ -0,0 +1,9 @@ +{ + "name": "a", + "version": "1.0.0", + "description": "Test package a", + "scripts": { + "build": "fake_build_task_but_works_with_mock", + "rebuild": "fake_REbuild_task_but_works_with_mock" + } +} diff --git a/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/b/package.json b/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/b/package.json new file mode 100644 index 00000000000..8f203bb691d --- /dev/null +++ b/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/b/package.json @@ -0,0 +1,9 @@ +{ + "name": "b", + "version": "1.0.0", + "description": "Test package b", + "scripts": { + "build": "fake_build_task_but_works_with_mock", + "rebuild": "fake_REbuild_task_but_works_with_mock" + } +} diff --git a/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/common/autoinstallers/plugins/package.json b/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/common/autoinstallers/plugins/package.json new file mode 100644 index 00000000000..9d477cd6aad --- /dev/null +++ b/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/common/autoinstallers/plugins/package.json @@ -0,0 +1,8 @@ +{ + "name": "plugins", + "version": "1.0.0", + "private": true, + "dependencies": { + "rush-mock-clear-operations-plugin": "file:../../../../rush-mock-clear-operations-plugin" + } +} diff --git a/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/common/autoinstallers/plugins/rush-plugins/rush-mock-clear-operations-plugin/rush-plugin-manifest.json b/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/common/autoinstallers/plugins/rush-plugins/rush-mock-clear-operations-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000000..94c8982167f --- /dev/null +++ b/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/common/autoinstallers/plugins/rush-plugins/rush-mock-clear-operations-plugin/rush-plugin-manifest.json @@ -0,0 +1,9 @@ +{ + "plugins": [ + { + "pluginName": "rush-mock-clear-operations-plugin", + "description": "Rush plugin for testing a phased command that produces no operations", + "entryPoint": "index.js" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/common/config/rush/rush-plugins.json b/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/common/config/rush/rush-plugins.json new file mode 100644 index 00000000000..94f6be8e009 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/common/config/rush/rush-plugins.json @@ -0,0 +1,9 @@ +{ + "plugins": [ + { + "packageName": "rush-mock-clear-operations-plugin", + "pluginName": "rush-mock-clear-operations-plugin", + "autoinstallerName": "plugins" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/rush.json b/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/rush.json new file mode 100644 index 00000000000..8bdab648130 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/clearOperationsAndRunBuildActionRepo/rush.json @@ -0,0 +1,17 @@ +{ + "npmVersion": "6.4.1", + "rushVersion": "5.62.2", + "projectFolderMinDepth": 1, + "projectFolderMaxDepth": 99, + + "projects": [ + { + "packageName": "a", + "projectFolder": "a" + }, + { + "packageName": "b", + "projectFolder": "b" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/mockRushCommandLineParser.ts b/libraries/rush-lib/src/cli/test/mockRushCommandLineParser.ts index b4f11e529f6..9904cfcabf8 100644 --- a/libraries/rush-lib/src/cli/test/mockRushCommandLineParser.ts +++ b/libraries/rush-lib/src/cli/test/mockRushCommandLineParser.ts @@ -2,7 +2,7 @@ // See LICENSE in the project root for license information. // Mock child_process so we can verify tasks are (or are not) invoked as we expect -jest.mock('child_process'); +jest.mock('node:child_process', () => jest.requireActual('./mock_child_process')); function mockReportErrorAndSetExitCode(error: Error): void { // Just rethrow the error so the unit tests can catch it diff --git a/libraries/rush-lib/src/cli/test/mock_child_process.ts b/libraries/rush-lib/src/cli/test/mock_child_process.ts new file mode 100644 index 00000000000..8b1bcf67f47 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/mock_child_process.ts @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/* eslint-disable */ + +const EventEmitter = require('node:events'); + +const childProcess: any = jest.createMockFromModule('node:child_process'); +const childProcessActual = jest.requireActual('node:child_process'); +childProcess.spawn.mockImplementation(spawn); +childProcess.__setSpawnMockConfig = setSpawnMockConfig; + +let spawnMockConfig = normalizeSpawnMockConfig(); + +/** + * Helper to initialize how the `spawn` mock should behave. + */ +function normalizeSpawnMockConfig(maybeConfig?: any) { + const config = maybeConfig || {}; + return { + emitError: typeof config.emitError !== 'undefined' ? config.emitError : false, + returnCode: typeof config.returnCode !== 'undefined' ? config.returnCode : 0 + }; +} + +/** + * Initialize the `spawn` mock behavior. + * + * Not a pure function. + */ +function setSpawnMockConfig(spawnConfig: any) { + spawnMockConfig = normalizeSpawnMockConfig(spawnConfig); +} + +/** + * Mock of `spawn`. + */ +function spawn(file: string, args: string[], options: {}) { + const cpMock = new childProcess.ChildProcess(); + + // Add working event emitters ourselves since `createMockFromModule` does not add them because they + // are dynamically added by `spawn`. + const cpEmitter = new EventEmitter(); + const cp = Object.assign({}, cpMock, { + stdin: new EventEmitter(), + stdout: new EventEmitter(), + stderr: new EventEmitter(), + on: cpEmitter.on, + emit: cpEmitter.emit + }); + + setTimeout(() => { + cp.stdout.emit('data', `${file} ${args}: Mock task is spawned`); + + if (spawnMockConfig.emitError) { + cp.stderr.emit('data', `${file} ${args}: A mock error occurred in the task`); + } + + cp.emit('close', spawnMockConfig.returnCode); + }, 0); + + return cp; +} + +/** + * Ensure the real spawnSync function is used, otherwise LockFile breaks. + */ +childProcess.spawnSync = childProcessActual.spawnSync; + +module.exports = childProcess; diff --git a/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/a/package.json b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/a/package.json new file mode 100644 index 00000000000..f00575e3099 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/a/package.json @@ -0,0 +1,9 @@ +{ + "name": "a", + "version": "1.0.0", + "description": "Test package a", + "scripts": { + "build": "fake_build_task_but_works_with_mock", + "rebuild": "fake_REbuild_task_but_works_with_mock" + } +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/b/package.json b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/b/package.json new file mode 100644 index 00000000000..d3c148830da --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/b/package.json @@ -0,0 +1,12 @@ +{ + "name": "b", + "version": "1.0.0", + "description": "Test package b", + "dependencies": { + "a": "1.0.0" + }, + "scripts": { + "build": "fake_build_task_but_works_with_mock", + "rebuild": "fake_REbuild_task_but_works_with_mock" + } +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/common/autoinstallers/plugins/package.json b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/common/autoinstallers/plugins/package.json new file mode 100644 index 00000000000..ed811641144 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/common/autoinstallers/plugins/package.json @@ -0,0 +1,8 @@ +{ + "name": "plugins", + "version": "1.0.0", + "private": true, + "dependencies": { + "rush-build-command-plugin": "file: ../../../rush-build-command-plugin" + } +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-build-command-plugin/rush-build-command-plugin/command-line.json b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-build-command-plugin/rush-build-command-plugin/command-line.json new file mode 100644 index 00000000000..ca988f3f6c1 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-build-command-plugin/rush-build-command-plugin/command-line.json @@ -0,0 +1,17 @@ +// If no command-line.json file defines "rush build" and "rush rebuild", then normally Rush's default +// definitions are applied as if they were defined in common/config/rush/command-line.json, +// and therefore the corresponding phases must be in that file as well. But because the line below +// customizes "rush build" in this plugin, then the defaults are applied here, and as a result the +// corresponding phases must also be defined by this plugin. +{ + "$schema": "../../../../../../../../../schemas/command-line.schema.json", + "commands": [ + { + "commandKind": "bulk", + "name": "build", + "summary": "Override build command summary in plugin", + "enableParallelism": true, + "allowWarningsInSuccessfulBuild": true + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-build-command-plugin/rush-plugin-manifest.json b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-build-command-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000000..e8f1e2aa799 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-build-command-plugin/rush-plugin-manifest.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + { + "pluginName": "rush-build-command-plugin", + "description": "Rush plugin for testing command line parameters" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/common/config/rush/rush-plugins.json b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/common/config/rush/rush-plugins.json new file mode 100644 index 00000000000..de1071044d6 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/common/config/rush/rush-plugins.json @@ -0,0 +1,9 @@ +{ + "plugins": [ + { + "packageName": "rush-build-command-plugin", + "pluginName": "rush-build-command-plugin", + "autoinstallerName": "plugins" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/rush-build-command-plugin/index.js b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/rush-build-command-plugin/index.js new file mode 100644 index 00000000000..e21aeba022f --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/rush-build-command-plugin/index.js @@ -0,0 +1 @@ +console.log('Rush Build Command Line Repo Test', process.argv); diff --git a/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/rush-build-command-plugin/package.json b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/rush-build-command-plugin/package.json new file mode 100644 index 00000000000..5d11cb9ba50 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/rush-build-command-plugin/package.json @@ -0,0 +1,6 @@ +{ + "name": "rush-build-command-plugin", + "version": "1.0.0", + "private": true, + "dependencies": {} +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/rush-build-command-plugin/rush-plugin-manifest.json b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/rush-build-command-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000000..2ffd155ccd5 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/rush-build-command-plugin/rush-plugin-manifest.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + { + "pluginName": "rush-build-command-plugin", + "description": "Rush plugin for testing build command in plugin command-line.json" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/rush.json b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/rush.json new file mode 100644 index 00000000000..72e572e7e38 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithBuildCommandRepo/rush.json @@ -0,0 +1,16 @@ +{ + "npmVersion": "6.4.1", + "rushVersion": "5.62.2", + "projectFolderMinDepth": 1, + "projectFolderMaxDepth": 99, + "projects": [ + { + "packageName": "a", + "projectFolder": "a" + }, + { + "packageName": "b", + "projectFolder": "b" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/a/package.json b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/a/package.json new file mode 100644 index 00000000000..f00575e3099 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/a/package.json @@ -0,0 +1,9 @@ +{ + "name": "a", + "version": "1.0.0", + "description": "Test package a", + "scripts": { + "build": "fake_build_task_but_works_with_mock", + "rebuild": "fake_REbuild_task_but_works_with_mock" + } +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/b/package.json b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/b/package.json new file mode 100644 index 00000000000..d3c148830da --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/b/package.json @@ -0,0 +1,12 @@ +{ + "name": "b", + "version": "1.0.0", + "description": "Test package b", + "dependencies": { + "a": "1.0.0" + }, + "scripts": { + "build": "fake_build_task_but_works_with_mock", + "rebuild": "fake_REbuild_task_but_works_with_mock" + } +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/autoinstallers/plugins/package.json b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/autoinstallers/plugins/package.json new file mode 100644 index 00000000000..ed811641144 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/autoinstallers/plugins/package.json @@ -0,0 +1,8 @@ +{ + "name": "plugins", + "version": "1.0.0", + "private": true, + "dependencies": { + "rush-build-command-plugin": "file: ../../../rush-build-command-plugin" + } +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-build-command-plugin/rush-build-command-plugin/command-line.json b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-build-command-plugin/rush-build-command-plugin/command-line.json new file mode 100644 index 00000000000..ca988f3f6c1 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-build-command-plugin/rush-build-command-plugin/command-line.json @@ -0,0 +1,17 @@ +// If no command-line.json file defines "rush build" and "rush rebuild", then normally Rush's default +// definitions are applied as if they were defined in common/config/rush/command-line.json, +// and therefore the corresponding phases must be in that file as well. But because the line below +// customizes "rush build" in this plugin, then the defaults are applied here, and as a result the +// corresponding phases must also be defined by this plugin. +{ + "$schema": "../../../../../../../../../schemas/command-line.schema.json", + "commands": [ + { + "commandKind": "bulk", + "name": "build", + "summary": "Override build command summary in plugin", + "enableParallelism": true, + "allowWarningsInSuccessfulBuild": true + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-build-command-plugin/rush-plugin-manifest.json b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-build-command-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000000..e8f1e2aa799 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-build-command-plugin/rush-plugin-manifest.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + { + "pluginName": "rush-build-command-plugin", + "description": "Rush plugin for testing command line parameters" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/config/rush/command-line.json b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/config/rush/command-line.json new file mode 100644 index 00000000000..3f754daa271 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/config/rush/command-line.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json", + "commands": [ + { + "commandKind": "bulk", + "name": "build", + "summary": "Build all projects that haven't been built, or have changed since they were last built", + "enableParallelism": true, + "allowWarningsInSuccessfulBuild": true + } + ], + "parameters": [ + { + "longName": "--no-color", + "parameterKind": "flag", + "description": "disable colors in the build log, defaults to 'true'", + "associatedCommands": ["build", "rebuild"] + }, + { + "longName": "--production", + "parameterKind": "flag", + "description": "Perform a production build, including minification and localization steps", + "associatedCommands": ["build", "rebuild"] + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/config/rush/rush-plugins.json b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/config/rush/rush-plugins.json new file mode 100644 index 00000000000..de1071044d6 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/common/config/rush/rush-plugins.json @@ -0,0 +1,9 @@ +{ + "plugins": [ + { + "packageName": "rush-build-command-plugin", + "pluginName": "rush-build-command-plugin", + "autoinstallerName": "plugins" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/rush-build-command-plugin/index.js b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/rush-build-command-plugin/index.js new file mode 100644 index 00000000000..e21aeba022f --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/rush-build-command-plugin/index.js @@ -0,0 +1 @@ +console.log('Rush Build Command Line Repo Test', process.argv); diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/rush-build-command-plugin/package.json b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/rush-build-command-plugin/package.json new file mode 100644 index 00000000000..5d11cb9ba50 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/rush-build-command-plugin/package.json @@ -0,0 +1,6 @@ +{ + "name": "rush-build-command-plugin", + "version": "1.0.0", + "private": true, + "dependencies": {} +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/rush-build-command-plugin/rush-plugin-manifest.json b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/rush-build-command-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000000..2ffd155ccd5 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/rush-build-command-plugin/rush-plugin-manifest.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + { + "pluginName": "rush-build-command-plugin", + "description": "Rush plugin for testing build command in plugin command-line.json" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/rush.json b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/rush.json new file mode 100644 index 00000000000..72e572e7e38 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictBuildCommandRepo/rush.json @@ -0,0 +1,16 @@ +{ + "npmVersion": "6.4.1", + "rushVersion": "5.62.2", + "projectFolderMinDepth": 1, + "projectFolderMaxDepth": 99, + "projects": [ + { + "packageName": "a", + "projectFolder": "a" + }, + { + "packageName": "b", + "projectFolder": "b" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/a/package.json b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/a/package.json new file mode 100644 index 00000000000..f00575e3099 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/a/package.json @@ -0,0 +1,9 @@ +{ + "name": "a", + "version": "1.0.0", + "description": "Test package a", + "scripts": { + "build": "fake_build_task_but_works_with_mock", + "rebuild": "fake_REbuild_task_but_works_with_mock" + } +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/b/package.json b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/b/package.json new file mode 100644 index 00000000000..d3c148830da --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/b/package.json @@ -0,0 +1,12 @@ +{ + "name": "b", + "version": "1.0.0", + "description": "Test package b", + "dependencies": { + "a": "1.0.0" + }, + "scripts": { + "build": "fake_build_task_but_works_with_mock", + "rebuild": "fake_REbuild_task_but_works_with_mock" + } +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/autoinstallers/plugins/package.json b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/autoinstallers/plugins/package.json new file mode 100644 index 00000000000..27185747630 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/autoinstallers/plugins/package.json @@ -0,0 +1,8 @@ +{ + "name": "plugins", + "version": "1.0.0", + "private": true, + "dependencies": { + "rush-build-command-plugin": "file: ../../../rush-rebuild-command-plugin" + } +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-rebuild-command-plugin/rush-plugin-manifest.json b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-rebuild-command-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000000..4e06a8d8ee6 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-rebuild-command-plugin/rush-plugin-manifest.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + { + "pluginName": "rush-rebuild-command-plugin", + "description": "Rush plugin for testing command line parameters" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-rebuild-command-plugin/rush-rebuild-command-plugin/command-line.json b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-rebuild-command-plugin/rush-rebuild-command-plugin/command-line.json new file mode 100644 index 00000000000..074830bdb95 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-rebuild-command-plugin/rush-rebuild-command-plugin/command-line.json @@ -0,0 +1,17 @@ +// If no command-line.json file defines "rush build" and "rush rebuild", then normally Rush's default +// definitions are applied as if they were defined in common/config/rush/command-line.json, +// and therefore the corresponding phases must be in that file as well. But because the line below +// customizes "rush rebuild" in this plugin, then the defaults are applied here, and as a result the +// corresponding phases must also be defined by this plugin. +{ + "$schema": "../../../../../../../../../schemas/command-line.schema.json", + "commands": [ + { + "commandKind": "bulk", + "name": "rebuild", + "summary": "Override rebuild command summary in plugin", + "enableParallelism": true, + "allowWarningsInSuccessfulBuild": true + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/config/rush/command-line.json b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/config/rush/command-line.json new file mode 100644 index 00000000000..d29ec490dac --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/config/rush/command-line.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json", + "commands": [ + { + "commandKind": "bulk", + "name": "rebuild", + "summary": "ReBuild all projects that haven't been built, or have changed since they were last built", + "enableParallelism": true, + "allowWarningsInSuccessfulBuild": true + } + ], + "parameters": [ + { + "longName": "--no-color", + "parameterKind": "flag", + "description": "disable colors in the build log, defaults to 'true'", + "associatedCommands": ["build", "rebuild"] + }, + { + "longName": "--production", + "parameterKind": "flag", + "description": "Perform a production build, including minification and localization steps", + "associatedCommands": ["build", "rebuild"] + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/config/rush/rush-plugins.json b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/config/rush/rush-plugins.json new file mode 100644 index 00000000000..03b1e169e1a --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/common/config/rush/rush-plugins.json @@ -0,0 +1,9 @@ +{ + "plugins": [ + { + "packageName": "rush-rebuild-command-plugin", + "pluginName": "rush-rebuild-command-plugin", + "autoinstallerName": "plugins" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/rush-rebuild-command-plugin/index.js b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/rush-rebuild-command-plugin/index.js new file mode 100644 index 00000000000..9f4fd7259cd --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/rush-rebuild-command-plugin/index.js @@ -0,0 +1 @@ +console.log('Rush ReBuild Command Line Repo Test', process.argv); diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/rush-rebuild-command-plugin/package.json b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/rush-rebuild-command-plugin/package.json new file mode 100644 index 00000000000..a5249dba3fd --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/rush-rebuild-command-plugin/package.json @@ -0,0 +1,6 @@ +{ + "name": "rush-rebuild-command-plugin", + "version": "1.0.0", + "private": true, + "dependencies": {} +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/rush-rebuild-command-plugin/rush-plugin-manifest.json b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/rush-rebuild-command-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000000..b913b2c4cea --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/rush-rebuild-command-plugin/rush-plugin-manifest.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + { + "pluginName": "rush-rebuild-command-plugin", + "description": "Rush plugin for testing rebuild command in plugin command-line.json" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/rush.json b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/rush.json new file mode 100644 index 00000000000..72e572e7e38 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithConflictRebuildCommandRepo/rush.json @@ -0,0 +1,16 @@ +{ + "npmVersion": "6.4.1", + "rushVersion": "5.62.2", + "projectFolderMinDepth": 1, + "projectFolderMaxDepth": 99, + "projects": [ + { + "packageName": "a", + "projectFolder": "a" + }, + { + "packageName": "b", + "projectFolder": "b" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/a/package.json b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/a/package.json new file mode 100644 index 00000000000..f00575e3099 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/a/package.json @@ -0,0 +1,9 @@ +{ + "name": "a", + "version": "1.0.0", + "description": "Test package a", + "scripts": { + "build": "fake_build_task_but_works_with_mock", + "rebuild": "fake_REbuild_task_but_works_with_mock" + } +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/b/package.json b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/b/package.json new file mode 100644 index 00000000000..d3c148830da --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/b/package.json @@ -0,0 +1,12 @@ +{ + "name": "b", + "version": "1.0.0", + "description": "Test package b", + "dependencies": { + "a": "1.0.0" + }, + "scripts": { + "build": "fake_build_task_but_works_with_mock", + "rebuild": "fake_REbuild_task_but_works_with_mock" + } +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/common/autoinstallers/plugins/package.json b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/common/autoinstallers/plugins/package.json new file mode 100644 index 00000000000..27185747630 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/common/autoinstallers/plugins/package.json @@ -0,0 +1,8 @@ +{ + "name": "plugins", + "version": "1.0.0", + "private": true, + "dependencies": { + "rush-build-command-plugin": "file: ../../../rush-rebuild-command-plugin" + } +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-rebuild-command-plugin/rush-plugin-manifest.json b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-rebuild-command-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000000..4e06a8d8ee6 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-rebuild-command-plugin/rush-plugin-manifest.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + { + "pluginName": "rush-rebuild-command-plugin", + "description": "Rush plugin for testing command line parameters" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-rebuild-command-plugin/rush-rebuild-command-plugin/command-line.json b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-rebuild-command-plugin/rush-rebuild-command-plugin/command-line.json new file mode 100644 index 00000000000..074830bdb95 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/common/autoinstallers/plugins/rush-plugins/rush-rebuild-command-plugin/rush-rebuild-command-plugin/command-line.json @@ -0,0 +1,17 @@ +// If no command-line.json file defines "rush build" and "rush rebuild", then normally Rush's default +// definitions are applied as if they were defined in common/config/rush/command-line.json, +// and therefore the corresponding phases must be in that file as well. But because the line below +// customizes "rush rebuild" in this plugin, then the defaults are applied here, and as a result the +// corresponding phases must also be defined by this plugin. +{ + "$schema": "../../../../../../../../../schemas/command-line.schema.json", + "commands": [ + { + "commandKind": "bulk", + "name": "rebuild", + "summary": "Override rebuild command summary in plugin", + "enableParallelism": true, + "allowWarningsInSuccessfulBuild": true + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/common/config/rush/rush-plugins.json b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/common/config/rush/rush-plugins.json new file mode 100644 index 00000000000..03b1e169e1a --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/common/config/rush/rush-plugins.json @@ -0,0 +1,9 @@ +{ + "plugins": [ + { + "packageName": "rush-rebuild-command-plugin", + "pluginName": "rush-rebuild-command-plugin", + "autoinstallerName": "plugins" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/rush-rebuild-command-plugin/index.js b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/rush-rebuild-command-plugin/index.js new file mode 100644 index 00000000000..9f4fd7259cd --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/rush-rebuild-command-plugin/index.js @@ -0,0 +1 @@ +console.log('Rush ReBuild Command Line Repo Test', process.argv); diff --git a/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/rush-rebuild-command-plugin/package.json b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/rush-rebuild-command-plugin/package.json new file mode 100644 index 00000000000..a5249dba3fd --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/rush-rebuild-command-plugin/package.json @@ -0,0 +1,6 @@ +{ + "name": "rush-rebuild-command-plugin", + "version": "1.0.0", + "private": true, + "dependencies": {} +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/rush-rebuild-command-plugin/rush-plugin-manifest.json b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/rush-rebuild-command-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000000..b913b2c4cea --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/rush-rebuild-command-plugin/rush-plugin-manifest.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + { + "pluginName": "rush-rebuild-command-plugin", + "description": "Rush plugin for testing rebuild command in plugin command-line.json" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/rush.json b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/rush.json new file mode 100644 index 00000000000..72e572e7e38 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/pluginWithRebuildCommandRepo/rush.json @@ -0,0 +1,16 @@ +{ + "npmVersion": "6.4.1", + "rushVersion": "5.62.2", + "projectFolderMinDepth": 1, + "projectFolderMaxDepth": 99, + "projects": [ + { + "packageName": "a", + "projectFolder": "a" + }, + { + "packageName": "b", + "projectFolder": "b" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/rush-mock-clear-operations-plugin/index.ts b/libraries/rush-lib/src/cli/test/rush-mock-clear-operations-plugin/index.ts new file mode 100644 index 00000000000..f4f7fd9d557 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/rush-mock-clear-operations-plugin/index.ts @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { RushSession, IPhasedCommand, Operation } from '../../../index'; + +/** + * Mimics the shape of `@rushstack/rush-buildxl-graph-plugin`, which performs its work during + * `createOperationsAsync` and then returns an empty operation set because there is nothing left + * for Rush to execute. + * + * Such an invocation must be reported as a success, not a failure. + */ +export default class RushMockClearOperationsPlugin { + public apply(rushSession: RushSession): void { + rushSession.hooks.runAnyPhasedCommand.tapPromise( + RushMockClearOperationsPlugin.name, + async (command: IPhasedCommand) => { + command.hooks.createOperationsAsync.tapPromise( + { + name: RushMockClearOperationsPlugin.name, + // Run after every other plugin has finished creating operations. + stage: Number.MAX_SAFE_INTEGER + }, + async () => { + return new Set(); + } + ); + } + ); + } +} diff --git a/libraries/rush-lib/src/cli/test/rush-mock-clear-operations-plugin/package.json b/libraries/rush-lib/src/cli/test/rush-mock-clear-operations-plugin/package.json new file mode 100644 index 00000000000..a28fd24a685 --- /dev/null +++ b/libraries/rush-lib/src/cli/test/rush-mock-clear-operations-plugin/package.json @@ -0,0 +1,6 @@ +{ + "name": "rush-mock-clear-operations-plugin", + "version": "1.0.0", + "private": true, + "dependencies": {} +} diff --git a/libraries/rush-lib/src/cli/test/rush-mock-clear-operations-plugin/rush-plugin-manifest.json b/libraries/rush-lib/src/cli/test/rush-mock-clear-operations-plugin/rush-plugin-manifest.json new file mode 100644 index 00000000000..94c8982167f --- /dev/null +++ b/libraries/rush-lib/src/cli/test/rush-mock-clear-operations-plugin/rush-plugin-manifest.json @@ -0,0 +1,9 @@ +{ + "plugins": [ + { + "pluginName": "rush-mock-clear-operations-plugin", + "description": "Rush plugin for testing a phased command that produces no operations", + "entryPoint": "index.js" + } + ] +} diff --git a/libraries/rush-lib/src/cli/test/rush-mock-flush-telemetry-plugin/index.ts b/libraries/rush-lib/src/cli/test/rush-mock-flush-telemetry-plugin/index.ts index 996b3e04b3f..d3e9d15ae67 100644 --- a/libraries/rush-lib/src/cli/test/rush-mock-flush-telemetry-plugin/index.ts +++ b/libraries/rush-lib/src/cli/test/rush-mock-flush-telemetry-plugin/index.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +// This is a false-positive +// eslint-disable-next-line import/no-extraneous-dependencies import { JsonFile } from '@rushstack/node-core-library'; import type { RushSession, RushConfiguration, ITelemetryData } from '../../../index'; diff --git a/libraries/rush-lib/src/index.ts b/libraries/rush-lib/src/index.ts index 860553f9c41..5ab636b906d 100644 --- a/libraries/rush-lib/src/index.ts +++ b/libraries/rush-lib/src/index.ts @@ -1,43 +1,66 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +/// + /** * A library for writing scripts that interact with the {@link https://rushjs.io/ | Rush} tool. * @packageDocumentation */ +// #region Backwards compatibility +export { LookupByPath as LookupByPath, type IPrefixMatch } from '@rushstack/lookup-by-path'; + +export { + type ICredentialCacheOptions, + type ICredentialCacheEntry, + CredentialCache +} from '@rushstack/credential-cache'; +// #endregion + export { ApprovedPackagesPolicy } from './api/ApprovedPackagesPolicy'; -export { RushConfiguration, ITryFindRushJsonLocationOptions } from './api/RushConfiguration'; +export { RushConfiguration, type ITryFindRushJsonLocationOptions } from './api/RushConfiguration'; + +export { Subspace } from './api/Subspace'; +export { SubspacesConfiguration } from './api/SubspacesConfiguration'; export { - IPackageManagerOptionsJsonBase, - IConfigurationEnvironment, - IConfigurationEnvironmentVariable, + type IPackageManagerOptionsJsonBase, + type IConfigurationEnvironment, + type IConfigurationEnvironmentVariable, PackageManagerOptionsConfigurationBase } from './logic/base/BasePackageManagerOptionsConfiguration'; export { - INpmOptionsJson as _INpmOptionsJson, + type INpmOptionsJson as _INpmOptionsJson, NpmOptionsConfiguration } from './logic/npm/NpmOptionsConfiguration'; export { - IYarnOptionsJson as _IYarnOptionsJson, + type IYarnOptionsJson as _IYarnOptionsJson, YarnOptionsConfiguration } from './logic/yarn/YarnOptionsConfiguration'; export { - IPnpmOptionsJson as _IPnpmOptionsJson, - PnpmStoreOptions, - PnpmOptionsConfiguration + type IPnpmOptionsJson as _IPnpmOptionsJson, + type PnpmStoreLocation, + type IPnpmLockfilePolicies, + type IPnpmPackageExtension, + type IPnpmPeerDependencyRules, + type IPnpmPeerDependenciesMeta, + type PnpmStoreOptions, + PnpmOptionsConfiguration, + type PnpmResolutionMode, + type PnpmTrustPolicy } from './logic/pnpm/PnpmOptionsConfiguration'; export { BuildCacheConfiguration } from './api/BuildCacheConfiguration'; -export { GetCacheEntryIdFunction, IGenerateCacheEntryIdOptions } from './logic/buildCache/CacheEntryId'; +export { CobuildConfiguration, type ICobuildJson } from './api/CobuildConfiguration'; +export type { GetCacheEntryIdFunction, IGenerateCacheEntryIdOptions } from './logic/buildCache/CacheEntryId'; export { FileSystemBuildCacheProvider, - IFileSystemBuildCacheProviderOptions + type IFileSystemBuildCacheProviderOptions } from './logic/buildCache/FileSystemBuildCacheProvider'; -export { +export type { IPhase, PhaseBehaviorForMissingScript as IPhaseBehaviorForMissingScript } from './api/CommandLineConfiguration'; @@ -45,15 +68,23 @@ export { export { EnvironmentConfiguration, EnvironmentVariableNames, - IEnvironmentConfigurationInitializeOptions + type IEnvironmentConfigurationInitializeOptions } from './api/EnvironmentConfiguration'; export { RushConstants } from './logic/RushConstants'; -export { PackageManagerName, PackageManager } from './api/packageManager/PackageManager'; +export { type PackageManagerName, PackageManager } from './api/packageManager/PackageManager'; export { RushConfigurationProject } from './api/RushConfigurationProject'; +export { + type IRushProjectJson as _IRushProjectJson, + type IOperationSettings, + type NodeVersionGranularity, + RushProjectConfiguration, + type IRushPhaseSharding +} from './api/RushProjectConfiguration'; + export { RushUserConfiguration } from './api/RushUserConfiguration'; export { RushGlobalFolder as _RushGlobalFolder } from './api/RushGlobalFolder'; @@ -62,19 +93,20 @@ export { ApprovedPackagesItem, ApprovedPackagesConfiguration } from './api/Appro export { CommonVersionsConfiguration } from './api/CommonVersionsConfiguration'; -export { PackageJsonEditor, PackageJsonDependency, DependencyType } from './api/PackageJsonEditor'; +export { + PackageJsonEditor, + PackageJsonDependency, + DependencyType, + PackageJsonDependencyMeta +} from './api/PackageJsonEditor'; export { RepoStateFile } from './logic/RepoStateFile'; -export { LookupByPath, IPrefixMatch } from './logic/LookupByPath'; export { EventHooks, Event } from './api/EventHooks'; export { ChangeManager } from './api/ChangeManager'; -export { - LastInstallFlag as _LastInstallFlag, - ILockfileValidityCheckOptions as _ILockfileValidityCheckOptions -} from './api/LastInstallFlag'; +export { FlagFile as _FlagFile } from './api/FlagFile'; export { VersionPolicyDefinitionName, @@ -84,54 +116,109 @@ export { VersionPolicy } from './api/VersionPolicy'; -export { VersionPolicyConfiguration } from './api/VersionPolicyConfiguration'; +export { + VersionPolicyConfiguration, + type ILockStepVersionJson, + type IIndividualVersionJson, + type IVersionPolicyJson +} from './api/VersionPolicyConfiguration'; -export { ILaunchOptions, Rush } from './api/Rush'; +export { type ILaunchOptions, Rush } from './api/Rush'; export { RushInternals as _RushInternals } from './api/RushInternals'; -export { ExperimentsConfiguration, IExperimentsJson } from './api/ExperimentsConfiguration'; - -export { ProjectChangeAnalyzer, IGetChangedProjectsOptions } from './logic/ProjectChangeAnalyzer'; - -export { IOperationRunner, IOperationRunnerContext } from './logic/operations/IOperationRunner'; -export { IExecutionResult, IOperationExecutionResult } from './logic/operations/IOperationExecutionResult'; -export { IOperationOptions, Operation } from './logic/operations/Operation'; +export { ExperimentsConfiguration, type IExperimentsJson } from './api/ExperimentsConfiguration'; +export { + CustomTipsConfiguration, + CustomTipId, + type ICustomTipsJson, + type ICustomTipInfo, + type ICustomTipItemJson, + CustomTipSeverity, + CustomTipType +} from './api/CustomTipsConfiguration'; + +export { ProjectChangeAnalyzer, type IGetChangedProjectsOptions } from './logic/ProjectChangeAnalyzer'; +export type { + IInputsSnapshot, + GetInputsSnapshotAsyncFn as GetInputsSnapshotAsyncFn, + IRushConfigurationProjectForSnapshot +} from './logic/incremental/InputsSnapshot'; + +export type { + IOperationRunner, + IOperationRunnerContext, + IOperationLastState +} from './logic/operations/IOperationRunner'; +export type { + IConfigurableOperation, + IBaseOperationExecutionResult, + IExecutionResult, + IOperationExecutionResult, + IOperationStateHashComponents +} from './logic/operations/IOperationExecutionResult'; +export { type IOperationOptions, type OperationEnabledState, Operation } from './logic/operations/Operation'; +export { type IParallelismScalar, type Parallelism } from './logic/operations/ParseParallelism'; export { OperationStatus } from './logic/operations/OperationStatus'; +export type { ILogFilePaths } from './logic/operations/ProjectLogWritable'; export { RushSession, - IRushSessionOptions, - CloudBuildCacheProviderFactory + type IRushSessionOptions, + type CloudBuildCacheProviderFactory, + type CobuildLockProviderFactory } from './pluginFramework/RushSession'; export { - IRushCommand, - IGlobalCommand, - IPhasedCommand, + type IRushCommand, + type IGlobalCommand, + type IPhasedCommand, RushLifecycleHooks } from './pluginFramework/RushLifeCycle'; -export { ICreateOperationsContext, PhasedCommandHooks } from './pluginFramework/PhasedCommandHooks'; - -export { IRushPlugin } from './pluginFramework/IRushPlugin'; -export { IBuiltInPluginConfiguration as _IBuiltInPluginConfiguration } from './pluginFramework/PluginLoader/BuiltInPluginLoader'; -export { IRushPluginConfigurationBase as _IRushPluginConfigurationBase } from './api/RushPluginsConfiguration'; -export { ILogger } from './pluginFramework/logging/Logger'; - -export { ICloudBuildCacheProvider } from './logic/buildCache/ICloudBuildCacheProvider'; - -export { ICredentialCacheOptions, ICredentialCacheEntry, CredentialCache } from './logic/CredentialCache'; +export { + type ICreateOperationsContext, + type IOperationGraphContext, + type IPhasedCommandPlugin, + PhasedCommandHooks +} from './pluginFramework/PhasedCommandHooks'; +export type { IOperationGraph, IOperationGraphIterationOptions } from './logic/operations/IOperationGraph'; +export { OperationGraphHooks } from './pluginFramework/OperationGraphHooks'; + +export type { IRushPlugin } from './pluginFramework/IRushPlugin'; +export type { IBuiltInPluginConfiguration as _IBuiltInPluginConfiguration } from './pluginFramework/PluginLoader/BuiltInPluginLoader'; +export type { IRushPluginConfigurationBase as _IRushPluginConfigurationBase } from './api/RushPluginsConfiguration'; +export type { ILogger } from './pluginFramework/logging/Logger'; + +export type { ICloudBuildCacheProvider } from './logic/buildCache/ICloudBuildCacheProvider'; +export type { + ICobuildLockProvider, + ICobuildContext, + ICobuildCompletedState +} from './logic/cobuild/ICobuildLockProvider'; export type { ITelemetryData, ITelemetryMachineInfo, ITelemetryOperationResult } from './logic/Telemetry'; -export { IStopwatchResult } from './utilities/Stopwatch'; +export type { IStopwatchResult } from './utilities/Stopwatch'; export { OperationStateFile as _OperationStateFile, - IOperationStateFileOptions as _IOperationStateFileOptions, - IOperationStateJson as _IOperationStateJson + type IOperationStateFileOptions as _IOperationStateFileOptions, + type IOperationStateJson as _IOperationStateJson } from './logic/operations/OperationStateFile'; export { OperationMetadataManager as _OperationMetadataManager, - IOperationMetadataManagerOptions as _IOperationMetadataManagerOptions, - IOperationMetaData as _IOperationMetadata + type IOperationMetadataManagerOptions as _IOperationMetadataManagerOptions, + type IOperationMetaData as _IOperationMetadata } from './logic/operations/OperationMetadataManager'; + +export { + RushCommandLine, + type IRushCommandLineSpec, + type IRushCommandLineParameter, + type IRushCommandLineAction +} from './api/RushCommandLine'; + +export { OperationBuildCache as _OperationBuildCache } from './logic/buildCache/OperationBuildCache'; +export type { + IOperationBuildCacheOptions as _IOperationBuildCacheOptions, + IProjectBuildCacheOptions as _IProjectBuildCacheOptions +} from './logic/buildCache/OperationBuildCache'; diff --git a/libraries/rush-lib/src/legacy-compatibility/index.js b/libraries/rush-lib/src/legacy-compatibility/index.js new file mode 100644 index 00000000000..16cb8ded821 --- /dev/null +++ b/libraries/rush-lib/src/legacy-compatibility/index.js @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// This file is specifically included for compatibility with older global installations of Rush. +module.exports = require('../lib-commonjs/index.js'); diff --git a/libraries/rush-lib/src/logic/ApprovedPackagesChecker.ts b/libraries/rush-lib/src/logic/ApprovedPackagesChecker.ts index 99e432a6ba9..c20f3b525d9 100644 --- a/libraries/rush-lib/src/logic/ApprovedPackagesChecker.ts +++ b/libraries/rush-lib/src/logic/ApprovedPackagesChecker.ts @@ -1,11 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ApprovedPackagesPolicy } from '../api/ApprovedPackagesPolicy'; -import { RushConfiguration } from '../api/RushConfiguration'; -import { RushConfigurationProject } from '../api/RushConfigurationProject'; +import type { IPackageJson } from '@rushstack/node-core-library'; + +import type { ApprovedPackagesPolicy } from '../api/ApprovedPackagesPolicy'; +import type { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; import { DependencySpecifier } from './DependencySpecifier'; -import { IPackageJson } from '@rushstack/node-core-library'; export class ApprovedPackagesChecker { private readonly _rushConfiguration: RushConfiguration; @@ -70,7 +71,7 @@ export class ApprovedPackagesChecker { // "dependencies": { // "alias-name": "npm:target-name@^1.2.3" // } - const dependencySpecifier: DependencySpecifier = new DependencySpecifier( + const dependencySpecifier: DependencySpecifier = DependencySpecifier.parseWithCache( packageName, dependencies[packageName] ); diff --git a/libraries/rush-lib/src/logic/Autoinstaller.ts b/libraries/rush-lib/src/logic/Autoinstaller.ts index 879bee9fcf8..a47dd0d89be 100644 --- a/libraries/rush-lib/src/logic/Autoinstaller.ts +++ b/libraries/rush-lib/src/logic/Autoinstaller.ts @@ -1,22 +1,31 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; - -import { FileSystem, IPackageJson, JsonFile, LockFile, NewlineKind } from '@rushstack/node-core-library'; +import * as path from 'node:path'; + +import { + FileSystem, + type IPackageJson, + JsonFile, + LockFile, + NewlineKind, + PackageName, + type IParsedPackageNameOrError +} from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; + +import { AsyncRecycler } from '../utilities/AsyncRecycler'; import { Utilities } from '../utilities/Utilities'; - -import { PackageName, IParsedPackageNameOrError } from '@rushstack/node-core-library'; -import { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfiguration } from '../api/RushConfiguration'; import { PackageJsonEditor } from '../api/PackageJsonEditor'; import { InstallHelpers } from './installManager/InstallHelpers'; import type { RushGlobalFolder } from '../api/RushGlobalFolder'; import { RushConstants } from './RushConstants'; import { LastInstallFlag } from '../api/LastInstallFlag'; import { RushCommandLineParser } from '../cli/RushCommandLineParser'; +import type { PnpmPackageManager } from '../api/packageManager/PnpmPackageManager'; -interface IAutoinstallerOptions { +export interface IAutoinstallerOptions { autoinstallerName: string; rushConfiguration: RushConfiguration; rushGlobalFolder: RushGlobalFolder; @@ -78,7 +87,7 @@ export class Autoinstaller { ); } - await InstallHelpers.ensureLocalPackageManager( + await InstallHelpers.ensureLocalPackageManagerAsync( this._rushConfiguration, this._rushGlobalFolder, RushConstants.defaultMaxInstallAttempts, @@ -93,7 +102,7 @@ export class Autoinstaller { this._logIfConsoleOutputIsNotRestricted(`Acquiring lock for "${relativePathForLogs}" folder...`); - const lock: LockFile = await LockFile.acquire(autoinstallerFullPath, 'autoinstaller'); + const lock: LockFile = await LockFile.acquireAsync(autoinstallerFullPath, 'autoinstaller'); try { // Example: .../common/autoinstallers/my-task/.rush/temp @@ -117,22 +126,31 @@ export class Autoinstaller { // Example: ../common/autoinstallers/my-task/node_modules const nodeModulesFolder: string = `${autoinstallerFullPath}/${RushConstants.nodeModulesFolderName}`; const flagPath: string = `${nodeModulesFolder}/rush-autoinstaller.flag`; - const isLastInstallFlagDirty: boolean = !lastInstallFlag.isValid() || !FileSystem.exists(flagPath); + const isLastInstallFlagDirty: boolean = + !(await lastInstallFlag.isValidAsync()) || !FileSystem.exists(flagPath); if (isLastInstallFlagDirty || lock.dirtyWhenAcquired) { if (FileSystem.exists(nodeModulesFolder)) { this._logIfConsoleOutputIsNotRestricted('Deleting old files from ' + nodeModulesFolder); - FileSystem.ensureEmptyFolder(nodeModulesFolder); + const recycler: AsyncRecycler = new AsyncRecycler( + `${this._rushConfiguration.commonTempFolder}/${RushConstants.rushRecyclerFolderName}` + ); + recycler.moveFolder(nodeModulesFolder); + await recycler.startDeleteAllAsync(); } // Copy: .../common/autoinstallers/my-task/.npmrc - Utilities.syncNpmrc(this._rushConfiguration.commonRushConfigFolder, autoinstallerFullPath); + Utilities.syncNpmrc({ + sourceNpmrcFolder: this._rushConfiguration.commonRushConfigFolder, + targetNpmrcFolder: autoinstallerFullPath, + supportEnvVarFallbackSyntax: this._rushConfiguration.isPnpm + }); this._logIfConsoleOutputIsNotRestricted( `Installing dependencies under ${autoinstallerFullPath}...\n` ); - Utilities.executeCommand({ + await Utilities.executeCommandAsync({ command: this._rushConfiguration.packageManagerToolFilename, args: ['install', '--frozen-lockfile'], workingDirectory: autoinstallerFullPath, @@ -140,7 +158,7 @@ export class Autoinstaller { }); // Create file: ../common/autoinstallers/my-task/.rush/temp/last-install.flag - lastInstallFlag.create(); + await lastInstallFlag.createAsync(); FileSystem.writeFile( flagPath, @@ -158,7 +176,7 @@ export class Autoinstaller { } public async updateAsync(): Promise { - await InstallHelpers.ensureLocalPackageManager( + await InstallHelpers.ensureLocalPackageManagerAsync( this._rushConfiguration, this._rushGlobalFolder, RushConstants.defaultMaxInstallAttempts, @@ -167,7 +185,7 @@ export class Autoinstaller { const autoinstallerPackageJsonPath: string = path.join(this.folderFullPath, 'package.json'); - if (!FileSystem.exists(autoinstallerPackageJsonPath)) { + if (!(await FileSystem.existsAsync(autoinstallerPackageJsonPath))) { throw new Error(`The specified autoinstaller path does not exist: ` + autoinstallerPackageJsonPath); } @@ -177,15 +195,28 @@ export class Autoinstaller { let oldFileContents: string = ''; - if (FileSystem.exists(this.shrinkwrapFilePath)) { + if (await FileSystem.existsAsync(this.shrinkwrapFilePath)) { oldFileContents = FileSystem.readFile(this.shrinkwrapFilePath, { convertLineEndings: NewlineKind.Lf }); this._logIfConsoleOutputIsNotRestricted('Deleting ' + this.shrinkwrapFilePath); - FileSystem.deleteFile(this.shrinkwrapFilePath); + await FileSystem.deleteFileAsync(this.shrinkwrapFilePath); + if (this._rushConfiguration.isPnpm) { + // Workaround for https://github.com/pnpm/pnpm/issues/1890 + // + // When "rush update-autoinstaller" is run, Rush deletes "common/autoinstallers/my-task/pnpm-lock.yaml" + // so that a new lockfile will be generated. However "pnpm install" by design will try to recover + // "pnpm-lock.yaml" from "my-task/node_modules/.pnpm/lock.yaml", which may prevent a full upgrade. + // Deleting both files ensures that a new lockfile will always be generated. + const pnpmPackageManager: PnpmPackageManager = this._rushConfiguration + .packageManagerWrapper as PnpmPackageManager; + await FileSystem.deleteFileAsync( + path.join(this.folderFullPath, pnpmPackageManager.internalShrinkwrapRelativePath) + ); + } } // Detect a common mistake where PNPM prints "Already up-to-date" without creating a shrinkwrap file - const packageJsonEditor: PackageJsonEditor = PackageJsonEditor.load(this.packageJsonPath); - if (packageJsonEditor.dependencyList.length === 0 && packageJsonEditor.dependencyList.length === 0) { + const packageJsonEditor: PackageJsonEditor = await PackageJsonEditor.loadAsync(this.packageJsonPath); + if (packageJsonEditor.dependencyList.length === 0) { throw new Error( 'You must add at least one dependency to the autoinstaller package' + ' before invoking this command:\n' + @@ -195,9 +226,13 @@ export class Autoinstaller { this._logIfConsoleOutputIsNotRestricted(); - Utilities.syncNpmrc(this._rushConfiguration.commonRushConfigFolder, this.folderFullPath); + Utilities.syncNpmrc({ + sourceNpmrcFolder: this._rushConfiguration.commonRushConfigFolder, + targetNpmrcFolder: this.folderFullPath, + supportEnvVarFallbackSyntax: this._rushConfiguration.isPnpm + }); - Utilities.executeCommand({ + await Utilities.executeCommandAsync({ command: this._rushConfiguration.packageManagerToolFilename, args: ['install'], workingDirectory: this.folderFullPath, @@ -207,8 +242,8 @@ export class Autoinstaller { this._logIfConsoleOutputIsNotRestricted(); if (this._rushConfiguration.packageManager === 'npm') { - this._logIfConsoleOutputIsNotRestricted(colors.bold('Running "npm shrinkwrap"...')); - Utilities.executeCommand({ + this._logIfConsoleOutputIsNotRestricted(Colorize.bold('Running "npm shrinkwrap"...')); + await Utilities.executeCommandAsync({ command: this._rushConfiguration.packageManagerToolFilename, args: ['shrinkwrap'], workingDirectory: this.folderFullPath, @@ -218,28 +253,29 @@ export class Autoinstaller { this._logIfConsoleOutputIsNotRestricted(); } - if (!FileSystem.exists(this.shrinkwrapFilePath)) { + if (!(await FileSystem.existsAsync(this.shrinkwrapFilePath))) { throw new Error( 'The package manager did not create the expected shrinkwrap file: ' + this.shrinkwrapFilePath ); } - const newFileContents: string = FileSystem.readFile(this.shrinkwrapFilePath, { + const newFileContents: string = await FileSystem.readFileAsync(this.shrinkwrapFilePath, { convertLineEndings: NewlineKind.Lf }); if (oldFileContents !== newFileContents) { this._logIfConsoleOutputIsNotRestricted( - colors.green('The shrinkwrap file has been updated.') + ' Please commit the updated file:' + Colorize.green('The shrinkwrap file has been updated.') + ' Please commit the updated file:' ); this._logIfConsoleOutputIsNotRestricted(`\n ${this.shrinkwrapFilePath}`); } else { - this._logIfConsoleOutputIsNotRestricted(colors.green('Already up to date.')); + this._logIfConsoleOutputIsNotRestricted(Colorize.green('Already up to date.')); } } private _logIfConsoleOutputIsNotRestricted(message?: string): void { if (!this._restrictConsoleOutput) { - console.log(message); + // eslint-disable-next-line no-console + console.log(message ?? ''); } } } diff --git a/libraries/rush-lib/src/logic/ChangeFiles.ts b/libraries/rush-lib/src/logic/ChangeFiles.ts index 0ce554997e3..d63e011dbd7 100644 --- a/libraries/rush-lib/src/logic/ChangeFiles.ts +++ b/libraries/rush-lib/src/logic/ChangeFiles.ts @@ -1,13 +1,27 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { Async, FileSystem, JsonFile, JsonSchema, LegacyAdapters } from '@rushstack/node-core-library'; +import { Async, FileSystem, JsonFile, JsonSchema } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; -import { IChangeInfo } from '../api/ChangeManagement'; -import { IChangelog } from '../api/Changelog'; -import { RushConfiguration } from '../api/RushConfiguration'; +import type { IChangeInfo } from '../api/ChangeManagement'; +import type { IChangelog } from '../api/Changelog'; +import type { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; +import { type LockStepVersionPolicy, VersionPolicyDefinitionName } from '../api/VersionPolicy'; import schemaJson from '../schemas/change-file.schema.json'; +export interface IValidateOptions { + terminal: ITerminal; + filesToValidate: Iterable; + changedProjectNames: Iterable; + /** + * Optional set of project names that were removed from rush.json. + * When provided, produces a more specific error message for these projects. + */ + deletedProjectNames?: ReadonlySet; +} + /** * This class represents the collection of change files existing in the repo and provides operations * for those change files. @@ -17,53 +31,117 @@ export class ChangeFiles { * Change file path relative to changes folder. */ private _files: string[] | undefined; - private _changesPath: string; + private readonly _rushConfiguration: RushConfiguration; + private readonly _changesPath: string; - public constructor(changesPath: string) { - this._changesPath = changesPath; + public constructor(rushConfiguration: RushConfiguration) { + this._rushConfiguration = rushConfiguration; + this._changesPath = rushConfiguration.changesFolder; } /** * Validate if the newly added change files match the changed packages. */ - public static validate( - newChangeFilePaths: string[], - changedPackages: string[], - rushConfiguration: RushConfiguration - ): void { + public async validateAsync(options: IValidateOptions): Promise { + const { terminal, filesToValidate, changedProjectNames, deletedProjectNames } = options; const schema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); + const rushConfiguration: RushConfiguration = this._rushConfiguration; + const { + hotfixChangeEnabled, + experimentsConfiguration: { + configuration: { strictChangefileValidation } + } + } = rushConfiguration; - const projectsWithChangeDescriptions: Set = new Set(); - newChangeFilePaths.forEach((filePath) => { - console.log(`Found change file: ${filePath}`); + const projectsWithChangeDescriptions: Set = new Set(); + const changefilesByProjectName: Map = new Map(); + await Async.forEachAsync( + filesToValidate, + async (filePath) => { + terminal.writeLine(`Found change file: ${filePath}`); + + const changeFile: IChangeInfo = JsonFile.loadAndValidate(filePath, schema); - const changeFile: IChangeInfo = JsonFile.loadAndValidate(filePath, schema); + if (hotfixChangeEnabled) { + if (changeFile && changeFile.changes) { + for (const change of changeFile.changes) { + if (change.type !== 'none' && change.type !== 'hotfix') { + throw new Error( + `Change file ${filePath} specifies a type of '${change.type}' ` + + `but only 'hotfix' and 'none' change types may be used in a branch with 'hotfixChangeEnabled'.` + ); + } + } + } + } - if (rushConfiguration.hotfixChangeEnabled) { if (changeFile && changeFile.changes) { - for (const change of changeFile.changes) { - if (change.type !== 'none' && change.type !== 'hotfix') { - throw new Error( - `Change file ${filePath} specifies a type of '${change.type}' ` + - `but only 'hotfix' and 'none' change types may be used in a branch with 'hotfixChangeEnabled'.` - ); + for (const { packageName } of changeFile.changes) { + projectsWithChangeDescriptions.add(packageName); + let files: string[] | undefined = changefilesByProjectName.get(packageName); + if (!files) { + files = []; + changefilesByProjectName.set(packageName, files); } + + files.push(filePath); + } + } else { + throw new Error(`Invalid change file: ${filePath}`); + } + }, + { concurrency: 50 } + ); + + if (strictChangefileValidation) { + const errors: string[] = []; + + for (const packageName of projectsWithChangeDescriptions) { + const affectedFiles: string[] = changefilesByProjectName.get(packageName) ?? []; + const fileList: string = affectedFiles.map((f) => ` - ${f}`).join('\n'); + const project: RushConfigurationProject | undefined = rushConfiguration.getProjectByName(packageName); + + if (!project) { + if (deletedProjectNames?.has(packageName)) { + errors.push( + `The project "${packageName}" was removed from rush.json, but the following change ` + + `files still reference it. Please delete them:\n${fileList}` + ); + } else { + errors.push( + `Change file(s) reference a project "${packageName}" that does not exist in the Rush ` + + `configuration:\n${fileList}` + ); + } + continue; + } + + if (project.versionPolicy?.definitionName === VersionPolicyDefinitionName.lockStepVersion) { + const { mainProject }: LockStepVersionPolicy = project.versionPolicy as LockStepVersionPolicy; + if (mainProject && mainProject !== packageName) { + errors.push( + `Change file(s) reference the project "${packageName}" which belongs to lockstepped ` + + `version policy "${project.versionPolicy.policyName}". Change files should be ` + + `created for the policy's main project "${mainProject}" instead:\n${fileList}` + ); } } } - if (changeFile && changeFile.changes) { - changeFile.changes.forEach((change) => projectsWithChangeDescriptions.add(change.packageName)); - } else { - throw new Error(`Invalid change file: ${filePath}`); + if (errors.length > 0) { + throw new Error(errors.join('\n')); } - }); + } + + const projectsMissingChangeDescriptions: Set = new Set(changedProjectNames); + for (const name of projectsWithChangeDescriptions) { + projectsMissingChangeDescriptions.delete(name); + } - const projectsMissingChangeDescriptions: Set = new Set(changedPackages); - projectsWithChangeDescriptions.forEach((name) => projectsMissingChangeDescriptions.delete(name)); if (projectsMissingChangeDescriptions.size > 0) { - const projectsMissingChangeDescriptionsArray: string[] = []; - projectsMissingChangeDescriptions.forEach((name) => projectsMissingChangeDescriptionsArray.push(name)); + const projectsMissingChangeDescriptionsArray: string[] = Array.from( + projectsMissingChangeDescriptions + ).sort(); throw new Error( [ 'The following projects have been changed and require change descriptions, but change descriptions were not ' + @@ -76,11 +154,11 @@ export class ChangeFiles { } } - public static getChangeComments(newChangeFilePaths: string[]): Map { + public static getChangeComments(terminal: ITerminal, newChangeFilePaths: string[]): Map { const changes: Map = new Map(); newChangeFilePaths.forEach((filePath) => { - console.log(`Found change file: ${filePath}`); + terminal.writeLine(`Found change file: ${filePath}`); const changeRequest: IChangeInfo = JsonFile.load(filePath); if (changeRequest && changeRequest.changes) { changeRequest.changes!.forEach((change) => { @@ -101,11 +179,10 @@ export class ChangeFiles { /** * Get the array of absolute paths of change files. */ - public async getFilesAsync(): Promise { + public async getAllChangeFilesAsync(): Promise { if (!this._files) { - const { default: glob } = await import('glob'); - this._files = - (await LegacyAdapters.convertCallbackToPromise(glob, `${this._changesPath}/**/*.json`)) || []; + const { default: glob } = await import('fast-glob'); + this._files = (await glob('**/*.json', { cwd: this._changesPath, absolute: true })) || []; } return this._files; @@ -121,7 +198,11 @@ export class ChangeFiles { /** * Delete all change files */ - public async deleteAllAsync(shouldDelete: boolean, updatedChangelogs?: IChangelog[]): Promise { + public async deleteAllAsync( + terminal: ITerminal, + shouldDelete: boolean, + updatedChangelogs?: IChangelog[] + ): Promise { if (updatedChangelogs) { // Skip changes files if the package's change log is not updated. const packagesToInclude: Set = new Set(); @@ -129,43 +210,49 @@ export class ChangeFiles { packagesToInclude.add(changelog.name); }); - const files: string[] = await this.getFilesAsync(); + const files: string[] = await this.getAllChangeFilesAsync(); const filesToDelete: string[] = []; await Async.forEachAsync( files, async (filePath) => { const changeRequest: IChangeInfo = await JsonFile.loadAsync(filePath); - let shouldDelete: boolean = true; + let shouldDeleteFile: boolean = true; for (const changeInfo of changeRequest.changes!) { if (!packagesToInclude.has(changeInfo.packageName)) { - shouldDelete = false; + shouldDeleteFile = false; break; } } - if (shouldDelete) { + if (shouldDeleteFile) { filesToDelete.push(filePath); } }, { concurrency: 5 } ); - return await this._deleteFilesAsync(filesToDelete, shouldDelete); + return await this._deleteFilesAsync(terminal, filesToDelete, shouldDelete); } else { // Delete all change files. - const files: string[] = await this.getFilesAsync(); - return await this._deleteFilesAsync(files, shouldDelete); + const files: string[] = await this.getAllChangeFilesAsync(); + return await this._deleteFilesAsync(terminal, files, shouldDelete); } } - private async _deleteFilesAsync(files: string[], shouldDelete: boolean): Promise { + private async _deleteFilesAsync( + terminal: ITerminal, + files: string[], + shouldDelete: boolean + ): Promise { if (files.length) { - console.log(`\n* ${shouldDelete ? 'DELETING:' : 'DRYRUN: Deleting'} ${files.length} change file(s).`); + terminal.writeLine( + `\n* ${shouldDelete ? 'DELETING:' : 'DRYRUN: Deleting'} ${files.length} change file(s).` + ); await Async.forEachAsync( - files, + files.sort(), async (filePath) => { - console.log(` - ${filePath}`); + terminal.writeLine(` - ${filePath}`); if (shouldDelete) { await FileSystem.deleteFileAsync(filePath); } diff --git a/libraries/rush-lib/src/logic/ChangeManager.ts b/libraries/rush-lib/src/logic/ChangeManager.ts index cca99018a6a..7b891d37b7d 100644 --- a/libraries/rush-lib/src/logic/ChangeManager.ts +++ b/libraries/rush-lib/src/logic/ChangeManager.ts @@ -1,14 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IPackageJson } from '@rushstack/node-core-library'; - -import { IChangeInfo } from '../api/ChangeManagement'; -import { IChangelog } from '../api/Changelog'; -import { RushConfiguration } from '../api/RushConfiguration'; -import { RushConfigurationProject } from '../api/RushConfigurationProject'; -import { VersionPolicyConfiguration } from '../api/VersionPolicyConfiguration'; -import { PublishUtilities, IChangeRequests } from './PublishUtilities'; +import type { IPackageJson } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import type { IChangeInfo } from '../api/ChangeManagement'; +import type { IChangelog } from '../api/Changelog'; +import type { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; +import type { VersionPolicyConfiguration } from '../api/VersionPolicyConfiguration'; +import { PublishUtilities, type IChangeRequests } from './PublishUtilities'; import { ChangeFiles } from './ChangeFiles'; import { PrereleaseToken } from './PrereleaseToken'; import { ChangelogGenerator } from './ChangelogGenerator'; @@ -20,7 +21,7 @@ import { ChangelogGenerator } from './ChangelogGenerator'; export class ChangeManager { private _prereleaseToken!: PrereleaseToken; private _orderedChanges!: IChangeInfo[]; - private _allPackages!: Map; + private _allPackages!: ReadonlyMap; private _allChanges!: IChangeRequests; private _changeFiles!: ChangeFiles; private _rushConfiguration: RushConfiguration; @@ -33,12 +34,10 @@ export class ChangeManager { /** * Load changes from change files - * @param changesPath - location of change files * @param prereleaseToken - prerelease token * @param includeCommitDetails - whether commit details need to be included in changes */ public async loadAsync( - changesPath: string, prereleaseToken: PrereleaseToken = new PrereleaseToken(), includeCommitDetails: boolean = false ): Promise { @@ -46,7 +45,7 @@ export class ChangeManager { this._prereleaseToken = prereleaseToken; - this._changeFiles = new ChangeFiles(changesPath); + this._changeFiles = new ChangeFiles(this._rushConfiguration); this._allChanges = await PublishUtilities.findChangeRequestsAsync( this._allPackages, this._rushConfiguration, @@ -69,7 +68,7 @@ export class ChangeManager { return this._orderedChanges; } - public get allPackages(): Map { + public get allPackages(): ReadonlyMap { return this._allPackages; } @@ -117,7 +116,7 @@ export class ChangeManager { return updatedPackages; } - public async updateChangelogAsync(shouldCommit: boolean): Promise { + public async updateChangelogAsync(terminal: ITerminal, shouldCommit: boolean): Promise { // Do not update changelog or delete the change files for prerelease. // Save them for the official release. if (!this._prereleaseToken.hasValue) { @@ -130,7 +129,7 @@ export class ChangeManager { ); // Remove the change request files only if "-a" was provided. - await this._changeFiles.deleteAllAsync(shouldCommit, updatedChangelogs); + await this._changeFiles.deleteAllAsync(terminal, shouldCommit, updatedChangelogs); } } } diff --git a/libraries/rush-lib/src/logic/ChangelogGenerator.ts b/libraries/rush-lib/src/logic/ChangelogGenerator.ts index 611ebba9ab5..6ee830c6c8e 100644 --- a/libraries/rush-lib/src/logic/ChangelogGenerator.ts +++ b/libraries/rush-lib/src/logic/ChangelogGenerator.ts @@ -1,16 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import * as semver from 'semver'; import { FileSystem, JsonFile, JsonSchema } from '@rushstack/node-core-library'; -import { IChangeRequests, PublishUtilities } from './PublishUtilities'; -import { IChangeInfo, ChangeType } from '../api/ChangeManagement'; -import { IChangelog, IChangeLogEntry, IChangeLogComment, IChangeLogEntryComments } from '../api/Changelog'; -import { RushConfigurationProject } from '../api/RushConfigurationProject'; -import { RushConfiguration } from '../api/RushConfiguration'; +import { type IChangeRequests, PublishUtilities } from './PublishUtilities'; +import { type IChangeInfo, ChangeType } from '../api/ChangeManagement'; +import type { + IChangelog, + IChangeLogEntry, + IChangeLogComment, + IChangeLogEntryComments +} from '../api/Changelog'; +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; +import type { RushConfiguration } from '../api/RushConfiguration'; import schemaJson from '../schemas/changelog.schema.json'; const CHANGELOG_JSON: string = 'CHANGELOG.json'; @@ -28,7 +34,7 @@ export class ChangelogGenerator { */ public static updateChangelogs( allChanges: IChangeRequests, - allProjects: Map, + allProjects: ReadonlyMap, rushConfiguration: RushConfiguration, shouldCommit: boolean ): IChangelog[] { @@ -37,7 +43,7 @@ export class ChangelogGenerator { allChanges.packageChanges.forEach((change, packageName) => { const project: RushConfigurationProject | undefined = allProjects.get(packageName); - if (project && ChangelogGenerator._shouldUpdateChangeLog(project, allChanges)) { + if (project && _shouldUpdateChangeLog(project, allChanges)) { const changeLog: IChangelog | undefined = ChangelogGenerator.updateIndividualChangelog( change, project.projectFolder, @@ -59,7 +65,7 @@ export class ChangelogGenerator { * Fully regenerate the markdown files based on the current json files. */ public static regenerateChangelogs( - allProjects: Map, + allProjects: ReadonlyMap, rushConfiguration: RushConfiguration ): void { allProjects.forEach((project) => { @@ -67,20 +73,18 @@ export class ChangelogGenerator { const markdownJSONPath: string = path.resolve(project.projectFolder, CHANGELOG_JSON); if (FileSystem.exists(markdownPath)) { + // eslint-disable-next-line no-console console.log('Found: ' + markdownPath); if (!FileSystem.exists(markdownJSONPath)) { throw new Error('A CHANGELOG.md without json: ' + markdownPath); } - const changelog: IChangelog = ChangelogGenerator._getChangelog( - project.packageName, - project.projectFolder - ); + const changelog: IChangelog = _getChangelog(project.packageName, project.projectFolder); const isLockstepped: boolean = !!project.versionPolicy && project.versionPolicy.isLockstepped; FileSystem.writeFile( path.join(project.projectFolder, CHANGELOG_MD), - ChangelogGenerator._translateToMarkdown(changelog, rushConfiguration, isLockstepped) + _translateToMarkdown(changelog, rushConfiguration, isLockstepped) ); } }); @@ -101,7 +105,7 @@ export class ChangelogGenerator { // Early return if the project is lockstepped and does not host change logs return undefined; } - const changelog: IChangelog = ChangelogGenerator._getChangelog(change.packageName, projectFolder); + const changelog: IChangelog = _getChangelog(change.packageName, projectFolder); if (!changelog.entries.some((entry) => entry.version === change.newVersion)) { const changelogEntry: IChangeLogEntry = { @@ -146,6 +150,7 @@ export class ChangelogGenerator { const changelogFilename: string = path.join(projectFolder, CHANGELOG_JSON); + // eslint-disable-next-line no-console console.log( `${EOL}* ${shouldCommit ? 'APPLYING' : 'DRYRUN'}: ` + `Changelog update for "${change.packageName}@${change.newVersion}".` @@ -157,7 +162,7 @@ export class ChangelogGenerator { FileSystem.writeFile( path.join(projectFolder, CHANGELOG_MD), - ChangelogGenerator._translateToMarkdown(changelog, rushConfiguration, isLockstepped) + _translateToMarkdown(changelog, rushConfiguration, isLockstepped) ); } return changelog; @@ -165,121 +170,116 @@ export class ChangelogGenerator { // change log not updated. return undefined; } +} - /** - * Loads the changelog json from disk, or creates a new one if there isn't one. - */ - private static _getChangelog(packageName: string, projectFolder: string): IChangelog { - const changelogFilename: string = path.join(projectFolder, CHANGELOG_JSON); - let changelog: IChangelog | undefined = undefined; - - // Try to read the existing changelog. - if (FileSystem.exists(changelogFilename)) { - changelog = JsonFile.loadAndValidate(changelogFilename, ChangelogGenerator.jsonSchema); - } +/** + * Loads the changelog json from disk, or creates a new one if there isn't one. + */ +function _getChangelog(packageName: string, projectFolder: string): IChangelog { + const changelogFilename: string = path.join(projectFolder, CHANGELOG_JSON); + let changelog: IChangelog | undefined = undefined; - if (!changelog) { - changelog = { - name: packageName, - entries: [] - }; - } else { - // Force the changelog name to be same as package name. - // In case the package has been renamed but change log name is not updated. - changelog.name = packageName; - } + // Try to read the existing changelog. + if (FileSystem.exists(changelogFilename)) { + changelog = JsonFile.loadAndValidate(changelogFilename, ChangelogGenerator.jsonSchema); + } - return changelog; + if (!changelog) { + changelog = { + name: packageName, + entries: [] + }; + } else { + // Force the changelog name to be same as package name. + // In case the package has been renamed but change log name is not updated. + changelog.name = packageName; } - /** - * Translates the given changelog json object into a markdown string. - */ - private static _translateToMarkdown( - changelog: IChangelog, - rushConfiguration: RushConfiguration, - isLockstepped: boolean = false - ): string { - let markdown: string = [ - `# Change Log - ${changelog.name}`, - '', - `This log was last generated on ${new Date().toUTCString()} and should not be manually modified.`, - '', - '' - ].join(EOL); - - changelog.entries.forEach((entry, index) => { - markdown += `## ${entry.version}${EOL}`; - - if (entry.date) { - markdown += `${entry.date}${EOL}`; - } + return changelog; +} - markdown += EOL; +/** + * Translates the given changelog json object into a markdown string. + */ +function _translateToMarkdown( + changelog: IChangelog, + rushConfiguration: RushConfiguration, + isLockstepped: boolean = false +): string { + let markdown: string = [ + `# Change Log - ${changelog.name}`, + '', + `This log was last generated on ${new Date().toUTCString()} and should not be manually modified.`, + '', + '' + ].join(EOL); + + changelog.entries.forEach((entry, index) => { + markdown += `## ${entry.version}${EOL}`; + + if (entry.date) { + markdown += `${entry.date}${EOL}`; + } - let comments: string = ''; + markdown += EOL; - comments += ChangelogGenerator._getChangeComments('Breaking changes', entry.comments.major); + let comments: string = ''; - comments += ChangelogGenerator._getChangeComments('Minor changes', entry.comments.minor); + comments += _getChangeComments('Breaking changes', entry.comments.major); - comments += ChangelogGenerator._getChangeComments('Patches', entry.comments.patch); + comments += _getChangeComments('Minor changes', entry.comments.minor); - if (isLockstepped) { - // In lockstepped projects, all changes are of type ChangeType.none. - comments += ChangelogGenerator._getChangeComments('Updates', entry.comments.none); - } + comments += _getChangeComments('Patches', entry.comments.patch); - if (rushConfiguration.hotfixChangeEnabled) { - comments += ChangelogGenerator._getChangeComments('Hotfixes', entry.comments.hotfix); - } + if (isLockstepped) { + // In lockstepped projects, all changes are of type ChangeType.none. + comments += _getChangeComments('Updates', entry.comments.none); + } - if (!comments) { - markdown += - (changelog.entries.length === index + 1 ? '_Initial release_' : '_Version update only_') + - EOL + - EOL; - } else { - markdown += comments; - } - }); + if (rushConfiguration.hotfixChangeEnabled) { + comments += _getChangeComments('Hotfixes', entry.comments.hotfix); + } - return markdown; - } + if (!comments) { + markdown += + (changelog.entries.length === index + 1 ? '_Initial release_' : '_Version update only_') + EOL + EOL; + } else { + markdown += comments; + } + }); - /** - * Helper to return the comments string to be appends to the markdown content. - */ - private static _getChangeComments(title: string, commentsArray: IChangeLogComment[] | undefined): string { - let comments: string = ''; + return markdown; +} - if (commentsArray) { - comments = `### ${title}${EOL + EOL}`; - commentsArray.forEach((comment) => { - comments += `- ${comment.comment}${EOL}`; - }); - comments += EOL; - } +/** + * Helper to return the comments string to be appends to the markdown content. + */ +function _getChangeComments(title: string, commentsArray: IChangeLogComment[] | undefined): string { + let comments: string = ''; - return comments; + if (commentsArray) { + comments = `### ${title}${EOL + EOL}`; + commentsArray.forEach((comment) => { + comments += `- ${comment.comment}${EOL}`; + }); + comments += EOL; } - /** - * Changelogs should only be generated for publishable projects. - * Do not update changelog or delete the change files for prerelease. Save them for the official release. - * Unless the package is a hotfix, in which case do delete the change files. - * - * @param project - * @param allChanges - */ - private static _shouldUpdateChangeLog( - project: RushConfigurationProject, - allChanges: IChangeRequests - ): boolean { - return ( - project.shouldPublish && - (!semver.prerelease(project.packageJson.version) || - allChanges.packageChanges.get(project.packageName)?.changeType === ChangeType.hotfix) - ); - } + return comments; +} + +/** + * Changelogs should only be generated for publishable projects. + * Do not update changelog or delete the change files for prerelease. Save them for the official release. + * Unless the package is a hotfix, in which case do delete the change files. + * + * @param project + * @param allChanges + */ +function _shouldUpdateChangeLog(project: RushConfigurationProject, allChanges: IChangeRequests): boolean { + return ( + project.shouldPublish && + (!semver.prerelease(project.packageJson.version) || + allChanges.packageChanges.get(project.packageName)?.changeType === ChangeType.hotfix) + ); } diff --git a/libraries/rush-lib/src/logic/DependencyAnalyzer.ts b/libraries/rush-lib/src/logic/DependencyAnalyzer.ts index 13a96e12aa1..cca5a86385e 100644 --- a/libraries/rush-lib/src/logic/DependencyAnalyzer.ts +++ b/libraries/rush-lib/src/logic/DependencyAnalyzer.ts @@ -2,10 +2,12 @@ // See LICENSE in the project root for license information. import * as semver from 'semver'; -import { CommonVersionsConfiguration } from '../api/CommonVersionsConfiguration'; -import { DependencyType, PackageJsonDependency } from '../api/PackageJsonEditor'; -import { RushConfiguration } from '../api/RushConfiguration'; -import { RushConfigurationProject } from '../api/RushConfigurationProject'; + +import type { CommonVersionsConfiguration } from '../api/CommonVersionsConfiguration'; +import { DependencyType, type PackageJsonDependency } from '../api/PackageJsonEditor'; +import type { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; +import type { Subspace } from '../api/Subspace'; export interface IDependencyAnalysis { /** @@ -15,7 +17,7 @@ export interface IDependencyAnalysis { /** * A map of all direct dependencies that only have a single semantic version specifier, - * unless the variant has the {@link CommonVersionsConfiguration.implicitlyPreferredVersions} option + * unless the {@link CommonVersionsConfiguration.implicitlyPreferredVersions} option * set to `false`. */ implicitlyPreferredVersionByPackageName: Map; @@ -26,62 +28,87 @@ export interface IDependencyAnalysis { allVersionsByPackageName: Map>; } -export class DependencyAnalyzer { - private static _dependencyAnalyzerByRushConfiguration: - | WeakMap - | undefined; +let _dependencyAnalyzerByRushConfiguration: WeakMap | undefined; +export class DependencyAnalyzer { private _rushConfiguration: RushConfiguration; - private _analysisByVariant: Map = new Map(); + private _analysisByVariantBySubspace: Map> | undefined; private constructor(rushConfiguration: RushConfiguration) { this._rushConfiguration = rushConfiguration; } public static forRushConfiguration(rushConfiguration: RushConfiguration): DependencyAnalyzer { - if (!DependencyAnalyzer._dependencyAnalyzerByRushConfiguration) { - DependencyAnalyzer._dependencyAnalyzerByRushConfiguration = new WeakMap(); + if (!_dependencyAnalyzerByRushConfiguration) { + _dependencyAnalyzerByRushConfiguration = new WeakMap(); } let analyzer: DependencyAnalyzer | undefined = - DependencyAnalyzer._dependencyAnalyzerByRushConfiguration.get(rushConfiguration); + _dependencyAnalyzerByRushConfiguration.get(rushConfiguration); if (!analyzer) { analyzer = new DependencyAnalyzer(rushConfiguration); - DependencyAnalyzer._dependencyAnalyzerByRushConfiguration.set(rushConfiguration, analyzer); + _dependencyAnalyzerByRushConfiguration.set(rushConfiguration, analyzer); } return analyzer; } - public getAnalysis(variant?: string): IDependencyAnalysis { + public getAnalysis( + subspace: Subspace | undefined, + variant: string | undefined, + addAction: boolean + ): IDependencyAnalysis { // Use an empty string as the key when no variant provided. Anything else would possibly conflict // with a variant created by the user const variantKey: string = variant || ''; - let analysis: IDependencyAnalysis | undefined = this._analysisByVariant.get(variantKey); - if (!analysis) { - analysis = this._getAnalysisInternal(variant); - this._analysisByVariant.set(variantKey, analysis); + + if (!this._analysisByVariantBySubspace) { + this._analysisByVariantBySubspace = new Map(); } - return analysis; + const subspaceToAnalyze: Subspace = subspace || this._rushConfiguration.defaultSubspace; + let analysisForVariant: WeakMap | undefined = + this._analysisByVariantBySubspace.get(variantKey); + + if (!analysisForVariant) { + analysisForVariant = new WeakMap(); + this._analysisByVariantBySubspace.set(variantKey, analysisForVariant); + } + + let analysisForSubspace: IDependencyAnalysis | undefined = analysisForVariant.get(subspaceToAnalyze); + if (!analysisForSubspace) { + analysisForSubspace = this._getAnalysisInternal(subspaceToAnalyze, variant, addAction); + + analysisForVariant.set(subspaceToAnalyze, analysisForSubspace); + } + + return analysisForSubspace; } /** - * Generates the {@link IDependencyAnalysis} for a variant. + * Generates the {@link IDependencyAnalysis}. * * @remarks * The result of this function is not cached. */ - private _getAnalysisInternal(variant: string | undefined): IDependencyAnalysis { - const commonVersionsConfiguration: CommonVersionsConfiguration = - this._rushConfiguration.getCommonVersions(variant); + private _getAnalysisInternal( + subspace: Subspace, + variant: string | undefined, + addAction: boolean + ): IDependencyAnalysis { + const commonVersionsConfiguration: CommonVersionsConfiguration = subspace.getCommonVersions(variant); const allVersionsByPackageName: Map> = new Map(); const allowedAlternativeVersions: Map< string, ReadonlyArray > = commonVersionsConfiguration.allowedAlternativeVersions; - for (const project of this._rushConfiguration.projects) { + let projectsToProcess: RushConfigurationProject[] = this._rushConfiguration.projects; + if (addAction && this._rushConfiguration.subspacesFeatureEnabled) { + projectsToProcess = subspace.getProjects(); + } + + for (const project of projectsToProcess) { const dependencies: PackageJsonDependency[] = [ ...project.packageJsonEditor.dependencyList, ...project.packageJsonEditor.devDependencyList @@ -93,6 +120,11 @@ export class DependencyAnalyzer { continue; } + if (dependencyVersion.startsWith('workspace:')) { + // If this is a workspace protocol dependency, ignore it. + continue; + } + // Is it a local project? const localProject: RushConfigurationProject | undefined = this._rushConfiguration.getProjectByName(dependencyName); @@ -117,7 +149,7 @@ export class DependencyAnalyzer { } const implicitlyPreferredVersionByPackageName: Map = new Map(); - // Only generate implicitly preferred versions for variants that request it + // Only generate implicitly preferred versions when requested const useImplicitlyPreferredVersions: boolean = commonVersionsConfiguration.implicitlyPreferredVersions ?? true; if (useImplicitlyPreferredVersions) { diff --git a/libraries/rush-lib/src/logic/DependencySpecifier.ts b/libraries/rush-lib/src/logic/DependencySpecifier.ts index 76d9aca854c..f43e4110f9a 100644 --- a/libraries/rush-lib/src/logic/DependencySpecifier.ts +++ b/libraries/rush-lib/src/logic/DependencySpecifier.ts @@ -2,8 +2,75 @@ // See LICENSE in the project root for license information. import npmPackageArg from 'npm-package-arg'; + import { InternalError } from '@rushstack/node-core-library'; +/** + * match workspace protocol in dependencies value declaration in `package.json` + * example: + * `"workspace:*"` + * `"workspace:alias@1.2.3"` + */ +const WORKSPACE_PREFIX_REGEX: RegExp = /^workspace:((?[^._/][^@]*)@)?(?.*)$/; + +/** + * match catalog protocol in dependencies value declaration in `package.json` + * example: + * `"catalog:"` - references the default catalog + * `"catalog:catalogName"` - references a named catalog + */ +const CATALOG_PREFIX_REGEX: RegExp = /^catalog:(?.*)$/; + +/** + * resolve workspace protocol(from `@pnpm/workspace.spec-parser`). + * used by pnpm. see [pkgs-graph](https://github.com/pnpm/pnpm/blob/27c33f0319f86c45c1645d064cd9c28aada80780/workspace/pkgs-graph/src/index.ts#L49) + */ +class WorkspaceSpec { + public readonly alias?: string; + public readonly version: string; + public readonly versionSpecifier: string; + + public constructor(version: string, alias?: string) { + this.version = version; + this.alias = alias; + this.versionSpecifier = alias ? `${alias}@${version}` : version; + } + + public static tryParse(pref: string): WorkspaceSpec | undefined { + const parts: RegExpExecArray | null = WORKSPACE_PREFIX_REGEX.exec(pref); + if (parts?.groups) { + return new WorkspaceSpec(parts.groups.version, parts.groups.alias); + } + } + + public toString(): `workspace:${string}` { + return `workspace:${this.versionSpecifier}`; + } +} + +/** + * resolve catalog protocol. + * Used by pnpm for centralized version management via catalogs. + */ +class CatalogSpec { + public readonly catalogName: string; + + public constructor(catalogName: string) { + this.catalogName = catalogName; + } + + public static tryParse(pref: string): CatalogSpec | undefined { + const parts: RegExpExecArray | null = CATALOG_PREFIX_REGEX.exec(pref); + if (parts?.groups !== undefined) { + return new CatalogSpec(parts.groups.catalogName); + } + } + + public toString(): `catalog:${string}` { + return `catalog:${this.catalogName}`; + } +} + /** * The parsed format of a provided version specifier. */ @@ -51,9 +118,16 @@ export enum DependencySpecifierType { /** * A package specified using workspace protocol, e.g. "workspace:^1.2.3" */ - Workspace = 'Workspace' + Workspace = 'Workspace', + + /** + * A package specified using catalog protocol, e.g. "catalog:" or "catalog:react18" + */ + Catalog = 'Catalog' } +const dependencySpecifierParseCache: Map = new Map(); + /** * An NPM "version specifier" is a string that can appear as a package.json "dependencies" value. * Example version specifiers: `^1.2.3`, `file:./blah.tgz`, `npm:other-package@~1.2.3`, and so forth. @@ -87,12 +161,32 @@ export class DependencySpecifier { this.packageName = packageName; this.versionSpecifier = versionSpecifier; + // Catalog protocol is a feature from PNPM for centralized version management + const catalogSpecResult: CatalogSpec | undefined = CatalogSpec.tryParse(versionSpecifier); + if (catalogSpecResult) { + this.specifierType = DependencySpecifierType.Catalog; + this.versionSpecifier = catalogSpecResult.catalogName; + this.aliasTarget = undefined; + return; + } + // Workspace ranges are a feature from PNPM and Yarn. Set the version specifier // to the trimmed version range. - if (versionSpecifier.startsWith('workspace:')) { + const workspaceSpecResult: WorkspaceSpec | undefined = WorkspaceSpec.tryParse(versionSpecifier); + if (workspaceSpecResult) { this.specifierType = DependencySpecifierType.Workspace; - this.versionSpecifier = versionSpecifier.slice(this.specifierType.length + 1).trim(); - this.aliasTarget = undefined; + this.versionSpecifier = workspaceSpecResult.versionSpecifier; + + if (workspaceSpecResult.alias) { + // "workspace:some-package@^1.2.3" should be resolved as alias + this.aliasTarget = DependencySpecifier.parseWithCache( + workspaceSpecResult.alias, + workspaceSpecResult.version + ); + } else { + this.aliasTarget = undefined; + } + return; } @@ -104,12 +198,38 @@ export class DependencySpecifier { if (!aliasResult.subSpec || !aliasResult.subSpec.name) { throw new InternalError('Unexpected result from npm-package-arg'); } - this.aliasTarget = new DependencySpecifier(aliasResult.subSpec.name, aliasResult.subSpec.rawSpec); + this.aliasTarget = DependencySpecifier.parseWithCache( + aliasResult.subSpec.name, + aliasResult.subSpec.rawSpec + ); } else { this.aliasTarget = undefined; } } + /** + * Clears the dependency specifier parse cache. + */ + public static clearCache(): void { + dependencySpecifierParseCache.clear(); + } + + /** + * Parses a dependency specifier with caching. + * @param packageName - The name of the package the version specifier corresponds to + * @param versionSpecifier - The version specifier to parse + * @returns The parsed dependency specifier + */ + public static parseWithCache(packageName: string, versionSpecifier: string): DependencySpecifier { + const cacheKey: string = `${packageName}\0${versionSpecifier}`; + let result: DependencySpecifier | undefined = dependencySpecifierParseCache.get(cacheKey); + if (!result) { + result = new DependencySpecifier(packageName, versionSpecifier); + dependencySpecifierParseCache.set(cacheKey, result); + } + return result; + } + public static getDependencySpecifierType(specifierType: string): DependencySpecifierType { switch (specifierType) { case 'git': diff --git a/libraries/rush-lib/src/logic/EventHooksManager.ts b/libraries/rush-lib/src/logic/EventHooksManager.ts index 7156466e403..a7324a8f119 100644 --- a/libraries/rush-lib/src/logic/EventHooksManager.ts +++ b/libraries/rush-lib/src/logic/EventHooksManager.ts @@ -1,13 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; +import { Colorize } from '@rushstack/terminal'; -import { EventHooks } from '../api/EventHooks'; -import { Utilities } from '../utilities/Utilities'; +import type { EventHooks } from '../api/EventHooks'; +import { type IEnvironment, Utilities } from '../utilities/Utilities'; import { Event } from '../api/EventHooks'; import { Stopwatch } from '../utilities/Stopwatch'; -import { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfiguration } from '../api/RushConfiguration'; +import { EnvironmentVariableNames } from '../api/EnvironmentConfiguration'; export class EventHooksManager { private _rushConfiguration: RushConfiguration; @@ -28,42 +29,55 @@ export class EventHooksManager { const scripts: string[] = this._eventHooks.get(event); if (scripts.length > 0) { if (ignoreHooks) { + // eslint-disable-next-line no-console console.log(`Skipping event hooks for ${Event[event]} since --ignore-hooks was specified`); return; } const stopwatch: Stopwatch = Stopwatch.start(); - console.log('\n' + colors.green(`Executing event hooks for ${Event[event]}`)); + // eslint-disable-next-line no-console + console.log('\n' + Colorize.green(`Executing event hooks for ${Event[event]}`)); const printEventHooksOutputToConsole: boolean | undefined = isDebug || this._rushConfiguration.experimentsConfiguration.configuration.printEventHooksOutputToConsole; scripts.forEach((script) => { try { + const environment: IEnvironment = { ...process.env }; + + // NOTE: Do NOT expose this variable to other subprocesses besides telemetry hooks. We do NOT want + // child processes to inspect Rush's raw command line and magically change their behavior in a way + // that might be confusing to end users, or rely on CLI parameters that the build cache is unaware of. + environment[EnvironmentVariableNames.RUSH_INVOKED_ARGS] = JSON.stringify(process.argv); + Utilities.executeLifecycleCommand(script, { rushConfiguration: this._rushConfiguration, workingDirectory: this._rushConfiguration.rushJsonFolder, initCwd: this._commonTempFolder, handleOutput: !printEventHooksOutputToConsole, + initialEnvironment: environment, environmentPathOptions: { includeRepoBin: true } }); } catch (error) { + // eslint-disable-next-line no-console console.error( '\n' + - colors.yellow( - `Event hook "${script}" failed. Run "rush" with --debug` + + Colorize.yellow( + `Event hook "${script}" failed: ${error}\nRun "rush" with --debug` + ` to see detailed error information.` ) ); if (isDebug) { + // eslint-disable-next-line no-console console.error('\n' + (error as Error).message); } } }); stopwatch.stop(); - console.log('\n' + colors.green(`Event hooks finished. (${stopwatch.toString()})`)); + // eslint-disable-next-line no-console + console.log('\n' + Colorize.green(`Event hooks finished. (${stopwatch.toString()})`)); } } } diff --git a/libraries/rush-lib/src/logic/Git.ts b/libraries/rush-lib/src/logic/Git.ts index be54245f90e..36386d4224d 100644 --- a/libraries/rush-lib/src/logic/Git.ts +++ b/libraries/rush-lib/src/logic/Git.ts @@ -1,20 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import child_process from 'child_process'; +import type child_process from 'node:child_process'; +import * as path from 'node:path'; + import gitInfo from 'git-repo-info'; -import * as path from 'path'; -import * as url from 'url'; -import colors from 'colors/safe'; import { trueCasePathSync } from 'true-case-path'; -import { Executable, AlreadyReportedError, Path, ITerminal } from '@rushstack/node-core-library'; + +import { Executable, AlreadyReportedError, Path, Async } from '@rushstack/node-core-library'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; import { ensureGitMinimumVersion } from '@rushstack/package-deps-hash'; import { Utilities } from '../utilities/Utilities'; import * as GitEmailPolicy from './policy/GitEmailPolicy'; -import { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfiguration } from '../api/RushConfiguration'; import { EnvironmentConfiguration } from '../api/EnvironmentConfiguration'; -import { IChangedGitStatusEntry, IGitStatusEntry, parseGitStatus } from './GitStatusParser'; +import { type IChangedGitStatusEntry, type IGitStatusEntry, parseGitStatus } from './GitStatusParser'; +import { RushConstants } from './RushConstants'; export const DEFAULT_GIT_TAG_SEPARATOR: string = '_'; @@ -87,31 +89,20 @@ export class Git { } } - /** - * If a Git email address is configured and is nonempty, this returns it. - * Otherwise, undefined is returned. - */ - public tryGetGitEmail(): string | undefined { - const emailResult: IResultOrError = this._tryGetGitEmail(); - if (emailResult.result !== undefined && emailResult.result.length > 0) { - return emailResult.result; - } - return undefined; - } - /** * If a Git email address is configured and is nonempty, this returns it. * Otherwise, configuration instructions are printed to the console, * and AlreadyReportedError is thrown. */ - public getGitEmail(): string { + public async getGitEmailAsync(): Promise { // Determine the user's account // Ex: "bob@example.com" - const emailResult: IResultOrError = this._tryGetGitEmail(); - if (emailResult.error) { + const { error, result } = await this._tryGetGitEmailAsync(); + if (error) { + // eslint-disable-next-line no-console console.log( [ - `Error: ${emailResult.error.message}`, + `Error: ${error.message}`, 'Unable to determine your Git configuration using this command:', '', ' git config user.email', @@ -120,8 +111,16 @@ export class Git { ); throw new AlreadyReportedError(); } + return this.validateGitEmail(result); + } - if (emailResult.result === undefined || emailResult.result.length === 0) { + /** + * If the Git email address is configured and non-empty, this returns it. Otherwise + * it prints an error message and throws. + */ + public validateGitEmail(userEmail: string | undefined): string { + if (userEmail === undefined || userEmail.length === 0) { + // eslint-disable-next-line no-console console.log( [ 'This operation requires that a Git email be specified.', @@ -135,7 +134,7 @@ export class Git { throw new AlreadyReportedError(); } - return emailResult.result; + return userEmail; } /** @@ -150,7 +149,7 @@ export class Git { return undefined; } - public isHooksPathDefault(): boolean { + public async getIsHooksPathDefaultAsync(): Promise { const repoInfo: gitInfo.GitRepoInfo | undefined = this.getGitInfo(); if (!repoInfo?.commonGitDir) { // This should have never been called in a non-Git environment @@ -163,8 +162,9 @@ export class Git { /* ignore errors from true-case-path */ } const defaultHooksPath: string = path.resolve(commonGitDir, 'hooks'); - const hooksResult: IResultOrError = this._tryGetGitHooksPath(); + const hooksResult: IResultOrError = await this._tryGetGitHooksPathAsync(); if (hooksResult.error) { + // eslint-disable-next-line no-console console.log( [ `Error: ${hooksResult.error.message}`, @@ -190,11 +190,13 @@ export class Git { return true; } - public getConfigHooksPath(): string { + public async getConfigHooksPathAsync(): Promise { let configHooksPath: string = ''; const gitPath: string = this.getGitPathOrThrow(); try { - configHooksPath = this._executeGitCommandAndCaptureOutput(gitPath, ['config', 'core.hooksPath']).trim(); + configHooksPath = ( + await this._executeGitCommandAndCaptureOutputAsync(gitPath, ['config', 'core.hooksPath']) + ).trim(); } catch (e) { // git config returns error code 1 if core.hooksPath is not set. } @@ -223,14 +225,18 @@ export class Git { return this._gitInfo; } - public getMergeBase(targetBranch: string, terminal: ITerminal, shouldFetch: boolean = false): string { + public async getMergeBaseAsync( + targetBranch: string, + terminal: ITerminal, + shouldFetch: boolean = false + ): Promise { if (shouldFetch) { this._fetchRemoteBranch(targetBranch, terminal); } const gitPath: string = this.getGitPathOrThrow(); try { - const output: string = this._executeGitCommandAndCaptureOutput(gitPath, [ + const output: string = await this._executeGitCommandAndCaptureOutputAsync(gitPath, [ '--no-optional-locks', 'merge-base', '--', @@ -251,9 +257,9 @@ export class Git { } } - public getBlobContent({ blobSpec, repositoryRoot }: IGetBlobOptions): string { + public async getBlobContentAsync({ blobSpec, repositoryRoot }: IGetBlobOptions): Promise { const gitPath: string = this.getGitPathOrThrow(); - const output: string = this._executeGitCommandAndCaptureOutput( + const output: string = await this._executeGitCommandAndCaptureOutputAsync( gitPath, ['cat-file', 'blob', blobSpec, '--'], repositoryRoot @@ -269,18 +275,18 @@ export class Git { * those in the provided {@param targetBranch}. If a {@param pathPrefix} is provided, * this function only returns results under the that path. */ - public getChangedFiles( + public async getChangedFilesAsync( targetBranch: string, terminal: ITerminal, skipFetch: boolean = false, pathPrefix?: string - ): string[] { + ): Promise { if (!skipFetch) { this._fetchRemoteBranch(targetBranch, terminal); } const gitPath: string = this.getGitPathOrThrow(); - const output: string = this._executeGitCommandAndCaptureOutput(gitPath, [ + const output: string = await this._executeGitCommandAndCaptureOutputAsync(gitPath, [ 'diff', `${targetBranch}...`, '--name-only', @@ -313,11 +319,11 @@ export class Git { * * @param rushConfiguration - rush configuration */ - public getRemoteDefaultBranch(): string { + public async getRemoteDefaultBranchAsync(): Promise { const repositoryUrls: string[] = this._rushConfiguration.repositoryUrls; if (repositoryUrls.length > 0) { const gitPath: string = this.getGitPathOrThrow(); - const output: string = this._executeGitCommandAndCaptureOutput(gitPath, ['remote']).trim(); + const output: string = (await this._executeGitCommandAndCaptureOutputAsync(gitPath, ['remote'])).trim(); const normalizedRepositoryUrls: Set = new Set(); for (const repositoryUrl of repositoryUrls) { @@ -325,31 +331,35 @@ export class Git { normalizedRepositoryUrls.add(Git.normalizeGitUrlForComparison(repositoryUrl).toUpperCase()); } - const matchingRemotes: string[] = output.split('\n').filter((remoteName) => { - if (remoteName) { - const remoteUrl: string = this._executeGitCommandAndCaptureOutput(gitPath, [ - 'remote', - 'get-url', - '--', - remoteName - ]).trim(); - - if (!remoteUrl) { - return false; - } - - // Also apply toUpperCase() for a case-insensitive comparison - const normalizedRemoteUrl: string = Git.normalizeGitUrlForComparison(remoteUrl).toUpperCase(); - if (normalizedRepositoryUrls.has(normalizedRemoteUrl)) { - return true; + const matchingRemotes: string[] = []; + await Async.forEachAsync( + output.split('\n'), + async (remoteName) => { + if (remoteName) { + const remoteUrl: string = ( + await this._executeGitCommandAndCaptureOutputAsync(gitPath, [ + 'remote', + 'get-url', + '--', + remoteName + ]) + ).trim(); + + if (remoteUrl) { + // Also apply toUpperCase() for a case-insensitive comparison + const normalizedRemoteUrl: string = Git.normalizeGitUrlForComparison(remoteUrl).toUpperCase(); + if (normalizedRepositoryUrls.has(normalizedRemoteUrl)) { + matchingRemotes.push(remoteName); + } + } } - } - - return false; - }); + }, + { concurrency: 10 } + ); if (matchingRemotes.length > 0) { if (matchingRemotes.length > 1) { + // eslint-disable-next-line no-console console.log( `More than one git remote matches the repository URL. Using the first remote (${matchingRemotes[0]}).` ); @@ -363,24 +373,25 @@ export class Git { ', ' )}). ` : `Unable to find a git remote matching the repository URL (${repositoryUrls[0]}). `; - console.log(colors.yellow(errorMessage + 'Detected changes are likely to be incorrect.')); + // eslint-disable-next-line no-console + console.log(Colorize.yellow(errorMessage + 'Detected changes are likely to be incorrect.')); return this._rushConfiguration.repositoryDefaultFullyQualifiedRemoteBranch; } } else { + // eslint-disable-next-line no-console console.log( - colors.yellow( - 'A git remote URL has not been specified in rush.json. Setting the baseline remote URL is recommended.' + Colorize.yellow( + `A git remote URL has not been specified in ${RushConstants.rushJsonFilename}. Setting the baseline remote URL is recommended.` ) ); return this._rushConfiguration.repositoryDefaultFullyQualifiedRemoteBranch; } } - public hasUncommittedChanges(): boolean { - const gitStatusEntries: Iterable = this.getGitStatus(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - for (const gitStatusEntry of gitStatusEntries) { + public async hasUncommittedChangesAsync(): Promise { + const gitStatusEntries: Iterable = await this.getGitStatusAsync(); + for (const _ of gitStatusEntries) { // If there are any changes, return true. We only need to evaluate the first iterator entry return true; } @@ -388,8 +399,8 @@ export class Git { return false; } - public hasUnstagedChanges(): boolean { - const gitStatusEntries: Iterable = this.getGitStatus(); + public async hasUnstagedChangesAsync(): Promise { + const gitStatusEntries: Iterable = await this.getGitStatusAsync(); for (const gitStatusEntry of gitStatusEntries) { if ( gitStatusEntry.kind === 'untracked' || @@ -405,9 +416,9 @@ export class Git { /** * The list of files changed but not committed */ - public getUncommittedChanges(): ReadonlyArray { + public async getUncommittedChangesAsync(): Promise> { const result: string[] = []; - const gitStatusEntries: Iterable = this.getGitStatus(); + const gitStatusEntries: Iterable = await this.getGitStatusAsync(); for (const gitStatusEntry of gitStatusEntries) { result.push(gitStatusEntry.path); } @@ -419,10 +430,10 @@ export class Git { return this._rushConfiguration.gitTagSeparator || DEFAULT_GIT_TAG_SEPARATOR; } - public getGitStatus(): Iterable { + public async getGitStatusAsync(): Promise> { const gitPath: string = this.getGitPathOrThrow(); // See Git.test.ts for example output - const output: string = this._executeGitCommandAndCaptureOutput(gitPath, [ + const output: string = await this._executeGitCommandAndCaptureOutputAsync(gitPath, [ 'status', '--porcelain=2', '--null', @@ -466,34 +477,40 @@ export class Git { // Example: "host.ext" const host: string = scpLikeSyntaxMatch[1]; // Example: "path/to/repo" - const path: string = scpLikeSyntaxMatch[2]; + const urlPath: string = scpLikeSyntaxMatch[2]; - if (path.startsWith('/')) { - result = `https://${host}${path}`; + if (urlPath.startsWith('/')) { + result = `https://${host}${urlPath}`; } else { - result = `https://${host}/${path}`; + result = `https://${host}/${urlPath}`; } } - const parsedUrl: url.UrlWithStringQuery = url.parse(result); - - // Only convert recognized schemes - - switch (parsedUrl.protocol) { - case 'http:': - case 'https:': - case 'ssh:': - case 'ftp:': - case 'ftps:': - case 'git:': - case 'git+http:': - case 'git+https:': - case 'git+ssh:': - case 'git+ftp:': - case 'git+ftps:': - // Assemble the parts we want: - result = `https://${parsedUrl.host}${parsedUrl.pathname}`; - break; + // Use the WHATWG URL API instead of the deprecated url.parse(). + // The URL constructor throws for non-standard URLs (e.g. local paths), so we + // catch and leave the result unchanged in that case. + try { + const parsedUrl: URL = new URL(result); + + // Only convert recognized schemes + switch (parsedUrl.protocol) { + case 'http:': + case 'https:': + case 'ssh:': + case 'ftp:': + case 'ftps:': + case 'git:': + case 'git+http:': + case 'git+https:': + case 'git+ssh:': + case 'git+ftp:': + case 'git+ftps:': + // Assemble the parts we want: + result = `https://${parsedUrl.host}${parsedUrl.pathname}`; + break; + } + } catch { + // Not a valid URL (e.g. a local path) -- leave result unchanged } // Trim ".git" or ".git/" from the end @@ -501,12 +518,50 @@ export class Git { return result; } - private _tryGetGitEmail(): IResultOrError { + /** + * This will throw errors only if we cannot find Git commandline. + * If git email didn't configure, this will return undefined; otherwise, + * returns user.email config + */ + public async tryGetGitEmailAsync(): Promise { + const { result } = await this._tryGetGitEmailAsync(); + return result; + } + + public async stageAndCommitGitChangesAsync( + pattern: string[], + message: string, + terminal: ITerminal + ): Promise { + try { + const gitPath: string = this.getGitPathOrThrow(); + await this._executeGitCommandAndCaptureOutputAsync( + gitPath, + ['add', '--', ...pattern], + this._rushConfiguration.changesFolder + ); + await this._executeGitCommandAndCaptureOutputAsync( + gitPath, + ['commit', '-m', message, '--', ...pattern], + this._rushConfiguration.changesFolder + ); + } catch (error) { + terminal.writeErrorLine(`ERROR: Cannot stage and commit git changes ${(error as Error).message}`); + } + } + + /** + * Returns an object containing either the result of the `git config user.email` + * command or an error. + */ + private async _tryGetGitEmailAsync(): Promise> { if (this._gitEmailResult === undefined) { const gitPath: string = this.getGitPathOrThrow(); try { this._gitEmailResult = { - result: this._executeGitCommandAndCaptureOutput(gitPath, ['config', 'user.email']).trim() + result: ( + await this._executeGitCommandAndCaptureOutputAsync(gitPath, ['config', 'user.email']) + ).trim() }; } catch (e) { this._gitEmailResult = { @@ -518,16 +573,14 @@ export class Git { return this._gitEmailResult; } - private _tryGetGitHooksPath(): IResultOrError { + private async _tryGetGitHooksPathAsync(): Promise> { if (this._gitHooksPath === undefined) { const gitPath: string = this.getGitPathOrThrow(); try { this._gitHooksPath = { - result: this._executeGitCommandAndCaptureOutput(gitPath, [ - 'rev-parse', - '--git-path', - 'hooks' - ]).trim() + result: ( + await this._executeGitCommandAndCaptureOutputAsync(gitPath, ['rev-parse', '--git-path', 'hooks']) + ).trim() }; } catch (e) { this._gitHooksPath = { @@ -562,6 +615,7 @@ export class Git { } private _fetchRemoteBranch(remoteBranchName: string, terminal: ITerminal): void { + // eslint-disable-next-line no-console console.log(`Checking for updates to ${remoteBranchName}...`); const fetchResult: boolean = this._tryFetchRemoteBranch(remoteBranchName); if (!fetchResult) { @@ -574,16 +628,40 @@ export class Git { /** * @internal */ - public _executeGitCommandAndCaptureOutput( + public async _executeGitCommandAndCaptureOutputAsync( gitPath: string, args: string[], - repositoryRoot: string = this._rushConfiguration.rushJsonFolder - ): string { + workingDirectory: string = this._rushConfiguration.rushJsonFolder + ): Promise { try { - return Utilities.executeCommandAndCaptureOutput(gitPath, args, repositoryRoot); + return await Utilities.executeCommandAndCaptureOutputAsync({ + command: gitPath, + args, + workingDirectory + }); } catch (e) { ensureGitMinimumVersion(gitPath); throw e; } } + /** + * + * @param ref Given a ref which can be branch name, commit hash, tag name, etc, check if it is a commit hash + */ + public async determineIfRefIsACommitAsync(ref: string): Promise { + const gitPath: string = this.getGitPathOrThrow(); + try { + const output: string = await this._executeGitCommandAndCaptureOutputAsync(gitPath, [ + 'rev-parse', + '--verify', + ref + ]); + const result: string = output.trim(); + + return result === ref; + } catch (e) { + // assume not a commit + return false; + } + } } diff --git a/libraries/rush-lib/src/logic/InstallManagerFactory.ts b/libraries/rush-lib/src/logic/InstallManagerFactory.ts index f9b49b2b023..c86ad166c0e 100644 --- a/libraries/rush-lib/src/logic/InstallManagerFactory.ts +++ b/libraries/rush-lib/src/logic/InstallManagerFactory.ts @@ -2,10 +2,9 @@ // See LICENSE in the project root for license information. import { WorkspaceInstallManager } from './installManager/WorkspaceInstallManager'; -import { PurgeManager } from './PurgeManager'; -import { RushConfiguration } from '../api/RushConfiguration'; -import { RushGlobalFolder } from '../api/RushGlobalFolder'; - +import type { PurgeManager } from './PurgeManager'; +import type { RushConfiguration } from '../api/RushConfiguration'; +import type { RushGlobalFolder } from '../api/RushGlobalFolder'; import type { BaseInstallManager } from './base/BaseInstallManager'; import type { IInstallManagerOptions } from './base/BaseInstallManagerTypes'; @@ -17,7 +16,7 @@ export class InstallManagerFactory { options: IInstallManagerOptions ): Promise { if ( - rushConfiguration.packageManager === 'pnpm' && + rushConfiguration.isPnpm && rushConfiguration.pnpmOptions && rushConfiguration.pnpmOptions.useWorkspaces ) { diff --git a/libraries/rush-lib/src/logic/InteractiveUpgrader.ts b/libraries/rush-lib/src/logic/InteractiveUpgrader.ts index 3b2e1d0b8cf..a9a2e855179 100644 --- a/libraries/rush-lib/src/logic/InteractiveUpgrader.ts +++ b/libraries/rush-lib/src/logic/InteractiveUpgrader.ts @@ -1,16 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import npmCheck from 'npm-check'; -import type * as NpmCheck from 'npm-check'; -import colors from 'colors/safe'; +import { NpmCheck, type INpmCheckState, type INpmCheckPackageSummary } from '@rushstack/npm-check-fork'; +import { Colorize } from '@rushstack/terminal'; -import { RushConfiguration } from '../api/RushConfiguration'; -import { upgradeInteractive, IDepsToUpgradeAnswers } from '../utilities/InteractiveUpgradeUI'; -import { RushConfigurationProject } from '../api/RushConfigurationProject'; -import Prompt from 'inquirer/lib/ui/prompt'; - -import { SearchListPrompt } from '../utilities/prompts/SearchListPrompt'; +import type { RushConfiguration } from '../api/RushConfiguration'; +import { upgradeInteractive, type IDepsToUpgradeAnswers } from '../utilities/InteractiveUpgradeUI'; +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; interface IUpgradeInteractiveDeps { projects: RushConfigurationProject[]; @@ -24,59 +20,57 @@ export class InteractiveUpgrader { this._rushConfiguration = rushConfiguration; } - public async upgrade(): Promise { - const rushProject: RushConfigurationProject = await this._getUserSelectedProjectForUpgrade(); + public async upgradeAsync(): Promise { + const rushProject: RushConfigurationProject = await this._getUserSelectedProjectForUpgradeAsync(); - const dependenciesState: NpmCheck.INpmCheckPackage[] = await this._getPackageDependenciesStatus( - rushProject - ); + const dependenciesState: INpmCheckPackageSummary[] = + await this._getPackageDependenciesStatusAsync(rushProject); - const depsToUpgrade: IDepsToUpgradeAnswers = await this._getUserSelectedDependenciesToUpgrade( - dependenciesState - ); + const depsToUpgrade: IDepsToUpgradeAnswers = + await this._getUserSelectedDependenciesToUpgradeAsync(dependenciesState); return { projects: [rushProject], depsToUpgrade }; } - private async _getUserSelectedDependenciesToUpgrade( - packages: NpmCheck.INpmCheckPackage[] + private async _getUserSelectedDependenciesToUpgradeAsync( + packages: INpmCheckPackageSummary[] ): Promise { return upgradeInteractive(packages); } - private async _getUserSelectedProjectForUpgrade(): Promise { + private async _getUserSelectedProjectForUpgradeAsync(): Promise { const projects: RushConfigurationProject[] | undefined = this._rushConfiguration.projects; - const ui: Prompt = new Prompt({ - list: SearchListPrompt - }); - const { selectProject } = await ui.run([ - { - name: 'selectProject', - message: 'Select a project you would like to upgrade', - type: 'list', - choices: projects.map((project) => { - return { - name: colors.green(project.packageName), - value: project - }; - }), - pageSize: 12 - } - ]); + const { default: search } = await import('@inquirer/search'); - return selectProject; + return await search({ + message: 'Select a project you would like to upgrade', + source: (term) => { + const choices: { name: string; short: string; value: RushConfigurationProject }[] = projects.map( + (project) => ({ + name: Colorize.green(project.packageName), + short: project.packageName, + value: project + }) + ); + if (!term) { + return choices; + } + const filter: string = term.toUpperCase(); + return choices.filter((choice) => choice.short.toUpperCase().includes(filter)); + }, + pageSize: 12 + }); } - private async _getPackageDependenciesStatus( + private async _getPackageDependenciesStatusAsync( rushProject: RushConfigurationProject - ): Promise { + ): Promise { const { projectFolder } = rushProject; - const currentState: NpmCheck.INpmCheckCurrentState = await npmCheck({ - cwd: projectFolder, - skipUnused: true + const currentState: INpmCheckState = await NpmCheck({ + cwd: projectFolder }); - return currentState.get('packages'); + return currentState.packages ?? []; } } diff --git a/libraries/rush-lib/src/logic/LinkManagerFactory.ts b/libraries/rush-lib/src/logic/LinkManagerFactory.ts index f90b4312d46..810b3796a91 100644 --- a/libraries/rush-lib/src/logic/LinkManagerFactory.ts +++ b/libraries/rush-lib/src/logic/LinkManagerFactory.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { RushConfiguration } from '../api/RushConfiguration'; -import { BaseLinkManager } from './base/BaseLinkManager'; +import type { RushConfiguration } from '../api/RushConfiguration'; +import type { BaseLinkManager } from './base/BaseLinkManager'; import { NpmLinkManager } from './npm/NpmLinkManager'; import { PnpmLinkManager } from './pnpm/PnpmLinkManager'; diff --git a/libraries/rush-lib/src/logic/LookupByPath.ts b/libraries/rush-lib/src/logic/LookupByPath.ts deleted file mode 100644 index ce209f909e4..00000000000 --- a/libraries/rush-lib/src/logic/LookupByPath.ts +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -/** - * A node in the path tree used in LookupByPath - */ -interface IPathTreeNode { - /** - * The value that exactly matches the current relative path - */ - value: TItem | undefined; - /** - * Child nodes by subfolder - */ - children: Map> | undefined; -} - -interface IPrefixEntry { - prefix: string; - index: number; -} - -/** - * Object containing both the matched item and the start index of the remainder of the query. - * - * @beta - */ -export interface IPrefixMatch { - value: TItem; - index: number; -} - -/** - * This class is used to associate POSIX relative paths, such as those returned by `git` commands, - * with entities that correspond with ancestor folders, such as Rush Projects. - * - * It is optimized for efficiently locating the nearest ancestor path with an associated value. - * - * @example - * ```ts - * const tree = new LookupByPath([['foo', 1], ['bar', 2], ['foo/bar', 3]]); - * tree.findChildPath('foo'); // returns 1 - * tree.findChildPath('foo/baz'); // returns 1 - * tree.findChildPath('baz'); // returns undefined - * tree.findChildPath('foo/bar/baz'); returns 3 - * tree.findChildPath('bar/foo/bar'); returns 2 - * ``` - * @beta - */ -export class LookupByPath { - /** - * The delimiter used to split paths - */ - public readonly delimiter: string; - /** - * The root node of the tree, corresponding to the path '' - */ - private readonly _root: IPathTreeNode; - - /** - * Constructs a new `LookupByPath` - * - * @param entries - Initial path-value pairs to populate the tree. - */ - public constructor(entries?: Iterable<[string, TItem]>, delimiter?: string) { - this._root = { - value: undefined, - children: undefined - }; - - this.delimiter = delimiter ?? '/'; - - if (entries) { - for (const [path, item] of entries) { - this.setItem(path, item); - } - } - } - - /** - * Iterates over the segments of a serialized path. - * - * @example - * - * `LookupByPath.iteratePathSegments('foo/bar/baz')` yields 'foo', 'bar', 'baz' - * - * `LookupByPath.iteratePathSegments('foo\\bar\\baz', '\\')` yields 'foo', 'bar', 'baz' - */ - public static *iteratePathSegments(serializedPath: string, delimiter: string = '/'): Iterable { - for (const prefixMatch of this._iteratePrefixes(serializedPath, delimiter)) { - yield prefixMatch.prefix; - } - } - - private static *_iteratePrefixes(input: string, delimiter: string = '/'): Iterable { - if (!input) { - return; - } - - let previousIndex: number = 0; - let nextIndex: number = input.indexOf(delimiter); - - // Leading segments - while (nextIndex >= 0) { - yield { - prefix: input.slice(previousIndex, nextIndex), - index: nextIndex - }; - previousIndex = nextIndex + 1; - nextIndex = input.indexOf(delimiter, previousIndex); - } - - // Last segment - if (previousIndex < input.length) { - yield { - prefix: input.slice(previousIndex, input.length), - index: input.length - }; - } - } - - /** - * Associates the value with the specified serialized path. - * If a value is already associated, will overwrite. - * - * @returns this, for chained calls - */ - public setItem(serializedPath: string, value: TItem): this { - return this.setItemFromSegments(LookupByPath.iteratePathSegments(serializedPath, this.delimiter), value); - } - - /** - * Associates the value with the specified path. - * If a value is already associated, will overwrite. - * - * @returns this, for chained calls - */ - public setItemFromSegments(pathSegments: Iterable, value: TItem): this { - let node: IPathTreeNode = this._root; - for (const segment of pathSegments) { - if (!node.children) { - node.children = new Map(); - } - let child: IPathTreeNode | undefined = node.children.get(segment); - if (!child) { - node.children.set( - segment, - (child = { - value: undefined, - children: undefined - }) - ); - } - node = child; - } - node.value = value; - - return this; - } - - /** - * Searches for the item associated with `childPath`, or the nearest ancestor of that path that - * has an associated item. - * - * @returns the found item, or `undefined` if no item was found - * - * @example - * ```ts - * const tree = new LookupByPath([['foo', 1], ['foo/bar', 2]]); - * tree.findChildPath('foo/baz'); // returns 1 - * tree.findChildPath('foo/bar/baz'); // returns 2 - * ``` - */ - public findChildPath(childPath: string): TItem | undefined { - return this.findChildPathFromSegments(LookupByPath.iteratePathSegments(childPath, this.delimiter)); - } - - /** - * Searches for the item for which the recorded prefix is the longest matching prefix of `query`. - * Obtains both the item and the length of the matched prefix, so that the remainder of the path can be - * extracted. - * - * @returns the found item and the length of the matched prefix, or `undefined` if no item was found - * - * @example - * ```ts - * const tree = new LookupByPath([['foo', 1], ['foo/bar', 2]]); - * tree.findLongestPrefixMatch('foo/baz'); // returns { item: 1, index: 3 } - * tree.findLongestPrefixMatch('foo/bar/baz'); // returns { item: 2, index: 7 } - * ``` - */ - public findLongestPrefixMatch(query: string): IPrefixMatch | undefined { - return this._findLongestPrefixMatch(LookupByPath._iteratePrefixes(query, this.delimiter)); - } - - /** - * Searches for the item associated with `childPathSegments`, or the nearest ancestor of that path that - * has an associated item. - * - * @returns the found item, or `undefined` if no item was found - * - * @example - * ```ts - * const tree = new LookupByPath([['foo', 1], ['foo/bar', 2]]); - * tree.findChildPathFromSegments(['foo', 'baz']); // returns 1 - * tree.findChildPathFromSegments(['foo','bar', 'baz']); // returns 2 - * ``` - */ - public findChildPathFromSegments(childPathSegments: Iterable): TItem | undefined { - let node: IPathTreeNode = this._root; - let best: TItem | undefined = node.value; - // Trivial cases - if (node.children) { - for (const segment of childPathSegments) { - const child: IPathTreeNode | undefined = node.children.get(segment); - if (!child) { - break; - } - node = child; - best = node.value ?? best; - if (!node.children) { - break; - } - } - } - - return best; - } - - /** - * Iterates through progressively longer prefixes of a given string and returns as soon - * as the number of candidate items that match the prefix are 1 or 0. - * - * If a match is present, returns the matched itme and the length of the matched prefix. - * - * @returns the found item, or `undefined` if no item was found - */ - private _findLongestPrefixMatch(prefixes: Iterable): IPrefixMatch | undefined { - let node: IPathTreeNode = this._root; - let best: IPrefixMatch | undefined = node.value - ? { - value: node.value, - index: 0 - } - : undefined; - // Trivial cases - if (node.children) { - for (const { prefix: hash, index } of prefixes) { - const child: IPathTreeNode | undefined = node.children.get(hash); - if (!child) { - break; - } - node = child; - if (node.value !== undefined) { - best = { - value: node.value, - index - }; - } - if (!node.children) { - break; - } - } - } - - return best; - } -} diff --git a/libraries/rush-lib/src/logic/NodeJsCompatibility.ts b/libraries/rush-lib/src/logic/NodeJsCompatibility.ts index 1010e1f4554..b39d4a954b1 100644 --- a/libraries/rush-lib/src/logic/NodeJsCompatibility.ts +++ b/libraries/rush-lib/src/logic/NodeJsCompatibility.ts @@ -1,12 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; import * as semver from 'semver'; +import { Colorize } from '@rushstack/terminal'; + // Minimize dependencies to avoid compatibility errors that might be encountered before // NodeJsCompatibility.terminateIfVersionIsTooOld() gets to run. import type { RushConfiguration } from '../api/RushConfiguration'; +import { RushConstants } from './RushConstants'; /** * This constant is the major version of the next LTS node Node.js release. This constant should be updated when @@ -15,7 +17,7 @@ import type { RushConfiguration } from '../api/RushConfiguration'; * LTS schedule: https://nodejs.org/en/about/releases/ * LTS versions: https://nodejs.org/en/download/releases/ */ -const UPCOMING_NODE_LTS_VERSION: number = 18; +const UPCOMING_NODE_LTS_VERSION: number = 24; const nodeVersion: string = process.versions.node; const nodeMajorVersion: number = semver.major(nodeVersion); @@ -49,9 +51,10 @@ export class NodeJsCompatibility { // IMPORTANT: If this test fails, the Rush CLI front-end process will terminate with an error. // Only increment it when our code base is known to use newer features (e.g. "async"/"await") that // have no hope of working with older Node.js. - if (semver.satisfies(nodeVersion, '< 8.9.0')) { + if (semver.satisfies(nodeVersion, '<14.18.0')) { + // eslint-disable-next-line no-console console.error( - colors.red( + Colorize.red( `Your version of Node.js (${nodeVersion}) is very old and incompatible with Rush. ` + `Please upgrade to the latest Long-Term Support (LTS) version.\n` ) @@ -73,8 +76,8 @@ export class NodeJsCompatibility { return ( NodeJsCompatibility.reportAncientIncompatibleVersion() || NodeJsCompatibility.warnAboutVersionTooNew(options) || - NodeJsCompatibility._warnAboutOddNumberedVersion() || - NodeJsCompatibility._warnAboutNonLtsVersion(options.rushConfiguration) + _warnAboutOddNumberedVersion() || + _warnAboutNonLtsVersion(options.rushConfiguration) ); } @@ -86,16 +89,18 @@ export class NodeJsCompatibility { if (!options.alreadyReportedNodeTooNewError) { // We are on a much newer release than we have tested and support if (options.isRushLib) { + // eslint-disable-next-line no-console console.warn( - colors.yellow( + Colorize.yellow( `Your version of Node.js (${nodeVersion}) has not been tested with this release ` + - `of the Rush engine. Please consider upgrading the "rushVersion" setting in rush.json, ` + + `of the Rush engine. Please consider upgrading the "rushVersion" setting in ${RushConstants.rushJsonFilename}, ` + `or downgrading Node.js.\n` ) ); } else { + // eslint-disable-next-line no-console console.warn( - colors.yellow( + Colorize.yellow( `Your version of Node.js (${nodeVersion}) has not been tested with this release ` + `of Rush. Please consider installing a newer version of the "@microsoft/rush" ` + `package, or downgrading Node.js.\n` @@ -110,42 +115,44 @@ export class NodeJsCompatibility { } } - private static _warnAboutNonLtsVersion(rushConfiguration: RushConfiguration | undefined): boolean { - if (rushConfiguration && !rushConfiguration.suppressNodeLtsWarning && !NodeJsCompatibility.isLtsVersion) { - console.warn( - colors.yellow( - `Your version of Node.js (${nodeVersion}) is not a Long-Term Support (LTS) release. ` + - 'These versions frequently have bugs. Please consider installing a stable release.\n' - ) - ); + public static get isLtsVersion(): boolean { + return !!process.release.lts; + } - return true; - } else { - return false; - } + public static get isOddNumberedVersion(): boolean { + return nodeMajorVersion % 2 !== 0; } +} - private static _warnAboutOddNumberedVersion(): boolean { - if (NodeJsCompatibility.isOddNumberedVersion) { - console.warn( - colors.yellow( - `Your version of Node.js (${nodeVersion}) is an odd-numbered release. ` + - `These releases frequently have bugs. Please consider installing a Long Term Support (LTS) ` + - `version instead.\n` - ) - ); +function _warnAboutNonLtsVersion(rushConfiguration: RushConfiguration | undefined): boolean { + if (rushConfiguration && !rushConfiguration.suppressNodeLtsWarning && !NodeJsCompatibility.isLtsVersion) { + // eslint-disable-next-line no-console + console.warn( + Colorize.yellow( + `Your version of Node.js (${nodeVersion}) is not a Long-Term Support (LTS) release. ` + + 'These versions frequently have bugs. Please consider installing a stable release.\n' + ) + ); - return true; - } else { - return false; - } + return true; + } else { + return false; } +} - public static get isLtsVersion(): boolean { - return !!process.release.lts; - } +function _warnAboutOddNumberedVersion(): boolean { + if (NodeJsCompatibility.isOddNumberedVersion) { + // eslint-disable-next-line no-console + console.warn( + Colorize.yellow( + `Your version of Node.js (${nodeVersion}) is an odd-numbered release. ` + + `These releases frequently have bugs. Please consider installing a Long Term Support (LTS) ` + + `version instead.\n` + ) + ); - public static get isOddNumberedVersion(): boolean { - return nodeMajorVersion % 2 !== 0; + return true; + } else { + return false; } } diff --git a/libraries/rush-lib/src/logic/PackageJsonUpdater.ts b/libraries/rush-lib/src/logic/PackageJsonUpdater.ts index 1f1b29aa1bd..3a1e4789fbb 100644 --- a/libraries/rush-lib/src/logic/PackageJsonUpdater.ts +++ b/libraries/rush-lib/src/logic/PackageJsonUpdater.ts @@ -1,22 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; import * as semver from 'semver'; -import type * as NpmCheck from 'npm-check'; -import { ConsoleTerminalProvider, Terminal, ITerminalProvider, Colors } from '@rushstack/node-core-library'; -import { RushConfiguration } from '../api/RushConfiguration'; +import type { INpmCheckPackageSummary } from '@rushstack/npm-check-fork'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; +import { Async } from '@rushstack/node-core-library'; + +import type { RushConfiguration } from '../api/RushConfiguration'; import type { BaseInstallManager } from './base/BaseInstallManager'; import type { IInstallManagerOptions } from './base/BaseInstallManagerTypes'; import { InstallManagerFactory } from './InstallManagerFactory'; import { VersionMismatchFinder } from './versionMismatch/VersionMismatchFinder'; import { PurgeManager } from './PurgeManager'; import { Utilities } from '../utilities/Utilities'; -import { DependencyType, PackageJsonDependency } from '../api/PackageJsonEditor'; -import { RushGlobalFolder } from '../api/RushGlobalFolder'; -import { RushConfigurationProject } from '../api/RushConfigurationProject'; -import { VersionMismatchFinderEntity } from './versionMismatch/VersionMismatchFinderEntity'; +import { DependencyType, type PackageJsonDependency } from '../api/PackageJsonEditor'; +import type { RushGlobalFolder } from '../api/RushGlobalFolder'; +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; +import type { VersionMismatchFinderEntity } from './versionMismatch/VersionMismatchFinderEntity'; import { VersionMismatchFinderProject } from './versionMismatch/VersionMismatchFinderProject'; import { RushConstants } from './RushConstants'; import { InstallHelpers } from './installManager/InstallHelpers'; @@ -28,6 +29,8 @@ import { type IPackageJsonUpdaterRushRemoveOptions, SemVerStyle } from './PackageJsonUpdaterTypes'; +import type { Subspace } from '../api/Subspace'; +import { MAKE_CONSISTENT_FLAG_NAME } from '../cli/actions/AddAction'; /** * Options for adding a dependency to a particular project. @@ -40,7 +43,7 @@ export interface IPackageJsonUpdaterRushUpgradeOptions { /** * The dependencies to be added. */ - packagesToAdd: NpmCheck.INpmCheckPackage[]; + packagesToAdd: INpmCheckPackageSummary[]; /** * If specified, other packages that use this dependency will also have their package.json's updated. */ @@ -56,7 +59,7 @@ export interface IPackageJsonUpdaterRushUpgradeOptions { /** * The variant to consider when performing installations and validating shrinkwrap updates. */ - variant?: string | undefined; + variant: string | undefined; } /** @@ -97,18 +100,18 @@ export interface IRemoveProjectOptions extends IBaseUpdateProjectOptions {} * @internal */ export class PackageJsonUpdater { - private _rushConfiguration: RushConfiguration; - private _rushGlobalFolder: RushGlobalFolder; - - private readonly _terminalProvider: ITerminalProvider; - private readonly _terminal: Terminal; - - public constructor(rushConfiguration: RushConfiguration, rushGlobalFolder: RushGlobalFolder) { + private readonly _terminal: ITerminal; + private readonly _rushConfiguration: RushConfiguration; + private readonly _rushGlobalFolder: RushGlobalFolder; + + public constructor( + terminal: ITerminal, + rushConfiguration: RushConfiguration, + rushGlobalFolder: RushGlobalFolder + ) { + this._terminal = terminal; this._rushConfiguration = rushConfiguration; this._rushGlobalFolder = rushGlobalFolder; - - this._terminalProvider = new ConsoleTerminalProvider(); - this._terminal = new Terminal(this._terminalProvider); } /** @@ -128,10 +131,11 @@ export class PackageJsonUpdater { allVersionsByPackageName, implicitlyPreferredVersionByPackageName, commonVersionsConfiguration - }: IDependencyAnalysis = dependencyAnalyzer.getAnalysis(variant); + }: IDependencyAnalysis = dependencyAnalyzer.getAnalysis(undefined, variant, false); const dependenciesToUpdate: Record = {}; const devDependenciesToUpdate: Record = {}; + const peerDependenciesToUpdate: Record = {}; for (const { moduleName, latest: latestVersion, packageJson, devDependency } of packagesToAdd) { const inferredRangeStyle: SemVerStyle = this._cheaplyDetectSemVerRangeStyle(packageJson); @@ -141,13 +145,14 @@ export class PackageJsonUpdater { const explicitlyPreferredVersion: string | undefined = commonVersionsConfiguration.preferredVersions.get(moduleName); - const version: string = await this._getNormalizedVersionSpec( + const version: string = await this._getNormalizedVersionSpecAsync( projects, moduleName, latestVersion, implicitlyPreferredVersion, explicitlyPreferredVersion, - inferredRangeStyle + inferredRangeStyle, + commonVersionsConfiguration.ensureConsistentVersions ); if (devDependency) { @@ -157,7 +162,7 @@ export class PackageJsonUpdater { } this._terminal.writeLine( - colors.green(`Updating projects to use `) + moduleName + '@' + colors.cyan(version) + Colorize.green(`Updating projects to use `) + moduleName + '@' + Colorize.cyan(version) ); this._terminal.writeLine(); @@ -165,7 +170,7 @@ export class PackageJsonUpdater { if ( existingSpecifiedVersions && !existingSpecifiedVersions.has(version) && - this._rushConfiguration.ensureConsistentVersions && + commonVersionsConfiguration.ensureConsistentVersions && !updateOtherPackages ) { // There are existing versions, and the version we're going to use is not one of them, and this repo @@ -183,7 +188,8 @@ export class PackageJsonUpdater { const allPackageUpdates: Map = new Map(); const allDependenciesToUpdate: [string, string][] = [ ...Object.entries(dependenciesToUpdate), - ...Object.entries(devDependenciesToUpdate) + ...Object.entries(devDependenciesToUpdate), + ...Object.entries(peerDependenciesToUpdate) ]; for (const project of projects) { @@ -210,9 +216,7 @@ export class PackageJsonUpdater { if (updateOtherPackages) { const mismatchFinder: VersionMismatchFinder = VersionMismatchFinder.getMismatches( this._rushConfiguration, - { - variant: variant - } + options ); for (const update of this._getUpdates(mismatchFinder, allDependenciesToUpdate)) { this.updateProject(update); @@ -220,43 +224,27 @@ export class PackageJsonUpdater { } } - for (const [filePath, project] of allPackageUpdates) { - if (project.saveIfModified()) { - this._terminal.writeLine(colors.green('Wrote ') + filePath); - } - } + await Async.forEachAsync( + allPackageUpdates, + async ([filePath, project]) => { + const modified: boolean = await project.saveIfModifiedAsync(); + if (modified) { + this._terminal.writeLine(Colorize.green('Wrote ') + filePath); + } + }, + { concurrency: 10 } + ); if (!skipUpdate) { - this._terminal.writeLine(); - this._terminal.writeLine(colors.green('Running "rush update"')); - this._terminal.writeLine(); - - const purgeManager: PurgeManager = new PurgeManager(this._rushConfiguration, this._rushGlobalFolder); - const installManagerOptions: IInstallManagerOptions = { - debug: debugInstall, - allowShrinkwrapUpdates: true, - bypassPolicy: false, - noLink: false, - fullUpgrade: false, - recheckShrinkwrap: false, - networkConcurrency: undefined, - collectLogFile: false, - variant: variant, - maxInstallAttempts: RushConstants.defaultMaxInstallAttempts, - pnpmFilterArguments: [], - checkOnly: false - }; - - const installManager: BaseInstallManager = await InstallManagerFactory.getInstallManagerAsync( - this._rushConfiguration, - this._rushGlobalFolder, - purgeManager, - installManagerOptions - ); - try { - await installManager.doInstallAsync(); - } finally { - purgeManager.deleteAll(); + if (this._rushConfiguration.subspacesFeatureEnabled) { + const subspaceSet: ReadonlySet = this._rushConfiguration.getSubspacesForProjects( + options.projects + ); + for (const subspace of subspaceSet) { + await this._doUpdateAsync(debugInstall, subspace, variant); + } + } else { + await this._doUpdateAsync(debugInstall, this._rushConfiguration.defaultSubspace, variant); } } } @@ -270,45 +258,72 @@ export class PackageJsonUpdater { } else { throw new Error('only accept "rush add" or "rush remove"'); } + const { skipUpdate, debugInstall, variant } = options; - for (const { project } of allPackageUpdates) { - if (project.saveIfModified()) { - this._terminal.writeLine(Colors.green('Wrote'), project.filePath); + await Async.forEachAsync( + allPackageUpdates, + async ({ project }) => { + const modified: boolean = await project.saveIfModifiedAsync(); + if (modified) { + this._terminal.writeLine(Colorize.green('Wrote'), project.filePath); + } + }, + { concurrency: 10 } + ); + + if (!skipUpdate) { + if (this._rushConfiguration.subspacesFeatureEnabled) { + const subspaceSet: ReadonlySet = this._rushConfiguration.getSubspacesForProjects( + options.projects + ); + for (const subspace of subspaceSet) { + await this._doUpdateAsync(debugInstall, subspace, variant); + } + } else { + await this._doUpdateAsync(debugInstall, this._rushConfiguration.defaultSubspace, variant); } } + } - if (!skipUpdate) { - this._terminal.writeLine(); - this._terminal.writeLine(Colors.green('Running "rush update"')); - this._terminal.writeLine(); + private async _doUpdateAsync( + debugInstall: boolean, + subspace: Subspace, + variant: string | undefined + ): Promise { + this._terminal.writeLine(); + this._terminal.writeLine(Colorize.green('Running "rush update"')); + this._terminal.writeLine(); - const purgeManager: PurgeManager = new PurgeManager(this._rushConfiguration, this._rushGlobalFolder); - const installManagerOptions: IInstallManagerOptions = { - debug: debugInstall, - allowShrinkwrapUpdates: true, - bypassPolicy: false, - noLink: false, - fullUpgrade: false, - recheckShrinkwrap: false, - networkConcurrency: undefined, - collectLogFile: false, - variant: variant, - maxInstallAttempts: RushConstants.defaultMaxInstallAttempts, - pnpmFilterArguments: [], - checkOnly: false - }; + const purgeManager: PurgeManager = new PurgeManager(this._rushConfiguration, this._rushGlobalFolder); + const installManagerOptions: IInstallManagerOptions = { + debug: debugInstall, + allowShrinkwrapUpdates: true, + bypassPolicy: false, + noLink: false, + fullUpgrade: false, + recheckShrinkwrap: false, + networkConcurrency: undefined, + offline: false, + collectLogFile: false, + variant, + maxInstallAttempts: RushConstants.defaultMaxInstallAttempts, + pnpmFilterArgumentValues: [], + selectedProjects: new Set(this._rushConfiguration.projects), + checkOnly: false, + subspace: subspace, + terminal: this._terminal + }; - const installManager: BaseInstallManager = await InstallManagerFactory.getInstallManagerAsync( - this._rushConfiguration, - this._rushGlobalFolder, - purgeManager, - installManagerOptions - ); - try { - await installManager.doInstallAsync(); - } finally { - purgeManager.deleteAll(); - } + const installManager: BaseInstallManager = await InstallManagerFactory.getInstallManagerAsync( + this._rushConfiguration, + this._rushGlobalFolder, + purgeManager, + installManagerOptions + ); + try { + await installManager.doInstallAsync(); + } finally { + await purgeManager.startDeleteAllAsync(); } } @@ -318,7 +333,7 @@ export class PackageJsonUpdater { private async _doRushAddAsync( options: IPackageJsonUpdaterRushAddOptions ): Promise { - const { projects, packagesToUpdate, devDependency, updateOtherPackages, variant } = options; + const { projects } = options; const { DependencyAnalyzer } = await import( /* webpackChunkName: 'DependencyAnalyzer' */ @@ -327,11 +342,35 @@ export class PackageJsonUpdater { const dependencyAnalyzer: DependencyAnalyzer = DependencyAnalyzer.forRushConfiguration( this._rushConfiguration ); + + const allPackageUpdates: IUpdateProjectOptions[] = []; + const subspaceSet: ReadonlySet = this._rushConfiguration.getSubspacesForProjects(projects); + for (const subspace of subspaceSet) { + // Projects for this subspace + allPackageUpdates.push(...(await this._updateProjectsAsync(subspace, dependencyAnalyzer, options))); + } + + return allPackageUpdates; + } + + private async _updateProjectsAsync( + subspace: Subspace, + dependencyAnalyzer: DependencyAnalyzer, + options: IPackageJsonUpdaterRushAddOptions + ): Promise { + const { projects, packagesToUpdate, devDependency, peerDependency, updateOtherPackages, variant } = + options; + + // Get projects for this subspace + const subspaceProjects: RushConfigurationProject[] = projects.filter( + (project) => project.subspace === subspace + ); + const { allVersionsByPackageName, implicitlyPreferredVersionByPackageName, commonVersionsConfiguration - }: IDependencyAnalysis = dependencyAnalyzer.getAnalysis(variant); + }: IDependencyAnalysis = dependencyAnalyzer.getAnalysis(subspace, variant, options.actionName === 'add'); this._terminal.writeLine(); const dependenciesToAddOrUpdate: Record = {}; @@ -342,20 +381,21 @@ export class PackageJsonUpdater { const explicitlyPreferredVersion: string | undefined = commonVersionsConfiguration.preferredVersions.get(packageName); - const version: string = await this._getNormalizedVersionSpec( - projects, + const version: string = await this._getNormalizedVersionSpecAsync( + subspaceProjects, packageName, initialVersion, implicitlyPreferredVersion, explicitlyPreferredVersion, - rangeStyle + rangeStyle, + commonVersionsConfiguration.ensureConsistentVersions ); dependenciesToAddOrUpdate[packageName] = version; this._terminal.writeLine( - Colors.green('Updating projects to use'), + Colorize.green('Updating projects to use '), `${packageName}@`, - Colors.cyan(version) + Colorize.cyan(version) ); this._terminal.writeLine(); @@ -363,7 +403,7 @@ export class PackageJsonUpdater { if ( existingSpecifiedVersions && !existingSpecifiedVersions.has(version) && - this._rushConfiguration.ensureConsistentVersions && + commonVersionsConfiguration.ensureConsistentVersions && !updateOtherPackages ) { // There are existing versions, and the version we're going to use is not one of them, and this repo @@ -372,7 +412,7 @@ export class PackageJsonUpdater { const existingVersionList: string = Array.from(existingSpecifiedVersions).join(', '); throw new Error( `Adding '${packageName}@${version}' ` + - `causes mismatched dependencies. Use the "--make-consistent" flag to update other packages to use ` + + `causes mismatched dependencies. Use the "${MAKE_CONSISTENT_FLAG_NAME}" flag to update other packages to use ` + `this version, or try specify one of the existing versions (${existingVersionList}).` ); } @@ -380,11 +420,11 @@ export class PackageJsonUpdater { const allPackageUpdates: IUpdateProjectOptions[] = []; - for (const project of projects) { + for (const project of subspaceProjects) { const currentProjectUpdate: IUpdateProjectOptions = { project: new VersionMismatchFinderProject(project), dependenciesToAddOrUpdateOrRemove: dependenciesToAddOrUpdate, - dependencyType: devDependency ? DependencyType.Dev : undefined + dependencyType: devDependency ? DependencyType.Dev : peerDependency ? DependencyType.Peer : undefined }; this.updateProject(currentProjectUpdate); @@ -395,7 +435,8 @@ export class PackageJsonUpdater { const mismatchFinder: VersionMismatchFinder = VersionMismatchFinder.getMismatches( this._rushConfiguration, { - variant: variant + subspace, + variant } ); otherPackageUpdates = this._getUpdates(mismatchFinder, Object.entries(dependenciesToAddOrUpdate)); @@ -490,8 +531,8 @@ export class PackageJsonUpdater { const oldDependencyType: DependencyType | undefined = oldDevDependency ? oldDevDependency.dependencyType : oldDependency - ? oldDependency.dependencyType - : undefined; + ? oldDependency.dependencyType + : undefined; dependencyType = dependencyType || oldDependencyType || DependencyType.Regular; @@ -527,17 +568,18 @@ export class PackageJsonUpdater { * @param rangeStyle - if this version is selected by querying registry, then this range specifier is prepended to * the selected version. */ - private async _getNormalizedVersionSpec( + private async _getNormalizedVersionSpecAsync( projects: RushConfigurationProject[], packageName: string, initialSpec: string | undefined, implicitlyPreferredVersion: string | undefined, explicitlyPreferredVersion: string | undefined, - rangeStyle: SemVerStyle + rangeStyle: SemVerStyle, + ensureConsistentVersions: boolean | undefined ): Promise { - this._terminal.writeLine(colors.gray(`Determining new version for dependency: ${packageName}`)); + this._terminal.writeLine(Colorize.gray(`Determining new version for dependency: ${packageName}`)); if (initialSpec) { - this._terminal.writeLine(`Specified version selector: ${colors.cyan(initialSpec)}`); + this._terminal.writeLine(`Specified version selector: ${Colorize.cyan(initialSpec)}`); } else { this._terminal.writeLine( `No version selector was specified, so the version will be determined automatically.` @@ -550,9 +592,9 @@ export class PackageJsonUpdater { if (initialSpec) { if (initialSpec === implicitlyPreferredVersion) { this._terminal.writeLine( - colors.green('Assigning "') + - colors.cyan(initialSpec) + - colors.green( + Colorize.green('Assigning "') + + Colorize.cyan(initialSpec) + + Colorize.green( `" for "${packageName}" because it matches what other projects are using in this repo.` ) ); @@ -561,9 +603,9 @@ export class PackageJsonUpdater { if (initialSpec === explicitlyPreferredVersion) { this._terminal.writeLine( - colors.green('Assigning "') + - colors.cyan(initialSpec) + - colors.green( + Colorize.green('Assigning "') + + Colorize.cyan(initialSpec) + + Colorize.green( `" for "${packageName}" because it is the preferred version listed in ${RushConstants.commonVersionsFilename}.` ) ); @@ -571,10 +613,10 @@ export class PackageJsonUpdater { } } - if (this._rushConfiguration.ensureConsistentVersions && !initialSpec) { + if (ensureConsistentVersions && !initialSpec) { if (implicitlyPreferredVersion) { this._terminal.writeLine( - `Assigning the version "${colors.cyan(implicitlyPreferredVersion)}" for "${packageName}" ` + + `Assigning the version "${Colorize.cyan(implicitlyPreferredVersion)}" for "${packageName}" ` + 'because it is already used by other projects in this repo.' ); return implicitlyPreferredVersion; @@ -582,14 +624,14 @@ export class PackageJsonUpdater { if (explicitlyPreferredVersion) { this._terminal.writeLine( - `Assigning the version "${colors.cyan(explicitlyPreferredVersion)}" for "${packageName}" ` + + `Assigning the version "${Colorize.cyan(explicitlyPreferredVersion)}" for "${packageName}" ` + `because it is the preferred version listed in ${RushConstants.commonVersionsFilename}.` ); return explicitlyPreferredVersion; } } - await InstallHelpers.ensureLocalPackageManager( + await InstallHelpers.ensureLocalPackageManagerAsync( this._rushConfiguration, this._rushGlobalFolder, RushConstants.defaultMaxInstallAttempts @@ -615,7 +657,7 @@ export class PackageJsonUpdater { let selectedVersionPrefix: string = ''; if (initialSpec && initialSpec !== 'latest') { - this._terminal.writeLine(colors.gray('Finding versions that satisfy the selector: ') + initialSpec); + this._terminal.writeLine(Colorize.gray('Finding versions that satisfy the selector: ') + initialSpec); this._terminal.writeLine(); if (localProject !== undefined) { @@ -641,18 +683,18 @@ export class PackageJsonUpdater { } else { this._terminal.writeLine(`Querying registry for all versions of "${packageName}"...`); - let commandArgs: string[]; + let args: string[]; if (this._rushConfiguration.packageManager === 'yarn') { - commandArgs = ['info', packageName, 'versions', '--json']; + args = ['info', packageName, 'versions', '--json']; } else { - commandArgs = ['view', packageName, 'versions', '--json']; + args = ['view', packageName, 'versions', '--json']; } - const allVersions: string = Utilities.executeCommandAndCaptureOutput( - this._rushConfiguration.packageManagerToolFilename, - commandArgs, - this._rushConfiguration.commonTempFolder - ); + const allVersions: string = await Utilities.executeCommandAndCaptureOutputAsync({ + command: this._rushConfiguration.packageManagerToolFilename, + args, + workingDirectory: this._rushConfiguration.commonTempFolder + }); let versionList: string[]; if (this._rushConfiguration.packageManager === 'yarn') { @@ -661,13 +703,13 @@ export class PackageJsonUpdater { versionList = JSON.parse(allVersions); } - this._terminal.writeLine(colors.gray(`Found ${versionList.length} available versions.`)); + this._terminal.writeLine(Colorize.gray(`Found ${versionList.length} available versions.`)); for (const version of versionList) { if (semver.satisfies(version, initialSpec)) { selectedVersion = initialSpec; this._terminal.writeLine( - `Found a version that satisfies ${initialSpec}: ${colors.cyan(version)}` + `Found a version that satisfies ${initialSpec}: ${Colorize.cyan(version)}` ); break; } @@ -693,7 +735,7 @@ export class PackageJsonUpdater { } else { if (!this._rushConfiguration.ensureConsistentVersions) { this._terminal.writeLine( - colors.gray( + Colorize.gray( `The "ensureConsistentVersions" policy is NOT active, so we will assign the latest version.` ) ); @@ -702,23 +744,25 @@ export class PackageJsonUpdater { this._terminal.writeLine(`Querying NPM registry for latest version of "${packageName}"...`); - let commandArgs: string[]; + let args: string[]; if (this._rushConfiguration.packageManager === 'yarn') { - commandArgs = ['info', packageName, 'dist-tags.latest', '--silent']; + args = ['info', packageName, 'dist-tags.latest', '--silent']; } else { - commandArgs = ['view', `${packageName}@latest`, 'version']; + args = ['view', `${packageName}@latest`, 'version']; } - selectedVersion = Utilities.executeCommandAndCaptureOutput( - this._rushConfiguration.packageManagerToolFilename, - commandArgs, - this._rushConfiguration.commonTempFolder + selectedVersion = ( + await Utilities.executeCommandAndCaptureOutputAsync({ + command: this._rushConfiguration.packageManagerToolFilename, + args, + workingDirectory: this._rushConfiguration.commonTempFolder + }) ).trim(); } this._terminal.writeLine(); - this._terminal.writeLine(`Found latest version: ${colors.cyan(selectedVersion)}`); + this._terminal.writeLine(`Found latest version: ${Colorize.cyan(selectedVersion)}`); } this._terminal.writeLine(); @@ -754,7 +798,7 @@ export class PackageJsonUpdater { const normalizedVersion: string = selectedVersionPrefix + selectedVersion; this._terminal.writeLine( - colors.gray(`Assigning version "${normalizedVersion}" for "${packageName}"${reasonForModification}.`) + Colorize.gray(`Assigning version "${normalizedVersion}" for "${packageName}"${reasonForModification}.`) ); return normalizedVersion; } @@ -826,7 +870,8 @@ export class PackageJsonUpdater { if (project === foundProject) { throw new Error( 'Unable to add a project as a dependency of itself unless the dependency is listed as a cyclic dependency ' + - `in rush.json. This command attempted to add "${foundProject.packageName}" as a dependency of itself.` + `in ${RushConstants.rushJsonFilename}. This command attempted to add "${foundProject.packageName}" ` + + `as a dependency of itself.` ); } @@ -860,7 +905,7 @@ export class PackageJsonUpdater { } } - private _normalizeDepsToUpgrade(deps: NpmCheck.INpmCheckPackage[]): IPackageForRushAdd[] { + private _normalizeDepsToUpgrade(deps: INpmCheckPackageSummary[]): IPackageForRushAdd[] { return deps.map((dep) => { return { packageName: dep.moduleName, diff --git a/libraries/rush-lib/src/logic/PackageJsonUpdaterTypes.ts b/libraries/rush-lib/src/logic/PackageJsonUpdaterTypes.ts index a9807bcb1bb..bdc9ee6b285 100644 --- a/libraries/rush-lib/src/logic/PackageJsonUpdaterTypes.ts +++ b/libraries/rush-lib/src/logic/PackageJsonUpdaterTypes.ts @@ -6,7 +6,7 @@ import type { RushConfigurationProject } from '../api/RushConfigurationProject'; /** * The type of SemVer range specifier that is prepended to the version */ -export const enum SemVerStyle { +export enum SemVerStyle { Exact = 'exact', Caret = 'caret', Tilde = 'tilde', @@ -56,7 +56,7 @@ export interface IPackageJsonUpdaterRushBaseUpdateOptions { /** * The variant to consider when performing installations and validating shrinkwrap updates. */ - variant?: string | undefined; + variant: string | undefined | undefined; } /** @@ -67,6 +67,10 @@ export interface IPackageJsonUpdaterRushAddOptions extends IPackageJsonUpdaterRu * Whether or not this dependency should be added as a devDependency or a regular dependency. */ devDependency: boolean; + /** + * Whether or not this dependency should be added as a peerDependency or a regular dependency. + */ + peerDependency: boolean; /** * If specified, other packages that use this dependency will also have their package.json's updated. */ diff --git a/libraries/rush-lib/src/logic/PackageLookup.ts b/libraries/rush-lib/src/logic/PackageLookup.ts index 342e1b3a725..9b0253fc30c 100644 --- a/libraries/rush-lib/src/logic/PackageLookup.ts +++ b/libraries/rush-lib/src/logic/PackageLookup.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { BasePackage } from './base/BasePackage'; +import type { BasePackage } from './base/BasePackage'; export class PackageLookup { private _packageMap: Map; diff --git a/libraries/rush-lib/src/logic/ProjectChangeAnalyzer.ts b/libraries/rush-lib/src/logic/ProjectChangeAnalyzer.ts index 8b775ebfe97..3dc0fc6ed25 100644 --- a/libraries/rush-lib/src/logic/ProjectChangeAnalyzer.ts +++ b/libraries/rush-lib/src/logic/ProjectChangeAnalyzer.ts @@ -1,27 +1,36 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as crypto from 'crypto'; -import ignore, { Ignore } from 'ignore'; +import * as path from 'node:path'; +import ignore, { type Ignore } from 'ignore'; + +import type { IReadonlyLookupByPath, LookupByPath, IPrefixMatch } from '@rushstack/lookup-by-path'; +import { Path, FileSystem, Async, AlreadyReportedError, Sort, JsonFile } from '@rushstack/node-core-library'; import { getRepoChanges, getRepoRoot, - getRepoStateAsync, - IFileDiffStatus + getDetailedRepoStateAsync, + hashFilesAsync, + type IFileDiffStatus } from '@rushstack/package-deps-hash'; -import { Path, FileSystem, ITerminal, Async } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; -import { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfiguration } from '../api/RushConfiguration'; +import type { Subspace } from '../api/Subspace'; import { RushProjectConfiguration } from '../api/RushProjectConfiguration'; -import { Git } from './Git'; +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; import { BaseProjectShrinkwrapFile } from './base/BaseProjectShrinkwrapFile'; -import { RushConfigurationProject } from '../api/RushConfigurationProject'; -import { RushConstants } from './RushConstants'; -import { LookupByPath } from './LookupByPath'; import { PnpmShrinkwrapFile } from './pnpm/PnpmShrinkwrapFile'; -import { UNINITIALIZED } from '../utilities/Utilities'; +import { Git } from './Git'; +import { DependencySpecifier, DependencySpecifierType } from './DependencySpecifier'; +import type { IPnpmOptionsJson, PnpmOptionsConfiguration } from './pnpm/PnpmOptionsConfiguration'; +import { + type IInputsSnapshotProjectMetadata, + type IInputsSnapshot, + InputsSnapshot, + type GetInputsSnapshotAsyncFn +} from './incremental/InputsSnapshot'; /** * @beta @@ -30,6 +39,7 @@ export interface IGetChangedProjectsOptions { targetBranchName: string; terminal: ITerminal; shouldFetch?: boolean; + variant?: string; /** * If set to `true`, consider a project's external dependency installation layout as defined in the @@ -42,12 +52,16 @@ export interface IGetChangedProjectsOptions { * and exclude matched files from change detection. */ enableFiltering: boolean; -} -interface IGitState { - gitPath: string; - hashes: Map; - rootDir: string; + /** + * If set to `true`, excludes projects where the only changes are: + * - A version-only change to `package.json` (only the "version" field differs) + * - Changes to `CHANGELOG.md` and/or `CHANGELOG.json` files + * + * This prevents `rush version --bump` from triggering `rush change --verify` to request change files + * for the version bumps and changelog updates it creates. + */ + excludeVersionOnlyChanges?: boolean; } /** @@ -63,13 +77,6 @@ export interface IRawRepoState { * @beta */ export class ProjectChangeAnalyzer { - /** - * UNINITIALIZED === we haven't looked - * undefined === data isn't available (i.e. - git isn't present) - */ - private _data: IRawRepoState | UNINITIALIZED | undefined = UNINITIALIZED; - private readonly _filteredData: Map> = new Map(); - private readonly _projectStateCache: Map = new Map(); private readonly _rushConfiguration: RushConfiguration; private readonly _git: Git; @@ -79,313 +86,416 @@ export class ProjectChangeAnalyzer { } /** - * Try to get a list of the specified project's dependencies and their hashes. - * - * @remarks - * If the data can't be generated (i.e. - if Git is not present) this returns undefined. - * - * @internal + * Gets a list of projects that have changed in the current state of the repo + * when compared to the specified branch, optionally taking the shrinkwrap and settings in + * the rush-project.json file into consideration. */ - public async _tryGetProjectDependenciesAsync( - project: RushConfigurationProject, - terminal: ITerminal - ): Promise | undefined> { - // Check the cache for any existing data - let filteredProjectData: Map | undefined = this._filteredData.get(project); - if (filteredProjectData) { - return filteredProjectData; - } + public async getChangedProjectsAsync( + options: IGetChangedProjectsOptions + ): Promise> { + const { _rushConfiguration: rushConfiguration } = this; - const data: IRawRepoState | undefined = await this._ensureInitializedAsync(terminal); + const { + targetBranchName, + terminal, + includeExternalDependencies, + enableFiltering, + shouldFetch, + variant, + excludeVersionOnlyChanges + } = options; - if (!data) { - return undefined; - } + const gitPath: string = this._git.getGitPathOrThrow(); + const repoRoot: string = getRepoRoot(rushConfiguration.rushJsonFolder); - const { projectState, rootDir } = data; + // if the given targetBranchName is a commit, we assume it is the merge base + const isTargetBranchACommit: boolean = await this._git.determineIfRefIsACommitAsync(targetBranchName); + const mergeCommit: string = isTargetBranchACommit + ? targetBranchName + : await this._git.getMergeBaseAsync(targetBranchName, terminal, shouldFetch); - if (projectState === undefined) { - return undefined; - } + const changedFiles: Map = getRepoChanges(repoRoot, mergeCommit, gitPath); + const lookup: LookupByPath = + rushConfiguration.getProjectLookupForRoot(repoRoot); + const changesByProject: Map< + RushConfigurationProject, + Map + > = this.getChangesByProject(lookup, changedFiles); - const unfilteredProjectData: Map | undefined = projectState.get(project); - if (!unfilteredProjectData) { - throw new Error(`Project "${project.packageName}" does not exist in the current Rush configuration.`); - } + const changedProjects: Set = new Set(); - filteredProjectData = await this._filterProjectDataAsync( - project, - unfilteredProjectData, - rootDir, - terminal - ); + await Async.forEachAsync( + changesByProject, + async ([project, projectChanges]) => { + const filteredChanges: Map = enableFiltering + ? await this._filterProjectDataAsync(project, projectChanges, repoRoot, terminal) + : projectChanges; - this._filteredData.set(project, filteredProjectData); - return filteredProjectData; - } + // Skip if no changes + if (filteredChanges.size === 0) { + return; + } - /** - * @internal - */ - public async _ensureInitializedAsync(terminal: ITerminal): Promise { - if (this._data === UNINITIALIZED) { - this._data = await this._getDataAsync(terminal); - } + // If excludeVersionOnlyChanges is not enabled, include the project + if (!excludeVersionOnlyChanges) { + changedProjects.add(project); + return; + } - return this._data; - } + // Filter out package.json with version-only changes, CHANGELOG.md, and CHANGELOG.json + for (const [filePath, diffStatus] of filteredChanges) { + // Use lookup to find the project-relative path + const match: IPrefixMatch | undefined = + lookup.findLongestPrefixMatch(filePath); + if (!match) { + // This should be unreachable as projectChanges contains files where match.value === project + changedProjects.add(project); + return; + } - /** - * The project state hash is calculated in the following way: - * - Project dependencies are collected (see ProjectChangeAnalyzer.getPackageDeps) - * - If project dependencies cannot be collected (i.e. - if Git isn't available), - * this function returns `undefined` - * - The (path separator normalized) repo-root-relative dependencies' file paths are sorted - * - A SHA1 hash is created and each (sorted) file path is fed into the hash and then its - * Git SHA is fed into the hash - * - A hex digest of the hash is returned - * - * @internal - */ - public async _tryGetProjectStateHashAsync( - project: RushConfigurationProject, - terminal: ITerminal - ): Promise { - let projectState: string | undefined = this._projectStateCache.get(project); - if (!projectState) { - const packageDeps: Map | undefined = await this._tryGetProjectDependenciesAsync( - project, - terminal - ); + const projectRelativePath: string = filePath.slice(match.index); - if (!packageDeps) { - return undefined; - } else { - const sortedPackageDepsFiles: string[] = Array.from(packageDeps.keys()).sort(); - const hash: crypto.Hash = crypto.createHash('sha1'); - for (const packageDepsFile of sortedPackageDepsFiles) { - hash.update(packageDepsFile); - hash.update(RushConstants.hashDelimiter); - hash.update(packageDeps.get(packageDepsFile)!); - hash.update(RushConstants.hashDelimiter); + // Skip CHANGELOG.md and CHANGELOG.json files at project root + if (projectRelativePath === '/CHANGELOG.md' || projectRelativePath === '/CHANGELOG.json') { + continue; + } + + // Check if this is package.json at project root with version-only changes + if (projectRelativePath === '/package.json') { + const isVersionOnlyChange: boolean = await isVersionOnlyChangeAsync( + diffStatus, + repoRoot, + this._git + ); + if (isVersionOnlyChange) { + continue; // Skip version-only package.json changes + } + } + + // Found a non-excluded change + changedProjects.add(project); + break; } + }, + { concurrency: 10 } + ); - projectState = hash.digest('hex'); - this._projectStateCache.set(project, projectState); + // Detect per-subspace changes: catalog entries in pnpm-config.json and external dependency lockfiles + const subspaces: Iterable = rushConfiguration.subspacesFeatureEnabled + ? rushConfiguration.subspaces + : [rushConfiguration.defaultSubspace]; + + const variantToUse: string | undefined = includeExternalDependencies + ? (variant ?? (await this._rushConfiguration.getCurrentlyInstalledVariantAsync())) + : undefined; + + await Async.forEachAsync(subspaces, async (subspace: Subspace) => { + const subspaceProjects: RushConfigurationProject[] = subspace.getProjects(); + + // Detect changes to pnpm catalog entries in pnpm-config.json + if (rushConfiguration.isPnpm) { + await this._detectCatalogChangesAsync( + subspace, + rushConfiguration, + changedFiles, + mergeCommit, + repoRoot, + terminal, + changedProjects + ); } - } - return projectState; - } + // External dependency changes are not allowed to be filtered, so add these after filtering + if (includeExternalDependencies) { + // Even though changing the installed version of a nested dependency merits a change file, + // ignore lockfile changes for `rush change` for the moment - public async _filterProjectDataAsync( - project: RushConfigurationProject, - unfilteredProjectData: Map, - rootDir: string, - terminal: ITerminal - ): Promise> { - const ignoreMatcher: Ignore | undefined = await this._getIgnoreMatcherForProjectAsync(project, terminal); - if (!ignoreMatcher) { - return unfilteredProjectData; - } + const fullShrinkwrapPath: string = subspace.getCommittedShrinkwrapFilePath(variantToUse); - const projectKey: string = path.relative(rootDir, project.projectFolder); - const projectKeyLength: number = projectKey.length + 1; + const relativeShrinkwrapFilePath: string = Path.convertToSlashes( + path.relative(repoRoot, fullShrinkwrapPath) + ); + const shrinkwrapStatus: IFileDiffStatus | undefined = changedFiles.get(relativeShrinkwrapFilePath); - // At this point, `filePath` is guaranteed to start with `projectKey`, so - // we can safely slice off the first N characters to get the file path relative to the - // root of the project. - const filteredProjectData: Map = new Map(); - for (const [filePath, value] of unfilteredProjectData) { - const relativePath: string = filePath.slice(projectKeyLength); - if (!ignoreMatcher.ignores(relativePath)) { - // Add the file path to the filtered data if it is not ignored - filteredProjectData.set(filePath, value); - } - } - return filteredProjectData; - } + if (shrinkwrapStatus) { + if (shrinkwrapStatus.status !== 'M') { + if (rushConfiguration.subspacesFeatureEnabled) { + terminal.writeWarningLine( + `"${subspace.subspaceName}" subspace lockfile was created or deleted. Assuming all projects are affected.` + ); + } else { + terminal.writeWarningLine( + `Lockfile was created or deleted. Assuming all projects are affected.` + ); + } + for (const project of subspaceProjects) { + changedProjects.add(project); + } + return; + } - /** - * Gets a list of projects that have changed in the current state of the repo - * when compared to the specified branch, optionally taking the shrinkwrap and settings in - * the rush-project.json file into consideration. - */ - public async getChangedProjectsAsync( - options: IGetChangedProjectsOptions - ): Promise> { - const { _rushConfiguration: rushConfiguration } = this; + if (rushConfiguration.isPnpm) { + const subspaceHasNoProjects: boolean = subspaceProjects.length === 0; + const currentShrinkwrap: PnpmShrinkwrapFile | undefined = PnpmShrinkwrapFile.loadFromFile( + fullShrinkwrapPath, + { subspaceHasNoProjects } + ); - const { targetBranchName, terminal, includeExternalDependencies, enableFiltering, shouldFetch } = options; + if (!currentShrinkwrap) { + throw new Error(`Unable to obtain current shrinkwrap file.`); + } - const gitPath: string = this._git.getGitPathOrThrow(); - const repoRoot: string = getRepoRoot(rushConfiguration.rushJsonFolder); + const oldShrinkwrapText: string = await this._git.getBlobContentAsync({ + // : syntax: https://git-scm.com/docs/gitrevisions + blobSpec: `${mergeCommit}:${relativeShrinkwrapFilePath}`, + repositoryRoot: repoRoot + }); + const oldShrinkWrap: PnpmShrinkwrapFile = PnpmShrinkwrapFile.loadFromString(oldShrinkwrapText, { + subspaceHasNoProjects + }); + + for (const project of subspaceProjects) { + if ( + currentShrinkwrap + .getProjectShrinkwrap(project) + .hasChanges(oldShrinkWrap.getProjectShrinkwrap(project)) + ) { + changedProjects.add(project); + } + } + } else { + if (rushConfiguration.subspacesFeatureEnabled) { + terminal.writeLine( + `"${subspace.subspaceName}" subspace lockfile has changed and lockfile content comparison is only supported for pnpm. Assuming all projects are affected.` + ); + } else { + terminal.writeLine( + `Lockfile has changed and lockfile content comparison is only supported for pnpm. Assuming all projects are affected.` + ); + } + subspaceProjects.forEach((project) => changedProjects.add(project)); + return; + } + } + } + }); - const mergeCommit: string = this._git.getMergeBase(targetBranchName, terminal, shouldFetch); + // Sort the set by projectRelativeFolder to avoid race conditions in the results + const sortedChangedProjects: RushConfigurationProject[] = Array.from(changedProjects); + Sort.sortBy(sortedChangedProjects, (project) => project.projectRelativeFolder); - const repoChanges: Map = getRepoChanges(repoRoot, mergeCommit, gitPath); + return new Set(sortedChangedProjects); + } - const changedProjects: Set = new Set(); + protected getChangesByProject( + lookup: LookupByPath, + changedFiles: Map + ): Map> { + return lookup.groupByChild(changedFiles); + } - if (includeExternalDependencies) { - // Even though changing the installed version of a nested dependency merits a change file, - // ignore lockfile changes for `rush change` for the moment + /** + * Gets a snapshot of the input state of the Rush workspace that can be queried for incremental + * build operations and use by the build cache. + * @internal + */ + public async _tryGetSnapshotProviderAsync( + projectConfigurations: ReadonlyMap, + terminal: ITerminal, + projectSelection?: ReadonlySet + ): Promise { + try { + const gitPath: string = this._git.getGitPathOrThrow(); - // Determine the current variant from the link JSON. - const variant: string | undefined = rushConfiguration.currentInstalledVariant; + if (!this._git.isPathUnderGitWorkingTree()) { + terminal.writeLine( + `The Rush monorepo is not in a Git repository. Rush will proceed without incremental build support.` + ); - const fullShrinkwrapPath: string = rushConfiguration.getCommittedShrinkwrapFilename(variant); + return; + } - const shrinkwrapFile: string = Path.convertToSlashes(path.relative(repoRoot, fullShrinkwrapPath)); - const shrinkwrapStatus: IFileDiffStatus | undefined = repoChanges.get(shrinkwrapFile); + const rushConfiguration: RushConfiguration = this._rushConfiguration; - if (shrinkwrapStatus) { - if (shrinkwrapStatus.status !== 'M') { - terminal.writeLine(`Lockfile was created or deleted. Assuming all projects are affected.`); - return new Set(rushConfiguration.projects); - } + // Do not use getGitInfo().root; it is the root of the *primary* worktree, not the *current* one. + const rootDirectory: string = getRepoRoot(rushConfiguration.rushJsonFolder, gitPath); - const { packageManager } = rushConfiguration; + // Load the rush-project.json files for the whole repository + const additionalGlobs: IAdditionalGlob[] = []; - if (packageManager === 'pnpm') { - const currentShrinkwrap: PnpmShrinkwrapFile | undefined = - PnpmShrinkwrapFile.loadFromFile(fullShrinkwrapPath); + const projectMap: Map = new Map(); - if (!currentShrinkwrap) { - throw new Error(`Unable to obtain current shrinkwrap file.`); - } + for (const project of rushConfiguration.projects) { + const projectConfig: RushProjectConfiguration | undefined = projectConfigurations.get(project); - const oldShrinkwrapText: string = this._git.getBlobContent({ - // : syntax: https://git-scm.com/docs/gitrevisions - blobSpec: `${mergeCommit}:${shrinkwrapFile}`, - repositoryRoot: repoRoot - }); - const oldShrinkWrap: PnpmShrinkwrapFile = PnpmShrinkwrapFile.loadFromString(oldShrinkwrapText); - - for (const project of rushConfiguration.projects) { - if ( - currentShrinkwrap - .getProjectShrinkwrap(project) - .hasChanges(oldShrinkWrap.getProjectShrinkwrap(project)) - ) { - changedProjects.add(project); + const additionalFilesByOperationName: Map> = new Map(); + const projectMetadata: IInputsSnapshotProjectMetadata = { + projectConfig, + additionalFilesByOperationName + }; + projectMap.set(project, projectMetadata); + + if (projectConfig) { + const { operationSettingsByOperationName } = projectConfig; + for (const [operationName, { dependsOnAdditionalFiles }] of operationSettingsByOperationName) { + if (dependsOnAdditionalFiles) { + const additionalFilesForOperation: Set = new Set(); + additionalFilesByOperationName.set(operationName, additionalFilesForOperation); + for (const pattern of dependsOnAdditionalFiles) { + additionalGlobs.push({ + project, + operationName, + additionalFilesForOperation, + pattern + }); + } } } - } else { - terminal.writeLine( - `Lockfile has changed and lockfile content comparison is only supported for pnpm. Assuming all projects are affected.` - ); - return new Set(rushConfiguration.projects); } } - } - - const changesByProject: Map> = new Map(); - const lookup: LookupByPath = - rushConfiguration.getProjectLookupForRoot(repoRoot); - for (const [file, diffStatus] of repoChanges) { - const project: RushConfigurationProject | undefined = lookup.findChildPath(file); - if (project) { - if (changedProjects.has(project)) { - // Lockfile changes cannot be ignored via rush-project.json - continue; - } + // Include project shrinkwrap files as part of the computation + const additionalRelativePathsToHash: string[] = []; + const globalAdditionalFiles: string[] = []; + if (rushConfiguration.isPnpm) { + await Async.forEachAsync(rushConfiguration.projects, async (project: RushConfigurationProject) => { + const projectShrinkwrapFilePath: string = BaseProjectShrinkwrapFile.getFilePathForProject(project); + if (!(await FileSystem.existsAsync(projectShrinkwrapFilePath))) { + if (rushConfiguration.subspacesFeatureEnabled) { + return; + } - if (enableFiltering) { - let projectChanges: Map | undefined = changesByProject.get(project); - if (!projectChanges) { - projectChanges = new Map(); - changesByProject.set(project, projectChanges); + throw new Error( + `A project dependency file (${projectShrinkwrapFilePath}) is missing. You may need to run ` + + '"rush install" or "rush update".' + ); } - projectChanges.set(file, diffStatus); - } else { - changedProjects.add(project); - } - } - } - if (enableFiltering) { - // Reading rush-project.json may be problematic if, e.g. rush install has not yet occurred and rigs are in use - await Async.forEachAsync( - changesByProject, - async ([project, projectChanges]) => { - const filteredChanges: Map = await this._filterProjectDataAsync( - project, - projectChanges, - repoRoot, - terminal + const relativeProjectShrinkwrapFilePath: string = Path.convertToSlashes( + path.relative(rootDirectory, projectShrinkwrapFilePath) ); + additionalRelativePathsToHash.push(relativeProjectShrinkwrapFilePath); + }); + } else { + // Add the shrinkwrap file to every project's dependencies + const currentVariant: string | undefined = + await this._rushConfiguration.getCurrentlyInstalledVariantAsync(); + + const shrinkwrapFile: string = Path.convertToSlashes( + path.relative( + rootDirectory, + rushConfiguration.defaultSubspace.getCommittedShrinkwrapFilePath(currentVariant) + ) + ); + + globalAdditionalFiles.push(shrinkwrapFile); + } - if (filteredChanges.size > 0) { - changedProjects.add(project); - } - }, - { concurrency: 10 } - ); - } + const lookupByPath: IReadonlyLookupByPath = + this._rushConfiguration.getProjectLookupForRoot(rootDirectory); - return changedProjects; - } + let filterPath: string[] = []; - private async _getDataAsync(terminal: ITerminal): Promise { - const repoState: IGitState | undefined = await this._getRepoDepsAsync(terminal); - if (!repoState) { - // Mark as resolved, but no data - return { - projectState: undefined, - rootDir: this._rushConfiguration.rushJsonFolder, - rawHashes: new Map() - }; - } + if ( + projectSelection && + projectSelection.size > 0 && + this._rushConfiguration.experimentsConfiguration.configuration.enableSubpathScan + ) { + filterPath = Array.from(projectSelection, ({ projectFolder }) => projectFolder); + } - const lookup: LookupByPath = this._rushConfiguration.getProjectLookupForRoot( - repoState.rootDir - ); - const projectHashDeps: Map> = new Map(); + return async function tryGetSnapshotAsync(): Promise { + try { + const [{ files: hashes, symlinks, hasUncommittedChanges }, additionalFiles] = await Promise.all([ + getDetailedRepoStateAsync(rootDirectory, additionalRelativePathsToHash, gitPath, filterPath), + getAdditionalFilesFromRushProjectConfigurationAsync( + additionalGlobs, + lookupByPath, + rootDirectory, + terminal + ) + ]); + + if (symlinks.size > 0) { + terminal.writeWarningLine( + `Warning: Detected ${symlinks.size} Git-tracked symlinks in the repository. ` + + `These will be ignored by the change detection engine.` + ); + } - for (const project of this._rushConfiguration.projects) { - projectHashDeps.set(project, new Map()); - } + for (const file of additionalFiles) { + if (hashes.has(file) || symlinks.has(file)) { + additionalFiles.delete(file); + } + } - const { hashes: repoDeps, rootDir } = repoState; + const additionalHashes: Map = new Map( + await hashFilesAsync(rootDirectory, additionalFiles, gitPath) + ); - // Currently, only pnpm handles project shrinkwraps - if (this._rushConfiguration.packageManager !== 'pnpm') { - // Determine the current variant from the link JSON. - const variant: string | undefined = this._rushConfiguration.currentInstalledVariant; + return new InputsSnapshot({ + additionalHashes, + globalAdditionalFiles, + hashes, + hasUncommittedChanges, + lookupByPath, + projectMap, + rootDir: rootDirectory + }); + } catch (e) { + // If getRepoState fails, don't fail the whole build. Treat this case as if we don't know anything about + // the state of the files in the repo. This can happen if the environment doesn't have Git. + terminal.writeWarningLine( + `Error calculating the state of the repo. (inner error: ${ + e.stack ?? e.message ?? e + }). Continuing without diffing files.` + ); - // Add the shrinkwrap file to every project's dependencies - const shrinkwrapFile: string = Path.convertToSlashes( - path.relative(rootDir, this._rushConfiguration.getCommittedShrinkwrapFilename(variant)) + return; + } + }; + } catch (e) { + // If getRepoState fails, don't fail the whole build. Treat this case as if we don't know anything about + // the state of the files in the repo. This can happen if the environment doesn't have Git. + terminal.writeWarningLine( + `Error calculating the state of the repo. (inner error: ${ + e.stack ?? e.message ?? e + }). Continuing without diffing files.` ); - const shrinkwrapHash: string | undefined = repoDeps.get(shrinkwrapFile); + return; + } + } - for (const projectDeps of projectHashDeps.values()) { - if (shrinkwrapHash) { - projectDeps.set(shrinkwrapFile, shrinkwrapHash); - } - } + /** + * @internal + */ + public async _filterProjectDataAsync( + project: RushConfigurationProject, + unfilteredProjectData: Map, + rootDir: string, + terminal: ITerminal + ): Promise> { + const ignoreMatcher: Ignore | undefined = await this._getIgnoreMatcherForProjectAsync(project, terminal); + if (!ignoreMatcher) { + return unfilteredProjectData; } - // Sort each project folder into its own package deps hash - for (const [filePath, fileHash] of repoDeps) { - // lookups in findChildPath are O(K) - // K being the maximum folder depth of any project in rush.json (usually on the order of 3) - const owningProject: RushConfigurationProject | undefined = lookup.findChildPath(filePath); + const projectKey: string = path.relative(rootDir, project.projectFolder); + const projectKeyLength: number = projectKey.length + 1; - if (owningProject) { - const owningProjectHashDeps: Map = projectHashDeps.get(owningProject)!; - owningProjectHashDeps.set(filePath, fileHash); + // At this point, `filePath` is guaranteed to start with `projectKey`, so + // we can safely slice off the first N characters to get the file path relative to the + // root of the project. + const filteredProjectData: Map = new Map(); + for (const [filePath, value] of unfilteredProjectData) { + const relativePath: string = filePath.slice(projectKeyLength); + if (!ignoreMatcher.ignores(relativePath)) { + // Add the file path to the filtered data if it is not ignored + filteredProjectData.set(filePath, value); } } - - return { - projectState: projectHashDeps, - rootDir, - rawHashes: repoState.hashes - }; + return filteredProjectData; } private async _getIgnoreMatcherForProjectAsync( @@ -402,58 +512,260 @@ export class ProjectChangeAnalyzer { } } - private async _getRepoDepsAsync(terminal: ITerminal): Promise { + /** + * Detects changes to pnpm catalog entries in a subspace's pnpm-config.json and marks + * affected projects as changed. + */ + private async _detectCatalogChangesAsync( + subspace: Subspace, + rushConfiguration: RushConfiguration, + changedFiles: Map, + mergeCommit: string, + repoRoot: string, + terminal: ITerminal, + changedProjects: Set + ): Promise { + const pnpmOptions: PnpmOptionsConfiguration | undefined = subspace.getPnpmOptions(); + // Default to an empty object if no global catalogs are configured, handle case of globalCatalogs being deleted + const currentCatalogs: Record> = pnpmOptions?.globalCatalogs ?? {}; + + const pnpmConfigRelativePath: string = Path.convertToSlashes( + path.relative(repoRoot, subspace.getPnpmConfigFilePath()) + ); + + if (!changedFiles.has(pnpmConfigRelativePath)) { + return; + } + + // Determine which specific packages changed within each catalog namespace + // Maps catalogNamespace (e.g. "default", "react17") → Set of changed package names + let oldCatalogs: Record> | undefined; try { - const gitPath: string = this._git.getGitPathOrThrow(); + const oldPnpmConfigText: string = await this._git.getBlobContentAsync({ + blobSpec: `${mergeCommit}:${pnpmConfigRelativePath}`, + repositoryRoot: repoRoot + }); + const oldPnpmConfig: IPnpmOptionsJson = JsonFile.parseString(oldPnpmConfigText); + oldCatalogs = oldPnpmConfig.globalCatalogs ?? {}; + } catch { + // Old file didn't exist or was unparseable — treat all packages in all current catalogs as changed + if (rushConfiguration.subspacesFeatureEnabled) { + terminal.writeWarningLine( + `"${subspace.subspaceName}" subspace pnpm-config.json was created or unparseable. Assuming all projects are affected.` + ); + } else { + terminal.writeWarningLine( + `pnpm-config.json was created or unparseable. Assuming all projects are affected.` + ); + } + } - if (this._git.isPathUnderGitWorkingTree()) { - // Do not use getGitInfo().root; it is the root of the *primary* worktree, not the *current* one. - const rootDir: string = getRepoRoot(this._rushConfiguration.rushJsonFolder, gitPath); - // Load the package deps hash for the whole repository - // Include project shrinkwrap files as part of the computation - const additionalFilesToHash: string[] = []; - - if (this._rushConfiguration.packageManager === 'pnpm') { - const absoluteFilePathsToCheck: string[] = []; - - for (const project of this._rushConfiguration.projects) { - const projectShrinkwrapFilePath: string = - BaseProjectShrinkwrapFile.getFilePathForProject(project); - absoluteFilePathsToCheck.push(projectShrinkwrapFilePath); - const relativeProjectShrinkwrapFilePath: string = Path.convertToSlashes( - path.relative(rootDir, projectShrinkwrapFilePath) - ); + const changedCatalogPackages: Map> = new Map(); + const currentCatalogEntries: Map> = new Map( + Object.entries(currentCatalogs) + ); - additionalFilesToHash.push(relativeProjectShrinkwrapFilePath); + if (oldCatalogs === undefined) { + // Could not load old catalogs — treat all packages in all current catalogs as changed + for (const [catalogName, packages] of currentCatalogEntries) { + changedCatalogPackages.set(catalogName, new Set(Object.keys(packages))); + } + } else { + // Check current catalogs for new or modified package entries + for (const [catalogName, packages] of currentCatalogEntries) { + const oldPackages: Record | undefined = oldCatalogs[catalogName]; + if (!oldPackages) { + // Entire catalog is new — all packages in it are changed + changedCatalogPackages.set(catalogName, new Set(Object.keys(packages))); + continue; + } + const changedPackages: Set = new Set(); + for (const [pkgName, version] of Object.entries(packages)) { + if (oldPackages[pkgName] !== version) { + changedPackages.add(pkgName); + } + } + // Check for packages that were removed from this catalog + for (const pkgName of Object.keys(oldPackages)) { + if (!Object.prototype.hasOwnProperty.call(packages, pkgName)) { + changedPackages.add(pkgName); } + } + if (changedPackages.size > 0) { + changedCatalogPackages.set(catalogName, changedPackages); + } + } - await Async.forEachAsync(absoluteFilePathsToCheck, async (filePath: string) => { - if (!(await FileSystem.existsAsync(filePath))) { - throw new Error( - `A project dependency file (${filePath}) is missing. You may need to run ` + - '"rush install" or "rush update".' - ); + // Check for catalogs that were entirely removed + for (const [catalogName, oldPackages] of Object.entries(oldCatalogs)) { + if (!Object.prototype.hasOwnProperty.call(currentCatalogs, catalogName)) { + changedCatalogPackages.set(catalogName, new Set(Object.keys(oldPackages))); + } + } + } + + if (changedCatalogPackages.size > 0) { + // Check each project in the subspace to see if it depends on a changed catalog package + const subspaceProjects: RushConfigurationProject[] = subspace.getProjects(); + subspaceProjects.forEach((project) => { + const { dependencies, devDependencies, optionalDependencies, peerDependencies } = project.packageJson; + const allDependencies: Set<[string, string]> = new Set( + [dependencies, devDependencies, optionalDependencies, peerDependencies].flatMap((deps) => + Object.entries(deps ?? {}) + ) + ); + + for (const [depName, depVersion] of allDependencies) { + const specifier: DependencySpecifier = DependencySpecifier.parseWithCache(depName, depVersion); + if (specifier.specifierType === DependencySpecifierType.Catalog) { + // versionSpecifier holds the catalog name (empty string for "catalog:") + const catalogName: string = specifier.versionSpecifier || 'default'; + const changedPkgs: Set | undefined = changedCatalogPackages.get(catalogName); + if (changedPkgs?.has(depName)) { + changedProjects.add(project); + return; } - }); + } } + }); + } + } +} - const hashes: Map = await getRepoStateAsync(rootDir, additionalFilesToHash, gitPath); - return { - gitPath, - hashes, - rootDir - }; +/** + * Checks if a diff represents a version-only change to package.json. + */ +async function isVersionOnlyChangeAsync( + diffStatus: IFileDiffStatus, + repoRoot: string, + git: Git +): Promise { + try { + // Only check modified files, not additions or deletions + if (diffStatus.status !== 'M') { + return false; + } + + // Get both versions of package.json from Git in parallel + const [oldPackageJsonContent, currentPackageJsonContent] = await Promise.all([ + git.getBlobContentAsync({ + blobSpec: diffStatus.oldhash, + repositoryRoot: repoRoot + }), + git.getBlobContentAsync({ + blobSpec: diffStatus.newhash, + repositoryRoot: repoRoot + }) + ]); + + return isPackageJsonVersionOnlyChange(oldPackageJsonContent, currentPackageJsonContent); + } catch (error) { + // If we can't read the file or parse it, assume it's not a version-only change + return false; + } +} + +interface IAdditionalGlob { + project: RushConfigurationProject; + operationName: string; + additionalFilesForOperation: Set; + pattern: string; +} + +async function getAdditionalFilesFromRushProjectConfigurationAsync( + additionalGlobs: IAdditionalGlob[], + rootRelativeLookupByPath: IReadonlyLookupByPath, + rootDirectory: string, + terminal: ITerminal +): Promise> { + const additionalFilesFromRushProjectConfiguration: Set = new Set(); + + if (!additionalGlobs.length) { + return additionalFilesFromRushProjectConfiguration; + } + + const { default: glob } = await import('fast-glob'); + await Async.forEachAsync(additionalGlobs, async (item: IAdditionalGlob) => { + const { project, operationName, additionalFilesForOperation, pattern } = item; + const matches: string[] = await glob(pattern, { + cwd: project.projectFolder, + onlyFiles: true, + // We want to keep path's type unchanged, + // i.e. if the pattern was a relative path, then matched paths should also be relative paths + // if the pattern was an absolute path, then matched paths should also be absolute paths + // + // We are doing this because these paths are going to be used to calculate operation state hashes and some users + // might choose to depend on global files (e.g. `/etc/os-release`) and some might choose to depend on local non-project files + // (e.g. `../path/to/workspace/file`) + // + // In both cases we want that path to the resource to be the same on all machines, + // regardless of what is the current working directory. + // + // That being said, we want to keep `absolute` options here as false: + absolute: false + }); + + for (const match of matches) { + // The glob result is relative to the project folder, but we want it to be relative to the repo root + const rootRelativeFilePath: string = Path.convertToSlashes( + path.relative(rootDirectory, path.resolve(project.projectFolder, match)) + ); + + if (rootRelativeFilePath.startsWith('../')) { + // The target file is outside of the Git tree, use the original result of the match. + additionalFilesFromRushProjectConfiguration.add(match); + additionalFilesForOperation.add(match); } else { - return undefined; + // The target file is inside of the Git tree, find out if it is in a Rush project. + const projectMatch: RushConfigurationProject | undefined = + rootRelativeLookupByPath.findChildPath(rootRelativeFilePath); + if (projectMatch && projectMatch !== project) { + terminal.writeErrorLine( + `In project "${project.packageName}" ("${project.projectRelativeFolder}"), ` + + `config for operation "${operationName}" specifies a glob "${pattern}" that selects a file "${rootRelativeFilePath}" in a different workspace project ` + + `"${projectMatch.packageName}" ("${projectMatch.projectRelativeFolder}"). ` + + `This is forbidden. The "dependsOnAdditionalFiles" property of "rush-project.json" may only be used to refer to non-workspace files, non-project files, ` + + `or untracked files in the current project. To depend on files in another workspace project, use "devDependencies" in "package.json".` + ); + throw new AlreadyReportedError(); + } + additionalFilesForOperation.add(rootRelativeFilePath); + additionalFilesFromRushProjectConfiguration.add(rootRelativeFilePath); } - } catch (e) { - // If getPackageDeps fails, don't fail the whole build. Treat this case as if we don't know anything about - // the state of the files in the repo. This can happen if the environment doesn't have Git. - terminal.writeWarningLine( - `Error calculating the state of the repo. (inner error: ${e}). Continuing without diffing files.` - ); + } + }); + + return additionalFilesFromRushProjectConfiguration; +} - return undefined; +/** + * Compares two package.json file contents and determines if the only difference is the "version" field. + * @param oldPackageJsonContent - The old package.json content as a string + * @param newPackageJsonContent - The new package.json content as a string + * @returns true if the only difference is the version field, false otherwise + */ +export function isPackageJsonVersionOnlyChange( + oldPackageJsonContent: string, + newPackageJsonContent: string +): boolean { + try { + // Parse both versions - use specific type since we only care about version field + const oldPackageJson: { version?: string } = JSON.parse(oldPackageJsonContent); + const newPackageJson: { version?: string } = JSON.parse(newPackageJsonContent); + + // Ensure both have a version field + if (!oldPackageJson.version || !newPackageJson.version) { + return false; } + + // Remove the version field from both (no need to clone, these are fresh objects from JSON.parse) + oldPackageJson.version = undefined; + newPackageJson.version = undefined; + + // Compare the objects without the version field + return JSON.stringify(oldPackageJson) === JSON.stringify(newPackageJson); + } catch (error) { + // If we can't parse the JSON, assume it's not a version-only change + return false; } } diff --git a/libraries/rush-lib/src/logic/ProjectCommandSet.ts b/libraries/rush-lib/src/logic/ProjectCommandSet.ts index b02ff60762e..1070f31b5f8 100644 --- a/libraries/rush-lib/src/logic/ProjectCommandSet.ts +++ b/libraries/rush-lib/src/logic/ProjectCommandSet.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IPackageJson, IPackageJsonScriptTable } from '@rushstack/node-core-library'; +import type { IPackageJson, IPackageJsonScriptTable } from '@rushstack/node-core-library'; /** * Parses the "scripts" section from package.json and provides support for executing scripts. diff --git a/libraries/rush-lib/src/logic/ProjectImpactGraphGenerator.ts b/libraries/rush-lib/src/logic/ProjectImpactGraphGenerator.ts new file mode 100644 index 00000000000..5f23e9d6339 --- /dev/null +++ b/libraries/rush-lib/src/logic/ProjectImpactGraphGenerator.ts @@ -0,0 +1,158 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import yaml from 'js-yaml'; + +import { FileSystem, Text, Async } from '@rushstack/node-core-library'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; + +import type { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; +import { Stopwatch } from '../utilities/Stopwatch'; +import { RushConstants } from './RushConstants'; + +/** + * Project property configuration + */ +export interface IProjectImpactGraphProjectConfiguration { + includedGlobs: string[]; + excludedGlobs?: string[]; + dependentProjects: string[]; +} + +/** + * The schema of `project-impact-graph.yaml` + */ +export interface IProjectImpactGraphFile { + globalExcludedGlobs: string[]; + projects: Record; +} + +/** + * Default global excluded globs + * Only used if the `/.mergequeueignore` does not exist + */ +const DEFAULT_GLOBAL_EXCLUDED_GLOBS: string[] = ['common/autoinstallers/**']; + +async function tryReadFileLinesAsync(filePath: string): Promise { + let fileContents: string | undefined; + try { + fileContents = await FileSystem.readFileAsync(filePath); + } catch (error) { + if (!FileSystem.isNotExistError(error)) { + throw error; + } + } + + if (fileContents) { + return Text.convertToLf(fileContents).split('\n'); + } +} + +export class ProjectImpactGraphGenerator { + private readonly _terminal: ITerminal; + + /** + * The Rush configuration + */ + private readonly _rushConfiguration: RushConfiguration; + + /** + * Full path of repository root + */ + private readonly _repositoryRoot: string; + + /** + * Full path to `project-impact-graph.yaml` + */ + private readonly _projectImpactGraphFilePath: string; + + /** + * Get repositoryRoot and load projects within the rush.json + */ + public constructor(terminal: ITerminal, rushConfiguration: RushConfiguration) { + this._terminal = terminal; + this._rushConfiguration = rushConfiguration; + const { rushJsonFolder } = rushConfiguration; + this._repositoryRoot = rushJsonFolder; + this._projectImpactGraphFilePath = `${rushJsonFolder}/${RushConstants.projectImpactGraphFilename}`; + } + + /** + * Load global excluded globs + */ + private async _loadGlobalExcludedGlobsAsync(): Promise { + const filePath: string = `${this._repositoryRoot}/${RushConstants.mergeQueueIgnoreFileName}`; + return await tryReadFileLinesAsync(filePath); + } + + /** + * Load project excluded globs + * @param projectRootRelativePath - project root relative path + */ + private async _tryLoadProjectExcludedGlobsAsync( + projectRootRelativePath: string + ): Promise { + const filePath: string = `${this._repositoryRoot}/${projectRootRelativePath}/${RushConstants.mergeQueueIgnoreFileName}`; + + const globs: string[] | undefined = await tryReadFileLinesAsync(filePath); + if (globs) { + for (let i: number = 0; i < globs.length; i++) { + globs[i] = `${projectRootRelativePath}/${globs[i]}`; + } + + return globs; + } + } + + /** + * Core Logic: generate project-impact-graph.yaml + */ + public async generateAsync(): Promise { + const stopwatch: Stopwatch = Stopwatch.start(); + + const [globalExcludedGlobs = DEFAULT_GLOBAL_EXCLUDED_GLOBS, projectEntries] = await Promise.all([ + this._loadGlobalExcludedGlobsAsync(), + Async.mapAsync( + this._rushConfiguration.projects, + async ({ packageName, consumingProjects, projectRelativeFolder }) => { + const dependentList: string[] = [packageName]; + for (const consumingProject of consumingProjects) { + dependentList.push(consumingProject.packageName); + } + + const projectImpactGraphProjectConfiguration: IProjectImpactGraphProjectConfiguration = { + includedGlobs: [`${projectRelativeFolder}/**`], + dependentProjects: dependentList.sort() + }; + + const projectExcludedGlobs: string[] | undefined = + await this._tryLoadProjectExcludedGlobsAsync(projectRelativeFolder); + if (projectExcludedGlobs) { + projectImpactGraphProjectConfiguration.excludedGlobs = projectExcludedGlobs; + } + + return [packageName, projectImpactGraphProjectConfiguration]; + }, + { concurrency: 50 } + ) + ]); + + projectEntries.sort(([aName], [bName]) => aName.localeCompare(bName)); + const projects: Record = + Object.fromEntries(projectEntries); + const content: IProjectImpactGraphFile = { globalExcludedGlobs, projects }; + await FileSystem.writeFileAsync(this._projectImpactGraphFilePath, yaml.dump(content)); + + stopwatch.stop(); + this._terminal.writeLine(); + this._terminal.writeLine( + Colorize.green(`Generate project impact graph successfully. (${stopwatch.toString()})`) + ); + } + + public async validateAsync(): Promise { + // TODO: More validation other than just existence + return await FileSystem.existsAsync(this._projectImpactGraphFilePath); + } +} diff --git a/libraries/rush-lib/src/logic/ProjectWatcher.ts b/libraries/rush-lib/src/logic/ProjectWatcher.ts index 756b5c3fd15..0e0c9adb877 100644 --- a/libraries/rush-lib/src/logic/ProjectWatcher.ts +++ b/libraries/rush-lib/src/logic/ProjectWatcher.ts @@ -1,24 +1,30 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as fs from 'fs'; -import * as os from 'os'; -import * as readline from 'readline'; -import { once } from 'events'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as readline from 'node:readline'; +import { once } from 'node:events'; + import { getRepoRoot } from '@rushstack/package-deps-hash'; -import { Colors, Path, ITerminal, FileSystemStats, FileSystem } from '@rushstack/node-core-library'; +import { Path } from '@rushstack/node-core-library'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; import { Git } from './Git'; -import { ProjectChangeAnalyzer } from './ProjectChangeAnalyzer'; -import { RushConfiguration } from '../api/RushConfiguration'; -import { RushConfigurationProject } from '../api/RushConfigurationProject'; +import type { IInputsSnapshot } from './incremental/InputsSnapshot'; +import type { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; +import type { IOperationGraph, IOperationGraphIterationOptions } from './operations/IOperationGraph'; +import type { Operation } from './operations/Operation'; +import { OperationStatus } from './operations/OperationStatus'; export interface IProjectWatcherOptions { - debounceMs?: number; + graph: IOperationGraph; + debounceMs: number; rushConfiguration: RushConfiguration; - projectsToWatch: ReadonlySet; terminal: ITerminal; - initialState?: ProjectChangeAnalyzer | undefined; + /** Initial inputs snapshot; required so watcher can enumerate nested folders immediately */ + initialSnapshot: IInputsSnapshot; } export interface IProjectChangeResult { @@ -29,354 +35,443 @@ export interface IProjectChangeResult { /** * Contains the git hashes for all tracked files in the repo */ - state: ProjectChangeAnalyzer; + inputsSnapshot: IInputsSnapshot; } -interface IPathWatchOptions { - recurse: boolean; +export interface IPromptGeneratorFunction { + (isPaused: boolean): Iterable; } +const KEY_QUIT: 'q' = 'q'; +const KEY_ABORT: 'a' = 'a'; +const KEY_INVALIDATE: 'i' = 'i'; +const KEY_CLOSE_RUNNERS: 'x' = 'x'; +const KEY_DEBUG: 'd' = 'd'; +const KEY_VERBOSE: 'v' = 'v'; +const KEY_PAUSE_RESUME: 'w' = 'w'; +const KEY_BUILD: 'b' = 'b'; +const KEY_PARALLELISM_UP: '+' = '+'; +const KEY_PARALLELISM_DOWN: '-' = '-'; + +const KEYBIND_HELP: string = + `[${KEY_QUIT}]quit [${KEY_ABORT}]abort-iteration [${KEY_INVALIDATE}]invalidate ` + + `[${KEY_CLOSE_RUNNERS}]close-runners [${KEY_DEBUG}]debug [${KEY_VERBOSE}]verbose ` + + `[${KEY_PAUSE_RESUME}]pause/resume [${KEY_BUILD}]build [${KEY_PARALLELISM_UP}/${KEY_PARALLELISM_DOWN}]parallelism`; + /** - * This class is for incrementally watching a set of projects in the repository for changes. - * - * We are manually using fs.watch() instead of `chokidar` because all we want from the file system watcher is a boolean - * signal indicating that "at least 1 file in a watched project changed". We then defer to ProjectChangeAnalyzer (which - * is responsible for change detection in all incremental builds) to determine what actually chanaged. + * Watches a set of projects in the repository for file changes and triggers + * rebuild iterations on the operation graph. * - * Calling `waitForChange()` will return a promise that resolves when the package-deps of one or - * more projects differ from the value the previous time it was invoked. The first time will always resolve with the full selection. + * Uses `fs.watch()` rather than `chokidar` because only a boolean "something changed" + * signal is needed; actual change detection is deferred to `getInputsSnapshotAsync`. */ export class ProjectWatcher { private readonly _debounceMs: number; - private readonly _repoRoot: string; private readonly _rushConfiguration: RushConfiguration; - private readonly _projectsToWatch: ReadonlySet; private readonly _terminal: ITerminal; - - private _initialState: ProjectChangeAnalyzer | undefined; - private _previousState: ProjectChangeAnalyzer | undefined; - - private _hasRenderedStatus: boolean; + private readonly _graph: IOperationGraph; + + private _repoRoot: string | undefined; + private _watchers: Map | undefined; + private _closePromises: Promise[] = []; + private _debounceHandle: NodeJS.Timeout | undefined; + private _isWatching: boolean = false; + private _lastStatus: string | undefined; + private _renderedStatusLines: number = 0; + private _lastSnapshot: IInputsSnapshot | undefined; + private _stdinListening: boolean = false; + private _stdinHadRawMode: boolean | undefined; + private _onStdinDataBound: ((chunk: Buffer | string) => void) | undefined; public constructor(options: IProjectWatcherOptions) { - const { debounceMs = 1000, rushConfiguration, projectsToWatch, terminal, initialState } = options; - + const { graph, debounceMs, rushConfiguration, terminal, initialSnapshot } = options; + this._graph = graph; this._debounceMs = debounceMs; this._rushConfiguration = rushConfiguration; - this._projectsToWatch = projectsToWatch; this._terminal = terminal; + this._lastSnapshot = initialSnapshot; // Seed snapshot const gitPath: string = new Git(rushConfiguration).getGitPathOrThrow(); this._repoRoot = Path.convertToSlashes(getRepoRoot(rushConfiguration.rushJsonFolder, gitPath)); - this._initialState = initialState; - this._previousState = initialState; + // Initialize stdin listener early so keybinds are available immediately + this._ensureStdin(); + + // Capture snapshot (if provided) prior to executing next iteration (will replace initial snapshot) + graph.hooks.beforeExecuteIterationAsync.tapPromise( + 'ProjectWatcher', + async ( + records: ReadonlyMap, + iterationOptions: IOperationGraphIterationOptions + ): Promise => { + this.clearStatus(); + this._lastSnapshot = iterationOptions.inputsSnapshot; + await this._stopWatchingAsync(); + } + ); - this._hasRenderedStatus = false; + // Start watching once execution loop enters waiting state + graph.hooks.onIdle.tap('ProjectWatcher', () => { + this._startWatching(); + }); + + // Dispose stdin listener when session aborts + graph.abortController.signal.addEventListener( + 'abort', + () => { + this._disposeStdin(); + }, + { once: true } + ); } /** - * Waits for a change to the package-deps of one or more of the selected projects, since the previous invocation. - * Will return immediately the first time it is invoked, since no state has been recorded. - * If no change is currently present, watches the source tree of all selected projects for file changes. + * Resets the rendered line count so the next status update does not attempt + * to overwrite previously rendered lines. */ - public async waitForChange(onWatchingFiles?: () => void): Promise { - const initialChangeResult: IProjectChangeResult = await this._computeChanged(); - // Ensure that the new state is recorded so that we don't loop infinitely - this._commitChanges(initialChangeResult.state); - if (initialChangeResult.changedProjects.size) { - return initialChangeResult; - } - - const previousState: ProjectChangeAnalyzer = initialChangeResult.state; - const repoRoot: string = Path.convertToSlashes(this._rushConfiguration.rushJsonFolder); - - // Map of path to whether config for the path - const pathsToWatch: Map = new Map(); - - // Node 12 supports the "recursive" parameter to fs.watch only on win32 and OSX - // https://nodejs.org/docs/latest-v12.x/api/fs.html#fs_caveats - const useNativeRecursiveWatch: boolean = os.platform() === 'win32' || os.platform() === 'darwin'; - - if (useNativeRecursiveWatch) { - // Watch the root non-recursively - pathsToWatch.set(repoRoot, { recurse: false }); + public clearStatus(): void { + this._renderedStatusLines = 0; + } - // Watch the rush config folder non-recursively - pathsToWatch.set(Path.convertToSlashes(this._rushConfiguration.commonRushConfigFolder), { - recurse: false - }); + /** + * Re-renders the most recent status line (or a default) in place. + */ + public rerenderStatus(): void { + this._setStatus(this._lastStatus ?? 'Waiting for changes...'); + } - for (const project of this._projectsToWatch) { - // Use recursive watch in individual project folders - pathsToWatch.set(Path.convertToSlashes(project.projectFolder), { recurse: true }); - } - } else { - for (const project of this._projectsToWatch) { - const projectState: Map = (await previousState._tryGetProjectDependenciesAsync( - project, - this._terminal - ))!; - - const prefixLength: number = project.projectFolder.length - repoRoot.length - 1; - // Watch files in the root of the project, or - for (const pathToWatch of ProjectWatcher._enumeratePathsToWatch(projectState.keys(), prefixLength)) { - pathsToWatch.set(`${this._repoRoot}/${pathToWatch}`, { recurse: true }); - } + /** + * Renders the given status message to the terminal, preceded by mode indicators + * and keybind help when stdin is active. Overwrites previously rendered status lines + * when not mid-execution. + */ + private _setStatus(status: string): void { + const graph: IOperationGraph = this._graph; + const isPaused: boolean = graph.pauseNextIteration === true; + const hasScheduledIteration: boolean = graph.hasScheduledIteration; + const modeLabel: string = isPaused ? 'PAUSED' : 'WATCHING'; + const pendingLabel: string = hasScheduledIteration ? ' PENDING' : ''; + const statusLines: string[] = [`[${modeLabel}${pendingLabel}] Watch Status: ${status}`]; + if (this._stdinListening) { + const lines: string[] = []; + // First line: modes + lines.push( + ` debug:${graph.debugMode ? 'on' : 'off'} verbose:${!graph.quietMode ? 'on' : 'off'} parallel:${graph.parallelism}` + ); + // Second line: keybind help kept concise to avoid overwhelming output + lines.push(` keys(active): ${KEYBIND_HELP}`); + statusLines.push(...lines.map((l) => ` ${l}`)); + } + if (graph.status !== OperationStatus.Executing) { + // If rendering during execution, don't try to clean previous output. + if (this._renderedStatusLines > 0) { + readline.cursorTo(process.stdout, 0); + readline.moveCursor(process.stdout, 0, -this._renderedStatusLines); + readline.clearScreenDown(process.stdout); } + this._renderedStatusLines = statusLines.length; } + this._lastStatus = status; + this._terminal.writeLine(Colorize.bold(Colorize.cyan(statusLines.join('\n')))); + } - const watchers: Map = new Map(); - - const watchedResult: IProjectChangeResult = await new Promise( - (resolve: (result: IProjectChangeResult) => void, reject: (err: Error) => void) => { - let timeout: NodeJS.Timeout | undefined; - let terminated: boolean = false; - - const terminal: ITerminal = this._terminal; - - const debounceMs: number = this._debounceMs; - - this._hasRenderedStatus = false; - - const resolveIfChanged = async (): Promise => { - timeout = undefined; - if (terminated) { - return; - } - - try { - this._setStatus(`Evaluating changes to tracked files...`); - const result: IProjectChangeResult = await this._computeChanged(); - this._setStatus(`Finished analyzing.`); - - // Need an async tick to allow for more file system events to be handled - process.nextTick(() => { - if (timeout) { - // If another file has changed, wait for another pass. - this._setStatus(`More file changes detected, aborting.`); - return; - } + /** + * Begins watching the file system for changes in all tracked project folders. + * On platforms without native recursive watch support (Linux), enumerates nested + * folders from the last snapshot to set up individual watchers. + */ + private _startWatching(): void { + if (this._isWatching) { + return; + } + this._isWatching = true; + const sessionAbortSignal: AbortSignal = this._graph.abortController.signal; + const repoRoot: string = Path.convertToSlashes(this._rushConfiguration.rushJsonFolder); + const useNativeRecursiveWatch: boolean = os.platform() === 'win32' || os.platform() === 'darwin'; + const operations: ReadonlySet = this._graph.operations; - this._commitChanges(result.state); + const projectFolders: Set = new Set(); + for (const op of operations) { + projectFolders.add(Path.convertToSlashes(op.associatedProject.projectFolder)); + } - if (result.changedProjects.size) { - terminated = true; - terminal.writeLine(); - resolve(result); - } else { - this._setStatus(`No changes detected to tracked files.`); - } - }); - } catch (err) { - // eslint-disable-next-line require-atomic-updates - terminated = true; - terminal.writeLine(); - reject(err as NodeJS.ErrnoException); - } - }; - - for (const [pathToWatch, { recurse }] of pathsToWatch) { - addWatcher(pathToWatch, recurse); + // Derive nested folder list if on Linux (no native recursive) and snapshot available + let foldersToWatch: Set = new Set(); + if (!useNativeRecursiveWatch && this._lastSnapshot) { + for (const op of operations) { + const { associatedProject: rushProject } = op; + const tracked: ReadonlyMap | undefined = + this._lastSnapshot.getTrackedFileHashesForOperation(rushProject); + if (!tracked) { + continue; } - - if (onWatchingFiles) { - onWatchingFiles(); + const prefixLength: number = rushProject.projectFolder.length - repoRoot.length - 1; + for (const relPrefix of _enumeratePathsToWatch(tracked.keys(), prefixLength)) { + foldersToWatch.add(`${this._repoRoot}/${relPrefix}`); } + } + } + if (!useNativeRecursiveWatch && foldersToWatch.size === 0) { + // Fallback to project roots if snapshot missing + foldersToWatch = projectFolders; + } - function onError(err: Error): void { - if (terminated) { - return; - } - - terminated = true; - terminal.writeLine(); - reject(err); - } + const watchers: Map = (this._watchers = new Map()); - function addWatcher(watchedPath: string, recursive: boolean): void { - if (watchers.has(watchedPath)) { - return; - } - const listener: (event: string, fileName: string) => void = changeListener(watchedPath, recursive); - const watcher: fs.FSWatcher = fs.watch( - watchedPath, - { - encoding: 'utf-8', - recursive: recursive && useNativeRecursiveWatch - }, - listener - ); - watchers.set(watchedPath, watcher); - watcher.on('error', (err) => { + const addWatcher = (watchedPath: string, recursive: boolean): void => { + if (watchers.has(watchedPath)) { + return; + } + try { + const watcher: fs.FSWatcher = fs.watch( + watchedPath, + { + encoding: 'utf-8', + recursive: recursive && useNativeRecursiveWatch, + signal: sessionAbortSignal + }, + (eventType, fileName) => this._onFsEvent(fileName) + ); + watchers.set(watchedPath, watcher); + this._closePromises.push( + once(watcher, 'close').then(() => { watchers.delete(watchedPath); - onError(err); - }); - } - - function innerListener(root: string, recursive: boolean, event: string, fileName: string): void { - try { - if (terminated) { - return; - } - - if (fileName === '.git' || fileName === 'node_modules') { - return; - } - - // Handling for added directories - if (recursive && !useNativeRecursiveWatch) { - const decodedName: string = fileName && fileName.toString(); - const normalizedName: string = decodedName && Path.convertToSlashes(decodedName); - const fullName: string = normalizedName && `${root}/${normalizedName}`; - - if (fullName && !watchers.has(fullName)) { - try { - const stat: FileSystemStats = FileSystem.getStatistics(fullName); - if (stat.isDirectory()) { - addWatcher(fullName, true); - } - } catch (err) { - const code: string | undefined = (err as NodeJS.ErrnoException).code; - - if (code !== 'ENOENT' && code !== 'ENOTDIR') { - throw err; - } - } - } - } - - // Use a timeout to debounce changes, e.g. bulk copying files into the directory while the watcher is running. - if (timeout) { - clearTimeout(timeout); - } - - timeout = setTimeout(resolveIfChanged, debounceMs); - } catch (err) { - terminated = true; - terminal.writeLine(); - reject(err as NodeJS.ErrnoException); - } - } - - function changeListener(root: string, recursive: boolean): (event: string, fileName: string) => void { - return innerListener.bind(0, root, recursive); - } + watcher.removeAllListeners(); + watcher.unref(); + }) + ); + } catch (e) { + this._terminal.writeDebugLine(`Failed to watch path ${watchedPath}: ${(e as Error).message}`); } - ); + }; - const closePromises: Promise[] = []; - for (const [watchedPath, watcher] of watchers) { - closePromises.push( - once(watcher, 'close').then(() => { - watchers.delete(watchedPath); - }) - ); - watcher.close(); + // Always watch repo root and common config + addWatcher(repoRoot, false); + addWatcher(Path.convertToSlashes(this._rushConfiguration.commonRushConfigFolder), false); + if (useNativeRecursiveWatch) { + for (const folder of projectFolders) { + addWatcher(folder, true); + } + } else { + for (const folder of foldersToWatch) { + addWatcher(folder, true); + } } - - await Promise.all(closePromises); - - return watchedResult; + this._setStatus('Waiting for changes...'); } - private _setStatus(status: string): void { - if (this._hasRenderedStatus) { - readline.clearLine(process.stdout, 0); - readline.cursorTo(process.stdout, 0); - } else { - this._hasRenderedStatus = true; + /** + * Closes all active file system watchers and waits for their close events to settle. + */ + private async _stopWatchingAsync(): Promise { + if (!this._isWatching) { + return; + } + this._isWatching = false; + if (this._debounceHandle) { + clearTimeout(this._debounceHandle); + this._debounceHandle = undefined; + } + if (this._watchers) { + for (const watcher of this._watchers.values()) { + watcher.close(); + } } - this._terminal.write(Colors.bold(Colors.cyan(`Watch Status: ${status}`))); + await Promise.all(this._closePromises); + this._closePromises = []; + this._watchers = undefined; + this._terminal.writeDebugLine('ProjectWatcher: watchers stopped'); } /** - * Determines which, if any, projects (within the selection) have new hashes for files that are not in .gitignore + * Handles a raw file system event by debouncing and scheduling an iteration. + * Ignores changes to `.git` and `node_modules`. */ - private async _computeChanged(): Promise { - const state: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(this._rushConfiguration); - - const previousState: ProjectChangeAnalyzer | undefined = this._previousState; - - if (!previousState) { - return { - changedProjects: this._projectsToWatch, - state - }; + private _onFsEvent(fileName: string | null): void { + if (fileName === '.git' || fileName === 'node_modules') { + return; } - - const changedProjects: Set = new Set(); - for (const project of this._projectsToWatch) { - const [previous, current] = await Promise.all([ - previousState._tryGetProjectDependenciesAsync(project, this._terminal), - state._tryGetProjectDependenciesAsync(project, this._terminal) - ]); - - if (ProjectWatcher._haveProjectDepsChanged(previous, current)) { - // May need to detect if the nature of the change will break the process, e.g. changes to package.json - changedProjects.add(project); - } + if (this._debounceHandle) { + clearTimeout(this._debounceHandle); } + this._debounceHandle = setTimeout(() => this._scheduleIteration(), this._debounceMs); + } - return { - changedProjects, - state - }; + /** + * Schedules a new execution iteration on the graph in response to detected file changes. + */ + private _scheduleIteration(): void { + this._setStatus('File change detected. Queuing new iteration...'); + this._graph + .scheduleIterationAsync({}) + .catch((e: unknown) => + this._terminal.writeErrorLine(`Failed to queue iteration: ${(e as Error).message}`) + ); } - private _commitChanges(state: ProjectChangeAnalyzer): void { - this._previousState = state; - if (!this._initialState) { - this._initialState = state; + /** + * Sets up a raw-mode stdin listener so the user can interact with the watch session + * via single-key keybinds. Captures the previous raw-mode state for restoration on dispose. + */ + private _ensureStdin(): void { + if (this._stdinListening || !process.stdin.isTTY) { + return; } + const stdin: NodeJS.ReadStream = process.stdin as NodeJS.ReadStream; + // Node's ReadStream has an undocumented isRaw property when setRawMode has been used. + // Capture it in a type-safe way. + this._stdinHadRawMode = + typeof (stdin as unknown as { isRaw?: boolean }).isRaw === 'boolean' + ? (stdin as unknown as { isRaw?: boolean }).isRaw + : undefined; // capture existing raw state + try { + stdin.setRawMode?.(true); + } catch { + // ignore if cannot set raw mode + } + stdin.resume(); + stdin.setEncoding('utf8'); + const handler = (chunk: Buffer | string): void => this._onStdinData(chunk.toString()); + stdin.on('data', handler); + this._onStdinDataBound = handler; + this._stdinListening = true; } /** - * Tests for inequality of the passed Maps. Order invariant. - * - * @returns `true` if the maps are different, `false` otherwise + * Removes the stdin listener and restores the previous raw-mode state. */ - private static _haveProjectDepsChanged( - prev: Map | undefined, - next: Map | undefined - ): boolean { - if (!prev && !next) { - return false; + private _disposeStdin(): void { + if (!this._stdinListening) { + return; } - - if (!prev || !next) { - return true; + const stdin: NodeJS.ReadStream = process.stdin as NodeJS.ReadStream; + if (this._onStdinDataBound) { + stdin.off('data', this._onStdinDataBound); + this._onStdinDataBound = undefined; } - - if (prev.size !== next.size) { - return true; + try { + stdin.setRawMode?.(!!this._stdinHadRawMode); + } catch { + // ignore } + stdin.unref(); + this._stdinListening = false; + } - for (const [key, value] of prev) { - if (next.get(key) !== value) { - return true; + /** + * Processes a chunk of stdin data, dispatching each character to the appropriate + * keybind action on the operation graph. + */ + private _onStdinData(chunk: string): void { + const graph: IOperationGraph = this._graph; + if (!chunk) return; + for (const ch of chunk) { + // Once aborted, only respond to Ctrl+C (force exit) + if (graph.abortController.signal.aborted) { + if (ch === '\u0003') { + process.exit(1); + } + continue; + } + switch (ch) { + case '\u0003': + case KEY_QUIT: { + this._terminal.writeLine('Aborting watch session... (Ctrl+C to force exit)'); + graph.abortController.abort(); + break; + } + case KEY_ABORT: { + void graph.abortCurrentIterationAsync().then(() => { + this._setStatus('Current iteration aborted'); + }); + break; + } + case KEY_INVALIDATE: { + graph.invalidateOperations(undefined, 'manual-invalidation'); + this._setStatus('All operations invalidated'); + break; + } + case KEY_CLOSE_RUNNERS: { + void graph.closeRunnersAsync().then(() => { + this._setStatus('Closed all runners'); + }); + break; + } + case KEY_DEBUG: { + graph.debugMode = !graph.debugMode; + this._setStatus(`Debug mode ${graph.debugMode ? 'enabled' : 'disabled'}`); + break; + } + case KEY_VERBOSE: { + graph.quietMode = !graph.quietMode; + this._setStatus(`Verbose mode ${!graph.quietMode ? 'enabled' : 'disabled'}`); + break; + } + case KEY_PAUSE_RESUME: { + graph.pauseNextIteration = !graph.pauseNextIteration; + this._setStatus(graph.pauseNextIteration ? 'Watch paused' : 'Watch resumed'); + break; + } + case KEY_PARALLELISM_UP: + case '=': { + this._adjustParallelism(1); + break; + } + case KEY_PARALLELISM_DOWN: { + this._adjustParallelism(-1); + break; + } + case KEY_BUILD: { + void graph.scheduleIterationAsync({ startTime: performance.now() }).then((queued) => { + if (queued) { + if (graph.pauseNextIteration === true) { + void graph.executeScheduledIterationAsync(); + } + this._setStatus('Build iteration queued'); + } else { + this._setStatus('No work to queue'); + } + }); + break; + } + default: { + // ignore other keys + break; + } } } - - return false; } - private static *_enumeratePathsToWatch(paths: Iterable, prefixLength: number): Iterable { - for (const path of paths) { - const rootSlashIndex: number = path.indexOf('/', prefixLength); - - if (rootSlashIndex < 0) { - yield path; - return; - } - - yield path.slice(0, rootSlashIndex); + /** + * Adjusts the parallelism on the operation graph by the given delta + * and reports the result. + */ + private _adjustParallelism(delta: number): void { + const graph: IOperationGraph = this._graph; + const previous: number = graph.parallelism; + graph.parallelism = previous + delta; // setter will clamp/normalize + const effective: number = graph.parallelism; + this._setStatus(`Parallelism ${effective !== previous ? 'set to' : 'remains'} ${effective}`); + } +} - let slashIndex: number = path.indexOf('/', rootSlashIndex + 1); - while (slashIndex >= 0) { - yield path.slice(0, slashIndex); - slashIndex = path.indexOf('/', slashIndex + 1); - } +/** + * Given an iterable of repo-relative file paths, yields the set of directory prefixes + * that should be watched to cover those files. Used on platforms without native recursive + * watch support to enumerate nested folders. + */ +function* _enumeratePathsToWatch(paths: Iterable, prefixLength: number): Iterable { + for (const path of paths) { + const rootSlashIndex: number = path.indexOf('/', prefixLength); + if (rootSlashIndex < 0) { + yield path; + return; + } + yield path.slice(0, rootSlashIndex); + let slashIndex: number = path.indexOf('/', rootSlashIndex + 1); + while (slashIndex >= 0) { + yield path.slice(0, slashIndex); + slashIndex = path.indexOf('/', slashIndex + 1); } } } diff --git a/libraries/rush-lib/src/logic/PublishGit.ts b/libraries/rush-lib/src/logic/PublishGit.ts index 53f8528a526..f61e504ba22 100644 --- a/libraries/rush-lib/src/logic/PublishGit.ts +++ b/libraries/rush-lib/src/logic/PublishGit.ts @@ -3,8 +3,8 @@ import { PublishUtilities } from './PublishUtilities'; import { Utilities } from '../utilities/Utilities'; -import { RushConfigurationProject } from '../api/RushConfigurationProject'; -import { Git } from './Git'; +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; +import type { Git } from './Git'; const DUMMY_BRANCH_NAME: string = '-branch-name-'; @@ -19,138 +19,160 @@ export class PublishGit { this._gitTagSeparator = git.getTagSeparator(); } - public checkout(branchName: string | undefined, createBranch: boolean = false): void { - const params: string[] = ['checkout']; + public async checkoutAsync(branchName: string | undefined, createBranch: boolean = false): Promise { + const args: string[] = ['checkout']; if (createBranch) { - params.push('-b'); + args.push('-b'); } - params.push(branchName || DUMMY_BRANCH_NAME); + args.push(branchName || DUMMY_BRANCH_NAME); - PublishUtilities.execCommand(!!this._targetBranch, this._gitPath, params); + await PublishUtilities.execCommandAsync({ + shouldExecute: !!this._targetBranch, + command: this._gitPath, + args + }); } - public merge(branchName: string, verify: boolean = false): void { - PublishUtilities.execCommand(!!this._targetBranch, this._gitPath, [ - 'merge', - branchName, - '--no-edit', - ...(verify ? [] : ['--no-verify']) - ]); + public async mergeAsync(branchName: string, verify: boolean = false): Promise { + await PublishUtilities.execCommandAsync({ + shouldExecute: !!this._targetBranch, + command: this._gitPath, + args: ['merge', branchName, '--no-edit', ...(verify ? [] : ['--no-verify'])] + }); } - public deleteBranch( + public async deleteBranchAsync( branchName: string | undefined, hasRemote: boolean = true, verify: boolean = false - ): void { + ): Promise { if (!branchName) { branchName = DUMMY_BRANCH_NAME; } - PublishUtilities.execCommand(!!this._targetBranch, this._gitPath, ['branch', '-d', branchName]); + await PublishUtilities.execCommandAsync({ + shouldExecute: !!this._targetBranch, + command: this._gitPath, + args: ['branch', '-d', branchName] + }); if (hasRemote) { - PublishUtilities.execCommand(!!this._targetBranch, this._gitPath, [ - 'push', - 'origin', - '--delete', - branchName, - ...(verify ? [] : ['--no-verify']) - ]); + await PublishUtilities.execCommandAsync({ + shouldExecute: !!this._targetBranch, + command: this._gitPath, + args: ['push', 'origin', '--delete', branchName, ...(verify ? [] : ['--no-verify'])] + }); } } - public pull(verify: boolean = false): void { - const params: string[] = ['pull', 'origin']; + public async pullAsync(verify: boolean = false): Promise { + const args: string[] = ['pull', 'origin']; if (this._targetBranch) { - params.push(this._targetBranch); + args.push(this._targetBranch); } if (!verify) { - params.push('--no-verify'); + args.push('--no-verify'); } - PublishUtilities.execCommand(!!this._targetBranch, this._gitPath, params); + await PublishUtilities.execCommandAsync({ + shouldExecute: !!this._targetBranch, + command: this._gitPath, + args + }); } - public fetch(): void { - PublishUtilities.execCommand(!!this._targetBranch, this._gitPath, ['fetch', 'origin']); + public async fetchAsync(): Promise { + await PublishUtilities.execCommandAsync({ + shouldExecute: !!this._targetBranch, + command: this._gitPath, + args: ['fetch', 'origin'] + }); } - public addChanges(pathspec?: string, workingDirectory?: string): void { - const files: string = pathspec ? pathspec : '.'; - PublishUtilities.execCommand( - !!this._targetBranch, - this._gitPath, - ['add', files], - workingDirectory ? workingDirectory : process.cwd() - ); + public async addChangesAsync(pathspec?: string, workingDirectory?: string): Promise { + const files: string = pathspec || '.'; + await PublishUtilities.execCommandAsync({ + shouldExecute: !!this._targetBranch, + command: this._gitPath, + args: ['add', files], + workingDirectory + }); } - public addTag( + public async addTagAsync( shouldExecute: boolean, packageName: string, packageVersion: string, commitId: string | undefined, preReleaseName?: string - ): void { + ): Promise { // Tagging only happens if we're publishing to real NPM and committing to git. const tagName: string = PublishUtilities.createTagname( packageName, packageVersion, this._gitTagSeparator ); - PublishUtilities.execCommand(!!this._targetBranch && shouldExecute, this._gitPath, [ - 'tag', - '-a', - preReleaseName ? `${tagName}-${preReleaseName}` : tagName, - '-m', - preReleaseName - ? `${packageName} v${packageVersion}-${preReleaseName}` - : `${packageName} v${packageVersion}`, - ...(commitId ? [commitId] : []) - ]); + await PublishUtilities.execCommandAsync({ + shouldExecute: !!this._targetBranch && shouldExecute, + command: this._gitPath, + args: [ + 'tag', + '-a', + preReleaseName ? `${tagName}-${preReleaseName}` : tagName, + '-m', + preReleaseName + ? `${packageName} v${packageVersion}-${preReleaseName}` + : `${packageName} v${packageVersion}`, + ...(commitId ? [commitId] : []) + ] + }); } - public hasTag(packageConfig: RushConfigurationProject): boolean { + public async hasTagAsync(packageConfig: RushConfigurationProject): Promise { const tagName: string = PublishUtilities.createTagname( packageConfig.packageName, packageConfig.packageJson.version, this._gitTagSeparator ); - const tagOutput: string = Utilities.executeCommandAndCaptureOutput( - this._gitPath, - ['tag', '-l', tagName], - packageConfig.projectFolder, - PublishUtilities.getEnvArgs(), - true + const tagOutput: string = ( + await Utilities.executeCommandAndCaptureOutputAsync({ + command: this._gitPath, + args: ['tag', '-l', tagName], + workingDirectory: packageConfig.projectFolder, + environment: PublishUtilities.getEnvArgs(), + keepEnvironment: true + }) ).replace(/(\r\n|\n|\r)/gm, ''); return tagOutput === tagName; } - public commit(commitMessage: string, verify: boolean = false): void { - PublishUtilities.execCommand(!!this._targetBranch, this._gitPath, [ - 'commit', - '-m', - commitMessage, - ...(verify ? [] : ['--no-verify']) - ]); + public async commitAsync(commitMessage: string, verify: boolean = false): Promise { + await PublishUtilities.execCommandAsync({ + shouldExecute: !!this._targetBranch, + command: this._gitPath, + args: ['commit', '-m', commitMessage, ...(verify ? [] : ['--no-verify'])] + }); } - public push(branchName: string | undefined, verify: boolean = false): void { - PublishUtilities.execCommand( - !!this._targetBranch, - this._gitPath, + public async pushAsync( + branchName: string | undefined, + verify: boolean = false, + followTags: boolean = true + ): Promise { + await PublishUtilities.execCommandAsync({ + shouldExecute: !!this._targetBranch, + command: this._gitPath, // We append "--no-verify" to prevent Git hooks from running. For example, people may // want to invoke "rush change -v" as a pre-push hook. - [ + args: [ 'push', 'origin', `HEAD:${branchName || DUMMY_BRANCH_NAME}`, - '--follow-tags', + ...(followTags ? ['--follow-tags'] : []), '--verbose', ...(verify ? [] : ['--no-verify']) ] - ); + }); } } diff --git a/libraries/rush-lib/src/logic/PublishUtilities.ts b/libraries/rush-lib/src/logic/PublishUtilities.ts index aab78b0751d..1005e20922b 100644 --- a/libraries/rush-lib/src/logic/PublishUtilities.ts +++ b/libraries/rush-lib/src/logic/PublishUtilities.ts @@ -6,16 +6,19 @@ * which itself is a thin wrapper around these helpers. */ -import * as path from 'path'; +import * as path from 'node:path'; +import type child_process from 'node:child_process'; + import * as semver from 'semver'; -import { execSync } from 'child_process'; + import { type IPackageJson, JsonFile, FileConstants, Text, Enum, - InternalError + InternalError, + Executable } from '@rushstack/node-core-library'; import { type IChangeInfo, ChangeType, type IVersionPolicyChangeInfo } from '../api/ChangeManagement'; @@ -33,16 +36,27 @@ export interface IChangeRequests { versionPolicyChanges: Map; } +export interface IExecCommandOptions { + shouldExecute: boolean; + command: string; + args?: string[]; + workingDirectory?: string; + environment?: IEnvironment; + secretSubstring?: string; +} + interface IAddChangeOptions { change: IChangeInfo; changeFilePath?: string; allChanges: IChangeRequests; - allPackages: Map; + allPackages: ReadonlyMap; rushConfiguration: RushConfiguration; prereleaseToken?: PrereleaseToken; projectsToExclude?: Set; } +const MAGIC_SPECIFIERS: Set = new Set(['*', '^', '~']); + export class PublishUtilities { /** * Finds change requests in the given folder. @@ -50,7 +64,7 @@ export class PublishUtilities { * @returns Dictionary of all change requests, keyed by package name. */ public static async findChangeRequestsAsync( - allPackages: Map, + allPackages: ReadonlyMap, rushConfiguration: RushConfiguration, changeFiles: ChangeFiles, includeCommitDetails?: boolean, @@ -62,21 +76,23 @@ export class PublishUtilities { versionPolicyChanges: new Map() }; + // eslint-disable-next-line no-console console.log(`Finding changes in: ${changeFiles.getChangesPath()}`); - const files: string[] = await changeFiles.getFilesAsync(); + const files: string[] = await changeFiles.getAllChangeFilesAsync(); // Add the minimum changes defined by the change descriptions. for (const changeFilePath of files) { const changeRequest: IChangeInfo = JsonFile.load(changeFilePath); + const changes: IChangeInfo[] = changeRequest.changes!; if (includeCommitDetails) { const git: Git = new Git(rushConfiguration); - PublishUtilities._updateCommitDetails(git, changeFilePath, changeRequest.changes); + await _updateCommitDetailsAsync(git, changeFilePath, changes); } - for (const change of changeRequest.changes!) { - PublishUtilities._addChange({ + for (const change of changes) { + _addChange({ change, changeFilePath, allChanges, @@ -97,7 +113,7 @@ export class PublishUtilities { // For each requested package change, ensure downstream dependencies are also updated. allChanges.packageChanges.forEach((change, packageName) => { hasChanges = - PublishUtilities._updateDownstreamDependencies( + _updateDownstreamDependencies( change, allChanges, allPackages, @@ -118,7 +134,7 @@ export class PublishUtilities { return; } - const projectHasChanged: boolean = this._addChange({ + const projectHasChanged: boolean = _addChange({ change: { packageName: project.packageName, changeType: versionPolicyChange.changeType, @@ -132,6 +148,7 @@ export class PublishUtilities { }); if (projectHasChanged) { + // eslint-disable-next-line no-console console.log( `\n* APPLYING: update ${project.packageName} to version ${versionPolicyChange.newVersion}` ); @@ -148,21 +165,17 @@ export class PublishUtilities { const deps: Iterable = project.consumingProjects; // Write the new version expected for the change. - const skipVersionBump: boolean = PublishUtilities._shouldSkipVersionBump( - project, - prereleaseToken, - projectsToExclude - ); + const skipVersionBump: boolean = _shouldSkipVersionBump(project, prereleaseToken, projectsToExclude); if (skipVersionBump) { change.newVersion = packageJson.version; } else { // For hotfix changes, do not re-write new version change.newVersion = change.changeType! >= ChangeType.patch - ? semver.inc(packageJson.version, PublishUtilities._getReleaseType(change.changeType!))! + ? semver.inc(packageJson.version, _getReleaseType(change.changeType!))! : change.changeType === ChangeType.hotfix - ? change.newVersion - : packageJson.version; + ? change.newVersion + : packageJson.version; } if (deps) { @@ -194,7 +207,7 @@ export class PublishUtilities { */ public static updatePackages( allChanges: IChangeRequests, - allPackages: Map, + allPackages: ReadonlyMap, rushConfiguration: RushConfiguration, shouldCommit: boolean, prereleaseToken?: PrereleaseToken, @@ -203,7 +216,7 @@ export class PublishUtilities { const updatedPackages: Map = new Map(); allChanges.packageChanges.forEach((change, packageName) => { - const updatedPackage: IPackageJson = PublishUtilities._writePackageChanges( + const updatedPackage: IPackageJson = _writePackageChanges( change, allChanges, allPackages, @@ -249,14 +262,16 @@ export class PublishUtilities { * @param secretSubstring -- if specified, a substring to be replaced by `<>` to avoid printing secrets * on the console */ - public static execCommand( - shouldExecute: boolean, - command: string, - args: string[] = [], - workingDirectory: string = process.cwd(), - environment?: IEnvironment, - secretSubstring?: string - ): void { + public static async execCommandAsync(options: IExecCommandOptions): Promise { + const { + shouldExecute, + command, + args = [], + workingDirectory = process.cwd(), + environment, + secretSubstring + } = options; + let relativeDirectory: string = path.relative(process.cwd(), workingDirectory); if (relativeDirectory) { @@ -270,12 +285,13 @@ export class PublishUtilities { commandArgs = Text.replaceAll(commandArgs, secretSubstring, '<>'); } + // eslint-disable-next-line no-console console.log( `\n* ${shouldExecute ? 'EXECUTING' : 'DRYRUN'}: ${command} ${commandArgs} ${relativeDirectory}` ); if (shouldExecute) { - Utilities.executeCommand({ + await Utilities.executeCommandAsync({ command, args, workingDirectory, @@ -291,17 +307,19 @@ export class PublishUtilities { dependencyName: string, newProjectVersion: string ): string { - const currentDependencySpecifier: DependencySpecifier = new DependencySpecifier( + const currentDependencySpecifier: DependencySpecifier = DependencySpecifier.parseWithCache( dependencyName, dependencies[dependencyName] ); const currentDependencyVersion: string = currentDependencySpecifier.versionSpecifier; let newDependencyVersion: string; - if (currentDependencyVersion === '*') { - newDependencyVersion = '*'; + if (MAGIC_SPECIFIERS.has(currentDependencyVersion)) { + // pnpm and yarn support `workspace:*', `workspace:~`, and `workspace:^` as valid version specifiers + // These translate as `current`, `~current`, and `^current` when published + newDependencyVersion = currentDependencyVersion; } else if (PublishUtilities.isRangeDependency(currentDependencyVersion)) { - newDependencyVersion = PublishUtilities._getNewRangeDependency(newProjectVersion); + newDependencyVersion = _getNewRangeDependency(newProjectVersion); } else if (currentDependencyVersion.lastIndexOf('~', 0) === 0) { newDependencyVersion = '~' + newProjectVersion; } else if (currentDependencyVersion.lastIndexOf('^', 0) === 0) { @@ -313,582 +331,593 @@ export class PublishUtilities { ? `workspace:${newDependencyVersion}` : newDependencyVersion; } +} - private static _getReleaseType(changeType: ChangeType): semver.ReleaseType { - switch (changeType) { - case ChangeType.major: - return 'major'; - case ChangeType.minor: - return 'minor'; - case ChangeType.patch: - return 'patch'; - case ChangeType.hotfix: - return 'prerelease'; - default: - throw new Error(`Wrong change type ${changeType}`); - } +function _getReleaseType(changeType: ChangeType): semver.ReleaseType { + switch (changeType) { + case ChangeType.major: + return 'major'; + case ChangeType.minor: + return 'minor'; + case ChangeType.patch: + return 'patch'; + case ChangeType.hotfix: + return 'prerelease'; + default: + throw new Error(`Wrong change type ${changeType}`); } +} - private static _getChangeTypeForSemverReleaseType(releaseType: semver.ReleaseType): ChangeType { - switch (releaseType) { - case 'major': - return ChangeType.major; - case 'minor': - return ChangeType.minor; - case 'patch': - return ChangeType.patch; - case 'premajor': - case 'preminor': - case 'prepatch': - case 'prerelease': - return ChangeType.hotfix; - default: - throw new Error(`Unsupported release type "${releaseType}"`); - } +function _getNewRangeDependency(newVersion: string): string { + let upperLimit: string = newVersion; + if (semver.prerelease(newVersion)) { + // Remove the prerelease first, then bump major. + upperLimit = semver.inc(newVersion, 'patch')!; } + upperLimit = semver.inc(upperLimit, 'major')!; - private static _getNewRangeDependency(newVersion: string): string { - let upperLimit: string = newVersion; - if (semver.prerelease(newVersion)) { - // Remove the prerelease first, then bump major. - upperLimit = semver.inc(newVersion, 'patch')!; - } - upperLimit = semver.inc(upperLimit, 'major')!; + return `>=${newVersion} <${upperLimit}`; +} - return `>=${newVersion} <${upperLimit}`; - } +function _shouldSkipVersionBump( + project: RushConfigurationProject, + prereleaseToken?: PrereleaseToken, + projectsToExclude?: Set +): boolean { + // Suffix does not bump up the version. + // Excluded projects do not bump up version. + return ( + (prereleaseToken && prereleaseToken.isSuffix) || + (projectsToExclude && projectsToExclude.has(project.packageName)) || + !project.shouldPublish + ); +} - private static _shouldSkipVersionBump( - project: RushConfigurationProject, - prereleaseToken?: PrereleaseToken, - projectsToExclude?: Set - ): boolean { - // Suffix does not bump up the version. - // Excluded projects do not bump up version. - return ( - (prereleaseToken && prereleaseToken.isSuffix) || - (projectsToExclude && projectsToExclude.has(project.packageName)) || - !project.shouldPublish +/** + * Exported for unit tests only. + * @internal + */ +export async function _updateCommitDetailsAsync( + git: Git, + filename: string, + changes: IChangeInfo[] +): Promise { + try { + const gitPath: string = git.getGitPathOrThrow(); + const gitProcess: child_process.ChildProcess = Executable.spawn( + gitPath, + ['log', '-n', '1', '--', filename], + { + currentWorkingDirectory: path.dirname(filename) + } ); + const { + stdout: fileLog, + exitCode, + signal + } = await Executable.waitForExitAsync(gitProcess, { + encoding: 'utf8' + }); + if (exitCode !== 0 || signal) { + return; + } + + const author: string = fileLog.match(/Author: (.*)/)![1]; + const commit: string = fileLog.match(/commit (.*)/)![1]; + + changes.forEach((change) => { + change.author = author; + change.commit = commit; + }); + } catch (e) { + /* no-op, best effort. */ } +} - private static _updateCommitDetails(git: Git, filename: string, changes: IChangeInfo[] | undefined): void { - try { - const gitPath: string = git.getGitPathOrThrow(); - const fileLog: string = execSync(`${gitPath} log -n 1 ${filename}`, { - cwd: path.dirname(filename) - }).toString(); - const author: string = fileLog.match(/Author: (.*)/)![1]; - const commit: string = fileLog.match(/commit (.*)/)![1]; - - changes!.forEach((change) => { - change.author = author; - change.commit = commit; - }); - } catch (e) { - /* no-op, best effort. */ - } +function _writePackageChanges( + change: IChangeInfo, + allChanges: IChangeRequests, + allPackages: ReadonlyMap, + rushConfiguration: RushConfiguration, + shouldCommit: boolean, + prereleaseToken?: PrereleaseToken, + projectsToExclude?: Set +): IPackageJson { + const project: RushConfigurationProject = allPackages.get(change.packageName)!; + const packageJson: IPackageJson = project.packageJson; + + const shouldSkipVersionBump: boolean = + !project.shouldPublish || (!!projectsToExclude && projectsToExclude.has(change.packageName)); + + const newVersion: string = shouldSkipVersionBump + ? packageJson.version + : _getChangeInfoNewVersion(change, prereleaseToken); + + if (!shouldSkipVersionBump) { + // eslint-disable-next-line no-console + console.log( + `\n* ${shouldCommit ? 'APPLYING' : 'DRYRUN'}: ${ChangeType[change.changeType!]} update ` + + `for ${change.packageName} to ${newVersion}` + ); + } else { + // eslint-disable-next-line no-console + console.log( + `\n* ${shouldCommit ? 'APPLYING' : 'DRYRUN'}: update for ${change.packageName} at ${newVersion}` + ); } - private static _writePackageChanges( - change: IChangeInfo, - allChanges: IChangeRequests, - allPackages: Map, - rushConfiguration: RushConfiguration, - shouldCommit: boolean, - prereleaseToken?: PrereleaseToken, - projectsToExclude?: Set - ): IPackageJson { - const project: RushConfigurationProject = allPackages.get(change.packageName)!; - const packageJson: IPackageJson = project.packageJson; + const packagePath: string = path.join(project.projectFolder, FileConstants.PackageJson); - const shouldSkipVersionBump: boolean = - !project.shouldPublish || (!!projectsToExclude && projectsToExclude.has(change.packageName)); + packageJson.version = newVersion; - const newVersion: string = shouldSkipVersionBump - ? packageJson.version - : PublishUtilities._getChangeInfoNewVersion(change, prereleaseToken); + // Update the package's dependencies. + _updateDependencies( + packageJson.name, + packageJson.dependencies, + allChanges, + allPackages, + rushConfiguration, + prereleaseToken, + projectsToExclude + ); + // Update the package's dev dependencies. + _updateDependencies( + packageJson.name, + packageJson.devDependencies, + allChanges, + allPackages, + rushConfiguration, + prereleaseToken, + projectsToExclude + ); + // Update the package's peer dependencies. + _updateDependencies( + packageJson.name, + packageJson.peerDependencies, + allChanges, + allPackages, + rushConfiguration, + prereleaseToken, + projectsToExclude + ); - if (!shouldSkipVersionBump) { - console.log( - `\n* ${shouldCommit ? 'APPLYING' : 'DRYRUN'}: ${ChangeType[change.changeType!]} update ` + - `for ${change.packageName} to ${newVersion}` - ); - } else { - console.log( - `\n* ${shouldCommit ? 'APPLYING' : 'DRYRUN'}: update for ${change.packageName} at ${newVersion}` - ); + change.changes!.forEach((subChange) => { + if (subChange.comment) { + // eslint-disable-next-line no-console + console.log(` - [${ChangeType[subChange.changeType!]}] ${subChange.comment}`); } + }); - const packagePath: string = path.join(project.projectFolder, FileConstants.PackageJson); + if (shouldCommit) { + JsonFile.save(packageJson, packagePath, { updateExistingFile: true }); + } + return packageJson; +} - packageJson.version = newVersion; +function _isCyclicDependency( + allPackages: ReadonlyMap, + packageName: string, + dependencyName: string +): boolean { + const packageConfig: RushConfigurationProject | undefined = allPackages.get(packageName); + return !!packageConfig && packageConfig.decoupledLocalDependencies.has(dependencyName); +} - // Update the package's dependencies. - PublishUtilities._updateDependencies( - packageJson.name, - packageJson.dependencies, - allChanges, - allPackages, - rushConfiguration, - prereleaseToken, - projectsToExclude - ); - // Update the package's dev dependencies. - PublishUtilities._updateDependencies( - packageJson.name, - packageJson.devDependencies, - allChanges, - allPackages, - rushConfiguration, - prereleaseToken, - projectsToExclude - ); - // Update the package's peer dependencies. - PublishUtilities._updateDependencies( - packageJson.name, - packageJson.peerDependencies, - allChanges, - allPackages, - rushConfiguration, - prereleaseToken, - projectsToExclude - ); +function _updateDependencies( + packageName: string, + dependencies: { [key: string]: string } | undefined, + allChanges: IChangeRequests, + allPackages: ReadonlyMap, + rushConfiguration: RushConfiguration, + prereleaseToken: PrereleaseToken | undefined, + projectsToExclude?: Set +): void { + if (dependencies) { + Object.keys(dependencies).forEach((depName) => { + if (!_isCyclicDependency(allPackages, packageName, depName)) { + const depChange: IChangeInfo | undefined = allChanges.packageChanges.get(depName); + if (!depChange) { + return; + } + const depProject: RushConfigurationProject = allPackages.get(depName)!; - change.changes!.forEach((subChange) => { - if (subChange.comment) { - console.log(` - [${ChangeType[subChange.changeType!]}] ${subChange.comment}`); + if (!depProject.shouldPublish || (projectsToExclude && projectsToExclude.has(depName))) { + // No version change. + return; + } else if ( + prereleaseToken && + prereleaseToken.hasValue && + prereleaseToken.isPartialPrerelease && + depChange.changeType! < ChangeType.hotfix + ) { + // For partial prereleases, do not version bump dependencies with the `prereleaseToken` + // value unless an actual change (hotfix, patch, minor, major) has occurred + return; + } else if (depChange && prereleaseToken && prereleaseToken.hasValue) { + // TODO: treat prerelease version the same as non-prerelease version. + // For prerelease, the newVersion needs to be appended with prerelease name. + // And dependency should specify the specific prerelease version. + const currentSpecifier: DependencySpecifier = DependencySpecifier.parseWithCache( + depName, + dependencies[depName] + ); + const newVersion: string = _getChangeInfoNewVersion(depChange, prereleaseToken); + dependencies[depName] = + currentSpecifier.specifierType === DependencySpecifierType.Workspace + ? `workspace:${newVersion}` + : newVersion; + } else if (depChange && depChange.changeType! >= ChangeType.hotfix) { + _updateDependencyVersion( + packageName, + dependencies, + depName, + depChange, + allChanges, + allPackages, + rushConfiguration + ); + } } }); + } +} - if (shouldCommit) { - JsonFile.save(packageJson, packagePath, { updateExistingFile: true }); +/** + * Gets the new version from the ChangeInfo. + * The value of newVersion in ChangeInfo remains unchanged when the change type is dependency, + * However, for pre-release build, it won't pick up the updated pre-released dependencies. That is why + * this function should return a pre-released patch for that case. The exception to this is when we're + * running a partial pre-release build. In this case, only user-changed packages should update. + */ +function _getChangeInfoNewVersion(change: IChangeInfo, prereleaseToken: PrereleaseToken | undefined): string { + let newVersion: string = change.newVersion!; + if (prereleaseToken && prereleaseToken.hasValue) { + if (prereleaseToken.isPartialPrerelease && change.changeType! <= ChangeType.hotfix) { + return newVersion; + } + if (prereleaseToken.isPrerelease && change.changeType === ChangeType.dependency) { + newVersion = semver.inc(newVersion, 'patch')!; } - return packageJson; + return `${newVersion}-${prereleaseToken.name}`; + } else { + return newVersion; } +} - private static _isCyclicDependency( - allPackages: Map, - packageName: string, - dependencyName: string - ): boolean { - const packageConfig: RushConfigurationProject | undefined = allPackages.get(packageName); - return !!packageConfig && packageConfig.decoupledLocalDependencies.has(dependencyName); +/** + * Adds the given change to the packageChanges map. + * + * @returns true if the change caused the dependency change type to increase. + */ +function _addChange({ + change, + changeFilePath, + allChanges, + allPackages, + rushConfiguration, + prereleaseToken, + projectsToExclude +}: IAddChangeOptions): boolean { + let hasChanged: boolean = false; + const packageName: string = change.packageName; + const project: RushConfigurationProject | undefined = allPackages.get(packageName); + + if (!project) { + // eslint-disable-next-line no-console + console.log( + `The package ${packageName} was requested for publishing but does not exist. Skip this change.` + ); + return false; } - private static _updateDependencies( - packageName: string, - dependencies: { [key: string]: string } | undefined, - allChanges: IChangeRequests, - allPackages: Map, - rushConfiguration: RushConfiguration, - prereleaseToken: PrereleaseToken | undefined, - projectsToExclude?: Set - ): void { - if (dependencies) { - Object.keys(dependencies).forEach((depName) => { - if (!PublishUtilities._isCyclicDependency(allPackages, packageName, depName)) { - const depChange: IChangeInfo | undefined = allChanges.packageChanges.get(depName); - if (!depChange) { - return; - } - const depProject: RushConfigurationProject = allPackages.get(depName)!; - - if (!depProject.shouldPublish || (projectsToExclude && projectsToExclude.has(depName))) { - // No version change. - return; - } else if ( - prereleaseToken && - prereleaseToken.hasValue && - prereleaseToken.isPartialPrerelease && - depChange.changeType! < ChangeType.hotfix - ) { - // For partial prereleases, do not version bump dependencies with the `prereleaseToken` - // value unless an actual change (hotfix, patch, minor, major) has occurred - return; - } else if (depChange && prereleaseToken && prereleaseToken.hasValue) { - // TODO: treat prerelease version the same as non-prerelease version. - // For prerelease, the newVersion needs to be appended with prerelease name. - // And dependency should specify the specific prerelease version. - const currentSpecifier: DependencySpecifier = new DependencySpecifier( - depName, - dependencies[depName] - ); - const newVersion: string = PublishUtilities._getChangeInfoNewVersion(depChange, prereleaseToken); - dependencies[depName] = - currentSpecifier.specifierType === DependencySpecifierType.Workspace - ? `workspace:${newVersion}` - : newVersion; - } else if (depChange && depChange.changeType! >= ChangeType.hotfix) { - PublishUtilities._updateDependencyVersion( - packageName, - dependencies, - depName, - depChange, - allChanges, - allPackages, - rushConfiguration - ); - } - } - }); - } - } + const packageJson: IPackageJson = project.packageJson; - /** - * Gets the new version from the ChangeInfo. - * The value of newVersion in ChangeInfo remains unchanged when the change type is dependency, - * However, for pre-release build, it won't pick up the updated pre-released dependencies. That is why - * this function should return a pre-released patch for that case. The exception to this is when we're - * running a partial pre-release build. In this case, only user-changed packages should update. - */ - private static _getChangeInfoNewVersion( - change: IChangeInfo, - prereleaseToken: PrereleaseToken | undefined - ): string { - let newVersion: string = change.newVersion!; - if (prereleaseToken && prereleaseToken.hasValue) { - if (prereleaseToken.isPartialPrerelease && change.changeType! <= ChangeType.hotfix) { - return newVersion; - } - if (prereleaseToken.isPrerelease && change.changeType === ChangeType.dependency) { - newVersion = semver.inc(newVersion, 'patch')!; + // If the given change does not have a changeType, derive it from the "type" string. + if (change.changeType === undefined) { + change.changeType = Enum.tryGetValueByKey(ChangeType, change.type!); + + if (change.changeType === undefined) { + if (changeFilePath) { + throw new Error(`Invalid change type ${JSON.stringify(change.type)} in ${changeFilePath}`); + } else { + throw new InternalError(`Invalid change type ${JSON.stringify(change.type)}`); } - return `${newVersion}-${prereleaseToken.name}`; - } else { - return newVersion; } } - /** - * Adds the given change to the packageChanges map. - * - * @returns true if the change caused the dependency change type to increase. - */ - private static _addChange({ - change, - changeFilePath, - allChanges, - allPackages, - rushConfiguration, - prereleaseToken, - projectsToExclude - }: IAddChangeOptions): boolean { - let hasChanged: boolean = false; - const packageName: string = change.packageName; - const project: RushConfigurationProject | undefined = allPackages.get(packageName); - - if (!project) { - console.log( - `The package ${packageName} was requested for publishing but does not exist. Skip this change.` - ); - return false; - } + let currentChange: IChangeInfo | undefined = allChanges.packageChanges.get(packageName); - const packageJson: IPackageJson = project.packageJson; - - // If the given change does not have a changeType, derive it from the "type" string. - if (change.changeType === undefined) { - change.changeType = Enum.tryGetValueByKey(ChangeType, change.type!); + if (currentChange === undefined) { + hasChanged = true; + currentChange = { + packageName, + changeType: change.changeType, + order: 0, + changes: [change] + }; + allChanges.packageChanges.set(packageName, currentChange); + } else { + const oldChangeType: ChangeType = currentChange.changeType!; - if (change.changeType === undefined) { - if (changeFilePath) { - throw new Error(`Invalid change type ${JSON.stringify(change.type)} in ${changeFilePath}`); - } else { - throw new InternalError(`Invalid change type ${JSON.stringify(change.type)}`); - } - } + if (oldChangeType === ChangeType.hotfix && change.changeType! > oldChangeType) { + throw new Error( + `Cannot apply ${_getReleaseType(change.changeType!)} change after hotfix on same package` + ); + } + if (change.changeType! === ChangeType.hotfix && oldChangeType > change.changeType!) { + throw new Error( + `Cannot apply hotfix alongside ${_getReleaseType(oldChangeType!)} change on same package` + ); } - let currentChange: IChangeInfo | undefined = allChanges.packageChanges.get(packageName); - - if (currentChange === undefined) { - hasChanged = true; - currentChange = { - packageName, - changeType: change.changeType, - order: 0, - changes: [change] - }; - allChanges.packageChanges.set(packageName, currentChange); - } else { - const oldChangeType: ChangeType = currentChange.changeType!; - - if (oldChangeType === ChangeType.hotfix && change.changeType! > oldChangeType) { - throw new Error( - `Cannot apply ${this._getReleaseType(change.changeType!)} change after hotfix on same package` - ); - } - if (change.changeType! === ChangeType.hotfix && oldChangeType > change.changeType!) { - throw new Error( - `Cannot apply hotfix alongside ${this._getReleaseType(oldChangeType!)} change on same package` - ); - } + currentChange.changeType = Math.max(currentChange.changeType!, change.changeType!); + currentChange.changes!.push(change); - currentChange.changeType = Math.max(currentChange.changeType!, change.changeType!); - currentChange.changes!.push(change); + hasChanged = hasChanged || oldChangeType !== currentChange.changeType; + hasChanged = + hasChanged || + (change.newVersion !== undefined && + currentChange.newVersion !== undefined && + semver.gt(change.newVersion, currentChange.newVersion)); + } - hasChanged = hasChanged || oldChangeType !== currentChange.changeType; - hasChanged = - hasChanged || - (change.newVersion !== undefined && - currentChange.newVersion !== undefined && - semver.gt(change.newVersion, currentChange.newVersion)); - } + const skipVersionBump: boolean = _shouldSkipVersionBump(project, prereleaseToken, projectsToExclude); - const skipVersionBump: boolean = PublishUtilities._shouldSkipVersionBump( - project, - prereleaseToken, - projectsToExclude - ); + if (skipVersionBump) { + currentChange.newVersion = change.newVersion ?? packageJson.version; + hasChanged = false; + currentChange.changeType = ChangeType.none; + } else { + if (change.changeType === ChangeType.hotfix) { + const prereleaseComponents: ReadonlyArray | null = semver.prerelease( + packageJson.version + ); + if (!rushConfiguration.hotfixChangeEnabled) { + throw new Error(`Cannot add hotfix change; hotfixChangeEnabled is false in configuration.`); + } - if (skipVersionBump) { - currentChange.newVersion = change.newVersion ?? packageJson.version; - hasChanged = false; - currentChange.changeType = ChangeType.none; + currentChange.newVersion = change.newVersion ?? (packageJson.version as string); + if (!prereleaseComponents) { + currentChange.newVersion += '-hotfix'; + } + currentChange.newVersion = semver.inc(currentChange.newVersion, 'prerelease')!; } else { - if (change.changeType === ChangeType.hotfix) { - const prereleaseComponents: ReadonlyArray | null = semver.prerelease( - packageJson.version - ); - if (!rushConfiguration.hotfixChangeEnabled) { - throw new Error(`Cannot add hotfix change; hotfixChangeEnabled is false in configuration.`); - } - - currentChange.newVersion = change.newVersion ?? (packageJson.version as string); - if (!prereleaseComponents) { - currentChange.newVersion += '-hotfix'; - } - currentChange.newVersion = semver.inc(currentChange.newVersion, 'prerelease')!; - } else { - // When there are multiple changes of this package, the final value of new version - // should not depend on the order of the changes. - let packageVersion: string = change.newVersion ?? packageJson.version; - if (currentChange.newVersion && semver.gt(currentChange.newVersion, packageVersion)) { - packageVersion = currentChange.newVersion; - } + // When there are multiple changes of this package, the final value of new version + // should not depend on the order of the changes. + let packageVersion: string = change.newVersion ?? packageJson.version; + if (currentChange.newVersion && semver.gt(currentChange.newVersion, packageVersion)) { + packageVersion = currentChange.newVersion; + } - const shouldBump: boolean = - change.newVersion === undefined && change.changeType! >= ChangeType.hotfix; + const shouldBump: boolean = change.newVersion === undefined && change.changeType! >= ChangeType.hotfix; - currentChange.newVersion = shouldBump - ? semver.inc(packageVersion, PublishUtilities._getReleaseType(currentChange.changeType!))! - : packageVersion; + currentChange.newVersion = shouldBump + ? semver.inc(packageVersion, _getReleaseType(currentChange.changeType!))! + : packageVersion; - // set versionpolicy version to the current version + // set versionpolicy version to the current version + if ( + hasChanged && + project.versionPolicyName !== undefined && + project.versionPolicy !== undefined && + project.versionPolicy.isLockstepped + ) { + const projectVersionPolicy: LockStepVersionPolicy = project.versionPolicy as LockStepVersionPolicy; + const currentVersionPolicyChange: IVersionPolicyChangeInfo | undefined = + allChanges.versionPolicyChanges.get(project.versionPolicyName); if ( - hasChanged && - project.versionPolicyName !== undefined && - project.versionPolicy !== undefined && - project.versionPolicy.isLockstepped + projectVersionPolicy.nextBump === undefined && + (currentVersionPolicyChange === undefined || + semver.gt(currentChange.newVersion, currentVersionPolicyChange.newVersion)) ) { - const projectVersionPolicy: LockStepVersionPolicy = project.versionPolicy as LockStepVersionPolicy; - const currentVersionPolicyChange: IVersionPolicyChangeInfo | undefined = - allChanges.versionPolicyChanges.get(project.versionPolicyName); - if ( - projectVersionPolicy.nextBump === undefined && - (currentVersionPolicyChange === undefined || - semver.gt(currentChange.newVersion, currentVersionPolicyChange.newVersion)) - ) { - allChanges.versionPolicyChanges.set(project.versionPolicyName, { - versionPolicyName: project.versionPolicyName, - changeType: currentChange.changeType!, - newVersion: currentChange.newVersion - }); - } + allChanges.versionPolicyChanges.set(project.versionPolicyName, { + versionPolicyName: project.versionPolicyName, + changeType: currentChange.changeType!, + newVersion: currentChange.newVersion + }); } } - - // If hotfix change, force new range dependency to be the exact new version - currentChange.newRangeDependency = - change.changeType === ChangeType.hotfix - ? currentChange.newVersion - : PublishUtilities._getNewRangeDependency(currentChange.newVersion!); } - return hasChanged; + + // If hotfix change, force new range dependency to be the exact new version + currentChange.newRangeDependency = + change.changeType === ChangeType.hotfix + ? currentChange.newVersion + : _getNewRangeDependency(currentChange.newVersion!); } + return hasChanged; +} - private static _updateDownstreamDependencies( - change: IChangeInfo, - allChanges: IChangeRequests, - allPackages: Map, - rushConfiguration: RushConfiguration, - prereleaseToken: PrereleaseToken | undefined, - projectsToExclude?: Set - ): boolean { - let hasChanges: boolean = false; - const packageName: string = change.packageName; - const downstream: ReadonlySet = allPackages.get(packageName)!.consumingProjects; - - // Iterate through all downstream dependencies for the package. - if (downstream) { - if (change.changeType! >= ChangeType.hotfix || (prereleaseToken && prereleaseToken.hasValue)) { - for (const dependency of downstream) { - const packageJson: IPackageJson = dependency.packageJson; - - hasChanges = - PublishUtilities._updateDownstreamDependency( - packageJson.name, - packageJson.dependencies, - change, - allChanges, - allPackages, - rushConfiguration, - prereleaseToken, - projectsToExclude - ) || hasChanges; - - hasChanges = - PublishUtilities._updateDownstreamDependency( - packageJson.name, - packageJson.devDependencies, - change, - allChanges, - allPackages, - rushConfiguration, - prereleaseToken, - projectsToExclude - ) || hasChanges; - } +function _updateDownstreamDependencies( + change: IChangeInfo, + allChanges: IChangeRequests, + allPackages: ReadonlyMap, + rushConfiguration: RushConfiguration, + prereleaseToken: PrereleaseToken | undefined, + projectsToExclude?: Set +): boolean { + let hasChanges: boolean = false; + const packageName: string = change.packageName; + const downstream: ReadonlySet = allPackages.get(packageName)!.consumingProjects; + + // Iterate through all downstream dependencies for the package. + if (downstream) { + if (change.changeType! >= ChangeType.hotfix || (prereleaseToken && prereleaseToken.hasValue)) { + for (const dependency of downstream) { + const packageJson: IPackageJson = dependency.packageJson; + + hasChanges = + _updateDownstreamDependency( + packageJson.name, + packageJson.dependencies, + change, + allChanges, + allPackages, + rushConfiguration, + prereleaseToken, + projectsToExclude + ) || hasChanges; + + hasChanges = + _updateDownstreamDependency( + packageJson.name, + packageJson.devDependencies, + change, + allChanges, + allPackages, + rushConfiguration, + prereleaseToken, + projectsToExclude + ) || hasChanges; } } - - return hasChanges; } - private static _updateDownstreamDependency( - parentPackageName: string, - dependencies: { [packageName: string]: string } | undefined, - change: IChangeInfo, - allChanges: IChangeRequests, - allPackages: Map, - rushConfiguration: RushConfiguration, - prereleaseToken: PrereleaseToken | undefined, - projectsToExclude?: Set - ): boolean { - let hasChanges: boolean = false; - if ( - dependencies && - dependencies[change.packageName] && - !PublishUtilities._isCyclicDependency(allPackages, parentPackageName, change.packageName) - ) { - const requiredVersion: DependencySpecifier = new DependencySpecifier( - change.packageName, - dependencies[change.packageName] - ); - const isWorkspaceWildcardVersion: boolean = - requiredVersion.specifierType === DependencySpecifierType.Workspace && - requiredVersion.versionSpecifier === '*'; + return hasChanges; +} - const isPrerelease: boolean = - !!prereleaseToken && prereleaseToken.hasValue && !allChanges.packageChanges.has(parentPackageName); +function _updateDownstreamDependency( + parentPackageName: string, + dependencies: { [packageName: string]: string } | undefined, + change: IChangeInfo, + allChanges: IChangeRequests, + allPackages: ReadonlyMap, + rushConfiguration: RushConfiguration, + prereleaseToken: PrereleaseToken | undefined, + projectsToExclude?: Set +): boolean { + let hasChanges: boolean = false; + if ( + dependencies && + dependencies[change.packageName] && + !_isCyclicDependency(allPackages, parentPackageName, change.packageName) + ) { + const requiredVersion: DependencySpecifier = DependencySpecifier.parseWithCache( + change.packageName, + dependencies[change.packageName] + ); + const isWorkspaceWildcardVersion: boolean = + requiredVersion.specifierType === DependencySpecifierType.Workspace && + MAGIC_SPECIFIERS.has(requiredVersion.versionSpecifier); - // If the version range exists and has not yet been updated to this version, update it. - if ( - isPrerelease || - isWorkspaceWildcardVersion || - requiredVersion.versionSpecifier !== change.newRangeDependency - ) { - let changeType: ChangeType | undefined; - // Propagate hotfix changes to dependencies - if (change.changeType === ChangeType.hotfix) { - changeType = ChangeType.hotfix; - } else { - // Either it already satisfies the new version, or doesn't. - // If not, the downstream dep needs to be republished. - // The downstream dep will also need to be republished if using `workspace:*` as this will publish - // as the exact version. - changeType = - !isWorkspaceWildcardVersion && - semver.satisfies(change.newVersion!, requiredVersion.versionSpecifier) - ? ChangeType.dependency - : ChangeType.patch; - } + const isPrerelease: boolean = + !!prereleaseToken && prereleaseToken.hasValue && !allChanges.packageChanges.has(parentPackageName); - hasChanges = PublishUtilities._addChange({ - change: { - packageName: parentPackageName, - changeType - }, - allChanges, - allPackages, - rushConfiguration, - prereleaseToken, - projectsToExclude - }); + // If the version range exists and has not yet been updated to this version, update it. + if ( + isPrerelease || + isWorkspaceWildcardVersion || + requiredVersion.versionSpecifier !== change.newRangeDependency + ) { + let changeType: ChangeType | undefined; + // Propagate hotfix changes to dependencies + if (change.changeType === ChangeType.hotfix) { + changeType = ChangeType.hotfix; + } else { + // Either it already satisfies the new version, or doesn't. + // If not, the downstream dep needs to be republished. + // The downstream dep will also need to be republished if using `workspace:*` as this will publish + // as the exact version. + changeType = + !isWorkspaceWildcardVersion && + semver.satisfies(change.newVersion!, requiredVersion.versionSpecifier) + ? ChangeType.dependency + : ChangeType.patch; + } - if (hasChanges || isPrerelease) { - // Only re-evaluate downstream dependencies if updating the parent package's dependency - // caused a version bump. - hasChanges = - PublishUtilities._updateDownstreamDependencies( - allChanges.packageChanges.get(parentPackageName)!, - allChanges, - allPackages, - rushConfiguration, - prereleaseToken, - projectsToExclude - ) || hasChanges; - } + hasChanges = _addChange({ + change: { + packageName: parentPackageName, + changeType + }, + allChanges, + allPackages, + rushConfiguration, + prereleaseToken, + projectsToExclude + }); + + if (hasChanges || isPrerelease) { + // Only re-evaluate downstream dependencies if updating the parent package's dependency + // caused a version bump. + hasChanges = + _updateDownstreamDependencies( + allChanges.packageChanges.get(parentPackageName)!, + allChanges, + allPackages, + rushConfiguration, + prereleaseToken, + projectsToExclude + ) || hasChanges; } } - - return hasChanges; } - private static _updateDependencyVersion( - packageName: string, - dependencies: { [key: string]: string }, - dependencyName: string, - dependencyChange: IChangeInfo, - allChanges: IChangeRequests, - allPackages: Map, - rushConfiguration: RushConfiguration - ): void { - let currentDependencyVersion: string | undefined = dependencies[dependencyName]; - let newDependencyVersion: string = PublishUtilities.getNewDependencyVersion( - dependencies, - dependencyName, - dependencyChange.newVersion! - ); - dependencies[dependencyName] = newDependencyVersion; - - // "*" is a special case for workspace ranges, since it will publish using the exact - // version of the local dependency, so we need to modify what we write for our change - // comment - const currentDependencySpecifier: DependencySpecifier = new DependencySpecifier( - dependencyName, - currentDependencyVersion - ); - currentDependencyVersion = - currentDependencySpecifier.specifierType === DependencySpecifierType.Workspace && - currentDependencySpecifier.versionSpecifier === '*' - ? undefined - : currentDependencySpecifier.versionSpecifier; + return hasChanges; +} - const newDependencySpecifier: DependencySpecifier = new DependencySpecifier( - dependencyName, - newDependencyVersion - ); - newDependencyVersion = - newDependencySpecifier.specifierType === DependencySpecifierType.Workspace && - newDependencySpecifier.versionSpecifier === '*' - ? dependencyChange.newVersion! - : newDependencySpecifier.versionSpecifier; - - // Add dependency version update comment. - PublishUtilities._addChange({ - change: { - packageName: packageName, - changeType: ChangeType.dependency, - comment: - `Updating dependency "${dependencyName}" ` + - (currentDependencyVersion ? `from \`${currentDependencyVersion}\` ` : '') + - `to \`${newDependencyVersion}\`` - }, - allChanges, - allPackages, - rushConfiguration - }); +function _getPublishDependencyVersion(specifier: DependencySpecifier, newVersion: string): string { + if (specifier.specifierType === DependencySpecifierType.Workspace) { + const { versionSpecifier } = specifier; + switch (versionSpecifier) { + case '*': + return newVersion; + case '~': + case '^': + return `${versionSpecifier}${newVersion}`; + } } + return newVersion; +} + +function _updateDependencyVersion( + packageName: string, + dependencies: { [key: string]: string }, + dependencyName: string, + dependencyChange: IChangeInfo, + allChanges: IChangeRequests, + allPackages: ReadonlyMap, + rushConfiguration: RushConfiguration +): void { + let currentDependencyVersion: string | undefined = dependencies[dependencyName]; + let newDependencyVersion: string = PublishUtilities.getNewDependencyVersion( + dependencies, + dependencyName, + dependencyChange.newVersion! + ); + dependencies[dependencyName] = newDependencyVersion; + + // "*", "~", and "^" are special cases for workspace ranges, since it will publish using the exact + // version of the local dependency, so we need to modify what we write for our change + // comment + const currentDependencySpecifier: DependencySpecifier = DependencySpecifier.parseWithCache( + dependencyName, + currentDependencyVersion + ); + currentDependencyVersion = + currentDependencySpecifier.specifierType === DependencySpecifierType.Workspace && + MAGIC_SPECIFIERS.has(currentDependencySpecifier.versionSpecifier) + ? undefined + : currentDependencySpecifier.versionSpecifier; + + const newDependencySpecifier: DependencySpecifier = DependencySpecifier.parseWithCache( + dependencyName, + newDependencyVersion + ); + newDependencyVersion = _getPublishDependencyVersion(newDependencySpecifier, dependencyChange.newVersion!); + + // Add dependency version update comment. + _addChange({ + change: { + packageName: packageName, + changeType: ChangeType.dependency, + comment: + `Updating dependency "${dependencyName}" ` + + (currentDependencyVersion ? `from \`${currentDependencyVersion}\` ` : '') + + `to \`${newDependencyVersion}\`` + }, + allChanges, + allPackages, + rushConfiguration + }); } diff --git a/libraries/rush-lib/src/logic/PurgeManager.ts b/libraries/rush-lib/src/logic/PurgeManager.ts index b8f60855cb9..58df2009851 100644 --- a/libraries/rush-lib/src/logic/PurgeManager.ts +++ b/libraries/rush-lib/src/logic/PurgeManager.ts @@ -1,13 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; +import * as path from 'node:path'; + +import { Colorize } from '@rushstack/terminal'; import { AsyncRecycler } from '../utilities/AsyncRecycler'; -import { RushConfiguration } from '../api/RushConfiguration'; -import { RushConstants } from '../logic/RushConstants'; -import { RushGlobalFolder } from '../api/RushGlobalFolder'; +import type { RushConfiguration } from '../api/RushConfiguration'; +import { RushConstants } from './RushConstants'; +import type { RushGlobalFolder } from '../api/RushGlobalFolder'; /** * This class implements the logic for "rush purge" @@ -40,9 +41,11 @@ export class PurgeManager { * Performs the AsyncRecycler.deleteAll() operation. This should be called before * the PurgeManager instance is disposed. */ - public deleteAll(): void { - this.commonTempFolderRecycler.deleteAll(); - this._rushUserFolderRecycler.deleteAll(); + public async startDeleteAllAsync(): Promise { + await Promise.all([ + this.commonTempFolderRecycler.startDeleteAllAsync(), + this._rushUserFolderRecycler.startDeleteAllAsync() + ]); } /** @@ -50,6 +53,7 @@ export class PurgeManager { */ public purgeNormal(): void { // Delete everything under common\temp except for the recycler folder itself + // eslint-disable-next-line no-console console.log('Purging ' + this._rushConfiguration.commonTempFolder); this.commonTempFolderRecycler.moveAllItemsInFolder( @@ -66,6 +70,7 @@ export class PurgeManager { this.purgeNormal(); // We will delete everything under ~/.rush/ except for the recycler folder itself + // eslint-disable-next-line no-console console.log('Purging ' + this._rushGlobalFolder.path); // If Rush itself is running under a folder such as ~/.rush/node-v4.5.6/rush-1.2.3, @@ -84,11 +89,12 @@ export class PurgeManager { ); if ( - this._rushConfiguration.packageManager === 'pnpm' && + this._rushConfiguration.isPnpm && this._rushConfiguration.pnpmOptions.pnpmStore === 'global' && this._rushConfiguration.pnpmOptions.pnpmStorePath ) { - console.warn(colors.yellow(`Purging the global pnpm-store`)); + // eslint-disable-next-line no-console + console.warn(Colorize.yellow(`Purging the global pnpm-store`)); this._rushUserFolderRecycler.moveAllItemsInFolder(this._rushConfiguration.pnpmOptions.pnpmStorePath); } } @@ -115,8 +121,9 @@ export class PurgeManager { if (showWarning) { // Warn that we won't dispose this folder + // eslint-disable-next-line no-console console.log( - colors.yellow( + Colorize.yellow( "The active process's folder will not be deleted: " + path.join(folderToRecycle, firstPart) ) ); diff --git a/libraries/rush-lib/src/logic/RepoStateFile.ts b/libraries/rush-lib/src/logic/RepoStateFile.ts index 531f7243daa..499903848fa 100644 --- a/libraries/rush-lib/src/logic/RepoStateFile.ts +++ b/libraries/rush-lib/src/logic/RepoStateFile.ts @@ -3,17 +3,20 @@ import { FileSystem, JsonFile, JsonSchema, NewlineKind } from '@rushstack/node-core-library'; -import { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfiguration } from '../api/RushConfiguration'; import { PnpmShrinkwrapFile } from './pnpm/PnpmShrinkwrapFile'; -import { CommonVersionsConfiguration } from '../api/CommonVersionsConfiguration'; +import type { CommonVersionsConfiguration } from '../api/CommonVersionsConfiguration'; import schemaJson from '../schemas/repo-state.schema.json'; +import type { Subspace } from '../api/Subspace'; /** * This interface represents the raw repo-state.json file * Example: * { * "pnpmShrinkwrapHash": "...", - * "preferredVersionsHash": "..." + * "preferredVersionsHash": "...", + * "packageJsonInjectedDependenciesHash": "...", + * "pnpmCatalogsHash": "..." * } */ interface IRepoStateJson { @@ -25,8 +28,18 @@ interface IRepoStateJson { * A hash of the CommonVersionsConfiguration.preferredVersions field */ preferredVersionsHash?: string; + /** + * A hash of the injected dependencies in related package.json + */ + packageJsonInjectedDependenciesHash?: string; + /** + * A hash of the PNPM catalog definitions + */ + pnpmCatalogsHash?: string; } +const _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); + /** * This file is used to track the state of various Rush-related features. It is generated * and updated by Rush. @@ -34,11 +47,10 @@ interface IRepoStateJson { * @public */ export class RepoStateFile { - private static _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); - - private _variant: string | undefined; private _pnpmShrinkwrapHash: string | undefined; private _preferredVersionsHash: string | undefined; + private _packageJsonInjectedDependenciesHash: string | undefined; + private _pnpmCatalogsHash: string | undefined; private _isValid: boolean; private _modified: boolean = false; @@ -47,19 +59,15 @@ export class RepoStateFile { */ public readonly filePath: string; - private constructor( - repoStateJson: IRepoStateJson | undefined, - isValid: boolean, - filePath: string, - variant: string | undefined - ) { + private constructor(repoStateJson: IRepoStateJson | undefined, isValid: boolean, filePath: string) { this.filePath = filePath; - this._variant = variant; this._isValid = isValid; if (repoStateJson) { this._pnpmShrinkwrapHash = repoStateJson.pnpmShrinkwrapHash; this._preferredVersionsHash = repoStateJson.preferredVersionsHash; + this._packageJsonInjectedDependenciesHash = repoStateJson.packageJsonInjectedDependenciesHash; + this._pnpmCatalogsHash = repoStateJson.pnpmCatalogsHash; } } @@ -77,6 +85,20 @@ export class RepoStateFile { return this._preferredVersionsHash; } + /** + * The hash of all preferred versions at the end of the last update. + */ + public get packageJsonInjectedDependenciesHash(): string | undefined { + return this._packageJsonInjectedDependenciesHash; + } + + /** + * The hash of the PNPM catalog definitions at the end of the last update. + */ + public get pnpmCatalogsHash(): string | undefined { + return this._pnpmCatalogsHash; + } + /** * If false, the repo-state.json file is not valid and its values cannot be relied upon */ @@ -89,9 +111,8 @@ export class RepoStateFile { * If the file has not been created yet, then an empty object is returned. * * @param jsonFilename - The path to the repo-state.json file. - * @param variant - The variant currently being used by Rush. */ - public static loadFromFile(jsonFilename: string, variant: string | undefined): RepoStateFile { + public static loadFromFile(jsonFilename: string): RepoStateFile { let fileContents: string | undefined; try { fileContents = FileSystem.readFile(jsonFilename); @@ -127,11 +148,11 @@ export class RepoStateFile { } if (repoStateJson) { - this._jsonSchema.validateObject(repoStateJson, jsonFilename); + _jsonSchema.validateObject(repoStateJson, jsonFilename); } } - return new RepoStateFile(repoStateJson, !foundMergeConflictMarker, jsonFilename, variant); + return new RepoStateFile(repoStateJson, !foundMergeConflictMarker, jsonFilename); } /** @@ -139,18 +160,29 @@ export class RepoStateFile { * of the Rush repo, and save the file if changes were made. * * @param rushConfiguration - The Rush configuration for the repo. + * @param subspace - The subspace that repo-state.json was loaded from, + * or `undefined` for the default subspace. * * @returns true if the file was modified, otherwise false. */ - public refreshState(rushConfiguration: RushConfiguration): boolean { + public refreshState( + rushConfiguration: RushConfiguration, + subspace: Subspace | undefined, + variant?: string + ): boolean { + if (subspace === undefined) { + subspace = rushConfiguration.defaultSubspace; + } + // Only support saving the pnpm shrinkwrap hash if it was enabled const preventShrinkwrapChanges: boolean = - rushConfiguration.packageManager === 'pnpm' && + rushConfiguration.isPnpm && rushConfiguration.pnpmOptions && rushConfiguration.pnpmOptions.preventManualShrinkwrapChanges; if (preventShrinkwrapChanges) { const pnpmShrinkwrapFile: PnpmShrinkwrapFile | undefined = PnpmShrinkwrapFile.loadFromFile( - rushConfiguration.getCommittedShrinkwrapFilename(this._variant) + subspace.getCommittedShrinkwrapFilePath(variant), + { subspaceHasNoProjects: subspace.getProjects().length === 0 } ); if (pnpmShrinkwrapFile) { @@ -172,7 +204,7 @@ export class RepoStateFile { const useWorkspaces: boolean = rushConfiguration.pnpmOptions && rushConfiguration.pnpmOptions.useWorkspaces; if (useWorkspaces) { - const commonVersions: CommonVersionsConfiguration = rushConfiguration.getCommonVersions(this._variant); + const commonVersions: CommonVersionsConfiguration = subspace.getCommonVersions(variant); const preferredVersionsHash: string = commonVersions.getPreferredVersionsHash(); if (this._preferredVersionsHash !== preferredVersionsHash) { this._preferredVersionsHash = preferredVersionsHash; @@ -183,6 +215,37 @@ export class RepoStateFile { this._modified = true; } + if (rushConfiguration.isPnpm) { + const packageJsonInjectedDependenciesHash: string | undefined = + subspace.getPackageJsonInjectedDependenciesHash(variant); + + // packageJsonInjectedDependenciesHash is undefined, means there is no injected dependencies for that subspace + // so we don't need to track the hash value for that subspace + if ( + packageJsonInjectedDependenciesHash && + packageJsonInjectedDependenciesHash !== this._packageJsonInjectedDependenciesHash + ) { + this._packageJsonInjectedDependenciesHash = packageJsonInjectedDependenciesHash; + this._modified = true; + } else if (!packageJsonInjectedDependenciesHash && this._packageJsonInjectedDependenciesHash) { + // if packageJsonInjectedDependenciesHash is undefined, but this._packageJsonInjectedDependenciesHash is not + // means users may turn off the injected installation + // so we will need to remove unused fields in repo-state.json as well + this._packageJsonInjectedDependenciesHash = undefined; + this._modified = true; + } + + // Track catalog hash to detect when catalog definitions change + const pnpmCatalogsHash: string | undefined = subspace.getPnpmCatalogsHash(); + if (pnpmCatalogsHash && pnpmCatalogsHash !== this._pnpmCatalogsHash) { + this._pnpmCatalogsHash = pnpmCatalogsHash; + this._modified = true; + } else if (!pnpmCatalogsHash && this._pnpmCatalogsHash) { + this._pnpmCatalogsHash = undefined; + this._modified = true; + } + } + // Now that the file has been refreshed, we know its contents are valid this._isValid = true; @@ -214,6 +277,12 @@ export class RepoStateFile { if (this._preferredVersionsHash) { repoStateJson.preferredVersionsHash = this._preferredVersionsHash; } + if (this._packageJsonInjectedDependenciesHash) { + repoStateJson.packageJsonInjectedDependenciesHash = this._packageJsonInjectedDependenciesHash; + } + if (this._pnpmCatalogsHash) { + repoStateJson.pnpmCatalogsHash = this._pnpmCatalogsHash; + } return JsonFile.stringify(repoStateJson, { newlineConversion: NewlineKind.Lf }); } diff --git a/libraries/rush-lib/src/logic/RushConstants.ts b/libraries/rush-lib/src/logic/RushConstants.ts index 6b565f0f1c5..b853450fd17 100644 --- a/libraries/rush-lib/src/logic/RushConstants.ts +++ b/libraries/rush-lib/src/logic/RushConstants.ts @@ -1,6 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import type { RUSH_USER_FOLDER_NAME } from '@rushstack/credential-cache'; + +// Use the typing here to enforce consistency between the two libraries +const rushUserConfigurationFolderName: typeof RUSH_USER_FOLDER_NAME = '.rush-user'; + /** * Constants used by the Rush tool. * @beta @@ -12,6 +17,11 @@ * the Rush config files; instead, they should rely on the official APIs from rush-lib. */ export class RushConstants { + /** + * The filename ("rush.json") for the root-level configuration file. + */ + public static readonly rushJsonFilename: 'rush.json' = 'rush.json'; + /** * The filename ("browser-approved-packages.json") for an optional policy configuration file * that stores a list of NPM packages that have been approved for usage by Rush projects. @@ -19,12 +29,13 @@ export class RushConstants { * (e.g. whose approval criteria mostly focuses on licensing and code size), and one for everywhere else * (e.g. tooling projects whose approval criteria mostly focuses on avoiding node_modules sprawl). */ - public static readonly browserApprovedPackagesFilename: string = 'browser-approved-packages.json'; + public static readonly browserApprovedPackagesFilename: 'browser-approved-packages.json' = + 'browser-approved-packages.json'; /** * The folder name ("changes") where change files will be stored. */ - public static readonly changeFilesFolderName: string = 'changes'; + public static readonly changeFilesFolderName: 'changes' = 'changes'; /** * The filename ("nonbrowser-approved-packages.json") for an optional policy configuration file @@ -33,80 +44,102 @@ export class RushConstants { * (e.g. whose approval criteria mostly focuses on licensing and code size), and one for everywhere else * (e.g. tooling projects whose approval criteria mostly focuses on avoiding node_modules sprawl). */ - public static readonly nonbrowserApprovedPackagesFilename: string = 'nonbrowser-approved-packages.json'; + public static readonly nonbrowserApprovedPackagesFilename: 'nonbrowser-approved-packages.json' = + 'nonbrowser-approved-packages.json'; /** * The folder name ("common") where Rush's common data will be stored. */ - public static readonly commonFolderName: string = 'common'; + public static readonly commonFolderName: 'common' = 'common'; /** * The NPM scope ("\@rush-temp") that is used for Rush's temporary projects. */ - public static readonly rushTempNpmScope: string = '@rush-temp'; + public static readonly rushTempNpmScope: '@rush-temp' = '@rush-temp'; + + /** + * The folder name ("variants") under which named variant configurations for + * alternate dependency sets may be found. + * Example: `C:\MyRepo\common\config\rush\variants` + */ + public static readonly rushVariantsFolderName: 'variants' = 'variants'; /** * The folder name ("temp") under the common folder, or under the .rush folder in each project's directory where * temporary files will be stored. * Example: `C:\MyRepo\common\temp` */ - public static readonly rushTempFolderName: string = 'temp'; + public static readonly rushTempFolderName: 'temp' = 'temp'; /** * The folder name ("projects") where temporary projects will be stored. * Example: `C:\MyRepo\common\temp\projects` */ - public static readonly rushTempProjectsFolderName: string = 'projects'; - - /** - * The folder name ("variants") under which named variant configurations for - * alternate dependency sets may be found. - * Example: `C:\MyRepo\common\config\rush\variants` - */ - public static readonly rushVariantsFolderName: string = 'variants'; + public static readonly rushTempProjectsFolderName: 'projects' = 'projects'; /** * The filename ("npm-shrinkwrap.json") used to store an installation plan for the NPM package manger. */ - public static readonly npmShrinkwrapFilename: string = 'npm-shrinkwrap.json'; + public static readonly npmShrinkwrapFilename: 'npm-shrinkwrap.json' = 'npm-shrinkwrap.json'; /** * Number of installation attempts */ - public static readonly defaultMaxInstallAttempts: number = 1; + public static readonly defaultMaxInstallAttempts: 1 = 1; /** * The filename ("pnpm-lock.yaml") used to store an installation plan for the PNPM package manger * (PNPM version 3.x and later). */ - public static readonly pnpmV3ShrinkwrapFilename: string = 'pnpm-lock.yaml'; + public static readonly pnpmV3ShrinkwrapFilename: 'pnpm-lock.yaml' = 'pnpm-lock.yaml'; /** * The filename ("pnpmfile.js") used to add custom configuration to PNPM (PNPM version 1.x and later). */ - public static readonly pnpmfileV1Filename: string = 'pnpmfile.js'; + public static readonly pnpmfileV1Filename: 'pnpmfile.js' = 'pnpmfile.js'; /** * The filename (".pnpmfile.cjs") used to add custom configuration to PNPM (PNPM version 6.x and later). */ - public static readonly pnpmfileV6Filename: string = '.pnpmfile.cjs'; + public static readonly pnpmfileV6Filename: '.pnpmfile.cjs' = '.pnpmfile.cjs'; + + /** + * The filename (".modules.yaml") used by pnpm to specify configurations in the node_modules directory + */ + public static readonly pnpmModulesFilename: '.modules.yaml' = '.modules.yaml'; + + /** + * The folder name (".pnpm") used by pnpm to store the code of the dependencies for this subspace + */ + public static readonly pnpmVirtualStoreFolderName: '.pnpm' = '.pnpm'; + + /** + * The filename ("global-pnpmfile.cjs") used to add custom configuration to subspaces + */ + public static readonly pnpmfileGlobalFilename: 'global-pnpmfile.cjs' = 'global-pnpmfile.cjs'; /** * The folder name used to store patch files for pnpm * Example: `C:\MyRepo\common\config\pnpm-patches` * Example: `C:\MyRepo\common\temp\patches` */ - public static readonly pnpmPatchesFolderName: string = 'patches'; + public static readonly pnpmPatchesFolderName: 'patches' = 'patches'; + + /** + * The folder name under `/common/temp` used to store checked-in patches. + * Example: `C:\MyRepo\common\pnpm-patches` + */ + public static readonly pnpmPatchesCommonFolderName: `pnpm-patches` = `pnpm-${RushConstants.pnpmPatchesFolderName}`; /** * The filename ("shrinkwrap.yaml") used to store state for pnpm */ - public static readonly yarnShrinkwrapFilename: string = 'yarn.lock'; + public static readonly yarnShrinkwrapFilename: 'yarn.lock' = 'yarn.lock'; /** * The folder name ("node_modules") where NPM installs its packages. */ - public static readonly nodeModulesFolderName: string = 'node_modules'; + public static readonly nodeModulesFolderName: 'node_modules' = 'node_modules'; /** * The filename ("pinned-versions.json") for an old configuration file that @@ -117,97 +150,120 @@ export class RushConstants { */ // NOTE: Although this is marked as "deprecated", we will probably never retire it, // since we always want to report the warning when someone upgrades an old repo. - public static readonly pinnedVersionsFilename: string = 'pinned-versions.json'; + public static readonly pinnedVersionsFilename: 'pinned-versions.json' = 'pinned-versions.json'; /** * The filename ("common-versions.json") for an optional configuration file * that stores dependency version information that affects all projects in the repo. * This configuration file should go in the "common/config/rush" folder. */ - public static readonly commonVersionsFilename: string = 'common-versions.json'; + public static readonly commonVersionsFilename: 'common-versions.json' = 'common-versions.json'; /** * The filename ("repo-state.json") for a file used by Rush to * store the state of various features as they stand in the repo. */ - public static readonly repoStateFilename: string = 'repo-state.json'; + public static readonly repoStateFilename: 'repo-state.json' = 'repo-state.json'; + + /** + * The filename ("custom-tips.json") for the file used by Rush to + * print user-customized messages. + * This configuration file should go in the "common/config/rush" folder. + */ + public static readonly customTipsFilename: 'custom-tips.json' = 'custom-tips.json'; /** * The name of the per-project folder where project-specific Rush files are stored. For example, * the package-deps files, which are used by commands to determine if a particular project needs to be rebuilt. */ - public static readonly projectRushFolderName: string = '.rush'; + public static readonly projectRushFolderName: '.rush' = '.rush'; /** * Custom command line configuration file, which is used by rush for implementing * custom command and options. */ - public static readonly commandLineFilename: string = 'command-line.json'; + public static readonly commandLineFilename: 'command-line.json' = 'command-line.json'; - public static readonly versionPoliciesFilename: string = 'version-policies.json'; + public static readonly versionPoliciesFilename: 'version-policies.json' = 'version-policies.json'; /** * Experiments configuration file. */ - public static readonly experimentsFilename: string = 'experiments.json'; + public static readonly experimentsFilename: 'experiments.json' = 'experiments.json'; /** * Pnpm configuration file */ - public static readonly pnpmConfigFilename: string = 'pnpm-config.json'; + public static readonly pnpmConfigFilename: 'pnpm-config.json' = 'pnpm-config.json'; /** * Rush plugins configuration file name. */ - public static readonly rushPluginsConfigFilename: string = 'rush-plugins.json'; + public static readonly rushPluginsConfigFilename: 'rush-plugins.json' = 'rush-plugins.json'; /** * Rush plugin manifest file name. */ - public static readonly rushPluginManifestFilename: string = 'rush-plugin-manifest.json'; + public static readonly rushPluginManifestFilename: 'rush-plugin-manifest.json' = + 'rush-plugin-manifest.json'; /** * The artifactory.json configuration file name. */ - public static readonly artifactoryFilename: string = 'artifactory.json'; + public static readonly artifactoryFilename: 'artifactory.json' = 'artifactory.json'; + + /** + * The subspaces.json configuration file name + */ + public static readonly subspacesConfigFilename: 'subspaces.json' = 'subspaces.json'; + + /** + * The name of the default subspace if one isn't specified but subspaces is enabled. + */ + public static readonly defaultSubspaceName: 'default' = 'default'; /** * Build cache configuration file. */ - public static readonly buildCacheFilename: string = 'build-cache.json'; + public static readonly buildCacheFilename: 'build-cache.json' = 'build-cache.json'; /** * Build cache version number, incremented when the logic to create cache entries changes. * Changing this ensures that cache entries generated by an old version will no longer register as a cache hit. */ - public static readonly buildCacheVersion: number = 1; + public static readonly buildCacheVersion: 1 = 1; + + /** + * Cobuild configuration file. + */ + public static readonly cobuildFilename: 'cobuild.json' = 'cobuild.json'; /** * Per-project configuration filename. */ - public static readonly rushProjectConfigFilename: string = 'rush-project.json'; + public static readonly rushProjectConfigFilename: 'rush-project.json' = 'rush-project.json'; /** * The URL ("http://rushjs.io") for the Rush web site. */ - public static readonly rushWebSiteUrl: string = 'https://rushjs.io'; + public static readonly rushWebSiteUrl: 'https://rushjs.io' = 'https://rushjs.io'; /** * The name of the NPM package for the Rush tool ("\@microsoft/rush"). */ - public static readonly rushPackageName: string = '@microsoft/rush'; + public static readonly rushPackageName: '@microsoft/rush' = '@microsoft/rush'; /** * The folder name ("rush-recycler") where Rush moves large folder trees * before asynchronously deleting them. */ - public static readonly rushRecyclerFolderName: string = 'rush-recycler'; + public static readonly rushRecyclerFolderName: 'rush-recycler' = 'rush-recycler'; /** * The name of the file to drop in project-folder/.rush/temp/ containing a listing of the project's direct * and indirect dependencies. This is used to detect if a project's dependencies have changed since the last build. */ - public static readonly projectShrinkwrapFilename: string = 'shrinkwrap-deps.json'; + public static readonly projectShrinkwrapFilename: 'shrinkwrap-deps.json' = 'shrinkwrap-deps.json'; /** * The value of the "commandKind" property for a bulk command in command-line.json @@ -219,6 +275,12 @@ export class RushConstants { */ public static readonly globalCommandKind: 'global' = 'global'; + /** + * The value of the "commandKind" property for a plugin-only global command in command-line.json. + * This command kind can only be used in command-line.json files provided by Rush plugins. + */ + public static readonly globalPluginCommandKind: 'globalPlugin' = 'globalPlugin'; + /** * The value of the "commandKind" property for a phased command in command-line.json */ @@ -227,31 +289,32 @@ export class RushConstants { /** * The name of the incremental build command. */ - public static readonly buildCommandName: string = 'build'; + public static readonly buildCommandName: 'build' = 'build'; /** * The name of the non-incremental build command. */ - public static readonly rebuildCommandName: string = 'rebuild'; + public static readonly rebuildCommandName: 'rebuild' = 'rebuild'; - public static readonly updateCloudCredentialsCommandName: string = 'update-cloud-credentials'; + public static readonly updateCloudCredentialsCommandName: 'update-cloud-credentials' = + 'update-cloud-credentials'; /** * When a hash generated that contains multiple input segments, this character may be used * to separate them to avoid issues like * crypto.createHash('sha1').update('a').update('bc').digest('hex') === crypto.createHash('sha1').update('ab').update('c').digest('hex') */ - public static readonly hashDelimiter: string = '|'; + public static readonly hashDelimiter: '|' = '|'; /** * The name of the per-user Rush configuration data folder. */ - public static readonly rushUserConfigurationFolderName: string = '.rush-user'; + public static readonly rushUserConfigurationFolderName: '.rush-user' = rushUserConfigurationFolderName; /** * The name of the project `rush-logs` folder. */ - public static readonly rushLogsFolderName: string = 'rush-logs'; + public static readonly rushLogsFolderName: 'rush-logs' = 'rush-logs'; /** * The expected prefix for phase names in "common/config/rush/command-line.json" @@ -263,10 +326,52 @@ export class RushConstants { * how long to wait after the last encountered file system event before execution. If another * file system event occurs in this interval, the timeout will reset. */ - public static readonly defaultWatchDebounceMs: number = 1000; + public static readonly defaultWatchDebounceMs: 1000 = 1000; /** * The name of the parameter that can be used to bypass policies. */ public static readonly bypassPolicyFlagLongName: '--bypass-policy' = '--bypass-policy'; + + /** + * Merge Queue ignore configuration file. + */ + public static readonly mergeQueueIgnoreFileName: '.mergequeueignore' = '.mergequeueignore'; + + /** + * The filename ("project-impact-graph.yaml") for the project impact graph file. + */ + public static readonly projectImpactGraphFilename: 'project-impact-graph.yaml' = + 'project-impact-graph.yaml'; + + /** + * The filename for the last link flag + */ + public static readonly lastLinkFlagFilename: 'last-link' = 'last-link'; + + /** + * The filename for the Rush alerts config file. + */ + public static readonly rushAlertsConfigFilename: 'rush-alerts.json' = 'rush-alerts.json'; + + /** + * The filename for the file that tracks which variant is currently installed. + */ + public static readonly currentVariantsFilename: 'current-variants.json' = 'current-variants.json'; + + /** + * The filename ("rush-hotlink-state.json") used to store information about packages connected via + * "rush link-package" and "rush bridge-package" commands. + */ + public static readonly rushHotlinkStateFilename: 'rush-hotlink-state.json' = 'rush-hotlink-state.json'; + + /** + * The filename ("pnpm-sync.json") used to store the state of the pnpm sync command. + */ + public static readonly pnpmSyncFilename: '.pnpm-sync.json' = '.pnpm-sync.json'; + + /** + * The filename ("pnpm-workspace.yaml") used to store the state of the pnpm workspace configuration. + */ + public static readonly pnpmWorkspaceFileName: 'pnpm-workspace.yaml' = 'pnpm-workspace.yaml'; } diff --git a/libraries/rush-lib/src/logic/SetupChecks.ts b/libraries/rush-lib/src/logic/SetupChecks.ts index 963ed0ae147..b7e9f1ae31e 100644 --- a/libraries/rush-lib/src/logic/SetupChecks.ts +++ b/libraries/rush-lib/src/logic/SetupChecks.ts @@ -1,14 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; +import * as path from 'node:path'; + import * as semver from 'semver'; + import { FileSystem, AlreadyReportedError } from '@rushstack/node-core-library'; -import { PrintUtilities } from '@rushstack/terminal'; +import { Colorize, PrintUtilities } from '@rushstack/terminal'; -import { RushConfiguration } from '../api/RushConfiguration'; -import { RushConstants } from '../logic/RushConstants'; +import type { RushConfiguration } from '../api/RushConfiguration'; +import { RushConstants } from './RushConstants'; // Refuses to run at all if the PNPM version is older than this, because there // are known bugs or missing features in earlier releases. @@ -30,130 +31,135 @@ const MINIMUM_SUPPORTED_PNPM_VERSION: string = '5.0.0'; export class SetupChecks { public static validate(rushConfiguration: RushConfiguration): void { // NOTE: The Node.js version is also checked in rush/src/start.ts - const errorMessage: string | undefined = SetupChecks._validate(rushConfiguration); + const errorMessage: string | undefined = _validate(rushConfiguration); if (errorMessage) { - console.error(colors.red(PrintUtilities.wrapWords(errorMessage))); + // eslint-disable-next-line no-console + console.error(Colorize.red(PrintUtilities.wrapWords(errorMessage))); throw new AlreadyReportedError(); } } +} - private static _validate(rushConfiguration: RushConfiguration): string | undefined { - // Check for outdated tools - if (rushConfiguration.packageManager === 'pnpm') { - if (semver.lt(rushConfiguration.packageManagerToolVersion, MINIMUM_SUPPORTED_PNPM_VERSION)) { - return ( - `The rush.json file requests PNPM version ` + - rushConfiguration.packageManagerToolVersion + - `, but PNPM ${MINIMUM_SUPPORTED_PNPM_VERSION} is the minimum supported by Rush.` - ); - } - } else if (rushConfiguration.packageManager === 'npm') { - if (semver.lt(rushConfiguration.packageManagerToolVersion, MINIMUM_SUPPORTED_NPM_VERSION)) { - return ( - `The rush.json file requests NPM version ` + - rushConfiguration.packageManagerToolVersion + - `, but NPM ${MINIMUM_SUPPORTED_NPM_VERSION} is the minimum supported by Rush.` - ); - } +function _validate(rushConfiguration: RushConfiguration): string | undefined { + // Check for outdated tools + if (rushConfiguration.isPnpm) { + if (semver.lt(rushConfiguration.packageManagerToolVersion, MINIMUM_SUPPORTED_PNPM_VERSION)) { + return ( + `The ${RushConstants.rushJsonFilename} file requests PNPM version ` + + rushConfiguration.packageManagerToolVersion + + `, but PNPM ${MINIMUM_SUPPORTED_PNPM_VERSION} is the minimum supported by Rush.` + ); + } + } else if (rushConfiguration.packageManager === 'npm') { + if (semver.lt(rushConfiguration.packageManagerToolVersion, MINIMUM_SUPPORTED_NPM_VERSION)) { + return ( + `The ${RushConstants.rushJsonFilename} file requests NPM version ` + + rushConfiguration.packageManagerToolVersion + + `, but NPM ${MINIMUM_SUPPORTED_NPM_VERSION} is the minimum supported by Rush.` + ); } - - SetupChecks._checkForPhantomFolders(rushConfiguration); } - private static _checkForPhantomFolders(rushConfiguration: RushConfiguration): void { - const phantomFolders: string[] = []; - const seenFolders: Set = new Set(); - - // Check from the real parent of the common/temp folder - const commonTempParent: string = path.dirname(FileSystem.getRealPath(rushConfiguration.commonTempFolder)); - SetupChecks._collectPhantomFoldersUpwards(commonTempParent, phantomFolders, seenFolders); - - // Check from the real folder containing rush.json - const realRushJsonFolder: string = FileSystem.getRealPath(rushConfiguration.rushJsonFolder); - SetupChecks._collectPhantomFoldersUpwards(realRushJsonFolder, phantomFolders, seenFolders); - - if (phantomFolders.length > 0) { - if (phantomFolders.length === 1) { - console.log( - colors.yellow( - PrintUtilities.wrapWords( - 'Warning: A phantom "node_modules" folder was found. This defeats Rush\'s protection against' + - ' NPM phantom dependencies and may cause confusing build errors. It is recommended to' + - ' delete this folder:' - ) + _checkForPhantomFolders(rushConfiguration); +} + +function _checkForPhantomFolders(rushConfiguration: RushConfiguration): void { + const phantomFolders: string[] = []; + const seenFolders: Set = new Set(); + + // Check from the real parent of the common/temp folder + const commonTempParent: string = path.dirname(FileSystem.getRealPath(rushConfiguration.commonTempFolder)); + _collectPhantomFoldersUpwards(commonTempParent, phantomFolders, seenFolders); + + // Check from the real folder containing rush.json + const realRushJsonFolder: string = FileSystem.getRealPath(rushConfiguration.rushJsonFolder); + _collectPhantomFoldersUpwards(realRushJsonFolder, phantomFolders, seenFolders); + + if (phantomFolders.length > 0) { + if (phantomFolders.length === 1) { + // eslint-disable-next-line no-console + console.log( + Colorize.yellow( + PrintUtilities.wrapWords( + 'Warning: A phantom "node_modules" folder was found. This defeats Rush\'s protection against' + + ' NPM phantom dependencies and may cause confusing build errors. It is recommended to' + + ' delete this folder:' ) - ); - } else { - console.log( - colors.yellow( - PrintUtilities.wrapWords( - 'Warning: Phantom "node_modules" folders were found. This defeats Rush\'s protection against' + - ' NPM phantom dependencies and may cause confusing build errors. It is recommended to' + - ' delete these folders:' - ) + ) + ); + } else { + // eslint-disable-next-line no-console + console.log( + Colorize.yellow( + PrintUtilities.wrapWords( + 'Warning: Phantom "node_modules" folders were found. This defeats Rush\'s protection against' + + ' NPM phantom dependencies and may cause confusing build errors. It is recommended to' + + ' delete these folders:' ) - ); - } - for (const folder of phantomFolders) { - console.log(colors.yellow(`"${folder}"`)); - } - console.log(); // add a newline + ) + ); } + for (const folder of phantomFolders) { + // eslint-disable-next-line no-console + console.log(Colorize.yellow(`"${folder}"`)); + } + // eslint-disable-next-line no-console + console.log(); // add a newline } +} - /** - * Checks "folder" and each of its parents to see if it contains a node_modules folder. - * The bad folders will be added to phantomFolders. - * The seenFolders set is used to avoid duplicates. - */ - private static _collectPhantomFoldersUpwards( - folder: string, - phantomFolders: string[], - seenFolders: Set - ): void { - // Stop if we reached a folder that we already analyzed - while (!seenFolders.has(folder)) { - seenFolders.add(folder); - - // If there is a node_modules folder under this folder, add it to the list of bad folders - const nodeModulesFolder: string = path.join(folder, RushConstants.nodeModulesFolderName); - if (FileSystem.exists(nodeModulesFolder)) { - // Collect the names of files/folders in that node_modules folder - const filenames: string[] = FileSystem.readFolderItemNames(nodeModulesFolder).filter( - (x) => !x.startsWith('.') - ); - - let ignore: boolean = false; - - if (filenames.length === 0) { - // If the node_modules folder is completely empty, then it's not a concern - ignore = true; - } else if (filenames.length === 1 && filenames[0] === 'vso-task-lib') { - // Special case: The Azure DevOps build agent installs the "vso-task-lib" NPM package - // in a top-level path such as: - // - // /home/vsts/work/node_modules/vso-task-lib - // - // It is always the only package in that node_modules folder. The "vso-task-lib" package - // is now deprecated, so it is unlikely to be a real dependency of any modern project. - // To avoid false alarms, we ignore this specific case. - ignore = true; - } - - if (!ignore) { - phantomFolders.push(nodeModulesFolder); - } +/** + * Checks "folder" and each of its parents to see if it contains a node_modules folder. + * The bad folders will be added to phantomFolders. + * The seenFolders set is used to avoid duplicates. + */ +function _collectPhantomFoldersUpwards( + folder: string, + phantomFolders: string[], + seenFolders: Set +): void { + // Stop if we reached a folder that we already analyzed + while (!seenFolders.has(folder)) { + seenFolders.add(folder); + + // If there is a node_modules folder under this folder, add it to the list of bad folders + const nodeModulesFolder: string = path.join(folder, RushConstants.nodeModulesFolderName); + if (FileSystem.exists(nodeModulesFolder)) { + // Collect the names of files/folders in that node_modules folder + const filenames: string[] = FileSystem.readFolderItemNames(nodeModulesFolder).filter( + (x) => !x.startsWith('.') + ); + + let ignore: boolean = false; + + if (filenames.length === 0) { + // If the node_modules folder is completely empty, then it's not a concern + ignore = true; + } else if (filenames.length === 1 && filenames[0] === 'vso-task-lib') { + // Special case: The Azure DevOps build agent installs the "vso-task-lib" NPM package + // in a top-level path such as: + // + // /home/vsts/work/node_modules/vso-task-lib + // + // It is always the only package in that node_modules folder. The "vso-task-lib" package + // is now deprecated, so it is unlikely to be a real dependency of any modern project. + // To avoid false alarms, we ignore this specific case. + ignore = true; } - // Walk upwards - const parentFolder: string = path.dirname(folder); - if (!parentFolder || parentFolder === folder) { - // If path.dirname() returns its own input, then means we reached the root - break; + if (!ignore) { + phantomFolders.push(nodeModulesFolder); } + } - folder = parentFolder; + // Walk upwards + const parentFolder: string = path.dirname(folder); + if (!parentFolder || parentFolder === folder) { + // If path.dirname() returns its own input, then means we reached the root + break; } + + folder = parentFolder; } } diff --git a/libraries/rush-lib/src/logic/ShrinkwrapFileFactory.ts b/libraries/rush-lib/src/logic/ShrinkwrapFileFactory.ts index a1924290acc..1372d8db6c9 100644 --- a/libraries/rush-lib/src/logic/ShrinkwrapFileFactory.ts +++ b/libraries/rush-lib/src/logic/ShrinkwrapFileFactory.ts @@ -1,41 +1,47 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { PackageManagerName } from '../api/packageManager/PackageManager'; -import { PackageManagerOptionsConfigurationBase } from './base/BasePackageManagerOptionsConfiguration'; -import { BaseShrinkwrapFile } from './base/BaseShrinkwrapFile'; +import type { PackageManagerName } from '../api/packageManager/PackageManager'; +import type { BaseShrinkwrapFile } from './base/BaseShrinkwrapFile'; import { NpmShrinkwrapFile } from './npm/NpmShrinkwrapFile'; import { PnpmShrinkwrapFile } from './pnpm/PnpmShrinkwrapFile'; import { YarnShrinkwrapFile } from './yarn/YarnShrinkwrapFile'; +export interface IShrinkwrapFileFactoryOptions { + packageManager: PackageManagerName; + subspaceHasNoProjects: boolean; +} + +export interface IGetShrinkwrapFileOptions extends IShrinkwrapFileFactoryOptions { + shrinkwrapFilePath: string; +} + +export interface IParseShrinkwrapFileOptions extends IShrinkwrapFileFactoryOptions { + shrinkwrapContent: string; +} + export class ShrinkwrapFileFactory { - public static getShrinkwrapFile( - packageManager: PackageManagerName, - packageManagerOptions: PackageManagerOptionsConfigurationBase, - shrinkwrapFilename: string - ): BaseShrinkwrapFile | undefined { + public static getShrinkwrapFile(options: IGetShrinkwrapFileOptions): BaseShrinkwrapFile | undefined { + const { packageManager, shrinkwrapFilePath, subspaceHasNoProjects } = options; switch (packageManager) { case 'npm': - return NpmShrinkwrapFile.loadFromFile(shrinkwrapFilename); + return NpmShrinkwrapFile.loadFromFile(shrinkwrapFilePath); case 'pnpm': - return PnpmShrinkwrapFile.loadFromFile(shrinkwrapFilename); + return PnpmShrinkwrapFile.loadFromFile(shrinkwrapFilePath, { subspaceHasNoProjects }); case 'yarn': - return YarnShrinkwrapFile.loadFromFile(shrinkwrapFilename); + return YarnShrinkwrapFile.loadFromFile(shrinkwrapFilePath); default: throw new Error(`Invalid package manager: ${packageManager}`); } } - public static parseShrinkwrapFile( - packageManager: PackageManagerName, - packageManagerOptions: PackageManagerOptionsConfigurationBase, - shrinkwrapContent: string - ): BaseShrinkwrapFile | undefined { + public static parseShrinkwrapFile(options: IParseShrinkwrapFileOptions): BaseShrinkwrapFile | undefined { + const { packageManager, shrinkwrapContent, subspaceHasNoProjects } = options; switch (packageManager) { case 'npm': return NpmShrinkwrapFile.loadFromString(shrinkwrapContent); case 'pnpm': - return PnpmShrinkwrapFile.loadFromString(shrinkwrapContent); + return PnpmShrinkwrapFile.loadFromString(shrinkwrapContent, { subspaceHasNoProjects }); case 'yarn': return YarnShrinkwrapFile.loadFromString(shrinkwrapContent); default: diff --git a/libraries/rush-lib/src/logic/StandardScriptUpdater.ts b/libraries/rush-lib/src/logic/StandardScriptUpdater.ts index 1f336c9af37..de0f90ad48c 100644 --- a/libraries/rush-lib/src/logic/StandardScriptUpdater.ts +++ b/libraries/rush-lib/src/logic/StandardScriptUpdater.ts @@ -3,7 +3,7 @@ import { FileSystem, Async } from '@rushstack/node-core-library'; -import { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfiguration } from '../api/RushConfiguration'; import { installRunRushScriptFilename, installRunRushxScriptFilename, @@ -11,6 +11,7 @@ import { installRunScriptFilename, scriptsFolderPath } from '../utilities/PathConstants'; +import { RushConstants } from './RushConstants'; const HEADER_LINES_PREFIX: string[] = [ '// THIS FILE WAS GENERATED BY A TOOL. ANY MANUAL MODIFICATIONS WILL GET OVERWRITTEN WHENEVER RUSH IS UPGRADED.', @@ -20,6 +21,9 @@ const HEADER_LINES_PREFIX: string[] = [ const HEADER_LINES_SUFFIX: string[] = [ '//', '// For more information, see: https://rushjs.io/pages/maintainer/setup_new_repo/', + '//', + '// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.', + "// See the @microsoft/rush package's LICENSE file for details.", '' ]; @@ -45,7 +49,7 @@ const _scripts: IScriptSpecifier[] = [ headerLines: [ '// This script is intended for usage in an automated build environment where the Rush command may not have', '// been preinstalled, or may have an unpredictable version. This script will automatically install the version of Rush', - '// specified in the rush.json configuration file (if not already installed), and then pass a command-line to it.', + `// specified in the ${RushConstants.rushJsonFilename} configuration file (if not already installed), and then pass a command-line to it.`, '// An example usage would be:', '//', `// node common/scripts/${installRunRushScriptFilename} install` @@ -56,7 +60,7 @@ const _scripts: IScriptSpecifier[] = [ headerLines: [ '// This script is intended for usage in an automated build environment where the Rush command may not have', '// been preinstalled, or may have an unpredictable version. This script will automatically install the version of Rush', - '// specified in the rush.json configuration file (if not already installed), and then pass a command-line to the', + `// specified in the ${RushConstants.rushJsonFilename} configuration file (if not already installed), and then pass a command-line to the`, '// rushx command.', '//', '// An example usage would be:', @@ -72,7 +76,7 @@ const _pnpmOnlyScripts: IScriptSpecifier[] = [ headerLines: [ '// This script is intended for usage in an automated build environment where the Rush command may not have', '// been preinstalled, or may have an unpredictable version. This script will automatically install the version of Rush', - '// specified in the rush.json configuration file (if not already installed), and then pass a command-line to the', + `// specified in the ${RushConstants.rushJsonFilename} configuration file (if not already installed), and then pass a command-line to the`, '// rush-pnpm command.', '//', '// An example usage would be:', @@ -83,7 +87,7 @@ const _pnpmOnlyScripts: IScriptSpecifier[] = [ ]; const getScripts = (rushConfiguration: RushConfiguration): IScriptSpecifier[] => { - if (rushConfiguration.packageManager === 'pnpm') { + if (rushConfiguration.isPnpm) { return _scripts.concat(_pnpmOnlyScripts); } @@ -106,17 +110,14 @@ export class StandardScriptUpdater { await Async.forEachAsync( getScripts(rushConfiguration), async (script: IScriptSpecifier) => { - const changed: boolean = await StandardScriptUpdater._updateScriptOrThrowAsync( - script, - rushConfiguration, - false - ); + const changed: boolean = await _updateScriptOrThrowAsync(script, rushConfiguration, false); anyChanges ||= changed; }, { concurrency: 10 } ); if (anyChanges) { + // eslint-disable-next-line no-console console.log(); // print a newline after the notices } @@ -131,84 +132,78 @@ export class StandardScriptUpdater { await Async.forEachAsync( getScripts(rushConfiguration), async (script: IScriptSpecifier) => { - await StandardScriptUpdater._updateScriptOrThrowAsync(script, rushConfiguration, true); + await _updateScriptOrThrowAsync(script, rushConfiguration, true); }, { concurrency: 10 } ); } +} - /** - * Compares a single script in the common/script folder to see if it needs to be updated. - * If throwInsteadOfCopy=false, then an outdated or missing script will be recopied; - * otherwise, an exception is thrown. - */ - private static async _updateScriptOrThrowAsync( - script: IScriptSpecifier, - rushConfiguration: RushConfiguration, - throwInsteadOfCopy: boolean - ): Promise { - const targetFilePath: string = `${rushConfiguration.commonScriptsFolder}/${script.scriptName}`; - - // Are the files the same? - let filesAreSame: boolean = false; - - let targetContent: string | undefined; - try { - targetContent = await FileSystem.readFileAsync(targetFilePath); - } catch (e) { - if (!FileSystem.isNotExistError(e)) { - throw e; - } - } - const targetNormalized: string | undefined = targetContent - ? StandardScriptUpdater._normalize(targetContent) - : undefined; - - let sourceNormalized: string; - if (targetNormalized) { - sourceNormalized = await StandardScriptUpdater._getExpectedFileDataAsync(script); - if (sourceNormalized === targetNormalized) { - filesAreSame = true; - } +/** + * Compares a single script in the common/script folder to see if it needs to be updated. + * If throwInsteadOfCopy=false, then an outdated or missing script will be recopied; + * otherwise, an exception is thrown. + */ +async function _updateScriptOrThrowAsync( + script: IScriptSpecifier, + rushConfiguration: RushConfiguration, + throwInsteadOfCopy: boolean +): Promise { + const targetFilePath: string = `${rushConfiguration.commonScriptsFolder}/${script.scriptName}`; + + // Are the files the same? + let filesAreSame: boolean = false; + + let targetContent: string | undefined; + try { + targetContent = await FileSystem.readFileAsync(targetFilePath); + } catch (e) { + if (!FileSystem.isNotExistError(e)) { + throw e; } + } + const targetNormalized: string | undefined = targetContent ? _normalize(targetContent) : undefined; - if (!filesAreSame) { - if (throwInsteadOfCopy) { - throw new Error( - 'The standard files in the "common/scripts" folders need to be updated' + - ' for this Rush version. Please run "rush update" and commit the changes.' - ); - } else { - console.log(`Script is out of date; updating "${targetFilePath}"`); - sourceNormalized ||= await StandardScriptUpdater._getExpectedFileDataAsync(script); - await FileSystem.writeFileAsync(targetFilePath, sourceNormalized); - } + let sourceNormalized: string; + if (targetNormalized) { + sourceNormalized = await _getExpectedFileDataAsync(script); + if (sourceNormalized === targetNormalized) { + filesAreSame = true; } - - return !filesAreSame; } - private static _normalize(content: string): string { - // Ignore newline differences from .gitattributes - return ( - content - .split('\n') - // Ignore trailing whitespace - .map((x) => x.trimRight()) - .join('\n') - ); + if (!filesAreSame) { + if (throwInsteadOfCopy) { + throw new Error( + 'The standard files in the "common/scripts" folders need to be updated' + + ' for this Rush version. Please run "rush update" and commit the changes.' + ); + } else { + // eslint-disable-next-line no-console + console.log(`Script is out of date; updating "${targetFilePath}"`); + sourceNormalized ||= await _getExpectedFileDataAsync(script); + await FileSystem.writeFileAsync(targetFilePath, sourceNormalized); + } } - private static async _getExpectedFileDataAsync({ - scriptName, - headerLines - }: IScriptSpecifier): Promise { - const sourceFilePath: string = `${scriptsFolderPath}/${scriptName}`; - let sourceContent: string = await FileSystem.readFileAsync(sourceFilePath); - sourceContent = [...HEADER_LINES_PREFIX, ...headerLines, ...HEADER_LINES_SUFFIX, sourceContent].join( - '\n' - ); - const sourceNormalized: string = StandardScriptUpdater._normalize(sourceContent); - return sourceNormalized; - } + return !filesAreSame; +} + +function _normalize(content: string): string { + // Ignore newline differences from .gitattributes + return ( + content + .split('\n') + // Ignore trailing whitespace + .map((x) => x.trimRight()) + .join('\n') + ); +} + +async function _getExpectedFileDataAsync({ scriptName, headerLines }: IScriptSpecifier): Promise { + const sourceFilePath: string = `${scriptsFolderPath}/${scriptName}`; + let sourceContent: string = await FileSystem.readFileAsync(sourceFilePath); + sourceContent = [...HEADER_LINES_PREFIX, ...headerLines, ...HEADER_LINES_SUFFIX, sourceContent].join('\n'); + const sourceNormalized: string = _normalize(sourceContent); + return sourceNormalized; } diff --git a/libraries/rush-lib/src/logic/Telemetry.ts b/libraries/rush-lib/src/logic/Telemetry.ts index e786ca10742..8d855cd46a0 100644 --- a/libraries/rush-lib/src/logic/Telemetry.ts +++ b/libraries/rush-lib/src/logic/Telemetry.ts @@ -1,13 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as os from 'os'; -import * as path from 'path'; -import { FileSystem, FileSystemStats, JsonFile } from '@rushstack/node-core-library'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import type { PerformanceEntry } from 'node:perf_hooks'; -import { RushConfiguration } from '../api/RushConfiguration'; +import { FileSystem, type FileSystemStats, JsonFile } from '@rushstack/node-core-library'; + +import type { RushConfiguration } from '../api/RushConfiguration'; import { Rush } from '../api/Rush'; -import { RushSession } from '../pluginFramework/RushSession'; +import type { RushSession } from '../pluginFramework/RushSession'; +import { collectPerformanceEntries } from '../utilities/performance'; /** * @beta @@ -71,6 +74,12 @@ export interface ITelemetryOperationResult { * Duration in milliseconds when the operation does not hit cache */ nonCachedDurationMs?: number; + + /** + * Was this operation built on this machine? If so, the duration can be calculated from `startTimestampMs` and `endTimestampMs`. + * If not, you should use the metrics from the machine that built it. + */ + wasExecutedOnThisMachine?: boolean; } /** @@ -123,6 +132,12 @@ export interface ITelemetryData { readonly operationResults?: Record; readonly extraData?: { [key: string]: string | number | boolean }; + + /** + * Performance marks and measures collected during the execution of this command. + * This is an array of `PerformanceEntry` objects, which can include marks, measures, and function timings. + */ + readonly performanceEntries?: readonly PerformanceEntry[]; } const MAX_FILE_COUNT: number = 100; @@ -135,6 +150,7 @@ export class Telemetry { private _rushConfiguration: RushConfiguration; private _rushSession: RushSession; private _flushAsyncTasks: Set> = new Set(); + private _telemetryStartTime: number = 0; public constructor(rushConfiguration: RushConfiguration, rushSession: RushSession) { this._rushConfiguration = rushConfiguration; @@ -150,14 +166,17 @@ export class Telemetry { if (!this._enabled) { return; } + const cpus: os.CpuInfo[] = os.cpus(); const data: ITelemetryData = { ...telemetryData, + performanceEntries: + telemetryData.performanceEntries || collectPerformanceEntries(this._telemetryStartTime), machineInfo: telemetryData.machineInfo || { machineArchitecture: os.arch(), // The Node.js model is sometimes padded, for example: // "AMD Ryzen 7 3700X 8-Core Processor " - machineCpu: os.cpus()[0].model.trim(), - machineCores: os.cpus().length, + machineCpu: cpus[0].model.trim(), + machineCores: cpus.length, machineTotalMemoryMiB: Math.round(os.totalmem() / ONE_MEGABYTE_IN_BYTES), machineFreeMemoryMiB: Math.round(os.freemem() / ONE_MEGABYTE_IN_BYTES) }, @@ -165,6 +184,7 @@ export class Telemetry { platform: telemetryData.platform || process.platform, rushVersion: telemetryData.rushVersion || Rush.version }; + this._telemetryStartTime = performance.now(); this._store.push(data); } diff --git a/libraries/rush-lib/src/logic/TempProjectHelper.ts b/libraries/rush-lib/src/logic/TempProjectHelper.ts index ce09809bfd5..10c27d2e847 100644 --- a/libraries/rush-lib/src/logic/TempProjectHelper.ts +++ b/libraries/rush-lib/src/logic/TempProjectHelper.ts @@ -1,26 +1,35 @@ -import { FileConstants, FileSystem, PosixModeBits } from '@rushstack/node-core-library'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import type { Stats } from 'node:fs'; + import * as tar from 'tar'; -import * as path from 'path'; -import { RushConfigurationProject } from '../api/RushConfigurationProject'; -import { RushConfiguration } from '../api/RushConfiguration'; +import { FileConstants, FileSystem, PosixModeBits } from '@rushstack/node-core-library'; + +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; +import type { RushConfiguration } from '../api/RushConfiguration'; import { RushConstants } from './RushConstants'; +import type { Subspace } from '../api/Subspace'; // The PosixModeBits are intended to be used with bitwise operations. /* eslint-disable no-bitwise */ export class TempProjectHelper { private _rushConfiguration: RushConfiguration; + private _subspace: Subspace; - public constructor(rushConfiguration: RushConfiguration) { + public constructor(rushConfiguration: RushConfiguration, subspace: Subspace) { this._rushConfiguration = rushConfiguration; + this._subspace = subspace; } /** * Deletes the existing tarball and creates a tarball for the given rush project */ public createTempProjectTarball(rushProject: RushConfigurationProject): void { - FileSystem.ensureFolder(path.resolve(this._rushConfiguration.commonTempFolder, 'projects')); + FileSystem.ensureFolder(path.resolve(this._subspace.getSubspaceTempFolderPath(), 'projects')); const tarballFile: string = this.getTarballFilePath(rushProject); const tempProjectFolder: string = this.getTempProjectFolder(rushProject); @@ -38,7 +47,7 @@ export class TempProjectHelper { noPax: true, sync: true, prefix: npmPackageFolder, - filter: (path: string, stat: tar.FileStat): boolean => { + filter: (tarPath: string, stat: Stats): boolean => { if ( !this._rushConfiguration.experimentsConfiguration.configuration.noChmodFieldInTarHeaderNormalization ) { @@ -58,7 +67,7 @@ export class TempProjectHelper { */ public getTarballFilePath(project: RushConfigurationProject): string { return path.join( - this._rushConfiguration.commonTempFolder, + this._subspace.getSubspaceTempFolderPath(), RushConstants.rushTempProjectsFolderName, `${project.unscopedTempProjectName}.tgz` ); @@ -67,7 +76,7 @@ export class TempProjectHelper { public getTempProjectFolder(rushProject: RushConfigurationProject): string { const unscopedTempProjectName: string = rushProject.unscopedTempProjectName; return path.join( - this._rushConfiguration.commonTempFolder, + this._subspace.getSubspaceTempFolderPath(), RushConstants.rushTempProjectsFolderName, unscopedTempProjectName ); diff --git a/libraries/rush-lib/src/logic/UnlinkManager.ts b/libraries/rush-lib/src/logic/UnlinkManager.ts index 7072117af32..d77a73b97b4 100644 --- a/libraries/rush-lib/src/logic/UnlinkManager.ts +++ b/libraries/rush-lib/src/logic/UnlinkManager.ts @@ -1,14 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; +import * as path from 'node:path'; + import { FileSystem, AlreadyReportedError } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; -import { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfiguration } from '../api/RushConfiguration'; import { Utilities } from '../utilities/Utilities'; import { BaseProjectShrinkwrapFile } from './base/BaseProjectShrinkwrapFile'; -import { LastLinkFlagFactory } from '../api/LastLinkFlag'; +import { FlagFile } from '../api/FlagFile'; +import { RushConstants } from './RushConstants'; /** * This class implements the logic for "rush unlink" @@ -26,12 +28,13 @@ export class UnlinkManager { * * Returns true if anything was deleted. */ - public unlink(force: boolean = false): boolean { + public async unlinkAsync(force: boolean = false): Promise { const useWorkspaces: boolean = this._rushConfiguration.pnpmOptions && this._rushConfiguration.pnpmOptions.useWorkspaces; if (!force && useWorkspaces) { + // eslint-disable-next-line no-console console.log( - colors.red( + Colorize.red( 'Unlinking is not supported when using workspaces. Run "rush purge" to remove ' + 'project node_modules folders.' ) @@ -39,7 +42,11 @@ export class UnlinkManager { throw new AlreadyReportedError(); } - LastLinkFlagFactory.getCommonTempFlag(this._rushConfiguration).clear(); + await new FlagFile( + this._rushConfiguration.defaultSubspace.getSubspaceTempFolderPath(), + RushConstants.lastLinkFlagFilename, + {} + ).clearAsync(); return this._deleteProjectFiles(); } @@ -56,6 +63,7 @@ export class UnlinkManager { for (const rushProject of this._rushConfiguration.projects) { const localModuleFolder: string = path.join(rushProject.projectFolder, 'node_modules'); if (FileSystem.exists(localModuleFolder)) { + // eslint-disable-next-line no-console console.log(`Purging ${localModuleFolder}`); Utilities.dangerouslyDeletePath(localModuleFolder); didDeleteAnything = true; @@ -63,6 +71,7 @@ export class UnlinkManager { const projectShrinkwrapFilePath: string = BaseProjectShrinkwrapFile.getFilePathForProject(rushProject); if (FileSystem.exists(projectShrinkwrapFilePath)) { + // eslint-disable-next-line no-console console.log(`Deleting ${projectShrinkwrapFilePath}`); FileSystem.deleteFile(projectShrinkwrapFilePath); didDeleteAnything = true; diff --git a/libraries/rush-lib/src/logic/VersionManager.ts b/libraries/rush-lib/src/logic/VersionManager.ts index 5170a487070..ac41623f215 100644 --- a/libraries/rush-lib/src/logic/VersionManager.ts +++ b/libraries/rush-lib/src/logic/VersionManager.ts @@ -1,21 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; + import * as semver from 'semver'; -import { IPackageJson, JsonFile, FileConstants, Import } from '@rushstack/node-core-library'; -import { VersionPolicy, BumpType, LockStepVersionPolicy } from '../api/VersionPolicy'; +import { type IPackageJson, JsonFile, FileConstants } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import { type VersionPolicy, type BumpType, LockStepVersionPolicy } from '../api/VersionPolicy'; import { ChangeFile } from '../api/ChangeFile'; -import { ChangeType, IChangeInfo } from '../api/ChangeManagement'; +import { ChangeType, type IChangeInfo } from '../api/ChangeManagement'; import { RushConfiguration } from '../api/RushConfiguration'; -import { RushConfigurationProject } from '../api/RushConfigurationProject'; -import { VersionPolicyConfiguration } from '../api/VersionPolicyConfiguration'; +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; +import type { VersionPolicyConfiguration } from '../api/VersionPolicyConfiguration'; import { PublishUtilities } from './PublishUtilities'; import { ChangeManager } from './ChangeManager'; import { DependencySpecifier } from './DependencySpecifier'; - -const lodash: typeof import('lodash') = Import.lazy('lodash', require); +import { cloneDeep } from '../utilities/objectUtilities'; export class VersionManager { private _rushConfiguration: RushConfiguration; @@ -64,6 +66,7 @@ export class VersionManager { * @param shouldCommit - whether the changes will be written to disk */ public async bumpAsync( + terminal: ITerminal, lockStepVersionPolicyName?: string, bumpType?: BumpType, identifier?: string, @@ -87,13 +90,13 @@ export class VersionManager { this._getManuallyVersionedProjects() ); - await changeManager.loadAsync(this._rushConfiguration.changesFolder); + await changeManager.loadAsync(); if (changeManager.hasChanges()) { changeManager.validateChanges(this._versionPolicyConfiguration); changeManager.apply(!!shouldCommit)!.forEach((packageJson) => { this.updatedProjects.set(packageJson.name, packageJson); }); - await changeManager.updateChangelogAsync(!!shouldCommit); + await changeManager.updateChangelogAsync(terminal, !!shouldCommit); } // Refresh rush configuration again, since we've further modified the package.json files @@ -203,7 +206,7 @@ export class VersionManager { let projectVersionChanged: boolean = true; if (!clonedProject) { - clonedProject = lodash.cloneDeep(rushProject.packageJson); + clonedProject = cloneDeep(rushProject.packageJson); projectVersionChanged = false; } @@ -286,6 +289,7 @@ export class VersionManager { if (dependencies[updatedDependentProjectName]) { if (rushProject.decoupledLocalDependencies.has(updatedDependentProjectName)) { // Skip if cyclic + // eslint-disable-next-line no-console console.log(`Found cyclic ${rushProject.packageName} ${updatedDependentProjectName}`); return; } @@ -341,7 +345,7 @@ export class VersionManager { oldDependencyVersion: string, newDependencyVersion: string ): void { - const oldSpecifier: DependencySpecifier = new DependencySpecifier( + const oldSpecifier: DependencySpecifier = DependencySpecifier.parseWithCache( updatedDependentProject.name, oldDependencyVersion ); diff --git a/libraries/rush-lib/src/logic/WorkspaceCycleDetector.ts b/libraries/rush-lib/src/logic/WorkspaceCycleDetector.ts new file mode 100644 index 00000000000..32f280ea8be --- /dev/null +++ b/libraries/rush-lib/src/logic/WorkspaceCycleDetector.ts @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { AlreadyReportedError } from '@rushstack/node-core-library'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; + +import type { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; +import { RushConstants } from './RushConstants'; + +/** + * Detects cycles in the workspace package dependency graph (i.e., cycles that are not + * broken by `decoupledLocalDependencies`) and reports them as errors. + * + * @remarks + * A cycle means that pnpm would be unable to install the workspace, so it is better to + * fail fast with a clear message rather than let pnpm produce a cryptic error. + * + * The fix is to refactor the code to eliminate the cycle, for example by extracting shared + * code into a new package that both projects can depend on, or by moving code from one project + * to another. `decoupledLocalDependencies` is intended only for the bootstrapping problem + * (e.g. the version of a compiler used to compile itself) and should not be used as a + * general escape hatch for cycles. + */ +export function detectAndReportWorkspaceCycles( + rushConfiguration: RushConfiguration, + terminal: ITerminal +): void { + const cycle: ReadonlyArray | undefined = _findWorkspaceCycle(rushConfiguration.projects); + + if (cycle !== undefined) { + terminal.writeLine(); + terminal.writeLine( + Colorize.red( + 'A cyclic dependency was detected among workspace packages:\n' + + ` ${cycle.join(' -> ')}\n\n` + + `To fix this, refactor the code to eliminate the cycle. For example, extract the shared ` + + `code into a new package that both projects can depend on, or move code from one project ` + + `to another so the dependency only goes in one direction.\n\n` + + `NOTE: The "decoupledLocalDependencies" setting in ${RushConstants.rushJsonFilename} is ` + + `intended only for the bootstrapping problem (for example, the version of a compiler used ` + + `to compile itself). It is not a general solution for cyclic dependencies.` + ) + ); + throw new AlreadyReportedError(); + } +} + +/** + * Finds one cycle in the workspace dependency graph, or returns `undefined` if there are none. + * + * Uses depth-first search with a "currently visiting" set for O(V + E) detection. + * The `visiting` set doubles as the ordered path: ES6 Sets preserve insertion order, + * so iterating it from the cycle-start node yields the cycle without a separate array. + */ +export function _findWorkspaceCycle( + projects: ReadonlyArray +): ReadonlyArray | undefined { + // Nodes that have been fully explored (no cycles reachable from them) + const visited: Set = new Set(); + // Nodes currently on the DFS recursion stack, in insertion order + const visiting: Set = new Set(); + + function dfs(node: RushConfigurationProject): ReadonlyArray | undefined { + if (visited.has(node)) { + return undefined; + } + if (visiting.has(node)) { + // Back-edge found — iterate `visiting` (insertion order) and collect from + // the cycle-start node onward, then close the loop. + const cycleNames: string[] = []; + let found: boolean = false; + for (const n of visiting) { + if (n === node) { + found = true; + } + if (found) { + cycleNames.push(n.packageName); + } + } + cycleNames.push(node.packageName); // close the cycle + return cycleNames; + } + + visiting.add(node); + + for (const dep of node.dependencyProjects) { + const cycle: ReadonlyArray | undefined = dfs(dep); + if (cycle !== undefined) { + return cycle; + } + } + + visiting.delete(node); + visited.add(node); + return undefined; + } + + for (const project of projects) { + if (!visited.has(project)) { + const cycle: ReadonlyArray | undefined = dfs(project); + if (cycle !== undefined) { + return cycle; + } + } + } + + return undefined; +} diff --git a/libraries/rush-lib/src/logic/base/BaseInstallManager.ts b/libraries/rush-lib/src/logic/base/BaseInstallManager.ts index 83f6d67f4b3..fa21aed84c0 100644 --- a/libraries/rush-lib/src/logic/base/BaseInstallManager.ts +++ b/libraries/rush-lib/src/logic/base/BaseInstallManager.ts @@ -1,46 +1,66 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as fetch from 'node-fetch'; -import * as os from 'os'; -import * as path from 'path'; -import * as crypto from 'crypto'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import * as crypto from 'node:crypto'; +import { existsSync } from 'node:fs'; +import { readFile, unlink } from 'node:fs/promises'; + import * as semver from 'semver'; +import { + type ILockfile, + type ILogMessageCallbackOptions, + pnpmSyncGetJsonVersion, + pnpmSyncPrepareAsync +} from 'pnpm-sync-lib'; + import { FileSystem, JsonFile, PosixModeBits, NewlineKind, AlreadyReportedError, - FileSystemStats, - ConsoleTerminalProvider, - Terminal, - ITerminalProvider + type FileSystemStats, + Path, + type FolderItem, + Async } from '@rushstack/node-core-library'; -import { PrintUtilities } from '@rushstack/terminal'; +import { PrintUtilities, Colorize, type ITerminal } from '@rushstack/terminal'; import { ApprovedPackagesChecker } from '../ApprovedPackagesChecker'; -import { AsyncRecycler } from '../../utilities/AsyncRecycler'; -import { BaseShrinkwrapFile } from '../base/BaseShrinkwrapFile'; +import type { AsyncRecycler } from '../../utilities/AsyncRecycler'; +import type { BaseShrinkwrapFile } from './BaseShrinkwrapFile'; import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration'; import { Git } from '../Git'; -import { LastInstallFlag, LastInstallFlagFactory } from '../../api/LastInstallFlag'; -import { LastLinkFlag, LastLinkFlagFactory } from '../../api/LastLinkFlag'; -import { PnpmPackageManager } from '../../api/packageManager/PnpmPackageManager'; -import { PurgeManager } from '../PurgeManager'; -import { RushConfiguration, ICurrentVariantJson } from '../../api/RushConfiguration'; +import { + type LastInstallFlag, + getCommonTempFlag, + type ILastInstallFlagJson +} from '../../api/LastInstallFlag'; +import type { PnpmPackageManager } from '../../api/packageManager/PnpmPackageManager'; +import type { PurgeManager } from '../PurgeManager'; +import type { ICurrentVariantJson, RushConfiguration } from '../../api/RushConfiguration'; import { Rush } from '../../api/Rush'; -import { RushGlobalFolder } from '../../api/RushGlobalFolder'; +import type { RushGlobalFolder } from '../../api/RushGlobalFolder'; import { RushConstants } from '../RushConstants'; import { ShrinkwrapFileFactory } from '../ShrinkwrapFileFactory'; import { Utilities } from '../../utilities/Utilities'; import { InstallHelpers } from '../installManager/InstallHelpers'; import * as PolicyValidator from '../policy/PolicyValidator'; -import { WebClient, WebClientResponse } from '../../utilities/WebClient'; +import type { WebClient as WebClientType, IWebClientResponse } from '../../utilities/WebClient'; import { SetupPackageRegistry } from '../setup/SetupPackageRegistry'; import { PnpmfileConfiguration } from '../pnpm/PnpmfileConfiguration'; import type { IInstallManagerOptions } from './BaseInstallManagerTypes'; +import { isVariableSetInNpmrcFile } from '../../utilities/npmrcUtilities'; +import type { PnpmResolutionMode } from '../pnpm/PnpmOptionsConfiguration'; +import { SubspacePnpmfileConfiguration } from '../pnpm/SubspacePnpmfileConfiguration'; +import type { Subspace } from '../../api/Subspace'; +import { ProjectImpactGraphGenerator } from '../ProjectImpactGraphGenerator'; +import { FlagFile } from '../../api/FlagFile'; +import { PnpmSyncUtilities } from '../../utilities/PnpmSyncUtilities'; +import { HotlinkManager } from '../../utilities/HotlinkManager'; +import { detectAndReportWorkspaceCycles } from '../WorkspaceCycleDetector'; /** * Pnpm don't support --ignore-compatibility-db, so use --config.ignoreCompatibilityDb for now. @@ -49,21 +69,24 @@ export const pnpmIgnoreCompatibilityDbParameter: string = '--config.ignoreCompat const pnpmCacheDirParameter: string = '--config.cacheDir'; const pnpmStateDirParameter: string = '--config.stateDir'; +const gitLfsHooks: ReadonlySet = new Set(['post-checkout', 'post-commit', 'post-merge', 'pre-push']); + /** * This class implements common logic between "rush install" and "rush update". */ export abstract class BaseInstallManager { - private readonly _commonTempLinkFlag: LastLinkFlag; + private readonly _commonTempLinkFlag: FlagFile; private _npmSetupValidated: boolean = false; private _syncNpmrcAlreadyCalled: boolean = false; - private readonly _terminalProvider: ITerminalProvider; - private readonly _terminal: Terminal; + protected readonly _terminal: ITerminal; protected readonly rushConfiguration: RushConfiguration; protected readonly rushGlobalFolder: RushGlobalFolder; protected readonly installRecycler: AsyncRecycler; protected readonly options: IInstallManagerOptions; + // Mapping of subspaceName -> LastInstallFlag + protected readonly subspaceInstallFlags: Map; public constructor( rushConfiguration: RushConfiguration, @@ -71,27 +94,39 @@ export abstract class BaseInstallManager { purgeManager: PurgeManager, options: IInstallManagerOptions ) { + this._terminal = options.terminal; this.rushConfiguration = rushConfiguration; this.rushGlobalFolder = rushGlobalFolder; this.installRecycler = purgeManager.commonTempFolderRecycler; this.options = options; - this._commonTempLinkFlag = LastLinkFlagFactory.getCommonTempFlag(rushConfiguration); + this._commonTempLinkFlag = new FlagFile( + options.subspace.getSubspaceTempFolderPath(), + RushConstants.lastLinkFlagFilename, + {} + ); - this._terminalProvider = new ConsoleTerminalProvider(); - this._terminal = new Terminal(this._terminalProvider); + this.subspaceInstallFlags = new Map(); + if (rushConfiguration.subspacesFeatureEnabled) { + for (const subspace of rushConfiguration.subspaces) { + this.subspaceInstallFlags.set(subspace.subspaceName, getCommonTempFlag(rushConfiguration, subspace)); + } + } } public async doInstallAsync(): Promise { - const isFilteredInstall: boolean = this.options.pnpmFilterArguments.length > 0; + const { allowShrinkwrapUpdates, selectedProjects, pnpmFilterArgumentValues, resolutionOnly, variant } = + this.options; + const isFilteredInstall: boolean = pnpmFilterArgumentValues.length > 0; const useWorkspaces: boolean = this.rushConfiguration.pnpmOptions && this.rushConfiguration.pnpmOptions.useWorkspaces; - // Prevent filtered installs when workspaces is disabled if (isFilteredInstall && !useWorkspaces) { + // eslint-disable-next-line no-console console.log(); + // eslint-disable-next-line no-console console.log( - colors.red( + Colorize.red( 'Project filtering arguments can only be used when running in a workspace environment. Run the ' + 'command again without specifying these arguments.' ) @@ -100,174 +135,329 @@ export abstract class BaseInstallManager { } // Prevent update when using a filter, as modifications to the shrinkwrap shouldn't be saved - if (this.options.allowShrinkwrapUpdates && isFilteredInstall) { - console.log(); - console.log( - colors.red( - 'Project filtering arguments cannot be used when running "rush update". Run the command again ' + - 'without specifying these arguments.' - ) - ); - throw new AlreadyReportedError(); + if (allowShrinkwrapUpdates && isFilteredInstall) { + // Allow partial update when there are subspace projects + if (!this.rushConfiguration.subspacesFeatureEnabled) { + // eslint-disable-next-line no-console + console.log(); + // eslint-disable-next-line no-console + console.log( + Colorize.red( + 'Project filtering arguments cannot be used when running "rush update". Run the command again ' + + 'without specifying these arguments.' + ) + ); + throw new AlreadyReportedError(); + } } - const { shrinkwrapIsUpToDate, variantIsUpToDate, npmrcHash } = await this.prepareAsync(); + const subspace: Subspace = this.options.subspace; + + const projectImpactGraphGenerator: ProjectImpactGraphGenerator | undefined = this.rushConfiguration + .experimentsConfiguration.configuration.generateProjectImpactGraphDuringRushUpdate + ? new ProjectImpactGraphGenerator(this._terminal, this.rushConfiguration) + : undefined; + const { shrinkwrapIsUpToDate, npmrcHash, projectImpactGraphIsUpToDate, variantIsUpToDate } = + await this.prepareAsync(subspace, variant, projectImpactGraphGenerator); if (this.options.checkOnly) { return; } - console.log('\n' + colors.bold(`Checking installation in "${this.rushConfiguration.commonTempFolder}"`)); + // eslint-disable-next-line no-console + console.log('\n' + Colorize.bold(`Checking installation in "${subspace.getSubspaceTempFolderPath()}"`)); // This marker file indicates that the last "rush install" completed successfully. // Always perform a clean install if filter flags were provided. Additionally, if // "--purge" was specified, or if the last install was interrupted, then we will // need to perform a clean install. Otherwise, we can do an incremental install. - const commonTempInstallFlag: LastInstallFlag = LastInstallFlagFactory.getCommonTempFlag( - this.rushConfiguration, - { npmrcHash: npmrcHash || '' } - ); - const optionsToIgnore: string[] | undefined = !this.rushConfiguration.experimentsConfiguration - .configuration.cleanInstallAfterNpmrcChanges + const commonTempInstallFlag: LastInstallFlag = getCommonTempFlag(this.rushConfiguration, subspace, { + npmrcHash: npmrcHash || '' + }); + if (isFilteredInstall && selectedProjects) { + const selectedProjectNames: string[] = []; + for (const { packageName } of selectedProjects) { + selectedProjectNames.push(packageName); + } + + selectedProjectNames.sort(); + // Get the projects involved in this filtered install + commonTempInstallFlag.mergeFromObject({ + selectedProjectNames + }); + } + const optionsToIgnore: (keyof ILastInstallFlagJson)[] | undefined = !this.rushConfiguration + .experimentsConfiguration.configuration.cleanInstallAfterNpmrcChanges ? ['npmrcHash'] // If the "cleanInstallAfterNpmrcChanges" experiment is disabled, ignore the npmrcHash : undefined; - const cleanInstall: boolean = - isFilteredInstall || - !commonTempInstallFlag.checkValidAndReportStoreIssues({ - rushVerb: this.options.allowShrinkwrapUpdates ? 'update' : 'install', - statePropertiesToIgnore: optionsToIgnore - }); + const cleanInstall: boolean = !(await commonTempInstallFlag.checkValidAndReportStoreIssuesAsync({ + rushVerb: allowShrinkwrapUpdates ? 'update' : 'install', + statePropertiesToIgnore: optionsToIgnore + })); + + const hotlinkManager: HotlinkManager = HotlinkManager.loadFromRushConfiguration(this.rushConfiguration); + const wasNodeModulesModifiedOutsideInstallation: boolean = await hotlinkManager.purgeLinksAsync( + this._terminal, + subspace.subspaceName + ); // Allow us to defer the file read until we need it - const canSkipInstall: () => boolean = () => { + const canSkipInstallAsync: () => Promise = async () => { // Based on timestamps, can we skip this install entirely? - const outputStats: FileSystemStats = FileSystem.getStatistics(commonTempInstallFlag.path); - return this.canSkipInstall(outputStats.mtime); + const outputStats: FileSystemStats = await FileSystem.getStatisticsAsync(commonTempInstallFlag.path); + return this.canSkipInstallAsync(outputStats.mtime, subspace, variant); }; - if (cleanInstall || !shrinkwrapIsUpToDate || !variantIsUpToDate || !canSkipInstall()) { + if ( + resolutionOnly || + cleanInstall || + wasNodeModulesModifiedOutsideInstallation || + !variantIsUpToDate || + !shrinkwrapIsUpToDate || + !(await canSkipInstallAsync()) || + !projectImpactGraphIsUpToDate + ) { + // eslint-disable-next-line no-console console.log(); - await this.validateNpmSetup(); - - let publishedRelease: boolean | undefined; - try { - publishedRelease = await this._checkIfReleaseIsPublished(); - } catch { - // If the user is working in an environment that can't reach the registry, - // don't bother them with errors. - } + await this.validateNpmSetupAsync(); + + if (!this.rushConfiguration.rushConfigurationJson.suppressRushIsPublicVersionCheck) { + let publishedRelease: boolean | undefined; + try { + publishedRelease = await this._checkIfReleaseIsPublishedAsync(); + } catch { + // If the user is working in an environment that can't reach the registry, + // don't bother them with errors. + } - if (publishedRelease === false) { - console.log( - colors.yellow('Warning: This release of the Rush tool was unpublished; it may be unstable.') - ); + if (publishedRelease === false) { + // eslint-disable-next-line no-console + console.log( + Colorize.yellow('Warning: This release of the Rush tool was unpublished; it may be unstable.') + ); + } } - // Delete the successful install file to indicate the install transaction has started - commonTempInstallFlag.clear(); + if (!resolutionOnly) { + // Delete the successful install file to indicate the install transaction has started + await commonTempInstallFlag.clearAsync(); - // Since we're going to be tampering with common/node_modules, delete the "rush link" flag file if it exists; - // this ensures that a full "rush link" is required next time - this._commonTempLinkFlag.clear(); + // Since we're going to be tampering with common/node_modules, delete the "rush link" flag file if it exists; + // this ensures that a full "rush link" is required next time + await this._commonTempLinkFlag.clearAsync(); + } // Give plugins an opportunity to act before invoking the installation process if (this.options.beforeInstallAsync !== undefined) { - await this.options.beforeInstallAsync(); + await this.options.beforeInstallAsync(subspace); } - // Perform the actual install - await this.installAsync(cleanInstall); + await Promise.all([ + // Perform the actual install + this.installAsync(cleanInstall, subspace), + // If allowed, generate the project impact graph + allowShrinkwrapUpdates ? projectImpactGraphGenerator?.generateAsync() : undefined + ]); if (this.options.allowShrinkwrapUpdates && !shrinkwrapIsUpToDate) { + const shrinkwrapFilePath: string = subspace.getCommittedShrinkwrapFilePath(variant); + const shrinkwrapFile: BaseShrinkwrapFile | undefined = ShrinkwrapFileFactory.getShrinkwrapFile({ + packageManager: this.rushConfiguration.packageManager, + shrinkwrapFilePath, + subspaceHasNoProjects: subspace.getProjects().length === 0 + }); + shrinkwrapFile?.validateShrinkwrapAfterUpdate(this.rushConfiguration, subspace, this._terminal); // Copy (or delete) common\temp\pnpm-lock.yaml --> common\config\rush\pnpm-lock.yaml - Utilities.syncFile( - this.rushConfiguration.tempShrinkwrapFilename, - this.rushConfiguration.getCommittedShrinkwrapFilename(this.options.variant) - ); + Utilities.syncFile(subspace.getTempShrinkwrapFilename(), shrinkwrapFilePath); } else { // TODO: Validate whether the package manager updated it in a nontrivial way } // Always update the state file if running "rush update" if (this.options.allowShrinkwrapUpdates) { - if (this.rushConfiguration.getRepoState(this.options.variant).refreshState(this.rushConfiguration)) { + if (subspace.getRepoState().refreshState(this.rushConfiguration, subspace, variant)) { + // eslint-disable-next-line no-console console.log( - colors.yellow( + Colorize.yellow( `${RushConstants.repoStateFilename} has been modified and must be committed to source control.` ) ); } } } else { + // eslint-disable-next-line no-console console.log('Installation is already up-to-date.'); } - // Create the marker file to indicate a successful install if it's not a filtered install - if (!isFilteredInstall) { - commonTempInstallFlag.create(); + const { configuration: experiments } = this.rushConfiguration.experimentsConfiguration; + // if usePnpmSyncForInjectedDependencies is true + // the pnpm-sync will generate the pnpm-sync.json based on lockfile + if (this.rushConfiguration.isPnpm && experiments?.usePnpmSyncForInjectedDependencies) { + const pnpmLockfilePath: string = subspace.getTempShrinkwrapFilename(); + const dotPnpmFolder: string = `${subspace.getSubspaceTempFolderPath()}/node_modules/.pnpm`; + const modulesFilePath: string = `${subspace.getSubspaceTempFolderPath()}/node_modules/.modules.yaml`; + + // we have an edge case here + // if a package.json has no dependencies, pnpm will still generate the pnpm-lock.yaml but not .pnpm folder + // so we need to make sure pnpm-lock.yaml and .pnpm exists before calling the pnpmSync APIs + if ( + (await FileSystem.existsAsync(pnpmLockfilePath)) && + (await FileSystem.existsAsync(dotPnpmFolder)) && + (await FileSystem.existsAsync(modulesFilePath)) + ) { + await pnpmSyncPrepareAsync({ + lockfilePath: pnpmLockfilePath, + dotPnpmFolder, + lockfileId: subspace.subspaceName, + ensureFolderAsync: FileSystem.ensureFolderAsync.bind(FileSystem), + // eslint-disable-next-line @typescript-eslint/naming-convention + readPnpmLockfile: async (lockfilePath: string, options): Promise => { + const pnpmLockFolder: string = path.dirname(lockfilePath); + + // TODO: Rework this to pre-parse out the version first, then load + // the relevant `@rushstack/rush-pnpm-kit-*` package. + const { lockfileFs: lockfileFsV9 } = await import('@rushstack/rush-pnpm-kit-v9'); + const lockfileV9: ILockfile | null = (await lockfileFsV9.readWantedLockfile( + pnpmLockFolder, + options + // TODO: pnpm-sync-lib.d.ts was at some point generalized to support multiple lockfile formats, + // however its API still returns a single "ILockfile" that is incompatible with the newer interfaces + )) as ILockfile | null; + + if (lockfileV9?.lockfileVersion.toString().startsWith('9')) { + return lockfileV9; + } + + const { lockfileFs: lockfileFsV6 } = await import('@rushstack/rush-pnpm-kit-v8'); + const lockfileV6: ILockfile | null = await lockfileFsV6.readWantedLockfile( + pnpmLockFolder, + options + ); + + if (lockfileV6?.lockfileVersion.toString().startsWith('6')) { + return lockfileV6; + } + + return undefined; + }, + logMessageCallback: (logMessageOptions: ILogMessageCallbackOptions) => + PnpmSyncUtilities.processLogMessage(logMessageOptions, this._terminal) + }); + } + + // clean up the out of date .pnpm-sync.json + for (const rushProject of subspace.getProjects()) { + const pnpmSyncJsonPath: string = `${rushProject.projectFolder}/${RushConstants.nodeModulesFolderName}/${RushConstants.pnpmSyncFilename}`; + if (!existsSync(pnpmSyncJsonPath)) { + continue; + } + + let existingPnpmSyncJsonFile: { version: string } | undefined; + try { + existingPnpmSyncJsonFile = JSON.parse((await readFile(pnpmSyncJsonPath)).toString()); + if (existingPnpmSyncJsonFile?.version !== pnpmSyncGetJsonVersion()) { + await unlink(pnpmSyncJsonPath); + } + } catch (e) { + await unlink(pnpmSyncJsonPath); + } + } } // Perform any post-install work the install manager requires - await this.postInstallAsync(); + await this.postInstallAsync(subspace); + if (!resolutionOnly) { + // Create the marker file to indicate a successful install + await commonTempInstallFlag.createAsync(); + } + + // Give plugins an opportunity to act after a successful install + if (this.options.afterInstallAsync !== undefined) { + await this.options.afterInstallAsync(subspace); + } + + // eslint-disable-next-line no-console console.log(''); } protected abstract prepareCommonTempAsync( + subspace: Subspace, shrinkwrapFile: BaseShrinkwrapFile | undefined ): Promise<{ shrinkwrapIsUpToDate: boolean; shrinkwrapWarnings: string[] }>; - protected abstract installAsync(cleanInstall: boolean): Promise; + protected abstract installAsync(cleanInstall: boolean, subspace: Subspace): Promise; - protected abstract postInstallAsync(): Promise; + protected abstract postInstallAsync(subspace: Subspace): Promise; - protected canSkipInstall(lastModifiedDate: Date): boolean { + protected async canSkipInstallAsync( + lastModifiedDate: Date, + subspace: Subspace, + variant: string | undefined + ): Promise { // Based on timestamps, can we skip this install entirely? const potentiallyChangedFiles: string[] = []; // Consider the timestamp on the node_modules folder; if someone tampered with it // or deleted it entirely, then we can't skip this install potentiallyChangedFiles.push( - path.join(this.rushConfiguration.commonTempFolder, RushConstants.nodeModulesFolderName) + path.join(subspace.getSubspaceTempFolderPath(), RushConstants.nodeModulesFolderName) ); // Additionally, if they pulled an updated shrinkwrap file from Git, // then we can't skip this install - potentiallyChangedFiles.push(this.rushConfiguration.getCommittedShrinkwrapFilename(this.options.variant)); + potentiallyChangedFiles.push(subspace.getCommittedShrinkwrapFilePath(variant)); // Add common-versions.json file to the potentially changed files list. - potentiallyChangedFiles.push(this.rushConfiguration.getCommonVersionsFilePath(this.options.variant)); + potentiallyChangedFiles.push(subspace.getCommonVersionsFilePath(variant)); + + // Add pnpm-config.json file to the potentially changed files list. + potentiallyChangedFiles.push(subspace.getPnpmConfigFilePath()); - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { // If the repo is using pnpmfile.js, consider that also - const pnpmFileFilename: string = this.rushConfiguration.getPnpmfilePath(this.options.variant); + const pnpmFileFilePath: string = subspace.getPnpmfilePath(variant); + const pnpmFileExists: boolean = await FileSystem.existsAsync(pnpmFileFilePath); - if (FileSystem.exists(pnpmFileFilename)) { - potentiallyChangedFiles.push(pnpmFileFilename); + if (pnpmFileExists) { + potentiallyChangedFiles.push(pnpmFileFilePath); } } - return Utilities.isFileTimestampCurrent(lastModifiedDate, potentiallyChangedFiles); + return await Utilities.isFileTimestampCurrentAsync(lastModifiedDate, potentiallyChangedFiles); } - protected async prepareAsync(): Promise<{ - variantIsUpToDate: boolean; + protected async prepareAsync( + subspace: Subspace, + variant: string | undefined, + projectImpactGraphGenerator: ProjectImpactGraphGenerator | undefined + ): Promise<{ shrinkwrapIsUpToDate: boolean; npmrcHash: string | undefined; + projectImpactGraphIsUpToDate: boolean; + variantIsUpToDate: boolean; }> { + const terminal: ITerminal = this._terminal; + const { allowShrinkwrapUpdates } = this.options; + // Check the policies - await PolicyValidator.validatePolicyAsync(this.rushConfiguration, this.options); + await PolicyValidator.validatePolicyAsync(this.rushConfiguration, subspace, variant, this.options); - this._installGitHooks(); + // Fail fast if there are undeclared cycles in the workspace package dependency graph. + // Pnpm cannot install a workspace with cycles, so this gives a clearer error message + // than whatever pnpm would emit. + detectAndReportWorkspaceCycles(this.rushConfiguration, terminal); + + await this._installGitHooksAsync(); const approvedPackagesChecker: ApprovedPackagesChecker = new ApprovedPackagesChecker( this.rushConfiguration ); if (approvedPackagesChecker.approvedPackagesFilesAreOutOfDate) { - if (this.options.allowShrinkwrapUpdates) { - approvedPackagesChecker.rewriteConfigFiles(); - console.log( - colors.yellow( + approvedPackagesChecker.rewriteConfigFiles(); + if (allowShrinkwrapUpdates) { + terminal.writeLine( + Colorize.yellow( 'Approved package files have been updated. These updates should be committed to source control' ) ); @@ -277,7 +467,7 @@ export abstract class BaseInstallManager { } // Ensure that the package manager is installed - await InstallHelpers.ensureLocalPackageManager( + await InstallHelpers.ensureLocalPackageManagerAsync( this.rushConfiguration, this.rushGlobalFolder, this.options.maxInstallAttempts @@ -287,21 +477,22 @@ export abstract class BaseInstallManager { // (If it's a full update, then we ignore the shrinkwrap from Git since it will be overwritten) if (!this.options.fullUpgrade) { + const shrinkwrapFilePath: string = subspace.getCommittedShrinkwrapFilePath(variant); try { - shrinkwrapFile = ShrinkwrapFileFactory.getShrinkwrapFile( - this.rushConfiguration.packageManager, - this.rushConfiguration.packageManagerOptions, - this.rushConfiguration.getCommittedShrinkwrapFilename(this.options.variant) - ); + shrinkwrapFile = ShrinkwrapFileFactory.getShrinkwrapFile({ + packageManager: this.rushConfiguration.packageManager, + shrinkwrapFilePath, + subspaceHasNoProjects: subspace.getProjects().length === 0 + }); } catch (ex) { - console.log(); - console.log( + terminal.writeLine(); + terminal.writeLine( `Unable to load the ${this.rushConfiguration.shrinkwrapFilePhrase}: ${(ex as Error).message}` ); - if (!this.options.allowShrinkwrapUpdates) { - console.log(); - console.log(colors.red('You need to run "rush update" to fix this problem')); + if (!allowShrinkwrapUpdates) { + terminal.writeLine(); + terminal.writeLine(Colorize.red('You need to run "rush update" to fix this problem')); throw new AlreadyReportedError(); } @@ -311,68 +502,159 @@ export abstract class BaseInstallManager { // Write a file indicating which variant is being installed. // This will be used by bulk scripts to determine the correct Shrinkwrap file to track. - const currentVariantJsonFilename: string = this.rushConfiguration.currentVariantJsonFilename; + const currentVariantJsonFilePath: string = this.rushConfiguration.currentVariantJsonFilePath; const currentVariantJson: ICurrentVariantJson = { - variant: this.options.variant || null + variant: variant ?? null }; // Determine if the variant is already current by updating current-variant.json. // If nothing is written, the variant has not changed. - const variantIsUpToDate: boolean = !JsonFile.save(currentVariantJson, currentVariantJsonFilename, { - onlyIfChanged: true - }); + const variantIsUpToDate: boolean = !(await JsonFile.saveAsync( + currentVariantJson, + currentVariantJsonFilePath, + { + onlyIfChanged: true + } + )); + this.rushConfiguration._currentVariantJsonLoadingPromise = undefined; if (this.options.variant) { - console.log(); - console.log(colors.bold(`Using variant '${this.options.variant}' for installation.`)); - } else if (!variantIsUpToDate && !this.options.variant) { - console.log(); - console.log(colors.bold('Using the default variant for installation.')); + terminal.writeLine(); + terminal.writeLine(Colorize.bold(`Using variant '${this.options.variant}' for installation.`)); + } else if (!variantIsUpToDate && !variant && this.rushConfiguration.variants.size > 0) { + terminal.writeLine(); + terminal.writeLine(Colorize.bold('Using the default variant for installation.')); + } + + const extraNpmrcLines: string[] = []; + if (this.rushConfiguration.subspacesFeatureEnabled) { + // Look for a monorepo level .npmrc file + const commonNpmrcPath: string = `${this.rushConfiguration.commonRushConfigFolder}/.npmrc`; + let commonNpmrcFileLines: string[] | undefined; + try { + commonNpmrcFileLines = (await FileSystem.readFileAsync(commonNpmrcPath)).split('\n'); + } catch (e) { + if (!FileSystem.isNotExistError(e)) { + throw e; + } + } + + if (commonNpmrcFileLines) { + extraNpmrcLines.push(...commonNpmrcFileLines); + } + + // NOTE: pnpm 11+ only reads auth/registry settings from .npmrc, so it ignores this line; + // for pnpm 11+ the global pnpmfile path is emitted via the generated pnpm-workspace.yaml + // instead (see InstallHelpers.resolvePnpmSettings). The line is kept for pnpm 10 and earlier. + extraNpmrcLines.push( + `global-pnpmfile=${subspace.getSubspaceTempFolderPath()}/${RushConstants.pnpmfileGlobalFilename}` + ); } // Also copy down the committed .npmrc file, if there is one // "common\config\rush\.npmrc" --> "common\temp\.npmrc" // Also ensure that we remove any old one that may be hanging around - const npmrcText: string | undefined = Utilities.syncNpmrc( - this.rushConfiguration.commonRushConfigFolder, - this.rushConfiguration.commonTempFolder - ); + const npmrcText: string | undefined = Utilities.syncNpmrc({ + sourceNpmrcFolder: subspace.getSubspaceConfigFolderPath(), + targetNpmrcFolder: subspace.getSubspaceTempFolderPath(), + linesToPrepend: extraNpmrcLines, + createIfMissing: this.rushConfiguration.subspacesFeatureEnabled, + supportEnvVarFallbackSyntax: this.rushConfiguration.isPnpm + }); this._syncNpmrcAlreadyCalled = true; const npmrcHash: string | undefined = npmrcText ? crypto.createHash('sha1').update(npmrcText).digest('hex') : undefined; - // Copy the committed patches folder if using pnpm - if (this.rushConfiguration.packageManager === 'pnpm') { - const commonTempPnpmPatchesFolder: string = `${this.rushConfiguration.commonTempFolder}/${RushConstants.pnpmPatchesFolderName}`; - const rushPnpmPatchesFolder: string = `${this.rushConfiguration.commonFolder}/pnpm-${RushConstants.pnpmPatchesFolderName}`; - if (FileSystem.exists(rushPnpmPatchesFolder)) { - FileSystem.copyFiles({ - sourcePath: rushPnpmPatchesFolder, - destinationPath: commonTempPnpmPatchesFolder - }); + if (this.rushConfiguration.isPnpm) { + // Copy the committed patches folder if using pnpm + const commonTempPnpmPatchesFolder: string = `${subspace.getSubspaceTempFolderPath()}/${ + RushConstants.pnpmPatchesFolderName + }`; + const rushPnpmPatchesFolder: string = subspace.getSubspacePnpmPatchesFolderPath(); + let rushPnpmPatches: FolderItem[] | undefined; + try { + rushPnpmPatches = await FileSystem.readFolderItemsAsync(rushPnpmPatchesFolder); + } catch (e) { + if (!FileSystem.isNotExistError(e)) { + throw e; + } + } + + if (rushPnpmPatches) { + await FileSystem.ensureFolderAsync(commonTempPnpmPatchesFolder); + const existingPatches: FolderItem[] = + await FileSystem.readFolderItemsAsync(commonTempPnpmPatchesFolder); + const copiedPatchNames: Set = new Set(); + await Async.forEachAsync( + rushPnpmPatches, + async (patch: FolderItem) => { + const name: string = patch.name; + const sourcePath: string = `${rushPnpmPatchesFolder}/${name}`; + if (patch.isFile()) { + await FileSystem.copyFileAsync({ + sourcePath, + destinationPath: `${commonTempPnpmPatchesFolder}/${name}` + }); + copiedPatchNames.add(name); + } else { + throw new Error(`Unexpected non-file item found in ${rushPnpmPatchesFolder}: ${sourcePath}`); + } + }, + { concurrency: 50 } + ); + + await Async.forEachAsync( + existingPatches, + async (patch: FolderItem) => { + const name: string = patch.name; + if (!copiedPatchNames.has(name)) { + await FileSystem.deleteFileAsync(`${commonTempPnpmPatchesFolder}/${name}`); + } + }, + { concurrency: 50 } + ); + } else { + await FileSystem.deleteFolderAsync(commonTempPnpmPatchesFolder); } } - // Shim support for pnpmfile in. This shim will call back into the variant-specific pnpmfile. + // Shim support for pnpmfile in. // Additionally when in workspaces, the shim implements support for common versions. - if (this.rushConfiguration.packageManager === 'pnpm') { - await PnpmfileConfiguration.writeCommonTempPnpmfileShimAsync(this.rushConfiguration, this.options); + if (this.rushConfiguration.isPnpm) { + await PnpmfileConfiguration.writeCommonTempPnpmfileShimAsync( + this.rushConfiguration, + subspace.getSubspaceTempFolderPath(), + subspace, + variant + ); + + if (this.rushConfiguration.subspacesFeatureEnabled) { + await SubspacePnpmfileConfiguration.writeCommonTempSubspaceGlobalPnpmfileAsync( + this.rushConfiguration, + subspace, + variant + ); + } } - // Allow for package managers to do their own preparation and check that the shrinkwrap is up to date // eslint-disable-next-line prefer-const - let { shrinkwrapIsUpToDate, shrinkwrapWarnings } = await this.prepareCommonTempAsync(shrinkwrapFile); + let [{ shrinkwrapIsUpToDate, shrinkwrapWarnings }, projectImpactGraphIsUpToDate = true] = + await Promise.all([ + // Allow for package managers to do their own preparation and check that the shrinkwrap is up to date + this.prepareCommonTempAsync(subspace, shrinkwrapFile), + projectImpactGraphGenerator?.validateAsync() + ]); shrinkwrapIsUpToDate = shrinkwrapIsUpToDate && !this.options.recheckShrinkwrap; - this._syncTempShrinkwrap(shrinkwrapFile); + this._syncTempShrinkwrap(subspace, variant, shrinkwrapFile); // Write out the reported warnings if (shrinkwrapWarnings.length > 0) { - console.log(); - console.log( - colors.yellow( + terminal.writeLine(); + terminal.writeLine( + Colorize.yellow( PrintUtilities.wrapWords( `The ${this.rushConfiguration.shrinkwrapFilePhrase} contains the following issues:` ) @@ -380,31 +662,43 @@ export abstract class BaseInstallManager { ); for (const shrinkwrapWarning of shrinkwrapWarnings) { - console.log(colors.yellow(' ' + shrinkwrapWarning)); + terminal.writeLine(Colorize.yellow(' ' + shrinkwrapWarning)); } - console.log(); + + terminal.writeLine(); } + let hasErrors: boolean = false; // Force update if the shrinkwrap is out of date - if (!shrinkwrapIsUpToDate) { - if (!this.options.allowShrinkwrapUpdates) { - console.log(); - console.log( - colors.red( - `The ${this.rushConfiguration.shrinkwrapFilePhrase} is out of date. You need to run "rush update".` - ) - ); - throw new AlreadyReportedError(); - } + if (!shrinkwrapIsUpToDate && !allowShrinkwrapUpdates) { + terminal.writeErrorLine(); + terminal.writeErrorLine( + `The ${this.rushConfiguration.shrinkwrapFilePhrase} is out of date. You need to run "rush update".` + ); + hasErrors = true; + } + + if (!projectImpactGraphIsUpToDate && !allowShrinkwrapUpdates) { + hasErrors = true; + terminal.writeErrorLine(); + terminal.writeErrorLine( + Colorize.red( + `The ${RushConstants.projectImpactGraphFilename} file is missing or out of date. You need to run "rush update".` + ) + ); + } + + if (hasErrors) { + throw new AlreadyReportedError(); } - return { shrinkwrapIsUpToDate, variantIsUpToDate, npmrcHash }; + return { shrinkwrapIsUpToDate, npmrcHash, projectImpactGraphIsUpToDate, variantIsUpToDate }; } /** * Git hooks are only installed if the repo opts in by including files in /common/git-hooks */ - private _installGitHooks(): void { + private async _installGitHooksAsync(): Promise { const hookSource: string = path.join(this.rushConfiguration.commonFolder, 'git-hooks'); const git: Git = new Git(this.rushConfiguration); const hookDestination: string | undefined = git.getHooksFolder(); @@ -414,16 +708,19 @@ export abstract class BaseInstallManager { // Ignore the ".sample" file(s) in this folder. const hookFilenames: string[] = allHookFilenames.filter((x) => !/\.sample$/.test(x)); if (hookFilenames.length > 0) { - console.log('\n' + colors.bold('Found files in the "common/git-hooks" folder.')); + // eslint-disable-next-line no-console + console.log('\n' + Colorize.bold('Found files in the "common/git-hooks" folder.')); - if (!git.isHooksPathDefault()) { - const color: (str: string) => string = this.options.bypassPolicy ? colors.yellow : colors.red; + if (!(await git.getIsHooksPathDefaultAsync())) { + const hooksPath: string = await git.getConfigHooksPathAsync(); + const color: (str: string) => string = this.options.bypassPolicy ? Colorize.yellow : Colorize.red; + // eslint-disable-next-line no-console console.error( color( [ ' ', `Rush cannot install the "common/git-hooks" scripts because your Git configuration `, - `specifies "core.hooksPath=${git.getConfigHooksPath()}". You can remove the setting by running:`, + `specifies "core.hooksPath=${hooksPath}". You can remove the setting by running:`, ' ', ' git config --unset core.hooksPath', ' ' @@ -435,6 +732,7 @@ export abstract class BaseInstallManager { // own the hooks folder return; } + // eslint-disable-next-line no-console console.error( color( [ @@ -450,11 +748,48 @@ export abstract class BaseInstallManager { // Clear the currently installed git hooks and install fresh copies FileSystem.ensureEmptyFolder(hookDestination); + // Find the relative path from Git hooks directory to the directory storing the actual scripts. + const hookRelativePath: string = Path.convertToSlashes(path.relative(hookDestination, hookSource)); + // Only copy files that look like Git hook names const filteredHookFilenames: string[] = hookFilenames.filter((x) => /^[a-z\-]+/.test(x)); for (const filename of filteredHookFilenames) { - // Copy the file. Important: For Bash scripts, the EOL must not be CRLF. - const hookFileContent: string = FileSystem.readFile(path.join(hookSource, filename)); + const hookFilePath: string = `${hookSource}/${filename}`; + // Make sure the actual script in the hookSource directory has correct Linux compatible line endings + const originalHookFileContent: string = FileSystem.readFile(hookFilePath); + FileSystem.writeFile(hookFilePath, originalHookFileContent, { + convertLineEndings: NewlineKind.Lf + }); + // Make sure the actual script in the hookSource directory has required permission bits + const originalPosixModeBits: PosixModeBits = FileSystem.getPosixModeBits(hookFilePath); + FileSystem.changePosixModeBits( + hookFilePath, + // eslint-disable-next-line no-bitwise + originalPosixModeBits | PosixModeBits.UserRead | PosixModeBits.UserExecute + ); + + const gitLfsHookHandling: string = gitLfsHooks.has(filename) + ? ` +# Inspired by https://github.com/git-lfs/git-lfs/issues/2865#issuecomment-365742940 +if command -v git-lfs &> /dev/null; then + git lfs ${filename} "$@" +fi +` + : ''; + + const hookFileContent: string = `#!/usr/bin/env bash +set -e +SCRIPT_DIR="$( cd "$( dirname "\${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +SCRIPT_IMPLEMENTATION_PATH="$SCRIPT_DIR/${hookRelativePath}/${filename}" + +if [[ -f "$SCRIPT_IMPLEMENTATION_PATH" ]]; then + "$SCRIPT_IMPLEMENTATION_PATH" $@ +else + echo "The ${filename} Git hook no longer exists in your version of the repo. Run 'rush install' or 'rush update' to refresh your installed Git hooks." >&2 +fi +${gitLfsHookHandling} +`; + // Create the hook file. Important: For Bash scripts, the EOL must not be CRLF. FileSystem.writeFile(path.join(hookDestination, filename), hookFileContent, { convertLineEndings: NewlineKind.Lf }); @@ -466,6 +801,7 @@ export abstract class BaseInstallManager { ); } + // eslint-disable-next-line no-console console.log( 'Successfully installed these Git hook scripts: ' + filteredHookFilenames.join(', ') + '\n' ); @@ -477,7 +813,25 @@ export abstract class BaseInstallManager { * Used when invoking the NPM tool. Appends the common configuration options * to the command-line. */ - protected pushConfigurationArgs(args: string[], options: IInstallManagerOptions): void { + protected pushConfigurationArgs(args: string[], options: IInstallManagerOptions, subspace: Subspace): void { + const { + offline, + collectLogFile, + pnpmFilterArgumentValues, + onlyShrinkwrap, + networkConcurrency, + allowShrinkwrapUpdates, + resolutionOnly + } = options; + + if (offline && this.rushConfiguration.packageManager !== 'pnpm') { + throw new Error('The "--offline" parameter is only supported when using the PNPM package manager.'); + } + if (resolutionOnly && this.rushConfiguration.packageManager !== 'pnpm') { + throw new Error( + 'The "--resolution-only" parameter is only supported when using the PNPM package manager.' + ); + } if (this.rushConfiguration.packageManager === 'npm') { if (semver.lt(this.rushConfiguration.packageManagerToolVersion, '5.0.0')) { // NOTE: @@ -503,10 +857,10 @@ export abstract class BaseInstallManager { args.push('--cache', this.rushConfiguration.npmCacheFolder); args.push('--tmp', this.rushConfiguration.npmTmpFolder); - if (options.collectLogFile) { + if (collectLogFile) { args.push('--verbose'); } - } else if (this.rushConfiguration.packageManager === 'pnpm') { + } else if (this.rushConfiguration.isPnpm) { // Only explicitly define the store path if `pnpmStore` is using the default, or has been set to // 'local'. If `pnpmStore` = 'global', then allow PNPM to use the system's default // path. In all cases, this will be overridden by RUSH_PNPM_STORE_PATH @@ -528,8 +882,16 @@ export abstract class BaseInstallManager { const { configuration: experiments } = this.rushConfiguration.experimentsConfiguration; - if (experiments.usePnpmFrozenLockfileForRushInstall && !this.options.allowShrinkwrapUpdates) { + if (experiments.usePnpmFrozenLockfileForRushInstall && !allowShrinkwrapUpdates) { args.push('--frozen-lockfile'); + + if ( + pnpmFilterArgumentValues.length > 0 && + Number.parseInt(this.rushConfiguration.packageManagerToolVersion, 10) >= 8 // PNPM Major version 8+ + ) { + // On pnpm@8, disable the "dedupe-peer-dependents" feature when doing a filtered CI install so that filters take effect. + args.push('--config.dedupe-peer-dependents=false'); + } } else if (experiments.usePnpmPreferFrozenLockfileForRushUpdate) { // In workspaces, we want to avoid unnecessary lockfile churn args.push('--prefer-frozen-lockfile'); @@ -539,12 +901,20 @@ export abstract class BaseInstallManager { args.push('--no-prefer-frozen-lockfile'); } - if (options.collectLogFile) { + if (onlyShrinkwrap) { + args.push(`--lockfile-only`); + } + + if (collectLogFile) { args.push('--reporter', 'ndjson'); } - if (options.networkConcurrency) { - args.push('--network-concurrency', options.networkConcurrency.toString()); + if (networkConcurrency) { + args.push('--network-concurrency', networkConcurrency.toString()); + } + + if (offline) { + args.push('--offline'); } if (this.rushConfiguration.pnpmOptions.strictPeerDependencies === false) { @@ -553,6 +923,68 @@ export abstract class BaseInstallManager { args.push('--strict-peer-dependencies'); } + if (resolutionOnly) { + args.push('--resolution-only'); + } + + /* + If user set auto-install-peers in pnpm-config.json only, use the value in pnpm-config.json + If user set auto-install-peers in pnpm-config.json and .npmrc, use the value in pnpm-config.json + If user set auto-install-peers in .npmrc only, do nothing, let pnpm handle it + If user does not set auto-install-peers in both pnpm-config.json and .npmrc, rush will default it to "false" + */ + const isAutoInstallPeersInNpmrc: boolean = isVariableSetInNpmrcFile( + subspace.getSubspaceConfigFolderPath(), + 'auto-install-peers', + this.rushConfiguration.isPnpm + ); + + let autoInstallPeers: boolean | undefined = this.rushConfiguration.pnpmOptions.autoInstallPeers; + if (autoInstallPeers !== undefined) { + if (isAutoInstallPeersInNpmrc) { + this._terminal.writeWarningLine( + `Warning: PNPM's auto-install-peers is specified in both .npmrc and pnpm-config.json. ` + + `The value in pnpm-config.json will take precedence.` + ); + } + } else if (!isAutoInstallPeersInNpmrc) { + // if auto-install-peers isn't specified in either .npmrc or pnpm-config.json, + // then rush will default it to "false" + autoInstallPeers = false; + } + if (autoInstallPeers !== undefined) { + args.push(`--config.auto-install-peers=${autoInstallPeers}`); + } + + /* + If user set resolution-mode in pnpm-config.json only, use the value in pnpm-config.json + If user set resolution-mode in pnpm-config.json and .npmrc, use the value in pnpm-config.json + If user set resolution-mode in .npmrc only, do nothing, let pnpm handle it + If user does not set resolution-mode in pnpm-config.json and .npmrc, rush will default it to "highest" + */ + const isResolutionModeInNpmrc: boolean = isVariableSetInNpmrcFile( + subspace.getSubspaceConfigFolderPath(), + 'resolution-mode', + this.rushConfiguration.isPnpm + ); + + let resolutionMode: PnpmResolutionMode | undefined = this.rushConfiguration.pnpmOptions.resolutionMode; + if (resolutionMode) { + if (isResolutionModeInNpmrc) { + this._terminal.writeWarningLine( + `Warning: PNPM's resolution-mode is specified in both .npmrc and pnpm-config.json. ` + + `The value in pnpm-config.json will take precedence.` + ); + } + } else if (!isResolutionModeInNpmrc) { + // if resolution-mode isn't specified in either .npmrc or pnpm-config.json, + // then rush will default it to "highest" + resolutionMode = 'highest'; + } + if (resolutionMode) { + args.push(`--config.resolutionMode=${resolutionMode}`); + } + if ( semver.satisfies( this.rushConfiguration.packageManagerToolVersion, @@ -560,7 +992,7 @@ export abstract class BaseInstallManager { ) ) { this._terminal.writeWarningLine( - 'Warning: Your rush.json specifies a pnpmVersion with a known issue ' + + `Warning: Your ${RushConstants.rushJsonFilename} specifies a pnpmVersion with a known issue ` + 'that may cause unintended version selections.' + " It's recommended to upgrade to PNPM >=6.34.0 or >=7.9.0. " + 'For details see: https://rushjs.io/link/pnpm-issue-5132' @@ -580,21 +1012,21 @@ export abstract class BaseInstallManager { // (e.g. "Which command would you like to run?"). args.push('--non-interactive'); - if (options.networkConcurrency) { - args.push('--network-concurrency', options.networkConcurrency.toString()); + if (networkConcurrency) { + args.push('--network-concurrency', networkConcurrency.toString()); } if (this.rushConfiguration.yarnOptions.ignoreEngines) { args.push('--ignore-engines'); } - if (options.collectLogFile) { + if (collectLogFile) { args.push('--verbose'); } } } - private async _checkIfReleaseIsPublished(): Promise { + private async _checkIfReleaseIsPublishedAsync(): Promise { const lastCheckFile: string = path.join( this.rushGlobalFolder.nodeSpecificPath, 'rush-' + Rush.version, @@ -653,16 +1085,19 @@ export abstract class BaseInstallManager { // Note that the "@" symbol does not normally get URL-encoded queryUrl += RushConstants.rushPackageName.replace('/', '%2F'); - const webClient: WebClient = new WebClient(); + const { WebClient } = await import('../../utilities/WebClient'); + + const webClient: WebClientType = new WebClient(); webClient.userAgent = `pnpm/? npm/? node/${process.version} ${os.platform()} ${os.arch()}`; webClient.accept = 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*'; - const response: WebClientResponse = await webClient.fetchAsync(queryUrl); + const response: IWebClientResponse = await webClient.fetchAsync(queryUrl); if (!response.ok) { throw new Error('Failed to query'); } - const data: { versions: { [version: string]: { dist: { tarball: string } } } } = await response.json(); + const data: { versions: { [version: string]: { dist: { tarball: string } } } } = + await response.getJsonAsync(); let url: string; try { if (!data.versions[Rush.version]) { @@ -681,7 +1116,7 @@ export abstract class BaseInstallManager { // Make sure the tarball wasn't deleted from the CDN webClient.accept = '*/*'; - const response2: fetch.Response = await webClient.fetchAsync(url); + const response2: IWebClientResponse = await webClient.fetchAsync(url); if (!response2.ok) { if (response2.status === 404) { @@ -694,43 +1129,37 @@ export abstract class BaseInstallManager { return true; } - private _syncTempShrinkwrap(shrinkwrapFile: BaseShrinkwrapFile | undefined): void { + private _syncTempShrinkwrap( + subspace: Subspace, + variant: string | undefined, + shrinkwrapFile: BaseShrinkwrapFile | undefined + ): void { + const committedShrinkwrapFileName: string = subspace.getCommittedShrinkwrapFilePath(variant); if (shrinkwrapFile) { - Utilities.syncFile( - this.rushConfiguration.getCommittedShrinkwrapFilename(this.options.variant), - this.rushConfiguration.tempShrinkwrapFilename - ); - Utilities.syncFile( - this.rushConfiguration.getCommittedShrinkwrapFilename(this.options.variant), - this.rushConfiguration.tempShrinkwrapPreinstallFilename - ); + Utilities.syncFile(committedShrinkwrapFileName, subspace.getTempShrinkwrapFilename()); + Utilities.syncFile(committedShrinkwrapFileName, subspace.getTempShrinkwrapPreinstallFilename()); } else { // Otherwise delete the temporary file - FileSystem.deleteFile(this.rushConfiguration.tempShrinkwrapFilename); + FileSystem.deleteFile(subspace.getTempShrinkwrapFilename()); - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { // Workaround for https://github.com/pnpm/pnpm/issues/1890 // - // When "rush update --full" is run, rush deletes common/temp/pnpm-lock.yaml so that - // a new lockfile can be generated. But because of the above bug "pnpm install" would - // respect "common/temp/node_modules/.pnpm-lock.yaml" and thus would not generate a - // new lockfile. Deleting this file in addition to deleting common/temp/pnpm-lock.yaml - // ensures that a new lockfile will be generated with "rush update --full". - + // When "rush update --full" is run, Rush deletes "common/temp/pnpm-lock.yaml" + // so that a new lockfile will be generated. However "pnpm install" by design will try to recover + // "pnpm-lock.yaml" from "common/temp/node_modules/.pnpm/lock.yaml", which may prevent a full upgrade. + // Deleting both files ensures that a new lockfile will always be generated. const pnpmPackageManager: PnpmPackageManager = this.rushConfiguration .packageManagerWrapper as PnpmPackageManager; FileSystem.deleteFile( - path.join( - this.rushConfiguration.commonTempFolder, - pnpmPackageManager.internalShrinkwrapRelativePath - ) + path.join(subspace.getSubspaceTempFolderPath(), pnpmPackageManager.internalShrinkwrapRelativePath) ); } } } - protected async validateNpmSetup(): Promise { + protected async validateNpmSetupAsync(): Promise { if (this._npmSetupValidated) { return; } @@ -741,13 +1170,17 @@ export abstract class BaseInstallManager { isDebug: this.options.debug, syncNpmrcAlreadyCalled: this._syncNpmrcAlreadyCalled }); - const valid: boolean = await setupPackageRegistry.checkOnly(); + const valid: boolean = await setupPackageRegistry.checkOnlyAsync(); if (!valid) { + // eslint-disable-next-line no-console console.error(); - console.error(colors.red('ERROR: NPM credentials are missing or expired')); + // eslint-disable-next-line no-console + console.error(Colorize.red('ERROR: NPM credentials are missing or expired')); + // eslint-disable-next-line no-console console.error(); + // eslint-disable-next-line no-console console.error( - colors.bold( + Colorize.bold( '==> Please run "rush setup" to update your NPM token. ' + `(Or append "${RushConstants.bypassPolicyFlagLongName}" to proceed anyway.)` ) diff --git a/libraries/rush-lib/src/logic/base/BaseInstallManagerTypes.ts b/libraries/rush-lib/src/logic/base/BaseInstallManagerTypes.ts index bab66d03e41..2825f927fe4 100644 --- a/libraries/rush-lib/src/logic/base/BaseInstallManagerTypes.ts +++ b/libraries/rush-lib/src/logic/base/BaseInstallManagerTypes.ts @@ -1,6 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import type { ITerminal } from '@rushstack/terminal'; + +import type { Subspace } from '../../api/Subspace'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; + export interface IInstallManagerOptions { /** * Whether the global "--debug" flag was specified. @@ -18,6 +23,11 @@ export interface IInstallManagerOptions { */ checkOnly: boolean; + /** + * Whether to only run resolutions. Only supported for PNPM. + */ + resolutionOnly?: boolean; + /** * Whether a "--bypass-policy" flag can be specified. */ @@ -39,6 +49,11 @@ export interface IInstallManagerOptions { */ fullUpgrade: boolean; + /** + * If set, only update the shrinkwrap file; do not create node_modules. + */ + onlyShrinkwrap?: boolean; + /** * Whether to force an update to the shrinkwrap file even if it appears to be unnecessary. * Normally Rush uses heuristics to determine when "pnpm install" can be skipped, @@ -47,6 +62,12 @@ export interface IInstallManagerOptions { */ recheckShrinkwrap: boolean; + /** + * Do not attempt to access the network. Report an error if the required dependencies + * cannot be obtained from the local cache. + */ + offline: boolean; + /** * The value of the "--network-concurrency" command-line parameter, which * is a diagnostic option used to troubleshoot network failures. @@ -64,7 +85,7 @@ export interface IInstallManagerOptions { /** * The variant to consider when performing installations and validating shrinkwrap updates. */ - variant?: string | undefined; + variant: string | undefined; /** * Retry the install the specified number of times @@ -72,13 +93,40 @@ export interface IInstallManagerOptions { maxInstallAttempts: number; /** - * Filters to be passed to PNPM during installation, if applicable. - * These restrict the scope of a workspace installation. + * An array of `--filter` argument values. For example, if the array is ["a", "b"] then Rush would invoke + * `pnpm install --filter a --filter b` which restricts the install/update to dependencies of + * workspace projects "a" and "b". If the array is empty, then an unfiltered install + * is performed. Filtered installs have some limitations such as less comprehensive version analysis. + * + * @remarks + * Note that PNPM may arbitrarily ignore `--filter` (producing an unfiltered install) in certain situations, + * for example when `config.dedupe-peer-dependents=true` with PNPM 8. Rush tries to circumvent this, under the + * assumption that a user who invokes a filtered install cares more about lockfile stability than duplication. + */ + pnpmFilterArgumentValues: string[]; + + /** + * The set of projects for which installation should be performed. */ - pnpmFilterArguments: string[]; + selectedProjects: Set; /** * Callback to invoke between preparing the common/temp folder and running installation. */ - beforeInstallAsync?: () => Promise; + beforeInstallAsync?: (subspace: Subspace) => Promise; + + /** + * Callback to invoke after a successful installation. + */ + afterInstallAsync?: (subspace: Subspace) => Promise; + + /** + * The specific subspace to install. + */ + subspace: Subspace; + + /** + * The terminal where output should be printed. + */ + terminal: ITerminal; } diff --git a/libraries/rush-lib/src/logic/base/BaseLinkManager.ts b/libraries/rush-lib/src/logic/base/BaseLinkManager.ts index 7329d4473e7..cca31d18f18 100644 --- a/libraries/rush-lib/src/logic/base/BaseLinkManager.ts +++ b/libraries/rush-lib/src/logic/base/BaseLinkManager.ts @@ -1,22 +1,24 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; +import * as path from 'node:path'; import { FileSystem, - FileSystemStats, - IFileSystemCreateLinkOptions, + type FileSystemStats, + type IFileSystemCreateLinkOptions, InternalError } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; -import { RushConfiguration } from '../../api/RushConfiguration'; +import type { RushConfiguration } from '../../api/RushConfiguration'; import { Utilities } from '../../utilities/Utilities'; import { Stopwatch } from '../../utilities/Stopwatch'; -import { BasePackage } from './BasePackage'; +import type { BasePackage } from './BasePackage'; import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration'; -import { LastLinkFlagFactory } from '../../api/LastLinkFlag'; +import { RushConstants } from '../RushConstants'; +import { FlagFile } from '../../api/FlagFile'; +import { IS_WINDOWS } from '../../utilities/executionUtilities'; export enum SymlinkKind { File, @@ -34,50 +36,47 @@ export abstract class BaseLinkManager { this._rushConfiguration = rushConfiguration; } - protected static _createSymlink(options: IBaseLinkManagerCreateSymlinkOptions): void { + public static async _createSymlinkAsync(options: IBaseLinkManagerCreateSymlinkOptions): Promise { + // TODO: Consider promoting this to node-core-library const newLinkFolder: string = path.dirname(options.newLinkPath); - FileSystem.ensureFolder(newLinkFolder); + await FileSystem.ensureFolderAsync(newLinkFolder); - let targetPath: string; - if (EnvironmentConfiguration.absoluteSymlinks) { - targetPath = options.linkTargetPath; - } else { - // Link to the relative path, to avoid going outside containers such as a Docker image - targetPath = path.relative(FileSystem.getRealPath(newLinkFolder), options.linkTargetPath); - } + let relativePathForbidden: boolean = false; + let linkFunctionAsync: (options: IBaseLinkManagerCreateSymlinkOptions) => Promise; - if (process.platform === 'win32') { + if (IS_WINDOWS) { if (options.symlinkKind === SymlinkKind.Directory) { // For directories, we use a Windows "junction". On Unix, this produces a regular symlink. - FileSystem.createSymbolicLinkJunction({ - linkTargetPath: targetPath, - newLinkPath: options.newLinkPath - }); + linkFunctionAsync = FileSystem.createSymbolicLinkJunctionAsync.bind(FileSystem); } else { // For files, we use a Windows "hard link", because creating a symbolic link requires // administrator permission. + linkFunctionAsync = FileSystem.createHardLinkAsync.bind(FileSystem); // NOTE: We cannot use the relative path for hard links - FileSystem.createHardLink({ - linkTargetPath: options.linkTargetPath, - newLinkPath: options.newLinkPath - }); + relativePathForbidden = true; } } else { // However hard links seem to cause build failures on Mac, so for all other operating systems // we use symbolic links for this case. if (options.symlinkKind === SymlinkKind.Directory) { - FileSystem.createSymbolicLinkFolder({ - linkTargetPath: targetPath, - newLinkPath: options.newLinkPath - }); + linkFunctionAsync = FileSystem.createSymbolicLinkFolderAsync.bind(FileSystem); } else { - FileSystem.createSymbolicLinkFile({ - linkTargetPath: targetPath, - newLinkPath: options.newLinkPath - }); + linkFunctionAsync = FileSystem.createSymbolicLinkFileAsync.bind(FileSystem); } } + + let { linkTargetPath } = options; + if (!relativePathForbidden && !EnvironmentConfiguration.absoluteSymlinks) { + // Link to the relative path, to avoid going outside containers such as a Docker image + const newLinkFolderRealPath: string = await FileSystem.getRealPathAsync(newLinkFolder); + linkTargetPath = path.relative(newLinkFolderRealPath, linkTargetPath); + } + + await linkFunctionAsync({ + ...options, + linkTargetPath + }); } /** @@ -85,7 +84,7 @@ export abstract class BaseLinkManager { * (i.e. with source code that we will be building), this clears out its * node_modules folder and then recursively creates all the symlinked folders. */ - protected static _createSymlinksForTopLevelProject(localPackage: BasePackage): void { + protected static async _createSymlinksForTopLevelProjectAsync(localPackage: BasePackage): Promise { const localModuleFolder: string = path.join(localPackage.folderPath, 'node_modules'); // Sanity check @@ -95,6 +94,7 @@ export abstract class BaseLinkManager { // The root-level folder is the project itself, so we simply delete its node_modules // to start clean + // eslint-disable-next-line no-console console.log('Purging ' + localModuleFolder); Utilities.dangerouslyDeletePath(localModuleFolder); @@ -102,105 +102,112 @@ export abstract class BaseLinkManager { Utilities.createFolderWithRetry(localModuleFolder); for (const child of localPackage.children) { - BaseLinkManager._createSymlinksForDependencies(child); + await _createSymlinksForDependenciesAsync(child); } } } /** - * This is a helper function used by createSymlinksForTopLevelProject(). - * It will recursively creates symlinked folders corresponding to each of the - * Package objects in the provided tree. + * Creates node_modules symlinks for all Rush projects defined in the RushConfiguration. + * @param force - Normally the operation will be skipped if the links are already up to date; + * if true, this option forces the links to be recreated. */ - private static _createSymlinksForDependencies(localPackage: BasePackage): void { - const localModuleFolder: string = path.join(localPackage.folderPath, 'node_modules'); + public async createSymlinksForProjectsAsync(force: boolean): Promise { + // eslint-disable-next-line no-console + console.log('\n' + Colorize.bold('Linking local projects')); + const stopwatch: Stopwatch = Stopwatch.start(); - if (!localPackage.symlinkTargetFolderPath) { - throw new InternalError('localPackage.symlinkTargetFolderPath was not assigned'); - } + await this._linkProjectsAsync(); - // This is special case for when localPackage.name has the form '@scope/name', - // in which case we need to create the '@scope' folder first. - const parentFolderPath: string = path.dirname(localPackage.folderPath); - if (parentFolderPath && parentFolderPath !== localPackage.folderPath) { - if (!FileSystem.exists(parentFolderPath)) { - Utilities.createFolderWithRetry(parentFolderPath); - } + // TODO: Remove when "rush link" and "rush unlink" are deprecated + await new FlagFile( + this._rushConfiguration.defaultSubspace.getSubspaceTempFolderPath(), + RushConstants.lastLinkFlagFilename, + {} + ).createAsync(); + + stopwatch.stop(); + // eslint-disable-next-line no-console + console.log('\n' + Colorize.green(`Linking finished successfully. (${stopwatch.toString()})`)); + // eslint-disable-next-line no-console + console.log('\nNext you should probably run "rush build" or "rush rebuild"'); + } + + protected abstract _linkProjectsAsync(): Promise; +} + +/** + * This is a helper function used by createSymlinksForTopLevelProject(). + * It will recursively creates symlinked folders corresponding to each of the + * Package objects in the provided tree. + */ +async function _createSymlinksForDependenciesAsync(localPackage: BasePackage): Promise { + const localModuleFolder: string = path.join(localPackage.folderPath, 'node_modules'); + + if (!localPackage.symlinkTargetFolderPath) { + throw new InternalError('localPackage.symlinkTargetFolderPath was not assigned'); + } + + // This is special case for when localPackage.name has the form '@scope/name', + // in which case we need to create the '@scope' folder first. + const parentFolderPath: string = path.dirname(localPackage.folderPath); + if (parentFolderPath && parentFolderPath !== localPackage.folderPath) { + if (!FileSystem.exists(parentFolderPath)) { + Utilities.createFolderWithRetry(parentFolderPath); } + } - if (localPackage.children.length === 0) { - // If there are no children, then we can symlink the entire folder - BaseLinkManager._createSymlink({ - linkTargetPath: localPackage.symlinkTargetFolderPath, - newLinkPath: localPackage.folderPath, - symlinkKind: SymlinkKind.Directory - }); - } else { - // If there are children, then we need to symlink each item in the folder individually - Utilities.createFolderWithRetry(localPackage.folderPath); - - for (const filename of FileSystem.readFolderItemNames(localPackage.symlinkTargetFolderPath)) { - if (filename.toLowerCase() !== 'node_modules') { - // Create the symlink - let symlinkKind: SymlinkKind = SymlinkKind.File; - - const linkSource: string = path.join(localPackage.folderPath, filename); - let linkTarget: string = path.join(localPackage.symlinkTargetFolderPath, filename); - - const linkStats: FileSystemStats = FileSystem.getLinkStatistics(linkTarget); - - if (linkStats.isSymbolicLink()) { - const targetStats: FileSystemStats = FileSystem.getStatistics(FileSystem.getRealPath(linkTarget)); - if (targetStats.isDirectory()) { - // Neither a junction nor a directory-symlink can have a directory-symlink - // as its target; instead, we must obtain the real physical path. - // A junction can link to another junction. Unfortunately, the node 'fs' API - // lacks the ability to distinguish between a junction and a directory-symlink - // (even though it has the ability to create them both), so the safest policy - // is to always make a junction and always to the real physical path. - linkTarget = FileSystem.getRealPath(linkTarget); - symlinkKind = SymlinkKind.Directory; - } - } else if (linkStats.isDirectory()) { + if (localPackage.children.length === 0) { + // If there are no children, then we can symlink the entire folder + await BaseLinkManager._createSymlinkAsync({ + linkTargetPath: localPackage.symlinkTargetFolderPath, + newLinkPath: localPackage.folderPath, + symlinkKind: SymlinkKind.Directory + }); + } else { + // If there are children, then we need to symlink each item in the folder individually + Utilities.createFolderWithRetry(localPackage.folderPath); + + for (const filename of FileSystem.readFolderItemNames(localPackage.symlinkTargetFolderPath)) { + if (filename.toLowerCase() !== 'node_modules') { + // Create the symlink + let symlinkKind: SymlinkKind = SymlinkKind.File; + + const linkSource: string = path.join(localPackage.folderPath, filename); + let linkTarget: string = path.join(localPackage.symlinkTargetFolderPath, filename); + + const linkStats: FileSystemStats = FileSystem.getLinkStatistics(linkTarget); + + if (linkStats.isSymbolicLink()) { + const targetStats: FileSystemStats = FileSystem.getStatistics(FileSystem.getRealPath(linkTarget)); + if (targetStats.isDirectory()) { + // Neither a junction nor a directory-symlink can have a directory-symlink + // as its target; instead, we must obtain the real physical path. + // A junction can link to another junction. Unfortunately, the node 'fs' API + // lacks the ability to distinguish between a junction and a directory-symlink + // (even though it has the ability to create them both), so the safest policy + // is to always make a junction and always to the real physical path. + linkTarget = FileSystem.getRealPath(linkTarget); symlinkKind = SymlinkKind.Directory; } - - BaseLinkManager._createSymlink({ - linkTargetPath: linkTarget, - newLinkPath: linkSource, - symlinkKind - }); + } else if (linkStats.isDirectory()) { + symlinkKind = SymlinkKind.Directory; } - } - } - - if (localPackage.children.length > 0) { - Utilities.createFolderWithRetry(localModuleFolder); - for (const child of localPackage.children) { - BaseLinkManager._createSymlinksForDependencies(child); + await BaseLinkManager._createSymlinkAsync({ + linkTargetPath: linkTarget, + newLinkPath: linkSource, + symlinkKind + }); } } } - /** - * Creates node_modules symlinks for all Rush projects defined in the RushConfiguration. - * @param force - Normally the operation will be skipped if the links are already up to date; - * if true, this option forces the links to be recreated. - */ - public async createSymlinksForProjects(force: boolean): Promise { - console.log('\n' + colors.bold('Linking local projects')); - const stopwatch: Stopwatch = Stopwatch.start(); - - await this._linkProjects(); + if (localPackage.children.length > 0) { + Utilities.createFolderWithRetry(localModuleFolder); - // TODO: Remove when "rush link" and "rush unlink" are deprecated - LastLinkFlagFactory.getCommonTempFlag(this._rushConfiguration).create(); - - stopwatch.stop(); - console.log('\n' + colors.green(`Linking finished successfully. (${stopwatch.toString()})`)); - console.log('\nNext you should probably run "rush build" or "rush rebuild"'); + for (const child of localPackage.children) { + await _createSymlinksForDependenciesAsync(child); + } } - - protected abstract _linkProjects(): Promise; } diff --git a/libraries/rush-lib/src/logic/base/BasePackage.ts b/libraries/rush-lib/src/logic/base/BasePackage.ts index 8901e5fae31..2a1fb304a0d 100644 --- a/libraries/rush-lib/src/logic/base/BasePackage.ts +++ b/libraries/rush-lib/src/logic/base/BasePackage.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { JsonFile, IPackageJson } from '@rushstack/node-core-library'; +import { JsonFile, type IPackageJson } from '@rushstack/node-core-library'; /** * The type of dependency; used by IPackageDependency. @@ -202,6 +202,8 @@ export class BasePackage { if (!indent) { indent = ''; } + + // eslint-disable-next-line no-console console.log(indent + this.nameAndVersion); for (const child of this.children) { child.printTree(indent + ' '); diff --git a/libraries/rush-lib/src/logic/base/BaseProjectShrinkwrapFile.ts b/libraries/rush-lib/src/logic/base/BaseProjectShrinkwrapFile.ts index 0354a826795..284eed0d9cd 100644 --- a/libraries/rush-lib/src/logic/base/BaseProjectShrinkwrapFile.ts +++ b/libraries/rush-lib/src/logic/base/BaseProjectShrinkwrapFile.ts @@ -1,12 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; import { FileSystem, JsonFile } from '@rushstack/node-core-library'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import { RushConstants } from '../RushConstants'; -import { BaseShrinkwrapFile } from './BaseShrinkwrapFile'; +import type { BaseShrinkwrapFile } from './BaseShrinkwrapFile'; /** * This class handles creating the project/.rush/temp/shrinkwrap-deps.json file @@ -42,7 +41,7 @@ export abstract class BaseProjectShrinkwrapFile { + public findOrphanedProjects( + rushConfiguration: RushConfiguration, + subspace: Subspace + ): ReadonlyArray { const orphanedProjectNames: string[] = []; // We can recognize temp projects because they are under the "@rush-temp" NPM scope. for (const tempProjectName of this.getTempProjectNames()) { @@ -145,7 +161,8 @@ export abstract class BaseShrinkwrapFile { */ public abstract isWorkspaceProjectModifiedAsync( project: RushConfigurationProject, - variant?: string + subspace: Subspace, + variant: string | undefined ): Promise; /** @virtual */ @@ -211,8 +228,9 @@ export abstract class BaseShrinkwrapFile { // Only warn once for each versionSpecifier if (!this._alreadyWarnedSpecs.has(projectDependency.versionSpecifier)) { this._alreadyWarnedSpecs.add(projectDependency.versionSpecifier); + // eslint-disable-next-line no-console console.log( - colors.yellow( + Colorize.yellow( `WARNING: Not validating ${projectDependency.specifierType}-based` + ` specifier: "${projectDependency.versionSpecifier}"` ) diff --git a/libraries/rush-lib/src/logic/base/BaseWorkspaceFile.ts b/libraries/rush-lib/src/logic/base/BaseWorkspaceFile.ts index e3546d58ed5..15900596233 100644 --- a/libraries/rush-lib/src/logic/base/BaseWorkspaceFile.ts +++ b/libraries/rush-lib/src/logic/base/BaseWorkspaceFile.ts @@ -26,32 +26,33 @@ export abstract class BaseWorkspaceFile { /** * Serializes and saves the workspace file to specified location */ - public save(filePath: string, options: IWorkspaceFileSaveOptions): void { + public async saveAsync(filePath: string, options: IWorkspaceFileSaveOptions): Promise { + const { onlyIfChanged, ensureFolderExists } = options; + // Do we need to read the previous file contents? - let oldBuffer: Buffer | undefined = undefined; - if (options.onlyIfChanged && FileSystem.exists(filePath)) { + let oldBuffer: Buffer | undefined; + if (onlyIfChanged) { try { - oldBuffer = FileSystem.readFileToBuffer(filePath); + oldBuffer = await FileSystem.readFileToBufferAsync(filePath); } catch (error) { // Ignore this error, and try writing a new file. If that fails, then we should report that // error instead. } } - const newYaml: string = this.serialize(); - - const newBuffer: Buffer = Buffer.from(newYaml); // utf8 encoding happens here + const newContent: string = await this.serializeAsync(); + const newBuffer: Buffer = Buffer.from(newContent); // utf8 encoding happens here - if (options.onlyIfChanged) { + if (oldBuffer) { // Has the file changed? - if (oldBuffer && Buffer.compare(newBuffer, oldBuffer) === 0) { + if (Buffer.compare(newBuffer, oldBuffer) === 0) { // Nothing has changed, so don't touch the file return; } } - FileSystem.writeFile(filePath, newBuffer.toString(), { - ensureFolderExists: options.ensureFolderExists + await FileSystem.writeFileAsync(filePath, newBuffer.toString(), { + ensureFolderExists }); } @@ -63,5 +64,5 @@ export abstract class BaseWorkspaceFile { public abstract addPackage(packagePath: string): void; /** @virtual */ - protected abstract serialize(): string; + protected abstract serializeAsync(): Promise; } diff --git a/libraries/rush-lib/src/logic/buildCache/CacheEntryId.ts b/libraries/rush-lib/src/logic/buildCache/CacheEntryId.ts index b3f2f906d40..893e9bb08a6 100644 --- a/libraries/rush-lib/src/logic/buildCache/CacheEntryId.ts +++ b/libraries/rush-lib/src/logic/buildCache/CacheEntryId.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import process from 'node:process'; + const OPTIONS_ARGUMENT_NAME: string = 'options'; /** @@ -28,9 +30,13 @@ export interface IGenerateCacheEntryIdOptions { */ export type GetCacheEntryIdFunction = (options: IGenerateCacheEntryIdOptions) => string; -const HASH_TOKEN_NAME: string = 'hash'; -const PROJECT_NAME_TOKEN_NAME: string = 'projectName'; -const PHASE_NAME_TOKEN_NAME: string = 'phaseName'; +// NOTE: When adding new tokens, make sure to document the syntax in the "rush init" +// template for build-cache.json +const HASH_TOKEN_NAME: 'hash' = 'hash'; +const PROJECT_NAME_TOKEN_NAME: 'projectName' = 'projectName'; +const PHASE_NAME_TOKEN_NAME: 'phaseName' = 'phaseName'; +const OS_TOKEN_NAME: 'os' = 'os'; +const ARCH_TOKEN_NAME: 'arch' = 'arch'; // This regex matches substrings that look like [token] const TOKEN_REGEX: RegExp = /\[[^\]]*\]/g; @@ -128,6 +134,22 @@ export class CacheEntryId { } } + case OS_TOKEN_NAME: { + if (tokenAttribute !== undefined) { + throw new Error(`An attribute isn\'t supported for the "${tokenName}" token.`); + } + + return process.platform; + } + + case ARCH_TOKEN_NAME: { + if (tokenAttribute !== undefined) { + throw new Error(`An attribute isn\'t supported for the "${tokenName}" token.`); + } + + return process.arch; + } + default: { throw new Error(`Unexpected token name "${tokenName}".`); } diff --git a/libraries/rush-lib/src/logic/buildCache/FileSystemBuildCacheProvider.ts b/libraries/rush-lib/src/logic/buildCache/FileSystemBuildCacheProvider.ts index ddeda3090a1..96af3eebb27 100644 --- a/libraries/rush-lib/src/logic/buildCache/FileSystemBuildCacheProvider.ts +++ b/libraries/rush-lib/src/logic/buildCache/FileSystemBuildCacheProvider.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { FileSystem, ITerminal } from '@rushstack/node-core-library'; +import { FileSystem } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; -import { RushConfiguration } from '../../api/RushConfiguration'; -import { RushUserConfiguration } from '../../api/RushUserConfiguration'; +import type { RushConfiguration } from '../../api/RushConfiguration'; +import type { RushUserConfiguration } from '../../api/RushUserConfiguration'; /** * Options for creating a file system build cache provider. @@ -30,19 +30,17 @@ const DEFAULT_BUILD_CACHE_FOLDER_NAME: string = 'build-cache'; * @beta */ export class FileSystemBuildCacheProvider { - private readonly _cacheFolderPath: string; - - public constructor(options: IFileSystemBuildCacheProviderOptions) { - this._cacheFolderPath = - options.rushUserConfiguration.buildCacheFolder || - path.join(options.rushConfiguration.commonTempFolder, DEFAULT_BUILD_CACHE_FOLDER_NAME); - } - /** * Returns the absolute disk path for the specified cache id. */ - public getCacheEntryPath(cacheId: string): string { - return path.join(this._cacheFolderPath, cacheId); + public readonly getCacheEntryPath: (cacheId: string) => string; + + public constructor(options: IFileSystemBuildCacheProviderOptions) { + const { + rushConfiguration: { commonTempFolder }, + rushUserConfiguration: { buildCacheFolder = `${commonTempFolder}/${DEFAULT_BUILD_CACHE_FOLDER_NAME}` } + } = options; + this.getCacheEntryPath = (cacheId: string) => `${buildCacheFolder}/${cacheId}`; } /** @@ -53,7 +51,8 @@ export class FileSystemBuildCacheProvider { cacheId: string ): Promise { const cacheEntryFilePath: string = this.getCacheEntryPath(cacheId); - if (await FileSystem.existsAsync(cacheEntryFilePath)) { + const cacheEntryExists: boolean = await FileSystem.existsAsync(cacheEntryFilePath); + if (cacheEntryExists) { return cacheEntryFilePath; } else { return undefined; diff --git a/libraries/rush-lib/src/logic/buildCache/ICloudBuildCacheProvider.ts b/libraries/rush-lib/src/logic/buildCache/ICloudBuildCacheProvider.ts index 313fca8c11b..9cad627fae5 100644 --- a/libraries/rush-lib/src/logic/buildCache/ICloudBuildCacheProvider.ts +++ b/libraries/rush-lib/src/logic/buildCache/ICloudBuildCacheProvider.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ITerminal } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; /** * @beta @@ -11,6 +11,36 @@ export interface ICloudBuildCacheProvider { tryGetCacheEntryBufferByIdAsync(terminal: ITerminal, cacheId: string): Promise; trySetCacheEntryBufferAsync(terminal: ITerminal, cacheId: string, entryBuffer: Buffer): Promise; + + /** + * If implemented, the build cache will prefer to use this method over + * {@link ICloudBuildCacheProvider.tryGetCacheEntryBufferByIdAsync} to avoid loading the entire + * cache entry into memory, if possible. The implementation should download the cache entry and write it + * to the specified local file path. + * + * @returns `true` if the cache entry was found and written to the file; otherwise `false`. + * Implementations typically log transfer failures and return `false`, but may still throw for + * unexpected errors. + */ + tryDownloadCacheEntryToFileAsync?( + terminal: ITerminal, + cacheId: string, + localFilePath: string + ): Promise; + /** + * If implemented, the build cache will prefer to use this method over + * {@link ICloudBuildCacheProvider.trySetCacheEntryBufferAsync} to avoid loading the entire + * cache entry into memory, if possible. The implementation should read the cache entry from + * the specified local file path and upload it. + * + * @returns `true` if the cache entry was written to the cache, otherwise `false`. + */ + tryUploadCacheEntryFromFileAsync?( + terminal: ITerminal, + cacheId: string, + localFilePath: string + ): Promise; + updateCachedCredentialAsync(terminal: ITerminal, credential: string): Promise; updateCachedCredentialInteractiveAsync(terminal: ITerminal): Promise; deleteCachedCredentialsAsync(terminal: ITerminal): Promise; diff --git a/libraries/rush-lib/src/logic/buildCache/OperationBuildCache.ts b/libraries/rush-lib/src/logic/buildCache/OperationBuildCache.ts new file mode 100644 index 00000000000..b7f98903827 --- /dev/null +++ b/libraries/rush-lib/src/logic/buildCache/OperationBuildCache.ts @@ -0,0 +1,581 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import * as crypto from 'node:crypto'; + +import { FileSystem, type FolderItem, InternalError, Async, LockFile } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { BuildCacheConfiguration } from '../../api/BuildCacheConfiguration'; +import type { ICloudBuildCacheProvider } from './ICloudBuildCacheProvider'; +import type { FileSystemBuildCacheProvider } from './FileSystemBuildCacheProvider'; +import { TarExecutable } from '../../utilities/TarExecutable'; +import { EnvironmentVariableNames } from '../../api/EnvironmentConfiguration'; +import type { IBaseOperationExecutionResult } from '../operations/IOperationExecutionResult'; + +/** + * How long to wait to acquire the per-cache-entry download lock (see + * {@link OperationBuildCache._getDirectFileTransferLockResourceName}) before giving up and + * downloading independently. This is strictly a best-effort optimization to avoid redundant + * downloads when multiple local Rush processes race to restore the same cache entry; it is not + * required for correctness, since downloads always land in a uniquely-named temp file that is + * atomically renamed into place. + */ +const DIRECT_FILE_TRANSFER_LOCK_MAX_WAIT_MS: number = 5 * 60 * 1000; // Five minutes + +/** + * @internal + */ +export interface IOperationBuildCacheOptions { + /** + * The repo-wide configuration for the build cache. + */ + buildCacheConfiguration: BuildCacheConfiguration; + /** + * The terminal to use for logging. + */ + terminal: ITerminal; + /** + * If true, omit AppleDouble (`._*`) files from cache archives when running on macOS + * and a companion file exists in the same directory. + */ + excludeAppleDoubleFiles: boolean; + /** + * If true, use file-based APIs (when available) to transfer cache entries to and from the + * cloud provider, avoiding buffering the entire entry in memory. + */ + useDirectFileTransfersForBuildCache: boolean; +} + +/** + * @internal + */ +export type IProjectBuildCacheOptions = IOperationBuildCacheOptions & { + /** + * Value from rush-project.json + */ + projectOutputFolderNames: ReadonlyArray; + /** + * The project to be cached. + */ + project: RushConfigurationProject; + /** + * The hash of all relevant inputs and configuration that uniquely identifies this execution. + */ + operationStateHash: string; + /** + * The name of the phase that is being cached. + */ + phaseName: string; +}; + +interface IPathsToCache { + filteredOutputFolderNames: string[]; + outputFilePaths: string[]; +} + +function _getDirectFileTransferLockResourceName(cacheId: string): string { + // LockFile resource names must match /^[a-zA-Z0-9][a-zA-Z0-9-.]+[a-zA-Z0-9]$/, but cacheId may + // contain other characters (e.g. "/") depending on the configured cacheEntryNamePattern, so hash + // it into a fixed-length, lock-file-safe token. + return crypto.createHash('sha1').update(cacheId).digest('hex'); +} + +let _tarUtilityPromise: Promise | undefined; + +function _getTempLocalCacheEntryPath(finalLocalCacheEntryPath: string): string { + // Derive the temp file from the destination path to ensure they are on the same volume. + // In the case of a shared network drive containing the build cache, we also need to make + // sure the temp path won't be shared by two parallel rush builds. + const randomSuffix: string = crypto.randomBytes(8).toString('hex'); + return `${finalLocalCacheEntryPath}-${randomSuffix}.temp`; +} + +function _tryGetTarUtility(terminal: ITerminal): Promise { + if (!_tarUtilityPromise) { + _tarUtilityPromise = TarExecutable.tryInitializeAsync(terminal); + } + + return _tarUtilityPromise; +} + +function _getCacheId(options: IProjectBuildCacheOptions): string | undefined { + const { + buildCacheConfiguration, + project: { packageName }, + operationStateHash, + phaseName + } = options; + return buildCacheConfiguration.getCacheEntryId({ + projectName: packageName, + projectStateHash: operationStateHash, + phaseName + }); +} + +/** + * Overrides the cached `TarExecutable` promise. Exposed for unit testing only. + * @internal + */ +export function _setTarUtilityPromiseForTesting( + promise: Promise | undefined +): void { + _tarUtilityPromise = promise; +} + +/** + * @internal + */ +export class OperationBuildCache { + private readonly _project: RushConfigurationProject; + private readonly _localBuildCacheProvider: FileSystemBuildCacheProvider; + private readonly _cloudBuildCacheProvider: ICloudBuildCacheProvider | undefined; + private readonly _buildCacheEnabled: boolean; + private readonly _cacheWriteEnabled: boolean; + private readonly _projectOutputFolderNames: ReadonlyArray; + private readonly _cacheId: string | undefined; + private readonly _excludeAppleDoubleFiles: boolean; + private readonly _useDirectFileTransfersForBuildCache: boolean; + + private constructor(cacheId: string | undefined, options: IProjectBuildCacheOptions) { + const { + buildCacheConfiguration: { + localCacheProvider, + cloudCacheProvider, + buildCacheEnabled, + cacheWriteEnabled + }, + project, + projectOutputFolderNames, + excludeAppleDoubleFiles, + useDirectFileTransfersForBuildCache + } = options; + this._project = project; + this._localBuildCacheProvider = localCacheProvider; + this._cloudBuildCacheProvider = cloudCacheProvider; + this._buildCacheEnabled = buildCacheEnabled; + this._cacheWriteEnabled = cacheWriteEnabled; + this._projectOutputFolderNames = projectOutputFolderNames || []; + this._cacheId = cacheId; + this._excludeAppleDoubleFiles = excludeAppleDoubleFiles && process.platform === 'darwin'; + this._useDirectFileTransfersForBuildCache = useDirectFileTransfersForBuildCache; + } + + public get cacheId(): string | undefined { + return this._cacheId; + } + + public static getOperationBuildCache(options: IProjectBuildCacheOptions): OperationBuildCache { + const cacheId: string | undefined = _getCacheId(options); + return new OperationBuildCache(cacheId, options); + } + + public static forOperation( + executionResult: IBaseOperationExecutionResult, + options: IOperationBuildCacheOptions + ): OperationBuildCache { + const { + buildCacheConfiguration, + terminal, + excludeAppleDoubleFiles, + useDirectFileTransfersForBuildCache + } = options; + const outputFolders: string[] = [...(executionResult.operation.settings?.outputFolderNames ?? [])]; + if (executionResult.metadataFolderPath) { + outputFolders.push(executionResult.metadataFolderPath); + } + + const buildCacheOptions: IProjectBuildCacheOptions = { + buildCacheConfiguration, + terminal, + project: executionResult.operation.associatedProject, + phaseName: executionResult.operation.associatedPhase.name, + projectOutputFolderNames: outputFolders, + operationStateHash: executionResult.getStateHash(), + excludeAppleDoubleFiles, + useDirectFileTransfersForBuildCache + }; + const cacheId: string | undefined = _getCacheId(buildCacheOptions); + return new OperationBuildCache(cacheId, buildCacheOptions); + } + + public async tryRestoreFromCacheAsync(terminal: ITerminal, specifiedCacheId?: string): Promise { + const cacheId: string | undefined = specifiedCacheId || this._cacheId; + if (!cacheId) { + terminal.writeWarningLine('Unable to get cache ID. Ensure Git is installed.'); + return false; + } + + if (!this._buildCacheEnabled) { + // Skip reading local and cloud build caches, without any noise + return false; + } + + let localCacheEntryPath: string | undefined = + await this._localBuildCacheProvider.tryGetCacheEntryPathByIdAsync(terminal, cacheId); + let cloudCacheHit: boolean = false; + let updateLocalCacheSuccess: boolean | undefined; + if (!localCacheEntryPath && this._cloudBuildCacheProvider) { + terminal.writeVerboseLine( + 'This project was not found in the local build cache. Querying the cloud build cache.' + ); + + if ( + this._useDirectFileTransfersForBuildCache && + this._cloudBuildCacheProvider.tryDownloadCacheEntryToFileAsync + ) { + // Use file-based path to avoid loading the entire cache entry into memory. + // The provider downloads directly to a temp file that is atomically moved into place. + const targetPath: string = this._localBuildCacheProvider.getCacheEntryPath(cacheId); + + // If multiple local Rush processes race to restore the same cache entry (e.g. parallel + // "rush build" invocations on the same machine or CI agent), avoid redundant downloads by + // having later processes wait for the first one to finish, mirroring the pattern used for + // installing the package manager (see InstallHelpers.ensureLocalPackageManagerAsync). This + // is strictly a best-effort optimization: if the lock cannot be acquired (for example, + // because it is held by a process on a different machine sharing a network cache folder, + // where the lock's stale-holder detection cannot see across machines) we simply fall back + // to downloading independently below. Correctness never depends on the lock, because + // downloads always land in a uniquely-named temp file that is atomically renamed into + // place. + let downloadLock: LockFile | undefined; + try { + downloadLock = await LockFile.acquireAsync( + path.dirname(targetPath), + _getDirectFileTransferLockResourceName(cacheId), + DIRECT_FILE_TRANSFER_LOCK_MAX_WAIT_MS + ); + } catch (e) { + terminal.writeVerboseLine( + `Unable to acquire the local download lock for cache entry "${cacheId}"; downloading independently: ${e}` + ); + } + + try { + // Another process may have finished populating the cache entry while we were waiting + // for the lock (or the lock may not have been acquired at all). + if (await FileSystem.existsAsync(targetPath)) { + cloudCacheHit = true; + localCacheEntryPath = targetPath; + updateLocalCacheSuccess = true; + } else { + const tempTargetPath: string = _getTempLocalCacheEntryPath(targetPath); + try { + const downloadedToTempFile: boolean = + await this._cloudBuildCacheProvider.tryDownloadCacheEntryToFileAsync( + terminal, + cacheId, + tempTargetPath + ); + if (downloadedToTempFile) { + await Async.runWithRetriesAsync({ + action: () => + FileSystem.moveAsync({ + sourcePath: tempTargetPath, + destinationPath: targetPath, + overwrite: true + }), + maxRetries: 2, + retryDelayMs: 500 + }); + cloudCacheHit = true; + localCacheEntryPath = targetPath; + updateLocalCacheSuccess = true; + } + } catch (e) { + terminal.writeVerboseLine(`Failed to download cache entry to local cache: ${e}`); + updateLocalCacheSuccess = false; + } + + if (!cloudCacheHit) { + // Clean up any partial file left by the failed or missed download so it isn't + // mistaken for a valid cache entry on the next build. Providers may catch errors + // internally and return false instead of throwing, leaving a partially written file. + try { + await FileSystem.deleteFileAsync(tempTargetPath); + } catch { + // Ignore cleanup errors (file may not have been created) + } + } + } + } finally { + downloadLock?.release(); + } + } else { + const cacheEntryBuffer: Buffer | undefined = + await this._cloudBuildCacheProvider.tryGetCacheEntryBufferByIdAsync(terminal, cacheId); + if (cacheEntryBuffer) { + cloudCacheHit = true; + try { + localCacheEntryPath = await this._localBuildCacheProvider.trySetCacheEntryBufferAsync( + terminal, + cacheId, + cacheEntryBuffer + ); + updateLocalCacheSuccess = true; + } catch (e) { + terminal.writeVerboseLine(`Failed to update local cache: ${e}`); + updateLocalCacheSuccess = false; + } + } + } + } + + if (!localCacheEntryPath && !cloudCacheHit) { + terminal.writeVerboseLine('This project was not found in the build cache.'); + return false; + } + + terminal.writeLine('Build cache hit.'); + terminal.writeVerboseLine(`Cache key: ${cacheId}`); + + const projectFolderPath: string = this._project.projectFolder; + + // Purge output folders + terminal.writeVerboseLine(`Clearing cached folders: ${this._projectOutputFolderNames.join(', ')}`); + await Promise.all( + this._projectOutputFolderNames.map((outputFolderName: string) => + FileSystem.deleteFolderAsync(`${projectFolderPath}/${outputFolderName}`) + ) + ); + + const tarUtility: TarExecutable | undefined = await _tryGetTarUtility(terminal); + let restoreSuccess: boolean = false; + if (tarUtility && localCacheEntryPath) { + const logFilePath: string = this._getTarLogFilePath(cacheId, 'untar'); + const tarExitCode: number = await tarUtility.tryUntarAsync({ + archivePath: localCacheEntryPath, + outputFolderPath: projectFolderPath, + logFilePath + }); + if (tarExitCode === 0) { + restoreSuccess = true; + terminal.writeLine('Successfully restored output from the build cache.'); + } else { + terminal.writeWarningLine( + 'Unable to restore output from the build cache. ' + + `See "${logFilePath}" for logs from the tar process.` + ); + } + } + + if (updateLocalCacheSuccess === false) { + terminal.writeWarningLine('Unable to update the local build cache with data from the cloud cache.'); + } + + return restoreSuccess; + } + + public async trySetCacheEntryAsync(terminal: ITerminal, specifiedCacheId?: string): Promise { + if (!this._cacheWriteEnabled) { + // Skip writing local and cloud build caches, without any noise + return true; + } + + const cacheId: string | undefined = specifiedCacheId || this._cacheId; + if (!cacheId) { + terminal.writeWarningLine('Unable to get cache ID. Ensure Git is installed.'); + return false; + } + + const filesToCache: IPathsToCache | undefined = await this._tryCollectPathsToCacheAsync(terminal); + if (!filesToCache) { + return false; + } + + terminal.writeVerboseLine( + `Caching build output folders: ${filesToCache.filteredOutputFolderNames.join(', ')}` + ); + + let localCacheEntryPath: string | undefined; + + const tarUtility: TarExecutable | undefined = await _tryGetTarUtility(terminal); + if (tarUtility) { + const finalLocalCacheEntryPath: string = this._localBuildCacheProvider.getCacheEntryPath(cacheId); + const tempLocalCacheEntryPath: string = _getTempLocalCacheEntryPath(finalLocalCacheEntryPath); + + const logFilePath: string = this._getTarLogFilePath(cacheId, 'tar'); + const tarExitCode: number = await tarUtility.tryCreateArchiveFromProjectPathsAsync({ + archivePath: tempLocalCacheEntryPath, + paths: filesToCache.outputFilePaths, + project: this._project, + logFilePath + }); + + if (tarExitCode === 0) { + // Move after the archive is finished so that if the process is interrupted we aren't left with an invalid file + try { + await Async.runWithRetriesAsync({ + action: () => + FileSystem.moveAsync({ + sourcePath: tempLocalCacheEntryPath, + destinationPath: finalLocalCacheEntryPath, + overwrite: true + }), + maxRetries: 2, + retryDelayMs: 500 + }); + } catch (moveError) { + try { + await FileSystem.deleteFileAsync(tempLocalCacheEntryPath); + } catch (deleteError) { + // Ignored + } + throw moveError; + } + localCacheEntryPath = finalLocalCacheEntryPath; + } else { + terminal.writeWarningLine( + `"tar" exited with code ${tarExitCode} while attempting to create the cache entry. ` + + `See "${logFilePath}" for logs from the tar process.` + ); + return false; + } + } else { + terminal.writeWarningLine( + `Unable to locate "tar". Please ensure that "tar" is on your PATH environment variable, or set the ` + + `${EnvironmentVariableNames.RUSH_TAR_BINARY_PATH} environment variable to the full path to the "tar" binary.` + ); + return false; + } + + let setCloudCacheEntryPromise: Promise | undefined; + + // Note that "writeAllowed" settings (whether in config or environment) always apply to + // the configured CLOUD cache. If the cache is enabled, rush is always allowed to read from and + // write to the local build cache. + + if (this._cloudBuildCacheProvider?.isCacheWriteAllowed) { + if (!localCacheEntryPath) { + throw new InternalError('Expected the local cache entry path to be set.'); + } + + if ( + this._useDirectFileTransfersForBuildCache && + this._cloudBuildCacheProvider.tryUploadCacheEntryFromFileAsync + ) { + // Use file-based upload to avoid loading the entire cache entry into memory. + // The provider reads from the local cache file directly. + setCloudCacheEntryPromise = this._cloudBuildCacheProvider.tryUploadCacheEntryFromFileAsync( + terminal, + cacheId, + localCacheEntryPath + ); + } else { + const cacheEntryBuffer: Buffer = await FileSystem.readFileToBufferAsync(localCacheEntryPath); + setCloudCacheEntryPromise = this._cloudBuildCacheProvider.trySetCacheEntryBufferAsync( + terminal, + cacheId, + cacheEntryBuffer + ); + } + } + + const updateCloudCacheSuccess: boolean | undefined = (await setCloudCacheEntryPromise) ?? true; + + const success: boolean = updateCloudCacheSuccess && !!localCacheEntryPath; + if (success) { + terminal.writeLine('Successfully set cache entry.'); + terminal.writeVerboseLine(`Cache key: ${cacheId}`); + } else if (!localCacheEntryPath && updateCloudCacheSuccess) { + terminal.writeWarningLine('Unable to set local cache entry.'); + } else if (localCacheEntryPath && !updateCloudCacheSuccess) { + terminal.writeWarningLine('Unable to set cloud cache entry.'); + } else { + terminal.writeWarningLine('Unable to set both cloud and local cache entries.'); + } + + return success; + } + + /** + * Walks the declared output folders of the project and collects a list of files. + * @returns The list of output files as project-relative paths, or `undefined` if a + * symbolic link was encountered. + */ + private async _tryCollectPathsToCacheAsync(terminal: ITerminal): Promise { + const projectFolderPath: string = this._project.projectFolder; + const outputFilePaths: string[] = []; + const queue: [string, string][] = []; + + const filteredOutputFolderNames: string[] = []; + + let hasSymbolicLinks: boolean = false; + const excludeAppleDoubleFiles: boolean = this._excludeAppleDoubleFiles; + + // Adds child directories to the queue, files to the path list, and bails on symlinks + function processChildren(relativePath: string, diskPath: string, children: FolderItem[]): void { + // When excluding AppleDouble files, build a set of sibling names so we can check + // whether a companion file exists for each ._X file. + let childNameSet: Set | undefined; + if (excludeAppleDoubleFiles) { + childNameSet = new Set(children.map(({ name }) => name)); + } + + for (const child of children) { + const childName: string = child.name; + const childRelativePath: string = `${relativePath}/${childName}`; + if (child.isSymbolicLink()) { + terminal.writeError( + `Unable to include "${childRelativePath}" in build cache. It is a symbolic link.` + ); + hasSymbolicLinks = true; + } else if (child.isDirectory()) { + queue.push([childRelativePath, `${diskPath}/${child.name}`]); + } else { + // Check for macOS AppleDouble files (._X pattern) that have a companion file + if (childNameSet && childName.length > 2 && childName.startsWith('._')) { + const companionName: string = childName.substring(2); + if (childNameSet.has(companionName)) { + terminal.writeVerboseLine(`Omitting AppleDouble file "${childRelativePath}" from build cache.`); + continue; + } + } + + outputFilePaths.push(childRelativePath); + } + } + } + + // Handle declared output folders. + for (const outputFolder of this._projectOutputFolderNames) { + const diskPath: string = `${projectFolderPath}/${outputFolder}`; + try { + const children: FolderItem[] = await FileSystem.readFolderItemsAsync(diskPath); + processChildren(outputFolder, diskPath, children); + // The folder exists, record it + filteredOutputFolderNames.push(outputFolder); + } catch (error) { + if (!FileSystem.isNotExistError(error as Error)) { + throw error; + } + + // If the folder does not exist, ignore it. + } + } + + for (const [relativePath, diskPath] of queue) { + const children: FolderItem[] = await FileSystem.readFolderItemsAsync(diskPath); + processChildren(relativePath, diskPath, children); + } + + if (hasSymbolicLinks) { + // Symbolic links do not round-trip safely. + return undefined; + } + + // Ensure stable output path order. + outputFilePaths.sort(); + + return { + outputFilePaths, + filteredOutputFolderNames + }; + } + + private _getTarLogFilePath(cacheId: string, mode: 'tar' | 'untar'): string { + return path.join(this._project.projectRushTempFolder, `${cacheId}.${mode}.log`); + } +} diff --git a/libraries/rush-lib/src/logic/buildCache/ProjectBuildCache.ts b/libraries/rush-lib/src/logic/buildCache/ProjectBuildCache.ts deleted file mode 100644 index 658bd3217b0..00000000000 --- a/libraries/rush-lib/src/logic/buildCache/ProjectBuildCache.ts +++ /dev/null @@ -1,495 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import * as path from 'path'; -import * as crypto from 'crypto'; -import { FileSystem, Path, ITerminal, FolderItem, InternalError, Async } from '@rushstack/node-core-library'; - -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; -import { ProjectChangeAnalyzer } from '../ProjectChangeAnalyzer'; -import { RushProjectConfiguration } from '../../api/RushProjectConfiguration'; -import { RushConstants } from '../RushConstants'; -import { BuildCacheConfiguration } from '../../api/BuildCacheConfiguration'; -import { ICloudBuildCacheProvider } from './ICloudBuildCacheProvider'; -import { FileSystemBuildCacheProvider } from './FileSystemBuildCacheProvider'; -import { TarExecutable } from '../../utilities/TarExecutable'; -import { EnvironmentVariableNames } from '../../api/EnvironmentConfiguration'; - -export interface IProjectBuildCacheOptions { - buildCacheConfiguration: BuildCacheConfiguration; - projectConfiguration: RushProjectConfiguration; - projectOutputFolderNames: ReadonlyArray; - additionalProjectOutputFilePaths?: ReadonlyArray; - additionalContext?: Record; - command: string; - trackedProjectFiles: string[] | undefined; - projectChangeAnalyzer: ProjectChangeAnalyzer; - terminal: ITerminal; - phaseName: string; -} - -interface IPathsToCache { - filteredOutputFolderNames: string[]; - outputFilePaths: string[]; -} - -export class ProjectBuildCache { - /** - * null === we haven't tried to initialize yet - * undefined === unable to initialize - */ - private static _tarUtilityPromise: Promise | null = null; - - private readonly _project: RushConfigurationProject; - private readonly _localBuildCacheProvider: FileSystemBuildCacheProvider; - private readonly _cloudBuildCacheProvider: ICloudBuildCacheProvider | undefined; - private readonly _buildCacheEnabled: boolean; - private readonly _cacheWriteEnabled: boolean; - private readonly _projectOutputFolderNames: ReadonlyArray; - private readonly _additionalProjectOutputFilePaths: ReadonlyArray; - private _cacheId: string | undefined; - - private constructor(cacheId: string | undefined, options: IProjectBuildCacheOptions) { - const { - buildCacheConfiguration, - projectConfiguration, - projectOutputFolderNames, - additionalProjectOutputFilePaths - } = options; - this._project = projectConfiguration.project; - this._localBuildCacheProvider = buildCacheConfiguration.localCacheProvider; - this._cloudBuildCacheProvider = buildCacheConfiguration.cloudCacheProvider; - this._buildCacheEnabled = buildCacheConfiguration.buildCacheEnabled; - this._cacheWriteEnabled = buildCacheConfiguration.cacheWriteEnabled; - this._projectOutputFolderNames = projectOutputFolderNames || []; - this._additionalProjectOutputFilePaths = additionalProjectOutputFilePaths || []; - this._cacheId = cacheId; - } - - private static _tryGetTarUtility(terminal: ITerminal): Promise { - if (ProjectBuildCache._tarUtilityPromise === null) { - ProjectBuildCache._tarUtilityPromise = TarExecutable.tryInitializeAsync(terminal); - } - - return ProjectBuildCache._tarUtilityPromise; - } - - public static async tryGetProjectBuildCache( - options: IProjectBuildCacheOptions - ): Promise { - const { terminal, projectConfiguration, projectOutputFolderNames, trackedProjectFiles } = options; - if (!trackedProjectFiles) { - return undefined; - } - - if ( - !ProjectBuildCache._validateProject( - terminal, - projectConfiguration, - projectOutputFolderNames, - trackedProjectFiles - ) - ) { - return undefined; - } - - const cacheId: string | undefined = await ProjectBuildCache._getCacheId(options); - return new ProjectBuildCache(cacheId, options); - } - - private static _validateProject( - terminal: ITerminal, - projectConfiguration: RushProjectConfiguration, - projectOutputFolderNames: ReadonlyArray, - trackedProjectFiles: string[] - ): boolean { - const normalizedProjectRelativeFolder: string = Path.convertToSlashes( - projectConfiguration.project.projectRelativeFolder - ); - const outputFolders: string[] = []; - if (projectOutputFolderNames) { - for (const outputFolderName of projectOutputFolderNames) { - outputFolders.push(`${normalizedProjectRelativeFolder}/${outputFolderName}/`); - } - } - - const inputOutputFiles: string[] = []; - for (const file of trackedProjectFiles) { - for (const outputFolder of outputFolders) { - if (file.startsWith(outputFolder)) { - inputOutputFiles.push(file); - } - } - } - - if (inputOutputFiles.length > 0) { - terminal.writeWarningLine( - 'Unable to use build cache. The following files are used to calculate project state ' + - `and are considered project output: ${inputOutputFiles.join(', ')}` - ); - return false; - } else { - return true; - } - } - - public async tryRestoreFromCacheAsync(terminal: ITerminal): Promise { - const cacheId: string | undefined = this._cacheId; - if (!cacheId) { - terminal.writeWarningLine('Unable to get cache ID. Ensure Git is installed.'); - return false; - } - - if (!this._buildCacheEnabled) { - // Skip reading local and cloud build caches, without any noise - return false; - } - - let localCacheEntryPath: string | undefined = - await this._localBuildCacheProvider.tryGetCacheEntryPathByIdAsync(terminal, cacheId); - let cacheEntryBuffer: Buffer | undefined; - let updateLocalCacheSuccess: boolean | undefined; - if (!localCacheEntryPath && this._cloudBuildCacheProvider) { - terminal.writeVerboseLine( - 'This project was not found in the local build cache. Querying the cloud build cache.' - ); - - cacheEntryBuffer = await this._cloudBuildCacheProvider.tryGetCacheEntryBufferByIdAsync( - terminal, - cacheId - ); - if (cacheEntryBuffer) { - try { - localCacheEntryPath = await this._localBuildCacheProvider.trySetCacheEntryBufferAsync( - terminal, - cacheId, - cacheEntryBuffer - ); - updateLocalCacheSuccess = true; - } catch (e) { - updateLocalCacheSuccess = false; - } - } - } - - if (!localCacheEntryPath && !cacheEntryBuffer) { - terminal.writeVerboseLine('This project was not found in the build cache.'); - return false; - } - - terminal.writeLine('Build cache hit.'); - - const projectFolderPath: string = this._project.projectFolder; - - // Purge output folders - terminal.writeVerboseLine(`Clearing cached folders: ${this._projectOutputFolderNames.join(', ')}`); - await Promise.all( - this._projectOutputFolderNames.map((outputFolderName: string) => - FileSystem.deleteFolderAsync(`${projectFolderPath}/${outputFolderName}`) - ) - ); - - const tarUtility: TarExecutable | undefined = await ProjectBuildCache._tryGetTarUtility(terminal); - let restoreSuccess: boolean = false; - if (tarUtility && localCacheEntryPath) { - const logFilePath: string = this._getTarLogFilePath(); - const tarExitCode: number = await tarUtility.tryUntarAsync({ - archivePath: localCacheEntryPath, - outputFolderPath: projectFolderPath, - logFilePath - }); - if (tarExitCode === 0) { - restoreSuccess = true; - terminal.writeLine('Successfully restored output from the build cache.'); - } else { - terminal.writeWarningLine('Unable to restore output from the build cache.'); - } - } - - if (updateLocalCacheSuccess === false) { - terminal.writeWarningLine('Unable to update the local build cache with data from the cloud cache.'); - } - - return restoreSuccess; - } - - public async trySetCacheEntryAsync(terminal: ITerminal): Promise { - if (!this._cacheWriteEnabled) { - // Skip writing local and cloud build caches, without any noise - return true; - } - - const cacheId: string | undefined = this._cacheId; - if (!cacheId) { - terminal.writeWarningLine('Unable to get cache ID. Ensure Git is installed.'); - return false; - } - - const filesToCache: IPathsToCache | undefined = await this._tryCollectPathsToCacheAsync(terminal); - if (!filesToCache) { - return false; - } - - terminal.writeVerboseLine( - `Caching build output folders: ${filesToCache.filteredOutputFolderNames.join(', ')}` - ); - - let localCacheEntryPath: string | undefined; - - const tarUtility: TarExecutable | undefined = await ProjectBuildCache._tryGetTarUtility(terminal); - if (tarUtility) { - const finalLocalCacheEntryPath: string = this._localBuildCacheProvider.getCacheEntryPath(cacheId); - - // Derive the temp file from the destination path to ensure they are on the same volume - // In the case of a shared network drive containing the build cache, we also need to make - // sure the the temp path won't be shared by two parallel rush builds. - const randomSuffix: string = crypto.randomBytes(8).toString('hex'); - const tempLocalCacheEntryPath: string = `${finalLocalCacheEntryPath}-${randomSuffix}.temp`; - - const logFilePath: string = this._getTarLogFilePath(); - const tarExitCode: number = await tarUtility.tryCreateArchiveFromProjectPathsAsync({ - archivePath: tempLocalCacheEntryPath, - paths: filesToCache.outputFilePaths, - project: this._project, - logFilePath - }); - - if (tarExitCode === 0) { - // Move after the archive is finished so that if the process is interrupted we aren't left with an invalid file - try { - await Async.runWithRetriesAsync({ - action: () => - FileSystem.moveAsync({ - sourcePath: tempLocalCacheEntryPath, - destinationPath: finalLocalCacheEntryPath, - overwrite: true - }), - maxRetries: 2, - retryDelayMs: 500 - }); - } catch (moveError) { - try { - await FileSystem.deleteFileAsync(tempLocalCacheEntryPath); - } catch (deleteError) { - // Ignored - } - throw moveError; - } - localCacheEntryPath = finalLocalCacheEntryPath; - } else { - terminal.writeWarningLine( - `"tar" exited with code ${tarExitCode} while attempting to create the cache entry. ` + - `See "${logFilePath}" for logs from the tar process.` - ); - return false; - } - } else { - terminal.writeWarningLine( - `Unable to locate "tar". Please ensure that "tar" is on your PATH environment variable, or set the ` + - `${EnvironmentVariableNames.RUSH_TAR_BINARY_PATH} environment variable to the full path to the "tar" binary.` - ); - return false; - } - - let cacheEntryBuffer: Buffer | undefined; - - let setCloudCacheEntryPromise: Promise | undefined; - - // Note that "writeAllowed" settings (whether in config or environment) always apply to - // the configured CLOUD cache. If the cache is enabled, rush is always allowed to read from and - // write to the local build cache. - - if (this._cloudBuildCacheProvider?.isCacheWriteAllowed) { - if (localCacheEntryPath) { - cacheEntryBuffer = await FileSystem.readFileToBufferAsync(localCacheEntryPath); - } else { - throw new InternalError('Expected the local cache entry path to be set.'); - } - - setCloudCacheEntryPromise = this._cloudBuildCacheProvider?.trySetCacheEntryBufferAsync( - terminal, - cacheId, - cacheEntryBuffer - ); - } - - const updateCloudCacheSuccess: boolean | undefined = (await setCloudCacheEntryPromise) ?? true; - - const success: boolean = updateCloudCacheSuccess && !!localCacheEntryPath; - if (success) { - terminal.writeLine('Successfully set cache entry.'); - } else if (!localCacheEntryPath && updateCloudCacheSuccess) { - terminal.writeWarningLine('Unable to set local cache entry.'); - } else if (localCacheEntryPath && !updateCloudCacheSuccess) { - terminal.writeWarningLine('Unable to set cloud cache entry.'); - } else { - terminal.writeWarningLine('Unable to set both cloud and local cache entries.'); - } - - return success; - } - - /** - * Walks the declared output folders of the project and collects a list of files. - * @returns The list of output files as project-relative paths, or `undefined` if a - * symbolic link was encountered. - */ - private async _tryCollectPathsToCacheAsync(terminal: ITerminal): Promise { - const projectFolderPath: string = this._project.projectFolder; - const outputFilePaths: string[] = []; - const queue: [string, string][] = []; - - const filteredOutputFolderNames: string[] = []; - - let hasSymbolicLinks: boolean = false; - - // Adds child directories to the queue, files to the path list, and bails on symlinks - function processChildren(relativePath: string, diskPath: string, children: FolderItem[]): void { - for (const child of children) { - const childRelativePath: string = `${relativePath}/${child.name}`; - if (child.isSymbolicLink()) { - terminal.writeError( - `Unable to include "${childRelativePath}" in build cache. It is a symbolic link.` - ); - hasSymbolicLinks = true; - } else if (child.isDirectory()) { - queue.push([childRelativePath, `${diskPath}/${child.name}`]); - } else { - outputFilePaths.push(childRelativePath); - } - } - } - - // Handle declared output folders. - for (const outputFolder of this._projectOutputFolderNames) { - const diskPath: string = `${projectFolderPath}/${outputFolder}`; - try { - const children: FolderItem[] = await FileSystem.readFolderItemsAsync(diskPath); - processChildren(outputFolder, diskPath, children); - // The folder exists, record it - filteredOutputFolderNames.push(outputFolder); - } catch (error) { - if (!FileSystem.isNotExistError(error as Error)) { - throw error; - } - - // If the folder does not exist, ignore it. - } - } - - for (const [relativePath, diskPath] of queue) { - const children: FolderItem[] = await FileSystem.readFolderItemsAsync(diskPath); - processChildren(relativePath, diskPath, children); - } - - if (hasSymbolicLinks) { - // Symbolic links do not round-trip safely. - return undefined; - } - - // Add additional output file paths - await Async.forEachAsync( - this._additionalProjectOutputFilePaths, - async (additionalProjectOutputFilePath) => { - const fullPath: string = `${projectFolderPath}/${additionalProjectOutputFilePath}`; - const pathExists: boolean = await FileSystem.existsAsync(fullPath); - if (pathExists) { - outputFilePaths.push(additionalProjectOutputFilePath); - } - }, - { concurrency: 10 } - ); - - // Ensure stable output path order. - outputFilePaths.sort(); - - return { - outputFilePaths, - filteredOutputFolderNames - }; - } - - private _getTarLogFilePath(): string { - return path.join(this._project.projectRushTempFolder, `${this._cacheId}.log`); - } - - private static async _getCacheId(options: IProjectBuildCacheOptions): Promise { - // The project state hash is calculated in the following method: - // - The current project's hash (see ProjectChangeAnalyzer.getProjectStateHash) is - // calculated and appended to an array - // - The current project's recursive dependency projects' hashes are calculated - // and appended to the array - // - A SHA1 hash is created and the following data is fed into it, in order: - // 1. The JSON-serialized list of output folder names for this - // project (see ProjectBuildCache._projectOutputFolderNames) - // 2. The command that will be run in the project - // 3. Each dependency project hash (from the array constructed in previous steps), - // in sorted alphanumerical-sorted order - // - A hex digest of the hash is returned - const projectChangeAnalyzer: ProjectChangeAnalyzer = options.projectChangeAnalyzer; - const projectStates: string[] = []; - const projectsThatHaveBeenProcessed: Set = new Set(); - let projectsToProcess: Set = new Set(); - projectsToProcess.add(options.projectConfiguration.project); - - while (projectsToProcess.size > 0) { - const newProjectsToProcess: Set = new Set(); - for (const projectToProcess of projectsToProcess) { - projectsThatHaveBeenProcessed.add(projectToProcess); - - const projectState: string | undefined = await projectChangeAnalyzer._tryGetProjectStateHashAsync( - projectToProcess, - options.terminal - ); - if (!projectState) { - // If we hit any projects with unknown state, return unknown cache ID - return undefined; - } else { - projectStates.push(projectState); - for (const dependency of projectToProcess.dependencyProjects) { - if (!projectsThatHaveBeenProcessed.has(dependency)) { - newProjectsToProcess.add(dependency); - } - } - } - } - - projectsToProcess = newProjectsToProcess; - } - - const sortedProjectStates: string[] = projectStates.sort(); - const hash: crypto.Hash = crypto.createHash('sha1'); - // This value is used to force cache bust when the build cache algorithm changes - hash.update(`${RushConstants.buildCacheVersion}`); - hash.update(RushConstants.hashDelimiter); - const serializedOutputFolders: string = JSON.stringify(options.projectOutputFolderNames); - hash.update(serializedOutputFolders); - hash.update(RushConstants.hashDelimiter); - hash.update(options.command); - hash.update(RushConstants.hashDelimiter); - if (options.additionalContext) { - for (const key of Object.keys(options.additionalContext).sort()) { - // Add additional context keys and values. - // - // This choice (to modiy the hash for every key regardless of whether a value is set) implies - // that just _adding_ an env var to the list of dependsOnEnvVars will modify its hash. This - // seems appropriate, because this behavior is consistent whether or not the env var happens - // to have a value. - hash.update(`${key}=${options.additionalContext[key]}`); - hash.update(RushConstants.hashDelimiter); - } - } - for (const projectHash of sortedProjectStates) { - hash.update(projectHash); - hash.update(RushConstants.hashDelimiter); - } - - const projectStateHash: string = hash.digest('hex'); - - return options.buildCacheConfiguration.getCacheEntryId({ - projectName: options.projectConfiguration.project.packageName, - projectStateHash, - phaseName: options.phaseName - }); - } -} diff --git a/libraries/rush-lib/src/logic/buildCache/getHashesForGlobsAsync.ts b/libraries/rush-lib/src/logic/buildCache/getHashesForGlobsAsync.ts deleted file mode 100644 index acc4d4ded28..00000000000 --- a/libraries/rush-lib/src/logic/buildCache/getHashesForGlobsAsync.ts +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { Async, LegacyAdapters } from '@rushstack/node-core-library'; -import { getGitHashForFiles } from '@rushstack/package-deps-hash'; -import * as path from 'path'; -import type { IOptions } from 'glob'; -import type { IRawRepoState } from '../ProjectChangeAnalyzer'; - -async function expandGlobPatternsAsync( - globPatterns: Iterable, - packagePath: string -): Promise { - const allMatches: Set = new Set(); - - const { default: glob } = await import('glob'); - const globAsync = (pattern: string, options: IOptions = {}): Promise => { - return LegacyAdapters.convertCallbackToPromise(glob, pattern, options); - }; - await Async.forEachAsync( - globPatterns, - async (pattern) => { - const matches: string[] = await globAsync(pattern, { - cwd: packagePath, - nodir: true, - // We want to keep path's type unchanged, - // i.e. if the pattern was a relative path, then matched paths should also be relative paths - // if the pattern was an absolute path, then matched paths should also be absolute paths - // - // We are doing this because these paths are going to be used to calculate a hash for the build cache and some users - // might choose to depend on global files (e.g. `/etc/os-release`) and some might choose to depend on local files - // (e.g. `../path/to/workspace/file`) - // - // In both cases we want that path to the resource would be the same on all machines, - // regardless of what is the current working directory. - // - // That being said, we want to keep `realpath` and `absolute` options here as false: - realpath: false, - absolute: false - }); - matches.forEach((match) => allMatches.add(match)); - }, - { concurrency: 10 } - ); - - if (allMatches.size === 0) { - throw new Error( - `Couldn't find any files matching provided glob patterns: ["${Array.from(globPatterns).join('", "')}"].` - ); - } - - return Array.from(allMatches); -} - -interface IKnownHashesResult { - foundPaths: Map; - missingPaths: string[]; -} - -function getKnownHashes( - filePaths: string[], - packagePath: string, - repoState: IRawRepoState -): IKnownHashesResult { - const missingPaths: string[] = []; - const foundPaths: Map = new Map(); - - for (const filePath of filePaths) { - const absolutePath: string = path.isAbsolute(filePath) ? filePath : path.join(packagePath, filePath); - - /** - * We are using RegExp here to prevent false positives in the following string.replace function - * - `^` anchor makes sure that we are replacing only the beginning of the string - * - extra `/` makes sure that we are remove extra slash from the relative path - */ - const gitFilePath: string = absolutePath.replace(new RegExp('^' + repoState.rootDir + '/'), ''); - const foundHash: string | undefined = repoState.rawHashes.get(gitFilePath); - - if (foundHash) { - foundPaths.set(filePath, foundHash); - } else { - missingPaths.push(filePath); - } - } - - return { foundPaths, missingPaths }; -} - -export async function getHashesForGlobsAsync( - globPatterns: Iterable, - packagePath: string, - repoState: IRawRepoState | undefined -): Promise> { - const filePaths: string[] = await expandGlobPatternsAsync(globPatterns, packagePath); - - if (!repoState) { - return getGitHashForFiles(filePaths, packagePath); - } - - const { foundPaths, missingPaths } = getKnownHashes(filePaths, packagePath, repoState); - const calculatedHashes: Map = getGitHashForFiles(missingPaths, packagePath); - - /** - * We want to keep the order of the output the same regardless whether the file was already - * hashed by git or not (as this can change, e.g. due to .gitignore). - * Therefore we will populate our final hashes map in the same order as `filePaths`. - */ - const result: Map = new Map(); - for (const filePath of filePaths) { - const hash: string | undefined = foundPaths.get(filePath) || calculatedHashes.get(filePath); - if (!hash) { - // Sanity check -- this should never happen - throw new Error(`Failed to calculate hash of file: "${filePath}"`); - } - result.set(filePath, hash); - } - - return result; -} diff --git a/libraries/rush-lib/src/logic/buildCache/test/CacheEntryId.test.ts b/libraries/rush-lib/src/logic/buildCache/test/CacheEntryId.test.ts index c05247f5c56..83e7c3afd1e 100644 --- a/libraries/rush-lib/src/logic/buildCache/test/CacheEntryId.test.ts +++ b/libraries/rush-lib/src/logic/buildCache/test/CacheEntryId.test.ts @@ -1,86 +1,72 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { CacheEntryId, GetCacheEntryIdFunction, IGenerateCacheEntryIdOptions } from '../CacheEntryId'; +jest.mock('node:process', () => { + return { + ...jest.requireActual('node:process'), + platform: 'dummyplatform', + arch: 'dummyarch' + }; +}); + +import { CacheEntryId, type GetCacheEntryIdFunction } from '../CacheEntryId'; describe(CacheEntryId.name, () => { describe('Valid pattern names', () => { - function validatePatternMatchesSnapshot( - projectName: string, - pattern?: string, - generateCacheEntryIdOptions?: Partial - ): void { - const getCacheEntryId: GetCacheEntryIdFunction = CacheEntryId.parsePattern(pattern); - expect( - getCacheEntryId({ - projectName, - projectStateHash: '09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3', - phaseName: '_phase:compile', - ...generateCacheEntryIdOptions - }) - ).toMatchSnapshot(pattern || 'no pattern'); - } - - // prettier-ignore - it('Handles a cache entry name for a project name without a scope', () => { - const projectName: string = 'project+name'; - validatePatternMatchesSnapshot(projectName); - validatePatternMatchesSnapshot(projectName, '[hash]'); - validatePatternMatchesSnapshot(projectName, '[projectName]_[hash]'); - validatePatternMatchesSnapshot(projectName, '[phaseName:normalize]_[hash]'); - validatePatternMatchesSnapshot(projectName, '[phaseName:trimPrefix]_[hash]'); - validatePatternMatchesSnapshot(projectName, '[projectName:normalize]_[hash]'); - validatePatternMatchesSnapshot(projectName, '[projectName:normalize]_[phaseName:normalize]_[hash]'); - validatePatternMatchesSnapshot(projectName, '[projectName:normalize]_[phaseName:trimPrefix]_[hash]'); - validatePatternMatchesSnapshot(projectName, 'prefix/[projectName:normalize]_[hash]'); - validatePatternMatchesSnapshot(projectName, 'prefix/[projectName:normalize]_[phaseName:normalize]_[hash]'); - validatePatternMatchesSnapshot(projectName, 'prefix/[projectName:normalize]_[phaseName:trimPrefix]_[hash]'); - validatePatternMatchesSnapshot(projectName, 'prefix/[projectName]_[hash]'); - validatePatternMatchesSnapshot(projectName, 'prefix/[projectName]_[phaseName:normalize]_[hash]'); - validatePatternMatchesSnapshot(projectName, 'prefix/[projectName]_[phaseName:trimPrefix]_[hash]'); - }); - - // prettier-ignore - it('Handles a cache entry name for a project name with a scope', () => { - const projectName: string = '@scope/project+name'; - validatePatternMatchesSnapshot(projectName); - validatePatternMatchesSnapshot(projectName, '[hash]'); - validatePatternMatchesSnapshot(projectName, '[projectName]_[hash]'); - validatePatternMatchesSnapshot(projectName, '[phaseName:normalize]_[hash]'); - validatePatternMatchesSnapshot(projectName, '[phaseName:trimPrefix]_[hash]'); - validatePatternMatchesSnapshot(projectName, '[projectName:normalize]_[hash]'); - validatePatternMatchesSnapshot(projectName, '[projectName:normalize]_[phaseName:normalize]_[hash]'); - validatePatternMatchesSnapshot(projectName, '[projectName:normalize]_[phaseName:trimPrefix]_[hash]'); - validatePatternMatchesSnapshot(projectName, 'prefix/[projectName:normalize]_[hash]'); - validatePatternMatchesSnapshot(projectName, 'prefix/[projectName:normalize]_[phaseName:normalize]_[hash]'); - validatePatternMatchesSnapshot(projectName, 'prefix/[projectName:normalize]_[phaseName:trimPrefix]_[hash]'); - validatePatternMatchesSnapshot(projectName, 'prefix/[projectName]_[hash]'); - validatePatternMatchesSnapshot(projectName, 'prefix/[projectName]_[phaseName:normalize]_[hash]'); - validatePatternMatchesSnapshot(projectName, 'prefix/[projectName]_[phaseName:trimPrefix]_[hash]'); - }); + describe.each([ + { projectName: 'project+name', note: 'without a scope' }, + { projectName: '@scope/project+name', note: 'with a scope' } + ])('For a project name $note', ({ projectName }) => + it.each([ + undefined, + '[hash]', + '[projectName]_[hash]', + '[phaseName:normalize]_[hash]', + '[phaseName:trimPrefix]_[hash]', + '[projectName:normalize]_[hash]', + '[projectName:normalize]_[phaseName:normalize]_[hash]', + '[projectName:normalize]_[phaseName:normalize]_[hash]_[os]_[arch]', + '[projectName:normalize]_[phaseName:trimPrefix]_[hash]', + 'prefix/[projectName:normalize]_[hash]', + 'prefix/[projectName:normalize]_[phaseName:normalize]_[hash]', + 'prefix/[projectName:normalize]_[phaseName:trimPrefix]_[hash]', + 'prefix/[projectName]_[hash]', + 'prefix/[projectName]_[phaseName:normalize]_[hash]', + 'prefix/[projectName]_[phaseName:trimPrefix]_[hash]' + ])('Handles pattern %s', (pattern) => { + const getCacheEntryId: GetCacheEntryIdFunction = CacheEntryId.parsePattern(pattern); + expect( + getCacheEntryId({ + projectName, + projectStateHash: '09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3', + phaseName: '_phase:compile' + }) + ).toMatchSnapshot(); + }) + ); }); describe('Invalid pattern names', () => { - async function validateInvalidPatternErrorMatchesSnapshotAsync(pattern: string): Promise { - await expect(() => CacheEntryId.parsePattern(pattern)).toThrowErrorMatchingSnapshot(); - } - - it('Throws an exception for an invalid pattern', async () => { - await validateInvalidPatternErrorMatchesSnapshotAsync('x'); - await validateInvalidPatternErrorMatchesSnapshotAsync('[invalidTag]'); - await validateInvalidPatternErrorMatchesSnapshotAsync('unstartedTag]'); - await validateInvalidPatternErrorMatchesSnapshotAsync('[incompleteTag'); - await validateInvalidPatternErrorMatchesSnapshotAsync('[hash:badAttribute]'); - await validateInvalidPatternErrorMatchesSnapshotAsync('[hash:badAttribute:attr2]'); - await validateInvalidPatternErrorMatchesSnapshotAsync('[projectName:badAttribute]'); - await validateInvalidPatternErrorMatchesSnapshotAsync('[projectName:]'); - await validateInvalidPatternErrorMatchesSnapshotAsync('[phaseName]'); - await validateInvalidPatternErrorMatchesSnapshotAsync('[phaseName:]'); - await validateInvalidPatternErrorMatchesSnapshotAsync('[phaseName:badAttribute]'); - await validateInvalidPatternErrorMatchesSnapshotAsync('[:attr1]'); - await validateInvalidPatternErrorMatchesSnapshotAsync('[projectName:attr1:attr2]'); - await validateInvalidPatternErrorMatchesSnapshotAsync('/[hash]'); - await validateInvalidPatternErrorMatchesSnapshotAsync('~'); + it.each([ + 'x', + '[invalidTag]', + 'unstartedTag]', + '[incompleteTag', + '[hash:badAttribute]', + '[hash:badAttribute:attr2]', + '[projectName:badAttribute]', + '[projectName:]', + '[phaseName]', + '[phaseName:]', + '[phaseName:badAttribute]', + '[:attr1]', + '[projectName:attr1:attr2]', + '/[hash]', + '[os:attr]', + '[arch:attr]', + '~' + ])('Throws an exception for an invalid pattern (%s)', (pattern) => { + expect(() => CacheEntryId.parsePattern(pattern)).toThrowErrorMatchingSnapshot(); }); }); }); diff --git a/libraries/rush-lib/src/logic/buildCache/test/OperationBuildCache.test.ts b/libraries/rush-lib/src/logic/buildCache/test/OperationBuildCache.test.ts new file mode 100644 index 00000000000..d91554100aa --- /dev/null +++ b/libraries/rush-lib/src/logic/buildCache/test/OperationBuildCache.test.ts @@ -0,0 +1,395 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { FileSystem, type FolderItem, LockFile } from '@rushstack/node-core-library'; +import { StringBufferTerminalProvider, Terminal, type ITerminal } from '@rushstack/terminal'; + +import type { BuildCacheConfiguration } from '../../../api/BuildCacheConfiguration'; +import type { RushConfigurationProject } from '../../../api/RushConfigurationProject'; +import type { IGenerateCacheEntryIdOptions } from '../CacheEntryId'; +import type { FileSystemBuildCacheProvider } from '../FileSystemBuildCacheProvider'; +import type { TarExecutable } from '../../../utilities/TarExecutable'; + +import { OperationBuildCache, _setTarUtilityPromiseForTesting } from '../OperationBuildCache'; + +interface ITestOptions { + enabled: boolean; + writeAllowed: boolean; + trackedProjectFiles: string[] | undefined; + excludeAppleDoubleFiles: boolean; +} + +function createFolderItem(name: string, type: 'file' | 'directory' | 'symlink'): FolderItem { + return { + name, + isSymbolicLink: () => type === 'symlink', + isDirectory: () => type === 'directory', + isFile: () => type === 'file', + isBlockDevice: () => false, + isCharacterDevice: () => false, + isFIFO: () => false, + isSocket: () => false, + parentPath: '', + path: name + } as unknown as FolderItem; +} + +describe(OperationBuildCache.name, () => { + function prepareSubject(options: Partial): OperationBuildCache { + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); + + const subject: OperationBuildCache = OperationBuildCache.getOperationBuildCache({ + buildCacheConfiguration: { + buildCacheEnabled: options.hasOwnProperty('enabled') ? options.enabled : true, + getCacheEntryId: (opts: IGenerateCacheEntryIdOptions) => + `${opts.projectName}/${opts.projectStateHash}`, + localCacheProvider: undefined as unknown as FileSystemBuildCacheProvider, + cloudCacheProvider: { + isCacheWriteAllowed: options.hasOwnProperty('writeAllowed') ? options.writeAllowed : false + } + } as unknown as BuildCacheConfiguration, + projectOutputFolderNames: ['dist'], + project: { + packageName: 'acme-wizard', + projectRelativeFolder: 'apps/acme-wizard', + projectFolder: '/repo/apps/acme-wizard', + projectRushTempFolder: '/repo/common/temp/project', + dependencyProjects: [] + } as unknown as RushConfigurationProject, + // Value from past tests, for consistency. + // The project build cache is not responsible for calculating this value. + operationStateHash: '1926f30e8ed24cb47be89aea39e7efd70fcda075', + terminal, + phaseName: 'build', + excludeAppleDoubleFiles: !!options.excludeAppleDoubleFiles, + useDirectFileTransfersForBuildCache: false + }); + + return subject; + } + + describe(OperationBuildCache.getOperationBuildCache.name, () => { + it('returns an OperationBuildCache with a calculated cacheId value', () => { + const subject: OperationBuildCache = prepareSubject({}); + expect(subject['_cacheId']).toMatchInlineSnapshot( + `"acme-wizard/1926f30e8ed24cb47be89aea39e7efd70fcda075"` + ); + }); + }); + + describe('direct file cloud cache restore', () => { + let fakeLockRelease: jest.Mock; + + function mockTarSuccess(): jest.Mock { + const tryUntarAsync: jest.Mock = jest.fn().mockResolvedValue(0); + _setTarUtilityPromiseForTesting(Promise.resolve({ tryUntarAsync } as unknown as TarExecutable)); + return tryUntarAsync; + } + + beforeEach(() => { + fakeLockRelease = jest.fn(); + // By default, simulate an uncontended lock acquisition and no pre-existing cache entry. + // Individual tests may override these to exercise the lock-contention/fallback paths. + jest + .spyOn(LockFile, 'acquireAsync') + .mockImplementation(async () => ({ release: fakeLockRelease }) as unknown as LockFile); + jest.spyOn(FileSystem, 'existsAsync').mockResolvedValue(false); + }); + + afterEach(() => { + _setTarUtilityPromiseForTesting(undefined); + jest.restoreAllMocks(); + }); + + function prepareDirectTransferSubject(cloudBuildCacheProvider: { + tryDownloadCacheEntryToFileAsync: jest.Mock, [ITerminal, string, string]>; + }): OperationBuildCache { + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); + + return OperationBuildCache.getOperationBuildCache({ + buildCacheConfiguration: { + buildCacheEnabled: true, + getCacheEntryId: (opts: IGenerateCacheEntryIdOptions) => + `${opts.projectName}/${opts.projectStateHash}`, + localCacheProvider: { + getCacheEntryPath: jest.fn().mockReturnValue('/cache/acme-wizard-cache-entry'), + tryGetCacheEntryPathByIdAsync: jest.fn().mockResolvedValue(undefined) + }, + cloudCacheProvider: { + isCacheWriteAllowed: false, + ...cloudBuildCacheProvider + } + } as unknown as BuildCacheConfiguration, + projectOutputFolderNames: ['dist'], + project: { + packageName: 'acme-wizard', + projectRelativeFolder: 'apps/acme-wizard', + projectFolder: '/repo/apps/acme-wizard', + projectRushTempFolder: '/repo/common/temp/project', + dependencyProjects: [] + } as unknown as RushConfigurationProject, + operationStateHash: '1926f30e8ed24cb47be89aea39e7efd70fcda075', + terminal, + phaseName: 'build', + excludeAppleDoubleFiles: false, + useDirectFileTransfersForBuildCache: true + }); + } + + it('downloads cloud cache entries to a temp file before atomically moving them into place', async () => { + const tryDownloadCacheEntryToFileAsync: jest.Mock, [ITerminal, string, string]> = jest + .fn() + .mockResolvedValue(true); + const subject: OperationBuildCache = prepareDirectTransferSubject({ + tryDownloadCacheEntryToFileAsync + }); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); + + jest.spyOn(FileSystem, 'deleteFolderAsync').mockResolvedValue(); + const moveAsyncSpy: jest.SpyInstance = jest.spyOn(FileSystem, 'moveAsync').mockResolvedValue(); + const deleteFileAsyncSpy: jest.SpyInstance = jest + .spyOn(FileSystem, 'deleteFileAsync') + .mockResolvedValue(); + const tryUntarAsync: jest.Mock = mockTarSuccess(); + + const result: boolean = await subject.tryRestoreFromCacheAsync(terminal); + + expect(result).toBe(true); + expect(tryDownloadCacheEntryToFileAsync).toHaveBeenCalledTimes(1); + const [, , tempPath]: [ITerminal, string, string] = tryDownloadCacheEntryToFileAsync.mock.calls[0]; + expect(tempPath).toMatch(/^\/cache\/acme-wizard-cache-entry-[0-9a-f]+\.temp$/); + expect(moveAsyncSpy).toHaveBeenCalledWith({ + sourcePath: tempPath, + destinationPath: '/cache/acme-wizard-cache-entry', + overwrite: true + }); + expect(tryUntarAsync).toHaveBeenCalledWith( + expect.objectContaining({ + archivePath: '/cache/acme-wizard-cache-entry' + }) + ); + expect(deleteFileAsyncSpy).not.toHaveBeenCalled(); + expect(LockFile.acquireAsync).toHaveBeenCalledWith( + '/cache', + expect.stringMatching(/^[0-9a-f]{40}$/), + expect.any(Number) + ); + expect(fakeLockRelease).toHaveBeenCalledTimes(1); + }); + + it('cleans up the temp file when a direct file download misses or fails', async () => { + const tryDownloadCacheEntryToFileAsync: jest.Mock, [ITerminal, string, string]> = jest + .fn() + .mockResolvedValue(false); + const subject: OperationBuildCache = prepareDirectTransferSubject({ + tryDownloadCacheEntryToFileAsync + }); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); + + const deleteFileAsyncSpy: jest.SpyInstance = jest + .spyOn(FileSystem, 'deleteFileAsync') + .mockResolvedValue(); + + const result: boolean = await subject.tryRestoreFromCacheAsync(terminal); + + expect(result).toBe(false); + expect(tryDownloadCacheEntryToFileAsync).toHaveBeenCalledTimes(1); + const [, , tempPath]: [ITerminal, string, string] = tryDownloadCacheEntryToFileAsync.mock.calls[0]; + expect(tempPath).toMatch(/^\/cache\/acme-wizard-cache-entry-[0-9a-f]+\.temp$/); + expect(deleteFileAsyncSpy).toHaveBeenCalledWith(tempPath); + }); + + it('skips downloading when another process already populated the cache entry while waiting for the lock', async () => { + const tryDownloadCacheEntryToFileAsync: jest.Mock< + Promise, + [ITerminal, string, string] + > = jest.fn(); + const subject: OperationBuildCache = prepareDirectTransferSubject({ + tryDownloadCacheEntryToFileAsync + }); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); + + jest.spyOn(FileSystem, 'deleteFolderAsync').mockResolvedValue(); + // Simulate the cache entry having been fully populated (e.g. by another local process) + // by the time we acquired the lock. + jest.spyOn(FileSystem, 'existsAsync').mockResolvedValue(true); + const tryUntarAsync: jest.Mock = mockTarSuccess(); + + const result: boolean = await subject.tryRestoreFromCacheAsync(terminal); + + expect(result).toBe(true); + expect(tryDownloadCacheEntryToFileAsync).not.toHaveBeenCalled(); + expect(tryUntarAsync).toHaveBeenCalledWith( + expect.objectContaining({ + archivePath: '/cache/acme-wizard-cache-entry' + }) + ); + expect(fakeLockRelease).toHaveBeenCalledTimes(1); + }); + + it('falls back to downloading independently when the download lock cannot be acquired', async () => { + jest.spyOn(LockFile, 'acquireAsync').mockRejectedValue(new Error('Exceeded maximum wait time')); + + const tryDownloadCacheEntryToFileAsync: jest.Mock, [ITerminal, string, string]> = jest + .fn() + .mockResolvedValue(true); + const subject: OperationBuildCache = prepareDirectTransferSubject({ + tryDownloadCacheEntryToFileAsync + }); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); + + jest.spyOn(FileSystem, 'deleteFolderAsync').mockResolvedValue(); + jest.spyOn(FileSystem, 'moveAsync').mockResolvedValue(); + mockTarSuccess(); + + const result: boolean = await subject.tryRestoreFromCacheAsync(terminal); + + expect(result).toBe(true); + expect(tryDownloadCacheEntryToFileAsync).toHaveBeenCalledTimes(1); + // No lock instance was returned, so there is nothing to release. + expect(fakeLockRelease).not.toHaveBeenCalled(); + }); + }); + + describe('AppleDouble file exclusion', () => { + const originalPlatform: NodeJS.Platform = process.platform; + + afterEach(() => { + Object.defineProperty(process, 'platform', { value: originalPlatform }); + jest.restoreAllMocks(); + }); + + it('omits AppleDouble files with companions when enabled on macOS', async () => { + Object.defineProperty(process, 'platform', { value: 'darwin' }); + + const subject: OperationBuildCache = prepareSubject({ excludeAppleDoubleFiles: true }); + + jest + .spyOn(FileSystem, 'readFolderItemsAsync') + .mockResolvedValue([ + createFolderItem('foo.txt', 'file'), + createFolderItem('._foo.txt', 'file'), + createFolderItem('bar.js', 'file'), + createFolderItem('._bar.js', 'file') + ]); + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(true); + const terminal: Terminal = new Terminal(terminalProvider); + + const result: { outputFilePaths: string[]; filteredOutputFolderNames: string[] } | undefined = + await subject['_tryCollectPathsToCacheAsync'](terminal); + + expect(result).toBeDefined(); + expect(result!.outputFilePaths).toEqual(['dist/bar.js', 'dist/foo.txt']); + expect(result!.outputFilePaths).not.toContain('dist/._foo.txt'); + expect(result!.outputFilePaths).not.toContain('dist/._bar.js'); + }); + + it('keeps AppleDouble files without companion files', async () => { + Object.defineProperty(process, 'platform', { value: 'darwin' }); + + const subject: OperationBuildCache = prepareSubject({ excludeAppleDoubleFiles: true }); + + jest + .spyOn(FileSystem, 'readFolderItemsAsync') + .mockResolvedValue([createFolderItem('._orphan.txt', 'file'), createFolderItem('other.js', 'file')]); + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(true); + const terminal: Terminal = new Terminal(terminalProvider); + + const result: { outputFilePaths: string[]; filteredOutputFolderNames: string[] } | undefined = + await subject['_tryCollectPathsToCacheAsync'](terminal); + + expect(result).toBeDefined(); + expect(result!.outputFilePaths).toEqual(['dist/._orphan.txt', 'dist/other.js']); + }); + + it('does not exclude AppleDouble files when the experiment is disabled', async () => { + Object.defineProperty(process, 'platform', { value: 'darwin' }); + + const subject: OperationBuildCache = prepareSubject({ excludeAppleDoubleFiles: false }); + + jest + .spyOn(FileSystem, 'readFolderItemsAsync') + .mockResolvedValue([createFolderItem('foo.txt', 'file'), createFolderItem('._foo.txt', 'file')]); + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(true); + const terminal: Terminal = new Terminal(terminalProvider); + + const result: { outputFilePaths: string[]; filteredOutputFolderNames: string[] } | undefined = + await subject['_tryCollectPathsToCacheAsync'](terminal); + + expect(result).toBeDefined(); + expect(result!.outputFilePaths).toEqual(['dist/._foo.txt', 'dist/foo.txt']); + }); + + it('does not exclude AppleDouble files on non-macOS platforms', async () => { + Object.defineProperty(process, 'platform', { value: 'win32' }); + + const subject: OperationBuildCache = prepareSubject({ excludeAppleDoubleFiles: true }); + + jest + .spyOn(FileSystem, 'readFolderItemsAsync') + .mockResolvedValue([createFolderItem('foo.txt', 'file'), createFolderItem('._foo.txt', 'file')]); + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(true); + const terminal: Terminal = new Terminal(terminalProvider); + + const result: { outputFilePaths: string[]; filteredOutputFolderNames: string[] } | undefined = + await subject['_tryCollectPathsToCacheAsync'](terminal); + + expect(result).toBeDefined(); + expect(result!.outputFilePaths).toEqual(['dist/._foo.txt', 'dist/foo.txt']); + }); + + it('does not exclude files named exactly "._"', async () => { + Object.defineProperty(process, 'platform', { value: 'darwin' }); + + const subject: OperationBuildCache = prepareSubject({ excludeAppleDoubleFiles: true }); + + jest + .spyOn(FileSystem, 'readFolderItemsAsync') + .mockResolvedValue([createFolderItem('._', 'file'), createFolderItem('other.txt', 'file')]); + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(true); + const terminal: Terminal = new Terminal(terminalProvider); + + const result: { outputFilePaths: string[]; filteredOutputFolderNames: string[] } | undefined = + await subject['_tryCollectPathsToCacheAsync'](terminal); + + expect(result).toBeDefined(); + expect(result!.outputFilePaths).toEqual(['dist/._', 'dist/other.txt']); + }); + + it('excludes AppleDouble files in nested directories', async () => { + Object.defineProperty(process, 'platform', { value: 'darwin' }); + + const subject: OperationBuildCache = prepareSubject({ excludeAppleDoubleFiles: true }); + + // First call returns the top-level dist/ contents with a subdirectory + // Second call returns the subdirectory contents + jest + .spyOn(FileSystem, 'readFolderItemsAsync') + .mockResolvedValueOnce([ + createFolderItem('index.js', 'file'), + createFolderItem('._index.js', 'file'), + createFolderItem('sub', 'directory') + ]) + .mockResolvedValueOnce([ + createFolderItem('nested.js', 'file'), + createFolderItem('._nested.js', 'file') + ]); + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(true); + const terminal: Terminal = new Terminal(terminalProvider); + + const result: { outputFilePaths: string[]; filteredOutputFolderNames: string[] } | undefined = + await subject['_tryCollectPathsToCacheAsync'](terminal); + + expect(result).toBeDefined(); + expect(result!.outputFilePaths).toEqual(['dist/index.js', 'dist/sub/nested.js']); + expect(result!.outputFilePaths).not.toContain('dist/._index.js'); + expect(result!.outputFilePaths).not.toContain('dist/sub/._nested.js'); + }); + }); +}); diff --git a/libraries/rush-lib/src/logic/buildCache/test/ProjectBuildCache.test.ts b/libraries/rush-lib/src/logic/buildCache/test/ProjectBuildCache.test.ts deleted file mode 100644 index c690607a45a..00000000000 --- a/libraries/rush-lib/src/logic/buildCache/test/ProjectBuildCache.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { StringBufferTerminalProvider, Terminal } from '@rushstack/node-core-library'; -import { BuildCacheConfiguration } from '../../../api/BuildCacheConfiguration'; -import { RushProjectConfiguration } from '../../../api/RushProjectConfiguration'; -import { ProjectChangeAnalyzer } from '../../ProjectChangeAnalyzer'; -import { IGenerateCacheEntryIdOptions } from '../CacheEntryId'; -import { FileSystemBuildCacheProvider } from '../FileSystemBuildCacheProvider'; - -import { ProjectBuildCache } from '../ProjectBuildCache'; - -interface ITestOptions { - enabled: boolean; - writeAllowed: boolean; - trackedProjectFiles: string[] | undefined; -} - -describe(ProjectBuildCache.name, () => { - async function prepareSubject(options: Partial): Promise { - const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); - const projectChangeAnalyzer = { - [ProjectChangeAnalyzer.prototype._tryGetProjectStateHashAsync.name]: async () => { - return 'state_hash'; - } - } as unknown as ProjectChangeAnalyzer; - - const subject: ProjectBuildCache | undefined = await ProjectBuildCache.tryGetProjectBuildCache({ - buildCacheConfiguration: { - buildCacheEnabled: options.hasOwnProperty('enabled') ? options.enabled : true, - getCacheEntryId: (options: IGenerateCacheEntryIdOptions) => - `${options.projectName}/${options.projectStateHash}`, - localCacheProvider: undefined as unknown as FileSystemBuildCacheProvider, - cloudCacheProvider: { - isCacheWriteAllowed: options.hasOwnProperty('writeAllowed') ? options.writeAllowed : false - } - } as unknown as BuildCacheConfiguration, - projectOutputFolderNames: ['dist'], - projectConfiguration: { - project: { - packageName: 'acme-wizard', - projectRelativeFolder: 'apps/acme-wizard', - dependencyProjects: [] - } - } as unknown as RushProjectConfiguration, - command: 'build', - trackedProjectFiles: options.hasOwnProperty('trackedProjectFiles') ? options.trackedProjectFiles : [], - projectChangeAnalyzer, - terminal, - phaseName: 'build' - }); - - return subject; - } - - describe(ProjectBuildCache.tryGetProjectBuildCache.name, () => { - it('returns a ProjectBuildCache with a calculated cacheId value', async () => { - const subject: ProjectBuildCache = (await prepareSubject({}))!; - expect(subject['_cacheId']).toMatchInlineSnapshot( - `"acme-wizard/1926f30e8ed24cb47be89aea39e7efd70fcda075"` - ); - }); - - it('returns undefined if the tracked file list is undefined', async () => { - expect( - await prepareSubject({ - trackedProjectFiles: undefined - }) - ).toBe(undefined); - }); - }); -}); diff --git a/libraries/rush-lib/src/logic/buildCache/test/__snapshots__/CacheEntryId.test.ts.snap b/libraries/rush-lib/src/logic/buildCache/test/__snapshots__/CacheEntryId.test.ts.snap index 5f6ea981ee6..5944f490b2a 100644 --- a/libraries/rush-lib/src/logic/buildCache/test/__snapshots__/CacheEntryId.test.ts.snap +++ b/libraries/rush-lib/src/logic/buildCache/test/__snapshots__/CacheEntryId.test.ts.snap @@ -1,87 +1,95 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 1`] = `"Cache entry name pattern is missing a [hash] token."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern (/[hash]) 1`] = `"Cache entry name patterns may not start with a slash."`; -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 2`] = `"Unexpected token name \\"invalidTag\\"."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern ([:attr1]) 1`] = `"Unexpected token name \\"\\"."`; -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 3`] = `"Unexpected \\"]\\" character in cache entry name pattern."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern ([arch:attr]) 1`] = `"An attribute isn't supported for the \\"arch\\" token."`; -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 4`] = `"Unclosed token in cache entry name pattern."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern ([hash:badAttribute:attr2]) 1`] = `"An attribute isn't supported for the \\"hash\\" token."`; -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 5`] = `"An attribute isn't supported for the \\"hash\\" token."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern ([hash:badAttribute]) 1`] = `"An attribute isn't supported for the \\"hash\\" token."`; -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 6`] = `"An attribute isn't supported for the \\"hash\\" token."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern ([incompleteTag) 1`] = `"Unclosed token in cache entry name pattern."`; -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 7`] = `"Unexpected attribute \\"badAttribute\\" for the \\"projectName\\" token."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern ([invalidTag]) 1`] = `"Unexpected token name \\"invalidTag\\"."`; -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 8`] = `"Unexpected attribute \\"\\" for the \\"projectName\\" token."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern ([os:attr]) 1`] = `"An attribute isn't supported for the \\"os\\" token."`; -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 9`] = `"Either the \\"normalize\\" or the \\"trimPrefix\\" attribute is required for the \\"phaseName\\" token."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern ([phaseName:]) 1`] = `"Unexpected attribute \\"\\" for the \\"phaseName\\" token."`; -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 10`] = `"Unexpected attribute \\"\\" for the \\"phaseName\\" token."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern ([phaseName:badAttribute]) 1`] = `"Unexpected attribute \\"badAttribute\\" for the \\"phaseName\\" token."`; -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 11`] = `"Unexpected attribute \\"badAttribute\\" for the \\"phaseName\\" token."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern ([phaseName]) 1`] = `"Either the \\"normalize\\" or the \\"trimPrefix\\" attribute is required for the \\"phaseName\\" token."`; -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 12`] = `"Unexpected token name \\"\\"."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern ([projectName:]) 1`] = `"Unexpected attribute \\"\\" for the \\"projectName\\" token."`; -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 13`] = `"Unexpected attribute \\"attr1:attr2\\" for the \\"projectName\\" token."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern ([projectName:attr1:attr2]) 1`] = `"Unexpected attribute \\"attr1:attr2\\" for the \\"projectName\\" token."`; -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 14`] = `"Cache entry name patterns may not start with a slash."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern ([projectName:badAttribute]) 1`] = `"Unexpected attribute \\"badAttribute\\" for the \\"projectName\\" token."`; -exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern 15`] = `"Cache entry name pattern contains an invalid character. Only alphanumeric characters, slashes, underscores, and hyphens are allowed."`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern (~) 1`] = `"Cache entry name pattern contains an invalid character. Only alphanumeric characters, slashes, underscores, and hyphens are allowed."`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name with a scope: [hash] 1`] = `"09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern (unstartedTag]) 1`] = `"Unexpected \\"]\\" character in cache entry name pattern."`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name with a scope: [phaseName:normalize]_[hash] 1`] = `"_phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Invalid pattern names Throws an exception for an invalid pattern (x) 1`] = `"Cache entry name pattern is missing a [hash] token."`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name with a scope: [phaseName:trimPrefix]_[hash] 1`] = `"compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern [hash] 1`] = `"09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name with a scope: [projectName:normalize]_[hash] 1`] = `"scope+project++name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern [phaseName:normalize]_[hash] 1`] = `"_phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name with a scope: [projectName:normalize]_[phaseName:normalize]_[hash] 1`] = `"scope+project++name__phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern [phaseName:trimPrefix]_[hash] 1`] = `"compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name with a scope: [projectName:normalize]_[phaseName:trimPrefix]_[hash] 1`] = `"scope+project++name_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern [projectName:normalize]_[hash] 1`] = `"scope+project++name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name with a scope: [projectName]_[hash] 1`] = `"@scope/project+name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern [projectName:normalize]_[phaseName:normalize]_[hash] 1`] = `"scope+project++name__phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name with a scope: no pattern 1`] = `"09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern [projectName:normalize]_[phaseName:normalize]_[hash]_[os]_[arch] 1`] = `"scope+project++name__phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3_dummyplatform_dummyarch"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name with a scope: prefix/[projectName:normalize]_[hash] 1`] = `"prefix/scope+project++name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern [projectName:normalize]_[phaseName:trimPrefix]_[hash] 1`] = `"scope+project++name_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name with a scope: prefix/[projectName:normalize]_[phaseName:normalize]_[hash] 1`] = `"prefix/scope+project++name__phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern [projectName]_[hash] 1`] = `"@scope/project+name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name with a scope: prefix/[projectName:normalize]_[phaseName:trimPrefix]_[hash] 1`] = `"prefix/scope+project++name_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern prefix/[projectName:normalize]_[hash] 1`] = `"prefix/scope+project++name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name with a scope: prefix/[projectName]_[hash] 1`] = `"prefix/@scope/project+name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern prefix/[projectName:normalize]_[phaseName:normalize]_[hash] 1`] = `"prefix/scope+project++name__phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name with a scope: prefix/[projectName]_[phaseName:normalize]_[hash] 1`] = `"prefix/@scope/project+name__phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern prefix/[projectName:normalize]_[phaseName:trimPrefix]_[hash] 1`] = `"prefix/scope+project++name_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name with a scope: prefix/[projectName]_[phaseName:trimPrefix]_[hash] 1`] = `"prefix/@scope/project+name_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern prefix/[projectName]_[hash] 1`] = `"prefix/@scope/project+name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name without a scope: [hash] 1`] = `"09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern prefix/[projectName]_[phaseName:normalize]_[hash] 1`] = `"prefix/@scope/project+name__phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name without a scope: [phaseName:normalize]_[hash] 1`] = `"_phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern prefix/[projectName]_[phaseName:trimPrefix]_[hash] 1`] = `"prefix/@scope/project+name_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name without a scope: [phaseName:trimPrefix]_[hash] 1`] = `"compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name with a scope Handles pattern undefined 1`] = `"09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name without a scope: [projectName:normalize]_[hash] 1`] = `"project++name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern [hash] 1`] = `"09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name without a scope: [projectName:normalize]_[phaseName:normalize]_[hash] 1`] = `"project++name__phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern [phaseName:normalize]_[hash] 1`] = `"_phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name without a scope: [projectName:normalize]_[phaseName:trimPrefix]_[hash] 1`] = `"project++name_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern [phaseName:trimPrefix]_[hash] 1`] = `"compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name without a scope: [projectName]_[hash] 1`] = `"project+name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern [projectName:normalize]_[hash] 1`] = `"project++name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name without a scope: no pattern 1`] = `"09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern [projectName:normalize]_[phaseName:normalize]_[hash] 1`] = `"project++name__phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name without a scope: prefix/[projectName:normalize]_[hash] 1`] = `"prefix/project++name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern [projectName:normalize]_[phaseName:normalize]_[hash]_[os]_[arch] 1`] = `"project++name__phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3_dummyplatform_dummyarch"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name without a scope: prefix/[projectName:normalize]_[phaseName:normalize]_[hash] 1`] = `"prefix/project++name__phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern [projectName:normalize]_[phaseName:trimPrefix]_[hash] 1`] = `"project++name_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name without a scope: prefix/[projectName:normalize]_[phaseName:trimPrefix]_[hash] 1`] = `"prefix/project++name_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern [projectName]_[hash] 1`] = `"project+name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name without a scope: prefix/[projectName]_[hash] 1`] = `"prefix/project+name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern prefix/[projectName:normalize]_[hash] 1`] = `"prefix/project++name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name without a scope: prefix/[projectName]_[phaseName:normalize]_[hash] 1`] = `"prefix/project+name__phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern prefix/[projectName:normalize]_[phaseName:normalize]_[hash] 1`] = `"prefix/project++name__phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; -exports[`CacheEntryId Valid pattern names Handles a cache entry name for a project name without a scope: prefix/[projectName]_[phaseName:trimPrefix]_[hash] 1`] = `"prefix/project+name_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern prefix/[projectName:normalize]_[phaseName:trimPrefix]_[hash] 1`] = `"prefix/project++name_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; + +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern prefix/[projectName]_[hash] 1`] = `"prefix/project+name_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; + +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern prefix/[projectName]_[phaseName:normalize]_[hash] 1`] = `"prefix/project+name__phase_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; + +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern prefix/[projectName]_[phaseName:trimPrefix]_[hash] 1`] = `"prefix/project+name_compile_09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; + +exports[`CacheEntryId Valid pattern names For a project name without a scope Handles pattern undefined 1`] = `"09d1ecee6d5f888fa6c35ca804b5dac7c3735ce3"`; diff --git a/libraries/rush-lib/src/logic/cobuild/CobuildLock.ts b/libraries/rush-lib/src/logic/cobuild/CobuildLock.ts new file mode 100644 index 00000000000..9dbf7114c3d --- /dev/null +++ b/libraries/rush-lib/src/logic/cobuild/CobuildLock.ts @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { InternalError } from '@rushstack/node-core-library'; + +import type { CobuildConfiguration } from '../../api/CobuildConfiguration'; +import type { OperationStatus } from '../operations/OperationStatus'; +import type { ICobuildContext } from './ICobuildLockProvider'; +import type { OperationBuildCache } from '../buildCache/OperationBuildCache'; + +const KEY_SEPARATOR: ':' = ':'; + +export interface ICobuildLockOptions { + /** + * {@inheritdoc CobuildConfiguration} + */ + cobuildConfiguration: CobuildConfiguration; + /** + * {@inheritdoc ICobuildContext.clusterId} + */ + cobuildClusterId: string; + /** + * {@inheritdoc ICobuildContext.packageName} + */ + packageName: string; + /** + * {@inheritdoc ICobuildContext.phaseName} + */ + phaseName: string; + operationBuildCache: OperationBuildCache; + /** + * The expire time of the lock in seconds. + */ + lockExpireTimeInSeconds: number; +} + +export interface ICobuildCompletedState { + status: OperationStatus.Success | OperationStatus.SuccessWithWarning | OperationStatus.Failure; + cacheId: string; +} + +export class CobuildLock { + public readonly cobuildConfiguration: CobuildConfiguration; + public readonly operationBuildCache: OperationBuildCache; + + private _cobuildContext: ICobuildContext; + + public constructor(options: ICobuildLockOptions) { + const { + cobuildConfiguration, + operationBuildCache, + cobuildClusterId: clusterId, + lockExpireTimeInSeconds, + packageName, + phaseName + } = options; + const { cobuildContextId: contextId, cobuildRunnerId: runnerId } = cobuildConfiguration; + const { cacheId } = operationBuildCache; + this.cobuildConfiguration = cobuildConfiguration; + this.operationBuildCache = operationBuildCache; + + if (!cacheId) { + // This should never happen + throw new InternalError(`Cache id is require for cobuild lock`); + } + + if (!contextId) { + // This should never happen + throw new InternalError(`Cobuild context id is require for cobuild lock`); + } + + // Example: cobuild:lock:: + const lockKey: string = ['cobuild', 'lock', contextId, clusterId].join(KEY_SEPARATOR); + + // Example: cobuild:completed:: + const completedStateKey: string = ['cobuild', 'completed', contextId, cacheId].join(KEY_SEPARATOR); + + this._cobuildContext = { + contextId, + clusterId, + runnerId, + lockKey, + completedStateKey, + packageName, + phaseName, + lockExpireTimeInSeconds: lockExpireTimeInSeconds, + cacheId + }; + } + + public async setCompletedStateAsync(state: ICobuildCompletedState): Promise { + await this.cobuildConfiguration + .getCobuildLockProvider() + .setCompletedStateAsync(this._cobuildContext, state); + } + + public async getCompletedStateAsync(): Promise { + const state: ICobuildCompletedState | undefined = await this.cobuildConfiguration + .getCobuildLockProvider() + .getCompletedStateAsync(this._cobuildContext); + return state; + } + + public async tryAcquireLockAsync(): Promise { + const acquireLockResult: boolean = await this.cobuildConfiguration + .getCobuildLockProvider() + .acquireLockAsync(this._cobuildContext); + if (acquireLockResult) { + // renew the lock in a redundant way in case of losing the lock + await this.renewLockAsync(); + } + return acquireLockResult; + } + + public async renewLockAsync(): Promise { + await this.cobuildConfiguration.getCobuildLockProvider().renewLockAsync(this._cobuildContext); + } + + public get cobuildContext(): ICobuildContext { + return this._cobuildContext; + } +} diff --git a/libraries/rush-lib/src/logic/cobuild/DisjointSet.ts b/libraries/rush-lib/src/logic/cobuild/DisjointSet.ts new file mode 100644 index 00000000000..3a33aef59ae --- /dev/null +++ b/libraries/rush-lib/src/logic/cobuild/DisjointSet.ts @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { InternalError } from '@rushstack/node-core-library'; + +/** + * A disjoint set data structure + */ +export class DisjointSet { + private _forest: Set; + private _parentMap: Map; + private _sizeMap: Map; + private _setByElement: Map> | undefined; + + public constructor() { + this._forest = new Set(); + this._parentMap = new Map(); + this._sizeMap = new Map(); + this._setByElement = new Map>(); + } + + public destroy(): void { + this._forest.clear(); + this._parentMap.clear(); + this._sizeMap.clear(); + this._setByElement?.clear(); + } + + /** + * Adds a new set containing specific object + */ + public add(x: T): void { + if (this._forest.has(x)) { + return; + } + + this._forest.add(x); + this._parentMap.set(x, x); + this._sizeMap.set(x, 1); + this._setByElement = undefined; + } + + /** + * Unions the sets that contain two objects + */ + public union(a: T, b: T): void { + let x: T = this._find(a); + let y: T = this._find(b); + + if (x === y) { + // x and y are already in the same set + return; + } + + const xSize: number = this._getSize(x); + const ySize: number = this._getSize(y); + if (xSize < ySize) { + const t: T = x; + x = y; + y = t; + } + this._parentMap.set(y, x); + this._sizeMap.set(x, xSize + ySize); + this._setByElement = undefined; + } + + public getAllSets(): Iterable> { + if (this._setByElement === undefined) { + this._setByElement = new Map>(); + + for (const element of this._forest) { + const root: T = this._find(element); + let set: Set | undefined = this._setByElement.get(root); + if (set === undefined) { + set = new Set(); + this._setByElement.set(root, set); + } + set.add(element); + } + } + return this._setByElement.values(); + } + + /** + * Returns true if x and y are in the same set + */ + public isConnected(x: T, y: T): boolean { + return this._find(x) === this._find(y); + } + + private _find(a: T): T { + let x: T = a; + let parent: T = this._getParent(x); + while (parent !== x) { + parent = this._getParent(parent); + this._parentMap.set(x, parent); + x = parent; + parent = this._getParent(x); + } + return x; + } + + private _getParent(x: T): T { + const parent: T | undefined = this._parentMap.get(x); + if (parent === undefined) { + // This should not happen + throw new InternalError(`Can not find parent`); + } + return parent; + } + + private _getSize(x: T): number { + const size: number | undefined = this._sizeMap.get(x); + if (size === undefined) { + // This should not happen + throw new InternalError(`Can not get size`); + } + return size; + } +} diff --git a/libraries/rush-lib/src/logic/cobuild/ICobuildLockProvider.ts b/libraries/rush-lib/src/logic/cobuild/ICobuildLockProvider.ts new file mode 100644 index 00000000000..027d8556a0c --- /dev/null +++ b/libraries/rush-lib/src/logic/cobuild/ICobuildLockProvider.ts @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { OperationStatus } from '../operations/OperationStatus'; + +/** + * @beta + */ +export interface ICobuildContext { + /** + * The key for acquiring lock. + */ + lockKey: string; + /** + * The expire time of the lock in seconds. + */ + lockExpireTimeInSeconds: number; + /** + * The key for storing completed state. + */ + completedStateKey: string; + /** + * The contextId is provided by the monorepo maintainer, it reads from environment variable {@link EnvironmentVariableNames.RUSH_COBUILD_CONTEXT_ID}. + * It ensure only the builds from the same given contextId cooperated. + */ + contextId: string; + /** + * The id of the cluster. The operations in the same cluster share the same clusterId and + * will be executed on the same machine. + */ + clusterId: string; + /** + * The id of the runner. The identifier for the running machine. + * + * It can be specified via assigning `RUSH_COBUILD_RUNNER_ID` environment variable. + */ + runnerId: string; + /** + * The id of the cache entry. It should be kept the same as the normal cacheId from ProjectBuildCache. + * Otherwise, there is a discrepancy in the success case wherein turning on cobuilds will + * fail to populate the normal build cache. + */ + cacheId: string; + /** + * The name of NPM package + * + * Example: `@scope/MyProject` + */ + packageName: string; + /** + * The name of the phase. + * + * Example: _phase:build + */ + phaseName: string; +} + +/** + * @beta + */ +export interface ICobuildCompletedState { + status: OperationStatus.Success | OperationStatus.SuccessWithWarning | OperationStatus.Failure; + /** + * Completed state points to the cache id that was used to store the build cache. + * Note: Cache failed builds in a separate cache id + */ + cacheId: string; +} + +/** + * @beta + */ +export interface ICobuildLockProvider { + /** + * The callback function invoked to connect to the lock provider. + * For example, initializing the connection to the redis server. + */ + connectAsync(): Promise; + /** + * The callback function invoked to disconnect the lock provider. + */ + disconnectAsync(): Promise; + /** + * The callback function to acquire a lock with a lock key and specific contexts. + * + * NOTE: This lock implementation must be a ReentrantLock. It says the lock might be acquired + * multiple times, since tasks in the same cluster can be run in the same VM. + */ + acquireLockAsync(context: Readonly): Promise; + /** + * The callback function to renew a lock with a lock key and specific contexts. + * + * NOTE: If the lock key expired + */ + renewLockAsync(context: Readonly): Promise; + /** + * The callback function to set completed state. + */ + setCompletedStateAsync(context: Readonly, state: ICobuildCompletedState): Promise; + /** + * The callback function to get completed state. + */ + getCompletedStateAsync(context: Readonly): Promise; +} diff --git a/libraries/rush-lib/src/logic/cobuild/test/CobuildLock.test.ts b/libraries/rush-lib/src/logic/cobuild/test/CobuildLock.test.ts new file mode 100644 index 00000000000..bde478c4919 --- /dev/null +++ b/libraries/rush-lib/src/logic/cobuild/test/CobuildLock.test.ts @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { CobuildLock, type ICobuildLockOptions } from '../CobuildLock'; + +import type { CobuildConfiguration } from '../../../api/CobuildConfiguration'; +import type { OperationBuildCache } from '../../buildCache/OperationBuildCache'; +import type { ICobuildContext } from '../ICobuildLockProvider'; + +describe(CobuildLock.name, () => { + function prepareSubject(): CobuildLock { + const cobuildLockOptions: ICobuildLockOptions = { + cobuildConfiguration: { + cobuildContextId: 'context_id', + cobuildRunnerId: 'runner_id' + } as unknown as CobuildConfiguration, + operationBuildCache: { + cacheId: 'cache_id' + } as unknown as OperationBuildCache, + cobuildClusterId: 'cluster_id', + lockExpireTimeInSeconds: 30, + packageName: 'package_name', + phaseName: 'phase_name' + }; + const subject: CobuildLock = new CobuildLock(cobuildLockOptions); + return subject; + } + it('returns cobuild context', () => { + const subject: CobuildLock = prepareSubject(); + const expected: ICobuildContext = { + lockKey: 'cobuild:lock:context_id:cluster_id', + completedStateKey: 'cobuild:completed:context_id:cache_id', + lockExpireTimeInSeconds: 30, + contextId: 'context_id', + cacheId: 'cache_id', + clusterId: 'cluster_id', + runnerId: 'runner_id', + packageName: 'package_name', + phaseName: 'phase_name' + }; + expect(subject.cobuildContext).toEqual(expected); + }); +}); diff --git a/libraries/rush-lib/src/logic/cobuild/test/DisjointSet.test.ts b/libraries/rush-lib/src/logic/cobuild/test/DisjointSet.test.ts new file mode 100644 index 00000000000..56bb80695d5 --- /dev/null +++ b/libraries/rush-lib/src/logic/cobuild/test/DisjointSet.test.ts @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { DisjointSet } from '../DisjointSet'; + +describe(DisjointSet.name, () => { + it('can disjoint two sets', () => { + const disjointSet = new DisjointSet<{ id: number }>(); + const obj1 = { id: 1 }; + const obj2 = { id: 2 }; + disjointSet.add(obj1); + disjointSet.add(obj2); + + expect(disjointSet.isConnected(obj1, obj2)).toBe(false); + }); + + it('can disjoint multiple sets', () => { + const disjointSet = new DisjointSet<{ id: number }>(); + const obj1 = { id: 1 }; + const obj2 = { id: 2 }; + const obj3 = { id: 3 }; + const obj4 = { id: 4 }; + disjointSet.add(obj1); + disjointSet.add(obj2); + disjointSet.add(obj3); + disjointSet.add(obj4); + + expect(disjointSet.isConnected(obj1, obj2)).toBe(false); + expect(disjointSet.isConnected(obj1, obj3)).toBe(false); + expect(disjointSet.isConnected(obj1, obj4)).toBe(false); + }); + + it('can union two sets', () => { + const disjointSet = new DisjointSet<{ id: number }>(); + const obj1 = { id: 1 }; + const obj2 = { id: 2 }; + disjointSet.add(obj1); + disjointSet.add(obj2); + expect(disjointSet.isConnected(obj1, obj2)).toBe(false); + + disjointSet.union(obj1, obj2); + expect(disjointSet.isConnected(obj1, obj2)).toBe(true); + }); + + it('can union two sets transitively', () => { + const disjointSet = new DisjointSet<{ id: number }>(); + const obj1 = { id: 1 }; + const obj2 = { id: 2 }; + const obj3 = { id: 3 }; + disjointSet.add(obj1); + disjointSet.add(obj2); + disjointSet.add(obj3); + + disjointSet.union(obj1, obj2); + expect(disjointSet.isConnected(obj1, obj2)).toBe(true); + expect(disjointSet.isConnected(obj1, obj3)).toBe(false); + expect(disjointSet.isConnected(obj2, obj3)).toBe(false); + + disjointSet.union(obj1, obj3); + expect(disjointSet.isConnected(obj1, obj2)).toBe(true); + expect(disjointSet.isConnected(obj2, obj3)).toBe(true); + expect(disjointSet.isConnected(obj1, obj3)).toBe(true); + }); + + it('can union and disjoint sets', () => { + const disjointSet = new DisjointSet<{ id: number }>(); + const obj1 = { id: 1 }; + const obj2 = { id: 2 }; + const obj3 = { id: 3 }; + const obj4 = { id: 4 }; + disjointSet.add(obj1); + disjointSet.add(obj2); + disjointSet.add(obj3); + disjointSet.add(obj4); + + expect(disjointSet.isConnected(obj1, obj2)).toBe(false); + expect(disjointSet.isConnected(obj1, obj3)).toBe(false); + expect(disjointSet.isConnected(obj1, obj4)).toBe(false); + + disjointSet.union(obj1, obj2); + expect(disjointSet.isConnected(obj1, obj2)).toBe(true); + expect(disjointSet.isConnected(obj1, obj3)).toBe(false); + expect(disjointSet.isConnected(obj1, obj4)).toBe(false); + }); + + it('can get all sets', () => { + const disjointSet = new DisjointSet<{ id: number }>(); + const obj1 = { id: 1 }; + const obj2 = { id: 2 }; + const obj3 = { id: 3 }; + disjointSet.add(obj1); + disjointSet.add(obj2); + disjointSet.add(obj3); + + disjointSet.union(obj1, obj2); + + const allSets: Iterable> = disjointSet.getAllSets(); + + const allSetList: Array> = []; + for (const set of allSets) { + allSetList.push(set); + } + + expect(allSetList.length).toBe(2); + expect(Array.from(allSetList[0]).map((x) => x.id)).toEqual(expect.arrayContaining([1, 2])); + expect(Array.from(allSetList[1]).map((x) => x.id)).toEqual(expect.arrayContaining([3])); + }); +}); diff --git a/libraries/rush-lib/src/logic/deploy/DeployScenarioConfiguration.ts b/libraries/rush-lib/src/logic/deploy/DeployScenarioConfiguration.ts index 975eff935ef..f198eaad8ba 100644 --- a/libraries/rush-lib/src/logic/deploy/DeployScenarioConfiguration.ts +++ b/libraries/rush-lib/src/logic/deploy/DeployScenarioConfiguration.ts @@ -1,11 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { FileSystem, JsonFile, JsonSchema, Colors, type ITerminal } from '@rushstack/node-core-library'; +import * as path from 'node:path'; + +import { FileSystem, JsonFile, JsonSchema } from '@rushstack/node-core-library'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; import type { RushConfiguration } from '../../api/RushConfiguration'; import schemaJson from '../../schemas/deploy-scenario.schema.json'; +import { RushConstants } from '../RushConstants'; // Describes IDeployScenarioJson.projectSettings export interface IDeployScenarioProjectJson { @@ -13,6 +16,15 @@ export interface IDeployScenarioProjectJson { additionalProjectsToInclude?: string[]; additionalDependenciesToInclude?: string[]; dependenciesToExclude?: string[]; + patternsToInclude?: string[]; + patternsToExclude?: string[]; +} + +export interface IDeployScenarioDependencyJson { + dependencyName: string; + dependencyVersionRange: string; + patternsToExclude?: string[]; + patternsToInclude?: string[]; } // The parsed JSON file structure, as defined by the "deploy-scenario.schema.json" JSON schema @@ -24,16 +36,17 @@ export interface IDeployScenarioJson { linkCreation?: 'default' | 'script' | 'none'; folderToCopy?: string; projectSettings?: IDeployScenarioProjectJson[]; + dependencySettings?: IDeployScenarioDependencyJson[]; } -export class DeployScenarioConfiguration { - // Used by validateScenarioName() - // Matches lowercase words separated by dashes. - // Example: "deploy-the-thing123" - private static _scenarioNameRegExp: RegExp = /^[a-z0-9]+(-[a-z0-9]+)*$/; +// Used by validateScenarioName() +// Matches lowercase words separated by dashes. +// Example: "deploy-the-thing123" +const _scenarioNameRegExp: RegExp = /^[a-z0-9]+(-[a-z0-9]+)*$/; - private static _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); +const _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); +export class DeployScenarioConfiguration { public readonly json: IDeployScenarioJson; /** @@ -56,7 +69,7 @@ export class DeployScenarioConfiguration { if (!scenarioName) { throw new Error('The scenario name cannot be an empty string'); } - if (!this._scenarioNameRegExp.test(scenarioName)) { + if (!_scenarioNameRegExp.test(scenarioName)) { throw new Error( `"${scenarioName}" is not a valid scenario name. The name must be comprised of` + ' lowercase letters and numbers, separated by single hyphens. Example: "my-scenario"' @@ -94,12 +107,9 @@ export class DeployScenarioConfiguration { throw new Error('The scenario config file was not found: ' + scenarioFilePath); } - terminal.writeLine(Colors.cyan(`Loading deployment scenario: ${scenarioFilePath}`)); + terminal.writeLine(Colorize.cyan(`Loading deployment scenario: ${scenarioFilePath}`)); - const deployScenarioJson: IDeployScenarioJson = JsonFile.loadAndValidate( - scenarioFilePath, - DeployScenarioConfiguration._jsonSchema - ); + const deployScenarioJson: IDeployScenarioJson = JsonFile.loadAndValidate(scenarioFilePath, _jsonSchema); // Apply the defaults if (!deployScenarioJson.linkCreation) { @@ -113,14 +123,14 @@ export class DeployScenarioConfiguration { if (!rushConfiguration.getProjectByName(projectSetting.projectName)) { throw new Error( `The "projectSettings" section refers to the project name "${projectSetting.projectName}"` + - ` which was not found in rush.json` + ` which was not found in ${RushConstants.rushJsonFilename}` ); } for (const additionalProjectsToInclude of projectSetting.additionalProjectsToInclude || []) { if (!rushConfiguration.getProjectByName(projectSetting.projectName)) { throw new Error( `The "additionalProjectsToInclude" setting refers to the` + - ` project name "${additionalProjectsToInclude}" which was not found in rush.json` + ` project name "${additionalProjectsToInclude}" which was not found in ${RushConstants.rushJsonFilename}` ); } } diff --git a/libraries/rush-lib/src/logic/dotenv.ts b/libraries/rush-lib/src/logic/dotenv.ts new file mode 100644 index 00000000000..7df044eecac --- /dev/null +++ b/libraries/rush-lib/src/logic/dotenv.ts @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; + +import dotenv from 'dotenv'; + +import type { ITerminal } from '@rushstack/terminal'; + +import { RushUserConfiguration } from '../api/RushUserConfiguration'; +import { EnvironmentConfiguration } from '../api/EnvironmentConfiguration'; + +export function initializeDotEnv(terminal: ITerminal, rushJsonFilePath: string | undefined): void { + if (EnvironmentConfiguration.hasBeenValidated) { + throw terminal.writeWarningLine( + `The ${EnvironmentConfiguration.name} was initialized before .env files were loaded. Rush environment ` + + 'variables may have unexpected values.' + ); + } + + if (rushJsonFilePath) { + const rushJsonFolder: string = path.dirname(rushJsonFilePath); + dotenv.config({ path: `${rushJsonFolder}/.env` }); + } + + const rushUserFolder: string = RushUserConfiguration.getRushUserFolderPath(); + dotenv.config({ path: `${rushUserFolder}/.env` }); + + // TODO: Consider adding support for repo-specific `.rush-user` `.env` files. +} diff --git a/libraries/rush-lib/src/logic/incremental/InputsSnapshot.ts b/libraries/rush-lib/src/logic/incremental/InputsSnapshot.ts new file mode 100644 index 00000000000..c0c61334f33 --- /dev/null +++ b/libraries/rush-lib/src/logic/incremental/InputsSnapshot.ts @@ -0,0 +1,510 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import { createHash, type Hash } from 'node:crypto'; + +import ignore, { type Ignore } from 'ignore'; + +import { type IReadonlyLookupByPath, LookupByPath } from '@rushstack/lookup-by-path'; +import { InternalError, Path, Sort } from '@rushstack/node-core-library'; + +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { + IOperationSettings, + NodeVersionGranularity, + RushProjectConfiguration +} from '../../api/RushProjectConfiguration'; +import { RushConstants } from '../RushConstants'; + +/** + * @beta + */ +export type IRushConfigurationProjectForSnapshot = Pick< + RushConfigurationProject, + 'projectFolder' | 'projectRelativeFolder' +>; + +/** + * @internal + */ +export interface IInputsSnapshotProjectMetadata { + /** + * The contents of rush-project.json for the project, if available + */ + projectConfig?: RushProjectConfiguration; + /** + * A map of operation name to additional files that should be included in the hash for that operation. + */ + additionalFilesByOperationName?: ReadonlyMap>; +} + +interface IInternalInputsSnapshotProjectMetadata extends IInputsSnapshotProjectMetadata { + /** + * Cached filter of files that are not ignored by the project's `incrementalBuildIgnoredGlobs`. + * @param filePath - The path to the file to check + * @returns true if the file path is an input to all operations in the project, false otherwise + */ + projectFilePathFilter?: (filePath: string) => boolean; + /** + * The cached Git hashes for all files in the project folder. + */ + hashes: Map; + /** + * Cached hashes for all files in the project folder, including additional files. + * Upon calculating this map, input-output file collisions are detected. + */ + fileHashesByOperationName: Map>; + /** + * The flattened state hash for each operation name, where the key "undefined" represents no particular operation. + */ + hashByOperationName: Map; + /** + * The project relative folder, which is a prefix in all relative paths. + */ + relativePrefix: string; +} + +export type IRushSnapshotProjectMetadataMap = ReadonlyMap< + IRushConfigurationProjectForSnapshot, + IInputsSnapshotProjectMetadata +>; + +/** + * Function that computes a new snapshot of the current state of the repository as of the current moment. + * Rush-level configuration state will have been bound during creation of the function. + * Captures the state of the environment, tracked files, and additional files. + * + * @beta + */ +export type GetInputsSnapshotAsyncFn = () => Promise; + +/** + * The parameters for constructing an {@link InputsSnapshot}. + * @internal + */ +export interface IInputsSnapshotParameters { + /** + * Hashes for files selected by `dependsOnAdditionalFiles`. + * Separated out to prevent being auto-assigned to a project. + */ + additionalHashes?: ReadonlyMap; + /** + * The environment to use for `dependsOnEnvVars`. By default performs a snapshot of process.env upon construction. + * @defaultValue \{ ...process.env \} + */ + environment?: Record; + /** + * The Node.js version string to use for `dependsOnNodeVersion`. Defaults to `process.version`. + * @defaultValue process.version + */ + nodeVersion?: string; + /** + * File paths (keys into additionalHashes or hashes) to be included as part of every operation's dependencies. + */ + globalAdditionalFiles?: Iterable; + /** + * The hashes of all tracked files in the repository. + */ + hashes: ReadonlyMap; + /** + * Whether or not the repository has uncommitted changes. + */ + hasUncommittedChanges: boolean; + /** + * Optimized lookup engine used to route `hashes` to individual projects. + */ + lookupByPath: IReadonlyLookupByPath; + /** + * Metadata for each project. + */ + projectMap: IRushSnapshotProjectMetadataMap; + /** + * The directory that all relative paths are relative to. + */ + rootDir: string; +} + +const { hashDelimiter } = RushConstants; + +/** + * Represents a synchronously-queryable in-memory snapshot of the state of the inputs to a Rush repository. + * + * The methods on this interface are idempotent and will return the same result regardless of when they are executed. + * @beta + */ +export interface IInputsSnapshot { + /** + * The raw hashes of all tracked files in the repository. + */ + readonly hashes: ReadonlyMap; + + /** + * The directory that all paths in `hashes` are relative to. + */ + readonly rootDirectory: string; + + /** + * Whether or not the repository has uncommitted changes. + */ + readonly hasUncommittedChanges: boolean; + + /** + * Gets the map of file paths to Git hashes that will be used to compute the local state hash of the operation. + * Exposed separately from the final state hash to facilitate detailed change detection. + * + * @param project - The Rush project to get hashes for + * @param operationName - The name of the operation (phase) to get hashes for. If omitted, returns a default set for the project, as used for bulk commands. + * @returns A map of file name to Git hash. For local files paths will be relative. Configured additional files may be absolute paths. + */ + getTrackedFileHashesForOperation( + project: IRushConfigurationProjectForSnapshot, + operationName?: string + ): ReadonlyMap; + + /** + * Gets the state hash for the files owned by this operation, including the resolutions of package.json dependencies. This will later be combined with the hash of + * the command being executed and the final hashes of the operation's dependencies to compute the final hash for the operation. + * @param project - The Rush project to compute the state hash for + * @param operationName - The name of the operation (phase) to get hashes for. If omitted, returns a generic hash for the whole project, as used for bulk commands. + * @returns The local state hash for the project. This is a hash of the environment, the project's tracked files, and any additional files. + */ + getOperationOwnStateHash(project: IRushConfigurationProjectForSnapshot, operationName?: string): string; +} + +/** + * Represents a synchronously-queryable in-memory snapshot of the state of the inputs to a Rush repository. + * Any asynchronous work needs to be performed by the caller and the results passed to the constructor. + * + * @remarks + * All operations on this class will return the same result regardless of when they are executed. + * + * @internal + */ +export class InputsSnapshot implements IInputsSnapshot { + /** + * {@inheritdoc IInputsSnapshot.hashes} + */ + public readonly hashes: ReadonlyMap; + /** + * {@inheritdoc IInputsSnapshot.hasUncommittedChanges} + */ + public readonly hasUncommittedChanges: boolean; + /** + * {@inheritdoc IInputsSnapshot.rootDirectory} + */ + public readonly rootDirectory: string; + + /** + * The metadata for each project. This is a superset of the information in `projectMap` and includes caching of queries. + */ + private readonly _projectMetadataMap: Map< + IRushConfigurationProjectForSnapshot, + IInternalInputsSnapshotProjectMetadata + >; + /** + * Hashes of files to be included in all result sets. + */ + private readonly _globalAdditionalHashes: ReadonlyMap | undefined; + /** + * Hashes for files selected by `dependsOnAdditionalFiles`. + */ + private readonly _additionalHashes: ReadonlyMap | undefined; + /** + * The environment to use for `dependsOnEnvVars`. + */ + private readonly _environment: Record; + /** + * Pre-computed Node.js version strings at each granularity level for `dependsOnNodeVersion`. + */ + private readonly _nodeVersionByGranularity: Readonly>; + + /** + * + * @param params - The parameters for the snapshot + * @internal + */ + public constructor(params: IInputsSnapshotParameters) { + const { + additionalHashes, + environment = { ...process.env }, + globalAdditionalFiles, + hashes, + hasUncommittedChanges, + lookupByPath, + nodeVersion = process.version, + rootDir + } = params; + const projectMetadataMap: Map< + IRushConfigurationProjectForSnapshot, + IInternalInputsSnapshotProjectMetadata + > = new Map(); + for (const [project, record] of params.projectMap) { + projectMetadataMap.set(project, createInternalRecord(project, record, rootDir)); + } + + // Route hashes to individual projects + for (const [file, hash] of hashes) { + const project: IRushConfigurationProjectForSnapshot | undefined = lookupByPath.findChildPath(file); + if (!project) { + continue; + } + + let record: IInternalInputsSnapshotProjectMetadata | undefined = projectMetadataMap.get(project); + if (!record) { + projectMetadataMap.set(project, (record = createInternalRecord(project, undefined, rootDir))); + } + + record.hashes.set(file, hash); + } + + let globalAdditionalHashes: Map | undefined; + if (globalAdditionalFiles) { + globalAdditionalHashes = new Map(); + const sortedAdditionalFiles: string[] = Array.from(globalAdditionalFiles).sort(); + for (const file of sortedAdditionalFiles) { + const hash: string | undefined = hashes.get(file); + if (!hash) { + throw new Error(`Hash not found for global file: "${file}"`); + } + const owningProject: IRushConfigurationProjectForSnapshot | undefined = + lookupByPath.findChildPath(file); + if (owningProject) { + throw new InternalError( + `Requested global additional file "${file}" is owned by project in "${owningProject.projectRelativeFolder}". Declare a project dependency instead.` + ); + } + globalAdditionalHashes.set(file, hash); + } + } + + for (const record of projectMetadataMap.values()) { + // Ensure stable ordering. + Sort.sortMapKeys(record.hashes); + } + + this._projectMetadataMap = projectMetadataMap; + this._additionalHashes = additionalHashes; + this._globalAdditionalHashes = globalAdditionalHashes; + // Snapshot the environment so that queries are not impacted by when they happen + this._environment = environment; + // Parse Node.js version once so it doesn't need to be re-parsed per operation + this._nodeVersionByGranularity = _parseNodeVersion(nodeVersion); + this.hashes = hashes; + this.hasUncommittedChanges = hasUncommittedChanges; + this.rootDirectory = rootDir; + } + + /** + * {@inheritdoc} + */ + public getTrackedFileHashesForOperation( + project: IRushConfigurationProjectForSnapshot, + operationName?: string + ): ReadonlyMap { + const record: IInternalInputsSnapshotProjectMetadata | undefined = this._projectMetadataMap.get(project); + if (!record) { + throw new InternalError(`No information available for project at ${project.projectFolder}`); + } + + const { fileHashesByOperationName } = record; + let hashes: Map | undefined = fileHashesByOperationName.get(operationName); + if (!hashes) { + hashes = new Map(); + fileHashesByOperationName.set(operationName, hashes); + // TODO: Support incrementalBuildIgnoredGlobs per-operation + const filter: (filePath: string) => boolean = getOrCreateProjectFilter(record); + + let outputValidator: LookupByPath | undefined; + + if (operationName) { + const operationSettings: Readonly | undefined = + record.projectConfig?.operationSettingsByOperationName.get(operationName); + + const outputFolderNames: string[] | undefined = operationSettings?.outputFolderNames; + if (outputFolderNames) { + const { relativePrefix } = record; + outputValidator = new LookupByPath(); + for (const folderName of outputFolderNames) { + outputValidator.setItem(`${relativePrefix}/${folderName}`, folderName); + } + } + + // Hash any additional files (files outside of a project, untracked project files, or even files outside of the repository) + const additionalFilesForOperation: ReadonlySet | undefined = + record.additionalFilesByOperationName?.get(operationName); + if (additionalFilesForOperation) { + // Sort the additional files to ensure deterministic hash computation + const sortedAdditionalFiles: string[] = Array.from(additionalFilesForOperation).sort(); + for (const [filePath, hash] of this._resolveHashes(sortedAdditionalFiles)) { + hashes.set(filePath, hash); + } + } + } + + const { _globalAdditionalHashes: globalAdditionalHashes } = this; + if (globalAdditionalHashes) { + for (const [file, hash] of globalAdditionalHashes) { + record.hashes.set(file, hash); + } + } + + // Hash the base project files + for (const [filePath, hash] of record.hashes) { + if (filter(filePath)) { + hashes.set(filePath, hash); + } + + // Ensure that the configured output folders for this operation do not contain any input files + // This should be reworked to operate on a global file origin map to ensure a hashed input + // is not a declared output of *any* operation. + const outputMatch: string | undefined = outputValidator?.findChildPath(filePath); + if (outputMatch) { + throw new Error( + `Configured output folder "${outputMatch}" for operation "${operationName}" in project "${project.projectRelativeFolder}" contains tracked input file "${filePath}".` + + ` If it is intended that this operation modifies its own input files, modify the build process to emit a warning if the output version differs from the input, and remove the directory from "outputFolderNames".` + + ` This will ensure cache correctness. Otherwise, change the build process to output to a disjoint folder.` + ); + } + } + } + + return hashes; + } + + /** + * {@inheritdoc} + */ + public getOperationOwnStateHash( + project: IRushConfigurationProjectForSnapshot, + operationName?: string + ): string { + const record: IInternalInputsSnapshotProjectMetadata | undefined = this._projectMetadataMap.get(project); + if (!record) { + throw new Error(`No information available for project at ${project.projectFolder}`); + } + + const { hashByOperationName } = record; + let hash: string | undefined = hashByOperationName.get(operationName); + if (!hash) { + const hashes: ReadonlyMap = this.getTrackedFileHashesForOperation( + project, + operationName + ); + + const hasher: Hash = createHash('sha1'); + // If this is for a specific operation, apply operation-specific options + if (operationName) { + const operationSettings: Readonly | undefined = + record.projectConfig?.operationSettingsByOperationName.get(operationName); + if (operationSettings) { + const { dependsOnEnvVars, dependsOnNodeVersion, outputFolderNames } = operationSettings; + if (dependsOnEnvVars) { + // As long as we enumerate environment variables in a consistent order, we will get a stable hash. + // Changing the order in rush-project.json will change the hash anyway since the file contents are part of the hash. + for (const envVar of dependsOnEnvVars) { + hasher.update(`${hashDelimiter}$${envVar}=${this._environment[envVar] || ''}`); + } + } + + if (dependsOnNodeVersion) { + const granularity: NodeVersionGranularity = + dependsOnNodeVersion === true ? 'patch' : dependsOnNodeVersion; + hasher.update(`${hashDelimiter}nodeVersion=${this._nodeVersionByGranularity[granularity]}`); + } + + if (outputFolderNames) { + hasher.update(`${hashDelimiter}${JSON.stringify(outputFolderNames)}`); + } + } + } + + // Hash the base project files + for (const [filePath, fileHash] of hashes) { + hasher.update(`${hashDelimiter}${filePath}${hashDelimiter}${fileHash}`); + } + + hash = hasher.digest('hex'); + + hashByOperationName.set(operationName, hash); + } + + return hash; + } + + private *_resolveHashes(filePaths: Iterable): Generator<[string, string]> { + const { hashes, _additionalHashes } = this; + + for (const filePath of filePaths) { + const hash: string | undefined = hashes.get(filePath) ?? _additionalHashes?.get(filePath); + if (!hash) { + throw new Error(`Could not find hash for file path "${filePath}"`); + } + yield [filePath, hash]; + } + } +} + +/** + * Parses a Node.js version string once and returns pre-computed strings for each granularity level. + * + * @param rawVersion - The full Node.js version string (e.g. `v18.17.1`) + * @returns An object with pre-computed version strings for `major`, `minor`, and `patch` granularities + */ +function _parseNodeVersion(rawVersion: string): Record { + // Strip leading 'v' if present + const version: string = rawVersion.startsWith('v') ? rawVersion.slice(1) : rawVersion; + const [major, minor]: string[] = version.split('.'); + + return { + major, + minor: `${major}.${minor}`, + patch: version + }; +} + +function getOrCreateProjectFilter( + record: IInternalInputsSnapshotProjectMetadata +): (filePath: string) => boolean { + if (!record.projectFilePathFilter) { + const ignoredGlobs: readonly string[] | undefined = record.projectConfig?.incrementalBuildIgnoredGlobs; + if (!ignoredGlobs || ignoredGlobs.length === 0) { + record.projectFilePathFilter = noopFilter; + } else { + const ignorer: Ignore = ignore(); + ignorer.add(ignoredGlobs as string[]); + const prefixLength: number = record.relativePrefix.length + 1; + record.projectFilePathFilter = function projectFilePathFilter(filePath: string): boolean { + return !ignorer.ignores(filePath.slice(prefixLength)); + }; + } + } + + return record.projectFilePathFilter; +} + +function createInternalRecord( + project: IRushConfigurationProjectForSnapshot, + baseRecord: IInputsSnapshotProjectMetadata | undefined, + rootDir: string +): IInternalInputsSnapshotProjectMetadata { + return { + // Data from the caller + projectConfig: baseRecord?.projectConfig, + additionalFilesByOperationName: baseRecord?.additionalFilesByOperationName, + + // Caches + hashes: new Map(), + hashByOperationName: new Map(), + fileHashesByOperationName: new Map(), + relativePrefix: getRelativePrefix(project, rootDir) + }; +} + +function getRelativePrefix(project: IRushConfigurationProjectForSnapshot, rootDir: string): string { + return Path.convertToSlashes(path.relative(rootDir, project.projectFolder)); +} + +function noopFilter(filePath: string): boolean { + return true; +} diff --git a/libraries/rush-lib/src/logic/incremental/test/InputsSnapshot.test.ts b/libraries/rush-lib/src/logic/incremental/test/InputsSnapshot.test.ts new file mode 100644 index 00000000000..4f7e487fd1c --- /dev/null +++ b/libraries/rush-lib/src/logic/incremental/test/InputsSnapshot.test.ts @@ -0,0 +1,717 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { LookupByPath } from '@rushstack/lookup-by-path'; + +import type { RushProjectConfiguration } from '../../../api/RushProjectConfiguration'; +import { + InputsSnapshot, + type IInputsSnapshotParameters, + type IRushConfigurationProjectForSnapshot +} from '../InputsSnapshot'; + +describe(InputsSnapshot.name, () => { + function getTestConfig(): { + project: IRushConfigurationProjectForSnapshot; + options: IInputsSnapshotParameters; + } { + const project: IRushConfigurationProjectForSnapshot = { + projectFolder: '/root/a', + projectRelativeFolder: 'a' + }; + + return { + project, + options: { + rootDir: '/root', + additionalHashes: new Map([['/ext/config.json', 'hash4']]), + hashes: new Map([ + ['a/file1.js', 'hash1'], + ['a/file2.js', 'hash2'], + ['a/lib/file3.js', 'hash3'], + ['common/config/some-config.json', 'hash5'] + ]), + hasUncommittedChanges: false, + lookupByPath: new LookupByPath([[project.projectRelativeFolder, project]]), + projectMap: new Map() + } + }; + } + + function getTrivialSnapshot(): { + project: IRushConfigurationProjectForSnapshot; + input: InputsSnapshot; + } { + const { project, options } = getTestConfig(); + + const input: InputsSnapshot = new InputsSnapshot(options); + + return { project, input }; + } + + describe(InputsSnapshot.prototype.getTrackedFileHashesForOperation.name, () => { + it('Handles trivial input', () => { + const { project, input } = getTrivialSnapshot(); + + const result: ReadonlyMap = input.getTrackedFileHashesForOperation(project); + + expect(result).toMatchSnapshot(); + expect(result.size).toEqual(3); + expect(result.get('a/file1.js')).toEqual('hash1'); + expect(result.get('a/file2.js')).toEqual('hash2'); + expect(result.get('a/lib/file3.js')).toEqual('hash3'); + }); + + it('Detects outputFileNames collisions', () => { + const { project, options } = getTestConfig(); + + const projectConfig: Pick = { + operationSettingsByOperationName: new Map([ + [ + '_phase:build', + { + operationName: '_phase:build', + outputFolderNames: ['lib'] + } + ] + ]) + }; + + options.projectMap = new Map([ + [ + project, + { + projectConfig: projectConfig as RushProjectConfiguration + } + ] + ]); + + const input: InputsSnapshot = new InputsSnapshot(options); + + expect(() => + input.getTrackedFileHashesForOperation(project, '_phase:build') + ).toThrowErrorMatchingSnapshot(); + }); + + it('Respects additionalFilesByOperationName', () => { + const { project, options } = getTestConfig(); + + const projectConfig: Pick = { + operationSettingsByOperationName: new Map([ + [ + '_phase:build', + { + operationName: '_phase:build' + } + ] + ]) + }; + + const input: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([ + [ + project, + { + projectConfig: projectConfig as RushProjectConfiguration, + additionalFilesByOperationName: new Map([['_phase:build', new Set(['/ext/config.json'])]]) + } + ] + ]) + }); + + const result: ReadonlyMap = input.getTrackedFileHashesForOperation( + project, + '_phase:build' + ); + + expect(result).toMatchSnapshot(); + expect(result.size).toEqual(4); + expect(result.get('a/file1.js')).toEqual('hash1'); + expect(result.get('a/file2.js')).toEqual('hash2'); + expect(result.get('a/lib/file3.js')).toEqual('hash3'); + expect(result.get('/ext/config.json')).toEqual('hash4'); + }); + + it('Respects globalAdditionalFiles', () => { + const { project, options } = getTestConfig(); + + const projectConfig: Pick = { + operationSettingsByOperationName: new Map([ + [ + '_phase:build', + { + operationName: '_phase:build' + } + ] + ]) + }; + + const input: InputsSnapshot = new InputsSnapshot({ + ...options, + globalAdditionalFiles: new Set(['common/config/some-config.json']), + projectMap: new Map([ + [ + project, + { + projectConfig: projectConfig as RushProjectConfiguration + } + ] + ]) + }); + + const result: ReadonlyMap = input.getTrackedFileHashesForOperation( + project, + '_phase:build' + ); + + expect(result).toMatchSnapshot(); + expect(result.size).toEqual(4); + expect(result.get('a/file1.js')).toEqual('hash1'); + expect(result.get('a/file2.js')).toEqual('hash2'); + expect(result.get('a/lib/file3.js')).toEqual('hash3'); + expect(result.get('common/config/some-config.json')).toEqual('hash5'); + }); + + it('Respects incrementalBuildIgnoredGlobs', () => { + const { project, options } = getTestConfig(); + + const projectConfig: Pick = { + incrementalBuildIgnoredGlobs: ['*2.js'] + }; + + const input: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([ + [ + project, + { + projectConfig: projectConfig as RushProjectConfiguration + } + ] + ]) + }); + + const result: ReadonlyMap = input.getTrackedFileHashesForOperation(project); + + expect(result).toMatchSnapshot(); + expect(result.size).toEqual(2); + expect(result.get('a/file1.js')).toEqual('hash1'); + expect(result.get('a/lib/file3.js')).toEqual('hash3'); + }); + }); + + describe(InputsSnapshot.prototype.getOperationOwnStateHash.name, () => { + it('Handles trivial input', () => { + const { project, input } = getTrivialSnapshot(); + + const result: string = input.getOperationOwnStateHash(project); + + expect(result).toMatchSnapshot(); + }); + + it('Is invariant to input hash order', () => { + const { project, options } = getTestConfig(); + + const baseline: string = new InputsSnapshot(options).getOperationOwnStateHash(project); + + const input: InputsSnapshot = new InputsSnapshot({ + ...options, + hashes: new Map(Array.from(options.hashes).reverse()) + }); + + const result: string = input.getOperationOwnStateHash(project); + + expect(result).toEqual(baseline); + }); + + it('Detects outputFileNames collisions', () => { + const { project, options } = getTestConfig(); + + const projectConfig: Pick = { + operationSettingsByOperationName: new Map([ + [ + '_phase:build', + { + operationName: '_phase:build', + outputFolderNames: ['lib'] + } + ] + ]) + }; + + options.projectMap = new Map([ + [ + project, + { + projectConfig: projectConfig as RushProjectConfiguration + } + ] + ]); + + const input: InputsSnapshot = new InputsSnapshot(options); + + expect(() => input.getOperationOwnStateHash(project, '_phase:build')).toThrowErrorMatchingSnapshot(); + }); + + it('Changes if outputFileNames changes', () => { + const { project, options } = getTestConfig(); + const baseline: string = new InputsSnapshot(options).getOperationOwnStateHash(project, '_phase:build'); + + const projectConfig1: Pick = { + operationSettingsByOperationName: new Map([ + [ + '_phase:build', + { + operationName: '_phase:build', + outputFolderNames: ['lib-commonjs'] + } + ] + ]) + }; + + const projectConfig2: Pick = { + operationSettingsByOperationName: new Map([ + [ + '_phase:build', + { + operationName: '_phase:build', + outputFolderNames: ['lib-esm'] + } + ] + ]) + }; + + const input1: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([ + [ + project, + { + projectConfig: projectConfig1 as RushProjectConfiguration + } + ] + ]) + }); + + const input2: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([ + [ + project, + { + projectConfig: projectConfig2 as RushProjectConfiguration + } + ] + ]) + }); + + const result1: string = input1.getOperationOwnStateHash(project, '_phase:build'); + + const result2: string = input2.getOperationOwnStateHash(project, '_phase:build'); + + expect(result1).not.toEqual(baseline); + expect(result2).not.toEqual(baseline); + expect(result1).not.toEqual(result2); + }); + + it('Respects additionalOutputFilesByOperationName', () => { + const { project, options } = getTestConfig(); + const baseline: string = new InputsSnapshot(options).getOperationOwnStateHash(project, '_phase:build'); + + const projectConfig: Pick = { + operationSettingsByOperationName: new Map([ + [ + '_phase:build', + { + operationName: '_phase:build' + } + ] + ]) + }; + + const input: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([ + [ + project, + { + projectConfig: projectConfig as RushProjectConfiguration, + additionalFilesByOperationName: new Map([['_phase:build', new Set(['/ext/config.json'])]]) + } + ] + ]) + }); + + const result: string = input.getOperationOwnStateHash(project, '_phase:build'); + + expect(result).toMatchSnapshot(); + expect(result).not.toEqual(baseline); + }); + + it('Respects globalAdditionalFiles', () => { + const { project, options } = getTestConfig(); + const baseline: string = new InputsSnapshot(options).getOperationOwnStateHash(project, '_phase:build'); + + const input: InputsSnapshot = new InputsSnapshot({ + ...options, + globalAdditionalFiles: new Set(['common/config/some-config.json']) + }); + + const result: string = input.getOperationOwnStateHash(project); + + expect(result).toMatchSnapshot(); + expect(result).not.toEqual(baseline); + }); + + it('Respects incrementalBuildIgnoredGlobs', () => { + const { project, options } = getTestConfig(); + const baseline: string = new InputsSnapshot(options).getOperationOwnStateHash(project, '_phase:build'); + + const projectConfig1: Pick = { + incrementalBuildIgnoredGlobs: ['*2.js'] + }; + + const input1: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([ + [ + project, + { + projectConfig: projectConfig1 as RushProjectConfiguration + } + ] + ]) + }); + + const result1: string = input1.getOperationOwnStateHash(project); + + expect(result1).toMatchSnapshot(); + expect(result1).not.toEqual(baseline); + + const projectConfig2: Pick = { + incrementalBuildIgnoredGlobs: ['*1.js'] + }; + + const input2: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([ + [ + project, + { + projectConfig: projectConfig2 as RushProjectConfiguration + } + ] + ]) + }); + + const result2: string = input2.getOperationOwnStateHash(project); + + expect(result2).toMatchSnapshot(); + expect(result2).not.toEqual(baseline); + + expect(result2).not.toEqual(result1); + }); + + it('Respects dependsOnNodeVersion', () => { + const { project, options } = getTestConfig(); + const baseline: string = new InputsSnapshot(options).getOperationOwnStateHash(project, '_phase:build'); + + const projectConfig1: Pick = { + operationSettingsByOperationName: new Map([ + [ + '_phase:build', + { + operationName: '_phase:build', + dependsOnNodeVersion: true + } + ] + ]) + }; + + const input1: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([ + [ + project, + { + projectConfig: projectConfig1 as RushProjectConfiguration + } + ] + ]), + nodeVersion: 'v18.17.0' + }); + + const result1: string = input1.getOperationOwnStateHash(project, '_phase:build'); + + expect(result1).toMatchSnapshot(); + expect(result1).not.toEqual(baseline); + + const input2: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([ + [ + project, + { + projectConfig: projectConfig1 as RushProjectConfiguration + } + ] + ]), + nodeVersion: 'v20.10.0' + }); + + const result2: string = input2.getOperationOwnStateHash(project, '_phase:build'); + + expect(result2).toMatchSnapshot(); + expect(result2).not.toEqual(baseline); + expect(result2).not.toEqual(result1); + }); + + it('Respects dependsOnNodeVersion with major granularity', () => { + const { project, options } = getTestConfig(); + + const projectConfig: Pick = { + operationSettingsByOperationName: new Map([ + [ + '_phase:build', + { + operationName: '_phase:build', + dependsOnNodeVersion: 'major' + } + ] + ]) + }; + + // Same major, different minor - should produce the same hash + const input1: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([[project, { projectConfig: projectConfig as RushProjectConfiguration }]]), + nodeVersion: 'v18.17.0' + }); + + const input2: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([[project, { projectConfig: projectConfig as RushProjectConfiguration }]]), + nodeVersion: 'v18.20.3' + }); + + const result1: string = input1.getOperationOwnStateHash(project, '_phase:build'); + const result2: string = input2.getOperationOwnStateHash(project, '_phase:build'); + + expect(result1).toEqual(result2); + + // Different major - should produce a different hash + const input3: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([[project, { projectConfig: projectConfig as RushProjectConfiguration }]]), + nodeVersion: 'v20.10.0' + }); + + const result3: string = input3.getOperationOwnStateHash(project, '_phase:build'); + + expect(result3).not.toEqual(result1); + }); + + it('Respects dependsOnNodeVersion with minor granularity', () => { + const { project, options } = getTestConfig(); + + const projectConfig: Pick = { + operationSettingsByOperationName: new Map([ + [ + '_phase:build', + { + operationName: '_phase:build', + dependsOnNodeVersion: 'minor' + } + ] + ]) + }; + + // Same major.minor, different patch - should produce the same hash + const input1: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([[project, { projectConfig: projectConfig as RushProjectConfiguration }]]), + nodeVersion: 'v18.17.0' + }); + + const input2: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([[project, { projectConfig: projectConfig as RushProjectConfiguration }]]), + nodeVersion: 'v18.17.5' + }); + + const result1: string = input1.getOperationOwnStateHash(project, '_phase:build'); + const result2: string = input2.getOperationOwnStateHash(project, '_phase:build'); + + expect(result1).toEqual(result2); + + // Different minor - should produce a different hash + const input3: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([[project, { projectConfig: projectConfig as RushProjectConfiguration }]]), + nodeVersion: 'v18.20.0' + }); + + const result3: string = input3.getOperationOwnStateHash(project, '_phase:build'); + + expect(result3).not.toEqual(result1); + }); + + it('Respects dependsOnNodeVersion with patch granularity', () => { + const { project, options } = getTestConfig(); + + const projectConfig: Pick = { + operationSettingsByOperationName: new Map([ + [ + '_phase:build', + { + operationName: '_phase:build', + dependsOnNodeVersion: 'patch' + } + ] + ]) + }; + + // true and 'patch' should produce identical hashes + const projectConfigTrue: Pick = { + operationSettingsByOperationName: new Map([ + [ + '_phase:build', + { + operationName: '_phase:build', + dependsOnNodeVersion: true + } + ] + ]) + }; + + const inputPatch: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([[project, { projectConfig: projectConfig as RushProjectConfiguration }]]), + nodeVersion: 'v18.17.1' + }); + + const inputTrue: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([[project, { projectConfig: projectConfigTrue as RushProjectConfiguration }]]), + nodeVersion: 'v18.17.1' + }); + + const resultPatch: string = inputPatch.getOperationOwnStateHash(project, '_phase:build'); + const resultTrue: string = inputTrue.getOperationOwnStateHash(project, '_phase:build'); + + expect(resultPatch).toEqual(resultTrue); + + // Different patch - should produce a different hash + const input2: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([[project, { projectConfig: projectConfig as RushProjectConfiguration }]]), + nodeVersion: 'v18.17.2' + }); + + const result2: string = input2.getOperationOwnStateHash(project, '_phase:build'); + + expect(result2).not.toEqual(resultPatch); + }); + + it('Does not include node version when dependsOnNodeVersion is not set', () => { + const { project, options } = getTestConfig(); + + const projectConfig: Pick = { + operationSettingsByOperationName: new Map([ + [ + '_phase:build', + { + operationName: '_phase:build' + } + ] + ]) + }; + + const input1: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([ + [ + project, + { + projectConfig: projectConfig as RushProjectConfiguration + } + ] + ]), + nodeVersion: 'v18.17.0' + }); + + const input2: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([ + [ + project, + { + projectConfig: projectConfig as RushProjectConfiguration + } + ] + ]), + nodeVersion: 'v20.10.0' + }); + + const result1: string = input1.getOperationOwnStateHash(project, '_phase:build'); + const result2: string = input2.getOperationOwnStateHash(project, '_phase:build'); + + expect(result1).toEqual(result2); + }); + + it('Respects dependsOnEnvVars', () => { + const { project, options } = getTestConfig(); + const baseline: string = new InputsSnapshot(options).getOperationOwnStateHash(project, '_phase:build'); + + const projectConfig1: Pick = { + operationSettingsByOperationName: new Map([ + [ + '_phase:build', + { + operationName: '_phase:build', + dependsOnEnvVars: ['ENV_VAR'] + } + ] + ]) + }; + + const input1: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([ + [ + project, + { + projectConfig: projectConfig1 as RushProjectConfiguration + } + ] + ]), + environment: {} + }); + + const result1: string = input1.getOperationOwnStateHash(project, '_phase:build'); + + expect(result1).toMatchSnapshot(); + expect(result1).not.toEqual(baseline); + + const input2: InputsSnapshot = new InputsSnapshot({ + ...options, + projectMap: new Map([ + [ + project, + { + projectConfig: projectConfig1 as RushProjectConfiguration + } + ] + ]), + environment: { ENV_VAR: 'some_value' } + }); + + const result2: string = input2.getOperationOwnStateHash(project, '_phase:build'); + + expect(result2).toMatchSnapshot(); + expect(result2).not.toEqual(baseline); + expect(result2).not.toEqual(result1); + }); + }); +}); diff --git a/libraries/rush-lib/src/logic/incremental/test/__snapshots__/InputsSnapshot.test.ts.snap b/libraries/rush-lib/src/logic/incremental/test/__snapshots__/InputsSnapshot.test.ts.snap new file mode 100644 index 00000000000..f26d737bd38 --- /dev/null +++ b/libraries/rush-lib/src/logic/incremental/test/__snapshots__/InputsSnapshot.test.ts.snap @@ -0,0 +1,56 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`InputsSnapshot getOperationOwnStateHash Detects outputFileNames collisions 1`] = `"Configured output folder \\"lib\\" for operation \\"_phase:build\\" in project \\"a\\" contains tracked input file \\"a/lib/file3.js\\". If it is intended that this operation modifies its own input files, modify the build process to emit a warning if the output version differs from the input, and remove the directory from \\"outputFolderNames\\". This will ensure cache correctness. Otherwise, change the build process to output to a disjoint folder."`; + +exports[`InputsSnapshot getOperationOwnStateHash Handles trivial input 1`] = `"acf14e45ed255b0288e449432d48c285f619d146"`; + +exports[`InputsSnapshot getOperationOwnStateHash Respects additionalOutputFilesByOperationName 1`] = `"07f4147294d21a07865de84875d60bfbcc690652"`; + +exports[`InputsSnapshot getOperationOwnStateHash Respects dependsOnEnvVars 1`] = `"ad1f915d0ac6331c09febbbe1496c970a5401b73"`; + +exports[`InputsSnapshot getOperationOwnStateHash Respects dependsOnEnvVars 2`] = `"2c68d56fc9278b6495496070a6a992b929c37a83"`; + +exports[`InputsSnapshot getOperationOwnStateHash Respects dependsOnNodeVersion 1`] = `"bdfb861c2d1106b68b604b74e13f1c3f95095df6"`; + +exports[`InputsSnapshot getOperationOwnStateHash Respects dependsOnNodeVersion 2`] = `"cc182bde6aa81c0410ede7db22f3af2f0a24d3e3"`; + +exports[`InputsSnapshot getOperationOwnStateHash Respects globalAdditionalFiles 1`] = `"0e0437ad1941bacd098b22da15dc673f86ca6003"`; + +exports[`InputsSnapshot getOperationOwnStateHash Respects incrementalBuildIgnoredGlobs 1`] = `"f7b5af9ffdaa39831ed3374f28d0f7dccbee9c8d"`; + +exports[`InputsSnapshot getOperationOwnStateHash Respects incrementalBuildIgnoredGlobs 2`] = `"24047d4271ebf8badc9403c9a21a09fb6db2fb9c"`; + +exports[`InputsSnapshot getTrackedFileHashesForOperation Detects outputFileNames collisions 1`] = `"Configured output folder \\"lib\\" for operation \\"_phase:build\\" in project \\"a\\" contains tracked input file \\"a/lib/file3.js\\". If it is intended that this operation modifies its own input files, modify the build process to emit a warning if the output version differs from the input, and remove the directory from \\"outputFolderNames\\". This will ensure cache correctness. Otherwise, change the build process to output to a disjoint folder."`; + +exports[`InputsSnapshot getTrackedFileHashesForOperation Handles trivial input 1`] = ` +Map { + "a/file1.js" => "hash1", + "a/file2.js" => "hash2", + "a/lib/file3.js" => "hash3", +} +`; + +exports[`InputsSnapshot getTrackedFileHashesForOperation Respects additionalFilesByOperationName 1`] = ` +Map { + "/ext/config.json" => "hash4", + "a/file1.js" => "hash1", + "a/file2.js" => "hash2", + "a/lib/file3.js" => "hash3", +} +`; + +exports[`InputsSnapshot getTrackedFileHashesForOperation Respects globalAdditionalFiles 1`] = ` +Map { + "a/file1.js" => "hash1", + "a/file2.js" => "hash2", + "a/lib/file3.js" => "hash3", + "common/config/some-config.json" => "hash5", +} +`; + +exports[`InputsSnapshot getTrackedFileHashesForOperation Respects incrementalBuildIgnoredGlobs 1`] = ` +Map { + "a/file1.js" => "hash1", + "a/lib/file3.js" => "hash3", +} +`; diff --git a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts index 8ab1965bf61..220df476bdc 100644 --- a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts +++ b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts @@ -1,91 +1,380 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; +import * as semver from 'semver'; + import { FileConstants, FileSystem, - Import, - IPackageJson, + type IPackageJson, JsonFile, LockFile } from '@rushstack/node-core-library'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; import { LastInstallFlag } from '../../api/LastInstallFlag'; -import { PackageManagerName } from '../../api/packageManager/PackageManager'; -import { RushConfiguration } from '../../api/RushConfiguration'; -import { RushGlobalFolder } from '../../api/RushGlobalFolder'; +import type { PackageManagerName } from '../../api/packageManager/PackageManager'; +import type { RushConfiguration } from '../../api/RushConfiguration'; +import type { RushGlobalFolder } from '../../api/RushGlobalFolder'; import { Utilities } from '../../utilities/Utilities'; -import { IConfigurationEnvironment } from '../base/BasePackageManagerOptionsConfiguration'; - -const lodash: typeof import('lodash') = Import.lazy('lodash', require); +import type { IConfigurationEnvironment } from '../base/BasePackageManagerOptionsConfiguration'; +import type { PnpmOptionsConfiguration } from '../pnpm/PnpmOptionsConfiguration'; +import { PnpmWorkspaceFile } from '../pnpm/PnpmWorkspaceFile'; +import { merge } from '../../utilities/objectUtilities'; +import type { Subspace } from '../../api/Subspace'; +import { RushConstants } from '../RushConstants'; + +export interface ICommonPackageJsonPnpmSection { + overrides?: typeof PnpmOptionsConfiguration.prototype.globalOverrides; + packageExtensions?: typeof PnpmOptionsConfiguration.prototype.globalPackageExtensions; + peerDependencyRules?: typeof PnpmOptionsConfiguration.prototype.globalPeerDependencyRules; + neverBuiltDependencies?: typeof PnpmOptionsConfiguration.prototype.globalNeverBuiltDependencies; + onlyBuiltDependencies?: typeof PnpmOptionsConfiguration.prototype.globalOnlyBuiltDependencies; + ignoredOptionalDependencies?: typeof PnpmOptionsConfiguration.prototype.globalIgnoredOptionalDependencies; + allowedDeprecatedVersions?: typeof PnpmOptionsConfiguration.prototype.globalAllowedDeprecatedVersions; + patchedDependencies?: typeof PnpmOptionsConfiguration.prototype.globalPatchedDependencies; + trustPolicy?: typeof PnpmOptionsConfiguration.prototype.trustPolicy; + trustPolicyExclude?: typeof PnpmOptionsConfiguration.prototype.trustPolicyExclude; + trustPolicyIgnoreAfter?: typeof PnpmOptionsConfiguration.prototype.trustPolicyIgnoreAfterMinutes; +} interface ICommonPackageJson extends IPackageJson { - pnpm?: unknown; + pnpm?: ICommonPackageJsonPnpmSection; +} + +/** + * The pnpm-specific settings that Rush writes into the generated `common/temp` install files, + * derived from a subspace's pnpm options by {@link InstallHelpers.resolvePnpmSettings}. + */ +export interface IResolvedPnpmSettings { + /** + * The "pnpm" field to write into `common/temp/package.json`, or `undefined` for pnpm 11+ (which + * no longer reads that field — all settings are placed on {@link workspaceFile} instead). + */ + packageJsonPnpmSection: ICommonPackageJsonPnpmSection | undefined; + + /** + * Additional top-level properties to merge into `common/temp/package.json` + * (from `pnpmOptions.unsupportedPackageJsonSettings`). + */ + additionalPackageJsonProperties: unknown; + + /** + * The `common/temp/pnpm-workspace.yaml` file, populated with the version-gated pnpm settings. + * The caller is responsible for adding the workspace packages and saving the file. Only used by + * workspace installs. + */ + workspaceFile: PnpmWorkspaceFile; + + /** + * The configured pnpm `globalOverrides` (defaulting to `{}`), regardless of the pnpm version. + * Used to verify that the shrinkwrap file is up to date. + */ + configuredOverrides: Record; + + /** + * The configured pnpm `globalPackageExtensions`, regardless of the pnpm version. Used to verify + * that the shrinkwrap file is up to date. + */ + configuredPackageExtensions: typeof PnpmOptionsConfiguration.prototype.globalPackageExtensions; } export class InstallHelpers { - public static generateCommonPackageJson( - rushConfiguration: RushConfiguration, - dependencies: Map = new Map() - ): void { + public static async generateCommonPackageJsonAsync( + subspace: Subspace, + dependenciesMap: Map = new Map(), + resolvedPnpmSettings: IResolvedPnpmSettings | undefined + ): Promise { + const { packageJsonPnpmSection, additionalPackageJsonProperties } = resolvedPnpmSettings ?? {}; + + // Add any preferred versions to the top of the commonPackageJson + // do this in alphabetical order for simpler debugging + const sortedDependencyEntries: [string, string][] = Array.from(dependenciesMap.entries()).sort( + ([a], [b]) => (a < b ? -1 : a > b ? 1 : 0) + ); + const dependencies: Record = Object.fromEntries(sortedDependencyEntries); const commonPackageJson: ICommonPackageJson = { - dependencies: {}, + dependencies, description: 'Temporary file generated by the Rush tool', name: 'rush-common', private: true, - version: '0.0.0' + version: '0.0.0', + pnpm: packageJsonPnpmSection }; - if (rushConfiguration.packageManager === 'pnpm') { - const { pnpmOptions } = rushConfiguration; - if (pnpmOptions.globalOverrides) { - lodash.set(commonPackageJson, 'pnpm.overrides', pnpmOptions.globalOverrides); - } - if (pnpmOptions.globalPackageExtensions) { - lodash.set(commonPackageJson, 'pnpm.packageExtensions', pnpmOptions.globalPackageExtensions); - } - if (pnpmOptions.globalPeerDependencyRules) { - lodash.set(commonPackageJson, 'pnpm.peerDependencyRules', pnpmOptions.globalPeerDependencyRules); - } - if (pnpmOptions.globalNeverBuiltDependencies) { - lodash.set( - commonPackageJson, - 'pnpm.neverBuiltDependencies', - pnpmOptions.globalNeverBuiltDependencies + if (additionalPackageJsonProperties) { + merge(commonPackageJson, additionalPackageJsonProperties); + } + + // Example: "C:\MyRepo\common\temp\package.json" + const commonPackageJsonFilename: string = `${subspace.getSubspaceTempFolderPath()}/${FileConstants.PackageJson}`; + + // Don't update the file timestamp unless the content has changed, since "rush install" + // will consider this timestamp + await JsonFile.saveAsync(commonPackageJson, commonPackageJsonFilename, { + onlyIfChanged: true, + ignoreUndefinedValues: true + }); + } + + /** + * Interprets the pnpm options for the given subspace and derives the pnpm-specific settings that + * Rush writes into the generated install files: the "pnpm" field of `common/temp/package.json` + * (see {@link IResolvedPnpmSettings.packageJsonPnpmSection}) and the settings for + * `common/temp/pnpm-workspace.yaml` (see {@link IResolvedPnpmSettings.workspaceSettings}). + * + * This is the single place that reads through the pnpm options and emits the associated + * version-compatibility warnings, so that both "rush install" code paths stay consistent. + * + * Returns `undefined` when the package manager is not pnpm. + */ + public static resolvePnpmSettings( + rushConfiguration: RushConfiguration, + subspace: Subspace, + terminal: ITerminal + ): IResolvedPnpmSettings | undefined { + if (!rushConfiguration.isPnpm) { + return undefined; + } + + const { + globalOverrides = {}, + globalPackageExtensions, + globalPeerDependencyRules, + globalNeverBuiltDependencies, + globalOnlyBuiltDependencies, + globalIgnoredOptionalDependencies, + globalAllowedDeprecatedVersions, + globalPatchedDependencies, + globalCatalogs, + globalAllowBuilds, + minimumReleaseAgeMinutes, + minimumReleaseAgeExclude, + trustPolicy, + trustPolicyExclude, + // NOTE: the pnpm setting is `trustPolicyIgnoreAfter`, but the rush pnpm setting is `trustPolicyIgnoreAfterMinutes` + trustPolicyIgnoreAfterMinutes: trustPolicyIgnoreAfter, + unsupportedPackageJsonSettings + } = subspace.getPnpmOptions() || rushConfiguration.pnpmOptions; + + const pnpmVersion: string = rushConfiguration.packageManagerToolVersion; + const isPnpm11: boolean = semver.gte(pnpmVersion, '11.0.0'); + // Example: "C:\MyRepo\common\config\rush\pnpm-config.json" + const pnpmConfigLocation: string = `${rushConfiguration.commonRushConfigFolder}/${RushConstants.pnpmConfigFilename}`; + + // + // Derive the "pnpm" field of common/temp/package.json + // + let neverBuiltDependencies: ICommonPackageJsonPnpmSection['neverBuiltDependencies']; + if (globalNeverBuiltDependencies) { + if (isPnpm11) { + terminal.writeWarningLine( + Colorize.yellow( + `Your version of PNPM (${pnpmVersion}) ` + + `no longer supports the "globalNeverBuiltDependencies" field in ` + + `${pnpmConfigLocation}. ` + + 'Use "globalAllowBuilds" instead (with a value of false to deny build scripts).' + ) ); + } else { + neverBuiltDependencies = globalNeverBuiltDependencies; } - if (pnpmOptions.globalAllowedDeprecatedVersions) { - lodash.set( - commonPackageJson, - 'pnpm.allowedDeprecatedVersions', - pnpmOptions.globalAllowedDeprecatedVersions + } + + let onlyBuiltDependencies: ICommonPackageJsonPnpmSection['onlyBuiltDependencies']; + if (globalOnlyBuiltDependencies) { + if (isPnpm11) { + terminal.writeWarningLine( + Colorize.yellow( + `Your version of PNPM (${pnpmVersion}) ` + + `no longer supports the "globalOnlyBuiltDependencies" field in ` + + `${pnpmConfigLocation}. ` + + 'Use "globalAllowBuilds" instead (with a value of true to allow build scripts).' + ) ); + } else { + if (semver.lt(pnpmVersion, '10.1.0')) { + terminal.writeWarningLine( + Colorize.yellow( + `Your version of PNPM (${pnpmVersion}) ` + + `doesn't support the "globalOnlyBuiltDependencies" field in ` + + `${pnpmConfigLocation}. ` + + 'Remove this field or upgrade to PNPM 10.1.0 or newer.' + ) + ); + } + + onlyBuiltDependencies = globalOnlyBuiltDependencies; } - if (pnpmOptions.globalPatchedDependencies) { - lodash.set(commonPackageJson, 'pnpm.patchedDependencies', pnpmOptions.globalPatchedDependencies); - } - if (pnpmOptions.unsupportedPackageJsonSettings) { - lodash.merge(commonPackageJson, pnpmOptions.unsupportedPackageJsonSettings); + } + + if (globalIgnoredOptionalDependencies && semver.lt(pnpmVersion, '9.0.0')) { + terminal.writeWarningLine( + Colorize.yellow( + `Your version of PNPM (${pnpmVersion}) ` + + `doesn't support the "globalIgnoredOptionalDependencies" field in ` + + `${pnpmConfigLocation}. ` + + 'Remove this field or upgrade to PNPM 9.' + ) + ); + } + + if (trustPolicy !== undefined && semver.lt(pnpmVersion, '10.21.0')) { + terminal.writeWarningLine( + Colorize.yellow( + `Your version of PNPM (${pnpmVersion}) ` + + `doesn't support the "trustPolicy" field in ` + + `${pnpmConfigLocation}. ` + + 'Remove this field or upgrade to PNPM 10.21.0 or newer.' + ) + ); + } + + if (trustPolicyExclude && semver.lt(pnpmVersion, '10.22.0')) { + terminal.writeWarningLine( + Colorize.yellow( + `Your version of PNPM (${pnpmVersion}) ` + + `doesn't support the "trustPolicyExclude" field in ` + + `${pnpmConfigLocation}. ` + + 'Remove this field or upgrade to PNPM 10.22.0 or newer.' + ) + ); + } + + if (trustPolicyIgnoreAfter !== undefined && semver.lt(pnpmVersion, '10.27.0')) { + terminal.writeWarningLine( + Colorize.yellow( + `Your version of PNPM (${pnpmVersion}) ` + + `doesn't support the "trustPolicyIgnoreAfterMinutes" field in ` + + `${pnpmConfigLocation}. ` + + 'Remove this field or upgrade to PNPM 10.27.0 or newer.' + ) + ); + } + + // + // Derive the settings for common/temp/pnpm-workspace.yaml + // + if (globalCatalogs && semver.lt(pnpmVersion, '9.5.0')) { + terminal.writeWarningLine( + Colorize.yellow( + `Your version of pnpm (${pnpmVersion}) ` + + `doesn't support the "globalCatalogs" fields in ` + + `${pnpmConfigLocation}. ` + + 'Remove these fields or upgrade to pnpm 9.5.0 or newer.' + ) + ); + } + + // For pnpm 11+, "allowBuilds" replaces onlyBuiltDependencies/neverBuiltDependencies + let allowBuilds: Record | undefined; + if (isPnpm11) { + if (globalAllowBuilds) { + allowBuilds = globalAllowBuilds; + } else if (globalOnlyBuiltDependencies || globalNeverBuiltDependencies) { + // Backward compatibility: convert globalOnlyBuiltDependencies/globalNeverBuiltDependencies + // to allowBuilds format for pnpm 11+ + allowBuilds = {}; + if (globalOnlyBuiltDependencies) { + for (const pkg of globalOnlyBuiltDependencies) { + allowBuilds[pkg] = true; + } + } + + if (globalNeverBuiltDependencies) { + for (const pkg of globalNeverBuiltDependencies) { + allowBuilds[pkg] = false; + } + } } + } else if (globalAllowBuilds) { + terminal.writeWarningLine( + Colorize.yellow( + `Your version of pnpm (${pnpmVersion}) ` + + `doesn't support the "globalAllowBuilds" field in ` + + `${pnpmConfigLocation}. ` + + 'Remove this field or upgrade to pnpm 11.0.0 or newer.' + ) + ); } - // Add any preferred versions to the top of the commonPackageJson - // do this in alphabetical order for simpler debugging - for (const dependency of Array.from(dependencies.keys()).sort()) { - commonPackageJson.dependencies![dependency] = dependencies.get(dependency)!; + // pnpm does not read these fields from package.json, only from pnpm-workspace.yaml or .npmrc. + if ( + (minimumReleaseAgeMinutes !== undefined || minimumReleaseAgeExclude) && + semver.lt(pnpmVersion, '10.16.0') + ) { + terminal.writeWarningLine( + Colorize.yellow( + `Your version of pnpm (${pnpmVersion}) ` + + `doesn't support the "minimumReleaseAgeMinutes" or "minimumReleaseAgeExclude" fields in ` + + `${pnpmConfigLocation}. ` + + 'Remove these fields or upgrade to pnpm 10.16.0 or newer.' + ) + ); } - // Example: "C:\MyRepo\common\temp\package.json" - const commonPackageJsonFilename: string = path.join( - rushConfiguration.commonTempFolder, - FileConstants.PackageJson + // Populate a PnpmWorkspaceFile with the workspace settings. The caller adds the workspace + // packages to this file and saves it; here we only set the pnpm settings. + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile( + `${subspace.getSubspaceTempFolderPath()}/${RushConstants.pnpmWorkspaceFileName}` ); + workspaceFile.catalogs = globalCatalogs; + workspaceFile.allowBuilds = allowBuilds; + // NOTE: the pnpm setting is `minimumReleaseAge`, but the Rush setting is `minimumReleaseAgeMinutes` + workspaceFile.minimumReleaseAge = minimumReleaseAgeMinutes; + workspaceFile.minimumReleaseAgeExclude = minimumReleaseAgeExclude; + + // pnpm 11 no longer reads the "pnpm" field of package.json, so for pnpm 11+ we don't generate + // it at all; every setting is written to common/temp/pnpm-workspace.yaml instead. + // See https://github.com/microsoft/rushstack/issues/5837 + let packageJsonPnpmSection: ICommonPackageJsonPnpmSection | undefined; + + if (isPnpm11) { + workspaceFile.overrides = globalOverrides; + workspaceFile.packageExtensions = globalPackageExtensions; + workspaceFile.peerDependencyRules = globalPeerDependencyRules; + workspaceFile.allowedDeprecatedVersions = globalAllowedDeprecatedVersions; + workspaceFile.patchedDependencies = globalPatchedDependencies; + workspaceFile.ignoredOptionalDependencies = globalIgnoredOptionalDependencies; + workspaceFile.trustPolicy = trustPolicy; + workspaceFile.trustPolicyExclude = trustPolicyExclude; + workspaceFile.trustPolicyIgnoreAfter = trustPolicyIgnoreAfter; + + if (rushConfiguration.subspacesFeatureEnabled) { + // When subspaces are enabled, Rush generates a "global pnpmfile" that rewrites + // cross-subspace "workspace:*" dependency specifiers to "link:" specifiers. For pnpm 10 and + // earlier it is wired up via a "global-pnpmfile=" line in the generated .npmrc (see + // BaseInstallManager), but pnpm 11+ only reads auth/registry settings from .npmrc, so that + // line is silently ignored and installation fails with ERR_PNPM_WORKSPACE_PKG_NOT_FOUND. + // For pnpm 11+, emit the path via the generated pnpm-workspace.yaml instead. + workspaceFile.globalPnpmfile = `${subspace.getSubspaceTempFolderPath()}/${RushConstants.pnpmfileGlobalFilename}`; + } + } else { + // For older pnpm, these settings live in the "pnpm" field of package.json. + packageJsonPnpmSection = { + neverBuiltDependencies, + onlyBuiltDependencies, + overrides: globalOverrides, + packageExtensions: globalPackageExtensions, + peerDependencyRules: globalPeerDependencyRules, + allowedDeprecatedVersions: globalAllowedDeprecatedVersions, + patchedDependencies: globalPatchedDependencies, + ignoredOptionalDependencies: globalIgnoredOptionalDependencies, + trustPolicy, + trustPolicyExclude, + trustPolicyIgnoreAfter + }; + } - // Don't update the file timestamp unless the content has changed, since "rush install" - // will consider this timestamp - JsonFile.save(commonPackageJson, commonPackageJsonFilename, { onlyIfChanged: true }); + return { + packageJsonPnpmSection, + additionalPackageJsonProperties: unsupportedPackageJsonSettings, + workspaceFile, + // The configured overrides/packageExtensions are needed to verify the shrinkwrap is up to + // date, regardless of the pnpm version. + configuredOverrides: globalOverrides, + configuredPackageExtensions: globalPackageExtensions + }; } public static getPackageManagerEnvironment( @@ -97,27 +386,21 @@ export class InstallHelpers { let configurationEnvironment: IConfigurationEnvironment | undefined = undefined; if (rushConfiguration.packageManager === 'npm') { - if (rushConfiguration.npmOptions && rushConfiguration.npmOptions.environmentVariables) { - configurationEnvironment = rushConfiguration.npmOptions.environmentVariables; - } - } else if (rushConfiguration.packageManager === 'pnpm') { - if (rushConfiguration.pnpmOptions && rushConfiguration.pnpmOptions.environmentVariables) { - configurationEnvironment = rushConfiguration.pnpmOptions.environmentVariables; - } + configurationEnvironment = rushConfiguration.npmOptions?.environmentVariables; + } else if (rushConfiguration.isPnpm) { + configurationEnvironment = rushConfiguration.pnpmOptions?.environmentVariables; } else if (rushConfiguration.packageManager === 'yarn') { - if (rushConfiguration.yarnOptions && rushConfiguration.yarnOptions.environmentVariables) { - configurationEnvironment = rushConfiguration.yarnOptions.environmentVariables; - } + configurationEnvironment = rushConfiguration.yarnOptions?.environmentVariables; } - return InstallHelpers._mergeEnvironmentVariables(process.env, configurationEnvironment, options); + return _mergeEnvironmentVariables(process.env, configurationEnvironment, options); } /** * If the "(p)npm-local" symlink hasn't been set up yet, this creates it, installing the * specified (P)npm version in the user's home directory if needed. */ - public static async ensureLocalPackageManager( + public static async ensureLocalPackageManagerAsync( rushConfiguration: RushConfiguration, rushGlobalFolder: RushGlobalFolder, maxInstallAttempts: number, @@ -130,6 +413,7 @@ export class InstallHelpers { }; } else { logIfConsoleOutputIsNotRestricted = (message?: string) => { + // eslint-disable-next-line no-console console.log(message); }; } @@ -147,7 +431,7 @@ export class InstallHelpers { const packageManagerAndVersion: string = `${packageManager}-${packageManagerVersion}`; // Example: "C:\Users\YourName\.rush\pnpm-1.2.3" - const packageManagerToolFolder: string = path.join(rushUserFolder, packageManagerAndVersion); + const packageManagerToolFolder: string = `${rushUserFolder}/${packageManagerAndVersion}`; const packageManagerMarker: LastInstallFlag = new LastInstallFlag(packageManagerToolFolder, { node: process.versions.node @@ -155,17 +439,17 @@ export class InstallHelpers { logIfConsoleOutputIsNotRestricted(`Trying to acquire lock for ${packageManagerAndVersion}`); - const lock: LockFile = await LockFile.acquire(rushUserFolder, packageManagerAndVersion); + const lock: LockFile = await LockFile.acquireAsync(rushUserFolder, packageManagerAndVersion); logIfConsoleOutputIsNotRestricted(`Acquired lock for ${packageManagerAndVersion}`); - if (!packageManagerMarker.isValid() || lock.dirtyWhenAcquired) { + if (!(await packageManagerMarker.isValidAsync()) || lock.dirtyWhenAcquired) { logIfConsoleOutputIsNotRestricted( - colors.bold(`Installing ${packageManager} version ${packageManagerVersion}\n`) + Colorize.bold(`Installing ${packageManager} version ${packageManagerVersion}\n`) ); // note that this will remove the last-install flag from the directory - Utilities.installPackageInDirectory({ + await Utilities.installPackageInDirectoryAsync({ directory: packageManagerToolFolder, packageName: packageManager, version: rushConfiguration.packageManagerToolVersion, @@ -177,7 +461,10 @@ export class InstallHelpers { // In particular, we'll assume that two different NPM registries cannot have two // different implementations of the same version of the same package. // This was needed for: https://github.com/microsoft/rushstack/issues/691 - commonRushConfigFolder: rushConfiguration.commonRushConfigFolder + commonRushConfigFolder: rushConfiguration.commonRushConfigFolder, + // Only filter npm-incompatible properties when the repo uses pnpm or yarn. + // If the repo uses npm, the .npmrc is already configured for npm, so don't filter. + filterNpmIncompatibleProperties: rushConfiguration.packageManager !== 'npm' }); logIfConsoleOutputIsNotRestricted( @@ -189,16 +476,13 @@ export class InstallHelpers { ); } - packageManagerMarker.create(); + await packageManagerMarker.createAsync(); // Example: "C:\MyRepo\common\temp" FileSystem.ensureFolder(rushConfiguration.commonTempFolder); // Example: "C:\MyRepo\common\temp\pnpm-local" - const localPackageManagerToolFolder: string = path.join( - rushConfiguration.commonTempFolder, - `${packageManager}-local` - ); + const localPackageManagerToolFolder: string = `${rushConfiguration.commonTempFolder}/${packageManager}-local`; logIfConsoleOutputIsNotRestricted(`\nSymlinking "${localPackageManagerToolFolder}"`); logIfConsoleOutputIsNotRestricted(` --> "${packageManagerToolFolder}"`); @@ -206,63 +490,77 @@ export class InstallHelpers { // We cannot use FileSystem.exists() to test the existence of a symlink, because it will // return false for broken symlinks. There is no way to test without catching an exception. try { - FileSystem.deleteFolder(localPackageManagerToolFolder); + await FileSystem.deleteFolderAsync(localPackageManagerToolFolder); } catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { throw error; } } - FileSystem.createSymbolicLinkJunction({ + await FileSystem.createSymbolicLinkJunctionAsync({ linkTargetPath: packageManagerToolFolder, newLinkPath: localPackageManagerToolFolder }); lock.release(); } +} - // Helper for getPackageManagerEnvironment - private static _mergeEnvironmentVariables( - baseEnv: NodeJS.ProcessEnv, - environmentVariables?: IConfigurationEnvironment, - options: { - debug?: boolean; - } = {} - ): NodeJS.ProcessEnv { - const packageManagerEnv: NodeJS.ProcessEnv = baseEnv; - - if (environmentVariables) { - // eslint-disable-next-line guard-for-in - for (const envVar in environmentVariables) { - let setEnvironmentVariable: boolean = true; - console.log(`\nProcessing definition for environment variable: ${envVar}`); +// Helper for getPackageManagerEnvironment +function _mergeEnvironmentVariables( + baseEnv: NodeJS.ProcessEnv, + environmentVariables?: IConfigurationEnvironment, + options: { + debug?: boolean; + } = {} +): NodeJS.ProcessEnv { + const packageManagerEnv: NodeJS.ProcessEnv = baseEnv; + + if (environmentVariables) { + // eslint-disable-next-line guard-for-in + for (const envVar in environmentVariables) { + let setEnvironmentVariable: boolean = true; + // eslint-disable-next-line no-console + console.log(`\nProcessing definition for environment variable: ${envVar}`); + + if (baseEnv.hasOwnProperty(envVar)) { + setEnvironmentVariable = false; + // eslint-disable-next-line no-console + console.log(`Environment variable already defined:`); + // eslint-disable-next-line no-console + console.log(` Name: ${envVar}`); + // eslint-disable-next-line no-console + console.log(` Existing value: ${baseEnv[envVar]}`); + // eslint-disable-next-line no-console + console.log( + ` Value set in ${RushConstants.rushJsonFilename}: ${environmentVariables[envVar].value}` + ); - if (baseEnv.hasOwnProperty(envVar)) { - setEnvironmentVariable = false; - console.log(`Environment variable already defined:`); - console.log(` Name: ${envVar}`); - console.log(` Existing value: ${baseEnv[envVar]}`); - console.log(` Value set in rush.json: ${environmentVariables[envVar].value}`); - - if (environmentVariables[envVar].override) { - setEnvironmentVariable = true; - console.log(`Overriding the environment variable with the value set in rush.json.`); - } else { - console.log(colors.yellow(`WARNING: Not overriding the value of the environment variable.`)); - } + if (environmentVariables[envVar].override) { + setEnvironmentVariable = true; + // eslint-disable-next-line no-console + console.log( + `Overriding the environment variable with the value set in ${RushConstants.rushJsonFilename}.` + ); + } else { + // eslint-disable-next-line no-console + console.log(Colorize.yellow(`WARNING: Not overriding the value of the environment variable.`)); } + } - if (setEnvironmentVariable) { - if (options.debug) { - console.log(`Setting environment variable for package manager.`); - console.log(` Name: ${envVar}`); - console.log(` Value: ${environmentVariables[envVar].value}`); - } - packageManagerEnv[envVar] = environmentVariables[envVar].value; + if (setEnvironmentVariable) { + if (options.debug) { + // eslint-disable-next-line no-console + console.log(`Setting environment variable for package manager.`); + // eslint-disable-next-line no-console + console.log(` Name: ${envVar}`); + // eslint-disable-next-line no-console + console.log(` Value: ${environmentVariables[envVar].value}`); } + packageManagerEnv[envVar] = environmentVariables[envVar].value; } } - - return packageManagerEnv; } + + return packageManagerEnv; } diff --git a/libraries/rush-lib/src/logic/installManager/RushInstallManager.ts b/libraries/rush-lib/src/logic/installManager/RushInstallManager.ts index ad4f9a10bfa..4e1c294ba86 100644 --- a/libraries/rush-lib/src/logic/installManager/RushInstallManager.ts +++ b/libraries/rush-lib/src/logic/installManager/RushInstallManager.ts @@ -1,11 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as fs from 'fs'; -import * as path from 'path'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; + import * as semver from 'semver'; import * as ssri from 'ssri'; + import { JsonFile, Text, @@ -13,31 +14,33 @@ import { FileConstants, Sort, InternalError, - AlreadyReportedError, - LegacyAdapters + AlreadyReportedError } from '@rushstack/node-core-library'; -import { PrintUtilities } from '@rushstack/terminal'; +import { Colorize, PrintUtilities } from '@rushstack/terminal'; import { BaseInstallManager } from '../base/BaseInstallManager'; import type { IInstallManagerOptions } from '../base/BaseInstallManagerTypes'; -import { BaseShrinkwrapFile } from '../../logic/base/BaseShrinkwrapFile'; -import { IRushTempPackageJson } from '../../logic/base/BasePackage'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; -import { RushConstants } from '../../logic/RushConstants'; +import type { BaseShrinkwrapFile } from '../base/BaseShrinkwrapFile'; +import type { IRushTempPackageJson } from '../base/BasePackage'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import { RushConstants } from '../RushConstants'; import { Stopwatch } from '../../utilities/Stopwatch'; import { Utilities } from '../../utilities/Utilities'; -import { PackageJsonEditor, DependencyType, PackageJsonDependency } from '../../api/PackageJsonEditor'; +import { + type PackageJsonEditor, + DependencyType, + type PackageJsonDependency +} from '../../api/PackageJsonEditor'; import { DependencySpecifier, DependencySpecifierType } from '../DependencySpecifier'; -import { InstallHelpers } from './InstallHelpers'; +import { InstallHelpers, type IResolvedPnpmSettings } from './InstallHelpers'; import { TempProjectHelper } from '../TempProjectHelper'; -import { RushGlobalFolder } from '../../api/RushGlobalFolder'; -import { RushConfiguration } from '../..'; -import { PurgeManager } from '../PurgeManager'; +import type { RushGlobalFolder } from '../../api/RushGlobalFolder'; +import type { RushConfiguration } from '../..'; +import type { PurgeManager } from '../PurgeManager'; import { LinkManagerFactory } from '../LinkManagerFactory'; -import { BaseLinkManager } from '../base/BaseLinkManager'; -import { PnpmShrinkwrapFile, IPnpmShrinkwrapDependencyYaml } from '../pnpm/PnpmShrinkwrapFile'; - -const globEscape: (unescaped: string) => string = require('glob-escape'); // No @types/glob-escape package exists +import type { BaseLinkManager } from '../base/BaseLinkManager'; +import type { PnpmShrinkwrapFile, IPnpmShrinkwrapDependencyYaml } from '../pnpm/PnpmShrinkwrapFile'; +import type { Subspace } from '../../api/Subspace'; /** * The "noMtime" flag is new in tar@4.4.1 and not available yet for \@types/tar. @@ -67,7 +70,10 @@ export class RushInstallManager extends BaseInstallManager { options: IInstallManagerOptions ) { super(rushConfiguration, rushGlobalFolder, purgeManager, options); - this._tempProjectHelper = new TempProjectHelper(this.rushConfiguration); + this._tempProjectHelper = new TempProjectHelper( + this.rushConfiguration, + rushConfiguration.defaultSubspace + ); } /** @@ -75,21 +81,23 @@ export class RushInstallManager extends BaseInstallManager { * If shrinkwrapFile is provided, this function also validates whether it contains * everything we need to install and returns true if so; in all other cases, * the return value is false. - * - * @override */ - public async prepareCommonTempAsync( + public override async prepareCommonTempAsync( + subspace: Subspace, shrinkwrapFile: BaseShrinkwrapFile | undefined ): Promise<{ shrinkwrapIsUpToDate: boolean; shrinkwrapWarnings: string[] }> { const stopwatch: Stopwatch = Stopwatch.start(); + const { fullUpgrade, variant } = this.options; + // Example: "C:\MyRepo\common\temp\projects" const tempProjectsFolder: string = path.join( this.rushConfiguration.commonTempFolder, RushConstants.rushTempProjectsFolderName ); - console.log('\n' + colors.bold('Updating temp projects in ' + tempProjectsFolder)); + // eslint-disable-next-line no-console + console.log('\n' + Colorize.bold('Updating temp projects in ' + tempProjectsFolder)); Utilities.createFolderWithRetry(tempProjectsFolder); @@ -101,10 +109,12 @@ export class RushInstallManager extends BaseInstallManager { if (!shrinkwrapFile) { shrinkwrapIsUpToDate = false; - } else if (shrinkwrapFile.isWorkspaceCompatible && !this.options.fullUpgrade) { + } else if (shrinkwrapFile.isWorkspaceCompatible && !fullUpgrade) { + // eslint-disable-next-line no-console console.log(); + // eslint-disable-next-line no-console console.log( - colors.red( + Colorize.red( 'The shrinkwrap file had previously been updated to support workspaces. Run "rush update --full" ' + 'to update the shrinkwrap file.' ) @@ -113,14 +123,17 @@ export class RushInstallManager extends BaseInstallManager { } // dependency name --> version specifier - const allExplicitPreferredVersions: Map = this.rushConfiguration - .getCommonVersions(this.options.variant) + const allExplicitPreferredVersions: Map = this.rushConfiguration.defaultSubspace + .getCommonVersions(variant) .getAllPreferredVersions(); if (shrinkwrapFile) { // Check any (explicitly) preferred dependencies first allExplicitPreferredVersions.forEach((version: string, dependency: string) => { - const dependencySpecifier: DependencySpecifier = new DependencySpecifier(dependency, version); + const dependencySpecifier: DependencySpecifier = DependencySpecifier.parseWithCache( + dependency, + version + ); if (!shrinkwrapFile.hasCompatibleTopLevelDependency(dependencySpecifier)) { shrinkwrapWarnings.push( @@ -139,13 +152,15 @@ export class RushInstallManager extends BaseInstallManager { // If there are orphaned projects, we need to update const orphanedProjects: ReadonlyArray = shrinkwrapFile.findOrphanedProjects( - this.rushConfiguration + this.rushConfiguration, + this.rushConfiguration.defaultSubspace ); + if (orphanedProjects.length > 0) { - for (const orhpanedProject of orphanedProjects) { + for (const orphanedProject of orphanedProjects) { shrinkwrapWarnings.push( - `Your ${this.rushConfiguration.shrinkwrapFilePhrase} references "${orhpanedProject}" ` + - 'which was not found in rush.json' + `Your ${this.rushConfiguration.shrinkwrapFilePhrase} references "${orphanedProject}" ` + + `which was not found in ${RushConstants.rushJsonFilename}` ); } shrinkwrapIsUpToDate = false; @@ -155,7 +170,7 @@ export class RushInstallManager extends BaseInstallManager { // dependency name --> version specifier const commonDependencies: Map = new Map([ ...allExplicitPreferredVersions, - ...this.rushConfiguration.getImplicitlyPreferredVersions(this.options.variant) + ...this.rushConfiguration.getImplicitlyPreferredVersions(subspace, variant) ]); // To make the common/package.json file more readable, sort alphabetically @@ -216,7 +231,10 @@ export class RushInstallManager extends BaseInstallManager { Sort.sortMapKeys(tempDependencies); for (const [packageName, packageVersion] of tempDependencies.entries()) { - const dependencySpecifier: DependencySpecifier = new DependencySpecifier(packageName, packageVersion); + const dependencySpecifier: DependencySpecifier = DependencySpecifier.parseWithCache( + packageName, + packageVersion + ); // Is there a locally built Rush project that could satisfy this dependency? // If so, then we will symlink to the project folder rather than to common/temp/node_modules. @@ -303,9 +321,11 @@ export class RushInstallManager extends BaseInstallManager { // Delete the existing tarball and create a new one this._tempProjectHelper.createTempProjectTarball(rushProject); + // eslint-disable-next-line no-console console.log(`Updating ${tarballFile}`); } catch (error) { - console.log(colors.yellow(error as string)); + // eslint-disable-next-line no-console + console.log(Colorize.yellow(error as string)); // delete everything in case of any error FileSystem.deleteFile(tarballFile); FileSystem.deleteFile(tempPackageJsonFilename); @@ -316,7 +336,7 @@ export class RushInstallManager extends BaseInstallManager { // with the shrinkwrap file, since these will cause install to fail. if ( shrinkwrapFile && - this.rushConfiguration.packageManager === 'pnpm' && + this.rushConfiguration.isPnpm && this.rushConfiguration.experimentsConfiguration.configuration.usePnpmFrozenLockfileForRushInstall ) { const pnpmShrinkwrapFile: PnpmShrinkwrapFile = shrinkwrapFile as PnpmShrinkwrapFile; @@ -333,9 +353,11 @@ export class RushInstallManager extends BaseInstallManager { } // Save the package.json if we modified the version references and warn that the package.json was modified - if (packageJson.saveIfModified()) { + const modified: boolean = await packageJson.saveIfModifiedAsync(); + if (modified) { + // eslint-disable-next-line no-console console.log( - colors.yellow( + Colorize.yellow( `"${rushProject.packageName}" depends on one or more local packages which used "workspace:" ` + 'notation. The package.json has been modified and must be committed to source control.' ) @@ -344,11 +366,8 @@ export class RushInstallManager extends BaseInstallManager { } // Remove the workspace file if it exists - if (this.rushConfiguration.packageManager === 'pnpm') { - const workspaceFilePath: string = path.join( - this.rushConfiguration.commonTempFolder, - 'pnpm-workspace.yaml' - ); + if (this.rushConfiguration.isPnpm) { + const workspaceFilePath: string = `${this.rushConfiguration.commonTempFolder}/pnpm-workspace.yaml`; try { await FileSystem.deleteFileAsync(workspaceFilePath); } catch (e) { @@ -358,17 +377,30 @@ export class RushInstallManager extends BaseInstallManager { } } - // Write the common package.json - InstallHelpers.generateCommonPackageJson(this.rushConfiguration, commonDependencies); + // Read the pnpm options (if any) in a single place, then write the common package.json. + const pnpmSettings: IResolvedPnpmSettings | undefined = InstallHelpers.resolvePnpmSettings( + this.rushConfiguration, + this.rushConfiguration.defaultSubspace, + this._terminal + ); + await InstallHelpers.generateCommonPackageJsonAsync( + this.rushConfiguration.defaultSubspace, + commonDependencies, + pnpmSettings + ); stopwatch.stop(); + // eslint-disable-next-line no-console console.log(`Finished creating temporary modules (${stopwatch.toString()})`); return { shrinkwrapIsUpToDate, shrinkwrapWarnings }; } private _revertWorkspaceNotation(dependency: PackageJsonDependency): boolean { - const specifier: DependencySpecifier = new DependencySpecifier(dependency.name, dependency.version); + const specifier: DependencySpecifier = DependencySpecifier.parseWithCache( + dependency.name, + dependency.version + ); if (specifier.specifierType !== DependencySpecifierType.Workspace) { return false; } @@ -415,11 +447,13 @@ export class RushInstallManager extends BaseInstallManager { /** * Check whether or not the install is already valid, and therefore can be skipped. - * - * @override */ - protected canSkipInstall(lastModifiedDate: Date): boolean { - if (!super.canSkipInstall(lastModifiedDate)) { + protected override async canSkipInstallAsync( + lastModifiedDate: Date, + subspace: Subspace, + variant: string | undefined + ): Promise { + if (!(await super.canSkipInstallAsync(lastModifiedDate, subspace, variant))) { return false; } @@ -434,15 +468,13 @@ export class RushInstallManager extends BaseInstallManager { }) ); - return Utilities.isFileTimestampCurrent(lastModifiedDate, potentiallyChangedFiles); + return Utilities.isFileTimestampCurrentAsync(lastModifiedDate, potentiallyChangedFiles); } /** * Runs "npm/pnpm/yarn install" in the "common/temp" folder. - * - * @override */ - protected async installAsync(cleanInstall: boolean): Promise { + protected override async installAsync(cleanInstall: boolean, subspace: Subspace): Promise { // Since we are actually running npm/pnpm/yarn install, recreate all the temp project tarballs. // This ensures that any existing tarballs with older header bits will be regenerated. // It is safe to assume that temp project pacakge.jsons already exist. @@ -454,10 +486,12 @@ export class RushInstallManager extends BaseInstallManager { // The user must request that via the command line. if (cleanInstall) { if (this.rushConfiguration.packageManager === 'npm') { + // eslint-disable-next-line no-console console.log(`Deleting the "npm-cache" folder`); // This is faster and more thorough than "npm cache clean" this.installRecycler.moveFolder(this.rushConfiguration.npmCacheFolder); + // eslint-disable-next-line no-console console.log(`Deleting the "npm-tmp" folder`); this.installRecycler.moveFolder(this.rushConfiguration.npmTmpFolder); } @@ -483,6 +517,7 @@ export class RushInstallManager extends BaseInstallManager { // YES: Delete "node_modules" // Explain to the user why we are hosing their node_modules folder + // eslint-disable-next-line no-console console.log('Deleting files from ' + commonNodeModulesFolder); this.installRecycler.moveFolder(commonNodeModulesFolder); @@ -493,17 +528,18 @@ export class RushInstallManager extends BaseInstallManager { // note: it is not necessary to run "prune" with pnpm if (this.rushConfiguration.packageManager === 'npm') { + // eslint-disable-next-line no-console console.log( `Running "${this.rushConfiguration.packageManager} prune"` + ` in ${this.rushConfiguration.commonTempFolder}` ); const args: string[] = ['prune']; - this.pushConfigurationArgs(args, this.options); + this.pushConfigurationArgs(args, this.options, subspace); - Utilities.executeCommandWithRetry( + await Utilities.executeCommandWithRetryAsync( { command: packageManagerFilename, - args: args, + args, workingDirectory: this.rushConfiguration.commonTempFolder, environment: packageManagerEnv }, @@ -519,18 +555,18 @@ export class RushInstallManager extends BaseInstallManager { commonNodeModulesFolder, RushConstants.rushTempNpmScope ); + // eslint-disable-next-line no-console console.log(`Deleting ${pathToDeleteWithoutStar}\\*`); // Glob can't handle Windows paths - const normalizedpathToDeleteWithoutStar: string = Text.replaceAll( + const normalizedPathToDeleteWithoutStar: string = Text.replaceAll( pathToDeleteWithoutStar, '\\', '/' ); - const { default: glob } = await import('glob'); - const tempModulePaths: string[] = await LegacyAdapters.convertCallbackToPromise( - glob, - globEscape(normalizedpathToDeleteWithoutStar) + '/*' + const { default: glob } = await import('fast-glob'); + const tempModulePaths: string[] = await glob( + glob.escapePath(normalizedPathToDeleteWithoutStar) + '/*' ); // Example: "C:/MyRepo/common/temp/node_modules/@rush-temp/*" for (const tempModulePath of tempModulePaths) { @@ -550,6 +586,7 @@ export class RushInstallManager extends BaseInstallManager { 'npm-@rush-temp' ); if (FileSystem.exists(yarnRushTempCacheFolder)) { + // eslint-disable-next-line no-console console.log('Deleting ' + yarnRushTempCacheFolder); Utilities.dangerouslyDeletePath(yarnRushTempCacheFolder); } @@ -557,11 +594,12 @@ export class RushInstallManager extends BaseInstallManager { // Run "npm install" in the common folder const installArgs: string[] = ['install']; - this.pushConfigurationArgs(installArgs, this.options); + this.pushConfigurationArgs(installArgs, this.options, subspace); + // eslint-disable-next-line no-console console.log( '\n' + - colors.bold( + Colorize.bold( `Running "${this.rushConfiguration.packageManager} install" in` + ` ${this.rushConfiguration.commonTempFolder}` ) + @@ -570,9 +608,10 @@ export class RushInstallManager extends BaseInstallManager { // If any diagnostic options were specified, then show the full command-line if (this.options.debug || this.options.collectLogFile || this.options.networkConcurrency) { + // eslint-disable-next-line no-console console.log( '\n' + - colors.green('Invoking package manager: ') + + Colorize.green('Invoking package manager: ') + FileSystem.getRealPath(packageManagerFilename) + ' ' + installArgs.join(' ') + @@ -580,7 +619,7 @@ export class RushInstallManager extends BaseInstallManager { ); } - Utilities.executeCommandWithRetry( + await Utilities.executeCommandWithRetryAsync( { command: packageManagerFilename, args: installArgs, @@ -590,8 +629,9 @@ export class RushInstallManager extends BaseInstallManager { }, this.options.maxInstallAttempts, () => { - if (this.rushConfiguration.packageManager === 'pnpm') { - console.log(colors.yellow(`Deleting the "node_modules" folder`)); + if (this.rushConfiguration.isPnpm) { + // eslint-disable-next-line no-console + console.log(Colorize.yellow(`Deleting the "node_modules" folder`)); this.installRecycler.moveFolder(commonNodeModulesFolder); // Leave the pnpm-store as is for the retry. This ensures that packages that have already @@ -604,27 +644,30 @@ export class RushInstallManager extends BaseInstallManager { ); if (this.rushConfiguration.packageManager === 'npm') { - console.log('\n' + colors.bold('Running "npm shrinkwrap"...')); + // eslint-disable-next-line no-console + console.log('\n' + Colorize.bold('Running "npm shrinkwrap"...')); const npmArgs: string[] = ['shrinkwrap']; - this.pushConfigurationArgs(npmArgs, this.options); - Utilities.executeCommand({ + this.pushConfigurationArgs(npmArgs, this.options, subspace); + await Utilities.executeCommandAsync({ command: this.rushConfiguration.packageManagerToolFilename, args: npmArgs, workingDirectory: this.rushConfiguration.commonTempFolder }); + // eslint-disable-next-line no-console console.log('"npm shrinkwrap" completed\n'); await this._fixupNpm5RegressionAsync(); } } - protected async postInstallAsync(): Promise { + protected async postInstallAsync(subspace: Subspace): Promise { if (!this.options.noLink) { const linkManager: BaseLinkManager = LinkManagerFactory.getLinkManager(this.rushConfiguration); - await linkManager.createSymlinksForProjects(false); + await linkManager.createSymlinksForProjectsAsync(false); } else { + // eslint-disable-next-line no-console console.log( - '\n' + colors.yellow('Since "--no-link" was specified, you will need to run "rush link" manually.') + '\n' + Colorize.yellow('Since "--no-link" was specified, you will need to run "rush link" manually.') ); } } @@ -654,10 +697,9 @@ export class RushInstallManager extends BaseInstallManager { let anyChanges: boolean = false; - const { default: glob } = await import('glob'); - const packageJsonPaths: string[] = await LegacyAdapters.convertCallbackToPromise( - glob, - globEscape(normalizedPathToDeleteWithoutStar) + '/*/package.json' + const { default: glob } = await import('fast-glob'); + const packageJsonPaths: string[] = await glob( + glob.escapePath(normalizedPathToDeleteWithoutStar) + '/*/package.json' ); // Example: "C:/MyRepo/common/temp/node_modules/@rush-temp/*/package.json" for (const packageJsonPath of packageJsonPaths) { @@ -673,7 +715,10 @@ export class RushInstallManager extends BaseInstallManager { } if (anyChanges) { - console.log('\n' + colors.yellow(PrintUtilities.wrapWords(`Applied workaround for NPM 5 bug`)) + '\n'); + // eslint-disable-next-line no-console + console.log( + '\n' + Colorize.yellow(PrintUtilities.wrapWords(`Applied workaround for NPM 5 bug`)) + '\n' + ); } } @@ -688,9 +733,10 @@ export class RushInstallManager extends BaseInstallManager { for (const rushProject of this.rushConfiguration.projects) { if (!tempProjectNames.has(rushProject.tempProjectName)) { + // eslint-disable-next-line no-console console.log( '\n' + - colors.yellow( + Colorize.yellow( PrintUtilities.wrapWords( `Your ${this.rushConfiguration.shrinkwrapFilePhrase} is missing the project "${rushProject.packageName}".` ) diff --git a/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts b/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts index 557e77718b6..de745a72338 100644 --- a/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts +++ b/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts @@ -1,40 +1,63 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; +import * as path from 'node:path'; +import { createHash } from 'node:crypto'; + import * as semver from 'semver'; -import { FileSystem, FileConstants, AlreadyReportedError, Async } from '@rushstack/node-core-library'; + +import { + FileSystem, + FileConstants, + AlreadyReportedError, + Async, + type IDependenciesMetaTable, + InternalError, + Objects, + Path, + Sort +} from '@rushstack/node-core-library'; +import { Colorize, ConsoleTerminalProvider } from '@rushstack/terminal'; import { BaseInstallManager } from '../base/BaseInstallManager'; import type { IInstallManagerOptions } from '../base/BaseInstallManagerTypes'; -import { BaseShrinkwrapFile } from '../../logic/base/BaseShrinkwrapFile'; +import type { BaseShrinkwrapFile } from '../base/BaseShrinkwrapFile'; import { DependencySpecifier, DependencySpecifierType } from '../DependencySpecifier'; -import { PackageJsonEditor, DependencyType } from '../../api/PackageJsonEditor'; -import { PnpmWorkspaceFile } from '../pnpm/PnpmWorkspaceFile'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; -import { RushConstants } from '../../logic/RushConstants'; +import { + type PackageJsonEditor, + DependencyType, + type PackageJsonDependencyMeta +} from '../../api/PackageJsonEditor'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import { RushConstants } from '../RushConstants'; import { Utilities } from '../../utilities/Utilities'; -import { InstallHelpers } from './InstallHelpers'; -import { CommonVersionsConfiguration } from '../../api/CommonVersionsConfiguration'; -import { RepoStateFile } from '../RepoStateFile'; -import { LastLinkFlagFactory } from '../../api/LastLinkFlag'; +import { InstallHelpers, type IResolvedPnpmSettings } from './InstallHelpers'; +import type { CommonVersionsConfiguration } from '../../api/CommonVersionsConfiguration'; +import type { RepoStateFile } from '../RepoStateFile'; import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration'; import { ShrinkwrapFileFactory } from '../ShrinkwrapFileFactory'; import { BaseProjectShrinkwrapFile } from '../base/BaseProjectShrinkwrapFile'; +import { type CustomTipId, type ICustomTipInfo, PNPM_CUSTOM_TIPS } from '../../api/CustomTipsConfiguration'; +import type { PnpmShrinkwrapFile } from '../pnpm/PnpmShrinkwrapFile'; +import type { Subspace } from '../../api/Subspace'; +import { BaseLinkManager, SymlinkKind } from '../base/BaseLinkManager'; +import { FlagFile } from '../../api/FlagFile'; +import { Stopwatch } from '../../utilities/Stopwatch'; + +export interface IPnpmModules { + hoistedDependencies: { [dep in string]: { [depPath in string]: string } }; +} /** * This class implements common logic between "rush install" and "rush update". */ export class WorkspaceInstallManager extends BaseInstallManager { - /** - * @override - */ - public async doInstallAsync(): Promise { + public override async doInstallAsync(): Promise { // TODO: Remove when "rush link" and "rush unlink" are deprecated if (this.options.noLink) { + // eslint-disable-next-line no-console console.log( - colors.red( + Colorize.red( 'The "--no-link" option was provided but is not supported when using workspaces. Run the command again ' + 'without specifying this argument.' ) @@ -50,11 +73,10 @@ export class WorkspaceInstallManager extends BaseInstallManager { * If shrinkwrapFile is provided, this function also validates whether it contains * everything we need to install and returns true if so; in all other cases, * the return value is false. - * - * @override */ - protected async prepareCommonTempAsync( - shrinkwrapFile: BaseShrinkwrapFile | undefined + protected override async prepareCommonTempAsync( + subspace: Subspace, + shrinkwrapFile: (PnpmShrinkwrapFile & BaseShrinkwrapFile) | undefined ): Promise<{ shrinkwrapIsUpToDate: boolean; shrinkwrapWarnings: string[] }> { // Block use of the RUSH_TEMP_FOLDER environment variable if (EnvironmentConfiguration.rushTempFolderOverride !== undefined) { @@ -64,7 +86,10 @@ export class WorkspaceInstallManager extends BaseInstallManager { ); } - console.log('\n' + colors.bold('Updating workspace files in ' + this.rushConfiguration.commonTempFolder)); + const { fullUpgrade, allowShrinkwrapUpdates, variant } = this.options; + + // eslint-disable-next-line no-console + console.log('\n' + Colorize.bold('Updating workspace files in ' + subspace.getSubspaceTempFolderPath())); const shrinkwrapWarnings: string[] = []; @@ -75,10 +100,12 @@ export class WorkspaceInstallManager extends BaseInstallManager { if (!shrinkwrapFile) { shrinkwrapIsUpToDate = false; } else { - if (!shrinkwrapFile.isWorkspaceCompatible && !this.options.fullUpgrade) { + if (!shrinkwrapFile.isWorkspaceCompatible && !fullUpgrade) { + // eslint-disable-next-line no-console console.log(); + // eslint-disable-next-line no-console console.log( - colors.red( + Colorize.red( 'The shrinkwrap file has not been updated to support workspaces. Run "rush update --full" to update ' + 'the shrinkwrap file.' ) @@ -88,47 +115,104 @@ export class WorkspaceInstallManager extends BaseInstallManager { // If there are orphaned projects, we need to update const orphanedProjects: ReadonlyArray = shrinkwrapFile.findOrphanedProjects( - this.rushConfiguration + this.rushConfiguration, + subspace ); + if (orphanedProjects.length > 0) { - for (const orhpanedProject of orphanedProjects) { + for (const orphanedProject of orphanedProjects) { shrinkwrapWarnings.push( - `Your ${this.rushConfiguration.shrinkwrapFilePhrase} references "${orhpanedProject}" ` + - 'which was not found in rush.json' + `Your ${this.rushConfiguration.shrinkwrapFilePhrase} references "${orphanedProject}" ` + + `which was not found in ${RushConstants.rushJsonFilename}` ); } + shrinkwrapIsUpToDate = false; } } // If preferred versions have been updated, or if the repo-state.json is invalid, // we can't be certain of the state of the shrinkwrap - const repoState: RepoStateFile = this.rushConfiguration.getRepoState(this.options.variant); + const repoState: RepoStateFile = subspace.getRepoState(); if (!repoState.isValid) { shrinkwrapWarnings.push( `The ${RushConstants.repoStateFilename} file is invalid. There may be a merge conflict marker in the file.` ); shrinkwrapIsUpToDate = false; } else { - const commonVersions: CommonVersionsConfiguration = this.rushConfiguration.getCommonVersions( - this.options.variant - ); + const commonVersions: CommonVersionsConfiguration = subspace.getCommonVersions(variant); if (repoState.preferredVersionsHash !== commonVersions.getPreferredVersionsHash()) { shrinkwrapWarnings.push( `Preferred versions from ${RushConstants.commonVersionsFilename} have been modified.` ); shrinkwrapIsUpToDate = false; } + + const stopwatch: Stopwatch = Stopwatch.start(); + + const packageJsonInjectedDependenciesHash: string | undefined = + subspace.getPackageJsonInjectedDependenciesHash(variant); + + stopwatch.stop(); + + this._terminal.writeDebugLine( + `Total amount of time spent to hash related package.json files in the injected installation case: ${stopwatch.toString()}` + ); + + if (packageJsonInjectedDependenciesHash) { + // if packageJsonInjectedDependenciesHash exists + // make sure it matches the value in repoState + if (packageJsonInjectedDependenciesHash !== repoState.packageJsonInjectedDependenciesHash) { + shrinkwrapWarnings.push(`Some injected dependencies' package.json might have been modified.`); + shrinkwrapIsUpToDate = false; + } + } else { + // if packageJsonInjectedDependenciesHash not exists + // there is a situation that the subspace previously has injected dependencies but removed + // so we can check if the repoState up to date + if (repoState.packageJsonInjectedDependenciesHash !== undefined) { + shrinkwrapWarnings.push( + `It was detected that ${repoState.filePath} contains packageJsonInjectedDependenciesHash` + + ' but the injected dependencies feature is not enabled. You can manually remove this field in repo-state.json.' + + ' Or run rush update command to update the repo-state.json file.' + ); + } + } } - // To generate the workspace file, we will add each project to the file as we loop through and validate - const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile( - path.join(this.rushConfiguration.commonTempFolder, 'pnpm-workspace.yaml') + // Read the pnpm options in a single place, deriving the "pnpm" field for the common package.json + // and a pnpm-workspace.yaml file pre-populated with its settings (and emitting any related + // warnings). WorkspaceInstallManager is only used for pnpm, so this always returns a value. + const pnpmSettings: IResolvedPnpmSettings | undefined = InstallHelpers.resolvePnpmSettings( + this.rushConfiguration, + subspace, + this._terminal ); + if (!pnpmSettings) { + throw new InternalError('Expected pnpm settings to be resolved for a workspace install'); + } + + // To generate the workspace file, we will add each project to the file as we loop through and + // validate. The file already carries the pnpm settings; here we add the workspace packages. + const { workspaceFile, configuredOverrides, configuredPackageExtensions } = pnpmSettings; + + // For pnpm package manager, we need to handle dependenciesMeta changes in package.json. See more: https://pnpm.io/package_json#dependenciesmeta + // If dependenciesMeta settings is different between package.json and pnpm-lock.yaml, then shrinkwrapIsUpToDate return false. + // Build a object for dependenciesMeta settings in projects' package.json + // key is the package path, value is the dependenciesMeta info for that package + const expectedDependenciesMetaByProjectRelativePath: Record = {}; + const commonTempFolder: string = subspace.getSubspaceTempFolderPath(); + const rushJsonFolder: string = this.rushConfiguration.rushJsonFolder; + // get the relative path from common temp folder to repo root folder + const relativeFromTempFolderToRootFolder: string = path.relative(commonTempFolder, rushJsonFolder); // Loop through the projects and add them to the workspace file. While we're at it, also validate that // referenced workspace projects are valid, and check if the shrinkwrap file is already up-to-date. for (const rushProject of this.rushConfiguration.projects) { + if (!subspace.contains(rushProject)) { + // skip processing any project that isn't in this subspace + continue; + } const packageJson: PackageJsonEditor = rushProject.packageJsonEditor; workspaceFile.addPackage(rushProject.projectFolder); @@ -143,12 +227,22 @@ export class WorkspaceInstallManager extends BaseInstallManager { continue; } - const dependencySpecifier: DependencySpecifier = new DependencySpecifier(name, version); + const dependencySpecifier: DependencySpecifier = DependencySpecifier.parseWithCache(name, version); // Is there a locally built Rush project that could satisfy this dependency? - const referencedLocalProject: RushConfigurationProject | undefined = + let referencedLocalProject: RushConfigurationProject | undefined = this.rushConfiguration.getProjectByName(name); + // If we enable exemptDecoupledDependenciesBetweenSubspaces, it will only check dependencies within the subspace. + if ( + this.rushConfiguration.experimentsConfiguration.configuration + .exemptDecoupledDependenciesBetweenSubspaces + ) { + if (referencedLocalProject && !subspace.contains(referencedLocalProject)) { + referencedLocalProject = undefined; + } + } + // Validate that local projects are referenced with workspace notation. If not, and it is not a // cyclic dependency, then it needs to be updated to specify `workspace:*` explicitly. Currently only // supporting versions and version ranges for specifying a local project. @@ -166,29 +260,36 @@ export class WorkspaceInstallManager extends BaseInstallManager { dependencySpecifier.versionSpecifier ) ) { + // eslint-disable-next-line no-console console.log(); + // eslint-disable-next-line no-console console.log( - colors.red( - `"${rushProject.packageName}" depends on package "${name}" (${version}) which exists ` + - 'within the workspace but cannot be fulfilled with the specified version range. Either ' + - 'specify a valid version range, or add the package as a cyclic dependency.' + Colorize.red( + `"${rushProject.packageName}" depends on package "${name}" (${version}) which belongs to ` + + 'the workspace but cannot be fulfilled with the specified version range. Either ' + + 'specify a valid version range, or add the package to "decoupledLocalDependencies" in rush.json.' ) ); throw new AlreadyReportedError(); } - if (!this.options.allowShrinkwrapUpdates) { + if (!allowShrinkwrapUpdates) { + // eslint-disable-next-line no-console console.log(); + // eslint-disable-next-line no-console console.log( - colors.red( + Colorize.red( `"${rushProject.packageName}" depends on package "${name}" (${version}) which exists within ` + - 'the workspace. Run "rush update" to update workspace references for this package.' + 'the workspace. Run "rush update" to update workspace references for this package. ' + + `If package "${name}" is intentionally expected to be installed from an external package feed, ` + + `list package "${name}" in the "decoupledLocalDependencies" field in the ` + + `"${rushProject.packageName}" entry in rush.json to suppress this error.` ) ); throw new AlreadyReportedError(); } - if (this.options.fullUpgrade) { + if (fullUpgrade) { // We will update to `workspace` notation. If the version specified is a range, then use the provided range. // Otherwise, use `workspace:*` to ensure we're always using the workspace package. const workspaceRange: string = @@ -200,16 +301,31 @@ export class WorkspaceInstallManager extends BaseInstallManager { shrinkwrapIsUpToDate = false; continue; } - } else if (dependencySpecifier.specifierType === DependencySpecifierType.Workspace) { + } else if ( + dependencySpecifier.specifierType === DependencySpecifierType.Workspace && + rushProject.decoupledLocalDependencies.has(name) + ) { + // If the dependency is a local project that is decoupled, then we need to ensure that it is not specified + // as a workspace project. If it is, then we need to update the package.json to remove the workspace notation. + this._terminal.writeWarningLine( + `"${rushProject.packageName}" depends on package ${name}@${version}, but also lists it in ` + + `its "decoupledLocalDependencies" array. Either update the host project's package.json to use ` + + `a version from an external feed instead of "workspace:" notation, or remove the dependency from the ` + + `host project's "decoupledLocalDependencies" array in rush.json.` + ); + throw new AlreadyReportedError(); + } else if (!rushProject.decoupledLocalDependencies.has(name)) { // Already specified as a local project. Allow the package manager to validate this continue; } } // Save the package.json if we modified the version references and warn that the package.json was modified - if (packageJson.saveIfModified()) { + const modified: boolean = await packageJson.saveIfModifiedAsync(); + if (modified) { + // eslint-disable-next-line no-console console.log( - colors.yellow( + Colorize.yellow( `"${rushProject.packageName}" depends on one or more workspace packages which did not use "workspace:" ` + 'notation. The package.json has been modified and must be committed to source control.' ) @@ -217,37 +333,139 @@ export class WorkspaceInstallManager extends BaseInstallManager { } // Now validate that the shrinkwrap file matches what is in the package.json - if (await shrinkwrapFile?.isWorkspaceProjectModifiedAsync(rushProject, this.options.variant)) { + if (await shrinkwrapFile?.isWorkspaceProjectModifiedAsync(rushProject, subspace, variant)) { shrinkwrapWarnings.push( `Dependencies of project "${rushProject.packageName}" do not match the current shrinkwrap.` ); shrinkwrapIsUpToDate = false; } + + const dependencyMetaList: ReadonlyArray = packageJson.dependencyMetaList; + if (dependencyMetaList.length !== 0) { + const dependenciesMeta: IDependenciesMetaTable = {}; + for (const dependencyMeta of dependencyMetaList) { + dependenciesMeta[dependencyMeta.name] = { + injected: dependencyMeta.injected + }; + } + + // get the relative path from common temp folder to package folder, to align with the value in pnpm-lock.yaml + const relativePathFromTempFolderToPackageFolder: string = Path.convertToSlashes( + `${relativeFromTempFolderToRootFolder}/${rushProject.projectRelativeFolder}` + ); + expectedDependenciesMetaByProjectRelativePath[relativePathFromTempFolderToPackageFolder] = + dependenciesMeta; + } + } + + // Build a object for dependenciesMeta settings in pnpm-lock.yaml + // key is the package path, value is the dependenciesMeta info for that package + const lockfileDependenciesMetaByProjectRelativePath: { [key: string]: IDependenciesMetaTable } = {}; + if (shrinkwrapFile?.importers !== undefined) { + for (const [key, value] of shrinkwrapFile?.importers) { + const projectRelativePath: string = Path.convertToSlashes(key); + + // we only need to verify packages that exist in package.json and pnpm-lock.yaml + // PNPM won't actively remove deleted packages in importers, unless it has to + // so it is possible that a deleted package still showing in pnpm-lock.yaml + if (expectedDependenciesMetaByProjectRelativePath[projectRelativePath] === undefined) { + continue; + } + if (value.dependenciesMeta !== undefined) { + lockfileDependenciesMetaByProjectRelativePath[projectRelativePath] = value.dependenciesMeta; + } + } + } + + // Now, we compare these two objects to see if they are equal or not + const dependenciesMetaAreEqual: boolean = Objects.areDeepEqual( + expectedDependenciesMetaByProjectRelativePath, + lockfileDependenciesMetaByProjectRelativePath + ); + + if (!dependenciesMetaAreEqual) { + shrinkwrapWarnings.push( + "The dependenciesMeta settings in one or more package.json don't match the current shrinkwrap." + ); + shrinkwrapIsUpToDate = false; + } + + // Check if the configured overrides match the shrinkwrap + const overridesAreEqual: boolean = Objects.areDeepEqual>( + configuredOverrides, + shrinkwrapFile?.overrides ? Object.fromEntries(shrinkwrapFile?.overrides) : {} + ); + + if (!overridesAreEqual) { + shrinkwrapWarnings.push("The overrides settings doesn't match the current shrinkwrap."); + shrinkwrapIsUpToDate = false; + } + + // Check if packageExtensionsChecksum matches the configured packageExtensions' hash + let packageExtensionsChecksum: string | undefined; + let existingPackageExtensionsChecksum: string | undefined; + if (shrinkwrapFile) { + existingPackageExtensionsChecksum = shrinkwrapFile.packageExtensionsChecksum; + let packageExtensionsChecksumAlgorithm: string | undefined; + if (existingPackageExtensionsChecksum) { + const dashIndex: number = existingPackageExtensionsChecksum.indexOf('-'); + if (dashIndex !== -1) { + packageExtensionsChecksumAlgorithm = existingPackageExtensionsChecksum.substring(0, dashIndex); + } + + if (packageExtensionsChecksumAlgorithm && packageExtensionsChecksumAlgorithm !== 'sha256') { + this._terminal.writeErrorLine( + `The existing packageExtensionsChecksum algorithm "${packageExtensionsChecksumAlgorithm}" is not supported. ` + + `This may indicate that the shrinkwrap was created with a newer version of PNPM than Rush supports.` + ); + throw new AlreadyReportedError(); + } + } + + // https://github.com/pnpm/pnpm/blob/ba9409ffcef0c36dc1b167d770a023c87444822d/pkg-manager/core/src/install/index.ts#L331 + if (configuredPackageExtensions && Object.keys(configuredPackageExtensions).length !== 0) { + if (packageExtensionsChecksumAlgorithm) { + // In PNPM v10, the algorithm changed to SHA256 and the digest changed from hex to base64 + packageExtensionsChecksum = await createObjectChecksumAsync(configuredPackageExtensions); + } else { + packageExtensionsChecksum = createObjectChecksumLegacy(configuredPackageExtensions); + } + } + } + + const packageExtensionsChecksumAreEqual: boolean = + packageExtensionsChecksum === existingPackageExtensionsChecksum; + + if (!packageExtensionsChecksumAreEqual) { + shrinkwrapWarnings.push("The package extension hash doesn't match the current shrinkwrap."); + shrinkwrapIsUpToDate = false; } - // Write the common package.json - InstallHelpers.generateCommonPackageJson(this.rushConfiguration); + // Write the common package.json using the "pnpm" field derived above. + await InstallHelpers.generateCommonPackageJsonAsync(subspace, undefined, pnpmSettings); - // Save the generated workspace file. Don't update the file timestamp unless the content has changed, - // since "rush install" will consider this timestamp - workspaceFile.save(workspaceFile.workspaceFilename, { onlyIfChanged: true }); + // The pnpm-workspace.yaml settings were already populated by resolvePnpmSettings, and the + // workspace packages were added in the loop above. Save the generated workspace file. Don't + // update the file timestamp unless the content has changed, since "rush install" considers it. + await workspaceFile.saveAsync(workspaceFile.workspaceFilename, { onlyIfChanged: true }); return { shrinkwrapIsUpToDate, shrinkwrapWarnings }; } - protected canSkipInstall(lastModifiedDate: Date): boolean { - if (!super.canSkipInstall(lastModifiedDate)) { + protected override async canSkipInstallAsync( + lastModifiedDate: Date, + subspace: Subspace, + variant: string | undefined + ): Promise { + if (!(await super.canSkipInstallAsync(lastModifiedDate, subspace, variant))) { return false; } const potentiallyChangedFiles: string[] = []; - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { // Add workspace file. This file is only modified when workspace packages change. - const pnpmWorkspaceFilename: string = path.join( - this.rushConfiguration.commonTempFolder, - 'pnpm-workspace.yaml' - ); + const pnpmWorkspaceFilename: string = `${subspace.getSubspaceTempFolderPath()}/${RushConstants.pnpmWorkspaceFileName}`; if (FileSystem.exists(pnpmWorkspaceFilename)) { potentiallyChangedFiles.push(pnpmWorkspaceFilename); @@ -257,24 +475,20 @@ export class WorkspaceInstallManager extends BaseInstallManager { // Also consider timestamps for all the project node_modules folders, as well as the package.json // files // Example: [ "C:\MyRepo\projects\projectA\node_modules", "C:\MyRepo\projects\projectA\package.json" ] - potentiallyChangedFiles.push( - ...this.rushConfiguration.projects.map((project) => { - return path.join(project.projectFolder, RushConstants.nodeModulesFolderName); - }), - ...this.rushConfiguration.projects.map((project) => { - return path.join(project.projectFolder, FileConstants.PackageJson); - }) - ); + for (const { projectFolder } of subspace.getProjects()) { + potentiallyChangedFiles.push(`${projectFolder}/${RushConstants.nodeModulesFolderName}`); + potentiallyChangedFiles.push(`${projectFolder}/${FileConstants.PackageJson}`); + } // NOTE: If any of the potentiallyChangedFiles does not exist, then isFileTimestampCurrent() // returns false. - return Utilities.isFileTimestampCurrent(lastModifiedDate, potentiallyChangedFiles); + return Utilities.isFileTimestampCurrentAsync(lastModifiedDate, potentiallyChangedFiles); } /** - * Runs "npm install" in the common folder. + * Runs "pnpm install" in the common folder. */ - protected async installAsync(cleanInstall: boolean): Promise { + protected async installAsync(cleanInstall: boolean, subspace: Subspace): Promise { // Example: "C:\MyRepo\common\temp\npm-local\node_modules\.bin\npm" const packageManagerFilename: string = this.rushConfiguration.packageManagerToolFilename; @@ -282,11 +496,11 @@ export class WorkspaceInstallManager extends BaseInstallManager { this.rushConfiguration, this.options ); + if (ConsoleTerminalProvider.supportsColor) { + packageManagerEnv.FORCE_COLOR = '1'; + } - const commonNodeModulesFolder: string = path.join( - this.rushConfiguration.commonTempFolder, - RushConstants.nodeModulesFolderName - ); + const commonNodeModulesFolder: string = `${subspace.getSubspaceTempFolderPath()}/${RushConstants.nodeModulesFolderName}`; // Is there an existing "node_modules" folder to consider? if (FileSystem.exists(commonNodeModulesFolder)) { @@ -295,6 +509,7 @@ export class WorkspaceInstallManager extends BaseInstallManager { // YES: Delete "node_modules" // Explain to the user why we are hosing their node_modules folder + // eslint-disable-next-line no-console console.log('Deleting files from ' + commonNodeModulesFolder); this.installRecycler.moveFolder(commonNodeModulesFolder); @@ -303,62 +518,140 @@ export class WorkspaceInstallManager extends BaseInstallManager { } } - // Run "npm install" in the common folder - const installArgs: string[] = ['install']; - this.pushConfigurationArgs(installArgs, this.options); - - console.log( - '\n' + - colors.bold( - `Running "${this.rushConfiguration.packageManager} install" in` + - ` ${this.rushConfiguration.commonTempFolder}` - ) + - '\n' - ); + const doInstallInternalAsync = async (options: IInstallManagerOptions): Promise => { + // Run "npm install" in the common folder + // To ensure that the output is always colored, set the option "--color=always", even when it's piped. + // Without this argument, certain text that should be colored (such as red) will appear white. + const installArgs: string[] = ['install']; + this.pushConfigurationArgs(installArgs, options, subspace); - // If any diagnostic options were specified, then show the full command-line - if (this.options.debug || this.options.collectLogFile || this.options.networkConcurrency) { + // eslint-disable-next-line no-console console.log( '\n' + - colors.green('Invoking package manager: ') + - FileSystem.getRealPath(packageManagerFilename) + - ' ' + - installArgs.join(' ') + + Colorize.bold( + `Running "${this.rushConfiguration.packageManager} install" in` + + ` ${subspace.getSubspaceTempFolderPath()}` + ) + '\n' ); - } - Utilities.executeCommandWithRetry( - { - command: packageManagerFilename, - args: installArgs, - workingDirectory: this.rushConfiguration.commonTempFolder, - environment: packageManagerEnv, - suppressOutput: false - }, - this.options.maxInstallAttempts, - () => { - if (this.rushConfiguration.packageManager === 'pnpm') { - console.log(colors.yellow(`Deleting the "node_modules" folder`)); - this.installRecycler.moveFolder(commonNodeModulesFolder); - - // Leave the pnpm-store as is for the retry. This ensures that packages that have already - // been downloaded need not be downloaded again, thereby potentially increasing the chances - // of a subsequent successful install. - - Utilities.createFolderWithRetry(commonNodeModulesFolder); + // If any diagnostic options were specified, then show the full command-line + if ( + this.options.debug || + this.options.collectLogFile || + this.options.networkConcurrency || + this.options.onlyShrinkwrap + ) { + // eslint-disable-next-line no-console + console.log( + '\n' + + Colorize.green('Invoking package manager: ') + + FileSystem.getRealPath(packageManagerFilename) + + ' ' + + installArgs.join(' ') + + '\n' + ); + } + + // Store the tip IDs that should be printed. + // They will be printed all at once *after* the install + const tipIDsToBePrinted: Set = new Set(); + const pnpmTips: ICustomTipInfo[] = []; + for (const [customTipId, customTip] of Object.entries(PNPM_CUSTOM_TIPS)) { + if ( + this.rushConfiguration.customTipsConfiguration.providedCustomTipsByTipId.has( + customTipId as CustomTipId + ) + ) { + pnpmTips.push(customTip); + } + } + + const onPnpmStdoutChunk: ((chunk: string) => string | void) | undefined = ( + chunk: string + ): string | void => { + // Iterate over the supported custom tip metadata and try to match the chunk. + if (pnpmTips.length > 0) { + for (const { isMatch, tipId } of pnpmTips) { + if (isMatch?.(chunk)) { + tipIDsToBePrinted.add(tipId); + } + } + } + + // Replace `pnpm approve-builds` with `rush-pnpm approve-builds` when running + // `rush install` or `rush update` to instruct users to use the correct command + const modifiedChunk: string = chunk.replace( + /pnpm approve-builds/g, + `rush-pnpm --subspace ${subspace.subspaceName} approve-builds` + ); + + // Return modified chunk if it was changed, otherwise return void to keep original + return modifiedChunk !== chunk ? modifiedChunk : undefined; + }; + try { + await Utilities.executeCommandWithRetryAsync( + { + command: packageManagerFilename, + args: installArgs, + workingDirectory: subspace.getSubspaceTempFolderPath(), + environment: packageManagerEnv, + suppressOutput: false, + onStdoutStreamChunk: onPnpmStdoutChunk + }, + this.options.maxInstallAttempts, + () => { + if (this.rushConfiguration.isPnpm) { + this._terminal.writeWarningLine(`Deleting the "node_modules" folder`); + this.installRecycler.moveFolder(commonNodeModulesFolder); + + // Leave the pnpm-store as is for the retry. This ensures that packages that have already + // been downloaded need not be downloaded again, thereby potentially increasing the chances + // of a subsequent successful install. + + Utilities.createFolderWithRetry(commonNodeModulesFolder); + } + } + ); + } finally { + // The try-finally is to avoid the tips NOT being printed if the install fails. + // NOT catching the error because we want to keep the other behaviors (i.e., the error will be caught and handle in upper layers). + + if (tipIDsToBePrinted.size > 0) { + this._terminal.writeLine(); + for (const tipID of tipIDsToBePrinted) { + this.rushConfiguration.customTipsConfiguration._showTip(this._terminal, tipID); + } } } - ); + }; + + const { configuration: experiments } = this.rushConfiguration.experimentsConfiguration; + if ( + this.options.allowShrinkwrapUpdates && + experiments.usePnpmLockfileOnlyThenFrozenLockfileForRushUpdate + ) { + await doInstallInternalAsync({ + ...this.options, + onlyShrinkwrap: true + }); + + await doInstallInternalAsync({ + ...this.options, + allowShrinkwrapUpdates: false + }); + } else { + await doInstallInternalAsync(this.options); + } // If all attempts fail we just terminate. No special handling needed. // Ensure that node_modules folders exist after install, since the timestamps on these folders are used // to determine if the install can be skipped const projectNodeModulesFolders: string[] = [ - path.join(this.rushConfiguration.commonTempFolder, RushConstants.nodeModulesFolderName), + `${subspace.getSubspaceTempFolderPath()}/${RushConstants.nodeModulesFolderName}`, ...this.rushConfiguration.projects.map((project) => { - return path.join(project.projectFolder, RushConstants.nodeModulesFolderName); + return `${project.projectFolder}/${RushConstants.nodeModulesFolderName}`; }) ]; @@ -366,37 +659,35 @@ export class WorkspaceInstallManager extends BaseInstallManager { FileSystem.ensureFolder(nodeModulesFolder); } + // eslint-disable-next-line no-console console.log(''); } - protected async postInstallAsync(): Promise { + protected async postInstallAsync(subspace: Subspace): Promise { // Grab the temp shrinkwrap, as this was the most recently completed install. It may also be // more up-to-date than the checked-in shrinkwrap since filtered installs are not written back. // Note that if there are no projects, or if we're in PNPM workspace mode and there are no // projects with dependencies, a lockfile won't be generated. - const tempShrinkwrapFile: BaseShrinkwrapFile | undefined = ShrinkwrapFileFactory.getShrinkwrapFile( - this.rushConfiguration.packageManager, - this.rushConfiguration.pnpmOptions, - this.rushConfiguration.tempShrinkwrapFilename - ); + const tempShrinkwrapFile: BaseShrinkwrapFile | undefined = ShrinkwrapFileFactory.getShrinkwrapFile({ + packageManager: this.rushConfiguration.packageManager, + shrinkwrapFilePath: subspace.getTempShrinkwrapFilename(), + subspaceHasNoProjects: subspace.getProjects().length === 0 + }); if (tempShrinkwrapFile) { // Write or delete all project shrinkwraps related to the install await Async.forEachAsync( - this.rushConfiguration.projects, + subspace.getProjects(), async (project) => { await tempShrinkwrapFile.getProjectShrinkwrap(project)?.updateProjectShrinkwrapAsync(); }, { concurrency: 10 } ); - } else if ( - this.rushConfiguration.packageManager === 'pnpm' && - this.rushConfiguration.pnpmOptions?.useWorkspaces - ) { + } else if (this.rushConfiguration.isPnpm && this.rushConfiguration.pnpmOptions?.useWorkspaces) { // If we're in PNPM workspace mode and PNPM didn't create a shrinkwrap file, // there are no dependencies. Generate empty shrinkwrap files for all projects. await Async.forEachAsync( - this.rushConfiguration.projects, + subspace.getProjects(), async (project) => { await BaseProjectShrinkwrapFile.saveEmptyProjectShrinkwrapFileAsync(project); }, @@ -410,25 +701,155 @@ export class WorkspaceInstallManager extends BaseInstallManager { ); } + // If the splitWorkspaceCompatibility is enabled for subspaces, create symlinks to mimic the behaviour + // of having the node_modules folder created directly in the project folder. This requires symlinking two categories: + // 1) Symlink any packages that are declared to be publicly hoisted, such as by using public-hoist-pattern in .npmrc. + // This creates a symlink from /node_modules/ -> temp//node_modules/ + // 2) Symlink any workspace packages that are declared in the temp folder, as some packages may expect these packages to exist + // in the node_modules folder. + // This creates a symlink from temp//node_modules/ -> + if ( + this.rushConfiguration.subspacesFeatureEnabled && + this.rushConfiguration.subspacesConfiguration?.splitWorkspaceCompatibility + ) { + const tempNodeModulesPath: string = `${subspace.getSubspaceTempFolderPath()}/node_modules`; + const modulesFilePath: string = `${tempNodeModulesPath}/${RushConstants.pnpmModulesFilename}`; + if ( + subspace.subspaceName.startsWith('split_') && + subspace.getProjects().length === 1 && + (await FileSystem.existsAsync(modulesFilePath)) + ) { + // Find the .modules.yaml file in the subspace temp/node_modules folder + const modulesContent: string = await FileSystem.readFileAsync(modulesFilePath); + const yaml: typeof import('js-yaml') = await import('js-yaml'); + const yamlContent: IPnpmModules = yaml.load(modulesContent, { + filename: modulesFilePath + }) as IPnpmModules; + const { hoistedDependencies } = yamlContent; + const subspaceProject: RushConfigurationProject = subspace.getProjects()[0]; + const projectNodeModulesPath: string = `${subspaceProject.projectFolder}/node_modules`; + for (const value of Object.values(hoistedDependencies)) { + for (const [filePath, type] of Object.entries(value)) { + if (type === 'public') { + if (Utilities.existsOrIsSymlink(`${projectNodeModulesPath}/${filePath}`)) { + await FileSystem.deleteFolderAsync(`${projectNodeModulesPath}/${filePath}`); + } + // If we don't already have a symlink for this package, create one + const parentDir: string = Utilities.trimAfterLastSlash(`${projectNodeModulesPath}/${filePath}`); + await FileSystem.ensureFolderAsync(parentDir); + await BaseLinkManager._createSymlinkAsync({ + linkTargetPath: `${tempNodeModulesPath}/${filePath}`, + newLinkPath: `${projectNodeModulesPath}/${filePath}`, + symlinkKind: SymlinkKind.Directory + }); + } + } + } + } + + // Look for any workspace linked packages anywhere in this subspace, symlink them from the temp node_modules folder. + const subspaceDependencyProjects: Set = new Set(); + for (const subspaceProject of subspace.getProjects()) { + for (const dependencyProject of subspaceProject.dependencyProjects) { + subspaceDependencyProjects.add(dependencyProject); + } + } + for (const dependencyProject of subspaceDependencyProjects) { + const symlinkToCreate: string = `${tempNodeModulesPath}/${dependencyProject.packageName}`; + if (!Utilities.existsOrIsSymlink(symlinkToCreate)) { + const parentFolder: string = Utilities.trimAfterLastSlash(symlinkToCreate); + await FileSystem.ensureFolderAsync(parentFolder); + await BaseLinkManager._createSymlinkAsync({ + linkTargetPath: dependencyProject.projectFolder, + newLinkPath: symlinkToCreate, + symlinkKind: SymlinkKind.Directory + }); + } + } + } // TODO: Remove when "rush link" and "rush unlink" are deprecated - LastLinkFlagFactory.getCommonTempFlag(this.rushConfiguration).create(); + await new FlagFile( + subspace.getSubspaceTempFolderPath(), + RushConstants.lastLinkFlagFilename, + {} + ).createAsync(); } /** * Used when invoking the NPM tool. Appends the common configuration options * to the command-line. */ - protected pushConfigurationArgs(args: string[], options: IInstallManagerOptions): void { - super.pushConfigurationArgs(args, options); + protected override pushConfigurationArgs( + args: string[], + options: IInstallManagerOptions, + subspace: Subspace + ): void { + super.pushConfigurationArgs(args, options, subspace); // Add workspace-specific args - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { args.push('--recursive'); args.push('--link-workspace-packages', 'false'); - for (const arg of this.options.pnpmFilterArguments) { - args.push(arg); + if (process.stdout.isTTY) { + // If we're on a TTY console and something else didn't set a `--reporter` parameter, + // explicitly set the default reporter. This fixes an issue where, when the pnpm + // output is being monitored to match custom tips, pnpm will detect a non-TTY + // stdout stream and use the `append-only` reporter. + // + // See docs here: https://pnpm.io/cli/install#--reportername + let includesReporterArg: boolean = false; + for (const arg of args) { + if (arg.startsWith('--reporter')) { + includesReporterArg = true; + break; + } + } + + if (!includesReporterArg) { + args.push('--reporter', 'default'); + } + } + + for (const arg of this.options.pnpmFilterArgumentValues) { + args.push('--filter', arg); } } } } + +/** + * Source: https://github.com/pnpm/pnpm/blob/ba9409ffcef0c36dc1b167d770a023c87444822d/pkg-manager/core/src/install/index.ts#L821-L824 + */ +function createObjectChecksumLegacy(obj: Record): string { + const s: string = JSON.stringify(Sort.sortKeys(obj)); + return createHash('md5').update(s).digest('hex'); +} + +/** + * Source: https://github.com/pnpm/pnpm/blob/bdbd31aa4fa6546d65b6eee50a79b51879340d40/crypto/object-hasher/src/index.ts#L8-L12 + */ +const defaultOptions: import('object-hash').NormalOption = { + respectType: false, + algorithm: 'sha256', + encoding: 'base64' +}; + +/** + * https://github.com/pnpm/pnpm/blob/bdbd31aa4fa6546d65b6eee50a79b51879340d40/crypto/object-hasher/src/index.ts#L21-L26 + */ +const withSortingOptions: import('object-hash').NormalOption = { + ...defaultOptions, + unorderedArrays: true, + unorderedObjects: true, + unorderedSets: true +}; + +/** + * Source: https://github.com/pnpm/pnpm/blob/bdbd31aa4fa6546d65b6eee50a79b51879340d40/crypto/object-hasher/src/index.ts#L45-L49 + */ +async function createObjectChecksumAsync(obj: Record): Promise { + const { default: hash } = await import('object-hash'); + const packageExtensionsChecksum: string = hash(obj, withSortingOptions); + return `${defaultOptions.algorithm}-${packageExtensionsChecksum}`; +} diff --git a/libraries/rush-lib/src/logic/installManager/doBasicInstallAsync.ts b/libraries/rush-lib/src/logic/installManager/doBasicInstallAsync.ts index 62ef369f9c1..05d3aa49c89 100644 --- a/libraries/rush-lib/src/logic/installManager/doBasicInstallAsync.ts +++ b/libraries/rush-lib/src/logic/installManager/doBasicInstallAsync.ts @@ -1,24 +1,45 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import type { ITerminal } from '@rushstack/terminal'; + import type { RushConfiguration } from '../../api/RushConfiguration'; import type { RushGlobalFolder } from '../../api/RushGlobalFolder'; import type { BaseInstallManager } from '../base/BaseInstallManager'; +import type { IInstallManagerOptions } from '../base/BaseInstallManagerTypes'; import { InstallManagerFactory } from '../InstallManagerFactory'; import { SetupChecks } from '../SetupChecks'; import { PurgeManager } from '../PurgeManager'; import { VersionMismatchFinder } from '../versionMismatch/VersionMismatchFinder'; +import type { Subspace } from '../../api/Subspace'; export interface IRunInstallOptions { + afterInstallAsync?: IInstallManagerOptions['afterInstallAsync']; + beforeInstallAsync?: IInstallManagerOptions['beforeInstallAsync']; rushConfiguration: RushConfiguration; rushGlobalFolder: RushGlobalFolder; isDebug: boolean; + terminal: ITerminal; + variant: string | undefined; + subspace: Subspace; } export async function doBasicInstallAsync(options: IRunInstallOptions): Promise { - const { rushConfiguration, rushGlobalFolder, isDebug } = options; + const { + rushConfiguration, + rushGlobalFolder, + isDebug, + variant, + terminal, + beforeInstallAsync, + afterInstallAsync, + subspace + } = options; - VersionMismatchFinder.ensureConsistentVersions(rushConfiguration); + VersionMismatchFinder.ensureConsistentVersions(rushConfiguration, terminal, { + variant, + subspace + }); SetupChecks.validate(rushConfiguration); const purgeManager: typeof PurgeManager.prototype = new PurgeManager(rushConfiguration, rushGlobalFolder); @@ -35,16 +56,23 @@ export async function doBasicInstallAsync(options: IRunInstallOptions): Promise< noLink: false, fullUpgrade: false, recheckShrinkwrap: false, + offline: false, collectLogFile: false, - pnpmFilterArguments: [], + pnpmFilterArgumentValues: [], + selectedProjects: new Set(rushConfiguration.projects), maxInstallAttempts: 1, - networkConcurrency: undefined + networkConcurrency: undefined, + subspace, + terminal, + variant, + afterInstallAsync, + beforeInstallAsync } ); try { await installManager.doInstallAsync(); } finally { - purgeManager.deleteAll(); + await purgeManager.startDeleteAllAsync(); } } diff --git a/libraries/rush-lib/src/logic/npm/NpmLinkManager.ts b/libraries/rush-lib/src/logic/npm/NpmLinkManager.ts index c59c7228ce0..97e4098d7ef 100644 --- a/libraries/rush-lib/src/logic/npm/NpmLinkManager.ts +++ b/libraries/rush-lib/src/logic/npm/NpmLinkManager.ts @@ -1,17 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as path from 'path'; +import * as path from 'node:path'; + import * as semver from 'semver'; import * as tar from 'tar'; import readPackageTree from 'read-package-tree'; + import { FileSystem, FileConstants, LegacyAdapters } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; -import { RushConstants } from '../../logic/RushConstants'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import { RushConstants } from '../RushConstants'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import { Utilities } from '../../utilities/Utilities'; -import { NpmPackage, IResolveOrCreateResult, PackageDependencyKind } from './NpmPackage'; +import { NpmPackage, type IResolveOrCreateResult, PackageDependencyKind } from './NpmPackage'; import { PackageLookup } from '../PackageLookup'; import { BaseLinkManager, SymlinkKind } from '../base/BaseLinkManager'; @@ -28,7 +30,7 @@ interface IQueueItem { } export class NpmLinkManager extends BaseLinkManager { - protected async _linkProjects(): Promise { + protected async _linkProjectsAsync(): Promise { const npmPackage: readPackageTree.Node = await LegacyAdapters.convertCallbackToPromise< readPackageTree.Node, Error, @@ -41,8 +43,9 @@ export class NpmLinkManager extends BaseLinkManager { commonPackageLookup.loadTree(commonRootPackage); for (const rushProject of this._rushConfiguration.projects) { + // eslint-disable-next-line no-console console.log(`\nLINKING: ${rushProject.packageName}`); - this._linkProject(rushProject, commonRootPackage, commonPackageLookup); + await this._linkProjectAsync(rushProject, commonRootPackage, commonPackageLookup); } } @@ -52,11 +55,11 @@ export class NpmLinkManager extends BaseLinkManager { * @param commonRootPackage The common/temp/package.json package * @param commonPackageLookup A dictionary for finding packages under common/temp/node_modules */ - private _linkProject( + private async _linkProjectAsync( project: RushConfigurationProject, commonRootPackage: NpmPackage, commonPackageLookup: PackageLookup - ): void { + ): Promise { let commonProjectPackage: NpmPackage | undefined = commonRootPackage.getChildByName( project.tempProjectName ) as NpmPackage; @@ -177,8 +180,9 @@ export class NpmLinkManager extends BaseLinkManager { // immediate dependencies of top-level projects, indicated by PackageDependencyKind.LocalLink. // Is this wise?) + // eslint-disable-next-line no-console console.log( - colors.yellow( + Colorize.yellow( `Rush will not locally link ${dependency.name} for ${localPackage.name}` + ` because the requested version "${dependency.versionRange}" is incompatible` + ` with the local version ${matchedVersion}` @@ -287,6 +291,7 @@ export class NpmLinkManager extends BaseLinkManager { ` was not found in the common folder -- do you need to run "rush install"?` ); } else { + // eslint-disable-next-line no-console console.log('Skipping optional dependency: ' + dependency.name); } } @@ -297,7 +302,7 @@ export class NpmLinkManager extends BaseLinkManager { // to the console: // localProjectPackage.printTree(); - NpmLinkManager._createSymlinksForTopLevelProject(localProjectPackage); + await NpmLinkManager._createSymlinksForTopLevelProjectAsync(localProjectPackage); // Also symlink the ".bin" folder if (localProjectPackage.children.length > 0) { @@ -308,8 +313,9 @@ export class NpmLinkManager extends BaseLinkManager { ); const projectBinFolder: string = path.join(localProjectPackage.folderPath, 'node_modules', '.bin'); - if (FileSystem.exists(commonBinFolder)) { - NpmLinkManager._createSymlink({ + const commonBinFolderExists: boolean = await FileSystem.existsAsync(commonBinFolder); + if (commonBinFolderExists) { + await NpmLinkManager._createSymlinkAsync({ linkTargetPath: commonBinFolder, newLinkPath: projectBinFolder, symlinkKind: SymlinkKind.Directory diff --git a/libraries/rush-lib/src/logic/npm/NpmOptionsConfiguration.ts b/libraries/rush-lib/src/logic/npm/NpmOptionsConfiguration.ts index ce226c1903b..b25db65c2ad 100644 --- a/libraries/rush-lib/src/logic/npm/NpmOptionsConfiguration.ts +++ b/libraries/rush-lib/src/logic/npm/NpmOptionsConfiguration.ts @@ -2,7 +2,7 @@ // See LICENSE in the project root for license information. import { - IPackageManagerOptionsJsonBase, + type IPackageManagerOptionsJsonBase, PackageManagerOptionsConfigurationBase } from '../base/BasePackageManagerOptionsConfiguration'; diff --git a/libraries/rush-lib/src/logic/npm/NpmPackage.ts b/libraries/rush-lib/src/logic/npm/NpmPackage.ts index 826d795fe67..6afb3cb1b4b 100644 --- a/libraries/rush-lib/src/logic/npm/NpmPackage.ts +++ b/libraries/rush-lib/src/logic/npm/NpmPackage.ts @@ -1,11 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import readPackageTree from 'read-package-tree'; -import { JsonFile, IPackageJson } from '@rushstack/node-core-library'; +import * as path from 'node:path'; -import { BasePackage, IRushTempPackageJson } from '../base/BasePackage'; +import type readPackageTree from 'read-package-tree'; + +import { JsonFile, type IPackageJson } from '@rushstack/node-core-library'; + +import { BasePackage, type IRushTempPackageJson } from '../base/BasePackage'; /** * Used by the linking algorithm when doing NPM package resolution. @@ -89,7 +91,10 @@ export class NpmPackage extends BasePackage { * @param targetFolderName - Filename where it should have been installed * Example: `C:\MyRepo\common\temp\node_modules\@rush-temp\project1` */ - public static createVirtualTempPackage(packageJsonFilename: string, installFolderName: string): NpmPackage { + public static override createVirtualTempPackage( + packageJsonFilename: string, + installFolderName: string + ): NpmPackage { const packageJson: IPackageJson = JsonFile.load(packageJsonFilename); const npmPackage: readPackageTree.Node = { children: [], diff --git a/libraries/rush-lib/src/logic/npm/NpmShrinkwrapFile.ts b/libraries/rush-lib/src/logic/npm/NpmShrinkwrapFile.ts index 35d2df9cc32..b0880d98b7b 100644 --- a/libraries/rush-lib/src/logic/npm/NpmShrinkwrapFile.ts +++ b/libraries/rush-lib/src/logic/npm/NpmShrinkwrapFile.ts @@ -5,8 +5,9 @@ import { JsonFile, FileSystem, InternalError } from '@rushstack/node-core-librar import { BaseShrinkwrapFile } from '../base/BaseShrinkwrapFile'; import { DependencySpecifier } from '../DependencySpecifier'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; -import { BaseProjectShrinkwrapFile } from '../base/BaseProjectShrinkwrapFile'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { BaseProjectShrinkwrapFile } from '../base/BaseProjectShrinkwrapFile'; +import type { Subspace } from '../../api/Subspace'; interface INpmShrinkwrapDependencyJson { version: string; @@ -66,18 +67,15 @@ export class NpmShrinkwrapFile extends BaseShrinkwrapFile { return new NpmShrinkwrapFile(JSON.parse(data)); } - /** @override */ - public getTempProjectNames(): ReadonlyArray { + public override getTempProjectNames(): ReadonlyArray { return this._getTempProjectNames(this._shrinkwrapJson.dependencies); } - /** @override */ - protected serialize(): string { + protected override serialize(): string { return JsonFile.stringify(this._shrinkwrapJson); } - /** @override */ - protected getTopLevelDependencyVersion(dependencyName: string): DependencySpecifier | undefined { + protected override getTopLevelDependencyVersion(dependencyName: string): DependencySpecifier | undefined { // First, check under tempProjectName, as this is the first place we look during linking. const dependencyJson: INpmShrinkwrapDependencyJson | undefined = NpmShrinkwrapFile.tryGetValue( this._shrinkwrapJson.dependencies, @@ -88,16 +86,15 @@ export class NpmShrinkwrapFile extends BaseShrinkwrapFile { return undefined; } - return new DependencySpecifier(dependencyName, dependencyJson.version); + return DependencySpecifier.parseWithCache(dependencyName, dependencyJson.version); } /** * @param dependencyName the name of the dependency to get a version for * @param tempProjectName the name of the temp project to check for this dependency * @param versionRange Not used, just exists to satisfy abstract API contract - * @override */ - protected tryEnsureDependencyVersion( + protected override tryEnsureDependencyVersion( dependencySpecifier: DependencySpecifier, tempProjectName: string ): DependencySpecifier | undefined { @@ -120,20 +117,19 @@ export class NpmShrinkwrapFile extends BaseShrinkwrapFile { return this.getTopLevelDependencyVersion(dependencySpecifier.packageName); } - return new DependencySpecifier(dependencySpecifier.packageName, dependencyJson.version); + return DependencySpecifier.parseWithCache(dependencySpecifier.packageName, dependencyJson.version); } - /** @override */ - public getProjectShrinkwrap( + public override getProjectShrinkwrap( project: RushConfigurationProject ): BaseProjectShrinkwrapFile | undefined { return undefined; } - /** @override */ - public async isWorkspaceProjectModifiedAsync( + public override async isWorkspaceProjectModifiedAsync( project: RushConfigurationProject, - variant?: string + subspace: Subspace, + variant: string | undefined ): Promise { throw new InternalError('Not implemented'); } diff --git a/libraries/rush-lib/src/logic/operations/AsyncOperationQueue.ts b/libraries/rush-lib/src/logic/operations/AsyncOperationQueue.ts index 006df21d840..6602ca74485 100644 --- a/libraries/rush-lib/src/logic/operations/AsyncOperationQueue.ts +++ b/libraries/rush-lib/src/logic/operations/AsyncOperationQueue.ts @@ -1,11 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { OperationExecutionRecord } from './OperationExecutionRecord'; +import type { OperationExecutionRecord } from './OperationExecutionRecord'; import { OperationStatus } from './OperationStatus'; +import { RushConstants } from '../RushConstants'; /** - * Implmentation of the async iteration protocol for a collection of IOperation objects. + * Implementation of the async iteration protocol for a collection of IOperation objects. * The async iterator will wait for an operation to be ready for execution, or terminate if there are no more operations. * * @remarks @@ -18,6 +19,17 @@ export class AsyncOperationQueue { private readonly _queue: OperationExecutionRecord[]; private readonly _pendingIterators: ((result: IteratorResult) => void)[]; + private readonly _totalOperations: number; + private readonly _completedOperations: Set; + + /** + * Tracks how many times each operation has been assigned to an execution slot. + * Operations that have been assigned more times (e.g. cobuild retries) are sorted + * after operations with fewer attempts, so untried work is preferred. + */ + private readonly _numberOfTimesQueuedByOperation: Map; + + private _isDone: boolean; /** * @param operations - The set of operations to be executed @@ -29,6 +41,10 @@ export class AsyncOperationQueue public constructor(operations: Iterable, sortFn: IOperationSortFunction) { this._queue = computeTopologyAndSort(operations, sortFn); this._pendingIterators = []; + this._totalOperations = this._queue.length; + this._isDone = false; + this._completedOperations = new Set(); + this._numberOfTimesQueuedByOperation = new Map(); } /** @@ -49,43 +65,116 @@ export class AsyncOperationQueue return promise; } + /** + * Set a callback to be invoked when one operation is completed. + * If all operations are completed, set the queue to done, resolve all pending iterators in next cycle. + */ + public complete(record: OperationExecutionRecord): void { + this._completedOperations.add(record); + this._numberOfTimesQueuedByOperation.delete(record); + + // Apply status changes to direct dependents + if (record.status !== OperationStatus.Failure && record.status !== OperationStatus.Blocked) { + // Only do so if the operation did not fail or get blocked + for (const item of record.consumers) { + // Remove this operation from the dependencies, to unblock the scheduler + if ( + item.dependencies.delete(record) && + item.dependencies.size === 0 && + item.status === OperationStatus.Waiting + ) { + item.status = OperationStatus.Ready; + } + } + } + + this.assignOperations(); + + if (this._completedOperations.size === this._totalOperations) { + this._isDone = true; + } + } + /** * Routes ready operations with 0 dependencies to waiting iterators. Normally invoked as part of `next()`, but * if the caller does not update operation dependencies prior to calling `next()`, may need to be invoked manually. */ public assignOperations(): void { - const { _queue: queue, _pendingIterators: waitingIterators } = this; + const { + _queue: queue, + _pendingIterators: waitingIterators, + _numberOfTimesQueuedByOperation: timesQueued + } = this; + + const readyOperations: OperationExecutionRecord[] = []; // By iterating in reverse order we do less array shuffling when removing operations for (let i: number = queue.length - 1; waitingIterators.length > 0 && i >= 0; i--) { - const operation: OperationExecutionRecord = queue[i]; + const record: OperationExecutionRecord = queue[i]; - if (operation.status === OperationStatus.Blocked) { + if ( + record.status === OperationStatus.Blocked || + record.status === OperationStatus.Skipped || + record.status === OperationStatus.Success || + record.status === OperationStatus.SuccessWithWarning || + record.status === OperationStatus.FromCache || + record.status === OperationStatus.NoOp || + record.status === OperationStatus.Failure || + record.status === OperationStatus.Aborted + ) { // It shouldn't be on the queue, remove it queue.splice(i, 1); - } else if (operation.status !== OperationStatus.Ready) { + timesQueued.delete(record); + } else if (record.status === OperationStatus.Queued || record.status === OperationStatus.Executing) { + // This operation is currently executing + // next one plz :) + } else if (record.status === OperationStatus.Waiting) { + // This operation is not yet ready to be executed + // next one plz :) + continue; + } else if (record.status !== OperationStatus.Ready) { // Sanity check - throw new Error(`Unexpected status "${operation.status}" for queued operation: ${operation.name}`); - } else if (operation.dependencies.size === 0) { - // This task is ready to process, hand it to the iterator. - queue.splice(i, 1); - // Needs to have queue semantics, otherwise tools that iterate it get confused - waitingIterators.shift()!({ - value: operation, - done: false - }); + throw new Error(`Unexpected status "${record.status}" for queued operation: ${record.name}`); + } else { + readyOperations.push(record); } // Otherwise operation is still waiting } + if (readyOperations.length > 1) { + // Sort by times queued ascending. Operations that have never been queued (0) + // come first, then operations with fewer attempts. This ensures cobuild retries + // (queued 1+ times, returned to Ready) are tried after untried operations. + readyOperations.sort((a, b) => (timesQueued.get(a) ?? 0) - (timesQueued.get(b) ?? 0)); + } + + for (const record of readyOperations) { + if (waitingIterators.length === 0) { + break; + } + // This task is ready to process, hand it to the iterator. + // Needs to have queue semantics, otherwise tools that iterate it get confused + timesQueued.set(record, (timesQueued.get(record) ?? 0) + 1); + record.status = OperationStatus.Queued; + waitingIterators.shift()!({ + value: record, + done: false + }); + } + + // Since items only get removed from the queue when they have a final status, this should be safe. if (queue.length === 0) { - // Queue is empty, flush + this._isDone = true; + } + + if (this._isDone) { for (const resolveAsyncIterator of waitingIterators.splice(0)) { resolveAsyncIterator({ value: undefined, done: true }); } + return; } } @@ -142,7 +231,7 @@ function calculateCriticalPathLength( .map((visitedTask) => visitedTask.name) .reverse() .join('\n -> ') + - '\nConsider using the decoupledLocalDependencies option for rush.json.' + `\nConsider using the decoupledLocalDependencies option in ${RushConstants.rushJsonFilename}.` ); } diff --git a/libraries/rush-lib/src/logic/operations/BuildPlanPlugin.ts b/libraries/rush-lib/src/logic/operations/BuildPlanPlugin.ts new file mode 100644 index 00000000000..dce4ed3785a --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/BuildPlanPlugin.ts @@ -0,0 +1,362 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { ITerminal } from '@rushstack/terminal'; + +import type { + IOperationGraphContext, + IPhasedCommandPlugin, + PhasedCommandHooks +} from '../../pluginFramework/PhasedCommandHooks'; +import type { IOperationGraph, IOperationGraphIterationOptions } from './IOperationGraph'; +import type { Operation } from './Operation'; +import { clusterOperations, type IOperationBuildCacheContext } from './CacheableOperationPlugin'; +import { DisjointSet } from '../cobuild/DisjointSet'; +import type { IConfigurableOperation } from './IOperationExecutionResult'; +import { RushProjectConfiguration } from '../../api/RushProjectConfiguration'; + +const PLUGIN_NAME: 'BuildPlanPlugin' = 'BuildPlanPlugin'; + +interface IBuildPlanOperationCacheContext { + cacheDisabledReason: IOperationBuildCacheContext['cacheDisabledReason']; +} + +interface ICobuildPlan { + summary: { + maxWidth: number; + maxDepth: number; + numberOfNodesPerDepth: number[]; + }; + operations: Operation[]; + clusters: Set[]; + buildCacheByOperation: Map; + clusterByOperation: Map>; +} + +export class BuildPlanPlugin implements IPhasedCommandPlugin { + private readonly _terminal: ITerminal; + + public constructor(terminal: ITerminal) { + this._terminal = terminal; + } + + public apply(hooks: PhasedCommandHooks): void { + const terminal: ITerminal = this._terminal; + + hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph: IOperationGraph, context: IOperationGraphContext) => { + graph.hooks.configureIteration.tap(PLUGIN_NAME, (currentStates, lastStates, iterationOptions) => { + createBuildPlan(currentStates, iterationOptions, context); + }); + }); + + function createBuildPlan( + recordByOperation: ReadonlyMap, + iterationOptions: IOperationGraphIterationOptions, + context: IOperationGraphContext + ): void { + const { inputsSnapshot } = iterationOptions; + const { projectConfigurations } = context; + const disjointSet: DisjointSet = new DisjointSet(); + const operations: Operation[] = [...recordByOperation.keys()]; + for (const operation of operations) { + disjointSet.add(operation); + } + const buildCacheByOperation: Map = new Map< + Operation, + IBuildPlanOperationCacheContext + >(); + + for (const operation of operations) { + const { associatedProject, associatedPhase } = operation; + + const projectConfiguration: RushProjectConfiguration | undefined = + projectConfigurations.get(associatedProject); + const fileHashes: ReadonlyMap | undefined = + inputsSnapshot?.getTrackedFileHashesForOperation(associatedProject, associatedPhase.name); + if (!fileHashes) { + continue; + } + const cacheDisabledReason: string | undefined = + RushProjectConfiguration.getCacheDisabledReasonForProject({ + projectConfiguration, + trackedFileNames: fileHashes.keys(), + isNoOp: operation.isNoOp, + phaseName: associatedPhase.name + }); + buildCacheByOperation.set(operation, { cacheDisabledReason }); + } + clusterOperations(disjointSet, buildCacheByOperation); + const buildPlan: ICobuildPlan = createCobuildPlan(disjointSet, terminal, buildCacheByOperation); + logCobuildBuildPlan(buildPlan, terminal); + } + } +} + +/** + * Output the build plan summary, this will include the depth of the build plan, the width of the build plan, and + * the number of nodes at each depth. + * + * Example output: +``` +Build Plan Depth (deepest dependency tree): 3 +Build Plan Width (maximum parallelism): 7 +Number of Nodes per Depth: 2, 7, 5 +Plan @ Depth 0 has 2 nodes and 0 dependents: +- b (build) +- a (build) +Plan @ Depth 1 has 7 nodes and 2 dependents: +- c (build) +- d (build) +- f (pre-build) +- g (pre-build) +- e (build) +- f (build) +- g (build) +Plan @ Depth 2 has 5 nodes and 9 dependents: +- c (build) +- d (build) +- e (build) +- f (build) +- g (build) +``` + * The summary data can be useful for understanding the shape of the build plan. The depth of the build plan is the + * longest dependency chain in the build plan. The width of the build plan is the maximum number of operations that + * can be executed in parallel. The number of nodes per depth is the number of operations that can be executed in parallel + * at each depth. **This does not currently include clustering information, which further restricts which operations can + * be executed in parallel.** + * The depth data can be useful for debugging situations where cobuilds aren't utilizing multiple agents as expected. There may be + * some long dependency trees that can't be executed in parallel. Or there may be some key operations at the base of the + * build graph that are blocking the rest of the build. + */ +function generateCobuildPlanSummary(operations: Operation[], terminal: ITerminal): ICobuildPlan['summary'] { + const numberOfDependenciesByOperation: Map = new Map(); + + const queue: Operation[] = operations.filter((e) => e.dependencies.size === 0); + const seen: Set = new Set(queue); + for (const operation of queue) { + numberOfDependenciesByOperation.set(operation, 0); + } + + /** + * Traverse the build plan to determine the number of dependencies for each operation. This is done by starting + * at the base of the build plan and traversing the graph in a breadth-first manner. We use the parent operation + * to determine the number of dependencies for each child operation. This allows us to detect cases where no-op + * operations are strung together, and correctly mark the first real operation as being a root operation. + */ + while (queue.length > 0) { + const operation: Operation = queue.shift()!; + const increment: number = operation.isNoOp ? 0 : 1; + for (const consumer of operation.consumers) { + const numberOfDependencies: number = (numberOfDependenciesByOperation.get(operation) ?? 0) + increment; + numberOfDependenciesByOperation.set(consumer, numberOfDependencies); + if (!seen.has(consumer)) { + queue.push(consumer); + seen.add(consumer); + } + } + } + + const layerQueue: Operation[] = []; + for (const operation of operations) { + if (operation.isNoOp) { + continue; + } + + const numberOfDependencies: number = numberOfDependenciesByOperation.get(operation) ?? 0; + if (numberOfDependencies === 0) { + layerQueue.push(operation); + } + } + + let nextLayer: Set = new Set(); + const remainingOperations: Set = new Set(operations); + let depth: number = 0; + let maxWidth: number = layerQueue.length; + const numberOfNodes: number[] = [maxWidth]; + const depthToOperationsMap: Map> = new Map>(); + depthToOperationsMap.set(depth, new Set(layerQueue)); + + /** + * Determine the depth and width of the build plan. We start with the inner layer and gradually traverse layer by + * layer up the tree/graph until we have no more nodes to process. At each layer, we determine the + * number of executable operations. + */ + do { + if (layerQueue.length === 0) { + layerQueue.push(...nextLayer); + const realOperations: Operation[] = layerQueue.filter((e) => !e.isNoOp); + if (realOperations.length > 0) { + depth += 1; + depthToOperationsMap.set(depth, new Set(realOperations)); + numberOfNodes.push(realOperations.length); + } + const currentWidth: number = realOperations.length; + if (currentWidth > maxWidth) { + maxWidth = currentWidth; + } + nextLayer = new Set(); + + if (layerQueue.length === 0) { + break; + } + } + const leaf: Operation = layerQueue.shift()!; + if (remainingOperations.delete(leaf)) { + for (const consumer of leaf.consumers) { + nextLayer.add(consumer); + } + } + } while (remainingOperations.size > 0); + + terminal.writeLine(`Build Plan Depth (deepest dependency tree): ${depth + 1}`); + terminal.writeLine(`Build Plan Width (maximum parallelism): ${maxWidth}`); + terminal.writeLine(`Number of Nodes per Depth: ${numberOfNodes.join(', ')}`); + for (const [operationDepth, operationsAtDepth] of depthToOperationsMap) { + let numberOfDependents: number = 0; + for (let i: number = 0; i < operationDepth; i++) { + numberOfDependents += numberOfNodes[i]; + } + terminal.writeLine( + `Plan @ Depth ${operationDepth} has ${numberOfNodes[operationDepth]} nodes and ${numberOfDependents} dependents:` + ); + for (const operation of operationsAtDepth) { + if (operation.isNoOp !== true) { + terminal.writeLine(`- ${operation.name}`); + } + } + } + + return { + maxDepth: depth === 0 && numberOfNodes[0] !== 0 ? depth + 1 : 0, + maxWidth: maxWidth, + numberOfNodesPerDepth: numberOfNodes + }; +} + +function getName(op: Operation): string { + return op.name; +} + +/** + * Log the cobuild build plan by cluster. This is intended to help debug situations where cobuilds aren't + * utilizing multiple agents correctly. + */ +function createCobuildPlan( + disjointSet: DisjointSet, + terminal: ITerminal, + buildCacheByOperation: Map +): ICobuildPlan { + const clusters: Set[] = [...disjointSet.getAllSets()]; + const operations: Operation[] = clusters.flatMap((e) => Array.from(e)); + + const operationToClusterMap: Map> = new Map>(); + for (const cluster of clusters) { + for (const operation of cluster) { + operationToClusterMap.set(operation, cluster); + } + } + + return { + summary: generateCobuildPlanSummary(operations, terminal), + operations, + buildCacheByOperation, + clusterByOperation: operationToClusterMap, + clusters + }; +} + +/** + * This method logs in depth details about the cobuild plan, including the operations in each cluster, the dependencies + * for each cluster, and the reason why each operation is clustered. + */ +function logCobuildBuildPlan(buildPlan: ICobuildPlan, terminal: ITerminal): void { + const { operations, clusters, buildCacheByOperation, clusterByOperation } = buildPlan; + + const executionPlan: Operation[] = []; + for (const operation of operations) { + if (!operation.isNoOp) { + executionPlan.push(operation); + } + } + + // This is a lazy way of getting the waterfall chart, basically check for the latest + // dependency and put this operation after that finishes. + const spacingByDependencyMap: Map = new Map(); + for (let index: number = 0; index < executionPlan.length; index++) { + const operation: Operation = executionPlan[index]; + + const spacing: number = Math.max( + ...Array.from(operation.dependencies, (e) => { + const dependencySpacing: number | undefined = spacingByDependencyMap.get(e); + return dependencySpacing !== undefined ? dependencySpacing + 1 : 0; + }), + 0 + ); + spacingByDependencyMap.set(operation, spacing); + } + executionPlan.sort((a, b) => { + const aSpacing: number = spacingByDependencyMap.get(a) ?? 0; + const bSpacing: number = spacingByDependencyMap.get(b) ?? 0; + return aSpacing - bSpacing; + }); + + terminal.writeLine('##################################################'); + // Get the maximum name length for left padding. + let maxOperationNameLength: number = 1; + for (const operation of executionPlan) { + const name: string = getName(operation); + maxOperationNameLength = Math.max(maxOperationNameLength, name.length); + } + for (const operation of executionPlan) { + const spacing: number = spacingByDependencyMap.get(operation) ?? 0; + terminal.writeLine( + `${getName(operation).padStart(maxOperationNameLength + 1)}: ${'-'.repeat(spacing)}(${clusters.indexOf( + clusterByOperation.get(operation)! + )})` + ); + } + terminal.writeLine('##################################################'); + + function getDependenciesForCluster(cluster: Set): Set { + const dependencies: Set = new Set(); + for (const operation of cluster) { + for (const dependent of operation.dependencies) { + dependencies.add(dependent); + } + } + return dependencies; + } + + function dedupeShards(ops: Set): string[] { + const dedupedOperations: Set = new Set(); + for (const operation of ops) { + dedupedOperations.add(`${operation.associatedProject.packageName} (${operation.associatedPhase.name})`); + } + return [...dedupedOperations]; + } + + for (let clusterIndex: number = 0; clusterIndex < clusters.length; clusterIndex++) { + const cluster: Set = clusters[clusterIndex]; + const allClusterDependencies: Set = getDependenciesForCluster(cluster); + const outOfClusterDependencies: Set = new Set( + [...allClusterDependencies].filter((e) => !cluster.has(e)) + ); + + terminal.writeLine(`Cluster ${clusterIndex}:`); + terminal.writeLine(`- Dependencies: ${dedupeShards(outOfClusterDependencies).join(', ') || 'none'}`); + // Only log clustering info, if we did in fact cluster. + if (cluster.size > 1) { + terminal.writeLine( + `- Clustered by: \n${[...allClusterDependencies] + .filter((e) => buildCacheByOperation.get(e)?.cacheDisabledReason) + .map((e) => ` - (${e.name}) "${buildCacheByOperation.get(e)?.cacheDisabledReason ?? ''}"`) + .join('\n')}` + ); + } + terminal.writeLine( + `- Operations: ${Array.from(cluster, (e) => `${getName(e)}${e.isNoOp ? ' [SKIPPED]' : ''}`).join(', ')}` + ); + terminal.writeLine('--------------------------------------------------'); + } + terminal.writeLine('##################################################'); +} diff --git a/libraries/rush-lib/src/logic/operations/CacheableOperationPlugin.ts b/libraries/rush-lib/src/logic/operations/CacheableOperationPlugin.ts new file mode 100644 index 00000000000..6f254f52fc6 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/CacheableOperationPlugin.ts @@ -0,0 +1,834 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as crypto from 'node:crypto'; + +import { InternalError, NewlineKind, Sort } from '@rushstack/node-core-library'; +import { CollatedTerminal, type CollatedWriter } from '@rushstack/stream-collator'; +import { + DiscardStdoutTransform, + TextRewriterTransform, + SplitterTransform, + type TerminalWritable, + type ITerminal, + Terminal +} from '@rushstack/terminal'; + +import { CollatedTerminalProvider } from '../../utilities/CollatedTerminalProvider'; +import { OperationStatus } from './OperationStatus'; +import { CobuildLock, type ICobuildCompletedState } from '../cobuild/CobuildLock'; +import { OperationBuildCache } from '../buildCache/OperationBuildCache'; +import { RushConstants } from '../RushConstants'; +import type { RushProjectConfiguration } from '../../api/RushProjectConfiguration'; +import { + initializeProjectLogFilesAsync, + getProjectLogFilePaths, + type ILogFilePaths +} from './ProjectLogWritable'; +import type { CobuildConfiguration } from '../../api/CobuildConfiguration'; +import { DisjointSet } from '../cobuild/DisjointSet'; +import { PeriodicCallback } from './PeriodicCallback'; +import { NullTerminalProvider } from '../../utilities/NullTerminalProvider'; +import type { Operation } from './Operation'; +import type { IOperationRunnerContext } from './IOperationRunner'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { + IOperationGraphContext, + IPhasedCommandPlugin, + PhasedCommandHooks +} from '../../pluginFramework/PhasedCommandHooks'; +import type { IOperationGraph, IOperationGraphIterationOptions } from './IOperationGraph'; +import type { BuildCacheConfiguration } from '../../api/BuildCacheConfiguration'; +import type { IOperationExecutionResult } from './IOperationExecutionResult'; +import type { OperationExecutionRecord } from './OperationExecutionRecord'; + +const PLUGIN_NAME: 'CacheablePhasedOperationPlugin' = 'CacheablePhasedOperationPlugin'; +const PERIODIC_CALLBACK_INTERVAL_IN_SECONDS: number = 10; + +export interface IProjectDeps { + files: { [filePath: string]: string }; + arguments: string; +} + +export interface IOperationBuildCacheContext { + isCacheWriteAllowed: boolean; + isCacheReadAllowed: boolean; + + operationBuildCache: OperationBuildCache | undefined; + cacheDisabledReason: string | undefined; + outputFolderNames: ReadonlyArray; + + cobuildLock: CobuildLock | undefined; + + // The id of the cluster contains the operation, used when acquiring cobuild lock + cobuildClusterId: string | undefined; + + // Controls the log for the cache subsystem + buildCacheTerminal: ITerminal | undefined; + buildCacheTerminalWritable: TerminalWritable | undefined; + + periodicCallback: PeriodicCallback; + cacheRestored: boolean; + isCacheReadAttempted: boolean; +} + +export interface ICacheableOperationPluginOptions { + allowWarningsInSuccessfulBuild: boolean; + buildCacheConfiguration: BuildCacheConfiguration; + cobuildConfiguration: CobuildConfiguration | undefined; + terminal: ITerminal; + excludeAppleDoubleFiles: boolean; + useDirectFileTransfersForBuildCache: boolean; +} + +interface ITryGetOperationBuildCacheOptionsBase { + buildCacheContext: IOperationBuildCacheContext; + buildCacheConfiguration: BuildCacheConfiguration | undefined; + terminal: ITerminal; + excludeAppleDoubleFiles: boolean; + useDirectFileTransfersForBuildCache: boolean; + record: TRecord; +} + +type ITryGetOperationBuildCacheOptions = ITryGetOperationBuildCacheOptionsBase; + +interface ITryGetLogOnlyOperationBuildCacheOptions + extends ITryGetOperationBuildCacheOptionsBase { + cobuildConfiguration: CobuildConfiguration; +} + +export class CacheableOperationPlugin implements IPhasedCommandPlugin { + private _buildCacheContextByOperation: Map = new Map(); + + private readonly _options: ICacheableOperationPluginOptions; + + public constructor(options: ICacheableOperationPluginOptions) { + this._options = options; + } + + public apply(hooks: PhasedCommandHooks): void { + const { + allowWarningsInSuccessfulBuild, + buildCacheConfiguration, + cobuildConfiguration, + excludeAppleDoubleFiles, + useDirectFileTransfersForBuildCache + } = this._options; + + hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph: IOperationGraph, context: IOperationGraphContext) => { + graph.hooks.beforeExecuteIterationAsync.tap( + PLUGIN_NAME, + ( + recordByOperation: ReadonlyMap, + iterationOptions: IOperationGraphIterationOptions + ): undefined => { + const { inputsSnapshot } = iterationOptions; + + if (!inputsSnapshot) { + throw new Error( + `Build cache is only supported if running in a Git repository. Either disable the build cache or run Rush in a Git repository.` + ); + } + + const { isIncrementalBuildAllowed, projectConfigurations } = context; + const { cacheWriteEnabled } = buildCacheConfiguration; + + const disjointSet: DisjointSet | undefined = cobuildConfiguration?.cobuildFeatureEnabled + ? new DisjointSet() + : undefined; + + for (const [operation, record] of recordByOperation) { + const { associatedProject, associatedPhase, runner, settings: operationSettings } = operation; + if (!runner) { + return; + } + + const { name: phaseName } = associatedPhase; + + const projectConfiguration: RushProjectConfiguration | undefined = + projectConfigurations.get(associatedProject); + + // This value can *currently* be cached per-project, but in the future the list of files will vary + // depending on the selected phase. + const fileHashes: ReadonlyMap | undefined = + inputsSnapshot.getTrackedFileHashesForOperation(associatedProject, phaseName); + + const cacheDisabledReason: string | undefined = projectConfiguration + ? projectConfiguration.getCacheDisabledReason(fileHashes.keys(), phaseName, operation.isNoOp) + : `Project does not have a ${RushConstants.rushProjectConfigFilename} configuration file, ` + + 'or one provided by a rig, so it does not support caching.'; + + const outputFolderNames: string[] = [record.metadataFolderPath]; + const configuredOutputFolderNames: string[] | undefined = operationSettings?.outputFolderNames; + if (configuredOutputFolderNames) { + for (const folderName of configuredOutputFolderNames) { + outputFolderNames.push(folderName); + } + } + + disjointSet?.add(operation); + + const buildCacheContext: IOperationBuildCacheContext = { + // Supports cache writes by default for initial operations. + // Don't write during watch runs for performance reasons (and to avoid flooding the cache) + isCacheWriteAllowed: cacheWriteEnabled, + isCacheReadAllowed: isIncrementalBuildAllowed, + operationBuildCache: undefined, + outputFolderNames, + cacheDisabledReason, + cobuildLock: undefined, + cobuildClusterId: undefined, + buildCacheTerminal: undefined, + buildCacheTerminalWritable: undefined, + periodicCallback: new PeriodicCallback({ + interval: PERIODIC_CALLBACK_INTERVAL_IN_SECONDS * 1000 + }), + cacheRestored: false, + isCacheReadAttempted: false + }; + // Upstream runners may mutate the property of build cache context for downstream runners + this._buildCacheContextByOperation.set(operation, buildCacheContext); + } + + if (disjointSet) { + clusterOperations(disjointSet, this._buildCacheContextByOperation); + for (const operationSet of disjointSet.getAllSets()) { + if (cobuildConfiguration?.cobuildFeatureEnabled && cobuildConfiguration.cobuildContextId) { + // Get a deterministic ordered array of operations, which is important to get a deterministic cluster id. + const groupedOperations: Operation[] = Array.from(operationSet); + Sort.sortBy(groupedOperations, (operation: Operation) => { + return operation.name; + }); + + // Generates cluster id, cluster id comes from the project folder and operation name of all operations in the same cluster. + const hash: crypto.Hash = crypto.createHash('sha1'); + for (const operation of groupedOperations) { + const { associatedPhase: phase, associatedProject: project } = operation; + hash.update(project.projectRelativeFolder); + hash.update(RushConstants.hashDelimiter); + hash.update(operation.name ?? phase.name); + hash.update(RushConstants.hashDelimiter); + } + const cobuildClusterId: string = hash.digest('hex'); + + // Assign same cluster id to all operations in the same cluster. + for (const record of groupedOperations) { + const buildCacheContext: IOperationBuildCacheContext = + this._getBuildCacheContextByOperationOrThrow(record); + buildCacheContext.cobuildClusterId = cobuildClusterId; + } + } + } + } + } + ); + + graph.hooks.beforeExecuteOperationAsync.tapPromise( + PLUGIN_NAME, + async ( + runnerContext: IOperationRunnerContext & IOperationExecutionResult + ): Promise => { + if (this._buildCacheContextByOperation.size === 0) { + return; + } + + const buildCacheContext: IOperationBuildCacheContext | undefined = + this._getBuildCacheContextByOperation(runnerContext.operation); + + if (!buildCacheContext) { + return; + } + + const record: OperationExecutionRecord = runnerContext as OperationExecutionRecord; + + const { + associatedProject: project, + associatedPhase: phase, + runner, + _operationMetadataManager: operationMetadataManager, + operation + } = record; + + if (!record.enabled || !runner?.cacheable) { + return; + } + + const runBeforeExecute = async (): Promise => { + if ( + !buildCacheContext.buildCacheTerminal || + buildCacheContext.buildCacheTerminalWritable?.isOpen === false + ) { + // The writable does not exist or has been closed, re-create one + // eslint-disable-next-line require-atomic-updates + buildCacheContext.buildCacheTerminal = await this._createBuildCacheTerminalAsync({ + record, + buildCacheContext, + buildCacheEnabled: buildCacheConfiguration?.buildCacheEnabled, + rushProject: project, + logFilenameIdentifier: operation.logFilenameIdentifier, + quietMode: record.quietMode, + debugMode: record.debugMode + }); + } + + const buildCacheTerminal: ITerminal = buildCacheContext.buildCacheTerminal; + + let operationBuildCache: OperationBuildCache | undefined = this._tryGetOperationBuildCache({ + buildCacheContext, + buildCacheConfiguration, + terminal: buildCacheTerminal, + record, + excludeAppleDoubleFiles, + useDirectFileTransfersForBuildCache + }); + + // Try to acquire the cobuild lock + let cobuildLock: CobuildLock | undefined; + if (cobuildConfiguration?.cobuildFeatureEnabled) { + if ( + cobuildConfiguration?.cobuildLeafProjectLogOnlyAllowed && + operation.consumers.size === 0 && + !operationBuildCache + ) { + // When the leaf project log only is allowed and the leaf project is build cache "disabled", try to get + // a log files only project build cache + operationBuildCache = await this._tryGetLogOnlyOperationBuildCacheAsync({ + buildCacheConfiguration, + cobuildConfiguration, + buildCacheContext, + record, + terminal: buildCacheTerminal, + excludeAppleDoubleFiles, + useDirectFileTransfersForBuildCache + }); + if (operationBuildCache) { + buildCacheTerminal.writeVerboseLine( + `Log files only build cache is enabled for the project "${project.packageName}" because the cobuild leaf project log only is allowed` + ); + } else { + buildCacheTerminal.writeWarningLine( + `Failed to get log files only build cache for the project "${project.packageName}"` + ); + } + } + + cobuildLock = await this._tryGetCobuildLockAsync({ + buildCacheContext, + operationBuildCache, + cobuildConfiguration, + packageName: project.packageName, + phaseName: phase.name + }); + } + + // eslint-disable-next-line require-atomic-updates -- we are mutating the build cache context intentionally + buildCacheContext.cobuildLock = cobuildLock; + + // If possible, we want to skip this operation -- either by restoring it from the + // cache, if caching is enabled, or determining that the project + // is unchanged (using the older incremental execution logic). These two approaches, + // "caching" and "skipping", are incompatible, so only one applies. + // + // Note that "caching" and "skipping" take two different approaches + // to tracking dependents: + // + // - For caching, "isCacheReadAllowed" is set if a project supports + // incremental builds, and determining whether this project or a dependent + // has changed happens inside the hashing logic. + // + + const { error: errorLogPath } = getProjectLogFilePaths({ + project, + logFilenameIdentifier: operation.logFilenameIdentifier + }); + const restoreCacheAsync = async ( + // TODO: Investigate if `operationBuildCacheForRestore` is always the same instance as `operationBuildCache` + // above, and if it is, remove this parameter + operationBuildCacheForRestore: OperationBuildCache | undefined, + specifiedCacheId?: string + ): Promise => { + buildCacheContext.isCacheReadAttempted = true; + const restoreFromCacheSuccess: boolean | undefined = + await operationBuildCacheForRestore?.tryRestoreFromCacheAsync( + buildCacheTerminal, + specifiedCacheId + ); + if (restoreFromCacheSuccess) { + buildCacheContext.cacheRestored = true; + await runnerContext.runWithTerminalAsync( + async (taskTerminal, terminalProvider) => { + // Restore the original state of the operation without cache + await operationMetadataManager?.tryRestoreAsync({ + terminalProvider, + terminal: buildCacheTerminal, + errorLogPath, + cobuildContextId: cobuildConfiguration?.cobuildContextId, + cobuildRunnerId: cobuildConfiguration?.cobuildRunnerId + }); + }, + { createLogFile: false } + ); + } + return !!restoreFromCacheSuccess; + }; + if (cobuildLock) { + // handling rebuilds. "rush rebuild" or "rush retest" command will save operations to + // the build cache once completed, but does not retrieve them (since the "incremental" + // flag is disabled). However, we still need a cobuild to be able to retrieve a finished + // build from another cobuild in this case. + const cobuildCompletedState: ICobuildCompletedState | undefined = + await cobuildLock.getCompletedStateAsync(); + if (cobuildCompletedState) { + const { status, cacheId } = cobuildCompletedState; + + if (record.operation.settings?.allowCobuildWithoutCache) { + // This should only be enabled if the experiment for cobuild orchestration is enabled. + return status; + } + + const restoreFromCacheSuccess: boolean = await restoreCacheAsync( + cobuildLock.operationBuildCache, + cacheId + ); + + if (restoreFromCacheSuccess) { + return status; + } + } else if (!buildCacheContext.isCacheReadAttempted && buildCacheContext.isCacheReadAllowed) { + const restoreFromCacheSuccess: boolean = await restoreCacheAsync(operationBuildCache); + + if (restoreFromCacheSuccess) { + return OperationStatus.FromCache; + } + } + } else if (buildCacheContext.isCacheReadAllowed) { + const restoreFromCacheSuccess: boolean = await restoreCacheAsync(operationBuildCache); + + if (restoreFromCacheSuccess) { + return OperationStatus.FromCache; + } + } + + if (buildCacheContext.isCacheWriteAllowed && cobuildLock) { + const acquireSuccess: boolean = await cobuildLock.tryAcquireLockAsync(); + if (acquireSuccess) { + const { periodicCallback } = buildCacheContext; + periodicCallback.addCallback(async () => { + await cobuildLock?.renewLockAsync(); + }); + periodicCallback.start(); + } else { + setTimeout(() => { + record.status = OperationStatus.Ready; + }, 500); + return OperationStatus.Executing; + } + } + }; + + return await runBeforeExecute(); + } + ); + + graph.hooks.afterExecuteOperationAsync.tapPromise( + PLUGIN_NAME, + async (runnerContext: IOperationRunnerContext): Promise => { + const record: OperationExecutionRecord = runnerContext as OperationExecutionRecord; + const { + status, + stopwatch, + _operationMetadataManager: operationMetadataManager, + operation + } = record; + + const { associatedProject: project, runner } = operation; + + if (!record.enabled || !runner?.cacheable) { + return; + } + + const buildCacheContext: IOperationBuildCacheContext | undefined = + this._getBuildCacheContextByOperation(operation); + + if (!buildCacheContext) { + return; + } + + // No need to run for the following operation status + if (!record.isTerminal || record.status === OperationStatus.NoOp) { + return; + } + + const { cobuildLock, operationBuildCache, isCacheWriteAllowed, buildCacheTerminal, cacheRestored } = + buildCacheContext; + + try { + if (!cacheRestored) { + // Save the metadata to disk + const { logFilenameIdentifier } = operationMetadataManager; + const { duration: durationInSeconds } = stopwatch; + const { + text: logPath, + error: errorLogPath, + jsonl: logChunksPath + } = getProjectLogFilePaths({ + project, + logFilenameIdentifier + }); + await operationMetadataManager.saveAsync({ + durationInSeconds, + cobuildContextId: cobuildLock?.cobuildConfiguration.cobuildContextId, + cobuildRunnerId: cobuildLock?.cobuildConfiguration.cobuildRunnerId, + logPath, + errorLogPath, + logChunksPath + }); + } + + if (!buildCacheTerminal) { + // This should not happen + throw new InternalError(`Build Cache Terminal is not created`); + } + + let setCompletedStatePromiseFunction: (() => Promise | undefined) | undefined; + let setCacheEntryPromise: (() => Promise | undefined) | undefined; + if (cobuildLock && isCacheWriteAllowed) { + const { cacheId, contextId } = cobuildLock.cobuildContext; + + let finalCacheId: string = cacheId; + if (status === OperationStatus.Failure) { + finalCacheId = `${cacheId}-${contextId}-failed`; + } else if (status === OperationStatus.SuccessWithWarning && !record.runner.warningsAreAllowed) { + finalCacheId = `${cacheId}-${contextId}-warnings`; + } + switch (status) { + case OperationStatus.SuccessWithWarning: + case OperationStatus.Success: + case OperationStatus.Failure: { + const currentStatus: ICobuildCompletedState['status'] = status; + setCompletedStatePromiseFunction = () => { + return cobuildLock?.setCompletedStateAsync({ + status: currentStatus, + cacheId: finalCacheId + }); + }; + setCacheEntryPromise = () => + cobuildLock.operationBuildCache.trySetCacheEntryAsync(buildCacheTerminal, finalCacheId); + } + } + } + + const taskIsSuccessful: boolean = + status === OperationStatus.Success || + (status === OperationStatus.SuccessWithWarning && + record.runner.warningsAreAllowed && + allowWarningsInSuccessfulBuild); + + // If the command is successful, we can calculate project hash, and no dependencies were skipped, + // write a new cache entry. + if (!setCacheEntryPromise && taskIsSuccessful && isCacheWriteAllowed && operationBuildCache) { + setCacheEntryPromise = () => operationBuildCache.trySetCacheEntryAsync(buildCacheTerminal); + } + if (!cacheRestored) { + const cacheWriteSuccess: boolean | undefined = await setCacheEntryPromise?.(); + await setCompletedStatePromiseFunction?.(); + + if (cacheWriteSuccess === false && status === OperationStatus.Success) { + record.status = OperationStatus.SuccessWithWarning; + } + } + } finally { + buildCacheContext.buildCacheTerminalWritable?.close(); + buildCacheContext.periodicCallback.stop(); + } + } + ); + + graph.hooks.afterExecuteOperationAsync.tap( + PLUGIN_NAME, + (record: IOperationRunnerContext & IOperationExecutionResult): void => { + const { operation } = record; + const buildCacheContext: IOperationBuildCacheContext | undefined = + this._buildCacheContextByOperation.get(operation); + // Status changes to direct dependents + let blockCacheWrite: boolean = !buildCacheContext?.isCacheWriteAllowed; + + switch (record.status) { + case OperationStatus.Skipped: { + // Skipping means cannot guarantee integrity, so prevent cache writes in dependents. + blockCacheWrite = true; + break; + } + } + + // Apply status changes to direct dependents + if (blockCacheWrite) { + for (const consumer of operation.consumers) { + const consumerBuildCacheContext: IOperationBuildCacheContext | undefined = + this._getBuildCacheContextByOperation(consumer); + if (consumerBuildCacheContext) { + consumerBuildCacheContext.isCacheWriteAllowed = false; + } + } + } + } + ); + + graph.hooks.afterExecuteIterationAsync.tap(PLUGIN_NAME, (status: OperationStatus) => { + this._buildCacheContextByOperation.clear(); + return status; + }); + }); + } + + private _getBuildCacheContextByOperation(operation: Operation): IOperationBuildCacheContext | undefined { + const buildCacheContext: IOperationBuildCacheContext | undefined = + this._buildCacheContextByOperation.get(operation); + return buildCacheContext; + } + + private _getBuildCacheContextByOperationOrThrow(operation: Operation): IOperationBuildCacheContext { + const buildCacheContext: IOperationBuildCacheContext | undefined = + this._getBuildCacheContextByOperation(operation); + if (!buildCacheContext) { + // This should not happen + throw new InternalError(`Build cache context for operation ${operation.name} should be defined`); + } + return buildCacheContext; + } + + private _tryGetOperationBuildCache( + options: ITryGetOperationBuildCacheOptions + ): OperationBuildCache | undefined { + const { + buildCacheConfiguration, + buildCacheContext, + terminal, + record, + excludeAppleDoubleFiles, + useDirectFileTransfersForBuildCache + } = options; + if (!buildCacheContext.operationBuildCache) { + const { cacheDisabledReason } = buildCacheContext; + if (cacheDisabledReason && !record.operation.settings?.allowCobuildWithoutCache) { + terminal.writeVerboseLine(cacheDisabledReason); + return; + } + + if (!buildCacheConfiguration) { + // Unreachable, since this will have set `cacheDisabledReason`. + return; + } + + buildCacheContext.operationBuildCache = OperationBuildCache.forOperation(record, { + buildCacheConfiguration, + terminal, + excludeAppleDoubleFiles, + useDirectFileTransfersForBuildCache + }); + } + + return buildCacheContext.operationBuildCache; + } + + // Get an OperationBuildCache only cache/restore log files + private async _tryGetLogOnlyOperationBuildCacheAsync( + options: ITryGetLogOnlyOperationBuildCacheOptions + ): Promise { + const { + buildCacheContext, + buildCacheConfiguration, + cobuildConfiguration, + record, + terminal, + excludeAppleDoubleFiles, + useDirectFileTransfersForBuildCache + } = options; + + if (!buildCacheConfiguration?.buildCacheEnabled) { + return; + } + + const { outputFolderNames } = buildCacheContext; + + const hasher: crypto.Hash = crypto.createHash('sha1'); + hasher.update(record.getStateHash()); + + if (cobuildConfiguration.cobuildContextId) { + hasher.update( + `${RushConstants.hashDelimiter}cobuildContextId=${cobuildConfiguration.cobuildContextId}` + ); + } + + hasher.update(`${RushConstants.hashDelimiter}logFilesOnly=1`); + + const operationStateHash: string = hasher.digest('hex'); + + const { associatedPhase, associatedProject } = record.operation; + + const operationBuildCache: OperationBuildCache = OperationBuildCache.getOperationBuildCache({ + project: associatedProject, + projectOutputFolderNames: outputFolderNames, + buildCacheConfiguration, + terminal, + operationStateHash, + phaseName: associatedPhase.name, + excludeAppleDoubleFiles, + useDirectFileTransfersForBuildCache + }); + + buildCacheContext.operationBuildCache = operationBuildCache; + + return operationBuildCache; + } + + private async _tryGetCobuildLockAsync({ + cobuildConfiguration, + buildCacheContext, + operationBuildCache, + packageName, + phaseName + }: { + cobuildConfiguration: CobuildConfiguration | undefined; + buildCacheContext: IOperationBuildCacheContext; + operationBuildCache: OperationBuildCache | undefined; + packageName: string; + phaseName: string; + }): Promise { + if (!buildCacheContext.cobuildLock) { + if (operationBuildCache && cobuildConfiguration?.cobuildFeatureEnabled) { + if (!buildCacheContext.cobuildClusterId) { + // This should not happen + throw new InternalError('Cobuild cluster id is not defined'); + } + buildCacheContext.cobuildLock = new CobuildLock({ + cobuildConfiguration, + operationBuildCache, + cobuildClusterId: buildCacheContext.cobuildClusterId, + lockExpireTimeInSeconds: PERIODIC_CALLBACK_INTERVAL_IN_SECONDS * 3, + packageName, + phaseName + }); + } + } + return buildCacheContext.cobuildLock; + } + + private async _createBuildCacheTerminalAsync({ + record, + buildCacheContext, + buildCacheEnabled, + rushProject, + logFilenameIdentifier, + quietMode, + debugMode + }: { + record: OperationExecutionRecord; + buildCacheContext: IOperationBuildCacheContext; + buildCacheEnabled: boolean | undefined; + rushProject: RushConfigurationProject; + logFilenameIdentifier: string; + quietMode: boolean; + debugMode: boolean; + }): Promise { + const silent: boolean = record.silent; + if (silent) { + const nullTerminalProvider: NullTerminalProvider = new NullTerminalProvider(); + return new Terminal(nullTerminalProvider); + } + + let cacheConsoleWritable: TerminalWritable; + // This creates the writer, only do this if necessary. + const collatedWriter: CollatedWriter = record.collatedWriter; + const cacheProjectLogWritable: TerminalWritable | undefined = + await this._tryGetBuildCacheTerminalWritableAsync({ + buildCacheContext, + buildCacheEnabled, + rushProject, + logFilenameIdentifier + }); + + if (quietMode) { + const discardTransform: DiscardStdoutTransform = new DiscardStdoutTransform({ + destination: collatedWriter + }); + const normalizeNewlineTransform: TextRewriterTransform = new TextRewriterTransform({ + destination: discardTransform, + normalizeNewlines: NewlineKind.Lf, + ensureNewlineAtEnd: true + }); + cacheConsoleWritable = normalizeNewlineTransform; + } else { + cacheConsoleWritable = collatedWriter; + } + + let cacheCollatedTerminal: CollatedTerminal; + if (cacheProjectLogWritable) { + const cacheSplitterTransform: SplitterTransform = new SplitterTransform({ + destinations: [cacheConsoleWritable, cacheProjectLogWritable] + }); + cacheCollatedTerminal = new CollatedTerminal(cacheSplitterTransform); + } else { + cacheCollatedTerminal = new CollatedTerminal(cacheConsoleWritable); + } + + const buildCacheTerminalProvider: CollatedTerminalProvider = new CollatedTerminalProvider( + cacheCollatedTerminal, + { + debugEnabled: debugMode + } + ); + return new Terminal(buildCacheTerminalProvider); + } + + private async _tryGetBuildCacheTerminalWritableAsync({ + buildCacheEnabled, + rushProject, + buildCacheContext, + logFilenameIdentifier + }: { + buildCacheEnabled: boolean | undefined; + rushProject: RushConfigurationProject; + buildCacheContext: IOperationBuildCacheContext; + logFilenameIdentifier: string; + }): Promise { + // Only open the *.cache.log file(s) if the cache is enabled. + if (!buildCacheEnabled) { + return; + } + + const logFilePaths: ILogFilePaths = getProjectLogFilePaths({ + project: rushProject, + logFilenameIdentifier: `${logFilenameIdentifier}.cache` + }); + + buildCacheContext.buildCacheTerminalWritable = await initializeProjectLogFilesAsync({ + logFilePaths + }); + + return buildCacheContext.buildCacheTerminalWritable; + } +} + +export function clusterOperations( + initialClusters: DisjointSet, + operationBuildCacheMap: Map +): void { + // If disjoint set exists, connect build cache disabled project with its consumers + for (const [operation, { cacheDisabledReason }] of operationBuildCacheMap) { + if (cacheDisabledReason && !operation.settings?.allowCobuildWithoutCache) { + /** + * Group the project build cache disabled with its consumers. This won't affect too much in + * a monorepo with high build cache coverage. + * + * The mental model is that if X disables the cache, and Y depends on X, then: + * 1. Y must be built by the same VM that build X; + * 2. OR, Y must be rebuilt on each VM that needs it. + * Approach 1 is probably the better choice. + */ + for (const consumer of operation.consumers) { + initialClusters?.union(operation, consumer); + } + } + } +} diff --git a/libraries/rush-lib/src/logic/operations/ConsoleTimelinePlugin.ts b/libraries/rush-lib/src/logic/operations/ConsoleTimelinePlugin.ts index aff052574b4..770480762ce 100644 --- a/libraries/rush-lib/src/logic/operations/ConsoleTimelinePlugin.ts +++ b/libraries/rush-lib/src/logic/operations/ConsoleTimelinePlugin.ts @@ -1,17 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ITerminal } from '@rushstack/node-core-library'; -import { PrintUtilities } from '@rushstack/terminal'; -import colors from 'colors/safe'; -import { IPhase } from '../../api/CommandLineConfiguration'; -import { - ICreateOperationsContext, - IPhasedCommandPlugin, - PhasedCommandHooks -} from '../../pluginFramework/PhasedCommandHooks'; -import { IExecutionResult } from './IOperationExecutionResult'; +import type { ITerminal } from '@rushstack/terminal'; +import { Colorize, PrintUtilities } from '@rushstack/terminal'; + +import type { IPhase } from '../../api/CommandLineConfiguration'; +import type { IPhasedCommandPlugin, PhasedCommandHooks } from '../../pluginFramework/PhasedCommandHooks'; +import type { IExecutionResult, IOperationExecutionResult } from './IOperationExecutionResult'; import { OperationStatus } from './OperationStatus'; +import type { CobuildConfiguration } from '../../api/CobuildConfiguration'; +import type { OperationExecutionRecord } from './OperationExecutionRecord'; +import type { Operation } from './Operation'; const PLUGIN_NAME: 'ConsoleTimelinePlugin' = 'ConsoleTimelinePlugin'; @@ -52,12 +51,22 @@ export class ConsoleTimelinePlugin implements IPhasedCommandPlugin { } public apply(hooks: PhasedCommandHooks): void { - hooks.afterExecuteOperations.tap( - PLUGIN_NAME, - (result: IExecutionResult, context: ICreateOperationsContext): void => { - _printTimeline(this._terminal, result); - } - ); + hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph, context) => { + graph.hooks.afterExecuteIterationAsync.tap( + PLUGIN_NAME, + ( + status: OperationStatus, + operationResults: ReadonlyMap + ): OperationStatus => { + _printTimeline({ + terminal: this._terminal, + result: { status, operationResults }, + cobuildConfiguration: context.cobuildConfiguration + }); + return status; + } + ); + }); } } @@ -70,7 +79,9 @@ const TIMELINE_WIDTH: number = 109; * Timeline - symbols representing each operation status */ const TIMELINE_CHART_SYMBOLS: Record = { + [OperationStatus.Waiting]: '?', [OperationStatus.Ready]: '?', + [OperationStatus.Queued]: '?', [OperationStatus.Executing]: '?', [OperationStatus.Success]: '#', [OperationStatus.SuccessWithWarning]: '!', @@ -78,22 +89,33 @@ const TIMELINE_CHART_SYMBOLS: Record = { [OperationStatus.Blocked]: '.', [OperationStatus.Skipped]: '%', [OperationStatus.FromCache]: '%', - [OperationStatus.NoOp]: '%' + [OperationStatus.NoOp]: '%', + [OperationStatus.Aborted]: '@' }; +const COBUILD_REPORTABLE_STATUSES: Set = new Set([ + OperationStatus.Success, + OperationStatus.SuccessWithWarning, + OperationStatus.Failure, + OperationStatus.Blocked +]); + /** * Timeline - colorizer for each operation status */ const TIMELINE_CHART_COLORIZER: Record string> = { - [OperationStatus.Ready]: colors.yellow, - [OperationStatus.Executing]: colors.yellow, - [OperationStatus.Success]: colors.green, - [OperationStatus.SuccessWithWarning]: colors.yellow, - [OperationStatus.Failure]: colors.red, - [OperationStatus.Blocked]: colors.red, - [OperationStatus.Skipped]: colors.green, - [OperationStatus.FromCache]: colors.green, - [OperationStatus.NoOp]: colors.gray + [OperationStatus.Waiting]: Colorize.yellow, + [OperationStatus.Ready]: Colorize.yellow, + [OperationStatus.Queued]: Colorize.yellow, + [OperationStatus.Executing]: Colorize.yellow, + [OperationStatus.Success]: Colorize.green, + [OperationStatus.SuccessWithWarning]: Colorize.yellow, + [OperationStatus.Failure]: Colorize.red, + [OperationStatus.Blocked]: Colorize.red, + [OperationStatus.Skipped]: Colorize.green, + [OperationStatus.FromCache]: Colorize.green, + [OperationStatus.NoOp]: Colorize.gray, + [OperationStatus.Aborted]: Colorize.red }; interface ITimelineRecord { @@ -102,18 +124,34 @@ interface ITimelineRecord { durationString: string; name: string; status: OperationStatus; + isExecuteByOtherCobuildRunner: boolean; } + +/** + * @internal + */ +export interface IPrintTimelineParameters { + terminal: ITerminal; + result: IExecutionResult; + cobuildConfiguration?: CobuildConfiguration; +} + +interface ICachedDuration { + cached?: number; + uncached: number; +} + /** * Print a more detailed timeline and analysis of CPU usage for the build. * @internal */ -export function _printTimeline(terminal: ITerminal, result: IExecutionResult): void { +export function _printTimeline({ terminal, result }: IPrintTimelineParameters): void { // // Gather the operation records we'll be displaying. Do some inline max() // finding to reduce the number of times we need to loop through operations. // - const durationByPhase: Map = new Map(); + const durationByPhase: Map = new Map(); const data: ITimelineRecord[] = []; let longestNameLength: number = 0; @@ -123,22 +161,38 @@ export function _printTimeline(terminal: ITerminal, result: IExecutionResult): v let workDuration: number = 0; for (const [operation, operationResult] of result.operationResults) { - if (operation.runner?.silent) { + if (operationResult.silent) { continue; } const { stopwatch } = operationResult; + const { _operationMetadataManager: operationMetadataManager } = + operationResult as OperationExecutionRecord; + + let { startTime } = stopwatch; + const { endTime } = stopwatch; - const { startTime, endTime } = stopwatch; + const duration: ICachedDuration = { cached: undefined, uncached: stopwatch.duration }; if (startTime && endTime) { const nameLength: number = operation.name?.length || 0; if (nameLength > longestNameLength) { longestNameLength = nameLength; } + const wasCobuilt: boolean = !!operationMetadataManager?.wasCobuilt; + if ( + wasCobuilt && + operationResult.status !== OperationStatus.FromCache && + operationResult.nonCachedDurationMs + ) { + duration.cached = stopwatch.duration; + startTime = Math.max(0, endTime - operationResult.nonCachedDurationMs); + duration.uncached = (endTime - startTime) / 1000; + } - const { duration } = stopwatch; - const durationString: string = duration.toFixed(1); + workDuration += stopwatch.duration; + + const durationString: string = duration.uncached.toFixed(1); const durationLength: number = durationString.length; if (durationLength > longestDurationLength) { longestDurationLength = durationLength; @@ -150,20 +204,31 @@ export function _printTimeline(terminal: ITerminal, result: IExecutionResult): v if (startTime < allStart) { allStart = startTime; } - workDuration += duration; const { associatedPhase } = operation; if (associatedPhase) { - durationByPhase.set(associatedPhase, (durationByPhase.get(associatedPhase) || 0) + duration); + let durationRecord: ICachedDuration | undefined = durationByPhase.get(associatedPhase); + if (!durationRecord) { + durationRecord = { + cached: undefined, + uncached: 0 + }; + durationByPhase.set(associatedPhase, durationRecord); + } + if (duration.cached !== undefined) { + durationRecord.cached = (durationRecord.cached ?? 0) + duration.cached; + } + durationRecord.uncached += duration.uncached; } data.push({ startTime, endTime, durationString, - name: operation.name!, - status: operationResult.status + name: operation.name, + status: operationResult.status, + isExecuteByOtherCobuildRunner: wasCobuilt }); } } @@ -203,7 +268,19 @@ export function _printTimeline(terminal: ITerminal, result: IExecutionResult): v terminal.writeLine(''); terminal.writeLine('='.repeat(maxWidth)); - for (const { startTime, endTime, durationString, name, status } of data) { + let hasCobuildSymbol: boolean = false; + + function getChartSymbol(record: ITimelineRecord): string { + const { isExecuteByOtherCobuildRunner, status } = record; + if (isExecuteByOtherCobuildRunner && COBUILD_REPORTABLE_STATUSES.has(status)) { + hasCobuildSymbol = true; + return 'C'; + } + return TIMELINE_CHART_SYMBOLS[status]; + } + + for (const record of data) { + const { startTime, endTime, durationString, name, status } = record; // Track busy CPUs const openCpu: number = getOpenCPU(startTime); busyCpus[openCpu] = endTime; @@ -214,11 +291,11 @@ export function _printTimeline(terminal: ITerminal, result: IExecutionResult): v const length: number = endIdx - startIdx + 1; const chart: string = - colors.gray('-'.repeat(startIdx)) + - TIMELINE_CHART_COLORIZER[status](TIMELINE_CHART_SYMBOLS[status].repeat(length)) + - colors.gray('-'.repeat(chartWidth - endIdx)); + Colorize.gray('-'.repeat(startIdx)) + + TIMELINE_CHART_COLORIZER[status](getChartSymbol(record).repeat(length)) + + Colorize.gray('-'.repeat(chartWidth - endIdx)); terminal.writeLine( - `${colors.cyan(name.padStart(longestNameLength))} ${chart} ${colors.white( + `${Colorize.cyan(name.padStart(longestNameLength))} ${chart} ${Colorize.white( durationString.padStart(longestDurationLength) + 's' )}` ); @@ -232,19 +309,27 @@ export function _printTimeline(terminal: ITerminal, result: IExecutionResult): v const usedCpus: number = busyCpus.length; - const legend: string[] = ['LEGEND:', ' [#] Success [!] Failed/warnings [%] Skipped/cached/no-op']; + const legend: string[] = [ + 'LEGEND:', + ' [#] Success [!] Failed/warnings [%] Skipped/cached/no-op', + '', + '' + ]; + if (hasCobuildSymbol) { + legend[2] = ' [C] Cobuild'; + } const summary: string[] = [ `Total Work: ${workDuration.toFixed(1)}s`, - `Wall Clock: ${allDurationSeconds.toFixed(1)}s` + `Wall Clock: ${allDurationSeconds.toFixed(1)}s`, + `Max Parallelism Used: ${usedCpus}`, + `Avg Parallelism Used: ${(workDuration / allDurationSeconds).toFixed(1)}` ]; terminal.writeLine(legend[0] + summary[0].padStart(maxWidth - legend[0].length)); terminal.writeLine(legend[1] + summary[1].padStart(maxWidth - legend[1].length)); - terminal.writeLine(`Max Parallelism Used: ${usedCpus}`.padStart(maxWidth)); - terminal.writeLine( - `Avg Parallelism Used: ${(workDuration / allDurationSeconds).toFixed(1)}`.padStart(maxWidth) - ); + terminal.writeLine(legend[2] + summary[2].padStart(maxWidth - legend[2].length)); + terminal.writeLine(legend[3] + summary[3].padStart(maxWidth - legend[3].length)); // // Include time-by-phase, if phases are enabled @@ -262,7 +347,11 @@ export function _printTimeline(terminal: ITerminal, result: IExecutionResult): v } for (const [phase, duration] of durationByPhase.entries()) { - terminal.writeLine(` ${colors.cyan(phase.name.padStart(maxPhaseName))} ${duration.toFixed(1)}s`); + const cachedDurationString: string = duration.cached + ? `, from cache: ${duration.cached.toFixed(1)}s` + : ''; + const durationString: string = `${duration.uncached.toFixed(1)}s${cachedDurationString}`; + terminal.writeLine(` ${Colorize.cyan(phase.name.padStart(maxPhaseName))} ${durationString}`); } } diff --git a/libraries/rush-lib/src/logic/operations/DebugHashesPlugin.ts b/libraries/rush-lib/src/logic/operations/DebugHashesPlugin.ts new file mode 100644 index 00000000000..343f9b906ad --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/DebugHashesPlugin.ts @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { Colorize, type ITerminal } from '@rushstack/terminal'; + +import type { IPhasedCommandPlugin, PhasedCommandHooks } from '../../pluginFramework/PhasedCommandHooks'; +import type { Operation } from './Operation'; +import type { IConfigurableOperation, IOperationStateHashComponents } from './IOperationExecutionResult'; + +const PLUGIN_NAME: 'DebugHashesPlugin' = 'DebugHashesPlugin'; + +export class DebugHashesPlugin implements IPhasedCommandPlugin { + private readonly _terminal: ITerminal; + + public constructor(terminal: ITerminal) { + this._terminal = terminal; + } + + public apply(hooks: PhasedCommandHooks): void { + hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph) => { + graph.hooks.configureIteration.tap( + PLUGIN_NAME, + (operations: ReadonlyMap) => { + const terminal: ITerminal = this._terminal; + terminal.writeLine(Colorize.blue(`===== Begin Hash Computation =====`)); + for (const [operation, record] of operations) { + terminal.writeLine(Colorize.cyan(`--- ${operation.name} ---`)); + const { dependencies, local, config }: IOperationStateHashComponents = + record.getStateHashComponents(); + for (const dep of dependencies) { + terminal.writeLine(dep); + } + terminal.writeLine(`local=${local}`); + terminal.writeLine(`config=${config}`); + terminal.writeLine(Colorize.green(`Result: ${record.getStateHash()}`)); + // Add a blank line between operations to visually separate them + terminal.writeLine(); + } + terminal.writeLine(Colorize.blue(`===== End Hash Computation =====`)); + } + ); + }); + } +} diff --git a/libraries/rush-lib/src/logic/operations/IOperationExecutionResult.ts b/libraries/rush-lib/src/logic/operations/IOperationExecutionResult.ts index ebb3e892cc2..8df76ec1b69 100644 --- a/libraries/rush-lib/src/logic/operations/IOperationExecutionResult.ts +++ b/libraries/rush-lib/src/logic/operations/IOperationExecutionResult.ts @@ -1,16 +1,79 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { StdioSummarizer } from '@rushstack/terminal'; +import type { StdioSummarizer, IProblemCollector } from '@rushstack/terminal'; + import type { OperationStatus } from './OperationStatus'; -import type { IStopwatchResult } from '../../utilities/Stopwatch'; +import type { IOperationLastState } from './IOperationRunner'; import type { Operation } from './Operation'; +import type { IStopwatchResult } from '../../utilities/Stopwatch'; +import type { ILogFilePaths } from './ProjectLogWritable'; + +/** + * Structured components of the state hash for an operation. + * @alpha + */ +export interface IOperationStateHashComponents { + /** + * The state hashes of operation dependencies, sorted by name. + * Each entry is of the form `{dependencyName}={hash}`. + */ + readonly dependencies: readonly string[]; + /** + * The hash of the operation's own local inputs (e.g. tracked files, environment variables). + */ + readonly local: string; + /** + * The hash of the operation's configuration (e.g. CLI parameters). + */ + readonly config: string; +} + +/** + * @alpha + */ +export interface IBaseOperationExecutionResult { + /** + * The operation itself + */ + readonly operation: Operation; + + /** + * The relative path to the folder that contains operation metadata. This folder will be automatically included in cache entries. + */ + readonly metadataFolderPath: string; + + /** + * Gets the hash of the state of all registered inputs to this operation. + * Calling this method will throw if Git is not available. + */ + getStateHash(): string; + + /** + * Gets the structured components of the state hash. This is useful for debugging and + * incremental change detection. + * Calling this method will throw if Git is not available. + */ + getStateHashComponents(): IOperationStateHashComponents; +} + +/** + * The `IConfigurableOperation` interface represents an {@link Operation} whose + * execution can be configured before running. + * @alpha + */ +export interface IConfigurableOperation extends IBaseOperationExecutionResult { + /** + * True if the operation should execute in this iteration, false otherwise. + */ + enabled: boolean; +} /** * The `IOperationExecutionResult` interface represents the results of executing an {@link Operation}. * @alpha */ -export interface IOperationExecutionResult { +export interface IOperationExecutionResult extends IBaseOperationExecutionResult, IOperationLastState { /** * The current execution status of an operation. Operations start in the 'ready' state, * but can be 'blocked' if an upstream operation failed. It is 'executing' when @@ -23,6 +86,14 @@ export interface IOperationExecutionResult { * it later (for example to re-print errors at end of execution). */ readonly error: Error | undefined; + /** + * If this operation is only present in the graph to maintain dependency relationships, this flag will be set to true. + */ + readonly silent: boolean; + /** + * True if the operation should execute in this iteration, false otherwise. + */ + readonly enabled: boolean; /** * Object tracking execution timing. */ @@ -31,10 +102,18 @@ export interface IOperationExecutionResult { * Object used to report a summary at the end of the Rush invocation. */ readonly stdioSummarizer: StdioSummarizer; + /** + * Object used to collect problems (errors/warnings/info) encountered during the operation. + */ + readonly problemCollector: IProblemCollector; /** * The value indicates the duration of the same operation without cache hit. */ readonly nonCachedDurationMs: number | undefined; + /** + * The paths to the log files, if applicable. + */ + readonly logFilePaths: ILogFilePaths | undefined; } /** diff --git a/libraries/rush-lib/src/logic/operations/IOperationGraph.ts b/libraries/rush-lib/src/logic/operations/IOperationGraph.ts new file mode 100644 index 00000000000..294a6205675 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/IOperationGraph.ts @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { TerminalWritable } from '@rushstack/terminal'; + +import type { Operation } from './Operation'; +import type { IOperationExecutionResult } from './IOperationExecutionResult'; +import type { Parallelism } from './ParseParallelism'; +import type { OperationStatus } from './OperationStatus'; +import type { IInputsSnapshot } from '../incremental/InputsSnapshot'; +import type { OperationGraphHooks } from '../../pluginFramework/OperationGraphHooks'; + +/** + * Options for a single iteration of operation execution. + * @alpha + */ +export interface IOperationGraphIterationOptions { + inputsSnapshot?: IInputsSnapshot; + + /** + * The time when the iteration was scheduled, if available, as returned by `performance.now()`. + */ + startTime?: number; +} + +/** + * Public API for the operation graph. + * @alpha + */ +export interface IOperationGraph { + /** + * Hooks into the execution process for operations + */ + readonly hooks: OperationGraphHooks; + + /** + * The set of operations in the graph. + */ + readonly operations: ReadonlySet; + + /** + * A map from each `Operation` in the graph to its current result record. + * The map is updated in real time as operations execute during an iteration. + * Only statuses representing a completed execution (e.g. `Success`, `Failure`, + * `SuccessWithWarning`) write to this map; statuses such as `Skipped` or `Aborted` — + * which indicate that an operation did not actually run — do not update it. + * For operations that have not yet run in the current iteration, the map retains the + * result from whichever prior iteration the operation last ran in. + * An entry with status `Ready` indicates that the operation is considered stale and + * has been queued to run again. + * Empty until at least one operation has completed execution. + */ + readonly resultByOperation: ReadonlyMap; + + /** + * The maximum allowed parallelism for this operation graph. + * Reads as a concrete integer. Accepts a `Parallelism` value and coerces it on write. + */ + get parallelism(): number; + set parallelism(value: Parallelism); + + /** + * If additional debug information should be printed during execution. + */ + debugMode: boolean; + + /** + * If true, operations will be executed in "quiet mode" where only errors are reported. + */ + quietMode: boolean; + + /** + * If true, allow operations to oversubscribe the CPU. Defaults to true. + */ + allowOversubscription: boolean; + + /** + * When true, the operation graph will pause before running the next iteration (manual mode). + * When false, iterations run automatically when scheduled. + */ + pauseNextIteration: boolean; + + /** + * The current overall status of the execution. + */ + readonly status: OperationStatus; + + /** + * The current set of terminal destinations. + */ + readonly terminalDestinations: ReadonlySet; + + /** + * True if there is a scheduled (but not yet executing) iteration. + * This will be false while an iteration is actively executing, or when no work is scheduled. + */ + readonly hasScheduledIteration: boolean; + + /** + * AbortController controlling the lifetime of the overall session (e.g. watch mode). + * Aborting this controller should signal all listeners (such as file system watchers) to dispose + * and prevent further iterations from being scheduled. + */ + readonly abortController: AbortController; + + /** + * Abort the current execution iteration, if any. Operations that have already started + * will run to completion; only operations that have not yet begun will be aborted. + */ + abortCurrentIterationAsync(): Promise; + + /** + * Cleans up any resources used by the operation runners, if applicable. + * @param operations - The operations whose runners should be closed, or undefined to close all runners. + */ + closeRunnersAsync(operations?: Iterable): Promise; + + /** + * Executes a single iteration of the operations. + * @param options - Options for this execution iteration. + * @returns A promise that resolves to true if the iteration has work to be done, or false if the iteration was empty and therefore not scheduled. + */ + scheduleIterationAsync(options: IOperationGraphIterationOptions): Promise; + + /** + * Executes all operations in the currently scheduled iteration, if any. + * @returns A promise which is resolved when all operations have been processed to a final state. + */ + executeScheduledIterationAsync(): Promise; + + /** + * Invalidates the specified operations, causing them to be re-executed. + * @param operations - The operations to invalidate, or undefined to invalidate all operations. + * @param reason - Optional reason for invalidation. + */ + invalidateOperations(operations?: Iterable, reason?: string): void; + + /** + * Sets the enabled state for a collection of operations. + * + * @param operations - The operations whose enabled state should be updated. + * @param targetState - The target enabled state to apply. + * @param mode - 'unsafe' to directly mutate only the provided operations, 'safe' to also enable + * transitive dependencies of enabled operations and disable transitive dependents of disabled operations. + * @returns true if any operation's enabled state changed, false otherwise. + */ + setEnabledStates( + operations: Iterable, + targetState: Operation['enabled'], + mode: 'safe' | 'unsafe' + ): boolean; + + /** + * Adds a terminal destination for output. Only new output will be sent to the destination. + * @param destination - The destination to add. + */ + addTerminalDestination(destination: TerminalWritable): void; + + /** + * Removes a terminal destination for output. Optionally closes the stream. + * New output will no longer be sent to the destination. + * @param destination - The destination to remove. + * @param close - Whether to close the stream. Defaults to `true`. + */ + removeTerminalDestination(destination: TerminalWritable, close?: boolean): boolean; +} diff --git a/libraries/rush-lib/src/logic/operations/IOperationRunner.ts b/libraries/rush-lib/src/logic/operations/IOperationRunner.ts index 22062e82b71..e8dc3f2d10d 100644 --- a/libraries/rush-lib/src/logic/operations/IOperationRunner.ts +++ b/libraries/rush-lib/src/logic/operations/IOperationRunner.ts @@ -1,12 +1,25 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { StdioSummarizer } from '@rushstack/terminal'; +import type { ITerminal, ITerminalProvider } from '@rushstack/terminal'; import type { CollatedWriter } from '@rushstack/stream-collator'; import type { OperationStatus } from './OperationStatus'; import type { OperationMetadataManager } from './OperationMetadataManager'; import type { IStopwatchResult } from '../../utilities/Stopwatch'; +import type { IEnvironment } from '../../utilities/Utilities'; + +/** + * A snapshot of a previous operation execution, passed to runners to inform incremental behavior. + * + * @beta + */ +export interface IOperationLastState { + /** + * The status from the previous execution of this operation. + */ + readonly status: OperationStatus; +} /** * Information passed to the executing `IOperationRunner` @@ -26,25 +39,60 @@ export interface IOperationRunnerContext { * Defaults to `true`. Will be `false` if Rush was invoked with `--verbose`. */ quietMode: boolean; - /** - * Object used to report a summary at the end of the Rush invocation. - */ - stdioSummarizer: StdioSummarizer; /** * Object used to manage metadata of the operation. * * @internal */ - _operationMetadataManager?: OperationMetadataManager; + _operationMetadataManager: OperationMetadataManager; /** * Object used to track elapsed time. */ stopwatch: IStopwatchResult; + /** + * The current execution status of an operation. Operations start in the 'ready' state, + * but can be 'blocked' if an upstream operation failed. It is 'executing' when + * the operation is executing. Once execution is complete, it is either 'success' or + * 'failure'. + */ + status: OperationStatus; + + /** + * The environment in which the operation is being executed. + * A return value of `undefined` indicates that it should inherit the environment from the parent process. + */ + environment: IEnvironment | undefined; + + /** + * Error which occurred while executing this operation, this is stored in case we need + * it later (for example to re-print errors at end of execution). + */ + error?: Error; + + /** + * Returns a callback that invalidates this operation so that it will be re-executed in the next iteration. + * The returned callback captures only the minimal state needed, avoiding retention of the full context. + * Callers should store the result rather than calling this method repeatedly. + */ + getInvalidateCallback(): (reason: string) => void; + + /** + * Invokes the specified callback with a terminal that is associated with this operation. + * + * Will write to a log file corresponding to the phase and project, and clean it up upon completion. + */ + runWithTerminalAsync( + callback: (terminal: ITerminal, terminalProvider: ITerminalProvider) => Promise, + options: { + createLogFile: boolean; + logFileSuffix?: string; + } + ): Promise; } /** * The `Operation` class is a node in the dependency graph of work that needs to be scheduled by the - * `OperationExecutionManager`. Each `Operation` has a `runner` member of type `IOperationRunner`, whose + * `OperationGraph`. Each `Operation` has a `runner` member of type `IOperationRunner`, whose * implementation manages the actual process for running a single operation. * * @beta @@ -56,9 +104,9 @@ export interface IOperationRunner { readonly name: string; /** - * This flag determines if the operation is allowed to be skipped if up to date. + * Whether or not the operation is cacheable. If false, all cache engines will be disabled for this operation. */ - isSkipAllowed: boolean; + cacheable: boolean; /** * Indicates that this runner's duration has meaning. @@ -77,12 +125,33 @@ export interface IOperationRunner { warningsAreAllowed: boolean; /** - * Indicates if the output of this operation may be written to the cache + * If set to true, this operation is considered a no-op and can be considered always skipped for + * analysis purposes. */ - isCacheWriteAllowed: boolean; + readonly isNoOp?: boolean; + + /** + * If true, this runner currently owns some kind of active resource (e.g. a service or a watch process). + * This can be used to determine if the operation is "in progress" even if it is not currently executing. + * If the runner supports this property, it should update it as appropriate during execution. + * The property is optional to avoid breaking existing implementations of IOperationRunner. + */ + readonly isActive?: boolean; /** * Method to be executed for the operation. + * @param context - The context object containing information about the execution environment. + * @param lastState - The last execution result of this operation, if any. + */ + executeAsync(context: IOperationRunnerContext, lastState?: IOperationLastState): Promise; + + /** + * Return a hash of the configuration that affects the operation. + */ + getConfigHash(): string; + + /** + * If this runner performs any background work to optimize future runs, this method will clean it up. */ - executeAsync(context: IOperationRunnerContext): Promise; + closeAsync?(): Promise; } diff --git a/libraries/rush-lib/src/logic/operations/IPCOperationRunner.ts b/libraries/rush-lib/src/logic/operations/IPCOperationRunner.ts new file mode 100644 index 00000000000..6d8396591c5 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/IPCOperationRunner.ts @@ -0,0 +1,250 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { ChildProcess } from 'node:child_process'; +import { once } from 'node:events'; + +import type { + IAfterExecuteEventMessage, + IRequestRunEventMessage, + ISyncEventMessage, + IRunCommandMessage, + IExitCommandMessage +} from '@rushstack/operation-graph'; +import { TerminalProviderSeverity, type ITerminal, type ITerminalProvider } from '@rushstack/terminal'; + +import type { IPhase } from '../../api/CommandLineConfiguration'; +import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import { Utilities } from '../../utilities/Utilities'; +import type { IOperationRunner, IOperationRunnerContext, IOperationLastState } from './IOperationRunner'; +import { OperationError } from './OperationError'; +import { OperationStatus } from './OperationStatus'; + +export interface IIPCOperationRunnerOptions { + phase: IPhase; + project: RushConfigurationProject; + name: string; + initialCommand: string; + incrementalCommand: string | undefined; + commandForHash: string; + persist: boolean; + ignoredParameterValues: ReadonlyArray; +} + +function isAfterExecuteEventMessage(message: unknown): message is IAfterExecuteEventMessage { + return typeof message === 'object' && (message as IAfterExecuteEventMessage).event === 'after-execute'; +} + +function isRequestRunEventMessage(message: unknown): message is IRequestRunEventMessage { + return typeof message === 'object' && (message as IRequestRunEventMessage).event === 'requestRun'; +} + +function isSyncEventMessage(message: unknown): message is ISyncEventMessage { + return typeof message === 'object' && (message as ISyncEventMessage).event === 'sync'; +} + +/** + * Runner that hosts a long-lived process to which it communicates via IPC. + */ +export class IPCOperationRunner implements IOperationRunner { + public readonly name: string; + public readonly cacheable: boolean = false; + public readonly reportTiming: boolean = true; + public readonly silent: boolean = false; + public readonly warningsAreAllowed: boolean; + + private readonly _rushProject: RushConfigurationProject; + private readonly _initialCommand: string; + private readonly _incrementalCommand: string | undefined; + private readonly _commandForHash: string; + private readonly _persist: boolean; + private readonly _ignoredParameterValues: ReadonlyArray; + + private _ipcProcess: ChildProcess | undefined; + private _processReadyPromise: Promise | undefined; + + public constructor(options: IIPCOperationRunnerOptions) { + const { + name, + phase: { allowWarningsOnSuccess = false }, + project, + initialCommand, + incrementalCommand, + commandForHash, + persist, + ignoredParameterValues + } = options; + this.name = name; + this.warningsAreAllowed = + EnvironmentConfiguration.allowWarningsInSuccessfulBuild || allowWarningsOnSuccess; + this._rushProject = project; + this._initialCommand = initialCommand; + this._incrementalCommand = incrementalCommand; + this._commandForHash = commandForHash; + + this._persist = persist; + this._ignoredParameterValues = ignoredParameterValues; + } + + public get isActive(): boolean { + return !!(this._ipcProcess && !this._ipcProcess.killed && typeof this._ipcProcess.exitCode !== 'number'); + } + + public async executeAsync( + context: IOperationRunnerContext, + lastState?: IOperationLastState + ): Promise { + const commandToRun: string = + lastState && this._incrementalCommand ? this._incrementalCommand : this._initialCommand; + const invalidate: (reason: string) => void = context.getInvalidateCallback(); + return await context.runWithTerminalAsync( + async (terminal: ITerminal, terminalProvider: ITerminalProvider): Promise => { + let isConnected: boolean = false; + if (!this._ipcProcess || typeof this._ipcProcess.exitCode === 'number') { + // Log any ignored parameters + if (this._ignoredParameterValues.length > 0) { + terminal.writeLine( + `These parameters were ignored for this operation by project-level configuration: ${this._ignoredParameterValues.join(' ')}` + ); + } + + // Run the operation + terminal.writeLine('Invoking: ' + commandToRun); + + const { rushConfiguration, projectFolder } = this._rushProject; + + const { environment: initialEnvironment } = context; + + this._ipcProcess = Utilities.executeLifecycleCommandAsync(commandToRun, { + rushConfiguration, + workingDirectory: projectFolder, + initCwd: rushConfiguration.commonTempFolder, + handleOutput: true, + environmentPathOptions: { + includeProjectBin: true + }, + ipc: true, + connectSubprocessTerminator: true, + initialEnvironment + }); + + let resolveReadyPromise!: () => void; + + this._processReadyPromise = new Promise((resolve) => { + resolveReadyPromise = resolve; + }); + + this._ipcProcess.on('message', (message: unknown) => { + if (isRequestRunEventMessage(message)) { + const reason: string = message.detail + ? `${message.requestor}: ${message.detail}` + : message.requestor; + invalidate(reason); + } else if (isSyncEventMessage(message)) { + resolveReadyPromise(); + } + }); + } else { + terminal.writeLine(`Connecting to existing IPC process...`); + } + const subProcess: ChildProcess = this._ipcProcess; + let hasWarningOrError: boolean = false; + + function onStdout(data: Buffer): void { + const text: string = data.toString(); + terminalProvider.write(text, TerminalProviderSeverity.log); + } + function onStderr(data: Buffer): void { + const text: string = data.toString(); + terminalProvider.write(text, TerminalProviderSeverity.error); + hasWarningOrError = true; + } + + // Hook into events, in order to get live streaming of the log + subProcess.stdout?.on('data', onStdout); + subProcess.stderr?.on('data', onStderr); + + const status: OperationStatus = await new Promise((resolve, reject) => { + function finishHandler(message: unknown): void { + if (isAfterExecuteEventMessage(message)) { + terminal.writeLine('Received finish notification'); + subProcess.stdout?.off('data', onStdout); + subProcess.stderr?.off('data', onStderr); + subProcess.off('message', finishHandler); + subProcess.off('error', reject); + subProcess.off('exit', onExit); + terminal.writeLine('Disconnected from IPC process'); + // These types are currently distinct but have the same underlying values + resolve(message.status as unknown as OperationStatus); + } + } + + function onExit(exitCode: number | null, signal: NodeJS.Signals | null): void { + try { + if (signal) { + context.error = new OperationError('error', `Terminated by signal: ${signal}`); + resolve(OperationStatus.Failure); + } else if (exitCode !== 0) { + // Do NOT reject here immediately, give a chance for other logic to suppress the error + context.error = new OperationError('error', `Returned error code: ${exitCode}`); + resolve(OperationStatus.Failure); + } else if (hasWarningOrError) { + resolve(OperationStatus.SuccessWithWarning); + } else { + resolve(OperationStatus.Success); + } + } catch (error) { + reject(error as OperationError); + } + } + + subProcess.on('message', finishHandler); + subProcess.on('error', reject); + subProcess.on('exit', onExit); + + this._processReadyPromise!.then(() => { + isConnected = true; + terminal.writeLine('Child supports IPC protocol. Sending "run" command...'); + const runCommand: IRunCommandMessage = { + command: 'run' + }; + subProcess.send(runCommand); + }, reject); + }); + + if (isConnected && !this._persist) { + await this.closeAsync(); + } + + // @rushstack/operation-graph does not currently have a concept of "Success with Warning" + // To match existing ShellOperationRunner behavior we treat any stderr as a warning. + return status === OperationStatus.Success && hasWarningOrError + ? OperationStatus.SuccessWithWarning + : status; + }, + { + createLogFile: true + } + ); + } + + public getConfigHash(): string { + return this._commandForHash; + } + + public async closeAsync(): Promise { + const { _ipcProcess: subProcess } = this; + if (!subProcess) { + return; + } + + if (subProcess.connected) { + const exitCommand: IExitCommandMessage = { + command: 'exit' + }; + subProcess.send(exitCommand); + await once(subProcess, 'exit'); + } + } +} diff --git a/libraries/rush-lib/src/logic/operations/IPCOperationRunnerPlugin.ts b/libraries/rush-lib/src/logic/operations/IPCOperationRunnerPlugin.ts new file mode 100644 index 00000000000..7d5cccd7851 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/IPCOperationRunnerPlugin.ts @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { + ICreateOperationsContext, + IPhasedCommandPlugin, + PhasedCommandHooks +} from '../../pluginFramework/PhasedCommandHooks'; +import { IPCOperationRunner } from './IPCOperationRunner'; +import type { Operation } from './Operation'; +import { + PLUGIN_NAME as ShellOperationPluginName, + formatCommand, + getCustomParameterValuesByOperation, + type ICustomParameterValuesForOperation, + getDisplayName +} from './ShellOperationRunnerPlugin'; + +const PLUGIN_NAME: 'IPCOperationRunnerPlugin' = 'IPCOperationRunnerPlugin'; + +/** + * Plugin that implements compatible phases via IPC to a long-lived watch process. + */ +export class IPCOperationRunnerPlugin implements IPhasedCommandPlugin { + public apply(hooks: PhasedCommandHooks): void { + hooks.createOperationsAsync.tap( + { + name: PLUGIN_NAME, + before: ShellOperationPluginName + }, + (operations: Set, context: ICreateOperationsContext) => { + const { isWatch, isIncrementalBuildAllowed } = context; + if (!isWatch || !isIncrementalBuildAllowed) { + return operations; + } + + const getCustomParameterValues: (operation: Operation) => ICustomParameterValuesForOperation = + getCustomParameterValuesByOperation(); + + for (const operation of operations) { + const { associatedPhase: phase, associatedProject: project, runner } = operation; + + if (runner) { + continue; + } + + const { scripts } = project.packageJson; + if (!scripts) { + continue; + } + + const { name: phaseName } = phase; + + const incrementalScript: string | undefined = scripts[`${phaseName}:incremental:ipc`]; + let initialScript: string | undefined = scripts[`${phaseName}:ipc`]; + + // Both must be absent to skip. A project may define only one of `_phase:ipc` or + // `_phase:incremental:ipc` — defining either opts into the IPC runner. + if (!initialScript && !incrementalScript) { + continue; + } + + initialScript ??= scripts[phaseName]; + + // This is the command that will be used to identify the cache entry for this operation, to allow + // for this operation (or downstream operations) to be restored from the build cache. + const commandForHash: string | undefined = phase.shellCommand ?? scripts?.[phaseName]; + + const { parameterValues: customParameterValues, ignoredParameterValues } = + getCustomParameterValues(operation); + const initialCommand: string = formatCommand(initialScript, customParameterValues); + const incrementalCommand: string | undefined = incrementalScript + ? formatCommand(incrementalScript, customParameterValues) + : undefined; + + const operationName: string = getDisplayName(phase, project); + const ipcOperationRunner: IPCOperationRunner = new IPCOperationRunner({ + phase, + project, + name: operationName, + initialCommand, + incrementalCommand, + commandForHash, + persist: true, + ignoredParameterValues + }); + + operation.runner = ipcOperationRunner; + } + + return operations; + } + ); + } +} diff --git a/libraries/rush-lib/src/logic/operations/IgnoredParametersPlugin.ts b/libraries/rush-lib/src/logic/operations/IgnoredParametersPlugin.ts new file mode 100644 index 00000000000..5ac89f7f733 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/IgnoredParametersPlugin.ts @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IPhasedCommandPlugin, PhasedCommandHooks } from '../../pluginFramework/PhasedCommandHooks'; +import type { IEnvironment } from '../../utilities/Utilities'; +import type { IOperationExecutionResult } from './IOperationExecutionResult'; + +const PLUGIN_NAME: 'IgnoredParametersPlugin' = 'IgnoredParametersPlugin'; + +/** + * Environment variable name for forwarding ignored parameters to child processes + * @public + */ +export const RUSHSTACK_CLI_IGNORED_PARAMETER_NAMES_ENV_VAR: 'RUSHSTACK_CLI_IGNORED_PARAMETER_NAMES' = + 'RUSHSTACK_CLI_IGNORED_PARAMETER_NAMES'; + +/** + * Phased command plugin that forwards the value of the `parameterNamesToIgnore` operation setting + * to child processes as the RUSHSTACK_CLI_IGNORED_PARAMETER_NAMES environment variable. + */ +export class IgnoredParametersPlugin implements IPhasedCommandPlugin { + public apply(hooks: PhasedCommandHooks): void { + hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph) => { + graph.hooks.createEnvironmentForOperation.tap( + PLUGIN_NAME, + (env: IEnvironment, record: IOperationExecutionResult) => { + const { settings } = record.operation; + + // If there are parameter names to ignore, set the environment variable + if (settings?.parameterNamesToIgnore && settings.parameterNamesToIgnore.length > 0) { + env[RUSHSTACK_CLI_IGNORED_PARAMETER_NAMES_ENV_VAR] = JSON.stringify( + settings.parameterNamesToIgnore + ); + } + + return env; + } + ); + }); + } +} diff --git a/libraries/rush-lib/src/logic/operations/LegacySkipPlugin.ts b/libraries/rush-lib/src/logic/operations/LegacySkipPlugin.ts new file mode 100644 index 00000000000..846362409c5 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/LegacySkipPlugin.ts @@ -0,0 +1,262 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; + +import { FileSystem, JsonFile, type JsonObject } from '@rushstack/node-core-library'; +import { PrintUtilities, Colorize, type ITerminal } from '@rushstack/terminal'; + +import type { Operation } from './Operation'; +import { OperationStatus } from './OperationStatus'; +import type { IPhasedCommandPlugin, PhasedCommandHooks } from '../../pluginFramework/PhasedCommandHooks'; +import type { IOperationGraphIterationOptions } from './IOperationGraph'; +import type { IOperationRunnerContext } from './IOperationRunner'; +import type { IOperationExecutionResult } from './IOperationExecutionResult'; + +const PLUGIN_NAME: 'LegacySkipPlugin' = 'LegacySkipPlugin'; + +function _areShallowEqual(object1: JsonObject, object2: JsonObject): boolean { + for (const n in object1) { + if (!(n in object2) || object1[n] !== object2[n]) { + return false; + } + } + for (const n in object2) { + if (!(n in object1)) { + return false; + } + } + return true; +} + +export interface IProjectDeps { + files: { [filePath: string]: string }; + arguments: string; +} + +interface ILegacySkipRecord { + allowSkip: boolean; + packageDeps: IProjectDeps | undefined; + packageDepsPath: string; +} + +export interface ILegacySkipPluginOptions { + terminal: ITerminal; + changedProjectsOnly: boolean; + isIncrementalBuildAllowed: boolean; + allowWarningsInSuccessfulBuild?: boolean; +} + +/** + * Core phased command plugin that implements the legacy skip detection logic, used when build cache is disabled. + */ +export class LegacySkipPlugin implements IPhasedCommandPlugin { + private readonly _options: ILegacySkipPluginOptions; + + public constructor(options: ILegacySkipPluginOptions) { + this._options = options; + } + + public apply(hooks: PhasedCommandHooks): void { + const stateMap: WeakMap = new WeakMap(); + + const { terminal, changedProjectsOnly, isIncrementalBuildAllowed, allowWarningsInSuccessfulBuild } = + this._options; + + hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph) => { + graph.hooks.beforeExecuteIterationAsync.tap( + PLUGIN_NAME, + ( + operations: ReadonlyMap, + iterationOptions: IOperationGraphIterationOptions + ): void => { + let logGitWarning: boolean = false; + const { inputsSnapshot } = iterationOptions; + + for (const record of operations.values()) { + const { operation } = record; + const { associatedProject, associatedPhase, runner, logFilenameIdentifier } = operation; + if (!runner) { + continue; + } + + if (!runner.cacheable) { + stateMap.set(operation, { + allowSkip: true, + packageDeps: undefined, + packageDepsPath: '' + }); + continue; + } + + const packageDepsFilename: string = `package-deps_${logFilenameIdentifier}.json`; + + const packageDepsPath: string = path.join( + associatedProject.projectRushTempFolder, + packageDepsFilename + ); + + let packageDeps: IProjectDeps | undefined; + + try { + const fileHashes: ReadonlyMap | undefined = + inputsSnapshot?.getTrackedFileHashesForOperation(associatedProject, associatedPhase.name); + + if (!fileHashes) { + logGitWarning = true; + continue; + } + + const files: Record = {}; + for (const [filePath, fileHash] of fileHashes) { + files[filePath] = fileHash; + } + + packageDeps = { + files, + arguments: runner.getConfigHash() + }; + } catch (error) { + // To test this code path: + // Delete a project's ".rush/temp/shrinkwrap-deps.json" then run "rush build --verbose" + terminal.writeLine( + `Unable to calculate incremental state for ${record.operation.name}: ` + + (error as Error).toString() + ); + terminal.writeLine( + Colorize.cyan('Rush will proceed without incremental execution and change detection.') + ); + } + + stateMap.set(operation, { + packageDepsPath, + packageDeps, + allowSkip: isIncrementalBuildAllowed + }); + } + + if (logGitWarning) { + // To test this code path: + // Remove the `.git` folder then run "rush build --verbose" + terminal.writeLine( + Colorize.cyan( + PrintUtilities.wrapWords( + 'This workspace does not appear to be tracked by Git. ' + + 'Rush will proceed without incremental execution, caching, and change detection.' + ) + ) + ); + } + } + ); + + graph.hooks.beforeExecuteOperationAsync.tapPromise( + PLUGIN_NAME, + async ( + record: IOperationRunnerContext & IOperationExecutionResult + ): Promise => { + const { operation } = record; + const skipRecord: ILegacySkipRecord | undefined = stateMap.get(operation); + if (!skipRecord) { + // This operation doesn't support skip detection. + return; + } + + if (!operation.runner!.cacheable) { + // This operation doesn't support skip detection. + return; + } + + const { associatedProject } = operation; + + const { packageDepsPath, packageDeps, allowSkip } = skipRecord; + + let lastProjectDeps: IProjectDeps | undefined = undefined; + + try { + const lastDepsContents: string = await FileSystem.readFileAsync(packageDepsPath); + lastProjectDeps = JSON.parse(lastDepsContents); + } catch (e) { + if (!FileSystem.isNotExistError(e)) { + // Warn and ignore - treat failing to load the file as the operation being not built. + // TODO: Update this to be the terminal specific to the operation. + terminal.writeWarningLine( + `Warning: error parsing ${packageDepsPath}: ${e}. Ignoring and treating this operation as not run.` + ); + } + } + + if (allowSkip) { + const isPackageUnchanged: boolean = !!( + lastProjectDeps && + packageDeps && + packageDeps.arguments === lastProjectDeps.arguments && + _areShallowEqual(packageDeps.files, lastProjectDeps.files) + ); + + if (isPackageUnchanged) { + return OperationStatus.Skipped; + } + } + + // TODO: Remove legacyDepsPath with the next major release of Rush + const legacyDepsPath: string = path.join(associatedProject.projectFolder, 'package-deps.json'); + + await Promise.all([ + // Delete the legacy package-deps.json + FileSystem.deleteFileAsync(legacyDepsPath), + + // If the deps file exists, remove it before starting execution. + FileSystem.deleteFileAsync(packageDepsPath) + ]); + } + ); + + graph.hooks.afterExecuteOperationAsync.tapPromise( + PLUGIN_NAME, + async (record: IOperationRunnerContext & IOperationExecutionResult): Promise => { + const { status, operation } = record; + + const skipRecord: ILegacySkipRecord | undefined = stateMap.get(operation); + if (!skipRecord) { + return; + } + + const blockSkip: boolean = + !skipRecord.allowSkip || + (!changedProjectsOnly && + (status === OperationStatus.Success || status === OperationStatus.SuccessWithWarning)); + if (blockSkip) { + for (const consumer of operation.consumers) { + const consumerSkipRecord: ILegacySkipRecord | undefined = stateMap.get(consumer); + if (consumerSkipRecord) { + consumerSkipRecord.allowSkip = false; + } + } + } + + if (!record.operation.runner!.cacheable) { + // This operation doesn't support skip detection. + return; + } + + const { packageDeps, packageDepsPath } = skipRecord; + + if ( + status === OperationStatus.NoOp || + (packageDeps && + (status === OperationStatus.Success || + (status === OperationStatus.SuccessWithWarning && + record.operation.runner!.warningsAreAllowed && + allowWarningsInSuccessfulBuild))) + ) { + // Write deps on success. + await JsonFile.saveAsync(packageDeps, packageDepsPath, { + ensureFolderExists: true + }); + } + } + ); + }); + } +} diff --git a/libraries/rush-lib/src/logic/operations/NodeDiagnosticDirPlugin.ts b/libraries/rush-lib/src/logic/operations/NodeDiagnosticDirPlugin.ts new file mode 100644 index 00000000000..70e1d88c69f --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/NodeDiagnosticDirPlugin.ts @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; + +import { FileSystem } from '@rushstack/node-core-library'; + +import type { IPhasedCommandPlugin, PhasedCommandHooks } from '../../pluginFramework/PhasedCommandHooks'; +import type { IEnvironment } from '../../utilities/Utilities'; +import type { Operation } from './Operation'; +import type { IOperationExecutionResult } from './IOperationExecutionResult'; + +const PLUGIN_NAME: 'NodeDiagnosticDirPlugin' = 'NodeDiagnosticDirPlugin'; + +export interface INodeDiagnosticDirPluginOptions { + diagnosticDir: string; +} + +/** + * Phased command plugin that configures the NodeJS --diagnostic-dir option to contain the project and phase name. + */ +export class NodeDiagnosticDirPlugin implements IPhasedCommandPlugin { + private readonly _diagnosticsDir: string; + + public constructor(options: INodeDiagnosticDirPluginOptions) { + this._diagnosticsDir = options.diagnosticDir; + } + + public apply(hooks: PhasedCommandHooks): void { + const getDiagnosticDir = (operation: Operation): string | undefined => { + const { associatedProject } = operation; + + const diagnosticDir: string = path.resolve( + this._diagnosticsDir, + associatedProject.packageName, + operation.logFilenameIdentifier + ); + + return diagnosticDir; + }; + + hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph) => { + graph.hooks.createEnvironmentForOperation.tap( + PLUGIN_NAME, + (env: IEnvironment, record: IOperationExecutionResult) => { + const diagnosticDir: string | undefined = getDiagnosticDir(record.operation); + if (!diagnosticDir) { + return env; + } + + // Not all versions of NodeJS create the directory, so ensure it exists: + FileSystem.ensureFolder(diagnosticDir); + + const { NODE_OPTIONS } = env; + + const diagnosticDirEnv: string = `--diagnostic-dir="${diagnosticDir}"`; + + env.NODE_OPTIONS = NODE_OPTIONS ? `${NODE_OPTIONS} ${diagnosticDirEnv}` : diagnosticDirEnv; + + return env; + } + ); + }); + } +} diff --git a/libraries/rush-lib/src/logic/operations/NullOperationRunner.ts b/libraries/rush-lib/src/logic/operations/NullOperationRunner.ts index 8e3f14fac25..03e9260bc7c 100644 --- a/libraries/rush-lib/src/logic/operations/NullOperationRunner.ts +++ b/libraries/rush-lib/src/logic/operations/NullOperationRunner.ts @@ -31,12 +31,11 @@ export class NullOperationRunner implements IOperationRunner { // This operation does nothing, so timing is meaningless public readonly reportTiming: boolean = false; public readonly silent: boolean; - // The operation may be skipped; it doesn't do anything anyway - public isSkipAllowed: boolean = true; - // The operation is a no-op, so is cacheable. - public isCacheWriteAllowed: boolean = true; + // The operation is a no-op, so it is faster to not cache it + public cacheable: boolean = false; // Nothing will get logged, no point allowing warnings public readonly warningsAreAllowed: boolean = false; + public readonly isNoOp: boolean = true; public readonly result: OperationStatus; @@ -49,4 +48,8 @@ export class NullOperationRunner implements IOperationRunner { public async executeAsync(context: IOperationRunnerContext): Promise { return this.result; } + + public getConfigHash(): string { + return ''; + } } diff --git a/libraries/rush-lib/src/logic/operations/Operation.ts b/libraries/rush-lib/src/logic/operations/Operation.ts index d74795cc246..6b57da4e2af 100644 --- a/libraries/rush-lib/src/logic/operations/Operation.ts +++ b/libraries/rush-lib/src/logic/operations/Operation.ts @@ -1,9 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; -import { IPhase } from '../../api/CommandLineConfiguration'; -import { IOperationRunner } from './IOperationRunner'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { IPhase } from '../../api/CommandLineConfiguration'; +import type { IOperationRunner } from './IOperationRunner'; +import type { IOperationSettings } from '../../api/RushProjectConfiguration'; +import { type Parallelism, parseParallelismPercent } from './ParseParallelism'; + +/** + * State for the `enabled` property of an `Operation`. + * + * - `true`: The operation should be executed if it or any dependencies changed. + * - `false`: The operation should be skipped. + * - `"ignore-dependency-changes"`: The operation should be executed if there are local changes in the project, + * otherwise it should be skipped. This is useful for operations like "test" where you may want to skip + * testing projects that haven't changed. + * @alpha + */ +export type OperationEnabledState = boolean | 'ignore-dependency-changes'; /** * Options for constructing a new Operation. @@ -11,23 +25,48 @@ import { IOperationRunner } from './IOperationRunner'; */ export interface IOperationOptions { /** - * The Rush phase associated with this Operation, if any + * The Rush phase associated with this Operation + */ + phase: IPhase; + + /** + * The Rush project associated with this Operation */ - phase?: IPhase | undefined; + project: RushConfigurationProject; + /** - * The Rush project associated with this Operation, if any + * If set to false, this operation will be skipped during evaluation (return OperationStatus.Skipped). + * This is useful for plugins to alter the scope of the operation graph across executions, + * e.g. to enable or disable unit test execution, or to include or exclude dependencies. + * + * The special value "ignore-dependency-changes" can be used to indicate that this operation should only + * be executed if there are local changes in the project. This is useful for operations like + * "test" where you may want to skip testing projects that haven't changed. + * + * The default value is `true`, meaning the operation will be executed if it or any dependencies change. */ - project?: RushConfigurationProject | undefined; + enabled?: OperationEnabledState; + /** * When the scheduler is ready to process this `Operation`, the `runner` implements the actual work of * running the operation. */ runner?: IOperationRunner | undefined; + + /** + * Settings defined in the project configuration for this operation, can be overridden. + */ + settings?: IOperationSettings | undefined; + + /** + * {@inheritDoc Operation.logFilenameIdentifier} + */ + logFilenameIdentifier: string; } /** * The `Operation` class is a node in the dependency graph of work that needs to be scheduled by the - * `OperationExecutionManager`. Each `Operation` has a `runner` member of type `IOperationRunner`, whose + * `OperationGraph`. Each `Operation` has a `runner` member of type `IOperationRunner`, whose * implementation manages the actual process of running a single operation. * * The graph of `Operation` instances will be cloned into a separate execution graph after processing. @@ -36,14 +75,14 @@ export interface IOperationOptions { */ export class Operation { /** - * The Rush phase associated with this Operation, if any + * The Rush phase associated with this Operation */ - public readonly associatedPhase: IPhase | undefined; + public readonly associatedPhase: IPhase; /** - * The Rush project associated with this Operation, if any + * The Rush project associated with this Operation */ - public readonly associatedProject: RushConfigurationProject | undefined; + public readonly associatedProject: RushConfigurationProject; /** * A set of all operations which depend on this operation. @@ -55,6 +94,13 @@ export class Operation { */ public readonly dependencies: ReadonlySet = new Set(); + /** + * This property is used in the name of the filename for the logs generated by this + * operation. This is a filesystem-safe version of the phase name. For example, + * an operation for a phase with name `_phase:compile` has a `logFilenameIdentifier` of `_phase_compile`. + */ + public logFilenameIdentifier: string; + /** * When the scheduler is ready to process this `Operation`, the `runner` implements the actual work of * running the operation. @@ -62,29 +108,77 @@ export class Operation { public runner: IOperationRunner | undefined = undefined; /** - * The weight for this operation. This scalar is the contribution of this operation to the - * `criticalPathLength` calculation above. Modify to indicate the following: - * - `weight` === 1: indicates that this operation has an average duration - * - `weight` > 1: indicates that this operation takes longer than average and so the scheduler - * should try to favor starting it over other, shorter operations. An example might be an operation that - * bundles an entire application and runs whole-program optimization. - * - `weight` < 1: indicates that this operation takes less time than average and so the scheduler - * should favor other, longer operations over it. An example might be an operation to unpack a cached - * output, or an operation using NullOperationRunner, which might use a value of 0. + * The concurrency weight for this operation. When coerced to an integer via `coerceParallelism`, + * this value represents how many concurrency slots the operation consumes while running. + * + * May be specified as: + * - A raw `number`: used directly as the slot count (e.g. `2` consumes two slots). + * - An `IParallelismScalar` (e.g. `{ scalar: 0.5 }`): coerced relative to the graph's + * configured `maxParallelism` at execution time, so the weight scales with the available + * concurrency rather than being fixed at parse time. + * + * Coerced values guide scheduling as follows: + * - `1` slot: typical operation consuming one logical thread. + * - `> 1` slots: operation that spawns multiple threads or requires significant RAM; reserving + * extra slots prevents overloading the machine (e.g. a whole-program bundler or a test suite + * that runs its own internal parallelism). + * - `0` slots: effectively free (e.g. a no-op or cache-restore step). + */ + public weight: Parallelism; + + /** + * Get the operation settings for this operation, defaults to the values defined in + * the project configuration. + */ + public settings: IOperationSettings | undefined = undefined; + + /** + * If set to false, this operation will be skipped during evaluation (return OperationStatus.Skipped). + * This is useful for plugins to alter the scope of the operation graph across executions, + * e.g. to enable or disable unit test execution, or to include or exclude dependencies. + * + * The special value "ignore-dependency-changes" can be used to indicate that this operation should only + * be executed if there are local changes in the project. This is useful for operations like + * "test" where you may want to skip testing projects that haven't changed. + * + * The default value is `true`, meaning the operation will be executed if it or any dependencies change. */ - public weight: number = 1; + public enabled: OperationEnabledState; - public constructor(options?: IOperationOptions) { - this.associatedPhase = options?.phase; - this.associatedProject = options?.project; - this.runner = options?.runner; + public constructor(options: IOperationOptions) { + const { phase, project, runner, settings, logFilenameIdentifier, enabled = true } = options; + this.associatedPhase = phase; + this.associatedProject = project; + this.runner = runner; + this.settings = settings; + this.logFilenameIdentifier = logFilenameIdentifier; + this.enabled = enabled; + this.weight = _getFinalWeight( + settings?.weight ?? 1, + runner?.name ?? `${project.packageName} (${phase.name})` + ); } /** * The name of this operation, for logging. */ - public get name(): string | undefined { - return this.runner?.name; + public get name(): string { + const { runner } = this; + if (!runner) { + throw new Error(`Cannot get name of an Operation that does not yet have a runner.`); + } + return runner.name; + } + + /** + * If set to true, this operation is considered a no-op and can be considered always skipped for analysis purposes. + */ + public get isNoOp(): boolean { + const { runner } = this; + if (!runner) { + throw new Error(`Cannot get isNoOp of an Operation that does not yet have a runner.`); + } + return !!runner.isNoOp; } /** @@ -105,3 +199,16 @@ export class Operation { (dependency.consumers as Set).delete(this); } } + +function _getFinalWeight(rawWeight: string | number, context: string): Parallelism { + if (typeof rawWeight === 'number') { + // Explicit numeric weight allows any value. + return rawWeight; + } else { + try { + return { scalar: parseParallelismPercent(rawWeight) }; + } catch (err) { + throw new Error(`Invalid weight for operation "${context}": ${err.message}`); + } + } +} diff --git a/libraries/rush-lib/src/logic/operations/OperationError.ts b/libraries/rush-lib/src/logic/operations/OperationError.ts index ac5d803016e..21a9446c6c2 100644 --- a/libraries/rush-lib/src/logic/operations/OperationError.ts +++ b/libraries/rush-lib/src/logic/operations/OperationError.ts @@ -14,11 +14,11 @@ export class OperationError extends Error { this._type = type; } - public get message(): string { + public override get message(): string { return `[${this._type}] '${super.message}'`; } - public toString(): string { + public override toString(): string { return this.message; } } diff --git a/libraries/rush-lib/src/logic/operations/OperationExecutionManager.ts b/libraries/rush-lib/src/logic/operations/OperationExecutionManager.ts deleted file mode 100644 index 4cc9847e9f9..00000000000 --- a/libraries/rush-lib/src/logic/operations/OperationExecutionManager.ts +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import colors from 'colors/safe'; -import { TerminalWritable, StdioWritable, TextRewriterTransform } from '@rushstack/terminal'; -import { StreamCollator, CollatedTerminal, CollatedWriter } from '@rushstack/stream-collator'; -import { NewlineKind, Async } from '@rushstack/node-core-library'; - -import { AsyncOperationQueue, IOperationSortFunction } from './AsyncOperationQueue'; -import { Operation } from './Operation'; -import { OperationStatus } from './OperationStatus'; -import { IOperationExecutionRecordContext, OperationExecutionRecord } from './OperationExecutionRecord'; -import { IExecutionResult } from './IOperationExecutionResult'; - -export interface IOperationExecutionManagerOptions { - quietMode: boolean; - debugMode: boolean; - parallelism: number; - changedProjectsOnly: boolean; - destination?: TerminalWritable; - - onOperationStatusChanged?: (record: OperationExecutionRecord) => void; - beforeExecuteOperations?: (records: Map) => Promise; -} - -/** - * Format "======" lines for a shell window with classic 80 columns - */ -const ASCII_HEADER_WIDTH: number = 79; - -/** - * A class which manages the execution of a set of tasks with interdependencies. - * Initially, and at the end of each task execution, all unblocked tasks - * are added to a ready queue which is then executed. This is done continually until all - * tasks are complete, or prematurely fails if any of the tasks fail. - */ -export class OperationExecutionManager { - private readonly _changedProjectsOnly: boolean; - private readonly _executionRecords: Map; - private readonly _quietMode: boolean; - private readonly _parallelism: number; - private readonly _totalOperations: number; - - private readonly _outputWritable: TerminalWritable; - private readonly _colorsNewlinesTransform: TextRewriterTransform; - private readonly _streamCollator: StreamCollator; - - private readonly _terminal: CollatedTerminal; - - private readonly _onOperationStatusChanged?: (record: OperationExecutionRecord) => void; - private readonly _beforeExecuteOperations?: ( - records: Map - ) => Promise; - - // Variables for current status - private _hasAnyFailures: boolean; - private _hasAnyNonAllowedWarnings: boolean; - private _completedOperations: number; - - public constructor(operations: Set, options: IOperationExecutionManagerOptions) { - const { - quietMode, - debugMode, - parallelism, - changedProjectsOnly, - onOperationStatusChanged, - beforeExecuteOperations - } = options; - this._completedOperations = 0; - this._quietMode = quietMode; - this._hasAnyFailures = false; - this._hasAnyNonAllowedWarnings = false; - this._changedProjectsOnly = changedProjectsOnly; - this._parallelism = parallelism; - - this._beforeExecuteOperations = beforeExecuteOperations; - this._onOperationStatusChanged = onOperationStatusChanged; - - // TERMINAL PIPELINE: - // - // streamCollator --> colorsNewlinesTransform --> StdioWritable - // - this._outputWritable = options.destination || StdioWritable.instance; - this._colorsNewlinesTransform = new TextRewriterTransform({ - destination: this._outputWritable, - normalizeNewlines: NewlineKind.OsDefault, - removeColors: !colors.enabled - }); - this._streamCollator = new StreamCollator({ - destination: this._colorsNewlinesTransform, - onWriterActive: this._streamCollator_onWriterActive - }); - this._terminal = this._streamCollator.terminal; - - // Convert the developer graph to the mutable execution graph - const executionRecordContext: IOperationExecutionRecordContext = { - streamCollator: this._streamCollator, - onOperationStatusChanged, - debugMode, - quietMode - }; - - let totalOperations: number = 0; - const executionRecords: Map = (this._executionRecords = new Map()); - for (const operation of operations) { - const executionRecord: OperationExecutionRecord = new OperationExecutionRecord( - operation, - executionRecordContext - ); - - executionRecords.set(operation, executionRecord); - if (!executionRecord.runner.silent) { - // Only count non-silent operations - totalOperations++; - } - } - this._totalOperations = totalOperations; - - for (const [operation, consumer] of executionRecords) { - for (const dependency of operation.dependencies) { - const dependencyRecord: OperationExecutionRecord | undefined = executionRecords.get(dependency); - if (!dependencyRecord) { - throw new Error( - `Operation "${consumer.name}" declares a dependency on operation "${dependency.name}" that is not in the set of operations to execute.` - ); - } - consumer.dependencies.add(dependencyRecord); - dependencyRecord.consumers.add(consumer); - } - } - } - - private _streamCollator_onWriterActive = (writer: CollatedWriter | undefined): void => { - if (writer) { - this._completedOperations++; - - // Format a header like this - // - // ==[ @rushstack/the-long-thing ]=================[ 1 of 1000 ]== - - // leftPart: "==[ @rushstack/the-long-thing " - const leftPart: string = colors.gray('==[') + ' ' + colors.cyan(writer.taskName) + ' '; - const leftPartLength: number = 4 + writer.taskName.length + 1; - - // rightPart: " 1 of 1000 ]==" - const completedOfTotal: string = `${this._completedOperations} of ${this._totalOperations}`; - const rightPart: string = ' ' + colors.white(completedOfTotal) + ' ' + colors.gray(']=='); - const rightPartLength: number = 1 + completedOfTotal.length + 4; - - // middlePart: "]=================[" - const twoBracketsLength: number = 2; - const middlePartLengthMinusTwoBrackets: number = Math.max( - ASCII_HEADER_WIDTH - (leftPartLength + rightPartLength + twoBracketsLength), - 0 - ); - - const middlePart: string = colors.gray(']' + '='.repeat(middlePartLengthMinusTwoBrackets) + '['); - - this._terminal.writeStdoutLine('\n' + leftPart + middlePart + rightPart); - - if (!this._quietMode) { - this._terminal.writeStdoutLine(''); - } - } - }; - - /** - * Executes all operations which have been registered, returning a promise which is resolved when all the - * operations are completed successfully, or rejects when any operation fails. - */ - public async executeAsync(): Promise { - this._completedOperations = 0; - const totalOperations: number = this._totalOperations; - - if (!this._quietMode) { - const plural: string = totalOperations === 1 ? '' : 's'; - this._terminal.writeStdoutLine(`Selected ${totalOperations} operation${plural}:`); - const nonSilentOperations: string[] = []; - for (const record of this._executionRecords.values()) { - if (!record.runner.silent) { - nonSilentOperations.push(record.name); - } - } - nonSilentOperations.sort(); - for (const name of nonSilentOperations) { - this._terminal.writeStdoutLine(` ${name}`); - } - this._terminal.writeStdoutLine(''); - } - - this._terminal.writeStdoutLine(`Executing a maximum of ${this._parallelism} simultaneous processes...`); - - const maxParallelism: number = Math.min(totalOperations, this._parallelism); - const prioritySort: IOperationSortFunction = ( - a: OperationExecutionRecord, - b: OperationExecutionRecord - ): number => { - return a.criticalPathLength! - b.criticalPathLength!; - }; - const executionQueue: AsyncOperationQueue = new AsyncOperationQueue( - this._executionRecords.values(), - prioritySort - ); - - await this._beforeExecuteOperations?.(this._executionRecords); - - // This function is a callback because it may write to the collatedWriter before - // operation.executeAsync returns (and cleans up the writer) - const onOperationComplete: (record: OperationExecutionRecord) => void = ( - record: OperationExecutionRecord - ) => { - this._onOperationComplete(record); - }; - - await Async.forEachAsync( - executionQueue, - async (operation: OperationExecutionRecord) => { - await operation.executeAsync(onOperationComplete); - }, - { - concurrency: maxParallelism - } - ); - - const status: OperationStatus = this._hasAnyFailures - ? OperationStatus.Failure - : this._hasAnyNonAllowedWarnings - ? OperationStatus.SuccessWithWarning - : OperationStatus.Success; - - return { - operationResults: this._executionRecords, - status - }; - } - - /** - * Handles the result of the operation and propagates any relevant effects. - */ - private _onOperationComplete(record: OperationExecutionRecord): void { - const { runner, name, status } = record; - - let blockCacheWrite: boolean = !runner.isCacheWriteAllowed; - let blockSkip: boolean = !runner.isSkipAllowed; - - const silent: boolean = runner.silent; - - switch (status) { - /** - * This operation failed. Mark it as such and all reachable dependents as blocked. - */ - case OperationStatus.Failure: { - // Failed operations get reported, even if silent. - // Generally speaking, silent operations shouldn't be able to fail, so this is a safety measure. - const message: string | undefined = record.error?.message; - // This creates the writer, so don't do this globally - const { terminal } = record.collatedWriter; - if (message) { - terminal.writeStderrLine(message); - } - terminal.writeStderrLine(colors.red(`"${name}" failed to build.`)); - const blockedQueue: Set = new Set(record.consumers); - for (const blockedRecord of blockedQueue) { - if (blockedRecord.status === OperationStatus.Ready) { - this._completedOperations++; - - // Now that we have the concept of architectural no-ops, we could implement this by replacing - // {blockedRecord.runner} with a no-op that sets status to Blocked and logs the blocking - // operations. However, the existing behavior is a bit simpler, so keeping that for now. - if (!blockedRecord.runner.silent) { - terminal.writeStdoutLine(`"${blockedRecord.name}" is blocked by "${name}".`); - } - blockedRecord.status = OperationStatus.Blocked; - this._onOperationStatusChanged?.(blockedRecord); - - for (const dependent of blockedRecord.consumers) { - blockedQueue.add(dependent); - } - } - } - this._hasAnyFailures = true; - break; - } - - /** - * This operation was restored from the build cache. - */ - case OperationStatus.FromCache: { - if (!silent) { - record.collatedWriter.terminal.writeStdoutLine( - colors.green(`"${name}" was restored from the build cache.`) - ); - } - break; - } - - /** - * This operation was skipped via legacy change detection. - */ - case OperationStatus.Skipped: { - if (!silent) { - record.collatedWriter.terminal.writeStdoutLine(colors.green(`"${name}" was skipped.`)); - } - // Skipping means cannot guarantee integrity, so prevent cache writes in dependents. - blockCacheWrite = true; - break; - } - - /** - * This operation intentionally didn't do anything. - */ - case OperationStatus.NoOp: { - if (!silent) { - record.collatedWriter.terminal.writeStdoutLine(colors.gray(`"${name}" did not define any work.`)); - } - break; - } - - case OperationStatus.Success: { - if (!silent) { - record.collatedWriter.terminal.writeStdoutLine( - colors.green(`"${name}" completed successfully in ${record.stopwatch.toString()}.`) - ); - } - // Legacy incremental build, if asked, prevent skip in dependents if the operation executed. - blockSkip ||= !this._changedProjectsOnly; - break; - } - - case OperationStatus.SuccessWithWarning: { - if (!silent) { - record.collatedWriter.terminal.writeStderrLine( - colors.yellow(`"${name}" completed with warnings in ${record.stopwatch.toString()}.`) - ); - } - // Legacy incremental build, if asked, prevent skip in dependents if the operation executed. - blockSkip ||= !this._changedProjectsOnly; - this._hasAnyNonAllowedWarnings = this._hasAnyNonAllowedWarnings || !runner.warningsAreAllowed; - break; - } - } - - // Apply status changes to direct dependents - for (const item of record.consumers) { - if (blockCacheWrite) { - item.runner.isCacheWriteAllowed = false; - } - - if (blockSkip) { - item.runner.isSkipAllowed = false; - } - - // Remove this operation from the dependencies, to unblock the scheduler - item.dependencies.delete(record); - } - } -} diff --git a/libraries/rush-lib/src/logic/operations/OperationExecutionRecord.ts b/libraries/rush-lib/src/logic/operations/OperationExecutionRecord.ts index 3c97bbc574a..2de7655d1d3 100644 --- a/libraries/rush-lib/src/logic/operations/OperationExecutionRecord.ts +++ b/libraries/rush-lib/src/logic/operations/OperationExecutionRecord.ts @@ -1,35 +1,77 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { StdioSummarizer } from '@rushstack/terminal'; -import { InternalError } from '@rushstack/node-core-library'; -import { CollatedWriter, StreamCollator } from '@rushstack/stream-collator'; +import * as crypto from 'node:crypto'; -import { OperationStatus } from './OperationStatus'; -import { IOperationRunner, IOperationRunnerContext } from './IOperationRunner'; -import { Operation } from './Operation'; +import { + type ITerminal, + type ITerminalProvider, + DiscardStdoutTransform, + SplitterTransform, + StderrLineTransform, + StdioSummarizer, + ProblemCollector, + TextRewriterTransform, + Terminal, + type TerminalWritable +} from '@rushstack/terminal'; +import { InternalError, NewlineKind, FileError } from '@rushstack/node-core-library'; +import { CollatedTerminal, type CollatedWriter, type StreamCollator } from '@rushstack/stream-collator'; + +import { coerceParallelism } from './ParseParallelism'; +import { OperationStatus, TERMINAL_STATUSES } from './OperationStatus'; +import type { IOperationRunner, IOperationRunnerContext } from './IOperationRunner'; +import type { Operation } from './Operation'; import { Stopwatch } from '../../utilities/Stopwatch'; import { OperationMetadataManager } from './OperationMetadataManager'; +import type { IPhase } from '../../api/CommandLineConfiguration'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import { CollatedTerminalProvider } from '../../utilities/CollatedTerminalProvider'; +import type { IOperationExecutionResult, IOperationStateHashComponents } from './IOperationExecutionResult'; +import type { IInputsSnapshot } from '../incremental/InputsSnapshot'; +import { OperationError } from './OperationError'; +import { RushConstants } from '../RushConstants'; +import type { IEnvironment } from '../../utilities/Utilities'; +import { + getProjectLogFilePaths, + type ILogFilePaths, + initializeProjectLogFilesAsync +} from './ProjectLogWritable'; +/** + * @internal + */ export interface IOperationExecutionRecordContext { streamCollator: StreamCollator; - onOperationStatusChanged?: (record: OperationExecutionRecord) => void; + onOperationStateChanged?: (record: OperationExecutionRecord) => void; + createEnvironment?: (record: OperationExecutionRecord) => IEnvironment; + invalidate?: (operations: Iterable, reason: string) => void; + inputsSnapshot: IInputsSnapshot | undefined; + maxParallelism: number; debugMode: boolean; quietMode: boolean; } +/** + * Context object for the executeAsync() method. + * @internal + */ +export interface IOperationExecutionContext { + onStartAsync: (record: OperationExecutionRecord) => Promise; + onResultAsync: (record: OperationExecutionRecord) => Promise; +} + /** * Internal class representing everything about executing an operation + * + * @internal */ -export class OperationExecutionRecord implements IOperationRunnerContext { +export class OperationExecutionRecord implements IOperationRunnerContext, IOperationExecutionResult { /** - * The current execution status of an operation. Operations start in the 'ready' state, - * but can be 'blocked' if an upstream operation failed. It is 'executing' when - * the operation is executing. Once execution is complete, it is either 'success' or - * 'failure'. + * The associated operation. */ - public status: OperationStatus = OperationStatus.Ready; + public readonly operation: Operation; /** * The error which occurred while executing this operation, this is stored in case we need @@ -37,6 +79,11 @@ export class OperationExecutionRecord implements IOperationRunnerContext { */ public error: Error | undefined = undefined; + /** + * If true, this operation should be executed. If false, it should be skipped. + */ + public enabled: boolean; + /** * This number represents how far away this Operation is from the furthest "root" operation (i.e. * an operation with no consumers). This helps us to calculate the critical path (i.e. the @@ -47,6 +94,7 @@ export class OperationExecutionRecord implements IOperationRunnerContext { * operation to execute, the operation with the highest criticalPathLength is chosen. * * Example: + * ``` * (0) A * \ * (1) B C (0) (applications) @@ -63,6 +111,7 @@ export class OperationExecutionRecord implements IOperationRunnerContext { * X has a score of 1, since the only package which depends on it is A * Z has a score of 2, since only X depends on it, and X has a score of 1 * Y has a score of 2, since the chain Y->X->C is longer than Y->C + * ``` * * The algorithm is implemented in AsyncOperationQueue.ts as calculateCriticalPathLength() */ @@ -78,34 +127,65 @@ export class OperationExecutionRecord implements IOperationRunnerContext { public readonly consumers: Set = new Set(); public readonly stopwatch: Stopwatch = new Stopwatch(); - public readonly stdioSummarizer: StdioSummarizer = new StdioSummarizer(); + public readonly stdioSummarizer: StdioSummarizer = new StdioSummarizer({ + // Allow writing to this object after transforms have been closed. We clean it up manually in a finally block. + preventAutoclose: true + }); + public readonly problemCollector: ProblemCollector = new ProblemCollector({ + // Allow writing to this object after transforms have been closed. We clean it up manually in a finally block. + preventAutoclose: true, + matcherJson: [ + { + name: 'rushstack-file-error-unix', + pattern: FileError.getProblemMatcher({ format: 'Unix' }) + }, + { + name: 'rushstack-file-error-visualstudio', + pattern: FileError.getProblemMatcher({ format: 'VisualStudio' }) + } + ] + }); public readonly runner: IOperationRunner; public readonly weight: number; - public readonly _operationMetadataManager: OperationMetadataManager | undefined; + public readonly associatedPhase: IPhase; + public readonly associatedProject: RushConfigurationProject; + public readonly _operationMetadataManager: OperationMetadataManager; + + public logFilePaths: ILogFilePaths | undefined; private readonly _context: IOperationExecutionRecordContext; private _collatedWriter: CollatedWriter | undefined = undefined; + private _status: OperationStatus; + private _stateHash: string | undefined; + private _stateHashComponents: IOperationStateHashComponents | undefined; public constructor(operation: Operation, context: IOperationExecutionRecordContext) { - const { runner } = operation; + const { runner, associatedPhase, associatedProject, enabled } = operation; if (!runner) { throw new InternalError( - `Operation for phase '${operation.associatedPhase?.name}' and project '${operation.associatedProject?.packageName}' has no runner.` + `Operation for phase '${associatedPhase.name}' and project '${associatedProject.packageName}' has no runner.` ); } + this.operation = operation; + this.enabled = !!enabled; this.runner = runner; - this.weight = operation.weight; - if (operation.associatedPhase && operation.associatedProject) { - this._operationMetadataManager = new OperationMetadataManager({ - phase: operation.associatedPhase, - rushProject: operation.associatedProject - }); - } + this.weight = runner.isNoOp ? 0 : coerceParallelism(operation.weight, context.maxParallelism); + this.associatedPhase = associatedPhase; + this.associatedProject = associatedProject; + this.logFilePaths = undefined; + + this._operationMetadataManager = new OperationMetadataManager({ + operation + }); + this._context = context; + this._status = operation.dependencies.size > 0 ? OperationStatus.Waiting : OperationStatus.Ready; + this._stateHash = undefined; + this._stateHashComponents = undefined; } public get name(): string { @@ -133,25 +213,246 @@ export class OperationExecutionRecord implements IOperationRunnerContext { return this._operationMetadataManager?.stateFile.state?.nonCachedDurationMs; } - public async executeAsync(onResult: (record: OperationExecutionRecord) => void): Promise { - this.status = OperationStatus.Executing; + public get cobuildRunnerId(): string | undefined { + // Lazy calculated because the state file is created/restored later on + return this._operationMetadataManager?.stateFile.state?.cobuildRunnerId; + } + + public get environment(): IEnvironment | undefined { + return this._context.createEnvironment?.(this); + } + + public getInvalidateCallback(): (reason: string) => void { + const invalidateFn: ((operations: Iterable, reason: string) => void) | undefined = + this._context.invalidate; + const operations: [Operation] = [this.operation]; + return (reason: string) => { + invalidateFn?.(operations, reason); + }; + } + + public get metadataFolderPath(): string { + return this._operationMetadataManager.metadataFolderPath; + } + + public get isTerminal(): boolean { + return TERMINAL_STATUSES.has(this.status); + } + + /** + * The current execution status of an operation. Operations start in the 'ready' state, + * but can be 'blocked' if an upstream operation failed. It is 'executing' when + * the operation is executing. Once execution is complete, it is either 'success' or + * 'failure'. + */ + public get status(): OperationStatus { + return this._status; + } + public set status(newStatus: OperationStatus) { + if (newStatus === this._status) { + return; + } + this._status = newStatus; + this._context.onOperationStateChanged?.(this); + } + + public get silent(): boolean { + return !this.enabled || this.runner.silent; + } + + public getStateHash(): string { + if (this._stateHash === undefined) { + const { dependencies, local, config } = this.getStateHashComponents(); + + const hasher: crypto.Hash = crypto.createHash('sha1'); + for (const dep of dependencies) { + hasher.update(`${RushConstants.hashDelimiter}${dep}`); + } + hasher.update(`${RushConstants.hashDelimiter}local=${local}`); + hasher.update(`${RushConstants.hashDelimiter}config=${config}`); + + const hash: string = hasher.digest('hex'); + this._stateHash = hash; + } + return this._stateHash; + } + + public getStateHashComponents(): IOperationStateHashComponents { + if (!this._stateHashComponents) { + const { inputsSnapshot } = this._context; + + if (!inputsSnapshot) { + throw new Error(`Cannot calculate state hash without git.`); + } + + if (this.dependencies.size !== this.operation.dependencies.size) { + throw new InternalError( + `State hash calculation failed. Dependencies of record do not match the operation.` + ); + } + + // The final state hashes of operation dependencies are factored into the hash to ensure that any + // state changes in dependencies will invalidate the cache. + const dependencies: string[] = Array.from(this.dependencies, (record) => { + return `${record.name}=${record.getStateHash()}`; + }).sort(); + + const { associatedProject, associatedPhase } = this; + // Examples of data in the local state hash: + // - Environment variables specified in `dependsOnEnvVars` + // - Git hashes of tracked files in the associated project + // - Git hash of the shrinkwrap file for the project + // - Git hashes of any files specified in `dependsOnAdditionalFiles` (must not be associated with a project) + const local: string = inputsSnapshot.getOperationOwnStateHash(associatedProject, associatedPhase.name); + + // Examples of data in the config hash: + // - CLI parameters (ShellOperationRunner) + const config: string = this.runner.getConfigHash(); + + this._stateHashComponents = { dependencies, local, config }; + } + return this._stateHashComponents; + } + + /** + * {@inheritdoc IOperationRunnerContext.runWithTerminalAsync} + */ + public async runWithTerminalAsync( + callback: (terminal: ITerminal, terminalProvider: ITerminalProvider) => Promise, + options: { + createLogFile: boolean; + logFileSuffix: string; + } + ): Promise { + const { associatedProject, stdioSummarizer, problemCollector } = this; + const { createLogFile, logFileSuffix = '' } = options; + + const logFilePaths: ILogFilePaths | undefined = createLogFile + ? getProjectLogFilePaths({ + project: associatedProject, + logFilenameIdentifier: `${this._operationMetadataManager.logFilenameIdentifier}${logFileSuffix}` + }) + : undefined; + + const projectLogWritable: TerminalWritable | undefined = logFilePaths + ? await initializeProjectLogFilesAsync({ + logFilePaths, + enableChunkedOutput: true + }) + : undefined; + if (logFilePaths) { + // Only assign if it won't clear an existing value; stopgap until we support multiple sets of log files per operation. + this.logFilePaths = logFilePaths; + this._context.onOperationStateChanged?.(this); + } + + try { + //#region OPERATION LOGGING + // TERMINAL PIPELINE: + // + // +--> quietModeTransform? --> collatedWriter + // | + // normalizeNewlineTransform --1--> stderrLineTransform --2--> projectLogWritable? + // | + // +--> stdioSummarizer + // | + // +--> removeColorsTransform --> problemCollector + const removeColorsTransform: TextRewriterTransform = new TextRewriterTransform({ + destination: problemCollector, + removeColors: true + }); + + const destination: TerminalWritable = new SplitterTransform({ + destinations: projectLogWritable + ? [projectLogWritable, stdioSummarizer, removeColorsTransform] + : [stdioSummarizer, removeColorsTransform] + }); + + const stderrLineTransform: StderrLineTransform = new StderrLineTransform({ + destination, + newlineKind: NewlineKind.Lf // for StdioSummarizer + }); + + const splitterTransform1: SplitterTransform = new SplitterTransform({ + destinations: [ + this.quietMode + ? new DiscardStdoutTransform({ destination: this.collatedWriter }) + : this.collatedWriter, + stderrLineTransform + ] + }); + + const normalizeNewlineTransform: TextRewriterTransform = new TextRewriterTransform({ + destination: splitterTransform1, + normalizeNewlines: NewlineKind.Lf, + ensureNewlineAtEnd: true + }); + + const collatedTerminal: CollatedTerminal = new CollatedTerminal(normalizeNewlineTransform); + const terminalProvider: CollatedTerminalProvider = new CollatedTerminalProvider(collatedTerminal, { + debugEnabled: this.debugMode + }); + const terminal: Terminal = new Terminal(terminalProvider); + //#endregion + + const result: T = await callback(terminal, terminalProvider); + + normalizeNewlineTransform.close(); + + // If the pipeline is wired up correctly, then closing normalizeNewlineTransform should + // have closed projectLogWritable. + if (projectLogWritable?.isOpen) { + throw new InternalError('The output file handle was not closed'); + } + + return result; + } finally { + projectLogWritable?.close(); + } + } + + public async executeAsync( + lastState: OperationExecutionRecord | undefined, + executeContext: IOperationExecutionContext + ): Promise { + if (!this.isTerminal) { + this.stopwatch.reset(); + } this.stopwatch.start(); - this._context.onOperationStatusChanged?.(this); + this.status = OperationStatus.Executing; try { - this.status = await this.runner.executeAsync(this); + const earlyReturnStatus: OperationStatus | undefined = await executeContext.onStartAsync(this); + // When the operation status returns by the hook, bypass the runner execution. + if (earlyReturnStatus) { + this.status = earlyReturnStatus; + } else { + // If the operation is disabled, skip the runner and directly mark as Skipped. + // However, if the operation is a NoOp, return NoOp so that cache entries can still be written. + this.status = this.enabled + ? await this.runner.executeAsync(this, lastState) + : this.runner.isNoOp + ? OperationStatus.NoOp + : OperationStatus.Skipped; + } + // Make sure that the stopwatch is stopped before reporting the result, otherwise endTime is undefined. + this.stopwatch.stop(); // Delegate global state reporting - onResult(this); + await executeContext.onResultAsync(this); } catch (error) { this.status = OperationStatus.Failure; - this.error = error; + this.error = + error instanceof OperationError ? error : new OperationError('executing', (error as Error).message); + // Make sure that the stopwatch is stopped before reporting the result, otherwise endTime is undefined. + this.stopwatch.stop(); // Delegate global state reporting - onResult(this); + await executeContext.onResultAsync(this); } finally { - this._collatedWriter?.close(); - this.stdioSummarizer.close(); - this.stopwatch.stop(); - this._context.onOperationStatusChanged?.(this); + if (this.isTerminal) { + this._collatedWriter?.close(); + this.stdioSummarizer.close(); + this.problemCollector.close(); + } } } } diff --git a/libraries/rush-lib/src/logic/operations/OperationGraph.ts b/libraries/rush-lib/src/logic/operations/OperationGraph.ts new file mode 100644 index 00000000000..8d01d746186 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/OperationGraph.ts @@ -0,0 +1,1259 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { + type TerminalWritable, + TextRewriterTransform, + Colorize, + ConsoleTerminalProvider, + TerminalChunkKind, + SplitterTransform +} from '@rushstack/terminal'; +import { StreamCollator, CollatedTerminal, type CollatedWriter } from '@rushstack/stream-collator'; +import { NewlineKind, Async, InternalError, AlreadyReportedError } from '@rushstack/node-core-library'; + +import { AsyncOperationQueue, type IOperationSortFunction } from './AsyncOperationQueue'; +import type { Operation } from './Operation'; +import { OperationStatus, SUCCESS_STATUSES, TERMINAL_STATUSES } from './OperationStatus'; +import { + type IOperationExecutionContext, + type IOperationExecutionRecordContext, + OperationExecutionRecord +} from './OperationExecutionRecord'; +import type { IExecutionResult } from './IOperationExecutionResult'; +import type { IInputsSnapshot } from '../incremental/InputsSnapshot'; +import type { IEnvironment } from '../../utilities/Utilities'; +import type { IStopwatchResult } from '../../utilities/Stopwatch'; +import type { IOperationGraph, IOperationGraphIterationOptions } from './IOperationGraph'; +import { OperationGraphHooks } from '../../pluginFramework/OperationGraphHooks'; +import { type Parallelism, coerceParallelism, getNumberOfCores } from './ParseParallelism'; +import { measureAsyncFn, measureFn } from '../../utilities/performance'; +import type { ITelemetryData, ITelemetryOperationResult } from '../Telemetry'; + +export interface IOperationGraphTelemetry { + initialExtraData: Record; + changedProjectsOnlyKey: string | undefined; + nameForLog: string; + log: (telemetry: ITelemetryData) => void; +} + +export interface IOperationGraphOptions { + quietMode: boolean; + debugMode: boolean; + parallelism: Parallelism; + allowOversubscription: boolean; + destinations: Iterable; + /** Optional maximum allowed parallelism. Defaults to `getNumberOfCores()`. */ + maxParallelism?: number; + + /** + * Controller used to signal abortion of the entire execution session (e.g. terminating watch mode). + * Consumers (e.g. ProjectWatcher) can subscribe to this to perform cleanup. + */ + abortController: AbortController; + + isWatch?: boolean; + pauseNextIteration?: boolean; + + telemetry?: IOperationGraphTelemetry; + getInputsSnapshotAsync?: () => Promise; +} + +/** + * Internal context state used during an execution iteration. + */ +interface IStatefulExecutionContext { + hasAnyFailures: boolean; + hasAnyNonAllowedWarnings: boolean; + hasAnyAborted: boolean; + + executionQueue: AsyncOperationQueue; + resultByOperation: Map; + + get completedOperations(): number; + set completedOperations(value: number); +} + +/** + * Context for a single execution iteration. + */ +interface IExecutionIterationContext extends IOperationExecutionRecordContext { + abortController: AbortController; + terminal: CollatedTerminal; + + records: Map; + promise: Promise | undefined; + + startTime?: number; + + completedOperations: number; + totalOperations: number; +} + +/** + * Telemetry data for a phased execution + */ +interface IPhasedExecutionTelemetry { + [key: string]: string | number | boolean; + isInitial: boolean; + isWatch: boolean; + + countAll: number; + countSuccess: number; + countSuccessWithWarnings: number; + countFailure: number; + countBlocked: number; + countFromCache: number; + countSkipped: number; + countNoOp: number; + countAborted: number; +} + +const PERF_PREFIX: 'rush:executionManager' = 'rush:executionManager'; + +/** + * Format "======" lines for a shell window with classic 80 columns + */ +const ASCII_HEADER_WIDTH: number = 79; + +const prioritySort: IOperationSortFunction = ( + a: OperationExecutionRecord, + b: OperationExecutionRecord +): number => { + return a.criticalPathLength! - b.criticalPathLength!; +}; + +/** + * Sorts operations lexicographically by their name. + * @param a - The first operation to compare + * @param b - The second operation to compare + * @returns A comparison result: -1 if a < b, 0 if a === b, 1 if a > b + */ +function sortOperationsByName(a: Operation, b: Operation): number { + const aName: string = a.name; + const bName: string = b.name; + return aName === bName ? 0 : aName < bName ? -1 : 1; +} + +/** + * A class which manages the execution of a set of tasks with interdependencies. + */ +export class OperationGraph implements IOperationGraph { + public readonly hooks: OperationGraphHooks = new OperationGraphHooks(); + public readonly operations: Set; + public readonly abortController: AbortController; + private readonly _sortedOperations: readonly Operation[]; + + public resultByOperation: Map; + + // Mutable properties extracted from options + private _parallelism: number; + private _maxParallelism: number; + private _debugMode: boolean; + private _quietMode: boolean; + private _allowOversubscription: boolean; + private _pauseNextIteration: boolean; + + // Immutable properties from options + private readonly _isWatch: boolean; + private readonly _telemetry: IOperationGraphTelemetry | undefined; + private readonly _getInputsSnapshotAsync: (() => Promise) | undefined; + + /** + * Records invalidated during the current iteration that could not be marked `Ready` immediately + * because their record object is shared between `resultByOperation` and the active iteration's + * `records` map (mutating it mid-iteration would corrupt the summarizer's view of results). + * Maps each record to the invalidation reason; applied once the iteration completes. + */ + private readonly _deferredInvalidations: Map = new Map(); + + private _currentIteration: IExecutionIterationContext | undefined = undefined; + private _scheduledIteration: IExecutionIterationContext | undefined = undefined; + + private _terminalSplitter: SplitterTransform; + private _idleTimeout: NodeJS.Timeout | undefined = undefined; + /** Tracks if a graph state change notification has been scheduled for next tick. */ + private _graphStateChangeScheduled: boolean = false; + private _status: OperationStatus = OperationStatus.Ready; + + public constructor(operations: Set, options: IOperationGraphOptions) { + const { + debugMode, + quietMode, + parallelism, + allowOversubscription, + destinations, + maxParallelism = getNumberOfCores(), + abortController, + isWatch = false, + pauseNextIteration = false, + telemetry, + getInputsSnapshotAsync + } = options; + + this.operations = operations; + + this._maxParallelism = maxParallelism; + this._parallelism = coerceParallelism(parallelism, maxParallelism, 1); + this._debugMode = debugMode; + this._quietMode = quietMode; + this._allowOversubscription = allowOversubscription; + this._pauseNextIteration = pauseNextIteration; + this._isWatch = isWatch; + this._telemetry = telemetry; + this._getInputsSnapshotAsync = getInputsSnapshotAsync; + + this._sortedOperations = Array.from(operations).sort(sortOperationsByName); + this._terminalSplitter = new SplitterTransform({ destinations }); + this.resultByOperation = new Map(); + this.abortController = abortController; + + this.abortController.signal.addEventListener( + 'abort', + () => { + if (this._idleTimeout) { + clearTimeout(this._idleTimeout); + } + void this.closeRunnersAsync(); + }, + { once: true } + ); + } + + /** + * {@inheritDoc IOperationGraph.setEnabledStates} + */ + public setEnabledStates( + operations: Iterable, + targetState: Operation['enabled'], + mode: 'safe' | 'unsafe' + ): boolean { + const changedOperations: Set = new Set(); + const requested: Set = new Set(operations); + if (requested.size === 0) { + return false; + } + + if (mode === 'unsafe') { + for (const op of requested) { + if (op.enabled !== targetState) { + op.enabled = targetState; + changedOperations.add(op); + } + } + } else { + // Safe mode logic + if (targetState === true) { + // Expand dependencies of all provided operations (closure) + for (const op of requested) { + for (const dep of op.dependencies) { + requested.add(dep); + } + } + for (const op of requested) { + if (op.enabled !== true) { + op.enabled = true; + changedOperations.add(op); + } + } + } else if (targetState === false) { + const operationsToDisable: Set = new Set(requested); + for (const op of operationsToDisable) { + for (const dep of op.dependencies) { + operationsToDisable.add(dep); + } + } + + const enabledOperations: Set = new Set(); + for (const op of this.operations) { + if (op.enabled !== false && !operationsToDisable.has(op)) { + enabledOperations.add(op); + } + } + for (const op of enabledOperations) { + for (const dep of op.dependencies) { + enabledOperations.add(dep); + } + } + for (const op of enabledOperations) { + operationsToDisable.delete(op); + } + for (const op of operationsToDisable) { + if (op.enabled !== false) { + op.enabled = false; + changedOperations.add(op); + } + } + } else if (targetState === 'ignore-dependency-changes') { + const toEnable: Set = new Set(requested); + for (const op of toEnable) { + for (const dep of op.dependencies) { + toEnable.add(dep); + } + } + for (const op of toEnable) { + const opTargetState: Operation['enabled'] = op.settings?.ignoreChangedProjectsOnlyFlag + ? true + : targetState; + if (op.enabled !== opTargetState) { + op.enabled = opTargetState; + changedOperations.add(op); + } + } + } + } + + if (changedOperations.size > 0) { + // Notify via dedicated hook (do not schedule generic graph state change) + this.hooks.onEnableStatesChanged.call(changedOperations); + } + return changedOperations.size > 0; + } + + public get parallelism(): number { + return this._parallelism; + } + public set parallelism(value: Parallelism) { + const coerced: number = coerceParallelism(value, this._maxParallelism, 1); + if (coerced !== this._parallelism) { + this._parallelism = coerced; + this._scheduleManagerStateChanged(); + } + } + + public get debugMode(): boolean { + return this._debugMode; + } + public set debugMode(value: boolean) { + if (value !== this._debugMode) { + this._debugMode = value; + this._scheduleManagerStateChanged(); + } + } + + public get quietMode(): boolean { + return this._quietMode; + } + public set quietMode(value: boolean) { + if (value !== this._quietMode) { + this._quietMode = value; + this._scheduleManagerStateChanged(); + } + } + + public get allowOversubscription(): boolean { + return this._allowOversubscription; + } + public set allowOversubscription(value: boolean) { + if (value !== this._allowOversubscription) { + this._allowOversubscription = value; + this._scheduleManagerStateChanged(); + } + } + + public get pauseNextIteration(): boolean { + return this._pauseNextIteration; + } + public set pauseNextIteration(value: boolean) { + if (value !== this._pauseNextIteration) { + this._pauseNextIteration = value; + this._scheduleManagerStateChanged(); + + this._setIdleTimeout(); + } + } + + public get hasScheduledIteration(): boolean { + return !!this._scheduledIteration; + } + + public get status(): OperationStatus { + return this._status; + } + + public get terminalDestinations(): ReadonlySet { + return this._terminalSplitter.destinations; + } + + private _setStatus(newStatus: OperationStatus): void { + if (this._status !== newStatus) { + this._status = newStatus; + this._scheduleManagerStateChanged(); + } + } + + private _setScheduledIteration(iteration: IExecutionIterationContext | undefined): void { + const hadScheduled: boolean = !!this._scheduledIteration; + this._scheduledIteration = iteration; + if (hadScheduled !== !!this._scheduledIteration) { + this._scheduleManagerStateChanged(); + } + } + + public async closeRunnersAsync(operations?: Operation[]): Promise { + const promises: Promise[] = []; + const recordMap: ReadonlyMap = + this._currentIteration?.records ?? this.resultByOperation; + const closedRecords: Set = new Set(); + for (const operation of operations ?? this.operations) { + if (operation.runner?.closeAsync) { + const record: OperationExecutionRecord | undefined = recordMap.get(operation); + promises.push( + operation.runner.closeAsync().then(() => { + if (record) { + // Collect for batched notification + closedRecords.add(record); + } + }) + ); + } + } + await Promise.all(promises); + if (this.abortController.signal.aborted) { + return; + } + if (closedRecords.size) { + this.hooks.onExecutionStatesUpdated.call(closedRecords); + } + } + + public invalidateOperations(operations?: Iterable, reason?: string): void { + const invalidated: Set = new Set(); + const currentIteration: IExecutionIterationContext | undefined = this._currentIteration; + const currentIterationRecords: Map | undefined = + currentIteration?.records; + for (const operation of operations ?? this.operations) { + const existing: OperationExecutionRecord | undefined = this.resultByOperation.get(operation); + if (existing && TERMINAL_STATUSES.has(existing.status)) { + if (currentIterationRecords?.get(operation) === existing) { + // The record has already executed in the current iteration and was written to + // resultByOperation. Mutating its status now would corrupt the iteration's result + // snapshot (used by the summarizer). Defer the reset until the iteration ends, and + // abort so the operation can be re-run in the next iteration. + this._deferredInvalidations.set(existing, reason); + currentIteration?.abortController.abort(); + } else { + existing.status = OperationStatus.Ready; + invalidated.add(operation); + } + } + } + if (invalidated.size > 0) { + this.hooks.onInvalidateOperations.call(invalidated, reason); + } + if (!currentIteration) { + this._setStatus(OperationStatus.Ready); + } + } + + /** + * Shorthand for scheduling an iteration then executing it. + * Call `abortCurrentIterationAsync()` to cancel the execution of any operations that have not yet begun execution. + * @param iterationOptions - Options for this execution iteration. + * @returns A promise which is resolved when all operations have been processed to a final state. + */ + public async executeAsync(iterationOptions: IOperationGraphIterationOptions): Promise { + await this.abortCurrentIterationAsync(); + const scheduled: IExecutionIterationContext | undefined = + await this._scheduleIterationAsync(iterationOptions); + if (!scheduled) { + return { + operationResults: this.resultByOperation, + status: OperationStatus.NoOp + }; + } + await this.executeScheduledIterationAsync(); + return { + operationResults: scheduled.records, + status: this.status + }; + } + + /** + * Queues a new execution iteration. + * @param iterationOptions - Options for this execution iteration. + * @returns A promise that resolves to true if the iteration was successfully queued, or false if it was not. + */ + public async scheduleIterationAsync(iterationOptions: IOperationGraphIterationOptions): Promise { + return !!(await this._scheduleIterationAsync(iterationOptions)); + } + + /** + * Executes all operations which have been registered, returning a promise which is resolved when all operations have been processed to a final state. + * Aborts the current iteration first, if any. + */ + public async executeScheduledIterationAsync(): Promise { + await this.abortCurrentIterationAsync(); + + const iteration: IExecutionIterationContext | undefined = this._scheduledIteration; + + if (!iteration) { + return false; + } + + this._currentIteration = iteration; + this._setScheduledIteration(undefined); + + iteration.promise = this._executeInnerAsync(this._currentIteration).finally(() => { + this._currentIteration = undefined; + + // Apply any status resets that were deferred because the records were part of the + // now-completed iteration and could not be mutated mid-iteration. + // Coalesce by reason so consumers receive one notification per reason group. + const byReason: Map = new Map(); + for (const [record, deferredReason] of this._deferredInvalidations) { + record.status = OperationStatus.Ready; + let group: Operation[] | undefined = byReason.get(deferredReason); + if (!group) { + group = []; + byReason.set(deferredReason, group); + } + group.push(record.operation); + } + this._deferredInvalidations.clear(); + for (const [deferredReason, ops] of byReason) { + this.hooks.onInvalidateOperations.call(ops, deferredReason); + } + + this._setIdleTimeout(); + }); + + await iteration.promise; + return true; + } + + public async abortCurrentIterationAsync(): Promise { + const iteration: IExecutionIterationContext | undefined = this._currentIteration; + if (iteration) { + iteration.abortController.abort(); + try { + await iteration.promise; + } catch (e) { + // Swallow errors from aborting + } + } + + this._setIdleTimeout(); + } + + public addTerminalDestination(destination: TerminalWritable): void { + this._terminalSplitter.addDestination(destination); + } + + public removeTerminalDestination(destination: TerminalWritable, close: boolean = true): boolean { + return this._terminalSplitter.removeDestination(destination, close); + } + + private _setIdleTimeout(): void { + if (this._currentIteration || this.abortController.signal.aborted) { + return; + } + + if (!this._idleTimeout) { + this._idleTimeout = setTimeout(this._onIdle, 0); + } + } + + private _onIdle = (): void => { + this._idleTimeout = undefined; + if (this._currentIteration || this.abortController.signal.aborted) { + return; + } + + if (!this.pauseNextIteration && this._scheduledIteration) { + void this.executeScheduledIterationAsync(); + } else { + this.hooks.onIdle.call(); + } + }; + + private async _scheduleIterationAsync( + iterationOptions: IOperationGraphIterationOptions + ): Promise { + const { _getInputsSnapshotAsync: getInputsSnapshotAsync } = this; + + const { startTime = performance.now(), inputsSnapshot = await getInputsSnapshotAsync?.() } = + iterationOptions; + const iterationOptionsForCallbacks: IOperationGraphIterationOptions = { startTime, inputsSnapshot }; + + const { hooks } = this; + + const abortController: AbortController = new AbortController(); + + // TERMINAL PIPELINE: + // + // streamCollator --> colorsNewlinesTransform --> StdioWritable + // + const colorsNewlinesTransform: TextRewriterTransform = new TextRewriterTransform({ + destination: this._terminalSplitter, + normalizeNewlines: NewlineKind.OsDefault, + removeColors: !ConsoleTerminalProvider.supportsColor + }); + const terminal: CollatedTerminal = new CollatedTerminal(colorsNewlinesTransform); + const streamCollator: StreamCollator = new StreamCollator({ + destination: colorsNewlinesTransform, + onWriterActive + }); + + const sortedOperations: readonly Operation[] = this._sortedOperations; + + const graph: OperationGraph = this; + + function createEnvironmentForOperation(record: OperationExecutionRecord): IEnvironment { + return hooks.createEnvironmentForOperation.call({ ...process.env }, record); + } + + // Convert the developer graph to the mutable execution graph + const iterationContext: IExecutionIterationContext = { + abortController, + startTime, + streamCollator, + terminal, + inputsSnapshot, + maxParallelism: this._maxParallelism, + onOperationStateChanged: undefined, + createEnvironment: createEnvironmentForOperation, + invalidate: (operations: Iterable, reason: string) => { + graph.invalidateOperations(operations, reason); + }, + get debugMode(): boolean { + return graph.debugMode; + }, + get quietMode(): boolean { + return graph.quietMode; + }, + records: new Map(), + promise: undefined, + completedOperations: 0, + totalOperations: 0 + }; + + const executionRecords: Map = iterationContext.records; + for (const operation of sortedOperations) { + const executionRecord: OperationExecutionRecord = new OperationExecutionRecord( + operation, + iterationContext + ); + + executionRecords.set(operation, executionRecord); + } + + for (const [operation, record] of executionRecords) { + for (const dependency of operation.dependencies) { + const dependencyRecord: OperationExecutionRecord | undefined = executionRecords.get(dependency); + if (!dependencyRecord) { + throw new Error( + `Operation "${record.name}" declares a dependency on operation "${dependency.name}" that is not in the set of operations to execute.` + ); + } + record.dependencies.add(dependencyRecord); + dependencyRecord.consumers.add(record); + } + } + + // Configure operations to execute. + // Ensure we compute the compute the state hashes for all operations before the runtime graph potentially mutates. + if (inputsSnapshot) { + for (const record of executionRecords.values()) { + record.getStateHash(); + } + } + + measureFn(`${PERF_PREFIX}:configureIteration`, () => { + hooks.configureIteration.call(executionRecords, this.resultByOperation, iterationOptionsForCallbacks); + }); + + for (const executionRecord of executionRecords.values()) { + if (!executionRecord.silent) { + // Only count non-silent operations + iterationContext.totalOperations++; + } + } + + if (iterationContext.totalOperations === 0) { + return; + } + + this._setScheduledIteration(iterationContext); + // Notify listeners that an iteration has been scheduled with the planned operation records + try { + this.hooks.onIterationScheduled.call(iterationContext.records); + } catch (e) { + // Surface configuration-time issues clearly + terminal.writeStderrLine( + Colorize.red(`An error occurred in onIterationScheduled hook: ${(e as Error).message}`) + ); + throw e; + } + if (!this._currentIteration) { + this._setIdleTimeout(); + } else if (!this.pauseNextIteration) { + void this.abortCurrentIterationAsync(); + } + return iterationContext; + + function onWriterActive(writer: CollatedWriter | undefined): void { + if (writer) { + iterationContext.completedOperations++; + // Format a header like this + // + // ==[ @rushstack/the-long-thing ]=================[ 1 of 1000 ]== + + // leftPart: "==[ @rushstack/the-long-thing " + const leftPart: string = Colorize.gray('==[') + ' ' + Colorize.cyan(writer.taskName) + ' '; + const leftPartLength: number = 4 + writer.taskName.length + 1; + + // rightPart: " 1 of 1000 ]==" + const completedOfTotal: string = `${iterationContext.completedOperations} of ${iterationContext.totalOperations}`; + const rightPart: string = ' ' + Colorize.white(completedOfTotal) + ' ' + Colorize.gray(']=='); + const rightPartLength: number = 1 + completedOfTotal.length + 4; + + // middlePart: "]=================[" + const twoBracketsLength: number = 2; + const middlePartLengthMinusTwoBrackets: number = Math.max( + ASCII_HEADER_WIDTH - (leftPartLength + rightPartLength + twoBracketsLength), + 0 + ); + + const middlePart: string = Colorize.gray(']' + '='.repeat(middlePartLengthMinusTwoBrackets) + '['); + + terminal.writeStdoutLine('\n' + leftPart + middlePart + rightPart); + + if (!graph.quietMode) { + terminal.writeStdoutLine(''); + } + } + } + } + + /** + * Debounce configuration change notifications so that multiple property setters invoked within the same tick + * only trigger the hook once. This avoids redundant re-computation in listeners (e.g. UI refresh) while preserving + * ordering guarantees that the notification occurs after the initiating state changes are fully applied. + */ + private _scheduleManagerStateChanged(): void { + if (this._graphStateChangeScheduled || this.abortController.signal.aborted) { + return; + } + this._graphStateChangeScheduled = true; + process.nextTick(() => { + this._graphStateChangeScheduled = false; + this.hooks.onGraphStateChanged.call(this); + }); + } + + /** + * Executes all operations which have been registered, returning a promise which is resolved when all operations have been processed to a final state. + * The abortController can be used to cancel the execution of any operations that have not yet begun execution. + */ + private async _executeInnerAsync(iterationContext: IExecutionIterationContext): Promise { + this._setStatus(OperationStatus.Executing); + + const { hooks } = this; + + const { abortController, records: executionRecords, terminal, totalOperations } = iterationContext; + + const isInitial: boolean = this.resultByOperation.size === 0; + + const iterationOptions: IOperationGraphIterationOptions = { + inputsSnapshot: iterationContext.inputsSnapshot, + startTime: iterationContext.startTime + }; + + const executionQueue: AsyncOperationQueue = new AsyncOperationQueue( + executionRecords.values(), + prioritySort + ); + + const abortSignal: AbortSignal = abortController.signal; + + iterationContext.onOperationStateChanged = onOperationStatusChanged; + + // Batched state change tracking using a Set for uniqueness + let batchedStateChanges: Set = new Set(); + function flushBatchedStateChanges(): void { + if (!batchedStateChanges.size) return; + try { + hooks.onExecutionStatesUpdated.call(batchedStateChanges); + } finally { + // Replace the set so that if anything held onto the old one it doesn't get mutated. + batchedStateChanges = new Set(); + } + } + + const state: IStatefulExecutionContext = { + hasAnyFailures: false, + hasAnyNonAllowedWarnings: false, + hasAnyAborted: false, + executionQueue, + resultByOperation: this.resultByOperation, + get completedOperations(): number { + return iterationContext.completedOperations; + }, + set completedOperations(value: number) { + iterationContext.completedOperations = value; + } + }; + + const executionContext: IOperationExecutionContext = { + onStartAsync: onOperationStartAsync, + onResultAsync: onOperationCompleteAsync + }; + + if (!this.quietMode) { + const plural: string = totalOperations === 1 ? '' : 's'; + terminal.writeStdoutLine(`Selected ${totalOperations} operation${plural}:`); + const nonSilentOperations: string[] = []; + for (const record of executionRecords.values()) { + if (!record.silent) { + nonSilentOperations.push(record.name); + } + } + nonSilentOperations.sort(); + for (const name of nonSilentOperations) { + terminal.writeStdoutLine(` ${name}`); + } + terminal.writeStdoutLine(''); + } + + const maxSimultaneousProcesses: number = Math.min(totalOperations, this.parallelism); + // For logging purposes, don't confuse the user by suggesting we might run more operations in parallel than are scheduled. + terminal.writeStdoutLine(`Executing a maximum of ${maxSimultaneousProcesses} simultaneous processes...`); + + const bailStatus: OperationStatus | undefined | void = abortSignal.aborted + ? OperationStatus.Aborted + : await measureAsyncFn( + `${PERF_PREFIX}:beforeExecuteIterationAsync`, + async () => await hooks.beforeExecuteIterationAsync.promise(executionRecords, iterationOptions) + ); + + if (bailStatus) { + // A tap short-circuited the iteration. If it bailed with a successful status (e.g. the + // bridge-cache plugin performed a cache read/write out-of-band), the remaining operations + // were intentionally not executed rather than aborted, so report them as Skipped. Genuine + // aborts and failures still mark the remaining, not-yet-executed work as Aborted. + const unexecutedStatus: OperationStatus = SUCCESS_STATUSES.has(bailStatus) + ? OperationStatus.Skipped + : OperationStatus.Aborted; + for (const record of executionRecords.values()) { + if (!record.isTerminal) { + record.status = unexecutedStatus; + if (unexecutedStatus === OperationStatus.Aborted) { + state.hasAnyAborted = true; + } + } + } + } else { + await measureAsyncFn(`${PERF_PREFIX}:executeOperationsAsync`, async () => { + await Async.forEachAsync( + executionQueue, + async (record: OperationExecutionRecord) => { + if (abortSignal.aborted) { + record.status = OperationStatus.Aborted; + // Bypass the normal completion handler, directly mark the operation as aborted and unblock the queue. + // We do this to ensure that we aren't messing with the stopwatch or terminal. + state.hasAnyAborted = true; + executionQueue.complete(record); + } else { + const lastState: OperationExecutionRecord | undefined = state.resultByOperation.get( + record.operation + ); + await record.executeAsync(lastState, executionContext); + } + }, + { + // In weighted mode, concurrency represents the total "unit budget", not the max number of tasks. + // Do not cap by totalOperations, since that would incorrectly shrink the unit budget and + // reduce parallelism for operations with weight > 1. + concurrency: this.parallelism, + weighted: true, + allowOversubscription: this.allowOversubscription + } + ); + }); + } + + const status: OperationStatus = (() => { + if (bailStatus) return bailStatus; + if (state.hasAnyFailures) return OperationStatus.Failure; + if (state.hasAnyAborted) return OperationStatus.Aborted; + if (state.hasAnyNonAllowedWarnings) return OperationStatus.SuccessWithWarning; + if (iterationContext.totalOperations === 0) return OperationStatus.NoOp; + return OperationStatus.Success; + })(); + + this._setStatus( + (await measureAsyncFn(`${PERF_PREFIX}:afterExecuteIterationAsync`, async () => { + return await hooks.afterExecuteIterationAsync.promise(status, executionRecords, iterationOptions); + })) ?? status + ); + + const { _telemetry: telemetry } = this; + if (telemetry) { + const logEntry: ITelemetryData = measureFn(`${PERF_PREFIX}:prepareTelemetry`, () => { + const isWatch: boolean = this._isWatch; + const jsonOperationResults: Record = {}; + + const durationInSeconds: number = (performance.now() - (iterationContext.startTime ?? 0)) / 1000; + + const extraData: IPhasedExecutionTelemetry = { + ...telemetry.initialExtraData, + isWatch, + // Fields specific to the current operation set + isInitial, + + countAll: 0, + countSuccess: 0, + countSuccessWithWarnings: 0, + countFailure: 0, + countBlocked: 0, + countFromCache: 0, + countSkipped: 0, + countNoOp: 0, + countAborted: 0 + }; + + let changedProjectsOnly: boolean = false; + for (const operation of executionRecords.keys()) { + if (operation.enabled === 'ignore-dependency-changes') { + changedProjectsOnly = true; + break; + } + } + + if (telemetry.changedProjectsOnlyKey) { + // Overwrite this value since we allow changing it at runtime. + extraData[telemetry.changedProjectsOnlyKey] = changedProjectsOnly; + } + + const nonSilentDependenciesByOperation: Map> = new Map(); + function getNonSilentDependencies(operation: Operation): ReadonlySet { + let realDependencies: Set | undefined = nonSilentDependenciesByOperation.get(operation); + if (!realDependencies) { + realDependencies = new Set(); + nonSilentDependenciesByOperation.set(operation, realDependencies); + for (const dependency of operation.dependencies) { + const dependencyRecord: OperationExecutionRecord | undefined = executionRecords.get(dependency); + if (dependencyRecord?.silent) { + for (const deepDependency of getNonSilentDependencies(dependency)) { + realDependencies.add(deepDependency); + } + } else { + realDependencies.add(dependency.name!); + } + } + } + return realDependencies; + } + + for (const [operation, operationResult] of executionRecords) { + if (operationResult.silent) { + // Architectural operation. Ignore. + continue; + } + + const { _operationMetadataManager: operationMetadataManager } = operationResult; + + const { startTime, endTime } = operationResult.stopwatch; + jsonOperationResults[operation.name!] = { + startTimestampMs: startTime, + endTimestampMs: endTime, + nonCachedDurationMs: operationResult.nonCachedDurationMs, + wasExecutedOnThisMachine: operationMetadataManager?.wasCobuilt !== true, + result: operationResult.status, + dependencies: Array.from(getNonSilentDependencies(operation)).sort() + }; + + extraData.countAll++; + switch (operationResult.status) { + case OperationStatus.Success: + extraData.countSuccess++; + break; + case OperationStatus.SuccessWithWarning: + extraData.countSuccessWithWarnings++; + break; + case OperationStatus.Failure: + extraData.countFailure++; + break; + case OperationStatus.Blocked: + extraData.countBlocked++; + break; + case OperationStatus.FromCache: + extraData.countFromCache++; + break; + case OperationStatus.Skipped: + extraData.countSkipped++; + break; + case OperationStatus.NoOp: + extraData.countNoOp++; + break; + case OperationStatus.Aborted: + extraData.countAborted++; + break; + default: + // Do nothing. + break; + } + } + + const innerLogEntry: ITelemetryData = { + name: telemetry.nameForLog, + durationInSeconds, + result: status === OperationStatus.Success ? 'Succeeded' : 'Failed', + extraData, + operationResults: jsonOperationResults + }; + + return innerLogEntry; + }); + + measureFn(`${PERF_PREFIX}:beforeLog`, () => this.hooks.beforeLog.call(logEntry)); + telemetry.log(logEntry); + } + + return status; + + // This function is a callback because it may write to the collatedWriter before + // operation.executeAsync returns (and cleans up the writer) + async function onOperationCompleteAsync(record: OperationExecutionRecord): Promise { + // If the operation is not terminal, we should _only_ notify the queue to assign operations. + if (!record.isTerminal) { + executionQueue.assignOperations(); + } else { + try { + await hooks.afterExecuteOperationAsync.promise(record); + } catch (e) { + _reportOperationErrorIfAny(record); + record.error = e; + record.status = OperationStatus.Failure; + } + _onOperationComplete(record, state); + } + } + + async function onOperationStartAsync( + record: OperationExecutionRecord + ): Promise { + return await hooks.beforeExecuteOperationAsync.promise(record); + } + + function onOperationStatusChanged(record: OperationExecutionRecord): void { + if (record.status === OperationStatus.Ready) { + executionQueue.assignOperations(); + } + const wasEmpty: boolean = batchedStateChanges.size === 0; + batchedStateChanges.add(record); + if (wasEmpty) { + // First change in this microtask; schedule flush + queueMicrotask(flushBatchedStateChanges); + } + } + } +} + +/** + * Handles the result of the operation and propagates any relevant effects. + */ +function _onOperationComplete(record: OperationExecutionRecord, context: IStatefulExecutionContext): void { + const { status } = record; + + switch (status) { + /** + * This operation failed. Mark it as such and all reachable dependents as blocked. + */ + case OperationStatus.Failure: { + _handleOperationFailure(record, context); + break; + } + + /** + * This operation was restored from the build cache. + */ + case OperationStatus.FromCache: { + _handleOperationFromCache(record, context); + break; + } + + /** + * This operation was skipped via legacy change detection. + */ + case OperationStatus.Skipped: { + _handleOperationSkipped(record, context); + break; + } + + /** + * This operation intentionally didn't do anything. + */ + case OperationStatus.NoOp: { + _handleOperationNoOp(record, context); + break; + } + + case OperationStatus.Success: { + _handleOperationSuccess(record, context); + break; + } + + case OperationStatus.SuccessWithWarning: { + _handleOperationSuccessWithWarning(record, context); + break; + } + + case OperationStatus.Aborted: { + _handleOperationAborted(record, context); + break; + } + + default: { + throw new InternalError(`Unexpected operation status: ${status}`); + } + } + + context.executionQueue.complete(record); +} + +/** + * Handle a failed operation and propagate the Blocked status to dependent operations. + */ +function _handleOperationFailure(record: OperationExecutionRecord, context: IStatefulExecutionContext): void { + // Failed operations get reported, even if silent. + // Generally speaking, silent operations shouldn't be able to fail, so this is a safety measure. + _reportOperationErrorIfAny(record); + + const { name } = record; + const { terminal } = record.collatedWriter; // Creates the writer if needed + terminal.writeStderrLine(Colorize.red(`"${name}" failed to build.`)); + + const blockedQueue: Set = new Set(record.consumers); + for (const blockedRecord of blockedQueue) { + if (blockedRecord.status === OperationStatus.Waiting) { + if (!blockedRecord.silent) { + terminal.writeStdoutLine(`"${blockedRecord.name}" is blocked by "${name}".`); + } + blockedRecord.status = OperationStatus.Blocked; + context.executionQueue.complete(blockedRecord); + if (!blockedRecord.silent) { + context.completedOperations++; // Only count non-silent operations + } + for (const dependent of blockedRecord.consumers) { + blockedQueue.add(dependent); + } + } else if (blockedRecord.status !== OperationStatus.Blocked) { + throw new InternalError( + `Blocked operation ${blockedRecord.name} is in an unexpected state: ${blockedRecord.status}` + ); + } + } + context.resultByOperation.set(record.operation, record); + context.hasAnyFailures = true; +} + +/** + * Handle operation restored from cache. + */ +function _handleOperationFromCache( + record: OperationExecutionRecord, + context: IStatefulExecutionContext +): void { + if (!record.silent) { + record.collatedWriter.terminal.writeStdoutLine( + Colorize.green(`"${record.name}" was restored from the build cache.`) + ); + } + context.resultByOperation.set(record.operation, record); +} + +/** + * Handle skipped operation. + */ +function _handleOperationSkipped(record: OperationExecutionRecord, context: IStatefulExecutionContext): void { + // Do not set resultByOperation here. "Skipped" means the operation was not executed, + // so it should not be considered the last *execution* result. + if (!record.silent) { + record.collatedWriter.terminal.writeStdoutLine(Colorize.green(`"${record.name}" was skipped.`)); + } +} + +/** + * Handle no-op operation. + */ +function _handleOperationNoOp(record: OperationExecutionRecord, context: IStatefulExecutionContext): void { + if (!record.silent) { + record.collatedWriter.terminal.writeStdoutLine( + Colorize.gray(`"${record.name}" did not define any work.`) + ); + } + context.resultByOperation.set(record.operation, record); +} + +/** + * Handle successful operation. + */ +function _handleOperationSuccess(record: OperationExecutionRecord, context: IStatefulExecutionContext): void { + const stopwatch: IStopwatchResult = _getOperationStopwatch(record); + if (!record.silent) { + record.collatedWriter.terminal.writeStdoutLine( + Colorize.green(`"${record.name}" completed successfully in ${stopwatch.toString()}.`) + ); + } + context.resultByOperation.set(record.operation, record); +} + +/** + * Handle successful operation with warnings. + */ +function _handleOperationSuccessWithWarning( + record: OperationExecutionRecord, + context: IStatefulExecutionContext +): void { + const stopwatch: IStopwatchResult = _getOperationStopwatch(record); + if (!record.silent) { + record.collatedWriter.terminal.writeStderrLine( + Colorize.yellow(`"${record.name}" completed with warnings in ${stopwatch.toString()}.`) + ); + } + context.resultByOperation.set(record.operation, record); + context.hasAnyNonAllowedWarnings ||= !record.runner.warningsAreAllowed; +} + +/** + * Resolve the appropriate stopwatch for an operation, restoring from metadata if available. + */ +function _getOperationStopwatch(record: OperationExecutionRecord): IStopwatchResult { + const operationMetadataManager: import('./OperationMetadataManager').OperationMetadataManager = + record._operationMetadataManager; + return operationMetadataManager?.tryRestoreStopwatch(record.stopwatch) || record.stopwatch; +} + +/** + * Handle aborted operation. + */ +function _handleOperationAborted(record: OperationExecutionRecord, context: IStatefulExecutionContext): void { + // Do not set resultByOperation here. "Aborted" means the operation was not executed, + // so it should not be considered the last *execution* result. + context.hasAnyAborted = true; +} + +function _reportOperationErrorIfAny(record: OperationExecutionRecord): void { + // Failed operations get reported, even if silent. + // Generally speaking, silent operations shouldn't be able to fail, so this is a safety measure. + let message: string | undefined = undefined; + if (record.error) { + if (!(record.error instanceof AlreadyReportedError)) { + message = record.error.message; + } + } + + if (message) { + // This creates the writer, so don't do this until needed + record.collatedWriter.terminal.writeStderrLine(message); + // Ensure that the summary isn't blank if we have an error message + // If the summary already contains max lines of stderr, this will get dropped, so we hope those lines + // are more useful than the final exit code. + record.stdioSummarizer.writeChunk({ + text: `${message}\n`, + kind: TerminalChunkKind.Stdout + }); + } +} diff --git a/libraries/rush-lib/src/logic/operations/OperationMetadataManager.ts b/libraries/rush-lib/src/logic/operations/OperationMetadataManager.ts index 90c7caa22ff..e3f1f2f0dc5 100644 --- a/libraries/rush-lib/src/logic/operations/OperationMetadataManager.ts +++ b/libraries/rush-lib/src/logic/operations/OperationMetadataManager.ts @@ -1,22 +1,28 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as fs from 'fs'; -import { Async, FileSystem, IFileSystemCopyFileOptions, ITerminal } from '@rushstack/node-core-library'; +import * as fs from 'node:fs'; + +import { Async, FileSystem, type IFileSystemCopyFileOptions } from '@rushstack/node-core-library'; +import { + type ITerminalChunk, + TerminalChunkKind, + TerminalProviderSeverity, + type ITerminal, + type ITerminalProvider +} from '@rushstack/terminal'; import { OperationStateFile } from './OperationStateFile'; import { RushConstants } from '../RushConstants'; - -import type { IPhase } from '../../api/CommandLineConfiguration'; -import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import type { IOperationStateJson } from './OperationStateFile'; +import type { Operation } from './Operation'; +import { type IStopwatchResult, Stopwatch } from '../../utilities/Stopwatch'; /** * @internal */ export interface IOperationMetadataManagerOptions { - rushProject: RushConfigurationProject; - phase: IPhase; + operation: Operation; } /** @@ -26,6 +32,13 @@ export interface IOperationMetaData { durationInSeconds: number; logPath: string; errorLogPath: string; + logChunksPath: string; + cobuildContextId: string | undefined; + cobuildRunnerId: string | undefined; +} + +export interface ILogChunkStorage { + chunks: ITerminalChunk[]; } /** @@ -35,28 +48,32 @@ export interface IOperationMetaData { */ export class OperationMetadataManager { public readonly stateFile: OperationStateFile; - private _metadataFolder: string; - private _logPath: string; - private _errorLogPath: string; - private _relativeLogPath: string; - private _relativeErrorLogPath: string; + public readonly logFilenameIdentifier: string; + private readonly _metadataFolderPath: string; + private readonly _logPath: string; + private readonly _errorLogPath: string; + private readonly _logChunksPath: string; + public wasCobuilt: boolean = false; public constructor(options: IOperationMetadataManagerOptions) { - const { rushProject, phase } = options; - const { projectFolder } = rushProject; + const { + operation: { logFilenameIdentifier, associatedProject } + } = options; + const { projectFolder } = associatedProject; - const identifier: string = phase.logFilenameIdentifier; - this._metadataFolder = `${RushConstants.projectRushFolderName}/${RushConstants.rushTempFolderName}/operation/${identifier}`; + this.logFilenameIdentifier = logFilenameIdentifier; + + const metadataFolderPath: string = `${RushConstants.projectRushFolderName}/${RushConstants.rushTempFolderName}/operation/${logFilenameIdentifier}`; this.stateFile = new OperationStateFile({ projectFolder: projectFolder, - metadataFolder: this._metadataFolder + metadataFolder: metadataFolderPath }); - this._relativeLogPath = `${this._metadataFolder}/all.log`; - this._relativeErrorLogPath = `${this._metadataFolder}/error.log`; - this._logPath = `${projectFolder}/${this._relativeLogPath}`; - this._errorLogPath = `${projectFolder}/${this._relativeErrorLogPath}`; + this._metadataFolderPath = metadataFolderPath; + this._logPath = `${projectFolder}/${metadataFolderPath}/all.log`; + this._errorLogPath = `${projectFolder}/${metadataFolderPath}/error.log`; + this._logChunksPath = `${projectFolder}/${metadataFolderPath}/log-chunks.jsonl`; } /** @@ -66,13 +83,22 @@ export class OperationMetadataManager { * Example: `.rush/temp/operation/_phase_build/all.log` * Example: `.rush/temp/operation/_phase_build/error.log` */ - public get relativeFilepaths(): string[] { - return [this.stateFile.relativeFilepath, this._relativeLogPath, this._relativeErrorLogPath]; + public get metadataFolderPath(): string { + return this._metadataFolderPath; } - public async saveAsync({ durationInSeconds, logPath, errorLogPath }: IOperationMetaData): Promise { + public async saveAsync({ + durationInSeconds, + cobuildContextId, + cobuildRunnerId, + logPath, + errorLogPath, + logChunksPath + }: IOperationMetaData): Promise { const state: IOperationStateJson = { - nonCachedDurationMs: durationInSeconds * 1000 + nonCachedDurationMs: durationInSeconds * 1000, + cobuildContextId, + cobuildRunnerId }; await this.stateFile.writeAsync(state); @@ -84,6 +110,10 @@ export class OperationMetadataManager { { sourcePath: errorLogPath, destinationPath: this._errorLogPath + }, + { + sourcePath: logChunksPath, + destinationPath: this._logChunksPath } ]; @@ -101,26 +131,44 @@ export class OperationMetadataManager { public async tryRestoreAsync({ terminal, - logPath, - errorLogPath + terminalProvider, + errorLogPath, + cobuildContextId, + cobuildRunnerId }: { + terminalProvider: ITerminalProvider; terminal: ITerminal; - logPath: string; errorLogPath: string; + cobuildContextId?: string; + cobuildRunnerId?: string; }): Promise { await this.stateFile.tryRestoreAsync(); + this.wasCobuilt = + this.stateFile.state?.cobuildContextId !== undefined && + cobuildContextId !== undefined && + this.stateFile.state?.cobuildContextId === cobuildContextId && + this.stateFile.state?.cobuildRunnerId !== cobuildRunnerId; - // Append cached log into current log file - terminal.writeLine(`Restoring cached log file at ${this._logPath}`); try { - const logReadStream: fs.ReadStream = fs.createReadStream(this._logPath, { - encoding: 'utf-8' - }); - for await (const data of logReadStream) { - terminal.write(data); + const rawLogChunks: string = await FileSystem.readFileAsync(this._logChunksPath); + const chunks: ITerminalChunk[] = []; + for (const chunk of rawLogChunks.split('\n')) { + if (chunk) { + chunks.push(JSON.parse(chunk)); + } + } + for (const { kind, text } of chunks) { + if (kind === TerminalChunkKind.Stderr) { + terminalProvider.write(text, TerminalProviderSeverity.error); + } else { + terminalProvider.write(text, TerminalProviderSeverity.log); + } } } catch (e) { - if (!FileSystem.isNotExistError(e)) { + if (FileSystem.isNotExistError(e)) { + // Log chunks file doesn't exist, try to restore log file + await restoreFromLogFile(terminal, this._logPath); + } else { throw e; } } @@ -137,4 +185,36 @@ export class OperationMetadataManager { } } } + + public tryRestoreStopwatch(originalStopwatch: IStopwatchResult): IStopwatchResult { + if (this.wasCobuilt && this.stateFile.state && originalStopwatch.endTime !== undefined) { + const endTime: number = originalStopwatch.endTime; + const startTime: number = Math.max(0, endTime - (this.stateFile.state.nonCachedDurationMs ?? 0)); + return Stopwatch.fromState({ + startTime, + endTime + }); + } + return originalStopwatch; + } +} + +async function restoreFromLogFile(terminal: ITerminal, path: string): Promise { + let logReadStream: fs.ReadStream | undefined; + + try { + logReadStream = fs.createReadStream(path, { + encoding: 'utf-8' + }); + for await (const data of logReadStream) { + terminal.write(data); + } + } catch (logReadStreamError) { + if (!FileSystem.isNotExistError(logReadStreamError)) { + throw logReadStreamError; + } + } finally { + // Close the read stream + logReadStream?.close(); + } } diff --git a/libraries/rush-lib/src/logic/operations/OperationResultSummarizerPlugin.ts b/libraries/rush-lib/src/logic/operations/OperationResultSummarizerPlugin.ts index e328fe78ef1..1ff82c3baf6 100644 --- a/libraries/rush-lib/src/logic/operations/OperationResultSummarizerPlugin.ts +++ b/libraries/rush-lib/src/logic/operations/OperationResultSummarizerPlugin.ts @@ -1,16 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import { InternalError, ITerminal } from '@rushstack/node-core-library'; -import { - ICreateOperationsContext, - IPhasedCommandPlugin, - PhasedCommandHooks -} from '../../pluginFramework/PhasedCommandHooks'; -import { IExecutionResult, IOperationExecutionResult } from './IOperationExecutionResult'; -import { Operation } from './Operation'; +import { InternalError } from '@rushstack/node-core-library'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; + +import type { IPhasedCommandPlugin, PhasedCommandHooks } from '../../pluginFramework/PhasedCommandHooks'; +import type { IExecutionResult, IOperationExecutionResult } from './IOperationExecutionResult'; +import type { Operation } from './Operation'; import { OperationStatus } from './OperationStatus'; +import type { OperationExecutionRecord } from './OperationExecutionRecord'; +import type { IStopwatchResult } from '../../utilities/Stopwatch'; const PLUGIN_NAME: 'OperationResultSummarizerPlugin' = 'OperationResultSummarizerPlugin'; @@ -33,12 +32,19 @@ export class OperationResultSummarizerPlugin implements IPhasedCommandPlugin { } public apply(hooks: PhasedCommandHooks): void { - hooks.afterExecuteOperations.tap( - PLUGIN_NAME, - (result: IExecutionResult, context: ICreateOperationsContext): void => { - _printOperationStatus(this._terminal, result); - } - ); + hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph) => { + // Ensure this plugin runs after all other plugins + graph.hooks.afterExecuteIterationAsync.tap( + PLUGIN_NAME, + ( + status: OperationStatus, + results: ReadonlyMap + ): OperationStatus => { + _printOperationStatus(this._terminal, { status, operationResults: results }); + return status; + } + ); + }); } } @@ -47,11 +53,9 @@ export class OperationResultSummarizerPlugin implements IPhasedCommandPlugin { * @internal */ export function _printOperationStatus(terminal: ITerminal, result: IExecutionResult): void { - const { operationResults } = result; - const operationsByStatus: IOperationsByStatus = new Map(); - for (const record of operationResults) { - if (record[0].runner?.silent) { + for (const record of result.operationResults) { + if (record[1].silent) { // Don't report silenced operations continue; } @@ -66,6 +70,7 @@ export function _printOperationStatus(terminal: ITerminal, result: IExecutionRes case OperationStatus.Blocked: case OperationStatus.Failure: case OperationStatus.NoOp: + case OperationStatus.Aborted: break; default: // This should never happen @@ -88,7 +93,7 @@ export function _printOperationStatus(terminal: ITerminal, result: IExecutionRes terminal, OperationStatus.Skipped, operationsByStatus, - colors.green, + Colorize.green, 'These operations were already up to date:' ); @@ -96,7 +101,7 @@ export function _printOperationStatus(terminal: ITerminal, result: IExecutionRes terminal, OperationStatus.NoOp, operationsByStatus, - colors.gray, + Colorize.gray, 'These operations did not define any work:' ); @@ -104,7 +109,7 @@ export function _printOperationStatus(terminal: ITerminal, result: IExecutionRes terminal, OperationStatus.FromCache, operationsByStatus, - colors.green, + Colorize.green, 'These operations were restored from the build cache:' ); @@ -112,7 +117,7 @@ export function _printOperationStatus(terminal: ITerminal, result: IExecutionRes terminal, OperationStatus.Success, operationsByStatus, - colors.green, + Colorize.green, 'These operations completed successfully:' ); @@ -120,19 +125,27 @@ export function _printOperationStatus(terminal: ITerminal, result: IExecutionRes terminal, OperationStatus.SuccessWithWarning, operationsByStatus, - colors.yellow, + Colorize.yellow, 'WARNING' ); + writeCondensedSummary( + terminal, + OperationStatus.Aborted, + operationsByStatus, + Colorize.white, + 'These operations were aborted:' + ); + writeCondensedSummary( terminal, OperationStatus.Blocked, operationsByStatus, - colors.white, + Colorize.white, 'These operations were blocked by dependencies that failed:' ); - writeDetailedSummary(terminal, OperationStatus.Failure, operationsByStatus, colors.red); + writeDetailedSummary(terminal, OperationStatus.Failure, operationsByStatus, Colorize.red); terminal.writeLine(''); @@ -171,21 +184,30 @@ function writeCondensedSummary( let longestTaskName: number = 0; for (const [operation] of operations) { - const nameLength: number = (operation.name || '').length; + const nameLength: number = operation.name.length; if (nameLength > longestTaskName) { longestTaskName = nameLength; } } for (const [operation, operationResult] of operations) { + const { _operationMetadataManager: operationMetadataManager } = + operationResult as OperationExecutionRecord; + const stopwatch: IStopwatchResult = + operationMetadataManager?.tryRestoreStopwatch(operationResult.stopwatch) ?? operationResult.stopwatch; if ( - operationResult.stopwatch.duration !== 0 && + stopwatch.duration !== 0 && operation.runner!.reportTiming && operationResult.status !== OperationStatus.Skipped ) { - const time: string = operationResult.stopwatch.toString(); + const time: string = stopwatch.toString(); const padding: string = ' '.repeat(longestTaskName - (operation.name || '').length); - terminal.writeLine(` ${operation.name}${padding} ${time}`); + const cacheString: string = operationMetadataManager?.wasCobuilt + ? ` (restore ${( + (operationResult.stopwatch.endTime ?? 0) - (operationResult.stopwatch.startTime ?? 0) + ).toFixed(1)}ms)` + : ''; + terminal.writeLine(` ${operation.name}${padding} ${time}${cacheString}`); } else { terminal.writeLine(` ${operation.name}`); } @@ -220,6 +242,8 @@ function writeDetailedSummary( } for (const [operation, operationResult] of operations) { + const { _operationMetadataManager: operationMetadataManager } = + operationResult as OperationExecutionRecord; // Format a header like this // // --[ WARNINGS: f ]------------------------------------[ 5.07 seconds ]-- @@ -230,7 +254,9 @@ function writeDetailedSummary( const leftPartLength: number = 4 + subheadingText.length + 1; // rightPart: " 5.07 seconds ]--" - const time: string = operationResult.stopwatch.toString(); + const stopwatch: IStopwatchResult = + operationMetadataManager?.tryRestoreStopwatch(operationResult.stopwatch) ?? operationResult.stopwatch; + const time: string = stopwatch.toString(); const rightPartLength: number = 1 + time.length + 1 + 3; // middlePart: "]----------------------[" @@ -241,9 +267,9 @@ function writeDetailedSummary( ); terminal.writeLine( - `${colors.gray('--[')} ${headingColor(subheadingText)} ${colors.gray( + `${Colorize.gray('--[')} ${headingColor(subheadingText)} ${Colorize.gray( `]${'-'.repeat(middlePartLengthMinusTwoBrackets)}[` - )} ${colors.white(time)} ${colors.gray(']--')}\n` + )} ${Colorize.white(time)} ${Colorize.gray(']--')}\n` ); const details: string = operationResult.stdioSummarizer.getReport(); @@ -280,7 +306,7 @@ function writeSummaryHeader( // rightPart: "]======================" terminal.writeLine( - `${colors.gray('==[')} ${headingColor(headingText)} ${colors.gray( + `${Colorize.gray('==[')} ${headingColor(headingText)} ${Colorize.gray( `]${'='.repeat(rightPartLengthMinusBracket)}` )}\n` ); diff --git a/libraries/rush-lib/src/logic/operations/OperationStateFile.ts b/libraries/rush-lib/src/logic/operations/OperationStateFile.ts index de8b6ed7321..b05cf70b57f 100644 --- a/libraries/rush-lib/src/logic/operations/OperationStateFile.ts +++ b/libraries/rush-lib/src/logic/operations/OperationStateFile.ts @@ -16,6 +16,8 @@ export interface IOperationStateFileOptions { */ export interface IOperationStateJson { nonCachedDurationMs: number; + cobuildContextId: string | undefined; + cobuildRunnerId: string | undefined; } /** @@ -53,7 +55,7 @@ export class OperationStateFile { } public async writeAsync(json: IOperationStateJson): Promise { - await JsonFile.saveAsync(json, this.filepath, { ensureFolderExists: true, updateExistingFile: true }); + await JsonFile.saveAsync(json, this.filepath, { ensureFolderExists: true, ignoreUndefinedValues: true }); this._state = json; } diff --git a/libraries/rush-lib/src/logic/operations/OperationStatus.ts b/libraries/rush-lib/src/logic/operations/OperationStatus.ts index 3fdff28ba84..0fe797eda5c 100644 --- a/libraries/rush-lib/src/logic/operations/OperationStatus.ts +++ b/libraries/rush-lib/src/logic/operations/OperationStatus.ts @@ -7,9 +7,17 @@ */ export enum OperationStatus { /** - * The Operation is on the queue, ready to execute (but may be waiting for dependencies) + * The Operation is ready to execute. All its dependencies have succeeded. */ Ready = 'READY', + /** + * The Operation is waiting for one or more dependencies to complete. + */ + Waiting = 'WAITING', + /** + * The Operation is Queued + */ + Queued = 'QUEUED', /** * The Operation is currently executing */ @@ -41,5 +49,33 @@ export enum OperationStatus { /** * The Operation was a no-op (for example, it had an empty script) */ - NoOp = 'NO OP' + NoOp = 'NO OP', + /** + * The Operation was aborted before it could execute. + */ + Aborted = 'ABORTED' } + +/** + * The set of statuses that are considered terminal. + * @alpha + */ +export const TERMINAL_STATUSES: Set = new Set([ + OperationStatus.Success, + OperationStatus.SuccessWithWarning, + OperationStatus.Skipped, + OperationStatus.Blocked, + OperationStatus.FromCache, + OperationStatus.Failure, + OperationStatus.NoOp, + OperationStatus.Aborted +]); + +/** + * The set of statuses that are considered successful and don't trigger a rebuild if current. + */ +export const SUCCESS_STATUSES: Set = new Set([ + OperationStatus.Success, + OperationStatus.FromCache, + OperationStatus.NoOp +]); diff --git a/libraries/rush-lib/src/logic/operations/ParseParallelism.ts b/libraries/rush-lib/src/logic/operations/ParseParallelism.ts new file mode 100644 index 00000000000..915477bf6f2 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/ParseParallelism.ts @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as os from 'node:os'; + +import { IS_WINDOWS } from '../../utilities/executionUtilities'; + +let _maxParallelism: number = 0; + +export function getNumberOfCores(): number { + // Ensure this function caches the result (which is expected not to change while the process is loaded), but is expensive to obtain. + return _maxParallelism || (_maxParallelism = os.availableParallelism?.() ?? os.cpus().length); +} + +/** + * A parallelism value expressed as a fraction of total available concurrency slots. + * @beta + */ +export interface IParallelismScalar { + readonly scalar: number; +} + +/** + * A parallelism value, either as an absolute integer count or a scalar fraction of available parallelism. + * @beta + */ +export type Parallelism = number | IParallelismScalar; + +/** + * Since the JSON value is a string, it must be a percentage like "50%", + * which we parse into a scalar in the range (0, 1]. + * The caller is responsible for multiplying by the available parallelism. + */ +export function parseParallelismPercent(weight: string): number { + const percentageRegExp: RegExp = /^\d+(\.\d+)?%$/; + + if (!percentageRegExp.test(weight)) { + throw new Error(`Expecting a percentage string like "12%" or "34.56%".`); + } + + const percentValue: number = parseFloat(weight); + + if (percentValue <= 0) { + throw new Error(`Invalid percentage value of "${percentValue}": value must be greater than zero`); + } + + if (percentValue > 100) { + throw new Error(`Invalid percentage value of "${percentValue}": value must not exceed 100%`); + } + + return percentValue / 100; +} + +/** + * Coerces a `Parallelism` value to a concrete integer number of concurrency units, given the + * maximum number of available slots. + * + * - Raw numeric values are clamped to `[minimum, maxParallelism]`. + * - Scalar values are multiplied by `maxParallelism`, floored, and clamped to `[Math.max(1, minimum), maxParallelism]`. + */ +export function coerceParallelism( + parallelism: Parallelism, + maxParallelism: number, + minimum: number = 0 +): number { + if (typeof parallelism === 'number') { + return Math.max(minimum, Math.min(parallelism, maxParallelism)); + } + // eslint-disable-next-line no-bitwise + return Math.max(Math.max(1, minimum), Math.min((parallelism.scalar * maxParallelism) | 0, maxParallelism)); +} + +/** + * Parses a command line specification for desired parallelism. + * Factored out to enable unit tests + */ +export function parseParallelism(rawParallelism: string | undefined): Parallelism { + if (rawParallelism) { + rawParallelism = rawParallelism.trim(); + + if (rawParallelism === 'max') { + return { scalar: 1 }; + } + + if (rawParallelism.endsWith('%')) { + return { scalar: parseParallelismPercent(rawParallelism) }; + } + + const parallelismAsNumber: number = Number(rawParallelism); + if (!isNaN(parallelismAsNumber)) { + return parallelismAsNumber; + } + + throw new Error( + `Invalid parallelism value of "${rawParallelism}": expected a number, a percentage string, or "max"` + ); + } else { + // If an explicit parallelism number wasn't provided, then choose a sensible + // default. + if (IS_WINDOWS) { + // On desktop Windows, some people have complained that their system becomes + // sluggish if Rush is using all the CPU cores. Leave one thread for + // other operations. For CI environments, you can use the "max" argument to use all available cores. + // Since we use Math.floor when coercing scalars, 0.999 * N = N - 1 for any integer N >= 1. + return { scalar: 0.999 }; + } else { + // Unix-like operating systems have more balanced scheduling, so default + // to the number of CPU cores + return { scalar: 1 }; + } + } +} diff --git a/libraries/rush-lib/src/logic/operations/PeriodicCallback.ts b/libraries/rush-lib/src/logic/operations/PeriodicCallback.ts new file mode 100644 index 00000000000..26aa1814f55 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/PeriodicCallback.ts @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export type ICallbackFn = () => Promise | void; + +export interface IPeriodicCallbackOptions { + interval: number; +} + +/** + * A help class to run callbacks in a loop with a specified interval. + * + * @beta + */ +export class PeriodicCallback { + private _callbacks: ICallbackFn[]; + private _interval: number; + private _intervalId: NodeJS.Timeout | undefined; + private _isRunning: boolean; + + public constructor(options: IPeriodicCallbackOptions) { + this._callbacks = []; + this._interval = options.interval; + this._isRunning = false; + } + + public addCallback(callback: ICallbackFn): void { + if (this._isRunning) { + throw new Error('Can not add callback while watcher is running'); + } + this._callbacks.push(callback); + } + + public start(): void { + if (this._intervalId) { + throw new Error('Watcher already started'); + } + if (this._callbacks.length === 0) { + return; + } + this._isRunning = true; + this._intervalId = setInterval(() => { + this._callbacks.forEach((callback) => callback()); + }, this._interval); + } + + public stop(): void { + if (this._intervalId) { + clearInterval(this._intervalId); + this._intervalId = undefined; + this._isRunning = false; + } + } +} diff --git a/libraries/rush-lib/src/logic/operations/PhasedOperationPlugin.ts b/libraries/rush-lib/src/logic/operations/PhasedOperationPlugin.ts index 17de0854eaf..ac98661fc64 100644 --- a/libraries/rush-lib/src/logic/operations/PhasedOperationPlugin.ts +++ b/libraries/rush-lib/src/logic/operations/PhasedOperationPlugin.ts @@ -3,15 +3,22 @@ import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import type { IPhase } from '../../api/CommandLineConfiguration'; - -import { Operation } from './Operation'; -import { OperationStatus } from './OperationStatus'; -import { NullOperationRunner } from './NullOperationRunner'; +import { Operation, type OperationEnabledState } from './Operation'; import type { ICreateOperationsContext, + IOperationGraphContext, IPhasedCommandPlugin, PhasedCommandHooks } from '../../pluginFramework/PhasedCommandHooks'; +import type { IOperationGraph, IOperationGraphIterationOptions } from './IOperationGraph'; +import type { IOperationSettings } from '../../api/RushProjectConfiguration'; +import type { + IConfigurableOperation, + IOperationExecutionResult, + IOperationStateHashComponents +} from './IOperationExecutionResult'; +import { SUCCESS_STATUSES } from './OperationStatus'; +import type { IInputsSnapshot } from '../incremental/InputsSnapshot'; const PLUGIN_NAME: 'PhasedOperationPlugin' = 'PhasedOperationPlugin'; @@ -21,7 +28,15 @@ const PLUGIN_NAME: 'PhasedOperationPlugin' = 'PhasedOperationPlugin'; */ export class PhasedOperationPlugin implements IPhasedCommandPlugin { public apply(hooks: PhasedCommandHooks): void { - hooks.createOperations.tap(PLUGIN_NAME, createOperations); + hooks.createOperationsAsync.tap(PLUGIN_NAME, createOperations); + // Configure operations later. + hooks.onGraphCreatedAsync.tap( + { + name: `${PLUGIN_NAME}.Configure`, + stage: 1000 + }, + configureExecutionManager + ); } } @@ -30,38 +45,27 @@ function createOperations( context: ICreateOperationsContext ): Set { const { - projectsInUnknownState: changedProjects, - phaseOriginal, - phaseSelection, - projectSelection + phaseSelection: phases, + projectSelection: projects, + projectConfigurations, + changedProjectsOnly, + includePhaseDeps, + isIncrementalBuildAllowed, + generateFullGraph, + rushConfiguration } = context; - const operationsWithWork: Set = new Set(); const operations: Map = new Map(); - // Create tasks for selected phases and projects - for (const phase of phaseOriginal) { - for (const project of projectSelection) { - getOrCreateOperation(phase, project); - } - } + const defaultEnabledState: OperationEnabledState = + changedProjectsOnly && isIncrementalBuildAllowed ? 'ignore-dependency-changes' : true; - // Recursively expand all consumers in the `operationsWithWork` set. - for (const operation of operationsWithWork) { - for (const consumer of operation.consumers) { - operationsWithWork.add(consumer); - } - } - - for (const [key, operation] of operations) { - if (!operationsWithWork.has(operation)) { - // This operation is in scope, but did not change since it was last executed by the current command. - // However, we have no state tracking across executions, so treat as unknown. - operation.runner = new NullOperationRunner({ - name: key, - result: OperationStatus.Skipped, - silent: true - }); + const projectUniverse: Iterable = generateFullGraph + ? rushConfiguration.projects + : projects; + for (const phase of phases) { + for (const project of projectUniverse) { + getOrCreateOperation(phase, project); } } @@ -71,30 +75,34 @@ function createOperations( function getOrCreateOperation(phase: IPhase, project: RushConfigurationProject): Operation { const key: string = getOperationKey(phase, project); let operation: Operation | undefined = operations.get(key); + if (!operation) { + const { + dependencies: { self, upstream }, + name, + logFilenameIdentifier + } = phase; + const operationSettings: IOperationSettings | undefined = projectConfigurations + .get(project) + ?.operationSettingsByOperationName.get(name); + + const includedInSelection: boolean = phases.has(phase) && projects.has(project); operation = new Operation({ project, - phase + phase, + settings: operationSettings, + logFilenameIdentifier: logFilenameIdentifier, + enabled: + includePhaseDeps || includedInSelection + ? operationSettings?.ignoreChangedProjectsOnlyFlag + ? true + : defaultEnabledState + : false }); - if (!phaseSelection.has(phase) || !projectSelection.has(project)) { - // Not in scope. Mark skipped because state is unknown. - operation.runner = new NullOperationRunner({ - name: key, - result: OperationStatus.Skipped, - silent: true - }); - } else if (changedProjects.has(project)) { - operationsWithWork.add(operation); - } - operations.set(key, operation); existingOperations.add(operation); - const { - dependencies: { self, upstream } - } = phase; - for (const depPhase of self) { operation.addDependency(getOrCreateOperation(depPhase, project)); } @@ -115,6 +123,76 @@ function createOperations( } } +function configureExecutionManager(graph: IOperationGraph, context: IOperationGraphContext): void { + graph.hooks.configureIteration.tap( + PLUGIN_NAME, + ( + currentStates: ReadonlyMap, + lastStates: ReadonlyMap, + iterationOptions: IOperationGraphIterationOptions + ) => { + configureOperations(currentStates, lastStates, iterationOptions); + } + ); +} + +function shouldEnableOperation( + currentState: IConfigurableOperation, + lastState: IOperationExecutionResult | undefined, + inputsSnapshot?: IInputsSnapshot +): boolean { + if (!lastState) { + return true; + } + + if (!SUCCESS_STATUSES.has(lastState.status)) { + return true; + } + + if (!inputsSnapshot) { + // Insufficient information to tell if a rebuild is needed, so assume yes. + return true; + } + + const current: IOperationStateHashComponents = currentState.getStateHashComponents(); + const last: IOperationStateHashComponents = lastState.getStateHashComponents(); + + // Always compare local and config hashes + if (current.local !== last.local || current.config !== last.config) { + return true; + } + + const localChangesOnly: boolean = currentState.operation.enabled === 'ignore-dependency-changes'; + if (localChangesOnly) { + return false; + } + + // Compare dependency hashes + if (current.dependencies.length !== last.dependencies.length) { + return true; + } + for (let i: number = 0; i < current.dependencies.length; i++) { + if (current.dependencies[i] !== last.dependencies[i]) { + return true; + } + } + + return false; +} + +function configureOperations( + currentStates: ReadonlyMap, + lastStates: ReadonlyMap, + iterationOptions: IOperationGraphIterationOptions +): void { + for (const [operation, currentState] of currentStates) { + const lastState: IOperationExecutionResult | undefined = lastStates.get(operation); + + currentState.enabled = + operation.enabled && shouldEnableOperation(currentState, lastState, iterationOptions.inputsSnapshot); + } +} + // Convert the [IPhase, RushConfigurationProject] into a value suitable for use as a Map key function getOperationKey(phase: IPhase, project: RushConfigurationProject): string { return `${project.packageName};${phase.name}`; diff --git a/libraries/rush-lib/src/logic/operations/PnpmSyncCopyOperationPlugin.ts b/libraries/rush-lib/src/logic/operations/PnpmSyncCopyOperationPlugin.ts new file mode 100644 index 00000000000..6857acf1ec0 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/PnpmSyncCopyOperationPlugin.ts @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { type ILogMessageCallbackOptions, pnpmSyncCopyAsync } from 'pnpm-sync-lib'; + +import { Async, FileSystem } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import { OperationStatus } from './OperationStatus'; +import type { IOperationRunnerContext } from './IOperationRunner'; +import type { IPhasedCommandPlugin, PhasedCommandHooks } from '../../pluginFramework/PhasedCommandHooks'; +import type { OperationExecutionRecord } from './OperationExecutionRecord'; +import { PnpmSyncUtilities } from '../../utilities/PnpmSyncUtilities'; +import { RushConstants } from '../RushConstants'; + +const PLUGIN_NAME: 'PnpmSyncCopyOperationPlugin' = 'PnpmSyncCopyOperationPlugin'; + +export class PnpmSyncCopyOperationPlugin implements IPhasedCommandPlugin { + private readonly _terminal: ITerminal; + + public constructor(terminal: ITerminal) { + this._terminal = terminal; + } + public apply(hooks: PhasedCommandHooks): void { + hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph) => { + graph.hooks.afterExecuteOperationAsync.tapPromise( + PLUGIN_NAME, + async (runnerContext: IOperationRunnerContext): Promise => { + const record: OperationExecutionRecord = runnerContext as OperationExecutionRecord; + const { + status, + operation: { associatedProject: project } + } = record; + + //skip if the phase is skipped or no operation + if ( + status === OperationStatus.Skipped || + status === OperationStatus.NoOp || + status === OperationStatus.Failure + ) { + return; + } + + const pnpmSyncJsonPath: string = `${project.projectFolder}/${RushConstants.nodeModulesFolderName}/${RushConstants.pnpmSyncFilename}`; + if (await FileSystem.exists(pnpmSyncJsonPath)) { + const { PackageExtractor } = await import( + /* webpackChunkName: 'PackageExtractor' */ + '@rushstack/package-extractor' + ); + await pnpmSyncCopyAsync({ + pnpmSyncJsonPath, + ensureFolderAsync: FileSystem.ensureFolderAsync, + forEachAsyncWithConcurrency: Async.forEachAsync, + getPackageIncludedFiles: PackageExtractor.getPackageIncludedFilesAsync, + logMessageCallback: (logMessageOptions: ILogMessageCallbackOptions) => + PnpmSyncUtilities.processLogMessage(logMessageOptions, this._terminal) + }); + } + } + ); + }); + } +} diff --git a/libraries/rush-lib/src/logic/operations/ProjectLogWritable.ts b/libraries/rush-lib/src/logic/operations/ProjectLogWritable.ts index 838811c41e4..c11888a6b0d 100644 --- a/libraries/rush-lib/src/logic/operations/ProjectLogWritable.ts +++ b/libraries/rush-lib/src/logic/operations/ProjectLogWritable.ts @@ -1,94 +1,163 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { FileSystem, FileWriter, InternalError } from '@rushstack/node-core-library'; -import { TerminalChunkKind, TerminalWritable, ITerminalChunk } from '@rushstack/terminal'; -import { CollatedTerminal } from '@rushstack/stream-collator'; +import { FileSystem, FileWriter, InternalError, NewlineKind } from '@rushstack/node-core-library'; +import { + SplitterTransform, + TerminalChunkKind, + TerminalWritable, + TextRewriterTransform, + type ITerminalChunk +} from '@rushstack/terminal'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import { PackageNameParsers } from '../../api/PackageNameParsers'; import { RushConstants } from '../RushConstants'; -export class ProjectLogWritable extends TerminalWritable { - private readonly _project: RushConfigurationProject; - private readonly _terminal: CollatedTerminal; +export interface IProjectLogWritableOptions { + logFilePaths: ILogFilePaths; + enableChunkedOutput?: boolean; +} + +export interface ILogFileNames { + textFileName: string; + jsonlFileName: string; + errorFileName: string; +} + +/** + * Information about the log files for an operation. + * + * @alpha + */ +export interface ILogFilePaths { + /** + * The absolute path to the folder containing the text log files. + * Provided as a convenience since it is an intermediary value of producing the text log file path. + */ + textFolder: string; + /** + * The absolute path to the folder containing the JSONL log files. + * Provided as a convenience since it is an intermediary value of producing the jsonl log file path. + */ + jsonlFolder: string; + + /** + * The absolute path to the merged (interleaved stdout and stderr) text log. + * ANSI escape codes have been stripped. + */ + text: string; + /** + * The absolute path to the stderr text log. + * ANSI escape codes have been stripped. + */ + error: string; + /** + * The absolute path to the JSONL log. ANSI escape codes are left intact to be able to reproduce the console output. + */ + jsonl: string; +} + +export interface IGetLogFilePathsOptions { + project: Pick; + logFilenameIdentifier: string; +} + +const LOG_CHUNKS_FOLDER_RELATIVE_PATH: string = `${RushConstants.projectRushFolderName}/${RushConstants.rushTempFolderName}/chunked-rush-logs`; +/** + * A terminal stream that writes all log chunks to a JSONL format so they can be faithfully reconstructed + * during build cache restores. This is used for adding warning + error messages in cobuilds where the original + * logs cannot be completely restored from the existing `all.log` and `error.log` files. + * + * Example output: + * libraries/rush-lib/.rush/temp/operations/rush-lib._phase_build.chunks.jsonl + * ``` + * {"kind":"O","text":"Invoking: heft run --only build -- --clean \n"} + * {"kind":"O","text":" ---- build started ---- \n"} + * {"kind":"O","text":"[build:clean] Deleted 0 files and 5 folders\n"} + * {"kind":"O","text":"[build:typescript] Using TypeScript version 5.4.2\n"} + * {"kind":"O","text":"[build:lint] Using ESLint version 8.57.0\n"} + * {"kind":"E","text":"[build:lint] Warning: libraries/rush-lib/src/logic/operations/LogChunksWritable.ts:15:7 - (@typescript-eslint/typedef) Expected test to have a type annotation.\n"} + * {"kind":"E","text":"[build:lint] Warning: libraries/rush-lib/src/logic/operations/LogChunksWritable.ts:15:7 - (@typescript-eslint/no-unused-vars) 'test' is assigned a value but never used.\n"} + * {"kind":"O","text":"[build:typescript] Copied 1138 folders or files and linked 0 files\n"} + * {"kind":"O","text":"[build:webpack] Using Webpack version 5.82.1\n"} + * {"kind":"O","text":"[build:webpack] Running Webpack compilation\n"} + * {"kind":"O","text":"[build:api-extractor] Using API Extractor version 7.43.1\n"} + * {"kind":"O","text":"[build:api-extractor] Analysis will use the bundled TypeScript version 5.4.2\n"} + * {"kind":"O","text":"[build:copy-mock-flush-telemetry-plugin] Copied 1260 folders or files and linked 5 files\n"} + * {"kind":"O","text":" ---- build finished (6.856s) ---- \n"} + * {"kind":"O","text":"-------------------- Finished (6.858s) --------------------\n"} + * ``` + */ +export class JsonLFileWritable extends TerminalWritable { + public readonly logPath: string; + + private _writer: FileWriter | undefined; + + public constructor(logPath: string) { + super(); + + this.logPath = logPath; + + this._writer = FileWriter.open(logPath); + } + + // Override writeChunk function to throw custom error + public override writeChunk(chunk: ITerminalChunk): void { + if (!this._writer) { + throw new InternalError(`Log writer was closed for ${this.logPath}`); + } + // Stderr can always get written to a error log writer + super.writeChunk(chunk); + } + + protected onWriteChunk(chunk: ITerminalChunk): void { + if (!this._writer) { + throw new InternalError(`Log writer was closed for ${this.logPath}`); + } + this._writer.write(JSON.stringify(chunk) + '\n'); + } + + protected override onClose(): void { + if (this._writer) { + try { + this._writer.close(); + } catch (error) { + throw new InternalError('Failed to close file handle for ' + this._writer.filePath); + } + this._writer = undefined; + } + } +} + +/** + * A terminal stream that writes two text log files: one with interleaved stdout and stderr, and one with just stderr. + */ +export class SplitLogFileWritable extends TerminalWritable { public readonly logPath: string; public readonly errorLogPath: string; - public readonly relativeLogPath: string; - public readonly relativeErrorLogPath: string; private _logWriter: FileWriter | undefined = undefined; private _errorLogWriter: FileWriter | undefined = undefined; - public constructor( - project: RushConfigurationProject, - terminal: CollatedTerminal, - logFilenameIdentifier: string - ) { + public constructor(logPath: string, errorLogPath: string) { super(); - this._project = project; - this._terminal = terminal; - - function getLogFilePaths( - projectFolder: string, - logFilenameIdentifier: string, - logFolder?: string - ): { logPath: string; errorLogPath: string; relativeLogPath: string; relativeErrorLogPath: string } { - const unscopedProjectName: string = PackageNameParsers.permissive.getUnscopedName(project.packageName); - const logFilename: string = `${unscopedProjectName}.${logFilenameIdentifier}.log`; - const errorLogFilename: string = `${unscopedProjectName}.${logFilenameIdentifier}.error.log`; - - const relativeLogPath: string = logFolder ? `${logFolder}/${logFilename}` : logFilename; - const relativeErrorLogPath: string = logFolder ? `${logFolder}/${errorLogFilename}` : errorLogFilename; - - const logPath: string = `${projectFolder}/${relativeLogPath}`; - const errorLogPath: string = `${projectFolder}/${relativeErrorLogPath}`; - - return { - logPath, - errorLogPath, - relativeLogPath, - relativeErrorLogPath - }; - } - const projectFolder: string = this._project.projectFolder; - const { - logPath: legacyLogPath, - errorLogPath: legacyErrorLogPath, - relativeLogPath: legacyRelativeLogPath, - relativeErrorLogPath: legacyRelativeErrorLogPath - } = getLogFilePaths(projectFolder, 'build'); - // If the phased commands experiment is enabled, put logs under `rush-logs` - if (project.rushConfiguration.experimentsConfiguration.configuration.phasedCommands) { - // Delete the legacy logs - FileSystem.deleteFile(legacyLogPath); - FileSystem.deleteFile(legacyErrorLogPath); - - const logPathPrefix: string = `${projectFolder}/${RushConstants.rushLogsFolderName}`; - FileSystem.ensureFolder(logPathPrefix); - - const { logPath, errorLogPath, relativeLogPath, relativeErrorLogPath } = getLogFilePaths( - projectFolder, - logFilenameIdentifier, - RushConstants.rushLogsFolderName - ); - this.logPath = logPath; - this.errorLogPath = errorLogPath; - this.relativeLogPath = relativeLogPath; - this.relativeErrorLogPath = relativeErrorLogPath; - } else { - this.logPath = legacyLogPath; - this.errorLogPath = legacyErrorLogPath; - this.relativeLogPath = legacyRelativeLogPath; - this.relativeErrorLogPath = legacyRelativeErrorLogPath; - } + this.logPath = logPath; + this.errorLogPath = errorLogPath; - FileSystem.deleteFile(this.logPath); - FileSystem.deleteFile(this.errorLogPath); + this._logWriter = FileWriter.open(logPath); + this._errorLogWriter = undefined; + } - this._logWriter = FileWriter.open(this.logPath); + // Override writeChunk function to throw custom error + public override writeChunk(chunk: ITerminalChunk): void { + if (!this._logWriter) { + throw new InternalError(`Log writer was closed for ${this.logPath}`); + } + // Stderr can always get written to a error log writer + super.writeChunk(chunk); } protected onWriteChunk(chunk: ITerminalChunk): void { @@ -107,12 +176,12 @@ export class ProjectLogWritable extends TerminalWritable { } } - protected onClose(): void { + protected override onClose(): void { if (this._logWriter) { try { this._logWriter.close(); } catch (error) { - this._terminal.writeStderrLine('Failed to close file handle for ' + this._logWriter.filePath); + throw new InternalError('Failed to close file handle for ' + this._logWriter.filePath); } this._logWriter = undefined; } @@ -121,9 +190,118 @@ export class ProjectLogWritable extends TerminalWritable { try { this._errorLogWriter.close(); } catch (error) { - this._terminal.writeStderrLine('Failed to close file handle for ' + this._errorLogWriter.filePath); + throw new InternalError('Failed to close file handle for ' + this._errorLogWriter.filePath); } this._errorLogWriter = undefined; } } } + +/** + * Initializes the project log files for a project. Produces a combined log file, an error log file, and optionally a + * chunks file that can be used to reconstrct the original console output. + * @param options - The options to initialize the project log files. + * @returns The terminal writable stream that will write to the log files. + */ +export async function initializeProjectLogFilesAsync( + options: IProjectLogWritableOptions +): Promise { + const { logFilePaths, enableChunkedOutput = false } = options; + + const { + textFolder: logFolderPath, + jsonlFolder: jsonlFolderPath, + text: logPath, + error: errorLogPath, + jsonl: jsonlPath + } = logFilePaths; + await Promise.all([ + FileSystem.ensureFolderAsync(logFolderPath), + enableChunkedOutput && FileSystem.ensureFolderAsync(jsonlFolderPath), + FileSystem.deleteFileAsync(logPath), + FileSystem.deleteFileAsync(errorLogPath), + FileSystem.deleteFileAsync(jsonlPath) + ]); + + const splitLog: TerminalWritable = new TextRewriterTransform({ + destination: new SplitLogFileWritable(logPath, errorLogPath), + removeColors: true, + normalizeNewlines: NewlineKind.OsDefault + }); + + if (enableChunkedOutput) { + const chunksFile: JsonLFileWritable = new JsonLFileWritable(jsonlPath); + const splitter: SplitterTransform = new SplitterTransform({ + destinations: [splitLog, chunksFile] + }); + return splitter; + } + + return splitLog; +} + +/** + * @internal + * + * @param packageName - The raw package name + * @param logFilenameIdentifier - The identifier to append to the log file name (typically the phase name) + * @returns The base names of the log files + */ +export function getLogfileBaseNames(packageName: string, logFilenameIdentifier: string): ILogFileNames { + const unscopedProjectName: string = PackageNameParsers.permissive.getUnscopedName(packageName); + const logFileBaseName: string = `${unscopedProjectName}.${logFilenameIdentifier}`; + + return { + textFileName: `${logFileBaseName}.log`, + jsonlFileName: `${logFileBaseName}.chunks.jsonl`, + errorFileName: `${logFileBaseName}.error.log` + }; +} + +/** + * @internal + * + * @param projectFolder - The absolute path of the project folder + * @returns The absolute paths of the log folders for regular and chunked logs + */ +export function getProjectLogFolders( + projectFolder: string +): Pick { + const textFolder: string = `${projectFolder}/${RushConstants.rushLogsFolderName}`; + const jsonlFolder: string = `${projectFolder}/${LOG_CHUNKS_FOLDER_RELATIVE_PATH}`; + + return { textFolder, jsonlFolder }; +} + +/** + * @internal + * + * @param options - The options to get the log file paths + * @returns All information about log file paths for the project and log identifier + */ +export function getProjectLogFilePaths(options: IGetLogFilePathsOptions): ILogFilePaths { + const { + project: { projectFolder, packageName }, + logFilenameIdentifier + } = options; + + const { textFolder, jsonlFolder } = getProjectLogFolders(projectFolder); + const { + textFileName: textLog, + jsonlFileName: jsonlLog, + errorFileName: errorLog + } = getLogfileBaseNames(packageName, logFilenameIdentifier); + + const textPath: string = `${textFolder}/${textLog}`; + const errorPath: string = `${textFolder}/${errorLog}`; + const jsonlPath: string = `${jsonlFolder}/${jsonlLog}`; + + return { + textFolder, + jsonlFolder, + + text: textPath, + error: errorPath, + jsonl: jsonlPath + }; +} diff --git a/libraries/rush-lib/src/logic/operations/ShardedPhaseOperationPlugin.ts b/libraries/rush-lib/src/logic/operations/ShardedPhaseOperationPlugin.ts new file mode 100644 index 00000000000..9be45e9734c --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/ShardedPhaseOperationPlugin.ts @@ -0,0 +1,227 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IOperationSettings, RushProjectConfiguration } from '../../api/RushProjectConfiguration'; +import type { + ICreateOperationsContext, + IPhasedCommandPlugin, + PhasedCommandHooks +} from '../../pluginFramework/PhasedCommandHooks'; +import { RushConstants } from '../RushConstants'; +import { NullOperationRunner } from './NullOperationRunner'; +import { Operation } from './Operation'; +import { OperationStatus } from './OperationStatus'; +import { + getCustomParameterValuesByOperation, + type ICustomParameterValuesForOperation, + getDisplayName, + initializeShellOperationRunner +} from './ShellOperationRunnerPlugin'; + +export const PLUGIN_NAME: 'ShardedPhasedOperationPlugin' = 'ShardedPhasedOperationPlugin'; + +// eslint-disable-next-line @typescript-eslint/typedef +const TemplateStrings = { + SHARD_INDEX: '{shardIndex}', + SHARD_COUNT: '{shardCount}', + PHASE_NAME: '{phaseName}' +} as const; + +// eslint-disable-next-line @typescript-eslint/typedef +const TemplateStringRegexes = { + SHARD_INDEX: new RegExp(TemplateStrings.SHARD_INDEX, 'g'), + SHARD_COUNT: new RegExp(TemplateStrings.SHARD_COUNT, 'g'), + PHASE_NAME: new RegExp(TemplateStrings.PHASE_NAME, 'g') +} as const; + +/** + * Phased command that shards a phase into multiple operations. + */ +export class ShardedPhasedOperationPlugin implements IPhasedCommandPlugin { + public apply(hooks: PhasedCommandHooks): void { + hooks.createOperationsAsync.tap(PLUGIN_NAME, spliceShards); + } +} + +function spliceShards(existingOperations: Set, context: ICreateOperationsContext): Set { + const { rushConfiguration, projectConfigurations } = context; + + const getCustomParameterValues: (operation: Operation) => ICustomParameterValuesForOperation = + getCustomParameterValuesByOperation(); + + for (const operation of existingOperations) { + const { + associatedPhase: phase, + associatedProject: project, + settings: operationSettings, + logFilenameIdentifier: baseLogFilenameIdentifier + } = operation; + if (operationSettings?.sharding && !operation.runner) { + const { count: shards } = operationSettings.sharding; + + /** + * A single operation to reduce the number of edges in the graph when creating shards. + * ``` + * depA -\ /- shard 1 -\ + * depB -- > noop < -- shard 2 -- > collator (reused operation) + * depC -/ \- shard 3 -/ + * ``` + */ + const preShardOperation: Operation = new Operation({ + phase, + project, + settings: operationSettings, + runner: new NullOperationRunner({ + name: `${getDisplayName(phase, project)} - pre-shard`, + result: OperationStatus.NoOp, + silent: true + }), + logFilenameIdentifier: `${baseLogFilenameIdentifier}_pre-shard` + }); + + existingOperations.add(preShardOperation); + + for (const dependency of operation.dependencies) { + preShardOperation.addDependency(dependency); + operation.deleteDependency(dependency); + } + + const outputFolderArgumentFormat: string = + operationSettings.sharding.outputFolderArgumentFormat ?? + `--shard-output-directory=${RushConstants.projectRushFolderName}/operations/${TemplateStrings.PHASE_NAME}/shards/${TemplateStrings.SHARD_INDEX}`; + + if (!outputFolderArgumentFormat.includes('=')) { + throw new Error( + 'sharding.outputFolderArgumentFormat must contain an "=" sign to differentiate between the key and the value' + ); + } + + if (!outputFolderArgumentFormat.endsWith(TemplateStrings.SHARD_INDEX)) { + throw new Error( + `sharding.outputFolderArgumentFormat must end with ${TemplateStrings.SHARD_INDEX}, "${outputFolderArgumentFormat}"` + ); + } + + // Replace the phase name only to begin with. + const outputDirectoryArgument: string = outputFolderArgumentFormat.replace( + TemplateStringRegexes.PHASE_NAME, + baseLogFilenameIdentifier + ); + + const outputFolderWithTemplate: string = outputDirectoryArgument.substring( + outputDirectoryArgument.indexOf('=') + 1 + ); + + const parentFolder: string = outputFolderWithTemplate.substring( + 0, + outputFolderWithTemplate.indexOf(TemplateStrings.SHARD_INDEX) + ); + + const collatorDisplayName: string = `${getDisplayName(phase, project)} - collate`; + + // Get the custom parameter values for the collator, filtered according to the operation settings + const { parameterValues: customParameterValues, ignoredParameterValues } = + getCustomParameterValues(operation); + + const collatorParameters: string[] = [ + ...customParameterValues, + `--shard-parent-folder="${parentFolder}"`, + `--shard-count="${shards}"` + ]; + + const { scripts } = project.packageJson; + const commandToRun: string | undefined = phase.shellCommand ?? scripts?.[phase.name]; + + operation.logFilenameIdentifier = `${baseLogFilenameIdentifier}_collate`; + operation.runner = initializeShellOperationRunner({ + phase, + project, + displayName: collatorDisplayName, + rushConfiguration, + initialCommand: commandToRun, + incrementalCommand: undefined, + customParameterValues: collatorParameters, + ignoredParameterValues + }); + + const shardOperationName: string = `${phase.name}:shard`; + const baseCommand: string | undefined = scripts?.[shardOperationName]; + if (baseCommand === undefined) { + throw new Error( + `The project '${project.packageName}' does not define a '${phase.name}:shard' command in the 'scripts' section of its package.json` + ); + } + + const shardArgumentFormat: string = + operationSettings.sharding.shardArgumentFormat ?? + `--shard=${TemplateStrings.SHARD_INDEX}/${TemplateStrings.SHARD_COUNT}`; + + if ( + operationSettings.sharding.shardArgumentFormat && + !shardArgumentFormat.includes(TemplateStrings.SHARD_INDEX) && + !shardArgumentFormat.includes(TemplateStrings.SHARD_COUNT) + ) { + throw new Error( + `'shardArgumentFormat' must contain both ${TemplateStrings.SHARD_INDEX} and ${TemplateStrings.SHARD_COUNT} to be used for sharding.` + ); + } + + const projectConfiguration: RushProjectConfiguration | undefined = projectConfigurations.get(project); + for (let shard: number = 1; shard <= shards; shard++) { + const outputDirectory: string = outputFolderWithTemplate.replace( + TemplateStringRegexes.SHARD_INDEX, + shard.toString() + ); + + const shardOperationSettings: IOperationSettings = + projectConfiguration?.operationSettingsByOperationName.get(shardOperationName) ?? + (operationSettings.sharding.shardOperationSettings as IOperationSettings); + + const shardOperation: Operation = new Operation({ + project, + phase, + settings: { + ...shardOperationSettings, + operationName: shardOperationName, + outputFolderNames: [outputDirectory] + }, + logFilenameIdentifier: `${baseLogFilenameIdentifier}_shard_${shard}` + }); + + const shardArgument: string = shardArgumentFormat + .replace(TemplateStringRegexes.SHARD_INDEX, shard.toString()) + .replace(TemplateStringRegexes.SHARD_COUNT, shards.toString()); + + const outputDirectoryArgumentWithShard: string = outputDirectoryArgument.replace( + TemplateStringRegexes.SHARD_INDEX, + shard.toString() + ); + + const shardedParameters: string[] = [ + ...customParameterValues, + shardArgument, + outputDirectoryArgumentWithShard + ]; + + const shardDisplayName: string = `${getDisplayName(phase, project)} - shard ${shard}/${shards}`; + + shardOperation.runner = initializeShellOperationRunner({ + phase, + project, + initialCommand: baseCommand, + incrementalCommand: undefined, + customParameterValues: shardedParameters, + displayName: shardDisplayName, + rushConfiguration, + ignoredParameterValues + }); + + shardOperation.addDependency(preShardOperation); + operation.addDependency(shardOperation); + existingOperations.add(shardOperation); + } + } + } + + return existingOperations; +} diff --git a/libraries/rush-lib/src/logic/operations/ShellOperationRunner.ts b/libraries/rush-lib/src/logic/operations/ShellOperationRunner.ts index 68b8e145a2b..c4b368ed38a 100644 --- a/libraries/rush-lib/src/logic/operations/ShellOperationRunner.ts +++ b/libraries/rush-lib/src/logic/operations/ShellOperationRunner.ts @@ -1,528 +1,158 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as child_process from 'child_process'; -import * as path from 'path'; -import { - JsonFile, - Text, - FileSystem, - JsonObject, - NewlineKind, - InternalError, - ITerminal, - Terminal, - ColorValue -} from '@rushstack/node-core-library'; -import { - TerminalChunkKind, - TextRewriterTransform, - StderrLineTransform, - SplitterTransform, - DiscardStdoutTransform, - PrintUtilities -} from '@rushstack/terminal'; -import { CollatedTerminal } from '@rushstack/stream-collator'; -import { Utilities, UNINITIALIZED } from '../../utilities/Utilities'; -import { OperationStatus } from './OperationStatus'; -import { OperationError } from './OperationError'; -import { IOperationRunner, IOperationRunnerContext } from './IOperationRunner'; -import { ProjectLogWritable } from './ProjectLogWritable'; -import { ProjectBuildCache } from '../buildCache/ProjectBuildCache'; -import { getHashesForGlobsAsync } from '../buildCache/getHashesForGlobsAsync'; -import { IOperationSettings, RushProjectConfiguration } from '../../api/RushProjectConfiguration'; -import { CollatedTerminalProvider } from '../../utilities/CollatedTerminalProvider'; -import { RushConstants } from '../RushConstants'; -import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration'; -import { OperationMetadataManager } from './OperationMetadataManager'; +import type * as child_process from 'node:child_process'; -import type { RushConfiguration } from '../../api/RushConfiguration'; -import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; -import type { ProjectChangeAnalyzer, IRawRepoState } from '../ProjectChangeAnalyzer'; -import type { BuildCacheConfiguration } from '../../api/BuildCacheConfiguration'; -import type { IPhase } from '../../api/CommandLineConfiguration'; +import { Text } from '@rushstack/node-core-library'; +import { type ITerminal, type ITerminalProvider, TerminalProviderSeverity } from '@rushstack/terminal'; -export interface IProjectDeps { - files: { [filePath: string]: string }; - arguments: string; -} +import type { IPhase } from '../../api/CommandLineConfiguration'; +import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import { Utilities } from '../../utilities/Utilities'; +import type { IOperationRunner, IOperationRunnerContext, IOperationLastState } from './IOperationRunner'; +import { OperationError } from './OperationError'; +import { OperationStatus } from './OperationStatus'; -export interface IOperationRunnerOptions { +export interface IShellOperationRunnerOptions { + phase: IPhase; rushProject: RushConfigurationProject; - rushConfiguration: RushConfiguration; - buildCacheConfiguration: BuildCacheConfiguration | undefined; - commandToRun: string; - isIncrementalBuildAllowed: boolean; - projectChangeAnalyzer: ProjectChangeAnalyzer; displayName: string; - phase: IPhase; - /** - * The set of phases being executed in the current command, for validation of rush-project.json - */ - selectedPhases: Iterable; -} - -function _areShallowEqual(object1: JsonObject, object2: JsonObject): boolean { - for (const n in object1) { - if (!(n in object2) || object1[n] !== object2[n]) { - return false; - } - } - for (const n in object2) { - if (!(n in object1)) { - return false; - } - } - return true; + initialCommand: string; + incrementalCommand: string | undefined; + commandForHash: string; + ignoredParameterValues: ReadonlyArray; } /** - * An `IOperationRunner` subclass that performs an operation via a shell command. + * An `IOperationRunner` implementation that performs an operation via a shell command. * Currently contains the build cache logic, pending extraction as separate operations. * Supports skipping an operation if allowed and it is already up-to-date. */ export class ShellOperationRunner implements IOperationRunner { public readonly name: string; - // This runner supports cache writes by default. - public isCacheWriteAllowed: boolean = true; - public isSkipAllowed: boolean; public readonly reportTiming: boolean = true; public readonly silent: boolean = false; + public readonly cacheable: boolean = true; public readonly warningsAreAllowed: boolean; + /** + * The creator is expected to use a different runner if the command is known to be a noop. + */ + public readonly isNoOp: boolean = false; + + private readonly _commandForHash: string; + private readonly _initialCommand: string; + private readonly _incrementalCommand: string | undefined; private readonly _rushProject: RushConfigurationProject; - private readonly _phase: IPhase; - private readonly _rushConfiguration: RushConfiguration; - private readonly _buildCacheConfiguration: BuildCacheConfiguration | undefined; - private readonly _commandName: string; - private readonly _commandToRun: string; - private readonly _isCacheReadAllowed: boolean; - private readonly _projectChangeAnalyzer: ProjectChangeAnalyzer; - private readonly _packageDepsFilename: string; - private readonly _logFilenameIdentifier: string; - private readonly _selectedPhases: Iterable; - /** - * UNINITIALIZED === we haven't tried to initialize yet - * undefined === we didn't create one because the feature is not enabled - */ - private _projectBuildCache: ProjectBuildCache | undefined | UNINITIALIZED = UNINITIALIZED; + private readonly _ignoredParameterValues: ReadonlyArray; - public constructor(options: IOperationRunnerOptions) { - const { phase } = options; + public constructor(options: IShellOperationRunnerOptions) { + const { + phase, + displayName, + rushProject, + initialCommand, + incrementalCommand, + commandForHash, + ignoredParameterValues + } = options; - this.name = options.displayName; - this._rushProject = options.rushProject; - this._phase = phase; - this._rushConfiguration = options.rushConfiguration; - this._buildCacheConfiguration = options.buildCacheConfiguration; - this._commandName = phase.name; - this._commandToRun = options.commandToRun; - this._isCacheReadAllowed = options.isIncrementalBuildAllowed; - this.isSkipAllowed = options.isIncrementalBuildAllowed; - this._projectChangeAnalyzer = options.projectChangeAnalyzer; - this._packageDepsFilename = `package-deps_${phase.logFilenameIdentifier}.json`; + this.name = displayName; this.warningsAreAllowed = EnvironmentConfiguration.allowWarningsInSuccessfulBuild || phase.allowWarningsOnSuccess || false; - this._logFilenameIdentifier = phase.logFilenameIdentifier; - this._selectedPhases = options.selectedPhases; + this._rushProject = rushProject; + this._initialCommand = initialCommand; + this._incrementalCommand = incrementalCommand; + this._commandForHash = commandForHash; + this._ignoredParameterValues = ignoredParameterValues; } - public async executeAsync(context: IOperationRunnerContext): Promise { - try { - return await this._executeAsync(context); - } catch (error) { - throw new OperationError('executing', (error as Error).message); - } - } - - private async _executeAsync(context: IOperationRunnerContext): Promise { - // TERMINAL PIPELINE: - // - // +--> quietModeTransform? --> collatedWriter - // | - // normalizeNewlineTransform --1--> stderrLineTransform --2--> removeColorsTransform --> projectLogWritable - // | - // +--> stdioSummarizer - const projectLogWritable: ProjectLogWritable = new ProjectLogWritable( - this._rushProject, - context.collatedWriter.terminal, - this._logFilenameIdentifier - ); - - try { - const removeColorsTransform: TextRewriterTransform = new TextRewriterTransform({ - destination: projectLogWritable, - removeColors: true, - normalizeNewlines: NewlineKind.OsDefault - }); - - const splitterTransform2: SplitterTransform = new SplitterTransform({ - destinations: [removeColorsTransform, context.stdioSummarizer] - }); - - const stderrLineTransform: StderrLineTransform = new StderrLineTransform({ - destination: splitterTransform2, - newlineKind: NewlineKind.Lf // for StdioSummarizer - }); - - const discardTransform: DiscardStdoutTransform = new DiscardStdoutTransform({ - destination: context.collatedWriter - }); - - const splitterTransform1: SplitterTransform = new SplitterTransform({ - destinations: [context.quietMode ? discardTransform : context.collatedWriter, stderrLineTransform] - }); - - const normalizeNewlineTransform: TextRewriterTransform = new TextRewriterTransform({ - destination: splitterTransform1, - normalizeNewlines: NewlineKind.Lf, - ensureNewlineAtEnd: true - }); - - const collatedTerminal: CollatedTerminal = new CollatedTerminal(normalizeNewlineTransform); - const terminalProvider: CollatedTerminalProvider = new CollatedTerminalProvider(collatedTerminal, { - debugEnabled: context.debugMode - }); - const terminal: Terminal = new Terminal(terminalProvider); - - // Controls the log for the cache subsystem - const buildCacheCollatedTerminal: CollatedTerminal = new CollatedTerminal(context.collatedWriter); - const buildCacheTerminalProvider: CollatedTerminalProvider = new CollatedTerminalProvider( - buildCacheCollatedTerminal, - { - debugEnabled: context.debugMode - } - ); - const buildCacheTerminal: Terminal = new Terminal(buildCacheTerminalProvider); - - let hasWarningOrError: boolean = false; - const projectFolder: string = this._rushProject.projectFolder; - let lastProjectDeps: IProjectDeps | undefined = undefined; - - const currentDepsPath: string = path.join( - this._rushProject.projectRushTempFolder, - this._packageDepsFilename - ); - - if (FileSystem.exists(currentDepsPath)) { - try { - lastProjectDeps = JsonFile.load(currentDepsPath); - } catch (e) { - // Warn and ignore - treat failing to load the file as the project being not built. - terminal.writeWarningLine( - `Warning: error parsing ${this._packageDepsFilename}: ${e}. Ignoring and ` + - `treating the command "${this._commandToRun}" as not run.` + public async executeAsync( + context: IOperationRunnerContext, + lastState?: IOperationLastState + ): Promise { + return await context.runWithTerminalAsync( + async (terminal: ITerminal, terminalProvider: ITerminalProvider) => { + let hasWarningOrError: boolean = false; + + // Log any ignored parameters + if (this._ignoredParameterValues.length > 0) { + terminal.writeLine( + `These parameters were ignored for this operation by project-level configuration: ${this._ignoredParameterValues.join(' ')}` ); } - } - - let projectDeps: IProjectDeps | undefined; - let trackedProjectFiles: string[] | undefined; - try { - const fileHashes: Map | undefined = - await this._projectChangeAnalyzer._tryGetProjectDependenciesAsync(this._rushProject, terminal); - - if (fileHashes) { - const files: { [filePath: string]: string } = {}; - trackedProjectFiles = []; - for (const [filePath, fileHash] of fileHashes) { - files[filePath] = fileHash; - trackedProjectFiles.push(filePath); - } - - projectDeps = { - files, - arguments: this._commandToRun - }; - } else if (this.isSkipAllowed) { - // To test this code path: - // Remove the `.git` folder then run "rush build --verbose" - terminal.writeLine({ - text: PrintUtilities.wrapWords( - 'This workspace does not appear to be tracked by Git. ' + - 'Rush will proceed without incremental execution, caching, and change detection.' - ), - foregroundColor: ColorValue.Cyan - }); - } - } catch (error) { - // To test this code path: - // Delete a project's ".rush/temp/shrinkwrap-deps.json" then run "rush build --verbose" - terminal.writeLine('Unable to calculate incremental state: ' + (error as Error).toString()); - terminal.writeLine({ - text: 'Rush will proceed without incremental execution, caching, and change detection.', - foregroundColor: ColorValue.Cyan - }); - } - - // If possible, we want to skip this operation -- either by restoring it from the - // cache, if caching is enabled, or determining that the project - // is unchanged (using the older incremental execution logic). These two approaches, - // "caching" and "skipping", are incompatible, so only one applies. - // - // Note that "caching" and "skipping" take two different approaches - // to tracking dependents: - // - // - For caching, "isCacheReadAllowed" is set if a project supports - // incremental builds, and determining whether this project or a dependent - // has changed happens inside the hashing logic. - // - // - For skipping, "isSkipAllowed" is set to true initially, and during - // the process of running dependents, it will be changed by OperationExecutionManager to - // false if a dependency wasn't able to be skipped. - // - let buildCacheReadAttempted: boolean = false; - if (this._isCacheReadAllowed) { - const projectBuildCache: ProjectBuildCache | undefined = await this._tryGetProjectBuildCacheAsync({ - terminal: buildCacheTerminal, - trackedProjectFiles, - operationMetadataManager: context._operationMetadataManager - }); - - buildCacheReadAttempted = !!projectBuildCache; - const restoreFromCacheSuccess: boolean | undefined = - await projectBuildCache?.tryRestoreFromCacheAsync(buildCacheTerminal); + const incrementalCommand: string | undefined = + lastState && this._incrementalCommand ? this._incrementalCommand : undefined; + const commandToRun: string = incrementalCommand ?? this._initialCommand; - if (restoreFromCacheSuccess) { - // Restore the original state of the operation without cache - await context._operationMetadataManager?.tryRestoreAsync({ - terminal, - logPath: projectLogWritable.logPath, - errorLogPath: projectLogWritable.errorLogPath - }); - return OperationStatus.FromCache; - } - } - if (this.isSkipAllowed && !buildCacheReadAttempted) { - const isPackageUnchanged: boolean = !!( - lastProjectDeps && - projectDeps && - projectDeps.arguments === lastProjectDeps.arguments && - _areShallowEqual(projectDeps.files, lastProjectDeps.files) + // Run the operation + terminal.writeLine( + `Invoking (${incrementalCommand !== undefined ? 'incremental' : 'initial'}): ${commandToRun}` ); - if (isPackageUnchanged) { - return OperationStatus.Skipped; - } - } - - // If the deps file exists, remove it before starting execution. - FileSystem.deleteFile(currentDepsPath); - - // TODO: Remove legacyDepsPath with the next major release of Rush - const legacyDepsPath: string = path.join(this._rushProject.projectFolder, 'package-deps.json'); - // Delete the legacy package-deps.json - FileSystem.deleteFile(legacyDepsPath); - - if (!this._commandToRun) { - // Write deps on success. - if (projectDeps) { - JsonFile.save(projectDeps, currentDepsPath, { - ensureFolderExists: true - }); - } - - return OperationStatus.Success; - } + const { rushConfiguration, projectFolder } = this._rushProject; - // Run the operation - terminal.writeLine('Invoking: ' + this._commandToRun); + const { environment: initialEnvironment } = context; - const subProcess: child_process.ChildProcess = Utilities.executeLifecycleCommandAsync( - this._commandToRun, - { - rushConfiguration: this._rushConfiguration, + const subProcess: child_process.ChildProcess = Utilities.executeLifecycleCommandAsync(commandToRun, { + rushConfiguration: rushConfiguration, workingDirectory: projectFolder, - initCwd: this._rushConfiguration.commonTempFolder, + initCwd: rushConfiguration.commonTempFolder, handleOutput: true, environmentPathOptions: { includeProjectBin: true - } - } - ); + }, + initialEnvironment + }); - // Hook into events, in order to get live streaming of the log - if (subProcess.stdout !== null) { - subProcess.stdout.on('data', (data: Buffer) => { + // Hook into events, in order to get live streaming of the log + subProcess.stdout?.on('data', (data: Buffer) => { const text: string = data.toString(); - collatedTerminal.writeChunk({ text, kind: TerminalChunkKind.Stdout }); + terminalProvider.write(text, TerminalProviderSeverity.log); }); - } - if (subProcess.stderr !== null) { - subProcess.stderr.on('data', (data: Buffer) => { + subProcess.stderr?.on('data', (data: Buffer) => { const text: string = data.toString(); - collatedTerminal.writeChunk({ text, kind: TerminalChunkKind.Stderr }); + terminalProvider.write(text, TerminalProviderSeverity.error); hasWarningOrError = true; }); - } - let status: OperationStatus = await new Promise( - (resolve: (status: OperationStatus) => void, reject: (error: OperationError) => void) => { - subProcess.on('close', (code: number) => { - try { - if (code !== 0) { - reject(new OperationError('error', `Returned error code: ${code}`)); - } else if (hasWarningOrError) { - resolve(OperationStatus.SuccessWithWarning); - } else { - resolve(OperationStatus.Success); - } - } catch (error) { - reject(error as OperationError); - } - }); - } - ); - - // projectLogWritable should be closed before copy the logs to build cache - normalizeNewlineTransform.close(); - - // If the pipeline is wired up correctly, then closing normalizeNewlineTransform should - // have closed projectLogWritable. - if (projectLogWritable.isOpen) { - throw new InternalError('The output file handle was not closed'); - } - - const taskIsSuccessful: boolean = - status === OperationStatus.Success || - (status === OperationStatus.SuccessWithWarning && - this.warningsAreAllowed && - !!this._rushConfiguration.experimentsConfiguration.configuration - .buildCacheWithAllowWarningsInSuccessfulBuild); - - if (taskIsSuccessful && projectDeps) { - // Write deps on success. - const writeProjectStatePromise: Promise = JsonFile.saveAsync(projectDeps, currentDepsPath, { - ensureFolderExists: true - }); - - // If the operation without cache was successful, we can save the metadata to disk - const { duration: durationInSeconds } = context.stopwatch; - await context._operationMetadataManager?.saveAsync({ - durationInSeconds, - logPath: projectLogWritable.logPath, - errorLogPath: projectLogWritable.errorLogPath - }); - - // If the command is successful, we can calculate project hash, and no dependencies were skipped, - // write a new cache entry. - const setCacheEntryPromise: Promise | undefined = this.isCacheWriteAllowed - ? ( - await this._tryGetProjectBuildCacheAsync({ - terminal: buildCacheTerminal, - trackedProjectFiles, - operationMetadataManager: context._operationMetadataManager - }) - )?.trySetCacheEntryAsync(buildCacheTerminal) - : undefined; - - const [, cacheWriteSuccess] = await Promise.all([writeProjectStatePromise, setCacheEntryPromise]); - - if (buildCacheTerminalProvider.hasErrors) { - status = OperationStatus.Failure; - } else if (cacheWriteSuccess === false) { - status = OperationStatus.SuccessWithWarning; - } - } - - return status; - } finally { - projectLogWritable.close(); - } - } - - private async _tryGetProjectBuildCacheAsync({ - terminal, - trackedProjectFiles, - operationMetadataManager - }: { - terminal: ITerminal; - trackedProjectFiles: string[] | undefined; - operationMetadataManager: OperationMetadataManager | undefined; - }): Promise { - if (this._projectBuildCache === UNINITIALIZED) { - this._projectBuildCache = undefined; - - if (this._buildCacheConfiguration && this._buildCacheConfiguration.buildCacheEnabled) { - // Disable legacy skip logic if the build cache is in play - this.isSkipAllowed = false; - - const projectConfiguration: RushProjectConfiguration | undefined = - await RushProjectConfiguration.tryLoadForProjectAsync(this._rushProject, terminal); - if (projectConfiguration) { - projectConfiguration.validatePhaseConfiguration(this._selectedPhases, terminal); - if (projectConfiguration.disableBuildCacheForProject) { - terminal.writeVerboseLine('Caching has been disabled for this project.'); - } else { - const operationSettings: IOperationSettings | undefined = - projectConfiguration.operationSettingsByOperationName.get(this._commandName); - if (!operationSettings) { - terminal.writeVerboseLine( - `This project does not define the caching behavior of the "${this._commandName}" command, so caching has been disabled.` - ); - } else if (operationSettings.disableBuildCacheForOperation) { - terminal.writeVerboseLine( - `Caching has been disabled for this project's "${this._commandName}" command.` - ); - } else { - const projectOutputFolderNames: ReadonlyArray = - operationSettings.outputFolderNames || []; - const additionalProjectOutputFilePaths: ReadonlyArray = [ - ...(operationMetadataManager?.relativeFilepaths || []) - ]; - const additionalContext: Record = {}; - if (operationSettings.dependsOnEnvVars) { - for (const varName of operationSettings.dependsOnEnvVars) { - additionalContext['$' + varName] = process.env[varName] || ''; + const status: OperationStatus = await new Promise( + (resolve: (status: OperationStatus) => void, reject: (error: OperationError) => void) => { + subProcess.on('close', (exitCode: number | null, signal: NodeJS.Signals | null) => { + try { + // Do NOT reject here immediately, give a chance for other logic to suppress the error + if (signal) { + context.error = new OperationError('error', `Terminated by signal: ${signal}`); + resolve(OperationStatus.Failure); + } else if (exitCode !== 0) { + context.error = new OperationError('error', `Returned error code: ${exitCode}`); + resolve(OperationStatus.Failure); + } else if (hasWarningOrError) { + resolve(OperationStatus.SuccessWithWarning); + } else { + resolve(OperationStatus.Success); } + } catch (error) { + context.error = error as OperationError; + reject(error as OperationError); } - - if (operationSettings.dependsOnAdditionalFiles) { - const repoState: IRawRepoState | undefined = - await this._projectChangeAnalyzer._ensureInitializedAsync(terminal); - - const additionalFiles: Map = await getHashesForGlobsAsync( - operationSettings.dependsOnAdditionalFiles, - this._rushProject.projectFolder, - repoState - ); - - terminal.writeDebugLine( - `Including additional files to calculate build cache hash:\n ${Array.from( - additionalFiles.keys() - ).join('\n ')} ` - ); - - for (const [filePath, fileHash] of additionalFiles) { - additionalContext['file://' + filePath] = fileHash; - } - } - this._projectBuildCache = await ProjectBuildCache.tryGetProjectBuildCache({ - projectConfiguration, - projectOutputFolderNames, - additionalProjectOutputFilePaths, - additionalContext, - buildCacheConfiguration: this._buildCacheConfiguration, - terminal, - command: this._commandToRun, - trackedProjectFiles: trackedProjectFiles, - projectChangeAnalyzer: this._projectChangeAnalyzer, - phaseName: this._phase.name - }); - } + }); } - } else { - terminal.writeVerboseLine( - `Project does not have a ${RushConstants.rushProjectConfigFilename} configuration file, ` + - 'or one provided by a rig, so it does not support caching.' - ); - } + ); + + return status; + }, + { + createLogFile: true } - } + ); + } - return this._projectBuildCache; + public getConfigHash(): string { + return this._commandForHash; } } diff --git a/libraries/rush-lib/src/logic/operations/ShellOperationRunnerPlugin.ts b/libraries/rush-lib/src/logic/operations/ShellOperationRunnerPlugin.ts index d9a3e08a55b..990aedb6177 100644 --- a/libraries/rush-lib/src/logic/operations/ShellOperationRunnerPlugin.ts +++ b/libraries/rush-lib/src/logic/operations/ShellOperationRunnerPlugin.ts @@ -12,119 +12,237 @@ import type { IPhasedCommandPlugin, PhasedCommandHooks } from '../../pluginFramework/PhasedCommandHooks'; -import { Operation } from './Operation'; +import type { Operation } from './Operation'; +import type { RushConfiguration } from '../../api/RushConfiguration'; +import type { IOperationRunner } from './IOperationRunner'; +import { IS_WINDOWS } from '../../utilities/executionUtilities'; -const PLUGIN_NAME: 'ShellOperationRunnerPlugin' = 'ShellOperationRunnerPlugin'; +export const PLUGIN_NAME: 'ShellOperationRunnerPlugin' = 'ShellOperationRunnerPlugin'; /** * Core phased command plugin that provides the functionality for executing an operation via shell command. */ export class ShellOperationRunnerPlugin implements IPhasedCommandPlugin { public apply(hooks: PhasedCommandHooks): void { - hooks.createOperations.tap(PLUGIN_NAME, createShellOperations); + hooks.createOperationsAsync.tap( + PLUGIN_NAME, + function createShellOperations( + operations: Set, + context: ICreateOperationsContext + ): Set { + const { rushConfiguration, isIncrementalBuildAllowed } = context; + + const getCustomParameterValues: (operation: Operation) => ICustomParameterValuesForOperation = + getCustomParameterValuesByOperation(); + + for (const operation of operations) { + const { associatedPhase: phase, associatedProject: project } = operation; + + if (!operation.runner) { + // This is a shell command. In the future, may consider having a property on the initial operation + // to specify a runner type requested in rush-project.json + const { parameterValues: customParameterValues, ignoredParameterValues } = + getCustomParameterValues(operation); + + const displayName: string = getDisplayName(phase, project); + const { name: phaseName, shellCommand } = phase; + + const { scripts } = project.packageJson; + + // This is the command that will be used to identify the cache entry for this operation + const commandForHash: string | undefined = shellCommand ?? scripts?.[phaseName]; + + // For execution of non-initial iterations, prefer the `:incremental` script if it exists. + // However, the `shellCommand` value still takes precedence per the spec for that feature. + const initialCommand: string | undefined = shellCommand ?? scripts?.[phaseName]; + const incrementalCommand: string | undefined = isIncrementalBuildAllowed + ? (shellCommand ?? scripts?.[`${phaseName}:incremental`]) + : undefined; + + operation.runner = initializeShellOperationRunner({ + phase, + project, + displayName, + commandForHash, + initialCommand, + incrementalCommand, + customParameterValues, + ignoredParameterValues, + rushConfiguration + }); + } + } + + return operations; + } + ); } } -function createShellOperations( - operations: Set, - context: ICreateOperationsContext -): Set { +export function initializeShellOperationRunner(options: { + phase: IPhase; + project: RushConfigurationProject; + displayName: string; + rushConfiguration: RushConfiguration; + initialCommand: string | undefined; + incrementalCommand: string | undefined; + commandForHash?: string; + customParameterValues: ReadonlyArray; + ignoredParameterValues: ReadonlyArray; +}): IOperationRunner { const { - buildCacheConfiguration, - isIncrementalBuildAllowed, - phaseSelection: selectedPhases, - projectChangeAnalyzer, - rushConfiguration - } = context; + phase, + project, + initialCommand: rawInitialCommand, + incrementalCommand: rawIncrementalCommand, + displayName, + ignoredParameterValues + } = options; + + if (typeof rawInitialCommand !== 'string' && phase.missingScriptBehavior === 'error') { + throw new Error( + `The project '${project.packageName}' does not define a '${phase.name}' command in the 'scripts' section of its package.json` + ); + } + + if (rawInitialCommand) { + const { commandForHash: rawCommandForHash, customParameterValues } = options; + + const initialCommand: string = formatCommand(rawInitialCommand, customParameterValues); + const incrementalCommand: string | undefined = rawIncrementalCommand + ? formatCommand(rawIncrementalCommand, customParameterValues) + : undefined; + const commandForHash: string = rawCommandForHash + ? formatCommand(rawCommandForHash, customParameterValues) + : initialCommand; + + return new ShellOperationRunner({ + initialCommand, + incrementalCommand, + commandForHash, + displayName, + phase, + rushProject: project, + ignoredParameterValues + }); + } else { + // Empty build script indicates a no-op, so use a no-op runner + return new NullOperationRunner({ + name: displayName, + result: OperationStatus.NoOp, + silent: phase.missingScriptBehavior === 'silent' + }); + } +} + +/** + * Result of filtering custom parameters for an operation + */ +export interface ICustomParameterValuesForOperation { + /** + * The serialized custom parameter values that should be included in the command + */ + parameterValues: ReadonlyArray; + /** + * The serialized custom parameter values that were ignored for this operation + */ + ignoredParameterValues: ReadonlyArray; +} + +/** + * Helper function to collect all parameter arguments for a phase + */ +function collectPhaseParameterArguments(phase: IPhase): string[] { + const customParameterList: string[] = []; + for (const tsCommandLineParameter of phase.associatedParameters) { + tsCommandLineParameter.appendToArgList(customParameterList); + } + return customParameterList; +} +/** + * Memoizer for custom parameter values by phase + * @returns A function that returns the custom parameter values for a given phase + */ +export function getCustomParameterValuesByPhase(): (phase: IPhase) => ReadonlyArray { const customParametersByPhase: Map = new Map(); function getCustomParameterValuesForPhase(phase: IPhase): ReadonlyArray { - let customParameterValues: string[] | undefined = customParametersByPhase.get(phase); - if (!customParameterValues) { - customParameterValues = []; - for (const tsCommandLineParameter of phase.associatedParameters) { - tsCommandLineParameter.appendToArgList(customParameterValues); - } - - customParametersByPhase.set(phase, customParameterValues); + let customParameterList: string[] | undefined = customParametersByPhase.get(phase); + if (!customParameterList) { + customParameterList = collectPhaseParameterArguments(phase); + customParametersByPhase.set(phase, customParameterList); } - return customParameterValues; + return customParameterList; } - for (const operation of operations) { - const { associatedPhase: phase, associatedProject: project } = operation; - - if (phase && project && !operation.runner) { - // This is a shell command. In the future, may consider having a property on the initial operation - // to specify a runner type requested in rush-project.json - const customParameterValues: ReadonlyArray = getCustomParameterValuesForPhase(phase); + return getCustomParameterValuesForPhase; +} - const commandToRun: string | undefined = getScriptToRun( - project, - phase.name, - customParameterValues, - phase.shellCommand - ); +/** + * Gets custom parameter values for an operation, filtering out any parameters that should be ignored + * based on the operation's settings. + * @returns A function that returns the filtered custom parameter values and ignored parameter values for a given operation + */ +export function getCustomParameterValuesByOperation(): ( + operation: Operation +) => ICustomParameterValuesForOperation { + const customParametersByPhase: Map = new Map(); - if (commandToRun === undefined && phase.missingScriptBehavior === 'error') { - throw new Error( - `The project '${project.packageName}' does not define a '${phase.name}' command in the 'scripts' section of its package.json` - ); + function getCustomParameterValuesForOp(operation: Operation): ICustomParameterValuesForOperation { + const { associatedPhase: phase, settings } = operation; + + // Check if there are any parameters to ignore + const parameterNamesToIgnore: string[] | undefined = settings?.parameterNamesToIgnore; + if (!parameterNamesToIgnore || parameterNamesToIgnore.length === 0) { + // No filtering needed - use the cached parameter list for efficiency + let customParameterList: string[] | undefined = customParametersByPhase.get(phase); + if (!customParameterList) { + customParameterList = collectPhaseParameterArguments(phase); + customParametersByPhase.set(phase, customParameterList); } - const displayName: string = getDisplayName(phase, project); - - if (commandToRun) { - operation.runner = new ShellOperationRunner({ - buildCacheConfiguration, - commandToRun: commandToRun || '', - displayName, - isIncrementalBuildAllowed, - phase, - projectChangeAnalyzer, - rushConfiguration, - rushProject: project, - selectedPhases - }); - } else { - // Empty build script indicates a no-op, so use a no-op runner - operation.runner = new NullOperationRunner({ - name: displayName, - result: OperationStatus.NoOp, - silent: phase.missingScriptBehavior === 'silent' - }); - } + return { + parameterValues: customParameterList, + ignoredParameterValues: [] + }; } - } - return operations; -} + // Filtering is needed - we must iterate through parameter objects to check longName + // Note: We cannot use the cached parameter list here because we need access to + // the parameter objects to get their longName property for filtering + const ignoreSet: Set = new Set(parameterNamesToIgnore); + const filteredParameterValues: string[] = []; + const ignoredParameterValues: string[] = []; -function getScriptToRun( - rushProject: RushConfigurationProject, - commandToRun: string, - customParameterValues: ReadonlyArray, - shellCommand: string | undefined -): string | undefined { - const { scripts } = rushProject.packageJson; + for (const tsCommandLineParameter of phase.associatedParameters) { + const parameterLongName: string = tsCommandLineParameter.longName; - const rawCommand: string | undefined | null = shellCommand ?? scripts?.[commandToRun]; + tsCommandLineParameter.appendToArgList( + ignoreSet.has(parameterLongName) ? ignoredParameterValues : filteredParameterValues + ); + } - if (rawCommand === undefined || rawCommand === null) { - return undefined; + return { + parameterValues: filteredParameterValues, + ignoredParameterValues + }; } + return getCustomParameterValuesForOp; +} + +export function formatCommand(rawCommand: string, customParameterValues: ReadonlyArray): string { if (!rawCommand) { return ''; } else { - const shellCommand: string = `${rawCommand} ${customParameterValues.join(' ')}`; - return process.platform === 'win32' ? convertSlashesForWindows(shellCommand) : shellCommand; + const fullCommand: string = `${rawCommand} ${customParameterValues.join(' ')}`; + return IS_WINDOWS ? convertSlashesForWindows(fullCommand) : fullCommand; } } -function getDisplayName(phase: IPhase, project: RushConfigurationProject): string { +export function getDisplayName(phase: IPhase, project: RushConfigurationProject): string { if (phase.isSynthetic) { // Because this is a synthetic phase, just use the project name because there aren't any other phases return project.packageName; diff --git a/libraries/rush-lib/src/logic/operations/ValidateOperationsPlugin.ts b/libraries/rush-lib/src/logic/operations/ValidateOperationsPlugin.ts new file mode 100644 index 00000000000..0f256c4789b --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/ValidateOperationsPlugin.ts @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { ITerminal } from '@rushstack/terminal'; + +import type { IPhasedCommandPlugin, PhasedCommandHooks } from '../../pluginFramework/PhasedCommandHooks'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { RushProjectConfiguration } from '../../api/RushProjectConfiguration'; +import type { IPhase } from '../../api/CommandLineConfiguration'; + +const PLUGIN_NAME: 'ValidateOperationsPlugin' = 'ValidateOperationsPlugin'; + +/** + * Core phased command plugin that verifies correctness of the entries in rush-project.json + */ +export class ValidateOperationsPlugin implements IPhasedCommandPlugin { + private readonly _terminal: ITerminal; + + public constructor(terminal: ITerminal) { + this._terminal = terminal; + } + + public apply(hooks: PhasedCommandHooks): void { + hooks.onGraphCreatedAsync.tap(PLUGIN_NAME, (graph, context) => { + const phasesByProject: Map> = new Map(); + for (const { associatedPhase, associatedProject, runner } of graph.operations) { + if (!runner?.isNoOp) { + // Ignore operations that aren't associated with a project or phase, or that + // use the NullOperationRunner (i.e. - the phase doesn't do anything) + let projectPhases: Set | undefined = phasesByProject.get(associatedProject); + if (!projectPhases) { + projectPhases = new Set(); + phasesByProject.set(associatedProject, projectPhases); + } + + projectPhases.add(associatedPhase); + } + } + + for (const [project, phases] of phasesByProject) { + const projectConfiguration: RushProjectConfiguration | undefined = + context.projectConfigurations.get(project); + if (projectConfiguration) { + projectConfiguration.validatePhaseConfiguration(phases, this._terminal); + } + } + }); + } +} diff --git a/libraries/rush-lib/src/logic/operations/test/AsyncOperationQueue.test.ts b/libraries/rush-lib/src/logic/operations/test/AsyncOperationQueue.test.ts index 1270b19c262..e93441a49fd 100644 --- a/libraries/rush-lib/src/logic/operations/test/AsyncOperationQueue.test.ts +++ b/libraries/rush-lib/src/logic/operations/test/AsyncOperationQueue.test.ts @@ -2,25 +2,56 @@ // See LICENSE in the project root for license information. import { Operation } from '../Operation'; -import { IOperationExecutionRecordContext, OperationExecutionRecord } from '../OperationExecutionRecord'; +import { type IOperationExecutionRecordContext, OperationExecutionRecord } from '../OperationExecutionRecord'; import { MockOperationRunner } from './MockOperationRunner'; -import { AsyncOperationQueue, IOperationSortFunction } from '../AsyncOperationQueue'; +import { AsyncOperationQueue, type IOperationSortFunction } from '../AsyncOperationQueue'; +import { OperationStatus } from '../OperationStatus'; +import type { RushConfigurationProject } from '../../../api/RushConfigurationProject'; +import type { IPhase } from '../../../api/CommandLineConfiguration'; function addDependency(consumer: OperationExecutionRecord, dependency: OperationExecutionRecord): void { consumer.dependencies.add(dependency); dependency.consumers.add(consumer); + consumer.status = OperationStatus.Waiting; } function nullSort(a: OperationExecutionRecord, b: OperationExecutionRecord): number { return 0; } +const mockPhase: IPhase = { + name: 'phase', + allowWarningsOnSuccess: false, + associatedParameters: new Set(), + dependencies: { + self: new Set(), + upstream: new Set() + }, + isSynthetic: false, + logFilenameIdentifier: 'phase', + missingScriptBehavior: 'silent' +}; +const projectsByName: Map = new Map(); +function getOrCreateProject(name: string): RushConfigurationProject { + let project: RushConfigurationProject | undefined = projectsByName.get(name); + if (!project) { + project = { + packageName: name + } as unknown as RushConfigurationProject; + projectsByName.set(name, project); + } + return project; +} + function createRecord(name: string): OperationExecutionRecord { return new OperationExecutionRecord( new Operation({ - runner: new MockOperationRunner(name) + runner: new MockOperationRunner(name), + logFilenameIdentifier: 'operation', + phase: mockPhase, + project: getOrCreateProject(name) }), - {} as unknown as IOperationExecutionRecordContext + { maxParallelism: 10 } as unknown as IOperationExecutionRecordContext ); } @@ -37,9 +68,8 @@ describe(AsyncOperationQueue.name, () => { const queue: AsyncOperationQueue = new AsyncOperationQueue(operations, nullSort); for await (const operation of queue) { actualOrder.push(operation); - for (const consumer of operation.consumers) { - consumer.dependencies.delete(operation); - } + operation.status = OperationStatus.Success; + queue.complete(operation); } expect(actualOrder).toEqual(expectedOrder); @@ -60,15 +90,14 @@ describe(AsyncOperationQueue.name, () => { const queue: AsyncOperationQueue = new AsyncOperationQueue(operations, customSort); for await (const operation of queue) { actualOrder.push(operation); - for (const consumer of operation.consumers) { - consumer.dependencies.delete(operation); - } + operation.status = OperationStatus.Success; + queue.complete(operation); } expect(actualOrder).toEqual(expectedOrder); }); - it('detects cyles', async () => { + it('detects cycles', async () => { const operations = [createRecord('a'), createRecord('b'), createRecord('c'), createRecord('d')]; addDependency(operations[0], operations[2]); @@ -119,11 +148,9 @@ describe(AsyncOperationQueue.name, () => { await Promise.resolve(); - for (const consumer of operation.consumers) { - consumer.dependencies.delete(operation); - } - --concurrency; + operation.status = OperationStatus.Success; + queue.complete(operation); } }) ); @@ -132,4 +159,83 @@ describe(AsyncOperationQueue.name, () => { expect(actualConcurrency.get(operation)).toEqual(operationConcurrency); } }); + + it('handles an empty queue', async () => { + const operations: OperationExecutionRecord[] = []; + + const queue: AsyncOperationQueue = new AsyncOperationQueue(operations, nullSort); + const iterator: AsyncIterator = queue[Symbol.asyncIterator](); + const result: IteratorResult = await iterator.next(); + expect(result.done).toEqual(true); + }); + + it('sorts cobuild retries after untried operations', async () => { + // Three independent operations: A, B, C (all Ready). + // A is assigned first, then returns to Ready (cobuild lock failed). + // On the next pass, B and C should be assigned before A because A + // has a recent lastAssignedAt timestamp. + const opA = createRecord('a'); + const opB = createRecord('b'); + const opC = createRecord('c'); + + const queue: AsyncOperationQueue = new AsyncOperationQueue([opA, opB, opC], nullSort); + + // Assign one operation + const r1: IteratorResult = await queue.next(); + const firstAssigned: OperationExecutionRecord = r1.value; + + // Simulate cobuild retry: operation returns to Ready + firstAssigned.status = OperationStatus.Ready; + + // Assign all three - untried operations should come before the retry + const results: OperationExecutionRecord[] = []; + for await (const item of queue) { + results.push(item); + queue.complete(item); + } + + // The cobuild retry should be last + expect(results[2]).toBe(firstAssigned); + }); + + it('assigns freshly unblocked operations before cobuild retries', async () => { + // A (no deps), B (depends on C), C (no deps) + // A is assigned and returns to Ready (cobuild retry). + // C completes, unblocking B. B should be assigned before A. + const opA = createRecord('a'); + const opB = createRecord('b'); + const opC = createRecord('c'); + + addDependency(opB, opC); + + const queue: AsyncOperationQueue = new AsyncOperationQueue([opA, opB, opC], nullSort); + + // Pull both initially ready operations (A and C) + const r1: IteratorResult = await queue.next(); + const r2: IteratorResult = await queue.next(); + expect(new Set([r1.value, r2.value])).toEqual(new Set([opA, opC])); + + // Simulate: A fails cobuild lock and returns to Ready + opA.status = OperationStatus.Ready; + + // C succeeds, which unblocks B + opC.status = OperationStatus.Success; + queue.complete(opC); + + // B is freshly unblocked (never assigned), A is a cobuild retry - B should be first + const r3: IteratorResult = await queue.next(); + expect(r3.value).toBe(opB); + + const r4: IteratorResult = await queue.next(); + expect(r4.value).toBe(opA); + + // Complete remaining + opA.status = OperationStatus.Success; + queue.complete(opA); + opB.status = OperationStatus.Success; + queue.complete(opB); + + const rEnd: IteratorResult = await queue.next(); + expect(rEnd.done).toBe(true); + }); }); diff --git a/libraries/rush-lib/src/logic/operations/test/BuildPlanPlugin.test.ts b/libraries/rush-lib/src/logic/operations/test/BuildPlanPlugin.test.ts new file mode 100644 index 00000000000..daad7a03431 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/test/BuildPlanPlugin.test.ts @@ -0,0 +1,151 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; +import { MockWritable, StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; +import { JsonFile } from '@rushstack/node-core-library'; +import { BuildPlanPlugin } from '../BuildPlanPlugin'; +import { + type ICreateOperationsContext, + type IOperationGraphContext as IOperationExecutionManagerContext, + PhasedCommandHooks +} from '../../../pluginFramework/PhasedCommandHooks'; +import type { Operation } from '../Operation'; +import { RushConfiguration } from '../../../api/RushConfiguration'; +import { + CommandLineConfiguration, + type IPhase, + type IPhasedCommandConfig +} from '../../../api/CommandLineConfiguration'; +import { PhasedOperationPlugin } from '../PhasedOperationPlugin'; +import type { RushConfigurationProject } from '../../../api/RushConfigurationProject'; +import { RushConstants } from '../../RushConstants'; +import { MockOperationRunner } from './MockOperationRunner'; +import type { ICommandLineJson } from '../../../api/CommandLineJson'; +import type { IInputsSnapshot } from '../../incremental/InputsSnapshot'; +import { OperationGraph } from '../OperationGraph'; + +describe(BuildPlanPlugin.name, () => { + const rushJsonFile: string = path.resolve(__dirname, `../../test/workspaceRepo/rush.json`); + const commandLineJsonFile: string = path.resolve( + __dirname, + `../../test/workspaceRepo/common/config/rush/command-line.json` + ); + let rushConfiguration!: RushConfiguration; + let commandLineConfiguration!: CommandLineConfiguration; + let stringBufferTerminalProvider!: StringBufferTerminalProvider; + let terminal!: Terminal; + const mockStreamWritable: MockWritable = new MockWritable(); + beforeEach(() => { + stringBufferTerminalProvider = new StringBufferTerminalProvider(); + terminal = new Terminal(stringBufferTerminalProvider); + mockStreamWritable.reset(); + rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); + const commandLineJson: ICommandLineJson = JsonFile.load(commandLineJsonFile); + + commandLineConfiguration = new CommandLineConfiguration(commandLineJson); + }); + + function createMockRunner(operations: Set, context: ICreateOperationsContext): Set { + for (const operation of operations) { + const { associatedPhase, associatedProject } = operation; + + if (!operation.runner) { + const name: string = `${associatedProject.packageName} (${associatedPhase.name.slice( + RushConstants.phaseNamePrefix.length + )})`; + + operation.runner = new MockOperationRunner(name, undefined, undefined, false); + } + } + + return operations; + } + + async function testCreateOperationsAsync( + hooks: PhasedCommandHooks, + phaseSelection: Set, + projectSelection: Set, + changedProjects: Set + ): Promise { + // Add mock runners for included operations. + hooks.createOperationsAsync.tap('MockOperationRunnerPlugin', createMockRunner); + + const createOperationsContext: Pick< + ICreateOperationsContext, + 'phaseSelection' | 'projectSelection' | 'projectConfigurations' + > = { + phaseSelection, + projectSelection, + projectConfigurations: new Map() + }; + const operations: Set = await hooks.createOperationsAsync.promise( + new Set(), + createOperationsContext as ICreateOperationsContext + ); + + const graph: OperationGraph = new OperationGraph(operations, { + debugMode: false, + quietMode: true, + destinations: [mockStreamWritable], + allowOversubscription: true, + parallelism: 1, + abortController: new AbortController() + }); + + const operationManagerContext: Pick< + IOperationExecutionManagerContext, + 'projectConfigurations' | 'phaseSelection' | 'projectSelection' + > = { + projectConfigurations: new Map(), + phaseSelection, + projectSelection + }; + + await hooks.onGraphCreatedAsync.promise( + graph, + operationManagerContext as IOperationExecutionManagerContext + ); + + return graph; + } + + describe('build plan debugging', () => { + it('should generate a build plan', async () => { + const hooks: PhasedCommandHooks = new PhasedCommandHooks(); + new PhasedOperationPlugin().apply(hooks); + // Apply the plugin being tested + new BuildPlanPlugin(terminal).apply(hooks); + const buildCommand: IPhasedCommandConfig = commandLineConfiguration.commands.get( + 'build' + )! as IPhasedCommandConfig; + + const graph = await testCreateOperationsAsync( + hooks, + buildCommand.phases, + new Set(rushConfiguration.projects), + new Set(rushConfiguration.projects) + ); + + const inputsSnapshot: Pick< + IInputsSnapshot, + 'getTrackedFileHashesForOperation' | 'getOperationOwnStateHash' + > = { + getTrackedFileHashesForOperation() { + return new Map(); + }, + getOperationOwnStateHash() { + return '0'; + } + }; + await graph.executeAsync({ inputsSnapshot: inputsSnapshot as IInputsSnapshot }); + + expect( + stringBufferTerminalProvider.getAllOutputAsChunks({ + normalizeSpecialCharacters: false, + asLines: true + }) + ).toMatchSnapshot(); + }); + }); +}); diff --git a/libraries/rush-lib/src/logic/operations/test/IgnoredParametersPlugin.test.ts b/libraries/rush-lib/src/logic/operations/test/IgnoredParametersPlugin.test.ts new file mode 100644 index 00000000000..de074eff375 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/test/IgnoredParametersPlugin.test.ts @@ -0,0 +1,178 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; +import { JsonFile } from '@rushstack/node-core-library'; +import { ConsoleTerminalProvider, Terminal } from '@rushstack/terminal'; + +import { RushConfiguration } from '../../../api/RushConfiguration'; +import { CommandLineConfiguration, type IPhasedCommandConfig } from '../../../api/CommandLineConfiguration'; +import type { Operation } from '../Operation'; +import type { ICommandLineJson } from '../../../api/CommandLineJson'; +import { PhasedOperationPlugin } from '../PhasedOperationPlugin'; +import { ShellOperationRunnerPlugin } from '../ShellOperationRunnerPlugin'; +import { + IgnoredParametersPlugin, + RUSHSTACK_CLI_IGNORED_PARAMETER_NAMES_ENV_VAR +} from '../IgnoredParametersPlugin'; +import { + type ICreateOperationsContext, + type IOperationGraphContext, + PhasedCommandHooks +} from '../../../pluginFramework/PhasedCommandHooks'; +import type { IOperationGraph } from '../IOperationGraph'; +import { OperationGraphHooks } from '../../../pluginFramework/OperationGraphHooks'; +import { RushProjectConfiguration } from '../../../api/RushProjectConfiguration'; +import type { IEnvironment } from '../../../utilities/Utilities'; +import type { IOperationRunnerContext } from '../IOperationRunner'; +import type { IOperationExecutionResult } from '../IOperationExecutionResult'; + +/** + * Helper function to create a minimal mock record for testing the createEnvironmentForOperation hook + */ +function createMockRecord(operation: Operation): IOperationRunnerContext & IOperationExecutionResult { + return { + operation, + environment: undefined + } as IOperationRunnerContext & IOperationExecutionResult; +} + +describe(IgnoredParametersPlugin.name, () => { + it('should set RUSHSTACK_OPERATION_IGNORED_PARAMETERS environment variable', async () => { + const rushJsonFile: string = path.resolve(__dirname, `../../test/parameterIgnoringRepo/rush.json`); + const commandLineJsonFile: string = path.resolve( + __dirname, + `../../test/parameterIgnoringRepo/common/config/rush/command-line.json` + ); + + const rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); + const commandLineJson: ICommandLineJson = JsonFile.load(commandLineJsonFile); + + const commandLineConfiguration = new CommandLineConfiguration(commandLineJson); + const buildCommand: IPhasedCommandConfig = commandLineConfiguration.commands.get( + 'build' + )! as IPhasedCommandConfig; + + // Load project configurations + const terminalProvider: ConsoleTerminalProvider = new ConsoleTerminalProvider(); + const terminal: Terminal = new Terminal(terminalProvider); + + const projectConfigurations = await RushProjectConfiguration.tryLoadForProjectsAsync( + rushConfiguration.projects, + terminal + ); + + const fakeCreateOperationsContext: Pick< + ICreateOperationsContext, + 'phaseSelection' | 'projectSelection' | 'projectConfigurations' | 'rushConfiguration' + > = { + phaseSelection: buildCommand.phases, + projectSelection: new Set(rushConfiguration.projects), + projectConfigurations, + rushConfiguration + }; + + const hooks: PhasedCommandHooks = new PhasedCommandHooks(); + + // Apply plugins + new PhasedOperationPlugin().apply(hooks); + new ShellOperationRunnerPlugin().apply(hooks); + new IgnoredParametersPlugin().apply(hooks); + + const operations: Set = await hooks.createOperationsAsync.promise( + new Set(), + fakeCreateOperationsContext as unknown as ICreateOperationsContext + ); + + // Set up a mock graph and invoke onGraphCreatedAsync so the plugin registers its graph hooks + const graphHooks: OperationGraphHooks = new OperationGraphHooks(); + const fakeGraph: IOperationGraph = { hooks: graphHooks } as unknown as IOperationGraph; + await hooks.onGraphCreatedAsync.promise( + fakeGraph, + fakeCreateOperationsContext as unknown as IOperationGraphContext + ); + + // Test project 'a' which has parameterNamesToIgnore: ["--production"] + const operationA = Array.from(operations).find((op) => op.name === 'a'); + expect(operationA).toBeDefined(); + + // Create a mock operation execution result with required fields + const mockRecordA = createMockRecord(operationA!); + + // Call the hook to get the environment + const envA: IEnvironment = graphHooks.createEnvironmentForOperation.call({ ...process.env }, mockRecordA); + + // Verify the environment variable is set correctly for project 'a' + expect(envA[RUSHSTACK_CLI_IGNORED_PARAMETER_NAMES_ENV_VAR]).toBe('["--production"]'); + + // Test project 'b' which has parameterNamesToIgnore: ["--verbose", "--config", "--mode", "--tags"] + const operationB = Array.from(operations).find((op) => op.name === 'b'); + expect(operationB).toBeDefined(); + + const mockRecordB = createMockRecord(operationB!); + + const envB: IEnvironment = graphHooks.createEnvironmentForOperation.call({ ...process.env }, mockRecordB); + + // Verify the environment variable is set correctly for project 'b' + expect(envB[RUSHSTACK_CLI_IGNORED_PARAMETER_NAMES_ENV_VAR]).toBe( + '["--verbose","--config","--mode","--tags"]' + ); + }); + + it('should not set environment variable when parameterNamesToIgnore is not specified', async () => { + const rushJsonFile: string = path.resolve(__dirname, `../../test/customShellCommandinBulkRepo/rush.json`); + const commandLineJsonFile: string = path.resolve( + __dirname, + `../../test/customShellCommandinBulkRepo/common/config/rush/command-line.json` + ); + + const rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); + const commandLineJson: ICommandLineJson = JsonFile.load(commandLineJsonFile); + + const commandLineConfiguration = new CommandLineConfiguration(commandLineJson); + const echoCommand: IPhasedCommandConfig = commandLineConfiguration.commands.get( + 'echo' + )! as IPhasedCommandConfig; + + const fakeCreateOperationsContext: Pick< + ICreateOperationsContext, + 'phaseSelection' | 'projectSelection' | 'projectConfigurations' | 'rushConfiguration' + > = { + phaseSelection: echoCommand.phases, + projectSelection: new Set(rushConfiguration.projects), + projectConfigurations: new Map(), + rushConfiguration + }; + + const hooks: PhasedCommandHooks = new PhasedCommandHooks(); + + // Apply plugins + new PhasedOperationPlugin().apply(hooks); + new ShellOperationRunnerPlugin().apply(hooks); + new IgnoredParametersPlugin().apply(hooks); + + const operations: Set = await hooks.createOperationsAsync.promise( + new Set(), + fakeCreateOperationsContext as unknown as ICreateOperationsContext + ); + + // Set up a mock graph and invoke onGraphCreatedAsync so the plugin registers its graph hooks + const graphHooks: OperationGraphHooks = new OperationGraphHooks(); + const fakeGraph: IOperationGraph = { hooks: graphHooks } as unknown as IOperationGraph; + await hooks.onGraphCreatedAsync.promise( + fakeGraph, + fakeCreateOperationsContext as unknown as IOperationGraphContext + ); + + // Get any operation + const operation = Array.from(operations)[0]; + expect(operation).toBeDefined(); + + const mockRecord = createMockRecord(operation); + + const env: IEnvironment = graphHooks.createEnvironmentForOperation.call({ ...process.env }, mockRecord); + + // Verify the environment variable is not set + expect(env[RUSHSTACK_CLI_IGNORED_PARAMETER_NAMES_ENV_VAR]).toBeUndefined(); + }); +}); diff --git a/libraries/rush-lib/src/logic/operations/test/MockOperationRunner.ts b/libraries/rush-lib/src/logic/operations/test/MockOperationRunner.ts index 58a71c26a21..3e620634580 100644 --- a/libraries/rush-lib/src/logic/operations/test/MockOperationRunner.ts +++ b/libraries/rush-lib/src/logic/operations/test/MockOperationRunner.ts @@ -4,22 +4,24 @@ import type { CollatedTerminal } from '@rushstack/stream-collator'; import { OperationStatus } from '../OperationStatus'; -import { IOperationRunner, IOperationRunnerContext } from '../IOperationRunner'; +import type { IOperationRunner, IOperationRunnerContext } from '../IOperationRunner'; export class MockOperationRunner implements IOperationRunner { private readonly _action: ((terminal: CollatedTerminal) => Promise) | undefined; public readonly name: string; public readonly reportTiming: boolean = true; public readonly silent: boolean = false; - public isSkipAllowed: boolean = false; - public isCacheWriteAllowed: boolean = false; + public readonly cacheable: boolean = false; public readonly warningsAreAllowed: boolean; + public readonly isNoOp?: boolean | undefined; public constructor( name: string, action?: (terminal: CollatedTerminal) => Promise, - warningsAreAllowed: boolean = false + warningsAreAllowed: boolean = false, + isNoOp: boolean | undefined = undefined ) { + this.isNoOp = isNoOp; this.name = name; this._action = action; this.warningsAreAllowed = warningsAreAllowed; @@ -32,4 +34,8 @@ export class MockOperationRunner implements IOperationRunner { } return result || OperationStatus.Success; } + + public getConfigHash(): string { + return 'mock'; + } } diff --git a/libraries/rush-lib/src/logic/operations/test/Operation.test.ts b/libraries/rush-lib/src/logic/operations/test/Operation.test.ts new file mode 100644 index 00000000000..7428bf14478 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/test/Operation.test.ts @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IPhase } from '../../../api/CommandLineConfiguration'; +import type { RushConfigurationProject } from '../../../api/RushConfigurationProject'; +import type { IOperationSettings } from '../../../api/RushProjectConfiguration'; +import { Operation } from '../Operation'; +import { MockOperationRunner } from './MockOperationRunner'; + +const MOCK_PHASE: IPhase = { + name: '_phase:test', + allowWarningsOnSuccess: false, + associatedParameters: new Set(), + dependencies: { + self: new Set(), + upstream: new Set() + }, + isSynthetic: false, + logFilenameIdentifier: '_phase_test', + missingScriptBehavior: 'silent' +}; + +function createProject(packageName: string): RushConfigurationProject { + return { + packageName + } as RushConfigurationProject; +} + +function createOperation(options: { + project: RushConfigurationProject; + settings?: IOperationSettings; + isNoOp?: boolean; +}): Operation { + const { project, settings, isNoOp } = options; + return new Operation({ + phase: MOCK_PHASE, + project, + settings, + runner: new MockOperationRunner(`${project.packageName} (${MOCK_PHASE.name})`, undefined, false, isNoOp), + logFilenameIdentifier: `${project.packageName}_phase_test` + }); +} + +describe('Operation weight assignment', () => { + it('applies numeric weight from operation settings', () => { + const project: RushConfigurationProject = createProject('project-number'); + const operation: Operation = createOperation({ + project, + settings: { + operationName: MOCK_PHASE.name, + weight: 7 + } + }); + + expect(operation.weight).toBe(7); + }); + + it('parses percentage weight as a scalar', () => { + const project: RushConfigurationProject = createProject('project-percent'); + const operation: Operation = createOperation({ + project, + settings: { + operationName: MOCK_PHASE.name, + weight: '25%' + } as IOperationSettings + }); + + expect(operation.weight).toEqual({ scalar: 0.25 }); + }); + + it('parses 50% weight as scalar 0.5', () => { + const project: RushConfigurationProject = createProject('project-config'); + const operation: Operation = createOperation({ + project, + settings: { + operationName: MOCK_PHASE.name, + weight: '50%' + } as IOperationSettings + }); + + expect(operation.weight).toEqual({ scalar: 0.5 }); + }); + + it('parses fractional percentage weight as a scalar', () => { + const project: RushConfigurationProject = createProject('project-floor'); + const operation: Operation = createOperation({ + project, + settings: { + operationName: MOCK_PHASE.name, + weight: '33.3333%' + } as IOperationSettings + }); + + expect(operation.weight).toEqual({ scalar: 0.333333 }); + }); + + it('throws for invalid percentage weight format', () => { + const project: RushConfigurationProject = createProject('project-invalid'); + expect(() => { + createOperation({ + project, + // @ts-expect-error Testing invalid input + settings: { + operationName: MOCK_PHASE.name, + weight: '12.5a%' + } as IOperationSettings + }); + }).toThrow(/invalid weight for operation/i); + }); +}); diff --git a/libraries/rush-lib/src/logic/operations/test/OperationExecutionManager.test.ts b/libraries/rush-lib/src/logic/operations/test/OperationExecutionManager.test.ts deleted file mode 100644 index 378175310d6..00000000000 --- a/libraries/rush-lib/src/logic/operations/test/OperationExecutionManager.test.ts +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -// The TaskExecutionManager prints "x.xx seconds" in TestRunner.test.ts.snap; ensure that the Stopwatch timing is deterministic -jest.mock('../../../utilities/Utilities'); - -import colors from 'colors/safe'; - -import { Terminal } from '@rushstack/node-core-library'; -import { CollatedTerminal } from '@rushstack/stream-collator'; -import { MockWritable, PrintUtilities } from '@rushstack/terminal'; - -import { OperationExecutionManager, IOperationExecutionManagerOptions } from '../OperationExecutionManager'; -import { _printOperationStatus } from '../OperationResultSummarizerPlugin'; -import { _printTimeline } from '../ConsoleTimelinePlugin'; -import { OperationStatus } from '../OperationStatus'; -import { Operation } from '../Operation'; -import { Utilities } from '../../../utilities/Utilities'; -import type { IOperationRunner } from '../IOperationRunner'; -import { MockOperationRunner } from './MockOperationRunner'; -import type { IExecutionResult, IOperationExecutionResult } from '../IOperationExecutionResult'; -import { CollatedTerminalProvider } from '../../../utilities/CollatedTerminalProvider'; - -const mockGetTimeInMs: jest.Mock = jest.fn(); -Utilities.getTimeInMs = mockGetTimeInMs; - -let mockTimeInMs: number = 0; -mockGetTimeInMs.mockImplementation(() => { - console.log('CALLED mockGetTimeInMs'); - mockTimeInMs += 100; - return mockTimeInMs; -}); - -const mockWritable: MockWritable = new MockWritable(); -const mockTerminal: Terminal = new Terminal(new CollatedTerminalProvider(new CollatedTerminal(mockWritable))); - -function createExecutionManager( - executionManagerOptions: IOperationExecutionManagerOptions, - operationRunner: IOperationRunner -): OperationExecutionManager { - const operation: Operation = new Operation({ - runner: operationRunner - }); - - return new OperationExecutionManager(new Set([operation]), executionManagerOptions); -} - -describe(OperationExecutionManager.name, () => { - let executionManager: OperationExecutionManager; - let executionManagerOptions: IOperationExecutionManagerOptions; - - let initialColorsEnabled: boolean; - - beforeAll(() => { - initialColorsEnabled = colors.enabled; - colors.enable(); - }); - - afterAll(() => { - if (!initialColorsEnabled) { - colors.disable(); - } - }); - - beforeEach(() => { - jest.spyOn(PrintUtilities, 'getConsoleWidth').mockReturnValue(90); - mockWritable.reset(); - }); - - describe('Error logging', () => { - beforeEach(() => { - executionManagerOptions = { - quietMode: false, - debugMode: false, - parallelism: 1, - changedProjectsOnly: false, - destination: mockWritable - }; - }); - - it('printedStderrAfterError', async () => { - executionManager = createExecutionManager( - executionManagerOptions, - new MockOperationRunner('stdout+stderr', async (terminal: CollatedTerminal) => { - terminal.writeStdoutLine('Build step 1\n'); - terminal.writeStderrLine('Error: step 1 failed\n'); - return OperationStatus.Failure; - }) - ); - - const result: IExecutionResult = await executionManager.executeAsync(); - _printOperationStatus(mockTerminal, result); - expect(result.status).toEqual(OperationStatus.Failure); - expect(result.operationResults.size).toEqual(1); - const firstResult: IOperationExecutionResult = result.operationResults.values().next().value; - expect(firstResult.status).toEqual(OperationStatus.Failure); - - const allMessages: string = mockWritable.getAllOutput(); - expect(allMessages).toContain('Error: step 1 failed'); - expect(mockWritable.getFormattedChunks()).toMatchSnapshot(); - }); - - it('printedStdoutAfterErrorWithEmptyStderr', async () => { - executionManager = createExecutionManager( - executionManagerOptions, - new MockOperationRunner('stdout only', async (terminal: CollatedTerminal) => { - terminal.writeStdoutLine('Build step 1\n'); - terminal.writeStdoutLine('Error: step 1 failed\n'); - return OperationStatus.Failure; - }) - ); - - const result: IExecutionResult = await executionManager.executeAsync(); - _printOperationStatus(mockTerminal, result); - expect(result.status).toEqual(OperationStatus.Failure); - expect(result.operationResults.size).toEqual(1); - const firstResult: IOperationExecutionResult = result.operationResults.values().next().value; - expect(firstResult.status).toEqual(OperationStatus.Failure); - - const allOutput: string = mockWritable.getAllOutput(); - expect(allOutput).toMatch(/Build step 1/); - expect(allOutput).toMatch(/Error: step 1 failed/); - expect(mockWritable.getFormattedChunks()).toMatchSnapshot(); - }); - }); - - describe('Warning logging', () => { - describe('Fail on warning', () => { - beforeEach(() => { - executionManagerOptions = { - quietMode: false, - debugMode: false, - parallelism: 1, - changedProjectsOnly: false, - destination: mockWritable - }; - }); - - it('Logs warnings correctly', async () => { - executionManager = createExecutionManager( - executionManagerOptions, - new MockOperationRunner('success with warnings (failure)', async (terminal: CollatedTerminal) => { - terminal.writeStdoutLine('Build step 1\n'); - terminal.writeStdoutLine('Warning: step 1 succeeded with warnings\n'); - return OperationStatus.SuccessWithWarning; - }) - ); - - const result: IExecutionResult = await executionManager.executeAsync(); - _printOperationStatus(mockTerminal, result); - expect(result.status).toEqual(OperationStatus.SuccessWithWarning); - expect(result.operationResults.size).toEqual(1); - const firstResult: IOperationExecutionResult = result.operationResults.values().next().value; - expect(firstResult.status).toEqual(OperationStatus.SuccessWithWarning); - - const allMessages: string = mockWritable.getAllOutput(); - expect(allMessages).toContain('Build step 1'); - expect(allMessages).toContain('step 1 succeeded with warnings'); - expect(mockWritable.getFormattedChunks()).toMatchSnapshot(); - }); - }); - - describe('Success on warning', () => { - beforeEach(() => { - executionManagerOptions = { - quietMode: false, - debugMode: false, - parallelism: 1, - changedProjectsOnly: false, - destination: mockWritable - }; - }); - - it('Logs warnings correctly', async () => { - executionManager = createExecutionManager( - executionManagerOptions, - new MockOperationRunner( - 'success with warnings (success)', - async (terminal: CollatedTerminal) => { - terminal.writeStdoutLine('Build step 1\n'); - terminal.writeStdoutLine('Warning: step 1 succeeded with warnings\n'); - return OperationStatus.SuccessWithWarning; - }, - /* warningsAreAllowed */ true - ) - ); - - const result: IExecutionResult = await executionManager.executeAsync(); - _printOperationStatus(mockTerminal, result); - expect(result.status).toEqual(OperationStatus.Success); - expect(result.operationResults.size).toEqual(1); - const firstResult: IOperationExecutionResult = result.operationResults.values().next().value; - expect(firstResult.status).toEqual(OperationStatus.SuccessWithWarning); - const allMessages: string = mockWritable.getAllOutput(); - expect(allMessages).toContain('Build step 1'); - expect(allMessages).toContain('Warning: step 1 succeeded with warnings'); - expect(mockWritable.getFormattedChunks()).toMatchSnapshot(); - }); - - it('logs warnings correctly with --timeline option', async () => { - executionManager = createExecutionManager( - executionManagerOptions, - new MockOperationRunner( - 'success with warnings (success)', - async (terminal: CollatedTerminal) => { - terminal.writeStdoutLine('Build step 1\n'); - terminal.writeStdoutLine('Warning: step 1 succeeded with warnings\n'); - return OperationStatus.SuccessWithWarning; - }, - /* warningsAreAllowed */ true - ) - ); - - const result: IExecutionResult = await executionManager.executeAsync(); - _printTimeline(mockTerminal, result); - _printOperationStatus(mockTerminal, result); - const allMessages: string = mockWritable.getAllOutput(); - expect(allMessages).toContain('Build step 1'); - expect(allMessages).toContain('Warning: step 1 succeeded with warnings'); - expect(mockWritable.getFormattedChunks()).toMatchSnapshot(); - }); - }); - }); -}); diff --git a/libraries/rush-lib/src/logic/operations/test/OperationExecutionRecord.test.ts b/libraries/rush-lib/src/logic/operations/test/OperationExecutionRecord.test.ts new file mode 100644 index 00000000000..6919cc704bf --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/test/OperationExecutionRecord.test.ts @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IPhase } from '../../../api/CommandLineConfiguration'; +import type { RushConfigurationProject } from '../../../api/RushConfigurationProject'; +import type { IOperationSettings } from '../../../api/RushProjectConfiguration'; +import { Operation } from '../Operation'; +import { type IOperationExecutionRecordContext, OperationExecutionRecord } from '../OperationExecutionRecord'; +import { MockOperationRunner } from './MockOperationRunner'; + +const MOCK_PHASE: IPhase = { + name: '_phase:test', + allowWarningsOnSuccess: false, + associatedParameters: new Set(), + dependencies: { + self: new Set(), + upstream: new Set() + }, + isSynthetic: false, + logFilenameIdentifier: '_phase_test', + missingScriptBehavior: 'silent' +}; + +function createProject(packageName: string): RushConfigurationProject { + return { + packageName + } as RushConfigurationProject; +} + +function createOperation(options: { + project: RushConfigurationProject; + settings?: IOperationSettings; + isNoOp?: boolean; +}): Operation { + const { project, settings, isNoOp } = options; + return new Operation({ + phase: MOCK_PHASE, + project, + settings, + runner: new MockOperationRunner(`${project.packageName} (${MOCK_PHASE.name})`, undefined, false, isNoOp), + logFilenameIdentifier: `${project.packageName}_phase_test` + }); +} + +function createRecord(operation: Operation, maxParallelism: number = 8): OperationExecutionRecord { + return new OperationExecutionRecord(operation, { + maxParallelism + } as unknown as IOperationExecutionRecordContext); +} + +describe(OperationExecutionRecord.name, () => { + describe('weight', () => { + it('snapshots numeric operation weight for a normal (non-no-op) operation', () => { + const project: RushConfigurationProject = createProject('project-normal'); + const operation: Operation = createOperation({ + project, + settings: { + operationName: MOCK_PHASE.name, + weight: 3 + } + }); + + const record: OperationExecutionRecord = createRecord(operation); + expect(record.weight).toBe(3); + }); + + it('coerces percentage weight to integer slots using maxParallelism', () => { + // 25% of 8 slots = floor(0.25 * 8) = 2 + const project: RushConfigurationProject = createProject('project-percent'); + const operation: Operation = createOperation({ + project, + settings: { + operationName: MOCK_PHASE.name, + weight: '25%' + } as IOperationSettings + }); + + const record: OperationExecutionRecord = createRecord(operation, 8); + expect(record.weight).toBe(2); + }); + + it('coerces weight to 0 for no-op operations regardless of operation weight', () => { + const project: RushConfigurationProject = createProject('project-noop'); + const operation: Operation = createOperation({ + project, + settings: { + operationName: MOCK_PHASE.name, + weight: 5 + }, + isNoOp: true + }); + + const record: OperationExecutionRecord = createRecord(operation); + expect(record.weight).toBe(0); + }); + + it('snapshots default weight (1) for a normal operation with no weight setting', () => { + const project: RushConfigurationProject = createProject('project-default'); + const operation: Operation = createOperation({ project }); + + const record: OperationExecutionRecord = createRecord(operation); + expect(record.weight).toBe(1); + }); + + it('coerces weight to 0 for no-op operations even with default weight', () => { + const project: RushConfigurationProject = createProject('project-noop-default'); + const operation: Operation = createOperation({ project, isNoOp: true }); + + const record: OperationExecutionRecord = createRecord(operation); + expect(record.weight).toBe(0); + }); + + it('uses the graph maxParallelism (not OS core count) when coercing percentage weights', () => { + // 50% of 4 slots = floor(0.5 * 4) = 2, not floor(0.5 * ) + const project: RushConfigurationProject = createProject('project-graph-max'); + const operation: Operation = createOperation({ + project, + settings: { + operationName: MOCK_PHASE.name, + weight: '50%' + } as IOperationSettings + }); + + const record: OperationExecutionRecord = createRecord(operation, 4); + expect(record.weight).toBe(2); + }); + }); +}); diff --git a/libraries/rush-lib/src/logic/operations/test/OperationGraph.test.ts b/libraries/rush-lib/src/logic/operations/test/OperationGraph.test.ts new file mode 100644 index 00000000000..913f15f705a --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/test/OperationGraph.test.ts @@ -0,0 +1,1430 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// The TaskExecutionManager prints "x.xx seconds" in TestRunner.test.ts.snap; ensure that the Stopwatch timing is deterministic +jest.mock('@rushstack/terminal', () => { + const originalModule = jest.requireActual('@rushstack/terminal'); + return { + ...originalModule, + ConsoleTerminalProvider: { + ...originalModule.ConsoleTerminalProvider, + supportsColor: true + } + }; +}); + +jest.mock('../../../utilities/Utilities'); +jest.mock('../OperationStateFile'); +// Mock project log file creation to avoid filesystem writes; return a simple writable collecting chunks. +jest.mock('../ProjectLogWritable', () => { + const actual = jest.requireActual('../ProjectLogWritable'); + const terminalModule = jest.requireActual('@rushstack/terminal'); + const { TerminalWritable } = terminalModule; + class MockTerminalWritable extends TerminalWritable { + public readonly chunks: string[] = []; + protected onWriteChunk(chunk: { text: string }): void { + this.chunks.push(chunk.text); + } + protected onClose(): void { + /* noop */ + } + } + return { + ...actual, + initializeProjectLogFilesAsync: jest.fn(async () => new MockTerminalWritable()) + }; +}); + +import { type ITerminal, Terminal } from '@rushstack/terminal'; +import { CollatedTerminal } from '@rushstack/stream-collator'; +import { MockWritable, PrintUtilities } from '@rushstack/terminal'; +import { Async } from '@rushstack/node-core-library'; + +import type { IPhase } from '../../../api/CommandLineConfiguration'; +import type { RushConfigurationProject } from '../../../api/RushConfigurationProject'; +import { OperationGraph, type IOperationGraphOptions } from '../OperationGraph'; +import { _printOperationStatus } from '../OperationResultSummarizerPlugin'; +import { _printTimeline } from '../ConsoleTimelinePlugin'; +import { OperationStatus } from '../OperationStatus'; +import { Operation } from '../Operation'; +import { Utilities } from '../../../utilities/Utilities'; +import type { IOperationRunner, IOperationRunnerContext } from '../IOperationRunner'; +import { MockOperationRunner } from './MockOperationRunner'; +import type { IExecutionResult, IOperationExecutionResult } from '../IOperationExecutionResult'; +import { CollatedTerminalProvider } from '../../../utilities/CollatedTerminalProvider'; +import type { CobuildConfiguration } from '../../../api/CobuildConfiguration'; +import type { OperationStateFile } from '../OperationStateFile'; +import type { IOperationGraphIterationOptions } from '../IOperationGraph'; +import type { IOperationGraph } from '../IOperationGraph'; + +const mockGetTimeInMs: jest.Mock = jest.fn(); +Utilities.getTimeInMs = mockGetTimeInMs; + +let mockTimeInMs: number = 0; +mockGetTimeInMs.mockImplementation(() => { + mockTimeInMs += 100; + return mockTimeInMs; +}); + +const mockWritable: MockWritable = new MockWritable(); +const mockTerminal: Terminal = new Terminal(new CollatedTerminalProvider(new CollatedTerminal(mockWritable))); + +const mockPhase: IPhase = { + name: 'phase', + allowWarningsOnSuccess: false, + associatedParameters: new Set(), + dependencies: { + self: new Set(), + upstream: new Set() + }, + isSynthetic: false, + logFilenameIdentifier: 'phase', + missingScriptBehavior: 'silent' +}; +const projectsByName: Map = new Map(); +function getOrCreateProject(name: string): RushConfigurationProject { + let project: RushConfigurationProject | undefined = projectsByName.get(name); + if (!project) { + project = { + packageName: name + } as unknown as RushConfigurationProject; + projectsByName.set(name, project); + } + return project; +} + +function createGraph( + graphOptions: IOperationGraphOptions, + operationRunner: IOperationRunner +): OperationGraph { + const operation: Operation = new Operation({ + runner: operationRunner, + logFilenameIdentifier: 'operation', + phase: mockPhase, + project: getOrCreateProject('project') + }); + + return new OperationGraph(new Set([operation]), graphOptions); +} + +describe('OperationGraph', () => { + let graphOptions: IOperationGraphOptions; + let graphIterationOptions: IOperationGraphIterationOptions; + + beforeEach(() => { + jest.spyOn(PrintUtilities, 'getConsoleWidth').mockReturnValue(90); + mockWritable.reset(); + }); + + describe('Error logging', () => { + beforeEach(() => { + graphOptions = { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }; + graphIterationOptions = {}; + }); + + it('printedStderrAfterError', async () => { + const graph: OperationGraph = createGraph( + graphOptions, + new MockOperationRunner('stdout+stderr', async (terminal: CollatedTerminal) => { + terminal.writeStdoutLine('Build step 1\n'); + terminal.writeStderrLine('Error: step 1 failed\n'); + return OperationStatus.Failure; + }) + ); + + const result: IExecutionResult = await graph.executeAsync(graphIterationOptions); + _printOperationStatus(mockTerminal, result); + expect(result.status).toEqual(OperationStatus.Failure); + expect(graph.status).toEqual(OperationStatus.Failure); + expect(result.operationResults.size).toEqual(1); + const firstResult: IOperationExecutionResult | undefined = result.operationResults + .values() + .next().value; + expect(firstResult?.status).toEqual(OperationStatus.Failure); + + const allMessages: string = mockWritable.getAllOutput(); + expect(allMessages).toContain('Error: step 1 failed'); + expect(mockWritable.getFormattedChunks()).toMatchSnapshot(); + }); + + it('printedStdoutAfterErrorWithEmptyStderr', async () => { + const graph: OperationGraph = createGraph( + graphOptions, + new MockOperationRunner('stdout only', async (terminal: CollatedTerminal) => { + terminal.writeStdoutLine('Build step 1\n'); + terminal.writeStdoutLine('Error: step 1 failed\n'); + return OperationStatus.Failure; + }) + ); + + const result: IExecutionResult = await graph.executeAsync(graphIterationOptions); + _printOperationStatus(mockTerminal, result); + expect(result.status).toEqual(OperationStatus.Failure); + expect(result.operationResults.size).toEqual(1); + const firstResult: IOperationExecutionResult | undefined = result.operationResults + .values() + .next().value; + expect(firstResult?.status).toEqual(OperationStatus.Failure); + + const allOutput: string = mockWritable.getAllOutput(); + expect(allOutput).toMatch(/Build step 1/); + expect(allOutput).toMatch(/Error: step 1 failed/); + expect(mockWritable.getFormattedChunks()).toMatchSnapshot(); + }); + }); + + describe('Aborting', () => { + it('Aborted operations abort', async () => { + const mockRun: jest.Mock = jest.fn(); + + const firstOperation = new Operation({ + runner: new MockOperationRunner('1', mockRun), + phase: mockPhase, + project: getOrCreateProject('1'), + logFilenameIdentifier: '1' + }); + + const secondOperation = new Operation({ + runner: new MockOperationRunner('2', mockRun), + phase: mockPhase, + project: getOrCreateProject('2'), + logFilenameIdentifier: '2' + }); + + secondOperation.addDependency(firstOperation); + + const graph: OperationGraph = new OperationGraph(new Set([firstOperation, secondOperation]), { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }); + + graph.hooks.beforeExecuteIterationAsync.tapPromise( + 'test', + (): Promise => graph.abortCurrentIterationAsync() + ); + const result: IExecutionResult = await graph.executeAsync(graphIterationOptions); + expect(result.status).toEqual(OperationStatus.Aborted); + expect(graph.status).toEqual(OperationStatus.Aborted); + expect(mockRun).not.toHaveBeenCalled(); + expect(result.operationResults.size).toEqual(2); + expect(result.operationResults.get(firstOperation)?.status).toEqual(OperationStatus.Aborted); + expect(result.operationResults.get(secondOperation)?.status).toEqual(OperationStatus.Aborted); + }); + + it('Successful bail marks unexecuted operations as Skipped', async () => { + const mockRun: jest.Mock = jest.fn(); + + const firstOperation = new Operation({ + runner: new MockOperationRunner('1', mockRun), + phase: mockPhase, + project: getOrCreateProject('1'), + logFilenameIdentifier: '1' + }); + + const secondOperation = new Operation({ + runner: new MockOperationRunner('2', mockRun), + phase: mockPhase, + project: getOrCreateProject('2'), + logFilenameIdentifier: '2' + }); + + secondOperation.addDependency(firstOperation); + + const graph: OperationGraph = new OperationGraph(new Set([firstOperation, secondOperation]), { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }); + + // Simulate a plugin (e.g. bridge-cache) that performs the work out-of-band and short-circuits + // the iteration with a successful status. + graph.hooks.beforeExecuteIterationAsync.tapPromise( + 'test', + async (): Promise => OperationStatus.FromCache + ); + const result: IExecutionResult = await graph.executeAsync(graphIterationOptions); + expect(result.status).toEqual(OperationStatus.FromCache); + expect(graph.status).toEqual(OperationStatus.FromCache); + expect(mockRun).not.toHaveBeenCalled(); + expect(result.operationResults.size).toEqual(2); + // Operations were intentionally not executed, so they should be Skipped rather than Aborted. + expect(result.operationResults.get(firstOperation)?.status).toEqual(OperationStatus.Skipped); + expect(result.operationResults.get(secondOperation)?.status).toEqual(OperationStatus.Skipped); + }); + }); + + describe('Blocking', () => { + it('Failed operations block', async () => { + const failingOperation = new Operation({ + runner: new MockOperationRunner('fail', async () => { + return OperationStatus.Failure; + }), + phase: mockPhase, + project: getOrCreateProject('fail'), + logFilenameIdentifier: 'fail' + }); + + const blockedRunFn: jest.Mock = jest.fn(); + + const blockedOperation = new Operation({ + runner: new MockOperationRunner('blocked', blockedRunFn), + phase: mockPhase, + project: getOrCreateProject('blocked'), + logFilenameIdentifier: 'blocked' + }); + + blockedOperation.addDependency(failingOperation); + + const graph: OperationGraph = new OperationGraph(new Set([failingOperation, blockedOperation]), { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }); + + const result = await graph.executeAsync({}); + expect(result.status).toEqual(OperationStatus.Failure); + expect(blockedRunFn).not.toHaveBeenCalled(); + expect(result.operationResults.size).toEqual(2); + expect(result.operationResults.get(failingOperation)?.status).toEqual(OperationStatus.Failure); + expect(result.operationResults.get(blockedOperation)?.status).toEqual(OperationStatus.Blocked); + }); + }); + + describe('Concurrency', () => { + it('runs independent operations concurrently when parallelism allows', async () => { + let concurrency: number = 0; + let maxConcurrency: number = 0; + + const trackingRun = async (): Promise => { + ++concurrency; + await Async.sleepAsync(0); + if (concurrency > maxConcurrency) { + maxConcurrency = concurrency; + } + --concurrency; + return OperationStatus.Success; + }; + + const alpha = new Operation({ + runner: new MockOperationRunner('alpha', trackingRun), + phase: mockPhase, + project: getOrCreateProject('alpha'), + logFilenameIdentifier: 'alpha' + }); + const beta = new Operation({ + runner: new MockOperationRunner('beta', trackingRun), + phase: mockPhase, + project: getOrCreateProject('beta'), + logFilenameIdentifier: 'beta' + }); + + const graph: OperationGraph = new OperationGraph(new Set([alpha, beta]), { + quietMode: false, + debugMode: false, + parallelism: 2, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }); + + const result: IExecutionResult = await graph.executeAsync({}); + expect(result.status).toEqual(OperationStatus.Success); + expect(maxConcurrency).toBe(2); + }); + + it('serializes independent operations when parallelism is 1', async () => { + let concurrency: number = 0; + let maxConcurrency: number = 0; + + const trackingRun = async (): Promise => { + ++concurrency; + await Async.sleepAsync(0); + if (concurrency > maxConcurrency) { + maxConcurrency = concurrency; + } + --concurrency; + return OperationStatus.Success; + }; + + const alpha = new Operation({ + runner: new MockOperationRunner('alpha-seq', trackingRun), + phase: mockPhase, + project: getOrCreateProject('alpha-seq'), + logFilenameIdentifier: 'alpha-seq' + }); + const beta = new Operation({ + runner: new MockOperationRunner('beta-seq', trackingRun), + phase: mockPhase, + project: getOrCreateProject('beta-seq'), + logFilenameIdentifier: 'beta-seq' + }); + + const graph: OperationGraph = new OperationGraph(new Set([alpha, beta]), { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }); + + const result: IExecutionResult = await graph.executeAsync({}); + expect(result.status).toEqual(OperationStatus.Success); + expect(maxConcurrency).toBe(1); + }); + }); + + describe('onExecutionStatesUpdated hook', () => { + beforeEach(() => { + graphOptions = { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }; + graphIterationOptions = {}; + }); + + class LogFileCreatingRunner extends MockOperationRunner { + public constructor() { + super('logfile-op'); + } + public override async executeAsync(context: IOperationRunnerContext): Promise { + await context.runWithTerminalAsync( + async (terminal: ITerminal) => { + terminal.writeLine('Hello world'); + return Promise.resolve(); + }, + { createLogFile: true, logFileSuffix: '' } + ); + return OperationStatus.Success; + } + } + + it('fires state updates for status transitions (captures snapshot statuses)', async () => { + const runner: IOperationRunner = new MockOperationRunner('state-change-op'); + const graph: OperationGraph = createGraph(graphOptions, runner); + + const stateUpdates: OperationStatus[][] = []; + graph.hooks.onExecutionStatesUpdated.tap('test', (records) => { + // Capture immutable array of status values at callback time + stateUpdates.push(Array.from(records, (r) => r.status)); + }); + + const result: IExecutionResult = await graph.executeAsync(graphIterationOptions); + expect(result.status).toBe(OperationStatus.Success); + // Expect at least two batches now that we introduced a delay + expect(stateUpdates.length).toBeGreaterThanOrEqual(2); + const flattenedStatuses: OperationStatus[] = stateUpdates.flat(); + // Should observe an Executing intermediate status in snapshots (not just final Success) + expect(flattenedStatuses).toContain(OperationStatus.Executing); + expect(flattenedStatuses).toContain(OperationStatus.Success); + }); + + it('fires state update when logFilePaths are assigned (createLogFile=true) regardless of final status', async () => { + const runner: IOperationRunner = new LogFileCreatingRunner(); + const graph: OperationGraph = createGraph(graphOptions, runner); + + const operationStateUpdates: ReadonlySet[] = []; + graph.hooks.onExecutionStatesUpdated.tap('test', (records) => { + operationStateUpdates.push(new Set(records)); + }); + + const result: IExecutionResult = await graph.executeAsync(graphIterationOptions); + // Status may be Success or Failure if logging pipeline errors; we only care that hook fired with logFilePaths + expect(result.status === OperationStatus.Success || result.status === OperationStatus.Failure).toBe( + true + ); + // Find a batch where logFilePaths is defined + const anyWithLogFile: boolean = operationStateUpdates.some((recordSet) => + Array.from(recordSet).some((r) => Boolean((r as { logFilePaths?: unknown }).logFilePaths)) + ); + expect(anyWithLogFile).toBe(true); + }); + }); + + describe('Warning logging', () => { + describe('Fail on warning', () => { + beforeEach(() => { + graphOptions = { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }; + }); + + it('Logs warnings correctly', async () => { + const graph: OperationGraph = createGraph( + graphOptions, + new MockOperationRunner('success with warnings (failure)', async (terminal: CollatedTerminal) => { + terminal.writeStdoutLine('Build step 1\n'); + terminal.writeStdoutLine('Warning: step 1 succeeded with warnings\n'); + return OperationStatus.SuccessWithWarning; + }) + ); + + const result: IExecutionResult = await graph.executeAsync(graphIterationOptions); + _printOperationStatus(mockTerminal, result); + expect(result.status).toEqual(OperationStatus.SuccessWithWarning); + expect(graph.status).toEqual(OperationStatus.SuccessWithWarning); + expect(result.operationResults.size).toEqual(1); + const firstResult: IOperationExecutionResult | undefined = result.operationResults + .values() + .next().value; + expect(firstResult?.status).toEqual(OperationStatus.SuccessWithWarning); + + const allMessages: string = mockWritable.getAllOutput(); + expect(allMessages).toContain('Build step 1'); + expect(allMessages).toContain('step 1 succeeded with warnings'); + expect(mockWritable.getFormattedChunks()).toMatchSnapshot(); + }); + }); + + describe('Success on warning', () => { + beforeEach(() => { + graphOptions = { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }; + }); + + it('Logs warnings correctly', async () => { + const graph: OperationGraph = createGraph( + graphOptions, + new MockOperationRunner( + 'success with warnings (success)', + async (terminal: CollatedTerminal) => { + terminal.writeStdoutLine('Build step 1\n'); + terminal.writeStdoutLine('Warning: step 1 succeeded with warnings\n'); + return OperationStatus.SuccessWithWarning; + }, + /* warningsAreAllowed */ true + ) + ); + + const result: IExecutionResult = await graph.executeAsync(graphIterationOptions); + _printOperationStatus(mockTerminal, result); + expect(result.status).toEqual(OperationStatus.Success); + expect(result.operationResults.size).toEqual(1); + const firstResult: IOperationExecutionResult | undefined = result.operationResults + .values() + .next().value; + expect(firstResult?.status).toEqual(OperationStatus.SuccessWithWarning); + const allMessages: string = mockWritable.getAllOutput(); + expect(allMessages).toContain('Build step 1'); + expect(allMessages).toContain('Warning: step 1 succeeded with warnings'); + expect(mockWritable.getFormattedChunks()).toMatchSnapshot(); + }); + + it('logs warnings correctly with --timeline option', async () => { + const graph: OperationGraph = createGraph( + graphOptions, + new MockOperationRunner( + 'success with warnings (success)', + async (terminal: CollatedTerminal) => { + terminal.writeStdoutLine('Build step 1\n'); + terminal.writeStdoutLine('Warning: step 1 succeeded with warnings\n'); + return OperationStatus.SuccessWithWarning; + }, + /* warningsAreAllowed */ true + ) + ); + const result: IExecutionResult = await graph.executeAsync(graphIterationOptions); + _printTimeline({ terminal: mockTerminal, result, cobuildConfiguration: undefined }); + _printOperationStatus(mockTerminal, result); + const allMessages: string = mockWritable.getAllOutput(); + expect(allMessages).toContain('Build step 1'); + expect(allMessages).toContain('Warning: step 1 succeeded with warnings'); + expect(mockWritable.getFormattedChunks()).toMatchSnapshot(); + }); + }); + }); + + describe('Cobuild logging', () => { + beforeEach(() => { + let mockCobuildTimeInMs: number = 0; + mockGetTimeInMs.mockImplementation(() => { + mockCobuildTimeInMs += 10_000; + return mockCobuildTimeInMs; + }); + }); + + function createCobuildGraph( + cobuildExecutionManagerOptions: IOperationGraphOptions, + operationRunnerFactory: (name: string) => IOperationRunner, + phase: IPhase, + project: RushConfigurationProject + ): OperationGraph { + const operation: Operation = new Operation({ + runner: operationRunnerFactory('operation'), + logFilenameIdentifier: 'operation', + phase, + project + }); + + const operation2: Operation = new Operation({ + runner: operationRunnerFactory('operation2'), + logFilenameIdentifier: 'operation2', + phase, + project + }); + + const graph: OperationGraph = new OperationGraph( + new Set([operation, operation2]), + cobuildExecutionManagerOptions + ); + + graph.hooks.afterExecuteOperationAsync.tapPromise('TestPlugin', async (record) => { + if (!record._operationMetadataManager) { + throw new Error('OperationMetadataManager is not defined'); + } + // Mock the readonly state property. + (record._operationMetadataManager as unknown as Record).stateFile = { + state: { + cobuildContextId: '123', + cobuildRunnerId: '456', + nonCachedDurationMs: 15_000 + } + } as unknown as OperationStateFile; + record._operationMetadataManager.wasCobuilt = true; + }); + + return graph; + } + it('logs cobuilt operations correctly with --timeline option', async () => { + const graph: OperationGraph = createCobuildGraph( + graphOptions, + (name) => + new MockOperationRunner( + `${name} (success)`, + async () => { + return OperationStatus.Success; + }, + /* warningsAreAllowed */ true + ), + { name: 'my-name' } as unknown as IPhase, + {} as unknown as RushConfigurationProject + ); + + const result: IExecutionResult = await graph.executeAsync(graphIterationOptions); + _printTimeline({ + terminal: mockTerminal, + result, + cobuildConfiguration: { + cobuildRunnerId: '123', + cobuildContextId: '123' + } as unknown as CobuildConfiguration + }); + _printOperationStatus(mockTerminal, result); + expect(mockWritable.getFormattedChunks()).toMatchSnapshot(); + }); + it('logs warnings correctly with --timeline option', async () => { + const graph: OperationGraph = createCobuildGraph( + graphOptions, + (name) => + new MockOperationRunner(`${name} (success with warnings)`, async (terminal: CollatedTerminal) => { + terminal.writeStdoutLine('Build step 1\n'); + terminal.writeStdoutLine('Warning: step 1 succeeded with warnings\n'); + return OperationStatus.SuccessWithWarning; + }), + { name: 'my-name' } as unknown as IPhase, + {} as unknown as RushConfigurationProject + ); + + const result: IExecutionResult = await graph.executeAsync(graphIterationOptions); + _printTimeline({ + terminal: mockTerminal, + result, + cobuildConfiguration: { + cobuildRunnerId: '123', + cobuildContextId: '123' + } as unknown as CobuildConfiguration + }); + _printOperationStatus(mockTerminal, result); + const allMessages: string = mockWritable.getAllOutput(); + expect(allMessages).toContain('Build step 1'); + expect(allMessages).toContain('Warning: step 1 succeeded with warnings'); + expect(mockWritable.getFormattedChunks()).toMatchSnapshot(); + }); + }); + + describe('Manual iteration mode', () => { + it('queues an iteration in manual mode and does not auto-execute until executeScheduledIterationAsync is called', async () => { + jest.useFakeTimers({ legacyFakeTimers: true }); + try { + const options: IOperationGraphOptions = { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController(), + pauseNextIteration: true + }; + + const runFn: jest.Mock = jest.fn(async () => OperationStatus.Success); + const op: Operation = new Operation({ + runner: new MockOperationRunner('manual-op', runFn), + phase: mockPhase, + project: getOrCreateProject('manual-project'), + logFilenameIdentifier: 'manual-op' + }); + + const graph: OperationGraph = new OperationGraph(new Set([op]), options); + + const passQueuedCalls: ReadonlyMap[] = []; + graph.hooks.onIterationScheduled.tap('test', (records) => passQueuedCalls.push(records)); + + const idleCalls: number[] = []; + graph.hooks.onIdle.tap('test', () => idleCalls.push(1)); + + const queued: boolean = await graph.scheduleIterationAsync({}); + expect(queued).toBe(true); + expect(passQueuedCalls.length).toBe(1); + // Iteration should be scheduled but not yet started. + expect(graph.hasScheduledIteration).toBe(true); + expect(runFn).not.toHaveBeenCalled(); + + // Flush the idle timeout. Since pauseNextIteration is true, execution should NOT start automatically. + jest.runAllTimers(); + expect(idleCalls.length).toBe(1); + expect(runFn).not.toHaveBeenCalled(); + + // Now manually execute the scheduled iteration + const executed: boolean = await graph.executeScheduledIterationAsync(); + expect(executed).toBe(true); + expect(runFn).toHaveBeenCalledTimes(1); + expect(graph.hasScheduledIteration).toBe(false); + // After execution status should be Success + expect(graph.status).toBe(OperationStatus.Success); + } finally { + jest.useRealTimers(); + } + }); + + it('does not queue an iteration if all operations are disabled (no enabled operations)', async () => { + const options: IOperationGraphOptions = { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController(), + pauseNextIteration: true + }; + + const runFn: jest.Mock = jest.fn(async () => OperationStatus.Success); + const disabledOp: Operation = new Operation({ + runner: new MockOperationRunner('disabled-op', runFn), + phase: mockPhase, + project: getOrCreateProject('disabled-project'), + logFilenameIdentifier: 'disabled-op', + enabled: false + }); + + const graph: OperationGraph = new OperationGraph(new Set([disabledOp]), options); + + const passQueuedCalls: ReadonlyMap[] = []; + graph.hooks.onIterationScheduled.tap('test', (records) => passQueuedCalls.push(records)); + + const queued: boolean = await graph.scheduleIterationAsync({}); + expect(queued).toBe(false); // Nothing to do + expect(passQueuedCalls.length).toBe(0); // Hook not fired + expect(graph.hasScheduledIteration).toBe(false); + expect(runFn).not.toHaveBeenCalled(); + // Status remains Ready (no operations executed) + expect(graph.status).toBe(OperationStatus.Ready); + }); + }); + + describe('Terminal destination APIs', () => { + beforeEach(() => { + graphOptions = { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }; + graphIterationOptions = {}; + }); + + it('addTerminalDestination causes new destination to receive output', async () => { + const extraDest = new MockWritable(); + + const graph: OperationGraph = createGraph( + graphOptions, + new MockOperationRunner('to-extra', async (terminal: CollatedTerminal) => { + terminal.writeStdoutLine('Message for extra destination'); + return OperationStatus.Success; + }) + ); + + // Add destination before executing + graph.addTerminalDestination(extraDest); + + const result: IExecutionResult = await graph.executeAsync(graphIterationOptions); + expect(result.status).toBe(OperationStatus.Success); + + const allOutput: string = extraDest.getAllOutput(); + expect(allOutput).toContain('Message for extra destination'); + }); + + it('removeTerminalDestination closes destination by default and stops further output', async () => { + const extraDest = new MockWritable(); + + const graph: OperationGraph = createGraph( + graphOptions, + new MockOperationRunner('to-extra', async (terminal: CollatedTerminal) => { + terminal.writeStdoutLine('Iteration message'); + return OperationStatus.Success; + }) + ); + + graph.addTerminalDestination(extraDest); + + // First run: destination should receive output + const first = await graph.executeAsync(graphIterationOptions); + expect(first.status).toBe(OperationStatus.Success); + expect(extraDest.getAllOutput()).toContain('Iteration message'); + + // Now remove destination (default close = true) and ensure it was removed/closed + const removed = graph.removeTerminalDestination(extraDest); + expect(removed).toBe(true); + // TerminalWritable exposes isOpen + expect(extraDest.isOpen).toBe(false); + + // Second run: should not write to closed destination + const beforeSecond = extraDest.getAllOutput(); + const second = await graph.executeAsync(graphIterationOptions); + expect(second.status).toBe(OperationStatus.Success); + const afterSecond = extraDest.getAllOutput(); + expect(afterSecond).toBe(beforeSecond); + }); + + it('removeTerminalDestination with close=false does not close destination but still stops further output', async () => { + const extraDest = new MockWritable(); + + const graph: OperationGraph = createGraph( + graphOptions, + new MockOperationRunner('to-extra', async (terminal: CollatedTerminal) => { + terminal.writeStdoutLine('Iteration message 2'); + return OperationStatus.Success; + }) + ); + + graph.addTerminalDestination(extraDest); + + // First run: destination should receive output + const first = await graph.executeAsync(graphIterationOptions); + expect(first.status).toBe(OperationStatus.Success); + expect(extraDest.getAllOutput()).toContain('Iteration message 2'); + + // Remove without closing + const removed = graph.removeTerminalDestination(extraDest, false); + expect(removed).toBe(true); + // Destination should remain open + expect(extraDest.isOpen).toBe(true); + + // Second run: destination should not receive additional output + const beforeSecond = extraDest.getAllOutput(); + const second = await graph.executeAsync(graphIterationOptions); + expect(second.status).toBe(OperationStatus.Success); + const afterSecond = extraDest.getAllOutput(); + expect(afterSecond).toBe(beforeSecond); + }); + + it('removeTerminalDestination returns false when destination not found', () => { + const unknown = new MockWritable(); + const graph = createGraph(graphOptions, new MockOperationRunner('noop')); + const removed = graph.removeTerminalDestination(unknown); + expect(removed).toBe(false); + }); + }); +}); + +describe('invalidateOperations', () => { + it('invalidates a specific operation and updates graph status', async () => { + const graphOptions: IOperationGraphOptions = { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }; + + const runner: IOperationRunner = new MockOperationRunner('invalidate-success', async () => { + return OperationStatus.Success; + }); + + const graph: OperationGraph = createGraph(graphOptions, runner); + + const invalidateCalls: Array<{ ops: Iterable; reason: string | undefined }> = []; + graph.hooks.onInvalidateOperations.tap('test', (ops: Iterable, reason: string | undefined) => { + invalidateCalls.push({ ops, reason }); + }); + + const result: IExecutionResult = await graph.executeAsync({}); + expect(result.status).toBe(OperationStatus.Success); + const record: IOperationExecutionResult | undefined = result.operationResults.values().next().value; + expect(record?.status).toBe(OperationStatus.Success); + + const operation: Operation = Array.from(graph.operations)[0]; + graph.invalidateOperations([operation], 'unit-test'); + + const postRecord: IOperationExecutionResult | undefined = graph.resultByOperation.get(operation); + expect(postRecord?.status).toBe(OperationStatus.Ready); + expect(graph.status).toBe(OperationStatus.Ready); + expect(invalidateCalls.length).toBe(1); + const invalidatedOps: Operation[] = Array.from(invalidateCalls[0].ops as Set); + expect(invalidatedOps).toHaveLength(1); + expect(invalidatedOps[0]).toBe(operation); + expect(invalidateCalls[0].reason).toBe('unit-test'); + }); + + it('invalidates all operations when no iterable is provided', async () => { + const graphOptions: IOperationGraphOptions = { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }; + + const op1Runner: IOperationRunner = new MockOperationRunner('op1'); + const op2Runner: IOperationRunner = new MockOperationRunner('op2'); + + const op1: Operation = new Operation({ + runner: op1Runner, + logFilenameIdentifier: 'op1', + phase: mockPhase, + project: getOrCreateProject('p1') + }); + const op2: Operation = new Operation({ + runner: op2Runner, + logFilenameIdentifier: 'op2', + phase: mockPhase, + project: getOrCreateProject('p2') + }); + + const graph: OperationGraph = new OperationGraph(new Set([op1, op2]), graphOptions); + + await graph.executeAsync({}); + for (const record of graph.resultByOperation.values()) { + expect(record.status).toBeDefined(); + } + + graph.invalidateOperations(undefined, 'bulk'); + for (const record of graph.resultByOperation.values()) { + expect(record.status).toBe(OperationStatus.Ready); + } + }); +}); + +describe('deferred invalidation during active iteration', () => { + const deferGraphOptions: IOperationGraphOptions = { + quietMode: false, + debugMode: false, + parallelism: 2, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }; + + function createNamedOp(name: string, runner?: IOperationRunner): Operation { + return new Operation({ + runner: runner ?? new MockOperationRunner(name, async () => OperationStatus.Success), + phase: mockPhase, + project: getOrCreateProject(name), + logFilenameIdentifier: name + }); + } + + it('does not mutate a completed current-iteration record until after the iteration ends', async () => { + // op2 depends on op1, so op1 completes first and its record is written to resultByOperation. + // The afterExecuteOperationAsync hook for op2 fires while the iteration is still active, giving + // us a natural point to invalidate op1 and verify the deferred path. + const op1: Operation = createNamedOp('defer-op1'); + const op2: Operation = createNamedOp('defer-op2'); + op2.addDependency(op1); + + let op1StatusAtHookTime: OperationStatus | undefined; + let op1StatusAfterInvalidateCall: OperationStatus | undefined; + + const invalidateCalls: Array<{ ops: Operation[]; reason: string | undefined }> = []; + const graph: OperationGraph = new OperationGraph(new Set([op1, op2]), { + ...deferGraphOptions, + abortController: new AbortController() + }); + graph.hooks.afterExecuteOperationAsync.tapPromise('test', async (record) => { + if (record.operation === op2) { + // op1 is already in resultByOperation (set during op1's completion handler) + op1StatusAtHookTime = graph.resultByOperation.get(op1)?.status; + graph.invalidateOperations([op1], 'mid-iter'); + // Deferred — record must not have been mutated + op1StatusAfterInvalidateCall = graph.resultByOperation.get(op1)?.status; + } + }); + graph.hooks.onInvalidateOperations.tap('test', (ops, reason) => { + invalidateCalls.push({ ops: [...(ops as Set)], reason }); + }); + + const result: IExecutionResult = await graph.executeAsync({}); + + // Both ops executed successfully; the abort triggered by invalidation had nothing left to abort. + expect(result.status).toBe(OperationStatus.Success); + + // op1 was in resultByOperation (Success) when the hook fired for op2 + expect(op1StatusAtHookTime).toBe(OperationStatus.Success); + // The deferred path must not mutate the record mid-iteration + expect(op1StatusAfterInvalidateCall).toBe(OperationStatus.Success); + + // After the iteration .finally() fires, the deferred reset is applied + expect(graph.resultByOperation.get(op1)?.status).toBe(OperationStatus.Ready); + + // onInvalidateOperations fires once — after the iteration ends — with [op1] and the reason. + // The synchronous call inside invalidateOperations() is skipped because the invalidated set is empty. + expect(invalidateCalls).toHaveLength(1); + expect(invalidateCalls[0].ops).toHaveLength(1); + expect(invalidateCalls[0].ops[0]).toBe(op1); + expect(invalidateCalls[0].reason).toBe('mid-iter'); + }); + + it('coalesces deferred invalidations with the same reason into a single hook call', async () => { + // op3 depends on both op1 and op2, so both complete before op3 starts. + // The afterExecuteOperationAsync hook for op3 invalidates both with the same reason. + // The deferred .finally() handler must fire a single onInvalidateOperations call for both. + const op1: Operation = createNamedOp('coalesce-op1'); + const op2: Operation = createNamedOp('coalesce-op2'); + const op3: Operation = createNamedOp('coalesce-op3'); + op3.addDependency(op1); + op3.addDependency(op2); + + const graph: OperationGraph = new OperationGraph(new Set([op1, op2, op3]), { + ...deferGraphOptions, + abortController: new AbortController() + }); + graph.hooks.afterExecuteOperationAsync.tapPromise('test', async (record) => { + if (record.operation === op3) { + graph.invalidateOperations([op1], 'same-reason'); + graph.invalidateOperations([op2], 'same-reason'); + } + }); + + const deferredCalls: Array<{ ops: Operation[]; reason: string | undefined }> = []; + graph.hooks.onInvalidateOperations.tap('test', (ops, reason) => { + const opsArray: Operation[] = [...(ops as Set)]; + if (opsArray.length > 0) { + deferredCalls.push({ ops: opsArray, reason }); + } + }); + + await graph.executeAsync({}); + + // Only one deferred call: both op1 and op2 arrive together under 'same-reason' + expect(deferredCalls).toHaveLength(1); + expect(deferredCalls[0].reason).toBe('same-reason'); + expect(new Set(deferredCalls[0].ops)).toEqual(new Set([op1, op2])); + + // Both records are now Ready + expect(graph.resultByOperation.get(op1)?.status).toBe(OperationStatus.Ready); + expect(graph.resultByOperation.get(op2)?.status).toBe(OperationStatus.Ready); + }); + + it('fires separate hook calls for deferred invalidations with distinct reasons', async () => { + const op1: Operation = createNamedOp('distinct-op1'); + const op2: Operation = createNamedOp('distinct-op2'); + const op3: Operation = createNamedOp('distinct-op3'); + op3.addDependency(op1); + op3.addDependency(op2); + + const graph: OperationGraph = new OperationGraph(new Set([op1, op2, op3]), { + ...deferGraphOptions, + abortController: new AbortController() + }); + graph.hooks.afterExecuteOperationAsync.tapPromise('test', async (record) => { + if (record.operation === op3) { + graph.invalidateOperations([op1], 'reason-a'); + graph.invalidateOperations([op2], 'reason-b'); + } + }); + + const deferredCalls: Array<{ ops: Operation[]; reason: string | undefined }> = []; + graph.hooks.onInvalidateOperations.tap('test', (ops, reason) => { + const opsArray: Operation[] = [...(ops as Set)]; + if (opsArray.length > 0) { + deferredCalls.push({ ops: opsArray, reason }); + } + }); + + await graph.executeAsync({}); + + // Two separate deferred calls — one per reason + expect(deferredCalls).toHaveLength(2); + const callA: { ops: Operation[]; reason: string | undefined } | undefined = deferredCalls.find( + (c) => c.reason === 'reason-a' + ); + const callB: { ops: Operation[]; reason: string | undefined } | undefined = deferredCalls.find( + (c) => c.reason === 'reason-b' + ); + expect(callA?.ops).toHaveLength(1); + expect(callA?.ops[0]).toBe(op1); + expect(callB?.ops).toHaveLength(1); + expect(callB?.ops[0]).toBe(op2); + }); + + it('skips operations that are already in a non-terminal (Ready) state', async () => { + // Run the graph, then manually invalidate an op to put it in Ready state. + // A second invalidateOperations call on the same (already-Ready) op must be a no-op: + // TERMINAL_STATUSES does not include Ready, so the guard prevents processing. + const op: Operation = createNamedOp('skip-ready-op'); + const graph: OperationGraph = new OperationGraph(new Set([op]), { + ...deferGraphOptions, + abortController: new AbortController() + }); + + await graph.executeAsync({}); + expect(graph.resultByOperation.get(op)?.status).toBe(OperationStatus.Success); + + // First invalidation: Success → Ready + graph.invalidateOperations([op], 'first'); + expect(graph.resultByOperation.get(op)?.status).toBe(OperationStatus.Ready); + + // Now tap AFTER the first invalidation so we only observe the second call + const secondCallOps: Operation[][] = []; + graph.hooks.onInvalidateOperations.tap('test-second', (ops) => { + secondCallOps.push([...(ops as Set)]); + }); + + // Second invalidation: op is in Ready state (not terminal) — should be skipped + graph.invalidateOperations([op], 'second'); + + // Hook is not called at all — the invalidated set was empty, so the call is skipped + expect(secondCallOps).toHaveLength(0); + expect(graph.resultByOperation.get(op)?.status).toBe(OperationStatus.Ready); // unchanged + }); +}); + +describe('closeRunnersAsync', () => { + class ClosableRunner extends MockOperationRunner { + public readonly closeAsync: jest.Mock, []> = jest.fn(async () => { + /* no-op */ + }); + } + + it('invokes closeAsync on runners and triggers onExecutionStatesUpdated hook', async () => { + const localOptions: IOperationGraphOptions = { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }; + + const runner = new ClosableRunner('closable'); + const graph: OperationGraph = createGraph(localOptions, runner); + + await graph.executeAsync({}); + + const statusChangedCalls: ReadonlySet[] = []; + graph.hooks.onExecutionStatesUpdated.tap('test', (records) => { + statusChangedCalls.push(records); + }); + + await graph.closeRunnersAsync(); + + expect(runner.closeAsync).toHaveBeenCalledTimes(1); + expect(statusChangedCalls.length).toBe(1); + const firstBatchArray = Array.from(statusChangedCalls[0]); + expect(firstBatchArray[0].operation.runner).toBe(runner); + }); + + it('only closes specified runners when operations iterable provided', async () => { + const graphOptions: IOperationGraphOptions = { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }; + + const runner1 = new ClosableRunner('closable1'); + const runner2 = new ClosableRunner('closable2'); + + const op1: Operation = new Operation({ + runner: runner1, + logFilenameIdentifier: 'c1', + phase: mockPhase, + project: getOrCreateProject('c1') + }); + const op2: Operation = new Operation({ + runner: runner2, + logFilenameIdentifier: 'c2', + phase: mockPhase, + project: getOrCreateProject('c2') + }); + + const graph: OperationGraph = new OperationGraph(new Set([op1, op2]), graphOptions); + await graph.executeAsync({}); + + await graph.closeRunnersAsync([op1]); + expect(runner1.closeAsync).toHaveBeenCalledTimes(1); + expect(runner2.closeAsync).not.toHaveBeenCalled(); + }); +}); + +describe('Graph state change notifications', () => { + function createGraphForStateTests(overrides: Partial = {}): OperationGraph { + const baseOptions: IOperationGraphOptions = { + quietMode: false, + debugMode: false, + parallelism: 2, + maxParallelism: 4, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }; + return new OperationGraph(new Set(), { ...baseOptions, ...overrides }); + } + + beforeEach(() => { + jest.useFakeTimers({ legacyFakeTimers: true }); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + async function flushNextTick(): Promise { + jest.runAllTicks(); + } + + it('invokes callback when a single property changes', async () => { + const graph: OperationGraph = createGraphForStateTests(); + const calls: IOperationGraph[] = []; + graph.hooks.onGraphStateChanged.tap('test', (m) => calls.push(m)); + graph.debugMode = true; + expect(calls.length).toBe(0); + await flushNextTick(); + expect(calls.length).toBe(1); + expect(graph.debugMode).toBe(true); + }); + + it('debounces multiple property changes in the same tick', async () => { + const graph: OperationGraph = createGraphForStateTests(); + const calls: IOperationGraph[] = []; + graph.hooks.onGraphStateChanged.tap('test', (m) => calls.push(m)); + graph.debugMode = true; + graph.quietMode = true; + graph.pauseNextIteration = true; + graph.parallelism = 3; + await flushNextTick(); + expect(calls.length).toBe(1); + expect(graph.debugMode).toBe(true); + expect(graph.quietMode).toBe(true); + expect(graph.pauseNextIteration).toBe(true); + expect(graph.parallelism).toBe(3); + }); + + it('does not invoke callback when setting a property to its existing value', async () => { + const graph: OperationGraph = createGraphForStateTests(); + const calls: IOperationGraph[] = []; + graph.hooks.onGraphStateChanged.tap('test', (m) => calls.push(m)); + graph.debugMode = false; + graph.quietMode = false; + await flushNextTick(); + expect(calls.length).toBe(0); + }); + + it('clamps parallelism to configured bounds and invokes callback only when value changes', async () => { + const graph: OperationGraph = createGraphForStateTests({ + parallelism: 2, + maxParallelism: 4 + }); + const calls: IOperationGraph[] = []; + graph.hooks.onGraphStateChanged.tap('test', (m) => calls.push(m)); + + // Increase beyond max -> clamp to 4 + graph.parallelism = 10; + await flushNextTick(); + expect(graph.parallelism).toBe(4); + expect(calls.length).toBe(1); + + // Set to same clamped value -> no new callback + graph.parallelism = 10; // still clamps to 4, unchanged + await flushNextTick(); + expect(calls.length).toBe(1); + + // Decrease below minimum -> clamp to 1 (change from 4) + graph.parallelism = 0; + await flushNextTick(); + expect(graph.parallelism).toBe(1); + expect(calls.length).toBe(2); + }); + + it('pauseNextIteration change triggers callback only when value changes', async () => { + const graph: OperationGraph = createGraphForStateTests(); + const calls: IOperationGraph[] = []; + graph.hooks.onGraphStateChanged.tap('test', (m) => calls.push(m)); + + graph.pauseNextIteration = true; + await flushNextTick(); + expect(calls.length).toBe(1); + expect(graph.pauseNextIteration).toBe(true); + + graph.pauseNextIteration = true; + await flushNextTick(); + expect(calls.length).toBe(1); + + graph.pauseNextIteration = false; + await flushNextTick(); + expect(calls.length).toBe(2); + expect(graph.pauseNextIteration).toBe(false); + }); +}); + +describe('setEnabledStates', () => { + function createChain(names: string[]): Operation[] { + const ops: Operation[] = names.map( + (n) => + new Operation({ + runner: new MockOperationRunner(n, async () => OperationStatus.Success), + phase: mockPhase, + project: getOrCreateProject(n), + logFilenameIdentifier: n + }) + ); + // Simple linear dependencies a->b->c (each depends on next) for dependency expansion tests + for (let i = 0; i < ops.length - 1; i++) { + ops[i].addDependency(ops[i + 1]); + } + return ops; + } + + function createGraphWithOperations(ops: Operation[]): OperationGraph { + return new OperationGraph(new Set(ops), { + quietMode: false, + debugMode: false, + parallelism: 1, + allowOversubscription: true, + destinations: [mockWritable], + abortController: new AbortController() + }); + } + + it('safe enable expands dependencies', () => { + const [a, b, c] = createChain(['a', 'b', 'c']); + // start disabled + a.enabled = false; + b.enabled = false; + c.enabled = false; + const graph = createGraphWithOperations([a, b, c]); + const calls: ReadonlySet[] = []; + graph.hooks.onEnableStatesChanged.tap('test', (ops) => calls.push(new Set(ops))); + const changed = graph.setEnabledStates([a], true, 'safe'); + expect(changed).toBe(true); + // All three should now be true because of dependency expansion (a depends on b depends on c) + expect(a.enabled).toBe(true); + expect(b.enabled).toBe(true); + expect(c.enabled).toBe(true); + expect(calls).toHaveLength(1); + expect(Array.from(calls[0]).sort((x, y) => x.name!.localeCompare(y.name!))).toEqual([a, b, c]); + }); + + it('safe disable disables entire dependency subtree when not required elsewhere', () => { + const [a, b, c] = createChain(['a', 'b', 'c']); + // Initially all true + const graph = createGraphWithOperations([a, b, c]); + const calls: ReadonlySet[] = []; + graph.hooks.onEnableStatesChanged.tap('test', (ops) => calls.push(new Set(ops))); + // Attempt to disable middle dependency (b) safely -> should NOT disable since a depends on b (b and its subtree still required) + const changedB = graph.setEnabledStates([b], false, 'safe'); + expect(changedB).toBe(false); + expect(calls).toHaveLength(0); + // Attempt to disable leaf c safely -> should NOT disable since b (and thus a) still depend on c + const changedC = graph.setEnabledStates([c], false, 'safe'); + expect(changedC).toBe(false); + expect(calls).toHaveLength(0); + // Disable root a safely -> this should disable a and its entire dependency subtree (b,c) since nothing else depends on them + const changedA = graph.setEnabledStates([a], false, 'safe'); + expect(changedA).toBe(true); + expect(a.enabled).toBe(false); + expect(b.enabled).toBe(false); + expect(c.enabled).toBe(false); + expect(calls).toHaveLength(1); // single batch for subtree disable + const changedNames: string[] = Array.from(calls[0], (op) => op.name!).sort(); + expect(changedNames).toEqual(['a', 'b', 'c']); + }); + + it('safe ignore-dependency-changes sets requested and dependencies to ignore state, respects per-op flag', () => { + const [a, b, c] = createChain(['a', 'b', 'c']); + // Simulate b having ignoreChangedProjectsOnlyFlag forcing it to true rather than ignore-dependency-changes + b.settings = { ignoreChangedProjectsOnlyFlag: true } as unknown as typeof b.settings; + a.enabled = false; + b.enabled = false; + c.enabled = false; + const graph = createGraphWithOperations([a, b, c]); + const calls: ReadonlySet[] = []; + graph.hooks.onEnableStatesChanged.tap('test', (ops) => calls.push(new Set(ops))); + const changed = graph.setEnabledStates([a], 'ignore-dependency-changes', 'safe'); + expect(changed).toBe(true); + expect(a.enabled).toBe('ignore-dependency-changes'); + // b forced to true because of its settings flag + expect(b.enabled).toBe(true); + const cState: Operation['enabled'] = c.enabled; + const acceptable: boolean = + cState === (true as Operation['enabled']) || + cState === ('ignore-dependency-changes' as Operation['enabled']); + expect(acceptable).toBe(true); + expect(calls).toHaveLength(1); + // a and b at least must be in changed set (c may also if changed) + const changedNames = new Set(Array.from(calls[0], (o) => o.name)); + expect(changedNames.has('a')).toBe(true); + expect(changedNames.has('b')).toBe(true); + }); + + it('unsafe mode only mutates provided operations', () => { + const [a, b, c] = createChain(['a', 'b', 'c']); + const graph = createGraphWithOperations([a, b, c]); + const calls: ReadonlySet[] = []; + graph.hooks.onEnableStatesChanged.tap('test', (ops) => calls.push(new Set(ops))); + const changed = graph.setEnabledStates([b], false, 'unsafe'); + expect(changed).toBe(true); + expect(a.enabled).not.toBe(false); + expect(b.enabled).toBe(false); + expect(c.enabled).not.toBe(false); + expect(calls).toHaveLength(1); + expect(Array.from(calls[0])).toEqual([b]); + }); +}); diff --git a/libraries/rush-lib/src/logic/operations/test/OperationMetadataManager.test.ts b/libraries/rush-lib/src/logic/operations/test/OperationMetadataManager.test.ts new file mode 100644 index 00000000000..6443e873d67 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/test/OperationMetadataManager.test.ts @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +jest.mock('../OperationStateFile'); +jest.mock('node:fs'); + +import { MockWritable, StringBufferTerminalProvider, Terminal, TerminalChunkKind } from '@rushstack/terminal'; +import type { IPhase } from '../../../api/CommandLineConfiguration'; +import type { RushConfigurationProject } from '../../../api/RushConfigurationProject'; +import { OperationMetadataManager } from '../OperationMetadataManager'; +import { CollatedTerminalProvider } from '../../../utilities/CollatedTerminalProvider'; +import { CollatedTerminal } from '@rushstack/stream-collator'; +import { FileSystem } from '@rushstack/node-core-library'; +import * as fs from 'node:fs'; +import { Readable } from 'node:stream'; +import { Operation } from '../Operation'; + +const mockWritable: MockWritable = new MockWritable(); +const mockTerminal: Terminal = new Terminal(new CollatedTerminalProvider(new CollatedTerminal(mockWritable))); + +const operation = new Operation({ + logFilenameIdentifier: 'identifier', + project: { + projectFolder: '/path/to/project' + } as unknown as RushConfigurationProject, + phase: { + logFilenameIdentifier: 'identifier' + } as unknown as IPhase +}); + +const manager: OperationMetadataManager = new OperationMetadataManager({ + operation +}); + +describe(OperationMetadataManager.name, () => { + let mockTerminalProvider: StringBufferTerminalProvider; + beforeEach(() => { + mockTerminalProvider = new StringBufferTerminalProvider(false); + jest.spyOn(FileSystem, 'copyFileAsync').mockResolvedValue(); + }); + + function toJsonLines(data: object[]): string { + return data.map((item) => JSON.stringify(item)).join('\n'); + } + + it('should restore chunked stdout', async () => { + const data = [ + { + text: 'chunk1\n', + kind: TerminalChunkKind.Stdout + }, + { + text: 'chunk2\n', + kind: TerminalChunkKind.Stdout + } + ]; + + jest.spyOn(FileSystem, 'readFileAsync').mockResolvedValue(toJsonLines(data)); + + await manager.tryRestoreAsync({ + terminal: mockTerminal, + terminalProvider: mockTerminalProvider, + errorLogPath: '/path/to/errorLog' + }); + + expect(mockTerminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); + expect(mockTerminalProvider.getWarningOutput()).toBeFalsy(); + }); + + it('should restore chunked stderr', async () => { + const data = [ + { + text: 'chunk1\n', + kind: TerminalChunkKind.Stderr + }, + { + text: 'chunk2\n', + kind: TerminalChunkKind.Stderr + } + ]; + + jest.spyOn(FileSystem, 'readFileAsync').mockResolvedValue(toJsonLines(data)); + + await manager.tryRestoreAsync({ + terminal: mockTerminal, + terminalProvider: mockTerminalProvider, + errorLogPath: '/path/to/errorLog' + }); + + expect(mockTerminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); + }); + + it('should restore mixed chunked output', async () => { + const data = [ + { + text: 'logged to stdout\n', + kind: TerminalChunkKind.Stdout + }, + { + text: 'logged to stderr\n', + kind: TerminalChunkKind.Stderr + } + ]; + + jest.spyOn(FileSystem, 'readFileAsync').mockResolvedValue(toJsonLines(data)); + + await manager.tryRestoreAsync({ + terminal: mockTerminal, + terminalProvider: mockTerminalProvider, + errorLogPath: '/path/to/errorLog' + }); + expect(mockTerminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); + }); + + it("should fallback to the log file when chunked output isn't available", async () => { + // Normalize newlines to make the error message consistent across platforms + const normalizedRawLogFile: string = `stdout log file`; + jest + .spyOn(FileSystem, 'readFileAsync') + .mockRejectedValue({ code: 'ENOENT', syscall: 'open', path: '/path/to/file', errno: 1 }); + + const mockClose = jest.fn(); + const mockReadStream: fs.ReadStream = Readable.from([normalizedRawLogFile]) as fs.ReadStream; + mockReadStream.close = mockClose; + jest.spyOn(fs, 'createReadStream').mockReturnValue(mockReadStream); + + await manager.tryRestoreAsync({ + terminal: mockTerminal, + terminalProvider: mockTerminalProvider, + errorLogPath: '/path/to/errorLog' + }); + + expect(mockTerminalProvider.getAllOutput(true)).toEqual({}); + expect(mockClose).toHaveBeenCalledTimes(1); + expect(mockWritable.chunks).toMatchSnapshot(); + }); +}); diff --git a/libraries/rush-lib/src/logic/operations/test/ParseParallelism.test.ts b/libraries/rush-lib/src/logic/operations/test/ParseParallelism.test.ts new file mode 100644 index 00000000000..c038e2f6771 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/test/ParseParallelism.test.ts @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { coerceParallelism, parseParallelism } from '../ParseParallelism'; + +describe(parseParallelism.name, () => { + it('throwsErrorOnInvalidParallelism', () => { + expect(() => parseParallelism('tequila')).toThrowErrorMatchingSnapshot(); + }); + + it('throwsErrorOnInvalidParallelismPercentage', () => { + expect(() => parseParallelism('200%')).toThrowErrorMatchingSnapshot(); + }); + + it('returns scalar 1 for "max"', () => { + expect(parseParallelism('max')).toEqual({ scalar: 1 }); + }); + + it('returns a scalar for a percentage input', () => { + expect(parseParallelism('50%')).toEqual({ scalar: 0.5 }); + }); + + it('returns a raw number for a numeric input', () => { + expect(parseParallelism('4')).toBe(4); + }); + + it('trims whitespace from input', () => { + expect(parseParallelism(' 4 ')).toBe(4); + }); +}); + +describe(coerceParallelism.name, () => { + describe('raw numeric values', () => { + it('passes through a number within range', () => { + expect(coerceParallelism(4, 8)).toBe(4); + }); + + it('clamps a number above maxParallelism down to maxParallelism', () => { + expect(coerceParallelism(16, 8)).toBe(8); + }); + + it('clamps a negative number up to 0', () => { + expect(coerceParallelism(-1, 8)).toBe(0); + }); + + it('allows 0', () => { + expect(coerceParallelism(0, 8)).toBe(0); + }); + }); + + describe('scalar values', () => { + it('converts scalar 1 to maxParallelism', () => { + expect(coerceParallelism({ scalar: 1 }, 8)).toBe(8); + }); + + it('converts scalar 0.5 to half of maxParallelism', () => { + expect(coerceParallelism({ scalar: 0.5 }, 8)).toBe(4); + }); + + it('floors fractional results', () => { + // floor(0.333333 * 8) = floor(2.666...) = 2 + expect(coerceParallelism({ scalar: 0.333333 }, 8)).toBe(2); + }); + + it('clamps scalar result to at least 1', () => { + // floor(0.001 * 8) = 0, clamped up to 1 + expect(coerceParallelism({ scalar: 0.001 }, 8)).toBe(1); + }); + + it('Windows default scalar (0.999) yields one less than maxParallelism', () => { + // floor(0.999 * 8) = floor(7.992) = 7 + expect(coerceParallelism({ scalar: 0.999 }, 8)).toBe(7); + }); + }); +}); diff --git a/libraries/rush-lib/src/logic/operations/test/PhasedOperationPlugin.test.ts b/libraries/rush-lib/src/logic/operations/test/PhasedOperationPlugin.test.ts index 2d5b46c79ec..109c96676b8 100644 --- a/libraries/rush-lib/src/logic/operations/test/PhasedOperationPlugin.test.ts +++ b/libraries/rush-lib/src/logic/operations/test/PhasedOperationPlugin.test.ts @@ -1,35 +1,51 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import path from 'path'; +import path from 'node:path'; import { JsonFile } from '@rushstack/node-core-library'; import { RushConfiguration } from '../../../api/RushConfiguration'; -import { - CommandLineConfiguration, - IPhase, - IPhasedCommandConfig -} from '../../../api/CommandLineConfiguration'; +import type { RushConfigurationProject } from '../../../api/RushConfigurationProject'; +import { CommandLineConfiguration, type IPhasedCommandConfig } from '../../../api/CommandLineConfiguration'; import { PhasedOperationPlugin } from '../PhasedOperationPlugin'; -import { Operation } from '../Operation'; -import { ICommandLineJson } from '../../../api/CommandLineJson'; +import type { Operation } from '../Operation'; +import type { ICommandLineJson } from '../../../api/CommandLineJson'; import { RushConstants } from '../../RushConstants'; import { MockOperationRunner } from './MockOperationRunner'; -import { ICreateOperationsContext, PhasedCommandHooks } from '../../../pluginFramework/PhasedCommandHooks'; -import { RushConfigurationProject } from '../../..'; +import { + type ICreateOperationsContext, + type IOperationGraphContext, + PhasedCommandHooks +} from '../../../pluginFramework/PhasedCommandHooks'; +import type { IOperationGraph } from '../IOperationGraph'; +import { OperationGraphHooks } from '../../../pluginFramework/OperationGraphHooks'; +type IOperationExecutionManager = IOperationGraph; +type IOperationExecutionManagerContext = IOperationGraphContext; + +function serializeOperation(operation: Operation): string { + return `${operation.name} (${operation.enabled ? 'enabled' : 'disabled'}${operation.runner!.silent ? ', silent' : ''}) -> [${Array.from( + operation.dependencies, + (dep: Operation) => dep.name + ) + .sort() + .join(', ')}]`; +} -interface ISerializedOperation { - name: string; - silent: boolean; - dependencies: string[]; +function compareOperation(a: Operation, b: Operation): number { + if (a.enabled && !b.enabled) { + return -1; + } + if (!a.enabled && b.enabled) { + return 1; + } + return a.name < b.name ? -1 : a.name > b.name ? 1 : 0; } -function serializeOperation(operation: Operation): ISerializedOperation { - return { - name: operation.name!, - silent: operation.runner!.silent, - dependencies: Array.from(operation.dependencies, (dep: Operation) => dep.name!) - }; +function expectOperationsToMatchSnapshot(operations: Set, name: string): void { + const serializedOperations: string[] = Array.from(operations) + .sort(compareOperation) + .map(serializeOperation); + expect(serializedOperations).toMatchSnapshot(name); } describe(PhasedOperationPlugin.name, () => { @@ -43,7 +59,7 @@ describe(PhasedOperationPlugin.name, () => { for (const operation of operations) { const { associatedPhase, associatedProject } = operation; - if (associatedPhase && associatedProject && !operation.runner) { + if (!operation.runner) { const name: string = `${associatedProject.packageName} (${associatedPhase.name.slice( RushConstants.phaseNamePrefix.length )})`; @@ -55,57 +71,74 @@ describe(PhasedOperationPlugin.name, () => { return operations; } - async function testCreateOperationsAsync( - phaseSelection: Set, - projectSelection: Set, - changedProjects: Set - ): Promise> { + interface ITestCreateOperationsContext { + phaseSelection: ICreateOperationsContext['phaseSelection']; + projectSelection: ICreateOperationsContext['projectSelection']; + includePhaseDeps?: ICreateOperationsContext['includePhaseDeps']; + generateFullGraph?: ICreateOperationsContext['generateFullGraph']; + } + + let rushConfiguration!: RushConfiguration; + let commandLineConfiguration!: CommandLineConfiguration; + + beforeAll(() => { + rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); + const commandLineJson: ICommandLineJson = JsonFile.load(commandLineJsonFile); + + commandLineConfiguration = new CommandLineConfiguration(commandLineJson); + }); + + async function testCreateOperationsAsync(options: ITestCreateOperationsContext): Promise> { + const { phaseSelection, projectSelection, includePhaseDeps = false, generateFullGraph = false } = options; const hooks: PhasedCommandHooks = new PhasedCommandHooks(); // Apply the plugin being tested new PhasedOperationPlugin().apply(hooks); // Add mock runners for included operations. - hooks.createOperations.tap('MockOperationRunnerPlugin', createMockRunner); + hooks.createOperationsAsync.tap('MockOperationRunnerPlugin', createMockRunner); - const context: Pick< - ICreateOperationsContext, - 'phaseOriginal' | 'phaseSelection' | 'projectSelection' | 'projectsInUnknownState' - > = { - phaseOriginal: phaseSelection, + const context: Partial = { phaseSelection, projectSelection, - projectsInUnknownState: changedProjects + projectConfigurations: new Map(), + includePhaseDeps, + generateFullGraph, + // Minimal required fields for plugin logic not used directly in these tests + changedProjectsOnly: false, + isIncrementalBuildAllowed: true, + isWatch: generateFullGraph, // simulate watch when using full graph flag + customParameters: new Map(), + rushConfiguration }; - const operations: Set = await hooks.createOperations.promise( + const operations: Set = await hooks.createOperationsAsync.promise( new Set(), context as ICreateOperationsContext ); + const executionHooks: OperationGraphHooks = new OperationGraphHooks(); + const executionManager: Partial = { + operations, + hooks: executionHooks + }; + await hooks.onGraphCreatedAsync.promise( + executionManager as IOperationExecutionManager, + context as IOperationExecutionManagerContext + ); + return operations; } - let rushConfiguration!: RushConfiguration; - let commandLineConfiguration!: CommandLineConfiguration; - - beforeAll(() => { - rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); - const commandLineJson: ICommandLineJson = JsonFile.load(commandLineJsonFile); - - commandLineConfiguration = new CommandLineConfiguration(commandLineJson); - }); - it('handles a full build', async () => { const buildCommand: IPhasedCommandConfig = commandLineConfiguration.commands.get( 'build' )! as IPhasedCommandConfig; - const operations: Set = await testCreateOperationsAsync( - buildCommand.phases, - new Set(rushConfiguration.projects), - new Set(rushConfiguration.projects) - ); + const operations: Set = await testCreateOperationsAsync({ + phaseSelection: buildCommand.phases, + projectSelection: new Set(rushConfiguration.projects) + }); // All projects - expect(Array.from(operations, serializeOperation)).toMatchSnapshot(); + expectOperationsToMatchSnapshot(operations, 'full'); }); it('handles filtered projects', async () => { @@ -113,139 +146,123 @@ describe(PhasedOperationPlugin.name, () => { 'build' )! as IPhasedCommandConfig; - let operations: Set = await testCreateOperationsAsync( - buildCommand.phases, - new Set([rushConfiguration.getProjectByName('g')!]), - new Set([rushConfiguration.getProjectByName('g')!]) - ); + let operations: Set = await testCreateOperationsAsync({ + phaseSelection: buildCommand.phases, + projectSelection: new Set([rushConfiguration.getProjectByName('g')!]) + }); // Single project - expect(Array.from(operations, serializeOperation)).toMatchSnapshot(); + expectOperationsToMatchSnapshot(operations, 'single'); - operations = await testCreateOperationsAsync( - buildCommand.phases, - new Set([ - rushConfiguration.getProjectByName('f')!, - rushConfiguration.getProjectByName('a')!, - rushConfiguration.getProjectByName('c')! - ]), - new Set([ + operations = await testCreateOperationsAsync({ + phaseSelection: buildCommand.phases, + projectSelection: new Set([ rushConfiguration.getProjectByName('f')!, rushConfiguration.getProjectByName('a')!, rushConfiguration.getProjectByName('c')! ]) - ); + }); // Filtered projects - expect(Array.from(operations, serializeOperation)).toMatchSnapshot(); + expectOperationsToMatchSnapshot(operations, 'filtered'); }); - it('handles some changed projects', async () => { - const buildCommand: IPhasedCommandConfig = commandLineConfiguration.commands.get( - 'build' - )! as IPhasedCommandConfig; - - let operations: Set = await testCreateOperationsAsync( - buildCommand.phases, - new Set(rushConfiguration.projects), - new Set([rushConfiguration.getProjectByName('g')!]) - ); + it('handles incomplete phaseSelection without --include-phase-deps', async () => { + const operations: Set = await testCreateOperationsAsync({ + includePhaseDeps: false, + phaseSelection: new Set([commandLineConfiguration.phases.get('_phase:upstream-self')!]), + projectSelection: new Set([rushConfiguration.getProjectByName('a')!]) + }); - // Single project - expect(Array.from(operations, serializeOperation)).toMatchSnapshot(); + expectOperationsToMatchSnapshot(operations, 'single-project'); + }); - operations = await testCreateOperationsAsync( - buildCommand.phases, - new Set(rushConfiguration.projects), - new Set([ - rushConfiguration.getProjectByName('f')!, - rushConfiguration.getProjectByName('a')!, - rushConfiguration.getProjectByName('c')! - ]) - ); + it('handles incomplete phaseSelection with --include-phase-deps', async () => { + const operations: Set = await testCreateOperationsAsync({ + includePhaseDeps: true, + phaseSelection: new Set([commandLineConfiguration.phases.get('_phase:upstream-self')!]), + projectSelection: new Set([rushConfiguration.getProjectByName('a')!]) + }); - // Filtered projects - expect(Array.from(operations, serializeOperation)).toMatchSnapshot(); + expectOperationsToMatchSnapshot(operations, 'single-project'); }); - it('handles some changed projects within filtered projects', async () => { - const buildCommand: IPhasedCommandConfig = commandLineConfiguration.commands.get( - 'build' - )! as IPhasedCommandConfig; - - const operations: Set = await testCreateOperationsAsync( - buildCommand.phases, - new Set([ - rushConfiguration.getProjectByName('f')!, - rushConfiguration.getProjectByName('a')!, - rushConfiguration.getProjectByName('c')! - ]), - new Set([rushConfiguration.getProjectByName('a')!, rushConfiguration.getProjectByName('c')!]) - ); + it('handles incomplete phaseSelection cross-project with --include-phase-deps', async () => { + const operations: Set = await testCreateOperationsAsync({ + includePhaseDeps: true, + phaseSelection: new Set([commandLineConfiguration.phases.get('_phase:upstream-1')!]), + projectSelection: new Set([rushConfiguration.getProjectByName('h')!]) + }); - // Single project - expect(Array.from(operations, serializeOperation)).toMatchSnapshot(); + expectOperationsToMatchSnapshot(operations, 'multiple-project'); }); it('handles filtered phases', async () => { // Single phase with a missing dependency - let operations: Set = await testCreateOperationsAsync( - new Set([commandLineConfiguration.phases.get('_phase:upstream-self')!]), - new Set(rushConfiguration.projects), - new Set(rushConfiguration.projects) - ); - expect(Array.from(operations, serializeOperation)).toMatchSnapshot(); + let operations: Set = await testCreateOperationsAsync({ + phaseSelection: new Set([commandLineConfiguration.phases.get('_phase:upstream-self')!]), + projectSelection: new Set(rushConfiguration.projects) + }); + expectOperationsToMatchSnapshot(operations, 'single-phase'); // Two phases with a missing link - operations = await testCreateOperationsAsync( - new Set([ + operations = await testCreateOperationsAsync({ + phaseSelection: new Set([ commandLineConfiguration.phases.get('_phase:complex')!, commandLineConfiguration.phases.get('_phase:upstream-3')!, commandLineConfiguration.phases.get('_phase:upstream-1')!, commandLineConfiguration.phases.get('_phase:no-deps')! ]), - new Set(rushConfiguration.projects), - new Set(rushConfiguration.projects) - ); - expect(Array.from(operations, serializeOperation)).toMatchSnapshot(); + projectSelection: new Set(rushConfiguration.projects) + }); + expectOperationsToMatchSnapshot(operations, 'two-phases'); }); it('handles filtered phases on filtered projects', async () => { // Single phase with a missing dependency - let operations: Set = await testCreateOperationsAsync( - new Set([commandLineConfiguration.phases.get('_phase:upstream-2')!]), - new Set([ - rushConfiguration.getProjectByName('f')!, - rushConfiguration.getProjectByName('a')!, - rushConfiguration.getProjectByName('c')! - ]), - new Set([ + let operations: Set = await testCreateOperationsAsync({ + phaseSelection: new Set([commandLineConfiguration.phases.get('_phase:upstream-2')!]), + projectSelection: new Set([ rushConfiguration.getProjectByName('f')!, rushConfiguration.getProjectByName('a')!, rushConfiguration.getProjectByName('c')! ]) - ); - expect(Array.from(operations, serializeOperation)).toMatchSnapshot(); + }); + expectOperationsToMatchSnapshot(operations, 'single-phase'); // Phases with missing links - operations = await testCreateOperationsAsync( - new Set([ + operations = await testCreateOperationsAsync({ + phaseSelection: new Set([ commandLineConfiguration.phases.get('_phase:complex')!, commandLineConfiguration.phases.get('_phase:upstream-3')!, commandLineConfiguration.phases.get('_phase:upstream-1')!, commandLineConfiguration.phases.get('_phase:no-deps')! ]), - new Set([ - rushConfiguration.getProjectByName('f')!, - rushConfiguration.getProjectByName('a')!, - rushConfiguration.getProjectByName('c')! - ]), - new Set([ + projectSelection: new Set([ rushConfiguration.getProjectByName('f')!, rushConfiguration.getProjectByName('a')!, rushConfiguration.getProjectByName('c')! ]) - ); - expect(Array.from(operations, serializeOperation)).toMatchSnapshot(); + }); + expectOperationsToMatchSnapshot(operations, 'missing-links'); + }); + + it('includes full graph but enables subset when generateFullGraph is true', async () => { + const buildCommand: IPhasedCommandConfig = commandLineConfiguration.commands.get( + 'build' + )! as IPhasedCommandConfig; + const subset: Set = new Set([ + rushConfiguration.getProjectByName('a')!, + rushConfiguration.getProjectByName('c')! + ]); + + const operations: Set = await testCreateOperationsAsync({ + phaseSelection: buildCommand.phases, + projectSelection: subset, + generateFullGraph: true + }); + + // Expect all projects to be present, but only selected subset enabled + expectOperationsToMatchSnapshot(operations, 'full-graph-filtered'); }); }); diff --git a/libraries/rush-lib/src/logic/operations/test/ShellOperationRunnerPlugin.test.ts b/libraries/rush-lib/src/logic/operations/test/ShellOperationRunnerPlugin.test.ts index 96fe059ee21..3e558acc906 100644 --- a/libraries/rush-lib/src/logic/operations/test/ShellOperationRunnerPlugin.test.ts +++ b/libraries/rush-lib/src/logic/operations/test/ShellOperationRunnerPlugin.test.ts @@ -1,17 +1,29 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import path from 'path'; +import path from 'node:path'; import { JsonFile } from '@rushstack/node-core-library'; +import { ConsoleTerminalProvider, Terminal } from '@rushstack/terminal'; +import { CommandLineAction, CommandLineParser, type CommandLineParameter } from '@rushstack/ts-command-line'; import { RushConfiguration } from '../../../api/RushConfiguration'; -import { CommandLineConfiguration, IPhasedCommandConfig } from '../../../api/CommandLineConfiguration'; -import { Operation } from '../Operation'; -import { ICommandLineJson } from '../../../api/CommandLineJson'; +import { + CommandLineConfiguration, + type IPhasedCommandConfig, + type IParameterJson, + type IPhase +} from '../../../api/CommandLineConfiguration'; +import type { Operation } from '../Operation'; +import type { ICommandLineJson } from '../../../api/CommandLineJson'; import { PhasedOperationPlugin } from '../PhasedOperationPlugin'; import { ShellOperationRunnerPlugin } from '../ShellOperationRunnerPlugin'; -import { ICreateOperationsContext, PhasedCommandHooks } from '../../../pluginFramework/PhasedCommandHooks'; -import { ShellOperationRunner } from '../ShellOperationRunner'; +import { + type ICreateOperationsContext, + PhasedCommandHooks +} from '../../../pluginFramework/PhasedCommandHooks'; +import { RushProjectConfiguration } from '../../../api/RushProjectConfiguration'; +import { defineCustomParameters } from '../../../cli/parsing/defineCustomParameters'; +import { associateParametersByPhase } from '../../../cli/parsing/associateParametersByPhase'; interface ISerializedOperation { name: string; @@ -20,11 +32,32 @@ interface ISerializedOperation { function serializeOperation(operation: Operation): ISerializedOperation { return { - name: operation.name!, - commandToRun: (operation.runner as ShellOperationRunner)['_commandToRun'] + name: operation.name, + commandToRun: operation.runner!.getConfigHash() }; } +/** + * Test implementation of CommandLineAction for testing parameter handling + */ +class TestCommandLineAction extends CommandLineAction { + protected async onExecuteAsync(): Promise { + // No-op for testing + } +} + +/** + * Test implementation of CommandLineParser for testing parameter handling + */ +class TestCommandLineParser extends CommandLineParser { + public constructor() { + super({ + toolFilename: 'test-tool', + toolDescription: 'Test tool for parameter parsing' + }); + } +} + describe(ShellOperationRunnerPlugin.name, () => { it('shellCommand "echo custom shellCommand" should be set to commandToRun', async () => { const rushJsonFile: string = path.resolve(__dirname, `../../test/customShellCommandinBulkRepo/rush.json`); @@ -44,12 +77,11 @@ describe(ShellOperationRunnerPlugin.name, () => { const fakeCreateOperationsContext: Pick< ICreateOperationsContext, - 'phaseOriginal' | 'phaseSelection' | 'projectSelection' | 'projectsInUnknownState' + 'phaseSelection' | 'projectSelection' | 'projectConfigurations' > = { - phaseOriginal: echoCommand.phases, phaseSelection: echoCommand.phases, projectSelection: new Set(rushConfiguration.projects), - projectsInUnknownState: new Set(rushConfiguration.projects) + projectConfigurations: new Map() }; const hooks: PhasedCommandHooks = new PhasedCommandHooks(); @@ -59,7 +91,7 @@ describe(ShellOperationRunnerPlugin.name, () => { // Applies the Shell Operation Runner to selected operations new ShellOperationRunnerPlugin().apply(hooks); - const operations: Set = await hooks.createOperations.promise( + const operations: Set = await hooks.createOperationsAsync.promise( new Set(), fakeCreateOperationsContext as ICreateOperationsContext ); @@ -87,12 +119,11 @@ describe(ShellOperationRunnerPlugin.name, () => { const fakeCreateOperationsContext: Pick< ICreateOperationsContext, - 'phaseOriginal' | 'phaseSelection' | 'projectSelection' | 'projectsInUnknownState' + 'phaseSelection' | 'projectSelection' | 'projectConfigurations' > = { - phaseOriginal: echoCommand.phases, phaseSelection: echoCommand.phases, projectSelection: new Set(rushConfiguration.projects), - projectsInUnknownState: new Set(rushConfiguration.projects) + projectConfigurations: new Map() }; const hooks: PhasedCommandHooks = new PhasedCommandHooks(); @@ -102,11 +133,130 @@ describe(ShellOperationRunnerPlugin.name, () => { // Applies the Shell Operation Runner to selected operations new ShellOperationRunnerPlugin().apply(hooks); - const operations: Set = await hooks.createOperations.promise( + const operations: Set = await hooks.createOperationsAsync.promise( new Set(), fakeCreateOperationsContext as ICreateOperationsContext ); // All projects expect(Array.from(operations, serializeOperation)).toMatchSnapshot(); }); + + it('parameters should be filtered when parameterNamesToIgnore is specified', async () => { + const rushJsonFile: string = path.resolve(__dirname, `../../test/parameterIgnoringRepo/rush.json`); + const commandLineJsonFile: string = path.resolve( + __dirname, + `../../test/parameterIgnoringRepo/common/config/rush/command-line.json` + ); + + const rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); + const commandLineJson: ICommandLineJson = JsonFile.load(commandLineJsonFile); + + const commandLineConfiguration = new CommandLineConfiguration(commandLineJson); + const buildCommand: IPhasedCommandConfig = commandLineConfiguration.commands.get( + 'build' + )! as IPhasedCommandConfig; + + // Load project configurations + const terminalProvider: ConsoleTerminalProvider = new ConsoleTerminalProvider(); + const terminal: Terminal = new Terminal(terminalProvider); + + const projectConfigurations = await RushProjectConfiguration.tryLoadForProjectsAsync( + rushConfiguration.projects, + terminal + ); + + // Create CommandLineParser and action to parse parameter values + const parser: TestCommandLineParser = new TestCommandLineParser(); + const action: TestCommandLineAction = new TestCommandLineAction({ + actionName: 'build', + summary: 'Test build action', + documentation: 'Test' + }); + parser.addAction(action); + + // Create CommandLineParameter instances from the parameter definitions + const customParametersMap: Map = new Map(); + defineCustomParameters(action, buildCommand.associatedParameters, customParametersMap); + + // Parse parameter values using the parser + await parser.executeWithoutErrorHandlingAsync([ + 'build', + '--production', + '--verbose', + '--config', + '/path/to/config.json', + '--mode', + 'prod', + '--tags', + 'tag1', + '--tags', + 'tag2' + ]); + + // Associate parameters with phases using the helper + // Create a map of phase names to phases for the helper + const phasesMap: Map = new Map(); + for (const phase of buildCommand.phases) { + phasesMap.set(phase.name, phase); + } + associateParametersByPhase(customParametersMap, phasesMap); + + // Create customParameters map for ICreateOperationsContext (keyed by longName) + const customParametersForContext: Map = new Map(); + for (const [param, cli] of customParametersMap) { + customParametersForContext.set(param.longName, cli); + } + + const fakeCreateOperationsContext: Pick< + ICreateOperationsContext, + | 'phaseSelection' + | 'projectSelection' + | 'projectConfigurations' + | 'rushConfiguration' + | 'customParameters' + > = { + phaseSelection: buildCommand.phases, + projectSelection: new Set(rushConfiguration.projects), + projectConfigurations, + rushConfiguration, + customParameters: customParametersForContext + }; + + const hooks: PhasedCommandHooks = new PhasedCommandHooks(); + + // Generates the default operation graph + new PhasedOperationPlugin().apply(hooks); + // Applies the Shell Operation Runner to selected operations + new ShellOperationRunnerPlugin().apply(hooks); + + const operations: Set = await hooks.createOperationsAsync.promise( + new Set(), + fakeCreateOperationsContext as unknown as ICreateOperationsContext + ); + + // Verify that project 'a' has the --production parameter filtered out + const operationA = Array.from(operations).find((op) => op.name === 'a'); + expect(operationA).toBeDefined(); + const commandHashA = operationA!.runner!.getConfigHash(); + // Should not contain --production but should contain other parameters + expect(commandHashA).not.toContain('--production'); + expect(commandHashA).toContain('--verbose'); + expect(commandHashA).toContain('--config'); + expect(commandHashA).toContain('--mode'); + expect(commandHashA).toContain('--tags'); + + // Verify that project 'b' has --verbose, --config, --mode, and --tags filtered out + const operationB = Array.from(operations).find((op) => op.name === 'b'); + expect(operationB).toBeDefined(); + const commandHashB = operationB!.runner!.getConfigHash(); + // Should contain --production but not the other parameters since they are filtered + expect(commandHashB).toContain('--production'); + expect(commandHashB).not.toContain('--verbose'); + expect(commandHashB).not.toContain('--config'); + expect(commandHashB).not.toContain('--mode'); + expect(commandHashB).not.toContain('--tags'); + + // All projects snapshot + expect(Array.from(operations, serializeOperation)).toMatchSnapshot(); + }); }); diff --git a/libraries/rush-lib/src/logic/operations/test/__snapshots__/AsyncOperationQueue.test.ts.snap b/libraries/rush-lib/src/logic/operations/test/__snapshots__/AsyncOperationQueue.test.ts.snap index 7a4c086efd4..fbb856f0f4b 100644 --- a/libraries/rush-lib/src/logic/operations/test/__snapshots__/AsyncOperationQueue.test.ts.snap +++ b/libraries/rush-lib/src/logic/operations/test/__snapshots__/AsyncOperationQueue.test.ts.snap @@ -1,11 +1,11 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`AsyncOperationQueue detects cyles 1`] = ` +exports[`AsyncOperationQueue detects cycles 1`] = ` "A cyclic dependency was encountered: a -> c -> d -> b -> a -Consider using the decoupledLocalDependencies option for rush.json." +Consider using the decoupledLocalDependencies option in rush.json." `; diff --git a/libraries/rush-lib/src/logic/operations/test/__snapshots__/BuildPlanPlugin.test.ts.snap b/libraries/rush-lib/src/logic/operations/test/__snapshots__/BuildPlanPlugin.test.ts.snap new file mode 100644 index 00000000000..39e0821fce0 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/test/__snapshots__/BuildPlanPlugin.test.ts.snap @@ -0,0 +1,326 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`BuildPlanPlugin build plan debugging should generate a build plan 1`] = ` +Array [ + "[ log] Build Plan Depth (deepest dependency tree): 5", + "[ log] Build Plan Width (maximum parallelism): 38", + "[ log] Number of Nodes per Depth: 22, 38, 33, 11, 1", + "[ log] Plan @ Depth 0 has 22 nodes and 0 dependents:", + "[ log] - a (upstream-3)", + "[ log] - a (no-deps)", + "[ log] - a (upstream-1)", + "[ log] - a (upstream-1-self-upstream)", + "[ log] - a (upstream-2)", + "[ log] - b (no-deps)", + "[ log] - c (no-deps)", + "[ log] - d (no-deps)", + "[ log] - e (no-deps)", + "[ log] - f (no-deps)", + "[ log] - g (no-deps)", + "[ log] - h (no-deps)", + "[ log] - i (upstream-3)", + "[ log] - i (no-deps)", + "[ log] - i (upstream-1)", + "[ log] - i (upstream-1-self-upstream)", + "[ log] - i (upstream-2)", + "[ log] - j (upstream-3)", + "[ log] - j (no-deps)", + "[ log] - j (upstream-1)", + "[ log] - j (upstream-1-self-upstream)", + "[ log] - j (upstream-2)", + "[ log] Plan @ Depth 1 has 38 nodes and 22 dependents:", + "[ log] - a (complex)", + "[ log] - a (upstream-self)", + "[ log] - b (upstream-1)", + "[ log] - f (upstream-1)", + "[ log] - g (upstream-1)", + "[ log] - h (upstream-1)", + "[ log] - b (upstream-2)", + "[ log] - f (upstream-2)", + "[ log] - g (upstream-2)", + "[ log] - h (upstream-2)", + "[ log] - a (upstream-1-self)", + "[ log] - b (complex)", + "[ log] - f (complex)", + "[ log] - g (complex)", + "[ log] - h (complex)", + "[ log] - b (upstream-3)", + "[ log] - f (upstream-3)", + "[ log] - g (upstream-3)", + "[ log] - h (upstream-3)", + "[ log] - a (upstream-2-self)", + "[ log] - b (upstream-self)", + "[ log] - c (upstream-1)", + "[ log] - d (upstream-1)", + "[ log] - c (upstream-self)", + "[ log] - e (upstream-1)", + "[ log] - d (upstream-self)", + "[ log] - e (upstream-self)", + "[ log] - f (upstream-self)", + "[ log] - g (upstream-self)", + "[ log] - h (upstream-self)", + "[ log] - i (complex)", + "[ log] - i (upstream-self)", + "[ log] - i (upstream-1-self)", + "[ log] - i (upstream-2-self)", + "[ log] - j (complex)", + "[ log] - j (upstream-self)", + "[ log] - j (upstream-1-self)", + "[ log] - j (upstream-2-self)", + "[ log] Plan @ Depth 2 has 33 nodes and 60 dependents:", + "[ log] - b (upstream-self)", + "[ log] - f (upstream-self)", + "[ log] - h (upstream-self)", + "[ log] - g (upstream-self)", + "[ log] - c (upstream-2)", + "[ log] - d (upstream-2)", + "[ log] - b (upstream-1-self)", + "[ log] - f (upstream-1-self)", + "[ log] - g (upstream-1-self)", + "[ log] - f (upstream-2)", + "[ log] - h (upstream-1-self)", + "[ log] - c (upstream-3)", + "[ log] - d (upstream-3)", + "[ log] - b (upstream-2-self)", + "[ log] - f (upstream-2-self)", + "[ log] - g (upstream-2-self)", + "[ log] - f (upstream-3)", + "[ log] - h (upstream-2-self)", + "[ log] - b (upstream-1-self-upstream)", + "[ log] - f (upstream-1-self-upstream)", + "[ log] - g (upstream-1-self-upstream)", + "[ log] - h (upstream-1-self-upstream)", + "[ log] - b (complex)", + "[ log] - f (complex)", + "[ log] - g (complex)", + "[ log] - h (complex)", + "[ log] - c (upstream-self)", + "[ log] - d (upstream-self)", + "[ log] - e (upstream-2)", + "[ log] - c (upstream-1-self)", + "[ log] - d (upstream-1-self)", + "[ log] - e (upstream-self)", + "[ log] - e (upstream-1-self)", + "[ log] Plan @ Depth 3 has 11 nodes and 93 dependents:", + "[ log] - e (upstream-3)", + "[ log] - c (upstream-2-self)", + "[ log] - d (upstream-2-self)", + "[ log] - c (upstream-1-self-upstream)", + "[ log] - d (upstream-1-self-upstream)", + "[ log] - f (upstream-1-self-upstream)", + "[ log] - c (complex)", + "[ log] - d (complex)", + "[ log] - f (complex)", + "[ log] - e (upstream-2-self)", + "[ log] - e (upstream-1-self-upstream)", + "[ log] Plan @ Depth 4 has 1 nodes and 104 dependents:", + "[ log] - e (complex)", + "[ log] ##################################################", + "[ log] a (complex): (0)", + "[ log] a (upstream-3): (0)", + "[ log] a (no-deps): (1)", + "[ log] a (upstream-1): (1)", + "[ log] a (upstream-1-self-upstream): (1)", + "[ log] a (upstream-2): (1)", + "[ log] b (no-deps): (1)", + "[ log] c (no-deps): (1)", + "[ log] d (no-deps): (1)", + "[ log] e (no-deps): (1)", + "[ log] f (no-deps): (1)", + "[ log] g (no-deps): (1)", + "[ log] h (no-deps): (1)", + "[ log] i (complex): (2)", + "[ log] i (upstream-3): (2)", + "[ log] i (no-deps): (3)", + "[ log] i (upstream-1): (4)", + "[ log] i (upstream-1-self-upstream): (5)", + "[ log] i (upstream-2): (6)", + "[ log] j (complex): (7)", + "[ log] j (upstream-3): (7)", + "[ log] j (no-deps): (8)", + "[ log] j (upstream-1): (9)", + "[ log] j (upstream-1-self-upstream): (10)", + "[ log] j (upstream-2): (11)", + "[ log] a (upstream-1-self): -(1)", + "[ log] a (upstream-2-self): -(1)", + "[ log] a (upstream-self): -(1)", + "[ log] b (upstream-1): -(1)", + "[ log] b (upstream-2): -(1)", + "[ log] b (upstream-3): -(1)", + "[ log] c (upstream-1): -(1)", + "[ log] d (upstream-1): -(1)", + "[ log] e (upstream-1): -(1)", + "[ log] f (upstream-1): -(1)", + "[ log] f (upstream-2): -(1)", + "[ log] f (upstream-3): -(1)", + "[ log] g (upstream-1): -(1)", + "[ log] g (upstream-2): -(1)", + "[ log] g (upstream-3): -(1)", + "[ log] h (upstream-1): -(1)", + "[ log] h (upstream-2): -(1)", + "[ log] h (upstream-3): -(1)", + "[ log] i (upstream-self): -(3)", + "[ log] i (upstream-1-self): -(4)", + "[ log] i (upstream-2-self): -(6)", + "[ log] j (upstream-self): -(8)", + "[ log] j (upstream-1-self): -(9)", + "[ log] j (upstream-2-self): -(11)", + "[ log] b (complex): --(1)", + "[ log] b (upstream-1-self): --(1)", + "[ log] b (upstream-1-self-upstream): --(1)", + "[ log] b (upstream-2-self): --(1)", + "[ log] b (upstream-self): --(1)", + "[ log] c (upstream-1-self): --(1)", + "[ log] c (upstream-2): --(1)", + "[ log] c (upstream-3): --(1)", + "[ log] d (upstream-1-self): --(1)", + "[ log] d (upstream-2): --(1)", + "[ log] d (upstream-3): --(1)", + "[ log] e (upstream-1-self): --(1)", + "[ log] e (upstream-2): --(1)", + "[ log] f (complex): --(1)", + "[ log] f (upstream-1-self): --(1)", + "[ log] f (upstream-1-self-upstream): --(1)", + "[ log] f (upstream-2-self): --(1)", + "[ log] f (upstream-self): --(1)", + "[ log] g (complex): --(1)", + "[ log] g (upstream-1-self): --(1)", + "[ log] g (upstream-1-self-upstream): --(1)", + "[ log] g (upstream-2-self): --(1)", + "[ log] g (upstream-self): --(1)", + "[ log] h (complex): --(1)", + "[ log] h (upstream-1-self): --(1)", + "[ log] h (upstream-1-self-upstream): --(1)", + "[ log] h (upstream-2-self): --(1)", + "[ log] h (upstream-self): --(1)", + "[ log] c (complex): ---(1)", + "[ log] c (upstream-1-self-upstream): ---(1)", + "[ log] c (upstream-2-self): ---(1)", + "[ log] c (upstream-self): ---(1)", + "[ log] d (complex): ---(1)", + "[ log] d (upstream-1-self-upstream): ---(1)", + "[ log] d (upstream-2-self): ---(1)", + "[ log] d (upstream-self): ---(1)", + "[ log] e (upstream-1-self-upstream): ---(1)", + "[ log] e (upstream-2-self): ---(1)", + "[ log] e (upstream-3): ---(1)", + "[ log] e (complex): ----(1)", + "[ log] e (upstream-self): ----(1)", + "[ log] ##################################################", + "[ log] Cluster 0:", + "[ log] - Dependencies: none", + "[ log] - Clustered by: ", + "[ log] - (a (upstream-3)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - Operations: a (complex), a (upstream-3)", + "[ log] --------------------------------------------------", + "[ log] Cluster 1:", + "[ log] - Dependencies: none", + "[ log] - Clustered by: ", + "[ log] - (a (upstream-1)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (a (upstream-2)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (a (no-deps)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (b (upstream-3)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (a (upstream-1-self-upstream)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (a (upstream-2-self)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (b (upstream-1)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (a (upstream-1-self)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (b (upstream-2)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (b (no-deps)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (a (upstream-self)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (c (upstream-3)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (b (upstream-1-self-upstream)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (b (upstream-2-self)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (c (upstream-1)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (b (upstream-1-self)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (c (upstream-2)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (c (no-deps)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (b (upstream-self)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (d (upstream-3)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (d (upstream-1)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (d (upstream-2)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (d (no-deps)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (e (upstream-3)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (c (upstream-1-self-upstream)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (c (upstream-2-self)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (e (upstream-1)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (c (upstream-1-self)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (e (upstream-2)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (e (no-deps)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (c (upstream-self)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (f (upstream-3)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (h (upstream-1-self-upstream)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (h (upstream-2-self)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (h (no-deps)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (f (upstream-1)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (h (upstream-1-self)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (h (upstream-1)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (f (upstream-2)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (h (upstream-2)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (f (no-deps)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (h (upstream-self)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (g (upstream-3)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (g (upstream-1)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (g (upstream-2)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (g (no-deps)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - (h (upstream-3)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - Operations: a (no-deps), a (upstream-1), a (upstream-1-self), a (upstream-1-self-upstream), a (upstream-2), a (upstream-2-self), a (upstream-self), b (complex), b (no-deps), b (upstream-1), b (upstream-1-self), b (upstream-1-self-upstream), b (upstream-2), b (upstream-2-self), b (upstream-3), b (upstream-self), c (complex), c (no-deps), c (upstream-1), c (upstream-1-self), c (upstream-1-self-upstream), c (upstream-2), c (upstream-2-self), c (upstream-3), c (upstream-self), d (complex), d (no-deps), d (upstream-1), d (upstream-1-self), d (upstream-1-self-upstream), d (upstream-2), d (upstream-2-self), d (upstream-3), d (upstream-self), e (complex), e (no-deps), e (upstream-1), e (upstream-1-self), e (upstream-1-self-upstream), e (upstream-2), e (upstream-2-self), e (upstream-3), e (upstream-self), f (complex), f (no-deps), f (upstream-1), f (upstream-1-self), f (upstream-1-self-upstream), f (upstream-2), f (upstream-2-self), f (upstream-3), f (upstream-self), g (complex), g (no-deps), g (upstream-1), g (upstream-1-self), g (upstream-1-self-upstream), g (upstream-2), g (upstream-2-self), g (upstream-3), g (upstream-self), h (complex), h (no-deps), h (upstream-1), h (upstream-1-self), h (upstream-1-self-upstream), h (upstream-2), h (upstream-2-self), h (upstream-3), h (upstream-self)", + "[ log] --------------------------------------------------", + "[ log] Cluster 2:", + "[ log] - Dependencies: none", + "[ log] - Clustered by: ", + "[ log] - (i (upstream-3)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - Operations: i (complex), i (upstream-3)", + "[ log] --------------------------------------------------", + "[ log] Cluster 3:", + "[ log] - Dependencies: none", + "[ log] - Clustered by: ", + "[ log] - (i (no-deps)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - Operations: i (no-deps), i (upstream-self)", + "[ log] --------------------------------------------------", + "[ log] Cluster 4:", + "[ log] - Dependencies: none", + "[ log] - Clustered by: ", + "[ log] - (i (upstream-1)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - Operations: i (upstream-1), i (upstream-1-self)", + "[ log] --------------------------------------------------", + "[ log] Cluster 5:", + "[ log] - Dependencies: none", + "[ log] - Operations: i (upstream-1-self-upstream)", + "[ log] --------------------------------------------------", + "[ log] Cluster 6:", + "[ log] - Dependencies: none", + "[ log] - Clustered by: ", + "[ log] - (i (upstream-2)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - Operations: i (upstream-2), i (upstream-2-self)", + "[ log] --------------------------------------------------", + "[ log] Cluster 7:", + "[ log] - Dependencies: none", + "[ log] - Clustered by: ", + "[ log] - (j (upstream-3)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - Operations: j (complex), j (upstream-3)", + "[ log] --------------------------------------------------", + "[ log] Cluster 8:", + "[ log] - Dependencies: none", + "[ log] - Clustered by: ", + "[ log] - (j (no-deps)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - Operations: j (no-deps), j (upstream-self)", + "[ log] --------------------------------------------------", + "[ log] Cluster 9:", + "[ log] - Dependencies: none", + "[ log] - Clustered by: ", + "[ log] - (j (upstream-1)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - Operations: j (upstream-1), j (upstream-1-self)", + "[ log] --------------------------------------------------", + "[ log] Cluster 10:", + "[ log] - Dependencies: none", + "[ log] - Operations: j (upstream-1-self-upstream)", + "[ log] --------------------------------------------------", + "[ log] Cluster 11:", + "[ log] - Dependencies: none", + "[ log] - Clustered by: ", + "[ log] - (j (upstream-2)) \\"Project does not have a rush-project.json configuration file, or one provided by a rig, so it does not support caching.\\"", + "[ log] - Operations: j (upstream-2), j (upstream-2-self)", + "[ log] --------------------------------------------------", + "[ log] ##################################################", +] +`; diff --git a/libraries/rush-lib/src/logic/operations/test/__snapshots__/OperationExecutionManager.test.ts.snap b/libraries/rush-lib/src/logic/operations/test/__snapshots__/OperationExecutionManager.test.ts.snap deleted file mode 100644 index 72414d79905..00000000000 --- a/libraries/rush-lib/src/logic/operations/test/__snapshots__/OperationExecutionManager.test.ts.snap +++ /dev/null @@ -1,474 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`OperationExecutionManager Error logging printedStderrAfterError 1`] = ` -Array [ - Object { - "kind": "O", - "text": "Selected 1 operation: -", - }, - Object { - "kind": "O", - "text": " stdout+stderr -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": "Executing a maximum of 1 simultaneous processes... -", - }, - Object { - "kind": "O", - "text": " -[gray]==[[default] [cyan]stdout+stderr[default] [gray]]================================================[[default] [white]1 of 1[default] [gray]]==[default] -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": "Build step 1 - -", - }, - Object { - "kind": "E", - "text": "Error: step 1 failed - -", - }, - Object { - "kind": "E", - "text": "[red]\\"stdout+stderr\\" failed to build.[default] -", - }, - Object { - "kind": "O", - "text": " - - -", - }, - Object { - "kind": "O", - "text": "[gray]==[[default] [red]FAILURE: 1 operation[default] [gray]]=====================================================[default] - -", - }, - Object { - "kind": "O", - "text": "[gray]--[[default] [red]FAILURE: stdout+stderr[default] [gray]]---------------------------------[[default] [white]0.10 seconds[default] [gray]]--[default] - -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "E", - "text": "[red]Operations failed. -[default] -", - }, -] -`; - -exports[`OperationExecutionManager Error logging printedStdoutAfterErrorWithEmptyStderr 1`] = ` -Array [ - Object { - "kind": "O", - "text": "Selected 1 operation: -", - }, - Object { - "kind": "O", - "text": " stdout only -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": "Executing a maximum of 1 simultaneous processes... -", - }, - Object { - "kind": "O", - "text": " -[gray]==[[default] [cyan]stdout only[default] [gray]]==================================================[[default] [white]1 of 1[default] [gray]]==[default] -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": "Build step 1 - -", - }, - Object { - "kind": "O", - "text": "Error: step 1 failed - -", - }, - Object { - "kind": "E", - "text": "[red]\\"stdout only\\" failed to build.[default] -", - }, - Object { - "kind": "O", - "text": " - - -", - }, - Object { - "kind": "O", - "text": "[gray]==[[default] [red]FAILURE: 1 operation[default] [gray]]=====================================================[default] - -", - }, - Object { - "kind": "O", - "text": "[gray]--[[default] [red]FAILURE: stdout only[default] [gray]]-----------------------------------[[default] [white]0.10 seconds[default] [gray]]--[default] - -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "E", - "text": "[red]Operations failed. -[default] -", - }, -] -`; - -exports[`OperationExecutionManager Warning logging Fail on warning Logs warnings correctly 1`] = ` -Array [ - Object { - "kind": "O", - "text": "Selected 1 operation: -", - }, - Object { - "kind": "O", - "text": " success with warnings (failure) -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": "Executing a maximum of 1 simultaneous processes... -", - }, - Object { - "kind": "O", - "text": " -[gray]==[[default] [cyan]success with warnings (failure)[default] [gray]]==============================[[default] [white]1 of 1[default] [gray]]==[default] -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": "Build step 1 - -", - }, - Object { - "kind": "O", - "text": "Warning: step 1 succeeded with warnings - -", - }, - Object { - "kind": "E", - "text": "[yellow]\\"success with warnings (failure)\\" completed with warnings in 0.10 seconds.[default] -", - }, - Object { - "kind": "O", - "text": " - - -", - }, - Object { - "kind": "O", - "text": "[gray]==[[default] [yellow]SUCCESS WITH WARNINGS: 1 operation[default] [gray]]=======================================[default] - -", - }, - Object { - "kind": "O", - "text": "[gray]--[[default] [yellow]WARNING: success with warnings (failure)[default] [gray]]---------------[[default] [white]0.20 seconds[default] [gray]]--[default] - -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "E", - "text": "[yellow]Operations succeeded with warnings. -[default] -", - }, -] -`; - -exports[`OperationExecutionManager Warning logging Success on warning Logs warnings correctly 1`] = ` -Array [ - Object { - "kind": "O", - "text": "Selected 1 operation: -", - }, - Object { - "kind": "O", - "text": " success with warnings (success) -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": "Executing a maximum of 1 simultaneous processes... -", - }, - Object { - "kind": "O", - "text": " -[gray]==[[default] [cyan]success with warnings (success)[default] [gray]]==============================[[default] [white]1 of 1[default] [gray]]==[default] -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": "Build step 1 - -", - }, - Object { - "kind": "O", - "text": "Warning: step 1 succeeded with warnings - -", - }, - Object { - "kind": "E", - "text": "[yellow]\\"success with warnings (success)\\" completed with warnings in 0.10 seconds.[default] -", - }, - Object { - "kind": "O", - "text": " - - -", - }, - Object { - "kind": "O", - "text": "[gray]==[[default] [yellow]SUCCESS WITH WARNINGS: 1 operation[default] [gray]]=======================================[default] - -", - }, - Object { - "kind": "O", - "text": "[gray]--[[default] [yellow]WARNING: success with warnings (success)[default] [gray]]---------------[[default] [white]0.20 seconds[default] [gray]]--[default] - -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": " -", - }, -] -`; - -exports[`OperationExecutionManager Warning logging Success on warning logs warnings correctly with --timeline option 1`] = ` -Array [ - Object { - "kind": "O", - "text": "Selected 1 operation: -", - }, - Object { - "kind": "O", - "text": " success with warnings (success) -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": "Executing a maximum of 1 simultaneous processes... -", - }, - Object { - "kind": "O", - "text": " -[gray]==[[default] [cyan]success with warnings (success)[default] [gray]]==============================[[default] [white]1 of 1[default] [gray]]==[default] -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": "Build step 1 - -", - }, - Object { - "kind": "O", - "text": "Warning: step 1 succeeded with warnings - -", - }, - Object { - "kind": "E", - "text": "[yellow]\\"success with warnings (success)\\" completed with warnings in 0.10 seconds.[default] -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": "========================================================================================== -", - }, - Object { - "kind": "O", - "text": "[cyan]success with warnings (success)[default] [yellow]!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!![default] [white]0.2s[default] -", - }, - Object { - "kind": "O", - "text": "========================================================================================== -", - }, - Object { - "kind": "O", - "text": "LEGEND: Total Work: 0.2s -", - }, - Object { - "kind": "O", - "text": " [#] Success [!] Failed/warnings [%] Skipped/cached/no-op Wall Clock: 0.2s -", - }, - Object { - "kind": "O", - "text": " Max Parallelism Used: 1 -", - }, - Object { - "kind": "O", - "text": " Avg Parallelism Used: 1.0 -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": " - - -", - }, - Object { - "kind": "O", - "text": "[gray]==[[default] [yellow]SUCCESS WITH WARNINGS: 1 operation[default] [gray]]=======================================[default] - -", - }, - Object { - "kind": "O", - "text": "[gray]--[[default] [yellow]WARNING: success with warnings (success)[default] [gray]]---------------[[default] [white]0.20 seconds[default] [gray]]--[default] - -", - }, - Object { - "kind": "O", - "text": " -", - }, - Object { - "kind": "O", - "text": " -", - }, -] -`; diff --git a/libraries/rush-lib/src/logic/operations/test/__snapshots__/OperationGraph.test.ts.snap b/libraries/rush-lib/src/logic/operations/test/__snapshots__/OperationGraph.test.ts.snap new file mode 100644 index 00000000000..11cd4061cef --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/test/__snapshots__/OperationGraph.test.ts.snap @@ -0,0 +1,836 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`OperationGraph Cobuild logging logs cobuilt operations correctly with --timeline option 1`] = ` +Array [ + Object { + "kind": "O", + "text": "Selected 2 operations: +", + }, + Object { + "kind": "O", + "text": " operation (success) +", + }, + Object { + "kind": "O", + "text": " operation2 (success) +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "Executing a maximum of 1 simultaneous processes... +", + }, + Object { + "kind": "O", + "text": " +[gray]==[[default] [cyan]operation2 (success)[default] [gray]]=========================================[[default] [white]1 of 2[default] [gray]]==[default] +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "[green]\\"operation2 (success)\\" completed successfully in 15.00 seconds.[default] +", + }, + Object { + "kind": "O", + "text": " +[gray]==[[default] [cyan]operation (success)[default] [gray]]==========================================[[default] [white]2 of 2[default] [gray]]==[default] +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "[green]\\"operation (success)\\" completed successfully in 15.00 seconds.[default] +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "========================================================================================== +", + }, + Object { + "kind": "O", + "text": "[cyan]operation2 (success)[default] [gray][default][green]CCCCCCCCCCCCCCCCCCCCCCCCCCC[default][gray]------------------------------------[default] [white]15.0s[default] +", + }, + Object { + "kind": "O", + "text": "[cyan] operation (success)[default] [gray]-----------------------------------[default][green]CCCCCCCCCCCCCCCCCCCCCCCCCCCC[default][gray][default] [white]15.0s[default] +", + }, + Object { + "kind": "O", + "text": "========================================================================================== +", + }, + Object { + "kind": "O", + "text": "LEGEND: Total Work: 20.0s +", + }, + Object { + "kind": "O", + "text": " [#] Success [!] Failed/warnings [%] Skipped/cached/no-op Wall Clock: 35.0s +", + }, + Object { + "kind": "O", + "text": " [C] Cobuild Max Parallelism Used: 1 +", + }, + Object { + "kind": "O", + "text": " Avg Parallelism Used: 0.6 +", + }, + Object { + "kind": "O", + "text": "BY PHASE: +", + }, + Object { + "kind": "O", + "text": " [cyan] my-name[default] 30.0s, from cache: 20.0s +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": " + + +", + }, + Object { + "kind": "O", + "text": "[gray]==[[default] [green]SUCCESS: 2 operations[default] [gray]]====================================================[default] + +", + }, + Object { + "kind": "O", + "text": "These operations completed successfully: +", + }, + Object { + "kind": "O", + "text": " operation (success) 15.00 seconds (restore 10000.0ms) +", + }, + Object { + "kind": "O", + "text": " operation2 (success) 15.00 seconds (restore 10000.0ms) +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": " +", + }, +] +`; + +exports[`OperationGraph Cobuild logging logs warnings correctly with --timeline option 1`] = ` +Array [ + Object { + "kind": "O", + "text": "Selected 2 operations: +", + }, + Object { + "kind": "O", + "text": " operation (success with warnings) +", + }, + Object { + "kind": "O", + "text": " operation2 (success with warnings) +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "Executing a maximum of 1 simultaneous processes... +", + }, + Object { + "kind": "O", + "text": " +[gray]==[[default] [cyan]operation2 (success with warnings)[default] [gray]]===========================[[default] [white]1 of 2[default] [gray]]==[default] +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "Build step 1 + +", + }, + Object { + "kind": "O", + "text": "Warning: step 1 succeeded with warnings + +", + }, + Object { + "kind": "E", + "text": "[yellow]\\"operation2 (success with warnings)\\" completed with warnings in 15.00 seconds.[default] +", + }, + Object { + "kind": "O", + "text": " +[gray]==[[default] [cyan]operation (success with warnings)[default] [gray]]============================[[default] [white]2 of 2[default] [gray]]==[default] +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "Build step 1 + +", + }, + Object { + "kind": "O", + "text": "Warning: step 1 succeeded with warnings + +", + }, + Object { + "kind": "E", + "text": "[yellow]\\"operation (success with warnings)\\" completed with warnings in 15.00 seconds.[default] +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "========================================================================================== +", + }, + Object { + "kind": "O", + "text": "[cyan]operation2 (success with warnings)[default] [gray][default][yellow]CCCCCCCCCCCCCCCCCCCCC[default][gray]----------------------------[default] [white]15.0s[default] +", + }, + Object { + "kind": "O", + "text": "[cyan] operation (success with warnings)[default] [gray]---------------------------[default][yellow]CCCCCCCCCCCCCCCCCCCCCC[default][gray][default] [white]15.0s[default] +", + }, + Object { + "kind": "O", + "text": "========================================================================================== +", + }, + Object { + "kind": "O", + "text": "LEGEND: Total Work: 20.0s +", + }, + Object { + "kind": "O", + "text": " [#] Success [!] Failed/warnings [%] Skipped/cached/no-op Wall Clock: 35.0s +", + }, + Object { + "kind": "O", + "text": " [C] Cobuild Max Parallelism Used: 1 +", + }, + Object { + "kind": "O", + "text": " Avg Parallelism Used: 0.6 +", + }, + Object { + "kind": "O", + "text": "BY PHASE: +", + }, + Object { + "kind": "O", + "text": " [cyan] my-name[default] 30.0s, from cache: 20.0s +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": " + + +", + }, + Object { + "kind": "O", + "text": "[gray]==[[default] [yellow]SUCCESS WITH WARNINGS: 2 operations[default] [gray]]======================================[default] + +", + }, + Object { + "kind": "O", + "text": "[gray]--[[default] [yellow]WARNING: operation (success with warnings)[default] [gray]]------------[[default] [white]15.00 seconds[default] [gray]]--[default] + +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "[gray]--[[default] [yellow]WARNING: operation2 (success with warnings)[default] [gray]]-----------[[default] [white]15.00 seconds[default] [gray]]--[default] + +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "E", + "text": "[yellow]Operations succeeded with warnings. +[default] +", + }, +] +`; + +exports[`OperationGraph Error logging printedStderrAfterError 1`] = ` +Array [ + Object { + "kind": "O", + "text": "Selected 1 operation: +", + }, + Object { + "kind": "O", + "text": " stdout+stderr +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "Executing a maximum of 1 simultaneous processes... +", + }, + Object { + "kind": "O", + "text": " +[gray]==[[default] [cyan]stdout+stderr[default] [gray]]================================================[[default] [white]1 of 1[default] [gray]]==[default] +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "Build step 1 + +", + }, + Object { + "kind": "E", + "text": "Error: step 1 failed + +", + }, + Object { + "kind": "E", + "text": "[red]\\"stdout+stderr\\" failed to build.[default] +", + }, + Object { + "kind": "O", + "text": " + + +", + }, + Object { + "kind": "O", + "text": "[gray]==[[default] [red]FAILURE: 1 operation[default] [gray]]=====================================================[default] + +", + }, + Object { + "kind": "O", + "text": "[gray]--[[default] [red]FAILURE: stdout+stderr[default] [gray]]---------------------------------[[default] [white]0.10 seconds[default] [gray]]--[default] + +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "E", + "text": "[red]Operations failed. +[default] +", + }, +] +`; + +exports[`OperationGraph Error logging printedStdoutAfterErrorWithEmptyStderr 1`] = ` +Array [ + Object { + "kind": "O", + "text": "Selected 1 operation: +", + }, + Object { + "kind": "O", + "text": " stdout only +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "Executing a maximum of 1 simultaneous processes... +", + }, + Object { + "kind": "O", + "text": " +[gray]==[[default] [cyan]stdout only[default] [gray]]==================================================[[default] [white]1 of 1[default] [gray]]==[default] +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "Build step 1 + +", + }, + Object { + "kind": "O", + "text": "Error: step 1 failed + +", + }, + Object { + "kind": "E", + "text": "[red]\\"stdout only\\" failed to build.[default] +", + }, + Object { + "kind": "O", + "text": " + + +", + }, + Object { + "kind": "O", + "text": "[gray]==[[default] [red]FAILURE: 1 operation[default] [gray]]=====================================================[default] + +", + }, + Object { + "kind": "O", + "text": "[gray]--[[default] [red]FAILURE: stdout only[default] [gray]]-----------------------------------[[default] [white]0.10 seconds[default] [gray]]--[default] + +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "E", + "text": "[red]Operations failed. +[default] +", + }, +] +`; + +exports[`OperationGraph Warning logging Fail on warning Logs warnings correctly 1`] = ` +Array [ + Object { + "kind": "O", + "text": "Selected 1 operation: +", + }, + Object { + "kind": "O", + "text": " success with warnings (failure) +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "Executing a maximum of 1 simultaneous processes... +", + }, + Object { + "kind": "O", + "text": " +[gray]==[[default] [cyan]success with warnings (failure)[default] [gray]]==============================[[default] [white]1 of 1[default] [gray]]==[default] +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "Build step 1 + +", + }, + Object { + "kind": "O", + "text": "Warning: step 1 succeeded with warnings + +", + }, + Object { + "kind": "E", + "text": "[yellow]\\"success with warnings (failure)\\" completed with warnings in 0.10 seconds.[default] +", + }, + Object { + "kind": "O", + "text": " + + +", + }, + Object { + "kind": "O", + "text": "[gray]==[[default] [yellow]SUCCESS WITH WARNINGS: 1 operation[default] [gray]]=======================================[default] + +", + }, + Object { + "kind": "O", + "text": "[gray]--[[default] [yellow]WARNING: success with warnings (failure)[default] [gray]]---------------[[default] [white]0.10 seconds[default] [gray]]--[default] + +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "E", + "text": "[yellow]Operations succeeded with warnings. +[default] +", + }, +] +`; + +exports[`OperationGraph Warning logging Success on warning Logs warnings correctly 1`] = ` +Array [ + Object { + "kind": "O", + "text": "Selected 1 operation: +", + }, + Object { + "kind": "O", + "text": " success with warnings (success) +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "Executing a maximum of 1 simultaneous processes... +", + }, + Object { + "kind": "O", + "text": " +[gray]==[[default] [cyan]success with warnings (success)[default] [gray]]==============================[[default] [white]1 of 1[default] [gray]]==[default] +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "Build step 1 + +", + }, + Object { + "kind": "O", + "text": "Warning: step 1 succeeded with warnings + +", + }, + Object { + "kind": "E", + "text": "[yellow]\\"success with warnings (success)\\" completed with warnings in 0.10 seconds.[default] +", + }, + Object { + "kind": "O", + "text": " + + +", + }, + Object { + "kind": "O", + "text": "[gray]==[[default] [yellow]SUCCESS WITH WARNINGS: 1 operation[default] [gray]]=======================================[default] + +", + }, + Object { + "kind": "O", + "text": "[gray]--[[default] [yellow]WARNING: success with warnings (success)[default] [gray]]---------------[[default] [white]0.10 seconds[default] [gray]]--[default] + +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": " +", + }, +] +`; + +exports[`OperationGraph Warning logging Success on warning logs warnings correctly with --timeline option 1`] = ` +Array [ + Object { + "kind": "O", + "text": "Selected 1 operation: +", + }, + Object { + "kind": "O", + "text": " success with warnings (success) +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "Executing a maximum of 1 simultaneous processes... +", + }, + Object { + "kind": "O", + "text": " +[gray]==[[default] [cyan]success with warnings (success)[default] [gray]]==============================[[default] [white]1 of 1[default] [gray]]==[default] +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "Build step 1 + +", + }, + Object { + "kind": "O", + "text": "Warning: step 1 succeeded with warnings + +", + }, + Object { + "kind": "E", + "text": "[yellow]\\"success with warnings (success)\\" completed with warnings in 0.10 seconds.[default] +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": "========================================================================================== +", + }, + Object { + "kind": "O", + "text": "[cyan]success with warnings (success)[default] [gray][default][yellow]!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!![default][gray][default] [white]0.1s[default] +", + }, + Object { + "kind": "O", + "text": "========================================================================================== +", + }, + Object { + "kind": "O", + "text": "LEGEND: Total Work: 0.1s +", + }, + Object { + "kind": "O", + "text": " [#] Success [!] Failed/warnings [%] Skipped/cached/no-op Wall Clock: 0.1s +", + }, + Object { + "kind": "O", + "text": " Max Parallelism Used: 1 +", + }, + Object { + "kind": "O", + "text": " Avg Parallelism Used: 1.0 +", + }, + Object { + "kind": "O", + "text": "BY PHASE: +", + }, + Object { + "kind": "O", + "text": " [cyan] phase[default] 0.1s +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": " + + +", + }, + Object { + "kind": "O", + "text": "[gray]==[[default] [yellow]SUCCESS WITH WARNINGS: 1 operation[default] [gray]]=======================================[default] + +", + }, + Object { + "kind": "O", + "text": "[gray]--[[default] [yellow]WARNING: success with warnings (success)[default] [gray]]---------------[[default] [white]0.10 seconds[default] [gray]]--[default] + +", + }, + Object { + "kind": "O", + "text": " +", + }, + Object { + "kind": "O", + "text": " +", + }, +] +`; diff --git a/libraries/rush-lib/src/logic/operations/test/__snapshots__/OperationMetadataManager.test.ts.snap b/libraries/rush-lib/src/logic/operations/test/__snapshots__/OperationMetadataManager.test.ts.snap new file mode 100644 index 00000000000..fa2d79cf8af --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/test/__snapshots__/OperationMetadataManager.test.ts.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`OperationMetadataManager should fallback to the log file when chunked output isn't available 1`] = ` +Array [ + Object { + "kind": "O", + "text": "stdout log file", + }, +] +`; + +exports[`OperationMetadataManager should restore chunked stderr 1`] = ` +Array [ + "[ error] chunk1[n]", + "[ error] chunk2[n]", +] +`; + +exports[`OperationMetadataManager should restore chunked stdout 1`] = ` +Array [ + "[ log] chunk1[n]", + "[ log] chunk2[n]", +] +`; + +exports[`OperationMetadataManager should restore mixed chunked output 1`] = ` +Array [ + "[ log] logged to stdout[n]", + "[ error] logged to stderr[n]", +] +`; diff --git a/libraries/rush-lib/src/logic/operations/test/__snapshots__/ParseParallelism.test.ts.snap b/libraries/rush-lib/src/logic/operations/test/__snapshots__/ParseParallelism.test.ts.snap new file mode 100644 index 00000000000..9b1b825c540 --- /dev/null +++ b/libraries/rush-lib/src/logic/operations/test/__snapshots__/ParseParallelism.test.ts.snap @@ -0,0 +1,5 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`parseParallelism throwsErrorOnInvalidParallelism 1`] = `"Invalid parallelism value of \\"tequila\\": expected a number, a percentage string, or \\"max\\""`; + +exports[`parseParallelism throwsErrorOnInvalidParallelismPercentage 1`] = `"Invalid percentage value of \\"200\\": value must not exceed 100%"`; diff --git a/libraries/rush-lib/src/logic/operations/test/__snapshots__/PhasedOperationPlugin.test.ts.snap b/libraries/rush-lib/src/logic/operations/test/__snapshots__/PhasedOperationPlugin.test.ts.snap index f0d8741667a..4a14399e8eb 100644 --- a/libraries/rush-lib/src/logic/operations/test/__snapshots__/PhasedOperationPlugin.test.ts.snap +++ b/libraries/rush-lib/src/logic/operations/test/__snapshots__/PhasedOperationPlugin.test.ts.snap @@ -1,3256 +1,404 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`PhasedOperationPlugin handles a full build 1`] = ` +exports[`PhasedOperationPlugin handles a full build: full 1`] = ` Array [ - Object { - "dependencies": Array [], - "name": "a (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "b (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "c (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "d (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "e (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "f (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "g (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "h (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "i (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "j (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "a (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (no-deps)", - "a (upstream-self)", - ], - "name": "b (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (no-deps)", - "b (upstream-self)", - ], - "name": "c (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "d (no-deps)", - "b (upstream-self)", - ], - "name": "d (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "e (no-deps)", - "c (upstream-self)", - ], - "name": "e (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "f (no-deps)", - "a (upstream-self)", - "h (upstream-self)", - ], - "name": "f (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h (no-deps)", - "a (upstream-self)", - ], - "name": "h (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "g (no-deps)", - "a (upstream-self)", - ], - "name": "g (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "i (no-deps)", - ], - "name": "i (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "j (no-deps)", - ], - "name": "j (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "b (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (no-deps)", - ], - "name": "c (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (no-deps)", - ], - "name": "d (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (no-deps)", - ], - "name": "e (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - "h (no-deps)", - ], - "name": "f (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "g (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "h (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "i (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "j (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "b (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-1)", - ], - "name": "c (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-1)", - ], - "name": "d (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-1)", - ], - "name": "e (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - "h (upstream-1)", - ], - "name": "f (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "g (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "h (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "i (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "j (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-2)", - ], - "name": "b (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-2)", - ], - "name": "c (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-2)", - ], - "name": "d (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-2)", - ], - "name": "e (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-2)", - "h (upstream-2)", - ], - "name": "f (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-2)", - ], - "name": "g (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-2)", - ], - "name": "h (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "i (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "j (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "a (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-1)", - ], - "name": "b (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-1)", - ], - "name": "c (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "d (upstream-1)", - ], - "name": "d (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "e (upstream-1)", - ], - "name": "e (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "f (upstream-1)", - ], - "name": "f (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "g (upstream-1)", - ], - "name": "g (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h (upstream-1)", - ], - "name": "h (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "i (upstream-1)", - ], - "name": "i (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "j (upstream-1)", - ], - "name": "j (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-2)", - ], - "name": "a (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-2)", - ], - "name": "b (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-2)", - ], - "name": "c (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "d (upstream-2)", - ], - "name": "d (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "e (upstream-2)", - ], - "name": "e (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "f (upstream-2)", - ], - "name": "f (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "g (upstream-2)", - ], - "name": "g (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h (upstream-2)", - ], - "name": "h (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "i (upstream-2)", - ], - "name": "i (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "j (upstream-2)", - ], - "name": "j (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1-self)", - ], - "name": "b (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-1-self)", - ], - "name": "c (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-1-self)", - ], - "name": "d (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-1-self)", - ], - "name": "e (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1-self)", - "h (upstream-1-self)", - ], - "name": "f (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1-self)", - ], - "name": "g (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1-self)", - ], - "name": "h (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "i (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "j (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-3)", - ], - "name": "a (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-3)", - "a (upstream-1-self-upstream)", - "a (upstream-2-self)", - ], - "name": "b (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-3)", - "b (upstream-1-self-upstream)", - "b (upstream-2-self)", - ], - "name": "c (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "d (upstream-3)", - "b (upstream-1-self-upstream)", - "b (upstream-2-self)", - ], - "name": "d (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "e (upstream-3)", - "c (upstream-1-self-upstream)", - "c (upstream-2-self)", - ], - "name": "e (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "f (upstream-3)", - "a (upstream-1-self-upstream)", - "h (upstream-1-self-upstream)", - "a (upstream-2-self)", - "h (upstream-2-self)", - ], - "name": "f (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "g (upstream-3)", - "a (upstream-1-self-upstream)", - "a (upstream-2-self)", - ], - "name": "g (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h (upstream-3)", - "a (upstream-1-self-upstream)", - "a (upstream-2-self)", - ], - "name": "h (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "i (upstream-3)", - ], - "name": "i (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "j (upstream-3)", - ], - "name": "j (complex)", - "silent": false, - }, + "a (complex) (enabled) -> [a (upstream-3)]", + "a (no-deps) (enabled) -> []", + "a (upstream-1) (enabled) -> []", + "a (upstream-1-self) (enabled) -> [a (upstream-1)]", + "a (upstream-1-self-upstream) (enabled) -> []", + "a (upstream-2) (enabled) -> []", + "a (upstream-2-self) (enabled) -> [a (upstream-2)]", + "a (upstream-3) (enabled) -> []", + "a (upstream-self) (enabled) -> [a (no-deps)]", + "b (complex) (enabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), b (upstream-3)]", + "b (no-deps) (enabled) -> []", + "b (upstream-1) (enabled) -> [a (no-deps)]", + "b (upstream-1-self) (enabled) -> [b (upstream-1)]", + "b (upstream-1-self-upstream) (enabled) -> [a (upstream-1-self)]", + "b (upstream-2) (enabled) -> [a (upstream-1)]", + "b (upstream-2-self) (enabled) -> [b (upstream-2)]", + "b (upstream-3) (enabled) -> [a (upstream-2)]", + "b (upstream-self) (enabled) -> [a (upstream-self), b (no-deps)]", + "c (complex) (enabled) -> [b (upstream-1-self-upstream), b (upstream-2-self), c (upstream-3)]", + "c (no-deps) (enabled) -> []", + "c (upstream-1) (enabled) -> [b (no-deps)]", + "c (upstream-1-self) (enabled) -> [c (upstream-1)]", + "c (upstream-1-self-upstream) (enabled) -> [b (upstream-1-self)]", + "c (upstream-2) (enabled) -> [b (upstream-1)]", + "c (upstream-2-self) (enabled) -> [c (upstream-2)]", + "c (upstream-3) (enabled) -> [b (upstream-2)]", + "c (upstream-self) (enabled) -> [b (upstream-self), c (no-deps)]", + "d (complex) (enabled) -> [b (upstream-1-self-upstream), b (upstream-2-self), d (upstream-3)]", + "d (no-deps) (enabled) -> []", + "d (upstream-1) (enabled) -> [b (no-deps)]", + "d (upstream-1-self) (enabled) -> [d (upstream-1)]", + "d (upstream-1-self-upstream) (enabled) -> [b (upstream-1-self)]", + "d (upstream-2) (enabled) -> [b (upstream-1)]", + "d (upstream-2-self) (enabled) -> [d (upstream-2)]", + "d (upstream-3) (enabled) -> [b (upstream-2)]", + "d (upstream-self) (enabled) -> [b (upstream-self), d (no-deps)]", + "e (complex) (enabled) -> [c (upstream-1-self-upstream), c (upstream-2-self), e (upstream-3)]", + "e (no-deps) (enabled) -> []", + "e (upstream-1) (enabled) -> [c (no-deps)]", + "e (upstream-1-self) (enabled) -> [e (upstream-1)]", + "e (upstream-1-self-upstream) (enabled) -> [c (upstream-1-self)]", + "e (upstream-2) (enabled) -> [c (upstream-1)]", + "e (upstream-2-self) (enabled) -> [e (upstream-2)]", + "e (upstream-3) (enabled) -> [c (upstream-2)]", + "e (upstream-self) (enabled) -> [c (upstream-self), e (no-deps)]", + "f (complex) (enabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), f (upstream-3), h (upstream-1-self-upstream), h (upstream-2-self)]", + "f (no-deps) (enabled) -> []", + "f (upstream-1) (enabled) -> [a (no-deps), h (no-deps)]", + "f (upstream-1-self) (enabled) -> [f (upstream-1)]", + "f (upstream-1-self-upstream) (enabled) -> [a (upstream-1-self), h (upstream-1-self)]", + "f (upstream-2) (enabled) -> [a (upstream-1), h (upstream-1)]", + "f (upstream-2-self) (enabled) -> [f (upstream-2)]", + "f (upstream-3) (enabled) -> [a (upstream-2), h (upstream-2)]", + "f (upstream-self) (enabled) -> [a (upstream-self), f (no-deps), h (upstream-self)]", + "g (complex) (enabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), g (upstream-3)]", + "g (no-deps) (enabled) -> []", + "g (upstream-1) (enabled) -> [a (no-deps)]", + "g (upstream-1-self) (enabled) -> [g (upstream-1)]", + "g (upstream-1-self-upstream) (enabled) -> [a (upstream-1-self)]", + "g (upstream-2) (enabled) -> [a (upstream-1)]", + "g (upstream-2-self) (enabled) -> [g (upstream-2)]", + "g (upstream-3) (enabled) -> [a (upstream-2)]", + "g (upstream-self) (enabled) -> [a (upstream-self), g (no-deps)]", + "h (complex) (enabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), h (upstream-3)]", + "h (no-deps) (enabled) -> []", + "h (upstream-1) (enabled) -> [a (no-deps)]", + "h (upstream-1-self) (enabled) -> [h (upstream-1)]", + "h (upstream-1-self-upstream) (enabled) -> [a (upstream-1-self)]", + "h (upstream-2) (enabled) -> [a (upstream-1)]", + "h (upstream-2-self) (enabled) -> [h (upstream-2)]", + "h (upstream-3) (enabled) -> [a (upstream-2)]", + "h (upstream-self) (enabled) -> [a (upstream-self), h (no-deps)]", + "i (complex) (enabled) -> [i (upstream-3)]", + "i (no-deps) (enabled) -> []", + "i (upstream-1) (enabled) -> []", + "i (upstream-1-self) (enabled) -> [i (upstream-1)]", + "i (upstream-1-self-upstream) (enabled) -> []", + "i (upstream-2) (enabled) -> []", + "i (upstream-2-self) (enabled) -> [i (upstream-2)]", + "i (upstream-3) (enabled) -> []", + "i (upstream-self) (enabled) -> [i (no-deps)]", + "j (complex) (enabled) -> [j (upstream-3)]", + "j (no-deps) (enabled) -> []", + "j (upstream-1) (enabled) -> []", + "j (upstream-1-self) (enabled) -> [j (upstream-1)]", + "j (upstream-1-self-upstream) (enabled) -> []", + "j (upstream-2) (enabled) -> []", + "j (upstream-2-self) (enabled) -> [j (upstream-2)]", + "j (upstream-3) (enabled) -> []", + "j (upstream-self) (enabled) -> [j (no-deps)]", ] `; -exports[`PhasedOperationPlugin handles filtered phases 1`] = ` +exports[`PhasedOperationPlugin handles filtered phases on filtered projects: missing-links 1`] = ` Array [ - Object { - "dependencies": Array [ - "a;_phase:no-deps", - ], - "name": "a (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:no-deps", - "a (upstream-self)", - ], - "name": "b (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "b;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "c;_phase:no-deps", - "b (upstream-self)", - ], - "name": "c (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "c;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "d;_phase:no-deps", - "b (upstream-self)", - ], - "name": "d (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "d;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "e;_phase:no-deps", - "c (upstream-self)", - ], - "name": "e (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "e;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "f;_phase:no-deps", - "a (upstream-self)", - "h (upstream-self)", - ], - "name": "f (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "f;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "h;_phase:no-deps", - "a (upstream-self)", - ], - "name": "h (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "h;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "g;_phase:no-deps", - "a (upstream-self)", - ], - "name": "g (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "g;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "i;_phase:no-deps", - ], - "name": "i (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "i;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "j;_phase:no-deps", - ], - "name": "j (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "j;_phase:no-deps", - "silent": true, - }, + "a (complex) (enabled) -> [a (upstream-3)]", + "a (no-deps) (enabled) -> []", + "a (upstream-1) (enabled) -> []", + "a (upstream-3) (enabled) -> []", + "c (complex) (enabled) -> [b (upstream-1-self-upstream), b (upstream-2-self), c (upstream-3)]", + "c (no-deps) (enabled) -> []", + "c (upstream-1) (enabled) -> [b (no-deps)]", + "c (upstream-3) (enabled) -> [b (upstream-2)]", + "f (complex) (enabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), f (upstream-3), h (upstream-1-self-upstream), h (upstream-2-self)]", + "f (no-deps) (enabled) -> []", + "f (upstream-1) (enabled) -> [a (no-deps), h (no-deps)]", + "f (upstream-3) (enabled) -> [a (upstream-2), h (upstream-2)]", + "a (upstream-1-self) (disabled) -> [a (upstream-1)]", + "a (upstream-1-self-upstream) (disabled) -> []", + "a (upstream-2) (disabled) -> []", + "a (upstream-2-self) (disabled) -> [a (upstream-2)]", + "b (no-deps) (disabled) -> []", + "b (upstream-1-self-upstream) (disabled) -> [a (upstream-1-self)]", + "b (upstream-2) (disabled) -> [a (upstream-1)]", + "b (upstream-2-self) (disabled) -> [b (upstream-2)]", + "h (no-deps) (disabled) -> []", + "h (upstream-1-self-upstream) (disabled) -> [a (upstream-1-self)]", + "h (upstream-2) (disabled) -> [a (upstream-1)]", + "h (upstream-2-self) (disabled) -> [h (upstream-2)]", ] `; -exports[`PhasedOperationPlugin handles filtered phases 2`] = ` +exports[`PhasedOperationPlugin handles filtered phases on filtered projects: single-phase 1`] = ` Array [ - Object { - "dependencies": Array [ - "a (upstream-3)", - ], - "name": "a (complex)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-3)", - "a;_phase:upstream-1-self-upstream", - "a;_phase:upstream-2-self", - ], - "name": "b (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-2", - ], - "name": "b (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-2", - ], - "name": "a;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "c (upstream-3)", - "b;_phase:upstream-1-self-upstream", - "b;_phase:upstream-2-self", - ], - "name": "c (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-2", - ], - "name": "c (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "b;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1-self", - ], - "name": "b;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "a;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-2", - ], - "name": "b;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "d (upstream-3)", - "b;_phase:upstream-1-self-upstream", - "b;_phase:upstream-2-self", - ], - "name": "d (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-2", - ], - "name": "d (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "e (upstream-3)", - "c;_phase:upstream-1-self-upstream", - "c;_phase:upstream-2-self", - ], - "name": "e (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c;_phase:upstream-2", - ], - "name": "e (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-1)", - ], - "name": "c;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "b (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-1-self", - ], - "name": "c;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "b (upstream-1)", - ], - "name": "b;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "c;_phase:upstream-2", - ], - "name": "c;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "f (upstream-3)", - "a;_phase:upstream-1-self-upstream", - "h;_phase:upstream-1-self-upstream", - "a;_phase:upstream-2-self", - "h;_phase:upstream-2-self", - ], - "name": "f (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-2", - "h;_phase:upstream-2", - ], - "name": "f (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "h;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1-self", - ], - "name": "h;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "h;_phase:upstream-2", - ], - "name": "h;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "g (upstream-3)", - "a;_phase:upstream-1-self-upstream", - "a;_phase:upstream-2-self", - ], - "name": "g (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-2", - ], - "name": "g (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h (upstream-3)", - "a;_phase:upstream-1-self-upstream", - "a;_phase:upstream-2-self", - ], - "name": "h (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-2", - ], - "name": "h (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "i (upstream-3)", - ], - "name": "i (complex)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "i (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "j (upstream-3)", - ], - "name": "j (complex)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "j (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (no-deps)", - ], - "name": "c (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "b (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (no-deps)", - ], - "name": "d (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (no-deps)", - ], - "name": "e (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "c (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - "h (no-deps)", - ], - "name": "f (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "h (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "g (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "h (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "i (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "j (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "d (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "e (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "f (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "g (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "i (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "j (no-deps)", - "silent": false, - }, + "a (upstream-2) (enabled) -> []", + "c (upstream-2) (enabled) -> [b (upstream-1)]", + "f (upstream-2) (enabled) -> [a (upstream-1), h (upstream-1)]", + "a (no-deps) (disabled) -> []", + "a (upstream-1) (disabled) -> []", + "b (upstream-1) (disabled) -> [a (no-deps)]", + "h (upstream-1) (disabled) -> [a (no-deps)]", ] `; -exports[`PhasedOperationPlugin handles filtered phases on filtered projects 1`] = ` +exports[`PhasedOperationPlugin handles filtered phases: single-phase 1`] = ` Array [ - Object { - "dependencies": Array [ - "a;_phase:upstream-1", - "h;_phase:upstream-1", - ], - "name": "f (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:no-deps", - ], - "name": "h;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-1", - ], - "name": "c (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:no-deps", - ], - "name": "b;_phase:upstream-1", - "silent": true, - }, + "a (upstream-self) (enabled) -> [a (no-deps)]", + "b (upstream-self) (enabled) -> [a (upstream-self), b (no-deps)]", + "c (upstream-self) (enabled) -> [b (upstream-self), c (no-deps)]", + "d (upstream-self) (enabled) -> [b (upstream-self), d (no-deps)]", + "e (upstream-self) (enabled) -> [c (upstream-self), e (no-deps)]", + "f (upstream-self) (enabled) -> [a (upstream-self), f (no-deps), h (upstream-self)]", + "g (upstream-self) (enabled) -> [a (upstream-self), g (no-deps)]", + "h (upstream-self) (enabled) -> [a (upstream-self), h (no-deps)]", + "i (upstream-self) (enabled) -> [i (no-deps)]", + "j (upstream-self) (enabled) -> [j (no-deps)]", + "a (no-deps) (disabled) -> []", + "b (no-deps) (disabled) -> []", + "c (no-deps) (disabled) -> []", + "d (no-deps) (disabled) -> []", + "e (no-deps) (disabled) -> []", + "f (no-deps) (disabled) -> []", + "g (no-deps) (disabled) -> []", + "h (no-deps) (disabled) -> []", + "i (no-deps) (disabled) -> []", + "j (no-deps) (disabled) -> []", ] `; -exports[`PhasedOperationPlugin handles filtered phases on filtered projects 2`] = ` +exports[`PhasedOperationPlugin handles filtered phases: two-phases 1`] = ` Array [ - Object { - "dependencies": Array [ - "f (upstream-3)", - "a;_phase:upstream-1-self-upstream", - "h;_phase:upstream-1-self-upstream", - "a;_phase:upstream-2-self", - "h;_phase:upstream-2-self", - ], - "name": "f (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-2", - "h;_phase:upstream-2", - ], - "name": "f (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "h;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1-self", - ], - "name": "h;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "a;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-2", - ], - "name": "a;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "h;_phase:upstream-2", - ], - "name": "h;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (upstream-3)", - ], - "name": "a (complex)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-3)", - "b;_phase:upstream-1-self-upstream", - "b;_phase:upstream-2-self", - ], - "name": "c (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-2", - ], - "name": "c (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "b;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1-self", - ], - "name": "b;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-2", - ], - "name": "b;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - "h;_phase:no-deps", - ], - "name": "f (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "h;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:no-deps", - ], - "name": "c (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "b;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "f (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "c (no-deps)", - "silent": false, - }, + "a (complex) (enabled) -> [a (upstream-3)]", + "a (no-deps) (enabled) -> []", + "a (upstream-1) (enabled) -> []", + "a (upstream-3) (enabled) -> []", + "b (complex) (enabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), b (upstream-3)]", + "b (no-deps) (enabled) -> []", + "b (upstream-1) (enabled) -> [a (no-deps)]", + "b (upstream-3) (enabled) -> [a (upstream-2)]", + "c (complex) (enabled) -> [b (upstream-1-self-upstream), b (upstream-2-self), c (upstream-3)]", + "c (no-deps) (enabled) -> []", + "c (upstream-1) (enabled) -> [b (no-deps)]", + "c (upstream-3) (enabled) -> [b (upstream-2)]", + "d (complex) (enabled) -> [b (upstream-1-self-upstream), b (upstream-2-self), d (upstream-3)]", + "d (no-deps) (enabled) -> []", + "d (upstream-1) (enabled) -> [b (no-deps)]", + "d (upstream-3) (enabled) -> [b (upstream-2)]", + "e (complex) (enabled) -> [c (upstream-1-self-upstream), c (upstream-2-self), e (upstream-3)]", + "e (no-deps) (enabled) -> []", + "e (upstream-1) (enabled) -> [c (no-deps)]", + "e (upstream-3) (enabled) -> [c (upstream-2)]", + "f (complex) (enabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), f (upstream-3), h (upstream-1-self-upstream), h (upstream-2-self)]", + "f (no-deps) (enabled) -> []", + "f (upstream-1) (enabled) -> [a (no-deps), h (no-deps)]", + "f (upstream-3) (enabled) -> [a (upstream-2), h (upstream-2)]", + "g (complex) (enabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), g (upstream-3)]", + "g (no-deps) (enabled) -> []", + "g (upstream-1) (enabled) -> [a (no-deps)]", + "g (upstream-3) (enabled) -> [a (upstream-2)]", + "h (complex) (enabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), h (upstream-3)]", + "h (no-deps) (enabled) -> []", + "h (upstream-1) (enabled) -> [a (no-deps)]", + "h (upstream-3) (enabled) -> [a (upstream-2)]", + "i (complex) (enabled) -> [i (upstream-3)]", + "i (no-deps) (enabled) -> []", + "i (upstream-1) (enabled) -> []", + "i (upstream-3) (enabled) -> []", + "j (complex) (enabled) -> [j (upstream-3)]", + "j (no-deps) (enabled) -> []", + "j (upstream-1) (enabled) -> []", + "j (upstream-3) (enabled) -> []", + "a (upstream-1-self) (disabled) -> [a (upstream-1)]", + "a (upstream-1-self-upstream) (disabled) -> []", + "a (upstream-2) (disabled) -> []", + "a (upstream-2-self) (disabled) -> [a (upstream-2)]", + "b (upstream-1-self) (disabled) -> [b (upstream-1)]", + "b (upstream-1-self-upstream) (disabled) -> [a (upstream-1-self)]", + "b (upstream-2) (disabled) -> [a (upstream-1)]", + "b (upstream-2-self) (disabled) -> [b (upstream-2)]", + "c (upstream-1-self-upstream) (disabled) -> [b (upstream-1-self)]", + "c (upstream-2) (disabled) -> [b (upstream-1)]", + "c (upstream-2-self) (disabled) -> [c (upstream-2)]", + "h (upstream-1-self-upstream) (disabled) -> [a (upstream-1-self)]", + "h (upstream-2) (disabled) -> [a (upstream-1)]", + "h (upstream-2-self) (disabled) -> [h (upstream-2)]", ] `; -exports[`PhasedOperationPlugin handles filtered projects 1`] = ` +exports[`PhasedOperationPlugin handles filtered projects: filtered 1`] = ` Array [ - Object { - "dependencies": Array [], - "name": "g (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [ - "g (no-deps)", - "a;_phase:upstream-self", - ], - "name": "g (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:no-deps", - ], - "name": "a;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:no-deps", - ], - "name": "g (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1", - ], - "name": "g (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-2", - ], - "name": "g (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [ - "g (upstream-1)", - ], - "name": "g (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "g (upstream-2)", - ], - "name": "g (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1-self", - ], - "name": "g (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1", - ], - "name": "a;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "g (upstream-3)", - "a;_phase:upstream-1-self-upstream", - "a;_phase:upstream-2-self", - ], - "name": "g (complex)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-2", - ], - "name": "a;_phase:upstream-2-self", - "silent": true, - }, + "a (complex) (enabled) -> [a (upstream-3)]", + "a (no-deps) (enabled) -> []", + "a (upstream-1) (enabled) -> []", + "a (upstream-1-self) (enabled) -> [a (upstream-1)]", + "a (upstream-1-self-upstream) (enabled) -> []", + "a (upstream-2) (enabled) -> []", + "a (upstream-2-self) (enabled) -> [a (upstream-2)]", + "a (upstream-3) (enabled) -> []", + "a (upstream-self) (enabled) -> [a (no-deps)]", + "c (complex) (enabled) -> [b (upstream-1-self-upstream), b (upstream-2-self), c (upstream-3)]", + "c (no-deps) (enabled) -> []", + "c (upstream-1) (enabled) -> [b (no-deps)]", + "c (upstream-1-self) (enabled) -> [c (upstream-1)]", + "c (upstream-1-self-upstream) (enabled) -> [b (upstream-1-self)]", + "c (upstream-2) (enabled) -> [b (upstream-1)]", + "c (upstream-2-self) (enabled) -> [c (upstream-2)]", + "c (upstream-3) (enabled) -> [b (upstream-2)]", + "c (upstream-self) (enabled) -> [b (upstream-self), c (no-deps)]", + "f (complex) (enabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), f (upstream-3), h (upstream-1-self-upstream), h (upstream-2-self)]", + "f (no-deps) (enabled) -> []", + "f (upstream-1) (enabled) -> [a (no-deps), h (no-deps)]", + "f (upstream-1-self) (enabled) -> [f (upstream-1)]", + "f (upstream-1-self-upstream) (enabled) -> [a (upstream-1-self), h (upstream-1-self)]", + "f (upstream-2) (enabled) -> [a (upstream-1), h (upstream-1)]", + "f (upstream-2-self) (enabled) -> [f (upstream-2)]", + "f (upstream-3) (enabled) -> [a (upstream-2), h (upstream-2)]", + "f (upstream-self) (enabled) -> [a (upstream-self), f (no-deps), h (upstream-self)]", + "b (no-deps) (disabled) -> []", + "b (upstream-1) (disabled) -> [a (no-deps)]", + "b (upstream-1-self) (disabled) -> [b (upstream-1)]", + "b (upstream-1-self-upstream) (disabled) -> [a (upstream-1-self)]", + "b (upstream-2) (disabled) -> [a (upstream-1)]", + "b (upstream-2-self) (disabled) -> [b (upstream-2)]", + "b (upstream-self) (disabled) -> [a (upstream-self), b (no-deps)]", + "h (no-deps) (disabled) -> []", + "h (upstream-1) (disabled) -> [a (no-deps)]", + "h (upstream-1-self) (disabled) -> [h (upstream-1)]", + "h (upstream-1-self-upstream) (disabled) -> [a (upstream-1-self)]", + "h (upstream-2) (disabled) -> [a (upstream-1)]", + "h (upstream-2-self) (disabled) -> [h (upstream-2)]", + "h (upstream-self) (disabled) -> [a (upstream-self), h (no-deps)]", ] `; -exports[`PhasedOperationPlugin handles filtered projects 2`] = ` +exports[`PhasedOperationPlugin handles filtered projects: single 1`] = ` Array [ - Object { - "dependencies": Array [], - "name": "f (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "c (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [ - "f (no-deps)", - "a (upstream-self)", - "h;_phase:upstream-self", - ], - "name": "f (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "a (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h;_phase:no-deps", - "a (upstream-self)", - ], - "name": "h;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "h;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "c (no-deps)", - "b;_phase:upstream-self", - ], - "name": "c (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:no-deps", - "a (upstream-self)", - ], - "name": "b;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "b;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - "h;_phase:no-deps", - ], - "name": "f (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:no-deps", - ], - "name": "c (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - "h;_phase:upstream-1", - ], - "name": "f (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "h;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-1", - ], - "name": "c (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "b;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (upstream-2)", - "h;_phase:upstream-2", - ], - "name": "f (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "h;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-2", - ], - "name": "c (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "b;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [ - "f (upstream-1)", - ], - "name": "f (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "a (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-1)", - ], - "name": "c (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "f (upstream-2)", - ], - "name": "f (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-2)", - ], - "name": "a (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-2)", - ], - "name": "c (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1-self)", - "h;_phase:upstream-1-self", - ], - "name": "f (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h;_phase:upstream-1", - ], - "name": "h;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-1-self", - ], - "name": "c (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-1", - ], - "name": "b;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "f (upstream-3)", - "a (upstream-1-self-upstream)", - "h;_phase:upstream-1-self-upstream", - "a (upstream-2-self)", - "h;_phase:upstream-2-self", - ], - "name": "f (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1-self)", - ], - "name": "h;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "h;_phase:upstream-2", - ], - "name": "h;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (upstream-3)", - ], - "name": "a (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-3)", - "b;_phase:upstream-1-self-upstream", - "b;_phase:upstream-2-self", - ], - "name": "c (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1-self)", - ], - "name": "b;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-2", - ], - "name": "b;_phase:upstream-2-self", - "silent": true, - }, + "g (complex) (enabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), g (upstream-3)]", + "g (no-deps) (enabled) -> []", + "g (upstream-1) (enabled) -> [a (no-deps)]", + "g (upstream-1-self) (enabled) -> [g (upstream-1)]", + "g (upstream-1-self-upstream) (enabled) -> [a (upstream-1-self)]", + "g (upstream-2) (enabled) -> [a (upstream-1)]", + "g (upstream-2-self) (enabled) -> [g (upstream-2)]", + "g (upstream-3) (enabled) -> [a (upstream-2)]", + "g (upstream-self) (enabled) -> [a (upstream-self), g (no-deps)]", + "a (no-deps) (disabled) -> []", + "a (upstream-1) (disabled) -> []", + "a (upstream-1-self) (disabled) -> [a (upstream-1)]", + "a (upstream-1-self-upstream) (disabled) -> []", + "a (upstream-2) (disabled) -> []", + "a (upstream-2-self) (disabled) -> [a (upstream-2)]", + "a (upstream-self) (disabled) -> [a (no-deps)]", ] `; -exports[`PhasedOperationPlugin handles some changed projects 1`] = ` +exports[`PhasedOperationPlugin handles incomplete phaseSelection cross-project with --include-phase-deps: multiple-project 1`] = ` Array [ - Object { - "dependencies": Array [], - "name": "a;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "b;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "c;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "d;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "e;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "f;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "g (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "h;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "i;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "j;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:no-deps", - ], - "name": "a;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:no-deps", - "a;_phase:upstream-self", - ], - "name": "b;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "c;_phase:no-deps", - "b;_phase:upstream-self", - ], - "name": "c;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "d;_phase:no-deps", - "b;_phase:upstream-self", - ], - "name": "d;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "e;_phase:no-deps", - "c;_phase:upstream-self", - ], - "name": "e;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "f;_phase:no-deps", - "a;_phase:upstream-self", - "h;_phase:upstream-self", - ], - "name": "f;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "h;_phase:no-deps", - "a;_phase:upstream-self", - ], - "name": "h;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "g (no-deps)", - "a;_phase:upstream-self", - ], - "name": "g (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "i;_phase:no-deps", - ], - "name": "i;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "j;_phase:no-deps", - ], - "name": "j;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:no-deps", - ], - "name": "b;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:no-deps", - ], - "name": "c;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:no-deps", - ], - "name": "d;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [ - "c;_phase:no-deps", - ], - "name": "e;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:no-deps", - "h;_phase:no-deps", - ], - "name": "f;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:no-deps", - ], - "name": "g (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:no-deps", - ], - "name": "h;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "i;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "j;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1", - ], - "name": "b;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-1", - ], - "name": "c;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-1", - ], - "name": "d;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [ - "c;_phase:upstream-1", - ], - "name": "e;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1", - "h;_phase:upstream-1", - ], - "name": "f;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1", - ], - "name": "g (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1", - ], - "name": "h;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "i;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "j;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:upstream-3", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-2", - ], - "name": "b;_phase:upstream-3", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-2", - ], - "name": "c;_phase:upstream-3", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-2", - ], - "name": "d;_phase:upstream-3", - "silent": true, - }, - Object { - "dependencies": Array [ - "c;_phase:upstream-2", - ], - "name": "e;_phase:upstream-3", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-2", - "h;_phase:upstream-2", - ], - "name": "f;_phase:upstream-3", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-2", - ], - "name": "g (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-2", - ], - "name": "h;_phase:upstream-3", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "i;_phase:upstream-3", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "j;_phase:upstream-3", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1", - ], - "name": "a;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-1", - ], - "name": "b;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "c;_phase:upstream-1", - ], - "name": "c;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "d;_phase:upstream-1", - ], - "name": "d;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "e;_phase:upstream-1", - ], - "name": "e;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "f;_phase:upstream-1", - ], - "name": "f;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "g (upstream-1)", - ], - "name": "g (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h;_phase:upstream-1", - ], - "name": "h;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "i;_phase:upstream-1", - ], - "name": "i;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "j;_phase:upstream-1", - ], - "name": "j;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-2", - ], - "name": "a;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-2", - ], - "name": "b;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "c;_phase:upstream-2", - ], - "name": "c;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "d;_phase:upstream-2", - ], - "name": "d;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "e;_phase:upstream-2", - ], - "name": "e;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "f;_phase:upstream-2", - ], - "name": "f;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "g (upstream-2)", - ], - "name": "g (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h;_phase:upstream-2", - ], - "name": "h;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "i;_phase:upstream-2", - ], - "name": "i;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "j;_phase:upstream-2", - ], - "name": "j;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1-self", - ], - "name": "b;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-1-self", - ], - "name": "c;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-1-self", - ], - "name": "d;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "c;_phase:upstream-1-self", - ], - "name": "e;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1-self", - "h;_phase:upstream-1-self", - ], - "name": "f;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1-self", - ], - "name": "g (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-1-self", - ], - "name": "h;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "i;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "j;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "a;_phase:upstream-3", - ], - "name": "a;_phase:complex", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-3", - "a;_phase:upstream-1-self-upstream", - "a;_phase:upstream-2-self", - ], - "name": "b;_phase:complex", - "silent": true, - }, - Object { - "dependencies": Array [ - "c;_phase:upstream-3", - "b;_phase:upstream-1-self-upstream", - "b;_phase:upstream-2-self", - ], - "name": "c;_phase:complex", - "silent": true, - }, - Object { - "dependencies": Array [ - "d;_phase:upstream-3", - "b;_phase:upstream-1-self-upstream", - "b;_phase:upstream-2-self", - ], - "name": "d;_phase:complex", - "silent": true, - }, - Object { - "dependencies": Array [ - "e;_phase:upstream-3", - "c;_phase:upstream-1-self-upstream", - "c;_phase:upstream-2-self", - ], - "name": "e;_phase:complex", - "silent": true, - }, - Object { - "dependencies": Array [ - "f;_phase:upstream-3", - "a;_phase:upstream-1-self-upstream", - "h;_phase:upstream-1-self-upstream", - "a;_phase:upstream-2-self", - "h;_phase:upstream-2-self", - ], - "name": "f;_phase:complex", - "silent": true, - }, - Object { - "dependencies": Array [ - "g (upstream-3)", - "a;_phase:upstream-1-self-upstream", - "a;_phase:upstream-2-self", - ], - "name": "g (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h;_phase:upstream-3", - "a;_phase:upstream-1-self-upstream", - "a;_phase:upstream-2-self", - ], - "name": "h;_phase:complex", - "silent": true, - }, - Object { - "dependencies": Array [ - "i;_phase:upstream-3", - ], - "name": "i;_phase:complex", - "silent": true, - }, - Object { - "dependencies": Array [ - "j;_phase:upstream-3", - ], - "name": "j;_phase:complex", - "silent": true, - }, + "a (no-deps) (enabled) -> []", + "h (upstream-1) (enabled) -> [a (no-deps)]", ] `; -exports[`PhasedOperationPlugin handles some changed projects 2`] = ` +exports[`PhasedOperationPlugin handles incomplete phaseSelection with --include-phase-deps: single-project 1`] = ` Array [ - Object { - "dependencies": Array [], - "name": "a (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "b;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "c (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "d;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "e;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "f (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "g;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "h;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "i;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "j;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "a (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:no-deps", - "a (upstream-self)", - ], - "name": "b (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (no-deps)", - "b (upstream-self)", - ], - "name": "c (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "d;_phase:no-deps", - "b (upstream-self)", - ], - "name": "d (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "e;_phase:no-deps", - "c (upstream-self)", - ], - "name": "e (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "f (no-deps)", - "a (upstream-self)", - "h (upstream-self)", - ], - "name": "f (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h;_phase:no-deps", - "a (upstream-self)", - ], - "name": "h (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "g;_phase:no-deps", - "a (upstream-self)", - ], - "name": "g (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "i;_phase:no-deps", - ], - "name": "i;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "j;_phase:no-deps", - ], - "name": "j;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "b (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:no-deps", - ], - "name": "c (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:no-deps", - ], - "name": "d;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [ - "c (no-deps)", - ], - "name": "e (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - "h;_phase:no-deps", - ], - "name": "f (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "g (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "h (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "i;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "j;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "b (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-1)", - ], - "name": "c (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-1)", - ], - "name": "d (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-1)", - ], - "name": "e (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - "h (upstream-1)", - ], - "name": "f (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "g (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "h (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "i;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "j;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-2)", - ], - "name": "b (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-2)", - ], - "name": "c (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-2)", - ], - "name": "d (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-2)", - ], - "name": "e (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-2)", - "h (upstream-2)", - ], - "name": "f (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-2)", - ], - "name": "g (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-2)", - ], - "name": "h (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "i;_phase:upstream-3", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "j;_phase:upstream-3", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "a (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-1)", - ], - "name": "b (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-1)", - ], - "name": "c (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "d;_phase:upstream-1", - ], - "name": "d;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "e (upstream-1)", - ], - "name": "e (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "f (upstream-1)", - ], - "name": "f (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "g (upstream-1)", - ], - "name": "g (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h (upstream-1)", - ], - "name": "h (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "i;_phase:upstream-1", - ], - "name": "i;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "j;_phase:upstream-1", - ], - "name": "j;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (upstream-2)", - ], - "name": "a (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-2)", - ], - "name": "b (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-2)", - ], - "name": "c (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "d (upstream-2)", - ], - "name": "d (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "e (upstream-2)", - ], - "name": "e (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "f (upstream-2)", - ], - "name": "f (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "g (upstream-2)", - ], - "name": "g (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h (upstream-2)", - ], - "name": "h (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "i;_phase:upstream-2", - ], - "name": "i;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "j;_phase:upstream-2", - ], - "name": "j;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1-self)", - ], - "name": "b (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-1-self)", - ], - "name": "c (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-1-self)", - ], - "name": "d (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-1-self)", - ], - "name": "e (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1-self)", - "h (upstream-1-self)", - ], - "name": "f (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1-self)", - ], - "name": "g (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1-self)", - ], - "name": "h (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "i;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "j;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (upstream-3)", - ], - "name": "a (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b (upstream-3)", - "a (upstream-1-self-upstream)", - "a (upstream-2-self)", - ], - "name": "b (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-3)", - "b (upstream-1-self-upstream)", - "b (upstream-2-self)", - ], - "name": "c (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "d (upstream-3)", - "b (upstream-1-self-upstream)", - "b (upstream-2-self)", - ], - "name": "d (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "e (upstream-3)", - "c (upstream-1-self-upstream)", - "c (upstream-2-self)", - ], - "name": "e (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "f (upstream-3)", - "a (upstream-1-self-upstream)", - "h (upstream-1-self-upstream)", - "a (upstream-2-self)", - "h (upstream-2-self)", - ], - "name": "f (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "g (upstream-3)", - "a (upstream-1-self-upstream)", - "a (upstream-2-self)", - ], - "name": "g (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h (upstream-3)", - "a (upstream-1-self-upstream)", - "a (upstream-2-self)", - ], - "name": "h (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "i;_phase:upstream-3", - ], - "name": "i;_phase:complex", - "silent": true, - }, - Object { - "dependencies": Array [ - "j;_phase:upstream-3", - ], - "name": "j;_phase:complex", - "silent": true, - }, + "a (no-deps) (enabled) -> []", + "a (upstream-self) (enabled) -> [a (no-deps)]", ] `; -exports[`PhasedOperationPlugin handles some changed projects within filtered projects 1`] = ` +exports[`PhasedOperationPlugin handles incomplete phaseSelection without --include-phase-deps: single-project 1`] = ` Array [ - Object { - "dependencies": Array [], - "name": "f;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "c (no-deps)", - "silent": false, - }, - Object { - "dependencies": Array [ - "f;_phase:no-deps", - "a (upstream-self)", - "h;_phase:upstream-self", - ], - "name": "f (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "a (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h;_phase:no-deps", - "a (upstream-self)", - ], - "name": "h;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "h;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "c (no-deps)", - "b;_phase:upstream-self", - ], - "name": "c (upstream-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:no-deps", - "a (upstream-self)", - ], - "name": "b;_phase:upstream-self", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "b;_phase:no-deps", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - "h;_phase:no-deps", - ], - "name": "f (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:no-deps", - ], - "name": "c (upstream-1)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - "h;_phase:upstream-1", - ], - "name": "f (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "h;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-1", - ], - "name": "c (upstream-2)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (no-deps)", - ], - "name": "b;_phase:upstream-1", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (upstream-2)", - "h;_phase:upstream-2", - ], - "name": "f (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "h;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-2", - ], - "name": "c (upstream-3)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "b;_phase:upstream-2", - "silent": true, - }, - Object { - "dependencies": Array [ - "f (upstream-1)", - ], - "name": "f (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1)", - ], - "name": "a (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-1)", - ], - "name": "c (upstream-1-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "f (upstream-2)", - ], - "name": "f (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-2)", - ], - "name": "a (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-2)", - ], - "name": "c (upstream-2-self)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1-self)", - "h;_phase:upstream-1-self", - ], - "name": "f (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "h;_phase:upstream-1", - ], - "name": "h;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [], - "name": "a (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-1-self", - ], - "name": "c (upstream-1-self-upstream)", - "silent": false, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-1", - ], - "name": "b;_phase:upstream-1-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "f (upstream-3)", - "a (upstream-1-self-upstream)", - "h;_phase:upstream-1-self-upstream", - "a (upstream-2-self)", - "h;_phase:upstream-2-self", - ], - "name": "f (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1-self)", - ], - "name": "h;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "h;_phase:upstream-2", - ], - "name": "h;_phase:upstream-2-self", - "silent": true, - }, - Object { - "dependencies": Array [ - "a (upstream-3)", - ], - "name": "a (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "c (upstream-3)", - "b;_phase:upstream-1-self-upstream", - "b;_phase:upstream-2-self", - ], - "name": "c (complex)", - "silent": false, - }, - Object { - "dependencies": Array [ - "a (upstream-1-self)", - ], - "name": "b;_phase:upstream-1-self-upstream", - "silent": true, - }, - Object { - "dependencies": Array [ - "b;_phase:upstream-2", - ], - "name": "b;_phase:upstream-2-self", - "silent": true, - }, + "a (upstream-self) (enabled) -> [a (no-deps)]", + "a (no-deps) (disabled) -> []", +] +`; + +exports[`PhasedOperationPlugin includes full graph but enables subset when generateFullGraph is true: full-graph-filtered 1`] = ` +Array [ + "a (complex) (enabled) -> [a (upstream-3)]", + "a (no-deps) (enabled) -> []", + "a (upstream-1) (enabled) -> []", + "a (upstream-1-self) (enabled) -> [a (upstream-1)]", + "a (upstream-1-self-upstream) (enabled) -> []", + "a (upstream-2) (enabled) -> []", + "a (upstream-2-self) (enabled) -> [a (upstream-2)]", + "a (upstream-3) (enabled) -> []", + "a (upstream-self) (enabled) -> [a (no-deps)]", + "c (complex) (enabled) -> [b (upstream-1-self-upstream), b (upstream-2-self), c (upstream-3)]", + "c (no-deps) (enabled) -> []", + "c (upstream-1) (enabled) -> [b (no-deps)]", + "c (upstream-1-self) (enabled) -> [c (upstream-1)]", + "c (upstream-1-self-upstream) (enabled) -> [b (upstream-1-self)]", + "c (upstream-2) (enabled) -> [b (upstream-1)]", + "c (upstream-2-self) (enabled) -> [c (upstream-2)]", + "c (upstream-3) (enabled) -> [b (upstream-2)]", + "c (upstream-self) (enabled) -> [b (upstream-self), c (no-deps)]", + "b (complex) (disabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), b (upstream-3)]", + "b (no-deps) (disabled) -> []", + "b (upstream-1) (disabled) -> [a (no-deps)]", + "b (upstream-1-self) (disabled) -> [b (upstream-1)]", + "b (upstream-1-self-upstream) (disabled) -> [a (upstream-1-self)]", + "b (upstream-2) (disabled) -> [a (upstream-1)]", + "b (upstream-2-self) (disabled) -> [b (upstream-2)]", + "b (upstream-3) (disabled) -> [a (upstream-2)]", + "b (upstream-self) (disabled) -> [a (upstream-self), b (no-deps)]", + "d (complex) (disabled) -> [b (upstream-1-self-upstream), b (upstream-2-self), d (upstream-3)]", + "d (no-deps) (disabled) -> []", + "d (upstream-1) (disabled) -> [b (no-deps)]", + "d (upstream-1-self) (disabled) -> [d (upstream-1)]", + "d (upstream-1-self-upstream) (disabled) -> [b (upstream-1-self)]", + "d (upstream-2) (disabled) -> [b (upstream-1)]", + "d (upstream-2-self) (disabled) -> [d (upstream-2)]", + "d (upstream-3) (disabled) -> [b (upstream-2)]", + "d (upstream-self) (disabled) -> [b (upstream-self), d (no-deps)]", + "e (complex) (disabled) -> [c (upstream-1-self-upstream), c (upstream-2-self), e (upstream-3)]", + "e (no-deps) (disabled) -> []", + "e (upstream-1) (disabled) -> [c (no-deps)]", + "e (upstream-1-self) (disabled) -> [e (upstream-1)]", + "e (upstream-1-self-upstream) (disabled) -> [c (upstream-1-self)]", + "e (upstream-2) (disabled) -> [c (upstream-1)]", + "e (upstream-2-self) (disabled) -> [e (upstream-2)]", + "e (upstream-3) (disabled) -> [c (upstream-2)]", + "e (upstream-self) (disabled) -> [c (upstream-self), e (no-deps)]", + "f (complex) (disabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), f (upstream-3), h (upstream-1-self-upstream), h (upstream-2-self)]", + "f (no-deps) (disabled) -> []", + "f (upstream-1) (disabled) -> [a (no-deps), h (no-deps)]", + "f (upstream-1-self) (disabled) -> [f (upstream-1)]", + "f (upstream-1-self-upstream) (disabled) -> [a (upstream-1-self), h (upstream-1-self)]", + "f (upstream-2) (disabled) -> [a (upstream-1), h (upstream-1)]", + "f (upstream-2-self) (disabled) -> [f (upstream-2)]", + "f (upstream-3) (disabled) -> [a (upstream-2), h (upstream-2)]", + "f (upstream-self) (disabled) -> [a (upstream-self), f (no-deps), h (upstream-self)]", + "g (complex) (disabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), g (upstream-3)]", + "g (no-deps) (disabled) -> []", + "g (upstream-1) (disabled) -> [a (no-deps)]", + "g (upstream-1-self) (disabled) -> [g (upstream-1)]", + "g (upstream-1-self-upstream) (disabled) -> [a (upstream-1-self)]", + "g (upstream-2) (disabled) -> [a (upstream-1)]", + "g (upstream-2-self) (disabled) -> [g (upstream-2)]", + "g (upstream-3) (disabled) -> [a (upstream-2)]", + "g (upstream-self) (disabled) -> [a (upstream-self), g (no-deps)]", + "h (complex) (disabled) -> [a (upstream-1-self-upstream), a (upstream-2-self), h (upstream-3)]", + "h (no-deps) (disabled) -> []", + "h (upstream-1) (disabled) -> [a (no-deps)]", + "h (upstream-1-self) (disabled) -> [h (upstream-1)]", + "h (upstream-1-self-upstream) (disabled) -> [a (upstream-1-self)]", + "h (upstream-2) (disabled) -> [a (upstream-1)]", + "h (upstream-2-self) (disabled) -> [h (upstream-2)]", + "h (upstream-3) (disabled) -> [a (upstream-2)]", + "h (upstream-self) (disabled) -> [a (upstream-self), h (no-deps)]", + "i (complex) (disabled) -> [i (upstream-3)]", + "i (no-deps) (disabled) -> []", + "i (upstream-1) (disabled) -> []", + "i (upstream-1-self) (disabled) -> [i (upstream-1)]", + "i (upstream-1-self-upstream) (disabled) -> []", + "i (upstream-2) (disabled) -> []", + "i (upstream-2-self) (disabled) -> [i (upstream-2)]", + "i (upstream-3) (disabled) -> []", + "i (upstream-self) (disabled) -> [i (no-deps)]", + "j (complex) (disabled) -> [j (upstream-3)]", + "j (no-deps) (disabled) -> []", + "j (upstream-1) (disabled) -> []", + "j (upstream-1-self) (disabled) -> [j (upstream-1)]", + "j (upstream-1-self-upstream) (disabled) -> []", + "j (upstream-2) (disabled) -> []", + "j (upstream-2-self) (disabled) -> [j (upstream-2)]", + "j (upstream-3) (disabled) -> []", + "j (upstream-self) (disabled) -> [j (no-deps)]", ] `; diff --git a/libraries/rush-lib/src/logic/operations/test/__snapshots__/ShellOperationRunnerPlugin.test.ts.snap b/libraries/rush-lib/src/logic/operations/test/__snapshots__/ShellOperationRunnerPlugin.test.ts.snap index f5990af4d43..29e7fda746b 100644 --- a/libraries/rush-lib/src/logic/operations/test/__snapshots__/ShellOperationRunnerPlugin.test.ts.snap +++ b/libraries/rush-lib/src/logic/operations/test/__snapshots__/ShellOperationRunnerPlugin.test.ts.snap @@ -1,4 +1,17 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`ShellOperationRunnerPlugin parameters should be filtered when parameterNamesToIgnore is specified 1`] = ` +Array [ + Object { + "commandToRun": "echo building a --verbose --config /path/to/config.json --mode prod --tags tag1 --tags tag2", + "name": "a", + }, + Object { + "commandToRun": "echo building b --production", + "name": "b", + }, +] +`; exports[`ShellOperationRunnerPlugin shellCommand "echo custom shellCommand" should be set to commandToRun 1`] = ` Array [ diff --git a/libraries/rush-lib/src/logic/pnpm/IPnpmfile.ts b/libraries/rush-lib/src/logic/pnpm/IPnpmfile.ts index 71b735e2100..50e9ddadd25 100644 --- a/libraries/rush-lib/src/logic/pnpm/IPnpmfile.ts +++ b/libraries/rush-lib/src/logic/pnpm/IPnpmfile.ts @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { LogBase } from '@pnpm/logger'; +import type * as pnpmKitV8 from '@rushstack/rush-pnpm-kit-v8'; import type { IPackageJson } from '@rushstack/node-core-library'; + import type { IPnpmShrinkwrapYaml } from './PnpmShrinkwrapFile'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; /** * The `settings` parameter passed to {@link IPnpmfileShim.hooks.readPackage} and @@ -20,6 +22,23 @@ export interface IPnpmfileShimSettings { userPnpmfilePath?: string; } +export interface IWorkspaceProjectInfo + extends Pick { + packageVersion: RushConfigurationProject['packageJson']['version']; + injectedDependencies: Array; +} + +/** + * The `settings` parameter passed to {@link IPnpmfileShim.hooks.readPackage} and + * {@link IPnpmfileShim.hooks.afterAllResolved}. + */ +export interface ISubspacePnpmfileShimSettings { + semverPath: string; + workspaceProjects: Record; + subspaceProjects: Record; + userPnpmfilePath?: string; +} + /** * The `context` parameter passed to {@link IPnpmfile.hooks.readPackage}, as defined by the * pnpmfile API contract. @@ -27,12 +46,13 @@ export interface IPnpmfileShimSettings { export interface IPnpmfileContext { log: (message: string) => void; pnpmfileShimSettings?: IPnpmfileShimSettings; + subspacePnpmfileShimSettings?: ISubspacePnpmfileShimSettings; } /** * The `log` parameter passed to {@link IPnpmfile.hooks.filterLog}. */ -export type IPnpmLog = LogBase & { +export type IPnpmLog = pnpmKitV8.logger.LogBase & { [key: string]: unknown; }; diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmLinkManager.ts b/libraries/rush-lib/src/logic/pnpm/PnpmLinkManager.ts index 43599b6c748..b020bea25af 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmLinkManager.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmLinkManager.ts @@ -1,13 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as crypto from 'crypto'; +import * as path from 'node:path'; +import * as crypto from 'node:crypto'; + import uriEncode from 'strict-uri-encode'; import pnpmLinkBins from '@pnpm/link-bins'; import * as semver from 'semver'; -import { depPathToFilename } from 'dependency-path'; -import colors from 'colors/safe'; import { AlreadyReportedError, @@ -16,12 +15,20 @@ import { InternalError, Path } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; import { BaseLinkManager } from '../base/BaseLinkManager'; import { BasePackage } from '../base/BasePackage'; -import { RushConstants } from '../../logic/RushConstants'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; -import { PnpmShrinkwrapFile, IPnpmShrinkwrapDependencyYaml } from './PnpmShrinkwrapFile'; +import { RushConstants } from '../RushConstants'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import { + PnpmShrinkwrapFile, + type IPnpmShrinkwrapDependencyYaml, + type IPnpmVersionSpecifier, + normalizePnpmVersionSpecifier +} from './PnpmShrinkwrapFile'; +import type { Subspace } from '../../api/Subspace'; +import { IS_WINDOWS } from '../../utilities/executionUtilities'; // special flag for debugging, will print extra diagnostic information, // but comes with performance cost @@ -32,15 +39,13 @@ export class PnpmLinkManager extends BaseLinkManager { this._rushConfiguration.packageManagerToolVersion ); - /** - * @override - */ - public async createSymlinksForProjects(force: boolean): Promise { + public override async createSymlinksForProjectsAsync(force: boolean): Promise { const useWorkspaces: boolean = this._rushConfiguration.pnpmOptions && this._rushConfiguration.pnpmOptions.useWorkspaces; if (useWorkspaces) { + // eslint-disable-next-line no-console console.log( - colors.red( + Colorize.red( 'Linking is not supported when using workspaces. Run "rush install" or "rush update" ' + 'to restore project node_modules folders.' ) @@ -48,30 +53,33 @@ export class PnpmLinkManager extends BaseLinkManager { throw new AlreadyReportedError(); } - await super.createSymlinksForProjects(force); + await super.createSymlinksForProjectsAsync(force); } - protected async _linkProjects(): Promise { + protected async _linkProjectsAsync(): Promise { if (this._rushConfiguration.projects.length > 0) { + const subspace: Subspace = this._rushConfiguration.defaultSubspace; // Use shrinkwrap from temp as the committed shrinkwrap may not always be up to date // See https://github.com/microsoft/rushstack/issues/1273#issuecomment-492779995 const pnpmShrinkwrapFile: PnpmShrinkwrapFile | undefined = PnpmShrinkwrapFile.loadFromFile( - this._rushConfiguration.tempShrinkwrapFilename + subspace.getTempShrinkwrapFilename(), + { subspaceHasNoProjects: subspace.getProjects().length === 0 } ); if (!pnpmShrinkwrapFile) { throw new InternalError( - `Cannot load shrinkwrap at "${this._rushConfiguration.tempShrinkwrapFilename}"` + `Cannot load shrinkwrap at "${this._rushConfiguration.defaultSubspace.getTempShrinkwrapFilename()}"` ); } for (const rushProject of this._rushConfiguration.projects) { - await this._linkProject(rushProject, pnpmShrinkwrapFile); + await this._linkProjectAsync(rushProject, pnpmShrinkwrapFile); } } else { + // eslint-disable-next-line no-console console.log( - colors.yellow( - '\nWarning: Nothing to do. Please edit rush.json and add at least one project' + + Colorize.yellow( + `\nWarning: Nothing to do. Please edit ${RushConstants.rushJsonFilename} and add at least one project` + ' to the "projects" section.\n' ) ); @@ -83,10 +91,11 @@ export class PnpmLinkManager extends BaseLinkManager { * @param project The local project that we will create symlinks for * @param rushLinkJson The common/temp/rush-link.json output file */ - private async _linkProject( + private async _linkProjectAsync( project: RushConfigurationProject, pnpmShrinkwrapFile: PnpmShrinkwrapFile ): Promise { + // eslint-disable-next-line no-console console.log(`\nLINKING: ${project.packageName}`); // first, read the temp package.json information @@ -209,18 +218,21 @@ export class PnpmLinkManager extends BaseLinkManager { // e.g.: // '' [empty string] + // _@types+node@14.18.36 // _jsdom@11.12.0 // _2a665c89609864b4e75bc5365d7f8f56 + // (@types/node@14.18.36) const folderNameSuffix: string = tarballEntry && tarballEntry.length < tempProjectDependencyKey.length ? tempProjectDependencyKey.slice(tarballEntry.length) : ''; // e.g.: C:\wbt\common\temp\node_modules\.local\C%3A%2Fwbt%2Fcommon%2Ftemp%2Fprojects%2Fapi-documenter.tgz\node_modules - const pathToLocalInstallation: string = this._getPathToLocalInstallation( + const pathToLocalInstallation: string = await this._getPathToLocalInstallationAsync( tarballEntry, absolutePathToTgzFile, - folderNameSuffix + folderNameSuffix, + tempProjectDependencyKey ); const parentShrinkwrapEntry: IPnpmShrinkwrapDependencyYaml | undefined = @@ -263,22 +275,26 @@ export class PnpmLinkManager extends BaseLinkManager { await pnpmShrinkwrapFile.getProjectShrinkwrap(project)!.updateProjectShrinkwrapAsync(); - PnpmLinkManager._createSymlinksForTopLevelProject(localPackage); + await PnpmLinkManager._createSymlinksForTopLevelProjectAsync(localPackage); // Also symlink the ".bin" folder const projectFolder: string = path.join(localPackage.folderPath, 'node_modules'); const projectBinFolder: string = path.join(localPackage.folderPath, 'node_modules', '.bin'); await pnpmLinkBins(projectFolder, projectBinFolder, { - warn: (msg: string) => console.warn(colors.yellow(msg)) + warn: (msg: string) => { + // eslint-disable-next-line no-console + console.warn(Colorize.yellow(msg)); + } }); } - private _getPathToLocalInstallation( + private async _getPathToLocalInstallationAsync( tarballEntry: string, absolutePathToTgzFile: string, - folderSuffix: string - ): string { + folderSuffix: string, + tempProjectDependencyKey: string + ): Promise { if (this._pnpmVersion.major === 6) { // PNPM 6 changed formatting to replace all ':' and '/' chars with '+'. Additionally, folder names > 120 // are trimmed and hashed. NOTE: PNPM internally uses fs.realpath.native, which will cause additional @@ -300,6 +316,59 @@ export class PnpmLinkManager extends BaseLinkManager { .digest('hex')}`; } + return path.join( + this._rushConfiguration.commonTempFolder, + RushConstants.nodeModulesFolderName, + '.pnpm', + folderName, + RushConstants.nodeModulesFolderName + ); + } else if (this._pnpmVersion.major >= 10) { + const pnpmKitV10: typeof import('@rushstack/rush-pnpm-kit-v10') = await import( + '@rushstack/rush-pnpm-kit-v10' + ); + + // project@file+projects+presentation-integration-tests.tgz_jsdom@11.12.0 + // The second parameter is max length of virtual store dir, + // for v10 default is 120 on Linux/MacOS and 60 on Windows https://pnpm.io/next/settings#virtualstoredirmaxlength + // TODO Read virtual-store-dir-max-length from .npmrc + const folderName: string = pnpmKitV10.dependencyPath.depPathToFilename( + tempProjectDependencyKey, + IS_WINDOWS ? 60 : 120 + ); + return path.join( + this._rushConfiguration.commonTempFolder, + RushConstants.nodeModulesFolderName, + '.pnpm', + folderName, + RushConstants.nodeModulesFolderName + ); + } else if (this._pnpmVersion.major >= 9) { + const pnpmKitV9: typeof import('@rushstack/rush-pnpm-kit-v9') = await import( + '@rushstack/rush-pnpm-kit-v9' + ); + + // project@file+projects+presentation-integration-tests.tgz_jsdom@11.12.0 + // The second parameter is max length of virtual store dir, for v9 default is 120 https://pnpm.io/9.x/npmrc#virtual-store-dir-max-length + // TODO Read virtual-store-dir-max-length from .npmrc + const folderName: string = pnpmKitV9.dependencyPath.depPathToFilename(tempProjectDependencyKey, 120); + return path.join( + this._rushConfiguration.commonTempFolder, + RushConstants.nodeModulesFolderName, + '.pnpm', + folderName, + RushConstants.nodeModulesFolderName + ); + } else if (this._pnpmVersion.major >= 8) { + const pnpmKitV8: typeof import('@rushstack/rush-pnpm-kit-v8') = await import( + '@rushstack/rush-pnpm-kit-v8' + ); + // PNPM 8 changed the local path format again and the hashing algorithm, and + // is now using the scoped '@pnpm/dependency-path' package + // See https://github.com/pnpm/pnpm/releases/tag/v8.0.0 + // e.g.: + // file+projects+presentation-integration-tests.tgz_jsdom@11.12.0 + const folderName: string = pnpmKitV8.dependencyPath.depPathToFilename(`${tarballEntry}${folderSuffix}`); return path.join( this._rushConfiguration.commonTempFolder, RushConstants.nodeModulesFolderName, @@ -308,6 +377,7 @@ export class PnpmLinkManager extends BaseLinkManager { RushConstants.nodeModulesFolderName ); } else if (this._pnpmVersion.major >= 7) { + const { depPathToFilename } = await import('dependency-path'); // PNPM 7 changed the local path format again and the hashing algorithm // See https://github.com/pnpm/pnpm/releases/tag/v7.0.0 // e.g.: @@ -371,10 +441,10 @@ export class PnpmLinkManager extends BaseLinkManager { // read the version number from the shrinkwrap entry and return if no version is specified // and the dependency is optional - const version: string | undefined = isOptional + const versionSpecifier: IPnpmVersionSpecifier | undefined = isOptional ? (parentShrinkwrapEntry.optionalDependencies || {})[dependencyName] : (parentShrinkwrapEntry.dependencies || {})[dependencyName]; - if (!version) { + if (!versionSpecifier) { if (!isOptional) { throw new InternalError( `Cannot find shrinkwrap entry dependency "${dependencyName}" for temp project: ` + @@ -385,6 +455,7 @@ export class PnpmLinkManager extends BaseLinkManager { } const newLocalFolderPath: string = path.join(localPackage.folderPath, 'node_modules', dependencyName); + const version: string = normalizePnpmVersionSpecifier(versionSpecifier); const newLocalPackage: BasePackage = BasePackage.createLinkedPackage( dependencyName, version, diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts b/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts index 2b892ced43d..f5c3b0481d5 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts @@ -1,10 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { JsonFile, JsonObject, JsonSchema } from '@rushstack/node-core-library'; +import * as path from 'node:path'; + +import { JsonFile, type JsonObject, Path } from '@rushstack/node-core-library'; +import { NonProjectConfigurationFile } from '@rushstack/heft-config-file'; +import { ConsoleTerminalProvider, Terminal } from '@rushstack/terminal'; import { - IPackageManagerOptionsJsonBase, + type IPackageManagerOptionsJsonBase, PackageManagerOptionsConfigurationBase } from '../base/BasePackageManagerOptionsConfiguration'; import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration'; @@ -14,10 +18,50 @@ import schemaJson from '../../schemas/pnpm-config.schema.json'; * This represents the available PNPM store options * @public */ -export type PnpmStoreOptions = 'local' | 'global'; +export type PnpmStoreLocation = 'local' | 'global'; + +/** + * @deprecated Use {@link PnpmStoreLocation} instead + * @public + */ +export type PnpmStoreOptions = PnpmStoreLocation; + +/** + * Possible values for the `resolutionMode` setting in Rush's pnpm-config.json file. + * @remarks + * These modes correspond to PNPM's `resolution-mode` values, which are documented here: + * {@link https://pnpm.io/npmrc#resolution-mode} + * + * @public + */ +export type PnpmResolutionMode = 'highest' | 'time-based' | 'lowest-direct'; + +/** + * Possible values for the `trustPolicy` setting in Rush's pnpm-config.json file. + * @remarks + * These values correspond to PNPM's `trust-policy` setting, which is documented here: + * {@link https://pnpm.io/settings#trustpolicy} + * + * @public + */ +export type PnpmTrustPolicy = 'no-downgrade' | 'off'; + +/** + * Possible values for the `pnpmLockfilePolicies` setting in Rush's pnpm-config.json file. + * @public + */ +export interface IPnpmLockfilePolicies { + /** + * Forbid sha1 hashes in `pnpm-lock.yaml` + */ + disallowInsecureSha1?: { + enabled: boolean; + exemptPackageVersions: Record; + }; +} /** - * @beta + * @public */ export interface IPnpmPeerDependencyRules { ignoreMissing?: string[]; @@ -25,12 +69,18 @@ export interface IPnpmPeerDependencyRules { allowedVersions?: Record; } +/** + * @public + */ export interface IPnpmPeerDependenciesMeta { [packageName: string]: { optional?: boolean; }; } +/** + * @public + */ export interface IPnpmPackageExtension { dependencies?: Record; optionalDependencies?: Record; @@ -43,10 +93,11 @@ export interface IPnpmPackageExtension { * @internal */ export interface IPnpmOptionsJson extends IPackageManagerOptionsJsonBase { + $schema?: string; /** * {@inheritDoc PnpmOptionsConfiguration.pnpmStore} */ - pnpmStore?: PnpmStoreOptions; + pnpmStore?: PnpmStoreLocation; /** * {@inheritDoc PnpmOptionsConfiguration.strictPeerDependencies} */ @@ -75,6 +126,18 @@ export interface IPnpmOptionsJson extends IPackageManagerOptionsJsonBase { * {@inheritDoc PnpmOptionsConfiguration.globalNeverBuiltDependencies} */ globalNeverBuiltDependencies?: string[]; + /** + * {@inheritDoc PnpmOptionsConfiguration.globalOnlyBuiltDependencies} + */ + globalOnlyBuiltDependencies?: string[]; + /** + * {@inheritDoc PnpmOptionsConfiguration.globalAllowBuilds} + */ + globalAllowBuilds?: Record; + /** + * {@inheritDoc PnpmOptionsConfiguration.globalIgnoredOptionalDependencies} + */ + globalIgnoredOptionalDependencies?: string[]; /** * {@inheritDoc PnpmOptionsConfiguration.globalAllowedDeprecatedVersions} */ @@ -87,6 +150,54 @@ export interface IPnpmOptionsJson extends IPackageManagerOptionsJsonBase { * {@inheritDoc PnpmOptionsConfiguration.unsupportedPackageJsonSettings} */ unsupportedPackageJsonSettings?: unknown; + /** + * {@inheritDoc PnpmOptionsConfiguration.resolutionMode} + */ + resolutionMode?: PnpmResolutionMode; + /** + * {@inheritDoc PnpmOptionsConfiguration.autoInstallPeers} + */ + autoInstallPeers?: boolean; + /** + * {@inheritDoc PnpmOptionsConfiguration.minimumReleaseAgeMinutes} + */ + minimumReleaseAgeMinutes?: number; + /** + * @deprecated Use `minimumReleaseAgeMinutes` instead. + */ + minimumReleaseAge?: number; + /** + * {@inheritDoc PnpmOptionsConfiguration.minimumReleaseAgeExclude} + */ + minimumReleaseAgeExclude?: string[]; + /** + * {@inheritDoc PnpmOptionsConfiguration.trustPolicy} + */ + trustPolicy?: PnpmTrustPolicy; + /** + * {@inheritDoc PnpmOptionsConfiguration.trustPolicyExclude} + */ + trustPolicyExclude?: string[]; + /** + * {@inheritDoc PnpmOptionsConfiguration.trustPolicyIgnoreAfterMinutes} + */ + trustPolicyIgnoreAfterMinutes?: number; + /** + * {@inheritDoc PnpmOptionsConfiguration.alwaysInjectDependenciesFromOtherSubspaces} + */ + alwaysInjectDependenciesFromOtherSubspaces?: boolean; + /** + * {@inheritDoc PnpmOptionsConfiguration.alwaysFullInstall} + */ + alwaysFullInstall?: boolean; + /** + * {@inheritDoc PnpmOptionsConfiguration.pnpmLockfilePolicies} + */ + pnpmLockfilePolicies?: IPnpmLockfilePolicies; + /** + * {@inheritDoc PnpmOptionsConfiguration.globalCatalogs} + */ + globalCatalogs?: Record>; } /** @@ -101,9 +212,8 @@ export interface IPnpmOptionsJson extends IPackageManagerOptionsJsonBase { * @public */ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfigurationBase { - private static _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); - private readonly _json: JsonObject; + private readonly _commonTempFolder: string; private _globalPatchedDependencies: Record | undefined; /** @@ -114,7 +224,33 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration * - local: Use the standard Rush store path: common/temp/pnpm-store * - global: Use PNPM's global store path */ - public readonly pnpmStore: PnpmStoreOptions; + public readonly pnpmStore: PnpmStoreLocation; + + /** + * This setting determines how PNPM chooses version numbers during `rush update`. + * + * @remarks + * For example, suppose `lib-x@3.0.0` depends on `"lib-y": "^1.2.3"` whose latest major + * releases are `1.8.9` and `2.3.4`. The resolution mode `lowest-direct` might choose + * `lib-y@1.2.3`, wheres `highest` will choose 1.8.9, and `time-based` will pick the + * highest compatible version at the time when `lib-x@3.0.0` itself was published (ensuring + * that the version could have been tested by the maintainer of "lib-x"). For local workspace + * projects, `time-based` instead works like `lowest-direct`, avoiding upgrades unless + * they are explicitly requested. Although `time-based` is the most robust option, it may be + * slightly slower with registries such as npmjs.com that have not implemented an optimization. + * + * IMPORTANT: Be aware that PNPM 8.0.0 initially defaulted to `lowest-direct` instead of + * `highest`, but PNPM reverted this decision in 8.6.12 because it caused confusion for users. + * Rush version 5.106.0 and newer avoids this confusion by consistently defaulting to + * `highest` when `resolutionMode` is not explicitly set in pnpm-config.json or .npmrc, + * regardless of your PNPM version. + * + * PNPM documentation: https://pnpm.io/npmrc#resolution-mode + * + * Possible values are: `highest`, `time-based`, and `lowest-direct`. + * The default is `highest`. + */ + public readonly resolutionMode: PnpmResolutionMode | undefined; /** * The path for PNPM to use as the store directory. @@ -163,6 +299,96 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration */ public readonly useWorkspaces: boolean; + /** + * When true, any missing non-optional peer dependencies are automatically installed. + * + * @remarks + * The default value is same as PNPM default value. (In PNPM 8.x, this value is true) + */ + public readonly autoInstallPeers: boolean | undefined; + + /** + * The minimum number of minutes that must pass after a version is published before pnpm will install it. + * This setting helps reduce the risk of installing compromised packages, as malicious releases are typically + * discovered and removed within a short time frame. + * + * @remarks + * (SUPPORTED ONLY IN PNPM 10.16.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#minimumreleaseage + * + * The default value is 0 (disabled). + */ + public readonly minimumReleaseAgeMinutes: number | undefined; + + /** + * @deprecated Use {@link PnpmOptionsConfiguration.minimumReleaseAgeMinutes} instead. + */ + public get minimumReleaseAge(): number | undefined { + return this.minimumReleaseAgeMinutes; + } + + /** + * List of package names or patterns that are excluded from the minimumReleaseAge check. + * These packages will always install the newest version immediately, even if minimumReleaseAgeMinutes is set. + * + * @remarks + * (SUPPORTED ONLY IN PNPM 10.16.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#minimumreleaseageexclude + * + * Example: ["webpack", "react", "\@myorg/*"] + */ + public readonly minimumReleaseAgeExclude: string[] | undefined; + + /** + * The trust policy controls whether pnpm should block installation of package versions where the + * trust level has decreased (e.g., a package previously published with provenance is now published + * without it). Setting this to `"no-downgrade"` enables the protection. + * + * @remarks + * (SUPPORTED ONLY IN PNPM 10.21.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#trustpolicy + */ + public readonly trustPolicy: PnpmTrustPolicy | undefined; + + /** + * List of package names or patterns that are excluded from the trust policy check. + * These packages will be allowed to install even if their trust level has decreased. + * + * @remarks + * (SUPPORTED ONLY IN PNPM 10.22.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#trustpolicyexclude + * + * Example: ["webpack", "react", "\@myorg/*"] + */ + public readonly trustPolicyExclude: string[] | undefined; + + /** + * The number of minutes after which pnpm will ignore trust level downgrades. Packages published + * longer ago than this threshold will not be blocked even if their trust level has decreased. + * + * @remarks + * (SUPPORTED ONLY IN PNPM 10.27.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#trustpolicyignoreafter + */ + public readonly trustPolicyIgnoreAfterMinutes: number | undefined; + + /** + * If true, then `rush update` add injected install options for all cross-subspace + * workspace dependencies, to avoid subspace doppelganger issue. + * + * Here, the injected install refers to PNPM's PNPM's "injected dependencies" + * feature. Learn more: https://pnpm.io/package_json#dependenciesmeta + * + * @remarks + * The default value is false. + */ + public readonly alwaysInjectDependenciesFromOtherSubspaces: boolean | undefined; + /** * The "globalOverrides" setting provides a simple mechanism for overriding version selections * for all dependencies of all projects in the monorepo workspace. The settings are copied @@ -219,6 +445,48 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration */ public readonly globalNeverBuiltDependencies: string[] | undefined; + /** + * The `globalOnlyBuiltDependencies` setting specifies an allowlist of dependencies that are permitted + * to run build scripts (`preinstall`, `install`, and `postinstall` lifecycle events). This is the inverse + * of `globalNeverBuiltDependencies`. In PNPM 10.x, build scripts are disabled by default for security, + * so this setting is required to explicitly permit specific packages to run their build scripts. + * The settings are copied into the `pnpm.onlyBuiltDependencies` field of the `common/temp/package.json` + * file that is generated by Rush during installation. + * + * (SUPPORTED ONLY IN PNPM 10.1.0 AND NEWER; replaced by `globalAllowBuilds` in PNPM 11.0.0) + * + * PNPM documentation: https://pnpm.io/package_json#pnpmonlybuiltdependencies + */ + public readonly globalOnlyBuiltDependencies: string[] | undefined; + + /** + * The `globalAllowBuilds` setting controls which packages are allowed to run build scripts + * (`preinstall`, `install`, and `postinstall` lifecycle events). A value of `true` means the + * package is allowed to run build scripts; `false` means it is explicitly denied. + * Packages with build scripts not listed here will cause pnpm to fail with ERR_PNPM_IGNORED_BUILDS. + * The settings are written to the `allowBuilds` field of the `pnpm-workspace.yaml` file + * that is generated by Rush during installation. + * + * (SUPPORTED ONLY IN PNPM 11.0.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/settings#allowbuilds + */ + public readonly globalAllowBuilds: Record | undefined; + + /** + * The ignoredOptionalDependencies setting allows you to exclude certain optional dependencies from being installed + * during the Rush installation process. This can be useful when optional dependencies are not required or are + * problematic in specific environments (e.g., dependencies with incompatible binaries or platform-specific requirements). + * The listed dependencies will be treated as though they are missing, even if other packages specify them as optional + * dependencies. The settings are copied into the pnpm.ignoredOptionalDependencies field of the common/temp/package.json + * file that is generated by Rush during installation. + * + * (SUPPORTED ONLY IN PNPM 9.0.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/package_json#pnpmignoredoptionaldependencies + */ + public readonly globalIgnoredOptionalDependencies: string[] | undefined; + /** * The `globalAllowedDeprecatedVersions` setting suppresses installation warnings for package * versions that the NPM registry reports as being deprecated. This is useful if the @@ -249,6 +517,32 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration public readonly jsonFilename: string | undefined; + /** + * The `pnpmLockfilePolicies` setting defines the policies that govern the `pnpm-lock.yaml` file. + */ + public readonly pnpmLockfilePolicies: IPnpmLockfilePolicies | undefined; + + /** + * (EXPERIMENTAL) If "true", then filtered installs ("rush install --to my-project") + * will be disregarded, instead always performing a full installation of the lockfile. + * This setting is primarily useful with Rush subspaces which enable filtering across + * multiple lockfiles, if filtering may be inefficient or undesirable for certain lockfiles. + * + * The default value is false. + */ + /*[LINE "DEMO"]*/ + public readonly alwaysFullInstall: boolean | undefined; + + /** + * The `globalCatalogs` setting provides named catalogs for organizing dependency versions. + * Each catalog can be referenced using the `catalog:catalogName` protocol in package.json files + * (e.g., `catalog:react18`). The settings are written to the `catalogs` field of the + * `pnpm-workspace.yaml` file that is generated by Rush during installation. + * + * PNPM documentation: https://pnpm.io/catalogs + */ + public readonly globalCatalogs: Record> | undefined; + /** * (GENERATED BY RUSH-PNPM PATCH-COMMIT) When modifying this property, make sure you know what you are doing. * @@ -265,6 +559,7 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration private constructor(json: IPnpmOptionsJson, commonTempFolder: string, jsonFilename?: string) { super(json); this._json = json; + this._commonTempFolder = commonTempFolder; this.jsonFilename = jsonFilename; this.pnpmStore = json.pnpmStore || 'local'; if (EnvironmentConfiguration.pnpmStorePathOverride) { @@ -282,21 +577,57 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration this.globalPeerDependencyRules = json.globalPeerDependencyRules; this.globalPackageExtensions = json.globalPackageExtensions; this.globalNeverBuiltDependencies = json.globalNeverBuiltDependencies; + if (json.globalOnlyBuiltDependencies !== undefined && json.globalAllowBuilds !== undefined) { + throw new Error( + 'The "globalOnlyBuiltDependencies" and "globalAllowBuilds" settings cannot both be specified' + + ' in pnpm-config.json. Use "globalAllowBuilds" for PNPM 11.0.0 and newer.' + ); + } + this.globalOnlyBuiltDependencies = json.globalOnlyBuiltDependencies; + this.globalAllowBuilds = json.globalAllowBuilds; + this.globalIgnoredOptionalDependencies = json.globalIgnoredOptionalDependencies; this.globalAllowedDeprecatedVersions = json.globalAllowedDeprecatedVersions; this.unsupportedPackageJsonSettings = json.unsupportedPackageJsonSettings; this._globalPatchedDependencies = json.globalPatchedDependencies; + this.resolutionMode = json.resolutionMode; + this.autoInstallPeers = json.autoInstallPeers; + + if (json.minimumReleaseAge !== undefined && json.minimumReleaseAgeMinutes !== undefined) { + throw new Error( + 'The "minimumReleaseAge" setting is deprecated. Use "minimumReleaseAgeMinutes" instead.' + + ' Both settings cannot be specified together in pnpm-config.json.' + ); + } + this.minimumReleaseAgeMinutes = json.minimumReleaseAgeMinutes ?? json.minimumReleaseAge; + + this.minimumReleaseAgeExclude = json.minimumReleaseAgeExclude; + this.trustPolicy = json.trustPolicy; + this.trustPolicyExclude = json.trustPolicyExclude; + this.trustPolicyIgnoreAfterMinutes = json.trustPolicyIgnoreAfterMinutes; + this.alwaysInjectDependenciesFromOtherSubspaces = json.alwaysInjectDependenciesFromOtherSubspaces; + this.alwaysFullInstall = json.alwaysFullInstall; + this.pnpmLockfilePolicies = json.pnpmLockfilePolicies; + this.globalCatalogs = json.globalCatalogs; } /** @internal */ public static loadFromJsonFileOrThrow( - jsonFilename: string, + jsonFilePath: string, commonTempFolder: string ): PnpmOptionsConfiguration { - const pnpmOptionJson: IPnpmOptionsJson = JsonFile.loadAndValidate( - jsonFilename, - PnpmOptionsConfiguration._jsonSchema + // TODO: plumb through the terminal + const terminal: Terminal = new Terminal(new ConsoleTerminalProvider()); + + const pnpmOptionsConfigFile: NonProjectConfigurationFile = + new NonProjectConfigurationFile({ + jsonSchemaObject: schemaJson + }); + const pnpmConfigJson: IPnpmOptionsJson = pnpmOptionsConfigFile.loadConfigurationFile( + terminal, + jsonFilePath ); - return new PnpmOptionsConfiguration(pnpmOptionJson || {}, commonTempFolder, jsonFilename); + pnpmConfigJson.$schema = pnpmOptionsConfigFile.getSchemaPropertyOriginalValue(pnpmConfigJson); + return new PnpmOptionsConfiguration(pnpmConfigJson || {}, commonTempFolder, jsonFilePath); } /** @internal */ @@ -307,12 +638,89 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration return new PnpmOptionsConfiguration(json, commonTempFolder); } + private _getJsonFilenameOrThrow(): string { + if (!this.jsonFilename) { + throw new Error('Cannot save pnpm-config.json because no jsonFilename was provided.'); + } + + return this.jsonFilename; + } + /** * Updates patchedDependencies field of the PNPM options in the common/config/rush/pnpm-config.json file. + * + * @remarks + * When running "pnpm patch-commit"/"pnpm patch-remove" (pnpm 9 and newer), pnpm rewrites the + * pre-existing entries using absolute paths pointing into the common/temp folder. Normalize any such + * value back to a path relative to the common/temp folder (e.g. "patches/example\@1.0.0.patch") so the location + * of the local checkout does not leak into pnpm-config.json. */ public updateGlobalPatchedDependencies(patchedDependencies: Record | undefined): void { + if (patchedDependencies) { + const normalized: Record = {}; + for (const [dependency, patchPath] of Object.entries(patchedDependencies)) { + normalized[dependency] = + path.isAbsolute(patchPath) && Path.isUnder(patchPath, this._commonTempFolder) + ? Path.convertToSlashes(path.relative(this._commonTempFolder, patchPath)) + : patchPath; + } + patchedDependencies = normalized; + } + this._globalPatchedDependencies = patchedDependencies; this._json.globalPatchedDependencies = patchedDependencies; + JsonFile.save(this._json, this._getJsonFilenameOrThrow(), { + updateExistingFile: true, + ignoreUndefinedValues: true + }); + } + + /** + * Updates globalOnlyBuiltDependencies field of the PNPM options in the common/config/rush/pnpm-config.json file. + * + * @deprecated Use {@link PnpmOptionsConfiguration.updateGlobalOnlyBuiltDependenciesAsync} instead. + */ + public updateGlobalOnlyBuiltDependencies(onlyBuiltDependencies: string[] | undefined): void { + this._json.globalOnlyBuiltDependencies = onlyBuiltDependencies; + if (this.jsonFilename) { + JsonFile.save(this._json, this.jsonFilename, { + updateExistingFile: true, + ignoreUndefinedValues: true + }); + } + } + + /** + * Updates globalOnlyBuiltDependencies field of the PNPM options in the common/config/rush/pnpm-config.json file. + */ + public async updateGlobalOnlyBuiltDependenciesAsync( + onlyBuiltDependencies: string[] | undefined + ): Promise { + this._json.globalOnlyBuiltDependencies = onlyBuiltDependencies; + await JsonFile.saveAsync(this._json, this._getJsonFilenameOrThrow(), { + updateExistingFile: true, + ignoreUndefinedValues: true + }); + } + + /** + * Updates globalCatalogs field of the PNPM options in the common/config/rush/pnpm-config.json file. + */ + public async updateGlobalCatalogsAsync( + catalogs: Record> | undefined + ): Promise { + this._json.globalCatalogs = catalogs; + await JsonFile.saveAsync(this._json, this._getJsonFilenameOrThrow(), { + updateExistingFile: true, + ignoreUndefinedValues: true + }); + } + + /** + * Updates globalAllowBuilds field of the PNPM options in the common/config/rush/pnpm-config.json file. + */ + public updateGlobalAllowBuilds(allowBuilds: Record | undefined): void { + this._json.globalAllowBuilds = allowBuilds; if (this.jsonFilename) { JsonFile.save(this._json, this.jsonFilename, { updateExistingFile: true }); } diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmProjectShrinkwrapFile.ts b/libraries/rush-lib/src/logic/pnpm/PnpmProjectShrinkwrapFile.ts index e9d5abdf504..19e3899804c 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmProjectShrinkwrapFile.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmProjectShrinkwrapFile.ts @@ -1,13 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as crypto from 'crypto'; +import * as crypto from 'node:crypto'; + import { InternalError, JsonFile } from '@rushstack/node-core-library'; import { BaseProjectShrinkwrapFile } from '../base/BaseProjectShrinkwrapFile'; -import { PnpmShrinkwrapFile, IPnpmShrinkwrapDependencyYaml } from './PnpmShrinkwrapFile'; -import { DependencySpecifier } from '../DependencySpecifier'; +import type { + PnpmShrinkwrapFile, + IPnpmShrinkwrapDependencyYaml, + IPnpmVersionSpecifier +} from './PnpmShrinkwrapFile'; +import type { DependencySpecifier } from '../DependencySpecifier'; import { RushConstants } from '../RushConstants'; +import type { Subspace } from '../../api/Subspace'; /** * @@ -68,8 +74,10 @@ export class PnpmProjectShrinkwrapFile extends BaseProjectShrinkwrapFile | undefined { // Obtain the workspace importer from the shrinkwrap, which lists resolved dependencies + const subspace: Subspace = this.project.subspace; + const importerKey: string = this.shrinkwrapFile.getImporterKeyByPath( - this.project.rushConfiguration.commonTempFolder, + subspace.getSubspaceTempFolderPath(), this.project.projectFolder ); @@ -89,7 +97,7 @@ export class PnpmProjectShrinkwrapFile extends BaseProjectShrinkwrapFile, name: string, - version: string, + version: IPnpmVersionSpecifier, parentShrinkwrapEntry: IPnpmShrinkwrapDependencyYaml, throwIfShrinkwrapEntryMissing: boolean = true ): void { @@ -135,36 +143,33 @@ export class PnpmProjectShrinkwrapFile extends BaseProjectShrinkwrapFile(obj: Record, mapper: (val: T, key: string) => U): Record { + const result: Record = {}; + for (const [key, value] of Object.entries(obj)) { + result[key] = mapper(value, key); + } + return result; +} + +/** + * Convert lockfile v9 object to standard lockfile object. + * + * This function will mutate the lockfile object. It will: + * 1. Ensure importers['.'] exists. + * 2. Merge snapshots and packages into packages. + * 3. Extract specifier from importers['xxx'] into the specifiers field. + */ +export function convertLockfileV9ToLockfileObject(lockfile: LockfileFileV9): Lockfile { + const { importers, ...rest } = convertFromLockfileFileMutable(lockfile); + + const packages: PackageSnapshots = {}; + for (const [depPath, pkg] of Object.entries(lockfile.snapshots ?? {})) { + const pkgId: string = pnpmKitV9.dependencyPath.removeSuffix(depPath); + packages[depPath as DepPath] = Object.assign(pkg, lockfile.packages?.[pkgId]); + } + return { + ...rest, + packages, + importers: mapValues(importers ?? {}, revertProjectSnapshot) + }; +} diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmShrinkwrapFile.ts b/libraries/rush-lib/src/logic/pnpm/PnpmShrinkwrapFile.ts index 50a849df7ff..515d416ea09 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmShrinkwrapFile.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmShrinkwrapFile.ts @@ -1,106 +1,197 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; +import crypto from 'node:crypto'; + import * as semver from 'semver'; -import crypto from 'crypto'; -import colors from 'colors/safe'; -import { FileSystem, AlreadyReportedError, Import, Path, IPackageJson } from '@rushstack/node-core-library'; +import type { + ProjectId, + Lockfile, + PackageSnapshot, + ProjectSnapshot, + LockfileFileV9, + ResolvedDependencies +} from '@pnpm/lockfile.types-900'; + +import { + FileSystem, + AlreadyReportedError, + Import, + Path, + type IPackageJson, + InternalError +} from '@rushstack/node-core-library'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; +import type { IReadonlyLookupByPath } from '@rushstack/lookup-by-path'; import { BaseShrinkwrapFile } from '../base/BaseShrinkwrapFile'; import { DependencySpecifier } from '../DependencySpecifier'; -import { RushConfiguration } from '../../api/RushConfiguration'; -import { IShrinkwrapFilePolicyValidatorOptions } from '../policy/ShrinkwrapFilePolicy'; +import type { RushConfiguration } from '../../api/RushConfiguration'; +import type { IShrinkwrapFilePolicyValidatorOptions } from '../policy/ShrinkwrapFilePolicy'; import { PNPM_SHRINKWRAP_YAML_FORMAT } from './PnpmYamlCommon'; import { RushConstants } from '../RushConstants'; -import { IExperimentsJson } from '../../api/ExperimentsConfiguration'; -import { DependencyType, PackageJsonDependency, PackageJsonEditor } from '../../api/PackageJsonEditor'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { IExperimentsJson } from '../../api/ExperimentsConfiguration'; +import { DependencyType, type PackageJsonDependency, PackageJsonEditor } from '../../api/PackageJsonEditor'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import { PnpmfileConfiguration } from './PnpmfileConfiguration'; import { PnpmProjectShrinkwrapFile } from './PnpmProjectShrinkwrapFile'; -import { PackageManagerOptionsConfigurationBase } from '../base/BasePackageManagerOptionsConfiguration'; +import type { PackageManagerOptionsConfigurationBase } from '../base/BasePackageManagerOptionsConfiguration'; import { PnpmOptionsConfiguration } from './PnpmOptionsConfiguration'; +import type { IPnpmfile, IPnpmfileContext } from './IPnpmfile'; +import type { Subspace } from '../../api/Subspace'; +import { CustomTipId, type CustomTipsConfiguration } from '../../api/CustomTipsConfiguration'; +import { convertLockfileV9ToLockfileObject } from './PnpmShrinkWrapFileConverters'; const yamlModule: typeof import('js-yaml') = Import.lazy('js-yaml', require); +const pnpmKitV8: typeof import('@rushstack/rush-pnpm-kit-v8') = Import.lazy( + '@rushstack/rush-pnpm-kit-v8', + require +); +const pnpmKitV9: typeof import('@rushstack/rush-pnpm-kit-v9') = Import.lazy( + '@rushstack/rush-pnpm-kit-v9', + require +); + +export enum ShrinkwrapFileMajorVersion { + V6 = 6, + V9 = 9 +} export interface IPeerDependenciesMetaYaml { optional?: boolean; } +export interface IDependenciesMetaYaml { + injected?: boolean; +} -export interface IPnpmShrinkwrapDependencyYaml { - /** Information about the resolved package */ - resolution?: { +export type IPnpmV7VersionSpecifier = string; +export interface IPnpmV8VersionSpecifier { + version: string; + specifier: string; +} +export type IPnpmV9VersionSpecifier = string; +export type IPnpmVersionSpecifier = + | IPnpmV7VersionSpecifier + | IPnpmV8VersionSpecifier + | IPnpmV9VersionSpecifier; + +export interface IPnpmShrinkwrapDependencyYaml extends Omit { + resolution: { + /** The directory this package should clone, for injected dependencies */ + directory?: string; /** The hash of the tarball, to ensure archive integrity */ - integrity: string; - /** The name of the tarball, if this was from a TGX file */ + integrity?: string; + /** The name of the tarball, if this was from a TGZ file */ tarball?: string; }; - /** The list of dependencies and the resolved version */ - dependencies?: { [dependency: string]: string }; - /** The list of optional dependencies and the resolved version */ - optionalDependencies?: { [dependency: string]: string }; - /** The list of peer dependencies and the resolved version */ - peerDependencies?: { [dependency: string]: string }; +} + +export type IPnpmShrinkwrapImporterYaml = ProjectSnapshot; + +export interface IPnpmShrinkwrapYaml extends Lockfile { /** - * Used to indicate optional peer dependencies, as described in this RFC: - * https://github.com/yarnpkg/rfcs/blob/master/accepted/0000-optional-peer-dependencies.md + * This interface represents the raw pnpm-lock.YAML file + * Example: + * { + * "dependencies": { + * "@rush-temp/project1": "file:./projects/project1.tgz" + * }, + * "packages": { + * "file:projects/library1.tgz": { + * "dependencies: { + * "markdown": "0.5.0" + * }, + * "name": "@rush-temp/library1", + * "resolution": { + * "tarball": "file:projects/library1.tgz" + * }, + * "version": "0.0.0" + * }, + * "markdown/0.5.0": { + * "resolution": { + * "integrity": "sha1-KCBbVlqK51kt4gdGPWY33BgnIrI=" + * } + * } + * }, + * "registry": "http://localhost:4873/", + * "shrinkwrapVersion": 3, + * "specifiers": { + * "@rush-temp/project1": "file:./projects/project1.tgz" + * } + * } */ - peerDependenciesMeta?: { [dependency: string]: IPeerDependenciesMetaYaml }; + /** The list of resolved version numbers for direct dependencies */ + dependencies?: Record; + /** The list of specifiers used to resolve direct dependency versions */ + specifiers?: Record; + /** URL of the registry which was used */ + registry?: string; } -export interface IPnpmShrinkwrapImporterYaml { - /** The list of resolved version numbers for direct dependencies */ - dependencies?: { [dependency: string]: string }; - /** The list of resolved version numbers for dev dependencies */ - devDependencies?: { [dependency: string]: string }; - /** The list of resolved version numbers for optional dependencies */ - optionalDependencies?: { [dependency: string]: string }; - /** The list of specifiers used to resolve dependency versions */ - specifiers: { [dependency: string]: string }; +export interface ILoadFromStringOptions { + subspaceHasNoProjects: boolean; } -/** - * This interface represents the raw pnpm-lock.YAML file - * Example: - * { - * "dependencies": { - * "@rush-temp/project1": "file:./projects/project1.tgz" - * }, - * "packages": { - * "file:projects/library1.tgz": { - * "dependencies: { - * "markdown": "0.5.0" - * }, - * "name": "@rush-temp/library1", - * "resolution": { - * "tarball": "file:projects/library1.tgz" - * }, - * "version": "0.0.0" - * }, - * "markdown/0.5.0": { - * "resolution": { - * "integrity": "sha1-KCBbVlqK51kt4gdGPWY33BgnIrI=" - * } - * } - * }, - * "registry": "http://localhost:4873/", - * "shrinkwrapVersion": 3, - * "specifiers": { - * "@rush-temp/project1": "file:./projects/project1.tgz" - * } - * } - */ -export interface IPnpmShrinkwrapYaml { - /** The list of resolved version numbers for direct dependencies */ - dependencies: { [dependency: string]: string }; - /** The list of importers for local workspace projects */ - importers: { [relativePath: string]: IPnpmShrinkwrapImporterYaml }; - /** The description of the solved graph */ - packages: { [dependencyVersion: string]: IPnpmShrinkwrapDependencyYaml }; - /** URL of the registry which was used */ - registry: string; - /** The list of specifiers used to resolve direct dependency versions */ - specifiers: { [dependency: string]: string }; +export interface ILoadFromFileOptions extends ILoadFromStringOptions { + withCaching?: boolean; +} + +export function parsePnpm9DependencyKey( + dependencyName: string, + versionSpecifier: IPnpmVersionSpecifier +): DependencySpecifier | undefined { + if (!versionSpecifier) { + return undefined; + } + + const dependencyKey: string = normalizePnpmVersionSpecifier(versionSpecifier); + + // Example: file:projects/project2 + // Example: project-2@file:projects/project2 + // Example: link:../projects/project1 + if (/(file|link):/.test(dependencyKey)) { + // If it starts with an NPM scheme such as "file:projects/my-app.tgz", we don't support that + return undefined; + } + + const { peersIndex } = pnpmKitV9.dependencyPath.indexOfPeersSuffix(dependencyKey); + if (peersIndex !== -1) { + // Remove peer suffix + const key: string = dependencyKey.slice(0, peersIndex); + + // Example: 7.26.0 + if (semver.valid(key)) { + return DependencySpecifier.parseWithCache(dependencyName, key); + } + } + + // Example: @babel/preset-env@7.26.0 -> name=@babel/preset-env version=7.26.0 + // Example: @babel/preset-env@7.26.0(peer@1.2.3) -> name=@babel/preset-env version=7.26.0 + // Example: https://github.com/jonschlinkert/pad-left/tarball/2.1.0 -> name=undefined version=undefined + // Example: pad-left@https://github.com/jonschlinkert/pad-left/tarball/2.1.0 -> name=pad-left nonSemverVersion=https://xxxx + // Example: pad-left@https://codeload.github.com/jonschlinkert/pad-left/tar.gz/7798d648225aa5 -> name=pad-left nonSemverVersion=https://xxxx + const dependency: import('@rushstack/rush-pnpm-kit-v9').dependencyPath.DependencyPath = + pnpmKitV9.dependencyPath.parse(dependencyKey); + + const name: string = dependency.name ?? dependencyName; + const version: string = dependency.version ?? dependency.nonSemverVersion ?? dependencyKey; + + // Example: https://xxxx/pad-left/tarball/2.1.0 + // Example: https://github.com/jonschlinkert/pad-left/tarball/2.1.0 + // Example: https://codeload.github.com/jonschlinkert/pad-left/tar.gz/7798d648225aa5d879660a37c408ab4675b65ac7 + if (/^https?:/.test(version)) { + return DependencySpecifier.parseWithCache(name, version); + } + + // Is it an alias for a different package? + if (name === dependencyName) { + // No, it's a regular dependency + return DependencySpecifier.parseWithCache(name, version); + } else { + // If the parsed package name is different from the dependencyName, then this is an NPM package alias + return DependencySpecifier.parseWithCache(dependencyName, `npm:${name}@${version}`); + } } /** @@ -111,12 +202,14 @@ export interface IPnpmShrinkwrapYaml { */ export function parsePnpmDependencyKey( dependencyName: string, - dependencyKey: string + versionSpecifier: IPnpmVersionSpecifier ): DependencySpecifier | undefined { - if (!dependencyKey) { + if (!versionSpecifier) { return undefined; } + const dependencyKey: string = normalizePnpmVersionSpecifier(versionSpecifier); + if (/^\w+:/.test(dependencyKey)) { // If it starts with an NPM scheme such as "file:projects/my-app.tgz", we don't support that return undefined; @@ -133,7 +226,12 @@ export function parsePnpmDependencyKey( // Example: "path.pkgs.visualstudio.com/@scope/depame/1.4.0" --> 0="@scope/depame" 1="1.4.0" // Example: "/isarray/2.0.1" --> 0="isarray" 1="2.0.1" // Example: "/sinon-chai/2.8.0/chai@3.5.0+sinon@1.17.7" --> 0="sinon-chai" 1="2.8.0/chai@3.5.0+sinon@1.17.7" - const packageNameMatch: RegExpMatchArray | null = /^[^\/]*\/((?:@[^\/]+\/)?[^\/]+)\/(.*)$/.exec( + // Example: "/typescript@5.1.6" --> 0=typescript 1="5.1.6" + // Example: 1.2.3_peer-dependency@.4.5.6 --> no match + // Example: 1.2.3_@scope+peer-dependency@.4.5.6 --> no match + // Example: 1.2.3(peer-dependency@.4.5.6) --> no match + // Example: 1.2.3(@scope/peer-dependency@.4.5.6) --> no match + const packageNameMatch: RegExpMatchArray | null = /^[^\/(]*\/((?:@[^\/(]+\/)?[^\/(]+)[\/@](.*)$/.exec( dependencyKey ); if (packageNameMatch) { @@ -153,7 +251,8 @@ export function parsePnpmDependencyKey( // Example: "23.6.0_babel-core@6.26.3" --> "23.6.0" // Example: "2.8.0/chai@3.5.0+sinon@1.17.7" --> "2.8.0" - const versionMatch: RegExpMatchArray | null = /^([^\/_]+)[\/_]/.exec(parsedInstallPath); + // Example: "0.53.1(@types/node@14.18.36)" --> "0.53.1" + const versionMatch: RegExpMatchArray | null = /^([^\(\/_]+)[(\/_]/.exec(parsedInstallPath); if (versionMatch) { parsedVersionPart = versionMatch[1]; } else { @@ -179,7 +278,10 @@ export function parsePnpmDependencyKey( // git@bitbucket.com+abc/def/188ed64efd5218beda276e02f2277bf3a6b745b2 // bitbucket.co.in/abc/def/188ed64efd5218beda276e02f2277bf3a6b745b2 if (urlRegex.test(dependencyKey)) { - const dependencySpecifier: DependencySpecifier = new DependencySpecifier(dependencyName, dependencyKey); + const dependencySpecifier: DependencySpecifier = DependencySpecifier.parseWithCache( + dependencyName, + dependencyKey + ); return dependencySpecifier; } else { return undefined; @@ -189,57 +291,174 @@ export function parsePnpmDependencyKey( // Is it an alias for a different package? if (parsedPackageName === dependencyName) { // No, it's a regular dependency - return new DependencySpecifier(parsedPackageName, parsedVersionPart); + return DependencySpecifier.parseWithCache(parsedPackageName, parsedVersionPart); } else { // If the parsed package name is different from the dependencyName, then this is an NPM package alias - return new DependencySpecifier(dependencyName, `npm:${parsedPackageName}@${parsedVersionPart}`); + return DependencySpecifier.parseWithCache( + dependencyName, + `npm:${parsedPackageName}@${parsedVersionPart}` + ); + } +} + +export function normalizePnpmVersionSpecifier(versionSpecifier: IPnpmVersionSpecifier): string { + if (typeof versionSpecifier === 'string') { + return versionSpecifier; + } else { + return versionSpecifier.version; } } +const cacheByLockfileHash: Map = new Map(); + export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { + public readonly shrinkwrapFileMajorVersion: number; public readonly isWorkspaceCompatible: boolean; public readonly registry: string; - public readonly dependencies: ReadonlyMap; + public readonly dependencies: ReadonlyMap; public readonly importers: ReadonlyMap; public readonly specifiers: ReadonlyMap; public readonly packages: ReadonlyMap; + public readonly overrides: ReadonlyMap; + public readonly packageExtensionsChecksum: undefined | string; + public readonly hash: string; private readonly _shrinkwrapJson: IPnpmShrinkwrapYaml; private readonly _integrities: Map>; private _pnpmfileConfiguration: PnpmfileConfiguration | undefined; - private constructor(shrinkwrapJson: IPnpmShrinkwrapYaml) { + private constructor(shrinkwrapJson: IPnpmShrinkwrapYaml, hash: string, subspaceHasNoProjects: boolean) { super(); + this.hash = hash; this._shrinkwrapJson = shrinkwrapJson; + cacheByLockfileHash.set(hash, this); // Normalize the data + const lockfileVersion: string | number | undefined = shrinkwrapJson.lockfileVersion; + if (typeof lockfileVersion === 'string') { + const isDotIncluded: boolean = lockfileVersion.includes('.'); + this.shrinkwrapFileMajorVersion = parseInt( + lockfileVersion.substring(0, isDotIncluded ? lockfileVersion.indexOf('.') : undefined), + 10 + ); + } else if (typeof lockfileVersion === 'number') { + this.shrinkwrapFileMajorVersion = Math.floor(lockfileVersion); + } else { + this.shrinkwrapFileMajorVersion = 0; + } + this.registry = shrinkwrapJson.registry || ''; this.dependencies = new Map(Object.entries(shrinkwrapJson.dependencies || {})); this.importers = new Map(Object.entries(shrinkwrapJson.importers || {})); this.specifiers = new Map(Object.entries(shrinkwrapJson.specifiers || {})); this.packages = new Map(Object.entries(shrinkwrapJson.packages || {})); + this.overrides = new Map(Object.entries(shrinkwrapJson.overrides || {})); + this.packageExtensionsChecksum = shrinkwrapJson.packageExtensionsChecksum; + + let isWorkspaceCompatible: boolean; + const importerCount: number = this.importers.size; + if (this.shrinkwrapFileMajorVersion >= ShrinkwrapFileMajorVersion.V9) { + // Lockfile v9 always has "." in importers filed. + if (subspaceHasNoProjects) { + // If there are no projects in this subspace, the "." importer will be the only importer + isWorkspaceCompatible = importerCount === 1; + } else { + isWorkspaceCompatible = importerCount > 1; + } + } else { + isWorkspaceCompatible = importerCount > 0; + } - // Importers only exist in workspaces - this.isWorkspaceCompatible = this.importers.size > 0; + this.isWorkspaceCompatible = isWorkspaceCompatible; this._integrities = new Map(); } - public static loadFromFile(shrinkwrapYamlFilename: string): PnpmShrinkwrapFile | undefined { + public static getLockfileV9PackageId(name: string, version: string): string { + /** + * name@1.2.3 -> name@1.2.3 + * name@1.2.3(peer) -> name@1.2.3(peer) + * https://xxx/@a/b -> name@https://xxx/@a/b + * file://xxx -> name@file://xxx + * 1.2.3 -> name@1.2.3 + */ + + if (/https?:/.test(version)) { + return /@https?:/.test(version) ? version : `${name}@${version}`; + } else if (/file:/.test(version)) { + return /@file:/.test(version) ? version : `${name}@${version}`; + } + + return pnpmKitV9.dependencyPath.removeSuffix(version).includes('@', 1) ? version : `${name}@${version}`; + } + + /** + * Clears the cache of PnpmShrinkwrapFile instances to free up memory. + */ + public static clearCache(): void { + cacheByLockfileHash.clear(); + } + + public static loadFromFile( + shrinkwrapYamlFilePath: string, + options: ILoadFromFileOptions + ): PnpmShrinkwrapFile | undefined { try { - const shrinkwrapContent: string = FileSystem.readFile(shrinkwrapYamlFilename); - return PnpmShrinkwrapFile.loadFromString(shrinkwrapContent); + const shrinkwrapContent: string = FileSystem.readFile(shrinkwrapYamlFilePath); + return PnpmShrinkwrapFile.loadFromString(shrinkwrapContent, options); } catch (error) { if (FileSystem.isNotExistError(error as Error)) { return undefined; // file does not exist } - throw new Error(`Error reading "${shrinkwrapYamlFilename}":\n ${(error as Error).message}`); + throw new Error(`Error reading "${shrinkwrapYamlFilePath}":\n ${(error as Error).message}`); } } - public static loadFromString(shrinkwrapContent: string): PnpmShrinkwrapFile { - const parsedData: IPnpmShrinkwrapYaml = yamlModule.safeLoad(shrinkwrapContent); - return new PnpmShrinkwrapFile(parsedData); + public static loadFromString( + shrinkwrapContent: string, + options: ILoadFromStringOptions + ): PnpmShrinkwrapFile { + const hash: string = crypto.createHash('sha-256').update(shrinkwrapContent, 'utf8').digest('hex'); + const cached: PnpmShrinkwrapFile | undefined = cacheByLockfileHash.get(hash); + if (cached) { + return cached; + } + + const { subspaceHasNoProjects } = options; + const shrinkwrapJson: IPnpmShrinkwrapYaml = yamlModule.load(shrinkwrapContent) as IPnpmShrinkwrapYaml; + if ((shrinkwrapJson as LockfileFileV9).snapshots) { + const lockfile: IPnpmShrinkwrapYaml | null = convertLockfileV9ToLockfileObject( + shrinkwrapJson as LockfileFileV9 + ); + /** + * In Lockfile V9, + * 1. There is no top-level dependencies field, but it is a property of the importers field. + * 2. The version may is not equal to the key in the package field. Thus, it needs to be standardized in the form of `:`. + * + * importers: + * .: + * dependencies: + * 'project1': + * specifier: file:./projects/project1 + * version: file:projects/project1 + * + * packages: + * project1@file:projects/project1: + * resolution: {directory: projects/project1, type: directory} + */ + const dependencies: ResolvedDependencies | undefined = + lockfile.importers['.' as ProjectId]?.dependencies; + if (dependencies) { + lockfile.dependencies = {}; + for (const [name, versionSpecifier] of Object.entries(dependencies)) { + lockfile.dependencies[name] = PnpmShrinkwrapFile.getLockfileV9PackageId(name, versionSpecifier); + } + } + + return new PnpmShrinkwrapFile(lockfile, hash, subspaceHasNoProjects); + } + + return new PnpmShrinkwrapFile(shrinkwrapJson, hash, subspaceHasNoProjects); } public getShrinkwrapHash(experimentsConfig?: IExperimentsJson): string { @@ -253,8 +472,69 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { return crypto.createHash('sha1').update(shrinkwrapContent).digest('hex'); } - /** @override */ - public validate( + /** + * Determine whether `pnpm-lock.yaml` contains insecure sha1 hashes. + * @internal + */ + private _disallowInsecureSha1( + customTipsConfiguration: CustomTipsConfiguration, + exemptPackageVersions: Record, + terminal: ITerminal, + subspaceName: string + ): boolean { + const exemptPackageList: Map = new Map(); + for (const [pkgName, versions] of Object.entries(exemptPackageVersions)) { + for (const version of versions) { + exemptPackageList.set(this._getPackageId(pkgName, version), true); + } + } + + for (const [pkgName, { resolution }] of this.packages) { + if ( + resolution?.integrity?.startsWith('sha1') && + !exemptPackageList.has(this._parseDependencyPath(pkgName)) + ) { + terminal.writeErrorLine( + 'Error: An integrity field with "sha1" was detected in the pnpm-lock.yaml file located in subspace ' + + `${subspaceName}; this conflicts with the "disallowInsecureSha1" policy from pnpm-config.json.\n` + ); + + customTipsConfiguration._showErrorTip(terminal, CustomTipId.TIP_RUSH_DISALLOW_INSECURE_SHA1); + + return true; // Indicates an error was found + } + } + return false; + } + + public override validateShrinkwrapAfterUpdate( + rushConfiguration: RushConfiguration, + subspace: Subspace, + terminal: ITerminal + ): void { + const pnpmOptions: PnpmOptionsConfiguration = subspace.getPnpmOptions() || rushConfiguration.pnpmOptions; + const { pnpmLockfilePolicies } = pnpmOptions; + + let invalidPoliciesCount: number = 0; + + if (pnpmLockfilePolicies?.disallowInsecureSha1?.enabled) { + const isError: boolean = this._disallowInsecureSha1( + rushConfiguration.customTipsConfiguration, + pnpmLockfilePolicies.disallowInsecureSha1.exemptPackageVersions, + terminal, + subspace.subspaceName + ); + if (isError) { + invalidPoliciesCount += 1; + } + } + + if (invalidPoliciesCount > 0) { + throw new AlreadyReportedError(); + } + } + + public override validate( packageManagerOptionsConfig: PackageManagerOptionsConfigurationBase, policyOptions: IShrinkwrapFilePolicyValidatorOptions, experimentsConfig?: IExperimentsJson @@ -266,8 +546,9 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { if (!policyOptions.allowShrinkwrapUpdates) { if (!policyOptions.repoState.isValid) { + // eslint-disable-next-line no-console console.log( - colors.red( + Colorize.red( `The ${RushConstants.repoStateFilename} file is invalid. There may be a merge conflict marker ` + 'in the file. You may need to run "rush update" to refresh its contents.' ) + '\n' @@ -279,8 +560,9 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { // may have changed and the hash could be invalid. if (packageManagerOptionsConfig.preventManualShrinkwrapChanges) { if (!policyOptions.repoState.pnpmShrinkwrapHash) { + // eslint-disable-next-line no-console console.log( - colors.red( + Colorize.red( 'The existing shrinkwrap file hash could not be found. You may need to run "rush update" to ' + 'populate the hash. See the "preventManualShrinkwrapChanges" setting documentation for details.' ) + '\n' @@ -289,8 +571,9 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { } if (this.getShrinkwrapHash(experimentsConfig) !== policyOptions.repoState.pnpmShrinkwrapHash) { + // eslint-disable-next-line no-console console.log( - colors.red( + Colorize.red( 'The shrinkwrap file hash does not match the expected hash. Please run "rush update" to ensure the ' + 'shrinkwrap file is up to date. See the "preventManualShrinkwrapChanges" setting documentation for ' + 'details.' @@ -302,8 +585,51 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { } } - /** @override */ - public getTempProjectNames(): ReadonlyArray { + /** + * This operation exactly mirrors the behavior of PNPM's own implementation: + * https://github.com/pnpm/pnpm/blob/73ebfc94e06d783449579cda0c30a40694d210e4/lockfile/lockfile-file/src/experiments/inlineSpecifiersLockfileConverters.ts#L162 + */ + private _convertLockfileV6DepPathToV5DepPath(newDepPath: string): string { + if (!newDepPath.includes('@', 2) || newDepPath.startsWith('file:')) return newDepPath; + const index: number = newDepPath.indexOf('@', newDepPath.indexOf('/@') + 2); + if (newDepPath.includes('(') && index > pnpmKitV8.dependencyPath.indexOfPeersSuffix(newDepPath)) + return newDepPath; + return `${newDepPath.substring(0, index)}/${newDepPath.substring(index + 1)}`; + } + + /** + * Normalize dependency paths for PNPM shrinkwrap files. + * Example: "/eslint-utils@3.0.0(eslint@8.23.1)" --> "/eslint-utils@3.0.0" + * Example: "/@typescript-eslint/experimental-utils/5.9.1_eslint@8.6.0+typescript@4.4.4" --> "/@typescript-eslint/experimental-utils/5.9.1" + */ + private _parseDependencyPath(packagePath: string): string { + let name: string | undefined; + let version: string | undefined; + + /** + * For PNPM lockfile version 9 and above, use pnpmKitV9 to parse the dependency path. + * Example: "@some/pkg@1.0.0" --> "@some/pkg@1.0.0" + * Example: "@some/pkg@1.0.0(peer@2.0.0)" --> "@some/pkg@1.0.0" + * Example: "pkg@1.0.0(patch_hash)" --> "pkg@1.0.0" + */ + if (this.shrinkwrapFileMajorVersion >= ShrinkwrapFileMajorVersion.V9) { + ({ name, version } = pnpmKitV9.dependencyPath.parse(packagePath)); + } else { + if (this.shrinkwrapFileMajorVersion >= ShrinkwrapFileMajorVersion.V6) { + packagePath = this._convertLockfileV6DepPathToV5DepPath(packagePath); + } + + ({ name, version } = pnpmKitV8.dependencyPath.parse(packagePath)); + } + + if (!name || !version) { + throw new InternalError(`Unable to parse package path: ${packagePath}`); + } + + return this._getPackageId(name, version); + } + + public override getTempProjectNames(): ReadonlyArray { return this._getTempProjectNames(this._shrinkwrapJson.dependencies || {}); } @@ -317,7 +643,7 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { return dependency?.resolution?.tarball; } - public getTopLevelDependencyKey(dependencyName: string): string | undefined { + public getTopLevelDependencyKey(dependencyName: string): IPnpmVersionSpecifier | undefined { return this.dependencies.get(dependencyName); } @@ -328,19 +654,28 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { * '1.9.0-dev.27' * 'file:projects/empty-webpart-project.tgz' * undefined - * - * @override */ - public getTopLevelDependencyVersion(dependencyName: string): DependencySpecifier | undefined { - let value: string | undefined = this.dependencies.get(dependencyName); + public override getTopLevelDependencyVersion(dependencyName: string): DependencySpecifier | undefined { + let value: IPnpmVersionSpecifier | undefined = this.dependencies.get(dependencyName); if (value) { - // Getting the top level dependency version from a PNPM lockfile version 5.1 + value = normalizePnpmVersionSpecifier(value); + + // Getting the top level dependency version from a PNPM lockfile version 5.x or 6.1 // -------------------------------------------------------------------------- // - // 1) Top-level tarball dependency entries in pnpm-lock.yaml look like: + // 1) Top-level tarball dependency entries in pnpm-lock.yaml look like in 5.x: + // ``` // '@rush-temp/sp-filepicker': 'file:projects/sp-filepicker.tgz_0ec79d3b08edd81ebf49cd19ca50b3f5' - - // Then, it would be defined below: + // ``` + // And in version 6.1, they look like: + // ``` + // '@rush-temp/sp-filepicker': + // specifier: file:./projects/generate-api-docs.tgz + // version: file:projects/generate-api-docs.tgz + // ``` + + // Then, it would be defined below (version 5.x): + // ``` // 'file:projects/sp-filepicker.tgz_0ec79d3b08edd81ebf49cd19ca50b3f5': // dependencies: // '@microsoft/load-themed-styles': 1.10.7 @@ -348,36 +683,69 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { // resolution: // integrity: sha512-guuoFIc**== // tarball: 'file:projects/sp-filepicker.tgz' + // ``` + // Or in version 6.1: + // ``` + // file:projects/sp-filepicker.tgz: + // resolution: {integrity: sha512-guuoFIc**==, tarball: file:projects/sp-filepicker.tgz} + // name: '@rush-temp/sp-filepicker' + // version: 0.0.0 + // dependencies: + // '@microsoft/load-themed-styles': 1.10.7 + // ... + // dev: false + // ``` // Here, we are interested in the part 'file:projects/sp-filepicker.tgz'. Splitting by underscores is not the // best way to get this because file names could have underscores in them. Instead, we could use the tarball // field in the resolution section. - // 2) Top-level non-tarball dependency entries in pnpm-lock.yaml would look like: + // 2) Top-level non-tarball dependency entries in pnpm-lock.yaml would look like in 5.x: + // ``` // '@rushstack/set-webpack-public-path-plugin': 2.1.133 // @microsoft/sp-build-node': 1.9.0-dev.27_typescript@2.9.2 - - // Here, we could just split by underscores and take the first part. + // ``` + // And in version 6.1, they look like: + // ``` + // '@rushstack/set-webpack-public-path-plugin': + // specifier: ^2.1.133 + // version: 2.1.133 + // '@microsoft/sp-build-node': + // specifier: 1.9.0-dev.27 + // version: 1.9.0-dev.27(typescript@2.9.2) + // ``` + + // Here, we could either just split by underscores and take the first part (5.x) or use the specifier field + // (6.1). // The below code is also compatible with lockfile versions < 5.1 const dependency: IPnpmShrinkwrapDependencyYaml | undefined = this.packages.get(value); if (dependency?.resolution?.tarball && value.startsWith(dependency.resolution.tarball)) { - return new DependencySpecifier(dependencyName, dependency.resolution.tarball); + return DependencySpecifier.parseWithCache(dependencyName, dependency.resolution.tarball); + } + + if (this.shrinkwrapFileMajorVersion >= ShrinkwrapFileMajorVersion.V9) { + const { version, nonSemverVersion } = pnpmKitV9.dependencyPath.parse(value); + value = version ?? nonSemverVersion ?? value; } else { - const underscoreIndex: number = value.indexOf('_'); - if (underscoreIndex >= 0) { - value = value.substr(0, underscoreIndex); + let underscoreOrParenthesisIndex: number = value.indexOf('_'); + if (underscoreOrParenthesisIndex < 0) { + underscoreOrParenthesisIndex = value.indexOf('('); + } + + if (underscoreOrParenthesisIndex >= 0) { + value = value.substring(0, underscoreOrParenthesisIndex); } } - return new DependencySpecifier(dependencyName, value); + return DependencySpecifier.parseWithCache(dependencyName, value); } return undefined; } /** - * The PNPM shrinkwrap file has top-level dependencies on the temp projects like this: + * The PNPM shrinkwrap file has top-level dependencies on the temp projects like this (version 5.x): * * ``` * dependencies: @@ -391,12 +759,33 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { * version: 0.0.0 * ``` * - * We refer to 'file:projects/my-app.tgz_25c559a5921686293a001a397be4dce0' as the temp project dependency key - * of the temp project '@rush-temp/my-app'. + * or in version 6.1, like this: + * ``` + * dependencies: + * '@rush-temp/my-app': + * specifier: file:./projects/my-app.tgz + * version: file:projects/my-app.tgz + * packages: + * /@types/node@10.14.15: + * resolution: {integrity: sha512-iAB+**==} + * dev: false + * file:projects/my-app.tgz + * resolution: {integrity: sha512-guuoFIc**==, tarball: file:projects/sp-filepicker.tgz} + * name: '@rush-temp/my-app' + * version: 0.0.0 + * dependencies: + * '@microsoft/load-themed-styles': 1.10.7 + * ... + * dev: false + * ``` + * + * We refer to 'file:projects/my-app.tgz_25c559a5921686293a001a397be4dce0' or 'file:projects/my-app.tgz' as + * the temp project dependency key of the temp project '@rush-temp/my-app'. */ public getTempProjectDependencyKey(tempProjectName: string): string | undefined { - const tempProjectDependencyKey: string | undefined = this.dependencies.get(tempProjectName); - return tempProjectDependencyKey ? tempProjectDependencyKey : undefined; + const tempProjectDependencyKey: IPnpmVersionSpecifier | undefined = + this.dependencies.get(tempProjectName); + return tempProjectDependencyKey ? normalizePnpmVersionSpecifier(tempProjectDependencyKey) : undefined; } public getShrinkwrapEntryFromTempProjectDependencyKey( @@ -405,17 +794,18 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { return this.packages.get(tempProjectDependencyKey); } - public getShrinkwrapEntry(name: string, version: string): IPnpmShrinkwrapDependencyYaml | undefined { + public getShrinkwrapEntry( + name: string, + version: IPnpmVersionSpecifier + ): IPnpmShrinkwrapDependencyYaml | undefined { const packageId: string = this._getPackageId(name, version); return this.packages.get(packageId); } /** * Serializes the PNPM Shrinkwrap file - * - * @override */ - protected serialize(): string { + protected override serialize(): string { return this._serializeInternal(false); } @@ -423,10 +813,8 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { * Gets the resolved version number of a dependency for a specific temp project. * For PNPM, we can reuse the version that another project is using. * Note that this function modifies the shrinkwrap data if tryReusingPackageVersionsFromShrinkwrap is set to true. - * - * @override */ - protected tryEnsureDependencyVersion( + protected override tryEnsureDependencyVersion( dependencySpecifier: DependencySpecifier, tempProjectName: string ): DependencySpecifier | undefined { @@ -455,31 +843,35 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { return undefined; } - const dependencyKey: string = packageDescription.dependencies[packageName]; + const dependencyKey: IPnpmVersionSpecifier = packageDescription.dependencies[packageName]; return this._parsePnpmDependencyKey(packageName, dependencyKey); } - /** @override */ - public findOrphanedProjects(rushConfiguration: RushConfiguration): ReadonlyArray { + public override findOrphanedProjects( + rushConfiguration: RushConfiguration, + subspace: Subspace + ): ReadonlyArray { // The base shrinkwrap handles orphaned projects the same across all package managers, // but this is only valid for non-workspace installs if (!this.isWorkspaceCompatible) { - return super.findOrphanedProjects(rushConfiguration); + return super.findOrphanedProjects(rushConfiguration, subspace); } + const subspaceTempFolder: string = subspace.getSubspaceTempFolderPath(); + const lookup: IReadonlyLookupByPath = + rushConfiguration.getProjectLookupForRoot(subspaceTempFolder); + const orphanedProjectPaths: string[] = []; for (const importerKey of this.getImporterKeys()) { - // PNPM importer keys are relative paths from the workspace root, which is the common temp folder - const rushProjectPath: string = path.resolve(rushConfiguration.commonTempFolder, importerKey); - if (!rushConfiguration.tryGetProjectForPath(rushProjectPath)) { - orphanedProjectPaths.push(rushProjectPath); + if (!lookup.findChildPath(importerKey)) { + // PNPM importer keys are relative paths from the workspace root, which is the common temp folder + orphanedProjectPaths.push(path.resolve(subspaceTempFolder, importerKey)); } } return orphanedProjectPaths; } - /** @override */ - public getProjectShrinkwrap(project: RushConfigurationProject): PnpmProjectShrinkwrapFile { + public override getProjectShrinkwrap(project: RushConfigurationProject): PnpmProjectShrinkwrapFile { return new PnpmProjectShrinkwrapFile(this, project); } @@ -508,35 +900,57 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { if (!integrityMap) { const importer: IPnpmShrinkwrapImporterYaml | undefined = this.getImporter(importerKey); if (importer) { - integrityMap = new Map(); - this._integrities.set(importerKey, integrityMap); + const resolvedIntegrityMap: Map = new Map(); + integrityMap = resolvedIntegrityMap; + this._integrities.set(importerKey, resolvedIntegrityMap); const sha256Digest: string = crypto .createHash('sha256') .update(JSON.stringify(importer)) .digest('base64'); const selfIntegrity: string = `${importerKey}:${sha256Digest}:`; - integrityMap.set(importerKey, selfIntegrity); + resolvedIntegrityMap.set(importerKey, selfIntegrity); const { dependencies, devDependencies, optionalDependencies } = importer; - const externalFilter: (name: string, version: string) => boolean = ( - name: string, - version: string - ): boolean => { - return !version.includes('link:'); + const processCollection = ( + collection: Record, + optional: boolean + ): void => { + const externalDeps: Record = {}; + for (const [name, versionSpecifier] of Object.entries(collection)) { + const version: string = normalizePnpmVersionSpecifier(versionSpecifier); + if (version.startsWith('link:')) { + // This is a workspace-local dependency; resolve it to an importer key and recurse. + // The link: path is relative to the project folder (which is the importer key itself), + // so we join the importer key with the link path (not dirname). + // Lockfile paths are always POSIX, so we use path.posix helpers. + const linkPath: string = version.slice('link:'.length); + const targetKey: string = path.posix.normalize(path.posix.join(importerKey, linkPath)); + const linkedIntegrities: Map | undefined = + this.getIntegrityForImporter(targetKey); + if (linkedIntegrities) { + for (const [dep, integrity] of linkedIntegrities) { + resolvedIntegrityMap.set(dep, integrity); + } + } + } else { + externalDeps[name] = versionSpecifier; + } + } + this._addIntegrities(resolvedIntegrityMap, externalDeps, optional); }; if (dependencies) { - this._addIntegrities(integrityMap, dependencies, false, externalFilter); + processCollection(dependencies, false); } if (devDependencies) { - this._addIntegrities(integrityMap, devDependencies, false, externalFilter); + processCollection(devDependencies, false); } if (optionalDependencies) { - this._addIntegrities(integrityMap, optionalDependencies, true, externalFilter); + processCollection(optionalDependencies, true); } } } @@ -544,15 +958,16 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { return integrityMap; } - /** @override */ - public async isWorkspaceProjectModifiedAsync( + public override async isWorkspaceProjectModifiedAsync( project: RushConfigurationProject, - variant?: string + subspace: Subspace, + variant: string | undefined ): Promise { const importerKey: string = this.getImporterKeyByPath( - project.rushConfiguration.commonTempFolder, + subspace.getSubspaceTempFolderPath(), project.projectFolder ); + const importer: IPnpmShrinkwrapImporterYaml | undefined = this.getImporter(importerKey); if (!importer) { return true; @@ -563,77 +978,254 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { // Initialize the pnpmfile if it doesn't exist if (!this._pnpmfileConfiguration) { - this._pnpmfileConfiguration = await PnpmfileConfiguration.initializeAsync(project.rushConfiguration, { + this._pnpmfileConfiguration = await PnpmfileConfiguration.initializeAsync( + project.rushConfiguration, + subspace, variant - }); + ); + } + + let transformedPackageJson: IPackageJson = packageJson; + + let subspacePnpmfile: IPnpmfile | undefined; + if (project.rushConfiguration.subspacesFeatureEnabled) { + // Get the pnpmfile + const subspacePnpmfilePath: string = path.join( + subspace.getSubspaceTempFolderPath(), + RushConstants.pnpmfileGlobalFilename + ); + + if (await FileSystem.existsAsync(subspacePnpmfilePath)) { + try { + subspacePnpmfile = require(subspacePnpmfilePath); + } catch (err) { + if (err instanceof SyntaxError) { + // eslint-disable-next-line no-console + console.error( + Colorize.red( + `A syntax error in the ${RushConstants.pnpmfileV6Filename} at ${subspacePnpmfilePath}\n` + ) + ); + } else { + // eslint-disable-next-line no-console + console.error( + Colorize.red( + `Error during pnpmfile execution. pnpmfile: "${subspacePnpmfilePath}". Error: "${err.message}".` + + '\n' + ) + ); + } + } + } + + if (subspacePnpmfile) { + const individualContext: IPnpmfileContext = { + log: (message: string) => { + // eslint-disable-next-line no-console + console.log(message); + } + }; + try { + transformedPackageJson = + subspacePnpmfile.hooks?.readPackage?.(transformedPackageJson, individualContext) || + transformedPackageJson; + } catch (err) { + // eslint-disable-next-line no-console + console.error( + Colorize.red( + `Error during readPackage hook execution. pnpmfile: "${subspacePnpmfilePath}". Error: "${err.message}".` + + '\n' + ) + ); + } + } } // Use a new PackageJsonEditor since it will classify each dependency type, making tracking the // found versions much simpler. - const { dependencyList, devDependencyList } = PackageJsonEditor.fromObject( - this._pnpmfileConfiguration.transform(packageJson), + const { dependencyList, devDependencyList, dependencyMetaList } = PackageJsonEditor.fromObject( + this._pnpmfileConfiguration.transform(transformedPackageJson), project.packageJsonEditor.filePath ); - // Then get the unique package names and map them to package versions. - const dependencyVersions: Map = new Map(); - for (const packageDependency of [...dependencyList, ...devDependencyList]) { - // We will also filter out peer dependencies since these are not installed at development time. - if (packageDependency.dependencyType === DependencyType.Peer) { - continue; + const allDependencies: PackageJsonDependency[] = [...dependencyList, ...devDependencyList]; + + if (this.shrinkwrapFileMajorVersion < ShrinkwrapFileMajorVersion.V6) { + // PNPM <= v7 + + // Then get the unique package names and map them to package versions. + const dependencyVersions: Map = new Map(); + for (const packageDependency of allDependencies) { + // We will also filter out peer dependencies since these are not installed at development time. + if (packageDependency.dependencyType === DependencyType.Peer) { + continue; + } + + const foundDependency: PackageJsonDependency | undefined = dependencyVersions.get( + packageDependency.name + ); + if (!foundDependency) { + dependencyVersions.set(packageDependency.name, packageDependency); + } else { + // Shrinkwrap will prioritize optional dependencies, followed by regular dependencies, with dev being + // the least prioritized. We will only keep the most prioritized option. + // See: https://github.com/pnpm/pnpm/blob/main/packages/lockfile-utils/src/satisfiesPackageManifest.ts + switch (foundDependency.dependencyType) { + case DependencyType.Optional: + break; + case DependencyType.Regular: + if (packageDependency.dependencyType === DependencyType.Optional) { + dependencyVersions.set(packageDependency.name, packageDependency); + } + break; + case DependencyType.Dev: + dependencyVersions.set(packageDependency.name, packageDependency); + break; + } + } } - const foundDependency: PackageJsonDependency | undefined = dependencyVersions.get( - packageDependency.name - ); - if (!foundDependency) { - dependencyVersions.set(packageDependency.name, packageDependency); - } else { - // Shrinkwrap will prioritize optional dependencies, followed by regular dependencies, with dev being - // the least prioritized. We will only keep the most prioritized option. - // See: https://github.com/pnpm/pnpm/blob/main/packages/lockfile-utils/src/satisfiesPackageManifest.ts - switch (foundDependency.dependencyType) { + // Then validate that the dependency fields are as expected in the shrinkwrap to avoid false-negatives + // when moving a package from one field to the other. + for (const { dependencyType, name } of dependencyVersions.values()) { + switch (dependencyType) { case DependencyType.Optional: + if (!importer.optionalDependencies?.[name]) return true; break; case DependencyType.Regular: - if (packageDependency.dependencyType === DependencyType.Optional) { - dependencyVersions.set(packageDependency.name, packageDependency); - } + if (!importer.dependencies?.[name]) return true; break; case DependencyType.Dev: - dependencyVersions.set(packageDependency.name, packageDependency); + if (!importer.devDependencies?.[name]) return true; break; } } - } - // Then validate that the dependency fields are as expected in the shrinkwrap to avoid false-negatives - // when moving a package from one field to the other. - for (const dependencyVersion of dependencyVersions.values()) { - switch (dependencyVersion.dependencyType) { - case DependencyType.Optional: - if (!importer.optionalDependencies || !importer.optionalDependencies[dependencyVersion.name]) + const specifiers: Record | undefined = importer.specifiers; + if (!specifiers) { + throw new InternalError('Expected specifiers to be defined, but is expected in lockfile version 5'); + } + + // Then validate the length matches between the importer and the dependency list, since duplicates are + // a valid use-case. Importers will only take one of these values, so no need to do more work here. + if (dependencyVersions.size !== Object.keys(specifiers).length) { + return true; + } + + // Finally, validate that all values in the importer are also present in the dependency list. + for (const [importerPackageName, importerVersionSpecifier] of Object.entries(specifiers)) { + const foundDependency: PackageJsonDependency | undefined = + dependencyVersions.get(importerPackageName); + if (!foundDependency) { + return true; + } + const resolvedVersion: string = this.overrides.get(importerPackageName) ?? foundDependency.version; + if (resolvedVersion !== importerVersionSpecifier) { + return true; + } + } + } else { + // >= PNPM v8 + const importerOptionalDependencies: Set = new Set( + Object.keys(importer.optionalDependencies ?? {}) + ); + const importerDependencies: Set = new Set(Object.keys(importer.dependencies ?? {})); + const importerDevDependencies: Set = new Set(Object.keys(importer.devDependencies ?? {})); + const importerDependenciesMeta: Set = new Set(Object.keys(importer.dependenciesMeta ?? {})); + + const regularDependencyNames: Set = new Set(dependencyList.map(({ name }) => name)); + + for (const { dependencyType, name, version } of allDependencies) { + let isOptional: boolean = false; + let specifierFromLockfile: IPnpmVersionSpecifier | undefined; + let isDevDepFallThrough: boolean = false; + switch (dependencyType) { + case DependencyType.Optional: { + specifierFromLockfile = importer.optionalDependencies?.[name]; + importerOptionalDependencies.delete(name); + break; + } + + case DependencyType.Peer: { + // Peer dependencies of workspace projects may be installed as regular dependencies + isOptional = true; // fall through + } + + case DependencyType.Dev: { + specifierFromLockfile = importer.devDependencies?.[name]; + if (specifierFromLockfile) { + // If the dev dependency is not found, it may be installed as a regular dependency, + // so fall through + importerDevDependencies.delete(name); + break; + } + // If not a peer and not also a regular dependency, the lockfile is stale. + if (!isOptional && !regularDependencyNames.has(name)) { + return true; + } + // If fall through, there is a chance the package declares an inconsistent version, ignore it. + isDevDepFallThrough = true; + } + + // eslint-disable-next-line no-fallthrough + case DependencyType.Regular: + specifierFromLockfile = importer.dependencies?.[name]; + importerDependencies.delete(name); + break; + } + + if (!specifierFromLockfile) { + if (!isOptional) { return true; - break; - case DependencyType.Regular: - if (!importer.dependencies || !importer.dependencies[dependencyVersion.name]) return true; - break; - case DependencyType.Dev: - if (!importer.devDependencies || !importer.devDependencies[dependencyVersion.name]) return true; - break; + } + } else { + if (this.shrinkwrapFileMajorVersion >= ShrinkwrapFileMajorVersion.V9) { + // TODO: Emit an error message when someone tries to override a version of something in one of their + // local repo packages. + let resolvedVersion: string = this.overrides.get(name) ?? version; + // convert path in posix style, otherwise pnpm install will fail in subspace case + resolvedVersion = Path.convertToSlashes(resolvedVersion); + const specifier: string = importer.specifiers[name]; + if (specifier !== resolvedVersion && !isDevDepFallThrough && !isOptional) { + return true; + } + } else { + if (typeof specifierFromLockfile === 'string') { + throw new Error( + `The PNPM lockfile is in an unexpected format. The "${name}" package is specified as ` + + `"${specifierFromLockfile}" instead of an object.` + ); + } else { + // TODO: Emit an error message when someone tries to override a version of something in one of their + // local repo packages. + let resolvedVersion: string = this.overrides.get(name) ?? version; + // convert path in posix style, otherwise pnpm install will fail in subspace case + resolvedVersion = Path.convertToSlashes(resolvedVersion); + if ( + specifierFromLockfile.specifier !== resolvedVersion && + !isDevDepFallThrough && + !isOptional + ) { + return true; + } + } + } + } } - } - // Then validate the length matches between the importer and the dependency list, since duplicates are - // a valid use-case. Importers will only take one of these values, so no need to do more work here. - if (dependencyVersions.size !== Object.keys(importer.specifiers).length) { - return true; - } + for (const { name, injected } of dependencyMetaList) { + if (importer.dependenciesMeta?.[name]?.injected === injected) { + importerDependenciesMeta.delete(name); + } + } - // Finally, validate that all values in the importer are also present in the dependency list. - for (const [importerPackageName, importerVersionSpecifier] of Object.entries(importer.specifiers)) { - const foundDependency: PackageJsonDependency | undefined = dependencyVersions.get(importerPackageName); - if (!foundDependency || foundDependency.version !== importerVersionSpecifier) { + // Finally, validate that all values in the importer are also present in the dependency list. + if ( + importerOptionalDependencies.size > 0 || + importerDependencies.size > 0 || + importerDevDependencies.size > 0 || + importerDependenciesMeta.size > 0 + ) { return true; } } @@ -664,21 +1256,16 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { return integrityMap; } - let selfIntegrity: string | undefined = shrinkwrapEntry.resolution?.integrity; - if (!selfIntegrity) { - // git dependency specifiers do not have an integrity entry. Instead, they specify the tarball field. - // So instead, we will hash the contents of the dependency entry and use that as the integrity hash. - // Ex: - // github.com/chfritz/node-xmlrpc/948db2fbd0260e5d56ed5ba58df0f5b6599bbe38: - // ... - // resolution: - // tarball: 'https://codeload.github.com/chfritz/node-xmlrpc/tar.gz/948db2fbd0260e5d56ed5ba58df0f5b6599bbe38' - const sha256Digest: string = crypto - .createHash('sha256') - .update(JSON.stringify(shrinkwrapEntry)) - .digest('base64'); - selfIntegrity = `${specifier}:${sha256Digest}:`; - } + // Hash the full shrinkwrap entry instead of using just resolution.integrity. + // This ensures that changes to sub-dependency resolutions are detected. + // For example, if package A depends on B@1.0 and B@1.0's resolution of C changes + // from C@1.3 to C@1.2, the hash of A's shrinkwrap entry will change because + // the dependencies field in the entry reflects the resolved versions. + const sha256Digest: string = crypto + .createHash('sha256') + .update(JSON.stringify(shrinkwrapEntry)) + .digest('base64'); + const selfIntegrity: string = `${specifier}:${sha256Digest}:`; integrityMap.set(specifier, selfIntegrity); const { dependencies, optionalDependencies } = shrinkwrapEntry; @@ -696,24 +1283,33 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { private _addIntegrities( integrityMap: Map, - collection: Record, - optional: boolean, - filter?: (name: string, version: string) => boolean + collection: Record, + optional: boolean ): void { for (const [name, version] of Object.entries(collection)) { - if (filter && !filter(name, version)) { - continue; - } - - const packageId: string = this._getPackageId(name, version); - if (integrityMap.has(packageId)) { - // The entry could already have been added as a nested dependency - continue; - } + const normalizedVersion: string = normalizePnpmVersionSpecifier(version); + if (normalizedVersion.startsWith('link:')) { + // In a package snapshot, link: paths are resolved relative to the pnpm workspace root, + // which means they are already direct keys into the importer map. + const targetKey: string = normalizedVersion.slice('link:'.length); + const linkedIntegrities: Map | undefined = + this.getIntegrityForImporter(targetKey); + if (linkedIntegrities) { + for (const [dep, integrity] of linkedIntegrities) { + integrityMap.set(dep, integrity); + } + } + } else { + const packageId: string = this._getPackageId(name, version); + if (integrityMap.has(packageId)) { + // The entry could already have been added as a nested dependency + continue; + } - const contribution: Map = this._getIntegrityForPackage(packageId, optional); - for (const [dep, integrity] of contribution) { - integrityMap.set(dep, integrity); + const contribution: Map = this._getIntegrityForPackage(packageId, optional); + for (const [dep, integrity] of contribution) { + integrityMap.set(dep, integrity); + } } } } @@ -730,21 +1326,32 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { return packageDescription && packageDescription.dependencies ? packageDescription : undefined; } - private _getPackageId(name: string, version: string): string { - // Version can sometimes be in the form of a path that's already in the /name/version format. - const packageId: string = version.indexOf('/') !== -1 ? version : `/${name}/${version}`; - return packageId; + private _getPackageId(name: string, versionSpecifier: IPnpmVersionSpecifier): string { + const version: string = normalizePnpmVersionSpecifier(versionSpecifier); + if (this.shrinkwrapFileMajorVersion >= ShrinkwrapFileMajorVersion.V9) { + return PnpmShrinkwrapFile.getLockfileV9PackageId(name, version); + } else if (this.shrinkwrapFileMajorVersion >= ShrinkwrapFileMajorVersion.V6) { + if (version.startsWith('@github')) { + // This is a github repo reference + return version; + } else { + return version.startsWith('/') ? version : `/${name}@${version}`; + } + } else { + // Version can sometimes be in the form of a path that's already in the /name/version format. + return version.indexOf('/') !== -1 ? version : `/${name}/${version}`; + } } private _parsePnpmDependencyKey( dependencyName: string, - pnpmDependencyKey: string + pnpmDependencyKey: IPnpmVersionSpecifier ): DependencySpecifier | undefined { if (pnpmDependencyKey) { - const result: DependencySpecifier | undefined = parsePnpmDependencyKey( - dependencyName, - pnpmDependencyKey - ); + const result: DependencySpecifier | undefined = + this.shrinkwrapFileMajorVersion >= ShrinkwrapFileMajorVersion.V9 + ? parsePnpm9DependencyKey(dependencyName, pnpmDependencyKey) + : parsePnpmDependencyKey(dependencyName, pnpmDependencyKey); if (!result) { throw new Error( @@ -773,6 +1380,6 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile { } } - return yamlModule.safeDump(shrinkwrapToSerialize, PNPM_SHRINKWRAP_YAML_FORMAT); + return yamlModule.dump(shrinkwrapToSerialize, PNPM_SHRINKWRAP_YAML_FORMAT); } } diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmWorkspaceFile.ts b/libraries/rush-lib/src/logic/pnpm/PnpmWorkspaceFile.ts index 0615741be7c..7734ee575e1 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmWorkspaceFile.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmWorkspaceFile.ts @@ -1,15 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { Sort, Import, Path } from '@rushstack/node-core-library'; +import * as path from 'node:path'; -import { BaseWorkspaceFile } from '../base/BaseWorkspaceFile'; -import { PNPM_SHRINKWRAP_YAML_FORMAT } from './PnpmYamlCommon'; +import { escapePath as globEscape } from 'fast-glob'; -const yamlModule: typeof import('js-yaml') = Import.lazy('js-yaml', require); +import { FileSystem, Sort, Path } from '@rushstack/node-core-library'; -const globEscape: (unescaped: string) => string = require('glob-escape'); // No @types/glob-escape package exists +import { BaseWorkspaceFile } from '../base/BaseWorkspaceFile'; +import { PNPM_SHRINKWRAP_YAML_FORMAT } from './PnpmYamlCommon'; +import type { + IPnpmPackageExtension, + IPnpmPeerDependencyRules, + PnpmTrustPolicy +} from './PnpmOptionsConfiguration'; /** * This interface represents the raw pnpm-workspace.YAML file @@ -17,12 +21,105 @@ const globEscape: (unescaped: string) => string = require('glob-escape'); // No * { * "packages": [ * "../../apps/project1" - * ] + * ], + * "catalogs": { + * "default": { + * "react": "^18.0.0" + * } + * }, + * "allowBuilds": { + * "esbuild": true, + * "fsevents": false + * } * } */ interface IPnpmWorkspaceYaml { /** The list of local package directories */ packages: string[]; + /** Catalog definitions for centralized version management */ + catalogs: Record> | undefined; + /** + * Controls which packages are allowed to run build scripts. A value of `true` means the + * package is allowed to run build scripts; `false` means it is explicitly denied. + * Packages with build scripts not listed here will cause pnpm to fail with ERR_PNPM_IGNORED_BUILDS. + * (SUPPORTED ONLY IN PNPM 11.0.0 AND NEWER) + */ + allowBuilds: Record | undefined; + /** + * Dependency version overrides. In pnpm 11+ this replaces the `pnpm.overrides` field of + * `package.json`, which pnpm no longer reads. + * (SUPPORTED ONLY IN PNPM 11.0.0 AND NEWER) + */ + overrides: Record | undefined; + /** + * Extensions applied to the `package.json` of matched dependencies. In pnpm 11+ this replaces + * the `pnpm.packageExtensions` field of `package.json`, which pnpm no longer reads. + * (SUPPORTED ONLY IN PNPM 11.0.0 AND NEWER) + */ + packageExtensions: Record | undefined; + /** + * Rules for suppressing peer dependency validation errors. In pnpm 11+ this replaces the + * `pnpm.peerDependencyRules` field of `package.json`, which pnpm no longer reads. + * (SUPPORTED ONLY IN PNPM 11.0.0 AND NEWER) + */ + peerDependencyRules: IPnpmPeerDependencyRules | undefined; + /** + * Suppresses installation warnings for deprecated package versions. In pnpm 11+ this replaces + * the `pnpm.allowedDeprecatedVersions` field of `package.json`, which pnpm no longer reads. + * (SUPPORTED ONLY IN PNPM 11.0.0 AND NEWER) + */ + allowedDeprecatedVersions: Record | undefined; + /** + * Patches applied to dependencies. In pnpm 11+ this replaces the `pnpm.patchedDependencies` + * field of `package.json`, which pnpm no longer reads. + * (SUPPORTED ONLY IN PNPM 11.0.0 AND NEWER) + */ + patchedDependencies: Record | undefined; + /** + * Optional dependencies whose names are listed here are skipped during installation. In pnpm 11+ + * this replaces the `pnpm.ignoredOptionalDependencies` field of `package.json`, which pnpm no + * longer reads. + * (SUPPORTED ONLY IN PNPM 9.0.0 AND NEWER) + */ + ignoredOptionalDependencies: string[] | undefined; + /** + * The trust policy applied when installing packages. In pnpm 11+ this replaces the + * `pnpm.trustPolicy` field of `package.json`, which pnpm no longer reads. + * (SUPPORTED ONLY IN PNPM 10.21.0 AND NEWER) + */ + trustPolicy: PnpmTrustPolicy | undefined; + /** + * Package selectors excluded from the trust policy check. In pnpm 11+ this replaces the + * `pnpm.trustPolicyExclude` field of `package.json`, which pnpm no longer reads. + * (SUPPORTED ONLY IN PNPM 10.22.0 AND NEWER) + */ + trustPolicyExclude: string[] | undefined; + /** + * Ignore the trust policy check for packages published more than this many minutes ago. In + * pnpm 11+ this replaces the `pnpm.trustPolicyIgnoreAfter` field of `package.json`, which pnpm + * no longer reads. + * (SUPPORTED ONLY IN PNPM 10.27.0 AND NEWER) + */ + trustPolicyIgnoreAfter: number | undefined; + /** + * The minimum number of minutes that must pass after a version is published before pnpm will install it. + * (SUPPORTED ONLY IN PNPM 10.16.0 AND NEWER) + */ + minimumReleaseAge: number | undefined; + /** + * List of package names or patterns that are excluded from the minimumReleaseAge check. + * (SUPPORTED ONLY IN PNPM 10.16.0 AND NEWER) + */ + minimumReleaseAgeExclude: string[] | undefined; + /** + * The path to the "global pnpmfile" that Rush generates when subspaces are enabled, which rewrites + * cross-subspace `workspace:*` dependency specifiers to `link:` specifiers. pnpm 11+ only reads + * auth/registry settings from `.npmrc`, so the `global-pnpmfile=` line Rush writes there is + * silently ignored; for pnpm 11+ the path is emitted here instead. Without it, installation of a + * subspace with cross-subspace dependencies fails with ERR_PNPM_WORKSPACE_PKG_NOT_FOUND. + * (USED ONLY IN PNPM 11.0.0 AND NEWER) + */ + globalPnpmfile: string | undefined; } export class PnpmWorkspaceFile extends BaseWorkspaceFile { @@ -31,7 +128,21 @@ export class PnpmWorkspaceFile extends BaseWorkspaceFile { */ public readonly workspaceFilename: string; - private _workspacePackages: Set; + private readonly _workspacePackages: Set; + public catalogs: IPnpmWorkspaceYaml['catalogs']; + public allowBuilds: IPnpmWorkspaceYaml['allowBuilds']; + public overrides: IPnpmWorkspaceYaml['overrides']; + public packageExtensions: IPnpmWorkspaceYaml['packageExtensions']; + public peerDependencyRules: IPnpmWorkspaceYaml['peerDependencyRules']; + public allowedDeprecatedVersions: IPnpmWorkspaceYaml['allowedDeprecatedVersions']; + public patchedDependencies: IPnpmWorkspaceYaml['patchedDependencies']; + public ignoredOptionalDependencies: IPnpmWorkspaceYaml['ignoredOptionalDependencies']; + public trustPolicy: IPnpmWorkspaceYaml['trustPolicy']; + public trustPolicyExclude: IPnpmWorkspaceYaml['trustPolicyExclude']; + public trustPolicyIgnoreAfter: IPnpmWorkspaceYaml['trustPolicyIgnoreAfter']; + public minimumReleaseAge: IPnpmWorkspaceYaml['minimumReleaseAge']; + public minimumReleaseAgeExclude: IPnpmWorkspaceYaml['minimumReleaseAgeExclude']; + public globalPnpmfile: IPnpmWorkspaceYaml['globalPnpmfile']; /** * The PNPM workspace file is used to specify the location of workspaces relative to the root @@ -46,8 +157,72 @@ export class PnpmWorkspaceFile extends BaseWorkspaceFile { this._workspacePackages = new Set(); } - /** @override */ - public addPackage(packagePath: string): void { + /** + * Reads an existing `pnpm-workspace.yaml` file and returns a {@link PnpmWorkspaceFile} whose + * settings properties are populated from its contents. + * + * @remarks + * The workspace `packages` list is not loaded; the returned instance is intended for reading the + * generated pnpm settings (such as `allowBuilds` and `patchedDependencies`), not for + * re-serialization. + * + * @param workspaceYamlFilename - The path to the `pnpm-workspace.yaml` file + */ + public static async tryLoadAsync(workspaceYamlFilename: string): Promise { + let workspaceYamlContent: string; + try { + workspaceYamlContent = await FileSystem.readFileAsync(workspaceYamlFilename); + } catch (error) { + if (FileSystem.isNotExistError(error)) { + return undefined; + } else { + throw error; + } + } + + const yamlModule: typeof import('js-yaml') = await import('js-yaml'); + const workspaceYaml: IPnpmWorkspaceYaml | undefined = yamlModule.load(workspaceYamlContent) as + | IPnpmWorkspaceYaml + | undefined; + + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceYamlFilename); + if (workspaceYaml) { + const { + catalogs, + allowBuilds, + overrides, + packageExtensions, + peerDependencyRules, + allowedDeprecatedVersions, + patchedDependencies, + ignoredOptionalDependencies, + trustPolicy, + trustPolicyExclude, + trustPolicyIgnoreAfter, + minimumReleaseAge, + minimumReleaseAgeExclude, + globalPnpmfile + } = workspaceYaml; + workspaceFile.catalogs = catalogs; + workspaceFile.allowBuilds = allowBuilds; + workspaceFile.overrides = overrides; + workspaceFile.packageExtensions = packageExtensions; + workspaceFile.peerDependencyRules = peerDependencyRules; + workspaceFile.allowedDeprecatedVersions = allowedDeprecatedVersions; + workspaceFile.patchedDependencies = patchedDependencies; + workspaceFile.ignoredOptionalDependencies = ignoredOptionalDependencies; + workspaceFile.trustPolicy = trustPolicy; + workspaceFile.trustPolicyExclude = trustPolicyExclude; + workspaceFile.trustPolicyIgnoreAfter = trustPolicyIgnoreAfter; + workspaceFile.minimumReleaseAge = minimumReleaseAge; + workspaceFile.minimumReleaseAgeExclude = minimumReleaseAgeExclude; + workspaceFile.globalPnpmfile = globalPnpmfile; + } + + return workspaceFile; + } + + public override addPackage(packagePath: string): void { // Ensure the path is relative to the pnpm-workspace.yaml file if (path.isAbsolute(packagePath)) { packagePath = path.relative(path.dirname(this.workspaceFilename), packagePath); @@ -58,14 +233,47 @@ export class PnpmWorkspaceFile extends BaseWorkspaceFile { this._workspacePackages.add(globEscape(globPath)); } - /** @override */ - protected serialize(): string { + protected override async serializeAsync(): Promise { + const { + _workspacePackages: workspacePackages, + catalogs, + allowBuilds, + overrides, + packageExtensions, + peerDependencyRules, + allowedDeprecatedVersions, + patchedDependencies, + ignoredOptionalDependencies, + trustPolicy, + trustPolicyExclude, + trustPolicyIgnoreAfter, + minimumReleaseAge, + minimumReleaseAgeExclude, + globalPnpmfile + } = this; // Ensure stable sort order when serializing - Sort.sortSet(this._workspacePackages); - + Sort.sortSet(workspacePackages); const workspaceYaml: IPnpmWorkspaceYaml = { - packages: Array.from(this._workspacePackages) + packages: Array.from(workspacePackages), + // js-yaml omits mapping entries whose value is `undefined`, so no guard is needed here. + // An explicitly-set empty object is passed through as-is. + catalogs, + allowBuilds, + overrides, + packageExtensions, + peerDependencyRules, + allowedDeprecatedVersions, + patchedDependencies, + ignoredOptionalDependencies, + trustPolicy, + trustPolicyExclude, + trustPolicyIgnoreAfter, + minimumReleaseAge, + minimumReleaseAgeExclude, + globalPnpmfile }; - return yamlModule.safeDump(workspaceYaml, PNPM_SHRINKWRAP_YAML_FORMAT); + + const yamlModule: typeof import('js-yaml') = await import('js-yaml'); + return yamlModule.dump(workspaceYaml, PNPM_SHRINKWRAP_YAML_FORMAT); } } diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmfileConfiguration.ts b/libraries/rush-lib/src/logic/pnpm/PnpmfileConfiguration.ts index fa492aaeaee..0db2eb38807 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmfileConfiguration.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmfileConfiguration.ts @@ -1,27 +1,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { FileSystem, Import, IPackageJson, JsonFile, MapExtensions } from '@rushstack/node-core-library'; +import * as path from 'node:path'; -import { PnpmPackageManager } from '../../api/packageManager/PnpmPackageManager'; -import { RushConfiguration } from '../../api/RushConfiguration'; -import { CommonVersionsConfiguration } from '../../api/CommonVersionsConfiguration'; -import { PnpmOptionsConfiguration } from './PnpmOptionsConfiguration'; +import * as semver from 'semver'; + +import { FileSystem, Import, type IPackageJson, JsonFile, MapExtensions } from '@rushstack/node-core-library'; + +import type { PnpmPackageManager } from '../../api/packageManager/PnpmPackageManager'; +import type { RushConfiguration } from '../../api/RushConfiguration'; +import type { CommonVersionsConfiguration } from '../../api/CommonVersionsConfiguration'; +import type { PnpmOptionsConfiguration } from './PnpmOptionsConfiguration'; import * as pnpmfile from './PnpmfileShim'; import { pnpmfileShimFilename, scriptsFolderPath } from '../../utilities/PathConstants'; - import type { IPnpmfileContext, IPnpmfileShimSettings } from './IPnpmfile'; - -/** - * Options used when generating the pnpmfile shim settings file. - */ -export interface IPnpmfileShimOptions { - /** - * The variant that the client pnpmfile will be sourced from. - */ - variant?: string; -} +import type { Subspace } from '../../api/Subspace'; /** * Loads PNPM's pnpmfile.js configuration, and invokes it to preprocess package.json files, @@ -31,12 +24,14 @@ export class PnpmfileConfiguration { private _context: IPnpmfileContext | undefined; private constructor(context: IPnpmfileContext) { + pnpmfile.reset(); this._context = context; } public static async initializeAsync( rushConfiguration: RushConfiguration, - pnpmfileShimOptions?: IPnpmfileShimOptions + subspace: Subspace, + variant: string | undefined ): Promise { if (rushConfiguration.packageManager !== 'pnpm') { throw new Error( @@ -47,10 +42,7 @@ export class PnpmfileConfiguration { // Set the context to swallow log output and store our settings const context: IPnpmfileContext = { log: (message: string) => {}, - pnpmfileShimSettings: await PnpmfileConfiguration._getPnpmfileShimSettingsAsync( - rushConfiguration, - pnpmfileShimOptions - ) + pnpmfileShimSettings: await _getPnpmfileShimSettingsAsync(rushConfiguration, subspace, variant) }; return new PnpmfileConfiguration(context); @@ -58,7 +50,9 @@ export class PnpmfileConfiguration { public static async writeCommonTempPnpmfileShimAsync( rushConfiguration: RushConfiguration, - options?: IPnpmfileShimOptions + targetDir: string, + subspace: Subspace, + variant: string | undefined ): Promise { if (rushConfiguration.packageManager !== 'pnpm') { throw new Error( @@ -66,7 +60,6 @@ export class PnpmfileConfiguration { ); } - const targetDir: string = rushConfiguration.commonTempFolder; const pnpmfilePath: string = path.join( targetDir, (rushConfiguration.packageManagerWrapper as PnpmPackageManager).pnpmfileFilename @@ -78,8 +71,11 @@ export class PnpmfileConfiguration { destinationPath: pnpmfilePath }); - const pnpmfileShimSettings: IPnpmfileShimSettings = - await PnpmfileConfiguration._getPnpmfileShimSettingsAsync(rushConfiguration, options); + const pnpmfileShimSettings: IPnpmfileShimSettings = await _getPnpmfileShimSettingsAsync( + rushConfiguration, + subspace, + variant + ); // Write the settings file used by the shim await JsonFile.saveAsync(pnpmfileShimSettings, path.join(targetDir, 'pnpmfileSettings.json'), { @@ -87,46 +83,6 @@ export class PnpmfileConfiguration { }); } - private static async _getPnpmfileShimSettingsAsync( - rushConfiguration: RushConfiguration, - options?: IPnpmfileShimOptions - ): Promise { - let allPreferredVersions: { [dependencyName: string]: string } = {}; - let allowedAlternativeVersions: { [dependencyName: string]: readonly string[] } = {}; - const workspaceVersions: Record = {}; - - // Only workspaces shims in the common versions using pnpmfile - if ((rushConfiguration.packageManagerOptions as PnpmOptionsConfiguration).useWorkspaces) { - const commonVersionsConfiguration: CommonVersionsConfiguration = rushConfiguration.getCommonVersions(); - const preferredVersions: Map = new Map(); - MapExtensions.mergeFromMap(preferredVersions, commonVersionsConfiguration.getAllPreferredVersions()); - MapExtensions.mergeFromMap(preferredVersions, rushConfiguration.getImplicitlyPreferredVersions()); - allPreferredVersions = MapExtensions.toObject(preferredVersions); - allowedAlternativeVersions = MapExtensions.toObject( - commonVersionsConfiguration.allowedAlternativeVersions - ); - - for (const project of rushConfiguration.projects) { - workspaceVersions[project.packageName] = project.packageJson.version; - } - } - - const settings: IPnpmfileShimSettings = { - allPreferredVersions, - allowedAlternativeVersions, - workspaceVersions, - semverPath: Import.resolveModule({ modulePath: 'semver', baseFolderPath: __dirname }) - }; - - // Use the provided path if available. Otherwise, use the default path. - const userPnpmfilePath: string | undefined = rushConfiguration.getPnpmfilePath(options?.variant); - if (userPnpmfilePath && FileSystem.exists(userPnpmfilePath)) { - settings.userPnpmfilePath = userPnpmfilePath; - } - - return settings; - } - /** * Transform a package.json file using the pnpmfile.js hook. * @returns the transformed object, or the original input if pnpmfile.js was not found. @@ -139,3 +95,52 @@ export class PnpmfileConfiguration { } } } + +async function _getPnpmfileShimSettingsAsync( + rushConfiguration: RushConfiguration, + subspace: Subspace, + variant: string | undefined +): Promise { + let allPreferredVersions: { [dependencyName: string]: string } = {}; + let allowedAlternativeVersions: { [dependencyName: string]: readonly string[] } = {}; + const workspaceVersions: Record = {}; + + // Only workspaces shims in the common versions using pnpmfile + if ((rushConfiguration.packageManagerOptions as PnpmOptionsConfiguration).useWorkspaces) { + const commonVersionsConfiguration: CommonVersionsConfiguration = subspace.getCommonVersions(variant); + const preferredVersions: Map = new Map(); + MapExtensions.mergeFromMap( + preferredVersions, + rushConfiguration.getImplicitlyPreferredVersions(subspace, variant) + ); + for (const [name, version] of commonVersionsConfiguration.getAllPreferredVersions()) { + // Use the most restrictive version range available + if (!preferredVersions.has(name) || semver.subset(version, preferredVersions.get(name)!)) { + preferredVersions.set(name, version); + } + } + allPreferredVersions = MapExtensions.toObject(preferredVersions); + allowedAlternativeVersions = MapExtensions.toObject( + commonVersionsConfiguration.allowedAlternativeVersions + ); + + for (const project of rushConfiguration.projects) { + workspaceVersions[project.packageName] = project.packageJson.version; + } + } + + const settings: IPnpmfileShimSettings = { + allPreferredVersions, + allowedAlternativeVersions, + workspaceVersions, + semverPath: Import.resolveModule({ modulePath: 'semver', baseFolderPath: __dirname }) + }; + + // Use the provided path if available. Otherwise, use the default path. + const userPnpmfilePath: string | undefined = subspace.getPnpmfilePath(variant); + if (userPnpmfilePath && FileSystem.exists(userPnpmfilePath)) { + settings.userPnpmfilePath = userPnpmfilePath; + } + + return settings; +} diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmfileShim.ts b/libraries/rush-lib/src/logic/pnpm/PnpmfileShim.ts index 5375ea4250b..924ce92c7eb 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmfileShim.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmfileShim.ts @@ -3,24 +3,41 @@ // The "rush install" or "rush update" commands will copy this template to // "common/temp/" so that it can implement Rush-specific features such as -// implicitly preferred versions. It reads its input data from "common/temp/pnpmfileSettings.json", -// which includes the path to the user's pnpmfile for the currently selected variant. The pnpmfile is -// required directly by this shim and is called after Rush's transformations are applied. +// implicitly preferred versions. It reads its input data from "common/temp/pnpmfileSettings.json". +// The pnpmfile is required directly by this shim and is called after Rush's transformations are applied. // This file can use "import type" but otherwise should not reference any other modules, since it will // be run from the "common/temp" directory import type * as TSemver from 'semver'; + import type { IPackageJson } from '@rushstack/node-core-library'; import type { IPnpmShrinkwrapYaml } from './PnpmShrinkwrapFile'; import type { IPnpmfile, IPnpmfileShimSettings, IPnpmfileContext, IPnpmfileHooks } from './IPnpmfile'; -let settings: IPnpmfileShimSettings; -let allPreferredVersions: Map; +let settings: IPnpmfileShimSettings | undefined; +let allPreferredVersions: Map | undefined; +let rangeParseCache: Map | undefined; let allowedAlternativeVersions: Map> | undefined; +let workspaceVersions: Map | undefined; let userPnpmfile: IPnpmfile | undefined; let semver: typeof TSemver | undefined; +// All calls to new semver.Range and the comparison functions need to have the same options +// It can be a different object with the same properties, but reusing this const avoids allocations +const SEMVER_COMPARE_OPTIONS: TSemver.RangeOptions = { includePrerelease: true }; + +// Resets the internal state of the pnpmfile +export function reset(): void { + settings = undefined; + allPreferredVersions = undefined; + allowedAlternativeVersions = undefined; + workspaceVersions = undefined; + rangeParseCache = undefined; + userPnpmfile = undefined; + semver = undefined; +} + // Initialize all external aspects of the pnpmfile shim. When using the shim, settings // are always expected to be available. Init must be called before running any hook that // depends on a resource obtained from or related to the settings, and will require modules @@ -41,13 +58,14 @@ function init(context: IPnpmfileContext | any): IPnpmfileContext { if (!context.pnpmfileShimSettings) { context.pnpmfileShimSettings = __non_webpack_require__('./pnpmfileSettings.json'); } - settings = context.pnpmfileShimSettings!; + settings = context.pnpmfileShimSettings as IPnpmfileShimSettings; } else if (!context.pnpmfileShimSettings) { // Reuse the already initialized settings context.pnpmfileShimSettings = settings; } if (!allPreferredVersions && settings.allPreferredVersions) { allPreferredVersions = new Map(Object.entries(settings.allPreferredVersions)); + rangeParseCache = new Map(); } if (!allowedAlternativeVersions && settings.allowedAlternativeVersions) { allowedAlternativeVersions = new Map( @@ -56,6 +74,9 @@ function init(context: IPnpmfileContext | any): IPnpmfileContext { }) ); } + if (!workspaceVersions && settings.workspaceVersions) { + workspaceVersions = new Map(Object.entries(settings.workspaceVersions)); + } // If a userPnpmfilePath is provided, we expect it to exist if (!userPnpmfile && settings.userPnpmfilePath) { userPnpmfile = require(settings.userPnpmfilePath); @@ -68,48 +89,87 @@ function init(context: IPnpmfileContext | any): IPnpmfileContext { return context as IPnpmfileContext; } +function parseRange(range: string): TSemver.Range | false { + if (!rangeParseCache || !semver) { + return false; + } + + const entry: TSemver.Range | false | undefined = rangeParseCache.get(range); + if (entry !== undefined) { + return entry; + } + + if (range.includes(':')) { + // This version specifier has a protocol (e.g. npm:, workspace:, etc.), so it is not a normal semver range. + rangeParseCache.set(range, false); + return false; + } + + try { + const parsedRange: TSemver.Range = new semver.Range(range, SEMVER_COMPARE_OPTIONS); + rangeParseCache.set(range, parsedRange); + return parsedRange; + } catch { + rangeParseCache.set(range, false); + return false; + } +} + // Set the preferred versions on the dependency map. If the version on the map is an allowedAlternativeVersion // then skip it. Otherwise, check to ensure that the common version is a subset of the specified version. If // it is, then replace the specified version with the preferredVersion function setPreferredVersions(dependencies: { [dependencyName: string]: string } | undefined): void { - for (const [name, version] of Object.entries(dependencies || {})) { - const preferredVersion: string | undefined = allPreferredVersions?.get(name); + if (!dependencies || !semver || !allPreferredVersions) { + return; + } + + // Needed for control flow analyzer. + const definitelyDefinedAllPreferredVersions: Map = allPreferredVersions; + const definiteSemver: typeof TSemver = semver; + + Object.entries(dependencies).forEach(([name, version]: [string, string]) => { + const preferredVersion: string | undefined = definitelyDefinedAllPreferredVersions.get(name); + // If preferredVersionRange is valid and the current version is not an allowed alternative, proceed to check subsets if (preferredVersion && !allowedAlternativeVersions?.get(name)?.has(version)) { - let preferredVersionRange: TSemver.Range | undefined; - let versionRange: TSemver.Range | undefined; - try { - preferredVersionRange = new semver!.Range(preferredVersion); - versionRange = new semver!.Range(version); - } catch { - // Swallow invalid range errors + const preferredVersionRange: TSemver.Range | false = parseRange(preferredVersion); + if (!preferredVersionRange) { + return; } - if ( - preferredVersionRange && - versionRange && - semver!.subset(preferredVersionRange, versionRange, { includePrerelease: true }) - ) { - dependencies![name] = preferredVersion; + + const versionRange: TSemver.Range | false = parseRange(version); + if (!versionRange) { + return; + } + + if (definiteSemver.subset(preferredVersionRange, versionRange, SEMVER_COMPARE_OPTIONS)) { + dependencies[name] = preferredVersion; } } - } + }); } export const hooks: IPnpmfileHooks = { // Call the original pnpmfile (if it exists) afterAllResolved: (lockfile: IPnpmShrinkwrapYaml, context: IPnpmfileContext) => { context = init(context); - return userPnpmfile?.hooks?.afterAllResolved - ? userPnpmfile.hooks.afterAllResolved(lockfile, context) - : lockfile; + return userPnpmfile?.hooks?.afterAllResolved?.(lockfile, context) ?? lockfile; }, // Set the preferred versions in the package, then call the original pnpmfile (if it exists) readPackage: (pkg: IPackageJson, context: IPnpmfileContext) => { context = init(context); + // Apply the user pnpmfile readPackage hook first, in case it moves dependencies around, and so that it sees the true package.json + pkg = userPnpmfile?.hooks?.readPackage?.(pkg, context) ?? pkg; + + // Then do version refinement of preferredVersions, since this is just supposed to act as if pnpm "prefers" these resolutions during + // calculation. setPreferredVersions(pkg.dependencies); - setPreferredVersions(pkg.devDependencies); + if (workspaceVersions && workspaceVersions.get(pkg.name) === pkg.version) { + // devDependencies are only installed for workspace packages, so the rest of the time we can save the trouble of scanning. + setPreferredVersions(pkg.devDependencies); + } setPreferredVersions(pkg.optionalDependencies); - return userPnpmfile?.hooks?.readPackage ? userPnpmfile.hooks.readPackage(pkg, context) : pkg; + return pkg; }, // Call the original pnpmfile (if it exists) diff --git a/libraries/rush-lib/src/logic/pnpm/SubspaceGlobalPnpmfileShim.ts b/libraries/rush-lib/src/logic/pnpm/SubspaceGlobalPnpmfileShim.ts new file mode 100644 index 00000000000..02c143fcb31 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/SubspaceGlobalPnpmfileShim.ts @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +// The "rush install" or "rush update" commands will copy this template to +// "common/temp-split/global-pnpmfile.js" so that it can implement Rush-specific features. +// It reads its input data from "common/temp/pnpmfileSettings.json". The pnpmfile is +// required directly by this shim and is called after Rush's transformations are applied. + +import path from 'node:path'; + +// This file can use "import type" but otherwise should not reference any other modules, since it will +// be run from the "common/temp" directory +import type * as TSemver from 'semver'; + +import type { IPackageJson } from '@rushstack/node-core-library'; + +import type { + IPnpmfile, + IPnpmfileContext, + IPnpmfileHooks, + ISubspacePnpmfileShimSettings, + IWorkspaceProjectInfo +} from './IPnpmfile'; +import type { IPnpmShrinkwrapYaml } from './PnpmShrinkwrapFile'; + +let settings: ISubspacePnpmfileShimSettings; +let userPnpmfile: IPnpmfile | undefined; +let semver: typeof TSemver | undefined; + +// Initialize all external aspects of the pnpmfile shim. When using the shim, settings +// are always expected to be available. Init must be called before running any hook that +// depends on a resource obtained from or related to the settings, and will require modules +// once so they aren't repeatedly required in the hook functions. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function init(context: IPnpmfileContext | any): IPnpmfileContext { + // Sometimes PNPM may provide us a context arg that doesn't fit spec, ex.: + // https://github.com/pnpm/pnpm/blob/97c64bae4d14a8c8f05803f1d94075ee29c2df2f/packages/get-context/src/index.ts#L134 + // So we need to normalize the context format before we move on + if (typeof context !== 'object' || Array.isArray(context)) { + context = { + log: (message: string) => {}, + originalContext: context + } as IPnpmfileContext; + } + if (!settings) { + // Initialize the settings from file + if (!context.splitWorkspacePnpmfileShimSettings) { + context.splitWorkspacePnpmfileShimSettings = __non_webpack_require__('./pnpmfileSettings.json'); + } + settings = context.splitWorkspacePnpmfileShimSettings!; + } else if (!context.splitWorkspacePnpmfileShimSettings) { + // Reuse the already initialized settings + context.splitWorkspacePnpmfileShimSettings = settings; + } + // If a userPnpmfilePath is provided, we expect it to exist + if (!userPnpmfile && settings.userPnpmfilePath) { + userPnpmfile = require(settings.userPnpmfilePath); + } + // If a semverPath is provided, we expect it to exist + if (!semver && settings.semverPath) { + semver = require(settings.semverPath); + } + // Return the normalized context + return context as IPnpmfileContext; +} + +// Rewrite rush project referenced in split workspace. +// For example: "project-a": "workspace:*" --> "project-a": "link:../../project-a" +function rewriteRushProjectVersions( + packageName: string, + dependencies: { [dependencyName: string]: string } | undefined +): void { + if (!dependencies) { + return; + } + + if (!settings) { + throw new Error(`splitWorkspaceGlobalPnpmfileShimSettings not initialized`); + } + + const workspaceProject: IWorkspaceProjectInfo | undefined = + settings.subspaceProjects[packageName] || settings.workspaceProjects[packageName]; + if (!workspaceProject) { + return; + } + + for (const dependencyName of Object.keys(dependencies)) { + const currentVersion: string = dependencies[dependencyName]; + + if (currentVersion.startsWith('workspace:')) { + const workspaceProjectInfo: IWorkspaceProjectInfo | undefined = + settings.workspaceProjects[dependencyName]; + if (workspaceProjectInfo) { + // Case 1. "": "workspace:*" + let workspaceVersionProtocol: string = 'link:'; + + const injectedDependenciesSet: ReadonlySet = new Set(workspaceProject.injectedDependencies); + if (injectedDependenciesSet.has(dependencyName)) { + workspaceVersionProtocol = 'file:'; + } + let relativePath: string = path.normalize( + path.relative(workspaceProject.projectRelativeFolder, workspaceProjectInfo.projectRelativeFolder) + ); + // convert path in posix style, otherwise pnpm install will fail in subspace case + relativePath = relativePath.split(path.sep).join(path.posix.sep); + const newVersion: string = workspaceVersionProtocol + relativePath; + dependencies[dependencyName] = newVersion; + } else { + // Case 2. "": "workspace:@" + const packageSpec: string = currentVersion.slice('workspace:'.length); + const nameEndsAt: number = + packageSpec[0] === '@' ? packageSpec.slice(1).indexOf('@') + 1 : packageSpec.indexOf('@'); + const aliasedPackageName: string = nameEndsAt > 0 ? packageSpec.slice(0, nameEndsAt) : packageSpec; + // const depVersion: string = nameEndsAt > 0 ? packageSpec.slice(nameEndsAt + 1) : ''; + const aliasedWorkspaceProjectInfo: IWorkspaceProjectInfo | undefined = + settings.workspaceProjects[aliasedPackageName]; + if (aliasedWorkspaceProjectInfo) { + const relativePath: string = path.normalize( + path.relative( + workspaceProject.projectRelativeFolder, + aliasedWorkspaceProjectInfo.projectRelativeFolder + ) + ); + const newVersion: string = 'link:' + relativePath; + dependencies[dependencyName] = newVersion; + } + } + } else if (currentVersion.startsWith('npm:')) { + // Case 3. "": "npm:@" + const packageSpec: string = currentVersion.slice('npm:'.length); + const nameEndsAt: number = + packageSpec[0] === '@' ? packageSpec.slice(1).indexOf('@') + 1 : packageSpec.indexOf('@'); + const aliasedPackageName: string = nameEndsAt > 0 ? packageSpec.slice(0, nameEndsAt) : packageSpec; + // const depVersion: string = nameEndsAt > 0 ? packageSpec.slice(nameEndsAt + 1) : ''; + const aliasedWorkspaceProjectInfo: IWorkspaceProjectInfo | undefined = + settings.workspaceProjects[aliasedPackageName]; + if (aliasedWorkspaceProjectInfo) { + const relativePath: string = path.normalize( + path.relative( + workspaceProject.projectRelativeFolder, + aliasedWorkspaceProjectInfo.projectRelativeFolder + ) + ); + const newVersion: string = 'link:' + relativePath; + dependencies[dependencyName] = newVersion; + } + } + } +} + +export const hooks: IPnpmfileHooks = { + // Call the original pnpmfile (if it exists) + afterAllResolved: (lockfile: IPnpmShrinkwrapYaml, context: IPnpmfileContext) => { + context = init(context); + return userPnpmfile?.hooks?.afterAllResolved + ? userPnpmfile.hooks.afterAllResolved(lockfile, context) + : lockfile; + }, + + // Rewrite workspace protocol to link protocol for non split workspace projects + readPackage: (pkg: IPackageJson, context: IPnpmfileContext) => { + context = init(context); + rewriteRushProjectVersions(pkg.name, pkg.dependencies); + rewriteRushProjectVersions(pkg.name, pkg.devDependencies); + return userPnpmfile?.hooks?.readPackage ? userPnpmfile.hooks.readPackage(pkg, context) : pkg; + }, + + // Call the original pnpmfile (if it exists) + filterLog: userPnpmfile?.hooks?.filterLog +}; diff --git a/libraries/rush-lib/src/logic/pnpm/SubspacePnpmfileConfiguration.ts b/libraries/rush-lib/src/logic/pnpm/SubspacePnpmfileConfiguration.ts new file mode 100644 index 00000000000..b190671a0d0 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/SubspacePnpmfileConfiguration.ts @@ -0,0 +1,209 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { FileSystem, Import, JsonFile, type IDependenciesMetaTable } from '@rushstack/node-core-library'; + +import { subspacePnpmfileShimFilename, scriptsFolderPath } from '../../utilities/PathConstants'; +import type { ISubspacePnpmfileShimSettings, IWorkspaceProjectInfo } from './IPnpmfile'; +import type { RushConfiguration } from '../../api/RushConfiguration'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { PnpmPackageManager } from '../../api/packageManager/PnpmPackageManager'; +import { RushConstants } from '../RushConstants'; +import type { Subspace } from '../../api/Subspace'; +import type { PnpmOptionsConfiguration } from './PnpmOptionsConfiguration'; + +/** + * Loads PNPM's pnpmfile.js configuration, and invokes it to preprocess package.json files, + * optionally utilizing a pnpmfile shim to inject preferred versions. + */ +export class SubspacePnpmfileConfiguration { + /** + * Split workspace use global pnpmfile, because in split workspace, user may set `shared-workspace-lockfile=false`. + * That means each project owns their individual pnpmfile under project folder. While the global pnpmfile could be + * under the common/temp-split/ folder and be used by all split workspace projects. + */ + public static async writeCommonTempSubspaceGlobalPnpmfileAsync( + rushConfiguration: RushConfiguration, + subspace: Subspace, + variant: string | undefined + ): Promise { + if (rushConfiguration.packageManager !== 'pnpm') { + throw new Error( + `PnpmfileConfiguration cannot be used with package manager "${rushConfiguration.packageManager}"` + ); + } + + const targetDir: string = subspace.getSubspaceTempFolderPath(); + const subspaceGlobalPnpmfilePath: string = path.join(targetDir, RushConstants.pnpmfileGlobalFilename); + + // Write the shim itself + await FileSystem.copyFileAsync({ + sourcePath: `${scriptsFolderPath}/${subspacePnpmfileShimFilename}`, + destinationPath: subspaceGlobalPnpmfilePath + }); + + const subspaceGlobalPnpmfileShimSettings: ISubspacePnpmfileShimSettings = + SubspacePnpmfileConfiguration.getSubspacePnpmfileShimSettings(rushConfiguration, subspace, variant); + + // Write the settings file used by the shim + await JsonFile.saveAsync( + subspaceGlobalPnpmfileShimSettings, + path.join(targetDir, 'pnpmfileSettings.json'), + { + ensureFolderExists: true + } + ); + } + + public static getSubspacePnpmfileShimSettings( + rushConfiguration: RushConfiguration, + subspace: Subspace, + variant: string | undefined + ): ISubspacePnpmfileShimSettings { + const workspaceProjects: Record = {}; + const subspaceProjects: Record = {}; + + const projectNameToInjectedDependenciesMap: Map< + string, + Set + > = _getProjectNameToInjectedDependenciesMap(rushConfiguration, subspace); + for (const project of rushConfiguration.projects) { + const { packageName, projectRelativeFolder, packageJson } = project; + const workspaceProjectInfo: IWorkspaceProjectInfo = { + packageName, + projectRelativeFolder, + packageVersion: packageJson.version, + injectedDependencies: Array.from(projectNameToInjectedDependenciesMap.get(packageName) || []) + }; + (subspace.contains(project) ? subspaceProjects : workspaceProjects)[packageName] = workspaceProjectInfo; + } + + const settings: ISubspacePnpmfileShimSettings = { + workspaceProjects, + subspaceProjects, + semverPath: Import.resolveModule({ modulePath: 'semver', baseFolderPath: __dirname }) + }; + + // common/config/subspaces//.pnpmfile.cjs + const userPnpmfilePath: string = path.join( + subspace.getVariantDependentSubspaceConfigFolderPath(variant), + (rushConfiguration.packageManagerWrapper as PnpmPackageManager).pnpmfileFilename + ); + if (FileSystem.exists(userPnpmfilePath)) { + settings.userPnpmfilePath = userPnpmfilePath; + } + + return settings; + } +} + +function _getProjectNameToInjectedDependenciesMap( + rushConfiguration: RushConfiguration, + subspace: Subspace +): Map> { + const projectNameToInjectedDependenciesMap: Map> = new Map(); + + const workspaceProjectsMap: Map = new Map(); + const subspaceProjectsMap: Map = new Map(); + for (const project of rushConfiguration.projects) { + if (subspace.contains(project)) { + subspaceProjectsMap.set(project.packageName, project); + } else { + workspaceProjectsMap.set(project.packageName, project); + } + + projectNameToInjectedDependenciesMap.set(project.packageName, new Set()); + } + + const processTransitiveInjectedInstallQueue: Array = []; + + for (const subspaceProject of subspaceProjectsMap.values()) { + const injectedDependencySet: Set = new Set(); + const dependenciesMeta: IDependenciesMetaTable | undefined = subspaceProject.packageJson.dependenciesMeta; + if (dependenciesMeta) { + for (const [dependencyName, { injected }] of Object.entries(dependenciesMeta)) { + if (injected) { + injectedDependencySet.add(dependencyName); + projectNameToInjectedDependenciesMap.get(subspaceProject.packageName)?.add(dependencyName); + + //if this dependency is in the same subspace, leave as it is, PNPM will handle it + //if this dependency is in another subspace, then it is transitive injected installation + //so, we need to let all the workspace dependencies along the dependency chain to use injected installation + if (!subspaceProjectsMap.has(dependencyName)) { + processTransitiveInjectedInstallQueue.push(workspaceProjectsMap.get(dependencyName)!); + } + } + } + } + + // if alwaysInjectDependenciesFromOtherSubspaces policy is true in pnpm-config.json + // and the dependency is not injected yet + // and the dependency is in another subspace + // then, make this dependency as injected dependency + const pnpmOptions: PnpmOptionsConfiguration | undefined = + subspace.getPnpmOptions() || rushConfiguration.pnpmOptions; + if (pnpmOptions && pnpmOptions.alwaysInjectDependenciesFromOtherSubspaces) { + const dependencyProjects: ReadonlySet = subspaceProject.dependencyProjects; + for (const dependencyProject of dependencyProjects) { + const dependencyName: string = dependencyProject.packageName; + if (!injectedDependencySet.has(dependencyName) && !subspaceProjectsMap.has(dependencyName)) { + projectNameToInjectedDependenciesMap.get(subspaceProject.packageName)?.add(dependencyName); + // process transitive injected installation + processTransitiveInjectedInstallQueue.push(workspaceProjectsMap.get(dependencyName)!); + } + } + } + } + + // rewrite all workspace dependencies to injected install all for transitive injected installation case + while (processTransitiveInjectedInstallQueue.length > 0) { + const currentProject: RushConfigurationProject | undefined = + processTransitiveInjectedInstallQueue.shift(); + const dependencies: Record | undefined = currentProject?.packageJson?.dependencies; + const optionalDependencies: Record | undefined = + currentProject?.packageJson?.optionalDependencies; + if (currentProject) { + if (dependencies) { + _processDependenciesForTransitiveInjectedInstall( + projectNameToInjectedDependenciesMap, + processTransitiveInjectedInstallQueue, + dependencies, + currentProject, + rushConfiguration + ); + } + if (optionalDependencies) { + _processDependenciesForTransitiveInjectedInstall( + projectNameToInjectedDependenciesMap, + processTransitiveInjectedInstallQueue, + optionalDependencies, + currentProject, + rushConfiguration + ); + } + } + } + + return projectNameToInjectedDependenciesMap; +} + +function _processDependenciesForTransitiveInjectedInstall( + projectNameToInjectedDependencies: Map>, + processTransitiveInjectedInstallQueue: Array, + dependencies: Record, + currentProject: RushConfigurationProject, + rushConfiguration: RushConfiguration +): void { + for (const dependencyName in dependencies) { + if (dependencies[dependencyName].startsWith('workspace:')) { + projectNameToInjectedDependencies.get(currentProject.packageName)?.add(dependencyName); + const nextProject: RushConfigurationProject | undefined = + rushConfiguration.getProjectByName(dependencyName); + if (nextProject) { + processTransitiveInjectedInstallQueue.push(nextProject); + } + } + } +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts b/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts index bc16d4891cf..2691fb28569 100644 --- a/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts +++ b/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts @@ -1,12 +1,21 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; +import { FileSystem, JsonFile } from '@rushstack/node-core-library'; import { PnpmOptionsConfiguration } from '../PnpmOptionsConfiguration'; +import { TestUtilities } from '@rushstack/heft-config-file'; -const fakeCommonTempFolder: string = path.join(__dirname, 'common', 'temp'); +const PACKAGE_ROOT: string = path.resolve(__dirname, '../../../..'); +const TEST_TEMP_FOLDER: string = `${PACKAGE_ROOT}/temp/pnpm-config-update-test`; + +const fakeCommonTempFolder: string = `${__dirname}/common/temp`; describe(PnpmOptionsConfiguration.name, () => { + afterEach(async () => { + await FileSystem.deleteFolderAsync(TEST_TEMP_FOLDER); + }); + it('throw error if pnpm-config.json does not exist', () => { expect(() => { PnpmOptionsConfiguration.loadFromJsonFileOrThrow( @@ -22,7 +31,7 @@ describe(PnpmOptionsConfiguration.name, () => { `${__dirname}/jsonFiles/pnpm-config-unknown.json`, fakeCommonTempFolder ) - ).toThrow(/Additional properties not allowed: unknownProperty/); + ).toThrow(/must NOT have additional properties/); }); it('loads overrides', () => { @@ -31,14 +40,14 @@ describe(PnpmOptionsConfiguration.name, () => { fakeCommonTempFolder ); - expect(pnpmConfiguration.globalOverrides).toEqual({ + expect(TestUtilities.stripAnnotations(pnpmConfiguration.globalOverrides)).toEqual({ foo: '^1.0.0', quux: 'npm:@myorg/quux@^1.0.0', 'bar@^2.1.0': '3.0.0', 'qar@1>zoo': '2' }); - expect(pnpmConfiguration.environmentVariables).toEqual({ + expect(TestUtilities.stripAnnotations(pnpmConfiguration.environmentVariables)).toEqual({ NODE_OPTIONS: { value: '--max-old-space-size=4096', override: false @@ -52,7 +61,7 @@ describe(PnpmOptionsConfiguration.name, () => { fakeCommonTempFolder ); - expect(pnpmConfiguration.globalPackageExtensions).toEqual({ + expect(TestUtilities.stripAnnotations(pnpmConfiguration.globalPackageExtensions)).toEqual({ 'react-redux': { peerDependencies: { 'react-dom': '*' @@ -67,6 +76,333 @@ describe(PnpmOptionsConfiguration.name, () => { fakeCommonTempFolder ); - expect(pnpmConfiguration.globalNeverBuiltDependencies).toEqual(['fsevents', 'level']); + expect(TestUtilities.stripAnnotations(pnpmConfiguration.globalNeverBuiltDependencies)).toEqual([ + 'fsevents', + 'level' + ]); + }); + + it('loads onlyBuiltDependencies', () => { + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + `${__dirname}/jsonFiles/pnpm-config-onlyBuiltDependencies.json`, + fakeCommonTempFolder + ); + + expect(TestUtilities.stripAnnotations(pnpmConfiguration.globalOnlyBuiltDependencies)).toEqual([ + 'esbuild', + 'playwright', + '@swc/core' + ]); + }); + + it('loads allowBuilds', () => { + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + `${__dirname}/jsonFiles/pnpm-config-allowBuilds.json`, + fakeCommonTempFolder + ); + + expect(TestUtilities.stripAnnotations(pnpmConfiguration.globalAllowBuilds)).toEqual({ + esbuild: true, + '@parcel/watcher': true, + fsevents: false + }); + }); + + it('loads minimumReleaseAgeMinutes', () => { + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + `${__dirname}/jsonFiles/pnpm-config-minimumReleaseAge.json`, + fakeCommonTempFolder + ); + + expect(pnpmConfiguration.minimumReleaseAgeMinutes).toEqual(1440); + expect(TestUtilities.stripAnnotations(pnpmConfiguration.minimumReleaseAgeExclude)).toEqual([ + 'webpack', + '@myorg/*' + ]); + }); + + it('loads deprecated minimumReleaseAge as minimumReleaseAgeMinutes', () => { + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + `${__dirname}/jsonFiles/pnpm-config-minimumReleaseAge-deprecated.json`, + fakeCommonTempFolder + ); + + expect(pnpmConfiguration.minimumReleaseAgeMinutes).toEqual(720); + }); + + it('throws if both minimumReleaseAge and minimumReleaseAgeMinutes are specified', () => { + expect(() => + PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + `${__dirname}/jsonFiles/pnpm-config-minimumReleaseAge-both.json`, + fakeCommonTempFolder + ) + ).toThrow(/Both settings cannot be specified together/); + }); + + it('loads trustPolicy', () => { + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + `${__dirname}/jsonFiles/pnpm-config-trustPolicy.json`, + fakeCommonTempFolder + ); + + expect(pnpmConfiguration.trustPolicy).toEqual('no-downgrade'); + expect(TestUtilities.stripAnnotations(pnpmConfiguration.trustPolicyExclude)).toEqual([ + '@myorg/*', + 'chokidar@4.0.3', + 'webpack@4.47.0 || 5.102.1', + '@babel/core@7.28.5' + ]); + expect(pnpmConfiguration.trustPolicyIgnoreAfterMinutes).toEqual(20160); + }); + + it('loads catalog and catalogs', () => { + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + `${__dirname}/jsonFiles/pnpm-config-catalog.json`, + fakeCommonTempFolder + ); + + expect(TestUtilities.stripAnnotations(pnpmConfiguration.globalCatalogs)).toEqual({ + default: { + react: '^18.0.0', + 'react-dom': '^18.0.0', + typescript: '~5.3.0' + }, + frontend: { + vue: '^3.4.0', + 'vue-router': '^4.2.0' + }, + backend: { + express: '^4.18.0', + fastify: '^4.26.0' + } + }); + }); + + describe('updateGlobalPatchedDependencies', () => { + function update( + patchedDependencies: Record | undefined + ): Record | undefined { + const testConfigPath: string = `${TEST_TEMP_FOLDER}/pnpm-config-patched-deps-test.json`; + JsonFile.save( + { + $schema: 'https://developer.microsoft.com/json-schemas/rush/v5/pnpm-config.schema.json' + }, + testConfigPath, + { ensureFolderExists: true } + ); + + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + testConfigPath, + fakeCommonTempFolder + ); + pnpmConfiguration.updateGlobalPatchedDependencies(patchedDependencies); + return pnpmConfiguration.globalPatchedDependencies; + } + + it('converts absolute patch paths under the common temp folder back to relative paths', () => { + // pnpm >= 9 "patch-commit"/"patch-remove" rewrite pre-existing "patchedDependencies" entries + // using absolute paths pointing into the common/temp folder + expect( + update({ + 'example@1.0.0': path.join(fakeCommonTempFolder, 'patches', 'example@1.0.0.patch'), + '@scope/example2@2.0.0': path.join(fakeCommonTempFolder, 'patches', '@scope__example2@2.0.0.patch') + }) + ).toEqual({ + 'example@1.0.0': 'patches/example@1.0.0.patch', + '@scope/example2@2.0.0': 'patches/@scope__example2@2.0.0.patch' + }); + }); + + it('leaves relative patch paths unchanged', () => { + expect( + update({ + 'example@1.0.0': 'patches/example@1.0.0.patch' + }) + ).toEqual({ + 'example@1.0.0': 'patches/example@1.0.0.patch' + }); + }); + + it('leaves absolute patch paths outside the common temp folder unchanged', () => { + const outsidePath: string = path.join(path.sep, 'somewhere', 'else', 'example@1.0.0.patch'); + expect( + update({ + 'example@1.0.0': outsidePath + }) + ).toEqual({ + 'example@1.0.0': outsidePath + }); + }); + + it('passes through undefined', () => { + expect(update(undefined)).toBeUndefined(); + }); + }); + + describe('updateGlobalCatalogs', () => { + it('updates and saves globalCatalogs to pnpm-config.json', async () => { + const testConfigPath: string = `${TEST_TEMP_FOLDER}/pnpm-config-update-test.json`; + + const initialConfig = { + globalCatalogs: { + default: { + react: '^18.0.0' + } + } + }; + await JsonFile.saveAsync(initialConfig, testConfigPath, { ensureFolderExists: true }); + + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + testConfigPath, + fakeCommonTempFolder + ); + + const updatedCatalogs = { + default: { + react: '^18.2.0', + 'react-dom': '^18.2.0' + }, + frontend: { + vue: '^3.4.0' + } + }; + await pnpmConfiguration.updateGlobalCatalogsAsync(updatedCatalogs); + + const reloadedConfig: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + testConfigPath, + fakeCommonTempFolder + ); + + expect(TestUtilities.stripAnnotations(reloadedConfig.globalCatalogs)).toEqual(updatedCatalogs); + }); + + it('handles undefined catalogs', async () => { + const testConfigPath: string = `${TEST_TEMP_FOLDER}/pnpm-config-undefined-test.json`; + + const initialConfig = { + globalCatalogs: { + default: { + react: '^18.0.0' + } + } + }; + await JsonFile.saveAsync(initialConfig, testConfigPath, { ensureFolderExists: true }); + + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + testConfigPath, + fakeCommonTempFolder + ); + + await pnpmConfiguration.updateGlobalCatalogsAsync(undefined); + + const reloadedConfig: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + testConfigPath, + fakeCommonTempFolder + ); + + expect(reloadedConfig.globalCatalogs).toBeUndefined(); + }); + }); + + describe('$schema handling', () => { + it('does not fail when $schema is undefined', async () => { + const testConfigPath: string = `${TEST_TEMP_FOLDER}/pnpm-config-no-schema.json`; + + const configWithoutSchema = { + globalCatalogs: { + default: { + react: '^18.0.0' + } + } + }; + await JsonFile.saveAsync(configWithoutSchema, testConfigPath, { ensureFolderExists: true }); + + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + testConfigPath, + fakeCommonTempFolder + ); + + const updatedCatalogs = { + default: { + react: '^18.2.0' + } + }; + + await expect(pnpmConfiguration.updateGlobalCatalogsAsync(updatedCatalogs)).resolves.not.toThrow(); + + const reloadedConfig: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + testConfigPath, + fakeCommonTempFolder + ); + + expect(TestUtilities.stripAnnotations(reloadedConfig.globalCatalogs)).toEqual(updatedCatalogs); + }); + + it('preserves $schema when it exists', async () => { + const testConfigPath: string = `${TEST_TEMP_FOLDER}/pnpm-config-with-schema.json`; + + const configWithSchema = { + $schema: 'https://developer.microsoft.com/json-schemas/rush/v5/pnpm-config.schema.json', + globalCatalogs: { + default: { + react: '^18.0.0' + } + } + }; + await JsonFile.saveAsync(configWithSchema, testConfigPath, { ensureFolderExists: true }); + + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + testConfigPath, + fakeCommonTempFolder + ); + + const updatedCatalogs = { + default: { + react: '^18.2.0' + } + }; + await pnpmConfiguration.updateGlobalCatalogsAsync(updatedCatalogs); + + const savedConfig = await JsonFile.loadAsync(testConfigPath); + expect(savedConfig.$schema).toBe( + 'https://developer.microsoft.com/json-schemas/rush/v5/pnpm-config.schema.json' + ); + }); + + it('handles undefined in updateGlobalOnlyBuiltDependenciesAsync', async () => { + const testConfigPath: string = `${TEST_TEMP_FOLDER}/pnpm-config-undefined-test.json`; + + const initialConfig = { + $schema: 'https://developer.microsoft.com/json-schemas/rush/v5/pnpm-config.schema.json', + globalOnlyBuiltDependencies: ['node-gyp', 'esbuild'] + }; + await JsonFile.saveAsync(initialConfig, testConfigPath, { ensureFolderExists: true }); + + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + testConfigPath, + fakeCommonTempFolder + ); + + expect(TestUtilities.stripAnnotations(pnpmConfiguration.globalOnlyBuiltDependencies)).toEqual([ + 'node-gyp', + 'esbuild' + ]); + + await expect( + pnpmConfiguration.updateGlobalOnlyBuiltDependenciesAsync(undefined) + ).resolves.not.toThrow(); + + const reloadedConfig: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + testConfigPath, + fakeCommonTempFolder + ); + expect(reloadedConfig.globalOnlyBuiltDependencies).toBeUndefined(); + + const savedConfigJson = await JsonFile.loadAsync(testConfigPath); + expect(savedConfigJson.$schema).toBe( + 'https://developer.microsoft.com/json-schemas/rush/v5/pnpm-config.schema.json' + ); + expect(savedConfigJson.globalOnlyBuiltDependencies).toBeUndefined(); + }); }); }); diff --git a/libraries/rush-lib/src/logic/pnpm/test/PnpmShrinkwrapConverters.test.ts b/libraries/rush-lib/src/logic/pnpm/test/PnpmShrinkwrapConverters.test.ts new file mode 100644 index 00000000000..045de7afede --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/PnpmShrinkwrapConverters.test.ts @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { LockfileFileV9, PackageSnapshot, ProjectSnapshot } from '@pnpm/lockfile.types-900'; +import { convertLockfileV9ToLockfileObject } from '../PnpmShrinkWrapFileConverters'; +import { FileSystem } from '@rushstack/node-core-library'; +import yamlModule from 'js-yaml'; + +describe(convertLockfileV9ToLockfileObject.name, () => { + const lockfileContent: string = FileSystem.readFile( + `${__dirname}/yamlFiles/pnpm-lock-v9/pnpm-lock-v9.yaml` + ); + const lockfileJson: LockfileFileV9 = yamlModule.load(lockfileContent) as LockfileFileV9; + const lockfile = convertLockfileV9ToLockfileObject(lockfileJson); + + it('merge packages and snapshots', () => { + const packages = new Map(Object.entries(lockfile.packages || {})); + const padLeftPackage = packages.get('pad-left@2.1.0'); + expect(padLeftPackage).toBeDefined(); + expect(padLeftPackage?.dependencies).toEqual({ + 'repeat-string': '1.6.1' + }); + }); + + it("importers['.']", () => { + const importers = new Map(Object.entries(lockfile.importers || {})); + + const currentPackage = importers.get('.'); + expect(currentPackage).toBeDefined(); + + expect(currentPackage?.dependencies).toEqual({ + jquery: '3.7.1', + 'pad-left': '2.1.0' + }); + + expect(currentPackage?.specifiers).toEqual({ + jquery: '^3.7.1', + 'pad-left': '^2.1.0' + }); + }); + + it('no nullish values', () => { + const importers = new Map(Object.entries(lockfile.importers || {})); + + const currentPackage = importers.get('.'); + const props = Object.keys(currentPackage || {}); + expect(props).toContain('dependencies'); + expect(props).toContain('specifiers'); + expect(props).not.toContain('optionalDependencies'); + expect(props).not.toContain('devDependencies'); + }); +}); diff --git a/libraries/rush-lib/src/logic/pnpm/test/PnpmShrinkwrapFile.test.ts b/libraries/rush-lib/src/logic/pnpm/test/PnpmShrinkwrapFile.test.ts index f1424adf287..07a6f1b4e9d 100644 --- a/libraries/rush-lib/src/logic/pnpm/test/PnpmShrinkwrapFile.test.ts +++ b/libraries/rush-lib/src/logic/pnpm/test/PnpmShrinkwrapFile.test.ts @@ -1,5 +1,14 @@ -import { DependencySpecifier, DependencySpecifierType } from '../../DependencySpecifier'; -import { PnpmShrinkwrapFile, parsePnpmDependencyKey } from '../PnpmShrinkwrapFile'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { type DependencySpecifier, DependencySpecifierType } from '../../DependencySpecifier'; +import { PnpmShrinkwrapFile, parsePnpm9DependencyKey, parsePnpmDependencyKey } from '../PnpmShrinkwrapFile'; +import { RushConfiguration } from '../../../api/RushConfiguration'; +import type { RushConfigurationProject } from '../../../api/RushConfigurationProject'; +import type { Subspace } from '../../../api/Subspace'; +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; +import { PnpmOptionsConfiguration } from '../PnpmOptionsConfiguration'; +import { AlreadyReportedError } from '@rushstack/node-core-library'; const DEPENDENCY_NAME: string = 'dependency_name'; const SCOPED_DEPENDENCY_NAME: string = '@scope/dependency_name'; @@ -75,7 +84,7 @@ describe(PnpmShrinkwrapFile.name, () => { ); }); - it('Supports aliased package specifiers', () => { + it('Supports aliased package specifiers (v5)', () => { const parsedSpecifier: DependencySpecifier | undefined = parsePnpmDependencyKey( SCOPED_DEPENDENCY_NAME, `/${DEPENDENCY_NAME}/${VERSION}` @@ -83,7 +92,18 @@ describe(PnpmShrinkwrapFile.name, () => { expect(parsedSpecifier).toBeDefined(); expect(parsedSpecifier!.specifierType).toBe(DependencySpecifierType.Alias); expect(parsedSpecifier!.packageName).toBe(SCOPED_DEPENDENCY_NAME); - expect(parsedSpecifier!.versionSpecifier).toMatchInlineSnapshot(`"npm:dependency_name@1.4.0"`); + expect(parsedSpecifier!.versionSpecifier).toMatchInlineSnapshot(`"npm:${DEPENDENCY_NAME}@${VERSION}"`); + }); + + it('Supports aliased package specifiers (v6)', () => { + const parsedSpecifier: DependencySpecifier | undefined = parsePnpmDependencyKey( + SCOPED_DEPENDENCY_NAME, + `/${DEPENDENCY_NAME}@${VERSION}` + ); + expect(parsedSpecifier).toBeDefined(); + expect(parsedSpecifier!.specifierType).toBe(DependencySpecifierType.Alias); + expect(parsedSpecifier!.packageName).toBe(SCOPED_DEPENDENCY_NAME); + expect(parsedSpecifier!.versionSpecifier).toMatchInlineSnapshot(`"npm:${DEPENDENCY_NAME}@${VERSION}"`); }); it('Supports URL package specifiers', () => { @@ -109,4 +129,699 @@ describe(PnpmShrinkwrapFile.name, () => { } }); }); + + describe(parsePnpm9DependencyKey.name, () => { + it('Does not support file:// specifiers', () => { + expect(parsePnpm9DependencyKey(DEPENDENCY_NAME, 'file:///path/to/file')).toBeUndefined(); + expect(parsePnpm9DependencyKey(DEPENDENCY_NAME, 'pad-left@file:///path/to/file')).toBeUndefined(); + expect(parsePnpm9DependencyKey(DEPENDENCY_NAME, 'link:///path/to/file')).toBeUndefined(); + }); + + it('Supports a variety of non-aliased package specifiers', () => { + function testSpecifiers(specifiers: string[], expectedName: string, expectedVersion: string): void { + for (const specifier of specifiers) { + const parsedSpecifier: DependencySpecifier | undefined = parsePnpm9DependencyKey( + expectedName, + specifier + ); + expect(parsedSpecifier).toBeDefined(); + expect(parsedSpecifier!.specifierType).toBe(DependencySpecifierType.Version); + expect(parsedSpecifier!.packageName).toBe(expectedName); + expect(parsedSpecifier!.versionSpecifier).toBe(expectedVersion); + } + } + + // non-scoped, non-prerelease + testSpecifiers( + [`${DEPENDENCY_NAME}@${VERSION}`, `${DEPENDENCY_NAME}@${VERSION}(peer@3.5.0+peer2@1.17.7)`], + DEPENDENCY_NAME, + VERSION + ); + + // scoped, non-prerelease + testSpecifiers( + [ + `${SCOPED_DEPENDENCY_NAME}@${VERSION}`, + `${SCOPED_DEPENDENCY_NAME}@${VERSION}(peer@3.5.0+peer2@1.17.7)` + ], + SCOPED_DEPENDENCY_NAME, + VERSION + ); + + // non-scoped, prerelease + testSpecifiers( + [ + `${DEPENDENCY_NAME}@${PRERELEASE_VERSION}`, + `${DEPENDENCY_NAME}@${PRERELEASE_VERSION}(peer@3.5.0+peer2@1.17.7)` + ], + DEPENDENCY_NAME, + PRERELEASE_VERSION + ); + + // scoped, prerelease + testSpecifiers( + [ + `${SCOPED_DEPENDENCY_NAME}@${PRERELEASE_VERSION}`, + `${SCOPED_DEPENDENCY_NAME}@${PRERELEASE_VERSION}(peer@3.5.0+peer2@1.17.7)` + ], + SCOPED_DEPENDENCY_NAME, + PRERELEASE_VERSION + ); + }); + + it('Supports aliased package specifiers (v9)', () => { + const parsedSpecifier: DependencySpecifier | undefined = parsePnpm9DependencyKey( + SCOPED_DEPENDENCY_NAME, + `${DEPENDENCY_NAME}@${VERSION}` + ); + expect(parsedSpecifier).toBeDefined(); + expect(parsedSpecifier!.specifierType).toBe(DependencySpecifierType.Alias); + expect(parsedSpecifier!.packageName).toBe(SCOPED_DEPENDENCY_NAME); + expect(parsedSpecifier!.versionSpecifier).toMatchInlineSnapshot(`"npm:${DEPENDENCY_NAME}@${VERSION}"`); + }); + + it('Supports URL package specifiers', () => { + const specifiers: string[] = [ + 'https://github.com/jonschlinkert/pad-left/tarball/2.1.0', + 'https://xxx.xxxx.org/pad-left/-/pad-left-2.1.0.tgz', + 'https://codeload.github.com/jonschlinkert/pad-left/tar.gz/7798d648225aa5d879660a37c408ab4675b65ac7', + `${SCOPED_DEPENDENCY_NAME}@http://abc.com/jonschlinkert/pad-left/tarball/2.1.0`, + `${SCOPED_DEPENDENCY_NAME}@https://xxx.xxxx.org/pad-left/-/pad-left-2.1.0.tgz`, + `${SCOPED_DEPENDENCY_NAME}@https://codeload.github.com/jonschlinkert/pad-left/tar.gz/7798d648225aa5d879660a37c408ab4675b65ac7` + ]; + + for (const specifier of specifiers) { + const parsedSpecifier: DependencySpecifier | undefined = parsePnpm9DependencyKey( + SCOPED_DEPENDENCY_NAME, + specifier + ); + expect(parsedSpecifier).toBeDefined(); + expect(parsedSpecifier!.specifierType).toBe(DependencySpecifierType.Remote); + expect(parsedSpecifier!.packageName).toBe(SCOPED_DEPENDENCY_NAME); + expect(parsedSpecifier!.versionSpecifier).toBe(specifier.replace(`${SCOPED_DEPENDENCY_NAME}@`, '')); + } + }); + }); + + describe('getIntegrityForImporter', () => { + it('produces different hashes when sub-dependency resolutions change', () => { + // This test verifies that changes to sub-dependency resolutions are detected. + // The issue is that if package A depends on B, and B's resolution of C changes + // (e.g., from C@1.3 to C@1.2), the integrity hash for A should change. + // This is important for build orchestrators that rely on shrinkwrap-deps.json + // to detect changes to resolution and invalidate caches appropriately. + + // Two shrinkwrap files with the same package but different sub-dependency resolutions + const shrinkwrapContent1: string = ` +lockfileVersion: '9.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false +importers: + .: + dependencies: + foo: + specifier: ~1.0.0 + version: 1.0.0 +packages: + foo@1.0.0: + resolution: + integrity: sha512-abc123== + dependencies: + bar: 1.3.0 + bar@1.3.0: + resolution: + integrity: sha512-bar130== +snapshots: + foo@1.0.0: + dependencies: + bar: 1.3.0 + bar@1.3.0: {} +`; + + const shrinkwrapContent2: string = ` +lockfileVersion: '9.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false +importers: + .: + dependencies: + foo: + specifier: ~1.0.0 + version: 1.0.0 +packages: + foo@1.0.0: + resolution: + integrity: sha512-abc123== + dependencies: + bar: 1.2.0 + bar@1.2.0: + resolution: + integrity: sha512-bar120== +snapshots: + foo@1.0.0: + dependencies: + bar: 1.2.0 + bar@1.2.0: {} +`; + + const shrinkwrapFile1 = PnpmShrinkwrapFile.loadFromString(shrinkwrapContent1, { + subspaceHasNoProjects: false + }); + const shrinkwrapFile2 = PnpmShrinkwrapFile.loadFromString(shrinkwrapContent2, { + subspaceHasNoProjects: false + }); + + // Clear cache to ensure fresh computation + PnpmShrinkwrapFile.clearCache(); + + const integrityMap1 = shrinkwrapFile1.getIntegrityForImporter('.'); + const integrityMap2 = shrinkwrapFile2.getIntegrityForImporter('.'); + + // Both should have integrity maps + expect(integrityMap1).toBeDefined(); + expect(integrityMap2).toBeDefined(); + + // The integrity for 'foo@1.0.0' should be different because bar's resolution changed + const fooIntegrity1 = integrityMap1!.get('foo@1.0.0'); + const fooIntegrity2 = integrityMap2!.get('foo@1.0.0'); + + expect(fooIntegrity1).toBeDefined(); + expect(fooIntegrity2).toBeDefined(); + + // This is the key assertion: the integrity hashes should be different + // because the sub-dependency (bar) resolved to different versions + expect(fooIntegrity1).not.toEqual(fooIntegrity2); + }); + + it('includes workspace-local link: dependencies by recursing into their importer entries', () => { + // This test verifies that link: (workspace-local) dependencies are no longer filtered out. + // The shrinkwrap-deps.json for an importer should include hashes from its workspace + // dependencies' importer sections, all the way down the tree. + // + // In a real Rush repo (no subspaces), importer keys start with '../../' and + // link: paths start with '../'. + // + // Topology: project-1 -> (link:) project-2 -> lodash@4.17.21 + + const shrinkwrapContent: string = ` +lockfileVersion: '9.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false +importers: + .: + {} + ../../project-1: + dependencies: + project-2: + specifier: workspace:* + version: link:../project-2 + ../../project-2: + dependencies: + lodash: + specifier: ^4.17.0 + version: 4.17.21 +packages: + lodash@4.17.21: + resolution: + integrity: sha512-lodash== +snapshots: + lodash@4.17.21: {} +`; + + const shrinkwrapFile = PnpmShrinkwrapFile.loadFromString(shrinkwrapContent, { + subspaceHasNoProjects: false + }); + + PnpmShrinkwrapFile.clearCache(); + + const proj1IntegrityMap = shrinkwrapFile.getIntegrityForImporter('../../project-1'); + + expect(proj1IntegrityMap).toBeDefined(); + + // project-1's integrity map should include project-2's importer entry + expect(proj1IntegrityMap!.has('../../project-2')).toBe(true); + + // It should also include the transitive external dependency of project-2 + expect(proj1IntegrityMap!.has('lodash@4.17.21')).toBe(true); + + // The integrity map for project-2 itself should also be populated + const proj2IntegrityMap = shrinkwrapFile.getIntegrityForImporter('../../project-2'); + expect(proj2IntegrityMap).toBeDefined(); + expect(proj2IntegrityMap!.has('../../project-2')).toBe(true); + expect(proj2IntegrityMap!.has('lodash@4.17.21')).toBe(true); + }); + + it('produces different hashes when a workspace-local dependency changes', () => { + // This test verifies that changing the dependencies of a workspace-local package + // causes the dependent importer's integrity to differ. + // + // Topology: project-1 -> (link:) project-2 -> lodash@4.17.x (version differs between cases) + + const buildContent = (lodashVersion: string): string => ` +lockfileVersion: '9.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false +importers: + .: + {} + ../../project-1: + dependencies: + project-2: + specifier: workspace:* + version: link:../project-2 + ../../project-2: + dependencies: + lodash: + specifier: ^4.17.0 + version: ${lodashVersion} +packages: + lodash@${lodashVersion}: + resolution: + integrity: sha512-lodash-${lodashVersion}== +snapshots: + lodash@${lodashVersion}: {} +`; + + const shrinkwrapFile1 = PnpmShrinkwrapFile.loadFromString(buildContent('4.17.21'), { + subspaceHasNoProjects: false + }); + const shrinkwrapFile2 = PnpmShrinkwrapFile.loadFromString(buildContent('4.17.20'), { + subspaceHasNoProjects: false + }); + + PnpmShrinkwrapFile.clearCache(); + + const proj1IntegrityMap1 = shrinkwrapFile1.getIntegrityForImporter('../../project-1'); + const proj1IntegrityMap2 = shrinkwrapFile2.getIntegrityForImporter('../../project-1'); + + expect(proj1IntegrityMap1).toBeDefined(); + expect(proj1IntegrityMap2).toBeDefined(); + + // The self-hash of project-1 does NOT change because the root importer object itself is + // identical in both cases (it still references link:../project-2). However, project-2's + // integrity hash should differ because its lodash dependency resolved to a different version. + const proj2Integrity1 = proj1IntegrityMap1!.get('../../project-2'); + const proj2Integrity2 = proj1IntegrityMap2!.get('../../project-2'); + + expect(proj2Integrity1).toBeDefined(); + expect(proj2Integrity2).toBeDefined(); + expect(proj2Integrity1).not.toEqual(proj2Integrity2); + }); + + it('scenario 1: workspace project 1 -> workspace project 2 -> external dep 1 -> external dep 2', () => { + // Tests the full chain: project-1 links to project-2, project-2 depends on ext-a, + // and ext-a transitively depends on ext-b. All four should appear in project-1's integrity map. + + const shrinkwrapContent: string = ` +lockfileVersion: '9.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false +importers: + .: + {} + ../../project-1: + dependencies: + project-2: + specifier: workspace:* + version: link:../project-2 + ../../project-2: + dependencies: + ext-a: + specifier: ^1.0.0 + version: 1.0.0 +packages: + ext-a@1.0.0: + resolution: + integrity: sha512-ext-a== + ext-b@1.0.0: + resolution: + integrity: sha512-ext-b== +snapshots: + ext-a@1.0.0: + dependencies: + ext-b: 1.0.0 + ext-b@1.0.0: {} +`; + + const shrinkwrapFile = PnpmShrinkwrapFile.loadFromString(shrinkwrapContent, { + subspaceHasNoProjects: false + }); + + PnpmShrinkwrapFile.clearCache(); + + const proj1IntegrityMap = shrinkwrapFile.getIntegrityForImporter('../../project-1'); + + expect(proj1IntegrityMap).toBeDefined(); + // project-2's importer entry + expect(proj1IntegrityMap!.has('../../project-2')).toBe(true); + // ext-a (direct dep of project-2) + expect(proj1IntegrityMap!.has('ext-a@1.0.0')).toBe(true); + // ext-b (transitive dep through ext-a) + expect(proj1IntegrityMap!.has('ext-b@1.0.0')).toBe(true); + }); + + it('scenario 2: workspace project 1 -> external dep 1 -> (link:) workspace project 2 -> external dep 2', () => { + // Tests that when an external package's snapshot has a link: dependency pointing back into + // the workspace, the linked workspace project's integrity is fully captured. + // + // project-1 depends on ext-a (external). + // ext-a's snapshot has a link: dep that resolves to project-2 (a workspace project). + // project-2 depends on ext-b (external). + // All four entries should appear in project-1's integrity map. + + const shrinkwrapContent: string = ` +lockfileVersion: '9.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false +importers: + .: + {} + ../../project-1: + dependencies: + ext-a: + specifier: ^1.0.0 + version: 1.0.0 + ../../project-2: + dependencies: + ext-b: + specifier: ^2.0.0 + version: 2.0.0 +packages: + ext-a@1.0.0: + resolution: + integrity: sha512-ext-a== + ext-b@2.0.0: + resolution: + integrity: sha512-ext-b== +snapshots: + ext-a@1.0.0: + dependencies: + project-2: link:../../project-2 + ext-b@2.0.0: {} +`; + + const shrinkwrapFile = PnpmShrinkwrapFile.loadFromString(shrinkwrapContent, { + subspaceHasNoProjects: false + }); + + PnpmShrinkwrapFile.clearCache(); + + const proj1IntegrityMap = shrinkwrapFile.getIntegrityForImporter('../../project-1'); + + expect(proj1IntegrityMap).toBeDefined(); + // ext-a (direct external dep of project-1) + expect(proj1IntegrityMap!.has('ext-a@1.0.0')).toBe(true); + // project-2 (workspace project, reached via link: in ext-a's snapshot) + expect(proj1IntegrityMap!.has('../../project-2')).toBe(true); + // ext-b (external dep of project-2, reached transitively through the link:) + expect(proj1IntegrityMap!.has('ext-b@2.0.0')).toBe(true); + }); + }); + + describe('Check is workspace project modified', () => { + describe('pnpm lockfile major version 5', () => { + it('can detect not modified', async () => { + const project = getMockRushProject(); + const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile( + `${__dirname}/yamlFiles/pnpm-lock-v5/not-modified.yaml`, + project.rushConfiguration.defaultSubspace + ); + await expect( + pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync( + project, + project.rushConfiguration.defaultSubspace, + undefined + ) + ).resolves.toBe(false); + }); + + it('can detect modified', async () => { + const project = getMockRushProject(); + const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile( + `${__dirname}/yamlFiles/pnpm-lock-v5/modified.yaml`, + project.rushConfiguration.defaultSubspace + ); + await expect( + pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync( + project, + project.rushConfiguration.defaultSubspace, + undefined + ) + ).resolves.toBe(true); + }); + + it('can detect overrides', async () => { + const project = getMockRushProject(); + const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile( + `${__dirname}/yamlFiles/pnpm-lock-v5/overrides-not-modified.yaml`, + project.rushConfiguration.defaultSubspace + ); + await expect( + pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync( + project, + project.rushConfiguration.defaultSubspace, + undefined + ) + ).resolves.toBe(false); + }); + }); + + describe('pnpm lockfile major version 6', () => { + it('can detect not modified', async () => { + const project = getMockRushProject(); + const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile( + `${__dirname}/yamlFiles/pnpm-lock-v6/not-modified.yaml`, + project.rushConfiguration.defaultSubspace + ); + await expect( + pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync( + project, + project.rushConfiguration.defaultSubspace, + undefined + ) + ).resolves.toBe(false); + }); + + it('can detect modified', async () => { + const project = getMockRushProject(); + const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile( + `${__dirname}/yamlFiles/pnpm-lock-v6/modified.yaml`, + project.rushConfiguration.defaultSubspace + ); + await expect( + pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync( + project, + project.rushConfiguration.defaultSubspace, + undefined + ) + ).resolves.toBe(true); + }); + + it('can detect overrides', async () => { + const project = getMockRushProject(); + const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile( + `${__dirname}/yamlFiles/pnpm-lock-v6/overrides-not-modified.yaml`, + project.rushConfiguration.defaultSubspace + ); + await expect( + pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync( + project, + project.rushConfiguration.defaultSubspace, + undefined + ) + ).resolves.toBe(false); + }); + + it('can handle the inconsistent version of a package declared in dependencies and devDependencies', async () => { + const project = getMockRushProject2(); + const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile( + `${__dirname}/yamlFiles/pnpm-lock-v6/inconsistent-dep-devDep.yaml`, + project.rushConfiguration.defaultSubspace + ); + await expect( + pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync( + project, + project.rushConfiguration.defaultSubspace, + undefined + ) + ).resolves.toBe(false); + }); + }); + + describe('pnpm lockfile major version 9', () => { + it('can detect not modified', async () => { + const project = getMockRushProject(); + const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile( + `${__dirname}/yamlFiles/pnpm-lock-v9/not-modified.yaml`, + project.rushConfiguration.defaultSubspace + ); + await expect( + pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync( + project, + project.rushConfiguration.defaultSubspace, + undefined + ) + ).resolves.toBe(false); + }); + + it('can detect modified', async () => { + const project = getMockRushProject(); + const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile( + `${__dirname}/yamlFiles/pnpm-lock-v9/modified.yaml`, + project.rushConfiguration.defaultSubspace + ); + await expect( + pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync( + project, + project.rushConfiguration.defaultSubspace, + undefined + ) + ).resolves.toBe(true); + }); + + it('can detect overrides', async () => { + const project = getMockRushProject(); + const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile( + `${__dirname}/yamlFiles/pnpm-lock-v9/overrides-not-modified.yaml`, + project.rushConfiguration.defaultSubspace + ); + await expect( + pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync( + project, + project.rushConfiguration.defaultSubspace, + undefined + ) + ).resolves.toBe(false); + }); + + it('can handle the inconsistent version of a package declared in dependencies and devDependencies', async () => { + const project = getMockRushProject2(); + const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile( + `${__dirname}/yamlFiles/pnpm-lock-v9/inconsistent-dep-devDep.yaml`, + project.rushConfiguration.defaultSubspace + ); + await expect( + pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync( + project, + project.rushConfiguration.defaultSubspace, + undefined + ) + ).resolves.toBe(false); + }); + + it('can detect a devDependency that is still listed under dependencies in the importer', async () => { + // Regression: moving a dep from dependencies to devDependencies should be detected as modified. + const project = getMockRushProject(); + const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile( + `${__dirname}/yamlFiles/pnpm-lock-v9/stale-dev-in-dependencies.yaml`, + project.rushConfiguration.defaultSubspace + ); + await expect( + pnpmShrinkwrapFile.isWorkspaceProjectModifiedAsync( + project, + project.rushConfiguration.defaultSubspace, + undefined + ) + ).resolves.toBe(true); + }); + + it('sha1 integrity can be handled when disallowInsecureSha1', async () => { + const project = getMockRushProject(); + const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile( + `${__dirname}/yamlFiles/pnpm-lock-v9/sha1-integrity.yaml`, + project.rushConfiguration.defaultSubspace + ); + + const defaultSubspace = project.rushConfiguration.defaultSubspace; + + const mockPnpmOptions = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + `${__dirname}/jsonFiles/pnpm-config-disallow-sha1.json`, + defaultSubspace.getSubspaceTempFolderPath() + ); + + jest.spyOn(defaultSubspace, 'getPnpmOptions').mockReturnValue(mockPnpmOptions); + + const spyTerminalWrite = jest.fn(); + const terminal = new Terminal({ + eolCharacter: '\n', + supportsColor: false, + write: spyTerminalWrite + }); + + expect(() => + pnpmShrinkwrapFile.validateShrinkwrapAfterUpdate( + project.rushConfiguration, + project.rushConfiguration.defaultSubspace, + terminal + ) + ).not.toThrow(); + expect(spyTerminalWrite).not.toHaveBeenCalled(); + }); + + it('sha1 integrity can be handled when disallowInsecureSha1', async () => { + const project = getMockRushProject(); + const pnpmShrinkwrapFile = getPnpmShrinkwrapFileFromFile( + `${__dirname}/yamlFiles/pnpm-lock-v9/sha1-integrity-non-exempted-package.yaml`, + project.rushConfiguration.defaultSubspace + ); + + const defaultSubspace = project.rushConfiguration.defaultSubspace; + + const mockPnpmOptions = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + `${__dirname}/jsonFiles/pnpm-config-disallow-sha1.json`, + defaultSubspace.getSubspaceTempFolderPath() + ); + + jest.spyOn(defaultSubspace, 'getPnpmOptions').mockReturnValue(mockPnpmOptions); + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(); + const terminal = new Terminal(terminalProvider); + + expect(() => + pnpmShrinkwrapFile.validateShrinkwrapAfterUpdate( + project.rushConfiguration, + project.rushConfiguration.defaultSubspace, + terminal + ) + ).toThrow(AlreadyReportedError); + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); + }); + }); + }); }); + +function getPnpmShrinkwrapFileFromFile(filepath: string, subspace: Subspace): PnpmShrinkwrapFile { + const pnpmShrinkwrapFile = PnpmShrinkwrapFile.loadFromFile(filepath, { + subspaceHasNoProjects: subspace.getProjects().length === 0 + }); + if (!pnpmShrinkwrapFile) { + throw new Error(`Get PnpmShrinkwrapFileFromFile failed from ${filepath}`); + } + return pnpmShrinkwrapFile; +} + +function getMockRushProject(): RushConfigurationProject { + const rushFilename: string = `${__dirname}/repo/rush.json`; + const rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushFilename); + const project = rushConfiguration.projectsByName.get('foo'); + if (!project) { + throw new Error(`Can not get project "foo"`); + } + return project; +} + +function getMockRushProject2(): RushConfigurationProject { + const rushFilename: string = `${__dirname}/repo/rush2.json`; + const rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushFilename); + const project = rushConfiguration.projectsByName.get('bar'); + if (!project) { + throw new Error(`Can not get project "bar"`); + } + return project; +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/PnpmWorkspaceFile.test.ts b/libraries/rush-lib/src/logic/pnpm/test/PnpmWorkspaceFile.test.ts new file mode 100644 index 00000000000..d34b55191ea --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/PnpmWorkspaceFile.test.ts @@ -0,0 +1,557 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { FileSystem } from '@rushstack/node-core-library'; +import { PnpmWorkspaceFile } from '../PnpmWorkspaceFile'; + +describe(PnpmWorkspaceFile.name, () => { + const tempDir: string = `${__dirname}/temp`; + const workspaceFilePath: string = `${tempDir}/pnpm-workspace.yaml`; + const projectsDir: string = `${tempDir}/projects`; + + let writtenContent: string | undefined; + + beforeEach(() => { + writtenContent = undefined; + + // Mock FileSystem.writeFile to capture content instead of writing to disk + jest + .spyOn(FileSystem, 'writeFileAsync') + .mockImplementation(async (filePath: string, contents: string | Buffer) => { + writtenContent = String(contents); + }); + }); + + describe('basic functionality', () => { + it('generates workspace file with packages only', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app2`); + workspaceFile.addPackage(`${projectsDir}/app1`); + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toMatchSnapshot(); + }); + + it('escapes special characters in package paths', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/[app-with-brackets]`); + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toContain('\\[app-with-brackets\\]'); + }); + }); + + describe('catalog functionality', () => { + it('generates workspace file with default catalog only', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.catalogs = { + default: { + react: '^18.0.0', + 'react-dom': '^18.0.0', + typescript: '~5.3.0' + } + }; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toMatchSnapshot(); + }); + + it('generates workspace file with named catalogs', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.catalogs = { + default: { + typescript: '~5.3.0' + }, + frontend: { + vue: '^3.4.0', + 'vue-router': '^4.2.0' + }, + backend: { + express: '^4.18.0', + fastify: '^4.26.0' + } + }; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toMatchSnapshot(); + }); + + it('handles empty catalog object', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.catalogs = {}; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toMatchSnapshot(); + }); + + it('handles undefined catalog', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.catalogs = undefined; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toMatchSnapshot(); + }); + + it('handles scoped packages in catalogs', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.catalogs = { + default: { + '@types/node': '~22.9.4', + '@types/cookies': '^0.7.7', + '@rushstack/node-core-library': '~5.0.0' + } + }; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toMatchSnapshot(); + }); + + it('can update catalogs after initial creation', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.catalogs = { + default: { + react: '^18.0.0' + } + }; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + // Update catalogs + workspaceFile.catalogs = { + default: { + react: '^18.2.0', + 'react-dom': '^18.2.0' + } + }; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toMatchSnapshot(); + }); + }); + + describe('allowBuilds functionality', () => { + it('generates workspace file with allowBuilds', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.allowBuilds = { + esbuild: true, + '@parcel/watcher': true, + fsevents: false + }; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toMatchSnapshot(); + }); + + it('generates workspace file with allowBuilds and catalogs', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.catalogs = { + default: { + react: '^18.0.0' + } + }; + + workspaceFile.allowBuilds = { + esbuild: true + }; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toMatchSnapshot(); + }); + + it('handles empty allowBuilds object', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.allowBuilds = {}; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toContain('allowBuilds: {}'); + }); + + it('handles undefined allowBuilds', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.allowBuilds = undefined; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).not.toContain('allowBuilds'); + }); + }); + + describe('overrides functionality', () => { + it('generates workspace file with overrides', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.overrides = { + 'foo@1.0.0': '1.0.1', + bar: '^2.0.0' + }; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toContain('overrides:'); + expect(writtenContent).toMatchSnapshot(); + }); + + it('handles undefined overrides', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.overrides = undefined; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).not.toContain('overrides'); + }); + }); + + describe('packageExtensions functionality', () => { + it('generates workspace file with packageExtensions', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.packageExtensions = { + 'react@*': { + dependencies: { + foo: '1.0.0' + } + } + }; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toContain('packageExtensions:'); + expect(writtenContent).toMatchSnapshot(); + }); + + it('handles undefined packageExtensions', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.packageExtensions = undefined; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).not.toContain('packageExtensions'); + }); + }); + + describe('peerDependencyRules functionality', () => { + it('generates workspace file with peerDependencyRules', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.peerDependencyRules = { + ignoreMissing: ['baz'], + allowedVersions: { + react: '18' + } + }; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toContain('peerDependencyRules:'); + expect(writtenContent).toMatchSnapshot(); + }); + + it('handles undefined peerDependencyRules', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.peerDependencyRules = undefined; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).not.toContain('peerDependencyRules'); + }); + }); + + describe('allowedDeprecatedVersions functionality', () => { + it('generates workspace file with allowedDeprecatedVersions', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.allowedDeprecatedVersions = { + querystring: '*' + }; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toContain('allowedDeprecatedVersions:'); + expect(writtenContent).toMatchSnapshot(); + }); + + it('handles undefined allowedDeprecatedVersions', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.allowedDeprecatedVersions = undefined; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).not.toContain('allowedDeprecatedVersions'); + }); + }); + + describe('patchedDependencies functionality', () => { + it('generates workspace file with patchedDependencies', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.patchedDependencies = { + 'lodash@4.17.21': 'patches/lodash@4.17.21.patch' + }; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toContain('patchedDependencies:'); + expect(writtenContent).toMatchSnapshot(); + }); + + it('handles undefined patchedDependencies', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.patchedDependencies = undefined; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).not.toContain('patchedDependencies'); + }); + }); + + describe(PnpmWorkspaceFile.tryLoadAsync.name, () => { + let mockReadFileAsync: jest.SpyInstance; + + describe('file exists', () => { + beforeEach(() => { + // Mock FileSystem.readFileAsync to return the content captured by the FileSystem.writeFile mock + mockReadFileAsync = jest.spyOn(FileSystem, 'readFileAsync').mockImplementation(async () => { + if (writtenContent === undefined) { + throw new Error('File not found'); + } + + return writtenContent; + }); + }); + + afterEach(() => { + mockReadFileAsync.mockRestore(); + }); + + it('reads patchedDependencies from an existing workspace file', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + workspaceFile.patchedDependencies = { + 'lodash@4.17.21': 'patches/lodash@4.17.21.patch' + }; + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + const loadedWorkspaceFile: PnpmWorkspaceFile | undefined = + await PnpmWorkspaceFile.tryLoadAsync(workspaceFilePath); + expect(loadedWorkspaceFile?.patchedDependencies).toEqual({ + 'lodash@4.17.21': 'patches/lodash@4.17.21.patch' + }); + }); + + it('reads globalPnpmfile from an existing workspace file', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + workspaceFile.globalPnpmfile = '/repo/common/temp/my-subspace/global-pnpmfile.cjs'; + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + const loadedWorkspaceFile: PnpmWorkspaceFile | undefined = + await PnpmWorkspaceFile.tryLoadAsync(workspaceFilePath); + expect(loadedWorkspaceFile?.globalPnpmfile).toEqual( + '/repo/common/temp/my-subspace/global-pnpmfile.cjs' + ); + }); + + it('returns undefined when the workspace file has no patchedDependencies', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + const loadedWorkspaceFile: PnpmWorkspaceFile | undefined = + await PnpmWorkspaceFile.tryLoadAsync(workspaceFilePath); + expect(loadedWorkspaceFile!.patchedDependencies).toBeUndefined(); + }); + }); + + it("handles the case when the file doesn't exist", async () => { + const loadedWorkspaceFile: PnpmWorkspaceFile | undefined = await PnpmWorkspaceFile.tryLoadAsync( + `${__dirname}/file-that-does-not-exist.yaml` + ); + expect(loadedWorkspaceFile).toBeUndefined(); + }); + }); + + describe('combined pnpm 11 settings', () => { + it('generates workspace file with all relocated settings together', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.catalogs = { + default: { + react: '^18.0.0' + } + }; + workspaceFile.allowBuilds = { + esbuild: true + }; + workspaceFile.overrides = { + 'foo@1.0.0': '1.0.1' + }; + workspaceFile.packageExtensions = { + 'react@*': { + dependencies: { + foo: '1.0.0' + } + } + }; + workspaceFile.peerDependencyRules = { + allowedVersions: { + react: '18' + } + }; + workspaceFile.allowedDeprecatedVersions = { + querystring: '*' + }; + workspaceFile.patchedDependencies = { + 'lodash@4.17.21': 'patches/lodash@4.17.21.patch' + }; + workspaceFile.ignoredOptionalDependencies = ['fsevents']; + workspaceFile.trustPolicy = 'no-downgrade'; + workspaceFile.trustPolicyExclude = ['chokidar@4.0.3']; + workspaceFile.trustPolicyIgnoreAfter = 1440; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toMatchSnapshot(); + }); + }); + + describe('globalPnpmfile functionality', () => { + it('generates workspace file with globalPnpmfile', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.globalPnpmfile = '/repo/common/temp/my-subspace/global-pnpmfile.cjs'; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toMatchSnapshot(); + }); + }); + + describe('minimumReleaseAge functionality', () => { + it('generates workspace file with minimumReleaseAge', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.minimumReleaseAge = 20160; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toMatchSnapshot(); + }); + + it('generates workspace file with minimumReleaseAge and minimumReleaseAgeExclude', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.minimumReleaseAge = 1440; + workspaceFile.minimumReleaseAgeExclude = ['webpack', '@myorg/*']; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toMatchSnapshot(); + }); + + it('generates workspace file with minimumReleaseAgeExclude only', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.minimumReleaseAgeExclude = ['webpack']; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toMatchSnapshot(); + }); + + it('handles zero value for minimumReleaseAge', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.minimumReleaseAge = 0; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toContain('minimumReleaseAge: 0'); + }); + + it('handles undefined minimumReleaseAge', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.minimumReleaseAge = undefined; + workspaceFile.minimumReleaseAgeExclude = undefined; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).not.toContain('minimumReleaseAge'); + }); + + it('passes through an explicitly-set empty minimumReleaseAgeExclude', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.minimumReleaseAgeExclude = []; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).toContain('minimumReleaseAgeExclude: []'); + }); + + it('omits an undefined minimumReleaseAgeExclude', async () => { + const workspaceFile: PnpmWorkspaceFile = new PnpmWorkspaceFile(workspaceFilePath); + workspaceFile.addPackage(`${projectsDir}/app1`); + + workspaceFile.minimumReleaseAgeExclude = undefined; + + await workspaceFile.saveAsync(workspaceFilePath, { onlyIfChanged: true }); + + expect(writtenContent).not.toContain('minimumReleaseAgeExclude'); + }); + }); +}); diff --git a/libraries/rush-lib/src/logic/pnpm/test/PnpmfileConfiguration.test.ts b/libraries/rush-lib/src/logic/pnpm/test/PnpmfileConfiguration.test.ts new file mode 100644 index 00000000000..23dafd9caf1 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/PnpmfileConfiguration.test.ts @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { RushConfiguration } from '../../../api/RushConfiguration'; +import { PnpmfileConfiguration } from '../PnpmfileConfiguration'; +import { JsonFile, type JsonObject } from '@rushstack/node-core-library'; + +describe(PnpmfileConfiguration.name, () => { + const repoPath: string = `${__dirname}/repo`; + const rushFilename: string = `${repoPath}/rush3.json`; + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile(rushFilename); + const shimPath: string = `${rushConfiguration.defaultSubspace.getSubspaceTempFolderPath()}/pnpmfileSettings.json`; + + beforeAll(async () => { + const subspace = rushConfiguration.defaultSubspace; + await PnpmfileConfiguration.writeCommonTempPnpmfileShimAsync( + rushConfiguration, + subspace.getSubspaceTempFolderPath(), + subspace, + undefined + ); + }); + + it('should use the smallest-available SemVer range (preferredVersions)', async () => { + const shimJson: JsonObject = await JsonFile.loadAsync(shimPath); + expect(shimJson.allPreferredVersions).toHaveProperty('core-js', '3.6.5'); + }); + + it('should use the smallest-available SemVer range (per-project)', async () => { + const shimJson: JsonObject = await JsonFile.loadAsync(shimPath); + expect(shimJson.allPreferredVersions).toHaveProperty('delay', '5.0.0'); + }); + + it('should override preferredVersions when per-project versions conflict', async () => { + const shimJson: JsonObject = await JsonFile.loadAsync(shimPath); + expect(shimJson.allPreferredVersions).toHaveProperty('find-up', '5.0.0'); + }); +}); diff --git a/libraries/rush-lib/src/logic/pnpm/test/__snapshots__/PnpmShrinkwrapFile.test.ts.snap b/libraries/rush-lib/src/logic/pnpm/test/__snapshots__/PnpmShrinkwrapFile.test.ts.snap new file mode 100644 index 00000000000..3b9a0417c3b --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/__snapshots__/PnpmShrinkwrapFile.test.ts.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`PnpmShrinkwrapFile Check is workspace project modified pnpm lockfile major version 9 sha1 integrity can be handled when disallowInsecureSha1 1`] = ` +Array [ + "[ error] Error: An integrity field with \\"sha1\\" was detected in the pnpm-lock.yaml file located in subspace default; this conflicts with the \\"disallowInsecureSha1\\" policy from pnpm-config.json.[n]", + "[ error] [n]", +] +`; diff --git a/libraries/rush-lib/src/logic/pnpm/test/__snapshots__/PnpmWorkspaceFile.test.ts.snap b/libraries/rush-lib/src/logic/pnpm/test/__snapshots__/PnpmWorkspaceFile.test.ts.snap new file mode 100644 index 00000000000..b0eff80d3aa --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/__snapshots__/PnpmWorkspaceFile.test.ts.snap @@ -0,0 +1,197 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`PnpmWorkspaceFile allowBuilds functionality generates workspace file with allowBuilds 1`] = ` +"allowBuilds: + '@parcel/watcher': true + esbuild: true + fsevents: false +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile allowBuilds functionality generates workspace file with allowBuilds and catalogs 1`] = ` +"allowBuilds: + esbuild: true +catalogs: + default: + react: ^18.0.0 +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile allowedDeprecatedVersions functionality generates workspace file with allowedDeprecatedVersions 1`] = ` +"allowedDeprecatedVersions: + querystring: '*' +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile basic functionality generates workspace file with packages only 1`] = ` +"packages: + - projects/app1 + - projects/app2 +" +`; + +exports[`PnpmWorkspaceFile catalog functionality can update catalogs after initial creation 1`] = ` +"catalogs: + default: + react: ^18.2.0 + react-dom: ^18.2.0 +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile catalog functionality generates workspace file with default catalog only 1`] = ` +"catalogs: + default: + react: ^18.0.0 + react-dom: ^18.0.0 + typescript: ~5.3.0 +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile catalog functionality generates workspace file with named catalogs 1`] = ` +"catalogs: + backend: + express: ^4.18.0 + fastify: ^4.26.0 + default: + typescript: ~5.3.0 + frontend: + vue: ^3.4.0 + vue-router: ^4.2.0 +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile catalog functionality handles empty catalog object 1`] = ` +"catalogs: {} +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile catalog functionality handles scoped packages in catalogs 1`] = ` +"catalogs: + default: + '@rushstack/node-core-library': ~5.0.0 + '@types/cookies': ^0.7.7 + '@types/node': ~22.9.4 +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile catalog functionality handles undefined catalog 1`] = ` +"packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile combined pnpm 11 settings generates workspace file with all relocated settings together 1`] = ` +"allowBuilds: + esbuild: true +allowedDeprecatedVersions: + querystring: '*' +catalogs: + default: + react: ^18.0.0 +ignoredOptionalDependencies: + - fsevents +overrides: + foo@1.0.0: 1.0.1 +packageExtensions: + react@*: + dependencies: + foo: 1.0.0 +packages: + - projects/app1 +patchedDependencies: + lodash@4.17.21: patches/lodash@4.17.21.patch +peerDependencyRules: + allowedVersions: + react: '18' +trustPolicy: no-downgrade +trustPolicyExclude: + - chokidar@4.0.3 +trustPolicyIgnoreAfter: 1440 +" +`; + +exports[`PnpmWorkspaceFile globalPnpmfile functionality generates workspace file with globalPnpmfile 1`] = ` +"globalPnpmfile: /repo/common/temp/my-subspace/global-pnpmfile.cjs +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile minimumReleaseAge functionality generates workspace file with minimumReleaseAge 1`] = ` +"minimumReleaseAge: 20160 +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile minimumReleaseAge functionality generates workspace file with minimumReleaseAge and minimumReleaseAgeExclude 1`] = ` +"minimumReleaseAge: 1440 +minimumReleaseAgeExclude: + - webpack + - '@myorg/*' +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile minimumReleaseAge functionality generates workspace file with minimumReleaseAgeExclude only 1`] = ` +"minimumReleaseAgeExclude: + - webpack +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile overrides functionality generates workspace file with overrides 1`] = ` +"overrides: + bar: ^2.0.0 + foo@1.0.0: 1.0.1 +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile packageExtensions functionality generates workspace file with packageExtensions 1`] = ` +"packageExtensions: + react@*: + dependencies: + foo: 1.0.0 +packages: + - projects/app1 +" +`; + +exports[`PnpmWorkspaceFile patchedDependencies functionality generates workspace file with patchedDependencies 1`] = ` +"packages: + - projects/app1 +patchedDependencies: + lodash@4.17.21: patches/lodash@4.17.21.patch +" +`; + +exports[`PnpmWorkspaceFile peerDependencyRules functionality generates workspace file with peerDependencyRules 1`] = ` +"packages: + - projects/app1 +peerDependencyRules: + allowedVersions: + react: '18' + ignoreMissing: + - baz +" +`; diff --git a/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-allowBuilds.json b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-allowBuilds.json new file mode 100644 index 00000000000..8a99dc3b3ee --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-allowBuilds.json @@ -0,0 +1,7 @@ +{ + "globalAllowBuilds": { + "esbuild": true, + "@parcel/watcher": true, + "fsevents": false + } +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-catalog.json b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-catalog.json new file mode 100644 index 00000000000..5e0189115bd --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-catalog.json @@ -0,0 +1,17 @@ +{ + "globalCatalogs": { + "default": { + "react": "^18.0.0", + "react-dom": "^18.0.0", + "typescript": "~5.3.0" + }, + "frontend": { + "vue": "^3.4.0", + "vue-router": "^4.2.0" + }, + "backend": { + "express": "^4.18.0", + "fastify": "^4.26.0" + } + } +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-disallow-sha1.json b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-disallow-sha1.json new file mode 100644 index 00000000000..a07b8d4cab9 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-disallow-sha1.json @@ -0,0 +1,14 @@ +{ + "pnpmLockfilePolicies": { + "disallowInsecureSha1": { + "enabled": true, + "exemptPackageVersions": { + "@some/sha1-pkg": ["1.4.3"], + "other-sha1-pkg": ["2.0.0"], + "fake-with-patch": ["1.0.0"], + "fake-with-peer": ["1.0.0"], + "fake": ["7.8.1"] + } + } + } +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-minimumReleaseAge-both.json b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-minimumReleaseAge-both.json new file mode 100644 index 00000000000..130174aff56 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-minimumReleaseAge-both.json @@ -0,0 +1,4 @@ +{ + "minimumReleaseAge": 720, + "minimumReleaseAgeMinutes": 1440 +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-minimumReleaseAge-deprecated.json b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-minimumReleaseAge-deprecated.json new file mode 100644 index 00000000000..f7f02838388 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-minimumReleaseAge-deprecated.json @@ -0,0 +1,3 @@ +{ + "minimumReleaseAge": 720 +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-minimumReleaseAge.json b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-minimumReleaseAge.json new file mode 100644 index 00000000000..a0c8447ed24 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-minimumReleaseAge.json @@ -0,0 +1,4 @@ +{ + "minimumReleaseAgeMinutes": 1440, + "minimumReleaseAgeExclude": ["webpack", "@myorg/*"] +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-onlyBuiltDependencies.json b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-onlyBuiltDependencies.json new file mode 100644 index 00000000000..46b45a3dd41 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-onlyBuiltDependencies.json @@ -0,0 +1,3 @@ +{ + "globalOnlyBuiltDependencies": ["esbuild", "playwright", "@swc/core"] +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-trustPolicy.json b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-trustPolicy.json new file mode 100644 index 00000000000..443644be3fa --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-trustPolicy.json @@ -0,0 +1,5 @@ +{ + "trustPolicy": "no-downgrade", + "trustPolicyExclude": ["@myorg/*", "chokidar@4.0.3", "webpack@4.47.0 || 5.102.1", "@babel/core@7.28.5"], + "trustPolicyIgnoreAfterMinutes": 20160 +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/repo/apps/bar/package.json b/libraries/rush-lib/src/logic/pnpm/test/repo/apps/bar/package.json new file mode 100644 index 00000000000..0aa90dc79fe --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/repo/apps/bar/package.json @@ -0,0 +1,10 @@ +{ + "name": "bar", + "version": "1.0.0", + "dependencies": { + "prettier": "~2.3.0" + }, + "devDependencies": { + "prettier": "~2.7.1" + } +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/repo/apps/baz/package.json b/libraries/rush-lib/src/logic/pnpm/test/repo/apps/baz/package.json new file mode 100644 index 00000000000..524e9d4cb89 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/repo/apps/baz/package.json @@ -0,0 +1,9 @@ +{ + "name": "baz", + "version": "1.0.0", + "dependencies": { + "core-js": "^3.0.0", + "delay": "5.0.0", + "find-up": "*" + } +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/repo/apps/foo/package.json b/libraries/rush-lib/src/logic/pnpm/test/repo/apps/foo/package.json new file mode 100644 index 00000000000..7f2663a0bd7 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/repo/apps/foo/package.json @@ -0,0 +1,10 @@ +{ + "name": "foo", + "version": "1.0.0", + "dependencies": { + "tslib": "~2.3.1" + }, + "devDependencies": { + "typescript": "~5.0.4" + } +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/repo/common/config/rush/common-versions.json b/libraries/rush-lib/src/logic/pnpm/test/repo/common/config/rush/common-versions.json new file mode 100644 index 00000000000..30d53549184 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/repo/common/config/rush/common-versions.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/common-versions.schema.json", + "preferredVersions": { + "core-js": "3.6.5", + "delay": "4.0.0", + "find-up": "5.0.0" + } +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/repo/rush.json b/libraries/rush-lib/src/logic/pnpm/test/repo/rush.json new file mode 100644 index 00000000000..406da20f28e --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/repo/rush.json @@ -0,0 +1,13 @@ +{ + "pnpmVersion": "7.0.0", + "rushVersion": "5.46.1", + "projectFolderMinDepth": 1, + "projectFolderMaxDepth": 99, + + "projects": [ + { + "packageName": "foo", + "projectFolder": "apps/foo" + } + ] +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/repo/rush2.json b/libraries/rush-lib/src/logic/pnpm/test/repo/rush2.json new file mode 100644 index 00000000000..4a7bc3d2854 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/repo/rush2.json @@ -0,0 +1,13 @@ +{ + "pnpmVersion": "7.0.0", + "rushVersion": "5.46.1", + "projectFolderMinDepth": 1, + "projectFolderMaxDepth": 99, + + "projects": [ + { + "packageName": "bar", + "projectFolder": "apps/bar" + } + ] +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/repo/rush3.json b/libraries/rush-lib/src/logic/pnpm/test/repo/rush3.json new file mode 100644 index 00000000000..d5b3a4e1d82 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/repo/rush3.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush.schema.json", + "pnpmVersion": "7.0.0", + "rushVersion": "5.46.1", + "projects": [ + { + "packageName": "baz", + "projectFolder": "apps/baz" + } + ], + "pnpmOptions": { + "useWorkspaces": true + } +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v5/modified.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v5/modified.yaml new file mode 100644 index 00000000000..42479b27eec --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v5/modified.yaml @@ -0,0 +1,29 @@ +lockfileVersion: 5.3 + +importers: + .: + specifiers: {} + + ../../apps/foo: + specifiers: + tslib: ~2.0.0 + typescript: ~5.0.4 + dependencies: + tslib: 2.3.1 + devDependencies: + typescript: 5.0.4 + +packages: + /typescript/5.0.4: + resolution: + { + integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + } + engines: { node: '>=12.20' } + hasBin: true + + /tslib/2.3.1: + resolution: + { + integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + } diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v5/not-modified.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v5/not-modified.yaml new file mode 100644 index 00000000000..52785c178a2 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v5/not-modified.yaml @@ -0,0 +1,29 @@ +lockfileVersion: 5.3 + +importers: + .: + specifiers: {} + + ../../apps/foo: + specifiers: + tslib: ~2.3.1 + typescript: ~5.0.4 + dependencies: + tslib: 2.3.1 + devDependencies: + typescript: 5.0.4 + +packages: + /typescript/5.0.4: + resolution: + { + integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + } + engines: { node: '>=12.20' } + hasBin: true + + /tslib/2.3.1: + resolution: + { + integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + } diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v5/overrides-not-modified.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v5/overrides-not-modified.yaml new file mode 100644 index 00000000000..7df12535b6b --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v5/overrides-not-modified.yaml @@ -0,0 +1,32 @@ +lockfileVersion: 5.3 + +overrides: + typescript: 5.0.4 + +importers: + .: + specifiers: {} + + ../../apps/foo: + specifiers: + tslib: ~2.3.1 + typescript: 5.0.4 + dependencies: + tslib: 2.3.1 + devDependencies: + typescript: 5.0.4 + +packages: + /typescript/5.0.4: + resolution: + { + integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + } + engines: { node: '>=12.20' } + hasBin: true + + /tslib/2.3.1: + resolution: + { + integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + } diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v6/inconsistent-dep-devDep.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v6/inconsistent-dep-devDep.yaml new file mode 100644 index 00000000000..87174fcac83 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v6/inconsistent-dep-devDep.yaml @@ -0,0 +1,20 @@ +lockfileVersion: '6.0' + +importers: + .: {} + + ../../apps/bar: + dependencies: + prettier: + specifier: ~2.3.0 + version: 2.3.0 + devDependencies: + +packages: + /prettier/2.3.0: + resolution: + { + integrity: sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== + } + engines: { node: '>=10.13.0' } + hasBin: true diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v6/modified.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v6/modified.yaml new file mode 100644 index 00000000000..e6c3cb335f9 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v6/modified.yaml @@ -0,0 +1,29 @@ +lockfileVersion: '6.0' + +importers: + .: {} + + ../../apps/foo: + dependencies: + tslib: + specifier: ~2.0.0 + version: 2.3.1 + devDependencies: + typescript: + specifier: ~5.0.4 + version: 5.0.4 + +packages: + /typescript/5.0.4: + resolution: + { + integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + } + engines: { node: '>=12.20' } + hasBin: true + + /tslib/2.3.1: + resolution: + { + integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + } diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v6/not-modified.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v6/not-modified.yaml new file mode 100644 index 00000000000..5dc4730f9e9 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v6/not-modified.yaml @@ -0,0 +1,29 @@ +lockfileVersion: '6.0' + +importers: + .: {} + + ../../apps/foo: + dependencies: + tslib: + specifier: ~2.3.1 + version: 2.3.1 + devDependencies: + typescript: + specifier: ~5.0.4 + version: 5.0.4 + +packages: + /typescript/5.0.4: + resolution: + { + integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + } + engines: { node: '>=12.20' } + hasBin: true + + /tslib/2.3.1: + resolution: + { + integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + } diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v6/overrides-not-modified.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v6/overrides-not-modified.yaml new file mode 100644 index 00000000000..67715316f32 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v6/overrides-not-modified.yaml @@ -0,0 +1,32 @@ +lockfileVersion: '6.0' + +overrides: + typescript: 5.0.4 + +importers: + .: {} + + ../../apps/foo: + dependencies: + tslib: + specifier: ~2.3.1 + version: 2.3.1 + devDependencies: + typescript: + specifier: 5.0.4 + version: 5.0.4 + +packages: + /typescript/5.0.4: + resolution: + { + integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + } + engines: { node: '>=12.20' } + hasBin: true + + /tslib/2.3.1: + resolution: + { + integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + } diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/inconsistent-dep-devDep.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/inconsistent-dep-devDep.yaml new file mode 100644 index 00000000000..08f6420eaf5 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/inconsistent-dep-devDep.yaml @@ -0,0 +1,26 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: {} + + ../../apps/bar: + dependencies: + prettier: + specifier: ~2.3.0 + version: 2.3.2 + +packages: + prettier@2.3.2: + resolution: + { + integrity: sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== + } + engines: { node: '>=10.13.0' } + hasBin: true + +snapshots: + prettier@2.3.2: {} diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/modified.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/modified.yaml new file mode 100644 index 00000000000..7f156d05a28 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/modified.yaml @@ -0,0 +1,38 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: {} + + ../../apps/foo: + dependencies: + tslib: + specifier: ~2.3.0 + version: 2.3.1 + devDependencies: + typescript: + specifier: ~5.0.4 + version: 5.0.4 + +packages: + tslib@2.3.1: + resolution: + { + integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + } + + typescript@5.0.4: + resolution: + { + integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + } + engines: { node: '>=12.20' } + hasBin: true + +snapshots: + tslib@2.3.1: {} + + typescript@5.0.4: {} diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/not-modified.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/not-modified.yaml new file mode 100644 index 00000000000..f17060bc2eb --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/not-modified.yaml @@ -0,0 +1,38 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: {} + + ../../apps/foo: + dependencies: + tslib: + specifier: ~2.3.1 + version: 2.3.1 + devDependencies: + typescript: + specifier: ~5.0.4 + version: 5.0.4 + +packages: + tslib@2.3.1: + resolution: + { + integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + } + + typescript@5.0.4: + resolution: + { + integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + } + engines: { node: '>=12.20' } + hasBin: true + +snapshots: + tslib@2.3.1: {} + + typescript@5.0.4: {} diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/overrides-not-modified.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/overrides-not-modified.yaml new file mode 100644 index 00000000000..69e66401e1e --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/overrides-not-modified.yaml @@ -0,0 +1,41 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + typescript: 5.0.4 + +importers: + .: {} + + ../../apps/foo: + dependencies: + tslib: + specifier: ~2.3.1 + version: 2.3.1 + devDependencies: + typescript: + specifier: 5.0.4 + version: 5.0.4 + +packages: + tslib@2.3.1: + resolution: + { + integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + } + + typescript@5.0.4: + resolution: + { + integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + } + engines: { node: '>=12.20' } + hasBin: true + +snapshots: + tslib@2.3.1: {} + + typescript@5.0.4: {} diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/pnpm-lock-v9.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/pnpm-lock-v9.yaml new file mode 100644 index 00000000000..91130e28c33 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/pnpm-lock-v9.yaml @@ -0,0 +1,45 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: + dependencies: + jquery: + specifier: ^3.7.1 + version: 3.7.1 + pad-left: + specifier: ^2.1.0 + version: 2.1.0 + +packages: + jquery@3.7.1: + resolution: + { + integrity: sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== + } + + pad-left@2.1.0: + resolution: + { + integrity: sha512-HJxs9K9AztdIQIAIa/OIazRAUW/L6B9hbQDxO4X07roW3eo9XqZc2ur9bn1StH9CnbbI9EgvejHQX7CBpCF1QA== + } + engines: { node: '>=0.10.0' } + + repeat-string@1.6.1: + resolution: + { + integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + } + engines: { node: '>=0.10' } + +snapshots: + jquery@3.7.1: {} + + pad-left@2.1.0: + dependencies: + repeat-string: 1.6.1 + + repeat-string@1.6.1: {} diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/sha1-integrity-non-exempted-package.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/sha1-integrity-non-exempted-package.yaml new file mode 100644 index 00000000000..30a5c5526eb --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/sha1-integrity-non-exempted-package.yaml @@ -0,0 +1,66 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +patchedDependencies: + fake-with-patch@1.0.0: + hash: tta6uuavpnftppsegagihriayy + path: patches/fake-with-patch@1.0.0.patch + +importers: + .: + dependencies: + '@some/sha1-pkg': + specifier: ^1.0.0 + version: 1.4.3 + other-sha1-pkg: + specifier: ~2.0.0 + version: 2.0.0 + fake-with-patch: + specifier: 1.0.0 + version: 1.0.0 + fake-with-peer: + specifier: 1.0.0 + version: 1.0.0 + fake-with-npm: + specifier: npm:fake@7.8.1 + version: fake@7.8.1 + fake-non-exempted: + specifier: 1.0.0 + version: 1.0.0 + +packages: + '@some/sha1-pkg@1.4.3': + resolution: { integrity: sha1-KQzv7h3EqVCA2u7BOFut5Vqbl5o= } + + other-sha1-pkg@2.0.0: + resolution: { integrity: sha1-+5v5y5gkJ6x2+X1K3pZ5p8W7m4o= } + + fake-with-patch@1.0.0: + resolution: { integrity: sha1-FAKEPATCHINTEGRITY1234567890= } + + fake-with-peer@1.0.0: + resolution: { integrity: sha1-FAKEPEERINTEGRITY0987654321= } + + fake@7.8.1: + resolution: { integrity: sha1-FAKEPEERINTEGRITY0987654321= } + + fake-non-exempted@1.0.0: + resolution: { integrity: sha1-FAKEPEERINTEGRITY0987654321= } + +snapshots: + '@some/sha1-pkg@1.4.3': {} + + other-sha1-pkg@2.0.0: {} + + fake-with-patch@1.0.0(patch_hash=tta6uuavpnftppsegagihriayy): {} + + fake-with-peer@1.0.0(@some/sha1-pkg@1.4.3): + transitivePeerDependencies: + - '@some/sha1-pkg' + + fake@7.8.1: {} + + fake-non-exempted@1.0.0: {} diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/sha1-integrity.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/sha1-integrity.yaml new file mode 100644 index 00000000000..b2dd4e49386 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/sha1-integrity.yaml @@ -0,0 +1,58 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +patchedDependencies: + fake-with-patch@1.0.0: + hash: tta6uuavpnftppsegagihriayy + path: patches/fake-with-patch@1.0.0.patch + +importers: + .: + dependencies: + '@some/sha1-pkg': + specifier: ^1.0.0 + version: 1.4.3 + other-sha1-pkg: + specifier: ~2.0.0 + version: 2.0.0 + fake-with-patch: + specifier: 1.0.0 + version: 1.0.0 + fake-with-peer: + specifier: 1.0.0 + version: 1.0.0 + fake-with-npm: + specifier: npm:fake@7.8.1 + version: fake@7.8.1 + +packages: + '@some/sha1-pkg@1.4.3': + resolution: { integrity: sha1-KQzv7h3EqVCA2u7BOFut5Vqbl5o= } + + other-sha1-pkg@2.0.0: + resolution: { integrity: sha1-+5v5y5gkJ6x2+X1K3pZ5p8W7m4o= } + + fake-with-patch@1.0.0: + resolution: { integrity: sha1-FAKEPATCHINTEGRITY1234567890= } + + fake-with-peer@1.0.0: + resolution: { integrity: sha1-FAKEPEERINTEGRITY0987654321= } + + fake@7.8.1: + resolution: { integrity: sha1-FAKEPEERINTEGRITY0987654321= } + +snapshots: + '@some/sha1-pkg@1.4.3': {} + + other-sha1-pkg@2.0.0: {} + + fake-with-patch@1.0.0(patch_hash=tta6uuavpnftppsegagihriayy): {} + + fake-with-peer@1.0.0(@some/sha1-pkg@1.4.3): + transitivePeerDependencies: + - '@some/sha1-pkg' + + fake@7.8.1: {} diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/stale-dev-in-dependencies.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/stale-dev-in-dependencies.yaml new file mode 100644 index 00000000000..f299a150078 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-v9/stale-dev-in-dependencies.yaml @@ -0,0 +1,37 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: {} + + ../../apps/foo: + dependencies: + tslib: + specifier: ~2.3.1 + version: 2.3.1 + typescript: + specifier: ~5.0.4 + version: 5.0.4 + +packages: + tslib@2.3.1: + resolution: + { + integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + } + + typescript@5.0.4: + resolution: + { + integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + } + engines: { node: '>=12.20' } + hasBin: true + +snapshots: + tslib@2.3.1: {} + + typescript@5.0.4: {} diff --git a/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-with-catalog.yaml b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-with-catalog.yaml new file mode 100644 index 00000000000..143be44f5b0 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/yamlFiles/pnpm-lock-with-catalog.yaml @@ -0,0 +1,34 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +catalogs: + default: + react: 18.2.0 + typescript: 5.3.0 + frontend: + vue: 3.4.0 + +importers: + .: + dependencies: + react: + specifier: 'catalog:default' + version: 18.2.0 + typescript: + specifier: 'catalog:default' + version: 5.3.0 + +packages: + react@18.2.0: + resolution: { integrity: sha512-abc123 } + + typescript@5.3.0: + resolution: { integrity: sha512-def456 } + +snapshots: + react@18.2.0: {} + + typescript@5.3.0: {} diff --git a/libraries/rush-lib/src/logic/policy/EnvironmentPolicy.ts b/libraries/rush-lib/src/logic/policy/EnvironmentPolicy.ts index 8990ac2855a..3ae578f77f9 100644 --- a/libraries/rush-lib/src/logic/policy/EnvironmentPolicy.ts +++ b/libraries/rush-lib/src/logic/policy/EnvironmentPolicy.ts @@ -38,6 +38,7 @@ export async function validateAsync( errorMessage += ` To ignore, use the "${RushConstants.bypassPolicyFlagLongName}" flag.`; } + // eslint-disable-next-line no-console console.error(errorMessage); throw new AlreadyReportedError(); } diff --git a/libraries/rush-lib/src/logic/policy/GitEmailPolicy.ts b/libraries/rush-lib/src/logic/policy/GitEmailPolicy.ts index 66c91c443db..455f9b202dc 100644 --- a/libraries/rush-lib/src/logic/policy/GitEmailPolicy.ts +++ b/libraries/rush-lib/src/logic/policy/GitEmailPolicy.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; import { AlreadyReportedError } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; import type { RushConfiguration } from '../../api/RushConfiguration'; import { Utilities } from '../../utilities/Utilities'; @@ -10,14 +10,18 @@ import { Git } from '../Git'; import { RushConstants } from '../RushConstants'; import type { IPolicyValidatorOptions } from './PolicyValidator'; -export function validate(rushConfiguration: RushConfiguration, options: IPolicyValidatorOptions): void { +export async function validateAsync( + rushConfiguration: RushConfiguration, + options: IPolicyValidatorOptions +): Promise { const git: Git = new Git(rushConfiguration); if (!git.isGitPresent()) { // If Git isn't installed, or this Rush project is not under a Git working folder, // then we don't care about the Git email + // eslint-disable-next-line no-console console.log( - colors.cyan('Ignoring Git validation because the Git binary was not found in the shell path.') + '\n' + Colorize.cyan('Ignoring Git validation because the Git binary was not found in the shell path.') + '\n' ); return; } @@ -25,14 +29,16 @@ export function validate(rushConfiguration: RushConfiguration, options: IPolicyV if (!git.isPathUnderGitWorkingTree()) { // If Git isn't installed, or this Rush project is not under a Git working folder, // then we don't care about the Git email - console.log(colors.cyan('Ignoring Git validation because this is not a Git working folder.') + '\n'); + // eslint-disable-next-line no-console + console.log(Colorize.cyan('Ignoring Git validation because this is not a Git working folder.') + '\n'); return; } + let userEmail: string | undefined = await git.tryGetGitEmailAsync(); // If there isn't a Git policy, then we don't care whether the person configured - // a Git email address at all. This helps people who don't + // a Git email address at all. if (rushConfiguration.gitAllowedEmailRegExps.length === 0) { - if (git.tryGetGitEmail() === undefined) { + if (userEmail === undefined) { return; } @@ -40,16 +46,16 @@ export function validate(rushConfiguration: RushConfiguration, options: IPolicyV // sanity checks (e.g. no spaces in the address). } - let userEmail: string; try { - userEmail = git.getGitEmail(); + userEmail = git.validateGitEmail(userEmail); // sanity check; a valid email should not contain any whitespace // if this fails, then we have another issue to report if (!userEmail.match(/^\S+$/g)) { + // eslint-disable-next-line no-console console.log( [ - colors.red('Your Git email address is invalid: ' + JSON.stringify(userEmail)), + Colorize.red('Your Git email address is invalid: ' + JSON.stringify(userEmail)), '', `To configure your Git email address, try something like this:`, '', @@ -66,7 +72,8 @@ export function validate(rushConfiguration: RushConfiguration, options: IPolicyV errorMessage += ` (Or use "${RushConstants.bypassPolicyFlagLongName}" to skip.)`; } - console.log(colors.red(errorMessage)); + // eslint-disable-next-line no-console + console.log(Colorize.red(errorMessage)); throw e; } else { throw e; @@ -78,6 +85,7 @@ export function validate(rushConfiguration: RushConfiguration, options: IPolicyV return; } + // eslint-disable-next-line no-console console.log('Checking Git policy for this repository.\n'); // If there is a policy, at least one of the RegExp's must match @@ -90,12 +98,14 @@ export function validate(rushConfiguration: RushConfiguration, options: IPolicyV // Show the user's name as well. // Ex. "Example Name " - let fancyEmail: string = colors.cyan(userEmail); + let fancyEmail: string = Colorize.cyan(userEmail); try { - const userName: string = Utilities.executeCommandAndCaptureOutput( - git.gitPath!, - ['config', 'user.name'], - '.' + const userName: string = ( + await Utilities.executeCommandAndCaptureOutputAsync({ + command: git.gitPath!, + args: ['config', 'user.name'], + workingDirectory: '.' + }) ).trim(); if (userName) { fancyEmail = `${userName} <${fancyEmail}>`; @@ -104,12 +114,13 @@ export function validate(rushConfiguration: RushConfiguration, options: IPolicyV // but if it fails, this isn't critical, so don't bother them about it } + // eslint-disable-next-line no-console console.log( [ 'Hey there! To keep things tidy, this repo asks you to submit your Git commits using an email like ' + (rushConfiguration.gitAllowedEmailRegExps.length > 1 ? 'one of these patterns:' : 'this pattern:'), '', - ...rushConfiguration.gitAllowedEmailRegExps.map((pattern) => ' ' + colors.cyan(pattern)), + ...rushConfiguration.gitAllowedEmailRegExps.map((pattern) => ' ' + Colorize.cyan(pattern)), '', '...but yours is configured like this:', '', @@ -127,14 +138,15 @@ export function validate(rushConfiguration: RushConfiguration, options: IPolicyV errorMessage += ` (Or use "${RushConstants.bypassPolicyFlagLongName}" to skip.)`; } - console.log(colors.red(errorMessage)); + // eslint-disable-next-line no-console + console.log(Colorize.red(errorMessage)); throw new AlreadyReportedError(); } export function getEmailExampleLines(rushConfiguration: RushConfiguration): string[] { return [ - colors.cyan(' git config --local user.name "Example Name"'), - colors.cyan( + Colorize.cyan(' git config --local user.name "Example Name"'), + Colorize.cyan( ` git config --local user.email "${rushConfiguration.gitSampleEmail || 'name@example.com'}"` ) ]; diff --git a/libraries/rush-lib/src/logic/policy/PolicyValidator.ts b/libraries/rush-lib/src/logic/policy/PolicyValidator.ts index 26407bb1113..b54e31d1295 100644 --- a/libraries/rush-lib/src/logic/policy/PolicyValidator.ts +++ b/libraries/rush-lib/src/logic/policy/PolicyValidator.ts @@ -5,25 +5,27 @@ import type { RushConfiguration } from '../../api/RushConfiguration'; import * as GitEmailPolicy from './GitEmailPolicy'; import * as ShrinkwrapFilePolicy from './ShrinkwrapFilePolicy'; import * as EnvironmentPolicy from './EnvironmentPolicy'; +import type { Subspace } from '../../api/Subspace'; export interface IPolicyValidatorOptions { bypassPolicyAllowed?: boolean; bypassPolicy?: boolean; allowShrinkwrapUpdates?: boolean; - shrinkwrapVariant?: string; } export async function validatePolicyAsync( rushConfiguration: RushConfiguration, + subspace: Subspace, + variant: string | undefined, options: IPolicyValidatorOptions ): Promise { if (!options.bypassPolicy) { - GitEmailPolicy.validate(rushConfiguration, options); + await GitEmailPolicy.validateAsync(rushConfiguration, options); await EnvironmentPolicy.validateAsync(rushConfiguration, options); if (!options.allowShrinkwrapUpdates) { // Don't validate the shrinkwrap if updates are allowed, as it's likely to change // It also may have merge conflict markers, which PNPM can gracefully handle, but the validator cannot - ShrinkwrapFilePolicy.validate(rushConfiguration, options); + ShrinkwrapFilePolicy.validate(rushConfiguration, subspace, variant, options); } } } diff --git a/libraries/rush-lib/src/logic/policy/ShrinkwrapFilePolicy.ts b/libraries/rush-lib/src/logic/policy/ShrinkwrapFilePolicy.ts index 6ce4d44af85..224cffe679a 100644 --- a/libraries/rush-lib/src/logic/policy/ShrinkwrapFilePolicy.ts +++ b/libraries/rush-lib/src/logic/policy/ShrinkwrapFilePolicy.ts @@ -6,6 +6,7 @@ import type { IPolicyValidatorOptions } from './PolicyValidator'; import type { BaseShrinkwrapFile } from '../base/BaseShrinkwrapFile'; import { ShrinkwrapFileFactory } from '../ShrinkwrapFileFactory'; import type { RepoStateFile } from '../RepoStateFile'; +import type { Subspace } from '../../api/Subspace'; export interface IShrinkwrapFilePolicyValidatorOptions extends IPolicyValidatorOptions { repoState: RepoStateFile; @@ -14,15 +15,22 @@ export interface IShrinkwrapFilePolicyValidatorOptions extends IPolicyValidatorO /** * A policy that validates shrinkwrap files used by package managers. */ -export function validate(rushConfiguration: RushConfiguration, options: IPolicyValidatorOptions): void { +export function validate( + rushConfiguration: RushConfiguration, + subspace: Subspace, + variant: string | undefined, + options: IPolicyValidatorOptions +): void { + // eslint-disable-next-line no-console console.log('Validating package manager shrinkwrap file.\n'); - const shrinkwrapFile: BaseShrinkwrapFile | undefined = ShrinkwrapFileFactory.getShrinkwrapFile( - rushConfiguration.packageManager, - rushConfiguration.packageManagerOptions, - rushConfiguration.getCommittedShrinkwrapFilename(options.shrinkwrapVariant) - ); + const shrinkwrapFile: BaseShrinkwrapFile | undefined = ShrinkwrapFileFactory.getShrinkwrapFile({ + packageManager: rushConfiguration.packageManager, + shrinkwrapFilePath: subspace.getCommittedShrinkwrapFilePath(variant), + subspaceHasNoProjects: subspace.getProjects().length === 0 + }); if (!shrinkwrapFile) { + // eslint-disable-next-line no-console console.log('Shrinkwrap file could not be found, skipping validation.\n'); return; } @@ -32,7 +40,7 @@ export function validate(rushConfiguration: RushConfiguration, options: IPolicyV rushConfiguration.packageManagerOptions, { ...options, - repoState: rushConfiguration.getRepoState(options.shrinkwrapVariant) + repoState: subspace.getRepoState() }, rushConfiguration.experimentsConfiguration.configuration ); diff --git a/libraries/rush-lib/src/logic/selectors/GitChangedProjectSelectorParser.ts b/libraries/rush-lib/src/logic/selectors/GitChangedProjectSelectorParser.ts index 3e2745c26cc..65d7b40fae5 100644 --- a/libraries/rush-lib/src/logic/selectors/GitChangedProjectSelectorParser.ts +++ b/libraries/rush-lib/src/logic/selectors/GitChangedProjectSelectorParser.ts @@ -3,8 +3,8 @@ import type { RushConfiguration } from '../../api/RushConfiguration'; import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; -import { IEvaluateSelectorOptions, ISelectorParser } from './ISelectorParser'; -import { IGetChangedProjectsOptions, ProjectChangeAnalyzer } from '../ProjectChangeAnalyzer'; +import type { IEvaluateSelectorOptions, ISelectorParser } from './ISelectorParser'; +import { type IGetChangedProjectsOptions, ProjectChangeAnalyzer } from '../ProjectChangeAnalyzer'; export interface IGitSelectorParserOptions { /** diff --git a/libraries/rush-lib/src/logic/selectors/ISelectorParser.ts b/libraries/rush-lib/src/logic/selectors/ISelectorParser.ts index e20d89fa0da..000010afa11 100644 --- a/libraries/rush-lib/src/logic/selectors/ISelectorParser.ts +++ b/libraries/rush-lib/src/logic/selectors/ISelectorParser.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { ITerminal } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; export interface IEvaluateSelectorOptions { unscopedSelector: string; diff --git a/libraries/rush-lib/src/logic/selectors/NamedProjectSelectorParser.ts b/libraries/rush-lib/src/logic/selectors/NamedProjectSelectorParser.ts index 44b13df52f3..b39ab7ffe96 100644 --- a/libraries/rush-lib/src/logic/selectors/NamedProjectSelectorParser.ts +++ b/libraries/rush-lib/src/logic/selectors/NamedProjectSelectorParser.ts @@ -6,6 +6,7 @@ import { AlreadyReportedError, PackageName } from '@rushstack/node-core-library' import type { RushConfiguration } from '../../api/RushConfiguration'; import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import type { IEvaluateSelectorOptions, ISelectorParser } from './ISelectorParser'; +import { RushConstants } from '../RushConstants'; export class NamedProjectSelectorParser implements ISelectorParser { private readonly _rushConfiguration: RushConfiguration; @@ -23,7 +24,8 @@ export class NamedProjectSelectorParser implements ISelectorParser { + private readonly _rushConfiguration: RushConfiguration; + private readonly _workingDirectory: string; + + public constructor(rushConfiguration: RushConfiguration, workingDirectory: string) { + this._rushConfiguration = rushConfiguration; + this._workingDirectory = workingDirectory; + } + + public async evaluateSelectorAsync({ + unscopedSelector, + terminal, + parameterName + }: IEvaluateSelectorOptions): Promise> { + // Resolve the input path against the working directory + const absolutePath: string = nodePath.resolve(this._workingDirectory, unscopedSelector); + + // Relativize it to the rushJsonFolder + const relativePath: string = nodePath.relative(this._rushConfiguration.rushJsonFolder, absolutePath); + + // Normalize path separators to forward slashes for LookupByPath + const normalizedPath: string = Path.convertToSlashes(relativePath); + + // Get the LookupByPath instance for the Rush root + const lookupByPath: LookupByPath = + this._rushConfiguration.getProjectLookupForRoot(this._rushConfiguration.rushJsonFolder); + + // Check if this path is within a project or matches a project exactly + const containingProject: RushConfigurationProject | undefined = + lookupByPath.findChildPath(normalizedPath); + + if (containingProject) { + return [containingProject]; + } + + // Check if there are any projects under this path (i.e., it's a directory containing projects) + const projectsUnderPath: Set = new Set(); + for (const [, project] of lookupByPath.entries(normalizedPath)) { + projectsUnderPath.add(project); + } + + if (projectsUnderPath.size > 0) { + return projectsUnderPath; + } + + // No projects found + terminal.writeErrorLine( + `The path "${unscopedSelector}" passed to "${parameterName}" does not match any project in ` + + `${RushConstants.rushJsonFilename}. The resolved path relative to the Rush root is "${relativePath}".` + ); + throw new AlreadyReportedError(); + } + + public getCompletions(): Iterable { + // Return empty completions as path completions are typically handled by the shell + return []; + } +} diff --git a/libraries/rush-lib/src/logic/selectors/SubspaceSelectorParser.ts b/libraries/rush-lib/src/logic/selectors/SubspaceSelectorParser.ts new file mode 100644 index 00000000000..f4b3a2bfd06 --- /dev/null +++ b/libraries/rush-lib/src/logic/selectors/SubspaceSelectorParser.ts @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { RushConfiguration } from '../../api/RushConfiguration'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { Subspace } from '../../api/Subspace'; +import { RushConstants } from '../RushConstants'; +import type { IEvaluateSelectorOptions, ISelectorParser } from './ISelectorParser'; + +export class SubspaceSelectorParser implements ISelectorParser { + private readonly _rushConfiguration: RushConfiguration; + + public constructor(rushConfiguration: RushConfiguration) { + this._rushConfiguration = rushConfiguration; + } + + public async evaluateSelectorAsync({ + unscopedSelector + }: IEvaluateSelectorOptions): Promise> { + const subspace: Subspace = this._rushConfiguration.getSubspace(unscopedSelector); + + return subspace.getProjects(); + } + + public getCompletions(): Iterable { + // Tab completion is a performance sensitive operation, so avoid loading all the projects + const subspaceNames: string[] = []; + if (this._rushConfiguration.subspacesConfiguration) { + subspaceNames.push(...this._rushConfiguration.subspacesConfiguration.subspaceNames); + } + if (!subspaceNames.indexOf(RushConstants.defaultSubspaceName)) { + subspaceNames.push(RushConstants.defaultSubspaceName); + } + return subspaceNames; + } +} diff --git a/libraries/rush-lib/src/logic/selectors/TagProjectSelectorParser.ts b/libraries/rush-lib/src/logic/selectors/TagProjectSelectorParser.ts index 79572f091c7..f704bb81edc 100644 --- a/libraries/rush-lib/src/logic/selectors/TagProjectSelectorParser.ts +++ b/libraries/rush-lib/src/logic/selectors/TagProjectSelectorParser.ts @@ -6,6 +6,7 @@ import { AlreadyReportedError } from '@rushstack/node-core-library'; import type { RushConfiguration } from '../../api/RushConfiguration'; import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import type { IEvaluateSelectorOptions, ISelectorParser } from './ISelectorParser'; +import { RushConstants } from '../RushConstants'; export class TagProjectSelectorParser implements ISelectorParser { private readonly _rushConfiguration: RushConfiguration; @@ -23,7 +24,8 @@ export class TagProjectSelectorParser implements ISelectorParser { + let rushConfiguration: RushConfiguration; + let terminal: Terminal; + let terminalProvider: StringBufferTerminalProvider; + let parser: NamedProjectSelectorParser; + + beforeEach(() => { + const rushJsonFile: string = path.resolve(__dirname, '../../../api/test/repo/rush-npm.json'); + rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); + terminalProvider = new StringBufferTerminalProvider(); + terminal = new Terminal(terminalProvider); + parser = new NamedProjectSelectorParser(rushConfiguration); + }); + + it('should select a project by exact package name', async () => { + const result = await parser.evaluateSelectorAsync({ + unscopedSelector: 'project1', + terminal, + parameterName: '--only' + }); + + const projects = Array.from(result); + expect(projects).toHaveLength(1); + expect(projects[0].packageName).toBe('project1'); + }); + + it('should throw error for non-existent project', async () => { + await expect( + parser.evaluateSelectorAsync({ + unscopedSelector: 'nonexistent', + terminal, + parameterName: '--only' + }) + ).rejects.toThrow(); + }); + + it('should provide completions for all projects', () => { + const completions = Array.from(parser.getCompletions()); + expect(completions).toContain('project1'); + expect(completions).toContain('project2'); + expect(completions).toContain('project3'); + }); +}); diff --git a/libraries/rush-lib/src/logic/selectors/test/PathProjectSelectorParser.test.ts b/libraries/rush-lib/src/logic/selectors/test/PathProjectSelectorParser.test.ts new file mode 100644 index 00000000000..982b852f23c --- /dev/null +++ b/libraries/rush-lib/src/logic/selectors/test/PathProjectSelectorParser.test.ts @@ -0,0 +1,132 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; + +import { RushConfiguration } from '../../../api/RushConfiguration'; +import { PathProjectSelectorParser } from '../PathProjectSelectorParser'; + +describe(PathProjectSelectorParser.name, () => { + let rushConfiguration: RushConfiguration; + let terminal: Terminal; + let terminalProvider: StringBufferTerminalProvider; + let parser: PathProjectSelectorParser; + + beforeEach(() => { + const rushJsonFile: string = path.resolve(__dirname, '../../../api/test/repo/rush-npm.json'); + rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); + terminalProvider = new StringBufferTerminalProvider(); + terminal = new Terminal(terminalProvider); + parser = new PathProjectSelectorParser(rushConfiguration, rushConfiguration.rushJsonFolder); + }); + + it('should select a project by exact path', async () => { + const result = await parser.evaluateSelectorAsync({ + unscopedSelector: 'project1', + terminal, + parameterName: '--only' + }); + + const projects = Array.from(result); + expect(projects).toHaveLength(1); + expect(projects[0].packageName).toBe('project1'); + }); + + it('should select a project by path within the project', async () => { + const result = await parser.evaluateSelectorAsync({ + unscopedSelector: 'project1/src/index.ts', + terminal, + parameterName: '--only' + }); + + const projects = Array.from(result); + expect(projects).toHaveLength(1); + expect(projects[0].packageName).toBe('project1'); + }); + + it('should select multiple projects from a parent directory', async () => { + const result = await parser.evaluateSelectorAsync({ + unscopedSelector: '.', + terminal, + parameterName: '--only' + }); + + const projects = Array.from(result); + expect(projects.length).toBeGreaterThan(0); + // Should include all projects in the test repo + const packageNames = projects.map((p) => p.packageName).sort(); + expect(packageNames).toContain('project1'); + expect(packageNames).toContain('project2'); + expect(packageNames).toContain('project3'); + }); + + it('should select multiple projects from a shared subfolder', async () => { + const result = await parser.evaluateSelectorAsync({ + unscopedSelector: 'apps', + terminal, + parameterName: '--only' + }); + + const projects = Array.from(result); + expect(projects).toHaveLength(2); + const packageNames = projects.map((p) => p.packageName).sort(); + expect(packageNames).toEqual(['app1', 'app2']); + }); + + it('should select project from specified directory', async () => { + const project1Path = path.join(rushConfiguration.rushJsonFolder, 'project1'); + const parserWithCustomCwd = new PathProjectSelectorParser(rushConfiguration, project1Path); + + const result = await parserWithCustomCwd.evaluateSelectorAsync({ + unscopedSelector: '.', + terminal, + parameterName: '--only' + }); + + const projects = Array.from(result); + expect(projects).toHaveLength(1); + expect(projects[0].packageName).toBe('project1'); + }); + + it('should handle absolute paths', async () => { + const absolutePath = path.join(rushConfiguration.rushJsonFolder, 'project2'); + + const result = await parser.evaluateSelectorAsync({ + unscopedSelector: absolutePath, + terminal, + parameterName: '--only' + }); + + const projects = Array.from(result); + expect(projects).toHaveLength(1); + expect(projects[0].packageName).toBe('project2'); + }); + + it('should throw error for paths that do not match any project', async () => { + await expect( + parser.evaluateSelectorAsync({ + unscopedSelector: 'nonexistent/path', + terminal, + parameterName: '--only' + }) + ).rejects.toThrow(); + }); + + it('should handle paths outside workspace', async () => { + // Paths outside the workspace should not match any project and throw + await expect( + parser.evaluateSelectorAsync({ + unscopedSelector: '../outside', + terminal, + parameterName: '--only' + }) + ).rejects.toThrow(); + }); + + it('should return empty completions', () => { + const completions = Array.from(parser.getCompletions()); + expect(completions).toHaveLength(0); + }); +}); diff --git a/libraries/rush-lib/src/logic/selectors/test/SubspaceSelectorParser.test.ts b/libraries/rush-lib/src/logic/selectors/test/SubspaceSelectorParser.test.ts new file mode 100644 index 00000000000..7508ec45a60 --- /dev/null +++ b/libraries/rush-lib/src/logic/selectors/test/SubspaceSelectorParser.test.ts @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; + +import { RushConfiguration } from '../../../api/RushConfiguration'; +import { SubspaceSelectorParser } from '../SubspaceSelectorParser'; + +describe(SubspaceSelectorParser.name, () => { + let rushConfiguration: RushConfiguration; + let terminal: Terminal; + let terminalProvider: StringBufferTerminalProvider; + let parser: SubspaceSelectorParser; + + beforeEach(() => { + const rushJsonFile: string = path.resolve(__dirname, '../../../api/test/repo/rush-npm.json'); + rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); + terminalProvider = new StringBufferTerminalProvider(); + terminal = new Terminal(terminalProvider); + parser = new SubspaceSelectorParser(rushConfiguration); + }); + + it('should return completions based on configuration', () => { + const completions = Array.from(parser.getCompletions()); + // The test fixture doesn't have subspaces configured, so completions may be empty + expect(Array.isArray(completions)).toBe(true); + }); + + it('should select projects from default subspace', async () => { + const result = await parser.evaluateSelectorAsync({ + unscopedSelector: 'default', + terminal, + parameterName: '--only' + }); + + const projects = Array.from(result); + // Should get projects from the default subspace + expect(projects.length).toBeGreaterThan(0); + }); +}); diff --git a/libraries/rush-lib/src/logic/selectors/test/TagProjectSelectorParser.test.ts b/libraries/rush-lib/src/logic/selectors/test/TagProjectSelectorParser.test.ts new file mode 100644 index 00000000000..1c78cd5765f --- /dev/null +++ b/libraries/rush-lib/src/logic/selectors/test/TagProjectSelectorParser.test.ts @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; + +import { RushConfiguration } from '../../../api/RushConfiguration'; +import { TagProjectSelectorParser } from '../TagProjectSelectorParser'; + +describe(TagProjectSelectorParser.name, () => { + let rushConfiguration: RushConfiguration; + let terminal: Terminal; + let terminalProvider: StringBufferTerminalProvider; + let parser: TagProjectSelectorParser; + + beforeEach(() => { + const rushJsonFile: string = path.resolve(__dirname, '../../../api/test/repo/rush-npm.json'); + rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); + terminalProvider = new StringBufferTerminalProvider(); + terminal = new Terminal(terminalProvider); + parser = new TagProjectSelectorParser(rushConfiguration); + }); + + it('should provide completions for tags', () => { + const completions = Array.from(parser.getCompletions()); + expect(completions.length).toBeGreaterThan(0); + expect(completions).toContain('frontend'); + expect(completions).toContain('backend'); + expect(completions).toContain('ui'); + }); + + it('should select projects by tag', async () => { + const result = await parser.evaluateSelectorAsync({ + unscopedSelector: 'frontend', + terminal, + parameterName: '--only' + }); + + const projects = Array.from(result); + expect(projects.length).toBe(2); + const packageNames = projects.map((p) => p.packageName).sort(); + expect(packageNames).toEqual(['project1', 'project3']); + }); + + it('should select single project by unique tag', async () => { + const result = await parser.evaluateSelectorAsync({ + unscopedSelector: 'backend', + terminal, + parameterName: '--only' + }); + + const projects = Array.from(result); + expect(projects).toHaveLength(1); + expect(projects[0].packageName).toBe('project2'); + }); + + it('should throw error for non-existent tag', async () => { + await expect( + parser.evaluateSelectorAsync({ + unscopedSelector: 'nonexistent-tag', + terminal, + parameterName: '--only' + }) + ).rejects.toThrow(); + }); +}); diff --git a/libraries/rush-lib/src/logic/selectors/test/VersionPolicyProjectSelectorParser.test.ts b/libraries/rush-lib/src/logic/selectors/test/VersionPolicyProjectSelectorParser.test.ts new file mode 100644 index 00000000000..e84c355cc8c --- /dev/null +++ b/libraries/rush-lib/src/logic/selectors/test/VersionPolicyProjectSelectorParser.test.ts @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; + +import { RushConfiguration } from '../../../api/RushConfiguration'; +import { VersionPolicyProjectSelectorParser } from '../VersionPolicyProjectSelectorParser'; + +describe(VersionPolicyProjectSelectorParser.name, () => { + let rushConfiguration: RushConfiguration; + let terminal: Terminal; + let terminalProvider: StringBufferTerminalProvider; + let parser: VersionPolicyProjectSelectorParser; + + beforeEach(() => { + const rushJsonFile: string = path.resolve(__dirname, '../../../api/test/repo/rush-npm.json'); + rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); + terminalProvider = new StringBufferTerminalProvider(); + terminal = new Terminal(terminalProvider); + parser = new VersionPolicyProjectSelectorParser(rushConfiguration); + }); + + it('should return completions for version policies', () => { + const completions = Array.from(parser.getCompletions()); + expect(completions.length).toBeGreaterThan(0); + expect(completions).toContain('testPolicy'); + }); + + it('should select projects by version policy', async () => { + const result = await parser.evaluateSelectorAsync({ + unscopedSelector: 'testPolicy', + terminal, + parameterName: '--only' + }); + + const projects = Array.from(result); + expect(projects).toHaveLength(2); + const packageNames = projects.map((p) => p.packageName).sort(); + expect(packageNames).toEqual(['project1', 'project3']); + }); + + it('should throw error for non-existent version policy', async () => { + await expect( + parser.evaluateSelectorAsync({ + unscopedSelector: 'nonexistent-policy', + terminal, + parameterName: '--only' + }) + ).rejects.toThrow(); + }); +}); diff --git a/libraries/rush-lib/src/logic/setup/ArtifactoryConfiguration.ts b/libraries/rush-lib/src/logic/setup/ArtifactoryConfiguration.ts index fc72e437b4b..effb502a5c9 100644 --- a/libraries/rush-lib/src/logic/setup/ArtifactoryConfiguration.ts +++ b/libraries/rush-lib/src/logic/setup/ArtifactoryConfiguration.ts @@ -31,13 +31,13 @@ export interface IArtifactoryJson { packageRegistry: IArtifactoryPackageRegistryJson; } +const _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); + /** * Use this class to load the "common/config/rush/artifactory.json" config file. * It configures the "rush setup" command. */ export class ArtifactoryConfiguration { - private static _jsonSchema: JsonSchema = JsonSchema.fromLoadedObject(schemaJson); - private readonly _jsonFileName: string; /** @@ -60,7 +60,7 @@ export class ArtifactoryConfiguration { }; if (FileSystem.exists(this._jsonFileName)) { - this.configuration = JsonFile.loadAndValidate(this._jsonFileName, ArtifactoryConfiguration._jsonSchema); + this.configuration = JsonFile.loadAndValidate(this._jsonFileName, _jsonSchema); if (!this.configuration.packageRegistry.credentialType) { this.configuration.packageRegistry.credentialType = 'password'; } diff --git a/libraries/rush-lib/src/logic/setup/KeyboardLoop.ts b/libraries/rush-lib/src/logic/setup/KeyboardLoop.ts index fa67f66fac7..962dd2d5e68 100644 --- a/libraries/rush-lib/src/logic/setup/KeyboardLoop.ts +++ b/libraries/rush-lib/src/logic/setup/KeyboardLoop.ts @@ -1,13 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; -import * as readline from 'readline'; -import * as process from 'process'; +import * as readline from 'node:readline'; +import * as process from 'node:process'; + import { AlreadyReportedError, InternalError } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; + +import { IS_WINDOWS } from '../../utilities/executionUtilities'; -// TODO: Integrate these into the AnsiEscape API in @rushstack/node-core-library -// As part of that work we should generalize the "Colors" API to support more general +// TODO: Integrate these into the AnsiEscape API in @rushstack/terminal +// As part of that work we should generalize the "Colorize" API to support more general // terminal escapes, and simplify the interface for that API. const ANSI_ESCAPE_SHOW_CURSOR: string = '\u001B[?25l'; const ANSI_ESCAPE_HIDE_CURSOR: string = '\u001B[?25h'; @@ -49,12 +52,13 @@ export class KeyboardLoop { return; } - if (process.platform === 'win32') { + if (IS_WINDOWS) { const shell: string = process.env.SHELL ?? ''; if (shell.toUpperCase().endsWith('BASH.EXE')) { // Git Bash has a known problem where the Node.js TTY is lost when invoked via an NPM binary script. + // eslint-disable-next-line no-console console.error( - colors.red( + Colorize.red( 'ERROR: It appears that Rush was invoked from Git Bash shell, which does not support the\n' + 'TTY mode for interactive input that is required by this feature.' ) + @@ -69,8 +73,9 @@ export class KeyboardLoop { } } + // eslint-disable-next-line no-console console.error( - colors.red( + Colorize.red( 'ERROR: Rush was invoked by a command whose STDIN does not support the TTY mode for\n' + 'interactive input that is required by this feature.' ) + '\n\nTry invoking "rush" directly from your shell.' diff --git a/libraries/rush-lib/src/logic/setup/SetupPackageRegistry.ts b/libraries/rush-lib/src/logic/setup/SetupPackageRegistry.ts index e834123b35a..6ffa3504e6a 100644 --- a/libraries/rush-lib/src/logic/setup/SetupPackageRegistry.ts +++ b/libraries/rush-lib/src/logic/setup/SetupPackageRegistry.ts @@ -1,26 +1,25 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import * as child_process from 'child_process'; +import * as path from 'node:path'; +import type * as child_process from 'node:child_process'; + import { AlreadyReportedError, - Colors, - ConsoleTerminalProvider, Executable, FileSystem, InternalError, - JsonObject, + type JsonObject, NewlineKind, - Terminal, - Text + Text, + User } from '@rushstack/node-core-library'; -import { PrintUtilities } from '@rushstack/terminal'; +import { PrintUtilities, Colorize, ConsoleTerminalProvider, Terminal } from '@rushstack/terminal'; -import { RushConfiguration } from '../../api/RushConfiguration'; +import type { RushConfiguration } from '../../api/RushConfiguration'; import { Utilities } from '../../utilities/Utilities'; -import { IArtifactoryPackageRegistryJson, ArtifactoryConfiguration } from './ArtifactoryConfiguration'; -import { WebClient, WebClientResponse } from '../../utilities/WebClient'; +import { type IArtifactoryPackageRegistryJson, ArtifactoryConfiguration } from './ArtifactoryConfiguration'; +import type { WebClient as WebClientType, IWebClientResponse } from '../../utilities/WebClient'; import { TerminalInput } from './TerminalInput'; interface IArtifactoryCustomizableMessages { @@ -97,7 +96,7 @@ export class SetupPackageRegistry { * * @returns - `true` if valid, `false` if not valid */ - public async checkOnly(): Promise { + public async checkOnlyAsync(): Promise { const packageRegistry: IArtifactoryPackageRegistryJson = this._artifactoryConfiguration.configuration.packageRegistry; if (!packageRegistry.enabled) { @@ -111,10 +110,11 @@ export class SetupPackageRegistry { } if (!this._options.syncNpmrcAlreadyCalled) { - Utilities.syncNpmrc( - this.rushConfiguration.commonRushConfigFolder, - this.rushConfiguration.commonTempFolder - ); + Utilities.syncNpmrc({ + sourceNpmrcFolder: this.rushConfiguration.commonRushConfigFolder, + targetNpmrcFolder: this.rushConfiguration.commonTempFolder, + supportEnvVarFallbackSyntax: this.rushConfiguration.isPnpm + }); } // Artifactory does not implement the "npm ping" protocol or any equivalent REST API. @@ -155,10 +155,10 @@ export class SetupPackageRegistry { } // NPM 6.x writes to stdout - let jsonContent: string | undefined = SetupPackageRegistry._tryFindJson(result.stdout); + let jsonContent: string | undefined = _tryFindJson(result.stdout); if (jsonContent === undefined) { // NPM 7.x writes dirty output to stderr; see https://github.com/npm/cli/issues/2740 - jsonContent = SetupPackageRegistry._tryFindJson(result.stderr); + jsonContent = _tryFindJson(result.stderr); } if (jsonContent === undefined) { throw new InternalError('The "npm view" command did not return a JSON structure'); @@ -167,10 +167,11 @@ export class SetupPackageRegistry { let jsonOutput: JsonObject; try { jsonOutput = JSON.parse(jsonContent); - } catch (error) { + } catch (e) { this._terminal.writeVerboseLine('NPM response:\n\n--------\n' + jsonContent + '\n--------\n\n'); throw new InternalError('The "npm view" command returned an invalid JSON structure'); } + const errorCode: JsonObject = jsonOutput?.error?.code; if (typeof errorCode !== 'string') { this._terminal.writeVerboseLine('NPM response:\n' + JSON.stringify(jsonOutput, undefined, 2) + '\n\n'); @@ -200,8 +201,8 @@ export class SetupPackageRegistry { /** * Test whether the NPM token is valid. If not, prompt to update it. */ - public async checkAndSetup(): Promise { - if (await this.checkOnly()) { + public async checkAndSetupAsync(): Promise { + if (await this.checkOnlyAsync()) { return; } @@ -211,7 +212,7 @@ export class SetupPackageRegistry { const packageRegistry: IArtifactoryPackageRegistryJson = this._artifactoryConfiguration.configuration.packageRegistry; - const fixThisProblem: boolean = await TerminalInput.promptYesNo({ + const fixThisProblem: boolean = await TerminalInput.promptYesNoAsync({ message: 'Fix this problem now?', defaultValue: false }); @@ -222,7 +223,7 @@ export class SetupPackageRegistry { this._writeInstructionBlock(this._messages.introduction); - const hasArtifactoryAccount: boolean = await TerminalInput.promptYesNo({ + const hasArtifactoryAccount: boolean = await TerminalInput.promptYesNoAsync({ message: 'Do you already have an Artifactory user account?' }); this._terminal.writeLine(); @@ -239,54 +240,56 @@ export class SetupPackageRegistry { this._artifactoryConfiguration.configuration.packageRegistry.artifactoryWebsiteUrl; if (artifactoryWebsiteUrl) { - this._terminal.writeLine(' ', Colors.cyan(artifactoryWebsiteUrl)); + this._terminal.writeLine(' ', Colorize.cyan(artifactoryWebsiteUrl)); this._terminal.writeLine(); } } this._writeInstructionBlock(this._messages.locateUserName); - let artifactoryUser: string = await TerminalInput.promptLine({ + let artifactoryUser: string = await TerminalInput.promptLineAsync({ message: this._messages.userNamePrompt }); this._terminal.writeLine(); artifactoryUser = artifactoryUser.trim(); if (artifactoryUser.length === 0) { - this._terminal.writeLine(Colors.red('Operation aborted because the input was empty')); + this._terminal.writeLine(Colorize.red('Operation aborted because the input was empty')); this._terminal.writeLine(); throw new AlreadyReportedError(); } this._writeInstructionBlock(this._messages.locateApiKey); - let artifactoryKey: string = await TerminalInput.promptPasswordLine({ + let artifactoryKey: string = await TerminalInput.promptPasswordLineAsync({ message: this._messages.apiKeyPrompt }); this._terminal.writeLine(); artifactoryKey = artifactoryKey.trim(); if (artifactoryKey.length === 0) { - this._terminal.writeLine(Colors.red('Operation aborted because the input was empty')); + this._terminal.writeLine(Colorize.red('Operation aborted because the input was empty')); this._terminal.writeLine(); throw new AlreadyReportedError(); } - await this._fetchTokenAndUpdateNpmrc(artifactoryUser, artifactoryKey, packageRegistry); + await this._fetchTokenAndUpdateNpmrcAsync(artifactoryUser, artifactoryKey, packageRegistry); } /** * Fetch a valid NPM token from the Artifactory service and add it to the `~/.npmrc` file, * preserving other settings in that file. */ - private async _fetchTokenAndUpdateNpmrc( + private async _fetchTokenAndUpdateNpmrcAsync( artifactoryUser: string, artifactoryKey: string, packageRegistry: IArtifactoryPackageRegistryJson ): Promise { this._terminal.writeLine('\nFetching an NPM token from the Artifactory service...'); - const webClient: WebClient = new WebClient(); + // Defer this import since it is conditionally needed. + const { WebClient } = await import('../../utilities/WebClient'); + const webClient: WebClientType = new WebClient(); webClient.addBasicAuthHeader(artifactoryUser, artifactoryKey); @@ -300,10 +303,11 @@ export class SetupPackageRegistry { // our token. queryUrl += `auth/.npm`; - let response: WebClientResponse; + let response: IWebClientResponse; try { response = await webClient.fetchAsync(queryUrl); } catch (e) { + // eslint-disable-next-line no-console console.log((e as Error).toString()); return; } @@ -323,7 +327,7 @@ export class SetupPackageRegistry { // //your-company.jfrog.io/your-artifacts/api/npm/npm-private/:username=your.name@your-company.com // //your-company.jfrog.io/your-artifacts/api/npm/npm-private/:email=your.name@your-company.com // //your-company.jfrog.io/your-artifacts/api/npm/npm-private/:always-auth=true - const responseText: string = await response.text(); + const responseText: string = await response.getTextAsync(); const responseLines: string[] = Text.convertToLf(responseText).trim().split('\n'); if (responseLines.length < 2 || !responseLines[0].startsWith('@.npm:')) { throw new Error('Unexpected response from Artifactory'); @@ -348,9 +352,9 @@ export class SetupPackageRegistry { } // ...then append the stuff we got from the REST API, but discard any junk that isn't a proper key/value - linesToAdd.push(...responseLines.filter((x) => SetupPackageRegistry._getNpmrcKey(x) !== undefined)); + linesToAdd.push(...responseLines.filter((x) => _getNpmrcKey(x) !== undefined)); - const npmrcPath: string = path.join(Utilities.getHomeFolder(), '.npmrc'); + const npmrcPath: string = path.join(User.getHomeFolder(), '.npmrc'); this._mergeLinesIntoNpmrc(npmrcPath, linesToAdd); } @@ -381,7 +385,7 @@ export class SetupPackageRegistry { for (let index: number = 0; index < workingLinesToAdd.length; ++index) { const lineToAdd: string = workingLinesToAdd[index]!; - const key: string | undefined = SetupPackageRegistry._getNpmrcKey(lineToAdd); + const key: string | undefined = _getNpmrcKey(lineToAdd); if (key !== undefined) { // If there are duplicate keys, the first one takes precedence. // In particular this means "userNpmrcLinesToAdd" takes precedence over the REST API response @@ -395,7 +399,7 @@ export class SetupPackageRegistry { } this._terminal.writeLine(); - this._terminal.writeLine(Colors.green('Adding Artifactory token to: '), npmrcPath); + this._terminal.writeLine(Colorize.green('Adding Artifactory token to: '), npmrcPath); const npmrcLines: string[] = []; @@ -414,7 +418,7 @@ export class SetupPackageRegistry { for (const npmrcLine of npmrcLines) { const trimmed: string = npmrcLine.trim(); if (trimmed.length > 0) { - if (SetupPackageRegistry._getNpmrcKey(trimmed) === undefined) { + if (_getNpmrcKey(trimmed) === undefined) { npmrcNonKeyLinesSet.add(trimmed); } } @@ -424,7 +428,7 @@ export class SetupPackageRegistry { for (let index: number = 0; index < npmrcLines.length; ++index) { const line: string = npmrcLines[index]; - const key: string | undefined = SetupPackageRegistry._getNpmrcKey(line); + const key: string | undefined = _getNpmrcKey(line); if (key) { const linesToAddIndex: number | undefined = keysToReplace.get(key); if (linesToAddIndex !== undefined) { @@ -459,73 +463,73 @@ export class SetupPackageRegistry { // Save the result FileSystem.writeFile(npmrcPath, npmrcLines.join('\n').trimRight() + '\n'); } +} - private static _getNpmrcKey(npmrcLine: string): string | undefined { - if (SetupPackageRegistry._isCommentLine(npmrcLine)) { - return undefined; - } - const delimiterIndex: number = npmrcLine.indexOf('='); - if (delimiterIndex < 1) { - return undefined; - } - const key: string = npmrcLine.substring(0, delimiterIndex + 1); - return key.trim(); +function _getNpmrcKey(npmrcLine: string): string | undefined { + if (_isCommentLine(npmrcLine)) { + return undefined; } - - private static _isCommentLine(npmrcLine: string): boolean { - return /^\s*#/.test(npmrcLine); + const delimiterIndex: number = npmrcLine.indexOf('='); + if (delimiterIndex < 1) { + return undefined; } + const key: string = npmrcLine.substring(0, delimiterIndex + 1); + return key.trim(); +} - /** - * This is a workaround for https://github.com/npm/cli/issues/2740 where the NPM tool sometimes - * mixes together JSON and terminal messages in a single STDERR stream. - * - * @remarks - * Given an input like this: - * ``` - * npm ERR! 404 Note that you can also install from a - * npm ERR! 404 tarball, folder, http url, or git url. - * { - * "error": { - * "code": "E404", - * "summary": "Not Found - GET https://registry.npmjs.org/@rushstack%2fnonexistent-package - Not found" - * } - * } - * npm ERR! A complete log of this run can be found in: - * ``` - * - * @returns the JSON section, or `undefined` if a JSON object could not be detected - */ - private static _tryFindJson(dirtyOutput: string): string | undefined { - const lines: string[] = dirtyOutput.split(/\r?\n/g); - let startIndex: number | undefined; - let endIndex: number | undefined; - - // Find the first line that starts with "{" - for (let i: number = 0; i < lines.length; ++i) { - const line: string = lines[i]; - if (/^\s*\{/.test(line)) { - startIndex = i; - break; - } - } - if (startIndex === undefined) { - return undefined; - } +function _isCommentLine(npmrcLine: string): boolean { + return /^\s*#/.test(npmrcLine); +} - // Find the last line that ends with "}" - for (let i: number = lines.length - 1; i >= startIndex; --i) { - const line: string = lines[i]; - if (/\}\s*$/.test(line)) { - endIndex = i; - break; - } +/** + * This is a workaround for https://github.com/npm/cli/issues/2740 where the NPM tool sometimes + * mixes together JSON and terminal messages in a single STDERR stream. + * + * @remarks + * Given an input like this: + * ``` + * npm ERR! 404 Note that you can also install from a + * npm ERR! 404 tarball, folder, http url, or git url. + * { + * "error": { + * "code": "E404", + * "summary": "Not Found - GET https://registry.npmjs.org/@rushstack%2fnonexistent-package - Not found" + * } + * } + * npm ERR! A complete log of this run can be found in: + * ``` + * + * @returns the JSON section, or `undefined` if a JSON object could not be detected + */ +function _tryFindJson(dirtyOutput: string): string | undefined { + const lines: string[] = Text.splitByNewLines(dirtyOutput); + let startIndex: number | undefined; + let endIndex: number | undefined; + + // Find the first line that starts with "{" + for (let i: number = 0; i < lines.length; ++i) { + const line: string = lines[i]; + if (/^\s*\{/.test(line)) { + startIndex = i; + break; } + } + if (startIndex === undefined) { + return undefined; + } - if (endIndex === undefined) { - return undefined; + // Find the last line that ends with "}" + for (let i: number = lines.length - 1; i >= startIndex; --i) { + const line: string = lines[i]; + if (/\}\s*$/.test(line)) { + endIndex = i; + break; } + } - return lines.slice(startIndex, endIndex + 1).join('\n'); + if (endIndex === undefined) { + return undefined; } + + return lines.slice(startIndex, endIndex + 1).join('\n'); } diff --git a/libraries/rush-lib/src/logic/setup/TerminalInput.ts b/libraries/rush-lib/src/logic/setup/TerminalInput.ts index 05f46cb810f..a73b4eda1f6 100644 --- a/libraries/rush-lib/src/logic/setup/TerminalInput.ts +++ b/libraries/rush-lib/src/logic/setup/TerminalInput.ts @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as readline from 'readline'; -import * as process from 'process'; -import colors from 'colors/safe'; -import { AnsiEscape } from '@rushstack/node-core-library'; +import * as readline from 'node:readline'; +import * as process from 'node:process'; + +import { AnsiEscape, Colorize } from '@rushstack/terminal'; import { KeyboardLoop } from './KeyboardLoop'; @@ -35,9 +35,9 @@ class YesNoKeyboardLoop extends KeyboardLoop { this.options = options; } - protected onStart(): void { - this.stderr.write(colors.green('==>') + ' '); - this.stderr.write(colors.bold(this.options.message)); + protected override onStart(): void { + this.stderr.write(Colorize.green('==>') + ' '); + this.stderr.write(Colorize.bold(this.options.message)); let optionSuffix: string = ''; switch (this.options.defaultValue) { case true: @@ -50,10 +50,10 @@ class YesNoKeyboardLoop extends KeyboardLoop { optionSuffix = '(y/n)'; break; } - this.stderr.write(' ' + colors.bold(optionSuffix) + ' '); + this.stderr.write(' ' + Colorize.bold(optionSuffix) + ' '); } - protected onKeypress(character: string, key: readline.Key): void { + protected override onKeypress(character: string, key: readline.Key): void { if (this.result !== undefined) { return; } @@ -102,12 +102,12 @@ class PasswordKeyboardLoop extends KeyboardLoop { return this.stderr.columns ? this.stderr.columns : 80; } - protected onStart(): void { + protected override onStart(): void { this.result = ''; readline.cursorTo(this.stderr, 0); readline.clearLine(this.stderr, 1); - const prefix: string = colors.green('==>') + ' ' + colors.bold(this._options.message) + ' '; + const prefix: string = Colorize.green('==>') + ' ' + Colorize.bold(this._options.message) + ' '; this.stderr.write(prefix); let lineStartIndex: number = prefix.lastIndexOf('\n'); @@ -118,7 +118,7 @@ class PasswordKeyboardLoop extends KeyboardLoop { this._startX = AnsiEscape.removeCodes(line).length % this._getLineWrapWidth(); } - protected onKeypress(character: string, key: readline.Key): void { + protected override onKeypress(character: string, key: readline.Key): void { switch (key.name) { case 'enter': case 'return': @@ -204,36 +204,36 @@ class PasswordKeyboardLoop extends KeyboardLoop { } export class TerminalInput { - private static async _readLine(): Promise { - const readlineInterface: readline.Interface = readline.createInterface({ input: process.stdin }); - try { - return await new Promise((resolve, reject) => { - readlineInterface.question('', (answer: string) => { - resolve(answer); - }); - }); - } finally { - readlineInterface.close(); - } - } - - public static async promptYesNo(options: IPromptYesNoOptions): Promise { + public static async promptYesNoAsync(options: IPromptYesNoOptions): Promise { const keyboardLoop: YesNoKeyboardLoop = new YesNoKeyboardLoop(options); await keyboardLoop.startAsync(); return keyboardLoop.result!; } - public static async promptLine(options: IPromptLineOptions): Promise { + public static async promptLineAsync(options: IPromptLineOptions): Promise { const stderr: NodeJS.WriteStream = process.stderr; - stderr.write(colors.green('==>') + ' '); - stderr.write(colors.bold(options.message)); + stderr.write(Colorize.green('==>') + ' '); + stderr.write(Colorize.bold(options.message)); stderr.write(' '); - return await TerminalInput._readLine(); + return await _readLineAsync(); } - public static async promptPasswordLine(options: IPromptLineOptions): Promise { + public static async promptPasswordLineAsync(options: IPromptLineOptions): Promise { const keyboardLoop: PasswordKeyboardLoop = new PasswordKeyboardLoop(options); await keyboardLoop.startAsync(); return keyboardLoop.result; } } + +async function _readLineAsync(): Promise { + const readlineInterface: readline.Interface = readline.createInterface({ input: process.stdin }); + try { + return await new Promise((resolve, reject) => { + readlineInterface.question('', (answer: string) => { + resolve(answer); + }); + }); + } finally { + readlineInterface.close(); + } +} diff --git a/libraries/rush-lib/src/logic/test/BaseInstallManager.test.ts b/libraries/rush-lib/src/logic/test/BaseInstallManager.test.ts index a2c45ccc194..7ecf61071f4 100644 --- a/libraries/rush-lib/src/logic/test/BaseInstallManager.test.ts +++ b/libraries/rush-lib/src/logic/test/BaseInstallManager.test.ts @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { ConsoleTerminalProvider } from '@rushstack/node-core-library'; + +import * as path from 'node:path'; +import { ConsoleTerminalProvider, type ITerminal, Terminal } from '@rushstack/terminal'; import { PurgeManager } from '../PurgeManager'; import { BaseInstallManager, pnpmIgnoreCompatibilityDbParameter } from '../base/BaseInstallManager'; @@ -9,6 +10,7 @@ import type { IInstallManagerOptions } from '../base/BaseInstallManagerTypes'; import { RushConfiguration } from '../../api/RushConfiguration'; import { RushGlobalFolder } from '../../api/RushGlobalFolder'; +import type { Subspace } from '../../api/Subspace'; class FakeBaseInstallManager extends BaseInstallManager { public constructor( @@ -34,8 +36,13 @@ class FakeBaseInstallManager extends BaseInstallManager { protected postInstallAsync(): Promise { return Promise.resolve(); } - public pushConfigurationArgs(args: string[], options: IInstallManagerOptions): void { - return super.pushConfigurationArgs(args, options); + + public override pushConfigurationArgs( + args: string[], + options: IInstallManagerOptions, + subspace: Subspace + ): void { + return super.pushConfigurationArgs(args, options, subspace); } } @@ -49,6 +56,15 @@ describe('BaseInstallManager Test', () => { RushConfiguration.loadFromConfigurationFile(rushJsonFilePnpmV6); const rushConfigurationV7: RushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFilePnpmV7); + const terminal: ITerminal = new Terminal(new ConsoleTerminalProvider()); + const options6: IInstallManagerOptions = { + subspace: rushConfigurationV6.defaultSubspace, + terminal + } as IInstallManagerOptions; + const options7: IInstallManagerOptions = { + subspace: rushConfigurationV7.defaultSubspace, + terminal + } as IInstallManagerOptions; const purgeManager6: typeof PurgeManager.prototype = new PurgeManager( rushConfigurationV6, rushGlobalFolder @@ -57,34 +73,33 @@ describe('BaseInstallManager Test', () => { rushConfigurationV7, rushGlobalFolder ); - const options: IInstallManagerOptions = {} as IInstallManagerOptions; const fakeBaseInstallManager6: FakeBaseInstallManager = new FakeBaseInstallManager( rushConfigurationV6, rushGlobalFolder, purgeManager6, - options + options6 ); const fakeBaseInstallManager7: FakeBaseInstallManager = new FakeBaseInstallManager( rushConfigurationV7, rushGlobalFolder, purgeManager7, - options + options7 ); const mockWrite = jest.fn(); jest.spyOn(ConsoleTerminalProvider.prototype, 'write').mockImplementation(mockWrite); const argsPnpmV6: string[] = []; - fakeBaseInstallManager6.pushConfigurationArgs(argsPnpmV6, options); + fakeBaseInstallManager6.pushConfigurationArgs(argsPnpmV6, options6, rushConfigurationV7.defaultSubspace); expect(argsPnpmV6).not.toContain(pnpmIgnoreCompatibilityDbParameter); expect(mockWrite.mock.calls[0][0]).toContain( "Warning: Your rush.json specifies a pnpmVersion with a known issue that may cause unintended version selections. It's recommended to upgrade to PNPM >=6.34.0 or >=7.9.0. For details see: https://rushjs.io/link/pnpm-issue-5132" ); const argsPnpmV7: string[] = []; - fakeBaseInstallManager7.pushConfigurationArgs(argsPnpmV7, options); + fakeBaseInstallManager7.pushConfigurationArgs(argsPnpmV7, options7, rushConfigurationV7.defaultSubspace); expect(argsPnpmV7).not.toContain(pnpmIgnoreCompatibilityDbParameter); expect(mockWrite.mock.calls[0][0]).toContain( "Warning: Your rush.json specifies a pnpmVersion with a known issue that may cause unintended version selections. It's recommended to upgrade to PNPM >=6.34.0 or >=7.9.0. For details see: https://rushjs.io/link/pnpm-issue-5132" @@ -95,7 +110,9 @@ describe('BaseInstallManager Test', () => { const rushJsonFile: string = path.resolve(__dirname, 'ignoreCompatibilityDb/rush3.json'); const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); const purgeManager: typeof PurgeManager.prototype = new PurgeManager(rushConfiguration, rushGlobalFolder); - const options: IInstallManagerOptions = {} as IInstallManagerOptions; + const options: IInstallManagerOptions = { + subspace: rushConfiguration.defaultSubspace + } as IInstallManagerOptions; const fakeBaseInstallManager: FakeBaseInstallManager = new FakeBaseInstallManager( rushConfiguration, @@ -108,7 +125,7 @@ describe('BaseInstallManager Test', () => { jest.spyOn(ConsoleTerminalProvider.prototype, 'write').mockImplementation(mockWrite); const args: string[] = []; - fakeBaseInstallManager.pushConfigurationArgs(args, options); + fakeBaseInstallManager.pushConfigurationArgs(args, options, rushConfiguration.defaultSubspace); expect(args).toContain(pnpmIgnoreCompatibilityDbParameter); if (mockWrite.mock.calls.length) { diff --git a/libraries/rush-lib/src/logic/test/ChangeFiles.test.ts b/libraries/rush-lib/src/logic/test/ChangeFiles.test.ts index 3d37c8208a6..716615a8990 100644 --- a/libraries/rush-lib/src/logic/test/ChangeFiles.test.ts +++ b/libraries/rush-lib/src/logic/test/ChangeFiles.test.ts @@ -3,109 +3,291 @@ import { Path } from '@rushstack/node-core-library'; -import { IChangelog } from '../../api/Changelog'; +import type { IChangelog } from '../../api/Changelog'; import { ChangeFiles } from '../ChangeFiles'; -import { RushConfiguration } from '../../api/RushConfiguration'; +import type { RushConfiguration } from '../../api/RushConfiguration'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import { VersionPolicyDefinitionName } from '../../api/VersionPolicy'; +import type { ExperimentsConfiguration } from '../../api/ExperimentsConfiguration'; +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; + +const FORWARD_SLASH_DIRNAME: string = Path.convertToSlashes(__dirname); describe(ChangeFiles.name, () => { let rushConfiguration: RushConfiguration; + let terminalProvider: StringBufferTerminalProvider; + let terminal: Terminal; + beforeEach(() => { - rushConfiguration = {} as RushConfiguration; + rushConfiguration = { + experimentsConfiguration: { + configuration: {} + } + } as RushConfiguration; + + terminalProvider = new StringBufferTerminalProvider(); + terminal = new Terminal(terminalProvider); + }); + + afterEach(() => { + expect( + terminalProvider + .getAllOutputAsChunks({ asLines: true }) + .map((chunk) => Path.convertToSlashes(chunk).replace(FORWARD_SLASH_DIRNAME, '')) + ).toMatchSnapshot(); }); - describe(ChangeFiles.prototype.getFilesAsync.name, () => { + describe(ChangeFiles.prototype.getAllChangeFilesAsync.name, () => { + const leafChangeDir: string = `${__dirname}/leafChange`; + const noChangeDir: string = `${__dirname}/noChange`; + const categorizedChangesDir: string = `${__dirname}/categorizedChanges`; + it('returns correctly when there is one change file', async () => { - const changesPath: string = `${__dirname}/leafChange`; - const changeFiles: ChangeFiles = new ChangeFiles(changesPath); - const expectedPath: string = Path.convertToSlashes(`${changesPath}/change1.json`); - expect(await changeFiles.getFilesAsync()).toEqual([expectedPath]); + const changeFiles: ChangeFiles = new ChangeFiles({ + changesFolder: leafChangeDir + } as unknown as RushConfiguration); + const expectedPath: string = Path.convertToSlashes(`${leafChangeDir}/change1.json`); + await expect(changeFiles.getAllChangeFilesAsync()).resolves.toEqual([expectedPath]); }); it('returns empty array when no change files', async () => { - const changesPath: string = `${__dirname}/noChange`; - const changeFiles: ChangeFiles = new ChangeFiles(changesPath); - expect(await changeFiles.getFilesAsync()).toHaveLength(0); + const changeFiles: ChangeFiles = new ChangeFiles({ + changesFolder: noChangeDir + } as unknown as RushConfiguration); + await expect(changeFiles.getAllChangeFilesAsync()).resolves.toHaveLength(0); }); it('returns correctly when change files are categorized', async () => { - const changesPath: string = `${__dirname}/categorizedChanges`; - const changeFiles: ChangeFiles = new ChangeFiles(changesPath); - const files: string[] = await changeFiles.getFilesAsync(); + const changeFiles: ChangeFiles = new ChangeFiles({ + changesFolder: categorizedChangesDir + } as unknown as RushConfiguration); + const files: string[] = await changeFiles.getAllChangeFilesAsync(); expect(files).toHaveLength(3); - const expectedPathA: string = Path.convertToSlashes(`${changesPath}/@ms/a/changeA.json`); - const expectedPathB: string = Path.convertToSlashes(`${changesPath}/@ms/b/changeB.json`); - const expectedPathC: string = Path.convertToSlashes(`${changesPath}/changeC.json`); + const expectedPathA: string = Path.convertToSlashes(`${categorizedChangesDir}/@ms/a/changeA.json`); + const expectedPathB: string = Path.convertToSlashes(`${categorizedChangesDir}/@ms/b/changeB.json`); + const expectedPathC: string = Path.convertToSlashes(`${categorizedChangesDir}/changeC.json`); expect(files).toContain(expectedPathA); expect(files).toContain(expectedPathB); expect(files).toContain(expectedPathC); }); }); - describe(ChangeFiles.validate.name, () => { - it('throws when there is a patch in a hotfix branch.', () => { - const changeFile: string = `${__dirname}/leafChange/change1.json`; + describe(ChangeFiles.prototype.validateAsync.name, () => { + const leafChangeFile: string = `${__dirname}/leafChange/change1.json`; + const hotfixChangeFile: string = `${__dirname}/multipleHotfixChanges/change1.json`; + const verifyChangesFile: string = `${__dirname}/verifyChanges/changes.json`; + const categorizedChangeFileA: string = `${__dirname}/categorizedChanges/@ms/a/changeA.json`; + const categorizedChangeFileB: string = `${__dirname}/categorizedChanges/@ms/b/changeB.json`; + const categorizedChangeFileC: string = `${__dirname}/categorizedChanges/changeC.json`; + + it('throws when there is a patch in a hotfix branch.', async () => { const changedPackages: string[] = ['d']; - expect(() => { - ChangeFiles.validate([changeFile], changedPackages, { - hotfixChangeEnabled: true - } as RushConfiguration); - }).toThrow(Error); + await expect( + new ChangeFiles({ + hotfixChangeEnabled: true, + experimentsConfiguration: { + configuration: {} + } + } as unknown as RushConfiguration).validateAsync({ + terminal, + filesToValidate: [leafChangeFile], + changedProjectNames: changedPackages + }) + ).rejects.toThrow(Error); }); - it('allows a hotfix in a hotfix branch.', () => { - const changeFile: string = `${__dirname}/multipleHotfixChanges/change1.json`; + it('allows a hotfix in a hotfix branch.', async () => { const changedPackages: string[] = ['a']; - ChangeFiles.validate([changeFile], changedPackages, { hotfixChangeEnabled: true } as RushConfiguration); + await new ChangeFiles({ + ...rushConfiguration, + hotfixChangeEnabled: true + } as unknown as RushConfiguration).validateAsync({ + terminal, + filesToValidate: [hotfixChangeFile], + changedProjectNames: changedPackages + }); }); - it('throws when there is any missing package.', () => { - const changeFile: string = `${__dirname}/verifyChanges/changes.json`; + it('throws when there is any missing package.', async () => { const changedPackages: string[] = ['a', 'b', 'c']; - expect(() => { - ChangeFiles.validate([changeFile], changedPackages, rushConfiguration); - }).toThrow(Error); + await expect( + new ChangeFiles(rushConfiguration).validateAsync({ + terminal, + filesToValidate: [verifyChangesFile], + changedProjectNames: changedPackages + }) + ).rejects.toThrow(Error); }); - it('does not throw when there is no missing packages', () => { - const changeFile: string = `${__dirname}/verifyChanges/changes.json`; + it('does not throw when there is no missing packages', async () => { const changedPackages: string[] = ['a']; - expect(() => { - ChangeFiles.validate([changeFile], changedPackages, rushConfiguration); - }).not.toThrow(); + await new ChangeFiles(rushConfiguration).validateAsync({ + terminal, + filesToValidate: [verifyChangesFile], + changedProjectNames: changedPackages + }); }); - it('throws when missing packages from categorized changes', () => { - const changeFileA: string = `${__dirname}/categorizedChanges/@ms/a/changeA.json`; - const changeFileB: string = `${__dirname}/categorizedChanges/@ms/b/changeB.json`; + it('throws when missing packages from categorized changes', async () => { const changedPackages: string[] = ['@ms/a', '@ms/b', 'c']; - expect(() => { - ChangeFiles.validate([changeFileA, changeFileB], changedPackages, rushConfiguration); - }).toThrow(Error); + await expect( + new ChangeFiles(rushConfiguration).validateAsync({ + terminal, + filesToValidate: [categorizedChangeFileA, categorizedChangeFileB], + changedProjectNames: changedPackages + }) + ).rejects.toThrow(Error); }); - it('does not throw when no missing packages from categorized changes', () => { - const changeFileA: string = `${__dirname}/categorizedChanges/@ms/a/changeA.json`; - const changeFileB: string = `${__dirname}/categorizedChanges/@ms/b/changeB.json`; - const changeFileC: string = `${__dirname}/categorizedChanges/changeC.json`; + it('does not throw when no missing packages from categorized changes', async () => { const changedPackages: string[] = ['@ms/a', '@ms/b', 'c']; - expect(() => { - ChangeFiles.validate([changeFileA, changeFileB, changeFileC], changedPackages, rushConfiguration); - }).not.toThrow(Error); + await new ChangeFiles(rushConfiguration).validateAsync({ + terminal, + filesToValidate: [categorizedChangeFileA, categorizedChangeFileB, categorizedChangeFileC], + changedProjectNames: changedPackages + }); + }); + + describe('with strictChangefileValidation', () => { + const nonexistentProjectChangeFile: string = `${__dirname}/strictValidation/nonexistentProject.json`; + const nonMainLockstepChangeFile: string = `${__dirname}/strictValidation/nonMainLockstep.json`; + const mainLockstepChangeFile: string = `${__dirname}/strictValidation/mainLockstep.json`; + + let strictConfig: RushConfiguration; + + function createStrictConfig( + getProjectByName: (name: string) => RushConfigurationProject | undefined + ): RushConfiguration { + return { + experimentsConfiguration: { + configuration: { strictChangefileValidation: true } + } as ExperimentsConfiguration, + getProjectByName + } as unknown as RushConfiguration; + } + + it('throws when change file references a nonexistent project', async () => { + strictConfig = createStrictConfig(() => undefined); + try { + await new ChangeFiles(strictConfig).validateAsync({ + terminal, + filesToValidate: [nonexistentProjectChangeFile], + changedProjectNames: ['nonexistent-package'] + }); + fail('Expected validateAsync to throw'); + } catch (error) { + const normalizedMessage: string = Path.convertToSlashes(error.message).replace( + FORWARD_SLASH_DIRNAME, + '' + ); + expect(normalizedMessage).toMatchSnapshot(); + } + }); + + it('throws when change file references a non-main lockstep project', async () => { + strictConfig = createStrictConfig((name: string) => { + if (name === 'lockstep-secondary') { + return { + packageName: 'lockstep-secondary', + versionPolicy: { + policyName: 'myLockstep', + definitionName: VersionPolicyDefinitionName.lockStepVersion, + mainProject: 'lockstep-main' + } + } as unknown as RushConfigurationProject; + } + return undefined; + }); + try { + await new ChangeFiles(strictConfig).validateAsync({ + terminal, + filesToValidate: [nonMainLockstepChangeFile], + changedProjectNames: ['lockstep-secondary'] + }); + fail('Expected validateAsync to throw'); + } catch (error) { + const normalizedMessage: string = Path.convertToSlashes(error.message).replace( + FORWARD_SLASH_DIRNAME, + '' + ); + expect(normalizedMessage).toMatchSnapshot(); + } + }); + + it('does not throw when change file references the main lockstep project', async () => { + strictConfig = createStrictConfig((name: string) => { + if (name === 'lockstep-main') { + return { + packageName: 'lockstep-main', + versionPolicy: { + policyName: 'myLockstep', + definitionName: VersionPolicyDefinitionName.lockStepVersion, + mainProject: 'lockstep-main' + } + } as unknown as RushConfigurationProject; + } + return undefined; + }); + await new ChangeFiles(strictConfig).validateAsync({ + terminal, + filesToValidate: [mainLockstepChangeFile], + changedProjectNames: ['lockstep-main'] + }); + }); + + it('does not throw when change file references a lockstep project with no mainProject', async () => { + strictConfig = createStrictConfig((name: string) => { + if (name === 'lockstep-main') { + return { + packageName: 'lockstep-main', + versionPolicy: { + policyName: 'myLockstep', + definitionName: VersionPolicyDefinitionName.lockStepVersion, + mainProject: undefined + } + } as unknown as RushConfigurationProject; + } + return undefined; + }); + await new ChangeFiles(strictConfig).validateAsync({ + terminal, + filesToValidate: [mainLockstepChangeFile], + changedProjectNames: ['lockstep-main'] + }); + }); + + it('does not throw when experiment is disabled', async () => { + const config: RushConfiguration = { + experimentsConfiguration: { + configuration: { strictChangefileValidation: false } + } as ExperimentsConfiguration + } as unknown as RushConfiguration; + await new ChangeFiles(config).validateAsync({ + terminal, + filesToValidate: [nonexistentProjectChangeFile], + changedProjectNames: ['nonexistent-package'] + }); + }); }); }); describe(ChangeFiles.prototype.deleteAllAsync.name, () => { + const multipleChangeFilesDir: string = `${__dirname}/multipleChangeFiles`; + const multipleHotfixChangesDir: string = `${__dirname}/multipleHotfixChanges`; + it('delete all files when there are no prerelease packages', async () => { - const changesPath: string = `${__dirname}/multipleChangeFiles`; - const changeFiles: ChangeFiles = new ChangeFiles(changesPath); - expect(await changeFiles.deleteAllAsync(false)).toEqual(3); + const changeFiles: ChangeFiles = new ChangeFiles({ + changesFolder: multipleChangeFilesDir + } as unknown as RushConfiguration); + await expect(changeFiles.deleteAllAsync(terminal, false)).resolves.toEqual(3); }); it('does not delete change files for package whose change logs do not get updated. ', async () => { - const changesPath: string = `${__dirname}/multipleChangeFiles`; - const changeFiles: ChangeFiles = new ChangeFiles(changesPath); + const changeFiles: ChangeFiles = new ChangeFiles({ + changesFolder: multipleChangeFilesDir + } as unknown as RushConfiguration); const updatedChangelogs: IChangelog[] = [ { name: 'a', @@ -116,13 +298,14 @@ describe(ChangeFiles.name, () => { entries: [] } ]; - expect(await changeFiles.deleteAllAsync(false, updatedChangelogs)).toEqual(2); + await expect(changeFiles.deleteAllAsync(terminal, false, updatedChangelogs)).resolves.toEqual(2); }); it('delete all files when there are hotfixes', async () => { - const changesPath: string = `${__dirname}/multipleHotfixChanges`; - const changeFiles: ChangeFiles = new ChangeFiles(changesPath); - expect(await changeFiles.deleteAllAsync(false)).toEqual(3); + const changeFiles: ChangeFiles = new ChangeFiles({ + changesFolder: multipleHotfixChangesDir + } as unknown as RushConfiguration); + await expect(changeFiles.deleteAllAsync(terminal, false)).resolves.toEqual(3); }); }); }); diff --git a/libraries/rush-lib/src/logic/test/ChangeManager.test.ts b/libraries/rush-lib/src/logic/test/ChangeManager.test.ts index 7eb18a10a74..f953c238357 100644 --- a/libraries/rush-lib/src/logic/test/ChangeManager.test.ts +++ b/libraries/rush-lib/src/logic/test/ChangeManager.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { LockStepVersionPolicy } from '../../api/VersionPolicy'; +import type { LockStepVersionPolicy } from '../../api/VersionPolicy'; import { RushConfiguration } from '../../api/RushConfiguration'; import { ChangeManager } from '../ChangeManager'; import { PrereleaseToken } from '../PrereleaseToken'; @@ -18,7 +18,8 @@ describe(ChangeManager.name, () => { /* eslint-disable dot-notation */ it('can apply changes to the package.json files in the dictionary', async () => { - await changeManager.loadAsync(`${__dirname}/multipleChanges`); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/multipleChanges`; + await changeManager.loadAsync(); changeManager.apply(false); expect(changeManager.allPackages.get('a')!.packageJson.version).toEqual('2.0.0'); @@ -33,7 +34,8 @@ describe(ChangeManager.name, () => { }); it('can update explicit version dependency', async () => { - await changeManager.loadAsync(`${__dirname}/explicitVersionChange`); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/explicitVersionChange`; + await changeManager.loadAsync(); changeManager.apply(false); expect(changeManager.allPackages.get('c')!.packageJson.version).toEqual('1.0.1'); @@ -42,7 +44,8 @@ describe(ChangeManager.name, () => { }); it('can update a project using lockStepVersion policy with no nextBump from changefiles', async () => { - await changeManager.loadAsync(`${__dirname}/lockstepWithoutNextBump`); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/lockstepWithoutNextBump`; + await changeManager.loadAsync(); changeManager.apply(false); const policy: LockStepVersionPolicy = rushConfiguration.versionPolicyConfiguration.getVersionPolicy( @@ -55,7 +58,8 @@ describe(ChangeManager.name, () => { }); it('can update explicit cyclic dependency', async () => { - await changeManager.loadAsync(`${__dirname}/cyclicDepsExplicit`); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/cyclicDepsExplicit`; + await changeManager.loadAsync(); changeManager.apply(false); expect(changeManager.allPackages.get('cyclic-dep-explicit-1')!.packageJson.version).toEqual('2.0.0'); @@ -76,7 +80,8 @@ describe(ChangeManager.name, () => { const prereleaseName: string = 'alpha.1'; const prereleaseToken: PrereleaseToken = new PrereleaseToken(prereleaseName); - await changeManager.loadAsync(`${__dirname}/rootPatchChange`, prereleaseToken); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/rootPatchChange`; + await changeManager.loadAsync(prereleaseToken); changeManager.apply(false); expect(changeManager.allPackages.get('a')!.packageJson.version).toEqual('1.0.1-' + prereleaseName); @@ -95,7 +100,8 @@ describe(ChangeManager.name, () => { const prereleaseName: string = 'beta.1'; const prereleaseToken: PrereleaseToken = new PrereleaseToken(prereleaseName); - await changeManager.loadAsync(`${__dirname}/explicitVersionChange`, prereleaseToken); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/explicitVersionChange`; + await changeManager.loadAsync(prereleaseToken); changeManager.apply(false); expect(changeManager.allPackages.get('a')!.packageJson.version).toEqual('1.0.0'); @@ -112,7 +118,8 @@ describe(ChangeManager.name, () => { const prereleaseName: string = 'beta.1'; const prereleaseToken: PrereleaseToken = new PrereleaseToken(prereleaseName); - await changeManager.loadAsync(`${__dirname}/cyclicDeps`, prereleaseToken); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/cyclicDeps`; + await changeManager.loadAsync(prereleaseToken); changeManager.apply(false); expect(changeManager.allPackages.get('cyclic-dep-1')!.packageJson.version).toEqual( @@ -133,7 +140,8 @@ describe(ChangeManager.name, () => { const suffix: string = 'dk.1'; const prereleaseToken: PrereleaseToken = new PrereleaseToken(undefined, suffix); - await changeManager.loadAsync(`${__dirname}/rootPatchChange`, prereleaseToken); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/rootPatchChange`; + await changeManager.loadAsync(prereleaseToken); changeManager.apply(false); expect(changeManager.allPackages.get('a')!.packageJson.version).toEqual('1.0.0-' + suffix); @@ -148,7 +156,8 @@ describe(ChangeManager.name, () => { const suffix: string = 'dk.1'; const prereleaseToken: PrereleaseToken = new PrereleaseToken(undefined, suffix); - await changeManager.loadAsync(`${__dirname}/explicitVersionChange`, prereleaseToken); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/explicitVersionChange`; + await changeManager.loadAsync(prereleaseToken); changeManager.apply(false); expect(changeManager.allPackages.get('a')!.packageJson.version).toEqual('1.0.0'); @@ -163,7 +172,8 @@ describe(ChangeManager.name, () => { const suffix: string = 'dk.1'; const prereleaseToken: PrereleaseToken = new PrereleaseToken(undefined, suffix); - await changeManager.loadAsync(`${__dirname}/cyclicDeps`, prereleaseToken); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/cyclicDeps`; + await changeManager.loadAsync(prereleaseToken); changeManager.apply(false); expect(changeManager.allPackages.get('cyclic-dep-1')!.packageJson.version).toEqual('1.0.0-' + suffix); @@ -190,7 +200,8 @@ describe(`${ChangeManager.name} (workspace)`, () => { /* eslint-disable dot-notation */ it('can apply changes to the package.json files in the dictionary', async () => { - await changeManager.loadAsync(`${__dirname}/multipleChanges`); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/multipleChanges`; + await changeManager.loadAsync(); changeManager.apply(false); expect(changeManager.allPackages.get('a')!.packageJson.version).toEqual('2.0.0'); @@ -213,7 +224,8 @@ describe(`${ChangeManager.name} (workspace)`, () => { }); it('can update explicit version dependency', async () => { - await changeManager.loadAsync(`${__dirname}/explicitVersionChange`); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/explicitVersionChange`; + await changeManager.loadAsync(); changeManager.apply(false); expect(changeManager.allPackages.get('c')!.packageJson.version).toEqual('1.0.1'); @@ -222,7 +234,8 @@ describe(`${ChangeManager.name} (workspace)`, () => { }); it('can update explicit cyclic dependency', async () => { - await changeManager.loadAsync(`${__dirname}/cyclicDepsExplicit`); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/cyclicDepsExplicit`; + await changeManager.loadAsync(); changeManager.apply(false); expect(changeManager.allPackages.get('cyclic-dep-explicit-1')!.packageJson.version).toEqual('2.0.0'); @@ -243,7 +256,8 @@ describe(`${ChangeManager.name} (workspace)`, () => { const prereleaseName: string = 'alpha.1'; const prereleaseToken: PrereleaseToken = new PrereleaseToken(prereleaseName); - await changeManager.loadAsync(`${__dirname}/rootPatchChange`, prereleaseToken); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/rootPatchChange`; + await changeManager.loadAsync(prereleaseToken); changeManager.apply(false); expect(changeManager.allPackages.get('a')!.packageJson.version).toEqual('1.0.1-' + prereleaseName); @@ -262,7 +276,8 @@ describe(`${ChangeManager.name} (workspace)`, () => { const prereleaseName: string = 'beta.1'; const prereleaseToken: PrereleaseToken = new PrereleaseToken(prereleaseName); - await changeManager.loadAsync(`${__dirname}/explicitVersionChange`, prereleaseToken); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/explicitVersionChange`; + await changeManager.loadAsync(prereleaseToken); changeManager.apply(false); expect(changeManager.allPackages.get('a')!.packageJson.version).toEqual('1.0.0'); @@ -281,7 +296,8 @@ describe(`${ChangeManager.name} (workspace)`, () => { const prereleaseName: string = 'beta.1'; const prereleaseToken: PrereleaseToken = new PrereleaseToken(prereleaseName); - await changeManager.loadAsync(`${__dirname}/cyclicDeps`, prereleaseToken); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/cyclicDeps`; + await changeManager.loadAsync(prereleaseToken); changeManager.apply(false); expect(changeManager.allPackages.get('cyclic-dep-1')!.packageJson.version).toEqual( @@ -302,7 +318,8 @@ describe(`${ChangeManager.name} (workspace)`, () => { const suffix: string = 'dk.1'; const prereleaseToken: PrereleaseToken = new PrereleaseToken(undefined, suffix); - await changeManager.loadAsync(`${__dirname}/rootPatchChange`, prereleaseToken); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/rootPatchChange`; + await changeManager.loadAsync(prereleaseToken); changeManager.apply(false); expect(changeManager.allPackages.get('a')!.packageJson.version).toEqual('1.0.0-' + suffix); @@ -321,7 +338,8 @@ describe(`${ChangeManager.name} (workspace)`, () => { const suffix: string = 'dk.1'; const prereleaseToken: PrereleaseToken = new PrereleaseToken(undefined, suffix); - await changeManager.loadAsync(`${__dirname}/explicitVersionChange`, prereleaseToken); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/explicitVersionChange`; + await changeManager.loadAsync(prereleaseToken); changeManager.apply(false); expect(changeManager.allPackages.get('a')!.packageJson.version).toEqual('1.0.0'); @@ -340,7 +358,8 @@ describe(`${ChangeManager.name} (workspace)`, () => { const suffix: string = 'dk.1'; const prereleaseToken: PrereleaseToken = new PrereleaseToken(undefined, suffix); - await changeManager.loadAsync(`${__dirname}/cyclicDeps`, prereleaseToken); + (rushConfiguration as { changesFolder: string }).changesFolder = `${__dirname}/cyclicDeps`; + await changeManager.loadAsync(prereleaseToken); changeManager.apply(false); expect(changeManager.allPackages.get('cyclic-dep-1')!.packageJson.version).toEqual('1.0.0-' + suffix); diff --git a/libraries/rush-lib/src/logic/test/ChangelogGenerator.test.ts b/libraries/rush-lib/src/logic/test/ChangelogGenerator.test.ts index e4e51de48c0..6a300b8578c 100644 --- a/libraries/rush-lib/src/logic/test/ChangelogGenerator.test.ts +++ b/libraries/rush-lib/src/logic/test/ChangelogGenerator.test.ts @@ -1,12 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IChangelog } from '../../api/Changelog'; +import type { IChangelog } from '../../api/Changelog'; import { ChangeType } from '../../api/ChangeManagement'; import { RushConfiguration } from '../../api/RushConfiguration'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; import { ChangelogGenerator } from '../ChangelogGenerator'; -import { IChangeRequests } from '../PublishUtilities'; +import type { IChangeRequests } from '../PublishUtilities'; describe(ChangelogGenerator.updateIndividualChangelog.name, () => { const rushJsonFile: string = `${__dirname}/packages/rush.json`; @@ -347,7 +347,7 @@ describe(ChangelogGenerator.updateIndividualChangelog.name, () => { const emptyObjectFileInvoke = generateUpdateInvoke(`${__dirname}/exampleInvalidChangelog/emptyObject`); expect(emptyObjectFileInvoke).toThrow(Error); - expect(emptyObjectFileInvoke).toThrow(/Missing required property: name/); + expect(emptyObjectFileInvoke).toThrow(/must have required property 'name'/); }); }); @@ -359,7 +359,6 @@ describe(ChangelogGenerator.updateChangelogs.name, () => { rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); }); - /* eslint-disable dot-notation */ it('skips changes logs if the project version is not changed.', () => { const allChanges: IChangeRequests = { packageChanges: new Map(), versionPolicyChanges: new Map() }; // Package a does not have version change. @@ -446,5 +445,4 @@ describe(ChangelogGenerator.updateChangelogs.name, () => { expect(updatedChangeLogs[0].name).toEqual('a'); expect(updatedChangeLogs[1].name).toEqual('b'); }); - /* eslint-enable dot-notation */ }); diff --git a/libraries/rush-lib/src/logic/test/CredentialCache.test.ts b/libraries/rush-lib/src/logic/test/CredentialCache.test.ts deleted file mode 100644 index 86da1f0e3ec..00000000000 --- a/libraries/rush-lib/src/logic/test/CredentialCache.test.ts +++ /dev/null @@ -1,414 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { LockFile, Async, FileSystem } from '@rushstack/node-core-library'; -import { RushUserConfiguration } from '../../api/RushUserConfiguration'; -import { CredentialCache } from '../CredentialCache'; - -const FAKE_RUSH_USER_FOLDER: string = '~/.rush-user'; -const FAKE_CREDENTIALS_CACHE_FILE: string = `${FAKE_RUSH_USER_FOLDER}/credentials.json`; - -describe(CredentialCache.name, () => { - let fakeFilesystem: { [key: string]: string }; - let filesystemLocks: { [key: string]: Promise }; - let unresolvedLockfiles: Set; - - beforeEach(() => { - fakeFilesystem = {}; - filesystemLocks = {}; - unresolvedLockfiles = new Set(); - }); - - beforeEach(() => { - jest.spyOn(RushUserConfiguration, 'getRushUserFolderPath').mockReturnValue(FAKE_RUSH_USER_FOLDER); - - // TODO: Consider expanding these mocks and moving them to node-core-library - jest - .spyOn(LockFile, 'acquire') - .mockImplementation(async (folderPath: string, lockFilePath: string, maxWaitMs?: number) => { - const fullPath: string = `${folderPath}/${lockFilePath}`; - const existingLock: Promise | undefined = filesystemLocks[fullPath]; - if (existingLock) { - if (maxWaitMs === undefined) { - await existingLock; - } else { - await Promise.race([existingLock, Async.sleep(maxWaitMs)]); - } - } - - let release: () => void; - const lockPromise: Promise = new Promise((resolve: () => void) => { - release = resolve; - }); - - // eslint-disable-next-line require-atomic-updates - filesystemLocks[fullPath] = lockPromise; - const result: LockFile = { - release: () => { - release(); - unresolvedLockfiles.delete(result); - } - } as LockFile; - unresolvedLockfiles.add(result); - return result; - }); - - jest - .spyOn(FileSystem, 'writeFileAsync') - .mockImplementation(async (filePath: string, data: Buffer | string) => { - fakeFilesystem[filePath] = data.toString(); - }); - - jest.spyOn(FileSystem, 'readFileAsync').mockImplementation(async (filePath: string) => { - if (filePath in fakeFilesystem) { - return fakeFilesystem[filePath]; - } else { - const notExistError: NodeJS.ErrnoException = new Error( - `ENOENT: no such file or directory, open '${filePath}'` - ); - notExistError.code = 'ENOENT'; - notExistError.errno = -2; - notExistError.syscall = 'open'; - notExistError.path = filePath; - throw notExistError; - } - }); - }); - - afterEach(() => { - for (const lockfile of unresolvedLockfiles) { - lockfile.release(); - } - }); - - afterAll(() => { - jest.restoreAllMocks(); - }); - - it("initializes a credential cache correctly when one doesn't exist on disk", async () => { - const credentialCache: CredentialCache = await CredentialCache.initializeAsync({ supportEditing: false }); - expect(credentialCache).toBeDefined(); - credentialCache.dispose(); - }); - - it('initializes a credential cache correctly when one exists on disk', async () => { - const credentialId: string = 'test-credential'; - const credentialValue: string = 'test-value'; - fakeFilesystem[FAKE_CREDENTIALS_CACHE_FILE] = JSON.stringify({ - version: '0.1.0', - cacheEntries: { - [credentialId]: { - expires: 0, - credential: credentialValue - } - } - }); - - const credentialCache: CredentialCache = await CredentialCache.initializeAsync({ supportEditing: false }); - expect(credentialCache.tryGetCacheEntry(credentialId)?.credential).toEqual(credentialValue); - expect(credentialCache.tryGetCacheEntry(credentialId)?.expires).toBeUndefined(); - credentialCache.dispose(); - }); - - it('initializes a credential cache correctly when one exists on disk with a expired credential', async () => { - const credentialId: string = 'test-credential'; - const credentialValue: string = 'test-value'; - fakeFilesystem[FAKE_CREDENTIALS_CACHE_FILE] = JSON.stringify({ - version: '0.1.0', - cacheEntries: { - [credentialId]: { - expires: 100, // Expired - credential: credentialValue - } - } - }); - - const credentialCache: CredentialCache = await CredentialCache.initializeAsync({ supportEditing: false }); - expect(credentialCache.tryGetCacheEntry(credentialId)?.credential).toEqual(credentialValue); - expect(credentialCache.tryGetCacheEntry(credentialId)?.expires).toMatchInlineSnapshot( - `1970-01-01T00:00:00.100Z` - ); - credentialCache.dispose(); - }); - - it('correctly trims expired credentials', async () => { - const credentialId: string = 'test-credential'; - const credentialValue: string = 'test-value'; - fakeFilesystem[FAKE_CREDENTIALS_CACHE_FILE] = JSON.stringify({ - version: '0.1.0', - cacheEntries: { - [credentialId]: { - expires: 100, // Expired - credential: credentialValue - } - } - }); - - const credentialCache: CredentialCache = await CredentialCache.initializeAsync({ supportEditing: true }); - credentialCache.trimExpiredEntries(); - expect(credentialCache.tryGetCacheEntry(credentialId)).toBeUndefined(); - await credentialCache.saveIfModifiedAsync(); - credentialCache.dispose(); - - expect(fakeFilesystem[FAKE_CREDENTIALS_CACHE_FILE]).toMatchInlineSnapshot(` -"{ - \\"version\\": \\"0.1.0\\", - \\"cacheEntries\\": {} -} -" -`); - }); - - it('correctly adds a new credential', async () => { - const credentialId: string = 'test-credential'; - const credentialValue: string = 'test-value'; - - const credentialCache1: CredentialCache = await CredentialCache.initializeAsync({ supportEditing: true }); - credentialCache1.setCacheEntry(credentialId, { credential: credentialValue }); - expect(credentialCache1.tryGetCacheEntry(credentialId)?.credential).toEqual(credentialValue); - expect(credentialCache1.tryGetCacheEntry(credentialId)?.expires).toBeUndefined(); - await credentialCache1.saveIfModifiedAsync(); - credentialCache1.dispose(); - - expect(fakeFilesystem[FAKE_CREDENTIALS_CACHE_FILE]).toMatchInlineSnapshot(` -"{ - \\"version\\": \\"0.1.0\\", - \\"cacheEntries\\": { - \\"test-credential\\": { - \\"expires\\": 0, - \\"credential\\": \\"test-value\\" - } - } -} -" -`); - - const credentialCache2: CredentialCache = await CredentialCache.initializeAsync({ - supportEditing: false - }); - expect(credentialCache2.tryGetCacheEntry(credentialId)?.credential).toEqual(credentialValue); - expect(credentialCache2.tryGetCacheEntry(credentialId)?.expires).toBeUndefined(); - credentialCache2.dispose(); - }); - - it('correctly updates an existing credential', async () => { - const credentialId: string = 'test-credential'; - const credentialValue: string = 'test-value'; - const newCredentialValue: string = 'new-test-value'; - fakeFilesystem[FAKE_CREDENTIALS_CACHE_FILE] = JSON.stringify({ - version: '0.1.0', - cacheEntries: { - [credentialId]: { - expires: 0, - credential: credentialValue - } - } - }); - - const credentialCache1: CredentialCache = await CredentialCache.initializeAsync({ supportEditing: true }); - credentialCache1.setCacheEntry(credentialId, { credential: newCredentialValue }); - expect(credentialCache1.tryGetCacheEntry(credentialId)?.credential).toEqual(newCredentialValue); - expect(credentialCache1.tryGetCacheEntry(credentialId)?.expires).toBeUndefined(); - await credentialCache1.saveIfModifiedAsync(); - credentialCache1.dispose(); - - expect(fakeFilesystem[FAKE_CREDENTIALS_CACHE_FILE]).toMatchInlineSnapshot(` -"{ - \\"version\\": \\"0.1.0\\", - \\"cacheEntries\\": { - \\"test-credential\\": { - \\"expires\\": 0, - \\"credential\\": \\"new-test-value\\" - } - } -} -" -`); - - const credentialCache2: CredentialCache = await CredentialCache.initializeAsync({ - supportEditing: false - }); - expect(credentialCache2.tryGetCacheEntry(credentialId)?.credential).toEqual(newCredentialValue); - expect(credentialCache2.tryGetCacheEntry(credentialId)?.expires).toBeUndefined(); - credentialCache2.dispose(); - }); - - it('correctly deletes an existing credential', async () => { - const credentialId: string = 'test-credential'; - fakeFilesystem[FAKE_CREDENTIALS_CACHE_FILE] = JSON.stringify({ - version: '0.1.0', - cacheEntries: { - [credentialId]: { - expires: 0, - credential: 'test-value' - } - } - }); - - const credentialCache1: CredentialCache = await CredentialCache.initializeAsync({ supportEditing: true }); - credentialCache1.deleteCacheEntry(credentialId); - expect(credentialCache1.tryGetCacheEntry(credentialId)).toBeUndefined(); - await credentialCache1.saveIfModifiedAsync(); - credentialCache1.dispose(); - - expect(fakeFilesystem[FAKE_CREDENTIALS_CACHE_FILE]).toMatchInlineSnapshot(` -"{ - \\"version\\": \\"0.1.0\\", - \\"cacheEntries\\": {} -} -" -`); - - const credentialCache2: CredentialCache = await CredentialCache.initializeAsync({ - supportEditing: false - }); - expect(credentialCache2.tryGetCacheEntry(credentialId)).toBeUndefined(); - credentialCache2.dispose(); - }); - - it('does not allow interaction if already disposed', async () => { - const credentialCache: CredentialCache = await CredentialCache.initializeAsync({ supportEditing: true }); - credentialCache.dispose(); - - expect(() => credentialCache.deleteCacheEntry('test')).toThrowErrorMatchingInlineSnapshot( - `"This instance of CredentialCache has been disposed."` - ); - await expect(() => credentialCache.saveIfModifiedAsync()).rejects.toThrowErrorMatchingInlineSnapshot( - `"This instance of CredentialCache has been disposed."` - ); - expect(() => - credentialCache.setCacheEntry('test', { credential: 'test' }) - ).toThrowErrorMatchingInlineSnapshot(`"This instance of CredentialCache has been disposed."`); - expect(() => credentialCache.trimExpiredEntries()).toThrowErrorMatchingInlineSnapshot( - `"This instance of CredentialCache has been disposed."` - ); - expect(() => credentialCache.tryGetCacheEntry('test')).toThrowErrorMatchingInlineSnapshot( - `"This instance of CredentialCache has been disposed."` - ); - }); - - it("does not allow modification if initialized with 'supportEditing': false", async () => { - const credentialCache: CredentialCache = await CredentialCache.initializeAsync({ supportEditing: false }); - - expect(() => credentialCache.deleteCacheEntry('test')).toThrowErrorMatchingInlineSnapshot( - `"This instance of CredentialCache does not support editing."` - ); - await expect(() => credentialCache.saveIfModifiedAsync()).rejects.toThrowErrorMatchingInlineSnapshot( - `"This instance of CredentialCache does not support editing."` - ); - expect(() => - credentialCache.setCacheEntry('test', { credential: 'test' }) - ).toThrowErrorMatchingInlineSnapshot(`"This instance of CredentialCache does not support editing."`); - expect(() => credentialCache.trimExpiredEntries()).toThrowErrorMatchingInlineSnapshot( - `"This instance of CredentialCache does not support editing."` - ); - }); - - it('correctly sets credentialMetadata', async () => { - const credentialId: string = 'test-credential'; - const credentialValue: string = 'test-value'; - const credentialMetadata: object = { - a: 1, - b: true - }; - - const credentialCache1: CredentialCache = await CredentialCache.initializeAsync({ supportEditing: true }); - credentialCache1.setCacheEntry(credentialId, { credential: credentialValue, credentialMetadata }); - expect(credentialCache1.tryGetCacheEntry(credentialId)).toEqual({ - credential: credentialValue, - credentialMetadata - }); - await credentialCache1.saveIfModifiedAsync(); - credentialCache1.dispose(); - - expect(fakeFilesystem[FAKE_CREDENTIALS_CACHE_FILE]).toMatchInlineSnapshot(` -"{ - \\"version\\": \\"0.1.0\\", - \\"cacheEntries\\": { - \\"test-credential\\": { - \\"expires\\": 0, - \\"credential\\": \\"test-value\\", - \\"credentialMetadata\\": { - \\"a\\": 1, - \\"b\\": true - } - } - } -} -" -`); - - const credentialCache2: CredentialCache = await CredentialCache.initializeAsync({ - supportEditing: false - }); - expect(credentialCache2.tryGetCacheEntry(credentialId)).toEqual({ - credential: credentialValue, - credentialMetadata - }); - credentialCache2.dispose(); - }); - - it('correctly updates credentialMetadata', async () => { - const credentialId: string = 'test-credential'; - const credentialValue: string = 'test-value'; - const oldCredentialMetadata: object = { - a: 1, - b: true - }; - const newCredentialMetadata: object = { - c: ['a', 'b', 'c'] - }; - - fakeFilesystem[FAKE_CREDENTIALS_CACHE_FILE] = JSON.stringify({ - version: '0.1.0', - cacheEntries: { - [credentialId]: { - expires: 0, - credential: 'test-value', - credentialMetadata: oldCredentialMetadata - } - } - }); - - const credentialCache1: CredentialCache = await CredentialCache.initializeAsync({ supportEditing: true }); - credentialCache1.setCacheEntry(credentialId, { - credential: credentialValue, - credentialMetadata: newCredentialMetadata - }); - expect(credentialCache1.tryGetCacheEntry(credentialId)).toEqual({ - credential: credentialValue, - credentialMetadata: newCredentialMetadata - }); - await credentialCache1.saveIfModifiedAsync(); - credentialCache1.dispose(); - - expect(fakeFilesystem[FAKE_CREDENTIALS_CACHE_FILE]).toMatchInlineSnapshot(` -"{ - \\"version\\": \\"0.1.0\\", - \\"cacheEntries\\": { - \\"test-credential\\": { - \\"expires\\": 0, - \\"credential\\": \\"test-value\\", - \\"credentialMetadata\\": { - \\"c\\": [ - \\"a\\", - \\"b\\", - \\"c\\" - ] - } - } - } -} -" -`); - - const credentialCache2: CredentialCache = await CredentialCache.initializeAsync({ - supportEditing: false - }); - expect(credentialCache2.tryGetCacheEntry(credentialId)).toEqual({ - credential: credentialValue, - credentialMetadata: newCredentialMetadata - }); - credentialCache2.dispose(); - }); -}); diff --git a/libraries/rush-lib/src/logic/test/DependencyAnalyzer.test.ts b/libraries/rush-lib/src/logic/test/DependencyAnalyzer.test.ts index ead3f44da7c..04f192fef8a 100644 --- a/libraries/rush-lib/src/logic/test/DependencyAnalyzer.test.ts +++ b/libraries/rush-lib/src/logic/test/DependencyAnalyzer.test.ts @@ -2,7 +2,7 @@ // See LICENSE in the project root for license information. import { RushConfiguration } from '../../api/RushConfiguration'; -import { DependencyAnalyzer, IDependencyAnalysis } from '../DependencyAnalyzer'; +import { DependencyAnalyzer, type IDependencyAnalysis } from '../DependencyAnalyzer'; describe(DependencyAnalyzer.name, () => { function getAnalysisForRepoByName(repoName: string): IDependencyAnalysis { @@ -10,7 +10,7 @@ describe(DependencyAnalyzer.name, () => { `${__dirname}/DependencyAnalyzerTestRepos/${repoName}/rush.json` ); const dependencyAnalyzer: DependencyAnalyzer = DependencyAnalyzer.forRushConfiguration(rushConfiguration); - const analysis: IDependencyAnalysis = dependencyAnalyzer.getAnalysis(); + const analysis: IDependencyAnalysis = dependencyAnalyzer.getAnalysis(undefined, undefined, false); return analysis; } diff --git a/libraries/rush-lib/src/logic/test/DependencySpecifier.test.ts b/libraries/rush-lib/src/logic/test/DependencySpecifier.test.ts new file mode 100644 index 00000000000..9ec30a66024 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/DependencySpecifier.test.ts @@ -0,0 +1,187 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { DependencySpecifier } from '../DependencySpecifier'; + +describe(DependencySpecifier.name, () => { + afterEach(() => { + DependencySpecifier.clearCache(); + }); + + it('parses a simple version', () => { + const specifier = new DependencySpecifier('dep', '1.2.3'); + expect(specifier).toMatchInlineSnapshot(` +DependencySpecifier { + "aliasTarget": undefined, + "packageName": "dep", + "specifierType": "Version", + "versionSpecifier": "1.2.3", +} +`); + }); + + it('parses a range version', () => { + const specifier = new DependencySpecifier('dep', '^1.2.3'); + expect(specifier).toMatchInlineSnapshot(` +DependencySpecifier { + "aliasTarget": undefined, + "packageName": "dep", + "specifierType": "Range", + "versionSpecifier": "^1.2.3", +} +`); + }); + + it('parses an alias version', () => { + const specifier = new DependencySpecifier('dep', 'npm:alias-target@1.2.3'); + expect(specifier).toMatchInlineSnapshot(` +DependencySpecifier { + "aliasTarget": DependencySpecifier { + "aliasTarget": undefined, + "packageName": "alias-target", + "specifierType": "Version", + "versionSpecifier": "1.2.3", + }, + "packageName": "dep", + "specifierType": "Alias", + "versionSpecifier": "npm:alias-target@1.2.3", +} +`); + }); + + it('parses a git version', () => { + const specifier = new DependencySpecifier('dep', 'git+https://github.com/user/foo'); + expect(specifier).toMatchInlineSnapshot(` +DependencySpecifier { + "aliasTarget": undefined, + "packageName": "dep", + "specifierType": "Git", + "versionSpecifier": "git+https://github.com/user/foo", +} +`); + }); + + it('parses a file version', () => { + const specifier = new DependencySpecifier('dep', 'file:foo.tar.gz'); + expect(specifier).toMatchInlineSnapshot(` +DependencySpecifier { + "aliasTarget": undefined, + "packageName": "dep", + "specifierType": "File", + "versionSpecifier": "file:foo.tar.gz", +} +`); + }); + + it('parses a directory version', () => { + const specifier = new DependencySpecifier('dep', 'file:../foo/bar/'); + expect(specifier).toMatchInlineSnapshot(` +DependencySpecifier { + "aliasTarget": undefined, + "packageName": "dep", + "specifierType": "Directory", + "versionSpecifier": "file:../foo/bar/", +} +`); + }); + + it('parses a remote version', () => { + const specifier = new DependencySpecifier('dep', 'https://example.com/foo.tgz'); + expect(specifier).toMatchInlineSnapshot(` +DependencySpecifier { + "aliasTarget": undefined, + "packageName": "dep", + "specifierType": "Remote", + "versionSpecifier": "https://example.com/foo.tgz", +} +`); + }); + + describe('Workspace protocol', () => { + it('correctly parses a "workspace:*" version', () => { + const specifier = new DependencySpecifier('dep', 'workspace:*'); + expect(specifier).toMatchInlineSnapshot(` +DependencySpecifier { + "aliasTarget": undefined, + "packageName": "dep", + "specifierType": "Workspace", + "versionSpecifier": "*", +} +`); + }); + + it('correctly parses a "workspace:^1.0.0" version', () => { + const specifier = new DependencySpecifier('dep', 'workspace:^1.0.0'); + expect(specifier).toMatchInlineSnapshot(` +DependencySpecifier { + "aliasTarget": undefined, + "packageName": "dep", + "specifierType": "Workspace", + "versionSpecifier": "^1.0.0", +} +`); + }); + + it('correctly parses a "workspace:alias@1.2.3" version', () => { + const specifier = new DependencySpecifier('dep', 'workspace:alias-target@*'); + expect(specifier).toMatchInlineSnapshot(` +DependencySpecifier { + "aliasTarget": DependencySpecifier { + "aliasTarget": undefined, + "packageName": "alias-target", + "specifierType": "Range", + "versionSpecifier": "*", + }, + "packageName": "dep", + "specifierType": "Workspace", + "versionSpecifier": "alias-target@*", +} +`); + }); + }); + + describe('Catalog protocol', () => { + it('correctly parses a "catalog:" version (default catalog)', () => { + const specifier = new DependencySpecifier('dep', 'catalog:'); + expect(specifier).toMatchInlineSnapshot(` +DependencySpecifier { + "aliasTarget": undefined, + "packageName": "dep", + "specifierType": "Catalog", + "versionSpecifier": "", +} +`); + }); + + it('correctly parses a "catalog:catalogName" version (named catalog)', () => { + const specifier = new DependencySpecifier('dep', 'catalog:react18'); + expect(specifier).toMatchInlineSnapshot(` +DependencySpecifier { + "aliasTarget": undefined, + "packageName": "dep", + "specifierType": "Catalog", + "versionSpecifier": "react18", +} +`); + }); + }); + + describe(DependencySpecifier.parseWithCache.name, () => { + it('returns a cached instance for the same input', () => { + const specifier1 = DependencySpecifier.parseWithCache('dep', '1.2.3'); + const specifier2 = DependencySpecifier.parseWithCache('dep', '1.2.3'); + expect(specifier1).toBe(specifier2); + }); + it('returns a cached instance for the same alias', () => { + const specifier1 = DependencySpecifier.parseWithCache('dep1', 'npm:dep@1.2.3'); + const specifier2 = DependencySpecifier.parseWithCache('dep2', 'npm:dep@1.2.3'); + expect(specifier1.aliasTarget).toBe(specifier2.aliasTarget); + }); + + it('returns different instances for different inputs', () => { + const specifier1 = DependencySpecifier.parseWithCache('dep', '1.2.3'); + const specifier2 = DependencySpecifier.parseWithCache('dep', '1.2.4'); + expect(specifier1).not.toBe(specifier2); + }); + }); +}); diff --git a/libraries/rush-lib/src/logic/test/Git.test.ts b/libraries/rush-lib/src/logic/test/Git.test.ts index 0a08a25ab0d..54d4c7d0035 100644 --- a/libraries/rush-lib/src/logic/test/Git.test.ts +++ b/libraries/rush-lib/src/logic/test/Git.test.ts @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { RushConfiguration } from '../../api/RushConfiguration'; +import type { RushConfiguration } from '../../api/RushConfiguration'; import { Git } from '../Git'; -import { IGitStatusEntry } from '../GitStatusParser'; +import type { IGitStatusEntry } from '../GitStatusParser'; describe(Git.name, () => { describe(Git.normalizeGitUrlForComparison.name, () => { @@ -19,7 +19,7 @@ describe(Git.name, () => { 'https://host.xz/path/to/repo' ); expect(Git.normalizeGitUrlForComparison('http://host.xz:80/path/to/repo')).toEqual( - 'https://host.xz:80/path/to/repo' + 'https://host.xz/path/to/repo' ); expect(Git.normalizeGitUrlForComparison('host.xz:path/to/repo.git/')).toEqual( 'https://host.xz/path/to/repo' @@ -35,24 +35,26 @@ describe(Git.name, () => { }); }); - describe(Git.prototype.getGitStatus.name, () => { - function getGitStatusEntriesForCommandOutput(outputSections: string[]): IGitStatusEntry[] { + describe(Git.prototype.getGitStatusAsync.name, () => { + async function getGitStatusEntriesForCommandOutputAsync( + outputSections: string[] + ): Promise { const gitInstance: Git = new Git({ rushJsonFolder: '/repo/root' } as RushConfiguration); jest.spyOn(gitInstance, 'getGitPathOrThrow').mockReturnValue('/git/bin/path'); jest - .spyOn(gitInstance, '_executeGitCommandAndCaptureOutput') - .mockImplementation((gitPath: string, args: string[]) => { + .spyOn(gitInstance, '_executeGitCommandAndCaptureOutputAsync') + .mockImplementation(async (gitPath: string, args: string[]) => { expect(gitPath).toEqual('/git/bin/path'); expect(args).toEqual(['status', '--porcelain=2', '--null', '--ignored=no']); return outputSections.join('\0'); }); - return Array.from(gitInstance.getGitStatus()); + return Array.from(await gitInstance.getGitStatusAsync()); } - it('parses a git status', () => { - expect( - getGitStatusEntriesForCommandOutput([ + it('parses a git status', async () => { + await expect( + getGitStatusEntriesForCommandOutputAsync([ // Staged add '1 A. N... 000000 100644 100644 0000000000000000000000000000000000000000 a171a25d2c978ba071959f39dbeaa339fe84f768 path/a.ts', // Modifications, some staged and some unstaged @@ -73,7 +75,7 @@ describe(Git.name, () => { '1 AM N... 000000 100644 100644 0000000000000000000000000000000000000000 9d9ab4adc79c591c0aa72f7fd29a008c80893e3e path/h.ts', '' ]) - ).toMatchInlineSnapshot(` + ).resolves.toMatchInlineSnapshot(` Array [ Object { "headFileMode": "000000", @@ -183,10 +185,79 @@ describe(Git.name, () => { `); }); - it('throws with invalid git output', () => { - expect(() => - getGitStatusEntriesForCommandOutput(['1 A. N... 000000 100644 100644 000000000000000000']) - ).toThrowErrorMatchingInlineSnapshot(`"Unexpected end of git status output after position 31"`); + it('throws with invalid git output', async () => { + await expect(() => + getGitStatusEntriesForCommandOutputAsync(['1 A. N... 000000 100644 100644 000000000000000000']) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Unexpected end of git status output after position 31"`); + }); + }); + + describe(Git.prototype.determineIfRefIsACommitAsync.name, () => { + const commit = `d9bc1881959b9e44d846655521cd055fcf713f4d`; + async function getMockedGitIsRefACommitAsync(ref: string): Promise { + const gitInstance: Git = new Git({ rushJsonFolder: '/repo/root' } as RushConfiguration); + jest.spyOn(gitInstance, 'getGitPathOrThrow').mockReturnValue('/git/bin/path'); + jest + .spyOn(gitInstance, '_executeGitCommandAndCaptureOutputAsync') + .mockImplementation(async (gitPath: string, args: string[]) => { + expect(gitPath).toEqual('/git/bin/path'); + expect(args).toEqual(['rev-parse', '--verify', ref]); + return commit; + }); + return await gitInstance.determineIfRefIsACommitAsync(ref); + } + + it('Returns true for commit ref', async () => { + await expect(getMockedGitIsRefACommitAsync(commit)).resolves.toBe(true); + }); + + it('Returns false for branch ref', async () => { + await expect(getMockedGitIsRefACommitAsync('kenrick/skip-merge-base')).resolves.toBe(false); + }); + + it('Returns false for ref that is a tag', async () => { + await expect(getMockedGitIsRefACommitAsync('testing-tag-v1.2.3')).resolves.toBe(false); + }); + + it('Returns false for ref that is other string', async () => { + await expect(getMockedGitIsRefACommitAsync('HEAD')).resolves.toBe(false); + }); + }); + + describe(Git.prototype.tryGetGitEmailAsync.name, () => { + async function getMockGitEmail(hasGitPath: boolean, output: string | Error): Promise { + const gitInstance: Git = new Git({ rushJsonFolder: '/repo/root' } as RushConfiguration); + jest.spyOn(gitInstance, 'gitPath', 'get').mockImplementation(() => { + if (hasGitPath) return '/git/bin/path'; + else return undefined; + }); + + jest + .spyOn(gitInstance, '_executeGitCommandAndCaptureOutputAsync') + .mockImplementation(async (gitPath: string, args: string[]) => { + expect(gitPath).toEqual('/git/bin/path'); + expect(args).toEqual(['config', 'user.email']); + if (typeof output === 'string') return output; + else throw output; + }); + + return await gitInstance.tryGetGitEmailAsync(); + } + + it('Throw exception when cannot find git path', async () => { + await expect(getMockGitEmail(false, 'user@example.com')).rejects.toBeInstanceOf(Error); + }); + + it('Returns result when git user.email has been found', async () => { + await expect(getMockGitEmail(true, 'user@example.com')).resolves.toEqual('user@example.com'); + }); + + it('Returns empty email when git user.email return empty string', async () => { + await expect(getMockGitEmail(true, '')).resolves.toEqual(''); + }); + + it('Returns undefined when git user.email not configure', async () => { + await expect(getMockGitEmail(true, new Error('Email is missing'))).resolves.toEqual(undefined); }); }); }); diff --git a/libraries/rush-lib/src/logic/test/InstallHelpers.test.ts b/libraries/rush-lib/src/logic/test/InstallHelpers.test.ts index 4bf32dbba6d..cfc2319219a 100644 --- a/libraries/rush-lib/src/logic/test/InstallHelpers.test.ts +++ b/libraries/rush-lib/src/logic/test/InstallHelpers.test.ts @@ -1,27 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { type IPackageJson, JsonFile } from '@rushstack/node-core-library'; +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; +import { TestUtilities } from '@rushstack/heft-config-file'; + import { InstallHelpers } from '../installManager/InstallHelpers'; import { RushConfiguration } from '../../api/RushConfiguration'; -import { IPackageJson, JsonFile } from '@rushstack/node-core-library'; +import type { PnpmWorkspaceFile } from '../pnpm/PnpmWorkspaceFile'; + +describe(InstallHelpers.name, () => { + describe(InstallHelpers.generateCommonPackageJsonAsync.name, () => { + let mockJsonFileSaveAsync: jest.SpyInstance; + let terminal: Terminal; + let terminalProvider: StringBufferTerminalProvider; -describe('InstallHelpers', () => { - describe('generateCommonPackageJson', () => { - const originalJsonFileSave = JsonFile.save; - const mockJsonFileSave: jest.Mock = jest.fn(); beforeAll(() => { - JsonFile.save = mockJsonFileSave; + mockJsonFileSaveAsync = jest.spyOn(JsonFile, 'saveAsync').mockImplementation(async () => true); }); - afterEach(() => { - mockJsonFileSave.mockClear(); + + beforeEach(() => { + terminalProvider = new StringBufferTerminalProvider(); + terminal = new Terminal(terminalProvider); }); - afterAll(() => { - JsonFile.save = originalJsonFileSave; + + afterEach(() => { + expect( + terminalProvider.getAllOutputAsChunks({ + normalizeSpecialCharacters: true, + asLines: true + }) + ).toMatchSnapshot('Terminal Output'); + mockJsonFileSaveAsync.mockClear(); }); - it('generates correct package json with pnpm configurations', () => { + it('generates correct package json with pnpm configurations', async () => { const RUSH_JSON_FILENAME: string = `${__dirname}/pnpmConfig/rush.json`; const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile(RUSH_JSON_FILENAME); - InstallHelpers.generateCommonPackageJson(rushConfiguration); - const packageJson: IPackageJson = mockJsonFileSave.mock.calls[0][0]; + const pnpmSettings = InstallHelpers.resolvePnpmSettings( + rushConfiguration, + rushConfiguration.defaultSubspace, + terminal + ); + await InstallHelpers.generateCommonPackageJsonAsync( + rushConfiguration.defaultSubspace, + undefined, + pnpmSettings + ); + const packageJson: IPackageJson = JSON.parse( + JsonFile.stringify(mockJsonFileSaveAsync.mock.calls[0][0], { ignoreUndefinedValues: true }) + ); expect(packageJson).toEqual( expect.objectContaining({ pnpm: { @@ -31,6 +60,7 @@ describe('InstallHelpers', () => { 'bar@^2.1.0': '3.0.0', 'qar@1>zoo': '2' }, + // For pnpm < 11 all of these settings are still written into the package.json "pnpm" field. packageExtensions: { 'react-redux': { peerDependencies: { @@ -38,11 +68,96 @@ describe('InstallHelpers', () => { } } }, + peerDependencyRules: { + allowedVersions: { + react: '18' + }, + ignoreMissing: ['@babel/core'] + }, + allowedDeprecatedVersions: { + request: '*' + }, + patchedDependencies: { + 'lodash@4.17.21': 'patches/lodash@4.17.21.patch' + }, neverBuiltDependencies: ['fsevents', 'level'], + onlyBuiltDependencies: ['esbuild', 'playwright'], pnpmFutureFeature: true } }) ); + expect(packageJson).toMatchSnapshot(); + }); + + it('does not generate a "pnpm" field for pnpm 11 (all settings belong in pnpm-workspace.yaml)', async () => { + const RUSH_JSON_FILENAME: string = `${__dirname}/pnpmConfigPnpm11/rush.json`; + const rushConfiguration: RushConfiguration = + RushConfiguration.loadFromConfigurationFile(RUSH_JSON_FILENAME); + const pnpmSettings = InstallHelpers.resolvePnpmSettings( + rushConfiguration, + rushConfiguration.defaultSubspace, + terminal + ); + await InstallHelpers.generateCommonPackageJsonAsync( + rushConfiguration.defaultSubspace, + undefined, + pnpmSettings + ); + const packageJson: IPackageJson = JSON.parse( + JsonFile.stringify(mockJsonFileSaveAsync.mock.calls[0][0], { ignoreUndefinedValues: true }) + ); + // For pnpm >= 11 the "pnpm" field is not generated at all; every setting is written to + // common/temp/pnpm-workspace.yaml instead. + expect(packageJson).not.toHaveProperty('pnpm'); + + // ...and the relocated settings are instead placed on the generated pnpm-workspace.yaml file. + const workspaceFile: PnpmWorkspaceFile | undefined = + TestUtilities.stripAnnotations(pnpmSettings)?.workspaceFile; + expect(workspaceFile?.ignoredOptionalDependencies).toEqual(['fsevents']); + expect(workspaceFile?.trustPolicy).toEqual('no-downgrade'); + expect(workspaceFile?.trustPolicyExclude).toEqual(['chokidar@4.0.3']); + expect(workspaceFile?.trustPolicyIgnoreAfter).toEqual(1440); + + // The subspaces feature is not enabled in this repo, so no global pnpmfile is emitted. + expect(workspaceFile?.globalPnpmfile).toBeUndefined(); + }); + + it('emits the subspace global pnpmfile path via pnpm-workspace.yaml for pnpm 11', async () => { + const RUSH_JSON_FILENAME: string = `${__dirname}/pnpmConfigPnpm11Subspaces/rush.json`; + const rushConfiguration: RushConfiguration = + RushConfiguration.loadFromConfigurationFile(RUSH_JSON_FILENAME); + const pnpmSettings = InstallHelpers.resolvePnpmSettings( + rushConfiguration, + rushConfiguration.defaultSubspace, + terminal + ); + + // pnpm 11+ only reads auth/registry settings from .npmrc, so the "global-pnpmfile=" line in + // the generated .npmrc is ignored; the path must be emitted via pnpm-workspace.yaml instead, + // otherwise cross-subspace "workspace:*" dependencies fail with + // ERR_PNPM_WORKSPACE_PKG_NOT_FOUND. + const workspaceFile: PnpmWorkspaceFile | undefined = + TestUtilities.stripAnnotations(pnpmSettings)?.workspaceFile; + expect(workspaceFile?.globalPnpmfile).toEqual( + `${rushConfiguration.defaultSubspace.getSubspaceTempFolderPath()}/global-pnpmfile.cjs` + ); + }); + + it('does not emit the global pnpmfile via pnpm-workspace.yaml for pnpm < 11', async () => { + const RUSH_JSON_FILENAME: string = `${__dirname}/repoWithSubspaces/rush.json`; + const rushConfiguration: RushConfiguration = + RushConfiguration.loadFromConfigurationFile(RUSH_JSON_FILENAME); + const pnpmSettings = InstallHelpers.resolvePnpmSettings( + rushConfiguration, + rushConfiguration.defaultSubspace, + terminal + ); + + // For pnpm 10 and earlier the global pnpmfile stays wired up via the generated .npmrc + // (see BaseInstallManager); the workspace file must not carry it. + const workspaceFile: PnpmWorkspaceFile | undefined = + TestUtilities.stripAnnotations(pnpmSettings)?.workspaceFile; + expect(workspaceFile?.globalPnpmfile).toBeUndefined(); }); }); }); diff --git a/libraries/rush-lib/src/logic/test/LookupByPath.test.ts b/libraries/rush-lib/src/logic/test/LookupByPath.test.ts deleted file mode 100644 index 2d56d082d58..00000000000 --- a/libraries/rush-lib/src/logic/test/LookupByPath.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import { LookupByPath } from '../LookupByPath'; - -describe(LookupByPath.iteratePathSegments.name, () => { - it('returns empty for an empty string', () => { - const result = [...LookupByPath.iteratePathSegments('')]; - expect(result.length).toEqual(0); - }); - it('returns the only segment of a trival string', () => { - const result = [...LookupByPath.iteratePathSegments('foo')]; - expect(result).toEqual(['foo']); - }); - it('treats backslashes as ordinary characters, per POSIX', () => { - const result = [...LookupByPath.iteratePathSegments('foo\\bar\\baz')]; - expect(result).toEqual(['foo\\bar\\baz']); - }); - it('iterates segments', () => { - const result = [...LookupByPath.iteratePathSegments('foo/bar/baz')]; - expect(result).toEqual(['foo', 'bar', 'baz']); - }); - it('returns correct last single character segment', () => { - const result = [...LookupByPath.iteratePathSegments('foo/a')]; - expect(result).toEqual(['foo', 'a']); - }); -}); - -describe(LookupByPath.prototype.findChildPath.name, () => { - it('returns empty for an empty tree', () => { - expect(new LookupByPath().findChildPath('foo')).toEqual(undefined); - }); - it('returns the matching node for a trivial tree', () => { - expect(new LookupByPath([['foo', 1]]).findChildPath('foo')).toEqual(1); - }); - it('returns the matching node for a single-layer tree', () => { - const tree: LookupByPath = new LookupByPath([ - ['foo', 1], - ['bar', 2], - ['baz', 3] - ]); - - expect(tree.findChildPath('foo')).toEqual(1); - expect(tree.findChildPath('bar')).toEqual(2); - expect(tree.findChildPath('baz')).toEqual(3); - expect(tree.findChildPath('buzz')).toEqual(undefined); - }); - it('returns the matching parent for multi-layer queries', () => { - const tree: LookupByPath = new LookupByPath([ - ['foo', 1], - ['bar', 2], - ['baz', 3] - ]); - - expect(tree.findChildPath('foo/bar')).toEqual(1); - expect(tree.findChildPath('bar/baz')).toEqual(2); - expect(tree.findChildPath('baz/foo')).toEqual(3); - expect(tree.findChildPath('foo/foo')).toEqual(1); - }); - it('returns the matching parent for multi-layer queries in multi-layer trees', () => { - const tree: LookupByPath = new LookupByPath([ - ['foo', 1], - ['bar', 2], - ['baz', 3], - ['foo/bar', 4], - ['foo/bar/baz', 5], - ['baz/foo', 6], - ['baz/baz/baz/baz', 7] - ]); - - expect(tree.findChildPath('foo/foo')).toEqual(1); - expect(tree.findChildPath('foo/bar\\baz')).toEqual(1); - - expect(tree.findChildPath('bar/baz')).toEqual(2); - - expect(tree.findChildPath('baz/bar')).toEqual(3); - expect(tree.findChildPath('baz/baz')).toEqual(3); - expect(tree.findChildPath('baz/baz/baz')).toEqual(3); - - expect(tree.findChildPath('foo/bar')).toEqual(4); - expect(tree.findChildPath('foo/bar/foo')).toEqual(4); - - expect(tree.findChildPath('foo/bar/baz')).toEqual(5); - expect(tree.findChildPath('foo/bar/baz/baz/baz/baz/baz')).toEqual(5); - - expect(tree.findChildPath('baz/foo/')).toEqual(6); - - expect(tree.findChildPath('baz/baz/baz/baz')).toEqual(7); - - expect(tree.findChildPath('')).toEqual(undefined); - expect(tree.findChildPath('foofoo')).toEqual(undefined); - expect(tree.findChildPath('foo\\bar\\baz')).toEqual(undefined); - }); - it('handles custom delimiters', () => { - const tree: LookupByPath = new LookupByPath( - [ - ['foo,bar', 1], - ['foo/bar', 2] - ], - ',' - ); - - expect(tree.findChildPath('foo/bar,baz')).toEqual(2); - expect(tree.findChildPath('foo,bar/baz')).toEqual(undefined); - expect(tree.findChildPathFromSegments(['foo', 'bar', 'baz'])).toEqual(1); - }); -}); - -describe(LookupByPath.prototype.findLongestPrefixMatch.name, () => { - it('returns empty for an empty tree', () => { - expect(new LookupByPath().findLongestPrefixMatch('foo')).toEqual(undefined); - }); - it('returns the matching node for a trivial tree', () => { - expect(new LookupByPath([['foo', 1]]).findLongestPrefixMatch('foo')).toEqual({ value: 1, index: 3 }); - }); - it('returns the matching node for a single-layer tree', () => { - const tree: LookupByPath = new LookupByPath([ - ['foo', 1], - ['barbar', 2], - ['baz', 3] - ]); - - expect(tree.findLongestPrefixMatch('foo')).toEqual({ value: 1, index: 3 }); - expect(tree.findLongestPrefixMatch('barbar')).toEqual({ value: 2, index: 6 }); - expect(tree.findLongestPrefixMatch('baz')).toEqual({ value: 3, index: 3 }); - expect(tree.findLongestPrefixMatch('buzz')).toEqual(undefined); - }); - it('returns the matching parent for multi-layer queries', () => { - const tree: LookupByPath = new LookupByPath([ - ['foo', 1], - ['barbar', 2], - ['baz', 3], - ['foo/bar', 4] - ]); - - expect(tree.findLongestPrefixMatch('foo/bar')).toEqual({ value: 4, index: 7 }); - expect(tree.findLongestPrefixMatch('barbar/baz')).toEqual({ value: 2, index: 6 }); - expect(tree.findLongestPrefixMatch('baz/foo')).toEqual({ value: 3, index: 3 }); - expect(tree.findLongestPrefixMatch('foo/foo')).toEqual({ value: 1, index: 3 }); - }); -}); diff --git a/libraries/rush-lib/src/logic/test/ProjectChangeAnalyzer.test.ts b/libraries/rush-lib/src/logic/test/ProjectChangeAnalyzer.test.ts index 326e1d09006..bbc8274c26a 100644 --- a/libraries/rush-lib/src/logic/test/ProjectChangeAnalyzer.test.ts +++ b/libraries/rush-lib/src/logic/test/ProjectChangeAnalyzer.test.ts @@ -1,319 +1,1271 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { StringBufferTerminalProvider, Terminal } from '@rushstack/node-core-library'; +const mockHashes: Map = new Map([ + ['a/package.json', 'hash1'], + ['b/package.json', 'hash2'], + ['c/package.json', 'hash3'], + ['changes/a.json', 'hash4'], + ['changes/b.json', 'hash5'], + ['changes/c.json', 'hash6'], + ['changes/d.json', 'hash7'], + ['changes/h.json', 'hash8'], + ['common/config/rush/version-policies.json', 'hash9'], + ['common/config/rush/npm-shrinkwrap.json', 'hash10'], + ['d/package.json', 'hash11'], + ['e/package.json', 'hash12'], + ['f/package.json', 'hash13'], + ['g/package.json', 'hash14'], + ['h/package.json', 'hash15'], + ['i/package.json', 'hash16'], + ['j/package.json', 'hash17'], + ['rush.json', 'hash18'] +]); -import { ProjectChangeAnalyzer } from '../ProjectChangeAnalyzer'; +// Mock function for customizing repo changes in each test +const mockGetRepoChanges: jest.MockedFunction = + jest.fn(); + +jest.mock(`@rushstack/package-deps-hash`, () => { + return { + getRepoRoot(dir: string): string { + return dir; + }, + getDetailedRepoStateAsync(): IDetailedRepoState { + return { + hasSubmodules: false, + hasUncommittedChanges: false, + files: mockHashes, + symlinks: new Map() + }; + }, + getRepoChangesAsync(): ReadonlyMap { + return new Map(); + }, + getGitHashForFiles(filePaths: Iterable): ReadonlyMap { + return new Map(Array.from(filePaths, (filePath: string) => [filePath, filePath])); + }, + hashFilesAsync(rootDirectory: string, filePaths: Iterable): ReadonlyMap { + return new Map(Array.from(filePaths, (filePath: string) => [filePath, filePath])); + }, + getRepoChanges( + currentWorkingDirectory: string, + revision?: string, + gitPath?: string + ): Map { + return mockGetRepoChanges(currentWorkingDirectory, revision, gitPath); + } + }; +}); + +const { Git: OriginalGit } = jest.requireActual('../Git'); + +// Mock function for getBlobContentAsync to be customized in each test +const mockGetBlobContentAsync: jest.MockedFunction< + typeof import('../Git').Git.prototype.getBlobContentAsync +> = jest.fn(); + +/** Mock Git to test `getChangedProjectsAsync` */ +jest.mock('../Git', () => { + return { + Git: class MockGit extends OriginalGit { + public async determineIfRefIsACommitAsync(ref: string): Promise { + return true; + } + public async getMergeBaseAsync(ref1: string, ref2: string): Promise { + return 'merge-base-sha'; + } + public async getBlobContentAsync(opts: { blobSpec: string; repositoryRoot: string }): Promise { + return mockGetBlobContentAsync(opts); + } + } + }; +}); + +const OriginalPnpmShrinkwrapFile: typeof PnpmShrinkwrapFile = jest.requireActual( + '../pnpm/PnpmShrinkwrapFile' +).PnpmShrinkwrapFile; +jest.mock('../pnpm/PnpmShrinkwrapFile', () => { + return { + PnpmShrinkwrapFile: { + loadFromFile: (fullShrinkwrapPath: string, options: ILoadFromFileOptions): PnpmShrinkwrapFile => { + return OriginalPnpmShrinkwrapFile.loadFromString(_getMockedPnpmShrinkwrapFile(), options); + }, + loadFromString: (text: string, options: ILoadFromStringOptions): PnpmShrinkwrapFile => { + return OriginalPnpmShrinkwrapFile.loadFromString( + _getMockedPnpmShrinkwrapFile() + // Change dependencies version + .replace(/1\.0\.1/g, '1.0.0') + .replace(/foo_1_0_1/g, 'foo_1_0_0'), + options + ); + } + } + }; +}); + +const mockSnapshot: jest.Mock = jest.fn(); + +jest.mock('../incremental/InputsSnapshot', () => { + return { + InputsSnapshot: mockSnapshot + }; +}); + +import { resolve } from 'node:path'; + +import type { IDetailedRepoState, IFileDiffStatus } from '@rushstack/package-deps-hash'; +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; + +import { ProjectChangeAnalyzer, isPackageJsonVersionOnlyChange } from '../ProjectChangeAnalyzer'; import { RushConfiguration } from '../../api/RushConfiguration'; -import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; -import { RushProjectConfiguration } from '../../api/RushProjectConfiguration'; -import { LookupByPath } from '../LookupByPath'; -import { UNINITIALIZED } from '../../utilities/Utilities'; +import type { + IInputsSnapshot, + GetInputsSnapshotAsyncFn, + IInputsSnapshotParameters +} from '../incremental/InputsSnapshot'; +import type { + ILoadFromFileOptions, + ILoadFromStringOptions, + PnpmShrinkwrapFile +} from '../pnpm/PnpmShrinkwrapFile'; describe(ProjectChangeAnalyzer.name, () => { beforeEach(() => { - jest.spyOn(EnvironmentConfiguration, 'gitBinaryPath', 'get').mockReturnValue(undefined); - jest.spyOn(RushProjectConfiguration, 'tryLoadIgnoreGlobsForProjectAsync').mockResolvedValue(undefined); + mockSnapshot.mockClear(); + mockGetBlobContentAsync.mockClear(); + mockGetRepoChanges.mockClear(); }); - afterEach(() => { - jest.resetAllMocks(); + describe(ProjectChangeAnalyzer.prototype._tryGetSnapshotProviderAsync.name, () => { + it('returns a snapshot', async () => { + const rootDir: string = resolve(__dirname, 'repo'); + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile( + resolve(rootDir, 'rush.json') + ); + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(true); + const terminal: Terminal = new Terminal(terminalProvider); + const mockSnapshotValue: {} = {}; + mockSnapshot.mockImplementation(() => mockSnapshotValue); + const snapshotProvider: GetInputsSnapshotAsyncFn | undefined = + await projectChangeAnalyzer._tryGetSnapshotProviderAsync(new Map(), terminal); + const snapshot: IInputsSnapshot | undefined = await snapshotProvider?.(); + + expect(snapshot).toBe(mockSnapshotValue); + expect(terminalProvider.getAllOutput(true)).toEqual({}); + expect(mockSnapshot).toHaveBeenCalledTimes(1); + + const mockInput: IInputsSnapshotParameters = mockSnapshot.mock.calls[0][0]; + expect(mockInput.globalAdditionalFiles).toBeDefined(); + expect(mockInput.globalAdditionalFiles).toMatchObject(['common/config/rush/npm-shrinkwrap.json']); + + expect(mockInput.hashes).toEqual(mockHashes); + expect(mockInput.rootDir).toEqual(rootDir); + expect(mockInput.additionalHashes).toEqual(new Map()); + }); }); - function createTestSubject( - projects: RushConfigurationProject[], - files: Map - ): ProjectChangeAnalyzer { - const rushConfiguration: RushConfiguration = { - commonRushConfigFolder: '', - projects, - rushJsonFolder: '', - getCommittedShrinkwrapFilename(): string { - return 'common/config/rush/pnpm-lock.yaml'; - }, - getProjectLookupForRoot(root: string): LookupByPath { - const lookup: LookupByPath = new LookupByPath(); - for (const project of projects) { - lookup.setItem(project.projectRelativeFolder, project); - } - return lookup; - }, - getProjectByName(name: string): RushConfigurationProject | undefined { - return projects.find((project) => project.packageName === name); - } - } as RushConfiguration; + describe(ProjectChangeAnalyzer.prototype.getChangedProjectsAsync.name, () => { + it('Subspaces detects external changes', async () => { + // Set up mock repo changes for this test + mockGetRepoChanges.mockReturnValue( + new Map([ + [ + // Test subspace lockfile change detection + 'common/config/subspaces/project-change-analyzer-test-subspace/pnpm-lock.yaml', + { + mode: 'modified', + newhash: 'newhash', + oldhash: 'oldhash', + status: 'M' + } + ], + [ + // Test lockfile deletion detection + 'common/config/subspaces/default/pnpm-lock.yaml', + { + mode: 'deleted', + newhash: '', + oldhash: 'oldhash', + status: 'D' + } + ] + ]) + ); - const subject: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const rootDir: string = resolve(__dirname, 'repoWithSubspaces'); + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile( + resolve(rootDir, 'rush.json') + ); + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(true); + const terminal: Terminal = new Terminal(terminalProvider); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: true, + targetBranchName: 'main', + terminal + }); - subject['_getRepoDepsAsync'] = jest.fn(() => { - return Promise.resolve({ - gitPath: 'git', - hashes: files, - rootDir: '' + // a,b,c is included because of change modifier is not modified + // d is included because its dependency foo version changed in the subspace lockfile + ['a', 'b', 'c', 'd'].forEach((projectName) => { + expect(changedProjects.has(rushConfiguration.getProjectByName(projectName)!)).toBe(true); + }); + + // e depends on d via workspace:*, but its calculated lockfile (e.g. "e/.rush/temp/shrinkwrap-deps.json") didn't change. + // So it's not included. e will be included by `expandConsumers` if needed. + ['e', 'f'].forEach((projectName) => { + expect(changedProjects.has(rushConfiguration.getProjectByName(projectName)!)).toBe(false); }); }); - return subject; - } + it('excludeVersionOnlyChanges excludes projects with only version field changes', async () => { + const rootDir: string = resolve(__dirname, 'repo'); + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile( + resolve(rootDir, 'rush.json') + ); - describe(ProjectChangeAnalyzer.prototype._tryGetProjectDependenciesAsync.name, () => { - it('returns the files for the specified project', async () => { - const projects: RushConfigurationProject[] = [ + // Mock package.json with only version change + const oldPackageJsonContent = JSON.stringify( { - packageName: 'apple', - projectFolder: 'apps/apple', - projectRelativeFolder: 'apps/apple' - } as RushConfigurationProject, + name: 'a', + version: '1.0.0', + description: 'Test package', + dependencies: { + b: '1.0.0' + } + }, + null, + 2 + ); + + const newPackageJsonContent = JSON.stringify( { - packageName: 'banana', - projectFolder: 'apps/apple', - projectRelativeFolder: 'apps/banana' - } as RushConfigurationProject - ]; - const files: Map = new Map([ - ['apps/apple/core.js', 'a101'], - ['apps/banana/peel.js', 'b201'] - ]); - const subject: ProjectChangeAnalyzer = createTestSubject(projects, files); - const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); - - expect(await subject._tryGetProjectDependenciesAsync(projects[0], terminal)).toEqual( - new Map([['apps/apple/core.js', 'a101']]) + name: 'a', + version: '1.0.1', + description: 'Test package', + dependencies: { + b: '1.0.0' + } + }, + null, + 2 ); - expect(await subject._tryGetProjectDependenciesAsync(projects[1], terminal)).toEqual( - new Map([['apps/banana/peel.js', 'b201']]) + + // Set up mock repo changes - only package.json changed for project 'a' + mockGetRepoChanges.mockReturnValue( + new Map([ + [ + 'a/package.json', + { + mode: 'modified', + newhash: 'newhash1', + oldhash: 'oldhash1', + status: 'M' + } + ] + ]) ); + + // Mock the blob content to return different versions based on the hash + mockGetBlobContentAsync.mockImplementation((opts: { blobSpec: string; repositoryRoot: string }) => { + if (opts.blobSpec === 'oldhash1') { + return Promise.resolve(oldPackageJsonContent); + } else if (opts.blobSpec === 'newhash1') { + return Promise.resolve(newPackageJsonContent); + } + return Promise.resolve(''); + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(true); + const terminal: Terminal = new Terminal(terminalProvider); + + // Test without excludeVersionOnlyChanges - project should be detected as changed + const changedProjectsWithoutExclude = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + expect(changedProjectsWithoutExclude.has(rushConfiguration.getProjectByName('a')!)).toBe(true); + + // Test with excludeVersionOnlyChanges - project should NOT be detected as changed + const changedProjectsWithExclude = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal, + excludeVersionOnlyChanges: true + }); + expect(changedProjectsWithExclude.has(rushConfiguration.getProjectByName('a')!)).toBe(false); }); - it('ignores files specified by project configuration files, relative to project folder', async () => { - // rush-project.json configuration for 'apple' - jest - .spyOn(RushProjectConfiguration, 'tryLoadIgnoreGlobsForProjectAsync') - .mockResolvedValueOnce(['assets/*.png', '*.js.map']); - // rush-project.json configuration for 'banana' does not exist - jest - .spyOn(RushProjectConfiguration, 'tryLoadIgnoreGlobsForProjectAsync') - .mockResolvedValueOnce(undefined); - - const projects: RushConfigurationProject[] = [ + it('excludeVersionOnlyChanges does not exclude projects with non-version changes', async () => { + const rootDir: string = resolve(__dirname, 'repo'); + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile( + resolve(rootDir, 'rush.json') + ); + + // Mock package.json with version AND dependency change + const oldPackageJsonContent = JSON.stringify( { - packageName: 'apple', - projectFolder: 'apps/apple', - projectRelativeFolder: 'apps/apple' - } as RushConfigurationProject, + name: 'b', + version: '1.0.0', + description: 'Test package', + dependencies: { + a: '1.0.0' + } + }, + null, + 2 + ); + + const newPackageJsonContent = JSON.stringify( { - packageName: 'banana', - projectFolder: 'apps/apple', - projectRelativeFolder: 'apps/banana' - } as RushConfigurationProject - ]; - const files: Map = new Map([ - ['apps/apple/core.js', 'a101'], - ['apps/apple/core.js.map', 'a102'], - ['apps/apple/assets/one.jpg', 'a103'], - ['apps/apple/assets/two.png', 'a104'], - ['apps/banana/peel.js', 'b201'], - ['apps/banana/peel.js.map', 'b202'] - ]); - const subject: ProjectChangeAnalyzer = createTestSubject(projects, files); - const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); - - expect(await subject._tryGetProjectDependenciesAsync(projects[0], terminal)).toEqual( - new Map([ - ['apps/apple/core.js', 'a101'], - ['apps/apple/assets/one.jpg', 'a103'] - ]) + name: 'b', + version: '1.0.1', + description: 'Test package', + dependencies: { + a: '1.0.1' // Dependency version also changed + } + }, + null, + 2 ); - expect(await subject._tryGetProjectDependenciesAsync(projects[1], terminal)).toEqual( - new Map([ - ['apps/banana/peel.js', 'b201'], - ['apps/banana/peel.js.map', 'b202'] + + // Set up mock repo changes - only package.json changed for project 'b' + mockGetRepoChanges.mockReturnValue( + new Map([ + [ + 'b/package.json', + { + mode: 'modified', + newhash: 'newhash2', + oldhash: 'oldhash2', + status: 'M' + } + ] ]) ); + + // Mock the blob content to return different versions based on the hash + mockGetBlobContentAsync.mockImplementation((opts: { blobSpec: string; repositoryRoot: string }) => { + if (opts.blobSpec === 'oldhash2') { + return Promise.resolve(oldPackageJsonContent); + } else if (opts.blobSpec === 'newhash2') { + return Promise.resolve(newPackageJsonContent); + } + return Promise.resolve(''); + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(true); + const terminal: Terminal = new Terminal(terminalProvider); + + // Test with excludeVersionOnlyChanges - project should still be detected as changed + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal, + excludeVersionOnlyChanges: true + }); + expect(changedProjects.has(rushConfiguration.getProjectByName('b')!)).toBe(true); }); - it('interprets ignored globs as a dot-ignore file (not as individually handled globs)', async () => { - // rush-project.json configuration for 'apple' - jest - .spyOn(RushProjectConfiguration, 'tryLoadIgnoreGlobsForProjectAsync') - .mockResolvedValue(['*.png', 'assets/*.psd', '!assets/important/**']); + it('excludeVersionOnlyChanges does not exclude projects when package.json and other files changed', async () => { + const rootDir: string = resolve(__dirname, 'repo'); + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile( + resolve(rootDir, 'rush.json') + ); + + // Mock package.json with only version change + const oldPackageJsonContent = JSON.stringify( + { + name: 'c', + version: '1.0.0', + description: 'Test package', + dependencies: { + a: '1.0.0' + } + }, + null, + 2 + ); - const projects: RushConfigurationProject[] = [ + const newPackageJsonContent = JSON.stringify( { - packageName: 'apple', - projectFolder: 'apps/apple', - projectRelativeFolder: 'apps/apple' - } as RushConfigurationProject - ]; - const files: Map = new Map([ - ['apps/apple/one.png', 'a101'], - ['apps/apple/assets/two.psd', 'a102'], - ['apps/apple/assets/three.png', 'a103'], - ['apps/apple/assets/important/four.png', 'a104'], - ['apps/apple/assets/important/five.psd', 'a105'], - ['apps/apple/src/index.ts', 'a106'] - ]); - const subject: ProjectChangeAnalyzer = createTestSubject(projects, files); - const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); - - // In a dot-ignore file, the later rule '!assets/important/**' should override the previous - // rule of '*.png'. This unit test verifies that this behavior doesn't change later if - // we modify the implementation. - expect(await subject._tryGetProjectDependenciesAsync(projects[0], terminal)).toEqual( - new Map([ - ['apps/apple/assets/important/four.png', 'a104'], - ['apps/apple/assets/important/five.psd', 'a105'], - ['apps/apple/src/index.ts', 'a106'] + name: 'c', + version: '1.0.1', + description: 'Test package', + dependencies: { + a: '1.0.0' + } + }, + null, + 2 + ); + + // Set up mock repo changes - package.json AND another file changed for project 'c' + mockGetRepoChanges.mockReturnValue( + new Map([ + [ + 'c/package.json', + { + mode: 'modified', + newhash: 'newhash3', + oldhash: 'oldhash3', + status: 'M' + } + ], + [ + 'c/src/index.ts', + { + mode: 'modified', + newhash: 'newhash4', + oldhash: 'oldhash4', + status: 'M' + } + ] ]) ); + + // Mock the blob content to return different versions based on the hash + mockGetBlobContentAsync.mockImplementation((opts: { blobSpec: string; repositoryRoot: string }) => { + if (opts.blobSpec === 'oldhash3') { + return Promise.resolve(oldPackageJsonContent); + } else if (opts.blobSpec === 'newhash3') { + return Promise.resolve(newPackageJsonContent); + } + return Promise.resolve(''); + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(true); + const terminal: Terminal = new Terminal(terminalProvider); + + // Test with excludeVersionOnlyChanges - project should still be detected as changed because multiple files changed + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal, + excludeVersionOnlyChanges: true + }); + expect(changedProjects.has(rushConfiguration.getProjectByName('c')!)).toBe(true); }); - it('includes the committed shrinkwrap file as a dep for all projects', async () => { - const projects: RushConfigurationProject[] = [ - { - packageName: 'apple', - projectFolder: 'apps/apple', - projectRelativeFolder: 'apps/apple' - } as RushConfigurationProject, - { - packageName: 'banana', - projectFolder: 'apps/apple', - projectRelativeFolder: 'apps/banana' - } as RushConfigurationProject - ]; - const files: Map = new Map([ - ['apps/apple/core.js', 'a101'], - ['apps/banana/peel.js', 'b201'], - ['common/config/rush/pnpm-lock.yaml', 'ffff'], - ['tools/random-file.js', 'e00e'] - ]); - const subject: ProjectChangeAnalyzer = createTestSubject(projects, files); - const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); - - expect(await subject._tryGetProjectDependenciesAsync(projects[0], terminal)).toEqual( - new Map([ - ['apps/apple/core.js', 'a101'], - ['common/config/rush/pnpm-lock.yaml', 'ffff'] + it('excludeVersionOnlyChanges ignores CHANGELOG.md and CHANGELOG.json files', async () => { + const rootDir: string = resolve(__dirname, 'repo'); + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile( + resolve(rootDir, 'rush.json') + ); + + // Mock package.json with only version change + const oldPackageJsonContent = JSON.stringify({ + name: 'd', + version: '1.0.0', + description: 'Test package' + }); + + const newPackageJsonContent = JSON.stringify({ + name: 'd', + version: '1.0.1', + description: 'Test package' + }); + + // Set up mock repo changes - package.json (version only), CHANGELOG.md, and CHANGELOG.json changed + mockGetRepoChanges.mockReturnValue( + new Map([ + [ + 'd/package.json', + { + mode: 'modified', + newhash: 'newhash4', + oldhash: 'oldhash4', + status: 'M' + } + ], + [ + 'd/CHANGELOG.md', + { + mode: 'modified', + newhash: 'newhash5', + oldhash: 'oldhash5', + status: 'M' + } + ], + [ + 'd/CHANGELOG.json', + { + mode: 'modified', + newhash: 'newhash6', + oldhash: 'oldhash6', + status: 'M' + } + ] ]) ); - expect(await subject._tryGetProjectDependenciesAsync(projects[1], terminal)).toEqual( - new Map([ - ['apps/banana/peel.js', 'b201'], - ['common/config/rush/pnpm-lock.yaml', 'ffff'] + + // Mock the blob content to return different versions based on the hash + mockGetBlobContentAsync.mockImplementation((opts: { blobSpec: string; repositoryRoot: string }) => { + if (opts.blobSpec === 'oldhash4') { + return Promise.resolve(oldPackageJsonContent); + } else if (opts.blobSpec === 'newhash4') { + return Promise.resolve(newPackageJsonContent); + } + return Promise.resolve(''); + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(true); + const terminal: Terminal = new Terminal(terminalProvider); + + // Test with excludeVersionOnlyChanges - project should NOT be detected as changed + // because only version-only package.json and CHANGELOG files changed + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal, + excludeVersionOnlyChanges: true + }); + expect(changedProjects.has(rushConfiguration.getProjectByName('d')!)).toBe(false); + }); + + it('excludeVersionOnlyChanges does not ignore projects with CHANGELOG and other substantive changes', async () => { + const rootDir: string = resolve(__dirname, 'repo'); + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile( + resolve(rootDir, 'rush.json') + ); + + // Set up mock repo changes - CHANGELOG.md and src file changed + mockGetRepoChanges.mockReturnValue( + new Map([ + [ + 'e/CHANGELOG.md', + { + mode: 'modified', + newhash: 'newhash7', + oldhash: 'oldhash7', + status: 'M' + } + ], + [ + 'e/src/index.ts', + { + mode: 'modified', + newhash: 'newhash8', + oldhash: 'oldhash8', + status: 'M' + } + ] ]) ); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(true); + const terminal: Terminal = new Terminal(terminalProvider); + + // Test with excludeVersionOnlyChanges - project should be detected as changed + // because there's a substantive change in addition to CHANGELOG + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal, + excludeVersionOnlyChanges: true + }); + expect(changedProjects.has(rushConfiguration.getProjectByName('e')!)).toBe(true); }); - it('throws an exception if the specified project does not exist', async () => { - const projects: RushConfigurationProject[] = [ - { - packageName: 'apple', - projectFolder: 'apps/apple', - projectRelativeFolder: 'apps/apple' - } as RushConfigurationProject - ]; - const files: Map = new Map([['apps/apple/core.js', 'a101']]); - const subject: ProjectChangeAnalyzer = createTestSubject(projects, files); - const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); - - try { - await subject._tryGetProjectDependenciesAsync( - { - packageName: 'carrot' - } as RushConfigurationProject, - terminal + describe('catalog change detection', () => { + function getCatalogRushConfiguration(): RushConfiguration { + return RushConfiguration.loadFromConfigurationFile( + resolve(__dirname, 'repoWithCatalogs', 'rush.json') + ); + } + + function mockPnpmConfigChanged(): void { + mockGetRepoChanges.mockReturnValue( + new Map([ + [ + 'common/config/rush/pnpm-config.json', + { mode: 'modified', newhash: 'newhash', oldhash: 'oldhash', status: 'M' } + ] + ]) ); - fail('Should have thrown error'); - } catch (e) { - expect(e).toMatchSnapshot(); } + + function mockOldCatalogs(catalogs: Record>): void { + mockGetBlobContentAsync.mockImplementation(() => { + return Promise.resolve(JSON.stringify({ globalCatalogs: catalogs })); + }); + } + + it('detects change to default catalog', async () => { + const rushConfiguration: RushConfiguration = getCatalogRushConfiguration(); + mockPnpmConfigChanged(); + // react version bumped in the default catalog + mockOldCatalogs({ + default: { react: '^17.0.0', semver: '^7.5.4' }, + tools: { typescript: '~5.3.0', eslint: '^8.50.0' } + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider(true)); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + + // 'a' uses "catalog:" (default) for react (changed) and semver + expect(changedProjects.has(rushConfiguration.getProjectByName('a')!)).toBe(true); + // 'd' uses "catalog:" (default) for semver only (unchanged) — should NOT be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('d')!)).toBe(false); + // 'b' uses "catalog:tools" only — tools catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('b')!)).toBe(false); + // 'e' uses "catalog:tools" for eslint only — tools catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('e')!)).toBe(false); + // 'c' has no catalog deps + expect(changedProjects.has(rushConfiguration.getProjectByName('c')!)).toBe(false); + }); + + it('detects change to named catalog', async () => { + const rushConfiguration: RushConfiguration = getCatalogRushConfiguration(); + mockPnpmConfigChanged(); + // typescript version bumped in the tools catalog + mockOldCatalogs({ + default: { react: '^18.0.0', semver: '^7.5.4' }, + tools: { typescript: '~5.2.0', eslint: '^8.50.0' } + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider(true)); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + + // 'b' uses "catalog:tools" for typescript (changed) + expect(changedProjects.has(rushConfiguration.getProjectByName('b')!)).toBe(true); + // 'e' uses "catalog:tools" for eslint only (unchanged) — should NOT be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('e')!)).toBe(false); + // 'a' uses "catalog:" (default) — default catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('a')!)).toBe(false); + // 'd' uses "catalog:" (default) for semver — default catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('d')!)).toBe(false); + // 'c' has no catalog deps + expect(changedProjects.has(rushConfiguration.getProjectByName('c')!)).toBe(false); + }); + + it('no changes when catalogs are unchanged', async () => { + const rushConfiguration: RushConfiguration = getCatalogRushConfiguration(); + mockPnpmConfigChanged(); + // Old catalogs are identical to current + mockOldCatalogs({ + default: { react: '^18.0.0', semver: '^7.5.4' }, + tools: { typescript: '~5.3.0', eslint: '^8.50.0' } + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider(true)); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + + expect(changedProjects.size).toBe(0); + }); + + it('only marks projects whose specific catalog package changed, not all projects in the same catalog namespace', async () => { + const rushConfiguration: RushConfiguration = getCatalogRushConfiguration(); + mockPnpmConfigChanged(); + // Only react changed in the default catalog; semver is unchanged + mockOldCatalogs({ + default: { react: '^17.0.0', semver: '^7.5.4' }, + tools: { typescript: '~5.3.0', eslint: '^8.50.0' } + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider(true)); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + + // 'a' uses "catalog:" for react (changed) and semver (unchanged) — should be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('a')!)).toBe(true); + // 'd' uses "catalog:" for semver only (unchanged) — should NOT be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('d')!)).toBe(false); + // 'b' uses "catalog:tools" for typescript — tools catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('b')!)).toBe(false); + // 'e' uses "catalog:tools" for eslint — tools catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('e')!)).toBe(false); + // 'c' has no catalog deps + expect(changedProjects.has(rushConfiguration.getProjectByName('c')!)).toBe(false); + }); + + it('marks project when its specific package version changed in catalog, even if other packages in same catalog are unchanged', async () => { + const rushConfiguration: RushConfiguration = getCatalogRushConfiguration(); + mockPnpmConfigChanged(); + // Only semver changed in the default catalog; react is unchanged + mockOldCatalogs({ + default: { react: '^18.0.0', semver: '^7.4.0' }, + tools: { typescript: '~5.3.0', eslint: '^8.50.0' } + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider(true)); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + + // 'a' uses "catalog:" for react (unchanged) and semver (changed) — should be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('a')!)).toBe(true); + // 'd' uses "catalog:" for semver (changed) — should be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('d')!)).toBe(true); + // 'b' uses "catalog:tools" for typescript — tools catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('b')!)).toBe(false); + // 'e' uses "catalog:tools" for eslint — tools catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('e')!)).toBe(false); + // 'c' has no catalog deps + expect(changedProjects.has(rushConfiguration.getProjectByName('c')!)).toBe(false); + }); + + it('only marks projects whose specific named catalog package changed', async () => { + const rushConfiguration: RushConfiguration = getCatalogRushConfiguration(); + mockPnpmConfigChanged(); + // Only typescript changed in the tools catalog; eslint is unchanged + mockOldCatalogs({ + default: { react: '^18.0.0', semver: '^7.5.4' }, + tools: { typescript: '~5.2.0', eslint: '^8.50.0' } + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider(true)); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + + // 'b' uses "catalog:tools" for typescript (changed) — should be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('b')!)).toBe(true); + // 'e' uses "catalog:tools" for eslint only (unchanged) — should NOT be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('e')!)).toBe(false); + // 'a' uses "catalog:" (default) — default catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('a')!)).toBe(false); + // 'd' uses "catalog:" (default) — default catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('d')!)).toBe(false); + // 'c' has no catalog deps + expect(changedProjects.has(rushConfiguration.getProjectByName('c')!)).toBe(false); + }); + + it('marks project when its specific named catalog package changed', async () => { + const rushConfiguration: RushConfiguration = getCatalogRushConfiguration(); + mockPnpmConfigChanged(); + // Only eslint changed in the tools catalog; typescript is unchanged + mockOldCatalogs({ + default: { react: '^18.0.0', semver: '^7.5.4' }, + tools: { typescript: '~5.3.0', eslint: '^8.40.0' } + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider(true)); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + + // 'e' uses "catalog:tools" for eslint (changed) — should be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('e')!)).toBe(true); + // 'b' uses "catalog:tools" for typescript only (unchanged) — should NOT be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('b')!)).toBe(false); + // 'a' uses "catalog:" (default) — default catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('a')!)).toBe(false); + // 'd' uses "catalog:" (default) — default catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('d')!)).toBe(false); + // 'c' has no catalog deps + expect(changedProjects.has(rushConfiguration.getProjectByName('c')!)).toBe(false); + }); + + it('all catalog-using projects marked as changed when no old catalog existed', async () => { + const rushConfiguration: RushConfiguration = getCatalogRushConfiguration(); + mockPnpmConfigChanged(); + // Old file did not exist in git + mockGetBlobContentAsync.mockImplementation(() => { + return Promise.reject(new Error('fatal: path not found')); + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider(true)); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + + // All catalog-using projects detected: 'a' (default: react, semver), 'b' (tools: typescript), 'd' (default: semver), 'e' (tools: eslint) + expect(changedProjects.has(rushConfiguration.getProjectByName('a')!)).toBe(true); + expect(changedProjects.has(rushConfiguration.getProjectByName('b')!)).toBe(true); + expect(changedProjects.has(rushConfiguration.getProjectByName('d')!)).toBe(true); + expect(changedProjects.has(rushConfiguration.getProjectByName('e')!)).toBe(true); + // 'c' has no catalog deps + expect(changedProjects.has(rushConfiguration.getProjectByName('c')!)).toBe(false); + }); + + it('parses pnpm-config.json blobs containing comments (regression #5813)', async () => { + const rushConfiguration: RushConfiguration = getCatalogRushConfiguration(); + mockPnpmConfigChanged(); + mockGetBlobContentAsync.mockImplementation(() => { + return Promise.resolve( + `// banner comment from rush init template\n` + + `/* block comment */\n` + + JSON.stringify({ + globalCatalogs: { + default: { react: '^18.0.0', semver: '^7.5.4' }, + tools: { typescript: '~5.3.0', eslint: '^8.50.0' } + } + }) + ); + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider(true)); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + + expect(changedProjects.size).toBe(0); + }); }); - it('lazy-loads project data and caches it for future calls', async () => { - const projects: RushConfigurationProject[] = [ - { - packageName: 'apple', - projectFolder: 'apps/apple', - projectRelativeFolder: 'apps/apple' - } as RushConfigurationProject - ]; - const files: Map = new Map([['apps/apple/core.js', 'a101']]); - const subject: ProjectChangeAnalyzer = createTestSubject(projects, files); - const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); - - // Because other unit tests rely on the fact that a freshly instantiated - // ProjectChangeAnalyzer is inert until someone actually requests project data, - // this test makes that expectation explicit. - - expect(subject['_data']).toEqual(UNINITIALIZED); - expect(await subject._tryGetProjectDependenciesAsync(projects[0], terminal)).toEqual( - new Map([['apps/apple/core.js', 'a101']]) - ); - expect(subject['_data']).toBeDefined(); - expect(subject['_data']).not.toEqual(UNINITIALIZED); - expect(await subject._tryGetProjectDependenciesAsync(projects[0], terminal)).toEqual( - new Map([['apps/apple/core.js', 'a101']]) - ); - expect(subject['_getRepoDepsAsync']).toHaveBeenCalledTimes(1); + describe('subspace catalog change detection', () => { + function getSubspaceCatalogRushConfiguration(): RushConfiguration { + return RushConfiguration.loadFromConfigurationFile( + resolve(__dirname, 'repoWithSubspacesCatalogs', 'rush.json') + ); + } + + it('detects change to default catalog in subspace', async () => { + const rushConfiguration: RushConfiguration = getSubspaceCatalogRushConfiguration(); + + // Only the subspace pnpm-config.json changed + mockGetRepoChanges.mockReturnValue( + new Map([ + [ + 'common/config/subspaces/project-change-analyzer-test-subspace/pnpm-config.json', + { mode: 'modified', newhash: 'newhash', oldhash: 'oldhash', status: 'M' } + ] + ]) + ); + + // foo version bumped in the default catalog + mockGetBlobContentAsync.mockImplementation(() => { + return Promise.resolve( + JSON.stringify({ + globalCatalogs: { + default: { foo: '~1.0.0', bar: '^3.0.0' }, + tools: { typescript: '~5.3.0', eslint: '^8.50.0' } + } + }) + ); + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider(true)); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + + // 'd' uses "catalog:" (default) for foo (changed) — should be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('d')!)).toBe(true); + // 'g' uses "catalog:" (default) for bar only (unchanged) — should NOT be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('g')!)).toBe(false); + // 'e' uses "catalog:tools" for typescript — tools catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('e')!)).toBe(false); + // 'h' uses "catalog:tools" for eslint — tools catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('h')!)).toBe(false); + // 'f' has no catalog deps + expect(changedProjects.has(rushConfiguration.getProjectByName('f')!)).toBe(false); + // default subspace projects should not be affected + expect(changedProjects.has(rushConfiguration.getProjectByName('a')!)).toBe(false); + expect(changedProjects.has(rushConfiguration.getProjectByName('b')!)).toBe(false); + expect(changedProjects.has(rushConfiguration.getProjectByName('c')!)).toBe(false); + }); + + it('detects change to named catalog in subspace', async () => { + const rushConfiguration: RushConfiguration = getSubspaceCatalogRushConfiguration(); + + // Only the subspace pnpm-config.json changed + mockGetRepoChanges.mockReturnValue( + new Map([ + [ + 'common/config/subspaces/project-change-analyzer-test-subspace/pnpm-config.json', + { mode: 'modified', newhash: 'newhash', oldhash: 'oldhash', status: 'M' } + ] + ]) + ); + + // typescript version bumped in the tools catalog + mockGetBlobContentAsync.mockImplementation(() => { + return Promise.resolve( + JSON.stringify({ + globalCatalogs: { + default: { foo: '~2.0.0', bar: '^3.0.0' }, + tools: { typescript: '~5.2.0', eslint: '^8.50.0' } + } + }) + ); + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider(true)); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + + // 'e' uses "catalog:tools" for typescript (changed) — should be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('e')!)).toBe(true); + // 'h' uses "catalog:tools" for eslint only (unchanged) — should NOT be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('h')!)).toBe(false); + // 'd' uses "catalog:" (default) for foo — default catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('d')!)).toBe(false); + // 'g' uses "catalog:" (default) for bar — default catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('g')!)).toBe(false); + // 'f' has no catalog deps + expect(changedProjects.has(rushConfiguration.getProjectByName('f')!)).toBe(false); + // default subspace projects should not be affected + expect(changedProjects.has(rushConfiguration.getProjectByName('a')!)).toBe(false); + }); + + it('only marks subspace projects whose specific default catalog package changed', async () => { + const rushConfiguration: RushConfiguration = getSubspaceCatalogRushConfiguration(); + + mockGetRepoChanges.mockReturnValue( + new Map([ + [ + 'common/config/subspaces/project-change-analyzer-test-subspace/pnpm-config.json', + { mode: 'modified', newhash: 'newhash', oldhash: 'oldhash', status: 'M' } + ] + ]) + ); + + // Only bar changed in the default catalog; foo is unchanged + mockGetBlobContentAsync.mockImplementation(() => { + return Promise.resolve( + JSON.stringify({ + globalCatalogs: { + default: { foo: '~2.0.0', bar: '^2.0.0' }, + tools: { typescript: '~5.3.0', eslint: '^8.50.0' } + } + }) + ); + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider(true)); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + + // 'g' uses "catalog:" for bar (changed) — should be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('g')!)).toBe(true); + // 'd' uses "catalog:" for foo only (unchanged) — should NOT be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('d')!)).toBe(false); + // 'e' uses "catalog:tools" — tools catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('e')!)).toBe(false); + // 'h' uses "catalog:tools" — tools catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('h')!)).toBe(false); + // 'f' has no catalog deps + expect(changedProjects.has(rushConfiguration.getProjectByName('f')!)).toBe(false); + }); + + it('only marks subspace projects whose specific named catalog package changed', async () => { + const rushConfiguration: RushConfiguration = getSubspaceCatalogRushConfiguration(); + + mockGetRepoChanges.mockReturnValue( + new Map([ + [ + 'common/config/subspaces/project-change-analyzer-test-subspace/pnpm-config.json', + { mode: 'modified', newhash: 'newhash', oldhash: 'oldhash', status: 'M' } + ] + ]) + ); + + // Only eslint changed in the tools catalog; typescript is unchanged + mockGetBlobContentAsync.mockImplementation(() => { + return Promise.resolve( + JSON.stringify({ + globalCatalogs: { + default: { foo: '~2.0.0', bar: '^3.0.0' }, + tools: { typescript: '~5.3.0', eslint: '^8.40.0' } + } + }) + ); + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider(true)); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + + // 'h' uses "catalog:tools" for eslint (changed) — should be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('h')!)).toBe(true); + // 'e' uses "catalog:tools" for typescript only (unchanged) — should NOT be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('e')!)).toBe(false); + // 'd' uses "catalog:" (default) — default catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('d')!)).toBe(false); + // 'g' uses "catalog:" (default) — default catalog is unchanged + expect(changedProjects.has(rushConfiguration.getProjectByName('g')!)).toBe(false); + // 'f' has no catalog deps + expect(changedProjects.has(rushConfiguration.getProjectByName('f')!)).toBe(false); + }); + + it('detects changes when multiple subspace pnpm-configs have catalog changes', async () => { + const rushConfiguration: RushConfiguration = getSubspaceCatalogRushConfiguration(); + + // Both the default subspace and named subspace pnpm-config.json files changed + mockGetRepoChanges.mockReturnValue( + new Map([ + [ + 'common/config/subspaces/default/pnpm-config.json', + { mode: 'modified', newhash: 'newhash1', oldhash: 'oldhash1', status: 'M' } + ], + [ + 'common/config/subspaces/project-change-analyzer-test-subspace/pnpm-config.json', + { mode: 'modified', newhash: 'newhash2', oldhash: 'oldhash2', status: 'M' } + ] + ]) + ); + + // Return old catalogs based on which config is being read + mockGetBlobContentAsync.mockImplementation((opts: { blobSpec: string; repositoryRoot: string }) => { + if (opts.blobSpec.includes('default/pnpm-config.json')) { + // react version bumped in the default subspace + return Promise.resolve(JSON.stringify({ globalCatalogs: { default: { react: '^17.0.0' } } })); + } + if (opts.blobSpec.includes('project-change-analyzer-test-subspace/pnpm-config.json')) { + // foo version bumped in the named subspace; bar, tools unchanged + return Promise.resolve( + JSON.stringify({ + globalCatalogs: { + default: { foo: '~1.0.0', bar: '^3.0.0' }, + tools: { typescript: '~5.3.0', eslint: '^8.50.0' } + } + }) + ); + } + return Promise.resolve('{}'); + }); + + const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(rushConfiguration); + const terminal: Terminal = new Terminal(new StringBufferTerminalProvider(true)); + + const changedProjects = await projectChangeAnalyzer.getChangedProjectsAsync({ + enableFiltering: false, + includeExternalDependencies: false, + targetBranchName: 'main', + terminal + }); + + // 'a' uses "catalog:" (default) for react in the default subspace — should be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('a')!)).toBe(true); + // 'd' uses "catalog:" (default) for foo (changed) in the named subspace — should be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('d')!)).toBe(true); + // 'g' uses "catalog:" (default) for bar (unchanged) in the named subspace — should NOT be detected + expect(changedProjects.has(rushConfiguration.getProjectByName('g')!)).toBe(false); + // 'b' has no catalog deps in default subspace + expect(changedProjects.has(rushConfiguration.getProjectByName('b')!)).toBe(false); + // 'c' has no catalog deps in default subspace + expect(changedProjects.has(rushConfiguration.getProjectByName('c')!)).toBe(false); + // 'e' uses "catalog:tools" for typescript — tools catalog is unchanged in the named subspace + expect(changedProjects.has(rushConfiguration.getProjectByName('e')!)).toBe(false); + // 'h' uses "catalog:tools" for eslint — tools catalog is unchanged in the named subspace + expect(changedProjects.has(rushConfiguration.getProjectByName('h')!)).toBe(false); + // 'f' has no catalog deps + expect(changedProjects.has(rushConfiguration.getProjectByName('f')!)).toBe(false); + }); }); }); - describe(ProjectChangeAnalyzer.prototype._tryGetProjectStateHashAsync.name, () => { - it('returns a fixed hash snapshot for a set of project deps', async () => { - const projects: RushConfigurationProject[] = [ - { - packageName: 'apple', - projectFolder: 'apps/apple', - projectRelativeFolder: 'apps/apple' - } as RushConfigurationProject - ]; - const files: Map = new Map([ - ['apps/apple/core.js', 'a101'], - ['apps/apple/juice.js', 'e333'], - ['apps/apple/slices.js', 'a102'] - ]); - const subject: ProjectChangeAnalyzer = createTestSubject(projects, files); - const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); - - expect(await subject._tryGetProjectStateHashAsync(projects[0], terminal)).toMatchInlineSnapshot( - `"265536e325cdfac3fa806a51873d927a712fc6c9"` - ); + describe('isPackageJsonVersionOnlyChange', () => { + it('returns true when only version field changed', () => { + const oldContent = JSON.stringify({ + name: 'test-package', + version: '1.0.0', + description: 'Test package', + dependencies: { foo: '1.0.0' } + }); + const newContent = JSON.stringify({ + name: 'test-package', + version: '1.0.1', + description: 'Test package', + dependencies: { foo: '1.0.0' } + }); + + expect(isPackageJsonVersionOnlyChange(oldContent, newContent)).toBe(true); }); - it('returns the same hash regardless of dep order', async () => { - const projectsA: RushConfigurationProject[] = [ - { - packageName: 'apple', - projectFolder: '/apps/apple', - projectRelativeFolder: 'apps/apple' - } as RushConfigurationProject - ]; - const filesA: Map = new Map([ - ['apps/apple/core.js', 'a101'], - ['apps/apple/juice.js', 'e333'], - ['apps/apple/slices.js', 'a102'] - ]); - const subjectA: ProjectChangeAnalyzer = createTestSubject(projectsA, filesA); - - const projectsB: RushConfigurationProject[] = [ + it('returns false when other fields changed', () => { + const oldContent = JSON.stringify({ + name: 'test-package', + version: '1.0.0', + description: 'Test package', + dependencies: { foo: '1.0.0' } + }); + const newContent = JSON.stringify({ + name: 'test-package', + version: '1.0.1', + description: 'Test package', + dependencies: { foo: '1.0.1' } + }); + + expect(isPackageJsonVersionOnlyChange(oldContent, newContent)).toBe(false); + }); + + it('returns false when version field is missing in old content', () => { + const oldContent = JSON.stringify({ + name: 'test-package', + description: 'Test package' + }); + const newContent = JSON.stringify({ + name: 'test-package', + version: '1.0.1', + description: 'Test package' + }); + + expect(isPackageJsonVersionOnlyChange(oldContent, newContent)).toBe(false); + }); + + it('returns false when version field is missing in new content', () => { + const oldContent = JSON.stringify({ + name: 'test-package', + version: '1.0.0', + description: 'Test package' + }); + const newContent = JSON.stringify({ + name: 'test-package', + description: 'Test package' + }); + + expect(isPackageJsonVersionOnlyChange(oldContent, newContent)).toBe(false); + }); + + it('returns false when JSON is invalid', () => { + const oldContent = 'invalid json'; + const newContent = '{ "name": "test" }'; + + expect(isPackageJsonVersionOnlyChange(oldContent, newContent)).toBe(false); + }); + + it('returns true even with whitespace differences', () => { + const oldContent = JSON.stringify( { - packageName: 'apple', - projectFolder: 'apps/apple', - projectRelativeFolder: 'apps/apple' - } as RushConfigurationProject - ]; - const filesB: Map = new Map([ - ['apps/apple/slices.js', 'a102'], - ['apps/apple/core.js', 'a101'], - ['apps/apple/juice.js', 'e333'] - ]); - const subjectB: ProjectChangeAnalyzer = createTestSubject(projectsB, filesB); - - const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); - expect(await subjectA._tryGetProjectStateHashAsync(projectsA[0], terminal)).toEqual( - await subjectB._tryGetProjectStateHashAsync(projectsB[0], terminal) + name: 'test-package', + version: '1.0.0', + description: 'Test package' + }, + null, + 2 ); + const newContent = JSON.stringify({ + name: 'test-package', + version: '1.0.1', + description: 'Test package' + }); + + expect(isPackageJsonVersionOnlyChange(oldContent, newContent)).toBe(true); }); }); }); + +/** + * Create a fake pnpm-lock.yaml content matches "libraries/rush-lib/src/logic/test/repoWithSubspaces" test repo + */ +function _getMockedPnpmShrinkwrapFile(): string { + return `lockfileVersion: '9.0' + +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false + +importers: + + .: {} + + ../../../d: + dependencies: + foo: + specifier: ~1.0.0 + version: 1.0.1 + + ../../../e: + dependencies: + d: + specifier: workspace:* + version: link:../../../d + + ../../../f: + dependencies: + +packages: + + foo@1.0.1: + resolution: {integrity: 'foo_1_0_1'} + +snapshots: + + foo@1.0.1: {} +`; +} diff --git a/libraries/rush-lib/src/logic/test/ProjectImpactGraphGenerator.test.ts b/libraries/rush-lib/src/logic/test/ProjectImpactGraphGenerator.test.ts new file mode 100644 index 00000000000..583afbca64a --- /dev/null +++ b/libraries/rush-lib/src/logic/test/ProjectImpactGraphGenerator.test.ts @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { FileSystem, Path } from '@rushstack/node-core-library'; +import { ProjectImpactGraphGenerator } from '../ProjectImpactGraphGenerator'; +import { RushConfiguration } from '../../api/RushConfiguration'; +import { Stopwatch } from '../../utilities/Stopwatch'; +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; + +const NORMALIZED_DIRNAME: string = Path.convertToSlashes(__dirname); + +async function runTestForExampleRepoAsync( + repoName: string, + testFn: (generator: ProjectImpactGraphGenerator) => Promise +): Promise { + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(true); + const terminal: Terminal = new Terminal(terminalProvider); + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile( + `${NORMALIZED_DIRNAME}/${repoName}/rush.json` + ); + + const generator: ProjectImpactGraphGenerator = new ProjectImpactGraphGenerator(terminal, rushConfiguration); + await testFn(generator); + + expect( + terminalProvider.getAllOutputAsChunks({ + normalizeSpecialCharacters: true, + asLines: true + }) + ).toMatchSnapshot('Terminal Output'); +} + +describe(ProjectImpactGraphGenerator.name, () => { + describe(ProjectImpactGraphGenerator.prototype.generateAsync.name, () => { + beforeEach(() => { + jest.spyOn(Stopwatch.prototype, 'duration', 'get').mockReturnValue(1.5); + }); + + it.each(['workspacePackages', 'packages', 'repo'])( + 'Correctly generates a project impact graph (repo: "%p")', + async (repoName) => + await runTestForExampleRepoAsync(repoName, async (generator) => { + const writeFileAsyncSpy: jest.SpyInstance = jest + .spyOn(FileSystem, 'writeFileAsync') + .mockImplementation(); + + await generator.generateAsync(); + + expect(writeFileAsyncSpy).toHaveBeenCalledTimes(1); + expect( + Path.convertToSlashes(writeFileAsyncSpy.mock.calls[0][0]).replace( + `${NORMALIZED_DIRNAME}/${repoName}`, + '' + ) + ).toMatchSnapshot('Output file path'); + expect(writeFileAsyncSpy.mock.calls[0][1]).toMatchSnapshot('Output file data'); + }) + ); + }); + + describe(ProjectImpactGraphGenerator.prototype.validateAsync.name, () => { + it.each(['workspacePackages'])( + 'Reports if the project-impact-graph.yaml file is missing (repo: "%p")', + async (repoName) => + await runTestForExampleRepoAsync(repoName, async (generator) => { + await expect(generator.validateAsync()).resolves.toBe(false); + }) + ); + }); +}); diff --git a/libraries/rush-lib/src/logic/test/PublishGit.test.ts b/libraries/rush-lib/src/logic/test/PublishGit.test.ts index b07535615cb..dbeaccec084 100644 --- a/libraries/rush-lib/src/logic/test/PublishGit.test.ts +++ b/libraries/rush-lib/src/logic/test/PublishGit.test.ts @@ -1,4 +1,7 @@ -import * as path from 'path'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; import { RushConfiguration } from '../../api/RushConfiguration'; import { Git } from '../Git'; @@ -16,7 +19,7 @@ describe('PublishGit Test', () => { }); beforeEach(() => { - execCommand = jest.spyOn(PublishUtilities, 'execCommand').mockImplementation(() => { + execCommand = jest.spyOn(PublishUtilities, 'execCommandAsync').mockImplementation(async () => { /* no-op */ }); @@ -30,33 +33,35 @@ describe('PublishGit Test', () => { execCommand.mockClear(); }); - it('Test git with no command line arg tag', () => { - publishGit.addTag( + it('Test git with no command line arg tag', async () => { + await publishGit.addTagAsync( false, 'project1', '2', undefined, undefined // This is undefined to simulate `rush publish ...` without --prerelease-name ); - expect(execCommand).toBeCalledTimes(1); - expect(execCommand).toBeCalledWith(false, gitPath, ['tag', '-a', `project1_v2`, '-m', 'project1 v2']); + expect(execCommand).toHaveBeenCalledTimes(1); + expect(execCommand).toHaveBeenCalledWith({ + shouldExecute: false, + command: gitPath, + args: ['tag', '-a', `project1_v2`, '-m', 'project1 v2'] + }); }); - it('Test git with command line arg tag', () => { - publishGit.addTag( + it('Test git with command line arg tag', async () => { + await publishGit.addTagAsync( false, 'project1', '2', undefined, 'new_version_prerelease' // Simulates `rush publish ... --prerelease-name new_version_prerelease` ); - expect(execCommand).toBeCalledTimes(1); - expect(execCommand).toBeCalledWith(false, gitPath, [ - 'tag', - '-a', - `project1_v2-new_version_prerelease`, - '-m', - 'project1 v2-new_version_prerelease' - ]); + expect(execCommand).toHaveBeenCalledTimes(1); + expect(execCommand).toHaveBeenCalledWith({ + shouldExecute: false, + command: gitPath, + args: ['tag', '-a', `project1_v2-new_version_prerelease`, '-m', 'project1 v2-new_version_prerelease'] + }); }); }); diff --git a/libraries/rush-lib/src/logic/test/PublishUtilities.test.ts b/libraries/rush-lib/src/logic/test/PublishUtilities.test.ts index 1ec0bc5ffb6..572fd3ed21b 100644 --- a/libraries/rush-lib/src/logic/test/PublishUtilities.test.ts +++ b/libraries/rush-lib/src/logic/test/PublishUtilities.test.ts @@ -1,16 +1,36 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IChangeInfo, ChangeType } from '../../api/ChangeManagement'; +import * as path from 'node:path'; +import type { ChildProcess } from 'node:child_process'; + +import { Executable, type IWaitForExitResult } from '@rushstack/node-core-library'; +import { type IChangeInfo, ChangeType } from '../../api/ChangeManagement'; import { RushConfiguration } from '../../api/RushConfiguration'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; -import { PublishUtilities, IChangeRequests } from '../PublishUtilities'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import { PublishUtilities, _updateCommitDetailsAsync, type IChangeRequests } from '../PublishUtilities'; import { ChangeFiles } from '../ChangeFiles'; +import { Git } from '../Git'; + +function createChangeFiles(changesFolder: string): ChangeFiles { + return new ChangeFiles({ changesFolder } as unknown as RushConfiguration); +} -/* eslint-disable dot-notation */ +function createGitResult( + stdout: string, + exitCode: IWaitForExitResult['exitCode'] = 0, + signal: IWaitForExitResult['signal'] = null +): IWaitForExitResult { + return { + stdout, + stderr: '', + exitCode, + signal + }; +} function generateChangeSnapshot( - allPackages: Map, + allPackages: ReadonlyMap, allChanges: IChangeRequests ): string { const unchangedLines: string[] = []; @@ -81,24 +101,116 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { repoRushConfiguration = RushConfiguration.loadFromConfigurationFile(`${__dirname}/repo/rush.json`); }); + afterEach(() => { + jest.restoreAllMocks(); + }); + it('returns no changes in an empty change folder', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/noChange`) + createChangeFiles(`${__dirname}/noChange`) ); expect(allChanges.packageChanges.size).toEqual(0); expect(allChanges.versionPolicyChanges.size).toEqual(0); }); + it('passes change file paths as discrete Git arguments', async () => { + const gitPath: string = path.resolve('git with spaces', 'git.exe'); + const changeFilePath: string = path.resolve( + 'repo with spaces', + 'common', + 'changes', + 'change & echo injected.json' + ); + const changes: IChangeInfo[] = [{ packageName: 'd' }]; + const git: Git = new Git(packagesRushConfiguration); + const gitProcess: ChildProcess = {} as ChildProcess; + + jest.spyOn(git, 'getGitPathOrThrow').mockReturnValue(gitPath); + const spawnSpy: jest.SpyInstance = jest.spyOn(Executable, 'spawn').mockReturnValue(gitProcess); + const waitForExitSpy: jest.SpyInstance = jest + .spyOn(Executable, 'waitForExitAsync') + .mockResolvedValue( + createGitResult('commit 0123456789abcdef\nAuthor: Test Author \n') + ); + + await _updateCommitDetailsAsync(git, changeFilePath, changes); + + expect(spawnSpy).toHaveBeenCalledWith(gitPath, ['log', '-n', '1', '--', changeFilePath], { + currentWorkingDirectory: path.dirname(changeFilePath) + }); + expect(waitForExitSpy).toHaveBeenCalledWith(gitProcess, { encoding: 'utf8' }); + expect(changes).toEqual([ + { + packageName: 'd', + author: 'Test Author ', + commit: '0123456789abcdef' + } + ]); + }); + + it('delegates Git wrapper paths to Executable', async () => { + const gitPath: string = path.resolve('git-wrapper', 'git.cmd'); + const changeFilePath: string = path.resolve('repo', 'common', 'changes', 'change.json'); + const changes: IChangeInfo[] = [{ packageName: 'd' }]; + const git: Git = new Git(packagesRushConfiguration); + const gitProcess: ChildProcess = {} as ChildProcess; + + jest.spyOn(git, 'getGitPathOrThrow').mockReturnValue(gitPath); + const spawnSpy: jest.SpyInstance = jest.spyOn(Executable, 'spawn').mockReturnValue(gitProcess); + jest + .spyOn(Executable, 'waitForExitAsync') + .mockResolvedValue( + createGitResult('commit 0123456789abcdef\nAuthor: Test Author \n') + ); + + await _updateCommitDetailsAsync(git, changeFilePath, changes); + + expect(spawnSpy).toHaveBeenCalledWith(gitPath, ['log', '-n', '1', '--', changeFilePath], { + currentWorkingDirectory: path.dirname(changeFilePath) + }); + expect(changes[0].commit).toEqual('0123456789abcdef'); + }); + + it.each([ + { exitCode: 1, signal: null }, + { exitCode: null, signal: 'SIGTERM' } + ])( + 'does not use Git output from an unsuccessful process ($exitCode, $signal)', + async ({ exitCode, signal }) => { + const changes: IChangeInfo[] = [{ packageName: 'd' }]; + const git: Git = new Git(packagesRushConfiguration); + const gitProcess: ChildProcess = {} as ChildProcess; + + jest.spyOn(git, 'getGitPathOrThrow').mockReturnValue(path.resolve('git.exe')); + jest.spyOn(Executable, 'spawn').mockReturnValue(gitProcess); + jest + .spyOn(Executable, 'waitForExitAsync') + .mockResolvedValue( + createGitResult( + 'commit 0123456789abcdef\nAuthor: Test Author \n', + exitCode, + signal + ) + ); + + await _updateCommitDetailsAsync(git, path.resolve('change.json'), changes); + + expect(changes).toEqual([{ packageName: 'd' }]); + } + ); + it('returns 1 change when changing a leaf package', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/leafChange`) + createChangeFiles(`${__dirname}/leafChange`) ); expect(allChanges.packageChanges.size).toEqual(1); @@ -109,11 +221,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('returns 6 changes when patching a root package', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/rootPatchChange`) + createChangeFiles(`${__dirname}/rootPatchChange`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -142,11 +255,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('returns 8 changes when hotfixing a root package', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/rootHotfixChange`) + createChangeFiles(`${__dirname}/rootHotfixChange`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -173,11 +287,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('returns 9 changes when major bumping a root package', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/rootMajorChange`) + createChangeFiles(`${__dirname}/rootMajorChange`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -206,11 +321,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('updates policy project dependencies when updating a lockstep version policy with no nextBump', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/lockstepWithoutNextBump`) + createChangeFiles(`${__dirname}/lockstepWithoutNextBump`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -239,11 +355,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('returns 2 changes when bumping cyclic dependencies', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/cyclicDeps`) + createChangeFiles(`${__dirname}/cyclicDeps`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -270,19 +387,21 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('returns error when mixing hotfix and non-hotfix changes', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; await expect( async () => await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/hotfixWithPatchChanges`) + createChangeFiles(`${__dirname}/hotfixWithPatchChanges`) ) ).rejects.toThrow('Cannot apply hotfix alongside patch change on same package'); }); it('returns error when adding hotfix with config disabled', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; // Overload hotfixChangeEnabled function (packagesRushConfiguration as unknown as Record).hotfixChangeEnabled = false; @@ -291,17 +410,18 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/rootHotfixChange`) + createChangeFiles(`${__dirname}/rootHotfixChange`) ) ).rejects.toThrow('Cannot add hotfix change; hotfixChangeEnabled is false in configuration.'); }); it('can resolve multiple changes requests on the same package', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/multipleChanges`) + createChangeFiles(`${__dirname}/multipleChanges`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -330,11 +450,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('can resolve multiple reverse-ordered changes requests on the same package', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/orderedChanges`) + createChangeFiles(`${__dirname}/orderedChanges`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -363,11 +484,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('can resolve multiple hotfix changes', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/multipleHotfixChanges`) + createChangeFiles(`${__dirname}/multipleHotfixChanges`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -394,11 +516,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('can update an explicit dependency', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/explicitVersionChange`) + createChangeFiles(`${__dirname}/explicitVersionChange`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -425,11 +548,11 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('can exclude lock step projects', async () => { - const allPackages: Map = repoRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = repoRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, repoRushConfiguration, - new ChangeFiles(`${__dirname}/repo/changes`), + createChangeFiles(`${__dirname}/repo/changes`), false, undefined, new Set(['a', 'b', 'e']) @@ -465,11 +588,11 @@ describe(PublishUtilities.sortChangeRequests.name, () => { }); it('can return a sorted array of the change requests to be published in the correct order', async () => { - const allPackages: Map = rushConfiguration.projectsByName; + const allPackages: ReadonlyMap = rushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, rushConfiguration, - new ChangeFiles(`${__dirname}/multipleChanges`) + createChangeFiles(`${__dirname}/multipleChanges`) ); const orderedChanges: IChangeInfo[] = PublishUtilities.sortChangeRequests(allChanges.packageChanges); @@ -553,11 +676,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('returns no changes in an empty change folder', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/noChange`) + createChangeFiles(`${__dirname}/noChange`) ); expect(allChanges.packageChanges.size).toEqual(0); @@ -565,11 +689,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('returns 1 change when changing a leaf package', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/leafChange`) + createChangeFiles(`${__dirname}/leafChange`) ); expect(allChanges.packageChanges.size).toEqual(1); @@ -580,11 +705,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('returns 6 changes when patching a root package', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/rootPatchChange`) + createChangeFiles(`${__dirname}/rootPatchChange`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -613,11 +739,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('returns 8 changes when hotfixing a root package', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/rootHotfixChange`) + createChangeFiles(`${__dirname}/rootHotfixChange`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -644,11 +771,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('returns 9 changes when major bumping a root package', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/rootMajorChange`) + createChangeFiles(`${__dirname}/rootMajorChange`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -677,11 +805,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('returns 2 changes when bumping cyclic dependencies', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/cyclicDeps`) + createChangeFiles(`${__dirname}/cyclicDeps`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -708,19 +837,21 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('returns error when mixing hotfix and non-hotfix changes', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; await expect( async () => await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/hotfixWithPatchChanges`) + createChangeFiles(`${__dirname}/hotfixWithPatchChanges`) ) ).rejects.toThrow('Cannot apply hotfix alongside patch change on same package'); }); it('returns error when adding hotfix with config disabled', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; // Overload hotfixChangeEnabled function (packagesRushConfiguration as unknown as Record).hotfixChangeEnabled = false; @@ -729,17 +860,18 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/rootHotfixChange`) + createChangeFiles(`${__dirname}/rootHotfixChange`) ) ).rejects.toThrow('Cannot add hotfix change; hotfixChangeEnabled is false in configuration.'); }); it('can resolve multiple changes requests on the same package', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/multipleChanges`) + createChangeFiles(`${__dirname}/multipleChanges`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -768,11 +900,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('can resolve multiple reverse-ordered changes requests on the same package', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/orderedChanges`) + createChangeFiles(`${__dirname}/orderedChanges`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -801,11 +934,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('can resolve multiple hotfix changes', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/multipleHotfixChanges`) + createChangeFiles(`${__dirname}/multipleHotfixChanges`) ); expect(generateChangeSnapshot(allPackages, allChanges)).toMatchInlineSnapshot(` @@ -832,11 +966,12 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('can update an explicit dependency', async () => { - const allPackages: Map = packagesRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = + packagesRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, packagesRushConfiguration, - new ChangeFiles(`${__dirname}/explicitVersionChange`) + createChangeFiles(`${__dirname}/explicitVersionChange`) ); expect(allChanges.packageChanges.size).toEqual(2); @@ -850,11 +985,11 @@ describe(PublishUtilities.findChangeRequestsAsync.name, () => { }); it('can exclude lock step projects', async () => { - const allPackages: Map = repoRushConfiguration.projectsByName; + const allPackages: ReadonlyMap = repoRushConfiguration.projectsByName; const allChanges: IChangeRequests = await PublishUtilities.findChangeRequestsAsync( allPackages, repoRushConfiguration, - new ChangeFiles(`${__dirname}/repo/changes`), + createChangeFiles(`${__dirname}/repo/changes`), false, undefined, new Set(['a', 'b', 'e']) @@ -888,7 +1023,9 @@ describe(PublishUtilities.getNewDependencyVersion.name, () => { a: 'workspace:~1.0.0', b: 'workspace:^1.0.0', c: 'workspace:>=1.0.0 <2.0.0', - d: 'workspace:*' + d: 'workspace:*', + e: 'workspace:~', + f: 'workspace:^' }; expect(PublishUtilities.getNewDependencyVersion(dependencies, 'a', '1.1.0')).toEqual('workspace:~1.1.0'); expect(PublishUtilities.getNewDependencyVersion(dependencies, 'b', '1.2.0')).toEqual('workspace:^1.2.0'); @@ -896,6 +1033,8 @@ describe(PublishUtilities.getNewDependencyVersion.name, () => { 'workspace:>=1.3.0 <2.0.0' ); expect(PublishUtilities.getNewDependencyVersion(dependencies, 'd', '1.4.0')).toEqual('workspace:*'); + expect(PublishUtilities.getNewDependencyVersion(dependencies, 'e', '1.5.0')).toEqual('workspace:~'); + expect(PublishUtilities.getNewDependencyVersion(dependencies, 'f', '1.6.0')).toEqual('workspace:^'); }); it('can update dependency versions with prereleases', () => { @@ -903,7 +1042,9 @@ describe(PublishUtilities.getNewDependencyVersion.name, () => { a: 'workspace:~1.0.0-pr.1', b: 'workspace:^1.0.0-pr.1', c: 'workspace:>=1.0.0-pr.1 <2.0.0', - d: 'workspace:*' + d: 'workspace:*', + e: 'workspace:~', + f: 'workspace:^' }; expect(PublishUtilities.getNewDependencyVersion(dependencies, 'a', '1.1.0-pr.1')).toEqual( 'workspace:~1.1.0-pr.1' @@ -915,6 +1056,8 @@ describe(PublishUtilities.getNewDependencyVersion.name, () => { 'workspace:>=1.3.0-pr.3 <2.0.0' ); expect(PublishUtilities.getNewDependencyVersion(dependencies, 'd', '1.3.0-pr.3')).toEqual('workspace:*'); + expect(PublishUtilities.getNewDependencyVersion(dependencies, 'e', '1.5.0-pr.3')).toEqual('workspace:~'); + expect(PublishUtilities.getNewDependencyVersion(dependencies, 'f', '1.6.0-pr.3')).toEqual('workspace:^'); }); it('can update to prerelease', () => { @@ -922,7 +1065,9 @@ describe(PublishUtilities.getNewDependencyVersion.name, () => { a: 'workspace:~1.0.0', b: 'workspace:^1.0.0', c: 'workspace:>=1.0.0 <2.0.0', - d: 'workspace:*' + d: 'workspace:*', + e: 'workspace:~', + f: 'workspace:^' }; expect(PublishUtilities.getNewDependencyVersion(dependencies, 'a', '1.0.0-hotfix.0')).toEqual( 'workspace:~1.0.0-hotfix.0' @@ -936,5 +1081,11 @@ describe(PublishUtilities.getNewDependencyVersion.name, () => { expect(PublishUtilities.getNewDependencyVersion(dependencies, 'd', '1.0.0-hotfix.0')).toEqual( 'workspace:*' ); + expect(PublishUtilities.getNewDependencyVersion(dependencies, 'e', '1.0.0-hotfix.0')).toEqual( + 'workspace:~' + ); + expect(PublishUtilities.getNewDependencyVersion(dependencies, 'f', '1.0.0-hotfix.0')).toEqual( + 'workspace:^' + ); }); }); diff --git a/libraries/rush-lib/src/logic/test/Selection.test.ts b/libraries/rush-lib/src/logic/test/Selection.test.ts index 977f761bb54..56da6544890 100644 --- a/libraries/rush-lib/src/logic/test/Selection.test.ts +++ b/libraries/rush-lib/src/logic/test/Selection.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IPartialProject, Selection } from '../Selection'; +import { type IPartialProject, Selection } from '../Selection'; const { union, intersection, expandAllDependencies, expandAllConsumers } = Selection; diff --git a/libraries/rush-lib/src/logic/test/ShrinkwrapFile.test.ts b/libraries/rush-lib/src/logic/test/ShrinkwrapFile.test.ts index 6bf41e360ed..88ddd366b1e 100644 --- a/libraries/rush-lib/src/logic/test/ShrinkwrapFile.test.ts +++ b/libraries/rush-lib/src/logic/test/ShrinkwrapFile.test.ts @@ -1,17 +1,27 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; +import { JsonFile } from '@rushstack/node-core-library'; -import { BaseShrinkwrapFile } from '../base/BaseShrinkwrapFile'; +import type { BaseShrinkwrapFile } from '../base/BaseShrinkwrapFile'; import { ShrinkwrapFileFactory } from '../ShrinkwrapFileFactory'; -import { parsePnpmDependencyKey, PnpmShrinkwrapFile } from '../pnpm/PnpmShrinkwrapFile'; +import { + parsePnpmDependencyKey, + PnpmShrinkwrapFile, + ShrinkwrapFileMajorVersion +} from '../pnpm/PnpmShrinkwrapFile'; import { DependencySpecifier } from '../DependencySpecifier'; import { NpmShrinkwrapFile } from '../npm/NpmShrinkwrapFile'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; describe(NpmShrinkwrapFile.name, () => { - const filename: string = `${__dirname}/shrinkwrapFile/npm-shrinkwrap.json`; - const shrinkwrapFile: BaseShrinkwrapFile = ShrinkwrapFileFactory.getShrinkwrapFile('npm', {}, filename)!; + const shrinkwrapFilePath: string = `${__dirname}/shrinkwrapFile/npm-shrinkwrap.json`; + const shrinkwrapFile: BaseShrinkwrapFile = ShrinkwrapFileFactory.getShrinkwrapFile({ + packageManager: 'npm', + shrinkwrapFilePath, + subspaceHasNoProjects: false + })!; it('verifies root-level dependency', () => { expect(shrinkwrapFile.hasCompatibleTopLevelDependency(new DependencySpecifier('q', '~1.5.0'))).toEqual( @@ -44,46 +54,227 @@ describe(NpmShrinkwrapFile.name, () => { }); describe(PnpmShrinkwrapFile.name, () => { - const filename: string = path.resolve(__dirname, '../../../src/logic/test/shrinkwrapFile/pnpm-lock.yaml'); - const shrinkwrapFile: BaseShrinkwrapFile = ShrinkwrapFileFactory.getShrinkwrapFile('pnpm', {}, filename)!; + describe('non-workspace', () => { + function validateNonWorkspaceLockfile(shrinkwrapFile: BaseShrinkwrapFile): void { + it('verifies root-level dependency', () => { + expect( + shrinkwrapFile.hasCompatibleTopLevelDependency(new DependencySpecifier('q', '~1.5.0')) + ).toEqual(false); + }); - it('verifies root-level dependency', () => { - expect(shrinkwrapFile.hasCompatibleTopLevelDependency(new DependencySpecifier('q', '~1.5.0'))).toEqual( - false - ); - }); + it('verifies temp project dependencies', () => { + expect( + shrinkwrapFile.tryEnsureCompatibleDependency( + new DependencySpecifier('jquery', '>=1.0.0 <2.0.0'), + '@rush-temp/project1' + ) + ).toEqual(true); + expect( + shrinkwrapFile.tryEnsureCompatibleDependency( + new DependencySpecifier('q', '~1.5.0'), + '@rush-temp/project2' + ) + ).toEqual(true); + expect( + shrinkwrapFile.tryEnsureCompatibleDependency( + new DependencySpecifier('pad-left', '^2.0.0'), + '@rush-temp/project1' + ) + ).toEqual(false); - it('verifies temp project dependencies', () => { - expect( - shrinkwrapFile.tryEnsureCompatibleDependency( - new DependencySpecifier('jquery', '>=2.0.0 <3.0.0'), - '@rush-temp/project1' - ) - ).toEqual(true); - expect( - shrinkwrapFile.tryEnsureCompatibleDependency( - new DependencySpecifier('q', '~1.5.0'), - '@rush-temp/project2' - ) - ).toEqual(true); - expect( - shrinkwrapFile.tryEnsureCompatibleDependency( - new DependencySpecifier('left-pad', '~9.9.9'), - '@rush-temp/project1' - ) - ).toEqual(false); - expect( - shrinkwrapFile.tryEnsureCompatibleDependency( - new DependencySpecifier('@scope/testDep', '>=1.0.0 <2.0.0'), - '@rush-temp/project3' - ) - ).toEqual(true); + if ( + shrinkwrapFile instanceof PnpmShrinkwrapFile && + shrinkwrapFile.shrinkwrapFileMajorVersion >= ShrinkwrapFileMajorVersion.V9 + ) { + expect( + shrinkwrapFile.tryEnsureCompatibleDependency( + new DependencySpecifier( + '@scope/testDep', + 'https://github.com/jonschlinkert/pad-left/tarball/2.1.0' + ), + '@rush-temp/project3' + ) + ).toEqual(true); + } else { + expect( + shrinkwrapFile.tryEnsureCompatibleDependency( + new DependencySpecifier('@scope/testDep', '>=2.0.0 <3.0.0'), + '@rush-temp/project3' + ) + ).toEqual(true); + } + }); + + it('extracts temp projects successfully', () => { + const tempProjectNames: ReadonlyArray = shrinkwrapFile.getTempProjectNames(); + + expect(tempProjectNames).toEqual([ + '@rush-temp/project1', + '@rush-temp/project2', + '@rush-temp/project3' + ]); + }); + } + + describe('V5.0 lockfile', () => { + const shrinkwrapFilePath: string = path.resolve( + __dirname, + '../../../src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v5.yaml' + ); + const shrinkwrapFile: BaseShrinkwrapFile = ShrinkwrapFileFactory.getShrinkwrapFile({ + packageManager: 'pnpm', + shrinkwrapFilePath, + subspaceHasNoProjects: false + })!; + + validateNonWorkspaceLockfile(shrinkwrapFile); + expect(shrinkwrapFile.isWorkspaceCompatible).toBe(false); + }); + + describe('V5.3 lockfile', () => { + const shrinkwrapFilePath: string = path.resolve( + __dirname, + '../../../src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v5.3.yaml' + ); + const shrinkwrapFile: BaseShrinkwrapFile = ShrinkwrapFileFactory.getShrinkwrapFile({ + packageManager: 'pnpm', + shrinkwrapFilePath, + subspaceHasNoProjects: false + })!; + + validateNonWorkspaceLockfile(shrinkwrapFile); + expect(shrinkwrapFile.isWorkspaceCompatible).toBe(false); + }); + + describe('V6.1 lockfile', () => { + const shrinkwrapFilePath: string = path.resolve( + __dirname, + '../../../src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v6.1.yaml' + ); + const shrinkwrapFile: BaseShrinkwrapFile = ShrinkwrapFileFactory.getShrinkwrapFile({ + packageManager: 'pnpm', + shrinkwrapFilePath, + subspaceHasNoProjects: false + })!; + + validateNonWorkspaceLockfile(shrinkwrapFile); + expect(shrinkwrapFile.isWorkspaceCompatible).toBe(false); + }); + + describe('V9 lockfile', () => { + const shrinkwrapFilePath: string = path.resolve( + __dirname, + '../../../src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v9.yaml' + ); + const shrinkwrapFile: BaseShrinkwrapFile = ShrinkwrapFileFactory.getShrinkwrapFile({ + packageManager: 'pnpm', + shrinkwrapFilePath, + subspaceHasNoProjects: false + })!; + validateNonWorkspaceLockfile(shrinkwrapFile); + expect(shrinkwrapFile.isWorkspaceCompatible).toBe(false); + }); }); - it('extracts temp projects successfully', () => { - const tempProjectNames: ReadonlyArray = shrinkwrapFile.getTempProjectNames(); + describe('workspace', () => { + let jsonSaveAsyncSpy: jest.SpyInstance; + beforeEach(() => { + jsonSaveAsyncSpy = jest.spyOn(JsonFile, 'saveAsync').mockReturnValue(Promise.resolve(true)); + }); + + afterEach(() => { + jsonSaveAsyncSpy.mockRestore(); + }); + + function validateWorkspaceLockfile(shrinkwrapFile: BaseShrinkwrapFile): void { + it('verifies project dependencies', async () => { + const projectNames: string[] = ['project1', 'project2', 'project3']; + for (const projectName of projectNames) { + jsonSaveAsyncSpy.mockClear(); + const rushConfigurationProject: RushConfigurationProject = { + projectRushTempFolder: `${projectName}/.rush/temp`, + projectFolder: projectName, + rushConfiguration: { + commonTempFolder: 'common/temp' + }, + subspace: { + getSubspaceTempFolderPath: () => 'common/temp' + } + } as RushConfigurationProject; + + const projectShrinkwrap = shrinkwrapFile.getProjectShrinkwrap(rushConfigurationProject); + await projectShrinkwrap?.updateProjectShrinkwrapAsync(); + expect(jsonSaveAsyncSpy).toHaveBeenCalledTimes(1); + expect(jsonSaveAsyncSpy.mock.calls).toMatchSnapshot(projectName); + } + }); + + it('does not have any temp projects', () => { + const tempProjectNames: ReadonlyArray = shrinkwrapFile.getTempProjectNames(); + expect(tempProjectNames).toHaveLength(0); + }); + } + + describe('V5.3 lockfile', () => { + const shrinkwrapFilePath: string = path.resolve( + __dirname, + '../../../src/logic/test/shrinkwrapFile/workspace-pnpm-lock-v5.3.yaml' + ); + const shrinkwrapFile: BaseShrinkwrapFile = ShrinkwrapFileFactory.getShrinkwrapFile({ + packageManager: 'pnpm', + shrinkwrapFilePath, + subspaceHasNoProjects: false + })!; + + validateWorkspaceLockfile(shrinkwrapFile); + expect(shrinkwrapFile.isWorkspaceCompatible).toBe(true); + }); + + describe('V6.1 lockfile', () => { + const shrinkwrapFilePath: string = path.resolve( + __dirname, + '../../../src/logic/test/shrinkwrapFile/workspace-pnpm-lock-v5.3.yaml' + ); + const shrinkwrapFile: BaseShrinkwrapFile = ShrinkwrapFileFactory.getShrinkwrapFile({ + packageManager: 'pnpm', + shrinkwrapFilePath, + subspaceHasNoProjects: false + })!; + + validateWorkspaceLockfile(shrinkwrapFile); + expect(shrinkwrapFile.isWorkspaceCompatible).toBe(true); + }); + + describe('V9 lockfile', () => { + const shrinkwrapFilePath: string = path.resolve( + __dirname, + '../../../src/logic/test/shrinkwrapFile/workspace-pnpm-lock-v9.yaml' + ); + + const shrinkwrapFile: BaseShrinkwrapFile = ShrinkwrapFileFactory.getShrinkwrapFile({ + packageManager: 'pnpm', + shrinkwrapFilePath, + subspaceHasNoProjects: false + })!; + + validateWorkspaceLockfile(shrinkwrapFile); + expect(shrinkwrapFile.isWorkspaceCompatible).toBe(true); + }); + + describe('V9 lockfile with no projects', () => { + const shrinkwrapFilePath: string = path.resolve( + __dirname, + '../../../src/logic/test/shrinkwrapFile/workspace-pnpm-lock-no-projects-v9.yaml' + ); + + const shrinkwrapFile: BaseShrinkwrapFile = ShrinkwrapFileFactory.getShrinkwrapFile({ + packageManager: 'pnpm', + shrinkwrapFilePath, + subspaceHasNoProjects: true + })!; - expect(tempProjectNames).toEqual(['@rush-temp/project1', '@rush-temp/project2', '@rush-temp/project3']); + expect(shrinkwrapFile.isWorkspaceCompatible).toBe(true); + }); }); }); diff --git a/libraries/rush-lib/src/logic/test/Telemetry.test.ts b/libraries/rush-lib/src/logic/test/Telemetry.test.ts index dc0c048c28e..aebc60ef4a9 100644 --- a/libraries/rush-lib/src/logic/test/Telemetry.test.ts +++ b/libraries/rush-lib/src/logic/test/Telemetry.test.ts @@ -1,11 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import { JsonFile } from '@rushstack/node-core-library'; +import { ConsoleTerminalProvider } from '@rushstack/terminal'; + import { RushConfiguration } from '../../api/RushConfiguration'; import { Rush } from '../../api/Rush'; -import { Telemetry, ITelemetryData, ITelemetryMachineInfo } from '../Telemetry'; +import { Telemetry, type ITelemetryData, type ITelemetryMachineInfo } from '../Telemetry'; import { RushSession } from '../../pluginFramework/RushSession'; -import { ConsoleTerminalProvider, JsonFile } from '@rushstack/node-core-library'; interface ITelemetryPrivateMembers extends Omit { _flushAsyncTasks: Map>; @@ -18,6 +20,8 @@ describe(Telemetry.name, () => { }); beforeEach(() => { + performance.clearMarks(); + performance.clearMeasures(); jest.clearAllMocks(); }); @@ -40,7 +44,8 @@ describe(Telemetry.name, () => { timestampMs: new Date().getTime(), platform: process.platform, rushVersion: Rush.version, - machineInfo: {} as ITelemetryMachineInfo + machineInfo: {} as ITelemetryMachineInfo, + performanceEntries: [] }; const logData2: ITelemetryData = { @@ -50,7 +55,8 @@ describe(Telemetry.name, () => { timestampMs: new Date().getTime(), platform: process.platform, rushVersion: Rush.version, - machineInfo: {} as ITelemetryMachineInfo + machineInfo: {} as ITelemetryMachineInfo, + performanceEntries: [] }; telemetry.log(logData1); @@ -94,7 +100,8 @@ describe(Telemetry.name, () => { timestampMs: new Date().getTime(), platform: process.platform, rushVersion: Rush.version, - machineInfo: {} as ITelemetryMachineInfo + machineInfo: {} as ITelemetryMachineInfo, + performanceEntries: [] }; telemetry.log(logData); diff --git a/libraries/rush-lib/src/logic/test/VersionManager.test.ts b/libraries/rush-lib/src/logic/test/VersionManager.test.ts index 7fa4dddddd0..259ec579a05 100644 --- a/libraries/rush-lib/src/logic/test/VersionManager.test.ts +++ b/libraries/rush-lib/src/logic/test/VersionManager.test.ts @@ -1,13 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IPackageJson } from '@rushstack/node-core-library'; +import type { IPackageJson } from '@rushstack/node-core-library'; import { BumpType } from '../../api/VersionPolicy'; -import { ChangeFile } from '../../api/ChangeFile'; -import { ChangeType, IChangeInfo } from '../../api/ChangeManagement'; +import type { ChangeFile } from '../../api/ChangeFile'; +import { ChangeType, type IChangeInfo } from '../../api/ChangeManagement'; import { RushConfiguration } from '../../api/RushConfiguration'; import { VersionManager } from '../VersionManager'; +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; function _getChanges(changeFiles: Map, packageName: string): IChangeInfo[] | undefined { const changeFile: ChangeFile | undefined = changeFiles.get(packageName); @@ -22,12 +23,22 @@ describe(VersionManager.name, () => { const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); let versionManager: VersionManager; + let terminalProvider: StringBufferTerminalProvider; + let terminal: Terminal; + beforeEach(() => { versionManager = new VersionManager( rushConfiguration, 'test@microsoft.com', rushConfiguration.versionPolicyConfiguration ); + + terminalProvider = new StringBufferTerminalProvider(); + terminal = new Terminal(terminalProvider); + }); + + afterEach(() => { + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); }); /* eslint-disable dot-notation */ @@ -88,7 +99,7 @@ describe(VersionManager.name, () => { describe(VersionManager.prototype.bumpAsync.name, () => { it('bumps a lockStepPolicy to prerelease version', async () => { - await versionManager.bumpAsync('testPolicy1', BumpType.prerelease, 'dev', false); + await versionManager.bumpAsync(terminal, 'testPolicy1', BumpType.prerelease, 'dev', false); const updatedPackages: Map = versionManager.updatedProjects; const changeFiles: Map = versionManager.changeFiles; @@ -102,7 +113,7 @@ describe(VersionManager.name, () => { }); it('bumps a lockStepPolicy without bumpType to prerelease version', async () => { - await versionManager.bumpAsync('lockStepWithoutNextBump', BumpType.prerelease, 'dev', false); + await versionManager.bumpAsync(terminal, 'lockStepWithoutNextBump', BumpType.prerelease, 'dev', false); const updatedPackages: Map = versionManager.updatedProjects; const changeFiles: Map = versionManager.changeFiles; @@ -120,12 +131,22 @@ describe(`${VersionManager.name} (workspace)`, () => { const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFile); let versionManager: VersionManager; + let terminalProvider: StringBufferTerminalProvider; + let terminal: Terminal; + beforeEach(() => { versionManager = new VersionManager( rushConfiguration, 'test@microsoft.com', rushConfiguration.versionPolicyConfiguration ); + + terminalProvider = new StringBufferTerminalProvider(); + terminal = new Terminal(terminalProvider); + }); + + afterEach(() => { + expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); }); /* eslint-disable dot-notation */ @@ -186,7 +207,7 @@ describe(`${VersionManager.name} (workspace)`, () => { describe(VersionManager.prototype.bumpAsync.name, () => { it('bumps to prerelease version', async () => { - await versionManager.bumpAsync('testPolicy1', BumpType.prerelease, 'dev', false); + await versionManager.bumpAsync(terminal, 'testPolicy1', BumpType.prerelease, 'dev', false); const updatedPackages: Map = versionManager.updatedProjects; const expectedVersion: string = '10.10.1-dev.0'; diff --git a/libraries/rush-lib/src/logic/test/WorkspaceCycleDetector.test.ts b/libraries/rush-lib/src/logic/test/WorkspaceCycleDetector.test.ts new file mode 100644 index 00000000000..66f1c422d36 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/WorkspaceCycleDetector.test.ts @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { RushConfiguration } from '../../api/RushConfiguration'; +import { _findWorkspaceCycle } from '../WorkspaceCycleDetector'; + +describe(_findWorkspaceCycle.name, () => { + function loadProjectsFromRepo(repoName: string): RushConfiguration['projects'] { + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile( + path.resolve(__dirname, `workspaceCycleDetector/${repoName}/rush.json`) + ); + return rushConfiguration.projects; + } + + it('returns undefined when there are no cycles', () => { + const projects: RushConfiguration['projects'] = loadProjectsFromRepo('no-cycle'); + const result: ReadonlyArray | undefined = _findWorkspaceCycle(projects); + expect(result).toBeUndefined(); + }); + + it('returns the cycle path when an undeclared cycle is present', () => { + const projects: RushConfiguration['projects'] = loadProjectsFromRepo('with-cycle'); + const result: ReadonlyArray | undefined = _findWorkspaceCycle(projects); + expect(result).toBeDefined(); + // The cycle should form a closed loop: [..., pkg-a] or [..., pkg-b] depending on + // iteration order. Either way the first and last element must be the same package, + // and both pkg-a and pkg-b must appear in the cycle. + expect(result!.length).toBeGreaterThanOrEqual(2); + expect(result![0]).toBe(result![result!.length - 1]); + const uniqueNames: Set = new Set(result); + expect(uniqueNames.has('pkg-a')).toBe(true); + expect(uniqueNames.has('pkg-b')).toBe(true); + }); + + it('returns undefined when the cycle is intentionally broken with decoupledLocalDependencies', () => { + const projects: RushConfiguration['projects'] = loadProjectsFromRepo('decoupled-cycle'); + const result: ReadonlyArray | undefined = _findWorkspaceCycle(projects); + expect(result).toBeUndefined(); + }); + + it('returns the cycle path when using the workspacePackages test repo (cyclic-dep-1 <-> cyclic-dep-2)', () => { + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile( + path.resolve(__dirname, 'workspacePackages/rush.json') + ); + const result: ReadonlyArray | undefined = _findWorkspaceCycle(rushConfiguration.projects); + expect(result).toBeDefined(); + expect(result![0]).toBe(result![result!.length - 1]); + const uniqueNames: Set = new Set(result); + expect(uniqueNames.has('cyclic-dep-1')).toBe(true); + expect(uniqueNames.has('cyclic-dep-2')).toBe(true); + }); +}); diff --git a/libraries/rush-lib/src/logic/test/__snapshots__/ChangeFiles.test.ts.snap b/libraries/rush-lib/src/logic/test/__snapshots__/ChangeFiles.test.ts.snap new file mode 100644 index 00000000000..afe951c5462 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/__snapshots__/ChangeFiles.test.ts.snap @@ -0,0 +1,115 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`ChangeFiles deleteAllAsync delete all files when there are hotfixes 1`] = ` +Array [ + "[ log] [n]", + "[ log] * DRYRUN: Deleting 3 change file(s).[n]", + "[ log] - /multipleHotfixChanges/change1.json[n]", + "[ log] - /multipleHotfixChanges/change2.json[n]", + "[ log] - /multipleHotfixChanges/change3.json[n]", +] +`; + +exports[`ChangeFiles deleteAllAsync delete all files when there are no prerelease packages 1`] = ` +Array [ + "[ log] [n]", + "[ log] * DRYRUN: Deleting 3 change file(s).[n]", + "[ log] - /multipleChangeFiles/a.json[n]", + "[ log] - /multipleChangeFiles/b.json[n]", + "[ log] - /multipleChangeFiles/c.json[n]", +] +`; + +exports[`ChangeFiles deleteAllAsync does not delete change files for package whose change logs do not get updated. 1`] = ` +Array [ + "[ log] [n]", + "[ log] * DRYRUN: Deleting 2 change file(s).[n]", + "[ log] - /multipleChangeFiles/a.json[n]", + "[ log] - /multipleChangeFiles/b.json[n]", +] +`; + +exports[`ChangeFiles getAllChangeFilesAsync returns correctly when change files are categorized 1`] = `Array []`; + +exports[`ChangeFiles getAllChangeFilesAsync returns correctly when there is one change file 1`] = `Array []`; + +exports[`ChangeFiles getAllChangeFilesAsync returns empty array when no change files 1`] = `Array []`; + +exports[`ChangeFiles validateAsync allows a hotfix in a hotfix branch. 1`] = ` +Array [ + "[ log] Found change file: /multipleHotfixChanges/change1.json[n]", +] +`; + +exports[`ChangeFiles validateAsync does not throw when no missing packages from categorized changes 1`] = ` +Array [ + "[ log] Found change file: /categorizedChanges/@ms/a/changeA.json[n]", + "[ log] Found change file: /categorizedChanges/@ms/b/changeB.json[n]", + "[ log] Found change file: /categorizedChanges/changeC.json[n]", +] +`; + +exports[`ChangeFiles validateAsync does not throw when there is no missing packages 1`] = ` +Array [ + "[ log] Found change file: /verifyChanges/changes.json[n]", +] +`; + +exports[`ChangeFiles validateAsync throws when missing packages from categorized changes 1`] = ` +Array [ + "[ log] Found change file: /categorizedChanges/@ms/a/changeA.json[n]", + "[ log] Found change file: /categorizedChanges/@ms/b/changeB.json[n]", +] +`; + +exports[`ChangeFiles validateAsync throws when there is a patch in a hotfix branch. 1`] = ` +Array [ + "[ log] Found change file: /leafChange/change1.json[n]", +] +`; + +exports[`ChangeFiles validateAsync throws when there is any missing package. 1`] = ` +Array [ + "[ log] Found change file: /verifyChanges/changes.json[n]", +] +`; + +exports[`ChangeFiles validateAsync with strictChangefileValidation does not throw when change file references a lockstep project with no mainProject 1`] = ` +Array [ + "[ log] Found change file: /strictValidation/mainLockstep.json[n]", +] +`; + +exports[`ChangeFiles validateAsync with strictChangefileValidation does not throw when change file references the main lockstep project 1`] = ` +Array [ + "[ log] Found change file: /strictValidation/mainLockstep.json[n]", +] +`; + +exports[`ChangeFiles validateAsync with strictChangefileValidation does not throw when experiment is disabled 1`] = ` +Array [ + "[ log] Found change file: /strictValidation/nonexistentProject.json[n]", +] +`; + +exports[`ChangeFiles validateAsync with strictChangefileValidation throws when change file references a non-main lockstep project 1`] = ` +"Change file(s) reference the project \\"lockstep-secondary\\" which belongs to lockstepped version policy \\"myLockstep\\". Change files should be created for the policy's main project \\"lockstep-main\\" instead: + - /strictValidation/nonMainLockstep.json" +`; + +exports[`ChangeFiles validateAsync with strictChangefileValidation throws when change file references a non-main lockstep project 2`] = ` +Array [ + "[ log] Found change file: /strictValidation/nonMainLockstep.json[n]", +] +`; + +exports[`ChangeFiles validateAsync with strictChangefileValidation throws when change file references a nonexistent project 1`] = ` +"Change file(s) reference a project \\"nonexistent-package\\" that does not exist in the Rush configuration: + - /strictValidation/nonexistentProject.json" +`; + +exports[`ChangeFiles validateAsync with strictChangefileValidation throws when change file references a nonexistent project 2`] = ` +Array [ + "[ log] Found change file: /strictValidation/nonexistentProject.json[n]", +] +`; diff --git a/libraries/rush-lib/src/logic/test/__snapshots__/InstallHelpers.test.ts.snap b/libraries/rush-lib/src/logic/test/__snapshots__/InstallHelpers.test.ts.snap new file mode 100644 index 00000000000..002fcde9038 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/__snapshots__/InstallHelpers.test.ts.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`InstallHelpers generateCommonPackageJsonAsync does not emit the global pnpmfile via pnpm-workspace.yaml for pnpm < 11: Terminal Output 1`] = `Array []`; + +exports[`InstallHelpers generateCommonPackageJsonAsync does not generate a "pnpm" field for pnpm 11 (all settings belong in pnpm-workspace.yaml): Terminal Output 1`] = `Array []`; + +exports[`InstallHelpers generateCommonPackageJsonAsync emits the subspace global pnpmfile path via pnpm-workspace.yaml for pnpm 11: Terminal Output 1`] = `Array []`; + +exports[`InstallHelpers generateCommonPackageJsonAsync generates correct package json with pnpm configurations 1`] = ` +Object { + "dependencies": Object {}, + "description": "Temporary file generated by the Rush tool", + "name": "rush-common", + "pnpm": Object { + "allowedDeprecatedVersions": Object { + "request": "*", + }, + "neverBuiltDependencies": Array [ + "fsevents", + "level", + ], + "onlyBuiltDependencies": Array [ + "esbuild", + "playwright", + ], + "overrides": Object { + "bar@^2.1.0": "3.0.0", + "foo": "^2.0.0", + "qar@1>zoo": "2", + "quux": "npm:@myorg/quux@^1.0.0", + }, + "packageExtensions": Object { + "react-redux": Object { + "peerDependencies": Object { + "react-dom": "*", + }, + }, + }, + "patchedDependencies": Object { + "lodash@4.17.21": "patches/lodash@4.17.21.patch", + }, + "peerDependencyRules": Object { + "allowedVersions": Object { + "react": "18", + }, + "ignoreMissing": Array [ + "@babel/core", + ], + }, + "pnpmFutureFeature": true, + }, + "private": true, + "version": "0.0.0", +} +`; + +exports[`InstallHelpers generateCommonPackageJsonAsync generates correct package json with pnpm configurations: Terminal Output 1`] = `Array []`; diff --git a/libraries/rush-lib/src/logic/test/__snapshots__/ProjectChangeAnalyzer.test.ts.snap b/libraries/rush-lib/src/logic/test/__snapshots__/ProjectChangeAnalyzer.test.ts.snap deleted file mode 100644 index bc265de44ee..00000000000 --- a/libraries/rush-lib/src/logic/test/__snapshots__/ProjectChangeAnalyzer.test.ts.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ProjectChangeAnalyzer _tryGetProjectDependenciesAsync throws an exception if the specified project does not exist 1`] = `[Error: Project "carrot" does not exist in the current Rush configuration.]`; diff --git a/libraries/rush-lib/src/logic/test/__snapshots__/ProjectImpactGraphGenerator.test.ts.snap b/libraries/rush-lib/src/logic/test/__snapshots__/ProjectImpactGraphGenerator.test.ts.snap new file mode 100644 index 00000000000..5c9e9869f4a --- /dev/null +++ b/libraries/rush-lib/src/logic/test/__snapshots__/ProjectImpactGraphGenerator.test.ts.snap @@ -0,0 +1,275 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`ProjectImpactGraphGenerator generateAsync Correctly generates a project impact graph (repo: ""packages""): Output file data 1`] = ` +"globalExcludedGlobs: + - common/autoinstallers/** +projects: + a: + includedGlobs: + - a/** + dependentProjects: + - a + - b + - e + - g + - h + b: + includedGlobs: + - b/** + dependentProjects: + - b + - c + - f + c: + includedGlobs: + - c/** + dependentProjects: + - c + - d + cyclic-dep-1: + includedGlobs: + - cyclic-dep-1/** + dependentProjects: + - cyclic-dep-1 + - cyclic-dep-2 + cyclic-dep-2: + includedGlobs: + - cyclic-dep-2/** + dependentProjects: + - cyclic-dep-1 + - cyclic-dep-2 + cyclic-dep-explicit-1: + includedGlobs: + - cyclic-dep-explicit-1/** + dependentProjects: + - cyclic-dep-explicit-1 + cyclic-dep-explicit-2: + includedGlobs: + - cyclic-dep-explicit-2/** + dependentProjects: + - cyclic-dep-explicit-1 + - cyclic-dep-explicit-2 + d: + includedGlobs: + - d/** + dependentProjects: + - d + e: + includedGlobs: + - e/** + dependentProjects: + - e + f: + includedGlobs: + - f/** + dependentProjects: + - f + g: + includedGlobs: + - g/** + dependentProjects: + - g + h: + includedGlobs: + - h/** + dependentProjects: + - f + - h + i: + includedGlobs: + - i/** + dependentProjects: + - i + - j + j: + includedGlobs: + - j/** + dependentProjects: + - j +" +`; + +exports[`ProjectImpactGraphGenerator generateAsync Correctly generates a project impact graph (repo: ""packages""): Output file path 1`] = `"/project-impact-graph.yaml"`; + +exports[`ProjectImpactGraphGenerator generateAsync Correctly generates a project impact graph (repo: ""packages""): Terminal Output 1`] = ` +Array [ + "[ log] [n]", + "[ log] [green]Generate project impact graph successfully. (1.50 seconds)[default][n]", +] +`; + +exports[`ProjectImpactGraphGenerator generateAsync Correctly generates a project impact graph (repo: ""repo""): Output file data 1`] = ` +"globalExcludedGlobs: + - common/autoinstallers/** +projects: + a: + includedGlobs: + - a/** + dependentProjects: + - a + - b + - f + - g + - h + b: + includedGlobs: + - b/** + dependentProjects: + - b + - c + - d + c: + includedGlobs: + - c/** + dependentProjects: + - c + - e + d: + includedGlobs: + - d/** + dependentProjects: + - d + e: + includedGlobs: + - e/** + dependentProjects: + - e + f: + includedGlobs: + - f/** + dependentProjects: + - f + g: + includedGlobs: + - g/** + dependentProjects: + - g + h: + includedGlobs: + - h/** + dependentProjects: + - f + - h + i: + includedGlobs: + - i/** + dependentProjects: + - i + j: + includedGlobs: + - j/** + dependentProjects: + - j +" +`; + +exports[`ProjectImpactGraphGenerator generateAsync Correctly generates a project impact graph (repo: ""repo""): Output file path 1`] = `"/project-impact-graph.yaml"`; + +exports[`ProjectImpactGraphGenerator generateAsync Correctly generates a project impact graph (repo: ""repo""): Terminal Output 1`] = ` +Array [ + "[ log] [n]", + "[ log] [green]Generate project impact graph successfully. (1.50 seconds)[default][n]", +] +`; + +exports[`ProjectImpactGraphGenerator generateAsync Correctly generates a project impact graph (repo: ""workspacePackages""): Output file data 1`] = ` +"globalExcludedGlobs: + - common/config/version-policies.json +projects: + a: + includedGlobs: + - a/** + dependentProjects: + - a + - b + - e + - g + - h + b: + includedGlobs: + - b/** + dependentProjects: + - b + - c + - f + c: + includedGlobs: + - c/** + dependentProjects: + - c + - d + cyclic-dep-1: + includedGlobs: + - cyclic-dep-1/** + dependentProjects: + - cyclic-dep-1 + - cyclic-dep-2 + cyclic-dep-2: + includedGlobs: + - cyclic-dep-2/** + dependentProjects: + - cyclic-dep-1 + - cyclic-dep-2 + cyclic-dep-explicit-1: + includedGlobs: + - cyclic-dep-explicit-1/** + dependentProjects: + - cyclic-dep-explicit-1 + cyclic-dep-explicit-2: + includedGlobs: + - cyclic-dep-explicit-2/** + dependentProjects: + - cyclic-dep-explicit-1 + - cyclic-dep-explicit-2 + d: + includedGlobs: + - d/** + dependentProjects: + - d + e: + includedGlobs: + - e/** + dependentProjects: + - e + excludedGlobs: + - e/src/** + f: + includedGlobs: + - f/** + dependentProjects: + - f + g: + includedGlobs: + - g/** + dependentProjects: + - g + h: + includedGlobs: + - h/** + dependentProjects: + - f + - h + i: + includedGlobs: + - i/** + dependentProjects: + - i + - j + j: + includedGlobs: + - j/** + dependentProjects: + - j +" +`; + +exports[`ProjectImpactGraphGenerator generateAsync Correctly generates a project impact graph (repo: ""workspacePackages""): Output file path 1`] = `"/project-impact-graph.yaml"`; + +exports[`ProjectImpactGraphGenerator generateAsync Correctly generates a project impact graph (repo: ""workspacePackages""): Terminal Output 1`] = ` +Array [ + "[ log] [n]", + "[ log] [green]Generate project impact graph successfully. (1.50 seconds)[default][n]", +] +`; + +exports[`ProjectImpactGraphGenerator validateAsync Reports if the project-impact-graph.yaml file is missing (repo: ""workspacePackages""): Terminal Output 1`] = `Array []`; diff --git a/libraries/rush-lib/src/logic/test/__snapshots__/ShrinkwrapFile.test.ts.snap b/libraries/rush-lib/src/logic/test/__snapshots__/ShrinkwrapFile.test.ts.snap new file mode 100644 index 00000000000..fad47cbc064 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/__snapshots__/ShrinkwrapFile.test.ts.snap @@ -0,0 +1,151 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`PnpmShrinkwrapFile workspace V5.3 lockfile verifies project dependencies: project1 1`] = ` +Array [ + Array [ + Object { + "../../project1": "../../project1:D5ar2j+w6/zH15/eOoF37Nkdbamt2tX47iijyj7LVXk=:", + "/jquery/1.12.3": "/jquery/1.12.3:Y74h7210GWDRLFidqe7W0rGD9dEVjPuIpExxLG3ql7U=:", + "/pad-left/1.0.2": "/pad-left/1.0.2:fNuxq+VtdNt2R9HJ6ip7x62AjQvQK7tiTHVLF6JGjpE=:", + "/repeat-string/1.6.1": "/repeat-string/1.6.1:FSrgyzed38htiD39oWRz9lAr9wD9AxuRmBW5tC28Jvc=:", + }, + "project1/.rush/temp/shrinkwrap-deps.json", + Object { + "ensureFolderExists": true, + }, + ], +] +`; + +exports[`PnpmShrinkwrapFile workspace V5.3 lockfile verifies project dependencies: project2 1`] = ` +Array [ + Array [ + Object { + "../../project2": "../../project2:PQ2FvyHHwmt/FIUaiJBVAfpHv6hj9EGJrAw69+0IY50=:", + "/jquery/2.2.4": "/jquery/2.2.4:PIfhtCRWsOQOCNHXI0s+2Ssbxc/U0IZ1ZXD+YcrHwi4=:", + "/q/1.5.1": "/q/1.5.1:A6WReS0f6nc67cF0NQHN3YGoUP6sLNfcWK/7orfz1J4=:", + }, + "project2/.rush/temp/shrinkwrap-deps.json", + Object { + "ensureFolderExists": true, + }, + ], +] +`; + +exports[`PnpmShrinkwrapFile workspace V5.3 lockfile verifies project dependencies: project3 1`] = ` +Array [ + Array [ + Object { + "../../project3": "../../project3:jomsZKvXG32qqYOfW3HUBGfWWSw6ybFV1WDf9c/kiP4=:", + "/q/1.5.1": "/q/1.5.1:A6WReS0f6nc67cF0NQHN3YGoUP6sLNfcWK/7orfz1J4=:", + "/repeat-string/1.6.1": "/repeat-string/1.6.1:FSrgyzed38htiD39oWRz9lAr9wD9AxuRmBW5tC28Jvc=:", + "example.pkgs.visualstudio.com/@scope/testDep/2.1.0": "example.pkgs.visualstudio.com/@scope/testDep/2.1.0:i5jbUOp/IxUgiN0dHYsTRzmimOVBwu7f9908rRaC9VY=:", + }, + "project3/.rush/temp/shrinkwrap-deps.json", + Object { + "ensureFolderExists": true, + }, + ], +] +`; + +exports[`PnpmShrinkwrapFile workspace V6.1 lockfile verifies project dependencies: project1 1`] = ` +Array [ + Array [ + Object { + "../../project1": "../../project1:D5ar2j+w6/zH15/eOoF37Nkdbamt2tX47iijyj7LVXk=:", + "/jquery/1.12.3": "/jquery/1.12.3:Y74h7210GWDRLFidqe7W0rGD9dEVjPuIpExxLG3ql7U=:", + "/pad-left/1.0.2": "/pad-left/1.0.2:fNuxq+VtdNt2R9HJ6ip7x62AjQvQK7tiTHVLF6JGjpE=:", + "/repeat-string/1.6.1": "/repeat-string/1.6.1:FSrgyzed38htiD39oWRz9lAr9wD9AxuRmBW5tC28Jvc=:", + }, + "project1/.rush/temp/shrinkwrap-deps.json", + Object { + "ensureFolderExists": true, + }, + ], +] +`; + +exports[`PnpmShrinkwrapFile workspace V6.1 lockfile verifies project dependencies: project2 1`] = ` +Array [ + Array [ + Object { + "../../project2": "../../project2:PQ2FvyHHwmt/FIUaiJBVAfpHv6hj9EGJrAw69+0IY50=:", + "/jquery/2.2.4": "/jquery/2.2.4:PIfhtCRWsOQOCNHXI0s+2Ssbxc/U0IZ1ZXD+YcrHwi4=:", + "/q/1.5.1": "/q/1.5.1:A6WReS0f6nc67cF0NQHN3YGoUP6sLNfcWK/7orfz1J4=:", + }, + "project2/.rush/temp/shrinkwrap-deps.json", + Object { + "ensureFolderExists": true, + }, + ], +] +`; + +exports[`PnpmShrinkwrapFile workspace V6.1 lockfile verifies project dependencies: project3 1`] = ` +Array [ + Array [ + Object { + "../../project3": "../../project3:jomsZKvXG32qqYOfW3HUBGfWWSw6ybFV1WDf9c/kiP4=:", + "/q/1.5.1": "/q/1.5.1:A6WReS0f6nc67cF0NQHN3YGoUP6sLNfcWK/7orfz1J4=:", + "/repeat-string/1.6.1": "/repeat-string/1.6.1:FSrgyzed38htiD39oWRz9lAr9wD9AxuRmBW5tC28Jvc=:", + "example.pkgs.visualstudio.com/@scope/testDep/2.1.0": "example.pkgs.visualstudio.com/@scope/testDep/2.1.0:i5jbUOp/IxUgiN0dHYsTRzmimOVBwu7f9908rRaC9VY=:", + }, + "project3/.rush/temp/shrinkwrap-deps.json", + Object { + "ensureFolderExists": true, + }, + ], +] +`; + +exports[`PnpmShrinkwrapFile workspace V9 lockfile verifies project dependencies: project1 1`] = ` +Array [ + Array [ + Object { + "../../project1": "../../project1:6yFTI2g+Ny0Au80xpo6zIY61TCNDUuLUd6EgLlbOBtc=:", + "jquery@1.12.3": "jquery@1.12.3:nkmD9jsJt8eUeR2cOltYXX5TFnlK30nBsVeOz047iPo=:", + "pad-left@1.0.2": "pad-left@1.0.2:R80aACjIFOqWnDG/5JgPO4SM4jLta89Xjp5el1RQm+g=:", + "repeat-string@1.6.1": "repeat-string@1.6.1:YqQsoCDmP4kj4raEmb5SYE4GsoFGxpBoRbOA/U9rqB4=:", + }, + "project1/.rush/temp/shrinkwrap-deps.json", + Object { + "ensureFolderExists": true, + }, + ], +] +`; + +exports[`PnpmShrinkwrapFile workspace V9 lockfile verifies project dependencies: project2 1`] = ` +Array [ + Array [ + Object { + "../../project2": "../../project2:l6v/HWUhScMI0m4k6D5qHiCOFj3Z0GoIFJEcp4I63w0=:", + "jquery@2.2.4": "jquery@2.2.4:e3VqitHw5v+hfYoCAwnNmSwfWqvOCOLGdwIKR1fzqhM=:", + "q@1.5.0": "q@1.5.0:lSldncUZjX1nP6wk6WAWwPXA6wLli1dIBnuA3SPeIE4=:", + }, + "project2/.rush/temp/shrinkwrap-deps.json", + Object { + "ensureFolderExists": true, + }, + ], +] +`; + +exports[`PnpmShrinkwrapFile workspace V9 lockfile verifies project dependencies: project3 1`] = ` +Array [ + Array [ + Object { + "../../project3": "../../project3:vMoje8cXfsHYOc6EXbxEw/qyBGXGUL1RApmNfwl7oA8=:", + "pad-left@https://github.com/jonschlinkert/pad-left/tarball/2.1.0": "pad-left@https://github.com/jonschlinkert/pad-left/tarball/2.1.0:bKrL+SvVYubL0HwTq/GOOXq1d05LTQ+HGqlXabzGEAU=:", + "q@1.5.0": "q@1.5.0:lSldncUZjX1nP6wk6WAWwPXA6wLli1dIBnuA3SPeIE4=:", + "repeat-string@1.6.1": "repeat-string@1.6.1:YqQsoCDmP4kj4raEmb5SYE4GsoFGxpBoRbOA/U9rqB4=:", + }, + "project3/.rush/temp/shrinkwrap-deps.json", + Object { + "ensureFolderExists": true, + }, + ], +] +`; diff --git a/libraries/rush-lib/src/logic/test/__snapshots__/VersionManager.test.ts.snap b/libraries/rush-lib/src/logic/test/__snapshots__/VersionManager.test.ts.snap new file mode 100644 index 00000000000..28fa39d9be8 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/__snapshots__/VersionManager.test.ts.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`VersionManager (workspace) bumpAsync bumps to prerelease version 1`] = `Array []`; + +exports[`VersionManager (workspace) ensure does not change packageJson if not needed by individual version policy 1`] = `Array []`; + +exports[`VersionManager (workspace) ensure fixes lock step versions 1`] = `Array []`; + +exports[`VersionManager (workspace) ensure fixes major version for individual version policy 1`] = `Array []`; + +exports[`VersionManager bumpAsync bumps a lockStepPolicy to prerelease version 1`] = `Array []`; + +exports[`VersionManager bumpAsync bumps a lockStepPolicy without bumpType to prerelease version 1`] = `Array []`; + +exports[`VersionManager ensure does not change packageJson if not needed by individual version policy 1`] = `Array []`; + +exports[`VersionManager ensure fixes lock step versions 1`] = `Array []`; + +exports[`VersionManager ensure fixes major version for individual version policy 1`] = `Array []`; diff --git a/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/a/config/rush-project.json b/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/a/config/rush-project.json new file mode 100644 index 00000000000..103f06da2e0 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/a/config/rush-project.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ + { + "operationName": "build", + "parameterNamesToIgnore": ["--production"] + } + ] +} diff --git a/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/a/package.json b/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/a/package.json new file mode 100644 index 00000000000..d77de36cc7b --- /dev/null +++ b/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/a/package.json @@ -0,0 +1,7 @@ +{ + "name": "a", + "version": "1.0.0", + "scripts": { + "build": "echo building a" + } +} diff --git a/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/b/config/rush-project.json b/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/b/config/rush-project.json new file mode 100644 index 00000000000..e6f27ab1857 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/b/config/rush-project.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "operationSettings": [ + { + "operationName": "build", + "parameterNamesToIgnore": ["--verbose", "--config", "--mode", "--tags"] + } + ] +} diff --git a/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/b/package.json b/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/b/package.json new file mode 100644 index 00000000000..41a4b66e358 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/b/package.json @@ -0,0 +1,7 @@ +{ + "name": "b", + "version": "1.0.0", + "scripts": { + "build": "echo building b" + } +} diff --git a/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/common/config/rush/command-line.json b/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/common/config/rush/command-line.json new file mode 100644 index 00000000000..0f2df3fd189 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/common/config/rush/command-line.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json", + "commands": [], + "parameters": [ + { + "longName": "--production", + "description": "A production flag", + "parameterKind": "flag", + "associatedCommands": ["build"] + }, + { + "longName": "--verbose", + "description": "A verbose flag", + "parameterKind": "flag", + "associatedCommands": ["build"] + }, + { + "longName": "--config", + "description": "Config file path", + "parameterKind": "string", + "argumentName": "PATH", + "associatedCommands": ["build"] + }, + { + "longName": "--mode", + "description": "Build mode", + "parameterKind": "choice", + "alternatives": [ + { + "name": "dev", + "description": "Development mode" + }, + { + "name": "prod", + "description": "Production mode" + } + ], + "associatedCommands": ["build"] + }, + { + "longName": "--tags", + "description": "Build tags", + "parameterKind": "stringList", + "argumentName": "TAG", + "associatedCommands": ["build"] + } + ] +} diff --git a/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/rush.json b/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/rush.json new file mode 100644 index 00000000000..529024b893c --- /dev/null +++ b/libraries/rush-lib/src/logic/test/parameterIgnoringRepo/rush.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush.schema.json", + "rushVersion": "5.162.0", + "pnpmVersion": "8.15.9", + "nodeSupportedVersionRange": ">=18.0.0", + "projects": [ + { + "packageName": "a", + "projectFolder": "a" + }, + { + "packageName": "b", + "projectFolder": "b" + } + ] +} diff --git a/libraries/rush-lib/src/logic/test/pnpmConfig/common/config/rush/pnpm-config.json b/libraries/rush-lib/src/logic/test/pnpmConfig/common/config/rush/pnpm-config.json index b5d6f9baba8..7b1bf486144 100644 --- a/libraries/rush-lib/src/logic/test/pnpmConfig/common/config/rush/pnpm-config.json +++ b/libraries/rush-lib/src/logic/test/pnpmConfig/common/config/rush/pnpm-config.json @@ -12,7 +12,29 @@ } } }, + "globalPeerDependencyRules": { + "allowedVersions": { + "react": "18" + }, + "ignoreMissing": ["@babel/core"] + }, + "globalAllowedDeprecatedVersions": { + "request": "*" + }, + "globalPatchedDependencies": { + "lodash@4.17.21": "patches/lodash@4.17.21.patch" + }, "globalNeverBuiltDependencies": ["fsevents", "level"], + "globalOnlyBuiltDependencies": ["esbuild", "playwright"], + "globalCatalogs": { + "default": { + "react": "^18.0.0", + "semver": "^7.5.4" + }, + "test": { + "jest": "^29.0.0" + } + }, "unsupportedPackageJsonSettings": { "pnpm": { "overrides": { diff --git a/libraries/rush-lib/src/logic/test/pnpmConfig/rush.json b/libraries/rush-lib/src/logic/test/pnpmConfig/rush.json index 332b70494a4..db85227290b 100644 --- a/libraries/rush-lib/src/logic/test/pnpmConfig/rush.json +++ b/libraries/rush-lib/src/logic/test/pnpmConfig/rush.json @@ -1,5 +1,5 @@ { - "pnpmVersion": "6.23.1", + "pnpmVersion": "10.1.0", "rushVersion": "5.58.0", "projects": [] } diff --git a/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11/common/config/rush/pnpm-config.json b/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11/common/config/rush/pnpm-config.json new file mode 100644 index 00000000000..16aecf97bf7 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11/common/config/rush/pnpm-config.json @@ -0,0 +1,29 @@ +{ + "globalOverrides": { + "foo": "^1.0.0", + "bar@^2.1.0": "3.0.0" + }, + "globalPackageExtensions": { + "react-redux": { + "peerDependencies": { + "react-dom": "*" + } + } + }, + "globalPeerDependencyRules": { + "allowedVersions": { + "react": "18" + }, + "ignoreMissing": ["@babel/core"] + }, + "globalAllowedDeprecatedVersions": { + "request": "*" + }, + "globalPatchedDependencies": { + "lodash@4.17.21": "patches/lodash@4.17.21.patch" + }, + "globalIgnoredOptionalDependencies": ["fsevents"], + "trustPolicy": "no-downgrade", + "trustPolicyExclude": ["chokidar@4.0.3"], + "trustPolicyIgnoreAfterMinutes": 1440 +} diff --git a/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11/rush.json b/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11/rush.json new file mode 100644 index 00000000000..de05d6e5169 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11/rush.json @@ -0,0 +1,5 @@ +{ + "pnpmVersion": "11.0.0", + "rushVersion": "5.58.0", + "projects": [] +} diff --git a/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11Subspaces/common/config/rush/pnpm-config.json b/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11Subspaces/common/config/rush/pnpm-config.json new file mode 100644 index 00000000000..e783afd001e --- /dev/null +++ b/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11Subspaces/common/config/rush/pnpm-config.json @@ -0,0 +1,3 @@ +{ + "useWorkspaces": true +} diff --git a/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11Subspaces/common/config/rush/subspaces.json b/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11Subspaces/common/config/rush/subspaces.json new file mode 100644 index 00000000000..fefe0ee4878 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11Subspaces/common/config/rush/subspaces.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/subspaces.schema.json", + "subspacesEnabled": true, + "subspaceNames": ["extra-subspace"] +} diff --git a/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11Subspaces/common/config/subspaces/default/pnpm-config.json b/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11Subspaces/common/config/subspaces/default/pnpm-config.json new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11Subspaces/common/config/subspaces/default/pnpm-config.json @@ -0,0 +1 @@ +{} diff --git a/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11Subspaces/rush.json b/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11Subspaces/rush.json new file mode 100644 index 00000000000..de05d6e5169 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/pnpmConfigPnpm11Subspaces/rush.json @@ -0,0 +1,5 @@ +{ + "pnpmVersion": "11.0.0", + "rushVersion": "5.58.0", + "projects": [] +} diff --git a/libraries/rush-lib/src/logic/test/repoWithCatalogs/a/package.json b/libraries/rush-lib/src/logic/test/repoWithCatalogs/a/package.json new file mode 100644 index 00000000000..893aac61ae0 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithCatalogs/a/package.json @@ -0,0 +1,9 @@ +{ + "name": "a", + "version": "1.0.0", + "description": "Project A uses default catalog", + "dependencies": { + "react": "catalog:", + "semver": "catalog:" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithCatalogs/b/package.json b/libraries/rush-lib/src/logic/test/repoWithCatalogs/b/package.json new file mode 100644 index 00000000000..1f535dd8543 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithCatalogs/b/package.json @@ -0,0 +1,11 @@ +{ + "name": "b", + "version": "1.0.0", + "description": "Project B depends on A and uses tools catalog", + "dependencies": { + "a": "workspace:*" + }, + "devDependencies": { + "typescript": "catalog:tools" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithCatalogs/c/package.json b/libraries/rush-lib/src/logic/test/repoWithCatalogs/c/package.json new file mode 100644 index 00000000000..ea46bdf7c6e --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithCatalogs/c/package.json @@ -0,0 +1,5 @@ +{ + "name": "c", + "version": "1.0.0", + "description": "Project C has no catalog dependencies" +} diff --git a/libraries/rush-lib/src/logic/test/repoWithCatalogs/common/config/rush/pnpm-config.json b/libraries/rush-lib/src/logic/test/repoWithCatalogs/common/config/rush/pnpm-config.json new file mode 100644 index 00000000000..3402e25453a --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithCatalogs/common/config/rush/pnpm-config.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/pnpm-config.schema.json", + "globalCatalogs": { + "default": { + "react": "^18.0.0", + "semver": "^7.5.4" + }, + "tools": { + "typescript": "~5.3.0", + "eslint": "^8.50.0" + } + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithCatalogs/d/package.json b/libraries/rush-lib/src/logic/test/repoWithCatalogs/d/package.json new file mode 100644 index 00000000000..3a9c1e1f890 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithCatalogs/d/package.json @@ -0,0 +1,8 @@ +{ + "name": "d", + "version": "1.0.0", + "description": "Project D uses only semver from default catalog", + "dependencies": { + "semver": "catalog:" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithCatalogs/e/package.json b/libraries/rush-lib/src/logic/test/repoWithCatalogs/e/package.json new file mode 100644 index 00000000000..8992f865145 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithCatalogs/e/package.json @@ -0,0 +1,8 @@ +{ + "name": "e", + "version": "1.0.0", + "description": "Project E uses only eslint from tools catalog", + "devDependencies": { + "eslint": "catalog:tools" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithCatalogs/rush.json b/libraries/rush-lib/src/logic/test/repoWithCatalogs/rush.json new file mode 100644 index 00000000000..848cf846c1a --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithCatalogs/rush.json @@ -0,0 +1,27 @@ +{ + "pnpmVersion": "9.15.0", + "rushVersion": "1.0.5", + + "projects": [ + { + "packageName": "a", + "projectFolder": "a" + }, + { + "packageName": "b", + "projectFolder": "b" + }, + { + "packageName": "c", + "projectFolder": "c" + }, + { + "packageName": "d", + "projectFolder": "d" + }, + { + "packageName": "e", + "projectFolder": "e" + } + ] +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/a/package.json b/libraries/rush-lib/src/logic/test/repoWithSubspaces/a/package.json new file mode 100644 index 00000000000..e57f46f8473 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/a/package.json @@ -0,0 +1,5 @@ +{ + "name": "a", + "version": "1.0.0", + "description": "Test package a" +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/b/package.json b/libraries/rush-lib/src/logic/test/repoWithSubspaces/b/package.json new file mode 100644 index 00000000000..3a7fdf92a46 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/b/package.json @@ -0,0 +1,8 @@ +{ + "name": "b", + "version": "2.0.0", + "description": "Test package b", + "dependencies": { + "foo": "~1.0.0" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/c/package.json b/libraries/rush-lib/src/logic/test/repoWithSubspaces/c/package.json new file mode 100644 index 00000000000..84d308bd6c0 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/c/package.json @@ -0,0 +1,8 @@ +{ + "name": "c", + "version": "3.1.1", + "description": "Test package c", + "dependencies": { + "b": "workspace:*" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/rush/experiments.json b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/rush/experiments.json new file mode 100644 index 00000000000..a20c6d24388 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/rush/experiments.json @@ -0,0 +1,3 @@ +{ + "exemptDecoupledDependenciesBetweenSubspaces": true +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/rush/pnpm-config.json b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/rush/pnpm-config.json new file mode 100644 index 00000000000..fdb1cb3ac59 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/rush/pnpm-config.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/pnpm-config.schema.json", + "useWorkspaces": true +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/rush/subspaces.json b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/rush/subspaces.json new file mode 100644 index 00000000000..ab4e6b3a3c4 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/rush/subspaces.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/subspaces.schema.json", + "subspacesEnabled": true, + "subspaceNames": ["project-change-analyzer-test-subspace"] +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/rush/version-policies.json b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/rush/version-policies.json new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/rush/version-policies.json @@ -0,0 +1 @@ +[] diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/subspaces/default/.pnpmfile.cjs b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/subspaces/default/.pnpmfile.cjs new file mode 100644 index 00000000000..ee041f83a4e --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/subspaces/default/.pnpmfile.cjs @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = { + hooks: { + readPackage(pkgJson) { + return pkgJson; + } + } +}; diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/subspaces/default/common-versions.json b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/subspaces/default/common-versions.json new file mode 100644 index 00000000000..9280fe7b96d --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/subspaces/default/common-versions.json @@ -0,0 +1,8 @@ +/** + * This configuration file specifies NPM dependency version selections that affect all projects + * in a Rush repo. More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/common-versions.schema.json", + "ensureConsistentVersions": true +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/subspaces/project-change-analyzer-test-subspace/.pnpmfile.cjs b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/subspaces/project-change-analyzer-test-subspace/.pnpmfile.cjs new file mode 100644 index 00000000000..ee041f83a4e --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/subspaces/project-change-analyzer-test-subspace/.pnpmfile.cjs @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = { + hooks: { + readPackage(pkgJson) { + return pkgJson; + } + } +}; diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/subspaces/project-change-analyzer-test-subspace/common-versions.json b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/subspaces/project-change-analyzer-test-subspace/common-versions.json new file mode 100644 index 00000000000..9280fe7b96d --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/common/config/subspaces/project-change-analyzer-test-subspace/common-versions.json @@ -0,0 +1,8 @@ +/** + * This configuration file specifies NPM dependency version selections that affect all projects + * in a Rush repo. More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/common-versions.schema.json", + "ensureConsistentVersions": true +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/d/package.json b/libraries/rush-lib/src/logic/test/repoWithSubspaces/d/package.json new file mode 100644 index 00000000000..fc4d11d2037 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/d/package.json @@ -0,0 +1,8 @@ +{ + "name": "d", + "version": "4.1.1", + "description": "Test package d", + "dependencies": { + "foo": "~1.0.0" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/e/package.json b/libraries/rush-lib/src/logic/test/repoWithSubspaces/e/package.json new file mode 100644 index 00000000000..c7210f01031 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/e/package.json @@ -0,0 +1,8 @@ +{ + "name": "e", + "version": "10.10.0", + "description": "Test package e", + "dependencies": { + "d": "workspace:*" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/f/package.json b/libraries/rush-lib/src/logic/test/repoWithSubspaces/f/package.json new file mode 100644 index 00000000000..5d6ea8a762e --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/f/package.json @@ -0,0 +1,5 @@ +{ + "name": "f", + "version": "10.10.0", + "description": "Test package f" +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspaces/rush.json b/libraries/rush-lib/src/logic/test/repoWithSubspaces/rush.json new file mode 100644 index 00000000000..895d07437cd --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspaces/rush.json @@ -0,0 +1,34 @@ +{ + "rushVersion": "1.0.5", + "pnpmVersion": "9.15.0", + + "projects": [ + { + "packageName": "a", + "projectFolder": "a" + }, + { + "packageName": "b", + "projectFolder": "b" + }, + { + "packageName": "c", + "projectFolder": "c" + }, + { + "packageName": "d", + "projectFolder": "d", + "subspaceName": "project-change-analyzer-test-subspace" + }, + { + "packageName": "e", + "projectFolder": "e", + "subspaceName": "project-change-analyzer-test-subspace" + }, + { + "packageName": "f", + "projectFolder": "f", + "subspaceName": "project-change-analyzer-test-subspace" + } + ] +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/a/package.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/a/package.json new file mode 100644 index 00000000000..5c102c007ba --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/a/package.json @@ -0,0 +1,8 @@ +{ + "name": "a", + "version": "1.0.0", + "description": "Test package a", + "dependencies": { + "react": "catalog:" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/b/package.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/b/package.json new file mode 100644 index 00000000000..3a7fdf92a46 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/b/package.json @@ -0,0 +1,8 @@ +{ + "name": "b", + "version": "2.0.0", + "description": "Test package b", + "dependencies": { + "foo": "~1.0.0" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/c/package.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/c/package.json new file mode 100644 index 00000000000..84d308bd6c0 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/c/package.json @@ -0,0 +1,8 @@ +{ + "name": "c", + "version": "3.1.1", + "description": "Test package c", + "dependencies": { + "b": "workspace:*" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/rush/experiments.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/rush/experiments.json new file mode 100644 index 00000000000..a20c6d24388 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/rush/experiments.json @@ -0,0 +1,3 @@ +{ + "exemptDecoupledDependenciesBetweenSubspaces": true +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/rush/pnpm-config.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/rush/pnpm-config.json new file mode 100644 index 00000000000..fdb1cb3ac59 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/rush/pnpm-config.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/pnpm-config.schema.json", + "useWorkspaces": true +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/rush/subspaces.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/rush/subspaces.json new file mode 100644 index 00000000000..ab4e6b3a3c4 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/rush/subspaces.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/subspaces.schema.json", + "subspacesEnabled": true, + "subspaceNames": ["project-change-analyzer-test-subspace"] +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/rush/version-policies.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/rush/version-policies.json new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/rush/version-policies.json @@ -0,0 +1 @@ +[] diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/default/.pnpmfile.cjs b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/default/.pnpmfile.cjs new file mode 100644 index 00000000000..ee041f83a4e --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/default/.pnpmfile.cjs @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = { + hooks: { + readPackage(pkgJson) { + return pkgJson; + } + } +}; diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/default/common-versions.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/default/common-versions.json new file mode 100644 index 00000000000..9280fe7b96d --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/default/common-versions.json @@ -0,0 +1,8 @@ +/** + * This configuration file specifies NPM dependency version selections that affect all projects + * in a Rush repo. More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/common-versions.schema.json", + "ensureConsistentVersions": true +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/default/pnpm-config.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/default/pnpm-config.json new file mode 100644 index 00000000000..291ec697142 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/default/pnpm-config.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/pnpm-config.schema.json", + "globalCatalogs": { + "default": { + "react": "^18.0.0" + } + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/project-change-analyzer-test-subspace/.pnpmfile.cjs b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/project-change-analyzer-test-subspace/.pnpmfile.cjs new file mode 100644 index 00000000000..ee041f83a4e --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/project-change-analyzer-test-subspace/.pnpmfile.cjs @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = { + hooks: { + readPackage(pkgJson) { + return pkgJson; + } + } +}; diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/project-change-analyzer-test-subspace/common-versions.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/project-change-analyzer-test-subspace/common-versions.json new file mode 100644 index 00000000000..9280fe7b96d --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/project-change-analyzer-test-subspace/common-versions.json @@ -0,0 +1,8 @@ +/** + * This configuration file specifies NPM dependency version selections that affect all projects + * in a Rush repo. More documentation is available on the Rush website: https://rushjs.io + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/common-versions.schema.json", + "ensureConsistentVersions": true +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/project-change-analyzer-test-subspace/pnpm-config.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/project-change-analyzer-test-subspace/pnpm-config.json new file mode 100644 index 00000000000..cc0a0ac626f --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/common/config/subspaces/project-change-analyzer-test-subspace/pnpm-config.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/pnpm-config.schema.json", + "globalCatalogs": { + "default": { + "foo": "~2.0.0", + "bar": "^3.0.0" + }, + "tools": { + "typescript": "~5.3.0", + "eslint": "^8.50.0" + } + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/d/package.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/d/package.json new file mode 100644 index 00000000000..bf308cf79f5 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/d/package.json @@ -0,0 +1,8 @@ +{ + "name": "d", + "version": "4.1.1", + "description": "Test package d", + "dependencies": { + "foo": "catalog:" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/e/package.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/e/package.json new file mode 100644 index 00000000000..1d6b2e26469 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/e/package.json @@ -0,0 +1,11 @@ +{ + "name": "e", + "version": "10.10.0", + "description": "Test package e", + "dependencies": { + "d": "workspace:*" + }, + "devDependencies": { + "typescript": "catalog:tools" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/f/package.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/f/package.json new file mode 100644 index 00000000000..5d6ea8a762e --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/f/package.json @@ -0,0 +1,5 @@ +{ + "name": "f", + "version": "10.10.0", + "description": "Test package f" +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/g/package.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/g/package.json new file mode 100644 index 00000000000..42bbb7abbd7 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/g/package.json @@ -0,0 +1,8 @@ +{ + "name": "g", + "version": "1.0.0", + "description": "Test package g — uses only bar from default catalog", + "dependencies": { + "bar": "catalog:" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/h/package.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/h/package.json new file mode 100644 index 00000000000..d6cc9ac86d6 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/h/package.json @@ -0,0 +1,8 @@ +{ + "name": "h", + "version": "1.0.0", + "description": "Test package h — uses only eslint from tools catalog", + "devDependencies": { + "eslint": "catalog:tools" + } +} diff --git a/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/rush.json b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/rush.json new file mode 100644 index 00000000000..4714895adf8 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/repoWithSubspacesCatalogs/rush.json @@ -0,0 +1,44 @@ +{ + "rushVersion": "1.0.5", + "pnpmVersion": "9.15.0", + + "projects": [ + { + "packageName": "a", + "projectFolder": "a" + }, + { + "packageName": "b", + "projectFolder": "b" + }, + { + "packageName": "c", + "projectFolder": "c" + }, + { + "packageName": "d", + "projectFolder": "d", + "subspaceName": "project-change-analyzer-test-subspace" + }, + { + "packageName": "e", + "projectFolder": "e", + "subspaceName": "project-change-analyzer-test-subspace" + }, + { + "packageName": "f", + "projectFolder": "f", + "subspaceName": "project-change-analyzer-test-subspace" + }, + { + "packageName": "g", + "projectFolder": "g", + "subspaceName": "project-change-analyzer-test-subspace" + }, + { + "packageName": "h", + "projectFolder": "h", + "subspaceName": "project-change-analyzer-test-subspace" + } + ] +} diff --git a/libraries/rush-lib/src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v5.3.yaml b/libraries/rush-lib/src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v5.3.yaml new file mode 100644 index 00000000000..31dffb91ba8 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v5.3.yaml @@ -0,0 +1,78 @@ +lockfileVersion: 5.3 + +specifiers: + '@rush-temp/project1': file:./projects/project1.tgz + '@rush-temp/project2': file:./projects/project2.tgz + '@rush-temp/project3': file:./projects/project3.tgz + '@scope/testDep': https://github.com/jonschlinkert/pad-left/tarball/2.1.0 + pad-left: ^1.0.0 + +dependencies: + '@rush-temp/project1': file:projects/project1.tgz + '@rush-temp/project2': file:projects/project2.tgz + '@rush-temp/project3': file:projects/project3.tgz + '@scope/testDep': 'example.pkgs.visualstudio.com/@scope/testDep/2.1.0' + pad-left: 1.0.2 + +packages: + + /jquery/1.12.3: + resolution: {integrity: sha512-FzM42/Ew+Hb8ha2OlhHRBLgWIZS32gZ0+NvWTf+ZvVvGaIlJkOiXQyb7VBjv4L6fJfmTrRf3EsAmbfsHDhfemw==} + dev: false + + /jquery/2.2.4: + resolution: {integrity: sha512-lBHj60ezci2u1v2FqnZIraShGgEXq35qCzMv4lITyHGppTnA13rwR0MgwyNJh9TnDs3aXUvd1xjAotfraMHX/Q==} + dev: false + + /pad-left/1.0.2: + resolution: {integrity: sha512-saxSV1EYAytuZDtQYEwi0DPzooG6aN18xyHrnJtzwjVwmMauzkEecd7hynVJGolNGk1Pl9tltmZqfze4TZTCxg==} + engines: {node: '>=0.10.0'} + dependencies: + repeat-string: 1.6.1 + dev: false + + /q/1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + dev: false + + /repeat-string/1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + dev: false + + 'example.pkgs.visualstudio.com/@scope/testDep/2.1.0': + resolution: {tarball: example.pkgs.visualstudio.com/@scope/testDep/2.1.0} + name: pad-left + version: 2.1.0 + engines: {node: '>=0.10.0'} + dependencies: + repeat-string: 1.6.1 + dev: false + + file:projects/project1.tgz: + resolution: {integrity: sha512-REmnAQ8v0kz+nQT9p9C8WUnhETgSwn/+XFAI9YFeMErmpGjJHC9bmH3RpOIj/GMEwGfRgNL5irqavrS4na1f3g==, tarball: file:projects/project1.tgz} + name: '@rush-temp/project1' + version: 0.0.0 + dependencies: + jquery: 1.12.3 + pad-left: 1.0.2 + dev: false + + file:projects/project2.tgz: + resolution: {integrity: sha512-tfYwAK8GXMMLMJK1K/FvhD9ZZQazg/60GZWkjM4Y/4oslAHWAuqTsPQ3bT8Z6NGarRzC5D4V7r7ftc4ifeuNaw==, tarball: file:projects/project2.tgz} + name: '@rush-temp/project2' + version: 0.0.0 + dependencies: + jquery: 2.2.4 + q: 1.5.1 + dev: false + + file:projects/project3.tgz: + resolution: {integrity: sha512-O/1Pan0WVX0t3fctCiRlhv1Lz7WFytgY5YPhBjMPMbh1PwLhF/9UwXrP5n4OC085bmG3JoMqigAtUsI9C8J9Fw==, tarball: file:projects/project3.tgz} + name: '@rush-temp/project3' + version: 0.0.0 + dependencies: + '@scope/testDep': 'example.pkgs.visualstudio.com/@scope/testDep/2.1.0' + q: 1.5.1 + dev: false diff --git a/libraries/rush-lib/src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v5.yaml b/libraries/rush-lib/src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v5.yaml new file mode 100644 index 00000000000..60be0e49d63 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v5.yaml @@ -0,0 +1,38 @@ +dependencies: + '@rush-temp/project1': 'file:projects/project1.tgz' + '@rush-temp/project2': 'file:projects/project2.tgz' + '@rush-temp/project3': 'file:projects/project3.tgz_462eaf34881863298955eb323c130fc7' +packages: + /jquery/2.2.4: + resolution: + integrity: sha1-PjAtxh6zKaIenvrJN9cx8GETTFk= + /jquery/1.12.3: + resolution: + integrity: sha1-PjAtxh6zKaIenvrJN9cx8GETTFk= + /q/1.5.3: + resolution: + integrity: sha1-3QG6ydBtMObyGa7LglPunr3DCPE= + /pad-left/1.0.2: + resolution: + integrity: sha1-3QG6ydBtMObyGa7LglPunr3DCPE= + example.pkgs.visualstudio.com/@scope/testDep/1.0.0: + resolution: + integrity: sha1-3QG6ydBtMObyGa7LglPunr3DCPE= + 'file:projects/project1.tgz': + dependencies: + jquery: 1.12.3 + 'file:projects/project2.tgz': + dependencies: + q: 1.5.3 + jquery: 2.2.4 + 'file:projects/project3.tgz_462eaf34881863298955eb323c130fc7': + dependencies: + q: 1.5.3 + '@scope/testDep': example.pkgs.visualstudio.com/@scope/testDep/2.1.0 +registry: 'http://localhost:4873/' +lockfileVersion: 5 +specifiers: + '@rush-temp/project1': 'file:./projects/project1.tgz' + '@rush-temp/project2': 'file:./projects/project2.tgz' + '@rush-temp/project3': 'file:./projects/project3.tgz' + q: '~1.5.0' diff --git a/libraries/rush-lib/src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v6.1.yaml b/libraries/rush-lib/src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v6.1.yaml new file mode 100644 index 00000000000..6d158598476 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v6.1.yaml @@ -0,0 +1,85 @@ +lockfileVersion: '6.1' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@rush-temp/project1': + specifier: file:./projects/project1.tgz + version: file:projects/project1.tgz + '@rush-temp/project2': + specifier: file:./projects/project2.tgz + version: file:projects/project2.tgz + '@rush-temp/project3': + specifier: file:./projects/project3.tgz + version: file:projects/project3.tgz + '@scope/testDep': + specifier: example.pkgs.visualstudio.com/@scope/testDep/2.1.0 + version: 'example.pkgs.visualstudio.com/@scope/testDep/2.1.0' + pad-left: + specifier: ^1.0.0 + version: 1.0.0 + +packages: + + /jquery@1.12.3: + resolution: {integrity: sha512-FzM42/Ew+Hb8ha2OlhHRBLgWIZS32gZ0+NvWTf+ZvVvGaIlJkOiXQyb7VBjv4L6fJfmTrRf3EsAmbfsHDhfemw==} + dev: false + + /jquery@2.2.4: + resolution: {integrity: sha512-lBHj60ezci2u1v2FqnZIraShGgEXq35qCzMv4lITyHGppTnA13rwR0MgwyNJh9TnDs3aXUvd1xjAotfraMHX/Q==} + dev: false + + /pad-left@1.0.0: + resolution: {integrity: sha512-VIgD7DviaDL6QCj+jEU1jpjXlu0z/sl4yzAmFLmM7YvM3ZRKLaxZAe+sZ1hKHeYUeI4zoZHfMetDpazu/uAwsw==} + engines: {node: '>=0.10.0'} + dependencies: + repeat-string: 1.6.1 + dev: false + + /q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + dev: false + + /repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + dev: false + + 'example.pkgs.visualstudio.com/@scope/testDep/2.1.0': + resolution: {tarball: example.pkgs.visualstudio.com/@scope/testDep/2.1.0} + name: '@scope/testDep' + version: 2.1.0 + engines: {node: '>=0.10.0'} + dependencies: + repeat-string: 1.6.1 + dev: false + + file:projects/project1.tgz: + resolution: {integrity: sha512-REmnAQ8v0kz+nQT9p9C8WUnhETgSwn/+XFAI9YFeMErmpGjJHC9bmH3RpOIj/GMEwGfRgNL5irqavrS4na1f3g==, tarball: file:projects/project1.tgz} + name: '@rush-temp/project1' + version: 0.0.0 + dependencies: + jquery: 1.12.3 + pad-left: 1.0.0 + dev: false + + file:projects/project2.tgz: + resolution: {integrity: sha512-tfYwAK8GXMMLMJK1K/FvhD9ZZQazg/60GZWkjM4Y/4oslAHWAuqTsPQ3bT8Z6NGarRzC5D4V7r7ftc4ifeuNaw==, tarball: file:projects/project2.tgz} + name: '@rush-temp/project2' + version: 0.0.0 + dependencies: + jquery: 2.2.4 + q: 1.5.1 + dev: false + + file:projects/project3.tgz: + resolution: {integrity: sha512-O/1Pan0WVX0t3fctCiRlhv1Lz7WFytgY5YPhBjMPMbh1PwLhF/9UwXrP5n4OC085bmG3JoMqigAtUsI9C8J9Fw==, tarball: file:projects/project3.tgz} + name: '@rush-temp/project3' + version: 0.0.0 + dependencies: + '@scope/testDep': 'example.pkgs.visualstudio.com/@scope/testDep/2.1.0' + q: 1.5.1 + dev: false diff --git a/libraries/rush-lib/src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v9.yaml b/libraries/rush-lib/src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v9.yaml new file mode 100644 index 00000000000..b74c0511f12 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/shrinkwrapFile/non-workspace-pnpm-lock-v9.yaml @@ -0,0 +1,196 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@pnpm/dependency-path': + specifier: ^5.1.7 + version: 5.1.7 + '@pnpm/lockfile.utils': + specifier: ^1.0.4 + version: 1.0.4 + '@rush-temp/project1': + specifier: file:./projects/project1 + version: project1@file:projects/project1 + '@rush-temp/project2': + specifier: file:./projects/project2 + version: project2@file:projects/project2 + '@rush-temp/project3': + specifier: file:./projects/project3 + version: project3@file:projects/project3 + pad-left: + specifier: 1.0.0 + version: 1.0.0 + +packages: + + '@pnpm/crypto.base32-hash@3.0.1': + resolution: {integrity: sha512-DM4RR/tvB7tMb2FekL0Q97A5PCXNyEC+6ht8SaufAUFSJNxeozqHw9PHTZR03mzjziPzNQLOld0pNINBX3srtw==} + engines: {node: '>=18.12'} + + '@pnpm/crypto.polyfill@1.0.0': + resolution: {integrity: sha512-WbmsqqcUXKKaAF77ox1TQbpZiaQcr26myuMUu+WjUtoWYgD3VP6iKYEvSx35SZ6G2L316lu+pv+40A2GbWJc1w==} + engines: {node: '>=18.12'} + + '@pnpm/dependency-path@5.1.7': + resolution: {integrity: sha512-MKCyaTy1r9fhBXAnhDZNBVgo6ThPnicwJEG203FDp7pGhD7NruS/FhBI+uMd7GNsK3D7aIFCDAgbWpNTXn/eWw==} + engines: {node: '>=18.12'} + + '@pnpm/lockfile.types@1.0.3': + resolution: {integrity: sha512-A7vUWktnhDkrIs+WmXm7AdffJVyVYJpQUEouya/DYhB+Y+tQ3BXjZ6CV0KybqLgI/8AZErgCJqFxA0GJH6QDjA==} + engines: {node: '>=18.12'} + + '@pnpm/lockfile.utils@1.0.4': + resolution: {integrity: sha512-ptHO2muziYyNCwpsuaPtaRgKiHMrE/lkGI4nqbHnRWWgfdJbTeL1tq+b/EUsxjlKlJ/a9Q4z2C+t38g+9bhTJg==} + engines: {node: '>=18.12'} + + '@pnpm/patching.types@1.0.0': + resolution: {integrity: sha512-juCdQCC1USqLcOhVPl1tYReoTO9YH4fTullMnFXXcmpsDM7Dkn3tzuOQKC3oPoJ2ozv+0EeWWMtMGqn2+IM3pQ==} + engines: {node: '>=18.12'} + + '@pnpm/pick-fetcher@3.0.0': + resolution: {integrity: sha512-2eisylRAU/jeuxFEPnS1gjLZKJGbYc4QEtEW6MVUYjO4Xi+2ttkSm7825S0J5IPpUIvln8HYPCUS0eQWSfpOaQ==} + engines: {node: '>=18.12'} + + '@pnpm/ramda@0.28.1': + resolution: {integrity: sha512-zcAG+lvU0fMziNeGXpPyCyCJYp5ZVrPElEE4t14jAmViaihohocZ+dDkcRIyAomox8pQsuZnv1EyHR+pOhmUWw==} + + '@pnpm/resolver-base@13.0.4': + resolution: {integrity: sha512-d6GtsaXDN1VmVdeB6ohrhwGwQfvYpEX/XkBZyRT0Hp772WabWVfaulvicwdh/8o7Rpzy7IV/2hKnDpodUY00lw==} + engines: {node: '>=18.12'} + + '@pnpm/types@12.2.0': + resolution: {integrity: sha512-5RtwWhX39j89/Tmyv2QSlpiNjErA357T/8r1Dkg+2lD3P7RuS7Xi2tChvmOC3VlezEFNcWnEGCOeKoGRkDuqFA==} + engines: {node: '>=18.12'} + + get-npm-tarball-url@2.1.0: + resolution: {integrity: sha512-ro+DiMu5DXgRBabqXupW38h7WPZ9+Ad8UjwhvsmmN8w1sU7ab0nzAXvVZ4kqYg57OrqomRtJvepX5/xvFKNtjA==} + engines: {node: '>=12.17'} + + jquery@1.12.3: + resolution: {integrity: sha512-FzM42/Ew+Hb8ha2OlhHRBLgWIZS32gZ0+NvWTf+ZvVvGaIlJkOiXQyb7VBjv4L6fJfmTrRf3EsAmbfsHDhfemw==} + + jquery@2.2.4: + resolution: {integrity: sha512-lBHj60ezci2u1v2FqnZIraShGgEXq35qCzMv4lITyHGppTnA13rwR0MgwyNJh9TnDs3aXUvd1xjAotfraMHX/Q==} + + pad-left@1.0.0: + resolution: {integrity: sha512-VIgD7DviaDL6QCj+jEU1jpjXlu0z/sl4yzAmFLmM7YvM3ZRKLaxZAe+sZ1hKHeYUeI4zoZHfMetDpazu/uAwsw==} + engines: {node: '>=0.10.0'} + + pad-left@https://github.com/jonschlinkert/pad-left/tarball/2.1.0: + resolution: {tarball: https://github.com/jonschlinkert/pad-left/tarball/2.1.0} + version: 2.1.0 + engines: {node: '>=0.10.0'} + + project1@file:projects/project1: + resolution: {directory: projects/project1, type: directory} + + project2@file:projects/project2: + resolution: {directory: projects/project2, type: directory} + + project3@file:projects/project3: + resolution: {directory: projects/project3, type: directory} + + q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + deprecated: |- + You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. + + (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) + + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + + rfc4648@1.5.3: + resolution: {integrity: sha512-MjOWxM065+WswwnmNONOT+bD1nXzY9Km6u3kzvnx8F8/HXGZdz3T6e6vZJ8Q/RIMUSp/nxqjH3GwvJDy8ijeQQ==} + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + +snapshots: + + '@pnpm/crypto.base32-hash@3.0.1': + dependencies: + '@pnpm/crypto.polyfill': 1.0.0 + rfc4648: 1.5.3 + + '@pnpm/crypto.polyfill@1.0.0': {} + + '@pnpm/dependency-path@5.1.7': + dependencies: + '@pnpm/crypto.base32-hash': 3.0.1 + '@pnpm/types': 12.2.0 + semver: 7.6.3 + + '@pnpm/lockfile.types@1.0.3': + dependencies: + '@pnpm/patching.types': 1.0.0 + '@pnpm/types': 12.2.0 + + '@pnpm/lockfile.utils@1.0.4': + dependencies: + '@pnpm/dependency-path': 5.1.7 + '@pnpm/lockfile.types': 1.0.3 + '@pnpm/pick-fetcher': 3.0.0 + '@pnpm/resolver-base': 13.0.4 + '@pnpm/types': 12.2.0 + get-npm-tarball-url: 2.1.0 + ramda: '@pnpm/ramda@0.28.1' + + '@pnpm/patching.types@1.0.0': {} + + '@pnpm/pick-fetcher@3.0.0': {} + + '@pnpm/ramda@0.28.1': {} + + '@pnpm/resolver-base@13.0.4': + dependencies: + '@pnpm/types': 12.2.0 + + '@pnpm/types@12.2.0': {} + + get-npm-tarball-url@2.1.0: {} + + jquery@1.12.3: {} + + jquery@2.2.4: {} + + pad-left@1.0.0: + dependencies: + repeat-string: 1.6.1 + + pad-left@https://github.com/jonschlinkert/pad-left/tarball/2.1.0: + dependencies: + repeat-string: 1.6.1 + + project1@file:projects/project1: + dependencies: + jquery: 1.12.3 + pad-left: 1.0.0 + + project2@file:projects/project2: + dependencies: + jquery: 2.2.4 + q: 1.5.1 + + project3@file:projects/project3: + dependencies: + '@scope/testDep': pad-left@https://github.com/jonschlinkert/pad-left/tarball/2.1.0 + q: 1.5.1 + + q@1.5.1: {} + + repeat-string@1.6.1: {} + + rfc4648@1.5.3: {} + + semver@7.6.3: {} diff --git a/libraries/rush-lib/src/logic/test/shrinkwrapFile/npm-shrinkwrap.json b/libraries/rush-lib/src/logic/test/shrinkwrapFile/npm-shrinkwrap.json index d1c9ff8090a..e929f2bd4f9 100644 --- a/libraries/rush-lib/src/logic/test/shrinkwrapFile/npm-shrinkwrap.json +++ b/libraries/rush-lib/src/logic/test/shrinkwrapFile/npm-shrinkwrap.json @@ -15,46 +15,46 @@ "fbjs": { "version": "0.8.12", "from": "fbjs@>=0.8.9 <0.9.0", - "resolved": "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/fbjs/-/fbjs-0.8.12.tgz" + "resolved": "https://example.pkgs.visualstudio.com/_packaging/feedname/npm/registry/fbjs/-/fbjs-0.8.12.tgz" }, "jquery": { "version": "2.2.4", "from": "jquery@>=2.2.4 <3.0.0", - "resolved": "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/jquery/-/jquery-2.2.4.tgz" + "resolved": "https://example.pkgs.visualstudio.com/_packaging/feedname/npm/registry/jquery/-/jquery-2.2.4.tgz" }, "object-assign": { "version": "4.1.1", "from": "object-assign@>=4.1.0 <5.0.0", - "resolved": "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/object-assign/-/object-assign-4.1.1.tgz" + "resolved": "https://example.pkgs.visualstudio.com/_packaging/feedname/npm/registry/object-assign/-/object-assign-4.1.1.tgz" }, "react": { "version": "15.5.4", "from": "react@>=15.5.4 <16.0.0", - "resolved": "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/react/-/react-15.5.4.tgz" + "resolved": "https://example.pkgs.visualstudio.com/_packaging/feedname/npm/registry/react/-/react-15.5.4.tgz" } } }, "prop-types": { "version": "15.5.8", "from": "prop-types@>=15.5.7 <16.0.0", - "resolved": "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/prop-types/-/prop-types-15.5.8.tgz", + "resolved": "https://example.pkgs.visualstudio.com/_packaging/feedname/npm/registry/prop-types/-/prop-types-15.5.8.tgz", "dependencies": { "fbjs": { "version": "0.8.12", "from": "fbjs@>=0.8.9 <0.9.0", - "resolved": "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/fbjs/-/fbjs-0.8.12.tgz" + "resolved": "https://example.pkgs.visualstudio.com/_packaging/feedname/npm/registry/fbjs/-/fbjs-0.8.12.tgz" }, "object-assign": { "version": "4.1.1", "from": "object-assign@>=4.1.0 <5.0.0", - "resolved": "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/object-assign/-/object-assign-4.1.1.tgz" + "resolved": "https://example.pkgs.visualstudio.com/_packaging/feedname/npm/registry/object-assign/-/object-assign-4.1.1.tgz" } } }, "q": { "version": "1.5.0", "from": "q@>=1.1.2 <2.0.0", - "resolved": "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/q/-/q-1.5.0.tgz" + "resolved": "https://example.pkgs.visualstudio.com/_packaging/feedname/npm/registry/q/-/q-1.5.0.tgz" } } } diff --git a/libraries/rush-lib/src/logic/test/shrinkwrapFile/pnpm-lock.yaml b/libraries/rush-lib/src/logic/test/shrinkwrapFile/pnpm-lock.yaml deleted file mode 100644 index 2504a1f2e4e..00000000000 --- a/libraries/rush-lib/src/logic/test/shrinkwrapFile/pnpm-lock.yaml +++ /dev/null @@ -1,38 +0,0 @@ -dependencies: - '@rush-temp/project1': 'file:projects/project1.tgz' - '@rush-temp/project2': 'file:projects/project2.tgz' - '@rush-temp/project3': 'file:projects/project3.tgz_462eaf34881863298955eb323c130fc7' -packages: - /jquery/1.0.0: - resolution: - integrity: sha1-PjAtxh6zKaIenvrJN9cx8GETTFk= - /jquery/2.9.9: - resolution: - integrity: sha1-PjAtxh6zKaIenvrJN9cx8GETTFk= - /q/1.5.3: - resolution: - integrity: sha1-3QG6ydBtMObyGa7LglPunr3DCPE= - /left-pad/9.9.9: - resolution: - integrity: sha1-3QG6ydBtMObyGa7LglPunr3DCPE= - example.pkgs.visualstudio.com/@scope/testDep/1.0.0: - resolution: - integrity: sha1-3QG6ydBtMObyGa7LglPunr3DCPE= - 'file:projects/project1.tgz': - dependencies: - jquery: 2.9.9 - 'file:projects/project2.tgz': - dependencies: - q: 1.5.3 - jquery: 1.0.0 - 'file:projects/project3.tgz_462eaf34881863298955eb323c130fc7': - dependencies: - q: 1.5.3 - '@scope/testDep': example.pkgs.visualstudio.com/@scope/testDep/1.0.0 -registry: 'http://localhost:4873/' -lockfileVersion: 5 -specifiers: - '@rush-temp/project1': 'file:./projects/project1.tgz' - '@rush-temp/project2': 'file:./projects/project2.tgz' - '@rush-temp/project3': 'file:./projects/project3.tgz' - q: '~1.5.0' diff --git a/libraries/rush-lib/src/logic/test/shrinkwrapFile/workspace-pnpm-lock-no-projects-v9.yaml b/libraries/rush-lib/src/logic/test/shrinkwrapFile/workspace-pnpm-lock-no-projects-v9.yaml new file mode 100644 index 00000000000..490c2e4fa8d --- /dev/null +++ b/libraries/rush-lib/src/logic/test/shrinkwrapFile/workspace-pnpm-lock-no-projects-v9.yaml @@ -0,0 +1,9 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false + +importers: + + .: {} diff --git a/libraries/rush-lib/src/logic/test/shrinkwrapFile/workspace-pnpm-lock-v5.3.yaml b/libraries/rush-lib/src/logic/test/shrinkwrapFile/workspace-pnpm-lock-v5.3.yaml new file mode 100644 index 00000000000..a5743e0bfec --- /dev/null +++ b/libraries/rush-lib/src/logic/test/shrinkwrapFile/workspace-pnpm-lock-v5.3.yaml @@ -0,0 +1,66 @@ +lockfileVersion: 5.3 + +importers: + + .: + specifiers: {} + + ../../project1: + specifiers: + jquery: 1.12.3 + pad-left: ^1.0.0 + dependencies: + jquery: 1.12.3 + pad-left: 1.0.2 + + ../../project2: + specifiers: + jquery: 2.2.4 + q: ~1.5.0 + dependencies: + jquery: 2.2.4 + q: 1.5.1 + + ../../project3: + specifiers: + '@scope/testDep': https://github.com/jonschlinkert/pad-left/tarball/2.1.0 + q: 1.5.1 + dependencies: + '@scope/testDep': 'example.pkgs.visualstudio.com/@scope/testDep/2.1.0' + q: 1.5.1 + +packages: + + /jquery/1.12.3: + resolution: {integrity: sha512-FzM42/Ew+Hb8ha2OlhHRBLgWIZS32gZ0+NvWTf+ZvVvGaIlJkOiXQyb7VBjv4L6fJfmTrRf3EsAmbfsHDhfemw==} + dev: false + + /jquery/2.2.4: + resolution: {integrity: sha512-lBHj60ezci2u1v2FqnZIraShGgEXq35qCzMv4lITyHGppTnA13rwR0MgwyNJh9TnDs3aXUvd1xjAotfraMHX/Q==} + dev: false + + /pad-left/1.0.2: + resolution: {integrity: sha512-saxSV1EYAytuZDtQYEwi0DPzooG6aN18xyHrnJtzwjVwmMauzkEecd7hynVJGolNGk1Pl9tltmZqfze4TZTCxg==} + engines: {node: '>=0.10.0'} + dependencies: + repeat-string: 1.6.1 + dev: false + + /q/1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + dev: false + + /repeat-string/1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + dev: false + + 'example.pkgs.visualstudio.com/@scope/testDep/2.1.0': + resolution: {tarball: example.pkgs.visualstudio.com/@scope/testDep/2.1.0} + name: pad-left + version: 2.1.0 + engines: {node: '>=0.10.0'} + dependencies: + repeat-string: 1.6.1 + dev: false diff --git a/libraries/rush-lib/src/logic/test/shrinkwrapFile/workspace-pnpm-lock-v6.1.yaml b/libraries/rush-lib/src/logic/test/shrinkwrapFile/workspace-pnpm-lock-v6.1.yaml new file mode 100644 index 00000000000..3bf93145322 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/shrinkwrapFile/workspace-pnpm-lock-v6.1.yaml @@ -0,0 +1,72 @@ +lockfileVersion: '6.1' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} + + ../../project1: + dependencies: + jquery: + specifier: 1.12.3 + version: 1.12.3 + pad-left: + specifier: ^1.0.0 + version: 1.0.0 + + ../../project2: + dependencies: + jquery: + specifier: 2.2.4 + version: 2.2.4 + q: + specifier: ~1.5.0 + version: 1.5.1 + + ../../project3: + dependencies: + '@scope/testDep': + specifier: https://github.com/jonschlinkert/pad-left/tarball/2.1.0 + version: '@github.com/jonschlinkert/pad-left/tarball/2.1.0' + q: + specifier: 1.5.1 + version: 1.5.1 + +packages: + + /jquery@1.12.3: + resolution: {integrity: sha512-FzM42/Ew+Hb8ha2OlhHRBLgWIZS32gZ0+NvWTf+ZvVvGaIlJkOiXQyb7VBjv4L6fJfmTrRf3EsAmbfsHDhfemw==} + dev: false + + /jquery@2.2.4: + resolution: {integrity: sha512-lBHj60ezci2u1v2FqnZIraShGgEXq35qCzMv4lITyHGppTnA13rwR0MgwyNJh9TnDs3aXUvd1xjAotfraMHX/Q==} + dev: false + + /pad-left@1.0.0: + resolution: {integrity: sha512-VIgD7DviaDL6QCj+jEU1jpjXlu0z/sl4yzAmFLmM7YvM3ZRKLaxZAe+sZ1hKHeYUeI4zoZHfMetDpazu/uAwsw==} + engines: {node: '>=0.10.0'} + dependencies: + repeat-string: 1.6.1 + dev: false + + /q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + dev: false + + /repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + dev: false + + '@github.com/jonschlinkert/pad-left/tarball/2.1.0': + resolution: {tarball: https://github.com/jonschlinkert/pad-left/tarball/2.1.0} + name: pad-left + version: 2.1.0 + engines: {node: '>=0.10.0'} + dependencies: + repeat-string: 1.6.1 + dev: false diff --git a/libraries/rush-lib/src/logic/test/shrinkwrapFile/workspace-pnpm-lock-v9.yaml b/libraries/rush-lib/src/logic/test/shrinkwrapFile/workspace-pnpm-lock-v9.yaml new file mode 100644 index 00000000000..ad160af09b8 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/shrinkwrapFile/workspace-pnpm-lock-v9.yaml @@ -0,0 +1,83 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} + + ../../project1: + dependencies: + jquery: + specifier: 1.12.3 + version: 1.12.3 + pad-left: + specifier: ^1.0.0 + version: 1.0.2 + + ../../project2: + dependencies: + jquery: + specifier: 2.2.4 + version: 2.2.4 + q: + specifier: ~1.5.0 + version: 1.5.0 + + ../../project3: + dependencies: + '@scope/testDep': + specifier: https://github.com/jonschlinkert/pad-left/tarball/2.1.0 + version: pad-left@https://github.com/jonschlinkert/pad-left/tarball/2.1.0 + q: + specifier: 1.5.0 + version: 1.5.0 + +packages: + + jquery@1.12.3: + resolution: {integrity: sha512-FzM42/Ew+Hb8ha2OlhHRBLgWIZS32gZ0+NvWTf+ZvVvGaIlJkOiXQyb7VBjv4L6fJfmTrRf3EsAmbfsHDhfemw==} + + jquery@2.2.4: + resolution: {integrity: sha512-lBHj60ezci2u1v2FqnZIraShGgEXq35qCzMv4lITyHGppTnA13rwR0MgwyNJh9TnDs3aXUvd1xjAotfraMHX/Q==} + + pad-left@1.0.2: + resolution: {integrity: sha512-saxSV1EYAytuZDtQYEwi0DPzooG6aN18xyHrnJtzwjVwmMauzkEecd7hynVJGolNGk1Pl9tltmZqfze4TZTCxg==} + engines: {node: '>=0.10.0'} + + pad-left@https://github.com/jonschlinkert/pad-left/tarball/2.1.0: + resolution: {tarball: https://github.com/jonschlinkert/pad-left/tarball/2.1.0} + version: 2.1.0 + engines: {node: '>=0.10.0'} + + q@1.5.0: + resolution: {integrity: sha512-VVMcd+HnuWZalHPycK7CsbVJ+sSrrrnCvHcW38YJVK9Tywnb5DUWJjONi81bLUj7aqDjIXnePxBl5t1r/F/ncg==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + deprecated: |- + You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. + + (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) + + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + +snapshots: + + jquery@1.12.3: {} + + jquery@2.2.4: {} + + pad-left@1.0.2: + dependencies: + repeat-string: 1.6.1 + + pad-left@https://github.com/jonschlinkert/pad-left/tarball/2.1.0: + dependencies: + repeat-string: 1.6.1 + + q@1.5.0: {} + + repeat-string@1.6.1: {} diff --git a/libraries/rush-lib/src/logic/test/strictValidation/mainLockstep.json b/libraries/rush-lib/src/logic/test/strictValidation/mainLockstep.json new file mode 100644 index 00000000000..797943db49d --- /dev/null +++ b/libraries/rush-lib/src/logic/test/strictValidation/mainLockstep.json @@ -0,0 +1,9 @@ +{ + "changes": [ + { + "packageName": "lockstep-main", + "type": "patch", + "comment": "Change for the main lockstep project" + } + ] +} diff --git a/libraries/rush-lib/src/logic/test/strictValidation/nonMainLockstep.json b/libraries/rush-lib/src/logic/test/strictValidation/nonMainLockstep.json new file mode 100644 index 00000000000..71c400f6f04 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/strictValidation/nonMainLockstep.json @@ -0,0 +1,9 @@ +{ + "changes": [ + { + "packageName": "lockstep-secondary", + "type": "patch", + "comment": "Change for a non-main lockstep project" + } + ] +} diff --git a/libraries/rush-lib/src/logic/test/strictValidation/nonexistentProject.json b/libraries/rush-lib/src/logic/test/strictValidation/nonexistentProject.json new file mode 100644 index 00000000000..4a8b812223e --- /dev/null +++ b/libraries/rush-lib/src/logic/test/strictValidation/nonexistentProject.json @@ -0,0 +1,9 @@ +{ + "changes": [ + { + "packageName": "nonexistent-package", + "type": "patch", + "comment": "Change for a project that does not exist" + } + ] +} diff --git a/libraries/rush-lib/src/logic/test/workspaceCycleDetector/decoupled-cycle/pkg-a/package.json b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/decoupled-cycle/pkg-a/package.json new file mode 100644 index 00000000000..7189d052e2b --- /dev/null +++ b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/decoupled-cycle/pkg-a/package.json @@ -0,0 +1,7 @@ +{ + "name": "pkg-a", + "version": "1.0.0", + "dependencies": { + "pkg-b": "workspace:*" + } +} diff --git a/libraries/rush-lib/src/logic/test/workspaceCycleDetector/decoupled-cycle/pkg-b/package.json b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/decoupled-cycle/pkg-b/package.json new file mode 100644 index 00000000000..fbfb2c70709 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/decoupled-cycle/pkg-b/package.json @@ -0,0 +1,7 @@ +{ + "name": "pkg-b", + "version": "1.0.0", + "dependencies": { + "pkg-a": "workspace:*" + } +} diff --git a/libraries/rush-lib/src/logic/test/workspaceCycleDetector/decoupled-cycle/rush.json b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/decoupled-cycle/rush.json new file mode 100644 index 00000000000..4dc29674fd1 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/decoupled-cycle/rush.json @@ -0,0 +1,15 @@ +{ + "rushVersion": "0.0.0", + "pnpmVersion": "8.0.0", + "projects": [ + { + "packageName": "pkg-a", + "projectFolder": "pkg-a" + }, + { + "packageName": "pkg-b", + "projectFolder": "pkg-b", + "decoupledLocalDependencies": ["pkg-a"] + } + ] +} diff --git a/libraries/rush-lib/src/logic/test/workspaceCycleDetector/no-cycle/pkg-a/package.json b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/no-cycle/pkg-a/package.json new file mode 100644 index 00000000000..ee09b194db8 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/no-cycle/pkg-a/package.json @@ -0,0 +1,4 @@ +{ + "name": "pkg-a", + "version": "1.0.0" +} diff --git a/libraries/rush-lib/src/logic/test/workspaceCycleDetector/no-cycle/pkg-b/package.json b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/no-cycle/pkg-b/package.json new file mode 100644 index 00000000000..fbfb2c70709 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/no-cycle/pkg-b/package.json @@ -0,0 +1,7 @@ +{ + "name": "pkg-b", + "version": "1.0.0", + "dependencies": { + "pkg-a": "workspace:*" + } +} diff --git a/libraries/rush-lib/src/logic/test/workspaceCycleDetector/no-cycle/rush.json b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/no-cycle/rush.json new file mode 100644 index 00000000000..ddd8b558437 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/no-cycle/rush.json @@ -0,0 +1,14 @@ +{ + "rushVersion": "0.0.0", + "pnpmVersion": "8.0.0", + "projects": [ + { + "packageName": "pkg-a", + "projectFolder": "pkg-a" + }, + { + "packageName": "pkg-b", + "projectFolder": "pkg-b" + } + ] +} diff --git a/libraries/rush-lib/src/logic/test/workspaceCycleDetector/with-cycle/pkg-a/package.json b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/with-cycle/pkg-a/package.json new file mode 100644 index 00000000000..7189d052e2b --- /dev/null +++ b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/with-cycle/pkg-a/package.json @@ -0,0 +1,7 @@ +{ + "name": "pkg-a", + "version": "1.0.0", + "dependencies": { + "pkg-b": "workspace:*" + } +} diff --git a/libraries/rush-lib/src/logic/test/workspaceCycleDetector/with-cycle/pkg-b/package.json b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/with-cycle/pkg-b/package.json new file mode 100644 index 00000000000..fbfb2c70709 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/with-cycle/pkg-b/package.json @@ -0,0 +1,7 @@ +{ + "name": "pkg-b", + "version": "1.0.0", + "dependencies": { + "pkg-a": "workspace:*" + } +} diff --git a/libraries/rush-lib/src/logic/test/workspaceCycleDetector/with-cycle/rush.json b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/with-cycle/rush.json new file mode 100644 index 00000000000..ddd8b558437 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/workspaceCycleDetector/with-cycle/rush.json @@ -0,0 +1,14 @@ +{ + "rushVersion": "0.0.0", + "pnpmVersion": "8.0.0", + "projects": [ + { + "packageName": "pkg-a", + "projectFolder": "pkg-a" + }, + { + "packageName": "pkg-b", + "projectFolder": "pkg-b" + } + ] +} diff --git a/libraries/rush-lib/src/logic/test/workspacePackages/.mergequeueignore b/libraries/rush-lib/src/logic/test/workspacePackages/.mergequeueignore new file mode 100644 index 00000000000..b68298052b9 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/workspacePackages/.mergequeueignore @@ -0,0 +1 @@ +common/config/version-policies.json \ No newline at end of file diff --git a/libraries/rush-lib/src/logic/test/workspacePackages/e/.mergequeueignore b/libraries/rush-lib/src/logic/test/workspacePackages/e/.mergequeueignore new file mode 100644 index 00000000000..c578b1d164c --- /dev/null +++ b/libraries/rush-lib/src/logic/test/workspacePackages/e/.mergequeueignore @@ -0,0 +1 @@ +src/** \ No newline at end of file diff --git a/libraries/rush-lib/src/logic/test/workspaceRepo/common/config/rush/experiments.json b/libraries/rush-lib/src/logic/test/workspaceRepo/common/config/rush/experiments.json deleted file mode 100644 index 992c3ad2479..00000000000 --- a/libraries/rush-lib/src/logic/test/workspaceRepo/common/config/rush/experiments.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "phasedCommands": true -} diff --git a/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinder.ts b/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinder.ts index f354e571135..f456dd81d99 100644 --- a/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinder.ts +++ b/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinder.ts @@ -1,20 +1,23 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import colors from 'colors/safe'; import { AlreadyReportedError } from '@rushstack/node-core-library'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; -import { RushConfiguration } from '../../api/RushConfiguration'; -import { PackageJsonDependency, DependencyType } from '../../api/PackageJsonEditor'; -import { CommonVersionsConfiguration } from '../../api/CommonVersionsConfiguration'; -import { VersionMismatchFinderEntity } from './VersionMismatchFinderEntity'; +import type { RushConfiguration } from '../../api/RushConfiguration'; +import { type PackageJsonDependency, DependencyType } from '../../api/PackageJsonEditor'; +import type { CommonVersionsConfiguration } from '../../api/CommonVersionsConfiguration'; +import type { VersionMismatchFinderEntity } from './VersionMismatchFinderEntity'; import { VersionMismatchFinderProject } from './VersionMismatchFinderProject'; import { VersionMismatchFinderCommonVersions } from './VersionMismatchFinderCommonVersions'; +import { CustomTipId } from '../../api/CustomTipsConfiguration'; +import type { Subspace } from '../../api/Subspace'; const TRUNCATE_AFTER_PACKAGE_NAME_COUNT: number = 5; export interface IVersionMismatchFinderOptions { - variant?: string | undefined; + subspace?: Subspace; + variant: string | undefined; } export interface IVersionMismatchFinderRushCheckOptions extends IVersionMismatchFinderOptions { @@ -65,20 +68,37 @@ export class VersionMismatchFinder { public static rushCheck( rushConfiguration: RushConfiguration, - options: IVersionMismatchFinderRushCheckOptions = {} + terminal: ITerminal, + options?: IVersionMismatchFinderRushCheckOptions ): void { - VersionMismatchFinder._checkForInconsistentVersions(rushConfiguration, { - ...options, + const { + variant, + subspace = rushConfiguration.defaultSubspace, + printAsJson, + truncateLongPackageNameLists + } = options ?? {}; + + _checkForInconsistentVersions(rushConfiguration, { + variant, + subspace, + printAsJson, + truncateLongPackageNameLists, + terminal, isRushCheckCommand: true }); } public static ensureConsistentVersions( rushConfiguration: RushConfiguration, - options: IVersionMismatchFinderEnsureConsistentVersionsOptions = {} + terminal: ITerminal, + options?: IVersionMismatchFinderEnsureConsistentVersionsOptions ): void { - VersionMismatchFinder._checkForInconsistentVersions(rushConfiguration, { - ...options, + const { variant, subspace = rushConfiguration.defaultSubspace } = options ?? {}; + + _checkForInconsistentVersions(rushConfiguration, { + subspace, + variant, + terminal, isRushCheckCommand: false, truncateLongPackageNameLists: true }); @@ -90,9 +110,10 @@ export class VersionMismatchFinder { */ public static getMismatches( rushConfiguration: RushConfiguration, - options: IVersionMismatchFinderOptions = {} + options?: IVersionMismatchFinderOptions ): VersionMismatchFinder { - const commonVersions: CommonVersionsConfiguration = rushConfiguration.getCommonVersions(options.variant); + const { subspace = rushConfiguration.defaultSubspace, variant } = options ?? {}; + const commonVersions: CommonVersionsConfiguration = subspace.getCommonVersions(variant); const projects: VersionMismatchFinderEntity[] = []; @@ -100,52 +121,14 @@ export class VersionMismatchFinder { // Make sure this one is first so it doesn't get truncated when a long list is printed projects.push(new VersionMismatchFinderCommonVersions(commonVersions)); - for (const project of rushConfiguration.projects) { + // If subspace is specified, only go through projects in that subspace + for (const project of subspace.getProjects()) { projects.push(new VersionMismatchFinderProject(project)); } return new VersionMismatchFinder(projects, commonVersions.allowedAlternativeVersions); } - private static _checkForInconsistentVersions( - rushConfiguration: RushConfiguration, - options: { - isRushCheckCommand: boolean; - variant?: string | undefined; - printAsJson?: boolean | undefined; - truncateLongPackageNameLists?: boolean | undefined; - } - ): void { - if (rushConfiguration.ensureConsistentVersions || options.isRushCheckCommand) { - const mismatchFinder: VersionMismatchFinder = VersionMismatchFinder.getMismatches( - rushConfiguration, - options - ); - - if (options.printAsJson) { - mismatchFinder.printAsJson(); - } else { - mismatchFinder.print(options.truncateLongPackageNameLists); - - if (mismatchFinder.numberOfMismatches > 0) { - console.log(colors.red(`Found ${mismatchFinder.numberOfMismatches} mis-matching dependencies!`)); - if (!options.isRushCheckCommand && options.truncateLongPackageNameLists) { - // There isn't a --verbose flag in `rush install`/`rush update`, so a long list will always be truncated. - console.log( - 'For more detailed reporting about these version mismatches, use the "rush check --verbose" command.' - ); - } - - throw new AlreadyReportedError(); - } else { - if (options.isRushCheckCommand) { - console.log(colors.green(`Found no mis-matching dependencies!`)); - } - } - } - } - } - public get mismatches(): ReadonlyMap> { return this._mismatches; } @@ -203,14 +186,17 @@ export class VersionMismatchFinder { mismatchedVersions: mismatchDependencies }; + // eslint-disable-next-line no-console console.log(JSON.stringify(output, undefined, 2)); } public print(truncateLongPackageNameLists: boolean = false): void { // Iterate over the list. For any dependency with mismatching versions, print the projects this.getMismatches().forEach((dependency: string) => { - console.log(colors.yellow(dependency)); + // eslint-disable-next-line no-console + console.log(Colorize.yellow(dependency)); this.getVersionsOfMismatch(dependency)!.forEach((version: string) => { + // eslint-disable-next-line no-console console.log(` ${version}`); const consumersOfMismatch: VersionMismatchFinderEntity[] = this.getConsumersOfMismatch( dependency, @@ -228,13 +214,16 @@ export class VersionMismatchFinder { numberRemaining--; + // eslint-disable-next-line no-console console.log(` - ${friendlyName}`); } if (numberRemaining > 0) { + // eslint-disable-next-line no-console console.log(` (and ${numberRemaining} others)`); } }); + // eslint-disable-next-line no-console console.log(); }); } @@ -306,3 +295,59 @@ export class VersionMismatchFinder { return keys; } } + +function _checkForInconsistentVersions( + rushConfiguration: RushConfiguration, + options: { + isRushCheckCommand: boolean; + subspace: Subspace; + variant: string | undefined; + printAsJson?: boolean | undefined; + terminal: ITerminal; + truncateLongPackageNameLists?: boolean | undefined; + } +): void { + const { variant, isRushCheckCommand, printAsJson, subspace, truncateLongPackageNameLists, terminal } = + options; + if (subspace.shouldEnsureConsistentVersions(variant) || isRushCheckCommand) { + const mismatchFinder: VersionMismatchFinder = VersionMismatchFinder.getMismatches( + rushConfiguration, + options + ); + + if (printAsJson) { + mismatchFinder.printAsJson(); + } else { + mismatchFinder.print(truncateLongPackageNameLists); + + if (mismatchFinder.numberOfMismatches > 0) { + // eslint-disable-next-line no-console + console.log( + Colorize.red( + `Found ${mismatchFinder.numberOfMismatches} mis-matching dependencies ${ + subspace?.subspaceName ? `in subspace: ${subspace?.subspaceName}` : '' + }` + ) + ); + rushConfiguration.customTipsConfiguration._showErrorTip( + terminal, + CustomTipId.TIP_RUSH_INCONSISTENT_VERSIONS + ); + if (!isRushCheckCommand && truncateLongPackageNameLists) { + // There isn't a --verbose flag in `rush install`/`rush update`, so a long list will always be truncated. + // eslint-disable-next-line no-console + console.log( + 'For more detailed reporting about these version mismatches, use the "rush check --verbose" command.' + ); + } + + throw new AlreadyReportedError(); + } else { + if (isRushCheckCommand) { + // eslint-disable-next-line no-console + console.log(Colorize.green(`Found no mis-matching dependencies!`)); + } + } + } + } +} diff --git a/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinderCommonVersions.ts b/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinderCommonVersions.ts index b3da5789afa..1e2c11b17bf 100644 --- a/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinderCommonVersions.ts +++ b/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinderCommonVersions.ts @@ -3,7 +3,7 @@ import { RushConstants } from '../RushConstants'; import { PackageJsonDependency, DependencyType } from '../../api/PackageJsonEditor'; -import { CommonVersionsConfiguration } from '../../api/CommonVersionsConfiguration'; +import type { CommonVersionsConfiguration } from '../../api/CommonVersionsConfiguration'; import { VersionMismatchFinderEntity } from './VersionMismatchFinderEntity'; export class VersionMismatchFinderCommonVersions extends VersionMismatchFinderEntity { @@ -63,8 +63,8 @@ export class VersionMismatchFinderCommonVersions extends VersionMismatchFinderEn throw new Error('Not supported.'); } - public saveIfModified(): boolean { - return this._fileManager.save(); + public async saveIfModifiedAsync(): Promise { + return await this._fileManager.saveAsync(); } private _getPackageJsonDependency(dependencyName: string, version: string): PackageJsonDependency { diff --git a/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinderEntity.ts b/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinderEntity.ts index 20e2295012f..5b2284d97aa 100644 --- a/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinderEntity.ts +++ b/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinderEntity.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { PackageJsonDependency, DependencyType } from '../../api/PackageJsonEditor'; +import type { PackageJsonDependency, DependencyType } from '../../api/PackageJsonEditor'; export interface IVersionMismatchFinderEntityOptions { friendlyName: string; @@ -15,9 +15,10 @@ export abstract class VersionMismatchFinderEntity { public readonly skipRushCheck: boolean | undefined; public constructor(options: IVersionMismatchFinderEntityOptions) { - this.friendlyName = options.friendlyName; - this.decoupledLocalDependencies = options.decoupledLocalDependencies; - this.skipRushCheck = options.skipRushCheck; + const { friendlyName, decoupledLocalDependencies, skipRushCheck } = options; + this.friendlyName = friendlyName; + this.decoupledLocalDependencies = decoupledLocalDependencies; + this.skipRushCheck = skipRushCheck; } public abstract get filePath(): string; @@ -31,5 +32,5 @@ export abstract class VersionMismatchFinderEntity { dependencyType: DependencyType ): void; public abstract removeDependency(packageName: string, dependencyType: DependencyType): void; - public abstract saveIfModified(): boolean; + public abstract saveIfModifiedAsync(): Promise; } diff --git a/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinderProject.ts b/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinderProject.ts index e8d9f4c81ae..cc63370b687 100644 --- a/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinderProject.ts +++ b/libraries/rush-lib/src/logic/versionMismatch/VersionMismatchFinderProject.ts @@ -2,8 +2,8 @@ // See LICENSE in the project root for license information. import { VersionMismatchFinderEntity } from './VersionMismatchFinderEntity'; -import { PackageJsonEditor, PackageJsonDependency, DependencyType } from '../../api/PackageJsonEditor'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { PackageJsonEditor, PackageJsonDependency, DependencyType } from '../../api/PackageJsonEditor'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; export class VersionMismatchFinderProject extends VersionMismatchFinderEntity { public packageName: string; @@ -48,7 +48,7 @@ export class VersionMismatchFinderProject extends VersionMismatchFinderEntity { return this._fileManager.removeDependency(packageName, dependencyType); } - public saveIfModified(): boolean { - return this._fileManager.saveIfModified(); + public async saveIfModifiedAsync(): Promise { + return await this._fileManager.saveIfModifiedAsync(); } } diff --git a/libraries/rush-lib/src/logic/yarn/YarnOptionsConfiguration.ts b/libraries/rush-lib/src/logic/yarn/YarnOptionsConfiguration.ts index 89a16592c45..479ee9693ca 100644 --- a/libraries/rush-lib/src/logic/yarn/YarnOptionsConfiguration.ts +++ b/libraries/rush-lib/src/logic/yarn/YarnOptionsConfiguration.ts @@ -2,7 +2,7 @@ // See LICENSE in the project root for license information. import { - IPackageManagerOptionsJsonBase, + type IPackageManagerOptionsJsonBase, PackageManagerOptionsConfigurationBase } from '../base/BasePackageManagerOptionsConfiguration'; diff --git a/libraries/rush-lib/src/logic/yarn/YarnShrinkwrapFile.ts b/libraries/rush-lib/src/logic/yarn/YarnShrinkwrapFile.ts index ee1df2fc1f1..920c72df3d4 100644 --- a/libraries/rush-lib/src/logic/yarn/YarnShrinkwrapFile.ts +++ b/libraries/rush-lib/src/logic/yarn/YarnShrinkwrapFile.ts @@ -1,13 +1,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import { + FileSystem, + type IParsedPackageNameOrError, + InternalError, + Import +} from '@rushstack/node-core-library'; + import { BaseShrinkwrapFile } from '../base/BaseShrinkwrapFile'; -import { FileSystem, IParsedPackageNameOrError, InternalError, Import } from '@rushstack/node-core-library'; import { RushConstants } from '../RushConstants'; -import { DependencySpecifier } from '../DependencySpecifier'; +import type { DependencySpecifier } from '../DependencySpecifier'; import { PackageNameParsers } from '../../api/PackageNameParsers'; -import { RushConfigurationProject } from '../../api/RushConfigurationProject'; -import { BaseProjectShrinkwrapFile } from '../base/BaseProjectShrinkwrapFile'; +import type { RushConfigurationProject } from '../../api/RushConfigurationProject'; +import type { BaseProjectShrinkwrapFile } from '../base/BaseProjectShrinkwrapFile'; +import type { Subspace } from '../../api/Subspace'; /** * @yarnpkg/lockfile doesn't have types @@ -81,6 +88,11 @@ interface IYarnShrinkwrapJson { [packageNameAndSemVer: string]: IYarnShrinkwrapEntry; } +// Example inputs: +// "js-tokens@^3.0.0 || ^4.0.0" +// "@rush-temp/api-extractor-test-03@file:./projects/api-extractor-test-03.tgz" +const _packageNameAndSemVerRegExp: RegExp = /^(@?[^@\s]+)(?:@(.*))?$/; + /** * Support for consuming the "yarn.lock" file. * @@ -95,11 +107,6 @@ interface IYarnShrinkwrapJson { export class YarnShrinkwrapFile extends BaseShrinkwrapFile { public readonly isWorkspaceCompatible: boolean; - // Example inputs: - // "js-tokens@^3.0.0 || ^4.0.0" - // "@rush-temp/api-extractor-test-03@file:./projects/api-extractor-test-03.tgz" - private static _packageNameAndSemVerRegExp: RegExp = /^(@?[^@\s]+)(?:@(.*))?$/; - private _shrinkwrapJson: IYarnShrinkwrapJson; private _tempProjectNames: string[]; @@ -112,7 +119,7 @@ export class YarnShrinkwrapFile extends BaseShrinkwrapFile { for (const key of Object.keys(this._shrinkwrapJson)) { // Example key: - const packageNameAndSemVer: IPackageNameAndSemVer = YarnShrinkwrapFile._decodePackageNameAndSemVer(key); + const packageNameAndSemVer: IPackageNameAndSemVer = _decodePackageNameAndSemVer(key); // If it starts with @rush-temp, then include it: if ( @@ -181,61 +188,14 @@ export class YarnShrinkwrapFile extends BaseShrinkwrapFile { return new YarnShrinkwrapFile(shrinkwrapJson.object); } - /** - * The `@yarnpkg/lockfile` API only partially deserializes its data, and expects the caller - * to parse the yarn.lock lookup keys (sometimes called a "pattern"). - * - * Example input: "js-tokens@^3.0.0 || ^4.0.0" - * Example output: { packageName: "js-tokens", semVerRange: "^3.0.0 || ^4.0.0" } - */ - private static _decodePackageNameAndSemVer(packageNameAndSemVer: string): IPackageNameAndSemVer { - const result: RegExpExecArray | null = - YarnShrinkwrapFile._packageNameAndSemVerRegExp.exec(packageNameAndSemVer); - if (!result) { - // Sanity check -- this should never happen - throw new Error( - 'Unable to parse package/semver expression in the Yarn shrinkwrap file (yarn.lock): ' + - JSON.stringify(packageNameAndSemVer) - ); - } - - const packageName: string = result[1] || ''; - const parsedPackageName: IParsedPackageNameOrError = PackageNameParsers.permissive.tryParse(packageName); - if (parsedPackageName.error) { - // Sanity check -- this should never happen - throw new Error( - 'Invalid package name the Yarn shrinkwrap file (yarn.lock): ' + - JSON.stringify(packageNameAndSemVer) + - '\n' + - parsedPackageName.error - ); - } - - return { - packageName, - semVerRange: result[2] || '' - }; - } - - /** - * This is the inverse of _decodePackageNameAndSemVer(): - * Given an IPackageNameAndSemVer object, recreate the yarn.lock lookup key - * (sometimes called a "pattern"). - */ - private static _encodePackageNameAndSemVer(packageNameAndSemVer: IPackageNameAndSemVer): string { - return packageNameAndSemVer.packageName + '@' + packageNameAndSemVer.semVerRange; - } - - /** @override */ - public getTempProjectNames(): ReadonlyArray { + public override getTempProjectNames(): ReadonlyArray { return this._tempProjectNames; } - /** @override */ - public hasCompatibleTopLevelDependency(dependencySpecifier: DependencySpecifier): boolean { + public override hasCompatibleTopLevelDependency(dependencySpecifier: DependencySpecifier): boolean { // It seems like we should normalize the key somehow, but Yarn apparently does not // do any normalization. - const key: string = YarnShrinkwrapFile._encodePackageNameAndSemVer({ + const key: string = _encodePackageNameAndSemVer({ packageName: dependencySpecifier.packageName, semVerRange: dependencySpecifier.versionSpecifier }); @@ -244,44 +204,82 @@ export class YarnShrinkwrapFile extends BaseShrinkwrapFile { return Object.hasOwnProperty.call(this._shrinkwrapJson, key); } - /** @override */ - public tryEnsureCompatibleDependency( + public override tryEnsureCompatibleDependency( dependencySpecifier: DependencySpecifier, tempProjectName: string ): boolean { return this.hasCompatibleTopLevelDependency(dependencySpecifier); } - /** @override */ - protected serialize(): string { + protected override serialize(): string { return lockfileModule.stringify(this._shrinkwrapJson); } - /** @override */ - protected getTopLevelDependencyVersion(dependencyName: string): DependencySpecifier | undefined { + protected override getTopLevelDependencyVersion(dependencyName: string): DependencySpecifier | undefined { throw new InternalError('Not implemented'); } - /** @override */ - protected tryEnsureDependencyVersion( + protected override tryEnsureDependencyVersion( dependencySpecifier: DependencySpecifier, tempProjectName: string ): DependencySpecifier | undefined { throw new InternalError('Not implemented'); } - /** @override */ - public getProjectShrinkwrap( + public override getProjectShrinkwrap( project: RushConfigurationProject ): BaseProjectShrinkwrapFile | undefined { return undefined; } - /** @override */ - public async isWorkspaceProjectModifiedAsync( + public override async isWorkspaceProjectModifiedAsync( project: RushConfigurationProject, - variant?: string + subspace: Subspace ): Promise { throw new InternalError('Not implemented'); } } + +/** + * The `@yarnpkg/lockfile` API only partially deserializes its data, and expects the caller + * to parse the yarn.lock lookup keys (sometimes called a "pattern"). + * + * Example input: "js-tokens@^3.0.0 || ^4.0.0" + * Example output: { packageName: "js-tokens", semVerRange: "^3.0.0 || ^4.0.0" } + */ +function _decodePackageNameAndSemVer(packageNameAndSemVer: string): IPackageNameAndSemVer { + const result: RegExpExecArray | null = _packageNameAndSemVerRegExp.exec(packageNameAndSemVer); + if (!result) { + // Sanity check -- this should never happen + throw new Error( + 'Unable to parse package/semver expression in the Yarn shrinkwrap file (yarn.lock): ' + + JSON.stringify(packageNameAndSemVer) + ); + } + + const packageName: string = result[1] || ''; + const parsedPackageName: IParsedPackageNameOrError = PackageNameParsers.permissive.tryParse(packageName); + if (parsedPackageName.error) { + // Sanity check -- this should never happen + throw new Error( + 'Invalid package name the Yarn shrinkwrap file (yarn.lock): ' + + JSON.stringify(packageNameAndSemVer) + + '\n' + + parsedPackageName.error + ); + } + + return { + packageName, + semVerRange: result[2] || '' + }; +} + +/** + * This is the inverse of _decodePackageNameAndSemVer(): + * Given an IPackageNameAndSemVer object, recreate the yarn.lock lookup key + * (sometimes called a "pattern"). + */ +function _encodePackageNameAndSemVer(packageNameAndSemVer: IPackageNameAndSemVer): string { + return packageNameAndSemVer.packageName + '@' + packageNameAndSemVer.semVerRange; +} diff --git a/libraries/rush-lib/src/npm-check-typings.d.ts b/libraries/rush-lib/src/npm-check-typings.d.ts index fe2a2115c5b..5c25d1d8c93 100644 --- a/libraries/rush-lib/src/npm-check-typings.d.ts +++ b/libraries/rush-lib/src/npm-check-typings.d.ts @@ -31,6 +31,7 @@ declare module 'npm-check' { packageWanted: string; // Requested version from the package.json. packageJson: string; // Version or range requested in the parent package.json. devDependency: boolean; // Is this a devDependency? + peerDependency: boolean; // Is this a peerDependency? usedInScripts: undefined | string[]; // Array of `scripts` in package.json that use this module. mismatch: boolean; // Does the version installed not match the range in package.json? semverValid: string; // Is the installed version valid semver? diff --git a/libraries/rush-lib/src/pluginFramework/OperationGraphHooks.ts b/libraries/rush-lib/src/pluginFramework/OperationGraphHooks.ts new file mode 100644 index 00000000000..f72bf682de4 --- /dev/null +++ b/libraries/rush-lib/src/pluginFramework/OperationGraphHooks.ts @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { + AsyncSeriesBailHook, + AsyncSeriesHook, + AsyncSeriesWaterfallHook, + SyncHook, + SyncWaterfallHook +} from 'tapable'; + +import type { Operation } from '../logic/operations/Operation'; +import type { + IOperationExecutionResult, + IConfigurableOperation +} from '../logic/operations/IOperationExecutionResult'; +import type { OperationStatus } from '../logic/operations/OperationStatus'; +import type { IOperationRunnerContext } from '../logic/operations/IOperationRunner'; +import type { ITelemetryData } from '../logic/Telemetry'; +import type { IEnvironment } from '../utilities/Utilities'; +import type { IOperationGraph, IOperationGraphIterationOptions } from '../logic/operations/IOperationGraph'; + +/** + * Hooks into the execution process for operations within the graph. + * + * Per-iteration lifecycle: + * 1. `configureIteration` - Synchronously decide which operations to enable for the next iteration. + * 2. `onIterationScheduled` - Fires after the iteration is prepared but before execution begins, if it has any enabled operations. + * 3. `beforeExecuteIterationAsync` - Async hook that can bail out the iteration entirely. + * 4. Operations execute (status changes reported via `onExecutionStatesUpdated`). + * 5. `afterExecuteIterationAsync` - Fires after all operations in the iteration have settled. + * 6. `onIdle` - Fires when the graph enters idle state awaiting changes (watch mode only). + * + * Additional hooks: + * - `onEnableStatesChanged` - Fires when `setEnabledStates` mutates operation enabled flags. + * - `onInvalidateOperations` - Fires when operations are invalidated (e.g. by file watchers). + * - `onGraphStateChanged` - Fires on any observable graph state change. + * + * @alpha + */ +export class OperationGraphHooks { + /** + * Hook invoked to decide what work a potential new iteration contains. + * Use the `lastExecutedRecords` to determine which operations are new or have had their inputs changed. + * Set the `enabled` states on the values in `initialRecords` to control which operations will be executed. + * + * @remarks + * This hook is synchronous to guarantee that the `lastExecutedRecords` map remains stable for the + * duration of configuration. This hook often executes while an execution iteration is currently running, so + * operations could complete if there were async ticks during the configuration phase. + * + * If no operations are marked for execution, the iteration will not be scheduled. + * If there is an existing scheduled iteration, it will remain. + */ + public readonly configureIteration: SyncHook< + [ + ReadonlyMap, + ReadonlyMap, + IOperationGraphIterationOptions + ] + > = new SyncHook(['initialRecords', 'lastExecutedRecords', 'context'], 'configureIteration'); + + /** + * Hook invoked before operation start for an iteration. Allows a plugin to perform side-effects or + * short-circuit the entire iteration. + * + * If any tap returns an {@link OperationStatus}, the remaining taps are skipped and the iteration will + * end immediately with that status. Operations which have not yet executed are marked Skipped if the + * returned status is successful (e.g. `Success`, `FromCache`, `NoOp`); otherwise they are marked Aborted. + */ + public readonly beforeExecuteIterationAsync: AsyncSeriesBailHook< + [ReadonlyMap, IOperationGraphIterationOptions], + OperationStatus | undefined | void + > = new AsyncSeriesBailHook(['records', 'context'], 'beforeExecuteIterationAsync'); + + /** + * Batched hook invoked when one or more operation statuses have changed during the same microtask. + * The hook receives an array of the operation execution results that changed status. + * @remarks + * This hook is batched to reduce noise when updating many operations synchronously in quick succession. + */ + public readonly onExecutionStatesUpdated: SyncHook<[ReadonlySet]> = new SyncHook( + ['records'], + 'onExecutionStatesUpdated' + ); + + /** + * Hook invoked when one or more operations have their enabled state mutated via + * {@link IOperationGraph.setEnabledStates}. Provides the set of operations whose + * enabled state actually changed. + */ + public readonly onEnableStatesChanged: SyncHook<[ReadonlySet]> = new SyncHook( + ['operations'], + 'onEnableStatesChanged' + ); + + /** + * Hook invoked immediately after a new execution iteration is scheduled (i.e. operations selected and prepared), + * before any operations in that iteration have started executing. Can be used to snapshot planned work, + * drive UIs, or pre-compute auxiliary data. + */ + public readonly onIterationScheduled: SyncHook<[ReadonlyMap]> = + new SyncHook(['records'], 'onIterationScheduled'); + + /** + * Hook invoked when any observable state on the operation graph changes. + * This includes configuration mutations (parallelism, quiet/debug modes, pauseNextIteration) + * as well as dynamic state (status transitions, scheduled iteration availability, etc.). + * Hook is series for stable output. + */ + public readonly onGraphStateChanged: SyncHook<[IOperationGraph]> = new SyncHook( + ['operationGraph'], + 'onGraphStateChanged' + ); + + /** + * Hook invoked when operations are invalidated for any reason. + */ + public readonly onInvalidateOperations: SyncHook<[Iterable, string | undefined]> = new SyncHook( + ['operations', 'reason'], + 'onInvalidateOperations' + ); + + /** + * Hook invoked after an iteration has finished and the command is watching for changes. + * May be used to display additional relevant data to the user. + * Only relevant when running in watch mode. + */ + public readonly onIdle: SyncHook = new SyncHook(undefined, 'onIdle'); + + /** + * Hook invoked after executing a set of operations. + * Hook is series for stable output. + */ + public readonly afterExecuteIterationAsync: AsyncSeriesWaterfallHook< + [OperationStatus, ReadonlyMap, IOperationGraphIterationOptions] + > = new AsyncSeriesWaterfallHook(['status', 'results', 'context'], 'afterExecuteIterationAsync'); + + /** + * Hook invoked after executing an iteration, before the telemetry entry is written. + * Allows the caller to augment or modify the log entry. + */ + public readonly beforeLog: SyncHook = new SyncHook(['telemetryData'], 'beforeLog'); + + /** + * Hook invoked before executing a operation. + */ + public readonly beforeExecuteOperationAsync: AsyncSeriesBailHook< + [IOperationRunnerContext & IOperationExecutionResult], + OperationStatus | undefined + > = new AsyncSeriesBailHook(['runnerContext'], 'beforeExecuteOperationAsync'); + + /** + * Hook invoked to define environment variables for an operation. + * May be invoked by the runner to get the environment for the operation. + */ + public readonly createEnvironmentForOperation: SyncWaterfallHook< + [IEnvironment, IOperationRunnerContext & IOperationExecutionResult] + > = new SyncWaterfallHook(['environment', 'runnerContext'], 'createEnvironmentForOperation'); + + /** + * Hook invoked after executing a operation. + */ + public readonly afterExecuteOperationAsync: AsyncSeriesHook< + [IOperationRunnerContext & IOperationExecutionResult] + > = new AsyncSeriesHook(['runnerContext'], 'afterExecuteOperationAsync'); +} diff --git a/libraries/rush-lib/src/pluginFramework/PhasedCommandHooks.ts b/libraries/rush-lib/src/pluginFramework/PhasedCommandHooks.ts index 7b69ae88cb5..d3a7fc15074 100644 --- a/libraries/rush-lib/src/pluginFramework/PhasedCommandHooks.ts +++ b/libraries/rush-lib/src/pluginFramework/PhasedCommandHooks.ts @@ -1,18 +1,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { AsyncSeriesHook, AsyncSeriesWaterfallHook, SyncHook } from 'tapable'; +import { AsyncSeriesHook, AsyncSeriesWaterfallHook } from 'tapable'; import type { CommandLineParameter } from '@rushstack/ts-command-line'; + import type { BuildCacheConfiguration } from '../api/BuildCacheConfiguration'; import type { IPhase } from '../api/CommandLineConfiguration'; import type { RushConfiguration } from '../api/RushConfiguration'; import type { RushConfigurationProject } from '../api/RushConfigurationProject'; - import type { Operation } from '../logic/operations/Operation'; -import type { ProjectChangeAnalyzer } from '../logic/ProjectChangeAnalyzer'; -import { ITelemetryData } from '../logic/Telemetry'; -import { IExecutionResult, IOperationExecutionResult } from '../logic/operations/IOperationExecutionResult'; +import type { CobuildConfiguration } from '../api/CobuildConfiguration'; +import type { RushProjectConfiguration } from '../api/RushProjectConfiguration'; +import type { Parallelism } from '../logic/operations/ParseParallelism'; +import type { IInputsSnapshot } from '../logic/incremental/InputsSnapshot'; +import type { IOperationGraph } from '../logic/operations/IOperationGraph'; /** * A plugin that interacts with a phased commands. @@ -34,46 +36,55 @@ export interface ICreateOperationsContext { * The configuration for the build cache, if the feature is enabled. */ readonly buildCacheConfiguration: BuildCacheConfiguration | undefined; + /** + * If true, for an incremental build, Rush will only include projects with immediate changes or projects with no consumers. + * @remarks + * This is an optimization that may produce invalid outputs if some of the intervening projects are impacted by the changes. + */ + readonly changedProjectsOnly: boolean; + /** + * The configuration for the cobuild, if cobuild feature and build cache feature are both enabled. + */ + readonly cobuildConfiguration: CobuildConfiguration | undefined; /** * The set of custom parameters for the executing command. * Maps from the `longName` field in command-line.json to the parser configuration in ts-command-line. */ readonly customParameters: ReadonlyMap; + /** + * If true, dependencies of the selected phases will be automatically enabled in the execution. + */ + readonly includePhaseDeps: boolean; /** * If true, projects may read their output from cache or be skipped if already up to date. * If false, neither of the above may occur, e.g. "rush rebuild" */ readonly isIncrementalBuildAllowed: boolean; - /** - * If true, this is the initial run of the command. - * If false, this execution is in response to changes. - */ - readonly isInitial: boolean; /** * If true, the command is running in watch mode. */ readonly isWatch: boolean; /** - * The set of phases original for the current command execution. + * The currently configured maximum parallelism for the command. */ - readonly phaseOriginal: ReadonlySet; + readonly parallelism: Parallelism; /** - * The set of phases selected for the current command execution. + * The set of phases selected for execution. */ readonly phaseSelection: ReadonlySet; /** - * The current state of the repository + * All successfully loaded rush-project.json data for selected projects. */ - readonly projectChangeAnalyzer: ProjectChangeAnalyzer; + readonly projectConfigurations: ReadonlyMap; /** - * The set of Rush projects selected for the current command execution. + * The set of Rush projects selected for execution. */ readonly projectSelection: ReadonlySet; /** - * The set of Rush projects that have not been built in the current process since they were last modified. - * When `isInitial` is true, this will be an exact match of `projectSelection`. + * If true, the operation graph should include all projects in the repository (watch broad graph mode). + * Only the projects in projectSelection should start enabled; others are present but disabled. */ - readonly projectsInUnknownState: ReadonlySet; + readonly generateFullGraph?: boolean; /** * The Rush configuration */ @@ -81,48 +92,39 @@ export interface ICreateOperationsContext { } /** - * Hooks into the execution process for phased commands + * Context used for configuring the operation graph. * @alpha */ -export class PhasedCommandHooks { +export interface IOperationGraphContext extends ICreateOperationsContext { /** - * Hook invoked to create operations for execution. - * Use the context to distinguish between the initial run and phased runs. + * The current state of the repository, if available. + * Not part of the creation context to avoid the overhead of Git calls when initializing the graph. */ - public readonly createOperations: AsyncSeriesWaterfallHook<[Set, ICreateOperationsContext]> = - new AsyncSeriesWaterfallHook(['operations', 'context'], 'createOperations'); - - /** - * Hook invoked before operation start - * Hook is series for stable output. - */ - public readonly beforeExecuteOperations: AsyncSeriesHook<[Map]> = - new AsyncSeriesHook(['records']); - - /** - * Hook invoked when operation status changed - * Hook is series for stable output. - */ - public readonly onOperationStatusChanged: SyncHook<[IOperationExecutionResult]> = new SyncHook(['record']); - - /** - * Hook invoked after executing a set of operations. - * Use the context to distinguish between the initial run and phased runs. - * Hook is series for stable output. - */ - public readonly afterExecuteOperations: AsyncSeriesHook<[IExecutionResult, ICreateOperationsContext]> = - new AsyncSeriesHook(['results', 'context']); + readonly initialSnapshot?: IInputsSnapshot; +} +/** + * Hooks into the execution process for phased commands. + * + * Lifecycle: + * 1. `createOperationsAsync` - Invoked to populate the set of operations for execution. + * 2. `onGraphCreatedAsync` - Invoked after the operation graph is created, allowing plugins to + * tap into graph-level hooks (e.g. `configureIteration`, `onIdle`). + * See {@link OperationGraphHooks} for the per-iteration lifecycle. + * + * @alpha + */ +export class PhasedCommandHooks { /** - * Hook invoked after a run has finished and the command is watching for changes. - * May be used to display additional relevant data to the user. - * Only relevant when running in watch mode. + * Hook invoked to create operations for execution. */ - public readonly waitingForChanges: SyncHook = new SyncHook(undefined, 'waitingForChanges'); + public readonly createOperationsAsync: AsyncSeriesWaterfallHook< + [Set, ICreateOperationsContext] + > = new AsyncSeriesWaterfallHook(['operations', 'context'], 'createOperationsAsync'); /** - * Hook invoked after executing operations and before waitingForChanges. Allows the caller - * to augment or modify the log entry about to be written. + * Hook invoked when the operation graph is created, allowing the plugin to tap into it and interact with it. */ - public readonly beforeLog: SyncHook = new SyncHook(['telemetryData'], 'beforeLog'); + public readonly onGraphCreatedAsync: AsyncSeriesHook<[IOperationGraph, IOperationGraphContext]> = + new AsyncSeriesHook(['operationGraph', 'context'], 'onGraphCreatedAsync'); } diff --git a/libraries/rush-lib/src/pluginFramework/PluginLoader/AutoinstallerPluginLoader.ts b/libraries/rush-lib/src/pluginFramework/PluginLoader/AutoinstallerPluginLoader.ts index 07a8d165247..7528fe72ea7 100644 --- a/libraries/rush-lib/src/pluginFramework/PluginLoader/AutoinstallerPluginLoader.ts +++ b/libraries/rush-lib/src/pluginFramework/PluginLoader/AutoinstallerPluginLoader.ts @@ -1,16 +1,24 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import { FileSystem, JsonFile, JsonObject, JsonSchema } from '@rushstack/node-core-library'; +import * as path from 'node:path'; -import { IRushPluginConfiguration } from '../../api/RushPluginsConfiguration'; +import { + FileSystem, + JsonFile, + NewlineKind, + PosixModeBits, + type JsonObject, + type JsonSchema +} from '@rushstack/node-core-library'; + +import type { IRushPluginConfiguration } from '../../api/RushPluginsConfiguration'; import { Autoinstaller } from '../../logic/Autoinstaller'; import { RushConstants } from '../../logic/RushConstants'; import { - IPluginLoaderOptions, - IRushPluginManifest, - IRushPluginManifestJson, + type IPluginLoaderOptions, + type IRushPluginManifest, + type IRushPluginManifestJson, PluginLoaderBase } from './PluginLoaderBase'; import type { RushGlobalFolder } from '../../api/RushGlobalFolder'; @@ -60,10 +68,20 @@ export class AutoinstallerPluginLoader extends PluginLoaderBase item.pluginName === pluginName @@ -83,10 +101,20 @@ export class AutoinstallerPluginLoader extends PluginLoaderBase = new (options: T) => IRushPlugin; + type IRushPluginCtor = new (opts: T) => IRushPlugin; let pluginPackage: IRushPluginCtor; try { - // eslint-disable-next-line @typescript-eslint/no-var-requires const loadedPluginPackage: IRushPluginCtor | { default: IRushPluginCtor } = require(resolvedPluginPath); pluginPackage = (loadedPluginPackage as { default: IRushPluginCtor }).default || loadedPluginPackage; } catch (e) { diff --git a/libraries/rush-lib/src/pluginFramework/PluginLoader/RushSdk.ts b/libraries/rush-lib/src/pluginFramework/PluginLoader/RushSdk.ts index 06641aadffc..472990c257c 100644 --- a/libraries/rush-lib/src/pluginFramework/PluginLoader/RushSdk.ts +++ b/libraries/rush-lib/src/pluginFramework/PluginLoader/RushSdk.ts @@ -2,23 +2,22 @@ // See LICENSE in the project root for license information. type RushLibModuleType = Record; -declare const global: NodeJS.Global & - typeof globalThis & { - ___rush___rushLibModule?: RushLibModuleType; - }; +declare const global: typeof globalThis & { + ___rush___rushLibModule?: RushLibModuleType; +}; -export class RushSdk { - private static _initialized: boolean = false; +let _initialized: boolean = false; +export class RushSdk { public static ensureInitialized(): void { - if (!RushSdk._initialized) { + if (!_initialized) { const rushLibModule: RushLibModuleType = require('../../index'); // The "@rushstack/rush-sdk" shim will look for this global variable to obtain // Rush's instance of "@microsoft/rush-lib". global.___rush___rushLibModule = rushLibModule; - RushSdk._initialized = true; + _initialized = true; } } } diff --git a/libraries/rush-lib/src/pluginFramework/PluginManager.ts b/libraries/rush-lib/src/pluginFramework/PluginManager.ts index cf2996c7c7d..9a5181e078c 100644 --- a/libraries/rush-lib/src/pluginFramework/PluginManager.ts +++ b/libraries/rush-lib/src/pluginFramework/PluginManager.ts @@ -1,15 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { FileSystem, Import, InternalError, ITerminal } from '@rushstack/node-core-library'; +import { FileSystem, Import, InternalError } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; -import { CommandLineConfiguration } from '../api/CommandLineConfiguration'; -import { RushConfiguration } from '../api/RushConfiguration'; -import { BuiltInPluginLoader, IBuiltInPluginConfiguration } from './PluginLoader/BuiltInPluginLoader'; -import { IRushPlugin } from './IRushPlugin'; +import type { CommandLineConfiguration } from '../api/CommandLineConfiguration'; +import type { RushConfiguration } from '../api/RushConfiguration'; +import { BuiltInPluginLoader, type IBuiltInPluginConfiguration } from './PluginLoader/BuiltInPluginLoader'; +import type { IRushPlugin } from './IRushPlugin'; import { AutoinstallerPluginLoader } from './PluginLoader/AutoinstallerPluginLoader'; -import { RushSession } from './RushSession'; -import { PluginLoaderBase } from './PluginLoader/PluginLoaderBase'; +import type { RushSession } from './RushSession'; +import type { PluginLoaderBase } from './PluginLoader/PluginLoaderBase'; import { Rush } from '../api/Rush'; import type { RushGlobalFolder } from '../api/RushGlobalFolder'; diff --git a/libraries/rush-lib/src/pluginFramework/RushLifeCycle.ts b/libraries/rush-lib/src/pluginFramework/RushLifeCycle.ts index fbe80696d1b..d5f68d63ffa 100644 --- a/libraries/rush-lib/src/pluginFramework/RushLifeCycle.ts +++ b/libraries/rush-lib/src/pluginFramework/RushLifeCycle.ts @@ -2,9 +2,12 @@ // See LICENSE in the project root for license information. import { AsyncParallelHook, AsyncSeriesHook, HookMap } from 'tapable'; -import type { ITelemetryData } from '../logic/Telemetry'; +import type { CommandLineParameter } from '@rushstack/ts-command-line'; + +import type { ITelemetryData } from '../logic/Telemetry'; import type { PhasedCommandHooks } from './PhasedCommandHooks'; +import type { Subspace } from '../api/Subspace'; /** * Information about the currently executing command provided to plugins. @@ -22,7 +25,17 @@ export interface IRushCommand { * @beta */ export interface IGlobalCommand extends IRushCommand { - // Nothing added. + /** + * Get a parameter by its long name (e.g. "--output-path") that was defined in command-line.json for this command. + * If the parameter was not defined or not provided on the command line, this will throw. + */ + getCustomParametersByLongName(longName: string): TParameter; + + /** + * Call this from a plugin hook to indicate that the command has been fully handled + * by the plugin. When set, the default shell command execution will be skipped. + */ + setHandled(): void; } /** @@ -35,6 +48,13 @@ export interface IPhasedCommand extends IRushCommand { * @alpha */ readonly hooks: PhasedCommandHooks; + + /** + * An abort controller that can be used to abort the command. + * Long-lived plugins should listen to the signal to handle any cleanup logic. + * @alpha + */ + readonly sessionAbortController: AbortController; } /** @@ -46,7 +66,7 @@ export class RushLifecycleHooks { /** * The hook to run before executing any Rush CLI Command. */ - public initialize: AsyncSeriesHook = new AsyncSeriesHook( + public readonly initialize: AsyncSeriesHook = new AsyncSeriesHook( ['command'], 'initialize' ); @@ -54,22 +74,23 @@ export class RushLifecycleHooks { /** * The hook to run before executing any global Rush CLI Command (defined in command-line.json). */ - public runAnyGlobalCustomCommand: AsyncSeriesHook = new AsyncSeriesHook( - ['command'], - 'runAnyGlobalCustomCommand' - ); + public readonly runAnyGlobalCustomCommand: AsyncSeriesHook = + new AsyncSeriesHook(['command'], 'runAnyGlobalCustomCommand'); /** * A hook map to allow plugins to hook specific named global commands (defined in command-line.json) before execution. */ - public runGlobalCustomCommand: HookMap> = new HookMap((key: string) => { - return new AsyncSeriesHook(['command'], key); - }, 'runGlobalCustomCommand'); + public readonly runGlobalCustomCommand: HookMap> = new HookMap( + (key: string) => { + return new AsyncSeriesHook(['command'], key); + }, + 'runGlobalCustomCommand' + ); /** * The hook to run before executing any phased Rush CLI Command (defined in command-line.json, or the default "build" or "rebuild"). */ - public runAnyPhasedCommand: AsyncSeriesHook = new AsyncSeriesHook( + public readonly runAnyPhasedCommand: AsyncSeriesHook = new AsyncSeriesHook( ['command'], 'runAnyPhasedCommand' ); @@ -77,22 +98,28 @@ export class RushLifecycleHooks { /** * A hook map to allow plugins to hook specific named phased commands (defined in command-line.json) before execution. */ - public runPhasedCommand: HookMap> = new HookMap((key: string) => { + public readonly runPhasedCommand: HookMap> = new HookMap((key: string) => { return new AsyncSeriesHook(['command'], key); }, 'runPhasedCommand'); /** * The hook to run between preparing the common/temp folder and invoking the package manager during "rush install" or "rush update". */ - public beforeInstall: AsyncSeriesHook = new AsyncSeriesHook( - ['command'], - 'beforeInstall' - ); + public readonly beforeInstall: AsyncSeriesHook< + [command: IRushCommand, subspace: Subspace, variant: string | undefined] + > = new AsyncSeriesHook(['command', 'subspace', 'variant'], 'beforeInstall'); + + /** + * The hook to run after a successful install. + */ + public readonly afterInstall: AsyncSeriesHook< + [command: IRushCommand, subspace: Subspace, variant: string | undefined] + > = new AsyncSeriesHook(['command', 'subspace', 'variant'], 'afterInstall'); /** * A hook to allow plugins to hook custom logic to process telemetry data. */ - public flushTelemetry: AsyncParallelHook<[ReadonlyArray]> = new AsyncParallelHook( + public readonly flushTelemetry: AsyncParallelHook<[ReadonlyArray]> = new AsyncParallelHook( ['telemetryData'], 'flushTelemetry' ); diff --git a/libraries/rush-lib/src/pluginFramework/RushSession.ts b/libraries/rush-lib/src/pluginFramework/RushSession.ts index edfd502455d..0e512764438 100644 --- a/libraries/rush-lib/src/pluginFramework/RushSession.ts +++ b/libraries/rush-lib/src/pluginFramework/RushSession.ts @@ -1,11 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { InternalError, ITerminalProvider } from '@rushstack/node-core-library'; -import { IBuildCacheJson } from '../api/BuildCacheConfiguration'; -import { ICloudBuildCacheProvider } from '../logic/buildCache/ICloudBuildCacheProvider'; -import { ILogger, ILoggerOptions, Logger } from './logging/Logger'; +import { InternalError } from '@rushstack/node-core-library'; +import type { ITerminalProvider } from '@rushstack/terminal'; + +import { type ILogger, type ILoggerOptions, Logger } from './logging/Logger'; import { RushLifecycleHooks } from './RushLifeCycle'; +import type { IBuildCacheJson } from '../api/BuildCacheConfiguration'; +import type { ICloudBuildCacheProvider } from '../logic/buildCache/ICloudBuildCacheProvider'; +import type { ICobuildJson } from '../api/CobuildConfiguration'; +import type { ICobuildLockProvider } from '../logic/cobuild/ICobuildLockProvider'; /** * @beta @@ -22,12 +26,20 @@ export type CloudBuildCacheProviderFactory = ( buildCacheJson: IBuildCacheJson ) => ICloudBuildCacheProvider | Promise; +/** + * @beta + */ +export type CobuildLockProviderFactory = ( + cobuildJson: ICobuildJson +) => ICobuildLockProvider | Promise; + /** * @beta */ export class RushSession { private readonly _options: IRushSessionOptions; private readonly _cloudBuildCacheProviderFactories: Map = new Map(); + private readonly _cobuildLockProviderFactories: Map = new Map(); public readonly hooks: RushLifecycleHooks; @@ -71,4 +83,22 @@ export class RushSession { ): CloudBuildCacheProviderFactory | undefined { return this._cloudBuildCacheProviderFactories.get(cacheProviderName); } + + public registerCobuildLockProviderFactory( + cobuildLockProviderName: string, + factory: CobuildLockProviderFactory + ): void { + if (this._cobuildLockProviderFactories.has(cobuildLockProviderName)) { + throw new Error( + `A cobuild lock provider factory for ${cobuildLockProviderName} has already been registered` + ); + } + this._cobuildLockProviderFactories.set(cobuildLockProviderName, factory); + } + + public getCobuildLockProviderFactory( + cobuildLockProviderName: string + ): CobuildLockProviderFactory | undefined { + return this._cobuildLockProviderFactories.get(cobuildLockProviderName); + } } diff --git a/libraries/rush-lib/src/pluginFramework/logging/Logger.ts b/libraries/rush-lib/src/pluginFramework/logging/Logger.ts index 79909913a35..46b01be524f 100644 --- a/libraries/rush-lib/src/pluginFramework/logging/Logger.ts +++ b/libraries/rush-lib/src/pluginFramework/logging/Logger.ts @@ -1,4 +1,7 @@ -import { ITerminalProvider, Terminal } from '@rushstack/node-core-library'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { type ITerminalProvider, Terminal } from '@rushstack/terminal'; /** * @beta diff --git a/libraries/rush-lib/src/schemas/build-cache.schema.json b/libraries/rush-lib/src/schemas/build-cache.schema.json index e91b4d361c7..2c7e8fd6967 100644 --- a/libraries/rush-lib/src/schemas/build-cache.schema.json +++ b/libraries/rush-lib/src/schemas/build-cache.schema.json @@ -8,6 +8,23 @@ "items": { "$ref": "#/definitions/anything" } + }, + "entraLoginFlow": { + "type": "string", + "description": "The Primary Entra ID login flow to use. Defaults to 'AdoCodespacesAuth' on GitHub Codespaces, 'VisualStudioCode' otherwise. If this flow fails it will fall back based on the configuration in `loginFlowFailover`.", + "enum": [ + "AdoCodespacesAuth", + "InteractiveBrowser", + "DeviceCode", + "VisualStudioCode", + "AzureCli", + "AzureDeveloperCli", + "AzurePowerShell" + ] + }, + "fallbackEntraLoginFlow": { + "$ref": "#/definitions/entraLoginFlow", + "description": "The Entra ID login flow to fall back to. If null, a failure in this login mode is terminal." } }, "type": "object", @@ -31,7 +48,11 @@ }, "cacheEntryNamePattern": { "type": "string", - "description": "Setting this property overrides the cache entry ID. If this property is set, it must contain a [hash] token. It may also contain a [projectName] or a [projectName:normalize] token." + "description": "Setting this property overrides the cache entry ID. If this property is set, it must contain a [hash] token. It may also contain one of the following tokens: [projectName], [projectName:normalize], [phaseName], [phaseName:normalize], [phaseName:trimPrefix], [os], and [arch]." + }, + "cacheHashSalt": { + "type": "string", + "description": "An optional salt to inject during calculation of the cache key. This can be used to invalidate the cache for all projects when the salt changes." }, "azureBlobStorageConfiguration": { "type": "object", @@ -50,6 +71,58 @@ "description": "The Azure environment the storage account exists in. Defaults to AzurePublicCloud.", "enum": ["AzurePublicCloud", "AzureChina", "AzureGermany", "AzureGovernment"] }, + "loginFlow": { + "$ref": "#/definitions/entraLoginFlow" + }, + "loginFlowFailover": { + "type": "object", + "description": "Optional configuration for a fallback login flow if the primary login flow fails. If not defined, the default order is: AdoCodespacesAuth -> VisualStudioCode -> AzureCli -> AzureDeveloperCli -> AzurePowerShell -> InteractiveBrowser -> DeviceCode.", + "additionalProperties": false, + "properties": { + "AdoCodespacesAuth": { + "allOf": [ + { "$ref": "#/definitions/fallbackEntraLoginFlow" }, + { "not": { "enum": ["AdoCodespacesAuth"] } } + ] + }, + "InteractiveBrowser": { + "allOf": [ + { "$ref": "#/definitions/fallbackEntraLoginFlow" }, + { "not": { "enum": ["InteractiveBrowser"] } } + ] + }, + "DeviceCode": { + "allOf": [ + { "$ref": "#/definitions/fallbackEntraLoginFlow" }, + { "not": { "enum": ["DeviceCode"] } } + ] + }, + "VisualStudioCode": { + "allOf": [ + { "$ref": "#/definitions/fallbackEntraLoginFlow" }, + { "not": { "enum": ["VisualStudioCode"] } } + ] + }, + "AzureCli": { + "allOf": [ + { "$ref": "#/definitions/fallbackEntraLoginFlow" }, + { "not": { "enum": ["AzureCli"] } } + ] + }, + "AzureDeveloperCli": { + "allOf": [ + { "$ref": "#/definitions/fallbackEntraLoginFlow" }, + { "not": { "enum": ["AzureDeveloperCli"] } } + ] + }, + "AzurePowerShell": { + "allOf": [ + { "$ref": "#/definitions/fallbackEntraLoginFlow" }, + { "not": { "enum": ["AzurePowerShell"] } } + ] + } + } + }, "blobPrefix": { "type": "string", "description": "An optional prefix for cache item blob names." @@ -57,6 +130,10 @@ "isCacheWriteAllowed": { "type": "boolean", "description": "If set to true, allow writing to the cache. Defaults to false." + }, + "readRequiresAuthentication": { + "type": "boolean", + "description": "If set to true, reading the cache requires authentication. Defaults to false." } } }, @@ -144,7 +221,17 @@ "properties": { "cacheProvider": { "type": "string", - "pattern": "^(?:(?!azure-blob-storage|amazon-s3|http).)*$" + "pattern": "^(?:(?!local-only|azure-blob-storage|amazon-s3|http).)*$" + } + } + }, + { + "type": "object", + "additionalProperties": true, + "properties": { + "cacheProvider": { + "type": "string", + "enum": ["local-only"] } } }, diff --git a/libraries/rush-lib/src/schemas/cobuild.schema.json b/libraries/rush-lib/src/schemas/cobuild.schema.json new file mode 100644 index 00000000000..6fe630b89d8 --- /dev/null +++ b/libraries/rush-lib/src/schemas/cobuild.schema.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Configuration for Rush's cobuild.", + "description": "For use with the Rush tool, this file provides configuration options for cobuild feature. See http://rushjs.io for details.", + "definitions": { + "anything": { + "type": ["array", "boolean", "integer", "number", "object", "string"], + "items": { + "$ref": "#/definitions/anything" + } + } + }, + "type": "object", + "allOf": [ + { + "type": "object", + "additionalProperties": false, + "required": ["cobuildFeatureEnabled", "cobuildLockProvider"], + "properties": { + "$schema": { + "description": "Part of the JSON Schema standard, this optional keyword declares the URL of the schema that the file conforms to. Editors may download the schema and use it to perform syntax highlighting.", + "type": "string" + }, + "cobuildFeatureEnabled": { + "description": "Set this to true to enable the cobuild feature.", + "type": "boolean" + }, + "cobuildLockProvider": { + "description": "Specify the cobuild lock provider to use", + "type": "string" + } + } + } + ] +} diff --git a/libraries/rush-lib/src/schemas/command-line.schema.json b/libraries/rush-lib/src/schemas/command-line.schema.json index 62821dfd777..195235d8e7f 100644 --- a/libraries/rush-lib/src/schemas/command-line.schema.json +++ b/libraries/rush-lib/src/schemas/command-line.schema.json @@ -18,7 +18,7 @@ "title": "Command Kind", "description": "Indicates the kind of command: \"bulk\" commands are run separately for each project; \"global\" commands are run once for the entire repository.", "type": "string", - "enum": ["bulk", "global", "phased"] + "enum": ["bulk", "global", "globalPlugin", "phased"] }, "name": { "title": "Custom Command Name", @@ -66,6 +66,11 @@ "description": "If true then this command can be run in parallel, i.e. executed simultaneously for multiple projects.", "type": "boolean" }, + "allowOversubscription": { + "title": "allowOversubscription", + "type": "boolean", + "description": "Controls whether weighted operations can start when the total weight would exceed the limit but is currently below the limit. This setting only applies when \"enableParallelism\" is true and operations have a \"weight\" property configured in their rush-project.json \"operationSettings\". Choose true (the default) to favor parallelism. Choose false to strictly stay under the limit." + }, "ignoreDependencyOrder": { "title": "ignoreDependencyOrder", "description": "Normally projects will be processed according to their dependency order: a given project will not start processing the command until all of its dependencies have completed. This restriction doesn't apply for certain operations, for example, a \"clean\" task that deletes output files. In this case you can set \"ignoreDependencyOrder\" to true to increase parallelism.", @@ -93,7 +98,7 @@ }, "disableBuildCache": { "title": "Disable build cache.", - "description": "Disable build cache for this action. This may be useful if this command affects state outside of projects' own folders.", + "description": "Disable build cache for this action. This may be useful if this command affects state outside of projects' own folders. If the build cache is not configured, this also disables the legacy skip detection logic.", "type": "boolean" } } @@ -110,6 +115,7 @@ "shellCommand": { "$ref": "#/definitions/anything" }, "enableParallelism": { "$ref": "#/definitions/anything" }, + "allowOversubscription": { "$ref": "#/definitions/anything" }, "ignoreDependencyOrder": { "$ref": "#/definitions/anything" }, "ignoreMissingScript": { "$ref": "#/definitions/anything" }, "incremental": { "$ref": "#/definitions/anything" }, @@ -162,6 +168,34 @@ } ] }, + "globalPluginCommand": { + "title": "Global Plugin Command", + "description": "A custom command that is run once for the entire repository, whose implementation is provided entirely by a Rush plugin. This command kind can only be used in command-line.json files provided by Rush plugins.", + "type": "object", + "allOf": [ + { "$ref": "#/definitions/baseCommand" }, + { + "type": "object", + "additionalProperties": true, + "properties": { + "commandKind": { + "enum": ["globalPlugin"] + } + } + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "commandKind": { "$ref": "#/definitions/anything" }, + "name": { "$ref": "#/definitions/anything" }, + "summary": { "$ref": "#/definitions/anything" }, + "description": { "$ref": "#/definitions/anything" }, + "safeForSimultaneousRushProcesses": { "$ref": "#/definitions/anything" } + } + } + ] + }, "phasedCommand": { "title": "Phased Command", "description": "A command that contains multiple phases, that are run separately for each project", @@ -181,6 +215,11 @@ "description": "If true then this command can be run in parallel, i.e. executed simultaneously for multiple projects.", "type": "boolean" }, + "allowOversubscription": { + "title": "allowOversubscription", + "type": "boolean", + "description": "Controls whether weighted operations can start when the total weight would exceed the limit but is currently below the limit. This setting only applies when \"enableParallelism\" is true and operations have a \"weight\" property configured in their rush-project.json \"operationSettings\". Choose true (the default) to favor parallelism. Choose false to strictly stay under the limit." + }, "incremental": { "title": "Incremental", "description": "If true then this command's phases will be incremental and support caching.", @@ -194,6 +233,11 @@ "type": "string" } }, + "disableBuildCache": { + "title": "Disable build cache.", + "description": "Disable build cache for this action. This may be useful if this command affects state outside of projects' own folders. If the build cache is not configured, this also disables the legacy skip detection logic.", + "type": "boolean" + }, "watchOptions": { "title": "Watch Options", "description": "Controls the file watching behavior of this command. If not specified, this command does not watch files.", @@ -213,11 +257,16 @@ }, "watchPhases": { "title": "Watch Phases", - "description": "List *exactly* the phases that should be run in watch mode for this command. If this property is specified and non-empty, after the phases defined in the \"phases\" property run, a file watcher will be started to watch projects for changes, and will run the phases listed in this property on changed projects.", + "description": "List *exactly* the phases that should be run in watch mode for this command. If this property is specified and non-empty, after the phases defined in the \"phases\" property run, a file watcher will be started to watch projects for changes, and will run the phases listed in this property on changed projects. Rush will prefer scripts named \"${phaseName}:incremental\" over \"${phaseName}\" for every iteration after the first, so you can reuse the same phase name but define different scripts, e.g. to not clean on incremental runs.", "type": "array", "items": { "type": "string" } + }, + "includeAllProjectsInWatchGraph": { + "title": "Include All Projects In Watch Graph", + "description": "If true, when entering watch mode Rush will construct the operation graph including every project in the repository (respecting phase selection), but will initially enable only those operations whose projects were selected by the user's CLI project selection parameters. Other projects will appear disabled until they change or become required by an enabled project's dependency graph. This can improve iteration by avoiding a full graph rebuild when broadening the selection mid-session.", + "type": "boolean" } } }, @@ -248,6 +297,7 @@ "safeForSimultaneousRushProcesses": { "$ref": "#/definitions/anything" }, "enableParallelism": { "$ref": "#/definitions/anything" }, + "allowOversubscription": { "$ref": "#/definitions/anything" }, "incremental": { "$ref": "#/definitions/anything" }, "phases": { "$ref": "#/definitions/anything" }, "watchOptions": { "$ref": "#/definitions/anything" }, @@ -695,6 +745,7 @@ "oneOf": [ { "$ref": "#/definitions/bulkCommand" }, { "$ref": "#/definitions/globalCommand" }, + { "$ref": "#/definitions/globalPluginCommand" }, { "$ref": "#/definitions/phasedCommand" } ] } diff --git a/libraries/rush-lib/src/schemas/common-versions.schema.json b/libraries/rush-lib/src/schemas/common-versions.schema.json index 456902848d6..35e3de17634 100644 --- a/libraries/rush-lib/src/schemas/common-versions.schema.json +++ b/libraries/rush-lib/src/schemas/common-versions.schema.json @@ -2,7 +2,6 @@ "$schema": "http://json-schema.org/draft-04/schema#", "title": "Rush common-versions.json config file", "description": "For use with the Rush tool, this file manages dependency versions that affect all projects in the repo. See http://rushjs.io for details.", - "type": "object", "properties": { "$schema": { @@ -20,6 +19,10 @@ "description": "When set to true, for all projects in the repo, all dependencies will be automatically added as preferredVersions, except in cases where different projects specify different version ranges for a given dependency. For older package managers, this tended to reduce duplication of indirect dependencies. However, it can sometimes cause trouble for indirect dependencies with incompatible peerDependencies ranges.", "type": "boolean" }, + "ensureConsistentVersions": { + "description": "If true, consistent version specifiers for dependencies will be enforced (i.e. \"rush check\" is run before some commands).", + "type": "boolean" + }, "allowedAlternativeVersions": { "description": "The \"rush check\" command can be used to enforce that every project in the repo must specify the same SemVer range for a given dependency. However, sometimes exceptions are needed. The allowedAlternativeVersions table allows you to list other SemVer ranges that will be accepted by \"rush check\" for a given dependency. Note that the normal version range (as inferred by looking at all projects in the repo) should NOT be included in this list.", "type": "object", diff --git a/libraries/rush-lib/src/schemas/custom-tips.schema.json b/libraries/rush-lib/src/schemas/custom-tips.schema.json new file mode 100644 index 00000000000..faa274fe091 --- /dev/null +++ b/libraries/rush-lib/src/schemas/custom-tips.schema.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Rush custom-tips.json config file", + "description": "The config file for adding tips to specific messages.", + + "type": "object", + "properties": { + "$schema": { + "description": "Part of the JSON Schema standard, this optional keyword declares the URL of the schema that the file conforms to. Editors may download the schema and use it to perform syntax highlighting.", + "type": "string" + }, + + "customTips": { + "type": "array", + "items": { + "type": "object", + "required": ["tipId", "message"], + "additionalProperties": false, + "properties": { + "tipId": { + "type": "string", + "description": "An identifier indicating a message that may be printed by Rush. If that message is printed, then this custom tip will be shown. Consult the Rush documentation for the current list of possible identifiers.", + "pattern": "^[A-Z0-9_]+$" + }, + "message": { + "type": "string", + "description": "The message text to be displayed for this tip." + } + } + } + } + }, + "additionalProperties": false +} diff --git a/libraries/rush-lib/src/schemas/deploy-scenario.schema.json b/libraries/rush-lib/src/schemas/deploy-scenario.schema.json index 4f7c57610ed..89bd1e88e01 100644 --- a/libraries/rush-lib/src/schemas/deploy-scenario.schema.json +++ b/libraries/rush-lib/src/schemas/deploy-scenario.schema.json @@ -14,9 +14,9 @@ "description": "The \"rush deploy\" command prepares a deployment folder, starting from the main project and collecting all of its dependencies (both NPM packages and other Rush projects). The main project is specified using the \"--project\" parameter. The \"deploymentProjectNames\" setting lists the allowable choices for the \"--project\" parameter; this documents the intended deployments for your monorepo and helps validate that \"rush deploy\" is invoked correctly. If there is only one item in the \"deploymentProjectNames\" array, then \"--project\" can be omitted. The names should be complete package names as declared in rush.json.\n\nIf the main project should include other unrelated Rush projects, add it to the \"projectSettings\" section, and then specify those projects in the \"additionalProjectsToInclude\" list.", "type": "array", "items": { - "type": "string", - "minItems": 1 - } + "type": "string" + }, + "minItems": 1 }, "includeDevDependencies": { @@ -75,11 +75,59 @@ "items": { "type": "string" } + }, + "patternsToInclude": { + "description": "A list of glob patterns to include when extracting this project. If a path is matched by both \"patternsToInclude\" and \"patternsToExclude\", the path will be excluded. If undefined, all paths will be included.", + "type": "array", + "items": { + "type": "string" + } + }, + "patternsToExclude": { + "description": "A list of glob patterns to exclude when extracting this project. If a path is matched by both \"patternsToInclude\" and \"patternsToExclude\", the path will be excluded. If undefined, no paths will be excluded.", + "type": "array", + "items": { + "type": "string" + } } }, "required": ["projectName"], "additionalProperties": false } + }, + + "dependencySettings": { + "description": "Customize how third party dependencies are processed during deployment.", + "type": "array", + "items": { + "type": "object", + "properties": { + "dependencyName": { + "description": "The full package name of third party dependency", + "type": "string" + }, + "dependencyVersionRange": { + "description": "The semantic version range of third party dependency", + "type": "string" + }, + "patternsToInclude": { + "description": "A list of glob patterns to include when extracting the dependency specified in this object. If a path is matched by both \"patternsToInclude\" and \"patternsToExclude\", the path will be excluded. If undefined, all paths will be included.", + "type": "array", + "items": { + "type": "string" + } + }, + "patternsToExclude": { + "description": "A list of glob patterns to include when extracting the dependency specified in this object. If a path is matched by both \"patternsToInclude\" and \"patternsToExclude\", the path will be excluded. If undefined, no paths will be excluded.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["dependencyName", "dependencyVersionRange"], + "additionalProperties": false + } } }, "required": ["deploymentProjectNames"], diff --git a/libraries/rush-lib/src/schemas/experiments.schema.json b/libraries/rush-lib/src/schemas/experiments.schema.json index e443f13cd84..dee04051b83 100644 --- a/libraries/rush-lib/src/schemas/experiments.schema.json +++ b/libraries/rush-lib/src/schemas/experiments.schema.json @@ -18,6 +18,10 @@ "description": "By default, 'rush update' passes --no-prefer-frozen-lockfile to 'pnpm install'. Set this option to true to pass '--prefer-frozen-lockfile' instead.", "type": "boolean" }, + "usePnpmLockfileOnlyThenFrozenLockfileForRushUpdate": { + "description": "By default, 'rush update' runs as a single operation. Set this option to true to instead update the lockfile with `--lockfile-only`, then perform a `--frozen-lockfile` install. Necessary when using the `afterAllResolved` hook in .pnpmfile.cjs.", + "type": "boolean" + }, "omitImportersFromPreventManualShrinkwrapChanges": { "description": "If using the 'preventManualShrinkwrapChanges' option, only prevent manual changes to the total set of external dependencies referenced by the repository, not which projects reference which dependencies. This offers a balance between lockfile integrity and merge conflicts.", "type": "boolean" @@ -30,8 +34,12 @@ "description": "If true, build caching will respect the allowWarningsInSuccessfulBuild flag and cache builds with warnings. This will not replay warnings from the cached build.", "type": "boolean" }, + "buildSkipWithAllowWarningsInSuccessfulBuild": { + "description": "If true, build skipping will respect the allowWarningsInSuccessfulBuild flag and skip builds with warnings. This will not replay warnings from the skipped build.", + "type": "boolean" + }, "phasedCommands": { - "description": "If true, the phased commands feature is enabled. To use this feature, create a \"phased\" command in common/config/rush/command-line.json.", + "description": "THIS EXPERIMENT HAS BEEN GRADUATED TO A STANDARD FEATURE. THIS PROPERTY SHOULD BE REMOVED.", "type": "boolean" }, "cleanInstallAfterNpmrcChanges": { @@ -45,6 +53,46 @@ "forbidPhantomResolvableNodeModulesFolders": { "description": "If true, Rush will not allow node_modules in the repo folder or in parent folders.", "type": "boolean" + }, + "usePnpmSyncForInjectedDependencies": { + "description": "(UNDER DEVELOPMENT) For certain installation problems involving peer dependencies, PNPM cannot correctly satisfy versioning requirements without installing duplicate copies of a package inside the node_modules folder. This poses a problem for 'workspace:*' dependencies, as they are normally installed by making a symlink to the local project source folder. PNPM's 'injected dependencies' feature provides a model for copying the local project folder into node_modules, however copying must occur AFTER the dependency project is built and BEFORE the consuming project starts to build. The 'pnpm-sync' tool manages this operation; see its documentation for details. Enable this experiment if you want 'rush' and 'rushx' commands to resync injected dependencies by invoking 'pnpm-sync' during the build.", + "type": "boolean" + }, + "generateProjectImpactGraphDuringRushUpdate": { + "description": "If set to true, Rush will generate a `project-impact-graph.yaml` file in the repository root during `rush update`.", + "type": "boolean" + }, + "useIPCScriptsInWatchMode": { + "description": "If true, when running in watch mode, Rush will check for phase scripts named `_phase::ipc` and run them instead of `_phase:` if they exist. The created child process will be provided with an IPC channel and expected to persist across invocations.", + "type": "boolean" + }, + "allowCobuildWithoutCache": { + "description": "When using cobuilds, this experiment allows uncacheable operations to benefit from cobuild orchestration without using the build cache.", + "type": "boolean" + }, + "rushAlerts": { + "description": "(UNDER DEVELOPMENT) The Rush alerts feature provides a way to send announcements to engineers working in the monorepo, by printing directly in the user's shell window when they invoke Rush commands. This ensures that important notices will be seen by anyone doing active development, since people often ignore normal discussion group messages or don't know to subscribe.", + "type": "boolean" + }, + "enableSubpathScan": { + "description": "By default, rush perform a full scan of the entire repository. For example, Rush runs `git status` to check for local file changes. When this toggle is enabled, Rush will only scan specific paths, significantly speeding up Git operations.", + "type": "boolean" + }, + "exemptDecoupledDependenciesBetweenSubspaces": { + "description": "Rush has a policy that normally requires Rush projects to specify `workspace:*` in package.json when depending on other projects in the workspace, unless they are explicitly declared as `decoupledLocalDependencies in rush.json. Enabling this experiment will remove that requirement for dependencies belonging to a different subspace. This is useful for large product groups who work in separate subspaces and generally prefer to consume each other's packages via the NPM registry.", + "type": "boolean" + }, + "omitAppleDoubleFilesFromBuildCache": { + "description": "If true, when running on macOS, Rush will omit AppleDouble files (._*) from build cache archives when a companion file exists in the same directory. AppleDouble files are automatically created by macOS to store extended attributes on filesystems that don't support them, and should generally not be included in the shared build cache.", + "type": "boolean" + }, + "strictChangefileValidation": { + "description": "If true, `rush change --verify` will report errors if change files reference projects that do not exist in the Rush configuration, or if change files target a project that belongs to a lockstepped version policy but is not the policy's main project.", + "type": "boolean" + }, + "useDirectFileTransfersForBuildCache": { + "description": "If true, the build cache will use file-based APIs to transfer cache entries to and from cloud storage. This avoids loading the entire cache entry into memory, which can prevent out-of-memory errors for large build outputs and allow cache entries to exceed the limit of a single Buffer. The cloud cache provider plugin must implement the optional file-based methods for this to take effect; otherwise it falls back to the buffer-based approach.", + "type": "boolean" } }, "additionalProperties": false diff --git a/libraries/rush-lib/src/schemas/pnpm-config.schema.json b/libraries/rush-lib/src/schemas/pnpm-config.schema.json index 5adbe10e074..93be4e62642 100644 --- a/libraries/rush-lib/src/schemas/pnpm-config.schema.json +++ b/libraries/rush-lib/src/schemas/pnpm-config.schema.json @@ -10,6 +10,11 @@ "type": "string" }, + "extends": { + "description": "Optionally specifies another JSON config file that this file extends from. This provides a way for standard settings to be shared across multiple projects. To delete an inherited setting, set it to `null` in this file.", + "type": "string" + }, + "useWorkspaces": { "description": "If true, then `rush install` and `rush update` will use the PNPM workspaces feature to perform the install, instead of the old model where Rush generated the symlinks for each projects's node_modules folder. This option is strongly recommended. The default value is false.", "type": "boolean" @@ -48,6 +53,11 @@ "type": "boolean" }, + "alwaysInjectDependenciesFromOtherSubspaces": { + "description": "When a project uses `workspace:` to depend on another Rush project, PNPM normally installs it by creating a symlink under `node_modules`. This generally works well, but in certain cases such as differing `peerDependencies` versions, symlinking may cause trouble such as incorrectly satisfied versions. For such cases, the dependency can be declared as \"injected\", causing PNPM to copy its built output into `node_modules` like a real install from a registry. Details here: https://rushjs.io/pages/advanced/injected_deps/\n\nWhen using Rush subspaces, these sorts of versioning problems are much more likely if `workspace:` refers to a project from a different subspace. This is because the symlink would point to a separate `node_modules` tree installed by a different PNPM lockfile. A comprehensive solution is to enable `alwaysInjectDependenciesFromOtherSubspaces`, which automatically treats all projects from other subspaces as injected dependencies without having to manually configure them.\n\nNOTE: Use carefully -- excessive file copying can slow down the `rush install` and `pnpm-sync` operations if too many dependencies become injected.\n\nThe default value is false.", + "type": "boolean" + }, + "globalOverrides": { "description": "The \"globalOverrides\" setting provides a simple mechanism for overriding version selections for all dependencies of all projects in the monorepo workspace. The settings are copied into the `pnpm.overrides` field of the `common/temp/package.json` file that is generated by Rush during installation.\n\nOrder of precedence: `.pnpmfile.cjs` has the highest precedence, followed by `unsupportedPackageJsonSettings`, `globalPeerDependencyRules`, `globalPackageExtensions`, and `globalOverrides` has lowest precedence.\n\nPNPM documentation: https://pnpm.io/package_json#pnpmoverrides", "type": "object", @@ -140,6 +150,32 @@ } }, + "globalOnlyBuiltDependencies": { + "description": "This field allows specifying which dependencies are permitted to run build scripts (preinstall, install, postinstall). In PNPM 10.x, build scripts are disabled by default for security. Use this allowlist to explicitly permit specific packages to run their build scripts.\n\n(SUPPORTED ONLY IN PNPM 10.1.0 - 10.x; replaced by `globalAllowBuilds` in PNPM 11.0.0)\n\nPNPM documentation: https://pnpm.io/settings#onlybuiltdependencies", + "type": "array", + "items": { + "description": "Specify package name of the dependency allowed to run build scripts", + "type": "string" + } + }, + + "globalAllowBuilds": { + "description": "This field controls which dependencies are allowed to run build scripts (preinstall, install, postinstall). A value of `true` means the package is allowed to run build scripts; `false` means it is explicitly denied. Packages with build scripts not listed here will cause pnpm to fail with ERR_PNPM_IGNORED_BUILDS. The settings are written to the `allowBuilds` field of the `pnpm-workspace.yaml` file that is generated by Rush during installation.\n\n(SUPPORTED ONLY IN PNPM 11.0.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/settings#allowbuilds", + "type": "object", + "additionalProperties": { + "type": "boolean" + } + }, + + "globalIgnoredOptionalDependencies": { + "description": "This field allows you to skip the installation of specific optional dependencies. The listed packages will be treated as if they are not present in the dependency tree during installation, meaning they will not be installed even if required by other packages.\n\n(SUPPORTED ONLY IN PNPM 9.0.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/package_json#pnpmalloweddeprecatedversions", + "type": "array", + "items": { + "description": "Specify the package name of the optional dependency to be ignored.", + "type": "string" + } + }, + "globalAllowedDeprecatedVersions": { "description": "The `globalAllowedDeprecatedVersions` setting suppresses installation warnings for package versions that the NPM registry reports as being deprecated. This is useful if the deprecated package is an indirect dependency of an external package that has not released a fix. The settings are copied into the `pnpm.allowedDeprecatedVersions` field of the `common/temp/package.json` file that is generated by Rush during installation.\n\nPNPM documentation: https://pnpm.io/package_json#pnpmalloweddeprecatedversions", "type": "object", @@ -159,6 +195,103 @@ "unsupportedPackageJsonSettings": { "description": "(USE AT YOUR OWN RISK) This is a free-form property bag that will be copied into the `common/temp/package.json` file that is generated by Rush during installation. This provides a way to experiment with new PNPM features. These settings will override any other Rush configuration associated with a given JSON field except for `.pnpmfile.cjs`.", "type": "object" + }, + + "resolutionMode": { + "description": "This option overrides the resolution-mode in PNPM. Use it if you want to change the default resolution behavior when installing dependencies. Defaults to \"highest\".\n\nPNPM documentation: https://pnpm.io/npmrc#resolution-mode.", + "type": "string", + "enum": ["highest", "time-based", "lowest-direct"] + }, + + "autoInstallPeers": { + "description": "This setting determines whether PNPM will automatically install (non-optional) missing peer dependencies instead of reporting an error. With Rush, the default value is always false.\n\nPNPM documentation: https://pnpm.io/npmrc#auto-install-peers", + "type": "boolean" + }, + + "minimumReleaseAgeMinutes": { + "description": "The minimum number of minutes that must pass after a version is published before pnpm will install it. This setting helps reduce the risk of installing compromised packages, as malicious releases are typically discovered and removed within a short time frame.\n\n(SUPPORTED ONLY IN PNPM 10.16.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/settings#minimumreleaseage\n\nThe default value is 0 (disabled).", + "type": "number" + }, + + "minimumReleaseAge": { + "description": "DEPRECATED - Use \"minimumReleaseAgeMinutes\" instead. Cannot be combined with \"minimumReleaseAgeMinutes\".\n\nThe minimum number of minutes that must pass after a version is published before pnpm will install it. This setting helps reduce the risk of installing compromised packages, as malicious releases are typically discovered and removed within a short time frame.\n\n(SUPPORTED ONLY IN PNPM 10.16.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/settings#minimumreleaseage\n\nThe default value is 0 (disabled).", + "type": "number" + }, + + "minimumReleaseAgeExclude": { + "description": "List of package names or patterns that are excluded from the minimumReleaseAge check. These packages will always install the newest version immediately, even if minimumReleaseAge is set. Supports glob patterns (e.g., \"@myorg/*\").\n\n(SUPPORTED ONLY IN PNPM 10.16.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/settings#minimumreleaseageexclude\n\nExample: [\"webpack\", \"react\", \"@myorg/*\"]", + "type": "array", + "items": { + "description": "Package name or pattern", + "type": "string" + } + }, + + "trustPolicy": { + "description": "The trust policy controls whether pnpm should block installation of package versions where the trust level has decreased (e.g., a package previously published with provenance is now published without it). Setting this to \"no-downgrade\" enables the protection.\n\n(SUPPORTED ONLY IN PNPM 10.21.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/settings#trustpolicy", + "type": "string", + "enum": ["no-downgrade", "off"] + }, + + "trustPolicyExclude": { + "description": "List of package names or patterns that are excluded from the trust policy check. These packages will be allowed to install even if their trust level has decreased. Supports glob patterns (e.g., \"@myorg/*\").\n\n(SUPPORTED ONLY IN PNPM 10.22.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/settings#trustpolicyexclude\n\nExample: [\"webpack\", \"react\", \"@myorg/*\"]", + "type": "array", + "items": { + "description": "Package name or pattern", + "type": "string" + } + }, + + "trustPolicyIgnoreAfterMinutes": { + "description": "The number of minutes after which pnpm will ignore trust level downgrades. Packages published longer ago than this threshold will not be blocked even if their trust level has decreased.\n\n(SUPPORTED ONLY IN PNPM 10.27.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/settings#trustpolicyignoreafter", + "type": "number" + }, + + "alwaysFullInstall": { + "description": "(EXPERIMENTAL) If 'true', then filtered installs ('rush install --to my-project') * will be disregarded, instead always performing a full installation of the lockfile.", + "type": "boolean" + }, + + "pnpmLockfilePolicies": { + "description": "This setting defines the policies that govern the `pnpm-lock.yaml` file.", + "type": "object", + "additionalProperties": false, + "properties": { + "disallowInsecureSha1": { + "type": "object", + "description": "Forbid sha1 hashes in `pnpm-lock.yaml`.", + "properties": { + "enabled": { + "type": "boolean" + }, + "exemptPackageVersions": { + "description": "A list of specific package versions to be exempted from the \"disallowInsecureSha1\" policy", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + }, + "description": "An array of exempted versions for this package." + } + } + }, + "required": ["enabled", "exemptPackageVersions"] + } + } + }, + + "globalCatalogs": { + "description": "The \"globalCatalogs\" setting provides named catalogs for organizing dependency versions. Each catalog can be referenced using the `catalog:catalogName` protocol in package.json files (e.g., `catalog:react18`). The settings are written to the `catalogs` field of the `pnpm-workspace.yaml` file that is generated by Rush during installation.\n\nPNPM documentation: https://pnpm.io/catalogs", + "type": "object", + "additionalProperties": { + "description": "A named catalog containing package versions", + "type": "object", + "additionalProperties": { + "description": "Specify the version for a package in this catalog", + "type": "string" + } + } } } } diff --git a/libraries/rush-lib/src/schemas/repo-state.schema.json b/libraries/rush-lib/src/schemas/repo-state.schema.json index 916f2fb69aa..d14c1de3ac4 100644 --- a/libraries/rush-lib/src/schemas/repo-state.schema.json +++ b/libraries/rush-lib/src/schemas/repo-state.schema.json @@ -16,6 +16,14 @@ "preferredVersionsHash": { "description": "A hash of \"preferred versions\" for the repository. This hash is used to determine whether or not preferred versions have been modified prior to install.", "type": "string" + }, + "packageJsonInjectedDependenciesHash": { + "description": "A hash of the injected dependencies in related package.json. This hash is used to determine whether or not the shrinkwrap needs to updated prior to install.", + "type": "string" + }, + "pnpmCatalogsHash": { + "description": "A hash of the PNPM catalog definitions for the repository. This hash is used to determine whether or not the catalog has been modified prior to install.", + "type": "string" } }, "additionalProperties": false diff --git a/libraries/rush-lib/src/schemas/rush-alerts.schema.json b/libraries/rush-lib/src/schemas/rush-alerts.schema.json new file mode 100644 index 00000000000..819fb27fcf4 --- /dev/null +++ b/libraries/rush-lib/src/schemas/rush-alerts.schema.json @@ -0,0 +1,79 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Rush rush-alerts.json file", + "description": "This configuration file provides settings to rush alerts feature.", + "type": "object", + "additionalProperties": false, + "properties": { + "$schema": { + "description": "Part of the JSON Schema standard, this optional keyword declares the URL of the schema that the file conforms to. Editors may download the schema and use it to perform syntax highlighting.", + "type": "string" + }, + + "timezone": { + "description": "Settings such as `startTime` and `endTime` will use this timezone.\n\nIf omitted, the default timezone is UTC (`+00:00`).", + "type": "string" + }, + "alerts": { + "description": "An array of alert messages and conditions for triggering them.", + "items": { + "$ref": "#/definitions/IAlert" + }, + "type": "array" + } + }, + "definitions": { + "IAlert": { + "type": "object", + "properties": { + "alertId": { + "description": "The alertId is used to identify the alert.", + "type": "string" + }, + "title": { + "description": "When the alert is displayed, this title will appear at the top of the message box. It should be a single line of text, as concise as possible.", + "type": "string" + }, + "message": { + "description": "When the alert is displayed, this text appears in the message box.\n\nTo make the JSON file more readable, if the text is longer than one line, you can instead provide an array of strings that will be concatenated.\n\nYour text may contain newline characters, but generally this is unnecessary because word-wrapping is automatically applied.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "detailsUrl": { + "description": "(OPTIONAL) To avoid spamming users, the `title` and `message` settings should be kept as concise as possible.\n\nIf you need to provide more detail, use this setting to print a hyperlink to a web page with further guidance.", + "type": "string" + }, + "startTime": { + "description": "(OPTIONAL) If `startTime` is specified, then this alert will not be shown prior to that time.\n\nKeep in mind that the alert is not guaranteed to be shown at this time, or at all. Alerts are only displayed after a Rush command has triggered fetching of the latest rush-alerts.json configuration.\n\nAlso, display of alerts is throttled to avoid spamming the user with too many messages.\n\nIf you need to test your alert, set the environment variable `RUSH_ALERTS_DEBUG=1` to disable throttling.\n\nThe `startTime` should be specified as `YYYY-MM-DD HH:MM` using 24 hour time format, or else `YYYY-MM-DD` in which case the time part will be `00:00` (start of that day). The time zone is obtained from the `timezone` setting above.", + "type": "string" + }, + "endTime": { + "description": "(OPTIONAL) This alert will not be shown if the current time is later than `endTime`.\n\nThe format is the same as `startTime`.", + "type": "string" + }, + "maximumDisplayInterval": { + "description": "(OPTIONAL) Specifies the maximum frequency at which this alert can be displayed within a defined time period.\n\nOptions are:\n\n \"always\" (default) - no limit on display frequency, \"monthly\" - display up to once per month, \"weekly\" - display up to once per week, \"daily\" - display up to once per day, \"hourly\" - display up to once per hour.", + "enum": ["always", "monthly", "weekly", "daily", "hourly"] + }, + "priority": { + "description": "(OPTIONAL) Determines the order in which this alert is shown relative to other alerts, based on urgency.\n\nOptions are: \n\n \"high\" - displayed first, \"normal\" (default) - standard urgency, \"low\" - least urgency.", + "enum": ["high", "normal", "low"] + }, + "conditionScript": { + "description": "(OPTIONAL) The filename of a script that determines whether this alert can be shown, found in the 'common/config/rush/alert-scripts' folder.\n\nThe script must define a CommonJS export named `canShowAlert` that returns a boolean value, for example:\n\n`module.exports.canShowAlert = function () { // (your logic goes here) return true; }`.\n\nRush will invoke this script with the working directory set to the monorepo root folder, with no guarantee that `rush install` has been run.\n\nTo ensure up-to-date alerts, Rush may fetch and checkout the 'common/config/rush-alerts' folder in an unpredictable temporary path. Therefore, your script should avoid importing dependencies from outside its folder, generally be kept as simple and reliable and quick as possible.\n\nFor more complex conditions, we suggest to design some other process that prepares a data file or environment variable that can be cheaply checked by your condition script.", + "type": "string" + } + }, + "required": ["alertId", "title", "message"] + } + } +} diff --git a/libraries/rush-lib/src/schemas/rush-hotlink-state.schema.json b/libraries/rush-lib/src/schemas/rush-hotlink-state.schema.json new file mode 100644 index 00000000000..2b882e970c3 --- /dev/null +++ b/libraries/rush-lib/src/schemas/rush-hotlink-state.schema.json @@ -0,0 +1,55 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema", + "title": "Rush rush-project-link-state.json config file", + + "type": "object", + "required": ["fileVersion", "linksBySubspace"], + "additionalProperties": false, + + "properties": { + "$schema": { + "description": "Part of the JSON Schema standard, this optional keyword declares the URL of the schema that the file conforms to. Editors may download the schema and use it to perform syntax highlighting.", + "type": "string" + }, + + "fileVersion": { + "type": "number" + }, + + "linksBySubspace": { + "description": "A map of subspace names to their corresponding links.", + "type": "object", + + "additionalProperties": { + "type": "array", + "items": { + "type": "object", + "required": ["linkedPackagePath", "linkedPackageName", "linkType"], + "additionalProperties": false, + + "properties": { + "linkedPackagePath": { + "type": "string" + }, + + "linkedPackageName": { + "type": "string" + }, + + "affectedPnpmVirtualStoreFolderPaths": { + "type": "array", + "items": { + "type": "string" + } + }, + + "linkType": { + "type": "string", + "enum": ["LinkPackage", "BridgePackage"] + } + } + } + } + } + } +} diff --git a/libraries/rush-lib/src/schemas/rush-project.schema.json b/libraries/rush-lib/src/schemas/rush-project.schema.json index d309a568e45..11b7a8e7049 100644 --- a/libraries/rush-lib/src/schemas/rush-project.schema.json +++ b/libraries/rush-lib/src/schemas/rush-project.schema.json @@ -12,7 +12,7 @@ }, "extends": { - "description": "Optionally specifies another JSON config file that this file extends from. This provides a way for standard settings to be shared across multiple projects.", + "description": "Optionally specifies another JSON config file that this file extends from. This provides a way for standard settings to be shared across multiple projects. To delete an inherited setting, set it to `null` in this file.", "type": "string" }, @@ -72,6 +72,68 @@ "disableBuildCacheForOperation": { "description": "Disable caching for this operation. The operation will never be restored from cache. This may be useful if this operation affects state outside of its folder.", "type": "boolean" + }, + "sharding": { + "type": "object", + "description": "If specified, the operation will be a 'sharded' operation. This means that the operation will be run multiple times in parallel.", + "additionalProperties": false, + "required": ["count"], + "properties": { + "count": { + "type": "integer", + "description": "The number of shards to run. This must be a positive integer." + }, + "shardArgumentFormat": { + "type": "string", + "description": "A template string that specifies the command-line argument to pass to the operation for each shard. The string may contain the following placeholders: {shardIndex} {shardCount}. Defaults to --shard=\"{shardIndex}/{shardCount}\"" + }, + "outputFolderArgumentFormat": { + "type": "string", + "description": "The command-line argument to pass to the operation to specify the output folder. The string may contain the following placeholders: {phaseName} {shardIndex}. Must end with {shardIndex}. Defaults to --shard-output-folder=\".rush/operations/{phaseName}/shards/{shardIndex}\"" + }, + "shardOperationSettings": { + "type": "object", + "description": "DEPRECATED. Use a separate operationSettings entry with {this operation's name}:shard as the name, ex _phase:build would have a separate operation _phase:build:shard to manage per-shard settings.", + "additionalProperties": true + } + } + }, + "dependsOnNodeVersion": { + "description": "Specifies whether and at what granularity the Node.js version should be included in the hash used for the build cache. When enabled, changing the Node.js version at the specified granularity will invalidate cached outputs and cause the operation to be re-executed. This is useful for projects that produce Node.js-version-specific outputs, such as native module builds. Allowed values: true (alias for 'patch'), 'major' (e.g. '18'), 'minor' (e.g. '18.17'), or 'patch' (e.g. '18.17.1').", + "oneOf": [ + { "type": "boolean", "enum": [true] }, + { "type": "string", "enum": ["major", "minor", "patch"] } + ] + }, + "weight": { + "oneOf": [ + { + "type": "string", + "pattern": "^[1-9][0-9]*(\\.\\d+)?%$", + "description": "The number of concurrency units that this operation should take up, as a percent of `os.availableParallelism()`. At runtime this value will be clamped to the range `[1, rushParallelism]`, where `rushParallelism` is the requested parallelism to the current Rush command. To have this operation consume no concurrency, use the number 0 instead of a string." + }, + { + "description": "The number of concurrency units that this operation should take up. At runtime this value will be clamped to the range `[0, rushParallelism]`, where `rushParallelism` is the requested parallelism to the current Rush command.", + "type": "integer", + "minimum": 0 + } + ] + }, + "allowCobuildWithoutCache": { + "type": "boolean", + "description": "If true, this operation will not need to use the build cache to leverage cobuilds" + }, + "ignoreChangedProjectsOnlyFlag": { + "type": "boolean", + "description": "If true, this operation never be skipped by the `--changed-projects-only` flag. This is useful for projects that bundle code from other packages." + }, + "parameterNamesToIgnore": { + "type": "array", + "description": "An optional list of custom command-line parameter names that should be ignored when invoking the command for this operation. The parameter names should match the exact longName field values from the command-line.json parameters array (e.g., '--production', '--verbose'). This allows a project to opt out of parameters that don't affect its operation, preventing unnecessary cache invalidation for this operation and its consumers.", + "items": { + "type": "string" + }, + "uniqueItems": true } } } diff --git a/libraries/rush-lib/src/schemas/rush.schema.json b/libraries/rush-lib/src/schemas/rush.schema.json index 0c3df3dc9a5..dce5fcaae37 100644 --- a/libraries/rush-lib/src/schemas/rush.schema.json +++ b/libraries/rush-lib/src/schemas/rush.schema.json @@ -64,12 +64,16 @@ "description": "Rush normally prints a warning if it detects a pre-LTS Node.js version. If you are testing pre-LTS versions in preparation for supporting the first LTS version, you can use this setting to disable Rush's warning.", "type": "boolean" }, + "suppressRushIsPublicVersionCheck": { + "description": "Rush normally prints a warning if it detects that the current version is not one published to the public npmjs.org registry. If you need to block calls to the npm registry, you can use this setting to disable Rush's check.", + "type": "boolean" + }, "projectFolderMinDepth": { "description": "The minimum folder depth for the projectFolder field. The default value is 1, i.e. no slashes in the path name.", "type": "number" }, "ensureConsistentVersions": { - "description": "If true, consistent version specifiers for dependencies will be enforced (i.e. \"rush check\" is run before some commands).", + "description": "If true, consistent version specifiers for dependencies will be enforced (i.e. \"rush check\" is run before some commands). Used when property is not defined in common-versions.json.", "type": "boolean" }, "hotfixChangeEnabled": { @@ -186,7 +190,7 @@ "type": "string" }, "changefilesCommitMessage": { - "description": "The commit message to use when commiting change files made by \"rush change\". Defaults to \"Rush change\"", + "description": "The commit message to use when committing change files made by \"rush change\". Defaults to \"Rush change\"", "type": "string" }, "tagSeparator": { @@ -307,6 +311,10 @@ "type": "string", "pattern": "^[a-z0-9.@]+([-/][a-z0-9.@]+)*$" } + }, + "subspaceName": { + "description": "(EXPERIMENTAL) An optional entry for specifying which subspace this project belongs to if the subspaces feature is enabled.", + "type": "string" } }, "additionalProperties": false, @@ -344,6 +352,20 @@ "items": { "type": "string" } + }, + "preRushx": { + "description": "The list of scripts to run before rushx starts.", + "type": "array", + "items": { + "type": "string" + } + }, + "postRushx": { + "description": "The list of scripts to run after rushx finishes.", + "type": "array", + "items": { + "type": "string" + } } }, "additionalProperties": false diff --git a/libraries/rush-lib/src/schemas/subspaces.schema.json b/libraries/rush-lib/src/schemas/subspaces.schema.json new file mode 100644 index 00000000000..5322a806a7b --- /dev/null +++ b/libraries/rush-lib/src/schemas/subspaces.schema.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Rush subspace config file.", + "description": "The configuration file for enabling the subspaces feature in rush. This is an EXPERIMENTAL feature which is not yet fully implemented. To opt into the experiment, simply toggle the 'enabled' property in this file.", + "type": "object", + + "properties": { + "$schema": { + "description": "Part of the JSON Schema standard, this optional keyword declares the URL of the schema that the file conforms to. Editors may download the schema and use it to perform syntax highlighting.", + "type": "string" + }, + "subspacesEnabled": { + "description": "If true, rush will use the subspaces configuration.", + "type": "boolean" + }, + "splitWorkspaceCompatibility": { + "description": "(DEPRECATED) Allows individual subspaces to be configured at the package level if that package is the only project in the subspace. Used to help migrate from a split workspace state.", + "type": "boolean" + }, + "preventSelectingAllSubspaces": { + "description": "If true, requires a selector for a subspace or set of subspaces when installing.", + "type": "boolean" + }, + "subspaceNames": { + "description": "Individual subspace configurations.", + "type": "array", + "items": { + "type": "string" + } + } + } +} diff --git a/libraries/rush-lib/src/schemas/version-policies.schema.json b/libraries/rush-lib/src/schemas/version-policies.schema.json index 08f01c2c8bb..35995c5cc30 100644 --- a/libraries/rush-lib/src/schemas/version-policies.schema.json +++ b/libraries/rush-lib/src/schemas/version-policies.schema.json @@ -68,7 +68,7 @@ }, "nextBump": { "description": "Type of next version bump", - "enum": ["none", "prerelease", "release", "minor", "patch", "major"] + "enum": ["none", "prerelease", "preminor", "minor", "patch", "major"] }, "mainProject": { "description": "The main project for this version policy", diff --git a/libraries/rush-lib/src/scripts/install-run-rush-pnpm.ts b/libraries/rush-lib/src/scripts/install-run-rush-pnpm.ts index e4e1ab8ab47..2b9a1f5a5b4 100644 --- a/libraries/rush-lib/src/scripts/install-run-rush-pnpm.ts +++ b/libraries/rush-lib/src/scripts/install-run-rush-pnpm.ts @@ -1,4 +1,4 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See the @microsoft/rush package's LICENSE file for license information. +// See LICENSE in the project root for license information. __non_webpack_require__('./install-run-rush'); diff --git a/libraries/rush-lib/src/scripts/install-run-rush.ts b/libraries/rush-lib/src/scripts/install-run-rush.ts index 84ffc562ee9..6fa7e8b21b5 100644 --- a/libraries/rush-lib/src/scripts/install-run-rush.ts +++ b/libraries/rush-lib/src/scripts/install-run-rush.ts @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See the @microsoft/rush package's LICENSE file for license information. +// See LICENSE in the project root for license information. -import * as path from 'path'; -import * as fs from 'fs'; +/* eslint-disable no-console */ + +import * as path from 'node:path'; +import * as fs from 'node:fs'; const { installAndRun, @@ -14,6 +16,7 @@ import type { ILogger } from '../utilities/npmrcUtilities'; const PACKAGE_NAME: string = '@microsoft/rush'; const RUSH_PREVIEW_VERSION: string = 'RUSH_PREVIEW_VERSION'; +const RUSH_QUIET_MODE: string = 'RUSH_QUIET_MODE'; const INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE: 'INSTALL_RUN_RUSH_LOCKFILE_PATH' = 'INSTALL_RUN_RUSH_LOCKFILE_PATH'; @@ -36,8 +39,8 @@ function _getRushVersion(logger: ILogger): string { return rushJsonMatches[1]; } catch (e) { throw new Error( - `Unable to determine the required version of Rush from rush.json (${rushJsonFolder}). ` + - "The 'rushVersion' field is either not assigned in rush.json or was specified " + + `Unable to determine the required version of Rush from ${RUSH_JSON_FILENAME} (${rushJsonFolder}). ` + + `The 'rushVersion' field is either not assigned in ${RUSH_JSON_FILENAME} or was specified ` + 'using an unexpected syntax.' ); } @@ -70,7 +73,9 @@ function _run(): void { } let commandFound: boolean = false; - let logger: ILogger = { info: console.log, error: console.error }; + + const quietModeEnvValue: string | undefined = process.env[RUSH_QUIET_MODE]; + let quiet: boolean = quietModeEnvValue === '1' || quietModeEnvValue === 'true'; for (const arg of packageBinArgs) { if (arg === '-q' || arg === '--quiet') { @@ -80,10 +85,7 @@ function _run(): void { // To maintain the same user experience, the install-run* scripts pass along this // flag but also use it to suppress any diagnostic information normally printed // to stdout. - logger = { - info: () => {}, - error: console.error - }; + quiet = true; } else if (!arg.startsWith('-') || arg === '-h' || arg === '--help') { // We either found something that looks like a command (i.e. - doesn't start with a "-"), // or we found the -h/--help flag, which can be run without a command @@ -103,9 +105,13 @@ function _run(): void { process.exit(1); } + const logger: ILogger = quiet + ? { info: () => {}, error: console.error } + : { info: console.log, error: console.error }; + runWithErrorAndStatusCode(logger, () => { const version: string = _getRushVersion(logger); - logger.info(`The rush.json configuration requests Rush version ${version}`); + logger.info(`The ${RUSH_JSON_FILENAME} configuration requests Rush version ${version}`); const lockFilePath: string | undefined = process.env[INSTALL_RUN_RUSH_LOCKFILE_PATH_VARIABLE]; if (lockFilePath) { diff --git a/libraries/rush-lib/src/scripts/install-run-rushx.ts b/libraries/rush-lib/src/scripts/install-run-rushx.ts index e4e1ab8ab47..2b9a1f5a5b4 100644 --- a/libraries/rush-lib/src/scripts/install-run-rushx.ts +++ b/libraries/rush-lib/src/scripts/install-run-rushx.ts @@ -1,4 +1,4 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See the @microsoft/rush package's LICENSE file for license information. +// See LICENSE in the project root for license information. __non_webpack_require__('./install-run-rush'); diff --git a/libraries/rush-lib/src/scripts/install-run.ts b/libraries/rush-lib/src/scripts/install-run.ts index f07c14c565e..7f568566485 100644 --- a/libraries/rush-lib/src/scripts/install-run.ts +++ b/libraries/rush-lib/src/scripts/install-run.ts @@ -1,14 +1,20 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See the @microsoft/rush package's LICENSE file for license information. +// See LICENSE in the project root for license information. + +/* eslint-disable no-console */ + +import * as childProcess from 'node:child_process'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; -import * as childProcess from 'child_process'; -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; import type { IPackageJson } from '@rushstack/node-core-library'; + import { syncNpmrc, type ILogger } from '../utilities/npmrcUtilities'; +import { escapeArgumentIfNeeded, IS_WINDOWS } from '../utilities/executionUtilities'; +import type { RushConstants } from '../logic/RushConstants'; -export const RUSH_JSON_FILENAME: string = 'rush.json'; +export const RUSH_JSON_FILENAME: typeof RushConstants.rushJsonFilename = 'rush.json'; const RUSH_TEMP_FOLDER_ENV_VARIABLE_NAME: string = 'RUSH_TEMP_FOLDER'; const INSTALL_RUN_LOCKFILE_PATH_VARIABLE: 'INSTALL_RUN_LOCKFILE_PATH' = 'INSTALL_RUN_LOCKFILE_PATH'; const INSTALLED_FLAG_FILENAME: string = 'installed.flag'; @@ -50,7 +56,7 @@ let _npmPath: string | undefined = undefined; export function getNpmPath(): string { if (!_npmPath) { try { - if (os.platform() === 'win32') { + if (IS_WINDOWS) { // We're on Windows const whereOutput: string = childProcess.execSync('where npm', { stdio: [] }).toString(); const lines: string[] = whereOutput.split(os.EOL).filter((line) => !!line); @@ -123,6 +129,24 @@ export interface IPackageSpecifier { version: string | undefined; } +/** + * Compare version strings according to semantic versioning. + * Returns a positive integer if "a" is a later version than "b", + * a negative integer if "b" is later than "a", + * and 0 otherwise. + */ +function _compareVersionStrings(a: string, b: string): number { + const aParts: string[] = a.split(/[.-]/); + const bParts: string[] = b.split(/[.-]/); + const numberOfParts: number = Math.max(aParts.length, bParts.length); + for (let i: number = 0; i < numberOfParts; i++) { + if (aParts[i] !== bParts[i]) { + return (Number(aParts[i]) || 0) - (Number(bParts[i]) || 0); + } + } + return 0; +} + /** * Resolve a package specifier to a static version */ @@ -145,42 +169,62 @@ function _resolvePackageVersion( const rushTempFolder: string = _getRushTempFolder(rushCommonFolder); const sourceNpmrcFolder: string = path.join(rushCommonFolder, 'config', 'rush'); - syncNpmrc(sourceNpmrcFolder, rushTempFolder, undefined, logger); - - const npmPath: string = getNpmPath(); + syncNpmrc({ + sourceNpmrcFolder, + targetNpmrcFolder: rushTempFolder, + logger, + supportEnvVarFallbackSyntax: false, + // Always filter npm-incompatible properties in install-run scripts. + // Any warnings will be shown when running Rush commands directly. + filterNpmIncompatibleProperties: true + }); // This returns something that looks like: - // @microsoft/rush@3.0.0 '3.0.0' - // @microsoft/rush@3.0.1 '3.0.1' - // ... - // @microsoft/rush@3.0.20 '3.0.20' - // - const npmVersionSpawnResult: childProcess.SpawnSyncReturns = childProcess.spawnSync( - npmPath, - ['view', `${name}@${version}`, 'version', '--no-update-notifier'], + // ``` + // [ + // "3.0.0", + // "3.0.1", + // ... + // "3.0.20" + // ] + // ``` + // + // if multiple versions match the selector, or + // + // ``` + // "3.0.0" + // ``` + // + // if only a single version matches. + + const npmVersionSpawnResult: childProcess.SpawnSyncReturns = _runNpmConfirmSuccess( + ['view', `${name}@${version}`, 'version', '--no-update-notifier', '--json'], { cwd: rushTempFolder, - stdio: [] - } + stdio: [], + env: process.env + }, + 'npm view' ); - if (npmVersionSpawnResult.status !== 0) { - throw new Error(`"npm view" returned error code ${npmVersionSpawnResult.status}`); + const npmViewVersionOutput: string = npmVersionSpawnResult.stdout.toString(); + const parsedVersionOutput: string | string[] = JSON.parse(npmViewVersionOutput); + const versions: string[] = Array.isArray(parsedVersionOutput) + ? parsedVersionOutput + : [parsedVersionOutput]; + let latestVersion: string | undefined = versions[0]; + for (let i: number = 1; i < versions.length; i++) { + const latestVersionCandidate: string = versions[i]; + if (_compareVersionStrings(latestVersionCandidate, latestVersion) > 0) { + latestVersion = latestVersionCandidate; + } } - const npmViewVersionOutput: string = npmVersionSpawnResult.stdout.toString(); - const versionLines: string[] = npmViewVersionOutput.split('\n').filter((line) => !!line); - const latestVersion: string | undefined = versionLines[versionLines.length - 1]; if (!latestVersion) { throw new Error('No versions found for the specified version range.'); } - const versionMatches: string[] | null = latestVersion.match(/^.+\s\'(.+)\'$/); - if (!versionMatches) { - throw new Error(`Invalid npm output ${latestVersion}`); - } - - return versionMatches[1]; + return latestVersion; } catch (e) { throw new Error(`Unable to resolve version ${version} of package ${name}: ${e}`); } @@ -206,7 +250,7 @@ export function findRushJsonFolder(): string { } while (basePath !== (tempPath = path.dirname(basePath))); // Exit the loop when we hit the disk root if (!_rushJsonFolder) { - throw new Error('Unable to find rush.json.'); + throw new Error(`Unable to find ${RUSH_JSON_FILENAME}.`); } } @@ -308,21 +352,19 @@ function _installPackage( packageInstallFolder: string, name: string, version: string, - command: 'install' | 'ci' + npmCommand: 'install' | 'ci' ): void { try { logger.info(`Installing ${name}...`); - const npmPath: string = getNpmPath(); - const result: childProcess.SpawnSyncReturns = childProcess.spawnSync(npmPath, [command], { - stdio: 'inherit', - cwd: packageInstallFolder, - env: process.env - }); - - if (result.status !== 0) { - throw new Error(`"npm ${command}" encountered an error`); - } - + _runNpmConfirmSuccess( + [npmCommand], + { + stdio: 'inherit', + cwd: packageInstallFolder, + env: process.env + }, + `npm ${npmCommand}` + ); logger.info(`Successfully installed ${name}@${version}`); } catch (e) { throw new Error(`Unable to install package: ${e}`); @@ -334,10 +376,16 @@ function _installPackage( */ function _getBinPath(packageInstallFolder: string, binName: string): string { const binFolderPath: string = path.resolve(packageInstallFolder, NODE_MODULES_FOLDER_NAME, '.bin'); - const resolvedBinName: string = os.platform() === 'win32' ? `${binName}.cmd` : binName; + const resolvedBinName: string = IS_WINDOWS ? `${binName}.cmd` : binName; return path.resolve(binFolderPath, resolvedBinName); } +function _buildShellCommand(command: string, args: string[]): string { + const escapedCommand: string = escapeArgumentIfNeeded(command); + const escapedArgs: string[] = args.map((arg) => escapeArgumentIfNeeded(arg)); + return [escapedCommand, ...escapedArgs].join(' '); +} + /** * Write a flag file to the package's install directory, signifying that the install was successful. */ @@ -350,6 +398,44 @@ function _writeFlagFile(packageInstallFolder: string): void { } } +/** + * Run npm under the platform's shell and throw if it didn't succeed. + */ +function _runNpmConfirmSuccess( + args: string[], + options: childProcess.SpawnSyncOptions, + commandNameForLogging: string +): childProcess.SpawnSyncReturns { + const command: string = getNpmPath(); + let result: childProcess.SpawnSyncReturns; + if (IS_WINDOWS) { + result = childProcess.spawnSync(_buildShellCommand(command, args), { + ...options, + shell: true, + windowsVerbatimArguments: false + }); + } else { + result = childProcess.spawnSync(command, args, options); + } + + if (result.status !== 0) { + if (!result.status) { + // Is status null or undefined? + if (result.error) { + throw new Error(`"${commandNameForLogging}" failed: ${result.error.message.toString()}`); + } else if (result.signal) { + throw new Error(`"${commandNameForLogging}" was terminated by signal: ${result.signal}`); + } else { + throw new Error(`"${commandNameForLogging}" failed for an unknown reason`); + } + } else { + throw new Error(`"${commandNameForLogging}" returned error code ${result.status}`); + } + } + + return result; +} + export function installAndRun( logger: ILogger, packageName: string, @@ -372,11 +458,19 @@ export function installAndRun( _cleanInstallFolder(rushTempFolder, packageInstallFolder, lockFilePath); const sourceNpmrcFolder: string = path.join(rushCommonFolder, 'config', 'rush'); - syncNpmrc(sourceNpmrcFolder, packageInstallFolder, undefined, logger); + syncNpmrc({ + sourceNpmrcFolder, + targetNpmrcFolder: packageInstallFolder, + logger, + supportEnvVarFallbackSyntax: false, + // Always filter npm-incompatible properties in install-run scripts. + // Any warnings will be shown when running Rush commands directly. + filterNpmIncompatibleProperties: true + }); _createPackageJson(packageInstallFolder, packageName, packageVersion); - const command: 'install' | 'ci' = lockFilePath ? 'ci' : 'install'; - _installPackage(logger, packageInstallFolder, packageName, packageVersion, command); + const installCommand: 'install' | 'ci' = lockFilePath ? 'ci' : 'install'; + _installPackage(logger, packageInstallFolder, packageName, packageVersion, installCommand); _writeFlagFile(packageInstallFolder); } @@ -390,24 +484,30 @@ export function installAndRun( // Windows environment variables are case-insensitive. Instead of using SpawnSyncOptions.env, we need to // assign via the process.env proxy to ensure that we append to the right PATH key. const originalEnvPath: string = process.env.PATH || ''; - let result: childProcess.SpawnSyncReturns; + let result: childProcess.SpawnSyncReturns; try { - // Node.js on Windows can not spawn a file when the path has a space on it - // unless the path gets wrapped in a cmd friendly way and shell mode is used - const shouldUseShell: boolean = binPath.includes(' ') && os.platform() === 'win32'; - const platformBinPath: string = shouldUseShell ? `"${binPath}"` : binPath; - process.env.PATH = [binFolderPath, originalEnvPath].join(path.delimiter); - result = childProcess.spawnSync(platformBinPath, packageBinArgs, { + + const spawnOptions: childProcess.SpawnSyncOptions = { stdio: 'inherit', - windowsVerbatimArguments: false, - shell: shouldUseShell, cwd: process.cwd(), env: process.env - }); + }; + if (IS_WINDOWS) { + result = childProcess.spawnSync(_buildShellCommand(binPath, packageBinArgs), { + ...spawnOptions, + windowsVerbatimArguments: false, + // `npm` bin stubs on Windows are `.cmd` files + // Node.js will not directly invoke a `.cmd` file unless `shell` is set to `true` + shell: true + }); + } else { + result = childProcess.spawnSync(binPath, packageBinArgs, spawnOptions); + } } finally { process.env.PATH = originalEnvPath; } + if (result.status !== null) { return result.status; } else { @@ -436,10 +536,11 @@ function _run(): void { ]: string[] = process.argv; if (!nodePath) { - throw new Error('Unexpected exception: could not detect node path'); + throw new Error('Could not detect node path'); } - if (path.basename(scriptPath).toLowerCase() !== 'install-run.js') { + const scriptFileName: string = path.basename(scriptPath).toLowerCase(); + if (scriptFileName !== 'install-run.js' && scriptFileName !== 'install-run') { // If install-run.js wasn't directly invoked, don't execute the rest of this function. Return control // to the script that (presumably) imported this file diff --git a/libraries/rush-lib/src/start.ts b/libraries/rush-lib/src/start.ts index b4da75c1edc..27c4e17d3b6 100644 --- a/libraries/rush-lib/src/start.ts +++ b/libraries/rush-lib/src/start.ts @@ -3,4 +3,8 @@ import { Rush } from './api/Rush'; +performance.mark('rush:start'); + Rush.launch(Rush.version, { isManaged: false }); + +// Rush.launch has async side effects, so no point ending the measurement. diff --git a/libraries/rush-lib/src/utilities/AsyncRecycler.ts b/libraries/rush-lib/src/utilities/AsyncRecycler.ts index af7f136a4ce..98ec9995096 100644 --- a/libraries/rush-lib/src/utilities/AsyncRecycler.ts +++ b/libraries/rush-lib/src/utilities/AsyncRecycler.ts @@ -1,14 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as child_process from 'child_process'; -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; +import * as child_process from 'node:child_process'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; -import { Text, Path, FileSystem } from '@rushstack/node-core-library'; +import { Text, Path, FileSystem, type FolderItem } from '@rushstack/node-core-library'; import { Utilities } from './Utilities'; +import { IS_WINDOWS } from './executionUtilities'; /** * For deleting large folders, AsyncRecycler is significantly faster than Utilities.dangerouslyDeletePath(). @@ -101,9 +101,11 @@ export class AsyncRecycler { * NOTE: To avoid spawning multiple instances of the same command, moveFolder() * MUST NOT be called again after deleteAll() has started. */ - public deleteAll(): void { + public async startDeleteAllAsync(): Promise { if (this._deleting) { - throw new Error('AsyncRecycler.deleteAll() must not be called more than once'); + throw new Error( + `${AsyncRecycler.name}.${this.startDeleteAllAsync.name}() must not be called more than once` + ); } this._deleting = true; @@ -123,7 +125,7 @@ export class AsyncRecycler { stdio: 'ignore' }; - if (os.platform() === 'win32') { + if (IS_WINDOWS) { // PowerShell.exe doesn't work with a detached console, so we need cmd.exe to create // the new console for us. command = 'cmd.exe'; @@ -150,9 +152,18 @@ export class AsyncRecycler { let pathCount: number = 0; + let folderItemNames: string[] = []; + try { + folderItemNames = await FileSystem.readFolderItemNamesAsync(this.recyclerFolder); + } catch (e) { + if (!FileSystem.isNotExistError(e)) { + throw e; + } + } + // child_process.spawn() doesn't expand wildcards. To be safe, we will do it manually // rather than rely on an unknown shell. - for (const filename of FileSystem.readFolderItemNames(this.recyclerFolder)) { + for (const filename of folderItemNames) { // The "." and ".." are supposed to be excluded, but let's be safe if (filename !== '.' && filename !== '..') { args.push(path.join(this.recyclerFolder, filename)); @@ -188,7 +199,7 @@ export class AsyncRecycler { } } - const children: fs.Dirent[] = FileSystem.readFolderItems(folderPath); + const children: FolderItem[] = FileSystem.readFolderItems(folderPath); for (const child of children) { const absoluteChild: string = `${folderPath}/${child.name}`; if (child.isDirectory()) { diff --git a/libraries/rush-lib/src/utilities/CollatedTerminalProvider.ts b/libraries/rush-lib/src/utilities/CollatedTerminalProvider.ts index 88abd9558f2..d83701011c3 100644 --- a/libraries/rush-lib/src/utilities/CollatedTerminalProvider.ts +++ b/libraries/rush-lib/src/utilities/CollatedTerminalProvider.ts @@ -1,9 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { ITerminalProvider, TerminalProviderSeverity } from '@rushstack/node-core-library'; -import { CollatedTerminal } from '@rushstack/stream-collator'; -import { TerminalChunkKind } from '@rushstack/terminal'; +import type { CollatedTerminal } from '@rushstack/stream-collator'; +import { type ITerminalProvider, TerminalProviderSeverity, TerminalChunkKind } from '@rushstack/terminal'; export interface ICollatedTerminalProviderOptions { debugEnabled: boolean; diff --git a/libraries/rush-lib/src/utilities/HotlinkManager.ts b/libraries/rush-lib/src/utilities/HotlinkManager.ts new file mode 100644 index 00000000000..24d0ad027c6 --- /dev/null +++ b/libraries/rush-lib/src/utilities/HotlinkManager.ts @@ -0,0 +1,380 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { pnpmSyncUpdateFileAsync, pnpmSyncCopyAsync, type ILogMessageCallbackOptions } from 'pnpm-sync-lib'; +import * as semver from 'semver'; + +import { Colorize, type ITerminal } from '@rushstack/terminal'; +import { + AlreadyExistsBehavior, + AlreadyReportedError, + Async, + FileConstants, + FileSystem, + JsonFile, + JsonSchema, + type INodePackageJson, + type IPackageJsonDependencyTable +} from '@rushstack/node-core-library'; +import { PackageExtractor } from '@rushstack/package-extractor'; + +import type { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; +import { RushConstants } from '../logic/RushConstants'; +import { PnpmSyncUtilities } from './PnpmSyncUtilities'; +import { BaseLinkManager, SymlinkKind } from '../logic/base/BaseLinkManager'; +import schema from '../schemas/rush-hotlink-state.schema.json'; +import { PURGE_ACTION_NAME } from './actionNameConstants'; +import type { Subspace } from '../api/Subspace'; + +type HotlinkLinkType = 'LinkPackage' | 'BridgePackage'; + +interface IProjectLinkInSubspaceJson { + linkedPackagePath: string; + linkedPackageName: string; + affectedPnpmVirtualStoreFolderPaths?: string[]; + linkType: HotlinkLinkType; +} + +interface IProjectLinksStateJson { + fileVersion: 0; + linksBySubspace: Record; +} + +interface ILinkedPackageInfo { + packageName: string; + linkedPackageNodeModulesPath: string; + externalDependencies: string[]; + workspaceDependencies: string[]; + peerDependencies: IPackageJsonDependencyTable; +} + +type LinksBySubspaceNameMap = Map; + +interface IRushLinkOptions { + rushLinkStateFilePath: string; + linksBySubspaceName: LinksBySubspaceNameMap; +} + +const PROJECT_LINKS_STATE_JSON_SCHEMA: JsonSchema = JsonSchema.fromLoadedObject(schema); + +export class HotlinkManager { + private _linksBySubspaceName: LinksBySubspaceNameMap; + private readonly _rushLinkStateFilePath: string; + + private constructor(options: IRushLinkOptions) { + const { rushLinkStateFilePath, linksBySubspaceName } = options; + this._rushLinkStateFilePath = rushLinkStateFilePath; + this._linksBySubspaceName = linksBySubspaceName; + } + + public hasAnyHotlinksInSubspace(subspaceName: string): boolean { + return !!this._linksBySubspaceName.get(subspaceName)?.length; + } + + private async _hardLinkToLinkedPackageAsync( + terminal: ITerminal, + sourcePath: string, + targetFolder: Set, + lockfileId: string + ): Promise { + const logMessageCallback = (logMessageOptions: ILogMessageCallbackOptions): void => { + PnpmSyncUtilities.processLogMessage(logMessageOptions, terminal); + }; + await pnpmSyncUpdateFileAsync({ + sourceProjectFolder: sourcePath, + // TODO: Update pnpmSyncUpdateFileAsync to take an Iterable + targetFolders: Array.from(targetFolder), + lockfileId, + logMessageCallback + }); + const pnpmSyncJsonPath: string = `${sourcePath}/${RushConstants.nodeModulesFolderName}/${RushConstants.pnpmSyncFilename}`; + await pnpmSyncCopyAsync({ + pnpmSyncJsonPath, + ensureFolderAsync: FileSystem.ensureFolderAsync, + forEachAsyncWithConcurrency: Async.forEachAsync, + getPackageIncludedFiles: PackageExtractor.getPackageIncludedFilesAsync, + logMessageCallback + }); + } + + private async _modifyAndSaveLinkStateAsync( + cb: (linkState: LinksBySubspaceNameMap) => Promise | LinksBySubspaceNameMap + ): Promise { + const newLinksBySubspaceName: LinksBySubspaceNameMap = await cb(this._linksBySubspaceName); + this._linksBySubspaceName = newLinksBySubspaceName; + const linkStateJson: IProjectLinksStateJson = { + fileVersion: 0, + linksBySubspace: Object.fromEntries(newLinksBySubspaceName) + }; + await JsonFile.saveAsync(linkStateJson, this._rushLinkStateFilePath); + } + + public async purgeLinksAsync(terminal: ITerminal, subspaceName: string): Promise { + if (!this.hasAnyHotlinksInSubspace(subspaceName)) { + return false; + } + + const logMessageCallback = (logMessageOptions: ILogMessageCallbackOptions): void => { + PnpmSyncUtilities.processLogMessage(logMessageOptions, terminal); + }; + + await this._modifyAndSaveLinkStateAsync(async (linksBySubspaceName) => { + const rushLinkFileState: IProjectLinkInSubspaceJson[] = linksBySubspaceName.get(subspaceName) ?? []; + await Async.forEachAsync( + rushLinkFileState, + async ({ linkedPackagePath, affectedPnpmVirtualStoreFolderPaths = [] }) => { + await pnpmSyncUpdateFileAsync({ + sourceProjectFolder: linkedPackagePath, + targetFolders: [], + lockfileId: subspaceName, + logMessageCallback + }); + // pnpm will reuse packages in .pnpm directory, so we need to manually delete them before installation + await Async.forEachAsync( + affectedPnpmVirtualStoreFolderPaths, + async (affectedPnpmVirtualStoreFolderName) => { + await FileSystem.deleteFolderAsync(affectedPnpmVirtualStoreFolderName); + }, + { concurrency: 10 } + ); + }, + { concurrency: 10 } + ); + + const newLinksBySubspaceName: LinksBySubspaceNameMap = new Map(linksBySubspaceName); + newLinksBySubspaceName.delete(subspaceName); + return newLinksBySubspaceName; + }); + + return true; + } + + private async _getLinkedPackageInfoAsync(linkedPackagePath: string): Promise { + const linkedPackageJsonPath: string = `${linkedPackagePath}/${FileConstants.PackageJson}`; + + const linkedPackageJsonExists: boolean = await FileSystem.existsAsync(linkedPackageJsonPath); + if (!linkedPackageJsonExists) { + throw new Error(`Cannot find ${FileConstants.PackageJson} in the path ${linkedPackagePath}`); + } + + const { + dependencies = {}, + name: packageName, + peerDependencies = {} + }: INodePackageJson = await JsonFile.loadAsync(linkedPackageJsonPath); + const linkedPackageNodeModulesPath: string = `${linkedPackagePath}/${RushConstants.nodeModulesFolderName}`; + + const externalDependencies: string[] = []; + const workspaceDependencies: string[] = []; + + for (const [name, protocol] of Object.entries(dependencies)) { + if (protocol.startsWith('workspace')) { + workspaceDependencies.push(name); + } else { + externalDependencies.push(name); + } + } + + return { + packageName, + linkedPackageNodeModulesPath, + externalDependencies, + workspaceDependencies, + peerDependencies + }; + } + + private async _getPackagePathsMatchingNameAndVersionAsync( + consumerPackagePnpmDependenciesFolderPath: string, + packageName: string, + versionRange: string + ): Promise> { + const subDirectories: string[] = await FileSystem.readFolderItemNamesAsync( + consumerPackagePnpmDependenciesFolderPath + ); + const packageSourcePathSet: Set = new Set(); + for (const dirName of subDirectories) { + const packageSourcePath: string = `${consumerPackagePnpmDependenciesFolderPath}/${dirName}/${RushConstants.nodeModulesFolderName}/${packageName}`; + if (await FileSystem.existsAsync(packageSourcePath)) { + const { version } = await JsonFile.loadAsync(`${packageSourcePath}/${FileConstants.PackageJson}`); + if (semver.satisfies(version, versionRange, { includePrerelease: true })) { + packageSourcePathSet.add(packageSourcePath); + } + } + } + return packageSourcePathSet; + } + + public async bridgePackageAsync( + terminal: ITerminal, + subspace: Subspace, + linkedPackagePath: string, + version: string + ): Promise { + const subspaceName: string = subspace.subspaceName; + try { + const { packageName } = await this._getLinkedPackageInfoAsync(linkedPackagePath); + const consumerPackagePnpmDependenciesFolderPath: string = `${subspace.getSubspaceTempFolderPath()}/${ + RushConstants.nodeModulesFolderName + }/${RushConstants.pnpmVirtualStoreFolderName}`; + const sourcePathSet: Set = await this._getPackagePathsMatchingNameAndVersionAsync( + consumerPackagePnpmDependenciesFolderPath, + packageName, + version + ); + if (sourcePathSet.size === 0) { + throw new Error( + `Cannot find package ${packageName} ${version} in ${consumerPackagePnpmDependenciesFolderPath}` + ); + } + await this._hardLinkToLinkedPackageAsync(terminal, linkedPackagePath, sourcePathSet, subspaceName); + await this._modifyAndSaveLinkStateAsync((linksBySubspaceName) => { + const newConsumerPackageLinks: IProjectLinkInSubspaceJson[] = [ + ...(linksBySubspaceName.get(subspaceName) ?? []) + ]; + const existingLinkIndex: number = newConsumerPackageLinks.findIndex( + (link) => link.linkedPackageName === packageName + ); + + if (existingLinkIndex >= 0) { + newConsumerPackageLinks.splice(existingLinkIndex, 1); + } + + newConsumerPackageLinks.push({ + linkedPackagePath, + linkedPackageName: packageName, + affectedPnpmVirtualStoreFolderPaths: Array.from(sourcePathSet), + linkType: 'BridgePackage' + }); + + const newLinksBySubspaceName: LinksBySubspaceNameMap = new Map(linksBySubspaceName); + newLinksBySubspaceName.set(subspaceName, newConsumerPackageLinks); + return newLinksBySubspaceName; + }); + + terminal.writeLine( + Colorize.green(`Successfully bridged package "${packageName}" for "${subspaceName}"`) + ); + } catch (error) { + terminal.writeErrorLine( + Colorize.red(`Failed to bridge package "${linkedPackagePath}" to "${subspaceName}": ${error.message}`) + ); + + const alreadyExistsError: Error = new AlreadyReportedError(); + alreadyExistsError.message = error.message; + alreadyExistsError.stack = error.stack; + throw alreadyExistsError; + } + } + + public async linkPackageAsync( + terminal: ITerminal, + consumerPackage: RushConfigurationProject, + linkedPackagePath: string + ): Promise { + const consumerPackageName: string = consumerPackage.packageName; + try { + const { packageName: linkedPackageName } = await this._getLinkedPackageInfoAsync(linkedPackagePath); + + const slashIndex: number = linkedPackageName.indexOf('/'); + const [scope, packageBaseName] = + slashIndex !== -1 + ? [linkedPackageName.substring(0, slashIndex), linkedPackageName.substring(slashIndex)] + : [undefined, linkedPackageName]; + + let sourceNodeModulesPath: string = `${consumerPackage.projectFolder}/${RushConstants.nodeModulesFolderName}`; + if (scope) { + sourceNodeModulesPath = `${sourceNodeModulesPath}/${scope}`; + } + + await FileSystem.ensureFolderAsync(sourceNodeModulesPath); + + const symlinkPath: string = `${sourceNodeModulesPath}/${packageBaseName}`; + + // Create symlink to linkedPackage + await BaseLinkManager._createSymlinkAsync({ + symlinkKind: SymlinkKind.Directory, + linkTargetPath: linkedPackagePath, + newLinkPath: symlinkPath, + alreadyExistsBehavior: AlreadyExistsBehavior.Overwrite + }); + + // Record the link information between the consumer package and the linked package + await this._modifyAndSaveLinkStateAsync((linksBySubspaceName) => { + const subspaceName: string = consumerPackage.subspace.subspaceName; + const newConsumerPackageLinks: IProjectLinkInSubspaceJson[] = [ + ...(linksBySubspaceName.get(subspaceName) ?? []) + ]; + const existingLinkIndex: number = newConsumerPackageLinks.findIndex( + (link) => link.linkedPackageName === linkedPackageName + ); + + if (existingLinkIndex >= 0) { + newConsumerPackageLinks.splice(existingLinkIndex, 1); + } + + newConsumerPackageLinks.push({ + linkedPackagePath, + linkedPackageName, + linkType: 'LinkPackage' + }); + + const newLinksBySubspaceName: LinksBySubspaceNameMap = new Map(linksBySubspaceName); + newLinksBySubspaceName.set(subspaceName, newConsumerPackageLinks); + return newLinksBySubspaceName; + }); + + terminal.writeLine( + Colorize.green(`Successfully linked package "${linkedPackageName}" for "${consumerPackageName}"`) + ); + } catch (error) { + terminal.writeErrorLine( + Colorize.red( + `Failed to link package "${linkedPackagePath}" to "${consumerPackageName}": ${error.message}` + ) + ); + + const alreadyExistsError: Error = new AlreadyReportedError(); + alreadyExistsError.message = error.message; + alreadyExistsError.stack = error.stack; + throw alreadyExistsError; + } + } + + public static loadFromRushConfiguration(rushConfiguration: RushConfiguration): HotlinkManager { + // TODO: make this function async + const rushLinkStateFilePath: string = `${rushConfiguration.commonTempFolder}/${RushConstants.rushHotlinkStateFilename}`; + let rushLinkState: IProjectLinksStateJson | undefined; + try { + rushLinkState = JsonFile.loadAndValidate(rushLinkStateFilePath, PROJECT_LINKS_STATE_JSON_SCHEMA); + } catch (error) { + if (!FileSystem.isNotExistError(error as Error)) { + throw error; + } + } + + if (!rushLinkState) { + return new HotlinkManager({ + rushLinkStateFilePath, + linksBySubspaceName: new Map() + }); + } else { + const { fileVersion, linksBySubspace } = rushLinkState; + if (fileVersion !== 0) { + throw new Error( + `The rush project link state file "${rushLinkStateFilePath}" has an unexpected format, so this repo's ` + + `installation state is likely in an inconsistent state. Run 'rush ${PURGE_ACTION_NAME}' purge to clear ` + + `the installation.` + ); + } else { + const linksBySubspaceName: LinksBySubspaceNameMap = new Map(Object.entries(linksBySubspace)); + + return new HotlinkManager({ + rushLinkStateFilePath, + linksBySubspaceName + }); + } + } + } +} diff --git a/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts b/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts index b4850790d37..701e0a5b8e1 100644 --- a/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts +++ b/libraries/rush-lib/src/utilities/InteractiveUpgradeUI.ts @@ -5,14 +5,10 @@ // https://github.com/dylang/npm-check/blob/master/lib/out/interactive-update.js // Extended to use one type of text table -import inquirer from 'inquirer'; -import colors from 'colors/safe'; -import CliTable from 'cli-table'; -import Separator from 'inquirer/lib/objects/separator'; -import { Import } from '@rushstack/node-core-library'; -import type * as NpmCheck from 'npm-check'; +import type { Separator } from '@inquirer/checkbox'; -const _: typeof import('lodash') = Import.lazy('lodash', require); +import { AnsiEscape, Colorize, TerminalTable } from '@rushstack/terminal'; +import type { INpmCheckPackageSummary } from '@rushstack/npm-check-fork'; export interface IUIGroup { title: string; @@ -25,11 +21,11 @@ export interface IUIGroup { } export interface IDepsToUpgradeAnswers { - packages: NpmCheck.INpmCheckPackage[]; + packages: INpmCheckPackageSummary[]; } export interface IUpgradeInteractiveDepChoice { - value: NpmCheck.INpmCheckPackage; + value: INpmCheckPackageSummary; name: string | string[]; short: string; } @@ -37,19 +33,19 @@ export interface IUpgradeInteractiveDepChoice { type ChoiceTable = (Separator | IUpgradeInteractiveDepChoice | boolean | undefined)[] | undefined; function greenUnderlineBold(text: string): string { - return colors.underline(colors.bold(colors.green(text))); + return Colorize.underline(Colorize.bold(Colorize.green(text))); } function yellowUnderlineBold(text: string): string { - return colors.underline(colors.bold(colors.yellow(text))); + return Colorize.underline(Colorize.bold(Colorize.yellow(text))); } function redUnderlineBold(text: string): string { - return colors.underline(colors.bold(colors.red(text))); + return Colorize.underline(Colorize.bold(Colorize.red(text))); } function magentaUnderlineBold(text: string): string { - return colors.underline(colors.bold(colors.magenta(text))); + return Colorize.underline(Colorize.bold(Colorize.magenta(text))); } export const UI_GROUPS: IUIGroup[] = [ @@ -58,52 +54,62 @@ export const UI_GROUPS: IUIGroup[] = [ filter: { mismatch: true, bump: undefined } }, { - title: `${greenUnderlineBold('Missing.')} ${colors.green('You probably want these.')}`, + title: `${greenUnderlineBold('Missing.')} ${Colorize.green('You probably want these.')}`, filter: { notInstalled: true, bump: undefined } }, { - title: `${greenUnderlineBold('Patch Update')} ${colors.green('Backwards-compatible bug fixes.')}`, + title: `${greenUnderlineBold('Patch Update')} ${Colorize.green('Backwards-compatible bug fixes.')}`, filter: { bump: 'patch' } }, { - title: `${yellowUnderlineBold('Minor Update')} ${colors.yellow('New backwards-compatible features.')}`, + title: `${yellowUnderlineBold('Minor Update')} ${Colorize.yellow('New backwards-compatible features.')}`, bgColor: 'yellow', filter: { bump: 'minor' } }, { - title: `${redUnderlineBold('Major Update')} ${colors.red( + title: `${redUnderlineBold('Major Update')} ${Colorize.red( 'Potentially breaking API changes. Use caution.' )}`, filter: { bump: 'major' } }, { - title: `${magentaUnderlineBold('Non-Semver')} ${colors.magenta('Versions less than 1.0.0, caution.')}`, + title: `${magentaUnderlineBold('Non-Semver')} ${Colorize.magenta('Versions less than 1.0.0, caution.')}`, filter: { bump: 'nonSemver' } } ]; -function label(dep: NpmCheck.INpmCheckPackage): string[] { +function label(dep: INpmCheckPackageSummary): string[] { const bumpInstalled: string = dep.bump ? dep.installed : ''; const installed: string = dep.mismatch ? dep.packageJson : bumpInstalled; - const name: string = colors.yellow(dep.moduleName); - const type: string = dep.devDependency ? colors.green(' devDep') : ''; - const missing: string = dep.notInstalled ? colors.red(' missing') : ''; - const homepage: string = dep.homepage ? colors.blue(colors.underline(dep.homepage)) : ''; + const name: string = Colorize.yellow(dep.moduleName); + const type: string = dep.devDependency ? Colorize.green(' devDep') : ''; + const missing: string = dep.notInstalled ? Colorize.red(' missing') : ''; + const homepage: string = dep.homepage ? Colorize.blue(Colorize.underline(dep.homepage)) : ''; return [ name + type + missing, installed, installed && '>', - colors.bold(dep.latest || ''), - dep.latest ? homepage : dep.regError || dep.pkgError + Colorize.bold(dep.latest || ''), + dep.latest ? homepage : getErrorDep(dep) ]; } -function short(dep: NpmCheck.INpmCheckPackage): string { +function getErrorDep(dep: INpmCheckPackageSummary): string { + if (dep.regError !== undefined && dep.regError && dep.regError instanceof Error) { + return dep.regError.message; + } else if (dep.pkgError !== undefined && dep.pkgError && dep.pkgError instanceof Error) { + return dep.pkgError.message; + } + + return ''; +} + +function short(dep: INpmCheckPackageSummary): string { return `${dep.moduleName}@${dep.latest}`; } -function choice(dep: NpmCheck.INpmCheckPackage): IUpgradeInteractiveDepChoice | boolean | Separator { +function getChoice(dep: INpmCheckPackageSummary): IUpgradeInteractiveDepChoice | boolean | Separator { if (!dep.mismatch && !dep.bump && !dep.notInstalled) { return false; } @@ -115,64 +121,70 @@ function choice(dep: NpmCheck.INpmCheckPackage): IUpgradeInteractiveDepChoice | }; } -function unselectable(options?: { title: string }): Separator { - return new inquirer.Separator(colors.reset(options ? options.title : '')); -} +export const upgradeInteractive = async (pkgs: INpmCheckPackageSummary[]): Promise => { + const { default: checkbox, Separator } = await import('@inquirer/checkbox'); -function createChoices(packages: NpmCheck.INpmCheckPackage[], options: IUIGroup): ChoiceTable { - const filteredChoices: NpmCheck.INpmCheckPackage[] = _.filter( - packages, - options.filter - ) as NpmCheck.INpmCheckPackage[]; - - const choices: (IUpgradeInteractiveDepChoice | Separator | boolean)[] = filteredChoices - .map(choice) - .filter(Boolean); - - const cliTable: CliTable = new CliTable({ - chars: { - top: '', - 'top-mid': '', - 'top-left': '', - 'top-right': '', - bottom: '', - 'bottom-mid': '', - 'bottom-left': '', - 'bottom-right': '', - left: '', - 'left-mid': '', - mid: '', - 'mid-mid': '', - right: '', - 'right-mid': '', - middle: ' ' - }, - colWidths: [50, 10, 3, 10, 100] - }); + function unselectable(options?: { title: string }): Separator { + return new Separator(AnsiEscape.removeCodes(options ? options.title : '')); + } - cliTable.push(..._.map(choices, 'name')); + function createChoices(packages: INpmCheckPackageSummary[], options: IUIGroup): ChoiceTable { + const { filter } = options; + const filteredChoices: INpmCheckPackageSummary[] = packages.filter((pkg: INpmCheckPackageSummary) => { + if ('mismatch' in filter && pkg.mismatch !== filter.mismatch) { + return false; + } else if ('bump' in filter && pkg.bump !== filter.bump) { + return false; + } else if ('notInstalled' in filter && pkg.notInstalled !== filter.notInstalled) { + return false; + } else { + return true; + } + }) as INpmCheckPackageSummary[]; + + const choices: (IUpgradeInteractiveDepChoice | Separator | boolean)[] = filteredChoices + .map(getChoice) + .filter(Boolean); + + const cliTable: TerminalTable = new TerminalTable({ + borderless: true, + colWidths: [50, 10, 3, 10, 100] + }); + + for (const choice of choices) { + if (typeof choice === 'object' && 'name' in choice) { + // choice.name is string[] at this point (set by label()); it is only replaced + // with a string after the table is rendered below. + cliTable.push(choice.name as string[]); + } + } - const choicesAsATable: string[] = cliTable.toString().split('\n'); - const choicesWithTableFormatting: boolean[] = _.map(choices, (choice: IUpgradeInteractiveDepChoice, i) => { - choice.name = choicesAsATable[i]; - return choice; - }); + const choicesAsATable: string[] = cliTable.getLines(); + for (let i: number = 0; i < choices.length; i++) { + const choice: IUpgradeInteractiveDepChoice | Separator | boolean | undefined = choices[i]; + if (typeof choice === 'object' && 'name' in choice) { + choice.name = choicesAsATable[i]; + } + } - if (choicesWithTableFormatting.length) { - choices.unshift(unselectable(options)); - choices.unshift(unselectable()); - return choices; + if (choices.length > 0) { + choices.unshift(unselectable(options)); + choices.unshift(unselectable()); + return choices; + } } -} -export const upgradeInteractive = async ( - pkgs: NpmCheck.INpmCheckPackage[] -): Promise => { const choicesGrouped: ChoiceTable[] = UI_GROUPS.map((group) => createChoices(pkgs, group)).filter(Boolean); - const choices: ChoiceTable = _.flatten(choicesGrouped); + const choices: ChoiceTable = []; + for (const choiceGroup of choicesGrouped) { + if (choiceGroup) { + choices.push(...choiceGroup); + } + } if (!choices.length) { + // eslint-disable-next-line no-console console.log('All dependencies are up to date!'); return { packages: [] }; } @@ -180,17 +192,15 @@ export const upgradeInteractive = async ( choices.push(unselectable()); choices.push(unselectable({ title: 'Space to select. Enter to start upgrading. Control-C to cancel.' })); - const promptQuestions: inquirer.QuestionCollection = [ - { - name: 'packages', - message: 'Choose which packages to upgrade', - type: 'checkbox', - choices: choices.concat(unselectable()), - pageSize: process.stdout.rows - 2 - } - ]; - - const answers: IDepsToUpgradeAnswers = (await inquirer.prompt(promptQuestions)) as IDepsToUpgradeAnswers; + const packages: INpmCheckPackageSummary[] = await checkbox({ + message: 'Choose which packages to upgrade', + // At this point choices only contains Separator and IUpgradeInteractiveDepChoice items + // with their `name` fields already set to strings by createChoices. + choices: choices.concat(unselectable()) as unknown as ReadonlyArray< + Separator | { value: INpmCheckPackageSummary; name: string; short: string } + >, + pageSize: process.stdout.rows - 2 + }); - return answers; + return { packages }; }; diff --git a/libraries/rush-lib/src/utilities/Npm.ts b/libraries/rush-lib/src/utilities/Npm.ts index 2b265114900..c031831cd9e 100644 --- a/libraries/rush-lib/src/utilities/Npm.ts +++ b/libraries/rush-lib/src/utilities/Npm.ts @@ -1,40 +1,61 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { Utilities } from './Utilities'; import * as semver from 'semver'; +import { Utilities } from './Utilities'; + +async function runNpmCommandAndCaptureOutputAsync( + args: string[], + workingDirectory: string, + environment: { [key: string]: string | undefined } +): Promise { + const { stdout, stderr, signal, exitCode } = await Utilities.executeCommandAndCaptureOutputAsync({ + command: 'npm', + args, + workingDirectory, + environment, + keepEnvironment: true, + captureExitCodeAndSignal: true + }); + + if (signal) { + throw new Error(`The npm command was terminated by signal: ${signal}. Output: ${stdout} ${stderr}`); + } else if (exitCode !== 0) { + throw new Error(`The npm command failed with exit code: ${exitCode}. Output: ${stdout} ${stderr}`); + } else { + return stdout; + } +} + export class Npm { - public static publishedVersions( + public static async getPublishedVersionsAsync( packageName: string, - cwd: string, - env: { [key: string]: string | undefined }, + workingDirectory: string, + environment: { [key: string]: string | undefined }, extraArgs: string[] = [] - ): string[] { + ): Promise { const versions: string[] = []; try { - const packageTime: string = Utilities.executeCommandAndCaptureOutput( - 'npm', + const packageTime: string = await runNpmCommandAndCaptureOutputAsync( ['view', packageName, 'time', '--json', ...extraArgs], - cwd, - env, - true + workingDirectory, + environment ); - if (packageTime && packageTime !== '') { + if (packageTime) { Object.keys(JSON.parse(packageTime)).forEach((v) => { if (semver.valid(v)) { versions.push(v); } }); } else { + // eslint-disable-next-line no-console console.log(`Package ${packageName} time value does not exist. Fall back to versions.`); // time property does not exist. It happens sometimes. Fall back to versions. - const packageVersions: string = Utilities.executeCommandAndCaptureOutput( - 'npm', + const packageVersions: string = await runNpmCommandAndCaptureOutputAsync( ['view', packageName, 'versions', '--json', ...extraArgs], - cwd, - env, - true + workingDirectory, + environment ); if (packageVersions && packageVersions.length > 0) { const parsedPackageVersions: string | string[] = JSON.parse(packageVersions); @@ -45,13 +66,17 @@ export class Npm { } ); } else { + // eslint-disable-next-line no-console console.log(`No version is found for ${packageName}`); } } - } catch (error) { - if ((error as Error).message.indexOf('npm ERR! 404') >= 0) { + } catch (e) { + const error: Error = e; + if (['E404', 'npm ERR! 404'].some((check) => error.message.includes(check))) { + // eslint-disable-next-line no-console console.log(`Package ${packageName} does not exist in the registry.`); } else { + // eslint-disable-next-line no-console console.log(`Failed to get NPM information about ${packageName}.`); throw error; } diff --git a/libraries/rush-lib/src/utilities/NullTerminalProvider.ts b/libraries/rush-lib/src/utilities/NullTerminalProvider.ts new file mode 100644 index 00000000000..675e75e29db --- /dev/null +++ b/libraries/rush-lib/src/utilities/NullTerminalProvider.ts @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { ITerminalProvider } from '@rushstack/terminal'; + +/** + * A terminal provider like /dev/null + */ +export class NullTerminalProvider implements ITerminalProvider { + public supportsColor: boolean = false; + public eolCharacter: string = '\n'; + public write(): void {} +} diff --git a/libraries/rush-lib/src/utilities/OverlappingPathAnalyzer.ts b/libraries/rush-lib/src/utilities/OverlappingPathAnalyzer.ts index 520c6e0dfa2..826a5e7255a 100644 --- a/libraries/rush-lib/src/utilities/OverlappingPathAnalyzer.ts +++ b/libraries/rush-lib/src/utilities/OverlappingPathAnalyzer.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + interface IPathTreeNode { encounteredLabels: Set; label?: TLabel; diff --git a/libraries/rush-lib/src/utilities/PathConstants.ts b/libraries/rush-lib/src/utilities/PathConstants.ts index 167bdb8af24..632bce8a6ae 100644 --- a/libraries/rush-lib/src/utilities/PathConstants.ts +++ b/libraries/rush-lib/src/utilities/PathConstants.ts @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See the @microsoft/rush package's LICENSE file for license information. +// See LICENSE in the project root for license information. import { PackageJsonLookup } from '@rushstack/node-core-library'; @@ -19,6 +19,7 @@ export const assetsFolderPath: string = `${rushLibFolderRootPath}/assets`; export const scriptsFolderName: string = 'scripts'; export const pnpmfileShimFilename: string = 'PnpmfileShim.js'; +export const subspacePnpmfileShimFilename: string = 'SubspaceGlobalPnpmfileShim.js'; export const installRunScriptFilename: string = 'install-run.js'; export const installRunRushScriptFilename: string = 'install-run-rush.js'; export const installRunRushxScriptFilename: string = 'install-run-rushx.js'; diff --git a/libraries/rush-lib/src/utilities/PnpmSyncUtilities.ts b/libraries/rush-lib/src/utilities/PnpmSyncUtilities.ts new file mode 100644 index 00000000000..58c0e3bacd7 --- /dev/null +++ b/libraries/rush-lib/src/utilities/PnpmSyncUtilities.ts @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { + type ILogMessageCallbackOptions, + LogMessageIdentifier, + type LogMessageDetails, + LogMessageKind +} from 'pnpm-sync-lib'; + +import { AlreadyReportedError } from '@rushstack/node-core-library'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; + +import { RushConstants } from '../logic/RushConstants'; + +export class PnpmSyncUtilities { + public static processLogMessage(options: ILogMessageCallbackOptions, terminal: ITerminal): void { + const message: string = options.message; + const details: LogMessageDetails = options.details; + + // Special formatting for interested messages + switch (details.messageIdentifier) { + case LogMessageIdentifier.PREPARE_FINISHING: + terminal.writeVerboseLine( + _addLinePrefix( + `Regenerated ${RushConstants.pnpmSyncFilename} in ${Math.round(details.executionTimeInMs)} ms` + ) + ); + return; + + case LogMessageIdentifier.COPY_FINISHING: + { + const customMessage: string = + `Synced ${details.fileCount} ` + + (details.fileCount === 1 ? 'file' : 'files') + + ` in ${Math.round(details.executionTimeInMs)} ms`; + + terminal.writeVerboseLine(_addLinePrefix(customMessage)); + } + return; + + case LogMessageIdentifier.PREPARE_REPLACING_FILE: + { + const customMessage: string = + `Expecting ${RushConstants.pnpmSyncFilename} version ${details.expectedVersion}, ` + + `but found version ${details.actualVersion}`; + + terminal.writeVerboseLine(_addLinePrefix(message)); + terminal.writeVerboseLine(_addLinePrefix(customMessage)); + } + return; + + case LogMessageIdentifier.COPY_ERROR_INCOMPATIBLE_SYNC_FILE: { + terminal.writeErrorLine( + _addLinePrefix( + `The workspace was installed using an incompatible version of pnpm-sync.\n` + + `Please run "rush install" or "rush update" again.` + ) + ); + + terminal.writeLine( + _addLinePrefix( + `Expecting ${RushConstants.pnpmSyncFilename} version ${details.expectedVersion}, ` + + `but found version ${details.actualVersion}\n` + + `Affected folder: ${details.pnpmSyncJsonPath}` + ) + ); + throw new AlreadyReportedError(); + } + } + + // Default handling for other messages + switch (options.messageKind) { + case LogMessageKind.ERROR: + terminal.writeErrorLine(Colorize.red('ERROR: pnpm-sync: ' + message)); + throw new AlreadyReportedError(); + + case LogMessageKind.WARNING: + terminal.writeWarningLine(Colorize.yellow('pnpm-sync: ' + message)); + return; + + case LogMessageKind.INFO: + case LogMessageKind.VERBOSE: + terminal.writeDebugLine(_addLinePrefix(message)); + return; + } + } +} + +function _addLinePrefix(message: string): string { + return message + .split('\n') + .map((x) => (x.trim() ? Colorize.cyan(`pnpm-sync: `) + x : x)) + .join('\n'); +} diff --git a/libraries/rush-lib/src/utilities/RushAlerts.ts b/libraries/rush-lib/src/utilities/RushAlerts.ts new file mode 100644 index 00000000000..abca0c70577 --- /dev/null +++ b/libraries/rush-lib/src/utilities/RushAlerts.ts @@ -0,0 +1,431 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { Colorize, PrintUtilities, type ITerminal } from '@rushstack/terminal'; +import { FileSystem, JsonFile, JsonSchema, JsonSyntax } from '@rushstack/node-core-library'; + +import type { RushConfiguration } from '../api/RushConfiguration'; +import rushAlertsSchemaJson from '../schemas/rush-alerts.schema.json'; +import { RushConstants } from '../logic/RushConstants'; +import { PURGE_ACTION_NAME } from './actionNameConstants'; + +export interface IRushAlertsOptions { + terminal: ITerminal; + rushJsonFolder: string; + rushAlertsConfig: IRushAlertsConfig | undefined; + rushAlertsState: IRushAlertsState | undefined; + rushAlertsConfigFilePath: string; + rushAlertsStateFilePath: string; +} + +interface IRushAlertsConfig { + alerts: Array; +} +interface IRushAlertsConfigEntry { + alertId: string; + title: string; + message: Array; + detailsUrl: string; + startTime: string; + endTime: string; + conditionScript?: string; + priority?: AlertPriority; + maximumDisplayInterval?: AlertDisplayInterval; +} +interface IRushAlertsState { + [alertId: string]: IRushAlertStateEntry; +} +interface IRushAlertStateEntry { + lastDisplayTime?: string; + snooze?: boolean; + snoozeEndTime?: string; +} + +type AlertStatus = 'active' | 'inactive' | 'snoozed'; + +const enum AlertDisplayInterval { + ALWAYS = 'always', + MONTHLY = 'monthly', + WEEKLY = 'weekly', + DAILY = 'daily', + HOURLY = 'hourly' +} + +const enum AlertPriority { + HIGH = 'high', + NORMAL = 'normal', + LOW = 'low' +} + +export class RushAlerts { + private readonly _terminal: ITerminal; + + private readonly _rushAlertsConfig: IRushAlertsConfig | undefined; + private readonly _rushAlertsState: IRushAlertsState; + + private readonly _rushJsonFolder: string; + public readonly rushAlertsStateFilePath: string; + public readonly rushAlertsConfigFilePath: string; + + public static readonly ALERT_PRIORITY: string[] = [ + AlertPriority.HIGH, + AlertPriority.NORMAL, + AlertPriority.LOW + ]; + public static readonly alertDisplayIntervalDurations: Map = new Map([ + [AlertDisplayInterval.ALWAYS, -1], + [AlertDisplayInterval.MONTHLY, 1000 * 60 * 60 * 24 * 30], + [AlertDisplayInterval.WEEKLY, 1000 * 60 * 60 * 24 * 7], + [AlertDisplayInterval.DAILY, 1000 * 60 * 60 * 24], + [AlertDisplayInterval.HOURLY, 1000 * 60 * 60] + ]); + // only display alerts when certain specific actions are triggered + public static readonly alertTriggerActions: string[] = [ + // TODO: put the rest of the action names in constants + 'add', + 'change', + 'deploy', + 'init', + 'publish', + PURGE_ACTION_NAME, + 'remove', + 'update', + 'install', + 'build', + 'list', + 'version' + ]; + + public constructor(options: IRushAlertsOptions) { + const { + terminal, + rushJsonFolder, + rushAlertsStateFilePath, + rushAlertsConfigFilePath, + rushAlertsConfig, + rushAlertsState = {} + } = options; + this._terminal = terminal; + this._rushJsonFolder = rushJsonFolder; + this.rushAlertsStateFilePath = rushAlertsStateFilePath; + this.rushAlertsConfigFilePath = rushAlertsConfigFilePath; + this._rushAlertsConfig = rushAlertsConfig; + this._rushAlertsState = rushAlertsState; + } + + public static async loadFromConfigurationAsync( + rushConfiguration: RushConfiguration, + terminal: ITerminal + ): Promise { + const rushAlertsStateFilePath: string = `${rushConfiguration.commonTempFolder}/${RushConstants.rushAlertsConfigFilename}`; + const rushAlertsConfigFilePath: string = `${rushConfiguration.commonRushConfigFolder}/${RushConstants.rushAlertsConfigFilename}`; + const rushJsonFolder: string = rushConfiguration.rushJsonFolder; + + const [isRushAlertsStateFileExists, isRushAlertsConfigFileExists] = await Promise.all([ + FileSystem.existsAsync(rushAlertsStateFilePath), + FileSystem.existsAsync(rushAlertsConfigFilePath) + ]); + + const [rushAlertsConfig, rushAlertsState] = await Promise.all([ + isRushAlertsConfigFileExists + ? JsonFile.loadAndValidateAsync( + rushAlertsConfigFilePath, + JsonSchema.fromLoadedObject(rushAlertsSchemaJson) + ) + : undefined, + isRushAlertsStateFileExists + ? JsonFile.loadAsync(rushAlertsStateFilePath, { jsonSyntax: JsonSyntax.JsonWithComments }) + : undefined + ]); + + return new RushAlerts({ + terminal, + rushAlertsStateFilePath, + rushAlertsConfigFilePath, + rushJsonFolder, + rushAlertsConfig, + rushAlertsState + }); + } + + private _ensureAlertStateIsUpToDate(): void { + // ensure `temp/rush-alerts.json` is up to date + if (this._rushAlertsConfig) { + for (const alert of this._rushAlertsConfig.alerts) { + if (!(alert.alertId in this._rushAlertsState)) { + this._rushAlertsState[alert.alertId] = { + snooze: false + }; + } + } + } + } + + public async printAlertsAsync(): Promise { + if (!this._rushAlertsConfig || this._rushAlertsConfig.alerts.length === 0) return; + + this._ensureAlertStateIsUpToDate(); + + this._terminal.writeLine(); + + const alert: IRushAlertsConfigEntry | undefined = await this._selectAlertByPriorityAsync(); + if (alert) { + this._printMessageInBoxStyle(alert); + this._rushAlertsState[alert.alertId].lastDisplayTime = new Date().toISOString(); + } + + await this._writeRushAlertStateAsync(); + } + + public async printAllAlertsAsync(): Promise { + const allAlerts: IRushAlertsConfigEntry[] = this._rushAlertsConfig?.alerts ?? []; + + const activeAlerts: IRushAlertsConfigEntry[] = []; + const snoozedAlerts: IRushAlertsConfigEntry[] = []; + const inactiveAlerts: IRushAlertsConfigEntry[] = []; + + await Promise.all( + allAlerts.map(async (alert) => { + const isAlertValid: boolean = await this._isAlertValidAsync(alert); + const alertState: IRushAlertStateEntry = this._rushAlertsState[alert.alertId]; + + if (!isAlertValid) { + inactiveAlerts.push(alert); + return; + } + + if (this._isSnoozing(alertState)) { + snoozedAlerts.push(alert); + return; + } + + activeAlerts.push(alert); + }) + ); + + this._printAlerts(activeAlerts, 'active'); + this._printAlerts(snoozedAlerts, 'snoozed'); + this._printAlerts(inactiveAlerts, 'inactive'); + } + + private _printAlerts(alerts: IRushAlertsConfigEntry[], status: AlertStatus): void { + if (alerts.length === 0) return; + switch (status) { + case 'active': + case 'inactive': + this._terminal.writeLine(Colorize.yellow(`The following alerts are currently ${status}:`)); + break; + case 'snoozed': + this._terminal.writeLine(Colorize.yellow('The following alerts are currently active but snoozed:')); + break; + } + alerts.forEach(({ title }) => { + this._terminal.writeLine(Colorize.green(`"${title}"`)); + }); + this._terminal.writeLine(); + } + + public async snoozeAlertsByAlertIdAsync(alertId: string, forever: boolean = false): Promise { + this._ensureAlertStateIsUpToDate(); + if (forever) { + this._rushAlertsState[alertId].snooze = true; + } else { + this._rushAlertsState[alertId].snooze = true; + const snoozeEndTime: Date = new Date(); + snoozeEndTime.setDate(snoozeEndTime.getDate() + 7); + this._rushAlertsState[alertId].snoozeEndTime = snoozeEndTime.toISOString(); + } + await this._writeRushAlertStateAsync(); + } + + private async _selectAlertByPriorityAsync(): Promise { + const alerts: Array = this._rushAlertsConfig!.alerts; + const alertsState: IRushAlertsState = this._rushAlertsState; + + const needDisplayAlerts: Array = ( + await Promise.all( + alerts.map(async (alert) => { + const isAlertValid: boolean = await this._isAlertValidAsync(alert); + const alertState: IRushAlertStateEntry = alertsState[alert.alertId]; + if ( + isAlertValid && + !this._isSnoozing(alertState) && + (!alertState.lastDisplayTime || + Number(new Date()) - Number(new Date(alertState.lastDisplayTime)) > + RushAlerts.alertDisplayIntervalDurations.get( + alert.maximumDisplayInterval ?? AlertDisplayInterval.ALWAYS + )!) + ) { + return alert; + } + }) + ) + ).filter((alert) => alert !== undefined) as Array; + + const alertsSortedByPriority: IRushAlertsConfigEntry[] = needDisplayAlerts.sort((a, b) => { + return ( + RushAlerts.ALERT_PRIORITY.indexOf(a.priority ?? AlertPriority.NORMAL) - + RushAlerts.ALERT_PRIORITY.indexOf(b.priority ?? AlertPriority.NORMAL) + ); + }); + return alertsSortedByPriority[0]; + } + + private _isSnoozing(alertState: IRushAlertStateEntry): boolean { + return ( + Boolean(alertState.snooze) && + (!alertState.snoozeEndTime || Number(new Date()) < Number(new Date(alertState.snoozeEndTime))) + ); + } + + private async _isAlertValidAsync(alert: IRushAlertsConfigEntry): Promise { + const timeNow: Date = new Date(); + + if (alert.startTime) { + const startTime: Date = _parseDate(alert.startTime); + if (timeNow < startTime) { + return false; + } + } + + if (alert.endTime) { + const endTime: Date = _parseDate(alert.endTime); + if (timeNow > endTime) { + return false; + } + } + + const conditionScript: string | undefined = alert.conditionScript; + if (conditionScript) { + // "(OPTIONAL) The filename of a script that determines whether this alert can be shown, + // found in the "common/config/rush/alert-scripts" folder." ... "To ensure up-to-date alerts, Rush + // may fetch and checkout the "common/config/rush-alerts" folder in an unpredictable temporary + // path. Therefore, your script should avoid importing dependencies from outside its folder, + // generally be kept as simple and reliable and quick as possible." + if (conditionScript.indexOf('/') >= 0 || conditionScript.indexOf('\\') >= 0) { + throw new Error( + `The rush-alerts.json file contains a "conditionScript" that is not inside the "alert-scripts" folder: ` + + JSON.stringify(conditionScript) + ); + } + const conditionScriptPath: string = `${this._rushJsonFolder}/common/config/rush/alert-scripts/${conditionScript}`; + if (!(await FileSystem.existsAsync(conditionScriptPath))) { + throw new Error( + 'The "conditionScript" field in rush-alerts.json refers to a nonexistent file:\n' + + conditionScriptPath + ); + } + + this._terminal.writeDebugLine(`Invoking condition script "${conditionScript}" from rush-alerts.json`); + const startTimemark: number = performance.now(); + + interface IAlertsConditionScriptModule { + canShowAlert(): boolean; + } + + let conditionScriptModule: IAlertsConditionScriptModule; + try { + conditionScriptModule = require(conditionScriptPath); + + if (typeof conditionScriptModule.canShowAlert !== 'function') { + throw new Error('The "canShowAlert" module export is missing'); + } + } catch (e) { + throw new Error( + `Error loading condition script "${conditionScript}" from rush-alerts.json:\n${e.stack}` + ); + } + + const oldCwd: string = process.cwd(); + + let conditionResult: boolean; + try { + // "Rush will invoke this script with the working directory set to the monorepo root folder, + // with no guarantee that `rush install` has been run." + process.chdir(this._rushJsonFolder); + conditionResult = conditionScriptModule.canShowAlert(); + + if (typeof conditionResult !== 'boolean') { + throw new Error('canShowAlert() did not return a boolean value'); + } + } catch (e) { + throw new Error( + `Error invoking condition script "${conditionScript}" from rush-alerts.json:\n${e.stack}` + ); + } finally { + process.chdir(oldCwd); + } + + const totalMs: number = performance.now() - startTimemark; + this._terminal.writeDebugLine( + `Invoked conditionScript "${conditionScript}"` + + ` in ${Math.round(totalMs)} ms with result "${conditionResult}"` + ); + + if (!conditionResult) { + return false; + } + } + return true; + } + + private _printMessageInBoxStyle(alert: IRushAlertsConfigEntry): void { + const boxTitle: string = alert.title.toUpperCase(); + + const boxMessage: string = typeof alert.message === 'string' ? alert.message : alert.message.join(''); + + const boxDetails: string = alert.detailsUrl ? 'Details: ' + alert.detailsUrl : ''; + + // ...minus the padding. + const PADDING: number = '╔══╗'.length; + + // Try to make it wide enough to fit the (unwrapped) strings... + let lineLength: number = Math.max(boxTitle.length, boxMessage.length, boxDetails.length); + + // ...but don't exceed the console width, and also keep it under 80... + lineLength = Math.min(lineLength, (PrintUtilities.getConsoleWidth() ?? 80) - PADDING, 80 - PADDING); + + // ...and the width needs to be at least 40 characters... + lineLength = Math.max(lineLength, 40 - PADDING); + + const lines: string[] = [ + ...PrintUtilities.wrapWordsToLines(boxTitle, lineLength).map((x) => + Colorize.bold(x.padEnd(lineLength)) + ), + '', + ...PrintUtilities.wrapWordsToLines(boxMessage, lineLength).map((x) => x.padEnd(lineLength)) + ]; + if (boxDetails) { + lines.push( + '', + ...PrintUtilities.wrapWordsToLines(boxDetails, lineLength).map((x) => + Colorize.cyan(x.padEnd(lineLength)) + ) + ); + } + + // Print the box + this._terminal.writeLine('╔═' + '═'.repeat(lineLength) + '═╗'); + for (const line of lines) { + this._terminal.writeLine(`║ ${line.padEnd(lineLength)} ║`); + } + this._terminal.writeLine('╚═' + '═'.repeat(lineLength) + '═╝'); + this._terminal.writeLine(`To stop seeing this alert, run "rush alert --snooze ${alert.alertId}"`); + } + + private async _writeRushAlertStateAsync(): Promise { + await JsonFile.saveAsync(this._rushAlertsState, this.rushAlertsStateFilePath, { + ignoreUndefinedValues: true, + headerComment: '// THIS FILE IS MACHINE-GENERATED -- DO NOT MODIFY', + jsonSyntax: JsonSyntax.JsonWithComments + }); + } +} + +function _parseDate(dateString: string): Date { + const parsedDate: Date = new Date(dateString); + if (isNaN(parsedDate.getTime())) { + throw new Error(`Invalid date/time value ${JSON.stringify(dateString)}`); + } + return parsedDate; +} diff --git a/libraries/rush-lib/src/utilities/SetRushLibPath.ts b/libraries/rush-lib/src/utilities/SetRushLibPath.ts index 236a8b89f25..ffc527ff22e 100644 --- a/libraries/rush-lib/src/utilities/SetRushLibPath.ts +++ b/libraries/rush-lib/src/utilities/SetRushLibPath.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + import { PackageJsonLookup } from '@rushstack/node-core-library'; import { EnvironmentVariableNames } from '../api/EnvironmentConfiguration'; @@ -6,5 +9,5 @@ const rootDir: string | undefined = PackageJsonLookup.instance.tryGetPackageFold if (rootDir) { // Route to the 'main' field of package.json const rushLibIndex: string = require.resolve(rootDir, { paths: [] }); - process.env[EnvironmentVariableNames.RUSH_LIB_PATH] = rushLibIndex; + process.env[EnvironmentVariableNames._RUSH_LIB_PATH] = rushLibIndex; } diff --git a/libraries/rush-lib/src/utilities/Stopwatch.ts b/libraries/rush-lib/src/utilities/Stopwatch.ts index 8495915a0e1..e137f27e140 100644 --- a/libraries/rush-lib/src/utilities/Stopwatch.ts +++ b/libraries/rush-lib/src/utilities/Stopwatch.ts @@ -52,11 +52,19 @@ export class Stopwatch implements IStopwatchResult { this._state = StopwatchState.Stopped; } + public static fromState({ startTime, endTime }: { startTime: number; endTime: number }): Stopwatch { + const stopwatch: Stopwatch = new Stopwatch(); + stopwatch._startTime = startTime; + stopwatch._endTime = endTime; + stopwatch._state = StopwatchState.Stopped; + return stopwatch; + } + /** * Static helper function which creates a stopwatch which is immediately started */ - public static start(): Stopwatch { - return new Stopwatch().start(); + public static start(startTimeOverride?: number): Stopwatch { + return new Stopwatch().start(startTimeOverride); } public get state(): StopwatchState { @@ -67,11 +75,11 @@ export class Stopwatch implements IStopwatchResult { * Starts the stopwatch. Note that if end() has been called, * reset() should be called before calling start() again. */ - public start(): Stopwatch { + public start(startTimeOverride?: number): Stopwatch { if (this._startTime !== undefined) { throw new Error('Call reset() before starting the Stopwatch'); } - this._startTime = this._getTime(); + this._startTime = startTimeOverride ?? this._getTime(); this._endTime = undefined; this._state = StopwatchState.Started; return this; diff --git a/libraries/rush-lib/src/utilities/TarExecutable.ts b/libraries/rush-lib/src/utilities/TarExecutable.ts index 308cc09bebf..6ea2e0f4d61 100644 --- a/libraries/rush-lib/src/utilities/TarExecutable.ts +++ b/libraries/rush-lib/src/utilities/TarExecutable.ts @@ -1,14 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; -import os from 'os'; -import { Executable, FileSystem, FileWriter, ITerminal } from '@rushstack/node-core-library'; -import { ChildProcess } from 'child_process'; -import events from 'events'; +import * as path from 'node:path'; +import type { ChildProcess } from 'node:child_process'; +import events from 'node:events'; -import { RushConfigurationProject } from '../api/RushConfigurationProject'; +import { Executable, FileSystem, FileWriter } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import type { RushConfigurationProject } from '../api/RushConfigurationProject'; import { EnvironmentConfiguration } from '../api/EnvironmentConfiguration'; +import { IS_WINDOWS } from './executionUtilities'; export interface ITarOptionsBase { logFilePath: string; @@ -35,13 +37,14 @@ export class TarExecutable { public static async tryInitializeAsync(terminal: ITerminal): Promise { terminal.writeVerboseLine('Trying to find "tar" binary'); const tarExecutablePath: string | undefined = - EnvironmentConfiguration.tarBinaryPath || (await TarExecutable._tryFindTarExecutablePathAsync()); + EnvironmentConfiguration.tarBinaryPath || (await _tryFindTarExecutablePathAsync()); if (!tarExecutablePath) { terminal.writeVerboseLine('"tar" was not found on the PATH'); return undefined; + } else { + terminal.writeVerboseLine(`Using "tar" binary: ${tarExecutablePath}`); + return new TarExecutable(tarExecutablePath); } - - return new TarExecutable(tarExecutablePath); } /** @@ -170,22 +173,22 @@ export class TarExecutable { return tarExitCode; } +} - private static async _tryFindTarExecutablePathAsync(): Promise { - if (os.platform() === 'win32') { - // If we're running on Windows, first try to use the OOB tar executable. If - // we're running in the Git Bash, the tar executable on the PATH doesn't handle - // Windows file paths correctly. - // eslint-disable-next-line dot-notation - const windowsFolderPath: string | undefined = process.env['WINDIR']; - if (windowsFolderPath) { - const defaultWindowsTarExecutablePath: string = `${windowsFolderPath}\\system32\\tar.exe`; - if (await FileSystem.existsAsync(defaultWindowsTarExecutablePath)) { - return defaultWindowsTarExecutablePath; - } +async function _tryFindTarExecutablePathAsync(): Promise { + if (IS_WINDOWS) { + // If we're running on Windows, first try to use the OOB tar executable. If + // we're running in the Git Bash, the tar executable on the PATH doesn't handle + // Windows file paths correctly. + // eslint-disable-next-line dot-notation + const windowsFolderPath: string | undefined = process.env['WINDIR']; + if (windowsFolderPath) { + const defaultWindowsTarExecutablePath: string = `${windowsFolderPath}\\system32\\tar.exe`; + if (await FileSystem.existsAsync(defaultWindowsTarExecutablePath)) { + return defaultWindowsTarExecutablePath; } } - - return Executable.tryResolve('tar'); } + + return Executable.tryResolve('tar'); } diff --git a/libraries/rush-lib/src/utilities/Utilities.ts b/libraries/rush-lib/src/utilities/Utilities.ts index d821e6a0644..1b4ca37cc41 100644 --- a/libraries/rush-lib/src/utilities/Utilities.ts +++ b/libraries/rush-lib/src/utilities/Utilities.ts @@ -1,23 +1,32 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as child_process from 'child_process'; -import * as os from 'os'; -import * as path from 'path'; -import { performance } from 'perf_hooks'; +import * as child_process from 'node:child_process'; +import * as path from 'node:path'; +import { performance } from 'node:perf_hooks'; +import { Transform } from 'node:stream'; + import { JsonFile, - IPackageJson, + type IPackageJson, FileSystem, FileConstants, - FileSystemStats + type FileSystemStats, + SubprocessTerminator, + Executable, + type IWaitForExitResult, + Async, + type IWaitForExitResultWithoutOutput } from '@rushstack/node-core-library'; -import type * as stream from 'stream'; -import { RushConfiguration } from '../api/RushConfiguration'; +import type { RushConfiguration } from '../api/RushConfiguration'; import { syncNpmrc } from './npmrcUtilities'; +import { EnvironmentVariableNames } from '../api/EnvironmentConfiguration'; +import { RushConstants } from '../logic/RushConstants'; +import { escapeArgumentIfNeeded, IS_WINDOWS } from './executionUtilities'; export type UNINITIALIZED = 'UNINITIALIZED'; +// eslint-disable-next-line @typescript-eslint/no-redeclare export const UNINITIALIZED: UNINITIALIZED = 'UNINITIALIZED'; export interface IEnvironment { @@ -29,7 +38,7 @@ export interface IEnvironment { } /** - * Options for Utilities.executeCommand(). + * Options for {@link Utilities.executeCommandAsync}. */ export interface IExecuteCommandOptions { command: string; @@ -38,10 +47,15 @@ export interface IExecuteCommandOptions { environment?: IEnvironment; suppressOutput?: boolean; keepEnvironment?: boolean; + /** + * Note that this takes precedence over {@link IExecuteCommandOptions.suppressOutput} + */ + onStdoutStreamChunk?: (chunk: string) => string | void; + captureExitCodeAndSignal?: boolean; } /** - * Options for Utilities.installPackageInDirectory(). + * Options for {@link Utilities.installPackageInDirectoryAsync}. */ export interface IInstallPackageInDirectoryOptions { directory: string; @@ -51,6 +65,12 @@ export interface IInstallPackageInDirectoryOptions { maxInstallAttempts: number; commonRushConfigFolder: string | undefined; suppressOutput?: boolean; + /** + * Whether to filter npm-incompatible properties from .npmrc. + * This should be true when the .npmrc is configured for a different package manager (pnpm/yarn) + * but npm is being used to install packages. + */ + filterNpmIncompatibleProperties?: boolean; } export interface ILifecycleCommandOptions { @@ -74,10 +94,25 @@ export interface ILifecycleCommandOptions { */ handleOutput: boolean; + /** + * an existing environment to copy instead of process.env + */ + initialEnvironment?: IEnvironment; + /** * Options for what should be added to the PATH variable */ environmentPathOptions: IEnvironmentPathOptions; + + /** + * If true, attempt to establish a NodeJS IPC channel to the child process. + */ + ipc?: boolean; + + /** + * If true, wire up SubprocessTerminator to the child process. + */ + connectSubprocessTerminator?: boolean; } export interface IEnvironmentPathOptions { @@ -102,6 +137,11 @@ export interface IDisposable { dispose(): void; } +export type IExecuteCommandAndCaptureOutputOptions = Omit< + IExecuteCommandOptions, + 'suppressOutput' | 'onStdoutStreamChunk' +>; + interface ICreateEnvironmentForRushCommandPathOptions extends IEnvironmentPathOptions { rushJsonFolder: string | undefined; projectRoot: string | undefined; @@ -125,27 +165,26 @@ interface ICreateEnvironmentForRushCommandOptions { pathOptions?: ICreateEnvironmentForRushCommandPathOptions; } -export class Utilities { - public static syncNpmrc: typeof syncNpmrc = syncNpmrc; +type OptionalKeys = { + [K in keyof T]-?: {} extends Pick ? K : never; +}[keyof T]; - /** - * Get the user's home directory. On windows this looks something like "C:\users\username\" and on UNIX - * this looks something like "/home/username/" - */ - public static getHomeFolder(): string { - const unresolvedUserFolder: string | undefined = - process.env[process.platform === 'win32' ? 'USERPROFILE' : 'HOME']; - const dirError: string = "Unable to determine the current user's home directory"; - if (unresolvedUserFolder === undefined) { - throw new Error(dirError); - } - const homeFolder: string = path.resolve(unresolvedUserFolder); - if (!FileSystem.exists(homeFolder)) { - throw new Error(dirError); - } +export type OptionalToUndefined = Omit> & { + [K in OptionalKeys]-?: Exclude | undefined; +}; - return homeFolder; - } +type IExecuteCommandInternalOptions = Omit & { + stdio: child_process.SpawnSyncOptions['stdio']; + captureOutput: boolean; +}; + +export interface ICommandAndArgs { + command: string; + args: string[]; +} + +export class Utilities { + public static syncNpmrc: typeof syncNpmrc = syncNpmrc; /** * Node.js equivalent of performance.now(). @@ -186,6 +225,7 @@ export class Utilities { const totalSeconds: string = ((currentTime - startTime) / 1000.0).toFixed(2); // This logging statement isn't meaningful to the end-user. `fnName` should be updated // to something like `operationDescription` + // eslint-disable-next-line no-console console.log(`${fnName}() stalled for ${totalSeconds} seconds`); } @@ -263,67 +303,122 @@ export class Utilities { * NOTE: The filenames can also be paths for directories, in which case the directory * timestamp is compared. */ - public static isFileTimestampCurrent(dateToCompare: Date, inputFilenames: string[]): boolean { - for (const inputFilename of inputFilenames) { - if (!FileSystem.exists(inputFilename)) { - return false; - } + public static async isFileTimestampCurrentAsync( + dateToCompare: Date, + inputFilePaths: string[] + ): Promise { + let anyAreOutOfDate: boolean = false; + await Async.forEachAsync( + inputFilePaths, + async (filePath) => { + if (!anyAreOutOfDate) { + let inputStats: FileSystemStats | undefined; + try { + inputStats = await FileSystem.getStatisticsAsync(filePath); + } catch (e) { + if (FileSystem.isNotExistError(e)) { + // eslint-disable-next-line require-atomic-updates + anyAreOutOfDate = true; + } else { + throw e; + } + } - const inputStats: FileSystemStats = FileSystem.getStatistics(inputFilename); - if (dateToCompare < inputStats.mtime) { - return false; - } - } + if (inputStats && dateToCompare < inputStats.mtime) { + // eslint-disable-next-line require-atomic-updates + anyAreOutOfDate = true; + } + } + }, + { concurrency: 10 } + ); - return true; + return !anyAreOutOfDate; } + public static async executeCommandAsync( + options: IExecuteCommandOptions & { captureExitCodeAndSignal: true } + ): Promise>; + public static async executeCommandAsync(options: IExecuteCommandOptions): Promise; /** * Executes the command with the specified command-line parameters, and waits for it to complete. * The current directory will be set to the specified workingDirectory. */ - public static executeCommand(options: IExecuteCommandOptions): void { - Utilities._executeCommandInternal( - options.command, - options.args, - options.workingDirectory, - options.suppressOutput ? undefined : [0, 1, 2], - options.environment, - options.keepEnvironment - ); + public static async executeCommandAsync( + options: IExecuteCommandOptions + ): Promise { + const { + command, + args, + workingDirectory, + suppressOutput, + onStdoutStreamChunk, + environment, + keepEnvironment, + captureExitCodeAndSignal + } = options; + const { exitCode, signal } = await _executeCommandInternalAsync({ + command, + args, + workingDirectory, + stdio: onStdoutStreamChunk + ? // Inherit the stdin and stderr streams, but pipe the stdout stream, which will then be piped + // to the process's stdout after being intercepted by the onStdoutStreamChunk callback. + ['inherit', 'pipe', 'inherit'] + : suppressOutput + ? // If the output is being suppressed, create pipes for all streams to prevent the child process + // from printing to the parent process's (this process's) stdout/stderr, but allow the stdout and + // stderr to be inspected if an error occurs. + // TODO: Consider ignoring stdout and stdin and only piping stderr for inspection on error. + ['pipe', 'pipe', 'pipe'] + : // If the output is not being suppressed or intercepted, inherit all streams from the parent process. + ['inherit', 'inherit', 'inherit'], + environment, + keepEnvironment, + onStdoutStreamChunk, + captureOutput: false, + captureExitCodeAndSignal + }); + + if (captureExitCodeAndSignal) { + return { exitCode, signal }; + } } /** * Executes the command with the specified command-line parameters, and waits for it to complete. * The current directory will be set to the specified workingDirectory. */ - public static executeCommandAndCaptureOutput( - command: string, - args: string[], - workingDirectory: string, - environment?: IEnvironment, - keepEnvironment: boolean = false - ): string { - const result: child_process.SpawnSyncReturns = Utilities._executeCommandInternal( - command, - args, - workingDirectory, - ['pipe', 'pipe', 'pipe'], - environment, - keepEnvironment - ); + public static async executeCommandAndCaptureOutputAsync( + options: IExecuteCommandAndCaptureOutputOptions & { captureExitCodeAndSignal?: false } + ): Promise; + public static async executeCommandAndCaptureOutputAsync( + options: IExecuteCommandAndCaptureOutputOptions & { captureExitCodeAndSignal: true } + ): Promise>; + public static async executeCommandAndCaptureOutputAsync( + options: IExecuteCommandAndCaptureOutputOptions + ): Promise> { + const result: IWaitForExitResult = await _executeCommandInternalAsync({ + ...options, + stdio: ['pipe', 'pipe', 'pipe'], + captureOutput: true + }); - return result.stdout.toString(); + if (options.captureExitCodeAndSignal) { + return result; + } else { + return result.stdout; + } } /** * Attempts to run Utilities.executeCommand() up to maxAttempts times before giving up. */ - public static executeCommandWithRetry( + public static async executeCommandWithRetryAsync( options: IExecuteCommandOptions, maxAttempts: number, retryCallback?: () => void - ): void { + ): Promise { if (maxAttempts < 1) { throw new Error('The maxAttempts parameter cannot be less than 1'); } @@ -332,14 +427,19 @@ export class Utilities { for (;;) { try { - Utilities.executeCommand(options); + await Utilities.executeCommandAsync(options); } catch (error) { + // eslint-disable-next-line no-console console.log('\nThe command failed:'); - console.log(` ${options.command} ` + options.args.join(' ')); + const { command, args } = options; + // eslint-disable-next-line no-console + console.log(` ${command} ` + args.join(' ')); + // eslint-disable-next-line no-console console.log(`ERROR: ${(error as Error).toString()}`); if (attemptNumber < maxAttempts) { ++attemptNumber; + // eslint-disable-next-line no-console console.log(`Trying again (attempt #${attemptNumber})...\n`); if (retryCallback) { retryCallback(); @@ -347,6 +447,7 @@ export class Utilities { continue; } else { + // eslint-disable-next-line no-console console.error(`Giving up after ${attemptNumber} attempts\n`); throw error; } @@ -362,11 +463,18 @@ export class Utilities { * @param options - options for how the command should be run */ public static executeLifecycleCommand(command: string, options: ILifecycleCommandOptions): number { - const result: child_process.SpawnSyncReturns = - Utilities._executeLifecycleCommandInternal(command, child_process.spawnSync, options); + const result: child_process.SpawnSyncReturns = _executeLifecycleCommandInternal( + command, + child_process.spawnSync, + options + ); if (options.handleOutput) { - Utilities._processResult(result); + _processResult({ + error: result.error, + status: result.status, + stderr: result.stderr.toString() + }); } if (result.status !== null) { @@ -385,59 +493,74 @@ export class Utilities { command: string, options: ILifecycleCommandOptions ): child_process.ChildProcess { - return Utilities._executeLifecycleCommandInternal(command, child_process.spawn, options); - } - - /** - * For strings passed to a shell command, this adds appropriate escaping - * to avoid misinterpretation of spaces or special characters. - * - * Example: 'hello there' --> '"hello there"' - */ - public static escapeShellParameter(parameter: string): string { - // This approach is based on what NPM 7 now does: - // https://github.com/npm/run-script/blob/47a4d539fb07220e7215cc0e482683b76407ef9b/lib/run-script-pkg.js#L34 - return JSON.stringify(parameter); + const child: child_process.ChildProcess = _executeLifecycleCommandInternal( + command, + child_process.spawn, + options + ); + if (options.connectSubprocessTerminator) { + SubprocessTerminator.killProcessTreeOnExit(child, SubprocessTerminator.RECOMMENDED_OPTIONS); + } + return child; } /** * Installs a package by name and version in the specified directory. */ - public static installPackageInDirectory(options: IInstallPackageInDirectoryOptions): void { - const directory: string = path.resolve(options.directory); - if (FileSystem.exists(directory)) { + public static async installPackageInDirectoryAsync({ + packageName, + version, + tempPackageTitle, + commonRushConfigFolder, + maxInstallAttempts, + suppressOutput, + directory, + filterNpmIncompatibleProperties = false + }: IInstallPackageInDirectoryOptions): Promise { + directory = path.resolve(directory); + const directoryExists: boolean = await FileSystem.existsAsync(directory); + if (directoryExists) { + // eslint-disable-next-line no-console console.log('Deleting old files from ' + directory); } - FileSystem.ensureEmptyFolder(directory); + await FileSystem.ensureEmptyFolderAsync(directory); const npmPackageJson: IPackageJson = { dependencies: { - [options.packageName]: options.version + [packageName]: version }, description: 'Temporary file generated by the Rush tool', - name: options.tempPackageTitle, + name: tempPackageTitle, private: true, version: '0.0.0' }; - JsonFile.save(npmPackageJson, path.join(directory, FileConstants.PackageJson)); - - if (options.commonRushConfigFolder) { - Utilities.syncNpmrc(options.commonRushConfigFolder, directory); + await JsonFile.saveAsync(npmPackageJson, path.join(directory, FileConstants.PackageJson)); + + if (commonRushConfigFolder) { + Utilities.syncNpmrc({ + sourceNpmrcFolder: commonRushConfigFolder, + targetNpmrcFolder: directory, + supportEnvVarFallbackSyntax: false, + // Filter out npm-incompatible properties only when the .npmrc is configured for + // a different package manager (pnpm/yarn) but npm is being used to install. + filterNpmIncompatibleProperties + }); } + // eslint-disable-next-line no-console console.log('\nRunning "npm install" in ' + directory); // NOTE: Here we use whatever version of NPM we happen to find in the PATH - Utilities.executeCommandWithRetry( + await Utilities.executeCommandWithRetryAsync( { command: 'npm', args: ['install'], workingDirectory: directory, - environment: Utilities._createEnvironmentForRushCommand({}), - suppressOutput: options.suppressOutput + environment: _createEnvironmentForRushCommand({}), + suppressOutput }, - options.maxInstallAttempts + maxInstallAttempts ); } @@ -447,12 +570,15 @@ export class Utilities { */ public static syncFile(sourcePath: string, destinationPath: string): void { if (FileSystem.exists(sourcePath)) { + // eslint-disable-next-line no-console console.log(`Copying "${sourcePath}"`); + // eslint-disable-next-line no-console console.log(` --> "${destinationPath}"`); FileSystem.copyFile({ sourcePath, destinationPath }); } else { if (FileSystem.exists(destinationPath)) { // If the source file doesn't exist and there is one in the target, delete the one in the target + // eslint-disable-next-line no-console console.log(`Deleting ${destinationPath}`); FileSystem.deleteFile(destinationPath); } @@ -460,7 +586,7 @@ export class Utilities { } public static getRushConfigNotFoundError(): Error { - return new Error('Unable to find rush.json configuration file'); + return new Error(`Unable to find ${RushConstants.rushJsonFilename} configuration file`); } public static async usingAsync( @@ -476,218 +602,310 @@ export class Utilities { } } - private static _executeLifecycleCommandInternal( - command: string, - spawnFunction: ( - command: string, - args: string[], - spawnOptions: child_process.SpawnOptions - ) => TCommandResult, - options: ILifecycleCommandOptions - ): TCommandResult { - let shellCommand: string = process.env.comspec || 'cmd'; - let commandFlags: string = '/d /s /c'; - let useShell: boolean = true; - if (process.platform !== 'win32') { + public static trimAfterLastSlash(filePath: string): string { + const indexOfLastSlash: number = Math.max(filePath.lastIndexOf('/'), filePath.lastIndexOf('\\')); + if (indexOfLastSlash < 0) { + return filePath; + } + return filePath.substring(0, indexOfLastSlash); + } + + /** + * If the path refers to a symlink, `FileSystem.exists()` would normally test whether the symlink + * points to a target that exists. By contrast, `existsOrIsBrokenSymlink()` will return true even if + * the symlink exists but its target does not. */ + public static existsOrIsSymlink(linkPath: string): boolean { + try { + FileSystem.getLinkStatistics(linkPath); + return true; + } catch (err) { + return false; + } + } + + /** @internal */ + public static _convertCommandAndArgsToShell(command: string, isWindows?: boolean): ICommandAndArgs; + public static _convertCommandAndArgsToShell(options: ICommandAndArgs, isWindows?: boolean): ICommandAndArgs; + public static _convertCommandAndArgsToShell( + options: ICommandAndArgs | string, + isWindows: boolean = IS_WINDOWS + ): ICommandAndArgs { + let shellCommand: string; + let commandFlags: string[]; + if (isWindows) { + shellCommand = process.env.comspec || 'cmd'; + commandFlags = ['/d', '/s', '/c']; + } else { shellCommand = 'sh'; - commandFlags = '-c'; - useShell = false; + commandFlags = ['-c']; } - const environment: IEnvironment = Utilities._createEnvironmentForRushCommand({ - initCwd: options.initCwd, - pathOptions: { - ...options.environmentPathOptions, - rushJsonFolder: options.rushConfiguration?.rushJsonFolder, - projectRoot: options.workingDirectory, - commonTempFolder: options.rushConfiguration ? options.rushConfiguration.commonTempFolder : undefined + let commandToRun: string; + if (typeof options === 'string') { + commandToRun = options; + } else { + const { command, args } = options; + const normalizedCommand: string = escapeArgumentIfNeeded(command, isWindows); + const normalizedArgs: string[] = []; + for (const arg of args) { + normalizedArgs.push(escapeArgumentIfNeeded(arg, isWindows)); } - }); - return spawnFunction(shellCommand, [commandFlags, command], { - cwd: options.workingDirectory, - shell: useShell, - env: environment, - stdio: options.handleOutput ? ['pipe', 'pipe', 'pipe'] : [0, 1, 2] - }); + commandToRun = [normalizedCommand, ...normalizedArgs].join(' '); + } + + return { + command: shellCommand, + args: [...commandFlags, commandToRun] + }; } +} - /** - * Returns a process.env environment suitable for executing lifecycle scripts. - * @param initialEnvironment - an existing environment to copy instead of process.env - * - * @remarks - * Rush._assignRushInvokedFolder() assigns the `RUSH_INVOKED_FOLDER` variable globally - * via the parent process's environment. - */ - private static _createEnvironmentForRushCommand( - options: ICreateEnvironmentForRushCommandOptions - ): IEnvironment { - if (options.initialEnvironment === undefined) { - options.initialEnvironment = process.env; +function _executeLifecycleCommandInternal( + commandAndArgs: string, + spawnFunction: ( + command: string, + args: string[], + spawnOptions: child_process.SpawnOptions + ) => TCommandResult, + options: ILifecycleCommandOptions +): TCommandResult { + const { + initCwd, + initialEnvironment, + environmentPathOptions, + rushConfiguration, + workingDirectory, + handleOutput, + ipc, + connectSubprocessTerminator + } = options; + const environment: IEnvironment = _createEnvironmentForRushCommand({ + initCwd, + initialEnvironment, + pathOptions: { + ...environmentPathOptions, + rushJsonFolder: rushConfiguration?.rushJsonFolder, + projectRoot: workingDirectory, + commonTempFolder: rushConfiguration ? rushConfiguration.commonTempFolder : undefined } + }); - // Set some defaults for the environment - const environment: IEnvironment = {}; - if (options.pathOptions?.rushJsonFolder) { - environment.RUSHSTACK_FILE_ERROR_BASE_FOLDER = options.pathOptions.rushJsonFolder; - } + const stdio: child_process.StdioOptions = handleOutput ? ['ignore', 'pipe', 'pipe'] : [0, 1, 2]; + if (ipc) { + stdio.push('ipc'); + } - for (const key of Object.getOwnPropertyNames(options.initialEnvironment)) { - const normalizedKey: string = os.platform() === 'win32' ? key.toUpperCase() : key; + const spawnOptions: child_process.SpawnOptions = { + cwd: workingDirectory, + env: environment, + stdio + }; - // If Rush itself was invoked inside a lifecycle script, this may be set and would interfere - // with Rush's installations. If we actually want it, we will set it explicitly below. - if (normalizedKey === 'INIT_CWD') { - continue; - } + if (connectSubprocessTerminator) { + Object.assign(spawnOptions, SubprocessTerminator.RECOMMENDED_OPTIONS); + } - // When NPM invokes a lifecycle event, it copies its entire configuration into environment - // variables. Rush is supposed to be a deterministic controlled environment, so don't bring - // this along. - // - // NOTE: Longer term we should clean out the entire environment and use rush.json to bring - // back specific environment variables that the repo maintainer has determined to be safe. - if (normalizedKey.match(/^NPM_CONFIG_/)) { - continue; - } + const { command, args } = Utilities._convertCommandAndArgsToShell(commandAndArgs); - // Use the uppercased environment variable name on Windows because environment variable names - // are case-insensitive on Windows - environment[normalizedKey] = options.initialEnvironment[key]; + if (IS_WINDOWS) { + const shellCommand: string = [command, ...args].join(' '); + return spawnFunction(shellCommand, [], { ...spawnOptions, shell: true }); + } else { + return spawnFunction(command, args, spawnOptions); + } +} + +/** + * Returns a process.env environment suitable for executing lifecycle scripts. + * @param initialEnvironment - an existing environment to copy instead of process.env + * + * @remarks + * Rush._assignRushInvokedFolder() assigns the `RUSH_INVOKED_FOLDER` variable globally + * via the parent process's environment. + */ +function _createEnvironmentForRushCommand(options: ICreateEnvironmentForRushCommandOptions): IEnvironment { + if (options.initialEnvironment === undefined) { + options.initialEnvironment = process.env; + } + + // Set some defaults for the environment + const environment: IEnvironment = {}; + if (options.pathOptions?.rushJsonFolder) { + environment.RUSHSTACK_FILE_ERROR_BASE_FOLDER = options.pathOptions.rushJsonFolder; + } + + for (const key of Object.getOwnPropertyNames(options.initialEnvironment)) { + const normalizedKey: string = IS_WINDOWS ? key.toUpperCase() : key; + + // If Rush itself was invoked inside a lifecycle script, this may be set and would interfere + // with Rush's installations. If we actually want it, we will set it explicitly below. + if (normalizedKey === 'INIT_CWD') { + continue; } - // When NPM invokes a lifecycle script, it sets an environment variable INIT_CWD that remembers - // the directory that NPM started in. This allows naive scripts to change their current working directory - // and invoke NPM operations, while still be able to find a local .npmrc file. Although Rush recommends - // for toolchain scripts to be professionally written (versus brittle stuff like - // "cd ./lib && npm run tsc && cd .."), we support INIT_CWD for compatibility. + // When NPM invokes a lifecycle event, it copies its entire configuration into environment + // variables. Rush is supposed to be a deterministic controlled environment, so don't bring + // this along. // - // More about this feature: https://github.com/npm/npm/pull/12356 - if (options.initCwd) { - environment['INIT_CWD'] = options.initCwd; // eslint-disable-line dot-notation + // NOTE: Longer term we should clean out the entire environment and use rush.json to bring + // back specific environment variables that the repo maintainer has determined to be safe. + if (normalizedKey.match(/^NPM_CONFIG_/)) { + continue; } - if (options.pathOptions) { - if (options.pathOptions.includeRepoBin && options.pathOptions.commonTempFolder) { - environment.PATH = Utilities._prependNodeModulesBinToPath( - environment.PATH, - options.pathOptions.commonTempFolder - ); - } + // Use the uppercased environment variable name on Windows because environment variable names + // are case-insensitive on Windows + environment[normalizedKey] = options.initialEnvironment[key]; + } - if (options.pathOptions.includeProjectBin && options.pathOptions.projectRoot) { - environment.PATH = Utilities._prependNodeModulesBinToPath( - environment.PATH, - options.pathOptions.projectRoot - ); - } + // When NPM invokes a lifecycle script, it sets an environment variable INIT_CWD that remembers + // the directory that NPM started in. This allows naive scripts to change their current working directory + // and invoke NPM operations, while still be able to find a local .npmrc file. Although Rush recommends + // for toolchain scripts to be professionally written (versus brittle stuff like + // "cd ./lib && npm run tsc && cd .."), we support INIT_CWD for compatibility. + // + // More about this feature: https://github.com/npm/npm/pull/12356 + if (options.initCwd) { + environment['INIT_CWD'] = options.initCwd; // eslint-disable-line dot-notation + } - if (options.pathOptions.additionalPathFolders) { - environment.PATH = [...options.pathOptions.additionalPathFolders, environment.PATH].join( - path.delimiter - ); - } + if (options.pathOptions) { + if (options.pathOptions.includeRepoBin && options.pathOptions.commonTempFolder) { + environment.PATH = _prependNodeModulesBinToPath(environment.PATH, options.pathOptions.commonTempFolder); } - return environment; - } + if (options.pathOptions.includeProjectBin && options.pathOptions.projectRoot) { + environment.PATH = _prependNodeModulesBinToPath(environment.PATH, options.pathOptions.projectRoot); + } - /** - * Prepend the node_modules/.bin folder under the specified folder to the specified PATH variable. For example, - * if `rootDirectory` is "/foobar" and `existingPath` is "/bin", this function will return - * "/foobar/node_modules/.bin:/bin" - */ - private static _prependNodeModulesBinToPath( - existingPath: string | undefined, - rootDirectory: string - ): string { - const binPath: string = path.resolve(rootDirectory, 'node_modules', '.bin'); - if (existingPath) { - return `${binPath}${path.delimiter}${existingPath}`; - } else { - return binPath; + if (options.pathOptions.additionalPathFolders) { + environment.PATH = [...options.pathOptions.additionalPathFolders, environment.PATH].join( + path.delimiter + ); } } - /** - * Executes the command with the specified command-line parameters, and waits for it to complete. - * The current directory will be set to the specified workingDirectory. - */ - private static _executeCommandInternal( - command: string, - args: string[], - workingDirectory: string, - stdio: - | 'pipe' - | 'ignore' - | 'inherit' - | (number | 'pipe' | 'ignore' | 'inherit' | 'ipc' | stream.Stream | null | undefined)[] - | undefined, - environment?: IEnvironment, - keepEnvironment: boolean = false - ): child_process.SpawnSyncReturns { - const options: child_process.SpawnSyncOptions = { - cwd: workingDirectory, - shell: true, - stdio: stdio, - env: keepEnvironment - ? environment - : Utilities._createEnvironmentForRushCommand({ initialEnvironment: environment }), - maxBuffer: 10 * 1024 * 1024 // Set default max buffer size to 10MB - }; + // Communicate to downstream calls that they should not try to run hooks + environment[EnvironmentVariableNames._RUSH_RECURSIVE_RUSHX_CALL] = '1'; - // This is needed since we specify shell=true below. - // NOTE: On Windows if we escape "NPM", the spawnSync() function runs something like this: - // [ 'C:\\Windows\\system32\\cmd.exe', '/s', '/c', '""NPM" "install""' ] - // - // Due to a bug with Windows cmd.exe, the npm.cmd batch file's "%~dp0" variable will - // return the current working directory instead of the batch file's directory. - // The workaround is to not escape, npm, i.e. do this instead: - // [ 'C:\\Windows\\system32\\cmd.exe', '/s', '/c', '"npm "install""' ] - // - // We will come up with a better solution for this when we promote executeCommand() - // into node-core-library, but for now this hack will unblock people: + return environment; +} + +/** + * Prepend the node_modules/.bin folder under the specified folder to the specified PATH variable. For example, + * if `rootDirectory` is "/foobar" and `existingPath` is "/bin", this function will return + * "/foobar/node_modules/.bin:/bin" + */ +function _prependNodeModulesBinToPath(existingPath: string | undefined, rootDirectory: string): string { + const binPath: string = path.resolve(rootDirectory, 'node_modules', '.bin'); + if (existingPath) { + return `${binPath}${path.delimiter}${existingPath}`; + } else { + return binPath; + } +} - // Only escape the command if it actually contains spaces: - const escapedCommand: string = - command.indexOf(' ') < 0 ? command : Utilities.escapeShellParameter(command); +/** + * Executes the command with the specified command-line parameters, and waits for it to complete. + * The current directory will be set to the specified workingDirectory. + */ +async function _executeCommandInternalAsync( + options: IExecuteCommandInternalOptions & { captureOutput: true } +): Promise>; +/** + * Executes the command with the specified command-line parameters, and waits for it to complete. + * The current directory will be set to the specified workingDirectory. This does not capture output. + */ +async function _executeCommandInternalAsync( + options: IExecuteCommandInternalOptions & { captureOutput: false | undefined } +): Promise; +async function _executeCommandInternalAsync({ + command, + args, + workingDirectory, + stdio, + environment, + keepEnvironment, + onStdoutStreamChunk, + captureOutput, + captureExitCodeAndSignal +}: IExecuteCommandInternalOptions): Promise | IWaitForExitResultWithoutOutput> { + const spawnOptions: child_process.SpawnSyncOptions = { + cwd: workingDirectory, + shell: true, + stdio: stdio, + env: keepEnvironment + ? environment + : _createEnvironmentForRushCommand({ initialEnvironment: environment }), + maxBuffer: 10 * 1024 * 1024 // Set default max buffer size to 10MB + }; + + // This is needed since we specify shell=true below. + // NOTE: On Windows if we escape "NPM", the spawnSync() function runs something like this: + // [ 'C:\\Windows\\system32\\cmd.exe', '/s', '/c', '""NPM" "install""' ] + // + // Due to a bug with Windows cmd.exe, the npm.cmd batch file's "%~dp0" variable will + // return the current working directory instead of the batch file's directory. + // The workaround is to not escape, npm, i.e. do this instead: + // [ 'C:\\Windows\\system32\\cmd.exe', '/s', '/c', '"npm "install""' ] + // + // We will come up with a better solution for this when we promote executeCommand() + // into node-core-library, but for now this hack will unblock people: + + // Only escape the command if it actually contains spaces: + const escapedCommand: string = escapeArgumentIfNeeded(command); + + const escapedArgs: string[] = args.map((x) => escapeArgumentIfNeeded(x)); + const shellCommand: string = [escapedCommand, ...escapedArgs].join(' '); + + const childProcess: child_process.ChildProcess = child_process.spawn(shellCommand, spawnOptions); + + if (onStdoutStreamChunk) { + const inspectStream: Transform = new Transform({ + transform: onStdoutStreamChunk + ? ( + chunk: string | Buffer, + encoding: BufferEncoding, + callback: (error?: Error, data?: string | Buffer) => void + ) => { + const chunkString: string = chunk.toString(); + const updatedChunk: string | void = onStdoutStreamChunk(chunkString); + callback(undefined, updatedChunk ?? chunk); + } + : undefined + }); - const escapedArgs: string[] = args.map((x) => Utilities.escapeShellParameter(x)); + childProcess.stdout?.pipe(inspectStream).pipe(process.stdout); + } - let result: child_process.SpawnSyncReturns = child_process.spawnSync( - escapedCommand, - escapedArgs, - options - ); + return await Executable.waitForExitAsync(childProcess, { + encoding: captureOutput ? 'utf8' : undefined, + throwOnNonZeroExitCode: !captureExitCodeAndSignal, + throwOnSignal: !captureExitCodeAndSignal + }); +} - // eslint-disable-next-line @typescript-eslint/no-explicit-any - if (result.error && (result.error as any).errno === 'ENOENT') { - // This is a workaround for GitHub issue #25330 - // https://github.com/nodejs/node-v0.x-archive/issues/25330 - // - // TODO: The fully worked out solution for this problem is now provided by the "Executable" API - // from @rushstack/node-core-library - result = child_process.spawnSync(command + '.cmd', args, options); +function _processResult({ + error, + stderr, + status +}: { + error: Error | undefined; + stderr: string; + // eslint-disable-next-line @rushstack/no-new-null + status: number | null; +}): void { + if (error) { + error.message += `\n${stderr}`; + if (status) { + error.message += `\nExited with status ${status}`; } - Utilities._processResult(result); - return result; + throw error; } - private static _processResult(result: child_process.SpawnSyncReturns): void { - if (result.error) { - result.error.message += '\n' + (result.stderr ? result.stderr.toString() + '\n' : ''); - throw result.error; - } - - if (result.status) { - throw new Error( - 'The command failed with exit code ' + - result.status + - '\n' + - (result.stderr ? result.stderr.toString() : '') - ); - } + if (status) { + throw new Error(`The command failed with exit code ${status}\n${stderr}`); } } diff --git a/libraries/rush-lib/src/utilities/WebClient.ts b/libraries/rush-lib/src/utilities/WebClient.ts index e30b4680c70..c80773e8c7e 100644 --- a/libraries/rush-lib/src/utilities/WebClient.ts +++ b/libraries/rush-lib/src/utilities/WebClient.ts @@ -1,31 +1,57 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as os from 'os'; -import * as process from 'process'; -import * as fetch from 'node-fetch'; -import * as http from 'http'; -import { Import } from '@rushstack/node-core-library'; +import * as os from 'node:os'; +import * as process from 'node:process'; +import type { Readable } from 'node:stream'; +import { + request as httpRequest, + type IncomingMessage, + type ClientRequest, + type Agent as HttpAgent +} from 'node:http'; +import { request as httpsRequest, type RequestOptions } from 'node:https'; -// =================================================================================================================== -// AS A TEMPORARY WORKAROUND, THIS FILE WAS COPY+PASTED INTO THE "rush-amazon-s3-build-cache-plugin" PROJECT. -// See that copy for notes. -// =================================================================================================================== +import { Import, LegacyAdapters } from '@rushstack/node-core-library'; const createHttpsProxyAgent: typeof import('https-proxy-agent') = Import.lazy('https-proxy-agent', require); +export interface IWebClientResponseBase { + ok: boolean; + status: number; + statusText?: string; + redirected: boolean; + headers: Record; +} + /** - * For use with {@link WebClient}. + * A response from {@link WebClient.fetchAsync}. + */ +export interface IWebClientResponse extends IWebClientResponseBase { + getTextAsync: () => Promise; + getJsonAsync: () => Promise; + getBufferAsync: () => Promise; +} + +/** + * A response from {@link WebClient.fetchStreamAsync} that provides the response body as a + * readable stream, avoiding buffering the entire response in memory. */ -export type WebClientResponse = fetch.Response; +export interface IWebClientStreamResponse extends IWebClientResponseBase { + stream: Readable; +} /** * For use with {@link WebClient}. */ export interface IWebFetchOptionsBase { timeoutMs?: number; - verb?: 'GET' | 'PUT'; - headers?: fetch.Headers; + headers?: Record; + redirect?: 'follow' | 'error' | 'manual'; + /** + * If true, the response will not be decoded if a Content-Encoding header is present. + */ + noDecode?: boolean; } /** @@ -38,9 +64,9 @@ export interface IGetFetchOptions extends IWebFetchOptionsBase { /** * For use with {@link WebClient}. */ -export interface IPutFetchOptions extends IWebFetchOptionsBase { - verb: 'PUT'; - body?: Buffer; +export interface IFetchOptionsWithBody extends IWebFetchOptionsBase { + verb: 'PUT' | 'POST' | 'PATCH'; + body?: Buffer | Readable; } /** @@ -51,88 +77,421 @@ export enum WebClientProxy { Detect, Fiddler } +export interface IRequestOptions + extends RequestOptions, + Pick {} + +export type FetchFn = ( + url: string, + options: IRequestOptions, + isRedirect?: boolean +) => Promise; + +const DEFLATE_ENCODING: 'deflate' = 'deflate'; +const GZIP_ENCODING: 'gzip' = 'gzip'; +const BROTLI_ENCODING: 'br' = 'br'; +export const AUTHORIZATION_HEADER_NAME: 'Authorization' = 'Authorization'; +const ACCEPT_HEADER_NAME: 'accept' = 'accept'; +const USER_AGENT_HEADER_NAME: 'user-agent' = 'user-agent'; +const CONTENT_ENCODING_HEADER_NAME: 'content-encoding' = 'content-encoding'; + +/** + * Parses the Content-Encoding header into an array of encoding names, + * or returns `undefined` if decoding should be skipped. + */ +function _getContentEncodings( + headers: Record, + noDecode: boolean | undefined +): string[] | undefined { + if (!noDecode) { + const encodings: string | string[] | undefined = headers[CONTENT_ENCODING_HEADER_NAME]; + if (encodings) { + return Array.isArray(encodings) ? encodings : encodings.split(','); + } + } +} + +type StreamFetchFn = ( + url: string, + options: IRequestOptions, + isRedirect?: boolean +) => Promise; + +/** + * Shared HTTP request core used by both buffer-based and streaming request functions. + * Handles URL parsing, protocol selection, redirect following, body sending, and error handling. + * The `handleResponse` callback is responsible for processing the response and calling + * `resolve`/`reject` to complete the outer promise. + */ +function _makeRawRequestAsync( + url: string, + options: IRequestOptions, + redirected: boolean, + handleResponse: ( + response: IncomingMessage, + redirected: boolean, + resolve: (result: TResponse | PromiseLike) => void, + reject: (error: Error) => void + ) => void, + requestFnAsync: (url: string, options: IRequestOptions, isRedirect?: boolean) => Promise +): Promise { + const { body, redirect } = options; + + return new Promise( + (resolve: (result: TResponse | PromiseLike) => void, reject: (error: Error) => void) => { + const parsedUrl: URL = typeof url === 'string' ? new URL(url) : url; + const requestFunction: typeof httpRequest | typeof httpsRequest = + parsedUrl.protocol === 'https:' ? httpsRequest : httpRequest; + + const req: ClientRequest = requestFunction(url, options, (response: IncomingMessage) => { + const { + statusCode, + headers: { location: redirectUrl } + } = response; + if (statusCode === 301 || statusCode === 302) { + switch (redirect) { + case 'follow': { + // Drain the redirect response since we're discarding it + response.resume(); + if (redirectUrl) { + requestFnAsync(redirectUrl, options, true).then(resolve).catch(reject); + } else { + reject(new Error(`Received status code ${statusCode} with no location header: ${url}`)); + } + return; + } + + case 'error': + response.resume(); + reject(new Error(`Received status code ${statusCode}: ${url}`)); + return; + } + } + + handleResponse(response, redirected, resolve, reject); + }).on('error', (error: Error) => { + if (body && !Buffer.isBuffer(body)) { + body.destroy(error); + } + + reject(error); + }); + + const isStream: boolean = !!body && typeof (body as Readable).pipe === 'function'; + if (isStream) { + (body as Readable).on('error', reject); + (body as Readable).pipe(req); + } else { + req.end(body as Buffer | undefined); + } + } + ); +} + +const makeRequestAsync: FetchFn = async ( + url: string, + options: IRequestOptions, + redirected: boolean = false +) => { + const { noDecode } = options; + + return _makeRawRequestAsync( + url, + options, + redirected, + ( + response: IncomingMessage, + wasRedirected: boolean, + resolve: (result: IWebClientResponse | PromiseLike) => void + ): void => { + const responseBuffers: (Buffer | Uint8Array)[] = []; + response.on('data', (chunk: string | Buffer | Uint8Array) => { + responseBuffers.push(Buffer.from(chunk)); + }); + response.on('end', () => { + const { statusCode: status = 0, statusMessage: statusText, headers } = response; + const responseData: Buffer = Buffer.concat(responseBuffers); + + let bodyString: string | undefined; + let bodyJson: unknown | undefined; + let decodedBuffer: Buffer | undefined; + const result: IWebClientResponse = { + ok: status >= 200 && status < 300, + status, + statusText, + redirected: wasRedirected, + headers, + getTextAsync: async () => { + if (bodyString === undefined) { + const buffer: Buffer = await result.getBufferAsync(); + // eslint-disable-next-line require-atomic-updates + bodyString = buffer.toString(); + } + + return bodyString; + }, + getJsonAsync: async () => { + if (bodyJson === undefined) { + const text: string = await result.getTextAsync(); + // eslint-disable-next-line require-atomic-updates + bodyJson = JSON.parse(text); + } + + return bodyJson as TJson; + }, + getBufferAsync: async () => { + // Determine if the buffer is compressed and decode it if necessary + if (decodedBuffer === undefined) { + const contentEncodings: string[] | undefined = _getContentEncodings(headers, noDecode); + if (contentEncodings) { + const zlib: typeof import('zlib') = await import('node:zlib'); + + let buffer: Buffer = responseData; + for (const encoding of contentEncodings) { + let decompressFn: (buffer: Buffer, callback: import('zlib').CompressCallback) => void; + switch (encoding.trim()) { + case DEFLATE_ENCODING: { + decompressFn = zlib.inflate.bind(zlib); + break; + } + case GZIP_ENCODING: { + decompressFn = zlib.gunzip.bind(zlib); + break; + } + case BROTLI_ENCODING: { + decompressFn = zlib.brotliDecompress.bind(zlib); + break; + } + default: { + throw new Error(`Unsupported content-encoding: ${encoding.trim()}`); + } + } + + buffer = await LegacyAdapters.convertCallbackToPromise(decompressFn, buffer); + } + + // eslint-disable-next-line require-atomic-updates + decodedBuffer = buffer; + } else { + decodedBuffer = responseData; + } + } + + return decodedBuffer; + } + }; + resolve(result); + }); + }, + makeRequestAsync + ); +}; + +const makeStreamRequestAsync: StreamFetchFn = async ( + url: string, + options: IRequestOptions, + redirected: boolean = false +) => { + const { noDecode } = options; + + return _makeRawRequestAsync( + url, + options, + redirected, + ( + response: IncomingMessage, + wasRedirected: boolean, + resolve: (result: IWebClientStreamResponse | PromiseLike) => void + ): void => { + const { statusCode: status = 0, statusMessage: statusText, headers } = response; + + const buildResult = (stream: Readable): IWebClientStreamResponse => ({ + ok: status >= 200 && status < 300, + status, + statusText, + redirected: wasRedirected, + headers, + stream + }); + + // Handle Content-Encoding decompression for streaming responses, + // matching the buffer-based path's behavior in getBufferAsync() + const contentEncodings: string[] | undefined = _getContentEncodings(headers, noDecode); + + if (contentEncodings) { + // Resolve with a promise so we can lazily import zlib (same pattern as buffer path) + resolve( + (async () => { + const zlib: typeof import('zlib') = await import('node:zlib'); + + let resultStream: Readable = response; + for (const encoding of contentEncodings) { + switch (encoding.trim()) { + case DEFLATE_ENCODING: { + resultStream = resultStream.pipe(zlib.createInflate()); + break; + } + case GZIP_ENCODING: { + resultStream = resultStream.pipe(zlib.createGunzip()); + break; + } + case BROTLI_ENCODING: { + resultStream = resultStream.pipe(zlib.createBrotliDecompress()); + break; + } + default: { + throw new Error(`Unsupported content-encoding: ${encoding.trim()}`); + } + } + } + + return buildResult(resultStream); + })() + ); + } else { + resolve(buildResult(response)); + } + }, + makeStreamRequestAsync + ); +}; + +// Module-level mutable state for mock injection. These must NOT be private members +// of WebClient because rush-sdk re-exports WebClient as a separate type declaration, +// and TypeScript's structural typing treats private members nominally, causing type +// incompatibility between the rush-lib and rush-sdk versions. +let _requestFnAsync: FetchFn = makeRequestAsync; +let _streamRequestFnAsync: StreamFetchFn = makeStreamRequestAsync; + +function _mergeHeaders(target: Record, source: Record): void { + for (const [name, value] of Object.entries(source)) { + target[name] = value; + } +} + +/** + * Builds the low-level IRequestOptions from WebClient instance state and caller-provided options. + * This is a module-level function (not a private method) to avoid the rush-sdk type mismatch. + */ +function buildRequestOptions( + webClient: WebClient, + options?: IGetFetchOptions | IFetchOptionsWithBody +): IRequestOptions { + const { + headers: optionsHeaders, + timeoutMs = 15 * 1000, + verb, + redirect, + body, + noDecode + } = (options as IFetchOptionsWithBody | undefined) ?? {}; + + const headers: Record = {}; + + const { standardHeaders, userAgent, accept, proxy } = webClient; + + _mergeHeaders(headers, standardHeaders); + + if (optionsHeaders) { + _mergeHeaders(headers, optionsHeaders); + } + + if (userAgent) { + headers[USER_AGENT_HEADER_NAME] = userAgent; + } + + if (accept) { + headers[ACCEPT_HEADER_NAME] = accept; + } + + let proxyUrl: string = ''; + + switch (proxy) { + case WebClientProxy.Detect: + if (process.env.HTTPS_PROXY) { + proxyUrl = process.env.HTTPS_PROXY; + } else if (process.env.HTTP_PROXY) { + proxyUrl = process.env.HTTP_PROXY; + } + break; + + case WebClientProxy.Fiddler: + // For debugging, disable cert validation + // eslint-disable-next-line + process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'; + proxyUrl = 'http://localhost:8888/'; + break; + } + + let agent: HttpAgent | undefined = undefined; + if (proxyUrl) { + agent = createHttpsProxyAgent(proxyUrl); + } + + return { + method: verb, + headers, + agent, + timeout: timeoutMs, + redirect, + body, + noDecode + }; +} /** * A helper for issuing HTTP requests. */ export class WebClient { - public readonly standardHeaders: fetch.Headers = new fetch.Headers(); + public readonly standardHeaders: Record = {}; public accept: string | undefined = '*/*'; public userAgent: string | undefined = `rush node/${process.version} ${os.platform()} ${os.arch()}`; public proxy: WebClientProxy = WebClientProxy.Detect; - public constructor() {} - - public static mergeHeaders(target: fetch.Headers, source: fetch.Headers): void { - source.forEach((value, name) => { - target.set(name, value); - }); + public static mockRequestFn(fn: FetchFn): void { + _requestFnAsync = fn; } - public addBasicAuthHeader(userName: string, password: string): void { - this.standardHeaders.set( - 'Authorization', - 'Basic ' + Buffer.from(userName + ':' + password).toString('base64') - ); + public static resetMockRequestFn(): void { + _requestFnAsync = makeRequestAsync; } - public async fetchAsync( - url: string, - options?: IGetFetchOptions | IPutFetchOptions - ): Promise { - const headers: fetch.Headers = new fetch.Headers(); - - WebClient.mergeHeaders(headers, this.standardHeaders); - - if (options?.headers) { - WebClient.mergeHeaders(headers, options.headers); - } - - if (this.userAgent) { - headers.set('user-agent', this.userAgent); - } - if (this.accept) { - headers.set('accept', this.accept); - } + public static mockStreamRequestFn(fn: StreamFetchFn): void { + _streamRequestFnAsync = fn; + } - let proxyUrl: string = ''; + public static resetMockStreamRequestFn(): void { + _streamRequestFnAsync = makeStreamRequestAsync; + } - switch (this.proxy) { - case WebClientProxy.Detect: - if (process.env.HTTPS_PROXY) { - proxyUrl = process.env.HTTPS_PROXY; - } else if (process.env.HTTP_PROXY) { - proxyUrl = process.env.HTTP_PROXY; - } - break; - - case WebClientProxy.Fiddler: - // For debugging, disable cert validation - // eslint-disable-next-line - process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'; - proxyUrl = 'http://localhost:8888/'; - break; - } + public static mergeHeaders(target: Record, source: Record): void { + _mergeHeaders(target, source); + } - let agent: http.Agent | undefined = undefined; - if (proxyUrl) { - agent = createHttpsProxyAgent(proxyUrl); - } + public addBasicAuthHeader(userName: string, password: string): void { + this.standardHeaders[AUTHORIZATION_HEADER_NAME] = + 'Basic ' + Buffer.from(userName + ':' + password).toString('base64'); + } - const timeoutMs: number = options?.timeoutMs !== undefined ? options.timeoutMs : 15 * 1000; // 15 seconds - const requestInit: fetch.RequestInit = { - method: options?.verb, - headers: headers, - agent: agent, - timeout: timeoutMs - }; - const putOptions: IPutFetchOptions | undefined = options as IPutFetchOptions | undefined; - if (putOptions?.body) { - requestInit.body = putOptions.body; - } + public async fetchAsync( + url: string, + options?: IGetFetchOptions | IFetchOptionsWithBody + ): Promise { + const requestInit: IRequestOptions = buildRequestOptions(this, options); + return await _requestFnAsync(url, requestInit); + } - return await fetch.default(url, requestInit); + /** + * Makes an HTTP request that resolves as soon as headers are received, providing the + * response body as a readable stream. This avoids buffering the entire response in memory. + */ + public async fetchStreamAsync( + url: string, + options?: IGetFetchOptions | IFetchOptionsWithBody + ): Promise { + const requestInit: IRequestOptions = buildRequestOptions(this, options); + return await _streamRequestFnAsync(url, requestInit); } } diff --git a/libraries/rush-lib/src/utilities/actionNameConstants.ts b/libraries/rush-lib/src/utilities/actionNameConstants.ts new file mode 100644 index 00000000000..9b28830eb3a --- /dev/null +++ b/libraries/rush-lib/src/utilities/actionNameConstants.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export const PURGE_ACTION_NAME: 'purge' = 'purge'; +export const LINK_PACKAGE_ACTION_NAME: 'link-package' = 'link-package'; +export const BRIDGE_PACKAGE_ACTION_NAME: 'bridge-package' = 'bridge-package'; diff --git a/libraries/rush-lib/src/utilities/executionUtilities.ts b/libraries/rush-lib/src/utilities/executionUtilities.ts new file mode 100644 index 00000000000..18bc33229ce --- /dev/null +++ b/libraries/rush-lib/src/utilities/executionUtilities.ts @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export const IS_WINDOWS: boolean = process.platform === 'win32'; + +export function escapeArgumentIfNeeded(command: string, isWindows: boolean = IS_WINDOWS): string { + if (command.includes(' ')) { + if (isWindows) { + // Windows: use double quotes and escape internal double quotes + return `"${command.replace(/"/g, '""')}"`; + } else { + // Unix: use JSON.stringify for proper escaping + return JSON.stringify(command); + } + } else { + return command; + } +} diff --git a/libraries/rush-lib/src/utilities/npmrcUtilities.ts b/libraries/rush-lib/src/utilities/npmrcUtilities.ts index 6260b837dd3..7ca6febe487 100644 --- a/libraries/rush-lib/src/utilities/npmrcUtilities.ts +++ b/libraries/rush-lib/src/utilities/npmrcUtilities.ts @@ -3,8 +3,8 @@ // IMPORTANT - do not use any non-built-in libraries in this file -import * as fs from 'fs'; -import * as path from 'path'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; export interface ILogger { info: (string: string) => void; @@ -12,24 +12,119 @@ export interface ILogger { } /** - * As a workaround, copyAndTrimNpmrcFile() copies the .npmrc file to the target folder, and also trims + * This function reads the content for given .npmrc file path, and also trims * unusable lines from the .npmrc file. * - * Why are we trimming the .npmrc lines? NPM allows environment variables to be specified in - * the .npmrc file to provide different authentication tokens for different registry. - * However, if the environment variable is undefined, it expands to an empty string, which - * produces a valid-looking mapping with an invalid URL that causes an error. Instead, - * we'd prefer to skip that line and continue looking in other places such as the user's - * home directory. - * * @returns * The text of the the .npmrc. */ -function _copyAndTrimNpmrcFile(logger: ILogger, sourceNpmrcPath: string, targetNpmrcPath: string): string { - logger.info(`Transforming ${sourceNpmrcPath}`); // Verbose - logger.info(` --> "${targetNpmrcPath}"`); - let npmrcFileLines: string[] = fs.readFileSync(sourceNpmrcPath).toString().split('\n'); + +function _trimNpmrcFile( + options: Pick< + INpmrcTrimOptions, + | 'sourceNpmrcPath' + | 'linesToAppend' + | 'linesToPrepend' + | 'supportEnvVarFallbackSyntax' + | 'filterNpmIncompatibleProperties' + | 'env' + > +): string { + const { + sourceNpmrcPath, + linesToPrepend, + linesToAppend, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties, + env = process.env + } = options; + + let npmrcFileLines: string[] = []; + if (linesToPrepend) { + npmrcFileLines.push(...linesToPrepend); + } + + if (fs.existsSync(sourceNpmrcPath)) { + npmrcFileLines.push(...fs.readFileSync(sourceNpmrcPath).toString().split('\n')); + } + + if (linesToAppend) { + npmrcFileLines.push(...linesToAppend); + } + npmrcFileLines = npmrcFileLines.map((line) => (line || '').trim()); + + const resultLines: string[] = trimNpmrcFileLines( + npmrcFileLines, + env, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties + ); + + const combinedNpmrc: string = resultLines.join('\n'); + + return combinedNpmrc; +} + +/** + * List of npmrc properties that are not supported by npm but may be present in the config. + * These include pnpm-specific properties and deprecated npm properties. + */ +const NPM_INCOMPATIBLE_PROPERTIES: Set = new Set([ + // pnpm-specific hoisting configuration + 'hoist', + 'hoist-pattern', + 'public-hoist-pattern', + 'shamefully-hoist', + // Deprecated or unknown npm properties that cause warnings + 'email', + 'publish-branch' +]); + +/** + * List of registry-scoped npmrc property suffixes that are pnpm-specific. + * These are properties like "//registry.example.com/:tokenHelper" where "tokenHelper" + * is the suffix after the last colon. + */ +const NPM_INCOMPATIBLE_REGISTRY_SCOPED_PROPERTIES: Set = new Set([ + // pnpm-specific token helper properties + 'tokenHelper', + 'urlTokenHelper' +]); + +/** + * Regular expression to extract property names from .npmrc lines. + * Matches everything before '=', '[', or whitespace to capture the property name. + * Note: The 'g' flag is intentionally omitted since we only need the first match. + * Examples: + * "registry=https://..." -> matches "registry" + * "hoist-pattern[]=..." -> matches "hoist-pattern" + */ +const PROPERTY_NAME_REGEX: RegExp = /^([^=\[\s]+)/; + +/** + * Regular expression to extract environment variable names and optional fallback values. + * Matches patterns like: + * nameString -> group 1: nameString, group 2: undefined + * nameString-fallbackString -> group 1: nameString, group 2: fallbackString + * nameString:-fallbackString -> group 1: nameString, group 2: fallbackString + */ +const ENV_VAR_WITH_FALLBACK_REGEX: RegExp = /^(?[^:-]+)(?::?-(?.+))?$/; + +/** + * + * @param npmrcFileLines The npmrc file's lines + * @param env The environment variables object + * @param supportEnvVarFallbackSyntax Whether to support fallback values in the form of `${VAR_NAME:-fallback}` + * @param filterNpmIncompatibleProperties Whether to filter out properties that npm doesn't understand + * @returns An array of processed npmrc file lines with undefined environment variables and npm-incompatible properties commented out + */ +export function trimNpmrcFileLines( + npmrcFileLines: string[], + env: NodeJS.ProcessEnv, + supportEnvVarFallbackSyntax: boolean, + filterNpmIncompatibleProperties: boolean = false +): string[] { const resultLines: string[] = []; // This finds environment variable tokens that look like "${VAR_NAME}" @@ -39,37 +134,144 @@ function _copyAndTrimNpmrcFile(logger: ILogger, sourceNpmrcPath: string, targetN const commentRegExp: RegExp = /^\s*[#;]/; // Trim out lines that reference environment variables that aren't defined - for (const line of npmrcFileLines) { + for (let line of npmrcFileLines) { let lineShouldBeTrimmed: boolean = false; + let trimReason: string = ''; + + //remove spaces before or after key and value + line = line + .split('=') + .map((lineToTrim) => lineToTrim.trim()) + .join('='); // Ignore comment lines if (!commentRegExp.test(line)) { - const environmentVariables: string[] | null = line.match(expansionRegExp); - if (environmentVariables) { - for (const token of environmentVariables) { - // Remove the leading "${" and the trailing "}" from the token - const environmentVariableName: string = token.substring(2, token.length - 1); - - // Is the environment variable defined? - if (!process.env[environmentVariableName]) { - // No, so trim this line - lineShouldBeTrimmed = true; - break; + // Check if this is a property that npm doesn't understand + if (filterNpmIncompatibleProperties) { + // Extract the property name (everything before the '=' or '[') + const match: RegExpMatchArray | null = line.match(PROPERTY_NAME_REGEX); + if (match) { + const propertyName: string = match[1]; + + // Check if this is a registry-scoped property (starts with "//" like "//registry.npmjs.org/:_authToken") + const isRegistryScoped: boolean = propertyName.startsWith('//'); + + if (isRegistryScoped) { + // For registry-scoped properties, check if the suffix (after the last colon) is npm-incompatible + // Example: "//registry.example.com/:tokenHelper" -> suffix is "tokenHelper" + const lastColonIndex: number = propertyName.lastIndexOf(':'); + if (lastColonIndex !== -1) { + const registryPropertySuffix: string = propertyName.substring(lastColonIndex + 1); + if (NPM_INCOMPATIBLE_REGISTRY_SCOPED_PROPERTIES.has(registryPropertySuffix)) { + lineShouldBeTrimmed = true; + trimReason = 'NPM_INCOMPATIBLE_PROPERTY'; + } + } + } else { + // For non-registry-scoped properties, check the full property name + if (NPM_INCOMPATIBLE_PROPERTIES.has(propertyName)) { + lineShouldBeTrimmed = true; + trimReason = 'NPM_INCOMPATIBLE_PROPERTY'; + } + } + } + } + + // Check for undefined environment variables + if (!lineShouldBeTrimmed) { + const environmentVariables: string[] | null = line.match(expansionRegExp); + if (environmentVariables) { + for (const token of environmentVariables) { + /** + * Remove the leading "${" and the trailing "}" from the token + * + * ${nameString} -> nameString + * ${nameString-fallbackString} -> name-fallbackString + * ${nameString:-fallbackString} -> name:-fallbackString + */ + const nameWithFallback: string = token.slice(2, -1); + + let environmentVariableName: string; + let fallback: string | undefined; + if (supportEnvVarFallbackSyntax) { + /** + * Get the environment variable name and fallback value. + * + * name fallback + * nameString -> nameString undefined + * nameString-fallbackString -> nameString fallbackString + * nameString:-fallbackString -> nameString fallbackString + */ + const matched: RegExpMatchArray | null = nameWithFallback.match(ENV_VAR_WITH_FALLBACK_REGEX); + environmentVariableName = matched?.groups?.name ?? nameWithFallback; + fallback = matched?.groups?.fallback; + } else { + environmentVariableName = nameWithFallback; + } + + // Is the environment variable and fallback value defined. + if (!env[environmentVariableName] && !fallback) { + // No, so trim this line + lineShouldBeTrimmed = true; + trimReason = 'MISSING_ENVIRONMENT_VARIABLE'; + break; + } } } } } if (lineShouldBeTrimmed) { - // Example output: - // "; MISSING ENVIRONMENT VARIABLE: //my-registry.com/npm/:_authToken=${MY_AUTH_TOKEN}" - resultLines.push('; MISSING ENVIRONMENT VARIABLE: ' + line); + // Comment out the line with appropriate reason + if (trimReason === 'NPM_INCOMPATIBLE_PROPERTY') { + // Example output: + // "; UNSUPPORTED BY NPM: email=test@example.com" + resultLines.push('; UNSUPPORTED BY NPM: ' + line); + } else { + // Example output: + // "; MISSING ENVIRONMENT VARIABLE: //my-registry.com/npm/:_authToken=${MY_AUTH_TOKEN}" + resultLines.push('; MISSING ENVIRONMENT VARIABLE: ' + line); + } } else { resultLines.push(line); } } - const combinedNpmrc: string = resultLines.join('\n'); + return resultLines; +} + +/** + * As a workaround, copyAndTrimNpmrcFile() copies the .npmrc file to the target folder, and also trims + * unusable lines from the .npmrc file. + * + * Why are we trimming the .npmrc lines? NPM allows environment variables to be specified in + * the .npmrc file to provide different authentication tokens for different registry. + * However, if the environment variable is undefined, it expands to an empty string, which + * produces a valid-looking mapping with an invalid URL that causes an error. Instead, + * we'd prefer to skip that line and continue looking in other places such as the user's + * home directory. + * + * @returns + * The text of the the .npmrc with lines containing undefined variables commented out. + */ +interface INpmrcTrimOptions { + sourceNpmrcPath: string; + targetNpmrcPath: string; + logger: ILogger; + linesToPrepend?: string[]; + linesToAppend?: string[]; + supportEnvVarFallbackSyntax: boolean; + filterNpmIncompatibleProperties?: boolean; + env?: NodeJS.ProcessEnv; +} + +function _copyAndTrimNpmrcFile(options: INpmrcTrimOptions): string { + const { logger, sourceNpmrcPath, targetNpmrcPath } = options; + logger.info(`Transforming ${sourceNpmrcPath}`); // Verbose + logger.info(` --> "${targetNpmrcPath}"`); + + const combinedNpmrc: string = _trimNpmrcFile(options); + fs.writeFileSync(targetNpmrcPath, combinedNpmrc); return combinedNpmrc; @@ -84,23 +286,50 @@ function _copyAndTrimNpmrcFile(logger: ILogger, sourceNpmrcPath: string, targetN * @returns * The text of the the synced .npmrc, if one exists. If one does not exist, then undefined is returned. */ -export function syncNpmrc( - sourceNpmrcFolder: string, - targetNpmrcFolder: string, - useNpmrcPublish?: boolean, - logger: ILogger = { - info: console.log, - error: console.error - } -): string | undefined { +export interface ISyncNpmrcOptions { + sourceNpmrcFolder: string; + targetNpmrcFolder: string; + supportEnvVarFallbackSyntax: boolean; + useNpmrcPublish?: boolean; + logger?: ILogger; + linesToPrepend?: string[]; + linesToAppend?: string[]; + createIfMissing?: boolean; + filterNpmIncompatibleProperties?: boolean; + env?: NodeJS.ProcessEnv; +} + +export function syncNpmrc(options: ISyncNpmrcOptions): string | undefined { + const { + sourceNpmrcFolder, + targetNpmrcFolder, + useNpmrcPublish, + logger = { + // eslint-disable-next-line no-console + info: console.log, + // eslint-disable-next-line no-console + error: console.error + }, + createIfMissing = false + } = options; const sourceNpmrcPath: string = path.join( sourceNpmrcFolder, !useNpmrcPublish ? '.npmrc' : '.npmrc-publish' ); const targetNpmrcPath: string = path.join(targetNpmrcFolder, '.npmrc'); try { - if (fs.existsSync(sourceNpmrcPath)) { - return _copyAndTrimNpmrcFile(logger, sourceNpmrcPath, targetNpmrcPath); + if (fs.existsSync(sourceNpmrcPath) || createIfMissing) { + // Ensure the target folder exists + if (!fs.existsSync(targetNpmrcFolder)) { + fs.mkdirSync(targetNpmrcFolder, { recursive: true }); + } + + return _copyAndTrimNpmrcFile({ + sourceNpmrcPath, + targetNpmrcPath, + logger, + ...options + }); } else if (fs.existsSync(targetNpmrcPath)) { // If the source .npmrc doesn't exist and there is one in the target, delete the one in the target logger.info(`Deleting ${targetNpmrcPath}`); // Verbose @@ -110,3 +339,25 @@ export function syncNpmrc( throw new Error(`Error syncing .npmrc file: ${e}`); } } + +export function isVariableSetInNpmrcFile( + sourceNpmrcFolder: string, + variableKey: string, + supportEnvVarFallbackSyntax: boolean +): boolean { + const sourceNpmrcPath: string = `${sourceNpmrcFolder}/.npmrc`; + + //if .npmrc file does not exist, return false directly + if (!fs.existsSync(sourceNpmrcPath)) { + return false; + } + + const trimmedNpmrcFile: string = _trimNpmrcFile({ + sourceNpmrcPath, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties: false + }); + + const variableKeyRegExp: RegExp = new RegExp(`^${variableKey}=`, 'm'); + return trimmedNpmrcFile.match(variableKeyRegExp) !== null; +} diff --git a/libraries/rush-lib/src/utilities/objectUtilities.ts b/libraries/rush-lib/src/utilities/objectUtilities.ts new file mode 100644 index 00000000000..7261c88ef4d --- /dev/null +++ b/libraries/rush-lib/src/utilities/objectUtilities.ts @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export function cloneDeep(obj: TObject): TObject { + return cloneDeepInner(obj, new Set()); +} + +export function merge(base: TBase, other: TOther): (TBase & TOther) | TOther { + if (typeof other === 'object' && other !== null && !Array.isArray(other)) { + for (const [key, value] of Object.entries(other)) { + if (key in base) { + const baseValue: unknown = (base as Record)[key]; + if (typeof baseValue === 'object' && baseValue !== null && !Array.isArray(baseValue)) { + (base as Record)[key] = merge(baseValue, value); + } else { + (base as Record)[key] = value; + } + } else { + (base as Record)[key] = value; + } + } + + return base as TBase & TOther; + } else { + return other; + } +} + +function cloneDeepInner(obj: TObject, seenObjects: Set): TObject { + if (seenObjects.has(obj)) { + throw new Error('Circular reference detected'); + } else if (typeof obj === 'object') { + if (obj === null) { + return null as TObject; + } else { + seenObjects.add(obj); + if (Array.isArray(obj)) { + const result: unknown[] = []; + for (const item of obj) { + result.push(cloneDeepInner(item, new Set(seenObjects))); + } + + return result as TObject; + } else { + const result: Record = {}; + for (const key of Object.getOwnPropertyNames(obj)) { + const value: unknown = (obj as Record)[key]; + result[key] = cloneDeepInner(value, new Set(seenObjects)); + } + + return result as TObject; + } + } + } else { + return obj; + } +} + +/** + * Performs a partial deep comparison between `obj` and `source` to + * determine if `obj` contains equivalent property values. + */ +export function isMatch(obj: TObject, source: TObject): boolean { + return obj === source || (typeof obj === typeof source && isMatchInner(obj, source)); +} + +function isMatchInner(obj: TObject, source: TObject): boolean { + if (obj === null || obj === undefined) { + return false; + } + + for (const k of Object.keys(source as object)) { + const key: keyof TObject = k as keyof TObject; + const sourceValue: unknown = source[key]; + if (isStrictComparable(sourceValue)) { + if (obj[key] !== sourceValue) { + return false; + } + } else if (!isMatchInner(obj[key], sourceValue)) { + return false; + } + } + + return true; +} + +/** + * Check if `value` is suitable for strict equality comparisons, i.e. `===`. + */ +function isStrictComparable(value: T): boolean { + const type: string = typeof value; + return ( + // eslint-disable-next-line no-self-compare + value === value && !(value !== null && value !== undefined && (type === 'object' || type === 'function')) + ); +} + +/** + * Removes `undefined` and `null` direct properties from an object. + * + * @remarks + * Note that this does not recurse through sub-objects. + */ +export function removeNullishProps(obj: T): Partial { + const result: Partial = {}; + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + if (obj[key] !== undefined && obj[key] !== null) { + result[key] = obj[key]; + } + } + } + return result; +} diff --git a/libraries/rush-lib/src/utilities/performance.ts b/libraries/rush-lib/src/utilities/performance.ts new file mode 100644 index 00000000000..ced7757096e --- /dev/null +++ b/libraries/rush-lib/src/utilities/performance.ts @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { PerformanceEntry } from 'node:perf_hooks'; + +/** + * Starts a performance measurement that can be disposed later to record the elapsed time. + * @param name - The name of the performance measurement. This should be unique for each measurement. + * @returns A Disposable object that, when disposed, will end and record the performance measurement. + */ +export function measureUntilDisposed(name: string): Disposable { + const start: number = performance.now(); + + return { + [Symbol.dispose]() { + performance.measure(name, { + start + }); + } + }; +} + +/** + * Measures the execution time of a Promise-returning function. + * @param name - The name of the performance measurement. This should be unique for each measurement. + * @param fn - A function that returns a Promise. This function will be executed, and its execution time will be measured. + * @returns A Promise that resolves with the result of the function. + */ +export function measureAsyncFn(name: string, fn: () => Promise): Promise { + const start: number = performance.now(); + return fn().finally(() => { + performance.measure(name, { + start + }); + }); +} + +/** + * Measures the execution time of a synchronous function. + * @param name - The name of the performance measurement. This should be unique for each measurement. + * @param fn - A function that returns a value. This function will be executed, and its execution time will be measured. + * @returns The result of the function. + */ +export function measureFn(name: string, fn: () => T): T { + const start: number = performance.now(); + try { + return fn(); + } finally { + performance.measure(name, { + start + }); + } +} + +/** + * Collects performance measurements that were created after a specified start time. + * @param startTime - The start time in milliseconds from which to collect performance measurements. + * @returns An array of `PerformanceEntry` objects with start times greater than or equal to the specified start time. + */ +export function collectPerformanceEntries(startTime: number): PerformanceEntry[] { + const entries: PerformanceEntry[] = performance.getEntries(); + const startIndex: number = entries.findIndex((entry) => entry.startTime >= startTime); + if (startIndex === -1) { + return []; // No entries found after the specified start time + } + return entries.slice(startIndex); +} diff --git a/libraries/rush-lib/src/utilities/prompts/SearchListPrompt.ts b/libraries/rush-lib/src/utilities/prompts/SearchListPrompt.ts deleted file mode 100644 index 90bebb9eb5c..00000000000 --- a/libraries/rush-lib/src/utilities/prompts/SearchListPrompt.ts +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import type { Interface } from 'readline'; -import colors from 'colors/safe'; -import { Import } from '@rushstack/node-core-library'; - -// Modified from the choice list prompt in inquirer: -// https://github.com/SBoudrias/Inquirer.js/blob/inquirer%407.3.3/packages/inquirer/lib/prompts/list.js -// Extended to include text filtering for the list -import type { default as inquirer, Answers, ListQuestion, DistinctChoice } from 'inquirer'; -import BasePrompt from 'inquirer/lib/prompts/base'; -import observe from 'inquirer/lib/utils/events'; -import Paginator from 'inquirer/lib/utils/paginator'; -import type Separator from 'inquirer/lib/objects/separator'; -import type Choice from 'inquirer/lib/objects/choice'; -import type Choices from 'inquirer/lib/objects/choices'; - -import figures from 'figures'; - -import { map, takeUntil } from 'rxjs/operators'; - -const _: typeof import('lodash') = Import.lazy('lodash', require); - -interface IKeyPressEvent { - key: { name: string; ctrl: boolean; sequence?: string }; -} - -export class SearchListPrompt extends BasePrompt { - protected done!: (result: unknown) => void; - - private readonly _paginator: Paginator; - private _selected: number = 0; - private _query: string = ''; - private _firstRender: boolean = true; - - public constructor(question: ListQuestion, readline: Interface, answers: Answers) { - super(question, readline, answers); - - if (!this.opt.choices) { - this.throwParamError('choices'); - } - - if ( - _.isNumber(this.opt.default) && - this.opt.default >= 0 && - this.opt.default < this.opt.choices.realLength - ) { - this._selected = this.opt.default; - } else if (!_.isNumber(this.opt.default) && this.opt.default !== null) { - const index: number = this.opt.choices.realChoices.findIndex(({ value }) => value === this.opt.default); - this._selected = Math.max(index, 0); - } - - // Make sure no default is set (so it won't be printed) - this.opt.default = null; - - this._paginator = new Paginator(this.screen); - } - - protected _run(callback: (result: unknown) => void): this { - this.done = callback; - - // eslint-disable-next-line @typescript-eslint/typedef - const events = observe(this.rl); - // eslint-disable-next-line @typescript-eslint/typedef - const validation = this.handleSubmitEvents(events.line.pipe(map(this._getCurrentValue.bind(this)))); - - //eslint-disable-next-line no-void - void validation.success.forEach(this._onSubmit.bind(this)); - //eslint-disable-next-line no-void - void validation.error.forEach(this._onError.bind(this)); - - // eslint-disable-next-line no-void - void events.numberKey - .pipe(takeUntil(events.line)) - .forEach(this._onNumberKey.bind(this) as (evt: unknown) => void); - - // eslint-disable-next-line no-void - void events.keypress - .pipe(takeUntil(validation.success)) - .forEach(this._onKeyPress.bind(this) as (evt: unknown) => void); - - this.render(); - return this; - } - - private _onUpKey(): void { - return this._adjustSelected(-1); - } - - private _onDownKey(): void { - return this._adjustSelected(1); - } - - private _onNumberKey(input: number): void { - if (input <= this.opt.choices.realLength) { - this._selected = input - 1; - } - - this.render(); - } - - /** - * When user press `enter` key - */ - private _onSubmit(state: { value: unknown }): void { - this.status = 'answered'; - // Rerender prompt (and clean subline error) - this.render(); - - this.screen.done(); - this.done(state.value); - } - - private _onError(state: inquirer.prompts.FailedPromptStateData): void { - this.render(state.isValid || undefined); - } - - private _onKeyPress(event: IKeyPressEvent): void { - if (event.key.ctrl) { - switch (event.key.name) { - case 'backspace': - return this._setQuery(''); - } - } else { - switch (event.key.name) { - // Go to beginning of list - case 'home': - return this._adjustSelected(-Infinity); - // Got to end of list - case 'end': - return this._adjustSelected(Infinity); - // Paginate up - case 'pageup': - return this._adjustSelected(-(this.opt.pageSize ?? 1)); - // Paginate down - case 'pagedown': - return this._adjustSelected(this.opt.pageSize ?? 1); - - case 'backspace': - return this._setQuery(this._query.slice(0, -1)); - case 'up': - return this._onUpKey(); - case 'down': - return this._onDownKey(); - - default: - if (event.key.sequence && event.key.sequence.length === 1) { - this._setQuery(this._query + event.key.sequence); - } - } - } - } - - private _setQuery(query: string): void { - this._query = query; - const filter: string = query.toUpperCase(); - - const { choices } = this.opt.choices; - for (const choice of choices as Iterable<{ disabled?: boolean; type: string; short: string }>) { - if (choice.type !== 'separator') { - choice.disabled = !choice.short.toUpperCase().includes(filter); - } - } - - // Select the first valid option - this._adjustSelected(0); - } - - // Provide the delta in deplayed choices and change the selected - // index accordingly by the delta in real choices - private _adjustSelected(delta: number): void { - const { choices } = this.opt.choices; - const pointer: number = this._selected; - let lastValidIndex: number = pointer; - - // if delta is less than 0, we are moving up in list w/ selected index - if (delta < 0) { - for (let i: number = pointer - 1; i >= 0; i--) { - const choice: Choice = choices[i] as Choice; - if (isValidChoice(choice)) { - ++delta; - lastValidIndex = i; - // if delta is 0, we have found the next valid choice that has an index less than the selected index - if (delta === 0) { - break; - } - } - } - } else { - // if delta is greater than 0, we are moving down in list w/ selected index - // Also, if delta is exactly 0, the request is to adjust to the first - // displayed choice that has an index >= the current selected choice. - ++delta; - for (let i: number = pointer, len: number = choices.length; i < len; i++) { - const choice: Choice = choices[i] as Choice; - if (isValidChoice(choice)) { - --delta; - lastValidIndex = i; - // if delta is 0, we have found the next valid choice that has an index greater than the selected index - if (delta === 0) { - break; - } - } - } - } - - this._selected = lastValidIndex; - this.render(); - } - - private _getCurrentValue(): string { - return this.opt.choices.getChoice(this._selected).value; - } - - public render(error?: string): void { - // Render the question - let message: string = this.getQuestion(); - let bottomContent: string = ''; - - if (this._firstRender) { - message += colors.dim(' (Use arrow keys)'); - } - - // Render choices or answer depending on the state - if (this.status === 'answered') { - message += colors.cyan(this.opt.choices.getChoice(this._selected).short!); - } else { - const choicesStr: string = listRender(this.opt.choices, this._selected); - const indexPosition: number = this.opt.choices.indexOf( - this.opt.choices.getChoice(this._selected) as Choice - ); - let realIndexPosition: number = 0; - const { choices } = this.opt.choices; - - for (let i: number = 0; i < indexPosition; i++) { - const value: DistinctChoice = choices[i]; - - // Add line if it's a separator - if (value.type === 'separator') { - realIndexPosition++; - continue; - } - - // Do not render choices which disabled property - // these represent choices that are filtered out - if ((value as { disabled?: unknown }).disabled) { - continue; - } - - const line: string | undefined = value.name; - // Non-strings take up one line - if (typeof line !== 'string') { - realIndexPosition++; - continue; - } - - // Calculate lines taken up by string - // eslint-disable-next-line no-bitwise - realIndexPosition += ((line.length / process.stdout.columns!) | 0) + 1; - } - message += `\n${colors.white(colors.bold('Start typing to filter:'))} ${colors.cyan(this._query)}`; - // @ts-expect-error Types are wrong - message += '\n' + this._paginator.paginate(choicesStr, realIndexPosition, this.opt.pageSize!); - } - - if (error) { - bottomContent = colors.red('>> ') + error; - } - - this.screen.render(message, bottomContent); - } -} - -function listRender(choices: Choices, pointer: number): string { - let output: string = ''; - - choices.forEach((choice: Separator | Choice, i: number) => { - if (choice.type === 'separator') { - output += ' ' + choice + '\n'; - return; - } - - if (!choice.disabled) { - const line: string = choice.name; - if (i === pointer) { - output += colors.cyan(figures.pointer + line); - } else { - output += ' ' + line; - } - } - - if (i < choices.length - 1) { - output += '\n'; - } - }); - - return output.replace(/\n$/, ''); -} - -function isValidChoice(choice: Choice): boolean { - return !choice.disabled; -} diff --git a/libraries/rush-lib/src/utilities/templateUtilities.ts b/libraries/rush-lib/src/utilities/templateUtilities.ts new file mode 100644 index 00000000000..a2ccdcef326 --- /dev/null +++ b/libraries/rush-lib/src/utilities/templateUtilities.ts @@ -0,0 +1,228 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { FileSystem, InternalError, NewlineKind } from '@rushstack/node-core-library'; +import { Colorize } from '@rushstack/terminal'; + +import { Rush } from '../api/Rush'; + +// Matches a well-formed BEGIN macro starting a block section. +// Example: /*[BEGIN "DEMO"]*/ +// +// Group #1 is the indentation spaces before the macro +// Group #2 is the section name +const BEGIN_MARCO_REGEXP: RegExp = /^(\s*)\/\*\[BEGIN "([A-Z]+)"\]\s*\*\/\s*$/; + +// Matches a well-formed END macro ending a block section. +// Example: /*[END "DEMO"]*/ +// +// Group #1 is the indentation spaces before the macro +// Group #2 is the section name +const END_MACRO_REGEXP: RegExp = /^(\s*)\/\*\[END "([A-Z]+)"\]\s*\*\/\s*$/; + +// Matches a well-formed single-line section, including the space character after it +// if present. +// Example: /*[LINE "HYPOTHETICAL"]*/ +// +// Group #1 is the section name +const LINE_MACRO_REGEXP: RegExp = /\/\*\[LINE "([A-Z]+)"\]\s*\*\/\s?/; + +// Matches a variable expansion. +// Example: [%RUSH_VERSION%] +// +// Group #1 is the variable name including the dollar sign +const VARIABLE_MACRO_REGEXP: RegExp = /\[(%[A-Z0-9_]+%)\]/; + +// Matches anything that starts with "/*[" and ends with "]*/" +// Used to catch malformed macro expressions +const ANY_MACRO_REGEXP: RegExp = /\/\*\s*\[.*\]\s*\*\//; + +// Copy the template from sourcePath, transform any macros, and write the output to destinationPath. +// +// We implement a simple template engine. "Single-line section" macros have this form: +// +// /*[LINE "NAME"]*/ (content goes here) +// +// ...and when commented out will look like this: +// +// // (content goes here) +// +// "Block section" macros have this form: +// +// /*[BEGIN "NAME"]*/ +// (content goes +// here) +// /*[END "NAME"]*/ +// +// ...and when commented out will look like this: +// +// // (content goes +// // here) +// +// Lastly, a variable expansion has this form: +// +// // The value is [%NAME%]. +// +// ...and when expanded with e.g. "123" will look like this: +// +// // The value is 123. +// +// The section names must be one of the predefined names used by "rush init". +// A single-line section may appear inside a block section, in which case it will get +// commented twice. +export async function copyTemplateFileAsync( + sourcePath: string, + destinationPath: string, + overwrite: boolean, + demo: boolean = false +): Promise { + const destinationFileExists: boolean = await FileSystem.existsAsync(destinationPath); + + if (!overwrite) { + if (destinationFileExists) { + // eslint-disable-next-line no-console + console.log(Colorize.yellow('Not overwriting already existing file: ') + destinationPath); + return; + } + } + + if (destinationFileExists) { + // eslint-disable-next-line no-console + console.log(Colorize.yellow(`Overwriting: ${destinationPath}`)); + } else { + // eslint-disable-next-line no-console + console.log(`Generating: ${destinationPath}`); + } + + const outputLines: string[] = []; + const lines: string[] = ( + await FileSystem.readFileAsync(sourcePath, { convertLineEndings: NewlineKind.Lf }) + ).split('\n'); + + let activeBlockSectionName: string | undefined = undefined; + let activeBlockIndent: string = ''; + + for (const line of lines) { + let match: RegExpMatchArray | null; + + // Check for a block section start + // Example: /*[BEGIN "DEMO"]*/ + match = line.match(BEGIN_MARCO_REGEXP); + if (match) { + if (activeBlockSectionName) { + // If this happens, please report a Rush bug + throw new InternalError( + `The template contains an unmatched BEGIN macro for "${activeBlockSectionName}"` + ); + } + + activeBlockSectionName = match[2]; + activeBlockIndent = match[1]; + // Remove the entire line containing the macro + continue; + } + + // Check for a block section end + // Example: /*[END "DEMO"]*/ + match = line.match(END_MACRO_REGEXP); + if (match) { + if (activeBlockSectionName === undefined) { + // If this happens, please report a Rush bug + throw new InternalError( + `The template contains an unmatched END macro for "${activeBlockSectionName}"` + ); + } + + if (activeBlockSectionName !== match[2]) { + // If this happens, please report a Rush bug + throw new InternalError( + `The template contains an mismatched END macro for "${activeBlockSectionName}"` + ); + } + + if (activeBlockIndent !== match[1]) { + // If this happens, please report a Rush bug + throw new InternalError( + `The template contains an inconsistently indented section "${activeBlockSectionName}"` + ); + } + + activeBlockSectionName = undefined; + + // Remove the entire line containing the macro + continue; + } + + let transformedLine: string = line; + + // Check for a single-line section + // Example: /*[LINE "HYPOTHETICAL"]*/ + match = transformedLine.match(LINE_MACRO_REGEXP); + if (match) { + const sectionName: string = match[1]; + const replacement: string = _isSectionCommented(sectionName, demo) ? '// ' : ''; + transformedLine = transformedLine.replace(LINE_MACRO_REGEXP, replacement); + } + + // Check for variable expansions + // Example: [%RUSH_VERSION%] + while ((match = transformedLine.match(VARIABLE_MACRO_REGEXP))) { + const variableName: string = match[1]; + const replacement: string = _expandMacroVariable(variableName); + transformedLine = transformedLine.replace(VARIABLE_MACRO_REGEXP, replacement); + } + + // Verify that all macros were handled + match = transformedLine.match(ANY_MACRO_REGEXP); + if (match) { + // If this happens, please report a Rush bug + throw new InternalError( + 'The template contains a malformed macro expression: ' + JSON.stringify(match[0]) + ); + } + + // If we are inside a block section that is commented out, then insert the "//" after indentation + if (activeBlockSectionName !== undefined) { + if (_isSectionCommented(activeBlockSectionName, demo)) { + // Is the line indented properly? + if (transformedLine.substr(0, activeBlockIndent.length).trim().length > 0) { + // If this happens, please report a Rush bug + throw new InternalError( + `The template contains inconsistently indented lines inside` + + ` the "${activeBlockSectionName}" section` + ); + } + + // Insert comment characters after the indentation + const contentAfterIndent: string = transformedLine.substr(activeBlockIndent.length); + transformedLine = activeBlockIndent + '// ' + contentAfterIndent; + } + } + + outputLines.push(transformedLine); + } + + // Write the output + await FileSystem.writeFileAsync(destinationPath, outputLines.join('\n'), { + ensureFolderExists: true + }); +} + +function _isSectionCommented(sectionName: string, demo: boolean): boolean { + // The "HYPOTHETICAL" sections are always commented out by "rush init". + // They are uncommented in the "assets" source folder so that we can easily validate + // that they conform to their JSON schema. + if (sectionName === 'HYPOTHETICAL') return true; + if (sectionName === 'DEMO') return demo; + // If this happens, please report a Rush bug + throw new InternalError(`The template references an undefined section name ${sectionName}`); +} + +function _expandMacroVariable(variableName: string): string { + switch (variableName) { + case '%RUSH_VERSION%': + return Rush.version; + default: + throw new InternalError(`The template references an undefined variable "${variableName}"`); + } +} diff --git a/libraries/rush-lib/src/utilities/test/Npm.test.ts b/libraries/rush-lib/src/utilities/test/Npm.test.ts index 83b82bfb661..faed375f2d5 100644 --- a/libraries/rush-lib/src/utilities/test/Npm.test.ts +++ b/libraries/rush-lib/src/utilities/test/Npm.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import process from 'process'; +import process from 'node:process'; import { Npm } from '../Npm'; import { Utilities } from '../Utilities'; @@ -11,7 +11,7 @@ describe(Npm.name, () => { let stub: jest.SpyInstance; beforeEach(() => { - stub = jest.spyOn(Utilities, 'executeCommandAndCaptureOutput'); + stub = jest.spyOn(Utilities, 'executeCommandAndCaptureOutputAsync'); }); afterEach(() => { @@ -19,7 +19,7 @@ describe(Npm.name, () => { stub.mockRestore(); }); - it('publishedVersions gets versions when package time is available.', () => { + it('publishedVersions gets versions when package time is available.', async () => { const json: string = `{ "modified": "2017-03-30T18:37:27.757Z", "created": "2017-01-03T20:28:10.342Z", @@ -28,47 +28,50 @@ describe(Npm.name, () => { "1.4.1": "2017-01-09T19:22:00.488Z", "2.4.0-alpha.1": "2017-03-30T18:37:27.757Z" }`; - stub.mockImplementationOnce(() => json); + stub.mockImplementationOnce(() => + Promise.resolve({ stdout: json, stderr: '', signal: undefined, exitCode: 0 }) + ); - const versions: string[] = Npm.publishedVersions(packageName, __dirname, process.env); + const versions: string[] = await Npm.getPublishedVersionsAsync(packageName, __dirname, process.env); expect(stub).toHaveBeenCalledWith( - 'npm', - `view ${packageName} time --json`.split(' '), - expect.anything(), - expect.anything(), - expect.anything() + expect.objectContaining({ + command: 'npm', + args: `view ${packageName} time --json`.split(' ') + }) ); expect(versions).toHaveLength(4); expect(versions).toMatchObject(['0.0.0', '1.4.0', '1.4.1', '2.4.0-alpha.1']); }); - it('publishedVersions gets versions when package time is not available', () => { + it('publishedVersions gets versions when package time is not available', async () => { const json: string = `[ "0.0.0", "1.4.0", "1.4.1", "2.4.0-alpha.1" ]`; - stub.mockImplementationOnce(() => ''); - stub.mockImplementationOnce(() => json); + stub.mockImplementationOnce(() => + Promise.resolve({ stdout: '', stderr: '', signal: undefined, exitCode: 0 }) + ); + stub.mockImplementationOnce(() => + Promise.resolve({ stdout: json, stderr: '', signal: undefined, exitCode: 0 }) + ); - const versions: string[] = Npm.publishedVersions(packageName, __dirname, process.env); + const versions: string[] = await Npm.getPublishedVersionsAsync(packageName, __dirname, process.env); expect(stub).toHaveBeenCalledWith( - 'npm', - `view ${packageName} time --json`.split(' '), - expect.anything(), - expect.anything(), - expect.anything() + expect.objectContaining({ + command: 'npm', + args: `view ${packageName} time --json`.split(' ') + }) ); expect(stub).toHaveBeenCalledWith( - 'npm', - `view ${packageName} versions --json`.split(' '), - expect.anything(), - expect.anything(), - expect.anything() + expect.objectContaining({ + command: 'npm', + args: `view ${packageName} versions --json`.split(' ') + }) ); expect(versions).toHaveLength(4); diff --git a/libraries/rush-lib/src/utilities/test/Stopwatch.test.ts b/libraries/rush-lib/src/utilities/test/Stopwatch.test.ts index 931707e2385..f15e9fcfd5f 100644 --- a/libraries/rush-lib/src/utilities/test/Stopwatch.test.ts +++ b/libraries/rush-lib/src/utilities/test/Stopwatch.test.ts @@ -111,4 +111,29 @@ describe(Stopwatch.name, () => { expect(watch.duration).toEqual(1); expect(watch.duration).toEqual(2); }); + + it('uses startTimeOverride when provided to start()', () => { + const watch: Stopwatch = new Stopwatch(pseudoTimeMilliseconds([5000])); + watch.start(2000); + expect(watch.startTime).toEqual(2000); + watch.stop(); + expect(watch.duration).toEqual(3); + }); + + it('uses startTimeOverride with the static start() shorthand', () => { + const watch: Stopwatch = Stopwatch.start(1000); + expect(watch.startTime).toEqual(1000); + expect(watch.state).toEqual(StopwatchState.Started); + }); + + it('ignores getTime for start when startTimeOverride is provided', () => { + const getTime = pseudoTimeMilliseconds([10000]); + const watch: Stopwatch = new Stopwatch(getTime); + watch.start(3000); + // startTime should be the override, not a value from getTime + expect(watch.startTime).toEqual(3000); + // stop still uses getTime + watch.stop(); + expect(watch.duration).toEqual(7); + }); }); diff --git a/libraries/rush-lib/src/utilities/test/Utilities.test.ts b/libraries/rush-lib/src/utilities/test/Utilities.test.ts index ed9c75f4dcc..cd3e79c85d0 100644 --- a/libraries/rush-lib/src/utilities/test/Utilities.test.ts +++ b/libraries/rush-lib/src/utilities/test/Utilities.test.ts @@ -1,7 +1,26 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { IDisposable, Utilities } from '../Utilities'; +import { type IDisposable, Utilities } from '../Utilities'; + +function withComSpec(value: string | undefined, callback: () => T): T { + const originalValue: string | undefined = process.env.comspec; + try { + if (value === undefined) { + delete process.env.comspec; + } else { + process.env.comspec = value; + } + + return callback(); + } finally { + if (originalValue === undefined) { + delete process.env.comspec; + } else { + process.env.comspec = originalValue; + } + } +} describe(Utilities.name, () => { describe(Utilities.usingAsync.name, () => { @@ -58,4 +77,37 @@ describe(Utilities.name, () => { expect(disposed).toEqual(false); }); }); + + describe(Utilities._convertCommandAndArgsToShell.name, () => { + it('builds a POSIX shell command from a string', () => { + const result = withComSpec(undefined, () => Utilities._convertCommandAndArgsToShell('npm test', false)); + + expect(result).toMatchSnapshot(); + }); + + it('builds a Windows shell command from a string', () => { + const result = withComSpec('cmd.exe', () => Utilities._convertCommandAndArgsToShell('npm test', true)); + + expect(result).toMatchSnapshot(); + }); + + it('keeps unescaped args when wrapping a POSIX command object', () => { + const result = withComSpec(undefined, () => + Utilities._convertCommandAndArgsToShell({ command: 'foo bar', args: ['baz qux', '--flag'] }, false) + ); + + expect(result).toMatchSnapshot(); + }); + + it('keeps unescaped args when wrapping a Windows command object', () => { + const result = withComSpec('cmd.exe', () => + Utilities._convertCommandAndArgsToShell( + { command: 'weird "cmd"', args: ['space arg', 'quote "arg"'] }, + true + ) + ); + + expect(result).toMatchSnapshot(); + }); + }); }); diff --git a/libraries/rush-lib/src/utilities/test/WebClient.test.ts b/libraries/rush-lib/src/utilities/test/WebClient.test.ts new file mode 100644 index 00000000000..f96a45ba5bb --- /dev/null +++ b/libraries/rush-lib/src/utilities/test/WebClient.test.ts @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { createServer, type Server } from 'node:http'; +import { Readable } from 'node:stream'; + +import { WebClient } from '../WebClient'; + +describe(WebClient.name, () => { + describe(WebClient.mergeHeaders.name, () => { + it('should merge headers', () => { + const target: Record = { header1: 'value1' }; + const source: Record = { header2: 'value2' }; + + WebClient.mergeHeaders(target, source); + expect(target).toMatchSnapshot(); + }); + + it('should handle an empty source', () => { + const target: Record = { header1: 'value1' }; + const source: Record = {}; + + WebClient.mergeHeaders(target, source); + expect(target).toMatchSnapshot(); + }); + + it('should handle an empty target', () => { + const target: Record = {}; + const source: Record = { header2: 'value2' }; + + WebClient.mergeHeaders(target, source); + expect(target).toMatchSnapshot(); + }); + + it('should handle both empty', () => { + const target: Record = {}; + const source: Record = {}; + + WebClient.mergeHeaders(target, source); + expect(target).toMatchSnapshot(); + }); + + it('should handle overwriting values', () => { + const target: Record = { header1: 'value1' }; + const source: Record = { header1: 'value2' }; + + WebClient.mergeHeaders(target, source); + expect(target).toMatchSnapshot(); + }); + + it('should handle a JS object as the source', () => { + const target: Record = { header1: 'value1' }; + + WebClient.mergeHeaders(target, { header2: 'value2' }); + expect(target).toMatchSnapshot(); + }); + }); + + describe(WebClient.prototype.fetchAsync.name, () => { + it('destroys a streamed request body if the request errors', async () => { + const server: Server = createServer((request) => { + request.socket.destroy(); + }); + await new Promise((resolve, reject) => { + server.once('error', reject); + server.listen(0, '127.0.0.1', () => resolve()); + }); + + const address = server.address(); + if (!address || typeof address === 'string') { + throw new Error('Expected a TCP server address'); + } + + const webClient: WebClient = new WebClient(); + const body: Readable = new Readable({ + read() { + this.push(Buffer.alloc(64 * 1024)); + } + }); + + await expect( + webClient.fetchAsync(`http://127.0.0.1:${address.port}`, { + verb: 'PUT', + body + }) + ).rejects.toThrow(); + expect(body.destroyed).toBe(true); + + await new Promise((resolve, reject) => { + server.close((error) => (error ? reject(error) : resolve())); + }); + }); + }); +}); diff --git a/libraries/rush-lib/src/utilities/test/__snapshots__/Utilities.test.ts.snap b/libraries/rush-lib/src/utilities/test/__snapshots__/Utilities.test.ts.snap index 019edf70019..86af4415898 100644 --- a/libraries/rush-lib/src/utilities/test/__snapshots__/Utilities.test.ts.snap +++ b/libraries/rush-lib/src/utilities/test/__snapshots__/Utilities.test.ts.snap @@ -1,4 +1,48 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`Utilities _convertCommandAndArgsToShell builds a POSIX shell command from a string 1`] = ` +Object { + "args": Array [ + "-c", + "npm test", + ], + "command": "sh", +} +`; + +exports[`Utilities _convertCommandAndArgsToShell builds a Windows shell command from a string 1`] = ` +Object { + "args": Array [ + "/d", + "/s", + "/c", + "npm test", + ], + "command": "cmd.exe", +} +`; + +exports[`Utilities _convertCommandAndArgsToShell keeps unescaped args when wrapping a POSIX command object 1`] = ` +Object { + "args": Array [ + "-c", + "\\"foo bar\\" \\"baz qux\\" --flag", + ], + "command": "sh", +} +`; + +exports[`Utilities _convertCommandAndArgsToShell keeps unescaped args when wrapping a Windows command object 1`] = ` +Object { + "args": Array [ + "/d", + "/s", + "/c", + "\\"weird \\"\\"cmd\\"\\"\\" \\"space arg\\" \\"quote \\"\\"arg\\"\\"\\"", + ], + "command": "cmd.exe", +} +`; exports[`Utilities usingAsync Disposes correctly after the operation throws an exception 1`] = `[Error: operation threw]`; diff --git a/libraries/rush-lib/src/utilities/test/__snapshots__/WebClient.test.ts.snap b/libraries/rush-lib/src/utilities/test/__snapshots__/WebClient.test.ts.snap new file mode 100644 index 00000000000..2c86c922852 --- /dev/null +++ b/libraries/rush-lib/src/utilities/test/__snapshots__/WebClient.test.ts.snap @@ -0,0 +1,35 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`WebClient mergeHeaders should handle a JS object as the source 1`] = ` +Object { + "header1": "value1", + "header2": "value2", +} +`; + +exports[`WebClient mergeHeaders should handle an empty source 1`] = ` +Object { + "header1": "value1", +} +`; + +exports[`WebClient mergeHeaders should handle an empty target 1`] = ` +Object { + "header2": "value2", +} +`; + +exports[`WebClient mergeHeaders should handle both empty 1`] = `Object {}`; + +exports[`WebClient mergeHeaders should handle overwriting values 1`] = ` +Object { + "header1": "value2", +} +`; + +exports[`WebClient mergeHeaders should merge headers 1`] = ` +Object { + "header1": "value1", + "header2": "value2", +} +`; diff --git a/libraries/rush-lib/src/utilities/test/__snapshots__/npmrcUtilities.test.ts.snap b/libraries/rush-lib/src/utilities/test/__snapshots__/npmrcUtilities.test.ts.snap new file mode 100644 index 00000000000..99093097dac --- /dev/null +++ b/libraries/rush-lib/src/utilities/test/__snapshots__/npmrcUtilities.test.ts.snap @@ -0,0 +1,292 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`npmrcUtilities trimNpmrcFileLines With npm-incompatible properties filtering does not filter when filterNpmIncompatibleProperties is false 1`] = ` +Array [ + "registry=https://registry.npmjs.org/", + "email=test@example.com", + "hoist=false", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With npm-incompatible properties filtering filters out deprecated npm properties 1`] = ` +Array [ + "registry=https://registry.npmjs.org/", + "; UNSUPPORTED BY NPM: email=test@example.com", + "; UNSUPPORTED BY NPM: publish-branch=main", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With npm-incompatible properties filtering filters out pnpm-specific hoisting properties 1`] = ` +Array [ + "registry=https://registry.npmjs.org/", + "; UNSUPPORTED BY NPM: hoist=false", + "; UNSUPPORTED BY NPM: hoist-pattern[]=*eslint*", + "; UNSUPPORTED BY NPM: public-hoist-pattern[]=", + "; UNSUPPORTED BY NPM: shamefully-hoist=true", + "always-auth=false", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With npm-incompatible properties filtering filters out pnpm-specific registry-scoped properties 1`] = ` +Array [ + "registry=https://registry.npmjs.org/", + "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}", + "; UNSUPPORTED BY NPM: //my-registry.com/:tokenHelper=/path/to/helper", + "; UNSUPPORTED BY NPM: //other-registry.com/:urlTokenHelper=/path/to/url-helper", + "//registry.npmjs.org/:always-auth=true", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With npm-incompatible properties filtering preserves registry-scoped auth tokens 1`] = ` +Array [ + "registry=https://registry.npmjs.org/", + "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}", + "//my-registry.com/:_authToken=\${MY_TOKEN}", + "; UNSUPPORTED BY NPM: email=test@example.com", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With npm-incompatible properties filtering preserves registry-scoped configurations 1`] = ` +Array [ + "registry=https://registry.npmjs.org/", + "//registry.npmjs.org/:always-auth=true", + "//my-registry.com/:_authToken=\${MY_TOKEN}", + "; UNSUPPORTED BY NPM: hoist=false", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With npm-incompatible properties filtering preserves standard npm properties 1`] = ` +Array [ + "registry=https://registry.npmjs.org/", + "always-auth=false", + "strict-ssl=true", + "save-exact=true", + "package-lock=true", + "; UNSUPPORTED BY NPM: hoist=false", + "; UNSUPPORTED BY NPM: email=test@example.com", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 1`] = ` +Array [ + "var1=\${foo-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 2`] = ` +Array [ + "var1=\${foo-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 3`] = ` +Array [ + "var1=\${foo:-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 4`] = ` +Array [ + "var1=\${foo:-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 5`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}-\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 6`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}-\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 7`] = ` +Array [ + "var1=\${foo}-\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 8`] = ` +Array [ + "var1=\${foo:-fallback_value}-\${bar-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable without a fallback 1`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable without a fallback 2`] = ` +Array [ + "var1=\${foo}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports malformed lines 1`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo_fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports malformed lines 2`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports malformed lines 3`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:_fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports malformed lines 4`] = ` +Array [ + "var1=\${foo", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports multiple lines 1`] = ` +Array [ + "var1=\${foo}", + "; MISSING ENVIRONMENT VARIABLE: var2=\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports multiple lines 2`] = ` +Array [ + "var1=\${foo}", + "var2=\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports multiple lines 3`] = ` +Array [ + "var1=\${foo}", + "var2=\${bar-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports multiple lines 4`] = ` +Array [ + "var1=\${foo:-fallback_value}", + "var2=\${bar-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 1`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 2`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 3`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 4`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 5`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}-\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 6`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}-\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 7`] = ` +Array [ + "var1=\${foo}-\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 8`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:-fallback_value}-\${bar-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable without a fallback 1`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable without a fallback 2`] = ` +Array [ + "var1=\${foo}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports malformed lines 1`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo_fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports malformed lines 2`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports malformed lines 3`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:_fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports malformed lines 4`] = ` +Array [ + "var1=\${foo", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports multiple lines 1`] = ` +Array [ + "var1=\${foo}", + "; MISSING ENVIRONMENT VARIABLE: var2=\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports multiple lines 2`] = ` +Array [ + "var1=\${foo}", + "var2=\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports multiple lines 3`] = ` +Array [ + "var1=\${foo}", + "; MISSING ENVIRONMENT VARIABLE: var2=\${bar-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports multiple lines 4`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:-fallback_value}", + "; MISSING ENVIRONMENT VARIABLE: var2=\${bar-fallback_value}", +] +`; diff --git a/libraries/rush-lib/src/utilities/test/global-teardown.ts b/libraries/rush-lib/src/utilities/test/global-teardown.ts new file mode 100644 index 00000000000..49f18a332d9 --- /dev/null +++ b/libraries/rush-lib/src/utilities/test/global-teardown.ts @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { FileSystem } from '@rushstack/node-core-library'; + +import { TEST_REPO_FOLDER_PATH } from '../../cli/test/TestUtils'; + +export default async function globalTeardown(): Promise { + await FileSystem.deleteFolderAsync(TEST_REPO_FOLDER_PATH); +} diff --git a/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts b/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts new file mode 100644 index 00000000000..3c84a54cfc9 --- /dev/null +++ b/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts @@ -0,0 +1,209 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { trimNpmrcFileLines } from '../npmrcUtilities'; + +describe('npmrcUtilities', () => { + function runTests(supportEnvVarFallbackSyntax: boolean): void { + it('handles empty input', () => { + expect(trimNpmrcFileLines([], {}, supportEnvVarFallbackSyntax)).toEqual([]); + }); + + it('supports a variable without a fallback', () => { + expect(trimNpmrcFileLines(['var1=${foo}'], {}, supportEnvVarFallbackSyntax)).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo}'], { foo: 'test' }, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + }); + + it('supports a variable with a fallback', () => { + expect( + trimNpmrcFileLines(['var1=${foo-fallback_value}'], {}, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo-fallback_value}'], { foo: 'test' }, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo:-fallback_value}'], {}, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo:-fallback_value}'], { foo: 'test' }, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo}-${bar}'], { foo: 'test' }, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo}-${bar}'], { bar: 'test' }, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo}-${bar}'], { foo: 'test', bar: 'test' }, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines( + ['var1=${foo:-fallback_value}-${bar-fallback_value}'], + {}, + supportEnvVarFallbackSyntax + ) + ).toMatchSnapshot(); + }); + + it('supports multiple lines', () => { + expect( + trimNpmrcFileLines(['var1=${foo}', 'var2=${bar}'], { foo: 'test' }, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines( + ['var1=${foo}', 'var2=${bar}'], + { foo: 'test', bar: 'test' }, + supportEnvVarFallbackSyntax + ) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines( + ['var1=${foo}', 'var2=${bar-fallback_value}'], + { foo: 'test' }, + supportEnvVarFallbackSyntax + ) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines( + ['var1=${foo:-fallback_value}', 'var2=${bar-fallback_value}'], + {}, + supportEnvVarFallbackSyntax + ) + ).toMatchSnapshot(); + }); + + it('supports malformed lines', () => { + // Malformed + expect( + trimNpmrcFileLines(['var1=${foo_fallback_value}'], {}, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo:fallback_value}'], {}, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo:_fallback_value}'], {}, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect(trimNpmrcFileLines(['var1=${foo'], {}, supportEnvVarFallbackSyntax)).toMatchSnapshot(); + }); + } + + describe(trimNpmrcFileLines.name, () => { + describe('With support for env var fallback syntax', () => runTests(true)); + describe('Without support for env var fallback syntax', () => runTests(false)); + + describe('With npm-incompatible properties filtering', () => { + const supportEnvVarFallbackSyntax = false; + const filterNpmIncompatibleProperties = true; + + it('filters out pnpm-specific hoisting properties', () => { + expect( + trimNpmrcFileLines( + [ + 'registry=https://registry.npmjs.org/', + 'hoist=false', + 'hoist-pattern[]=*eslint*', + 'public-hoist-pattern[]=', + 'shamefully-hoist=true', + 'always-auth=false' + ], + {}, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties + ) + ).toMatchSnapshot(); + }); + + it('filters out deprecated npm properties', () => { + expect( + trimNpmrcFileLines( + ['registry=https://registry.npmjs.org/', 'email=test@example.com', 'publish-branch=main'], + {}, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties + ) + ).toMatchSnapshot(); + }); + + it('preserves registry-scoped auth tokens', () => { + expect( + trimNpmrcFileLines( + [ + 'registry=https://registry.npmjs.org/', + '//registry.npmjs.org/:_authToken=${NPM_TOKEN}', + '//my-registry.com/:_authToken=${MY_TOKEN}', + 'email=test@example.com' + ], + { NPM_TOKEN: 'abc123', MY_TOKEN: 'xyz789' }, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties + ) + ).toMatchSnapshot(); + }); + + it('preserves registry-scoped configurations', () => { + expect( + trimNpmrcFileLines( + [ + 'registry=https://registry.npmjs.org/', + '//registry.npmjs.org/:always-auth=true', + '//my-registry.com/:_authToken=${MY_TOKEN}', + 'hoist=false' + ], + { MY_TOKEN: 'xyz789' }, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties + ) + ).toMatchSnapshot(); + }); + + it('does not filter when filterNpmIncompatibleProperties is false', () => { + expect( + trimNpmrcFileLines( + ['registry=https://registry.npmjs.org/', 'email=test@example.com', 'hoist=false'], + {}, + supportEnvVarFallbackSyntax, + false + ) + ).toMatchSnapshot(); + }); + + it('preserves standard npm properties', () => { + expect( + trimNpmrcFileLines( + [ + 'registry=https://registry.npmjs.org/', + 'always-auth=false', + 'strict-ssl=true', + 'save-exact=true', + 'package-lock=true', + 'hoist=false', + 'email=test@example.com' + ], + {}, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties + ) + ).toMatchSnapshot(); + }); + + it('filters out pnpm-specific registry-scoped properties', () => { + expect( + trimNpmrcFileLines( + [ + 'registry=https://registry.npmjs.org/', + '//registry.npmjs.org/:_authToken=${NPM_TOKEN}', + '//my-registry.com/:tokenHelper=/path/to/helper', + '//other-registry.com/:urlTokenHelper=/path/to/url-helper', + '//registry.npmjs.org/:always-auth=true' + ], + { NPM_TOKEN: 'abc123' }, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties + ) + ).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/libraries/rush-lib/src/utilities/test/objectUtilities.test.ts b/libraries/rush-lib/src/utilities/test/objectUtilities.test.ts new file mode 100644 index 00000000000..ce023119369 --- /dev/null +++ b/libraries/rush-lib/src/utilities/test/objectUtilities.test.ts @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { cloneDeep, merge, removeNullishProps } from '../objectUtilities'; + +describe('objectUtilities', () => { + describe(cloneDeep.name, () => { + function testClone(source: unknown): void { + const clone: unknown = cloneDeep(source); + expect(clone).toEqual(source); + expect(clone).not.toBe(source); + } + + it('can clone primitives', () => { + expect(cloneDeep(1)).toEqual(1); + expect(cloneDeep('a')).toEqual('a'); + expect(cloneDeep(true)).toEqual(true); + expect(cloneDeep(undefined)).toEqual(undefined); + expect(cloneDeep(null)).toEqual(null); + }); + + it('can clone arrays', () => { + testClone([]); + testClone([1]); + testClone([1, 2]); + testClone([1, 2, 3]); + }); + + it('can clone objects', () => { + testClone({}); + testClone({ a: 1 }); + testClone({ a: 1, b: 1 }); + testClone({ a: 1, b: 2 }); + + const a: Record = { a: 1 }; + testClone({ a, b: a }); + }); + + it('can clone nested objects', () => { + testClone({ a: { b: 1 } }); + }); + + it("can't clone objects with circular references", () => { + const a: Record = { a: 1 }; + a.b = a; + expect(() => cloneDeep(a)).toThrowErrorMatchingInlineSnapshot(`"Circular reference detected"`); + + const b: unknown[] = []; + b.push(b); + expect(() => cloneDeep(b)).toThrowErrorMatchingInlineSnapshot(`"Circular reference detected"`); + }); + }); + + describe(merge.name, () => { + it('will overwrite with primitives', () => { + expect(merge({}, 2)).toEqual(2); + expect(merge([], 2)).toEqual(2); + expect(merge({}, null)).toEqual(null); + expect(merge([], null)).toEqual(null); + expect(merge({}, undefined)).toEqual(undefined); + expect(merge([], undefined)).toEqual(undefined); + }); + + it('will overwrite with arrays', () => { + expect(merge({}, [1])).toEqual([1]); + expect(merge([], [1])).toEqual([1]); + expect(merge({ a: { b: 1 } }, { a: [1] })).toEqual({ a: [1] }); + }); + + it('will merge with objects', () => { + expect(merge({}, { a: 1 })).toEqual({ a: 1 }); + expect(merge({ a: 1 }, { b: 2 })).toEqual({ a: 1, b: 2 }); + expect(merge({ a: 1 }, { a: 2 })).toEqual({ a: 2 }); + expect(merge({ a: { b: 1 } }, { a: { c: 2 } })).toEqual({ a: { b: 1, c: 2 } }); + }); + }); + + describe(removeNullishProps.name, () => { + it('can remove undefined and null properties', () => { + expect(removeNullishProps({ a: 1, b: undefined })).toEqual({ a: 1 }); + expect(removeNullishProps({ a: 1, b: null })).toEqual({ a: 1 }); + expect(removeNullishProps({ a: 1, b: undefined, c: null })).toEqual({ a: 1 }); + }); + }); +}); diff --git a/libraries/rush-lib/tsconfig.json b/libraries/rush-lib/tsconfig.json index 6b9fa71f69e..78bc41dbb41 100644 --- a/libraries/rush-lib/tsconfig.json +++ b/libraries/rush-lib/tsconfig.json @@ -1,9 +1,10 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "types": ["heft-jest", "node", "webpack-env"], + "types": ["jest", "node", "webpack-env"], "skipLibCheck": true, "resolveJsonModule": true, - "outDir": "lib-commonjs" + "outDir": "lib-intermediate-commonjs", + "declarationDir": "lib-dts" } } diff --git a/libraries/rush-lib/webpack.config.js b/libraries/rush-lib/webpack.config.js index 9857e2de4ea..6952beba9dc 100644 --- a/libraries/rush-lib/webpack.config.js +++ b/libraries/rush-lib/webpack.config.js @@ -4,7 +4,7 @@ const webpack = require('webpack'); const { PackageJsonLookup } = require('@rushstack/node-core-library'); const { PreserveDynamicRequireWebpackPlugin } = require('@rushstack/webpack-preserve-dynamic-require-plugin'); const { DeepImportsPlugin } = require('@rushstack/webpack-deep-imports-plugin'); -const PathConstants = require('./lib-commonjs/utilities/PathConstants'); +const PathConstants = require('./lib-intermediate-commonjs/utilities/PathConstants'); const SCRIPT_ENTRY_OPTIONS = { filename: `${PathConstants.scriptsFolderName}/[name]` @@ -42,6 +42,18 @@ module.exports = () => { }), ...extraPlugins ], + module: { + rules: [ + { + // These files have side effects (e.g. setting process.env variables) that must not + // be tree-shaken, even though they are only imported for their side effects. + // The package.json "sideEffects" field references the shipped folder names (lib-commonjs, + // lib-esm), but webpack reads from the intermediate build folders (lib-intermediate-esm). + test: /[\\/](SetRushLibPath|start|startx|start-pnpm)\.js$/, + sideEffects: true + } + ] + }, externals: [ ({ request }, callback) => { let packageName; @@ -75,18 +87,21 @@ module.exports = () => { const configurations = [ generateConfiguration( { - 'rush-lib': `${__dirname}/lib-esnext/index.js`, - start: `${__dirname}/lib-esnext/start.js`, - startx: `${__dirname}/lib-esnext/startx.js`, - 'start-pnpm': `${__dirname}/lib-esnext/start-pnpm.js` + 'rush-lib': `${__dirname}/lib-intermediate-esm/index.js`, + start: `${__dirname}/lib-intermediate-esm/start.js`, + startx: `${__dirname}/lib-intermediate-esm/startx.js`, + 'start-pnpm': `${__dirname}/lib-intermediate-esm/start-pnpm.js` }, [ new DeepImportsPlugin({ - path: `${__dirname}/temp/rush-lib-manifest.json`, - inFolderName: 'lib-esnext', - outFolderName: 'lib', + // A manifest will be produced for each entry point, so since this compilation has multiple entry points, + // it needs to specify a template for the manifest filename. + // Otherwise webpack will throw an error about multiple writes to the same manifest file. + path: `${__dirname}/temp/build/webpack-dll/[name].json`, + inFolderName: 'lib-intermediate-esm', + outFolderName: 'lib-commonjs', pathsToIgnore: ['utilities/prompts/SearchListPrompt.js'], - dTsFilesInputFolderName: 'lib-commonjs' + dTsFilesInputFolderName: 'lib-dts' }) ], { @@ -103,23 +118,27 @@ module.exports = () => { ), generateConfiguration({ [PathConstants.pnpmfileShimFilename]: { - import: `${__dirname}/lib-esnext/logic/pnpm/PnpmfileShim.js`, + import: `${__dirname}/lib-intermediate-esm/logic/pnpm/PnpmfileShim.js`, + ...SCRIPT_ENTRY_OPTIONS + }, + [PathConstants.subspacePnpmfileShimFilename]: { + import: `${__dirname}/lib-intermediate-esm/logic/pnpm/SubspaceGlobalPnpmfileShim.js`, ...SCRIPT_ENTRY_OPTIONS }, [PathConstants.installRunScriptFilename]: { - import: `${__dirname}/lib-esnext/scripts/install-run.js`, + import: `${__dirname}/lib-intermediate-esm/scripts/install-run.js`, ...SCRIPT_ENTRY_OPTIONS }, [PathConstants.installRunRushScriptFilename]: { - import: `${__dirname}/lib-esnext/scripts/install-run-rush.js`, + import: `${__dirname}/lib-intermediate-esm/scripts/install-run-rush.js`, ...SCRIPT_ENTRY_OPTIONS }, [PathConstants.installRunRushxScriptFilename]: { - import: `${__dirname}/lib-esnext/scripts/install-run-rushx.js`, + import: `${__dirname}/lib-intermediate-esm/scripts/install-run-rushx.js`, ...SCRIPT_ENTRY_OPTIONS }, [PathConstants.installRunRushPnpmScriptFilename]: { - import: `${__dirname}/lib-esnext/scripts/install-run-rush-pnpm.js`, + import: `${__dirname}/lib-intermediate-esm/scripts/install-run-rush-pnpm.js`, ...SCRIPT_ENTRY_OPTIONS } }) diff --git a/libraries/rush-pnpm-kit-v10/.npmignore b/libraries/rush-pnpm-kit-v10/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/libraries/rush-pnpm-kit-v10/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/rush-pnpm-kit-v10/CHANGELOG.json b/libraries/rush-pnpm-kit-v10/CHANGELOG.json new file mode 100644 index 00000000000..3f14d08a822 --- /dev/null +++ b/libraries/rush-pnpm-kit-v10/CHANGELOG.json @@ -0,0 +1,399 @@ +{ + "name": "@rushstack/rush-pnpm-kit-v10", + "entries": [ + { + "version": "0.2.23", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.23", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "0.2.22", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.22", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "0.2.21", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.21", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "0.2.20", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.20", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "0.2.19", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.19", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "0.2.18", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.18", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "0.2.17", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.17", + "date": "Mon, 20 Apr 2026 15:15:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "0.2.16", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.16", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "0.2.15", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.15", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.14", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.13", + "date": "Fri, 10 Apr 2026 22:46:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.12", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.11", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.10", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.9", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.8", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.7", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.6", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.5", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.4", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.3", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.2", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.1", + "date": "Thu, 19 Feb 2026 01:30:06 GMT", + "comments": { + "patch": [ + { + "comment": "Filter files from publish." + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.2.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "0.1.7", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.1.7", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "0.1.6", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.1.6", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "0.1.5", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.1.5", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "0.1.4", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.1.4", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "0.1.3", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.1.3", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.1.2", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.1.1", + "date": "Mon, 05 Jan 2026 16:12:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/rush-pnpm-kit-v10_v0.1.0", + "date": "Wed, 24 Dec 2025 01:12:52 GMT", + "comments": { + "minor": [ + { + "comment": "Set up the `@rushstack/rush-pnpm-kit-v10` package to bundle all pnpm v10 related packages together." + } + ] + } + } + ] +} diff --git a/libraries/rush-pnpm-kit-v10/CHANGELOG.md b/libraries/rush-pnpm-kit-v10/CHANGELOG.md new file mode 100644 index 00000000000..d27f7af82c7 --- /dev/null +++ b/libraries/rush-pnpm-kit-v10/CHANGELOG.md @@ -0,0 +1,172 @@ +# Change Log - @rushstack/rush-pnpm-kit-v10 + +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 0.2.23 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 0.2.22 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 0.2.21 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.2.20 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.2.19 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 0.2.18 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.2.17 +Mon, 20 Apr 2026 15:15:25 GMT + +_Version update only_ + +## 0.2.16 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 0.2.15 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 0.2.14 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.2.13 +Fri, 10 Apr 2026 22:46:35 GMT + +_Version update only_ + +## 0.2.12 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.2.11 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 0.2.10 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 0.2.9 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.2.8 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 0.2.7 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.2.6 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 0.2.5 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.2.4 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 0.2.3 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 0.2.2 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.2.1 +Thu, 19 Feb 2026 01:30:06 GMT + +### Patches + +- Filter files from publish. + +## 0.2.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.1.7 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 0.1.6 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 0.1.5 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 0.1.4 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 0.1.3 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 0.1.2 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 0.1.1 +Mon, 05 Jan 2026 16:12:50 GMT + +_Version update only_ + +## 0.1.0 +Wed, 24 Dec 2025 01:12:52 GMT + +### Minor changes + +- Set up the `@rushstack/rush-pnpm-kit-v10` package to bundle all pnpm v10 related packages together. + diff --git a/libraries/rush-pnpm-kit-v10/LICENSE b/libraries/rush-pnpm-kit-v10/LICENSE new file mode 100644 index 00000000000..5cdb73542f0 --- /dev/null +++ b/libraries/rush-pnpm-kit-v10/LICENSE @@ -0,0 +1,24 @@ +@rushstack/rush-pnpm-kit-v10 + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/libraries/rush-pnpm-kit-v10/README.md b/libraries/rush-pnpm-kit-v10/README.md new file mode 100644 index 00000000000..5880e3e0ae6 --- /dev/null +++ b/libraries/rush-pnpm-kit-v10/README.md @@ -0,0 +1,14 @@ +## @microsoft/rush-pnpm-kit-v10 + +This is a companion package for the Rush tool. See the +[@microsoft/rush](https://www.npmjs.com/package/@microsoft/rush) +package for details. + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/libraries/rush-pnpm-kit-v10/CHANGELOG.md) - Find + out what's new in the latest version +- [API Reference](https://api.rushstack.io/pages/rush-lib/) + +Rush is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/libraries/rush-pnpm-kit-v10/config/api-extractor.json b/libraries/rush-pnpm-kit-v10/config/api-extractor.json new file mode 100644 index 00000000000..3dbb76c0e6f --- /dev/null +++ b/libraries/rush-pnpm-kit-v10/config/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json" +} diff --git a/libraries/rush-pnpm-kit-v10/config/rig.json b/libraries/rush-pnpm-kit-v10/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/libraries/rush-pnpm-kit-v10/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/libraries/rush-pnpm-kit-v10/eslint.config.js b/libraries/rush-pnpm-kit-v10/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/libraries/rush-pnpm-kit-v10/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/rush-pnpm-kit-v10/package.json b/libraries/rush-pnpm-kit-v10/package.json new file mode 100644 index 00000000000..e8ef33140bc --- /dev/null +++ b/libraries/rush-pnpm-kit-v10/package.json @@ -0,0 +1,47 @@ +{ + "name": "@rushstack/rush-pnpm-kit-v10", + "version": "0.2.23", + "description": "rush pnpm kit v10", + "license": "MIT", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/rush-pnpm-kit-v10.d.ts", + "exports": { + ".": { + "types": "./dist/rush-pnpm-kit-v10.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "scripts": { + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean", + "build": "heft build --clean", + "test": "heft test --clean" + }, + "dependencies": { + "@pnpm/dependency-path-pnpm-v10": "npm:@pnpm/dependency-path@~1000.0.9", + "@pnpm/lockfile.fs-pnpm-lock-v9": "npm:@pnpm/lockfile.fs@~1001.1.11", + "@pnpm/logger": "~1001.0.0" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" + }, + "sideEffects": false +} diff --git a/libraries/rush-pnpm-kit-v10/src/dependencyPath.ts b/libraries/rush-pnpm-kit-v10/src/dependencyPath.ts new file mode 100644 index 00000000000..a5995ab7e3d --- /dev/null +++ b/libraries/rush-pnpm-kit-v10/src/dependencyPath.ts @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { depPathToFilename, indexOfPeersSuffix, parse, removeSuffix } from '@pnpm/dependency-path-pnpm-v10'; +import type { DependencyPath } from '@pnpm/dependency-path-pnpm-v10'; + +export { depPathToFilename, indexOfPeersSuffix, parse, removeSuffix, DependencyPath }; diff --git a/libraries/rush-pnpm-kit-v10/src/index.ts b/libraries/rush-pnpm-kit-v10/src/index.ts new file mode 100644 index 00000000000..bb8a9dcdfa9 --- /dev/null +++ b/libraries/rush-pnpm-kit-v10/src/index.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export * as dependencyPath from './dependencyPath'; +export * as lockfileFs from './lockfileFs'; +export * as logger from './logger'; diff --git a/libraries/rush-pnpm-kit-v10/src/lockfileFs.ts b/libraries/rush-pnpm-kit-v10/src/lockfileFs.ts new file mode 100644 index 00000000000..bfed13bf89f --- /dev/null +++ b/libraries/rush-pnpm-kit-v10/src/lockfileFs.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { readWantedLockfile } from '@pnpm/lockfile.fs-pnpm-lock-v9'; + +export { readWantedLockfile }; diff --git a/libraries/rush-pnpm-kit-v10/src/logger.ts b/libraries/rush-pnpm-kit-v10/src/logger.ts new file mode 100644 index 00000000000..6d0c71dee47 --- /dev/null +++ b/libraries/rush-pnpm-kit-v10/src/logger.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { LogBase } from '@pnpm/logger'; + +export { LogBase }; diff --git a/libraries/rush-pnpm-kit-v10/tsconfig.json b/libraries/rush-pnpm-kit-v10/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/libraries/rush-pnpm-kit-v10/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/libraries/rush-pnpm-kit-v8/.npmignore b/libraries/rush-pnpm-kit-v8/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/libraries/rush-pnpm-kit-v8/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/rush-pnpm-kit-v8/CHANGELOG.json b/libraries/rush-pnpm-kit-v8/CHANGELOG.json new file mode 100644 index 00000000000..75c8791f984 --- /dev/null +++ b/libraries/rush-pnpm-kit-v8/CHANGELOG.json @@ -0,0 +1,399 @@ +{ + "name": "@rushstack/rush-pnpm-kit-v8", + "entries": [ + { + "version": "0.2.23", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.23", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "0.2.22", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.22", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "0.2.21", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.21", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "0.2.20", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.20", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "0.2.19", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.19", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "0.2.18", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.18", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "0.2.17", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.17", + "date": "Mon, 20 Apr 2026 15:15:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "0.2.16", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.16", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "0.2.15", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.15", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.14", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.13", + "date": "Fri, 10 Apr 2026 22:46:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.12", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.11", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.10", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.9", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.8", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.7", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.6", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.5", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.4", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.3", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.2", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.1", + "date": "Thu, 19 Feb 2026 01:30:06 GMT", + "comments": { + "patch": [ + { + "comment": "Filter files from publish." + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.2.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "0.1.7", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.1.7", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "0.1.6", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.1.6", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "0.1.5", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.1.5", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "0.1.4", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.1.4", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "0.1.3", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.1.3", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.1.2", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.1.1", + "date": "Mon, 05 Jan 2026 16:12:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/rush-pnpm-kit-v8_v0.1.0", + "date": "Wed, 24 Dec 2025 01:12:52 GMT", + "comments": { + "minor": [ + { + "comment": "Set up the `@rushstack/rush-pnpm-kit-v8` package to bundle all pnpm v8 related packages together." + } + ] + } + } + ] +} diff --git a/libraries/rush-pnpm-kit-v8/CHANGELOG.md b/libraries/rush-pnpm-kit-v8/CHANGELOG.md new file mode 100644 index 00000000000..6331d272c28 --- /dev/null +++ b/libraries/rush-pnpm-kit-v8/CHANGELOG.md @@ -0,0 +1,172 @@ +# Change Log - @rushstack/rush-pnpm-kit-v8 + +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 0.2.23 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 0.2.22 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 0.2.21 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.2.20 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.2.19 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 0.2.18 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.2.17 +Mon, 20 Apr 2026 15:15:25 GMT + +_Version update only_ + +## 0.2.16 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 0.2.15 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 0.2.14 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.2.13 +Fri, 10 Apr 2026 22:46:35 GMT + +_Version update only_ + +## 0.2.12 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.2.11 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 0.2.10 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 0.2.9 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.2.8 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 0.2.7 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.2.6 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 0.2.5 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.2.4 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 0.2.3 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 0.2.2 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.2.1 +Thu, 19 Feb 2026 01:30:06 GMT + +### Patches + +- Filter files from publish. + +## 0.2.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.1.7 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 0.1.6 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 0.1.5 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 0.1.4 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 0.1.3 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 0.1.2 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 0.1.1 +Mon, 05 Jan 2026 16:12:50 GMT + +_Version update only_ + +## 0.1.0 +Wed, 24 Dec 2025 01:12:52 GMT + +### Minor changes + +- Set up the `@rushstack/rush-pnpm-kit-v8` package to bundle all pnpm v8 related packages together. + diff --git a/libraries/rush-pnpm-kit-v8/LICENSE b/libraries/rush-pnpm-kit-v8/LICENSE new file mode 100644 index 00000000000..0ea811163c2 --- /dev/null +++ b/libraries/rush-pnpm-kit-v8/LICENSE @@ -0,0 +1,24 @@ +@rushstack/rush-pnpm-kit-v8 + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/libraries/rush-pnpm-kit-v8/README.md b/libraries/rush-pnpm-kit-v8/README.md new file mode 100644 index 00000000000..c8ecde4d611 --- /dev/null +++ b/libraries/rush-pnpm-kit-v8/README.md @@ -0,0 +1,14 @@ +## @microsoft/rush-pnpm-kit-v8 + +This is a companion package for the Rush tool. See the +[@microsoft/rush](https://www.npmjs.com/package/@microsoft/rush) +package for details. + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/libraries/rush-pnpm-kit-v8/CHANGELOG.md) - Find + out what's new in the latest version +- [API Reference](https://api.rushstack.io/pages/rush-lib/) + +Rush is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/libraries/rush-pnpm-kit-v8/config/api-extractor.json b/libraries/rush-pnpm-kit-v8/config/api-extractor.json new file mode 100644 index 00000000000..3dbb76c0e6f --- /dev/null +++ b/libraries/rush-pnpm-kit-v8/config/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json" +} diff --git a/libraries/rush-pnpm-kit-v8/config/rig.json b/libraries/rush-pnpm-kit-v8/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/libraries/rush-pnpm-kit-v8/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/libraries/rush-pnpm-kit-v8/eslint.config.js b/libraries/rush-pnpm-kit-v8/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/libraries/rush-pnpm-kit-v8/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/rush-pnpm-kit-v8/package.json b/libraries/rush-pnpm-kit-v8/package.json new file mode 100644 index 00000000000..a55e869219c --- /dev/null +++ b/libraries/rush-pnpm-kit-v8/package.json @@ -0,0 +1,47 @@ +{ + "name": "@rushstack/rush-pnpm-kit-v8", + "version": "0.2.23", + "description": "rush pnpm kit v8", + "license": "MIT", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/rush-pnpm-kit-v8.d.ts", + "exports": { + ".": { + "types": "./dist/rush-pnpm-kit-v8.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "scripts": { + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean", + "build": "heft build --clean", + "test": "heft test --clean" + }, + "dependencies": { + "@pnpm/dependency-path-pnpm-v8": "npm:@pnpm/dependency-path@~2.1.8", + "@pnpm/lockfile-file-pnpm-lock-v6": "npm:@pnpm/lockfile-file@~8.1.8", + "@pnpm/logger": "~5.0.0" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" + }, + "sideEffects": false +} diff --git a/libraries/rush-pnpm-kit-v8/src/dependencyPath.ts b/libraries/rush-pnpm-kit-v8/src/dependencyPath.ts new file mode 100644 index 00000000000..f1d1f63bf6e --- /dev/null +++ b/libraries/rush-pnpm-kit-v8/src/dependencyPath.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { depPathToFilename, indexOfPeersSuffix, parse } from '@pnpm/dependency-path-pnpm-v8'; + +export { depPathToFilename, indexOfPeersSuffix, parse }; diff --git a/libraries/rush-pnpm-kit-v8/src/index.ts b/libraries/rush-pnpm-kit-v8/src/index.ts new file mode 100644 index 00000000000..bb8a9dcdfa9 --- /dev/null +++ b/libraries/rush-pnpm-kit-v8/src/index.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export * as dependencyPath from './dependencyPath'; +export * as lockfileFs from './lockfileFs'; +export * as logger from './logger'; diff --git a/libraries/rush-pnpm-kit-v8/src/lockfileFs.ts b/libraries/rush-pnpm-kit-v8/src/lockfileFs.ts new file mode 100644 index 00000000000..53e6c97e72d --- /dev/null +++ b/libraries/rush-pnpm-kit-v8/src/lockfileFs.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { readWantedLockfile } from '@pnpm/lockfile-file-pnpm-lock-v6'; + +export { readWantedLockfile }; diff --git a/libraries/rush-pnpm-kit-v8/src/logger.ts b/libraries/rush-pnpm-kit-v8/src/logger.ts new file mode 100644 index 00000000000..6d0c71dee47 --- /dev/null +++ b/libraries/rush-pnpm-kit-v8/src/logger.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { LogBase } from '@pnpm/logger'; + +export { LogBase }; diff --git a/libraries/rush-pnpm-kit-v8/tsconfig.json b/libraries/rush-pnpm-kit-v8/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/libraries/rush-pnpm-kit-v8/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/libraries/rush-pnpm-kit-v9/.npmignore b/libraries/rush-pnpm-kit-v9/.npmignore new file mode 100644 index 00000000000..f7a40e10213 --- /dev/null +++ b/libraries/rush-pnpm-kit-v9/.npmignore @@ -0,0 +1,36 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/rush-pnpm-kit-v9/CHANGELOG.json b/libraries/rush-pnpm-kit-v9/CHANGELOG.json new file mode 100644 index 00000000000..bcde923b9a5 --- /dev/null +++ b/libraries/rush-pnpm-kit-v9/CHANGELOG.json @@ -0,0 +1,399 @@ +{ + "name": "@rushstack/rush-pnpm-kit-v9", + "entries": [ + { + "version": "0.2.23", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.23", + "date": "Tue, 21 Jul 2026 02:53:23 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.22`" + } + ] + } + }, + { + "version": "0.2.22", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.22", + "date": "Fri, 17 Jul 2026 00:16:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.21`" + } + ] + } + }, + { + "version": "0.2.21", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.21", + "date": "Thu, 16 Jul 2026 00:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.20`" + } + ] + } + }, + { + "version": "0.2.20", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.20", + "date": "Sat, 13 Jun 2026 00:16:19 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.19`" + } + ] + } + }, + { + "version": "0.2.19", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.19", + "date": "Mon, 08 Jun 2026 15:15:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.18`" + } + ] + } + }, + { + "version": "0.2.18", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.18", + "date": "Mon, 20 Apr 2026 23:31:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.17`" + } + ] + } + }, + { + "version": "0.2.17", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.17", + "date": "Mon, 20 Apr 2026 15:15:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.16`" + } + ] + } + }, + { + "version": "0.2.16", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.16", + "date": "Sat, 18 Apr 2026 03:47:10 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.15`" + } + ] + } + }, + { + "version": "0.2.15", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.15", + "date": "Sat, 18 Apr 2026 00:15:17 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.14`" + } + ] + } + }, + { + "version": "0.2.14", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.14", + "date": "Fri, 17 Apr 2026 15:14:57 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.13`" + } + ] + } + }, + { + "version": "0.2.13", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.13", + "date": "Fri, 10 Apr 2026 22:46:35 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.12`" + } + ] + } + }, + { + "version": "0.2.12", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.12", + "date": "Thu, 09 Apr 2026 00:15:07 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.11`" + } + ] + } + }, + { + "version": "0.2.11", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.11", + "date": "Sat, 04 Apr 2026 00:14:00 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.10`" + } + ] + } + }, + { + "version": "0.2.10", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.10", + "date": "Wed, 01 Apr 2026 15:13:38 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.9`" + } + ] + } + }, + { + "version": "0.2.9", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.9", + "date": "Tue, 31 Mar 2026 15:14:15 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.8`" + } + ] + } + }, + { + "version": "0.2.8", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.8", + "date": "Mon, 09 Mar 2026 15:14:08 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.7`" + } + ] + } + }, + { + "version": "0.2.7", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.7", + "date": "Wed, 25 Feb 2026 21:39:42 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.6`" + } + ] + } + }, + { + "version": "0.2.6", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.6", + "date": "Wed, 25 Feb 2026 00:34:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.5`" + } + ] + } + }, + { + "version": "0.2.5", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.5", + "date": "Tue, 24 Feb 2026 01:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.4`" + } + ] + } + }, + { + "version": "0.2.4", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.4", + "date": "Mon, 23 Feb 2026 00:42:21 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.3`" + } + ] + } + }, + { + "version": "0.2.3", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.3", + "date": "Fri, 20 Feb 2026 16:14:49 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.2`" + } + ] + } + }, + { + "version": "0.2.2", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.2", + "date": "Fri, 20 Feb 2026 00:15:04 GMT", + "comments": { + "patch": [ + { + "comment": "Add `\"node\"` condition before `\"import\"` in the `\"exports\"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `\"import\"`. Fixes https://github.com/microsoft/rushstack/issues/5644." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.1`" + } + ] + } + }, + { + "version": "0.2.1", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.1", + "date": "Thu, 19 Feb 2026 01:30:06 GMT", + "comments": { + "patch": [ + { + "comment": "Filter files from publish." + } + ] + } + }, + { + "version": "0.2.0", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.2.0", + "date": "Thu, 19 Feb 2026 00:04:53 GMT", + "comments": { + "minor": [ + { + "comment": "Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `\"exports\"` field in `package.json`." + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.2.0`" + } + ] + } + }, + { + "version": "0.1.7", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.1.7", + "date": "Sat, 07 Feb 2026 01:13:26 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.14`" + } + ] + } + }, + { + "version": "0.1.6", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.1.6", + "date": "Wed, 04 Feb 2026 20:42:47 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.13`" + } + ] + } + }, + { + "version": "0.1.5", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.1.5", + "date": "Wed, 04 Feb 2026 16:13:27 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.12`" + } + ] + } + }, + { + "version": "0.1.4", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.1.4", + "date": "Fri, 30 Jan 2026 01:16:13 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.11`" + } + ] + } + }, + { + "version": "0.1.3", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.1.3", + "date": "Thu, 08 Jan 2026 01:12:30 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.10`" + } + ] + } + }, + { + "version": "0.1.2", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.1.2", + "date": "Wed, 07 Jan 2026 01:12:25 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.9`" + } + ] + } + }, + { + "version": "0.1.1", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.1.1", + "date": "Mon, 05 Jan 2026 16:12:50 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@rushstack/heft\" to `1.1.8`" + } + ] + } + }, + { + "version": "0.1.0", + "tag": "@rushstack/rush-pnpm-kit-v9_v0.1.0", + "date": "Wed, 24 Dec 2025 01:12:52 GMT", + "comments": { + "minor": [ + { + "comment": "Set up the `@rushstack/rush-pnpm-kit-v9` package to bundle all pnpm v9 related packages together." + } + ] + } + } + ] +} diff --git a/libraries/rush-pnpm-kit-v9/CHANGELOG.md b/libraries/rush-pnpm-kit-v9/CHANGELOG.md new file mode 100644 index 00000000000..93088c6b26e --- /dev/null +++ b/libraries/rush-pnpm-kit-v9/CHANGELOG.md @@ -0,0 +1,172 @@ +# Change Log - @rushstack/rush-pnpm-kit-v9 + +This log was last generated on Tue, 21 Jul 2026 02:53:23 GMT and should not be manually modified. + +## 0.2.23 +Tue, 21 Jul 2026 02:53:23 GMT + +_Version update only_ + +## 0.2.22 +Fri, 17 Jul 2026 00:16:00 GMT + +_Version update only_ + +## 0.2.21 +Thu, 16 Jul 2026 00:16:13 GMT + +_Version update only_ + +## 0.2.20 +Sat, 13 Jun 2026 00:16:19 GMT + +_Version update only_ + +## 0.2.19 +Mon, 08 Jun 2026 15:15:50 GMT + +_Version update only_ + +## 0.2.18 +Mon, 20 Apr 2026 23:31:13 GMT + +_Version update only_ + +## 0.2.17 +Mon, 20 Apr 2026 15:15:25 GMT + +_Version update only_ + +## 0.2.16 +Sat, 18 Apr 2026 03:47:10 GMT + +_Version update only_ + +## 0.2.15 +Sat, 18 Apr 2026 00:15:17 GMT + +_Version update only_ + +## 0.2.14 +Fri, 17 Apr 2026 15:14:57 GMT + +_Version update only_ + +## 0.2.13 +Fri, 10 Apr 2026 22:46:35 GMT + +_Version update only_ + +## 0.2.12 +Thu, 09 Apr 2026 00:15:07 GMT + +_Version update only_ + +## 0.2.11 +Sat, 04 Apr 2026 00:14:00 GMT + +_Version update only_ + +## 0.2.10 +Wed, 01 Apr 2026 15:13:38 GMT + +_Version update only_ + +## 0.2.9 +Tue, 31 Mar 2026 15:14:15 GMT + +_Version update only_ + +## 0.2.8 +Mon, 09 Mar 2026 15:14:08 GMT + +_Version update only_ + +## 0.2.7 +Wed, 25 Feb 2026 21:39:42 GMT + +_Version update only_ + +## 0.2.6 +Wed, 25 Feb 2026 00:34:30 GMT + +_Version update only_ + +## 0.2.5 +Tue, 24 Feb 2026 01:13:27 GMT + +_Version update only_ + +## 0.2.4 +Mon, 23 Feb 2026 00:42:21 GMT + +_Version update only_ + +## 0.2.3 +Fri, 20 Feb 2026 16:14:49 GMT + +_Version update only_ + +## 0.2.2 +Fri, 20 Feb 2026 00:15:04 GMT + +### Patches + +- Add `"node"` condition before `"import"` in the `"exports"` map so that Node.js uses the CJS output (which handles extensionless imports), while bundlers still use ESM via `"import"`. Fixes https://github.com/microsoft/rushstack/issues/5644. + +## 0.2.1 +Thu, 19 Feb 2026 01:30:06 GMT + +### Patches + +- Filter files from publish. + +## 0.2.0 +Thu, 19 Feb 2026 00:04:53 GMT + +### Minor changes + +- Normalize package layout. CommonJS is now under `lib-commonjs`, DTS is now under `lib-dts`, and ESM is now under `lib-esm`. Imports to `lib` still work as before, handled by the `"exports"` field in `package.json`. + +## 0.1.7 +Sat, 07 Feb 2026 01:13:26 GMT + +_Version update only_ + +## 0.1.6 +Wed, 04 Feb 2026 20:42:47 GMT + +_Version update only_ + +## 0.1.5 +Wed, 04 Feb 2026 16:13:27 GMT + +_Version update only_ + +## 0.1.4 +Fri, 30 Jan 2026 01:16:13 GMT + +_Version update only_ + +## 0.1.3 +Thu, 08 Jan 2026 01:12:30 GMT + +_Version update only_ + +## 0.1.2 +Wed, 07 Jan 2026 01:12:25 GMT + +_Version update only_ + +## 0.1.1 +Mon, 05 Jan 2026 16:12:50 GMT + +_Version update only_ + +## 0.1.0 +Wed, 24 Dec 2025 01:12:52 GMT + +### Minor changes + +- Set up the `@rushstack/rush-pnpm-kit-v9` package to bundle all pnpm v9 related packages together. + diff --git a/libraries/rush-pnpm-kit-v9/LICENSE b/libraries/rush-pnpm-kit-v9/LICENSE new file mode 100644 index 00000000000..b530223f3b1 --- /dev/null +++ b/libraries/rush-pnpm-kit-v9/LICENSE @@ -0,0 +1,24 @@ +@rushstack/rush-pnpm-kit-v9 + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +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. diff --git a/libraries/rush-pnpm-kit-v9/README.md b/libraries/rush-pnpm-kit-v9/README.md new file mode 100644 index 00000000000..1a313d7a43c --- /dev/null +++ b/libraries/rush-pnpm-kit-v9/README.md @@ -0,0 +1,14 @@ +## @microsoft/rush-pnpm-kit-v9 + +This is a companion package for the Rush tool. See the +[@microsoft/rush](https://www.npmjs.com/package/@microsoft/rush) +package for details. + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/libraries/rush-pnpm-kit-v9/CHANGELOG.md) - Find + out what's new in the latest version +- [API Reference](https://api.rushstack.io/pages/rush-lib/) + +Rush is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/libraries/rush-pnpm-kit-v9/config/api-extractor.json b/libraries/rush-pnpm-kit-v9/config/api-extractor.json new file mode 100644 index 00000000000..3dbb76c0e6f --- /dev/null +++ b/libraries/rush-pnpm-kit-v9/config/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json" +} diff --git a/libraries/rush-pnpm-kit-v9/config/rig.json b/libraries/rush-pnpm-kit-v9/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/libraries/rush-pnpm-kit-v9/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/libraries/rush-pnpm-kit-v9/eslint.config.js b/libraries/rush-pnpm-kit-v9/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/libraries/rush-pnpm-kit-v9/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/rush-pnpm-kit-v9/package.json b/libraries/rush-pnpm-kit-v9/package.json new file mode 100644 index 00000000000..e89b2dc570b --- /dev/null +++ b/libraries/rush-pnpm-kit-v9/package.json @@ -0,0 +1,47 @@ +{ + "name": "@rushstack/rush-pnpm-kit-v9", + "version": "0.2.23", + "description": "rush pnpm kit v9", + "license": "MIT", + "main": "./lib-commonjs/index.js", + "module": "./lib-esm/index.js", + "types": "./dist/rush-pnpm-kit-v9.d.ts", + "exports": { + ".": { + "types": "./dist/rush-pnpm-kit-v9.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib-esm/index.js", + "require": "./lib-commonjs/index.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "scripts": { + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean", + "build": "heft build --clean", + "test": "heft test --clean" + }, + "dependencies": { + "@pnpm/dependency-path-pnpm-v9": "npm:@pnpm/dependency-path@~5.1.7", + "@pnpm/lockfile.fs-pnpm-lock-v9": "npm:@pnpm/lockfile.fs@~1001.1.11", + "@pnpm/logger": "~1001.0.0" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "local-node-rig": "workspace:*" + }, + "sideEffects": false +} diff --git a/libraries/rush-pnpm-kit-v9/src/dependencyPath.ts b/libraries/rush-pnpm-kit-v9/src/dependencyPath.ts new file mode 100644 index 00000000000..649f8936bab --- /dev/null +++ b/libraries/rush-pnpm-kit-v9/src/dependencyPath.ts @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { depPathToFilename, indexOfPeersSuffix, parse, removeSuffix } from '@pnpm/dependency-path-pnpm-v9'; +import type { DependencyPath } from '@pnpm/dependency-path-pnpm-v9'; + +export { depPathToFilename, indexOfPeersSuffix, parse, removeSuffix, DependencyPath }; diff --git a/libraries/rush-pnpm-kit-v9/src/index.ts b/libraries/rush-pnpm-kit-v9/src/index.ts new file mode 100644 index 00000000000..bb8a9dcdfa9 --- /dev/null +++ b/libraries/rush-pnpm-kit-v9/src/index.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export * as dependencyPath from './dependencyPath'; +export * as lockfileFs from './lockfileFs'; +export * as logger from './logger'; diff --git a/libraries/rush-pnpm-kit-v9/src/lockfileFs.ts b/libraries/rush-pnpm-kit-v9/src/lockfileFs.ts new file mode 100644 index 00000000000..bfed13bf89f --- /dev/null +++ b/libraries/rush-pnpm-kit-v9/src/lockfileFs.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { readWantedLockfile } from '@pnpm/lockfile.fs-pnpm-lock-v9'; + +export { readWantedLockfile }; diff --git a/libraries/rush-pnpm-kit-v9/src/logger.ts b/libraries/rush-pnpm-kit-v9/src/logger.ts new file mode 100644 index 00000000000..6d0c71dee47 --- /dev/null +++ b/libraries/rush-pnpm-kit-v9/src/logger.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { LogBase } from '@pnpm/logger'; + +export { LogBase }; diff --git a/libraries/rush-pnpm-kit-v9/tsconfig.json b/libraries/rush-pnpm-kit-v9/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/libraries/rush-pnpm-kit-v9/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/libraries/rush-sdk/.eslintrc.js b/libraries/rush-sdk/.eslintrc.js deleted file mode 100644 index 4c934799d67..00000000000 --- a/libraries/rush-sdk/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: [ - '@rushstack/eslint-config/profile/node-trusted-tool', - '@rushstack/eslint-config/mixins/friendly-locals' - ], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/libraries/rush-sdk/.gitignore b/libraries/rush-sdk/.gitignore new file mode 100644 index 00000000000..4717fcdca27 --- /dev/null +++ b/libraries/rush-sdk/.gitignore @@ -0,0 +1 @@ +lib-intermediate-*/ \ No newline at end of file diff --git a/libraries/rush-sdk/.npmignore b/libraries/rush-sdk/.npmignore index d64437e0b4e..80ba44fed93 100644 --- a/libraries/rush-sdk/.npmignore +++ b/libraries/rush-sdk/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,18 +21,19 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -/lib-shim/generate-stubs* +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- -# (Add your project-specific overrides here) \ No newline at end of file +# Exclude intermediate build outputs (not shipped) +/lib-intermediate-*/** \ No newline at end of file diff --git a/libraries/rush-sdk/README.md b/libraries/rush-sdk/README.md index 73ec410e3c7..149ff9b4b41 100644 --- a/libraries/rush-sdk/README.md +++ b/libraries/rush-sdk/README.md @@ -4,18 +4,110 @@ This is a companion package for the Rush tool. See the [@microsoft/rush](https:/ ⚠ **_THIS PACKAGE IS EXPERIMENTAL_** ⚠ -The **@rushstack/rush-sdk** package acts as a lightweight proxy for accessing the APIs of the **@microsoft/rush-lib** engine. It is intended to support three different use cases: +The **@rushstack/rush-sdk** package acts as a lightweight proxy for accessing the APIs of the **@microsoft/rush-lib** engine. It is intended to support five different use cases: -1. Rush plugins should import from **@rushstack/rush-sdk** instead of **@microsoft/rush-lib**. This gives plugins full access to Rush APIs while avoiding a redundant installation of those packages. At runtime, the APIs will be bound to the correct `rushVersion` from **rush.json**, and guaranteed to be the same **@microsoft/rush-lib** module instance as the plugin host. +1. **Rush plugins:** Rush plugins should import from **@rushstack/rush-sdk** instead of **@microsoft/rush-lib**. This gives plugins full access to Rush APIs while avoiding a redundant installation of those packages. At runtime, the APIs will be bound to the correct `rushVersion` from **rush.json**, and guaranteed to be the same **@microsoft/rush-lib** module instance as the plugin host. -2. When authoring unit tests for a Rush plugin, developers should add **@microsoft/rush-lib** to their **package.json** `devDependencies`. In this context, **@rushstack/rush-sdk** will resolve to that instance for testing purposes. +2. **Unit tests:** When authoring unit tests (for a Rush plugin, for example), developers should add **@microsoft/rush-lib** to their **package.json** `devDependencies` and add **@rushstack/rush-sdk** to the regular `dependencies`. In this context, **@rushstack/rush-sdk** will resolve to the locally installed instance for testing purposes. -3. For projects within a monorepo that use **@rushstack/rush-sdk** during their build process, child processes will inherit the installation of Rush that invoked them. This is communicated using the `_RUSH_LIB_PATH` environment variable. +3. **Rush subprocesses:** For tools within a monorepo that import **@rushstack/rush-sdk** during their build process, child processes will inherit the installation of Rush that invoked them. This is communicated using the `_RUSH_LIB_PATH` environment variable. -4. For scripts and tools that are designed to be used in a Rush monorepo, in the future **@rushstack/rush-sdk** will automatically invoke **install-run-rush.js** and load the local installation. This ensures that tools load a compatible version of the Rush engine for the given branch. Once this is implemented, **@rushstack/rush-sdk** can replace **@microsoft/rush-lib** entirely as the official API interface, with the latter serving as the underlying implementation. +4. **Monorepo tools:** For scripts and tools that are designed to be used in a Rush monorepo, **@rushstack/rush-sdk** will automatically invoke **install-run-rush.js** and load the local installation. This ensures that tools load a compatible version of the Rush engine for the given branch. + +5. **Advanced scenarios:** The secondary `@rushstack/rush-sdk/loader` entry point can be imported by tools that need to explicitly control where **@microsoft/rush-lib** gets loaded from. This API also allows monitoring installation and canceling the operation. This API is used by the Rush Stack VS Code extension, for example. The **@rushstack/rush-sdk** API declarations are identical to the corresponding version of **@microsoft/rush-lib**. +## Basic usage + +Here's an example of basic usage that works with cases 1-4 above: + +```ts +// CommonJS notation: +const { RushConfiguration } = require('@rushstack/rush-sdk'); + +const config = RushConfiguration.loadFromDefaultLocation(); +console.log(config.commonFolder); +``` + +```ts +// TypeScript notation: +import { RushConfiguration } from '@rushstack/rush-sdk'; + +const config = RushConfiguration.loadFromDefaultLocation(); +console.log(config.commonFolder); +``` + +## Loader API + +Here's a basic example of how to manually load **@rushstack/rush-sdk** and monitor installation progress: + +```ts +import { RushSdkLoader, ISdkCallbackEvent } from '@rushstack/rush-sdk/loader'; + +if (!RushSdkLoader.isLoaded) { + await RushSdkLoader.loadAsync({ + // the search for rush.json starts here: + rushJsonSearchFolder: "path/to/my-repo/apps/my-app", + + onNotifyEvent: (event: ISdkCallbackEvent) => { + if (event.logMessage) { + // Your tool can show progress about the loading: + if (event.logMessage.kind === 'info') { + console.log(event.logMessage.text); + } + } + } + }); +} + +// Any subsequent attempts to call require() will return the same instance +// that was loaded above. +const rushSdk = require('@rushstack/rush-sdk'); +const config = rushSdk.RushConfiguration.loadFromDefaultLocation(); +``` + +Here's a more elaborate example illustrating other API features: + +```ts +import { RushSdkLoader, ISdkCallbackEvent } from '@rushstack/rush-sdk/loader'; + +// Use an AbortController to cancel the operation after a certain time period +const abortController = new AbortController(); +setTimeout(() => { + abortController.abort(); +}, 1000); + +if (!RushSdkLoader.isLoaded) { + await RushSdkLoader.loadAsync({ + // the search for rush.json starts here: + rushJsonSearchFolder: "path/to/my-repo/apps/my-app", + + abortSignal: abortController.signal, + + onNotifyEvent: (event: ISdkCallbackEvent) => { + if (event.logMessage) { + // Your tool can show progress about the loading: + if (event.logMessage.kind === 'info') { + console.log(event.logMessage.text); + } + } + + if (event.progressPercent !== undefined) { + // If installation takes a long time, your tool can display a progress bar + displayYourProgressBar(event.progressPercent); + } + } + }); +} + +// Any subsequent attempts to call require() will return the same instance +// that was loaded above. +const rushSdk = require('@rushstack/rush-sdk'); +const config = rushSdk.RushConfiguration.loadFromDefaultLocation(); +``` + + ## Importing internal APIs Backwards compatibility is only guaranteed for the APIs marked as `@public` in the official `rush-lib.d.ts` entry point. diff --git a/libraries/rush-sdk/config/api-extractor.json b/libraries/rush-sdk/config/api-extractor.json new file mode 100644 index 00000000000..981641d969b --- /dev/null +++ b/libraries/rush-sdk/config/api-extractor.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "local-node-rig/profiles/default/config/api-extractor-base.json", + + "mainEntryPointFilePath": "/lib-intermediate-dts/loader.d.ts", + + "docModel": { + "enabled": false + }, + + "dtsRollup": { + "enabled": true, + "publicTrimmedFilePath": "/dist/loader.d.ts" + } +} diff --git a/libraries/rush-sdk/config/heft.json b/libraries/rush-sdk/config/heft.json index e1389cd7acc..21b0acb80a3 100644 --- a/libraries/rush-sdk/config/heft.json +++ b/libraries/rush-sdk/config/heft.json @@ -2,23 +2,29 @@ * Defines configuration used by core Heft. */ { - "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json", + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", - "extends": "@rushstack/heft-node-rig/profiles/default/config/heft.json", + "extends": "local-node-rig/profiles/default/config/heft.json", // TODO: Add comments "phasesByName": { "build": { "cleanFiles": [ { - "sourcePath": "lib-shim" + "includeGlobs": [ + "lib-shim", + "lib-intermediate-commonjs", + "lib-intermediate-esm", + "lib-intermediate-dts" + ] } ], "tasksByName": { "copy-rush-lib-types": { - "taskEvent": { - "eventKind": "copyFiles", + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", "options": { "copyOperations": [ { @@ -35,12 +41,20 @@ "taskDependencies": ["copy-rush-lib-types"] }, + "webpack": { + "taskDependencies": ["typescript"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-webpack5-plugin" + } + }, + "generate-stubs": { "taskDependencies": ["typescript"], - "taskEvent": { - "eventKind": "runScript", + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "run-script-plugin", "options": { - "scriptPath": "./lib-shim/generate-stubs.js" + "scriptPath": "./lib-intermediate-commonjs/generate-stubs.js" } } } diff --git a/libraries/rush-sdk/config/jest.config.json b/libraries/rush-sdk/config/jest.config.json index c9f2225429c..a50e6db90d0 100644 --- a/libraries/rush-sdk/config/jest.config.json +++ b/libraries/rush-sdk/config/jest.config.json @@ -1,9 +1,9 @@ { - "extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json", + "extends": "local-node-rig/profiles/default/config/jest.config.json", - "roots": ["/lib-shim"], + "roots": ["/lib-intermediate-commonjs"], - "testMatch": ["/lib-shim/**/*.test.js"], + "testMatch": ["/lib-intermediate-commonjs/**/*.test.js"], "collectCoverageFrom": [ "lib-shim/**/*.js", diff --git a/libraries/rush-sdk/config/rig.json b/libraries/rush-sdk/config/rig.json index 6ac88a96368..165ffb001f5 100644 --- a/libraries/rush-sdk/config/rig.json +++ b/libraries/rush-sdk/config/rig.json @@ -3,5 +3,5 @@ // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-node-rig" + "rigPackageName": "local-node-rig" } diff --git a/libraries/rush-sdk/config/rush-project.json b/libraries/rush-sdk/config/rush-project.json new file mode 100644 index 00000000000..4e1fc0eb2a1 --- /dev/null +++ b/libraries/rush-sdk/config/rush-project.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-project.schema.json", + "extends": "local-node-rig/profiles/default/config/rush-project.json", + "operationSettings": [ + { + "operationName": "_phase:build", + "outputFolderNames": [ + "lib-shim", + "lib-intermediate-commonjs", + "lib-intermediate-esm", + "lib-intermediate-dts" + ] + } + ] +} diff --git a/libraries/rush-sdk/config/typescript.json b/libraries/rush-sdk/config/typescript.json new file mode 100644 index 00000000000..403432e06f5 --- /dev/null +++ b/libraries/rush-sdk/config/typescript.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/typescript.schema.json", + + "extends": "local-node-rig/profiles/default/config/typescript.json", + + "$additionalModuleKindsToEmit.inheritanceType": "replace", + "additionalModuleKindsToEmit": [ + { + "moduleKind": "esnext", + "outFolderName": "lib-intermediate-esm" + } + ] +} diff --git a/libraries/rush-sdk/eslint.config.js b/libraries/rush-sdk/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/libraries/rush-sdk/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/rush-sdk/package.json b/libraries/rush-sdk/package.json index 62e739399e6..db92e74decb 100644 --- a/libraries/rush-sdk/package.json +++ b/libraries/rush-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@rushstack/rush-sdk", - "version": "5.98.0", + "version": "5.178.1", "description": "An API for interacting with the Rush engine", "repository": { "type": "git", @@ -8,8 +8,33 @@ "directory": "apps/rush-sdk" }, "homepage": "https://rushjs.io", - "main": "lib-shim/index.js", - "typings": "dist/rush-lib.d.ts", + "main": "./lib-shim/index.js", + "types": "./dist/rush-lib.d.ts", + "exports": { + ".": { + "types": "./dist/rush-lib.d.ts", + "default": "./lib-shim/index.js" + }, + "./loader": { + "types": "./dist/loader.d.ts", + "default": "./lib-shim/loader.js" + }, + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "default": "./lib-commonjs/*.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "loader": [ + "./dist/loader.d.ts" + ], + "lib/*": [ + "lib-dts/*" + ] + } + }, "scripts": { "build": "heft build --clean", "_phase:build": "heft run --only build -- --clean", @@ -17,19 +42,26 @@ }, "license": "MIT", "dependencies": { + "@pnpm/lockfile.types-900": "npm:@pnpm/lockfile.types@~900.0.0", + "@rushstack/credential-cache": "workspace:*", + "@rushstack/lookup-by-path": "workspace:*", "@rushstack/node-core-library": "workspace:*", - "@types/node-fetch": "2.6.2", + "@rushstack/package-deps-hash": "workspace:*", + "@rushstack/terminal": "workspace:*", "tapable": "2.2.1" }, "devDependencies": { "@microsoft/rush-lib": "workspace:*", - "@rushstack/eslint-config": "workspace:*", "@rushstack/heft": "workspace:*", - "@rushstack/heft-node-rig": "workspace:*", + "@rushstack/heft-webpack5-plugin": "workspace:*", "@rushstack/stream-collator": "workspace:*", "@rushstack/ts-command-line": "workspace:*", - "@rushstack/terminal": "workspace:*", - "@types/semver": "7.3.5", - "@types/webpack-env": "1.18.0" - } + "@rushstack/webpack-preserve-dynamic-require-plugin": "workspace:*", + "@types/semver": "7.7.1", + "@types/webpack-env": "1.18.8", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*", + "webpack": "~5.105.2" + }, + "sideEffects": false } diff --git a/libraries/rush-sdk/src/generate-stubs.ts b/libraries/rush-sdk/src/generate-stubs.ts index c03ba205903..d43b5502fec 100644 --- a/libraries/rush-sdk/src/generate-stubs.ts +++ b/libraries/rush-sdk/src/generate-stubs.ts @@ -1,66 +1,148 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; -import { FileSystem, Import, Path } from '@rushstack/node-core-library'; +import type { IRunScriptOptions } from '@rushstack/heft'; +import { Async, FileSystem, type FolderItem, Import, JsonFile, Path } from '@rushstack/node-core-library'; -function generateLibFilesRecursively(options: { +interface IGenerateOptions { parentSourcePath: string; - parentTargetPath: string; + parentCjsTargetPath: string; + parentDtsTargetPath: string; parentSrcImportPathWithSlash: string; libShimIndexPath: string; -}): void { - for (const folderItem of FileSystem.readFolderItems(options.parentSourcePath)) { - const sourcePath: string = path.join(options.parentSourcePath, folderItem.name); - const targetPath: string = path.join(options.parentTargetPath, folderItem.name); +} + +interface IFileTask { + type: 'dts' | 'js'; + sourcePath: string; + targetPath: string; + srcImportPath?: string; + shimPathLiteral?: string; +} + +async function* collectFileTasksAsync(options: IGenerateOptions): AsyncGenerator { + const { + parentSourcePath, + parentCjsTargetPath, + parentDtsTargetPath, + parentSrcImportPathWithSlash, + libShimIndexPath + } = options; + const folderItems: FolderItem[] = await FileSystem.readFolderItemsAsync(options.parentSourcePath); + + for (const folderItem of folderItems) { + const itemName: string = folderItem.name; + const sourcePath: string = `${parentSourcePath}/${itemName}`; + const cjsTargetPath: string = `${parentCjsTargetPath}/${itemName}`; + const dtsTargetPath: string = `${parentDtsTargetPath}/${itemName}`; if (folderItem.isDirectory()) { - // create destination folder - FileSystem.ensureEmptyFolder(targetPath); - generateLibFilesRecursively({ + // Ensure destination folder exists + await FileSystem.ensureFolderAsync(cjsTargetPath); + // Recursively yield tasks from subdirectory + yield* collectFileTasksAsync({ parentSourcePath: sourcePath, - parentTargetPath: targetPath, - parentSrcImportPathWithSlash: options.parentSrcImportPathWithSlash + folderItem.name + '/', - libShimIndexPath: options.libShimIndexPath + parentCjsTargetPath: cjsTargetPath, + parentDtsTargetPath: dtsTargetPath, + parentSrcImportPathWithSlash: parentSrcImportPathWithSlash + itemName + '/', + libShimIndexPath }); - } else { - if (folderItem.name.endsWith('.d.ts')) { - FileSystem.copyFile({ - sourcePath: sourcePath, - destinationPath: targetPath - }); - } else if (folderItem.name.endsWith('.js')) { - const srcImportPath: string = options.parentSrcImportPathWithSlash + path.parse(folderItem.name).name; - const shimPath: string = path.relative(options.parentTargetPath, options.libShimIndexPath); - const shimPathLiteral: string = JSON.stringify(Path.convertToSlashes(shimPath)); - const srcImportPathLiteral: string = JSON.stringify(srcImportPath); - - FileSystem.writeFile( - targetPath, - // Example: - // module.exports = require("../../../lib-shim/index")._rushSdk_loadInternalModule("logic/policy/GitEmailPolicy"); - `module.exports = require(${shimPathLiteral})._rushSdk_loadInternalModule(${srcImportPathLiteral});` - ); + } else if (folderItem.name.endsWith('.d.ts')) { + yield { + type: 'dts', + sourcePath, + targetPath: dtsTargetPath + }; + } else if (folderItem.name.endsWith('.js')) { + const srcImportPath: string = parentSrcImportPathWithSlash + path.parse(folderItem.name).name; + const shimPath: string = path.relative(parentCjsTargetPath, libShimIndexPath); + const shimPathLiteral: string = JSON.stringify(Path.convertToSlashes(shimPath)); + + yield { + type: 'js', + sourcePath, + targetPath: cjsTargetPath, + srcImportPath, + shimPathLiteral + }; + } + } +} + +async function processFileTaskAsync(task: IFileTask): Promise { + const { type, sourcePath, targetPath, srcImportPath, shimPathLiteral } = task; + if (type === 'dts') { + await FileSystem.copyFileAsync({ + sourcePath, + destinationPath: targetPath + }); + } else { + const srcImportPathLiteral: string = JSON.stringify(srcImportPath); + + let namedExportsAssignment: string = ''; + try { + // Read the sidecar .exports.json file generated by DeepImportsPlugin to get module exports + const exportsJsonPath: string = sourcePath.slice(0, -'.js'.length) + '.exports.json'; + const { moduleExports }: { moduleExports: string[] } = await JsonFile.loadAsync(exportsJsonPath); + if (moduleExports.length > 0) { + // Assign named exports after module.exports to ensure they're properly exposed for ESM imports + namedExportsAssignment = + '\n' + moduleExports.map((exportName) => `exports.${exportName} = _m.${exportName};`).join('\n'); + } + } catch (e) { + if (!FileSystem.isNotExistError(e)) { + throw e; } } + + await FileSystem.writeFileAsync( + targetPath, + // Example: + // ``` + // const _m = require("../../../lib-shim/index")._rushSdk_loadInternalModule("logic/policy/GitEmailPolicy"); + // module.exports = _m; + // exports.GitEmailPolicy = _m.GitEmailPolicy; + // ``` + `const _m = require(${shimPathLiteral})._rushSdk_loadInternalModule(${srcImportPathLiteral});\nmodule.exports = _m;${namedExportsAssignment}\n` + ); } } // Entry point invoked by "runScript" action from config/heft.json -export async function runAsync(): Promise { +export async function runAsync(options: IRunScriptOptions): Promise { + const { + heftConfiguration: { buildFolderPath }, + heftTaskSession: { + logger: { terminal } + } + } = options; + const rushLibFolder: string = Import.resolvePackage({ baseFolderPath: __dirname, - packageName: '@microsoft/rush-lib' + packageName: '@microsoft/rush-lib', + useNodeJSResolver: true }); - const stubsTargetPath: string = path.resolve(__dirname, '../lib'); - console.log('generate-stubs: Generating stub files under: ' + stubsTargetPath); - generateLibFilesRecursively({ - parentSourcePath: path.join(rushLibFolder, 'lib'), - parentTargetPath: stubsTargetPath, + const cjsStubsTargetPath: string = `${buildFolderPath}/lib-commonjs`; + const dtsStubsTargetPath: string = `${buildFolderPath}/lib-dts`; + terminal.writeLine( + `generate-stubs: Generating stub files under ${cjsStubsTargetPath} and ${dtsStubsTargetPath}` + ); + + // Ensure the target folder exists + await FileSystem.ensureFolderAsync(cjsStubsTargetPath); + + // Collect and process file tasks in parallel with controlled concurrency + const tasks: AsyncGenerator = collectFileTasksAsync({ + parentSourcePath: `${rushLibFolder}/lib-commonjs`, + parentCjsTargetPath: cjsStubsTargetPath, + parentDtsTargetPath: dtsStubsTargetPath, parentSrcImportPathWithSlash: '', - libShimIndexPath: path.join(__dirname, '../lib-shim/index') + libShimIndexPath: `${buildFolderPath}/lib-shim/index.js` }); - console.log('generate-stubs: Completed successfully.'); + await Async.forEachAsync(tasks, processFileTaskAsync, { concurrency: 50 }); + + terminal.writeLine('generate-stubs: Completed successfully.'); } diff --git a/libraries/rush-sdk/src/helpers.ts b/libraries/rush-sdk/src/helpers.ts new file mode 100644 index 00000000000..58266cf579c --- /dev/null +++ b/libraries/rush-sdk/src/helpers.ts @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { Import, FileSystem } from '@rushstack/node-core-library'; +import type { EnvironmentVariableNames } from '@microsoft/rush-lib'; + +export const RUSH_LIB_NAME: '@microsoft/rush-lib' = '@microsoft/rush-lib'; +export const RUSH_LIB_PATH_ENV_VAR_NAME: typeof EnvironmentVariableNames._RUSH_LIB_PATH = '_RUSH_LIB_PATH'; + +export type RushLibModuleType = Record; + +export interface ISdkContext { + rushLibModule: RushLibModuleType | undefined; +} + +export const sdkContext: ISdkContext = { + rushLibModule: undefined +}; + +/** + * Find the rush.json location and return the path, or undefined if a rush.json can't be found. + * + * @privateRemarks + * Keep this in sync with `RushConfiguration.tryFindRushJsonLocation`. + */ +export function tryFindRushJsonLocation(startingFolder: string): string | undefined { + let currentFolder: string = startingFolder; + + // Look upwards at parent folders until we find a folder containing rush.json + for (let i: number = 0; i < 10; ++i) { + const rushJsonFilename: string = path.join(currentFolder, 'rush.json'); + + if (FileSystem.exists(rushJsonFilename)) { + return rushJsonFilename; + } + + const parentFolder: string = path.dirname(currentFolder); + if (parentFolder === currentFolder) { + break; + } + + currentFolder = parentFolder; + } + + return undefined; +} + +export function _require(moduleName: string): TResult { + if (typeof __non_webpack_require__ === 'function') { + // If this library has been bundled with Webpack, we need to call the real `require` function + // that doesn't get turned into a `__webpack_require__` statement. + // `__non_webpack_require__` is a Webpack macro that gets turned into a `require` statement + // during bundling. + return __non_webpack_require__(moduleName); + } else { + return require(moduleName); + } +} + +/** + * Require `@microsoft/rush-lib` under the specified folder path. + */ +export function requireRushLibUnderFolderPath(folderPath: string): RushLibModuleType { + const rushLibModulePath: string = Import.resolveModule({ + modulePath: RUSH_LIB_NAME, + baseFolderPath: folderPath + }); + + return _require(rushLibModulePath); +} diff --git a/libraries/rush-sdk/src/index.ts b/libraries/rush-sdk/src/index.ts index 0e7e9baf6d0..6e1dbaa58b1 100644 --- a/libraries/rush-sdk/src/index.ts +++ b/libraries/rush-sdk/src/index.ts @@ -1,62 +1,60 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import * as path from 'path'; +import * as path from 'node:path'; +import type { SpawnSyncReturns } from 'node:child_process'; + import { JsonFile, - JsonObject, - Import, - IPackageJson, + type JsonObject, + type IPackageJson, PackageJsonLookup, - Executable, - FileSystem, - Terminal, - ConsoleTerminalProvider + Executable } from '@rushstack/node-core-library'; -import type { SpawnSyncReturns } from 'child_process'; -import type { EnvironmentVariableNames } from '@microsoft/rush-lib'; - -const RUSH_LIB_NAME: '@microsoft/rush-lib' = '@microsoft/rush-lib'; -const RUSH_LIB_PATH_ENV_VAR_NAME: typeof EnvironmentVariableNames.RUSH_LIB_PATH = '_RUSH_LIB_PATH'; +import { Terminal, ConsoleTerminalProvider } from '@rushstack/terminal'; +import { RushGlobalFolder } from '@microsoft/rush-lib/lib/api/RushGlobalFolder'; -const verboseEnabled: boolean = typeof process !== 'undefined' && process.env.RUSH_SDK_DEBUG === '1'; +import { + RUSH_LIB_NAME, + RUSH_LIB_PATH_ENV_VAR_NAME, + type RushLibModuleType, + _require, + requireRushLibUnderFolderPath, + tryFindRushJsonLocation, + sdkContext +} from './helpers'; + +const verboseEnabled: boolean = + typeof process !== 'undefined' && + (process.env.RUSH_SDK_DEBUG === '1' || process.env._RUSH_SDK_DEBUG === '1'); const terminal: Terminal = new Terminal( new ConsoleTerminalProvider({ verboseEnabled }) ); -type RushLibModuleType = Record; -declare const global: NodeJS.Global & - typeof globalThis & { - ___rush___rushLibModule?: RushLibModuleType; - ___rush___rushLibModuleFromEnvironment?: RushLibModuleType; - ___rush___rushLibModuleFromInstallAndRunRush?: RushLibModuleType; - }; +declare const global: typeof globalThis & { + ___rush___rushLibModule?: RushLibModuleType; + ___rush___rushLibModuleFromEnvironment?: RushLibModuleType; + ___rush___rushLibModuleFromRushGlobalFolder?: RushLibModuleType; + ___rush___rushLibModuleFromInstallAndRunRush?: RushLibModuleType; +}; -function _require(moduleName: string): TResult { - if (typeof __non_webpack_require__ === 'function') { - // If this library has been bundled with Webpack, we need to call the real `require` function - // that doesn't get turned into a `__webpack_require__` statement. - // `__non_webpack_require__` is a Webpack macro that gets turned into a `require` statement - // during bundling. - return __non_webpack_require__(moduleName); - } else { - return require(moduleName); - } -} +let errorMessage: string = ''; // SCENARIO 1: Rush's PluginManager has initialized "rush-sdk" with Rush's own instance of rush-lib. // The Rush host process will assign "global.___rush___rushLibModule" before loading the plugin. -let rushLibModule: RushLibModuleType | undefined = - global.___rush___rushLibModule || - global.___rush___rushLibModuleFromEnvironment || - global.___rush___rushLibModuleFromInstallAndRunRush; -let errorMessage: string = ''; +if (sdkContext.rushLibModule === undefined) { + sdkContext.rushLibModule = + global.___rush___rushLibModule || + global.___rush___rushLibModuleFromEnvironment || + global.___rush___rushLibModuleFromRushGlobalFolder || + global.___rush___rushLibModuleFromInstallAndRunRush; +} // SCENARIO 2: The project importing "rush-sdk" has installed its own instance of "rush-lib" // as a package.json dependency. For example, this is used by the Jest tests for Rush plugins. -if (rushLibModule === undefined) { +if (sdkContext.rushLibModule === undefined) { const importingPath: string | null | undefined = module?.parent?.filename; if (importingPath) { const callerPackageFolder: string | undefined = @@ -76,7 +74,7 @@ if (rushLibModule === undefined) { // Try to resolve rush-lib from the caller's folder terminal.writeVerboseLine(`Try to load ${RUSH_LIB_NAME} from caller package`); try { - rushLibModule = requireRushLibUnderFolderPath(callerPackageFolder); + sdkContext.rushLibModule = requireRushLibUnderFolderPath(callerPackageFolder); } catch (error) { // If we fail to resolve it, ignore the error terminal.writeVerboseLine(`Failed to load ${RUSH_LIB_NAME} from caller package`); @@ -84,9 +82,9 @@ if (rushLibModule === undefined) { // If two different libraries invoke `rush-sdk`, and one of them provides "rush-lib" // then the first version to be loaded wins. We do not support side-by-side instances of "rush-lib". - if (rushLibModule !== undefined) { + if (sdkContext.rushLibModule !== undefined) { // to track which scenario is active and how it got initialized. - global.___rush___rushLibModule = rushLibModule; + global.___rush___rushLibModule = sdkContext.rushLibModule; terminal.writeVerboseLine(`Loaded ${RUSH_LIB_NAME} from caller`); } } @@ -96,14 +94,14 @@ if (rushLibModule === undefined) { // SCENARIO 3: A tool or script has been invoked as a child process by an instance of "rush-lib" and can use the // version that invoked it. In this case, use process.env._RUSH_LIB_PATH to find "rush-lib". -if (rushLibModule === undefined) { +if (sdkContext.rushLibModule === undefined) { const rushLibPath: string | undefined = process.env[RUSH_LIB_PATH_ENV_VAR_NAME]; if (rushLibPath) { terminal.writeVerboseLine( `Try to load ${RUSH_LIB_NAME} from process.env.${RUSH_LIB_PATH_ENV_VAR_NAME} from caller package` ); try { - rushLibModule = _require(rushLibPath); + sdkContext.rushLibModule = _require(rushLibPath); } catch (error) { // Log this as a warning, since it is unexpected to define an incorrect value of the variable. terminal.writeWarningLine( @@ -111,17 +109,18 @@ if (rushLibModule === undefined) { ); } - if (rushLibModule !== undefined) { + if (sdkContext.rushLibModule !== undefined) { // to track which scenario is active and how it got initialized. - global.___rush___rushLibModuleFromEnvironment = rushLibModule; + global.___rush___rushLibModuleFromEnvironment = sdkContext.rushLibModule; terminal.writeVerboseLine(`Loaded ${RUSH_LIB_NAME} from process.env.${RUSH_LIB_PATH_ENV_VAR_NAME}`); } } } // SCENARIO 4: A standalone tool or script depends on "rush-sdk", and is meant to be used inside a monorepo folder. -// In this case, we can use install-run-rush.js to obtain the appropriate rush-lib version for the monorepo. -if (rushLibModule === undefined) { +// In this case, we can first load the rush-lib version in rush global folder. If the expected version is not installed, +// using install-run-rush.js to obtain the appropriate rush-lib version for the monorepo. +if (sdkContext.rushLibModule === undefined) { try { const rushJsonPath: string | undefined = tryFindRushJsonLocation(process.cwd()); if (!rushJsonPath) { @@ -135,50 +134,67 @@ if (rushLibModule === undefined) { const rushJson: JsonObject = JsonFile.load(rushJsonPath); const { rushVersion } = rushJson; - const installRunNodeModuleFolder: string = path.join( - monorepoRoot, - `common/temp/install-run/@microsoft+rush@${rushVersion}` - ); - try { - // First, try to load the version of "rush-lib" that was installed by install-run-rush.js - terminal.writeVerboseLine(`Trying to load ${RUSH_LIB_NAME} installed by install-run-rush`); - rushLibModule = requireRushLibUnderFolderPath(installRunNodeModuleFolder); + terminal.writeVerboseLine(`Try to load ${RUSH_LIB_NAME} from rush global folder`); + const rushGlobalFolder: RushGlobalFolder = new RushGlobalFolder(); + // The path needs to keep align with the logic inside RushVersionSelector + const expectedGlobalRushInstalledFolder: string = `${rushGlobalFolder.nodeSpecificPath}${path.sep}rush-${rushVersion}`; + terminal.writeVerboseLine( + `The expected global rush installed folder is "${expectedGlobalRushInstalledFolder}"` + ); + sdkContext.rushLibModule = requireRushLibUnderFolderPath(expectedGlobalRushInstalledFolder); } catch (e) { - let installAndRunRushStderrContent: string = ''; + terminal.writeVerboseLine(`Failed to load ${RUSH_LIB_NAME} from rush global folder: ${e.message}`); + } + + if (sdkContext.rushLibModule !== undefined) { + // to track which scenario is active and how it got initialized. + global.___rush___rushLibModuleFromRushGlobalFolder = sdkContext.rushLibModule; + terminal.writeVerboseLine(`Loaded ${RUSH_LIB_NAME} installed from rush global folder`); + } else { + const installRunNodeModuleFolder: string = `${monorepoRoot}/common/temp/install-run/@microsoft+rush@${rushVersion}`; + try { - const installAndRunRushJSPath: string = path.join(monorepoRoot, 'common/scripts/install-run-rush.js'); + // First, try to load the version of "rush-lib" that was installed by install-run-rush.js + terminal.writeVerboseLine(`Trying to load ${RUSH_LIB_NAME} installed by install-run-rush`); + sdkContext.rushLibModule = requireRushLibUnderFolderPath(installRunNodeModuleFolder); + } catch (e1) { + let installAndRunRushStderrContent: string = ''; + try { + const installAndRunRushJSPath: string = `${monorepoRoot}/common/scripts/install-run-rush.js`; + + terminal.writeLine('The Rush engine has not been installed yet. Invoking install-run-rush.js...'); - terminal.writeLine('The Rush engine has not been installed yet. Invoking install-run-rush.js...'); + const installAndRunRushProcess: SpawnSyncReturns = Executable.spawnSync( + 'node', + [installAndRunRushJSPath, '--help'], + { + stdio: 'pipe' + } + ); - const installAndRuhRushProcess: SpawnSyncReturns = Executable.spawnSync( - 'node', - [installAndRunRushJSPath, '--help'], - { - stdio: 'pipe' + installAndRunRushStderrContent = installAndRunRushProcess.stderr; + if (installAndRunRushProcess.status !== 0) { + throw new Error(`The ${RUSH_LIB_NAME} package failed to install`); } - ); - installAndRunRushStderrContent = installAndRuhRushProcess.stderr; - if (installAndRuhRushProcess.status !== 0) { - throw new Error(`The ${RUSH_LIB_NAME} package failed to install`); + // Retry to load "rush-lib" after install-run-rush run + terminal.writeVerboseLine( + `Trying to load ${RUSH_LIB_NAME} installed by install-run-rush a second time` + ); + sdkContext.rushLibModule = requireRushLibUnderFolderPath(installRunNodeModuleFolder); + } catch (e2) { + // eslint-disable-next-line no-console + console.error(`${installAndRunRushStderrContent}`); + throw new Error(`The ${RUSH_LIB_NAME} package failed to load`); } - - // Retry to load "rush-lib" after install-run-rush run - terminal.writeVerboseLine( - `Trying to load ${RUSH_LIB_NAME} installed by install-run-rush a second time` - ); - rushLibModule = requireRushLibUnderFolderPath(installRunNodeModuleFolder); - } catch (e) { - console.error(`${installAndRunRushStderrContent}`); - throw new Error(`The ${RUSH_LIB_NAME} package failed to load`); } - } - if (rushLibModule !== undefined) { - // to track which scenario is active and how it got initialized. - global.___rush___rushLibModuleFromInstallAndRunRush = rushLibModule; - terminal.writeVerboseLine(`Loaded ${RUSH_LIB_NAME} installed by install-run-rush`); + if (sdkContext.rushLibModule !== undefined) { + // to track which scenario is active and how it got initialized. + global.___rush___rushLibModuleFromInstallAndRunRush = sdkContext.rushLibModule; + terminal.writeVerboseLine(`Loaded ${RUSH_LIB_NAME} installed by install-run-rush`); + } } } catch (e) { // no-catch @@ -186,10 +202,11 @@ if (rushLibModule === undefined) { } } -if (rushLibModule === undefined) { +if (sdkContext.rushLibModule === undefined) { // This error indicates that a project is trying to import "@rushstack/rush-sdk", but the Rush engine // instance cannot be found. If you are writing Jest tests for a Rush plugin, add "@microsoft/rush-lib" // to the devDependencies for your project. + // eslint-disable-next-line no-console console.error(`Error: The @rushstack/rush-sdk package was not able to load the Rush engine: ${errorMessage} `); @@ -197,9 +214,9 @@ ${errorMessage} } // Based on TypeScript's __exportStar() -for (const property in rushLibModule) { +for (const property in sdkContext.rushLibModule) { if (property !== 'default' && !exports.hasOwnProperty(property)) { - const rushLibModuleForClosure: RushLibModuleType = rushLibModule; + const rushLibModuleForClosure: RushLibModuleType = sdkContext.rushLibModule; // Based on TypeScript's __createBinding() Object.defineProperty(exports, property, { @@ -222,43 +239,3 @@ export function _rushSdk_loadInternalModule(srcImportPath: string): unknown { } return exports._RushInternals.loadModule(srcImportPath); } - -/** - * Require `@microsoft/rush-lib` under the specified folder path. - */ -function requireRushLibUnderFolderPath(folderPath: string): RushLibModuleType { - const rushLibModulePath: string = Import.resolveModule({ - modulePath: RUSH_LIB_NAME, - baseFolderPath: folderPath - }); - - return _require(rushLibModulePath); -} - -/** - * Find the rush.json location and return the path, or undefined if a rush.json can't be found. - * - * @privateRemarks - * Keep this in sync with `RushConfiguration.tryFindRushJsonLocation`. - */ -function tryFindRushJsonLocation(startingFolder: string): string | undefined { - let currentFolder: string = startingFolder; - - // Look upwards at parent folders until we find a folder containing rush.json - for (let i: number = 0; i < 10; ++i) { - const rushJsonFilename: string = path.join(currentFolder, 'rush.json'); - - if (FileSystem.exists(rushJsonFilename)) { - return rushJsonFilename; - } - - const parentFolder: string = path.dirname(currentFolder); - if (parentFolder === currentFolder) { - break; - } - - currentFolder = parentFolder; - } - - return undefined; -} diff --git a/libraries/rush-sdk/src/loader.ts b/libraries/rush-sdk/src/loader.ts new file mode 100644 index 00000000000..5b90e10788e --- /dev/null +++ b/libraries/rush-sdk/src/loader.ts @@ -0,0 +1,287 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/// + +import * as path from 'node:path'; +import type { SpawnSyncReturns } from 'node:child_process'; + +import { JsonFile, type JsonObject, Executable } from '@rushstack/node-core-library'; + +import { + tryFindRushJsonLocation, + RUSH_LIB_NAME, + type RushLibModuleType, + requireRushLibUnderFolderPath, + sdkContext +} from './helpers'; + +declare const global: typeof globalThis & { + ___rush___rushLibModule?: RushLibModuleType; + ___rush___rushLibModuleFromEnvironment?: RushLibModuleType; + ___rush___rushLibModuleFromInstallAndRunRush?: RushLibModuleType; +}; + +/** + * Type of {@link ISdkCallbackEvent.logMessage} + * @public + */ +export interface IProgressBarCallbackLogMessage { + /** + * A status message to print in the log window, or `undefined` if there are + * no further messages. This string may contain newlines. + */ + text: string; + + /** + * The type of message. More message types may be added in the future. + */ + kind: 'info' | 'debug'; +} + +/** + * Event options for {@link ILoadSdkAsyncOptions.onNotifyEvent} + * @public + */ +export interface ISdkCallbackEvent { + /** + * Allows the caller to display log information about the operation. + */ + logMessage: IProgressBarCallbackLogMessage | undefined; + + /** + * Allows the caller to display a progress bar for long-running operations. + * + * @remarks + * If a long-running operation is required, then `progressPercent` will + * start at 0.0 and count upwards and finish at 100.0 if the operation completes + * successfully. If the long-running operation has not yet started, or + * is not required, then the value will be `undefined`. + */ + progressPercent: number | undefined; +} + +/** + * Type of {@link ILoadSdkAsyncOptions.onNotifyEvent} + * @public + */ +export type SdkNotifyEventCallback = (sdkEvent: ISdkCallbackEvent) => void; + +/** + * Options for {@link RushSdkLoader.loadAsync} + * @public + */ +export interface ILoadSdkAsyncOptions { + /** + * The folder to start from when searching for the Rush workspace configuration. + * If this folder does not contain a `rush.json` file, then each parent folder + * will be searched. If `rush.json` is not found, then the SDK fails to load. + */ + rushJsonSearchFolder?: string; + + /** + * A cancellation token that the caller can use to prematurely abort the operation. + */ + abortSignal?: AbortSignal; + + /** + * Allows the caller to monitor the progress of the operation. + */ + onNotifyEvent?: SdkNotifyEventCallback; +} + +/** + * Exposes operations that control how the `@microsoft/rush-lib` engine is + * located and loaded. + * @public + */ +export class RushSdkLoader { + /** + * Returns true if the Rush engine has already been loaded. + */ + public static get isLoaded(): boolean { + return sdkContext.rushLibModule !== undefined; + } + + /** + * Manually load the Rush engine based on rush.json found for `rushJsonSearchFolder`. + * Throws an exception if {@link RushSdkLoader.isLoaded} is already `true`. + * + * @remarks + * This API supports an callback that can be used display a progress bar, + * log of operations, and allow the operation to be canceled prematurely. + */ + public static async loadAsync(options?: ILoadSdkAsyncOptions): Promise { + // SCENARIO 5: The rush-lib engine is loaded manually using rushSdkLoader.loadAsync(). + + if (!options) { + options = {}; + } + + if (RushSdkLoader.isLoaded) { + throw new Error('RushSdkLoader.loadAsync() failed because the Rush engine has already been loaded'); + } + + const onNotifyEvent: SdkNotifyEventCallback | undefined = options.onNotifyEvent; + let progressPercent: number | undefined = undefined; + + const abortSignal: AbortSignal | undefined = options.abortSignal; + + try { + const rushJsonSearchFolder: string = options.rushJsonSearchFolder ?? process.cwd(); + + if (onNotifyEvent) { + onNotifyEvent({ + logMessage: { + kind: 'debug', + text: `Searching for rush.json starting from: ` + rushJsonSearchFolder + }, + progressPercent + }); + } + + const rushJsonPath: string | undefined = tryFindRushJsonLocation(rushJsonSearchFolder); + if (!rushJsonPath) { + throw new Error( + 'Unable to find rush.json in the specified folder or its parent folders:\n' + + `${rushJsonSearchFolder}\n` + ); + } + const monorepoRoot: string = path.dirname(rushJsonPath); + + const rushJson: JsonObject = await JsonFile.loadAsync(rushJsonPath); + const { rushVersion } = rushJson; + + const installRunNodeModuleFolder: string = path.join( + monorepoRoot, + `common/temp/install-run/@microsoft+rush@${rushVersion}` + ); + + try { + // First, try to load the version of "rush-lib" that was installed by install-run-rush.js + if (onNotifyEvent) { + onNotifyEvent({ + logMessage: { + kind: 'info', + text: `Trying to load ${RUSH_LIB_NAME} installed by install-run-rush` + }, + progressPercent + }); + } + sdkContext.rushLibModule = requireRushLibUnderFolderPath(installRunNodeModuleFolder); + } catch (e1) { + let installAndRunRushStderrContent: string = ''; + try { + const installAndRunRushJSPath: string = path.join( + monorepoRoot, + 'common/scripts/install-run-rush.js' + ); + + if (onNotifyEvent) { + onNotifyEvent({ + logMessage: { + kind: 'info', + text: 'The Rush engine has not been installed yet. Invoking install-run-rush.js...' + }, + progressPercent + }); + } + + // Start the installation + progressPercent = 0; + + const installAndRunRushProcess: SpawnSyncReturns = Executable.spawnSync( + 'node', + [installAndRunRushJSPath, '--help'], + { + stdio: 'pipe' + } + ); + + installAndRunRushStderrContent = installAndRunRushProcess.stderr; + if (installAndRunRushProcess.status !== 0) { + throw new Error(`The ${RUSH_LIB_NAME} package failed to install`); + } + + if (abortSignal) { + _checkForCancel(abortSignal, onNotifyEvent, progressPercent); + } + + // TODO: Implement incremental progress updates + progressPercent = 90; + + // Retry to load "rush-lib" after install-run-rush run + if (onNotifyEvent) { + onNotifyEvent({ + logMessage: { + kind: 'debug', + text: `Trying to load ${RUSH_LIB_NAME} installed by install-run-rush a second time` + }, + progressPercent + }); + } + + sdkContext.rushLibModule = requireRushLibUnderFolderPath(installRunNodeModuleFolder); + + progressPercent = 100; + } catch (e2) { + // eslint-disable-next-line no-console + console.error(`${installAndRunRushStderrContent}`); + throw new Error(`The ${RUSH_LIB_NAME} package failed to load`); + } + } + + if (sdkContext.rushLibModule !== undefined) { + // to track which scenario is active and how it got initialized. + global.___rush___rushLibModuleFromInstallAndRunRush = sdkContext.rushLibModule; + if (onNotifyEvent) { + onNotifyEvent({ + logMessage: { + kind: 'debug', + text: `Loaded ${RUSH_LIB_NAME} installed by install-run-rush` + }, + progressPercent + }); + } + } + } catch (e) { + if (onNotifyEvent) { + onNotifyEvent({ + logMessage: { + kind: 'info', + text: 'The operation failed: ' + (e.message ?? 'An unknown error occurred') + }, + progressPercent + }); + } + throw e; + } + } +} + +/** + * Throws an "AbortError" exception if abortSignal.aborted is true. + */ +function _checkForCancel( + abortSignal: AbortSignal, + onNotifyEvent: SdkNotifyEventCallback | undefined, + progressPercent: number | undefined +): void { + if (!abortSignal?.aborted) { + return; + } + + if (onNotifyEvent) { + onNotifyEvent({ + logMessage: { + kind: 'info', + text: `The operation was canceled` + }, + progressPercent + }); + } + + const error: Error = new Error('The operation was canceled'); + error.name = 'AbortError'; + throw error; +} diff --git a/libraries/rush-sdk/src/test/__snapshots__/script.test.ts.snap b/libraries/rush-sdk/src/test/__snapshots__/script.test.ts.snap index 36bce37042e..573fa555e28 100644 --- a/libraries/rush-sdk/src/test/__snapshots__/script.test.ts.snap +++ b/libraries/rush-sdk/src/test/__snapshots__/script.test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`@rushstack/rush-sdk Should load via env when Rush has loaded (for child processes): stderr 1`] = `""`; @@ -6,15 +6,19 @@ exports[`@rushstack/rush-sdk Should load via env when Rush has loaded (for child "Try to load @microsoft/rush-lib from process.env._RUSH_LIB_PATH from caller package Loaded @microsoft/rush-lib from process.env._RUSH_LIB_PATH [ - '_rushSdk_loadInternalModule', 'ApprovedPackagesConfiguration', 'ApprovedPackagesItem', 'ApprovedPackagesPolicy', 'BuildCacheConfiguration', 'BumpType', 'ChangeManager', + 'CobuildConfiguration', 'CommonVersionsConfiguration', 'CredentialCache', + 'CustomTipId', + 'CustomTipSeverity', + 'CustomTipType', + 'CustomTipsConfiguration', 'DependencyType', 'EnvironmentConfiguration', 'EnvironmentVariableNames', @@ -27,8 +31,10 @@ Loaded @microsoft/rush-lib from process.env._RUSH_LIB_PATH 'LookupByPath', 'NpmOptionsConfiguration', 'Operation', + 'OperationGraphHooks', 'OperationStatus', 'PackageJsonDependency', + 'PackageJsonDependencyMeta', 'PackageJsonEditor', 'PackageManager', 'PackageManagerOptionsConfigurationBase', @@ -37,34 +43,53 @@ Loaded @microsoft/rush-lib from process.env._RUSH_LIB_PATH 'ProjectChangeAnalyzer', 'RepoStateFile', 'Rush', + 'RushCommandLine', 'RushConfiguration', 'RushConfigurationProject', 'RushConstants', 'RushLifecycleHooks', + 'RushProjectConfiguration', 'RushSession', 'RushUserConfiguration', + 'Subspace', + 'SubspacesConfiguration', 'VersionPolicy', 'VersionPolicyConfiguration', 'VersionPolicyDefinitionName', 'YarnOptionsConfiguration', - '_LastInstallFlag', + '_FlagFile', + '_OperationBuildCache', '_OperationMetadataManager', '_OperationStateFile', '_RushGlobalFolder', - '_RushInternals' + '_RushInternals', + '_rushSdk_loadInternalModule' ]" `; exports[`@rushstack/rush-sdk Should load via global (for plugins): stderr 1`] = `""`; -exports[`@rushstack/rush-sdk Should load via global (for plugins): stdout 1`] = `"[ '_rushSdk_loadInternalModule', 'foo' ]"`; +exports[`@rushstack/rush-sdk Should load via global (for plugins): stdout 1`] = ` +"[ + '_rushSdk_loadInternalModule', + 'foo' +]" +`; exports[`@rushstack/rush-sdk Should load via install-run (for standalone tools): stderr 1`] = `""`; exports[`@rushstack/rush-sdk Should load via install-run (for standalone tools): stdout 1`] = ` -"Trying to load @microsoft/rush-lib installed by install-run-rush +"Try to load @microsoft/rush-lib from rush global folder +The expected global rush installed folder is \\"\\" +Failed to load @microsoft/rush-lib from rush global folder: File does not exist: +ENOENT: no such file or directory, lstat '' +Trying to load @microsoft/rush-lib installed by install-run-rush Loaded @microsoft/rush-lib installed by install-run-rush -[ '_rushSdk_loadInternalModule', 'foo' ]" +[ + '_rushSdk_loadInternalModule', + 'foo' +] +" `; exports[`@rushstack/rush-sdk Should load via process.env._RUSH_LIB_PATH (for child processes): stderr 1`] = `""`; @@ -72,5 +97,8 @@ exports[`@rushstack/rush-sdk Should load via process.env._RUSH_LIB_PATH (for chi exports[`@rushstack/rush-sdk Should load via process.env._RUSH_LIB_PATH (for child processes): stdout 1`] = ` "Try to load @microsoft/rush-lib from process.env._RUSH_LIB_PATH from caller package Loaded @microsoft/rush-lib from process.env._RUSH_LIB_PATH -[ '_rushSdk_loadInternalModule', 'foo' ]" +[ + '_rushSdk_loadInternalModule', + 'foo' +]" `; diff --git a/libraries/rush-sdk/src/test/build-assets-with-named-exports.test.ts b/libraries/rush-sdk/src/test/build-assets-with-named-exports.test.ts new file mode 100644 index 00000000000..4edcb80d19e --- /dev/null +++ b/libraries/rush-sdk/src/test/build-assets-with-named-exports.test.ts @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { Executable } from '@rushstack/node-core-library'; + +describe('@rushstack/rush-sdk named exports check', () => { + it('Should import named exports correctly (lib-shim)', async () => { + const childProcess = Executable.spawn(process.argv0, [ + '-e', + // Do not use top level await here because it is not supported in Node.js < 20.20 + ` +import('@rushstack/rush-sdk').then(({ RushConfiguration }) => { +console.log(typeof RushConfiguration.loadFromConfigurationFile); + }); +` + ]); + const { stdout, exitCode, signal } = await Executable.waitForExitAsync(childProcess, { + encoding: 'utf8' + }); + + expect(stdout.trim()).toEqual('function'); + expect(exitCode).toBe(0); + expect(signal).toBeNull(); + }); + + it('Should import named exports correctly (lib)', async () => { + const childProcess = Executable.spawn(process.argv0, [ + '-e', + ` +import('@rushstack/rush-sdk/lib/utilities/NullTerminalProvider').then(({ NullTerminalProvider }) => { +console.log(NullTerminalProvider.name); + }); +` + ]); + const { stdout, exitCode, signal } = await Executable.waitForExitAsync(childProcess, { + encoding: 'utf8' + }); + + expect(stdout.trim()).toEqual('NullTerminalProvider'); + expect(exitCode).toBe(0); + expect(signal).toBeNull(); + }); +}); diff --git a/libraries/rush-sdk/src/test/fixture/mock-rush-lib.ts b/libraries/rush-sdk/src/test/fixture/mock-rush-lib.ts index b143d40e041..b6ba64a3a23 100644 --- a/libraries/rush-sdk/src/test/fixture/mock-rush-lib.ts +++ b/libraries/rush-sdk/src/test/fixture/mock-rush-lib.ts @@ -1 +1,4 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + export const foo: number = 42; diff --git a/libraries/rush-sdk/src/test/script.test.ts b/libraries/rush-sdk/src/test/script.test.ts index e1919b55694..cdc34846f2a 100644 --- a/libraries/rush-sdk/src/test/script.test.ts +++ b/libraries/rush-sdk/src/test/script.test.ts @@ -1,12 +1,18 @@ -import * as path from 'path'; -import { Executable } from '@rushstack/node-core-library'; +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import { Executable, User } from '@rushstack/node-core-library'; const rushSdkPath: string = path.join(__dirname, '../../lib-shim/index.js'); const sandboxRepoPath: string = `${__dirname}/sandbox`; const mockPackageFolder: string = `${sandboxRepoPath}/mock-package`; +const mockRushJsonPath: string = `${sandboxRepoPath}/rush.json`; const mockRushLibPath: string = `${__dirname}/fixture/mock-rush-lib.js`; const coreLibPath: string = require.resolve('@rushstack/node-core-library'); +const quotedRushSdkPath: string = JSON.stringify(rushSdkPath); +const loadAndPrintRushSdkModule: string = `console.log(JSON.stringify(Object.keys(require(${quotedRushSdkPath})).sort(), undefined, 2).replace(/"/g, "'"));`; describe('@rushstack/rush-sdk', () => { it('Should load via global (for plugins)', () => { @@ -16,7 +22,7 @@ describe('@rushstack/rush-sdk', () => { '-e', ` global.___rush___rushLibModule = { foo: 1 }; -console.log(Object.keys(require(${JSON.stringify(rushSdkPath)})));` +${loadAndPrintRushSdkModule}` ], { currentWorkingDirectory: mockPackageFolder, @@ -39,7 +45,7 @@ console.log(Object.keys(require(${JSON.stringify(rushSdkPath)})));` '-e', ` require('@microsoft/rush-lib'); -console.log(Object.keys(require(${JSON.stringify(rushSdkPath)})));` +${loadAndPrintRushSdkModule}` ], { currentWorkingDirectory: mockPackageFolder, @@ -56,18 +62,14 @@ console.log(Object.keys(require(${JSON.stringify(rushSdkPath)})));` }); it('Should load via process.env._RUSH_LIB_PATH (for child processes)', () => { - const result = Executable.spawnSync( - 'node', - ['-e', `console.log(Object.keys(require(${JSON.stringify(rushSdkPath)})));`], - { - currentWorkingDirectory: mockPackageFolder, - environment: { - ...process.env, - RUSH_SDK_DEBUG: '1', - _RUSH_LIB_PATH: mockRushLibPath - } + const result = Executable.spawnSync('node', ['-e', loadAndPrintRushSdkModule], { + currentWorkingDirectory: mockPackageFolder, + environment: { + ...process.env, + RUSH_SDK_DEBUG: '1', + _RUSH_LIB_PATH: mockRushLibPath } - ); + }); expect(result.stderr.trim()).toMatchSnapshot('stderr'); expect(result.stdout.trim()).toMatchSnapshot('stdout'); expect(result.status).toBe(0); @@ -88,7 +90,7 @@ const mockResolveModule = (options) => { return originalResolveModule(options); } Import.resolveModule = mockResolveModule; -console.log(Object.keys(require(${JSON.stringify(rushSdkPath)}))); +${loadAndPrintRushSdkModule} ` ], { @@ -100,8 +102,18 @@ console.log(Object.keys(require(${JSON.stringify(rushSdkPath)}))); } } ); + + const nodeVersion = process.version; + const userRushSdkFolder = path.join( + User.getHomeFolder(), + '.rush', + `node-${nodeVersion}`, + 'rush-' + require(mockRushJsonPath).rushVersion + ); expect(result.stderr.trim()).toMatchSnapshot('stderr'); - expect(result.stdout.trim()).toMatchSnapshot('stdout'); + expect( + result.stdout.replace(new RegExp(userRushSdkFolder.replace(/\\/g, '\\\\'), 'g'), '') + ).toMatchSnapshot('stdout'); expect(result.status).toBe(0); }); }); diff --git a/libraries/rush-sdk/tsconfig.json b/libraries/rush-sdk/tsconfig.json index 5448a10b510..a7b61edd144 100644 --- a/libraries/rush-sdk/tsconfig.json +++ b/libraries/rush-sdk/tsconfig.json @@ -1,9 +1,11 @@ { - "extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json", + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "outDir": "lib-shim", + "outDir": "lib-intermediate-commonjs", + "declarationDir": "lib-intermediate-dts", "types": [ - "heft-jest", + "node", + "jest", "webpack-env" // Use webpack-env here instead of node so we have __non_webpack_require__ ] } diff --git a/libraries/rush-sdk/webpack.config.js b/libraries/rush-sdk/webpack.config.js new file mode 100644 index 00000000000..9f5d2189ccc --- /dev/null +++ b/libraries/rush-sdk/webpack.config.js @@ -0,0 +1,101 @@ +/* eslint-env es6 */ +'use strict'; + +const { PackageJsonLookup, Import } = require('@rushstack/node-core-library'); +const { PreserveDynamicRequireWebpackPlugin } = require('@rushstack/webpack-preserve-dynamic-require-plugin'); +const {} = require('webpack'); + +module.exports = ({ webpack: { BannerPlugin } }) => { + const packageJson = PackageJsonLookup.loadOwnPackageJson(__dirname); + + const externalDependencyNames = new Set(Object.keys(packageJson.dependencies || {})); + + // Get all export specifiers from the sidecar .exports.json file generated by DeepImportsPlugin + const rushLibFolder = Import.resolvePackage({ + baseFolderPath: __dirname, + packageName: '@microsoft/rush-lib', + useNodeJSResolver: true + }); + const { moduleExports: exportSpecifiers } = require(`${rushLibFolder}/lib-commonjs/index.exports.json`); + // Assign named exports after the bundle to ensure they're properly exposed for ESM imports + const footerCodeForLibShim = exportSpecifiers + .map((name) => `exports.${name} = module.exports.${name};`) + .join('\n'); + + // Explicitly exclude @microsoft/rush-lib + externalDependencyNames.delete('@microsoft/rush-lib'); + + // Resolve rush-lib deep imports to the intermediate ESM source rather than the + // DeepImportsPlugin stubs (which load from the dist/ webpack bundle). This avoids + // embedding a nested webpack runtime that expects a separate commons.js chunk. + const rushLibLibAlias = `${rushLibFolder}/lib-intermediate-esm`; + + return { + context: __dirname, + mode: 'development', // So the output isn't minified + devtool: 'source-map', + entry: { + // Using CommonJS due to access of module.parent + index: `${__dirname}/lib-intermediate-commonjs/index.js`, + loader: `${__dirname}/lib-intermediate-commonjs/loader.js` + }, + output: { + path: `${__dirname}/lib-shim`, + filename: '[name].js', + chunkFilename: 'chunks/[name].js', + library: { + type: 'commonjs2' + } + }, + optimization: { + flagIncludedChunks: true, + concatenateModules: true, + providedExports: true, + usedExports: true, + sideEffects: true, + removeAvailableModules: true, + minimize: false, + realContentHash: true, + innerGraph: true + }, + target: 'node', + plugins: [ + new BannerPlugin({ + raw: true, + footer: true, + include: /index\.js$/, + banner: footerCodeForLibShim + }), + new PreserveDynamicRequireWebpackPlugin() + ], + resolve: { + alias: { + '@microsoft/rush-lib/lib': rushLibLibAlias + } + }, + externals: [ + ({ request }, callback) => { + let packageName; + let firstSlashIndex = request.indexOf('/'); + if (firstSlashIndex === -1) { + packageName = request; + } else if (request.startsWith('@')) { + let secondSlash = request.indexOf('/', firstSlashIndex + 1); + if (secondSlash === -1) { + packageName = request; + } else { + packageName = request.substring(0, secondSlash); + } + } else { + packageName = request.substring(0, firstSlashIndex); + } + + if (externalDependencyNames.has(packageName)) { + callback(null, `commonjs ${request}`); + } else { + callback(); + } + } + ] + }; +}; diff --git a/libraries/rush-themed-ui/.eslintrc.js b/libraries/rush-themed-ui/.eslintrc.js deleted file mode 100644 index 288eaa16364..00000000000 --- a/libraries/rush-themed-ui/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -// This is a workaround for https://github.com/eslint/eslint/issues/3458 -require('@rushstack/eslint-config/patch/modern-module-resolution'); - -module.exports = { - extends: ['@rushstack/eslint-config/profile/web-app', '@rushstack/eslint-config/mixins/react'], - parserOptions: { tsconfigRootDir: __dirname } -}; diff --git a/libraries/rush-themed-ui/.npmignore b/libraries/rush-themed-ui/.npmignore index 302dbc5b019..f7a40e10213 100644 --- a/libraries/rush-themed-ui/.npmignore +++ b/libraries/rush-themed-ui/.npmignore @@ -8,6 +8,12 @@ !/lib/** !/lib-*/** !/dist/** +!/includes/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json !ThirdPartyNotice.txt # Ignore certain patterns that should not get published. @@ -15,16 +21,16 @@ /lib/**/test/ /lib-*/**/test/ *.test.js +*.test.[cm]js +*.test.d.ts +*.test.d.[cm]ts # NOTE: These don't need to be specified, because NPM includes them automatically. # # package.json -# README (and its variants) -# CHANGELOG (and its variants) -# LICENSE / LICENCE - -#-------------------------------------------- -# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE -#-------------------------------------------- +# README.md +# LICENSE -# (Add your project-specific overrides here) \ No newline at end of file +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- diff --git a/libraries/rush-themed-ui/config/api-extractor.json b/libraries/rush-themed-ui/config/api-extractor.json index cafe285cb0d..ccf98d88f00 100644 --- a/libraries/rush-themed-ui/config/api-extractor.json +++ b/libraries/rush-themed-ui/config/api-extractor.json @@ -1,427 +1,8 @@ -/** - * Config file for API Extractor. For more info, please visit: https://api-extractor.com - */ { "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "local-web-rig/profiles/library/config/api-extractor-base.json", - /** - * Optionally specifies another JSON config file that this file extends from. This provides a way for - * standard settings to be shared across multiple projects. - * - * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains - * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be - * resolved using NodeJS require(). - * - * SUPPORTED TOKENS: none - * DEFAULT VALUE: "" - */ - // "extends": "./shared/api-extractor-base.json" - // "extends": "my-package/include/api-extractor-base.json" - - /** - * Determines the "" token that can be used with other config file settings. The project folder - * typically contains the tsconfig.json and package.json config files, but the path is user-defined. - * - * The path is resolved relative to the folder of the config file that contains the setting. - * - * The default value for "projectFolder" is the token "", which means the folder is determined by traversing - * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder - * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error - * will be reported. - * - * SUPPORTED TOKENS: - * DEFAULT VALUE: "" - */ - // "projectFolder": "..", - - /** - * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor - * analyzes the symbols exported by this module. - * - * The file extension must be ".d.ts" and not ".ts". - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - */ - "mainEntryPointFilePath": "/lib/index.d.ts", - - /** - * A list of NPM package names whose exports should be treated as part of this package. - * - * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", - * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part - * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly - * imports library2. To avoid this, we can specify: - * - * "bundledPackages": [ "library2" ], - * - * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been - * local files for library1. - */ - "bundledPackages": [], - - /** - * Specifies what type of newlines API Extractor should use when writing output files. By default, the output files - * will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead. - * To use the OS's default newline kind, specify "os". - * - * DEFAULT VALUE: "crlf" - */ - // "newlineKind": "crlf", - - /** - * Set to true when invoking API Extractor's test harness. When `testMode` is true, the `toolVersion` field in the - * .api.json file is assigned an empty string to prevent spurious diffs in output files tracked for tests. - * - * DEFAULT VALUE: "false" - */ - // "testMode": false, - - /** - * Specifies how API Extractor sorts members of an enum when generating the .api.json file. By default, the output - * files will be sorted alphabetically, which is "by-name". To keep the ordering in the source code, specify - * "preserve". - * - * DEFAULT VALUE: "by-name" - */ - // "enumMemberOrder": "by-name", - - /** - * Determines how the TypeScript compiler engine will be invoked by API Extractor. - */ - "compiler": { - /** - * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * Note: This setting will be ignored if "overrideTsconfig" is used. - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/tsconfig.json" - */ - // "tsconfigFilePath": "/tsconfig.json", - /** - * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. - * The object must conform to the TypeScript tsconfig schema: - * - * http://json.schemastore.org/tsconfig - * - * If omitted, then the tsconfig.json file will be read from the "projectFolder". - * - * DEFAULT VALUE: no overrideTsconfig section - */ - // "overrideTsconfig": { - // . . . - // } - /** - * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended - * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when - * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses - * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck. - * - * DEFAULT VALUE: false - */ - // "skipLibCheck": true, - }, - - /** - * Configures how the API report file (*.api.md) will be generated. - */ - "apiReport": { - /** - * (REQUIRED) Whether to generate an API report. - */ - "enabled": true, - - /** - * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce - * a full file path. - * - * The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/". - * - * SUPPORTED TOKENS: , - * DEFAULT VALUE: ".api.md" - */ - // "reportFileName": ".api.md", - - /** - * Specifies the folder where the API report file is written. The file name portion is determined by - * the "reportFileName" setting. - * - * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy, - * e.g. for an API review. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/temp/" - */ - "reportFolder": "../../../common/reviews/api" - - /** - * Specifies the folder where the temporary report file is written. The file name portion is determined by - * the "reportFileName" setting. - * - * After the temporary file is written to disk, it is compared with the file in the "reportFolder". - * If they are different, a production build will fail. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/temp/" - */ - // "reportTempFolder": "/temp/", - - /** - * Whether "forgotten exports" should be included in the API report file. Forgotten exports are declarations - * flagged with `ae-forgotten-export` warnings. See https://api-extractor.com/pages/messages/ae-forgotten-export/ to - * learn more. - * - * DEFAULT VALUE: "false" - */ - // "includeForgottenExports": false - }, - - /** - * Configures how the doc model file (*.api.json) will be generated. - */ "docModel": { - /** - * (REQUIRED) Whether to generate a doc model file. - */ "enabled": false - - /** - * The output path for the doc model file. The file extension should be ".api.json". - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/temp/.api.json" - */ - // "apiJsonFilePath": "/temp/.api.json", - - /** - * Whether "forgotten exports" should be included in the doc model file. Forgotten exports are declarations - * flagged with `ae-forgotten-export` warnings. See https://api-extractor.com/pages/messages/ae-forgotten-export/ to - * learn more. - * - * DEFAULT VALUE: "false" - */ - // "includeForgottenExports": false, - - /** - * The base URL where the project's source code can be viewed on a website such as GitHub or - * Azure DevOps. This URL path corresponds to the `` path on disk. - * - * This URL is concatenated with the file paths serialized to the doc model to produce URL file paths to individual API items. - * For example, if the `projectFolderUrl` is "https://github.com/microsoft/rushstack/tree/main/apps/api-extractor" and an API - * item's file path is "api/ExtractorConfig.ts", the full URL file path would be - * "https://github.com/microsoft/rushstack/tree/main/apps/api-extractor/api/ExtractorConfig.js". - * - * Can be omitted if you don't need source code links in your API documentation reference. - * - * SUPPORTED TOKENS: none - * DEFAULT VALUE: "" - */ - // "projectFolderUrl": "http://github.com/path/to/your/projectFolder" - }, - - /** - * Configures how the .d.ts rollup file will be generated. - */ - "dtsRollup": { - /** - * (REQUIRED) Whether to generate the .d.ts rollup file. - */ - "enabled": true - - /** - * Specifies the output path for a .d.ts rollup file to be generated without any trimming. - * This file will include all declarations that are exported by the main entry point. - * - * If the path is an empty string, then this file will not be written. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "/dist/.d.ts" - */ - // "untrimmedFilePath": "/dist/.d.ts", - - /** - * Specifies the output path for a .d.ts rollup file to be generated with trimming for an "alpha" release. - * This file will include only declarations that are marked as "@public", "@beta", or "@alpha". - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "" - */ - // "alphaTrimmedFilePath": "/dist/-alpha.d.ts", - - /** - * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. - * This file will include only declarations that are marked as "@public" or "@beta". - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "" - */ - // "betaTrimmedFilePath": "/dist/-beta.d.ts", - - /** - * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. - * This file will include only declarations that are marked as "@public". - * - * If the path is an empty string, then this file will not be written. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "" - */ - // "publicTrimmedFilePath": "/dist/-public.d.ts", - - /** - * When a declaration is trimmed, by default it will be replaced by a code comment such as - * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the - * declaration completely. - * - * DEFAULT VALUE: false - */ - // "omitTrimmingComments": true - }, - - /** - * Configures how the tsdoc-metadata.json file will be generated. - */ - "tsdocMetadata": { - /** - * Whether to generate the tsdoc-metadata.json file. - * - * DEFAULT VALUE: true - */ - // "enabled": true, - /** - * Specifies where the TSDoc metadata file should be written. - * - * The path is resolved relative to the folder of the config file that contains the setting; to change this, - * prepend a folder token such as "". - * - * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata", - * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup - * falls back to "tsdoc-metadata.json" in the package folder. - * - * SUPPORTED TOKENS: , , - * DEFAULT VALUE: "" - */ - // "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" - }, - - /** - * Configures how API Extractor reports error and warning messages produced during analysis. - * - * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages. - */ - "messages": { - /** - * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing - * the input .d.ts files. - * - * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" - * - * DEFAULT VALUE: A single "default" entry with logLevel=warning. - */ - "compilerMessageReporting": { - /** - * Configures the default routing for messages that don't match an explicit rule in this table. - */ - "default": { - /** - * Specifies whether the message should be written to the the tool's output log. Note that - * the "addToApiReportFile" property may supersede this option. - * - * Possible values: "error", "warning", "none" - * - * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail - * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes - * the "--local" option), the warning is displayed but the build will not fail. - * - * DEFAULT VALUE: "warning" - */ - "logLevel": "warning" - - /** - * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), - * then the message will be written inside that file; otherwise, the message is instead logged according to - * the "logLevel" option. - * - * DEFAULT VALUE: false - */ - // "addToApiReportFile": false - } - - // "TS2551": { - // "logLevel": "warning", - // "addToApiReportFile": true - // }, - // - // . . . - }, - - /** - * Configures handling of messages reported by API Extractor during its analysis. - * - * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag" - * - * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings - */ - "extractorMessageReporting": { - "default": { - "logLevel": "warning" - // "addToApiReportFile": false - } - - // "ae-extra-release-tag": { - // "logLevel": "warning", - // "addToApiReportFile": true - // }, - // - // . . . - }, - - /** - * Configures handling of messages reported by the TSDoc parser when analyzing code comments. - * - * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text" - * - * DEFAULT VALUE: A single "default" entry with logLevel=warning. - */ - "tsdocMessageReporting": { - "default": { - "logLevel": "warning" - // "addToApiReportFile": false - } - - // "tsdoc-link-tag-unescaped-text": { - // "logLevel": "warning", - // "addToApiReportFile": true - // }, - // - // . . . - } } } diff --git a/libraries/rush-themed-ui/config/jest.config.json b/libraries/rush-themed-ui/config/jest.config.json index 600ba9ea39a..a6a75a1a029 100644 --- a/libraries/rush-themed-ui/config/jest.config.json +++ b/libraries/rush-themed-ui/config/jest.config.json @@ -1,3 +1,3 @@ { - "extends": "@rushstack/heft-web-rig/profiles/library/config/jest.config.json" + "extends": "local-web-rig/profiles/library/config/jest.config.json" } diff --git a/libraries/rush-themed-ui/config/rig.json b/libraries/rush-themed-ui/config/rig.json index d72946b5042..b7cba34165f 100644 --- a/libraries/rush-themed-ui/config/rig.json +++ b/libraries/rush-themed-ui/config/rig.json @@ -1,6 +1,6 @@ { "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@rushstack/heft-web-rig", + "rigPackageName": "local-web-rig", "rigProfile": "library" } diff --git a/libraries/rush-themed-ui/eslint.config.js b/libraries/rush-themed-ui/eslint.config.js new file mode 100644 index 00000000000..25d563f73a7 --- /dev/null +++ b/libraries/rush-themed-ui/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const webAppProfile = require('local-web-rig/profiles/library/includes/eslint/flat/profile/web-app'); +const reactMixin = require('local-web-rig/profiles/library/includes/eslint/flat/mixins/react'); + +module.exports = [ + ...webAppProfile, + ...reactMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/libraries/rush-themed-ui/package.json b/libraries/rush-themed-ui/package.json index 443c740cc24..295898f8994 100644 --- a/libraries/rush-themed-ui/package.json +++ b/libraries/rush-themed-ui/package.json @@ -4,31 +4,29 @@ "version": "0.0.0", "private": true, "license": "MIT", - "module": "dist/rush-themed-ui.js", - "types": "dist/rush-themed-ui.d.ts", + "module": "./dist/rush-themed-ui.js", + "types": "./dist/rush-themed-ui.d.ts", "scripts": { "build": "heft test --clean", "start": "heft build --watch", "test": "heft test", - "_phase:build": "heft run --only build -- --clean", - "_phase:test": "heft run --only test -- --clean" + "_phase:build": "heft run --only build -- --clean" }, "dependencies": { - "react": "~16.13.1", - "react-dom": "~16.13.1" + "react": "~19.2.3", + "react-dom": "~19.2.3" }, "devDependencies": { - "@rushstack/eslint-config": "workspace:*", - "@rushstack/heft-web-rig": "workspace:*", + "@radix-ui/colors": "~3.0.0", + "@radix-ui/react-checkbox": "~1.3.3", + "@radix-ui/react-icons": "~1.3.2", + "@radix-ui/react-scroll-area": "~1.2.10", + "@radix-ui/react-tabs": "~1.1.13", "@rushstack/heft": "workspace:*", - "@types/heft-jest": "1.0.1", - "@types/react-dom": "16.9.14", - "@types/react": "16.14.23", - "@types/webpack-env": "1.18.0", - "@radix-ui/react-scroll-area": "~1.0.2", - "@radix-ui/colors": "~0.1.8", - "@radix-ui/react-tabs": "~1.0.1", - "@radix-ui/react-checkbox": "~1.0.1", - "@radix-ui/react-icons": "~1.1.1" - } + "@types/react": "19.2.7", + "@types/react-dom": "19.2.3", + "eslint": "~9.37.0", + "local-web-rig": "workspace:*" + }, + "sideEffects": false } diff --git a/libraries/rush-themed-ui/src/components/Button/index.tsx b/libraries/rush-themed-ui/src/components/Button/index.tsx index 5fccc974bdc..dd1d5b3b88f 100644 --- a/libraries/rush-themed-ui/src/components/Button/index.tsx +++ b/libraries/rush-themed-ui/src/components/Button/index.tsx @@ -2,6 +2,7 @@ // See LICENSE in the project root for license information. import React from 'react'; + import { Text } from '../Text'; import styles from './styles.scss'; @@ -10,7 +11,7 @@ import styles from './styles.scss'; * @public */ export interface IButtonProps { - children: JSX.Element | string; + children: React.ReactElement | string; disabled?: boolean; onClick: () => void; } @@ -19,7 +20,7 @@ export interface IButtonProps { * A button UI component * @public */ -export const Button = ({ children, disabled = false, onClick }: IButtonProps): JSX.Element => { +export const Button = ({ children, disabled = false, onClick }: IButtonProps): React.ReactElement => { return (